Version 5.2.6.1, tag libreoffice-5.2.6.1
[LibreOffice.git] / vcl / source / gdi / print2.cxx
blobd7056bc897e8a931c7b92d1d14330b5507ac56ab
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>
29 #include <tools/debug.hxx>
31 #include <vcl/virdev.hxx>
32 #include <vcl/metaact.hxx>
33 #include <vcl/gdimtf.hxx>
34 #include <vcl/print.hxx>
35 #include <vcl/svapp.hxx>
36 #include <vcl/bitmapaccess.hxx>
38 #include <print.h>
40 #include "pdfwriter_impl.hxx"
42 #define MAX_TILE_WIDTH 1024
43 #define MAX_TILE_HEIGHT 1024
45 typedef ::std::pair< MetaAction*, int > Component; // MetaAction plus index in metafile
47 typedef ::std::list< Component > ComponentList;
49 // List of (intersecting) actions, plus overall bounds
50 struct ConnectedComponents
52 ConnectedComponents() :
53 aComponentList(),
54 aBounds(),
55 aBgColor(COL_WHITE),
56 bIsSpecial(false),
57 bIsFullyTransparent(false)
60 ComponentList aComponentList;
61 Rectangle aBounds;
62 Color aBgColor;
63 bool bIsSpecial;
64 bool bIsFullyTransparent;
67 typedef ::std::list< ConnectedComponents > ConnectedComponentsList;
69 namespace {
71 /** \#i10613# Extracted from Printer::GetPreparedMetaFile. Returns true
72 if given action requires special transparency handling
74 bool IsTransparentAction( const MetaAction& rAct )
76 switch( rAct.GetType() )
78 case MetaActionType::Transparent:
79 return true;
81 case MetaActionType::FLOATTRANSPARENT:
82 return true;
84 case MetaActionType::BMPEX:
85 return static_cast<const MetaBmpExAction&>(rAct).GetBitmapEx().IsTransparent();
87 case MetaActionType::BMPEXSCALE:
88 return static_cast<const MetaBmpExScaleAction&>(rAct).GetBitmapEx().IsTransparent();
90 case MetaActionType::BMPEXSCALEPART:
91 return static_cast<const MetaBmpExScalePartAction&>(rAct).GetBitmapEx().IsTransparent();
93 default:
94 return false;
99 /** Determines whether the action can handle transparency correctly
100 (i.e. when painted on white background, does the action still look
101 correct)?
103 bool DoesActionHandleTransparency( const MetaAction& rAct )
105 // MetaActionType::FLOATTRANSPARENT can contain a whole metafile,
106 // which is to be rendered with the given transparent gradient. We
107 // currently cannot emulate transparent painting on a white
108 // background reliably.
110 // the remainder can handle printing itself correctly on a uniform
111 // white background.
112 switch( rAct.GetType() )
114 case MetaActionType::Transparent:
115 case MetaActionType::BMPEX:
116 case MetaActionType::BMPEXSCALE:
117 case MetaActionType::BMPEXSCALEPART:
118 return true;
120 default:
121 return false;
125 /** Check whether rCurrRect rectangle fully covers io_rPrevRect - if
126 yes, return true and update o_rBgColor
128 bool checkRect( Rectangle& io_rPrevRect,
129 Color& o_rBgColor,
130 const Rectangle& rCurrRect,
131 OutputDevice& rMapModeVDev )
133 // shape needs to fully cover previous content, and have uniform
134 // color
135 const bool bRet(
136 rMapModeVDev.LogicToPixel(rCurrRect).IsInside(io_rPrevRect) &&
137 rMapModeVDev.IsFillColor() );
139 if( bRet )
141 io_rPrevRect = rCurrRect;
142 o_rBgColor = rMapModeVDev.GetFillColor();
145 return bRet;
148 /** #107169# Convert BitmapEx to Bitmap with appropriately blended
149 color. Convert MetaTransparentAction to plain polygon,
150 appropriately colored
152 @param o_rMtf
153 Add converted actions to this metafile
155 void ImplConvertTransparentAction( GDIMetaFile& o_rMtf,
156 const MetaAction& rAct,
157 const OutputDevice& rStateOutDev,
158 Color aBgColor )
160 if( rAct.GetType() == MetaActionType::Transparent )
162 const MetaTransparentAction* pTransAct = static_cast<const MetaTransparentAction*>(&rAct);
163 sal_uInt16 nTransparency( pTransAct->GetTransparence() );
165 // #i10613# Respect transparency for draw color
166 if( nTransparency )
168 o_rMtf.AddAction( new MetaPushAction( PushFlags::LINECOLOR|PushFlags::FILLCOLOR ) );
170 // assume white background for alpha blending
171 Color aLineColor( rStateOutDev.GetLineColor() );
172 aLineColor.SetRed( static_cast<sal_uInt8>( (255L*nTransparency + (100L - nTransparency)*aLineColor.GetRed()) / 100L ) );
173 aLineColor.SetGreen( static_cast<sal_uInt8>( (255L*nTransparency + (100L - nTransparency)*aLineColor.GetGreen()) / 100L ) );
174 aLineColor.SetBlue( static_cast<sal_uInt8>( (255L*nTransparency + (100L - nTransparency)*aLineColor.GetBlue()) / 100L ) );
175 o_rMtf.AddAction( new MetaLineColorAction(aLineColor, true) );
177 Color aFillColor( rStateOutDev.GetFillColor() );
178 aFillColor.SetRed( static_cast<sal_uInt8>( (255L*nTransparency + (100L - nTransparency)*aFillColor.GetRed()) / 100L ) );
179 aFillColor.SetGreen( static_cast<sal_uInt8>( (255L*nTransparency + (100L - nTransparency)*aFillColor.GetGreen()) / 100L ) );
180 aFillColor.SetBlue( static_cast<sal_uInt8>( (255L*nTransparency + (100L - nTransparency)*aFillColor.GetBlue()) / 100L ) );
181 o_rMtf.AddAction( new MetaFillColorAction(aFillColor, true) );
184 o_rMtf.AddAction( new MetaPolyPolygonAction(pTransAct->GetPolyPolygon()) );
186 if( nTransparency )
187 o_rMtf.AddAction( new MetaPopAction() );
189 else
191 BitmapEx aBmpEx;
193 switch( rAct.GetType() )
195 case MetaActionType::BMPEX:
196 aBmpEx = static_cast<const MetaBmpExAction&>(rAct).GetBitmapEx();
197 break;
199 case MetaActionType::BMPEXSCALE:
200 aBmpEx = static_cast<const MetaBmpExScaleAction&>(rAct).GetBitmapEx();
201 break;
203 case MetaActionType::BMPEXSCALEPART:
204 aBmpEx = static_cast<const MetaBmpExScaleAction&>(rAct).GetBitmapEx();
205 break;
207 case MetaActionType::Transparent:
209 default:
210 OSL_FAIL("Printer::GetPreparedMetafile impossible state reached");
211 break;
214 Bitmap aBmp( aBmpEx.GetBitmap() );
215 if( !aBmpEx.IsAlpha() )
217 // blend with mask
218 BitmapReadAccess* pRA = aBmp.AcquireReadAccess();
220 if( !pRA )
221 return; // what else should I do?
223 Color aActualColor( aBgColor );
225 if( pRA->HasPalette() )
226 aActualColor = pRA->GetBestPaletteColor( aBgColor ).operator Color();
228 Bitmap::ReleaseAccess(pRA);
230 // did we get true white?
231 if( aActualColor.GetColorError( aBgColor ) )
233 // no, create truecolor bitmap, then
234 aBmp.Convert( BMP_CONVERSION_24BIT );
236 // fill masked out areas white
237 aBmp.Replace( aBmpEx.GetMask(), aBgColor );
239 else
241 // fill masked out areas white
242 aBmp.Replace( aBmpEx.GetMask(), aActualColor );
245 else
247 // blend with alpha channel
248 aBmp.Convert( BMP_CONVERSION_24BIT );
249 aBmp.Blend(aBmpEx.GetAlpha(),aBgColor);
252 // add corresponding action
253 switch( rAct.GetType() )
255 case MetaActionType::BMPEX:
256 o_rMtf.AddAction( new MetaBmpAction(
257 static_cast<const MetaBmpExAction&>(rAct).GetPoint(),
258 aBmp ));
259 break;
260 case MetaActionType::BMPEXSCALE:
261 o_rMtf.AddAction( new MetaBmpScaleAction(
262 static_cast<const MetaBmpExScaleAction&>(rAct).GetPoint(),
263 static_cast<const MetaBmpExScaleAction&>(rAct).GetSize(),
264 aBmp ));
265 break;
266 case MetaActionType::BMPEXSCALEPART:
267 o_rMtf.AddAction( new MetaBmpScalePartAction(
268 static_cast<const MetaBmpExScalePartAction&>(rAct).GetDestPoint(),
269 static_cast<const MetaBmpExScalePartAction&>(rAct).GetDestSize(),
270 static_cast<const MetaBmpExScalePartAction&>(rAct).GetSrcPoint(),
271 static_cast<const MetaBmpExScalePartAction&>(rAct).GetSrcSize(),
272 aBmp ));
273 break;
274 default:
275 OSL_FAIL("Unexpected case");
276 break;
281 // #i10613# Extracted from ImplCheckRect::ImplCreate
282 // Returns true, if given action creates visible (i.e. non-transparent) output
283 bool ImplIsNotTransparent( const MetaAction& rAct, const OutputDevice& rOut )
285 const bool bLineTransparency( !rOut.IsLineColor() || rOut.GetLineColor().GetTransparency() == 255 );
286 const bool bFillTransparency( !rOut.IsFillColor() || rOut.GetFillColor().GetTransparency() == 255 );
287 bool bRet( false );
289 switch( rAct.GetType() )
291 case MetaActionType::POINT:
292 if( !bLineTransparency )
293 bRet = true;
294 break;
296 case MetaActionType::LINE:
297 if( !bLineTransparency )
298 bRet = true;
299 break;
301 case MetaActionType::RECT:
302 if( !bLineTransparency || !bFillTransparency )
303 bRet = true;
304 break;
306 case MetaActionType::ROUNDRECT:
307 if( !bLineTransparency || !bFillTransparency )
308 bRet = true;
309 break;
311 case MetaActionType::ELLIPSE:
312 if( !bLineTransparency || !bFillTransparency )
313 bRet = true;
314 break;
316 case MetaActionType::ARC:
317 if( !bLineTransparency || !bFillTransparency )
318 bRet = true;
319 break;
321 case MetaActionType::PIE:
322 if( !bLineTransparency || !bFillTransparency )
323 bRet = true;
324 break;
326 case MetaActionType::CHORD:
327 if( !bLineTransparency || !bFillTransparency )
328 bRet = true;
329 break;
331 case MetaActionType::POLYLINE:
332 if( !bLineTransparency )
333 bRet = true;
334 break;
336 case MetaActionType::POLYGON:
337 if( !bLineTransparency || !bFillTransparency )
338 bRet = true;
339 break;
341 case MetaActionType::POLYPOLYGON:
342 if( !bLineTransparency || !bFillTransparency )
343 bRet = true;
344 break;
346 case MetaActionType::TEXT:
348 const MetaTextAction& rTextAct = static_cast<const MetaTextAction&>(rAct);
349 const OUString aString( rTextAct.GetText().copy(rTextAct.GetIndex(), rTextAct.GetLen()) );
350 if (!aString.isEmpty())
351 bRet = true;
353 break;
355 case MetaActionType::TEXTARRAY:
357 const MetaTextArrayAction& rTextAct = static_cast<const MetaTextArrayAction&>(rAct);
358 const OUString aString( rTextAct.GetText().copy(rTextAct.GetIndex(), rTextAct.GetLen()) );
359 if (!aString.isEmpty())
360 bRet = true;
362 break;
364 case MetaActionType::PIXEL:
365 case MetaActionType::BMP:
366 case MetaActionType::BMPSCALE:
367 case MetaActionType::BMPSCALEPART:
368 case MetaActionType::BMPEX:
369 case MetaActionType::BMPEXSCALE:
370 case MetaActionType::BMPEXSCALEPART:
371 case MetaActionType::MASK:
372 case MetaActionType::MASKSCALE:
373 case MetaActionType::MASKSCALEPART:
374 case MetaActionType::GRADIENT:
375 case MetaActionType::GRADIENTEX:
376 case MetaActionType::HATCH:
377 case MetaActionType::WALLPAPER:
378 case MetaActionType::Transparent:
379 case MetaActionType::FLOATTRANSPARENT:
380 case MetaActionType::EPS:
381 case MetaActionType::TEXTRECT:
382 case MetaActionType::STRETCHTEXT:
383 case MetaActionType::TEXTLINE:
384 // all other actions: generate non-transparent output
385 bRet = true;
386 break;
388 default:
389 break;
392 return bRet;
395 // #i10613# Extracted from ImplCheckRect::ImplCreate
396 Rectangle ImplCalcActionBounds( const MetaAction& rAct, const OutputDevice& rOut )
398 Rectangle aActionBounds;
400 switch( rAct.GetType() )
402 case MetaActionType::PIXEL:
403 aActionBounds = Rectangle( static_cast<const MetaPixelAction&>(rAct).GetPoint(), Size( 1, 1 ) );
404 break;
406 case MetaActionType::POINT:
407 aActionBounds = Rectangle( static_cast<const MetaPointAction&>(rAct).GetPoint(), Size( 1, 1 ) );
408 break;
410 case MetaActionType::LINE:
412 const MetaLineAction& rMetaLineAction = static_cast<const MetaLineAction&>(rAct);
413 aActionBounds = Rectangle( rMetaLineAction.GetStartPoint(), rMetaLineAction.GetEndPoint() );
414 aActionBounds.Justify();
415 const long nLineWidth(rMetaLineAction.GetLineInfo().GetWidth());
416 if(nLineWidth)
418 const long nHalfLineWidth((nLineWidth + 1) / 2);
419 aActionBounds.Left() -= nHalfLineWidth;
420 aActionBounds.Top() -= nHalfLineWidth;
421 aActionBounds.Right() += nHalfLineWidth;
422 aActionBounds.Bottom() += nHalfLineWidth;
424 break;
427 case MetaActionType::RECT:
428 aActionBounds = static_cast<const MetaRectAction&>(rAct).GetRect();
429 break;
431 case MetaActionType::ROUNDRECT:
432 aActionBounds = tools::Polygon( static_cast<const MetaRoundRectAction&>(rAct).GetRect(),
433 static_cast<const MetaRoundRectAction&>(rAct).GetHorzRound(),
434 static_cast<const MetaRoundRectAction&>(rAct).GetVertRound() ).GetBoundRect();
435 break;
437 case MetaActionType::ELLIPSE:
439 const Rectangle& rRect = static_cast<const MetaEllipseAction&>(rAct).GetRect();
440 aActionBounds = tools::Polygon( rRect.Center(),
441 rRect.GetWidth() >> 1,
442 rRect.GetHeight() >> 1 ).GetBoundRect();
443 break;
446 case MetaActionType::ARC:
447 aActionBounds = tools::Polygon( static_cast<const MetaArcAction&>(rAct).GetRect(),
448 static_cast<const MetaArcAction&>(rAct).GetStartPoint(),
449 static_cast<const MetaArcAction&>(rAct).GetEndPoint(), POLY_ARC ).GetBoundRect();
450 break;
452 case MetaActionType::PIE:
453 aActionBounds = tools::Polygon( static_cast<const MetaPieAction&>(rAct).GetRect(),
454 static_cast<const MetaPieAction&>(rAct).GetStartPoint(),
455 static_cast<const MetaPieAction&>(rAct).GetEndPoint(), POLY_PIE ).GetBoundRect();
456 break;
458 case MetaActionType::CHORD:
459 aActionBounds = tools::Polygon( static_cast<const MetaChordAction&>(rAct).GetRect(),
460 static_cast<const MetaChordAction&>(rAct).GetStartPoint(),
461 static_cast<const MetaChordAction&>(rAct).GetEndPoint(), POLY_CHORD ).GetBoundRect();
462 break;
464 case MetaActionType::POLYLINE:
466 const MetaPolyLineAction& rMetaPolyLineAction = static_cast<const MetaPolyLineAction&>(rAct);
467 aActionBounds = rMetaPolyLineAction.GetPolygon().GetBoundRect();
468 const long nLineWidth(rMetaPolyLineAction.GetLineInfo().GetWidth());
469 if(nLineWidth)
471 const long nHalfLineWidth((nLineWidth + 1) / 2);
472 aActionBounds.Left() -= nHalfLineWidth;
473 aActionBounds.Top() -= nHalfLineWidth;
474 aActionBounds.Right() += nHalfLineWidth;
475 aActionBounds.Bottom() += nHalfLineWidth;
477 break;
480 case MetaActionType::POLYGON:
481 aActionBounds = static_cast<const MetaPolygonAction&>(rAct).GetPolygon().GetBoundRect();
482 break;
484 case MetaActionType::POLYPOLYGON:
485 aActionBounds = static_cast<const MetaPolyPolygonAction&>(rAct).GetPolyPolygon().GetBoundRect();
486 break;
488 case MetaActionType::BMP:
489 aActionBounds = Rectangle( static_cast<const MetaBmpAction&>(rAct).GetPoint(),
490 rOut.PixelToLogic( static_cast<const MetaBmpAction&>(rAct).GetBitmap().GetSizePixel() ) );
491 break;
493 case MetaActionType::BMPSCALE:
494 aActionBounds = Rectangle( static_cast<const MetaBmpScaleAction&>(rAct).GetPoint(),
495 static_cast<const MetaBmpScaleAction&>(rAct).GetSize() );
496 break;
498 case MetaActionType::BMPSCALEPART:
499 aActionBounds = Rectangle( static_cast<const MetaBmpScalePartAction&>(rAct).GetDestPoint(),
500 static_cast<const MetaBmpScalePartAction&>(rAct).GetDestSize() );
501 break;
503 case MetaActionType::BMPEX:
504 aActionBounds = Rectangle( static_cast<const MetaBmpExAction&>(rAct).GetPoint(),
505 rOut.PixelToLogic( static_cast<const MetaBmpExAction&>(rAct).GetBitmapEx().GetSizePixel() ) );
506 break;
508 case MetaActionType::BMPEXSCALE:
509 aActionBounds = Rectangle( static_cast<const MetaBmpExScaleAction&>(rAct).GetPoint(),
510 static_cast<const MetaBmpExScaleAction&>(rAct).GetSize() );
511 break;
513 case MetaActionType::BMPEXSCALEPART:
514 aActionBounds = Rectangle( static_cast<const MetaBmpExScalePartAction&>(rAct).GetDestPoint(),
515 static_cast<const MetaBmpExScalePartAction&>(rAct).GetDestSize() );
516 break;
518 case MetaActionType::MASK:
519 aActionBounds = Rectangle( static_cast<const MetaMaskAction&>(rAct).GetPoint(),
520 rOut.PixelToLogic( static_cast<const MetaMaskAction&>(rAct).GetBitmap().GetSizePixel() ) );
521 break;
523 case MetaActionType::MASKSCALE:
524 aActionBounds = Rectangle( static_cast<const MetaMaskScaleAction&>(rAct).GetPoint(),
525 static_cast<const MetaMaskScaleAction&>(rAct).GetSize() );
526 break;
528 case MetaActionType::MASKSCALEPART:
529 aActionBounds = Rectangle( static_cast<const MetaMaskScalePartAction&>(rAct).GetDestPoint(),
530 static_cast<const MetaMaskScalePartAction&>(rAct).GetDestSize() );
531 break;
533 case MetaActionType::GRADIENT:
534 aActionBounds = static_cast<const MetaGradientAction&>(rAct).GetRect();
535 break;
537 case MetaActionType::GRADIENTEX:
538 aActionBounds = static_cast<const MetaGradientExAction&>(rAct).GetPolyPolygon().GetBoundRect();
539 break;
541 case MetaActionType::HATCH:
542 aActionBounds = static_cast<const MetaHatchAction&>(rAct).GetPolyPolygon().GetBoundRect();
543 break;
545 case MetaActionType::WALLPAPER:
546 aActionBounds = static_cast<const MetaWallpaperAction&>(rAct).GetRect();
547 break;
549 case MetaActionType::Transparent:
550 aActionBounds = static_cast<const MetaTransparentAction&>(rAct).GetPolyPolygon().GetBoundRect();
551 break;
553 case MetaActionType::FLOATTRANSPARENT:
554 aActionBounds = Rectangle( static_cast<const MetaFloatTransparentAction&>(rAct).GetPoint(),
555 static_cast<const MetaFloatTransparentAction&>(rAct).GetSize() );
556 break;
558 case MetaActionType::EPS:
559 aActionBounds = Rectangle( static_cast<const MetaEPSAction&>(rAct).GetPoint(),
560 static_cast<const MetaEPSAction&>(rAct).GetSize() );
561 break;
563 case MetaActionType::TEXT:
565 const MetaTextAction& rTextAct = static_cast<const MetaTextAction&>(rAct);
566 const OUString aString( rTextAct.GetText().copy(rTextAct.GetIndex(), rTextAct.GetLen()) );
568 if (!aString.isEmpty())
570 const Point aPtLog( rTextAct.GetPoint() );
572 // #105987# Use API method instead of Impl* methods
573 // #107490# Set base parameter equal to index parameter
574 rOut.GetTextBoundRect( aActionBounds, rTextAct.GetText(), rTextAct.GetIndex(),
575 rTextAct.GetIndex(), rTextAct.GetLen() );
576 aActionBounds.Move( aPtLog.X(), aPtLog.Y() );
579 break;
581 case MetaActionType::TEXTARRAY:
583 const MetaTextArrayAction& rTextAct = static_cast<const MetaTextArrayAction&>(rAct);
584 const OUString aString( rTextAct.GetText().copy(rTextAct.GetIndex(), rTextAct.GetLen()) );
586 if( !aString.isEmpty() )
588 // #105987# ImplLayout takes everything in logical coordinates
589 SalLayout* pSalLayout = rOut.ImplLayout( rTextAct.GetText(), rTextAct.GetIndex(),
590 rTextAct.GetLen(), rTextAct.GetPoint(),
591 0, rTextAct.GetDXArray() );
592 if( pSalLayout )
594 Rectangle aBoundRect( const_cast<OutputDevice&>(rOut).ImplGetTextBoundRect( *pSalLayout ) );
595 aActionBounds = rOut.PixelToLogic( aBoundRect );
596 pSalLayout->Release();
600 break;
602 case MetaActionType::TEXTRECT:
603 aActionBounds = static_cast<const MetaTextRectAction&>(rAct).GetRect();
604 break;
606 case MetaActionType::STRETCHTEXT:
608 const MetaStretchTextAction& rTextAct = static_cast<const MetaStretchTextAction&>(rAct);
609 const OUString aString( rTextAct.GetText().copy(rTextAct.GetIndex(), rTextAct.GetLen()) );
611 // #i16195# Literate copy from TextArray action, the
612 // semantics for the ImplLayout call are copied from the
613 // OutDev::DrawStretchText() code. Unfortunately, also in
614 // this case, public outdev methods such as GetTextWidth()
615 // don't provide enough info.
616 if( !aString.isEmpty() )
618 // #105987# ImplLayout takes everything in logical coordinates
619 SalLayout* pSalLayout = rOut.ImplLayout( rTextAct.GetText(), rTextAct.GetIndex(),
620 rTextAct.GetLen(), rTextAct.GetPoint(),
621 rTextAct.GetWidth() );
622 if( pSalLayout )
624 Rectangle aBoundRect( const_cast<OutputDevice&>(rOut).ImplGetTextBoundRect( *pSalLayout ) );
625 aActionBounds = rOut.PixelToLogic( aBoundRect );
626 pSalLayout->Release();
630 break;
632 case MetaActionType::TEXTLINE:
633 OSL_FAIL("MetaActionType::TEXTLINE not supported");
634 break;
636 default:
637 break;
640 if( !aActionBounds.IsEmpty() )
642 // fdo#40421 limit current action's output to clipped area
643 if( rOut.IsClipRegion() )
644 return rOut.LogicToPixel(
645 rOut.GetClipRegion().GetBoundRect().Intersection( aActionBounds ) );
646 else
647 return rOut.LogicToPixel( aActionBounds );
649 else
650 return Rectangle();
653 } // end anon namespace
655 bool OutputDevice::RemoveTransparenciesFromMetaFile( const GDIMetaFile& rInMtf, GDIMetaFile& rOutMtf,
656 long nMaxBmpDPIX, long nMaxBmpDPIY,
657 bool bReduceTransparency, bool bTransparencyAutoMode,
658 bool bDownsampleBitmaps,
659 const Color& rBackground
662 MetaAction* pCurrAct;
663 bool bTransparent( false );
665 rOutMtf.Clear();
667 if( ! bReduceTransparency || bTransparencyAutoMode )
669 // watch for transparent drawing actions
670 for( pCurrAct = ( (GDIMetaFile&) rInMtf ).FirstAction();
671 pCurrAct && !bTransparent;
672 pCurrAct = ( (GDIMetaFile&) rInMtf ).NextAction() )
674 // #i10613# determine if the action is transparency capable
676 // #107169# Also examine metafiles with masked bitmaps in
677 // detail. Further down, this is optimized in such a way
678 // that there's no unnecessary painting of masked bitmaps
679 // (which are _always_ subdivided into rectangular regions
680 // of uniform opacity): if a masked bitmap is printed over
681 // empty background, we convert to a plain bitmap with
682 // white background.
683 if( IsTransparentAction( *pCurrAct ) )
685 bTransparent = true;
690 // #i10613# Determine set of connected components containing transparent objects. These are
691 // then processed as bitmaps, the original actions are removed from the metafile.
692 if( !bTransparent )
694 // nothing transparent -> just copy
695 rOutMtf = rInMtf;
697 else
699 // #i10613#
700 // This works as follows: we want a number of distinct sets of
701 // connected components, where each set contains metafile
702 // actions that are intersecting (note: there are possibly
703 // more actions contained as are directly intersecting,
704 // because we can only produce rectangular bitmaps later
705 // on. Thus, each set of connected components is the smallest
706 // enclosing, axis-aligned rectangle that completely bounds a
707 // number of intersecting metafile actions, plus any action
708 // that would otherwise be cut in two). Therefore, we
709 // iteratively add metafile actions from the original metafile
710 // to this connected components list (aCCList), by checking
711 // each element's bounding box against intersection with the
712 // metaaction at hand.
713 // All those intersecting elements are removed from aCCList
714 // and collected in a temporary list (aCCMergeList). After all
715 // elements have been checked, the aCCMergeList elements are
716 // merged with the metaaction at hand into one resulting
717 // connected component, with one big bounding box, and
718 // inserted into aCCList again.
719 // The time complexity of this algorithm is O(n^3), where n is
720 // the number of metafile actions, and it finds all distinct
721 // regions of rectangle-bounded connected components. This
722 // algorithm was designed by AF.
724 // STAGE 1: Detect background
726 // Receives uniform background content, and is _not_ merged
727 // nor checked for intersection against other aCCList elements
728 ConnectedComponents aBackgroundComponent;
730 // create an OutputDevice to record mapmode changes and the like
731 ScopedVclPtrInstance< VirtualDevice > aMapModeVDev;
732 aMapModeVDev->mnDPIX = mnDPIX;
733 aMapModeVDev->mnDPIY = mnDPIY;
734 aMapModeVDev->EnableOutput(false);
736 int nLastBgAction, nActionNum;
738 // weed out page-filling background objects (if they are
739 // uniformly coloured). Keeping them outside the other
740 // connected components often prevents whole-page bitmap
741 // generation.
742 bool bStillBackground=true; // true until first non-bg action
743 nActionNum=0; nLastBgAction=-1;
744 pCurrAct=const_cast<GDIMetaFile&>(rInMtf).FirstAction();
745 if( rBackground != Color( COL_TRANSPARENT ) )
747 aBackgroundComponent.aBgColor = rBackground;
748 if( meOutDevType == OUTDEV_PRINTER )
750 Printer* pThis = dynamic_cast<Printer*>(this);
751 Point aPageOffset = pThis->GetPageOffsetPixel();
752 aPageOffset = Point( 0, 0 ) - aPageOffset;
753 Size aSize = pThis->GetPaperSizePixel();
754 aBackgroundComponent.aBounds = Rectangle( aPageOffset, aSize );
756 else
757 aBackgroundComponent.aBounds = Rectangle( Point( 0, 0 ), GetOutputSizePixel() );
759 while( pCurrAct && bStillBackground )
761 switch( pCurrAct->GetType() )
763 case MetaActionType::RECT:
765 if( !checkRect(
766 aBackgroundComponent.aBounds,
767 aBackgroundComponent.aBgColor,
768 static_cast<const MetaRectAction*>(pCurrAct)->GetRect(),
769 *aMapModeVDev.get()) )
770 bStillBackground=false; // incomplete occlusion of background
771 else
772 nLastBgAction=nActionNum; // this _is_ background
773 break;
775 case MetaActionType::POLYGON:
777 const tools::Polygon aPoly(
778 static_cast<const MetaPolygonAction*>(pCurrAct)->GetPolygon());
779 if( !basegfx::tools::isRectangle(
780 aPoly.getB2DPolygon()) ||
781 !checkRect(
782 aBackgroundComponent.aBounds,
783 aBackgroundComponent.aBgColor,
784 aPoly.GetBoundRect(),
785 *aMapModeVDev.get()) )
786 bStillBackground=false; // incomplete occlusion of background
787 else
788 nLastBgAction=nActionNum; // this _is_ background
789 break;
791 case MetaActionType::POLYPOLYGON:
793 const tools::PolyPolygon aPoly(
794 static_cast<const MetaPolyPolygonAction*>(pCurrAct)->GetPolyPolygon());
795 if( aPoly.Count() != 1 ||
796 !basegfx::tools::isRectangle(
797 aPoly[0].getB2DPolygon()) ||
798 !checkRect(
799 aBackgroundComponent.aBounds,
800 aBackgroundComponent.aBgColor,
801 aPoly.GetBoundRect(),
802 *aMapModeVDev.get()) )
803 bStillBackground=false; // incomplete occlusion of background
804 else
805 nLastBgAction=nActionNum; // this _is_ background
806 break;
808 case MetaActionType::WALLPAPER:
810 if( !checkRect(
811 aBackgroundComponent.aBounds,
812 aBackgroundComponent.aBgColor,
813 static_cast<const MetaWallpaperAction*>(pCurrAct)->GetRect(),
814 *aMapModeVDev.get()) )
815 bStillBackground=false; // incomplete occlusion of background
816 else
817 nLastBgAction=nActionNum; // this _is_ background
818 break;
820 default:
822 if( ImplIsNotTransparent( *pCurrAct,
823 *aMapModeVDev.get() ) )
824 bStillBackground=false; // non-transparent action, possibly
825 // not uniform
826 else
827 // extend current bounds (next uniform action
828 // needs to fully cover this area)
829 aBackgroundComponent.aBounds.Union(
830 ImplCalcActionBounds(*pCurrAct, *aMapModeVDev.get()) );
831 break;
835 // execute action to get correct MapModes etc.
836 pCurrAct->Execute( aMapModeVDev.get() );
838 pCurrAct=const_cast<GDIMetaFile&>(rInMtf).NextAction();
839 ++nActionNum;
842 // clean up aMapModeVDev
843 sal_uInt32 nCount = aMapModeVDev->GetGCStackDepth();
844 while( nCount-- )
845 aMapModeVDev->Pop();
847 ConnectedComponentsList aCCList; // list containing distinct sets of connected components as elements.
849 // fast-forward until one after the last background action
850 // (need to reconstruct map mode vdev state)
851 nActionNum=0;
852 pCurrAct=const_cast<GDIMetaFile&>(rInMtf).FirstAction();
853 while( pCurrAct && nActionNum<=nLastBgAction )
855 // up to and including last ink-generating background
856 // action go to background component
857 aBackgroundComponent.aComponentList.push_back(
858 ::std::make_pair(
859 pCurrAct, nActionNum) );
861 // execute action to get correct MapModes etc.
862 pCurrAct->Execute( aMapModeVDev.get() );
863 pCurrAct=const_cast<GDIMetaFile&>(rInMtf).NextAction();
864 ++nActionNum;
867 // STAGE 2: Generate connected components list
869 // iterate over all actions (start where background action
870 // search left off)
871 for( ;
872 pCurrAct;
873 pCurrAct=const_cast<GDIMetaFile&>(rInMtf).NextAction(), ++nActionNum )
875 // execute action to get correct MapModes etc.
876 pCurrAct->Execute( aMapModeVDev.get() );
878 // cache bounds of current action
879 const Rectangle aBBCurrAct( ImplCalcActionBounds(*pCurrAct, *aMapModeVDev.get()) );
881 // accumulate collected bounds here, initialize with current action
882 Rectangle aTotalBounds( aBBCurrAct ); // thus,
883 // aTotalComponents.aBounds
884 // is
885 // empty
886 // for
887 // non-output-generating
888 // actions
889 bool bTreatSpecial( false );
890 ConnectedComponents aTotalComponents;
892 // STAGE 2.1: Search for intersecting cc entries
894 // if aBBCurrAct is empty, it will intersect with no
895 // aCCList member. Thus, we can save the check.
896 // Furthermore, this ensures that non-output-generating
897 // actions get their own aCCList entry, which is necessary
898 // when copying them to the output metafile (see stage 4
899 // below).
901 // #107169# Wholly transparent objects need
902 // not be considered for connected components,
903 // too. Just put each of them into a separate
904 // component.
905 aTotalComponents.bIsFullyTransparent = !ImplIsNotTransparent(*pCurrAct, *aMapModeVDev.get());
907 if( !aBBCurrAct.IsEmpty() &&
908 !aTotalComponents.bIsFullyTransparent )
910 if( !aBackgroundComponent.aComponentList.empty() &&
911 !aBackgroundComponent.aBounds.IsInside(aTotalBounds) )
913 // it seems the background is not large enough. to
914 // be on the safe side, combine with this component.
915 aTotalBounds.Union( aBackgroundComponent.aBounds );
917 // extract all aCurr actions to aTotalComponents
918 aTotalComponents.aComponentList.splice( aTotalComponents.aComponentList.end(),
919 aBackgroundComponent.aComponentList );
921 if( aBackgroundComponent.bIsSpecial )
922 bTreatSpecial = true;
925 ConnectedComponentsList::iterator aCurrCC;
926 const ConnectedComponentsList::iterator aLastCC( aCCList.end() );
927 bool bSomeComponentsChanged;
929 // now, this is unfortunate: since changing anyone of
930 // the aCCList elements (e.g. by merging or addition
931 // of an action) might generate new intersection with
932 // other aCCList elements, have to repeat the whole
933 // element scanning, until nothing changes anymore.
934 // Thus, this loop here makes us O(n^3) in the worst
935 // case.
938 // only loop here if 'intersects' branch below was hit
939 bSomeComponentsChanged = false;
941 // iterate over all current members of aCCList
942 for( aCurrCC=aCCList.begin(); aCurrCC != aLastCC; )
944 // first check if current element's bounds are
945 // empty. This ensures that empty actions are not
946 // merged into one component, as a matter of fact,
947 // they have no position.
949 // #107169# Wholly transparent objects need
950 // not be considered for connected components,
951 // too. Just put each of them into a separate
952 // component.
953 if( !aCurrCC->aBounds.IsEmpty() &&
954 !aCurrCC->bIsFullyTransparent &&
955 aCurrCC->aBounds.IsOver( aTotalBounds ) )
957 // union the intersecting aCCList element into aTotalComponents
959 // calc union bounding box
960 aTotalBounds.Union( aCurrCC->aBounds );
962 // extract all aCurr actions to aTotalComponents
963 aTotalComponents.aComponentList.splice( aTotalComponents.aComponentList.end(),
964 aCurrCC->aComponentList );
966 if( aCurrCC->bIsSpecial )
967 bTreatSpecial = true;
969 // remove and delete aCurrCC element from list (we've now merged its content)
970 aCurrCC = aCCList.erase( aCurrCC );
972 // at least one component changed, need to rescan everything
973 bSomeComponentsChanged = true;
975 else
977 ++aCurrCC;
981 while( bSomeComponentsChanged );
984 // STAGE 2.2: Determine special state for cc element
986 // now test whether the whole connected component must be
987 // treated specially (i.e. rendered as a bitmap): if the
988 // added action is the very first action, or all actions
989 // before it are completely transparent, the connected
990 // component need not be treated specially, not even if
991 // the added action contains transparency. This is because
992 // painting of transparent objects on _white background_
993 // works without alpha compositing (you just calculate the
994 // color). Note that for the test "all objects before me
995 // are transparent" no sorting is necessary, since the
996 // added metaaction pCurrAct is always in the order the
997 // metafile is painted. Generally, the order of the
998 // metaactions in the ConnectedComponents are not
999 // guaranteed to be the same as in the metafile.
1000 if( bTreatSpecial )
1002 // prev component(s) special -> this one, too
1003 aTotalComponents.bIsSpecial = true;
1005 else if( !IsTransparentAction( *pCurrAct ) )
1007 // added action and none of prev components special ->
1008 // this one normal, too
1009 aTotalComponents.bIsSpecial = false;
1011 else
1013 // added action is special and none of prev components
1014 // special -> do the detailed tests
1016 // can the action handle transparency correctly
1017 // (i.e. when painted on white background, does the
1018 // action still look correct)?
1019 if( !DoesActionHandleTransparency( *pCurrAct ) )
1021 // no, action cannot handle its transparency on
1022 // a printer device, render to bitmap
1023 aTotalComponents.bIsSpecial = true;
1025 else
1027 // yes, action can handle its transparency, so
1028 // check whether we're on white background
1029 if( aTotalComponents.aComponentList.empty() )
1031 // nothing between pCurrAct and page
1032 // background -> don't be special
1033 aTotalComponents.bIsSpecial = false;
1035 else
1037 // #107169# Fixes above now ensure that _no_
1038 // object in the list is fully transparent. Thus,
1039 // if the component list is not empty above, we
1040 // must assume that we have to treat this
1041 // component special.
1043 // there are non-transparent objects between
1044 // pCurrAct and the empty sheet of paper -> be
1045 // special, then
1046 aTotalComponents.bIsSpecial = true;
1051 // STAGE 2.3: Add newly generated CC list element
1053 // set new bounds and add action to list
1054 aTotalComponents.aBounds = aTotalBounds;
1055 aTotalComponents.aComponentList.push_back(
1056 ::std::make_pair(
1057 pCurrAct, nActionNum) );
1059 // add aTotalComponents as a new entry to aCCList
1060 aCCList.push_back( aTotalComponents );
1062 DBG_ASSERT( !aTotalComponents.aComponentList.empty(),
1063 "Printer::GetPreparedMetaFile empty component" );
1064 DBG_ASSERT( !aTotalComponents.aBounds.IsEmpty() || (aTotalComponents.aComponentList.size() == 1),
1065 "Printer::GetPreparedMetaFile non-output generating actions must be solitary");
1066 DBG_ASSERT( !aTotalComponents.bIsFullyTransparent || (aTotalComponents.aComponentList.size() == 1),
1067 "Printer::GetPreparedMetaFile fully transparent actions must be solitary");
1070 // well now, we've got the list of disjunct connected
1071 // components. Now we've got to create a map, which contains
1072 // the corresponding aCCList element for every
1073 // metaaction. Later on, we always process the complete
1074 // metafile for each bitmap to be generated, but switch on
1075 // output only for actions contained in the then current
1076 // aCCList element. This ensures correct mapmode and attribute
1077 // settings for all cases.
1079 // maps mtf actions to CC list entries
1080 ::std::vector< const ConnectedComponents* > aCCList_MemberMap( rInMtf.GetActionSize() );
1082 // iterate over all aCCList members and their contained metaactions
1083 ConnectedComponentsList::iterator aCurr( aCCList.begin() );
1084 const ConnectedComponentsList::iterator aLast( aCCList.end() );
1085 for( ; aCurr != aLast; ++aCurr )
1087 ComponentList::iterator aCurrentAction( aCurr->aComponentList.begin() );
1088 const ComponentList::iterator aLastAction( aCurr->aComponentList.end() );
1089 for( ; aCurrentAction != aLastAction; ++aCurrentAction )
1091 // set pointer to aCCList element for corresponding index
1092 aCCList_MemberMap[ aCurrentAction->second ] = &(*aCurr);
1096 // STAGE 3.1: Output background mtf actions (if there are any)
1098 ComponentList::iterator aCurrAct( aBackgroundComponent.aComponentList.begin() );
1099 const ComponentList::iterator aLastAct( aBackgroundComponent.aComponentList.end() );
1100 for( ; aCurrAct != aLastAct; ++aCurrAct )
1102 // simply add this action (above, we inserted the actions
1103 // starting at index 0 up to and including nLastBgAction)
1104 rOutMtf.AddAction( ( aCurrAct->first->Duplicate(), aCurrAct->first ) );
1107 // STAGE 3.2: Generate banded bitmaps for special regions
1109 Point aPageOffset;
1110 Size aTmpSize( GetOutputSizePixel() );
1111 if( mpPDFWriter )
1113 aTmpSize = mpPDFWriter->getCurPageSize();
1114 aTmpSize = LogicToPixel( aTmpSize, MapMode( MAP_POINT ) );
1116 // also add error code to PDFWriter
1117 mpPDFWriter->insertError( vcl::PDFWriter::Warning_Transparency_Converted );
1119 else if( meOutDevType == OUTDEV_PRINTER )
1121 Printer* pThis = dynamic_cast<Printer*>(this);
1122 aPageOffset = pThis->GetPageOffsetPixel();
1123 aPageOffset = Point( 0, 0 ) - aPageOffset;
1124 aTmpSize = pThis->GetPaperSizePixel();
1126 const Rectangle aOutputRect( aPageOffset, aTmpSize );
1127 bool bTiling = dynamic_cast<Printer*>(this) != nullptr;
1129 // iterate over all aCCList members and generate bitmaps for the special ones
1130 for( aCurr = aCCList.begin(); aCurr != aLast; ++aCurr )
1132 if( aCurr->bIsSpecial )
1134 Rectangle aBoundRect( aCurr->aBounds );
1135 aBoundRect.Intersection( aOutputRect );
1137 const double fBmpArea( (double) aBoundRect.GetWidth() * aBoundRect.GetHeight() );
1138 const double fOutArea( (double) aOutputRect.GetWidth() * aOutputRect.GetHeight() );
1140 // check if output doesn't exceed given size
1141 if( bReduceTransparency && bTransparencyAutoMode && ( fBmpArea > ( 0.25 * fOutArea ) ) )
1143 // output normally. Therefore, we simply clear the
1144 // special attribute, as everything non-special is
1145 // copied to rOutMtf further below.
1146 aCurr->bIsSpecial = false;
1148 else
1150 // create new bitmap action first
1151 if( aBoundRect.GetWidth() && aBoundRect.GetHeight() )
1153 Point aDstPtPix( aBoundRect.TopLeft() );
1154 Size aDstSzPix;
1156 ScopedVclPtrInstance<VirtualDevice> aMapVDev; // here, we record only mapmode information
1157 aMapVDev->EnableOutput(false);
1159 ScopedVclPtrInstance<VirtualDevice> aPaintVDev; // into this one, we render.
1160 aPaintVDev->SetBackground( aBackgroundComponent.aBgColor );
1162 rOutMtf.AddAction( new MetaPushAction( PushFlags::MAPMODE ) );
1163 rOutMtf.AddAction( new MetaMapModeAction() );
1165 aPaintVDev->SetDrawMode( GetDrawMode() );
1167 while( aDstPtPix.Y() <= aBoundRect.Bottom() )
1169 aDstPtPix.X() = aBoundRect.Left();
1170 aDstSzPix = bTiling ? Size( MAX_TILE_WIDTH, MAX_TILE_HEIGHT ) : aBoundRect.GetSize();
1172 if( ( aDstPtPix.Y() + aDstSzPix.Height() - 1L ) > aBoundRect.Bottom() )
1173 aDstSzPix.Height() = aBoundRect.Bottom() - aDstPtPix.Y() + 1L;
1175 while( aDstPtPix.X() <= aBoundRect.Right() )
1177 if( ( aDstPtPix.X() + aDstSzPix.Width() - 1L ) > aBoundRect.Right() )
1178 aDstSzPix.Width() = aBoundRect.Right() - aDstPtPix.X() + 1L;
1180 if( !Rectangle( aDstPtPix, aDstSzPix ).Intersection( aBoundRect ).IsEmpty() &&
1181 aPaintVDev->SetOutputSizePixel( aDstSzPix ) )
1183 aPaintVDev->Push();
1184 aMapVDev->Push();
1186 aMapVDev->mnDPIX = aPaintVDev->mnDPIX = mnDPIX;
1187 aMapVDev->mnDPIY = aPaintVDev->mnDPIY = mnDPIY;
1189 aPaintVDev->EnableOutput(false);
1191 // iterate over all actions
1192 for( pCurrAct=const_cast<GDIMetaFile&>(rInMtf).FirstAction(), nActionNum=0;
1193 pCurrAct;
1194 pCurrAct=const_cast<GDIMetaFile&>(rInMtf).NextAction(), ++nActionNum )
1196 // enable output only for
1197 // actions that are members of
1198 // the current aCCList element
1199 // (aCurr)
1200 if( aCCList_MemberMap[nActionNum] == &(*aCurr) )
1201 aPaintVDev->EnableOutput();
1203 // but process every action
1204 const MetaActionType nType( pCurrAct->GetType() );
1206 if( MetaActionType::MAPMODE == nType )
1208 pCurrAct->Execute( aMapVDev.get() );
1210 MapMode aMtfMap( aMapVDev->GetMapMode() );
1211 const Point aNewOrg( aMapVDev->PixelToLogic( aDstPtPix ) );
1213 aMtfMap.SetOrigin( Point( -aNewOrg.X(), -aNewOrg.Y() ) );
1214 aPaintVDev->SetMapMode( aMtfMap );
1216 else if( ( MetaActionType::PUSH == nType ) || ( MetaActionType::POP ) == nType )
1218 pCurrAct->Execute( aMapVDev.get() );
1219 pCurrAct->Execute( aPaintVDev.get() );
1221 else if( MetaActionType::GRADIENT == nType )
1223 MetaGradientAction* pGradientAction = static_cast<MetaGradientAction*>(pCurrAct);
1224 Printer* pPrinter = dynamic_cast< Printer* >(this);
1225 if( pPrinter )
1226 pPrinter->DrawGradientEx( aPaintVDev.get(), pGradientAction->GetRect(), pGradientAction->GetGradient() );
1227 else
1228 DrawGradient( pGradientAction->GetRect(), pGradientAction->GetGradient() );
1230 else
1232 pCurrAct->Execute( aPaintVDev.get() );
1235 if( !( nActionNum % 8 ) )
1236 Application::Reschedule();
1239 const bool bOldMap = mbMap;
1240 mbMap = aPaintVDev->mbMap = false;
1242 Bitmap aBandBmp( aPaintVDev->GetBitmap( Point(), aDstSzPix ) );
1244 // scale down bitmap, if requested
1245 if( bDownsampleBitmaps )
1247 aBandBmp = GetDownsampledBitmap( aDstSzPix,
1248 Point(), aBandBmp.GetSizePixel(),
1249 aBandBmp, nMaxBmpDPIX, nMaxBmpDPIY );
1252 rOutMtf.AddAction( new MetaCommentAction( "PRNSPOOL_TRANSPARENTBITMAP_BEGIN" ) );
1253 rOutMtf.AddAction( new MetaBmpScaleAction( aDstPtPix, aDstSzPix, aBandBmp ) );
1254 rOutMtf.AddAction( new MetaCommentAction( "PRNSPOOL_TRANSPARENTBITMAP_END" ) );
1256 aPaintVDev->mbMap = true;
1257 mbMap = bOldMap;
1258 aMapVDev->Pop();
1259 aPaintVDev->Pop();
1262 // overlapping bands to avoid missing lines (e.g. PostScript)
1263 aDstPtPix.X() += aDstSzPix.Width();
1266 // overlapping bands to avoid missing lines (e.g. PostScript)
1267 aDstPtPix.Y() += aDstSzPix.Height();
1270 rOutMtf.AddAction( new MetaPopAction() );
1276 // clean up aMapModeVDev
1277 nCount = aMapModeVDev->GetGCStackDepth();
1278 while( nCount-- )
1279 aMapModeVDev->Pop();
1281 // STAGE 4: Copy actions to output metafile
1283 // iterate over all actions and duplicate the ones not in a
1284 // special aCCList member into rOutMtf
1285 for( pCurrAct=const_cast<GDIMetaFile&>(rInMtf).FirstAction(), nActionNum=0;
1286 pCurrAct;
1287 pCurrAct=const_cast<GDIMetaFile&>(rInMtf).NextAction(), ++nActionNum )
1289 const ConnectedComponents* pCurrAssociatedComponent = aCCList_MemberMap[nActionNum];
1291 // NOTE: This relies on the fact that map-mode or draw
1292 // mode changing actions are solitary aCCList elements and
1293 // have empty bounding boxes, see comment on stage 2.1
1294 // above
1295 if( pCurrAssociatedComponent &&
1296 (pCurrAssociatedComponent->aBounds.IsEmpty() ||
1297 !pCurrAssociatedComponent->bIsSpecial) )
1299 // #107169# Treat transparent bitmaps special, if they
1300 // are the first (or sole) action in their bounds
1301 // list. Note that we previously ensured that no
1302 // fully-transparent objects are before us here.
1303 if( DoesActionHandleTransparency( *pCurrAct ) &&
1304 pCurrAssociatedComponent->aComponentList.begin()->first == pCurrAct )
1306 // convert actions, where masked-out parts are of
1307 // given background color
1308 ImplConvertTransparentAction(rOutMtf,
1309 *pCurrAct,
1310 *aMapModeVDev.get(),
1311 aBackgroundComponent.aBgColor);
1313 else
1315 // simply add this action
1316 rOutMtf.AddAction( ( pCurrAct->Duplicate(), pCurrAct ) );
1319 pCurrAct->Execute(aMapModeVDev.get());
1323 rOutMtf.SetPrefMapMode( rInMtf.GetPrefMapMode() );
1324 rOutMtf.SetPrefSize( rInMtf.GetPrefSize() );
1326 #if OSL_DEBUG_LEVEL > 1
1327 // iterate over all aCCList members and generate rectangles for the bounding boxes
1328 rOutMtf.AddAction( new MetaFillColorAction( COL_WHITE, false ) );
1329 for( aCurr = aCCList.begin(); aCurr != aLast; ++aCurr )
1331 if( aCurr->bIsSpecial )
1332 rOutMtf.AddAction( new MetaLineColorAction( COL_RED, true) );
1333 else
1334 rOutMtf.AddAction( new MetaLineColorAction( COL_BLUE, true) );
1336 rOutMtf.AddAction( new MetaRectAction( aMapModeVDev->PixelToLogic( aCurr->aBounds ) ) );
1338 #endif
1340 return bTransparent;
1343 void Printer::DrawGradientEx( OutputDevice* pOut, const Rectangle& rRect, const Gradient& rGradient )
1345 const PrinterOptions& rPrinterOptions = GetPrinterOptions();
1347 if( rPrinterOptions.IsReduceGradients() )
1349 if( PRINTER_GRADIENT_STRIPES == rPrinterOptions.GetReducedGradientMode() )
1351 if( !rGradient.GetSteps() || ( rGradient.GetSteps() > rPrinterOptions.GetReducedGradientStepCount() ) )
1353 Gradient aNewGradient( rGradient );
1355 aNewGradient.SetSteps( rPrinterOptions.GetReducedGradientStepCount() );
1356 pOut->DrawGradient( rRect, aNewGradient );
1358 else
1359 pOut->DrawGradient( rRect, rGradient );
1361 else
1363 const Color& rStartColor = rGradient.GetStartColor();
1364 const Color& rEndColor = rGradient.GetEndColor();
1365 const long nR = ( ( (long) rStartColor.GetRed() * rGradient.GetStartIntensity() ) / 100L +
1366 ( (long) rEndColor.GetRed() * rGradient.GetEndIntensity() ) / 100L ) >> 1;
1367 const long nG = ( ( (long) rStartColor.GetGreen() * rGradient.GetStartIntensity() ) / 100L +
1368 ( (long) rEndColor.GetGreen() * rGradient.GetEndIntensity() ) / 100L ) >> 1;
1369 const long nB = ( ( (long) rStartColor.GetBlue() * rGradient.GetStartIntensity() ) / 100L +
1370 ( (long) rEndColor.GetBlue() * rGradient.GetEndIntensity() ) / 100L ) >> 1;
1371 const Color aColor( (sal_uInt8) nR, (sal_uInt8) nG, (sal_uInt8) nB );
1373 pOut->Push( PushFlags::LINECOLOR | PushFlags::FILLCOLOR );
1374 pOut->SetLineColor( aColor );
1375 pOut->SetFillColor( aColor );
1376 pOut->DrawRect( rRect );
1377 pOut->Pop();
1380 else
1381 pOut->DrawGradient( rRect, rGradient );
1384 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */