// $Id: nodeshad.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" #ifdef BUILDSHADOWS #include //#include "pathedit.h" #include "nodeshad.h" #include "nodecont.h" #include "noderect.h" // Code headers //#include "attrmgr.h" - in camtypes.h [AUTOMATICALLY REMOVED] //#include "fillval.h" - in camtypes.h [AUTOMATICALLY REMOVED] //#include "fillattr.h" - in camtypes.h [AUTOMATICALLY REMOVED] #include "gclips.h" //#include "paths.h" - in camtypes.h [AUTOMATICALLY REMOVED] #include "pathtrap.h" #include "grndrgn.h" #include "nodebmp.h" //#include "becomea.h" - in camtypes.h [AUTOMATICALLY REMOVED] #include "bmpcomp.h" // New Bitmap Shadowing code //#include "app.h" - in camtypes.h [AUTOMATICALLY REMOVED] #include "bshadow.h" // necessary for special case with bevelling #include "opbevel.h" #include "nodebev.h" // Save/load //#include "cxfdefs.h" // TAG_SHADOW_SIZE - in camtypes.h [AUTOMATICALLY REMOVED] #include "cxftags.h" // TAG_SHADOW //#include "cxfrec.h" // CXaraFileRecord - in camtypes.h [AUTOMATICALLY REMOVED] //#include "camfiltr.h" // BaseCamelotFilter - in camtypes.h [AUTOMATICALLY REMOVED] #include "swfrndr.h" // FlashRenderRegion #include "cmxrendr.h" // CMXRenderRegion #include "ai_epsrr.h" // AIEPSRenderRegion //#include "dibutil.h" - in camtypes.h [AUTOMATICALLY REMOVED] #include "lineattr.h" // Resource headers //#include "resource.h" //#include "shadres.h" #include "moldtool.h" //#include "mario.h" #include "attrappl.h" //#include "view.h" - in camtypes.h [AUTOMATICALLY REMOVED] //#include "docview.h" - in camtypes.h [AUTOMATICALLY REMOVED] #include "blobs.h" #include "attrmap.h" #include "extender.h" #include "ngcore.h" // NameGallery, for stretching functionality #include "gdraw.h" #include "contmenu.h" #include "blndhelp.h" #include "osrndrgn.h" //#include "progress.h" //#include "spread.h" // for class Spread - in camtypes.h [AUTOMATICALLY REMOVED] #include "qualattr.h" // for class QualityAttribute //#include "rendbits.h" // for class OSRenderBitmap #include "nodebldr.h" // for class NodeCompoundBlendBecomeA #include "offscrn.h" // for GRenderRegionWrapper #include "oilbitmap.h" // for WinBitmap #include "nodecntr.h" // for ContourBecomeA #include "nodecont.h" // for default shadow settings #include "fthrattr.h" // for feather bodge. //#include "quality.h" // for quality setting info - used for caching. - in camtypes.h [AUTOMATICALLY REMOVED] #include "brshattr.h" // for AttrBrushType, which we don't want applied to us. #include "opfeathr.h" // for OpChangeFeatherSize, which we don't want operating on us. #include "nodeliveeffect.h" #include "ophist.h" #include "objchge.h" //#include "prntview.h" DECLARE_SOURCE( "$Revision: 1361 $" ); CC_IMPLEMENT_DYNCREATE(NodeShadow, NodeRenderableInk) // Declare smart memory handling in Debug builds #define new CAM_DEBUG_NEW // uncomment these to see the silhouette and final shadow bitmaps in the bitmap gallery. //#define ATTACH_SILHOUETTE_BMP //#define ATTACH_SHADOW_BMP #define NEW_GLOW_SILHOUETTE const UINT32 GlowWidthStrokeFlatness = 200; const double MaxShadowBitmapSize = 1024; const double MinShadowBitmapSize = 32; /*********************************************************************************************** > NodeShadow::NodeShadow(Node* ContextNode, AttachNodeDirection Direction, const DocRect& BoundingRect, BOOL Locked = FALSE, BOOL Mangled = FALSE, BOOL Marked = FALSE, BOOL Selected = FALSE, BOOL Renderable = FALSE ) Author: Peter_Arnold (Xara Group Ltd) Created: 16/11/94 Inputs: ContextNode: Pointer to a node which this node is to be attached to. MonoOn Direction: MonoOff Specifies the direction in which the node is to be attached to the ContextNode. The values this variable can take are as follows: PREV : Attach node as a previous sibling of the context node NEXT : Attach node as a next sibling of the context node FIRSTCHILD: Attach node as the first child of the context node LASTCHILD : Attach node as a last child of the context node BoundingRect: Bounding rectangle The remaining inputs specify the status of the node: Locked: Is node locked ? Mangled: Is node mangled ? Marked: Is node marked ? Selected: Is node selected ? Purpose: This constructor initialises the nodes flags and links it to ContextNode in the direction specified by Direction. All neccesary tree links are updated. Note: SetUpShape() must be called before the NodeRegularShape is in a state in which it can be used. SeeAlso: NodeRegularShape::SetUpShape Errors: An ENSURE will occur if ContextNode is NULL ***********************************************************************************************/ NodeShadow::NodeShadow(Node* ContextNode, AttachNodeDirection Direction, BOOL Locked, BOOL Mangled, BOOL Marked, BOOL Selected ) : NodeRenderableInk(ContextNode, Direction, Locked, Mangled, Marked, Selected ) { InitialiseMemberVars(); } /********************************************************************************************* > NodeShadow::NodeShadow() Author: Peter_Arnold (Xara Group Ltd) Created: 16/11/94 Purpose: This constructor creates a NodeRegularShape linked to no other. Note: SetUpShape() should be called to change the NodeRegularShape before use! SeeAlso: NodeRegularShape::SetUpShape **********************************************************************************************/ NodeShadow::NodeShadow() : NodeRenderableInk() { InitialiseMemberVars(); } /********************************************************************************************* > void NodeShadow::InitialiseMemberVars() Author: Peter_Arnold (Xara Group Ltd) Created: 18/6/96 Inputs: - Outputs: Initalised member variables Returns: - Purpose: One consistant place to initialise all the member variables of a NodeRegularShape **********************************************************************************************/ void NodeShadow::InitialiseMemberVars() { m_LastQualitySetting = (enum Quality::Fill)Quality::Solid; m_LastRequestedPixWidth = -1; m_LastActualPixWidth = -1; m_ShadowBitmap = NULL; m_RenderBitmaps = TRUE; m_bAmCopying = FALSE; m_bAmLoading = TRUE; m_Path.Initialise(); m_NonTranslatedPath.Initialise(); m_BitmapXOffset = 0; // m_CurrentBlur = 4 * 750; m_PreviousBlur = 0; m_ShadowWidth = 0; m_ShadowHeight = 0; // m_FloorShadowAngle = 20.0 * PI / 180.0; // m_FloorShadowHeight = 1.0; m_dDarkness = 0.0; m_bHaveTransformed = TRUE; m_pBMPTransFill = NULL; // Get a pointer to the My new Render Selection functions m_pShadower = NULL; } /********************************************************************************************* > NodeShadow::~NodeShadow() Author: Peter_Arnold (Xara Group Ltd) Created: 31/1/95 Purpose: Distructor to delete the cached render path SeeAlso: - **********************************************************************************************/ NodeShadow::~NodeShadow() { DeleteCache(); } /******************************************************************************************* > void NodeShadow::DeleteCurrentShadowStuff() Author: Mark_Howitt (Xara Group Ltd) Created: 16/9/98 Purpose: Deletes all Bitmaps and BitmapFillAttribute pointers ********************************************************************************************/ void NodeShadow::DeleteCurrentShadowStuff() { } /*********************************************************************************************** > virtual Node* NodeShadow::SimpleCopy() Author: Peter_Arnold (Xara Group Ltd) Created: 16/11/94 Returns: Pointer to a Node or NULL if there was not enough memory for the new node Purpose: Makes a copy of all the data in the node ***********************************************************************************************/ Node* NodeShadow::SimpleCopy() { NodeShadow* pNodeCopy = new NodeShadow(); if (pNodeCopy != NULL) CopyNodeContents(pNodeCopy); return pNodeCopy; } /*********************************************************************************************** > void NodeShadow::CopyNodeContents(NodeShadow* NodeCopy) Author: Peter_Arnold (Xara Group Ltd) Created: 16/11/94 Inputs: NodeCopy - The node to copy the data into Purpose: Copies the data in the node by first calling the base class to get it to copy its stuff, and then copying its own stuff Note : Copies FROM this TO NodeCopy SeeAlso: NodeRenderableInk::CopyNodeContents Errors: ERROR3 if passed a NULL pointer. ***********************************************************************************************/ void NodeShadow::CopyNodeContents(NodeShadow* NodeCopy) { ERROR3IF(NodeCopy == NULL,"NodeShadow::CopyNodeContents was asked to copy into a NULL pointer"); // copy the bitmaps and them fill too // if they're defined ! if (NodeCopy->m_ShadowBitmap) delete NodeCopy->m_ShadowBitmap; NodeCopy->m_ShadowBitmap = NULL; if (m_ShadowBitmap) { NodeCopy->m_ShadowBitmap = new KernelBitmap(m_ShadowBitmap->GetWidth(), m_ShadowBitmap->GetHeight(), 8, 0, TRUE); // ok, now copy the bitmap from me into the new bitmap on the copy node memcpy(NodeCopy->m_ShadowBitmap->GetBitmapBits(), m_ShadowBitmap->GetBitmapBits(), DIBUtil::ScanlineSize(m_ShadowBitmap->GetWidth(), 8) * m_ShadowBitmap->GetHeight()); // now, copy the palette LPRGBQUAD pTranspPalette = NodeCopy->m_ShadowBitmap->GetPaletteForBitmap(); double dI = 0; for (INT32 i = 0 ; i < 256; i++) { dI = ((double)i / 255.0); dI = m_BiasGain.MapZeroToOne((AFp)dI); dI *= 255.0; if (dI > 255.0) dI = 255.0; pTranspPalette->rgbBlue = (BYTE)dI; pTranspPalette->rgbRed = (BYTE)dI; pTranspPalette->rgbGreen = (BYTE)dI; pTranspPalette->rgbReserved = 0x00; pTranspPalette ++; } if (NodeCopy->m_pBMPTransFill) delete NodeCopy->m_pBMPTransFill; NodeCopy->m_pBMPTransFill = new BitmapTranspFillAttribute; if (NodeCopy->m_pBMPTransFill) { // Now work out the control points for the given Shadow DocCoord StartP(m_SelectedRect.lo.x, m_SelectedRect.lo.y); DocCoord EndPA(m_SelectedRect.hi.x, m_SelectedRect.lo.y); DocCoord EndPB(m_SelectedRect.lo.x, m_SelectedRect.hi.y); // Set the individual control points with the worked out values NodeCopy->m_pBMPTransFill->SetStartPoint(&StartP); NodeCopy->m_pBMPTransFill->SetEndPoint(&EndPA); NodeCopy->m_pBMPTransFill->SetEndPoint2(&EndPB); NodeCopy->m_pBMPTransFill->SetTesselation(RT_Simple); NodeCopy->m_pBMPTransFill->SetTranspType(TT_Mix); NodeCopy->m_pBMPTransFill->BitmapRef.SetBitmap(NodeCopy->m_ShadowBitmap); UINT32 startTrans = GetTransp(); NodeCopy->m_pBMPTransFill->SetStartTransp(&startTrans); UINT32 endTrans = 255; NodeCopy->m_pBMPTransFill->SetEndTransp(&endTrans); } } // DocRect dr = NodeCopy->GetBoundingRect(); // Make a copy of our internal state. NodeCopy->SetDarkness(GetDarkness()); NodeCopy->SetBiasGain(GetBiasGain()); // Remember to copy or blank cached path data without leaking memory... NodeCopy->m_Path.Initialise(m_Path.GetNumCoords()); NodeCopy->m_Path.CopyPathDataFrom(&m_Path); NodeCopy->m_NonTranslatedPath.Initialise(m_NonTranslatedPath.GetNumCoords()); NodeCopy->m_NonTranslatedPath.CopyPathDataFrom(&m_NonTranslatedPath); // Copy other member vars... NodeCopy->m_ShadowWidth = m_ShadowWidth; NodeCopy->m_ShadowHeight = m_ShadowHeight; NodeCopy->m_bHaveTransformed = m_bHaveTransformed; NodeCopy->m_RenderBitmaps = m_RenderBitmaps; NodeCopy->m_bAmCopying = m_bAmCopying; NodeCopy->m_bAmLoading = m_bAmLoading; // NodeCopy->m_Darkness; NodeCopy->m_SelectedRect = m_SelectedRect; NodeCopy->m_PreviousBlur = m_PreviousBlur; // DMc the bitmap shadower (not the COM shadower flat) // CBitmapShadow * m_pShadower; NodeCopy->m_BitmapXOffset = m_BitmapXOffset; NodeCopy->m_BiasGain = m_BiasGain; NodeCopy->m_LastRequestedPixWidth = m_LastRequestedPixWidth; NodeCopy->m_LastQualitySetting = m_LastQualitySetting; NodeCopy->m_LastActualPixWidth = m_LastActualPixWidth; // Copy from the base class NodeRenderableInk::CopyNodeContents(NodeCopy); } /*********************************************************************************************** > void NodeShadow::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 NodeShadow::PolyCopyNodeContents(NodeRenderable* pNodeCopy) { ENSURE(pNodeCopy, "Trying to copy a node's contents into a NULL node"); ENSURE(IS_A(pNodeCopy, NodeShadow), "PolyCopyNodeContents given wrong dest node type"); if (IS_A(pNodeCopy, NodeShadow)) CopyNodeContents((NodeShadow*)pNodeCopy); } /*********************************************************************************************** > void NodeShadow::ShowDebugTreeDetails() const Author: Peter_Arnold (Xara Group Ltd) Created: 16/11/94 Purpose: Displays debugging info of the tree SeeAlso: NodeRenderableInk::ShowDebugTreeDetails ***********************************************************************************************/ void NodeShadow::ShowDebugTreeDetails() const { // Display a bit of debugging info // For now, we will just call the base class version TRACEALL( _T("NodeShadow ") ); #ifdef _DEBUG NodeRenderableInk::ShowDebugTreeDetails(); #endif } /******************************************************************************************** > void NodeShadow::GetDebugDetails( StringBase* Str ) Author: Peter_Arnold (Xara Group Ltd) Created: 16/11/94 Outputs: Str: String giving debug info about the node Purpose: For obtaining debug information about the Node (for ********************************************************************************************/ void NodeShadow::GetDebugDetails( StringBase* Str ) { // Call base class #ifdef _DEBUG if (Str) { NodeRenderableInk::GetDebugDetails( Str ); String_256 TempStr; String_256 TempStr2; (*Str) += TEXT( "\r\nNode Shadow Data Dump\r\n" ); DocRect BlobRect = GetBlobBoundingRect(); TempStr._MakeMsg( TEXT("Blob Bounding Rect :\r\n\t#1%ld,\t#2%ld\r\n\t#3%ld,\t#4%ld\r\n"), BlobRect.lo.x, BlobRect.lo.y, BlobRect.hi.x, BlobRect.hi.y ); (*Str) += TempStr; } #endif } /*********************************************************************************************** > BOOL NodeShadow::SetUpShape() Author: Peter_Arnold (Xara Group Ltd) Created: 16/11/94 Inputs: - Returns: TRUE if the shape was init'ed ok, FALSE otherwise Purpose: To initialise the paths used by the shape into a state that can be used, by allocating memory, setting up member variables properly. ***********************************************************************************************/ BOOL NodeShadow::SetUpShape() { return TRUE; } /******************************************************************************************** > virtual void NodeShadow::Transform( TransformBase& Trans ) Author: Peter_Arnold (Xara Group Ltd) Created: 21/11/94 Inputs: Trans - The transform Object Purpose: Transforms the shape. SeeAlso: NodeRenderableInk::Transform() ********************************************************************************************/ void NodeShadow::Transform( TransformBase& Trans ) { NodeShadowController* pControl = (NodeShadowController*)GetParentController(); if (pControl && pControl->IsCapturingChildren()) { // TransformChildren(Trans); return; } if (m_pBMPTransFill) { Trans.Transform(m_pBMPTransFill->GetStartPoint(), 1); Trans.Transform(m_pBMPTransFill->GetEndPoint(), 1); Trans.Transform(m_pBMPTransFill->GetEndPoint2(), 1); Trans.Transform(m_pBMPTransFill->GetEndPoint3(), 1); } // transform my shape Trans.Transform(m_Path.GetCoordArray(), m_Path.GetNumCoords()); Trans.Transform(m_NonTranslatedPath.GetCoordArray(), m_NonTranslatedPath.GetNumCoords()); InvalidateBoundingRect(); // transform the selection rect // check for a change in aspect if (Trans.IsTranslation()) { if (pControl != NULL) { if (pControl->GetShadowType() == SHADOWTYPE_GLOW) { Trans.Transform(&m_SelectedRect.lo, 1); Trans.Transform(&m_SelectedRect.hi, 1); } else { // Karim 19/10/2000 // Chris' last changes to this method amount to commenting out this line. // pControl->RegenerateNode(NULL); } } } else { // force a redraw of myself to stop rendering glitches Document * pDoc = Document::GetCurrent(); if (pDoc) { DocRect dr(0,0,0,0); m_Path.GetTrueBoundingRect(&dr); pDoc->ForceRedraw(FindParentSpread(), dr, FALSE, this, FALSE); } } // transform my children TransformChildren(Trans); } /*********************************************************************************************** > void NodeShadow::Render(RenderRegion* pRender) Author: Peter_Arnold (Xara Group Ltd) Created: 16/11/94 Inputs: Pointer to a render region Purpose: Will build a path for the shape and draw it to the given render region. ***********************************************************************************************/ void NodeShadow::Render(RenderRegion* pRender) { m_bHaveTransformed = FALSE; m_RenderBitmaps = FALSE; // check for us having something to render or not NodeShadowController* pController = (NodeShadowController*)GetParentController(); if (pController) { #ifdef NEW_SHADOW_RENDER // If Controller has Effect Attributes applied // Then the Shadow Node must not render anything - it's RenderShadow // function will be called at the right time instead if (pController->IsCapturingChildren()) { // TRACEUSER( "Gerry", _T("Render# Controller is capturing\n")); // Normally we allow the ShadowController to render us when it wants to // But for hit detection we must render ourselves (aswell) // or when render quality is outline if (pRender->IsHitDetect() || pRender->RRQuality.GetFillQuality() <= Quality::Solid) { // TRACEUSER( "Gerry", _T("Render# Hit detect or outline\n")); RenderShadow(pRender); } return; } #endif // if my inside bounding rect is empty (i.e. the bounding rect of all // the objects to be shadowed) then do nothing if (pController->GetInsideBoundingRect().IsEmpty()) { // TRACEUSER( "Gerry", _T("Render# Inside bounding rect is empty\n")); return; } } if (!RenderBitmapForDisplay(pRender)) { // TRACEUSER( "Gerry", _T("Render# RenderBitmapForDisplay failed\n")); return; } // check the quality setting - if it's too low, then we need to render a shadow outline. enum Quality::Fill CurrentQuality = pRender->RRQuality.GetFillQuality(); if (CurrentQuality >= (enum Quality::Fill)Quality::Solid) { // TRACEUSER( "Gerry", _T("Render# Calling RenderBitmap\n")); RenderBitmap(pRender); } else { // outlines only - we'll just render the shadow's bounding rect. // TRACEUSER( "Gerry", _T("Render# Rendering bounding rect\n")); DocRect drBounds = GetBoundingRect(); pRender->SaveContext(); pRender->SetFillColour(COLOUR_NONE); pRender->SetLineColour(COLOUR_BLACK); pRender->DrawRect(&drBounds); pRender->RestoreContext(); } // get the view out of the renderer // and reset its rendering clock View * pView = pRender->GetRenderView(); if (pView) { if (pView->IS_KIND_OF(DocView)) { ((DocView *)pView)->ResetRenderingStartTime(); } } } /******************************************************************************************** > void NodeShadow::RenderEorDrag( RenderRegion* pRender ) Author: Peter_Arnold (Xara Group Ltd) Created: 16/11/94 Inputs: pRender - A Pointer to the current RenderRegion Purpose: Renders a version of the shape for EORed dragging. SeeAlso: NodeRenderableInk::RenderEorDrag ********************************************************************************************/ void NodeShadow::RenderEorDrag( RenderRegion* pRender ) { // outlines only - we'll just render the shadow's bounding rect. DocRect drBounds = GetBoundingRect(); // pRender->SaveContext(); // pRender->SetFillColour(COLOUR_NONE); // pRender->SetLineColour(COLOUR_BLACK); pRender->DrawRect(&drBounds); // pRender->RestoreContext(); } /******************************************************************************************** > void NodeShadow::RenderObjectBlobs(RenderRegion* pRender) Author: Peter_Arnold (Xara Group Ltd) Created: 16/11/94 Inputs: pRender - the region to render the blobs to Purpose: Renders the Object blobs for a NodeRegularShape SeeAlso: BlobManager ********************************************************************************************/ void NodeShadow::RenderObjectBlobs(RenderRegion* pRender) { } /******************************************************************************************** > void NodeShadow::RenderTinyBlobs(RenderRegion* pRender) Author: Peter_Arnold (Xara Group Ltd) Created: 16/11/94 Inputs: pRender - the region to render the blobs to Purpose: Renders the Tiny blobs for a NodeRegularShape SeeAlso: BlobManager ********************************************************************************************/ void NodeShadow::RenderTinyBlobs(RenderRegion* pRender) { pRender->SaveContext(); pRender->SetLineColour(COLOUR_NONE); pRender->SetFillColour(COLOUR_UNSELECTEDBLOB); // Render the blobs on the path BlobManager* BlobMgr = GetApplication()->GetBlobManager(); // DocRect dr = m_Path.GetBoundingRect(); MRH 19/5/00 DocRect dr(0,0,0,0); m_Path.GetTrueBoundingRect(&dr); DocCoord Coord(dr.lo.x + (BlobMgr->GetBlobSize() / 2), dr.hi.y - (BlobMgr->GetBlobSize() / 2)); pRender->DrawBlob(Coord, BT_UNSELECTED); pRender->RestoreContext(); } /******************************************************************************************** > DocRect NodeShadow::GetBoundingRect(BOOL DontUseAttrs = FALSE, BOOL HitTest = FALSE) Author: Karim_MacDonald (Xara Group Ltd) Created: 23/06/2000 Inputs: DontUseAttrs TRUE if we should take node attributes into account. HitTest TRUE if we're being called during a hit-test. Returns: This node's bounding rect. Purpose: If the bounding rect has been marked as invalid, it is recalculated, otherwise a cached bounding rectangle is returned. Errors: ERROR3 if we find an unrecognised shadow type. ********************************************************************************************/ DocRect NodeShadow::GetBoundingRect(BOOL DontUseAttrs, BOOL HitTest) { // is our cached bounding rect invalid? if (!IsBoundingRectValid) { DocRect drBounds(0, 0, 0, 0); // if our shadow path is valid, then just try to use its bounding rect. // This will give us a perfect bounds rect so cache it of appropriate if (m_Path.GetNumCoords() != 0) { if (m_Path.GetTrueBoundingRect(&drBounds) && !drBounds.IsEmpty()) { // if we're taking attributes into account then cache it. if (!DontUseAttrs) { // update our cached bounding rect. BoundingRectangle = drBounds; IsBoundingRectValid = TRUE; } return drBounds; } } // if that didn't work, then we'll use the lengthier method of finding bounds // for our next-siblings, and then transforming them appropriately. // However, we wont cache this result as it can be a bit small // which causes problems when printing // Get bounds of the ink node at the bottom of the shadow stack NodeShadowController* pControl = (NodeShadowController*)GetParentController(); if (pControl) { NodeShadowController* pBottomControl = pControl->FindBottomShadowController(); NodeRenderableInk* pSibling = pBottomControl->GetInkNodeFromController(); if (pSibling) drBounds = drBounds.Union(pSibling->GetBoundingRect(DontUseAttrs, HitTest)); } // a different transformation is required, according to our shadow type. if (pControl != NULL) { switch (pControl->GetShadowType()) { case SHADOWTYPE_WALL: drBounds.Translate(pControl->GetOffsetX(), pControl->GetOffsetY()); drBounds.Inflate(pControl->GetPenumbraWidth()); break; case SHADOWTYPE_GLOW: drBounds.Inflate(pControl->GetGlowWidth()); drBounds.Inflate(pControl->GetPenumbraWidth()); break; case SHADOWTYPE_FLOOR: { DocCoord dcControlVector = pControl->GetFloorShadowVector(); drBounds.hi.y = drBounds.lo.y + dcControlVector.y; drBounds.hi.y += pControl->GetPenumbraWidth(); if (dcControlVector.x > 0) { drBounds.hi.x += dcControlVector.x; drBounds.hi.x += pControl->GetPenumbraWidth(); } else { drBounds.lo.x += dcControlVector.x; drBounds.lo.x -= pControl->GetPenumbraWidth(); } } break; case SHADOWTYPE_FEATHER: drBounds.Inflate(pControl->GetFeatherWidth()); // TODO: /2? drBounds.Inflate(pControl->GetPenumbraWidth()); break; default: ERROR3("NodeShadow::GetBoundingRect; unknown parent shadow type!"); break; } } return drBounds; } return BoundingRectangle; } /******************************************************************************************** > DocRect NodeShadow::GetBlobBoundingRect() Author: Peter_Arnold (Xara Group Ltd) Created: 16/11/94 Returns: DocRect - Returns the bounding rect of the shape and its blobs Purpose: This calculates the bounding box of the shape's path and adds in the influence of the selection blobs. It does not consider if the blobs are visible or not, it just gives the bounding box that they would occupy if they were visible ********************************************************************************************/ DocRect NodeShadow::GetBlobBoundingRect() { // Find the Shapes bounding rectangle DocRect Rect = GetBoundingRect(); BlobManager * pBlobMgr = GetApplication()->GetBlobManager(); Rect.lo.x -= pBlobMgr->GetBlobSize(); Rect.lo.y -= pBlobMgr->GetBlobSize(); Rect.hi.x += pBlobMgr->GetBlobSize(); Rect.hi.y += pBlobMgr->GetBlobSize(); // Make sure we include the Bounds of our children IncludeChildrensBoundingRects(&Rect); return Rect; } /******************************************************************************************** > virtual UINT32 NodeShadow::GetNodeSize() const Author: Peter_Arnold (Xara Group Ltd) Created: 16/11/94 Returns: The size of the node in bytes Purpose: For finding the size of the node SeeAlso: Node::GetSubtreeSize ********************************************************************************************/ UINT32 NodeShadow::GetNodeSize() const { return (sizeof(NodeShadow)); } /******************************************************************************************** > BOOL NodeShadow::OnClick( DocCoord PointerPos, ClickType Click, ClickModifiers ClickMods, Spread* pSpread) Author: Peter_Arnold (Xara Group Ltd) Created: 16/11/94 Inputs: PointerPos - The Location of the mouse pointer at the time of the click Click - The type of click received (single, double, drag etc) ClickMods - The modifiers to the click (eg shift, control etc ) Returns: BOOL - TRUE if the node claims the click as its own and FALSE if it is not interested in the click Purpose: Allows the QuickShape to respond to clicks by selecting its blobs or starting drags etc. ********************************************************************************************/ BOOL NodeShadow::OnClick( DocCoord PointerPos, ClickType Click, ClickModifiers ClickMods, Spread* pSpread) { // did not use the click return FALSE; } /******************************************************************************************** > virtual BOOL NodeShadow::CanBecomeA(BecomeA* pBecomeA) Author: Peter_Arnold (Xara Group Ltd) Created: 16/11/94 Inputs: InkClass: The class of object pNumObjects = ptr to place number of objects of type pClass that will be created (Note: can be NULL). *pNumObects in undefined on entry Returns: TRUE if the node, or any of its children can transmogrify themselves to become an InkClass object Purpose: This function is used by the convert to shapes operation. It determines if the node or any of its children can convert themselves into an InkClass object. The number you put into pNumObjects (if it's not NULL) should exactly equal the total number of pClass objects you create. It should NOT contain any additional objects you may produce such as group objects for containing the pClass object, or attributes. Also, the entry value of *pNumObjects cannot be assumed to be 0. ********************************************************************************************/ BOOL NodeShadow::CanBecomeA(BecomeA* pBecomeA) { // The NodeRegularShape can become a NodePath if (pBecomeA->BAPath()) { pBecomeA->AddCount(1); return TRUE; } return FALSE; } /******************************************************************************************** > virtual BOOL NodeShadow::DoBecomeA(BecomeA* pBecomeA) Author: Peter_Arnold (Xara Group Ltd) Created: 25/11/94 Inputs: pBecomeA = ptr to a class that contains all the info needed to become a new type of node. Outputs: - Returns: TRUE if the object has been transformed, FALSE if we run out of memory Purpose: Transforms the object into another type of object. SeeAlso: NodeRegularShape::CanBecomeA ********************************************************************************************/ BOOL NodeShadow::DoBecomeA(BecomeA* pBecomeA) { // Check for a NULL entry param ERROR2IF(pBecomeA == NULL,FALSE,"pBecomeA is NULL"); // This lump checks that the Reason is one that we understand // It also makes sure that we don't have a NULL UndoOp ptr BOOL ValidReason = (pBecomeA->GetReason() == BECOMEA_REPLACE || pBecomeA->GetReason() == BECOMEA_PASSBACK); ERROR2IF_PF(!ValidReason,FALSE,("Unkown BecomeA reason %d",pBecomeA->GetReason())); // New behaviour: Leave shadow in the tree if the BecomeA claims // it will leave the result node in position in the tree if (pBecomeA->ResultsStayInPlace() && pBecomeA->IsCombineBecomeA()) { return TRUE; } Node * pAttrNode = NULL; BitmapTranspFillAttribute * pTranspBitmapFill = NULL; NodeAttribute * pAttr = NULL; BOOL bInsertNew = TRUE; // By default we will insert a new node if (RenderBitmapForDisplay()) ApplyProfileToShadowBitmap(); else bInsertNew = FALSE; // Failed to generate the shadow bitmap // if we're replacing we need to create a normal node path if (pBecomeA->GetReason() == BECOMEA_REPLACE) { DocRect dr(0, 0, 0, 0); m_Path.GetTrueBoundingRect(&dr); DocCoord dc1(dr.lo.x, dr.lo.y); DocCoord dc2(dr.hi.x, dr.lo.y); DocCoord dc3(dr.lo.x, dr.hi.y); UINT32 startTrans = GetTransp(); UINT32 endTrans = 255; DocColour TransparentColour(COLOUR_NONE); AttrStrokeColour * pAttrStrokeColour = NULL; CCAttrMap * pAttrMap = NULL; // Get the current Transparency value to use! UndoableOperation* pUndoOp = pBecomeA->GetUndoOp(); // We need to make sure that all the groups we may have produced have their attrs correctly // factored out. This is so the final factoring out done by the NodeBlend node will be able // to complete the job. // Without this step, leaf nodes can have common child attrs that should be factored up the // to a higher level. Node* pNode = this->FindNext(); while (pNode != NULL) { if (IS_A(pNode,NodeGroup)) { if (pUndoOp) { if (!pUndoOp->DoFactorOutCommonChildAttributes((NodeGroup*)pNode)) return FALSE; } else { if (!((NodeGroup*)pNode)->FactorOutCommonChildAttributes()) return FALSE; } } pNode = pNode->FindNext(); } if (bInsertNew) { // Make a new node to put the shadow bitmap into NodePath* pNodePath; ALLOC_WITH_FAIL(pNodePath, (new NodePath), pUndoOp); if (pNodePath == NULL) return FALSE; // apply the attribute map pAttrMap = CCAttrMap::MakeAppliedAttrMap(this); pNodePath->ApplyAttributes(pAttrMap, TRUE); delete pAttrMap; // remove all the appropriate attributes from this node pAttrNode = pNodePath->FindFirstChild(); while (pAttrNode) { if (pAttrNode->IsAnAttribute()) { pAttr = (NodeAttribute *)pAttrNode; if (pAttr->IsAStrokeColour() || pAttr->IsATranspFill() || pAttr->IS_KIND_OF(AttrTranspFillMapping)) { pAttrNode = pAttrNode->FindNext(); pAttr->UnlinkNodeFromTree(); delete pAttr; } else { pAttrNode = pAttrNode->FindNext(); } } else { pAttrNode = pAttrNode->FindNext(); } } // Setup the new Rect node that is to be replacing the shadow node. pNodePath->InkPath.Initialise(); pNodePath->InkPath.CloneFrom(m_Path); pNodePath->InkPath.IsFilled = TRUE; pNodePath->InkPath.TryToClose(); pNodePath->InkPath.InitialiseFlags(0, pNodePath->InkPath.GetNumCoords()-1); // set up the bitmap transparency fill AttrBitmapTranspFill * pAttrBitmapTranspFill = NULL; ALLOC_WITH_FAIL(pAttrBitmapTranspFill , (new AttrBitmapTranspFill), pUndoOp); pTranspBitmapFill = (BitmapTranspFillAttribute *)(pAttrBitmapTranspFill ->GetAttributeValue()); pTranspBitmapFill->SetStartPoint(&dc1); pTranspBitmapFill->SetEndPoint(&dc2); pTranspBitmapFill->SetEndPoint2(&dc3); pTranspBitmapFill->SetTesselation(RT_Simple); pTranspBitmapFill->SetTranspType(TT_Mix); pTranspBitmapFill->SetStartTransp(&startTrans); pTranspBitmapFill->SetEndTransp(&endTrans); pTranspBitmapFill->AttachBitmap(m_ShadowBitmap); // attach it to the node pAttrBitmapTranspFill->AttachNode(pNodePath, FIRSTCHILD); // ensure the line colour is transparent ALLOC_WITH_FAIL(pAttrStrokeColour, (new AttrStrokeColour), pUndoOp); pAttrStrokeColour->Value.SetStartColour(&TransparentColour); pAttrStrokeColour->AttachNode(pNodePath, FIRSTCHILD); AttrTranspFillMappingLinear * pFillMapping = NULL; ALLOC_WITH_FAIL(pFillMapping, new AttrTranspFillMappingLinear, pUndoOp); pFillMapping->Value.Repeat = 1; pFillMapping->AttachNode(pNodePath, FIRSTCHILD); // Insert the NodeGroup where the NodeBlend used to be if (pUndoOp) { if (!pUndoOp->DoInsertNewNode(pNodePath,this,PREV,TRUE,FALSE,FALSE,TRUE)) return FALSE; } else { pNodePath->AttachNode(this, PREV); pNodePath->NormaliseAttributes(); } CCAttrMap ThisMap; CCAttrMap* pThisMap = NULL; if (pNodePath->FindAppliedAttributes(&ThisMap)) pThisMap = ThisMap.Copy(); pBecomeA->PassBack(pNodePath, this, pThisMap); } if (pUndoOp) { NodeHidden* pHiddenNodeShadower = NULL; // hide this node if (!pUndoOp->DoHideNode(this, TRUE, &pHiddenNodeShadower, TRUE)) return FALSE; } else { CascadeDelete(); delete this; } } else { return TRUE; } return TRUE; } /******************************************************************************************** > NodePath * NodeShadow::CreateShadowPassbackNode(); Author: David_McClarnon (Xara Group Ltd) Created: 20/11/96 Inputs: Returns: NULL for failure, otherwise the new shadow passback node Purpose: Creates the shadow passback node for the DoBeomeA - BECOMEA_PASSBACK mechanism SeeAlso: NodeShadow::DoBecomeA ********************************************************************************************/ NodePath * NodeShadow::CreateShadowPassbackNode() { NodePath *pPassbackNode = new NodePath(); ERRORIF(pPassbackNode == NULL, _R(IDE_NOMORE_MEMORY), NULL); pPassbackNode->InkPath.Initialise(); NodeRenderableInk * pInk = (NodeRenderableInk *)FindNext(CC_RUNTIME_CLASS(NodeRenderableInk)); NodeShadowBecomeA MyBecomeA(BECOMEA_PASSBACK, CC_RUNTIME_CLASS(NodePath), NULL, FALSE, &(pPassbackNode->InkPath)); while (pInk) { if (pInk->CanBecomeA(&MyBecomeA)) { pInk->DoBecomeA(&MyBecomeA); } pInk = (NodeRenderableInk *)pInk->FindNext(CC_RUNTIME_CLASS(NodeRenderableInk)); } pPassbackNode->InkPath.InitialiseFlags(); return pPassbackNode; } /*********************************************************************************************** > BOOL NodeShadow::RenderBitmapForDisplay(RenderRegion* pRegion, List* pList = NULL) Author: David_McClarnon (Xara Group Ltd) Created: 19/4/2000 Inputs: pRegion the render region to take the values from pList list of SumAllPathsElem's - if non-NULL, then we'll use this list as our source of paths for shadowing. Outputs: Returns: TRUE - success FALSE - failure Purpose: Renders the shadow bitmap to the correct dpi Notes: Karim 03/07/2000 To use the old shadow rendering code, comment out the USE_NEW_SHADOW_CODE define up top. Note that as time progresses, the odds on something or other breaking the old code do grow. Also, sorry about lumping everything into one function - at some point, this function should probably be hacked apart into several smaller, more manageable pieces. Errors: - Scope: protected **********************************************************************************************/ BOOL NodeShadow::RenderBitmapForDisplay(RenderRegion* pRegion, List* pList) { // the shadow bitmap is cached, using the last scaled pixel width and quality settings. MILLIPOINT CurrentPixWidth = pRegion->GetScaledPixelWidth(); enum Quality::Fill CurrentQuality = pRegion->RRQuality.GetFillQuality(); if (m_ShadowBitmap != NULL) if (m_LastRequestedPixWidth == CurrentPixWidth && m_LastQualitySetting == CurrentQuality) { // TRACEUSER( "Gerry", _T("RenderBitmapForDisplay# Same as last\n")); return TRUE; } // no need to regenerate the bitmap if quality is below a certain level. if (CurrentQuality < (enum Quality::Fill)Quality::Solid) { // TRACEUSER( "Gerry", _T("RenderBitmapForDisplay# Quality too low\n")); return TRUE; } // we can only regenerate the bitmap if we're given a GRenderRegion. // don't return a failure code, as RRs such as ScanningRR may need to dummy-render us. if (!pRegion->IS_KIND_OF(GRenderRegion)) { // TRACEUSER( "Gerry", _T("RenderBitmapForDisplay# Not a GRenderRegion\n")); return TRUE; } // Bodge for effects that might change their bounds... // Floor shadows position themselves based on the bounding rect of the shadowed node // But the shadowed node might be an Effect that hasn't been generated yet and so // still has expanded bounds... // So we must ensure that the effect is rendered and knows its bounds before // we create and render the shadow // Note that this is not a huge overhead because the effect processing would have // taken place inside GetSilhouette bitmap anyway. Instead that function will get // the cached results of this render. NodeShadowController* pController = (NodeShadowController*)GetParentController(); if (pController && pController->GetShadowType()==SHADOWTYPE_FLOOR) { // Check whether Ink node is an effect without a processed bitmap NodeRenderableInk* pInk = pController->GetInkNodeFromController(); if (pInk && pInk->IsEffect() && ((NodeEffect*)pInk)->IsBitmapEffect()) { NodeBitmapEffect* pEffect = (NodeBitmapEffect*)pInk; BOOL bOK = pEffect->GetProcessedBitmap(FALSE, NULL, NULL, NULL); if (!bOK) { pRegion->SaveContext(); // Direct capture and ignore the bitmap handed back to us // so that we avoid creating unused capture bitmap CaptureFlags caFlags = CaptureFlags(cfLOCKEDTRANSPARENT | cfUNCLIP | cfALLOWDIRECT | cfPIXWIDTHSCALE); DocRect CaptureRect = pEffect->GetBoundingRect(); pRegion->StartCapture(this, CaptureRect, CAPTUREINFO(ctNESTABLE, caFlags), TRUE, FALSE, 0, pEffect); pRegion->RenderTree(pEffect, FALSE); pRegion->StopCapture(this, FALSE, TRUE); pRegion->RestoreContext(); } } } // BitmapShadower does all the silhouette creation work, and exposes methods // which return useful shadow info, as well as the silhouette bitmap. BitmapShadower* pBmpShadower = BitmapShadower::CreateSilhouetteBitmap((NodeShadowController*)GetParentController(), (GRenderRegion*)pRegion, pList); if (pBmpShadower == NULL) { // TRACEUSER( "Gerry", _T("RenderBitmapForDisplay# CreateSilhouetteBitmap failed\n")); return FALSE; } ///////////////////////////////////////////////////////////////////////////////////////// // Post-process the 32bpp 'silhouette' bitmap, to get the 8bpp greyscale shadow bitmap. ///////////////////////////////////////////////////////////////////////////////////////// // initialise the transparency fill attribute and clear our caches. SetupTransFill(); // get the silhouette bitmap out of the shadower. KernelBitmap* pSilhouetteBmp = pBmpShadower->GetSilhouetteBitmap(); // generate the shadow bitmap for the required blur diameter. const double pixBlurDiameter = pBmpShadower->GetPixBlurDiameter(); BOOL bOK = GenerateShadowBitmap(pSilhouetteBmp, pixBlurDiameter); if (bOK) { // if desired, attach a debug copy of the final shadow bitmap within the bitmap gallery. #ifdef ATTACH_SHADOW_BMP m_ShadowBitmap->AttachDebugCopyToCurrentDocument("Shadow Final Bitmap"); #endif // TRACEUSER( "Gerry", _T("RenderBitmapForDisplay# GenerateShadowBitmap ok\n")); // ok, we're done! now remember the requested and actual pix-per-inch settings // at which we have just successfully created the new shadow bitmap. m_LastRequestedPixWidth = CurrentPixWidth; m_LastQualitySetting = CurrentQuality; m_LastActualPixWidth = (CurrentPixWidth > 0) ? pBmpShadower->GetActualPixelWidth() : -1; // one last thing - create the path which the shadow bitmap-transparency is drawn into. DocRect drBounds = pBmpShadower->GetBitmapBounds(); CreateBitmapPath(drBounds, m_LastActualPixWidth, pixBlurDiameter); // free up the shadower, now we've finished using it. delete pBmpShadower; pBmpShadower = NULL; } // else // { // TRACEUSER( "Gerry", _T("RenderBitmapForDisplay# GenerateShadowBitmap failed\n")); // } if (pSilhouetteBmp) { // TRACE( _T("Warning - NodeShadow::RenderBitmapForDisplay not deleting bitmap\n") ); delete pSilhouetteBmp; pSilhouetteBmp = NULL; } return bOK; } /******************************************************************************************** > BOOL NodeShadow::RenderBitmapForDisplay() Author: Karim_MacDonald (Xara Group Ltd) Created: 27/10/2000 Outputs: Our shadow bitmap and path will be set up ready for rendering, if successful. Returns: TRUE if successful, FALSE otherwise. Purpose: A simpler overload of the complicated function above. Simpler because this function: * Does not use a render-region - it queries the current view for render information instead. * Does not do any caching - providing we don't fail, we _will_ render a new shadow whenever you call us, so do NOT use with abandon! Notes: In order to call this function, we must be installed in the tree. Somewhere deep in the innards of this routine, we need to access the Spread, in order to get the rendering origin right. See also: RenderBitmapForDisplay above, which is more fully featured and should be the function called in normal use. ********************************************************************************************/ BOOL NodeShadow::RenderBitmapForDisplay() { // Create a bitmap-shadower containing the shadow silhouette. BitmapShadower* pBmpShadower = BitmapShadower::CreateSilhouetteBitmap((NodeShadowController*)GetParentController()); if (pBmpShadower == NULL) return FALSE; // initialise the transparency fill attribute and clear our caches. SetupTransFill(); // get the silhouette bitmap out of the shadower. KernelBitmap* pSilhouetteBmp = pBmpShadower->GetSilhouetteBitmap(); // Blur & grayscale the silhouette, to get the shadow bitmap. const double pixBlurDiameter = pBmpShadower->GetPixBlurDiameter(); BOOL bOK = GenerateShadowBitmap(pSilhouetteBmp, pixBlurDiameter); if (bOK) { // Tell the bitmap what resolution people should render it at. // Doesn't affect rendering - just useful if it ends up in the bitmap gallery. LPBITMAPINFOHEADER pShadowHeader = m_ShadowBitmap->GetBitmapInfoHeader(); const double PixPerMetre = MILLIPOINTS_PER_INCH * INCHES_PER_METRE / pBmpShadower->GetActualPixelWidth(); pShadowHeader->biXPelsPerMeter = pShadowHeader->biYPelsPerMeter = (INT32)(PixPerMetre + 0.5); // attach a debug copy to the bitmap gallery, if required. #ifdef ATTACH_SHADOW_BMP m_ShadowBitmap->AttachDebugCopyToCurrentDocument("Shadow Final Bitmap"); #endif // generate the path to hold the shadow bitmap. DocRect drBounds = pBmpShadower->GetBitmapBounds(); CreateBitmapPath(drBounds, pBmpShadower->GetActualPixelWidth(), pixBlurDiameter); // the cached bitmap is now invalid, so mark it so via the last pixel width. m_LastRequestedPixWidth = -1; // free up our used bitmap-shadower. delete pBmpShadower; pBmpShadower = NULL; } if (pSilhouetteBmp) { delete pSilhouetteBmp; pSilhouetteBmp = NULL; } return bOK; } /******************************************************************************************** > BOOL NodeShadow::ApplyProfileToShadowBitmap() Author: Karim_MacDonald (Xara Group Ltd) Created: 30/10/2000 Outputs: The palette for m_ShadowBitmap will be set correctly, to use the information from the shadow's profile control. Returns: TRUE if successful, FALSE otherwise. Errors: ERROR3 if m_ShadowBitmap is NULL or our profile control gives a bad result. ********************************************************************************************/ BOOL NodeShadow::ApplyProfileToShadowBitmap() { if (m_ShadowBitmap == NULL) { ERROR3("NodeShadow::ApplyProfileToShadowBitmap; NULL shadow bitmap!"); return FALSE; } LPRGBQUAD pPalette = m_ShadowBitmap->GetPaletteForBitmap(); AFp res; m_BiasGain.SetBias(-m_BiasGain.GetBias()); for (INT32 i = 0 ; i < 256; i++) { double d = ((double)i) / 255; res = m_BiasGain.MapZeroToOne((AFp)(d)); res *= 255.0; ERROR3IF(res < 0 || res > 255, "NodeShadow::ApplyProfileToShadowBitmap; Bad result from profile control!"); pPalette[i].rgbRed = pPalette[i].rgbBlue = pPalette[i].rgbGreen = (BYTE)res; } m_BiasGain.SetBias(-m_BiasGain.GetBias()); return TRUE; } /*********************************************************************************************** > BOOL NodeShadow::Snap(DocCoord* pDocCoord) Author: Peter_Arnold (Xara Group Ltd) Created: 21/12/94 Inputs: pDocCoord = a coord in Spread coords Outputs: If the point is snapped then pDocCoord will contain the point of attraction. Returns: TRUE - the DocCoord has been snapped to the shapes path. FALSE - the DocCoord has not been processed. Purpose: Snaps to given coord to the nearest point on the shapes render path. If it is not appropriate to snap the coord to the shape (at the moment this means the coord is too far awawy), then FALSE is returned. Errors: - Scope: public **********************************************************************************************/ BOOL NodeShadow::Snap(DocCoord* pDocCoord) { return FALSE; } /*********************************************************************************************** > BOOL NodeShadow::Snap(DocRect* pDocRect,const DocCoord& PrevCoord,const DocCoord& CurCoord) Author: Mark_Neves (Xara Group Ltd) Created: 21/9/94 Inputs: pDocCoord - the rectangle to snap StartDrag - Start coord of drag EndDrag - End coord of drag Outputs: Returns: TRUE - the DocRect been snapped to the grid. FALSE - the DocRect has not been processed. Purpose: Snaps the given rect to the nearest position on the grid, preserving its width and height. The coords of the rect used for the snapping are determined by the PrevCoord and CurCoord coords supplied. This is done to allow the user to control how a selection rectangle is snapped to the grid by the direction of his/her last mouse movement. To force the bottom left hand corner of the rect to be snapped, supply PrevCoord=(0,0) and CurCoord(-1,-1). Scope: public **********************************************************************************************/ BOOL NodeShadow::Snap(DocRect* pDocRect,const DocCoord& PrevCoord,const DocCoord& CurCoord) { return FALSE; } /******************************************************************************************** > BOOL NodeShadow::SnapToCoords(DocCoord* pDocCoord) Author: Peter_Arnold (Xara Group Ltd) Created: 29/3/95 Inputs: pDocCoord - the coord to try and snap to Outputs: pDocCoord - The snapped coord, if it was close enough to any of the magnetic points for a rectangle. Returns: TRUE if it was snapped, FALSE if not Purpose: Snaps the point magnetically to the the significant points of the shape ********************************************************************************************/ BOOL NodeShadow::SnapToCoords(DocCoord* pDocCoord) { return FALSE; } /******************************************************************************************** > virtual String NodeShadow::Describe(BOOL Plural, BOOL Verbose = TRUE) Author: Peter_Arnold (Xara Group Ltd) Created: 22/11/94 Inputs: Plural: Flag indicating if the string description should be plural or singular. Returns: Description of the object Purpose: To return a description of the NodeRegularShape object in either the singular or the plural. This method is called by the DescribeRange method. The description will always begin with a lower case letter. Errors: A resource exception will be thrown if a problem occurs when loading the string resource. ********************************************************************************************/ String NodeShadow::Describe(BOOL Plural, BOOL Verbose) { if (Plural) { String str(_R(IDS_SHADOWNODENAME)); str += _T("s"); return(str); } else return(String(_R(IDS_SHADOWNODENAME))); } /******************************************************************************************** > virtual BOOL NodeShadow::ExportRender ( RenderRegion *pRegion ) Author: Graeme_Sutherland (Xara Group Ltd) Created: 14/12/99 Inputs: pRegion - A pointer to a RenderRegion. Outputs: After this call, our cached shadow-rendering information may have changed. Returns: TRUE if we successfully ExportRender()'ed the shadow, FALSE otherwise. Purpose: Used to output shadows into various export filters. Shadows are usually exported as a bitmap or a transparent bitmap. For more info, see the appropriate implementation of ExportShadow() in the filter render-regions. Notes: Rewritten by Karim 24/01/2000 for clarity and to stop unnecessary rendering. ********************************************************************************************/ BOOL NodeShadow::ExportRender( RenderRegion *pRegion ) { enum ShadowFilterType { NONE, FLASH, CMX, AIEPS }; ShadowFilterType ftExportFilter = pRegion->IS_KIND_OF(FlashRenderRegion) ? FLASH : PORTNOTE("cmx","NodeShadow::ExportRender - ignore CMX ") #ifndef EXCLUDE_FROM_XARALX pRegion->IS_KIND_OF(CMXRenderRegion) ? CMX : #endif pRegion->IS_KIND_OF(AIEPSRenderRegion) ? AIEPS : NONE; // Render our shadow bitmap at the export resolution. // Note that we choose to continue if ApplyProfileToShadowBitmap() fails. if (ftExportFilter != NONE) { if (!RenderBitmapForDisplay()) return FALSE; ApplyProfileToShadowBitmap(); } // OK, export our shadow! BOOL bResult = FALSE; switch (ftExportFilter) { // Flash export. case FLASH: { FlashRenderRegion *pFlash = static_cast ( pRegion ); bResult = pFlash->ExportShadow( &m_Path, m_ShadowBitmap->GetActualBitmap (), GetTransp() ); } break; PORTNOTE("cmx","NodeShadow::ExportRender - ignore CMX ") #ifndef EXCLUDE_FROM_XARALX // CMX export. case CMX: { CMXRenderRegion *pCMX = static_cast ( pRegion ); bResult = pCMX->ExportShadow( m_ShadowBitmap->GetActualBitmap (), GetTransp(), GetBoundingRect () ); } break; #endif // AI EPS export. case AIEPS: { DocRect brect = GetBoundingRect(); AIEPSRenderRegion *pAIEPS = static_cast ( pRegion ); bResult = pAIEPS->ExportShadow( m_ShadowBitmap->GetActualBitmap (), GetTransp(), brect ); } break; // Unrecognised filter. default: bResult = FALSE; break; } return bResult; } /******************************************************************************************** > BOOL NodeShadow::WritePreChildrenNative(BaseCamelotFilter * pFilter) BOOL NodeShadow::WritePreChildrenWeb(BaseCamelotFilter * pFilter) Author: Andy_Hayward (Xara Group Ltd) Created: 03/06/96 Inputs: pFilter - filter to use Returns: Boolean value indicating success Purpose: Writes this regular shape to the filter NodeShadows do NOT save anything out in the current version of Camelot. For now though, do save out just the place holder node Notes: Karim 06/02/2001 Added code to write out the shadow Darkness. ********************************************************************************************/ BOOL NodeShadow::WritePreChildrenNative(BaseCamelotFilter * pFilter) { #ifdef DO_EXPORT ERROR2IF(pFilter == NULL,FALSE,"NULL filter param"); BOOL ok = TRUE; CXaraFileRecord Rec(TAG_SHADOW, TAG_SHADOW_SIZE); // INT32 Blur = 0; Rec.Init(); if (ok) ok = Rec.WriteDOUBLE((double)m_BiasGain.GetBias()); if (ok) ok = Rec.WriteDOUBLE((double)m_BiasGain.GetGain()); if (ok) ok = Rec.WriteDOUBLE(GetDarkness()); if (ok) ok = pFilter->Write(&Rec); return ok; #else return FALSE; #endif } BOOL NodeShadow::WritePreChildrenWeb(BaseCamelotFilter * pFilter) { return WritePreChildrenNative(pFilter); } /******************************************************************************************** > virtual BOOL NodeShadow::CanWriteChildrenWeb(BaseCamelotFilter* pFilter) Author: Neville_Humphrys (Xara Group Ltd) Created: 5/12/97 Inputs: pFilter = ptr to the filter to write to Returns: TRUE if children can be written automatically, FALSE otherwise Purpose: This allows a node to stop the filter from writing out the child nodes automatically. NodeShadows do NOT save anything out in the current version of Camelot. Therefore, none of it's children can be saved out either. For now though, do save the childern ********************************************************************************************/ BOOL NodeShadow::CanWriteChildrenWeb(BaseCamelotFilter* pFilter) { return TRUE; } //------------------------------------------------------------------ // See comments for NodeMoulder::CanWriteChildrenNative(BaseCamelotFilter* pFilter) BOOL NodeShadow::CanWriteChildrenNative(BaseCamelotFilter* pFilter) { return CanWriteChildrenWeb(pFilter); } /******************************************************************************************* > void NodeShadow::PreExportRender(RenderRegion* pRegion) Author: Tim_Browse (Xara Group Ltd) Created: 2/2/95 Inputs: pRegion - points to the export render region Outputs: - Returns: - Purpose: This function is called just before the shape is exported. It outputs the shapes start token Errors: - SeeAlso: NodeRegularShape::ExportRender ********************************************************************************************/ void NodeShadow::PreExportRender(RenderRegion* pRegion) { } /******************************************************************************************** > virtual BOOL NodeShadow::ReleaseCached( BOOL bAndParents = TRUE, BOOL bAndChildren = TRUE, BOOL bSelf = TRUE, BOOL bAndDerived = TRUE) Author: Phil_Martin (Xara Group Ltd) Created: 02/12/2004 Returns: TRUE if captured data was cached Purpose: Protected Helper function Use the CaptureManager to capture the results of rendering, cache them and associate them with this Ink node SeeAlso: NodeRenderableInk::RenderCached, CaptureCached ********************************************************************************************/ BOOL NodeShadow::ReleaseCached(BOOL bAndParents, BOOL bAndChildren, BOOL bSelf, BOOL bAndDerived) { // Don't release cached data if the release request is the result of transformation // re-rendering because we can transform our cached data! if (!IsDragged() && bSelf) { DeleteCache(); } // If we should release our children's caches as well... if (bAndChildren) { Node* pChild = FindFirstChild(); while (pChild) { if (pChild->IsBounded()) ((NodeRenderableBounded*)pChild)->ReleaseCached(FALSE, TRUE, TRUE, TRUE); pChild = pChild->FindNext(); } } // If I can't be cached, neither can my parent... if (bAndParents && FindParent() && FindParent()->IsBounded()) ((NodeRenderableBounded*)FindParent())->ReleaseCached(TRUE, FALSE, TRUE, TRUE); return TRUE; } /******************************************************************************************* > void NodeShadow::DeleteCache() Author: Peter_Arnold (Xara Group Ltd) Created: 4/12/96 Inputs: - Outputs: - Returns: - Purpose: Deletes all cached shadow paths ********************************************************************************************/ void NodeShadow::DeleteCache() { // TRACEUSER( "Gerry", _T("DeleteCache\n")); m_LastRequestedPixWidth = -1; if (m_ShadowBitmap) { delete m_ShadowBitmap; m_ShadowBitmap = NULL; } if (m_pBMPTransFill) { delete m_pBMPTransFill; m_pBMPTransFill = NULL; } if (m_pShadower) { delete m_pShadower; m_pShadower = NULL; } } /******************************************************************************************* > void NodeShadow::DeleteAndRefreshCache() Author: Peter_Arnold (Xara Group Ltd) Created: 4/12/96 Inputs: - Outputs: - Returns: - Purpose: Deletes all cached shadow paths and regenerates them ********************************************************************************************/ void NodeShadow::DeleteAndRefreshCache() { DeleteCache(); } /******************************************************************************************** > virtual NodeCompound* NodeShadow::GetParentController() const Author: Peter_Arnold (Xara Group Ltd) Created: 4/12/96 Inputs: - Returns: Pointer to parent shadow controller (NULL if not found!) Purpose: Returns a type correct pointer to the parent shadow controller ********************************************************************************************/ NodeCompound* NodeShadow::GetParentController() const { NodeShadowController* pBob = (NodeShadowController*)FindParent(); if (pBob == NULL || !IS_A(pBob, NodeShadowController)) { // ERROR3("NodeShadow::GetParentController; cannot find NodeShadowController parent!"); pBob = NULL; } return pBob; } /******************************************************************************************** > void NodeShadow::RenderBitmap(RenderRegion* pRender) Author: Mark_Howitt (Xara Group Ltd) Created: 5/8/98 Inputs: pRender - The render region to use for rendering Purpose: Renders the shadow using the bitmap shadowing system. ********************************************************************************************/ void NodeShadow::RenderBitmap(RenderRegion* pRender) { // If we don't have a trans fill then try to create one if (m_pBMPTransFill == NULL) { // TRACEUSER( "Gerry", _T("RenderBitmap# TransFill is NULL\n")); SetupTransFill(); } // quit if we still have no bitmap transp fill. if (m_pBMPTransFill == NULL) { // TRACEUSER( "Gerry", _T("RenderBitmap# TransFill is still NULL!!! *********\n")); return; } // if we have no shadow bitmap, then create a dummy one. if (m_ShadowBitmap == NULL) { // TRACEUSER( "Gerry", _T("RenderBitmap# Creating dummy bitmap!!! ***********\n")); m_ShadowBitmap = new KernelBitmap(4, 4, 8, 0, TRUE); ::memset(m_ShadowBitmap->GetBitmapBits(), 0, DIBUtil::ScanlineSize(4, 8) * 4); m_pBMPTransFill->BitmapRef.SetBitmap(m_ShadowBitmap); } ApplyProfileToShadowBitmap(); // Get the current Transparency value to use! UINT32 startTrans = GetTransp(); // prepare a non-repeating bitmap-transp-fill type. AttrTranspFillMappingLinear FillMapping; FillMapping.Value.Repeat = 1; // do the bitmap fitting. DocRect dr(0,0,0,0); m_Path.GetTrueBoundingRect(&dr); DocCoord Centre(dr.lo.x, dr.lo.y); DocCoord HiX(dr.hi.x, dr.lo.y); DocCoord HiY(dr.lo.x, dr.hi.y); // m_pBMPTransFill->SetStartPoint(&Centre); // m_pBMPTransFill->SetEndPoint(&HiX); // m_pBMPTransFill->SetEndPoint2(&HiY); DocCoord* pCoords = m_Path.GetCoordArray(); // These lines allow cached bitmap to be transformed m_pBMPTransFill->SetStartPoint(&pCoords[3]); m_pBMPTransFill->SetEndPoint(&pCoords[2]); m_pBMPTransFill->SetEndPoint2(&pCoords[0]); m_pBMPTransFill->SetStartTransp(&startTrans); // ok, do all the rendering. pRender->SaveContext(); pRender->SetLineColour(COLOUR_NONE); m_pBMPTransFill->Render(pRender); FillMapping.Render(pRender); // DocRect BoundsRect = m_Path.GetBoundingRect(); // TRACEUSER( "Gerry", _T("Rendering shadow = (%d, %d) - (%d, %d)\n"), BoundsRect.lox, BoundsRect.loy, BoundsRect.hix, BoundsRect.hiy); pRender->DrawPath(&m_Path); pRender->RestoreContext(); } /******************************************************************************************** > BOOL NodeShadow::GenerateShadow() Author: David_McClarnon (Xara Group Ltd) Created: 16/9/98 Inputs: Purpose: Can be called externally to set up this node with its shadow in the correct place ********************************************************************************************/ BOOL NodeShadow::GenerateShadow() { // find my parent & test to see if I've got anything to shadow NodeShadowController * pControl = (NodeShadowController *)FindParent(); if (pControl) { if (pControl->GetInsideBoundingRect().IsEmpty()) { return FALSE; } } else { return FALSE; } // ensure a render m_bHaveTransformed = TRUE; m_RenderBitmaps = TRUE; // regen the shape path & everything !! SetupTransFill(); if (m_ShadowBitmap) { delete m_ShadowBitmap; m_ShadowBitmap = NULL; } ReleaseCached(); // set these two to false, as we've already generated the shadow m_RenderBitmaps = FALSE; m_bHaveTransformed = FALSE; m_Path.ClearPath(); if (pControl != NULL) { pControl->InvalidateBoundingRect(TRUE); InvalidateBoundingRect(); } return TRUE; } /******************************************************************************************** > BOOL NodeShadow::GenerateShadowForBlends(List * pList, RenderRegion * pRegion) Author: David_McClarnon (Xara Group Ltd) Created: 10/3/2000 Inputs: List of SumAllPathsElems (see blndhelp.h) Purpose: Creates the shadow using the given list Can take place inside of a render loop ********************************************************************************************/ BOOL NodeShadow::GenerateShadowForBlends(List * pList, RenderRegion * pRegion) { if (pList == NULL) return FALSE; else return RenderBitmapForDisplay(pRegion, pList); } /******************************************************************************************** > BOOL NodeShadow::GenerateShadowBitmap(double pixBlurDiameter) Author: Mark_Howitt (Xara Group Ltd) Created: 16/9/98 Inputs: INT32 Blurr : Current value of the blurr setting Returns: TRUE for Ok .. and FALSE for Not ok! Purpose: Generates a Bitmap Shadow from the current Selection Bitmap with the current Blurr setting. ********************************************************************************************/ BOOL NodeShadow::GenerateShadowBitmap(KernelBitmap* pSilhouetteBmp, double pixBlurDiameter) { if (pSilhouetteBmp == NULL) return FALSE; KernelBitmap * p8BitDIB = NULL; NodeShadowController * pController = (NodeShadowController*)GetParentController(); if (pController == NULL) return FALSE; if(m_RenderBitmaps || !m_ShadowBitmap || !m_pBMPTransFill) { // If we already have a shadow bitmap, get rid of it as it`s outa data! if(m_ShadowBitmap != NULL) { delete m_ShadowBitmap; m_ShadowBitmap = NULL; } // Set up a new kernel shadow bitmap with the selection bitmap as the reference to shadow with if (!m_pShadower) { m_pShadower = new CBitmapShadow; if (!m_pShadower) { ERROR3("Can't create shadower"); return FALSE; } } INT32 i = 0 ; CNativeSize ShadowSize( pSilhouetteBmp->GetWidth(), pSilhouetteBmp->GetHeight() ); // create the shadow switch (pController->GetShadowType()) { case SHADOWTYPE_WALL: { m_pShadower->CreateShadow(TRUE, 0, 0, pixBlurDiameter, 1.0, 0, 0, (UINT32*)pSilhouetteBmp->GetBitmapBits(), ShadowSize); } break; case SHADOWTYPE_FLOOR: { m_pShadower->CreateShadow(FALSE, pController->GetFloorShadowHeight(), pController->GetFloorShadowAngle() * 180.0 / PI, pixBlurDiameter, 1.0, 0, 0, (UINT32*)pSilhouetteBmp->GetBitmapBits(), ShadowSize); INT32 YOffset = 0; m_pShadower->GetShadowOffset(&m_BitmapXOffset, &YOffset); } break; case SHADOWTYPE_GLOW: { m_pShadower->CreateShadow(TRUE, 0, 0, pixBlurDiameter, 1.0, 0, 0, (UINT32*)pSilhouetteBmp->GetBitmapBits(), ShadowSize); } break; case SHADOWTYPE_FEATHER: { ERROR3("Unimplemented!"); // TODO: } break; default: ERROR3("Unknown Shadow type"); } INT32 BWidth = m_pShadower->GetWidth(); INT32 BHeight = m_pShadower->GetHeight(); // floor shadows are generated as full 32 bit dibs, whereas wall shadows are // generated as 8 bit dibs if (m_pShadower->GetDepth() == 32) { m_ShadowBitmap = new KernelBitmap(BWidth, BHeight, 24, 96, TRUE); if (!m_ShadowBitmap) { ERROR(_R(IDE_NOMORE_MEMORY), FALSE); } // change the shadow bitmap from being a 32 bit-DIB to a 24 bit-DIB BYTE * SrcPtr = ((LPBYTE)m_pShadower->GetBytes()) + 3; BYTE * DestPtr = (LPBYTE)m_ShadowBitmap->GetBitmapBits(); INT32 XSize = DIBUtil::ScanlineSize(BWidth, 24); INT32 XInc = XSize - (BWidth * 3); INT32 XPos = 0; INT32 size = BWidth * BHeight; for (i = 0 ; i < size; i++) { *(DestPtr + 0) = *(SrcPtr); *(DestPtr + 1) = *(SrcPtr); *(DestPtr + 2) = *(SrcPtr); XPos ++; if (XPos < BWidth) { DestPtr += 3; } else { XPos = 0; DestPtr += 3 + XInc; } SrcPtr += 4; } // change it into an 8-bit DIB, if not printing if (!pController->IsPrinting()) { if (Create8BitBitmap(m_ShadowBitmap, &p8BitDIB)) { delete m_ShadowBitmap; m_ShadowBitmap = p8BitDIB; } } } else if (m_pShadower->GetDepth() == 8) { // the shadowed bitmap is already 8 bit, therefore just make it into a kernel bitmap m_ShadowBitmap = new KernelBitmap(BWidth, BHeight, 8, 96, TRUE); if (!m_ShadowBitmap) { ERROR3("Can't create shadow bitmap"); return FALSE; } RGBQUAD * pQuad = NULL; pQuad = m_ShadowBitmap->GetPaletteForBitmap(); // make the palette for (INT32 i = 0 ; i <= 255; i++) { pQuad[i].rgbRed = i; pQuad[i].rgbBlue = i; pQuad[i].rgbGreen = i; } // copy the bits over memcpy(m_ShadowBitmap->GetBitmapBits(), m_pShadower->GetBytes(), DIBUtil::ScanlineSize(BWidth, 8) * BHeight); } else { ERROR3("Shadower has an unrecognised bmp depth!"); } // delete the shadower delete m_pShadower; m_pShadower = NULL; ERROR2IF(m_ShadowBitmap == NULL,FALSE,"Failed to create a shadow bitmap for shadowing!!!!"); // DMc // New Trans Bitmap Fill Attributes // ah, but has the bitmap fill been initialised ? if (!m_pBMPTransFill) { SetupTransFill(); } m_pBMPTransFill->BitmapRef.SetBitmap(m_ShadowBitmap); m_bHaveTransformed = TRUE; } return TRUE; } /******************************************************************************************** > BOOL NodeShadow::SetupTransFill(); Author: David_McClarnon (Xara Group Ltd) Created: 20/4/99 Inputs: Returns: TRUE = success, FALSE = fail Purpose: Sets up the transparency fill for this node ********************************************************************************************/ BOOL NodeShadow::SetupTransFill() { DeleteCache(); m_pBMPTransFill = new BitmapTranspFillAttribute; UINT32 EndTransp = 255; if(m_pBMPTransFill == NULL) { ERROR3("Arrhhh! Didn`t get an m_pBMPTransFill ptr for the shadow!"); } else { // Always have an end transparency of 100% m_pBMPTransFill->SetEndTransp(&EndTransp); // Always make the shadow bitmap non repeating! m_pBMPTransFill->SetTesselation(RT_Simple); } return TRUE; } /******************************************************************************************** > void NodeShadow::UpdateAfterWallOffset() Author: Karim_MacDonald (Xara Group Ltd) Created: 20/06/2000 Outputs: This NodeShadow may be translated so it is offset as defined by its parent NodeShdaowController. Its bounding box will also be invalidated and recalculated. Purpose: Updates our bounds and shadow path after our NodeShadowController parent has been told to move its wall shadow to a new offset. After a wall-shadow offset, the shadow needn't be regenerated, but simply translated. This method performs any necessary translation, so that the wall shadow is where the parent NodeShadowController says it should be. SeeAlso: NodeShadowController::SetWallShadowOffset(); ********************************************************************************************/ void NodeShadow::UpdateAfterWallOffset() { // update our path for the shadow bitmap-transparency. if (m_LastActualPixWidth > 0) { NodeShadowController* pControl = (NodeShadowController*)GetParentController(); if (pControl != NULL) { m_Path.ClearPath(); m_Path.CloneFrom(m_NonTranslatedPath); m_Path.IsFilled = TRUE; m_Path.Translate(pControl->GetWallShadowOffset()); } } else { // DEBUG: // TRACEUSER( "Karim", _T("NodeShadow::UpdateAfterWallOffset; Couldn't create shadow path!\n")); } // our bounds will likely be invalid after recalculating the shadow fill-path. InvalidateBoundingRect(); } /******************************************************************************************** > BOOL NodeShadow::PostImport() Author: Karim_MacDonald (Xara Group Ltd) Created: 07/02/2001 Purpose: Called immediately after this NodeShadow and all its children have been imported & installed in the tree, to ensure correct import of shadows from the old Xara X format: 1. Overwrite our shadow transparency, from that of any non-default attr-transp applied to us. 2. Delete all our attribute-children which we do not require. ********************************************************************************************/ BOOL NodeShadow::PostImport() { NodeAttribute* pAttrTransp; if (FindAppliedAttribute(CC_RUNTIME_CLASS(AttrTranspFillGeometry), &pAttrTransp) && !pAttrTransp->IsADefaultAttr()) { SetTransp( * ((AttrFillGeometry*)pAttrTransp)->GetStartTransp() ); } Node* pThisKid = FindFirstChild(); Node* pNextKid = NULL; while (pThisKid != NULL) { pNextKid = pThisKid->FindNext(); if (pThisKid->IsAnAttribute() && !RequiresAttrib((NodeAttribute*)pThisKid) ) { pThisKid->CascadeDelete(); delete pThisKid; } pThisKid = pNextKid; } return TRUE; } /******************************************************************************************** > BOOL NodeShadow::PostDuplicate(UndoableOperation* pOp) Author: David_McClarnon (Xara Group Ltd) Created: 16/4/99 Inputs: Returns: True if successful Purpose: Does post-import stuff ********************************************************************************************/ BOOL NodeShadow::PostDuplicate(UndoableOperation* pOp) { // force a regeneration of the shadow m_bAmCopying = FALSE; // GenerateShadow(); NodeRenderableInk::PostDuplicate(pOp); IsBoundingRectValid = FALSE; DocRect dr = GetBoundingRect(FALSE, FALSE); return TRUE; } /******************************************************************************************** > BOOL NodeShadow::Create8BitBitmap(KernelBitmap * pSrcBitmap, KernelBitmap * pDestBitmap); Author: David_McClarnon (Xara Group Ltd) Created: 16/4/99 Inputs: The source 24-bit greyscale bitmap Returns: True if successful Purpose: Returns an 8-bit bitmap out of the 24-bit greyscale bitmap ********************************************************************************************/ BOOL NodeShadow::Create8BitBitmap(KernelBitmap * pSrcBitmap, KernelBitmap ** ppDestBitmap) { if (!pSrcBitmap || !ppDestBitmap) { ERROR3("No source or destination bitmap defined"); return FALSE; } if (pSrcBitmap->GetBPP() != 24) { ERROR3("Source bitmap not 24 bit"); return FALSE; } UINT32 Width = pSrcBitmap->GetWidth(); UINT32 Height = pSrcBitmap->GetHeight(); // make the width a factor of 4 *ppDestBitmap = new KernelBitmap(Width, Height, 8, pSrcBitmap->GetHorizontalDPI(), TRUE); if (!ppDestBitmap) return FALSE; // ok, we've created both bitmaps now, so fill their bits ! BYTE * pDest = (*ppDestBitmap)->GetActualBitmap()->GetBitmapBits(); BYTE * pStartDest = pDest; BYTE * pSrc = pSrcBitmap->GetActualBitmap()->GetBitmapBits(); BYTE * pStartSrc = pSrc; INT32 size = pSrcBitmap->GetWidth() * pSrcBitmap->GetHeight(); UINT32 posX = 0; // get gavin's scanline size for 8-bit bitmaps UINT32 ScanlineSize24 = DIBUtil::ScanlineSize(Width, 24); UINT32 ScanlineSize8 = DIBUtil::ScanlineSize(Width, 8); // memset the destination so it's white memset(pDest, 0xff, ScanlineSize8 * Height); while (size > 0) { *pDest = *pSrc; pSrc += 3; pDest += 1; posX ++; if (posX >= Width) { pStartSrc += ScanlineSize24; pStartDest += ScanlineSize8; pSrc = pStartSrc; pDest = pStartDest; posX = 0; } size --; } // and do their palettes LPRGBQUAD pTranspPalette = (*ppDestBitmap)->GetPaletteForBitmap(); for (INT32 i = 0 ; i < 256; i++) { pTranspPalette->rgbBlue = (BYTE)i; pTranspPalette->rgbRed = (BYTE)i; pTranspPalette->rgbGreen = (BYTE)i; pTranspPalette->rgbReserved = 0x00; pTranspPalette ++; } return TRUE; } /******************************************************************************************** > void NodeShadow::CreateBitmapPath( const DocRect& drInkForShadowing, const double PixelWidth, const double pixBlurDiameter ) Author: Karim_MacDonald (Xara Group Ltd) Created: 15 September 2000 Inputs: drInkForShadowing the bounding rect of the object to be shadowed. PixelWidth the width of an onscreen pixel, in millipoints. pixBlurDiameter the exact penumbra width, in pixels. Outputs: m_NonTranslatedPath contains the path to render the shadow bmp into, but is centred on the object. m_Path contains the true path to render the shadow bmp into, ie m_NonTranslatedPath with any wall offset applied. Purpose: Create the path into which we will render our shadow bitmap: 1. Use PixelWidth together with the dimensions of our stored shadow bitmap, to work out the exact dimensions of the bitmap onscreen, in millipoints. If we get these dimensions wrong, then the bitmap will be slightly squashed or stretched when it is rendered. 2. Calculate the shadow path, using drInkForShadowing to determine its location onscreen, and the bounds calculated above to determine its size. Notes: + Can cope with fractional values for PixelWidth, but RenderBitmapForDisplay needs to be modified to send these more accurate values through. To see the problem this would solve, zoom to 25601%, create and examine a wall or glow shadow with no blur. + This method could be made more robust, so that if an invalid PixelWidth is passed in, or our bitmap is NULL, it will estimate a shadow path, based only on drInkForShadowing. ********************************************************************************************/ void NodeShadow::CreateBitmapPath( const DocRect& drInkForShadowing, const double PixelWidth, const double pixBlurDiameter ) { // clear our current shadow path. m_Path.ClearPath(); m_NonTranslatedPath.ClearPath(); // get a pointer to my controller node - if there is none, then exit. NodeShadowController* pControl = (NodeShadowController*)GetParentController(); if (pControl == NULL) { ERROR3("NodeShadow::CreateBitmapPath; No parent controller!"); return; } DocRect dr = drInkForShadowing; MILLIPOINT Width = 0; MILLIPOINT Height = 0; // double LowWidth = 0; // double LowHeight = 0; // double HighWidth = 0; // double HighHeight = 0; DocCoord dc1(0,0); DocCoord dc2(0,0); DocCoord dc3(0,0); DocCoord dc4(0,0); DocCoord Centre(0,0); // get the width and height of the bitmap in document coordinates. if (m_ShadowBitmap != NULL) { Width = (MILLIPOINT)(0.5 + (PixelWidth * m_ShadowBitmap->GetWidth())); Height = (MILLIPOINT)(0.5 + (PixelWidth * m_ShadowBitmap->GetHeight())); } else { Width = dr.Width(); Height = dr.Height(); if (pControl->GetShadowType() == SHADOWTYPE_GLOW) { MILLIPOINT ExtraGlow = pControl->GetPenumbraWidth() + pControl->GetGlowWidth(); Width += ExtraGlow; Height += ExtraGlow; } } // different behaviours for different types of shadow. switch (pControl->GetShadowType()) { case SHADOWTYPE_GLOW: case SHADOWTYPE_WALL: case SHADOWTYPE_FEATHER: { /* * Karim 04/10/2000 * Shadows are now applied from the bottom left, as it is much easier * to understand pixel-alignment issues this way! * // Apply from the centre Centre = dr.Centre(); // need to get the offset of the centre left & right in order to make // sure the pixel size of the bitmap in document coordinates is exactly right LowWidth = floor(0.5 * Width); LowHeight = floor(0.5 * Height); // calculate the rect for the bitmap dr.lo.x = Centre.x - (MILLIPOINT)LowWidth; dr.lo.y = Centre.y - (MILLIPOINT)LowHeight; */ MILLIPOINT DeltaBmpSize = CBitmapShadow::GetWholePixelBlurDiameter(pixBlurDiameter) - 1; MILLIPOINT HalfBlurWidth = MILLIPOINT((DeltaBmpSize < 0) ? 0 : PixelWidth * (DeltaBmpSize / 2)); dr.lo.x -= HalfBlurWidth; dr.lo.y -= HalfBlurWidth; dr.hi.x = dr.lo.x + Width; dr.hi.y = dr.lo.y + Height; } break; case SHADOWTYPE_FLOOR: { // slight bug fix dr.lo.x += (MILLIPOINT)(0.5 + (PixelWidth * m_BitmapXOffset)); dr.lo.y += (MILLIPOINT)PixelWidth; dr.hi.x = dr.lo.x + Width; dr.hi.y = dr.lo.y + Height; } break; default: ERROR3("NodeShadow::CreateBitmapPath; Unrecognised shadow type!"); break; } m_NonTranslatedPath.CreatePathFromDocRect(&dr); m_Path.CloneFrom(m_NonTranslatedPath); m_Path.IsFilled = TRUE; if (pControl->GetShadowType() == SHADOWTYPE_WALL) m_Path.Translate(pControl->GetWallShadowOffset()); m_Path.GetTrueBoundingRect(&m_SelectedRect); } /******************************************************************************************** > virtual BOOL NodeShadow::NeedsParent(Node* pClassNode) const Author: David_McClarnon (Xara Group Ltd) Created: 7/5/99 Inputs: A node to do the class checking on Returns: Purpose: Do we need a parent of the same class as the given node ??? See also: NodeCompound::OnChildChange ********************************************************************************************/ BOOL NodeShadow::NeedsParent(Node* pClassNode) const { if (!pClassNode) return TRUE; if (pClassNode->IsKindOf(CC_RUNTIME_CLASS(NodeShadowController))) return TRUE; return FALSE; } /******************************************************************************************** > virtual BOOL NodeShadow::AllowOp(ObjChangeParam *pParam, BOOL SetOpPermissionState, BOOL DoPreTriggerEdit) Author: David_McClarnon (Xara Group Ltd) ; Karim 20/01/2000 Created: 20/11/96 Inputs: pParam describes the way an op wants to change the node SetOpPermissionState if TRUE the OpPermission of nodes should be set DoPreTriggerEdit if TRUE then NameGallery::PreTriggerEdit is called. *Must* be TRUE if the calling Op may make any nodes change their bounds, eg move, line width, cut. Use TRUE if unsure. Purpose: SeeAlso: ********************************************************************************************/ BOOL NodeShadow::AllowOp(ObjChangeParam *pParam, BOOL SetOpPermissionState, BOOL DoPreTriggerEdit) { TRACE( _T("Warning - NodeShadow::AllowOp called\n") ); ERROR2IF(pParam == NULL, FALSE, "NodeShadowController::AllowOp(); NULL input(s) !!"); // clean out the calling-child ptr, so it doesn't get passed around unintentionally. pParam->SetCallingChild(NULL); // A flag for whether or not we allow the Op. BOOL allowed = TRUE; UndoableOperation* pOp = pParam->GetOpPointer(); // See if our parent allows this Op. if (allowed && Parent != NULL && pParam->GetDirection() != OBJCHANGE_CALLEDBYPARENT) { ObjChangeDirection OldDirection = pParam->GetDirection(); pParam->SetCallingChild(this); pParam->SetDirection(OBJCHANGE_CALLEDBYCHILD); allowed = Parent->AllowOp(pParam, SetOpPermissionState, DoPreTriggerEdit); pParam->SetDirection(OldDirection); } // If our parent allowed the Op, then see what we think. if (allowed) { if (pOp) { // Shadows can't be moulded. if ((pOp->IS_KIND_OF(OpCreateNewMould)) || pOp->IsKindOf(CC_RUNTIME_CLASS(OpPasteEnvelope)) || pOp->IsKindOf(CC_RUNTIME_CLASS(OpPastePerspective))) { allowed = FALSE; } // Shadows can't be feathered. else if (pParam->GetChangeFlags().Attribute) if (pOp->IS_KIND_OF(OpChangeFeatherSize)) allowed = FALSE; } } // if necessary, set permissions for OnChildChange. if (SetOpPermissionState) SetOpPermission(allowed ? PERMISSION_ALLOWED : PERMISSION_DENIED, TRUE); // If we're ok so far and were asked to do a PreTriggerEdit, then // determine whether the Op may change the bounds of some nodes. // If it may, then call NameGallery::PreTriggerEdit. if (allowed && DoPreTriggerEdit) { // if the Op is non-NULL then query its MayChangeNodeBounds() method. UndoableOperation* pChangeOp = pParam->GetOpPointer(); if (pChangeOp != NULL && pChangeOp->MayChangeNodeBounds()) { if (NameGallery::Instance()) allowed = NameGallery::Instance()->PreTriggerEdit(pChangeOp, pParam, this); } } return allowed; } /******************************************************************************************** > void NodeShadow::SetBiasGain(CProfileBiasGain &gain) Author: David_McClarnon (Xara Group Ltd) Created: 20/11/96 Inputs: Purpose: Sets the profile for the shadow node SeeAlso: ********************************************************************************************/ void NodeShadow::SetBiasGain(CProfileBiasGain &gain) { AFp BiasValue = gain.GetBias(); AFp GainValue = gain.GetGain(); m_BiasGain.SetBias(BiasValue); m_BiasGain.SetGain(GainValue); } /******************************************************************************************** > CProfileBiasGain &NodeShadow::GetBiasGain() Author: David_McClarnon (Xara Group Ltd) Created: 20/11/96 Inputs: Purpose: Gets the profile for the shadow node SeeAlso: ********************************************************************************************/ CProfileBiasGain &NodeShadow::GetBiasGain() { static CProfileBiasGain Profile; AFp BiasValue = m_BiasGain.GetBias(); AFp GainValue = m_BiasGain.GetGain(); Profile.SetBias(BiasValue); Profile.SetGain(GainValue); return Profile; } CProfileBiasGain* NodeShadow::GetBiasGainPtr() { return (&m_BiasGain); } /******************************************************************************************** > virtual DocRect NodeShadow::ValidateExtend(const ExtendParams& ExtParams) Author: Karim_MacDonald (Xara Group Ltd) Created: 06/12/1999 Inputs: ExtParams description parameters for the extension. Outputs: Returns: TRUE if extending this Nodewill be a reversible operation, FALSE otherwise. Purpose: Tests the reversibility of an Extend operation applied to this node. A NodeShadow cannot itself extend, so instead it asks its parent controller to extend for it. Infinite recursion does not occur, as the controller node ignores its child node, this NodeShadow. Errors: In debug builds, ERROR3 if this Node has no NodeShadowController, in release, we return TRUE. See also: IsTypeExtendible(), Extend(). ********************************************************************************************/ DocRect NodeShadow::ValidateExtend(const ExtendParams& ExtParams) { Node* pBob = GetParentController(); if (pBob == NULL) { ERROR3("NodeShadow::ValidateExtend- no controller Node found!"); return DocRect(INT32_MAX, INT32_MAX, INT32_MAX, INT32_MAX); } else { return pBob->ValidateExtend(ExtParams); } } /******************************************************************************************** > virtual void NodeShadow::Extend(const ExtendParams& ExtParams) Author: Karim_MacDonald (Xara Group Ltd) Created: 06/12/1999 Inputs: ExtParams description parameters for the extension. Outputs: Some of the child nodes of this ShadowNode's NodeShadowController may have their dimensions altered. Returns: Purpose: Perform an Extend operation on this Node, and its children if appropriate. A NodeShadow cannot itself extend, so instead it asks its parent controller to extend for it. Infinite recursion does not occur, as the controller node ignores its child node, this NodeShadow. Errors: In debug builds, ERROR3 if this Node has no NodeShadowController, in release, we do nothing. See also: ********************************************************************************************/ void NodeShadow::Extend(const ExtendParams& ExtParams) { Node* pBob = GetParentController(); if (pBob == NULL) { ERROR3("NodeShadow::ValidateExtend- no controller Node found!"); } else { pBob->Extend(ExtParams); } return; } /******************************************************************************************** > virtual BOOL NodeShadow::OnNodePopUp(Spread* pSpread, DocCoord PointerPos, ContextMenu* pMenu) Author: David_McClarnon (Xara Group Ltd) Created: 7/2/2000 Inputs: pSpread The spread in which things are happening PointerPos The Location of the mouse pointer at the time of the click pMenu The menu to which items should be added Returns: BOOL - TRUE if the node claims the click as its own and FALSE if it is not interested in the click Purpose: Allows the shadow to respond to the click ********************************************************************************************/ BOOL NodeShadow::OnNodePopUp(Spread* pSpread, DocCoord PointerPos, ContextMenu* pMenu) { BOOL ok = TRUE; #ifndef NO_ADVANCED_TOOLS ok = ok && pMenu->BuildCommand(TOOL_OPTOKEN_SOFTSHADOW, TRUE); #endif return ok; } /******************************************************************************************** > virtual INT32 NodeShadow::EstimateNodeComplexity (OpParam* details) Author: Chris_Snook (Xara Group Ltd) Created: 6/09/2000 Inputs: details any data that should be used for the calculation Outputs: - Returns: an estimate of the nodes complexity Purpose: This function estimates a complexity value for the node. The complexity value is based upon the total length of all paths in the node. See Also: OpBlendNodes::DeterminBlendObjectsProcessorHit () ********************************************************************************************/ INT32 NodeShadow::EstimateNodeComplexity (OpParam* details) { return (m_Path.GetUsedSlots ()); } /******************************************************************************************** > virtual BOOL NodeShadow::CanAttrBeAppliedToMe(CCRuntimeClass* pAttribClass) Author: Karim_MacDonald (Xara Group Ltd) Created: 22/08/2000 Inputs: pAttribClass the attr type to test. Returns: FALSE if pAttribClass is a brush attr, TRUE otherwise. Purpose: Determine whether the given attr type can be applied directly to us. Any of those attrs which we require can be directly applied to us. Errors: returns FALSE if pAttribClass is NULL. ********************************************************************************************/ BOOL NodeShadow::CanAttrBeAppliedToMe(CCRuntimeClass* pAttribClass) { // For NodeShadow, this fn does the same thing as RequiresAttrib(). return RequiresAttrib(pAttribClass); } /******************************************************************************************** > void NodeShadow::SetDarkness(double Darkness) Author: Karim_MacDonald (Xara Group Ltd) Created: 13/02/2001 Purpose: Set shadow darkness, from 0.0 (transparent) to 1.0 (opaque). Errors: ERROR3 if input value is out of range. ********************************************************************************************/ void NodeShadow::SetDarkness(double Darkness) { ERROR3IF(Darkness < 0.0 || Darkness > 1.0, "NodeShadow::SetDarkness; invalid input!"); m_dDarkness = Darkness < 0.0 ? 0.0 : Darkness > 1.0 ? 1.0 : Darkness ; } #ifdef NEW_SHADOW_RENDER /******************************************************************************************** > BOOL NodeShadow::MakeShadow(LPBITMAPINFO lpInfo, LPBYTE lpBits, DocRect orect) Author: Phil_Martin (Xara Group Ltd) Created: 11/03/2005 Purpose: Create a shadow from the supplied 32BPP RGBA captured bitmap of the original objects ********************************************************************************************/ BOOL NodeShadow::MakeShadow(LPBITMAPINFO lpInfo, LPBYTE lpBits, DocRect orect) { if (m_ShadowBitmap) return TRUE; // Get details of this shadow from our controller NodeCompound* pCompound = GetParentController(); NodeShadowController* pController = NULL; if (pCompound && pCompound->IsAShadowController()) pController = (NodeShadowController*)pCompound; if (pController==NULL) return FALSE; double dPixelWidth = pController->GetPixelWidth(); double dPixBlurDiameter = pController->GetPenumbraWidth()/dPixelWidth; // Use the RGBT bitmap provided to create a suitable shadow // and store it in the cache // LPBITMAPINFO lpShadowInfo = NULL; // LPBYTE lpShadowBits = NULL; DocRect ShadowRect; BOOL bOK = FALSE; // ------------------------------------------------ // This code lifted from RenderBitmapForDisplay // // initialise the transparency fill attribute and clear our caches. SetupTransFill(); // Get a kernel version of the silhouette bitmap CWxBitmap *pwBitmap = new CWxBitmap(lpInfo, lpBits); KernelBitmap *pkBitmap = new KernelBitmap(pwBitmap); // generate the shadow bitmap for the required blur diameter. bOK = GenerateShadowBitmap(pkBitmap, dPixBlurDiameter); if (!bOK) return FALSE; // if desired, attach a debug copy of the final shadow bitmap within the bitmap gallery. #ifdef ATTACH_SHADOW_BMP m_ShadowBitmap->AttachDebugCopyToCurrentDocument("MakeShadow Bitmap"); #endif // Get rid of temp kernel bitmap if (pwBitmap) pwBitmap->BMBytes = ((CWxBitmap*)OILBitmap::Default)->BMBytes; if (pkBitmap) { delete pkBitmap; pkBitmap = NULL; } m_LastRequestedPixWidth = MILLIPOINT(dPixelWidth); // m_LastQualitySetting = CurrentQuality; m_LastActualPixWidth = dPixelWidth; // one last thing - create the path which the shadow bitmap-transparency is drawn into. CreateBitmapPath(orect, dPixelWidth, dPixBlurDiameter); // TODO: Set pixel width properly // ------------------------------------------------ // No need to do this at the moment because GenerateShadowBitmap creates m_ShadowBitmap /* CBitmapCache* pBitmapCache = Camelot.GetBitmapCache(); if (pBitmapCache==NULL) return FALSE; if (lpInfo==NULL || lpBits==NULL) return FALSE; // Cache the ORIGINAL bitmap as Option 0 // See also, SetOriginalBitmap double PixelWidth = 42; // TODO: Does this matter? CBitmapCacheKey inky(this, PixelWidth, 0); CCachedBitmap cbmp; cbmp.pbmpBits = lpShadowBits; cbmp.pbmpInfo = lpShadowInfo; cbmp.SetCachedRect(ShadowRect); cbmp.nPriority = CACHEPRIORITY_TEMPBITMAP_HIGH; if (cbmp.IsValid()) { pBitmapCache->StoreBitmap(inky, cbmp); bCached = TRUE; } */ return TRUE; } /******************************************************************************************** > BOOL NodeShadow::RenderShadow(RenderRegion* pRender) Author: Phil_Martin (Xara Group Ltd) Created: 11/03/2005 Purpose: Render a previously prepared shadow ********************************************************************************************/ BOOL NodeShadow::RenderShadow(RenderRegion* pRender) { enum Quality::Fill CurrentQuality = pRender->RRQuality.GetFillQuality(); pRender->SaveContext(); if (CurrentQuality >= (enum Quality::Fill)Quality::Solid) { pRender->SetFillColour(COLOUR_BLACK); // make sure there's some shadow colour Node* pNode = FindFirstChild(); while (pNode) { if (pNode->IsAnAttribute()) { pNode->Render(pRender); } pNode = pNode->FindNext(); } RenderBitmap(pRender); } else { // outlines only - we'll just render the shadow's bounding rect. DocRect drBounds = GetBoundingRect(); pRender->SetFillColour(COLOUR_NONE); pRender->SetLineColour(COLOUR_BLACK); pRender->DrawRect(&drBounds); } pRender->RestoreContext(); return FALSE; } /******************************************************************************************** > BOOL NodeShadow::NeedsRemake() Author: Phil_Martin (Xara Group Ltd) Created: 11/03/2005 Purpose: Create a shadow from the supplied 32BPP RGBA captured bitmap of the original objects ********************************************************************************************/ BOOL NodeShadow::NeedsRemake() { return (m_ShadowBitmap == NULL); } /******************************************************************************************** > virtual BOOL NodeShadow::TransformShadow(TransformBase& Trans) Author: Phil_Martin (Xara Group Ltd) Created: 15/04/2005 Inputs: Trans - The transform Object Returns: TRUE if could transform the shadow FALSE otherwise Purpose: Transforms the shadow when the controller is in IsCapturingChildren mode if we can SeeAlso: NodeRenderableInk::Transform() ********************************************************************************************/ BOOL NodeShadow::TransformShadow(TransformBase& Trans) { NodeShadowController* pControl = (NodeShadowController*)GetParentController(); if (pControl==NULL) return FALSE; ERROR3IF(!pControl->IsCapturingChildren(), "TransformShadow should only be called when IsCapturingChildren"); ShadowType shadtype = pControl->GetShadowType(); // We can only translate floor shadows! if (!Trans.IsTranslation() && shadtype==SHADOWTYPE_FLOOR) return FALSE; // We can't transform shadows unless the transform has a usable matrix if (!Trans.IS_KIND_OF(Trans2DMatrix)) return FALSE; // Transform the base path Trans.Transform(m_NonTranslatedPath.GetCoordArray(), m_NonTranslatedPath.GetNumCoords()); switch (shadtype) { case SHADOWTYPE_WALL: // Transform the shadow path using the modified transform m_Path.ClearPath(); m_Path.CloneFrom(m_NonTranslatedPath); m_Path.IsFilled = TRUE; m_Path.Translate(pControl->GetWallShadowOffset()); break; case SHADOWTYPE_FLOOR: Trans.Transform(m_Path.GetCoordArray(), m_Path.GetNumCoords()); break; case SHADOWTYPE_GLOW: Trans.Transform(m_Path.GetCoordArray(), m_Path.GetNumCoords()); break; default: ERROR3("NodeShadow::TransformShadow; unknown parent shadow type!"); break; } TransformChildren(Trans); return TRUE; } #endif //------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------- ////////////////////////////////////////////////////// // The passback function for the shadow become a /******************************************************************************************** > BOOL NodeShadowBecomeA::PassBack(NodeRenderableInk* pNewNode, NodeRenderableInk* pCreatedByNode,CCAttrMap* pAttrMap) Author: David_McClarnon (Xara Group Ltd) Created: 11/12/99 Purpose: Used by NodeShadow to produce the path for the NodeShadowPassback node ********************************************************************************************/ BOOL NodeShadowBecomeA::PassBack(NodeRenderableInk* pNewNode, NodeRenderableInk* pCreatedByNode,CCAttrMap* pAttrMap) { if (pNewNode) { if (pNewNode->IsNodePath()) { m_pPath->MergeTwoPaths(((NodePath *)pNewNode)->InkPath); } pNewNode->CascadeDelete(); delete pNewNode; } return TRUE; } //------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------- /******************************************************************************************** > BitmapShadower* BitmapShadower::CreateSilhouetteBitmap( NodeShadowController* pControl, GRenderRegion* pGRR = NULL, List* pList = NULL ) Author: Karim_MacDonald (Xara Group Ltd) Created: 26/10/2000 Inputs: pControl NodeShadowController of the shadow which we're creating. pGRR render region for which to generate the shadow bitmap. pList list of SumAllPathsElem's to shadow - usually NULL, in which case we shadow the node lying within the shadow tree structure. Outputs: If successful, a 32bpp silhouette + other shadow information is generated and stored in a new instance of this class. Returns: A new instance of this class, containing all relevant shadow information, or NULL if we failed. Purpose: Generate a 32bpp silhouette bitmap ready for shadowing. All relevant shadow information is stored in the returned object. At a later date, I may move all non-bshadow.cpp/.h shadow bitmap generating code into this class. Errors: ERROR1 with NULL if we cannot create a new BitmapShadower. ERROR2 with NULL if pControl or pGRR is NULL. ERROR3 with NULL if pList is NULL and the shadowed node is NULL too. See also: NodeShadow::RenderBitmapForDisplay(). ********************************************************************************************/ BitmapShadower* BitmapShadower::CreateSilhouetteBitmap( NodeShadowController* pControl, GRenderRegion* pGRR, List* pList ) { // input validation. ERROR2IF(pControl == NULL, NULL, "BitmapShadower::CreateSilhouetteBitmap; NULL input params!"); // create a new shadower. BitmapShadower* pBitmapShadower = new BitmapShadower; ERROR1IF(pBitmapShadower == NULL, NULL, _R(IDS_OUT_OF_MEMORY)); // initialise it and tell it to do its stuff. BOOL ok = pBitmapShadower->Initialise(pControl, pGRR, pList); if (ok) ok = pBitmapShadower->DoCreateSilhouetteBitmap(); return ok ? pBitmapShadower : NULL; } /******************************************************************************************** > BitmapShadower::BitmapShadower() Author: Karim_MacDonald (Xara Group Ltd) Created: 26/10/2000 Purpose: Private constructor - you may only create this class through public static interface. See also: BitmapShadower::CreateSilhouetteBitmap(). ********************************************************************************************/ BitmapShadower::BitmapShadower() { m_pGRR = NULL; m_pControl = NULL; m_pShadow = NULL; m_pShadowedNode = NULL; m_pShadowedList = NULL; m_ShadowType = SHADOWTYPE_NONE; m_drShadowedBounds.MakeEmpty(); m_ShadowScaleFactor = 1.0; m_ActualPixelWidth = 0; m_pixBlurDiameter = 0; m_pRendWrap = NULL; m_pOffscreenRR = NULL; m_pSilhouetteBitmap = NULL; } /******************************************************************************************** > BitmapShadower::~BitmapShadower() Author: Karim_MacDonald (Xara Group Ltd) Created: 26/10/2000 Purpose: Destructor - frees the silhouette bitmap if it has not been extracted. ********************************************************************************************/ BitmapShadower::~BitmapShadower() { if (m_pSilhouetteBitmap != NULL) { delete m_pSilhouetteBitmap; m_pSilhouetteBitmap = NULL; } } /******************************************************************************************** > BOOL BitmapShadower::Initialise(NodeShadowController* pControl, GRenderRegion* pGRR, List* pList) Author: Karim_MacDonald (Xara Group Ltd) Created: 26/10/2000 Inputs: pControl NodeShadowController of the shadow which we're creating. pGRR render region for which to generate the shadow bitmap. pList list of SumAllPathsElem's to shadow - usually NULL, in which case we shadow the node lying within the shadow tree structure. Returns: TRUE if successful, FALSE othewise. Purpose: Initialises our state. ********************************************************************************************/ BOOL BitmapShadower::Initialise(NodeShadowController* pControl, GRenderRegion* pGRR, List* pList) { m_pControl = pControl; m_pGRR = pGRR; m_ShadowType = m_pControl->GetShadowType(); if (pList == NULL) m_pShadowedNode = pControl->GetShadowedNode(); else m_pShadowedList = pList; if (m_pShadowedList == NULL && m_pShadowedNode == NULL) { ERROR3("BitmapShadower called with NULL shadowed node or NULL path-list to shadow!"); return FALSE; } return TRUE; } /******************************************************************************************** > BOOL BitmapShadower::DoCreateSilhouetteBitmap() Author: Karim_MacDonald (Xara Group Ltd) Created: 26/10/2000 Returns: TRUE if successful, FALSE otherwise. Purpose: Creates the shadow silhouette bitmap. ********************************************************************************************/ BOOL BitmapShadower::DoCreateSilhouetteBitmap() { BOOL ok = CalculateBoundsAndScale1(); if (ok) ok = (m_pGRR == NULL) ? CalculateBoundsAndScale2NoGRR() : CalculateBoundsAndScale2WithGRR() ; if (ok) ok = CreateOffscreenRR(); if (ok) ok = RenderShadowedNodeOffscreen(); return ok; } /******************************************************************************************** > BOOL BitmapShadower::CalculateBoundsAndScale() Author: Karim_MacDonald (Xara Group Ltd) Created: 26/10/2000 Outputs: Following member variables are initialised: m_drShadowedBounds m_ShadowType m_ShadowScaleFactor m_ActualPixelWidth Returns: TRUE if successful, FALSE otherwise. Purpose: Calculate the bounds and scaling factor for our shadow bitmap. This function is split into two parts, 1 and 2. This is because part 2 performs differently, depending on whether or not we have a ptr to a GRenderRegion. A GRenderRegion ptr ensures that our rendering matches exactly with the target output device, however if we have no GRR, we must query the current view, which is likely to produce just as good results, but ********************************************************************************************/ BOOL BitmapShadower::CalculateBoundsAndScale1() { // if we're shadowing a node in the tree (usual behaviour), use its bounds. if (m_pShadowedNode != NULL) m_drShadowedBounds = m_pShadowedNode->GetBoundingRect(); // if we're shadowing a list of paths & attrs, use the union of their bounds, // taking into account any attributes attached. else if (m_pShadowedList != NULL) { SumAllPathsElem* pElem = (SumAllPathsElem*)m_pShadowedList->GetHead(); while (pElem != NULL) { DocRect PathBounds(0,0,0,0); pElem->GetPath()->GetTrueBoundingRect(&PathBounds, 0, pElem->GetAttrMap()); m_drShadowedBounds = m_drShadowedBounds.Union(PathBounds); ////////////////////////////////////////////////////////////////////////////////////////////// // This code block is a BODGE for feathers+blends, and does not belong here! // AttrFeather* appliedFeather = NULL; pElem->GetAttrMap()->Lookup( CC_RUNTIME_CLASS(AttrFeather), (void *&) appliedFeather); if (appliedFeather) { if (!(((FeatherAttrValue*) appliedFeather->GetAttributeValue ())->IsDefaultFlagSet ())) { pElem->GetAttrMap ()->PostBlendInit ((pElem->GetPath()), CC_RUNTIME_CLASS (NodePath)); } } ////////////////////////////////////////////////////////////////////////////////////////////// pElem = (SumAllPathsElem *)m_pShadowedList->GetNext(pElem); } } // glow shadows require a bitmap big enough to hold the glow contour. if (m_ShadowType == SHADOWTYPE_GLOW) m_drShadowedBounds.Inflate(m_pControl->GetGlowWidth()); return TRUE; } BOOL BitmapShadower::CalculateBoundsAndScale2WithGRR() { // leave a pixel-width's safety-margin around the bitmap. // we don't use GetScaledPixelWidth(), as that is not always correct! FIXED16 ScaleFactor; m_pGRR->GetMatrix().Decompose(&ScaleFactor); double GRRPixelWidth = (double)m_pGRR->GetPixelWidth() / fabs(ScaleFactor.MakeDouble()); m_drShadowedBounds.Inflate((INT32)(GRRPixelWidth + 0.5)); // if the offscreen bitmap will be too big, then we'll render it as large // as we can, and remember how much further it needs to scale up. WinRect wrBounds = m_pGRR->CalculateWinRect(m_drShadowedBounds); INT32 XSize = wrBounds.width; INT32 YSize = wrBounds.height; if (XSize > YSize) { if (XSize > MaxShadowBitmapSize) m_ShadowScaleFactor = MaxShadowBitmapSize / (double)XSize; } else { if (YSize > MaxShadowBitmapSize) m_ShadowScaleFactor = MaxShadowBitmapSize / (double)YSize; } // use the shadow scaling factor to calculate the actual pixel-width // for the shadow bitmap, so it renders at the correct size onscreen. m_ActualPixelWidth = GRRPixelWidth / m_ShadowScaleFactor; return TRUE; } BOOL BitmapShadower::CalculateBoundsAndScale2NoGRR() { // we have no render region, but we need a render matrix, // so we must get the view to construct a render matrix for us. View* pView = View::GetCurrent(); if (pView == NULL) return FALSE; Spread* pSpread = m_pControl->FindParentSpread(); if (pSpread == NULL) return FALSE; double PixelsPerInch = pView->GetConvertToEditableShapesDPI(); // leave a pixel-width's safety-margin around the bitmap. FIXED16 ViewPixelWidth = pView->GetScaledPixelWidth(); m_drShadowedBounds.Inflate(ViewPixelWidth.round().MakeLong()); // if the offscreen bitmap will be too big, then we'll render it as large // as we can, and remember how much further it needs to scale up. Matrix RenderMatrix = pView->ConstructRenderingMatrix(pSpread); WinRect wrBounds = OSRenderRegion::DocRectToWin(RenderMatrix, m_drShadowedBounds, PixelsPerInch); INT32 XSize = wrBounds.width; INT32 YSize = wrBounds.height; if (XSize > YSize) { if (XSize > MaxShadowBitmapSize) m_ShadowScaleFactor = MaxShadowBitmapSize / (double)XSize; } else { if (YSize > MaxShadowBitmapSize) m_ShadowScaleFactor = MaxShadowBitmapSize / (double)YSize; } // use the shadow scaling factor to calculate the actual pixel-width // for the shadow bitmap, so it renders at the correct size onscreen. const double AppPixelWidth = MILLIPOINTS_PER_INCH / PixelsPerInch; m_ActualPixelWidth = AppPixelWidth / m_ShadowScaleFactor; // Work out the adjustment to the scale factor to account for // not rendering at 96 dpi. This must be done after the calculations // above as the bitmap is always rendered at 96 dpi but with a scale factor // to ensure it is rendered the correct size. If we are rendering at 20 dpi // then we need to render 20/96 of the size but this must NOT be included in // the calculation of m_ActualPixelWidth or the bitmap will be rendered into // the main render region at the wrong size. // Phil, 21/02/2006 // This doesn't seme to be needed any more and in fact it causes bug BZ861 // Note that the ScaledPixelWidth resolution of captures is better controlled // than when this bodge was added. // So I'm tentatively removing it again // Unfortunately, removing this causes problems with export of shadows to // plugin filters so it is going back in for now... if (PixelsPerInch != 96.0) { double NewScale = PixelsPerInch / 96.0; m_ShadowScaleFactor *= NewScale; } return TRUE; } /******************************************************************************************** > BOOL BitmapShadower::CreateOffscreenRR() Author: Karim_MacDonald (Xara Group Ltd) Created: 26/10/2000 Outputs: Following member variables are initialised: m_pRendWrap m_pOffscreenRR m_pixBlurDiameter Returns: TRUE if successful, FALSE otherwise. Purpose: Creates the offscreen render-region, ready for rendering into. ********************************************************************************************/ BOOL BitmapShadower::CreateOffscreenRR() { // create a concurrent renderer please, scaling it up if necessary. // ConcurrentRenderer creates a new GRenderRegion, based on the given GRenderRegion. m_pRendWrap = GRenderRegionWrapper::GetConcurrentRenderer( m_pGRR, m_ShadowScaleFactor, m_drShadowedBounds, 32, TRUE ); if (m_pRendWrap == NULL) return FALSE; m_pOffscreenRR = m_pRendWrap->GetRenderRegion(); // work out the required blur diameter in pixels (allowed to be non-integer). m_pixBlurDiameter = m_pControl->GetPenumbraWidth() / m_ActualPixelWidth; // wall and glow shadows have alignment problems, which require that we: // 1. force the offscreen RR to always smooth bitmaps. // 2. turn off line hinting in the offscreen RR. // 3. offset the RR down and left by a half pixel if we have an even blur. // // Note: non-full-quality rendering can do this job properly, // so if we're not rendering at full quality then we can only do odd blurs. if (m_ShadowType == SHADOWTYPE_GLOW || m_ShadowType == SHADOWTYPE_WALL) { if (m_pOffscreenRR->RRQuality.GetAntialiasQuality() == (enum Quality::Antialias)Quality::FullAntialias) { m_pOffscreenRR->SetForceBitmapSmoothing(TRUE); m_pOffscreenRR->GetDrawContext()->SetHintingFlag(FALSE); if (CBitmapShadow::BlurringWillOffsetBitmap(m_pixBlurDiameter)) m_pOffscreenRR->OffsetByHalfPixel(); } else { // if blurring will offset the bitmap, then make the blur odd: +1, // and round to the nearest whole pixel: (UINT32)(+0.5). if (CBitmapShadow::BlurringWillOffsetBitmap(m_pixBlurDiameter)) m_pixBlurDiameter = (UINT32)(m_pixBlurDiameter + 1.5); } } return TRUE; } #ifndef NEW_GLOW_SILHOUETTE /******************************************************************************************** > BOOL BitmapShadower::RenderShadowedNodeOffscreen() Author: Karim_MacDonald (Xara Group Ltd) Created: 26/10/2000 Outputs: Following member variables are initialised: m_pSilhouetteBitmap Following member variables are deinitialised: m_pRendWrap m_pOffscreenRR Returns: TRUE if successful, FALSE otherwise. Purpose: Render the shadowed node offscreen, to create the silhouette bitmap. ********************************************************************************************/ BOOL BitmapShadower::RenderShadowedNodeOffscreen() { // if we're rendering our shadowed nodes, then we need to pre-render all // attributes which indirectly apply to it via the controller, // *except* for offscreen attributes, as they're meant to divert rendering offscreen // for our *controller* node, not us. if (m_pShadowedNode != NULL) { CCAttrMap* pControlMap = CCAttrMap::MakeAppliedAttrMap(m_pControl); if (pControlMap != NULL) { pControlMap->Render(m_pOffscreenRR, FALSE); delete pControlMap; } } // we save context here, as the rendered attributes are applied indirectly, // therefore normal rendering would have one or more SaveContext()'s before // the ink-node is rendered. m_pOffscreenRR->SaveContext(); // Create some no-transp attrs, for possible use if we're a glow shadow. FlatTranspFillAttribute NoFillTransp(TT_NoTranspType); StrokeTranspAttribute NoLineTransp(TT_NoTranspType); // for glow shadows, we must contour out from what we're shadowing and render that. if (m_ShadowType == SHADOWTYPE_GLOW) { // do a glow-width contour out from the shadowed node. // TODO: test and act on failure of this function. Path ContouredPath; NodeContour::GenerateContourPathForNode(&ContouredPath, m_pShadowedNode, m_pShadowedList, m_pControl->GetGlowWidth()); // set the render region to draw black silhouettes. m_pOffscreenRR->SetFillColour(COLOUR_BLACK); m_pOffscreenRR->SetLineColour(COLOUR_TRANS); // glow shadows do not pay attention to any transparency affecting the shadowed nodes, // so we must turn off any transparency attributes which were rendered by our attr-map. m_pOffscreenRR->SetTranspFillGeometry(&NoFillTransp, FALSE); m_pOffscreenRR->SetLineTransp(&NoLineTransp, FALSE); // that should do it - render the path into the render-region. m_pOffscreenRR->DrawPath(&ContouredPath); } // for other shadow types, we just render the node/path-list. // note that this is done *uninterruptibly*, unlike normal rendering. else { if (m_pShadowedNode != NULL) { BOOL SubRenderStateLockedByShadower = FALSE; if (!m_pOffscreenRR->IsSubRenderStateLocked()) { m_pOffscreenRR->LockSubRenderState(TRUE); SubRenderStateLockedByShadower = TRUE; } m_pShadowedNode->RenderTreeAtomic(m_pOffscreenRR); if (SubRenderStateLockedByShadower) { m_pOffscreenRR->LockSubRenderState(FALSE); SubRenderStateLockedByShadower = FALSE; } } else if (m_pShadowedList != NULL) { SumAllPathsElem* pElem = (SumAllPathsElem *)m_pShadowedList->GetHead(); while (pElem != NULL) { m_pOffscreenRR->SaveContext(); pElem->GetAttrMap()->Render(m_pOffscreenRR); m_pOffscreenRR->DrawPath(pElem->GetPath()); m_pOffscreenRR->RestoreContext(); pElem = (SumAllPathsElem*)m_pShadowedList->GetNext(pElem); } } } // restore the context to match our SaveContext() above. m_pOffscreenRR->RestoreContext(); // extract the bitmap from the offscreen RR before the RR is closed down. m_pSilhouetteBitmap = m_pRendWrap->GetKernelBitmap(); if (m_pSilhouetteBitmap == NULL) { ERROR3("BitmapShadower::RenderShadowedNodeOffscreen; Couldn't extract KernelBitmap!"); return FALSE; } // if desired, insert a debug copy of the silhouette bitmap within the bitmap gallery. #ifdef ATTACH_SILHOUETTE_BMP m_pSilhouetteBitmap->AttachDebugCopyToCurrentDocument("Shadow Silhouette Bitmap"); #endif; // close down the offscreen render-region and clear our ptrs to prevent future nastiness. m_pRendWrap->RestorePreviousRendererState(); m_pRendWrap = NULL; m_pOffscreenRR = NULL; return TRUE; } #endif #ifdef NEW_GLOW_SILHOUETTE /******************************************************************************************** > BOOL BitmapShadower::RenderShadowedNodeOffscreen() Author: Karim_MacDonald (Xara Group Ltd) Created: 26/10/2000 Outputs: Following member variables are initialised: m_pSilhouetteBitmap Following member variables are deinitialised: m_pRendWrap m_pOffscreenRR Returns: TRUE if successful, FALSE otherwise. Purpose: Render the shadowed node offscreen, to create the silhouette bitmap. ********************************************************************************************/ BOOL BitmapShadower::RenderShadowedNodeOffscreen() { // if we're rendering our shadowed nodes, then we need to pre-render all // attributes which indirectly apply to it via the controller, // *except* for offscreen attributes, as they're meant to divert rendering offscreen // for our *controller* node, not us. if (m_pShadowedNode != NULL) { CCAttrMap* pControlMap = CCAttrMap::MakeAppliedAttrMap(m_pControl, TRUE, FALSE); if (pControlMap != NULL) { pControlMap->Render(m_pOffscreenRR, FALSE); delete pControlMap; } } // we save context here, as the rendered attributes are applied indirectly, // therefore normal rendering would have one or more SaveContext()'s before // the ink-node is rendered. m_pOffscreenRR->SaveContext(); // Create some no-transp attrs, for possible use if we're a glow shadow. FlatTranspFillAttribute NoFillTransp(TT_NoTranspType); StrokeTranspAttribute NoLineTransp(TT_NoTranspType); // NOTE: This render is not interruptible for normal background rendering // NOTE: Should use Phil's new Capture system to avoid this extra render { if (m_pShadowedNode != NULL) { BOOL SubRenderStateLockedByShadower = FALSE; if (!m_pOffscreenRR->IsSubRenderStateLocked()) { m_pOffscreenRR->LockSubRenderState(TRUE); SubRenderStateLockedByShadower = TRUE; } m_pShadowedNode->RenderTreeAtomic(m_pOffscreenRR); if (SubRenderStateLockedByShadower) { m_pOffscreenRR->LockSubRenderState(FALSE); SubRenderStateLockedByShadower = FALSE; } } else if (m_pShadowedList != NULL) { SumAllPathsElem* pElem = (SumAllPathsElem *)m_pShadowedList->GetHead(); while (pElem != NULL) { m_pOffscreenRR->SaveContext(); pElem->GetAttrMap()->Render(m_pOffscreenRR); m_pOffscreenRR->DrawPath(pElem->GetPath()); m_pOffscreenRR->RestoreContext(); pElem = (SumAllPathsElem*)m_pShadowedList->GetNext(pElem); } } } // restore the context to match our SaveContext() above. m_pOffscreenRR->RestoreContext(); // for glow shadows, we must contour out from what we're shadowing and render that. if (m_ShadowType == SHADOWTYPE_GLOW) { GRenderRegion* pRegion = NULL; LPBITMAPINFO lpInfo = NULL; LPBYTE lpBits = NULL; pRegion = m_pRendWrap->GetRenderRegion(); m_pRendWrap->GetBitmapPointers(&lpInfo, &lpBits); ENSURE(pRegion && lpInfo && lpBits, "Failed to get offscreen region details in RenderShadowedNodeOffscreen"); LPBITMAPINFO lpContourInfo = NULL; LPBYTE lpContourBits = NULL; double OffsetX = 0; double OffsetY = 0; double dPixelContour = (double)m_pControl->GetGlowWidth()/pRegion->GetScaledPixelWidthDouble(); // if (dPixelContour>MAX_CONTOUR) // dPixelContour = MAX_CONTOUR; // BOOL ok = CBitmapShadow::ContourBitmap(lpInfo, // lpBits, // dPixelContour, // 0xFE, // Threshold // &lpContourInfo, // &lpContourBits, // &OffsetX, // &OffsetY // ); // Call ContourBitmap in 20 pixel steps - Gavin says this will be faster than // doing the whole contour at once and it overcomes the 100 pixel, MAX_CONTOUR // limit. BOOL ok = TRUE; double dPixRemaining = dPixelContour; if (dPixelContour<=0) ok = FALSE; LPBITMAPINFO lpIterInfo = lpInfo; LPBYTE lpIterBits = lpBits; while (ok && dPixRemaining>0) { double dPixIteration = dPixRemaining>20 ? 20 : dPixRemaining; double dx = 0; double dy = 0; ok = CBitmapShadow::ContourBitmap(lpIterInfo, lpIterBits, dPixIteration, 0xFE, // Threshold &lpContourInfo, &lpContourBits, &dx, &dy ); OffsetX += dx; OffsetY += dy; dPixRemaining = dPixRemaining - dPixIteration; if (ok && lpIterInfo!=lpInfo && lpIterBits!=lpBits) { FreeDIB(lpIterInfo, lpIterBits); } lpIterInfo = lpContourInfo; lpIterBits = lpContourBits; } // ERROR3IF(!ok, "ContourBitmap failed in RenderShadowedNodeOffscreen"); if (ok && lpContourInfo && lpContourBits) { if (m_pSilhouetteBitmap) delete m_pSilhouetteBitmap; double PixelWidth = m_pOffscreenRR->GetScaledPixelWidthDouble(); MILLIPOINT newWidth = MILLIPOINT(PixelWidth * lpContourInfo->bmiHeader.biWidth); MILLIPOINT newHeight = MILLIPOINT(PixelWidth * lpContourInfo->bmiHeader.biHeight); DocCoord newCorner = m_drShadowedBounds.lo; newCorner.x += INT32(OffsetX * PixelWidth); newCorner.y += INT32(OffsetY * PixelWidth); m_drShadowedBounds.lo = newCorner; m_drShadowedBounds.hi.x = newCorner.x + newWidth; m_drShadowedBounds.hi.y = newCorner.y + newHeight; CWxBitmap* wbmpSilhouette = new CWxBitmap(lpContourInfo, lpContourBits); m_pSilhouetteBitmap = new KernelBitmap(wbmpSilhouette, TRUE); // Make sure offscreen bitmap doesn't hang around KernelBitmap* pOffBitmap = m_pRendWrap->GetKernelBitmap(); delete pOffBitmap; } else { // ContourBitmap failed! // extract the bitmap from the offscreen RR before the RR is closed down. m_pSilhouetteBitmap = m_pRendWrap->GetKernelBitmap(); } } else { // extract the bitmap from the offscreen RR before the RR is closed down. m_pSilhouetteBitmap = m_pRendWrap->GetKernelBitmap(); } // close down the offscreen render-region and clear our ptrs to prevent future nastiness. m_pRendWrap->RestorePreviousRendererState(); m_pRendWrap = NULL; m_pOffscreenRR = NULL; if (m_pSilhouetteBitmap == NULL) { ERROR3("BitmapShadower::RenderShadowedNodeOffscreen; Couldn't extract KernelBitmap!"); return FALSE; } // if desired, insert a debug copy of the silhouette bitmap within the bitmap gallery. #ifdef ATTACH_SILHOUETTE_BMP m_pSilhouetteBitmap->AttachDebugCopyToCurrentDocument("Shadow Silhouette Bitmap"); #endif return TRUE; } #endif #endif // BUILDSHADOWS