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