1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <cassert>
21 
22 #include <sal/types.h>
23 #include <tools/helpers.hxx>
24 #include <rtl/math.hxx>
25 
26 #include <memory>
27 
28 #include <vcl/gdimtf.hxx>
29 #include <vcl/metaact.hxx>
30 #include <vcl/outdev.hxx>
31 #include <vcl/settings.hxx>
32 #include <vcl/virdev.hxx>
33 
34 #include <salgdi.hxx>
35 #include <bitmap/BitmapWriteAccess.hxx>
36 
37 namespace
38 {
39     /**
40      * Perform a safe approximation of a polygon from double-precision
41      * coordinates to integer coordinates, to ensure that it has at least 2
42      * pixels in both X and Y directions.
43      */
toPolygon(const basegfx::B2DPolygon & rPoly)44     tools::Polygon toPolygon( const basegfx::B2DPolygon& rPoly )
45     {
46         basegfx::B2DRange aRange = rPoly.getB2DRange();
47         double fW = aRange.getWidth(), fH = aRange.getHeight();
48         if (0.0 < fW && 0.0 < fH && (fW <= 1.0 || fH <= 1.0))
49         {
50             // This polygon not empty but is too small to display.  Approximate it
51             // with a rectangle large enough to be displayed.
52             double nX = aRange.getMinX(), nY = aRange.getMinY();
53             double nW = std::max<double>(1.0, rtl::math::round(fW));
54             double nH = std::max<double>(1.0, rtl::math::round(fH));
55 
56             tools::Polygon aTarget;
57             aTarget.Insert(0, Point(nX, nY));
58             aTarget.Insert(1, Point(nX+nW, nY));
59             aTarget.Insert(2, Point(nX+nW, nY+nH));
60             aTarget.Insert(3, Point(nX, nY+nH));
61             aTarget.Insert(4, Point(nX, nY));
62             return aTarget;
63         }
64         return tools::Polygon(rPoly);
65     }
66 
toPolyPolygon(const basegfx::B2DPolyPolygon & rPolyPoly)67     tools::PolyPolygon toPolyPolygon( const basegfx::B2DPolyPolygon& rPolyPoly )
68     {
69         tools::PolyPolygon aTarget;
70         for (auto const& rB2DPolygon : rPolyPoly)
71             aTarget.Insert(toPolygon(rB2DPolygon));
72 
73         return aTarget;
74     }
75 }
76 
ImplDrawModeToColor(const Color & rColor) const77 Color OutputDevice::ImplDrawModeToColor( const Color& rColor ) const
78 {
79     Color aColor( rColor );
80     DrawModeFlags nDrawMode = GetDrawMode();
81 
82     if( nDrawMode & ( DrawModeFlags::BlackLine | DrawModeFlags::WhiteLine |
83                       DrawModeFlags::GrayLine |
84                       DrawModeFlags::SettingsLine ) )
85     {
86         if( !aColor.IsTransparent() )
87         {
88             if( nDrawMode & DrawModeFlags::BlackLine )
89             {
90                 aColor = COL_BLACK;
91             }
92             else if( nDrawMode & DrawModeFlags::WhiteLine )
93             {
94                 aColor = COL_WHITE;
95             }
96             else if( nDrawMode & DrawModeFlags::GrayLine )
97             {
98                 const sal_uInt8 cLum = aColor.GetLuminance();
99                 aColor = Color( cLum, cLum, cLum );
100             }
101             else if( nDrawMode & DrawModeFlags::SettingsLine )
102             {
103                 aColor = GetSettings().GetStyleSettings().GetFontColor();
104             }
105         }
106     }
107     return aColor;
108 }
109 
110 // Caution: This method is nearly the same as
111 // void OutputDevice::DrawPolyPolygon( const basegfx::B2DPolyPolygon& rB2DPolyPoly )
112 // so when changes are made here do not forget to make changes there, too
113 
DrawTransparent(const basegfx::B2DHomMatrix & rObjectTransform,const basegfx::B2DPolyPolygon & rB2DPolyPoly,double fTransparency)114 void OutputDevice::DrawTransparent(
115     const basegfx::B2DHomMatrix& rObjectTransform,
116     const basegfx::B2DPolyPolygon& rB2DPolyPoly,
117     double fTransparency)
118 {
119     assert(!is_double_buffered_window());
120 
121     // AW: Do NOT paint empty PolyPolygons
122     if(!rB2DPolyPoly.count())
123         return;
124 
125     // we need a graphics
126     if( !mpGraphics && !AcquireGraphics() )
127         return;
128     assert(mpGraphics);
129 
130     if( mbInitClipRegion )
131         InitClipRegion();
132 
133     if( mbOutputClipped )
134         return;
135 
136     if( mbInitLineColor )
137         InitLineColor();
138 
139     if( mbInitFillColor )
140         InitFillColor();
141 
142     if(mpGraphics->supportsOperation(OutDevSupportType::B2DDraw) &&
143        (RasterOp::OverPaint == GetRasterOp()) )
144     {
145         // b2dpolygon support not implemented yet on non-UNX platforms
146         basegfx::B2DPolyPolygon aB2DPolyPolygon(rB2DPolyPoly);
147 
148         // ensure it is closed
149         if(!aB2DPolyPolygon.isClosed())
150         {
151             // maybe assert, prevents buffering due to making a copy
152             aB2DPolyPolygon.setClosed( true );
153         }
154 
155         // create ObjectToDevice transformation
156         const basegfx::B2DHomMatrix aFullTransform(ImplGetDeviceTransformation() * rObjectTransform);
157         // TODO: this must not drop transparency for mpAlphaVDev case, but instead use premultiplied
158         // alpha... but that requires using premultiplied alpha also for already drawn data
159         const double fAdjustedTransparency = mpAlphaVDev ? 0 : fTransparency;
160         bool bDrawnOk(true);
161 
162         if( IsFillColor() )
163         {
164             bDrawnOk = mpGraphics->DrawPolyPolygon(
165                 aFullTransform,
166                 aB2DPolyPolygon,
167                 fAdjustedTransparency,
168                 *this);
169         }
170 
171         if( bDrawnOk && IsLineColor() )
172         {
173             const bool bPixelSnapHairline(mnAntialiasing & AntialiasingFlags::PixelSnapHairline);
174 
175             for(auto const& rPolygon : aB2DPolyPolygon)
176             {
177                 mpGraphics->DrawPolyLine(
178                     aFullTransform,
179                     rPolygon,
180                     fAdjustedTransparency,
181                     0.0, // tdf#124848 hairline
182                     nullptr, // MM01
183                     basegfx::B2DLineJoin::NONE,
184                     css::drawing::LineCap_BUTT,
185                     basegfx::deg2rad(15.0), // not used with B2DLineJoin::NONE, but the correct default
186                     bPixelSnapHairline,
187                     *this );
188             }
189         }
190 
191         if( bDrawnOk )
192         {
193             if( mpMetaFile )
194             {
195                 // tdf#119843 need transformed Polygon here
196                 basegfx::B2DPolyPolygon aB2DPolyPoly(rB2DPolyPoly);
197                 aB2DPolyPoly.transform(rObjectTransform);
198                 mpMetaFile->AddAction(
199                     new MetaTransparentAction(
200                         tools::PolyPolygon(aB2DPolyPoly),
201                         static_cast< sal_uInt16 >(fTransparency * 100.0)));
202             }
203 
204             if (mpAlphaVDev)
205                 mpAlphaVDev->DrawTransparent(rObjectTransform, rB2DPolyPoly, fTransparency);
206 
207             return;
208         }
209     }
210 
211     // fallback to old polygon drawing if needed
212     // tdf#119843 need transformed Polygon here
213     basegfx::B2DPolyPolygon aB2DPolyPoly(rB2DPolyPoly);
214     aB2DPolyPoly.transform(rObjectTransform);
215     DrawTransparent(
216         toPolyPolygon(aB2DPolyPoly),
217         static_cast<sal_uInt16>(fTransparency * 100.0));
218 }
219 
DrawInvisiblePolygon(const tools::PolyPolygon & rPolyPoly)220 void OutputDevice::DrawInvisiblePolygon( const tools::PolyPolygon& rPolyPoly )
221 {
222     assert(!is_double_buffered_window());
223 
224     // short circuit if the polygon border is invisible too
225     if( !mbLineColor )
226         return;
227 
228     // we assume that the border is NOT to be drawn transparently???
229     Push( PushFlags::FILLCOLOR );
230     SetFillColor();
231     DrawPolyPolygon( rPolyPoly );
232     Pop();
233 }
234 
DrawTransparentNatively(const tools::PolyPolygon & rPolyPoly,sal_uInt16 nTransparencePercent)235 bool OutputDevice::DrawTransparentNatively ( const tools::PolyPolygon& rPolyPoly,
236                                              sal_uInt16 nTransparencePercent )
237 {
238     assert(!is_double_buffered_window());
239 
240     bool bDrawn = false;
241 
242     if (mpGraphics->supportsOperation(OutDevSupportType::B2DDraw)
243 #if defined UNX && ! defined MACOSX && ! defined IOS
244         && GetBitCount() > 8
245 #endif
246 #ifdef _WIN32
247         // workaround bad dithering on remote displaying when using GDI+ with toolbar button highlighting
248         && !rPolyPoly.IsRect()
249 #endif
250         )
251     {
252         // prepare the graphics device
253         if( mbInitClipRegion )
254             InitClipRegion();
255 
256         if( mbOutputClipped )
257             return false;
258 
259         if( mbInitLineColor )
260             InitLineColor();
261 
262         if( mbInitFillColor )
263             InitFillColor();
264 
265         // get the polygon in device coordinates
266         basegfx::B2DPolyPolygon aB2DPolyPolygon(rPolyPoly.getB2DPolyPolygon());
267         const basegfx::B2DHomMatrix aTransform(ImplGetDeviceTransformation());
268 
269         const double fTransparency = 0.01 * nTransparencePercent;
270         if( mbFillColor )
271         {
272             // #i121591#
273             // CAUTION: Only non printing (pixel-renderer) VCL commands from OutputDevices
274             // should be used when printing. Normally this is avoided by the printer being
275             // non-AAed and thus e.g. on WIN GdiPlus calls are not used. It may be necessary
276             // to figure out a way of moving this code to its own function that is
277             // overridden by the Print class, which will mean we deliberately override the
278             // functionality and we use the fallback some lines below (which is not very good,
279             // though. For now, WinSalGraphics::drawPolyPolygon will detect printer usage and
280             // correct the wrong mapping (see there for details)
281             bDrawn = mpGraphics->DrawPolyPolygon(
282                 aTransform,
283                 aB2DPolyPolygon,
284                 fTransparency,
285                 *this);
286         }
287 
288         if( mbLineColor )
289         {
290             // disable the fill color for now
291             mpGraphics->SetFillColor();
292 
293             // draw the border line
294             const bool bPixelSnapHairline(mnAntialiasing & AntialiasingFlags::PixelSnapHairline);
295 
296             for(auto const& rPolygon : aB2DPolyPolygon)
297             {
298                 bDrawn = mpGraphics->DrawPolyLine(
299                     aTransform,
300                     rPolygon,
301                     fTransparency,
302                     0.0, // tdf#124848 hairline
303                     nullptr, // MM01
304                     basegfx::B2DLineJoin::NONE,
305                     css::drawing::LineCap_BUTT,
306                     basegfx::deg2rad(15.0), // not used with B2DLineJoin::NONE, but the correct default
307                     bPixelSnapHairline,
308                     *this );
309             }
310 
311             // prepare to restore the fill color
312             mbInitFillColor = mbFillColor;
313         }
314     }
315 
316     return bDrawn;
317 }
318 
EmulateDrawTransparent(const tools::PolyPolygon & rPolyPoly,sal_uInt16 nTransparencePercent)319 void OutputDevice::EmulateDrawTransparent ( const tools::PolyPolygon& rPolyPoly,
320                                             sal_uInt16 nTransparencePercent )
321 {
322     // #110958# Disable alpha VDev, we perform the necessary
323     VirtualDevice* pOldAlphaVDev = mpAlphaVDev;
324 
325     // operation explicitly further below.
326     if( mpAlphaVDev )
327         mpAlphaVDev = nullptr;
328 
329     GDIMetaFile* pOldMetaFile = mpMetaFile;
330     mpMetaFile = nullptr;
331 
332     tools::PolyPolygon aPolyPoly( LogicToPixel( rPolyPoly ) );
333     tools::Rectangle aPolyRect( aPolyPoly.GetBoundRect() );
334     tools::Rectangle aDstRect( Point(), GetOutputSizePixel() );
335 
336     aDstRect.Intersection( aPolyRect );
337 
338     ClipToPaintRegion( aDstRect );
339 
340     if( !aDstRect.IsEmpty() )
341     {
342         bool bDrawn = false;
343 
344         // #i66849# Added fast path for exactly rectangular
345         // polygons
346         // #i83087# Naturally, system alpha blending cannot
347         // work with separate alpha VDev
348         if( !mpAlphaVDev && aPolyPoly.IsRect() )
349         {
350             // setup Graphics only here (other cases delegate
351             // to basic OutDev methods)
352             if ( mbInitClipRegion )
353                 InitClipRegion();
354 
355             if ( mbInitLineColor )
356                 InitLineColor();
357 
358             if ( mbInitFillColor )
359                 InitFillColor();
360 
361             tools::Rectangle aLogicPolyRect( rPolyPoly.GetBoundRect() );
362             tools::Rectangle aPixelRect( ImplLogicToDevicePixel( aLogicPolyRect ) );
363 
364             if( !mbOutputClipped )
365             {
366                 bDrawn = mpGraphics->DrawAlphaRect( aPixelRect.Left(), aPixelRect.Top(),
367                     // #i98405# use methods with small g, else one pixel too much will be painted.
368                     // This is because the source is a polygon which when painted would not paint
369                     // the rightmost and lowest pixel line(s), so use one pixel less for the
370                     // rectangle, too.
371                                                     aPixelRect.getWidth(), aPixelRect.getHeight(),
372                                                     sal::static_int_cast<sal_uInt8>(nTransparencePercent),
373                                                     *this );
374             }
375             else
376             {
377                 bDrawn = true;
378             }
379         }
380 
381         if( !bDrawn )
382         {
383             ScopedVclPtrInstance< VirtualDevice > aVDev(*this);
384             const Size aDstSz( aDstRect.GetSize() );
385             const sal_uInt8 cTrans = static_cast<sal_uInt8>(MinMax( FRound( nTransparencePercent * 2.55 ), 0, 255 ));
386 
387             if( aDstRect.Left() || aDstRect.Top() )
388                 aPolyPoly.Move( -aDstRect.Left(), -aDstRect.Top() );
389 
390             if( aVDev->SetOutputSizePixel( aDstSz ) )
391             {
392                 const bool bOldMap = mbMap;
393 
394                 EnableMapMode( false );
395 
396                 aVDev->SetLineColor( COL_BLACK );
397                 aVDev->SetFillColor( COL_BLACK );
398                 aVDev->DrawPolyPolygon( aPolyPoly );
399 
400                 Bitmap aPaint( GetBitmap( aDstRect.TopLeft(), aDstSz ) );
401                 Bitmap aPolyMask( aVDev->GetBitmap( Point(), aDstSz ) );
402 
403                 // #107766# check for non-empty bitmaps before accessing them
404                 if( !aPaint.IsEmpty() && !aPolyMask.IsEmpty() )
405                 {
406                     BitmapScopedWriteAccess pW(aPaint);
407                     Bitmap::ScopedReadAccess pR(aPolyMask);
408 
409                     if( pW && pR )
410                     {
411                         BitmapColor aPixCol;
412                         const BitmapColor aFillCol( GetFillColor() );
413                         const BitmapColor aBlack( pR->GetBestMatchingColor( COL_BLACK ) );
414                         const tools::Long nWidth = pW->Width();
415                         const tools::Long nHeight = pW->Height();
416                         const tools::Long nR = aFillCol.GetRed();
417                         const tools::Long nG = aFillCol.GetGreen();
418                         const tools::Long nB = aFillCol.GetBlue();
419                         tools::Long nX, nY;
420 
421                         if (vcl::isPalettePixelFormat(aPaint.getPixelFormat()))
422                         {
423                             const BitmapPalette& rPal = pW->GetPalette();
424                             const sal_uInt16 nCount = rPal.GetEntryCount();
425                             std::unique_ptr<sal_uInt8[]> xMap(new sal_uInt8[ nCount * sizeof( BitmapColor )]);
426                             BitmapColor* pMap = reinterpret_cast<BitmapColor*>(xMap.get());
427 
428                             for( sal_uInt16 i = 0; i < nCount; i++ )
429                             {
430                                 BitmapColor aCol( rPal[ i ] );
431                                 aCol.Merge( aFillCol, cTrans );
432                                 pMap[ i ] = BitmapColor( static_cast<sal_uInt8>(rPal.GetBestIndex( aCol )) );
433                             }
434 
435                             if( pR->GetScanlineFormat() == ScanlineFormat::N1BitMsbPal &&
436                                 pW->GetScanlineFormat() == ScanlineFormat::N8BitPal )
437                             {
438                                 const sal_uInt8 cBlack = aBlack.GetIndex();
439 
440                                 for( nY = 0; nY < nHeight; nY++ )
441                                 {
442                                     Scanline pWScan = pW->GetScanline( nY );
443                                     Scanline pRScan = pR->GetScanline( nY );
444                                     sal_uInt8 cBit = 128;
445 
446                                     for( nX = 0; nX < nWidth; nX++, cBit >>= 1, pWScan++ )
447                                     {
448                                         if( !cBit )
449                                         {
450                                             cBit = 128;
451                                             pRScan += 1;
452                                         }
453                                         if( ( *pRScan & cBit ) == cBlack )
454                                         {
455                                             *pWScan = pMap[ *pWScan ].GetIndex();
456                                         }
457                                     }
458                                 }
459                             }
460                             else
461                             {
462                                 for( nY = 0; nY < nHeight; nY++ )
463                                 {
464                                     Scanline pScanline = pW->GetScanline(nY);
465                                     Scanline pScanlineRead = pR->GetScanline(nY);
466                                     for( nX = 0; nX < nWidth; nX++ )
467                                     {
468                                         if( pR->GetPixelFromData( pScanlineRead, nX ) == aBlack )
469                                         {
470                                             pW->SetPixelOnData( pScanline, nX, pMap[ pW->GetIndexFromData( pScanline, nX ) ] );
471                                         }
472                                     }
473                                 }
474                             }
475                         }
476                         else
477                         {
478                             if( pR->GetScanlineFormat() == ScanlineFormat::N1BitMsbPal &&
479                                 pW->GetScanlineFormat() == ScanlineFormat::N24BitTcBgr )
480                             {
481                                 const sal_uInt8 cBlack = aBlack.GetIndex();
482 
483                                 for( nY = 0; nY < nHeight; nY++ )
484                                 {
485                                     Scanline pWScan = pW->GetScanline( nY );
486                                     Scanline pRScan = pR->GetScanline( nY );
487                                     sal_uInt8 cBit = 128;
488 
489                                     for( nX = 0; nX < nWidth; nX++, cBit >>= 1, pWScan += 3 )
490                                     {
491                                         if( !cBit )
492                                         {
493                                             cBit = 128;
494                                             pRScan += 1;
495                                         }
496                                         if( ( *pRScan & cBit ) == cBlack )
497                                         {
498                                             pWScan[ 0 ] = color::ColorChannelMerge( pWScan[ 0 ], nB, cTrans );
499                                             pWScan[ 1 ] = color::ColorChannelMerge( pWScan[ 1 ], nG, cTrans );
500                                             pWScan[ 2 ] = color::ColorChannelMerge( pWScan[ 2 ], nR, cTrans );
501                                         }
502                                     }
503                                 }
504                             }
505                             else
506                             {
507                                 for( nY = 0; nY < nHeight; nY++ )
508                                 {
509                                     Scanline pScanline = pW->GetScanline(nY);
510                                     Scanline pScanlineRead = pR->GetScanline(nY);
511                                     for( nX = 0; nX < nWidth; nX++ )
512                                     {
513                                         if( pR->GetPixelFromData( pScanlineRead, nX ) == aBlack )
514                                         {
515                                             aPixCol = pW->GetColor( nY, nX );
516                                             aPixCol.Merge(aFillCol, cTrans);
517                                             pW->SetPixelOnData(pScanline, nX, aPixCol);
518                                         }
519                                     }
520                                 }
521                             }
522                         }
523                     }
524 
525                     pR.reset();
526                     pW.reset();
527 
528                     DrawBitmap( aDstRect.TopLeft(), aPaint );
529 
530                     EnableMapMode( bOldMap );
531 
532                     if( mbLineColor )
533                     {
534                         Push( PushFlags::FILLCOLOR );
535                         SetFillColor();
536                         DrawPolyPolygon( rPolyPoly );
537                         Pop();
538                     }
539                 }
540             }
541             else
542             {
543                 DrawPolyPolygon( rPolyPoly );
544             }
545         }
546     }
547 
548     mpMetaFile = pOldMetaFile;
549 
550     // #110958# Restore disabled alpha VDev
551     mpAlphaVDev = pOldAlphaVDev;
552 }
553 
DrawTransparent(const tools::PolyPolygon & rPolyPoly,sal_uInt16 nTransparencePercent)554 void OutputDevice::DrawTransparent( const tools::PolyPolygon& rPolyPoly,
555                                     sal_uInt16 nTransparencePercent )
556 {
557     assert(!is_double_buffered_window());
558 
559     // short circuit for drawing an opaque polygon
560     if( (nTransparencePercent < 1) || (mnDrawMode & DrawModeFlags::NoTransparency) )
561     {
562         DrawPolyPolygon( rPolyPoly );
563         return;
564     }
565 
566     // short circuit for drawing an invisible polygon
567     if( !mbFillColor || (nTransparencePercent >= 100) )
568     {
569         DrawInvisiblePolygon( rPolyPoly );
570         return; // tdf#84294: do not record it in metafile
571     }
572 
573     // handle metafile recording
574     if( mpMetaFile )
575         mpMetaFile->AddAction( new MetaTransparentAction( rPolyPoly, nTransparencePercent ) );
576 
577     bool bDrawn = !IsDeviceOutputNecessary() || ImplIsRecordLayout();
578     if( bDrawn )
579         return;
580 
581     // get the device graphics as drawing target
582     if( !mpGraphics && !AcquireGraphics() )
583         return;
584     assert(mpGraphics);
585 
586     // try hard to draw it directly, because the emulation layers are slower
587     bDrawn = DrawTransparentNatively( rPolyPoly, nTransparencePercent );
588 
589     if (!bDrawn)
590         EmulateDrawTransparent( rPolyPoly, nTransparencePercent );
591 
592     // #110958# Apply alpha value also to VDev alpha channel
593     if( mpAlphaVDev )
594     {
595         const Color aFillCol( mpAlphaVDev->GetFillColor() );
596         mpAlphaVDev->SetFillColor( Color(sal::static_int_cast<sal_uInt8>(255*nTransparencePercent/100),
597                                          sal::static_int_cast<sal_uInt8>(255*nTransparencePercent/100),
598                                          sal::static_int_cast<sal_uInt8>(255*nTransparencePercent/100)) );
599 
600         mpAlphaVDev->DrawTransparent( rPolyPoly, nTransparencePercent );
601 
602         mpAlphaVDev->SetFillColor( aFillCol );
603     }
604 }
605 
DrawTransparent(const GDIMetaFile & rMtf,const Point & rPos,const Size & rSize,const Gradient & rTransparenceGradient)606 void OutputDevice::DrawTransparent( const GDIMetaFile& rMtf, const Point& rPos,
607                                     const Size& rSize, const Gradient& rTransparenceGradient )
608 {
609     assert(!is_double_buffered_window());
610 
611     const Color aBlack( COL_BLACK );
612 
613     if( mpMetaFile )
614     {
615          // missing here is to map the data using the DeviceTransformation
616         mpMetaFile->AddAction( new MetaFloatTransparentAction( rMtf, rPos, rSize, rTransparenceGradient ) );
617     }
618 
619     if ( !IsDeviceOutputNecessary() )
620         return;
621 
622     if( ( rTransparenceGradient.GetStartColor() == aBlack && rTransparenceGradient.GetEndColor() == aBlack ) ||
623         ( mnDrawMode & DrawModeFlags::NoTransparency ) )
624     {
625         const_cast<GDIMetaFile&>(rMtf).WindStart();
626         const_cast<GDIMetaFile&>(rMtf).Play(*this, rPos, rSize);
627         const_cast<GDIMetaFile&>(rMtf).WindStart();
628     }
629     else
630     {
631         GDIMetaFile* pOldMetaFile = mpMetaFile;
632         tools::Rectangle aOutRect( LogicToPixel( rPos ), LogicToPixel( rSize ) );
633         Point aPoint;
634         tools::Rectangle aDstRect( aPoint, GetOutputSizePixel() );
635 
636         mpMetaFile = nullptr;
637         aDstRect.Intersection( aOutRect );
638 
639         ClipToPaintRegion( aDstRect );
640 
641         if( !aDstRect.IsEmpty() )
642         {
643             // Create transparent buffer
644             ScopedVclPtrInstance<VirtualDevice> xVDev(DeviceFormat::DEFAULT, DeviceFormat::DEFAULT);
645 
646             xVDev->mnDPIX = mnDPIX;
647             xVDev->mnDPIY = mnDPIY;
648 
649             if( xVDev->SetOutputSizePixel( aDstRect.GetSize() ) )
650             {
651                 if(GetAntialiasing() != AntialiasingFlags::NONE)
652                 {
653                     // #i102109#
654                     // For MetaFile replay (see task) it may now be necessary to take
655                     // into account that the content is AntiAlialiased and needs to be masked
656                     // like that. Instead of masking, i will use a copy-modify-paste cycle
657                     // here (as i already use in the VclPrimiziveRenderer with success)
658                     xVDev->SetAntialiasing(GetAntialiasing());
659 
660                     // create MapMode for buffer (offset needed) and set
661                     MapMode aMap(GetMapMode());
662                     const Point aOutPos(PixelToLogic(aDstRect.TopLeft()));
663                     aMap.SetOrigin(Point(-aOutPos.X(), -aOutPos.Y()));
664                     xVDev->SetMapMode(aMap);
665 
666                     // copy MapMode state and disable for target
667                     const bool bOrigMapModeEnabled(IsMapModeEnabled());
668                     EnableMapMode(false);
669 
670                     // copy MapMode state and disable for buffer
671                     const bool bBufferMapModeEnabled(xVDev->IsMapModeEnabled());
672                     xVDev->EnableMapMode(false);
673 
674                     // copy content from original to buffer
675                     xVDev->DrawOutDev( aPoint, xVDev->GetOutputSizePixel(), // dest
676                                        aDstRect.TopLeft(), xVDev->GetOutputSizePixel(), // source
677                                        *this);
678 
679                     // draw MetaFile to buffer
680                     xVDev->EnableMapMode(bBufferMapModeEnabled);
681                     const_cast<GDIMetaFile&>(rMtf).WindStart();
682                     const_cast<GDIMetaFile&>(rMtf).Play(*xVDev, rPos, rSize);
683                     const_cast<GDIMetaFile&>(rMtf).WindStart();
684 
685                     // get content bitmap from buffer
686                     xVDev->EnableMapMode(false);
687 
688                     const Bitmap aPaint(xVDev->GetBitmap(aPoint, xVDev->GetOutputSizePixel()));
689 
690                     // create alpha mask from gradient and get as Bitmap
691                     xVDev->EnableMapMode(bBufferMapModeEnabled);
692                     xVDev->SetDrawMode(DrawModeFlags::GrayGradient);
693                     xVDev->DrawGradient(tools::Rectangle(rPos, rSize), rTransparenceGradient);
694                     xVDev->SetDrawMode(DrawModeFlags::Default);
695                     xVDev->EnableMapMode(false);
696 
697                     const AlphaMask aAlpha(xVDev->GetBitmap(aPoint, xVDev->GetOutputSizePixel()));
698 
699                     xVDev.disposeAndClear();
700 
701                     // draw masked content to target and restore MapMode
702                     DrawBitmapEx(aDstRect.TopLeft(), BitmapEx(aPaint, aAlpha));
703                     EnableMapMode(bOrigMapModeEnabled);
704                 }
705                 else
706                 {
707                     MapMode aMap( GetMapMode() );
708                     Point aOutPos( PixelToLogic( aDstRect.TopLeft() ) );
709                     const bool bOldMap = mbMap;
710 
711                     aMap.SetOrigin( Point( -aOutPos.X(), -aOutPos.Y() ) );
712                     xVDev->SetMapMode( aMap );
713                     const bool bVDevOldMap = xVDev->IsMapModeEnabled();
714 
715                     // create paint bitmap
716                     const_cast<GDIMetaFile&>(rMtf).WindStart();
717                     const_cast<GDIMetaFile&>(rMtf).Play(*xVDev, rPos, rSize);
718                     const_cast<GDIMetaFile&>(rMtf).WindStart();
719                     xVDev->EnableMapMode( false );
720                     BitmapEx aPaint = xVDev->GetBitmapEx(Point(), xVDev->GetOutputSizePixel());
721                     xVDev->EnableMapMode( bVDevOldMap ); // #i35331#: MUST NOT use EnableMapMode( sal_True ) here!
722 
723                     // create alpha mask from gradient
724                     xVDev->SetDrawMode( DrawModeFlags::GrayGradient );
725                     xVDev->DrawGradient( tools::Rectangle( rPos, rSize ), rTransparenceGradient );
726                     xVDev->SetDrawMode( DrawModeFlags::Default );
727                     xVDev->EnableMapMode( false );
728 
729                     AlphaMask aAlpha(xVDev->GetBitmap(Point(), xVDev->GetOutputSizePixel()));
730                     aAlpha.BlendWith(aPaint.GetAlpha());
731 
732                     xVDev.disposeAndClear();
733 
734                     EnableMapMode( false );
735                     DrawBitmapEx(aDstRect.TopLeft(), BitmapEx(aPaint.GetBitmap(), aAlpha));
736                     EnableMapMode( bOldMap );
737                 }
738             }
739         }
740 
741         mpMetaFile = pOldMetaFile;
742     }
743 }
744 
745 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
746