fracfill.cpp

Go to the documentation of this file.
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 

Generated on Sat Nov 10 03:45:21 2007 for Camelot by  doxygen 1.4.4