// $Id: cdrfill.cpp 751 2006-03-31 15:43:49Z alex $ /* @@tag:xara-cn@@ DO NOT MODIFY THIS LINE ================================XARAHEADERSTART=========================== Xara LX, a vector drawing and manipulation program. Copyright (C) 1993-2006 Xara Group Ltd. Copyright on certain contributions may be held in joint with their respective authors. See AUTHORS file for details. LICENSE TO USE AND MODIFY SOFTWARE ---------------------------------- This file is part of Xara LX. Xara LX is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. Xara LX and its component source files are distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Xara LX (see the file GPL in the root directory of the distribution); if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ADDITIONAL RIGHTS ----------------- Conditional upon your continuing compliance with the GNU General Public License described above, Xara Group Ltd grants to you certain additional rights. The additional rights are to use, modify, and distribute the software together with the wxWidgets library, the wxXtra library, and the "CDraw" library and any other such library that any version of Xara LX relased by Xara Group Ltd requires in order to compile and execute, including the static linking of that library to XaraLX. In the case of the "CDraw" library, you may satisfy obligation under the GNU General Public License to provide source code by providing a binary copy of the library concerned and a copy of the license accompanying it. Nothing in this section restricts any of the rights you have under the GNU General Public License. SCOPE OF LICENSE ---------------- This license applies to this program (XaraLX) and its constituent source files only, and does not necessarily apply to other Xara products which may in part share the same code base, and are subject to their own licensing terms. This license does not apply to files in the wxXtra directory, which are built into a separate library, and are subject to the wxWindows license contained within that directory in the file "WXXTRA-LICENSE". This license does not apply to the binary libraries (if any) within the "libs" directory, which are subject to a separate license contained within that directory in the file "LIBS-LICENSE". ARRANGEMENTS FOR CONTRIBUTION OF MODIFICATIONS ---------------------------------------------- Subject to the terms of the GNU Public License (see above), you are free to do whatever you like with your modifications. However, you may (at your option) wish contribute them to Xara's source tree. You can find details of how to do this at: http://www.xaraxtreme.org/developers/ Prior to contributing your modifications, you will need to complete our contributor agreement. This can be found at: http://www.xaraxtreme.org/developers/contribute/ Please note that Xara will not accept modifications which modify any of the text between the start and end of this header (marked XARAHEADERSTART and XARAHEADEREND). MARKS ----- Xara, Xara LX, Xara X, Xara X/Xtreme, Xara Xtreme, the Xtreme and Xara designs are registered or unregistered trademarks, design-marks, and/or service marks of Xara Group Ltd. All rights in these marks are reserved. Xara Group Ltd, Gaddesden Place, Hemel Hempstead, HP2 6EX, UK. http://www.xara.com/ =================================XARAHEADEREND============================ */ // *********Fills for the CDR file filter /* */ #include "camtypes.h" #include "cdrfiltr.h" #include "nodepath.h" #include "cdrbitm.h" //#include "ben.h" #include "nodebmp.h" #include "nodetxts.h" DECLARE_SOURCE("$Revision: 751 $"); #define new CAM_DEBUG_NEW /******************************************************************************************** > BOOL CDRFilter::SetFillAttr(cdrfOffsetHeader *Object) Author: Ben_Summers (Xara Group Ltd) Created: 27 03 95 Inputs: address of the object Returns: error flag Purpose: sets the fill attributes SeeAlso: Filter ********************************************************************************************/ BOOL CDRFilter::SetFillAttr(cdrfOffsetHeader *Object) { if(Version == CDRVERSION_3) { return SetFillAttr3(Object); } // find the reference... DWORD *pReference = (DWORD *)FindDataInObject(Object, cdrfOBJOFFSETTYPE_FILL); // if the pointer to the reference is zero, then the reference is probably // within some random style definition if(pReference == 0) { // OK, try and find a style number within the style reference WORD *pStyleReference = (WORD *)FindDataInObject(Object, cdrfOBJOFFSETTYPE_STYLE); if(pStyleReference != 0) { // OK, see if we can find a the style... cdrfStyle *pStyle; INT32 StyleSize; if((pStyle = (cdrfStyle *)Styles.Find(*pStyleReference, &StyleSize)) != 0) { // OK, got a style... now find a fill reference within it pReference = (DWORD *)FindDataInObject(&pStyle->Header, cdrfSTYLEOFFSETTYPE_FILL); } } } // should this be filled? if(pReference == 0 || ObjFilled == FALSE) { // set not filled attribute return NoFill(); } cdrfFillHeader *Fill; INT32 FillSize; // set is filled attribute if(!SetPathFilled(TRUE)) return FALSE; // try and find the data for a version 4 or 5 fill if((Fill = (cdrfFillHeader *)Fills.Find(*pReference, &FillSize)) == 0) return SetUKFill(); // check the fill's size if(FillSize < sizeof(cdrfFillHeader)) { // if no fill defn or it's just a tad to small, set the unknown fill colour return SetUKFill(); } // OK, what's this fill then? switch(CDRDATA_WORD(Fill->FillType)) { case cdrfFILLTYPE_NONE: // no fill - set things up to be hoopy about that. return NoFill(); break; case cdrfFILLTYPE_FLAT: return SetFillAttrFlat(Fill, FillSize); break; case cdrfFILLTYPE_GRADUATED: return SetFillAttrGrad(Fill, FillSize); break; case cdrfFILLTYPE_PATTERN: { CDRTiledFillInfo i; cdrfFillPattern *PFill = (cdrfFillPattern *)Fill; // check the size and set an unknown fill if it's too small if(FillSize < sizeof(cdrfFillPattern)) return SetUKFill(); // set the info structure i.SizeX = CDRDATA_SWORD(PFill->SizeX); i.SizeY = CDRDATA_SWORD(PFill->SizeY); i.OffsetX = CDRDATA_SWORD(PFill->OffsetX); i.OffsetY = CDRDATA_SWORD(PFill->OffsetY); i.Reference = CDRDATA_DWORD(PFill->Reference); // convert the colours DocColour Colour1; DocColour Colour2; ConvertColour(&PFill->Colour1, &Colour1); ConvertColour(&PFill->Colour2, &Colour2); return SetFillAttrPattern(&i, &Colour1, &Colour2); } break; case cdrfFILLTYPE_VECTOR: { CDRTiledFillInfo i; cdrfFillVector *VFill = (cdrfFillVector *)Fill; // check the size and set an unknown fill if it's too small if(FillSize < sizeof(cdrfFillVector)) return SetUKFill(); // set the info structure i.SizeX = CDRDATA_SWORD(VFill->SizeX); i.SizeY = CDRDATA_SWORD(VFill->SizeY); i.OffsetX = CDRDATA_SWORD(VFill->OffsetX); i.OffsetY = CDRDATA_SWORD(VFill->OffsetY); i.Reference = CDRDATA_DWORD(VFill->VectorReference); return SetFillAttrVector(&i); } break; case cdrfFILLTYPE_TEXTURE: return SetFillAttrTexture(Fill, FillSize); break; default: return SetUKFill(); break; } return TRUE; } /******************************************************************************************** > BOOL CDRFilter::SetFillAttr(cdrfOffsetHeader *Object) Author: Ben_Summers (Xara Group Ltd) Created: 24/04/95 Inputs: - Returns: error flag Purpose: makes an object not filled SeeAlso: Filter ********************************************************************************************/ BOOL CDRFilter::NoFill(void) { if(pMadeNode->IsKindOf(CC_RUNTIME_CLASS(NodePath))) { NodePath *pPath = (NodePath *)pMadeNode; pPath->InkPath.IsFilled = FALSE; } ObjFilled = FALSE; SetFillColour(DocColour(COLOUR_TRANS)); return SetPathFilled(FALSE); } /******************************************************************************************** > BOOL CDRFilter::SetFillAttr3(cdrfOffsetHeader *Object) Author: Ben_Summers (Xara Group Ltd) Created: 05/04/95 Inputs: address of object Returns: error flag Purpose: sets the fill attributes for version 3 SeeAlso: Filter ********************************************************************************************/ BOOL CDRFilter::SetFillAttr3(cdrfOffsetHeader *Object) { cdrfFillV3 *Fill = (cdrfFillV3 *)FindDataInObject(Object, cdrfOBJOFFSETTYPE_FILL); if(Fill == 0) { // set not filled attribute return NoFill(); } // info about the fill CDRFillInfo fi; // find the bounding box of this lovely node DocRect BBox; if(!GetObjCorelBBox((NodeRenderableBounded *)pMadeNode, &BBox)) return FALSE; // convert colours and things as necessary DocColour Col1; DocColour Col2; if(Fill->Type == cdrfFILLTYPEV3_FLAT) { if(!ConvertColour((cdrfColour *)&Fill->Data.Flat.Colour, &Col1)) return FALSE; } else if(Fill->Type == cdrfFILLTYPEV3_LINEAR || Fill->Type == cdrfFILLTYPEV3_RADIAL) { fi.Angle = CDRDATA_SWORD(Fill->Data.Grad.Angle); fi.Pad = CDRDATA_SWORD(Fill->Data.Grad.Pad); fi.HorizontalCentreOffset = CDRDATA_SWORD(Fill->Data.Grad.HorizontalCentreOffset); fi.VerticalCentreOffset = CDRDATA_SWORD(Fill->Data.Grad.VerticalCentreOffset); if(!ConvertColour((cdrfColour *)&Fill->Data.Grad.Colour1, &Col1)) return FALSE; if(!ConvertColour((cdrfColour *)&Fill->Data.Grad.Colour2, &Col2)) return FALSE; } // make the path filled if(!SetPathFilled(TRUE)) return FALSE; // set the fill switch(Fill->Type) { case cdrfFILLTYPEV3_NONE: return NoFill(); break; case cdrfFILLTYPEV3_FLAT: if(!SetFillColour(Col1)) return FALSE; break; case cdrfFILLTYPEV3_LINEAR: if(!SetLinearGrad(&fi, BBox, &Col1, &Col2)) return SetUKFill(); break; case cdrfFILLTYPEV3_RADIAL: if(!SetRadialGrad(&fi, BBox, &Col1, &Col2)) return SetUKFill(); break; case cdrfFILLTYPEV3_PATTERN: { CDRTiledFillInfo i; // set the info structure i.SizeX = CDRDATA_SWORD(Fill->Data.Pattern.SizeX); i.SizeY = CDRDATA_SWORD(Fill->Data.Pattern.SizeY); i.OffsetX = CDRDATA_SWORD(Fill->Data.Pattern.OffsetX); i.OffsetY = CDRDATA_SWORD(Fill->Data.Pattern.OffsetY); i.Reference = CDRDATA_DWORD(Fill->Data.Pattern.Reference); // convert the colours ConvertColour((cdrfColour *)&Fill->Data.Pattern.Colour1, &Col1); ConvertColour((cdrfColour *)&Fill->Data.Pattern.Colour2, &Col2); return SetFillAttrPattern(&i, &Col1, &Col2); } break; case cdrfFILLTYPEV3_VECTOR: { CDRTiledFillInfo i; // set the info structure i.SizeX = CDRDATA_SWORD(Fill->Data.Vector.SizeX); i.SizeY = CDRDATA_SWORD(Fill->Data.Vector.SizeY); i.OffsetX = CDRDATA_SWORD(Fill->Data.Vector.OffsetX); i.OffsetY = CDRDATA_SWORD(Fill->Data.Vector.OffsetY); i.Reference = CDRDATA_DWORD(Fill->Data.Vector.Reference); return SetFillAttrVector(&i); } break; default: return SetUKFill(); break; } return TRUE; } /******************************************************************************************** > BOOL CDRFilter::SetFillAttrFlat(cdrfFillHeader *Fill, INT32 Size) Author: Ben_Summers (Xara Group Ltd) Created: 28 03 95 Inputs: pointer to fill header Returns: error flag Purpose: sets flat fill attributes SeeAlso: Filter ********************************************************************************************/ BOOL CDRFilter::SetFillAttrFlat(cdrfFillHeader *Fill, INT32 Size) { cdrfFillFlat *FFill = (cdrfFillFlat *)Fill; // check the size and set an unknown fill if it's too small if(Size < sizeof(cdrfFillFlat)) return SetUKFill(); // convert and set the line colour DocColour Col; ConvertColour(&FFill->Colour, &Col); if(!SetFillColour(Col)) return FALSE; return TRUE; } /******************************************************************************************** > BOOL CDRFilter::SetFillAttrGrad(cdrfFillHeader *Fill, INT32 Size) Author: Ben_Summers (Xara Group Ltd) Created: 28 03 95 Inputs: pointer to fill header Returns: error flag Purpose: sets graduated fill attributes SeeAlso: Filter ********************************************************************************************/ BOOL CDRFilter::SetFillAttrGrad(cdrfFillHeader *Fill, INT32 Size) { // check that it's big enought to be a realistic grad fill if(Size < (sizeof(cdrfFillGraduated) + sizeof(cdrfFillGradColour))) { TRACEUSER( "Ben", _T("fill too small, size = %d\n"), Size); return SetUKFill(); } // make a new pointer cdrfFillGraduated *GFill = (cdrfFillGraduated *)Fill; // has it got enough colours in it? if(CDRDATA_WORD(GFill->NColours) < 1) return SetUKFill(); // find the bounding box of this lovely node DocRect BBox; if(!GetObjCorelBBox((NodeRenderableBounded *)pMadeNode, &BBox)) return FALSE; // sort out the colours on this fill - ATM we can only go from one colour to // another, so we just need to find the last and first ones and use those. cdrfFillGradColour *Cols = (cdrfFillGradColour *)(GFill + 1); UINT32 min = CDRDATA_WORD(Cols[0].Percentage); UINT32 max = min; UINT32 minO = 0; UINT32 maxO = 0; UINT32 l; for(l = 1; l < CDRDATA_WORD(GFill->NColours); l++) { UINT32 p = CDRDATA_WORD(Cols[l].Percentage); if(p < min) { min = p; minO = l; } if(p > max) { max = p; maxO = l; } } // OK, found the minimum and maximim percentages along the grad fill line. // get and convert the colours DocColour StartColour; DocColour EndColour; ConvertColour(&Cols[minO].Colour, &StartColour); ConvertColour(&Cols[maxO].Colour, &EndColour); // now work out what kind of fade we need here UINT32 FadeType = CDRDATA_WORD(GFill->ColourType); // if it's a rainbow effect, work out whether an anti-clockwise thingy will be a // a normal or alt rainbow fill BOOL NormalRainbow = TRUE; if(FadeType == cdrfGRADFILLCOLOURTYPE_RAINBOW_CLOCKWISE || FadeType == cdrfGRADFILLCOLOURTYPE_RAINBOW_ANTICLOCKWISE) { // convert both colours to HSV to find their hue ColourContext *Conv = ColourContext::GetGlobalDefault(COLOURMODEL_HSVT); ColourHSVT StartC; ColourHSVT EndC; Conv->ConvertColour(&StartColour, (ColourGeneric *)&StartC); Conv->ConvertColour(&EndColour, (ColourGeneric *)&EndC); // compare the hues and work out a difference ColourValue Difference; if(EndC.Hue > StartC.Hue) { Difference = EndC.Hue - StartC.Hue; } else { Difference = 1.0 - (StartC.Hue - EndC.Hue); } // if the difference is more than 0.5 then it's a alt rainbow thingy if(Difference > fixed24(0.5)) NormalRainbow = FALSE; } // set the fade effect switch(FadeType) { case cdrfGRADFILLCOLOURTYPE_DIRECT: case cdrfGRADFILLCOLOURTYPE_CUSTOM: default: SetFadeFillEffect(); break; case cdrfGRADFILLCOLOURTYPE_RAINBOW_ANTICLOCKWISE: if(NormalRainbow) SetRainbowFillEffect(); else SetAltRainbowFillEffect(); break; case cdrfGRADFILLCOLOURTYPE_RAINBOW_CLOCKWISE: if(NormalRainbow) SetAltRainbowFillEffect(); else SetRainbowFillEffect(); break; } // get the info about the fill CDRFillInfo fi; fi.Angle = CDRDATA_SWORD(GFill->Angle); fi.Pad = CDRDATA_SWORD(GFill->Pad); fi.HorizontalCentreOffset = CDRDATA_SWORD(GFill->HorizontalCentreOffset); fi.VerticalCentreOffset = CDRDATA_SWORD(GFill->VerticalCentreOffset); // and finally, sort out the start and end points of the thingy DocCoord StartPoint; DocCoord EndPoint; switch(CDRDATA_WORD(GFill->GradFillType)) { case cdrfGRADFILLTYPE_LINEAR: if(!SetLinearGrad(&fi, BBox, &StartColour, &EndColour)) return SetUKFill(); break; case cdrfGRADFILLTYPE_RADIAL: case cdrfGRADFILLTYPE_SQUARE: // radial is the nearest Camelot fill to a square fill if(!SetRadialGrad(&fi, BBox, &StartColour, &EndColour)) return SetUKFill(); break; case cdrfGRADFILLTYPE_CONICAL: if(!SetConicalGrad(&fi, BBox, &StartColour, &EndColour)) return SetUKFill(); break; default: return SetUKFill(); break; } return TRUE; } /******************************************************************************************** > BOOL CDRFilter::SetUKFill(void) Author: Ben_Summers (Xara Group Ltd) Created: 28 03 95 Inputs: pointer to fill header Returns: error flag Purpose: sets a fill attribute for unknown fills SeeAlso: Filter ********************************************************************************************/ BOOL CDRFilter::SetUKFill(void) { PColourCMYK cmyk; cmyk.Cyan = cmyk.Magenta = cmyk.Yellow = 0; cmyk.Key = 255; DocColour Col; Col.SetCMYKValue(&cmyk); return SetFillColour(Col); } /******************************************************************************************** > BOOL CDRFilter::GetObjCorelBBox(NodeRenderable *pNode, DocRect *BBox) Author: Ben_Summers (Xara Group Ltd) Created: 28 03 95 Inputs: converted node (after transformation) and a pointer to bbox to fill in Returns: error flag Purpose: gets the corel bbox of a converted object, looking up the bbox of text objects SeeAlso: Filter ********************************************************************************************/ BOOL CDRFilter::GetObjCorelBBox(NodeRenderableBounded *pNode, DocRect *BBox) { if(IS_A(pNode, TextStory) && TextBBoxes.Find(pNode, BBox) == TRUE) return TRUE; return GetCorelBBox(pNode, BBox); } /******************************************************************************************** > BOOL CDRFilter::GetCorelBBox(NodeRenderable *pNode, DocRect *BBox) Author: Ben_Summers (Xara Group Ltd) Created: 28 03 95 Inputs: converted node (after transformation) and a pointer to bbox to fill in Returns: error flag Purpose: gets the Corel simple union bbox of an object SeeAlso: Filter ********************************************************************************************/ BOOL CDRFilter::GetCorelBBox(NodeRenderableBounded *pNode, DocRect *BBox) { if(pNode->IsKindOf(CC_RUNTIME_CLASS(NodePath))) { // ok, so it's a path. That means we can run through it to find out the simple // union of all points. That's cool. Nice of corel not to do proper bboxes, isn't // it? NodePath *pNodePath = (NodePath *)pNode; // get a pointer to the array of coords in the nice path INT32 NCoords = pNodePath->InkPath.GetNumCoords(); DocCoord *Coords = pNodePath->InkPath.GetCoordArray(); ERROR3IF(NCoords < 1, "Awooga! Trying to find a corel bbox of a path with less than one coord."); INT32 c; INT32 x0, y0, x1, y1; // initialise my bbox variables x0 = x1 = Coords[0].x; y0 = y1 = Coords[0].y; for(c = 1; c < NCoords; c++) { // update my bbox if(Coords[c].x < x0) x0 = Coords[c].x; if(Coords[c].y < y0) y0 = Coords[c].y; if(Coords[c].x > x1) x1 = Coords[c].x; if(Coords[c].y > y1) y1 = Coords[c].y; } // fill in *BBox BBox->lo.x = x0; BBox->lo.y = y0; BBox->hi.x = x1; BBox->hi.y = y1; } else { // if it's not a path object, ask the node what it's bounding box is - we'll just // have to make do with a *correct* bounding box instead of simply corel union. *BBox = pNode->GetBoundingRect(); } return TRUE; } /******************************************************************************************** > BOOL CDRFilter::SetLinearGrad(CDRFillInfo *GFill, DocRect &BBox, DocColour *StartColour, DocColour *EndColour) Author: Ben_Summers (Xara Group Ltd) Created: 28 03 95 Inputs: grad fill defn and converted colours for the fill Returns: error flag Purpose: sets a linear graduated fill from fill definition in the file Copied from coreleps.cpp and modified SeeAlso: Filter ********************************************************************************************/ BOOL CDRFilter::SetLinearGrad(CDRFillInfo *GFill, DocRect &BBox, DocColour *StartColour, DocColour *EndColour) { // NB. this function is (hopefully!) over-complex and will be simplified. // However, it's like this atm so I can get my head around the weird maths Corel // forces us to use. // The desired start and end points of the grad fill 'arrow'. DocCoord Start, End; // Calculate width and height MILLIPOINT Width = BBox.Width(); MILLIPOINT Height = BBox.Height(); // bodge just in case of zero height or widths if(Width < 16) Width = 16; if(Height < 16) Height = 16; // Find centre of box DocCoord Centre; Centre.x = BBox.lo.x + (Width / 2); Centre.y = BBox.lo.y + (Height / 2); // Find total area of BBox double TotalArea = (double) Width * (double) Height; // Cope with angles > 180 BOOL Mirror = FALSE; INT32 Angle = GFill->Angle; // note angle is * 10 if (Angle >= 1800) { Angle -= 1800; Mirror = TRUE; } else if (Angle < 00) { Angle += 1800; Mirror = TRUE; } Angle += 900; if (Angle >= 1800) { Angle -= 1800; } // get edge pad value INT32 EdgePad = GFill->Pad; // Calculate tan of the angle - convert angle to radians first. double Radians = (((double) Angle) / 1800.0) * PI; double TanTheta; if (Angle == 900) { // Special case for horizontal grad fill arrow. // Make 0% padding first Start.x = BBox.lo.x; Start.y = Centre.y; End.x = BBox.hi.x; End.y = Centre.y; // Find out width of padding INT32 Padding = (Width * EdgePad) / 100; Start.x += Padding; End.x -= Padding; } else if (Angle == 0) { // Special case for vertical grad fill arrow. // Make 0% padding first Start.x = Centre.x; Start.y = BBox.lo.y; End.x = Centre.x; End.y = BBox.hi.y; // Find out width of padding INT32 Padding = (Height * EdgePad) / 100; Start.y += Padding; End.y -= Padding; } else { TanTheta = tan(Radians); // Find out what the maximum padding is that we can achieve using just triangles: // Find the maximum triangle width MILLIPOINT TriWidth = (MILLIPOINT) ((double) Height / TanTheta); // Limit it to sensible value if (TriWidth < 0) TriWidth = -TriWidth; if (TriWidth > Width) TriWidth = Width; // Find the maximum triangle width MILLIPOINT TriHeight = (MILLIPOINT) ((double) Width * TanTheta); // Limit it to sensible value if (TriHeight < 0) TriHeight = -TriHeight; if (TriHeight > Height) TriHeight = Height; // The 'c' values of the y = mx+c equation. MILLIPOINT StartC, EndC; // Work out the maximum percentage/edge padding this gives us // (50 because it's 100 / 2 because we want area of triangle, not rectangle). double Percentage = (50.0 * (double) TriWidth * (double) TriHeight) / TotalArea; INT32 Diff = 0; // Is this enough? if (((INT32) Percentage) >= EdgePad) { // Yes - calculate exactly how big the triangle needs to be. TriHeight = (MILLIPOINT) sqrt(ABS(((double) EdgePad * TotalArea * TanTheta) / 100.0)); TriWidth = (MILLIPOINT) ((double) TriHeight / TanTheta); if (TriWidth < 0) TriWidth = -TriWidth; ENSURE(TriWidth < Width, "Error in Corel Grad fill decoding logic"); } else { // How much percentage do we need to add with each rectangle? Percentage = (EdgePad - Percentage) / 2; // Handle the rectangle stuff. if (TriWidth == Width) { // Need to add rectangles to the top and bottom. Diff = (MILLIPOINT) ((Percentage * Height) / 100.0); } else { // Need to add rectangles to left and right Diff = (MILLIPOINT) ((Percentage * Width) / 100.0); Diff = (MILLIPOINT) (Diff / tan(PI - Radians)); Diff = ABS(Diff); } } // Work out the C value for the start line (c = y - mx) // (m = tan(angle) ) if (Angle == 900) { //ENSURE(FALSE, "90 degree angle found!"); } else if (Angle < 900) { StartC = (MILLIPOINT) (BBox.lo.y - ((BBox.hi.x - TriWidth) * TanTheta)); EndC = (MILLIPOINT) (BBox.hi.y - ((BBox.lo.x + TriWidth) * TanTheta)); } else { StartC = (MILLIPOINT) (BBox.lo.y - ((BBox.lo.x + TriWidth) * TanTheta)); EndC = (MILLIPOINT) (BBox.hi.y - ((BBox.hi.x - TriWidth) * TanTheta)); } // Add on difference for rectangles, if any. StartC += Diff; EndC -= Diff; // Work out m and c for the grad fill line. // We know m is -1/m of the triangle's hypotenuse. // c = roy - (rox/m) double FillM = -1.00 / TanTheta; MILLIPOINT FillC = (MILLIPOINT) (Centre.y - (Centre.x * FillM)); // Work out intersections: x = (c2 - c1) / (2m) Start.x = (MILLIPOINT) ( (FillC - StartC) / (TanTheta + (1.00 / TanTheta)) ); Start.y = (MILLIPOINT) ((FillM * Start.x) + FillC); End.x = (MILLIPOINT) ( (FillC - EndC) / (TanTheta + (1.00 / TanTheta)) ); End.y = (MILLIPOINT) ((FillM * End.x) + FillC); } if (Mirror) { // Swap the grid fill end-points over. DocCoord Tmp = Start; Start = End; End = Tmp; } // Set the fill type according to these calculations. return SetLinearFill(*StartColour, *EndColour, Start, End); } /******************************************************************************************** > BOOL CDRFilter::SetConicalGrad(CDRFillInfo *GFill, DocRect &BBox, DocColour *StartColour, DocColour *EndColour) Author: Ben_Summers (Xara Group Ltd) Created: 28 03 95 Inputs: grad fill defn and converted colours for the fill Returns: error flag Purpose: sets a conical graduated fill from fill definition in the file Copied from coreleps.cpp and modified SeeAlso: Filter ********************************************************************************************/ BOOL CDRFilter::SetConicalGrad(CDRFillInfo *GFill, DocRect &BBox, DocColour *StartColour, DocColour *EndColour) { // The desired start and end points of the grad fill 'arrow'. DocCoord Start, End; // Calculate width and height MILLIPOINT Width = BBox.Width(); MILLIPOINT Height = BBox.Height(); // Start point is the centre given by Corel. // This centre is percentage offsets from the centre of the object, i.e. (0,0) is // the centre of the bounding box. Start.x = BBox.lo.x + (Width / 2); Start.y = BBox.lo.y + (Height / 2); Start.x += ((GFill->HorizontalCentreOffset * Width) / 100); Start.y += ((GFill->VerticalCentreOffset * Height) / 100); // End point is start point + radius but takes into account the angle double Radius = Width / 2; // angle is * 10, and needs to be in radians double Theta = ((((double)(GFill->Angle)) / 10.0) / 360.0) * (2 * PI); // make the angle go anti-clockwise Theta = 0 - Theta; // rotate by PI / 2 Theta -= PI / 2; // angle can be negative, ensure it's positive while(Theta < 0) Theta += (2 * PI); // calculate the triangle double dx, dy; dx = Radius * sin(Theta); dy = Radius * cos(Theta); End.x = Start.x + (INT32)dx; End.y = Start.y + (INT32)dy; // Seems that we need to swap start and end colours... // Set the fill type according to these calculations. return SetConicalFill(*EndColour, *StartColour, Start, End); } /******************************************************************************************** > BOOL CDRFilter::SetRadialGrad(CDRFillInfo *GFill, DocRect &BBox, DocColour *StartColour, DocColour *EndColour) Author: Ben_Summers (Xara Group Ltd) Created: 21/04/95 Inputs: grad fill defn and converted colours for the fill Returns: error flag Purpose: sets a radial graduated fill from fill definition in the file SeeAlso: Filter ********************************************************************************************/ BOOL CDRFilter::SetRadialGrad(CDRFillInfo *GFill, DocRect &BBox, DocColour *StartColour, DocColour *EndColour) { // The desired start and end points of the grad fill 'arrow'. DocCoord Start, End; // Calculate width and height MILLIPOINT Width = BBox.Width(); MILLIPOINT Height = BBox.Height(); // caluculate the source area // first, what's the diagonal length double dWidth = Width; double dHeight = Height; INT32 Diagonal = (INT32)sqrt(dWidth*dWidth + dHeight*dHeight); // and from that calculate area of the box containing the bit of the // bit of the circle in the bbox INT32 Edge = (Diagonal * (100 - (GFill->Pad * 2))) / 100; // Start point is the centre given by Corel. // This centre is percentage offsets from the centre of the object, i.e. (0,0) is // the centre of the bounding box. DocCoord Centre = DocCoord(BBox.lo.x + (Width / 2), BBox.lo.y + (Height / 2)); INT32 OffX = (GFill->HorizontalCentreOffset * Width) / 100; INT32 OffY = (GFill->VerticalCentreOffset * Height) / 100; Start.x = Centre.x + OffX; Start.y = Centre.y + OffY; // Find required radius of circle. double Radius = Edge / 2; // how far away is the centre of the fill from the centre of the bbox? double dOffX = OffX; double dOffY = OffY; double Dist = (INT32)sqrt(dOffX*dOffX + dOffY*dOffY); // and increase the radius by a bodge factor double BodgeFactor = 1 + (Dist / (double)(Diagonal / 2)); Radius *= BodgeFactor; // End point is start point + radius End.x = Start.x + ((MILLIPOINT) Radius); End.y = Start.y; // Seems that we need to swap start and end colours... // Set the fill type according to these calculations. return SetRadialFill(*EndColour, *StartColour, Start, End); } /******************************************************************************************** > BOOL CDRBitmapStore::AddChunkToStore(RIFFFile *RIFF, BOOL Pattern, CDRFilter *C) Author: Ben_Summers (Xara Group Ltd) Created: 10/04/95 Inputs: RIFF file to get the chunk from, and wether it's pattern or not (Pattern are 1bpp and have a different format) Returns: error flag Purpose: Adds a bitmap to the store, converting it now rather than storing it up for later SeeAlso: CDRFilter ********************************************************************************************/ BOOL CDRBitmapStore::AddChunkToStore(RIFFFile *RIFF, BOOL Pattern, CDRFilter *C) { CDRBitmapStoredItem *Item = new CDRBitmapStoredItem; if(Item == 0) return FALSE; // convert the bitmap FOURCC Reference = 0; // the reference number of this thingy // convert depending on the type of thing if(Pattern) { Item->pBitmap = CDRBitmap::ConvertPattern(RIFF, &Reference, C); } else { switch(C->Version) { case CDRVERSION_5: Item->pBitmap = CDRBitmap::ConvertBitmap5(RIFF, &Reference, C); break; default: Item->pBitmap = CDRBitmap::ConvertBitmap4(RIFF, &Reference, C); break; } } // check that we got one if(Item->pBitmap == 0) return FALSE; // make a nice name for it String_256 Name; if(Pattern) Name.MakeMsg(_R(IDT_CDRPATTERNNAME), CDRFilter::PatternNumber++); else Name.MakeMsg(_R(IDT_CDRBITMAPNAME), CDRFilter::BitmapNumber++); // and set the name Item->pBitmap->ActualBitmap->SetName(Name); // store the reference Item->Reference = Reference; // add the item to this list AddTail(Item); return TRUE; } /******************************************************************************************** > KernelBitmap *CDRBitmapStore::FindBitmap(DWORD Reference) Author: Ben_Summers (Xara Group Ltd) Created: 10/04/95 Inputs: reference of the bitmap to find (a CDRDATA_ should already have been done on it) Returns: a pointer to a KernelBitmap or 0 for not found Purpose: Finds a bitmap within the store. Finding a bitmap sets it's used flag to TRUE to avoid it being deleted. SeeAlso: CDRFilter ********************************************************************************************/ KernelBitmap *CDRBitmapStore::FindBitmap(DWORD Reference) { CDRBitmapStoredItem *Item = (CDRBitmapStoredItem *)GetHead(); // go through the list of items searching for the reference number given while(Item != 0) { if(Item->Reference == Reference) { // set the used flag Item->Used = TRUE; return Item->pBitmap; } Item = (CDRBitmapStoredItem *)GetNext(Item); } return 0; } /******************************************************************************************** > CDRBitmapStoredItem::CDRBitmapStoredItem() Author: Ben_Summers (Xara Group Ltd) Created: 10/04/95 Inputs: - Returns: - Purpose: Constructor for a bitmap store entry SeeAlso: CDRBitmapStore ********************************************************************************************/ CDRBitmapStoredItem::CDRBitmapStoredItem() { pBitmap = 0; Used = FALSE; } /******************************************************************************************** > CDRBitmapStoredItem::~CDRBitmapStoredItem() Author: Ben_Summers (Xara Group Ltd) Created: 10/04/95 Inputs: - Returns: - Purpose: Destructor for a bitmap store entry SeeAlso: CDRBitmapStore ********************************************************************************************/ CDRBitmapStoredItem::~CDRBitmapStoredItem() { if(pBitmap != 0 && Used == FALSE) delete pBitmap; } /******************************************************************************************** > BOOL CDRFilter::SetFillAttrPattern(CDRTiledFillInfo *Info, DocColour *Colour1, DocColour *Colour2) Author: Ben_Summers (Xara Group Ltd) Created: 10/04/95 Inputs: pointer to fill header Returns: error flag Purpose: set pattern fill attributes SeeAlso: Filter ********************************************************************************************/ BOOL CDRFilter::SetFillAttrPattern(CDRTiledFillInfo *Info, DocColour *Colour1, DocColour *Colour2) { // find the bitmap we're going to use KernelBitmap *pBitmap = Patterns.FindBitmap(Info->Reference); if(pBitmap == 0) return SetUKFill(); // work out where it's going to go. // find the size of the tile INT32 SizeX = Info->SizeX * CDRCOORDS_TO_MILLIPOINTS; INT32 SizeY = Info->SizeY * CDRCOORDS_TO_MILLIPOINTS; // find the bounding box of this lovely node DocRect BBox; if(!GetObjCorelBBox((NodeRenderableBounded *)pMadeNode, &BBox)) return FALSE; // set to the top left + the first tile offset INT32 XPos = BBox.lo.x + ((SizeX * Info->OffsetX) / 100); INT32 YPos = BBox.hi.y - SizeY - ((SizeY * Info->OffsetY) / 100); // can't do anything with the row/column staggering thingy // set the bitmap fill DocCoord Start = DocCoord(XPos, YPos); DocCoord End1 = DocCoord(XPos + SizeX, YPos); DocCoord End2 = DocCoord(XPos, YPos + SizeY); if(!SetBitmapFill(pBitmap, Start, End1, End2, Colour1, Colour2)) return FALSE; return TRUE; } /******************************************************************************************** > BOOL CDRFilter::SetFillAttrVector(CDRTiledFillInfo *Info) Author: Ben_Summers (Xara Group Ltd) Created: 20/04/95 Inputs: pointer to fill header Returns: error flag Purpose: set vector fill attributes SeeAlso: Filter ********************************************************************************************/ BOOL CDRFilter::SetFillAttrVector(CDRTiledFillInfo *Info) { // find the bitmap we're going to use - at the moment, we can't only do a vector fill // if it actually referes to a bitmap // first things first - find the entry in the vectors list CDRVectorStoredItem *Item = Vectors.Find(Info->Reference); if(Item->Objects == 0) return SetUKFill(); // now, does this just refer to a bitmap? KernelBitmap *pBitmap = BitmapFromNodeList(Item->Objects); if(pBitmap == 0) return SetUKFill(); // no bitmap generation possible // work out where it's going to go. // find the size of the tile INT32 SizeX = Info->SizeX * CDRCOORDS_TO_MILLIPOINTS; INT32 SizeY = Info->SizeY * CDRCOORDS_TO_MILLIPOINTS; // find the bounding box of this lovely node DocRect BBox; if(!GetObjCorelBBox((NodeRenderableBounded *)pMadeNode, &BBox)) return FALSE; // set to the top left + the first tile offset INT32 XPos = BBox.lo.x + ((SizeX * Info->OffsetX) / 100); INT32 YPos = BBox.hi.y - SizeY - ((SizeY * Info->OffsetY) / 100); // can't do anything with the row/column staggering thingy // set the bitmap fill DocCoord Start = DocCoord(XPos, YPos); DocCoord End1 = DocCoord(XPos + SizeX, YPos); DocCoord End2 = DocCoord(XPos, YPos + SizeY); if(!SetBitmapFill(pBitmap, Start, End1, End2)) return FALSE; return TRUE; } /******************************************************************************************** > BOOL CDRFilter::SetFillAttrTexture(cdrfFillHeader *Fill, INT32 Size) Author: Ben_Summers (Xara Group Ltd) Created: 20/04/95 Inputs: pointer to fill header Returns: error flag Purpose: sets texture fill attributes SeeAlso: Filter ********************************************************************************************/ BOOL CDRFilter::SetFillAttrTexture(cdrfFillHeader *Fill, INT32 Size) { cdrfFillTexture *TFill = (cdrfFillTexture *)Fill; // check the size and set an unknown fill if it's too small if(Size < sizeof(cdrfFillTexture)) return SetUKFill(); // OK, let's try and find the vector thing it represents DocRect fBBox; Node *FillNodes; FillNodes = Vectors.Find(CDRDATA_WORD(TFill->VectorReference), &fBBox); // did we get any? if(FillNodes == 0) return SetUKFill(); // find the associated bitmap KernelBitmap *pBitmap; pBitmap = BitmapFromNodeList(FillNodes); // got one? if(pBitmap == 0) return SetUKFill(); // right then, set the fill to cover the object // what's it's bounding box? DocRect BBox; if(!GetObjCorelBBox((NodeRenderableBounded *)pMadeNode, &BBox)) return FALSE; // set the bitmap fill DocCoord Start = DocCoord(BBox.lo.x, BBox.lo.y); DocCoord End1 = DocCoord(BBox.hi.x, BBox.lo.y); DocCoord End2 = DocCoord(BBox.lo.x, BBox.hi.y); if(!SetBitmapFill(pBitmap, Start, End1, End2)) return FALSE; return TRUE; } /******************************************************************************************** > CDRVectorStoredItem::CDRVectorStoredItem(void) Author: Ben_Summers (Xara Group Ltd) Created: 19/04/95 Inputs: - Returns: - Purpose: constructor SeeAlso: CDRFilter ********************************************************************************************/ CDRVectorStoredItem::CDRVectorStoredItem(void) : BBox(0, 0, 0, 0) { Objects = 0; Reference = 0; pBitmap = 0; } /******************************************************************************************** > BOOL CDRVectorStoredItem::GenerateBitmap(Document *pDocument) Author: Ben_Summers (Xara Group Ltd) Created: 17/05/95 Inputs: a document to use the default attributes from Returns: pointer to a bitmap or zero for an error Purpose: generates a bitmap representatation of the vectors stored in this entry SeeAlso: CDRFilter ********************************************************************************************/ KernelBitmap *CDRVectorStoredItem::GetBitmap(Document *pDocument) { ERROR2IF(Objects == 0, FALSE, "not objects in VectorStoredItem!"); // has a bitmap already been made? if(pBitmap != 0) return pBitmap; // generate one... return 0; // nothing could be generated } /******************************************************************************************** > CDRVectorStoredItem::~CDRVectorStoredItem(void) Author: Ben_Summers (Xara Group Ltd) Created: 19/04/95 Inputs: - Returns: - Purpose: destructor SeeAlso: CDRFilter ********************************************************************************************/ CDRVectorStoredItem::~CDRVectorStoredItem(void) { // the objects should always be deleted as copies are made when necessary if(Objects != 0) CDRFilter::DeleteNodeList(Objects); } /******************************************************************************************** > BOOL CDRFilter::ProcessVectorList(ADDR Block, INT32 Size) Author: Ben_Summers (Xara Group Ltd) Created: 19/04/95 Inputs: vect list contents and size Returns: error flag Purpose: processes the vector list at the beginning of CDR files. This contains a list of objects which are used for things such as full colour fills, texture fills, mould shapes, that sort of thing. They are referenced by the objects serial number. This function takes a block containing the vect list contents and it's size as Corel store all these vectors *before* the fill and outline definitions. Are these people really crazy? SeeAlso: Filter ********************************************************************************************/ BOOL CDRFilter::ProcessVectorList(ADDR Block, INT32 Size) { ERROR3IF(pLevelNodeList != 0, "ProcessVectorList called when LevelNodeList was in use"); // first of all, make a CCMemFile out of the block so we can do RIFF stuff on it CCMemFile MFile; MFile.open(Block, Size); // make a RIFF file out of it - but we don't have a header RIFFFile RFile; RFile.Init(&MFile, TRUE); // OK, change a few variables over... INT32 oTranslateByX = TranslateByX; INT32 oTranslateByY = TranslateByY; RIFFFile *oRIFF = RIFF; TranslateByX = 0; TranslateByY = 0; RIFF = &RFile; // get on and process the block // what we've got it a load of 'grp' lists containing the objects we're interested in // they contain a serial number which is used to identify the vector and a bbox, // which we need to find and store in the list do { // is it a group list? if(RIFF->GetObjType() == RIFFOBJECTTYPE_LISTSTART && RIFF->GetObjChunkType() == cdrT_grp) { if(!ProcessVectorListEntry()) return FALSE; } if(!RIFF->NextObject()) goto Failure; } while(RIFF->GetObjType() != RIFFOBJECTTYPE_FILEEND); // close everything down and put the variables back MFile.close(); TranslateByX = oTranslateByX; TranslateByY = oTranslateByY; RIFF = oRIFF; return TRUE; Failure: // clear everything up... MFile.close(); TranslateByX = oTranslateByX; TranslateByY = oTranslateByY; RIFF = oRIFF; return FALSE; } /******************************************************************************************** > BOOL CDRFilter::ProcessVectorListEntry() Author: Ben_Summers (Xara Group Ltd) Created: 19/04/95 Inputs: - Returns: error flag Purpose: processes an entry in the vector list SeeAlso: Filter ********************************************************************************************/ BOOL CDRFilter::ProcessVectorListEntry() { UINT32 StartLevel = RIFF->GetObjLevel(); ERROR3IF(RIFF->GetObjType() != RIFFOBJECTTYPE_LISTSTART && RIFF->GetObjChunkType() != cdrT_grp, "ProcessVectorListEntry called without RIFF set up correctly"); // OK, wander though to find the serial number and the bbox WORD Reference; // the serial number DocRect BBox; BOOL GotReference = FALSE; BOOL GotBBox = FALSE; do { if(RIFF->GetObjType() == RIFFOBJECTTYPE_CHUNK) { switch(RIFF->GetObjChunkType()) { case cdrT_spnd: { // got the serial number block - get it... if(!RIFF->AquireChunkData()) return FALSE; WORD *Data = (WORD *)RIFF->GetAquiredData(); if(Data == 0) return FALSE; // store it... Reference = CDRDATA_WORD(*Data); GotReference = TRUE; } break; case cdrT_bbox: { // is it large enought? if(RIFF->GetObjSize() < sizeof(cdrfBBox)) { // no - ignore this entry RIFF->SkipToListEnd(StartLevel); return TRUE; } // found the bbox... get the data if(!RIFF->AquireChunkData()) return FALSE; cdrfBBox *Data = (cdrfBBox *)RIFF->GetAquiredData(); if(Data == 0) return FALSE; // get the bounding box BBox.lo.x = CDRDATA_SWORD(Data->x0) * CDRCOORDS_TO_MILLIPOINTS; BBox.lo.y = CDRDATA_SWORD(Data->y0) * CDRCOORDS_TO_MILLIPOINTS; BBox.hi.x = CDRDATA_SWORD(Data->x1) * CDRCOORDS_TO_MILLIPOINTS; BBox.hi.y = CDRDATA_SWORD(Data->y1) * CDRCOORDS_TO_MILLIPOINTS; GotBBox = TRUE; } break; default: break; } } if(!RIFF->NextObject()) return FALSE; } while((GotReference == FALSE || GotBBox == FALSE) && (RIFF->GetObjType() != RIFFOBJECTTYPE_LISTEND || RIFF->GetObjLevel() > StartLevel)); // OK, now get the object tree if(!ProcessObjectTree(StartLevel)) return FALSE; // right then, the converted nodes are in pLevelNodeList // create a nice list entry CDRVectorStoredItem *Item = new CDRVectorStoredItem; if(Item == 0) return FALSE; // set the object data Item->Objects = pLevelNodeList; Item->Reference = Reference; Item->BBox = BBox; // make the node list unused pLevelNodeList = 0; // add the list entry Vectors.AddTail(Item); return TRUE; } /******************************************************************************************** > Node *CDRVectorStore::Find(DWORD Reference, DocRect *BBox) Author: Ben_Summers (Xara Group Ltd) Created: 19/04/95 Inputs: reference to find, bounding box of found stuff Returns: pointer to a node list, or zero for not found Purpose: find a vector from the vectors list SeeAlso: Filter ********************************************************************************************/ Node *CDRVectorStore::Find(DWORD Reference, DocRect *BBox) { CDRVectorStoredItem *Item = (CDRVectorStoredItem *)GetHead(); // go through the list of items searching for the reference number given while(Item != 0) { if(Item->Reference == Reference) { // OK, we found it... if(BBox != 0) *BBox = Item->BBox; return Item->Objects; } Item = (CDRVectorStoredItem *)GetNext(Item); } return 0; } /******************************************************************************************** > KernelBitmap *CDRVectorStore::FindAsBitmap(DWORD Reference, DocRect *BBox) Author: Ben_Summers (Xara Group Ltd) Created: 19/04/95 Inputs: reference to find, bounding box of found stuff Returns: pointer to a node list, or zero for not found Purpose: find a vector from the vectors list SeeAlso: Filter ********************************************************************************************/ CDRVectorStoredItem *CDRVectorStore::Find(DWORD Reference) { CDRVectorStoredItem *Item = (CDRVectorStoredItem *)GetHead(); // go through the list of items searching for the reference number given while(Item != 0) { if(Item->Reference == Reference) { // OK, we found it... return Item; } Item = (CDRVectorStoredItem *)GetNext(Item); } return 0; } /******************************************************************************************** > KernelBitmap *CDRFilter::BitmapFromNodeList(Node *List) Author: Ben_Summers (Xara Group Ltd) Created: 20/04/95 Inputs: pointer to first node in a list Returns: a pointer to the relevant bitmap, or zero if one couldn't be found Purpose: given a list of nodes, if it only contains one bitmap then return a pointer to that bitmap SeeAlso: Filter ********************************************************************************************/ KernelBitmap *CDRFilter::BitmapFromNodeList(Node *List) { // make sure we've got something if(List == 0) return 0; // is there's more than one item? if(List->FindNext() != 0) return 0; // is it a bitmap? if(IS_A(List, NodeBitmap)) { // right then, woz it's bitmap den? NodeBitmap *NB = (NodeBitmap *)List; // return the bitmap... return NB->GetBitmap(); } return 0; }