osrndrgn.cpp

Go to the documentation of this file.
00001 // $Id: osrndrgn.cpp 1571 2006-07-27 15:18:28Z alex $
00002 /* @@tag:xara-cn@@ DO NOT MODIFY THIS LINE
00003 ================================XARAHEADERSTART===========================
00004  
00005                Xara LX, a vector drawing and manipulation program.
00006                     Copyright (C) 1993-2006 Xara Group Ltd.
00007        Copyright on certain contributions may be held in joint with their
00008               respective authors. See AUTHORS file for details.
00009 
00010 LICENSE TO USE AND MODIFY SOFTWARE
00011 ----------------------------------
00012 
00013 This file is part of Xara LX.
00014 
00015 Xara LX is free software; you can redistribute it and/or modify it
00016 under the terms of the GNU General Public License version 2 as published
00017 by the Free Software Foundation.
00018 
00019 Xara LX and its component source files are distributed in the hope
00020 that it will be useful, but WITHOUT ANY WARRANTY; without even the
00021 implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00022 See the GNU General Public License for more details.
00023 
00024 You should have received a copy of the GNU General Public License along
00025 with Xara LX (see the file GPL in the root directory of the
00026 distribution); if not, write to the Free Software Foundation, Inc., 51
00027 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
00028 
00029 
00030 ADDITIONAL RIGHTS
00031 -----------------
00032 
00033 Conditional upon your continuing compliance with the GNU General Public
00034 License described above, Xara Group Ltd grants to you certain additional
00035 rights. 
00036 
00037 The additional rights are to use, modify, and distribute the software
00038 together with the wxWidgets library, the wxXtra library, and the "CDraw"
00039 library and any other such library that any version of Xara LX relased
00040 by Xara Group Ltd requires in order to compile and execute, including
00041 the static linking of that library to XaraLX. In the case of the
00042 "CDraw" library, you may satisfy obligation under the GNU General Public
00043 License to provide source code by providing a binary copy of the library
00044 concerned and a copy of the license accompanying it.
00045 
00046 Nothing in this section restricts any of the rights you have under
00047 the GNU General Public License.
00048 
00049 
00050 SCOPE OF LICENSE
00051 ----------------
00052 
00053 This license applies to this program (XaraLX) and its constituent source
00054 files only, and does not necessarily apply to other Xara products which may
00055 in part share the same code base, and are subject to their own licensing
00056 terms.
00057 
00058 This license does not apply to files in the wxXtra directory, which
00059 are built into a separate library, and are subject to the wxWindows
00060 license contained within that directory in the file "WXXTRA-LICENSE".
00061 
00062 This license does not apply to the binary libraries (if any) within
00063 the "libs" directory, which are subject to a separate license contained
00064 within that directory in the file "LIBS-LICENSE".
00065 
00066 
00067 ARRANGEMENTS FOR CONTRIBUTION OF MODIFICATIONS
00068 ----------------------------------------------
00069 
00070 Subject to the terms of the GNU Public License (see above), you are
00071 free to do whatever you like with your modifications. However, you may
00072 (at your option) wish contribute them to Xara's source tree. You can
00073 find details of how to do this at:
00074   http://www.xaraxtreme.org/developers/
00075 
00076 Prior to contributing your modifications, you will need to complete our
00077 contributor agreement. This can be found at:
00078   http://www.xaraxtreme.org/developers/contribute/
00079 
00080 Please note that Xara will not accept modifications which modify any of
00081 the text between the start and end of this header (marked
00082 XARAHEADERSTART and XARAHEADEREND).
00083 
00084 
00085 MARKS
00086 -----
00087 
00088 Xara, Xara LX, Xara X, Xara X/Xtreme, Xara Xtreme, the Xtreme and Xara
00089 designs are registered or unregistered trademarks, design-marks, and/or
00090 service marks of Xara Group Ltd. All rights in these marks are reserved.
00091 
00092 
00093       Xara Group Ltd, Gaddesden Place, Hemel Hempstead, HP2 6EX, UK.
00094                         http://www.xara.com/
00095 
00096 =================================XARAHEADEREND============================
00097  */
00098 
00099 // OSRenderRegion class is responsible for rendering through GDI
00100 
00101 /*
00102 */
00103 
00104 
00105 #include "camtypes.h"
00106 
00107 #include "osrndrgn.h"   // This also includes 'rndrgn.h' and 'paths.h'
00108 #include <math.h>       // Uses 'atn' to find the angle of a line
00109 //#include "app.h"      // Need definition of 'Camelot' - in camtypes.h [AUTOMATICALLY REMOVED]
00110 // #include "mainfrm.h"
00111 //#include "docview.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00112 //#include "winrect.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00113 //#include "ensure.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00114 #include "camelot.h"
00115 //#include "fixmem.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00116 #include "rendbits.h"
00117 #include "grndrgn.h"
00118 //#include "attr.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00119 //#include "fillattr.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00120 #include "devcolor.h"
00121 #include "oilbitmap.h"
00122 //#include "bitmap.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00123 //#include "attrmgr.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00124 //#include "tool.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00125 // #include "oiltool.h"
00126 #include "camview.h"
00127 // #include "fonts.h"       // For FixedSystem font from the FontFactory
00128 //#include "dibutil.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00129 #include "fuzzclip.h"
00130 #include "nodeelip.h"
00131 #include "noderect.h"
00132 #include "princomp.h"
00133 #include "printctl.h"
00134 #include "psrndrgn.h"
00135 #include "palman.h"
00136 //#include "rrcaps.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00137 #include "maskedrr.h"
00138 // #include "prdlgctl.h"
00139 #include "textfuns.h"
00140 #include "colcontx.h"
00141 // #include "prncamvw.h"
00142 #include "nodebmp.h"
00143 #include "fontman.h"
00144 // #include "metaview.h"
00145 #include "gradtbl.h"
00146 #include "unicdman.h"  // For MBCS support
00147 #include "mrhbits.h"
00148 #include "strkattr.h"
00149 #include "cartprov.h"
00150 #include "lineattr.h"
00151 
00152 DECLARE_SOURCE("$Revision: 1571 $");
00153 
00154 /********************************************************************************************
00155 
00156     Preference: GDIPalette
00157     Section:    Screen
00158     Range:      1 or 0
00159     Purpose:    Allows the user to determine whether a special palette is used when rendering
00160                 with GDI. Ingored except on modes that support palettes (e.g. 256 colours).
00161                 Defaults to 1.
00162 
00163 ********************************************************************************************/
00164 
00165 static BOOL WantGDIPalette = TRUE;                  // TRUE if GDI palette wanted
00166 
00167 
00168 
00169 
00170 /********************************************************************************************
00171 
00172     Preference: BetterLines
00173     Section:    Screen
00174     Range:      0 (auto), 1 (yes), 2 (no)
00175     Purpose:    Allows the user to determine whether we let GDI do lines (2) or we do them
00176                 (1). When we do them we do end caps, mitres etc properly. 0 means let GDI32
00177                 do them, but we do them on GDI16.
00178                 Defaults to 0
00179 
00180 ********************************************************************************************/
00181 
00182 static BOOL WantBetterLines;
00183 
00184 
00185 
00186 
00187 /********************************************************************************************
00188 
00189     Preference: PrintRotatedTextAsPaths
00190     Section:    Printing
00191     Range:      1 or 0
00192     Purpose:    Allows the user to force printing on non-Postscript printers to output all
00193                 rotated characters as paths instead of using GDI text rendering.
00194                 Note that "Rotated" means rotated on the physical paper.
00195                 Defaults to FALSE (0).
00196 
00197 ********************************************************************************************/
00198 
00199 static BOOL PrintRotatedTextAsPaths = FALSE;        // FALSE because we want to allow the printer
00200                                                     // to attempt to render rotated text.
00201 
00202 
00203 
00204 /********************************************************************************************
00205 
00206     Preference: HitTestRadius
00207     Section:    Mouse
00208     Range:      0 to 10
00209     Purpose:    Allows the user to determine how close a mouse click can be to an object
00210                 before the object is hit-tested successfully.  Measured in pixels.
00211                 Defaults to 3.
00212 
00213 ********************************************************************************************/
00214 
00215 INT32 OSRenderRegion::HitTestRadius = 3;                // default hit-detect sensitivity
00216 
00217 
00218 
00219 
00220 /********************************************************************************************
00221 
00222     Preference: SlowRectangles
00223     Section:    DisplayKludges
00224     Range:      0 or 1
00225     Purpose:    Set to 1 for video drivers that cannot plot rectangles with line borders
00226                 correctly e.g. Avance 1.5F in 256 colours. Defaults to 0.
00227 
00228 ********************************************************************************************/
00229 
00230 BOOL BodgeRectangles = FALSE;                       // TRUE if hollow rects need drawing with Polygon
00231 
00232 
00233 
00234 
00235 // Other, unlabelled prefs and things...
00236 EORCacheClass OSRenderRegion::EORCache;             // new implementation of the EOR colour cache
00237 
00238 BOOL OSRenderRegion::DoBetterLines;
00239 
00240 wxPoint OSRenderRegion::PointArray[SIZEOF_POLYLINE_BUFFER]; // for flattening paths
00241 
00242 static INT32 GradFillQuality = 1;
00243 
00244 CC_IMPLEMENT_DYNAMIC(OSRenderRegion, RenderRegion)
00245 CC_IMPLEMENT_DYNAMIC(PaperRenderRegion, OSRenderRegion)
00246 
00247 #define new CAM_DEBUG_NEW
00248 
00249 
00250 OSRRFontInfo::OSRRFontInfo()
00251 {
00252     Rotation = FIXED16(0);
00253 }
00254 
00255 
00256 
00257 /********************************************************************************************
00258 
00259 >   BOOL OSRenderRegion::Init()
00260 
00261     Author:     Andy_Pennell (Xara Group Ltd) <camelotdev@xara.com>
00262     Created:    11/3/94
00263     Inputs:     -
00264     Returns:    TRUE if worked, FALSE if didnt (cannot fail at present)
00265     Purpose:    To be called during startup. Reads preferences.
00266     Scope:      Static
00267 
00268 ********************************************************************************************/
00269 
00270 BOOL OSRenderRegion::Init()
00271 {
00272     if (Camelot.DeclareSection(TEXT("DisplayKludges"), 10))
00273     {
00274         Camelot.DeclarePref( NULL, TEXT("SlowRectangles"), &BodgeRectangles, FALSE, TRUE );
00275     }
00276 
00277     if (Camelot.DeclareSection(TEXT("Printing"), 10))
00278     {
00279         Camelot.DeclarePref( NULL, TEXT("PrintRotatedTextAsShapes"), &PrintRotatedTextAsPaths, FALSE, TRUE );
00280     }
00281 
00282     if (Camelot.DeclareSection(TEXT("Screen"), 10))
00283     {
00284         Camelot.DeclarePref(NULL, TEXT("GDIPalette"), &WantGDIPalette, FALSE, TRUE );
00285         Camelot.DeclarePref(NULL, TEXT("BetterLines"), &WantBetterLines, 0, 2 );
00286         
00287         switch (WantBetterLines)
00288         {
00289             case 0:
00290                 DoBetterLines = TRUE;
00291                 break;
00292 
00293             case 1:
00294                 DoBetterLines = TRUE;
00295                 break;
00296 
00297             case 2:
00298                 DoBetterLines = FALSE;
00299                 break;
00300         }
00301     }
00302 
00303     if (Camelot.DeclareSection(TEXT("Mouse"), 10))
00304     {
00305         Camelot.DeclarePref(NULL, TEXT("HitTestRadius"), &HitTestRadius, 0, 10);
00306     }
00307 
00308     return TRUE;
00309 }
00310 
00311 
00312 
00313 /********************************************************************************************
00314 
00315 >   RenderRegion *OSRenderRegion::Create(DocRect ClipRect, Matrix ConvertMatrix, 
00316                                             FIXED16 ViewScale, RenderType rType,
00317                                             DocView* pView = NULL,
00318                                             BOOL UseOSRendering = FALSE)
00319 
00320 
00321     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00322     Created:    16/9/93
00323 
00324     Inputs:     ClipRect - a DocRect defining the invalid rectangle to be rendered.
00325 
00326                 ConvertMatrix - a Matrix for converting Doc coords to OS coords.
00327 
00328                 ViewScale - the scale factor of the view, used to calculate how much to
00329                 flatten paths etc.
00330 
00331                 rType - helps the choice of 32-bit rendering etc.
00332 
00333                 pView - The View (if any) for which the RenderRegion is to be used
00334 
00335                 UseOSRendering - TRUE to force the system to give you an OSRenderRegion,
00336                 rather than diverting this call to really give you a GRenderRegion if
00337                 GDraw is enabled (used by kernel-rendered dialogues which don't want
00338                 transparency/grad fills, etc, and wish to render simple FixedSystem text)
00339 
00340     Purpose:    Creates an RenderRegion, allowing a invalid rectangle to be specified.
00341                 The matrix passed is used to convert Document coords into device coords,
00342                 taking into account scaling, scroll offsets etc.
00343                 This function should be used to create all RenderRegions - it chooses the
00344                 best RenderRegion subclass to use depending on the host environment.
00345                 (e.g. on NT it chooses an OSRenderRegion that can use Win32 GDI calls).
00346 
00347 ********************************************************************************************/
00348 
00349 RenderRegion* OSRenderRegion::Create(DocRect ClipRect, Matrix ConvertMatrix, 
00350                                      FIXED16 ViewScale, RenderType rType,
00351                                      View* pView, BOOL UseOSRendering, BOOL bForce32BPP)
00352 {
00353 //  const bool bCanDo32 = true;
00354 /* GAT
00355     BOOL bCanDo32 = FALSE;
00356 
00357 #if WIN32
00358     if ( IsWin32NT() || IsWin32c() )
00359     {
00360         // Running on 32-bit GDI - use special NT-only Win32 GDI functions for those devices that
00361         // make sense. Currently only 16-bit Metafiles are not done using 32-bit GDI
00362         // so that paths are manually flattened.
00363         if (rType != RENDERTYPE_METAFILE16)
00364             bCanDo32 = TRUE;
00365     }
00366 #endif
00367 */
00368     OSRenderRegion *pOS = NULL;
00369 
00370     if (rType == RENDERTYPE_MONOBITMAP || rType == RENDERTYPE_COLOURBITMAP || rType == RENDERTYPE_HITDETECT)
00371     {
00372         RenderRegion *pRender = OSRenderBitmap::Create(ClipRect, ConvertMatrix, ViewScale, rType);
00373         
00374         if (pRender)
00375         {
00376             // if its not a OSRenderRegion, then return it directly, otherwise fall through
00377             // to set the flags on it
00378             if (!pRender->IsKindOf( CC_RUNTIME_CLASS(OSRenderRegion) ))
00379                 return pRender;
00380 
00381             pOS = (OSRenderRegion*) pRender;
00382         }
00383     }
00384     else if (rType == RENDERTYPE_SCREENPAPER)
00385     {
00386         // Special render region for paper rendering.
00387 //      TRACE(_T("Creating SCREENPAPER region (%d, %d) - (%d, %d)\n"), ClipRect.lo.x, ClipRect.lo.y, ClipRect.hi.x, ClipRect.hi.y);
00388         pOS = new PaperRenderRegion(ClipRect, ConvertMatrix, ViewScale);
00389     }
00390     else if ((rType == RENDERTYPE_PRINTER) || (rType == RENDERTYPE_PRINTER_PS))
00391     {
00392 #ifndef STANDALONE
00393         // Work out what kind of printer region to get - find document's print control info.
00394         Document *pDoc = pView->GetDoc();
00395         ERROR3IF(pDoc == NULL, "No document attached to view when printing!");
00396         if (pDoc != NULL)
00397         {
00398             // Get print information for this document.
00399             PrintComponent *pPrint = 
00400                 (PrintComponent *) pDoc->GetDocComponent(CC_RUNTIME_CLASS(PrintComponent));
00401             ERROR2IF(pPrint == NULL, NULL, "Unable to find PrintComponent in document.");
00402 
00403             PrintControl *pPrintControl = pPrint->GetPrintControl();
00404             ERROR2IF(pPrintControl == NULL, NULL,
00405                      "Unable to find PrintControl object in document component.");
00406 
00407             PrintMethodType PrintType = pPrintControl->GetPrintMethod();
00408 
00409             // Work out whether or not to use straight Gavin bitmap for printing
00410             if ((PrintType == PRINTMETHOD_AABITMAP) ||
00411                 (PrintType == PRINTMETHOD_BITMAP))
00412             {
00413                 // GDraw - use GRenderPrint region.
00414                 return GRenderRegion::Create(ClipRect, ConvertMatrix, ViewScale, 
00415                                              rType, pView);
00416             }
00417         }
00418 
00419         // Is this a PostScript printer?
00420         if (rType == RENDERTYPE_PRINTER_PS)
00421         {
00422             // Yes - make a PostScript render region
00423             return new PrintPSRenderRegion(ClipRect, ConvertMatrix, ViewScale);
00424         }
00425 #endif
00426         // If we get here, then we should use normal OS rendering.
00427 //      TRACE(_T("Creating PRINTER region (%d, %d) - (%d, %d)  OSRenderRegion\n"), ClipRect.lo.x, ClipRect.lo.y, ClipRect.hi.x, ClipRect.hi.y);
00428         return new OSRenderRegion(ClipRect, ConvertMatrix, ViewScale);
00429     }
00430     else
00431     {
00432         // Always try for a GRenderRegion first - unless the caller specifically asked for
00433         // the OSRenderRegion, the whole OSRenderRegion, and nothing but the OSRenderRegion.
00434         // (But by default, we'll give 'em a GRenderRegion if we can)
00435         if ((!UseOSRendering) && (rType != RENDERTYPE_PRINTER))
00436         {
00437             RenderRegion *pRender;
00438             pRender = GRenderRegion::Create(ClipRect, ConvertMatrix, ViewScale, rType, pView, bForce32BPP);
00439             if (pRender)
00440                 return pRender;
00441         }
00442 
00443         // if failed, use normal ones
00444 //      TRACE(_T("Creating OTHER region (%d, %d) - (%d, %d)  OSRenderRegion\n"), ClipRect.lo.x, ClipRect.lo.y, ClipRect.hi.x, ClipRect.hi.y);
00445         pOS = new OSRenderRegion(ClipRect, ConvertMatrix, ViewScale);
00446     }
00447 
00448     if (!pOS)
00449         return NULL;                                // if call failed
00450 
00451     // created a new one OK - must set RFlags suitably
00452 
00453     if (
00454         (rType == RENDERTYPE_METAFILE16) ||
00455         (rType == RENDERTYPE_METAFILE32)
00456        )
00457         pOS -> RFlags.Metafile = TRUE;              // mark metafiles correctly
00458                                                     // (this fn is a friend so can get to it)
00459 
00460     if (rType == RENDERTYPE_MONOBITMAP)
00461     {
00462         pOS -> RenderFlags.VeryMono = TRUE;
00463     }
00464 
00465     if (rType == RENDERTYPE_HITDETECT)
00466     {
00467         pOS -> RenderFlags.VeryMono = FALSE;
00468         pOS -> RenderFlags.HitDetect = TRUE;
00469         pOS -> m_DoCompression = TRUE;
00470     }
00471 
00472     if (rType == RENDERTYPE_COLOURBITMAP)
00473     {
00474         pOS -> RenderFlags.VeryMono = FALSE;
00475         pOS -> m_DoCompression = TRUE;
00476     }
00477 
00478     if (
00479         (rType == RENDERTYPE_SCREEN) ||
00480         (rType == RENDERTYPE_SCREENPAPER) ||
00481         (rType == RENDERTYPE_SCREENXOR)
00482        )
00483         {
00484             // if going to the screen, might have a palette selected
00485             if (WantGDIPalette && PaletteManager::UsePalette())
00486                 pOS->RFlags.UsePalette = TRUE;
00487         }
00488 
00489 //  pOS->RFlags.GDI32 = bCanDo32;
00490 
00491     return pOS;
00492 }
00493 
00494 
00495 
00496 /********************************************************************************************
00497 
00498 >   OSRenderRegion::OSRenderRegion(DocRect ClipRect, Matrix ConvertMatrix, FIXED16 ViewScale)
00499 
00500     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
00501     Created:    11/5/93
00502     Inputs:     ClipRect is a DocRect defining the invalid rectangle to be rendered.
00503                 ConvertMatrix is a Matrix for converting Doc coords to OS coords.
00504                 ViewScale is the scale factor of the view, used to calculate how much to
00505                 flatten paths etc.
00506     Purpose:    Constructor for OSRenderRegion, allowing a invalid rectangle to be specified.
00507                 The matrix passed is used to convert Document coords into device coords,
00508                 taking into account scaling, scroll offsets etc. All RFlags values default
00509                 to FALSE. The creator should set them.
00510 
00511 ********************************************************************************************/
00512 
00513 OSRenderRegion::OSRenderRegion(DocRect ClipRect, Matrix ConvertMatrix, FIXED16 ViewScale)
00514     : RenderRegion(ClipRect, ConvertMatrix, ViewScale)
00515 {
00516     CurrentPen = 0;
00517     CurrentBrush = 0;
00518 //  OldBrush = NULL;
00519 //  OldPen = NULL;
00520     OldFont = NULL;
00521     OSClipRegion = NULL;
00522     OldPalette = NULL;
00523     FontInfo.pRenderFont = NULL;
00524     FontInfo.pOldFont = NULL;
00525 
00526     RFlags.Metafile   = FALSE;
00527 //  RFlags.GDI32      = FALSE;
00528     RFlags.ValidPen   = FALSE;
00529     RFlags.ValidBrush = FALSE;
00530     RFlags.UsePalette = FALSE;
00531 
00532     // We don't bother rendering paper for OSRenderRegions cos it's already been done by the
00533     // PaperRenderRegion
00534     IsPaperRendered = TRUE;
00535 
00536     // Set the render caps up
00537     GetRenderRegionCaps(&Caps);
00538 
00539     // Initialise the SepTables pointer
00540     SepTables = NULL;
00541 }
00542 
00543 /********************************************************************************************
00544 
00545 >   OSRenderRegion::OSRenderRegion(const OSRenderRegion &other)
00546 
00547     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
00548     Created:    15/7/93
00549     Inputs:     An OSRenderRegion to copy
00550     Purpose:    Copy Constructor for OSRenderRegion.  Constructs an OSRenderRegion that is a
00551                 copy of another. Calls RenderRegion copy constructor to make a copy of the
00552                 current ContextStack.
00553     SeeAlso:    RenderRegion(const RenderRegion &other)
00554 
00555 ********************************************************************************************/
00556 
00557 OSRenderRegion::OSRenderRegion(const OSRenderRegion &other)
00558      : RenderRegion(other)
00559 {
00560     // We assume that the RenderRegion being copied is not curently rendering.
00561     CurrentPen = 0;
00562     CurrentBrush = 0;
00563 //  OldBrush = NULL;
00564 //  OldPen = NULL;
00565     OldFont = NULL;
00566     OSClipRegion = NULL;
00567     OldPalette = NULL;
00568     FontInfo.pRenderFont = NULL;
00569     FontInfo.pOldFont = NULL;
00570 
00571     RFlags = other.RFlags;
00572 
00573     // Set the render caps up
00574     GetRenderRegionCaps(&Caps);
00575 
00576     // Initialise the SepTables pointer
00577     SepTables = NULL;
00578 }
00579 
00580 /********************************************************************************************
00581 
00582 >   OSRenderRegion::~OSRenderRegion()
00583 
00584     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
00585     Created:    15/5/93
00586     Purpose:    Default Destructor for OSRenderRegion Class
00587 
00588 ********************************************************************************************/
00589 
00590 OSRenderRegion::~OSRenderRegion()
00591 {
00592     if (RenderFlags.Rendering)
00593     {
00594         TRACE( _T("StopRender() was not called before destructor\n") );
00595         StopRender();
00596     }
00597 
00598     // A bug used to cause OSClipRegion to be left undeleted when a clipping rect was set
00599     // externally.  This check is here just to make sure it doesn't creep back !!
00600     ENSURE(OSClipRegion == NULL, "Clip Region wasn't deleted");
00601 
00602     if (SepTables != NULL)
00603     {
00604         CCFree(SepTables);
00605         SepTables = NULL;
00606     }
00607 }
00608 
00609 
00610 
00611 /********************************************************************************************
00612 
00613 >   BOOL OSRenderRegion::AttachDevice(View* ViewToAttach, CDC* DCToAttach, Spread* SpreadToAttach)
00614 
00615     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00616     Created:    27/05/94
00617     Inputs:     A View ptr ,CDC ptr and Spread ptr to attach to the RenderRegion
00618     Returns:    TRUE if the device is attached successfully;
00619                 FALSE if not.
00620     Purpose:    Attach an OSRenderRegion to a particular device context.  This merely
00621                 remembers the information - the real work is done by 
00622                 [OS]RenderRegion::InitDevice().
00623     Errors:     Same as base class.
00624     SeeAlso:    OSRenderRegion::InitDevice; RenderRegion::InitDevice
00625 
00626 ********************************************************************************************/
00627 
00628 BOOL OSRenderRegion::AttachDevice(View* ViewToAttach, wxDC* DCToAttach, Spread* SpreadToAttach, bool fOwned /*= false*/)
00629 {
00630     // Call the base class first
00631     if (!RenderRegion::AttachDevice( ViewToAttach, DCToAttach, SpreadToAttach, fOwned ))
00632         return FALSE;
00633     
00634     ENSURE(RenderDC != NULL,"Attempted to attach Invalid DC to RenderRegion");
00635     // If it is a PrinterDC then set our flag to say we are Printing
00636     RenderFlags.Printing = CCDC::ConvertFromNativeDC(RenderDC)->IsPrinting();
00637 
00638     // All ok
00639     return TRUE;
00640 }
00641 
00642 /********************************************************************************************
00643 
00644 >   BOOL OSRenderRegion::InitDevice()
00645 
00646     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00647     Created:    27/05/94
00648     Returns:    TRUE if the device context is initialised ok;
00649                 FALSE if not.
00650     Purpose:    Initialise the device specific mechanisms for this render region.
00651     Errors:     Same as base class.
00652     SeeAlso:    RenderRegion::InitDevice
00653 
00654 ********************************************************************************************/
00655 
00656 BOOL OSRenderRegion::InitDevice()
00657 {
00658     // Call base class
00659     if (!RenderRegion::InitDevice())
00660         return FALSE;
00661 
00662     // Get the HDC so we can reconstruct the CDC later.
00663 //  DCHandle = RenderDC->GetSafeHdc();
00664 
00665     // Ignore any error from GBrush initialisation - we can live without GBrush.
00666     // However, we only want GBrush on the screen thanks
00667     if (IsPrinting())
00668     {
00669         // if we don't do this, bitmaps printed at the same DPI as the printer come
00670         // out wrong on NT drivers
00671         // Note: although this is claimed to be a Win32s-compatible function,
00672         // GDI16 generates an error for it (invalid value 4)
00673         // Note2: The MFC version of this function looks like the 16-bit API call,
00674         // so we call the API directly here
00675         //::SetStretchBltMode( RenderDC->m_hDC, HALFTONE );
00676     }
00677     else
00678     {
00679         GDrawBrush.Init( RenderDC );
00680     }
00681 
00682     // Find out what grad fill quality to use.
00683     PrintControl *pPrintControl = RenderView->GetPrintControl();
00684     PrintFillQuality FillQuality;
00685 
00686     if (pPrintControl != NULL)
00687         FillQuality = pPrintControl->GetFillQuality();
00688     else
00689         FillQuality = PRINTFILLQUALITY_MEDIUM;
00690 
00691     switch (FillQuality)
00692     {
00693         case PRINTFILLQUALITY_LOW:
00694             GradFillQuality = 2;
00695             break;
00696 
00697         case PRINTFILLQUALITY_HIGH:
00698             GradFillQuality = 0;
00699             break;
00700 
00701         case PRINTFILLQUALITY_MEDIUM:
00702         default:
00703             GradFillQuality = 1;
00704             break;
00705     }
00706 
00707 
00708     // All ok
00709     return TRUE;
00710 }
00711 
00712 
00713 
00714 /********************************************************************************************
00715 
00716 >   void OSRenderRegion::InitClipping()
00717 
00718     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
00719     Created:    02/6/93
00720     Purpose:    Creates a Windows CRgn object from the DocRect passed in the constructor.
00721                 The CRgn is set as the current clipping region during reendering.
00722     Scope:      Private
00723 
00724 ********************************************************************************************/
00725 
00726 void OSRenderRegion::InitClipping()
00727 {
00728 
00729     // OLD COMMENT and CODE:
00730     // Windows rects are slightly differnt to DocView rects so we expand them by 1 pixel
00731     //WinRect OSClipRect = DocRectToWin(CurrentClipRect, 0,0,1,1);
00732 
00733     // now the seemingly spurious 1,1s have been removed as they are indeed spurious
00734     WinRect OSClipRect = DocRectToWin(CurrentClipRect, 0,0,0,0);
00735 
00736 //  ENSURE(OSClipRect.top >= 0, "Blobby!");
00737 
00738 //  This line commented out by Alex, to fix Bugzilla #991, per Neil, Gavin
00739 //  ERROR3IF((OSClipRect.x < 0) || (OSClipRect.y < 0), "OSRenderRegion::InitClipping bad clip");
00740 
00741     // we can't clip within a metafile, but we can set the origin & extent based on it
00742 //  if (RFlags.Metafile)
00743 //  {
00744 //      RenderDC->SetWindowOrg( OSClipRect.x, OSClipRect.y );
00745 //      RenderDC->SetWindowExt( OSClipRect.width, OSClipRect.height );
00746 //  }
00747 //  else
00748     {
00749         delete OSClipRegion;    // This will be NULL if no previous CRgn defined.
00750         OSClipRegion = new wxRegion(OSClipRect);
00751         ENSURE(OSClipRegion != NULL,"'new' failed when creating OSClipRegion");
00752 
00753         // Set the Clipping Region
00754         if (OSClipRegion)
00755             RenderDC->SetClippingRegion(*OSClipRegion);
00756     }
00757 }
00758 
00759 /********************************************************************************************
00760 
00761 >   void OSRenderRegion::DeInitClipping()
00762 
00763     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00764     Created:    21/01/94
00765     Purpose:    Destroys the GDI clipping region object, and unsets the clipping region for
00766                 our DC.
00767 
00768 ********************************************************************************************/
00769 
00770 void OSRenderRegion::DeInitClipping()
00771 {
00772     delete OSClipRegion;            // Delete the CRgn
00773     OSClipRegion = NULL;
00774 
00775     // unset the Clipping Region
00776     if (!RFlags.Metafile)
00777         RenderDC->DestroyClippingRegion();
00778 }
00779 
00780 /********************************************************************************************
00781 
00782 >   void OSRenderRegion::InitAttributes()
00783 
00784     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
00785     Created:    31/5/93
00786     Purpose:    Sets up a default GDI pen and brush for rendering, and calculates the Brush
00787                 origin for aligned fill patterns.
00788     Scope:      Private
00789 
00790 ********************************************************************************************/
00791 
00792 void OSRenderRegion::InitAttributes()
00793 {
00794     CurrentPen = 0;
00795     CurrentBrush = 0;
00796 //  OldBrush = NULL;
00797 //  OldPen = NULL;
00798     OldFont = NULL;
00799 
00800     if (!RFlags.Metafile)
00801     {
00802         // Set the default font (intended for kernel-rendered dialogues)
00803         // Don't bother for metafiles (because we can't read text from them anyhow)
00804     PORTNOTE("other","OSRenderRegion::InitAttributes - removed setting of default font");
00805 #if !defined(EXCLUDE_FROM_XARALX)
00806         wxFont* FixedSystemFont = FontFactory::GetCFont(STOCKFONT_RNDRGNFIXEDTEXT);
00807         if (FixedSystemFont != NULL)
00808             OldFont = RenderDC->SetFont(*FixedSystemFont);
00809 #endif
00810     }
00811 
00812     SetOSDrawingMode();
00813     SetLineAttributes();    // Create a default Pen
00814     SetFillAttributes();    // Create a default Brush
00815 
00816     if (!RFlags.Metafile)
00817     {
00818         // Metafiles don't need brush origin calculations
00819 
00820         // Now we calculate the Origin for the Brush fill pattern, so that fill patterns
00821         // are always aligned as we scroll around the page.
00822     
00823         // (First we get the Origin of the Document in DocCoords)
00824         // Actually, at Jim's suggestion Andy now bases this on (0,0).
00825         DocCoord DocOrigin;
00826         DocOrigin.x = 0;                    //was MinDocCoord+0x20000;
00827         DocOrigin.y = 0;                    //was MaxDocCoord-0x20000;
00828         
00829 //      f1 = f;
00830         // Then we transform it into OSCoords
00831         WinCoord OSOrigin;
00832         OSOrigin = DocCoordToWin(DocOrigin);
00833     
00834         // when running on some machine combinations, we need to further adjust the point
00835         // relative to the screen. The only combination that works is a 32-bit version
00836         // running on NT. Everyone else has to fix it up.
00837 PORTNOTE("other", "Check this");
00838 //      if (IsWin32s())
00839         {
00840             if (RenderView)
00841             {
00842                 // Get the OIL window and get it to do the conversion for us.
00843                 CCamView* RenderWindow = RenderView->GetConnectionToOilView();
00844                 if ( RenderWindow!=NULL && RenderWindow->GetFrame()!=NULL )
00845                     RenderWindow->GetFrame()->ClientToScreen(OSOrigin);
00846                 // In galleries, RenderView used to be NULL, so this code was never called.
00847                 // This has changed, so suddenly this ensure was going off. Now it won't.
00848                 //else
00849                 //  ERROR2RAW("No render window in OSRenderRegion::InitAttributes()");
00850             }
00851         }
00852     
00853         // The Brush Origin is set to a value between 0 and 7 calculated from the position of the
00854         // transformed origin.
00855         NewBrushOrg.x = OSOrigin.x & 7;
00856         NewBrushOrg.y = OSOrigin.y & 7;
00857     } 
00858 
00859 }
00860 
00861 /********************************************************************************************
00862 
00863 >   void OSRenderRegion::DeInitAttributes()
00864 
00865     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00866     Created:    21/01/94
00867     Purpose:    Deletes the GDI pen and brush objects.
00868 
00869 ********************************************************************************************/
00870 
00871 void OSRenderRegion::DeInitAttributes()
00872 {
00873     // Get rid of pen and/or brush if we used them.
00874 /*  if (OldPen != NULL)
00875     {
00876         //ENSURE(OldPen->m_hObject != NULL, "NULL object selected!");
00877         RenderDC->SetPen(*OldPen);
00878     }
00879 
00880     if (OldBrush != NULL)
00881     {
00882         //ENSURE(OldBrush->m_hObject != NULL, "NULL object selected!");
00883         RenderDC->SetBrush(*OldBrush);
00884     }
00885 */
00886 
00887     // Deselect the FixedSystem font from the DC
00888     if (OldFont != NULL)
00889     {
00890         RenderDC->SetFont(*OldFont);
00891         OldFont = NULL;
00892     }
00893 
00894     // Deselect the normal rendering font from the DC
00895     if (FontInfo.pRenderFont != NULL)
00896     {
00897         RenderDC->SetFont(*FontInfo.pOldFont);
00898         FontInfo.pOldFont = NULL;
00899         delete FontInfo.pRenderFont;
00900         FontInfo.pRenderFont = NULL;
00901     }
00902 }
00903 
00904 
00905 /********************************************************************************************
00906 
00907 >   void OSRenderRegion::SetLineAttributes(ChangeLineAttrType Type = CHANGELINEATTR_ALL)
00908 
00909     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
00910     Created:    31/5/93
00911     Purpose:    When a line attribute is changed, this functions is called to create a new
00912                 pen to mirror the new attribute value.
00913     Scope:      Private
00914 
00915 ********************************************************************************************/
00916 
00917 void OSRenderRegion::SetLineAttributes(ChangeLineAttrType Type)
00918 {
00919     RFlags.ValidPen = FALSE;
00920 }
00921 
00922 /********************************************************************************************
00923 
00924 >    void OSRenderRegion::SetFillAttributes(ChangeAttrType Type = CHANGEATTR_ALL)
00925 
00926     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
00927     Created:    31/5/93
00928     Inputs:     Ignored for OSRenderRegion.
00929     Purpose:    When a fill attribute is changed, this functions is called to create a new
00930                 brush to mirror the new attribute value.
00931     Scope:      Private
00932 
00933 ********************************************************************************************/
00934 
00935 void OSRenderRegion::SetFillAttributes(ChangeAttrType)
00936 {
00937     RFlags.ValidBrush = FALSE;
00938 }
00939 
00940 /********************************************************************************************
00941 
00942 >   void OSRenderRegion::SetQualityLevel()
00943 
00944     Author:     Andy_Pennell (Xara Group Ltd) <camelotdev@xara.com>
00945     Created:    24/4/93
00946     Purpose:    Called when Quality level changes. We do nothing currently.
00947 
00948 ********************************************************************************************/
00949 
00950 void OSRenderRegion::SetQualityLevel()
00951 {
00952 }
00953 
00954 /********************************************************************************************
00955 
00956 >   COLORREF OSRenderRegion::CalcEORColour( DocColour &Wanted, COLORREF Background = WHITE)
00957 
00958     Author:     Andy_Pennell (Xara Group Ltd) <camelotdev@xara.com>
00959     Created:    19/11/93
00960     Inputs:     Wanted is the colour that is finally required. Background is the colour you
00961                 are assumed to be drawing onto.
00962     Returns:    The RGB value such that value XOR Background = Wanted (approx).
00963     Purpose:    To calculate correct colours when draing in EOR mode. Intended to be called
00964                 by OSRenderRegion code only (normally SetxxxColur)
00965     Errors:     If cannot calculate for some reason, returns Background, which works if Wanted
00966                 is black.
00967     Scope:      Protected
00968 
00969 ********************************************************************************************/
00970 
00971 wxColour OSRenderRegion::CalcEORColour( DocColour &Wanted, COLORREF Background )
00972 {
00973     // This routine caches the last EOR colour it calculated so that we don't create
00974     // a bitmap every time we e.g. draw a selection blob.
00975     INT32 R,G,B;
00976     Wanted.GetRGBValue(&R,&G,&B);
00977     return wxColour(
00978                 GetRValue(Background)^R,
00979                 GetGValue(Background)^G,
00980                 GetBValue(Background)^B
00981             );
00982 /*
00983 
00984     if (EORCache.Valid && (EORCache.SourceColour == Wanted))
00985         return(EORCache.EORColour);
00986 
00987     COLORREF Result;
00988     INT32 Red, Green, Blue;
00989     Wanted.GetRGBValue(&Red, &Green, &Blue);
00990 
00991     if (RFlags.UsePalette)
00992     {
00993         // a palette device has to be quite different - get palette indices and XOR
00994         // them together, then return that as a palette index
00995         const UINT32 BGIndex = PaletteManager::GetPalette()->GetNearestPaletteIndex( Background );
00996         const UINT32 FGIndex = PaletteManager::GetPalette()->GetNearestPaletteIndex( RGB(Red,Green,Blue) );
00997 
00998         Result = PALETTEINDEX( BGIndex ^ FGIndex );
00999     }
01000     else
01001     {
01002         // non-palette devices we XOR into a little bitmap to work it out
01003         CBitmap Tiny, *OldBitmap;
01004         CDC MemDC;
01005 
01006         BOOL bStatus;
01007 
01008         bStatus = MemDC.CreateCompatibleDC( RenderDC );
01009         ENSURE(bStatus, "cant create comp DC");
01010 
01011         bStatus = Tiny.CreateCompatibleBitmap( RenderDC, 1, 1 );        // make a tiny bitmap
01012                                                                         // compatible with screen
01013                                                                         // so colours OK
01014         ENSURE(bStatus, "cant create Tiny bitmap");
01015 
01016         OldBitmap = MemDC.SelectObject( &Tiny );
01017         if (OldBitmap == NULL)
01018         {
01019             ENSURE(FALSE, "Cant select tiny bitmap");
01020             return RGB(255,255,255);                                    // return WHITE
01021         }
01022 
01023         MemDC.SetPixel( 0,0, Background );
01024         MemDC.SetROP2( R2_XORPEN );                                     // into XOR mode
01025 
01026         MemDC.SetPixel( 0,0, RGB(Red, Green, Blue) );
01027 
01028 
01029         Result = MemDC.GetPixel( 0,0 );
01030         ENSURE( Result!=0xFFFFFFFF, "GetPixel failed");
01031 
01032         //TRACE( _T("Converted %d.%d.%d to %d.%d.%d\n"), Wanted.Red, Wanted.Green, Wanted.Blue,
01033         //                                          GetRValue(Result), GetGValue(Result), GetBValue(Result) );
01034 
01035         MemDC.SelectObject( OldBitmap );                                // restore normality
01036 
01037         // automatic destruction of HDC and Bitmaps will occur naturally
01038     }
01039 
01040 
01041     // Cache this result
01042     EORCache.SourceColour = Wanted;
01043     EORCache.EORColour = Result;
01044     EORCache.Valid = TRUE;
01045 
01046     return Result;
01047 */
01048 }
01049 
01050 
01051 
01052 /********************************************************************************************
01053 
01054 >   static void OSRenderRegion::FlushEORCache(void)
01055 
01056     Author:     Gerry_Iles (Xara Group Ltd) <camelotdev@xara.com>
01057     Created:    29/5/95
01058     Inputs:     -
01059     Returns:    -
01060     Purpose:    Flushes the EOR colour cache.  Currently called when screen mode changes
01061     Scope:      Public
01062 
01063 ********************************************************************************************/
01064 
01065 void OSRenderRegion::FlushEORCache(void)
01066 {
01067     EORCache.Valid = FALSE;
01068 }
01069 
01070 
01071 
01072 /********************************************************************************************
01073 
01074 >   void OSRenderRegion::CalcLogBrush(wxBrush* lpBrush, DocColour &Col)
01075 
01076     Author:     Andy_Pennell (Xara Group Ltd) <camelotdev@xara.com>
01077     Created:    13/4/94
01078     Inputs:     lpBrush will be filled with a LOGBRUSH.
01079                 Col is used to calculate said brush.
01080     Returns:    -
01081     Purpose:    To calculate a brush object given a Colour. Normally used for
01082                 brushes to fill things.
01083     Errors:     -
01084     Scope:      Protected
01085 
01086 ********************************************************************************************/
01087 
01088 void OSRenderRegion::CalcLogBrush(wxBrush* lpBrush, DocColour &Col)
01089 {
01090     if (Col.IsTransparent())
01091     {
01092         lpBrush->SetColour(*wxBLACK);
01093         lpBrush->SetStyle(wxTRANSPARENT);
01094     }
01095     else
01096     {
01097         // VeryMono devices have everything that is not transparent as Black
01098         if (RenderFlags.VeryMono)
01099             // Unless the quality says don't fill!!!
01100             if (RRQuality.GetFillQuality() <= Quality::NoFill)
01101             {
01102                 lpBrush->SetColour(*wxBLACK);
01103                 lpBrush->SetStyle(wxTRANSPARENT);
01104             }
01105             else
01106             {
01107                 lpBrush->SetStyle(wxSOLID);
01108                 lpBrush->SetColour(*wxBLACK);
01109             }
01110         else if (DrawingMode != DM_EORPEN)
01111         {
01112             if (GDrawBrush.Available())
01113                 GDrawBrush.GetLogBrush( CurrentColContext, Col, lpBrush );
01114             else
01115             {
01116                 INT32 R,G,B;
01117                 Col.GetRGBValue(&R, &G, &B);
01118                 lpBrush->SetStyle(wxSOLID);
01119                 lpBrush->SetColour(R, G, B);
01120             }
01121         }
01122         else
01123         {
01124             lpBrush->SetStyle(wxSOLID);
01125             lpBrush->SetColour(CalcEORColour(Col));
01126         }
01127     }
01128 }
01129 
01130 /********************************************************************************************
01131 
01132 >   void OSRenderRegion::CreateNewPen()
01133 
01134     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
01135     Created:    31/5/93
01136     Purpose:    Creates a new Pen, based on the current line attributes.
01137     Scope:      Private
01138 
01139 ********************************************************************************************/
01140 
01141 void OSRenderRegion::CreateNewPen()
01142 {
01143 
01144     BOOL EmptyPen = RR_STROKECOLOUR().IsTransparent();
01145     const enum Quality::Line LineQuality = RRQuality.GetLineQuality();
01146 
01147     // if no filling going on then must have a pen
01148     if ( RRQuality.GetFillQuality() <= Quality::NoFill )
01149         EmptyPen = FALSE;
01150 
01151     if (
01152 //      RFlags.GDI32 &&                             // must be GDI32
01153         !RenderFlags.VeryMono &&                    // not to bitmaps (special colour rules)
01154         !RenderFlags.bImmediateRender &&            // not when immediate (XORing messes up)
01155         (LineQuality >= Quality::FullLine) &&       // not on simple lines
01156         !EmptyPen                                   // not on NULL pens (over complex)
01157        )
01158     {
01159 //      Windows version would have used brush calculation to set pen properties
01160 //      CalcLogBrush( &Brush, RR_STROKECOLOUR() );
01161 
01162         INT32 PenWidth = MPtoLP(RR_LINEWIDTH());
01163         if (IsPrinting())
01164         {
01165             // Make sure we don't user single pixel width pens as they won't dither.
01166             if (PenWidth < 2)
01167                 PenWidth = 2;
01168         }
01169 
01170         INT32 R,G,B;
01171         RR_STROKECOLOUR().GetRGBValue(&R,&G,&B);
01172         RenderPen[CurrentPen] = wxPen(wxColour(R,G,B), PenWidth, wxSOLID);
01173 
01174         // work out pen type
01175         switch (RR_STARTCAP())
01176         {
01177             case LineCapRound:
01178                 RenderPen[CurrentPen].SetCap(wxCAP_ROUND);
01179                 break;
01180 
01181             case LineCapSquare:
01182                 RenderPen[CurrentPen].SetCap(wxCAP_PROJECTING);
01183                 break;
01184 
01185             case LineCapButt:
01186                 RenderPen[CurrentPen].SetCap(wxCAP_BUTT);
01187                 break;
01188 
01189             default:
01190                 ENSURE(FALSE, "Bad startcap");
01191                 break;
01192         }
01193         switch (RR_JOINTYPE())
01194         {
01195             case BevelledJoin:
01196                 RenderPen[CurrentPen].SetJoin(wxJOIN_BEVEL);
01197                 break;
01198 
01199             case MitreJoin:
01200                 RenderPen[CurrentPen].SetJoin(wxJOIN_MITER);
01201                 break;
01202 
01203             case RoundJoin:
01204                 RenderPen[CurrentPen].SetJoin(wxJOIN_ROUND);
01205                 break;
01206 
01207             default:
01208                 ENSURE(FALSE, "bad jointype");
01209                 break;
01210         }
01211         return ;
01212     }
01213 
01214     // fall through to use old-fashioned 16-bit GDI pen creation if failed
01215 
01216     if (EmptyPen)
01217         RenderPen[CurrentPen] = wxPen(*wxTRANSPARENT_PEN);
01218     else
01219     {
01220         INT32 PenWidth = MPtoLP(RR_LINEWIDTH());
01221 
01222         if (RR_LINEWIDTH()==0 || LineQuality < Quality::FullLine)
01223             PenWidth = 0;
01224         else
01225         {
01226             MILLIPOINT LineWidth = RR_LINEWIDTH();
01227             if ((RenderFlags.VeryMono || RenderFlags.HitDetect) && LineWidth < GetScaledPixelWidth())
01228                 PenWidth = MPtoLP(GetScaledPixelWidth());
01229         }
01230 
01231         wxColour Colour(0,0,0);
01232         if (RenderFlags.VeryMono || LineQuality < Quality::ThinLine)
01233             Colour = *wxBLACK;
01234         else if (DrawingMode==DM_EORPEN)
01235             Colour = CalcEORColour(RR_STROKECOLOUR());
01236         else
01237         {
01238             INT32 R,G,B;
01239             RR_STROKECOLOUR().GetRGBValue(&R,&G,&B);
01240             Colour = wxColour(R, G, B);
01241         }
01242 
01243         RenderPen[CurrentPen] = wxPen(Colour, PenWidth, wxSOLID);
01244     }
01245 }
01246 
01247 void OSRenderRegion::SelectNewPen()
01248 {
01249     // Swap pens
01250     CurrentPen = 1 - CurrentPen;
01251 
01252     // Create and select the new pen
01253     CreateNewPen();
01254 
01255     RenderDC->SetPen(RenderPen[CurrentPen]);
01256 
01257     RFlags.ValidPen = TRUE;
01258 }
01259 
01260 
01261 /********************************************************************************************
01262 
01263 >   void OSRenderRegion::CreateNewBrush()
01264 
01265     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
01266     Created:    31/5/93
01267     Purpose:    Creates a new Brush, based on the current fill attributes.
01268     Scope:      Private
01269 
01270 ********************************************************************************************/
01271 
01272 void OSRenderRegion::CreateNewBrush()
01273 {
01274     if (RRQuality.GetFillQuality() <= Quality::NoFill )
01275     {
01276         RenderBrush[CurrentBrush].SetColour(*wxBLACK);
01277         RenderBrush[CurrentBrush].SetStyle(wxTRANSPARENT);
01278     }
01279     else
01280         CalcLogBrush( &RenderBrush[CurrentBrush], RR_FILLCOLOUR() );
01281 }
01282 
01283 /********************************************************************************************
01284 
01285 >   void OSRenderRegion::SelectNewBrush()
01286 
01287     Author:     Andy_Pennell (Xara Group Ltd) <camelotdev@xara.com>
01288     Created:    15/10/93
01289     Purpose:    Selects RenderBrush while setting its new origin. A common operation that is in its
01290                 own function to cope with things such as metafiles which cannot do this.
01291     Scope:      Private
01292 
01293 ********************************************************************************************/
01294 
01295 void OSRenderRegion::SelectNewBrush()
01296 {
01297     // Swap brushes
01298     CurrentBrush = 1 - CurrentBrush;
01299 
01300     // Make the new brush
01301     CreateNewBrush();
01302 
01303     // Set up the brush origin
01304     if (!RFlags.Metafile)
01305     {
01306 //      RenderBrush[CurrentBrush]->UnrealizeObject();
01307         // this fn will assert if done on a metafile
01308     PORTNOTE("other","OSRenderRegion::SelectNewBrush - removed setting of brush origin");
01309 #if !defined(EXCLUDE_FROM_XARALX)
01310         RenderDC->SetBrushOrg(NewBrushOrg);
01311 #endif
01312     }
01313 
01314     // And select the new brush
01315     RenderDC->SetBrush(RenderBrush[CurrentBrush]);
01316 
01317     // Set winding rule as required
01318     if (RR_WINDINGRULE() == EvenOddWinding)
01319         nFillStyle = wxODDEVEN_RULE;
01320     else
01321         nFillStyle = wxWINDING_RULE;
01322 
01323     // We've now got a valid brush
01324     RFlags.ValidBrush = TRUE;
01325 }
01326 
01327 
01328 void OSRenderRegion::GetValidPen()
01329 {
01330     if (!RFlags.ValidPen)
01331         SelectNewPen();
01332 }
01333 
01334 void OSRenderRegion::GetValidBrush()
01335 {
01336     if (!RFlags.ValidBrush)
01337         SelectNewBrush();
01338 }
01339 
01340 
01341 /********************************************************************************************
01342 
01343 >   BOOL OSRenderRegion::SelectNewFont(WORD Typeface, BOOL Bold, BOOL Italic, 
01344                                        MILLIPOINT Width, MILLIPOINT Height, ANGLE Rotation)
01345 
01346     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
01347     Created:    05/28/95
01348     Inputs:     Details of the font to select.
01349                 NB. Width is not the actual width; it expresses the aspect ratio required.
01350                     So, e.g. if Width == Height/2 then the aspect ratio is 50%, and so on.
01351     Returns:    TRUE if the font was selected ok;
01352                 FALSE if not.
01353     Purpose:    Selects the specified font into the DC.  Performs clever stuff to cache
01354                 the font so that subsequent requests for the same font don't cause anything
01355                 to happen.
01356 
01357 ********************************************************************************************/
01358 
01359 BOOL OSRenderRegion::SelectNewFont(WORD Typeface, BOOL Bold, BOOL Italic, 
01360                                    MILLIPOINT Width, MILLIPOINT Height, ANGLE Rotation)
01361 {
01362     PORTNOTETRACE("text","OSRenderRegion::SelectNewFont - do nothing");
01363 #ifndef EXCLUDE_FROM_XARALX
01364     // Check to see if it is cached
01365     if ((FontInfo.pRenderFont != NULL) &&
01366         (FontInfo.Typeface == Typeface) &&
01367         (FontInfo.Bold == Bold) &&
01368         (FontInfo.Italic == Italic) &&
01369         (FontInfo.Width == Width) &&
01370         (FontInfo.Height == Height) &&
01371         (FontInfo.Rotation == Rotation))
01372     {
01373         // It is the same as the currently selected/cached font
01374         TRACEUSER( "Tim", _T("Using the cached font\n"));
01375         return TRUE;
01376     }
01377 
01378     // Is it rotated, and if so, can this DC do rotated fonts?
01379     if (Rotation != FIXED16(0))
01380     {
01381         // Don't use GetDeviceCaps to ask printers about their capabilities!
01382         // It tells lies.
01383         // Even Worse - many printers claim to be unable to render rotated
01384         // text, when they are actually perfectly cpaable of doing so!
01385         //
01386         // So, to maintain compatibility with previous versions just test
01387         // a Preference here and if it's set return FALSE like 1.5 and all
01388         // previous versions did.
01389         if (PrintRotatedTextAsPaths)
01390             return FALSE;
01391     }
01392 
01393     TRACEUSER( "Tim", _T("Font cache invalid - generating font to use\n"));
01394 
01395     // Not the cached font - ok, deselect and delete the cached font.
01396     if (FontInfo.pOldFont != NULL)
01397     {
01398         RenderDC->SelectObject(FontInfo.pOldFont);
01399         FontInfo.pOldFont = NULL;
01400     }
01401 
01402     if (FontInfo.pRenderFont != NULL)
01403     {
01404         delete FontInfo.pRenderFont;
01405         FontInfo.pRenderFont = NULL;
01406     }
01407 
01408 
01409     ENUMLOGFONT *pEnumLogFont = FONTMANAGER->GetEnumLogFont(Typeface);
01410     if (pEnumLogFont == NULL)
01411         // Error
01412         return FALSE;
01413 
01414     // Create the font and select it into the DC
01415     TRY
01416     {
01417         FontInfo.pRenderFont = new wxFont;
01418     }
01419     CATCH (CMemoryException, e)
01420     {
01421         return FALSE;
01422     }
01423     END_CATCH
01424 
01425     // Work out the font weight - bold or normal?
01426     INT32 FontWeight = Bold ? FW_BOLD : FW_NORMAL;
01427 
01428 
01429     // Work out how big the font is, in logical pixels
01430     FIXED16 fxPixWidth = 0;
01431     FIXED16 fxPixHeight = 0;
01432 
01433 //  if (RFlags.Metafile)
01434 //  {
01435         // Metafile - don't care about the view scale
01436 //      RenderView->GetPixelSize(&fxPixWidth, &fxPixHeight);
01437 //  }
01438 //  else
01439 //  {
01440         RenderView->GetScaledPixelSize(&fxPixWidth, &fxPixHeight);
01441 //  }
01442 
01443     INT32 FontWidth = (INT32) (Width / fxPixWidth.MakeDouble());
01444     INT32 FontHeight = (INT32) (Height / fxPixHeight.MakeDouble());
01445 
01446     if (FontHeight == FontWidth)
01447     {
01448         // Aspect ratio is 100% - use width of 0, and the GDI font engine gives us 100%
01449         // ratio automatically
01450         FontWidth = 0;
01451     }
01452     else
01453     {
01454         // Aspect ratio is not 100% - we need to do some clever stuff to work out
01455         // how wide we want the font to be.  Because fonts are generally much taller
01456         // than they are wide, we can't just pass in FontWidth directly - we must
01457         // transform it to the correct width.
01458 
01459         // BODGETEXT: ideally, we should use a NEWTEXTMETRIC structure here, to work out
01460         //            the correct width, but this involves calling EnumLogFontFamily() or
01461         //            whatever, which is too slow.  As the text code caches the LOGFONT
01462         //            for each font, it could also cache the NEWTEXTMETRIC structure,
01463         //            which this routine could then use.
01464         //            Having said all that, this code seems to work!
01465 
01466         //            Indeed the font manager code to cache the LOGFONTS could also
01467         //            cache the NEWTEXTMETRIC structures (or the TEXTMETRIC structures
01468         //            since NEWTEXTMETRIC structures only exist for TrueType fonts.)
01469         //            But at the moment it doesn't - the whole of the font system
01470         //            needs thinking through and rewriting.
01471         //                                                                  ach - 15/08/96
01472 
01473         // Create the font - the first time is to find out how wide the characters are,
01474         // so we can scale the width correctly. (So we give a width of 0 to get the normal
01475         // width for the given height)
01476         // (We do it at 100 units high so we get decent accuracy for the aspect ratio.)
01477         if (FontInfo.pRenderFont->CreateFont(-100, 0, 0, 0, FontWeight, Italic, 
01478                                              FALSE, FALSE, DEFAULT_CHARSET, 
01479                                              OUT_TT_PRECIS, CLIP_TT_ALWAYS,
01480                                              PROOF_QUALITY, DEFAULT_PITCH | FF_DONTCARE, 
01481                                              pEnumLogFont->elfLogFont.lfFaceName) == 0)
01482         {
01483             // Unable to select the font.
01484             delete FontInfo.pRenderFont;
01485             FontInfo.pRenderFont = NULL;
01486             return FALSE;
01487         }
01488 
01489         // Used to hold information about the font
01490         TEXTMETRIC Metrics;
01491 
01492         if (TRUE) //RFlags.Metafile)
01493         {
01494             // Metafile - we need to use the screen DC to do this
01495             wxDC* pDesktopDC = CWnd::GetDesktopWindow()->GetDC();
01496 
01497             // Get the metrics
01498             FontInfo.pOldFont = pDesktopDC->SetFont(FontInfo.pRenderFont);
01499             pDesktopDC->GetTextMetrics(&Metrics);
01500 
01501             // Select old font back into screen DC
01502             pDesktopDC->SetFont(FontInfo.pOldFont);
01503             CWnd::GetDesktopWindow()->ReleaseDC(pDesktopDC);
01504         }
01505         else
01506         {
01507             // Normal rendering - use the DC as we can use 'Getxxx' functions on it
01508             // because it is not a metafile DC.
01509             FontInfo.pOldFont = RenderDC->SelectObject(FontInfo.pRenderFont);
01510 
01511             // Get the metrics
01512             RenderDC->GetTextMetrics(&Metrics);
01513 
01514             // Select old font back into screen DC
01515             RenderDC->SelectObject(FontInfo.pOldFont);
01516         }
01517 
01518         // Scale the width by the font's aspect ratio: 
01519         // ActualWidth = (RealWidth / Height) * Width
01520         FontWidth = MulDiv(Metrics.tmAveCharWidth, FontWidth, 100);
01521 
01522         // Delete the font
01523         delete FontInfo.pRenderFont;
01524 
01525         // Create the 'proper' font and select it into the DC
01526         TRY
01527         {
01528             FontInfo.pRenderFont = new CFont;
01529         }
01530         CATCH (CMemoryException, e)
01531         {
01532             return FALSE;
01533         }
01534         END_CATCH
01535     }
01536 
01537     // Work out the rotation of the font, in tenths of degrees.
01538     INT32 lfRotation;
01539     if (Rotation == FIXED16(0))
01540         lfRotation = 0;
01541     else
01542         lfRotation = (INT32) ( (Rotation.MakeDouble() * 360.0 * 10.0) / (2 * PI) );
01543 
01544     // Create the font with the correct width   
01545     if (FontInfo.pRenderFont->CreateFont(-FontHeight, FontWidth, lfRotation, 0, FontWeight, Italic, 
01546                                          FALSE, FALSE, DEFAULT_CHARSET, 
01547                                          OUT_TT_PRECIS, CLIP_TT_ALWAYS,
01548                                          PROOF_QUALITY, DEFAULT_PITCH | FF_DONTCARE, 
01549                                          pEnumLogFont->elfLogFont.lfFaceName) == 0)
01550     {
01551         // Unable to select the font - probably because device cannot rotate fonts
01552         // arbitrarily.
01553         delete FontInfo.pRenderFont;
01554         FontInfo.pRenderFont = NULL;
01555         return FALSE;
01556     }
01557 
01558     // Select the real font into the DC.
01559     FontInfo.pOldFont = RenderDC->SetFont(FontInfo.pRenderFont);
01560 
01561     // Validate the cache
01562     FontInfo.Typeface = Typeface;
01563     FontInfo.Bold     = Bold;
01564     FontInfo.Italic   = Italic;
01565     FontInfo.Width    = Width;
01566     FontInfo.Height   = Height;
01567     FontInfo.Rotation = Rotation;
01568 #endif
01569     // Font created and selected ok
01570     return TRUE;
01571 }
01572 
01573 /********************************************************************************************
01574 
01575 >   void OSRenderRegion::SetOSDrawingMode()
01576 
01577     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
01578     Created:    31/5/93
01579     Purpose:    Sets the current drawing mode.
01580     Scope:      Private
01581 
01582 ********************************************************************************************/
01583 
01584 void OSRenderRegion::SetOSDrawingMode()
01585 {
01586     // Set ROP2 code accordingly.
01587     switch (DrawingMode)
01588     {
01589         case DM_COPYPEN:
01590             if (!RenderDC->IsKindOf(CLASSINFO(wxPostScriptDC))) // ignore for PS as it is the only option and
01591                 RenderDC->SetLogicalFunction(wxCOPY);// it asserts (ridiculous but true)
01592             break;
01593         case DM_EORPEN:
01594         case DM_EORDITHER:
01595             // Same as EORPEN, except we want the brush to be set to what we ask for,
01596             // and not messed about with, so we use this enum value instead.
01597             // The ROP2 code is the same as we use for DM_EORPEN - it is CalcLogBrush()
01598             // that behaves differently with DM_EORDITHER.
01599             RenderDC->SetLogicalFunction(wxXOR);
01600             break;
01601 
01602         default:
01603             ERROR3("Bad drawing mode in OSRenderRegion::SetOSDrawingMode()");
01604     }
01605 
01606     // When we change the ROP2 code, we also have to recreate the pen and brush objects,
01607     // otherwise we don't get an awful lot happening.  This was found out by trial and
01608     // error, and not by any of Microsoft's manuals telling us this...(Tim)
01609     SetLineAttributes();
01610     SetFillAttributes();
01611 }
01612 
01613 /********************************************************************************************
01614 
01615 >   BOOL OSRenderRegion::StartRender()
01616 
01617     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
01618     Created:    11/5/93
01619     Purpose:    Initialises an OSRenderRegion for rendering.
01620     SeeAlso:    OSRenderRegion::StopRender()
01621 
01622 ********************************************************************************************/
01623 
01624 BOOL OSRenderRegion::StartRender()
01625 {
01626     // Call base class first
01627     if (!RenderRegion::StartRender())
01628         return FALSE;
01629 
01630     ENSURE(RenderDC != NULL,"StartRender Called when DC was Invalid");
01631 
01632     // Check that Initialise hasn't been called already
01633     ENSURE(RenderFlags.Rendering == FALSE, "Initialise called whilst already rendering");
01634     TRACEUSER("Gavin",_T("OSRenderRegion::StartRender - RenderFlags.Rendering = TRUE;\n"));
01635     RenderFlags.Rendering = TRUE;
01636 
01637     PixelScale = RenderView->GetPixelWidth();
01638 
01639     InitClipping();
01640     InitAttributes();
01641 
01642     if (RFlags.UsePalette)
01643         OldPalette = PaletteManager::StartPaintPalette(RenderDC);
01644 
01645     GDrawBrush.Start();
01646 
01647 #if defined(_DEBUG) && defined(__WXMSW__)
01648 // **** DEBUG BODGE - Make GDI redraw everything immediately
01649 // instead of buffering stuff, so we can see exactly when stuff goes wrong
01650 GdiSetBatchLimit(1);
01651 #endif
01652 
01653     return TRUE;
01654 }
01655 
01656 /********************************************************************************************
01657 
01658 >   BOOL OSRenderRegion::StopRender()
01659 
01660     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
01661     Created:    11/5/93
01662     Inputs:     RenderState is a Node* pointing at the current rendering node in the tree,
01663                 or NULL if all objects rendered.
01664     Purpose:    Stops the rendering of a OSRenderRegion, saving it's current renderstate so
01665                 that rendering can continue where it left off, later on.  If the RenderState
01666                 passed is NULL then the RenderRegion will be unlinked from the list and will
01667                 then delete itself.
01668     SeeAlso:    OSRenderRegion::StartRender()
01669 
01670 ********************************************************************************************/
01671 
01672 BOOL OSRenderRegion::StopRender()
01673 {
01674 // ************************************************************
01675 // Some Debug code to plot the clipping rectangle
01676 #if 0
01677     {
01678         TRACE( _T("OSRR# DebugClipRect = (%d, %d) - (%d, %d)\n"), CurrentClipRect.lo.x, CurrentClipRect.lo.y, CurrentClipRect.hi.x, CurrentClipRect.hi.y);
01679         DocRect TempRect(CurrentClipRect);
01680         TempRect.Inflate(-GetScaledPixelWidth());
01681         TRACE( _T("OSRR# Filling = (%d, %d) - (%d, %d)\n"), TempRect.lo.x, TempRect.lo.y, TempRect.hi.x, TempRect.hi.y);
01682         Path ClipPath;
01683         ClipPath.Initialise();
01684         ClipPath.CreatePathFromDocRect(&TempRect);
01685 
01686         SaveContext();
01687         SetLineColour(COLOUR_TRANS);
01688         SetFillColour(COLOUR_RED);
01689         DrawPath(&ClipPath);
01690         RestoreContext();
01691     }
01692 #endif
01693 #if 0
01694     {
01695         SaveContext();
01696         SetLineColour(COLOUR_TRANS);
01697         SetFillColour(COLOUR_BLUE);
01698 
01699         DocRect SmallRect(750, 750, 1500, 1500);
01700         Path ThePath;
01701         ThePath.Initialise();
01702         ThePath.CreatePathFromDocRect(&SmallRect);
01703 
01704         DrawPath(&ThePath);
01705         ThePath.Translate(50, 1500);
01706         DrawPath(&ThePath);
01707         ThePath.Translate(50, 1500);
01708         DrawPath(&ThePath);
01709         ThePath.Translate(50, 1500);
01710         DrawPath(&ThePath);
01711         ThePath.Translate(50, 1500);
01712         DrawPath(&ThePath);
01713         ThePath.Translate(50, 1500);
01714         DrawPath(&ThePath);
01715         ThePath.Translate(50, 1500);
01716         DrawPath(&ThePath);
01717         ThePath.Translate(50, 1500);
01718         DrawPath(&ThePath);
01719         ThePath.Translate(50, 1500);
01720         DrawPath(&ThePath);
01721         ThePath.Translate(50, 1500);
01722         DrawPath(&ThePath);
01723         ThePath.Translate(50, 1500);
01724         DrawPath(&ThePath);
01725         ThePath.Translate(50, 1500);
01726         DrawPath(&ThePath);
01727         ThePath.Translate(50, 1500);
01728         DrawPath(&ThePath);
01729         ThePath.Translate(50, 1500);
01730         DrawPath(&ThePath);
01731         ThePath.Translate(50, 1500);
01732         DrawPath(&ThePath);
01733         ThePath.Translate(50, 1500);
01734         DrawPath(&ThePath);
01735 
01736         RestoreContext();
01737     }
01738 #endif
01739 // ************************************************************
01740 
01741     // Check that Initialise was called
01742     ENSURE(RenderFlags.Rendering, "DeInitialise called before Initialise");
01743 
01744     DeInitClipping();
01745     DeInitAttributes();
01746 
01747     if (OldPalette)
01748     {
01749         PaletteManager::StopPaintPalette(RenderDC, OldPalette);
01750         OldPalette = NULL;
01751     }
01752 
01753     // Clean out the Fuzzy Rectangles
01754     InnerRect.MakeEmpty();
01755     OuterRect.MakeEmpty();
01756 
01757     TRACEUSER("Gavin",_T("OSRenderRegion::StopRender - RenderFlags.Rendering = FALSE;\n"));
01758     RenderFlags.Rendering = FALSE;
01759 
01760     GDrawBrush.Stop();
01761 
01762     if (SepTables != NULL)
01763     {
01764         CCFree(SepTables);
01765         SepTables = NULL;
01766     }
01767 
01768     return TRUE;
01769 }
01770 
01771 
01772 
01773 /********************************************************************************************
01774 
01775 >   virtual void OSRenderRegion::SetSolidColours(BOOL SetSolid)
01776 
01777     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
01778     Created:    16/5/95
01779 
01780     Inputs:     SetSolid - TRUE to turn off dithering of colours
01781 
01782     Purpose:    Allows the dithering of colours to be turned off so they look the same
01783                 using GDraw as they do with the GDI. In OSRenderRegions, this causes
01784                 any attached GBrush technology to drop into/out of solid colour mode.
01785 
01786 ********************************************************************************************/
01787 
01788 void OSRenderRegion::SetSolidColours(BOOL SetSolid)
01789 {
01790     if (GDrawBrush.Available())
01791         GDrawBrush.SetSolidColours(SetSolid);
01792 }
01793 
01794 
01795 
01796 /********************************************************************************************
01797 
01798 >   void OSRenderRegion::DrawPathToOutputDevice(Path* PathToDraw)
01799 
01800     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
01801     Created:    26/5/93
01802     Inputs:     PathToDraw is a pointer to a Path object to render
01803     Purpose:    Renders a path object to the GDI
01804     SeeAlso:    OSRenderRegion::DrawRect()
01805 
01806 ********************************************************************************************/
01807 
01808 void OSRenderRegion::DrawPathToOutputDevice(Path* PathToDraw, PathShape)
01809 {
01810     // If we are not drawing complex shapes and this shape is, then return
01811     if ((!RenderComplexShapes) && (TestForComplexShape(&Caps)))
01812         return;
01813 
01814     // We want to draw it, so get pens and brushs
01815     GetValidPen();
01816     GetValidBrush();
01817 
01818     // and draw the path
01819     RenderPath(PathToDraw);                 // Render the Path
01820 
01821     // draw the arrow heads on to it.
01822     DrawPathArrowHeads(NORMALPATH(PathToDraw));
01823 }
01824 
01825 /********************************************************************************************
01826 
01827     static BOOL SlowRectangle( CDC* pDC, const RECT& Rect)
01828 
01829     Author:     Andy_Pennell (Xara Group Ltd) <camelotdev@xara.com>
01830     Created:    12/12/94
01831     Inputs:     A rendering context and a rectangle.
01832     Returns:    TRUE if worked, FALSE if failed
01833     Purpose:    Renders a rectangle using Ploygon instead of Rectangle. Used for BodgeRectangles
01834                 devices whi cannot render rectangles correctly. Should really only be used
01835                 when no fill brush has been set as solid ones with XORing on don't render
01836                 correctly.
01837 
01838 ********************************************************************************************/
01839 /*
01840 static BOOL SlowRectangle( wxDC* pDC, const wxRect& Rect)
01841 {
01842     wxPoint NotRect[4];
01843 
01844     NotRect[0].x = Rect.left;
01845     NotRect[0].y = Rect.top;
01846 
01847     NotRect[1].x = Rect.right;
01848     NotRect[1].y = Rect.top;
01849                              
01850     NotRect[2].x = Rect.right;
01851     NotRect[2].y = Rect.bottom;
01852 
01853     NotRect[3].x = Rect.left;
01854     NotRect[3].y = Rect.bottom;
01855 
01856     return pDC->DrawPolygon(4,NotRect);
01857 }
01858 */
01859 /********************************************************************************************
01860 
01861 >   void OSRenderRegion::DrawRect(DocRect* RectToDraw)
01862 
01863     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
01864     Created:    26/5/93
01865     Inputs:     RectToDraw is a pointer to a DocRect object to render
01866     Purpose:    Renders a Rectangle to the GDI
01867     SeeAlso:    OSRenderRegion::DrawPath()
01868 
01869 ********************************************************************************************/
01870 
01871 void OSRenderRegion::DrawRect(DocRect* RectToRend)
01872 {
01873     // If we are not drawing complex shapes and this shape is, then return
01874     if ((!RenderComplexShapes) && (TestForComplexShape(&Caps)))
01875         return;
01876 
01877     GetValidPen();
01878     GetValidBrush();
01879 
01880 //  TRACEUSER("Gavin",_T("RectToRend      : %08x %08x %08x %08x"),
01881 //        RectToRend->lo.x,RectToRend->lo.y,RectToRend->hi.x,RectToRend->hi.y);
01882 //  TRACEUSER("Gavin",_T("CurrentClipRect : %08x %08x %08x %08x"),
01883 //        CurrentClipRect.lo.x,CurrentClipRect.lo.y,CurrentClipRect.hi.x,CurrentClipRect.hi.y);
01884 
01885     // First see if the rect intersects with the clip rect
01886     if (!RectToRend->IsIntersectedWith(CurrentClipRect))
01887         return;
01888 
01889     // OK, it intersects, but we should try to clip it with the inner rect
01890     DocRect DrawRect = RectToRend->Intersection(InnerRect);
01891 
01892 //  TRACEUSER("Gavin",_T("DrawRect        : %08x %08x %08x %08x *"),
01893 //        DrawRect.lo.x,DrawRect.lo.y,DrawRect.hi.x,DrawRect.hi.y);
01894 
01895 // NOTE
01896 // Sometimes the PasteBoard and Page rects are dissapearing at high zoom.
01897 // This seems to be because of the GDI 16 bit limit being exceeded.
01898 // We need to check for the overflow, or clip the rects ourselves. Hence the
01899 // TRUE args on the end of DocRectToWin
01900     WinRect Rect;
01901 
01902     // Ok, all the Fuzzy clipping has been taken care of, now convert and draw the rectangle we have left
01903 /*  if (BodgeRectangles)
01904     {
01905         // if using Ploygon to draw rectangles, no bodging of co-ords is required at all
01906         // (as you would expect)
01907 */      Rect = DocRectToWin(DrawRect, 0,0,0,0, TRUE);
01908 /*  }
01909     else if (RR_STROKECOLOUR().IsTransparent())
01910     {
01911         Rect = DocRectToWin(DrawRect, 0,0,1,1, TRUE);
01912     }
01913     else
01914     {
01915         Rect = DocRectToWin(DrawRect, 0,-1,1,0, TRUE);
01916     }
01917 
01918     BOOL RectOK;
01919 
01920     // Draw the Rectangle
01921     if (BodgeRectangles)
01922     {
01923         RectOK = SlowRectangle(RenderDC, Rect);
01924         ENSURE(RectOK,"CDC::Rectangle failed in DrawRect");
01925     }
01926     else
01927 */      RenderDC->DrawRectangle(Rect);
01928 
01929 }
01930 
01931 /********************************************************************************************
01932 
01933 >   void OSRenderRegion::DrawDragRect(DocRect* RectToDraw)
01934 
01935     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
01936     Created:    26/5/93
01937     Inputs:     RectToDraw is a pointer to a DocRect object to render
01938     Purpose:    Renders a Rectangle to the GDI . Used to claim "without shifting it about"
01939                 bit it does adjust it now for the differences in rendering models.
01940     SeeAlso:    OSRenderRegion::DrawPath()
01941 
01942 ********************************************************************************************/
01943 
01944 void OSRenderRegion::DrawDragRect(DocRect* RectToDraw)
01945 {
01946     GetValidPen();
01947     GetValidBrush();
01948 
01949 // NOTE
01950 // Sometimes the PasteBoard and Page rects are dissapearing at high zoom.
01951 // This seems to be because of the GDI 16 bit limit being exceeded.
01952 // We need to check for the overflow, or clip the rects ourselves.
01953 
01954     WinRect Rect = DocRectToWin(*RectToDraw, 0,0,0,0);
01955 
01956     // for some reason, all flavours of GDI can screw up when asked to render zero-width/
01957     // height rectangles when in XOR mode, as it often leaves little pixels behind. Our fix
01958     // for this is to simply not render anything if zero width or height in EOR mode
01959 
01960     if ( DrawingMode != DM_EORPEN ||                    // if not EOR then always
01961         (
01962             Rect.width !=1 &&           // else must have width
01963             Rect.height!=1              // and height
01964         ) )
01965         RenderDC->DrawRectangle(Rect);      // Draw the Rectangle
01966 }
01967 
01968 
01969 
01970 /********************************************************************************************
01971 
01972 >   void OSRenderRegion::DrawDragBounds(DocRect *RectToRender)
01973 
01974     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
01975     Created:    16/5/95
01976 
01977     Inputs:     RectToRender - The bounding rectangle we wish to render.
01978 
01979     Purpose:    Used to draw a "rectangle" to screen (for dragging). However, the corners of
01980                 the rectangle are passed through the current rendering matrix, so the rect
01981                 may well be rotated or skewed by the time it reaches the screen.
01982 
01983                 The rectangle is never filled, and will be stroked in XOR mode, using the
01984                 current line colour. (i.e. only line colour needs to be set before calling
01985                 this method, and it'll *always* be XOR'd regardless of other settings)
01986 
01987                 This is used for XOR dragging of selected objects as they are skewed, rotated,
01988                 translated, etc
01989 
01990     Notes:      DO NOT confuse this method with DrawDragRect (which will draw a rectangle
01991                 without skew or rotation)
01992 
01993     SeeAlso:    OSRenderRegion::DrawDragRect; RenderRegion::DrawDragBounds;
01994                 Range::RenderXOROutlinesOn
01995 
01996 ********************************************************************************************/
01997 
01998 void OSRenderRegion::DrawDragBounds(DocRect *RectToRender)
01999 {
02000     INT32 OurOldBkMode = RenderDC->GetBackgroundMode() ;
02001     INT32 OurOldDrawingMode = RenderDC->GetLogicalFunction();
02002     wxPen OurOldPen = RenderDC->GetPen() ;
02003 
02004     wxPen DotPen(CalcEORColour(RR_STROKECOLOUR()),1,wxDOT);
02005     RenderDC->SetPen(DotPen);
02006     RenderDC->SetBackgroundMode(wxTRANSPARENT);
02007     RenderDC->SetLogicalFunction(wxXOR);
02008 
02009     // Now, render the 4 lines, converting the corners of the rectangle with the
02010     // current rendering matrix
02011     WinCoord Points[5];
02012     Points[0] = 
02013     Points[4] = DocCoordToWin(RectToRender->lo);
02014     Points[1] = DocCoordToWin(DocCoord(RectToRender->lo.x, RectToRender->hi.y));
02015     Points[2] = DocCoordToWin(RectToRender->hi);
02016     Points[3] = DocCoordToWin(DocCoord(RectToRender->hi.x, RectToRender->lo.y));
02017 
02018     // And close the shape
02019     RenderDC->DrawLines(5,Points);
02020 
02021     // And restore the previous GDI settings
02022     RenderDC->SetLogicalFunction(OurOldDrawingMode);
02023     RenderDC->SetBackgroundMode(OurOldBkMode);
02024     RenderDC->SetPen(OurOldPen);
02025 }
02026 
02027 
02028 /********************************************************************************************
02029 
02030 >   void OSRenderRegion::DrawDashLine(const DocCoord &StartPoint, const DocCoord &EndPoint)
02031 
02032     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
02033     Created:    10/10/95
02034     Inputs:     StartPoint  - start of the line
02035                 EndPoint    - end of the line
02036     Purpose:    Draws a dashed line between the two points.
02037                 Uses the technology from DrawDragBounds()
02038     SeeAlso:    OSRenderRegion::DrawDragBounds()
02039 
02040 ********************************************************************************************/
02041 
02042 void OSRenderRegion::DrawDashLine(const DocCoord &StartPoint, const DocCoord &EndPoint)
02043 {
02044     INT32 OurOldBkMode = RenderDC->GetBackgroundMode() ;
02045     INT32 OurOldDrawingMode = RenderDC->GetLogicalFunction();
02046     wxPen OurOldPen = RenderDC->GetPen() ;
02047 
02048     wxPen DotPen(CalcEORColour(RR_STROKECOLOUR()),1,wxDOT);
02049     RenderDC->SetPen(DotPen);
02050     RenderDC->SetBackgroundMode(wxTRANSPARENT);
02051     RenderDC->SetLogicalFunction(wxXOR);
02052 
02053     // Now, render the line
02054     WinCoord StartPt = DocCoordToWin(StartPoint);
02055     WinCoord EndPt   = DocCoordToWin(EndPoint  );
02056     RenderDC->DrawLine(StartPt,EndPt);
02057 
02058     // And restore the previous GDI settings
02059     RenderDC->SetLogicalFunction(OurOldDrawingMode);
02060     RenderDC->SetBackgroundMode(OurOldBkMode);
02061     RenderDC->SetPen(OurOldPen);
02062 }
02063 
02064 /********************************************************************************************
02065 
02066 >   void OSRenderRegion::DrawLine(const DocCoord &StartPoint, const DocCoord &EndPoint)
02067 
02068     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
02069     Created:    11/6/93
02070     Inputs:     StartPoint is the Inclusive start coordinate.
02071                 EndPoint is thw Exclusive end coordinate.
02072     Purpose:    Renders a Line to the GDI
02073     SeeAlso:    OSRenderRegion::DrawPath(); OSRenderRegion::DrawRect()
02074 
02075 ********************************************************************************************/
02076 
02077 void OSRenderRegion::DrawLine(const DocCoord &StartPoint, const DocCoord &EndPoint)
02078 {
02079     // If we are not drawing complex shapes and this shape is, then return
02080     if ((!RenderComplexShapes) && (TestForComplexShape(&Caps)))
02081         return;
02082 
02083     GetValidPen();
02084     GetValidBrush();
02085     
02086     const UINT32 LINEVERTICES = 2;
02087     wxPoint LinePoints[LINEVERTICES];
02088     LinePoints[0] = DocCoordToWin(StartPoint);  //LinePoints[0].y--;
02089     LinePoints[1] = DocCoordToWin(EndPoint);    //LinePoints[1].y--;
02090 
02091     RenderDC->DrawLine(LinePoints[0],LinePoints[1]);    // Draw the Line
02092 #if 0
02093     if (!StartArrow.IsNull)
02094         DrawLineArrow(StartArrow, StartPoint, EndPoint);
02095 
02096     if (!EndArrow.IsNull)
02097         DrawLineArrow(EndArrow, EndPoint, StartPoint);
02098 #endif
02099 }
02100 
02101 /********************************************************************************************
02102 
02103 >   void OSRenderRegion::DrawPixel(const DocCoord &Point)
02104 
02105     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
02106     Created:    21/02/94
02107     Inputs:     Point - the coordinate to plot the point at.
02108     Purpose:    Plot a single pixel in a render region.
02109 
02110 ********************************************************************************************/
02111 
02112 void OSRenderRegion::DrawPixel(const DocCoord &Point)
02113 {
02114     wxPoint LinePoint = DocCoordToWin(Point);
02115 //  RenderDC->SetPixel(LinePoint,
02116 //              ConvertColourToScreenWord(CurrentColContext, &RR_STROKECOLOUR()));
02117     INT32 R,G,B;
02118     RR_STROKECOLOUR().GetRGBValue(&R,&G,&B);
02119     RenderDC->SetPen(wxColour(R,G,B));
02120     RenderDC->DrawPoint(LinePoint);
02121 }
02122 
02123 /********************************************************************************************
02124 
02125 >   void OSRenderRegion::DrawCross(const DocCoord &Point, const UINT32 Size)
02126 
02127     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
02128     Created:    21/02/94
02129     Inputs:     Point - the centre point of the cross.
02130                 Size - the length of the lines used to draw the cross with.
02131     Purpose:    Draws a cross in the render region.  The cross consists of a vertical
02132                 line 'Size' millipoints high, and a horizontal line 'Size' millipoints
02133                 wide.
02134 
02135 ********************************************************************************************/
02136 
02137 void OSRenderRegion::DrawCross(const DocCoord &Point, const UINT32 Size)
02138 {
02139     GetValidPen();
02140     
02141     const MILLIPOINT Length = Size * ScaledPixelWidth;
02142 
02143     // Work out cross coordinates in documenty coords
02144     DocCoord Coords[3];
02145     Coords[0] = Point;
02146 
02147     Coords[1].x = Point.x - Length;
02148     Coords[1].y = Point.y - Length;
02149 
02150     Coords[2].x = Point.x + Length + ScaledPixelWidth;  
02151     Coords[2].y = Point.y + Length + ScaledPixelWidth;
02152 
02153     // Convert to window coordinates
02154     wxPoint Points[3];
02155     Points[0] = DocCoordToWin(Coords[0]);
02156     Points[1] = DocCoordToWin(Coords[1]);
02157     Points[2] = DocCoordToWin(Coords[2]);
02158 
02159     // Draw the horizontal line
02160     RenderDC->DrawLine(Points[1].x,Points[0].y,Points[2].x,Points[0].y);
02161     // Draw the vertical line
02162     RenderDC->DrawLine(Points[0].x,Points[1].y,Points[0].x,Points[2].y);
02163 }
02164 
02165 
02166 
02167 /********************************************************************************************
02168 
02169 >   virtual void OSRenderRegion::DrawFixedSystemText(StringBase *TheText,
02170                                                         DocRect &BoundsRect)
02171 
02172     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
02173     Created:    12/1/95
02174     Inputs:     TheText - The string to render
02175                 BoundsRect - Determines where the text is to be plotted. The text will also
02176                 be clipped within this bounding rectangle if it exceeds the available space.
02177 
02178     Purpose:    To draw simple text, using the default host-operating-system font.
02179                 The size and style of this font are decided by the host OS (or oil code)
02180                 and cannot be set in any way. To determine how much space is needed to
02181                 display a string with this method, see the SeeAlso.
02182 
02183     Notes:      This method is intended solely for the use of kernel-rendered dialogues,
02184                 such as SuperGalleries, which need to display simple text information. It
02185                 should not be confused with document rendering of arbitrary text paths etc.
02186 
02187                 Currently, the text is drawn in a default manner (left justified and centered
02188                 vertically; one line of text only (no word-wrap onto subsequent lines), etc)
02189                 Do not use special characters such as tab/newline -their effect is undefined
02190 
02191     SeeAlso:    OSRenderRegion::GetFixedSystemTextSize;
02192                 OSRenderRegion::SetFixedSystemTextColours
02193 
02194 ********************************************************************************************/
02195 
02196 void OSRenderRegion::DrawFixedSystemText(StringBase *TheText, DocRect &BoundsRect, UINT32 uFormat /* = FORMAT_SINGLELINE | FORMAT_NOPREFIX | FORMAT_VCENTER */)
02197 {
02198     wxString Text = (wxString)(TCHAR *)(*TheText);
02199     wxRect Rect(0,0,0,0);
02200 
02201     wxFont SaveFont=RenderDC->GetFont();
02202 
02203     wxFont FixedFont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
02204     FixedFont.SetPointSize(8);
02205     RenderDC->SetFont(FixedFont);
02206 
02207     wxDC * pDC = RenderDC;
02208     wxSize DPI = GetFixedDCPPI(*pDC);
02209     INT32 XDPI = DPI.GetWidth();
02210     INT32 YDPI = DPI.GetHeight();
02211     INT32 LineHeight = RenderDC->GetCharHeight();
02212 
02213     if (uFormat & FORMAT_CALCRECT) // just calculate the rect needed to draw the text and return
02214     {
02215         // This won't actually draw the text, instead it returns a rectangle in 'Rect'  
02216         // LineHeight = RenderDC->DrawText((TCHAR *) (*TheText), -1, &Rect, uFormat);
02217         wxCoord w, h;
02218         RenderDC->GetTextExtent(Text, &w, &h);
02219         Rect = wxRect(0, 0, w, h);
02220 
02221 
02222         if(XDPI == 0 || YDPI == 0 || LineHeight == 0)
02223         {
02224             ERROR3("Can not calculate text size");
02225             BoundsRect = DocRect(0, 0, 0, 0);
02226             RenderDC->SetFont(SaveFont);
02227             return;
02228         }
02229         // For some reason, Rect.bottom and Rect.top seem to be incorrect, so we have
02230         // to use the returned LineHeight value
02231         BoundsRect = DocRect(0, 0,
02232                               (INT32)(((double)Rect.width * IN_MP_VAL) / XDPI),
02233                               (INT32)(((double)LineHeight * IN_MP_VAL) / YDPI) );
02234         RenderDC->SetFont(SaveFont);
02235         return;
02236     }
02237     
02238     // We plot using the TOP of the rectangle supplied (that's the highest DocCoord
02239     // but we need to center it (i.e. reduce the value
02240     DocRect brect=BoundsRect;
02241 
02242     brect.hi.y -= ((brect.hi.y-brect.lo.y)-(INT32)(((double)LineHeight * IN_MP_VAL) / YDPI))/2;
02243 
02244     Rect = DocRectToWin(brect, 0,0,0,0, TRUE);
02245     // Rect = DocRectToWin(BoundsRect, 0,-1,1,0, TRUE);
02246 
02247     // Small 'fix' - If we DrawRect the 'BoundsRect' then windows draws the text 1 pixel
02248     // further to the right than the rectangle. This just ensures that the text is always
02249     // clipped inside the rectangle rather than outside (at worst, a pixel too far inside)
02250     if (Rect.width>0)       // Still a valid rectangle?
02251     {
02252 //      Rect.x--;
02253 
02254         // wxWidgets seems to corrupt the current brush here, which we need to preserve
02255         wxBrush SaveBrush=RenderDC->GetBrush();
02256         INT32 SaveBackgroundMode=RenderDC->GetBackgroundMode();
02257 
02258         // Draw the text
02259         RenderDC->SetBackgroundMode(wxTRANSPARENT);
02260         RenderDC->DrawText(Text, Rect.GetLeft(), Rect.GetTop());
02261 
02262         // Restore the brush. First we have to ensure SetBrush really works. This is
02263         // because the GTK brush is corrupted whilst the wxWidgets one is still right.
02264         // So we have to select a different brush or wxWidgets will skip the call
02265         // that would otherwise fix the GTK brush - wxWidgets bug #148096
02266         RenderDC->SetBackgroundMode(SaveBackgroundMode);
02267         RenderDC->SetBrush(*wxTRANSPARENT_BRUSH);
02268         RenderDC->SetBrush(wxNullBrush);
02269         RenderDC->SetBrush(SaveBrush);
02270 
02271 //      LineHeight = RenderDC->DrawText((TCHAR *) (*TheText), -1, &Rect, uFormat);
02272 
02273 //      ENSURE(LineHeight > 0, "OSRenderRegion::DrawFixedSystemText failed");
02274     }
02275 
02276     RenderDC->SetFont(SaveFont);
02277 }
02278 
02279 
02280 
02281 /********************************************************************************************
02282 
02283 >   virtual void OSRenderRegion::SetFixedSystemTextColours(DocColour *TextCol,
02284                                                          DocColour *Background)
02285 
02286     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
02287     Created:    12/1/95
02288     Inputs:     TextCol - The foreground (text) colour (may be NULL)
02289                 Background - The background colour (may be NULL)
02290 
02291     Purpose:    To set the text and background colour(s) for any FixedSystem text rendered
02292                 in this render region in the future (within the current rendering pass).
02293 
02294     Notes:      If either of the colours is passed in as a NULL pointer, that colour will
02295                 not be set, and will remain at the previous setting.
02296 
02297                 Note that these values are passed directly to the host OS at the point
02298                 of calling, so you must call this method every time the colour is changed
02299                 (i.e. don't rely on just changing the DocColours that you passed in
02300                 originally, as they aren't remembered in any way). Note also that if you
02301                 call DrawFixedSystemText without a prior call to this method, the colours
02302                 used are undefined.
02303 
02304                 The colour chosen may not exactly match the colour requested - under
02305                 Windows, for example, the nearest solid (non-dithered) colour to that
02306                 requested will be used. Thus, it is best to stick to 'simple' colour
02307                 schemes.
02308 
02309                 This method may or may not affect some other rendering processes (for
02310                 example, under Windows, plotting of bitmaps may be affected by the
02311                 settings used here...)
02312 
02313     SeeAlso:    OSRenderRegion::DrawFixedSystemText
02314 
02315 ********************************************************************************************/
02316 
02317 void OSRenderRegion::SetFixedSystemTextColours(DocColour *TextCol, DocColour *Background)
02318 {
02319     if (TextCol != NULL)
02320     {
02321         COLORREF Clr = ConvertColourToScreenWord(CurrentColContext, TextCol);
02322         RenderDC->SetTextForeground(Clr);
02323     }
02324 
02325     if (Background != NULL)
02326     {
02327         COLORREF Clr = ConvertColourToScreenWord(CurrentColContext, Background);
02328         RenderDC->SetTextBackground(Clr);
02329     }
02330 }
02331 
02332 
02333 
02334 /********************************************************************************************
02335 
02336 >   virtual void OSRenderRegion::GetFixedSystemTextSize(StringBase *TheText,
02337                                                         DocRect *BoundsRect)
02338 
02339     Author:     Richard_Millican (Xara Group Ltd) <camelotdev@xara.com>
02340     Created:    16/3/95
02341 
02342     Inputs:     TheText - The string to obtain the rendered size from
02343 
02344     Outputs:    BoundsRect - Returned with the size of the rectangle needed to display
02345                 said text string. This rect always has one corner at (0,0)
02346 
02347     Returns:    -
02348 
02349     Purpose:    To determine how much room is needed to plot a bit of text with
02350                 OSRenderRegion::DrawFixedSystemText
02351 
02352     Notes:      If for any reason the call fails, an origin based rectangle with zero
02353                 height and width will be returned.
02354                 
02355     SeeAlso:    OSRenderRegion::DrawFixedSystemText
02356 
02357 ********************************************************************************************/
02358 
02359 void OSRenderRegion::GetFixedSystemTextSize(StringBase *TheText, DocRect *BoundsRect, double* atDpi)
02360 {
02361     wxString Text = (wxString)(TCHAR *)(*TheText);
02362 
02363     ERROR3IF(TheText == NULL, "OSRenderRegion::GetFixedSystemTextSize given a null text pointer");
02364     ERROR3IF(BoundsRect == NULL, "OSRenderRegion::GetFixedSystemTextSize given a null bounds rect pointer");
02365     if(TheText == NULL || BoundsRect == NULL)
02366         return;
02367 
02368     wxFont SaveFont=RenderDC->GetFont();
02369 
02370     wxFont FixedFont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
02371     FixedFont.SetPointSize(8);
02372     RenderDC->SetFont(FixedFont);
02373 
02374     INT32 LineHeight = 0;
02375     // This won't actually draw the text, instead it returns a rectangle in 'Rect'  
02376     // LineHeight = RenderDC->DrawText((TCHAR *) (*TheText), -1, &Rect, uFormat);
02377     LineHeight = RenderDC->GetCharHeight();
02378     wxCoord w, h;
02379     RenderDC->GetTextExtent(Text, &w, &h);
02380     wxRect Rect(0, 0, w, h);
02381 
02382     RenderDC->SetFont(SaveFont);
02383 
02384     wxDC * pDC = RenderDC;
02385     wxSize DPI = GetFixedDCPPI(*pDC);
02386     INT32 XDPI = DPI.GetWidth();
02387     INT32 YDPI = DPI.GetHeight();
02388 
02389     if(XDPI == 0 || YDPI == 0 || LineHeight == 0)
02390     {
02391         ERROR3("Can not calculate text size");
02392         *BoundsRect = DocRect(0, 0, 0, 0);
02393         return;
02394     }
02395     // For some reason, Rect.bottom and Rect.top seem to be incorrect, so we have
02396     // to use the returned LineHeight value
02397     *BoundsRect = DocRect(0, 0,
02398                             (INT32)(((double)Rect.width * IN_MP_VAL) / XDPI),
02399                             (INT32)(((double)LineHeight * IN_MP_VAL) / YDPI) );
02400     return;
02401 
02402 }
02403 
02404 
02405 
02406 #define MAX_POLYGONS    256
02407 
02408 
02409 /********************************************************************************************
02410 
02411 >   INT32 OSRenderRegion::RawRenderPath( DocCoord *const Coords, PathVerb *const Verbs, 
02412                                        INT32 NumCoords, INT32 *PolyCount, INT32 *ResCount,
02413                                        INT32 Flatness = 0, INT32 *pActualFlatness = NULL )
02414 
02415     Author:     Andy_Pennell (Xara Group Ltd) <camelotdev@xara.com>
02416     Created:    20/4/94
02417     Inputs:     A path to be drawn (in DocCoords).
02418                 Flatness - the value to use when flattening a curve.
02419                 pActualFlatness - if the caller wants to know what flatness was eventually
02420                                   reached while flattening the path, it passes in a pointer
02421                                   here to be filled in.  If the path could not be flattened,
02422                                   this contains the last flatness level that was tried
02423                                   before giving up.
02424     Outputs:    PolyCounts must point to an array of MAX_POLYGONS ints which will be filled
02425                 in with a list of polygon sizes (can be just 1 big if ResCount is NULL). 
02426                 *ResCount will be updated with the number of polygons. If it is NULL then the
02427                 routine returns after the end of the first sub-path has been found.
02428                 The points calculated from this will be in the PointArray variable.
02429     Returns:    Number of points read, 0 if failed (e.g. not enough memory or too complex).
02430     Purpose:    Low level primitive for rendering paths without GDI32. Note that PointArray
02431                 should be freed by the caller upon completion.
02432     Errors:     -
02433     Scope:      Private
02434     SeeAlso:    RawRenderPath32
02435 
02436 ********************************************************************************************/
02437 
02438 INT32 OSRenderRegion::RawRenderPath( DocCoord *const Coords, PathVerb *const Verbs, INT32 NumCoords, 
02439                                    INT32 *PolyCounts, INT32 *ResCount,
02440                                    INT32 Flatness, INT32 *pActualFlatness )
02441 {
02442     // Flatness less than 0 will screw up, so let's ignore it
02443     if (Flatness < 0)
02444         return 0;
02445 
02446     // We have to manually flatten the beziers in the curve and decide when to draw them.
02447     INT32 MaxFlatness;
02448 
02449     // Decide how flat we are going to make this curve, if the caller has not specified
02450     if (Flatness == 0)
02451     {
02452         // (Tim says: these figures are a bit arbitrary, especially the MaxFlatness ones)
02453         if (RFlags.Metafile)
02454         {
02455 #if !defined(EXCLUDE_FROM_RALPH) && !defined(EXCLUDE_FROM_XARALX)
02456             // If this is a Metafile view then go and find out the flatness
02457             if (RenderView->IS_KIND_OF(MetafileView))
02458                 Flatness = ((MetafileView*)RenderView)->GetMetafileFlatness();
02459             else
02460 #endif
02461                 Flatness = 512;
02462         }
02463         else
02464         {
02465             Flatness = CalcPathFlattening();
02466         }
02467     }
02468 
02469     MaxFlatness = Flatness * 8;
02470 
02471     // The number of polygons found so far
02472     INT32 Count = 0;
02473 
02474     // The number of points that have been placed in complete polygons
02475     INT32 PointsSoFar = 0;
02476 
02477     // Position we are reading points from
02478     INT32 ReadPos = 0;
02479 
02480     // Flatten curve until it works or flatness becomes too inaccurate.
02481     BOOL Worked = FALSE;
02482     while (!Worked && (Flatness <= MaxFlatness) &&(Flatness > 0))
02483     {
02484         // Position we are adding points at
02485         InsertPos = 0;
02486 
02487         ReadPos = 0;
02488         Count = 0;
02489         PointsSoFar = 0;
02490 
02491         Worked = TRUE;
02492         BOOL KeepGoing = TRUE;
02493 
02494         // loop through the whole path
02495         while (
02496                 KeepGoing &&
02497                 (ReadPos < NumCoords) &&
02498                 Worked
02499               )
02500         {
02501             if ( InsertPos >= SIZEOF_POLYLINE_BUFFER )
02502             {
02503                 // this check is enough for those times when CurrentPoint is simply incrememented,
02504                 // but is insufficient when it is increased by more than one, so additional checks
02505                 // are needed elsewhere
02506                 TRACE( _T("Path too complex to flatten\n"));
02507                 InsertPos = 0;
02508                 Worked = FALSE;
02509                 break;
02510             }
02511 
02512             // Find out the type of element that we are over (after the close flag has been removed)
02513             Coord P[4];
02514             INT32 OldInsertPos;
02515 
02516             switch ( (Verbs[ReadPos]) & (~PT_CLOSEFIGURE) )
02517             {
02518                 case PT_MOVETO:
02519                     // This represents the start of a new polygon, so finish the last one
02520                     if (InsertPos>0)
02521                     {
02522                         // We have put points into the polygon, so this is not the first one
02523                         PolyCounts[Count] = InsertPos - PointsSoFar;
02524 
02525                         // Keep tabs on how many points have been put in polygons so far
02526                         PointsSoFar = InsertPos;
02527 
02528                         // Count the number of polygons
02529                         Count++;
02530 
02531                         // Make sure its not too many
02532                         if (Count == MAX_POLYGONS)
02533                         {
02534                             TRACE( _T("Too many polygons in path to render\n") );
02535                             InsertPos = 0;
02536                             Worked = FALSE;
02537                             break;
02538                         }
02539 
02540                         if (ResCount==NULL)
02541                         {
02542                             // sub-path has ended, stop the loop now
02543                             KeepGoing = FALSE;
02544                             break;
02545                         }
02546                     }
02547 
02548                     // Put the MoveTo into the point array
02549                     PointArray[InsertPos] = DocCoordToWin(Coords[ReadPos]);
02550                     InsertPos++;
02551                     ReadPos++;
02552                     break;
02553 
02554 
02555                 case PT_LINETO:
02556                     // Put the LineTo into the point array
02557                     PointArray[InsertPos] = DocCoordToWin(Coords[ReadPos]);
02558                     InsertPos++;
02559                     ReadPos++;
02560                     break;
02561 
02562 
02563                 case PT_BEZIERTO:
02564                     // If this point is a bezier, then the next 2 points should be beziers to
02565                     ENSURE((Verbs[ReadPos+1]) & (~PT_CLOSEFIGURE), "Bezier found with 1 point");
02566                     ENSURE((Verbs[ReadPos+2]) & (~PT_CLOSEFIGURE), "Bezier found with 2 points");
02567                 
02568                     // Make sure that this is not at the start of the path
02569                     ENSURE(ReadPos>0, "Broken path found while flattening" );
02570                     OldInsertPos = InsertPos;
02571 
02572                     // Flatten the bezier out
02573                     P[0] = DocCoordToOS256(Coords[ReadPos-1]);
02574                     P[1] = DocCoordToOS256(Coords[ReadPos]);
02575                     P[2] = DocCoordToOS256(Coords[ReadPos+1]);
02576                     P[3] = DocCoordToOS256(Coords[ReadPos+2]);
02577 
02578                     if (!Bezier(P[0].x,P[0].y, P[1].x,P[1].y, P[2].x,P[2].y, P[3].x,P[3].y, Flatness))
02579                     {
02580                         // Not enough room to flatten bezier.
02581                         Worked = FALSE;
02582                         break;
02583                     }
02584 
02585                     // If the curve was already flat then check to see if the curve was a vertical
02586                     // or horizontal line.  If it was then ensure the coords are the same to avoid kinks.
02587                     if (InsertPos == OldInsertPos+1)
02588                     {
02589                         if (Coords[ReadPos-1].x == Coords[ReadPos+2].x)
02590                             PointArray[OldInsertPos].x = PointArray[OldInsertPos-1].x;
02591                         if (Coords[ReadPos-1].y == Coords[ReadPos+2].y)
02592                             PointArray[OldInsertPos].y = PointArray[OldInsertPos-1].y;
02593                     }
02594 
02595                     // Update the positions and break
02596                     // InsertPos is updated by the bezier functions as the number of points
02597                     // that they add depends on the curve
02598                     ReadPos+=3;
02599                     break;
02600 
02601                 default:
02602                     ENSURE( FALSE, "We found a Path Element that does not exist!" );
02603                     break;
02604             }
02605 
02606             if (ResCount==NULL)
02607             {
02608                 // caller wants sub-paths, better check for close markers
02609                 if (Verbs[ReadPos-1] & PT_CLOSEFIGURE)
02610                     KeepGoing = FALSE;
02611             }
02612         }
02613 
02614         // Increase flatness in case we need to try again.
02615         Flatness *= 2;
02616     }
02617     
02618     if (!Worked)
02619     {
02620         // oops - didn't work
02621     }
02622     // complete the information about the last polygon in the list
02623     else if (InsertPos>0)
02624     {
02625         PolyCounts[Count] = InsertPos - PointsSoFar;
02626         Count++;
02627 
02628         if (ResCount)
02629             *ResCount = Count;
02630     }
02631     else
02632     {
02633         if (ResCount)
02634             *ResCount = 0;
02635         else
02636         {
02637             ENSURE(FALSE, "strange short path");
02638             Worked = FALSE;                                     // don't use incomplete data
02639         }
02640     }
02641 
02642     // Tell the caller what flatness we used if necessary
02643     if (pActualFlatness != NULL)
02644         *pActualFlatness = Flatness;
02645 
02646     // return value is number of verb/coord pairs read.
02647     return Worked ? ReadPos : 0;
02648 }
02649 
02650 /********************************************************************************************
02651 
02652 >   BOOL OSRenderRegion::SetClipToPathTemporary( Path *const PathToDraw)
02653 
02654     Author:     Andy_Pennell (Xara Group Ltd) <camelotdev@xara.com>
02655     Created:    20/4/94
02656     Inputs:     A path to be clipped to (in DocCoords).
02657     Outputs:    -
02658     Returns:    TRUE if worked, FALSE if failed (e.g. not enough memory or cannot do it).
02659     Purpose:    To set a complex clip path. This clip is pretty temporary and should be reset
02660                 afterwards using OSClipRegion before anything much else is done.
02661     Errors:     -
02662     Scope:      Private
02663 
02664 ********************************************************************************************/
02665 
02666 BOOL OSRenderRegion::SetClipToPathTemporary( Path *const PathToDraw)
02667 {
02668     PORTNOTETRACE("other","OSRenderRegion::SetClipToPathTemporary - do nothing");
02669 #ifndef EXCLUDE_FROM_XARALX
02670     BOOL Worked;
02671     Worked = RawRenderPath32( PathToDraw );
02672     if (Worked)
02673     {
02674 //      Worked = ::SelectClipPath( RenderDC, RGN_AND );
02675         if (!Worked)
02676             TRACE( _T("SelectClipPath failed"));
02677     }
02678     else
02679         TRACE( _T("RawPath32 failed in Clip"));
02680     return Worked;
02681 #else
02682     return FALSE;
02683 #endif
02684 }
02685 
02686 // define this to 1 for testing manual line flattening, 0 for real code
02687 #define TEST_MANUAL_LINES   0
02688 
02689 #ifdef _MAC
02690     // usefully Macs are limited to 32k of local vars (!)
02691     #define LONGEST_SEGMENT 2048
02692 #else
02693     #define LONGEST_SEGMENT 8192
02694 #endif
02695 static DocCoord ConvPoints[ LONGEST_SEGMENT ];
02696 static PathVerb ConvVerbs[ LONGEST_SEGMENT ];
02697 
02698 // this required a null pen to have been selected. Fill doesn't matter
02699 
02700 BOOL OSRenderRegion::StrokeProperly( Path *const DrawPath )
02701 {
02702 
02703     const INT32 Flatten = ScaledPixelWidth/2;
02704 
02705     DashType* pDashRec = NULL;
02706     DashType GavinDash;
02707 
02708     // Don't do it if we have transparent stroke colour
02709     DocColour Col = RR_STROKECOLOUR();
02710     if (Col.IsTransparent())
02711         return TRUE;
02712 
02713     // Don't do dash patten if there isn't one, or if the line has zero width.
02714     if ((RR_DASHPATTERN().Elements == 0) || (RR_LINEWIDTH() == 0))
02715     {
02716         // No dash pattern
02717         pDashRec = &GavinDash;
02718         MakeDashType(RR_DASHPATTERN(), pDashRec);
02719     }
02720     else
02721     {
02722         // Dash pattern - convert to Gavin-ness...
02723         MILLIPOINT Width = RR_LINEWIDTH();
02724         DashRec &DashPattern = RR_DASHPATTERN();
02725 
02726         if ((RRQuality.GetLineQuality() >= Quality::FullLine) && DashPattern.Elements > 0)
02727         {
02728             INT32 Length = DashPattern.Elements;
02729 
02730             if (Length > 8) Length = 8;
02731 
02732             BOOL DoScale = DashPattern.ScaleWithLineWidth;
02733             FIXED16 Scale = DoScale ? (double(Width) / double(DashPattern.LineWidth)) : 1;
02734 
02735             GavinDash.Length = Length;
02736             GavinDash.Offset = LongMulFixed16(DashPattern.DashStart, Scale);
02737 
02738             for (INT32 el = 0; el < Length; el++)
02739             {
02740                 GavinDash.Array[el] = LongMulFixed16(DashPattern.ElementData[el], Scale);
02741             }
02742 
02743             pDashRec = &GavinDash;
02744         }
02745     }
02746 
02747     INT32 Result = GRenderRegion::StrokePathToPath( DrawPath->GetCoordArray(),
02748                                     DrawPath->GetVerbArray(),
02749                                     DrawPath->GetNumCoords(),
02750                                     ConvPoints,
02751                                     ConvVerbs,
02752                                     sizeof(ConvVerbs),
02753                                     FALSE,
02754                                     RR_LINEWIDTH(),
02755                                     Flatten,
02756                                     RR_STARTCAP(),
02757                                     RR_JOINTYPE(),
02758                                     pDashRec );
02759     if (Result>0)
02760     {
02761         // Gavin has converted to another path - lets flatten & render that
02762 
02763         // switch filling colour to lines
02764         wxBrush NewBrush;
02765         #if TEST_MANUAL_LINES
02766         // for debugging, lets have a nice red pen and no fill
02767         NewBrush.CreateStockObject( NULL_BRUSH );
02768         wxPen DPen(wxBLACK,0);
02769         wxPen DOldPen = GetPen();
02770         RenderDC->SetPen( &DPen );
02771         #else
02772         // no quality checks here cos we only do this for high quality
02773 //      LOGBRUSH BrushType;
02774 //      CalcLogBrush( &BrushType, RR_STROKECOLOUR() );
02775 //      NewBrush.CreateBrushIndirect( &BrushType );
02776         CalcLogBrush(&NewBrush,RR_STROKECOLOUR());
02777         #endif
02778         wxBrush OldBrush = RenderDC->GetBrush();
02779         RenderDC->SetBrush(NewBrush);
02780 
02781         // we whip through Gavin's sub-paths manually so we are less likely to
02782         // run out of memory when flattening them. It also stops PolyPolygon from
02783         // rampantly filling them wrongly. The PointArray will be allocated the first
02784         // time and we leave it alone
02785 
02786         INT32 PathSizeLeft = Result;                        // items in Gavins path
02787         DocCoord* PathSoFar = ConvPoints;
02788         PathVerb* VerbSoFar = ConvVerbs;
02789         INT32 PolySize;                                 // size of this chunk
02790 
02791         while (PathSizeLeft > 0)
02792         {
02793             // flatten little sub-path              
02794             INT32 Read = RawRenderPath( PathSoFar, VerbSoFar, PathSizeLeft, &PolySize, NULL );
02795 
02796             if (Read)
02797             {
02798                 // (We don't bother to try again if GDI fails, as it is extremely unlikely as
02799                 // we are only doing small Gavin sub-paths from the path stroking routines).
02800                 RenderDC->DrawPolygon(PolySize,PointArray,0,0,nFillStyle);      // render it
02801                 PathSoFar += Read;                              // inc pointers etc
02802                 VerbSoFar += Read;
02803                 PathSizeLeft -= Read;
02804                 ENSURE(PathSizeLeft>=0, "Path backwards too far");
02805             }
02806             else
02807             {
02808                 TRACE( _T("RawRenderPath failed\n"));
02809                 break;                      // stop as we don't know how far to continue
02810             }
02811         }
02812         RenderDC->SetBrush( OldBrush );
02813 
02814         #if TEST_MANUAL_LINES
02815         RenderDC->SetPen( DOldPen );
02816         DPen.DeleteObject();
02817         #endif
02818 
02819         return TRUE;
02820     }
02821     else if (Result<0)
02822     {
02823         return FALSE;                               // as Gavin couldn't do it
02824     }
02825     else
02826         return TRUE;                                // no points is not an error
02827 }
02828 
02829 void OSRenderRegion::MakeDashType(DashRec& KernelDash, DashType* GavinDash)
02830 {
02831     INT32 Length = KernelDash.Elements;
02832 
02833     if (Length > 8) Length = 8;
02834         
02835     GavinDash->Length = Length;
02836     GavinDash->Offset = KernelDash.DashStart;
02837 
02838     for (INT32 el = 0; el < Length; el++)
02839     {
02840         GavinDash->Array[el] = KernelDash.ElementData[el];
02841     }
02842 }
02843 
02844 /********************************************************************************************
02845 
02846 >   void OSRenderRegion::RenderPath( Path *DrawPath )
02847 
02848     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
02849     Created:    28/6/93
02850     Purpose:    Renders a path to the GDI. Can recurse in the case of arrow heads.
02851     Scope:      Private
02852 
02853 ********************************************************************************************/
02854 
02855 
02856 void OSRenderRegion::RenderPath( Path *DrawPath )
02857 {
02858     /*
02859     26/9/94. Will. 
02860 
02861     Changed RFlag test, because of problems with NT bezier flattening.
02862 
02863     if (RFlags.GDI32 && (RR_DASHPATTERN().Elements == 0))
02864     {
02865         RenderPath32( DrawPath );
02866         return;
02867     }
02868     */
02869 
02870     // Flags to indicate whether fill-providers or stroke-providers have filled or stroke
02871     // the path yet.
02872     BOOL ExtendedFill = FALSE;
02873 
02874     // If this is not a simple fill, set the flag to indicate this.
02875     FillGeometryAttribute *pFillProvider = 
02876         (FillGeometryAttribute *) CurrentAttrs[ATTR_FILLGEOMETRY].pAttr;
02877 
02878     if (pFillProvider->GetRuntimeClass() != CC_RUNTIME_CLASS(FlatFillAttribute))
02879         ExtendedFill = TRUE;
02880 
02881     // we can't do fancy fills if quality is low
02882     if (RRQuality.GetFillQuality() < Quality::Graduated)
02883         ExtendedFill = FALSE;
02884 
02885     //
02886     // We ought to check for stroke-providers in here, when the great day comes that
02887     // we actually have them...
02888     //
02889     // DY - 27/11/2000 The Great Day has arrived!!! If we have an AttrStrokeType
02890     // AND an AttrVariableWidth then we don't want Gavin to mess with our line widths
02891     
02892     BOOL StrokeProvided = FALSE;
02893     StrokeTypeAttrValue* pStroke = (StrokeTypeAttrValue*) GetCurrentAttribute(ATTR_STROKETYPE);
02894     VariableWidthAttrValue* pVarWidth = (VariableWidthAttrValue*) GetCurrentAttribute(ATTR_VARWIDTH);
02895     if (pVarWidth && pStroke)
02896     {
02897         // Test that these attrs are active and not just defaults that don't do anything
02898         if (pStroke->GetPathProcessor() != NULL && pVarWidth->GetWidthFunction() != NULL)
02899             StrokeProvided = TRUE;
02900     }
02901 
02902     // Get the fill provider to fill the path - if it can't handle this sort of render
02903     // region, then we fill the path for it.
02904     if (ExtendedFill)
02905     {
02906         if (!pFillProvider->RenderFill(this, DrawPath))
02907             // Unable to render fill for this render region - default to simple fill.
02908             ExtendedFill = FALSE;
02909     }
02910 
02911     wxPen NewPen,OldPen;
02912 
02913     // can ge get Gavin to manually flatten the path? Is it worth it?
02914     // Note that if we have already had a stroke provided (above then we don't want Gavin, 
02915     // and we also don't want GDI having a go either
02916     BOOL ManualStroke = !StrokeProvided;
02917     if (ManualStroke)
02918         ManualStroke = GRenderRegion::StrokePathAvailable();
02919     
02920     if (ManualStroke)
02921     {
02922         // is it worth it for this line? Must be wider than 1 pixel and high enough quality
02923 
02924         // First, is it disabled?
02925         if (!DoBetterLines)
02926         {
02927             ManualStroke = FALSE;
02928         }
02929         // We have to stroke for Dash patterns
02930         else if ((RR_DASHPATTERN().Elements != 0) && (RR_LINEWIDTH() > 0))
02931         {
02932             // Keep manual stroke as being TRUE...
02933         }
02934         else
02935         {
02936             INT32 dpLineWidth  = MPtoLP(RR_LINEWIDTH());
02937 
02938             // If no 32-bit GDI then we have to stroke via Gavin if the line is wider than 
02939             // a few device pixels, because 16-bit GDI can't do end-caps/joins for toffee.
02940             if (dpLineWidth > 0) //(!RFlags.GDI32 && (dpLineWidth > 3))
02941             {
02942                 // Keep manual stroke as being TRUE...
02943             }
02944             // Don't bother with 0-width lines, or for very thin lines
02945             else if ((RR_LINEWIDTH() == 0) || (dpLineWidth <= 3))
02946             {
02947                 ManualStroke = FALSE;
02948             }
02949             else if (RR_STROKECOLOUR().IsTransparent() ||               // if no line, don't bother
02950                      (RRQuality.GetLineQuality() < Quality::FullLine)   // if low quality, don't bother
02951                      )
02952             {
02953                 ManualStroke = FALSE;
02954             }
02955         }
02956     }
02957 
02958     if (ManualStroke || StrokeProvided)
02959     {
02960         // if we're going to use Gavin for path stroking, or a stroke attribute has
02961         // already stroked the path then better stop GDI from outlining
02962         // the Polygons
02963 //      NewPen.CreateStockObject( NULL_PEN );
02964         OldPen = RenderDC->GetPen();
02965 
02966         // AMB thinks (15-Mar-05) the following will cause problems, because wx on GTK doesn't
02967         // like drawing with the NULL pen. Use a transparent pen instead
02968         // RenderDC->SetPen(wxNullPen);
02969         RenderDC->SetPen(*wxTRANSPARENT_PEN);
02970     }
02971 
02972     INT32 Count;
02973 
02974     // An array of the number of points in each polygon
02975     INT32 PolyCounts[MAX_POLYGONS];
02976 
02977     // Use default flattening
02978     INT32 Flatness = 0;
02979 
02980     // In all, we reduce flattening 8 times before giving up (arbitrary number)
02981     INT32 Attempts = 8;
02982 
02983     BOOL Worked = FALSE;
02984     
02985     // Keep flattening until it works (or we have tried 8 times)
02986     while (!Worked && (Attempts > 0))
02987     {
02988         Worked = RawRenderPath( NORMALPATH(DrawPath), PolyCounts, &Count, Flatness, &Flatness );
02989         Attempts--;
02990     }
02991 
02992     if (Worked)
02993     {
02994         if (Count)
02995         {
02996 #ifndef _MAC
02997             if (DrawPath->IsFilled && !ExtendedFill)
02998                 RenderDC->DrawPolyPolygon(Count,PolyCounts,PointArray,0,0,nFillStyle);
02999             else
03000 #endif
03001             {
03002                 // render the little sub-paths. Win16 doesn't have Polypolyline
03003                 wxPoint* PointList = PointArray;
03004                 INT32 *CountList = PolyCounts;
03005                 INT32 count;
03006 
03007                 while (Count--)
03008                 {
03009                     count = *CountList++;
03010                     RenderDC->DrawLines(count,PointList);
03011                     PointList += count;
03012                 }
03013             }
03014         }
03015     }
03016     else
03017     {
03018         TRACE( _T("Unable to flatten path into out buffer\n"));
03019     }
03020 
03021     // Let Gavin have a go at the lines now
03022     if (ManualStroke)
03023     {
03024         StrokeProperly( DrawPath );
03025         RenderDC->SetPen( OldPen );
03026     }
03027 }
03028 
03029 
03030 /********************************************************************************************
03031 
03032 >   BOOL OSRenderRegion::Bezier(INT32 Px0,INT32 Py0, INT32 Px1,INT32 Py1,
03033                                 INT32 Px2,INT32 Py2, INT32 Px3,INT32 Py3, 
03034                                 INT32 Flatness)
03035 
03036     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
03037     Created:    25/6/93
03038     Inputs:     Many INT32s - These are the x and y coords of the 4 bezier control points
03039                 Flatness - the flatness of the curve
03040     Returns:    TRUE if bezier was successfully flattened to specified flatness;
03041                 FALSE if buffer overflow occurred
03042     Purpose:    Flattens bezier path into an array of POINTS (for polyline).
03043     Scope:      Private
03044 
03045 ********************************************************************************************/
03046 
03047 BOOL OSRenderRegion::Bezier(INT32 Px0,INT32 Py0, INT32 Px1,INT32 Py1,
03048                             INT32 Px2,INT32 Py2, INT32 Px3,INT32 Py3, 
03049                             INT32 Flatness)
03050 {
03051     // We can't cope with negative flatness, so return if it is
03052     if (!(Flatness > 0))
03053         return FALSE;
03054 
03055     INT32 diff;
03056 
03057     INT32 dx0 = (Px1*3 - Px0*2 - Px3);
03058     dx0 = dx0 < 0 ? -dx0 : dx0;
03059 
03060     INT32 dy0 = (Py1*3 - Py0*2 - Py3);
03061     dy0 = dy0 < 0 ? -dy0 : dy0;
03062     
03063     // Get the line's distance from the curve
03064     if (dx0 >= dy0)
03065         diff = 3*dx0 + dy0;
03066     else
03067         diff = dx0 + 3*dy0;
03068         
03069     // Is the straight line close enough to the curve ?
03070     if (diff > Flatness)
03071     {
03072         // Not close enough so split it into two and recurse for each half
03073         return Split(Px0,Py0, Px1,Py1, Px2,Py2, Px3,Py3, Flatness);
03074     }
03075     
03076     INT32 dx1 = (Px2*3 - Px0 - Px3*2);
03077     dx1 = dx1 < 0 ? -dx1 : dx1;
03078 
03079     INT32 dy1 = (Py2*3 - Py0 - Py3*2);
03080     dy1 = dy1 < 0 ? -dy1 : dy1;
03081     
03082     // Get the line's distance from the curve
03083     if (dx1 >= dy1)
03084         diff = 3*dx1 + dy1;
03085     else
03086         diff = dx1 + 3*dy1;
03087 
03088     // Is the straight line close enough to the curve ?
03089     if (diff > Flatness)
03090     {
03091         // Not close enough so split it into two and recurse for each half
03092         return Split(Px0,Py0, Px1,Py1, Px2,Py2, Px3,Py3, Flatness);
03093     }
03094 
03095     if (InsertPos >= SIZEOF_POLYLINE_BUFFER)
03096         return FALSE;                                               // stop storing points
03097 
03098     // Line is now close enough so put it into our array
03099     PointArray[InsertPos].x = INT32((Px3+128)/256);
03100     PointArray[InsertPos].y = INT32((Py3+128)/256);
03101     InsertPos++;
03102 
03103     // Successfully flattened
03104     return TRUE;
03105 }
03106 
03107 /********************************************************************************************
03108 
03109 >   BOOL OSRenderRegion::Split(INT32 Px0,INT32 Py0, INT32 Px1,INT32 Py1,
03110                                INT32 Px2,INT32 Py2, INT32 Px3,INT32 Py3, 
03111                                INT32 Flatness)
03112 
03113 
03114     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
03115     Created:    25/6/93
03116     Inputs:     Many INT32s - These are the x and y coords for the 4 control points of
03117                 a bezier
03118                 Flatness - the flatness of the curve
03119     Returns:    TRUE if bezier was successfully split and flattened to specified flatness;
03120                 FALSE if buffer overflow occurred
03121     Purpose:    Splits a bezier curve into two halves, and flattens each half.
03122     Scope:      Private
03123 
03124 ********************************************************************************************/
03125 
03126 BOOL OSRenderRegion::Split(INT32 Px0,INT32 Py0, INT32 Px1,INT32 Py1,
03127                            INT32 Px2,INT32 Py2, INT32 Px3,INT32 Py3, 
03128                            INT32 Flatness)
03129 {
03130 
03131     // Calculate the first half of the curve
03132     INT32 lx0 = Px0;
03133     INT32 ly0 = Py0;
03134     INT32 lx1 = (Px0 + Px1)/2;
03135     INT32 ly1 = (Py0 + Py1)/2;
03136     INT32 lx2 = (Px0 + 2*Px1 + Px2)/4;
03137     INT32 ly2 = (Py0 + 2*Py1 + Py2)/4;
03138     INT32 lx3 = (Px0 + 3*Px1 + 3*Px2 + Px3)/8;
03139     INT32 ly3 = (Py0 + 3*Py1 + 3*Py2 + Py3)/8;
03140 
03141     // Calculate the second half of the curve
03142     INT32 rx0 = lx3;
03143     INT32 ry0 = ly3;
03144     INT32 rx1 = (Px1 + 2*Px2 + Px3)/4;
03145     INT32 ry1 = (Py1 + 2*Py2 + Py3)/4;
03146     INT32 rx2 = (Px2 + Px3)/2;
03147     INT32 ry2 = (Py2 + Py3)/2;
03148     INT32 rx3 = Px3;
03149     INT32 ry3 = Py3;
03150 
03151     if (InsertPos >= SIZEOF_POLYLINE_BUFFER)
03152         return FALSE;                                       // stop recursion when buffer fills
03153 
03154     // Recurse for first half of curve
03155     if (!Bezier(lx0,ly0, lx1,ly1, lx2,ly2, lx3,ly3, Flatness))
03156         // Failure - buffer overflow
03157         return FALSE;
03158 
03159     if (InsertPos >= SIZEOF_POLYLINE_BUFFER)
03160         return FALSE;                                       // stop recursion when buffer fills
03161 
03162     // Recurse for second half of curve
03163     return Bezier(rx0,ry0, rx1,ry1, rx2,ry2, rx3,ry3, Flatness);
03164 }
03165 
03166 
03167 
03168 /********************************************************************************************
03169 
03170 >   INT32 OSRenderRegion::CalcPathFlattening()
03171 
03172     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
03173     Created:    31/5/93
03174     Returns:    The max distance the lines can be away from the curve in MILLIPOINTS
03175     Purpose:    Adjust the bezier curve flattening according to the resolution of the
03176                 destination device and the view scale.
03177     Scope:      Private
03178 
03179 ********************************************************************************************/
03180 
03181 INT32 OSRenderRegion::CalcPathFlattening()
03182 {
03183     MILLIPOINT PixelWidth = GetScaledPixelWidth();  // The width of a pixel in DocCoords
03184     INT32 Accuracy = PixelWidth/8;                  // Accurate to 1/8 of a pixel
03185 
03186     return max(Accuracy, INT32(10));        // Ensure the value is greater than 10 MILLPOINTS
03187                                         // to avoid stack overflows.
03188 }
03189 
03190 /********************************************************************************************
03191 
03192 >   INT32 OSRenderRegion::MPtoLP(MILLIPOINT MPtoConvert)
03193 
03194     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
03195     Created:    28/5/93
03196     Inputs:     MPtoConvert is a value in MILLIPOINTS
03197     Returns:    The size in logical units
03198     Purpose:    Converts a value in MilliPoints into a GDI Logical value.
03199                 IT ASSUMES A MM_TEXT MAPPING MODE AT PRESENT.
03200     Scope:      Private
03201 
03202 ********************************************************************************************/
03203 
03204 INT32 OSRenderRegion::MPtoLP(MILLIPOINT MPtoConvert)
03205 {
03206     MILLIPOINT PixelWidth = GetScaledPixelWidth();
03207     return INT32(MPtoConvert/PixelWidth);   // Note this assumes MM_TEXT mapping mode   
03208 }
03209 
03210 /********************************************************************************************
03211 
03212 >   MILLIPOINT OSRenderRegion::CalcScaledPixelWidth()
03213 
03214     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
03215     Created:    19/7/93
03216     Returns:    The size of a pixel in MILLIPOINTS
03217     Purpose:    Calculates the size of a Pixel in MILLIPOINTS, based on the output DPI
03218                 and current scale factor.
03219     SeeAlso:    CalcPixelWidth()
03220 
03221 ********************************************************************************************/
03222 
03223 MILLIPOINT OSRenderRegion::CalcScaledPixelWidth()
03224 {
03225 //  return (RenderView->GetPixelWidth() / ScaleFactor).MakeLong();
03226     return (MILLIPOINT)( (RenderView->GetPixelWidth().MakeDouble() / ScaleFactor.MakeDouble()) + 0.5 );
03227 /*
03228     return MILLIPOINT(double(72000) /
03229             (double(RenderDC->GetDeviceCaps(LOGPIXELSX)) * ScaleFactor.MakeDouble()));
03230 */
03231 }
03232 
03233 
03234 
03235 /********************************************************************************************
03236 
03237 >   MILLIPOINT OSRenderRegion::CalcPixelWidth()
03238 
03239     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
03240     Created:    26/7/93
03241     Returns:    The size of a pixel in MILLIPOINTS
03242     Purpose:    Calculates the size of a Pixel in MILLIPOINTS, based on the output DPI
03243                 but IGNORES the scale factor.
03244     SeeAlso:    GetScaledPixelWidth()
03245 
03246 ********************************************************************************************/
03247 
03248 MILLIPOINT OSRenderRegion::CalcPixelWidth()
03249 {
03250 PORTNOTE("other","Can't handle different DPIs, using X")
03251     return MILLIPOINT(double(72000) / double(OSRenderRegion::GetFixedDCPPI(*RenderDC).x)); // x
03252 }
03253 
03254 /********************************************************************************************
03255 
03256 >   void OSRenderRegion::DrawBlob(DocCoord p, BlobType type)
03257 
03258     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
03259     Created:    23/8/93
03260     Inputs:     p - The Coord of the point to draw a blob over
03261                 type - the Type of Blob to draw. It can be either BT_SELECTED for a Selected
03262                 point or BT_UNSELECTED for an unselected point
03263     Purpose:    Draw a blob. This is the type of blob that appear round objects to show that
03264                 they have been selected.
03265     SeeAlso:    BlobType
03266 
03267 ********************************************************************************************/
03268 
03269 void OSRenderRegion::DrawBlob(DocCoord p, BlobType type)
03270 {
03271     DocRect MyBlob;
03272 
03273     GetBlobRect( ScaleFactor, p, type, &MyBlob);
03274 
03275     // I do not need to set the colour of the rect as that
03276     // should be done before this function is called
03277     // Draw Drag Rect draws a rect without shifting the sides about, so it is what
03278     // we need.
03279     DrawDragRect( &MyBlob );
03280 }
03281 
03282 
03283 
03284 /********************************************************************************************
03285 
03286 >   void OSRenderRegion::DrawBitmap(const DocCoord &Point, KernelBitmap* pBitmap)
03287 
03288     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
03289     Created:    7/2/95
03290     Inputs:     Point - The Coord specifying the bottom left of the Bitmap.
03291                 pBitmap, the KernelBitmap to plot.
03292     Purpose:    Draws an un-scaled Bitmap.
03293                 Use this for drawing Icons for dialogue boxes etc.
03294                 The Bitmap can be any size, but will be plotted unscaled.
03295                 Note that 'Point' specifies where the bottom left of the bitmap will be.
03296 
03297 ********************************************************************************************/
03298 
03299 void OSRenderRegion::DrawBitmap(const DocCoord &Point, KernelBitmap* pBitmap)
03300 {
03301     PORTNOTETRACE("other","OSRenderRegion::DrawBitmap - do nothing");
03302 #ifndef EXCLUDE_FROM_XARALX
03303     // If we are not drawing complex shapes and this shape is, then return
03304     if ((!RenderComplexShapes) && (TestForComplexShape(&Caps)))
03305         return;
03306 
03307     // Is there a Bitmap ??
03308     if (pBitmap == NULL || pBitmap->ActualBitmap == NULL)
03309     {
03310         ERROR3("NULL Bitmap passed to DrawBitmap");
03311         return;
03312     }
03313 
03314     // Get the 'Actual' windows Bitmap
03315     WinBitmap *WinBM = (WinBitmap*)pBitmap->ActualBitmap;
03316 
03317     // Is it valid ?
03318     if (
03319         (WinBM->BMInfo==NULL) ||
03320         (WinBM->BMBytes==NULL)
03321        )
03322         return;     // if not valid
03323 
03324     // Remember the Size of the Bitmap (in pixels)
03325     INT32 Width  = WinBM->GetWidth();
03326     INT32 Height = WinBM->GetHeight();
03327 
03328     // Convert the DocCoord into a windows coord
03329     POINT Origin = DocCoordToWin(Point);
03330     Origin.y = Origin.y - Height;   // Translate bottom left to top left
03331 
03332     CDC MemDC;
03333     // Create a memory DC based on this render region
03334     MemDC.CreateCompatibleDC(RenderDC);
03335 
03336     CBitmap Bitmap;
03337     // Create a CBitmap the same size as our one
03338     Bitmap.CreateCompatibleBitmap(RenderDC, Width, Height);
03339 
03340     // Select the CBitmap into the memory DC so that we have
03341     // a blank bitmap to copy into
03342     CBitmap* OldBmp = MemDC.SelectObject(&Bitmap);
03343 
03344     if (OldBmp == NULL) 
03345     {
03346         TRACE( _T("Couldn't select Bitmap into CDC in DrawBitmap\n"));
03347         return;
03348     }
03349 
03350     // Now copy the Bits from our Kernel Bitmap into the Memory DC
03351     SetDIBitsToDevice(          MemDC.m_hDC,
03352                                 0,0,
03353                                 Width,
03354                                 Height,
03355                                 0,0,
03356                                 0,
03357                                 Height,
03358                                 WinBM->BMBytes,
03359                                 WinBM->BMInfo,
03360                                 DIB_RGB_COLORS
03361                                 );
03362 
03363     // Copy the Bitmap onto this render region
03364     RenderDC->BitBlt(           Origin.x, Origin.y,
03365                                 Width,
03366                                 Height,
03367                                 &MemDC,
03368                                 0,0,
03369                                 SRCCOPY
03370                                 );
03371 
03372     // Unselect the bitmap and delete it
03373     MemDC.SelectObject(OldBmp);
03374     Bitmap.DeleteObject();
03375 #endif
03376 }
03377 
03378 
03379 
03380 /********************************************************************************************
03381 
03382 >   void OSRenderRegion::DrawBitmap(const DocCoord &Point, UINT32 BitmapID, UINT32 ToolID = NULL)
03383 
03384     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
03385     Created:    7/2/95
03386     Inputs:     Point - The Coord specifying the bottom left of the Bitmap.
03387                 BitmapID, the resource ID of the bitmap to plot.
03388                 ToolID, the optional ToolID of the tool containing the Bitmap resource.
03389     Purpose:    Draws an un-scaled Bitmap.
03390                 Use this for drawing Icons for dialogue boxes etc.
03391                 The Bitmap can be any size, but will be plotted unscaled.
03392                 Note that 'Point' specifies where the bottom left of the bitmap will be.
03393 
03394 ********************************************************************************************/
03395 
03396 void OSRenderRegion::DrawBitmap(const DocCoord &Point, UINT32 BitmapID, UINT32 ToolID)
03397 {
03398     // If we are not drawing complex shapes and this shape is, then return
03399     if ((!RenderComplexShapes) && (TestForComplexShape(&Caps)))
03400         return;
03401 
03402     wxBitmap * pBitmap=CamArtProvider::Get()->FindBitmap((ResourceID)BitmapID);
03403 
03404     if (!pBitmap)
03405         return;
03406 
03407     // Extract the Width and Height
03408     // INT32 Width  = pBitmap->GetWidth();
03409     INT32 Height = pBitmap->GetHeight();
03410 
03411     // Convert the DocCoord into a windows coord
03412     wxPoint Origin = DocCoordToWin(Point);
03413     Origin.y = Origin.y - Height;   // Translate bottom left to top left
03414 
03415     RenderDC->DrawBitmap(*pBitmap, Origin.x, Origin.y, TRUE);
03416 
03417 }
03418 
03419 BOOL OSRenderRegion::DrawTransformedBitmap(NodeBitmap *pNodeBitmap)
03420 {
03421     // If we are not printing, then we'll always render as a bitmap fill
03422     // (eg. Gallery items).
03423     if (IsPrinting())
03424     {
03425         // If we are not drawing complex shapes and this shape is, then return
03426         if ((!RenderComplexShapes) && (TestForComplexShape(&Caps)))
03427             return TRUE;
03428 
03429         if ((!RenderComplexShapes) && (pNodeBitmap->NeedsTransparency()))
03430             return TRUE;
03431 
03432         // If we've not got a valid colour plate or it is disabled
03433         if (CurrentColContext->GetColourPlate() == NULL ||
03434             CurrentColContext->GetColourPlate()->IsDisabled())
03435         {
03436             // If it is a simple rectangular shape then 
03437             // we can use GDI to render it
03438             if (pNodeBitmap->HasSimpleOrientation(this))
03439             {                            
03440                 // Get hold of the coord array for the bitmap.
03441                 DocCoord *Coords = pNodeBitmap->InkPath.GetCoordArray();
03442 
03443                 // Work out where to render the bitmap on the device context - find top left corner
03444                 OilCoord TopLeft(Coords[0].x, Coords[0].y);
03445                 RenderMatrix.transform(&TopLeft);
03446                 WinCoord DestTopLeft = TopLeft.ToWin(RenderView);
03447 
03448                 // Find required size of bitmap on device context.
03449                 MILLIPOINT Width  = Coords[1].x - Coords[0].x;
03450                 MILLIPOINT Height = Coords[0].y - Coords[3].y;
03451 
03452                 FIXED16 fxPixelSize = 0;
03453                 RenderView->GetScaledPixelSize(&fxPixelSize, &fxPixelSize);
03454                 double dPixelSize = fxPixelSize.MakeDouble();
03455                 INT32 DestWidth = (INT32) ((((double) Width) / dPixelSize) + 0.5);
03456                 INT32 DestHeight = (INT32) ((((double) Height) / dPixelSize) + 0.5);
03457 
03458                 // Get handle to bitmap (must be a WinBitmap as we are in winoil!)
03459                 CWxBitmap *WxBM = (CWxBitmap *) pNodeBitmap->GetBitmap()->ActualBitmap;
03460 
03461                 wxImage *pwxImage=WxBM->MakewxImage(DestWidth, DestHeight);
03462 
03463                 if (pwxImage)
03464                 {
03465                     wxBitmap TheBitmap(*pwxImage);
03466                     RenderDC->DrawBitmap(TheBitmap, DestTopLeft.x, DestTopLeft.y, TRUE);
03467                     delete pwxImage;
03468                 }
03469             }
03470         }
03471     }
03472 
03473     // We can't do arbitrarily transformed bitmaps - use a bitmap fill.
03474     RenderComplexShapes = TRUE;
03475     BOOL bOk = RenderRegion::DrawTransformedBitmap(pNodeBitmap);
03476     RenderComplexShapes = FALSE;
03477 
03478     return bOk;
03479 }
03480 
03481 /********************************************************************************************
03482 
03483 >   void OSRenderRegion::DrawBitmapBlob(const DocCoord &Point, KernelBitmap* BlobShape)
03484 
03485     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
03486     Created:    31/8/94
03487     Inputs:     Point - The Coord specifying the centre of the Blob.
03488                 BlobShape - A Kernel Bitmap describing the Shape of the Blob.
03489                             This is assumed to be a simple Monochrome Bitmap.
03490     Purpose:    Draws a Bitmap Blob. It is used for drawing more complex blob shapes, like
03491                 'rotation' arrows etc.
03492                 The Bitmap can be any size, but must be Monochrome.
03493                 Black pixels are transparent.  White pixels will EOR.
03494                 Note that 'Point' currently specifies where the centre of the bitmap will be.
03495     SeeAlso:    KernelBitmap
03496 
03497 ********************************************************************************************/
03498 
03499 void OSRenderRegion::DrawBitmapBlob(const DocCoord &Point, KernelBitmap* BlobShape)
03500 {
03501     PORTNOTETRACE("other","OSRenderRegion::DrawBitmapBlob - do nothing");
03502 #ifndef EXCLUDE_FROM_XARALX
03503     ENSURE(BlobShape != NULL, "NULL Bitmap passed to DrawBitmapBlob");
03504 
03505     // Is there a Bitmap ??
03506     if (BlobShape->ActualBitmap == NULL)
03507         return;     // if no bitmap
03508 
03509     // Get the 'Actual' windows Bitmap
03510     WinBitmap *WinBM = (WinBitmap*)BlobShape->ActualBitmap;
03511 
03512     // Is it valid ?
03513     if (
03514         (WinBM->BMInfo==NULL) ||
03515         (WinBM->BMBytes==NULL)
03516        )
03517         return;     // if not valid
03518 
03519     // Remember the Size of the Bitmap (in pixels)
03520     INT32 Width  = WinBM->BMInfo->bmiHeader.biWidth;
03521     INT32 Height = WinBM->BMInfo->bmiHeader.biHeight;
03522 
03523     // Convert the DocCoord into a windows coord
03524     POINT Origin = FindBitmapOrigin(Point, Width, Height);
03525 
03526     CDC MemDC;
03527     // Create a memory DC based on this render region
03528     MemDC.CreateCompatibleDC(RenderDC);
03529 
03530     CBitmap Bitmap;
03531     // Create a CBitmap the same size as our one
03532     Bitmap.CreateCompatibleBitmap(RenderDC, Width, Height);
03533 
03534     // Select the CBitmap into the memory DC so that we have
03535     // a blank bitmap to copy into
03536     CBitmap* OldBmp = MemDC.SelectObject(&Bitmap);
03537 
03538     if (OldBmp == NULL) 
03539     {
03540         TRACE( _T("Couldn't select Bitmap into CDC in DrawBitmapBlob\n"));
03541         return;
03542     }
03543 
03544     // Now copy the Bits from our Kernel Bitmap into
03545     // the Memory DC
03546     SetDIBitsToDevice(          MemDC.m_hDC,
03547                                 0,0,
03548                                 Width,
03549                                 Height,
03550                                 0,0,
03551                                 0,
03552                                 Height,
03553                                 WinBM->BMBytes,
03554                                 WinBM->BMInfo,
03555                                 DIB_RGB_COLORS
03556                                 );
03557 
03558     // EOR the Bitmap onto this render region
03559     RenderDC->BitBlt(           Origin.x, Origin.y,
03560                                 Width,
03561                                 Height,
03562                                 &MemDC,
03563                                 0,0,
03564                                 SRCINVERT
03565                                 );
03566 
03567     // Unselect the bitmap and delete it
03568     MemDC.SelectObject(OldBmp);
03569     Bitmap.DeleteObject();
03570 #endif
03571 }
03572 
03573 /********************************************************************************************
03574 
03575 >   void OSRenderRegion::DrawBitmapBlob(const DocCoord &Point, 
03576                                             UINT32 BitmapID, UINT32 ToolID = NULL)
03577     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
03578     Created:    1/9/94
03579     Inputs:     Point - The Coord specifying the centre of the Blob.
03580                 BitmapID - The Resource ID of the bitmap to use.
03581                 ToolID - The Tool to fetch the bitmap from (or NULL for Kernel).
03582     Purpose:    Draws a Bitmap Blob. It is used for drawing more complex blob shapes, like
03583                 'rotation' arrows etc.
03584                 The Bitmap can be any size, but must be Monochrome.
03585                 Black pixels are transparent.  White pixels will EOR.
03586                 Note that 'Point' currently specifies where the centre of the bitmap will be.
03587     SeeAlso:    -
03588 
03589 ********************************************************************************************/
03590 
03591 void OSRenderRegion::DrawBitmapBlob( const DocCoord &Point, ResourceID resID )
03592 {
03593     wxBitmap *pBitmap = (CamArtProvider::Get())->FindBitmap( resID );
03594     if( NULL == pBitmap ) 
03595     {   
03596         TRACE( _T("wxBitmap failed to create in DrawBitmapBlob\n"));
03597         return;
03598     }
03599 
03600     // Extract the Width and Height
03601     INT32 Width  = pBitmap->GetWidth();
03602     INT32 Height = pBitmap->GetHeight();
03603 
03604     // Convert the DocCoord into a windows coord
03605     wxPoint Origin = FindBitmapOrigin(Point, Width, Height);
03606 
03607     // EOR the bitmap onto this render region
03608     wxMemoryDC  memDc;
03609     memDc.SelectObject( *pBitmap );
03610     RenderDC->Blit( Origin.x, Origin.y, Width, Height, &memDc, 0, 0, wxXOR );
03611 }
03612 
03613 /********************************************************************************************
03614 
03615 >   POINT OSRenderRegion::FindBitmapOrigin(DocCoord Centre, INT32 Width, INT32 Height)
03616 
03617     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
03618     Created:    1/9/94
03619     Inputs:     Centre - The DocCoord specifying the centre of the Bitmap.
03620                 Width & Height of the bitmap in Pixels.
03621     Purpose:    Finds the windows origin to plot a bitmap at, so that it's centre will be
03622                 at the DocCoord specified.
03623 
03624                 Rewritten 20/06/95 by Phil: This routine now uses high-res coords until the
03625                 final return command so that the centre of the bitmap can be matched to the
03626                 centre of the pixel at the specified point. This allows these bitmap plots
03627                 to be centered accurately using the same rules as thin lines and grid points.
03628 
03629                 Karim 11/09/2000
03630                 This method no longer uses GetScaledPixelWidth() or an integer pixel size,
03631                 as these are inaccurate at high magnifications. Instead, it calculates its
03632                 own floating-point pixel width, and converts to integer arithmetic as late
03633                 as possible, for max. accuracy.
03634 
03635     Scope:      Protected
03636     SeeAlso:    -
03637 
03638 ********************************************************************************************/
03639 
03640 wxPoint OSRenderRegion::FindBitmapOrigin(DocCoord Centre, INT32 Width, INT32 Height)
03641 {
03642     DocCoord Origin;
03643     double PelSize;
03644 
03645     DocView *pDocView = DocView::GetSelected();
03646     ERROR3IF(pDocView == NULL, "OSRenderRegion::FindBitmapOrigin() when no view is selected!");
03647     if (pDocView)
03648         PelSize = (pDocView->GetPixelWidth() / pDocView->GetViewScale()).MakeDouble();
03649     else
03650         PelSize = 750;
03651 
03652     // Subract half the Width and Height
03653     Origin.x = Centre.x - (MILLIPOINT)(0.5 + (((double)Width * PelSize)/2));
03654     Origin.y = Centre.y + (MILLIPOINT)(0.5 + (((double)Height * PelSize)/2));
03655     
03656     return DocCoordToWin(Origin);
03657 }
03658 
03659 /********************************************************************************************
03660 
03661 >   static void OSRenderRegion::GetBlobRect( FIXED16 Scale, const DocCoord& BlobPoint, BlobType bType, DocRect *pResult )
03662 
03663     Author:     Andy_Pennell (Xara Group Ltd) <camelotdev@xara.com>
03664     Created:    14/11/93
03665     Inputs:     Scale - scale of view, normally DocView->GetViewScale()
03666                 BlobPoint - The Coord of the centre of the blob
03667                 bType - the Type of Blob to draw. It can be either BT_SELECTED for a Selected
03668                 point or BT_UNSELECTED for an unselected point or BT_CLICKME for the click
03669                 rectangle or BT_SELECTEDLARGEST for the larger of the two selected options.
03670     Outputs:    pResult - pointer to DocRect that will contain result
03671     Purpose:    To determine the DocRect of a blob without access to a render region
03672 
03673                 Karim 22/05/2000 - added BT_CLIPVIEW blobtype, which lets me return the
03674                 correct size for ClipView ToolObject blobs.
03675 
03676     SeeAlso:    BlobType
03677     Scope:      Public
03678 
03679 ********************************************************************************************/
03680 
03681 void OSRenderRegion::GetBlobRect( FIXED16 Scale, const DocCoord& BlobPoint, BlobType bType, DocRect *pResult )
03682 {
03683     // ideally we would call GetScaledPixelWidth(), but that is a member fn of
03684     // OSRenderRegion as we don't have a render region, so we do similar code but
03685     // here instead. (Andy's understanding is that this only works for square pixels BTW)
03686 
03687     // Note from Tim: this function is a problem because it used to use the now defunct
03688     // OilCoord::PixelWidth() - hence we need to get a view from somewhere.  We assume
03689     // that the selected DocView is a safe view to use because:
03690     //  (a) Hopefully we only draw blobs into the selected view
03691     //  (b) All DocViews will share the same pixel size... until we port to the Mac(!)
03692     MILLIPOINT BlobSize;
03693     DocView *pDocView = DocView::GetSelected();
03694     ERROR3IF(pDocView == NULL, "OSRenderRegion::GetBlobRect() when no view is selected!");
03695 
03696     // Karim 22/06/2000 - we're gonna use a double instead. Not much accuracy
03697     // is needed for PelSize, but a MILLIPOINT gives us far too little!
03698 //  const MILLIPOINT PelSize = (pDocView->GetPixelWidth() / Scale).MakeLong();
03699     const double PelSize = (pDocView->GetPixelWidth() / Scale).MakeDouble();
03700 
03701     // All of the sizes extracted by this Switch statement are RADII.
03702     // So, for the blob to be centered correctly, the diameter of the blob will be
03703     //      PIXEL_DIAMETER = 2 * RADIUS + 1 Pixel!!!!!
03704     //
03705     switch (bType)
03706     {
03707         case BT_UNSELECTED:
03708             BlobSize = (MILLIPOINT)((double)UnSelectedBlobSize * PelSize);
03709             break;
03710 
03711         case BT_SELECTED:
03712             BlobSize = (MILLIPOINT)((double)SelectedBlobSize * PelSize);
03713             break;
03714 
03715         case BT_CLICKME:
03716             BlobSize = (MILLIPOINT)((double)HitTestRadius * PelSize);
03717             break;
03718 
03719         case BT_SELECTEDLARGEST:
03720             BlobSize = max( (MILLIPOINT)((double)UnSelectedBlobSize * PelSize),
03721                             (MILLIPOINT)((double)SelectedBlobSize * PelSize) );
03722             break;
03723 
03724         case BT_MSTAGEFILLUNSELECTED:
03725             BlobSize = (MILLIPOINT)((double)MultiStageUnSelectedBlobSize * PelSize);
03726             break;
03727 
03728         case BT_MSTAGEFILLSELECTED:
03729             BlobSize = (MILLIPOINT)((double)MultiStageSelectedBlobSize * PelSize);
03730             break;
03731 
03732         case BT_MSTAGESELECTEDLARGEST:
03733             BlobSize = max( (MILLIPOINT)((double)MultiStageSelectedBlobSize * PelSize),
03734                             (MILLIPOINT)((double)MultiStageUnSelectedBlobSize * PelSize) );
03735             break;
03736 
03737         case BT_CLIPVIEW:
03738             BlobSize = (MILLIPOINT)((double)ClipViewBlobSize * PelSize);
03739             break;
03740 
03741         default:
03742             ENSURE( FALSE, "GetBlobRect() was called with an invalid BlobType" );
03743             BlobSize = 0;                           // so we get an empty rectangle
03744             break;
03745     }
03746 
03747     // Build the rectangle of the appropriate size and centre it on the DocCoord given
03748     pResult->lo.x = BlobPoint.x - BlobSize - (MILLIPOINT)(PelSize/2.0);
03749     pResult->lo.y = BlobPoint.y - BlobSize - (MILLIPOINT)(PelSize/2.0);
03750     pResult->hi.x = BlobPoint.x + BlobSize + (MILLIPOINT)(PelSize/2.0);
03751     pResult->hi.y = BlobPoint.y + BlobSize + (MILLIPOINT)(PelSize/2.0);
03752 
03753 }
03754 
03755 
03756 
03757 /********************************************************************************************
03758 >   static MILLIPOINT OSRenderRegion::GetHitTestRadius(DocView *pDocView)
03759 
03760     Author:     Justin_Flude (Xara Group Ltd) <camelotdev@xara.com>
03761     Created:    13/6/94
03762     Inputs:     The DocView that we are doing hit-testing in.
03763     Outputs:    -
03764     Returns:    The hit-test sensitivity measured in millipoints.  An object with given
03765                 document coordinates is considered to be clicked upon if it lies within
03766                 this radius of the click position.
03767     Purpose:    Allows user to adjust the sensitivity of hit-testing.
03768     Errors:     -
03769     SeeAlso:    OSRenderRegion::GetBlobRect; NodeRenderableInk::FindFirstAtPoint
03770 ********************************************************************************************/
03771 
03772 MILLIPOINT OSRenderRegion::GetHitTestRadius(DocView *pDocView)
03773 
03774 {
03775     FIXED16 fxViewScale = pDocView->GetViewScale();
03776     FIXED16 fxPelSize = ((FIXED16) HitTestRadius) * pDocView->GetPixelWidth();
03777     return (MILLIPOINT) (fxPelSize / fxViewScale).MakeLong();
03778 }
03779 
03780 
03781 
03782 /********************************************************************************************
03783 
03784 >   WinCoord OSRenderRegion::DocCoordToWin(const DocCoord& DocPoint)
03785 
03786     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
03787     Created:    5/8/93
03788     Inputs:     Matrix elements and pixel scale.
03789     Returns:    A WinCoord
03790     Purpose:    Converts a DocCoord to a WinCoord. Used during rendering
03791 
03792 ********************************************************************************************/
03793 
03794 WinCoord OSRenderRegion::DocCoordToWin(const DocCoord& DocPoint)
03795 {
03796     OilCoord OilPoint;
03797 
03798     OilPoint.x = MatrixCalc( RenderMatrix.a, DocPoint.x, RenderMatrix.c, DocPoint.y) +
03799                  RenderMatrix.e;
03800 
03801     OilPoint.y = MatrixCalc( RenderMatrix.b, DocPoint.x, RenderMatrix.d, DocPoint.y) +
03802                  RenderMatrix.f;    // This was F1
03803 
03804     return OilPoint.ToWin(RenderView);
03805 }
03806 
03807 /********************************************************************************************
03808 
03809 >   Coord OSRenderRegion::DocCoordToOS256(const DocCoord& DocPoint)
03810 
03811     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
03812     Created:    5/8/93
03813     Inputs:     Matrix elements and pixel scale.
03814     Returns:    A WinCoord
03815     Purpose:    Converts a DocCoord to a OS256 Coord. Used during rendering
03816 
03817 ********************************************************************************************/
03818 
03819 Coord OSRenderRegion::DocCoordToOS256(const DocCoord& DocPoint)
03820 {
03821     OilCoord OilPoint;
03822 
03823     OilPoint.x = MatrixCalc( RenderMatrix.a, DocPoint.x, RenderMatrix.c, DocPoint.y) +
03824                  RenderMatrix.e;
03825 
03826     OilPoint.y = MatrixCalc( RenderMatrix.b, DocPoint.x, RenderMatrix.d, DocPoint.y) +
03827                  RenderMatrix.f;    // This was f1
03828 
03829     Coord OS256Point;
03830     
03831     FIXED16 PixelWidth = RenderView->GetPixelWidth();
03832     OS256Point.x = MPtoOS256(OilPoint.x, PixelWidth); 
03833 //  OS256Point.y = -(MPtoOS256(OilPoint.y, PixelWidth) + 256); 
03834 
03835 //  Above line commented out by Phil, 11/2/97
03836 //  The above "+256" is spurious (see comments for OilCoordToWin)
03837 //  Fixes a nasty printing bug.
03838     OS256Point.y = -(MPtoOS256(OilPoint.y, PixelWidth));
03839 
03840     return OS256Point;
03841 }
03842 
03843 /*********************************************************************************************
03844 
03845 >   WinRect OSRenderRegion::DocRectToWin(const Matrix &RenderMatrix, DocRect& DocRect,
03846                              INT32 leftshift, INT32 topshift, INT32 rightshift, INT32 bottomshift,
03847                              BOOL MightClip=FALSE)
03848 
03849     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
03850     Created:    8/8/93
03851     Inputs:     pView - the view to convert in.
03852                 DocRect is a rectangle on document co-ords. The various shift parameters
03853                 allow the resulting rectangle to be moved about a little, to allow for
03854                 different rendering models etc.
03855                 If MightClip is TRUE then the rectangle is clipped to fit within 16-bit
03856                 GDI constraints, if running on 16-bit GDI.
03857     Returns:    Object containing the new rectangle coordinates.                
03858     Purpose:    To convert a rectangle in Doc coordinates to a rectangle in Win coordinates.
03859     Scope:      Static
03860 
03861 **********************************************************************************************/  
03862 
03863 
03864 WinRect OSRenderRegion::DocRectToWin(View *pView,
03865                                      const Matrix &RenderMatrix, 
03866                                      const DocRect& DRect,
03867                                      INT32 leftshift, INT32 topshift, 
03868                                      INT32 rightshift, INT32 bottomshift,
03869                                      BOOL MightClip)
03870 {
03871     WinRect WinRect;
03872 
03873     // Don't do ERROR2 as this is in redraw code.
03874     ERROR3IF(pView == NULL, "NULL view in OSRenderRegion::DocRectToWin()");
03875 
03876     OilCoord OilLo;
03877     OilCoord OilHi;
03878 
03879     OilLo.x = MatrixCalc( RenderMatrix.a, DRect.lo.x, RenderMatrix.c, DRect.lo.y ) + RenderMatrix.e;
03880     OilLo.y = MatrixCalc( RenderMatrix.b, DRect.lo.x, RenderMatrix.d, DRect.lo.y ) + RenderMatrix.f;    // This was f1
03881     OilHi.x = MatrixCalc( RenderMatrix.a, DRect.hi.x, RenderMatrix.c, DRect.hi.y ) + RenderMatrix.e;
03882     OilHi.y = MatrixCalc( RenderMatrix.b, DRect.hi.x, RenderMatrix.d, DRect.hi.y ) + RenderMatrix.f;    // this was f1
03883 
03884     // RenderMatrix can involve a rotation (but only multiples of 90 degrees), so we swap
03885     // the corners here if necessary
03886     MILLIPOINT Temp;
03887 
03888     if (OilLo.x > OilHi.x)
03889     {
03890         Temp = OilLo.x;
03891         OilLo.x = OilHi.x;
03892         OilHi.x = Temp;
03893     }
03894 
03895     if (OilLo.y > OilHi.y)
03896     {
03897         Temp = OilLo.y;
03898         OilLo.y = OilHi.y;
03899         OilHi.y = Temp;
03900     }
03901 
03902     WinCoord WinLo;
03903     WinCoord WinHi;
03904 
03905     WinLo = OilLo.ToWin(pView);
03906     WinHi = OilHi.ToWin(pView);
03907 
03908     WinRect.x      = WinLo.x + leftshift;
03909     WinRect.y      = WinHi.y + topshift;
03910     WinRect.width  = WinHi.x + rightshift -WinRect.x;
03911     WinRect.height = WinLo.y + bottomshift-WinRect.y;
03912 
03913 PORTNOTE("other", "Check this use of WIN32")
03914     if (
03915         MightClip
03916 //#if WIN32
03917 //       && IsWin32s()
03918 //#endif
03919        )
03920     {
03921         // assume MM_TEXT, clip to limits without being too close else we might effect
03922         // the actual rendering (e.g. if a line crosses the clip rect boundary)
03923         wxRect MaxRect(-100, -100, 32100, 32100 );
03924         WinRect.Intersect(MaxRect);
03925     }
03926 
03927     return WinRect;
03928 }
03929 
03930 
03931 /*********************************************************************************************
03932 
03933 >   WinRect OSRenderRegion::DocRectToWin(const Matrix &RenderMatrix, DocRect& DocRect,
03934                              const double dpi)
03935 
03936     Author:     Neville_Humphrys (Xara Group Ltd) <camelotdev@xara.com>
03937     Created:    30/11/94
03938     Inputs:     DocRect is a rectangle on document co-ords.
03939                 dpi is the resolution of the device we are rendering to
03940     Returns:    Object containing the new rectangle coordinates.                
03941     Purpose:    To convert a rectangle in Doc coordinates to a rectangle in Win coordinates
03942                 taking account of the destination dpi rather than assuming screen dpi as the
03943                 above form does. Does not need the extra parameters that the above uses.
03944                 INT32 dpi changed to double dpi (12/12/95) to improve the range of values allowed
03945                 at the < 1000dpi settings that we will be using.
03946     Scope:      Static
03947 
03948 **********************************************************************************************/  
03949 
03950 
03951 WinRect OSRenderRegion::DocRectToWin(const Matrix &RenderMatrix, const DocRect& DRect,
03952                                      const double dpi)
03953 {
03954     WinRect WinRect;
03955 
03956     OilCoord OilLo;
03957     OilCoord OilHi;
03958 
03959     OilLo.x = MatrixCalc( RenderMatrix.a, DRect.lo.x, RenderMatrix.c, DRect.lo.y ) + RenderMatrix.e;
03960     OilLo.y = MatrixCalc( RenderMatrix.b, DRect.lo.x, RenderMatrix.d, DRect.lo.y ) + RenderMatrix.f;    // This was f1
03961     OilHi.x = MatrixCalc( RenderMatrix.a, DRect.hi.x, RenderMatrix.c, DRect.hi.y ) + RenderMatrix.e;
03962     OilHi.y = MatrixCalc( RenderMatrix.b, DRect.hi.x, RenderMatrix.d, DRect.hi.y ) + RenderMatrix.f;    // this was f1
03963 
03964     // RenderMatrix can involve a rotation (but only multiples of 90 degrees), so we swap
03965     // the corners here if necessary
03966     MILLIPOINT Temp;
03967 
03968     if (OilLo.x > OilHi.x)
03969     {
03970         Temp = OilLo.x;
03971         OilLo.x = OilHi.x;
03972         OilHi.x = Temp;
03973     }
03974 
03975     if (OilLo.y > OilHi.y)
03976     {
03977         Temp = OilLo.y;
03978         OilLo.y = OilHi.y;
03979         OilHi.y = Temp;
03980     }
03981 
03982     WinCoord WinLo;
03983     WinCoord WinHi;
03984 
03985     WinLo = OilLo.ToWin(dpi);
03986     WinHi = OilHi.ToWin(dpi);
03987 
03988     WinRect.x      = WinLo.x;
03989     WinRect.y      = WinHi.y;
03990     WinRect.width  = WinHi.x-WinRect.x;
03991     WinRect.height = WinLo.y-WinRect.y;
03992 
03993     return WinRect;
03994 }
03995 
03996 
03997 
03998 
03999 /*********************************************************************************************
04000 
04001 >   DocRect OSRenderRegion::WinRectToDoc(const Matrix &RenderMatrix,
04002                                          WinRect& WRect,
04003                                          const double dpi)
04004 
04005     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
04006     Created:    31/08/2004
04007     Inputs:     WinRect is a rectangle in Windows co-ords.
04008                 dpi is the resolution of the device we are rendering to
04009     Returns:    Object containing the new rectangle coordinates.                
04010     Purpose:    To convert a rectangle in Windows coordinates to a rectangle in Document (spread) coordinates
04011                 taking account of the destination dpi. Does not need the extra parameters that the above uses.
04012     Scope:      Static
04013 
04014 **********************************************************************************************/  
04015 
04016 DocRect OSRenderRegion::WinRectToDoc(const Matrix &RenderMatrix, const WinRect& WRect,
04017                                      const double dpi)
04018 {
04019     DocRect dr;
04020 
04021     WinCoord WinLo(WRect.x, WRect.GetBottomEx());
04022     WinCoord WinHi(WRect.GetRightEx(), WRect.y);
04023     OilCoord OilLo = OilCoord(  LongMulFixed16(WinLo.x, 72000L / dpi),
04024                                 -LongMulFixed16(WinLo.y, 72000L / dpi)
04025                                 );
04026     OilCoord OilHi = OilCoord(  LongMulFixed16(WinHi.x, 72000L / dpi),
04027                                 -LongMulFixed16(WinHi.y, 72000L / dpi)
04028                                 );
04029 
04030     Matrix inv = RenderMatrix.Inverse();
04031 
04032     dr.lo.x = MatrixCalc( inv.a, OilLo.x, inv.c, OilLo.y ) + inv.e;
04033     dr.lo.y = MatrixCalc( inv.b, OilLo.x, inv.d, OilLo.y ) + inv.f;
04034     dr.hi.x = MatrixCalc( inv.a, OilHi.x, inv.c, OilHi.y ) + inv.e;
04035     dr.hi.y = MatrixCalc( inv.b, OilHi.x, inv.d, OilHi.y ) + inv.f;
04036 
04037     // RenderMatrix can involve a rotation (but only multiples of 90 degrees), so we swap
04038     // the corners here if necessary
04039     MILLIPOINT Temp;
04040 
04041     if (dr.lo.x > dr.hi.x)
04042     {
04043         Temp = dr.lo.x;
04044         dr.lo.x = dr.hi.x;
04045         dr.hi.x = Temp;
04046     }
04047 
04048     if (dr.lo.y > dr.hi.y)
04049     {
04050         Temp = dr.lo.y;
04051         dr.lo.y = dr.hi.y;
04052         dr.hi.y = Temp;
04053     }
04054 
04055     return dr;
04056 }
04057 
04058 
04059 
04060 
04061 /*********************************************************************************************
04062 
04063 >   WinRect OSRenderRegion::DocRectToWin(const Matrix &RenderMatrix, DocRect& DocRect,
04064                              const double dpi)
04065 
04066     Author:     Neville_Humphrys (Xara Group Ltd) <camelotdev@xara.com> and Gavin
04067     Created:    4/10/96
04068     Inputs:     DocRect is a rectangle on document co-ords.
04069                 dpi is the resolution of the device we are rendering to
04070     Returns:    Object containing the new rectangle coordinates.                
04071     Purpose:    To convert a rectangle in Doc coordinates to a rectangle in Win coordinates
04072                 taking account of the destination dpi rather than assuming screen dpi as the
04073                 above form does.
04074                 Same as above but tries to create the rectangle better to try and help pixel
04075                 problems.
04076     Scope:      Static
04077 
04078 **********************************************************************************************/  
04079 
04080 
04081 WinRect OSRenderRegion::BitmapDocRectToWin(Matrix &RenderMatrix, const DocRect& DRect,
04082                                            const double dpi)
04083 {
04084     // This is a version from Gavin, which should improve things
04085     OilRect OilRec;
04086     OilRec.lo.x = MatrixCalc( RenderMatrix.a, DRect.lo.x, RenderMatrix.c, DRect.lo.y ) + RenderMatrix.e;
04087     OilRec.lo.y = MatrixCalc( RenderMatrix.b, DRect.lo.x, RenderMatrix.d, DRect.lo.y ) + RenderMatrix.f;
04088     OilRec.hi.x = MatrixCalc( RenderMatrix.a, DRect.hi.x, RenderMatrix.c, DRect.hi.y ) + RenderMatrix.e;
04089     OilRec.hi.y = MatrixCalc( RenderMatrix.b, DRect.hi.x, RenderMatrix.d, DRect.hi.y ) + RenderMatrix.f;
04090 
04091     if (OilRec.lo.x > OilRec.hi.x)
04092     {
04093         MILLIPOINT Temp = OilRec.lo.x;
04094         OilRec.lo.x = OilRec.hi.x;
04095         OilRec.hi.x = Temp;
04096     }
04097     if (OilRec.lo.y > OilRec.hi.y)
04098     {
04099         MILLIPOINT Temp = OilRec.lo.y;
04100         OilRec.lo.y = OilRec.hi.y;
04101         OilRec.hi.y = Temp;
04102     }
04103 
04104     OilCoord OilSize(OilRec.hi.x-OilRec.lo.x, OilRec.hi.y-OilRec.lo.y);
04105 
04106 #if 0
04107     //
04108     // Note that this relies on ToWin performing the calculation
04109     //      +round(X*dpi/72000), -round(Y*dpi/72000)
04110     //
04111     WinCoord WinSize = OilSize.ToWin(dpi) ;         // This negates the Y.
04112 #else
04113     //
04114     // The following performs the same calculation as ToWin. Doing it here ensures
04115     // that the subsequent calculation of NewOilSize will be compatible.
04116     //
04117     WinCoord    WinSize( +(INT32)floor(OilSize.x*dpi/72000.0+0.5), -(INT32)floor(OilSize.y*dpi/72000.0+0.5) ) ;
04118 #endif
04119     // This should be the bitmap size.
04120 
04121     //
04122     // WinSize should now be the size of the export bitmap.
04123     // NewRenderMatrix.x and NewRenderMatrix.y should be the e and f
04124     // components of the new render matrix.
04125     //
04126 
04127     OilCoord OilLo = OilCoord(OilRec.lo.x,OilRec.lo.y);
04128     WinCoord WinLo = OilLo.ToWin(dpi);
04129 
04130     WinRect WinRectangle;
04131     WinRectangle.x      = WinLo.x;
04132     WinRectangle.y      = WinLo.y + WinSize.y;
04133     WinRectangle.width  = WinLo.x + WinSize.x-WinRectangle.x;
04134     WinRectangle.height = WinLo.y-WinRectangle.y;
04135 
04136     return WinRectangle;
04137 }
04138 
04139 /********************************************************************************************
04140 
04141 >   static BOOL OSRenderRegion::CalculateGMatrixOffsetsWinRect( const Matrix& RenderMatrix,
04142                                                                 const DocRect& docrect,
04143                                                                 const double dpi,
04144                                                                 GMATRIX* GMat,
04145                                                                 BOOL bCentralise
04146                                                                 double* pdXCentralAdjust
04147                                                                 double* pdYCentralAdjust)
04148 
04149     Author:     Neville_Humphrys (Xara Group Ltd) <camelotdev@xara.com> and Gavin
04150     Created:    4/10/96
04151     Inputs:     DocRect is a rectangle on document co-ords.
04152                 dpi is the resolution of the device we are rendering to
04153                 RenderMatrix is the rendering matrix
04154     Outputs:    An updated version of the Gavin matrix
04155     Returns:    Object containing the new rectangle coordinates.                
04156     Purpose:    To add in the offsets to the Gavin matrix.
04157                 This is used when exporting bitmaps. There used to a lot of pixel alignements
04158                 problems whereby on a rectangle with line colour of say 0.5pt width, you would
04159                 seemingly randomly loose one or more of the lines on the exported bitmap.
04160                 This is an attempt to fix this for the bitmap export case only, as other render
04161                 regions have their own problems and perculiarities.
04162     SeeAlso:    OSRenderRegion::DocRectToWin;
04163 
04164 ********************************************************************************************/
04165 
04166 BOOL OSRenderRegion::CalculateGavinOffsetsWinRect( const Matrix& RenderMatrix,
04167                                                    const DocRect& DRect,
04168                                                    const double dpi,
04169                                                    GMATRIX* GMat,
04170                                                    BOOL bCentralise,
04171                                                    double* pdXCentralAdjust,
04172                                                    double* pdYCentralAdjust)
04173 {
04174     OilRect OilRec ;
04175     OilRec.lo.x = MatrixCalc( RenderMatrix.a, DRect.lo.x, RenderMatrix.c, DRect.lo.y ) + RenderMatrix.e ;
04176     OilRec.lo.y = MatrixCalc( RenderMatrix.b, DRect.lo.x, RenderMatrix.d, DRect.lo.y ) + RenderMatrix.f ;
04177     OilRec.hi.x = MatrixCalc( RenderMatrix.a, DRect.hi.x, RenderMatrix.c, DRect.hi.y ) + RenderMatrix.e ;
04178     OilRec.hi.y = MatrixCalc( RenderMatrix.b, DRect.hi.x, RenderMatrix.d, DRect.hi.y ) + RenderMatrix.f ;
04179 
04180     if (OilRec.lo.x > OilRec.hi.x)
04181     {
04182         MILLIPOINT Temp = OilRec.lo.x ;
04183         OilRec.lo.x = OilRec.hi.x ;
04184         OilRec.hi.x = Temp ;
04185     }
04186     if (OilRec.lo.y > OilRec.hi.y)
04187     {
04188         MILLIPOINT Temp = OilRec.lo.y ;
04189         OilRec.lo.y = OilRec.hi.y ;
04190         OilRec.hi.y = Temp ;
04191     }
04192     OilCoord OilSize( OilRec.hi.x-OilRec.lo.x, OilRec.hi.y-OilRec.lo.y ) ;
04193 
04194     /*  double OilXOffset = (NewOilSize.x-OilSize.x)/2.0-OilRec.lo.x ;
04195     double OilYOffset = (NewOilSize.y-OilSize.y)/2.0-OilRec.lo.y ;
04196     double Scale = (dpi/72000.0)*(1<<16)*(1<<FX) ;
04197     double WinXOffset = OilXOffset*Scale ;
04198     double WinYOffset = OilYOffset*Scale ;
04199 */
04200 
04201     double Scale = (dpi/72000.0)*(1<<16)*(1<<FX) ;
04202     double WinXOffset = -OilRec.lo.x * Scale;
04203     double WinYOffset = -OilRec.lo.y * Scale;
04204 
04205     if (bCentralise)
04206     {
04207         //
04208         // The following performs the same calculation as ToWin. Doing it here ensures
04209         // that the subsequent calculation of NewOilSize will be compatible.
04210         //
04211         WinCoord    WinSize( +(INT32)floor(OilSize.x*dpi/72000.0+0.5), -(INT32)floor(OilSize.y*dpi/72000.0+0.5) ) ;
04212         OilCoord NewOilSize( +(INT32)floor(WinSize.x*72000.0/dpi+0.5), -(INT32)floor(WinSize.y*72000.0/dpi+0.5) ) ;
04213 
04214         if (pdXCentralAdjust) *pdXCentralAdjust = ((NewOilSize.x-OilSize.x)/2.0) * Scale;
04215         if (pdYCentralAdjust) *pdYCentralAdjust = ((NewOilSize.y-OilSize.y)/2.0) * Scale;
04216 
04217     }
04218 
04219     // Adjust offsets either by calculated value or supplied value
04220     if (pdXCentralAdjust) WinXOffset += *pdXCentralAdjust;
04221     if (pdYCentralAdjust) WinYOffset += *pdYCentralAdjust;
04222 
04223     //
04224     // The matrix is modified so that the drawing is centred within the export bitmap.
04225     //
04226     GMat->CX = MakeXLong(WinXOffset) ;
04227     GMat->CY = MakeXLong(WinYOffset) ;
04228 
04229     return TRUE;
04230 }
04231 
04232 /********************************************************************************************
04233 ********************************************************************************************/
04234 
04235 
04236 /********************************************************************************************
04237 
04238 >   BOOL OSRenderRegion::NewPolyDraw( const POINT* PointList, const BYTE* VerbList, INT32 NumCoords)
04239 
04240     Author:     Andy_Pennell (Xara Group Ltd) <camelotdev@xara.com>
04241     Created:    19/4/94
04242     Inputs:     A list of points (in GDI coords), a list of PT_ verbs and a count.
04243     Outputs:    -
04244     Errors:     -
04245     Returns:    TRUE if worked, FALSE if failed
04246     Purpose:    Like PolyDraw but works on Chicago. Does not work on Win32s.
04247     Scope:      Protected
04248 
04249 ********************************************************************************************/
04250 /*
04251 BOOL OSRenderRegion::NewPolyDraw( const POINT* PointList, const BYTE* VerbList, INT32 NumCoords)
04252 {
04253     if (IsWin32NT())
04254     {
04255         // NT can do PolyDraw, which expects our path structure exactly
04256         return PolyDraw( RenderDC, PointList, VerbList, NumCoords );
04257     }
04258     else
04259     {
04260         // Chicago is damn stupid and cannot do it, so we whip through it manually
04261         BOOL Worked = TRUE;
04262 
04263         while (NumCoords && Worked)
04264         {
04265             switch (*VerbList++ & ~PT_CLOSEFIGURE)
04266             {
04267                 case PT_MOVETO:
04268                     Worked = MoveToEx( RenderDC, PointList->x, PointList->y, NULL );
04269                     PointList++;
04270                     NumCoords--;
04271                     break;
04272 
04273                 case PT_LINETO:
04274                     Worked = LineTo( RenderDC, PointList->x, PointList->y );
04275                     PointList++;
04276                     NumCoords--;
04277                     break;
04278 
04279                 case PT_BEZIERTO:
04280                     Worked = PolyBezierTo( RenderDC, PointList, 3 );
04281                     PointList += 3;
04282                     VerbList += (3-1);                          // as 1 taken away above
04283                     NumCoords -= 3;
04284                     break;
04285 
04286                 default:
04287                     ENSURE(FALSE, "Bad verb in path");
04288                     PointList++;
04289                     NumCoords--;
04290                     break;
04291             }
04292             ENSURE( NumCoords>=0, "Messed up verb list");
04293         }
04294         return Worked;
04295     }
04296 }
04297 */
04298 
04299 static inline INT32 GetDiagonal( const WinRect& Rectangle )
04300 {
04301     const double W = Rectangle.width;
04302     const double H = Rectangle.height;
04303     return (INT32) sqrt(W*W+H*H);
04304 }
04305 
04306 /********************************************************************************************
04307 
04308 >   BOOL OSRenderRegion::RenderBitmapFill(Path *PathToDraw, BitmapFillAttribute* Fill)
04309 
04310     Author:     Andy_Pennell (Xara Group Ltd) <camelotdev@xara.com>
04311     Created:    10/8/94
04312     Inputs:     A path to render and a bitmap fill attribute.
04313     Outputs:    -
04314     Errors:     -
04315     Returns:    TRUE if worked, FALSE if failed.
04316     Purpose:    Render bitmap fills clipped through a path. BODGED to simple plot the bitmap.
04317     Scope:      Public
04318 
04319 ********************************************************************************************/
04320 
04321 BOOL OSRenderRegion::RenderBitmapFill(Path *PathToDraw, BitmapFillAttribute* Fill)
04322 {
04323     if (Fill->GetBitmap() == NULL || Fill->GetBitmap()->ActualBitmap == NULL)
04324         return FALSE;                                   // if no bitmap
04325 
04326     // if rendering to a bitmap, return FALSE so it is rendered solid. Test should be in
04327     // caller code in RenderBitmapFillAttribute but here is OK for now.
04328     if ((RenderFlags.VeryMono) || (RFlags.Metafile))
04329         return FALSE;
04330 
04331     ENSURE( Fill->GetBitmap()->ActualBitmap->IsKindOf( CC_RUNTIME_CLASS( CWxBitmap ) ), "Strange bitmapfill");
04332 
04333 // Since the GDI cannot cope with anything other than very simple bitmaps,
04334 // we will get GDraw to render the bitmap into a screen compatible DIB, and
04335 // then get the GDI to plot that
04336 
04337     GDrawContext* GD = GRenderRegion::GetStaticDrawContext();
04338     CWxBitmap *WxBM = NULL;
04339     CWxBitmap *OrigWxBM = NULL;
04340 
04341     const INT32 bpp = Fill->GetBitmap()->GetBPP();
04342     BYTE *pGreyTable = NULL;
04343 
04344     if (Fill->GetStartColour() != NULL && Fill->GetEndColour() != NULL)
04345     {
04346         // The bitmap is contoned ... so we need to use the greyscale version
04347         WxBM = (CWxBitmap*)Fill->GetBitmap()->GetGreyscaleVersion();
04348 
04349         if (WxBM == NULL && bpp == 8)
04350         {
04351             // Aha, we're gunna do some clever palette jiggery pokery
04352             pGreyTable = Fill->GetBitmap()->ActualBitmap->GetGreyscaleTable();
04353         }
04354     }   
04355 
04356     // Now make sure that if the WinBitmap is 32 that we RENDER it into a tempory FULLY TRANSPARENT WHITE BMP.
04357     // This stops the CONVERT function later on stripping the alpha channel off producing solid, non-transparent
04358     // coloured objects on black black backgrounds! - MarkH 20/7/99.
04359     if (WxBM == NULL)
04360     {
04361         if (bpp != 32)
04362         {
04363             WxBM = (CWxBitmap*)Fill->GetBitmap()->ActualBitmap;
04364         }
04365         else
04366         {
04367             WxBM = (CWxBitmap*)CBMPBits::RenderBMPFillAttrToTransparentWhiteRect(Fill);
04368         }
04369     }
04370 
04371     if (!WxBM)              // we really should check this pointer!
04372     {
04373         return (FALSE);
04374     }
04375 
04376     if( (WxBM->BMInfo==NULL) || (WxBM->BMBytes==NULL) )
04377         return FALSE;
04378 
04379     // Work out what Tempory bitmap we need
04380 
04381 // Note we are forcing the bitmap to be a horizontal rectangular bitmap, 
04382 // as the GDI can't cope with rotating them
04383 
04384     wxPoint BottomLeft  = DocCoordToWin( Fill->StartPoint );
04385     wxPoint BottomRight = DocCoordToWin( Fill->EndPoint   );
04386     wxPoint TopLeft     = DocCoordToWin( Fill->EndPoint2  );
04387 
04388     INT32 DestWidth  = BottomRight.x - BottomLeft.x;
04389     INT32 DestHeight = BottomLeft.y  - TopLeft.y;
04390     if (DestWidth==0 || DestHeight==0)
04391     {
04392         return TRUE;
04393     }
04394 
04395     if (IsPrinting())
04396     {
04397         if (CurrentColContext->GetColourPlate() == NULL ||
04398             CurrentColContext->GetColourPlate()->IsDisabled())
04399         {
04400 PORTNOTE("other", "No StretchDIBits call in OSRenderRegion::RenderBitmapFill")
04401 #ifndef EXCLUDE_FROM_XARALX
04402             // going to a printer? - let its driver stretch it about etc
04403             const LPBITMAPINFO bmInfo = WxBM->BMInfo;
04404 
04405             #if 1
04406             // this is the correct legit code
04407             const BOOL SendDirectToPrinter = true;
04408             #else
04409             // test code
04410             const BOOL SendDirectToPrinter = RFlags.Metafile;           // only metafiles
04411             #endif
04412 
04413             if (SendDirectToPrinter)
04414             {
04415                 // pass bitmap straight to driver. Might have to band this call
04416                 // up on low-capacity machines at some point. Also does this for
04417                 // metafiles too.
04418                 StretchDIBits( RenderDC->m_hDC, 
04419                                     BottomLeft.x, TopLeft.y,                // dest XY
04420                                     DestWidth, DestHeight,                  // dest WH
04421                                     0,0,                                    // source 0,0
04422                                     bmInfo->bmiHeader.biWidth,          // source W
04423                                     bmInfo->bmiHeader.biHeight,         // source H
04424                                     WinBM->BMBytes,
04425                                     bmInfo,
04426                                     DIB_RGB_COLORS,
04427                                     SRCCOPY );
04428 
04429                 return TRUE;
04430             }
04431 #endif
04432 
04433             // if 16- or 32-bit, leave for later. We get Gavin to scale it to an output bitmap
04434             // then PlotDeepDIB it. Sadly, this requires serious amounts of RAM, so is prone
04435             // to failure. The solution would be a ScaleDeepDIB call, like PlotDeepDIB but
04436             // that can scale appropriately. Such a function would then be called here, avoiding
04437             // the need for a huge intermediate bitmap
04438         }
04439     }
04440 
04441 // Setup a default matrix (we'll do everything in pixel-speak)
04442 
04443     GMATRIX GMatrix;
04444 
04445     GMatrix.AX = 1<<(16 + FX);
04446     GMatrix.AY = 0;
04447     GMatrix.BX = 0;
04448     GMatrix.BY = 1<<(16 + FX);
04449 
04450     GMatrix.CX = 0;
04451     GMatrix.CY = 0;
04452 
04453 // Create a Tempory bitmap compatible with the output device
04454 
04455     LPBITMAPINFO TempInfo;
04456     LPBYTE TempBits;
04457     INT32 DeviceDepth;
04458 
04459     if (IsPrinting())
04460     {
04461         // if printing, always convert to 24-bit (which as Gavin can't do 24- means 32-bit)
04462         // as all drivers can handle those (source bitmap only 16- or 32-bit anyway)
04463         DeviceDepth = 32;
04464     }
04465     else
04466     {
04467 PORTNOTE("other", "Assume 24 bit output device in OSRenderRegion::RenderBitmapFill")
04468 #ifndef EXCLUDE_FROM_XARALX
04469         DeviceDepth = GetDeviceCaps( RenderDC->m_hDC, BITSPIXEL ) * 
04470                                         GetDeviceCaps( RenderDC->m_hDC, PLANES );
04471 #else
04472         DeviceDepth=24; // assume true colour
04473 #endif
04474 
04475         if (DeviceDepth ==24)
04476             DeviceDepth = 32;   // GDraw cannot plot to 24-bit bitmaps
04477     }
04478 
04479     TempInfo = AllocDIB(DestWidth, DestHeight, DeviceDepth, &TempBits);
04480 
04481     if (TempInfo==NULL)
04482     {
04483         TRACEALL( _T("Out of memory during OSRenderRegion::RenderBitmapFill") );
04484         return FALSE;
04485     }
04486 
04487 // We may need to get a palette for the DIB.
04488 
04489     UINT32 DIBPal = DIB_RGB_COLORS;
04490 
04491     if (DeviceDepth <= 8)   // We only need a palette for 256 colours or less.
04492     {
04493         DIBPal = GRenderRegion::SetPaletteEntries( TempInfo, RenderDC );
04494     }
04495 
04496 // Setup GDraw with our Tempory Bitmap and Identity Matrix
04497 
04498     GD->SetupBitmap(TempInfo->bmiHeader.biWidth,
04499                     TempInfo->bmiHeader.biHeight,
04500                     TempInfo->bmiHeader.biBitCount, 
04501                     TempBits );
04502 
04503     GD->SetMatrix( &GMatrix );
04504 
04505     COLORREF DefaultColour = 0xFFFFFFFF;
04506 
04507     // --- Colour-correct and/or Contone the bitmap as necessary
04508     RGBQUAD *Palette = NULL;
04509 
04510     // Do the colour correction. This may produce a new pointer in Palette or BitmapBits,
04511     // which we should CCFree() when we are done with it - see the end of this function
04512     ColourCorrectBitmap(Fill, WxBM->BMInfo, &Palette);
04513 
04514     // Now see if we need to muck around with the palette for the contoning
04515     if (pGreyTable != NULL)
04516     {
04517         ERROR3IF(bpp != 8, "Greytable should only be here when rendering an 8bpp bitmap");
04518         RGBQUAD *OldPalette = Palette;
04519 
04520         // Create a new palette
04521         Palette = (RGBQUAD *) CCMalloc(256 * sizeof(RGBQUAD));
04522         if (Palette == NULL)
04523         {
04524             ERROR3("No memory for palette");
04525             return FALSE;
04526         }
04527         
04528         // Copy the entries from the contone palette into the new one,
04529         // using the Grey table as a guide
04530         for (INT32 i=0; i<256; i++)
04531         {
04532             Palette[i] = OldPalette[pGreyTable[i]];
04533         }
04534 
04535         if (OldPalette != WxBM->BMInfo->bmiColors)
04536             CCFree(OldPalette);         // Don't need the contone palette any more
04537     }
04538 
04539     // If we didn't create a temporary palette, then we'll use the original bitmap
04540     if (Palette == NULL)
04541         Palette = WxBM->BMInfo->bmiColors;
04542 
04543     // Search for a transparent colour setting the Style flags if necessary...
04544     if (bpp <= 8)
04545     {
04546         INT32 NumCols;
04547         switch (bpp)
04548         {
04549             case 1:
04550                 NumCols = 2;
04551                 break;
04552 
04553             case 2:
04554                 NumCols = 4;
04555                 break;
04556 
04557             case 4:
04558                 NumCols = 16;
04559                 break;
04560 
04561             case 8:
04562                 NumCols = 256;
04563                 break;
04564 
04565             default:
04566                 NumCols = 256;
04567                 break;
04568         }
04569 
04570         for (INT32 i=0; i<NumCols; i++)
04571         {
04572             if (Palette[i].rgbReserved == 0xFF)
04573             {
04574                 RGBQUAD* TempPalette = (RGBQUAD*)CCMalloc(NumCols * sizeof(RGBQUAD));
04575                 if (TempPalette)
04576                 {
04577                     // We'll use a copy of the palette ...
04578                     memcpy(TempPalette, Palette, NumCols*sizeof(RGBQUAD));
04579 
04580                     // so we can force this entry to be white
04581                     TempPalette[i].rgbRed       = 0xFF;
04582                     TempPalette[i].rgbGreen     = 0xFF;
04583                     TempPalette[i].rgbBlue      = 0xFF;
04584 
04585                     Palette = TempPalette;
04586                     break;
04587                 }
04588             }
04589         }
04590     }
04591 
04592 // Now set the bitmap fill
04593 
04594     POINT PGram[3];
04595     PGram[0].x = 0;             PGram[0].y = 0;
04596     PGram[1].x = DestWidth;     PGram[1].y = 0;
04597     PGram[2].x = 0;             PGram[2].y = DestHeight;
04598 
04599     DWORD Style = 1;
04600     BOOL bDoBitmapFill = TRUE;
04601     BYTE* pSepTables = NULL;
04602     BGR *pCyanSepTable = NULL;
04603     BGR *pMagentaSepTable = NULL;
04604     BGR *pYellowSepTable = NULL;
04605     BGR *pBlackSepTable = NULL;
04606     BYTE *pUnderColourRemovalTable = NULL;
04607     BYTE *pBlackGenerationTable = NULL;
04608     CWxBitmap* pNewBitmap = NULL;
04609     
04610 PORTNOTE("other", "No colour separation OSRenderRegion::RenderBitmapFill")
04611 #ifndef EXCLUDE_FROM_XARALX
04612     // --- Add Separation Style bits as approriate to the current colour separation mode
04613     if (bpp > 8)    // Only needed for deep bitmaps
04614     {
04615         // If we've got a valid colour plate and it is a composite preview
04616         if (CurrentColContext->GetColourPlate() != NULL &&
04617             !CurrentColContext->GetColourPlate()->IsDisabled())
04618         {
04619             if (CurrentColContext->GetColourPlate()->GetType() == COLOURPLATE_COMPOSITE)
04620             {
04621                 // Fall through (shouldn't be used at the moment)
04622             }
04623             else if (CurrentColContext->GetColourPlate()->GetType() == COLOURPLATE_SPOT)
04624             {
04625                 GD->SetColour(0xFFFFFF);
04626                 bDoBitmapFill = FALSE;
04627             }
04628             else if (CurrentColContext->GetColourPlate()->GetType() != COLOURPLATE_NONE)
04629             {
04630                 // Create a colour separated copy of the bitmap and render that instead
04631                 pSepTables = (BYTE *) CCMalloc(5 * 256 * sizeof(BYTE));
04632                 if (pSepTables != NULL)
04633                 {
04634                     XaraCMS* lpCMSMan = GetApplication()->GetCMSManager();
04635                     String_256 PrintProfile;
04636                     if (lpCMSMan)
04637                         lpCMSMan->GetPrinterProfile(&PrintProfile);
04638                     ColourContextCMYK *cc = new ColourContextCMYK(RenderView, &PrintProfile);
04639                     if (cc->GetProfileTables(pSepTables))
04640                     {
04641                         // Make a copy of the bitmap
04642                         pNewBitmap = (CWxBitmap*)WxBM->MakeSeparatedCopy(RenderView->GetColourPlate(), pSepTables);
04643                         OrigWxBM = WxBM;                    // Save original bitmap pointer
04644                         WxBM = pNewBitmap;                  // Use this bitmap instead
04645                     }
04646                     delete cc;
04647                 }
04648             }
04649         }
04650     }
04651 #endif
04652 
04653     // Set the context to the default values
04654 
04655     GD->SetDefaultBitmapParameters();
04656 
04657     GD->SetTileSmoothingFlag(TRUE/*FALSE*/);
04658     GD->SetTileFilteringFlag(TRUE/*FALSE*/);
04659 
04660     if (bDoBitmapFill)
04661     {
04662         GD->SetBitmapFill(  &(WxBM->BMInfo->bmiHeader),
04663                             WxBM->BMBytes,
04664                             Style,
04665                             PGram,
04666                             DefaultColour,
04667                             Palette,
04668                             NULL, NULL, NULL,
04669                             NULL,
04670                             0
04671                             );
04672     }
04673 
04674 // Now plot a filled rectangle
04675 
04676     RECT BmpRect;
04677     BmpRect.left    =  0;
04678     BmpRect.top     =  DestHeight;
04679     BmpRect.right   =  DestWidth;
04680     BmpRect.bottom  =  0;
04681 
04682     GD->FillRectangle(&BmpRect);
04683 
04684 
04685     if (IsPrinting())
04686     {
04687         // we are going to the printer. We had a 16- or 32-bit DIB which we have now converted
04688         // into a 32-bit one, so send it out
04689 
04690         // PlotDeepDIB ends up doing a SetDIBitsToDevice in 24-bit slices
04691         DIBUtil::PlotDeepDIB( RenderDC, TempInfo, TempBits, 
04692                             BottomLeft.x, TopLeft.y,
04693                             DestWidth,DestHeight,
04694                             0,0, 
04695                             CONVHINT_PRINTER );
04696     }
04697     else
04698     {
04699         // get the HPALETTE to pass to the plot bitmap call - crucial for quality Win32s DDB plotting
04700         wxPalette * hPal = NULL;
04701         if (RFlags.UsePalette)
04702             hPal = PaletteManager::GetPalette();
04703 
04704         // Finally call PlotBitmap, to render the DIB using the GDI.
04705         // If the screen cannot cope with the depth of the bitmap, it will be converted
04706         // for us. Isn't PlotBitmap wonderful?
04707 
04708         GRenderRegion::StaticPlotBitmap( RenderDC, 
04709                                 DIBPal, 
04710                                 TempInfo, 
04711                                 TempBits,
04712                                 BottomLeft.x, 
04713                                 TopLeft.y, 
04714                                 DestWidth, 
04715                                 DestHeight, 
04716                                 hPal,
04717                                 0,0
04718                              );
04719     }
04720 
04721     FreeDIB(TempInfo, TempBits);
04722 
04723     if (pNewBitmap)
04724     {
04725         WxBM = OrigWxBM;
04726         delete pNewBitmap;
04727     }
04728 
04729     // Free any memory used for colour-corrected bitmap palettes.
04730     // If this pointer doesn't point at the original palette, then it has
04731     // been temporarily allocated by ColourCorrectBitmap, above.
04732     if (Palette != WxBM->BMInfo->bmiColors)
04733         CCFree(Palette);
04734     
04735     if (pSepTables)
04736     {
04737         GD->SetSeparationTables();  // Defaults to setting everything to NULL
04738         CCFree(pSepTables);
04739     }
04740 
04741     if (pCyanSepTable)
04742         CCFree(pCyanSepTable);
04743     if (pMagentaSepTable)
04744         CCFree(pMagentaSepTable);
04745     if (pYellowSepTable)
04746         CCFree(pYellowSepTable);
04747     if (pBlackSepTable)
04748         CCFree(pBlackSepTable);
04749     if (pUnderColourRemovalTable)
04750         CCFree(pUnderColourRemovalTable);
04751     if (pBlackGenerationTable)
04752         CCFree(pBlackGenerationTable);
04753 
04754     return TRUE;
04755 }
04756 
04757 
04758 MILLIPOINT OSRenderRegion::CalcDistance(DocCoord a, DocCoord b)
04759 {
04760     const double Width = (double) a.x - b.x;
04761     const double Height = (double) a.y - b.y;
04762     double Diag = Width * Width + Height * Height;
04763     const INT32 Distance = (INT32) sqrt(Diag);
04764     return Distance;
04765 }
04766 
04767 void OSRenderRegion::MakeEllipticalPath(Path *pPath, DocCoord Parallel[4])
04768 {
04769     // Get an array to put the 12 different coords needed to specify an ellipse
04770     DocCoord NewCoords[12];
04771 
04772     // Calculate the 3 coordinates along each side of the parallelogram
04773     NodeEllipse::CalcEllipseEdge(Parallel[0], Parallel[1], &NewCoords[11], &NewCoords[0], &NewCoords[1]);
04774     NodeEllipse::CalcEllipseEdge(Parallel[1], Parallel[2], &NewCoords[2], &NewCoords[3], &NewCoords[4]);
04775     NodeEllipse::CalcEllipseEdge(Parallel[2], Parallel[3], &NewCoords[5], &NewCoords[6], &NewCoords[7]);
04776     NodeEllipse::CalcEllipseEdge(Parallel[3], Parallel[0], &NewCoords[8], &NewCoords[9], &NewCoords[10]);
04777 
04778     // build a path
04779     pPath->ClearPath();
04780     pPath->FindStartOfPath();
04781 
04782     // Start at bottom left corner
04783     PathFlags NewFlags;
04784     NewFlags.IsRotate = TRUE;
04785     pPath->InsertMoveTo(NewCoords[0], &NewFlags);
04786     pPath->InsertCurveTo(NewCoords[1], NewCoords[2], NewCoords[3], &NewFlags);
04787     pPath->InsertCurveTo(NewCoords[4], NewCoords[5], NewCoords[6], &NewFlags);
04788     pPath->InsertCurveTo(NewCoords[7], NewCoords[8], NewCoords[9], &NewFlags);
04789     pPath->InsertCurveTo(NewCoords[10], NewCoords[11], NewCoords[0], &NewFlags);
04790 
04791     // Close the path properly
04792     pPath->CloseSubPath();
04793 }
04794 
04795 /********************************************************************************************
04796 
04797 >   BOOL OSRenderRegion::RenderGradFillPath(Path *PathToDraw, GradFillAttribute* Fill)
04798 
04799     Author:     Andy_Pennell (Xara Group Ltd) <camelotdev@xara.com>
04800     Created:    19/4/94
04801     Inputs:     A path to render and a fill attribute.
04802     Outputs:    -
04803     Errors:     -
04804     Returns:    TRUE if worked, FALSE if failed.
04805     Purpose:    Render grad fills to GDI devices using whatever means available.
04806     Scope:      Public
04807 
04808 ********************************************************************************************/
04809 
04810 BOOL OSRenderRegion::RenderGradFillPath(Path *PathToDraw, GradFillAttribute* Fill)
04811 {
04812     CCRuntimeClass *FillType = Fill->GetRuntimeClass();
04813 
04814     if (FillType == CC_RUNTIME_CLASS(RadialFillAttribute))
04815         return RenderRadialFill(PathToDraw, (RadialFillAttribute *) Fill);
04816     else if (FillType == CC_RUNTIME_CLASS(LinearFillAttribute))
04817         return RenderLinearFill(PathToDraw, (LinearFillAttribute *) Fill); 
04818     else if (FillType == CC_RUNTIME_CLASS(ConicalFillAttribute))
04819         return RenderConicalFill(PathToDraw, (ConicalFillAttribute *) Fill); 
04820     else if (FillType == CC_RUNTIME_CLASS(SquareFillAttribute))
04821         return RenderSquareFill(PathToDraw, (SquareFillAttribute *) Fill); 
04822     else if (FillType == CC_RUNTIME_CLASS(FourColFillAttribute))
04823         return RenderFourColFill(PathToDraw, (FourColFillAttribute *) Fill); 
04824     else
04825         return FALSE;
04826 }
04827 
04828 
04829 
04830 /********************************************************************************************
04831 
04832 >   static INT32 ColDifference(DocColour &Start, DocColour &End, INT32 Depth, EFFECTTYPE EffectType)
04833 
04834     Author:     Andy_Pennell (Xara Group Ltd) <camelotdev@xara.com>? Rik? Tim?
04835     Created:    ?
04836     Inputs:     Start, End - The Start & end colours for the fill
04837                 EffectType - The fill type (EFFECT_RGB, EFFECT_HSV_SHORT, EFFECT_HSV_LONG)
04838 
04839     Returns:    The number of gradient steps you should generate when rendering this fill
04840 
04841     Purpose:    To determine the number of grad fill steps that will be necessary to render
04842                 the given graduation to a reasonable quality
04843 
04844     Scope:      Private static (in osrndrgn.cpp)
04845 
04846 ********************************************************************************************/
04847 
04848 static INT32 ColDifference(DocColour &Start, DocColour &End, INT32 Depth, EFFECTTYPE EffectType)
04849 {
04850     // Get RGB values of start and end colours...
04851     INT32 StartRed, StartGreen, StartBlue;
04852     Start.GetRGBValue(&StartRed, &StartGreen, &StartBlue);
04853 
04854     INT32 EndRed, EndGreen, EndBlue;
04855     End.GetRGBValue(&EndRed, &EndGreen, &EndBlue);
04856 
04857     // Find maximum difference between colours
04858     INT32 Diff = Abs(StartRed-EndRed);
04859     INT32 Tmp = StartGreen - EndGreen;
04860     Diff = max(Abs(Tmp), Diff);
04861     Tmp = StartBlue - EndBlue;
04862     Diff = max(Abs(Tmp), Diff);
04863 
04864     // Spot colours in a fill coerce the fill to a simple RGB mix
04865     if (Start.GetSpotParent() != NULL || End.GetSpotParent() != NULL)
04866         EffectType = EFFECT_RGB;
04867 
04868     // See if HSV route requires larger number of steps...
04869     if (EffectType == EFFECT_HSV_SHORT || EffectType == EFFECT_HSV_LONG)
04870     {
04871         // Get an HSV context (quite fast - simple array lookup)
04872         ColourContext *pContext = ColourContext::GetGlobalDefault(COLOURMODEL_HSVT);
04873         ERROR3IF(pContext == NULL, "No HSV context?!");
04874 
04875         ColourHSVT StartDef;
04876         pContext->ConvertColour(&Start, (ColourGeneric *) &StartDef);
04877 
04878         ColourHSVT EndDef;
04879         pContext->ConvertColour(&End, (ColourGeneric *) &EndDef);
04880 
04881         // HSV blend! We can go 2 ways, as HSV can 'wrap' from 1.0 back to 0.0
04882         BOOL BlendNormally = TRUE;
04883 
04884         // Calc. the "simple" (non-wrapping) distance between the hues
04885         const double StartHue   = StartDef.Hue.MakeDouble();
04886         const double EndHue     = EndDef.Hue.MakeDouble();
04887 
04888         double SimpleDist = StartHue - EndHue;
04889         if (SimpleDist < 0.0)
04890             SimpleDist = -SimpleDist;
04891 
04892         // Determine whether we do a simple blend, or we have to "wrap"
04893         if (SimpleDist <= 0.5)
04894             BlendNormally = !(EffectType == EFFECT_HSV_LONG);
04895         else
04896             BlendNormally = (EffectType == EFFECT_HSV_LONG);
04897 
04898         // Convert to 9 bit value
04899         INT32 HSVDiff = (INT32) (360.0 * SimpleDist);
04900         if (!BlendNormally)         // Go the long way around
04901             HSVDiff = 360 - HSVDiff;
04902 
04903         if (HSVDiff > Diff)         // Use this if it's larger.
04904             Diff = HSVDiff;
04905     }
04906 
04907     return((Depth >= 24) ? Diff : Diff / 3);    // Decide how many steps to do based on colour depth.
04908 }
04909 
04910 
04911 
04912 const INT32 MAX_FILL_STEPS = 128;
04913 const BOOL USE_GDI_CLIPPING = FALSE;
04914 
04915 typedef enum
04916 {
04917     GF_USE_GDICLIPPING,
04918     GF_USE_GDIEOR,
04919     GF_USE_GAVINCLIPPING
04920 } GradFillMethodType;
04921 
04922 /********************************************************************************************
04923 
04924 >   BOOL OSRenderRegion::RenderRadialFill(Path *PathToDraw, RadialFillAttribute *Fill)
04925 
04926     Author:     Andy_Pennell (Xara Group Ltd) <camelotdev@xara.com>? Rik? Tim?
04927     Created:    ?
04928     Inputs:     PathToDraw  - A pointer to the path to be rendered.
04929                 Fill        - A pointer to the radial fill applied.
04930     Returns:    TRUE        - Success.
04931                 FALSE       - An error occured.
04932     Purpose:    Renders the path with a gradient fill.
04933 
04934 ********************************************************************************************/
04935 
04936 BOOL OSRenderRegion::RenderRadialFill(Path *PathToDraw, RadialFillAttribute *Fill)
04937 {
04938     GradFillMethodType RenderMethod = GF_USE_GDICLIPPING;
04939 
04940     INT32 Radius1 = CalcDistance(Fill->StartPoint, Fill->EndPoint);
04941     INT32 Radius2 = CalcDistance(Fill->StartPoint, Fill->EndPoint2);
04942 
04943     // We want to change the rendering context, so we save it.
04944     SaveContext();
04945 
04946     // No stroking while rendering the fill.
04947     SetLineColour(COLOUR_TRANS);
04948 
04949     // Use device pixel size to work out how many steps to do.
04950     FIXED16 PixelSize = 0;
04951     GetRenderView()->GetScaledPixelSize(&PixelSize,&PixelSize);
04952     INT32 FillSteps = INT32( max( Radius1, Radius2 ) / PixelSize.MakeDouble() );
04953 
04954     // Do a slice every 4 pixels (arbitrary!)
04955     FillSteps /= 4;
04956 
04957     // Also base it on distance between colours
04958     EFFECTTYPE EffectType = GetFillEffect();
04959     FillSteps = min(ColDifference(Fill->Colour, Fill->EndColour, 
04960                                   IsPrinting() ? 24 : 8,    // Colour depth
04961                                   EffectType ), 
04962                                   FillSteps);
04963 
04964     // Limit fill steps to a maximum
04965     FillSteps = min(FillSteps, INT32(MAX_FILL_STEPS));
04966 
04967     // Use global Quality setting
04968     FillSteps = FillSteps / (1 << GradFillQuality);
04969     FillSteps = max(FillSteps, 10);
04970 
04971     // Work out what flatness and accuracy to use for Gavin's clipping functions.
04972     // Gavin recommends 1/2 pixel for flattening, and 1/4 pixel for tolerance
04973     // (but these were kind of off the top of his head - Tim).
04974     // PS. It seems we need maximum tolerance (i.e. 0) for accurate grad-fill rendering.
04975     INT32 ClipFlatness = PixelSize.MakeLong() / 16;
04976     INT32 ClipTolerance = 0;
04977 
04978     RGBQUAD ColourSteps[MAX_FILL_STEPS];
04979     GradTable32::BuildGraduatedPalette(Fill->Colour, Fill->EndColour,
04980                                         GetRenderView(), EffectType,
04981                                         FillSteps, ColourSteps);
04982 
04983     // If we are rendering into a metafile, clip the path ourselves
04984     if (RFlags.Metafile)
04985     {
04986         RenderMethod = GF_USE_GAVINCLIPPING;
04987     }
04988     else
04989     {
04990         if (!SetClipToPathTemporary(PathToDraw))
04991         {
04992             // Can't use clipping - fall back to EOR.
04993             RenderMethod = GF_USE_GDIEOR;
04994         }
04995     }
04996 
04997     INT32 RenderCount = 1;
04998 
04999     if (RenderMethod == GF_USE_GDIEOR)
05000     {
05001         SetDrawingMode(DM_EORDITHER);
05002         RenderCount = 2;
05003     }
05004 
05005     DocColour TempFillColour;
05006 
05007     while (RenderCount > 0)
05008     {
05009         COLORREF CurrentCol = RGB(ColourSteps[FillSteps - 1].rgbRed, 
05010                                     ColourSteps[FillSteps - 1].rgbGreen, 
05011                                     ColourSteps[FillSteps - 1].rgbBlue);
05012 
05013         // Put the RGB value back into a DocColour so we can set it as the fill colour. We set it
05014         // as non-separable, as it will have already been colour-separated by BuildGraduatedPalette
05015         TempFillColour.SetRGBValue(ColourSteps[FillSteps - 1].rgbRed, 
05016                                     ColourSteps[FillSteps - 1].rgbGreen, 
05017                                     ColourSteps[FillSteps - 1].rgbBlue);
05018         TempFillColour.SetSeparable(FALSE);
05019 
05020         SetFillColour(TempFillColour);
05021 
05022         GetValidBrush();
05023         GetValidPen();
05024 
05025         // First, fill in whole path with end colour
05026         //RenderPath(PathToDraw);
05027         Path PreviousPath;
05028         PreviousPath.Initialise(PathToDraw->GetNumCoords() + 2, 12);
05029         PreviousPath.CopyPathDataFrom(PathToDraw);
05030 
05031         // Work out relative positions of start and end points.
05032         DocCoord Steps[2];
05033         Steps[0].x = Fill->StartPoint.x - Fill->EndPoint.x;
05034         Steps[0].y = Fill->StartPoint.y - Fill->EndPoint.y;
05035         Steps[1].x = Fill->StartPoint.x - Fill->EndPoint2.x;
05036         Steps[1].y = Fill->StartPoint.y - Fill->EndPoint2.y;
05037 
05038         // Construct the parallelogram that describes the biggest ellipse.
05039         // (If I could draw a picture here, I would!)
05040         DocCoord Parallel[4];
05041         Parallel[0].x = Fill->StartPoint.x + (Steps[0].x + Steps[1].x);
05042         Parallel[0].y = Fill->StartPoint.y + (Steps[0].y + Steps[1].y);
05043         Parallel[1].x = Fill->StartPoint.x + (Steps[0].x - Steps[1].x);
05044         Parallel[1].y = Fill->StartPoint.y + (Steps[0].y - Steps[1].y);
05045         Parallel[2].x = Fill->StartPoint.x - (Steps[0].x + Steps[1].x);
05046         Parallel[2].y = Fill->StartPoint.y - (Steps[0].y + Steps[1].y);
05047         Parallel[3].x = Fill->StartPoint.x + (Steps[1].x - Steps[0].x);
05048         Parallel[3].y = Fill->StartPoint.y + (Steps[1].y - Steps[0].y);
05049 
05050         // Now convert the steps into the steps we should use to adjust the
05051         // parallelogram after each fill step
05052         Steps[0].x /= (FillSteps + 1);
05053         Steps[0].y /= (FillSteps + 1);
05054         Steps[1].x /= (FillSteps + 1);
05055         Steps[1].y /= (FillSteps + 1);
05056 
05057         // This are the paths we will use to create radial fills.
05058         Path RadialFill;
05059         RadialFill.Initialise(50,12);
05060         Path EllipsePath;
05061         EllipsePath.Initialise(15,12);
05062 
05063         // This is the path we use for all clipped rendering.
05064         Path ClippedPath;
05065         ClippedPath.Initialise(12, 12);
05066 
05067 
05068         // Now render the fill using concentric ellipses, starting with the biggest.
05069         for (INT32 i = FillSteps; i > 0;)
05070         {
05071             MakeEllipticalPath(&EllipsePath, Parallel);
05072             EllipsePath.IsFilled = TRUE;
05073 
05074             // Use this and the previous path to make an elliptical disc.
05075             RadialFill.ClearPath(FALSE);
05076             RadialFill.MakeSpaceInPath(PreviousPath.GetNumCoords() + EllipsePath.GetNumCoords() + 2);
05077             RadialFill.CopyPathDataFrom(&PreviousPath);
05078             RadialFill.IsFilled = TRUE;
05079             if (i > 1)
05080             {
05081                 // For all except the last step, make a complex path (which is
05082                 // usually an elliptical disc).
05083                 ERROR2IF(!RadialFill.MergeTwoPaths(EllipsePath), FALSE, "could not merge paths");
05084             }
05085 
05086             // Remember the elliptical path for next time
05087             PreviousPath.ClearPath(FALSE);
05088             PreviousPath.MakeSpaceInPath(EllipsePath.GetNumCoords() + 2);
05089             PreviousPath.CopyPathDataFrom(&EllipsePath);
05090 
05091             if (RenderMethod != GF_USE_GAVINCLIPPING)
05092             {
05093                 RenderPath(&RadialFill);
05094             }
05095             else
05096             {
05097                 PathToDraw->ClipPathToPath(RadialFill, &ClippedPath, 2,
05098                                            ClipTolerance, ClipFlatness, ClipFlatness);
05099                 ClippedPath.IsFilled = TRUE;
05100                 RenderPath(&ClippedPath);
05101             }
05102 
05103             // decrement loop counter here so that the next colour can be calculated
05104             i--;
05105 
05106             if (i > 0)
05107             {
05108                 // Calculate next brush (except at the end of the loop)
05109                 const COLORREF NewFill = RGB(ColourSteps[i].rgbRed, ColourSteps[i].rgbGreen,
05110                                                 ColourSteps[i].rgbBlue);
05111                 if (NewFill != CurrentCol)
05112                 {
05113                     // colour changed, make new brush
05114                     CurrentCol = NewFill;
05115 
05116                     // Put the RGB value back into a DocColour so we can set it as the fill colour. We set it
05117                     // as non-separable, as it will have already been colour-separated by BuildGraduatedPalette
05118                     TempFillColour.SetRGBValue(ColourSteps[i].rgbRed, 
05119                                                 ColourSteps[i].rgbGreen, 
05120                                                 ColourSteps[i].rgbBlue);
05121                     TempFillColour.SetSeparable(FALSE);
05122 
05123                     SetFillColour(TempFillColour);
05124                     GetValidBrush();
05125                     GetValidPen();
05126                 }
05127 
05128                 // Reduce size of paralleogram
05129                 Parallel[0].x -= (Steps[0].x + Steps[1].x);
05130                 Parallel[0].y -= (Steps[0].y + Steps[1].y);
05131                 Parallel[1].x -= (Steps[0].x - Steps[1].x);
05132                 Parallel[1].y -= (Steps[0].y - Steps[1].y);
05133                 Parallel[2].x += (Steps[0].x + Steps[1].x);
05134                 Parallel[2].y += (Steps[0].y + Steps[1].y);
05135                 Parallel[3].x -= (Steps[1].x - Steps[0].x);
05136                 Parallel[3].y -= (Steps[1].y - Steps[0].y);
05137             }
05138         }
05139 
05140         if ((RenderMethod == GF_USE_GDIEOR) && (RenderCount > 1))
05141         {
05142             // Render the path in white
05143             SetDrawingMode(DM_COPYPEN);
05144             SetFillColour(COLOUR_BLACK);
05145             SetLineColour(COLOUR_TRANS);
05146             GetValidBrush();
05147             GetValidPen();
05148             RenderPath(PathToDraw);
05149 
05150             // Set back to mode required for grad fill rendering
05151             SetDrawingMode(DM_EORDITHER);
05152         }
05153 
05154         RenderCount--;
05155     }
05156 
05157     // now tidy up
05158     RestoreContext();
05159     GetValidBrush();
05160     GetValidPen();
05161 
05162     // Lose the clipping region if we are using one.
05163     if (RenderMethod == GF_USE_GDICLIPPING)
05164         RenderDC->SetClippingRegion( *OSClipRegion );
05165 
05166     return TRUE;
05167 }
05168 
05169 /********************************************************************************************
05170 
05171 >   BOOL OSRenderRegion::RenderLinearFill(Path *PathToDraw, LinearFillAttribute *Fill)
05172 
05173     Author:     Andy_Pennell (Xara Group Ltd) <camelotdev@xara.com>? Rik? Tim?
05174     Created:    ?
05175     Inputs:     PathToDraw  - A pointer to the path to be rendered.
05176                 Fill        - A pointer to the radial fill applied.
05177     Returns:    TRUE        - Success.
05178                 FALSE       - An error occured.
05179     Purpose:    Renders the path with a gradient fill.
05180 
05181 ********************************************************************************************/
05182 
05183 BOOL OSRenderRegion::RenderLinearFill(Path *PathToDraw, LinearFillAttribute *Fill)
05184 {
05185     GradFillMethodType RenderMethod = GF_USE_GDICLIPPING;
05186 
05187     // First, we need to find out the minimum width of rectangles we can
05188     // plot and still fill the entire shape.
05189     // To do this we find the maxiumum distance between the start point and all four
05190     // corners of the bounding box.
05191     // NB. This actually gives us *half* the minimum width we need.
05192     DocRect BoundsRect = PathToDraw->GetBoundingRect();
05193 
05194     DocCoord TestPoint = BoundsRect.lo;
05195     INT32 Distance = CalcDistance(Fill->StartPoint, TestPoint);
05196     TestPoint.x = BoundsRect.hi.x;
05197     INT32 NewDistance = CalcDistance(Fill->StartPoint, TestPoint);
05198     Distance = max(Distance, NewDistance);
05199     TestPoint = BoundsRect.hi;
05200     NewDistance = CalcDistance(Fill->StartPoint, TestPoint);
05201     Distance = max(Distance, NewDistance);
05202     TestPoint.x = BoundsRect.lo.x;
05203     NewDistance = CalcDistance(Fill->StartPoint, TestPoint);
05204     Distance = max(Distance, NewDistance);
05205 
05206     // Distance now holds the minimum distance either side of the line that we should
05207     // render.
05208 
05209     // We want to change the rendering context, so we save it.
05210     SaveContext();
05211 
05212     // No stroking while rendering the fill.
05213     SetLineColour(COLOUR_TRANS);
05214 
05215     // Use device pixel size to work out how many steps to do.
05216     FIXED16 PixelSize = 0;
05217     GetRenderView()->GetScaledPixelSize(&PixelSize,&PixelSize);
05218 
05219     // Work out how long the fill arrow is in millipoints.
05220     INT32 FillLength = CalcDistance(Fill->StartPoint, Fill->EndPoint);
05221 
05222     // Do a slice every 4 pixels (arbitrary!)
05223     INT32 FillSteps = (INT32) ((FillLength / PixelSize.MakeDouble()) / 4);
05224 
05225     // Also base it on distance between colours
05226     EFFECTTYPE EffectType = GetFillEffect();
05227     FillSteps = min(ColDifference(Fill->Colour, Fill->EndColour,
05228                                   IsPrinting() ? 24 : 8,                // Colour depth
05229                                   EffectType), 
05230                                   FillSteps);
05231 
05232     // Limit fill steps to a maximum
05233     FillSteps = min( FillSteps, INT32(MAX_FILL_STEPS) );
05234 
05235     // Use global Quality setting
05236     FillSteps = FillSteps / (1 << GradFillQuality);
05237 
05238     FillSteps = max(FillSteps, 10);
05239 
05240     // Work out what flatness and accuracy to use for Gavin's clipping functions.
05241     // Gavin recommends 1/2 pixel for flattening, and 1/4 pixel for tolerance
05242     // (but these were kind of off the top of his head - Tim).
05243     // PS. It seems we need maximum tolerance (i.e. 0) for accurate grad-fill rendering.
05244     INT32 ClipFlatness = PixelSize.MakeLong() / 16;
05245     INT32 ClipTolerance = 0;
05246 
05247     RGBQUAD ColourSteps[MAX_FILL_STEPS];
05248     GradTable32::BuildGraduatedPalette(Fill->Colour, Fill->EndColour,
05249                                         GetRenderView(), EffectType,
05250                                         FillSteps, ColourSteps);
05251     INT32 RenderCount = 1;
05252 
05253     // If we are rendering into a metafile, clip the path ourselves
05254     if (RFlags.Metafile)
05255     {
05256         RenderMethod = GF_USE_GAVINCLIPPING;
05257     }
05258     else
05259     {
05260         if (!SetClipToPathTemporary(PathToDraw))
05261         {
05262             // Can't use clipping - fall back to EOR.
05263             RenderMethod = GF_USE_GDIEOR;
05264             SetDrawingMode(DM_EORDITHER);
05265             RenderCount = 2;
05266         }
05267     }
05268 
05269     DocColour TempFillColour;
05270 
05271     while (RenderCount > 0)
05272     {
05273         COLORREF CurrentCol = RGB(ColourSteps[0].rgbRed, ColourSteps[0].rgbGreen, ColourSteps[0].rgbBlue);
05274 
05275         // Put the RGB value back into a DocColour so we can set it as the fill colour. We set it
05276         // as non-separable, as it will have already been colour-separated by BuildGraduatedPalette
05277         TempFillColour.SetRGBValue(ColourSteps[0].rgbRed, 
05278                                     ColourSteps[0].rgbGreen, 
05279                                     ColourSteps[0].rgbBlue);
05280         TempFillColour.SetSeparable(FALSE);
05281 
05282         SetFillColour(TempFillColour);
05283         GetValidBrush();
05284         GetValidPen();
05285 
05286         // This is the path we will use to create linear fills.
05287         Path LinearFill;
05288         LinearFill.Initialise(12,12);
05289 
05290         // This is the path we use for all clipped rendering.
05291         Path ClippedPath;
05292         ClippedPath.Initialise(12, 12);
05293 
05294         // Work out differences in x and y coords for fill arrow.
05295         double FillStepX = ((double) (Fill->EndPoint.x - Fill->StartPoint.x)) / (double) FillSteps;
05296         double FillStepY = ((double) (Fill->EndPoint.y - Fill->StartPoint.y)) / (double) FillSteps;
05297 
05298         double FillAngle = atan2((double)(Fill->EndPoint.x - Fill->StartPoint.x),
05299                                  (double)(Fill->EndPoint.y - Fill->StartPoint.y));
05300 
05301         // Used to find perpendicular angles.
05302         const double HalfPI = PI/2.0;
05303 
05304         DocCoord FillPoint = Fill->StartPoint;
05305         DocCoord RectPoint[2];
05306 
05307         // Find first two corners of rectangle
05308         RectPoint[0].x = FillPoint.x + (INT32) (((double) Distance) * sin(FillAngle - HalfPI));
05309         RectPoint[0].y = FillPoint.y + (INT32) (((double) Distance) * cos(FillAngle - HalfPI));
05310         RectPoint[1].x = FillPoint.x + (INT32) (((double) Distance) * sin(FillAngle + HalfPI));
05311         RectPoint[1].y = FillPoint.y + (INT32) (((double) Distance) * cos(FillAngle + HalfPI));
05312 
05313 
05314 
05315         // Do the start colour shape - we need to find out how far we need 
05316         // to extend beyond the fill arrow.
05317         TestPoint = BoundsRect.lo;
05318         INT32 LastDistance = CalcDistance(Fill->EndPoint, TestPoint);
05319         TestPoint.x = BoundsRect.hi.x;
05320         NewDistance = CalcDistance(Fill->EndPoint, TestPoint);
05321         LastDistance = max(LastDistance, NewDistance);
05322         TestPoint = BoundsRect.hi;
05323         NewDistance = CalcDistance(Fill->EndPoint, TestPoint);
05324         LastDistance = max(LastDistance, NewDistance);
05325         TestPoint.x = BoundsRect.lo.x;
05326         NewDistance = CalcDistance(Fill->EndPoint, TestPoint);
05327         LastDistance = max(LastDistance, NewDistance);
05328 
05329         // Make the last (big) rectangle.   
05330         LinearFill.ClearPath();
05331         LinearFill.FindStartOfPath();
05332 
05333         // Create a rectangle
05334         PathFlags NewFlags;
05335         NewFlags.IsRotate = TRUE;
05336 
05337         // Insert first two corners of rectangle.
05338         LinearFill.InsertMoveTo(RectPoint[0], &NewFlags);
05339         LinearFill.InsertLineTo(RectPoint[1], &NewFlags);
05340 
05341         // Move past the fill arrow far enough to cover last bit of shape.
05342         FillPoint.x = Fill->EndPoint.x - (MILLIPOINT) (LastDistance * sin(FillAngle));
05343         FillPoint.y = Fill->EndPoint.y - (MILLIPOINT) (LastDistance * cos(FillAngle));
05344 
05345         // Find other two corners of rectangle.
05346         DocCoord EndPoint[2];
05347         EndPoint[0].x = FillPoint.x + (INT32) (((double) Distance) * sin(FillAngle - HalfPI));
05348         EndPoint[0].y = FillPoint.y + (INT32) (((double) Distance) * cos(FillAngle - HalfPI));
05349         EndPoint[1].x = FillPoint.x + (INT32) (((double) Distance) * sin(FillAngle + HalfPI));
05350         EndPoint[1].y = FillPoint.y + (INT32) (((double) Distance) * cos(FillAngle + HalfPI));
05351 
05352         // Insert last two corners of rectangle.
05353         LinearFill.InsertLineTo(EndPoint[1], &NewFlags);
05354         LinearFill.InsertLineTo(EndPoint[0], &NewFlags);
05355 
05356         // Close the path properly
05357         LinearFill.CloseSubPath();
05358 
05359         LinearFill.IsFilled = TRUE;
05360         if (RenderMethod != GF_USE_GAVINCLIPPING)
05361         {
05362             RenderPath(&LinearFill);
05363         }
05364         else
05365         {
05366             PathToDraw->ClipPathToPath(LinearFill, &ClippedPath, 2,
05367                                        ClipTolerance, ClipFlatness, ClipFlatness);
05368             ClippedPath.IsFilled = TRUE;
05369             RenderPath(&ClippedPath);
05370         }
05371 
05372 
05373         // Now render the fill using rectangles.
05374         for (INT32 i = 0; i < FillSteps;)
05375         {
05376             LinearFill.ClearPath();
05377             LinearFill.FindStartOfPath();
05378 
05379             // Create a rectangle
05380             PathFlags NewFlags;
05381             NewFlags.IsRotate = TRUE;
05382 
05383             // Insert first two corners of rectangle.
05384             LinearFill.InsertMoveTo(RectPoint[0], &NewFlags);
05385             LinearFill.InsertLineTo(RectPoint[1], &NewFlags);
05386 
05387             // increment loop counter here so that the next colour can be calculated
05388             i++;
05389 
05390             // Move along the arrow one step
05391             FillPoint.x = Fill->StartPoint.x + (MILLIPOINT) (i * FillStepX);
05392             FillPoint.y = Fill->StartPoint.y + (MILLIPOINT) (i * FillStepY);
05393 
05394             // Find other two corners of rectangle.
05395             RectPoint[0].x = FillPoint.x + (INT32) (((double) Distance) * sin(FillAngle - HalfPI));
05396             RectPoint[0].y = FillPoint.y + (INT32) (((double) Distance) * cos(FillAngle - HalfPI));
05397             RectPoint[1].x = FillPoint.x + (INT32) (((double) Distance) * sin(FillAngle + HalfPI));
05398             RectPoint[1].y = FillPoint.y + (INT32) (((double) Distance) * cos(FillAngle + HalfPI));
05399 
05400             // Insert last two corners of rectangle.
05401             LinearFill.InsertLineTo(RectPoint[1], &NewFlags);
05402             LinearFill.InsertLineTo(RectPoint[0], &NewFlags);
05403 
05404             // Close the path properly
05405             LinearFill.CloseSubPath();
05406 
05407             LinearFill.IsFilled = TRUE;
05408             if (RenderMethod != GF_USE_GAVINCLIPPING)
05409             {
05410                 RenderPath(&LinearFill);
05411             }
05412             else
05413             {
05414                 PathToDraw->ClipPathToPath(LinearFill, &ClippedPath, 2,
05415                                            ClipTolerance, ClipFlatness, ClipFlatness);
05416                 ClippedPath.IsFilled = TRUE;
05417                 RenderPath(&ClippedPath);
05418             }
05419 
05420             if (i != FillSteps)
05421             {
05422                 // Calculate next brush
05423                 const COLORREF NewFill = RGB(ColourSteps[i].rgbRed, ColourSteps[i].rgbGreen,
05424                                                 ColourSteps[i].rgbBlue);
05425                 if (NewFill != CurrentCol)
05426                 {
05427                     // colour changed, make new brush
05428                     CurrentCol = NewFill;
05429 
05430                     // Put the RGB value back into a DocColour so we can set it as the fill colour. We set it
05431                     // as non-separable, as it will have already been colour-separated by BuildGraduatedPalette
05432                     TempFillColour.SetRGBValue(ColourSteps[i].rgbRed, 
05433                                                 ColourSteps[i].rgbGreen, 
05434                                                 ColourSteps[i].rgbBlue);
05435                     TempFillColour.SetSeparable(FALSE);
05436 
05437                     SetFillColour(TempFillColour);
05438                     GetValidBrush();
05439                     GetValidPen();
05440                 }
05441             }
05442         }
05443 
05444         // Do the end colour shape - we need to find out how far we need 
05445         // to extend beyond the fill arrow.
05446         TestPoint = BoundsRect.lo;
05447         LastDistance = CalcDistance(Fill->EndPoint, TestPoint);
05448         TestPoint.x = BoundsRect.hi.x;
05449         NewDistance = CalcDistance(Fill->EndPoint, TestPoint);
05450         LastDistance = max(LastDistance, NewDistance);
05451         TestPoint = BoundsRect.hi;
05452         NewDistance = CalcDistance(Fill->EndPoint, TestPoint);
05453         LastDistance = max(LastDistance, NewDistance);
05454         TestPoint.x = BoundsRect.lo.x;
05455         NewDistance = CalcDistance(Fill->EndPoint, TestPoint);
05456         LastDistance = max(LastDistance, NewDistance);
05457 
05458         // Make the last (big) rectangle.   
05459         LinearFill.ClearPath();
05460         LinearFill.FindStartOfPath();
05461 
05462         // Create a rectangle
05463         NewFlags.IsRotate = TRUE;
05464 
05465         // Insert first two corners of rectangle.
05466         LinearFill.InsertMoveTo(RectPoint[0], &NewFlags);
05467         LinearFill.InsertLineTo(RectPoint[1], &NewFlags);
05468 
05469         // Move past the fill arrow far enough to cover last bit of shape.
05470         FillPoint.x = Fill->EndPoint.x + (MILLIPOINT) (LastDistance * sin(FillAngle));
05471         FillPoint.y = Fill->EndPoint.y + (MILLIPOINT) (LastDistance * cos(FillAngle));
05472 
05473         // Find other two corners of rectangle.
05474         RectPoint[0].x = FillPoint.x + (INT32) (((double) Distance) * sin(FillAngle - HalfPI));
05475         RectPoint[0].y = FillPoint.y + (INT32) (((double) Distance) * cos(FillAngle - HalfPI));
05476         RectPoint[1].x = FillPoint.x + (INT32) (((double) Distance) * sin(FillAngle + HalfPI));
05477         RectPoint[1].y = FillPoint.y + (INT32) (((double) Distance) * cos(FillAngle + HalfPI));
05478 
05479         // Insert last two corners of rectangle.
05480         LinearFill.InsertLineTo(RectPoint[1], &NewFlags);
05481         LinearFill.InsertLineTo(RectPoint[0], &NewFlags);
05482 
05483         // Close the path properly
05484         LinearFill.CloseSubPath();
05485 
05486         LinearFill.IsFilled = TRUE;
05487         if (RenderMethod != GF_USE_GAVINCLIPPING)
05488         {
05489             RenderPath(&LinearFill);
05490         }
05491         else
05492         {
05493             PathToDraw->ClipPathToPath(LinearFill, &ClippedPath, 2,
05494                                        ClipTolerance, ClipFlatness, ClipFlatness);
05495             ClippedPath.IsFilled = TRUE;
05496             RenderPath(&ClippedPath);
05497         }
05498 
05499         if ((RenderMethod == GF_USE_GDIEOR) && (RenderCount > 1))
05500         {
05501             // Render the path in white
05502             SetDrawingMode(DM_COPYPEN);
05503             SetFillColour(COLOUR_BLACK);
05504             SetLineColour(COLOUR_TRANS);
05505             GetValidBrush();
05506             GetValidPen();
05507             RenderPath(PathToDraw);
05508 
05509             // Set back to mode required for grad fill rendering
05510             SetDrawingMode(DM_EORDITHER);
05511         }
05512 
05513         RenderCount--;
05514     }
05515 
05516     // now tidy up
05517     RestoreContext();
05518     GetValidBrush();
05519     GetValidPen();
05520 
05521     // Lose the clipping region if we are using one.
05522     if (RenderMethod == GF_USE_GDICLIPPING)
05523         RenderDC->SetClippingRegion(*OSClipRegion);
05524 
05525     return TRUE;
05526 }
05527 
05528 /********************************************************************************************
05529 
05530 >   BOOL OSRenderRegion::RenderConicalFill(Path *PathToDraw, LinearFillAttribute *Fill)
05531 
05532     Author:     Andy_Pennell (Xara Group Ltd) <camelotdev@xara.com>? Rik? Tim?
05533     Created:    ?
05534     Inputs:     PathToDraw  - A pointer to the path to be rendered.
05535                 Fill        - A pointer to the radial fill applied.
05536     Returns:    TRUE        - Success.
05537                 FALSE       - An error occured.
05538     Purpose:    Renders the path with a gradient fill.
05539 
05540 ********************************************************************************************/
05541 
05542 BOOL OSRenderRegion::RenderConicalFill(Path *PathToDraw, ConicalFillAttribute *Fill)
05543 {
05544     GradFillMethodType RenderMethod = GF_USE_GDICLIPPING;
05545 
05546     // First, we need to find out the minimum radius of the circle we can
05547     // plot and still fill the entire shape.
05548     // To do this we find the maxiumum distance between the start point and all four
05549     // corners of the bounding box.
05550     DocRect BoundsRect = PathToDraw->GetBoundingRect();
05551 
05552     DocCoord TestPoint = BoundsRect.lo;
05553     INT32 Radius = CalcDistance(Fill->StartPoint, TestPoint);
05554     TestPoint.x = BoundsRect.hi.x;
05555     INT32 NewRadius = CalcDistance(Fill->StartPoint, TestPoint);
05556     Radius = max(Radius, NewRadius);
05557     TestPoint = BoundsRect.hi;
05558     NewRadius = CalcDistance(Fill->StartPoint, TestPoint);
05559     Radius = max(Radius, NewRadius);
05560     TestPoint.x = BoundsRect.lo.x;
05561     NewRadius = CalcDistance(Fill->StartPoint, TestPoint);
05562     Radius = max(Radius, NewRadius);
05563 
05564     // Radius now holds the minimum radius we can use.
05565 
05566     // We want to change the rendering context, so we save it.
05567     SaveContext();
05568 
05569     // No stroking while rendering the fill.
05570     SetLineColour(COLOUR_TRANS);
05571 
05572     // Use device pixel size to work out how many steps to do.
05573     FIXED16 PixelSize = 0;
05574     GetRenderView()->GetScaledPixelSize(&PixelSize,&PixelSize);
05575     INT32 FillSteps = (INT32) ((Radius * 2 * PI) / PixelSize.MakeDouble());
05576 
05577     // Do a slice every 4 pixels (arbitrary!)
05578     FillSteps /= 4;
05579 
05580     // Also base it on distance between colours
05581     EFFECTTYPE EffectType = GetFillEffect();
05582     FillSteps = min(ColDifference(Fill->Colour, Fill->EndColour, 
05583                                   IsPrinting() ? 24 : 8,            // Colour depth
05584                                   EffectType), 
05585                                   FillSteps);
05586 
05587     // Limit fill steps to a maximum
05588     FillSteps = min( FillSteps, INT32(MAX_FILL_STEPS) );
05589 
05590     // Use global Quality setting
05591     FillSteps = FillSteps / (1 << GradFillQuality);
05592 
05593     // Note that with a conical fill we fade from the start colour to the end colour
05594     // and then back to the start colour, so we only need half as many fill steps as you
05595     // might think.
05596     FillSteps /= 2;
05597 
05598     // Limit to minimum of 10 steps
05599     FillSteps = max(FillSteps, 10);
05600 
05601     // Work out what flatness and accuracy to use for Gavin's clipping functions.
05602     // Gavin recommends 1/2 pixel for flattening, and 1/4 pixel for tolerance
05603     // (but these were kind of off the top of his head - Tim).
05604     // PS. It seems we need maximum tolerance (i.e. 0) for accurate grad-fill rendering.
05605     INT32 ClipFlatness = PixelSize.MakeLong() / 16;
05606     INT32 ClipTolerance = 0;
05607 
05608     RGBQUAD ColourSteps[MAX_FILL_STEPS];
05609     GradTable32::BuildGraduatedPalette(Fill->Colour, Fill->EndColour,
05610                                         GetRenderView(), EffectType,
05611                                         FillSteps, ColourSteps);
05612 
05613 
05614     // If we are rendering into a metafile, clip the path ourselves
05615     if (RFlags.Metafile)
05616     {
05617         RenderMethod = GF_USE_GAVINCLIPPING;
05618     }
05619     else
05620     {
05621         if (!SetClipToPathTemporary(PathToDraw))
05622         {
05623             // Can't use clipping - fall back to EOR.
05624             RenderMethod = GF_USE_GDIEOR;
05625         }
05626     }
05627 
05628     INT32 RenderCount = 1;
05629 
05630     if (RenderMethod == GF_USE_GDIEOR)
05631     {
05632         SetDrawingMode(DM_EORDITHER);
05633         RenderCount = 2;
05634     }
05635 
05636     DocColour TempFillColour;
05637 
05638     while (RenderCount > 0)
05639     {
05640         COLORREF CurrentCol = RGB(ColourSteps[FillSteps - 1].rgbRed, 
05641                                     ColourSteps[FillSteps - 1].rgbGreen, 
05642                                     ColourSteps[FillSteps - 1].rgbBlue);
05643 
05644         // Put the RGB value back into a DocColour so we can set it as the fill colour. We set it
05645         // as non-separable, as it will have already been colour-separated by BuildGraduatedPalette
05646         TempFillColour.SetRGBValue(ColourSteps[FillSteps - 1].rgbRed, 
05647                                     ColourSteps[FillSteps - 1].rgbGreen, 
05648                                     ColourSteps[FillSteps - 1].rgbBlue);
05649         TempFillColour.SetSeparable(FALSE);
05650 
05651         SetFillColour(TempFillColour);
05652         GetValidBrush();
05653         GetValidPen();
05654 
05655         // This is the path we will use to create conical fills.
05656         Path ConicalFill;
05657         ConicalFill.Initialise(12,12);
05658 
05659         // This is the path we use for all clipped rendering.
05660         Path ClippedPath;
05661         ClippedPath.Initialise(12, 12);
05662 
05663         double FillAngle = atan2((double)(Fill->EndPoint.x - Fill->StartPoint.x),
05664                                  (double)(Fill->EndPoint.y - Fill->StartPoint.y));
05665 
05666         double AngleInc = PI / FillSteps; // Actually (2 * PI) / (2 * FillSteps)
05667 
05668         TestPoint.x = Fill->StartPoint.x + (INT32) (((double) Radius) * sin(FillAngle));
05669         TestPoint.y = Fill->StartPoint.y + (INT32) (((double) Radius) * cos(FillAngle));
05670 
05671         // Now render the fill using radiating triangles.
05672         INT32 FillInc = -1;
05673 
05674         // NB. This loop is unusual because we loop from FillSteps down to 0 and back 
05675         // up to FillSteps - because that's how conical fills work.
05676         for (INT32 i = FillSteps - 1; i <= FillSteps; i += FillInc)
05677         {
05678             ConicalFill.ClearPath();
05679             ConicalFill.FindStartOfPath();
05680 
05681             // Create a triangle
05682             PathFlags NewFlags;
05683             NewFlags.IsRotate = TRUE;
05684             ConicalFill.InsertMoveTo(Fill->StartPoint, &NewFlags);
05685             ConicalFill.InsertLineTo(TestPoint, &NewFlags);
05686             FillAngle += AngleInc;
05687             TestPoint.x = Fill->StartPoint.x + (INT32) (((double) Radius) * sin(FillAngle));
05688             TestPoint.y = Fill->StartPoint.y + (INT32) (((double) Radius) * cos(FillAngle));
05689             ConicalFill.InsertLineTo(TestPoint, &NewFlags);
05690 
05691             // Close the path properly
05692             ConicalFill.CloseSubPath();
05693 
05694             ConicalFill.IsFilled = TRUE;
05695 
05696             if (RenderMethod != GF_USE_GAVINCLIPPING)
05697             {
05698                 RenderPath(&ConicalFill);
05699             }
05700             else
05701             {
05702                 PathToDraw->ClipPathToPath(ConicalFill, &ClippedPath, 2,
05703                                            ClipTolerance, ClipFlatness, ClipFlatness);
05704                 ClippedPath.IsFilled = TRUE;
05705                 RenderPath(&ClippedPath);
05706             }
05707 
05708             // decrement loop counter here so that the next colour can be calculated
05709 
05710             if (i <= 0)
05711                 // We need to start going the other way!
05712                 FillInc = 1;
05713 
05714             // Calculate next brush
05715             if (i != FillSteps)
05716             {
05717                 const COLORREF NewFill = RGB(ColourSteps[i].rgbRed,
05718                                                 ColourSteps[i].rgbGreen,
05719                                                 ColourSteps[i].rgbBlue);
05720                 if (NewFill != CurrentCol)
05721                 {
05722                     // colour changed, make new brush
05723                     CurrentCol = NewFill;
05724 
05725                     // Put the RGB value back into a DocColour so we can set it as the fill colour. We set it
05726                     // as non-separable, as it will have already been colour-separated by BuildGraduatedPalette
05727                     TempFillColour.SetRGBValue(ColourSteps[i].rgbRed, 
05728                                                 ColourSteps[i].rgbGreen, 
05729                                                 ColourSteps[i].rgbBlue);
05730                     TempFillColour.SetSeparable(FALSE);
05731 
05732                     SetFillColour(TempFillColour);
05733                     GetValidBrush();
05734                     GetValidPen();
05735                 }
05736             }
05737         }
05738 
05739         if ((RenderMethod == GF_USE_GDIEOR) && (RenderCount > 1))
05740         {
05741             // Render the path in white
05742             SetDrawingMode(DM_COPYPEN);
05743             SetFillColour(COLOUR_BLACK);
05744             SetLineColour(COLOUR_TRANS);
05745             GetValidBrush();
05746             GetValidPen();
05747             RenderPath(PathToDraw);
05748 
05749             // Set back to mode required for grad fill rendering
05750             SetDrawingMode(DM_EORDITHER);
05751         }
05752 
05753         RenderCount--;
05754     }
05755 
05756     // now tidy up
05757     RestoreContext();
05758     GetValidBrush();
05759     GetValidPen();
05760 
05761     // Lose the clipping region if we are using one.
05762     if (RenderMethod == GF_USE_GDICLIPPING)
05763         RenderDC->SetClippingRegion(*OSClipRegion);
05764 
05765     return TRUE;
05766 }
05767 
05768 /********************************************************************************************
05769 
05770 >   BOOL OSRenderRegion::RenderSquareFill(Path *PathToDraw, LinearFillAttribute *Fill)
05771 
05772     Author:     Andy_Pennell (Xara Group Ltd) <camelotdev@xara.com>? Rik? Tim?
05773     Created:    ?
05774     Inputs:     PathToDraw  - A pointer to the path to be rendered.
05775                 Fill        - A pointer to the radial fill applied.
05776     Returns:    TRUE        - Success.
05777                 FALSE       - An error occured.
05778     Purpose:    Renders the path with a gradient fill.
05779 
05780 ********************************************************************************************/
05781 
05782 BOOL OSRenderRegion::RenderSquareFill(Path *PathToDraw, SquareFillAttribute *Fill)
05783 {
05784     GradFillMethodType RenderMethod = GF_USE_GDICLIPPING;
05785 
05786     INT32 Radius1 = CalcDistance(Fill->StartPoint, Fill->EndPoint);
05787     INT32 Radius2 = CalcDistance(Fill->StartPoint, Fill->EndPoint2);
05788 
05789     // We want to change the rendering context, so we save it.
05790     SaveContext();
05791 
05792     // No stroking while rendering the fill.
05793     SetLineColour(COLOUR_TRANS);
05794 
05795     // Use device pixel size to work out how many steps to do.
05796     FIXED16 PixelSize = 0;
05797     GetRenderView()->GetScaledPixelSize(&PixelSize,&PixelSize);
05798     INT32 FillSteps = (INT32) (max(Radius1, Radius2) / PixelSize.MakeDouble());
05799 
05800     // Do a slice every 4 pixels (arbitrary!)
05801     FillSteps /= 4;
05802 
05803     // Also base it on distance between colours
05804     EFFECTTYPE EffectType = GetFillEffect();
05805     FillSteps = min(ColDifference(Fill->Colour, Fill->EndColour, 
05806                                   IsPrinting() ? 24 : 8,    // Colour depth
05807                                   EffectType), 
05808                                   FillSteps);
05809 
05810     // Limit fill steps to a maximum
05811     FillSteps = min( FillSteps, INT32(MAX_FILL_STEPS) );
05812 
05813     // Use global Quality setting
05814     FillSteps = FillSteps / (1 << GradFillQuality);
05815     FillSteps = max(FillSteps, 10);
05816 
05817     // Work out what flatness and accuracy to use for Gavin's clipping functions.
05818     // Gavin recommends 1/2 pixel for flattening, and 1/4 pixel for tolerance
05819     // (but these were kind of off the top of his head - Tim).
05820     // PS. It seems we need maximum tolerance (i.e. 0) for accurate grad-fill rendering.
05821     INT32 ClipFlatness = PixelSize.MakeLong() / 16;
05822     INT32 ClipTolerance = 0;
05823 
05824     RGBQUAD ColourSteps[MAX_FILL_STEPS];
05825     GradTable32::BuildGraduatedPalette(Fill->Colour, Fill->EndColour,
05826                                         GetRenderView(), EffectType,
05827                                         FillSteps, ColourSteps);
05828 
05829     // If we are rendering into a metafile, clip the path ourselves
05830     if (RFlags.Metafile)
05831     {
05832         RenderMethod = GF_USE_GAVINCLIPPING;
05833     }
05834     else
05835     {
05836         if (!SetClipToPathTemporary(PathToDraw))
05837         {
05838             // Can't use clipping - fall back to EOR.
05839             RenderMethod = GF_USE_GDIEOR;
05840         }
05841     }
05842 
05843     INT32 RenderCount = 1;
05844 
05845     if (RenderMethod == GF_USE_GDIEOR)
05846     {
05847         SetDrawingMode(DM_EORDITHER);
05848         RenderCount = 2;
05849     }
05850 
05851     DocColour TempFillColour;
05852 
05853     while (RenderCount > 0)
05854     {
05855         COLORREF CurrentCol = RGB(ColourSteps[FillSteps - 1].rgbRed, 
05856                                     ColourSteps[FillSteps - 1].rgbGreen, 
05857                                     ColourSteps[FillSteps - 1].rgbBlue);
05858 
05859         // Put the RGB value back into a DocColour so we can set it as the fill colour. We set it
05860         // as non-separable, as it will have already been colour-separated by BuildGraduatedPalette
05861         TempFillColour.SetRGBValue(ColourSteps[FillSteps - 1].rgbRed, 
05862                                     ColourSteps[FillSteps - 1].rgbGreen, 
05863                                     ColourSteps[FillSteps - 1].rgbBlue);
05864         TempFillColour.SetSeparable(FALSE);
05865 
05866         SetFillColour(TempFillColour);
05867 
05868         GetValidBrush();
05869         GetValidPen();
05870 
05871         // First, fill in whole path with end colour
05872         //RenderPath(PathToDraw);
05873         Path PreviousPath;
05874         PreviousPath.Initialise(PathToDraw->GetNumCoords() + 2, 12);
05875         PreviousPath.CopyPathDataFrom(PathToDraw);
05876 
05877         // Work out relative positions of start and end points.
05878         DocCoord Steps[2];
05879         Steps[0].x = Fill->StartPoint.x - Fill->EndPoint.x;
05880         Steps[0].y = Fill->StartPoint.y - Fill->EndPoint.y;
05881         Steps[1].x = Fill->StartPoint.x - Fill->EndPoint2.x;
05882         Steps[1].y = Fill->StartPoint.y - Fill->EndPoint2.y;
05883 
05884         // Construct the parallelogram that describes the extent of the fill
05885         DocCoord Parallel[4];
05886         Parallel[0].x = Fill->StartPoint.x + (Steps[0].x + Steps[1].x);
05887         Parallel[0].y = Fill->StartPoint.y + (Steps[0].y + Steps[1].y);
05888         Parallel[1].x = Fill->StartPoint.x + (Steps[0].x - Steps[1].x);
05889         Parallel[1].y = Fill->StartPoint.y + (Steps[0].y - Steps[1].y);
05890         Parallel[2].x = Fill->StartPoint.x - (Steps[0].x + Steps[1].x);
05891         Parallel[2].y = Fill->StartPoint.y - (Steps[0].y + Steps[1].y);
05892         Parallel[3].x = Fill->StartPoint.x + (Steps[1].x - Steps[0].x);
05893         Parallel[3].y = Fill->StartPoint.y + (Steps[1].y - Steps[0].y);
05894 
05895         // Now convert the steps into the steps we should use to adjust the
05896         // parallelogram after each fill step
05897         Steps[0].x /= (FillSteps + 1);
05898         Steps[0].y /= (FillSteps + 1);
05899         Steps[1].x /= (FillSteps + 1);
05900         Steps[1].y /= (FillSteps + 1);
05901 
05902         // These are the paths we will use to create square fills.
05903         Path SquareFill;
05904         SquareFill.Initialise(50,12);
05905         Path ParallelogramPath;
05906         ParallelogramPath.Initialise(15,12);
05907 
05908         // This is the path we use for all clipped rendering.
05909         Path ClippedPath;
05910         ClippedPath.Initialise(12, 12);
05911 
05912         // Now render the fill using concentric parallelograms, starting with the biggest.
05913         for (INT32 i = FillSteps; i > 0;)
05914         {
05915             // build a path
05916             ParallelogramPath.ClearPath();
05917             ParallelogramPath.FindStartOfPath();
05918             ParallelogramPath.InsertMoveTo(Parallel[0]);
05919             ParallelogramPath.InsertLineTo(Parallel[1]);
05920             ParallelogramPath.InsertLineTo(Parallel[2]);
05921             ParallelogramPath.InsertLineTo(Parallel[3]);
05922             ParallelogramPath.InsertLineTo(Parallel[0]);
05923             ParallelogramPath.IsFilled = TRUE;
05924 
05925             // Use this and the previous path to make a parallelogram shaped 'washer'
05926             SquareFill.ClearPath(FALSE);
05927             SquareFill.MakeSpaceInPath(PreviousPath.GetNumCoords() + ParallelogramPath.GetNumCoords() + 2);
05928             SquareFill.CopyPathDataFrom(&PreviousPath);
05929             SquareFill.IsFilled = TRUE;
05930             if (i > 1)
05931             {
05932                 // For all except the last step, make a complex path (which is
05933                 // usually a parallelogram).
05934                 ERROR2IF(!SquareFill.MergeTwoPaths(ParallelogramPath), FALSE, "could not merge paths");
05935             }
05936 
05937             // Remember the parallelogram path for next time
05938             PreviousPath.ClearPath(FALSE);
05939             PreviousPath.MakeSpaceInPath(ParallelogramPath.GetNumCoords() + 2);
05940             PreviousPath.CopyPathDataFrom(&ParallelogramPath);
05941 
05942             if (RenderMethod != GF_USE_GAVINCLIPPING)
05943             {
05944                 RenderPath(&SquareFill);
05945             }
05946             else
05947             {
05948                 PathToDraw->ClipPathToPath(SquareFill, &ClippedPath, 2,
05949                                            ClipTolerance, ClipFlatness, ClipFlatness);
05950                 ClippedPath.IsFilled = TRUE;
05951                 RenderPath(&ClippedPath);
05952             }
05953 
05954             // decrement loop counter here so that the next colour can be calculated
05955             i--;
05956 
05957             if (i > 0)
05958             {
05959                 // Calculate next brush (except at the end of the loop)
05960                 const COLORREF NewFill = RGB(ColourSteps[i].rgbRed, ColourSteps[i].rgbGreen,
05961                                                 ColourSteps[i].rgbBlue);
05962                 if (NewFill != CurrentCol)
05963                 {
05964                     // colour changed, make new brush
05965                     CurrentCol = NewFill;
05966 
05967                     // Put the RGB value back into a DocColour so we can set it as the fill colour. We set it
05968                     // as non-separable, as it will have already been colour-separated by BuildGraduatedPalette
05969                     TempFillColour.SetRGBValue(ColourSteps[i].rgbRed, 
05970                                                 ColourSteps[i].rgbGreen, 
05971                                                 ColourSteps[i].rgbBlue);
05972                     TempFillColour.SetSeparable(FALSE);
05973 
05974                     SetFillColour(TempFillColour);
05975                     GetValidBrush();
05976                     GetValidPen();
05977                 }
05978 
05979                 // Reduce size of paralleogram
05980                 Parallel[0].x -= (Steps[0].x + Steps[1].x);
05981                 Parallel[0].y -= (Steps[0].y + Steps[1].y);
05982                 Parallel[1].x -= (Steps[0].x - Steps[1].x);
05983                 Parallel[1].y -= (Steps[0].y - Steps[1].y);
05984                 Parallel[2].x += (Steps[0].x + Steps[1].x);
05985                 Parallel[2].y += (Steps[0].y + Steps[1].y);
05986                 Parallel[3].x -= (Steps[1].x - Steps[0].x);
05987                 Parallel[3].y -= (Steps[1].y - Steps[0].y);
05988             }
05989         }
05990 
05991         if ((RenderMethod == GF_USE_GDIEOR) && (RenderCount > 1))
05992         {
05993             // Render the path in white
05994             SetDrawingMode(DM_COPYPEN);
05995             SetFillColour(COLOUR_BLACK);
05996             SetLineColour(COLOUR_TRANS);
05997             GetValidBrush();
05998             GetValidPen();
05999             RenderPath(PathToDraw);
06000 
06001             // Set back to mode required for grad fill rendering
06002             SetDrawingMode(DM_EORDITHER);
06003         }
06004 
06005         RenderCount--;
06006     }
06007 
06008     // now tidy up
06009     RestoreContext();
06010     GetValidBrush();
06011     GetValidPen();
06012 
06013     // Lose the clipping region if we are using one.
06014     if (RenderMethod == GF_USE_GDICLIPPING)
06015         RenderDC->SetClippingRegion(*OSClipRegion);
06016 
06017     return TRUE;
06018 }
06019 
06020 /********************************************************************************************
06021 
06022 >   BOOL OSRenderRegion::RenderThreeColFill ( Path                  *PathToDraw,
06023                                               ThreeColFillAttribute *Fill )
06024 
06025     Author:     Graeme_Sutherland (Xara Group Ltd) <camelotdev@xara.com>
06026     Created:    7/6/00
06027     Inputs:     PathToDraw  - A pointer to the path to be rendered.
06028                 Fill        - A pointer to the radial fill applied.
06029     Returns:    TRUE        - Success.
06030                 FALSE       - An error occured.
06031     Purpose:    Renders a three colour gradient fill to a Windows DC.
06032 
06033 ********************************************************************************************/
06034 
06035 BOOL OSRenderRegion::RenderThreeColFill ( Path                  *PathToDraw,
06036                                           ThreeColFillAttribute *Fill )
06037 {
06038     // Create a new end point for the fill.
06039     DocCoord            EndPoint     ( *( Fill->GetEndPoint () ) + *( Fill->GetEndPoint2 () )
06040                                        - *( Fill->GetStartPoint () ) );
06041     DocColour           EndColour;
06042     LinearFillAttribute LinearFill;
06043 
06044     // Mix the two end colours together to make a blend suitable for the end point.
06045     EndColour.Mix ( Fill->GetEndColour (), Fill->GetEndColour2 (),
06046                     0.5f, NULL, FALSE, NULL );
06047 
06048     // Set up the fill attribute.
06049     LinearFill.SetStartPoint    ( Fill->GetStartPoint () );
06050     LinearFill.SetEndPoint      ( &EndPoint );
06051     LinearFill.SetStartColour   ( Fill->GetStartColour () );
06052     LinearFill.SetEndColour     ( &EndColour );
06053 
06054     // Export the fill as a two colour linear fill.
06055     RenderLinearFill ( PathToDraw, &LinearFill );
06056 
06057     return TRUE;
06058 }
06059 
06060 /********************************************************************************************
06061 
06062 >   BOOL OSRenderRegion::RenderFourColFill ( Path                   *PathToDraw,
06063                                              FourColFillAttribute   *Fill )
06064 
06065     Author:     Graeme_Sutherland (Xara Group Ltd) <camelotdev@xara.com>
06066     Created:    7/6/00
06067     Inputs:     PathToDraw  - A pointer to the path to be rendered.
06068                 Fill        - A pointer to the radial fill applied.
06069     Returns:    TRUE        - Success.
06070                 FALSE       - An error occured.
06071     Purpose:    Renders a four colour gradient fill to a Windows DC. This is done by breaking
06072                 the fill up into a large number of tiles, and then applying these with the
06073                 colour at that point of the fill. This is in common with the other fills, and
06074                 much of the code in this function has been derived from the other fill
06075                 rendering functions.
06076 
06077 ********************************************************************************************/
06078 
06079 BOOL OSRenderRegion::RenderFourColFill ( Path                   *PathToDraw,
06080                                          FourColFillAttribute   *Fill )
06081 {
06082     // Create a linear fill attribute.
06083     LinearFillAttribute LinearFill;
06084 
06085     // Set it up.
06086     LinearFill.SetStartPoint    ( Fill->GetStartPoint () );
06087     LinearFill.SetEndPoint      ( Fill->GetEndPoint3 () );
06088     LinearFill.SetStartColour   ( Fill->GetStartColour () );
06089     LinearFill.SetEndColour     ( Fill->GetEndColour3 () );
06090 
06091     // Export the fill as a two colour linear fill.
06092     RenderLinearFill ( PathToDraw, &LinearFill );
06093 
06094     // Graeme (11-6-00) - Mark has asked me to drop everything, and concentrate on getting a
06095     // Photoshop file format export / import filter going. As a consequence, I'm expected to
06096     // drop everything, and follow his commands, and so I've replacing this partially
06097     // complete four colour fill function with a call to the linear fill rendering function.
06098     // Normally I'd remove a piece of commented out code, but in this case I'm leaving it in
06099     // so that either I can get back to it later, or someone else can fix it.
06100     //
06101     // At the present time, all the function needs is some way of extending the fill pattern
06102     // out to the edges of the space being filled. There might be some way of spoofing this
06103     // using a linear gradient fill, and some kind of clipping region, or just rolling your
06104     // own code, and putting it in here.
06105 /*
06106     DocCoord            HeightVector    = Fill->EndPoint  - Fill->StartPoint;
06107     DocCoord            WidthVector     = Fill->EndPoint2 - Fill->StartPoint;
06108     INT32                   FillHeight      = CalcDistance ( Fill->StartPoint, Fill->EndPoint );
06109     INT32                   FillWidth       = CalcDistance ( Fill->StartPoint, Fill->EndPoint2 );
06110     INT32                   HeightSteps     = 0;
06111     INT32                   WidthSteps      = 0;
06112     INT32                   HeightColDiff   = 0;
06113     INT32                   WidthColDiff    = 0;
06114     FIXED16             PixelSize       = 0;
06115     GradFillMethodType  RenderMethod    = GF_USE_GDICLIPPING;
06116     EFFECTTYPE          EffectType      = GetFillEffect();
06117     INT32               ClipFlatness    = 0;
06118     INT32               ClipTolerance   = 0;
06119     INT32                   RenderCount     = 1;
06120     RGBQUAD             StartColours    [MAX_FILL_STEPS];
06121     RGBQUAD             EndColours      [MAX_FILL_STEPS];
06122     DocCoord            RowOffsets      [MAX_FILL_STEPS + 1];
06123     DocCoord            ColumnOffsets   [MAX_FILL_STEPS + 1];
06124 
06125     // We want to change the rendering context, so we save it.
06126     SaveContext ();
06127 
06128     // No stroking while rendering the fill.
06129     SetLineColour ( COLOUR_TRANS );
06130 
06131     // Use device pixel size to work out how many steps to do.
06132     GetRenderView ()->GetScaledPixelSize ( &PixelSize, &PixelSize );
06133 
06134     // Calculate the number of steps to be made for the fill in each direction, making a
06135     // slice every four pixels.
06136     HeightSteps     = FillHeight / ( PixelSize.MakeLong () * 4);
06137     WidthSteps      = FillWidth  / ( PixelSize.MakeLong () * 4);
06138 
06139     // Calculate the distance between colours.
06140     HeightColDiff   = min ( ColDifference ( Fill->Colour, Fill->EndColour,
06141                                             IsPrinting () ? 24 : 8, EffectType ), 
06142                             ColDifference ( Fill->EndColour2, Fill->EndColour3,
06143                                             IsPrinting () ? 24 : 8, EffectType ) );
06144 
06145     WidthColDiff    = min ( ColDifference ( Fill->Colour, Fill->EndColour2,
06146                                             IsPrinting () ? 24 : 8, EffectType ), 
06147                             ColDifference ( Fill->EndColour, Fill->EndColour3,
06148                                             IsPrinting () ? 24 : 8, EffectType ) );
06149 
06150     // Use the number of discrete steps between colours, and the number of slices to
06151     // determine just how many colours are needed.
06152     HeightSteps     = min ( HeightSteps, HeightColDiff );
06153     WidthSteps      = min ( WidthSteps,  WidthColDiff  );
06154 
06155     // Limit fill steps to a maximum value.
06156     HeightSteps     = min ( HeightSteps, MAX_FILL_STEPS );
06157     WidthSteps      = min ( WidthSteps,  MAX_FILL_STEPS );
06158 
06159     // Use global Quality setting.
06160     HeightSteps     = max ( HeightSteps / (1 << GradFillQuality), 10 );
06161     WidthSteps      = max ( WidthSteps  / (1 << GradFillQuality), 10 );
06162 
06163     // Work out what flatness and accuracy to use for Gavin's clipping functions.
06164     // Gavin recommends 1/2 pixel for flattening, and 1/4 pixel for tolerance.
06165     // (But these were kind of off the top of his head - Tim).
06166     // PS. It seems we need maximum tolerance (i.e. 0) for accurate grad-fill rendering.
06167     ClipFlatness = PixelSize.MakeLong () / 16;
06168 
06169     // Build the gradient table. In the other gradient fills, they call the
06170     // GradTable32::BuildGraduatedPalette () function. Unfortunately I need to operate
06171     // in two dimensions...
06172 
06173     // The first thing to do is to get a pair of gradient tables to act as the start and
06174     // end colours for each row of the fill.
06175     GradTable32::BuildGraduatedPalette ( Fill->Colour,
06176                                          Fill->EndColour,
06177                                          GetRenderView (),
06178                                          EffectType,
06179                                          HeightSteps,
06180                                          StartColours );
06181 
06182     GradTable32::BuildGraduatedPalette ( Fill->EndColour2,
06183                                          Fill->EndColour3,
06184                                          GetRenderView (),
06185                                          EffectType,
06186                                          HeightSteps,
06187                                          EndColours );
06188 
06189     // If we are rendering into a metafile, clip the path ourselves.
06190     if ( RFlags.Metafile )
06191     {
06192         RenderMethod = GF_USE_GAVINCLIPPING;
06193     }
06194     else
06195     {
06196         if ( !SetClipToPathTemporary ( PathToDraw ) )
06197         {
06198             // Can't use clipping - fall back to EOR.
06199             RenderMethod = GF_USE_GDIEOR;
06200         }
06201     }
06202 
06203     // Set the drawing mode up.
06204     if ( RenderMethod == GF_USE_GDIEOR )
06205     {
06206         SetDrawingMode ( DM_EORDITHER );
06207         RenderCount = 2;
06208     }
06209 
06210     // Pre-calculate the vectors between the various rows of tiles.
06211     for ( INT32 i = 0; i <= HeightSteps; i++ )
06212     {
06213         DocCoord Temp ( ( HeightVector.x * i ) / HeightSteps,
06214                         ( HeightVector.y * i ) / HeightSteps);
06215 
06216         RowOffsets [i] = Temp + Fill->StartPoint;
06217     }
06218 
06219     // And between the various columns of tiles.
06220     for ( INT32 j = 0; j <= WidthSteps; j++ )
06221     {
06222         ColumnOffsets [j] = DocCoord ( ( WidthVector.x * j ) / HeightSteps,
06223                                        ( WidthVector.y * j ) / HeightSteps );
06224     }
06225 
06226     // Create the fill pattern.
06227     while ( RenderCount > 0 )
06228     {
06229         // Loop through the colour table, and create the necessary tiles to emulate the fill.
06230         for ( INT32 y = 0; y < HeightSteps; y++ )
06231         {
06232             RGBQUAD     RowColours  [MAX_FILL_STEPS];
06233             DocColour   FirstColour;
06234             DocColour   LastColour;
06235             DocCoord    TileHeight  = RowOffsets [y+1] - RowOffsets [y];
06236 
06237             // Initialise the start and end colours with values from the RGB quad. I need to
06238             // do this because the BuildGraduatePalette method only takes DocColors, and only
06239             // returns RGBQuads.
06240             FirstColour.SetRGBValue ( StartColours [y].rgbRed,
06241                                       StartColours [y].rgbGreen,
06242                                       StartColours [y].rgbBlue );
06243 
06244             LastColour.SetRGBValue  ( EndColours [y].rgbRed,
06245                                       EndColours [y].rgbGreen,
06246                                       EndColours [y].rgbBlue );
06247 
06248             // Create a new palette for this row of the fill.
06249             GradTable32::BuildGraduatedPalette ( FirstColour,
06250                                                  LastColour,
06251                                                  GetRenderView (),
06252                                                  EffectType,
06253                                                  WidthSteps,
06254                                                  RowColours );
06255 
06256             // Now write out the row.
06257             for ( INT32 x = 0; x < WidthSteps; x++ )
06258             {
06259                 // Create the path for the tile to be rendered.
06260                 Path        TilePath;
06261                 PathFlags   TileFlags;
06262                 DocColour   FillColour;
06263 
06264                 // Initialise the path.
06265                 TilePath.Initialise ( 12, 12 );
06266 
06267                 // Set up the path.
06268                 TilePath.InsertMoveTo ( RowOffsets [y]   + ColumnOffsets [x],   &TileFlags );
06269                 TilePath.InsertLineTo ( RowOffsets [y+1] + ColumnOffsets [x],   &TileFlags );
06270                 TilePath.InsertLineTo ( RowOffsets [y+1] + ColumnOffsets [x+1], &TileFlags );
06271                 TilePath.InsertLineTo ( RowOffsets [y]   + ColumnOffsets [x+1], &TileFlags );
06272 
06273                 // Mark the path as closed and filled.
06274                 TilePath.CloseSubPath ();
06275                 TilePath.IsFilled = TRUE;
06276 
06277                 // Set up the fill colour.
06278                 FillColour.SetRGBValue ( RowColours [x].rgbRed,
06279                                          RowColours [x].rgbGreen,
06280                                          RowColours [x].rgbBlue );
06281 
06282                 FillColour.SetSeparable ( FALSE );
06283 
06284                 // Set it as the rendering colour.
06285                 SetFillColour   ( FillColour );
06286                 GetValidBrush   ();
06287                 GetValidPen     ();
06288 
06289                 // And write it out.
06290                 if ( RenderMethod != GF_USE_GAVINCLIPPING )
06291                 {
06292                     RenderPath ( &TilePath );
06293                 }
06294                 else
06295                 {
06296                     // Create a new path.
06297                     Path        ClippedPath;
06298 
06299                     // Initialise the path.
06300                     ClippedPath.Initialise ( 12, 12 );
06301 
06302                     // Clip the tile path to the drawing path to ensure that it fits into the
06303                     // shape being rendered.
06304                     PathToDraw->ClipPathToPath ( TilePath, &ClippedPath, 2, ClipTolerance,
06305                                                  ClipFlatness, ClipFlatness);
06306 
06307                     // Set the IsFilled flag.
06308                     ClippedPath.IsFilled = TRUE;
06309 
06310                     // And render the path.
06311                     RenderPath ( &ClippedPath );
06312                 }
06313             }
06314         }
06315 
06316         // This comes at the end of the rendering loop.
06317         if ( ( RenderMethod == GF_USE_GDIEOR ) && ( RenderCount > 1 ) )
06318         {
06319             // Render the path in white
06320             SetDrawingMode  ( DM_COPYPEN );
06321             SetFillColour   ( COLOUR_BLACK );
06322             SetLineColour   ( COLOUR_TRANS );
06323             GetValidBrush   ();
06324             GetValidPen     ();
06325             RenderPath      ( PathToDraw );
06326 
06327             // Set back to mode required for grad fill rendering
06328             SetDrawingMode  ( DM_EORDITHER );
06329         }
06330 
06331         RenderCount--;
06332     }
06333 
06334     // Restore the original rendering state.
06335     RestoreContext  ();
06336     GetValidBrush   ();
06337     GetValidPen     ();
06338 
06339     // Lose the clipping region if we are using one.
06340     if ( RenderMethod == GF_USE_GDICLIPPING )
06341         RenderDC->SelectClipRgn ( OSClipRegion );
06342 */
06343     // Success.
06344     return TRUE;
06345 }
06346 
06347 BOOL OSRenderRegion::RawRenderPath32( Path *const DrawPath )
06348 {
06349     PORTNOTETRACE("other","OSRenderRegion::RawRenderPath32 - do nothing");
06350 #ifndef EXCLUDE_FROM_XARALX
06351     BOOL Worked;
06352 
06353     // Find out some details about the path and fail if it is not valid
06354     INT32 NumCoords = DrawPath->GetNumCoords();
06355     ENSURE(NumCoords>0, "Tried to draw a path with no elements in it in OSRenderRegion::RawRenderPath32");
06356     if (NumCoords==0)
06357         return FALSE;
06358 
06359     // Find out about the coords
06360     DocCoord* Coords = DrawPath->GetCoordArray();
06361 
06362     // ideally this would be new POINT[NumCoords] but it can't be because that throws an exception
06363     POINT* NTCoords = (POINT*)CCMalloc( sizeof(POINT)*NumCoords );
06364 
06365     if (NTCoords==NULL)
06366     {
06367         TRACE( _T("No memory to convert curve\n") );
06368         return FALSE;
06369     }
06370 
06371     // Convert the points to windows coords
06372     for (INT32 i=0; i<NumCoords; i++)
06373         NTCoords[i] = DocCoordToWin(Coords[i]);
06374 
06375     // render them
06376     Worked = BeginPath( RenderDC );
06377     if (Worked)
06378     {
06379         Worked = NewPolyDraw( NTCoords, DrawPath->GetVerbArray(), NumCoords );
06380         if (Worked)
06381             Worked = EndPath( RenderDC );
06382     }
06383 
06384     CCFree( NTCoords );
06385 
06386     if (!Worked)
06387         TRACE( _T("Raw render32 failed"));
06388 
06389     return Worked;
06390 #else
06391     ENSURE(FALSE, "Raw32 cannot work on win16");
06392     return FALSE;
06393 #endif
06394 }
06395 
06396 
06397 /********************************************************************************************
06398 
06399 >   void OSRenderRegion::RenderPath32( Path *DrawPath )
06400 
06401     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
06402     Created:    16/9/93
06403     Purpose:    Renders a path using Win32 GDI. Causes an ENSURE on Win16 as should never be
06404                 called.
06405     Scope:      Private
06406 
06407 ********************************************************************************************/
06408 
06409 void OSRenderRegion::RenderPath32( Path *DrawPath )
06410 {
06411     PORTNOTETRACE("other","OSRenderRegion::RenderPath32 - do nothing");
06412 #ifndef EXCLUDE_FROM_XARALX
06413     const wxDC* dc = RenderDC;
06414 
06415     // Flags to indicate whether fill-providers or stroke-providers have filled or stroked
06416     // the path yet.
06417     BOOL ExtendedFill = FALSE;
06418 
06419     // If this is not a simple fill, set the flag to indicate this.
06420     FillGeometryAttribute *pFillProvider = 
06421         (FillGeometryAttribute *) CurrentAttrs[ATTR_FILLGEOMETRY].pAttr;
06422 
06423     if (pFillProvider->GetRuntimeClass() != CC_RUNTIME_CLASS(FlatFillAttribute))
06424         ExtendedFill = TRUE;
06425 
06426     // low quality displays can't do fancy fills
06427     if (RRQuality.GetFillQuality() < Quality::Graduated)
06428         ExtendedFill = FALSE;
06429 
06430     //
06431     // We ought to check for stroke-providers in here, when the great day comes that
06432     // we actually have them...
06433     //
06434 
06435     // Get the fill provider to fill the path - if it can't handle this sort of render
06436     // region, then we fill the path for it.
06437     if (ExtendedFill)
06438     {
06439         if (!pFillProvider->RenderFill(this, DrawPath))
06440             // Unable to render fill for this render region - default to simple fill.
06441             ExtendedFill = FALSE;
06442     }
06443 
06444     // actually do the rendering
06445     BOOL Result = RawRenderPath32( DrawPath );
06446 
06447     if (!Result)
06448     {
06449         TRACE( _T("RawRenderPath failed"));
06450         return;
06451     }
06452 
06453     // Draw it either filled or not.
06454     // NB. If the fill-provider has already filled this path then we don't bother.
06455     if (DrawPath->IsFilled && !ExtendedFill)
06456         StrokeAndFillPath( dc );
06457     else
06458         StrokePath( dc );
06459 
06460 #endif
06461 }
06462 
06463 
06464 
06465 /********************************************************************************************
06466 
06467 >   virtual void OSRenderRegion::GetRenderRegionCaps(RRCaps* pCaps)
06468 
06469     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
06470     Created:    21/4/95
06471     Outputs:    pCaps - the render region marks all the things it can not do into the pCaps
06472                 param
06473     Purpose:    This function is used to help determine what an individual render region
06474                 is capable of. A render can mark the things it can not render (eg transparancy)
06475                 so that a more complex rendering of these parts of the document can be done.
06476 
06477 ********************************************************************************************/
06478 
06479 void OSRenderRegion::GetRenderRegionCaps(RRCaps* pCaps)
06480 {
06481     // Start off by saying I can't do anything
06482     pCaps->CanDoNothing();
06483 
06484     // but actually I can do these things
06485     pCaps->GradFills = TRUE;
06486     pCaps->LineAttrs = TRUE;
06487     pCaps->ArrowHeads = TRUE;
06488     pCaps->DashPatterns = TRUE;
06489 
06490     // We only try simple bitmaps with GDI if printing
06491     pCaps->SimpleBitmaps = IsPrinting();
06492 
06493 //  pCaps->CanDoAll();
06494 }
06495 
06496 
06497 
06498 /********************************************************************************************
06499 
06500 >   SlowJobResult OSRenderRegion::DrawMaskedBitmap(const DocRect &Rect, KernelBitmap* pBitmap, 
06501                                   MaskedRenderRegion* pMask, Progress *Progress)
06502 
06503     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
06504     Created:    20/4/95
06505     Inputs:     Point - the position to plot the bitmap
06506                 pBitmap - The bitmap that needs plotting
06507                 pMask - The mask render region to use to indicate which bits of pBitmap
06508                 needs to be plotted
06509     Purpose:    Plots the bitmap using the mask supplied.
06510 
06511 ********************************************************************************************/
06512 
06513 SlowJobResult OSRenderRegion::DrawMaskedBitmap(const DocRect &Rect, KernelBitmap* pBitmap, 
06514                                       MaskedRenderRegion* pMask, ProgressDisplay *Progress)
06515 {
06516     PORTNOTETRACE("other","OSRenderRegion::DrawMaskedBitmap - do nothing");
06517 #if !defined(STANDALONE) && !defined(EXCLUDE_FROM_XARALX)
06518     if (RenderView->GetColourPlate() != NULL &&
06519         !RenderView->GetColourPlate()->IsDisabled())
06520     {
06521         // We're colour separating. Indirect this call to the separation variant of this code
06522         // (see below)
06523         return(DrawSeparatedMaskedBitmap(Rect, pBitmap, pMask, Progress));
06524     }
06525 
06526 
06527     // Make sure the world is in one piece
06528     if ((pBitmap==NULL) || (pMask==NULL))
06529         return SLOWJOB_FAILURE;
06530 
06531     // If mask coverage is 0% we do not need to do anything. The TRUE param indicates
06532     // that we don't care about exact coverage, which makes it return as soon as it realises
06533     // that there are pixel(s) that we'll have to plot
06534     if (pMask->FindCoverage(TRUE) == 0)
06535         return SLOWJOB_SUCCESS;
06536 
06537     // Guess we will need to be doing something...
06538     if (pBitmap->ActualBitmap == NULL)
06539         return SLOWJOB_FAILURE;
06540 
06541     // Get the 'Actual' windows Bitmap
06542     WinBitmap *WinBM = (WinBitmap*)pBitmap->ActualBitmap;
06543 
06544     // Is it valid ?
06545     if ((WinBM->BMInfo==NULL) || (WinBM->BMBytes==NULL))
06546         return SLOWJOB_FAILURE;
06547 
06548     // Remember the Size of the Bitmap (in pixels)
06549     INT32 Width  = WinBM->GetWidth();
06550     INT32 Height = WinBM->GetHeight();
06551 
06552     if ((Width == 0) || (Height == 0))
06553         // Error - bitmap has no dimension
06554         return SLOWJOB_FAILURE;
06555 
06556     // Convert the bitmap from a 32bpp bitmap to a 24bpp bitmap
06557     INT32 BitmapDepth = WinBM->GetBPP();
06558     if (BitmapDepth == 32)
06559     {
06560         // Can't plot 32bpp bitmaps to GDI as 16-bit GDI doesn't understand them,
06561         // so we convert to 24bpp bitmap in-situ and render that...
06562 
06563         // How many bytes to a source scanline?
06564         const INT32 ScanlineBytes = WinBM->GetScanlineSize();
06565 
06566         // How many bytes to a destination scanline
06567         const INT32 DestlineBytes = DIBUtil::ScanlineSize(Width, 24);
06568 
06569         // Now convert the bitmap in-situ
06570         LPBYTE OriginalBuffer  = WinBM->BMBytes;
06571         LPBYTE ConvertedBuffer = WinBM->BMBytes;
06572 
06573         for (INT32 i=0; i<Height; i++)
06574         {
06575             DIBUtil::Convert32to24(Width, OriginalBuffer, ConvertedBuffer);
06576             OriginalBuffer += ScanlineBytes;
06577             ConvertedBuffer += DestlineBytes;
06578         }
06579 
06580         // Update bitmap info to show it is now a 24bpp bitmap...
06581         WinBM->BMInfo->bmiHeader.biSizeImage = DestlineBytes * Height;
06582         WinBM->BMInfo->bmiHeader.biBitCount  = 24;
06583         BitmapDepth = 24;       
06584     }
06585 
06586     // make sure we have a 24bpp bitmap
06587     ERROR3IF(BitmapDepth!=24, "Non 24bpp bitmap found in DrawMaskedBitmap");
06588 
06589     // Work out the coords to blit to, taking into acount the differnt dpis of the
06590     // source and destination bitmaps.
06591     INT32 SrcDpi = pMask->FindMaskDpi();
06592     INT32 DestDpi = SrcDpi;
06593     if (RenderDC!=NULL)
06594         DestDpi = RenderDC->GetDeviceCaps(LOGPIXELSY);
06595 
06596     // Work out the ratio between to two
06597     double Ratio = DestDpi;
06598     Ratio = Ratio/SrcDpi;
06599 
06600     // Convert the DocCoord into a windows coord
06601     DocRect ClipRect = pMask->GetClipRect();
06602     WinRect WinClipRect = DocRectToWin(RenderMatrix, ClipRect, DestDpi);
06603     POINT Origin;
06604     Origin.x = WinClipRect.left;
06605     Origin.y = WinClipRect.bottom;
06606 
06607     // Inform progress object how high this band is
06608     if (PrintMonitor::PrintMaskType==PrintMonitor::MASK_MASKED)
06609     {
06610         if (Progress!=NULL)
06611             Progress->StartBitmapPhaseBand(Height);
06612     }
06613 
06614     // We need to create a tempory bitmap that we use to render each scan line
06615     LPBITMAPINFO    TempBitmapInfo = NULL;
06616     LPBYTE          TempBitmapBytes = NULL;
06617 
06618     // Get some memory for a tempory bmp 1 pixel high
06619     TempBitmapInfo = AllocDIB(Width, 1, BitmapDepth, &TempBitmapBytes);
06620     if (TempBitmapInfo==NULL)
06621         return SLOWJOB_FAILURE;
06622 
06623     // How many bytes to a destination scanline
06624     INT32 ScanLineBytes = DIBUtil::ScanlineSize(Width, BitmapDepth);
06625     INT32  BytesPerPixel = 3;
06626 
06627     // Now convert the bitmap in-situ
06628     LPBYTE SrcBuffer  = WinBM->BMBytes;
06629     LPBYTE DestBuffer = TempBitmapBytes;
06630     
06631     // Set the blit mode
06632     INT32 OldMode = SetStretchBltMode(RenderDC->GetSafeHdc(), HALFTONE);//COLORONCOLOR);
06633 
06634     // Must call SetBrushOrgEx() after setting the stretchblt mode 
06635     // to HALFTONE (see Win32 SDK docs).
06636     POINT OldOrg;
06637     SetBrushOrgEx(RenderDC->m_hDC, 0, 0, &OldOrg);
06638 
06639     // Now copy the Bits from our Kernel Bitmap into the Memory DC - either we can
06640     // do this as a masked blit (a scan line at a time), or in one go.
06641     // We do the masked blit under NT, or if the preference has been over-ridden by the user.
06642     if (PrintMonitor::PrintMaskType!=PrintMonitor::MASK_SIMPLE)
06643     {
06644         MaskRegion MaskInfo;
06645         pMask->GetFirstMaskRegion(&MaskInfo);
06646         while (MaskInfo.Length!=0)
06647         {
06648             // Calculate the source buffer address from the x and y position
06649             SrcBuffer = WinBM->BMBytes;
06650             SrcBuffer += ScanLineBytes * MaskInfo.y;
06651             SrcBuffer += MaskInfo.x*BytesPerPixel;
06652             INT32 RegionWidth = MaskInfo.Length;
06653 
06654             // Update bitmap info to show the new scan line size
06655             TempBitmapInfo->bmiHeader.biWidth = RegionWidth;
06656             TempBitmapInfo->bmiHeader.biSizeImage = (RegionWidth*BytesPerPixel);
06657 
06658             memcpy(DestBuffer, SrcBuffer, RegionWidth*BytesPerPixel);
06659 
06660             // Work out the coords of the destination rectangle
06661             INT32 DestX = Origin.x + INT32(ceil(MaskInfo.x*Ratio));
06662             INT32 DestY = Origin.y - INT32(ceil((MaskInfo.y+1)*Ratio));
06663             INT32 DestWidth = INT32(ceil(RegionWidth*Ratio));
06664             INT32 DestHeight = INT32(ceil(1*Ratio));
06665         
06666             // Blit the data to the screen
06667             StretchDIBits(  RenderDC->GetSafeHdc(),
06668                             DestX, DestY, DestWidth, DestHeight,
06669                             0, 0,
06670                             RegionWidth, 1,
06671                             TempBitmapBytes, TempBitmapInfo,
06672                             DIB_RGB_COLORS, SRCCOPY);
06673 
06674             // Update the progress display if necessary.
06675             if (PrintMonitor::PrintMaskType==PrintMonitor::MASK_MASKED)
06676             {
06677                 if ((Progress!=NULL) && (!Progress->BitmapPhaseBandRenderedTo(MaskInfo.y)))
06678                 {
06679                     // Put the blit mode back as it was
06680                     SetStretchBltMode(RenderDC->GetSafeHdc(), OldMode);
06681                     SetBrushOrgEx(RenderDC->m_hDC, OldOrg.x, OldOrg.y, NULL);
06682 
06683                     // Free up the tempory DIB I made
06684                     FreeDIB(TempBitmapInfo, TempBitmapBytes);
06685 
06686                     return SLOWJOB_USERABORT;
06687                 }
06688             }
06689 
06690             // Find the next bit of scan line to plot
06691             pMask->GetNextMaskRegion(&MaskInfo);
06692         }
06693     }
06694     else
06695     {
06696         // Blit in one go...
06697 
06698         // Work out the coords of the destination rectangle
06699         INT32 DestX = Origin.x;
06700         INT32 DestY = Origin.y - INT32(ceil(Height * Ratio));
06701         INT32 DestWidth = INT32(ceil(Width * Ratio));
06702         INT32 DestHeight = INT32(ceil(Height * Ratio));
06703 
06704         // Blit the data to the screen
06705         StretchDIBits(  RenderDC->GetSafeHdc(),
06706                         DestX, DestY, DestWidth, DestHeight,
06707                         0, 0,
06708                         Width, Height,
06709                         WinBM->BMBytes, WinBM->BMInfo,
06710                         DIB_RGB_COLORS, SRCCOPY);
06711 
06712         // Update the progress display if necessary.
06713         if ((Progress!=NULL) && (!Progress->BitmapPhaseBandRenderedTo(Height)))
06714         {
06715             // Put the blit mode back as it was
06716             SetStretchBltMode(RenderDC->GetSafeHdc(), OldMode);
06717             SetBrushOrgEx(RenderDC->m_hDC, OldOrg.x, OldOrg.y, NULL);
06718 
06719             // Free up the tempory DIB I made
06720             FreeDIB(TempBitmapInfo, TempBitmapBytes);
06721 
06722             return SLOWJOB_USERABORT;
06723         }
06724     }
06725 
06726     // Put the blit mode back as it was
06727     SetStretchBltMode(RenderDC->GetSafeHdc(), OldMode);
06728     SetBrushOrgEx(RenderDC->m_hDC, OldOrg.x, OldOrg.y, NULL);
06729 
06730     // Update bitmap info to what it was before we started
06731     TempBitmapInfo->bmiHeader.biSizeImage = (Width*3);
06732     TempBitmapInfo->bmiHeader.biWidth = Width;
06733 
06734     // Free up the tempory DIB I made
06735     FreeDIB(TempBitmapInfo, TempBitmapBytes);
06736 
06737 #endif
06738 
06739     // All ok
06740     return SLOWJOB_SUCCESS;
06741 }
06742 
06743 
06744 
06745 
06746 /********************************************************************************************
06747 
06748 >   SlowJobResult OSRenderRegion::DrawSeparatedMaskedBitmap(const DocRect &Rect, KernelBitmap* pBitmap, 
06749                                   MaskedRenderRegion* pMask, Progress *Progress)
06750 
06751     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
06752     Created:    11/7/96
06753     Inputs:     Point - the position to plot the bitmap
06754                 pBitmap - The bitmap that needs plotting
06755                 pMask - The mask render region to use to indicate which bits of pBitmap
06756                 needs to be plotted
06757                 Progress - The progress object to be kept informed of progress
06758 
06759     Purpose:    Plots the bitmap using the mask supplied.
06760                 The bitmap is colour separated according to the current ColourPlate settings,
06761                 and output (a scanline at a time) in an 8bpp greyscale format.
06762 
06763     Notes:      This function is autoamtically called by DrawMaskedBitmap() if it is called
06764                 while we are rendering a colour separation plate. It is essentially a copy
06765                 of DrawMaskedBitmap which has been rejigged to colour separate the bitmap.
06766 
06767 ********************************************************************************************/
06768 
06769 SlowJobResult OSRenderRegion::DrawSeparatedMaskedBitmap(const DocRect &Rect, KernelBitmap* pBitmap, 
06770                                       MaskedRenderRegion* pMask, ProgressDisplay *Progress)
06771 {
06772     PORTNOTETRACE("other","OSRenderRegion::DrawSeparatedMaskedBitmap - do nothing");
06773 #ifndef EXCLUDE_FROM_XARALX
06774 #ifndef STANDALONE
06775     SlowJobResult Result = SLOWJOB_SUCCESS;
06776     
06777     // Make sure the world is in one piece
06778     if ((pBitmap==NULL) || (pMask==NULL))
06779         return SLOWJOB_FAILURE;
06780 
06781     // If mask coverage is 0% we do not need to do anything. The TRUE param indicates
06782     // that we don't care about exact coverage, which makes it return as soon as it realises
06783     // that there are pixel(s) that we'll have to plot
06784     if (pMask->FindCoverage(TRUE) == 0)
06785         return SLOWJOB_SUCCESS;
06786 
06787     // Get the 'Actual' windows Bitmap
06788     WinBitmap *WinBM = (WinBitmap*)pBitmap->ActualBitmap;
06789 
06790     // Is it valid ?
06791     if (WinBM == NULL || WinBM->BMInfo == NULL || WinBM->BMBytes == NULL)
06792         return SLOWJOB_FAILURE;
06793 
06794     // We're colour separating, so make sure we've got some sep tables to work with.
06795     // We cache them once we've used them. I would do this sort of set-up in StartRender
06796     // but of course all the derived classes just override that behaviour and we're stuffed
06797     if (SepTables == NULL && RenderView->GetColourPlate() != NULL)
06798     {
06799         ColourContextCMYK *cc = (ColourContextCMYK *)RenderView->GetColourContext(COLOURMODEL_CMYK);
06800         if (cc != NULL)
06801         {
06802             SepTables = (BYTE *) CCMalloc(5 * 256 * sizeof(BYTE));
06803             if (SepTables != NULL)
06804             {
06805                 if (!cc->GetProfileTables(SepTables))
06806                 {
06807                     CCFree(SepTables);
06808                     SepTables = NULL;
06809                 }
06810             }
06811         }
06812 
06813         ERROR3IF(SepTables == NULL, "Can't generate separation tables in OSRenderRegion");
06814         if (SepTables == NULL)
06815             return(SLOWJOB_FAILURE);
06816     }
06817 
06818 
06819     // Remember the Size of the Bitmap (in pixels)
06820     INT32 Width  = WinBM->GetWidth();
06821     INT32 Height = WinBM->GetHeight();
06822     INT32 BitmapDepth = WinBM->GetBPP();
06823 
06824     if (Width == 0 || Height == 0)
06825         return SLOWJOB_FAILURE;
06826 
06827     // Work out the coords to blit to, taking into acount the differnt dpis of the
06828     // source and destination bitmaps.
06829     INT32 SrcDpi = pMask->FindMaskDpi();
06830     INT32 DestDpi = SrcDpi;
06831     if (RenderDC!=NULL)
06832         DestDpi = RenderDC->GetDeviceCaps(LOGPIXELSY);
06833 
06834     // Work out the ratio between to two
06835     double Ratio = DestDpi;
06836     Ratio = Ratio/SrcDpi;
06837 
06838     // Convert the DocCoord into a windows coord
06839     DocRect ClipRect = pMask->GetClipRect();
06840     WinRect WinClipRect = DocRectToWin(RenderMatrix, ClipRect, DestDpi);
06841     POINT Origin;
06842     Origin.x = WinClipRect.left;
06843     Origin.y = WinClipRect.bottom;
06844 
06845     // Inform progress object how high this band is
06846     if (PrintMonitor::PrintMaskType==PrintMonitor::MASK_MASKED)
06847     {
06848         if (Progress!=NULL)
06849             Progress->StartBitmapPhaseBand(Height);
06850     }
06851 
06852     // Allocate a 32bpp scanline buffer
06853     Pixel32bpp *pScanline = (Pixel32bpp *) CCMalloc(Width * sizeof(Pixel32bpp));
06854     if (pScanline == NULL)
06855         return(SLOWJOB_FAILURE);
06856 
06857     // Create a bitmap header structure to use with our temporary scanline. As all output data
06858     // will be 8bpp, we set the header up as an 8bpp bitmap.
06859     LPBITMAPINFO pScanlineInfo = AllocDIB(Width, 1, 8, NULL);
06860     if (pScanlineInfo == NULL)
06861         return SLOWJOB_FAILURE;
06862 
06863     // Set up a simple greyscale palette for the bitmap
06864     for (INT32 i = 0; i < 256; i++)
06865     {
06866         pScanlineInfo->bmiColors[i].rgbRed = 
06867             pScanlineInfo->bmiColors[i].rgbGreen = 
06868                 pScanlineInfo->bmiColors[i].rgbBlue = i;
06869         pScanlineInfo->bmiColors[i].rgbReserved = 0;
06870     }
06871 
06872     // Set the blit mode
06873     INT32 OldMode = SetStretchBltMode(RenderDC->GetSafeHdc(), HALFTONE);//COLORONCOLOR);
06874 
06875     // Must call SetBrushOrgEx() after setting the stretchblt mode 
06876     // to HALFTONE (see Win32 SDK docs).
06877     POINT OldOrg;
06878     SetBrushOrgEx(RenderDC->m_hDC, 0, 0, &OldOrg);
06879 
06880     ColourContext *OutputContext = RenderView->GetColourContext(COLOURMODEL_RGBT);
06881     ERROR3IF(OutputContext == NULL, "Where's me RGB colour context gone then?");
06882 
06883     // Now copy the Bits from our Kernel Bitmap into the Memory DC
06884     // as a masked blit (a scan line at a time, so we can colour separate it)
06885     MaskRegion MaskInfo;
06886     pMask->GetFirstMaskRegion(&MaskInfo);
06887     while (MaskInfo.Length != 0)
06888     {
06889         // Update bitmap info to show the new scan line size
06890         pScanlineInfo->bmiHeader.biWidth     = MaskInfo.Length;
06891         pScanlineInfo->bmiHeader.biSizeImage = MaskInfo.Length * sizeof(BYTE);
06892 
06893         // Read out the scanline into our pScanline buffer, converting it to a generic
06894         // 32bpp format as we go. This is 
06895         WinBM->GetScanline32bpp(MaskInfo.y, TRUE, pScanline);
06896 
06897         // Now colour separate it. We only bother separating the unmasked portion of the scanline
06898         // which we place back in the left end of the scanline
06899         WinBM->ColourSeparateScanline32to8(OutputContext, SepTables,
06900                                             (BYTE *) pScanline,     // Output buffer (shared!)
06901                                             pScanline + MaskInfo.x, // Input buffer - !pointer arithmetic!
06902                                             MaskInfo.Length);
06903 
06904         // Work out the coords of the destination rectangle
06905         const INT32 DestX = Origin.x + INT32(ceil(MaskInfo.x * Ratio));
06906         const INT32 DestY = Origin.y - INT32(ceil((MaskInfo.y + 1) * Ratio));
06907         const INT32 DestWidth = INT32(ceil(MaskInfo.Length * Ratio));
06908         const INT32 DestHeight = INT32(ceil(1 * Ratio));
06909     
06910         // Blit the data to the screen
06911         StretchDIBits(  RenderDC->GetSafeHdc(),
06912                         DestX, DestY, DestWidth, DestHeight,
06913                         0, 0,
06914                         MaskInfo.Length, 1,
06915                         (BYTE *) pScanline, pScanlineInfo,
06916                         DIB_RGB_COLORS, SRCCOPY);
06917 
06918         // Update the progress display if necessary.
06919         if (PrintMonitor::PrintMaskType == PrintMonitor::MASK_MASKED &&
06920             Progress != NULL && !Progress->BitmapPhaseBandRenderedTo(MaskInfo.y))
06921         {
06922             Result = SLOWJOB_USERABORT;
06923             break;      // User aborted, so quit rendering, and return USERABORT
06924         }
06925 
06926         // Find the next bit of scan line to plot
06927         pMask->GetNextMaskRegion(&MaskInfo);
06928     }
06929 
06930     // Put the blit mode back as it was
06931     SetStretchBltMode(RenderDC->GetSafeHdc(), OldMode);
06932     SetBrushOrgEx(RenderDC->m_hDC, OldOrg.x, OldOrg.y, NULL);
06933 
06934     // Free up the tempory DIB I made
06935     FreeDIB(pScanlineInfo, NULL);
06936     CCFree(pScanline);
06937 
06938     return(Result);
06939 #else
06940     return SLOWJOB_FAILURE;
06941 #endif
06942 #else
06943     return SLOWJOB_SUCCESS;
06944 #endif
06945 }
06946 
06947 
06948 
06949 /********************************************************************************************
06950 
06951 >   BOOL OSRenderRegion::RenderChar(WCHAR ch, Matrix* pMatrix)
06952 
06953     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
06954     Created:    30/4/95
06955     Inputs:     ch      - unicode value of char
06956                 pMatrix - matrix specifying transforms to place char correctly in document
06957     Returns:    FALSE if fails
06958     Purpose:    Render a character,
06959                 using the specified transform and current attributes in the render region.
06960 
06961 ********************************************************************************************/
06962 
06963 BOOL OSRenderRegion::RenderChar(WCHAR ch, Matrix* pMatrix)
06964 {
06965     PORTNOTETRACE("text","OSRenderRegion::RenderChar - do nothing");
06966 #ifndef EXCLUDE_FROM_XARALX
06967     // If it is stroked or not simple flat fill, or not a standard ASCII character then we 
06968     //  must do this as paths. Graham 8/8/96: And now UNICODE works too.
06969     BOOL FlatFill = IS_A(CurrentAttrs[ATTR_FILLGEOMETRY].pAttr, FlatFillAttribute);
06970 
06971     if (!FlatFill || 
06972         !RR_STROKECOLOUR().IsTransparent() || 
06973         (FlatFill & RR_FILLCOLOUR().IsTransparent()))
06974         return RenderRegion::RenderChar(ch, pMatrix);
06975 
06976     if (IsPrinting())
06977     {
06978         // Check for emulsion down printing, GDI cannot render text backwards
06979         PrintControl *pPrintCtl;
06980         View *pView = GetRenderView();
06981         if (pView && (pPrintCtl=pView->GetPrintControl())) 
06982         { 
06983             if (pPrintCtl->GetTypesetInfo()->PrintEmulsionDown())
06984             return RenderRegion::RenderChar(ch, pMatrix);
06985         }
06986     }
06987 
06988     // get overall matrix - attribute matrix concatenated with given matrix if supplied
06989     Matrix matrix;
06990     if (GetCharAttributeMatrix(&matrix)==FALSE)
06991         return NULL;
06992     if (pMatrix)
06993         matrix*=*pMatrix;
06994 
06995     // Can we do this using a GDI font?
06996     // We can if the matrix only specifies scaling and translation
06997     FIXED16 abcd[4];
06998     INT32   ef[2];
06999     matrix.GetComponents(abcd, ef);
07000 
07001     // GDI can't do y-axis flips, so we do it as shapes if this is detected (and x-axis
07002     // flips, for consistency).
07003     if ((abcd[0] < FIXED16(0)) || (abcd[3] < FIXED16(0)))
07004     {
07005         // Flipped in one or both axes - render as a path.
07006         return RenderRegion::RenderChar(ch, pMatrix);
07007     }
07008 
07009     // Work out how complex the transformation is.
07010     FIXED16 ScaleX = 0;
07011     FIXED16 ScaleY = 0;
07012     ANGLE   Rotation = 0;
07013     ANGLE   Shear = 0;
07014 
07015     if ((abcd[1] == FIXED16(0)) && (abcd[2] == FIXED16(0)) && 
07016         (abcd[0] >= FIXED16(0)) && (abcd[3] >= FIXED16(0)))
07017     {
07018         // Simple scaling transformation.
07019         ScaleX   = abcd[0];
07020         ScaleY   = abcd[3];
07021         Rotation = FIXED16(0);
07022         Shear    = FIXED16(0);
07023     }
07024     else
07025     {
07026         // Decompose the matrix to find out how complex it is.
07027         // Pass in NULL for translation as we already know it is in 'ef'.
07028         FIXED16 Aspect;
07029         BOOL Result = matrix.Decompose(&ScaleY, &Aspect, &Rotation, &Shear, NULL);
07030 
07031         if (!Result || (Shear != FIXED16(0)))
07032             // Either there was a problem, or the character is sheared, in which case
07033             // we can't do it with GDI.
07034             return RenderRegion::RenderChar(ch, pMatrix);
07035 
07036         // Set up the ScaleX based on the aspect ratio
07037         ScaleX = ScaleY * Aspect;
07038     }
07039 
07040     // Check for sideways printing - if the render matrix has rotation, then we are
07041     // printing at 270 degrees rotation, so adjust the rotation accordingly.
07042     FIXED16 RenderABCD[4];
07043     INT32   RenderEF[2];
07044     RenderMatrix.GetComponents(RenderABCD, RenderEF);
07045     if ((RenderABCD[1] != FIXED16(0)) || (RenderABCD[2] != FIXED16(0)))
07046         // Rotate by 270 degrees (angle is in radians)
07047         Rotation += FIXED16(1.5 * PI);
07048 
07049     // Work out required width and height of the font
07050     MILLIPOINT ReferenceSize = TextManager::GetDefaultHeight();
07051     MILLIPOINT Width  = ReferenceSize * ScaleX;
07052     MILLIPOINT Height = ReferenceSize * ScaleY;
07053 
07054     if (!SelectNewFont(RR_TXTFONTTYPEFACE(), RR_TXTBOLD(), RR_TXTITALIC(),
07055                        Width, Height, Rotation))
07056     {
07057         // Could not select font (maybe because device can't rotate fonts)
07058         return RenderRegion::RenderChar(ch, pMatrix);
07059     }
07060 
07061     // First, set up the text attributes that are not encoded in the font.
07062     UINT32 OldTextAlign = RenderDC->SetTextAlign(TA_BASELINE);
07063     INT32 OldBKMode = RenderDC->SetBkMode(TRANSPARENT);
07064     COLORREF OldTextColor = 
07065         RenderDC->SetTextColor(ConvertColourToScreenWord(CurrentColContext, &RR_FILLCOLOUR()));
07066 
07067     // Render the character in the specified position
07068     DocCoord DocPos(ef[0], ef[1]);
07069     WinCoord WinPos = DocCoordToWin(DocPos);
07070 
07071 
07072     // Graham 5/8/96: "ch" is presently in UNICODE or ASCII
07073     // We need to convert it over to MBCS to deal with Japanese strings
07074     //So convert ch, which is of form WCHAR, over to a MBCS UINT32 character index
07075 
07076     UINT32 uiCharNumber = UnicodeManager::UnicodeToMultiByte(ch);
07077 
07078     //Now we want to put that UINT32 value into an array of char ready to pass to
07079     //RenderDC->TextOut. We do this using UnicodeManager::DecomposeMultiBytes
07080 
07081     BYTE bCharArray[2];
07082 
07083     UnicodeManager::DecomposeMultiBytes(uiCharNumber, &bCharArray[0], &bCharArray[1]);
07084 
07085     //Now, is the character in bCharArray one or two bytes long?
07086     //If it is one byte long, the first byte in bCharArray will be zero.
07087     if (bCharArray[0]==0)
07088         //It's a standard ASCII character, one byte long
07089         //So pass that character (bCharArray[1]) to the TextOut function.
07090         //The last parameter in text out is the number of bytes - in this case 1.
07091         RenderDC->TextOut(WinPos.x, WinPos.y, (CHAR*) &bCharArray[1], 1);
07092     else
07093         //The character is two bytes long (that is, it's a foreign character)
07094         //So we pass bCharArray[0] to TextOut and tell TextOut that it should
07095         //use two bytes from that address. We do this by setting the last
07096         //parameter to 2.
07097         RenderDC->TextOut(WinPos.x, WinPos.y, (CHAR*) &bCharArray[0], 2);
07098 
07099     // Clean up text attributes
07100     RenderDC->SetTextAlign(OldTextAlign);
07101     RenderDC->SetBkMode(OldBKMode);
07102     RenderDC->SetTextColor(OldTextColor);
07103 #elif !defined(DISABLE_TEXT_RENDERING)
07104     return RenderRegion::RenderChar(ch, pMatrix);
07105 #endif
07106     return TRUE;
07107 }
07108 
07109 
07110 
07111 
07112 /********************************************************************************************
07113 
07114 >   PaperRenderRegion::PaperRenderRegion(DocRect ClipRect, Matrix ConvertMatrix, FIXED16 ViewScale)
07115 
07116     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
07117     Created:    28/06/94
07118     Inputs:     As OSRenderRegion's constructor.
07119     Purpose:    Construct a PaperRenderRegion - this is a simple derivation from
07120                 OSRenderRegion's constructor.
07121     SeeAlso:    OSRenderRegion; OSRenderRegion::OSRenderRegion
07122 
07123 ********************************************************************************************/
07124 
07125 PaperRenderRegion::PaperRenderRegion(DocRect ClipRect, Matrix ConvertMatrix, FIXED16 ViewScale)
07126     : OSRenderRegion(ClipRect, ConvertMatrix, ViewScale)
07127 {
07128 }
07129 
07130 
07131 /********************************************************************************************
07132 
07133 >   PaperRenderRegion::~PaperRenderRegion()
07134 
07135     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
07136     Created:    28/06/94
07137     Purpose:    Perform any closedown necessary for the paper render region.
07138     SeeAlso:    OSRenderRegion
07139 
07140 ********************************************************************************************/
07141 
07142 PaperRenderRegion::~PaperRenderRegion()
07143 {
07144 }
07145 
07146 /********************************************************************************************
07147 
07148 >   BOOL PaperRenderRegion::AttachDevice(CWnd *pWnd, Spread *pSpread, CDC *pDC, 
07149                                      Matrix& ViewMatrix, FIXED16 ViewScale, 
07150                                      DocRect& ClipRect)
07151 
07152     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
07153     Created:    28/06/94
07154     Inputs:     pView - the window to attach to.
07155                 pSpread - the spread to attach to.
07156                 pDC - the device context to render into
07157                 ViewMatrix - the matrix needed to render this spread into this DC.
07158                 ViewScale - the scale of the view to be rendered.
07159                 ClipRect - the clip rectangle of the region.
07160     Returns:    TRUE if attached ok; FALSE if not.
07161     Purpose:    Change the device attached to the PaperRenderRegion.
07162     SeeAlso:    OSRenderRegion::AttachDevice
07163 
07164 ********************************************************************************************/
07165 
07166 BOOL PaperRenderRegion::AttachDevice(View *pView, Spread *pSpread, wxDC* pDC, 
07167                                      Matrix& ViewMatrix, FIXED16 ViewScale, 
07168                                      DocRect& ClipRect, bool fOwned /*= false*/)
07169 {
07170     if (!OSRenderRegion::AttachDevice(pView, pDC, pSpread))
07171         return(FALSE);
07172 
07173     // Update basic rendering variables for new device.
07174     // (These are normally set up the the constructor).
07175     CurrentClipRect = ClipRect;
07176     RenderMatrix = ViewMatrix;
07177     ScaleFactor = ViewScale;
07178 
07179     // Make sure we get rid of all attributes at Detach time - see DetachDevice() below.
07180     SaveContext();
07181 
07182     // Call base class to attach device.
07183 //  return OSRenderRegion::AttachDevice(pView, pDC, pSpread);
07184     return(TRUE);
07185 }
07186 
07187 /********************************************************************************************
07188 
07189 >   void PaperRenderRegion::DetachDevice()
07190 
07191     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
07192     Created:    28/06/94
07193     Purpose:    Detach the render region form its device.  This is done so that we don't
07194                 try to use the DC etc. after it has been deleted.
07195     SeeAlso:    PaperRenderRegion::AttachDevice
07196 
07197 ********************************************************************************************/
07198 
07199 void PaperRenderRegion::DetachDevice()
07200 {
07201     // Lose all the attributes on our rendering stack
07202     RestoreContext();
07203 
07204     // Lose our current attribute block.
07205     CCFree(CurrentAttrs);
07206     CurrentAttrs = NULL;
07207 
07208     // Make sure we don't try to use this device again.
07209     RenderDC = NULL;
07210     RenderView = NULL;
07211     RenderSpread = NULL;
07212     RenderFlags.ValidDevice = FALSE;        // make sure InitDevice is called subsequently
07213                                             // else GBrushes tend not to work
07214 }
07215 
07216 
07217 /********************************************************************************************
07218 
07219 >   BOOL PaperRenderRegion::InitDevice()
07220 
07221     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
07222     Created:    17/5/95
07223 
07224     Returns:    TRUE if the device context is initialised ok;
07225                 FALSE if not.
07226 
07227     Purpose:    Initialise the device specific mechanisms for this render region.
07228 
07229     Notes:      We use a single static PaperRenderRegion to save creating one all the
07230                 time. However, this means that the check to see if we should use the
07231                 MainFrame window's palette only occurs once (in Create()), and unfortunately
07232                 this means that we always have the palette disabled due to the point at
07233                 which the original Create happens to occur in startup.
07234 
07235                 Thus, this InitDevice has been overridden to check if we should use a palette
07236                 *every* time the RndRgn is initialised for a redraw, so that it will use the
07237                 palette in the same way as the render regions which are created when needed.
07238 
07239     Errors:     Same as base class.
07240     SeeAlso:    OSRenderRegion::InitDevice
07241 
07242 ********************************************************************************************/
07243 
07244 BOOL PaperRenderRegion::InitDevice()
07245 {
07246     // As we use a static PaperRenderRegion, it has to check every time we go to use it if
07247     // we should be using a palette - otherwise, we get the wrong idea when we're Create'd
07248     // and then we fail to use the palette forever onwards!
07249     RFlags.UsePalette = (WantGDIPalette && PaletteManager::UsePalette()) ? TRUE : FALSE;
07250 
07251     // Call base class
07252     if (!OSRenderRegion::InitDevice())
07253         return FALSE;
07254 
07255     // All ok
07256     return TRUE;
07257 }
07258 
07259 /********************************************************************************************
07260 
07261 >   static wxSize OSRenderRegion::GetFixedDCPPI(wxDC &DC)
07262 
07263     Author:     Alex Bligh
07264     Created:    07/01/2006
07265 
07266     Returns:    DPI in both directions as a wxSize
07267 
07268     Purpose:    Fix up crazy wxWindows screen DPI calculation
07269 
07270     Notes:      wxWindows REALLY tries to calculate the screen DPI. By that I mean it
07271                 takes the width and depth of the screen (as specified somehow to gdk by
07272                 the user) in millimeters, and then divides by the pixels. Camelot needs
07273                 a fixed view of the world, so on any system 100% is the same number of
07274                 pixels. We use a FIXED 96dpi on Windows & Linux and, presently, on the Mac
07275                 too so that documents look identical on all platforms (render same area
07276                 of pixels so that comparative timings make sense).
07277 
07278     Errors:     -
07279     SeeAlso:    wxDC::GetPPI
07280 
07281 ********************************************************************************************/
07282 
07283 wxSize OSRenderRegion::GetFixedDCPPI(wxDC &DC)
07284 {
07285     // Set system defaults
07286 PORTNOTE("MacPort", "We should allow the user to configure this value on the mac, maybe on all platforms")
07287 #ifdef __WXMAC__
07288     wxSize PPI(96,96);
07289 #else
07290     wxSize PPI(96,96);
07291 #endif
07292 
07293     // If it's not a Screen DC or a PaintDC we MIGHT just believe it!   
07294     if (! ( DC.IsKindOf(CLASSINFO(wxScreenDC)) || DC.IsKindOf(CLASSINFO(wxPaintDC)) ))
07295         PPI=DC.GetPPI();
07296 
07297     return PPI;
07298 }

Generated on Sat Nov 10 03:48:45 2007 for Camelot by  doxygen 1.4.4