1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
22 #include <sal/types.h>
24 #include <basegfx/matrix/b2dhommatrixtools.hxx>
25 #include <boost/scoped_array.hpp>
27 #include <vcl/bmpacc.hxx>
28 #include <vcl/outdev.hxx>
29 #include <vcl/settings.hxx>
30 #include <vcl/virdev.hxx>
31 #include <vcl/window.hxx>
33 #include "outdata.hxx"
39 * Perform a safe approximation of a polygon from double-precision
40 * coordinates to integer coordinates, to ensure that it has at least 2
41 * pixels in both X and Y directions.
43 Polygon
toPolygon( const basegfx::B2DPolygon
& rPoly
)
45 basegfx::B2DRange aRange
= rPoly
.getB2DRange();
46 double fW
= aRange
.getWidth(), fH
= aRange
.getHeight();
47 if (0.0 < fW
&& 0.0 < fH
&& (fW
<= 1.0 || fH
<= 1.0))
49 // This polygon not empty but is too small to display. Approximate it
50 // with a rectangle large enough to be displayed.
51 double nX
= aRange
.getMinX(), nY
= aRange
.getMinY();
52 double nW
= std::max
<double>(1.0, rtl::math::round(fW
));
53 double nH
= std::max
<double>(1.0, rtl::math::round(fH
));
56 aTarget
.Insert(0, Point(nX
, nY
));
57 aTarget
.Insert(1, Point(nX
+nW
, nY
));
58 aTarget
.Insert(2, Point(nX
+nW
, nY
+nH
));
59 aTarget
.Insert(3, Point(nX
, nY
+nH
));
60 aTarget
.Insert(4, Point(nX
, nY
));
63 return Polygon(rPoly
);
66 tools::PolyPolygon
toPolyPolygon( const basegfx::B2DPolyPolygon
& rPolyPoly
)
68 tools::PolyPolygon aTarget
;
69 for (sal_uInt32 i
= 0; i
< rPolyPoly
.count(); ++i
)
70 aTarget
.Insert(toPolygon(rPolyPoly
.getB2DPolygon(i
)));
76 Color
OutputDevice::ImplDrawModeToColor( const Color
& rColor
) const
78 Color
aColor( rColor
);
79 DrawModeFlags nDrawMode
= GetDrawMode();
81 if( nDrawMode
& ( DrawModeFlags::BlackLine
| DrawModeFlags::WhiteLine
|
82 DrawModeFlags::GrayLine
| DrawModeFlags::GhostedLine
|
83 DrawModeFlags::SettingsLine
) )
85 if( !ImplIsColorTransparent( aColor
) )
87 if( nDrawMode
& DrawModeFlags::BlackLine
)
89 aColor
= Color( COL_BLACK
);
91 else if( nDrawMode
& DrawModeFlags::WhiteLine
)
93 aColor
= Color( COL_WHITE
);
95 else if( nDrawMode
& DrawModeFlags::GrayLine
)
97 const sal_uInt8 cLum
= aColor
.GetLuminance();
98 aColor
= Color( cLum
, cLum
, cLum
);
100 else if( nDrawMode
& DrawModeFlags::SettingsLine
)
102 aColor
= GetSettings().GetStyleSettings().GetFontColor();
105 if( nDrawMode
& DrawModeFlags::GhostedLine
)
107 aColor
= Color( ( aColor
.GetRed() >> 1 ) | 0x80,
108 ( aColor
.GetGreen() >> 1 ) | 0x80,
109 ( aColor
.GetBlue() >> 1 ) | 0x80);
116 sal_uInt16
OutputDevice::GetAlphaBitCount() const
121 bool OutputDevice::HasAlpha()
123 return mpAlphaVDev
!= nullptr;
126 void OutputDevice::ImplPrintTransparent( const Bitmap
& rBmp
, const Bitmap
& rMask
,
127 const Point
& rDestPt
, const Size
& rDestSize
,
128 const Point
& rSrcPtPixel
, const Size
& rSrcSizePixel
)
131 Point
aDestPt( LogicToPixel( rDestPt
) );
132 Size
aDestSz( LogicToPixel( rDestSize
) );
133 Rectangle
aSrcRect( rSrcPtPixel
, rSrcSizePixel
);
137 if( !rBmp
.IsEmpty() && aSrcRect
.GetWidth() && aSrcRect
.GetHeight() && aDestSz
.Width() && aDestSz
.Height() )
139 Bitmap
aPaint( rBmp
), aMask( rMask
);
140 BmpMirrorFlags nMirrFlags
= BmpMirrorFlags::NONE
;
142 if( aMask
.GetBitCount() > 1 )
143 aMask
.Convert( BMP_CONVERSION_1BIT_THRESHOLD
);
145 // mirrored horizontically
146 if( aDestSz
.Width() < 0L )
148 aDestSz
.Width() = -aDestSz
.Width();
149 aDestPt
.X() -= ( aDestSz
.Width() - 1L );
150 nMirrFlags
|= BmpMirrorFlags::Horizontal
;
153 // mirrored vertically
154 if( aDestSz
.Height() < 0L )
156 aDestSz
.Height() = -aDestSz
.Height();
157 aDestPt
.Y() -= ( aDestSz
.Height() - 1L );
158 nMirrFlags
|= BmpMirrorFlags::Vertical
;
162 if( aSrcRect
!= Rectangle( aPt
, aPaint
.GetSizePixel() ) )
164 aPaint
.Crop( aSrcRect
);
165 aMask
.Crop( aSrcRect
);
168 // destination mirrored
169 if( nMirrFlags
!= BmpMirrorFlags::NONE
)
171 aPaint
.Mirror( nMirrFlags
);
172 aMask
.Mirror( nMirrFlags
);
175 // we always want to have a mask
176 if( aMask
.IsEmpty() )
178 aMask
= Bitmap( aSrcRect
.GetSize(), 1 );
179 aMask
.Erase( Color( COL_BLACK
) );
183 const long nSrcWidth
= aSrcRect
.GetWidth(), nSrcHeight
= aSrcRect
.GetHeight();
184 long nX
, nY
; // , nWorkX, nWorkY, nWorkWidth, nWorkHeight;
185 boost::scoped_array
<long> pMapX(new long[ nSrcWidth
+ 1 ]);
186 boost::scoped_array
<long> pMapY(new long[ nSrcHeight
+ 1 ]);
187 const bool bOldMap
= mbMap
;
191 // create forward mapping tables
192 for( nX
= 0L; nX
<= nSrcWidth
; nX
++ )
193 pMapX
[ nX
] = aDestPt
.X() + FRound( (double) aDestSz
.Width() * nX
/ nSrcWidth
);
195 for( nY
= 0L; nY
<= nSrcHeight
; nY
++ )
196 pMapY
[ nY
] = aDestPt
.Y() + FRound( (double) aDestSz
.Height() * nY
/ nSrcHeight
);
198 // walk through all rectangles of mask
199 const vcl::Region
aWorkRgn(aMask
.CreateRegion(COL_BLACK
, Rectangle(Point(), aMask
.GetSizePixel())));
200 RectangleVector aRectangles
;
201 aWorkRgn
.GetRegionRectangles(aRectangles
);
203 for(RectangleVector::const_iterator
aRectIter(aRectangles
.begin()); aRectIter
!= aRectangles
.end(); ++aRectIter
)
205 const Point
aMapPt(pMapX
[aRectIter
->Left()], pMapY
[aRectIter
->Top()]);
206 const Size
aMapSz( pMapX
[aRectIter
->Right() + 1] - aMapPt
.X(), // pMapX[L + W] -> L + ((R - L) + 1) -> R + 1
207 pMapY
[aRectIter
->Bottom() + 1] - aMapPt
.Y()); // same for Y
208 Bitmap
aBandBmp(aPaint
);
210 aBandBmp
.Crop(*aRectIter
);
211 DrawBitmap(aMapPt
, aMapSz
, Point(), aBandBmp
.GetSizePixel(), aBandBmp
, MetaActionType::BMPSCALEPART
);
218 // Caution: This method is nearly the same as
219 // void OutputDevice::DrawPolyPolygon( const basegfx::B2DPolyPolygon& rB2DPolyPoly )
220 // so when changes are made here do not forget to make changes there, too
222 void OutputDevice::DrawTransparent( const basegfx::B2DPolyPolygon
& rB2DPolyPoly
, double fTransparency
)
224 assert(!is_double_buffered_window());
226 // AW: Do NOT paint empty PolyPolygons
227 if(!rB2DPolyPoly
.count())
230 // we need a graphics
231 if( !mpGraphics
&& !AcquireGraphics() )
234 if( mbInitClipRegion
)
237 if( mbOutputClipped
)
240 if( mbInitLineColor
)
243 if( mbInitFillColor
)
246 if((mnAntialiasing
& AntialiasingFlags::EnableB2dDraw
) &&
247 mpGraphics
->supportsOperation(OutDevSupport_B2DDraw
) &&
248 (ROP_OVERPAINT
== GetRasterOp()) )
250 // b2dpolygon support not implemented yet on non-UNX platforms
251 const ::basegfx::B2DHomMatrix aTransform
= ImplGetDeviceTransformation();
252 basegfx::B2DPolyPolygon
aB2DPolyPolygon(rB2DPolyPoly
);
254 // transform the polygon into device space and ensure it is closed
255 aB2DPolyPolygon
.transform( aTransform
);
256 aB2DPolyPolygon
.setClosed( true );
258 bool bDrawnOk
= true;
260 bDrawnOk
= mpGraphics
->DrawPolyPolygon( aB2DPolyPolygon
, fTransparency
, this );
262 if( bDrawnOk
&& IsLineColor() )
264 const basegfx::B2DVector
aHairlineWidth(1,1);
265 const int nPolyCount
= aB2DPolyPolygon
.count();
266 for( int nPolyIdx
= 0; nPolyIdx
< nPolyCount
; ++nPolyIdx
)
268 const ::basegfx::B2DPolygon aOnePoly
= aB2DPolyPolygon
.getB2DPolygon( nPolyIdx
);
269 mpGraphics
->DrawPolyLine( aOnePoly
, fTransparency
, aHairlineWidth
, ::basegfx::B2DLINEJOIN_NONE
, com::sun::star::drawing::LineCap_BUTT
, this );
276 mpMetaFile
->AddAction( new MetaTransparentAction( tools::PolyPolygon( rB2DPolyPoly
), static_cast< sal_uInt16
>(fTransparency
* 100.0)));
282 // fallback to old polygon drawing if needed
283 DrawTransparent(toPolyPolygon(rB2DPolyPoly
), static_cast<sal_uInt16
>(fTransparency
* 100.0));
286 void OutputDevice::DrawInvisiblePolygon( const tools::PolyPolygon
& rPolyPoly
)
288 assert(!is_double_buffered_window());
290 // short circuit if the polygon border is invisible too
294 // we assume that the border is NOT to be drawn transparently???
295 Push( PushFlags::FILLCOLOR
);
297 DrawPolyPolygon( rPolyPoly
);
301 bool OutputDevice::DrawTransparentNatively ( const tools::PolyPolygon
& rPolyPoly
,
302 sal_uInt16 nTransparencePercent
)
304 assert(!is_double_buffered_window());
309 static const char* pDisableNative
= getenv( "SAL_DISABLE_NATIVE_ALPHA");
311 if( !pDisableNative
&&
312 mpGraphics
->supportsOperation( OutDevSupport_B2DDraw
)
313 #if defined UNX && ! defined MACOSX && ! defined IOS
317 // workaround bad dithering on remote displaying when using GDI+ with toolbar button highlighting
318 && !rPolyPoly
.IsRect()
322 // prepare the graphics device
323 if( mbInitClipRegion
)
326 if( mbOutputClipped
)
329 if( mbInitLineColor
)
332 if( mbInitFillColor
)
335 // get the polygon in device coordinates
336 basegfx::B2DPolyPolygon
aB2DPolyPolygon( rPolyPoly
.getB2DPolyPolygon() );
337 const ::basegfx::B2DHomMatrix aTransform
= ImplGetDeviceTransformation();
338 aB2DPolyPolygon
.transform( aTransform
);
340 const double fTransparency
= 0.01 * nTransparencePercent
;
344 // CAUTION: Only non printing (pixel-renderer) VCL commands from OutputDevices
345 // should be used when printing. Normally this is avoided by the printer being
346 // non-AAed and thus e.g. on WIN GdiPlus calls are not used. It may be necessary
347 // to figure out a way of moving this code to it's own function that is
348 // overriden by the Print class, which will mean we deliberately override the
349 // functionality and we use the fallback some lines below (which is not very good,
350 // though. For now, WinSalGraphics::drawPolyPolygon will detect printer usage and
351 // correct the wrong mapping (see there for details)
352 bDrawn
= mpGraphics
->DrawPolyPolygon( aB2DPolyPolygon
, fTransparency
, this );
357 // disable the fill color for now
358 mpGraphics
->SetFillColor();
359 // draw the border line
360 const basegfx::B2DVector
aLineWidths( 1, 1 );
361 const int nPolyCount
= aB2DPolyPolygon
.count();
362 for( int nPolyIdx
= 0; nPolyIdx
< nPolyCount
; ++nPolyIdx
)
364 const ::basegfx::B2DPolygon
& rPolygon
= aB2DPolyPolygon
.getB2DPolygon( nPolyIdx
);
365 bDrawn
= mpGraphics
->DrawPolyLine( rPolygon
, fTransparency
, aLineWidths
,
366 ::basegfx::B2DLINEJOIN_NONE
, css::drawing::LineCap_BUTT
, this );
368 // prepare to restore the fill color
369 mbInitFillColor
= mbFillColor
;
376 void OutputDevice::EmulateDrawTransparent ( const tools::PolyPolygon
& rPolyPoly
,
377 sal_uInt16 nTransparencePercent
)
380 static const char* pDisableNative
= getenv( "SAL_DISABLE_NATIVE_ALPHA" );
382 // #110958# Disable alpha VDev, we perform the necessary
383 VirtualDevice
* pOldAlphaVDev
= mpAlphaVDev
;
385 // operation explicitly further below.
389 GDIMetaFile
* pOldMetaFile
= mpMetaFile
;
392 tools::PolyPolygon
aPolyPoly( LogicToPixel( rPolyPoly
) );
393 Rectangle
aPolyRect( aPolyPoly
.GetBoundRect() );
395 Rectangle
aDstRect( aPoint
, GetOutputSizePixel() );
397 aDstRect
.Intersection( aPolyRect
);
399 ClipToPaintRegion( aDstRect
);
401 if( !aDstRect
.IsEmpty() )
405 // #i66849# Added fast path for exactly rectangular
407 // #i83087# Naturally, system alpha blending cannot
408 // work with separate alpha VDev
409 if( !mpAlphaVDev
&& !pDisableNative
&& aPolyPoly
.IsRect() )
411 // setup Graphics only here (other cases delegate
412 // to basic OutDev methods)
413 if ( mbInitClipRegion
)
416 if ( mbInitLineColor
)
419 if ( mbInitFillColor
)
422 Rectangle
aLogicPolyRect( rPolyPoly
.GetBoundRect() );
423 Rectangle
aPixelRect( ImplLogicToDevicePixel( aLogicPolyRect
) );
425 if( !mbOutputClipped
)
427 bDrawn
= mpGraphics
->DrawAlphaRect( aPixelRect
.Left(), aPixelRect
.Top(),
428 // #i98405# use methods with small g, else one pixel too much will be painted.
429 // This is because the source is a polygon which when painted would not paint
430 // the rightmost and lowest pixel line(s), so use one pixel less for the
432 aPixelRect
.getWidth(), aPixelRect
.getHeight(),
433 sal::static_int_cast
<sal_uInt8
>(nTransparencePercent
),
444 ScopedVclPtrInstance
< VirtualDevice
> aVDev( *this, 1 );
445 const Size
aDstSz( aDstRect
.GetSize() );
446 const sal_uInt8 cTrans
= (sal_uInt8
) MinMax( FRound( nTransparencePercent
* 2.55 ), 0, 255 );
448 if( aDstRect
.Left() || aDstRect
.Top() )
449 aPolyPoly
.Move( -aDstRect
.Left(), -aDstRect
.Top() );
451 if( aVDev
->SetOutputSizePixel( aDstSz
) )
453 const bool bOldMap
= mbMap
;
455 EnableMapMode( false );
457 aVDev
->SetLineColor( COL_BLACK
);
458 aVDev
->SetFillColor( COL_BLACK
);
459 aVDev
->DrawPolyPolygon( aPolyPoly
);
461 Bitmap
aPaint( GetBitmap( aDstRect
.TopLeft(), aDstSz
) );
462 Bitmap
aPolyMask( aVDev
->GetBitmap( Point(), aDstSz
) );
464 // #107766# check for non-empty bitmaps before accessing them
465 if( !!aPaint
&& !!aPolyMask
)
467 BitmapWriteAccess
* pW
= aPaint
.AcquireWriteAccess();
468 BitmapReadAccess
* pR
= aPolyMask
.AcquireReadAccess();
473 const BitmapColor
aFillCol( GetFillColor() );
474 const BitmapColor
aWhite( pR
->GetBestMatchingColor( Color( COL_WHITE
) ) );
475 const BitmapColor
aBlack( pR
->GetBestMatchingColor( Color( COL_BLACK
) ) );
476 const long nWidth
= pW
->Width();
477 const long nHeight
= pW
->Height();
478 const long nR
= aFillCol
.GetRed();
479 const long nG
= aFillCol
.GetGreen();
480 const long nB
= aFillCol
.GetBlue();
483 if( aPaint
.GetBitCount() <= 8 )
485 const BitmapPalette
& rPal
= pW
->GetPalette();
486 const sal_uInt16 nCount
= rPal
.GetEntryCount();
487 BitmapColor
* pMap
= reinterpret_cast<BitmapColor
*>(new sal_uInt8
[ nCount
* sizeof( BitmapColor
) ]);
489 for( sal_uInt16 i
= 0; i
< nCount
; i
++ )
491 BitmapColor
aCol( rPal
[ i
] );
492 pMap
[ i
] = BitmapColor( (sal_uInt8
) rPal
.GetBestIndex( aCol
.Merge( aFillCol
, cTrans
) ) );
495 if( pR
->GetScanlineFormat() == BMP_FORMAT_1BIT_MSB_PAL
&&
496 pW
->GetScanlineFormat() == BMP_FORMAT_8BIT_PAL
)
498 const sal_uInt8 cBlack
= aBlack
.GetIndex();
500 for( nY
= 0; nY
< nHeight
; nY
++ )
502 Scanline pWScan
= pW
->GetScanline( nY
);
503 Scanline pRScan
= pR
->GetScanline( nY
);
504 sal_uInt8 cBit
= 128;
506 for( nX
= 0; nX
< nWidth
; nX
++, cBit
>>= 1, pWScan
++ )
513 if( ( *pRScan
& cBit
) == cBlack
)
515 *pWScan
= (sal_uInt8
) pMap
[ *pWScan
].GetIndex();
522 for( nY
= 0; nY
< nHeight
; nY
++ )
524 for( nX
= 0; nX
< nWidth
; nX
++ )
526 if( pR
->GetPixel( nY
, nX
) == aBlack
)
528 pW
->SetPixel( nY
, nX
, pMap
[ pW
->GetPixel( nY
, nX
).GetIndex() ] );
533 delete[] reinterpret_cast<sal_uInt8
*>(pMap
);
537 if( pR
->GetScanlineFormat() == BMP_FORMAT_1BIT_MSB_PAL
&&
538 pW
->GetScanlineFormat() == BMP_FORMAT_24BIT_TC_BGR
)
540 const sal_uInt8 cBlack
= aBlack
.GetIndex();
542 for( nY
= 0; nY
< nHeight
; nY
++ )
544 Scanline pWScan
= pW
->GetScanline( nY
);
545 Scanline pRScan
= pR
->GetScanline( nY
);
546 sal_uInt8 cBit
= 128;
548 for( nX
= 0; nX
< nWidth
; nX
++, cBit
>>= 1, pWScan
+= 3 )
555 if( ( *pRScan
& cBit
) == cBlack
)
557 pWScan
[ 0 ] = COLOR_CHANNEL_MERGE( pWScan
[ 0 ], nB
, cTrans
);
558 pWScan
[ 1 ] = COLOR_CHANNEL_MERGE( pWScan
[ 1 ], nG
, cTrans
);
559 pWScan
[ 2 ] = COLOR_CHANNEL_MERGE( pWScan
[ 2 ], nR
, cTrans
);
566 for( nY
= 0; nY
< nHeight
; nY
++ )
568 for( nX
= 0; nX
< nWidth
; nX
++ )
570 if( pR
->GetPixel( nY
, nX
) == aBlack
)
572 aPixCol
= pW
->GetColor( nY
, nX
);
573 pW
->SetPixel( nY
, nX
, aPixCol
.Merge( aFillCol
, cTrans
) );
581 Bitmap::ReleaseAccess( pR
);
582 Bitmap::ReleaseAccess( pW
);
584 DrawBitmap( aDstRect
.TopLeft(), aPaint
);
586 EnableMapMode( bOldMap
);
590 Push( PushFlags::FILLCOLOR
);
592 DrawPolyPolygon( rPolyPoly
);
599 DrawPolyPolygon( rPolyPoly
);
604 mpMetaFile
= pOldMetaFile
;
606 // #110958# Restore disabled alpha VDev
607 mpAlphaVDev
= pOldAlphaVDev
;
610 void OutputDevice::DrawTransparent( const tools::PolyPolygon
& rPolyPoly
,
611 sal_uInt16 nTransparencePercent
)
613 assert(!is_double_buffered_window());
615 // short circuit for drawing an opaque polygon
616 if( (nTransparencePercent
< 1) || (mnDrawMode
& DrawModeFlags::NoTransparency
) )
618 DrawPolyPolygon( rPolyPoly
);
622 // short circuit for drawing an invisible polygon
623 if( !mbFillColor
|| (nTransparencePercent
>= 100) )
625 DrawInvisiblePolygon( rPolyPoly
);
626 return; // tdf#84294: do not record it in metafile
629 // handle metafile recording
631 mpMetaFile
->AddAction( new MetaTransparentAction( rPolyPoly
, nTransparencePercent
) );
633 bool bDrawn
= !IsDeviceOutputNecessary() || ImplIsRecordLayout();
637 // get the device graphics as drawing target
638 if( !mpGraphics
&& !AcquireGraphics() )
641 // try hard to draw it directly, because the emulation layers are slower
642 bDrawn
= DrawTransparentNatively( rPolyPoly
, nTransparencePercent
);
646 EmulateDrawTransparent( rPolyPoly
, nTransparencePercent
);
648 // #110958# Apply alpha value also to VDev alpha channel
651 const Color
aFillCol( mpAlphaVDev
->GetFillColor() );
652 mpAlphaVDev
->SetFillColor( Color(sal::static_int_cast
<sal_uInt8
>(255*nTransparencePercent
/100),
653 sal::static_int_cast
<sal_uInt8
>(255*nTransparencePercent
/100),
654 sal::static_int_cast
<sal_uInt8
>(255*nTransparencePercent
/100)) );
656 mpAlphaVDev
->DrawTransparent( rPolyPoly
, nTransparencePercent
);
658 mpAlphaVDev
->SetFillColor( aFillCol
);
662 void OutputDevice::DrawTransparent( const GDIMetaFile
& rMtf
, const Point
& rPos
,
663 const Size
& rSize
, const Gradient
& rTransparenceGradient
)
665 assert(!is_double_buffered_window());
667 const Color
aBlack( COL_BLACK
);
671 // missing here is to map the data using the DeviceTransformation
672 mpMetaFile
->AddAction( new MetaFloatTransparentAction( rMtf
, rPos
, rSize
, rTransparenceGradient
) );
675 if ( !IsDeviceOutputNecessary() )
678 if( ( rTransparenceGradient
.GetStartColor() == aBlack
&& rTransparenceGradient
.GetEndColor() == aBlack
) ||
679 ( mnDrawMode
& ( DrawModeFlags::NoTransparency
) ) )
681 ( (GDIMetaFile
&) rMtf
).WindStart();
682 ( (GDIMetaFile
&) rMtf
).Play( this, rPos
, rSize
);
683 ( (GDIMetaFile
&) rMtf
).WindStart();
687 GDIMetaFile
* pOldMetaFile
= mpMetaFile
;
688 Rectangle
aOutRect( LogicToPixel( rPos
), LogicToPixel( rSize
) );
690 Rectangle
aDstRect( aPoint
, GetOutputSizePixel() );
693 aDstRect
.Intersection( aOutRect
);
695 ClipToPaintRegion( aDstRect
);
697 if( !aDstRect
.IsEmpty() )
699 ScopedVclPtrInstance
< VirtualDevice
> xVDev
;
701 ((OutputDevice
*)xVDev
.get())->mnDPIX
= mnDPIX
;
702 ((OutputDevice
*)xVDev
.get())->mnDPIY
= mnDPIY
;
704 if( xVDev
->SetOutputSizePixel( aDstRect
.GetSize() ) )
706 if(GetAntialiasing() != AntialiasingFlags::NONE
)
709 // For MetaFile replay (see task) it may now be necessary to take
710 // into account that the content is AntiAlialised and needs to be masked
711 // like that. Instead of masking, i will use a copy-modify-paste cycle
712 // here (as i already use in the VclPrimiziveRenderer with successs)
713 xVDev
->SetAntialiasing(GetAntialiasing());
715 // create MapMode for buffer (offset needed) and set
716 MapMode
aMap(GetMapMode());
717 const Point
aOutPos(PixelToLogic(aDstRect
.TopLeft()));
718 aMap
.SetOrigin(Point(-aOutPos
.X(), -aOutPos
.Y()));
719 xVDev
->SetMapMode(aMap
);
721 // copy MapMode state and disable for target
722 const bool bOrigMapModeEnabled(IsMapModeEnabled());
723 EnableMapMode(false);
725 // copy MapMode state and disable for buffer
726 const bool bBufferMapModeEnabled(xVDev
->IsMapModeEnabled());
727 xVDev
->EnableMapMode(false);
729 // copy content from original to buffer
730 xVDev
->DrawOutDev( aPoint
, xVDev
->GetOutputSizePixel(), // dest
731 aDstRect
.TopLeft(), xVDev
->GetOutputSizePixel(), // source
734 // draw MetaFile to buffer
735 xVDev
->EnableMapMode(bBufferMapModeEnabled
);
736 ((GDIMetaFile
&)rMtf
).WindStart();
737 ((GDIMetaFile
&)rMtf
).Play(xVDev
.get(), rPos
, rSize
);
738 ((GDIMetaFile
&)rMtf
).WindStart();
740 // get content bitmap from buffer
741 xVDev
->EnableMapMode(false);
743 const Bitmap
aPaint(xVDev
->GetBitmap(aPoint
, xVDev
->GetOutputSizePixel()));
745 // create alpha mask from gradient and get as Bitmap
746 xVDev
->EnableMapMode(bBufferMapModeEnabled
);
747 xVDev
->SetDrawMode(DrawModeFlags::GrayGradient
);
748 xVDev
->DrawGradient(Rectangle(rPos
, rSize
), rTransparenceGradient
);
749 xVDev
->SetDrawMode(DrawModeFlags::Default
);
750 xVDev
->EnableMapMode(false);
752 const AlphaMask
aAlpha(xVDev
->GetBitmap(aPoint
, xVDev
->GetOutputSizePixel()));
754 xVDev
.disposeAndClear();
756 // draw masked content to target and restore MapMode
757 DrawBitmapEx(aDstRect
.TopLeft(), BitmapEx(aPaint
, aAlpha
));
758 EnableMapMode(bOrigMapModeEnabled
);
762 Bitmap aPaint
, aMask
;
764 MapMode
aMap( GetMapMode() );
765 Point
aOutPos( PixelToLogic( aDstRect
.TopLeft() ) );
766 const bool bOldMap
= mbMap
;
768 aMap
.SetOrigin( Point( -aOutPos
.X(), -aOutPos
.Y() ) );
769 xVDev
->SetMapMode( aMap
);
770 const bool bVDevOldMap
= xVDev
->IsMapModeEnabled();
772 // create paint bitmap
773 ( (GDIMetaFile
&) rMtf
).WindStart();
774 ( (GDIMetaFile
&) rMtf
).Play( xVDev
.get(), rPos
, rSize
);
775 ( (GDIMetaFile
&) rMtf
).WindStart();
776 xVDev
->EnableMapMode( false );
777 aPaint
= xVDev
->GetBitmap( Point(), xVDev
->GetOutputSizePixel() );
778 xVDev
->EnableMapMode( bVDevOldMap
); // #i35331#: MUST NOT use EnableMapMode( sal_True ) here!
780 // create mask bitmap
781 xVDev
->SetLineColor( COL_BLACK
);
782 xVDev
->SetFillColor( COL_BLACK
);
783 xVDev
->DrawRect( Rectangle( xVDev
->PixelToLogic( Point() ), xVDev
->GetOutputSize() ) );
784 xVDev
->SetDrawMode( DrawModeFlags::WhiteLine
| DrawModeFlags::WhiteFill
| DrawModeFlags::WhiteText
|
785 DrawModeFlags::WhiteBitmap
| DrawModeFlags::WhiteGradient
);
786 ( (GDIMetaFile
&) rMtf
).WindStart();
787 ( (GDIMetaFile
&) rMtf
).Play( xVDev
.get(), rPos
, rSize
);
788 ( (GDIMetaFile
&) rMtf
).WindStart();
789 xVDev
->EnableMapMode( false );
790 aMask
= xVDev
->GetBitmap( Point(), xVDev
->GetOutputSizePixel() );
791 xVDev
->EnableMapMode( bVDevOldMap
); // #i35331#: MUST NOT use EnableMapMode( sal_True ) here!
793 // create alpha mask from gradient
794 xVDev
->SetDrawMode( DrawModeFlags::GrayGradient
);
795 xVDev
->DrawGradient( Rectangle( rPos
, rSize
), rTransparenceGradient
);
796 xVDev
->SetDrawMode( DrawModeFlags::Default
);
797 xVDev
->EnableMapMode( false );
798 xVDev
->DrawMask( Point(), xVDev
->GetOutputSizePixel(), aMask
, Color( COL_WHITE
) );
800 aAlpha
= xVDev
->GetBitmap( Point(), xVDev
->GetOutputSizePixel() );
802 xVDev
.disposeAndClear();
804 EnableMapMode( false );
805 DrawBitmapEx( aDstRect
.TopLeft(), BitmapEx( aPaint
, aAlpha
) );
806 EnableMapMode( bOldMap
);
811 mpMetaFile
= pOldMetaFile
;
815 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */