00001 // $Id: rndstack.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 00099 // A attribute stack to be used when rendering Camelot files. 00100 00101 /* 00102 */ 00103 00104 #include "camtypes.h" 00105 00106 //#include "fixmem.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00107 //#include "attrval.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00108 //#include "errors.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00109 00110 //#include "rndrgn.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00111 //#include "rndstack.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00112 00113 /******************************************************************************************** 00114 00115 Technical notes: 00116 00117 Important note: 00118 00119 This class implements a rendering attribute context stack. It is done using a simple 00120 array structure, which grows when required. The stack is implemented using two classes; 00121 RenderStack and AttributeRec. The stack array is held by the RenderStack object, and the 00122 array is an array of AttributeRec objects. 00123 00124 The stack is optimised to avoid saving attributes when it is unnecessary. The way this 00125 works is that a 'context level' is maintained by the RenderStack object. Whenever an 00126 attribute is changed, then the render region that owns this stack pushes the current 00127 attribute onto the stack, along with the current context level. Then when the render region 00128 wants to save the context, all the stack object does is increment the context level. Hence 00129 all future attributes pushed onto the context stack will have a different context level. 00130 When the context is to be restored, the stack pops off all the stack records whose context 00131 level fields match the current context level, and then decrements the context level. 00132 00133 For, example, given the following sequence of calls: 00134 00135 pRegion->SetLineColour(COLOUR_BLACK); 00136 pRegion->SetLineWidth(250); 00137 pRegion->SaveContext(); 00138 pRegion->SetLineColour(COLOUR_RED); 00139 pRegion->SaveContext(); 00140 pRegion->SetLineColour(COLOUR_GREEN); 00141 pRegion->SetLineWidth(0); 00142 pRegion->SetFillColour(COLOUR_CYAN); 00143 00144 then we would end up with a stack like this: 00145 00146 00147 AttributeValue Context Level 00148 00149 +-------------------+---------------+ 00150 | | | <--- Next free element ('Top') 00151 +-------------------+---------------+ 00152 | FillColour: CYAN | 2 | <--- Last element 00153 +-------------------+---------------+ 00154 | LineWidth: 0 | 2 | 00155 +-------------------+---------------+ 00156 | LineColour: GREEN | 2 | 00157 +-------------------+---------------+ 00158 | LineColour: RED | 1 | 00159 +-------------------+---------------+ 00160 | LineWidth: 250 | 0 | 00161 +-------------------+---------------+ 00162 | LineColour: BLACK | 0 | <--- First element 00163 +-------------------+---------------+ 00164 00165 00166 and the current context level would be 2. Therefore when RestoreContext() is next called, 00167 the stack pops off all elements with the context level 2, i.e. the top three elements, and 00168 decrements the context value to 1. Obviously the next time RestoreContext() is called, 00169 then it just pops off one element (red line colour) and decrements the context level to 0. 00170 And this is basically how the stack works - this way, we don't have to save/restore every 00171 attribute whenever we save/restore the context, and so it is much more efficient. 00172 00173 There is one more complication, because the attribute value itself is not pushed onto the 00174 stack, but a pointer to it. The advantage of this is that less data has to be copied 00175 when setting and restoring attributes, and we can encapsulate the setting and restoring 00176 of the attribute within the attribute class itself (see kernel\attr.{h,cpp}). When an 00177 attribute is rendered, the render region asks it to render itself via its virtual 00178 function Render() - this will save the current attribute and install itself as the 00179 new current attribute via the RenderRegion::SvaeAttribute() function. Similarly, when 00180 the attribute record is popped off the stack, the virtual function Restore() is called. 00181 This will set the current attribute to be itself, via the RenderRegion::RestoreAttribute() 00182 function. 00183 At the moment (4/2/94), all our attributes our contained within the RenderRegion, so this 00184 system is not strictly needed, but if we want tools to be able to add types of attributes, 00185 then we must have an extensible system like this. At present, the current attribute array 00186 is of a fixed size, and when we start adding attributes via modules/tools, we will need 00187 to have a system of declaring/registering attributes when the modules/tools start up. 00188 They will then be given an 'ID' for their attribute. This will in fact be an index into 00189 the CurrentAttrs array maintained by RenderRegion, which will be dynamically allocated 00190 when the render region is initialised, according to how many attributes have been 00191 declared/registered. 00192 00193 00194 ********************************************************************************************/ 00195 00196 // InitialStackSize - how many entries can fit in the default stack size. 00197 // Various tests show that the stack very rarely gets deeper than 40 levels, so 30 00198 // is more than enough. 00199 // (Jason here, several years later: We now have more than 30 default attributes 00200 // so the stack always has more than 30 items, and usually sits between 40 and 80 00201 // entries, so is now guaranteed to realloc for every render! As the records are 00202 // only 12 bytes each, I've changed this from 30+20 to 80+30 so that the 00203 // reallocing should happen infrequently (if ever) and we can double the number 00204 // of default attributes before we get into trouble again! However, I've 00205 // kept it relatively small to reduce the overhead of initialising unused entries) 00206 const INT32 InitialStackSize = 80; // Was 30 00207 00208 // StackGranularity - this dictates how many entries should be added to the stack 00209 // when we run out of space. 00210 const INT32 StackGranularity = 30; // Was 20 00211 00212 // ITEM_SIZE - this value is used when allocating memory for the stack. 00213 #define ITEM_SIZE (sizeof(AttributeRec)) 00214 00215 /******************************************************************************************** 00216 00217 > class AttributeRec : public CC_CLASS_MEMDUMP 00218 00219 Author: Tim_Browse (Xara Group Ltd) <camelotdev@xara.com> 00220 Created: 28/01/94 00221 Purpose: Encapsulates an item in the RenderStack class. 00222 SeeAlso: RenderStack 00223 00224 ********************************************************************************************/ 00225 00226 class AttributeRec : public CC_CLASS_MEMDUMP 00227 { 00228 CC_DECLARE_MEMDUMP(AttributeRec) 00229 00230 public: 00231 AttributeRec(); 00232 AttributeValue *pAttrValue; 00233 UINT32 ContextLevel; 00234 BOOL Temporary; 00235 }; 00236 00237 CC_IMPLEMENT_MEMDUMP(AttributeRec, CCObject) 00238 CC_IMPLEMENT_MEMDUMP(RenderStack, CCObject) 00239 00240 /******************************************************************************************** 00241 00242 > AttributeRec::AttributeRec() 00243 00244 Author: Tim_Browse (Xara Group Ltd) <camelotdev@xara.com> 00245 Created: 28/01/94 00246 Purpose: Initialise a stack element in the RenderStack. 00247 00248 ********************************************************************************************/ 00249 00250 AttributeRec::AttributeRec() 00251 { 00252 pAttrValue = NULL; 00253 ContextLevel = 0; 00254 Temporary = FALSE; 00255 } 00256 00257 /******************************************************************************************** 00258 00259 > RenderStack::RenderStack() 00260 00261 Author: Tim_Browse (Xara Group Ltd) <camelotdev@xara.com> 00262 Created: 28/01/94 00263 Purpose: Sets up the empty rendering stack. 00264 00265 ********************************************************************************************/ 00266 00267 RenderStack::RenderStack() 00268 { 00269 // Initialise stack variables 00270 ContextLevel = 0; 00271 StackLimit = 0; 00272 Top = 0; 00273 TheStack = NULL; 00274 } 00275 00276 /******************************************************************************************** 00277 00278 > RenderStack::~RenderStack() 00279 00280 Author: Tim_Browse (Xara Group Ltd) <camelotdev@xara.com> 00281 Created: 28/01/94 00282 Purpose: Frees stack memory, including any temporary objects that belong to the 00283 stack. 00284 00285 ********************************************************************************************/ 00286 00287 RenderStack::~RenderStack() 00288 { 00289 // Delete all temporary items left in the stack 00290 for (UINT32 i = 0; i < Top; i++) 00291 { 00292 if (TheStack[i].Temporary) 00293 { 00294 // Make sure pointer is sane before we delete it. 00295 CC_ASSERT_VALID(TheStack[i].pAttrValue); 00296 delete TheStack[i].pAttrValue; 00297 } 00298 } 00299 00300 // Clean up and free stack objects. 00301 ContextLevel = 0; 00302 StackLimit = 0; 00303 Top = 0; 00304 CCFree(TheStack); 00305 TheStack = NULL; 00306 } 00307 00308 /******************************************************************************************** 00309 00310 > BOOL RenderStack::Copy(const RenderStack *Other, RenderRegion *pRegion) 00311 00312 Author: Tim_Browse (Xara Group Ltd) <camelotdev@xara.com> 00313 Created: 31/01/94 00314 00315 Inputs: Other - pointer to the RenderStack object to copy. 00316 pRegion - The render-region in which this stack lives. Needed so that 00317 all attrs currently on the stack can be properly de-initialised, 00318 and all new attrs being copied over can be properly rendered into 00319 the destination render region. Awful things are likely to happen 00320 if "this" is not equal to "&pRegion->TheStack"! 00321 00322 Returns: TRUE if successful, FALSE otherwise. 00323 00324 Purpose: Copies a RenderStack object. This cannot be done as a copy constructor as 00325 it allocates memory and hence can fail. 00326 This function handles RenderStacks which contain temporary objects, i.e. 00327 objects created especially to put in the stack, as opposed to objects 00328 from a document tree. Such objects are duplicated in the new stack, as 00329 otherwise the destructors of both stacks would try to delete the same 00330 object twice. 00331 00332 Errors: Out of memory. Ensure failure if there is already data in the stack. 00333 00334 ********************************************************************************************/ 00335 00336 BOOL RenderStack::Copy(const RenderStack *Other, RenderRegion *pRegion) 00337 { 00338 ERROR3IF(Other == NULL || pRegion == NULL, "Illegal NULL params"); 00339 00340 // WEBSTER - markn 15/8/97 00341 // This has bee taken out of Webster builds as it can cause fatal access violations with Paper render regions. 00342 // It is new path processor code that not's totally bug-free, and as path processors are not used in Webster, 00343 // it can be safely taken out. 00344 #ifndef WEBSTER 00345 // Make sure all attributes are properly popped && deinitialised, so that any used 00346 // PathProcessors are correctly removed, etc 00347 // (Note: This doesn't deinit the DEFAULT attributes, which we hope will never use 00348 // PathProcessors - this should be OK, because the places where this is called from 00349 // (at present) always make sure all PathProcessors are properly cleaned up too) 00350 CleanUpBeforeDestruct(pRegion); 00351 #endif // WEBSTER 00352 00353 // Make sure we have an empty stack to work with 00354 if (TheStack!=NULL) 00355 { 00356 // Empty the current stack before copying the new one onto it 00357 // For every item in the stack, check to see if it is 'temporary'. If it is, 00358 // we can't use it in our stack - we must take a copy. 00359 for (UINT32 i=0; i<Top; i++) 00360 { 00361 // Get rid of all the temp attrs in the stack 00362 if (TheStack[i].Temporary) 00363 { 00364 // Delete this tempory attr. 00365 CC_ASSERT_VALID(TheStack[i].pAttrValue); 00366 delete TheStack[i].pAttrValue; 00367 } 00368 } 00369 00370 // then get rid of the stack itself 00371 CCFree(TheStack); 00372 TheStack=NULL; 00373 00374 ContextLevel = 0; 00375 StackLimit = 0; 00376 Top = 0; 00377 } 00378 00379 // Sanity check 00380 ENSURE(TheStack == NULL,"RenderStack::Copy called on a non-empty stack"); 00381 00382 #if FALSE 00383 // Jason (7/3/97) 00384 // Copying the stack like this is rampant. We must render all the attributes across onto 00385 // the new stack, so that they are properly stacked and initialised 00386 /* 00387 // Copy stack variables 00388 ContextLevel = Other->ContextLevel; 00389 StackLimit = Other->StackLimit; 00390 Top = Other->Top; 00391 00392 // The other stack may be empty, which will often be the case when merging as it may 00393 // not have started rendering yet. 00394 if (Other->TheStack == NULL) 00395 { 00396 // Just to be on the safe side 00397 TheStack = NULL; 00398 00399 // We're done. 00400 return TRUE; 00401 } 00402 00403 // Allocate the same amount of memory for the stack 00404 TheStack = (AttributeRec *) CCMalloc(StackLimit * ITEM_SIZE); 00405 if (TheStack == NULL) 00406 return FALSE; 00407 00408 // Copy the other render region's stack data into this new stack 00409 memcpy(TheStack, Other->TheStack, StackLimit * ITEM_SIZE); 00410 00411 // For every item in the stack, check to see if it is 'temporary'. If it is, 00412 // we can't use it in our stack - we must take a copy. 00413 for (UINT32 i = 0; i < Top; i++) 00414 { 00415 if (TheStack[i].Temporary) 00416 { 00417 // Get the runtime class info on this object 00418 CCRuntimeClass *pCCRuntimeClass = TheStack[i].pAttrValue->GetRuntimeClass(); 00419 00420 // Create another object of the same type 00421 AttributeValue *pNewAttr = (AttributeValue *) pCCRuntimeClass->CreateObject(); 00422 00423 if (pNewAttr == NULL) 00424 { 00425 // Failed to create object - quit with error, but first ensure that this 00426 // stack is limited to the items copied so far. Otherwise the destructor 00427 // will attempt to delete objects that belong to the other stack. 00428 Top = i; 00429 return FALSE; 00430 } 00431 00432 // Object created ok - get the object to copy its contents across. 00433 pNewAttr->SimpleCopy(TheStack[i].pAttrValue); 00434 00435 // Put it in the stack 00436 TheStack[i].pAttrValue = pNewAttr; 00437 } 00438 } 00439 */ 00440 #else 00441 if (Other->TheStack == NULL) 00442 return(TRUE); 00443 00444 UINT32 LastContextLevel = 0; 00445 00446 // Copy all attributes across to the new stack by rendering them into the provided render region. 00447 // If we aren't "pRegion->TheStack" then we've just lost our paddle, and we're being sucked up the creek... 00448 for (UINT32 i = 0; i < Other->Top; i++) 00449 { 00450 // Make sure we copy context level information across too - if the context level changed, we must 00451 // save the context level now. 00452 if (LastContextLevel != Other->TheStack[i].ContextLevel) 00453 pRegion->SaveContext(); 00454 00455 // Get each attribute to render into our render region 00456 AttributeValue *pAttrVal = Other->TheStack[i].pAttrValue; 00457 00458 // If it's temporary, then replace it with a unique copy for this render region 00459 if (Other->TheStack[i].Temporary) 00460 { 00461 // Get the runtime class info on this object 00462 CCRuntimeClass *pCCRuntimeClass = Other->TheStack[i].pAttrValue->GetRuntimeClass(); 00463 00464 // Create another object of the same type 00465 pAttrVal = (AttributeValue *) pCCRuntimeClass->CreateObject(); 00466 00467 if (pAttrVal == NULL) 00468 return FALSE; 00469 00470 // Object created ok - get the object to copy its contents across. 00471 pAttrVal->SimpleCopy(Other->TheStack[i].pAttrValue); 00472 } 00473 00474 // and render the attribute into this render region 00475 pAttrVal->Render(pRegion, Other->TheStack[i].Temporary); 00476 } 00477 00478 #endif 00479 00480 // It worked 00481 return TRUE; 00482 } 00483 00484 00485 /******************************************************************************************** 00486 00487 > BOOL RenderStack::GrowStack() 00488 00489 Author: Tim_Browse (Xara Group Ltd) <camelotdev@xara.com> 00490 Created: 28/01/94 00491 Returns: TRUE if the stack size was increased, FALSE if not. 00492 Purpose: Increase the size of the stack because a new element needs to be pushed onto 00493 it, and there isn't enough room. 00494 Errors: Out of memory. 00495 00496 ********************************************************************************************/ 00497 00498 BOOL RenderStack::GrowStack() 00499 { 00500 INT32 FirstNewItem = 0; 00501 00502 if (TheStack == NULL) 00503 { 00504 // Try to allocate initial memory block for the stack. 00505 TheStack = (AttributeRec *) CCMalloc(InitialStackSize * ITEM_SIZE); 00506 00507 // Complain and return if it didn't work. 00508 if (TheStack == NULL) 00509 return FALSE; 00510 00511 // Update stack limit to reflect new size 00512 StackLimit = InitialStackSize; 00513 00514 } 00515 else 00516 { 00517 // if (IsUserName("Tim")) 00518 // TRACE( _T("Growing heap from %d to %d entries\n"), 00519 // StackLimit, StackLimit + StackGranularity); 00520 00521 // Increase the stack allocation 00522 AttributeRec *NewStack = (AttributeRec *) 00523 CCRealloc(TheStack, (StackLimit + StackGranularity) * ITEM_SIZE); 00524 00525 // Complain if no more memory 00526 if (NewStack == NULL) 00527 return FALSE; 00528 00529 // Otherwise use this new block 00530 TheStack = NewStack; 00531 00532 // Make sure only new items are initialised 00533 FirstNewItem = StackLimit; 00534 00535 // Update stack limit to reflect new size 00536 StackLimit += StackGranularity; 00537 } 00538 00539 // Initialise the stack elements 00540 for (UINT32 i = FirstNewItem; i < StackLimit; i++) 00541 { 00542 TheStack[i].pAttrValue = NULL; 00543 TheStack[i].ContextLevel = 0; 00544 TheStack[i].Temporary = FALSE; 00545 } 00546 00547 // Return success 00548 return TRUE; 00549 } 00550 00551 00552 /******************************************************************************************** 00553 00554 > BOOL RenderStack::Push(AttributeValue *pAttr, BOOL Temporary = FALSE) 00555 00556 Author: Tim_Browse (Xara Group Ltd) <camelotdev@xara.com> 00557 Created: 28/01/94 00558 Inputs: pAttr - the attribute to push. 00559 Temporary - TRUE if the AttributeValue should be deleted when the 00560 attribute is popped from the stack, FALSE if not. 00561 Returns: TRUE if the push succeeded, FALSE if not. 00562 Purpose: Push an attribute value on to the stack. 00563 The 'Temporary' parameter is used when the attribute has been constructed 00564 especially for changing the attribute - i.e. it is not part of the Camelot 00565 tree. For example, when rendering something that is not a Camelot 00566 document, we still need to change the line colour, so the function that 00567 changes the line colour creates a temporary attribute that is pushed onto 00568 the stack and then automatically deleted when it is removed. 00569 If pAttr is NULL, then it is assumed that this is the first time this 00570 attribute has been set, so we don't need to push the 'old' value. 00571 Errors: Out of memory. 00572 SeeAlso: RenderStack::RestoreContext; RenderStack::SaveContext 00573 00574 ********************************************************************************************/ 00575 00576 BOOL RenderStack::Push(AttributeValue *pAttr, BOOL Temporary) 00577 { 00578 // If we're saving an attribute that hasn't been set up yet, don't bother. 00579 if (pAttr == NULL) 00580 return TRUE; 00581 00582 // If we need more space, then get some. 00583 if (Top == StackLimit) 00584 { 00585 if (!GrowStack()) 00586 return FALSE; 00587 } 00588 00589 // Push the record onto the stack 00590 TheStack[Top].pAttrValue = pAttr; 00591 TheStack[Top].ContextLevel = ContextLevel; 00592 TheStack[Top].Temporary = Temporary; 00593 00594 // Sanity checks 00595 CC_ASSERT_VALID(pAttr); 00596 ENSURE((Temporary == TRUE) || (Temporary == FALSE), 00597 "Duff Temporary flag passed to Push()"); 00598 00599 // Move stack pointer to next record 00600 Top++; 00601 00602 // Worked ok 00603 return TRUE; 00604 } 00605 00606 00607 /******************************************************************************************** 00608 00609 > void RenderStack::RestoreContext(RenderRegion *pRegion) 00610 00611 Author: Tim_Browse (Xara Group Ltd) <camelotdev@xara.com> 00612 Created: 28/01/94 00613 Inputs: pRegion - the render region that the context should be restored to. 00614 Purpose: Restore all attributes saved by the last SaveContext() call. 00615 00616 ********************************************************************************************/ 00617 00618 void RenderStack::RestoreContext(RenderRegion *pRegion) 00619 { 00620 ENSURE(ContextLevel > 0, "Too many calls to RestoreContext!"); 00621 00622 // Restore all attributes that have changed since SaveContext() was last called. 00623 while ((Top > 0) && (TheStack[Top-1].ContextLevel == ContextLevel)) 00624 { 00625 // Move pointer to the item, because the stack is an 'empty' one. 00626 Top--; 00627 00628 // Restore the attribute 00629 TheStack[Top].pAttrValue->Restore(pRegion, TheStack[Top].Temporary); 00630 00631 // Make sure this is a valid pointer. 00632 CC_ASSERT_VALID((TheStack[Top].pAttrValue)); 00633 } 00634 00635 ContextLevel--; 00636 } 00637 00638 00639 00640 /******************************************************************************************** 00641 00642 > void RenderStack::CleanUpBeforeDestruct(RenderRegion *pRegion) 00643 00644 Author: Jason_Williams (Xara Group Ltd) <camelotdev@xara.com> 00645 Created: 14/01/97 00646 Inputs: pRegion - the render region that the context should be restored to. 00647 00648 Purpose: Restore all attributes saved by all SaveContext() calls ever. 00649 This just pops ALL entries from the stack, which gives all remaining 00650 attributes in the stack a chance to clean up and avoid memory leaks. 00651 (Used now to clean up stacked PathProcessors) 00652 00653 ********************************************************************************************/ 00654 00655 void RenderStack::CleanUpBeforeDestruct(RenderRegion *pRegion) 00656 { 00657 ERROR3IF(pRegion == NULL, "Illegal NULL param"); 00658 00659 // Just repeatedly restore until we've popped everything. 00660 // That way if someone changes Restore, they won't break this function! 00661 while (ContextLevel > 0) 00662 RestoreContext(pRegion); 00663 } 00664