summaryrefslogtreecommitdiff
path: root/vcl/source/gdi/outdev/outdev2.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'vcl/source/gdi/outdev/outdev2.cxx')
-rw-r--r--vcl/source/gdi/outdev/outdev2.cxx2228
1 files changed, 2228 insertions, 0 deletions
diff --git a/vcl/source/gdi/outdev/outdev2.cxx b/vcl/source/gdi/outdev/outdev2.cxx
new file mode 100644
index 000000000000..daf4e2c5ac8b
--- /dev/null
+++ b/vcl/source/gdi/outdev/outdev2.cxx
@@ -0,0 +1,2228 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <tools/debug.hxx>
+#include <vcl/bitmap.hxx>
+#include <vcl/bitmapex.hxx>
+#include <vcl/window.hxx>
+#include <vcl/metaact.hxx>
+#include <vcl/gdimtf.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/bmpacc.hxx>
+#include <vcl/outdev.hxx>
+#include <vcl/image.hxx>
+#include <bmpfast.hxx>
+#include <salbmp.hxx>
+#include <salgdi.hxx>
+#include <impbmp.hxx>
+#include <sallayout.hxx>
+#include <image.h>
+#include <outdev.h>
+#include <window.h>
+#include <outdata.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <boost/scoped_array.hpp>
+
+#define OUTDEV_INIT() \
+{ \
+ if ( !IsDeviceOutputNecessary() ) \
+ return; \
+ \
+ if ( !mpGraphics ) \
+ if ( !ImplGetGraphics() ) \
+ return; \
+ \
+ if ( mbInitClipRegion ) \
+ ImplInitClipRegion(); \
+ \
+ if ( mbOutputClipped ) \
+ return; \
+}
+
+extern const sal_uLong nVCLRLut[ 6 ];
+extern const sal_uLong nVCLGLut[ 6 ];
+extern const sal_uLong nVCLBLut[ 6 ];
+extern const sal_uLong nVCLDitherLut[ 256 ];
+extern const sal_uLong nVCLLut[ 256 ];
+
+sal_uLong ImplAdjustTwoRect( SalTwoRect& rTwoRect, const Size& rSizePix )
+{
+ sal_uLong nMirrFlags = 0;
+
+ if ( rTwoRect.mnDestWidth < 0 )
+ {
+ rTwoRect.mnSrcX = rSizePix.Width() - rTwoRect.mnSrcX - rTwoRect.mnSrcWidth;
+ rTwoRect.mnDestWidth = -rTwoRect.mnDestWidth;
+ rTwoRect.mnDestX -= rTwoRect.mnDestWidth-1;
+ nMirrFlags |= BMP_MIRROR_HORZ;
+ }
+
+ if ( rTwoRect.mnDestHeight < 0 )
+ {
+ rTwoRect.mnSrcY = rSizePix.Height() - rTwoRect.mnSrcY - rTwoRect.mnSrcHeight;
+ rTwoRect.mnDestHeight = -rTwoRect.mnDestHeight;
+ rTwoRect.mnDestY -= rTwoRect.mnDestHeight-1;
+ nMirrFlags |= BMP_MIRROR_VERT;
+ }
+
+ if( ( rTwoRect.mnSrcX < 0 ) || ( rTwoRect.mnSrcX >= rSizePix.Width() ) ||
+ ( rTwoRect.mnSrcY < 0 ) || ( rTwoRect.mnSrcY >= rSizePix.Height() ) ||
+ ( ( rTwoRect.mnSrcX + rTwoRect.mnSrcWidth ) > rSizePix.Width() ) ||
+ ( ( rTwoRect.mnSrcY + rTwoRect.mnSrcHeight ) > rSizePix.Height() ) )
+ {
+ const Rectangle aSourceRect( Point( rTwoRect.mnSrcX, rTwoRect.mnSrcY ),
+ Size( rTwoRect.mnSrcWidth, rTwoRect.mnSrcHeight ) );
+ Rectangle aCropRect( aSourceRect );
+
+ aCropRect.Intersection( Rectangle( Point(), rSizePix ) );
+
+ if( aCropRect.IsEmpty() )
+ rTwoRect.mnSrcWidth = rTwoRect.mnSrcHeight = rTwoRect.mnDestWidth = rTwoRect.mnDestHeight = 0;
+ else
+ {
+ const double fFactorX = ( rTwoRect.mnSrcWidth > 1 ) ? (double) ( rTwoRect.mnDestWidth - 1 ) / ( rTwoRect.mnSrcWidth - 1 ) : 0.0;
+ const double fFactorY = ( rTwoRect.mnSrcHeight > 1 ) ? (double) ( rTwoRect.mnDestHeight - 1 ) / ( rTwoRect.mnSrcHeight - 1 ) : 0.0;
+
+ const long nDstX1 = rTwoRect.mnDestX + FRound( fFactorX * ( aCropRect.Left() - rTwoRect.mnSrcX ) );
+ const long nDstY1 = rTwoRect.mnDestY + FRound( fFactorY * ( aCropRect.Top() - rTwoRect.mnSrcY ) );
+ const long nDstX2 = rTwoRect.mnDestX + FRound( fFactorX * ( aCropRect.Right() - rTwoRect.mnSrcX ) );
+ const long nDstY2 = rTwoRect.mnDestY + FRound( fFactorY * ( aCropRect.Bottom() - rTwoRect.mnSrcY ) );
+
+ rTwoRect.mnSrcX = aCropRect.Left();
+ rTwoRect.mnSrcY = aCropRect.Top();
+ rTwoRect.mnSrcWidth = aCropRect.GetWidth();
+ rTwoRect.mnSrcHeight = aCropRect.GetHeight();
+ rTwoRect.mnDestX = nDstX1;
+ rTwoRect.mnDestY = nDstY1;
+ rTwoRect.mnDestWidth = nDstX2 - nDstX1 + 1;
+ rTwoRect.mnDestHeight = nDstY2 - nDstY1 + 1;
+ }
+ }
+
+ return nMirrFlags;
+}
+
+void ImplAdjustTwoRect( SalTwoRect& rTwoRect, const Rectangle& rValidSrcRect )
+{
+ if( ( rTwoRect.mnSrcX < rValidSrcRect.Left() ) || ( rTwoRect.mnSrcX >= rValidSrcRect.Right() ) ||
+ ( rTwoRect.mnSrcY < rValidSrcRect.Top() ) || ( rTwoRect.mnSrcY >= rValidSrcRect.Bottom() ) ||
+ ( ( rTwoRect.mnSrcX + rTwoRect.mnSrcWidth ) > rValidSrcRect.Right() ) ||
+ ( ( rTwoRect.mnSrcY + rTwoRect.mnSrcHeight ) > rValidSrcRect.Bottom() ) )
+ {
+ const Rectangle aSourceRect( Point( rTwoRect.mnSrcX, rTwoRect.mnSrcY ),
+ Size( rTwoRect.mnSrcWidth, rTwoRect.mnSrcHeight ) );
+ Rectangle aCropRect( aSourceRect );
+
+ aCropRect.Intersection( rValidSrcRect );
+
+ if( aCropRect.IsEmpty() )
+ rTwoRect.mnSrcWidth = rTwoRect.mnSrcHeight = rTwoRect.mnDestWidth = rTwoRect.mnDestHeight = 0;
+ else
+ {
+ const double fFactorX = ( rTwoRect.mnSrcWidth > 1 ) ? (double) ( rTwoRect.mnDestWidth - 1 ) / ( rTwoRect.mnSrcWidth - 1 ) : 0.0;
+ const double fFactorY = ( rTwoRect.mnSrcHeight > 1 ) ? (double) ( rTwoRect.mnDestHeight - 1 ) / ( rTwoRect.mnSrcHeight - 1 ) : 0.0;
+
+ const long nDstX1 = rTwoRect.mnDestX + FRound( fFactorX * ( aCropRect.Left() - rTwoRect.mnSrcX ) );
+ const long nDstY1 = rTwoRect.mnDestY + FRound( fFactorY * ( aCropRect.Top() - rTwoRect.mnSrcY ) );
+ const long nDstX2 = rTwoRect.mnDestX + FRound( fFactorX * ( aCropRect.Right() - rTwoRect.mnSrcX ) );
+ const long nDstY2 = rTwoRect.mnDestY + FRound( fFactorY * ( aCropRect.Bottom() - rTwoRect.mnSrcY ) );
+
+ rTwoRect.mnSrcX = aCropRect.Left();
+ rTwoRect.mnSrcY = aCropRect.Top();
+ rTwoRect.mnSrcWidth = aCropRect.GetWidth();
+ rTwoRect.mnSrcHeight = aCropRect.GetHeight();
+ rTwoRect.mnDestX = nDstX1;
+ rTwoRect.mnDestY = nDstY1;
+ rTwoRect.mnDestWidth = nDstX2 - nDstX1 + 1;
+ rTwoRect.mnDestHeight = nDstY2 - nDstY1 + 1;
+ }
+ }
+}
+
+void OutputDevice::ImplDrawOutDevDirect( const OutputDevice* pSrcDev, SalTwoRect& rPosAry )
+{
+ SalGraphics* pGraphics2;
+
+ if ( this == pSrcDev )
+ pGraphics2 = NULL;
+ else
+ {
+ if ( (GetOutDevType() != pSrcDev->GetOutDevType()) ||
+ (GetOutDevType() != OUTDEV_WINDOW) )
+ {
+ if ( !pSrcDev->mpGraphics )
+ {
+ if ( !((OutputDevice*)pSrcDev)->ImplGetGraphics() )
+ return;
+ }
+ pGraphics2 = pSrcDev->mpGraphics;
+ }
+ else
+ {
+ if ( ((Window*)this)->mpWindowImpl->mpFrameWindow == ((Window*)pSrcDev)->mpWindowImpl->mpFrameWindow )
+ pGraphics2 = NULL;
+ else
+ {
+ if ( !pSrcDev->mpGraphics )
+ {
+ if ( !((OutputDevice*)pSrcDev)->ImplGetGraphics() )
+ return;
+ }
+ pGraphics2 = pSrcDev->mpGraphics;
+
+ if ( !mpGraphics )
+ {
+ if ( !ImplGetGraphics() )
+ return;
+ }
+ DBG_ASSERT( mpGraphics && pSrcDev->mpGraphics,
+ "OutputDevice::DrawOutDev(): We need more than one Graphics" );
+ }
+ }
+ }
+
+ // #102532# Offset only has to be pseudo window offset
+ const Rectangle aSrcOutRect( Point( pSrcDev->mnOutOffX, pSrcDev->mnOutOffY ),
+ Size( pSrcDev->mnOutWidth, pSrcDev->mnOutHeight ) );
+
+ ImplAdjustTwoRect( rPosAry, aSrcOutRect );
+
+ if ( rPosAry.mnSrcWidth && rPosAry.mnSrcHeight && rPosAry.mnDestWidth && rPosAry.mnDestHeight )
+ {
+ // --- RTL --- if this is no window, but pSrcDev is a window
+ // mirroring may be required
+ // because only windows have a SalGraphicsLayout
+ // mirroring is performed here
+ if( (GetOutDevType() != OUTDEV_WINDOW) && pGraphics2 && (pGraphics2->GetLayout() & SAL_LAYOUT_BIDI_RTL) )
+ {
+ SalTwoRect aPosAry2 = rPosAry;
+ pGraphics2->mirror( aPosAry2.mnSrcX, aPosAry2.mnSrcWidth, pSrcDev );
+ mpGraphics->CopyBits( aPosAry2, pGraphics2, this, pSrcDev );
+ }
+ else
+ mpGraphics->CopyBits( rPosAry, pGraphics2, this, pSrcDev );
+ }
+}
+
+void OutputDevice::DrawOutDev( const Point& rDestPt, const Size& rDestSize,
+ const Point& rSrcPt, const Size& rSrcSize )
+{
+ if( ImplIsRecordLayout() )
+ return;
+
+ if ( ROP_INVERT == meRasterOp )
+ {
+ DrawRect( Rectangle( rDestPt, rDestSize ) );
+ return;
+ }
+
+ if ( mpMetaFile )
+ {
+ const Bitmap aBmp( GetBitmap( rSrcPt, rSrcSize ) );
+ mpMetaFile->AddAction( new MetaBmpScaleAction( rDestPt, rDestSize, aBmp ) );
+ }
+
+ OUTDEV_INIT();
+
+ SalTwoRect aPosAry;
+ aPosAry.mnSrcWidth = ImplLogicWidthToDevicePixel( rSrcSize.Width() );
+ aPosAry.mnSrcHeight = ImplLogicHeightToDevicePixel( rSrcSize.Height() );
+ aPosAry.mnDestWidth = ImplLogicWidthToDevicePixel( rDestSize.Width() );
+ aPosAry.mnDestHeight = ImplLogicHeightToDevicePixel( rDestSize.Height() );
+
+ if ( aPosAry.mnSrcWidth && aPosAry.mnSrcHeight && aPosAry.mnDestWidth && aPosAry.mnDestHeight )
+ {
+ aPosAry.mnSrcX = ImplLogicXToDevicePixel( rSrcPt.X() );
+ aPosAry.mnSrcY = ImplLogicYToDevicePixel( rSrcPt.Y() );
+ aPosAry.mnDestX = ImplLogicXToDevicePixel( rDestPt.X() );
+ aPosAry.mnDestY = ImplLogicYToDevicePixel( rDestPt.Y() );
+
+ const Rectangle aSrcOutRect( Point( mnOutOffX, mnOutOffY ),
+ Size( mnOutWidth, mnOutHeight ) );
+
+ ImplAdjustTwoRect( aPosAry, aSrcOutRect );
+
+ if ( aPosAry.mnSrcWidth && aPosAry.mnSrcHeight && aPosAry.mnDestWidth && aPosAry.mnDestHeight )
+ mpGraphics->CopyBits( aPosAry, NULL, this, NULL );
+ }
+
+ if( mpAlphaVDev )
+ mpAlphaVDev->DrawOutDev( rDestPt, rDestSize, rSrcPt, rSrcSize );
+}
+
+void OutputDevice::DrawOutDev( const Point& rDestPt, const Size& rDestSize,
+ const Point& rSrcPt, const Size& rSrcSize,
+ const OutputDevice& rOutDev )
+{
+ if ( ImplIsRecordLayout() )
+ return;
+
+ if ( ROP_INVERT == meRasterOp )
+ {
+ DrawRect( Rectangle( rDestPt, rDestSize ) );
+ return;
+ }
+
+ if ( mpMetaFile )
+ {
+ const Bitmap aBmp( rOutDev.GetBitmap( rSrcPt, rSrcSize ) );
+ mpMetaFile->AddAction( new MetaBmpScaleAction( rDestPt, rDestSize, aBmp ) );
+ }
+
+ OUTDEV_INIT();
+
+ SalTwoRect aPosAry;
+ aPosAry.mnSrcX = rOutDev.ImplLogicXToDevicePixel( rSrcPt.X() );
+ aPosAry.mnSrcY = rOutDev.ImplLogicYToDevicePixel( rSrcPt.Y() );
+ aPosAry.mnSrcWidth = rOutDev.ImplLogicWidthToDevicePixel( rSrcSize.Width() );
+ aPosAry.mnSrcHeight = rOutDev.ImplLogicHeightToDevicePixel( rSrcSize.Height() );
+ aPosAry.mnDestX = ImplLogicXToDevicePixel( rDestPt.X() );
+ aPosAry.mnDestY = ImplLogicYToDevicePixel( rDestPt.Y() );
+ aPosAry.mnDestWidth = ImplLogicWidthToDevicePixel( rDestSize.Width() );
+ aPosAry.mnDestHeight = ImplLogicHeightToDevicePixel( rDestSize.Height() );
+
+ if( mpAlphaVDev )
+ {
+ if( rOutDev.mpAlphaVDev )
+ {
+ // alpha-blend source over destination
+ DrawBitmapEx( rDestPt, rDestSize, rOutDev.GetBitmapEx(rSrcPt, rSrcSize) );
+ }
+ else
+ {
+ ImplDrawOutDevDirect( &rOutDev, aPosAry );
+
+ // #i32109#: make destination rectangle opaque - source has no alpha
+ mpAlphaVDev->ImplFillOpaqueRectangle( Rectangle(rDestPt, rDestSize) );
+ }
+ }
+ else
+ {
+ if( rOutDev.mpAlphaVDev )
+ {
+ // alpha-blend source over destination
+ DrawBitmapEx( rDestPt, rDestSize, rOutDev.GetBitmapEx(rSrcPt, rSrcSize) );
+ }
+ else
+ {
+ // no alpha at all, neither in source nor destination device
+ ImplDrawOutDevDirect( &rOutDev, aPosAry );
+ }
+ }
+}
+
+void OutputDevice::CopyArea( const Point& rDestPt,
+ const Point& rSrcPt, const Size& rSrcSize,
+ sal_uInt16 nFlags )
+{
+ if ( ImplIsRecordLayout() )
+ return;
+
+ RasterOp eOldRop = GetRasterOp();
+ SetRasterOp( ROP_OVERPAINT );
+
+ OUTDEV_INIT();
+
+ SalTwoRect aPosAry;
+ aPosAry.mnSrcWidth = ImplLogicWidthToDevicePixel( rSrcSize.Width() );
+ aPosAry.mnSrcHeight = ImplLogicHeightToDevicePixel( rSrcSize.Height() );
+
+ if ( aPosAry.mnSrcWidth && aPosAry.mnSrcHeight )
+ {
+ aPosAry.mnSrcX = ImplLogicXToDevicePixel( rSrcPt.X() );
+ aPosAry.mnSrcY = ImplLogicYToDevicePixel( rSrcPt.Y() );
+ aPosAry.mnDestX = ImplLogicXToDevicePixel( rDestPt.X() );
+ aPosAry.mnDestY = ImplLogicYToDevicePixel( rDestPt.Y() );
+ aPosAry.mnDestWidth = aPosAry.mnSrcWidth;
+ aPosAry.mnDestHeight = aPosAry.mnSrcHeight;
+
+ const Rectangle aSrcOutRect( Point( mnOutOffX, mnOutOffY ),
+ Size( mnOutWidth, mnOutHeight ) );
+
+ ImplAdjustTwoRect( aPosAry, aSrcOutRect );
+
+ CopyAreaFinal ( aPosAry, nFlags );
+ }
+
+ SetRasterOp( eOldRop );
+
+ if( mpAlphaVDev )
+ mpAlphaVDev->CopyArea( rDestPt, rSrcPt, rSrcSize, nFlags );
+}
+
+void OutputDevice::CopyAreaFinal( SalTwoRect& aPosAry, sal_uInt32 /*nFlags*/)
+{
+ if (aPosAry.mnSrcWidth == 0 || aPosAry.mnSrcHeight == 0 || aPosAry.mnDestWidth == 0 || aPosAry.mnDestHeight == 0)
+ return;
+
+ aPosAry.mnDestWidth = aPosAry.mnSrcWidth;
+ aPosAry.mnDestHeight = aPosAry.mnSrcHeight;
+ mpGraphics->CopyBits(aPosAry, NULL, this, NULL);
+}
+
+void OutputDevice::ImplDrawFrameDev( const Point& rPt, const Point& rDevPt, const Size& rDevSize,
+ const OutputDevice& rOutDev, const Region& rRegion )
+{
+
+ GDIMetaFile* pOldMetaFile = mpMetaFile;
+ bool bOldMap = mbMap;
+ RasterOp eOldROP = GetRasterOp();
+ mpMetaFile = NULL;
+ mbMap = false;
+ SetRasterOp( ROP_OVERPAINT );
+
+ if ( !IsDeviceOutputNecessary() )
+ return;
+
+ if ( !mpGraphics )
+ {
+ if ( !ImplGetGraphics() )
+ return;
+ }
+
+ // ClipRegion zuruecksetzen
+ if ( rRegion.IsNull() )
+ mpGraphics->ResetClipRegion();
+ else
+ ImplSelectClipRegion( rRegion );
+
+ SalTwoRect aPosAry;
+ aPosAry.mnSrcX = rDevPt.X();
+ aPosAry.mnSrcY = rDevPt.Y();
+ aPosAry.mnSrcWidth = rDevSize.Width();
+ aPosAry.mnSrcHeight = rDevSize.Height();
+ aPosAry.mnDestX = rPt.X();
+ aPosAry.mnDestY = rPt.Y();
+ aPosAry.mnDestWidth = rDevSize.Width();
+ aPosAry.mnDestHeight = rDevSize.Height();
+ ImplDrawOutDevDirect( &rOutDev, aPosAry );
+
+ // Ensure that ClipRegion is recalculated and set
+ mbInitClipRegion = true;
+
+ SetRasterOp( eOldROP );
+ mbMap = bOldMap;
+ mpMetaFile = pOldMetaFile;
+}
+
+void OutputDevice::ImplGetFrameDev( const Point& rPt, const Point& rDevPt, const Size& rDevSize,
+ OutputDevice& rDev )
+{
+
+ bool bOldMap = mbMap;
+ mbMap = false;
+ rDev.DrawOutDev( rDevPt, rDevSize, rPt, rDevSize, *this );
+ mbMap = bOldMap;
+}
+
+void OutputDevice::DrawBitmap( const Point& rDestPt, const Bitmap& rBitmap )
+{
+ if( ImplIsRecordLayout() )
+ return;
+
+ const Size aSizePix( rBitmap.GetSizePixel() );
+ ImplDrawBitmap( rDestPt, PixelToLogic( aSizePix ), Point(), aSizePix, rBitmap, META_BMP_ACTION );
+
+ if( mpAlphaVDev )
+ {
+ // #i32109#: Make bitmap area opaque
+ mpAlphaVDev->ImplFillOpaqueRectangle( Rectangle(rDestPt, PixelToLogic( aSizePix )) );
+ }
+}
+
+void OutputDevice::DrawBitmap( const Point& rDestPt, const Size& rDestSize, const Bitmap& rBitmap )
+{
+ if( ImplIsRecordLayout() )
+ return;
+
+ ImplDrawBitmap( rDestPt, rDestSize, Point(), rBitmap.GetSizePixel(), rBitmap, META_BMPSCALE_ACTION );
+
+ if( mpAlphaVDev )
+ {
+ // #i32109#: Make bitmap area opaque
+ mpAlphaVDev->ImplFillOpaqueRectangle( Rectangle(rDestPt, rDestSize) );
+ }
+}
+
+void OutputDevice::DrawBitmap( const Point& rDestPt, const Size& rDestSize,
+ const Point& rSrcPtPixel, const Size& rSrcSizePixel,
+ const Bitmap& rBitmap )
+{
+ if( ImplIsRecordLayout() )
+ return;
+
+ ImplDrawBitmap( rDestPt, rDestSize, rSrcPtPixel, rSrcSizePixel, rBitmap, META_BMPSCALEPART_ACTION );
+
+ if( mpAlphaVDev )
+ {
+ // #i32109#: Make bitmap area opaque
+ mpAlphaVDev->ImplFillOpaqueRectangle( Rectangle(rDestPt, rDestSize) );
+ }
+}
+
+void OutputDevice::ImplDrawBitmap( const Point& rDestPt, const Size& rDestSize,
+ const Point& rSrcPtPixel, const Size& rSrcSizePixel,
+ const Bitmap& rBitmap, const sal_uLong nAction )
+{
+
+ if ( ( mnDrawMode & DRAWMODE_NOBITMAP ) )
+ {
+ return;
+ }
+ if ( ROP_INVERT == meRasterOp )
+ {
+ DrawRect( Rectangle( rDestPt, rDestSize ) );
+ return;
+ }
+
+ Bitmap aBmp( rBitmap );
+
+ if ( mnDrawMode & ( DRAWMODE_BLACKBITMAP | DRAWMODE_WHITEBITMAP |
+ DRAWMODE_GRAYBITMAP | DRAWMODE_GHOSTEDBITMAP ) )
+ {
+ if ( mnDrawMode & ( DRAWMODE_BLACKBITMAP | DRAWMODE_WHITEBITMAP ) )
+ {
+ sal_uInt8 cCmpVal;
+
+ if ( mnDrawMode & DRAWMODE_BLACKBITMAP )
+ cCmpVal = ( mnDrawMode & DRAWMODE_GHOSTEDBITMAP ) ? 0x80 : 0;
+ else
+ cCmpVal = 255;
+
+ Color aCol( cCmpVal, cCmpVal, cCmpVal );
+ Push( PUSH_LINECOLOR | PUSH_FILLCOLOR );
+ SetLineColor( aCol );
+ SetFillColor( aCol );
+ DrawRect( Rectangle( rDestPt, rDestSize ) );
+ Pop();
+ return;
+ }
+ else if( !!aBmp )
+ {
+ if ( mnDrawMode & DRAWMODE_GRAYBITMAP )
+ aBmp.Convert( BMP_CONVERSION_8BIT_GREYS );
+
+ if ( mnDrawMode & DRAWMODE_GHOSTEDBITMAP )
+ aBmp.Convert( BMP_CONVERSION_GHOSTED );
+ }
+ }
+
+ if ( mpMetaFile )
+ {
+ switch( nAction )
+ {
+ case( META_BMP_ACTION ):
+ mpMetaFile->AddAction( new MetaBmpAction( rDestPt, aBmp ) );
+ break;
+
+ case( META_BMPSCALE_ACTION ):
+ mpMetaFile->AddAction( new MetaBmpScaleAction( rDestPt, rDestSize, aBmp ) );
+ break;
+
+ case( META_BMPSCALEPART_ACTION ):
+ mpMetaFile->AddAction( new MetaBmpScalePartAction(
+ rDestPt, rDestSize, rSrcPtPixel, rSrcSizePixel, aBmp ) );
+ break;
+ }
+ }
+
+ OUTDEV_INIT();
+
+ if( !aBmp.IsEmpty() )
+ {
+ SalTwoRect aPosAry;
+
+ aPosAry.mnSrcX = rSrcPtPixel.X();
+ aPosAry.mnSrcY = rSrcPtPixel.Y();
+ aPosAry.mnSrcWidth = rSrcSizePixel.Width();
+ aPosAry.mnSrcHeight = rSrcSizePixel.Height();
+ aPosAry.mnDestX = ImplLogicXToDevicePixel( rDestPt.X() );
+ aPosAry.mnDestY = ImplLogicYToDevicePixel( rDestPt.Y() );
+ aPosAry.mnDestWidth = ImplLogicWidthToDevicePixel( rDestSize.Width() );
+ aPosAry.mnDestHeight = ImplLogicHeightToDevicePixel( rDestSize.Height() );
+
+ if ( aPosAry.mnSrcWidth && aPosAry.mnSrcHeight && aPosAry.mnDestWidth && aPosAry.mnDestHeight )
+ {
+ const sal_uLong nMirrFlags = ImplAdjustTwoRect( aPosAry, aBmp.GetSizePixel() );
+
+ if ( nMirrFlags )
+ aBmp.Mirror( nMirrFlags );
+
+ if ( aPosAry.mnSrcWidth && aPosAry.mnSrcHeight && aPosAry.mnDestWidth && aPosAry.mnDestHeight )
+ {
+ if ( nAction == META_BMPSCALE_ACTION )
+ ScaleBitmap (aBmp, aPosAry);
+
+ mpGraphics->DrawBitmap( aPosAry, *aBmp.ImplGetImpBitmap()->ImplGetSalBitmap(), this );
+ }
+ }
+ }
+}
+
+void OutputDevice::ScaleBitmap (Bitmap &rBmp, SalTwoRect &rPosAry)
+{
+ const double nScaleX = rPosAry.mnDestWidth / static_cast<double>( rPosAry.mnSrcWidth );
+ const double nScaleY = rPosAry.mnDestHeight / static_cast<double>( rPosAry.mnSrcHeight );
+
+ // If subsampling, use Bitmap::Scale for subsampling for better quality.
+ if ( nScaleX < 1.0 || nScaleY < 1.0 )
+ {
+ rBmp.Scale ( nScaleX, nScaleY );
+ rPosAry.mnSrcWidth = rPosAry.mnDestWidth;
+ rPosAry.mnSrcHeight = rPosAry.mnDestHeight;
+ }
+}
+
+void OutputDevice::DrawBitmapEx( const Point& rDestPt,
+ const BitmapEx& rBitmapEx )
+{
+ if( ImplIsRecordLayout() )
+ return;
+
+ if( TRANSPARENT_NONE == rBitmapEx.GetTransparentType() )
+ {
+ DrawBitmap( rDestPt, rBitmapEx.GetBitmap() );
+ }
+ else
+ {
+ const Size aSizePix( rBitmapEx.GetSizePixel() );
+ ImplDrawBitmapEx( rDestPt, PixelToLogic( aSizePix ), Point(), aSizePix, rBitmapEx, META_BMPEX_ACTION );
+ }
+}
+
+void OutputDevice::DrawBitmapEx( const Point& rDestPt, const Size& rDestSize,
+ const BitmapEx& rBitmapEx )
+{
+ if( ImplIsRecordLayout() )
+ return;
+
+ if ( TRANSPARENT_NONE == rBitmapEx.GetTransparentType() )
+ {
+ DrawBitmap( rDestPt, rDestSize, rBitmapEx.GetBitmap() );
+ }
+ else
+ {
+ ImplDrawBitmapEx( rDestPt, rDestSize, Point(), rBitmapEx.GetSizePixel(), rBitmapEx, META_BMPEXSCALE_ACTION );
+ }
+}
+
+void OutputDevice::DrawBitmapEx( const Point& rDestPt, const Size& rDestSize,
+ const Point& rSrcPtPixel, const Size& rSrcSizePixel,
+ const BitmapEx& rBitmapEx )
+{
+ if( ImplIsRecordLayout() )
+ return;
+
+ if( TRANSPARENT_NONE == rBitmapEx.GetTransparentType() )
+ {
+ DrawBitmap( rDestPt, rDestSize, rSrcPtPixel, rSrcSizePixel, rBitmapEx.GetBitmap() );
+ }
+ else
+ {
+ ImplDrawBitmapEx( rDestPt, rDestSize, rSrcPtPixel, rSrcSizePixel, rBitmapEx, META_BMPEXSCALEPART_ACTION );
+ }
+}
+
+bool OutputDevice::DrawTransformBitmapExDirect(
+ const basegfx::B2DHomMatrix& aFullTransform,
+ const BitmapEx& rBitmapEx)
+{
+ bool bDone = false;
+
+ // try to paint directly
+ const basegfx::B2DPoint aNull(aFullTransform * basegfx::B2DPoint(0.0, 0.0));
+ const basegfx::B2DPoint aTopX(aFullTransform * basegfx::B2DPoint(1.0, 0.0));
+ const basegfx::B2DPoint aTopY(aFullTransform * basegfx::B2DPoint(0.0, 1.0));
+ SalBitmap* pSalSrcBmp = rBitmapEx.GetBitmap().ImplGetImpBitmap()->ImplGetSalBitmap();
+ SalBitmap* pSalAlphaBmp = 0;
+
+ if(rBitmapEx.IsTransparent())
+ {
+ if(rBitmapEx.IsAlpha())
+ {
+ pSalAlphaBmp = rBitmapEx.GetAlpha().ImplGetImpBitmap()->ImplGetSalBitmap();
+ }
+ else
+ {
+ pSalAlphaBmp = rBitmapEx.GetMask().ImplGetImpBitmap()->ImplGetSalBitmap();
+ }
+ }
+
+ bDone = mpGraphics->DrawTransformedBitmap(
+ aNull,
+ aTopX,
+ aTopY,
+ *pSalSrcBmp,
+ pSalAlphaBmp,
+ this);
+
+ return bDone;
+};
+
+bool OutputDevice::TransformReduceBitmapExTargetRange(
+ const basegfx::B2DHomMatrix& aFullTransform,
+ basegfx::B2DRange &aVisibleRange,
+ double &fMaximumArea)
+{
+ // limit TargetRange to existing pixels (if pixel device)
+ // first get discrete range of object
+ basegfx::B2DRange aFullPixelRange(aVisibleRange);
+
+ aFullPixelRange.transform(aFullTransform);
+
+ if(basegfx::fTools::equalZero(aFullPixelRange.getWidth()) || basegfx::fTools::equalZero(aFullPixelRange.getHeight()))
+ {
+ // object is outside of visible area
+ return false;
+ }
+
+ // now get discrete target pixels; start with OutDev pixel size and evtl.
+ // intersect with active clipping area
+ basegfx::B2DRange aOutPixel(
+ 0.0,
+ 0.0,
+ GetOutputSizePixel().Width(),
+ GetOutputSizePixel().Height());
+
+ if(IsClipRegion())
+ {
+ const Rectangle aRegionRectangle(GetActiveClipRegion().GetBoundRect());
+
+ aOutPixel.intersect( // caution! Range from rectangle, one too much (!)
+ basegfx::B2DRange(
+ aRegionRectangle.Left(),
+ aRegionRectangle.Top(),
+ aRegionRectangle.Right() + 1,
+ aRegionRectangle.Bottom() + 1));
+ }
+
+ if(aOutPixel.isEmpty())
+ {
+ // no active output area
+ return false;
+ }
+
+ // if aFullPixelRange is not completely inside of aOutPixel,
+ // reduction of target pixels is possible
+ basegfx::B2DRange aVisiblePixelRange(aFullPixelRange);
+
+ if(!aOutPixel.isInside(aFullPixelRange))
+ {
+ aVisiblePixelRange.intersect(aOutPixel);
+
+ if(aVisiblePixelRange.isEmpty())
+ {
+ // nothing in visible part, reduces to nothing
+ return false;
+ }
+
+ // aVisiblePixelRange contains the reduced output area in
+ // discrete coordinates. To make it useful everywhere, make it relative to
+ // the object range
+ basegfx::B2DHomMatrix aMakeVisibleRangeRelative;
+
+ aVisibleRange = aVisiblePixelRange;
+ aMakeVisibleRangeRelative.translate(
+ -aFullPixelRange.getMinX(),
+ -aFullPixelRange.getMinY());
+ aMakeVisibleRangeRelative.scale(
+ 1.0 / aFullPixelRange.getWidth(),
+ 1.0 / aFullPixelRange.getHeight());
+ aVisibleRange.transform(aMakeVisibleRangeRelative);
+ }
+
+ // for pixel devices, do *not* limit size, else OutputDevice::ImplDrawAlpha
+ // will create another, badly scaled bitmap to do the job. Nonetheless, do a
+ // maximum clipping of something big (1600x1280x2). Add 1.0 to avoid rounding
+ // errors in rough estimations
+ const double fNewMaxArea(aVisiblePixelRange.getWidth() * aVisiblePixelRange.getHeight());
+
+ fMaximumArea = std::min(4096000.0, fNewMaxArea + 1.0);
+
+ return true;
+}
+
+void OutputDevice::DrawTransformedBitmapEx(
+ const basegfx::B2DHomMatrix& rTransformation,
+ const BitmapEx& rBitmapEx)
+{
+ if( ImplIsRecordLayout() )
+ return;
+
+ if(rBitmapEx.IsEmpty())
+ return;
+
+ if ( mnDrawMode & DRAWMODE_NOBITMAP )
+ return;
+
+ // decompose matrix to check rotation and shear
+ basegfx::B2DVector aScale, aTranslate;
+ double fRotate, fShearX;
+ rTransformation.decompose(aScale, aTranslate, fRotate, fShearX);
+ const bool bRotated(!basegfx::fTools::equalZero(fRotate));
+ const bool bSheared(!basegfx::fTools::equalZero(fShearX));
+ const bool bMirroredX(basegfx::fTools::less(aScale.getX(), 0.0));
+ const bool bMirroredY(basegfx::fTools::less(aScale.getY(), 0.0));
+ static bool bForceToOwnTransformer(false);
+
+ if(!bForceToOwnTransformer && !bRotated && !bSheared && !bMirroredX && !bMirroredY)
+ {
+ // with no rotation, shear or mirroring it can be mapped to DrawBitmapEx
+ // do *not* execute the mirroring here, it's done in the fallback
+ // #i124580# the correct DestSize needs to be calculated based on MaxXY values
+ const Point aDestPt(basegfx::fround(aTranslate.getX()), basegfx::fround(aTranslate.getY()));
+ const Size aDestSize(
+ basegfx::fround(aScale.getX() + aTranslate.getX()) - aDestPt.X(),
+ basegfx::fround(aScale.getY() + aTranslate.getY()) - aDestPt.Y());
+
+ DrawBitmapEx(aDestPt, aDestSize, rBitmapEx);
+ return;
+ }
+
+ // we have rotation,shear or mirror, check if some crazy mode needs the
+ // created transformed bitmap
+ const bool bInvert(ROP_INVERT == meRasterOp);
+ const bool bBitmapChangedColor(mnDrawMode & (DRAWMODE_BLACKBITMAP | DRAWMODE_WHITEBITMAP | DRAWMODE_GRAYBITMAP | DRAWMODE_GHOSTEDBITMAP));
+ const bool bMetafile(mpMetaFile);
+ bool bDone(false);
+ const basegfx::B2DHomMatrix aFullTransform(GetViewTransformation() * rTransformation);
+ const bool bTryDirectPaint(!bInvert && !bBitmapChangedColor && !bMetafile );
+
+ if(!bForceToOwnTransformer && bTryDirectPaint)
+ {
+ bDone = DrawTransformBitmapExDirect(aFullTransform, rBitmapEx);
+ }
+
+ if(!bDone)
+ {
+ // take the fallback when no rotate and shear, but mirror (else we would have done this above)
+ if(!bForceToOwnTransformer && !bRotated && !bSheared)
+ {
+ // with no rotation or shear it can be mapped to DrawBitmapEx
+ // do *not* execute the mirroring here, it's done in the fallback
+ // #i124580# the correct DestSize needs to be calculated based on MaxXY values
+ const Point aDestPt(basegfx::fround(aTranslate.getX()), basegfx::fround(aTranslate.getY()));
+ const Size aDestSize(
+ basegfx::fround(aScale.getX() + aTranslate.getX()) - aDestPt.X(),
+ basegfx::fround(aScale.getY() + aTranslate.getY()) - aDestPt.Y());
+
+ DrawBitmapEx(aDestPt, aDestSize, rBitmapEx);
+ return;
+ }
+
+ // fallback; create transformed bitmap the hard way (back-transform
+ // the pixels) and paint
+ basegfx::B2DRange aVisibleRange(0.0, 0.0, 1.0, 1.0);
+
+ // limit maximum area to something looking good for non-pixel-based targets (metafile, printer)
+ // by using a fixed minimum (allow at least, but no need to utilize) for good smooting and an area
+ // dependent of original size for good quality when e.g. rotated/sheared. Still, limit to a maximum
+ // to avoid crashes/ressource problems (ca. 1500x3000 here)
+ const Size& rOriginalSizePixel(rBitmapEx.GetSizePixel());
+ const double fOrigArea(rOriginalSizePixel.Width() * rOriginalSizePixel.Height() * 0.5);
+ const double fOrigAreaScaled(bSheared || bRotated ? fOrigArea * 1.44 : fOrigArea);
+ double fMaximumArea(std::min(4500000.0, std::max(1000000.0, fOrigAreaScaled)));
+
+ if(!bMetafile)
+ {
+ if ( !TransformReduceBitmapExTargetRange( aFullTransform, aVisibleRange, fMaximumArea ) )
+ return;
+ }
+
+ if(!aVisibleRange.isEmpty())
+ {
+ static bool bDoSmoothAtAll(true);
+ BitmapEx aTransformed(rBitmapEx);
+
+ // #122923# when the result needs an alpha channel due to being rotated or sheared
+ // and thus uncovering areas, add these channels so that the own transformer (used
+ // in getTransformed) also creates a transformed alpha channel
+ if(!aTransformed.IsTransparent() && (bSheared || bRotated))
+ {
+ // parts will be uncovered, extend aTransformed with a mask bitmap
+ const Bitmap aContent(aTransformed.GetBitmap());
+#if defined(MACOSX) || defined(IOS)
+ AlphaMask aMaskBmp(aContent.GetSizePixel());
+ aMaskBmp.Erase(0);
+#else
+ Bitmap aMaskBmp(aContent.GetSizePixel(), 1);
+ aMaskBmp.Erase(Color(COL_BLACK)); // #122758# Initialize to non-transparent
+#endif
+ aTransformed = BitmapEx(aContent, aMaskBmp);
+ }
+
+ aTransformed = aTransformed.getTransformed(
+ aFullTransform,
+ aVisibleRange,
+ fMaximumArea,
+ bDoSmoothAtAll);
+ basegfx::B2DRange aTargetRange(0.0, 0.0, 1.0, 1.0);
+
+ // get logic object target range
+ aTargetRange.transform(rTransformation);
+
+ // get from unified/relative VisibleRange to logoc one
+ aVisibleRange.transform(
+ basegfx::tools::createScaleTranslateB2DHomMatrix(
+ aTargetRange.getRange(),
+ aTargetRange.getMinimum()));
+
+ // extract point and size; do not remove size, the bitmap may have been prepared reduced by purpose
+ // #i124580# the correct DestSize needs to be calculated based on MaxXY values
+ const Point aDestPt(basegfx::fround(aVisibleRange.getMinX()), basegfx::fround(aVisibleRange.getMinY()));
+ const Size aDestSize(
+ basegfx::fround(aVisibleRange.getMaxX()) - aDestPt.X(),
+ basegfx::fround(aVisibleRange.getMaxY()) - aDestPt.Y());
+
+ DrawBitmapEx(aDestPt, aDestSize, aTransformed);
+ }
+ }
+}
+
+void OutputDevice::ImplDrawBitmapEx( const Point& rDestPt, const Size& rDestSize,
+ const Point& rSrcPtPixel, const Size& rSrcSizePixel,
+ const BitmapEx& rBitmapEx, const sal_uLong nAction )
+{
+ OSL_ENSURE(TRANSPARENT_NONE != rBitmapEx.GetTransparentType(), "ImplDrawBitmapEx not needed, no transparency in BitmapEx (!)");
+
+ if ( mnDrawMode & DRAWMODE_NOBITMAP )
+ return;
+
+ if ( ROP_INVERT == meRasterOp )
+ {
+ DrawRect( Rectangle( rDestPt, rDestSize ) );
+ return;
+ }
+
+ BitmapEx aBmpEx( rBitmapEx );
+
+ if ( mnDrawMode & ( DRAWMODE_BLACKBITMAP | DRAWMODE_WHITEBITMAP |
+ DRAWMODE_GRAYBITMAP | DRAWMODE_GHOSTEDBITMAP ) )
+ {
+ if ( mnDrawMode & ( DRAWMODE_BLACKBITMAP | DRAWMODE_WHITEBITMAP ) )
+ {
+ Bitmap aColorBmp( aBmpEx.GetSizePixel(), ( mnDrawMode & DRAWMODE_GHOSTEDBITMAP ) ? 4 : 1 );
+ sal_uInt8 cCmpVal;
+
+ if ( mnDrawMode & DRAWMODE_BLACKBITMAP )
+ cCmpVal = ( mnDrawMode & DRAWMODE_GHOSTEDBITMAP ) ? 0x80 : 0;
+ else
+ cCmpVal = 255;
+
+ aColorBmp.Erase( Color( cCmpVal, cCmpVal, cCmpVal ) );
+
+ if( aBmpEx.IsAlpha() )
+ {
+ // Create one-bit mask out of alpha channel, by
+ // thresholding it at alpha=0.5. As
+ // DRAWMODE_BLACK/WHITEBITMAP requires monochrome
+ // output, having alpha-induced grey levels is not
+ // acceptable.
+ Bitmap aMask( aBmpEx.GetAlpha().GetBitmap() );
+ aMask.MakeMono( 129 );
+ aBmpEx = BitmapEx( aColorBmp, aMask );
+ }
+ else
+ {
+ aBmpEx = BitmapEx( aColorBmp, aBmpEx.GetMask() );
+ }
+ }
+ else if( !!aBmpEx )
+ {
+ if ( mnDrawMode & DRAWMODE_GRAYBITMAP )
+ aBmpEx.Convert( BMP_CONVERSION_8BIT_GREYS );
+
+ if ( mnDrawMode & DRAWMODE_GHOSTEDBITMAP )
+ aBmpEx.Convert( BMP_CONVERSION_GHOSTED );
+ }
+ }
+
+ if ( mpMetaFile )
+ {
+ switch( nAction )
+ {
+ case( META_BMPEX_ACTION ):
+ mpMetaFile->AddAction( new MetaBmpExAction( rDestPt, aBmpEx ) );
+ break;
+
+ case( META_BMPEXSCALE_ACTION ):
+ mpMetaFile->AddAction( new MetaBmpExScaleAction( rDestPt, rDestSize, aBmpEx ) );
+ break;
+
+ case( META_BMPEXSCALEPART_ACTION ):
+ mpMetaFile->AddAction( new MetaBmpExScalePartAction( rDestPt, rDestSize,
+ rSrcPtPixel, rSrcSizePixel, aBmpEx ) );
+ break;
+ }
+ }
+
+ OUTDEV_INIT();
+
+ DrawDeviceBitmap( rDestPt, rDestSize, rSrcPtPixel, rSrcSizePixel, aBmpEx );
+}
+
+void OutputDevice::DrawDeviceBitmap( const Point& rDestPt, const Size& rDestSize,
+ const Point& rSrcPtPixel, const Size& rSrcSizePixel,
+ BitmapEx& rBmpEx )
+{
+ if(rBmpEx.IsAlpha())
+ {
+ Size aDestSizePixel(LogicToPixel(rDestSize));
+
+ BitmapEx aScaledBitmapEx(rBmpEx);
+ Point aSrcPtPixel(rSrcPtPixel);
+ Size aSrcSizePixel(rSrcSizePixel);
+
+ // we have beautiful scaling algorithms, let's use them
+ if (aDestSizePixel != rSrcSizePixel && rSrcSizePixel.Width() != 0 && rSrcSizePixel.Height() != 0)
+ {
+ double fScaleX = double(aDestSizePixel.Width()) / rSrcSizePixel.Width();
+ double fScaleY = double(aDestSizePixel.Height()) / rSrcSizePixel.Height();
+
+ aScaledBitmapEx.Scale(fScaleX, fScaleY);
+
+ aSrcSizePixel = aDestSizePixel;
+ aSrcPtPixel.X() = rSrcPtPixel.X() * fScaleX;
+ aSrcPtPixel.Y() = rSrcPtPixel.Y() * fScaleY;
+ }
+ ImplDrawAlpha(aScaledBitmapEx.GetBitmap(), aScaledBitmapEx.GetAlpha(), rDestPt, rDestSize, aSrcPtPixel, aSrcSizePixel);
+ return;
+ }
+
+ if( !( !rBmpEx ) )
+ {
+ SalTwoRect aPosAry;
+
+ aPosAry.mnSrcX = rSrcPtPixel.X();
+ aPosAry.mnSrcY = rSrcPtPixel.Y();
+ aPosAry.mnSrcWidth = rSrcSizePixel.Width();
+ aPosAry.mnSrcHeight = rSrcSizePixel.Height();
+ aPosAry.mnDestX = ImplLogicXToDevicePixel( rDestPt.X() );
+ aPosAry.mnDestY = ImplLogicYToDevicePixel( rDestPt.Y() );
+ aPosAry.mnDestWidth = ImplLogicWidthToDevicePixel( rDestSize.Width() );
+ aPosAry.mnDestHeight = ImplLogicHeightToDevicePixel( rDestSize.Height() );
+
+ const sal_uLong nMirrFlags = ImplAdjustTwoRect( aPosAry, rBmpEx.GetSizePixel() );
+
+ if( aPosAry.mnSrcWidth && aPosAry.mnSrcHeight && aPosAry.mnDestWidth && aPosAry.mnDestHeight )
+ {
+
+ if( nMirrFlags )
+ rBmpEx.Mirror( nMirrFlags );
+
+ const SalBitmap* pSalSrcBmp = rBmpEx.ImplGetBitmapImpBitmap()->ImplGetSalBitmap();
+ const ImpBitmap* pMaskBmp = rBmpEx.ImplGetMaskImpBitmap();
+
+ if ( pMaskBmp )
+ {
+ SalBitmap* pSalAlphaBmp = pMaskBmp->ImplGetSalBitmap();
+ bool bTryDirectPaint(pSalSrcBmp && pSalAlphaBmp);
+
+ if(bTryDirectPaint)
+ {
+ // only paint direct when no scaling and no MapMode, else the
+ // more expensive conversions may be done for short-time Bitmap/BitmapEx
+ // used for buffering only
+ if(!IsMapMode() && aPosAry.mnSrcWidth == aPosAry.mnDestWidth && aPosAry.mnSrcHeight == aPosAry.mnDestHeight)
+ {
+ bTryDirectPaint = false;
+ }
+ }
+
+ if(bTryDirectPaint && mpGraphics->DrawAlphaBitmap(aPosAry, *pSalSrcBmp, *pSalAlphaBmp, this))
+ {
+ // tried to paint as alpha directly. If tis worked, we are done (except
+ // alpha, see below)
+ }
+ else
+ {
+ // #4919452# reduce operation area to bounds of
+ // cliprect. since masked transparency involves
+ // creation of a large vdev and copying the screen
+ // content into that (slooow read from framebuffer),
+ // that should considerably increase performance for
+ // large bitmaps and small clippings.
+
+ // Note that this optimization is a workaround for a
+ // Writer peculiarity, namely, to decompose background
+ // graphics into myriads of disjunct, tiny
+ // rectangles. That otherwise kills us here, since for
+ // transparent output, SAL always prepares the whole
+ // bitmap, if aPosAry contains the whole bitmap (and
+ // it's _not_ to blame for that).
+
+ // Note the call to ImplPixelToDevicePixel(), since
+ // aPosAry already contains the mnOutOff-offsets, they
+ // also have to be applied to the region
+ Rectangle aClipRegionBounds( ImplPixelToDevicePixel(maRegion).GetBoundRect() );
+
+ // TODO: Also respect scaling (that's a bit tricky,
+ // since the source points have to move fractional
+ // amounts (which is not possible, thus has to be
+ // emulated by increases copy area)
+ // const double nScaleX( aPosAry.mnDestWidth / aPosAry.mnSrcWidth );
+ // const double nScaleY( aPosAry.mnDestHeight / aPosAry.mnSrcHeight );
+
+ // for now, only identity scales allowed
+ if( !aClipRegionBounds.IsEmpty() &&
+ aPosAry.mnDestWidth == aPosAry.mnSrcWidth &&
+ aPosAry.mnDestHeight == aPosAry.mnSrcHeight )
+ {
+ // now intersect dest rect with clip region
+ aClipRegionBounds.Intersection( Rectangle( aPosAry.mnDestX,
+ aPosAry.mnDestY,
+ aPosAry.mnDestX + aPosAry.mnDestWidth - 1,
+ aPosAry.mnDestY + aPosAry.mnDestHeight - 1 ) );
+
+ // Note: I could theoretically optimize away the
+ // DrawBitmap below, if the region is empty
+ // here. Unfortunately, cannot rule out that
+ // somebody relies on the side effects.
+ if( !aClipRegionBounds.IsEmpty() )
+ {
+ aPosAry.mnSrcX += aClipRegionBounds.Left() - aPosAry.mnDestX;
+ aPosAry.mnSrcY += aClipRegionBounds.Top() - aPosAry.mnDestY;
+ aPosAry.mnSrcWidth = aClipRegionBounds.GetWidth();
+ aPosAry.mnSrcHeight = aClipRegionBounds.GetHeight();
+
+ aPosAry.mnDestX = aClipRegionBounds.Left();
+ aPosAry.mnDestY = aClipRegionBounds.Top();
+ aPosAry.mnDestWidth = aClipRegionBounds.GetWidth();
+ aPosAry.mnDestHeight = aClipRegionBounds.GetHeight();
+ }
+ }
+
+ mpGraphics->DrawBitmap( aPosAry, *pSalSrcBmp,
+ *pMaskBmp->ImplGetSalBitmap(),
+ this );
+ }
+
+ // #110958# Paint mask to alpha channel. Luckily, the
+ // black and white representation of the mask maps to
+ // the alpha channel
+
+ // #i25167# Restrict mask painting to _opaque_ areas
+ // of the mask, otherwise we spoil areas where no
+ // bitmap content was ever visible. Interestingly
+ // enough, this can be achieved by taking the mask as
+ // the transparency mask of itself
+ if( mpAlphaVDev )
+ mpAlphaVDev->DrawBitmapEx( rDestPt,
+ rDestSize,
+ BitmapEx( rBmpEx.GetMask(),
+ rBmpEx.GetMask() ) );
+ }
+ else
+ {
+ mpGraphics->DrawBitmap( aPosAry, *pSalSrcBmp, this );
+
+ if( mpAlphaVDev )
+ {
+ // #i32109#: Make bitmap area opaque
+ mpAlphaVDev->ImplFillOpaqueRectangle( Rectangle(rDestPt, rDestSize) );
+ }
+ }
+ }
+ }
+}
+
+void OutputDevice::DrawMask( const Point& rDestPt,
+ const Bitmap& rBitmap, const Color& rMaskColor )
+{
+ const Size aSizePix( rBitmap.GetSizePixel() );
+ DrawMask( rDestPt, PixelToLogic( aSizePix ), Point(), aSizePix, rBitmap, rMaskColor, META_MASK_ACTION );
+}
+
+void OutputDevice::DrawMask( const Point& rDestPt, const Size& rDestSize,
+ const Bitmap& rBitmap, const Color& rMaskColor )
+{
+ DrawMask( rDestPt, rDestSize, Point(), rBitmap.GetSizePixel(), rBitmap, rMaskColor, META_MASKSCALE_ACTION );
+}
+
+void OutputDevice::DrawMask( const Point& rDestPt, const Size& rDestSize,
+ const Point& rSrcPtPixel, const Size& rSrcSizePixel,
+ const Bitmap& rBitmap, const Color& rMaskColor,
+ const sal_uLong nAction )
+{
+ if( ImplIsRecordLayout() )
+ return;
+
+ if( ROP_INVERT == meRasterOp )
+ {
+ DrawRect( Rectangle( rDestPt, rDestSize ) );
+ return;
+ }
+
+ if ( mpMetaFile )
+ {
+ switch( nAction )
+ {
+ case( META_MASK_ACTION ):
+ mpMetaFile->AddAction( new MetaMaskAction( rDestPt,
+ rBitmap, rMaskColor ) );
+ break;
+
+ case( META_MASKSCALE_ACTION ):
+ mpMetaFile->AddAction( new MetaMaskScaleAction( rDestPt,
+ rDestSize, rBitmap, rMaskColor ) );
+ break;
+
+ case( META_MASKSCALEPART_ACTION ):
+ mpMetaFile->AddAction( new MetaMaskScalePartAction( rDestPt, rDestSize,
+ rSrcPtPixel, rSrcSizePixel, rBitmap, rMaskColor ) );
+ break;
+ }
+ }
+
+ OUTDEV_INIT();
+
+ if ( OUTDEV_PRINTER == meOutDevType )
+ {
+ ImplPrintMask( rBitmap, rMaskColor, rDestPt, rDestSize, rSrcPtPixel, rSrcSizePixel );
+ return;
+ }
+
+ const ImpBitmap* pImpBmp = rBitmap.ImplGetImpBitmap();
+ if ( pImpBmp )
+ {
+ SalTwoRect aPosAry;
+
+ aPosAry.mnSrcX = rSrcPtPixel.X();
+ aPosAry.mnSrcY = rSrcPtPixel.Y();
+ aPosAry.mnSrcWidth = rSrcSizePixel.Width();
+ aPosAry.mnSrcHeight = rSrcSizePixel.Height();
+ aPosAry.mnDestX = ImplLogicXToDevicePixel( rDestPt.X() );
+ aPosAry.mnDestY = ImplLogicYToDevicePixel( rDestPt.Y() );
+ aPosAry.mnDestWidth = ImplLogicWidthToDevicePixel( rDestSize.Width() );
+ aPosAry.mnDestHeight = ImplLogicHeightToDevicePixel( rDestSize.Height() );
+
+ // we don't want to mirror via cooridates
+ const sal_uLong nMirrFlags = ImplAdjustTwoRect( aPosAry, pImpBmp->ImplGetSize() );
+
+ // check if output is necessary
+ if( aPosAry.mnSrcWidth && aPosAry.mnSrcHeight && aPosAry.mnDestWidth && aPosAry.mnDestHeight )
+ {
+
+ if( nMirrFlags )
+ {
+ Bitmap aTmp( rBitmap );
+ aTmp.Mirror( nMirrFlags );
+ mpGraphics->DrawMask( aPosAry, *aTmp.ImplGetImpBitmap()->ImplGetSalBitmap(),
+ ImplColorToSal( rMaskColor ) , this);
+ }
+ else
+ mpGraphics->DrawMask( aPosAry, *pImpBmp->ImplGetSalBitmap(),
+ ImplColorToSal( rMaskColor ), this );
+
+ }
+ }
+
+ // TODO: Use mask here
+ if( mpAlphaVDev )
+ {
+ const Bitmap& rMask( rBitmap.CreateMask( rMaskColor ) );
+
+ // #i25167# Restrict mask painting to _opaque_ areas
+ // of the mask, otherwise we spoil areas where no
+ // bitmap content was ever visible. Interestingly
+ // enough, this can be achieved by taking the mask as
+ // the transparency mask of itself
+ mpAlphaVDev->DrawBitmapEx( rDestPt,
+ rDestSize,
+ rSrcPtPixel,
+ rSrcSizePixel,
+ BitmapEx( rMask, rMask ) );
+ }
+}
+
+namespace
+{
+ BitmapEx makeDisabledBitmap(const Bitmap &rBitmap)
+ {
+ const Size aTotalSize( rBitmap.GetSizePixel() );
+ Bitmap aGrey( aTotalSize, 8, &Bitmap::GetGreyPalette( 256 ) );
+ AlphaMask aGreyAlphaMask( aTotalSize );
+ BitmapReadAccess* pBmp = const_cast<Bitmap&>(rBitmap).AcquireReadAccess();
+ BitmapWriteAccess* pGrey = aGrey.AcquireWriteAccess();
+ BitmapWriteAccess* pGreyAlphaMask = aGreyAlphaMask.AcquireWriteAccess();
+
+ if( pBmp && pGrey && pGreyAlphaMask )
+ {
+ BitmapColor aGreyVal( 0 );
+ BitmapColor aGreyAlphaMaskVal( 0 );
+ const int nLeft = 0, nRight = aTotalSize.Width();
+ const int nTop = 0, nBottom = nTop + aTotalSize.Height();
+
+ for( int nY = nTop; nY < nBottom; ++nY )
+ {
+ for( int nX = nLeft; nX < nRight; ++nX )
+ {
+ aGreyVal.SetIndex( pBmp->GetLuminance( nY, nX ) );
+ pGrey->SetPixel( nY, nX, aGreyVal );
+
+ aGreyAlphaMaskVal.SetIndex( static_cast< sal_uInt8 >( 128ul ) );
+ pGreyAlphaMask->SetPixel( nY, nX, aGreyAlphaMaskVal );
+ }
+ }
+ }
+
+ const_cast<Bitmap&>(rBitmap).ReleaseAccess( pBmp );
+ aGrey.ReleaseAccess( pGrey );
+ aGreyAlphaMask.ReleaseAccess( pGreyAlphaMask );
+ return BitmapEx( aGrey, aGreyAlphaMask );
+ }
+}
+
+void OutputDevice::DrawImage( const Point& rPos, const Image& rImage, sal_uInt16 nStyle )
+{
+ DBG_ASSERT( GetOutDevType() != OUTDEV_PRINTER, "DrawImage(): Images can't be drawn on any mprinter" );
+
+ if( !rImage.mpImplData || ImplIsRecordLayout() )
+ return;
+
+ switch( rImage.mpImplData->meType )
+ {
+ case IMAGETYPE_BITMAP:
+ {
+ const Bitmap &rBitmap = *static_cast< Bitmap* >( rImage.mpImplData->mpData );
+ if( nStyle & IMAGE_DRAW_DISABLE )
+ DrawBitmapEx( rPos, makeDisabledBitmap(rBitmap) );
+ else
+ DrawBitmap( rPos, rBitmap );
+ }
+ break;
+
+ case IMAGETYPE_IMAGE:
+ {
+ ImplImageData* pData = static_cast< ImplImageData* >( rImage.mpImplData->mpData );
+
+ if( !pData->mpImageBitmap )
+ {
+ const Size aSize( pData->maBmpEx.GetSizePixel() );
+
+ pData->mpImageBitmap = new ImplImageBmp;
+ pData->mpImageBitmap->Create( pData->maBmpEx, aSize.Width(), aSize.Height(), 1 );
+ }
+
+ pData->mpImageBitmap->Draw( 0, this, rPos, nStyle );
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+void OutputDevice::DrawImage( const Point& rPos, const Size& rSize,
+ const Image& rImage, sal_uInt16 nStyle )
+{
+ DBG_ASSERT( GetOutDevType() != OUTDEV_PRINTER, "DrawImage(): Images can't be drawn on any mprinter" );
+
+ if( rImage.mpImplData && !ImplIsRecordLayout() )
+ {
+ switch( rImage.mpImplData->meType )
+ {
+ case IMAGETYPE_BITMAP:
+ {
+ const Bitmap &rBitmap = *static_cast< Bitmap* >( rImage.mpImplData->mpData );
+ if( nStyle & IMAGE_DRAW_DISABLE )
+ DrawBitmapEx( rPos, rSize, makeDisabledBitmap(rBitmap) );
+ else
+ DrawBitmap( rPos, rSize, rBitmap );
+ }
+ break;
+
+ case IMAGETYPE_IMAGE:
+ {
+ ImplImageData* pData = static_cast< ImplImageData* >( rImage.mpImplData->mpData );
+
+ if ( !pData->mpImageBitmap )
+ {
+ const Size aSize( pData->maBmpEx.GetSizePixel() );
+
+ pData->mpImageBitmap = new ImplImageBmp;
+ pData->mpImageBitmap->Create( pData->maBmpEx, aSize.Width(), aSize.Height(), 1 );
+ }
+
+ pData->mpImageBitmap->Draw( 0, this, rPos, nStyle, &rSize );
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+Bitmap OutputDevice::GetBitmap( const Point& rSrcPt, const Size& rSize ) const
+{
+ OSL_ENSURE(OUTDEV_PRINTER != GetOutDevType(), "OutputDevice::GetBitmap with sorce type OUTDEV_PRINTER should not be used (!)");
+
+ Bitmap aBmp;
+ long nX = ImplLogicXToDevicePixel( rSrcPt.X() );
+ long nY = ImplLogicYToDevicePixel( rSrcPt.Y() );
+ long nWidth = ImplLogicWidthToDevicePixel( rSize.Width() );
+ long nHeight = ImplLogicHeightToDevicePixel( rSize.Height() );
+
+ if ( mpGraphics || ( (OutputDevice*) this )->ImplGetGraphics() )
+ {
+ if ( nWidth > 0 && nHeight > 0 && nX <= (mnOutWidth + mnOutOffX) && nY <= (mnOutHeight + mnOutOffY))
+ {
+ Rectangle aRect( Point( nX, nY ), Size( nWidth, nHeight ) );
+ bool bClipped = false;
+
+ // X-Coordinate outside of draw area?
+ if ( nX < mnOutOffX )
+ {
+ nWidth -= ( mnOutOffX - nX );
+ nX = mnOutOffX;
+ bClipped = true;
+ }
+
+ // Y-Coordinate outside of draw area?
+ if ( nY < mnOutOffY )
+ {
+ nHeight -= ( mnOutOffY - nY );
+ nY = mnOutOffY;
+ bClipped = true;
+ }
+
+ // Width outside of draw area?
+ if ( (nWidth + nX) > (mnOutWidth + mnOutOffX) )
+ {
+ nWidth = mnOutOffX + mnOutWidth - nX;
+ bClipped = true;
+ }
+
+ // Height outside of draw area?
+ if ( (nHeight + nY) > (mnOutHeight + mnOutOffY) )
+ {
+ nHeight = mnOutOffY + mnOutHeight - nY;
+ bClipped = true;
+ }
+
+ if ( bClipped )
+ {
+ // If the visible part has been clipped, we have to create a
+ // Bitmap with the correct size in which we copy the clipped
+ // Bitmap to the correct position.
+ VirtualDevice aVDev( *this );
+
+ if ( aVDev.SetOutputSizePixel( aRect.GetSize() ) )
+ {
+ if ( ((OutputDevice*)&aVDev)->mpGraphics || ((OutputDevice*)&aVDev)->ImplGetGraphics() )
+ {
+ SalTwoRect aPosAry;
+
+ aPosAry.mnSrcX = nX;
+ aPosAry.mnSrcY = nY;
+ aPosAry.mnSrcWidth = nWidth;
+ aPosAry.mnSrcHeight = nHeight;
+ aPosAry.mnDestX = ( aRect.Left() < mnOutOffX ) ? ( mnOutOffX - aRect.Left() ) : 0L;
+ aPosAry.mnDestY = ( aRect.Top() < mnOutOffY ) ? ( mnOutOffY - aRect.Top() ) : 0L;
+ aPosAry.mnDestWidth = nWidth;
+ aPosAry.mnDestHeight = nHeight;
+
+ if ( (nWidth > 0) && (nHeight > 0) )
+ {
+ (((OutputDevice*)&aVDev)->mpGraphics)->CopyBits( aPosAry, mpGraphics, this, this );
+ }
+ else
+ {
+ OSL_ENSURE(false, "CopyBits with negative width or height (!)");
+ }
+
+ aBmp = aVDev.GetBitmap( Point(), aVDev.GetOutputSizePixel() );
+ }
+ else
+ bClipped = false;
+ }
+ else
+ bClipped = false;
+ }
+
+ if ( !bClipped )
+ {
+ SalBitmap* pSalBmp = mpGraphics->GetBitmap( nX, nY, nWidth, nHeight, this );
+
+ if( pSalBmp )
+ {
+ ImpBitmap* pImpBmp = new ImpBitmap;
+ pImpBmp->ImplSetSalBitmap( pSalBmp );
+ aBmp.ImplSetImpBitmap( pImpBmp );
+ }
+ }
+ }
+ }
+
+ return aBmp;
+}
+
+BitmapEx OutputDevice::GetBitmapEx( const Point& rSrcPt, const Size& rSize ) const
+{
+
+ // #110958# Extract alpha value from VDev, if any
+ if( mpAlphaVDev )
+ {
+ Bitmap aAlphaBitmap( mpAlphaVDev->GetBitmap( rSrcPt, rSize ) );
+
+ // ensure 8 bit alpha
+ if( aAlphaBitmap.GetBitCount() > 8 )
+ aAlphaBitmap.Convert( BMP_CONVERSION_8BIT_GREYS );
+
+ return BitmapEx(GetBitmap( rSrcPt, rSize ), AlphaMask( aAlphaBitmap ) );
+ }
+ else
+ return GetBitmap( rSrcPt, rSize );
+}
+
+Color OutputDevice::GetPixel( const Point& rPt ) const
+{
+
+ Color aColor;
+
+ if ( mpGraphics || ((OutputDevice*)this)->ImplGetGraphics() )
+ {
+ if ( mbInitClipRegion )
+ ((OutputDevice*)this)->ImplInitClipRegion();
+
+ if ( !mbOutputClipped )
+ {
+ const long nX = ImplLogicXToDevicePixel( rPt.X() );
+ const long nY = ImplLogicYToDevicePixel( rPt.Y() );
+ const SalColor aSalCol = mpGraphics->GetPixel( nX, nY, this );
+ aColor.SetRed( SALCOLOR_RED( aSalCol ) );
+ aColor.SetGreen( SALCOLOR_GREEN( aSalCol ) );
+ aColor.SetBlue( SALCOLOR_BLUE( aSalCol ) );
+ }
+ }
+ return aColor;
+}
+
+void OutputDevice::DrawPixel( const Point& rPt )
+{
+
+ if ( mpMetaFile )
+ mpMetaFile->AddAction( new MetaPointAction( rPt ) );
+
+ if ( !IsDeviceOutputNecessary() || !mbLineColor || ImplIsRecordLayout() )
+ return;
+
+ Point aPt = ImplLogicToDevicePixel( rPt );
+
+ if ( !mpGraphics )
+ {
+ if ( !ImplGetGraphics() )
+ return;
+ }
+
+ if ( mbInitClipRegion )
+ ImplInitClipRegion();
+ if ( mbOutputClipped )
+ return;
+
+ if ( mbInitLineColor )
+ ImplInitLineColor();
+
+ mpGraphics->DrawPixel( aPt.X(), aPt.Y(), this );
+
+ if( mpAlphaVDev )
+ mpAlphaVDev->DrawPixel( rPt );
+}
+
+void OutputDevice::DrawPixel( const Point& rPt, const Color& rColor )
+{
+
+ Color aColor = ImplDrawModeToColor( rColor );
+
+ if ( mpMetaFile )
+ mpMetaFile->AddAction( new MetaPixelAction( rPt, aColor ) );
+
+ if ( !IsDeviceOutputNecessary() || ImplIsColorTransparent( aColor ) || ImplIsRecordLayout() )
+ return;
+
+ Point aPt = ImplLogicToDevicePixel( rPt );
+
+ if ( !mpGraphics )
+ {
+ if ( !ImplGetGraphics() )
+ return;
+ }
+
+ if ( mbInitClipRegion )
+ ImplInitClipRegion();
+ if ( mbOutputClipped )
+ return;
+
+ mpGraphics->DrawPixel( aPt.X(), aPt.Y(), ImplColorToSal( aColor ), this );
+
+ if( mpAlphaVDev )
+ mpAlphaVDev->DrawPixel( rPt );
+}
+
+void OutputDevice::DrawPixel( const Polygon& rPts, const Color* pColors )
+{
+ if ( !pColors )
+ DrawPixel( rPts, GetLineColor() );
+ else
+ {
+ DBG_ASSERT( pColors, "OutputDevice::DrawPixel: No color array specified" );
+
+ const sal_uInt16 nSize = rPts.GetSize();
+
+ if ( nSize )
+ {
+ if ( mpMetaFile )
+ for ( sal_uInt16 i = 0; i < nSize; i++ )
+ mpMetaFile->AddAction( new MetaPixelAction( rPts[ i ], pColors[ i ] ) );
+
+ if ( !IsDeviceOutputNecessary() || ImplIsRecordLayout() )
+ return;
+
+ if ( mpGraphics || ImplGetGraphics() )
+ {
+ if ( mbInitClipRegion )
+ ImplInitClipRegion();
+
+ if ( mbOutputClipped )
+ return;
+
+ for ( sal_uInt16 i = 0; i < nSize; i++ )
+ {
+ const Point aPt( ImplLogicToDevicePixel( rPts[ i ] ) );
+ mpGraphics->DrawPixel( aPt.X(), aPt.Y(), ImplColorToSal( pColors[ i ] ), this );
+ }
+ }
+ }
+ }
+
+ if( mpAlphaVDev )
+ mpAlphaVDev->DrawPixel( rPts, pColors );
+}
+
+void OutputDevice::DrawPixel( const Polygon& rPts, const Color& rColor )
+{
+ if( rColor != COL_TRANSPARENT && ! ImplIsRecordLayout() )
+ {
+ const sal_uInt16 nSize = rPts.GetSize();
+ boost::scoped_array<Color> pColArray(new Color[ nSize ]);
+
+ for( sal_uInt16 i = 0; i < nSize; i++ )
+ pColArray[ i ] = rColor;
+
+ DrawPixel( rPts, pColArray.get() );
+ }
+
+ if( mpAlphaVDev )
+ mpAlphaVDev->DrawPixel( rPts, rColor );
+}
+
+namespace
+{
+ // Co = Cs + Cd*(1-As) premultiplied alpha -or-
+ // Co = (AsCs + AdCd*(1-As)) / Ao
+ inline sal_uInt8 lcl_calcColor( const sal_uInt8 nSourceColor, const sal_uInt8 nSourceAlpha,
+ const sal_uInt8 nDstAlpha, const sal_uInt8 nResAlpha, const sal_uInt8 nDestColor )
+ {
+ int c = nResAlpha ? ( (int)nSourceAlpha*nSourceColor + (int)nDstAlpha*nDestColor -
+ (int)nDstAlpha*nDestColor*nSourceAlpha/255 ) / (int)nResAlpha : 0;
+ return sal_uInt8( c );
+ }
+
+inline BitmapColor lcl_AlphaBlend( int nX, int nY,
+ const long nMapX,
+ const long nMapY,
+ BitmapReadAccess* pP,
+ BitmapReadAccess* pA,
+ BitmapReadAccess* pB,
+ BitmapWriteAccess* pAlphaW,
+ sal_uInt8& nResAlpha )
+{
+ BitmapColor aDstCol,aSrcCol;
+ aSrcCol = pP->GetColor( nMapY, nMapX );
+ aDstCol = pB->GetColor( nY, nX );
+
+ // vcl stores transparency, not alpha - invert it
+ const sal_uInt8 nSrcAlpha = 255 - pA->GetPixelIndex( nMapY, nMapX );
+ const sal_uInt8 nDstAlpha = 255 - pAlphaW->GetPixelIndex( nY, nX );
+
+ // Perform porter-duff compositing 'over' operation
+
+ // Co = Cs + Cd*(1-As)
+ // Ad = As + Ad*(1-As)
+ nResAlpha = (int)nSrcAlpha + (int)nDstAlpha - (int)nDstAlpha*nSrcAlpha/255;
+
+ aDstCol.SetRed( lcl_calcColor( aSrcCol.GetRed(), nSrcAlpha, nDstAlpha, nResAlpha, aDstCol.GetRed() ) );
+ aDstCol.SetBlue( lcl_calcColor( aSrcCol.GetBlue(), nSrcAlpha, nDstAlpha, nResAlpha, aDstCol.GetBlue() ) );
+ aDstCol.SetGreen( lcl_calcColor( aSrcCol.GetGreen(), nSrcAlpha, nDstAlpha, nResAlpha, aDstCol.GetGreen() ) );
+
+ return aDstCol;
+}
+}
+
+Bitmap OutputDevice::ImplBlendWithAlpha( Bitmap aBmp,
+ BitmapReadAccess* pP,
+ BitmapReadAccess* pA,
+ const Rectangle& aDstRect,
+ const sal_Int32 nOffY,
+ const sal_Int32 nDstHeight,
+ const sal_Int32 nOffX,
+ const sal_Int32 nDstWidth,
+ const long* pMapX,
+ const long* pMapY )
+{
+ BitmapColor aDstCol;
+ Bitmap res;
+ int nX, nY;
+ sal_uInt8 nResAlpha;
+
+ OSL_ENSURE(mpAlphaVDev,
+ "ImplBlendWithAlpha(): call me only with valid alpha VDev!" );
+
+ bool bOldMapMode( mpAlphaVDev->IsMapModeEnabled() );
+ mpAlphaVDev->EnableMapMode(false);
+
+ Bitmap aAlphaBitmap( mpAlphaVDev->GetBitmap( aDstRect.TopLeft(), aDstRect.GetSize() ) );
+ BitmapWriteAccess* pAlphaW = aAlphaBitmap.AcquireWriteAccess();
+
+ if( GetBitCount() <= 8 )
+ {
+ Bitmap aDither( aBmp.GetSizePixel(), 8 );
+ BitmapColor aIndex( 0 );
+ BitmapReadAccess* pB = aBmp.AcquireReadAccess();
+ BitmapWriteAccess* pW = aDither.AcquireWriteAccess();
+
+ if( pB && pP && pA && pW && pAlphaW )
+ {
+ int nOutY;
+
+ for( nY = 0, nOutY = nOffY; nY < nDstHeight; nY++, nOutY++ )
+ {
+ const long nMapY = pMapY[ nY ];
+ const long nModY = ( nOutY & 0x0FL ) << 4L;
+ int nOutX;
+
+ for( nX = 0, nOutX = nOffX; nX < nDstWidth; nX++, nOutX++ )
+ {
+ const long nMapX = pMapX[ nX ];
+ const sal_uLong nD = nVCLDitherLut[ nModY | ( nOutX & 0x0FL ) ];
+
+ aDstCol = lcl_AlphaBlend( nX, nY, nMapX, nMapY, pP, pA, pB, pAlphaW, nResAlpha );
+
+ aIndex.SetIndex( (sal_uInt8) ( nVCLRLut[ ( nVCLLut[ aDstCol.GetRed() ] + nD ) >> 16UL ] +
+ nVCLGLut[ ( nVCLLut[ aDstCol.GetGreen() ] + nD ) >> 16UL ] +
+ nVCLBLut[ ( nVCLLut[ aDstCol.GetBlue() ] + nD ) >> 16UL ] ) );
+ pW->SetPixel( nY, nX, aIndex );
+
+ aIndex.SetIndex( (sal_uInt8) ( nVCLRLut[ ( nVCLLut[ 255-nResAlpha ] + nD ) >> 16UL ] +
+ nVCLGLut[ ( nVCLLut[ 255-nResAlpha ] + nD ) >> 16UL ] +
+ nVCLBLut[ ( nVCLLut[ 255-nResAlpha ] + nD ) >> 16UL ] ) );
+ pAlphaW->SetPixel( nY, nX, aIndex );
+ }
+ }
+ }
+
+ aBmp.ReleaseAccess( pB );
+ aDither.ReleaseAccess( pW );
+ res = aDither;
+ }
+ else
+ {
+ BitmapWriteAccess* pB = aBmp.AcquireWriteAccess();
+ if( pP && pA && pB )
+ {
+ for( nY = 0; nY < nDstHeight; nY++ )
+ {
+ const long nMapY = pMapY[ nY ];
+
+ for( nX = 0; nX < nDstWidth; nX++ )
+ {
+ const long nMapX = pMapX[ nX ];
+ aDstCol = lcl_AlphaBlend( nX, nY, nMapX, nMapY, pP, pA, pB, pAlphaW, nResAlpha );
+
+ pB->SetPixel( nY, nX, aDstCol );
+ pAlphaW->SetPixel( nY, nX, Color(255L-nResAlpha, 255L-nResAlpha, 255L-nResAlpha) );
+ }
+ }
+ }
+
+ aBmp.ReleaseAccess( pB );
+ res = aBmp;
+ }
+
+ aAlphaBitmap.ReleaseAccess( pAlphaW );
+ mpAlphaVDev->DrawBitmap( aDstRect.TopLeft(), aAlphaBitmap );
+ mpAlphaVDev->EnableMapMode( bOldMapMode );
+
+ return res;
+}
+
+Bitmap OutputDevice::ImplBlend( Bitmap aBmp,
+ BitmapReadAccess* pP,
+ BitmapReadAccess* pA,
+ const sal_Int32 nOffY,
+ const sal_Int32 nDstHeight,
+ const sal_Int32 nOffX,
+ const sal_Int32 nDstWidth,
+ const Rectangle& aBmpRect,
+ const Size& aOutSz,
+ const bool bHMirr,
+ const bool bVMirr,
+ const long* pMapX,
+ const long* pMapY )
+{
+ BitmapColor aDstCol;
+ Bitmap res;
+ int nX, nY;
+
+ if( GetBitCount() <= 8 )
+ {
+ Bitmap aDither( aBmp.GetSizePixel(), 8 );
+ BitmapColor aIndex( 0 );
+ BitmapReadAccess* pB = aBmp.AcquireReadAccess();
+ BitmapWriteAccess* pW = aDither.AcquireWriteAccess();
+
+ if( pB && pP && pA && pW )
+ {
+ int nOutY;
+
+ for( nY = 0, nOutY = nOffY; nY < nDstHeight; nY++, nOutY++ )
+ {
+ const long nMapY = pMapY[ nY ];
+ const long nModY = ( nOutY & 0x0FL ) << 4L;
+ int nOutX;
+
+ for( nX = 0, nOutX = nOffX; nX < nDstWidth; nX++, nOutX++ )
+ {
+ const long nMapX = pMapX[ nX ];
+ const sal_uLong nD = nVCLDitherLut[ nModY | ( nOutX & 0x0FL ) ];
+
+ aDstCol = pB->GetColor( nY, nX );
+ aDstCol.Merge( pP->GetColor( nMapY, nMapX ), pA->GetPixelIndex( nMapY, nMapX ) );
+ aIndex.SetIndex( (sal_uInt8) ( nVCLRLut[ ( nVCLLut[ aDstCol.GetRed() ] + nD ) >> 16UL ] +
+ nVCLGLut[ ( nVCLLut[ aDstCol.GetGreen() ] + nD ) >> 16UL ] +
+ nVCLBLut[ ( nVCLLut[ aDstCol.GetBlue() ] + nD ) >> 16UL ] ) );
+ pW->SetPixel( nY, nX, aIndex );
+ }
+ }
+ }
+
+ aBmp.ReleaseAccess( pB );
+ aDither.ReleaseAccess( pW );
+ res = aDither;
+ }
+ else
+ {
+ BitmapWriteAccess* pB = aBmp.AcquireWriteAccess();
+
+ bool bFastBlend = false;
+ if( pP && pA && pB )
+ {
+ SalTwoRect aTR;
+ aTR.mnSrcX = aBmpRect.Left();
+ aTR.mnSrcY = aBmpRect.Top();
+ aTR.mnSrcWidth = aBmpRect.GetWidth();
+ aTR.mnSrcHeight = aBmpRect.GetHeight();
+ aTR.mnDestX = nOffX;
+ aTR.mnDestY = nOffY;
+ aTR.mnDestWidth = aOutSz.Width();
+ aTR.mnDestHeight= aOutSz.Height();
+
+ if( !bHMirr && !bVMirr )
+ bFastBlend = ImplFastBitmapBlending( *pB,*pP,*pA, aTR );
+ }
+
+ if( pP && pA && pB && !bFastBlend )
+ {
+ switch( pP->GetScanlineFormat() )
+ {
+ case( BMP_FORMAT_8BIT_PAL ):
+ {
+ for( nY = 0; nY < nDstHeight; nY++ )
+ {
+ const long nMapY = pMapY[ nY ];
+ Scanline pPScan = pP->GetScanline( nMapY );
+ Scanline pAScan = pA->GetScanline( nMapY );
+
+ for( nX = 0; nX < nDstWidth; nX++ )
+ {
+ const long nMapX = pMapX[ nX ];
+ aDstCol = pB->GetPixel( nY, nX );
+ pB->SetPixel( nY, nX, aDstCol.Merge( pP->GetPaletteColor( pPScan[ nMapX ] ),
+ pAScan[ nMapX ] ) );
+ }
+ }
+ }
+ break;
+
+ case( BMP_FORMAT_24BIT_TC_BGR ):
+ {
+ for( nY = 0; nY < nDstHeight; nY++ )
+ {
+ const long nMapY = pMapY[ nY ];
+ Scanline pPScan = pP->GetScanline( nMapY );
+ Scanline pAScan = pA->GetScanline( nMapY );
+
+ for( nX = 0; nX < nDstWidth; nX++ )
+ {
+ const long nMapX = pMapX[ nX ];
+ Scanline pTmp = pPScan + nMapX * 3;
+
+ aDstCol = pB->GetPixel( nY, nX );
+ pB->SetPixel( nY, nX, aDstCol.Merge( pTmp[ 2 ], pTmp[ 1 ], pTmp[ 0 ],
+ pAScan[ nMapX ] ) );
+ }
+ }
+ }
+ break;
+
+ case( BMP_FORMAT_24BIT_TC_RGB ):
+ {
+ for( nY = 0; nY < nDstHeight; nY++ )
+ {
+ const long nMapY = pMapY[ nY ];
+ Scanline pPScan = pP->GetScanline( nMapY );
+ Scanline pAScan = pA->GetScanline( nMapY );
+
+ for( nX = 0; nX < nDstWidth; nX++ )
+ {
+ const long nMapX = pMapX[ nX ];
+ Scanline pTmp = pPScan + nMapX * 3;
+
+ aDstCol = pB->GetPixel( nY, nX );
+ pB->SetPixel( nY, nX, aDstCol.Merge( pTmp[ 0 ], pTmp[ 1 ], pTmp[ 2 ],
+ pAScan[ nMapX ] ) );
+ }
+ }
+ }
+ break;
+
+ default:
+ {
+ for( nY = 0; nY < nDstHeight; nY++ )
+ {
+ const long nMapY = pMapY[ nY ];
+ Scanline pAScan = pA->GetScanline( nMapY );
+
+ for( nX = 0; nX < nDstWidth; nX++ )
+ {
+ const long nMapX = pMapX[ nX ];
+ aDstCol = pB->GetPixel( nY, nX );
+ pB->SetPixel( nY, nX, aDstCol.Merge( pP->GetColor( nMapY, nMapX ),
+ pAScan[ nMapX ] ) );
+ }
+ }
+ }
+ break;
+ }
+ }
+
+ aBmp.ReleaseAccess( pB );
+ res = aBmp;
+ }
+
+ return res;
+}
+
+void OutputDevice::ClipToPaintRegion(Rectangle& /*rDstRect*/)
+{
+}
+
+void OutputDevice::ImplDrawAlpha( const Bitmap& rBmp, const AlphaMask& rAlpha,
+ const Point& rDestPt, const Size& rDestSize,
+ const Point& rSrcPtPixel, const Size& rSrcSizePixel )
+{
+ const Point aNullPt;
+ Point aOutPt( LogicToPixel( rDestPt ) );
+ Size aOutSz( LogicToPixel( rDestSize ) );
+ Rectangle aDstRect( aNullPt, GetOutputSizePixel() );
+ const bool bHMirr = aOutSz.Width() < 0;
+ const bool bVMirr = aOutSz.Height() < 0;
+
+ ClipToPaintRegion(aDstRect);
+
+ if( bHMirr )
+ {
+ aOutSz.Width() = -aOutSz.Width();
+ aOutPt.X() -= ( aOutSz.Width() - 1L );
+ }
+
+ if( bVMirr )
+ {
+ aOutSz.Height() = -aOutSz.Height();
+ aOutPt.Y() -= ( aOutSz.Height() - 1L );
+ }
+
+ if( !aDstRect.Intersection( Rectangle( aOutPt, aOutSz ) ).IsEmpty() )
+ {
+ bool bNativeAlpha = false;
+ static const char* pDisableNative = getenv( "SAL_DISABLE_NATIVE_ALPHA");
+ // #i83087# Naturally, system alpha blending cannot work with
+ // separate alpha VDev
+ bool bTryDirectPaint(!mpAlphaVDev && !pDisableNative && !bHMirr && !bVMirr);
+
+#ifdef WNT
+ if(bTryDirectPaint)
+ {
+ // only paint direct when no scaling and no MapMode, else the
+ // more expensive conversions may be done for short-time Bitmap/BitmapEx
+ // used for buffering only
+ if(!IsMapMode() && rSrcSizePixel.Width() == aOutSz.Width() && rSrcSizePixel.Height() == aOutSz.Height())
+ {
+ bTryDirectPaint = false;
+ }
+ }
+#endif
+
+ if(bTryDirectPaint)
+ {
+ Point aRelPt = aOutPt + Point( mnOutOffX, mnOutOffY );
+ SalTwoRect aTR = {
+ rSrcPtPixel.X(), rSrcPtPixel.Y(),
+ rSrcSizePixel.Width(), rSrcSizePixel.Height(),
+ aRelPt.X(), aRelPt.Y(),
+ aOutSz.Width(), aOutSz.Height()
+ };
+ SalBitmap* pSalSrcBmp = rBmp.ImplGetImpBitmap()->ImplGetSalBitmap();
+ SalBitmap* pSalAlphaBmp = rAlpha.ImplGetImpBitmap()->ImplGetSalBitmap();
+ bNativeAlpha = mpGraphics->DrawAlphaBitmap( aTR, *pSalSrcBmp, *pSalAlphaBmp, this );
+ }
+
+ VirtualDevice* pOldVDev = mpAlphaVDev;
+
+ Rectangle aBmpRect( aNullPt, rBmp.GetSizePixel() );
+ if( !bNativeAlpha
+ && !aBmpRect.Intersection( Rectangle( rSrcPtPixel, rSrcSizePixel ) ).IsEmpty() )
+ {
+ // The scaling in this code path produces really ugly results - it
+ // does the most trivial scaling with no smoothing.
+
+ GDIMetaFile* pOldMetaFile = mpMetaFile;
+ const bool bOldMap = mbMap;
+ mpMetaFile = NULL; // fdo#55044 reset before GetBitmap!
+ mbMap = false;
+ Bitmap aBmp( GetBitmap( aDstRect.TopLeft(), aDstRect.GetSize() ) );
+
+ // #109044# The generated bitmap need not necessarily be
+ // of aDstRect dimensions, it's internally clipped to
+ // window bounds. Thus, we correct the dest size here,
+ // since we later use it (in nDstWidth/Height) for pixel
+ // access)
+ // #i38887# reading from screen may sometimes fail
+ if( aBmp.ImplGetImpBitmap() )
+ aDstRect.SetSize( aBmp.GetSizePixel() );
+
+ BitmapColor aDstCol;
+ const long nSrcWidth = aBmpRect.GetWidth(), nSrcHeight = aBmpRect.GetHeight();
+ const long nDstWidth = aDstRect.GetWidth(), nDstHeight = aDstRect.GetHeight();
+ const long nOutWidth = aOutSz.Width(), nOutHeight = aOutSz.Height();
+ // calculate offset in original bitmap
+ // in RTL case this is a little more complicated since the contents of the
+ // bitmap is not mirrored (it never is), however the paint region and bmp region
+ // are in mirrored coordinates, so the intersection of (aOutPt,aOutSz) with these
+ // is content wise somewhere else and needs to take mirroring into account
+ const long nOffX = IsRTLEnabled()
+ ? aOutSz.Width() - aDstRect.GetWidth() - (aDstRect.Left() - aOutPt.X())
+ : aDstRect.Left() - aOutPt.X(),
+ nOffY = aDstRect.Top() - aOutPt.Y();
+ long nX, nOutX, nY, nOutY;
+ long nMirrOffX = 0;
+ long nMirrOffY = 0;
+ boost::scoped_array<long> pMapX(new long[ nDstWidth ]);
+ boost::scoped_array<long> pMapY(new long[ nDstHeight ]);
+
+ // create horizontal mapping table
+ if( bHMirr )
+ nMirrOffX = ( aBmpRect.Left() << 1 ) + nSrcWidth - 1;
+
+ for( nX = 0L, nOutX = nOffX; nX < nDstWidth; nX++, nOutX++ )
+ {
+ pMapX[ nX ] = aBmpRect.Left() + nOutX * nSrcWidth / nOutWidth;
+ if( bHMirr )
+ pMapX[ nX ] = nMirrOffX - pMapX[ nX ];
+ }
+
+ // create vertical mapping table
+ if( bVMirr )
+ nMirrOffY = ( aBmpRect.Top() << 1 ) + nSrcHeight - 1;
+
+ for( nY = 0L, nOutY = nOffY; nY < nDstHeight; nY++, nOutY++ )
+ {
+ pMapY[ nY ] = aBmpRect.Top() + nOutY * nSrcHeight / nOutHeight;
+
+ if( bVMirr )
+ pMapY[ nY ] = nMirrOffY - pMapY[ nY ];
+ }
+
+ BitmapReadAccess* pP = ( (Bitmap&) rBmp ).AcquireReadAccess();
+ BitmapReadAccess* pA = ( (AlphaMask&) rAlpha ).AcquireReadAccess();
+
+ DBG_ASSERT( pA->GetScanlineFormat() == BMP_FORMAT_8BIT_PAL ||
+ pA->GetScanlineFormat() == BMP_FORMAT_8BIT_TC_MASK,
+ "OutputDevice::ImplDrawAlpha(): non-8bit alpha no longer supported!" );
+
+ // #i38887# reading from screen may sometimes fail
+ if( aBmp.ImplGetImpBitmap() )
+ {
+ Bitmap aTmp;
+
+ if( mpAlphaVDev )
+ {
+ aTmp = ImplBlendWithAlpha(
+ aBmp,pP,pA,
+ aDstRect,
+ nOffY,nDstHeight,
+ nOffX,nDstWidth,
+ pMapX.get(),pMapY.get() );
+ }
+ else
+ {
+ aTmp = ImplBlend(
+ aBmp,pP,pA,
+ nOffY,nDstHeight,
+ nOffX,nDstWidth,
+ aBmpRect,aOutSz,
+ bHMirr,bVMirr,
+ pMapX.get(),pMapY.get() );
+ }
+
+ // #110958# Disable alpha VDev, we're doing the necessary
+ // stuff explicitly furher below
+ if( mpAlphaVDev )
+ mpAlphaVDev = NULL;
+
+ DrawBitmap( aDstRect.TopLeft(),
+ aTmp );
+
+ // #110958# Enable alpha VDev again
+ mpAlphaVDev = pOldVDev;
+ }
+
+ ( (Bitmap&) rBmp ).ReleaseAccess( pP );
+ ( (AlphaMask&) rAlpha ).ReleaseAccess( pA );
+
+ mbMap = bOldMap;
+ mpMetaFile = pOldMetaFile;
+ }
+ }
+}
+
+void OutputDevice::ImplPrintTransparent( const Bitmap& rBmp, const Bitmap& rMask,
+ const Point& rDestPt, const Size& rDestSize,
+ const Point& rSrcPtPixel, const Size& rSrcSizePixel )
+{
+ Point aPt;
+ Point aDestPt( LogicToPixel( rDestPt ) );
+ Size aDestSz( LogicToPixel( rDestSize ) );
+ Rectangle aSrcRect( rSrcPtPixel, rSrcSizePixel );
+
+ aSrcRect.Justify();
+
+ if( !rBmp.IsEmpty() && aSrcRect.GetWidth() && aSrcRect.GetHeight() && aDestSz.Width() && aDestSz.Height() )
+ {
+ Bitmap aPaint( rBmp ), aMask( rMask );
+ sal_uLong nMirrFlags = 0UL;
+
+ if( aMask.GetBitCount() > 1 )
+ aMask.Convert( BMP_CONVERSION_1BIT_THRESHOLD );
+
+ // mirrored horizontically
+ if( aDestSz.Width() < 0L )
+ {
+ aDestSz.Width() = -aDestSz.Width();
+ aDestPt.X() -= ( aDestSz.Width() - 1L );
+ nMirrFlags |= BMP_MIRROR_HORZ;
+ }
+
+ // mirrored vertically
+ if( aDestSz.Height() < 0L )
+ {
+ aDestSz.Height() = -aDestSz.Height();
+ aDestPt.Y() -= ( aDestSz.Height() - 1L );
+ nMirrFlags |= BMP_MIRROR_VERT;
+ }
+
+ // source cropped?
+ if( aSrcRect != Rectangle( aPt, aPaint.GetSizePixel() ) )
+ {
+ aPaint.Crop( aSrcRect );
+ aMask.Crop( aSrcRect );
+ }
+
+ // destination mirrored
+ if( nMirrFlags )
+ {
+ aPaint.Mirror( nMirrFlags );
+ aMask.Mirror( nMirrFlags );
+ }
+
+ // we always want to have a mask
+ if( aMask.IsEmpty() )
+ {
+ aMask = Bitmap( aSrcRect.GetSize(), 1 );
+ aMask.Erase( Color( COL_BLACK ) );
+ }
+
+ // do painting
+ const long nSrcWidth = aSrcRect.GetWidth(), nSrcHeight = aSrcRect.GetHeight();
+ long nX, nY; // , nWorkX, nWorkY, nWorkWidth, nWorkHeight;
+ boost::scoped_array<long> pMapX(new long[ nSrcWidth + 1 ]);
+ boost::scoped_array<long> pMapY(new long[ nSrcHeight + 1 ]);
+ const bool bOldMap = mbMap;
+
+ mbMap = false;
+
+ // create forward mapping tables
+ for( nX = 0L; nX <= nSrcWidth; nX++ )
+ pMapX[ nX ] = aDestPt.X() + FRound( (double) aDestSz.Width() * nX / nSrcWidth );
+
+ for( nY = 0L; nY <= nSrcHeight; nY++ )
+ pMapY[ nY ] = aDestPt.Y() + FRound( (double) aDestSz.Height() * nY / nSrcHeight );
+
+ // walk through all rectangles of mask
+ const Region aWorkRgn(aMask.CreateRegion(COL_BLACK, Rectangle(Point(), aMask.GetSizePixel())));
+ RectangleVector aRectangles;
+ aWorkRgn.GetRegionRectangles(aRectangles);
+
+ for(RectangleVector::const_iterator aRectIter(aRectangles.begin()); aRectIter != aRectangles.end(); ++aRectIter)
+ {
+ const Point aMapPt(pMapX[aRectIter->Left()], pMapY[aRectIter->Top()]);
+ const Size aMapSz(
+ pMapX[aRectIter->Right() + 1] - aMapPt.X(), // pMapX[L + W] -> L + ((R - L) + 1) -> R + 1
+ pMapY[aRectIter->Bottom() + 1] - aMapPt.Y()); // same for Y
+ Bitmap aBandBmp(aPaint);
+
+ aBandBmp.Crop(*aRectIter);
+ ImplDrawBitmap(aMapPt, aMapSz, Point(), aBandBmp.GetSizePixel(), aBandBmp, META_BMPSCALEPART_ACTION);
+ }
+
+ mbMap = bOldMap;
+ }
+}
+
+void OutputDevice::ImplPrintMask( const Bitmap& /*rMask*/, const Color& /*rMaskColor*/,
+ const Point& /*rDestPt*/, const Size& /*rDestSize*/,
+ const Point& /*rSrcPtPixel*/, const Size& /*rSrcSizePixel*/ )
+{
+ // let's leave this for Printer
+ return;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */