00001 // $Id: ppvecstr.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 // ppvecstr - Implemenbtation of rendering PathProcessor classes for Vector Path Stroking 00099 00100 #include "camtypes.h" 00101 00102 #include "ppvecstr.h" 00103 00104 //#include "attrmgr.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00105 //#include "docview.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00106 //#include "document.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00107 //#include "fillattr.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00108 //#include "fixmem.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00109 //#include "grndbmp.h" 00110 #include "nodepath.h" 00111 //#include "paths.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00112 #include "pathstrk.h" 00113 #include "pathtrap.h" 00114 #include "qualattr.h" 00115 //#include "rndrgn.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00116 //#include "spread.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00117 #include "strkattr.h" 00118 #include "valfunc.h" 00119 00120 00121 /*********************************************************************************************** 00122 00123 > class VectorStrokeSubRenderContext : public SubRenderContext 00124 00125 Author: Jason_Williams (Xara Group Ltd) <camelotdev@xara.com> 00126 Created: 6/3/97 00127 Purpose: Used during rendering to store the render context of a repeating vector stroke 00128 if it has taken too long to render so far. 00129 00130 Notes: pOutputTraps will be auto-deleted on destruct. pValFunc will NOT be deleted. 00131 00132 ***********************************************************************************************/ 00133 00134 PORTNOTE("other","Removed VectorStrokeSubRenderContext - derived from SubRenderContext") 00135 #ifndef EXCLUDE_FROM_XARALX 00136 class VectorStrokeSubRenderContext : public SubRenderContext 00137 { 00138 CC_DECLARE_DYNCREATE(VectorStrokeSubRenderContext); 00139 public: 00140 VectorStrokeSubRenderContext(); 00141 ~VectorStrokeSubRenderContext(); 00142 00143 TrapsList *pOutputTraps; // The output trapezoids we've generated for stroking 00144 ValueFunction *pValFunc; // The width function for the stroke 00145 JointType JoinStyle; // The stroke's join style 00146 INT32 LineWidth; // The stroke's line width 00147 INT32 RepeatDist; // The distance for each repeat (or 0 if non-repeating) 00148 UINT32 Index; // The index of the next TrapEdgeList to be stroked (in pOutputTraps) 00149 }; 00150 00151 VectorStrokeSubRenderContext::VectorStrokeSubRenderContext() 00152 { 00153 LineWidth = 5000; 00154 JoinStyle = RoundJoin; 00155 pValFunc = NULL; 00156 RepeatDist = 0; 00157 pOutputTraps = NULL; 00158 Index = 0; 00159 } 00160 00161 VectorStrokeSubRenderContext::~VectorStrokeSubRenderContext() 00162 { 00163 pValFunc = NULL; // Set it to NULL because it's a public var 00164 00165 if (pOutputTraps != NULL) 00166 delete pOutputTraps; 00167 pOutputTraps = NULL; // Set it to NULL because it's a public var 00168 } 00169 00170 CC_IMPLEMENT_DYNCREATE(VectorStrokeSubRenderContext, SubRenderContext); 00171 #endif 00172 00173 CC_IMPLEMENT_DYNAMIC(PathProcessorStrokeVector, PathProcessorStroke); 00174 00175 // Declare smart memory handling in Debug builds 00176 #define new CAM_DEBUG_NEW 00177 00178 00179 00180 00181 /******************************************************************************************** 00182 00183 > virtual BOOL PathProcessorStrokeVector::WillChangeFillAndStrokeSeparately(void) 00184 00185 Author: Jason_Williams (Xara Group Ltd) <camelotdev@xara.com> 00186 Created: 17/2/97 00187 00188 Returns: TRUE 00189 00190 Purpose: Called by the RenderRegion to determine if this PathProcessorStrokeVector affects 00191 the "fill" and "stroke" portions of the path separately. (Generally 00192 speaking, only fill/stroke providers will cause these two different 00193 "bits" of a path to be rendered separately). This call is made BEFORE 00194 this Processor's ProcessPath function will be called. 00195 00196 If the caller gets a TRUE back, the stroke and fill paths will be 00197 rendered separately. 00198 00199 Notes: Base class implementation returns FALSE. Derived Stroke and Fill 00200 processors (such as this one) override this method to return TRUE. 00201 00202 ********************************************************************************************/ 00203 00204 BOOL PathProcessorStrokeVector::WillChangeFillAndStrokeSeparately(void) 00205 { 00206 return(TRUE); 00207 } 00208 00209 00210 00211 /******************************************************************************************** 00212 00213 > virtual BOOL PathProcessorStrokeVector::NeedsTransparency() const 00214 00215 Author: Jason_Williams (Xara Group Ltd) <camelotdev@xara.com> 00216 Created: 4/3/97 00217 00218 Returns: TRUE if this stroke type needs transparency in order to render 00219 00220 Purpose: Determine if this stroke type needs transparency in order to render. 00221 Vector strokes 00222 00223 ********************************************************************************************/ 00224 00225 BOOL PathProcessorStrokeVector::NeedsTransparency() const 00226 { 00227 StrokeDefinition *pStrokeDef = StrokeComponent::FindStroke(StrokeID); 00228 if (pStrokeDef == NULL) 00229 { 00230 return(FALSE); 00231 } 00232 00233 return(pStrokeDef->NeedsTransparency()); 00234 } 00235 00236 00237 00238 00239 /******************************************************************************************** 00240 00241 > virtual void PathProcessorStrokeVector::ProcessPath(Path *pPath, 00242 RenderRegion *pRender, 00243 PathShape ShapePath = PATHSHAPE_PATH); 00244 00245 Author: Jason_Williams (Xara Group Ltd) <camelotdev@xara.com> 00246 Created: 17/2/97 00247 00248 Purpose: Called by the RenderRegion to apply the path processing operation to 00249 the given path. 00250 00251 The PathProcessorStrokeVector class changes the stroking (only) of paths 00252 passed into it. 00253 00254 Notes: * When rendering a path, always pass in your 'this' pointer to 00255 RenderRegion::DrawPath, so that you don't start an infinite 00256 recursion! 00257 00258 * To stop rendering of the path, simply return without calling the RR 00259 00260 * To render this path unchanged, simply call directly back to the RR: 00261 pRender->DrawPath(pPath, this); 00262 00263 * Only affect the fill of this path if pPath->IsFilled 00264 00265 * Only affect the stroke of this path if pPath->IsStroked 00266 00267 * If converting a path into a "filled path" for stroking, the output 00268 path should be rendered with IsStroked=FALSE or it'll get a line 00269 around the outside! 00270 00271 ********************************************************************************************/ 00272 00273 void PathProcessorStrokeVector::ProcessPath(Path *pPath, 00274 RenderRegion *pRender, 00275 PathShape ShapePath) 00276 { 00277 PORTNOTETRACE("other","PathProcessorStrokeVector::ProcessPath - do nothing"); 00278 #ifndef EXCLUDE_FROM_XARALX 00279 ERROR3IF(pPath == NULL || pRender == NULL, "Illegal NULL Params"); 00280 00281 // Get the RenderRegion SubRenderContext and see if we're returning part-way through a background render 00282 VectorStrokeSubRenderContext *pSubRenderContext = (VectorStrokeSubRenderContext *)pRender->GetSubRenderState(); 00283 if (pSubRenderContext != NULL && !IS_A(pSubRenderContext, VectorStrokeSubRenderContext)) 00284 { 00285 // We can't use the sub render context, because it's not ours! 00286 pSubRenderContext = NULL; 00287 } 00288 00289 // --- If we don't have a valid stroke definition, then get the base class to render 00290 // the stroke as a simple flat-filled stroke. 00291 StrokeDefinition *pStrokeDef = StrokeComponent::FindStroke(StrokeID); 00292 if (pStrokeDef == NULL) 00293 { 00294 PathProcessorStroke::ProcessPath(pPath, pRender); 00295 return; 00296 } 00297 00298 // --- See if we have to create a new SubRenderContext, or if we can use the one passed in. 00299 // We always store all relevant variables in a SubRenderContext object so that we can easily 00300 // return control to the RenderRegion without having to copy lots of local values in/out. 00301 const BOOL CreateSubRenderContext = (pSubRenderContext == NULL); 00302 if (CreateSubRenderContext) 00303 { 00304 pSubRenderContext = new VectorStrokeSubRenderContext; 00305 if (pSubRenderContext == NULL) 00306 { 00307 pRender->DrawPath(pPath, this, ShapePath); 00308 return; 00309 } 00310 00311 // --- If the provided path is not stroked, then we'll just pass it straight through 00312 // We also don't touch it if we're doing EOR rendering, or click hit detection 00313 if (!pPath->IsStroked || pRender->DrawingMode != DM_COPYPEN || pRender->IsHitDetect()) 00314 { 00315 delete pSubRenderContext; 00316 pRender->DrawPath(pPath, this, ShapePath); 00317 return; 00318 } 00319 00320 // --- If the quality is set low, or if the current stroke attribute is not our "parent" 00321 // attribute (so we're not the "current" stroker) then strokes are just rendered as centrelines 00322 // BLOCK 00323 { 00324 QualityAttribute *pQuality = (QualityAttribute *) pRender->GetCurrentAttribute(ATTR_QUALITY); 00325 StrokeTypeAttrValue *pTypeAttr = (StrokeTypeAttrValue *) pRender->GetCurrentAttribute(ATTR_STROKETYPE); 00326 00327 if ((pQuality != NULL && pQuality->QualityValue.GetLineQuality() != Quality::FullLine) || 00328 (pTypeAttr != NULL && pTypeAttr != GetParentAttr())) 00329 { 00330 delete pSubRenderContext; 00331 pRender->DrawPath(pPath, this, ShapePath); 00332 return; 00333 } 00334 } 00335 00336 // --- We don't expect the input path to be stroked AND filled on entry 00337 ERROR3IF(pPath->IsFilled, "PathProcessor expected RenderRegion to handle IsFilled case"); 00338 00339 // --- Get the current line width & Join Style from the render region 00340 // BLOCK 00341 { 00342 LineWidthAttribute *pWidthAttr = (LineWidthAttribute *) pRender->GetCurrentAttribute(ATTR_LINEWIDTH); 00343 if (pWidthAttr != NULL) 00344 pSubRenderContext->LineWidth = pWidthAttr->LineWidth; 00345 00346 JoinTypeAttribute *pJoinAttr = (JoinTypeAttribute *) pRender->GetCurrentAttribute(ATTR_JOINTYPE); 00347 if (pJoinAttr != NULL) 00348 pSubRenderContext->JoinStyle = pJoinAttr->JoinType; 00349 } 00350 } 00351 00352 // --- Create a new path to be rendered in place of the provided path 00353 // Note that I use a large allocation size so that reallocation need not be done 00354 // frequently, which also helps reduce memory fragmentation. 00355 Path *pOutput = new Path; 00356 if (pOutput == NULL) 00357 { 00358 if (!pRender->IsSubRenderStateLocked()) 00359 pRender->SetSubRenderState(NULL); 00360 delete pSubRenderContext; 00361 pRender->DrawPath(pPath, this, ShapePath); 00362 return; 00363 } 00364 00365 pOutput->Initialise(128, 128); 00366 00367 00368 // --- Find our Variable Width function 00369 if (CreateSubRenderContext) 00370 { 00371 // --- Get the variable line width descriptor from the render region 00372 VariableWidthAttrValue *pVarWidthAttr = (VariableWidthAttrValue *) pRender->GetCurrentAttribute(ATTR_VARWIDTH); 00373 if (pVarWidthAttr != NULL) 00374 pSubRenderContext->pValFunc = pVarWidthAttr->GetWidthFunction(); 00375 } 00376 00377 ValueFunction *pValFunc = pSubRenderContext->pValFunc; 00378 00379 // If we couldn't find a proper value function, then we'll default to constant-width. 00380 // We keep a static Constant function around always, because it's thread-safe and because 00381 // that saves us the overhead of creating and deleting it on the stack each time we're called 00382 static ValueFunctionConstant Constant(1.0); 00383 if (pValFunc == NULL) 00384 pValFunc = &Constant; 00385 00386 00387 // --- Find our brush clipart tree 00388 Node *pBrush = pStrokeDef->GetStrokeTree(); 00389 ERROR3IF(!IS_A(pBrush, Spread), "Brush does not start with a Spread Node!"); 00390 DocRect BrushBounds = ((Spread *)pBrush)->GetBoundingRect(); 00391 00392 // Get a PathStroker to map paths onto the destination stroke with 00393 PathStrokerVector Stroker(pValFunc, pSubRenderContext->LineWidth, 00394 LineCapButt, &BrushBounds); 00395 00396 // Work out if this is a repeating stroke, and if so, how often it repeats 00397 if (CreateSubRenderContext) 00398 { 00399 if (pStrokeDef->IsRepeating()) 00400 { 00401 // The repeat distance is calculated as a number of millipoints per repeat of the 00402 // stroke, such that it retains the correct aspect ratio. That is, the ratio of 00403 // brush width to height is the same as RepeatDist to LineWidth 00404 pSubRenderContext->RepeatDist = (INT32) (Stroker.GetScaleFactor() * (double)BrushBounds.Width()); 00405 if (pSubRenderContext->RepeatDist < 1000) // I absolutely refuse to repeat it more than 00406 pSubRenderContext->RepeatDist = 1000; // once every 1pt, as this is plenty small enough 00407 00408 // Suss the path length out 00409 ProcessLength GenerateLength(100); 00410 double Length = 0; 00411 BOOL ok = GenerateLength.PathLength(pPath, &Length); 00412 00413 // Ask the stroke def for its number of brush repeats - 0 means work it out 00414 INT32 NumberOfRepeats = pStrokeDef->NumRepeats(); 00415 if(NumberOfRepeats == 0 && pSubRenderContext->RepeatDist > 0) 00416 { 00417 // Work out the optimal number of repeats along the path 00418 NumberOfRepeats = (INT32)(floor((Length/pSubRenderContext->RepeatDist) + 0.5)); 00419 } 00420 00421 // Don't got dividing by zero now... 00422 if(NumberOfRepeats <= 0) 00423 NumberOfRepeats = 1; 00424 00425 // Alter the repeat distance to accomodate this number of repeats 00426 pSubRenderContext->RepeatDist = (INT32)(Length / (double)NumberOfRepeats); 00427 } 00428 00429 // Generate the set of trapezoids for the dest path 00430 ProcessPathToTrapezoids GenerateTraps(100); 00431 pSubRenderContext->pOutputTraps = new TrapsList(pSubRenderContext->RepeatDist); 00432 00433 BOOL Failed = (pSubRenderContext->pOutputTraps == NULL); 00434 if (!Failed && !GenerateTraps.Init(pPath, pSubRenderContext->pOutputTraps)) 00435 Failed = TRUE; 00436 00437 ProcessFlags PFlags(TRUE, FALSE, FALSE); // Flags are: Flatten, !QuantiseLines, !QuantiseAll 00438 if (!Failed && !GenerateTraps.Process(PFlags, TrapTravel_Parametric, pSubRenderContext->JoinStyle)) 00439 Failed = TRUE; 00440 00441 if (Failed) 00442 { 00443 pRender->DrawPath(pPath, this, ShapePath); 00444 if (!pRender->IsSubRenderStateLocked()) 00445 pRender->SetSubRenderState(NULL); 00446 delete pSubRenderContext; 00447 return; 00448 } 00449 } 00450 00451 ERROR3IF(pSubRenderContext->pOutputTraps == NULL || pValFunc == NULL, 00452 "Vector stroke SubRenderContext was not properly uninitialised!"); 00453 00454 // --- Handle background rendering. We always store all critical information in a SubRenderContext. 00455 // If we have to break into rendering because of background rendering, we give the context to the RndRgn 00456 // to keep for next time. However, when we finish rendering everything, we need to clean up - we will 00457 // assume that we'll make it to the end, and chnage this variable if we get interrupted. 00458 BOOL DeleteSubRenderContext = TRUE; 00459 00460 // Lock the sub-render context so that nobody "inside" the brush uses it 00461 const BOOL SRContextLocked = pRender->IsSubRenderStateLocked(); 00462 if (!SRContextLocked) 00463 pRender->LockSubRenderState(TRUE); 00464 00465 // --- Now "render" the brush clipart tree via our Stroker 00466 for ( ; 00467 pSubRenderContext->Index < pSubRenderContext->pOutputTraps->GetNumTraps() && DeleteSubRenderContext; 00468 pSubRenderContext->Index++) 00469 { 00470 // Find the trapezoid edge list for this pass 00471 TrapEdgeList *pEdgeList = pSubRenderContext->pOutputTraps->GetTrapEdgeList(pSubRenderContext->Index); 00472 if (pEdgeList->GetNumEdges() < 2) 00473 { 00474 ERROR3("No map traps when stroking! Subpath stripped\n"); 00475 continue; 00476 } 00477 00478 // And render away 00479 pRender->SaveContext(); 00480 00481 Node* pNode = pBrush->FindFirstForUnclippedInkRender(pRender); 00482 while (pNode) 00483 { 00484 // Prepare the stroker to stroke this sub-stroke 00485 Stroker.PrepareToStroke(pEdgeList); 00486 00487 if (pNode->IsAnAttribute()) 00488 { 00489 // If we are overriding the fill/transparency with the one applied to the stroke, 00490 // then we simply throw away all fill/transparency attributes as we render 00491 // (We do this on the fly rather than as we make the brush so that the user can 00492 // toggle this mode on and off at any time if they change their mind) 00493 BOOL RenderIt = TRUE; 00494 if ( (pStrokeDef->OverrideFill() && ((NodeAttribute *)pNode)->IsAColourFill()) || 00495 (pStrokeDef->OverrideFill() && ((NodeAttribute *)pNode)->IsAStrokeColour()) || 00496 (pStrokeDef->OverrideTrans() && ((NodeAttribute *)pNode)->IsAStrokeTransp()) || 00497 (pStrokeDef->OverrideTrans() && ((NodeAttribute *)pNode)->IsATranspFill()) ) 00498 { 00499 RenderIt = FALSE; 00500 } 00501 00502 if (RenderIt) 00503 { 00504 // We must modify all attributes to lie in the destination stroke. 00505 // This includes fill/trans geometry endpoints, line widths, and 00506 // also possibly modifying transparency levels to allow a flat transparency 00507 // to be applied to the whole stroke. 00508 AttributeValue *pAttrValue = ((NodeAttribute *)pNode)->GetAttributeValue(); 00509 AttributeValue *pNewValue = pAttrValue->MouldIntoStroke(&Stroker, 1.0); 00510 //****!!!!TODO - Just above, we have the chance to handle transparency better 00511 // - we could at least scale all transparencies by passing a flat scale factor into 00512 // the MouldIntoStroke call. 00513 00514 if (pNewValue != NULL) 00515 pNewValue->Render(pRender, TRUE); // The RndRgn will delete this when it's done with 00516 else 00517 pNode->Render(pRender); // No change, so render the original attribute 00518 } 00519 } 00520 else if (pNode->IsNodePath()) 00521 { 00522 // Stroke the trapezoids into the output path 00523 pOutput->ClearPath(); 00524 if (!Stroker.StrokePath(&((NodePath *)pNode)->InkPath, pOutput)) 00525 break; 00526 00527 pRender->SetWindingRule(NonZeroWinding); 00528 pRender->DrawPath(pOutput, this, ShapePath); 00529 } 00530 // else 00531 // TRACEUSER( "Jason", _T("\nBrush node %s not rendered\n"), pNode->GetRuntimeClass()->m_lpszClassName); 00532 00533 pNode = pNode->FindNextForUnclippedInkRender(pRender); 00534 } 00535 00536 pRender->RestoreContext(); 00537 00538 // --- Now check if we should break into rendering for background rendering purposes 00539 // If the Sub-render-context is locked, then we're inside a blend or something, and it's too dangerous 00540 // for us to store our sub-render state, so we have no choice but to render until we finish. 00541 // BLOCK 00542 if (!SRContextLocked && IS_A(pRender, GRenderDIB)) 00543 { 00544 View *pView = pRender->GetRenderView(); 00545 if (pView != NULL && !pRender->RenderTreeCanContinue()) 00546 { 00547 // We have been interrupted by the background redraw system. 00548 // We will immediately exit, storing our SubRenderContext away into the 00549 // RenderRegion for the next time it calls us (see below). 00550 DeleteSubRenderContext = FALSE; 00551 00552 // Drop through and let the loop condition handle exit 00553 } 00554 } 00555 } 00556 00557 // If we locked the sub-render context, then we must restore it 00558 if (!SRContextLocked) 00559 pRender->LockSubRenderState(FALSE); 00560 00561 // If we have finished rendering everything, then we vape our sub render context. 00562 // (We even check if we were interrupted just as we finished the final iteration) 00563 if (DeleteSubRenderContext || pSubRenderContext->Index >= pSubRenderContext->pOutputTraps->GetNumTraps()) 00564 { 00565 if (!SRContextLocked) 00566 pRender->SetSubRenderState(NULL); 00567 delete pSubRenderContext; 00568 pSubRenderContext = NULL; 00569 } 00570 else 00571 { 00572 if (!SRContextLocked) 00573 pRender->SetSubRenderState(pSubRenderContext); 00574 } 00575 00576 delete pOutput; 00577 pOutput = NULL; 00578 #endif 00579 } 00580 00581 00582 00583 /******************************************************************************************** 00584 00585 > virtual BOOL PathProcessorStrokeVector::IsDifferent(PathProcessorStroke *pOther); 00586 00587 Author: Jason_Williams (Xara Group Ltd) <camelotdev@xara.com> 00588 Created: 17/2/97 00589 00590 Inputs: pOther - The other PathProcessorStroke 00591 00592 Returns: TRUE if they're considered different, FALSE if they are equivalent 00593 00594 Purpose: Equality operator 00595 00596 Notes: The base class implementation compares the runtime classes of the 00597 2 objects to see if they are different classes. If they are the same 00598 class, it assumes they're considered equal - derived classes should 00599 override this behaviour if further comparisons are necessary. 00600 00601 ********************************************************************************************/ 00602 00603 BOOL PathProcessorStrokeVector::IsDifferent(PathProcessorStroke *pOther) 00604 { 00605 ERROR3IF(pOther == NULL, "Illegal NULL param"); 00606 00607 if (GetRuntimeClass() != pOther->GetRuntimeClass()) 00608 return(TRUE); 00609 00610 // We're different if we use different stroke definitions 00611 return(StrokeID != ((PathProcessorStrokeVector *)pOther)->StrokeID); 00612 } 00613 00614 00615 00616 /******************************************************************************************** 00617 00618 > virtual PathProcessorStroke *PathProcessorStrokeVector::Clone(void) 00619 00620 Author: Jason_Williams (Xara Group Ltd) <camelotdev@xara.com> 00621 Created: 17/2/97 00622 00623 Returns: NULL if we're out of memory, else 00624 a pointer to a clone (exact copy) of this object 00625 00626 Purpose: To copy PathProcessorStroke or derived-class object 00627 00628 ********************************************************************************************/ 00629 00630 PathProcessorStroke *PathProcessorStrokeVector::Clone(void) 00631 { 00632 // Clone this object - this can be done by just creating a new one 00633 PathProcessorStrokeVector *pClone = new PathProcessorStrokeVector; 00634 00635 // And copy the (base class) parent-attribute pointer so we know when our 00636 // parent attribute is "current" in the render region. 00637 if (pClone != NULL) 00638 { 00639 pClone->SetParentAttr(GetParentAttr()); 00640 pClone->StrokeID = StrokeID; 00641 } 00642 00643 return(pClone); 00644 } 00645 00646 00647 00648 /******************************************************************************************** 00649 00650 > void PathProcessorStrokeVector::SetStrokeDefinition(StrokeHandle Handle) 00651 00652 Author: Jason_Williams (Xara Group Ltd) <camelotdev@xara.com> 00653 Created: 29/2/97 00654 00655 Purpose: Sets this path processor up to use the given vector stroke definition 00656 in all future rendering. 00657 00658 Notes: If not set, or if set to StrokeHandle_NoStroke, the stroke processor 00659 will render its strokes as simple (flat filled) strokes by calling the 00660 base class ProcessPath method. 00661 00662 ********************************************************************************************/ 00663 00664 void PathProcessorStrokeVector::SetStrokeDefinition(StrokeHandle Handle) 00665 { 00666 ERROR3IF(Handle != StrokeHandle_NoStroke && StrokeComponent::FindStroke == NULL, 00667 "Trying to set a Stroke PathProcessor to use a deleted stroke!"); 00668 00669 StrokeID = Handle; 00670 } 00671 00672