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