Version 5.4.3.2, tag libreoffice-5.4.3.2
[LibreOffice.git] / vcl / source / gdi / print2.cxx
blob74ab167ecbdd8c39a8189efdf1f98fa91562761d
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <functional>
21 #include <algorithm>
22 #include <utility>
23 #include <list>
24 #include <vector>
26 #include <basegfx/polygon/b2dpolygon.hxx>
27 #include <basegfx/polygon/b2dpolygontools.hxx>
30 #include <vcl/virdev.hxx>
31 #include <vcl/metaact.hxx>
32 #include <vcl/gdimtf.hxx>
33 #include <vcl/print.hxx>
34 #include <vcl/svapp.hxx>
35 #include <vcl/bitmapaccess.hxx>
37 #include <print.h>
39 #include "pdfwriter_impl.hxx"
41 #define MAX_TILE_WIDTH 1024
42 #define MAX_TILE_HEIGHT 1024
44 typedef ::std::pair< MetaAction*, int > Component; // MetaAction plus index in metafile
46 typedef ::std::list< Component > ComponentList;
48 // List of (intersecting) actions, plus overall bounds
49 struct ConnectedComponents
51 ConnectedComponents() :
52 aComponentList(),
53 aBounds(),
54 aBgColor(COL_WHITE),
55 bIsSpecial(false),
56 bIsFullyTransparent(false)
59 ComponentList aComponentList;
60 tools::Rectangle aBounds;
61 Color aBgColor;
62 bool bIsSpecial;
63 bool bIsFullyTransparent;
66 typedef ::std::list< ConnectedComponents > ConnectedComponentsList;
68 namespace {
70 /** \#i10613# Extracted from Printer::GetPreparedMetaFile. Returns true
71 if given action requires special transparency handling
73 bool IsTransparentAction( const MetaAction& rAct )
75 switch( rAct.GetType() )
77 case MetaActionType::Transparent:
78 return true;
80 case MetaActionType::FLOATTRANSPARENT:
81 return true;
83 case MetaActionType::BMPEX:
84 return static_cast<const MetaBmpExAction&>(rAct).GetBitmapEx().IsTransparent();
86 case MetaActionType::BMPEXSCALE:
87 return static_cast<const MetaBmpExScaleAction&>(rAct).GetBitmapEx().IsTransparent();
89 case MetaActionType::BMPEXSCALEPART:
90 return static_cast<const MetaBmpExScalePartAction&>(rAct).GetBitmapEx().IsTransparent();
92 default:
93 return false;
98 /** Determines whether the action can handle transparency correctly
99 (i.e. when painted on white background, does the action still look
100 correct)?
102 bool DoesActionHandleTransparency( const MetaAction& rAct )
104 // MetaActionType::FLOATTRANSPARENT can contain a whole metafile,
105 // which is to be rendered with the given transparent gradient. We
106 // currently cannot emulate transparent painting on a white
107 // background reliably.
109 // the remainder can handle printing itself correctly on a uniform
110 // white background.
111 switch( rAct.GetType() )
113 case MetaActionType::Transparent:
114 case MetaActionType::BMPEX:
115 case MetaActionType::BMPEXSCALE:
116 case MetaActionType::BMPEXSCALEPART:
117 return true;
119 default:
120 return false;
124 /** Check whether rCurrRect rectangle fully covers io_rPrevRect - if
125 yes, return true and update o_rBgColor
127 bool checkRect( tools::Rectangle& io_rPrevRect,
128 Color& o_rBgColor,
129 const tools::Rectangle& rCurrRect,
130 OutputDevice& rMapModeVDev )
132 // shape needs to fully cover previous content, and have uniform
133 // color
134 const bool bRet(
135 rMapModeVDev.LogicToPixel(rCurrRect).IsInside(io_rPrevRect) &&
136 rMapModeVDev.IsFillColor() );
138 if( bRet )
140 io_rPrevRect = rCurrRect;
141 o_rBgColor = rMapModeVDev.GetFillColor();
144 return bRet;
147 /** #107169# Convert BitmapEx to Bitmap with appropriately blended
148 color. Convert MetaTransparentAction to plain polygon,
149 appropriately colored
151 @param o_rMtf
152 Add converted actions to this metafile
154 void ImplConvertTransparentAction( GDIMetaFile& o_rMtf,
155 const MetaAction& rAct,
156 const OutputDevice& rStateOutDev,
157 Color aBgColor )
159 if( rAct.GetType() == MetaActionType::Transparent )
161 const MetaTransparentAction* pTransAct = static_cast<const MetaTransparentAction*>(&rAct);
162 sal_uInt16 nTransparency( pTransAct->GetTransparence() );
164 // #i10613# Respect transparency for draw color
165 if( nTransparency )
167 o_rMtf.AddAction( new MetaPushAction( PushFlags::LINECOLOR|PushFlags::FILLCOLOR ) );
169 // assume white background for alpha blending
170 Color aLineColor( rStateOutDev.GetLineColor() );
171 aLineColor.SetRed( static_cast<sal_uInt8>( (255*nTransparency + (100 - nTransparency)*aLineColor.GetRed()) / 100 ) );
172 aLineColor.SetGreen( static_cast<sal_uInt8>( (255*nTransparency + (100 - nTransparency)*aLineColor.GetGreen()) / 100 ) );
173 aLineColor.SetBlue( static_cast<sal_uInt8>( (255*nTransparency + (100 - nTransparency)*aLineColor.GetBlue()) / 100 ) );
174 o_rMtf.AddAction( new MetaLineColorAction(aLineColor, true) );
176 Color aFillColor( rStateOutDev.GetFillColor() );
177 aFillColor.SetRed( static_cast<sal_uInt8>( (255*nTransparency + (100 - nTransparency)*aFillColor.GetRed()) / 100 ) );
178 aFillColor.SetGreen( static_cast<sal_uInt8>( (255*nTransparency + (100 - nTransparency)*aFillColor.GetGreen()) / 100 ) );
179 aFillColor.SetBlue( static_cast<sal_uInt8>( (255*nTransparency + (100 - nTransparency)*aFillColor.GetBlue()) / 100 ) );
180 o_rMtf.AddAction( new MetaFillColorAction(aFillColor, true) );
183 o_rMtf.AddAction( new MetaPolyPolygonAction(pTransAct->GetPolyPolygon()) );
185 if( nTransparency )
186 o_rMtf.AddAction( new MetaPopAction() );
188 else
190 BitmapEx aBmpEx;
192 switch( rAct.GetType() )
194 case MetaActionType::BMPEX:
195 aBmpEx = static_cast<const MetaBmpExAction&>(rAct).GetBitmapEx();
196 break;
198 case MetaActionType::BMPEXSCALE:
199 aBmpEx = static_cast<const MetaBmpExScaleAction&>(rAct).GetBitmapEx();
200 break;
202 case MetaActionType::BMPEXSCALEPART:
203 aBmpEx = static_cast<const MetaBmpExScaleAction&>(rAct).GetBitmapEx();
204 break;
206 case MetaActionType::Transparent:
208 default:
209 OSL_FAIL("Printer::GetPreparedMetafile impossible state reached");
210 break;
213 Bitmap aBmp( aBmpEx.GetBitmap() );
214 if( !aBmpEx.IsAlpha() )
216 // blend with mask
217 Bitmap::ScopedReadAccess pRA(aBmp);
219 if( !pRA )
220 return; // what else should I do?
222 Color aActualColor( aBgColor );
224 if( pRA->HasPalette() )
225 aActualColor = pRA->GetBestPaletteColor( aBgColor ).operator Color();
227 pRA.reset();
229 // did we get true white?
230 if( aActualColor.GetColorError( aBgColor ) )
232 // no, create truecolor bitmap, then
233 aBmp.Convert( BmpConversion::N24Bit );
235 // fill masked out areas white
236 aBmp.Replace( aBmpEx.GetMask(), aBgColor );
238 else
240 // fill masked out areas white
241 aBmp.Replace( aBmpEx.GetMask(), aActualColor );
244 else
246 // blend with alpha channel
247 aBmp.Convert( BmpConversion::N24Bit );
248 aBmp.Blend(aBmpEx.GetAlpha(),aBgColor);
251 // add corresponding action
252 switch( rAct.GetType() )
254 case MetaActionType::BMPEX:
255 o_rMtf.AddAction( new MetaBmpAction(
256 static_cast<const MetaBmpExAction&>(rAct).GetPoint(),
257 aBmp ));
258 break;
259 case MetaActionType::BMPEXSCALE:
260 o_rMtf.AddAction( new MetaBmpScaleAction(
261 static_cast<const MetaBmpExScaleAction&>(rAct).GetPoint(),
262 static_cast<const MetaBmpExScaleAction&>(rAct).GetSize(),
263 aBmp ));
264 break;
265 case MetaActionType::BMPEXSCALEPART:
266 o_rMtf.AddAction( new MetaBmpScalePartAction(
267 static_cast<const MetaBmpExScalePartAction&>(rAct).GetDestPoint(),
268 static_cast<const MetaBmpExScalePartAction&>(rAct).GetDestSize(),
269 static_cast<const MetaBmpExScalePartAction&>(rAct).GetSrcPoint(),
270 static_cast<const MetaBmpExScalePartAction&>(rAct).GetSrcSize(),
271 aBmp ));
272 break;
273 default:
274 OSL_FAIL("Unexpected case");
275 break;
280 // #i10613# Extracted from ImplCheckRect::ImplCreate
281 // Returns true, if given action creates visible (i.e. non-transparent) output
282 bool ImplIsNotTransparent( const MetaAction& rAct, const OutputDevice& rOut )
284 const bool bLineTransparency( !rOut.IsLineColor() || rOut.GetLineColor().GetTransparency() == 255 );
285 const bool bFillTransparency( !rOut.IsFillColor() || rOut.GetFillColor().GetTransparency() == 255 );
286 bool bRet( false );
288 switch( rAct.GetType() )
290 case MetaActionType::POINT:
291 if( !bLineTransparency )
292 bRet = true;
293 break;
295 case MetaActionType::LINE:
296 if( !bLineTransparency )
297 bRet = true;
298 break;
300 case MetaActionType::RECT:
301 if( !bLineTransparency || !bFillTransparency )
302 bRet = true;
303 break;
305 case MetaActionType::ROUNDRECT:
306 if( !bLineTransparency || !bFillTransparency )
307 bRet = true;
308 break;
310 case MetaActionType::ELLIPSE:
311 if( !bLineTransparency || !bFillTransparency )
312 bRet = true;
313 break;
315 case MetaActionType::ARC:
316 if( !bLineTransparency || !bFillTransparency )
317 bRet = true;
318 break;
320 case MetaActionType::PIE:
321 if( !bLineTransparency || !bFillTransparency )
322 bRet = true;
323 break;
325 case MetaActionType::CHORD:
326 if( !bLineTransparency || !bFillTransparency )
327 bRet = true;
328 break;
330 case MetaActionType::POLYLINE:
331 if( !bLineTransparency )
332 bRet = true;
333 break;
335 case MetaActionType::POLYGON:
336 if( !bLineTransparency || !bFillTransparency )
337 bRet = true;
338 break;
340 case MetaActionType::POLYPOLYGON:
341 if( !bLineTransparency || !bFillTransparency )
342 bRet = true;
343 break;
345 case MetaActionType::TEXT:
347 const MetaTextAction& rTextAct = static_cast<const MetaTextAction&>(rAct);
348 const OUString aString( rTextAct.GetText().copy(rTextAct.GetIndex(), rTextAct.GetLen()) );
349 if (!aString.isEmpty())
350 bRet = true;
352 break;
354 case MetaActionType::TEXTARRAY:
356 const MetaTextArrayAction& rTextAct = static_cast<const MetaTextArrayAction&>(rAct);
357 const OUString aString( rTextAct.GetText().copy(rTextAct.GetIndex(), rTextAct.GetLen()) );
358 if (!aString.isEmpty())
359 bRet = true;
361 break;
363 case MetaActionType::PIXEL:
364 case MetaActionType::BMP:
365 case MetaActionType::BMPSCALE:
366 case MetaActionType::BMPSCALEPART:
367 case MetaActionType::BMPEX:
368 case MetaActionType::BMPEXSCALE:
369 case MetaActionType::BMPEXSCALEPART:
370 case MetaActionType::MASK:
371 case MetaActionType::MASKSCALE:
372 case MetaActionType::MASKSCALEPART:
373 case MetaActionType::GRADIENT:
374 case MetaActionType::GRADIENTEX:
375 case MetaActionType::HATCH:
376 case MetaActionType::WALLPAPER:
377 case MetaActionType::Transparent:
378 case MetaActionType::FLOATTRANSPARENT:
379 case MetaActionType::EPS:
380 case MetaActionType::TEXTRECT:
381 case MetaActionType::STRETCHTEXT:
382 case MetaActionType::TEXTLINE:
383 // all other actions: generate non-transparent output
384 bRet = true;
385 break;
387 default:
388 break;
391 return bRet;
394 // #i10613# Extracted from ImplCheckRect::ImplCreate
395 tools::Rectangle ImplCalcActionBounds( const MetaAction& rAct, const OutputDevice& rOut )
397 tools::Rectangle aActionBounds;
399 switch( rAct.GetType() )
401 case MetaActionType::PIXEL:
402 aActionBounds = tools::Rectangle( static_cast<const MetaPixelAction&>(rAct).GetPoint(), Size( 1, 1 ) );
403 break;
405 case MetaActionType::POINT:
406 aActionBounds = tools::Rectangle( static_cast<const MetaPointAction&>(rAct).GetPoint(), Size( 1, 1 ) );
407 break;
409 case MetaActionType::LINE:
411 const MetaLineAction& rMetaLineAction = static_cast<const MetaLineAction&>(rAct);
412 aActionBounds = tools::Rectangle( rMetaLineAction.GetStartPoint(), rMetaLineAction.GetEndPoint() );
413 aActionBounds.Justify();
414 const long nLineWidth(rMetaLineAction.GetLineInfo().GetWidth());
415 if(nLineWidth)
417 const long nHalfLineWidth((nLineWidth + 1) / 2);
418 aActionBounds.Left() -= nHalfLineWidth;
419 aActionBounds.Top() -= nHalfLineWidth;
420 aActionBounds.Right() += nHalfLineWidth;
421 aActionBounds.Bottom() += nHalfLineWidth;
423 break;
426 case MetaActionType::RECT:
427 aActionBounds = static_cast<const MetaRectAction&>(rAct).GetRect();
428 break;
430 case MetaActionType::ROUNDRECT:
431 aActionBounds = tools::Polygon( static_cast<const MetaRoundRectAction&>(rAct).GetRect(),
432 static_cast<const MetaRoundRectAction&>(rAct).GetHorzRound(),
433 static_cast<const MetaRoundRectAction&>(rAct).GetVertRound() ).GetBoundRect();
434 break;
436 case MetaActionType::ELLIPSE:
438 const tools::Rectangle& rRect = static_cast<const MetaEllipseAction&>(rAct).GetRect();
439 aActionBounds = tools::Polygon( rRect.Center(),
440 rRect.GetWidth() >> 1,
441 rRect.GetHeight() >> 1 ).GetBoundRect();
442 break;
445 case MetaActionType::ARC:
446 aActionBounds = tools::Polygon( static_cast<const MetaArcAction&>(rAct).GetRect(),
447 static_cast<const MetaArcAction&>(rAct).GetStartPoint(),
448 static_cast<const MetaArcAction&>(rAct).GetEndPoint(), PolyStyle::Arc ).GetBoundRect();
449 break;
451 case MetaActionType::PIE:
452 aActionBounds = tools::Polygon( static_cast<const MetaPieAction&>(rAct).GetRect(),
453 static_cast<const MetaPieAction&>(rAct).GetStartPoint(),
454 static_cast<const MetaPieAction&>(rAct).GetEndPoint(), PolyStyle::Pie ).GetBoundRect();
455 break;
457 case MetaActionType::CHORD:
458 aActionBounds = tools::Polygon( static_cast<const MetaChordAction&>(rAct).GetRect(),
459 static_cast<const MetaChordAction&>(rAct).GetStartPoint(),
460 static_cast<const MetaChordAction&>(rAct).GetEndPoint(), PolyStyle::Chord ).GetBoundRect();
461 break;
463 case MetaActionType::POLYLINE:
465 const MetaPolyLineAction& rMetaPolyLineAction = static_cast<const MetaPolyLineAction&>(rAct);
466 aActionBounds = rMetaPolyLineAction.GetPolygon().GetBoundRect();
467 const long nLineWidth(rMetaPolyLineAction.GetLineInfo().GetWidth());
468 if(nLineWidth)
470 const long nHalfLineWidth((nLineWidth + 1) / 2);
471 aActionBounds.Left() -= nHalfLineWidth;
472 aActionBounds.Top() -= nHalfLineWidth;
473 aActionBounds.Right() += nHalfLineWidth;
474 aActionBounds.Bottom() += nHalfLineWidth;
476 break;
479 case MetaActionType::POLYGON:
480 aActionBounds = static_cast<const MetaPolygonAction&>(rAct).GetPolygon().GetBoundRect();
481 break;
483 case MetaActionType::POLYPOLYGON:
484 aActionBounds = static_cast<const MetaPolyPolygonAction&>(rAct).GetPolyPolygon().GetBoundRect();
485 break;
487 case MetaActionType::BMP:
488 aActionBounds = tools::Rectangle( static_cast<const MetaBmpAction&>(rAct).GetPoint(),
489 rOut.PixelToLogic( static_cast<const MetaBmpAction&>(rAct).GetBitmap().GetSizePixel() ) );
490 break;
492 case MetaActionType::BMPSCALE:
493 aActionBounds = tools::Rectangle( static_cast<const MetaBmpScaleAction&>(rAct).GetPoint(),
494 static_cast<const MetaBmpScaleAction&>(rAct).GetSize() );
495 break;
497 case MetaActionType::BMPSCALEPART:
498 aActionBounds = tools::Rectangle( static_cast<const MetaBmpScalePartAction&>(rAct).GetDestPoint(),
499 static_cast<const MetaBmpScalePartAction&>(rAct).GetDestSize() );
500 break;
502 case MetaActionType::BMPEX:
503 aActionBounds = tools::Rectangle( static_cast<const MetaBmpExAction&>(rAct).GetPoint(),
504 rOut.PixelToLogic( static_cast<const MetaBmpExAction&>(rAct).GetBitmapEx().GetSizePixel() ) );
505 break;
507 case MetaActionType::BMPEXSCALE:
508 aActionBounds = tools::Rectangle( static_cast<const MetaBmpExScaleAction&>(rAct).GetPoint(),
509 static_cast<const MetaBmpExScaleAction&>(rAct).GetSize() );
510 break;
512 case MetaActionType::BMPEXSCALEPART:
513 aActionBounds = tools::Rectangle( static_cast<const MetaBmpExScalePartAction&>(rAct).GetDestPoint(),
514 static_cast<const MetaBmpExScalePartAction&>(rAct).GetDestSize() );
515 break;
517 case MetaActionType::MASK:
518 aActionBounds = tools::Rectangle( static_cast<const MetaMaskAction&>(rAct).GetPoint(),
519 rOut.PixelToLogic( static_cast<const MetaMaskAction&>(rAct).GetBitmap().GetSizePixel() ) );
520 break;
522 case MetaActionType::MASKSCALE:
523 aActionBounds = tools::Rectangle( static_cast<const MetaMaskScaleAction&>(rAct).GetPoint(),
524 static_cast<const MetaMaskScaleAction&>(rAct).GetSize() );
525 break;
527 case MetaActionType::MASKSCALEPART:
528 aActionBounds = tools::Rectangle( static_cast<const MetaMaskScalePartAction&>(rAct).GetDestPoint(),
529 static_cast<const MetaMaskScalePartAction&>(rAct).GetDestSize() );
530 break;
532 case MetaActionType::GRADIENT:
533 aActionBounds = static_cast<const MetaGradientAction&>(rAct).GetRect();
534 break;
536 case MetaActionType::GRADIENTEX:
537 aActionBounds = static_cast<const MetaGradientExAction&>(rAct).GetPolyPolygon().GetBoundRect();
538 break;
540 case MetaActionType::HATCH:
541 aActionBounds = static_cast<const MetaHatchAction&>(rAct).GetPolyPolygon().GetBoundRect();
542 break;
544 case MetaActionType::WALLPAPER:
545 aActionBounds = static_cast<const MetaWallpaperAction&>(rAct).GetRect();
546 break;
548 case MetaActionType::Transparent:
549 aActionBounds = static_cast<const MetaTransparentAction&>(rAct).GetPolyPolygon().GetBoundRect();
550 break;
552 case MetaActionType::FLOATTRANSPARENT:
553 aActionBounds = tools::Rectangle( static_cast<const MetaFloatTransparentAction&>(rAct).GetPoint(),
554 static_cast<const MetaFloatTransparentAction&>(rAct).GetSize() );
555 break;
557 case MetaActionType::EPS:
558 aActionBounds = tools::Rectangle( static_cast<const MetaEPSAction&>(rAct).GetPoint(),
559 static_cast<const MetaEPSAction&>(rAct).GetSize() );
560 break;
562 case MetaActionType::TEXT:
564 const MetaTextAction& rTextAct = static_cast<const MetaTextAction&>(rAct);
565 const OUString aString( rTextAct.GetText().copy(rTextAct.GetIndex(), rTextAct.GetLen()) );
567 if (!aString.isEmpty())
569 const Point aPtLog( rTextAct.GetPoint() );
571 // #105987# Use API method instead of Impl* methods
572 // #107490# Set base parameter equal to index parameter
573 rOut.GetTextBoundRect( aActionBounds, rTextAct.GetText(), rTextAct.GetIndex(),
574 rTextAct.GetIndex(), rTextAct.GetLen() );
575 aActionBounds.Move( aPtLog.X(), aPtLog.Y() );
578 break;
580 case MetaActionType::TEXTARRAY:
582 const MetaTextArrayAction& rTextAct = static_cast<const MetaTextArrayAction&>(rAct);
583 const OUString aString( rTextAct.GetText().copy(rTextAct.GetIndex(), rTextAct.GetLen()) );
585 if( !aString.isEmpty() )
587 // #105987# ImplLayout takes everything in logical coordinates
588 SalLayout* pSalLayout = rOut.ImplLayout( rTextAct.GetText(), rTextAct.GetIndex(),
589 rTextAct.GetLen(), rTextAct.GetPoint(),
590 0, rTextAct.GetDXArray() );
591 if( pSalLayout )
593 tools::Rectangle aBoundRect( const_cast<OutputDevice&>(rOut).ImplGetTextBoundRect( *pSalLayout ) );
594 aActionBounds = rOut.PixelToLogic( aBoundRect );
595 pSalLayout->Release();
599 break;
601 case MetaActionType::TEXTRECT:
602 aActionBounds = static_cast<const MetaTextRectAction&>(rAct).GetRect();
603 break;
605 case MetaActionType::STRETCHTEXT:
607 const MetaStretchTextAction& rTextAct = static_cast<const MetaStretchTextAction&>(rAct);
608 const OUString aString( rTextAct.GetText().copy(rTextAct.GetIndex(), rTextAct.GetLen()) );
610 // #i16195# Literate copy from TextArray action, the
611 // semantics for the ImplLayout call are copied from the
612 // OutDev::DrawStretchText() code. Unfortunately, also in
613 // this case, public outdev methods such as GetTextWidth()
614 // don't provide enough info.
615 if( !aString.isEmpty() )
617 // #105987# ImplLayout takes everything in logical coordinates
618 SalLayout* pSalLayout = rOut.ImplLayout( rTextAct.GetText(), rTextAct.GetIndex(),
619 rTextAct.GetLen(), rTextAct.GetPoint(),
620 rTextAct.GetWidth() );
621 if( pSalLayout )
623 tools::Rectangle aBoundRect( const_cast<OutputDevice&>(rOut).ImplGetTextBoundRect( *pSalLayout ) );
624 aActionBounds = rOut.PixelToLogic( aBoundRect );
625 pSalLayout->Release();
629 break;
631 case MetaActionType::TEXTLINE:
632 OSL_FAIL("MetaActionType::TEXTLINE not supported");
633 break;
635 default:
636 break;
639 if( !aActionBounds.IsEmpty() )
641 // fdo#40421 limit current action's output to clipped area
642 if( rOut.IsClipRegion() )
643 return rOut.LogicToPixel(
644 rOut.GetClipRegion().GetBoundRect().Intersection( aActionBounds ) );
645 else
646 return rOut.LogicToPixel( aActionBounds );
648 else
649 return tools::Rectangle();
652 } // end anon namespace
654 bool OutputDevice::RemoveTransparenciesFromMetaFile( const GDIMetaFile& rInMtf, GDIMetaFile& rOutMtf,
655 long nMaxBmpDPIX, long nMaxBmpDPIY,
656 bool bReduceTransparency, bool bTransparencyAutoMode,
657 bool bDownsampleBitmaps,
658 const Color& rBackground
661 MetaAction* pCurrAct;
662 bool bTransparent( false );
664 rOutMtf.Clear();
666 if( ! bReduceTransparency || bTransparencyAutoMode )
668 // watch for transparent drawing actions
669 for( pCurrAct = const_cast<GDIMetaFile&>(rInMtf).FirstAction();
670 pCurrAct && !bTransparent;
671 pCurrAct = const_cast<GDIMetaFile&>(rInMtf).NextAction() )
673 // #i10613# determine if the action is transparency capable
675 // #107169# Also examine metafiles with masked bitmaps in
676 // detail. Further down, this is optimized in such a way
677 // that there's no unnecessary painting of masked bitmaps
678 // (which are _always_ subdivided into rectangular regions
679 // of uniform opacity): if a masked bitmap is printed over
680 // empty background, we convert to a plain bitmap with
681 // white background.
682 if( IsTransparentAction( *pCurrAct ) )
684 bTransparent = true;
689 // #i10613# Determine set of connected components containing transparent objects. These are
690 // then processed as bitmaps, the original actions are removed from the metafile.
691 if( !bTransparent )
693 // nothing transparent -> just copy
694 rOutMtf = rInMtf;
696 else
698 // #i10613#
699 // This works as follows: we want a number of distinct sets of
700 // connected components, where each set contains metafile
701 // actions that are intersecting (note: there are possibly
702 // more actions contained as are directly intersecting,
703 // because we can only produce rectangular bitmaps later
704 // on. Thus, each set of connected components is the smallest
705 // enclosing, axis-aligned rectangle that completely bounds a
706 // number of intersecting metafile actions, plus any action
707 // that would otherwise be cut in two). Therefore, we
708 // iteratively add metafile actions from the original metafile
709 // to this connected components list (aCCList), by checking
710 // each element's bounding box against intersection with the
711 // metaaction at hand.
712 // All those intersecting elements are removed from aCCList
713 // and collected in a temporary list (aCCMergeList). After all
714 // elements have been checked, the aCCMergeList elements are
715 // merged with the metaaction at hand into one resulting
716 // connected component, with one big bounding box, and
717 // inserted into aCCList again.
718 // The time complexity of this algorithm is O(n^3), where n is
719 // the number of metafile actions, and it finds all distinct
720 // regions of rectangle-bounded connected components. This
721 // algorithm was designed by AF.
723 // STAGE 1: Detect background
725 // Receives uniform background content, and is _not_ merged
726 // nor checked for intersection against other aCCList elements
727 ConnectedComponents aBackgroundComponent;
729 // create an OutputDevice to record mapmode changes and the like
730 ScopedVclPtrInstance< VirtualDevice > aMapModeVDev;
731 aMapModeVDev->mnDPIX = mnDPIX;
732 aMapModeVDev->mnDPIY = mnDPIY;
733 aMapModeVDev->EnableOutput(false);
735 int nLastBgAction, nActionNum;
737 // weed out page-filling background objects (if they are
738 // uniformly coloured). Keeping them outside the other
739 // connected components often prevents whole-page bitmap
740 // generation.
741 bool bStillBackground=true; // true until first non-bg action
742 nActionNum=0; nLastBgAction=-1;
743 pCurrAct=const_cast<GDIMetaFile&>(rInMtf).FirstAction();
744 if( rBackground != Color( COL_TRANSPARENT ) )
746 aBackgroundComponent.aBgColor = rBackground;
747 if( meOutDevType == OUTDEV_PRINTER )
749 Printer* pThis = dynamic_cast<Printer*>(this);
750 Point aPageOffset = pThis->GetPageOffsetPixel();
751 aPageOffset = Point( 0, 0 ) - aPageOffset;
752 Size aSize = pThis->GetPaperSizePixel();
753 aBackgroundComponent.aBounds = tools::Rectangle( aPageOffset, aSize );
755 else
756 aBackgroundComponent.aBounds = tools::Rectangle( Point( 0, 0 ), GetOutputSizePixel() );
758 while( pCurrAct && bStillBackground )
760 switch( pCurrAct->GetType() )
762 case MetaActionType::RECT:
764 if( !checkRect(
765 aBackgroundComponent.aBounds,
766 aBackgroundComponent.aBgColor,
767 static_cast<const MetaRectAction*>(pCurrAct)->GetRect(),
768 *aMapModeVDev.get()) )
769 bStillBackground=false; // incomplete occlusion of background
770 else
771 nLastBgAction=nActionNum; // this _is_ background
772 break;
774 case MetaActionType::POLYGON:
776 const tools::Polygon aPoly(
777 static_cast<const MetaPolygonAction*>(pCurrAct)->GetPolygon());
778 if( !basegfx::tools::isRectangle(
779 aPoly.getB2DPolygon()) ||
780 !checkRect(
781 aBackgroundComponent.aBounds,
782 aBackgroundComponent.aBgColor,
783 aPoly.GetBoundRect(),
784 *aMapModeVDev.get()) )
785 bStillBackground=false; // incomplete occlusion of background
786 else
787 nLastBgAction=nActionNum; // this _is_ background
788 break;
790 case MetaActionType::POLYPOLYGON:
792 const tools::PolyPolygon aPoly(
793 static_cast<const MetaPolyPolygonAction*>(pCurrAct)->GetPolyPolygon());
794 if( aPoly.Count() != 1 ||
795 !basegfx::tools::isRectangle(
796 aPoly[0].getB2DPolygon()) ||
797 !checkRect(
798 aBackgroundComponent.aBounds,
799 aBackgroundComponent.aBgColor,
800 aPoly.GetBoundRect(),
801 *aMapModeVDev.get()) )
802 bStillBackground=false; // incomplete occlusion of background
803 else
804 nLastBgAction=nActionNum; // this _is_ background
805 break;
807 case MetaActionType::WALLPAPER:
809 if( !checkRect(
810 aBackgroundComponent.aBounds,
811 aBackgroundComponent.aBgColor,
812 static_cast<const MetaWallpaperAction*>(pCurrAct)->GetRect(),
813 *aMapModeVDev.get()) )
814 bStillBackground=false; // incomplete occlusion of background
815 else
816 nLastBgAction=nActionNum; // this _is_ background
817 break;
819 default:
821 if( ImplIsNotTransparent( *pCurrAct,
822 *aMapModeVDev.get() ) )
823 bStillBackground=false; // non-transparent action, possibly
824 // not uniform
825 else
826 // extend current bounds (next uniform action
827 // needs to fully cover this area)
828 aBackgroundComponent.aBounds.Union(
829 ImplCalcActionBounds(*pCurrAct, *aMapModeVDev.get()) );
830 break;
834 // execute action to get correct MapModes etc.
835 pCurrAct->Execute( aMapModeVDev.get() );
837 pCurrAct=const_cast<GDIMetaFile&>(rInMtf).NextAction();
838 ++nActionNum;
841 // clean up aMapModeVDev
842 sal_uInt32 nCount = aMapModeVDev->GetGCStackDepth();
843 while( nCount-- )
844 aMapModeVDev->Pop();
846 ConnectedComponentsList aCCList; // list containing distinct sets of connected components as elements.
848 // fast-forward until one after the last background action
849 // (need to reconstruct map mode vdev state)
850 nActionNum=0;
851 pCurrAct=const_cast<GDIMetaFile&>(rInMtf).FirstAction();
852 while( pCurrAct && nActionNum<=nLastBgAction )
854 // up to and including last ink-generating background
855 // action go to background component
856 aBackgroundComponent.aComponentList.push_back(
857 ::std::make_pair(
858 pCurrAct, nActionNum) );
860 // execute action to get correct MapModes etc.
861 pCurrAct->Execute( aMapModeVDev.get() );
862 pCurrAct=const_cast<GDIMetaFile&>(rInMtf).NextAction();
863 ++nActionNum;
866 // STAGE 2: Generate connected components list
868 // iterate over all actions (start where background action
869 // search left off)
870 for( ;
871 pCurrAct;
872 pCurrAct=const_cast<GDIMetaFile&>(rInMtf).NextAction(), ++nActionNum )
874 // execute action to get correct MapModes etc.
875 pCurrAct->Execute( aMapModeVDev.get() );
877 // cache bounds of current action
878 const tools::Rectangle aBBCurrAct( ImplCalcActionBounds(*pCurrAct, *aMapModeVDev.get()) );
880 // accumulate collected bounds here, initialize with current action
881 tools::Rectangle aTotalBounds( aBBCurrAct ); // thus,
882 // aTotalComponents.aBounds
883 // is
884 // empty
885 // for
886 // non-output-generating
887 // actions
888 bool bTreatSpecial( false );
889 ConnectedComponents aTotalComponents;
891 // STAGE 2.1: Search for intersecting cc entries
893 // if aBBCurrAct is empty, it will intersect with no
894 // aCCList member. Thus, we can save the check.
895 // Furthermore, this ensures that non-output-generating
896 // actions get their own aCCList entry, which is necessary
897 // when copying them to the output metafile (see stage 4
898 // below).
900 // #107169# Wholly transparent objects need
901 // not be considered for connected components,
902 // too. Just put each of them into a separate
903 // component.
904 aTotalComponents.bIsFullyTransparent = !ImplIsNotTransparent(*pCurrAct, *aMapModeVDev.get());
906 if( !aBBCurrAct.IsEmpty() &&
907 !aTotalComponents.bIsFullyTransparent )
909 if( !aBackgroundComponent.aComponentList.empty() &&
910 !aBackgroundComponent.aBounds.IsInside(aTotalBounds) )
912 // it seems the background is not large enough. to
913 // be on the safe side, combine with this component.
914 aTotalBounds.Union( aBackgroundComponent.aBounds );
916 // extract all aCurr actions to aTotalComponents
917 aTotalComponents.aComponentList.splice( aTotalComponents.aComponentList.end(),
918 aBackgroundComponent.aComponentList );
920 if( aBackgroundComponent.bIsSpecial )
921 bTreatSpecial = true;
924 ConnectedComponentsList::iterator aCurrCC;
925 const ConnectedComponentsList::iterator aLastCC( aCCList.end() );
926 bool bSomeComponentsChanged;
928 // now, this is unfortunate: since changing anyone of
929 // the aCCList elements (e.g. by merging or addition
930 // of an action) might generate new intersection with
931 // other aCCList elements, have to repeat the whole
932 // element scanning, until nothing changes anymore.
933 // Thus, this loop here makes us O(n^3) in the worst
934 // case.
937 // only loop here if 'intersects' branch below was hit
938 bSomeComponentsChanged = false;
940 // iterate over all current members of aCCList
941 for( aCurrCC=aCCList.begin(); aCurrCC != aLastCC; )
943 // first check if current element's bounds are
944 // empty. This ensures that empty actions are not
945 // merged into one component, as a matter of fact,
946 // they have no position.
948 // #107169# Wholly transparent objects need
949 // not be considered for connected components,
950 // too. Just put each of them into a separate
951 // component.
952 if( !aCurrCC->aBounds.IsEmpty() &&
953 !aCurrCC->bIsFullyTransparent &&
954 aCurrCC->aBounds.IsOver( aTotalBounds ) )
956 // union the intersecting aCCList element into aTotalComponents
958 // calc union bounding box
959 aTotalBounds.Union( aCurrCC->aBounds );
961 // extract all aCurr actions to aTotalComponents
962 aTotalComponents.aComponentList.splice( aTotalComponents.aComponentList.end(),
963 aCurrCC->aComponentList );
965 if( aCurrCC->bIsSpecial )
966 bTreatSpecial = true;
968 // remove and delete aCurrCC element from list (we've now merged its content)
969 aCurrCC = aCCList.erase( aCurrCC );
971 // at least one component changed, need to rescan everything
972 bSomeComponentsChanged = true;
974 else
976 ++aCurrCC;
980 while( bSomeComponentsChanged );
983 // STAGE 2.2: Determine special state for cc element
985 // now test whether the whole connected component must be
986 // treated specially (i.e. rendered as a bitmap): if the
987 // added action is the very first action, or all actions
988 // before it are completely transparent, the connected
989 // component need not be treated specially, not even if
990 // the added action contains transparency. This is because
991 // painting of transparent objects on _white background_
992 // works without alpha compositing (you just calculate the
993 // color). Note that for the test "all objects before me
994 // are transparent" no sorting is necessary, since the
995 // added metaaction pCurrAct is always in the order the
996 // metafile is painted. Generally, the order of the
997 // metaactions in the ConnectedComponents are not
998 // guaranteed to be the same as in the metafile.
999 if( bTreatSpecial )
1001 // prev component(s) special -> this one, too
1002 aTotalComponents.bIsSpecial = true;
1004 else if( !IsTransparentAction( *pCurrAct ) )
1006 // added action and none of prev components special ->
1007 // this one normal, too
1008 aTotalComponents.bIsSpecial = false;
1010 else
1012 // added action is special and none of prev components
1013 // special -> do the detailed tests
1015 // can the action handle transparency correctly
1016 // (i.e. when painted on white background, does the
1017 // action still look correct)?
1018 if( !DoesActionHandleTransparency( *pCurrAct ) )
1020 // no, action cannot handle its transparency on
1021 // a printer device, render to bitmap
1022 aTotalComponents.bIsSpecial = true;
1024 else
1026 // yes, action can handle its transparency, so
1027 // check whether we're on white background
1028 if( aTotalComponents.aComponentList.empty() )
1030 // nothing between pCurrAct and page
1031 // background -> don't be special
1032 aTotalComponents.bIsSpecial = false;
1034 else
1036 // #107169# Fixes above now ensure that _no_
1037 // object in the list is fully transparent. Thus,
1038 // if the component list is not empty above, we
1039 // must assume that we have to treat this
1040 // component special.
1042 // there are non-transparent objects between
1043 // pCurrAct and the empty sheet of paper -> be
1044 // special, then
1045 aTotalComponents.bIsSpecial = true;
1050 // STAGE 2.3: Add newly generated CC list element
1052 // set new bounds and add action to list
1053 aTotalComponents.aBounds = aTotalBounds;
1054 aTotalComponents.aComponentList.push_back(
1055 ::std::make_pair(
1056 pCurrAct, nActionNum) );
1058 // add aTotalComponents as a new entry to aCCList
1059 aCCList.push_back( aTotalComponents );
1061 SAL_WARN_IF( aTotalComponents.aComponentList.empty(), "vcl",
1062 "Printer::GetPreparedMetaFile empty component" );
1063 SAL_WARN_IF( aTotalComponents.aBounds.IsEmpty() && (aTotalComponents.aComponentList.size() != 1), "vcl",
1064 "Printer::GetPreparedMetaFile non-output generating actions must be solitary");
1065 SAL_WARN_IF( aTotalComponents.bIsFullyTransparent && (aTotalComponents.aComponentList.size() != 1), "vcl",
1066 "Printer::GetPreparedMetaFile fully transparent actions must be solitary");
1069 // well now, we've got the list of disjunct connected
1070 // components. Now we've got to create a map, which contains
1071 // the corresponding aCCList element for every
1072 // metaaction. Later on, we always process the complete
1073 // metafile for each bitmap to be generated, but switch on
1074 // output only for actions contained in the then current
1075 // aCCList element. This ensures correct mapmode and attribute
1076 // settings for all cases.
1078 // maps mtf actions to CC list entries
1079 ::std::vector< const ConnectedComponents* > aCCList_MemberMap( rInMtf.GetActionSize() );
1081 // iterate over all aCCList members and their contained metaactions
1082 ConnectedComponentsList::iterator aCurr( aCCList.begin() );
1083 const ConnectedComponentsList::iterator aLast( aCCList.end() );
1084 for( ; aCurr != aLast; ++aCurr )
1086 ComponentList::iterator aCurrentAction( aCurr->aComponentList.begin() );
1087 const ComponentList::iterator aLastAction( aCurr->aComponentList.end() );
1088 for( ; aCurrentAction != aLastAction; ++aCurrentAction )
1090 // set pointer to aCCList element for corresponding index
1091 aCCList_MemberMap[ aCurrentAction->second ] = &(*aCurr);
1095 // STAGE 3.1: Output background mtf actions (if there are any)
1097 ComponentList::iterator aCurrAct( aBackgroundComponent.aComponentList.begin() );
1098 const ComponentList::iterator aLastAct( aBackgroundComponent.aComponentList.end() );
1099 for( ; aCurrAct != aLastAct; ++aCurrAct )
1101 // simply add this action (above, we inserted the actions
1102 // starting at index 0 up to and including nLastBgAction)
1103 rOutMtf.AddAction( ( aCurrAct->first->Duplicate(), aCurrAct->first ) );
1106 // STAGE 3.2: Generate banded bitmaps for special regions
1108 Point aPageOffset;
1109 Size aTmpSize( GetOutputSizePixel() );
1110 if( mpPDFWriter )
1112 aTmpSize = mpPDFWriter->getCurPageSize();
1113 aTmpSize = LogicToPixel( aTmpSize, MapMode( MapUnit::MapPoint ) );
1115 // also add error code to PDFWriter
1116 mpPDFWriter->insertError( vcl::PDFWriter::Warning_Transparency_Converted );
1118 else if( meOutDevType == OUTDEV_PRINTER )
1120 Printer* pThis = dynamic_cast<Printer*>(this);
1121 aPageOffset = pThis->GetPageOffsetPixel();
1122 aPageOffset = Point( 0, 0 ) - aPageOffset;
1123 aTmpSize = pThis->GetPaperSizePixel();
1125 const tools::Rectangle aOutputRect( aPageOffset, aTmpSize );
1126 bool bTiling = dynamic_cast<Printer*>(this) != nullptr;
1128 // iterate over all aCCList members and generate bitmaps for the special ones
1129 for( aCurr = aCCList.begin(); aCurr != aLast; ++aCurr )
1131 if( aCurr->bIsSpecial )
1133 tools::Rectangle aBoundRect( aCurr->aBounds );
1134 aBoundRect.Intersection( aOutputRect );
1136 const double fBmpArea( (double) aBoundRect.GetWidth() * aBoundRect.GetHeight() );
1137 const double fOutArea( (double) aOutputRect.GetWidth() * aOutputRect.GetHeight() );
1139 // check if output doesn't exceed given size
1140 if( bReduceTransparency && bTransparencyAutoMode && ( fBmpArea > ( 0.25 * fOutArea ) ) )
1142 // output normally. Therefore, we simply clear the
1143 // special attribute, as everything non-special is
1144 // copied to rOutMtf further below.
1145 aCurr->bIsSpecial = false;
1147 else
1149 // create new bitmap action first
1150 if( aBoundRect.GetWidth() && aBoundRect.GetHeight() )
1152 Point aDstPtPix( aBoundRect.TopLeft() );
1153 Size aDstSzPix;
1155 ScopedVclPtrInstance<VirtualDevice> aMapVDev; // here, we record only mapmode information
1156 aMapVDev->EnableOutput(false);
1158 ScopedVclPtrInstance<VirtualDevice> aPaintVDev; // into this one, we render.
1159 aPaintVDev->SetBackground( aBackgroundComponent.aBgColor );
1161 rOutMtf.AddAction( new MetaPushAction( PushFlags::MAPMODE ) );
1162 rOutMtf.AddAction( new MetaMapModeAction() );
1164 aPaintVDev->SetDrawMode( GetDrawMode() );
1166 while( aDstPtPix.Y() <= aBoundRect.Bottom() )
1168 aDstPtPix.X() = aBoundRect.Left();
1169 aDstSzPix = bTiling ? Size( MAX_TILE_WIDTH, MAX_TILE_HEIGHT ) : aBoundRect.GetSize();
1171 if( ( aDstPtPix.Y() + aDstSzPix.Height() - 1 ) > aBoundRect.Bottom() )
1172 aDstSzPix.Height() = aBoundRect.Bottom() - aDstPtPix.Y() + 1;
1174 while( aDstPtPix.X() <= aBoundRect.Right() )
1176 if( ( aDstPtPix.X() + aDstSzPix.Width() - 1 ) > aBoundRect.Right() )
1177 aDstSzPix.Width() = aBoundRect.Right() - aDstPtPix.X() + 1;
1179 if( !tools::Rectangle( aDstPtPix, aDstSzPix ).Intersection( aBoundRect ).IsEmpty() &&
1180 aPaintVDev->SetOutputSizePixel( aDstSzPix ) )
1182 aPaintVDev->Push();
1183 aMapVDev->Push();
1185 aMapVDev->mnDPIX = aPaintVDev->mnDPIX = mnDPIX;
1186 aMapVDev->mnDPIY = aPaintVDev->mnDPIY = mnDPIY;
1188 aPaintVDev->EnableOutput(false);
1190 // iterate over all actions
1191 for( pCurrAct=const_cast<GDIMetaFile&>(rInMtf).FirstAction(), nActionNum=0;
1192 pCurrAct;
1193 pCurrAct=const_cast<GDIMetaFile&>(rInMtf).NextAction(), ++nActionNum )
1195 // enable output only for
1196 // actions that are members of
1197 // the current aCCList element
1198 // (aCurr)
1199 if( aCCList_MemberMap[nActionNum] == &(*aCurr) )
1200 aPaintVDev->EnableOutput();
1202 // but process every action
1203 const MetaActionType nType( pCurrAct->GetType() );
1205 if( MetaActionType::MAPMODE == nType )
1207 pCurrAct->Execute( aMapVDev.get() );
1209 MapMode aMtfMap( aMapVDev->GetMapMode() );
1210 const Point aNewOrg( aMapVDev->PixelToLogic( aDstPtPix ) );
1212 aMtfMap.SetOrigin( Point( -aNewOrg.X(), -aNewOrg.Y() ) );
1213 aPaintVDev->SetMapMode( aMtfMap );
1215 else if( ( MetaActionType::PUSH == nType ) || ( MetaActionType::POP ) == nType )
1217 pCurrAct->Execute( aMapVDev.get() );
1218 pCurrAct->Execute( aPaintVDev.get() );
1220 else if( MetaActionType::GRADIENT == nType )
1222 MetaGradientAction* pGradientAction = static_cast<MetaGradientAction*>(pCurrAct);
1223 Printer* pPrinter = dynamic_cast< Printer* >(this);
1224 if( pPrinter )
1225 pPrinter->DrawGradientEx( aPaintVDev.get(), pGradientAction->GetRect(), pGradientAction->GetGradient() );
1226 else
1227 DrawGradient( pGradientAction->GetRect(), pGradientAction->GetGradient() );
1229 else
1231 pCurrAct->Execute( aPaintVDev.get() );
1234 if( !( nActionNum % 8 ) )
1235 Application::Reschedule();
1238 const bool bOldMap = mbMap;
1239 mbMap = aPaintVDev->mbMap = false;
1241 Bitmap aBandBmp( aPaintVDev->GetBitmap( Point(), aDstSzPix ) );
1243 // scale down bitmap, if requested
1244 if( bDownsampleBitmaps )
1246 aBandBmp = GetDownsampledBitmap( aDstSzPix,
1247 Point(), aBandBmp.GetSizePixel(),
1248 aBandBmp, nMaxBmpDPIX, nMaxBmpDPIY );
1251 rOutMtf.AddAction( new MetaCommentAction( "PRNSPOOL_TRANSPARENTBITMAP_BEGIN" ) );
1252 rOutMtf.AddAction( new MetaBmpScaleAction( aDstPtPix, aDstSzPix, aBandBmp ) );
1253 rOutMtf.AddAction( new MetaCommentAction( "PRNSPOOL_TRANSPARENTBITMAP_END" ) );
1255 aPaintVDev->mbMap = true;
1256 mbMap = bOldMap;
1257 aMapVDev->Pop();
1258 aPaintVDev->Pop();
1261 // overlapping bands to avoid missing lines (e.g. PostScript)
1262 aDstPtPix.X() += aDstSzPix.Width();
1265 // overlapping bands to avoid missing lines (e.g. PostScript)
1266 aDstPtPix.Y() += aDstSzPix.Height();
1269 rOutMtf.AddAction( new MetaPopAction() );
1275 // clean up aMapModeVDev
1276 nCount = aMapModeVDev->GetGCStackDepth();
1277 while( nCount-- )
1278 aMapModeVDev->Pop();
1280 // STAGE 4: Copy actions to output metafile
1282 // iterate over all actions and duplicate the ones not in a
1283 // special aCCList member into rOutMtf
1284 for( pCurrAct=const_cast<GDIMetaFile&>(rInMtf).FirstAction(), nActionNum=0;
1285 pCurrAct;
1286 pCurrAct=const_cast<GDIMetaFile&>(rInMtf).NextAction(), ++nActionNum )
1288 const ConnectedComponents* pCurrAssociatedComponent = aCCList_MemberMap[nActionNum];
1290 // NOTE: This relies on the fact that map-mode or draw
1291 // mode changing actions are solitary aCCList elements and
1292 // have empty bounding boxes, see comment on stage 2.1
1293 // above
1294 if( pCurrAssociatedComponent &&
1295 (pCurrAssociatedComponent->aBounds.IsEmpty() ||
1296 !pCurrAssociatedComponent->bIsSpecial) )
1298 // #107169# Treat transparent bitmaps special, if they
1299 // are the first (or sole) action in their bounds
1300 // list. Note that we previously ensured that no
1301 // fully-transparent objects are before us here.
1302 if( DoesActionHandleTransparency( *pCurrAct ) &&
1303 pCurrAssociatedComponent->aComponentList.begin()->first == pCurrAct )
1305 // convert actions, where masked-out parts are of
1306 // given background color
1307 ImplConvertTransparentAction(rOutMtf,
1308 *pCurrAct,
1309 *aMapModeVDev.get(),
1310 aBackgroundComponent.aBgColor);
1312 else
1314 // simply add this action
1315 rOutMtf.AddAction( ( pCurrAct->Duplicate(), pCurrAct ) );
1318 pCurrAct->Execute(aMapModeVDev.get());
1322 rOutMtf.SetPrefMapMode( rInMtf.GetPrefMapMode() );
1323 rOutMtf.SetPrefSize( rInMtf.GetPrefSize() );
1325 #if OSL_DEBUG_LEVEL > 1
1326 // iterate over all aCCList members and generate rectangles for the bounding boxes
1327 rOutMtf.AddAction( new MetaFillColorAction( COL_WHITE, false ) );
1328 for( aCurr = aCCList.begin(); aCurr != aLast; ++aCurr )
1330 if( aCurr->bIsSpecial )
1331 rOutMtf.AddAction( new MetaLineColorAction( COL_RED, true) );
1332 else
1333 rOutMtf.AddAction( new MetaLineColorAction( COL_BLUE, true) );
1335 rOutMtf.AddAction( new MetaRectAction( aMapModeVDev->PixelToLogic( aCurr->aBounds ) ) );
1337 #endif
1339 return bTransparent;
1342 void Printer::DrawGradientEx( OutputDevice* pOut, const tools::Rectangle& rRect, const Gradient& rGradient )
1344 const PrinterOptions& rPrinterOptions = GetPrinterOptions();
1346 if( rPrinterOptions.IsReduceGradients() )
1348 if( PrinterGradientMode::Stripes == rPrinterOptions.GetReducedGradientMode() )
1350 if( !rGradient.GetSteps() || ( rGradient.GetSteps() > rPrinterOptions.GetReducedGradientStepCount() ) )
1352 Gradient aNewGradient( rGradient );
1354 aNewGradient.SetSteps( rPrinterOptions.GetReducedGradientStepCount() );
1355 pOut->DrawGradient( rRect, aNewGradient );
1357 else
1358 pOut->DrawGradient( rRect, rGradient );
1360 else
1362 const Color& rStartColor = rGradient.GetStartColor();
1363 const Color& rEndColor = rGradient.GetEndColor();
1364 const long nR = ( ( (long) rStartColor.GetRed() * rGradient.GetStartIntensity() ) / 100 +
1365 ( (long) rEndColor.GetRed() * rGradient.GetEndIntensity() ) / 100 ) >> 1;
1366 const long nG = ( ( (long) rStartColor.GetGreen() * rGradient.GetStartIntensity() ) / 100 +
1367 ( (long) rEndColor.GetGreen() * rGradient.GetEndIntensity() ) / 100 ) >> 1;
1368 const long nB = ( ( (long) rStartColor.GetBlue() * rGradient.GetStartIntensity() ) / 100 +
1369 ( (long) rEndColor.GetBlue() * rGradient.GetEndIntensity() ) / 100 ) >> 1;
1370 const Color aColor( (sal_uInt8) nR, (sal_uInt8) nG, (sal_uInt8) nB );
1372 pOut->Push( PushFlags::LINECOLOR | PushFlags::FILLCOLOR );
1373 pOut->SetLineColor( aColor );
1374 pOut->SetFillColor( aColor );
1375 pOut->DrawRect( rRect );
1376 pOut->Pop();
1379 else
1380 pOut->DrawGradient( rRect, rGradient );
1383 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */