cmxdcobj.cpp

Go to the documentation of this file.
00001 // $Id: cmxdcobj.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 // CMXExportDC object output routines
00099 
00100 #include "camtypes.h"
00101 #include "cmxexdc.h"
00102 #include "cmxrendr.h"
00103 #include "cmxform.h"
00104 //#include "docrect.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00105 //#include "attrmgr.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00106 //#include "fillval.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00107 #include "colcontx.h"
00108 //#include "nev.h"
00109 //#include "cmxres.h"
00110 #include "fillramp.h"
00111 
00112 
00113 /********************************************************************************************
00114 
00115 >   BOOL CMXExportDC::WriteBlankAttributes(INT32 Tag)
00116 
00117     Author:     Ben_Summers (Xara Group Ltd) <camelotdev@xara.com>
00118     Created:    16/07/96
00119     Inputs:     render region
00120     Returns:    success
00121     Purpose:    writes a blank attribute tag and contents, which specifies nothing but
00122                 keeps everything happy
00123 
00124 ********************************************************************************************/
00125 
00126 BOOL CMXExportDC::WriteBlankAttributes(INT32 Tag)
00127 {
00128     // start the tag
00129     if(Tag != -1)
00130         if(!StartTag(Tag))
00131             return FALSE;
00132 
00133     // write the attribute mask thingy
00134     WriteByte(0);
00135 
00136     // end the tag
00137     if(Tag != -1)
00138         if(!EndTag())
00139             return FALSE;
00140     
00141     return TRUE;
00142 }
00143 
00144 
00145 /********************************************************************************************
00146 
00147 >   BOOL CMXExportDC::WriteAttributes(CMXRenderRegion *pReg, INT32 Tag, DocCoord *Coords, INT32 NumCoords, BOOL ForceNoFill = FALSE)
00148 
00149     Author:     Ben_Summers (Xara Group Ltd) <camelotdev@xara.com>
00150     Created:    01/07/96
00151     Inputs:     render region
00152     Returns:    success
00153     Purpose:    Writes the attributes to the DC.
00154                 if ForceNoFill == TRUE, the path will not be filled whatever the colour
00155 
00156 ********************************************************************************************/
00157 
00158 BOOL CMXExportDC::WriteAttributes(CMXRenderRegion *pReg, INT32 Tag, DocCoord *Coords, INT32 NumCoords, BOOL ForceNoFill)
00159 {
00160     // give lenses a chance
00161     BOOL WasLens = FALSE;
00162     if(!WriteAttrCheckLens(pReg, Tag, Coords, NumCoords, &WasLens))
00163         return FALSE;
00164 
00165     // now did the lens checking bit output something?
00166     if(WasLens)
00167         return TRUE;        // yep, so we don't want to output them too
00168 
00169     // start the tag
00170     if(Tag != -1)
00171         if(!StartTag(Tag))
00172             return FALSE;
00173 
00174     // write the attribute mask thingy
00175     WriteByte((ForceNoFill?0:cmxRENDATTRMASK_FILL) | cmxRENDATTRMASK_OUTLINE);
00176 
00177     // write the fill specification
00178     if(!ForceNoFill)
00179         if(!WriteFillSpec(pReg, Coords, NumCoords, ForceNoFill))
00180             return FALSE;
00181 
00182     // write the outline specification
00183     if(!WriteOutlineSpec(pReg))
00184         return FALSE;
00185 
00186     // end the tag
00187     if(Tag != -1)
00188         if(!EndTag())
00189             return FALSE;
00190     
00191     return TRUE;
00192 }
00193 
00194 
00195 /********************************************************************************************
00196 
00197 >   BOOL CMXExportDC::WriteFillType(WORD FillID)
00198 
00199     Author:     Ben_Summers (Xara Group Ltd) <camelotdev@xara.com>
00200     Created:    04/07/96
00201     Inputs:     fill type
00202     Returns:    success
00203     Purpose:    Writes the fill type to the DC
00204 
00205 ********************************************************************************************/
00206 
00207 BOOL CMXExportDC::WriteFillType(WORD FillID)
00208 {
00209     ExportFile->write(&FillID, sizeof(FillID));
00210 
00211     return TRUE;
00212 }
00213 
00214 
00215 /********************************************************************************************
00216 
00217 >   BOOL CMXExportDC::WriteFillSpec(CMXRenderRegion *pReg)
00218 
00219     Author:     Ben_Summers (Xara Group Ltd) <camelotdev@xara.com>
00220     Created:    04/07/96
00221     Inputs:     render region
00222     Returns:    success
00223     Purpose:    Writes the fill specifcation to the DC
00224 
00225 ********************************************************************************************/
00226 
00227 BOOL CMXExportDC::WriteFillSpec(CMXRenderRegion *pReg, DocCoord *Coords, INT32 NumCoords, BOOL NoFill)
00228 {
00229     // tag the start
00230     if(!StartNestedTag(cmxTAG_RenderAttr_FillSpec))
00231         return FALSE;
00232 
00233     if(NoFill)
00234     {
00235         if(!WriteFillType(cmxFILLID_NONE))
00236             return FALSE;
00237     }
00238     else
00239     {
00240         // get the attribute
00241         FillGeometryAttribute *pFillGeometry
00242                 = (FillGeometryAttribute *)pReg->GetCurrentAttribute(ATTR_FILLGEOMETRY);
00243 
00244         // check it out
00245         ERROR2IF(pFillGeometry == 0, FALSE, "No fill geometry");
00246         ERROR3IF(!pFillGeometry->IsKindOf(CC_RUNTIME_CLASS(FillGeometryAttribute)), "not one of them there fill geometries");
00247 
00248         // right then, decide what to do with the thingy
00249         if(pFillGeometry->IsAFlatFill())
00250         {
00251             // flat fill -- check to see if it's transparent
00252             DocColour *pCol = pFillGeometry->GetStartColour();
00253             ERROR2IF(pCol == NULL, FALSE, "not one of your earth colours");
00254             if(pCol->IsTransparent())
00255             {
00256                 // it's transparent, write no fill
00257                 if(!WriteFillType(cmxFILLID_NONE))
00258                     return FALSE;
00259             }
00260             else
00261             {
00262                 // it's OK, it is. Write it.
00263                 if(!WriteFillType(cmxFILLID_UNIFORM)
00264                     || !WriteFillSpecFlat(pReg, pFillGeometry))
00265                     return FALSE;
00266             }
00267         }
00268         else if(pFillGeometry->IsAKindOfBitmapFill())
00269         {
00270             // bitmap type fill -- do nothing for now
00271             if(!WriteFillType(9) //cmxFILLID_COLPATTERN)
00272                 || !WriteFillSpecBitmap(pReg, pFillGeometry, Coords, NumCoords))
00273                 return FALSE;
00274         }
00275         else if(pFillGeometry->IsAGradFill())
00276         {
00277             // grad fill
00278             if(!WriteFillType(cmxFILLID_FOUNTAIN)
00279                 || !WriteFillSpecGrad(pReg, pFillGeometry, Coords, NumCoords))
00280                 return FALSE;
00281         }
00282         else
00283         {
00284             // catch all -- write a no fill thingy
00285             if(!WriteFillType(cmxFILLID_NONE))
00286                 return FALSE;
00287         }
00288     }
00289 
00290     // end the tag
00291     if(!EndNestedTag())
00292         return FALSE;
00293     
00294     // write the end tag
00295     if(!WriteMinEndTag())
00296         return FALSE;
00297 
00298     return TRUE;
00299 }
00300 
00301 /********************************************************************************************
00302 
00303 >   BOOL CMXExportDC::WriteFillSpecFlat(CMXRenderRegion *pReg, FillGeometryAttribute *pFill)
00304 
00305     Author:     Ben_Summers (Xara Group Ltd) <camelotdev@xara.com>
00306     Created:    04/07/96
00307     Inputs:     render region
00308     Returns:    success
00309     Purpose:    Writes the flat fill specifcation to the DC
00310 
00311 ********************************************************************************************/
00312 
00313 BOOL CMXExportDC::WriteFillSpecFlat(CMXRenderRegion *pReg, FillGeometryAttribute *pFill)
00314 {
00315     // get the colour
00316     DocColour *pFillColour = pFill->GetStartColour();
00317     ERROR2IF(pFillColour == NULL, FALSE, "Wibble. No fill colour on this here flat fill");
00318 
00319     // find a reference for it
00320     WORD FillReference = GetColourReference(pFillColour);
00321 
00322     // write some stuff
00323     struct {
00324         WORD FillReference;
00325         WORD ScreenReference;
00326     } filldef = {FillReference, cmxSCREENREFERENCE};
00327 
00328     if(!WriteNestedTag(cmxTAG_RenderAttr_FillSpec_Uniform, &filldef, sizeof(filldef))
00329         || !WriteMinEndTag())
00330         return FALSE;
00331 
00332     return TRUE;
00333 }
00334 
00335 
00336 /********************************************************************************************
00337 
00338 >   BOOL CMXExportDC::WriteFillSpecGrad ( CMXRenderRegion *pReg,
00339                                           FillGeometryAttribute *pFill,
00340                                           DocCoord *Coords,
00341                                           INT32 NumCoords )
00342 
00343     Author:     Ben_Summers (Xara Group Ltd) <camelotdev@xara.com>
00344     Created:    10/07/96
00345     Inputs:     render region
00346     Returns:    success
00347     Purpose:    Writes the grad fill specifcation to the DC
00348 
00349 ********************************************************************************************/
00350 
00351 BOOL CMXExportDC::WriteFillSpecGrad ( CMXRenderRegion *pReg,
00352                                       FillGeometryAttribute *pFill,
00353                                       DocCoord *Coords,
00354                                       INT32 NumCoords )
00355 {
00356     DocColour   StartColour;                // The first colour in the fill.
00357     DocColour   EndColour;                  // The end colour of the fill.
00358     DocColour   MidColour;                  // Only used to approximate four colour fills.
00359     BOOL        Radial          = FALSE;    // Is it a radial fill?
00360     BOOL        FourColour      = FALSE;    // Is it a four colour fill?
00361 
00362     // is this a radial type derivative?
00363     if ( pFill->IsARadialFill () )
00364     {
00365         // We need to swap the colours over as Corel has a different idea
00366         // of start and finish colours to Camelot.
00367         StartColour = *( pFill->GetEndColour () );
00368         EndColour   = *( pFill->GetStartColour () );
00369 
00370         Radial = TRUE;
00371     }
00372     else if ( pFill->IsAConicalFill () || pFill->IsASquareFill () )
00373     {
00374         // Set up the colour values.
00375         StartColour = *( pFill->GetStartColour () );
00376         EndColour   = *( pFill->GetEndColour () );
00377 
00378         Radial = TRUE;
00379     }
00380     else if ( pFill->IsAFourColFill () )
00381     {
00382         // Set up the start and end colours as usual, but create a third colour value to
00383         // help fake the four colour fill.
00384         StartColour = *( pFill->GetStartColour () );
00385         EndColour   = *( pFill->GetEndColour3 () );
00386         MidColour.Mix ( pFill->GetEndColour (), pFill->GetEndColour2 (), 0.5f, NULL, FALSE, NULL );
00387 
00388         // Reset the four colour flag.
00389         FourColour  = TRUE;
00390     }
00391     else if ( pFill->IsAThreeColFill () )
00392     {
00393         // Set up the colour values. I'm blending the colours of the two endpoints
00394         // to get a better approximation of a three colour fill.
00395         StartColour = *( pFill->GetStartColour () );
00396         EndColour.Mix ( pFill->GetEndColour (), pFill->GetEndColour2 (), 0.5f, NULL, FALSE, NULL );
00397     }
00398     else
00399     {
00400         // Default behaviour: Store the start and end colours.
00401         StartColour = *( pFill->GetStartColour () );
00402         EndColour   = *( pFill->GetEndColour () );
00403     }
00404 
00405     // Find references for the color, and sort out the fill effect.
00406     WORD    StartColReference   = GetColourReference ( &StartColour );
00407     WORD    EndColReference     = GetColourReference ( &EndColour );
00408     WORD    MidColReference     = 0;
00409     WORD    FillMode            = cmxFILLMODE_RGB;
00410 
00411     // Get a colour reference to MidColour if it's been declared. (i.e. the fill is a
00412     // four colour fill.)
00413     if ( FourColour )
00414     {
00415         MidColReference = GetColourReference ( &MidColour );
00416     }
00417 
00418     // get the fill effect attribute
00419     FillEffectAttribute *pFillEff =
00420         (FillEffectAttribute *)pReg->GetCurrentAttribute(ATTR_FILLEFFECT);
00421     ERROR2IF(pFillEff == NULL || !pFillEff->IsKindOf(CC_RUNTIME_CLASS(FillEffectAttribute)), FALSE, "not a fill effect");
00422     BOOL Rainbow = FALSE, Alt = FALSE;
00423     if(IS_A(pFillEff, FillEffectRainbowAttribute))
00424         Rainbow = TRUE;
00425     if(IS_A(pFillEff, FillEffectAltRainbowAttribute))
00426         Rainbow = Alt = TRUE;
00427 
00428     if(Rainbow)
00429     {
00430         // right, we need to work out which direction we want the colour to go
00431         // around the HSV colour wheel. Convert both colours to HSV.
00432         ColourContext *Conv = ColourContext::GetGlobalDefault(COLOURMODEL_HSVT);
00433 
00434         ColourHSVT StartC;
00435         ColourHSVT EndC;
00436 
00437         Conv->ConvertColour( &StartColour, (ColourGeneric *)&StartC);
00438         Conv->ConvertColour( &EndColour, (ColourGeneric *)&EndC);
00439 
00440         // compare the hues and work out a difference
00441         double Difference = (EndC.Hue.MakeDouble()) - (StartC.Hue.MakeDouble());
00442 
00443         BOOL ClockWise = (Difference < 0);
00444 
00445         if(fabs(Difference) >= 0.5)
00446             ClockWise = !ClockWise;
00447 
00448         if(Alt = TRUE)
00449             ClockWise = !ClockWise;
00450         
00451         // set the correct thingy attribute
00452         if(ClockWise)
00453             FillMode = cmxFILLMODE_HSB_CW;
00454         else
00455             FillMode = cmxFILLMODE_HSB_CCW;
00456     }
00457 
00458     // find the start and end coords of the fill
00459     DocCoord StartPoint = *( pFill->GetStartPoint() );
00460     DocCoord EndPoint;
00461 
00462     // Graeme (18-2-00) - Modifications to handle three and four colour fills better.
00463     if ( pFill->IsAFourColFill () )
00464     {
00465         // Use EndPoint3 to get a diagonal across the bounding box.
00466         EndPoint = *( pFill->GetEndPoint3 () );
00467     }
00468     else if ( pFill->IsAThreeColFill () )
00469     {
00470         // Merge the existing two endpoints to get the correct endpoint for the fill.
00471         EndPoint = *( pFill->GetEndPoint () ) +
00472                    *( pFill->GetEndPoint2 () ) -
00473                    *( pFill->GetStartPoint () );
00474     }
00475     else
00476     {
00477         // Default: Use the standard endpoint.
00478         EndPoint = *( pFill->GetEndPoint () );
00479     }
00480 
00481 
00482     // find the corel bounding box of the path we're about to fiddle with
00483     DocRect cBBox;
00484     CalcCorelBBox(Coords, NumCoords, &cBBox);
00485 
00486     // run the chicken out clause for extreme cases I can't be bothered to handle
00487     if ( StartPoint == EndPoint || cBBox.Width () == 0 || cBBox.Height () == 0 )
00488         return WriteFillSpecFlat(pReg, pFill);
00489 
00490     // calc the central coord
00491     DocCoord Centre;
00492     Centre.x = cBBox.lo.x + cBBox.Width() / 2;
00493     Centre.y = cBBox.lo.y + cBBox.Height() / 2;
00494 
00495     // calc the fill parameters
00496 
00497     // first work out the distance between the start and end points
00498     double dx = EndPoint.x - StartPoint.x;
00499     double dy = EndPoint.y - StartPoint.y;
00500     double PointDist = sqrt(((dx * dx) + (dy * dy)));
00501 
00502     // now we need to work out the angle of the fill
00503     double Angle = atan2(dy, dx);
00504         // atan2 is defined if dx == 0
00505         // but not at the origin -- however we've filtered out this case
00506 
00507     INT32 StartPercent;
00508     INT32 EndPercent;
00509 
00510     cmxFillBase1 fb1;
00511     fb1.Padding = 0;
00512     cmxFillBase2 fb2;
00513     fb2.XOffset = 0;
00514     fb2.YOffset = 0;
00515 
00516     if(Radial)
00517     {
00518         //------------------------------------------------- radial type fill calucations
00519 
00520         // ...and for my first trick, I will calculate the X and Y
00521         // offsets of the centre of this fill
00522         DocCoord dCentre = StartPoint - Centre;
00523         fb2.XOffset = (dCentre.x * 100) / cBBox.Width();
00524         fb2.YOffset = (dCentre.y * 100) / cBBox.Height();
00525         // get these values within the required range
00526         if(fb2.XOffset > 100) fb2.XOffset = 100;
00527         if(fb2.XOffset < -100) fb2.XOffset = -100;
00528         if(fb2.YOffset > 100) fb2.YOffset = 100;
00529         if(fb2.YOffset < -100) fb2.YOffset = -100;
00530 
00531         // now this is the scary bit. Corel use a complex method for specifing
00532         // their radial type fills. 
00533         double w = cBBox.Width(), h = cBBox.Height();
00534         double bboxdiagonal = sqrt(w*w + h*h);
00535         double cdx = dCentre.x, cdy = dCentre.y;
00536         double cendist = sqrt(cdx*cdx + cdy*cdy);
00537         double BodgeFactor = 1 + (cendist / (bboxdiagonal / 2));
00538         double targetradius = PointDist / BodgeFactor;
00539         double Pad = 50 * (1 - (2 * targetradius) / bboxdiagonal);
00540         // munge the Pad value to the allowed range
00541         if(Pad < 0) Pad = 0;
00542         if(Pad > 44) Pad = 44;
00543         // and bung in the structure for export
00544         fb1.Padding = (WORD)Pad;
00545 
00546         // set the percentages to 0
00547         StartPercent = 0;
00548         EndPercent = 0;
00549 
00550         // if it's a square fill, swap the colours over
00551         if(pFill->IsASquareFill())
00552         {
00553             WORD t = StartColReference;
00554             StartColReference = EndColReference;
00555             EndColReference = t;
00556         }
00557     } // if ( Radial )
00558 
00559     else // if ( !Radial )
00560     {
00561         //------------------------------------------------- linear type fill calucations
00562 
00563         // now work out the coords of the intersection of a line through the
00564         // centre of the bbox and the edges of the bbox
00565 
00566         // right then, we want the angle within 0 and PI/2
00567         double cAngle = Angle;
00568         INT32 cAR = 0;
00569         while(cAngle > (PI/2))
00570         {
00571             cAngle -= PI/2;
00572             cAR++;
00573         }
00574 
00575         // luckily, everything is symmetrical so all we need do is calc the
00576         // intersect in the top quadrant.
00577         INT32 qdx = cBBox.Width() / 2;
00578         INT32 qdy = cBBox.Height() / 2;
00579         if((cAR & 1) != 0)
00580         {
00581             // we rotated by an odd multiple of PI/2 radians. Rotate the box as well.
00582             INT32 t = qdy;
00583             qdy = qdx;
00584             qdx = t;
00585         }
00586         DocCoord i;
00587         if(qdy == 0)
00588         {
00589             // special case to avoid division by tan(0) = 0
00590             i.x = qdx;
00591             i.y = 0;
00592         }
00593         else
00594         {
00595             double predqdy = qdx * tan(cAngle);
00596             if(predqdy > qdy)
00597             {
00598                 // intersects on the horizontal
00599                 i.x = (INT32)(((double)qdy) / tan(cAngle));
00600                 i.y = qdy;
00601             }
00602             else
00603             {
00604                 // intersects on the vertical
00605                 i.x = qdx;
00606                 i.y = (INT32)(((double)qdx) * tan(cAngle));
00607             }
00608         }
00609 
00610         // i is the offset from the centre of the bbox where the line of the
00611         // grad fill rotated to the top right quadrent intersects with the
00612         // rectange of the bounding box.
00613 
00614         // now we need to work out the percentages along this line of the start
00615         // and stop bit.
00616         INT32 linelength = (INT32)sqrt(((double)(i.x) * (double)(i.x)) + ((double)(i.y) * (double)(i.y)));
00617         // chicken out clause #2
00618         if(linelength == 0)
00619             return WriteFillSpecFlat(pReg, pFill);
00620 
00621         // work out the start and end points as offsets from the centre point
00622         DocCoord    dStartPoint = StartPoint - Centre;
00623         DocCoord    dEndPoint   = EndPoint - Centre;
00624 
00625         // rotate these points around the origin
00626         // TODO: do the rotation myself, instead of going through matrices
00627         Matrix Mat(((- Angle) * 360.0) / (2*PI));   // why can't people use proper measure? 
00628         Mat.transform(&dStartPoint);
00629         Mat.transform(&dEndPoint);
00630 
00631         // find out the smallest distance from the end of the line
00632         INT32 dStart = dStartPoint.x + linelength;
00633         INT32 dEnd = linelength - dEndPoint.x;
00634         if(dStart < 0) dStart = 0;
00635         if(dEnd < 0) dEnd = 0;
00636         INT32 dPad;     // smallest of dStart and dEnd
00637         if(dStart < dEnd)
00638             dPad = dStart;
00639         else
00640             dPad = dEnd;
00641 
00642         INT32 padval = (dPad * 100) / (linelength * 2);
00643         if(padval < 0) padval = 0;
00644         if(padval > 44) padval = 44;
00645         fb1.Padding = (WORD)padval;
00646         
00647         // right, now munge the line length value to take account of this padding
00648         linelength -= dPad;
00649 
00650         // right then, we can now simply compare the x coords of the start and end
00651         // with the linelength above to find the start and end percentages
00652         StartPercent = (INT32)((dStartPoint.x + linelength) * 100);
00653         EndPercent = (INT32)((dEndPoint.x + linelength) * 100);
00654         StartPercent /= (linelength * 2);
00655         EndPercent /= (linelength * 2);
00656 
00657         // sanity check the numbers so Corel is happy
00658         if(StartPercent < 0) StartPercent = 0;
00659         if(StartPercent > 100) StartPercent = 100;
00660         if(EndPercent < 0) EndPercent = 0;
00661         if(EndPercent > 100) EndPercent = 100;
00662         if(EndPercent < StartPercent)
00663         {
00664             INT32 t = StartPercent;
00665             StartPercent = EndPercent;
00666             EndPercent = t;
00667         }
00668     } // else if ( !Radial )
00669 
00670     // I allege we now know enough to get this thing to the file
00671     // start the tag
00672     if(!StartNestedTag(cmxTAG_RenderAttr_FillSpec_Fountain_Base))
00673         return FALSE;
00674 
00675     if(pFill->IsARadialFill())
00676         fb1.Type = cmxFOUNTAINTYPE_RADIAL;
00677     else if(pFill->IsASquareFill())
00678         fb1.Type = cmxFOUNTAINTYPE_SQUARE;
00679     else if(pFill->IsAConicalFill())
00680         fb1.Type = cmxFOUNTAINTYPE_CONICAL;
00681     else
00682         fb1.Type = cmxFOUNTAINTYPE_LINEAR;
00683     fb1.Screen = cmxSCREENREFERENCE;
00684     // padding set earlier
00685 
00686     // offsets set earlier
00687     fb2.StepCount = 0;
00688     fb2.FillMode = FillMode;
00689     fb2.RateMethod = 0;
00690     fb2.RateValue = 50;         // 50% is the centre, a commonly held belief
00691 
00692     if(!Radial)
00693     {
00694         if(FillMode == cmxFILLMODE_RGB)
00695         {
00696             // if it's an RGB mix in a linear fill, we can accurately
00697             // specify the fill by using a custom fill mode, and specifing
00698             // the end points presicely.
00699             fb2.FillMode = cmxFILLMODE_CUSTOM;
00700         }
00701         else
00702         {
00703             // if it's not an RGB mix, we need to approximate the fill using the
00704             // padding (set already) as we can't specify where we want the
00705             // colours to start and finish along the line of the fill
00706             StartPercent = 0;
00707             EndPercent = 0;
00708         }
00709     }
00710 
00711     // write the data
00712     ExportFile->write(&fb1, sizeof(fb1));
00713 
00714     WriteAngle(Angle);
00715 
00716     if(ThirtyTwoBit)
00717     {
00718         ExportFile->write(&fb2, sizeof(fb2));
00719     }
00720     else
00721     {
00722         // OK, so this way is a bit of a bodge
00723         cmxFillBase2_16 b = {0, (SWORD)fb2.XOffset, (SWORD)fb2.YOffset,
00724             fb2.StepCount, fb2.FillMode};
00725 
00726         ExportFile->write(&b, sizeof(b));
00727     }
00728 
00729     // end the tag
00730     if(!EndNestedTag())
00731         return FALSE;
00732 
00733     // write the colours
00734     if(!StartNestedTag(cmxTAG_RenderAttr_FillSpec_Fountain_Color))
00735         return FALSE;
00736 
00737     cmxGradFillColour   col;
00738     ColourRamp          *pRamp  = pFill->GetColourRamp ();
00739     WORD                NCol    = 2 + ( ( StartPercent != 0 ) ? 1 : 0 ) +
00740                                   ( ( EndPercent != 100 ) ? 1 : 0 ) + ( FourColour ? 1 : 0 );
00741 
00742     // If the fill has a colour ramp, calculate the number of entries within it.
00743     if ( pRamp != NULL )
00744     {
00745         UINT32  FirstIndex  = 0;
00746         UINT32  LastIndex   = 0;
00747 
00748         // Call a member function to get the indices to the first and last elements within
00749         // the colour ramp.
00750         pRamp->GetIndexRange ( &FirstIndex, &LastIndex );
00751 
00752         // Add the difference between these to the number of colours to be exported. The +1
00753         // in this sum is necessary since the index values include the number of the element
00754         // that they represent. E.g. for a one element ramp, we have 1024 for both the first
00755         // and last items. Thus, without the +1, we would get 0 colours in the index.
00756         NCol += 1 + LastIndex - FirstIndex;
00757     }
00758 
00759     // Write the number of colours.
00760     ExportFile->write ( &NCol, sizeof ( NCol ) );
00761 
00762     // Write as many colours as necessary to get accurate fills. First the start colour.
00763     if(StartPercent != 0)
00764     {
00765         col.ColRef = StartColReference;
00766         col.ColPos = 0;
00767         ExportFile->write(&col, sizeof(col));
00768     }
00769 
00770     col.ColRef = StartColReference;
00771     col.ColPos = (WORD)StartPercent;
00772     ExportFile->write(&col, sizeof(col));
00773 
00774     // If it's a four colour fill, add the middle colour to the mix.
00775     if ( FourColour )
00776     {
00777         col.ColRef  = MidColReference;
00778         col.ColPos  = ( WORD ) ( ( StartPercent + EndPercent ) / 2 );
00779         ExportFile->write ( &col, sizeof ( col ) );
00780     }
00781 
00782     // Process the colour ramp.
00783     if ( pRamp != NULL )
00784     {
00785         ColRampItem *pColour = pRamp->GetFirstCol ();
00786 
00787         // Write in the ramp's colour values.
00788         while ( pColour != NULL )
00789         {
00790             cmxGradFillColour   RampColour;
00791 
00792             // Get the position of the colour in the fill.
00793             RampColour.ColPos   = ( WORD ) ( 100.0f * pColour->GetPosition () );
00794             RampColour.ColRef   = GetColourReference ( &( pColour->GetColour () ) );
00795 
00796             // Write the colour.
00797             ExportFile->write ( &RampColour, sizeof ( RampColour ) );
00798 
00799             // Increment the pointer onto the next item.
00800             pColour = pRamp->GetNextCol ( pColour );
00801         }
00802     }
00803 
00804     // And finally the end colour.
00805     col.ColRef = EndColReference;
00806     col.ColPos = (WORD)EndPercent;
00807     ExportFile->write(&col, sizeof(col));
00808 
00809     if(EndPercent != 100)
00810     {
00811         col.ColPos = 100;
00812         ExportFile->write(&col, sizeof(col));
00813     }
00814 
00815     if(!EndNestedTag())
00816         return FALSE;
00817 
00818     // write the ending bit
00819     if(!WriteMinEndTag())
00820         return FALSE;
00821 
00822     return TRUE;
00823 }
00824 
00825 
00826 /********************************************************************************************
00827 
00828 >   BOOL CMXExportDC::WriteFillSpecBitmap(CMXRenderRegion *pReg, FillGeometryAttribute *pFill, DocCoord *Coords, INT32 NumCoords)
00829 
00830     Author:     Ben_Summers (Xara Group Ltd) <camelotdev@xara.com>
00831     Created:    23/07/96
00832     Inputs:     render region
00833     Returns:    success
00834     Purpose:    Writes the bitmap fill specifcation to the DC (also does fractals)
00835 
00836 ********************************************************************************************/
00837 
00838 BOOL CMXExportDC::WriteFillSpecBitmap(CMXRenderRegion *pReg, FillGeometryAttribute *pFill, DocCoord *Coords, INT32 NumCoords)
00839 {
00840     // make up a bitmap fill object -- first find the refer bitmap object necessary
00841     BitmapFillAttribute *pBFillAttr = (BitmapFillAttribute *)pFill;
00842     ERROR3IF(!pBFillAttr->IsKindOf(CC_RUNTIME_CLASS(BitmapFillAttribute)), "not a bitmap fill attr");
00843 
00844     // colours
00845     DocColour *pStartColour = 0;
00846     DocColour *pEndColour = 0;
00847     EFFECTTYPE Effect = EFFECT_RGB;
00848 
00849     // get the colours we should use, and the fill effect
00850     pStartColour = pFill->GetStartColour();
00851     pEndColour = pFill->GetEndColour();
00852     if(pStartColour == 0 || pEndColour == 0
00853         || pStartColour->IsTransparent()
00854         || pEndColour->IsTransparent())
00855     {
00856         pStartColour = 0;
00857         pEndColour = 0;
00858     }
00859     
00860     if(pStartColour != 0)
00861     {
00862         // get the fill effect
00863         Effect = pReg->GetFillEffect();
00864     }
00865 
00866     // get the reference for the bitmap
00867     KernelBitmap *pBitmap = pBFillAttr->BitmapRef.GetBitmap();
00868     WORD BitmapRef = GetBitmapReference(pBitmap, pStartColour, pEndColour, Effect);
00869 
00870     // make a new bitmap fill object
00871     CMXReferBitmapFill *pBFill = new CMXReferBitmapFill(this);
00872     if(pBFill == 0)
00873         return FALSE;
00874     pBFill->Set(BitmapRef);
00875 
00876     // add it to the refer list
00877     ReferList.AddTail(pBFill);
00878 
00879     // get it's reference
00880     WORD BitmapFillProcReference = GetProcedureReference(pBFill);
00881 
00882     // ready to write...
00883     if(!StartNestedTag(cmxTAG_RenderAttr_FillSpec_ColorBM))
00884         return FALSE;
00885     
00886     ExportFile->write(&BitmapFillProcReference, sizeof(BitmapFillProcReference));
00887 
00888     // write tiling
00889     if(!StartNestedTag(cmxTAG_Tiling))
00890         return FALSE;
00891 
00892     // get points from the attribute
00893     DocCoord *Start = pFill->GetStartPoint();
00894     DocCoord *End1 = pFill->GetEndPoint();
00895     DocCoord *End2 = pFill->GetEndPoint2();
00896 
00897     // get a tiling structure
00898     cmxTilingEnd tile;
00899 
00900     // Variables for rendering the texture to the file. I've deferred the scaling of
00901     // the image until it's written. It's slightly ugly doing it this way, but it
00902     // removes a few multipications, and so should get rid of some rounding errors
00903     // along the way.
00904     DocRect cBBox;
00905     double  TileWidth   = Start->Distance ( *End1 );
00906     double  TileHeight  = Start->Distance ( *End2 );
00907 
00908     // Calculate the untransformed Corel bounding box.
00909     CalcCorelBBox(Coords, NumCoords, &cBBox);
00910 
00911     // Graeme (9-5-00) - I've simplified Ben's code a little, and removed the
00912     // repeated bits in both branches of the if statement.
00913     if ( ThirtyTwoBit )
00914     {
00915         cmxTilingBegin32 Tile32;
00916         Tile32.Width    = static_cast<DWORD> ( Round ( TileWidth  * ScaleFactor ) );
00917         Tile32.Height   = static_cast<DWORD> ( Round ( TileHeight * ScaleFactor ) );
00918 
00919         WriteData ( &Tile32, sizeof ( Tile32 ) );
00920     }
00921     else
00922     {
00923         cmxTilingBegin16 Tile16;
00924         Tile16.Width    = static_cast<WORD> ( Round ( TileWidth  * ScaleFactor ) );
00925         Tile16.Height   = static_cast<WORD> ( Round ( TileHeight * ScaleFactor ) );
00926 
00927         WriteData ( &Tile16, sizeof ( Tile16 ) );
00928     }
00929 
00930     // now work out the offsets to the start of the tile
00931     double  dX  = static_cast<double> ( Start->x - cBBox.lo.x );
00932     double  dY  = static_cast<double> ( cBBox.hi.y - Start->y );
00933 
00934     // Graeme (9-5-00) - Round the offset up.
00935     tile.XOffset = ( static_cast<WORD> ( Round ( ( dX * 100.0 ) / TileWidth  ) ) ) % 100;
00936     tile.YOffset = ( static_cast<WORD> ( Round ( ( dY * 100.0 ) / TileHeight ) ) ) % 100;
00937 
00938     tile.InterTileOffset = tile.TilingFlags = 0;
00939 
00940     // write the structure to the file
00941     ExportFile->write(&tile, sizeof(tile));
00942     if(!EndNestedTag() || !WriteMinEndTag())
00943         return FALSE;
00944 
00945     // bounding box of bit to use
00946     DocRect bbox = DocRect(0, 0, cmxBITMAPFILLTILESIZE_X, cmxBITMAPFILLTILESIZE_Y);
00947 
00948     if(!WriteBBox(&bbox, FALSE))
00949         return FALSE;
00950 
00951     if(!EndNestedTag() || !WriteMinEndTag())
00952         return FALSE;
00953 
00954     return TRUE;
00955 }
00956 
00957 
00958 /********************************************************************************************
00959 
00960 >   BOOL CMXExportDC::WritePath ( DocCoord *Coords,
00961                                   PathVerb *Verbs,
00962                                   INT32 NumCoords,
00963                                   BOOL Filled )
00964 
00965     Author:     Ben_Summers (Xara Group Ltd) <camelotdev@xara.com>
00966     Created:    01/07/96
00967     Inputs:     render region, coords, verbs and number of coords
00968     Returns:    success
00969     Purpose:    Writes a path instruction -- transformed by matrix
00970 
00971 ********************************************************************************************/
00972 
00973 BOOL CMXExportDC::WritePath ( DocCoord *Coords,
00974                               PathVerb *Verbs,
00975                               INT32 NumCoords,
00976                               BOOL Filled )
00977 {
00978     // if this is one of those nasty 16 bit files, sort out a transform matrix
00979 
00980     // scan the verbs to see if there's a close path in it
00981     // as if there isn't, we need to force no fill on the thingy
00982     BOOL ForceNoFill = TRUE;
00983     for(INT32 p = 0; p < NumCoords; p++)
00984     {
00985         if((Verbs[p] & PT_CLOSEFIGURE) != 0)
00986             ForceNoFill = FALSE;
00987     }
00988     if(!Filled)
00989         ForceNoFill = TRUE;
00990 
00991     // begin the command
00992     if(!StartCommand(cmxINSTR_PolyCurve))
00993         return FALSE;
00994 
00995     // output the rendering attributes
00996     if(!WriteAttributes(pRenderRegion, cmxTAG_PolyCurve_RenderingAttr, Coords, NumCoords, ForceNoFill))
00997         return FALSE;
00998 
00999     // write the point list, transformed of course. Start by tagging it
01000     if(!StartTag(cmxTAG_PolyCurve_PointList))
01001         return FALSE;
01002 
01003     // we have to collect a bounding box as we go through as Corel use a
01004     // naff bbox which is simply the union of all points
01005     DocRect bb;             // which is written as a corel one
01006     DocCoord fc = Coords[0];
01007     pMatrix->transform(&fc);
01008     bb.lo = fc;             // start with sensible coords to begin with
01009     bb.hi = fc;
01010 
01011     // write the point count
01012     WORD Count = (WORD)NumCoords;
01013     ExportFile->write(&Count, sizeof(Count));
01014 
01015     // run through the coords writing and transforming
01016     INT32 l;
01017 TRACEUSER( "Ben", _T("\nPath\n"));
01018     for(l = 0; l < NumCoords; l++)
01019     {
01020         DocCoord Coord = Coords[l];
01021         pMatrix->transform(&Coord);
01022 
01023         // write it...
01024         if(ThirtyTwoBit)
01025         {
01026             cmxPoint32 p = {Coord.x, Coord.y};
01027             ExportFile->write(&p, sizeof(p));
01028         }
01029         else
01030         {
01031 TRACEUSER( "Ben", _T("Coord %d %d\n"), Coord.x, Coord.y);
01032             cmxPoint16 p = {(SWORD)Coord.x, (SWORD)Coord.y};
01033             ExportFile->write(&p, sizeof(p));
01034         }
01035 
01036         // flibble the bounding box with those lovely new coords
01037         if(Coord.x < bb.lo.x) bb.lo.x = Coord.x;
01038         if(Coord.y < bb.lo.y) bb.lo.y = Coord.y;
01039         if(Coord.x > bb.hi.x) bb.hi.x = Coord.x;
01040         if(Coord.y > bb.hi.y) bb.hi.y = Coord.y;
01041     }
01042 
01043     // run through the verbs writing point types
01044     INT32 BezCount = 0;     // for counting Bez
01045     for(l = 0; l < NumCoords; l++)
01046     {
01047         BYTE Type;
01048 
01049         switch(Verbs[l] & (PT_LINETO | PT_MOVETO | PT_BEZIERTO))
01050         {
01051         case PT_MOVETO:
01052             {
01053                 Type = CMXNODETYPE_TYPE_MOVE | CMXNODETYPE_USER;
01054             
01055                 // wibble forwards through the verbs to see if the correspondingness is closed
01056                 for(INT32 lp = l + 1; lp < NumCoords; lp++)
01057                 {
01058                     // OK, is this a move?
01059                     if((Verbs[lp] & (PT_LINETO | PT_MOVETO | PT_BEZIERTO)) == PT_MOVETO)
01060                         break;
01061 
01062                     // is closefigure set?
01063                     if((Verbs[lp] & PT_CLOSEFIGURE) != 0)
01064                     {
01065                         Type |= CMXNODETYPE_CLOSED;
01066                         break;
01067                     }
01068                 }
01069             }
01070             BezCount = 0;
01071             break;
01072 
01073         case PT_LINETO:
01074             Type = CMXNODETYPE_TYPE_LINE | CMXNODETYPE_USER;
01075             BezCount = 0;
01076             break;
01077 
01078         case PT_BEZIERTO:
01079             // in CMX, the control points have type ARC, and the end points
01080             // have type CURVE. This code does the trickery.
01081             if(BezCount == 2)
01082             {
01083                 Type = CMXNODETYPE_TYPE_CURVE | CMXNODETYPE_USER;
01084             }
01085             else
01086             {
01087                 Type = CMXNODETYPE_TYPE_ARC;
01088             }
01089             BezCount++;
01090             if(BezCount > 2)
01091                 BezCount = 0;       // so that only the end points of beziers get user flag
01092             break;
01093 
01094         default:
01095             ERROR3("Unknown node type");
01096             break;
01097         }
01098 
01099         if((Verbs[l] & PT_CLOSEFIGURE) != 0)
01100         {
01101             Type |= CMXNODETYPE_CLOSED;
01102         }
01103 
01104         ExportFile->write(&Type, sizeof(Type));
01105     }
01106 
01107     // end the point list
01108     if(!EndTag())
01109         return FALSE;
01110 
01111     // write bbox we've collected
01112     if(!StartTag(cmxTAG_PolyCurve_BoundingBox)
01113         || !WriteBBox(&bb, FALSE)
01114         || !EndTag())
01115         return FALSE;
01116 
01117     // end the command
01118     if(!WriteMinEndTag()
01119         || !EndCommand())
01120         return FALSE;
01121 
01122     // if necessary, get rid of the horrid transform matrix
01123 
01124     return TRUE;
01125 }
01126 
01127 
01128 /********************************************************************************************
01129 
01130 >   WORD CMXExportDC::GetColourReference(DocColour *pTheLovelyColour)
01131 
01132     Author:     Ben_Summers (Xara Group Ltd) <camelotdev@xara.com>
01133     Created:    04/07/96
01134     Inputs:     colour
01135     Returns:    refernce number of a colour
01136     Purpose:    to find a reference number for the colour when writing a CMX file
01137 
01138 ********************************************************************************************/
01139 
01140 WORD CMXExportDC::GetColourReference(DocColour *pTheLovelyColour)
01141 {
01142     // algorithm:
01143     // scan the refer list to see if the colour is already there.
01144     // if it is, return it's index.
01145     // if it isn't add the colour to the refer list, and return the new index.
01146 
01147     // the reference we need -- references count up from 1
01148     INT32 Ref = 0;
01149 
01150     // scan that list!
01151     CMXReferListItem *pEn = (CMXReferListItem *)ReferList.GetHead();
01152     while(pEn != 0)
01153     {
01154         ERROR3IF(!pEn->IsKindOf(CC_RUNTIME_CLASS(CMXReferListItem)), "unexpected type of entry in refer list");
01155 
01156         if(pEn->IsInWhichDesc() == cmxDESC_COLOUR)
01157         {
01158             Ref++;
01159 
01160             CMXReferColour *pLC = (CMXReferColour *)pEn;
01161             ERROR3IF(!pLC->IsKindOf(CC_RUNTIME_CLASS(CMXReferColour)), "not a refer colour, when it said it was");
01162             
01163             // is it this colour?
01164             if(pLC->AreYouThisColour(pTheLovelyColour))
01165             {
01166                 // yep. return the reference number
01167                 return Ref;
01168             }
01169         }
01170 
01171         pEn = (CMXReferListItem *)ReferList.GetNext(pEn);
01172     }
01173 
01174     // we haven't got one... make a new one, set it up and add it to the list
01175     CMXReferColour *pNewRefCol = new CMXReferColour(this);
01176     if(pNewRefCol == 0)
01177         return 0;
01178 
01179     pNewRefCol->SetColour(pTheLovelyColour);
01180 
01181     ReferList.AddTail(pNewRefCol);
01182 
01183     // return the next reference number, Ref is the ref of the last one in the list
01184     return (WORD)Ref + 1;
01185 }
01186 
01187 
01188 /********************************************************************************************
01189 
01190 >   WORD CMXExportDC::GetArrowReference(ArrowRec *pArrow)
01191 
01192     Author:     Ben_Summers (Xara Group Ltd) <camelotdev@xara.com>
01193     Created:    25/07/96
01194     Inputs:     colour
01195     Returns:    reference number of a colour
01196     Purpose:    to find a reference number for the arrow when writing a CMX file
01197                 will return 0 (ie no arrowhead) if it isn't one
01198 
01199 ********************************************************************************************/
01200 
01201 WORD CMXExportDC::GetArrowReference(ArrowRec *pArrow)
01202 {
01203     // check to see if the arrowrec is in fact an arrowhead
01204     if(pArrow->IsNullArrow())
01205         return 0;       // ie no arrow on here mate
01206 
01207     // the reference we need -- references count up from 1
01208     INT32 Ref = 0;
01209 
01210     // scan that list!
01211     CMXReferListItem *pEn = (CMXReferListItem *)ReferList.GetHead();
01212     while(pEn != 0)
01213     {
01214         ERROR3IF(!pEn->IsKindOf(CC_RUNTIME_CLASS(CMXReferListItem)), "unexpected type of entry in refer list");
01215 
01216         if(pEn->IsInWhichDesc() == cmxDESC_ARROW)
01217         {
01218             Ref++;
01219 
01220             CMXReferArrow *pAr = (CMXReferArrow *)pEn;
01221             ERROR3IF(!pAr->IsKindOf(CC_RUNTIME_CLASS(CMXReferArrow)), "not an arrow, when it said it was");
01222             
01223             // is it this colour?
01224             if(pAr->AreYouThisArrow(pArrow))
01225             {
01226                 // yep. return the reference number
01227                 return Ref;
01228             }
01229         }
01230 
01231         pEn = (CMXReferListItem *)ReferList.GetNext(pEn);
01232     }
01233 
01234     // we haven't got one... make a new one, set it up and add it to the list
01235     CMXReferArrow *pNewArrow = new CMXReferArrow(this);
01236     if(pNewArrow == 0)
01237         return 0;
01238 
01239     pNewArrow->Set(pArrow);
01240 
01241     ReferList.AddTail(pNewArrow);
01242 
01243     // return the next reference number, Ref is the ref of the last one in the list
01244     return (WORD)Ref + 1;
01245 }
01246 
01247 
01248 /********************************************************************************************
01249 
01250 >   WORD CMXExportDC::GetBitmapReference(KernelBitmap *pTheLovelyBitmap, DocColour *pStartCol, DocColour *pEndCol, CMXReferBitmap **ppRB = 0)
01251 
01252     Author:     Ben_Summers (Xara Group Ltd) <camelotdev@xara.com>
01253     Created:    16/07/96
01254     Inputs:     bitmap
01255     Returns:    refernce number of a bitmap
01256     Purpose:    to find a reference number for the bitmap when writing a CMX file.
01257                 *ppRB is set to a pointer to the CMXReferBitmap object if ppRB != 0
01258 
01259 ********************************************************************************************/
01260 
01261 WORD CMXExportDC::GetBitmapReference(KernelBitmap *pTheLovelyBitmap, DocColour *pStartCol,
01262     DocColour *pEndCol, EFFECTTYPE Effect, CMXReferBitmap **ppRB)
01263 {
01264