summaryrefslogtreecommitdiff
path: root/vcl/source/gdi/outdev.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'vcl/source/gdi/outdev.cxx')
-rw-r--r--vcl/source/gdi/outdev.cxx3493
1 files changed, 3493 insertions, 0 deletions
diff --git a/vcl/source/gdi/outdev.cxx b/vcl/source/gdi/outdev.cxx
new file mode 100644
index 000000000000..847a8d7a299a
--- /dev/null
+++ b/vcl/source/gdi/outdev.cxx
@@ -0,0 +1,3493 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_vcl.hxx"
+
+#include <tools/ref.hxx>
+#ifndef _SV_SVSYS_HXX
+#include <svsys.h>
+#endif
+#include <vcl/salgdi.hxx>
+#include <vcl/sallayout.hxx>
+#include <vcl/salframe.hxx>
+#include <vcl/salvd.hxx>
+#include <vcl/salprn.hxx>
+#include <tools/debug.hxx>
+#include <vcl/svdata.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/ctrl.hxx>
+#ifndef _POLY_HXX
+#include <tools/poly.hxx>
+#endif
+#include <vcl/region.hxx>
+#include <vcl/region.h>
+#include <vcl/virdev.hxx>
+#include <vcl/window.h>
+#include <vcl/window.hxx>
+#include <vcl/metaact.hxx>
+#include <vcl/gdimtf.hxx>
+#include <vcl/outdata.hxx>
+#include <vcl/print.hxx>
+#include <vcl/outdev.h>
+#include <vcl/outdev.hxx>
+#include <vcl/unowrap.hxx>
+#include <vcl/sysdata.hxx>
+
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/vector/b2dvector.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <basegfx/polygon/b2dlinegeometry.hxx>
+
+#include <com/sun/star/awt/XGraphics.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/rendering/XCanvas.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <vcl/unohelp.hxx>
+
+#include <numeric>
+
+using namespace ::com::sun::star;
+
+DBG_NAME( OutputDevice )
+DBG_NAME( Polygon )
+DBG_NAME( PolyPolygon )
+DBG_NAMEEX( Region )
+
+// -----------------------------------------------------------------------
+
+#ifdef DBG_UTIL
+const char* ImplDbgCheckOutputDevice( const void* pObj )
+{
+ DBG_TESTSOLARMUTEX();
+
+ const OutputDevice* pOutDev = (OutputDevice*)pObj;
+
+ if ( (pOutDev->GetOutDevType() != OUTDEV_DONTKNOW) &&
+ (pOutDev->GetOutDevType() != OUTDEV_WINDOW) &&
+ (pOutDev->GetOutDevType() != OUTDEV_PRINTER) &&
+ (pOutDev->GetOutDevType() != OUTDEV_VIRDEV) )
+ return "OutputDevice data overwrite";
+
+ return NULL;
+}
+#endif
+
+// =======================================================================
+
+#define OUTDEV_POLYPOLY_STACKBUF 32
+
+// =======================================================================
+
+struct ImplObjStack
+{
+ ImplObjStack* mpPrev;
+ MapMode* mpMapMode;
+ Region* mpClipRegion;
+ Color* mpLineColor;
+ Color* mpFillColor;
+ Font* mpFont;
+ Color* mpTextColor;
+ Color* mpTextFillColor;
+ Color* mpTextLineColor;
+ Color* mpOverlineColor;
+ Point* mpRefPoint;
+ TextAlign meTextAlign;
+ RasterOp meRasterOp;
+ ULONG mnTextLayoutMode;
+ LanguageType meTextLanguage;
+ USHORT mnFlags;
+};
+
+// -----------------------------------------------------------------------
+
+static void ImplDeleteObjStack( ImplObjStack* pObjStack )
+{
+ if ( pObjStack->mnFlags & PUSH_LINECOLOR )
+ {
+ if ( pObjStack->mpLineColor )
+ delete pObjStack->mpLineColor;
+ }
+ if ( pObjStack->mnFlags & PUSH_FILLCOLOR )
+ {
+ if ( pObjStack->mpFillColor )
+ delete pObjStack->mpFillColor;
+ }
+ if ( pObjStack->mnFlags & PUSH_FONT )
+ delete pObjStack->mpFont;
+ if ( pObjStack->mnFlags & PUSH_TEXTCOLOR )
+ delete pObjStack->mpTextColor;
+ if ( pObjStack->mnFlags & PUSH_TEXTFILLCOLOR )
+ {
+ if ( pObjStack->mpTextFillColor )
+ delete pObjStack->mpTextFillColor;
+ }
+ if ( pObjStack->mnFlags & PUSH_TEXTLINECOLOR )
+ {
+ if ( pObjStack->mpTextLineColor )
+ delete pObjStack->mpTextLineColor;
+ }
+ if ( pObjStack->mnFlags & PUSH_OVERLINECOLOR )
+ {
+ if ( pObjStack->mpOverlineColor )
+ delete pObjStack->mpOverlineColor;
+ }
+ if ( pObjStack->mnFlags & PUSH_MAPMODE )
+ {
+ if ( pObjStack->mpMapMode )
+ delete pObjStack->mpMapMode;
+ }
+ if ( pObjStack->mnFlags & PUSH_CLIPREGION )
+ {
+ if ( pObjStack->mpClipRegion )
+ delete pObjStack->mpClipRegion;
+ }
+ if ( pObjStack->mnFlags & PUSH_REFPOINT )
+ {
+ if ( pObjStack->mpRefPoint )
+ delete pObjStack->mpRefPoint;
+ }
+
+ delete pObjStack;
+}
+
+// -----------------------------------------------------------------------
+
+bool OutputDevice::ImplIsAntiparallel() const
+{
+ bool bRet = false;
+ if( ImplGetGraphics() )
+ {
+ if( ( (mpGraphics->GetLayout() & SAL_LAYOUT_BIDI_RTL) && ! IsRTLEnabled() ) ||
+ ( ! (mpGraphics->GetLayout() & SAL_LAYOUT_BIDI_RTL) && IsRTLEnabled() ) )
+ {
+ bRet = true;
+ }
+ }
+ return bRet;
+}
+
+// -----------------------------------------------------------------------
+
+
+bool OutputDevice::ImplSelectClipRegion( const Region& rRegion, SalGraphics* pGraphics )
+{
+ DBG_TESTSOLARMUTEX();
+
+ // TODO(Q3): Change from static to plain method - everybody's
+ // calling it with pOutDev=this!
+ // => done, but only with minimal changes for now => TODO
+ OutputDevice* const pOutDev = this;
+ if( !pGraphics )
+ {
+ if( !mpGraphics )
+ if( !ImplGetGraphics() )
+ return false;
+ pGraphics = mpGraphics;
+ }
+
+ if( rRegion.HasPolyPolygon()
+ && pGraphics->supportsOperation( OutDevSupport_B2DClip ) )
+ {
+ const ::basegfx::B2DPolyPolygon& rB2DPolyPolygon = rRegion.GetB2DPolyPolygon();
+ pGraphics->BeginSetClipRegion( 0 );
+ pGraphics->UnionClipRegion( rB2DPolyPolygon, pOutDev );
+ pGraphics->EndSetClipRegion();
+ return true;
+ }
+
+ long nX;
+ long nY;
+ long nWidth;
+ long nHeight;
+ ULONG nRectCount;
+ ImplRegionInfo aInfo;
+ BOOL bRegionRect;
+ BOOL bClipRegion = TRUE;
+ const BOOL bClipDeviceBounds( !pOutDev->GetPDFWriter()
+ && pOutDev->GetOutDevType() != OUTDEV_PRINTER );
+
+ nRectCount = rRegion.GetRectCount();
+ pGraphics->BeginSetClipRegion( nRectCount );
+ bRegionRect = rRegion.ImplGetFirstRect( aInfo, nX, nY, nWidth, nHeight );
+ if( bClipDeviceBounds )
+ {
+ // #b6520266# Perform actual rect clip against outdev
+ // dimensions, to generate empty clips whenever one of the
+ // values is completely off the device.
+ const long nOffX( pOutDev->mnOutOffX );
+ const long nOffY( pOutDev->mnOutOffY );
+ const long nDeviceWidth( pOutDev->GetOutputWidthPixel() );
+ const long nDeviceHeight( pOutDev->GetOutputHeightPixel() );
+ Rectangle aDeviceBounds( nOffX, nOffY,
+ nOffX+nDeviceWidth-1,
+ nOffY+nDeviceHeight-1 );
+ while ( bRegionRect )
+ {
+ // #i59315# Limit coordinates passed to sal layer to actual
+ // outdev dimensions - everything else bears the risk of
+ // overflowing internal coordinates (e.g. the 16 bit wire
+ // format of X11).
+ Rectangle aTmpRect(nX,nY,nX+nWidth-1,nY+nHeight-1);
+ aTmpRect.Intersection(aDeviceBounds);
+
+ if( !aTmpRect.IsEmpty() )
+ {
+ if ( !pGraphics->UnionClipRegion( aTmpRect.Left(),
+ aTmpRect.Top(),
+ aTmpRect.GetWidth(),
+ aTmpRect.GetHeight(),
+ pOutDev ) )
+ {
+ bClipRegion = FALSE;
+ }
+ }
+ else
+ {
+ // #i79850# Fake off-screen clip
+ if ( !pGraphics->UnionClipRegion( nDeviceWidth+1,
+ nDeviceHeight+1,
+ 1, 1,
+ pOutDev ) )
+ {
+ bClipRegion = FALSE;
+ }
+ }
+ DBG_ASSERT( bClipRegion, "OutputDevice::ImplSelectClipRegion() - can't create region" );
+ bRegionRect = rRegion.ImplGetNextRect( aInfo, nX, nY, nWidth, nHeight );
+ }
+ }
+ else
+ {
+ // #i65720# Actually, _don't_ clip anything on printer or PDF
+ // export, since output might be visible outside the specified
+ // device boundaries.
+ while ( bRegionRect )
+ {
+ if ( !pGraphics->UnionClipRegion( nX, nY, nWidth, nHeight, pOutDev ) )
+ bClipRegion = FALSE;
+ DBG_ASSERT( bClipRegion, "OutputDevice::ImplSelectClipRegion() - can't cerate region" );
+ bRegionRect = rRegion.ImplGetNextRect( aInfo, nX, nY, nWidth, nHeight );
+ }
+ }
+ pGraphics->EndSetClipRegion();
+ return bClipRegion;
+}
+
+
+// =======================================================================
+
+Polygon ImplSubdivideBezier( const Polygon& rPoly )
+{
+ Polygon aPoly;
+
+ // #100127# Use adaptive subdivide instead of fixed 25 segments
+ rPoly.AdaptiveSubdivide( aPoly );
+
+ return aPoly;
+}
+
+// =======================================================================
+
+PolyPolygon ImplSubdivideBezier( const PolyPolygon& rPolyPoly )
+{
+ USHORT i, nPolys = rPolyPoly.Count();
+ PolyPolygon aPolyPoly( nPolys );
+ for( i=0; i<nPolys; ++i )
+ aPolyPoly.Insert( ImplSubdivideBezier( rPolyPoly.GetObject(i) ) );
+
+ return aPolyPoly;
+}
+
+// =======================================================================
+
+// #100127# Extracted from OutputDevice::DrawPolyPolygon()
+void OutputDevice::ImplDrawPolyPolygon( USHORT nPoly, const PolyPolygon& rPolyPoly )
+{
+ // AW: This crashes on empty PolyPolygons, avoid that
+ if(!nPoly)
+ return;
+
+ sal_uInt32 aStackAry1[OUTDEV_POLYPOLY_STACKBUF];
+ PCONSTSALPOINT aStackAry2[OUTDEV_POLYPOLY_STACKBUF];
+ BYTE* aStackAry3[OUTDEV_POLYPOLY_STACKBUF];
+ sal_uInt32* pPointAry;
+ PCONSTSALPOINT* pPointAryAry;
+ const BYTE** pFlagAryAry;
+ USHORT i = 0, j = 0, last = 0;
+ BOOL bHaveBezier = sal_False;
+ if ( nPoly > OUTDEV_POLYPOLY_STACKBUF )
+ {
+ pPointAry = new sal_uInt32[nPoly];
+ pPointAryAry = new PCONSTSALPOINT[nPoly];
+ pFlagAryAry = new const BYTE*[nPoly];
+ }
+ else
+ {
+ pPointAry = aStackAry1;
+ pPointAryAry = aStackAry2;
+ pFlagAryAry = (const BYTE**)aStackAry3;
+ }
+ do
+ {
+ const Polygon& rPoly = rPolyPoly.GetObject( i );
+ USHORT nSize = rPoly.GetSize();
+ if ( nSize )
+ {
+ pPointAry[j] = nSize;
+ pPointAryAry[j] = (PCONSTSALPOINT)rPoly.GetConstPointAry();
+ pFlagAryAry[j] = rPoly.GetConstFlagAry();
+ last = i;
+
+ if( pFlagAryAry[j] )
+ bHaveBezier = sal_True;
+
+ ++j;
+ }
+
+ ++i;
+ }
+ while ( i < nPoly );
+
+ if ( j == 1 )
+ {
+ // #100127# Forward beziers to sal, if any
+ if( bHaveBezier )
+ {
+ if( !mpGraphics->DrawPolygonBezier( *pPointAry, *pPointAryAry, *pFlagAryAry, this ) )
+ {
+ Polygon aPoly = ImplSubdivideBezier( rPolyPoly.GetObject( last ) );
+ mpGraphics->DrawPolygon( aPoly.GetSize(), (const SalPoint*)aPoly.GetConstPointAry(), this );
+ }
+ }
+ else
+ {
+ mpGraphics->DrawPolygon( *pPointAry, *pPointAryAry, this );
+ }
+ }
+ else
+ {
+ // #100127# Forward beziers to sal, if any
+ if( bHaveBezier )
+ {
+ if( !mpGraphics->DrawPolyPolygonBezier( j, pPointAry, pPointAryAry, pFlagAryAry, this ) )
+ {
+ PolyPolygon aPolyPoly = ImplSubdivideBezier( rPolyPoly );
+ ImplDrawPolyPolygon( aPolyPoly.Count(), aPolyPoly );
+ }
+ }
+ else
+ {
+ mpGraphics->DrawPolyPolygon( j, pPointAry, pPointAryAry, this );
+ }
+ }
+
+ if ( pPointAry != aStackAry1 )
+ {
+ delete[] pPointAry;
+ delete[] pPointAryAry;
+ delete[] pFlagAryAry;
+ }
+}
+
+// =======================================================================
+
+OutputDevice::OutputDevice() :
+ maRegion( REGION_NULL ),
+ maFillColor( COL_WHITE ),
+ maTextLineColor( COL_TRANSPARENT ),
+ maSettings( Application::GetSettings() )
+{
+ DBG_CTOR( OutputDevice, ImplDbgCheckOutputDevice );
+
+ mpGraphics = NULL;
+ mpUnoGraphicsList = NULL;
+ mpPrevGraphics = NULL;
+ mpNextGraphics = NULL;
+ mpMetaFile = NULL;
+ mpFontEntry = NULL;
+ mpFontCache = NULL;
+ mpFontList = NULL;
+ mpGetDevFontList = NULL;
+ mpGetDevSizeList = NULL;
+ mpObjStack = NULL;
+ mpOutDevData = NULL;
+ mpPDFWriter = NULL;
+ mpAlphaVDev = NULL;
+ mpExtOutDevData = NULL;
+ mnOutOffX = 0;
+ mnOutOffY = 0;
+ mnOutWidth = 0;
+ mnOutHeight = 0;
+ mnDPIX = 0;
+ mnDPIY = 0;
+ mnTextOffX = 0;
+ mnTextOffY = 0;
+ mnOutOffOrigX = 0;
+ mnOutOffLogicX = 0;
+ mnOutOffOrigY = 0;
+ mnOutOffLogicY = 0;
+ mnEmphasisAscent = 0;
+ mnEmphasisDescent = 0;
+ mnDrawMode = 0;
+ mnTextLayoutMode = TEXT_LAYOUT_DEFAULT;
+ if( Application::GetSettings().GetLayoutRTL() ) //#i84553# tip BiDi preference to RTL
+ mnTextLayoutMode = TEXT_LAYOUT_BIDI_RTL | TEXT_LAYOUT_TEXTORIGIN_LEFT;
+ meOutDevType = OUTDEV_DONTKNOW;
+ meOutDevViewType = OUTDEV_VIEWTYPE_DONTKNOW;
+ mbMap = FALSE;
+ mbMapIsDefault = TRUE;
+ mbClipRegion = FALSE;
+ mbBackground = FALSE;
+ mbOutput = TRUE;
+ mbDevOutput = FALSE;
+ mbOutputClipped = FALSE;
+ maTextColor = Color( COL_BLACK );
+ maOverlineColor = Color( COL_TRANSPARENT );
+ meTextAlign = maFont.GetAlign();
+ meRasterOp = ROP_OVERPAINT;
+ mnAntialiasing = 0;
+ meTextLanguage = 0; // TODO: get default from configuration?
+ mbLineColor = TRUE;
+ mbFillColor = TRUE;
+ mbInitLineColor = TRUE;
+ mbInitFillColor = TRUE;
+ mbInitFont = TRUE;
+ mbInitTextColor = TRUE;
+ mbInitClipRegion = TRUE;
+ mbClipRegionSet = FALSE;
+ mbKerning = FALSE;
+ mbNewFont = TRUE;
+ mbTextLines = FALSE;
+ mbTextSpecial = FALSE;
+ mbRefPoint = FALSE;
+ mbEnableRTL = FALSE; // mirroring must be explicitly allowed (typically for windows only)
+
+ // struct ImplMapRes
+ maMapRes.mnMapOfsX = 0;
+ maMapRes.mnMapOfsY = 0;
+ maMapRes.mnMapScNumX = 1;
+ maMapRes.mnMapScNumY = 1;
+ maMapRes.mnMapScDenomX = 1;
+ maMapRes.mnMapScDenomY = 1;
+ // struct ImplThresholdRes
+ maThresRes.mnThresLogToPixX = 0;
+ maThresRes.mnThresLogToPixY = 0;
+ maThresRes.mnThresPixToLogX = 0;
+ maThresRes.mnThresPixToLogY = 0;
+}
+
+// -----------------------------------------------------------------------
+
+OutputDevice::~OutputDevice()
+{
+ DBG_DTOR( OutputDevice, ImplDbgCheckOutputDevice );
+
+ if ( GetUnoGraphicsList() )
+ {
+ UnoWrapperBase* pWrapper = Application::GetUnoWrapper( FALSE );
+ if ( pWrapper )
+ pWrapper->ReleaseAllGraphics( this );
+ delete mpUnoGraphicsList;
+ mpUnoGraphicsList = NULL;
+ }
+
+ if ( mpOutDevData )
+ ImplDeInitOutDevData();
+
+ ImplObjStack* pData = mpObjStack;
+ if ( pData )
+ {
+ DBG_ERRORFILE( "OutputDevice::~OutputDevice(): OutputDevice::Push() calls != OutputDevice::Pop() calls" );
+ while ( pData )
+ {
+ ImplObjStack* pTemp = pData;
+ pData = pData->mpPrev;
+ ImplDeleteObjStack( pTemp );
+ }
+ }
+
+ // release the active font instance
+ if( mpFontEntry )
+ mpFontCache->Release( mpFontEntry );
+ // remove cached results of GetDevFontList/GetDevSizeList
+ // TODO: use smart pointers for them
+ if( mpGetDevFontList )
+ delete mpGetDevFontList;
+ if( mpGetDevSizeList )
+ delete mpGetDevSizeList;
+
+ // release ImplFontCache specific to this OutputDevice
+ // TODO: refcount ImplFontCache
+ if( mpFontCache
+ && (mpFontCache != ImplGetSVData()->maGDIData.mpScreenFontCache)
+ && (ImplGetSVData()->maGDIData.mpScreenFontCache != NULL) )
+ {
+ delete mpFontCache;
+ mpFontCache = NULL;
+ }
+
+ // release ImplFontList specific to this OutputDevice
+ // TODO: refcount ImplFontList
+ if( mpFontList
+ && (mpFontList != ImplGetSVData()->maGDIData.mpScreenFontList)
+ && (ImplGetSVData()->maGDIData.mpScreenFontList != NULL) )
+ {
+ mpFontList->Clear();
+ delete mpFontList;
+ mpFontList = NULL;
+ }
+
+ delete mpAlphaVDev;
+}
+
+bool OutputDevice::supportsOperation( OutDevSupportType eType ) const
+{
+ if( !mpGraphics )
+ if( !ImplGetGraphics() )
+ return false;
+ const bool bHasSupport = mpGraphics->supportsOperation( eType );
+ return bHasSupport;
+}
+
+// -----------------------------------------------------------------------
+
+void OutputDevice::EnableRTL( BOOL bEnable )
+{
+ mbEnableRTL = (bEnable != 0);
+ if( meOutDevType == OUTDEV_VIRDEV )
+ {
+ // virdevs default to not mirroring, they will only be set to mirroring
+ // under rare circumstances in the UI, eg the valueset control
+ // because each virdev has its own SalGraphics we can safely switch the SalGraphics here
+ // ...hopefully
+ if( ImplGetGraphics() )
+ mpGraphics->SetLayout( mbEnableRTL ? SAL_LAYOUT_BIDI_RTL : 0 );
+ }
+
+ // convenience: for controls also switch layout mode
+ if( dynamic_cast<Control*>(this) != 0 )
+ SetLayoutMode( bEnable ? TEXT_LAYOUT_BIDI_RTL | TEXT_LAYOUT_TEXTORIGIN_LEFT : TEXT_LAYOUT_BIDI_LTR | TEXT_LAYOUT_TEXTORIGIN_LEFT);
+
+ Window* pWin = dynamic_cast<Window*>(this);
+ if( pWin )
+ pWin->StateChanged( STATE_CHANGE_MIRRORING );
+
+ if( mpAlphaVDev )
+ mpAlphaVDev->EnableRTL( bEnable );
+}
+
+BOOL OutputDevice::ImplHasMirroredGraphics()
+{
+ // HOTFIX for #i55719#
+ if( meOutDevType == OUTDEV_PRINTER )
+ return FALSE;
+
+ return ( ImplGetGraphics() && (mpGraphics->GetLayout() & SAL_LAYOUT_BIDI_RTL) );
+}
+
+// note: the coordiantes to be remirrored are in frame coordiantes !
+
+void OutputDevice::ImplReMirror( Point &rPoint ) const
+{
+ rPoint.X() = mnOutOffX + mnOutWidth - 1 - rPoint.X() + mnOutOffX;
+}
+void OutputDevice::ImplReMirror( Rectangle &rRect ) const
+{
+ long nWidth = rRect.nRight - rRect.nLeft;
+
+ //long lc_x = rRect.nLeft - mnOutOffX; // normalize
+ //lc_x = mnOutWidth - nWidth - 1 - lc_x; // mirror
+ //rRect.nLeft = lc_x + mnOutOffX; // re-normalize
+
+ rRect.nLeft = mnOutOffX + mnOutWidth - nWidth - 1 - rRect.nLeft + mnOutOffX;
+ rRect.nRight = rRect.nLeft + nWidth;
+}
+void OutputDevice::ImplReMirror( Region &rRegion ) const
+{
+ long nX;
+ long nY;
+ long nWidth;
+ long nHeight;
+ ImplRegionInfo aInfo;
+ BOOL bRegionRect;
+ Region aMirroredRegion;
+
+ bRegionRect = rRegion.ImplGetFirstRect( aInfo, nX, nY, nWidth, nHeight );
+ while ( bRegionRect )
+ {
+ Rectangle aRect( Point(nX, nY), Size(nWidth, nHeight) );
+ ImplReMirror( aRect );
+ aMirroredRegion.Union( aRect );
+ bRegionRect = rRegion.ImplGetNextRect( aInfo, nX, nY, nWidth, nHeight );
+ }
+ rRegion = aMirroredRegion;
+}
+
+
+// -----------------------------------------------------------------------
+
+int OutputDevice::ImplGetGraphics() const
+{
+ DBG_TESTSOLARMUTEX();
+
+ if ( mpGraphics )
+ return TRUE;
+
+ mbInitLineColor = TRUE;
+ mbInitFillColor = TRUE;
+ mbInitFont = TRUE;
+ mbInitTextColor = TRUE;
+ mbInitClipRegion = TRUE;
+
+ ImplSVData* pSVData = ImplGetSVData();
+ if ( meOutDevType == OUTDEV_WINDOW )
+ {
+ Window* pWindow = (Window*)this;
+
+ mpGraphics = pWindow->mpWindowImpl->mpFrame->GetGraphics();
+ // try harder if no wingraphics was available directly
+ if ( !mpGraphics )
+ {
+ // find another output device in the same frame
+ OutputDevice* pReleaseOutDev = pSVData->maGDIData.mpLastWinGraphics;
+ while ( pReleaseOutDev )
+ {
+ if ( ((Window*)pReleaseOutDev)->mpWindowImpl->mpFrame == pWindow->mpWindowImpl->mpFrame )
+ break;
+ pReleaseOutDev = pReleaseOutDev->mpPrevGraphics;
+ }
+
+ if ( pReleaseOutDev )
+ {
+ // steal the wingraphics from the other outdev
+ mpGraphics = pReleaseOutDev->mpGraphics;
+ pReleaseOutDev->ImplReleaseGraphics( FALSE );
+ }
+ else
+ {
+ // if needed retry after releasing least recently used wingraphics
+ while ( !mpGraphics )
+ {
+ if ( !pSVData->maGDIData.mpLastWinGraphics )
+ break;
+ pSVData->maGDIData.mpLastWinGraphics->ImplReleaseGraphics();
+ mpGraphics = pWindow->mpWindowImpl->mpFrame->GetGraphics();
+ }
+ }
+ }
+
+ // update global LRU list of wingraphics
+ if ( mpGraphics )
+ {
+ mpNextGraphics = pSVData->maGDIData.mpFirstWinGraphics;
+ pSVData->maGDIData.mpFirstWinGraphics = const_cast<OutputDevice*>(this);
+ if ( mpNextGraphics )
+ mpNextGraphics->mpPrevGraphics = const_cast<OutputDevice*>(this);
+ if ( !pSVData->maGDIData.mpLastWinGraphics )
+ pSVData->maGDIData.mpLastWinGraphics = const_cast<OutputDevice*>(this);
+ }
+ }
+ else if ( meOutDevType == OUTDEV_VIRDEV )
+ {
+ const VirtualDevice* pVirDev = (const VirtualDevice*)this;
+
+ if ( pVirDev->mpVirDev )
+ {
+ mpGraphics = pVirDev->mpVirDev->GetGraphics();
+ // if needed retry after releasing least recently used virtual device graphics
+ while ( !mpGraphics )
+ {
+ if ( !pSVData->maGDIData.mpLastVirGraphics )
+ break;
+ pSVData->maGDIData.mpLastVirGraphics->ImplReleaseGraphics();
+ mpGraphics = pVirDev->mpVirDev->GetGraphics();
+ }
+ // update global LRU list of virtual device graphics
+ if ( mpGraphics )
+ {
+ mpNextGraphics = pSVData->maGDIData.mpFirstVirGraphics;
+ pSVData->maGDIData.mpFirstVirGraphics = const_cast<OutputDevice*>(this);
+ if ( mpNextGraphics )
+ mpNextGraphics->mpPrevGraphics = const_cast<OutputDevice*>(this);
+ if ( !pSVData->maGDIData.mpLastVirGraphics )
+ pSVData->maGDIData.mpLastVirGraphics = const_cast<OutputDevice*>(this);
+ }
+ }
+ }
+ else if ( meOutDevType == OUTDEV_PRINTER )
+ {
+ const Printer* pPrinter = (const Printer*)this;
+
+ if ( pPrinter->mpJobGraphics )
+ mpGraphics = pPrinter->mpJobGraphics;
+ else if ( pPrinter->mpDisplayDev )
+ {
+ const VirtualDevice* pVirDev = pPrinter->mpDisplayDev;
+ mpGraphics = pVirDev->mpVirDev->GetGraphics();
+ // if needed retry after releasing least recently used virtual device graphics
+ while ( !mpGraphics )
+ {
+ if ( !pSVData->maGDIData.mpLastVirGraphics )
+ break;
+ pSVData->maGDIData.mpLastVirGraphics->ImplReleaseGraphics();
+ mpGraphics = pVirDev->mpVirDev->GetGraphics();
+ }
+ // update global LRU list of virtual device graphics
+ if ( mpGraphics )
+ {
+ mpNextGraphics = pSVData->maGDIData.mpFirstVirGraphics;
+ pSVData->maGDIData.mpFirstVirGraphics = const_cast<OutputDevice*>(this);
+ if ( mpNextGraphics )
+ mpNextGraphics->mpPrevGraphics = const_cast<OutputDevice*>(this);
+ if ( !pSVData->maGDIData.mpLastVirGraphics )
+ pSVData->maGDIData.mpLastVirGraphics = const_cast<OutputDevice*>(this);
+ }
+ }
+ else
+ {
+ mpGraphics = pPrinter->mpInfoPrinter->GetGraphics();
+ // if needed retry after releasing least recently used printer graphics
+ while ( !mpGraphics )
+ {
+ if ( !pSVData->maGDIData.mpLastPrnGraphics )
+ break;
+ pSVData->maGDIData.mpLastPrnGraphics->ImplReleaseGraphics();
+ mpGraphics = pPrinter->mpInfoPrinter->GetGraphics();
+ }
+ // update global LRU list of printer graphics
+ if ( mpGraphics )
+ {
+ mpNextGraphics = pSVData->maGDIData.mpFirstPrnGraphics;
+ pSVData->maGDIData.mpFirstPrnGraphics = const_cast<OutputDevice*>(this);
+ if ( mpNextGraphics )
+ mpNextGraphics->mpPrevGraphics = const_cast<OutputDevice*>(this);
+ if ( !pSVData->maGDIData.mpLastPrnGraphics )
+ pSVData->maGDIData.mpLastPrnGraphics = const_cast<OutputDevice*>(this);
+ }
+ }
+ }
+
+ if ( mpGraphics )
+ {
+ mpGraphics->SetXORMode( (ROP_INVERT == meRasterOp) || (ROP_XOR == meRasterOp), ROP_INVERT == meRasterOp );
+ mpGraphics->setAntiAliasB2DDraw(mnAntialiasing & ANTIALIASING_ENABLE_B2DDRAW);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+// -----------------------------------------------------------------------
+
+void OutputDevice::ImplReleaseGraphics( BOOL bRelease )
+{
+ DBG_TESTSOLARMUTEX();
+
+ if ( !mpGraphics )
+ return;
+
+ // release the fonts of the physically released graphics device
+ if( bRelease )
+ {
+#ifndef UNX
+ // HACK to fix an urgent P1 printing issue fast
+ // WinSalPrinter does not respect GetGraphics/ReleaseGraphics conventions
+ // so Printer::mpGraphics often points to a dead WinSalGraphics
+ // TODO: fix WinSalPrinter's GetGraphics/ReleaseGraphics handling
+ if( meOutDevType != OUTDEV_PRINTER )
+#endif
+ mpGraphics->ReleaseFonts();
+
+ mbNewFont = true;
+ mbInitFont = true;
+
+ if ( mpFontEntry )
+ {
+ mpFontCache->Release( mpFontEntry );
+ mpFontEntry = NULL;
+ }
+
+ if ( mpGetDevFontList )
+ {
+ delete mpGetDevFontList;
+ mpGetDevFontList = NULL;
+ }
+
+ if ( mpGetDevSizeList )
+ {
+ delete mpGetDevSizeList;
+ mpGetDevSizeList = NULL;
+ }
+ }
+
+ ImplSVData* pSVData = ImplGetSVData();
+ if ( meOutDevType == OUTDEV_WINDOW )
+ {
+ Window* pWindow = (Window*)this;
+
+ if ( bRelease )
+ pWindow->mpWindowImpl->mpFrame->ReleaseGraphics( mpGraphics );
+ // remove from global LRU list of window graphics
+ if ( mpPrevGraphics )
+ mpPrevGraphics->mpNextGraphics = mpNextGraphics;
+ else
+ pSVData->maGDIData.mpFirstWinGraphics = mpNextGraphics;
+ if ( mpNextGraphics )
+ mpNextGraphics->mpPrevGraphics = mpPrevGraphics;
+ else
+ pSVData->maGDIData.mpLastWinGraphics = mpPrevGraphics;
+ }
+ else if ( meOutDevType == OUTDEV_VIRDEV )
+ {
+ VirtualDevice* pVirDev = (VirtualDevice*)this;
+
+ if ( bRelease )
+ pVirDev->mpVirDev->ReleaseGraphics( mpGraphics );
+ // remove from global LRU list of virtual device graphics
+ if ( mpPrevGraphics )
+ mpPrevGraphics->mpNextGraphics = mpNextGraphics;
+ else
+ pSVData->maGDIData.mpFirstVirGraphics = mpNextGraphics;
+ if ( mpNextGraphics )
+ mpNextGraphics->mpPrevGraphics = mpPrevGraphics;
+ else
+ pSVData->maGDIData.mpLastVirGraphics = mpPrevGraphics;
+ }
+ else if ( meOutDevType == OUTDEV_PRINTER )
+ {
+ Printer* pPrinter = (Printer*)this;
+
+ if ( !pPrinter->mpJobGraphics )
+ {
+ if ( pPrinter->mpDisplayDev )
+ {
+ VirtualDevice* pVirDev = pPrinter->mpDisplayDev;
+ if ( bRelease )
+ pVirDev->mpVirDev->ReleaseGraphics( mpGraphics );
+ // remove from global LRU list of virtual device graphics
+ if ( mpPrevGraphics )
+ mpPrevGraphics->mpNextGraphics = mpNextGraphics;
+ else
+ pSVData->maGDIData.mpFirstVirGraphics = mpNextGraphics;
+ if ( mpNextGraphics )
+ mpNextGraphics->mpPrevGraphics = mpPrevGraphics;
+ else
+ pSVData->maGDIData.mpLastVirGraphics = mpPrevGraphics;
+ }
+ else
+ {
+ if ( bRelease )
+ pPrinter->mpInfoPrinter->ReleaseGraphics( mpGraphics );
+ // remove from global LRU list of printer graphics
+ if ( mpPrevGraphics )
+ mpPrevGraphics->mpNextGraphics = mpNextGraphics;
+ else
+ pSVData->maGDIData.mpFirstPrnGraphics = mpNextGraphics;
+ if ( mpNextGraphics )
+ mpNextGraphics->mpPrevGraphics = mpPrevGraphics;
+ else
+ pSVData->maGDIData.mpLastPrnGraphics = mpPrevGraphics;
+ }
+ }
+ }
+
+ mpGraphics = NULL;
+ mpPrevGraphics = NULL;
+ mpNextGraphics = NULL;
+}
+
+// -----------------------------------------------------------------------
+
+void OutputDevice::ImplInitOutDevData()
+{
+ if ( !mpOutDevData )
+ {
+ mpOutDevData = new ImplOutDevData;
+ mpOutDevData->mpRotateDev = NULL;
+ mpOutDevData->mpRecordLayout = NULL;
+
+ // #i75163#
+ mpOutDevData->mpViewTransform = NULL;
+ mpOutDevData->mpInverseViewTransform = NULL;
+ }
+}
+
+// -----------------------------------------------------------------------
+
+// #i75163#
+void OutputDevice::ImplInvalidateViewTransform()
+{
+ if(mpOutDevData)
+ {
+ if(mpOutDevData->mpViewTransform)
+ {
+ delete mpOutDevData->mpViewTransform;
+ mpOutDevData->mpViewTransform = NULL;
+ }
+
+ if(mpOutDevData->mpInverseViewTransform)
+ {
+ delete mpOutDevData->mpInverseViewTransform;
+ mpOutDevData->mpInverseViewTransform = NULL;
+ }
+ }
+}
+
+// -----------------------------------------------------------------------
+
+BOOL OutputDevice::ImplIsRecordLayout() const
+{
+ return mpOutDevData && mpOutDevData->mpRecordLayout;
+}
+
+// -----------------------------------------------------------------------
+
+void OutputDevice::ImplDeInitOutDevData()
+{
+ if ( mpOutDevData )
+ {
+ if ( mpOutDevData->mpRotateDev )
+ delete mpOutDevData->mpRotateDev;
+
+ // #i75163#
+ ImplInvalidateViewTransform();
+
+ delete mpOutDevData;
+ }
+}
+
+// -----------------------------------------------------------------------
+
+void OutputDevice::ImplInitLineColor()
+{
+ DBG_TESTSOLARMUTEX();
+
+ if( mbLineColor )
+ {
+ if( ROP_0 == meRasterOp )
+ mpGraphics->SetROPLineColor( SAL_ROP_0 );
+ else if( ROP_1 == meRasterOp )
+ mpGraphics->SetROPLineColor( SAL_ROP_1 );
+ else if( ROP_INVERT == meRasterOp )
+ mpGraphics->SetROPLineColor( SAL_ROP_INVERT );
+ else
+ mpGraphics->SetLineColor( ImplColorToSal( maLineColor ) );
+ }
+ else
+ mpGraphics->SetLineColor();
+
+ mbInitLineColor = FALSE;
+}
+
+// -----------------------------------------------------------------------
+
+void OutputDevice::ImplInitFillColor()
+{
+ DBG_TESTSOLARMUTEX();
+
+ if( mbFillColor )
+ {
+ if( ROP_0 == meRasterOp )
+ mpGraphics->SetROPFillColor( SAL_ROP_0 );
+ else if( ROP_1 == meRasterOp )
+ mpGraphics->SetROPFillColor( SAL_ROP_1 );
+ else if( ROP_INVERT == meRasterOp )
+ mpGraphics->SetROPFillColor( SAL_ROP_INVERT );
+ else
+ mpGraphics->SetFillColor( ImplColorToSal( maFillColor ) );
+ }
+ else
+ mpGraphics->SetFillColor();
+
+ mbInitFillColor = FALSE;
+}
+
+// -----------------------------------------------------------------------
+
+void OutputDevice::ImplInitClipRegion()
+{
+ DBG_TESTSOLARMUTEX();
+
+ if ( GetOutDevType() == OUTDEV_WINDOW )
+ {
+ Window* pWindow = (Window*)this;
+ Region aRegion;
+
+ // Hintergrund-Sicherung zuruecksetzen
+ if ( pWindow->mpWindowImpl->mpFrameData->mpFirstBackWin )
+ pWindow->ImplInvalidateAllOverlapBackgrounds();
+ if ( pWindow->mpWindowImpl->mbInPaint )
+ aRegion = *(pWindow->mpWindowImpl->mpPaintRegion);
+ else
+ {
+ aRegion = *(pWindow->ImplGetWinChildClipRegion());
+ // --- RTL -- only this region is in frame coordinates, so re-mirror it
+ // the mpWindowImpl->mpPaintRegion above is already correct (see ImplCallPaint()) !
+ if( ImplIsAntiparallel() )
+ ImplReMirror ( aRegion );
+ }
+ if ( mbClipRegion )
+ aRegion.Intersect( ImplPixelToDevicePixel( maRegion ) );
+ if ( aRegion.IsEmpty() )
+ mbOutputClipped = TRUE;
+ else
+ {
+ mbOutputClipped = FALSE;
+ ImplSelectClipRegion( aRegion );
+ }
+ mbClipRegionSet = TRUE;
+ }
+ else
+ {
+ if ( mbClipRegion )
+ {
+ if ( maRegion.IsEmpty() )
+ mbOutputClipped = TRUE;
+ else
+ {
+ mbOutputClipped = FALSE;
+ ImplSelectClipRegion(
+ // #102532# Respect output offset also for clip region
+ ImplPixelToDevicePixel( maRegion ) );
+ }
+
+ mbClipRegionSet = TRUE;
+ }
+ else
+ {
+ if ( mbClipRegionSet )
+ {
+ mpGraphics->ResetClipRegion();
+ mbClipRegionSet = FALSE;
+ }
+
+ mbOutputClipped = FALSE;
+ }
+ }
+
+ mbInitClipRegion = FALSE;
+}
+
+// -----------------------------------------------------------------------
+
+void OutputDevice::ImplSetClipRegion( const Region* pRegion )
+{
+ DBG_TESTSOLARMUTEX();
+
+ if ( !pRegion )
+ {
+ if ( mbClipRegion )
+ {
+ maRegion = Region( REGION_NULL );
+ mbClipRegion = FALSE;
+ mbInitClipRegion = TRUE;
+ }
+ }
+ else
+ {
+ maRegion = *pRegion;
+ mbClipRegion = TRUE;
+ mbInitClipRegion = TRUE;
+ }
+}
+
+// -----------------------------------------------------------------------
+
+namespace
+{
+ inline int iround( float x )
+ {
+ union
+ {
+ float f;
+ sal_Int32 i;
+ };
+ f = x;
+ sal_Int32 exponent = (127 + 31) - ((i >> 23) & 0xFF);
+ sal_Int32 r = ((sal_Int32(i) << 8) | (1U << 31)) >> exponent;
+ r &= ((exponent - 32) >> 31);
+ sal_Int32 sign = i >> 31;
+ return r = (r ^ sign) - sign;
+ }
+
+ inline int floorDiv(int a, int b)
+ {
+ if(b == 0)
+ return 0x80000000;
+ if(a >= 0)
+ return a / b;
+ int q = -(-a / b); // quotient
+ int r = -a % b; // remainder
+ if(r)
+ q--;
+ return q;
+ }
+
+ inline int floorMod( int a, int b )
+ {
+ if(b == 0)
+ return 0x80000000;
+ if(a >= 0)
+ return a % b;
+ int r = -a % b; // remainder
+ if(r)
+ r = b - r;
+ return r;
+ }
+
+ inline int ceilDiv( int a, int b )
+ {
+ if(b == 0)
+ return 0x80000000;
+ a += - 1 + b;
+ if(a >= 0)
+ return a / b;
+ int q = -(-a / b); // quotient
+ int r = -a % b; // remainder
+ if(r)
+ q--;
+ return q;
+ }
+
+ inline int ceilMod( int a, int b )
+ {
+ if(b == 0)
+ return 0x80000000;
+ a += - 1 + b;
+ if(a >= 0)
+ return (a % b) + 1 - b;
+ int r = -a % b;
+ if(r)
+ r = b - r;
+ return r + 1 - b;
+ }
+
+ inline int ceilFix4(int x) { return (x + 0xF) & 0xFFFFFFF0; }
+
+ struct vertex
+ {
+ float x,y;
+ inline vertex( const Point &p )
+ : x((float)p.getX()),y((float)p.getY()) {}
+ };
+
+ template<class T> inline void swap(T &a, T &b) { T t=a; a=b; b=t; }
+
+ class SpanIterator
+ {
+ public:
+
+ SpanIterator( sal_Int32 *pTable, size_t dwPitch, sal_Int32 dwNumScanlines );
+ std::pair<sal_Int32,sal_Int32> GetNextSpan( void );
+ sal_Int32 GetNumRemainingScanlines( void );
+ sal_Int32 GetNumEqualScanlines( void );
+ SpanIterator &operator++ ();
+ SpanIterator &Skip( sal_Int32 dwNumScanlines );
+ sal_Int32 GetRemainingSpans( void ) const { return maNumSpans; }
+
+ private:
+
+ sal_Int32 *mpTable;
+ sal_Int32 *mpSpanArray;
+ sal_Int32 maNumSpans;
+ sal_Int32 maRemainingScanlines;
+ size_t maPitch;
+ };
+
+ inline SpanIterator::SpanIterator( sal_Int32 *pTable, size_t dwPitch, sal_Int32 dwNumScanlines )
+ : mpTable(pTable),maRemainingScanlines(dwNumScanlines),maPitch(dwPitch)
+ {
+ sal_Int32 *pNumSpans = mpTable;
+ mpSpanArray = reinterpret_cast<sal_Int32 *>(pNumSpans+2);
+ maNumSpans = *pNumSpans;
+ }
+
+ inline SpanIterator &SpanIterator::operator++ ()
+ {
+ --maRemainingScanlines;
+ mpTable += maPitch;
+ sal_Int32 *pNumSpans = mpTable;
+ mpSpanArray = reinterpret_cast<sal_Int32 *>(pNumSpans+2);
+ maNumSpans = *pNumSpans;
+ return (*this);
+ }
+
+ inline SpanIterator &SpanIterator::Skip( sal_Int32 dwNumScanlines )
+ {
+ // don't skip more scanlines than there are...
+ if(dwNumScanlines > maRemainingScanlines)
+ dwNumScanlines = maRemainingScanlines;
+
+ // skip in one fellow swoop...
+ maRemainingScanlines -= dwNumScanlines;
+ mpTable += maPitch * dwNumScanlines;
+
+ // initialize necessary query fields...
+ sal_Int32 *pNumSpans = mpTable;
+ mpSpanArray = reinterpret_cast<sal_Int32 *>(pNumSpans+2);
+ maNumSpans = *pNumSpans;
+ return (*this);
+ }
+
+ inline std::pair<sal_Int32,sal_Int32> SpanIterator::GetNextSpan( void )
+ {
+ sal_Int32 x(0);
+ sal_Int32 w(0);
+ if(maNumSpans)
+ {
+ x = *mpSpanArray++;
+ w = *mpSpanArray++;
+ --maNumSpans;
+ }
+ return std::pair<sal_Int32,sal_Int32>(x,w);
+ }
+
+ inline sal_Int32 SpanIterator::GetNumEqualScanlines( void )
+ {
+ return mpTable[1];
+ }
+
+ inline sal_Int32 SpanIterator::GetNumRemainingScanlines( void )
+ {
+ return maRemainingScanlines;
+ }
+
+ class ScanlineContainer
+ {
+
+ public:
+
+ ScanlineContainer( sal_uInt32 dwNumScanlines,
+ sal_uInt32 dwNumSpansPerScanline );
+
+ ~ScanlineContainer( void );
+
+ void InsertSpan( sal_Int32 y, sal_Int32 lx, sal_Int32 rx );
+
+ SpanIterator Iterate( void ) const { return SpanIterator(mpTable,maPitch,maNumScanlines); }
+
+ inline sal_uInt32 GetNumSpans( void ) const { return maNumberOfSpans; }
+
+ void Consolidate( void );
+
+ private:
+
+ // the span table will assist in determinate exactly how many clipping
+ // regions [that is *spans*] we will end up with.
+ // the counter for this purpose is right ahead.
+ sal_uInt32 maNumberOfSpans;
+
+ struct span
+ {
+ sal_Int32 x;
+ sal_Int32 w;
+ };
+
+ sal_uInt32 maNumScanlines;
+ sal_uInt32 maNumSpansPerScanline;
+ sal_Int32 *mpTable;
+ size_t maPitch;
+ };
+
+ ScanlineContainer::ScanlineContainer( sal_uInt32 dwNumScanlines,
+ sal_uInt32 dwNumSpansPerScanline ) : maNumScanlines(dwNumScanlines),
+ maNumSpansPerScanline(dwNumSpansPerScanline)
+ {
+ // #128002# add one scanline buffer at the end, as
+ // SpanIterator::Skip reads two bytes past the end.
+ ++dwNumScanlines;
+
+ // since each triangle could possibly add another span
+ // we can calculate the upper limit by [num scanlines * num triangles].
+ const sal_uInt32 dwNumPossibleRegions = dwNumScanlines*dwNumSpansPerScanline;
+
+ // calculate the number of bytes the span table will consume
+ const size_t dwTableSize = dwNumPossibleRegions*sizeof(span)+dwNumScanlines*(sizeof(sal_Int32)<<1);
+
+ // allocate the span table [on the stack]
+ mpTable = static_cast<sal_Int32 *>(rtl_allocateMemory(dwTableSize));
+
+ // calculate the table pitch, that is how many int's do i need to get from a scanline to the next.
+ maPitch = (dwNumSpansPerScanline*sizeof(span)/sizeof(sal_Int32))+2;
+
+ // we need to initialize the table here.
+ // the first *int* on each scanline tells us how many spans are on it.
+ sal_Int32 *pNumSpans = mpTable;
+ for(unsigned int i=0; i<dwNumScanlines; ++i)
+ {
+ pNumSpans[0] = 0;
+ pNumSpans[1] = 0;
+ pNumSpans += maPitch;
+ }
+
+ maNumberOfSpans = 0;
+ }
+
+ ScanlineContainer::~ScanlineContainer( void )
+ {
+ rtl_freeMemory(mpTable);
+ }
+
+ void ScanlineContainer::InsertSpan( sal_Int32 y, sal_Int32 lx, sal_Int32 rx )
+ {
+ // there's new incoming span which we need to store in the table.
+ // first see if its width contributes a valid span.
+ if(sal_Int32 dwSpanWidth = rx-lx)
+ {
+ // first select the appropriate scanline the new span.
+ sal_Int32 *pNumSpans = mpTable+(y*maPitch);
+ span *pSpanArray = reinterpret_cast<span *>(pNumSpans+2);
+
+ // retrieve the number of already contained spans.
+ sal_Int32 dwNumSpan = *pNumSpans;
+
+ // since we need to sort the spans from top to bottom
+ // and left to right, we need to find the correct location
+ // in the table.
+ sal_Int32 dwIndex = 0;
+ while(dwIndex<dwNumSpan)
+ {
+ // since we would like to avoid unnecessary spans
+ // we try to consolidate them if possible.
+ // consolidate with right neighbour
+ if(pSpanArray[dwIndex].x == rx)
+ {
+ pSpanArray[dwIndex].x = lx;
+ pSpanArray[dwIndex].w += dwSpanWidth;
+ return;
+ }
+
+ // consolidate with left neighbour
+ if((pSpanArray[dwIndex].x+pSpanArray[dwIndex].w) == lx)
+ {
+ pSpanArray[dwIndex].w += rx-lx;
+ return;
+ }
+
+ // no consolidation possible, either this is a completely
+ // seperate span or it is the first in the list.
+ if(pSpanArray[dwIndex].x > lx)
+ break;
+
+ // forward to next element in the list.
+ ++dwIndex;
+ }
+
+ // if we reach here, the new span needs to be stored
+ // in the table, increase the number of spans in the
+ // current scanline.
+ *pNumSpans = dwNumSpan+1;
+
+ // keep the list of spans in sorted order. 'dwIndex'
+ // is where we want to store the new span. 'dwNumSpan'
+ // is the number of spans already there. now we need
+ // to move the offending spans out of the way.
+ while(dwIndex != dwNumSpan)
+ {
+ pSpanArray[dwNumSpan].x = pSpanArray[dwNumSpan-1].x;
+ pSpanArray[dwNumSpan].w = pSpanArray[dwNumSpan-1].w;
+ --dwNumSpan;
+ }
+
+ // insert the new span
+ pSpanArray[dwIndex].x = lx;
+ pSpanArray[dwIndex].w = rx-lx;
+
+ // remember the total number of spans in the table.
+ ++maNumberOfSpans;
+ }
+ }
+
+ void ScanlineContainer::Consolidate( void )
+ {
+ sal_Int32 *pScanline = mpTable;
+
+ sal_Int32 dwRemaining = maNumScanlines;
+ while(dwRemaining)
+ {
+ sal_Int32 dwNumSpans = pScanline[0];
+ sal_Int32 *pSpanArray = pScanline+2;
+
+ sal_Int32 dwRest = dwRemaining-1;
+ sal_Int32 *pNext = pScanline;
+ while(dwRest)
+ {
+ pNext += maPitch;
+ sal_Int32 dwNumNextSpans = pNext[0];
+ sal_Int32 *pSpanArrayNext = pNext+2;
+ if(dwNumSpans != dwNumNextSpans)
+ break;
+
+ sal_Int32 dwCompare = dwNumSpans<<1;
+ while(dwCompare)
+ {
+ if(pSpanArray[dwCompare-1] != pSpanArrayNext[dwCompare-1])
+ break;
+ --dwCompare;
+ }
+ if(dwCompare)
+ break;
+
+ --dwRest;
+ }
+
+ const sal_Int32 dwNumEqualScanlines(dwRemaining-dwRest);
+ pScanline[1] = dwNumEqualScanlines;
+ pScanline += maPitch*dwNumEqualScanlines;
+ dwRemaining -= dwNumEqualScanlines;
+
+ // since we track the total number of spans to generate,
+ // we need to account for consolidated scanlines here.
+ if(dwNumEqualScanlines > 1)
+ maNumberOfSpans -= dwNumSpans * (dwNumEqualScanlines-1);
+ }
+ }
+}
+
+// TODO: we should consider passing a basegfx b2dpolypolygon here to
+// ensure that the signature isn't misleading.
+// if we could pass a b2dpolypolygon here, we could easily triangulate it.
+void OutputDevice::ImplSetTriangleClipRegion( const PolyPolygon &rPolyPolygon )
+{
+ DBG_TESTSOLARMUTEX();
+
+ if(!(IsDeviceOutputNecessary()))
+ return;
+ if(!(mpGraphics))
+ if(!(ImplGetGraphics()))
+ return;
+
+ if( mpGraphics->supportsOperation( OutDevSupport_B2DClip ) )
+ {
+#if 0
+ ::basegfx::B2DPolyPolygon aB2DPolyPolygon = rPolyPolygon.getB2DPolyPolygon();
+#else
+ // getB2DPolyPolygon() "optimizes away" some points
+ // which prevents reliable undoing of the "triangle thingy" parameter
+ // so the toolspoly -> b2dpoly conversion has to be done manually
+ ::basegfx::B2DPolyPolygon aB2DPolyPolygon;
+ for( USHORT nPolyIdx = 0; nPolyIdx < rPolyPolygon.Count(); ++nPolyIdx )
+ {
+ const Polygon& rPolygon = rPolyPolygon[ nPolyIdx ];
+ ::basegfx::B2DPolygon aB2DPoly;
+ for( USHORT nPointIdx = 0; nPointIdx < rPolygon.GetSize(); ++nPointIdx )
+ {
+ const Point& rPoint = rPolygon[ nPointIdx ];
+ const ::basegfx::B2DPoint aB2DPoint( rPoint.X(), rPoint.Y() );
+ aB2DPoly.append( aB2DPoint );
+ }
+ aB2DPolyPolygon.append( aB2DPoly );
+ }
+#endif
+
+ const ::basegfx::B2DHomMatrix aTransform = ImplGetDeviceTransformation();
+ aB2DPolyPolygon.transform( aTransform );
+
+ // the rPolyPolygon argument is a "triangle thingy"
+ // so convert it to a normal polypolyon first
+ ::basegfx::B2DPolyPolygon aPolyTriangle;
+ const int nPolyCount = aB2DPolyPolygon.count();
+ for( int nPolyIdx = 0; nPolyIdx < nPolyCount; ++nPolyIdx )
+ {
+ const ::basegfx::B2DPolygon rPolygon = aB2DPolyPolygon.getB2DPolygon( nPolyIdx );
+ const int nPointCount = rPolygon.count();
+ for( int nPointIdx = 0; nPointIdx+2 < nPointCount; nPointIdx +=3 )
+ {
+ ::basegfx::B2DPolygon aTriangle;
+ aTriangle.append( rPolygon.getB2DPoint( nPointIdx+0 ) );
+ aTriangle.append( rPolygon.getB2DPoint( nPointIdx+1 ) );
+ aTriangle.append( rPolygon.getB2DPoint( nPointIdx+2 ) );
+ aPolyTriangle.append( aTriangle );
+ }
+ }
+
+ // now set the clip region with the real polypolygon
+ mpGraphics->BeginSetClipRegion( 0 );
+ mpGraphics->UnionClipRegion( aPolyTriangle, this );
+ mpGraphics->EndSetClipRegion();
+
+ // and mark the clip status as ready
+ mbOutputClipped = FALSE;
+ mbClipRegion = TRUE;
+ mbClipRegionSet = TRUE;
+ mbInitClipRegion = FALSE;
+ return;
+ }
+
+ sal_Int32 offset_x = 0;
+ sal_Int32 offset_y = 0;
+ if ( GetOutDevType() == OUTDEV_WINDOW )
+ {
+ offset_x = mnOutOffX+mnOutOffOrigX;
+ offset_y = mnOutOffY+mnOutOffOrigY;
+ }
+
+ // first of all we need to know the upper limit
+ // of the amount of possible clipping regions.
+ sal_Int32 maxy = SAL_MIN_INT32;
+ sal_Int32 miny = SAL_MAX_INT32;
+ sal_uInt32 dwNumTriangles = 0;
+ for(USHORT i=0; i<rPolyPolygon.Count(); ++i)
+ {
+ const Polygon &rPoly = rPolyPolygon.GetObject(i);
+ const sal_Int32 dwNumVertices = rPoly.GetSize();
+ if(!(dwNumVertices % 3))
+ {
+ for(USHORT j=0; j<rPoly.GetSize(); ++j)
+ {
+ const Point &p = rPoly.GetPoint(j);
+ if(p.Y() < miny)
+ miny = p.Y();
+ if(p.Y() > maxy)
+ maxy = p.Y();
+ }
+ dwNumTriangles += dwNumVertices / 3;
+ }
+ }
+
+ const sal_uInt32 dwNumScanlines = (maxy-miny);
+ if(!(dwNumScanlines))
+ {
+ // indicates that no output needs to be produced
+ // since the clipping region did not provide any
+ // visible areas.
+ mbOutputClipped = TRUE;
+
+ // indicates that a clip region has been
+ // presented to the output device.
+ mbClipRegion = TRUE;
+
+ // indicates that the set clipping region
+ // has been processed.
+ mbClipRegionSet = TRUE;
+
+ // under 'normal' circumstances a new clipping region
+ // needs to be processed by ImplInitClipRegion(),
+ // which we need to circumvent.
+ mbInitClipRegion = FALSE;
+ return;
+ }
+
+ // this container provides all services we need to
+ // efficiently store/retrieve spans from the table.
+ const sal_uInt32 dwNumSpansPerScanline = dwNumTriangles;
+ ScanlineContainer container(dwNumScanlines,dwNumSpansPerScanline);
+
+ // convert the incoming polypolygon to spans, we assume that
+ // the polypolygon has already been triangulated since we don't
+ // want to use the basegfx-types here. this could be leveraged
+ // after the tools-types had been removed.
+ for(USHORT i=0; i<rPolyPolygon.Count(); ++i)
+ {
+ const Polygon &rPoly = rPolyPolygon.GetObject(i);
+ const USHORT dwNumVertices = rPoly.GetSize();
+ if(!(dwNumVertices % 3))
+ {
+ for(USHORT j=0; j<dwNumVertices; j+=3)
+ {
+ const Point &p0 = rPoly.GetPoint(j+0);
+ const Point &p1 = rPoly.GetPoint(j+1);
+ const Point &p2 = rPoly.GetPoint(j+2);
+
+ // what now follows is an extremely fast triangle
+ // rasterizer from which all tricky and interesting
+ // parts were forcibly amputated.
+ // note: top.left fill-convention
+ vertex v0(p0);
+ vertex v1(p1);
+ vertex v2(p2);
+
+ //sprintf(string,"[%f,%f] [%f,%f] [%f,%f]\n",v0.x,v0.y,v1.x,v1.y,v2.x,v2.y);
+ //OSL_TRACE(string);
+
+ if(v0.y > v2.y) ::swap(v0, v2);
+ if(v1.y > v2.y) ::swap(v1, v2);
+ if(v0.y > v1.y) ::swap(v0, v1);
+
+ const float float2fixed(16.0f);
+
+ // vertex coordinates of the triangle [28.4 fixed-point]
+ const int i4x0 = iround(float2fixed * (v0.x - 0.5f));
+ const int i4y0 = iround(float2fixed * (v0.y - 0.5f));
+ const int i4x1 = iround(float2fixed * (v1.x - 0.5f));
+ const int i4y1 = iround(float2fixed * (v1.y - 0.5f));
+ const int i4x2 = iround(float2fixed * (v2.x - 0.5f));
+ const int i4y2 = iround(float2fixed * (v2.y - 0.5f));
+
+ // vertex coordinate deltas [28.4 fixed-point]
+ const int i4dx12 = i4x1-i4x0;
+ const int i4dy12 = i4y1-i4y0;
+ const int i4dx13 = i4x2-i4x0;
+ const int i4dy13 = i4y2-i4y0;
+ const int i4dx23 = i4x2-i4x1;
+ const int i4dy23 = i4y2-i4y1;
+
+ // slope of edges [quotient,remainder]
+ const int mq12 = floorDiv(i4dx12 << 4, i4dy12 << 4);
+ const int mq13 = floorDiv(i4dx13 << 4, i4dy13 << 4);
+ const int mq23 = floorDiv(i4dx23 << 4, i4dy23 << 4);
+ const int mr12 = floorMod(i4dx12 << 4, i4dy12 << 4);
+ const int mr13 = floorMod(i4dx13 << 4, i4dy13 << 4);
+ const int mr23 = floorMod(i4dx23 << 4, i4dy23 << 4);
+
+ // convert the vertical coordinates back to integers.
+ // according to the top-left fillrule we need to step
+ // the coordinates to the ceiling.
+ const int y0 = (i4y0+15)>>4;
+ const int y1 = (i4y1+15)>>4;
+ const int y2 = (i4y2+15)>>4;
+
+ // calculate the value of the horizontal coordinate
+ // from the edge that 'spans' the triangle.
+ const int x = ceilDiv(i4dx13*i4dy12 + i4x0*i4dy13, i4dy13);
+
+ // this will hold the horizontal coordinates
+ // of the seperate spans during the rasterization process.
+ int lx,rx;
+
+ // this pair will serve as the error accumulator while
+ // we step along the edges.
+ int ld,rd,lD,rD;
+
+ // these are the edge and error stepping values that
+ // will be used while stepping.
+ int lQ,rQ,lR,rR;
+
+ if(i4x1 < x)
+ {
+ lx = ceilDiv(i4dx12 * (ceilFix4(i4y0) - i4y0) + i4x0 * i4dy12, i4dy12 << 4);
+ ld = ceilMod(i4dx12 * (ceilFix4(i4y0) - i4y0) + i4x0 * i4dy12, i4dy12 << 4);
+ rx = ceilDiv(i4dx13 * (ceilFix4(i4y0) - i4y0) + i4x0 * i4dy13, i4dy13 << 4);
+ rd = ceilMod(i4dx13 * (ceilFix4(i4y0) - i4y0) + i4x0 * i4dy13, i4dy13 << 4);
+ lQ = mq12;
+ rQ = mq13;
+ lR = mr12;
+ rR = mr13;
+ lD = i4dy12 << 4;
+ rD = i4dy13 << 4;
+ }
+ else
+ {
+ lx = ceilDiv(i4dx13 * (ceilFix4(i4y0) - i4y0) + i4x0 * i4dy13, i4dy13 << 4);
+ ld = ceilMod(i4dx13 * (ceilFix4(i4y0) - i4y0) + i4x0 * i4dy13, i4dy13 << 4);
+ rx = ceilDiv(i4dx12 * (ceilFix4(i4y0) - i4y0) + i4x0 * i4dy12, i4dy12 << 4);
+ rd = ceilMod(i4dx12 * (ceilFix4(i4y0) - i4y0) + i4x0 * i4dy12, i4dy12 << 4);
+ lQ = mq13;
+ rQ = mq12;
+ lR = mr13;
+ rR = mr12;
+ lD = i4dy13 << 4;
+ rD = i4dy12 << 4;
+ }
+
+ for(signed int y=y0; y<y1; y++)
+ {
+ container.InsertSpan(y-miny,lx,rx);
+
+ lx += lQ; ld += lR;
+ if(ld > 0) { ld -= lD; lx += 1; }
+ rx += rQ; rd += rR;
+ if(rd > 0) { rd -= rD; rx += 1; }
+ }
+
+ if(i4x1 < x)
+ {
+ lx = ceilDiv(i4dx23 * (ceilFix4(i4y1) - i4y1) + i4x1 * i4dy23, i4dy23 << 4);
+ ld = ceilMod(i4dx23 * (ceilFix4(i4y1) - i4y1) + i4x1 * i4dy23, i4dy23 << 4);
+ rx = ceilDiv(i4dx13 * (ceilFix4(i4y1) - i4y0) + i4x0 * i4dy13, i4dy13 << 4);
+ rd = ceilMod(i4dx13 * (ceilFix4(i4y1) - i4y0) + i4x0 * i4dy13, i4dy13 << 4);
+ lQ = mq23;
+ lR = mr23;
+ lD = i4dy23 << 4;
+ }
+ else
+ {
+ rx = ceilDiv(i4dx23 * (ceilFix4(i4y1) - i4y1) + i4x1 * i4dy23, i4dy23 << 4);
+ rd = ceilMod(i4dx23 * (ceilFix4(i4y1) - i4y1) + i4x1 * i4dy23, i4dy23 << 4);
+ rQ = mq23;
+ rR = mr23;
+ rD = i4dy23 << 4;
+ }
+
+ for(signed int y=y1; y<y2; y++)
+ {
+ container.InsertSpan(y-miny,lx,rx);
+
+ lx += lQ; ld += lR;
+ if(ld > 0) { ld -= lD; lx += 1; }
+ rx += rQ; rd += rR;
+ if(rd > 0) { rd -= rD; rx += 1; }
+ }
+ }
+ }
+ }
+
+ // now try to consolidate as many scanlines as possible.
+ // please note that this will probably change the number
+ // of spans [at least this is why we do all this hassle].
+ // so, if you use 'consolidate' you should *use* this
+ // information during iteration, because the 'graphics'
+ // object we tell all those regions about is a bit,
+ // hm, how to say, *picky* if you supply not correctly
+ // the amount of regions.
+ container.Consolidate();
+
+ // now forward the spantable to the graphics handler.
+ SpanIterator it(container.Iterate());
+ mpGraphics->BeginSetClipRegion( container.GetNumSpans() );
+ while(miny < maxy)
+ {
+ const sal_Int32 dwNumEqual(it.GetNumEqualScanlines());
+ while(it.GetRemainingSpans())
+ {
+ // retrieve the next span [x-coordinate, width] from the current scanline.
+ std::pair<sal_Int32,sal_Int32> span(it.GetNextSpan());
+
+ // now forward this to the graphics object.
+ // the only part that is worth noting is that we use
+ // the number of equal spanlines [the current is always the
+ // first one of the equal bunch] as the height of the region.
+ mpGraphics->UnionClipRegion( offset_x+span.first,
+ offset_y+miny,
+ span.second,
+ dwNumEqual,
+ this );
+ }
+ it.Skip(dwNumEqual);
+ miny += dwNumEqual;
+ }
+ mpGraphics->EndSetClipRegion();
+
+ // indicates that no output needs to be produced
+ // since the clipping region did not provide any
+ // visible areas. the clip covers the whole area
+ // if there's not a single region.
+ mbOutputClipped = (container.GetNumSpans() == 0);
+
+ // indicates that a clip region has been
+ // presented to the output device.
+ mbClipRegion = TRUE;
+
+ // indicates that the set clipping region
+ // has been processed.
+ mbClipRegionSet = TRUE;
+
+ // under 'normal' circumstances a new clipping region
+ // needs to be processed by ImplInitClipRegion(),
+ // which we need to circumvent.
+ mbInitClipRegion = FALSE;
+}
+
+// -----------------------------------------------------------------------
+
+void OutputDevice::SetClipRegion()
+{
+ DBG_TRACE( "OutputDevice::SetClipRegion()" );
+ DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
+
+ if ( mpMetaFile )
+ mpMetaFile->AddAction( new MetaClipRegionAction( Region(), FALSE ) );
+
+ ImplSetClipRegion( NULL );
+
+ if( mpAlphaVDev )
+ mpAlphaVDev->SetClipRegion();
+}
+
+// -----------------------------------------------------------------------
+
+void OutputDevice::SetClipRegion( const Region& rRegion )
+{
+ DBG_TRACE( "OutputDevice::SetClipRegion( rRegion )" );
+ DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
+ DBG_CHKOBJ( &rRegion, Region, ImplDbgTestRegion );
+
+ if ( mpMetaFile )
+ mpMetaFile->AddAction( new MetaClipRegionAction( rRegion, TRUE ) );
+
+ if ( rRegion.GetType() == REGION_NULL )
+ ImplSetClipRegion( NULL );
+ else
+ {
+ Region aRegion = LogicToPixel( rRegion );
+ ImplSetClipRegion( &aRegion );
+ }
+
+ if( mpAlphaVDev )
+ mpAlphaVDev->SetClipRegion( rRegion );
+}
+
+// -----------------------------------------------------------------------
+
+void OutputDevice::SetTriangleClipRegion( const PolyPolygon &rPolyPolygon )
+{
+ DBG_TRACE( "OutputDevice::SetTriangleClipRegion( rPolyPolygon )" );
+ DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
+
+ // in case the passed polypolygon is empty, use the
+ // existing SetClipRegion() method which gracefully
+ // unsets any previously set clipping region.
+ if(!(rPolyPolygon.Count()))
+ SetClipRegion();
+
+ sal_Int32 offset_x = 0;
+ sal_Int32 offset_y = 0;
+ if ( GetOutDevType() == OUTDEV_WINDOW )
+ {
+ offset_x = mnOutOffX+mnOutOffOrigX;
+ offset_y = mnOutOffY+mnOutOffOrigY;
+ }
+
+ // play nice with the rest of the system and provide an old-style region.
+ // the rest of this method does not rely on this.
+ maRegion = Region::GetRegionFromPolyPolygon( LogicToPixel(rPolyPolygon) );
+ maRegion.Move(offset_x,offset_x);
+
+ // feed region to metafile
+ if ( mpMetaFile )
+ mpMetaFile->AddAction( new MetaClipRegionAction( maRegion, TRUE ) );
+
+ ImplSetTriangleClipRegion( rPolyPolygon );
+
+ if( mpAlphaVDev )
+ mpAlphaVDev->SetTriangleClipRegion( rPolyPolygon );
+}
+
+// -----------------------------------------------------------------------
+
+Region OutputDevice::GetClipRegion() const
+{
+ DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
+
+ return PixelToLogic( maRegion );
+}
+
+// -----------------------------------------------------------------------
+
+Region OutputDevice::GetActiveClipRegion() const
+{
+ DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
+
+ if ( GetOutDevType() == OUTDEV_WINDOW )
+ {
+ Region aRegion( REGION_NULL );
+ Window* pWindow = (Window*)this;
+ if ( pWindow->mpWindowImpl->mbInPaint )
+ {
+ aRegion = *(pWindow->mpWindowImpl->mpPaintRegion);
+ aRegion.Move( -mnOutOffX, -mnOutOffY );
+ }
+ if ( mbClipRegion )
+ aRegion.Intersect( maRegion );
+ return PixelToLogic( aRegion );
+ }
+ else
+ return GetClipRegion();
+}
+
+// -----------------------------------------------------------------------
+
+void OutputDevice::MoveClipRegion( long nHorzMove, long nVertMove )
+{
+ DBG_TRACE( "OutputDevice::MoveClipRegion()" );
+ DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
+
+ if ( mbClipRegion )
+ {
+ if( mpMetaFile )
+ mpMetaFile->AddAction( new MetaMoveClipRegionAction( nHorzMove, nVertMove ) );
+
+ maRegion.Move( ImplLogicWidthToDevicePixel( nHorzMove ),
+ ImplLogicHeightToDevicePixel( nVertMove ) );
+ mbInitClipRegion = TRUE;
+ }
+
+ if( mpAlphaVDev )
+ mpAlphaVDev->MoveClipRegion( nHorzMove, nVertMove );
+}
+
+// -----------------------------------------------------------------------
+
+void OutputDevice::IntersectClipRegion( const Rectangle& rRect )
+{
+ DBG_TRACE( "OutputDevice::IntersectClipRegion( rRect )" );
+ DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
+
+ if ( mpMetaFile )
+ mpMetaFile->AddAction( new MetaISectRectClipRegionAction( rRect ) );
+
+ Rectangle aRect = LogicToPixel( rRect );
+ maRegion.Intersect( aRect );
+ mbClipRegion = TRUE;
+ mbInitClipRegion = TRUE;
+
+ if( mpAlphaVDev )
+ mpAlphaVDev->IntersectClipRegion( rRect );
+}
+
+// -----------------------------------------------------------------------
+
+void OutputDevice::IntersectClipRegion( const Region& rRegion )
+{
+ DBG_TRACE( "OutputDevice::IntersectClipRegion( rRegion )" );
+ DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
+ DBG_CHKOBJ( &rRegion, Region, ImplDbgTestRegion );
+
+ RegionType eType = rRegion.GetType();
+
+ if ( eType != REGION_NULL )
+ {
+ if ( mpMetaFile )
+ mpMetaFile->AddAction( new MetaISectRegionClipRegionAction( rRegion ) );
+
+ Region aRegion = LogicToPixel( rRegion );
+ maRegion.Intersect( aRegion );
+ mbClipRegion = TRUE;
+ mbInitClipRegion = TRUE;
+ }
+
+ if( mpAlphaVDev )
+ mpAlphaVDev->IntersectClipRegion( rRegion );
+}
+
+// -----------------------------------------------------------------------
+
+void OutputDevice::SetDrawMode( ULONG nDrawMode )
+{
+ DBG_TRACE1( "OutputDevice::SetDrawMode( %lx )", nDrawMode );
+ DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
+
+ mnDrawMode = nDrawMode;
+
+ if( mpAlphaVDev )
+ mpAlphaVDev->SetDrawMode( nDrawMode );
+}
+
+// -----------------------------------------------------------------------
+
+void OutputDevice::SetRasterOp( RasterOp eRasterOp )
+{
+ DBG_TRACE1( "OutputDevice::SetRasterOp( %d )", (int)eRasterOp );
+ DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
+
+ if ( mpMetaFile )
+ mpMetaFile->AddAction( new MetaRasterOpAction( eRasterOp ) );
+
+ if ( meRasterOp != eRasterOp )
+ {
+ meRasterOp = eRasterOp;
+ mbInitLineColor = mbInitFillColor = TRUE;
+
+ if( mpGraphics || ImplGetGraphics() )
+ mpGraphics->SetXORMode( (ROP_INVERT == meRasterOp) || (ROP_XOR == meRasterOp), ROP_INVERT == meRasterOp );
+ }
+
+ if( mpAlphaVDev )
+ mpAlphaVDev->SetRasterOp( eRasterOp );
+}
+
+// -----------------------------------------------------------------------
+
+void OutputDevice::SetLineColor()
+{
+ DBG_TRACE( "OutputDevice::SetLineColor()" );
+ DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
+
+ if ( mpMetaFile )
+ mpMetaFile->AddAction( new MetaLineColorAction( Color(), FALSE ) );
+
+ if ( mbLineColor )
+ {
+ mbInitLineColor = TRUE;
+ mbLineColor = FALSE;
+ maLineColor = Color( COL_TRANSPARENT );
+ }
+
+ if( mpAlphaVDev )
+ mpAlphaVDev->SetLineColor();
+}
+
+// -----------------------------------------------------------------------
+
+void OutputDevice::SetLineColor( const Color& rColor )
+{
+ DBG_TRACE1( "OutputDevice::SetLineColor( %lx )", rColor.GetColor() );
+ DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
+
+ Color aColor( rColor );
+
+ if( mnDrawMode & ( DRAWMODE_BLACKLINE | DRAWMODE_WHITELINE |
+ DRAWMODE_GRAYLINE | DRAWMODE_GHOSTEDLINE |
+ DRAWMODE_SETTINGSLINE ) )
+ {
+ if( !ImplIsColorTransparent( aColor ) )
+ {
+ if( mnDrawMode & DRAWMODE_BLACKLINE )
+ {
+ aColor = Color( COL_BLACK );
+ }
+ else if( mnDrawMode & DRAWMODE_WHITELINE )
+ {
+ aColor = Color( COL_WHITE );
+ }
+ else if( mnDrawMode & DRAWMODE_GRAYLINE )
+ {
+ const UINT8 cLum = aColor.GetLuminance();
+ aColor = Color( cLum, cLum, cLum );
+ }
+ else if( mnDrawMode & DRAWMODE_SETTINGSLINE )
+ {
+ aColor = GetSettings().GetStyleSettings().GetFontColor();
+ }
+
+ if( mnDrawMode & DRAWMODE_GHOSTEDLINE )
+ {
+ aColor = Color( ( aColor.GetRed() >> 1 ) | 0x80,
+ ( aColor.GetGreen() >> 1 ) | 0x80,
+ ( aColor.GetBlue() >> 1 ) | 0x80);
+ }
+ }
+ }
+
+ if( mpMetaFile )
+ mpMetaFile->AddAction( new MetaLineColorAction( aColor, TRUE ) );
+
+ if( ImplIsColorTransparent( aColor ) )
+ {
+ if ( mbLineColor )
+ {
+ mbInitLineColor = TRUE;
+ mbLineColor = FALSE;
+ maLineColor = Color( COL_TRANSPARENT );
+ }
+ }
+ else
+ {
+ if( maLineColor != aColor )
+ {
+ mbInitLineColor = TRUE;
+ mbLineColor = TRUE;
+ maLineColor = aColor;
+ }
+ }
+
+ if( mpAlphaVDev )
+ mpAlphaVDev->SetLineColor( COL_BLACK );
+}
+
+// -----------------------------------------------------------------------
+
+void OutputDevice::SetFillColor()
+{
+ DBG_TRACE( "OutputDevice::SetFillColor()" );
+ DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
+
+ if ( mpMetaFile )
+ mpMetaFile->AddAction( new MetaFillColorAction( Color(), FALSE ) );
+
+ if ( mbFillColor )
+ {
+ mbInitFillColor = TRUE;
+ mbFillColor = FALSE;
+ maFillColor = Color( COL_TRANSPARENT );
+ }
+
+ if( mpAlphaVDev )
+ mpAlphaVDev->SetFillColor();
+}
+
+// -----------------------------------------------------------------------
+
+void OutputDevice::SetFillColor( const Color& rColor )
+{
+ DBG_TRACE1( "OutputDevice::SetFillColor( %lx )", rColor.GetColor() );
+ DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
+
+ Color aColor( rColor );
+
+ if( mnDrawMode & ( DRAWMODE_BLACKFILL | DRAWMODE_WHITEFILL |
+ DRAWMODE_GRAYFILL | DRAWMODE_NOFILL |
+ DRAWMODE_GHOSTEDFILL | DRAWMODE_SETTINGSFILL ) )
+ {
+ if( !ImplIsColorTransparent( aColor ) )
+ {
+ if( mnDrawMode & DRAWMODE_BLACKFILL )
+ {
+ aColor = Color( COL_BLACK );
+ }
+ else if( mnDrawMode & DRAWMODE_WHITEFILL )
+ {
+ aColor = Color( COL_WHITE );
+ }
+ else if( mnDrawMode & DRAWMODE_GRAYFILL )
+ {
+ const UINT8 cLum = aColor.GetLuminance();
+ aColor = Color( cLum, cLum, cLum );
+ }
+ else if( mnDrawMode & DRAWMODE_NOFILL )
+ {
+ aColor = Color( COL_TRANSPARENT );
+ }
+ else if( mnDrawMode & DRAWMODE_SETTINGSFILL )
+ {
+ aColor = GetSettings().GetStyleSettings().GetWindowColor();
+ }
+
+ if( mnDrawMode & DRAWMODE_GHOSTEDFILL )
+ {
+ aColor = Color( (aColor.GetRed() >> 1) | 0x80,
+ (aColor.GetGreen() >> 1) | 0x80,
+ (aColor.GetBlue() >> 1) | 0x80);
+ }
+ }
+ }
+
+ if ( mpMetaFile )
+ mpMetaFile->AddAction( new MetaFillColorAction( aColor, TRUE ) );
+
+ if ( ImplIsColorTransparent( aColor ) )
+ {
+ if ( mbFillColor )
+ {
+ mbInitFillColor = TRUE;
+ mbFillColor = FALSE;
+ maFillColor = Color( COL_TRANSPARENT );
+ }
+ }
+ else
+ {
+ if ( maFillColor != aColor )
+ {
+ mbInitFillColor = TRUE;
+ mbFillColor = TRUE;
+ maFillColor = aColor;
+ }
+ }
+
+ if( mpAlphaVDev )
+ mpAlphaVDev->SetFillColor( COL_BLACK );
+}
+
+// -----------------------------------------------------------------------
+
+void OutputDevice::SetBackground()
+{
+ DBG_TRACE( "OutputDevice::SetBackground()" );
+ DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
+
+ maBackground = Wallpaper();
+ mbBackground = FALSE;
+
+ if( mpAlphaVDev )
+ mpAlphaVDev->SetBackground();
+}
+
+// -----------------------------------------------------------------------
+
+void OutputDevice::SetBackground( const Wallpaper& rBackground )
+{
+ DBG_TRACE( "OutputDevice::SetBackground( rBackground )" );
+ DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
+
+ maBackground = rBackground;
+
+ if( rBackground.GetStyle() == WALLPAPER_NULL )
+ mbBackground = FALSE;
+ else
+ mbBackground = TRUE;
+
+ if( mpAlphaVDev )
+ mpAlphaVDev->SetBackground( rBackground );
+}
+
+// -----------------------------------------------------------------------
+
+void OutputDevice::SetRefPoint()
+{
+ DBG_TRACE( "OutputDevice::SetRefPoint()" );
+ DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
+
+ if ( mpMetaFile )
+ mpMetaFile->AddAction( new MetaRefPointAction( Point(), FALSE ) );
+
+ mbRefPoint = FALSE;
+ maRefPoint.X() = maRefPoint.Y() = 0L;
+
+ if( mpAlphaVDev )
+ mpAlphaVDev->SetRefPoint();
+}
+
+// -----------------------------------------------------------------------
+
+void OutputDevice::SetRefPoint( const Point& rRefPoint )
+{
+ DBG_TRACE( "OutputDevice::SetRefPoint( rRefPoint )" );
+ DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
+
+ if ( mpMetaFile )
+ mpMetaFile->AddAction( new MetaRefPointAction( rRefPoint, TRUE ) );
+
+ mbRefPoint = TRUE;
+ maRefPoint = rRefPoint;
+
+ if( mpAlphaVDev )
+ mpAlphaVDev->SetRefPoint( rRefPoint );
+}
+
+// -----------------------------------------------------------------------
+
+void OutputDevice::DrawLine( const Point& rStartPt, const Point& rEndPt )
+{
+ DBG_TRACE( "OutputDevice::DrawLine()" );
+ DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
+
+ if ( mpMetaFile )
+ mpMetaFile->AddAction( new MetaLineAction( rStartPt, rEndPt ) );
+
+ if ( !IsDeviceOutputNecessary() || !mbLineColor || ImplIsRecordLayout() )
+ return;
+
+ if ( !mpGraphics )
+ {
+ if ( !ImplGetGraphics() )
+ return;
+ }
+
+ if ( mbInitClipRegion )
+ ImplInitClipRegion();
+ if ( mbOutputClipped )
+ return;
+
+ if ( mbInitLineColor )
+ ImplInitLineColor();
+
+ // #i101598# support AA and snap for lines, too
+ if((mnAntialiasing & ANTIALIASING_ENABLE_B2DDRAW)
+ && mpGraphics->supportsOperation(OutDevSupport_B2DDraw)
+ && ROP_OVERPAINT == GetRasterOp()
+ && IsLineColor())
+ {
+ // at least transform with double precision to device coordinates; this will
+ // avoid pixel snap of single, appended lines
+ const basegfx::B2DHomMatrix aTransform(ImplGetDeviceTransformation());
+ const basegfx::B2DVector aB2DLineWidth( 1.0, 1.0 );
+ basegfx::B2DPolygon aB2DPolyLine;
+
+ aB2DPolyLine.append(basegfx::B2DPoint(rStartPt.X(), rStartPt.Y()));
+ aB2DPolyLine.append(basegfx::B2DPoint(rEndPt.X(), rEndPt.Y()));
+ aB2DPolyLine.transform( aTransform );
+
+ if(mnAntialiasing & ANTIALIASING_PIXELSNAPHAIRLINE)
+ {
+ aB2DPolyLine = basegfx::tools::snapPointsOfHorizontalOrVerticalEdges(aB2DPolyLine);
+ }
+
+ if( mpGraphics->DrawPolyLine( aB2DPolyLine, 0.0, aB2DLineWidth, basegfx::B2DLINEJOIN_NONE, this))
+ {
+ return;
+ }
+ }
+
+ const Point aStartPt(ImplLogicToDevicePixel(rStartPt));
+ const Point aEndPt(ImplLogicToDevicePixel(rEndPt));
+
+ mpGraphics->DrawLine( aStartPt.X(), aStartPt.Y(), aEndPt.X(), aEndPt.Y(), this );
+
+ if( mpAlphaVDev )
+ mpAlphaVDev->DrawLine( rStartPt, rEndPt );
+}
+
+// -----------------------------------------------------------------------
+
+void OutputDevice::impPaintLineGeometryWithEvtlExpand(
+ const LineInfo& rInfo,
+ basegfx::B2DPolyPolygon aLinePolyPolygon)
+{
+ const bool bTryAA((mnAntialiasing & ANTIALIASING_ENABLE_B2DDRAW)
+ && mpGraphics->supportsOperation(OutDevSupport_B2DDraw)
+ && ROP_OVERPAINT == GetRasterOp()
+ && IsLineColor());
+ basegfx::B2DPolyPolygon aFillPolyPolygon;
+ const bool bDashUsed(LINE_DASH == rInfo.GetStyle());
+ const bool bLineWidthUsed(rInfo.GetWidth() > 1);
+
+ if(bDashUsed && aLinePolyPolygon.count())
+ {
+ ::std::vector< double > fDotDashArray;
+ const double fDashLen(rInfo.GetDashLen());
+ const double fDotLen(rInfo.GetDotLen());
+ const double fDistance(rInfo.GetDistance());
+
+ for(sal_uInt16 a(0); a < rInfo.GetDashCount(); a++)
+ {
+ fDotDashArray.push_back(fDashLen);
+ fDotDashArray.push_back(fDistance);
+ }
+
+ for(sal_uInt16 b(0); b < rInfo.GetDotCount(); b++)
+ {
+ fDotDashArray.push_back(fDotLen);
+ fDotDashArray.push_back(fDistance);
+ }
+
+ const double fAccumulated(::std::accumulate(fDotDashArray.begin(), fDotDashArray.end(), 0.0));
+
+ if(fAccumulated > 0.0)
+ {
+ basegfx::B2DPolyPolygon aResult;
+
+ for(sal_uInt32 c(0); c < aLinePolyPolygon.count(); c++)
+ {
+ basegfx::B2DPolyPolygon aLineTraget;
+ basegfx::tools::applyLineDashing(
+ aLinePolyPolygon.getB2DPolygon(c),
+ fDotDashArray,
+ &aLineTraget);
+ aResult.append(aLineTraget);
+ }
+
+ aLinePolyPolygon = aResult;
+ }
+ }
+
+ if(bLineWidthUsed && aLinePolyPolygon.count())
+ {
+ const double fHalfLineWidth((rInfo.GetWidth() * 0.5) + 0.5);
+
+ if(aLinePolyPolygon.areControlPointsUsed())
+ {
+ // #i110768# When area geometry has to be created, do not
+ // use the fallback bezier decomposition inside createAreaGeometry,
+ // but one that is at least as good as ImplSubdivideBezier was.
+ // There, Polygon::AdaptiveSubdivide was used with default parameter
+ // 1.0 as quality index.
+ aLinePolyPolygon = basegfx::tools::adaptiveSubdivideByDistance(aLinePolyPolygon, 1.0);
+ }
+
+ for(sal_uInt32 a(0); a < aLinePolyPolygon.count(); a++)
+ {
+ aFillPolyPolygon.append(basegfx::tools::createAreaGeometry(
+ aLinePolyPolygon.getB2DPolygon(a),
+ fHalfLineWidth,
+ rInfo.GetLineJoin()));
+ }
+
+ aLinePolyPolygon.clear();
+ }
+
+ GDIMetaFile* pOldMetaFile = mpMetaFile;
+ mpMetaFile = NULL;
+
+ if(aLinePolyPolygon.count())
+ {
+ for(sal_uInt32 a(0); a < aLinePolyPolygon.count(); a++)
+ {
+ const basegfx::B2DPolygon aCandidate(aLinePolyPolygon.getB2DPolygon(a));
+ bool bDone(false);
+
+ if(bTryAA)
+ {
+ bDone = mpGraphics->DrawPolyLine( aCandidate, 0.0, basegfx::B2DVector(1.0,1.0), basegfx::B2DLINEJOIN_NONE, this);
+ }
+
+ if(!bDone)
+ {
+ const Polygon aPolygon(aCandidate);
+ mpGraphics->DrawPolyLine(aPolygon.GetSize(), (const SalPoint*)aPolygon.GetConstPointAry(), this);
+ }
+ }
+ }
+
+ if(aFillPolyPolygon.count())
+ {
+ const Color aOldLineColor( maLineColor );
+ const Color aOldFillColor( maFillColor );
+
+ SetLineColor();
+ ImplInitLineColor();
+ SetFillColor( aOldLineColor );
+ ImplInitFillColor();
+
+ bool bDone(false);
+
+ if(bTryAA)
+ {
+ bDone = mpGraphics->DrawPolyPolygon(aFillPolyPolygon, 0.0, this);
+ }
+
+ if(!bDone)
+ {
+ for(sal_uInt32 a(0); a < aFillPolyPolygon.count(); a++)
+ {
+ const Polygon aPolygon(aFillPolyPolygon.getB2DPolygon(a));
+ mpGraphics->DrawPolygon(aPolygon.GetSize(), (const SalPoint*)aPolygon.GetConstPointAry(), this);
+ }
+ }
+
+ SetFillColor( aOldFillColor );
+ SetLineColor( aOldLineColor );
+ }
+
+ mpMetaFile = pOldMetaFile;
+}
+
+// -----------------------------------------------------------------------
+
+void OutputDevice::DrawLine( const Point& rStartPt, const Point& rEndPt,
+ const LineInfo& rLineInfo )
+{
+ DBG_TRACE( "OutputDevice::DrawLine()" );
+ DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
+
+ if ( rLineInfo.IsDefault() )
+ {
+ DrawLine( rStartPt, rEndPt );
+ return;
+ }
+
+ if ( mpMetaFile )
+ mpMetaFile->AddAction( new MetaLineAction( rStartPt, rEndPt, rLineInfo ) );
+
+ if ( !IsDeviceOutputNecessary() || !mbLineColor || ( LINE_NONE == rLineInfo.GetStyle() ) || ImplIsRecordLayout() )
+ return;
+
+ if( !mpGraphics && !ImplGetGraphics() )
+ return;
+
+ if ( mbInitClipRegion )
+ ImplInitClipRegion();
+
+ if ( mbOutputClipped )
+ return;
+
+ const Point aStartPt( ImplLogicToDevicePixel( rStartPt ) );
+ const Point aEndPt( ImplLogicToDevicePixel( rEndPt ) );
+ const LineInfo aInfo( ImplLogicToDevicePixel( rLineInfo ) );
+ const bool bDashUsed(LINE_DASH == aInfo.GetStyle());
+ const bool bLineWidthUsed(aInfo.GetWidth() > 1);
+
+ if ( mbInitLineColor )
+ ImplInitLineColor();
+
+ if(bDashUsed || bLineWidthUsed)
+ {
+ basegfx::B2DPolygon aLinePolygon;
+ aLinePolygon.append(basegfx::B2DPoint(aStartPt.X(), aStartPt.Y()));
+ aLinePolygon.append(basegfx::B2DPoint(aEndPt.X(), aEndPt.Y()));
+
+ impPaintLineGeometryWithEvtlExpand(aInfo, basegfx::B2DPolyPolygon(aLinePolygon));
+ }
+ else
+ {
+ mpGraphics->DrawLine( aStartPt.X(), aStartPt.Y(), aEndPt.X(), aEndPt.Y(), this );
+ }
+
+ if( mpAlphaVDev )
+ mpAlphaVDev->DrawLine( rStartPt, rEndPt, rLineInfo );
+}
+
+// -----------------------------------------------------------------------
+
+void OutputDevice::DrawRect( const Rectangle& rRect )
+{
+ DBG_TRACE( "OutputDevice::DrawRect()" );
+ DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
+
+ if ( mpMetaFile )
+ mpMetaFile->AddAction( new MetaRectAction( rRect ) );
+
+ if ( !IsDeviceOutputNecessary() || (!mbLineColor && !mbFillColor) || ImplIsRecordLayout() )
+ return;
+
+ Rectangle aRect( ImplLogicToDevicePixel( rRect ) );
+
+ if ( aRect.IsEmpty() )
+ return;
+ aRect.Justify();
+
+ if ( !mpGraphics )
+ {
+ if ( !ImplGetGraphics() )
+ return;
+ }
+
+ if ( mbInitClipRegion )
+ ImplInitClipRegion();
+ if ( mbOutputClipped )
+ return;
+
+ if ( mbInitLineColor )
+ ImplInitLineColor();
+ if ( mbInitFillColor )
+ ImplInitFillColor();
+
+ mpGraphics->DrawRect( aRect.Left(), aRect.Top(), aRect.GetWidth(), aRect.GetHeight(), this );
+
+ if( mpAlphaVDev )
+ mpAlphaVDev->DrawRect( rRect );
+}
+
+// -----------------------------------------------------------------------
+
+void OutputDevice::DrawPolyLine( const Polygon& rPoly )
+{
+ DBG_TRACE( "OutputDevice::DrawPolyLine()" );
+ DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
+ DBG_CHKOBJ( &rPoly, Polygon, NULL );
+
+ if( mpMetaFile )
+ mpMetaFile->AddAction( new MetaPolyLineAction( rPoly ) );
+
+ USHORT nPoints = rPoly.GetSize();
+
+ if ( !IsDeviceOutputNecessary() || !mbLineColor || (nPoints < 2) || ImplIsRecordLayout() )
+ return;
+
+ // we need a graphics
+ if ( !mpGraphics )
+ if ( !ImplGetGraphics() )
+ return;
+
+ if ( mbInitClipRegion )
+ ImplInitClipRegion();
+ if ( mbOutputClipped )
+ return;
+
+ if ( mbInitLineColor )
+ ImplInitLineColor();
+
+ const bool bTryAA((mnAntialiasing & ANTIALIASING_ENABLE_B2DDRAW)
+ && mpGraphics->supportsOperation(OutDevSupport_B2DDraw)
+ && ROP_OVERPAINT == GetRasterOp()
+ && IsLineColor());
+
+ // use b2dpolygon drawing if possible
+ if(bTryAA && ImpTryDrawPolyLineDirect(rPoly.getB2DPolygon(), 0.0, basegfx::B2DLINEJOIN_NONE))
+ {
+ basegfx::B2DPolygon aB2DPolyLine(rPoly.getB2DPolygon());
+ const ::basegfx::B2DHomMatrix aTransform = ImplGetDeviceTransformation();
+ const ::basegfx::B2DVector aB2DLineWidth( 1.0, 1.0 );
+
+ // transform the polygon
+ aB2DPolyLine.transform( aTransform );
+
+ if(mnAntialiasing & ANTIALIASING_PIXELSNAPHAIRLINE)
+ {
+ aB2DPolyLine = basegfx::tools::snapPointsOfHorizontalOrVerticalEdges(aB2DPolyLine);
+ }
+
+ if(mpGraphics->DrawPolyLine( aB2DPolyLine, 0.0, aB2DLineWidth, basegfx::B2DLINEJOIN_NONE, this))
+ {
+ return;
+ }
+ }
+
+ Polygon aPoly = ImplLogicToDevicePixel( rPoly );
+ const SalPoint* pPtAry = (const SalPoint*)aPoly.GetConstPointAry();
+
+ // #100127# Forward beziers to sal, if any
+ if( aPoly.HasFlags() )
+ {
+ const BYTE* pFlgAry = aPoly.GetConstFlagAry();
+ if( !mpGraphics->DrawPolyLineBezier( nPoints, pPtAry, pFlgAry, this ) )
+ {
+ aPoly = ImplSubdivideBezier(aPoly);
+ pPtAry = (const SalPoint*)aPoly.GetConstPointAry();
+ mpGraphics->DrawPolyLine( aPoly.GetSize(), pPtAry, this );
+ }
+ }
+ else
+ {
+ mpGraphics->DrawPolyLine( nPoints, pPtAry, this );
+ }
+
+ if( mpAlphaVDev )
+ mpAlphaVDev->DrawPolyLine( rPoly );
+}
+
+// -----------------------------------------------------------------------
+
+void OutputDevice::DrawPolyLine( const Polygon& rPoly, const LineInfo& rLineInfo )
+{
+ DBG_TRACE( "OutputDevice::DrawPolyLine()" );
+ DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
+ DBG_CHKOBJ( &rPoly, Polygon, NULL );
+
+ if ( rLineInfo.IsDefault() )
+ {
+ DrawPolyLine( rPoly );
+ return;
+ }
+
+ // #i101491#
+ // Try direct Fallback to B2D-Version of DrawPolyLine
+ if((mnAntialiasing & ANTIALIASING_ENABLE_B2DDRAW)
+ && LINE_SOLID == rLineInfo.GetStyle())
+ {
+ DrawPolyLine( rPoly.getB2DPolygon(), (double)rLineInfo.GetWidth(), rLineInfo.GetLineJoin());
+ return;
+ }
+
+ if ( mpMetaFile )
+ mpMetaFile->AddAction( new MetaPolyLineAction( rPoly, rLineInfo ) );
+
+ ImpDrawPolyLineWithLineInfo(rPoly, rLineInfo);
+}
+
+void OutputDevice::ImpDrawPolyLineWithLineInfo(const Polygon& rPoly, const LineInfo& rLineInfo)
+{
+ USHORT nPoints(rPoly.GetSize());
+
+ if ( !IsDeviceOutputNecessary() || !mbLineColor || ( nPoints < 2 ) || ( LINE_NONE == rLineInfo.GetStyle() ) || ImplIsRecordLayout() )
+ return;
+
+ Polygon aPoly = ImplLogicToDevicePixel( rPoly );
+
+ // #100127# LineInfo is not curve-safe, subdivide always
+ //
+ // What shall this mean? It's wrong to subdivide here when the
+ // polygon is a fat line. In that case, the painted geometry
+ // WILL be much different.
+ // I also have no idea how this could be related to the given ID
+ // which reads 'consolidate boost versions' in the task description.
+ // Removing.
+ //
+ //if( aPoly.HasFlags() )
+ //{
+ // aPoly = ImplSubdivideBezier( aPoly );
+ // nPoints = aPoly.GetSize();
+ //}
+
+ // we need a graphics
+ if ( !mpGraphics && !ImplGetGraphics() )
+ return;
+
+ if ( mbInitClipRegion )
+ ImplInitClipRegion();
+
+ if ( mbOutputClipped )
+ return;
+
+ if ( mbInitLineColor )
+ ImplInitLineColor();
+
+ const LineInfo aInfo( ImplLogicToDevicePixel( rLineInfo ) );
+ const bool bDashUsed(LINE_DASH == aInfo.GetStyle());
+ const bool bLineWidthUsed(aInfo.GetWidth() > 1);
+
+ if(bDashUsed || bLineWidthUsed)
+ {
+ impPaintLineGeometryWithEvtlExpand(aInfo, basegfx::B2DPolyPolygon(aPoly.getB2DPolygon()));
+ }
+ else
+ {
+ // #100127# the subdivision HAS to be done here since only a pointer
+ // to an array of points is given to the DrawPolyLine method, there is
+ // NO way to find out there that it's a curve.
+ if( aPoly.HasFlags() )
+ {
+ aPoly = ImplSubdivideBezier( aPoly );
+ nPoints = aPoly.GetSize();
+ }
+
+ mpGraphics->DrawPolyLine(nPoints, (const SalPoint*)aPoly.GetConstPointAry(), this);
+ }
+
+ if( mpAlphaVDev )
+ mpAlphaVDev->DrawPolyLine( rPoly, rLineInfo );
+}
+
+// -----------------------------------------------------------------------
+
+void OutputDevice::DrawPolygon( const Polygon& rPoly )
+{
+ DBG_TRACE( "OutputDevice::DrawPolygon()" );
+ DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
+ DBG_CHKOBJ( &rPoly, Polygon, NULL );
+
+ if( mpMetaFile )
+ mpMetaFile->AddAction( new MetaPolygonAction( rPoly ) );
+
+ USHORT nPoints = rPoly.GetSize();
+
+ if ( !IsDeviceOutputNecessary() || (!mbLineColor && !mbFillColor) || (nPoints < 2) || ImplIsRecordLayout() )
+ return;
+
+ // we need a graphics
+ if ( !mpGraphics )
+ if ( !ImplGetGraphics() )
+ return;
+
+ if ( mbInitClipRegion )
+ ImplInitClipRegion();
+ if ( mbOutputClipped )
+ return;
+
+ if ( mbInitLineColor )
+ ImplInitLineColor();
+ if ( mbInitFillColor )
+ ImplInitFillColor();
+
+ // use b2dpolygon drawing if possible
+ if((mnAntialiasing & ANTIALIASING_ENABLE_B2DDRAW)
+ && mpGraphics->supportsOperation(OutDevSupport_B2DDraw)
+ && ROP_OVERPAINT == GetRasterOp()
+ && (IsLineColor() || IsFillColor()))
+ {
+ const ::basegfx::B2DHomMatrix aTransform = ImplGetDeviceTransformation();
+ basegfx::B2DPolygon aB2DPolygon(rPoly.getB2DPolygon());
+ bool bSuccess(true);
+
+ // transform the polygon and ensure closed
+ aB2DPolygon.transform(aTransform);
+ aB2DPolygon.setClosed(true);
+
+ if(IsFillColor())
+ {
+ bSuccess = mpGraphics->DrawPolyPolygon(basegfx::B2DPolyPolygon(aB2DPolygon), 0.0, this);
+ }
+
+ if(bSuccess && IsLineColor())
+ {
+ const ::basegfx::B2DVector aB2DLineWidth( 1.0, 1.0 );
+
+ if(mnAntialiasing & ANTIALIASING_PIXELSNAPHAIRLINE)
+ {
+ aB2DPolygon = basegfx::tools::snapPointsOfHorizontalOrVerticalEdges(aB2DPolygon);
+ }
+
+ bSuccess = mpGraphics->DrawPolyLine( aB2DPolygon, 0.0, aB2DLineWidth, basegfx::B2DLINEJOIN_NONE, this);
+ }
+
+ if(bSuccess)
+ {
+ return;
+ }
+ }
+
+ Polygon aPoly = ImplLogicToDevicePixel( rPoly );
+ const SalPoint* pPtAry = (const SalPoint*)aPoly.GetConstPointAry();
+
+ // #100127# Forward beziers to sal, if any
+ if( aPoly.HasFlags() )
+ {
+ const BYTE* pFlgAry = aPoly.GetConstFlagAry();
+ if( !mpGraphics->DrawPolygonBezier( nPoints, pPtAry, pFlgAry, this ) )
+ {
+ aPoly = ImplSubdivideBezier(aPoly);
+ pPtAry = (const SalPoint*)aPoly.GetConstPointAry();
+ mpGraphics->DrawPolygon( aPoly.GetSize(), pPtAry, this );
+ }
+ }
+ else
+ {
+ mpGraphics->DrawPolygon( nPoints, pPtAry, this );
+ }
+ if( mpAlphaVDev )
+ mpAlphaVDev->DrawPolygon( rPoly );
+}
+
+// -----------------------------------------------------------------------
+
+void OutputDevice::DrawPolyPolygon( const PolyPolygon& rPolyPoly )
+{
+ DBG_TRACE( "OutputDevice::DrawPolyPolygon()" );
+ DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
+ DBG_CHKOBJ( &rPolyPoly, PolyPolygon, NULL );
+
+ if( mpMetaFile )
+ mpMetaFile->AddAction( new MetaPolyPolygonAction( rPolyPoly ) );
+
+ USHORT nPoly = rPolyPoly.Count();
+
+ if ( !IsDeviceOutputNecessary() || (!mbLineColor && !mbFillColor) || !nPoly || ImplIsRecordLayout() )
+ return;
+
+ // we need a graphics
+ if ( !mpGraphics )
+ if ( !ImplGetGraphics() )
+ return;
+
+ if ( mbInitClipRegion )
+ ImplInitClipRegion();
+ if ( mbOutputClipped )
+ return;
+
+ if ( mbInitLineColor )
+ ImplInitLineColor();
+ if ( mbInitFillColor )
+ ImplInitFillColor();
+
+ // use b2dpolygon drawing if possible
+ if((mnAntialiasing & ANTIALIASING_ENABLE_B2DDRAW)
+ && mpGraphics->supportsOperation(OutDevSupport_B2DDraw)
+ && ROP_OVERPAINT == GetRasterOp()
+ && (IsLineColor() || IsFillColor()))
+ {
+ const ::basegfx::B2DHomMatrix aTransform = ImplGetDeviceTransformation();
+ basegfx::B2DPolyPolygon aB2DPolyPolygon(rPolyPoly.getB2DPolyPolygon());
+ bool bSuccess(true);
+
+ // transform the polygon and ensure closed
+ aB2DPolyPolygon.transform(aTransform);
+ aB2DPolyPolygon.setClosed(true);
+
+ if(IsFillColor())
+ {
+ bSuccess = mpGraphics->DrawPolyPolygon(aB2DPolyPolygon, 0.0, this);
+ }
+
+ if(bSuccess && IsLineColor())
+ {
+ const ::basegfx::B2DVector aB2DLineWidth( 1.0, 1.0 );
+
+ if(mnAntialiasing & ANTIALIASING_PIXELSNAPHAIRLINE)
+ {
+ aB2DPolyPolygon = basegfx::tools::snapPointsOfHorizontalOrVerticalEdges(aB2DPolyPolygon);
+ }
+
+ for(sal_uInt32 a(0); bSuccess && a < aB2DPolyPolygon.count(); a++)
+ {
+ bSuccess = mpGraphics->DrawPolyLine( aB2DPolyPolygon.getB2DPolygon(a), 0.0, aB2DLineWidth, basegfx::B2DLINEJOIN_NONE, this);
+ }
+ }
+
+ if(bSuccess)
+ {
+ return;
+ }
+ }
+
+ if ( nPoly == 1 )
+ {
+ // #100127# Map to DrawPolygon
+ Polygon aPoly = rPolyPoly.GetObject( 0 );
+ if( aPoly.GetSize() >= 2 )
+ {
+ GDIMetaFile* pOldMF = mpMetaFile;
+ mpMetaFile = NULL;
+
+ DrawPolygon( aPoly );
+
+ mpMetaFile = pOldMF;
+ }
+ }
+ else
+ {
+ // #100127# moved real PolyPolygon draw to separate method,
+ // have to call recursively, avoiding duplicate
+ // ImplLogicToDevicePixel calls
+ ImplDrawPolyPolygon( nPoly, ImplLogicToDevicePixel( rPolyPoly ) );
+ }
+ if( mpAlphaVDev )
+ mpAlphaVDev->DrawPolyPolygon( rPolyPoly );
+}
+
+// -----------------------------------------------------------------------
+
+void OutputDevice::DrawPolygon( const ::basegfx::B2DPolygon& rB2DPolygon)
+{
+ // AW: Do NOT paint empty polygons
+ if(rB2DPolygon.count())
+ {
+ ::basegfx::B2DPolyPolygon aPP( rB2DPolygon );
+ DrawPolyPolygon( aPP );
+ }
+}
+
+// -----------------------------------------------------------------------
+// Caution: This method is nearly the same as
+// OutputDevice::DrawTransparent( const basegfx::B2DPolyPolygon& rB2DPolyPoly, double fTransparency),
+// so when changes are made here do not forget to make change sthere, too
+
+void OutputDevice::DrawPolyPolygon( const basegfx::B2DPolyPolygon& rB2DPolyPoly )
+{
+ DBG_TRACE( "OutputDevice::DrawPolyPolygon(B2D&)" );
+ DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
+
+#if 0
+ // MetaB2DPolyPolygonAction is not implemented yet:
+ // according to AW adding it is very dangerous since there is a lot
+ // of code that uses the metafile actions directly and unless every
+ // place that does this knows about the new action we need to fallback
+ if( mpMetaFile )
+ mpMetaFile->AddAction( new MetaB2DPolyPolygonAction( rB2DPolyPoly ) );
+#else
+ if( mpMetaFile )
+ mpMetaFile->AddAction( new MetaPolyPolygonAction( PolyPolygon( rB2DPolyPoly ) ) );
+#endif
+
+ // call helper
+ ImpDrawPolyPolygonWithB2DPolyPolygon(rB2DPolyPoly);
+}
+
+void OutputDevice::ImpDrawPolyPolygonWithB2DPolyPolygon(const basegfx::B2DPolyPolygon& rB2DPolyPoly)
+{
+ // AW: Do NOT paint empty PolyPolygons
+ if(!rB2DPolyPoly.count())
+ return;
+
+ // we need a graphics
+ if( !mpGraphics )
+ if( !ImplGetGraphics() )
+ return;
+
+ if( mbInitClipRegion )
+ ImplInitClipRegion();
+ if( mbOutputClipped )
+ return;
+
+ if( mbInitLineColor )
+ ImplInitLineColor();
+ if( mbInitFillColor )
+ ImplInitFillColor();
+
+ if((mnAntialiasing & ANTIALIASING_ENABLE_B2DDRAW)
+ && mpGraphics->supportsOperation(OutDevSupport_B2DDraw)
+ && ROP_OVERPAINT == GetRasterOp()
+ && (IsLineColor() || IsFillColor()))
+ {
+ const basegfx::B2DHomMatrix aTransform(ImplGetDeviceTransformation());
+ basegfx::B2DPolyPolygon aB2DPolyPolygon(rB2DPolyPoly);
+ bool bSuccess(true);
+
+ // transform the polygon and ensure closed
+ aB2DPolyPolygon.transform(aTransform);
+ aB2DPolyPolygon.setClosed(true);
+
+ if(IsFillColor())
+ {
+ bSuccess = mpGraphics->DrawPolyPolygon(aB2DPolyPolygon, 0.0, this);
+ }
+
+ if(bSuccess && IsLineColor())
+ {
+ const ::basegfx::B2DVector aB2DLineWidth( 1.0, 1.0 );
+
+ if(mnAntialiasing & ANTIALIASING_PIXELSNAPHAIRLINE)
+ {
+ aB2DPolyPolygon = basegfx::tools::snapPointsOfHorizontalOrVerticalEdges(aB2DPolyPolygon);
+ }
+
+ for(sal_uInt32 a(0);bSuccess && a < aB2DPolyPolygon.count(); a++)
+ {
+ bSuccess = mpGraphics->DrawPolyLine( aB2DPolyPolygon.getB2DPolygon(a), 0.0, aB2DLineWidth, basegfx::B2DLINEJOIN_NONE, this);
+ }
+ }
+
+ if(bSuccess)
+ {
+ return;
+ }
+ }
+
+ // fallback to old polygon drawing if needed
+ const PolyPolygon aToolsPolyPolygon( rB2DPolyPoly );
+ const PolyPolygon aPixelPolyPolygon = ImplLogicToDevicePixel( aToolsPolyPolygon );
+ ImplDrawPolyPolygon( aPixelPolyPolygon.Count(), aPixelPolyPolygon );
+}
+
+// -----------------------------------------------------------------------
+
+bool OutputDevice::ImpTryDrawPolyLineDirect(
+ const basegfx::B2DPolygon& rB2DPolygon,
+ double fLineWidth,
+ basegfx::B2DLineJoin eLineJoin)
+{
+ const basegfx::B2DHomMatrix aTransform = ImplGetDeviceTransformation();
+ basegfx::B2DVector aB2DLineWidth(1.0, 1.0);
+
+ // transform the line width if used
+ if( fLineWidth != 0.0 )
+ {
+ aB2DLineWidth = aTransform * ::basegfx::B2DVector( fLineWidth, fLineWidth );
+ }
+
+ // transform the polygon
+ basegfx::B2DPolygon aB2DPolygon(rB2DPolygon);
+ aB2DPolygon.transform(aTransform);
+
+ if((mnAntialiasing & ANTIALIASING_PIXELSNAPHAIRLINE)
+ && aB2DPolygon.count() < 1000)
+ {
+ // #i98289#, #i101491#
+ // better to remove doubles on device coordinates. Also assume from a given amount
+ // of points that the single edges are not long enough to smooth
+ aB2DPolygon.removeDoublePoints();
+ aB2DPolygon = basegfx::tools::snapPointsOfHorizontalOrVerticalEdges(aB2DPolygon);
+ }
+
+ // draw the polyline
+ return mpGraphics->DrawPolyLine( aB2DPolygon, 0.0, aB2DLineWidth, eLineJoin, this);
+}
+
+void OutputDevice::DrawPolyLine(
+ const basegfx::B2DPolygon& rB2DPolygon,
+ double fLineWidth,
+ basegfx::B2DLineJoin eLineJoin)
+{
+ DBG_TRACE( "OutputDevice::DrawPolyLine(B2D&)" );
+ DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
+ (void)eLineJoin; // ATM used in UNX, but not in WNT, access it for warning-free
+
+#if 0 // MetaB2DPolyLineAction is not implemented yet:
+ // according to AW adding it is very dangerous since there is a lot
+ // of code that uses the metafile actions directly and unless every
+ // place that does this knows about the new action we need to fallback
+ if( mpMetaFile )
+ mpMetaFile->AddAction( new MetaB2DPolyLineAction( rB2DPolygon ) );
+#else
+ if( mpMetaFile )
+ {
+ LineInfo aLineInfo;
+ if( fLineWidth != 0.0 )
+ aLineInfo.SetWidth( static_cast<long>(fLineWidth+0.5) );
+ const Polygon aToolsPolygon( rB2DPolygon );
+ mpMetaFile->AddAction( new MetaPolyLineAction( aToolsPolygon, aLineInfo ) );
+ }
+#endif
+
+ // AW: Do NOT paint empty PolyPolygons
+ if(!rB2DPolygon.count())
+ return;
+
+ // we need a graphics
+ if( !mpGraphics )
+ if( !ImplGetGraphics() )
+ return;
+
+ if( mbInitClipRegion )
+ ImplInitClipRegion();
+ if( mbOutputClipped )
+ return;
+
+ if( mbInitLineColor )
+ ImplInitLineColor();
+
+ const bool bTryAA((mnAntialiasing & ANTIALIASING_ENABLE_B2DDRAW)
+ && mpGraphics->supportsOperation(OutDevSupport_B2DDraw)
+ && ROP_OVERPAINT == GetRasterOp()
+ && IsLineColor());
+
+ // use b2dpolygon drawing if possible
+ if(bTryAA && ImpTryDrawPolyLineDirect(rB2DPolygon, fLineWidth, eLineJoin))
+ {
+ return;
+ }
+
+ // #i101491#
+ // no output yet; fallback to geometry decomposition and use filled polygon paint
+ // when line is fat and not too complex. ImpDrawPolyPolygonWithB2DPolyPolygon
+ // will do internal needed AA checks etc.
+ if(fLineWidth >= 2.5
+ && rB2DPolygon.count()
+ && rB2DPolygon.count() <= 1000)
+ {
+ const double fHalfLineWidth((fLineWidth * 0.5) + 0.5);
+ const basegfx::B2DPolyPolygon aAreaPolyPolygon(basegfx::tools::createAreaGeometry(
+ rB2DPolygon, fHalfLineWidth, eLineJoin));
+
+ const Color aOldLineColor(maLineColor);
+ const Color aOldFillColor(maFillColor);
+
+ SetLineColor();
+ ImplInitLineColor();
+ SetFillColor(aOldLineColor);
+ ImplInitFillColor();
+
+ // draw usig a loop; else the topology will paint a PolyPolygon
+ for(sal_uInt32 a(0); a < aAreaPolyPolygon.count(); a++)
+ {
+ ImpDrawPolyPolygonWithB2DPolyPolygon(
+ basegfx::B2DPolyPolygon(aAreaPolyPolygon.getB2DPolygon(a)));
+ }
+
+ SetLineColor(aOldLineColor);
+ ImplInitLineColor();
+ SetFillColor(aOldFillColor);
+ ImplInitFillColor();
+
+ if(bTryAA)
+ {
+ // when AA it is necessary to also paint the filled polygon's outline
+ // to avoid optical gaps
+ for(sal_uInt32 a(0); a < aAreaPolyPolygon.count(); a++)
+ {
+ ImpTryDrawPolyLineDirect(aAreaPolyPolygon.getB2DPolygon(a), 0.0, basegfx::B2DLINEJOIN_NONE);
+ }
+ }
+ }
+ else
+ {
+ // fallback to old polygon drawing if needed
+ const Polygon aToolsPolygon( rB2DPolygon );
+ LineInfo aLineInfo;
+ if( fLineWidth != 0.0 )
+ aLineInfo.SetWidth( static_cast<long>(fLineWidth+0.5) );
+ ImpDrawPolyLineWithLineInfo( aToolsPolygon, aLineInfo );
+ }
+}
+
+// -----------------------------------------------------------------------
+
+void OutputDevice::Push( USHORT nFlags )
+{
+ DBG_TRACE( "OutputDevice::Push()" );
+ DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
+
+ if ( mpMetaFile )
+ mpMetaFile->AddAction( new MetaPushAction( nFlags ) );
+
+ ImplObjStack* pData = new ImplObjStack;
+ pData->mpPrev = mpObjStack;
+ mpObjStack = pData;
+
+ pData->mnFlags = nFlags;
+
+ if ( nFlags & PUSH_LINECOLOR )
+ {
+ if ( mbLineColor )
+ pData->mpLineColor = new Color( maLineColor );
+ else
+ pData->mpLineColor = NULL;
+ }
+ if ( nFlags & PUSH_FILLCOLOR )
+ {
+ if ( mbFillColor )
+ pData->mpFillColor = new Color( maFillColor );
+ else
+ pData->mpFillColor = NULL;
+ }
+ if ( nFlags & PUSH_FONT )
+ pData->mpFont = new Font( maFont );
+ if ( nFlags & PUSH_TEXTCOLOR )
+ pData->mpTextColor = new Color( GetTextColor() );
+ if ( nFlags & PUSH_TEXTFILLCOLOR )
+ {
+ if ( IsTextFillColor() )
+ pData->mpTextFillColor = new Color( GetTextFillColor() );
+ else
+ pData->mpTextFillColor = NULL;
+ }
+ if ( nFlags & PUSH_TEXTLINECOLOR )
+ {
+ if ( IsTextLineColor() )
+ pData->mpTextLineColor = new Color( GetTextLineColor() );
+ else
+ pData->mpTextLineColor = NULL;
+ }
+ if ( nFlags & PUSH_OVERLINECOLOR )
+ {
+ if ( IsOverlineColor() )
+ pData->mpOverlineColor = new Color( GetOverlineColor() );
+ else
+ pData->mpOverlineColor = NULL;
+ }
+ if ( nFlags & PUSH_TEXTALIGN )
+ pData->meTextAlign = GetTextAlign();
+ if( nFlags & PUSH_TEXTLAYOUTMODE )
+ pData->mnTextLayoutMode = GetLayoutMode();
+ if( nFlags & PUSH_TEXTLANGUAGE )
+ pData->meTextLanguage = GetDigitLanguage();
+ if ( nFlags & PUSH_RASTEROP )
+ pData->meRasterOp = GetRasterOp();
+ if ( nFlags & PUSH_MAPMODE )
+ {
+ if ( mbMap )
+ pData->mpMapMode = new MapMode( maMapMode );
+ else
+ pData->mpMapMode = NULL;
+ }
+ if ( nFlags & PUSH_CLIPREGION )
+ {
+ if ( mbClipRegion )
+ pData->mpClipRegion = new Region( maRegion );
+ else
+ pData->mpClipRegion = NULL;
+ }
+ if ( nFlags & PUSH_REFPOINT )
+ {
+ if ( mbRefPoint )
+ pData->mpRefPoint = new Point( maRefPoint );
+ else
+ pData->mpRefPoint = NULL;
+ }
+
+ if( mpAlphaVDev )
+ mpAlphaVDev->Push();
+}
+
+// -----------------------------------------------------------------------
+
+void OutputDevice::Pop()
+{
+ DBG_TRACE( "OutputDevice::Pop()" );
+ DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
+
+ if( mpMetaFile )
+ mpMetaFile->AddAction( new MetaPopAction() );
+
+ GDIMetaFile* pOldMetaFile = mpMetaFile;
+ ImplObjStack* pData = mpObjStack;
+ mpMetaFile = NULL;
+
+ if ( !pData )
+ {
+ DBG_ERRORFILE( "OutputDevice::Pop() without OutputDevice::Push()" );
+ return;
+ }
+
+ if( mpAlphaVDev )
+ mpAlphaVDev->Pop();
+
+ mpObjStack = pData->mpPrev;
+
+ if ( pData->mnFlags & PUSH_LINECOLOR )
+ {
+ if ( pData->mpLineColor )
+ SetLineColor( *pData->mpLineColor );
+ else
+ SetLineColor();
+ }
+ if ( pData->mnFlags & PUSH_FILLCOLOR )
+ {
+ if ( pData->mpFillColor )
+ SetFillColor( *pData->mpFillColor );
+ else
+ SetFillColor();
+ }
+ if ( pData->mnFlags & PUSH_FONT )
+ SetFont( *pData->mpFont );
+ if ( pData->mnFlags & PUSH_TEXTCOLOR )
+ SetTextColor( *pData->mpTextColor );
+ if ( pData->mnFlags & PUSH_TEXTFILLCOLOR )
+ {
+ if ( pData->mpTextFillColor )
+ SetTextFillColor( *pData->mpTextFillColor );
+ else
+ SetTextFillColor();
+ }
+ if ( pData->mnFlags & PUSH_TEXTLINECOLOR )
+ {
+ if ( pData->mpTextLineColor )
+ SetTextLineColor( *pData->mpTextLineColor );
+ else
+ SetTextLineColor();
+ }
+ if ( pData->mnFlags & PUSH_OVERLINECOLOR )
+ {
+ if ( pData->mpOverlineColor )
+ SetOverlineColor( *pData->mpOverlineColor );
+ else
+ SetOverlineColor();
+ }
+ if ( pData->mnFlags & PUSH_TEXTALIGN )
+ SetTextAlign( pData->meTextAlign );
+ if( pData->mnFlags & PUSH_TEXTLAYOUTMODE )
+ SetLayoutMode( pData->mnTextLayoutMode );
+ if( pData->mnFlags & PUSH_TEXTLANGUAGE )
+ SetDigitLanguage( pData->meTextLanguage );
+ if ( pData->mnFlags & PUSH_RASTEROP )
+ SetRasterOp( pData->meRasterOp );
+ if ( pData->mnFlags & PUSH_MAPMODE )
+ {
+ if ( pData->mpMapMode )
+ SetMapMode( *pData->mpMapMode );
+ else
+ SetMapMode();
+ }
+ if ( pData->mnFlags & PUSH_CLIPREGION )
+ ImplSetClipRegion( pData->mpClipRegion );
+ if ( pData->mnFlags & PUSH_REFPOINT )
+ {
+ if ( pData->mpRefPoint )
+ SetRefPoint( *pData->mpRefPoint );
+ else
+ SetRefPoint();
+ }
+
+ ImplDeleteObjStack( pData );
+
+ mpMetaFile = pOldMetaFile;
+}
+
+// -----------------------------------------------------------------------
+
+void OutputDevice::SetConnectMetaFile( GDIMetaFile* pMtf )
+{
+ mpMetaFile = pMtf;
+}
+
+// -----------------------------------------------------------------------
+
+void OutputDevice::EnableOutput( BOOL bEnable )
+{
+ mbOutput = (bEnable != 0);
+
+ if( mpAlphaVDev )
+ mpAlphaVDev->EnableOutput( bEnable );
+}
+
+// -----------------------------------------------------------------------
+
+void OutputDevice::SetSettings( const AllSettings& rSettings )
+{
+ maSettings = rSettings;
+
+ if( mpAlphaVDev )
+ mpAlphaVDev->SetSettings( rSettings );
+}
+
+// -----------------------------------------------------------------------
+
+USHORT OutputDevice::GetBitCount() const
+{
+ DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
+
+ if ( meOutDevType == OUTDEV_VIRDEV )
+ return ((VirtualDevice*)this)->mnBitCount;
+
+ // we need a graphics
+ if ( !mpGraphics )
+ {
+ if ( !((OutputDevice*)this)->ImplGetGraphics() )
+ return 0;
+ }
+
+ return (USHORT)mpGraphics->GetBitCount();
+}
+
+// -----------------------------------------------------------------------
+
+USHORT OutputDevice::GetAlphaBitCount() const
+{
+ DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
+
+ if ( meOutDevType == OUTDEV_VIRDEV &&
+ mpAlphaVDev != NULL )
+ {
+ return mpAlphaVDev->GetBitCount();
+ }
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------
+
+ULONG OutputDevice::GetColorCount() const
+{
+ DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
+
+ const USHORT nBitCount = GetBitCount();
+ return( ( nBitCount > 31 ) ? ULONG_MAX : ( ( (ULONG) 1 ) << nBitCount) );
+}
+
+// -----------------------------------------------------------------------
+
+BOOL OutputDevice::HasAlpha()
+{
+ return mpAlphaVDev != NULL;
+}
+
+// -----------------------------------------------------------------------
+
+::com::sun::star::uno::Reference< ::com::sun::star::awt::XGraphics > OutputDevice::CreateUnoGraphics()
+{
+ UnoWrapperBase* pWrapper = Application::GetUnoWrapper();
+ return pWrapper ? pWrapper->CreateGraphics( this ) : ::com::sun::star::uno::Reference< ::com::sun::star::awt::XGraphics >();
+}
+
+// -----------------------------------------------------------------------
+
+SystemGraphicsData OutputDevice::GetSystemGfxData() const
+{
+ if ( !mpGraphics )
+ {
+ if ( !ImplGetGraphics() )
+ return SystemGraphicsData();
+ }
+
+ return mpGraphics->GetGraphicsData();
+}
+
+// -----------------------------------------------------------------------
+
+::com::sun::star::uno::Any OutputDevice::GetSystemGfxDataAny() const
+{
+ ::com::sun::star::uno::Any aRet;
+ const SystemGraphicsData aSysData = GetSystemGfxData();
+ ::com::sun::star::uno::Sequence< sal_Int8 > aSeq( (sal_Int8*)&aSysData,
+ aSysData.nSize );
+
+ return uno::makeAny(aSeq);
+}
+
+// -----------------------------------------------------------------------
+
+::com::sun::star::uno::Reference< ::com::sun::star::rendering::XCanvas > OutputDevice::GetCanvas() const
+{
+ uno::Sequence< uno::Any > aArg(6);
+
+ aArg[ 0 ] = uno::makeAny( reinterpret_cast<sal_Int64>(this) );
+ aArg[ 2 ] = uno::makeAny( ::com::sun::star::awt::Rectangle( mnOutOffX, mnOutOffY, mnOutWidth, mnOutHeight ) );
+ aArg[ 3 ] = uno::makeAny( sal_False );
+ aArg[ 5 ] = GetSystemGfxDataAny();
+
+ uno::Reference<lang::XMultiServiceFactory> xFactory = vcl::unohelper::GetMultiServiceFactory();
+
+ uno::Reference<rendering::XCanvas> xCanvas;
+
+ // Create canvas instance with window handle
+ // =========================================
+ if ( xFactory.is() )
+ {
+ static uno::Reference<lang::XMultiServiceFactory> xCanvasFactory(
+ xFactory->createInstance(
+ OUString( RTL_CONSTASCII_USTRINGPARAM(
+ "com.sun.star."
+ "rendering.CanvasFactory") ) ),
+ uno::UNO_QUERY );
+ if(xCanvasFactory.is())
+ {
+ xCanvas.set(
+ xCanvasFactory->createInstanceWithArguments(
+ OUString( RTL_CONSTASCII_USTRINGPARAM(
+ "com.sun.star.rendering.Canvas" )),
+ aArg ),
+ uno::UNO_QUERY );
+ }
+ }
+
+ return xCanvas;
+}
+
+// -----------------------------------------------------------------------