blob: acb73d6f709375aa7ffd98fb9458d5b95a552a95 [file] [log] [blame]
/**************************************************************
*
* 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;
}
}