diff options
Diffstat (limited to 'vcl/source/gdi/outdev/outdev2.cxx')
-rw-r--r-- | vcl/source/gdi/outdev/outdev2.cxx | 2228 |
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: */ |