ppairbsh.cpp

Go to the documentation of this file.
00001 // $Id: ppairbsh.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 // ppairbsh - Definition of PathProcessor class for rendering Airbrush strokes
00099 
00100 #include "camtypes.h"
00101 
00102 #include "ppairbsh.h"
00103 
00104 //#include "attrmgr.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00105 #include "cameleps.h"
00106 //#include "docview.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00107 //#include "document.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00108 //#include "fillval.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00109 //#include "fixmem.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00110 //#include "grndbmp.h"
00111 //#include "paths.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00112 #include "pathstrk.h"
00113 #include "pathtrap.h"
00114 #include "printctl.h"
00115 #include "qualattr.h"
00116 //#include "rndrgn.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00117 //#include "spread.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00118 #include "strkattr.h"
00119 #include "valfunc.h"
00120 
00121 CC_IMPLEMENT_DYNAMIC(PathProcessorStrokeAirbrush, PathProcessorStroke);
00122 
00123 // Declare smart memory handling in Debug builds
00124 #define new CAM_DEBUG_NEW
00125 
00126 const INT32 MaxAirbrushSteps = 48;  // This is the max number of rings we'll plot
00127                                     // The user has to make a very thick stroke before 
00128                                     // 48 rings are noticably "steppy".
00129 
00130 
00131 /********************************************************************************************
00132 
00133 >   PathProcessorStrokeAirbrush::~PathProcessorStrokeAirbrush()
00134 
00135     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
00136     Created:    18/2/97
00137 
00138     Purpose:    Destructor
00139 
00140 ********************************************************************************************/
00141 
00142 PathProcessorStrokeAirbrush::~PathProcessorStrokeAirbrush()
00143 {
00144     if (pIntensityFunction != NULL)
00145         delete pIntensityFunction;
00146 }
00147 
00148 
00149 
00150 /********************************************************************************************
00151 
00152 >   virtual BOOL PathProcessorStrokeAirbrush::WillChangeFillAndStrokeSeparately(void)
00153 
00154     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
00155     Created:    31/12/96
00156 
00157     Returns:    TRUE
00158 
00159     Purpose:    Called by the RenderRegion to determine if this PathProcessorStrokeAirbrush affects
00160                 the "fill" and "stroke" portions of the path separately. (Generally
00161                 speaking, only fill/stroke providers will cause these two different
00162                 "bits" of a path to be rendered separately). This call is made BEFORE
00163                 this Processor's ProcessPath function will be called.
00164 
00165                 If the caller gets a TRUE back, the stroke and fill paths will be
00166                 rendered separately.
00167 
00168     Notes:      Base class implementation returns FALSE. Derived Stroke and Fill
00169                 processors (such as this one) override this method to return TRUE.
00170 
00171 ********************************************************************************************/
00172 
00173 BOOL PathProcessorStrokeAirbrush::WillChangeFillAndStrokeSeparately(void)
00174 {
00175     return(FALSE);
00176 }
00177 
00178 
00179 
00180 /********************************************************************************************
00181 
00182 >   virtual void PathProcessorStrokeAirbrush::ProcessPath(Path *pPath,
00183                                                           RenderRegion *pRender,
00184                                                           PathShape ShapePath=PATHSHAPE_PATH)
00185 
00186     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
00187     Created:    6/2/97
00188 
00189     Purpose:    Called by the RenderRegion to apply the path processing operation to 
00190                 the given path.
00191 
00192                 The PathProcessorStrokeAirbrush class changes the stroking (only) of paths
00193                 passed into it.
00194 
00195     Notes:      * When rendering a path, always pass in your 'this' pointer to
00196                   RenderRegion::DrawPath, so that you don't start an infinite
00197                   recursion!
00198 
00199                 * To stop rendering of the path, simply return without calling the RR
00200 
00201                 * To render this path unchanged, simply call directly back to the RR:
00202                     pRender->DrawPath(pPath, this);
00203                 
00204                 * Only affect the fill of this path if pPath->IsFilled
00205 
00206                 * Only affect the stroke of this path if pPath->IsStroked
00207 
00208                 * If converting a path into a "filled path" for stroking, the output
00209                   path should be rendered with IsStroked=FALSE or it'll get a line
00210                   around the outside!               
00211 
00212 ********************************************************************************************/
00213 
00214 void PathProcessorStrokeAirbrush::ProcessPath(Path *pPath,
00215                                               RenderRegion *pRender,
00216                                               PathShape ShapePath)
00217 {
00218     PORTNOTETRACE("other","PathProcessorStrokeAirbrush::ProcessPath - do nothing");
00219 #ifndef EXCLUDE_FROM_XARALX
00220     ERROR3IF(pPath == NULL || pRender == NULL, "Illegal NULL Params");
00221 
00222     // --- If the provided path is not stroked, then we'll just pass it straight through
00223     // We also don't touch it if we're doing EOR rendering, or click regions
00224     // BLOCK
00225     {
00226         StrokeColourAttribute *pStrokeColour = (StrokeColourAttribute *) pRender->GetCurrentAttribute(ATTR_STROKECOLOUR);
00227         if (pRender->DrawingMode != DM_COPYPEN || pRender->IsHitDetect()
00228             || !pPath->IsStroked || pStrokeColour == NULL || pStrokeColour->Colour.IsTransparent())
00229         {
00230             pRender->DrawPath(pPath, this, ShapePath);
00231             return;
00232         }
00233     }
00234 
00235     // --- If the quality is set low, strokes are just rendered as centrelines
00236     // BLOCK
00237     {
00238         QualityAttribute *pQuality = (QualityAttribute *) pRender->GetCurrentAttribute(ATTR_QUALITY);
00239         if (pQuality != NULL && pQuality->QualityValue.GetLineQuality() != Quality::FullLine)
00240         {
00241             pRender->DrawPath(pPath, this, ShapePath);
00242             return;
00243         }
00244     }
00245 
00246     // --- If the attribute which created us is not the current StrokeType attribute, then
00247     // we have been overridden by a different stroke type, so we do nothing.
00248     // BLOCK
00249     {
00250         StrokeTypeAttrValue *pTypeAttr = (StrokeTypeAttrValue *) pRender->GetCurrentAttribute(ATTR_STROKETYPE);
00251         if (pTypeAttr != NULL && pTypeAttr != GetParentAttr())
00252         {
00253             pRender->DrawPath(pPath, this, ShapePath);
00254             return;
00255         }
00256     }
00257 
00258     // --- Get the current line width from the render region
00259     // In case of failure, we initialise with suitable defaults
00260     INT32 LineWidth = 5000;
00261     // BLOCK
00262     {
00263         LineWidthAttribute *pWidthAttr = (LineWidthAttribute *) pRender->GetCurrentAttribute(ATTR_LINEWIDTH);
00264         if (pWidthAttr != NULL)
00265             LineWidth = pWidthAttr->LineWidth;
00266     }
00267 
00268     // Obtain an optimal number of steps for the line
00269     // When printing, we do heaps of steps to get top quality out the other end
00270     View *pView = pRender->GetRenderView();
00271     ERROR3IF(pView == NULL, "No render view?!");
00272 
00273     INT32 NumSteps = MaxAirbrushSteps;
00274     if (!pRender->IsPrinting())
00275         GetNumSteps(pView, LineWidth);
00276 
00277     // --- Now, create a transparency mask bitmap for the airbrush
00278     Spread *pSpread = pRender->GetRenderSpread();
00279 //  ERROR3IF(pSpread == NULL, "No render spread!?");    // This can happen, rendering into a gallery
00280 
00281     // Get the render region's clip rectangle in Spread Coords. We don't need to
00282     // render anything bigger than this size, so it is the upper limit on our bitmap area.
00283     DocRect ClipRegion = pRender->GetClipRect();
00284 
00285     // Intersect this with the path bounding rectangle to get the actual region we need to redraw
00286     // The smaller this is, the faster we go and the less memory we use.
00287     //DocRect PathRect = pPath->GetBoundingRect();
00288     DocRect PathRect = pPath->GetBlobRect();
00289     PathRect.Inflate(LineWidth);
00290 
00291     BOOL Intersects = ClipRegion.IsIntersectedWith(PathRect);
00292     if(!Intersects)
00293     {
00294         // Don't bother drawing anything - it's clipped out
00295         return;
00296     }
00297 
00298     ClipRegion = ClipRegion.Intersection(PathRect);
00299 
00300     // Round the ClipRegion edges up so they all lie exactly on screen pixel boundaries.
00301     // If we don't do this, then there can be a sub-pixel rounding error between the ClipRegion
00302     // and the actual bitmap size, so that the bitmap is scaled slightly when we plot it.
00303     // By making sure it's pixelised, we guarantee that the bitmap & clipregion are exactly equal sizes.
00304     // (It doesn't matter if the bitmap is a bit bigger than necessary)
00305     ClipRegion.Inflate(pRender->GetScaledPixelWidth());
00306     ClipRegion.lo.Pixelise(pView);
00307     ClipRegion.hi.Pixelise(pView);
00308 
00309     // Get the current view's rendering matrix and view scale
00310     Matrix ConvMatrix = pRender->GetMatrix();//pView->ConstructRenderingMatrix(pSpread);
00311     FIXED16 ViewScale = pView->GetViewScale();
00312 
00313     // Generate a 256-colour greyscale palette
00314     LOGPALETTE *pPalette = MakeGreyScalePalette();
00315     if(pPalette == NULL)
00316     {
00317         pRender->DrawPath(pPath, this, ShapePath);
00318         return;
00319     }
00320 
00321     // Work out the DPI to use. Rather than just asking for PixelWidth or DPI from the
00322     // render region, we have to do a load of non-object-oriented stuff instead...
00323     double dpi = 96.0;
00324     if (pRender->IsPrinting())
00325     {
00326         // we are printing, so ask the print options
00327         PrintControl *pPrintControl = pView->GetPrintControl();
00328         if (pPrintControl != NULL)
00329             dpi = (double) pPrintControl->GetDotsPerInch();
00330     }
00331     else if (IS_A(pRender, CamelotEPSRenderRegion))
00332     {
00333         // Use DPI as set in EPS export options dialog.
00334         dpi = (double) EPSFilter::XSEPSExportDPI;
00335     }
00336     else
00337     {
00338         ERROR3IF(pRender->GetPixelWidth() <= 0, "Stupid (<1 millipoint) Pixel Width!");
00339         if (pRender->GetPixelWidth() > 0)
00340             dpi = (double) (72000.0 / (double)pRender->GetPixelWidth());
00341     }
00342 
00343     GRenderBitmap *pMaskRegion = new GRenderBitmap(ClipRegion, ConvMatrix, ViewScale, 8, dpi,
00344                                                     pRender->IsPrinting(), XARADITHER_ORDERED_GREY,
00345                                                     pPalette, FALSE);
00346     if (pMaskRegion == NULL)
00347     {
00348         pRender->DrawPath(pPath, this, ShapePath);
00349         return;
00350     }
00351 
00352     BOOL PathIsFilled = FALSE;      // Will be set TRUE if this path should be filled at the bottom of the function
00353 
00354     //BLOCK
00355     {
00356         // Change the GDraw context in this render region so as to preserve the state
00357         // of the main render region. This is because GRenderRegions currently use
00358         // a single static GDrawContext! This also sets it up with a nice greyscale palette
00359         // so that we get the output we desire.
00360         pMaskRegion->UseGreyscaleContextDangerous();
00361 
00362         // Attach our DC to the view and render region...
00363         if (pMaskRegion->AttachDevice(pView, NULL, pSpread))
00364         {
00365             pMaskRegion->StartRender();
00366 
00367             // We must save & restore the attribute state around all our rendering because
00368             // attributes otherwise stay on the renderstack until we delete the RndRgn, and as our
00369             // ones are on the program stack, they will have ceased to exist before then, which
00370             // makes for a wicked explosion.
00371             pMaskRegion->SaveContext();
00372 
00374             // --- Main Airbrush rendering loop
00375 
00376             // Make sure we've got an intensity function to use. This will create a new one if necessary
00377             ValueFunction *pvValueFunction = GetIntensityFunction();
00378             if (pvValueFunction == NULL)
00379             {
00380                 ERROR3("Failed to set an intensity function on an airbrush stroke");
00381                 pRender->DrawPath(pPath, this, ShapePath);
00382                 return;
00383             }
00384 
00385 
00386             if(!RenderAirBrush(pPath, pMaskRegion, LineWidth, NumSteps, pvValueFunction,
00387                                pRender, ShapePath))
00388             {
00389                 // Airbrush failed - just render the path without the airbrush effect
00390                 pRender->DrawPath(pPath, this, ShapePath);
00391                 return;
00392             }
00393 
00394             pMaskRegion->RestoreContext();
00395 
00396             // --- ClipRect test code
00397 
00399 
00400 
00401             // --- We have drawn the airbrushed stroke - now, if the path is filled, we
00402             // will render the filled area, so that in semi-transparent cases, the
00403             // stroke will not "show through" from behind the filled area.          
00404             if (pPath->IsFilled)
00405             {
00406                 ColourFillAttribute *pCFAttr = (ColourFillAttribute   *) pRender->GetCurrentAttribute(ATTR_FILLGEOMETRY);
00407                 if (pCFAttr != NULL && (!pCFAttr->Colour.IsTransparent() || pCFAttr->IsABitmapFill()))
00408                 {
00409                     PathIsFilled = TRUE;
00410 
00411                     pMaskRegion->SaveContext();
00412 
00413                     ColourFillAttribute *pFillAttr = NULL;
00414                     FillMappingAttribute *pMapAttr = NULL;
00415 
00416                     // Obtain the object's transparent fill geometry
00417                     TranspFillAttribute *pTransAttr = (TranspFillAttribute *) pRender->GetCurrentAttribute(ATTR_TRANSPFILLGEOMETRY);
00418                     if (pTransAttr != NULL)
00419                     {
00420                         // Get a non-transparent version of the fill geometry
00421                         pFillAttr = pTransAttr->MakeSimilarNonTranspFillGeometry(1.0);
00422                         
00423                         // Convert a fill mapping
00424                         TranspFillMappingAttribute *pTransMapAttr = (TranspFillMappingAttribute *) pRender->GetCurrentAttribute(ATTR_TRANSPFILLMAPPING);
00425                         if(pTransMapAttr != NULL)
00426                             pMapAttr = pTransMapAttr->MakeSimilarNonTranspFillMapping();
00427                     }
00428 
00429                     // Setup region and draw path into it
00430                     if (pFillAttr != NULL)
00431                     {
00432                         pMaskRegion->SetFillGeometry(pFillAttr, TRUE);
00433 
00434                         if(pMapAttr != NULL)
00435                             pMaskRegion->SetFillMapping(pMapAttr, TRUE);
00436                     }
00437                     else
00438                         pMaskRegion->SetFillColour(DocColour(0, 0, 0));
00439 
00440                     pMaskRegion->SetLineColour(DocColour(COLOUR_TRANS));
00441                     pMaskRegion->DrawPath(pPath, NULL, ShapePath);
00442 
00443                     pMaskRegion->RestoreContext();
00444                 }
00445             }
00446 
00447             pMaskRegion->StopRender();
00448         }
00449 
00450         pMaskRegion->StopUsingGreyscaleContextDangerous();
00451     }
00452 
00453     // Extract the transparency mask bitmap from the render region
00454     OILBitmap *pOilBmp = pMaskRegion->ExtractBitmap();
00455 
00456     // We no longer need the RenderRegion, so scrap it.
00457     delete pMaskRegion;
00458     pMaskRegion = NULL;
00459     pPalette = NULL;
00460 
00461     // Now, render a rectangle to the output render region, using the transparency mask
00462     if (pOilBmp == NULL)
00463         return;
00464 
00465     KernelBitmap *pMask = new KernelBitmap(pOilBmp, TRUE);
00466 
00467     if (pMask != NULL)
00468     {
00469         // Make sure the bitmap knows it's already a greyscale, else it will spend a lot of
00470         // time "converting" itself to a greyscale, and what's more, corrupting the grey levels
00471         // so that 255 (invisible) becomes 254 (slightly visible). Arrrrrgh!
00472         pMask->SetAsGreyscale();
00473 
00474         // Create a transparency attribute from our mask bitmap
00475         BitmapTranspFillAttribute Trans;
00476 
00477         // We don't call pTrans->AttachBitmap because it seems to be stupid, and causes ructions
00478         // when we try to attach a temporary bitmap. We thus do the same thing, but avoiding
00479         // its attempts to automatically screw us about.
00480         Trans.BitmapRef.Detach();
00481         Trans.BitmapRef.SetBitmap(pMask);
00482 
00483         Trans.SetStartPoint(&ClipRegion.lo);
00484         DocCoord EndPoint(ClipRegion.hi.x, ClipRegion.lo.y);
00485         Trans.SetEndPoint(&EndPoint);
00486         DocCoord EndPoint2(ClipRegion.lo.x, ClipRegion.hi.y);
00487         Trans.SetEndPoint2(&EndPoint2);
00488 
00489         UINT32 TValue = 0;
00490         Trans.SetStartTransp(&TValue);
00491         TValue = 255;
00492         Trans.SetEndTransp(&TValue);
00493 
00494         // Use the same transparency type as is set on the object being rendered (if any)
00495         {
00496             TranspFillAttribute *pTransAttr = (TranspFillAttribute *) pRender->GetCurrentAttribute(ATTR_TRANSPFILLGEOMETRY);
00497 
00498             if (pTransAttr != NULL)
00499                 Trans.SetTranspType(pTransAttr->GetTranspType());
00500             else
00501                 Trans.SetTranspType(TT_Mix);        // By default, we'll use Mix transparency
00502             
00503         }
00504 
00505         // --- OK, we finally got here! Render the stroke, using the transparency mask we just made
00506         pRender->SaveContext();
00507 
00508             Trans.Render(pRender);
00509 
00510             // Render the path. If it is filled, then we render the entire thing (fill & stroke) using
00511             // the current fill geometry (to get a shadow/feather effect)
00512             if (PathIsFilled)
00513             {
00514                 // Render the entire thing (fill & stroke) in one go. We render a rectangle over the cliprect
00515                 // so that we do everything in one go (we can't render the fill &7 stroke separately, or
00516                 // the transparency will overlap & it'll look wrong)
00517                 pRender->SetLineColour(DocColour(COLOUR_TRANS));        // Don't render a line
00518 
00519                 Path Rect;
00520                 Rect.Initialise();
00521                 Rect.AddMoveTo(ClipRegion.lo);
00522                 Rect.AddLineTo(DocCoord(ClipRegion.hix, ClipRegion.loy));
00523                 Rect.AddLineTo(ClipRegion.hi);
00524                 Rect.AddLineTo(DocCoord(ClipRegion.lox, ClipRegion.hiy));
00525                 Rect.AddLineTo(ClipRegion.lo);
00526                 Rect.IsFilled  = TRUE;
00527                 Rect.IsStroked = FALSE;
00528                 pRender->DrawPath(&Rect, this, ShapePath);
00529             }
00530             else
00531             {
00532                 // Otherwise, create a filled-outline path for the entire stroke, and render it
00533                 // !!!!****ToDo - for now, strokes always render flat-filled with the stroke colour
00534                 StrokeColourAttribute *pStrokeColour = (StrokeColourAttribute *) pRender->GetCurrentAttribute(ATTR_STROKECOLOUR);
00535                 if (pStrokeColour != NULL)
00536                     pRender->SetFillColour(pStrokeColour->Colour);
00537 
00538                 // Fill the holes
00539                 pRender->SetWindingRule(NonZeroWinding);
00540 
00541                 Path *pOutput = CreateVarWidthStroke(pPath, pRender, LineWidth);
00542                 if (pOutput != NULL)
00543                 {
00544                     pRender->DrawPath(pOutput, NULL, ShapePath);
00545                     delete pOutput;
00546                     pOutput = NULL;
00547                 }
00548             }
00549 
00550         pRender->RestoreContext();
00551 
00552         // Delete the kernel bitmap. This auto-deletes the OIL bitmap for us
00553         delete pMask;
00554     }
00555 #endif
00556 }
00557 
00558 
00559 
00560 /********************************************************************************************
00561 
00562 >   virtual BOOL PathProcessorStrokeAirbrush::IsDifferent(PathProcessorStroke *pOther);
00563 
00564     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
00565     Created:    6/2/97
00566 
00567     Inputs:     pOther - The other PathProcessorStroke
00568 
00569     Returns:    TRUE if they're considered different, FALSE if they are equivalent
00570 
00571     Purpose:    Equality operator
00572 
00573     Notes:      The base class implementation compares the runtime classes of the
00574                 2 objects to see if they are different classes. If they are the same
00575                 class, it assumes they're cnsidered equal - derived classes should
00576                 override this behaviour if further comparisons are necessary.
00577 
00578 ********************************************************************************************/
00579 
00580 BOOL PathProcessorStrokeAirbrush::IsDifferent(PathProcessorStroke *pOther)
00581 {
00582     ERROR3IF(pOther == NULL, "Illegal NULL param");
00583 
00584     if (GetRuntimeClass() != pOther->GetRuntimeClass())
00585         return(TRUE);
00586 
00587     PathProcessorStrokeAirbrush *pOtherAir = (PathProcessorStrokeAirbrush *)pOther;
00588 
00589     ValueFunction *pMyFunc    = GetIntensityFunction();
00590     ValueFunction *pOtherFunc = pOtherAir->GetIntensityFunction();
00591     if (pMyFunc == NULL || pOtherFunc == NULL)
00592         return(TRUE);
00593 
00594     // We're the same if we share the same intensity function
00595     return(pMyFunc->IsDifferent(pOtherFunc));
00596 }
00597 
00598 
00599 
00600 /********************************************************************************************
00601 
00602 >   virtual PathProcessorStrokeAirbrush *PathProcessorStrokeAirbrush::Clone(void)
00603 
00604     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
00605     Created:    6/2/97
00606 
00607     Returns:    NULL if we're out of memory, else
00608                 a pointer to a clone (exact copy) of this object
00609 
00610     Purpose:    To copy PathProcessorStrokeAirbrush or derived-class object
00611 
00612 ********************************************************************************************/
00613 
00614 PathProcessorStroke *PathProcessorStrokeAirbrush::Clone(void)
00615 {
00616     // Clone this object - this can be done by just creating a new one
00617     PathProcessorStrokeAirbrush *pClone = new PathProcessorStrokeAirbrush;
00618 
00619     if (pClone != NULL)
00620     {
00621         // And copy the (base class) parent-attribute pointer so we know when our
00622         // parent attribute is "current" in the render region.
00623         pClone->SetParentAttr(GetParentAttr());
00624 
00625         // And copy our intensity function, if any
00626         if (pIntensityFunction != NULL)
00627         {
00628             ValueFunction *pNewFunc = pIntensityFunction->Clone();
00629             pClone->SetIntensityFunction(pNewFunc);     // This is OK if it's NULL
00630         }
00631     }
00632 
00633     return(pClone);
00634 }
00635 
00636 
00637 
00638 /********************************************************************************************
00639 >   virtual INT32 GetNumSteps(View *pView, INT32 LineWidth)
00640 
00641     Author:     Richard_Millican (Xara Group Ltd) <camelotdev@xara.com>
00642     Created:    11/2/97
00643     Inputs:     pView       - Relevant View (for zoom info)
00644                 LineWidth   - LineWidth in millipoints
00645     Returns:    Number of steps which will produce a smooth graduated airbrush for this
00646                 line at the current zoom, etc...
00647 
00648     Purpose:    To get the number of steps for an airbrush stroke
00649 ********************************************************************************************/
00650 
00651 INT32 PathProcessorStrokeAirbrush::GetNumSteps(View *pView, INT32 LineWidth)
00652 {
00653     if(pView == NULL)
00654         return 1;
00655 
00656     // Find out how many millipoints 1 pixel is
00657     const INT32 spw = pView->GetScaledPixelWidth().MakeLong();
00658     const INT32 sph = pView->GetScaledPixelHeight().MakeLong();
00659     const MILLIPOINT OnePixel = min(spw, sph);
00660     TRACEUSER( "Richard", _T("1 pixel = %d\n"), OnePixel);
00661 
00662     if (OnePixel <= 0)
00663         return(1);
00664 
00665     // Now work out how many steps to render. The fewer steps, the faster we go, but this
00666     // trades off against quality. Because we start in the centre and increase in both
00667     // directions, we need to divide line width by the size of 2 pixels to get a step per pixel.
00668     // However, 3 is used because it means each ring of the airbrush is 1.5 pixels wider than the
00669     // last, which, with anti-aliasing, gives a very smooth effect. Going up to 4 makes each one
00670     // 2 pixels, and you can begin to see tree-ring effects in the airbrush.
00671     INT32 NumSteps = (INT32)(LineWidth / (OnePixel * 3));
00672 
00673     // Limit it to a sensible range 
00674     if(NumSteps < 1)
00675         NumSteps = 1;
00676     if(NumSteps > MaxAirbrushSteps)
00677         NumSteps = MaxAirbrushSteps;
00678 
00679     TRACEUSER( "Richard", _T("NumSteps = %d\n"), NumSteps);
00680 
00681     return NumSteps;
00682 }
00683 
00684 
00685 
00686 /********************************************************************************************
00687 >   LOGPALETTE *PathProcessorStrokeAirbrush::MakeGreyScalePalette(void)
00688 
00689     Author:     Richard_Millican (Xara Group Ltd) <camelotdev@xara.com>
00690     Created:    11/2/97
00691     Returns:    A 256 entry greyscale palette, or NULL if it failed
00692 
00693     Purpose:    Obtain a greyscale palette
00694 
00695     Notes:      A static palette is cached to save building one for every redraw
00696 
00697 ********************************************************************************************/
00698 
00699 LOGPALETTE *PathProcessorStrokeAirbrush::MakeGreyScalePalette(void)
00700 {
00701     static BYTE Pal[sizeof(LOGPALETTE) + 256*sizeof(PALETTEENTRY)];
00702     static LOGPALETTE *pPal = NULL;
00703     
00704     if (pPal == NULL)       // Only fill in the palette the first time we're called
00705     {
00706         pPal = (LOGPALETTE *) Pal;
00707 
00708         pPal->palVersion = 0x300;
00709         pPal->palNumEntries = 256;
00710         for (INT32 i = 0; i < 256; i++)
00711         {
00712             pPal->palPalEntry[i].peRed      = i;
00713             pPal->palPalEntry[i].peGreen    = i;
00714             pPal->palPalEntry[i].peBlue     = i;
00715             pPal->palPalEntry[i].peFlags    = 0;
00716         }
00717     }
00718 
00719     return pPal;
00720 }
00721 
00722 
00723 
00724 /********************************************************************************************
00725 
00726 >   virtual BOOL PathProcessorStrokeAirbrush::RenderAirBrush(Path *pPath,
00727             GRenderBitmap *pRegion, INT32 LineWidth, INT32 NumSteps,
00728             ValueFunction *pvValueFunction, RenderRegion *pDestRegion,
00729             PathShape ShapePath)
00730 
00731     Author:     Richard_Millican (Xara Group Ltd) <camelotdev@xara.com>
00732     Created:    11/2/97
00733     Inputs:     pPath           - Path to render
00734                 pRegion         - Region to render the airbrush tranparency mask into
00735                 LineWidth       - Maximum linewidth
00736                 NumSteps        - Number of Airbrush graduation steps
00737                 pvValueFunction - Value function for the edge rampyness
00738                 pDestRegion     - Region we are rendering the airbrush to in the end
00739                                   (needed to get current stroke attributes from)
00740                 ShapePath       - The PathShape of the current shape.
00741     Returns:    TRUE if things went ok
00742 
00743     Purpose:    Main Airbrush rendering loop
00744 ********************************************************************************************/
00745 
00746 BOOL PathProcessorStrokeAirbrush::RenderAirBrush(Path *pPath, GRenderBitmap *pRegion,
00747         INT32 LineWidth, INT32 NumSteps, ValueFunction *pvValueFunction,
00748         RenderRegion *pDestRegion, PathShape ShapePath)
00749 {
00750     PORTNOTETRACE("other","PathProcessorStrokeAirbrush::RenderAirBrush - do nothing");
00751 #ifndef EXCLUDE_FROM_XARALX
00752     if(pPath == NULL || pRegion == NULL || NumSteps <= 0)
00753     {
00754         ERROR3("PathProcessorStrokeAirbrush::RenderAirBrush given dodgy params");
00755         return FALSE;
00756     }
00757 
00758     // Fill the holes
00759     pRegion->SetWindingRule(NonZeroWinding);
00760 
00761     // Precalculate the trapezoid structure for stroking with
00762     TrapsList *pTrapezoids = PrepareTrapsForStroke(pPath, pDestRegion, LineWidth);
00763     if (pTrapezoids == NULL)
00764         return FALSE;
00765 
00766     INT32 LastIntensity = 255;
00767 
00768     // Use the previously obtained number of steps for the graduation
00769     for(INT32 i = NumSteps; i > 0; i--)
00770     {
00771         INT32 Intensity = 0;
00772         
00773         // What intensity should we use ?
00774         if(pvValueFunction == NULL)
00775         {
00776             // A linear ramp
00777             Intensity = (255 * i) / NumSteps;
00778         }
00779         else
00780         {
00781             // Use the value function
00782             double Position = (double)i / (double)NumSteps;         
00783             Intensity = (INT32)((1.0 - pvValueFunction->GetValue(Position)) * 255.0);
00784         }
00785 
00786         // Bit dodgy for 1st stroke, but then it shouldn't be the same as the last
00787         if(LastIntensity != Intensity)
00788         {                              
00789             LastIntensity = Intensity;
00790 
00791             if(Intensity != 255)
00792             {
00793                 // --- Create the stroke outline by calling our helper function
00794                 INT32 ThisWidth = (LineWidth * i) / NumSteps;
00795 
00796                 Path *pOutput = CreateVarWidthStroke(pPath, pDestRegion, ThisWidth, pTrapezoids);
00797                 if(pOutput != NULL)
00798                 {
00799                     ColourFillAttribute *pFillAttr = NULL;
00800                     FillMappingAttribute *pMapAttr = NULL;
00801 
00802                     if(pDestRegion != NULL)
00803                     {
00804                         // Obtain the object's transparent fill geometry
00805                         TranspFillAttribute *pTransAttr = (TranspFillAttribute *) pDestRegion->GetCurrentAttribute(ATTR_TRANSPFILLGEOMETRY);
00806                         if (pTransAttr == NULL)
00807                         {
00808                             // There is no transparency on this object - just use a flat transparency
00809                             pFillAttr = new FlatFillAttribute;
00810                             if (pFillAttr)
00811                                 pFillAttr->SetStartColour(&DocColour(Intensity, Intensity, Intensity));
00812                         }
00813                         else
00814                         {
00815                             // Get a non-transparent version of the fill geometry
00816                             pFillAttr = pTransAttr->MakeSimilarNonTranspFillGeometry(1.0 - ((double)(Intensity) / 255.0));
00817                             
00818                             // Convert a fill mapping
00819                             TranspFillMappingAttribute *pTransMapAttr = (TranspFillMappingAttribute *) pDestRegion->GetCurrentAttribute(ATTR_TRANSPFILLMAPPING);
00820                             if(pTransMapAttr != NULL)
00821                                 pMapAttr = pTransMapAttr->MakeSimilarNonTranspFillMapping();
00822                         }
00823                     }
00824 
00825                     // Setup region and draw path into it
00826                     if(pFillAttr != NULL)
00827                     {
00828                         pRegion->SetFillGeometry(pFillAttr, TRUE);
00829 
00830                         if(pMapAttr != NULL)
00831                             pRegion->SetFillMapping(pMapAttr, TRUE);
00832                     }
00833                     else
00834                         pRegion->SetFillColour(DocColour(Intensity, Intensity, Intensity));
00835                     
00836                     pRegion->DrawPath(pOutput, NULL, ShapePath);
00837                     delete pOutput;
00838                 }
00839             }
00840         }
00841     }
00842 
00843     delete pTrapezoids;
00844 
00845     return TRUE;
00846 #else
00847     return FALSE;
00848 #endif
00849 }
00850 
00851 
00852 
00853 /********************************************************************************************
00854 
00855 >   ValueFunction *PathProcessorStrokeAirbrush::GetIntensityFunction(void)
00856 
00857     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
00858     Created:    18/2/97
00859 
00860     Returns:    NULL (if it fails horribly), else a pointer to the intensity ValueFunction
00861 
00862     Purpose:    Sets the intensity function which will be used when rendering this airbrush.
00863                 The function controls the intensity (opacity) of the airbrush at Position
00864                 values where Position 0.0 represents the airbrush centreline, and 1.0
00865                 represents the maximum width.
00866 
00867     SeeAlso:    PathProcessorStrokeAirbrush::GetIntensityFunction
00868 
00869 ********************************************************************************************/
00870 
00871 ValueFunction *PathProcessorStrokeAirbrush::GetIntensityFunction(void)
00872 {
00873     if (pIntensityFunction == NULL)
00874         pIntensityFunction = new ValueFunctionRampS(1.0, 0.0);
00875 
00876     return(pIntensityFunction);
00877 }
00878 
00879 
00880 
00881 /********************************************************************************************
00882 
00883 >   void PathProcessorStrokeAirbrush::SetIntensityFunction(ValueFunction *pFunc)
00884 
00885     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
00886     Created:    18/2/97
00887 
00888     Inputs:     pFunc - The new ValueFunction to use
00889                 A value of NULL will set the airbrush to the default setting (using
00890                 an ValueFunctionRampS from 1.0 to 0.0)
00891 
00892     Purpose:    Sets the intensity function which will be used when rendering this airbrush.
00893                 The function controls the intensity (opacity) of the airbrush at Position
00894                 values where Position 0.0 represents the airbrush centreline, and 1.0
00895                 represents the maximum width.
00896 
00897     SeeAlso:    PathProcessorStrokeAirbrush::GetIntensityFunction
00898 
00899 ********************************************************************************************/
00900 
00901 void PathProcessorStrokeAirbrush::SetIntensityFunction(ValueFunction *pFunc)
00902 {
00903     if (pIntensityFunction != NULL)
00904         delete pIntensityFunction;
00905 
00906     pIntensityFunction = pFunc;
00907 }
00908 

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