00001 // $Id: fracfill.cpp 1282 2006-06-09 09:46:49Z alex $ 00002 /* @@tag:xara-cn@@ DO NOT MODIFY THIS LINE 00003 ================================XARAHEADERSTART=========================== 00004 00005 Xara LX, a vector drawing and manipulation program. 00006 Copyright (C) 1993-2006 Xara Group Ltd. 00007 Copyright on certain contributions may be held in joint with their 00008 respective authors. See AUTHORS file for details. 00009 00010 LICENSE TO USE AND MODIFY SOFTWARE 00011 ---------------------------------- 00012 00013 This file is part of Xara LX. 00014 00015 Xara LX is free software; you can redistribute it and/or modify it 00016 under the terms of the GNU General Public License version 2 as published 00017 by the Free Software Foundation. 00018 00019 Xara LX and its component source files are distributed in the hope 00020 that it will be useful, but WITHOUT ANY WARRANTY; without even the 00021 implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 00022 See the GNU General Public License for more details. 00023 00024 You should have received a copy of the GNU General Public License along 00025 with Xara LX (see the file GPL in the root directory of the 00026 distribution); if not, write to the Free Software Foundation, Inc., 51 00027 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 00028 00029 00030 ADDITIONAL RIGHTS 00031 ----------------- 00032 00033 Conditional upon your continuing compliance with the GNU General Public 00034 License described above, Xara Group Ltd grants to you certain additional 00035 rights. 00036 00037 The additional rights are to use, modify, and distribute the software 00038 together with the wxWidgets library, the wxXtra library, and the "CDraw" 00039 library and any other such library that any version of Xara LX relased 00040 by Xara Group Ltd requires in order to compile and execute, including 00041 the static linking of that library to XaraLX. In the case of the 00042 "CDraw" library, you may satisfy obligation under the GNU General Public 00043 License to provide source code by providing a binary copy of the library 00044 concerned and a copy of the license accompanying it. 00045 00046 Nothing in this section restricts any of the rights you have under 00047 the GNU General Public License. 00048 00049 00050 SCOPE OF LICENSE 00051 ---------------- 00052 00053 This license applies to this program (XaraLX) and its constituent source 00054 files only, and does not necessarily apply to other Xara products which may 00055 in part share the same code base, and are subject to their own licensing 00056 terms. 00057 00058 This license does not apply to files in the wxXtra directory, which 00059 are built into a separate library, and are subject to the wxWindows 00060 license contained within that directory in the file "WXXTRA-LICENSE". 00061 00062 This license does not apply to the binary libraries (if any) within 00063 the "libs" directory, which are subject to a separate license contained 00064 within that directory in the file "LIBS-LICENSE". 00065 00066 00067 ARRANGEMENTS FOR CONTRIBUTION OF MODIFICATIONS 00068 ---------------------------------------------- 00069 00070 Subject to the terms of the GNU Public License (see above), you are 00071 free to do whatever you like with your modifications. However, you may 00072 (at your option) wish contribute them to Xara's source tree. You can 00073 find details of how to do this at: 00074 http://www.xaraxtreme.org/developers/ 00075 00076 Prior to contributing your modifications, you will need to complete our 00077 contributor agreement. This can be found at: 00078 http://www.xaraxtreme.org/developers/contribute/ 00079 00080 Please note that Xara will not accept modifications which modify any of 00081 the text between the start and end of this header (marked 00082 XARAHEADERSTART and XARAHEADEREND). 00083 00084 00085 MARKS 00086 ----- 00087 00088 Xara, Xara LX, Xara X, Xara X/Xtreme, Xara Xtreme, the Xtreme and Xara 00089 designs are registered or unregistered trademarks, design-marks, and/or 00090 service marks of Xara Group Ltd. All rights in these marks are reserved. 00091 00092 00093 Xara Group Ltd, Gaddesden Place, Hemel Hempstead, HP2 6EX, UK. 00094 http://www.xara.com/ 00095 00096 =================================XARAHEADEREND============================ 00097 */ 00098 // This file implements the fractal filling code itself 00099 00100 /* 00101 */ 00102 00103 #include "camtypes.h" 00104 //#include "bitmap.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00105 #include "bitmpinf.h" 00106 #include "fracfill.h" 00107 //WEBSTER-Martin-14/01/97 00108 #ifndef EXCLUDE_BFX 00109 //#include "bfxasm.h" 00110 #endif 00111 //#include "errors.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00112 00113 // This is not compulsory, but you may as well put it in so that the correct version 00114 // of your file can be registered in the .exe 00115 DECLARE_SOURCE("$Revision: 1282 $"); 00116 00117 // An implement to match the Declare in the .h file. 00118 // If you have many classes, it is recommended to place them all together, here at the start of the file 00119 CC_IMPLEMENT_DYNCREATE(PlasmaFractalFill, ListItem) 00120 00121 // This will get Camelot to display the filename and linenumber of any memory allocations 00122 // that are not released at program exit 00123 // Declare smart memory handling in Debug builds 00124 #define new CAM_DEBUG_NEW 00125 00126 00127 const INT32 MAX_FRACTAL_COORD = (1<<28); // All fractals are plotted in a coordinate space 00128 // going 0..(1<<31)-1 00129 00130 /******************************************************************************************** 00131 00132 > void PlasmaFractalFill::SetSeed(INT32 NewSeed); 00133 00134 Author: Alex_Bligh (Xara Group Ltd) <camelotdev@xara.com> 00135 Created: 12/9/94 00136 Inputs: NewSeed - the new seed 00137 Outputs: (changes seed) 00138 Returns: Nothing 00139 Purpose: Sets the random number seed of our fabby platform independent random 00140 number generator. Actually the seed is 33 bits long and we only allow 00141 half those values to be set. I don't think anyone will notice though :-). 00142 Errors: - 00143 SeeAlso: - 00144 00145 ********************************************************************************************/ 00146 00147 void PlasmaFractalFill::SetSeed(INT32 NewSeed) 00148 { 00149 CurrentSeed = (UINT32) NewSeed; 00150 CurrentSeed2 = 1; // Don't use 0 here as (0,0) has sequ length of zero 00151 } 00152 00153 /******************************************************************************************** 00154 00155 > void PlasmaFractalFill::GetRandom() 00156 00157 Author: Alex_Bligh (Xara Group Ltd) <camelotdev@xara.com> 00158 Created: 12/9/94 00159 Inputs: NewSeed - the new seed 00160 Outputs: a random number & changes seed 00161 Returns: Nothing 00162 Purpose: Find the next random number in a sequence as detailed ARM data manual p 2-51 00163 Errors: - 00164 SeeAlso: - 00165 00166 This is a Roger/Sophie Wilson routine port and thus incomprehensible 00167 00168 ARM Code: 00169 ; Enter with seed in R0 (32 bits) R1 (1 bit in b0 only) 00170 TST R1, R1, LSR #1 ; b0 into carry 00171 MOVS R2, R0, RRX ; 33 bit rotate 00172 ADC R1, R1, R1 ; Carry into lsb of R1 (rest of it junked) 00173 EOR R2, R2, R0, LSL #12 ; dual 00174 EOR R0, R2, R2, LSR #20 ; tap 00175 ; exit as before 00176 00177 ********************************************************************************************/ 00178 00179 // All must be INT32, t is temporary, the routine sorts out types 00180 #define DoRand(Seed1, Seed2, t) \ 00181 t = ((UINT32)(Seed1) >> 1 ) ^ ((UINT32)(Seed2)<<31); \ 00182 Seed2 = ((UINT32)(Seed1) & 1); \ 00183 t ^= (UINT32)(Seed1) << 12; \ 00184 Seed1 = ((UINT32)(t)) ^ ((UINT32)(t>>20)); 00185 00186 inline INT32 PlasmaFractalFill::GetRandom() 00187 { 00188 UINT32 t; 00189 DoRand(CurrentSeed, CurrentSeed2, t); 00190 return((INT32)(CurrentSeed)); 00191 } 00192 00193 00194 /******************************************************************************************** 00195 00196 > INT32 PlasmaFractalFill::Adjust(INT32 pa,INT32 pb,INT32 x, INT32 y 00197 INT32 aGraininess, INT32 aGravity) 00198 00199 Author: Alex_Bligh (Xara Group Ltd) <camelotdev@xara.com> 00200 Created: 12/9/94 00201 Inputs: (xa, ya) - (xb, yb) line to adjust 00202 pa, pb potentials at these two coordinates 00203 x, y coordinate we're interested in 00204 Outputs: Fills bitmap 00205 Returns: Potential at (x,y) 00206 Purpose: Linterpolates between potentials at (xa, ya, xb, yb) adding a random peturbation 00207 Errors: - 00208 SeeAlso: - 00209 00210 ********************************************************************************************/ 00211 00212 00213 inline INT32 PlasmaFractalFill::Adjust(INT32 pa,INT32 pb,INT32 x, INT32 y, 00214 INT32 aGraininess, INT32 aGravity) 00215 #ifdef FASTFRACTALADJUST 00216 { 00217 // The squash term only needs to be compared against zero and this disgustingness assumes that the 00218 // representation of (fixed16)0 is hex 0 on all architectures 00219 return FastFractalAdjust(pa, pb, x, y, aGraininess, aGravity, Seed, *((INT32 *)(&Squash)), RecursionLevel); 00220 } 00221 #else 00222 { 00223 // Bodge to pseudorandomate psuedorandomiser from coords alone! 00224 UINT32 topseed=1; 00225 INT32 potential=(pa ^ (pb<<16) ^ _lrotl((UINT32)x,16)^((UINT32) y) ^ Seed ^ 0xABCD1234); 00226 UINT32 t; 00227 DoRand(potential,topseed,t); // Do an initial spin 00228 potential = _lrotl(potential,(x^(x>>5)^(x>>10)^(x>>15)^(x>>20)^(x>>25)^(x>>30)^y^(y>>5)^(y>>10)^(y>>15)^(y>>20)^(y>>25)^(y>>30)) & 31); 00229 DoRand(potential,topseed,t); // random number to potential 00230 00231 // When squashing we only peturb along the edges and the rest is pure linterp. 00232 // (effect of gravity unknown!). At the edges (because of CoordinateMask) one of 00233 // x & y is zero. We can cunningly use the logical operators to make this a quick test. 00234 if ((Squash!=0) && !(x || y)) potential = 0; 00235 00236 // The adjustment is mostly linear interpolation (linterp) (i.e. (pa+pb)/2) but we add: 00237 // 00238 // Peterbation effects: We use a random number scaled by graininess in proportion to the (y1-y2) which 00239 // simply means shifting it right by the recursion level. 00240 // 00241 // Gravitational effects: 00242 // We linterp between y1 and y2. But we want this to look like a parabola. So if xh is the midpoint: 00243 // 00244 // 2 2 2 2 00245 // p1 = a y1 p2 = a y2 ph = a yh = a (y1 + y2) /4 00246 // 00247 // 2 00248 // ph = (p1+p2)/2 - (p1+p2)/2 + a(y1+y2) /4 00249 // 00250 // 2 00251 // ph = (p1+p2)/2 - (a/4)(y1-y2) 00252 // ~~~~~~~~~~~~~ 00253 // 00254 // Thus we must subtract the underlined term to make it look like a parabola. However a is negative 00255 // and we operate using a constant offset as our potential is high in the middle and zero at the edges 00256 // (the same annoyance happens all over potential theory!) so we acually add the term. We get the squared 00257 // term by shifting right by 2xrecursion level. 00258 // 00259 00260 potential = ( ( ((INT32)(aGraininess)) * ((INT32)(potential>>17)) ) >>RecursionLevel) /*peterb*/ 00261 - (aGravity>>(RecursionLevel*2)) /*gravity*/ 00262 + (( pa + pb + 1 ) >> 1 ); /* linterp */ 00263 00264 // Clip potential to max & min 00265 if ( potential > MaxPotential ) potential = MaxPotential; 00266 if ( potential < 0 ) potential = 0; 00267 00268 //if (p!=potential) {TRACEUSER( "Alex", _T("Awooga awooga maths error\n"));} 00269 return(potential); 00270 } 00271 #endif 00272 00273 /******************************************************************************************** 00274 00275 > BOOL PlasmaFractalFill::SubDivide(INT32 x1, INT32 y1, INT32 x2, INT32 y2, 00276 INT32 p11, INT32 p12, INT32 p21, INT32 p22, BOOL PlotNow) 00277 Author: Alex_Bligh (Xara Group Ltd) <camelotdev@xara.com> 00278 Created: 12/9/94 00279 Inputs: (x1, y1) - (x2, y2) rectangle to subdivide 00280 pxx = potential at a corner (e.g. p21 = potential at (x2,y1)) 00281 PlotNow = TRUE to plot pixel at x1,y1 with potential p11 00282 Outputs: Recursive (adjusts bitmap) 00283 Returns: TRUE if succeeded, FALSE if failed 00284 Purpose: Fills the 8 bit bitmap passed with the fractal requested 00285 Errors: - 00286 SeeAlso: - 00287 00288 ********************************************************************************************/ 00289 00290 BOOL PlasmaFractalFill::SubDivide(INT32 x1, INT32 y1, INT32 x2, INT32 y2, 00291 INT32 p11, INT32 p12, INT32 p22, INT32 p21, BOOL PlotNow) 00292 { 00293 INT32 xh, yh; 00294 INT32 ph1, ph2, p1h, p2h, phh; 00295 00296 #ifdef CRUMMYOLDCODE 00297 // First check this square is at least partially within the clip rectangle and thus is worth 00298 // subdividing. It only is NOT worth subdiving if the highest coordinate is too low, or the 00299 // lowest coordinate too high. 00300 00301 if ( (((x2>>EigenValue)-Ox)<0) || (((x1>>EigenValue)-Ox)>Width) 00302 || (((y2>>EigenValue)-Oy)<0) || (((y1>>EigenValue)-Oy)>Height) ) return (TRUE); 00303 00304 // We only ever plot the left hand corner. This implies the right most pixel never gets plotted 00305 // which is fine as the pixels go 0..n-1 and we pass n in as the first coordinate. 00306 // We use UINT32 casting to check the value is both smaller than Width and non-negative. 00307 if ( (PlotNow) && (((UINT32)((x1>>EigenValue)-Ox))<(UINT32)Width) 00308 && (((UINT32)((y1>>EigenValue)-Oy))<(UINT32)Height) ) 00309 pBitmap->PlotPixel((x1>>EigenValue)-Ox, (y1>>EigenValue)-Oy, (PixelGreyscale) ~(p11>>8)); 00310 00311 if ( ( ((x2>>EigenValue)-(x1>>EigenValue)) <2 ) && ( ((y2>>EigenValue)-(y1>>EigenValue)) <2 ) ) return(TRUE); 00312 #else 00313 // See how stupid the VCC optimiser is. OK, I'll do its job for it then. Soon no doubt I'll get round to writing 00314 // this whole thing in assembler 00315 { 00316 INT32 x1e=((x1>>EigenValue)-Ox); // one would hope the optimiser leaves the shift in CX. Oh no it doesn't... 00317 INT32 x2e=((x2>>EigenValue)-Ox); 00318 INT32 y1e=((y1>>EigenValue)-Oy); 00319 INT32 y2e=((y2>>EigenValue)-Oy); 00320 00321 // First check this square is at least partially within the clip rectangle and thus is worth 00322 // subdividing. It only is NOT worth subdiving if the highest coordinate is too low, or the 00323 // lowest coordinate too high. 00324 00325 if ( (x2e<0) || (x1e>Width) 00326 || (y2e<0) || (y1e>Height) ) return (TRUE); 00327 00328 // We only ever plot the left hand corner. This implies the right most pixel never gets plotted 00329 // which is fine as the pixels go 0..n-1 and we pass n in as the first coordinate. 00330 // We use UINT32 casting to check the value is both smaller than Width and non-negative. 00331 if ( (PlotNow) && (((UINT32)x1e)<(UINT32)Width) 00332 && (((UINT32)y1e)<(UINT32)Height) ) 00333 pBitmap->PlotPixel(x1e, y1e, (PixelGreyscale) ~(p11>>8)); 00334 00335 // we cunningly note that ((x2>>EigenValue)-(x1>>EigenValue)) = x2e-x1e 00336 if ( ( (x2e-x1e) <2 ) && ( (y2e-y1e) <2 ) ) return(TRUE); 00337 } 00338 #endif 00339 00340 // Mark the fact we are one recursion level deeper. 00341 RecursionLevel++; 00342 00343 // Calculate the midpoints 00344 xh = (x1+x2)>>1; 00345 yh = (y1+y2)>>1; 00346 00347 /* 00348 This is the way adjust is going to calculate the potential at the midpoints: 00349 00350 x1 xh x2 00351 y2__________|_________y2 00352 |p12 C|ph2 p22| 00353 | | | 00354 | 4 | 3 | 00355 | | | 00356 | | | 00357 yh|D_______E|________B|yh 00358 |p1h |phh p2h| 00359 | | | 00360 | | | 00361 | 1 | 2 | 00362 | | | 00363 y1|p11_____A|ph1___p21|y1 00364 x1 xh x2 00365 00366 Adjust splits each line (in order A, B, C, D) to find the potential at its midpoint adding a random peturbation. 00367 The point E is then calculated as the average of A, B, C & D. As we calculate the potentials at A, B, C, D, E we 00368 plot them. We can then subdivide each smaller square in the order 1, 2, 3, 4. 00369 */ 00370 00371 ph1=Adjust( p11, p21, xh & CoordinateMask, y1 & CoordinateMask, xGraininess, xGravity); 00372 p2h=Adjust( p21, p22, x2 & CoordinateMask, yh & CoordinateMask, yGraininess, yGravity); 00373 ph2=Adjust( p12, p22, xh & CoordinateMask, y2 & CoordinateMask, xGraininess, xGravity); 00374 p1h=Adjust( p11, p12, x1 & CoordinateMask, yh & CoordinateMask, yGraininess, yGravity); 00375 phh = (ph1+p2h+ph2+p1h+2)>>2; 00376 00377 // When we're at the top level under gravity we make the midpoint full potential 00378 if (RecursionLevel==1) 00379 { 00380 if (ForceCornersToZero ) 00381 { 00382 phh=MaxPotential; 00383 } 00384 else 00385 { 00386 // if it's tileable we set it to the opposite extreme. 00387 if (Tileable) phh = MaxPotential - p11; // p11==p12==p21==p22 as tileable recursion1 00388 } 00389 } 00390 00391 // Now subdivide our square into four 00392 if ( SubDivide( x1, y1, xh, yh, p11, p1h, phh, ph1, FALSE ) // This pixel's just been plotted 00393 && SubDivide( xh, y1, x2, yh, ph1, phh, p2h, p21, TRUE ) // These ones haven't 00394 && SubDivide( xh, yh, x2, y2, phh, ph2, p22, p2h, TRUE ) 00395 && SubDivide( x1, yh, xh, y2, p1h, p12, ph2, phh, TRUE )) 00396 { 00397 RecursionLevel--; 00398 return(TRUE); 00399 } 00400 else 00401 { 00402 RecursionLevel--; 00403 return(FALSE); 00404 } 00405 } 00406 00407 00408 /******************************************************************************************** 00409 00410 > PlasmaFractalFill::PlasmaFractalFill( 00411 INT32 tSeed = 0, 00412 BOOL tTileable = 1 00413 fixed16 tSquash = 0, 00414 fixed16 tGraininess = 1, 00415 fixed16 tGravity = 0, 00416 ) 00417 00418 Author: Alex_Bligh (Xara Group Ltd) <camelotdev@xara.com> 00419 Created: 12/9/94 00420 Inputs: INT32 Seed (seed of the random number generator) 00421 BOOL Tileable (true if the fractal can be tiled) 00422 fixed16 XSquash (amount of Squashing, 1.0 = none) 00423 fixed16 Graininess (fractal's graininess) 00424 fixed16 Gravity (gravitational pull towards graininess of the centre) 00425 Outputs: Constructs a PlasmaFractalFill 00426 Returns: TRUE if succeeded, FALSE if failed 00427 Purpose: Constructor for a PlasmaFractalFill 00428 Errors: - 00429 SeeAlso: - 00430 00431 Tileable fractals are just as fabby as normal fractals except they can be tiled. It's in here 00432 as a flag partle as you might want one that wasn't tileable (why?) but tiled fractals in 00433 general exhibit less assymetry than non-tiled fractals, especially with zero gravity. 00434 00435 If Squash is zero, no squashing is done. If it is negative, the image is XSquashed, and if it 00436 is positive, the image is YSquashed. Squashing only works if Tileable is set (unless you want 00437 the special grey morass effect). 00438 00439 ********************************************************************************************/ 00440 00441 PlasmaFractalFill::PlasmaFractalFill( 00442 INT32 tSeed, 00443 BOOL tTileable, 00444 fixed16 tSquash, 00445 fixed16 tGraininess, //0..about 32 00446 fixed16 tGravity //0..about 255 00447 ) 00448 { 00449 MaxPotential = (1<<16)-1; // *** DON'T CHANGE WITHOUT CHANGING THE 00450 // EQUATE IN FastFractalAdjust 00451 00452 pBitmap = NULL; 00453 Seed = tSeed; 00454 ForceCornersToZero = (tGravity != 0); 00455 Tileable = tTileable; 00456 Squash = tSquash; 00457 00458 if (Tileable) 00459 { 00460 CoordinateMask = MAX_FRACTAL_COORD - 1; 00461 } 00462 else 00463 { 00464 CoordinateMask = 0xffffffff; 00465 } 00466 00467 xGravity = yGravity = (UINT32) (tGravity.MakeDouble() * MaxPotential); 00468 xGraininess = yGraininess = (UINT32) (tGraininess.MakeDouble() * 8 + 0.5); 00469 00470 if (Squash > 0) 00471 { 00472 xGravity = (UINT32) ( ((tGravity.MakeDouble() * MaxPotential) / Squash.MakeDouble())); 00473 xGraininess = (UINT32) ( ((tGraininess * 8 + 0.5) / Squash).MakeLong()); 00474 } 00475 else if (Squash<0) 00476 { 00477 yGravity = (UINT32) ( ((tGravity.MakeDouble() * MaxPotential) / (-Squash.MakeDouble()))); 00478 yGraininess = (UINT32) ( ((tGraininess * 8 + 0.5) / (-Squash)).MakeLong()); 00479 } 00480 00481 // Limit Graininess to prevent overflow in Adjust 00482 if ( xGraininess < 0 ) xGraininess = 0; 00483 if ( xGraininess > 0xffff ) xGraininess = 0xffff; 00484 if ( yGraininess < 0 ) yGraininess = 0; 00485 if ( yGraininess > 0xffff ) yGraininess = 0xffff; 00486 00487 } 00488 00489 void PlasmaFractalFill::Test(KernelBitmap * pB, 00490 INT32 tSeed, 00491 BOOL tTileable, 00492 fixed16 tSquash, 00493 fixed16 tGraininess, 00494 fixed16 tGravity) 00495 00496 { 00497 PlasmaFractalFill pf(tSeed?tSeed:rand(), tTileable, tSquash, tGraininess, tGravity); 00498 pf.DoFill(pB); 00499 } 00500 00501 /******************************************************************************************** 00502 00503 > static INT32 PlasmaFractalFill::GetDimension(INT32 x, INT32 y) 00504 00505 Author: Alex_Bligh (Xara Group Ltd) <camelotdev@xara.com> 00506 Created: 12/9/94 00507 Inputs: x and y are pixel width/height of the (detransformed) bounding box you have to 00508 fill. 00509 Outputs: None 00510 Returns: Dimension of virtual fractal needed 00511 Purpose: Returns required dimension of virtual fractal 00512 Errors: - 00513 SeeAlso: PlasmaFractalFill::DoFill 00514 00515 The fractal bitmap must be power of 2 in width and it must be square (i.e. width = height) for 00516 resolution independent bitmaps to be created reliably. Given that most fractals will not be 00517 exactly this size, the expected use is that they will be generated at one such size and a scaled 00518 sprite plot be done. 00519 00520 This routine returns a suitable set of 'virtual fractal coordinate bounds' (i.e. a size of the 00521 theoretical fractal bitmap in pixels if it were plotted unclipped). This is a power of two. This 00522 should be used to work out the clipping calculations as the only coordinates the routines know how 00523 to deal with are of this sort. 00524 00525 If you ask for an inordinately large fractal the return value may be smaller than the input values. 00526 Presently this value squared is larger than the physical memory capacity of the platform though 00527 00528 You can, of course, use a smaller dimension if (for instance) memory requirements mean you must, 00529 but it must be a power of 2. 00530 00531 ********************************************************************************************/ 00532 00533 INT32 PlasmaFractalFill::GetDimension(INT32 x, INT32 y) 00534 { 00535 INT32 Dimension=4; // Minimum 00536 while ((Dimension < MAX_FRACTAL_COORD) && (( Dimension < x) || (Dimension < y))) 00537 Dimension=Dimension<<1; 00538 return(Dimension); 00539 } 00540 00541 /******************************************************************************************** 00542 00543 > BOOL PlasmaFractalFill::DoFill(KernelBitmap * pBitmap, INT32 Dimension, INT32 tOx, INT32 tOy) 00544 00545 Author: Alex_Bligh (Xara Group Ltd) <camelotdev@xara.com> 00546 Created: 12/9/94 00547 Inputs: pBitmap = ^KernelBitmap to fill, 00548 Dimension=dimension (see notes) 00549 (Ox, Oy) = origin of bitmap passed inside vitual bitmap 00550 Outputs: Fills bitmap 00551 Returns: TRUE if succeeded, FALSE if failed 00552 Purpose: Fills the 8 bit bitmap passed with the fractal requested 00553 Errors: - 00554 SeeAlso: PlasmaFractalFill::GetDimension 00555 00556 Dimension must be power of 2 for resolution independent bitmaps to be created reliably. It 00557 describes the length of the sides of the square bitmap into which the unclipped fractal would 00558 be plotted. You may pass this routine another bitmap (need not be square) into which the the 00559 fractal is plotted (clipped). Pixels outside the fractal will not be filled. i.e.: 00560 00561 00562 0____________________D 00563 D| |D 00564 | | 00565 | ____________|_____ 00566 | | | | 00567 | | + + + + + | | 00568 | | | | 00569 | | + + + + + | | <--- The bitmap you pass (dimensions obtained from the bitmap) 00570 | Oy|___________|____| 00571 | Ox | 00572 | | 00573 0|____________________|0 <--- Virtual fractal bitmap dimension D 00574 0 D 00575 00576 00577 The area marked with plus signs is plotted. 00578 00579 ********************************************************************************************/ 00580 00581 00582 BOOL PlasmaFractalFill::DoFill(KernelBitmap * theBitmap, INT32 Dimension, INT32 tOx, INT32 tOy ) 00583 { 00584 INT32 i; 00585 INT32 rnd[4]; 00586 BitmapInfo Info; 00587 00588 if ((pBitmap = theBitmap) == NULL) 00589 { 00590 ENSURE(FALSE,"PlasmaFractalFill::DoFill pBitmap is NULL"); 00591 return(FALSE); 00592 } 00593 00594 pBitmap->ActualBitmap->GetInfo(&Info); 00595 Width = Info.PixelWidth; 00596 Height = Info.PixelHeight; 00597 Ox = tOx; 00598 Oy = tOy; 00599 00600 // Dimension zero means 1:1 00601 if (Dimension==0) Dimension=GetDimension(Width,Height); 00602 00603 // Bump width and height up 00604 EigenValue = 0; 00605 while ((Dimension<<EigenValue) < MAX_FRACTAL_COORD) 00606 { 00607 EigenValue++; 00608 } 00609 00610 // This checks Width=Height and they are both MAX_FRACTAL_COORD. If all kernel bitmaps passed in 00611 // fulful this condition then on each subdivision the coordinates will be the same, so the random 00612 // peterurbation will be the same, so the bitmap will be the same. Wow! And thats the fabbiness of 00613 // psuedorandom numbers for you. 00614 if (Dimension<<EigenValue != MAX_FRACTAL_COORD) 00615 TRACE( _T("PlasmaFractalFill::DoFill only generates resolution independent bitmaps if Width==Height==2^n \n")); 00616 00617 // seed random number generator 00618 SetSeed(Seed); 00619 00620 // set corner potentials randomly 00621 for (i=0; i<4; i++) rnd[i]=(INT32)(((((UINT32)GetRandom())>>16)*(UINT32)MaxPotential+(1<<15))>>16); 00622 00623 // When we are not tiling we want to make quite sure we get enough contrast. We take the contrast range across one 00624 // diagonal and make the other diagonal have at least one minus that range. When we are tiling we make the value 00625 // either +1 or -1 00626 00627 if (Tileable && !(ForceCornersToZero)) 00628 { 00629 rnd[0]=0;// (rnd[0]>(MaxPotential/2))?MaxPotential:0; this makes them predictable 00630 } 00631 else 00632 { 00633 INT32 diagdiff1; 00634 INT32 diagdiff2; 00635 short a,b; 00636 // Calculate contrast across diagonals 00637 diagdiff1=Abs(rnd[0]-rnd[2]); 00638 diagdiff2=Abs(rnd[1]-rnd[3]); 00639 // Find smallest one 00640 if (diagdiff1<diagdiff2) 00641 { 00642 a=(rnd[0]<rnd[2])?0:2; 00643 b=2-a; // ab diagonal is 0-2 or 2-0 00644 } 00645 else 00646 { 00647 a=(rnd[1]<rnd[3])?1:3; 00648 b=4-a; // ab diagonal is 1-3 00649 } 00650 // set to how much we want to increase diagdiff by. 00651 diagdiff2=(MaxPotential-diagdiff1) /*new*/ - diagdiff2 /*current*/; 00652 if (diagdiff2>0) 00653 { 00654 rnd[a]-=diagdiff2/2; 00655 rnd[b]+=diagdiff2/2; 00656 } 00657 } 00658 00659 RecursionLevel = 0; 00660 SubDivide(0,0, MAX_FRACTAL_COORD, MAX_FRACTAL_COORD, 00661 ForceCornersToZero?0:rnd[Tileable?0:0], ForceCornersToZero?0:rnd[Tileable?0:1], 00662 ForceCornersToZero?0:rnd[Tileable?0:2], ForceCornersToZero?0:rnd[Tileable?0:3], TRUE ); 00663 00664 return(TRUE); 00665 } 00666 00667