| /************************************************************** |
| * |
| * 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 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| // MARKER(update_precomp.py): autogen include statement, do not remove |
| #include "precompiled_vcl.hxx" |
| |
| #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/window.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> |
| |
| #define BAND_MAX_SIZE 512000 |
| |
| // ======================================================================= |
| |
| DBG_NAMEEX( OutputDevice ) |
| |
| // ======================================================================= |
| |
| // ----------- |
| // - Defines - |
| // ----------- |
| |
| #define OUTDEV_INIT() \ |
| { \ |
| if ( !IsDeviceOutputNecessary() ) \ |
| return; \ |
| \ |
| if ( !mpGraphics ) \ |
| if ( !ImplGetGraphics() ) \ |
| return; \ |
| \ |
| if ( mbInitClipRegion ) \ |
| ImplInitClipRegion(); \ |
| \ |
| if ( mbOutputClipped ) \ |
| return; \ |
| } |
| |
| // ------------- |
| // - externals - |
| // ------------- |
| |
| extern sal_uLong nVCLRLut[ 6 ]; |
| extern sal_uLong nVCLGLut[ 6 ]; |
| extern sal_uLong nVCLBLut[ 6 ]; |
| extern sal_uLong nVCLDitherLut[ 256 ]; |
| extern 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 OutputDevice::ImplDrawOutDevDirect( const OutputDevice* pSrcDev, SalTwoRect& rPosAry ) |
| { |
| SalGraphics* pGraphics2; |
| |
| if ( rPosAry.mnSrcWidth && rPosAry.mnSrcHeight && rPosAry.mnDestWidth && rPosAry.mnDestHeight ) |
| { |
| 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 |
| Rectangle aSrcOutRect( Point( pSrcDev->mnOutOffX, pSrcDev->mnOutOffY ), |
| Size( pSrcDev->mnOutWidth, pSrcDev->mnOutHeight ) ); |
| Rectangle aSrcRect( Point( rPosAry.mnSrcX, rPosAry.mnSrcY ), |
| Size( rPosAry.mnSrcWidth, rPosAry.mnSrcHeight ) ); |
| const long nOldRight = aSrcRect.Right(); |
| const long nOldBottom = aSrcRect.Bottom(); |
| |
| if ( !aSrcRect.Intersection( aSrcOutRect ).IsEmpty() ) |
| { |
| if ( (rPosAry.mnSrcX+rPosAry.mnSrcWidth-1) > aSrcOutRect.Right() ) |
| { |
| const long nOldWidth = rPosAry.mnSrcWidth; |
| rPosAry.mnSrcWidth -= (nOldRight - aSrcRect.Right()); |
| rPosAry.mnDestWidth = rPosAry.mnDestWidth * rPosAry.mnSrcWidth / nOldWidth; |
| } |
| |
| if ( (rPosAry.mnSrcY+rPosAry.mnSrcHeight-1) > aSrcOutRect.Bottom() ) |
| { |
| const long nOldHeight = rPosAry.mnSrcHeight; |
| rPosAry.mnSrcHeight -= (nOldBottom - aSrcRect.Bottom()); |
| rPosAry.mnDestHeight = rPosAry.mnDestHeight * rPosAry.mnSrcHeight / nOldHeight; |
| } |
| |
| // --- 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 ) |
| { |
| DBG_TRACE( "OutputDevice::DrawOutDev()" ); |
| DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); |
| DBG_ASSERT( meOutDevType != OUTDEV_PRINTER, "Don't use OutputDevice::DrawOutDev(...) with printer devices!" ); |
| |
| if( ImplIsRecordLayout() ) |
| return; |
| |
| if ( meOutDevType == OUTDEV_PRINTER ) |
| 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() ); |
| |
| Rectangle aSrcOutRect( Point( mnOutOffX, mnOutOffY ), |
| Size( mnOutWidth, mnOutHeight ) ); |
| Rectangle aSrcRect( Point( aPosAry.mnSrcX, aPosAry.mnSrcY ), |
| Size( aPosAry.mnSrcWidth, aPosAry.mnSrcHeight ) ); |
| long nOldRight = aSrcRect.Right(); |
| long nOldBottom = aSrcRect.Bottom(); |
| |
| if ( !aSrcRect.Intersection( aSrcOutRect ).IsEmpty() ) |
| { |
| if ( (aPosAry.mnSrcX+aPosAry.mnSrcWidth-1) > aSrcOutRect.Right() ) |
| { |
| long nOldWidth = aPosAry.mnSrcWidth; |
| aPosAry.mnSrcWidth -= nOldRight-aSrcRect.Right(); |
| aPosAry.mnDestWidth = aPosAry.mnDestWidth*aPosAry.mnSrcWidth/nOldWidth; |
| } |
| |
| if ( (aPosAry.mnSrcY+aPosAry.mnSrcHeight-1) > aSrcOutRect.Bottom() ) |
| { |
| long nOldHeight = aPosAry.mnSrcHeight; |
| aPosAry.mnSrcHeight -= nOldBottom-aSrcRect.Bottom(); |
| aPosAry.mnDestHeight = aPosAry.mnDestHeight*aPosAry.mnSrcHeight/nOldHeight; |
| } |
| |
| 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 ) |
| { |
| DBG_TRACE( "OutputDevice::DrawOutDev()" ); |
| DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); |
| DBG_CHKOBJ( &rOutDev, OutputDevice, ImplDbgCheckOutputDevice ); |
| DBG_ASSERT( meOutDevType != OUTDEV_PRINTER, "Don't use OutputDevice::DrawOutDev(...) with printer devices!" ); |
| DBG_ASSERT( rOutDev.meOutDevType != OUTDEV_PRINTER, "Don't use OutputDevice::DrawOutDev(...) with printer devices!" ); |
| |
| if ( (meOutDevType == OUTDEV_PRINTER) || (rOutDev.meOutDevType == OUTDEV_PRINTER) || 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) ); |
| |
| // This would be mode SOURCE: |
| // copy source alpha channel to our alpha channel |
| //mpAlphaVDev->DrawOutDev( rDestPt, rDestSize, rSrcPt, rSrcSize, *rOutDev.mpAlphaVDev ); |
| } |
| 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 ) |
| { |
| DBG_TRACE( "OutputDevice::CopyArea()" ); |
| DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); |
| DBG_ASSERT( meOutDevType != OUTDEV_PRINTER, "Don't use OutputDevice::CopyArea(...) with printer devices!" ); |
| |
| if ( meOutDevType == OUTDEV_PRINTER || 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() ); |
| |
| Rectangle aSrcOutRect( Point( mnOutOffX, mnOutOffY ), |
| Size( mnOutWidth, mnOutHeight ) ); |
| Rectangle aSrcRect( Point( aPosAry.mnSrcX, aPosAry.mnSrcY ), |
| Size( aPosAry.mnSrcWidth, aPosAry.mnSrcHeight ) ); |
| long nOldRight = aSrcRect.Right(); |
| long nOldBottom = aSrcRect.Bottom(); |
| |
| if ( !aSrcRect.Intersection( aSrcOutRect ).IsEmpty() ) |
| { |
| if ( (aPosAry.mnSrcX+aPosAry.mnSrcWidth-1) > aSrcOutRect.Right() ) |
| aPosAry.mnSrcWidth -= nOldRight-aSrcRect.Right(); |
| |
| if ( (aPosAry.mnSrcY+aPosAry.mnSrcHeight-1) > aSrcOutRect.Bottom() ) |
| aPosAry.mnSrcHeight -= nOldBottom-aSrcRect.Bottom(); |
| |
| if ( (meOutDevType == OUTDEV_WINDOW) && (nFlags & COPYAREA_WINDOWINVALIDATE) ) |
| { |
| ((Window*)this)->ImplMoveAllInvalidateRegions( aSrcRect, |
| aPosAry.mnDestX-aPosAry.mnSrcX, |
| aPosAry.mnDestY-aPosAry.mnSrcY, |
| sal_False ); |
| |
| mpGraphics->CopyArea( aPosAry.mnDestX, aPosAry.mnDestY, |
| aPosAry.mnSrcX, aPosAry.mnSrcY, |
| aPosAry.mnSrcWidth, aPosAry.mnSrcHeight, |
| SAL_COPYAREA_WINDOWINVALIDATE, this ); |
| } |
| else |
| { |
| aPosAry.mnDestWidth = aPosAry.mnSrcWidth; |
| aPosAry.mnDestHeight = aPosAry.mnSrcHeight; |
| mpGraphics->CopyBits( aPosAry, NULL, this, NULL ); |
| } |
| } |
| } |
| |
| SetRasterOp( eOldRop ); |
| |
| if( mpAlphaVDev ) |
| mpAlphaVDev->CopyArea( rDestPt, rSrcPt, rSrcSize, nFlags ); |
| } |
| |
| // ------------------------------------------------------------------ |
| |
| void OutputDevice::ImplDrawFrameDev( const Point& rPt, const Point& rDevPt, const Size& rDevSize, |
| const OutputDevice& rOutDev, const Region& rRegion ) |
| { |
| DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); |
| |
| GDIMetaFile* pOldMetaFile = mpMetaFile; |
| sal_Bool bOldMap = mbMap; |
| RasterOp eOldROP = GetRasterOp(); |
| mpMetaFile = NULL; |
| mbMap = sal_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 ); |
| |
| // Dafuer sorgen, das ClipRegion neu berechnet und gesetzt wird |
| mbInitClipRegion = sal_True; |
| |
| SetRasterOp( eOldROP ); |
| mbMap = bOldMap; |
| mpMetaFile = pOldMetaFile; |
| } |
| |
| // ------------------------------------------------------------------ |
| |
| void OutputDevice::ImplGetFrameDev( const Point& rPt, const Point& rDevPt, const Size& rDevSize, |
| OutputDevice& rDev ) |
| { |
| DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); |
| |
| sal_Bool bOldMap = mbMap; |
| mbMap = sal_False; |
| rDev.DrawOutDev( rDevPt, rDevSize, rPt, rDevSize, *this ); |
| mbMap = bOldMap; |
| } |
| |
| // ------------------------------------------------------------------ |
| |
| void OutputDevice::DrawBitmap( const Point& rDestPt, const Bitmap& rBitmap ) |
| { |
| DBG_TRACE( "OutputDevice::DrawBitmap()" ); |
| |
| 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 ) |
| { |
| DBG_TRACE( "OutputDevice::DrawBitmap( Size )" ); |
| |
| 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 ) |
| { |
| DBG_TRACE( "OutputDevice::DrawBitmap( Point, Size )" ); |
| |
| 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 ) |
| { |
| DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); |
| |
| 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() ); |
| |
| const sal_uLong nMirrFlags = ImplAdjustTwoRect( aPosAry, aBmp.GetSizePixel() ); |
| |
| if ( aPosAry.mnSrcWidth && aPosAry.mnSrcHeight && aPosAry.mnDestWidth && aPosAry.mnDestHeight ) |
| { |
| if ( nMirrFlags ) |
| aBmp.Mirror( nMirrFlags ); |
| |
| /* #i75264# (corrected with #i81576#) |
| * sometimes a bitmap is scaled to a ridiculous size and drawn |
| * to a quite normal VDev, so only a very small part of |
| * the scaled bitmap will be visible. However actually scaling |
| * the bitmap will use so much memory that we end with a crash. |
| * Workaround: since only a small part of the scaled bitmap will |
| * be actually drawn anyway (because of clipping on the device |
| * boundary), limit the destination and source rectangles so |
| * that the destination rectangle will overlap the device but only |
| * be reasonably (say factor 2) larger than the device itself. |
| */ |
| |
| // not needed for win32, it uses GdiPlus and is able to do everything without |
| // internally scaling the bitmap |
| #ifndef WIN32 |
| |
| if( aPosAry.mnDestWidth > 2048 || aPosAry.mnDestHeight > 2048 ) |
| { |
| if( meOutDevType == OUTDEV_WINDOW || |
| (meOutDevType == OUTDEV_VIRDEV && mpPDFWriter == 0 ) ) |
| { |
| // #i81576# do the following trick only if there is overlap at all |
| // else the formulae don't work |
| // theoretically in this case we wouldn't need to draw the bitmap at all |
| // however there are some esoteric case where that is needed |
| if( aPosAry.mnDestX + aPosAry.mnDestWidth >= 0 |
| && aPosAry.mnDestX < mnOutWidth |
| && aPosAry.mnDestY + aPosAry.mnDestHeight >= 0 |
| && aPosAry.mnDestY < mnOutHeight ) |
| { |
| // reduce scaling to something reasonable taking into account the output size |
| if( aPosAry.mnDestWidth > 3*mnOutWidth && aPosAry.mnSrcWidth ) |
| { |
| const double nScaleX = aPosAry.mnDestWidth/double(aPosAry.mnSrcWidth); |
| |
| if( aPosAry.mnDestX + aPosAry.mnDestWidth > mnOutWidth ) |
| { |
| aPosAry.mnDestWidth = Max(long(0),mnOutWidth-aPosAry.mnDestX); |
| } |
| if( aPosAry.mnDestX < 0 ) |
| { |
| aPosAry.mnDestWidth += aPosAry.mnDestX; |
| aPosAry.mnSrcX -= sal::static_int_cast<long>(aPosAry.mnDestX / nScaleX); |
| aPosAry.mnDestX = 0; |
| } |
| |
| aPosAry.mnSrcWidth = sal::static_int_cast<long>(aPosAry.mnDestWidth / nScaleX); |
| } |
| |
| if( aPosAry.mnDestHeight > 3*mnOutHeight && aPosAry.mnSrcHeight != 0 ) |
| { |
| const double nScaleY = aPosAry.mnDestHeight/double(aPosAry.mnSrcHeight); |
| |
| if( aPosAry.mnDestY + aPosAry.mnDestHeight > mnOutHeight ) |
| { |
| aPosAry.mnDestHeight = Max(long(0),mnOutHeight-aPosAry.mnDestY); |
| } |
| if( aPosAry.mnDestY < 0 ) |
| { |
| aPosAry.mnDestHeight += aPosAry.mnDestY; |
| aPosAry.mnSrcY -= sal::static_int_cast<long>(aPosAry.mnDestY / nScaleY); |
| aPosAry.mnDestY = 0; |
| } |
| |
| aPosAry.mnSrcHeight = sal::static_int_cast<long>(aPosAry.mnDestHeight / nScaleY); |
| } |
| } |
| } |
| } |
| #endif |
| |
| if ( aPosAry.mnSrcWidth && aPosAry.mnSrcHeight && aPosAry.mnDestWidth && aPosAry.mnDestHeight ) |
| { |
| mpGraphics->DrawBitmap( aPosAry, *aBmp.ImplGetImpBitmap()->ImplGetSalBitmap(), this ); |
| } |
| } |
| } |
| } |
| |
| // ------------------------------------------------------------------ |
| |
| void OutputDevice::DrawBitmapEx( const Point& rDestPt, |
| const BitmapEx& rBitmapEx ) |
| { |
| DBG_TRACE( "OutputDevice::DrawBitmapEx()" ); |
| |
| 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 ) |
| { |
| DBG_TRACE( "OutputDevice::DrawBitmapEx( Size )" ); |
| |
| 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 ) |
| { |
| DBG_TRACE( "OutputDevice::DrawBitmapEx( Point, Size )" ); |
| |
| 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 ); |
| } |
| } |
| |
| // ------------------------------------------------------------------ |
| |
| void OutputDevice::DrawTransformedBitmapEx( |
| const basegfx::B2DHomMatrix& rTransformation, |
| const BitmapEx& rBitmapEx) |
| { |
| DBG_TRACE( "OutputDevice::DrawBitmapEx( Point, Size )" ); |
| |
| 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 |
| const Point aDestPt(basegfx::fround(aTranslate.getX()), basegfx::fround(aTranslate.getY())); |
| const Size aDestSize(basegfx::fround(aScale.getX()), basegfx::fround(aScale.getY())); |
| |
| 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); |
| const bool bPrinter(OUTDEV_PRINTER == meOutDevType); |
| bool bDone(false); |
| const basegfx::B2DHomMatrix aFullTransform(GetViewTransformation() * rTransformation); |
| const bool bTryDirectPaint(!bInvert && !bBitmapChangedColor && !bMetafile && !bPrinter); |
| |
| if(!bForceToOwnTransformer && bTryDirectPaint) |
| { |
| // 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); |
| } |
| |
| 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 |
| const Point aDestPt(basegfx::fround(aTranslate.getX()), basegfx::fround(aTranslate.getY())); |
| const Size aDestSize(basegfx::fround(aScale.getX()), basegfx::fround(aScale.getY())); |
| |
| 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 && !bPrinter) |
| { |
| // 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; |
| } |
| |
| // 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; |
| } |
| |
| // 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; |
| } |
| |
| // 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); |
| } |
| |
| 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) |
| 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 |
| const Point aDestPt(basegfx::fround(aVisibleRange.getMinX()), basegfx::fround(aVisibleRange.getMinY())); |
| const Size aDestSize(basegfx::fround(aVisibleRange.getWidth()), basegfx::fround(aVisibleRange.getHeight())); |
| |
| 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 ) |
| { |
| DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); |
| 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( 128 ); |
| 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(); |
| |
| if( OUTDEV_PRINTER == meOutDevType ) |
| { |
| if( aBmpEx.IsAlpha() ) |
| { |
| // #107169# For true alpha bitmaps, no longer masking the |
| // bitmap, but perform a full alpha blend against a white |
| // background here. |
| Bitmap aBmp( aBmpEx.GetBitmap() ); |
| aBmp.Blend( aBmpEx.GetAlpha(), Color( COL_WHITE) ); |
| DrawBitmap( rDestPt, rDestSize, rSrcPtPixel, rSrcSizePixel, aBmp ); |
| } |
| else |
| { |
| Bitmap aBmp( aBmpEx.GetBitmap() ), aMask( aBmpEx.GetMask() ); |
| aBmp.Replace( aMask, Color( COL_WHITE ) ); |
| ImplPrintTransparent( aBmp, aMask, rDestPt, rDestSize, rSrcPtPixel, rSrcSizePixel ); |
| } |
| |
| return; |
| } |
| |
| if(aBmpEx.IsAlpha()) |
| { |
| ImplDrawAlpha( aBmpEx.GetBitmap(), aBmpEx.GetAlpha(), rDestPt, rDestSize, rSrcPtPixel, rSrcSizePixel ); |
| return; |
| } |
| |
| if( !( !aBmpEx ) ) |
| { |
| 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, aBmpEx.GetSizePixel() ); |
| |
| if( aPosAry.mnSrcWidth && aPosAry.mnSrcHeight && aPosAry.mnDestWidth && aPosAry.mnDestHeight ) |
| { |
| |
| if( nMirrFlags ) |
| aBmpEx.Mirror( nMirrFlags ); |
| |
| const SalBitmap* pSalSrcBmp = aBmpEx.ImplGetBitmapImpBitmap()->ImplGetSalBitmap(); |
| const ImpBitmap* pMaskBmp = aBmpEx.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 optimisation 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( aBmpEx.GetMask(), |
| aBmpEx.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 ) |
| { |
| DBG_TRACE( "OutputDevice::DrawMask()" ); |
| |
| if( ImplIsRecordLayout() ) |
| return; |
| |
| const Size aSizePix( rBitmap.GetSizePixel() ); |
| ImplDrawMask( rDestPt, PixelToLogic( aSizePix ), Point(), aSizePix, rBitmap, rMaskColor, META_MASK_ACTION ); |
| |
| 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, |
| PixelToLogic( aSizePix ), |
| BitmapEx( rMask, rMask ) ); |
| } |
| } |
| |
| // ------------------------------------------------------------------ |
| |
| void OutputDevice::DrawMask( const Point& rDestPt, const Size& rDestSize, |
| const Bitmap& rBitmap, const Color& rMaskColor ) |
| { |
| DBG_TRACE( "OutputDevice::DrawMask( Size )" ); |
| |
| if( ImplIsRecordLayout() ) |
| return; |
| |
| ImplDrawMask( rDestPt, rDestSize, Point(), rBitmap.GetSizePixel(), rBitmap, rMaskColor, META_MASKSCALE_ACTION ); |
| |
| // 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, |
| BitmapEx( rMask, rMask ) ); |
| } |
| } |
| |
| // ------------------------------------------------------------------ |
| |
| void OutputDevice::DrawMask( const Point& rDestPt, const Size& rDestSize, |
| const Point& rSrcPtPixel, const Size& rSrcSizePixel, |
| const Bitmap& rBitmap, const Color& rMaskColor ) |
| { |
| DBG_TRACE( "OutputDevice::DrawMask( Point, Size )" ); |
| |
| if( ImplIsRecordLayout() ) |
| return; |
| |
| ImplDrawMask( rDestPt, rDestSize, rSrcPtPixel, rSrcSizePixel, rBitmap, rMaskColor, META_MASKSCALEPART_ACTION ); |
| |
| // 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 ) ); |
| } |
| } |
| |
| // ------------------------------------------------------------------ |
| |
| void OutputDevice::ImplDrawMask( const Point& rDestPt, const Size& rDestSize, |
| const Point& rSrcPtPixel, const Size& rSrcSizePixel, |
| const Bitmap& rBitmap, const Color& rMaskColor, |
| const sal_uLong nAction ) |
| { |
| DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); |
| |
| 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() ); |
| |
| // spiegeln via Koordinaten wollen wir nicht |
| 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 ); |
| |
| } |
| } |
| } |
| |
| // ------------------------------------------------------------------ |
| |
| 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: |
| DrawBitmap( rPos, *static_cast< Bitmap* >( rImage.mpImplData->mpData ) ); |
| 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: |
| DrawBitmap( rPos, rSize, *static_cast< Bitmap* >( rImage.mpImplData->mpData ) ); |
| 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 |
| { |
| DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); |
| 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 ) ); |
| sal_Bool bClipped = sal_False; |
| |
| // X-Koordinate ausserhalb des Bereichs? |
| if ( nX < mnOutOffX ) |
| { |
| nWidth -= ( mnOutOffX - nX ); |
| nX = mnOutOffX; |
| bClipped = sal_True; |
| } |
| |
| // Y-Koordinate ausserhalb des Bereichs? |
| if ( nY < mnOutOffY ) |
| { |
| nHeight -= ( mnOutOffY - nY ); |
| nY = mnOutOffY; |
| bClipped = sal_True; |
| } |
| |
| // Breite ausserhalb des Bereichs? |
| if ( (nWidth + nX) > (mnOutWidth + mnOutOffX) ) |
| { |
| nWidth = mnOutOffX + mnOutWidth - nX; |
| bClipped = sal_True; |
| } |
| |
| // Hoehe ausserhalb des Bereichs? |
| if ( (nHeight + nY) > (mnOutHeight + mnOutOffY) ) |
| { |
| nHeight = mnOutOffY + mnOutHeight - nY; |
| bClipped = sal_True; |
| } |
| |
| if ( bClipped ) |
| { |
| // Falls auf den sichtbaren Bereich geclipped wurde, |
| // muessen wir eine Bitmap in der rchtigen Groesse |
| // erzeugen, in die die geclippte Bitmap an die angepasste |
| // Position kopiert wird |
| 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 = sal_False; |
| } |
| else |
| bClipped = sal_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 |
| { |
| DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); |
| |
| // #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 ); |
| } |
| |
| // ------------------------------------------------------------------ |
| |
| void OutputDevice::ImplGetFrameBitmap( const Point& rDestPt, const Size& rSize, |
| Bitmap& rBitmap ) const |
| { |
| DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); |
| |
| sal_Bool bOldMap = mbMap; |
| ((OutputDevice*)this)->mbMap = sal_False; |
| rBitmap = GetBitmap( rDestPt, rSize ); |
| ((OutputDevice*)this)->mbMap = bOldMap; |
| } |
| |
| // ------------------------------------------------------------------ |
| |
| Color OutputDevice::GetPixel( const Point& rPt ) const |
| { |
| DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); |
| |
| 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; |
| } |
| |
| // ------------------------------------------------------------------ |
| |
| Color* OutputDevice::GetPixel( const Polygon& rPts ) const |
| { |
| DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); |
| |
| Color* pColors = NULL; |
| const sal_uInt16 nSize = rPts.GetSize(); |
| |
| if( nSize ) |
| { |
| if ( mpGraphics || ((OutputDevice*)this)->ImplGetGraphics() ) |
| { |
| if ( mbInitClipRegion ) |
| ((OutputDevice*)this)->ImplInitClipRegion(); |
| |
| if ( !mbOutputClipped ) |
| { |
| pColors = new Color[ nSize ]; |
| |
| for( sal_uInt16 i = 0; i < nSize; i++ ) |
| { |
| Color& rCol = pColors[ i ]; |
| const Point& rPt = rPts[ i ]; |
| const SalColor aSalCol( mpGraphics->GetPixel( ImplLogicXToDevicePixel( rPt.X() ), |
| ImplLogicYToDevicePixel( rPt.Y() ) , this) ); |
| |
| rCol.SetRed( SALCOLOR_RED( aSalCol ) ); |
| rCol.SetGreen( SALCOLOR_GREEN( aSalCol ) ); |
| rCol.SetBlue( SALCOLOR_BLUE( aSalCol ) ); |
| } |
| } |
| } |
| } |
| |
| return pColors; |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| void OutputDevice::DrawPixel( const Point& rPt ) |
| { |
| DBG_TRACE( "OutputDevice::DrawPixel()" ); |
| DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); |
| |
| if ( mpMetaFile ) |
| mpMetaFile->AddAction( new MetaPointAction( rPt ) ); |
| |
| if ( !IsDeviceOutputNecessary() || !mbLineColor || ImplIsRecordLayout() ) |
| return; |
| |
| Point aPt = ImplLogicToDevicePixel( rPt ); |
| |
| // we need a graphics |
| 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 ) |
| { |
| DBG_TRACE( "OutputDevice::DrawPixel()" ); |
| DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); |
| |
| Color aColor( rColor ); |
| |
| if( mnDrawMode & ( DRAWMODE_BLACKLINE | DRAWMODE_WHITELINE | |
| DRAWMODE_GRAYLINE | DRAWMODE_GHOSTEDLINE | |
| DRAWMODE_SETTINGSLINE ) ) |
| { |
| if( !ImplIsColorTransparent( aColor ) ) |
| { |
| if( mnDrawMode & DRAWMODE_BLACKLINE ) |
| { |
| aColor = Color( COL_BLACK ); |
| } |
| else if( mnDrawMode & DRAWMODE_WHITELINE ) |
| { |
| aColor = Color( COL_WHITE ); |
| } |
| else if( mnDrawMode & DRAWMODE_GRAYLINE ) |
| { |
| const sal_uInt8 cLum = aColor.GetLuminance(); |
| aColor = Color( cLum, cLum, cLum ); |
| } |
| else if( mnDrawMode & DRAWMODE_SETTINGSLINE ) |
| { |
| aColor = GetSettings().GetStyleSettings().GetFontColor(); |
| } |
| |
| if( mnDrawMode & DRAWMODE_GHOSTEDLINE ) |
| { |
| aColor = Color( ( aColor.GetRed() >> 1 ) | 0x80, |
| ( aColor.GetGreen() >> 1 ) | 0x80, |
| ( aColor.GetBlue() >> 1 ) | 0x80 ); |
| } |
| } |
| } |
| |
| if ( mpMetaFile ) |
| mpMetaFile->AddAction( new MetaPixelAction( rPt, aColor ) ); |
| |
| if ( !IsDeviceOutputNecessary() || ImplIsColorTransparent( aColor ) || ImplIsRecordLayout() ) |
| return; |
| |
| Point aPt = ImplLogicToDevicePixel( rPt ); |
| |
| // we need a graphics |
| 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_TRACE( "OutputDevice::DrawPixel()" ); |
| DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); |
| 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; |
| |
| // we need a graphics |
| 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(); |
| Color* pColArray = new Color[ nSize ]; |
| |
| for( sal_uInt16 i = 0; i < nSize; i++ ) |
| pColArray[ i ] = rColor; |
| |
| DrawPixel( rPts, pColArray ); |
| delete[] pColArray; |
| } |
| |
| if( mpAlphaVDev ) |
| mpAlphaVDev->DrawPixel( rPts, rColor ); |
| } |
| |
| // ------------------------------------------------------------------------ |
| |
| namespace |
| { |
| sal_uInt8 lcl_calcColor( const sal_uInt8 nSourceColor, const sal_uInt8 nSourceOpaq, const sal_uInt8 nDestColor ) |
| { |
| int c = ( (int)nDestColor * ( 255 - nSourceOpaq ) ) |
| + (int)nSourceOpaq * (int)nSourceColor; |
| return sal_uInt8( c / 255 ); |
| } |
| } |
| |
| // ------------------------------------------------------------------------ |
| |
| 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,aSrcCol; |
| Bitmap res; |
| int nX, nOutX, nY, nOutY; |
| |
| OSL_ENSURE(mpAlphaVDev, |
| "ImplBlendWithAlpha(): call me only with valid alpha VDev!" ); |
| |
| sal_Bool bOldMapMode( mpAlphaVDev->IsMapModeEnabled() ); |
| mpAlphaVDev->EnableMapMode(sal_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 ) |
| { |
| for( nY = 0, nOutY = nOffY; nY < nDstHeight; nY++, nOutY++ ) |
| { |
| const long nMapY = pMapY[ nY ]; |
| const long nModY = ( nOutY & 0x0FL ) << 4L; |
| |
| for( nX = 0, nOutX = nOffX; nX < nDstWidth; nX++, nOutX++ ) |
| { |
| const long nMapX = pMapX[ nX ]; |
| const sal_uLong nD = nVCLDitherLut[ nModY | ( nOutX & 0x0FL ) ]; |
| |
| aSrcCol = pP->GetColor( nMapY, nMapX ); |
| aDstCol = pB->GetColor( nY, nX ); |
| const sal_uInt8 nSrcOpaq = 255 - pA->GetPixelIndex( nMapY, nMapX ); |
| const sal_uInt8 nDstOpaq = 255 - pAlphaW->GetPixelIndex( nY, nX ); |
| |
| aDstCol.SetRed( lcl_calcColor( aSrcCol.GetRed(), nSrcOpaq, aDstCol.GetRed() ) ); |
| aDstCol.SetBlue( lcl_calcColor( aSrcCol.GetBlue(), nSrcOpaq, aDstCol.GetBlue() ) ); |
| aDstCol.SetGreen( lcl_calcColor( aSrcCol.GetGreen(), nSrcOpaq, aDstCol.GetGreen() ) ); |
| |
| 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 ); |
| |
| // Have to perform the compositing 'algebra' in |
| // the inverse alpha space (with 255 meaning |
| // opaque), otherwise, transitivity is not |
| // achieved. |
| const sal_uInt8 nSrcAlpha = 255-COLOR_CHANNEL_MERGE( 255, (sal_uInt8)nDstOpaq, nSrcOpaq ); |
| |
| aIndex.SetIndex( (sal_uInt8) ( nVCLRLut[ ( nVCLLut[ nSrcAlpha ] + nD ) >> 16UL ] + |
| nVCLGLut[ ( nVCLLut[ nSrcAlpha ] + nD ) >> 16UL ] + |
| nVCLBLut[ ( nVCLLut[ nSrcAlpha ] + 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 ]; |
| |
| aSrcCol = pP->GetColor( nMapY, nMapX ); |
| aDstCol = pB->GetColor( nY, nX ); |
| const sal_uInt8 nSrcOpaq = 255 - pA->GetPixelIndex( nMapY, nMapX ); |
| const sal_uInt8 nDstOpaq = 255 - pAlphaW->GetPixelIndex( nY, nX ); |
| |
| aDstCol.SetRed( lcl_calcColor( aSrcCol.GetRed(), nSrcOpaq, aDstCol.GetRed() ) ); |
| aDstCol.SetBlue( lcl_calcColor( aSrcCol.GetBlue(), nSrcOpaq, aDstCol.GetBlue() ) ); |
| aDstCol.SetGreen( lcl_calcColor( aSrcCol.GetGreen(), nSrcOpaq, aDstCol.GetGreen() ) ); |
| |
| pB->SetPixel( nY, nX, aDstCol ); |
| |
| // Have to perform the compositing 'algebra' in |
| // the inverse alpha space (with 255 meaning |
| // opaque), otherwise, transitivity is not |
| // achieved. |
| const sal_uInt8 nSrcAlpha = 255-COLOR_CHANNEL_MERGE( 255, (sal_uInt8)nDstOpaq, nSrcOpaq ); |
| |
| pAlphaW->SetPixel( nY, nX, Color(nSrcAlpha, nSrcAlpha, nSrcAlpha) ); |
| } |
| } |
| } |
| |
| 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, nOutX, nY, nOutY; |
| |
| if( GetBitCount() <= 8 ) |
| { |
| Bitmap aDither( aBmp.GetSizePixel(), 8 ); |
| BitmapColor aIndex( 0 ); |
| BitmapReadAccess* pB = aBmp.AcquireReadAccess(); |
| BitmapWriteAccess* pW = aDither.AcquireWriteAccess(); |
| |
| if( pB && pP && pA && pW ) |
| { |
| for( nY = 0, nOutY = nOffY; nY < nDstHeight; nY++, nOutY++ ) |
| { |
| const long nMapY = pMapY[ nY ]; |
| const long nModY = ( nOutY & 0x0FL ) << 4L; |
| |
| 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::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 sal_Bool bHMirr = aOutSz.Width() < 0, bVMirr = aOutSz.Height() < 0; |
| |
| if( OUTDEV_WINDOW == meOutDevType ) |
| { |
| const Region aPaintRgn( ( (Window*) this )->GetPaintRegion() ); |
| |
| if( !aPaintRgn.IsNull() ) |
| aDstRect.Intersection( LogicToPixel( aPaintRgn.GetBoundRect() ) ); |
| } |
| |
| 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() ) |
| { |
| GDIMetaFile* pOldMetaFile = mpMetaFile; mpMetaFile = NULL; |
| const sal_Bool bOldMap = mbMap; mbMap = sal_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; |
| long* pMapX = new long[ nDstWidth ]; |
| 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,pMapY ); |
| } |
| else |
| { |
| aTmp = ImplBlend( |
| aBmp,pP,pA, |
| nOffY,nDstHeight, |
| nOffX,nDstWidth, |
| aBmpRect,aOutSz, |
| bHMirr,bVMirr, |
| pMapX,pMapY ); |
| } |
| |
| // #110958# Disable alpha VDev, we're doing the necessary |
| // stuff explicitely 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 ); |
| |
| delete[] pMapX; |
| delete[] pMapY; |
| 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; |
| long* pMapX = new long[ nSrcWidth + 1 ]; |
| long* pMapY = new long[ nSrcHeight + 1 ]; |
| const sal_Bool bOldMap = mbMap; |
| |
| mbMap = sal_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); |
| } |
| |
| //Region aWorkRgn( aMask.CreateRegion( COL_BLACK, Rectangle( Point(), aMask.GetSizePixel() ) ) ); |
| //ImplRegionInfo aInfo; |
| //sal_Bool bRgnRect = aWorkRgn.ImplGetFirstRect( aInfo, nWorkX, nWorkY, nWorkWidth, nWorkHeight ); |
| // |
| //while( bRgnRect ) |
| //{ |
| // Bitmap aBandBmp( aPaint ); |
| // const Rectangle aBandRect( Point( nWorkX, nWorkY ), Size( nWorkWidth, nWorkHeight ) ); |
| // const Point aMapPt( pMapX[ nWorkX ], pMapY[ nWorkY ] ); |
| // const Size aMapSz( pMapX[ nWorkX + nWorkWidth ] - aMapPt.X(), pMapY[ nWorkY + nWorkHeight ] - aMapPt.Y() ); |
| // |
| // aBandBmp.Crop( aBandRect ); |
| // ImplDrawBitmap( aMapPt, aMapSz, Point(), aBandBmp.GetSizePixel(), aBandBmp, META_BMPSCALEPART_ACTION ); |
| // bRgnRect = aWorkRgn.ImplGetNextRect( aInfo, nWorkX, nWorkY, nWorkWidth, nWorkHeight ); |
| //} |
| |
| mbMap = bOldMap; |
| |
| delete[] pMapX; |
| delete[] pMapY; |
| } |
| } |
| |
| // ------------------------------------------------------------------------ |
| |
| void OutputDevice::ImplPrintMask( const Bitmap& rMask, const Color& rMaskColor, |
| 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( !rMask.IsEmpty() && aSrcRect.GetWidth() && aSrcRect.GetHeight() && aDestSz.Width() && aDestSz.Height() ) |
| { |
| Bitmap 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, aMask.GetSizePixel() ) ) |
| aMask.Crop( aSrcRect ); |
| |
| // destination mirrored |
| if( nMirrFlags ) |
| aMask.Mirror( nMirrFlags ); |
| |
| // do painting |
| const long nSrcWidth = aSrcRect.GetWidth(), nSrcHeight = aSrcRect.GetHeight(); |
| long nX, nY; //, nWorkX, nWorkY, nWorkWidth, nWorkHeight; |
| long* pMapX = new long[ nSrcWidth + 1 ]; |
| long* pMapY = new long[ nSrcHeight + 1 ]; |
| GDIMetaFile* pOldMetaFile = mpMetaFile; |
| const sal_Bool bOldMap = mbMap; |
| |
| mpMetaFile = NULL; |
| mbMap = sal_False; |
| Push( PUSH_FILLCOLOR | PUSH_LINECOLOR ); |
| SetLineColor( rMaskColor ); |
| SetFillColor( rMaskColor ); |
| ImplInitLineColor(); |
| ImplInitFillColor(); |
| |
| // 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 |
| |
| DrawRect(Rectangle(aMapPt, aMapSz)); |
| } |
| |
| //Region aWorkRgn( aMask.CreateRegion( COL_BLACK, Rectangle( Point(), aMask.GetSizePixel() ) ) ); |
| //ImplRegionInfo aInfo; |
| //sal_Bool bRgnRect = aWorkRgn.ImplGetFirstRect( aInfo, nWorkX, nWorkY, nWorkWidth, nWorkHeight ); |
| // |
| //while( bRgnRect ) |
| //{ |
| // const Point aMapPt( pMapX[ nWorkX ], pMapY[ nWorkY ] ); |
| // const Size aMapSz( pMapX[ nWorkX + nWorkWidth ] - aMapPt.X(), pMapY[ nWorkY + nWorkHeight ] - aMapPt.Y() ); |
| // |
| // DrawRect( Rectangle( aMapPt, aMapSz ) ); |
| // bRgnRect = aWorkRgn.ImplGetNextRect( aInfo, nWorkX, nWorkY, nWorkWidth, nWorkHeight ); |
| //} |
| |
| Pop(); |
| delete[] pMapX; |
| delete[] pMapY; |
| mbMap = bOldMap; |
| mpMetaFile = pOldMetaFile; |
| } |
| } |