00001 // $Id: brshcomp.cpp 1732 2006-09-03 14:08:16Z 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 00099 // Brush Component and Brush Definition headers 00100 00101 #include "camtypes.h" 00102 //#include "app.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00103 //#include "camfiltr.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00104 #include "brshcomp.h" 00105 #include "linecomp.h" 00106 //#include "colormgr.h" 00107 //#include "cxfrec.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00108 //#include "cxfrech.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00109 #include "cxftags.h" 00110 //#include "fixmem.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00111 #include "layer.h" 00112 //#include "spread.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00113 #include "brushref.h" 00114 #include "freehand.h" 00115 #include "freeinfo.h" 00116 //#include "xlong.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00117 #include "ppbrush.h" 00118 #include "brshattr.h" 00119 #include "sgline.h" 00120 //#include "loadbrsh.h" 00121 #include "fileutil.h" 00122 #include "clipint.h" 00123 #include "nodebldr.h" 00124 #include "bldbrdef.h" 00125 //#include "fillattr.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00126 //#include "fillval.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00127 #include "lineattr.h" 00128 #include "brshdata.h" 00129 //#include "swfrndr.h" 00130 #include "brshname.h" 00131 //#include "resdll.h" 00132 #include "nodeshad.h" 00133 #include "nodecntr.h" 00134 #include "nodebev.h" 00135 //#include "group.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00136 //#include "opdrbrsh.h" 00137 #include "toollist.h" 00138 //#include "freeinfo.h" 00139 #include "brshbeca.h" 00140 //#include "brdlgres.h" 00141 #include "brushop.h" 00142 00143 DECLARE_SOURCE("$Revision: 1732 $"); 00144 00145 CC_IMPLEMENT_DYNAMIC(BrushDefinition, LineDefinition) 00146 CC_IMPLEMENT_DYNAMIC(BrushComponent, LineComponent) 00147 CC_IMPLEMENT_DYNAMIC(BrushComponentClass, DocComponentClass) 00148 00149 // Declare smart memory handling in Debug builds 00150 #define new CAM_DEBUG_NEW 00151 00152 BOOL BrushComponent::m_bCancelNewBrush = FALSE; 00153 String_32 BrushComponent::m_NewName = TEXT("Unnamed brush"); 00154 00155 00156 const INT32 MAX_TRANSP_VALUE = 255; 00157 /******************************************************************************************** 00158 00159 > BrushDefinition::BrushDefinition(Node *pBrushTree); 00160 00161 Author: Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com> 00162 Created: 27/2/97 00163 00164 Inputs: pBrushTree - A pointer to a Spread node which is the root of a clipart 00165 subtree which should be used for this Brush definition. It 00166 must obey these rules: 00167 * It must be a Spread 00168 * It must not be linked into any other parent tree structure 00169 * It should contain at least one ink node (or else the Brush 00170 will appear "blank"). 00171 * It should be attribute complete, or close thereto 00172 00173 Purpose: Constructor 00174 00175 SeeAlso: BrushComponent::AddNewBrush 00176 00177 ********************************************************************************************/ 00178 00179 BrushDefinition::BrushDefinition(Node* pBrushTree) 00180 : LineDefinition(pBrushTree) 00181 { 00182 ResetMembers(); 00183 InitialiseBrushArray(MAX_BRUSH_OBJECTS); 00184 if (!GenerateBrush()) 00185 { 00186 // hmm, not sure what to do here, but you can detect if we failed to initialise 00187 // by calling IsActivated, if that fails you should delete me. 00188 } 00189 00190 } 00191 00192 00193 /******************************************************************************************** 00194 00195 > BrushDefinition::BrushDefinition() 00196 00197 Author: Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com> 00198 Created: 27/2/97 00199 00200 Purpose: Default constructor for the purposes of inherited classes, if you just want a 00201 normal brushdef you should really use the constructor above. 00202 00203 5/12/2000 Actually I've just read that it is a poor idea to initialise inside 00204 constructors, so I think that a reorganisation should occur whereby the construcotr 00205 should simply assign the brush tree and we should GenerateBrush after the constructor 00206 returns. 00207 00208 ********************************************************************************************/ 00209 00210 BrushDefinition::BrushDefinition() 00211 { 00212 ResetMembers(); 00213 } 00214 00215 00216 /******************************************************************************************** 00217 00218 > BrushDefinition::~BrushDefinition() 00219 00220 Author: Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com> 00221 Created: 27/2/97 00222 00223 Purpose: Destructor 00224 00225 ********************************************************************************************/ 00226 00227 BrushDefinition::~BrushDefinition() 00228 { 00229 for( size_t i = 0; i < m_BrushRefPtrArray.size(); i++ ) 00230 { 00231 // ask the brushref to delete the attribute maps it generated 00232 if (m_BrushRefPtrArray[i] != NULL) 00233 { 00234 // m_BrushRefPtrArray[i]->DeleteAttributeMapsAndAttributes(); 00235 delete m_BrushRefPtrArray[i]; 00236 } 00237 } 00238 m_BrushRefPtrArray.clear(); 00239 00240 00241 00242 } 00243 00244 00245 00246 /*********************************************************************************************** 00247 00248 > void BrushDefinition::ResetMembers() 00249 00250 Author: Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com> 00251 Created: 6/10/99 00252 Inputs: - 00253 Returns - 00254 Purpose: Initialises the member variables 00255 00256 ***********************************************************************************************/ 00257 00258 void BrushDefinition::ResetMembers() 00259 { 00260 m_BrushSpacing = 10000; 00261 m_BrushSpacingIncrProp = 1.0; 00262 m_BrushSpacingIncrConst = 0; 00263 m_BrushSpacingMaxRand = 0; 00264 m_BrushSpacingRandSeed = 0; 00265 00266 m_PathOffsetType = OFFSET_NONE; 00267 m_PathOffsetValue = 0; 00268 m_PathOffsetIncrProp = 1.0; 00269 m_PathOffsetIncrConst = 0; 00270 m_OffsetTypeRandSeed = 0; 00271 m_OffsetValueMaxRand = 0; 00272 m_OffsetValueRandSeed = 0; 00273 00274 m_bRotate = TRUE; 00275 m_RotateAngle = 0; 00276 m_RotationMaxRand = 0; 00277 m_RotationRandSeed = 0; 00278 m_RotAngleIncrConst = 0; 00279 m_RotAngleIncrProp = 1.0; 00280 m_RotationMaxPressure = 0; 00281 00282 m_bTile = TRUE; 00283 00284 m_BrushScaling = 1.0; 00285 m_BrushScalingIncr = 1.0; 00286 m_BrushScalingIncrConst = 0.0; 00287 m_BrushScalingMaxRand = 0; 00288 m_BrushScalingRandSeed = 0; 00289 m_ScalingMaxPressure = 35; 00290 00291 m_BrushHueIncrement = 0.0; 00292 m_BrushHueMaxRand = 0; 00293 m_BrushHueRandSeed = 1234; 00294 00295 m_BrushSatIncrement = 0.0; 00296 m_BrushSatMaxRand = 0; 00297 m_BrushSatRandSeed = 5432; 00298 00299 m_SequenceType = SEQ_FORWARD; 00300 m_SequenceRandSeed = 0; 00301 00302 m_NumBrushObjects = 0; 00303 m_bInitOk = FALSE; 00304 00305 m_LargestBoundingBox.MakeEmpty(); 00306 00307 m_Name = TEXT("Custom Brush"); 00308 00309 m_TimeStampPeriod = 0; 00310 00311 m_BrushTransparency = 100; 00312 m_TranspMaxPressure = 0; 00313 00314 m_DefaultFileID = BRUSHFILE_NONE; 00315 00316 m_bActivated = FALSE; 00317 } 00318 00319 00320 00321 /*********************************************************************************************** 00322 00323 > BOOL BrushDefinition::GenerateBrush() 00324 00325 Author: Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com> 00326 Created: 13/12/99 00327 Inputs: - 00328 Returns TRUE if successful, FALSE otherwise 00329 Purpose: goes through the input spread and calls AddNewObject() for each ink object found 00330 We try to make some intelligent guesses about what to set the starting spacing and 00331 offset values depending on the nature of the nodes. The following rules apply: 00332 - if there are multiple ink objects then the spacing is 1.1 * the largest dimension 00333 of the largest bounding box 00334 - if there is only one object and it contains a positive line width spacing is as above 00335 - if there is only one object with no line width then spacing is 10000 millipoint 00336 - defaulf offset is always the largest dimension of the largest BBOX. 00337 00338 ***********************************************************************************************/ 00339 00340 BOOL BrushDefinition::GenerateBrush() 00341 { 00342 if (m_pTree == NULL) 00343 return FALSE; 00344 00345 // get a node that points at the first child of the layer 00346 Node* pNode = m_pTree->FindFirstChild(); 00347 pNode = pNode->FindFirstChild(); 00348 00349 00350 AttrLineWidth* pLineWidth = NULL; 00351 while (pNode != NULL) 00352 { 00353 if (pNode->IsAnObject()) 00354 { 00355 // it has been drawn to my attention that it is possible to end up with a brush object 00356 // that has no line width attribute, which leads to all kinds of problems. So here we will 00357 // search for one and if we don't find one we will add one. 00358 ((NodeRenderableInk*)pNode)->FindAppliedAttribute(CC_RUNTIME_CLASS(AttrLineWidth), (NodeAttribute**)&pLineWidth); 00359 if (!pLineWidth) 00360 { 00361 pLineWidth = new AttrLineWidth(pNode, LASTCHILD); 00362 if (pLineWidth) 00363 { 00364 pLineWidth->Value.LineWidth = 501; 00365 pLineWidth = NULL; 00366 } 00367 } 00368 00369 // we don't want to make brushes from brushes, instead we want to convert them 00370 // to shapes first, so use the routine to do that. 00371 NodeGroup* pGroup = NULL; 00372 ReplaceBrushWithGroup((NodeRenderableInk*)pNode, &pGroup); 00373 if (pGroup != NULL) 00374 { 00375 // we got a group, so we have to delete the original node 00376 pGroup->AttachNode(pNode, NEXT); 00377 pGroup->FactorOutCommonChildAttributes(); 00378 // unhook from the tree 00379 pNode->CascadeDelete(); 00380 00381 // swap the pointers 00382 Node* pDelete = pNode; 00383 pNode = pGroup; 00384 00385 delete pDelete; 00386 } 00387 00388 if (!AddNewObject((NodeRenderableInk*)pNode)) 00389 return FALSE; 00390 } 00391 pNode = pNode->FindNext(); 00392 } 00393 CalculateMaxScaling(); 00394 00395 m_bActivated = TRUE; 00396 return TRUE; 00397 00398 } 00399 00400 00401 /*********************************************************************************************** 00402 00403 > BOOL BrushDefinition::ReplaceBrushWithGroup(NodeRenderableInk* pInk, NodeGroup* pGroup) 00404 00405 Author: Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com> 00406 Created: 13/12/99 00407 Inputs: pInk - the inknode that we are going to generate a brush from 00408 Outputs: if pInk has a brush attribute applied to it then we will generate a new group 00409 from that brush 00410 Returns TRUE if successful, FALSE otherwise 00411 Purpose: We are no longer allowed to make brushes from brushes, because of problems 00412 when saving and loading. Instead if we have a brush then we will convert 00413 it to shapes and use those instead. 00414 00415 Note: So you actually have to delete pInk yourself if this function outputs a group. 00416 I had to do that to get the recursion to work. Still I'm sure you can handle it. 00417 ***********************************************************************************************/ 00418 00419 BOOL BrushDefinition::ReplaceBrushWithGroup(NodeRenderableInk* pInk, NodeGroup** ppGroup) 00420 { 00421 ERROR2IF(pInk == NULL, FALSE, "Invalid inputs to BrushDefinition::ReplaceBrushWithGroups"); 00422 00423 BOOL ok = TRUE; 00424 // if we're compound then search through all our children first 00425 if (pInk->IsCompound()) 00426 { 00427 NodeGroup* pNodeGroup = NULL; 00428 Node* pChild = pInk->FindFirstChild(); 00429 Node* pDelete = NULL; 00430 00431 while (pChild) 00432 { 00433 if (pChild->IsAnObject()) 00434 { 00435 if (ok) ok = ReplaceBrushWithGroup((NodeRenderableInk*)pChild, &pNodeGroup); 00436 if (pNodeGroup) 00437 { 00438 // attach the new group next to the child 00439 pNodeGroup->AttachNode(pChild, NEXT); 00440 00441 pNodeGroup->FactorOutCommonChildAttributes(); 00442 00443 // unhook the child 00444 pChild->CascadeDelete(); 00445 00446 // we're ready to delete the original child 00447 pDelete = pChild; 00448 00449 // swap the pointer 00450 pChild = pNodeGroup; 00451 00452 delete pDelete; 00453 } 00454 } 00455 pChild = pChild->FindNext(); 00456 pNodeGroup = NULL; 00457 } 00458 } 00459 00460 AttrBrushType* pAttrBrush = NULL; 00461 pInk->FindAppliedAttribute(CC_RUNTIME_CLASS(AttrBrushType), (NodeAttribute**)&pAttrBrush); 00462 00463 if (pAttrBrush && pAttrBrush->GetBrushHandle() != BrushHandle_NoBrush && ok) 00464 { 00465 //Turn the brush into a group with lots of nodepaths 00466 BrushBecomeAGroup BecomeA(BECOMEA_PASSBACK, CC_RUNTIME_CLASS(NodePath), NULL); 00467 ok = pAttrBrush->DoBecomeA(&BecomeA, pInk); 00468 00469 // the brush will create a group out of itself and we want to retrieve that 00470 NodeGroup* pBrushGroup = BecomeA.GetNodeGroup(); 00471 pBrushGroup->FactorOutCommonChildAttributes(); 00472 pBrushGroup->NormaliseAttributes(); 00473 if (pBrushGroup != NULL && ok) 00474 { 00475 *ppGroup = pBrushGroup; 00476 } 00477 } 00478 return ok; 00479 } 00480 00481 /*********************************************************************************************** 00482 00483 > BOOL BrushDefinition::RegenerateBrush() 00484 00485 Author: Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com> 00486 Created: 13/12/99 00487 Inputs: - 00488 Returns TRUE if successful, FALSE otherwise 00489 Purpose: This function deletes the existing brushrefs and recreates them from scratch from 00490 m_pTree. 00491 00492 Notes: This function is necessary because of the inaccuracies in our matrix transformation 00493 system which make it impossible to repeatedly scale without either growing our objects. 00494 Therefore it is recommended that after every render where an object is scaled this function 00495 should be called to regenerate the brush. 00496 Obviously the potential for disaster is very high for instance we run out of memory 00497 in the middle of the operation. 00498 00499 ***********************************************************************************************/ 00500 00501 BOOL BrushDefinition::RegenerateBrush() 00502 { 00503 /* As we now always take copies of the brush objects, this is scheduled for demolition... 00504 00505 // first we need to transform all the attributes back to where they began 00506 PreExportSubTree(); 00507 00508 // make a temporary array to hold the existing elements, as we do not wish 00509 // to delete them until we are sure regeneration went ok 00510 CTypedPtrArray <CPtrArray, BrushRef*> TempArray; 00511 00512 TempArray.SetSize(MAX_BRUSH_OBJECTS, -1); 00513 00514 // fill the array with our existing objects 00515 BrushRef* pRef = NULL; 00516 INT32 i = 0; 00517 for (i = 0; i < TempArray.GetSize(); i++) 00518 { 00519 pRef = m_BrushRefPtrArray[i]; 00520 TempArray.SetAt(i, pRef); 00521 m_BrushRefPtrArray.SetAt(i, NULL); // set existing pointer to null 00522 } 00523 00524 m_NumBrushObjects = 0; 00525 00526 // now remake ourselves 00527 if (GenerateBrush() == TRUE) 00528 { 00529 // delete the existing brushrefs 00530 for (i = 0; i < TempArray.GetSize(); i++) 00531 { 00532 if (TempArray[i] != NULL) 00533 { 00534 delete TempArray[i]; 00535 } 00536 } 00537 TempArray.RemoveAll(); 00538 00539 return TRUE; 00540 } 00541 00542 // ok so something went wrong, not much we can do except for restore the original objects 00543 // and hope for the best 00544 ERROR3("Uh oh - Regenerate Brush failed"); 00545 pRef = NULL; 00546 i= 0; 00547 for (i = 0; i < TempArray.GetSize(); i++) 00548 { 00549 pRef = TempArray[i]; 00550 m_BrushRefPtrArray.SetAt(i, pRef); // set existing pointer to null 00551 TempArray[i] = NULL; 00552 } 00553 */ 00554 return TRUE; 00555 } 00556 00557 00558 00559 /*********************************************************************************************** 00560 00561 > BOOL BrushDefinition::IsActivated() 00562 00563 Author: Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com> 00564 Created: 13/12/99 00565 Inputs: - 00566 Returns the member indicating whether this definition is active 00567 Purpose: To see if this definition was initialised ok or if it was subsequently deactivated 00568 for some reason 00569 00570 ***********************************************************************************************/ 00571 00572 BOOL BrushDefinition::IsActivated() 00573 { 00574 return m_bActivated; 00575 } 00576 00577 00578 /*********************************************************************************************** 00579 00580 > void BrushDefinition::SetActivated(BOOL Value) 00581 00582 Author: Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com> 00583 Created: 13/12/99 00584 Inputs: Value - the value to set 00585 Returns - 00586 Purpose: To set this definition activated or not 00587 00588 ***********************************************************************************************/ 00589 00590 void BrushDefinition::SetActivated(BOOL Value) 00591 { 00592 m_bActivated = Value; 00593 } 00594 00595 /*********************************************************************************************** 00596 00597 > BOOL BrushDefinition::AddNewObject(NodeRenderableInk* pInkNode) 00598 00599 Author: Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com> 00600 Created: 13/12/99 00601 Inputs: pInkNode - node to add 00602 Returns TRUE if successful, FALSE otherwise 00603 Purpose: creates a brushref object from the inknode and its attributes, then adds the 00604 brushref to the member array 00605 00606 ***********************************************************************************************/ 00607 00608 BOOL BrushDefinition::AddNewObject(NodeRenderableInk* pInkNode) 00609 { 00610 00611 if (pInkNode == NULL) 00612 { 00613 ERROR3("Ink node is NULL"); 00614 return FALSE; 00615 } 00616 00617 if (m_NumBrushObjects >= MAX_BRUSH_OBJECTS) 00618 { 00619 ERROR3("Cannot exceed MAX_BRUSH_OBJECTS"); 00620 return FALSE; 00621 } 00622 00623 BrushRef *pNewBrushRef = new BrushRef; 00624 00625 if (pNewBrushRef == NULL) 00626 { 00627 ERROR3("Failed to allocate brushref"); 00628 return FALSE; 00629 } 00630 00631 00632 00633 if (!pNewBrushRef->Initialise(pInkNode)) 00634 { 00635 ERROR3("Failed to initialise brushref"); 00636 return FALSE; 00637 } 00638 00639 m_BrushRefPtrArray[m_NumBrushObjects++] = pNewBrushRef; 00640 00641 return TRUE; 00642 00643 } 00644 00645 00646 /*********************************************************************************************** 00647 00648 > void BrushDefinition::InitialiseBrushArray(UINT32 NumObjects) 00649 00650 Author: Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com> 00651 Created: 6/10/99 00652 Inputs: Number of objects to size the array at 00653 Returns: - 00654 Purpose: Clears out the m_BrushRefPtrArray if it is not empty, and sets the size 00655 00656 ***********************************************************************************************/ 00657 00658 void BrushDefinition::InitialiseBrushArray(UINT32 NumObjects) 00659 { 00660 size_t i = 0; 00661 while( i < m_BrushRefPtrArray.size() ) 00662 { 00663 delete m_BrushRefPtrArray[i++]; 00664 } 00665 m_BrushRefPtrArray.clear(); 00666 00667 m_BrushRefPtrArray.resize( NumObjects ); 00668 00669 // fill the array with NULL objects so that we can check later 00670 // to see if our allocations have worked 00671 i = 0; 00672 while( i < m_BrushRefPtrArray.size() ) 00673 { 00674 m_BrushRefPtrArray[i++] = NULL; 00675 } 00676 00677 } 00678 00679 00680 /******************************************************************************************** 00681 00682 > BOOL BrushDefinition::IsDifferent(BrushDefinition *pOther) 00683 00684 Author: Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com> 00685 Created: 13/12/99 00686 00687 Inputs: pOther - the Brush to compare this Brush to 00688 00689 Returns: TRUE if they're different in any way, 00690 FALSE if they are identical definitions 00691 00692 Purpose: Determine if 2 BrushDefinitions are considered different. 00693 Used when adding Brushes to the global list, so that like Brushes 00694 can be merged. 00695 00696 ********************************************************************************************/ 00697 00698 BOOL BrushDefinition::IsDifferent(LineDefinition *pOther) 00699 { 00700 ERROR3IF(pOther == NULL, "Illegal NULL param"); 00701 00702 // String_32* pName = pOther->GetLineName(); 00703 if (!m_Name.IsIdentical(*pOther->GetLineName())) 00704 return(TRUE); 00705 00706 if (pOther->GetLineTree() == NULL || m_pTree == NULL) 00707 { 00708 ERROR3("BrushDefinition has not been properly initialised"); 00709 return(TRUE); 00710 } 00711 00712 /* 00713 // --- Check to see if the brush bounds are equal 00714 DocRect OtherBounds = ((Spread *)(pOther->GetLineTree()))->GetBoundingRect(); 00715 DocRect Bounds = ((Spread *)m_pTree)->GetBoundingRect(); 00716 if (Bounds != OtherBounds) 00717 return(TRUE); 00718 */ 00719 // --- Check the subtrees node-for-node to see if they are the same 00720 Node *pCurNode1 = m_pTree->FindFirstDepthFirst(); 00721 Node *pCurNode2 = pOther->GetLineTree()->FindFirstDepthFirst(); 00722 00723 while (pCurNode1 != NULL && pCurNode2 != NULL) 00724 { 00725 // if they are fill attributes then we want to translate both attributes to the origin. 00726 // This prevents us getting duplication of brushes when we have the 00727 // same thing in a different place, note that occasionally this fails due to rounding errors. 00728 if (pCurNode1->IsAFillAttr() && pCurNode2->IsAFillAttr() && 00729 !((AttrFillGeometry*)pCurNode1)->IsAStrokeColour() && 00730 !((AttrFillGeometry*)pCurNode1)->IsAStrokeTransp()) 00731 { 00732 AttrFillGeometry* pFill1 = (AttrFillGeometry*)pCurNode1; 00733 AttrFillGeometry* pFill2 = (AttrFillGeometry*)pCurNode2; 00734 DocCoord* pStartPoint1 = pFill1->GetStartPoint(); 00735 DocCoord* pStartPoint2 = pFill2->GetStartPoint(); 00736 00737 // translate them both to 0,0 00738 if (pStartPoint1 && pStartPoint2) 00739 { 00740 Trans2DMatrix Trans1(-pStartPoint1->x, -pStartPoint1->y); 00741 Trans2DMatrix Trans2(-pStartPoint2->x, -pStartPoint2->y); 00742 pFill1->Transform(Trans1); 00743 pFill2->Transform(Trans2); 00744 BOOL Different = FALSE; 00745 if (pCurNode1->IsDifferent(pCurNode2)) 00746 Different = TRUE; 00747 00748 Trans1.Invert(); 00749 Trans2.Invert(); 00750 pFill1->Transform(Trans1); 00751 pFill2->Transform(Trans2); 00752 if (Different == TRUE) 00753 return TRUE; 00754 } 00755 00756 } 00757 else 00758 // likewise if they are an object then translate from the centre of the bounding 00759 // rect to the origin 00760 if (pCurNode1->IsAnObject() && pCurNode2->IsAnObject()) 00761 { 00762 NodeRenderableInk* pInk1 = (NodeRenderableInk*)pCurNode1; 00763 NodeRenderableInk* pInk2 = (NodeRenderableInk*)pCurNode2; 00764 DocRect BRect1 = ((NodeRenderableInk*)pCurNode1)->GetBoundingRect(); 00765 DocRect BRect2 = ((NodeRenderableInk*)pCurNode2)->GetBoundingRect(); 00766 DocCoord Coord1 = BRect1.Centre(); 00767 DocCoord Coord2 = BRect2.Centre(); 00768 Trans2DMatrix Trans1(-Coord1.x, -Coord1.y); 00769 Trans2DMatrix Trans2(-Coord2.x, -Coord2.y); 00770 pInk1->Transform(Trans1); 00771 pInk2->Transform(Trans2); 00772 BOOL Different = FALSE; 00773 if (pCurNode1->IsDifferent(pCurNode2)) 00774 Different = TRUE; 00775 00776 Trans1.Invert(); 00777 Trans2.Invert(); 00778 pInk1->Transform(Trans1); 00779 pInk2->Transform(Trans2); 00780 if (Different ==TRUE) 00781 return TRUE; 00782 } 00783 else 00784 { 00785 // otherwise just use the regular == operator 00786 if (pCurNode1->IsDifferent(pCurNode2)) 00787 return(TRUE); 00788 } 00789 00790 // And go to the next node in both brushes 00791 pCurNode1 = pCurNode1->FindNextDepthFirst(m_pTree); 00792 pCurNode2 = pCurNode2->FindNextDepthFirst(pOther->GetLineTree()); 00793 } 00794 00795 if (!AreBrushParametersIdentical((BrushDefinition*)pOther)) 00796 return TRUE; 00797 00798 // If we did the entire search and both pointers ended up NULL simultaneously, then 00799 // we have an exact match 00800 if (pCurNode1 == NULL && pCurNode2 == NULL) 00801 return(FALSE); 00802 00803 return(TRUE); 00804 } 00805 00806 00807 /******************************************************************************************** 00808 00809 > BOOL BrushDefinition::IsDifferent(BrushDefinition *pOther) 00810 00811 Author: Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com> 00812 Created: 13/12/99 00813 00814 Inputs: pOther - the Brush to compare this Brush to 00815 00816 Returns: TRUE if they're different 00817 FALSE if they are identical 00818 00819 Purpose: Compares the member variables of the two brushes 00820 00821 ********************************************************************************************/ 00822 00823 BOOL BrushDefinition::AreBrushParametersIdentical(BrushDefinition* pOther) 00824 { 00825 if (m_BrushSpacing != pOther->m_BrushSpacing || 00826 m_BrushSpacingIncrProp != pOther->m_BrushSpacingIncrProp || 00827 m_BrushSpacingIncrConst != pOther->m_BrushSpacingIncrConst || 00828 m_BrushSpacingMaxRand != pOther->m_BrushSpacingMaxRand || 00829 m_BrushSpacingRandSeed != pOther->m_BrushSpacingRandSeed || 00830 m_SpacingMaxPressure != pOther->m_SpacingMaxPressure || 00831 00832 m_bTile != pOther->m_bTile || 00833 m_bRotate != pOther->m_bRotate || 00834 00835 m_RotateAngle != pOther->m_RotateAngle || 00836 m_RotAngleIncrProp != pOther->m_RotAngleIncrProp || 00837 m_RotAngleIncrConst != pOther->m_RotAngleIncrConst || 00838 m_RotationMaxRand != pOther->m_RotationMaxRand || 00839 m_RotationRandSeed != pOther->m_RotationRandSeed || 00840 m_RotationMaxPressure != pOther->m_RotationMaxPressure || 00841 00842 m_PathOffsetType != pOther->m_PathOffsetType || 00843 m_PathOffsetValue != pOther->m_PathOffsetValue || 00844 m_PathOffsetIncrProp != pOther->m_PathOffsetIncrProp || 00845 m_PathOffsetIncrConst != pOther->m_PathOffsetIncrConst || 00846 m_OffsetTypeRandSeed != pOther->m_OffsetTypeRandSeed || 00847 m_OffsetValueMaxRand != pOther->m_OffsetValueMaxRand || 00848 m_OffsetValueRandSeed != pOther->m_OffsetValueRandSeed || 00849 00850 m_BrushScaling != pOther->m_BrushScaling || 00851 m_BrushScalingIncr != pOther->m_BrushScalingIncr || 00852 m_BrushScalingIncrConst != pOther->m_BrushScalingIncrConst || 00853 m_BrushScalingMaxRand != pOther->m_BrushScalingMaxRand || 00854 m_BrushScalingRandSeed != pOther->m_BrushScalingRandSeed || 00855 m_ScalingMaxPressure != pOther->m_ScalingMaxPressure || 00856 00857 m_BrushHueIncrement != pOther->m_BrushHueIncrement || 00858 m_BrushHueMaxRand != pOther->m_BrushHueMaxRand || 00859 m_BrushHueRandSeed != pOther->m_BrushHueRandSeed || 00860 m_HueMaxPressure != pOther->m_HueMaxPressure || 00861 00862 m_BrushSatIncrement != pOther->m_BrushSatIncrement || 00863 m_BrushSatMaxRand != pOther->m_BrushSatMaxRand || 00864 m_BrushSatRandSeed != pOther->m_BrushSatRandSeed || 00865 m_SatMaxPressure != pOther->m_SatMaxPressure || 00866 00867 m_SequenceType != pOther->m_SequenceType || 00868 m_SequenceRandSeed != pOther->m_SequenceRandSeed || 00869 00870 m_TimeStampPeriod != pOther->m_TimeStampPeriod) 00871 return FALSE; 00872 else 00873 return TRUE; 00874 } 00875 00876 00877 /******************************************************************************************** 00878 00879 > BrushDefinition* BrushDefinition::Copy() 00880 00881 Author: Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com> 00882 Created: 22/5/2000 00883 00884 Inputs: - 00885 00886 Returns: Pointer to new brush definition if succesful, NULL if it fails 00887 Purpose: Makes a copy of this definition, inknodes and all. 00888 00889 ********************************************************************************************/ 00890 00891 BrushDefinition* BrushDefinition::Copy() 00892 { 00893 // we need to make a range in order to copy the nodes to the new brush definition 00894 // so we need the start and end nodes of the range 00895 Node* pStart = NULL; 00896 Node* pNext = NULL; 00897 Node* pLast = NULL; 00898 00899 // don't forget we start on a spread 00900 Node* pTree = GetLineTree(); 00901 if (pTree != NULL) 00902 pTree = pTree->FindFirstChild(); // get the layer 00903 if (pTree != NULL) 00904 pTree = pTree->FindFirstChild(); // get the first ink object 00905 00906 if (pTree == NULL) 00907 { 00908 ERROR3("Tree node is NULL in BrushDefinition::Copy()"); 00909 return NULL; 00910 } 00911 00912 pNext = pStart = pTree; 00913 while (pNext != NULL) 00914 { 00915 pNext->SetSelected(FALSE); // we need to do this so that Range::FindFirst doesn't return NULL 00916 pLast = pNext; 00917 pNext = pLast->FindNext(); 00918 } 00919 00920 00921 // make a range out of our subtree as thats the easiest way to copy all the nodes 00922 RangeControl rc; 00923 rc.Unselected = TRUE; 00924 rc.Selected = FALSE; 00925 Range BrushRange(pStart, pLast, rc); 00926 00927 // make a new spread to attach our copy tree to 00928 Spread* pSpread = new Spread; 00929 if (pSpread == NULL) 00930 { 00931 ERROR3("Failed to allocate spread in BrushDefinition::Copy"); 00932 return NULL; 00933 } 00934 00935 Layer *pLayer = new Layer( pSpread, FIRSTCHILD, String_256( TEXT("Diccon did this") ) ); 00936 if (pLayer == NULL) 00937 { 00938 ERROR3("Failed to allocate layer in BrushDefinition::Copy"); 00939 delete pSpread; 00940 return NULL; 00941 } 00942 00943 // we need to reset our attributes in the same way that we need to with exporting. 00944 // If we don't do this then attributes with control points end up vanishing 00945 PreExportSubTree(); 00946 00947 if (!pLayer->CopyComplexRange(BrushRange)) 00948 { 00949 ERROR3("Failed to copy range in BrushDefinition::Copy"); 00950 delete pSpread; 00951 delete pLayer; 00952 return NULL; 00953 } 00954 00955 PostExportSubTree(); 00956 00957 // lets allocate ourselves a new empty definition 00958 BrushDefinition* pNewBrushDef = new BrushDefinition(pSpread); 00959 00960 if (pNewBrushDef == NULL) 00961 { 00962 ERROR3("Failed to allocate brush definition in BrushDefinition* BrushDefinition::Copy"); 00963 delete pSpread; 00964 delete pLayer; 00965 return NULL; 00966 } 00967 00968 if (!pNewBrushDef->IsActivated()) 00969 { 00970 ERROR3("Brush definition failed to initialise"); 00971 delete pNewBrushDef; 00972 delete pSpread; 00973 delete pLayer; 00974 return NULL; 00975 } 00976 00977 return pNewBrushDef; 00978 } 00979 00980 /*********************************************************************************************** 00981 00982 > BOOL BrushDefinition::UsesPressure() 00983 00984 Author: Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com> 00985 Created: 16/6/2000 00986 Inputs: - 00987 Returns TRUE if this brush has one of its pressure variables set 00988 Purpose: as above 00989 ***********************************************************************************************/ 00990 00991 BOOL BrushDefinition::UsesPressure() 00992 { 00993 if (m_ScalingMaxPressure != 0 || 00994 m_RotationMaxPressure != 0 || 00995 m_TranspMaxPressure != 0) 00996 return TRUE; 00997 00998 return FALSE; 00999 } 01000 01001 01002 /*********************************************************************************************** 01003 01004 > static BOOL BrushDefinition::ObjectCanCreateBrush(NodeRenderableInk* pObject) 01005 01006 Author: Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com> 01007 Created: 16/6/2000 01008 Inputs: pObject - the object to test 01009 Returns TRUE if we are allowed to make a brush out of this object, FALSE otherwise 01010 Purpose: To determine if this ink node can be made into a brush, currently returns TRUE for 01011 except for bevels, shadows, and contours, which are too damn awkward. 01012 ***********************************************************************************************/ 01013 01014 BOOL BrushDefinition::ObjectCanCreateBrush(NodeRenderableInk* pObject) 01015 { 01016 ERROR2IF(pObject == NULL, FALSE, "Object is NULL in BrushDefinition::ObjectCanCreateBrush"); 01017 01018 // only way to do it is some ugly IKO's 01019 if (pObject->IS_KIND_OF(NodeShadow)) 01020 return FALSE; 01021 if (pObject->IS_KIND_OF(NodeContour)) 01022 return FALSE; 01023 if (pObject->IS_KIND_OF(NodeBevel)) 01024 return FALSE; 01025 01026 // if its compound then check all the children 01027 if (pObject->IsCompound()) 01028 { 01029 Node* pChild = pObject->FindFirstChild(); 01030 while (pChild) 01031 { 01032 if (pChild->IsAnObject()) 01033 { 01034 if (!ObjectCanCreateBrush((NodeRenderableInk*)pChild)) 01035 return FALSE; 01036 } 01037 pChild = pChild->FindNext(); 01038 } 01039 } 01040 01041 return TRUE; 01042 01043 } 01044 01045 01046 /********************************************************************************************* 01047 01048 > BOOL BrushDefinition::StartRender() 01049 01050 Author: Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com> 01051 Created: 4/9/2000 01052 Inputs: - 01053 Outputs: - 01054 Returns: TRUE if successful, FALSE if not. If we return FALSE then don't even think 01055 about rendering with this brush. 01056 01057 Purpose: Basically due to imprecision in the arithmetic system we ended up corrupting the 01058 brush data if we used it too much. So to get around this we never transform 01059 the original brush data, instead we make copies of it each time we want to render 01060 a version of this brush. 01061 This function generates the data copies that we need to render this brush, make 01062 sure you call StopRender() to get rid of them. 01063 **********************************************************************************************/ 01064 01065 BOOL BrushDefinition::StartRender() 01066 { 01067 BrushRef* pBrushRef = GetFirstBrushRef(); 01068 BOOL ok = TRUE; 01069 while (pBrushRef != NULL) 01070 { 01071 if (ok) 01072 ok = pBrushRef->MakeCopiesForRendering(); 01073 pBrushRef = GetNextBrushRef(); 01074 } 01075 01076 return ok; 01077 01078 } 01079 01080 01081 01082 /********************************************************************************************* 01083 01084 > void BrushDefinition::StopRender() 01085 01086 Author: Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com> 01087 Created: 4/9/2000 01088 Inputs: - 01089 Outputs: - 01090 Returns: - 01091 01092 Purpose: Call this when you have finished rendering a node with this brush, it deletes 01093 the copied data that we used for rendering 01094 **********************************************************************************************/ 01095 01096 void BrushDefinition::StopRender() 01097 { 01098 BrushRef* pBrushRef = GetFirstBrushRef(); 01099 while (pBrushRef != NULL) 01100 { 01101 pBrushRef->DeleteRenderCopies(); 01102 pBrushRef = GetNextBrushRef(); 01103 } 01104 01105 return; 01106 01107 } 01108 01109 /*********************************************************************************************** 01110 01111 > void BrushDefinition::CalculateMaxScaling() 01112 01113 Author: Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com> 01114 Created: 13/12/99 01115 Inputs: - 01116 Returns - 01117 Purpose: Calculates the maximum scaling value possible by dividing the maximum size value by 01118 the longest side of the bounding rect of the largest object 01119 ***********************************************************************************************/ 01120 01121 void BrushDefinition::CalculateMaxScaling() 01122 { 01123 DocRect BRect = GetLargestBoundingBox(); 01124 if (BRect.IsEmpty()) 01125 { 01126 //ERROR3("Bounding rect is empty in BrushDefinition::CalculateMaxScaling"); 01127 return; 01128 } 01129 MILLIPOINT LongestSide = BRect.Height() > BRect.Width() ? BRect.Height() : BRect.Width(); 01130 m_MaxScaling = MAX_BRUSH_SIZE / LongestSide; 01131 01132 } 01133 01134 01135 /*********************************************************************************************** 01136 01137 > double BrushDefinition::GetMaxScaling() 01138 01139 Author: Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com> 01140 Created: 13/12/99 01141 Inputs: - 01142 Returns the maximum scaling value 01143 Purpose: as above 01144 ***********************************************************************************************/ 01145 01146 double BrushDefinition::GetMaxScaling() 01147 { 01148 return m_MaxScaling; 01149 } 01150 01151 /******************************************************************************************** 01152 01153 > BOOL BrushDefinition::SetBrushSpacing(double Spacing) 01154 01155 Author: Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com> 01156 Created: 6/10/99 01157 Inputs: the spacing value to set 01158 Returns: TRUE if successful, FALSE if the spacing is invalid 01159 Purpose: To set the spacing between brush objects 01160 SeeAlso: - 01161 01162 ********************************************************************************************/ 01163 01164 BOOL BrushDefinition::SetSpacing(MILLIPOINT Spacing) 01165 { 01166 if (Spacing < MIN_BRUSH_SPACING || Spacing > MAX_BRUSH_SPACING) 01167 return FALSE; 01168 01169 m_BrushSpacing = Spacing; 01170 01171 return TRUE; 01172 01173 } 01174 01175 01176 01177 /******************************************************************************************** 01178 01179 > MILLIPOINT BrushDefinition::GetBrushSpacing() 01180 01181 Author: Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com> 01182 Created: 6/10/99 01183 Returns: the spacing between the brush objects 01184 Purpose: As above 01185 01186 ********************************************************************************************/ 01187 01188 MILLIPOINT BrushDefinition::GetSpacing() 01189 { 01190 return m_BrushSpacing; 01191 } 01192 01193 01194 01195 /******************************************************************************************** 01196 01197 > BOOL PathProcessorBrush::SetSpacingIncrProp(double Incr) 01198 01199 Author: Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com> 01200 Created: 6/10/99 01201 Inputs: the spacing increment value to set 01202 Returns: TRUE if we can draw 10 objects, FALSE if the spacing is invalid 01203 Purpose: To set the proportional spacing increment between brush objects 01204 SeeAlso: - 01205 01206 ********************************************************************************************/ 01207 01208 BOOL BrushDefinition::SetSpacingIncrProp(double Incr) 01209 { 01210 if (Incr <= 0) 01211 return FALSE; 01212 01213 if (Incr != 1.0) 01214 { 01215 double TenIncr = pow(Incr, 10.0); 01216 double TenthSpacing = m_BrushSpacing * TenIncr; 01217 if (TenthSpacing >= MAX_BRUSH_SPACING || TenthSpacing < MIN_BRUSH_SPACING) 01218 { 01219 ERROR3("Illegal increment value"); 01220 return FALSE; 01221 } 01222 } 01223 m_BrushSpacingIncrProp = Incr; 01224 return TRUE; 01225 } 01226 01227 01228 01229 /******************************************************************************************** 01230 01231 > MILLIPOINT BrushDefinition::GetSpacingIncrProp() 01232 01233 Author: Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com> 01234 Created: 6/10/99 01235 Returns: the proportional spacing increment between the brush objects 01236 Purpose: As above 01237 01238 ********************************************************************************************/ 01239 01240 double BrushDefinition::GetSpacingIncrProp() 01241 { 01242 return m_BrushSpacingIncrProp; 01243 } 01244 01245 01246 /******************************************************************************************** 01247 01248 > BOOL PathProcessorBrush::SetSpacingIncrConst(MILLIPOINT Incr) 01249 01250 Author: Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com> 01251 Created: 6/10/99 01252 Inputs: the constant spacing increment value to set 01253 Returns: TRUE if we can draw 10 objects, FALSE if the spacing is invalid 01254 Purpose: To set the proportional spacing increment between brush objects 01255 SeeAlso: - 01256 01257 ********************************************************************************************/ 01258 01259 BOOL BrushDefinition::SetSpacingIncrConst(MILLIPOIN