// $Id: fthrattr.cpp 1361 2006-06-25 16:43:38Z alex $ /* @@tag:xara-cn@@ DO NOT MODIFY THIS LINE ================================XARAHEADERSTART=========================== Xara LX, a vector drawing and manipulation program. Copyright (C) 1993-2006 Xara Group Ltd. Copyright on certain contributions may be held in joint with their respective authors. See AUTHORS file for details. LICENSE TO USE AND MODIFY SOFTWARE ---------------------------------- This file is part of Xara LX. Xara LX is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. Xara LX and its component source files are distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Xara LX (see the file GPL in the root directory of the distribution); if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ADDITIONAL RIGHTS ----------------- Conditional upon your continuing compliance with the GNU General Public License described above, Xara Group Ltd grants to you certain additional rights. The additional rights are to use, modify, and distribute the software together with the wxWidgets library, the wxXtra library, and the "CDraw" library and any other such library that any version of Xara LX relased by Xara Group Ltd requires in order to compile and execute, including the static linking of that library to XaraLX. In the case of the "CDraw" library, you may satisfy obligation under the GNU General Public License to provide source code by providing a binary copy of the library concerned and a copy of the license accompanying it. Nothing in this section restricts any of the rights you have under the GNU General Public License. SCOPE OF LICENSE ---------------- This license applies to this program (XaraLX) and its constituent source files only, and does not necessarily apply to other Xara products which may in part share the same code base, and are subject to their own licensing terms. This license does not apply to files in the wxXtra directory, which are built into a separate library, and are subject to the wxWindows license contained within that directory in the file "WXXTRA-LICENSE". This license does not apply to the binary libraries (if any) within the "libs" directory, which are subject to a separate license contained within that directory in the file "LIBS-LICENSE". ARRANGEMENTS FOR CONTRIBUTION OF MODIFICATIONS ---------------------------------------------- Subject to the terms of the GNU Public License (see above), you are free to do whatever you like with your modifications. However, you may (at your option) wish contribute them to Xara's source tree. You can find details of how to do this at: http://www.xaraxtreme.org/developers/ Prior to contributing your modifications, you will need to complete our contributor agreement. This can be found at: http://www.xaraxtreme.org/developers/contribute/ Please note that Xara will not accept modifications which modify any of the text between the start and end of this header (marked XARAHEADERSTART and XARAHEADEREND). MARKS ----- Xara, Xara LX, Xara X, Xara X/Xtreme, Xara Xtreme, the Xtreme and Xara designs are registered or unregistered trademarks, design-marks, and/or service marks of Xara Group Ltd. All rights in these marks are reserved. Xara Group Ltd, Gaddesden Place, Hemel Hempstead, HP2 6EX, UK. http://www.xara.com/ =================================XARAHEADEREND============================ */ #include "camtypes.h" #include "fthrattr.h" // Algorithms and node classes used for creating feather transp mask #include "nodecntr.h" // NodeContour::GenerateContourPathForNode //#include "doccolor.h" // DocColour and COLOUR_BLACK #include "lineattr.h" // AttrLineWidth //#include "fillval.h" // BitmapFillAttribute, TransparencyFillAttribute - in camtypes.h [AUTOMATICALLY REMOVED] #include "offscrn.h" // GRenderRegionWrapper // Tags, messages and other UI stuff //#include "attrmgr.h" // enum AttrIndex - in camtypes.h [AUTOMATICALLY REMOVED] //#include "feather.h" // _R(IDS_FEATHER_ATTR_ID) //#include "mario.h" // _R(IDE_NOMORE_MEMORY) // RenderRegions #include "grndrgn.h" // GRenderRegion // Fiddling with bitmaps - offscreen and feather transp bmps //#include "bitmap.h" - in camtypes.h [AUTOMATICALLY REMOVED] #include "oilbitmap.h" // Caching contour path and updating on pseudo AllowOps #include "opfeathr.h" // RegenerateFeatherContourAction #include "objchge.h" // ObjChange and AllowOp stuff #include "transop.h" // TransOperation - optimise which operations trigger recontour // Blending (creating dynamic attributes) #include "blendatt.h" // BlendAttrParam #include "nodeblnd.h" // NodeBlend #include "attrmap.h" // CCAttrMap #include "nodepath.h" // Special cases #include "nodedoc.h" // NodeDocument //#include "grndclik.h" // GRenderClick //#include "grndbrsh.h" // GRenderBrush // Saving and loading #include "cxftags.h" //#include "cxfdefs.h" - in camtypes.h [AUTOMATICALLY REMOVED] //#include "cxfrec.h" - in camtypes.h [AUTOMATICALLY REMOVED] // Printing //#include "saveeps.h" // Quality settings #include "qualattr.h" #include "nodemold.h" //#include "bitmapcache.h" #include "pathndge.h" //DECLARE_SOURCE("$Revision: 1361 $") CC_IMPLEMENT_DYNCREATE( FeatherAttrValue, OffscreenAttrValue ) CC_IMPLEMENT_DYNAMIC( AttrFeather, NodeAttribute ) CC_IMPLEMENT_DYNAMIC( FeatherRecordHandler,CamelotRecordHandler ) // Declare smart memory handling in Debug builds #define new CAM_DEBUG_NEW // Uncomment any of the following to attach the associated bitmap into the bitmap gallery //#define DEBUG_ATTACH_PRECONV_OFFSCREEN_BMP //#define DEBUG_ATTACH_OFFSCREEN_BMP //#define DEBUG_ATTACH_SILHOUETTE_BMP //#define DEBUG_ATTACH_FEATHER_BMP //#define DEBUG_RENDER_OFFSCREEN_BMP_WITHOUT_MASK // The maximum feather size which we allow the user to play with. // If you're thinking of changing this, PLEASE BE AWARE that other/older versions // of the app will cap feathers loaded/pasted into them at THEIR maximum value. // As of 30/01/2001, GDraw's maximum blur size is a 200 pixel diameter. // I'm choosing a user limit of 50 pixels diameter at 96dpi (100% zoom on most systems) // which gives 50 * (72000 / 96) = 37500 millipoints. // const MILLIPOINT FeatherAttrValue::MaxUserFeatherSize = 37500; ///////////////////////////////////////////////////////////////////////////////////////////// // // Helper functions // ///////////////////////////////////////////////////////////////////////////////////////////// // TRUE if map contains all necessary line attrs for NodePath::MakeNodePathFromAttributes to succeed // FALSE otherwise BOOL CheckLineAttrs(CCAttrMap* pAttrMap) { void* ptr = NULL; return ( pAttrMap->Lookup( CC_RUNTIME_CLASS(AttrLineWidth),(void *&)ptr) && pAttrMap->Lookup( CC_RUNTIME_CLASS(AttrStartArrow),(void *&)ptr) && pAttrMap->Lookup( CC_RUNTIME_CLASS(AttrEndArrow),(void *&)ptr) && pAttrMap->Lookup( CC_RUNTIME_CLASS(AttrJoinType),(void *&)ptr) && pAttrMap->Lookup( CC_RUNTIME_CLASS(AttrStartCap),(void *&)ptr) && pAttrMap->Lookup( CC_RUNTIME_CLASS(AttrDashPattern),(void *&)ptr) ); } BOOL MakeLineAttributeCompleteForContouring(Node* Context, Node* NodeToModify, CCAttrMap* pAttrMap) { AttrLineWidth * pAttrLineWidth = NULL; AttrStartArrow * pAttrStartArrow = NULL; AttrEndArrow * pAttrEndArrow = NULL; AttrJoinType * pAttrJoinType = NULL; AttrStartCap * pAttrStartCap = NULL; AttrDashPattern * pAttrDashPattern = NULL; AttrStrokeColour* pAttrStrokeColour = NULL; // get all the attributes out of the attribute map pAttrMap->Lookup( CC_RUNTIME_CLASS(AttrLineWidth),(void *&)pAttrLineWidth); if(pAttrLineWidth) pAttrLineWidth->AttachNode(Context,FIRSTCHILD,FALSE,FALSE); pAttrMap->Lookup( CC_RUNTIME_CLASS(AttrStartArrow),(void *&)pAttrStartArrow); if (pAttrStartArrow) pAttrStartArrow->AttachNode(Context,FIRSTCHILD,FALSE,FALSE); pAttrMap->Lookup( CC_RUNTIME_CLASS(AttrEndArrow),(void *&)pAttrEndArrow); if (pAttrEndArrow) pAttrEndArrow->AttachNode(Context,FIRSTCHILD,FALSE,FALSE); pAttrMap->Lookup( CC_RUNTIME_CLASS(AttrJoinType),(void *&)pAttrJoinType); if(pAttrJoinType) pAttrJoinType->AttachNode(Context,FIRSTCHILD,FALSE,FALSE); pAttrMap->Lookup( CC_RUNTIME_CLASS(AttrStartCap),(void *&)pAttrStartCap); if(pAttrStartCap) pAttrStartCap->AttachNode(Context,FIRSTCHILD,FALSE,FALSE); pAttrMap->Lookup( CC_RUNTIME_CLASS(AttrDashPattern),(void *&)pAttrDashPattern); if(pAttrDashPattern) pAttrDashPattern->AttachNode(Context,FIRSTCHILD,FALSE,FALSE); pAttrMap->Lookup( CC_RUNTIME_CLASS(AttrStrokeColour),(void *&)pAttrStrokeColour); if(pAttrStrokeColour) pAttrStrokeColour->AttachNode(Context,FIRSTCHILD,FALSE,FALSE); if( pAttrLineWidth && pAttrStartArrow && pAttrEndArrow && pAttrJoinType && pAttrStartCap && pAttrDashPattern && pAttrStrokeColour) { Node* pLastChild = Context->FindLastChild(); NodeToModify->AttachNode(pLastChild,NEXT,FALSE,FALSE); // dynamic attrs are copies, so we are free to modify them return TRUE; } else { return FALSE; } } ///////////////////////////////////////////////////////////////////////////////////////////// // // AttributeValue classes // ///////////////////////////////////////////////////////////////////////////////////////////// /******************************************************************************************** > FeatherAttrValue:: Author: Ilan_Copelyn (Xara Group Ltd) Created: 24/01/2000 Purpose: This constructor is called in three different scenarios:- 1 - to initialise attrvalue tables 2 - to create a new feather to apply to a selected node by an OpFeather operation 3 - in the process of creating a simple copy of an exisiting Feather attr ie. either a default attr from the attr table (to attach to a new node) or a clone of an AttrFeather __node__ (ie as part of OpClone) The initialisation process will always create 0 width default feather attributes and hence will always attach 0 width feathers as the documents base feather attr. SeeAlso: ********************************************************************************************/ FeatherAttrValue::FeatherAttrValue() { // m_Profile = CProfileBiasGain() // Defaults to this m_GapSize = DEFAULT_GAP_TOLERANCE_MP; m_FeatherSize = 0; // Document base Feather has 0 feather size. // The doc base feather is created when camelot // initialises a new document. // The default feather applied to each new object // is also setup by this constructor. m_pOuterContour = NULL; m_bCached = FALSE; } /******************************************************************************************** > FeatherAttrValue:: Author: Ilan_Copelyn (Xara Group Ltd) Created: 24/01/2000 Purpose: Free outer contour path if we have one SeeAlso: ********************************************************************************************/ FeatherAttrValue::~FeatherAttrValue() { // only m_pOuterContour may be left because it is cached if (m_pOuterContour != NULL) { delete m_pOuterContour; m_pOuterContour = NULL; } } /******************************************************************************************** > FeatherAttrValue:: Author: Ilan_Copelyn (Xara Group Ltd) Created: 24/01/2000 Purpose: SeeAlso: ********************************************************************************************/ BOOL FeatherAttrValue::Init() { // Default to no feathering FeatherAttrValue *pAttr = new FeatherAttrValue; if (pAttr==NULL) // error message has already been set by new return FALSE; pAttr->SetDefault(); UINT32 AttrID = AttributeManager::RegisterDefaultAttribute(CC_RUNTIME_CLASS(NodeRenderableInk), pAttr); ERROR2IF(AttrID == ATTR_BAD_ID, FALSE, "Bad ID when Initialising FeatherAttrValue"); ERROR2IF(AttrID != ATTR_FEATHER, FALSE, "Incorrect ID for FeatherAttrValue"); return TRUE; } /******************************************************************************************** > FeatherAttrValue:: Author: Ilan_Copelyn (Xara Group Ltd) Created: 24/01/2000 Purpose: SeeAlso: TODO: Determine whether to copy bitmap or just pointer to it ********************************************************************************************/ void FeatherAttrValue::SimpleCopy(AttributeValue *pVal) { ERROR3IF(!pVal->IS_KIND_OF(FeatherAttrValue), "Incorrect runtime class passed to FeatherAttrValue::SimpleCopy"); FeatherAttrValue* pFthr = (FeatherAttrValue*) pVal; m_Profile = pFthr->m_Profile; SetFeatherSize(pFthr->GetFeatherSize()); m_GapSize = pFthr->m_GapSize; // NOTE! Don't copy m_outerContour because callers assume that this will // not be copied and rely on it being regenerated on demand. Urgh! // Remember to copy or blank cached path data without leaking memory... if (m_pOuterContour) delete m_pOuterContour; m_pOuterContour = NULL; /* Path* pSrcPath = pFthr->GetOuterContour(); if (pSrcPath) { m_pOuterContour = new Path(); m_pOuterContour->Initialise(pSrcPath->GetNumCoords()); m_pOuterContour->CopyPathDataFrom(pSrcPath); } */ // preserves default flag to avoid confusion when copying CCAttrMaps GeometryLinkedAttrValue::SimpleCopy(pVal); } /******************************************************************************************** > FeatherAttrValue:: Author: Ilan_Copelyn (Xara Group Ltd) Created: 24/01/2000 Purpose: SeeAlso: ********************************************************************************************/ NodeAttribute* FeatherAttrValue::MakeNode() { AttrFeather* pNode = new AttrFeather(); pNode->Value.SimpleCopy(this); return pNode; } /******************************************************************************************** > FeatherAttrValue:: Author: Ilan_Copelyn (Xara Group Ltd) Created: 24/01/2000 Purpose: SeeAlso: ********************************************************************************************/ BOOL FeatherAttrValue::Blend(BlendAttrParam* pBlendParam) { // First get the fill that we are blending to FeatherAttrValue* OtherFthr = (FeatherAttrValue*)pBlendParam->GetOtherAttrVal(); if(IsDefaultFlagSet() && OtherFthr->IsDefaultFlagSet()) { // no blending required // pBlendParam->SetBlendedAttrVal(NULL); return FALSE; } FeatherAttrValue* pNewAttr = new FeatherAttrValue; if (pNewAttr == NULL) { // Fail if we couldn't create the new fill pBlendParam->SetBlendedAttrVal(NULL); return FALSE; } // and what point along the blend we are at double Ratio = pBlendParam->GetBlendRatio(); // blend m_pFeatherWidth MILLIPOINT Start = GetFeatherSize(); MILLIPOINT End = OtherFthr->GetFeatherSize(); pNewAttr->SetFeatherSize( (MILLIPOINT) ( Start + Ratio*(End - Start) ) ); // blend m_Profile CProfileBiasGain BiasGain = m_Profile; // the first fills profile CProfileBiasGain OtherBiasGain = OtherFthr->m_Profile; // the other fills profile if (!(BiasGain == OtherBiasGain)) { /* From other blend Profiles code double InvRatio = 1 - Ratio; double BlendedBias = (BiasGain.GetBias () * InvRatio) + (OtherBiasGain.GetBias () * Ratio); double BlendedGain = (BiasGain.GetGain () * InvRatio) + (OtherBiasGain.GetGain () * Ratio); */ double StartBias = m_Profile.GetBias(); double EndBias = OtherBiasGain.GetBias(); double StartGain = m_Profile.GetGain(); double EndGain = OtherBiasGain.GetGain(); double BlendedBias = StartBias + ((EndBias - StartBias) * Ratio); double BlendedGain = StartGain + ((EndGain - StartGain) * Ratio); pNewAttr->m_Profile.SetBias (BlendedBias); pNewAttr->m_Profile.SetGain (BlendedGain); } // Set the new fill as the blended attribute pBlendParam->SetBlendedAttrVal(pNewAttr); return TRUE; } /******************************************************************************************** > FeatherAttrValue:: Author: Ilan_Copelyn (Xara Group Ltd) Created: 24/01/2000 Purpose: Put the GRenderRegion offscreen rendering system into play (provided we're dealing with a real feather attribute and not just the base doc attr). SeeAlso: GRenderRegion::SetFeathering, FeatherAttrValue::OffscreenRenderingCompleted() ********************************************************************************************/ void FeatherAttrValue::Render(RenderRegion *pRender, BOOL Temp) { // TRACEUSER( "Gerry", _T("Rendering feather attribute of %d (0x%08x)\n"), GetFeatherSize(), this); // Karim 20/07/2000 // currently, we only generate an offscreen bitmap for GRenderRegions, although we are // 'rendered' into all RR's. therefore we must quit quietly if pRender is not a GRR. // Gerry 12/01/2005 // This is no longer true. We need to render this into all types of render region // so the code has been removed // if (!pRender->IS_KIND_OF(GRenderRegion)) // return; // if the feather size is zero, then quit now - no feathering is necessary. MILLIPOINT mpFeather = GetFeatherSize(); if (mpFeather == 0) return; // ... and shout out if the feather size is negative - something is WRONG! else if (mpFeather < 0) { ERROR3("Negative feather size! What's going on?"); return; } // you would place code here if you need to affect currentAttrs table // also alloc any mem you need for the duration of the rendering cycle OffscreenAttrValue::Render(pRender, Temp); } /******************************************************************************************** > FeatherAttrValue:: Author: Ilan_Copelyn (Xara Group Ltd) Created: 24/01/2000 Purpose: SeeAlso: GRenderRegion::RestoreGDrawBitmap ********************************************************************************************/ void FeatherAttrValue::Restore(RenderRegion *pRegion, BOOL Temp) { //TRACEUSER( "Phil", _T("Restoring feather attribute of %d (0x%08x)\n"), GetFeatherSize(), this); // NB this function doesn't need to be implemented - the base class handles everything // Just showing you where to put extra code OffscreenAttrValue::Restore(pRegion, Temp); } /******************************************************************************************** > FeatherAttrValue:: Author: Ilan_Copelyn (Xara Group Ltd) Created: 24/01/2000 Purpose: The random nature of generation of redraw cliprect coordinates means that we may sometimes be asked to redraw a really thin sliver of the feathered object. This is a problem for the convolution code if the sliver is thinner (in height or width) than 2 pixels. SeeAlso: GRenderRegion::RestoreGDrawBitmap ********************************************************************************************/ void FeatherAttrValue::GetMinimumOffscreenBmpDimensions(UINT32* RequiredWidth, UINT32* RequiredHeight) { *RequiredWidth = 2; *RequiredHeight = 2; } /******************************************************************************************** > FeatherAttrValue:: Author: Ilan_Copelyn (Xara Group Ltd) Created: 24/01/2000 Purpose: To allow the offscreen attribute to perform whatever processing it requires on the bitmap copy of the subtree rooted at m_pLinkedNode and then draw into the partially rendered bitmap of the predecessors of m_pLinkedNode Notes: 1) This function gets called when the RR exits the context under which the attribute was Render()ed (ie after completeing a DrawPath("this->m_pLinkedNode->Path"). 2) The offscreen rendering system (implemented in GRenderRegion) has already restored the clip rect and GDraw bitmap and matrix state of the previous offscreen attribute. This function is actually being called by the RenderRegion in the process of Restore()ing this previous offscreen attribute (ie the process of taking the attribute which is sitting on top of the attribute context stack, and restoring it to the CurrentAttrs table). So at this point the GRenderRegion and GDraw context are back the way they were before "this" offscreen attribute was Render()ed. 3) m_pOffscreenBmpBits points to the buffer holding the fully rendered bitmap of the subtree rooted at m_pLinkedNode 4) Any transparency attribute existing at the same level as this offscreen attr (ie attached to "this->m_pLinkedNode") will already have affected the bitmap in "this->m_pOffscreenBmpBits". 5) You don't need to delete the bitmap pointers UNLESS you installed "this" attribute as a non-temporary attribute in the Render() function (ie SetFeathering(this, FALSE..)). If it is a temporary attribute (which is the default) then the deconstructor will handle the deallocation of the memory pointed to by m_pOffscreenBmpBits & m_pOffscreenBmpInfo SeeAlso: GRenderRegion::RestoreFeathering(); ~FeatherAttrValue(); RenderRegion::RestoreAttribute() FeatherAttrValue::Render(), GRenderRegion::SetFeathering() ********************************************************************************************/ void FeatherAttrValue::OffscreenRenderingCompleted(RenderRegion *pRender, LPBITMAPINFO lpBitmapInfo, LPBYTE lpBits, DocRect BitmapRect) { // TRACEUSER( "Gerry", _T("Feather attribute OffscreenRenderingCompleted (0x%08x)\n"), this); // TRACEUSER( "Phil", _T("Feather attribute OffscreenRenderingCompleted (0x%08x)\n"), this); // TEMP BODGE m_OffBB = BitmapRect; // Karim 20/07/2000 // currently, we only generate an offscreen bitmap for GRenderRegions, although we are // 'rendered' into all RR's. therefore we must quit quietly if pRender is not a GRR. if (!pRender->IS_KIND_OF(GRenderRegion)) return; GRenderRegion* pGRR = (GRenderRegion*)pRender; RRCaps Caps; pGRR->GetRenderRegionCaps(&Caps); if (!Caps.DoesRegionDoAll()) return; // Karim 19/10/2000 // The offscreen rendering may possibly fail, // in which case all we can do now is quit. if (lpBitmapInfo == NULL || lpBits == NULL) { // ERROR3("FeatherAttrValue::OffscreenRenderingCompleted; don't have any bitmap info!"); return; } double fScaledPixelWidth = pGRR->GetScaledPixelWidthDouble(); // TRACEUSER( "Gerry", _T("ScaledPixelWidth = %f\n"), fScaledPixelWidth); double pixBlurDiameter = GetFeatherPixelSize(fScaledPixelWidth); // TRACEUSER( "Gerry", _T("BlurDiameter = %f\n"), pixBlurDiameter); // Setup path to draw Path* pPathToDraw = GetVisibleBoundingPath(); if (pPathToDraw == NULL) { ERROR3("FeatherAttrValue::OffscreenRenderingCompleted; Couldn't create path!"); return; } ///////////////////////////////////////////////////////////////////////////////////////// // Create the bitmap fill containing the offscreen rendition of the objects to feather. ///////////////////////////////////////////////////////////////////////////////////////// CWxBitmap *wBitmap = NULL; KernelBitmap *OffscreenBitmap = NULL; // TRACEUSER( "Gerry", _T("Offscreen size = (%d, %d)\n"), pOffBmpHdr->biWidth, pOffBmpHdr->biHeight); // Create a Kernel bitmap from the bmp data. wBitmap = new CWxBitmap(lpBitmapInfo, lpBits); OffscreenBitmap = new KernelBitmap(wBitmap,TRUE); // Setup the bitmap fill. // Since the offscreen bitmap has been plotted using the full rendering matrix // which may include a rotation during printing, we must now force GDraw to // render it using the same rotation (see GRenderRegion::AllocateOffScreenState) BitmapFillAttribute BmpFill; ANGLE rot; Matrix m = pGRR->GetMatrix(); m.Decompose(NULL, NULL, &rot, NULL, NULL); if (rot==0) // CreateBitmapFill(OffscreenBitmap, &m_OffBB, &BmpFill); CreateBitmapFill(OffscreenBitmap, &BitmapRect, &BmpFill); else // CreateBitmapFillRot90(OffscreenBitmap, &m_OffBB, &BmpFill); CreateBitmapFillRot90(OffscreenBitmap, &BitmapRect, &BmpFill); // No other rotations are currently possible... // DEBUG: add offscreen bmp to bitmap gallery #ifdef DEBUG_ATTACH_OFFSCREEN_BMP OffscreenBitmap->AttachDebugCopyToCurrentDocument("Offscreen Bitmap"); #endif ///////////////////////////////////////////////////////////////////////////////////////// // Create the feather bitmap transparency mask. ///////////////////////////////////////////////////////////////////////////////////////// KernelBitmap* FeatherBitmap = CreateFeatherBitmap(pGRR, pixBlurDiameter); BitmapTranspFillAttribute BmpTranspFill; if (FeatherBitmap != NULL) { CreateBitmapTranspFill(FeatherBitmap, &m_drFeatherBB, &BmpTranspFill); } ///////////////////////////////////////////////////////////////////////////////////////// // Render the bitmaps together for the final feathered result. // // If we had any problems creating the feather bitmap transparency, or if our debug // rendering is enabled, then we try to fail gracefully by rendering just the // offscreen bitmap. Note that this bitmap will only have an alpha channel if some of // the feathered objects require it, and will have a white background otherwise. ///////////////////////////////////////////////////////////////////////////////////////// pGRR->SaveContext(); // DEBUG: see note above. #ifndef DEBUG_RENDER_OFFSCREEN_BMP_WITHOUT_MASK if (FeatherBitmap != NULL) pGRR->RenderBitmapWithTransparency(pPathToDraw, &BmpFill, &BmpTranspFill); #else pGRR->RenderBitmapWithTransparency(pPathToDraw, &BmpFill, NULL); #endif pGRR->RestoreContext(); ///////////////////////////////////////////////////////////////////////////////////////// // Clean up all our data structures. ///////////////////////////////////////////////////////////////////////////////////////// if (FeatherBitmap != NULL) { delete FeatherBitmap; FeatherBitmap = NULL; } if (pPathToDraw != NULL) { delete pPathToDraw; pPathToDraw = NULL; } FreeDIB(lpBitmapInfo, lpBits, NULL, FALSE); // makes ~CWxBitmap() think that bmp HasBeenDeleted(). wBitmap->BMBytes = ( (CWxBitmap *)OILBitmap::Default )->BMBytes; delete OffscreenBitmap; } /******************************************************************************************** > virtual BOOL FeatherAttrValue::CreateFeatherTransp( GRenderRegion* pGRR, Path* pPath, double dPixelWidth, BitmapTranspFillAttribute** ppFeatherTransp, KernelBitmap** ppFeatherBitmap ) Author: Phil_Martin (Xara Group Ltd) Created: 05/08/2005 Inputs: pPath - Pointer to path to be feathered dPixelWidth - Pixel width in millipoints Outputs: pFeatherTransp - Pointer to transparency attribute for feathering pFeatherBitmap - POinter to feather bitmap used in transparency attribute Returns: TRUE if it worked FALSE otherwise Purpose: Create the feather transparency attribute using the supplied path /without/ reference to any surrounding nodes in the tree Used by NodeBitmap::GetDirectBitmap ********************************************************************************************/ BOOL FeatherAttrValue::CreateFeatherTransp( GRenderRegion* pGRR, Path* pPath, double dPixelWidth, BitmapTranspFillAttribute** ppFeatherTransp, KernelBitmap** ppFeatherBitmap ) { double dpixBlurDiameter = GetFeatherPixelSize(dPixelWidth); LPBYTE pFeatherBits = NULL; LPBITMAPINFO pFeatherInfo = NULL; KernelBitmap* kbmpFeather = NULL; DocRect rectBounds = pPath->GetBoundingRect(); // UINT32 pixWidth = UINT32(floor( rectBounds.Width() / dPixelWidth + 0.5 )); // UINT32 pixHeight = UINT32(floor( rectBounds.Height() / dPixelWidth + 0.5 )); // ------------------------------------------------------------------- // First make the silhouette bitmap // create the silhouette bitmap. m_OffBB = rectBounds; m_GapSize = 0; LPBYTE pSilhouetteBits; LPBITMAPINFO pSilhouetteInfo = CreateSilhouetteBitmap(pGRR, &pSilhouetteBits, pPath); if (pSilhouetteInfo==NULL || pSilhouetteBits==NULL) return FALSE; // ------------------------------------------------------------------- // Next blur the silhouette bitmap to obtain the feather bitmap. pFeatherInfo = CBitmapShadow::Feather8BppBitmap( dpixBlurDiameter, pSilhouetteInfo, pSilhouetteBits, &pFeatherBits ); // If Feather8BPP returned the same bitmap, clear silhouette pointers so we don't // deallocate the bitmap at the end of this function... if (pFeatherInfo == pSilhouetteInfo) { pSilhouetteInfo = NULL; pSilhouetteBits = NULL; } if (pFeatherInfo != NULL && pFeatherBits != NULL) { CWxBitmap* wbmpFeather = new CWxBitmap(pFeatherInfo, pFeatherBits); kbmpFeather = new KernelBitmap(wbmpFeather, TRUE); SetupFeatherBitmapPalette(kbmpFeather); *ppFeatherBitmap = kbmpFeather; } // We don't need the silhouette bitmap any more if (pSilhouetteInfo != NULL && pSilhouetteBits != NULL) FreeDIB(pSilhouetteInfo, pSilhouetteBits); // If we failed to create the blurred bitmap then exit now if (*ppFeatherBitmap==NULL) return FALSE; // ---------------------------------------------------------------------- // Now make the transparency attribute to carry the feather bitmap *ppFeatherTransp = new BitmapTranspFillAttribute; if (*ppFeatherTransp==NULL) { delete *ppFeatherBitmap; *ppFeatherBitmap = NULL; return FALSE; } CreateBitmapTranspFill(*ppFeatherBitmap, &rectBounds, *ppFeatherTransp); return TRUE; } /******************************************************************************************** > virtual BOOL FeatherAttrValue::DoesOffscreenBmpRequireTransp(GRenderRegion* pGRR) Author: Karim_MacDonald (Xara Group Ltd) Created: 19/11/2000 Inputs: pGRR the RR which the feather will be rendered into. Purpose: Override of OffscreenAttrValue virtual function. Feathers always require a transparent bitmap if their feather size is less than a certain value, otherwise they use the default implementation. This prevents white artifacts on small feather sizes. See also: CreateSilhouetteBitmap, OffscreenAttrValue::DoesOffscreenBmpRequireTransp. ********************************************************************************************/ BOOL FeatherAttrValue::DoesOffscreenBmpRequireTransp(GRenderRegion* pGRR) { // In conjunction with DoesOffscreenBMPCaptureBackground this function should // now return FALSE always so that the offscreen bitmap does not attempt to // capture transparency info but rather captures the RESULTS of transparency... if (DoesOffscreenBMPCaptureBackground()) return FALSE; if (pGRR == NULL) { ERROR3("FeatherAttrValue::DoesOffscreenBmpRequireTransp; Invalid params!"); return FALSE; } const double ScaledPixelWidth = pGRR->GetScaledPixelWidthDouble(); const double FeatherPixelSize = GetFeatherPixelSize(ScaledPixelWidth); return (FeatherPixelSize < 2*MIN_BLUR_DIAMETER) ? TRUE : OffscreenAttrValue::DoesOffscreenBmpRequireTransp(pGRR) ; } /******************************************************************************************** > KernelBitmap* FeatherAttrValue::CreateFeatherBitmap(GRenderRegion* pGRR, double pixBlurDiameter) Author: Karim_MacDonald (Xara Group Ltd) Created: 19 September 2000 Inputs: pGRR the GRenderRegion to base the silhouette bitmap on. pixBlurDiameter feather blur width, in pixels (can be non-integer). Returns: A ptr to a KernelBitmap containing the transparency mask part of the feather. Purpose: Create the feather bitmap-transparency mask. See Also: CreateSilhouetteBitmap(). ********************************************************************************************/ KernelBitmap* FeatherAttrValue::CreateFeatherBitmap(GRenderRegion* pGRR, double pixBlurDiameter) { // will contain the feather bitmap transparency mask. LPBYTE pFeatherBits = NULL; LPBITMAPINFO pFeatherInfo = NULL; KernelBitmap* kbmpFeather = NULL; // create the silhouette bitmap. LPBYTE pSilhouetteBits; LPBITMAPINFO pSilhouetteInfo = CreateSilhouetteBitmap(pGRR, &pSilhouetteBits); if (pSilhouetteInfo != NULL && pSilhouetteBits != NULL) { // TRACEUSER( "Gerry", _T("Silhouette size = (%d, %d)\n"), pSilhouetteInfo->bmiHeader.biWidth, pSilhouetteInfo->bmiHeader.biHeight); // blur the silhouette bitmap to obtain the feather bitmap. pFeatherInfo = CBitmapShadow::Feather8BppBitmap( pixBlurDiameter, pSilhouetteInfo, pSilhouetteBits, &pFeatherBits ); // If Feather8BPP returned the same bitmap, clear silhouette pointers so we don't // deallocate the bitmap at the end of this function... if (pFeatherInfo == pSilhouetteInfo) { pSilhouetteInfo = NULL; pSilhouetteBits = NULL; } if (pFeatherInfo != NULL && pFeatherBits != NULL) { // TRACEUSER( "Gerry", _T("Feather size = (%d, %d)\n"), pFeatherInfo->bmiHeader.biWidth, pFeatherInfo->bmiHeader.biHeight); CWxBitmap* wbmpFeather = new CWxBitmap(pFeatherInfo, pFeatherBits); kbmpFeather = new KernelBitmap(wbmpFeather, TRUE); SetupFeatherBitmapPalette(kbmpFeather); } } ///////////////////////////////////////////////////////////////////////////////////////////// // // DEBUG: add copies of the silhouette and feather bitmaps to the bitmap gallery. // #ifdef DEBUG_ATTACH_SILHOUETTE_BMP if (pSilhouetteInfo != NULL && pSilhouetteBits != NULL) { CWxBitmap* wbmpSilhouette = new CWxBitmap(pSilhouetteInfo, pSilhouetteBits); KernelBitmap* kbmpSilhouette = new KernelBitmap(wbmpSilhouette, TRUE); SetupFeatherBitmapPalette(kbmpSilhouette); kbmpSilhouette->AttachDebugCopyToCurrentDocument("Silhouette Bitmap"); wbmpSilhouette->BMBytes = ((CWxBitmap*)OILBitmap::Default)->BMBytes; delete kbmpSilhouette; } #endif #ifdef DEBUG_ATTACH_FEATHER_BMP if (kbmpFeather != NULL) { kbmpFeather->AttachDebugCopyToCurrentDocument("Feather Mask Bitmap"); } #endif // ///////////////////////////////////////////////////////////////////////////////////////////// // Silhouette bitmap does not use limited mem manager, so free its DIB ourself. if (pSilhouetteInfo != NULL && pSilhouetteBits != NULL) FreeDIB(pSilhouetteInfo, pSilhouetteBits, NULL, FALSE); return kbmpFeather; } /******************************************************************************************** > FeatherAttrValue:: Author: Ilan_Copelyn (Xara Group Ltd) Created: 03/04/2000 Inputs: ppBmpBits = pointer to a LPBYTE pointer which we should allocate memory for Outputs: ppBmpBits = monochrome bitmap of the outline of the feathered object(s) Returns: BitmapInfo structure describing the mono bitmap @ **ppBmpBits Purpose: Create a monochrome image of the accumulated path, using a white outline so the resulting image matches the inner contour of the path. The thickness of the line is determined by the feather width. NB line width is centred about the path outline. Hence if we require the inner contour to be featherwidth pixels inside the outline, then need to use a line width of 2*FeatherSize NB2 Feathersize is moderated to max 40 pix SeeAlso: FeatherAttrValue::GetFeatherSize,OffscreenRenderingCompleted,Render PathAccumulatorPathProcessor OffscreenRenderer TechnicalNotes: leverages the offscreen rendering system to get the active renderegion to render the path into a new bitmap and then restore the renderregion to the way it was. I order to avoid DWORD aligning overhead in the convolution code we always render into bitmaps which are DWORD aligned. This means we need to be careful when creating a transparency attribute to hold the mask as we must use the aligned co-ords, not m_OffBB otherwise we will introduce scaling. NB we do this by drawing more pixels on the right hand border to account for the alignment. NB NB NB Non-technical notes: these notes are out of date so ignore! will fix later. **********************************************************************************************/ LPBITMAPINFO FeatherAttrValue::CreateSilhouetteBitmap(GRenderRegion* GRR, LPBYTE* ppBmpBits, Path* pOutline) { // ERROR3IF(m_pOffscreenBmpInfo == NULL, // "FeatherAttrValue::CreateSilhouetteBitmap; No offscreen bitmap info."); // generate a contour of m_GapSize around the path of our linked node, to // eradicate any small gaps which would otherwise be feathered. if (pOutline==NULL) { if (m_pOuterContour == NULL) GenerateOuterContour(); pOutline = m_pOuterContour; } // if we couldn't generate our outer contour, or it had no points in it, // don't try to produce a feather bitmap. if (pOutline == NULL || pOutline->GetNumCoords() == 0) { *ppBmpBits = NULL; return NULL; } ///////////////////////////////////////////////////////////////////////////////////////// // - Work out the appropriate size silhouette bitmap by inflating the invalid // offscreen rectangle by the blur *radius* (half the feather size). // - If the blur radius is above the maximum we can do, then we work out *how* // much bigger it is and render the silhouette bitmap appropriately scaled down. // - The final feather bitmap will then scale up to fill the offscreen rect, // making it look like we actually used the requested blur radius. const double ScaledPixelWidth = GRR->GetScaledPixelWidthDouble(); const double FeatherMPSize = GetFeatherSize(); const double FeatherPixelSize = GetFeatherPixelSize(ScaledPixelWidth); double FeatherScaleFactor = 1; DocRect drSilhouetteBounds = m_OffBB; m_drFeatherBB = drSilhouetteBounds; if (FeatherPixelSize >= MIN_BLUR_DIAMETER) { // requested blur diameter = BlurPixDiam pixels. // actual blur we can do = FeatherPixelSize pixels. double BlurPixDiam = (ScaledPixelWidth > 0) ? (FeatherMPSize / ScaledPixelWidth) : 0; if (BlurPixDiam > FeatherPixelSize) { FeatherScaleFactor = BlurPixDiam / FeatherPixelSize; // Scale the silhouette around its centre by the feather scale factor. // This is so that the rendered silhouette bitmap is has a width of // at least (2 * FeatherRadius + 1) pixels. double BoundsSF = (FeatherScaleFactor - 1.0) / 2.0; UINT32 nWidth = (UINT32)(0.5 + (double)drSilhouetteBounds.Width() * BoundsSF); UINT32 nHeight = (UINT32)(0.5 + (double)drSilhouetteBounds.Height() * BoundsSF); drSilhouetteBounds.Inflate(nWidth, nHeight); m_drFeatherBB = drSilhouetteBounds; } // Inflate the silhouette bounds by FeatherRadius pixels around each edge. // This will shrink down to the correct bounds when blurred. const UINT32 nBlur = (UINT32)(BlurPixDiam + 0.5); UINT32 nHiInflate = (nBlur - 1) / 2; UINT32 nLoInflate = (nBlur - 1) - nHiInflate; nHiInflate = (UINT32)(ScaledPixelWidth * (double)nHiInflate); nLoInflate = (UINT32)(ScaledPixelWidth * (double)nLoInflate); drSilhouetteBounds.hi.x += nHiInflate; drSilhouetteBounds.hi.y += nHiInflate; drSilhouetteBounds.lo.x -= nLoInflate; drSilhouetteBounds.lo.y -= nLoInflate; // Don't allow further code to try to make a zero pixel silhouette bitmap if (drSilhouetteBounds.Width()GetRenderRegion(); pLocalGRR->GetDrawContext()->SetHintingFlag(FALSE); // ask the bitmap shadower if it the final feather bitmap will be offset diagonally // by half a pixel. if this is the case then we must compensate, by rendering // the silhouette bitmap at a half-pixel offset in the other direction. if (CBitmapShadow::BlurringWillOffsetBitmap(FeatherPixelSize)) pLocalGRR->OffsetByHalfPixel(); //////////////////////////////////////////////////////////////////// // Draw into new RenderRegion which GRenderRegionWrapper has created //////////////////////////////////////////////////////////////////// pLocalGRR->SaveContext(); // Initialise our new render-region: // 1. Render in solid black with solid white line-widths. // 1a. Line colour now graduates from black -> off-white over 0->2*MIN_BLUR_DIAMETER. // 2. Line width is half feather size + contour gap size, on either side. // 3. No arrowheads/dashed lines/fancy join types. // 4. High quality rendering, so we get anti-aliased lines. pLocalGRR->SetFillColour(COLOUR_BLACK); if (FeatherPixelSize < (2*MIN_BLUR_DIAMETER)) { double RampValue = FeatherPixelSize / (2*MIN_BLUR_DIAMETER); ColourValue GreyVal(0.8 * RampValue); DocColour FeatherGrey(GreyVal, GreyVal, GreyVal); pLocalGRR->SetLineColour(FeatherGrey); } else pLocalGRR->SetLineColour(COLOUR_WHITE); pLocalGRR->SetJoinType(RoundJoin); pLocalGRR->SetStartArrow(SA_NULLARROW); pLocalGRR->SetEndArrow(SA_NULLARROW); pLocalGRR->SetDashPattern(SD_SOLID); MILLIPOINT RequiredLineWidth = (MILLIPOINT)(FeatherMPSize + 2*m_GapSize); pLocalGRR->SetLineWidth(RequiredLineWidth); FlatTranspFillAttribute NoFillTransp(TT_NoTranspType); pLocalGRR->SetTranspFillGeometry(&NoFillTransp, FALSE); StrokeTranspAttribute NoLineTransp(TT_NoTranspType); pLocalGRR->SetLineTransp(&NoLineTransp, FALSE); Quality AntiAliasQuality; AntiAliasQuality.SetQuality(Quality::QualityMax); QualityAttribute AntiAliasQualityAttr(AntiAliasQuality); pLocalGRR->SetQuality(&AntiAliasQualityAttr, FALSE); // render our contoured silhouette path into the new RR. pOutline->IsFilled = TRUE; pOutline->IsStroked = TRUE; pLocalGRR->DrawPath(pOutline); pLocalGRR->RestoreContext(); //////////////////////////////////////////////////////////////////// // Extract bitmap from GRenderRegionWrapper and restore GDraw state //////////////////////////////////////////////////////////////////// LPBITMAPINFO tmpBI; pRendWrap->GetBitmapPointers(&tmpBI, ppBmpBits); pRendWrap->RestorePreviousRendererState(); return tmpBI; } /******************************************************************************************** > FeatherAttrValue:: Author: Ilan_Copelyn (Xara Group Ltd) Created: 24/01/2000 Purpose: Setup the feather bitmap's palette using the bias/gain profile SeeAlso: **********************************************************************************************/ void FeatherAttrValue::SetupFeatherBitmapPalette(KernelBitmap* pFeatherBMP) { LPRGBQUAD pPalette = pFeatherBMP->GetPaletteForBitmap(); // Using linear palette // For some reason we have to initialise the palette even though the rendering // code sets up the palette using the profile which is attached to the bitmap // transparency attribute which contains the feather bitmap mask. for (INT32 i = 0; i < 256; i ++) { pPalette[i].rgbRed = pPalette[i].rgbBlue = pPalette[i].rgbGreen = (BYTE)i; pPalette[i].rgbReserved = 0x00; } // Mark this bitmap as being greyscale to avoid re-conversion by GetGreyscaleVersion pFeatherBMP->SetAsGreyscale(); } /******************************************************************************************** > FeatherAttrValue:: Author: Ilan_Copelyn (Xara Group Ltd) Created: 24/01/2000 Purpose: SeeAlso: ********************************************************************************************/ void FeatherAttrValue::CreateBitmapFill(KernelBitmap* pBitmap, DocRect* dr, BitmapFillAttribute* BmpFill) { // Bitmap fill is the size of the current offscreen bitmap DocCoord StartPoint(dr->lo); DocCoord EndPoint(dr->hi.x,dr->lo.y); DocCoord EndPoint2(dr->lo.x,dr->hi.y); DocCoord EndPoint3(dr->hi); BmpFill->SetStartPoint(&StartPoint); BmpFill->SetEndPoint(&EndPoint); BmpFill->SetEndPoint2(&EndPoint2); BmpFill->SetEndPoint3(&EndPoint3); BmpFill->GetBitmapRef()->SetBitmap(pBitmap); } /******************************************************************************************** > FeatherAttrValue:: Author: Ilan_Copelyn (Xara Group Ltd) Created: 24/01/2000 Purpose: SeeAlso: ********************************************************************************************/ void FeatherAttrValue::CreateBitmapFillRot90(KernelBitmap* pBitmap, DocRect* dr, BitmapFillAttribute* BmpFill) { // Bitmap fill is the size of the current offscreen bitmap DocCoord StartPoint(dr->hi.x, dr->lo.y); DocCoord EndPoint(dr->hi); DocCoord EndPoint2(dr->lo); DocCoord EndPoint3(dr->lo.x,dr->hi.y); BmpFill->SetStartPoint(&StartPoint); BmpFill->SetEndPoint(&EndPoint); BmpFill->SetEndPoint2(&EndPoint2); BmpFill->SetEndPoint3(&EndPoint3); BmpFill->GetBitmapRef()->SetBitmap(pBitmap); } void FeatherAttrValue::CreateBitmapTranspFill(KernelBitmap* pFeather, DocRect* dr, BitmapTranspFillAttribute* BmpTranspFill) { // Transp fill is same size as viewable part of the bitmap DocCoord StartPoint(dr->lo); DocCoord EndPoint(dr->hi.x,dr->lo.y); DocCoord EndPoint2(dr->lo.x,dr->hi.y); DocCoord EndPoint3(dr->hi); BmpTranspFill->SetStartPoint(&StartPoint); BmpTranspFill->SetEndPoint(&EndPoint); BmpTranspFill->SetEndPoint2(&EndPoint2); BmpTranspFill->SetEndPoint3(&EndPoint3); BmpTranspFill->GetBitmapRef()->SetBitmap(pFeather); BmpTranspFill->Transp = 0; BmpTranspFill->EndTransp = 255; m_Profile.SetIsAFeatherProfile (TRUE); // enable extra processing on the profile BmpTranspFill->SetProfile(m_Profile); } Path* FeatherAttrValue::GetVisibleBoundingPath() { DocRect dr = GetOffscreenBoundingRect(); Path* p = new Path(); if (p != NULL) { p->Initialise(5); p->CreatePathFromDocRect(&dr); } return p; } /******************************************************************************************** > FeatherAttrValue:: Author: Ilan_Copelyn (Xara Group Ltd) Created: 24/01/2000 Inputs: pNode - node to feather (possibly compound node) size - of feather region (NB millipoint = INT32, pixels = INT32) - "INT32" rather than "MILLIPOINT" Width used to emphasize that millipoints are expected but not only measure excepted type - which measurment used for size Purpose: Setup feather member variables so that it is created properly on rendering SeeAlso: OpFeather::Do ********************************************************************************************/ BOOL FeatherAttrValue::SetupFeather(Node* pNode, INT32 Width, UnitType type) { // Get required feather size in Millipoints, taking current zoom into account // if width specified in Pixels MILLIPOINT BlurSize = ConvertMeasurementToMillipointsAtCurrentViewScale(Width,type); if (BlurSize == -1) BlurSize = DEFAULT_FEATHERSIZE_MP; SetFeatherSize(BlurSize); // Save pointer to node so we can get at it in the rendering code. SetLinkedNode((NodeRenderableBounded*) pNode); #ifdef _DEBUG_OFFSCREEN_PIXEL_WIDTH RenderDebuggingBitmaps(NULL, pNode, 1.0, 32, FALSE); #endif return TRUE; } /******************************************************************************************** > FeatherAttrValue:: Author: Ilan_Copelyn (Xara Group Ltd) Created: 24/01/2000 Purpose: SeeAlso: ********************************************************************************************/ BOOL FeatherAttrValue::ChangeFeatherProfile(CProfileBiasGain &biasgain) { AFp BiasValue = biasgain.GetBias(); AFp GainValue = biasgain.GetGain(); m_Profile.SetBias(BiasValue); m_Profile.SetGain(GainValue); return TRUE; } /******************************************************************************************** > FeatherAttrValue:: Author: Ilan_Copelyn (Xara Group Ltd) Created: 24/01/2000 Purpose: SeeAlso: ********************************************************************************************/ BOOL FeatherAttrValue::ChangeFeatherSize(INT32 Width, UnitType type) { BOOL res=SetupFeather(GetLinkedNode(), Width, type); return res; } /******************************************************************************************** > MILLIPOINT FeatherAttrValue::GetFeatherSize() Author: Karim_MacDonald (Xara Group Ltd) Created: 31/01/2001 Returns: Our feather size, in millipoints. Purpose: Accessor method for our feather size. Notes: This value is always capped off to MaxUserFeatherSize millipoints, to prevent anybody ever using a feather size which we cannot handle. ********************************************************************************************/ MILLIPOINT FeatherAttrValue::GetFeatherSize() { return (m_FeatherSize > MaxUserFeatherSize) ? MaxUserFeatherSize : m_FeatherSize; } /******************************************************************************************** > MILLIPOINT FeatherAttrValue::SetFeatherSize(MILLIPOINT mpWidth) Author: Karim_MacDonald (Xara Group Ltd) Created: 31/01/2001 Inputs: Our new feather size, in millipoints. Returns: The feather size we actually set, taking cap values into account. Purpose: Mutator method for our feather size. Notes: This method will not set a value <0 mp or >MaxUserFeatherSize mp, to prevent us dealing in feather sizes that we cannot handle. ********************************************************************************************/ MILLIPOINT FeatherAttrValue::SetFeatherSize(MILLIPOINT mpWidth) { m_FeatherSize = mpWidth < 0 ? 0 : mpWidth > MaxUserFeatherSize ? MaxUserFeatherSize : mpWidth; return m_FeatherSize; } /******************************************************************************************** > inline double FeatherAttrValue::GetFeatherPixelSize(double fPixelWidth) Author: Karim_MacDonald (Xara Group Ltd) Created: 17/08/2000 Inputs: fPixelWidth width of a pixel in millipoints - accepts non-integer values. Returns: The feather size, in pixels which are fPixelWidth millipoints wide, capped off to a maximum of (2 * MAX_SHADOW_BLUR) pixels. Purpose: Return the size of this feather, in pixels of the specified width. Different from GetFeatherSize(PIXELS) mainly because: 1. we return a double, so eg you can get back a value of 2.5 pix. 2. we're inline. Notes: inline, as it's small and fast. ********************************************************************************************/ inline double FeatherAttrValue::GetFeatherPixelSize(double fPixelWidth) { const double MaxFeatherBlurDiameter = 2.0 * MAX_SHADOW_BLUR; double fFeatherSize = (fPixelWidth <= 0) ? 0 : (double)m_FeatherSize / fPixelWidth; return (fFeatherSize > MaxFeatherBlurDiameter) ? MaxFeatherBlurDiameter : fFeatherSize; } /******************************************************************************************** > FeatherAttrValue:: Author: Ilan_Copelyn (Xara Group Ltd) Created: 24/01/2000 Purpose: SeeAlso: ********************************************************************************************/ void FeatherAttrValue::SetFeatherProfile(CProfileBiasGain &biasgain) { AFp BiasValue = biasgain.GetBias(); AFp GainValue = biasgain.GetGain(); m_Profile.SetBias(BiasValue); m_Profile.SetGain(GainValue); } /******************************************************************************************** > virtual void FeatherAttrValue::SetLinkedNode(NodeRenderableBounded* pNode) Author: Karim_MacDonald (Xara Group Ltd) Created: 11/10/2000 Purpose: Our outer contour gap size depends on what our linked node is, so we must recalculate our gap size whenever our linked node is set. ********************************************************************************************/ void FeatherAttrValue::SetLinkedNode(NodeRenderableBounded* pNode) { m_pLinkedNode = pNode; if (pNode != NULL) { if (IS_A(GetLinkedNode(), NodeGroup) || IS_A(GetLinkedNode(), NodeMould) || IS_A(GetLinkedNode(), NodeBlend) || GetLinkedNode()->IsANodeClipViewController()) { m_GapSize = DEFAULT_GAP_TOLERANCE_MP; } else { m_GapSize = 1; } } } /******************************************************************************************** > FeatherAttrValue:: Author: Ilan_Copelyn (Xara Group Ltd) Created: 24/01/2000 Purpose: SeeAlso: ********************************************************************************************/ BOOL FeatherAttrValue::GenerateOuterContour() { // fail now if we have no linked node. if (GetLinkedNode() == NULL) { ERROR3("FeatherAttrValue::GenerateOuterContour; Aarrgh!! Where's our linked node?!!"); return FALSE; } // We know we are about to overwrite this value so if it's still set up // we shuold delete it now to prevent memory leaks if (m_pOuterContour) { delete m_pOuterContour; m_pOuterContour = NULL; } // Get outer contour // NB m_pOuterContour is a contour which is m_GapSize outside m_pPath's outline // Outer contour with m_GapSize is used so that small gaps are forced out. This is // especially desireable when there are a large number of tiny gaps within the object // we are trying feather (eg the gaps between the 'front windscreen' shape and the // 'windscreen frame') m_pOuterContour = new Path; BOOL fSuccess = NodeContour::GenerateContourPathForNode(m_pOuterContour, GetLinkedNode(), NULL, m_GapSize, TRUE, RoundJoin, -1, TRUE, TRUE); if (!fSuccess) { delete m_pOuterContour; m_pOuterContour = NULL; } return fSuccess; } /******************************************************************************************** > FeatherAttrValue:: Author: Ilan_Copelyn (Xara Group Ltd) Created: 3/5/2000 ********************************************************************************************/ BOOL FeatherAttrValue::CanBeRenderedDirectly() { if(!GetLinkedNode()) return FALSE; if(GetLinkedNode()->IS_KIND_OF(NodeDocument)) return FALSE; if(GetFeatherSize()==0) return FALSE; return TRUE; } /******************************************************************************************** > BOOL FeatherAttrValue::RegenerateOuterContourOnNextRedraw(UndoableOperation* pOp = NULL) Author: Ilan_Copelyn (Xara Group Ltd) ; Karim MacDonald Created: 24/01/2000; 01/03/2001 Inputs: pOp optional ptr to an UndoOp, letting us store undo information. Returns: TRUE if we were successful, FALSE otherwise. Purpose: Clear the cache of our outer-contour path. See also: AttrFeather::Transform(), AttrFeather::LinkedNodeGeometryHasChanged(). ********************************************************************************************/ BOOL FeatherAttrValue::RegenerateOuterContourOnNextRedraw(UndoableOperation* pOp) { if (m_pOuterContour != NULL) { if (pOp != NULL) { // make this undoable RegenerateFeatherContourAction* pAct = NULL; if (RegenerateFeatherContourAction::Init(pOp, pOp->GetUndoActionList(), this, m_pOuterContour, &pAct) == AC_FAIL) { TRACEALL( _T("FeatherAttrValue::RegenerateOuterContourOnNextRedraw; Could not record undo information.\n")); return FALSE; } } // don't actually delete the old outer contour // if it's been saved via the undoable operation. if (pOp == NULL) delete m_pOuterContour; m_pOuterContour = NULL; } return TRUE; } ///////////////////////////////////////////////////////////////////////////////////////////// // // NodeAttribute classes // ///////////////////////////////////////////////////////////////////////////////////////////// /******************************************************************************************** > void AttrFeather::Render( RenderRegion* pRender ) Author: Ilan_Copelyn (Xara Group Ltd) , Karim MacDonald Created: 24/01/2000 , 21/07/2000 Inputs: pRender the render-region to render into. Purpose: Render this feather attribute. We do one or two checks for validity of this attr, and then render its attribute-value into the RR. Notes: If you want to customise for different RRs, then override RenderRegion::SetOffscreen and FeatherAttrValue::OffscreenRenderingCompleted. Errors: ERROR3 if we have no linked node at the mo'. See also: FeatherAttrValue::Render(). ********************************************************************************************/ void AttrFeather::Render( RenderRegion* pRender ) { // no feathering for hit-testing, please. if (pRender->IsHitDetect()) return; // Karim 21/07/2000 // Feathers can render into most render regions. // If a render region can't deal with feathering, then the attribute will only be // 'rendered' in the RenderRegion sense - ie plonked on the context stack - and // no attempt will be made to generate offscreen bitmaps and what-not. // only bother rendering us if our feather size is non-zero. if (Value.GetFeatherSize() != 0) { // ensure internal pointer is setup for rendering. if (Value.GetLinkedNode() == NULL) { Node* pParent = FindParent(); if (pParent != NULL && pParent->IsAnObject()) Value.SetLinkedNode((NodeRenderableBounded*) FindParent()); else { if (!Error::IsUserName("Gavin")) ERROR3("AttrFeather::Render; No linked node and parent is not an ink-node!"); return; } } Value.Render(pRender); } } /******************************************************************************************** > void* AttrFeather::GetDebugDetails(StringBase* Str) Author: Ilan_Copelyn (Xara Group Ltd) Created: Outputs: Str: String giving debug info about the node Purpose: For obtaining debug information about the Node ********************************************************************************************/ #ifdef _DEBUG void AttrFeather::GetDebugDetails(StringBase* Str) { // Output base class debug info. NodeRenderable::GetDebugDetails(Str); // Output our feather size, in millipoints. MILLIPOINT mp = Value.GetFeatherSize(); String_256 TempStr; TempStr._MakeMsg(TEXT("\r\nFeather size (millipoints) #1%ld"), mp); (*Str) += TempStr; } #endif /******************************************************************************************** > AttrFeather:: Author: Ilan_Copelyn (Xara Group Ltd) Created: 24/01/2000 Purpose: SeeAlso: Node::CopyChildren(OpClone); attrmgr::applycurrentattribstonode (opnewregshape) TODO: Code which uses this to localise attributes must be changed. Inconsistencies arise if bitmap preserved and subsequently generated from ex-member node only, and time delays occur if feather bmp regenerated for each member node on localisation. ********************************************************************************************/ Node* AttrFeather::SimpleCopy() { AttrFeather* pAttr = new AttrFeather(); ERRORIF(pAttr==NULL, _R(IDE_NOMORE_MEMORY),NULL); CopyNodeContents(pAttr); return pAttr; } /******************************************************************************************** > AttrFeather:: Author: Ilan_Copelyn (Xara Group Ltd) Created: 24/01/2000 Purpose: ********************************************************************************************/ void AttrFeather::CopyNodeContents(NodeAttribute *pCopy) { NodeAttribute::CopyNodeContents(pCopy); ((AttrFeather*)pCopy)->Value.SimpleCopy( &Value ); } /*********************************************************************************************** > void AttrFeather::PolyCopyNodeContents(NodeRenderable* pNodeCopy) Author: Phil_Martin (Xara Group Ltd) Created: 18/12/2003 Outputs: - Purpose: Polymorphically copies the contents of this node to another Errors: An assertion failure will occur if NodeCopy is NULL Scope: protected ***********************************************************************************************/ void AttrFeather::PolyCopyNodeContents(NodeRenderable* pNodeCopy) { ENSURE(pNodeCopy, "Trying to copy a node's contents into a NULL node"); ENSURE(IS_A(pNodeCopy, AttrFeather), "PolyCopyNodeContents given wrong dest node type"); if (IS_A(pNodeCopy, AttrFeather)) CopyNodeContents((AttrFeather*)pNodeCopy); } /******************************************************************************************** > AttrFeather:: Author: Ilan_Copelyn (Xara Group Ltd) Created: 24/01/2000 Purpose: Closely tied in with the attr optimisation code. The ShouldBeOptimized() function will only allow a AttrFeather (ie a NodeAttribute) to be factored out if the contained value's feather size is 0. This will happen when new nodes are created using a current feather attribute with 0 feather size. When this occurs we want to let the optimisation code (NormaliseAttributes()) remove the AttrFeather from the node on insertion into the tree. NB the check in the optim code is "if (AttrAleadyAppliedHigherUpTheTree == attrToOptimize)" so we want to return TRUE if attrToOptimize->Value.feathersize is 0 so that it gets deleted. NB As long as the optimisation method is always used to insert nodes into the tree, and also the order of comparison (ie a==b but not b==a) is preserved, we can always expect 0 width feathers to be optimised out. TODOCHECK: Assuming here that == is not required anywhere other than in the attribute optimisation code. May not be correct behaviour if comparison required in other circumstance. ********************************************************************************************/ INT32 AttrFeather::operator==(const NodeAttribute& NodeAttrib) { ERROR3IF(!NodeAttrib.IsAFeatherAttr(), "AttrFeather::operator==; Compared with a non-feather attr."); AttrFeather *PotentialAttrToOptimize = (AttrFeather*) &NodeAttrib; return (Value.GetFeatherSize() == PotentialAttrToOptimize->Value.GetFeatherSize()); } /******************************************************************************************** > AttrFeather:: Author: Ilan_Copelyn (Xara Group Ltd) Created: 24/01/2000 Purpose: SeeAlso: ********************************************************************************************/ UINT32 AttrFeather::GetAttrNameID() { return (_R(IDS_FEATHER_ATTR_ID)); } /******************************************************************************************** > AttrFeather:: Author: Ilan_Copelyn (Xara Group Ltd) Created: 24/01/2000 Purpose: SeeAlso: ********************************************************************************************/ UINT32 AttrFeather::GetNodeSize() { return sizeof(AttrFeather); } /******************************************************************************************** > AttrFeather:: Author: Ilan_Copelyn (Xara Group Ltd) Created: 24/01/2000 Purpose: SeeAlso: ********************************************************************************************/ BOOL AttrFeather::IsAFeatherAttr() const { return TRUE; } /******************************************************************************************** > BOOL AttrFeather::DoFeather(Node* pNode, INT32 size = DEFAULT_FEATHERSIZE_MP, UnitType type = MILLIPOINTS) Author: Ilan_Copelyn (Xara Group Ltd) Created: 24/01/2000 Purpose: Setup new FeatherAttrValue to apply feathering or change existing feather size SeeAlso: OpFeather::Do(),OpChangeFeather::DoWithParam(...) ********************************************************************************************/ BOOL AttrFeather::DoFeather(Node* pNode, INT32 size, UnitType type) { return Value.SetupFeather(pNode, size, type); } /******************************************************************************************** > AttrFeather:: Author: Ilan_Copelyn (Xara Group Ltd) Created: 24/01/2000 Purpose: SeeAlso: ********************************************************************************************/ BOOL AttrFeather::DoSetProfile(CProfileBiasGain bg) { return Value.ChangeFeatherProfile(bg); } /******************************************************************************************** Layering and redundant feather removal Purpose: Feathers are layered attributes. Feathering individual members in a group will look visibly different to feathering the entire group (if any of the children overlap) Hence once a feather attribute has been inserted in to the tree it must not be moved around by the optimisation code. However, we don't want zero size feather attributes to be stuck into the tree ever. This function determines when an attribute can be optimised so determined whether we can get rid of rdundant attributes. Hence we allow optimisation to occur in the case where we have a zero width feather. ********************************************************************************************/ /******************************************************************************************** > AttrFeather:: Author: Ilan_Copelyn (Xara Group Ltd) Created: 24/01/2000 Purpose: Ensure that this node is not optimised (ie factored out when groups created or new / cloned nodes inserted into the tree). Called when :- 1) a new node is inserted into the tree 2) a clone is inserted into the tree 3) a node with feathering applied is being grouped with other nodes 4) when a group with feathering applied is being ungrouped Feathers should never be optimised. This means that they will always be inserted into the tree with each new node created, and not factored out. The default feather after camelot initialises itself (ie on startup) has zero feather width. To prevent the tree becomming filled with lots of superfluous 0 width feather attrs we have an extra check in AttributeManager::ApplyCurrentAttribsToNode. SeeAlso: AttributeManager::ApplyCurrentAttribsToNode ********************************************************************************************/ BOOL AttrFeather::ShouldBeOptimized() { // don't want zero width feathers in the tree if (Value.GetFeatherSize() == 0) return TRUE; return FALSE; } /******************************************************************************************** Feather prevention ;) Purpose: Feathering is not supported in certain circumstances listed below Rather than hacking other code to prevent these situations arising I am using this method to prevent feathers causing damage to other nodes. This way feathering can be added simply once the individual incompatibilities have been resolved Notes: Attributes aren't always rendered by the standard render loop (eg DocView::RenderView). Hence this function doesn't catch all cases where feathers will get rendered, and this test needs to be repeated in the corresdponding AttrValue::Render() See RenderRegion::InitDevice() and CCAttrMap::Render() for egs of where these functions will be skipped. More Notes: Karim 21/07/2000 I have now annihilated all of the render-region checks - rather than do checks on the type of render-region, we'll just let the RR's feather-rendering methods do the job for us. This makes it a *lot* easier to customise for different RR's ;o) ********************************************************************************************/ SubtreeRenderState AttrFeather::RenderSubtree(RenderRegion *pRender, Node** ppNextNode, BOOL bClip) { // Can't feather unless attribute is attached to an object (ie something // with an inkpath) if (Value.GetLinkedNode() == NULL && FindParent() == NULL) return SUBTREE_NORENDER; // don't bother rendering if feather size is zero or we're the default attr. if (Value.GetFeatherSize() == 0 || Value.IsDefaultFlagSet()) return SUBTREE_NORENDER; return SUBTREE_ROOTONLY; } /******************************************************************************************** Dynamic Attribute creation Purpose: Support dynamic creation of feather attibutes by other complex nodes (just Blends and Contours so far) Dynamic attrs are created by CCAttrMap::MakeAppliedAttrMap which calls NodeRenderableInk::FindAppliedAttributes However the problem is that some attributes require additional info inorder to apply themselves to a given path or node. These attributes are specific to the node that they are attached to, so need an additonal step to set themselves up after being dynamically created. Feather attributes need their internal node pointer to be setup. This pointer is required in order to work out the dimensions of the offscreen bitmap, to place this bitmap onscreen once it has been created, and to get the inkpath so that the convolved transparency mask can be created ********************************************************************************************/ BOOL AttrFeather::PostDynCreateInit(CCAttrMap* pBlendMap, Path* pInkPath, CCRuntimeClass* CreatorClass) { // all defaults will be out by this stage // blending default to default will return false - so no feather should be in blend attr map // blending from default to non-def will dyn create a new feather and blend it's state members // its m_bDefault will be FALSE as it's set in the constructor ENSURE(!Value.IsDefaultFlagSet(),"How did this attribute get copied from the default attr?"); if (Value.GetFeatherSize() == 0) return FALSE; // Won't render and doesn't alloc mem // Karim 17/08/2000 // Feather attrs usually decide for themselves whether to use a transparency-capable // bitmap when rendering. However, within contours and blends, we must force them to, // as they are now detached from the tree, and therefore cannot examine the surroundings // to make a transparency decision. Value.SetAlwaysUseTransparency(); // fix internal node pointer so that we can setup offscreen bitmap // and create outer contour for generating feather mask // if we fail (eg out of mem) then put this feather attr into a state so that it won't NeedsToRender() NodePath* pNodePath = new NodePath(); if(!pNodePath) { return FALSE; // nb don't report errors. simply render without feathering if this fails } pNodePath->SetUpPath(pInkPath->GetNumCoords(),12); pNodePath->InkPath.CopyPathDataFrom(pInkPath); if(!MakeLineAttributeCompleteForContouring(pNodePath, this, pBlendMap)) { // Feather is unrenderable // Need to de-alloc mem because we will be returning FALSE and // hence won't be added to the attrmap - therefore no PostDynCreateDeinit // Set Child pointer to null in case we had partial sucess // *** Change here if copy attrs from blendmap inside MakeLineAttributeCompleteForContouring pNodePath->SetChildDangerous(NULL); delete pNodePath; return FALSE; } Value.SetLinkedNode(pNodePath); return TRUE; } /******************************************************************************************** > void AttrFeather::PostDynCreateDeInit() Author: Ilan_Copelyn (Xara Group Ltd) Created: Mid-2000 Purpose: Free memory allocated by PostDynCreateInit(..) functions Errors: No memory needs to be allocated, so cannot fail. ********************************************************************************************/ void AttrFeather::PostDynCreateDeInit() { if (Value.GetFeatherSize() == 0) { ERROR3("Failure to PostDynCreateInit should have removed this attr from the attrmap"); return; // nothing to de-alloc } // Karim 17/08/2000 // This call is for completeness - probably isn't 100% necessary, but // better safe than sorry. See PostDynCreateInit() above for more info. Value.UnsetAlwaysUseTransparency(); // NB this must only be called if you have called PostDynCreateInit(..) // dealloc the memory that we allocated in order to perform dynamic feathering // check parent node Node* pParent = FindParent(); ENSURE(pParent->FindParent()==NULL && pParent->FindPrevious()==NULL && pParent->FindNext()==NULL,"Parent not created dynamically"); // delete line attrs which we added // *** Change here if copy attrs from blendmap inside MakeLineAttributeCompleteForContouring /* Node* pPrev = FindPrevious(); if(pPrev) { ENSURE(pPrev->FindParent()==pParent,"Deleting dynamic child attrs which we didn't alloc."); pPrev->SetNextDangerous(NULL); pParent->CascadeDelete(); } */ pParent->SetChildDangerous(NULL); delete pParent; return; } /******************************************************************************************** > BOOL AttrFeather::IsLinkedToThisNode(Node* pNode) Author: Ilan_Copelyn (Xara Group Ltd) Created: Mid-2000 Inputs: pNode ptr to the node to check for linkage. Returns: TRUE if we're linked to pNode, FALSE otherwise. See Also: LinkToGeometry(). ********************************************************************************************/ BOOL AttrFeather::IsLinkedToThisNode(Node* pNode) { return (Value.GetLinkedNode() == pNode); } /******************************************************************************************** > virtual void AttrFeather::Transform(TransformBase& Trans) Author: Karim_MacDonald (Xara Group Ltd) Created: 16/10/2000 Inputs: Trans the applied transformation. Outputs: The feather size of our FeatherAttrValue will scale. Purpose: Scales the size of the feather as the feathered object is transformed. ********************************************************************************************/ void AttrFeather::Transform(TransformBase& Trans) { static const FIXED16 One(1.0); FIXED16 ScaleFactor = Trans.GetScalar(); if (ScaleFactor != One) { // Only transform feather width of the transform allows widths to be affected // NEW FEATURE? // if (Trans.TransLines) // { MILLIPOINT FeatherSize = Value.GetFeatherSize(); MILLIPOINT NewSize = INT32(0.5 + fabs(ScaleFactor.MakeDouble() * (double)FeatherSize) ); FeatherSize = (NewSize == 0) ? 1 : NewSize; Value.SetFeatherSize(FeatherSize); // } } // Always transform the outer path so that the shape appears correctly during solid drags Path* pOuterContour = Value.GetOuterContour(); if (pOuterContour != NULL) { Trans.Transform(pOuterContour->GetCoordArray(), pOuterContour->GetNumCoords()); } } /******************************************************************************************** > BOOL AttrFeather::Blend(BlendAttrParam* pBlendParam) Author: Ilan_Copelyn (Xara Group Ltd) Created: Mid-2000 Inputs: pBlendParam contains the blending info. Returns: TRUE if success, FALSE otherwise. Purpose: Blend us. Notes: Always returns FALSE if blending from default to default. NB in this case will never add an attr of this type to the blended attr map hence PostDynCreateInit will not be called ('cause it doesn't get dyn created!) Errors: ERROR3 if NULL input param; ENSURE if pBlendParam has no blend-attr. ********************************************************************************************/ BOOL AttrFeather::Blend(BlendAttrParam* pBlendParam) { // Check entry param isn't NULL ERROR3IF(pBlendParam == NULL, "NULL entry param"); if (pBlendParam == NULL) return FALSE; BOOL Success = FALSE; // We have a valid feather to blend, so do it (in the convoluted style copied from other attrs...) // Get the Value member to blend to the Value member of the other NodeAttribute. // If it succeeds, ask the blended Value to make a NodeAttribute out of itself. if (Value.Blend(pBlendParam)) { // Get the blended attr val. After this call, the ptr is our reponsibility // so we have to delete it if it's no longer needed AttributeValue* pBlendedAttrVal = pBlendParam->GetBlendedAttrVal(); if (pBlendedAttrVal != NULL) { // We have a blended attr val, so ask it to make a NodeAttribute out of itself // and set the pBlendParam's blended NodeAttribute ptr to it NodeAttribute* pBlendedAttr = pBlendedAttrVal->MakeNode(); pBlendParam->SetBlendedAttr(pBlendedAttr); if (pBlendedAttr != NULL) { // We were able to make a blended NodeAttribute // so delete the blended attr val, and return TRUE delete pBlendedAttrVal; Success = TRUE; } else { // Couldn't make a blended NodeAttribute, so give the blended attr val back // and return FALSE pBlendParam->SetBlendedAttrVal(pBlendedAttrVal); Success = FALSE; } } else { ENSURE(FALSE,"Couldn't get blended attr val from BlendAttrParam."); Success = FALSE; } } return Success; } /******************************************************************************************** > virtual BOOL AttrFeather::PostDynCreateInit( CCAttrMap* pMap, Node* pNode, CCRuntimeClass* pCreatorClass) Author: Ilan_Copelyn (Xara Group Ltd) Created: 24/01/2000 Purpose: SeeAlso: ********************************************************************************************/ BOOL AttrFeather::PostDynCreateInit(CCAttrMap* pMap, Node* pNode, CCRuntimeClass* pCreatorClass) { return TRUE; } /******************************************************************************************** > BOOL AttrFeather::LinkedNodeGeometryHasChanged(UndoableOperation* pOp) Author: Ilan_Copelyn (Xara Group Ltd) ; Karim MacDonald Created: 27/4/00; 01/03/2001 Inputs: pOp the operation causing our linked node's geometry to change. Outputs: Our cache of our outer-contour path may be cleared. Returns: TRUE if successful, FALSE otherwise. Purpose: Inform this attribute that the geometry of its linked node may be changing. Errors: ERROR2 if pOp is NULL - should *never* happen. See also: Node::AllowOp() + overrides - main place from which this fn is called. ********************************************************************************************/ BOOL AttrFeather::LinkedNodeGeometryHasChanged(UndoableOperation* pOp) { // DEBUG: // static UINT32 ctr = 0; // TRACEUSER( "Karim", _T("%d. LinkedNodeGeometryHasChanged() for 0x%x.\n"), ++ctr, &Value); ERROR2IF(pOp == NULL, FALSE, "AttrFeather::LinkedNodeGeometryHasChanged; Should never call with NULL pOp!"); // Karim 28/02/2000 // If we're undergoing translation, rotation or reflection, then our transform // method will automatically update our outer-contour. // In all other cases, we should clear out our outer-contour, // so it is refreshed at the next render. // // The only way to detect whether we're being transformed as above, is to test on the Op. if (pOp->IS_KIND_OF(TransOperation) && !pOp->IsKindOf( CC_RUNTIME_CLASS(OpPathNudge) ) ) { // arbitrarily chosen error threshold. static const double epsilon = 0.000016; Matrix Mat = ((TransOperation*)pOp)->GetCurrentMatrix(); if (Mat.IsTranslation() || Mat.IsReflection() || Mat.IsRotation(epsilon)) { // can only know for sure that we were transformed, // if our linked node, or one of its parents, is the op's target. Node* pLinkedNode = Value.GetLinkedNode(); // Node* pTransNode = ((TransOperation*)pOp)->GetNode(); // if( pLinkedNode == pTransNode || // (pTransNode != NULL && pTransNode->IsNodeInSubtree(pLinkedNode)) ) // return TRUE; Range* pRange = ((TransOperation*)pOp)->GetTransformRange(); if (pRange && pRange->Contains(pLinkedNode, TRUE)) return TRUE; } } return Value.RegenerateOuterContourOnNextRedraw(pOp); } /******************************************************************************************** > virtual void AttrFeather::NewlyCreatedDefaultAttr(NodeDocument* pNode) Author: Ilan_Copelyn (Xara Group Ltd) Created: 27/4/00 Inputs: Outputs: Returns: Purpose: Attributes which are linked to the geometry of the objects which they affect, may not be able to perform a useful function if attached directly to the NodeDocument node (ie a default attribute). This function allows a default attribute of this type to perform an extra initialisation step so that the Render() function can handle Errors: SeeAlso: - ********************************************************************************************/ void AttrFeather::NewlyCreatedDefaultAttr(NodeDocument* pNode) { Value.SetLinkedNode((NodeRenderableBounded*)pNode); Value.SetDefault(); } /******************************************************************************************** > AttrFeather:: Author: Ilan_Copelyn (Xara Group Ltd) Created: 9/5/2000 Purpose: connect attribute values internal node pointer SeeAlso: ********************************************************************************************/ BOOL AttrFeather::LinkToGeometry(Node* pContext) { Value.SetLinkedNode((NodeRenderableBounded*) pContext); return TRUE; } /******************************************************************************************** > virtual BOOL AttrFeather::ContainsAttributeValue(AttributeValue* pVal) Author: Ilan_Copelyn (Xara Group Ltd) Created: 7/6/00 Inputs: Outputs: Returns: Purpose: To check if this NodeAttribute contains a the AttributeValue NB needs to be implemented in a manner specific to the way in which the derived NodeAttribute class stores the AttributeValue Errors: SeeAlso: OffscreenAttrValue::DoesOffscreenBmpRequireTransp AttrFeather::ContainsAttributeValue ********************************************************************************************/ BOOL AttrFeather::ContainsAttributeValue(AttributeValue* pVal) { if(!pVal->IS_KIND_OF(FeatherAttrValue)) { ENSURE(FALSE,"Strange attr value comparison test requested"); return FALSE; } return (pVal == &Value); } /******************************************************************************************** > AttrFeather:: Author: Ilan_Copelyn (Xara Group Ltd) Created: 24/01/2000 Purpose: SeeAlso: ********************************************************************************************/ ///////////////////////////////////////////////////////////////////////////////////////////// // // Saving and loading // ///////////////////////////////////////////////////////////////////////////////////////////// BOOL AttrFeather::WritePreChildrenWeb(BaseCamelotFilter* pFilter) { ERROR2IF(pFilter == NULL,FALSE,"NULL filter param"); CXaraFileRecord Rec(TAG_FEATHER,TAG_FEATHER_SIZE); BOOL ok = TRUE; MILLIPOINT sz = Value.GetFeatherSize(); CProfileBiasGain pProfile(Value.GetProfile()); ok = Rec.Init(); if (ok) ok = Rec.WriteINT32(sz); if (ok) ok = Rec.WriteDOUBLE ((double) pProfile.GetBias()); if (ok) ok = Rec.WriteDOUBLE ((double) pProfile.GetGain()); if (ok) ok = pFilter->Write(&Rec); return ok; } BOOL AttrFeather::WritePreChildrenNative(BaseCamelotFilter* pFilter) { return WritePreChildrenWeb(pFilter); } UINT32* FeatherRecordHandler::GetTagList() { static UINT32 TagList[] = {TAG_FEATHER,CXFRH_TAG_LIST_END}; return (UINT32*)&TagList; } BOOL FeatherRecordHandler::HandleRecord(CXaraFileRecord* pCXaraFileRecord) { ERROR2IF(pCXaraFileRecord == NULL,FALSE,"pCXaraFileRecord is NULL"); BOOL ok = TRUE; switch (pCXaraFileRecord->GetTag()) { case TAG_FEATHER: ok = HandleFeatherRecord(pCXaraFileRecord); break; default: ok = FALSE; ERROR3_PF(("Incorrect tag supplied to HandleRecord. Tag = (%d)\n",pCXaraFileRecord->GetTag())); break; } return ok; } BOOL FeatherRecordHandler::HandleFeatherRecord(CXaraFileRecord* pCXaraFileRecord) { BOOL ok = TRUE; MILLIPOINT FeatherSz; double Bias = 0, Gain = 0; double* ptrBias = &Bias, *ptrGain = &Gain; // Read in the feather data if (ok) ok = pCXaraFileRecord->ReadINT32(&FeatherSz); if (ok) ok = pCXaraFileRecord->ReadDOUBLEnoError (ptrBias); if (ok) ok = pCXaraFileRecord->ReadDOUBLEnoError (ptrGain); CProfileBiasGain Profile; if ((ptrBias != NULL) && (ptrGain != NULL)) { Profile.SetBias((AFp) Bias); Profile.SetGain((AFp) Gain); } if (ok) { AttrFeather* pAttr = new AttrFeather; if (pAttr != NULL) { // Get a ptr to the attr value object FeatherAttrValue* pValue = (FeatherAttrValue*)&pAttr->Value; if (pValue != NULL) { // Set the feather size pValue->SetFeatherSize(FeatherSz); // Set the profile pValue->SetFeatherProfile(Profile); if (ok) ok = InsertNode(pAttr); // Set the m_Node pointer (TODO remove) if (ok && pBaseCamelotFilter->GetInsertMode()==INSERTMODE_ATTACHTOTREE) pValue->SetLinkedNode((NodeRenderableBounded*)pAttr->FindParent()); } else ok = FALSE; } else ok = FALSE; } return ok; }