LibreOffice Module vcl (master)  1
transparent.cxx
Go to the documentation of this file.
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 <sal/types.h>
21 #include <osl/diagnose.h>
22 #include <rtl/math.hxx>
24 #include <tools/helpers.hxx>
25 #include <officecfg/Office/Common.hxx>
26 
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 
35 #include <pdf/pdfwriter_impl.hxx>
36 #include <salgdi.hxx>
37 
38 #include <list>
39 #include <memory>
40 
41 #define MAX_TILE_WIDTH 1024
42 #define MAX_TILE_HEIGHT 1024
43 
44 namespace
45 {
51  tools::Polygon toPolygon( const basegfx::B2DPolygon& rPoly )
52  {
53  basegfx::B2DRange aRange = rPoly.getB2DRange();
54  double fW = aRange.getWidth(), fH = aRange.getHeight();
55  if (0.0 < fW && 0.0 < fH && (fW <= 1.0 || fH <= 1.0))
56  {
57  // This polygon not empty but is too small to display. Approximate it
58  // with a rectangle large enough to be displayed.
59  double nX = aRange.getMinX(), nY = aRange.getMinY();
60  double nW = std::max<double>(1.0, rtl::math::round(fW));
61  double nH = std::max<double>(1.0, rtl::math::round(fH));
62 
63  tools::Polygon aTarget;
64  aTarget.Insert(0, Point(nX, nY));
65  aTarget.Insert(1, Point(nX+nW, nY));
66  aTarget.Insert(2, Point(nX+nW, nY+nH));
67  aTarget.Insert(3, Point(nX, nY+nH));
68  aTarget.Insert(4, Point(nX, nY));
69  return aTarget;
70  }
71  return tools::Polygon(rPoly);
72  }
73 
74  tools::PolyPolygon toPolyPolygon( const basegfx::B2DPolyPolygon& rPolyPoly )
75  {
76  tools::PolyPolygon aTarget;
77  for (auto const& rB2DPolygon : rPolyPoly)
78  aTarget.Insert(toPolygon(rB2DPolygon));
79 
80  return aTarget;
81  }
82 }
83 
84 // Caution: This method is nearly the same as
85 // void OutputDevice::DrawPolyPolygon( const basegfx::B2DPolyPolygon& rB2DPolyPoly )
86 // so when changes are made here do not forget to make changes there, too
87 
89  const basegfx::B2DHomMatrix& rObjectTransform,
90  const basegfx::B2DPolyPolygon& rB2DPolyPoly,
91  double fTransparency)
92 {
93  assert(!is_double_buffered_window());
94 
95  // AW: Do NOT paint empty PolyPolygons
96  if(!rB2DPolyPoly.count())
97  return;
98 
99  // we need a graphics
100  if( !mpGraphics && !AcquireGraphics() )
101  return;
102  assert(mpGraphics);
103 
104  if( mbInitClipRegion )
105  InitClipRegion();
106 
107  if( mbOutputClipped )
108  return;
109 
110  if( mbInitLineColor )
111  InitLineColor();
112 
113  if( mbInitFillColor )
114  InitFillColor();
115 
118  {
119  // b2dpolygon support not implemented yet on non-UNX platforms
120  basegfx::B2DPolyPolygon aB2DPolyPolygon(rB2DPolyPoly);
121 
122  // ensure it is closed
123  if(!aB2DPolyPolygon.isClosed())
124  {
125  // maybe assert, prevents buffering due to making a copy
126  aB2DPolyPolygon.setClosed( true );
127  }
128 
129  // create ObjectToDevice transformation
130  const basegfx::B2DHomMatrix aFullTransform(ImplGetDeviceTransformation() * rObjectTransform);
131  // TODO: this must not drop transparency for mpAlphaVDev case, but instead use premultiplied
132  // alpha... but that requires using premultiplied alpha also for already drawn data
133  const double fAdjustedTransparency = mpAlphaVDev ? 0 : fTransparency;
134  bool bDrawnOk(true);
135 
136  if( IsFillColor() )
137  {
138  bDrawnOk = mpGraphics->DrawPolyPolygon(
139  aFullTransform,
140  aB2DPolyPolygon,
141  fAdjustedTransparency,
142  *this);
143  }
144 
145  if( bDrawnOk && IsLineColor() )
146  {
147  const bool bPixelSnapHairline(mnAntialiasing & AntialiasingFlags::PixelSnapHairline);
148 
149  for(auto const& rPolygon : std::as_const(aB2DPolyPolygon))
150  {
152  aFullTransform,
153  rPolygon,
154  fAdjustedTransparency,
155  0.0, // tdf#124848 hairline
156  nullptr, // MM01
158  css::drawing::LineCap_BUTT,
159  basegfx::deg2rad(15.0), // not used with B2DLineJoin::NONE, but the correct default
160  bPixelSnapHairline,
161  *this );
162  }
163  }
164 
165  if( bDrawnOk )
166  {
167  if( mpMetaFile )
168  {
169  // tdf#119843 need transformed Polygon here
170  basegfx::B2DPolyPolygon aB2DPolyPoly(rB2DPolyPoly);
171  aB2DPolyPoly.transform(rObjectTransform);
174  tools::PolyPolygon(aB2DPolyPoly),
175  static_cast< sal_uInt16 >(fTransparency * 100.0)));
176  }
177 
178  if (mpAlphaVDev)
179  mpAlphaVDev->DrawTransparent(rObjectTransform, rB2DPolyPoly, fTransparency);
180 
181  return;
182  }
183  }
184 
185  // fallback to old polygon drawing if needed
186  // tdf#119843 need transformed Polygon here
187  basegfx::B2DPolyPolygon aB2DPolyPoly(rB2DPolyPoly);
188  aB2DPolyPoly.transform(rObjectTransform);
190  toPolyPolygon(aB2DPolyPoly),
191  static_cast<sal_uInt16>(fTransparency * 100.0));
192 }
193 
195 {
196  assert(!is_double_buffered_window());
197 
198  // short circuit if the polygon border is invisible too
199  if( !mbLineColor )
200  return;
201 
202  // we assume that the border is NOT to be drawn transparently???
204  SetFillColor();
205  DrawPolyPolygon( rPolyPoly );
206  Pop();
207 }
208 
210  sal_uInt16 nTransparencePercent )
211 {
212  assert(!is_double_buffered_window());
213 
214  bool bDrawn = false;
215 
217 #if defined UNX && ! defined MACOSX && ! defined IOS
218  && GetBitCount() > 8
219 #endif
220 #ifdef _WIN32
221  // workaround bad dithering on remote displaying when using GDI+ with toolbar button highlighting
222  && !rPolyPoly.IsRect()
223 #endif
224  )
225  {
226  // prepare the graphics device
227  if( mbInitClipRegion )
228  InitClipRegion();
229 
230  if( mbOutputClipped )
231  return false;
232 
233  if( mbInitLineColor )
234  InitLineColor();
235 
236  if( mbInitFillColor )
237  InitFillColor();
238 
239  // get the polygon in device coordinates
240  basegfx::B2DPolyPolygon aB2DPolyPolygon(rPolyPoly.getB2DPolyPolygon());
242 
243  const double fTransparency = 0.01 * nTransparencePercent;
244  if( mbFillColor )
245  {
246  // #i121591#
247  // CAUTION: Only non printing (pixel-renderer) VCL commands from OutputDevices
248  // should be used when printing. Normally this is avoided by the printer being
249  // non-AAed and thus e.g. on WIN GdiPlus calls are not used. It may be necessary
250  // to figure out a way of moving this code to its own function that is
251  // overridden by the Print class, which will mean we deliberately override the
252  // functionality and we use the fallback some lines below (which is not very good,
253  // though. For now, WinSalGraphics::drawPolyPolygon will detect printer usage and
254  // correct the wrong mapping (see there for details)
255  bDrawn = mpGraphics->DrawPolyPolygon(
256  aTransform,
257  aB2DPolyPolygon,
258  fTransparency,
259  *this);
260  }
261 
262  if( mbLineColor )
263  {
264  // disable the fill color for now
266 
267  // draw the border line
268  const bool bPixelSnapHairline(mnAntialiasing & AntialiasingFlags::PixelSnapHairline);
269 
270  for(auto const& rPolygon : std::as_const(aB2DPolyPolygon))
271  {
272  bDrawn = mpGraphics->DrawPolyLine(
273  aTransform,
274  rPolygon,
275  fTransparency,
276  0.0, // tdf#124848 hairline
277  nullptr, // MM01
279  css::drawing::LineCap_BUTT,
280  basegfx::deg2rad(15.0), // not used with B2DLineJoin::NONE, but the correct default
281  bPixelSnapHairline,
282  *this );
283  }
284 
285  // prepare to restore the fill color
287  }
288  }
289 
290  return bDrawn;
291 }
292 
294  sal_uInt16 nTransparencePercent )
295 {
296  // #110958# Disable alpha VDev, we perform the necessary
297  VirtualDevice* pOldAlphaVDev = mpAlphaVDev;
298 
299  // operation explicitly further below.
300  if( mpAlphaVDev )
301  mpAlphaVDev = nullptr;
302 
303  GDIMetaFile* pOldMetaFile = mpMetaFile;
304  mpMetaFile = nullptr;
305 
306  tools::PolyPolygon aPolyPoly( LogicToPixel( rPolyPoly ) );
307  tools::Rectangle aPolyRect( aPolyPoly.GetBoundRect() );
308  tools::Rectangle aDstRect( Point(), GetOutputSizePixel() );
309 
310  aDstRect.Intersection( aPolyRect );
311 
312  ClipToPaintRegion( aDstRect );
313 
314  if( !aDstRect.IsEmpty() )
315  {
316  bool bDrawn = false;
317 
318  // #i66849# Added fast path for exactly rectangular
319  // polygons
320  // #i83087# Naturally, system alpha blending cannot
321  // work with separate alpha VDev
322  if( !mpAlphaVDev && aPolyPoly.IsRect() )
323  {
324  // setup Graphics only here (other cases delegate
325  // to basic OutDev methods)
326  if ( mbInitClipRegion )
327  InitClipRegion();
328 
329  if ( mbInitLineColor )
330  InitLineColor();
331 
332  if ( mbInitFillColor )
333  InitFillColor();
334 
335  tools::Rectangle aLogicPolyRect( rPolyPoly.GetBoundRect() );
336  tools::Rectangle aPixelRect( ImplLogicToDevicePixel( aLogicPolyRect ) );
337 
338  if( !mbOutputClipped )
339  {
340  bDrawn = mpGraphics->DrawAlphaRect( aPixelRect.Left(), aPixelRect.Top(),
341  // #i98405# use methods with small g, else one pixel too much will be painted.
342  // This is because the source is a polygon which when painted would not paint
343  // the rightmost and lowest pixel line(s), so use one pixel less for the
344  // rectangle, too.
345  aPixelRect.getWidth(), aPixelRect.getHeight(),
346  sal::static_int_cast<sal_uInt8>(nTransparencePercent),
347  *this );
348  }
349  else
350  {
351  bDrawn = true;
352  }
353  }
354 
355  if( !bDrawn )
356  {
358  const Size aDstSz( aDstRect.GetSize() );
359  const sal_uInt8 cTrans = static_cast<sal_uInt8>(MinMax( FRound( nTransparencePercent * 2.55 ), 0, 255 ));
360 
361  if( aDstRect.Left() || aDstRect.Top() )
362  aPolyPoly.Move( -aDstRect.Left(), -aDstRect.Top() );
363 
364  if( aVDev->SetOutputSizePixel( aDstSz ) )
365  {
366  const bool bOldMap = mbMap;
367 
368  EnableMapMode( false );
369 
370  aVDev->SetLineColor( COL_BLACK );
371  aVDev->SetFillColor( COL_BLACK );
372  aVDev->DrawPolyPolygon( aPolyPoly );
373 
374  Bitmap aPaint( GetBitmap( aDstRect.TopLeft(), aDstSz ) );
375  Bitmap aPolyMask( aVDev->GetBitmap( Point(), aDstSz ) );
376 
377  // #107766# check for non-empty bitmaps before accessing them
378  if( !aPaint.IsEmpty() && !aPolyMask.IsEmpty() )
379  {
380  BitmapScopedWriteAccess pW(aPaint);
381  Bitmap::ScopedReadAccess pR(aPolyMask);
382 
383  if( pW && pR )
384  {
385  BitmapColor aPixCol;
386  const BitmapColor aFillCol( GetFillColor() );
387  const BitmapColor aBlack( pR->GetBestMatchingColor( COL_BLACK ) );
388  const tools::Long nWidth = pW->Width();
389  const tools::Long nHeight = pW->Height();
390  const tools::Long nR = aFillCol.GetRed();
391  const tools::Long nG = aFillCol.GetGreen();
392  const tools::Long nB = aFillCol.GetBlue();
393  tools::Long nX, nY;
394 
396  {
397  const BitmapPalette& rPal = pW->GetPalette();
398  const sal_uInt16 nCount = rPal.GetEntryCount();
399  std::unique_ptr<sal_uInt8[]> xMap(new sal_uInt8[ nCount * sizeof( BitmapColor )]);
400  BitmapColor* pMap = reinterpret_cast<BitmapColor*>(xMap.get());
401 
402  for( sal_uInt16 i = 0; i < nCount; i++ )
403  {
404  BitmapColor aCol( rPal[ i ] );
405  aCol.Merge( aFillCol, cTrans );
406  pMap[ i ] = BitmapColor( static_cast<sal_uInt8>(rPal.GetBestIndex( aCol )) );
407  }
408 
410  pW->GetScanlineFormat() == ScanlineFormat::N8BitPal )
411  {
412  const sal_uInt8 cBlack = aBlack.GetIndex();
413 
414  for( nY = 0; nY < nHeight; nY++ )
415  {
416  Scanline pWScan = pW->GetScanline( nY );
417  Scanline pRScan = pR->GetScanline( nY );
418  sal_uInt8 cBit = 128;
419 
420  for( nX = 0; nX < nWidth; nX++, cBit >>= 1, pWScan++ )
421  {
422  if( !cBit )
423  {
424  cBit = 128;
425  pRScan += 1;
426  }
427  if( ( *pRScan & cBit ) == cBlack )
428  {
429  *pWScan = pMap[ *pWScan ].GetIndex();
430  }
431  }
432  }
433  }
434  else
435  {
436  for( nY = 0; nY < nHeight; nY++ )
437  {
438  Scanline pScanline = pW->GetScanline(nY);
439  Scanline pScanlineRead = pR->GetScanline(nY);
440  for( nX = 0; nX < nWidth; nX++ )
441  {
442  if( pR->GetPixelFromData( pScanlineRead, nX ) == aBlack )
443  {
444  pW->SetPixelOnData( pScanline, nX, pMap[ pW->GetIndexFromData( pScanline, nX ) ] );
445  }
446  }
447  }
448  }
449  }
450  else
451  {
453  pW->GetScanlineFormat() == ScanlineFormat::N24BitTcBgr )
454  {
455  const sal_uInt8 cBlack = aBlack.GetIndex();
456 
457  for( nY = 0; nY < nHeight; nY++ )
458  {
459  Scanline pWScan = pW->GetScanline( nY );
460  Scanline pRScan = pR->GetScanline( nY );
461  sal_uInt8 cBit = 128;
462 
463  for( nX = 0; nX < nWidth; nX++, cBit >>= 1, pWScan += 3 )
464  {
465  if( !cBit )
466  {
467  cBit = 128;
468  pRScan += 1;
469  }
470  if( ( *pRScan & cBit ) == cBlack )
471  {
472  pWScan[ 0 ] = color::ColorChannelMerge( pWScan[ 0 ], nB, cTrans );
473  pWScan[ 1 ] = color::ColorChannelMerge( pWScan[ 1 ], nG, cTrans );
474  pWScan[ 2 ] = color::ColorChannelMerge( pWScan[ 2 ], nR, cTrans );
475  }
476  }
477  }
478  }
479  else
480  {
481  for( nY = 0; nY < nHeight; nY++ )
482  {
483  Scanline pScanline = pW->GetScanline(nY);
484  Scanline pScanlineRead = pR->GetScanline(nY);
485  for( nX = 0; nX < nWidth; nX++ )
486  {
487  if( pR->GetPixelFromData( pScanlineRead, nX ) == aBlack )
488  {
489  aPixCol = pW->GetColor( nY, nX );
490  aPixCol.Merge(aFillCol, cTrans);
491  pW->SetPixelOnData(pScanline, nX, aPixCol);
492  }
493  }
494  }
495  }
496  }
497  }
498 
499  pR.reset();
500  pW.reset();
501 
502  DrawBitmap( aDstRect.TopLeft(), aPaint );
503 
504  EnableMapMode( bOldMap );
505 
506  if( mbLineColor )
507  {
509  SetFillColor();
510  DrawPolyPolygon( rPolyPoly );
511  Pop();
512  }
513  }
514  }
515  else
516  {
517  DrawPolyPolygon( rPolyPoly );
518  }
519  }
520  }
521 
522  mpMetaFile = pOldMetaFile;
523 
524  // #110958# Restore disabled alpha VDev
525  mpAlphaVDev = pOldAlphaVDev;
526 }
527 
529  sal_uInt16 nTransparencePercent )
530 {
531  assert(!is_double_buffered_window());
532 
533  // short circuit for drawing an opaque polygon
534  if( (nTransparencePercent < 1) || (mnDrawMode & DrawModeFlags::NoTransparency) )
535  {
536  DrawPolyPolygon( rPolyPoly );
537  return;
538  }
539 
540  // short circuit for drawing an invisible polygon
541  if( !mbFillColor || (nTransparencePercent >= 100) )
542  {
543  DrawInvisiblePolygon( rPolyPoly );
544  return; // tdf#84294: do not record it in metafile
545  }
546 
547  // handle metafile recording
548  if( mpMetaFile )
549  mpMetaFile->AddAction( new MetaTransparentAction( rPolyPoly, nTransparencePercent ) );
550 
551  bool bDrawn = !IsDeviceOutputNecessary() || ImplIsRecordLayout();
552  if( bDrawn )
553  return;
554 
555  // get the device graphics as drawing target
556  if( !mpGraphics && !AcquireGraphics() )
557  return;
558  assert(mpGraphics);
559 
560  // try hard to draw it directly, because the emulation layers are slower
561  bDrawn = DrawTransparentNatively( rPolyPoly, nTransparencePercent );
562 
563  if (!bDrawn)
564  EmulateDrawTransparent( rPolyPoly, nTransparencePercent );
565 
566  // #110958# Apply alpha value also to VDev alpha channel
567  if( mpAlphaVDev )
568  {
569  const Color aFillCol( mpAlphaVDev->GetFillColor() );
570  mpAlphaVDev->SetFillColor( Color(sal::static_int_cast<sal_uInt8>(255*nTransparencePercent/100),
571  sal::static_int_cast<sal_uInt8>(255*nTransparencePercent/100),
572  sal::static_int_cast<sal_uInt8>(255*nTransparencePercent/100)) );
573 
574  mpAlphaVDev->DrawTransparent( rPolyPoly, nTransparencePercent );
575 
576  mpAlphaVDev->SetFillColor( aFillCol );
577  }
578 }
579 
580 void OutputDevice::DrawTransparent( const GDIMetaFile& rMtf, const Point& rPos,
581  const Size& rSize, const Gradient& rTransparenceGradient )
582 {
583  assert(!is_double_buffered_window());
584 
585  const Color aBlack( COL_BLACK );
586 
587  if( mpMetaFile )
588  {
589  // missing here is to map the data using the DeviceTransformation
590  mpMetaFile->AddAction( new MetaFloatTransparentAction( rMtf, rPos, rSize, rTransparenceGradient ) );
591  }
592 
593  if ( !IsDeviceOutputNecessary() )
594  return;
595 
596  if( ( rTransparenceGradient.GetStartColor() == aBlack && rTransparenceGradient.GetEndColor() == aBlack ) ||
598  {
599  const_cast<GDIMetaFile&>(rMtf).WindStart();
600  const_cast<GDIMetaFile&>(rMtf).Play(*this, rPos, rSize);
601  const_cast<GDIMetaFile&>(rMtf).WindStart();
602  }
603  else
604  {
605  GDIMetaFile* pOldMetaFile = mpMetaFile;
606  tools::Rectangle aOutRect( LogicToPixel( rPos ), LogicToPixel( rSize ) );
607  Point aPoint;
608  tools::Rectangle aDstRect( aPoint, GetOutputSizePixel() );
609 
610  mpMetaFile = nullptr;
611  aDstRect.Intersection( aOutRect );
612 
613  ClipToPaintRegion( aDstRect );
614 
615  if( !aDstRect.IsEmpty() )
616  {
617  // Create transparent buffer
619 
620  xVDev->mnDPIX = mnDPIX;
621  xVDev->mnDPIY = mnDPIY;
622 
623  if( xVDev->SetOutputSizePixel( aDstRect.GetSize() ) )
624  {
626  {
627  // #i102109#
628  // For MetaFile replay (see task) it may now be necessary to take
629  // into account that the content is AntiAlialiased and needs to be masked
630  // like that. Instead of masking, i will use a copy-modify-paste cycle
631  // here (as i already use in the VclPrimiziveRenderer with success)
632  xVDev->SetAntialiasing(GetAntialiasing());
633 
634  // create MapMode for buffer (offset needed) and set
636  const Point aOutPos(PixelToLogic(aDstRect.TopLeft()));
637  aMap.SetOrigin(Point(-aOutPos.X(), -aOutPos.Y()));
638  xVDev->SetMapMode(aMap);
639 
640  // copy MapMode state and disable for target
641  const bool bOrigMapModeEnabled(IsMapModeEnabled());
642  EnableMapMode(false);
643 
644  // copy MapMode state and disable for buffer
645  const bool bBufferMapModeEnabled(xVDev->IsMapModeEnabled());
646  xVDev->EnableMapMode(false);
647 
648  // copy content from original to buffer
649  xVDev->DrawOutDev( aPoint, xVDev->GetOutputSizePixel(), // dest
650  aDstRect.TopLeft(), xVDev->GetOutputSizePixel(), // source
651  *this);
652 
653  // draw MetaFile to buffer
654  xVDev->EnableMapMode(bBufferMapModeEnabled);
655  const_cast<GDIMetaFile&>(rMtf).WindStart();
656  const_cast<GDIMetaFile&>(rMtf).Play(*xVDev, rPos, rSize);
657  const_cast<GDIMetaFile&>(rMtf).WindStart();
658 
659  // get content bitmap from buffer
660  xVDev->EnableMapMode(false);
661 
662  const Bitmap aPaint(xVDev->GetBitmap(aPoint, xVDev->GetOutputSizePixel()));
663 
664  // create alpha mask from gradient and get as Bitmap
665  xVDev->EnableMapMode(bBufferMapModeEnabled);
666  xVDev->SetDrawMode(DrawModeFlags::GrayGradient);
667  xVDev->DrawGradient(tools::Rectangle(rPos, rSize), rTransparenceGradient);
668  xVDev->SetDrawMode(DrawModeFlags::Default);
669  xVDev->EnableMapMode(false);
670 
671  const AlphaMask aAlpha(xVDev->GetBitmap(aPoint, xVDev->GetOutputSizePixel()));
672 
673  xVDev.disposeAndClear();
674 
675  // draw masked content to target and restore MapMode
676  DrawBitmapEx(aDstRect.TopLeft(), BitmapEx(aPaint, aAlpha));
677  EnableMapMode(bOrigMapModeEnabled);
678  }
679  else
680  {
681  MapMode aMap( GetMapMode() );
682  Point aOutPos( PixelToLogic( aDstRect.TopLeft() ) );
683  const bool bOldMap = mbMap;
684 
685  aMap.SetOrigin( Point( -aOutPos.X(), -aOutPos.Y() ) );
686  xVDev->SetMapMode( aMap );
687  const bool bVDevOldMap = xVDev->IsMapModeEnabled();
688 
689  // create paint bitmap
690  const_cast<GDIMetaFile&>(rMtf).WindStart();
691  const_cast<GDIMetaFile&>(rMtf).Play(*xVDev, rPos, rSize);
692  const_cast<GDIMetaFile&>(rMtf).WindStart();
693  xVDev->EnableMapMode( false );
694  BitmapEx aPaint = xVDev->GetBitmapEx(Point(), xVDev->GetOutputSizePixel());
695  xVDev->EnableMapMode( bVDevOldMap ); // #i35331#: MUST NOT use EnableMapMode( sal_True ) here!
696 
697  // create alpha mask from gradient
698  xVDev->SetDrawMode( DrawModeFlags::GrayGradient );
699  xVDev->DrawGradient( tools::Rectangle( rPos, rSize ), rTransparenceGradient );
700  xVDev->SetDrawMode( DrawModeFlags::Default );
701  xVDev->EnableMapMode( false );
702 
703  AlphaMask aAlpha(xVDev->GetBitmap(Point(), xVDev->GetOutputSizePixel()));
704  aAlpha.BlendWith(aPaint.GetAlpha());
705 
706  xVDev.disposeAndClear();
707 
708  EnableMapMode( false );
709  DrawBitmapEx(aDstRect.TopLeft(), BitmapEx(aPaint.GetBitmap(), aAlpha));
710  EnableMapMode( bOldMap );
711  }
712  }
713  }
714 
715  mpMetaFile = pOldMetaFile;
716  }
717 }
718 
719 typedef ::std::pair< MetaAction*, int > Component; // MetaAction plus index in metafile
720 
721 namespace {
722 
723 // List of (intersecting) actions, plus overall bounds
724 struct ConnectedComponents
725 {
726  ConnectedComponents() :
727  aComponentList(),
728  aBounds(),
729  aBgColor(COL_WHITE),
730  bIsSpecial(false),
731  bIsFullyTransparent(false)
732  {}
733 
734  ::std::list< Component > aComponentList;
735  tools::Rectangle aBounds;
736  Color aBgColor;
737  bool bIsSpecial;
738  bool bIsFullyTransparent;
739 };
740 
741 }
742 
743 namespace {
744 
749 bool DoesActionHandleTransparency( const MetaAction& rAct )
750 {
751  // MetaActionType::FLOATTRANSPARENT can contain a whole metafile,
752  // which is to be rendered with the given transparent gradient. We
753  // currently cannot emulate transparent painting on a white
754  // background reliably.
755 
756  // the remainder can handle printing itself correctly on a uniform
757  // white background.
758  switch( rAct.GetType() )
759  {
764  return true;
765 
766  default:
767  return false;
768  }
769 }
770 
771 bool doesRectCoverWithUniformColor(
772  tools::Rectangle const & rPrevRect,
773  tools::Rectangle const & rCurrRect,
774  OutputDevice const & rMapModeVDev)
775 {
776  // shape needs to fully cover previous content, and have uniform
777  // color
778  return (rMapModeVDev.LogicToPixel(rCurrRect).Contains(rPrevRect) &&
779  rMapModeVDev.IsFillColor());
780 }
781 
785 bool checkRect( tools::Rectangle& io_rPrevRect,
786  Color& o_rBgColor,
787  const tools::Rectangle& rCurrRect,
788  OutputDevice const & rMapModeVDev )
789 {
790  bool bRet = doesRectCoverWithUniformColor(io_rPrevRect, rCurrRect, rMapModeVDev);
791 
792  if( bRet )
793  {
794  io_rPrevRect = rCurrRect;
795  o_rBgColor = rMapModeVDev.GetFillColor();
796  }
797 
798  return bRet;
799 }
800 
808 void ImplConvertTransparentAction( GDIMetaFile& o_rMtf,
809  const MetaAction& rAct,
810  const OutputDevice& rStateOutDev,
811  Color aBgColor )
812 {
813  if (rAct.GetType() == MetaActionType::Transparent)
814  {
815  const MetaTransparentAction* pTransAct = static_cast<const MetaTransparentAction*>(&rAct);
816  sal_uInt16 nTransparency( pTransAct->GetTransparence() );
817 
818  // #i10613# Respect transparency for draw color
819  if (nTransparency)
820  {
822 
823  // assume white background for alpha blending
824  Color aLineColor(rStateOutDev.GetLineColor());
825  aLineColor.SetRed(static_cast<sal_uInt8>((255*nTransparency + (100 - nTransparency) * aLineColor.GetRed()) / 100));
826  aLineColor.SetGreen(static_cast<sal_uInt8>((255*nTransparency + (100 - nTransparency) * aLineColor.GetGreen()) / 100));
827  aLineColor.SetBlue(static_cast<sal_uInt8>((255*nTransparency + (100 - nTransparency) * aLineColor.GetBlue()) / 100));
828  o_rMtf.AddAction(new MetaLineColorAction(aLineColor, true));
829 
830  Color aFillColor(rStateOutDev.GetFillColor());
831  aFillColor.SetRed(static_cast<sal_uInt8>((255*nTransparency + (100 - nTransparency)*aFillColor.GetRed()) / 100));
832  aFillColor.SetGreen(static_cast<sal_uInt8>((255*nTransparency + (100 - nTransparency)*aFillColor.GetGreen()) / 100));
833  aFillColor.SetBlue(static_cast<sal_uInt8>((255*nTransparency + (100 - nTransparency)*aFillColor.GetBlue()) / 100));
834  o_rMtf.AddAction(new MetaFillColorAction(aFillColor, true));
835  }
836 
837  o_rMtf.AddAction(new MetaPolyPolygonAction(pTransAct->GetPolyPolygon()));
838 
839  if(nTransparency)
840  o_rMtf.AddAction(new MetaPopAction());
841  }
842  else
843  {
844  BitmapEx aBmpEx;
845 
846  switch (rAct.GetType())
847  {
849  aBmpEx = static_cast<const MetaBmpExAction&>(rAct).GetBitmapEx();
850  break;
851 
853  aBmpEx = static_cast<const MetaBmpExScaleAction&>(rAct).GetBitmapEx();
854  break;
855 
857  aBmpEx = static_cast<const MetaBmpExScaleAction&>(rAct).GetBitmapEx();
858  break;
859 
861 
862  default:
863  OSL_FAIL("Printer::GetPreparedMetafile impossible state reached");
864  break;
865  }
866 
867  Bitmap aBmp(aBmpEx.GetBitmap());
868  if (aBmpEx.IsAlpha())
869  {
870  // blend with alpha channel
872  aBmp.Blend(aBmpEx.GetAlpha(), aBgColor);
873  }
874 
875  // add corresponding action
876  switch (rAct.GetType())
877  {
879  o_rMtf.AddAction(new MetaBmpAction(
880  static_cast<const MetaBmpExAction&>(rAct).GetPoint(),
881  aBmp));
882  break;
884  o_rMtf.AddAction(new MetaBmpScaleAction(
885  static_cast<const MetaBmpExScaleAction&>(rAct).GetPoint(),
886  static_cast<const MetaBmpExScaleAction&>(rAct).GetSize(),
887  aBmp));
888  break;
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;
900  }
901  }
902 }
903 
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 )
907 {
908  const bool bLineTransparency( !rOut.IsLineColor() || rOut.GetLineColor().IsFullyTransparent() );
909  const bool bFillTransparency( !rOut.IsFillColor() || rOut.GetFillColor().IsFullyTransparent() );
910  bool bRet( false );
911 
912  switch( rAct.GetType() )
913  {
915  if( !bLineTransparency )
916  bRet = true;
917  break;
918 
920  if( !bLineTransparency )
921  bRet = true;
922  break;
923 
925  if( !bLineTransparency || !bFillTransparency )
926  bRet = true;
927  break;
928 
930  if( !bLineTransparency || !bFillTransparency )
931  bRet = true;
932  break;
933 
935  if( !bLineTransparency || !bFillTransparency )
936  bRet = true;
937  break;
938 
939  case MetaActionType::ARC:
940  if( !bLineTransparency || !bFillTransparency )
941  bRet = true;
942  break;
943 
944  case MetaActionType::PIE:
945  if( !bLineTransparency || !bFillTransparency )
946  bRet = true;
947  break;
948 
950  if( !bLineTransparency || !bFillTransparency )
951  bRet = true;
952  break;
953 
955  if( !bLineTransparency )
956  bRet = true;
957  break;
958 
960  if( !bLineTransparency || !bFillTransparency )
961  bRet = true;
962  break;
963 
965  if( !bLineTransparency || !bFillTransparency )
966  bRet = true;
967  break;
968 
970  {
971  const MetaTextAction& rTextAct = static_cast<const MetaTextAction&>(rAct);
972  const OUString aString( rTextAct.GetText().copy(rTextAct.GetIndex(), rTextAct.GetLen()) );
973  if (!aString.isEmpty())
974  bRet = true;
975  }
976  break;
977 
979  {
980  const MetaTextArrayAction& rTextAct = static_cast<const MetaTextArrayAction&>(rAct);
981  const OUString aString( rTextAct.GetText().copy(rTextAct.GetIndex(), rTextAct.GetLen()) );
982  if (!aString.isEmpty())
983  bRet = true;
984  }
985  break;
986 
988  case MetaActionType::BMP:
1003  case MetaActionType::EPS:
1007  // all other actions: generate non-transparent output
1008  bRet = true;
1009  break;
1010 
1011  default:
1012  break;
1013  }
1014 
1015  return bRet;
1016 }
1017 
1018 // #i10613# Extracted from ImplCheckRect::ImplCreate
1019 tools::Rectangle ImplCalcActionBounds( const MetaAction& rAct, const OutputDevice& rOut )
1020 {
1021  tools::Rectangle aActionBounds;
1022 
1023  switch( rAct.GetType() )
1024  {
1025  case MetaActionType::PIXEL:
1026  aActionBounds = tools::Rectangle( static_cast<const MetaPixelAction&>(rAct).GetPoint(), Size( 1, 1 ) );
1027  break;
1028 
1029  case MetaActionType::POINT:
1030  aActionBounds = tools::Rectangle( static_cast<const MetaPointAction&>(rAct).GetPoint(), Size( 1, 1 ) );
1031  break;
1032 
1033  case MetaActionType::LINE:
1034  {
1035  const MetaLineAction& rMetaLineAction = static_cast<const MetaLineAction&>(rAct);
1036  aActionBounds = tools::Rectangle( rMetaLineAction.GetStartPoint(), rMetaLineAction.GetEndPoint() );
1037  aActionBounds.Justify();
1038  const tools::Long nLineWidth(rMetaLineAction.GetLineInfo().GetWidth());
1039  if(nLineWidth)
1040  {
1041  const tools::Long nHalfLineWidth((nLineWidth + 1) / 2);
1042  aActionBounds.AdjustLeft( -nHalfLineWidth );
1043  aActionBounds.AdjustTop( -nHalfLineWidth );
1044  aActionBounds.AdjustRight(nHalfLineWidth );
1045  aActionBounds.AdjustBottom(nHalfLineWidth );
1046  }
1047  break;
1048  }
1049 
1050  case MetaActionType::RECT:
1051  aActionBounds = static_cast<const MetaRectAction&>(rAct).GetRect();
1052  break;
1053 
1055  aActionBounds = tools::Polygon( static_cast<const MetaRoundRectAction&>(rAct).GetRect(),
1056  static_cast<const MetaRoundRectAction&>(rAct).GetHorzRound(),
1057  static_cast<const MetaRoundRectAction&>(rAct).GetVertRound() ).GetBoundRect();
1058  break;
1059 
1061  {
1062  const tools::Rectangle& rRect = static_cast<const MetaEllipseAction&>(rAct).GetRect();
1063  aActionBounds = tools::Polygon( rRect.Center(),
1064  rRect.GetWidth() >> 1,
1065  rRect.GetHeight() >> 1 ).GetBoundRect();
1066  break;
1067  }
1068 
1069  case MetaActionType::ARC:
1070  aActionBounds = tools::Polygon( static_cast<const MetaArcAction&>(rAct).GetRect(),
1071  static_cast<const MetaArcAction&>(rAct).GetStartPoint(),
1072  static_cast<const MetaArcAction&>(rAct).GetEndPoint(), PolyStyle::Arc ).GetBoundRect();
1073  break;
1074 
1075  case MetaActionType::PIE:
1076  aActionBounds = tools::Polygon( static_cast<const MetaPieAction&>(rAct).GetRect(),
1077  static_cast<const MetaPieAction&>(rAct).GetStartPoint(),
1078  static_cast<const MetaPieAction&>(rAct).GetEndPoint(), PolyStyle::Pie ).GetBoundRect();
1079  break;
1080 
1081  case MetaActionType::CHORD:
1082  aActionBounds = tools::Polygon( static_cast<const MetaChordAction&>(rAct).GetRect(),
1083  static_cast<const MetaChordAction&>(rAct).GetStartPoint(),
1084  static_cast<const MetaChordAction&>(rAct).GetEndPoint(), PolyStyle::Chord ).GetBoundRect();
1085  break;
1086 
1088  {
1089  const MetaPolyLineAction& rMetaPolyLineAction = static_cast<const MetaPolyLineAction&>(rAct);
1090  aActionBounds = rMetaPolyLineAction.GetPolygon().GetBoundRect();
1091  const tools::Long nLineWidth(rMetaPolyLineAction.GetLineInfo().GetWidth());
1092  if(nLineWidth)
1093  {
1094  const tools::Long nHalfLineWidth((nLineWidth + 1) / 2);
1095  aActionBounds.AdjustLeft( -nHalfLineWidth );
1096  aActionBounds.AdjustTop( -nHalfLineWidth );
1097  aActionBounds.AdjustRight(nHalfLineWidth );
1098  aActionBounds.AdjustBottom(nHalfLineWidth );
1099  }
1100  break;
1101  }
1102 
1104  aActionBounds = static_cast<const MetaPolygonAction&>(rAct).GetPolygon().GetBoundRect();
1105  break;
1106 
1108  aActionBounds = static_cast<const MetaPolyPolygonAction&>(rAct).GetPolyPolygon().GetBoundRect();
1109  break;
1110 
1111  case MetaActionType::BMP:
1112  aActionBounds = tools::Rectangle( static_cast<const MetaBmpAction&>(rAct).GetPoint(),
1113  rOut.PixelToLogic( static_cast<const MetaBmpAction&>(rAct).GetBitmap().GetSizePixel() ) );
1114  break;
1115 
1117  aActionBounds = tools::Rectangle( static_cast<const MetaBmpScaleAction&>(rAct).GetPoint(),
1118  static_cast<const MetaBmpScaleAction&>(rAct).GetSize() );
1119  break;
1120 
1122  aActionBounds = tools::Rectangle( static_cast<const MetaBmpScalePartAction&>(rAct).GetDestPoint(),
1123  static_cast<const MetaBmpScalePartAction&>(rAct).GetDestSize() );
1124  break;
1125 
1126  case MetaActionType::BMPEX:
1127  aActionBounds = tools::Rectangle( static_cast<const MetaBmpExAction&>(rAct).GetPoint(),
1128  rOut.PixelToLogic( static_cast<const MetaBmpExAction&>(rAct).GetBitmapEx().GetSizePixel() ) );
1129  break;
1130 
1132  aActionBounds = tools::Rectangle( static_cast<const MetaBmpExScaleAction&>(rAct).GetPoint(),
1133  static_cast<const MetaBmpExScaleAction&>(rAct).GetSize() );
1134  break;
1135 
1137  aActionBounds = tools::Rectangle( static_cast<const MetaBmpExScalePartAction&>(rAct).GetDestPoint(),
1138  static_cast<const MetaBmpExScalePartAction&>(rAct).GetDestSize() );
1139  break;
1140 
1141  case MetaActionType::MASK:
1142  aActionBounds = tools::Rectangle( static_cast<const MetaMaskAction&>(rAct).GetPoint(),
1143  rOut.PixelToLogic( static_cast<const MetaMaskAction&>(rAct).GetBitmap().GetSizePixel() ) );
1144  break;
1145 
1147  aActionBounds = tools::Rectangle( static_cast<const MetaMaskScaleAction&>(rAct).GetPoint(),
1148  static_cast<const MetaMaskScaleAction&>(rAct).GetSize() );
1149  break;
1150 
1152  aActionBounds = tools::Rectangle( static_cast<const MetaMaskScalePartAction&>(rAct).GetDestPoint(),
1153  static_cast<const MetaMaskScalePartAction&>(rAct).GetDestSize() );
1154  break;
1155 
1157  aActionBounds = static_cast<const MetaGradientAction&>(rAct).GetRect();
1158  break;
1159 
1161  aActionBounds = static_cast<const MetaGradientExAction&>(rAct).GetPolyPolygon().GetBoundRect();
1162  break;
1163 
1164  case MetaActionType::HATCH:
1165  aActionBounds = static_cast<const MetaHatchAction&>(rAct).GetPolyPolygon().GetBoundRect();
1166  break;
1167 
1169  aActionBounds = static_cast<const MetaWallpaperAction&>(rAct).GetRect();
1170  break;
1171 
1173  aActionBounds = static_cast<const MetaTransparentAction&>(rAct).GetPolyPolygon().GetBoundRect();
1174  break;
1175 
1177  aActionBounds = tools::Rectangle( static_cast<const MetaFloatTransparentAction&>(rAct).GetPoint(),
1178  static_cast<const MetaFloatTransparentAction&>(rAct).GetSize() );
1179  break;
1180 
1181  case MetaActionType::EPS:
1182  aActionBounds = tools::Rectangle( static_cast<const MetaEPSAction&>(rAct).GetPoint(),
1183  static_cast<const MetaEPSAction&>(rAct).GetSize() );
1184  break;
1185 
1186  case MetaActionType::TEXT:
1187  {
1188  const MetaTextAction& rTextAct = static_cast<const MetaTextAction&>(rAct);
1189  const OUString aString( rTextAct.GetText().copy(rTextAct.GetIndex(), rTextAct.GetLen()) );
1190 
1191  if (!aString.isEmpty())
1192  {
1193  const Point aPtLog( rTextAct.GetPoint() );
1194 
1195  // #105987# Use API method instead of Impl* methods
1196  // #107490# Set base parameter equal to index parameter
1197  rOut.GetTextBoundRect( aActionBounds, rTextAct.GetText(), rTextAct.GetIndex(),
1198  rTextAct.GetIndex(), rTextAct.GetLen() );
1199  aActionBounds.Move( aPtLog.X(), aPtLog.Y() );
1200  }
1201  }
1202  break;
1203 
1205  {
1206  const MetaTextArrayAction& rTextAct = static_cast<const MetaTextArrayAction&>(rAct);
1207  const OUString aString( rTextAct.GetText().copy(rTextAct.GetIndex(), rTextAct.GetLen()) );
1208 
1209  if( !aString.isEmpty() )
1210  {
1211  // #105987# ImplLayout takes everything in logical coordinates
1212  std::unique_ptr<SalLayout> pSalLayout = rOut.ImplLayout( rTextAct.GetText(), rTextAct.GetIndex(),
1213  rTextAct.GetLen(), rTextAct.GetPoint(),
1214  0, rTextAct.GetDXArray() );
1215  if( pSalLayout )
1216  {
1217  tools::Rectangle aBoundRect( rOut.ImplGetTextBoundRect( *pSalLayout ) );
1218  aActionBounds = rOut.PixelToLogic( aBoundRect );
1219  }
1220  }
1221  }
1222  break;
1223 
1225  aActionBounds = static_cast<const MetaTextRectAction&>(rAct).GetRect();
1226  break;
1227 
1229  {
1230  const MetaStretchTextAction& rTextAct = static_cast<const MetaStretchTextAction&>(rAct);
1231  const OUString aString( rTextAct.GetText().copy(rTextAct.GetIndex(), rTextAct.GetLen()) );
1232 
1233  // #i16195# Literate copy from TextArray action, the
1234  // semantics for the ImplLayout call are copied from the
1235  // OutDev::DrawStretchText() code. Unfortunately, also in
1236  // this case, public outdev methods such as GetTextWidth()
1237  // don't provide enough info.
1238  if( !aString.isEmpty() )
1239  {
1240  // #105987# ImplLayout takes everything in logical coordinates
1241  std::unique_ptr<SalLayout> pSalLayout = rOut.ImplLayout( rTextAct.GetText(), rTextAct.GetIndex(),
1242  rTextAct.GetLen(), rTextAct.GetPoint(),
1243  rTextAct.GetWidth() );
1244  if( pSalLayout )
1245  {
1246  tools::Rectangle aBoundRect( rOut.ImplGetTextBoundRect( *pSalLayout ) );
1247  aActionBounds = rOut.PixelToLogic( aBoundRect );
1248  }
1249  }
1250  }
1251  break;
1252 
1254  OSL_FAIL("MetaActionType::TEXTLINE not supported");
1255  break;
1256 
1257  default:
1258  break;
1259  }
1260 
1261  if( !aActionBounds.IsEmpty() )
1262  {
1263  // fdo#40421 limit current action's output to clipped area
1264  if( rOut.IsClipRegion() )
1265  return rOut.LogicToPixel(
1266  rOut.GetClipRegion().GetBoundRect().Intersection( aActionBounds ) );
1267  else
1268  return rOut.LogicToPixel( aActionBounds );
1269  }
1270  else
1271  return tools::Rectangle();
1272 }
1273 
1274 } // end anon namespace
1275 
1276 // TODO: this massive function operates on metafiles, so eventually it should probably
1277 // be shifted to the GDIMetaFile class
1279  tools::Long nMaxBmpDPIX, tools::Long nMaxBmpDPIY,
1280  bool bReduceTransparency, bool bTransparencyAutoMode,
1281  bool bDownsampleBitmaps,
1282  const Color& rBackground
1283  )
1284 {
1285  MetaAction* pCurrAct;
1286  bool bTransparent( false );
1287 
1288  rOutMtf.Clear();
1289 
1290  if(!bReduceTransparency || bTransparencyAutoMode)
1291  bTransparent = rInMtf.HasTransparentActions();
1292 
1293  // #i10613# Determine set of connected components containing transparent objects. These are
1294  // then processed as bitmaps, the original actions are removed from the metafile.
1295  if( !bTransparent )
1296  {
1297  // nothing transparent -> just copy
1298  rOutMtf = rInMtf;
1299  }
1300  else
1301  {
1302  // #i10613#
1303  // This works as follows: we want a number of distinct sets of
1304  // connected components, where each set contains metafile
1305  // actions that are intersecting (note: there are possibly
1306  // more actions contained as are directly intersecting,
1307  // because we can only produce rectangular bitmaps later
1308  // on. Thus, each set of connected components is the smallest
1309  // enclosing, axis-aligned rectangle that completely bounds a
1310  // number of intersecting metafile actions, plus any action
1311  // that would otherwise be cut in two). Therefore, we
1312  // iteratively add metafile actions from the original metafile
1313  // to this connected components list (aCCList), by checking
1314  // each element's bounding box against intersection with the
1315  // metaaction at hand.
1316  // All those intersecting elements are removed from aCCList
1317  // and collected in a temporary list (aCCMergeList). After all
1318  // elements have been checked, the aCCMergeList elements are
1319  // merged with the metaaction at hand into one resulting
1320  // connected component, with one big bounding box, and
1321  // inserted into aCCList again.
1322  // The time complexity of this algorithm is O(n^3), where n is
1323  // the number of metafile actions, and it finds all distinct
1324  // regions of rectangle-bounded connected components. This
1325  // algorithm was designed by AF.
1326 
1327  // STAGE 1: Detect background
1328 
1329  // Receives uniform background content, and is _not_ merged
1330  // nor checked for intersection against other aCCList elements
1331  ConnectedComponents aBackgroundComponent;
1332 
1333  // Read the configuration value of minimal object area where transparency will be removed
1334  double fReduceTransparencyMinArea = officecfg::Office::Common::VCL::ReduceTransparencyMinArea::get() / 100.0;
1335  SAL_WARN_IF(fReduceTransparencyMinArea > 1.0, "vcl",
1336  "Value of ReduceTransparencyMinArea config option is too high");
1337  SAL_WARN_IF(fReduceTransparencyMinArea < 0.0, "vcl",
1338  "Value of ReduceTransparencyMinArea config option is too low");
1339  fReduceTransparencyMinArea = std::clamp(fReduceTransparencyMinArea, 0.0, 1.0);
1340 
1341  // create an OutputDevice to record mapmode changes and the like
1343  aMapModeVDev->mnDPIX = mnDPIX;
1344  aMapModeVDev->mnDPIY = mnDPIY;
1345  aMapModeVDev->EnableOutput(false);
1346 
1347  // weed out page-filling background objects (if they are
1348  // uniformly coloured). Keeping them outside the other
1349  // connected components often prevents whole-page bitmap
1350  // generation.
1351  bool bStillBackground=true; // true until first non-bg action
1352  int nActionNum = 0, nLastBgAction = -1;
1353  pCurrAct=const_cast<GDIMetaFile&>(rInMtf).FirstAction();
1354  if( rBackground != COL_TRANSPARENT )
1355  {
1356  aBackgroundComponent.aBgColor = rBackground;
1357  aBackgroundComponent.aBounds = GetBackgroundComponentBounds();
1358  }
1359  while( pCurrAct && bStillBackground )
1360  {
1361  switch( pCurrAct->GetType() )
1362  {
1363  case MetaActionType::RECT:
1364  {
1365  if( !checkRect(
1366  aBackgroundComponent.aBounds,
1367  aBackgroundComponent.aBgColor,
1368  static_cast<const MetaRectAction*>(pCurrAct)->GetRect(),
1369  *aMapModeVDev) )
1370  bStillBackground=false; // incomplete occlusion of background
1371  else
1372  nLastBgAction=nActionNum; // this _is_ background
1373  break;
1374  }
1376  {
1377  const tools::Polygon aPoly(
1378  static_cast<const MetaPolygonAction*>(pCurrAct)->GetPolygon());
1380  aPoly.getB2DPolygon()) ||
1381  !checkRect(
1382  aBackgroundComponent.aBounds,
1383  aBackgroundComponent.aBgColor,
1384  aPoly.GetBoundRect(),
1385  *aMapModeVDev) )
1386  bStillBackground=false; // incomplete occlusion of background
1387  else
1388  nLastBgAction=nActionNum; // this _is_ background
1389  break;
1390  }
1392  {
1393  const tools::PolyPolygon aPoly(
1394  static_cast<const MetaPolyPolygonAction*>(pCurrAct)->GetPolyPolygon());
1395  if( aPoly.Count() != 1 ||
1397  aPoly[0].getB2DPolygon()) ||
1398  !checkRect(
1399  aBackgroundComponent.aBounds,
1400  aBackgroundComponent.aBgColor,
1401  aPoly.GetBoundRect(),
1402  *aMapModeVDev) )
1403  bStillBackground=false; // incomplete occlusion of background
1404  else
1405  nLastBgAction=nActionNum; // this _is_ background
1406  break;
1407  }
1409  {
1410  if( !checkRect(
1411  aBackgroundComponent.aBounds,
1412  aBackgroundComponent.aBgColor,
1413  static_cast<const MetaWallpaperAction*>(pCurrAct)->GetRect(),
1414  *aMapModeVDev) )
1415  bStillBackground=false; // incomplete occlusion of background
1416  else
1417  nLastBgAction=nActionNum; // this _is_ background
1418  break;
1419  }
1420  default:
1421  {
1422  if( ImplIsNotTransparent( *pCurrAct,
1423  *aMapModeVDev ) )
1424  bStillBackground=false; // non-transparent action, possibly
1425  // not uniform
1426  else
1427  // extend current bounds (next uniform action
1428  // needs to fully cover this area)
1429  aBackgroundComponent.aBounds.Union(
1430  ImplCalcActionBounds(*pCurrAct, *aMapModeVDev) );
1431  break;
1432  }
1433  }
1434 
1435  // execute action to get correct MapModes etc.
1436  pCurrAct->Execute( aMapModeVDev.get() );
1437 
1438  pCurrAct=const_cast<GDIMetaFile&>(rInMtf).NextAction();
1439  ++nActionNum;
1440  }
1441 
1442  if (nLastBgAction != -1)
1443  {
1444  size_t nActionSize = rInMtf.GetActionSize();
1445  // tdf#134736 move nLastBgAction to also include any trailing pops
1446  for (size_t nPostLastBgAction = nLastBgAction + 1; nPostLastBgAction < nActionSize; ++nPostLastBgAction)
1447  {
1448  if (rInMtf.GetAction(nPostLastBgAction)->GetType() != MetaActionType::POP)
1449  break;
1450  nLastBgAction = nPostLastBgAction;
1451  }
1452  }
1453 
1454  aMapModeVDev->ClearStack(); // clean up aMapModeVDev
1455 
1456  // fast-forward until one after the last background action
1457  // (need to reconstruct map mode vdev state)
1458  nActionNum=0;
1459  pCurrAct=const_cast<GDIMetaFile&>(rInMtf).FirstAction();
1460  while( pCurrAct && nActionNum<=nLastBgAction )
1461  {
1462  // up to and including last ink-generating background
1463  // action go to background component
1464  aBackgroundComponent.aComponentList.emplace_back(
1465  pCurrAct, nActionNum );
1466 
1467  // execute action to get correct MapModes etc.
1468  pCurrAct->Execute( aMapModeVDev.get() );
1469  pCurrAct=const_cast<GDIMetaFile&>(rInMtf).NextAction();
1470  ++nActionNum;
1471  }
1472 
1473  // STAGE 2: Generate connected components list
1474 
1475  ::std::vector<ConnectedComponents> aCCList; // contains distinct sets of connected components as elements.
1476 
1477  // iterate over all actions (start where background action
1478  // search left off)
1479  for( ;
1480  pCurrAct;
1481  pCurrAct=const_cast<GDIMetaFile&>(rInMtf).NextAction(), ++nActionNum )
1482  {
1483  // execute action to get correct MapModes etc.
1484  pCurrAct->Execute( aMapModeVDev.get() );
1485 
1486  // cache bounds of current action
1487  const tools::Rectangle aBBCurrAct( ImplCalcActionBounds(*pCurrAct, *aMapModeVDev) );
1488 
1489  // accumulate collected bounds here, initialize with current action
1490  tools::Rectangle aTotalBounds( aBBCurrAct ); // thus, aTotalComponents.aBounds is empty
1491  // for non-output-generating actions
1492  bool bTreatSpecial( false );
1493  ConnectedComponents aTotalComponents;
1494 
1495  // STAGE 2.1: Search for intersecting cc entries
1496 
1497  // if aBBCurrAct is empty, it will intersect with no
1498  // aCCList member. Thus, we can save the check.
1499  // Furthermore, this ensures that non-output-generating
1500  // actions get their own aCCList entry, which is necessary
1501  // when copying them to the output metafile (see stage 4
1502  // below).
1503 
1504  // #107169# Wholly transparent objects need
1505  // not be considered for connected components,
1506  // too. Just put each of them into a separate
1507  // component.
1508  aTotalComponents.bIsFullyTransparent = !ImplIsNotTransparent(*pCurrAct, *aMapModeVDev);
1509 
1510  if( !aBBCurrAct.IsEmpty() &&
1511  !aTotalComponents.bIsFullyTransparent )
1512  {
1513  if( !aBackgroundComponent.aComponentList.empty() &&
1514  !aBackgroundComponent.aBounds.Contains(aTotalBounds) )
1515  {
1516  // it seems the background is not large enough. to
1517  // be on the safe side, combine with this component.
1518  aTotalBounds.Union( aBackgroundComponent.aBounds );
1519 
1520  // extract all aCurr actions to aTotalComponents
1521  aTotalComponents.aComponentList.splice( aTotalComponents.aComponentList.end(),
1522  aBackgroundComponent.aComponentList );
1523 
1524  if( aBackgroundComponent.bIsSpecial )
1525  bTreatSpecial = true;
1526  }
1527 
1528  bool bSomeComponentsChanged;
1529 
1530  // now, this is unfortunate: since changing anyone of
1531  // the aCCList elements (e.g. by merging or addition
1532  // of an action) might generate new intersection with
1533  // other aCCList elements, have to repeat the whole
1534  // element scanning, until nothing changes anymore.
1535  // Thus, this loop here makes us O(n^3) in the worst
1536  // case.
1537  do
1538  {
1539  // only loop here if 'intersects' branch below was hit
1540  bSomeComponentsChanged = false;
1541 
1542  // iterate over all current members of aCCList
1543  for( auto aCurrCC=aCCList.begin(); aCurrCC != aCCList.end(); )
1544  {
1545  // first check if current element's bounds are
1546  // empty. This ensures that empty actions are not
1547  // merged into one component, as a matter of fact,
1548  // they have no position.
1549 
1550  // #107169# Wholly transparent objects need
1551  // not be considered for connected components,
1552  // too. Just put each of them into a separate
1553  // component.
1554  if( !aCurrCC->aBounds.IsEmpty() &&
1555  !aCurrCC->bIsFullyTransparent &&
1556  aCurrCC->aBounds.Overlaps( aTotalBounds ) )
1557  {
1558  // union the intersecting aCCList element into aTotalComponents
1559 
1560  // calc union bounding box
1561  aTotalBounds.Union( aCurrCC->aBounds );
1562 
1563  // extract all aCurr actions to aTotalComponents
1564  aTotalComponents.aComponentList.splice( aTotalComponents.aComponentList.end(),
1565  aCurrCC->aComponentList );
1566 
1567  if( aCurrCC->bIsSpecial )
1568  bTreatSpecial = true;
1569 
1570  // remove and delete aCurrCC element from list (we've now merged its content)
1571  aCurrCC = aCCList.erase( aCurrCC );
1572 
1573  // at least one component changed, need to rescan everything
1574  bSomeComponentsChanged = true;
1575  }
1576  else
1577  {
1578  ++aCurrCC;
1579  }
1580  }
1581  }
1582  while( bSomeComponentsChanged );
1583  }
1584 
1585  // STAGE 2.2: Determine special state for cc element
1586 
1587  // now test whether the whole connected component must be
1588  // treated specially (i.e. rendered as a bitmap): if the
1589  // added action is the very first action, or all actions
1590  // before it are completely transparent, the connected
1591  // component need not be treated specially, not even if
1592  // the added action contains transparency. This is because
1593  // painting of transparent objects on _white background_
1594  // works without alpha compositing (you just calculate the
1595  // color). Note that for the test "all objects before me
1596  // are transparent" no sorting is necessary, since the
1597  // added metaaction pCurrAct is always in the order the
1598  // metafile is painted. Generally, the order of the
1599  // metaactions in the ConnectedComponents are not
1600  // guaranteed to be the same as in the metafile.
1601  if( bTreatSpecial )
1602  {
1603  // prev component(s) special -> this one, too
1604  aTotalComponents.bIsSpecial = true;
1605  }
1606  else if(!pCurrAct->IsTransparent())
1607  {
1608  // added action and none of prev components special ->
1609  // this one normal, too
1610  aTotalComponents.bIsSpecial = false;
1611  }
1612  else
1613  {
1614  // added action is special and none of prev components
1615  // special -> do the detailed tests
1616 
1617  // can the action handle transparency correctly
1618  // (i.e. when painted on white background, does the
1619  // action still look correct)?
1620  if( !DoesActionHandleTransparency( *pCurrAct ) )
1621  {
1622  // no, action cannot handle its transparency on
1623  // a printer device, render to bitmap
1624  aTotalComponents.bIsSpecial = true;
1625  }
1626  else
1627  {
1628  // yes, action can handle its transparency, so
1629  // check whether we're on white background
1630  if( aTotalComponents.aComponentList.empty() )
1631  {
1632  // nothing between pCurrAct and page
1633  // background -> don't be special
1634  aTotalComponents.bIsSpecial = false;
1635  }
1636  else
1637  {
1638  // #107169# Fixes above now ensure that _no_
1639  // object in the list is fully transparent. Thus,
1640  // if the component list is not empty above, we
1641  // must assume that we have to treat this
1642  // component special.
1643 
1644  // there are non-transparent objects between
1645  // pCurrAct and the empty sheet of paper -> be
1646  // special, then
1647  aTotalComponents.bIsSpecial = true;
1648  }
1649  }
1650  }
1651 
1652  // STAGE 2.3: Add newly generated CC list element
1653 
1654  // set new bounds and add action to list
1655  aTotalComponents.aBounds = aTotalBounds;
1656  aTotalComponents.aComponentList.emplace_back(
1657  pCurrAct, nActionNum );
1658 
1659  // add aTotalComponents as a new entry to aCCList
1660  aCCList.push_back( aTotalComponents );
1661 
1662  SAL_WARN_IF( aTotalComponents.aComponentList.empty(), "vcl",
1663  "Printer::GetPreparedMetaFile empty component" );
1664  SAL_WARN_IF( aTotalComponents.aBounds.IsEmpty() && (aTotalComponents.aComponentList.size() != 1), "vcl",
1665  "Printer::GetPreparedMetaFile non-output generating actions must be solitary");
1666  SAL_WARN_IF( aTotalComponents.bIsFullyTransparent && (aTotalComponents.aComponentList.size() != 1), "vcl",
1667  "Printer::GetPreparedMetaFile fully transparent actions must be solitary");
1668  }
1669 
1670  // well now, we've got the list of disjunct connected
1671  // components. Now we've got to create a map, which contains
1672  // the corresponding aCCList element for every
1673  // metaaction. Later on, we always process the complete
1674  // metafile for each bitmap to be generated, but switch on
1675  // output only for actions contained in the then current
1676  // aCCList element. This ensures correct mapmode and attribute
1677  // settings for all cases.
1678 
1679  // maps mtf actions to CC list entries
1680  ::std::vector< const ConnectedComponents* > aCCList_MemberMap( rInMtf.GetActionSize() );
1681 
1682  // iterate over all aCCList members and their contained metaactions
1683  for (auto const& currentItem : aCCList)
1684  {
1685  for (auto const& currentAction : currentItem.aComponentList)
1686  {
1687  // set pointer to aCCList element for corresponding index
1688  aCCList_MemberMap[ currentAction.second ] = &currentItem;
1689  }
1690  }
1691 
1692  // STAGE 3.1: Output background mtf actions (if there are any)
1693 
1694  for (auto & component : aBackgroundComponent.aComponentList)
1695  {
1696  // simply add this action (above, we inserted the actions
1697  // starting at index 0 up to and including nLastBgAction)
1698  rOutMtf.AddAction( component.first );
1699  }
1700 
1701  // STAGE 3.2: Generate banded bitmaps for special regions
1702 
1703  Point aPageOffset;
1704  Size aTmpSize( GetOutputSizePixel() );
1705  if( meOutDevType == OUTDEV_PDF )
1706  {
1707  auto pPdfWriter = static_cast<vcl::PDFWriterImpl*>(this);
1708  aTmpSize = LogicToPixel(pPdfWriter->getCurPageSize(), MapMode(MapUnit::MapPoint));
1709 
1710  // also add error code to PDFWriter
1711  pPdfWriter->insertError(vcl::PDFWriter::Warning_Transparency_Converted);
1712  }
1713  else if( meOutDevType == OUTDEV_PRINTER )
1714  {
1715  Printer* pThis = dynamic_cast<Printer*>(this);
1716  assert(pThis);
1717  aPageOffset = pThis->GetPageOffsetPixel();
1718  aPageOffset = Point( 0, 0 ) - aPageOffset;
1719  aTmpSize = pThis->GetPaperSizePixel();
1720  }
1721  const tools::Rectangle aOutputRect( aPageOffset, aTmpSize );
1722  bool bTiling = dynamic_cast<Printer*>(this) != nullptr;
1723 
1724  // iterate over all aCCList members and generate bitmaps for the special ones
1725  for (auto & currentItem : aCCList)
1726  {
1727  if( currentItem.bIsSpecial )
1728  {
1729  tools::Rectangle aBoundRect( currentItem.aBounds );
1730  aBoundRect.Intersection( aOutputRect );
1731 
1732  const double fBmpArea( static_cast<double>(aBoundRect.GetWidth()) * aBoundRect.GetHeight() );
1733  const double fOutArea( static_cast<double>(aOutputRect.GetWidth()) * aOutputRect.GetHeight() );
1734 
1735  // check if output doesn't exceed given size
1736  if( bReduceTransparency && bTransparencyAutoMode && ( fBmpArea > ( fReduceTransparencyMinArea * fOutArea ) ) )
1737  {
1738  // output normally. Therefore, we simply clear the
1739  // special attribute, as everything non-special is
1740  // copied to rOutMtf further below.
1741  currentItem.bIsSpecial = false;
1742  }
1743  else
1744  {
1745  // create new bitmap action first
1746  if( aBoundRect.GetWidth() && aBoundRect.GetHeight() )
1747  {
1748  Point aDstPtPix( aBoundRect.TopLeft() );
1749  Size aDstSzPix;
1750 
1751  ScopedVclPtrInstance<VirtualDevice> aMapVDev; // here, we record only mapmode information
1752  aMapVDev->EnableOutput(false);
1753 
1754  ScopedVclPtrInstance<VirtualDevice> aPaintVDev; // into this one, we render.
1755  aPaintVDev->SetBackground( aBackgroundComponent.aBgColor );
1756 
1758  rOutMtf.AddAction( new MetaMapModeAction() );
1759 
1760  aPaintVDev->SetDrawMode( GetDrawMode() );
1761 
1762  while( aDstPtPix.Y() <= aBoundRect.Bottom() )
1763  {
1764  aDstPtPix.setX( aBoundRect.Left() );
1765  aDstSzPix = bTiling ? Size( MAX_TILE_WIDTH, MAX_TILE_HEIGHT ) : aBoundRect.GetSize();
1766 
1767  if( ( aDstPtPix.Y() + aDstSzPix.Height() - 1 ) > aBoundRect.Bottom() )
1768  aDstSzPix.setHeight( aBoundRect.Bottom() - aDstPtPix.Y() + 1 );
1769 
1770  while( aDstPtPix.X() <= aBoundRect.Right() )
1771  {
1772  if( ( aDstPtPix.X() + aDstSzPix.Width() - 1 ) > aBoundRect.Right() )
1773  aDstSzPix.setWidth( aBoundRect.Right() - aDstPtPix.X() + 1 );
1774 
1775  if( !tools::Rectangle( aDstPtPix, aDstSzPix ).Intersection( aBoundRect ).IsEmpty() &&
1776  aPaintVDev->SetOutputSizePixel( aDstSzPix ) )
1777  {
1778  aPaintVDev->Push();
1779  aMapVDev->Push();
1780 
1781  aMapVDev->mnDPIX = aPaintVDev->mnDPIX = mnDPIX;
1782  aMapVDev->mnDPIY = aPaintVDev->mnDPIY = mnDPIY;
1783 
1784  aPaintVDev->EnableOutput(false);
1785 
1786  // iterate over all actions
1787  for( pCurrAct=const_cast<GDIMetaFile&>(rInMtf).FirstAction(), nActionNum=0;
1788  pCurrAct;
1789  pCurrAct=const_cast<GDIMetaFile&>(rInMtf).NextAction(), ++nActionNum )
1790  {
1791  // enable output only for
1792  // actions that are members of
1793  // the current aCCList element
1794  // (currentItem)
1795  if( aCCList_MemberMap[nActionNum] == &currentItem )
1796  aPaintVDev->EnableOutput();
1797 
1798  // but process every action
1799  const MetaActionType nType( pCurrAct->GetType() );
1800 
1802  {
1803  pCurrAct->Execute( aMapVDev.get() );
1804 
1805  MapMode aMtfMap( aMapVDev->GetMapMode() );
1806  const Point aNewOrg( aMapVDev->PixelToLogic( aDstPtPix ) );
1807 
1808  aMtfMap.SetOrigin( Point( -aNewOrg.X(), -aNewOrg.Y() ) );
1809  aPaintVDev->SetMapMode( aMtfMap );
1810  }
1811  else if( ( MetaActionType::PUSH == nType ) || MetaActionType::POP == nType )
1812  {
1813  pCurrAct->Execute( aMapVDev.get() );
1814  pCurrAct->Execute( aPaintVDev.get() );
1815  }
1816  else if( MetaActionType::GRADIENT == nType )
1817  {
1818  MetaGradientAction* pGradientAction = static_cast<MetaGradientAction*>(pCurrAct);
1819  Printer* pPrinter = dynamic_cast< Printer* >(this);
1820  if( pPrinter )
1821  pPrinter->DrawGradientEx( aPaintVDev.get(), pGradientAction->GetRect(), pGradientAction->GetGradient() );
1822  else
1823  DrawGradient( pGradientAction->GetRect(), pGradientAction->GetGradient() );
1824  }
1825  else
1826  {
1827  pCurrAct->Execute( aPaintVDev.get() );
1828  }
1829 
1830  Application::Reschedule( true );
1831  }
1832 
1833  const bool bOldMap = mbMap;
1834  mbMap = aPaintVDev->mbMap = false;
1835 
1836  Bitmap aBandBmp( aPaintVDev->GetBitmap( Point(), aDstSzPix ) );
1837 
1838  // scale down bitmap, if requested
1839  if( bDownsampleBitmaps )
1840  aBandBmp = vcl::bitmap::GetDownsampledBitmap(PixelToLogic(LogicToPixel(aDstSzPix), MapMode(MapUnit::MapTwip)),
1841  Point(), aBandBmp.GetSizePixel(),
1842  aBandBmp, nMaxBmpDPIX, nMaxBmpDPIY);
1843 
1844  rOutMtf.AddAction( new MetaCommentAction( "PRNSPOOL_TRANSPARENTBITMAP_BEGIN" ) );
1845  rOutMtf.AddAction( new MetaBmpScaleAction( aDstPtPix, aDstSzPix, aBandBmp ) );
1846  rOutMtf.AddAction( new MetaCommentAction( "PRNSPOOL_TRANSPARENTBITMAP_END" ) );
1847 
1848  aPaintVDev->mbMap = true;
1849  mbMap = bOldMap;
1850  aMapVDev->Pop();
1851  aPaintVDev->Pop();
1852  }
1853 
1854  // overlapping bands to avoid missing lines (e.g. PostScript)
1855  aDstPtPix.AdjustX(aDstSzPix.Width() );
1856  }
1857 
1858  // overlapping bands to avoid missing lines (e.g. PostScript)
1859  aDstPtPix.AdjustY(aDstSzPix.Height() );
1860  }
1861 
1862  rOutMtf.AddAction( new MetaPopAction() );
1863  }
1864  }
1865  }
1866  }
1867 
1868  aMapModeVDev->ClearStack(); // clean up aMapModeVDev
1869 
1870  // STAGE 4: Copy actions to output metafile
1871 
1872  // iterate over all actions and duplicate the ones not in a
1873  // special aCCList member into rOutMtf
1874  for( pCurrAct=const_cast<GDIMetaFile&>(rInMtf).FirstAction(), nActionNum=0;
1875  pCurrAct;
1876  pCurrAct=const_cast<GDIMetaFile&>(rInMtf).NextAction(), ++nActionNum )
1877  {
1878  const ConnectedComponents* pCurrAssociatedComponent = aCCList_MemberMap[nActionNum];
1879 
1880  // NOTE: This relies on the fact that map-mode or draw
1881  // mode changing actions are solitary aCCList elements and
1882  // have empty bounding boxes, see comment on stage 2.1
1883  // above
1884  if( pCurrAssociatedComponent &&
1885  (pCurrAssociatedComponent->aBounds.IsEmpty() ||
1886  !pCurrAssociatedComponent->bIsSpecial) )
1887  {
1888  // #107169# Treat transparent bitmaps special, if they
1889  // are the first (or sole) action in their bounds
1890  // list. Note that we previously ensured that no
1891  // fully-transparent objects are before us here.
1892  if( DoesActionHandleTransparency( *pCurrAct ) &&
1893  pCurrAssociatedComponent->aComponentList.begin()->first == pCurrAct )
1894  {
1895  // convert actions, where masked-out parts are of
1896  // given background color
1897  ImplConvertTransparentAction(rOutMtf,
1898  *pCurrAct,
1899  *aMapModeVDev,
1900  aBackgroundComponent.aBgColor);
1901  }
1902  else
1903  {
1904  // simply add this action
1905  rOutMtf.AddAction( pCurrAct );
1906  }
1907 
1908  pCurrAct->Execute(aMapModeVDev.get());
1909  }
1910  }
1911 
1912  rOutMtf.SetPrefMapMode( rInMtf.GetPrefMapMode() );
1913  rOutMtf.SetPrefSize( rInMtf.GetPrefSize() );
1914 
1915 #if OSL_DEBUG_LEVEL > 1
1916  // iterate over all aCCList members and generate rectangles for the bounding boxes
1917  rOutMtf.AddAction( new MetaFillColorAction( COL_WHITE, false ) );
1918  for(auto const& aCurr:aCCList)
1919  {
1920  if( aCurr.bIsSpecial )
1921  rOutMtf.AddAction( new MetaLineColorAction( COL_RED, true) );
1922  else
1923  rOutMtf.AddAction( new MetaLineColorAction( COL_BLUE, true) );
1924 
1925  rOutMtf.AddAction( new MetaRectAction( aMapModeVDev->PixelToLogic( aCurr.aBounds ) ) );
1926  }
1927 #endif
1928  }
1929  return bTransparent;
1930 }
1931 
1932 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
sal_uInt16 Count() const
BitmapEx GetBitmapEx(BitmapEx const &rBitmapEx, DrawModeFlags nDrawMode)
Definition: drawmode.cxx:224
vcl::Region GetClipRegion() const
virtual void Execute(OutputDevice *pOut)
Definition: metaact.cxx:97
const LineInfo & GetLineInfo() const
Definition: metaact.hxx:184
sal_uInt8 GetIndex() const
Definition: BitmapColor.hxx:70
const LineInfo & GetLineInfo() const
Definition: metaact.hxx:406
sal_Int32 GetLen() const
Definition: metaact.hxx:495
sal_uInt8 GetRed() const
SAL_DLLPRIVATE bool ImplIsRecordLayout() const
Definition: outdev.cxx:702
#define MAX_TILE_HEIGHT
Definition: transparent.cxx:42
double getHeight() const
tools::Rectangle & Intersection(const tools::Rectangle &rRect)
tools::Long AdjustRight(tools::Long nHorzMoveDelta)
virtual void ClipToPaintRegion(tools::Rectangle &rDstRect)
void Merge(const Color &rMergeColor, sal_uInt8 cTransparency)
tools::Rectangle ImplGetTextBoundRect(const SalLayout &) const
Definition: text.cxx:187
void setClosed(bool bNew)
constexpr bool isPalettePixelFormat(PixelFormat ePixelFormat)
Is it a pixel format that forces creation of a palette.
Definition: BitmapTypes.hxx:29
constexpr tools::Long Left() const
void DrawBitmapEx(const Point &rDestPt, const BitmapEx &rBitmapEx)
Definition: bitmapex.cxx:33
virtual bool supportsOperation(OutDevSupportType) const =0
constexpr::Color COL_RED(0x80, 0x00, 0x00)
const MapMode & GetPrefMapMode() const
Definition: gdimtf.hxx:175
DrawModeFlags mnDrawMode
Definition: outdev.hxx:219
long Long
constexpr::Color COL_TRANSPARENT(ColorTransparency, 0xFF, 0xFF, 0xFF, 0xFF)
void disposeAndClear()
Definition: vclptr.hxx:200
void Push(vcl::PushFlags nFlags=vcl::PushFlags::ALL)
Definition: stack.cxx:33
std::enable_if< std::is_signed< T >::value||std::is_floating_point< T >::value, long >::type MinMax(T nVal, tools::Long nMin, tools::Long nMax)
bool IsMapModeEnabled() const
Definition: outdev.hxx:1526
virtual bool IsTransparent() const
#i10613# Extracted from Printer::GetPreparedMetaFile.
Definition: metaact.hxx:97
bool mbOutputClipped
Definition: outdev.hxx:244
void SetPrefSize(const Size &rSize)
Definition: gdimtf.hxx:173
void Clear()
Definition: gdimtf.cxx:270
void DrawPolyPolygon(sal_uInt32 nPoly, const sal_uInt32 *pPoints, const Point **pPtAry, const OutputDevice &rOutDev)
SAL_DLLPRIVATE bool is_double_buffered_window() const
const MapMode & GetMapMode() const
Definition: outdev.hxx:1532
sal_Int32 mnDPIY
Definition: outdev.hxx:212
const tools::PolyPolygon & GetPolyPolygon() const
Definition: metaact.hxx:1553
bool DrawAlphaRect(tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight, sal_uInt8 nTransparency, const OutputDevice &rOutDev)
Bitmap GetDownsampledBitmap(Size const &rDstSizeTwip, Point const &rSrcPt, Size const &rSrcSz, Bitmap const &rBmp, tools::Long nMaxBmpDPIX, tools::Long nMaxBmpDPIY)
Retrieve downsampled and cropped bitmap.
const Point & GetEndPoint() const
Definition: metaact.hxx:183
sal_Int32 mnDPIX
Definition: outdev.hxx:211
const OUString & GetText() const
Definition: metaact.hxx:571
This template handles BitmapAccess the RAII way.
void EnableMapMode(bool bEnable=true)
Definition: map.cxx:586
bool mbMap
Definition: outdev.hxx:239
tools::Rectangle GetBoundRect() const
Definition: region.cxx:1217
AntialiasingFlags GetAntialiasing() const
Definition: outdev.hxx:480
HashMap_OWString_Interface aMap
SAL_DLLPRIVATE void InitLineColor()
Definition: line.cxx:84
std::unique_ptr< SalLayout > ImplLayout(const OUString &, sal_Int32 nIndex, sal_Int32 nLen, const Point &rLogicPos=Point(0, 0), tools::Long nLogicWidth=0, const tools::Long *pLogicDXArray=nullptr, SalLayoutFlags flags=SalLayoutFlags::NONE, vcl::text::TextLayoutCache const *=nullptr, const SalLayoutGlyphs *pGlyphs=nullptr) const
Definition: text.cxx:1223
bool IsAlpha() const
Definition: BitmapEx.cxx:193
double getWidth() const
const OUString & GetText() const
Definition: metaact.hxx:493
Scanline GetScanline(tools::Long nY) const
int nCount
AntialiasingFlags mnAntialiasing
Definition: outdev.hxx:236
virtual Bitmap GetBitmap(const Point &rSrcPt, const Size &rSize) const
const Gradient & GetGradient() const
Definition: metaact.hxx:1011
constexpr tools::Long GetWidth() const
void Pop()
Definition: stack.cxx:92
const Size & GetPaperSizePixel() const
Definition: print.hxx:247
void Insert(const tools::Polygon &rPoly, sal_uInt16 nPos=POLYPOLY_APPEND)
MetaActionType
void DrawInvisiblePolygon(const tools::PolyPolygon &rPolyPoly)
void DrawGradientEx(OutputDevice *pOut, const tools::Rectangle &rRect, const Gradient &rGradient)
Definition: print2.cxx:29
tools::Long AdjustBottom(tools::Long nVertMoveDelta)
BitmapColor GetBestMatchingColor(const BitmapColor &rBitmapColor) const
sal_uInt8 GetBlue() const
::std::pair< MetaAction *, int > Component
constexpr bool IsEmpty() const
SalGraphics * mpGraphics
Graphics context to draw on.
Definition: outdev.hxx:181
SAL_DLLPRIVATE bool DrawTransparentNatively(const tools::PolyPolygon &rPolyPoly, sal_uInt16 nTransparencePercent)
SAL_DLLPRIVATE basegfx::B2DHomMatrix ImplGetDeviceTransformation() const
Get device transformation.
Definition: map.cxx:869
virtual void SetFillColor()=0
void BlendWith(const Bitmap &rOther)
Definition: alpha.cxx:83
sal_uInt16 GetEntryCount() const
virtual void EmulateDrawTransparent(const tools::PolyPolygon &rPolyPoly, sal_uInt16 nTransparencePercent)
SAL_DLLPRIVATE void InitFillColor()
Definition: fill.cxx:76
sal_uInt8 * Scanline
Definition: Scanline.hxx:26
vcl::PixelFormat getPixelFormat() const
static bool Reschedule(bool bHandleAllCurrentEvents=false)
Attempt to process current pending event(s)
Definition: svapp.cxx:476
const Size & GetPrefSize() const
Definition: gdimtf.hxx:172
bool isClosed() const
const tools::Polygon & GetPolygon() const
Definition: metaact.hxx:405
int i
virtual tools::Rectangle GetBackgroundComponentBounds() const
Definition: outdev.cxx:634
bool IsClipRegion() const
Definition: outdev.hxx:551
sal_uInt16 GetTransparence() const
Definition: metaact.hxx:1554
ScanlineFormat GetScanlineFormat() const
bool IsLineColor() const
Definition: outdev.hxx:507
const Point & GetPoint() const
Definition: metaact.hxx:492
tools::Long FRound(double fVal)
virtual bool AcquireGraphics() const =0
Acquire a graphics device that the output device uses to draw on.
const tools::Rectangle & GetRect() const
Definition: metaact.hxx:1010
bool IsRect() const
void DrawTransparent(const tools::PolyPolygon &rPolyPoly, sal_uInt16 nTransparencePercent)
void SetOrigin(const Point &rOrigin)
Definition: mapmod.cxx:104
sal_uInt32 GetWidth() const
Definition: metaact.hxx:572
bool mbInitLineColor
Definition: outdev.hxx:247
const OutDevType meOutDevType
Definition: outdev.hxx:222
constexpr tools::Long Right() const
void SetFillColor()
Definition: fill.cxx:29
Some things multiple-inherit from VclAbstractDialog and OutputDevice, so we need to use virtual inher...
Definition: outdev.hxx:168
const Color & GetLineColor() const
Definition: outdev.hxx:506
void SetRed(sal_uInt8 nRed)
void Move(tools::Long nHorzMove, tools::Long nVertMove)
void transform(const basegfx::B2DHomMatrix &rMatrix)
void DrawPolyLine(sal_uInt32 nPoints, Point const *pPtAry, const OutputDevice &rOutDev)
bool mbLineColor
Definition: outdev.hxx:245
constexpr double deg2rad(double v)
bool IsFullyTransparent() const
sal_Int32 GetLen() const
Definition: metaact.hxx:574
Size GetOutputSizePixel() const
Definition: outdev.hxx:310
::basegfx::B2DPolygon getB2DPolygon() const
virtual sal_uInt16 GetBitCount() const
Definition: outdev.cxx:366
DrawModeFlags GetDrawMode() const
Definition: outdev.hxx:483
bool HasTransparentActions() const
Definition: gdimtf.cxx:154
void DrawBitmap(const Point &rDestPt, const Bitmap &rBitmap)
sal_Int32 nLineWidth
constexpr Point Center() const
constexpr Point TopLeft() const
tools::Long AdjustTop(tools::Long nVertMoveDelta)
sal_uInt16 GetBestIndex(const BitmapColor &rCol) const
const Color & GetStartColor() const
constexpr tools::Long Bottom() const
Bitmap GetBitmap(Color aTransparentReplaceColor) const
Definition: BitmapEx.cxx:203
SAL_WARN_UNUSED_RESULT Point PixelToLogic(const Point &rDevicePt) const
Definition: map.cxx:1109
SAL_WARN_UNUSED_RESULT Point LogicToPixel(const Point &rLogicPt) const
Definition: map.cxx:878
const Point & GetPoint() const
Definition: metaact.hxx:570
sal_uInt8 GetGreen() const
B2DRange const & getB2DRange() const
const Point & GetStartPoint() const
Definition: metaact.hxx:182
bool mbFillColor
Definition: outdev.hxx:246
VclPtr< VirtualDevice > mpAlphaVDev
Definition: outdev.hxx:195
constexpr Size GetSize() const
const Point & GetPageOffsetPixel() const
Definition: print.hxx:250
double getMinY() const
sal_uInt32 count() const
AlphaMask GetAlpha() const
Definition: BitmapEx.cxx:215
::basegfx::B2DPolyPolygon getB2DPolyPolygon() const
tools::Rectangle & Union(const tools::Rectangle &rRect)
#define SAL_WARN_IF(condition, area, stream)
bool RemoveTransparenciesFromMetaFile(const GDIMetaFile &rInMtf, GDIMetaFile &rOutMtf, tools::Long nMaxBmpDPIX, tools::Long nMaxBmpDPIY, bool bReduceTransparency, bool bTransparencyAutoMode, bool bDownsampleBitmaps, const Color &rBackground=COL_TRANSPARENT)
helper method removing transparencies from a metafile (e.g.
unsigned char sal_uInt8
bool Convert(BmpConversion eConversion)
Convert bitmap format.
constexpr sal_uInt8 ColorChannelMerge(sal_uInt8 nDst, sal_uInt8 nSrc, sal_uInt8 nSrcTrans)
void AddAction(const rtl::Reference< MetaAction > &pAction)
Definition: gdimtf.cxx:562
MetaAction * GetAction(size_t nAction) const
Definition: gdimtf.cxx:184
bool mbInitClipRegion
Definition: outdev.hxx:251
const ::std::vector< Color > ImpSvNumberformatScan::StandardColor COL_WHITE
const Color & GetEndColor() const
void DrawPolyPolygon(const tools::PolyPolygon &rPolyPoly)
Render the given poly-polygon.
Definition: polygon.cxx:36
virtual void InitClipRegion()
bool IsEmpty() const
size_t GetActionSize() const
Definition: gdimtf.cxx:179
QPRO_FUNC_TYPE nType
const ::std::vector< Color > ImpSvNumberformatScan::StandardColor COL_BLACK
RasterOp GetRasterOp() const
Definition: outdev.hxx:492
BitmapColor GetPixelFromData(const sal_uInt8 *pData, tools::Long nX) const
MetaActionType GetType() const
Definition: metaact.hxx:93
bool mbInitFillColor
Definition: outdev.hxx:248
bool GetTextBoundRect(tools::Rectangle &rRect, const OUString &rStr, sal_Int32 nBase=0, sal_Int32 nIndex=0, sal_Int32 nLen=-1, sal_uLong nLayoutWidth=0, const tools::Long *pDXArray=nullptr, const SalLayoutGlyphs *pGlyphs=nullptr) const
Return the exact bounding rectangle of rStr.
Definition: text.cxx:2235
SAL_DLLPRIVATE tools::Rectangle ImplLogicToDevicePixel(const tools::Rectangle &rLogicRect) const
Convert a logical rectangle to a rectangle in physical device pixel units.
Definition: map.cxx:386
void setHeight(tools::Long nHeight)
void Move(tools::Long nHorzMoveDelta, tools::Long nVertMoveDelta)
reference_type * get() const
Get the body.
Definition: vclptr.hxx:143
tools::Rectangle GetBoundRect() const
tools::Long AdjustLeft(tools::Long nHorzMoveDelta)
double getMinX() const
constexpr::Color COL_BLUE(0x00, 0x00, 0x80)
tools::Rectangle GetBoundRect() const
bool IsFillColor() const
Definition: outdev.hxx:512
bool IsDeviceOutputNecessary() const
Definition: outdev.hxx:477
void DrawGradient(const tools::Rectangle &rRect, const Gradient &rGradient)
sal_Int32 GetIndex() const
Definition: metaact.hxx:573
bool isRectangle(const B2DPolygon &rPoly)
sal_Int32 GetIndex() const
Definition: metaact.hxx:494
#define MAX_TILE_WIDTH
Definition: transparent.cxx:41
const Color & GetFillColor() const
Definition: outdev.hxx:511
GDIMetaFile * mpMetaFile
Definition: outdev.hxx:184
void SetPrefMapMode(const MapMode &rMapMode)
Definition: gdimtf.hxx:176
constexpr tools::Long GetHeight() const
void Insert(sal_uInt16 nPos, const Point &rPt)