Drop some needless mappings to identical strings
[LibreOffice.git] / vcl / source / outdev / transparent.cxx
blob8c4051087c3e2bb07b1de61f2bff245a68c5046b
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 .
20 #include <sal/types.h>
21 #include <osl/diagnose.h>
22 #include <rtl/math.hxx>
23 #include <basegfx/polygon/b2dpolygontools.hxx>
24 #include <tools/helpers.hxx>
25 #include <officecfg/Office/Common.hxx>
27 #include <vcl/BitmapTools.hxx>
28 #include <vcl/metaact.hxx>
29 #include <vcl/print.hxx>
30 #include <vcl/settings.hxx>
31 #include <vcl/svapp.hxx>
32 #include <vcl/virdev.hxx>
33 #include <vcl/BitmapWriteAccess.hxx>
34 #include <pdf/pdfwriter_impl.hxx>
35 #include <salgdi.hxx>
37 #include <list>
38 #include <memory>
40 #define MAX_TILE_WIDTH 1024
41 #define MAX_TILE_HEIGHT 1024
43 namespace
45 /**
46 * Perform a safe approximation of a polygon from double-precision
47 * coordinates to integer coordinates, to ensure that it has at least 2
48 * pixels in both X and Y directions.
50 tools::Polygon toPolygon( const basegfx::B2DPolygon& rPoly )
52 basegfx::B2DRange aRange = rPoly.getB2DRange();
53 double fW = aRange.getWidth(), fH = aRange.getHeight();
54 if (0.0 < fW && 0.0 < fH && (fW <= 1.0 || fH <= 1.0))
56 // This polygon not empty but is too small to display. Approximate it
57 // with a rectangle large enough to be displayed.
58 double nX = aRange.getMinX(), nY = aRange.getMinY();
59 double nW = std::max<double>(1.0, rtl::math::round(fW));
60 double nH = std::max<double>(1.0, rtl::math::round(fH));
62 tools::Polygon aTarget;
63 aTarget.Insert(0, Point(nX, nY));
64 aTarget.Insert(1, Point(nX+nW, nY));
65 aTarget.Insert(2, Point(nX+nW, nY+nH));
66 aTarget.Insert(3, Point(nX, nY+nH));
67 aTarget.Insert(4, Point(nX, nY));
68 return aTarget;
70 return tools::Polygon(rPoly);
73 tools::PolyPolygon toPolyPolygon( const basegfx::B2DPolyPolygon& rPolyPoly )
75 tools::PolyPolygon aTarget;
76 for (auto const& rB2DPolygon : rPolyPoly)
77 aTarget.Insert(toPolygon(rB2DPolygon));
79 return aTarget;
83 // Caution: This method is nearly the same as
84 // void OutputDevice::DrawPolyPolygon( const basegfx::B2DPolyPolygon& rB2DPolyPoly )
85 // so when changes are made here do not forget to make changes there, too
87 void OutputDevice::DrawTransparent(
88 const basegfx::B2DHomMatrix& rObjectTransform,
89 const basegfx::B2DPolyPolygon& rB2DPolyPoly,
90 double fTransparency)
92 assert(!is_double_buffered_window());
94 // AW: Do NOT paint empty PolyPolygons
95 if(!rB2DPolyPoly.count())
96 return;
98 // we need a graphics
99 if( !mpGraphics && !AcquireGraphics() )
100 return;
101 assert(mpGraphics);
103 if( mbInitClipRegion )
104 InitClipRegion();
106 if( mbOutputClipped )
107 return;
109 if( mbInitLineColor )
110 InitLineColor();
112 if( mbInitFillColor )
113 InitFillColor();
115 if (RasterOp::OverPaint == GetRasterOp())
117 // b2dpolygon support not implemented yet on non-UNX platforms
118 basegfx::B2DPolyPolygon aB2DPolyPolygon(rB2DPolyPoly);
120 // ensure it is closed
121 if(!aB2DPolyPolygon.isClosed())
123 // maybe assert, prevents buffering due to making a copy
124 aB2DPolyPolygon.setClosed( true );
127 // create ObjectToDevice transformation
128 const basegfx::B2DHomMatrix aFullTransform(ImplGetDeviceTransformation() * rObjectTransform);
129 // TODO: this must not drop transparency for mpAlphaVDev case, but instead use premultiplied
130 // alpha... but that requires using premultiplied alpha also for already drawn data
131 const double fAdjustedTransparency = mpAlphaVDev ? 0 : fTransparency;
133 if (IsFillColor())
135 mpGraphics->DrawPolyPolygon(
136 aFullTransform,
137 aB2DPolyPolygon,
138 fAdjustedTransparency,
139 *this);
142 if (IsLineColor())
144 const bool bPixelSnapHairline(mnAntialiasing & AntialiasingFlags::PixelSnapHairline);
146 for(auto const& rPolygon : std::as_const(aB2DPolyPolygon))
148 mpGraphics->DrawPolyLine(
149 aFullTransform,
150 rPolygon,
151 fAdjustedTransparency,
152 0.0, // tdf#124848 hairline
153 nullptr, // MM01
154 basegfx::B2DLineJoin::NONE,
155 css::drawing::LineCap_BUTT,
156 basegfx::deg2rad(15.0), // not used with B2DLineJoin::NONE, but the correct default
157 bPixelSnapHairline,
158 *this );
162 if( mpMetaFile )
164 // tdf#119843 need transformed Polygon here
165 basegfx::B2DPolyPolygon aB2DPolyPoly(rB2DPolyPoly);
166 aB2DPolyPoly.transform(rObjectTransform);
167 mpMetaFile->AddAction(
168 new MetaTransparentAction(
169 tools::PolyPolygon(aB2DPolyPoly),
170 static_cast< sal_uInt16 >(fTransparency * 100.0)));
173 if (mpAlphaVDev)
174 mpAlphaVDev->DrawTransparent(rObjectTransform, rB2DPolyPoly, fTransparency);
176 return;
179 // fallback to old polygon drawing if needed
180 // tdf#119843 need transformed Polygon here
181 basegfx::B2DPolyPolygon aB2DPolyPoly(rB2DPolyPoly);
182 aB2DPolyPoly.transform(rObjectTransform);
183 DrawTransparent(
184 toPolyPolygon(aB2DPolyPoly),
185 static_cast<sal_uInt16>(fTransparency * 100.0));
188 bool OutputDevice::DrawTransparentNatively ( const tools::PolyPolygon& rPolyPoly,
189 sal_uInt16 nTransparencePercent )
191 assert(!is_double_buffered_window());
193 bool bDrawn = false;
195 if (true
196 #if defined UNX && ! defined MACOSX && ! defined IOS
197 && GetBitCount() > 8
198 #endif
199 #ifdef _WIN32
200 // workaround bad dithering on remote displaying when using GDI+ with toolbar button highlighting
201 && !rPolyPoly.IsRect()
202 #endif
205 // prepare the graphics device
206 if( mbInitClipRegion )
207 InitClipRegion();
209 if( mbOutputClipped )
210 return true;
212 if( mbInitLineColor )
213 InitLineColor();
215 if( mbInitFillColor )
216 InitFillColor();
218 // get the polygon in device coordinates
219 basegfx::B2DPolyPolygon aB2DPolyPolygon(rPolyPoly.getB2DPolyPolygon());
220 const basegfx::B2DHomMatrix aTransform(ImplGetDeviceTransformation());
222 const double fTransparency = 0.01 * nTransparencePercent;
223 if( mbFillColor )
225 // #i121591#
226 // CAUTION: Only non printing (pixel-renderer) VCL commands from OutputDevices
227 // should be used when printing. Normally this is avoided by the printer being
228 // non-AAed and thus e.g. on WIN GdiPlus calls are not used. It may be necessary
229 // to figure out a way of moving this code to its own function that is
230 // overridden by the Print class, which will mean we deliberately override the
231 // functionality and we use the fallback some lines below (which is not very good,
232 // though. For now, WinSalGraphics::drawPolyPolygon will detect printer usage and
233 // correct the wrong mapping (see there for details)
234 mpGraphics->DrawPolyPolygon(
235 aTransform,
236 aB2DPolyPolygon,
237 fTransparency,
238 *this);
239 bDrawn = true;
242 if( mbLineColor )
244 // disable the fill color for now
245 mpGraphics->SetFillColor();
247 // draw the border line
248 const bool bPixelSnapHairline(mnAntialiasing & AntialiasingFlags::PixelSnapHairline);
250 for(auto const& rPolygon : std::as_const(aB2DPolyPolygon))
252 bDrawn = mpGraphics->DrawPolyLine(
253 aTransform,
254 rPolygon,
255 fTransparency,
256 0.0, // tdf#124848 hairline
257 nullptr, // MM01
258 basegfx::B2DLineJoin::NONE,
259 css::drawing::LineCap_BUTT,
260 basegfx::deg2rad(15.0), // not used with B2DLineJoin::NONE, but the correct default
261 bPixelSnapHairline,
262 *this );
265 // prepare to restore the fill color
266 mbInitFillColor = mbFillColor;
270 return bDrawn;
273 void OutputDevice::EmulateDrawTransparent ( const tools::PolyPolygon& rPolyPoly,
274 sal_uInt16 nTransparencePercent )
276 // #110958# Disable alpha VDev, we perform the necessary
277 VirtualDevice* pOldAlphaVDev = mpAlphaVDev;
279 // operation explicitly further below.
280 if( mpAlphaVDev )
281 mpAlphaVDev = nullptr;
283 GDIMetaFile* pOldMetaFile = mpMetaFile;
284 mpMetaFile = nullptr;
286 tools::PolyPolygon aPolyPoly( LogicToPixel( rPolyPoly ) );
287 tools::Rectangle aPolyRect( aPolyPoly.GetBoundRect() );
288 tools::Rectangle aDstRect( Point(), GetOutputSizePixel() );
290 aDstRect.Intersection( aPolyRect );
292 ClipToPaintRegion( aDstRect );
294 if( !aDstRect.IsEmpty() )
296 bool bDrawn = false;
298 // #i66849# Added fast path for exactly rectangular
299 // polygons
300 // #i83087# Naturally, system alpha blending cannot
301 // work with separate alpha VDev
302 if( !mpAlphaVDev && aPolyPoly.IsRect() )
304 // setup Graphics only here (other cases delegate
305 // to basic OutDev methods)
306 if ( mbInitClipRegion )
307 InitClipRegion();
309 if ( mbInitLineColor )
310 InitLineColor();
312 if ( mbInitFillColor )
313 InitFillColor();
315 tools::Rectangle aLogicPolyRect( rPolyPoly.GetBoundRect() );
316 tools::Rectangle aPixelRect( ImplLogicToDevicePixel( aLogicPolyRect ) );
318 if( !mbOutputClipped )
320 bDrawn = mpGraphics->DrawAlphaRect( aPixelRect.Left(), aPixelRect.Top(),
321 // #i98405# use methods with small g, else one pixel too much will be painted.
322 // This is because the source is a polygon which when painted would not paint
323 // the rightmost and lowest pixel line(s), so use one pixel less for the
324 // rectangle, too.
325 aPixelRect.getOpenWidth(), aPixelRect.getOpenHeight(),
326 sal::static_int_cast<sal_uInt8>(nTransparencePercent),
327 *this );
329 else
331 bDrawn = true;
335 if( !bDrawn )
337 ScopedVclPtrInstance< VirtualDevice > aVDev(*this);
338 const Size aDstSz( aDstRect.GetSize() );
339 const sal_uInt8 cTrans = basegfx::fround<sal_uInt8>(nTransparencePercent * 2.55);
341 if( aDstRect.Left() || aDstRect.Top() )
342 aPolyPoly.Move( -aDstRect.Left(), -aDstRect.Top() );
344 if( aVDev->SetOutputSizePixel( aDstSz ) )
346 const bool bOldMap = mbMap;
348 EnableMapMode( false );
350 aVDev->SetLineColor( COL_BLACK );
351 aVDev->SetFillColor( COL_BLACK );
352 aVDev->DrawPolyPolygon( aPolyPoly );
354 Bitmap aPaint( GetBitmap( aDstRect.TopLeft(), aDstSz ) );
355 Bitmap aPolyMask( aVDev->GetBitmap( Point(), aDstSz ) );
357 // #107766# check for non-empty bitmaps before accessing them
358 if( !aPaint.IsEmpty() && !aPolyMask.IsEmpty() )
360 BitmapScopedWriteAccess pW(aPaint);
361 BitmapScopedReadAccess pR(aPolyMask);
363 if( pW && pR )
365 BitmapColor aPixCol;
366 const BitmapColor aFillCol( GetFillColor() );
367 const BitmapColor aBlack( pR->GetBestMatchingColor( COL_BLACK ) );
368 const tools::Long nWidth = pW->Width();
369 const tools::Long nHeight = pW->Height();
370 const tools::Long nR = aFillCol.GetRed();
371 const tools::Long nG = aFillCol.GetGreen();
372 const tools::Long nB = aFillCol.GetBlue();
373 tools::Long nX, nY;
375 if (vcl::isPalettePixelFormat(aPaint.getPixelFormat()))
377 const BitmapPalette& rPal = pW->GetPalette();
378 const sal_uInt16 nCount = rPal.GetEntryCount();
379 std::unique_ptr<sal_uInt8[]> xMap(new sal_uInt8[ nCount * sizeof( BitmapColor )]);
380 BitmapColor* pMap = reinterpret_cast<BitmapColor*>(xMap.get());
382 for( sal_uInt16 i = 0; i < nCount; i++ )
384 BitmapColor aCol( rPal[ i ] );
385 aCol.Merge( aFillCol, cTrans );
386 pMap[ i ] = BitmapColor( static_cast<sal_uInt8>(rPal.GetBestIndex( aCol )) );
389 if( pR->GetScanlineFormat() == ScanlineFormat::N1BitMsbPal &&
390 pW->GetScanlineFormat() == ScanlineFormat::N8BitPal )
392 const sal_uInt8 cBlack = aBlack.GetIndex();
394 for( nY = 0; nY < nHeight; nY++ )
396 Scanline pWScan = pW->GetScanline( nY );
397 Scanline pRScan = pR->GetScanline( nY );
398 sal_uInt8 cBit = 128;
400 for( nX = 0; nX < nWidth; nX++, cBit >>= 1, pWScan++ )
402 if( !cBit )
404 cBit = 128;
405 pRScan += 1;
407 if( ( *pRScan & cBit ) == cBlack )
409 *pWScan = pMap[ *pWScan ].GetIndex();
414 else
416 for( nY = 0; nY < nHeight; nY++ )
418 Scanline pScanline = pW->GetScanline(nY);
419 Scanline pScanlineRead = pR->GetScanline(nY);
420 for( nX = 0; nX < nWidth; nX++ )
422 if( pR->GetPixelFromData( pScanlineRead, nX ) == aBlack )
424 pW->SetPixelOnData( pScanline, nX, pMap[ pW->GetIndexFromData( pScanline, nX ) ] );
430 else
432 if( pR->GetScanlineFormat() == ScanlineFormat::N1BitMsbPal &&
433 pW->GetScanlineFormat() == ScanlineFormat::N24BitTcBgr )
435 const sal_uInt8 cBlack = aBlack.GetIndex();
437 for( nY = 0; nY < nHeight; nY++ )
439 Scanline pWScan = pW->GetScanline( nY );
440 Scanline pRScan = pR->GetScanline( nY );
441 sal_uInt8 cBit = 128;
443 for( nX = 0; nX < nWidth; nX++, cBit >>= 1, pWScan += 3 )
445 if( !cBit )
447 cBit = 128;
448 pRScan += 1;
450 if( ( *pRScan & cBit ) == cBlack )
452 pWScan[ 0 ] = color::ColorChannelMerge( pWScan[ 0 ], nB, cTrans );
453 pWScan[ 1 ] = color::ColorChannelMerge( pWScan[ 1 ], nG, cTrans );
454 pWScan[ 2 ] = color::ColorChannelMerge( pWScan[ 2 ], nR, cTrans );
459 else
461 for( nY = 0; nY < nHeight; nY++ )
463 Scanline pScanline = pW->GetScanline(nY);
464 Scanline pScanlineRead = pR->GetScanline(nY);
465 for( nX = 0; nX < nWidth; nX++ )
467 if( pR->GetPixelFromData( pScanlineRead, nX ) == aBlack )
469 aPixCol = pW->GetColor( nY, nX );
470 aPixCol.Merge(aFillCol, cTrans);
471 pW->SetPixelOnData(pScanline, nX, aPixCol);
479 pR.reset();
480 pW.reset();
482 DrawBitmap( aDstRect.TopLeft(), aPaint );
484 EnableMapMode( bOldMap );
486 if( mbLineColor )
488 Push( vcl::PushFlags::FILLCOLOR );
489 SetFillColor();
490 DrawPolyPolygon( rPolyPoly );
491 Pop();
495 else
497 DrawPolyPolygon( rPolyPoly );
502 mpMetaFile = pOldMetaFile;
504 // #110958# Restore disabled alpha VDev
505 mpAlphaVDev = pOldAlphaVDev;
508 void OutputDevice::DrawTransparent( const tools::PolyPolygon& rPolyPoly,
509 sal_uInt16 nTransparencePercent )
511 assert(!is_double_buffered_window());
513 // short circuit for drawing an opaque polygon
514 if( (nTransparencePercent < 1) || (mnDrawMode & DrawModeFlags::NoTransparency) )
516 DrawPolyPolygon( rPolyPoly );
517 return;
520 // short circuit for drawing an invisible polygon
521 if( (!mbFillColor && !mbLineColor) || (nTransparencePercent >= 100) )
522 return; // tdf#84294: do not record it in metafile
524 // handle metafile recording
525 if( mpMetaFile )
526 mpMetaFile->AddAction( new MetaTransparentAction( rPolyPoly, nTransparencePercent ) );
528 bool bDrawn = !IsDeviceOutputNecessary() || ImplIsRecordLayout();
529 if( bDrawn )
530 return;
532 // get the device graphics as drawing target
533 if( !mpGraphics && !AcquireGraphics() )
534 return;
535 assert(mpGraphics);
537 // try hard to draw it directly, because the emulation layers are slower
538 bDrawn = DrawTransparentNatively( rPolyPoly, nTransparencePercent );
540 if (!bDrawn)
541 EmulateDrawTransparent( rPolyPoly, nTransparencePercent );
543 // #110958# Apply alpha value also to VDev alpha channel
544 if( mpAlphaVDev )
546 const Color aFillCol( mpAlphaVDev->GetFillColor() );
547 sal_uInt8 nAlpha = 255 - sal::static_int_cast<sal_uInt8>(255*nTransparencePercent/100);
548 mpAlphaVDev->SetFillColor( Color(nAlpha, nAlpha, nAlpha) );
550 mpAlphaVDev->DrawTransparent( rPolyPoly, nTransparencePercent );
552 mpAlphaVDev->SetFillColor( aFillCol );
556 void OutputDevice::DrawTransparent( const GDIMetaFile& rMtf, const Point& rPos,
557 const Size& rSize, const Gradient& rTransparenceGradient )
559 DrawTransparent( rMtf, rPos, rSize, rPos, rSize, rTransparenceGradient );
562 void OutputDevice::DrawTransparent( const GDIMetaFile& rMtf, const Point& rPos, const Size& rSize,
563 const Point& rMtfPos, const Size& rMtfSize,
564 const Gradient& rTransparenceGradient )
566 assert(!is_double_buffered_window());
568 const Color aBlack( COL_BLACK );
570 if( mpMetaFile )
572 // missing here is to map the data using the DeviceTransformation
573 mpMetaFile->AddAction( new MetaFloatTransparentAction( rMtf, rPos, rSize, rTransparenceGradient ) );
576 if ( !IsDeviceOutputNecessary() )
577 return;
579 if( ( rTransparenceGradient.GetStartColor() == aBlack && rTransparenceGradient.GetEndColor() == aBlack ) ||
580 ( mnDrawMode & DrawModeFlags::NoTransparency ) )
582 const_cast<GDIMetaFile&>(rMtf).WindStart();
583 const_cast<GDIMetaFile&>(rMtf).Play(*this, rMtfPos, rMtfSize);
584 const_cast<GDIMetaFile&>(rMtf).WindStart();
586 else
588 GDIMetaFile* pOldMetaFile = mpMetaFile;
589 tools::Rectangle aOutRect( LogicToPixel( tools::Rectangle(rPos, rSize) ) );
590 Point aPoint;
591 tools::Rectangle aDstRect( aPoint, GetOutputSizePixel() );
593 mpMetaFile = nullptr;
594 aDstRect.Intersection( aOutRect );
596 ClipToPaintRegion( aDstRect );
598 if( !aDstRect.IsEmpty() )
600 // Create transparent buffer
601 ScopedVclPtrInstance<VirtualDevice> xVDev(DeviceFormat::WITH_ALPHA);
603 xVDev->mnDPIX = mnDPIX;
604 xVDev->mnDPIY = mnDPIY;
606 if( xVDev->SetOutputSizePixel( aDstRect.GetSize(), true, true ) )
608 // tdf#150610 fix broken rendering of text meta actions
609 // Even when drawing to a VirtualDevice that has antialiasing
610 // disabled, text will still be drawn with some antialiased
611 // pixels on HiDPI displays. So, use the antialiasing enabled
612 // code to render if there are any text meta actions in the
613 // metafile.
614 if(GetAntialiasing() != AntialiasingFlags::NONE || rPos != rMtfPos || rSize != rMtfSize)
616 // #i102109#
617 // For MetaFile replay (see task) it may now be necessary to take
618 // into account that the content is AntiAlialiased and needs to be masked
619 // like that. Instead of masking, i will use a copy-modify-paste cycle
620 // here (as i already use in the VclPrimiziveRenderer with success)
621 xVDev->SetAntialiasing(GetAntialiasing());
623 // create MapMode for buffer (offset needed) and set
624 MapMode aMap(GetMapMode());
625 const Point aOutPos(PixelToLogic(aDstRect.TopLeft()));
626 aMap.SetOrigin(Point(-aOutPos.X(), -aOutPos.Y()));
627 xVDev->SetMapMode(aMap);
629 // copy MapMode state and disable for target
630 const bool bOrigMapModeEnabled(IsMapModeEnabled());
631 EnableMapMode(false);
633 // copy MapMode state and disable for buffer
634 const bool bBufferMapModeEnabled(xVDev->IsMapModeEnabled());
635 xVDev->EnableMapMode(false);
637 // copy content from original to buffer
638 xVDev->DrawOutDev( aPoint, xVDev->GetOutputSizePixel(), // dest
639 aDstRect.TopLeft(), xVDev->GetOutputSizePixel(), // source
640 *this);
642 // draw MetaFile to buffer
643 xVDev->EnableMapMode(bBufferMapModeEnabled);
644 const_cast<GDIMetaFile&>(rMtf).WindStart();
645 const_cast<GDIMetaFile&>(rMtf).Play(*xVDev, rMtfPos, rMtfSize);
646 const_cast<GDIMetaFile&>(rMtf).WindStart();
648 // get content bitmap from buffer
649 xVDev->EnableMapMode(false);
651 const BitmapEx aPaint(xVDev->GetBitmapEx(aPoint, xVDev->GetOutputSizePixel()));
653 // create alpha mask from gradient and get as Bitmap
654 xVDev->EnableMapMode(bBufferMapModeEnabled);
655 xVDev->SetDrawMode(DrawModeFlags::GrayGradient);
656 // Related tdf#150610 draw gradient to VirtualDevice bounds
657 // If we are here and the metafile bounds differs from the
658 // VirtualDevice bounds so that we apply the transparency
659 // gradient to any pixels drawn outside of the metafile
660 // bounds.
661 xVDev->DrawGradient(tools::Rectangle(rPos, rSize), rTransparenceGradient);
662 xVDev->SetDrawMode(DrawModeFlags::Default);
663 xVDev->EnableMapMode(false);
665 AlphaMask aAlpha(xVDev->GetBitmap(aPoint, xVDev->GetOutputSizePixel()));
666 const AlphaMask& aPaintAlpha(aPaint.GetAlphaMask());
667 // The alpha mask is inverted from what
668 // is expected so invert it again
669 aAlpha.Invert(); // convert to alpha
670 aAlpha.BlendWith(aPaintAlpha);
672 xVDev.disposeAndClear();
674 // draw masked content to target and restore MapMode
675 DrawBitmapEx(aDstRect.TopLeft(), BitmapEx(aPaint.GetBitmap(), aAlpha));
676 EnableMapMode(bOrigMapModeEnabled);
678 else
680 MapMode aMap( GetMapMode() );
681 Point aOutPos( PixelToLogic( aDstRect.TopLeft() ) );
682 const bool bOldMap = mbMap;
684 aMap.SetOrigin( Point( -aOutPos.X(), -aOutPos.Y() ) );
685 xVDev->SetMapMode( aMap );
686 const bool bVDevOldMap = xVDev->IsMapModeEnabled();
688 // create paint bitmap
689 const_cast<GDIMetaFile&>(rMtf).WindStart();
690 const_cast<GDIMetaFile&>(rMtf).Play(*xVDev, rMtfPos, rMtfSize);
691 const_cast<GDIMetaFile&>(rMtf).WindStart();
692 xVDev->EnableMapMode( false );
693 BitmapEx aPaint = xVDev->GetBitmapEx(Point(), xVDev->GetOutputSizePixel());
694 xVDev->EnableMapMode( bVDevOldMap ); // #i35331#: MUST NOT use EnableMapMode( sal_True ) here!
696 // create alpha mask from gradient
697 xVDev->SetDrawMode( DrawModeFlags::GrayGradient );
698 xVDev->DrawGradient( tools::Rectangle( rMtfPos, rMtfSize ), rTransparenceGradient );
699 xVDev->SetDrawMode( DrawModeFlags::Default );
700 xVDev->EnableMapMode( false );
702 AlphaMask aAlpha(xVDev->GetBitmap(Point(), xVDev->GetOutputSizePixel()));
703 const AlphaMask& aPaintAlpha(aPaint.GetAlphaMask());
704 // The alpha mask is inverted from what
705 // is expected so invert it again
706 aAlpha.Invert(); // convert to alpha
707 aAlpha.BlendWith(aPaintAlpha);
709 xVDev.disposeAndClear();
711 EnableMapMode( false );
712 DrawBitmapEx(aDstRect.TopLeft(), BitmapEx(aPaint.GetBitmap(), aAlpha));
713 EnableMapMode( bOldMap );
718 mpMetaFile = pOldMetaFile;
722 typedef ::std::pair< MetaAction*, int > Component; // MetaAction plus index in metafile
724 namespace {
726 // List of (intersecting) actions, plus overall bounds
727 struct ConnectedComponents
729 ConnectedComponents() :
730 aComponentList(),
731 aBounds(),
732 aBgColor(COL_WHITE),
733 bIsSpecial(false),
734 bIsFullyTransparent(false)
737 ::std::list< Component > aComponentList;
738 tools::Rectangle aBounds;
739 Color aBgColor;
740 bool bIsSpecial;
741 bool bIsFullyTransparent;
746 namespace {
748 /** Determines whether the action can handle transparency correctly
749 (i.e. when painted on white background, does the action still look
750 correct)?
752 bool DoesActionHandleTransparency( const MetaAction& rAct )
754 // MetaActionType::FLOATTRANSPARENT can contain a whole metafile,
755 // which is to be rendered with the given transparent gradient. We
756 // currently cannot emulate transparent painting on a white
757 // background reliably.
759 // the remainder can handle printing itself correctly on a uniform
760 // white background.
761 switch( rAct.GetType() )
763 case MetaActionType::Transparent:
764 case MetaActionType::BMPEX:
765 case MetaActionType::BMPEXSCALE:
766 case MetaActionType::BMPEXSCALEPART:
767 return true;
769 default:
770 return false;
774 bool doesRectCoverWithUniformColor(
775 tools::Rectangle const & rPrevRect,
776 tools::Rectangle const & rCurrRect,
777 OutputDevice const & rMapModeVDev)
779 // shape needs to fully cover previous content, and have uniform
780 // color
781 return (rMapModeVDev.LogicToPixel(rCurrRect).Contains(rPrevRect) &&
782 rMapModeVDev.IsFillColor());
785 /** Check whether rCurrRect rectangle fully covers io_rPrevRect - if
786 yes, return true and update o_rBgColor
788 bool checkRect( tools::Rectangle& io_rPrevRect,
789 Color& o_rBgColor,
790 const tools::Rectangle& rCurrRect,
791 OutputDevice const & rMapModeVDev )
793 bool bRet = doesRectCoverWithUniformColor(io_rPrevRect, rCurrRect, rMapModeVDev);
795 if( bRet )
797 io_rPrevRect = rCurrRect;
798 o_rBgColor = rMapModeVDev.GetFillColor();
801 return bRet;
804 /** #107169# Convert BitmapEx to Bitmap with appropriately blended
805 color. Convert MetaTransparentAction to plain polygon,
806 appropriately colored
808 @param o_rMtf
809 Add converted actions to this metafile
811 void ImplConvertTransparentAction( GDIMetaFile& o_rMtf,
812 const MetaAction& rAct,
813 const OutputDevice& rStateOutDev,
814 Color aBgColor )
816 if (rAct.GetType() == MetaActionType::Transparent)
818 const MetaTransparentAction* pTransAct = static_cast<const MetaTransparentAction*>(&rAct);
819 sal_uInt16 nTransparency( pTransAct->GetTransparence() );
821 // #i10613# Respect transparency for draw color
822 if (nTransparency)
824 o_rMtf.AddAction(new MetaPushAction(vcl::PushFlags::LINECOLOR|vcl::PushFlags::FILLCOLOR));
826 // assume white background for alpha blending
827 Color aLineColor(rStateOutDev.GetLineColor());
828 aLineColor.SetRed(static_cast<sal_uInt8>((255*nTransparency + (100 - nTransparency) * aLineColor.GetRed()) / 100));
829 aLineColor.SetGreen(static_cast<sal_uInt8>((255*nTransparency + (100 - nTransparency) * aLineColor.GetGreen()) / 100));
830 aLineColor.SetBlue(static_cast<sal_uInt8>((255*nTransparency + (100 - nTransparency) * aLineColor.GetBlue()) / 100));
831 o_rMtf.AddAction(new MetaLineColorAction(aLineColor, true));
833 Color aFillColor(rStateOutDev.GetFillColor());
834 aFillColor.SetRed(static_cast<sal_uInt8>((255*nTransparency + (100 - nTransparency)*aFillColor.GetRed()) / 100));
835 aFillColor.SetGreen(static_cast<sal_uInt8>((255*nTransparency + (100 - nTransparency)*aFillColor.GetGreen()) / 100));
836 aFillColor.SetBlue(static_cast<sal_uInt8>((255*nTransparency + (100 - nTransparency)*aFillColor.GetBlue()) / 100));
837 o_rMtf.AddAction(new MetaFillColorAction(aFillColor, true));
840 o_rMtf.AddAction(new MetaPolyPolygonAction(pTransAct->GetPolyPolygon()));
842 if(nTransparency)
843 o_rMtf.AddAction(new MetaPopAction());
845 else
847 BitmapEx aBmpEx;
849 switch (rAct.GetType())
851 case MetaActionType::BMPEX:
852 aBmpEx = static_cast<const MetaBmpExAction&>(rAct).GetBitmapEx();
853 break;
855 case MetaActionType::BMPEXSCALE:
856 case MetaActionType::BMPEXSCALEPART:
857 aBmpEx = static_cast<const MetaBmpExScaleAction&>(rAct).GetBitmapEx();
858 break;
860 case MetaActionType::Transparent:
862 default:
863 OSL_FAIL("Printer::GetPreparedMetafile impossible state reached");
864 break;
867 Bitmap aBmp(aBmpEx.GetBitmap());
868 if (aBmpEx.IsAlpha())
870 // blend with alpha channel
871 aBmp.Convert(BmpConversion::N24Bit);
872 aBmp.Blend(aBmpEx.GetAlphaMask(), aBgColor);
875 // add corresponding action
876 switch (rAct.GetType())
878 case MetaActionType::BMPEX:
879 o_rMtf.AddAction(new MetaBmpAction(
880 static_cast<const MetaBmpExAction&>(rAct).GetPoint(),
881 aBmp));
882 break;
883 case MetaActionType::BMPEXSCALE:
884 o_rMtf.AddAction(new MetaBmpScaleAction(
885 static_cast<const MetaBmpExScaleAction&>(rAct).GetPoint(),
886 static_cast<const MetaBmpExScaleAction&>(rAct).GetSize(),
887 aBmp));
888 break;
889 case MetaActionType::BMPEXSCALEPART:
890 o_rMtf.AddAction(new MetaBmpScalePartAction(
891 static_cast<const MetaBmpExScalePartAction&>(rAct).GetDestPoint(),
892 static_cast<const MetaBmpExScalePartAction&>(rAct).GetDestSize(),
893 static_cast<const MetaBmpExScalePartAction&>(rAct).GetSrcPoint(),
894 static_cast<const MetaBmpExScalePartAction&>(rAct).GetSrcSize(),
895 aBmp));
896 break;
897 default:
898 OSL_FAIL("Unexpected case");
899 break;
904 // #i10613# Extracted from ImplCheckRect::ImplCreate
905 // Returns true, if given action creates visible (i.e. non-transparent) output
906 bool ImplIsNotTransparent( const MetaAction& rAct, const OutputDevice& rOut )
908 const bool bLineTransparency( !rOut.IsLineColor() || rOut.GetLineColor().IsFullyTransparent() );
909 const bool bFillTransparency( !rOut.IsFillColor() || rOut.GetFillColor().IsFullyTransparent() );
910 bool bRet( false );
912 switch( rAct.GetType() )
914 case MetaActionType::POINT:
915 case MetaActionType::LINE:
916 case MetaActionType::POLYLINE:
917 if( !bLineTransparency )
918 bRet = true;
919 break;
921 case MetaActionType::RECT:
922 if( !bLineTransparency || !bFillTransparency )
923 bRet = true;
924 break;
926 case MetaActionType::ROUNDRECT:
927 if( !bLineTransparency || !bFillTransparency )
928 bRet = true;
929 break;
931 case MetaActionType::ELLIPSE:
932 if( !bLineTransparency || !bFillTransparency )
933 bRet = true;
934 break;
936 case MetaActionType::ARC:
937 if( !bLineTransparency || !bFillTransparency )
938 bRet = true;
939 break;
941 case MetaActionType::PIE:
942 if( !bLineTransparency || !bFillTransparency )
943 bRet = true;
944 break;
946 case MetaActionType::CHORD:
947 if( !bLineTransparency || !bFillTransparency )
948 bRet = true;
949 break;
951 case MetaActionType::POLYGON:
952 if( !bLineTransparency || !bFillTransparency )
953 bRet = true;
954 break;
956 case MetaActionType::POLYPOLYGON:
957 if( !bLineTransparency || !bFillTransparency )
958 bRet = true;
959 break;
961 case MetaActionType::TEXT:
963 const MetaTextAction& rTextAct = static_cast<const MetaTextAction&>(rAct);
964 const OUString aString( rTextAct.GetText().copy(rTextAct.GetIndex(), rTextAct.GetLen()) );
965 if (!aString.isEmpty())
966 bRet = true;
968 break;
970 case MetaActionType::TEXTARRAY:
972 const MetaTextArrayAction& rTextAct = static_cast<const MetaTextArrayAction&>(rAct);
973 const OUString aString( rTextAct.GetText().copy(rTextAct.GetIndex(), rTextAct.GetLen()) );
974 if (!aString.isEmpty())
975 bRet = true;
977 break;
979 case MetaActionType::PIXEL:
980 case MetaActionType::BMP:
981 case MetaActionType::BMPSCALE:
982 case MetaActionType::BMPSCALEPART:
983 case MetaActionType::BMPEX:
984 case MetaActionType::BMPEXSCALE:
985 case MetaActionType::BMPEXSCALEPART:
986 case MetaActionType::MASK:
987 case MetaActionType::MASKSCALE:
988 case MetaActionType::MASKSCALEPART:
989 case MetaActionType::GRADIENT:
990 case MetaActionType::GRADIENTEX:
991 case MetaActionType::HATCH:
992 case MetaActionType::WALLPAPER:
993 case MetaActionType::Transparent:
994 case MetaActionType::FLOATTRANSPARENT:
995 case MetaActionType::EPS:
996 case MetaActionType::TEXTRECT:
997 case MetaActionType::STRETCHTEXT:
998 case MetaActionType::TEXTLINE:
999 // all other actions: generate non-transparent output
1000 bRet = true;
1001 break;
1003 default:
1004 break;
1007 return bRet;
1010 // #i10613# Extracted from ImplCheckRect::ImplCreate
1011 tools::Rectangle ImplCalcActionBounds( const MetaAction& rAct, const OutputDevice& rOut )
1013 tools::Rectangle aActionBounds;
1015 switch( rAct.GetType() )
1017 case MetaActionType::PIXEL:
1018 aActionBounds = tools::Rectangle( static_cast<const MetaPixelAction&>(rAct).GetPoint(), Size( 1, 1 ) );
1019 break;
1021 case MetaActionType::POINT:
1022 aActionBounds = tools::Rectangle( static_cast<const MetaPointAction&>(rAct).GetPoint(), Size( 1, 1 ) );
1023 break;
1025 case MetaActionType::LINE:
1027 const MetaLineAction& rMetaLineAction = static_cast<const MetaLineAction&>(rAct);
1028 aActionBounds = tools::Rectangle( rMetaLineAction.GetStartPoint(), rMetaLineAction.GetEndPoint() );
1029 aActionBounds.Normalize();
1030 const tools::Long nLineWidth(rMetaLineAction.GetLineInfo().GetWidth());
1031 if(nLineWidth)
1033 const tools::Long nHalfLineWidth((nLineWidth + 1) / 2);
1034 aActionBounds.AdjustLeft( -nHalfLineWidth );
1035 aActionBounds.AdjustTop( -nHalfLineWidth );
1036 aActionBounds.AdjustRight(nHalfLineWidth );
1037 aActionBounds.AdjustBottom(nHalfLineWidth );
1039 break;
1042 case MetaActionType::RECT:
1043 aActionBounds = static_cast<const MetaRectAction&>(rAct).GetRect();
1044 break;
1046 case MetaActionType::ROUNDRECT:
1047 aActionBounds = tools::Polygon( static_cast<const MetaRoundRectAction&>(rAct).GetRect(),
1048 static_cast<const MetaRoundRectAction&>(rAct).GetHorzRound(),
1049 static_cast<const MetaRoundRectAction&>(rAct).GetVertRound() ).GetBoundRect();
1050 break;
1052 case MetaActionType::ELLIPSE:
1054 const tools::Rectangle& rRect = static_cast<const MetaEllipseAction&>(rAct).GetRect();
1055 aActionBounds = tools::Polygon( rRect.Center(),
1056 rRect.GetWidth() >> 1,
1057 rRect.GetHeight() >> 1 ).GetBoundRect();
1058 break;
1061 case MetaActionType::ARC:
1062 aActionBounds = tools::Polygon( static_cast<const MetaArcAction&>(rAct).GetRect(),
1063 static_cast<const MetaArcAction&>(rAct).GetStartPoint(),
1064 static_cast<const MetaArcAction&>(rAct).GetEndPoint(), PolyStyle::Arc ).GetBoundRect();
1065 break;
1067 case MetaActionType::PIE:
1068 aActionBounds = tools::Polygon( static_cast<const MetaPieAction&>(rAct).GetRect(),
1069 static_cast<const MetaPieAction&>(rAct).GetStartPoint(),
1070 static_cast<const MetaPieAction&>(rAct).GetEndPoint(), PolyStyle::Pie ).GetBoundRect();
1071 break;
1073 case MetaActionType::CHORD:
1074 aActionBounds = tools::Polygon( static_cast<const MetaChordAction&>(rAct).GetRect(),
1075 static_cast<const MetaChordAction&>(rAct).GetStartPoint(),
1076 static_cast<const MetaChordAction&>(rAct).GetEndPoint(), PolyStyle::Chord ).GetBoundRect();
1077 break;
1079 case MetaActionType::POLYLINE:
1081 const MetaPolyLineAction& rMetaPolyLineAction = static_cast<const MetaPolyLineAction&>(rAct);
1082 aActionBounds = rMetaPolyLineAction.GetPolygon().GetBoundRect();
1083 const tools::Long nLineWidth(rMetaPolyLineAction.GetLineInfo().GetWidth());
1084 if(nLineWidth)
1086 const tools::Long nHalfLineWidth((nLineWidth + 1) / 2);
1087 aActionBounds.AdjustLeft( -nHalfLineWidth );
1088 aActionBounds.AdjustTop( -nHalfLineWidth );
1089 aActionBounds.AdjustRight(nHalfLineWidth );
1090 aActionBounds.AdjustBottom(nHalfLineWidth );
1092 break;
1095 case MetaActionType::POLYGON:
1096 aActionBounds = static_cast<const MetaPolygonAction&>(rAct).GetPolygon().GetBoundRect();
1097 break;
1099 case MetaActionType::POLYPOLYGON:
1100 aActionBounds = static_cast<const MetaPolyPolygonAction&>(rAct).GetPolyPolygon().GetBoundRect();
1101 break;
1103 case MetaActionType::BMP:
1104 aActionBounds = tools::Rectangle( static_cast<const MetaBmpAction&>(rAct).GetPoint(),
1105 rOut.PixelToLogic( static_cast<const MetaBmpAction&>(rAct).GetBitmap().GetSizePixel() ) );
1106 break;
1108 case MetaActionType::BMPSCALE:
1109 aActionBounds = tools::Rectangle( static_cast<const MetaBmpScaleAction&>(rAct).GetPoint(),
1110 static_cast<const MetaBmpScaleAction&>(rAct).GetSize() );
1111 break;
1113 case MetaActionType::BMPSCALEPART:
1114 aActionBounds = tools::Rectangle( static_cast<const MetaBmpScalePartAction&>(rAct).GetDestPoint(),
1115 static_cast<const MetaBmpScalePartAction&>(rAct).GetDestSize() );
1116 break;
1118 case MetaActionType::BMPEX:
1119 aActionBounds = tools::Rectangle( static_cast<const MetaBmpExAction&>(rAct).GetPoint(),
1120 rOut.PixelToLogic( static_cast<const MetaBmpExAction&>(rAct).GetBitmapEx().GetSizePixel() ) );
1121 break;
1123 case MetaActionType::BMPEXSCALE:
1124 aActionBounds = tools::Rectangle( static_cast<const MetaBmpExScaleAction&>(rAct).GetPoint(),
1125 static_cast<const MetaBmpExScaleAction&>(rAct).GetSize() );
1126 break;
1128 case MetaActionType::BMPEXSCALEPART:
1129 aActionBounds = tools::Rectangle( static_cast<const MetaBmpExScalePartAction&>(rAct).GetDestPoint(),
1130 static_cast<const MetaBmpExScalePartAction&>(rAct).GetDestSize() );
1131 break;
1133 case MetaActionType::MASK:
1134 aActionBounds = tools::Rectangle( static_cast<const MetaMaskAction&>(rAct).GetPoint(),
1135 rOut.PixelToLogic( static_cast<const MetaMaskAction&>(rAct).GetBitmap().GetSizePixel() ) );
1136 break;
1138 case MetaActionType::MASKSCALE:
1139 aActionBounds = tools::Rectangle( static_cast<const MetaMaskScaleAction&>(rAct).GetPoint(),
1140 static_cast<const MetaMaskScaleAction&>(rAct).GetSize() );
1141 break;
1143 case MetaActionType::MASKSCALEPART:
1144 aActionBounds = tools::Rectangle( static_cast<const MetaMaskScalePartAction&>(rAct).GetDestPoint(),
1145 static_cast<const MetaMaskScalePartAction&>(rAct).GetDestSize() );
1146 break;
1148 case MetaActionType::GRADIENT:
1149 aActionBounds = static_cast<const MetaGradientAction&>(rAct).GetRect();
1150 break;
1152 case MetaActionType::GRADIENTEX:
1153 aActionBounds = static_cast<const MetaGradientExAction&>(rAct).GetPolyPolygon().GetBoundRect();
1154 break;
1156 case MetaActionType::HATCH:
1157 aActionBounds = static_cast<const MetaHatchAction&>(rAct).GetPolyPolygon().GetBoundRect();
1158 break;
1160 case MetaActionType::WALLPAPER:
1161 aActionBounds = static_cast<const MetaWallpaperAction&>(rAct).GetRect();
1162 break;
1164 case MetaActionType::Transparent:
1165 aActionBounds = static_cast<const MetaTransparentAction&>(rAct).GetPolyPolygon().GetBoundRect();
1166 break;
1168 case MetaActionType::FLOATTRANSPARENT:
1169 aActionBounds = tools::Rectangle( static_cast<const MetaFloatTransparentAction&>(rAct).GetPoint(),
1170 static_cast<const MetaFloatTransparentAction&>(rAct).GetSize() );
1171 break;
1173 case MetaActionType::EPS:
1174 aActionBounds = tools::Rectangle( static_cast<const MetaEPSAction&>(rAct).GetPoint(),
1175 static_cast<const MetaEPSAction&>(rAct).GetSize() );
1176 break;
1178 case MetaActionType::TEXT:
1180 const MetaTextAction& rTextAct = static_cast<const MetaTextAction&>(rAct);
1181 const OUString aString( rTextAct.GetText().copy(rTextAct.GetIndex(), rTextAct.GetLen()) );
1183 if (!aString.isEmpty())
1185 const Point aPtLog( rTextAct.GetPoint() );
1187 // #105987# Use API method instead of Impl* methods
1188 // #107490# Set base parameter equal to index parameter
1189 rOut.GetTextBoundRect( aActionBounds, rTextAct.GetText(), rTextAct.GetIndex(),
1190 rTextAct.GetIndex(), rTextAct.GetLen() );
1191 aActionBounds.Move( aPtLog.X(), aPtLog.Y() );
1194 break;
1196 case MetaActionType::TEXTARRAY:
1198 const MetaTextArrayAction& rTextAct = static_cast<const MetaTextArrayAction&>(rAct);
1199 const OUString aString( rTextAct.GetText().copy(rTextAct.GetIndex(), rTextAct.GetLen()) );
1201 if( !aString.isEmpty() )
1203 // #105987# ImplLayout takes everything in logical coordinates
1204 std::unique_ptr<SalLayout> pSalLayout;
1205 if (rTextAct.GetLayoutContextIndex() >= 0)
1207 pSalLayout = rOut.ImplLayout(
1208 rTextAct.GetText(), rTextAct.GetLayoutContextIndex(),
1209 rTextAct.GetLayoutContextLen(), rTextAct.GetPoint(), 0,
1210 rTextAct.GetDXArray(), rTextAct.GetKashidaArray(), SalLayoutFlags::NONE,
1211 /*pTextLayoutCache=*/nullptr,
1212 /*pGlyphs=*/nullptr,
1213 /*nDrawOriginCluster=*/rTextAct.GetIndex(),
1214 /*nDrawMinCharPos=*/rTextAct.GetIndex(),
1215 /*nDrawEndCharPos=*/rTextAct.GetIndex() + rTextAct.GetLen());
1217 else
1219 pSalLayout = rOut.ImplLayout(rTextAct.GetText(), rTextAct.GetIndex(),
1220 rTextAct.GetLen(), rTextAct.GetPoint(), 0,
1221 rTextAct.GetDXArray(), rTextAct.GetKashidaArray());
1224 if( pSalLayout )
1226 tools::Rectangle aBoundRect( rOut.ImplGetTextBoundRect( *pSalLayout ) );
1227 aActionBounds = rOut.PixelToLogic( aBoundRect );
1231 break;
1233 case MetaActionType::TEXTRECT:
1234 aActionBounds = static_cast<const MetaTextRectAction&>(rAct).GetRect();
1235 break;
1237 case MetaActionType::STRETCHTEXT:
1239 const MetaStretchTextAction& rTextAct = static_cast<const MetaStretchTextAction&>(rAct);
1240 const OUString aString( rTextAct.GetText().copy(rTextAct.GetIndex(), rTextAct.GetLen()) );
1242 // #i16195# Literate copy from TextArray action, the
1243 // semantics for the ImplLayout call are copied from the
1244 // OutDev::DrawStretchText() code. Unfortunately, also in
1245 // this case, public outdev methods such as GetTextWidth()
1246 // don't provide enough info.
1247 if( !aString.isEmpty() )
1249 // #105987# ImplLayout takes everything in logical coordinates
1250 std::unique_ptr<SalLayout> pSalLayout = rOut.ImplLayout( rTextAct.GetText(), rTextAct.GetIndex(),
1251 rTextAct.GetLen(), rTextAct.GetPoint(),
1252 rTextAct.GetWidth() );
1253 if( pSalLayout )
1255 tools::Rectangle aBoundRect( rOut.ImplGetTextBoundRect( *pSalLayout ) );
1256 aActionBounds = rOut.PixelToLogic( aBoundRect );
1260 break;
1262 case MetaActionType::TEXTLINE:
1263 OSL_FAIL("MetaActionType::TEXTLINE not supported");
1264 break;
1266 default:
1267 break;
1270 if( !aActionBounds.IsEmpty() )
1272 // fdo#40421 limit current action's output to clipped area
1273 if( rOut.IsClipRegion() )
1274 return rOut.LogicToPixel(
1275 rOut.GetClipRegion().GetBoundRect().Intersection( aActionBounds ) );
1276 else
1277 return rOut.LogicToPixel( aActionBounds );
1279 else
1280 return tools::Rectangle();
1283 } // end anon namespace
1285 // TODO: this massive function operates on metafiles, so eventually it should probably
1286 // be shifted to the GDIMetaFile class
1287 bool OutputDevice::RemoveTransparenciesFromMetaFile( const GDIMetaFile& rInMtf, GDIMetaFile& rOutMtf,
1288 tools::Long nMaxBmpDPIX, tools::Long nMaxBmpDPIY,
1289 bool bReduceTransparency, bool bTransparencyAutoMode,
1290 bool bDownsampleBitmaps,
1291 const Color& rBackground
1294 MetaAction* pCurrAct;
1295 bool bTransparent( false );
1297 rOutMtf.Clear();
1299 #ifdef MACOSX
1300 // tdf#164354 skip unnecessary transparency removal
1301 // On macOS, there are no known problems drawing semi-transparent
1302 // shapes, fill, text, and bitmaps so only do this sometimes very
1303 // expensive step when both reduce transparency and no
1304 // transparency are true.
1305 if(bReduceTransparency && !bTransparencyAutoMode)
1306 #else
1307 if(!bReduceTransparency || bTransparencyAutoMode)
1308 #endif
1309 bTransparent = rInMtf.HasTransparentActions();
1311 // #i10613# Determine set of connected components containing transparent objects. These are
1312 // then processed as bitmaps, the original actions are removed from the metafile.
1313 if( !bTransparent )
1315 // nothing transparent -> just copy
1316 rOutMtf = rInMtf;
1318 else
1320 // #i10613#
1321 // This works as follows: we want a number of distinct sets of
1322 // connected components, where each set contains metafile
1323 // actions that are intersecting (note: there are possibly
1324 // more actions contained as are directly intersecting,
1325 // because we can only produce rectangular bitmaps later
1326 // on. Thus, each set of connected components is the smallest
1327 // enclosing, axis-aligned rectangle that completely bounds a
1328 // number of intersecting metafile actions, plus any action
1329 // that would otherwise be cut in two). Therefore, we
1330 // iteratively add metafile actions from the original metafile
1331 // to this connected components list (aCCList), by checking
1332 // each element's bounding box against intersection with the
1333 // metaaction at hand.
1334 // All those intersecting elements are removed from aCCList
1335 // and collected in a temporary list (aCCMergeList). After all
1336 // elements have been checked, the aCCMergeList elements are
1337 // merged with the metaaction at hand into one resulting
1338 // connected component, with one big bounding box, and
1339 // inserted into aCCList again.
1340 // The time complexity of this algorithm is O(n^3), where n is
1341 // the number of metafile actions, and it finds all distinct
1342 // regions of rectangle-bounded connected components. This
1343 // algorithm was designed by AF.
1345 // STAGE 1: Detect background
1347 // Receives uniform background content, and is _not_ merged
1348 // nor checked for intersection against other aCCList elements
1349 ConnectedComponents aBackgroundComponent;
1351 // Read the configuration value of minimal object area where transparency will be removed
1352 double fReduceTransparencyMinArea = officecfg::Office::Common::VCL::ReduceTransparencyMinArea::get() / 100.0;
1353 SAL_WARN_IF(fReduceTransparencyMinArea > 1.0, "vcl",
1354 "Value of ReduceTransparencyMinArea config option is too high");
1355 SAL_WARN_IF(fReduceTransparencyMinArea < 0.0, "vcl",
1356 "Value of ReduceTransparencyMinArea config option is too low");
1357 fReduceTransparencyMinArea = std::clamp(fReduceTransparencyMinArea, 0.0, 1.0);
1359 // create an OutputDevice to record mapmode changes and the like
1360 ScopedVclPtrInstance< VirtualDevice > aMapModeVDev;
1361 aMapModeVDev->mnDPIX = mnDPIX;
1362 aMapModeVDev->mnDPIY = mnDPIY;
1363 aMapModeVDev->EnableOutput(false);
1365 // weed out page-filling background objects (if they are
1366 // uniformly coloured). Keeping them outside the other
1367 // connected components often prevents whole-page bitmap
1368 // generation.
1369 bool bStillBackground=true; // true until first non-bg action
1370 int nActionNum = 0, nLastBgAction = -1;
1371 pCurrAct=const_cast<GDIMetaFile&>(rInMtf).FirstAction();
1372 if( rBackground != COL_TRANSPARENT )
1374 aBackgroundComponent.aBgColor = rBackground;
1375 aBackgroundComponent.aBounds = GetBackgroundComponentBounds();
1377 while( pCurrAct && bStillBackground )
1379 switch( pCurrAct->GetType() )
1381 case MetaActionType::RECT:
1383 if( !checkRect(
1384 aBackgroundComponent.aBounds,
1385 aBackgroundComponent.aBgColor,
1386 static_cast<const MetaRectAction*>(pCurrAct)->GetRect(),
1387 *aMapModeVDev) )
1388 bStillBackground=false; // incomplete occlusion of background
1389 else
1390 nLastBgAction=nActionNum; // this _is_ background
1391 break;
1393 case MetaActionType::POLYGON:
1395 const tools::Polygon aPoly(
1396 static_cast<const MetaPolygonAction*>(pCurrAct)->GetPolygon());
1397 if( !basegfx::utils::isRectangle(
1398 aPoly.getB2DPolygon()) ||
1399 !checkRect(
1400 aBackgroundComponent.aBounds,
1401 aBackgroundComponent.aBgColor,
1402 aPoly.GetBoundRect(),
1403 *aMapModeVDev) )
1404 bStillBackground=false; // incomplete occlusion of background
1405 else
1406 nLastBgAction=nActionNum; // this _is_ background
1407 break;
1409 case MetaActionType::POLYPOLYGON:
1411 const tools::PolyPolygon aPoly(
1412 static_cast<const MetaPolyPolygonAction*>(pCurrAct)->GetPolyPolygon());
1413 if( aPoly.Count() != 1 ||
1414 !basegfx::utils::isRectangle(
1415 aPoly[0].getB2DPolygon()) ||
1416 !checkRect(
1417 aBackgroundComponent.aBounds,
1418 aBackgroundComponent.aBgColor,
1419 aPoly.GetBoundRect(),
1420 *aMapModeVDev) )
1421 bStillBackground=false; // incomplete occlusion of background
1422 else
1423 nLastBgAction=nActionNum; // this _is_ background
1424 break;
1426 case MetaActionType::WALLPAPER:
1428 if( !checkRect(
1429 aBackgroundComponent.aBounds,
1430 aBackgroundComponent.aBgColor,
1431 static_cast<const MetaWallpaperAction*>(pCurrAct)->GetRect(),
1432 *aMapModeVDev) )
1433 bStillBackground=false; // incomplete occlusion of background
1434 else
1435 nLastBgAction=nActionNum; // this _is_ background
1436 break;
1438 default:
1440 if( ImplIsNotTransparent( *pCurrAct,
1441 *aMapModeVDev ) )
1442 bStillBackground=false; // non-transparent action, possibly
1443 // not uniform
1444 else
1445 // extend current bounds (next uniform action
1446 // needs to fully cover this area)
1447 aBackgroundComponent.aBounds.Union(
1448 ImplCalcActionBounds(*pCurrAct, *aMapModeVDev) );
1449 break;
1453 // execute action to get correct MapModes etc.
1454 pCurrAct->Execute( aMapModeVDev.get() );
1456 pCurrAct=const_cast<GDIMetaFile&>(rInMtf).NextAction();
1457 ++nActionNum;
1460 if (nLastBgAction != -1)
1462 size_t nActionSize = rInMtf.GetActionSize();
1463 // tdf#134736 move nLastBgAction to also include any trailing pops
1464 for (size_t nPostLastBgAction = nLastBgAction + 1; nPostLastBgAction < nActionSize; ++nPostLastBgAction)
1466 if (rInMtf.GetAction(nPostLastBgAction)->GetType() != MetaActionType::POP)
1467 break;
1468 nLastBgAction = nPostLastBgAction;
1472 aMapModeVDev->ClearStack(); // clean up aMapModeVDev
1474 // fast-forward until one after the last background action
1475 // (need to reconstruct map mode vdev state)
1476 nActionNum=0;
1477 pCurrAct=const_cast<GDIMetaFile&>(rInMtf).FirstAction();
1478 while( pCurrAct && nActionNum<=nLastBgAction )
1480 // up to and including last ink-generating background
1481 // action go to background component
1482 aBackgroundComponent.aComponentList.emplace_back(
1483 pCurrAct, nActionNum );
1485 // execute action to get correct MapModes etc.
1486 pCurrAct->Execute( aMapModeVDev.get() );
1487 pCurrAct=const_cast<GDIMetaFile&>(rInMtf).NextAction();
1488 ++nActionNum;
1491 // STAGE 2: Generate connected components list
1493 ::std::vector<ConnectedComponents> aCCList; // contains distinct sets of connected components as elements.
1495 // iterate over all actions (start where background action
1496 // search left off)
1497 for( ;
1498 pCurrAct;
1499 pCurrAct=const_cast<GDIMetaFile&>(rInMtf).NextAction(), ++nActionNum )
1501 // execute action to get correct MapModes etc.
1502 pCurrAct->Execute( aMapModeVDev.get() );
1504 // cache bounds of current action
1505 const tools::Rectangle aBBCurrAct( ImplCalcActionBounds(*pCurrAct, *aMapModeVDev) );
1507 // accumulate collected bounds here, initialize with current action
1508 tools::Rectangle aTotalBounds( aBBCurrAct ); // thus, aTotalComponents.aBounds is empty
1509 // for non-output-generating actions
1510 bool bTreatSpecial( false );
1511 ConnectedComponents aTotalComponents;
1513 // STAGE 2.1: Search for intersecting cc entries
1515 // if aBBCurrAct is empty, it will intersect with no
1516 // aCCList member. Thus, we can save the check.
1517 // Furthermore, this ensures that non-output-generating
1518 // actions get their own aCCList entry, which is necessary
1519 // when copying them to the output metafile (see stage 4
1520 // below).
1522 // #107169# Wholly transparent objects need
1523 // not be considered for connected components,
1524 // too. Just put each of them into a separate
1525 // component.
1526 aTotalComponents.bIsFullyTransparent = !ImplIsNotTransparent(*pCurrAct, *aMapModeVDev);
1528 if( !aBBCurrAct.IsEmpty() &&
1529 !aTotalComponents.bIsFullyTransparent )
1531 if( !aBackgroundComponent.aComponentList.empty() &&
1532 !aBackgroundComponent.aBounds.Contains(aTotalBounds) )
1534 // it seems the background is not large enough. to
1535 // be on the safe side, combine with this component.
1536 aTotalBounds.Union( aBackgroundComponent.aBounds );
1538 // extract all aCurr actions to aTotalComponents
1539 aTotalComponents.aComponentList.splice( aTotalComponents.aComponentList.end(),
1540 aBackgroundComponent.aComponentList );
1542 if( aBackgroundComponent.bIsSpecial )
1543 bTreatSpecial = true;
1546 bool bSomeComponentsChanged;
1548 // now, this is unfortunate: since changing anyone of
1549 // the aCCList elements (e.g. by merging or addition
1550 // of an action) might generate new intersection with
1551 // other aCCList elements, have to repeat the whole
1552 // element scanning, until nothing changes anymore.
1553 // Thus, this loop here makes us O(n^3) in the worst
1554 // case.
1557 // only loop here if 'intersects' branch below was hit
1558 bSomeComponentsChanged = false;
1560 // iterate over all current members of aCCList
1561 for( auto aCurrCC=aCCList.begin(); aCurrCC != aCCList.end(); )
1563 // first check if current element's bounds are
1564 // empty. This ensures that empty actions are not
1565 // merged into one component, as a matter of fact,
1566 // they have no position.
1568 // #107169# Wholly transparent objects need
1569 // not be considered for connected components,
1570 // too. Just put each of them into a separate
1571 // component.
1572 if( !aCurrCC->aBounds.IsEmpty() &&
1573 !aCurrCC->bIsFullyTransparent &&
1574 aCurrCC->aBounds.Overlaps( aTotalBounds ) )
1576 // union the intersecting aCCList element into aTotalComponents
1578 // calc union bounding box
1579 aTotalBounds.Union( aCurrCC->aBounds );
1581 // extract all aCurr actions to aTotalComponents
1582 aTotalComponents.aComponentList.splice( aTotalComponents.aComponentList.end(),
1583 aCurrCC->aComponentList );
1585 if( aCurrCC->bIsSpecial )
1586 bTreatSpecial = true;
1588 // remove and delete aCurrCC element from list (we've now merged its content)
1589 aCurrCC = aCCList.erase( aCurrCC );
1591 // at least one component changed, need to rescan everything
1592 bSomeComponentsChanged = true;
1594 else
1596 ++aCurrCC;
1600 while( bSomeComponentsChanged );
1603 // STAGE 2.2: Determine special state for cc element
1605 // now test whether the whole connected component must be
1606 // treated specially (i.e. rendered as a bitmap): if the
1607 // added action is the very first action, or all actions
1608 // before it are completely transparent, the connected
1609 // component need not be treated specially, not even if
1610 // the added action contains transparency. This is because
1611 // painting of transparent objects on _white background_
1612 // works without alpha compositing (you just calculate the
1613 // color). Note that for the test "all objects before me
1614 // are transparent" no sorting is necessary, since the
1615 // added metaaction pCurrAct is always in the order the
1616 // metafile is painted. Generally, the order of the
1617 // metaactions in the ConnectedComponents are not
1618 // guaranteed to be the same as in the metafile.
1619 if( bTreatSpecial )
1621 // prev component(s) special -> this one, too
1622 aTotalComponents.bIsSpecial = true;
1624 else if(!pCurrAct->IsTransparent())
1626 // added action and none of prev components special ->
1627 // this one normal, too
1628 aTotalComponents.bIsSpecial = false;
1630 else
1632 // added action is special and none of prev components
1633 // special -> do the detailed tests
1635 // can the action handle transparency correctly
1636 // (i.e. when painted on white background, does the
1637 // action still look correct)?
1638 if( !DoesActionHandleTransparency( *pCurrAct ) )
1640 // no, action cannot handle its transparency on
1641 // a printer device, render to bitmap
1642 aTotalComponents.bIsSpecial = true;
1644 else
1646 // yes, action can handle its transparency, so
1647 // check whether we're on white background
1648 if( aTotalComponents.aComponentList.empty() )
1650 // nothing between pCurrAct and page
1651 // background -> don't be special
1652 aTotalComponents.bIsSpecial = false;
1654 else
1656 // #107169# Fixes above now ensure that _no_
1657 // object in the list is fully transparent. Thus,
1658 // if the component list is not empty above, we
1659 // must assume that we have to treat this
1660 // component special.
1662 // there are non-transparent objects between
1663 // pCurrAct and the empty sheet of paper -> be
1664 // special, then
1665 aTotalComponents.bIsSpecial = true;
1670 // STAGE 2.3: Add newly generated CC list element
1672 // set new bounds and add action to list
1673 aTotalComponents.aBounds = aTotalBounds;
1674 aTotalComponents.aComponentList.emplace_back(
1675 pCurrAct, nActionNum );
1677 // add aTotalComponents as a new entry to aCCList
1678 aCCList.push_back( aTotalComponents );
1680 SAL_WARN_IF( aTotalComponents.aComponentList.empty(), "vcl",
1681 "Printer::GetPreparedMetaFile empty component" );
1682 SAL_WARN_IF( aTotalComponents.aBounds.IsEmpty() && (aTotalComponents.aComponentList.size() != 1), "vcl",
1683 "Printer::GetPreparedMetaFile non-output generating actions must be solitary");
1684 SAL_WARN_IF( aTotalComponents.bIsFullyTransparent && (aTotalComponents.aComponentList.size() != 1), "vcl",
1685 "Printer::GetPreparedMetaFile fully transparent actions must be solitary");
1688 // well now, we've got the list of disjunct connected
1689 // components. Now we've got to create a map, which contains
1690 // the corresponding aCCList element for every
1691 // metaaction. Later on, we always process the complete
1692 // metafile for each bitmap to be generated, but switch on
1693 // output only for actions contained in the then current
1694 // aCCList element. This ensures correct mapmode and attribute
1695 // settings for all cases.
1697 // maps mtf actions to CC list entries
1698 ::std::vector< const ConnectedComponents* > aCCList_MemberMap( rInMtf.GetActionSize() );
1700 // iterate over all aCCList members and their contained metaactions
1701 for (auto const& currentItem : aCCList)
1703 for (auto const& currentAction : currentItem.aComponentList)
1705 // set pointer to aCCList element for corresponding index
1706 aCCList_MemberMap[ currentAction.second ] = &currentItem;
1710 // STAGE 3.1: Output background mtf actions (if there are any)
1712 for (auto & component : aBackgroundComponent.aComponentList)
1714 // simply add this action (above, we inserted the actions
1715 // starting at index 0 up to and including nLastBgAction)
1716 rOutMtf.AddAction( component.first );
1719 // STAGE 3.2: Generate banded bitmaps for special regions
1721 Point aPageOffset;
1722 Size aTmpSize( GetOutputSizePixel() );
1723 if( meOutDevType == OUTDEV_PDF )
1725 auto pPdfWriter = static_cast<vcl::PDFWriterImpl*>(this);
1726 aTmpSize = LogicToPixel(pPdfWriter->getCurPageSize(), MapMode(MapUnit::MapPoint));
1728 // also add error code to PDFWriter
1729 pPdfWriter->insertError(vcl::PDFWriter::Warning_Transparency_Converted);
1731 else if( meOutDevType == OUTDEV_PRINTER )
1733 Printer* pThis = dynamic_cast<Printer*>(this);
1734 assert(pThis);
1735 aPageOffset = pThis->GetPageOffsetPixel();
1736 aPageOffset = Point( 0, 0 ) - aPageOffset;
1737 aTmpSize = pThis->GetPaperSizePixel();
1739 const tools::Rectangle aOutputRect( aPageOffset, aTmpSize );
1740 bool bTiling = dynamic_cast<Printer*>(this) != nullptr;
1742 // iterate over all aCCList members and generate bitmaps for the special ones
1743 for (auto & currentItem : aCCList)
1745 if( currentItem.bIsSpecial )
1747 tools::Rectangle aBoundRect( currentItem.aBounds );
1748 aBoundRect.Intersection( aOutputRect );
1750 const double fBmpArea( static_cast<double>(aBoundRect.GetWidth()) * aBoundRect.GetHeight() );
1751 const double fOutArea( static_cast<double>(aOutputRect.GetWidth()) * aOutputRect.GetHeight() );
1753 // check if output doesn't exceed given size
1754 if( bReduceTransparency && bTransparencyAutoMode && ( fBmpArea > ( fReduceTransparencyMinArea * fOutArea ) ) )
1756 // output normally. Therefore, we simply clear the
1757 // special attribute, as everything non-special is
1758 // copied to rOutMtf further below.
1759 currentItem.bIsSpecial = false;
1761 else
1763 // create new bitmap action first
1764 if( aBoundRect.GetWidth() && aBoundRect.GetHeight() )
1766 Point aDstPtPix( aBoundRect.TopLeft() );
1767 Size aDstSzPix;
1769 ScopedVclPtrInstance<VirtualDevice> aMapVDev; // here, we record only mapmode information
1770 aMapVDev->EnableOutput(false);
1772 ScopedVclPtrInstance<VirtualDevice> aPaintVDev; // into this one, we render.
1773 aPaintVDev->SetBackground( aBackgroundComponent.aBgColor );
1775 rOutMtf.AddAction( new MetaPushAction( vcl::PushFlags::MAPMODE ) );
1776 rOutMtf.AddAction( new MetaMapModeAction() );
1778 aPaintVDev->SetDrawMode( GetDrawMode() );
1780 while( aDstPtPix.Y() <= aBoundRect.Bottom() )
1782 aDstPtPix.setX( aBoundRect.Left() );
1783 aDstSzPix = bTiling ? Size( MAX_TILE_WIDTH, MAX_TILE_HEIGHT ) : aBoundRect.GetSize();
1785 if( ( aDstPtPix.Y() + aDstSzPix.Height() - 1 ) > aBoundRect.Bottom() )
1786 aDstSzPix.setHeight( aBoundRect.Bottom() - aDstPtPix.Y() + 1 );
1788 while( aDstPtPix.X() <= aBoundRect.Right() )
1790 if( ( aDstPtPix.X() + aDstSzPix.Width() - 1 ) > aBoundRect.Right() )
1791 aDstSzPix.setWidth( aBoundRect.Right() - aDstPtPix.X() + 1 );
1793 if( !tools::Rectangle( aDstPtPix, aDstSzPix ).Intersection( aBoundRect ).IsEmpty() &&
1794 aPaintVDev->SetOutputSizePixel( aDstSzPix ) )
1796 aPaintVDev->Push();
1797 aMapVDev->Push();
1799 aMapVDev->mnDPIX = aPaintVDev->mnDPIX = mnDPIX;
1800 aMapVDev->mnDPIY = aPaintVDev->mnDPIY = mnDPIY;
1802 aPaintVDev->EnableOutput(false);
1804 // iterate over all actions
1805 for( pCurrAct=const_cast<GDIMetaFile&>(rInMtf).FirstAction(), nActionNum=0;
1806 pCurrAct;
1807 pCurrAct=const_cast<GDIMetaFile&>(rInMtf).NextAction(), ++nActionNum )
1809 // enable output only for
1810 // actions that are members of
1811 // the current aCCList element
1812 // (currentItem)
1813 if( aCCList_MemberMap[nActionNum] == &currentItem )
1814 aPaintVDev->EnableOutput();
1816 // but process every action
1817 const MetaActionType nType( pCurrAct->GetType() );
1819 if( MetaActionType::MAPMODE == nType )
1821 pCurrAct->Execute( aMapVDev.get() );
1823 MapMode aMtfMap( aMapVDev->GetMapMode() );
1824 const Point aNewOrg( aMapVDev->PixelToLogic( aDstPtPix ) );
1826 aMtfMap.SetOrigin( Point( -aNewOrg.X(), -aNewOrg.Y() ) );
1827 aPaintVDev->SetMapMode( aMtfMap );
1829 else if( ( MetaActionType::PUSH == nType ) || MetaActionType::POP == nType )
1831 pCurrAct->Execute( aMapVDev.get() );
1832 pCurrAct->Execute( aPaintVDev.get() );
1834 else if( MetaActionType::GRADIENT == nType )
1836 MetaGradientAction* pGradientAction = static_cast<MetaGradientAction*>(pCurrAct);
1837 Printer* pPrinter = dynamic_cast< Printer* >(this);
1838 if( pPrinter )
1839 pPrinter->DrawGradientEx( aPaintVDev.get(), pGradientAction->GetRect(), pGradientAction->GetGradient() );
1840 else
1841 DrawGradient( pGradientAction->GetRect(), pGradientAction->GetGradient() );
1843 else
1845 pCurrAct->Execute( aPaintVDev.get() );
1848 Application::Reschedule( true );
1851 const bool bOldMap = mbMap;
1852 mbMap = aPaintVDev->mbMap = false;
1854 Bitmap aBandBmp( aPaintVDev->GetBitmap( Point(), aDstSzPix ) );
1856 // scale down bitmap, if requested
1857 if( bDownsampleBitmaps )
1858 aBandBmp = vcl::bitmap::GetDownsampledBitmap(PixelToLogic(LogicToPixel(aDstSzPix), MapMode(MapUnit::MapTwip)),
1859 Point(), aBandBmp.GetSizePixel(),
1860 aBandBmp, nMaxBmpDPIX, nMaxBmpDPIY);
1862 rOutMtf.AddAction( new MetaCommentAction( "PRNSPOOL_TRANSPARENTBITMAP_BEGIN"_ostr ) );
1863 rOutMtf.AddAction( new MetaBmpScaleAction( aDstPtPix, aDstSzPix, aBandBmp ) );
1864 rOutMtf.AddAction( new MetaCommentAction( "PRNSPOOL_TRANSPARENTBITMAP_END"_ostr ) );
1866 aPaintVDev->mbMap = true;
1867 mbMap = bOldMap;
1868 aMapVDev->Pop();
1869 aPaintVDev->Pop();
1872 // overlapping bands to avoid missing lines (e.g. PostScript)
1873 aDstPtPix.AdjustX(aDstSzPix.Width() );
1876 // overlapping bands to avoid missing lines (e.g. PostScript)
1877 aDstPtPix.AdjustY(aDstSzPix.Height() );
1880 rOutMtf.AddAction( new MetaPopAction() );
1886 aMapModeVDev->ClearStack(); // clean up aMapModeVDev
1888 // STAGE 4: Copy actions to output metafile
1890 // iterate over all actions and duplicate the ones not in a
1891 // special aCCList member into rOutMtf
1892 for( pCurrAct=const_cast<GDIMetaFile&>(rInMtf).FirstAction(), nActionNum=0;
1893 pCurrAct;
1894 pCurrAct=const_cast<GDIMetaFile&>(rInMtf).NextAction(), ++nActionNum )
1896 const ConnectedComponents* pCurrAssociatedComponent = aCCList_MemberMap[nActionNum];
1898 // NOTE: This relies on the fact that map-mode or draw
1899 // mode changing actions are solitary aCCList elements and
1900 // have empty bounding boxes, see comment on stage 2.1
1901 // above
1902 if( pCurrAssociatedComponent &&
1903 (pCurrAssociatedComponent->aBounds.IsEmpty() ||
1904 !pCurrAssociatedComponent->bIsSpecial) )
1906 // #107169# Treat transparent bitmaps special, if they
1907 // are the first (or sole) action in their bounds
1908 // list. Note that we previously ensured that no
1909 // fully-transparent objects are before us here.
1910 if( DoesActionHandleTransparency( *pCurrAct ) &&
1911 pCurrAssociatedComponent->aComponentList.begin()->first == pCurrAct )
1913 // convert actions, where masked-out parts are of
1914 // given background color
1915 ImplConvertTransparentAction(rOutMtf,
1916 *pCurrAct,
1917 *aMapModeVDev,
1918 aBackgroundComponent.aBgColor);
1920 else
1922 // simply add this action
1923 rOutMtf.AddAction( pCurrAct );
1926 pCurrAct->Execute(aMapModeVDev.get());
1930 rOutMtf.SetPrefMapMode( rInMtf.GetPrefMapMode() );
1931 rOutMtf.SetPrefSize( rInMtf.GetPrefSize() );
1933 #if OSL_DEBUG_LEVEL > 1
1934 // iterate over all aCCList members and generate rectangles for the bounding boxes
1935 rOutMtf.AddAction( new MetaFillColorAction( COL_WHITE, false ) );
1936 for(auto const& aCurr:aCCList)
1938 if( aCurr.bIsSpecial )
1939 rOutMtf.AddAction( new MetaLineColorAction( COL_RED, true) );
1940 else
1941 rOutMtf.AddAction( new MetaLineColorAction( COL_BLUE, true) );
1943 rOutMtf.AddAction( new MetaRectAction( aMapModeVDev->PixelToLogic( aCurr.aBounds ) ) );
1945 #endif
1947 return bTransparent;
1950 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */