// $Id: rgnlist.cpp 1328 2006-06-15 19:23:45Z alex $ /* @@tag:xara-cn@@ DO NOT MODIFY THIS LINE ================================XARAHEADERSTART=========================== Xara LX, a vector drawing and manipulation program. Copyright (C) 1993-2006 Xara Group Ltd. Copyright on certain contributions may be held in joint with their respective authors. See AUTHORS file for details. LICENSE TO USE AND MODIFY SOFTWARE ---------------------------------- This file is part of Xara LX. Xara LX is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. Xara LX and its component source files are distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Xara LX (see the file GPL in the root directory of the distribution); if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ADDITIONAL RIGHTS ----------------- Conditional upon your continuing compliance with the GNU General Public License described above, Xara Group Ltd grants to you certain additional rights. The additional rights are to use, modify, and distribute the software together with the wxWidgets library, the wxXtra library, and the "CDraw" library and any other such library that any version of Xara LX relased by Xara Group Ltd requires in order to compile and execute, including the static linking of that library to XaraLX. In the case of the "CDraw" library, you may satisfy obligation under the GNU General Public License to provide source code by providing a binary copy of the library concerned and a copy of the license accompanying it. Nothing in this section restricts any of the rights you have under the GNU General Public License. SCOPE OF LICENSE ---------------- This license applies to this program (XaraLX) and its constituent source files only, and does not necessarily apply to other Xara products which may in part share the same code base, and are subject to their own licensing terms. This license does not apply to files in the wxXtra directory, which are built into a separate library, and are subject to the wxWindows license contained within that directory in the file "WXXTRA-LICENSE". This license does not apply to the binary libraries (if any) within the "libs" directory, which are subject to a separate license contained within that directory in the file "LIBS-LICENSE". ARRANGEMENTS FOR CONTRIBUTION OF MODIFICATIONS ---------------------------------------------- Subject to the terms of the GNU Public License (see above), you are free to do whatever you like with your modifications. However, you may (at your option) wish contribute them to Xara's source tree. You can find details of how to do this at: http://www.xaraxtreme.org/developers/ Prior to contributing your modifications, you will need to complete our contributor agreement. This can be found at: http://www.xaraxtreme.org/developers/contribute/ Please note that Xara will not accept modifications which modify any of the text between the start and end of this header (marked XARAHEADERSTART and XARAHEADEREND). MARKS ----- Xara, Xara LX, Xara X, Xara X/Xtreme, Xara Xtreme, the Xtreme and Xara designs are registered or unregistered trademarks, design-marks, and/or service marks of Xara Group Ltd. All rights in these marks are reserved. Xara Group Ltd, Gaddesden Place, Hemel Hempstead, HP2 6EX, UK. http://www.xara.com/ =================================XARAHEADEREND============================ */ #include "camtypes.h" //#include "rgnlist.h" - in camtypes.h [AUTOMATICALLY REMOVED] #include "osrndrgn.h" //#include "ensure.h" - in camtypes.h [AUTOMATICALLY REMOVED] //#include "document.h" - in camtypes.h [AUTOMATICALLY REMOVED] //#include "docview.h" - in camtypes.h [AUTOMATICALLY REMOVED] #include "camview.h" #include "paper.h" //#include "menucmds.h" #include "camelot.h" //#include "errors.h" - in camtypes.h [AUTOMATICALLY REMOVED] //#include "spread.h" - in camtypes.h [AUTOMATICALLY REMOVED] //#include "ctrlhelp.h" //#include "app.h" - in camtypes.h [AUTOMATICALLY REMOVED] #include "statline.h" #include "dragmgr.h" #include "prncamvw.h" #include "menuops.h" DECLARE_SOURCE("$Revision: 1328 $"); /******************************************************************************************** > class ViewItem : public ListItem Author: Tim_Browse (Xara Group Ltd) Created: 07/12/94 Purpose: When optimising redraw for a particular View, we try to join all the render regions of a View into one if they cover more than 70% of the resulting union. This class is used to remember which DocViews we have already tried to optimise as we step through the render region list. SeeAlso: RenderRegionList; ViewList ********************************************************************************************/ class ViewItem : public ListItem { CC_DECLARE_MEMDUMP(ViewItem); public: ViewItem(View *pNewView) : pView(pNewView) {} View *pView; }; /******************************************************************************************** > class ViewList : public List Author: Tim_Browse (Xara Group Ltd) Created: 07/12/94 Purpose: When optimising redraw for a particular View, we try to join all the render regions of a DocView into one if they cover more than 70% of the resulting union. This class is used to remember which Views we have already tried to optimise as we step through the render region list SeeAlso: ViewItem; RenderRegionList ********************************************************************************************/ class ViewList : public List { CC_DECLARE_MEMDUMP(ViewList) public: ~ViewList() { DeleteAll(); } BOOL AddView(View *); BOOL IsViewOptimised(View*); }; CC_IMPLEMENT_MEMDUMP(RenderRegionList, List) CC_IMPLEMENT_MEMDUMP(ViewItem, ListItem) CC_IMPLEMENT_MEMDUMP(ViewList, List) #define new CAM_DEBUG_NEW /******************************************************************************************** > BOOL ViewList::AddView(View *pView) Author: Tim_Browse (Xara Group Ltd) Created: 07/12/94 Inputs: pView - pointer to the View to add to the list (only this pointer is stored in the list). Returns: TRUE if View was added ok; FALSE if not => ERROR1 Purpose: Add a View to a list to indicate that it has been optimised. Errors: Out of memory => ERROR1 SeeAlso: ViewList; ViewList::IsViewOptimised ********************************************************************************************/ BOOL ViewList::AddView(View *pView) { // Try to create the new item ViewItem *pItem = new ViewItem(pView); if (pItem == NULL) // Failed - flag error to caller return FALSE; // Ok - add to list and return success AddTail(pItem); return TRUE; } /******************************************************************************************** > BOOL ViewList::IsViewOptimised(View *pView) Author: Tim_Browse (Xara Group Ltd) Created: 07/12/94 Inputs: pView - the View to test for. Returns: TRUE if the View has been optimised; FALSE if not Purpose: Determine whether the given View is recorded in this list as having been optimised or not. SeeAlso: ViewList; ViewList::AddView ********************************************************************************************/ BOOL ViewList::IsViewOptimised(View *pView) { ViewItem *pItem = (ViewItem *) GetHead(); while (pItem != NULL) { if (pItem->pView == pView) // It's in the list return TRUE; // Try next item pItem = (ViewItem *) GetNext(pItem); } // Not found - inform caller return FALSE; } /******************************************************************************************** > RenderRegionList::RenderRegionList() Author: Tim_Browse (Xara Group Ltd) Created: 9/7/93 Purpose: Initialises the RenderRegionList - in particular, the pointer to the last render region to be rendered. ********************************************************************************************/ RenderRegionList::RenderRegionList() { LastRendered = NULL; ListIsOptimal = FALSE; } /******************************************************************************************** > RenderRegionList::~RenderRegionList() Author: Tim_Browse (Xara Group Ltd) Created: 9/7/93 Inputs: - Outputs: - Returns: - Purpose: Destroys all render regions in the list, and then destroys the list. Errors: - SeeAlso: - ********************************************************************************************/ RenderRegionList::~RenderRegionList() { // Call destructors on all objects in the list RenderRegion *Item = (RenderRegion *) RemoveHead(); while (Item != NULL) { // Delete the list item delete Item; // Try the next item Item = (RenderRegion *) RemoveHead(); } } /******************************************************************************************** > void RenderRegionList::Merge(RenderRegion *NewRegion) Author: Tim_Browse (Xara Group Ltd) Created: 9/7/93 Inputs: NewRegion - the RenderRegion object to add to the list. Outputs: - Returns: - Purpose: Add a render region object to the application's global list of render regions. Errors: - SeeAlso: - ********************************************************************************************/ /* Technical notes: Merging Algorithm ----------------- The New Rect is assumed to be in a Null RenderState (ie. it hasn't rendered yet) Clip any Rectangles in List to new Client Area (and Chapter) For each Rectangle in the List, If the Rect is in the same View as the New Rect, And it is in the same Chapter as the New Rect, If the RenderState is at the start of the Tree, Does the New Rectangle contain the Rect in the List ? Or, is it Adjacent to the Rect in the List ? Yes, then Set Rect in List to Union of the two Rects. Ignoring the RenderState do they Intersect at all ? Yes, then Split the Rect in the list into sub rects excluding the new rect. Add the New Rect to the List. Attempt to Compact the List. ********************************************************************************************/ void RenderRegionList::Merge(RenderRegion *NewRenderRegion) { // If it's a printer region, just render it to completion and return, because // we don't do background printing yet. if (NewRenderRegion->IsPrinting()) { NewRenderRegion->DefaultRender(); return; } // Adjust any render regions that are being scrolled away ClipRectsToClient(); // TRACE( _T("RenderRegionList::Merge GetCount == %d\n"), GetCount()); if (GetCount() == 0) { // Couldn't merge with any others, so just add it to the list. AddTail((ListItem *) NewRenderRegion); // IMMEDIATE REDRAW //#if _DEBUG // ImmediateRender(true); // Added by GAT //#endif return; } // Find out how big a pixel is PixelWidth = NewRenderRegion->GetScaledPixelWidth(); // Attempt to compact the list MergeRenderRegions(NewRenderRegion); // Make sure that there are no render regions that overlap - ever! RemoveRegionOverlap(NewRenderRegion); // add the New RenderRegion on the end of the List AddTail((ListItem *) NewRenderRegion); // Go though all the regions in the list and try to strictly // merge them with all the other in the list TidyUpAdjacentRegions(NewRenderRegion); // Set flag to indicate that one or more new regions have been added, and so we // should check for the possibility of merging all regions the next time we try a // background render. ListIsOptimal = FALSE; // IMMEDIATE REDRAW // ImmediateRender(); } /******************************************************************************************** > void RenderRegionList::TidyUpAdjacentRegions(RenderRegion* pBrandNewRegion) Author: Rik_Heywood (Xara Group Ltd) Created: 29/3/94 Inputs: pBrandNewRegion - The region that has just been added to the list by OnDraw. We should not delete this region during the Tidy up or OnDraw gets upset Purpose: Goes through all the regions in the list and tests to see if any of them line up exactly. If they do, they are joined into a single region ********************************************************************************************/ void RenderRegionList::TidyUpAdjacentRegions(RenderRegion* pBrandNewRegion) { // Need to perform an Nsquared loop through the regions RenderRegion* pRegion = (RenderRegion*) GetHead(); while(pRegion!=NULL) { // Get info about the region that we will need to test DocRect MergeClipRect = pRegion->GetRegionRect(); View* MergeView = pRegion->GetRenderView(); Node* NewContext = pRegion->GetRenderState(); // Find the region after pRegion to start searching from RenderRegion* pCompare = (RenderRegion*) GetNext(pRegion); // loop through all the regions that come after pRegion // see if any are suitbale for merging while(pCompare!=NULL) { // Find the next one now, as we may delete pCompare on the way through RenderRegion* pNextOne = (RenderRegion*) GetNext(pCompare); // We do not want to delete the brand new region, we should delete the others first if (pCompare!=pBrandNewRegion) { // Find the equivalent information in this region ready for testing View* OldView = pCompare->GetRenderView(); Node* OldContext = pCompare->GetRenderState(); // See if they can be merged if ( (MergeView == OldView) && (pRegion->GetRenderSpread()==pCompare->GetRenderSpread()) && ((OldContext==NewContext) || ((pRegion->CanBeMerged()) && (pCompare->CanBeMerged())))) { // So far so good. Just check that they are adjacent // Find this regions clip rect DocRect OldClipRect = pCompare->GetRegionRect(); // see if one of the render regions is EXACTLY adjacent to this one if (OldClipRect.IsAdjacent(MergeClipRect, 0)) { DocRect ClipRect = OldClipRect.Union(MergeClipRect); // Set the clip rect of one of the regions to be the union of both regions // pRegion->SetClipRect(ClipRect, TRUE); pRegion->SetBackmostChangedNode(pCompare->GetBackmostChangedNode()); pRegion->ResetRegion(ClipRect); // Get rid of the other region Remove(pCompare); delete pCompare; } } } // Get the next region pCompare = pNextOne; } // Get the next region in the outer loop pRegion = (RenderRegion*) GetNext(pRegion); } } /******************************************************************************************** > void RenderRegionList::StrictMerge(RenderRegion* pRegionToMerge) Author: Rik_Heywood (Xara Group Ltd) Created: 12/01/94 Inputs: pRegionToMerge - The region that we are planning to add to the list Purpose: Tries to merge the render region supplied with the rest of the list. They must be exactly adjacent ie. They don't intersect, and the union contains no area outside the two rectangles. ********************************************************************************************/ void RenderRegionList::StrictMerge(RenderRegion* pRegionToMerge) { // set a flag to indicate if none of the render regions could be merged BOOL Merged; // loop round until we can find no more render regions to merge together do { // We have not merged anything yet, so set the flag Merged = FALSE; // Find the render region to merges clipping rectangle and context node and // find which view the render region is to be rendering to DocRect MergeClipRect = pRegionToMerge->GetRegionRect(); View* MergeView = pRegionToMerge->GetRenderView(); Node* NewContext = pRegionToMerge->GetRenderState(); // Get the first render region in the list RenderRegion* pOldRegion = (RenderRegion*) GetHead(); RenderRegion* pNextRegion; while (pOldRegion != NULL) { pNextRegion = (RenderRegion *) GetNext(pOldRegion); // Find this old render regions view View* OldView = pOldRegion->GetRenderView(); Node* OldContext = pOldRegion->GetRenderState(); // see if the two render regions can be merged if ( (MergeView == OldView) && (pOldRegion->GetRenderSpread()==pRegionToMerge->GetRenderSpread()) && ((OldContext==NewContext) || ((pOldRegion->CanBeMerged()) && (pRegionToMerge->CanBeMerged())))) { // Find this regions clip rect DocRect OldClipRect = pOldRegion->GetRegionRect(); // see if one of the render regions is EXACTLY adjacent to this one if (OldClipRect.IsAdjacent(MergeClipRect, 0)) { DocRect ClipRect = OldClipRect.Union(MergeClipRect); // Set the clip rect and get the render region to find its starting context node again // as things could have changed. // pRegionToMerge->SetClipRect(ClipRect, TRUE); pRegionToMerge->ResetRegion(ClipRect); Remove(pOldRegion); delete pOldRegion; Merged = TRUE; break; } } // Get the next render region in the list // pOldRegion = (RenderRegion *) GetNext(pOldRegion); pOldRegion = pNextRegion; } } while( Merged ); AddTail( (ListItem *) pRegionToMerge); } /******************************************************************************************** > void RenderRegionList::MergeRenderRegions() Author: Tim_Browse (Xara Group Ltd) Created: 12/7/93 Inputs: - Outputs: - Returns: - Purpose: Attempts to find regions which can be merged together to make redrawing more efficient. At the moment, the two regions to be merged must satisfy the following conditions: * They must be have got to the same node in the tree when they last stopped rendering. * They must be rendering into the same View and Chapter. * One of the regions must completely enclose or be adjacent to the other. Errors: - SeeAlso: - ********************************************************************************************/ void RenderRegionList::MergeRenderRegions(RenderRegion* pRegionToMerge) { // set a flag to indicate if none of the render regions could be merged BOOL Merged; // loop round until we can find no more render regions to merge together do { // We have not merged anything yet, so set the flag Merged = FALSE; // Find the render region to merges clipping rectangle and context node DocRect MergeClipRect = pRegionToMerge->GetRegionRect(); // Find which view the render region is to be rendering to View* MergeView = pRegionToMerge->GetRenderView(); // Get the first render region in the list RenderRegion* pOldRegion = (RenderRegion*) GetHead(); RenderRegion* pNextRegion; while (pOldRegion != NULL) { pNextRegion = (RenderRegion *) GetNext(pOldRegion); if (pOldRegion != pRegionToMerge) { // Find this old render regions view View* OldView = pOldRegion->GetRenderView(); // see if the two render regions can be merged if ( (MergeView == OldView) && (pOldRegion->GetRenderSpread()==pRegionToMerge->GetRenderSpread()) && (pOldRegion->CanBeMerged()) && (pRegionToMerge->CanBeMerged())) { // Find this regions clip rect DocRect OldClipRect = pOldRegion->GetRegionRect(); // see if one of the render regions contains the other if (MergeClipRect.ContainsRect(OldClipRect)) { // Get rid of the region that is inside the other pRegionToMerge->SetBackmostChangedNode(pOldRegion->GetBackmostChangedNode()); pRegionToMerge->NeedsOSPaper = pRegionToMerge->NeedsOSPaper && pOldRegion->NeedsOSPaper; Remove(pOldRegion); delete pOldRegion; Merged = TRUE; break; } else if (OldClipRect.ContainsRect(MergeClipRect)) { // Get rid of the region that is inside the other // pRegionToMerge->SetClipRect(OldClipRect, TRUE); pRegionToMerge->SetBackmostChangedNode(pOldRegion->GetBackmostChangedNode()); pRegionToMerge->ResetRegion(OldClipRect); pRegionToMerge->NeedsOSPaper = pRegionToMerge->NeedsOSPaper && pOldRegion->NeedsOSPaper; Remove(pOldRegion); delete pOldRegion; Merged = TRUE; break; } else { // Find the smallest dimension of the new region INT32 ShortSide = MergeClipRect.Height(); if (MergeClipRect.Width() < ShortSide) ShortSide = MergeClipRect.Width(); // Make sure that this figure is smaller than the sides ShortSide /= 3; // Want a fuzzyness of at least 64 pixels INT32 MinFuzzy = PixelWidth * 64; if (ShortSideSetClipRect(ClipRect, TRUE); pRegionToMerge->SetBackmostChangedNode(pOldRegion->GetBackmostChangedNode()); pRegionToMerge->ResetRegion(ClipRect); pRegionToMerge->NeedsOSPaper = pRegionToMerge->NeedsOSPaper && pOldRegion->NeedsOSPaper; Remove(pOldRegion); delete pOldRegion; Merged = TRUE; break; } } } } // Get the next render region in the list // pOldRegion = (RenderRegion *) GetNext(pOldRegion); pOldRegion = pNextRegion; } } while( Merged ); } /******************************************************************************************** > void RenderRegionList::RemoveRegionOverlap(RenderRegion* NewRenderRegion) Author: Rik_Heywood (Xara Group Ltd) Created: 10/01/94 Inputs: NewRenderRegion - pointer to the render region being added Purpose: Called when adding a new render region to the list. It checks the new render region for overlap with all the old render regions and if it overlapps any of them, the old region is slit up. This function should guarantee that you never get render regions that overlap, so that the EORed stuff does not get screwed up by getting rendered more than once. ********************************************************************************************/ void RenderRegionList::RemoveRegionOverlap(RenderRegion* NewRenderRegion) { // Find the clipping rect of the new render region DocRect NewClipRect = NewRenderRegion->GetRegionRect(); DocRect OldClipRect; // Find which view and spread the render region is to be rendering to View* NewView = NewRenderRegion->GetRenderView(); Spread* NewSpread = NewRenderRegion->GetRenderSpread(); // Get the first render region in the list RenderRegion* pOldRegion = (RenderRegion*) GetHead(); while (pOldRegion != NULL) { // Get the old render regions clipping rectangle OldClipRect = pOldRegion->GetRegionRect(); // Find which view and spread the render region is to be rendering to View* OldView = pOldRegion->GetRenderView(); Spread* OldSpread = pOldRegion->GetRenderSpread(); // If they intersect, split the old rectangle up into bits if ((OldView==NewView) && (OldSpread==NewSpread) && (OldClipRect.IsIntersectedWith(NewClipRect))) { if (NewClipRect.ContainsRect(OldClipRect)) { // The 2 regions were the same, so get rid of it, and go back around // the loop with the next region in the list. NewRenderRegion->SetBackmostChangedNode(pOldRegion->GetBackmostChangedNode()); NewRenderRegion->NeedsOSPaper = NewRenderRegion->NeedsOSPaper && pOldRegion->NeedsOSPaper; RenderRegion *Tmp = pOldRegion; pOldRegion = (RenderRegion *) GetNext(pOldRegion); Remove(Tmp); delete Tmp; continue; } else { // Split the old rect into sub-rects and add the new one DocRect SubRects[4]; INT32 RectCount = OldClipRect.SplitRect(NewClipRect, SubRects); ENSURE( RectCount!=0, "No rectangles found in SplitRect" ); // Change the clip rect of the region already in the list // pOldRegion->ResizeRegion(SubRects[0]); // pOldRegion->SetClipRect(SubRects[0]); pOldRegion->ResetRegion(SubRects[0]); // and create some new render regions if needed for (INT32 i=1; iGetMatrix(), OldView->GetViewScale(), RENDERTYPE_SCREEN, NewView); //OSRenderRegion* SubRenderRegion = new OSRenderRegion(*(OSRenderRegion*)pOldRegion); ENSURE( SubRenderRegion != NULL, "RenderRegionList::Merge: SubRenderRegion == 0"); if (SubRenderRegion == NULL) return; // Set the new clip region and see if it joins exactly if (!SubRenderRegion->CopyRenderInfo(*pOldRegion)) { // copying failed, so tidy up delete SubRenderRegion; TRACE( _T("CopyRenderInfo %d failed\n"), i); continue; // carry on round loop } // make sure the region is re-rendered // SubRenderRegion->IsPaperRendered = FALSE; // SubRenderRegion->IsInkRenderStarted = FALSE; // SubRenderRegion->SetClipRect(SubRects[i]); SubRenderRegion->ResetRegion(SubRects[i]); // SubRenderRegion->ResizeRegion(SubRects[i]); StrictMerge(SubRenderRegion); } } } // Get the next region in the list pOldRegion = (RenderRegion*) GetNext(pOldRegion); } } /******************************************************************************************** > void RenderRegionList::ClipRectsToClient() Author: Will_Cowling (Xara Group Ltd) Created: 21/7/93 Inputs: - Outputs: - Returns: - Purpose: Clips the RenderRegions in the list to the client area, incase it has been scrolled. Errors: - Scope: Private. SeeAlso: - ********************************************************************************************/ void RenderRegionList::ClipRectsToClient() { RenderRegion *pRegion = (RenderRegion*) GetHead(); while (pRegion != NULL) { // Find the view associated with the window and skip if null. View* pView = pRegion->GetRenderView(); if (pView != NULL && DocView::GetCurrent() == pView) { // Get the views rectangle, and convert it into spread coords DocRect ClientRect = pView->GetDocViewRect(pRegion->GetRenderSpread()); pRegion->GetRenderSpread()->DocCoordToSpreadCoord(&ClientRect); // Get the clipping rectangle of this render region DocRect ClipRect = pRegion->GetRegionRect(); // see if we need to lose any parts of the render region if (!ClientRect.ContainsRect(ClipRect)) { if (ClipRect.IsIntersectedWith(ClientRect)) { ClipRect = ClipRect.Intersection(ClientRect); pRegion->ResizeRegion(ClipRect); // pRegion->SetClipRect(ClipRect); // pRegion->ResetRegion(ClipRect); } else { // This render region is now off screen, so we can throw it away RenderRegion* pPrev = (RenderRegion *) GetPrev(pRegion); Remove(pRegion); delete pRegion; pRegion = pPrev; } } } // Try the next item. pRegion = (RenderRegion*) ((pRegion != NULL) ? GetNext(pRegion) : GetHead()); } } /******************************************************************************************** > void RenderRegionList::Remove(RenderRegion *RegionToRemove) Author: Tim_Browse (Xara Group Ltd) Created: 9/7/93 Inputs: RegionToRemove - the render region object to remove from the list. Outputs: - Returns: - Purpose: Removes a render region from the application's list of render regions, It DOES NOT delete the RenderRegion object itself. This is used when the region has been completely rendered and is no longer needed. Errors: - SeeAlso: - ********************************************************************************************/ void RenderRegionList::Remove(RenderRegion *RegionToRemove) { ENSURE( FindPosition(RegionToRemove) != NOT_IN_LIST, "Could not find RenderRegion to Remove" ); // Is it the LastRendered one? if (RegionToRemove == LastRendered) { // Yes - so make LastRendered point to the previous render region // which means if Item is first in the list, LastRendered is set to // NULL, which is what we want - see BackgroundRender(). LastRendered = (RenderRegion *) GetPrev(RegionToRemove); } // Remove the item from the list RemoveItem((ListItem *) RegionToRemove); } /******************************************************************************************** > void RenderRegionList::Remove(Document *pParentDoc) Author: Tim_Browse (Xara Group Ltd) Created: 9/7/93 Inputs: pParentDoc - the parent document that is being removed. Purpose: Removes all render regions from the application's list of render regions that are associated with a particular document. Usually called because the document is being closed (deleted). It deletes all RenderRegion objects it removes. HOWEVER: When a Document is being closed, all of its DocViews should already have been closed, so there should be NO render regions still in the list that are associated with this document, so this function will ENSURE whenever it finds a render region belonging to this document. NB. This is a debugging aid, and hence it only exists in debug builds. ********************************************************************************************/ #if _DEBUG void RenderRegionList::Remove(Document *pParentDoc) { #if 0 // Find the NodeDocument object of the tree associated with this document Node *pRoot = pParentDoc->GetFirstNode(); ENSURE(pRoot != NULL, "Document has no root node"); pRoot = pRoot->FindNext(); ENSURE(pRoot != NULL, "Document has no NodeDocument node"); // Start at the beginning RenderRegion *Item = (RenderRegion *) GetHead(); // Look for this render region while (Item != NULL) { // Find its parent NodeDocument object Node *pNode = Item->GetRenderChapter(); ENSURE(pNode != NULL, "RenderRegion has no Chapter!"); Node *pParent = pNode->FindParent(); ENSURE(pParent != NULL, "Chapter has no parent!"); // Find ultimate parent while (pParent != NULL) { pNode = pParent; pParent = pNode->FindParent(); } // Is it the same as the document passed by the caller? if (pNode == pRoot) { // Is it the LastRendered one? if (Item == LastRendered) { // Yes - so make LastRendered point to the previous render region // which means if Item is first in the list, LastRendered is set to // NULL, which is what we want - see BackgroundRender(). LastRendered = (RenderRegion *) GetPrev(Item); } // Move to next region and remove this one from the list and delete it. RenderRegion *Tmp = Item; Item = (RenderRegion *) GetNext(Item); RemoveItem((ListItem *) Tmp); delete Tmp; // Bitch about the fact that this should not have happened ENSURE(FALSE, "Found a render region that should have been deleted"); } else // Try the next region Item = (RenderRegion *) GetNext(Item); } #endif } #endif /******************************************************************************************** > void RenderRegionList::Remove(View *pView) Author: Tim_Browse (Xara Group Ltd) Created: 9/7/93 Inputs: pView - the parent view that is being removed. Purpose: Removes all render regions from the application's list of render regions that are associated with a particular view. Usually called because the docview is being closed (deleted). It deletes all RenderRegion objects it removes. ********************************************************************************************/ void RenderRegionList::Remove(View *pView) { // Start at the beginning and try to find render regions attached to this CWnd object. RenderRegion *Item = (RenderRegion *) GetHead(); while (Item != NULL) { // Is it the same as the View passed by the caller? if (Item->GetRenderView() == pView) { // Is it the LastRendered one? if (Item == LastRendered) { // Yes - so make LastRendered point to the previous render region // which means if Item is first in the list, LastRendered is set to // NULL, which is what we want - see BackgroundRender(). LastRendered = (RenderRegion *) GetPrev(Item); } // Move to next region and remove this one from the list and delete it. RenderRegion *Tmp = Item; Item = (RenderRegion *) GetNext(Item); RemoveItem((ListItem *) Tmp); delete Tmp; } else // Try the next region Item = (RenderRegion *) GetNext(Item); } } /******************************************************************************************** > BOOL RenderRegionList::BackgroundRender() Author: Tim_Browse (Xara Group Ltd) Created: 9/7/93 Inputs: - Outputs: - Returns: TRUE if rendering was done, FALSE if no render regions to render. Purpose: Cycles through the members of the render region list, rendering each one in turn. Only one render region is rendered on each call to BackgroundRender(). Errors: - SeeAlso: - ********************************************************************************************/ BOOL RenderRegionList::BackgroundRender() { if ( CCamApp::IsDisabled() ) { // TRACE( wxT("BGR - IsDisabled\n") ); return FALSE; // If he has got the system disabled, ignore } // print monitor not present on viewer // Make sure printing is ok. if (!PrintMonitor::IsPrintStatusOK()) { // No - a print job has gone wrong, so we delete all printing render regions // before continuing. RenderRegion *pRender = (RenderRegion *) GetHead(); while (pRender != NULL) { if (pRender->IsPrinting()) { // Render region is printing (well it's not really as an exception // has occured) so delete it. Remove(pRender); delete pRender; } // Try next region pRender = (RenderRegion *) GetNext(pRender); } // Reset the last rendered render region LastRendered = NULL; } if (GetCount() == 0) { // TRACE( wxT("BGR - No regions\n") ); return FALSE; } //GAT // if (ControlHelper::IsUserInterfaceCaptured()) // return FALSE; // No bg rendering if UI is captured (e.g. menu is up) // // if (DragManagerOp::IsDragActive()) // return FALSE; // No bg rendering if a DragManager op is active (NB this is for // colour/bitmap/bar drags only - not doc operation drags). if (ViewOps::IsFullScreenPending()) // no bg rendering if we are about to enter full screen mode { // TRACE( wxT("BGR - About to full screen\n") ); return FALSE; } // Optimise list based on render region percentage coverage TryTotalUnion(); // Work out which render region to render if (LastRendered != NULL) LastRendered = (RenderRegion*) GetNext(LastRendered); if (LastRendered == NULL) LastRendered = (RenderRegion*) GetHead(); if (LastRendered == NULL) { // TRACE( wxT("BGR - No last region\n") ); return FALSE; // No regions to render } // Ignore the RenderRegion if we are Printing, because // we have probably been called from the Print AbortProc // and are already rendering. if (LastRendered->IsPrinting()) { // TRACE( wxT("BGR - Is printing\n") ); return FALSE; } // TRACEUSER("Gerry", _T("BackgroundRender\n")); // Show the user we are rendering. if (GetApplication()->GetpStatusLine()) GetApplication()->GetpStatusLine()->SetRenderIndicator(Animate); GetApplication()->BgRendering = TRUE; // Do the render. Error::RenderThreadIn(); // Make errors close window LastRendered->DefaultRender(); Error::RenderThreadOut(); // Normal errors // Turn off render indicator if needed. if (GetCount() ==0) { if (GetApplication()->GetpStatusLine()) GetApplication()->GetpStatusLine()->SetRenderIndicator(NotRendering); GetApplication()->BgRendering = FALSE; } // We did some rendering so return TRUE return TRUE; } /******************************************************************************************** > void RenderRegionList::ImmediateRender(BOOL bForceImmediate = FALSE) Author: Rik_Heywood (Xara Group Ltd) Created: 05/01/94 Purpose: Finds all render regions associated with DocViews that have background rendering disabled, and renders them all immediately (i.e. to completion). ********************************************************************************************/ void RenderRegionList::ImmediateRender(BOOL bForceImmediate) { TRACE( wxT("Warning - RenderRegionList::ImmediateRender - removed code\n") ); // TRACE( _T("Look, I'm in RenderRegionList::ImmediateRender()\n")); if ( CCamApp::IsDisabled() ) return; // If he has got the system disabled, ignore #ifndef STANDALONE // Make sure printing is ok. if (!PrintMonitor::IsPrintStatusOK()) { // No - a print job has gone wrong, so we delete all printing render regions // before continuing. RenderRegion *pRender = (RenderRegion *) GetHead(); while (pRender != NULL) { if (pRender->IsPrinting()) { // Render region is printing (well it's not really as an exception // has occured) so delete it. Remove(pRender); delete pRender; } // Try next region pRender = (RenderRegion *) GetNext(pRender); } // Reset the last rendered render region LastRendered = NULL; } #endif if (GetCount() == 0) return; PORTNOTE("other", "Disabled ControlHelper") #ifndef EXCLUDE_FROM_XARALX if (ControlHelper::IsUserInterfaceCaptured()) return; // No bg rendering if UI is captured (e.g. menu is up) #endif if (DragManagerOp::IsDragActive()) return; // No bg rendering if a DragManager op is active (NB this is for // colour/bitmap/bar drags only - not doc operation drags) if(ViewOps::IsFullScreenPending()) // no bg rendering if we are about to enter full screen mode return; // Optimise list based on render region percentage coverage TryTotalUnion(); RenderRegion *pRegion = (RenderRegion *) GetHead(); // Start the rendering indicator going. if (GetApplication()->GetpStatusLine()) GetApplication()->GetpStatusLine()->SetRenderIndicator(Rendering); while (pRegion != NULL) { // Find the next region before this one is deleted RenderRegion* pNext = (RenderRegion *) GetNext(pRegion); if (pRegion->IsPrinting() == FALSE) { // If this render region's DocView does not have background rendering enabled, // then it must be an immediate render region, so render it. View *pView = pRegion->GetRenderView(); ENSURE(pView != NULL, "No DocView in ImmediateRender!"); if ( !pView->GetForeBackMode() || bForceImmediate ) { // Keep user up to date if (GetApplication()->GetpStatusLine()) GetApplication()->GetpStatusLine()->SetRenderIndicator(Animate); //TRACE( _T("Immediate Render now\n")); // Render it. Error::RenderThreadIn(); // Make errors close window pRegion->DefaultRender(bForceImmediate); Error::RenderThreadOut(); // Normal errors } } // Find the next render region pRegion = pNext; } // Disable rendering indicator. if (GetApplication()->GetpStatusLine()) GetApplication()->GetpStatusLine()->SetRenderIndicator(NotRendering); } /******************************************************************************************** > void RenderRegionList::HandleNodeDeletion(Node* pNode) Author: Phil_Martin (Xara Group Ltd) Created: 10/08/2004 Purpose: Tells RenderRegions that they should be afraid, very afraid, if they are holding a pointer to this node... ********************************************************************************************/ void RenderRegionList::HandleNodeDeletion(Node* pNode) { if (GetCount() == 0) return; RenderRegion *pRegion = (RenderRegion *) GetHead(); while (pRegion != NULL) { // Find the next region before this one is deleted RenderRegion* pNext = (RenderRegion *) GetNext(pRegion); // TODO: Could attack the context pointer here as well! // Make sure we're not holding onto a pointer to this node as the "backmost" pRegion->ClearBackmostChangedNode(pNode); // And hope that this region is about to get merged out of existence!!! // Find the next render region pRegion = pNext; } } /******************************************************************************************** > void RenderRegionList::TryTotalUnion() Author: Tim_Browse (Xara Group Ltd) Created: 07/12/94 Purpose: For each DocView, try to union all the render regions, and see if the individual areas add up to more than 70% of the union area. If they do then we union all the regions together and just render one big one. SeeAlso: RenderRegionList::DoTotalUnion ********************************************************************************************/ void RenderRegionList::TryTotalUnion() { if (ListIsOptimal) // No need to do our checks return; DocRect TotalUnion; // List of DocViews we have optimised so far ViewList Views; // Go through each render region, and for each View, work out if it's worth // merging all the rectangles into one. RenderRegion *pRegion = (RenderRegion *) GetHead(); while (pRegion != NULL) { View *pView = pRegion->GetRenderView(); Spread* pSpread = pRegion->GetRenderSpread(); // Have we optimised this region's View already? if (!Views.IsViewOptimised(pView)) { // Haven't done this one yet - add it to the list for future reference if (!Views.AddView(pView)) { // Error - abort merging Views.DeleteAll(); return; } // We only do this merging for regions that have not started ink rendering yet. if (pRegion->CanBeMerged()) { // Get the union of all regions in this View, and the total area of them RenderRegion *pRegion2 = pRegion; // Indicates whether to test the percentage coverage - we don't if either: // (a) This View has only one region, or // (b) This View has regions which have started drawing ink already. BOOL TryUnion = FALSE; DocRect TotalUnion; double CombinedArea = 0.0; while (pRegion2 != NULL) { // Is this the correct View and render context? if (pRegion2->GetRenderView() == pView && pRegion2->GetRenderSpread() == pSpread ) { // More than one region - we can try to union them TryUnion = TRUE; // Have we started rendering the ink into this region yet? if (!pRegion2->CanBeMerged()) { // Yes, and we can't union if any have started rendering ink TryUnion = FALSE; // Quit the while loop break; } else { // No - so accumulate this region. DocRect ClipRect = pRegion2->GetRegionRect(); TotalUnion = TotalUnion.Union(ClipRect); CombinedArea += ((double) ClipRect.Width()) * ((double) ClipRect.Height()); } } // Find the next render region pRegion2 = (RenderRegion *) GetNext(pRegion2); } if (TryUnion) { // Ok - if the combined area is 70% or more of the area of the union, then // we might as well union all these regions. double UnionArea = (double) TotalUnion.Width() * (double) TotalUnion.Height(); double Coverage = CombinedArea / UnionArea; if (Coverage > 0.60) { // Yes - let's union all these regions DoTotalUnion(pRegion, pView, TotalUnion); } } } } // Find the next render region pRegion = (RenderRegion *) GetNext(pRegion); } // Don't bother with this again unless a new render region is added. ListIsOptimal = TRUE; } /******************************************************************************************** > void RenderRegionList::DoTotalUnion(RenderRegion *pRegion, View *pView, DocRect TotalUnion) Author: Tim_Browse (Xara Group Ltd) Created: 07/12/94 Inputs: pRegion - the region to put the merged area into. pView - the View that should have all its regions merged. TotalUnion - the union of all the regions for this DocView. Purpose: For the specified DocView, find all the regions that are attached to it, and union them all together and use the region 'pRegion' to hold this new region. All the other regions of this DocView are removed from the list and deleted. SeeAlso: RenderRegionList::TryTotalUnion ********************************************************************************************/ void RenderRegionList::DoTotalUnion(RenderRegion *pRegion, View *pView, DocRect TotalUnion) { pRegion->ResetRegion(TotalUnion); Spread* pSpread = pRegion->GetRenderSpread(); // Delete all the others RenderRegion *pRegion2 =(RenderRegion *) GetNext(pRegion); while (pRegion2 != NULL) { // Remember the 'next' region because we might delete this one. RenderRegion *pNextRegion = (RenderRegion *) GetNext(pRegion2); // Is this the correct DocView and render context? if ((pRegion2->GetRenderView() == pView) && (pRegion2->GetRenderSpread() == pSpread) && (pRegion2->CanBeMerged())) { // Yes - delete this region pRegion->NeedsOSPaper = pRegion->NeedsOSPaper && pRegion2->NeedsOSPaper; Remove(pRegion2); delete pRegion2; // Make sure we don't have a dangling pointer to this region. if (pRegion2 == LastRendered) // Start from the beginning of the list again. LastRendered = NULL; } // Find the next render region pRegion2 = pNextRegion; } }