ppstroke.cpp

Go to the documentation of this file.
00001 // $Id: ppstroke.cpp 1502 2006-07-24 11:51:13Z 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 // pathpcs - Definition of rendering PathProcessor class
00099 
00100 #include "camtypes.h"
00101 
00102 #include "ppstroke.h"
00103 
00104 //#include "attrmgr.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00105 //#include "fillval.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00106 //#include "paths.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00107 #include "pathstrk.h"
00108 #include "pathtrap.h"
00109 #include "qualattr.h"
00110 //#include "rndrgn.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00111 #include "strkattr.h"
00112 #include "valfunc.h"
00113 #include "nodetxtl.h" // for the format region
00114 //#include "becomea.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00115 #include "attrmap.h"
00116 #include "lineattr.h"
00117 //#include "fillattr.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00118 #include "brshattr.h"
00119 #include "ai_epsrr.h"   // for definition of AIEPSRenderRegion. Only used in ProcessStroke()
00120 
00121 #include "ophist.h"
00122 #include "combshps.h"
00123 
00124 CC_IMPLEMENT_DYNAMIC(PathProcessorStroke, PathProcessor);
00125 
00126 // Declare smart memory handling in Debug builds
00127 #define new CAM_DEBUG_NEW
00128 
00129 
00130 
00131 /********************************************************************************************
00132 
00133 >   PathProcessorStroke::PathProcessorStroke()
00134 
00135     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
00136     Created:    31/12/96
00137 
00138     Purpose:    Constructor
00139 
00140 ********************************************************************************************/
00141 
00142 PathProcessorStroke::PathProcessorStroke()
00143 {
00144 }
00145 
00146 
00147 
00148 /********************************************************************************************
00149 
00150 >   virtual BOOL PathProcessorStroke::WillChangeFillAndStrokeSeparately(void)
00151 
00152     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
00153     Created:    31/12/96
00154 
00155     Returns:    TRUE
00156 
00157     Purpose:    Called by the RenderRegion to determine if this PathProcessorStroke affects
00158                 the "fill" and "stroke" portions of the path separately. (Generally
00159                 speaking, only fill/stroke providers will cause these two different
00160                 "bits" of a path to be rendered separately). This call is made BEFORE
00161                 this Processor's ProcessPath function will be called.
00162 
00163                 If the caller gets a TRUE back, the stroke and fill paths will be
00164                 rendered separately.
00165 
00166     Notes:      Base class implementation returns FALSE. Derived Stroke and Fill
00167                 processors (such as this one) override this method to return TRUE.
00168 
00169 ********************************************************************************************/
00170 
00171 BOOL PathProcessorStroke::WillChangeFillAndStrokeSeparately(void)
00172 {
00173     return(TRUE);
00174 }
00175 
00176 
00177 
00178 /********************************************************************************************
00179 
00180 >   virtual void PathProcessorStroke::ProcessPath(Path *pPath,
00181                                                   RenderRegion *pRender,
00182                                                   PathShape ShapePath = PATHSHAPE_PATH);
00183 
00184     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
00185     Created:    31/12/96
00186 
00187     Purpose:    Called by the RenderRegion to apply the path processing operation to 
00188                 the given path.
00189 
00190                 The PathProcessorStroke class changes the stroking (only) of paths
00191                 passed into it.
00192 
00193     Notes:      * When rendering a path, always pass in your 'this' pointer to
00194                   RenderRegion::DrawPath, so that you don't start an infinite
00195                   recursion!
00196 
00197                 * To stop rendering of the path, simply return without calling the RR
00198 
00199                 * To render this path unchanged, simply call directly back to the RR:
00200                     pRender->DrawPath(pPath, this);
00201                 
00202                 * Only affect the fill of this path if pPath->IsFilled
00203 
00204                 * Only affect the stroke of this path if pPath->IsStroked
00205 
00206                 * If converting a path into a "filled path" for stroking, the output
00207                   path should be rendered with IsStroked=FALSE or it'll get a line
00208                   around the outside!               
00209 
00210 ********************************************************************************************/
00211 
00212 void PathProcessorStroke::ProcessPath(Path *pPath,
00213                                       RenderRegion *pRender,
00214                                       PathShape ShapePath)
00215 {
00216     TRACEUSER( "Diccon", _T("Rendering stroke\n"));
00217     ERROR3IF(pPath == NULL || pRender == NULL, "Illegal NULL Params");
00218 
00219     // --- If the provided path is not stroked, then we'll just pass it straight through
00220     // We also don't touch it if we're doing EOR rendering
00221     if (!pPath->IsStroked || pRender->DrawingMode != DM_COPYPEN)
00222     {
00223         pRender->DrawPath(pPath, this, ShapePath);
00224         return;
00225     }
00226 
00227     // --- If the quality is set low, strokes are just rendered as centrelines
00228     // BLOCK
00229     {
00230         QualityAttribute *pQuality = (QualityAttribute *) pRender->GetCurrentAttribute(ATTR_QUALITY);
00231         if (pQuality != NULL && pQuality->QualityValue.GetLineQuality() != Quality::FullLine)
00232         {
00233             pRender->DrawPath(pPath, this, ShapePath);
00234             return;
00235         }
00236     }
00237 
00238     // --- If the attribute which created us is not the current StrokeType attribute, then
00239     // we have been overridden by a different stroke type, so we do nothing.
00240     // BLOCK
00241     {
00242         StrokeTypeAttrValue *pTypeAttr = (StrokeTypeAttrValue *) pRender->GetCurrentAttribute(ATTR_STROKETYPE);
00243         if (pTypeAttr != NULL && pTypeAttr != GetParentAttr())
00244         {
00245             pRender->DrawPath(pPath, this);
00246             return;
00247         }
00248     }
00249 
00250     // --- We don't expect the input path to be stroked AND filled on entry
00251     ERROR3IF(pPath->IsFilled, "PathProcessor expected RenderRegion to handle IsFilled case");
00252 
00253     // --- Get the current line width, cap style, and join style from the render region
00254     // In case of failure, we initialise with suitable defaults
00255     INT32 LineWidth = 5000;
00256 
00257     // BLOCK
00258     {
00259         LineWidthAttribute *pWidthAttr = (LineWidthAttribute *) pRender->GetCurrentAttribute(ATTR_LINEWIDTH);
00260         if (pWidthAttr != NULL)
00261             LineWidth = pWidthAttr->LineWidth;
00262     }
00263 
00264     // --- Create a stroke outline by calling our helper function
00265 
00266     // (ChrisG 4/11/00) - Use smoothed paths with AIExport. This is only done for the export, as the smooth
00267     //  path creation function is about five times slower than the non-smoothed one.
00268     Path * pOutput = NULL;
00269     if (pRender->IsKindOf (CC_RUNTIME_CLASS (AIEPSRenderRegion)))
00270     {
00271         pOutput = CreateSmoothVarWidthStroke(pPath, pRender, LineWidth);
00272     }
00273     else
00274     {
00275         pOutput = CreateVarWidthStroke(pPath, pRender, LineWidth);
00276     }
00277     
00278     if (pOutput == NULL)
00279     {
00280         pRender->DrawPath(pPath, this);
00281         return;
00282     }
00283 
00284     // --- Finally, render the new stroke outline path in place of the one we were given
00285     pRender->SaveContext();
00286 
00287         // !!!!****ToDo - for now, strokes always render flat-filled with the stroke colour
00288         StrokeColourAttribute *pStrokeColour = (StrokeColourAttribute *) pRender->GetCurrentAttribute(ATTR_STROKECOLOUR);
00289         if (pStrokeColour != NULL)
00290             pRender->SetFillColour(pStrokeColour->Colour);
00291 
00292         pRender->SetWindingRule(NonZeroWinding);
00293         pRender->DrawPath(pOutput, this);
00294 
00295     pRender->RestoreContext();
00296 
00297     // --- And clean up
00298     delete pOutput;
00299 }
00300 
00301 /********************************************************************************************
00302 
00303 >   BOOL PathProcessorStroke::DoBecomeA(BecomeA* pBecomeA, Path* pPath, Node* pParent)
00304 
00305     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
00306     Created:    17/3/2000
00307     Inputs:     pBecomeA - the object that tells us what to become, and recieves the results
00308                 pPath - the path that we are processing
00309                 pParent- the node that this brush is applied to
00310     Outputs:    
00311     Returns:    TRUE if everything went ok,
00312     Purpose:    To return the processed path to a BecomeA object.  Does pretty much the same
00313                 as process path but withot rendering anything.
00314         
00315     Note:       Because of the problems with bevelling and contouring this function will never
00316                 be called as part of the normal DoBecomeA procedure.  You have to specifically
00317                 locate the AttrStrokeType and call its DoBecomeA directly if you wish to 
00318                 extract the paths from a stroke.  This should be resolved somehow after
00319                 the release.
00320 
00321                 25/10/2000 Extra note:  We are now working out a mechanism which uses
00322                 GetProcessedPath and GetOutlinePath, so this function may become obsolete
00323 ********************************************************************************************/
00324 
00325 BOOL PathProcessorStroke::DoBecomeA(BecomeA* pBecomeA, Path* pPath, Node* pParent)
00326 {
00327     ERROR2IF(pBecomeA == NULL, FALSE, "BecomeA pointer is NULL in PathProcessorStroke::DoBecomeA");
00328     ERROR2IF(pParent == NULL, FALSE, "Parent is NULL in PathProcessorStroke::DoBecomeA");
00329     ERROR2IF(pPath == NULL, FALSE, "Path is NULL in PathProcessorStroke::DoBecomeA");
00330     
00331 
00332     // we need to make a new nodepath
00333     NodePath* pNewNode = GetSmoothProcessedPath(pPath, pParent);
00334 
00335     if (pNewNode == FALSE)
00336         return FALSE;
00337 
00338     BOOL Success = FALSE;
00339     // now what we do depends on what type of BecomeA we've got
00340     if (pBecomeA->BAPath())
00341     {
00342         switch (pBecomeA->GetReason())
00343         {
00344             case BECOMEA_REPLACE:
00345             {
00346                 UndoableOperation* pUndoOp = pBecomeA->GetUndoOp();
00347                 ERROR2IF(pUndoOp == NULL, FALSE, "No op pointer in PathProcessorStroke::DoBecomeA");
00348                 //  apply attributes to our new node
00349                 // Get the attributes
00350                 CCAttrMap AttribMap(30);
00351                 if (!((NodeRenderableInk*)pParent)->FindAppliedAttributes(&AttribMap))
00352                     break;
00353                 
00354                 // another bodge: this time if we have a stroked path that is not closed and 
00355                 // we have given it a stroke colour then we want this stroke colour to become
00356                 // the fill colour of the new shape
00357                 AttrStrokeColour * pColour = NULL;
00358             //  if (!pParent->IsNodePath() && ((NodePath*)pParent)->InkPath.IsClosed())
00359                 {
00360                     // look up the stroke colour attribute
00361                     
00362                     //AttribMap.Lookup((void *)CC_RUNTIME_CLASS(AttrStrokeColour),
00363                     //  (void *&)pColour);
00364                     ((NodeRenderableInk*)pParent)->FindAppliedAttribute(CC_RUNTIME_CLASS(AttrStrokeColour), (NodeAttribute**)&pColour);
00365                     // make a new flat fill attribute and apply this to the node
00366                     if (pColour)
00367                     {
00368                         StrokeColourAttribute * pAttrVal = (StrokeColourAttribute *)pColour->GetAttributeValue();
00369 
00370                         if (pAttrVal)
00371                         {
00372                             AttrFlatColourFill *pFill = NULL;
00373                             // create and insert at first child
00374                             ALLOC_WITH_FAIL(pFill, new AttrFlatColourFill(pNewNode, FIRSTCHILD), pUndoOp);
00375 
00376                             pFill->SetStartColour(&(pAttrVal->Colour));
00377                             // remove from the map so we don't duplicate
00378                             AttribMap.RemoveKey(pFill->GetAttributeType());
00379                         }
00380                     }
00381 
00382                 }
00383 
00384                 if (!pNewNode->ApplyAttributes(&AttribMap))
00385                     break;
00386             
00387                 // insert the new node and delete the original
00388                 BOOL ok = TRUE;
00389                 // If we don't own the parent node, we should insert after (on top of) it
00390                 if (pBecomeA->IsSecondary())
00391                     ok = pUndoOp->DoInsertNewNode(pNewNode,pParent,NEXT,FALSE,FALSE,FALSE,FALSE);
00392                 else
00393                 {
00394                     ok = pUndoOp->DoInsertNewNode(pNewNode,pParent,PREV,FALSE,FALSE,FALSE,FALSE);
00395                     // If this BecomeA is a primary, then delete the source node
00396                     if (ok)
00397                         ok = pUndoOp->DoHideNode((NodeRenderableInk*)pParent, TRUE);
00398                 }
00399 
00400                 if (!ok)
00401                     break;
00402 
00403 
00404                 if (!pBecomeA->IsCombineBecomeA())
00405                 {
00406                     // pass it back then
00407                     pBecomeA->PassBack(pNewNode,(NodeRenderableInk*)pParent);
00408                 }
00409                 else
00410                 {
00411                     CombineBecomeA* pPassback = (CombineBecomeA*) pBecomeA;
00412                 
00413                     pPassback->SetStrokeCreatedPassbackPath(TRUE);
00414                     pBecomeA->PassBack(pNewNode,(NodeRenderableInk*)pParent);
00415                     pPassback->SetStrokeCreatedPassbackPath(FALSE);
00416                 }
00417                 Success = TRUE;
00418             }
00419             break;
00420 
00421             case BECOMEA_PASSBACK :
00422             {
00423                 if (!pBecomeA->IsCombineBecomeA ())
00424                 {
00425                     // pass it back then
00426                     Success = pBecomeA->PassBack(pNewNode,(NodeRenderableInk*)pParent);
00427                 }
00428                 else
00429                 {
00430                     CombineBecomeA* pPassback = (CombineBecomeA*) pBecomeA;
00431 
00432                     pPassback->SetStrokeCreatedPassbackPath(TRUE);
00433                     Success = pBecomeA->PassBack(pNewNode,(NodeRenderableInk*)pParent);
00434                     pPassback->SetStrokeCreatedPassbackPath(FALSE);
00435                 }
00436             }
00437             break;
00438 
00439             default:
00440                 break;
00441         }
00442     }
00443     
00444     
00445     if (!Success)
00446         delete pNewNode;
00447 
00448     return Success;
00449 }
00450 
00451 
00452 /********************************************************************************************
00453 
00454 >   NodePath* PathProcessorStroke::GetProcessedPath(Path* pPath, Node* pParent, BOOL OutlineOnly = FALSE)
00455 
00456     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
00457     Created:    17/3/2000
00458     Inputs:     pPath - the path that we are processing
00459                 pParent- the node that this brush is applied to
00460     Outputs:    
00461     Returns:    A NodePath, containing the path generated by our path stroker
00462     Purpose:    To return the stroked path
00463         
00464     Notes:  
00465 ********************************************************************************************/
00466 
00467 NodePath* PathProcessorStroke::GetProcessedPath(Path* pPath, Node* pParent)
00468 {
00469     // we need to fake a renderregion as our helper functions need one,
00470     // luckily FormatRegion is on hand from the text stuff
00471     FormatRegion FakeRender;
00472 
00473     if (!FakeRender.Init((NodeRenderableInk*)pParent)) // init renders all applied attributes into the region
00474         return NULL;
00475 
00476     
00477     // From here copied from ProcessPath:
00478     // --- Get the current line width, cap style, and join style from the render region
00479     // In case of failure, we initialise with suitable defaults
00480     INT32 LineWidth = 5000;
00481 
00482     // BLOCK
00483     {
00484         LineWidthAttribute *pWidthAttr = (LineWidthAttribute *) FakeRender.GetCurrentAttribute(ATTR_LINEWIDTH);
00485         if (pWidthAttr != NULL)
00486             LineWidth = pWidthAttr->LineWidth;
00487     }
00488 
00489     // --- Create a stroke outline by calling our helper function
00490     Path *pOutput = CreateVarWidthStroke(pPath, &FakeRender, LineWidth);
00491     if (pOutput == NULL)
00492         return FALSE;
00493 
00494     if (!pOutput->IsClosed())
00495         pOutput->TryToClose();
00496 
00497     // we need to make a new nodepath
00498     NodePath* pNewNode = new NodePath; 
00499         
00500     if (pNewNode == NULL)
00501     {
00502         delete pOutput;
00503         return FALSE;
00504     }
00505     
00506     // initialise the path
00507     if (!pNewNode->InkPath.Initialise(pOutput->GetNumCoords(), 128))
00508     {
00509         delete pOutput;
00510         delete pNewNode;
00511         return FALSE;
00512     }
00513 
00514     // Copy our path data into it
00515     //CALL_WITH_FAIL(!pNewNode->InkPath.CopyPathDataFrom(pOutput), pUndoOp, Success)
00516     if (!pNewNode->InkPath.CopyPathDataFrom(pOutput))
00517     {
00518         delete pOutput;
00519         delete pNewNode;
00520         return FALSE;
00521     }
00522     // don't need that path anymore
00523     delete pOutput;
00524     pOutput = NULL;
00525 
00526     return pNewNode;
00527 }
00528 
00529 
00530 /********************************************************************************************
00531 
00532 >   virtual BOOL PathProcessorStroke::IsDifferent(PathProcessorStroke *pOther);
00533 
00534     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
00535     Created:    8/1/97
00536 
00537     Inputs:     pOther - The other PathProcessorStroke
00538 
00539     Returns:    TRUE if they're considered different, FALSE if they are equivalent
00540 
00541     Purpose:    Equality operator
00542 
00543     Notes:      The base class implementation compares the runtime classes of the
00544                 2 objects to see if they are different classes. If they are the same
00545                 class, it assumes they're cnsidered equal - derived classes should
00546                 override this behaviour if further comparisons are necessary.
00547 
00548 ********************************************************************************************/
00549 
00550 BOOL PathProcessorStroke::IsDifferent(PathProcessorStroke *pOther)
00551 {
00552     ERROR3IF(pOther == NULL, "Illegal NULL param");
00553 
00554     if (GetRuntimeClass() != pOther->GetRuntimeClass())
00555         return(TRUE);
00556 
00557     // Assume that we're the same, as the base class is very simple
00558     return(FALSE);
00559 }
00560 
00561 
00562 
00563 /********************************************************************************************
00564 
00565 >   virtual PathProcessorStroke *PathProcessorStroke::Clone(void)
00566 
00567     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
00568     Created:    8/1/97
00569 
00570     Returns:    NULL if we're out of memory, else
00571                 a pointer to a clone (exact copy) of this object
00572 
00573     Purpose:    To copy PathProcessorStroke or derived-class object
00574 
00575 ********************************************************************************************/
00576 
00577 PathProcessorStroke *PathProcessorStroke::Clone(void)
00578 {
00579     // Clone this object - this can be done by just creating a new one
00580     PathProcessorStroke *pClone = new PathProcessorStroke;
00581 
00582     // And copy the (base class) parent-attribute pointer so we know when our
00583     // parent attribute is "current" in the render region.
00584     if (pClone != NULL)
00585         pClone->SetParentAttr(GetParentAttr());
00586 
00587     return(pClone);
00588 }
00589 
00590 
00591 /********************************************************************************************
00592 
00593 >   virtual BOOL PathProcessorStroke::DoesActuallyDoAnything(RenderRegion* pRender)
00594 
00595     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
00596     Created:    29/9/2000
00597     Inputs:     pRender - the render region we are about to render into.  Note that it is 
00598                 important that the region has had all its attributes rendered into it
00599     Returns:    TRUE if this PPS is going to change the path, FALSE if it will not.
00600                 
00601     Purpose:    Allows the render region to know whether this PPS will do anything, if not then
00602                 it can DrawPath normally allowing Dash patterns to be applied to nodes with
00603                 constant VarWidth attributes applied to them
00604 
00605 ********************************************************************************************/
00606 
00607 BOOL PathProcessorStroke::DoesActuallyDoAnything(RenderRegion* pRender)
00608 {
00609     ERROR2IF(pRender == NULL, FALSE, "RenderRegion is NULL in PathProcessorStroke::DoesActuallyDoAnything");
00610 
00611     if (IsDisabled())
00612         return FALSE;
00613 
00614     // Get the value function from the render region and have a look at it
00615     ValueFunction *pValFunc = NULL;
00616 
00617     VariableWidthAttrValue *pVarWidthAttr = (VariableWidthAttrValue *) pRender->GetCurrentAttribute(ATTR_VARWIDTH);
00618     if (pVarWidthAttr != NULL)
00619         pValFunc = pVarWidthAttr->GetWidthFunction();
00620 
00621     // If it is a constant width stroke, then we don't really do anything
00622     if (pValFunc == NULL || IS_A(pValFunc, ValueFunctionConstant))
00623         return FALSE;
00624 
00625     // likewise if we have a brush don't do anything either, because we will use the stroke to 
00626     // generate a pressure curve for the brush
00627     BrushAttrValue* pBrush  = (BrushAttrValue*) pRender->GetCurrentAttribute(ATTR_BRUSHTYPE);
00628     if (pBrush != NULL && pBrush->GetBrushHandle() != BrushHandle_NoBrush)
00629         return FALSE;
00630 
00631     return TRUE;
00632 }
00633 
00634 /********************************************************************************************
00635 
00636 >   TrapsList *PathProcessorStroke::PrepareTrapsForStroke(Path *pPath, RenderRegion *pRender,
00637                                                             INT32 LineWidth)
00638 
00639     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
00640     Created:    31/12/96 (split into new function, 6/2/97)
00641 
00642     Inputs:     pPath       - The source path to be stroked
00643                 pRender     - The render region it will be drawn to
00644                 LineWidth   - The maximum width of all strokes you intend to render with the
00645                               resulting TrapsList. Normally this is the current line width from
00646                               the output RenderRegion.
00647 
00648     Returns:    NULL if it failed, else
00649                 a pointer to a new TrapsList object (which the caller must delete).
00650 
00651     Purpose:    Precalculation function to be used in tandem with CreateVarWidthStroke().
00652                 If you want to create several strokes along the same path, then precalculating
00653                 the trapezoids will give a large performance improvement.
00654 
00655                 Call this with the width of the largest stroke you intend to render, and
00656                 then render all strokes using the provided trapezoids.
00657 
00658     SeeAlso:    PathProcessorStroke::CreateVarWidthStroke
00659         
00660 ********************************************************************************************/
00661 
00662 TrapsList *PathProcessorStroke::PrepareTrapsForStroke(Path *pPath, RenderRegion *pRender, INT32 LineWidth)
00663 {
00664     LineCapType LineCap = LineCapButt;
00665     JointType JoinStyle = RoundJoin;
00666 
00667     // BLOCK
00668     {
00669         // --- Get the current line cap style from the render region
00670         StartCapAttribute *pCapAttr = (StartCapAttribute *) pRender->GetCurrentAttribute(ATTR_STARTCAP);
00671         if (pCapAttr != NULL)
00672             LineCap = pCapAttr->StartCap;
00673 
00674         // --- Get the current line join style from the render region
00675         JoinTypeAttribute *pJoinAttr = (JoinTypeAttribute *) pRender->GetCurrentAttribute(ATTR_JOINTYPE);
00676         if (pJoinAttr != NULL)
00677             JoinStyle = pJoinAttr->JoinType;
00678     }
00679 
00680     ProcessPathToTrapezoids GenerateTraps(64);
00681     TrapsList *pTrapezoids = new TrapsList;
00682 
00683     if (pTrapezoids != NULL && GenerateTraps.Init(pPath, pTrapezoids))
00684     {
00685         // Flags are: Flatten, QuantiseLines, QuantiseAll
00686         ProcessFlags PFlags(TRUE, FALSE, FALSE);
00687         if (!GenerateTraps.Process(PFlags, TrapTravel_Parametric, JoinStyle))
00688         {
00689             delete pTrapezoids;
00690             pTrapezoids = NULL;
00691         }
00692     }
00693 
00694     return pTrapezoids;
00695 }
00696 
00697 
00698 
00699 /********************************************************************************************
00700 
00701 >   Path *PathProcessorStroke::CreateVarWidthStroke(Path *pPath, RenderRegion *pRender,
00702                                                     INT32 LineWidth, TrapsList *pTrapezoids = NULL)
00703 
00704     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
00705     Created:    31/12/96 (split into new function, 6/2/97)
00706 
00707     Inputs:     pPath       - The source path to be stroked
00708                 pRender     - The render region it will be drawn to
00709 
00710                 LineWidth   - The maximum width of the stroke. Normally this is the current
00711                               line width from the RndRgn, but derived classes like airbrushes
00712                               like to vary this value, hence it is a parameter.
00713 
00714                 pTrapezoids - NULL (in which case this function will generate the TrapList for itself),
00715                               or a pointer to a precalculated trapezoid list (this should be generated
00716                               by calling PathProcessorStroke::PrepareTrapsForStroke, and should be
00717                               used if you are generating multiple strokes from one source (e.g.
00718                               see the Airbrush stroker - ppairbsh.cpp)).
00719                               (NOTE that using one TrapsList for multiple strokes will also give more
00720                               consistent positioning of width/pattern elements along the path length)
00721 
00722     Returns:    NULL if it failed, else
00723                 a pointer to a new Path object (which the caller must delete) to be
00724                 drawn instead of the provided pPath parameter.
00725 
00726     Purpose:    Strokes the given path according to variable width parameters in the
00727                 given render region, producing a new path representing the outline of
00728                 a variable-width stroke.
00729 
00730                 Internal helper funciton for this class and derived classes.
00731 
00732     SeeAlso:    PathProcessorStroke::ProcessPath;
00733                 PathProcessorStroke::PrepareTrapsForStroke
00734 
00735 ********************************************************************************************/
00736 
00737 Path *PathProcessorStroke::CreateVarWidthStroke(Path *pPath, RenderRegion *pRender, INT32 LineWidth,
00738                                                 TrapsList *pTrapezoids)
00739 {
00740     ERROR3IF(pPath == NULL || pRender == NULL, "Illegal NULL Params");
00741     ERROR3IF(!pPath->IsStroked, "PathProcessor expects to be given a stroked path");
00742 
00743     // --- Ignore infinitely thin strokes
00744     if (LineWidth < 1)
00745         return(NULL);
00746 
00747     // --- Create a new path to be rendered in place of the provided path
00748     // Note that I use a large allocation size so that reallocation need not be done
00749     // frequently, which also helps reduce memory fragmentation.
00750     Path *pOutput = new Path;
00751     if (pOutput == NULL)
00752         return(NULL);
00753 
00754     pOutput->Initialise(128, 128);
00755 
00756     // --- Get the current cap style from the render region
00757     // In case of failure, we initialise with suitable defaults
00758     LineCapType LineCap = LineCapButt;
00759     // BLOCK
00760     {
00761         StartCapAttribute *pCapAttr = (StartCapAttribute *) pRender->GetCurrentAttribute(ATTR_STARTCAP);
00762         if (pCapAttr != NULL)
00763             LineCap = pCapAttr->StartCap;
00764     }
00765 
00766     // --- Get the variable line width descriptor from the render region
00767     ValueFunction *pValFunc = NULL;
00768     // BLOCK
00769     {
00770         VariableWidthAttrValue *pVarWidthAttr = (VariableWidthAttrValue *) pRender->GetCurrentAttribute(ATTR_VARWIDTH);
00771         if (pVarWidthAttr != NULL)
00772             pValFunc = pVarWidthAttr->GetWidthFunction();
00773 
00774         // If it is a constant width stroke, we'll just get GDraw to stroke it for us,
00775         // as that code is optimised assembler, and need not worry about variable width,
00776         // and as a result is about 4 times faster!
00777         if (pValFunc == NULL || IS_A(pValFunc, ValueFunctionConstant))
00778         {
00779             // Get the current line join style from the render region
00780             JointType JoinStyle = RoundJoin;
00781             JoinTypeAttribute *pJoinAttr = (JoinTypeAttribute *) pRender->GetCurrentAttribute(ATTR_JOINTYPE);
00782             if (pJoinAttr != NULL)
00783                 JoinStyle = pJoinAttr->JoinType;
00784 
00785             pPath->StrokePathToPath(LineWidth, LineCap, JoinStyle, NULL, pOutput);
00786 
00787             pOutput->IsFilled  = TRUE;
00788             pOutput->IsStroked = FALSE;
00789             return(pOutput);
00790         }
00791     }
00792 
00793     // --- If none were passed in, generate the set of trapezoids for the dest path
00794     BOOL MustDeleteTraps = FALSE;
00795     if (pTrapezoids == NULL)
00796     {
00797         MustDeleteTraps = TRUE;
00798         pTrapezoids = PrepareTrapsForStroke(pPath, pRender, LineWidth);
00799         if (pTrapezoids == NULL)
00800             return(NULL);
00801     }
00802 
00803     // --- Stroke the trapezoids into a variable-width path
00804     PathStroker Stroker(pValFunc, LineWidth, LineCap);
00805     
00806     // our new option allows us toonly get the forward mapped path
00807 
00808     if (!Stroker.StrokePath(pTrapezoids, pOutput))
00809     {
00810         if (MustDeleteTraps)
00811             delete pTrapezoids;
00812         return(NULL);
00813     }
00814 
00815     if (MustDeleteTraps)
00816         delete pTrapezoids;
00817 
00818     return(pOutput);
00819 }
00820 
00821 
00822 
00823 /********************************************************************************************
00824 
00825 >   Path* PathProcessorStroke::GetProcessedPath(Path* pPath, CCAttrMap* pAttrMap)
00826 
00827     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
00828     Created:    17/11/2000
00829     Inputs:     pPath       the path to stroke
00830                 pAttrMap    the attr-map to get things like line-width and cap styles from.
00831 
00832     Returns:    ptr to a new path which is a stroked version of the supplied source path, or
00833                 NULL if unsuccessful.
00834 
00835     Purpose:    This is a rehash of Diccon's GetProcessedPath method, except you needn't
00836                 worry about any nodes but the four NodeAttributes below, which must live
00837                 in the supplied attr-map.
00838 
00839                 We also don't use render-regions, as their not actually necessary
00840                 (the other version of this method does).
00841 
00842     Notes:      The following valid NodeAttributes must live in the attr-map:
00843                     AttrLineWidth
00844                     AttrStartCap
00845                     AttrVariableWidth
00846                     AttrJoinType
00847 
00848     Errors:     ERROR2 with FALSE if anything goes wrong, or our inputs are NULL or invalid.
00849 
00850     See also:   Diccon's GetProcessedPath() higher up this file.
00851 
00852 ********************************************************************************************/
00853 Path* PathProcessorStroke::GetProcessedPath(Path* pPath, CCAttrMap* pAttrMap)
00854 {
00855     ERROR2IF(pPath == NULL || pAttrMap == NULL, NULL,
00856             "PathProcessorStroke::GetProcessedPath; NULL input parameters!");
00857 
00858     INT32           LineWidth;
00859     LineCapType     LineCap;
00860     ValueFunction*  pValFunc;
00861     JointType       JoinStyle;
00862 
00863     if (!GetRequiredAttributes(pAttrMap, &LineWidth, &LineCap, &pValFunc, &JoinStyle))
00864         return NULL;
00865 
00866     if (LineWidth < 1)
00867         return NULL;
00868 
00869     // Create a path to hold our stroking exploits!
00870     // We'll prob'ly be generating loads of data, so be generous with our path's
00871     // initialisation, so it's not continually reallocating memory as it runs out.
00872     Path* pStroke = new Path;
00873     if (pStroke == NULL)
00874         return NULL;
00875     if (!pStroke->Initialise(128, 128))
00876     {
00877         delete pStroke;
00878         return NULL;
00879     }
00880 
00881     // if it's a constant-width stroke, ask GDraw to stroke it - it's
00882     // quicker and if it goes wrong, it's Gavin's fault (only joking ;o)
00883     if (pValFunc == NULL || IS_A(pValFunc, ValueFunctionConstant))
00884     {
00885         pPath->StrokePathToPath(LineWidth, LineCap, JoinStyle, NULL, pStroke);
00886         pStroke->IsFilled   = TRUE;
00887         pStroke->IsStroked  = TRUE;
00888         return pStroke;
00889     }
00890 
00891     // ok, GDraw didn't deal with it, so let's get down to business.
00892 
00893     // Generate the set of trapezoids for the dest path.
00894     ProcessPathToTrapezoids GenerateTraps(64);
00895     TrapsList *pTrapezoids = new TrapsList;
00896     BOOL    ok = (pTrapezoids != NULL);
00897     if (ok) ok = GenerateTraps.Init(pPath, pTrapezoids);
00898     if (ok)
00899     {
00900         // Flags are: Flatten, QuantiseLines, QuantiseAll
00901         ProcessFlags PFlags(TRUE, FALSE, FALSE);
00902         ok = GenerateTraps.Process(PFlags, TrapTravel_Parametric, JoinStyle);
00903     }
00904 
00905     // Stroke the trapezoids into a variable-width path.
00906     if (ok)
00907     {
00908         PathStroker Stroker(pValFunc, LineWidth, LineCap);
00909         ok = Stroker.StrokeSmoothPath(pTrapezoids, pStroke);
00910     }
00911 
00912     // tidy up temporary info.
00913     if (pTrapezoids != NULL)
00914     {
00915         delete pTrapezoids;
00916         pTrapezoids = NULL;
00917     }
00918 
00919     // tidy up the path and pass it out.
00920     if (pStroke != NULL)
00921     {
00922         if (ok)
00923         {
00924             if (!pStroke->IsClosed())
00925                 pStroke->TryToClose();
00926         }
00927         else
00928         {
00929             delete pStroke;
00930             pStroke = NULL;
00931         }
00932     }
00933 
00934     return pStroke;
00935 }
00936 
00937 
00938 
00939 /********************************************************************************************
00940 
00941 >   BOOL PathProcessorStroke::GetRequiredAttributes(CCAttrMap* pAttrMap,
00942                                                     INT32* pLineWidth,
00943                                                     LineCapType* pLineCap,
00944                                                     ValueFunction** ppValFunc,
00945                                                     JointType* pJoinStyle)
00946     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
00947     Created:    17/11/2000
00948 
00949     Inputs:     pAttrMap    ptr to the attr-map to search.
00950                 pLineWidth  line width ptr to fill.
00951                 pLineCap    end-cap type ptr to fill.
00952                 ppValFunc   variable line-width function ptr to fill.
00953                 pJoinStyle  join style ptr to fill.
00954 
00955     Outputs:    If successful, the supplied pointers are filled with the appropriate values
00956                 from the attribute map, otherwise they are filled with default values.
00957 
00958     Returns:    TRUE if successful,
00959                 FALSE otherwise.
00960 
00961     Purpose:    Extracts the following values, required for path stroking,
00962                 from the supplied attribute map:
00963                     Line width.
00964                     Line start-cap - eg square or round end.
00965                     Pointer to any applied width function - may be NULL.
00966                     Join style - eg bevelled, mitred, round.
00967 
00968     Notes:      If this function fails, *DO NOT* use the output values! They are only there
00969                 in case you screw up and *do* decide to use them, in which case they should
00970                 hopefully not bring Camelot down around your ears!
00971 
00972     Errors:     An error message is output to TRACE if we were unsuccessful.
00973                 I'm not putting in an ERROR2 or 3, as I think from a user view-point,
00974                 a quiet failure is more graceful.
00975 
00976 ********************************************************************************************/
00977 BOOL PathProcessorStroke::GetRequiredAttributes(CCAttrMap* pAttrMap,
00978                                                 INT32* pLineWidth,
00979                                                 LineCapType* pLineCap,
00980                                                 ValueFunction** ppValFunc,
00981                                                 JointType* pJoinStyle)
00982 {
00983     AttrLineWidth*      pAttrWidth      = NULL;
00984     AttrStartCap*       pAttrCap        = NULL;
00985     AttrVariableWidth*  pAttrVarWidth   = NULL;
00986     AttrJoinType*       pAttrJoinType   = NULL;
00987 
00988     pAttrMap->Lookup( CC_RUNTIME_CLASS(AttrLineWidth),  (void*&)pAttrWidth);
00989     pAttrMap->Lookup( CC_RUNTIME_CLASS(AttrStartCap),       (void*&)pAttrCap);
00990     pAttrMap->Lookup( CC_RUNTIME_CLASS(AttrVariableWidth),(void*&)pAttrVarWidth);
00991     pAttrMap->Lookup( CC_RUNTIME_CLASS(AttrJoinType),       (void*&)pAttrJoinType);
00992 
00993     if (pAttrWidth      == NULL || pAttrCap     == NULL ||
00994         pAttrVarWidth   == NULL || pAttrJoinType    == NULL)
00995     {
00996         TRACEALL( _T("PathProcessorStroke::GetProcessedPath; AttrMap doesn't contain the required attrs!") );
00997         *pLineWidth = 0;
00998         *pLineCap   = LineCapButt;
00999         *ppValFunc  = NULL;
01000         *pJoinStyle = RoundJoin;
01001         return FALSE;
01002     }
01003 
01004     *pLineWidth = pAttrWidth->Value.LineWidth;
01005     *pLineCap   = pAttrCap->Value.StartCap;
01006     *ppValFunc  = pAttrVarWidth->Value.GetWidthFunction();
01007     *pJoinStyle = pAttrJoinType->Value.JoinType;
01008 
01009     return TRUE;
01010 }
01011 
01012 
01013 
01014 NodePath* PathProcessorStroke::GetSmoothProcessedPath(Path* pPath, Node* pParent)
01015 {
01016     // we need to fake a renderregion as our helper functions need one,
01017     // luckily FormatRegion is on hand from the text stuff
01018     FormatRegion FakeRender;
01019 
01020     if (!FakeRender.Init((NodeRenderableInk*)pParent)) // init renders all applied attributes into the region
01021         return FALSE;
01022     
01023     // From here copied from ProcessPath:
01024     // --- Get the current line width, cap style, and join style from the render region
01025     // In case of failure, we initialise with suitable defaults
01026     INT32 LineWidth = 5000;
01027 
01028     // BLOCK
01029     {
01030         LineWidthAttribute *pWidthAttr = (LineWidthAttribute *) FakeRender.GetCurrentAttribute(ATTR_LINEWIDTH);
01031         if (pWidthAttr != NULL)
01032             LineWidth = pWidthAttr->LineWidth;
01033     }
01034 
01035     // --- Create a stroke outline by calling our helper function
01036     Path *pOutput = CreateSmoothVarWidthStroke(pPath, &FakeRender, LineWidth);
01037     if (pOutput == NULL)
01038         return FALSE;
01039 
01040     if (!pOutput->IsClosed())
01041         pOutput->TryToClose();
01042 
01043     // we need to make a new nodepath
01044     NodePath* pNewNode = new NodePath; 
01045         
01046     if (pNewNode == NULL)
01047     {
01048         delete pOutput;
01049         return FALSE;
01050     }
01051 
01052     // initialise the path
01053     if (!pNewNode->InkPath.Initialise(pOutput->GetNumCoords(), 128))
01054     {
01055         delete pOutput;
01056         delete pNewNode;
01057         return FALSE;
01058     }
01059 
01060     // Copy our path data into it
01061     //CALL_WITH_FAIL(!pNewNode->InkPath.CopyPathDataFrom(pOutput), pUndoOp, Success)
01062     if (!pNewNode->InkPath.CopyPathDataFrom(pOutput))
01063     {
01064         delete pOutput;
01065         delete pNewNode;
01066         return FALSE;
01067     }
01068     // don't need that path anymore
01069     delete pOutput;
01070     pOutput = NULL;
01071 
01072     return pNewNode;
01073 }
01074 
01075 
01076 Path *PathProcessorStroke::CreateSmoothVarWidthStroke(Path *pPath, RenderRegion *pRender, INT32 LineWidth,
01077                                                 TrapsList *pTrapezoids)
01078 {
01079     ERROR3IF(pPath == NULL || pRender == NULL, "Illegal NULL Params");
01080     ERROR3IF(!pPath->IsStroked, "PathProcessor expects to be given a stroked path");
01081 
01082     // --- Ignore infinitely thin strokes
01083     if (LineWidth < 1)
01084         return(NULL);
01085 
01086     // --- Create a new path to be rendered in place of the provided path
01087     // Note that I use a large allocation size so that reallocation need not be done
01088     // frequently, which also helps reduce memory fragmentation.
01089     Path *pOutput = new Path;
01090     if (pOutput == NULL)
01091         return(NULL);
01092 
01093     pOutput->Initialise(128, 128);
01094 
01095     // --- Get the current cap style from the render region
01096     // In case of failure, we initialise with suitable defaults
01097     LineCapType LineCap = LineCapButt;
01098     // BLOCK
01099     {
01100         StartCapAttribute *pCapAttr = (StartCapAttribute *) pRender->GetCurrentAttribute(ATTR_STARTCAP);
01101         if (pCapAttr != NULL)
01102             LineCap = pCapAttr->StartCap;
01103     }
01104 
01105     // --- Get the variable line width descriptor from the render region
01106     ValueFunction *pValFunc = NULL;
01107     // BLOCK
01108     {
01109         VariableWidthAttrValue *pVarWidthAttr = (VariableWidthAttrValue *) pRender->GetCurrentAttribute(ATTR_VARWIDTH);
01110         if (pVarWidthAttr != NULL)
01111             pValFunc = pVarWidthAttr->GetWidthFunction();
01112 
01113         // If it is a constant width stroke, we'll just get GDraw to stroke it for us,
01114         // as that code is optimised assembler, and need not worry about variable width,
01115         // and as a result is about 4 times faster!
01116         if (pValFunc == NULL || IS_A(pValFunc, ValueFunctionConstant))
01117         {
01118             // Get the current line join style from the render region
01119             JointType JoinStyle = RoundJoin;
01120             JoinTypeAttribute *pJoinAttr = (JoinTypeAttribute *) pRender->GetCurrentAttribute(ATTR_JOINTYPE);
01121             if (pJoinAttr != NULL)
01122                 JoinStyle = pJoinAttr->JoinType;
01123 
01124             pPath->StrokePathToPath(LineWidth, LineCap, JoinStyle, NULL, pOutput);
01125 
01126             pOutput->IsFilled  = TRUE;
01127             pOutput->IsStroked = FALSE;
01128             return(pOutput);
01129         }
01130     }
01131 
01132     // --- If none were passed in, generate the set of trapezoids for the dest path
01133     BOOL MustDeleteTraps = FALSE;
01134     if (pTrapezoids == NULL)
01135     {
01136         MustDeleteTraps = TRUE;
01137         pTrapezoids = PrepareTrapsForStroke(pPath, pRender, LineWidth);
01138         if (pTrapezoids == NULL)
01139             return(NULL);
01140     }
01141 
01142     // --- Stroke the trapezoids into a variable-width path
01143     PathStroker Stroker(pValFunc, LineWidth, LineCap);
01144     
01145     // our new option allows us to only get the forward mapped path
01146 
01147     if (!Stroker.StrokeSmoothPath(pTrapezoids, pOutput))
01148     {
01149         if (MustDeleteTraps)
01150             delete pTrapezoids;
01151         return(NULL);
01152     }
01153 
01154 
01155     if (MustDeleteTraps)
01156         delete pTrapezoids;
01157 
01158     return(pOutput);
01159 }

Generated on Sat Nov 10 03:46:33 2007 for Camelot by  doxygen 1.4.4