bump product version to 7.2.5.1
[LibreOffice.git] / vcl / source / gdi / print2.cxx
blob3526d9017cb51da8a09e5fa3357d2e11dc6d5729
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 <utility>
21 #include <list>
22 #include <vector>
24 #include <basegfx/polygon/b2dpolygontools.hxx>
25 #include <sal/log.hxx>
26 #include <officecfg/Office/Common.hxx>
28 #include <vcl/virdev.hxx>
29 #include <vcl/metaact.hxx>
30 #include <vcl/gdimtf.hxx>
31 #include <vcl/print.hxx>
32 #include <vcl/svapp.hxx>
33 #include <vcl/BitmapReadAccess.hxx>
35 #include "pdfwriter_impl.hxx"
37 #define MAX_TILE_WIDTH 1024
38 #define MAX_TILE_HEIGHT 1024
40 typedef ::std::pair< MetaAction*, int > Component; // MetaAction plus index in metafile
42 namespace {
44 // List of (intersecting) actions, plus overall bounds
45 struct ConnectedComponents
47 ConnectedComponents() :
48 aComponentList(),
49 aBounds(),
50 aBgColor(COL_WHITE),
51 bIsSpecial(false),
52 bIsFullyTransparent(false)
55 ::std::list< Component > aComponentList;
56 tools::Rectangle aBounds;
57 Color aBgColor;
58 bool bIsSpecial;
59 bool bIsFullyTransparent;
64 namespace {
66 /** Determines whether the action can handle transparency correctly
67 (i.e. when painted on white background, does the action still look
68 correct)?
70 bool DoesActionHandleTransparency( const MetaAction& rAct )
72 // MetaActionType::FLOATTRANSPARENT can contain a whole metafile,
73 // which is to be rendered with the given transparent gradient. We
74 // currently cannot emulate transparent painting on a white
75 // background reliably.
77 // the remainder can handle printing itself correctly on a uniform
78 // white background.
79 switch( rAct.GetType() )
81 case MetaActionType::Transparent:
82 case MetaActionType::BMPEX:
83 case MetaActionType::BMPEXSCALE:
84 case MetaActionType::BMPEXSCALEPART:
85 return true;
87 default:
88 return false;
92 bool doesRectCoverWithUniformColor(
93 tools::Rectangle const & rPrevRect,
94 tools::Rectangle const & rCurrRect,
95 OutputDevice const & rMapModeVDev)
97 // shape needs to fully cover previous content, and have uniform
98 // color
99 return (rMapModeVDev.LogicToPixel(rCurrRect).IsInside(rPrevRect) &&
100 rMapModeVDev.IsFillColor());
103 /** Check whether rCurrRect rectangle fully covers io_rPrevRect - if
104 yes, return true and update o_rBgColor
106 bool checkRect( tools::Rectangle& io_rPrevRect,
107 Color& o_rBgColor,
108 const tools::Rectangle& rCurrRect,
109 OutputDevice const & rMapModeVDev )
111 bool bRet = doesRectCoverWithUniformColor(io_rPrevRect, rCurrRect, rMapModeVDev);
113 if( bRet )
115 io_rPrevRect = rCurrRect;
116 o_rBgColor = rMapModeVDev.GetFillColor();
119 return bRet;
122 /** #107169# Convert BitmapEx to Bitmap with appropriately blended
123 color. Convert MetaTransparentAction to plain polygon,
124 appropriately colored
126 @param o_rMtf
127 Add converted actions to this metafile
129 void ImplConvertTransparentAction( GDIMetaFile& o_rMtf,
130 const MetaAction& rAct,
131 const OutputDevice& rStateOutDev,
132 Color aBgColor )
134 if (rAct.GetType() == MetaActionType::Transparent)
136 const MetaTransparentAction* pTransAct = static_cast<const MetaTransparentAction*>(&rAct);
137 sal_uInt16 nTransparency( pTransAct->GetTransparence() );
139 // #i10613# Respect transparency for draw color
140 if (nTransparency)
142 o_rMtf.AddAction(new MetaPushAction(PushFlags::LINECOLOR|PushFlags::FILLCOLOR));
144 // assume white background for alpha blending
145 Color aLineColor(rStateOutDev.GetLineColor());
146 aLineColor.SetRed(static_cast<sal_uInt8>((255*nTransparency + (100 - nTransparency) * aLineColor.GetRed()) / 100));
147 aLineColor.SetGreen(static_cast<sal_uInt8>((255*nTransparency + (100 - nTransparency) * aLineColor.GetGreen()) / 100));
148 aLineColor.SetBlue(static_cast<sal_uInt8>((255*nTransparency + (100 - nTransparency) * aLineColor.GetBlue()) / 100));
149 o_rMtf.AddAction(new MetaLineColorAction(aLineColor, true));
151 Color aFillColor(rStateOutDev.GetFillColor());
152 aFillColor.SetRed(static_cast<sal_uInt8>((255*nTransparency + (100 - nTransparency)*aFillColor.GetRed()) / 100));
153 aFillColor.SetGreen(static_cast<sal_uInt8>((255*nTransparency + (100 - nTransparency)*aFillColor.GetGreen()) / 100));
154 aFillColor.SetBlue(static_cast<sal_uInt8>((255*nTransparency + (100 - nTransparency)*aFillColor.GetBlue()) / 100));
155 o_rMtf.AddAction(new MetaFillColorAction(aFillColor, true));
158 o_rMtf.AddAction(new MetaPolyPolygonAction(pTransAct->GetPolyPolygon()));
160 if(nTransparency)
161 o_rMtf.AddAction(new MetaPopAction());
163 else
165 BitmapEx aBmpEx;
167 switch (rAct.GetType())
169 case MetaActionType::BMPEX:
170 aBmpEx = static_cast<const MetaBmpExAction&>(rAct).GetBitmapEx();
171 break;
173 case MetaActionType::BMPEXSCALE:
174 aBmpEx = static_cast<const MetaBmpExScaleAction&>(rAct).GetBitmapEx();
175 break;
177 case MetaActionType::BMPEXSCALEPART:
178 aBmpEx = static_cast<const MetaBmpExScaleAction&>(rAct).GetBitmapEx();
179 break;
181 case MetaActionType::Transparent:
183 default:
184 OSL_FAIL("Printer::GetPreparedMetafile impossible state reached");
185 break;
188 Bitmap aBmp(aBmpEx.GetBitmap());
189 if (aBmpEx.IsAlpha())
191 // blend with alpha channel
192 aBmp.Convert(BmpConversion::N24Bit);
193 aBmp.Blend(aBmpEx.GetAlpha(), aBgColor);
196 // add corresponding action
197 switch (rAct.GetType())
199 case MetaActionType::BMPEX:
200 o_rMtf.AddAction(new MetaBmpAction(
201 static_cast<const MetaBmpExAction&>(rAct).GetPoint(),
202 aBmp));
203 break;
204 case MetaActionType::BMPEXSCALE:
205 o_rMtf.AddAction(new MetaBmpScaleAction(
206 static_cast<const MetaBmpExScaleAction&>(rAct).GetPoint(),
207 static_cast<const MetaBmpExScaleAction&>(rAct).GetSize(),
208 aBmp));
209 break;
210 case MetaActionType::BMPEXSCALEPART:
211 o_rMtf.AddAction(new MetaBmpScalePartAction(
212 static_cast<const MetaBmpExScalePartAction&>(rAct).GetDestPoint(),
213 static_cast<const MetaBmpExScalePartAction&>(rAct).GetDestSize(),
214 static_cast<const MetaBmpExScalePartAction&>(rAct).GetSrcPoint(),
215 static_cast<const MetaBmpExScalePartAction&>(rAct).GetSrcSize(),
216 aBmp));
217 break;
218 default:
219 OSL_FAIL("Unexpected case");
220 break;
225 // #i10613# Extracted from ImplCheckRect::ImplCreate
226 // Returns true, if given action creates visible (i.e. non-transparent) output
227 bool ImplIsNotTransparent( const MetaAction& rAct, const OutputDevice& rOut )
229 const bool bLineTransparency( !rOut.IsLineColor() || rOut.GetLineColor().IsFullyTransparent() );
230 const bool bFillTransparency( !rOut.IsFillColor() || rOut.GetFillColor().IsFullyTransparent() );
231 bool bRet( false );
233 switch( rAct.GetType() )
235 case MetaActionType::POINT:
236 if( !bLineTransparency )
237 bRet = true;
238 break;
240 case MetaActionType::LINE:
241 if( !bLineTransparency )
242 bRet = true;
243 break;
245 case MetaActionType::RECT:
246 if( !bLineTransparency || !bFillTransparency )
247 bRet = true;
248 break;
250 case MetaActionType::ROUNDRECT:
251 if( !bLineTransparency || !bFillTransparency )
252 bRet = true;
253 break;
255 case MetaActionType::ELLIPSE:
256 if( !bLineTransparency || !bFillTransparency )
257 bRet = true;
258 break;
260 case MetaActionType::ARC:
261 if( !bLineTransparency || !bFillTransparency )
262 bRet = true;
263 break;
265 case MetaActionType::PIE:
266 if( !bLineTransparency || !bFillTransparency )
267 bRet = true;
268 break;
270 case MetaActionType::CHORD:
271 if( !bLineTransparency || !bFillTransparency )
272 bRet = true;
273 break;
275 case MetaActionType::POLYLINE:
276 if( !bLineTransparency )
277 bRet = true;
278 break;
280 case MetaActionType::POLYGON:
281 if( !bLineTransparency || !bFillTransparency )
282 bRet = true;
283 break;
285 case MetaActionType::POLYPOLYGON:
286 if( !bLineTransparency || !bFillTransparency )
287 bRet = true;
288 break;
290 case MetaActionType::TEXT:
292 const MetaTextAction& rTextAct = static_cast<const MetaTextAction&>(rAct);
293 const OUString aString( rTextAct.GetText().copy(rTextAct.GetIndex(), rTextAct.GetLen()) );
294 if (!aString.isEmpty())
295 bRet = true;
297 break;
299 case MetaActionType::TEXTARRAY:
301 const MetaTextArrayAction& rTextAct = static_cast<const MetaTextArrayAction&>(rAct);
302 const OUString aString( rTextAct.GetText().copy(rTextAct.GetIndex(), rTextAct.GetLen()) );
303 if (!aString.isEmpty())
304 bRet = true;
306 break;
308 case MetaActionType::PIXEL:
309 case MetaActionType::BMP:
310 case MetaActionType::BMPSCALE:
311 case MetaActionType::BMPSCALEPART:
312 case MetaActionType::BMPEX:
313 case MetaActionType::BMPEXSCALE:
314 case MetaActionType::BMPEXSCALEPART:
315 case MetaActionType::MASK:
316 case MetaActionType::MASKSCALE:
317 case MetaActionType::MASKSCALEPART:
318 case MetaActionType::GRADIENT:
319 case MetaActionType::GRADIENTEX:
320 case MetaActionType::HATCH:
321 case MetaActionType::WALLPAPER:
322 case MetaActionType::Transparent:
323 case MetaActionType::FLOATTRANSPARENT:
324 case MetaActionType::EPS:
325 case MetaActionType::TEXTRECT:
326 case MetaActionType::STRETCHTEXT:
327 case MetaActionType::TEXTLINE:
328 // all other actions: generate non-transparent output
329 bRet = true;
330 break;
332 default:
333 break;
336 return bRet;
339 // #i10613# Extracted from ImplCheckRect::ImplCreate
340 tools::Rectangle ImplCalcActionBounds( const MetaAction& rAct, const OutputDevice& rOut )
342 tools::Rectangle aActionBounds;
344 switch( rAct.GetType() )
346 case MetaActionType::PIXEL:
347 aActionBounds = tools::Rectangle( static_cast<const MetaPixelAction&>(rAct).GetPoint(), Size( 1, 1 ) );
348 break;
350 case MetaActionType::POINT:
351 aActionBounds = tools::Rectangle( static_cast<const MetaPointAction&>(rAct).GetPoint(), Size( 1, 1 ) );
352 break;
354 case MetaActionType::LINE:
356 const MetaLineAction& rMetaLineAction = static_cast<const MetaLineAction&>(rAct);
357 aActionBounds = tools::Rectangle( rMetaLineAction.GetStartPoint(), rMetaLineAction.GetEndPoint() );
358 aActionBounds.Justify();
359 const tools::Long nLineWidth(rMetaLineAction.GetLineInfo().GetWidth());
360 if(nLineWidth)
362 const tools::Long nHalfLineWidth((nLineWidth + 1) / 2);
363 aActionBounds.AdjustLeft( -nHalfLineWidth );
364 aActionBounds.AdjustTop( -nHalfLineWidth );
365 aActionBounds.AdjustRight(nHalfLineWidth );
366 aActionBounds.AdjustBottom(nHalfLineWidth );
368 break;
371 case MetaActionType::RECT:
372 aActionBounds = static_cast<const MetaRectAction&>(rAct).GetRect();
373 break;
375 case MetaActionType::ROUNDRECT:
376 aActionBounds = tools::Polygon( static_cast<const MetaRoundRectAction&>(rAct).GetRect(),
377 static_cast<const MetaRoundRectAction&>(rAct).GetHorzRound(),
378 static_cast<const MetaRoundRectAction&>(rAct).GetVertRound() ).GetBoundRect();
379 break;
381 case MetaActionType::ELLIPSE:
383 const tools::Rectangle& rRect = static_cast<const MetaEllipseAction&>(rAct).GetRect();
384 aActionBounds = tools::Polygon( rRect.Center(),
385 rRect.GetWidth() >> 1,
386 rRect.GetHeight() >> 1 ).GetBoundRect();
387 break;
390 case MetaActionType::ARC:
391 aActionBounds = tools::Polygon( static_cast<const MetaArcAction&>(rAct).GetRect(),
392 static_cast<const MetaArcAction&>(rAct).GetStartPoint(),
393 static_cast<const MetaArcAction&>(rAct).GetEndPoint(), PolyStyle::Arc ).GetBoundRect();
394 break;
396 case MetaActionType::PIE:
397 aActionBounds = tools::Polygon( static_cast<const MetaPieAction&>(rAct).GetRect(),
398 static_cast<const MetaPieAction&>(rAct).GetStartPoint(),
399 static_cast<const MetaPieAction&>(rAct).GetEndPoint(), PolyStyle::Pie ).GetBoundRect();
400 break;
402 case MetaActionType::CHORD:
403 aActionBounds = tools::Polygon( static_cast<const MetaChordAction&>(rAct).GetRect(),
404 static_cast<const MetaChordAction&>(rAct).GetStartPoint(),
405 static_cast<const MetaChordAction&>(rAct).GetEndPoint(), PolyStyle::Chord ).GetBoundRect();
406 break;
408 case MetaActionType::POLYLINE:
410 const MetaPolyLineAction& rMetaPolyLineAction = static_cast<const MetaPolyLineAction&>(rAct);
411 aActionBounds = rMetaPolyLineAction.GetPolygon().GetBoundRect();
412 const tools::Long nLineWidth(rMetaPolyLineAction.GetLineInfo().GetWidth());
413 if(nLineWidth)
415 const tools::Long nHalfLineWidth((nLineWidth + 1) / 2);
416 aActionBounds.AdjustLeft( -nHalfLineWidth );
417 aActionBounds.AdjustTop( -nHalfLineWidth );
418 aActionBounds.AdjustRight(nHalfLineWidth );
419 aActionBounds.AdjustBottom(nHalfLineWidth );
421 break;
424 case MetaActionType::POLYGON:
425 aActionBounds = static_cast<const MetaPolygonAction&>(rAct).GetPolygon().GetBoundRect();
426 break;
428 case MetaActionType::POLYPOLYGON:
429 aActionBounds = static_cast<const MetaPolyPolygonAction&>(rAct).GetPolyPolygon().GetBoundRect();
430 break;
432 case MetaActionType::BMP:
433 aActionBounds = tools::Rectangle( static_cast<const MetaBmpAction&>(rAct).GetPoint(),
434 rOut.PixelToLogic( static_cast<const MetaBmpAction&>(rAct).GetBitmap().GetSizePixel() ) );
435 break;
437 case MetaActionType::BMPSCALE:
438 aActionBounds = tools::Rectangle( static_cast<const MetaBmpScaleAction&>(rAct).GetPoint(),
439 static_cast<const MetaBmpScaleAction&>(rAct).GetSize() );
440 break;
442 case MetaActionType::BMPSCALEPART:
443 aActionBounds = tools::Rectangle( static_cast<const MetaBmpScalePartAction&>(rAct).GetDestPoint(),
444 static_cast<const MetaBmpScalePartAction&>(rAct).GetDestSize() );
445 break;
447 case MetaActionType::BMPEX:
448 aActionBounds = tools::Rectangle( static_cast<const MetaBmpExAction&>(rAct).GetPoint(),
449 rOut.PixelToLogic( static_cast<const MetaBmpExAction&>(rAct).GetBitmapEx().GetSizePixel() ) );
450 break;
452 case MetaActionType::BMPEXSCALE:
453 aActionBounds = tools::Rectangle( static_cast<const MetaBmpExScaleAction&>(rAct).GetPoint(),
454 static_cast<const MetaBmpExScaleAction&>(rAct).GetSize() );
455 break;
457 case MetaActionType::BMPEXSCALEPART:
458 aActionBounds = tools::Rectangle( static_cast<const MetaBmpExScalePartAction&>(rAct).GetDestPoint(),
459 static_cast<const MetaBmpExScalePartAction&>(rAct).GetDestSize() );
460 break;
462 case MetaActionType::MASK:
463 aActionBounds = tools::Rectangle( static_cast<const MetaMaskAction&>(rAct).GetPoint(),
464 rOut.PixelToLogic( static_cast<const MetaMaskAction&>(rAct).GetBitmap().GetSizePixel() ) );
465 break;
467 case MetaActionType::MASKSCALE:
468 aActionBounds = tools::Rectangle( static_cast<const MetaMaskScaleAction&>(rAct).GetPoint(),
469 static_cast<const MetaMaskScaleAction&>(rAct).GetSize() );
470 break;
472 case MetaActionType::MASKSCALEPART:
473 aActionBounds = tools::Rectangle( static_cast<const MetaMaskScalePartAction&>(rAct).GetDestPoint(),
474 static_cast<const MetaMaskScalePartAction&>(rAct).GetDestSize() );
475 break;
477 case MetaActionType::GRADIENT:
478 aActionBounds = static_cast<const MetaGradientAction&>(rAct).GetRect();
479 break;
481 case MetaActionType::GRADIENTEX:
482 aActionBounds = static_cast<const MetaGradientExAction&>(rAct).GetPolyPolygon().GetBoundRect();
483 break;
485 case MetaActionType::HATCH:
486 aActionBounds = static_cast<const MetaHatchAction&>(rAct).GetPolyPolygon().GetBoundRect();
487 break;
489 case MetaActionType::WALLPAPER:
490 aActionBounds = static_cast<const MetaWallpaperAction&>(rAct).GetRect();
491 break;
493 case MetaActionType::Transparent:
494 aActionBounds = static_cast<const MetaTransparentAction&>(rAct).GetPolyPolygon().GetBoundRect();
495 break;
497 case MetaActionType::FLOATTRANSPARENT:
498 aActionBounds = tools::Rectangle( static_cast<const MetaFloatTransparentAction&>(rAct).GetPoint(),
499 static_cast<const MetaFloatTransparentAction&>(rAct).GetSize() );
500 break;
502 case MetaActionType::EPS:
503 aActionBounds = tools::Rectangle( static_cast<const MetaEPSAction&>(rAct).GetPoint(),
504 static_cast<const MetaEPSAction&>(rAct).GetSize() );
505 break;
507 case MetaActionType::TEXT:
509 const MetaTextAction& rTextAct = static_cast<const MetaTextAction&>(rAct);
510 const OUString aString( rTextAct.GetText().copy(rTextAct.GetIndex(), rTextAct.GetLen()) );
512 if (!aString.isEmpty())
514 const Point aPtLog( rTextAct.GetPoint() );
516 // #105987# Use API method instead of Impl* methods
517 // #107490# Set base parameter equal to index parameter
518 rOut.GetTextBoundRect( aActionBounds, rTextAct.GetText(), rTextAct.GetIndex(),
519 rTextAct.GetIndex(), rTextAct.GetLen() );
520 aActionBounds.Move( aPtLog.X(), aPtLog.Y() );
523 break;
525 case MetaActionType::TEXTARRAY:
527 const MetaTextArrayAction& rTextAct = static_cast<const MetaTextArrayAction&>(rAct);
528 const OUString aString( rTextAct.GetText().copy(rTextAct.GetIndex(), rTextAct.GetLen()) );
530 if( !aString.isEmpty() )
532 // #105987# ImplLayout takes everything in logical coordinates
533 std::unique_ptr<SalLayout> pSalLayout = rOut.ImplLayout( rTextAct.GetText(), rTextAct.GetIndex(),
534 rTextAct.GetLen(), rTextAct.GetPoint(),
535 0, rTextAct.GetDXArray() );
536 if( pSalLayout )
538 tools::Rectangle aBoundRect( const_cast<OutputDevice&>(rOut).ImplGetTextBoundRect( *pSalLayout ) );
539 aActionBounds = rOut.PixelToLogic( aBoundRect );
543 break;
545 case MetaActionType::TEXTRECT:
546 aActionBounds = static_cast<const MetaTextRectAction&>(rAct).GetRect();
547 break;
549 case MetaActionType::STRETCHTEXT:
551 const MetaStretchTextAction& rTextAct = static_cast<const MetaStretchTextAction&>(rAct);
552 const OUString aString( rTextAct.GetText().copy(rTextAct.GetIndex(), rTextAct.GetLen()) );
554 // #i16195# Literate copy from TextArray action, the
555 // semantics for the ImplLayout call are copied from the
556 // OutDev::DrawStretchText() code. Unfortunately, also in
557 // this case, public outdev methods such as GetTextWidth()
558 // don't provide enough info.
559 if( !aString.isEmpty() )
561 // #105987# ImplLayout takes everything in logical coordinates
562 std::unique_ptr<SalLayout> pSalLayout = rOut.ImplLayout( rTextAct.GetText(), rTextAct.GetIndex(),
563 rTextAct.GetLen(), rTextAct.GetPoint(),
564 rTextAct.GetWidth() );
565 if( pSalLayout )
567 tools::Rectangle aBoundRect( const_cast<OutputDevice&>(rOut).ImplGetTextBoundRect( *pSalLayout ) );
568 aActionBounds = rOut.PixelToLogic( aBoundRect );
572 break;
574 case MetaActionType::TEXTLINE:
575 OSL_FAIL("MetaActionType::TEXTLINE not supported");
576 break;
578 default:
579 break;
582 if( !aActionBounds.IsEmpty() )
584 // fdo#40421 limit current action's output to clipped area
585 if( rOut.IsClipRegion() )
586 return rOut.LogicToPixel(
587 rOut.GetClipRegion().GetBoundRect().Intersection( aActionBounds ) );
588 else
589 return rOut.LogicToPixel( aActionBounds );
591 else
592 return tools::Rectangle();
595 } // end anon namespace
597 bool OutputDevice::RemoveTransparenciesFromMetaFile( const GDIMetaFile& rInMtf, GDIMetaFile& rOutMtf,
598 tools::Long nMaxBmpDPIX, tools::Long nMaxBmpDPIY,
599 bool bReduceTransparency, bool bTransparencyAutoMode,
600 bool bDownsampleBitmaps,
601 const Color& rBackground
604 MetaAction* pCurrAct;
605 bool bTransparent( false );
607 rOutMtf.Clear();
609 if(!bReduceTransparency || bTransparencyAutoMode)
610 bTransparent = rInMtf.HasTransparentActions();
612 // #i10613# Determine set of connected components containing transparent objects. These are
613 // then processed as bitmaps, the original actions are removed from the metafile.
614 if( !bTransparent )
616 // nothing transparent -> just copy
617 rOutMtf = rInMtf;
619 else
621 // #i10613#
622 // This works as follows: we want a number of distinct sets of
623 // connected components, where each set contains metafile
624 // actions that are intersecting (note: there are possibly
625 // more actions contained as are directly intersecting,
626 // because we can only produce rectangular bitmaps later
627 // on. Thus, each set of connected components is the smallest
628 // enclosing, axis-aligned rectangle that completely bounds a
629 // number of intersecting metafile actions, plus any action
630 // that would otherwise be cut in two). Therefore, we
631 // iteratively add metafile actions from the original metafile
632 // to this connected components list (aCCList), by checking
633 // each element's bounding box against intersection with the
634 // metaaction at hand.
635 // All those intersecting elements are removed from aCCList
636 // and collected in a temporary list (aCCMergeList). After all
637 // elements have been checked, the aCCMergeList elements are
638 // merged with the metaaction at hand into one resulting
639 // connected component, with one big bounding box, and
640 // inserted into aCCList again.
641 // The time complexity of this algorithm is O(n^3), where n is
642 // the number of metafile actions, and it finds all distinct
643 // regions of rectangle-bounded connected components. This
644 // algorithm was designed by AF.
646 // STAGE 1: Detect background
648 // Receives uniform background content, and is _not_ merged
649 // nor checked for intersection against other aCCList elements
650 ConnectedComponents aBackgroundComponent;
652 // Read the configuration value of minimal object area where transparency will be removed
653 double fReduceTransparencyMinArea = officecfg::Office::Common::VCL::ReduceTransparencyMinArea::get() / 100.0;
654 SAL_WARN_IF(fReduceTransparencyMinArea > 1.0, "vcl",
655 "Value of ReduceTransparencyMinArea config option is too high");
656 SAL_WARN_IF(fReduceTransparencyMinArea < 0.0, "vcl",
657 "Value of ReduceTransparencyMinArea config option is too low");
658 fReduceTransparencyMinArea = std::clamp(fReduceTransparencyMinArea, 0.0, 1.0);
660 // create an OutputDevice to record mapmode changes and the like
661 ScopedVclPtrInstance< VirtualDevice > aMapModeVDev;
662 aMapModeVDev->mnDPIX = mnDPIX;
663 aMapModeVDev->mnDPIY = mnDPIY;
664 aMapModeVDev->EnableOutput(false);
666 // weed out page-filling background objects (if they are
667 // uniformly coloured). Keeping them outside the other
668 // connected components often prevents whole-page bitmap
669 // generation.
670 bool bStillBackground=true; // true until first non-bg action
671 int nActionNum = 0, nLastBgAction = -1;
672 pCurrAct=const_cast<GDIMetaFile&>(rInMtf).FirstAction();
673 if( rBackground != COL_TRANSPARENT )
675 aBackgroundComponent.aBgColor = rBackground;
676 aBackgroundComponent.aBounds = GetBackgroundComponentBounds();
678 while( pCurrAct && bStillBackground )
680 switch( pCurrAct->GetType() )
682 case MetaActionType::RECT:
684 if( !checkRect(
685 aBackgroundComponent.aBounds,
686 aBackgroundComponent.aBgColor,
687 static_cast<const MetaRectAction*>(pCurrAct)->GetRect(),
688 *aMapModeVDev) )
689 bStillBackground=false; // incomplete occlusion of background
690 else
691 nLastBgAction=nActionNum; // this _is_ background
692 break;
694 case MetaActionType::POLYGON:
696 const tools::Polygon aPoly(
697 static_cast<const MetaPolygonAction*>(pCurrAct)->GetPolygon());
698 if( !basegfx::utils::isRectangle(
699 aPoly.getB2DPolygon()) ||
700 !checkRect(
701 aBackgroundComponent.aBounds,
702 aBackgroundComponent.aBgColor,
703 aPoly.GetBoundRect(),
704 *aMapModeVDev) )
705 bStillBackground=false; // incomplete occlusion of background
706 else
707 nLastBgAction=nActionNum; // this _is_ background
708 break;
710 case MetaActionType::POLYPOLYGON:
712 const tools::PolyPolygon aPoly(
713 static_cast<const MetaPolyPolygonAction*>(pCurrAct)->GetPolyPolygon());
714 if( aPoly.Count() != 1 ||
715 !basegfx::utils::isRectangle(
716 aPoly[0].getB2DPolygon()) ||
717 !checkRect(
718 aBackgroundComponent.aBounds,
719 aBackgroundComponent.aBgColor,
720 aPoly.GetBoundRect(),
721 *aMapModeVDev) )
722 bStillBackground=false; // incomplete occlusion of background
723 else
724 nLastBgAction=nActionNum; // this _is_ background
725 break;
727 case MetaActionType::WALLPAPER:
729 if( !checkRect(
730 aBackgroundComponent.aBounds,
731 aBackgroundComponent.aBgColor,
732 static_cast<const MetaWallpaperAction*>(pCurrAct)->GetRect(),
733 *aMapModeVDev) )
734 bStillBackground=false; // incomplete occlusion of background
735 else
736 nLastBgAction=nActionNum; // this _is_ background
737 break;
739 default:
741 if( ImplIsNotTransparent( *pCurrAct,
742 *aMapModeVDev ) )
743 bStillBackground=false; // non-transparent action, possibly
744 // not uniform
745 else
746 // extend current bounds (next uniform action
747 // needs to fully cover this area)
748 aBackgroundComponent.aBounds.Union(
749 ImplCalcActionBounds(*pCurrAct, *aMapModeVDev) );
750 break;
754 // execute action to get correct MapModes etc.
755 pCurrAct->Execute( aMapModeVDev.get() );
757 pCurrAct=const_cast<GDIMetaFile&>(rInMtf).NextAction();
758 ++nActionNum;
761 if (nLastBgAction != -1)
763 size_t nActionSize = rInMtf.GetActionSize();
764 // tdf#134736 move nLastBgAction to also include any trailing pops
765 for (size_t nPostLastBgAction = nLastBgAction + 1; nPostLastBgAction < nActionSize; ++nPostLastBgAction)
767 if (rInMtf.GetAction(nPostLastBgAction)->GetType() != MetaActionType::POP)
768 break;
769 nLastBgAction = nPostLastBgAction;
773 aMapModeVDev->ClearStack(); // clean up aMapModeVDev
775 // fast-forward until one after the last background action
776 // (need to reconstruct map mode vdev state)
777 nActionNum=0;
778 pCurrAct=const_cast<GDIMetaFile&>(rInMtf).FirstAction();
779 while( pCurrAct && nActionNum<=nLastBgAction )
781 // up to and including last ink-generating background
782 // action go to background component
783 aBackgroundComponent.aComponentList.emplace_back(
784 pCurrAct, nActionNum );
786 // execute action to get correct MapModes etc.
787 pCurrAct->Execute( aMapModeVDev.get() );
788 pCurrAct=const_cast<GDIMetaFile&>(rInMtf).NextAction();
789 ++nActionNum;
792 // STAGE 2: Generate connected components list
794 ::std::vector<ConnectedComponents> aCCList; // contains distinct sets of connected components as elements.
796 // iterate over all actions (start where background action
797 // search left off)
798 for( ;
799 pCurrAct;
800 pCurrAct=const_cast<GDIMetaFile&>(rInMtf).NextAction(), ++nActionNum )
802 // execute action to get correct MapModes etc.
803 pCurrAct->Execute( aMapModeVDev.get() );
805 // cache bounds of current action
806 const tools::Rectangle aBBCurrAct( ImplCalcActionBounds(*pCurrAct, *aMapModeVDev) );
808 // accumulate collected bounds here, initialize with current action
809 tools::Rectangle aTotalBounds( aBBCurrAct ); // thus, aTotalComponents.aBounds is empty
810 // for non-output-generating actions
811 bool bTreatSpecial( false );
812 ConnectedComponents aTotalComponents;
814 // STAGE 2.1: Search for intersecting cc entries
816 // if aBBCurrAct is empty, it will intersect with no
817 // aCCList member. Thus, we can save the check.
818 // Furthermore, this ensures that non-output-generating
819 // actions get their own aCCList entry, which is necessary
820 // when copying them to the output metafile (see stage 4
821 // below).
823 // #107169# Wholly transparent objects need
824 // not be considered for connected components,
825 // too. Just put each of them into a separate
826 // component.
827 aTotalComponents.bIsFullyTransparent = !ImplIsNotTransparent(*pCurrAct, *aMapModeVDev);
829 if( !aBBCurrAct.IsEmpty() &&
830 !aTotalComponents.bIsFullyTransparent )
832 if( !aBackgroundComponent.aComponentList.empty() &&
833 !aBackgroundComponent.aBounds.IsInside(aTotalBounds) )
835 // it seems the background is not large enough. to
836 // be on the safe side, combine with this component.
837 aTotalBounds.Union( aBackgroundComponent.aBounds );
839 // extract all aCurr actions to aTotalComponents
840 aTotalComponents.aComponentList.splice( aTotalComponents.aComponentList.end(),
841 aBackgroundComponent.aComponentList );
843 if( aBackgroundComponent.bIsSpecial )
844 bTreatSpecial = true;
847 bool bSomeComponentsChanged;
849 // now, this is unfortunate: since changing anyone of
850 // the aCCList elements (e.g. by merging or addition
851 // of an action) might generate new intersection with
852 // other aCCList elements, have to repeat the whole
853 // element scanning, until nothing changes anymore.
854 // Thus, this loop here makes us O(n^3) in the worst
855 // case.
858 // only loop here if 'intersects' branch below was hit
859 bSomeComponentsChanged = false;
861 // iterate over all current members of aCCList
862 for( auto aCurrCC=aCCList.begin(); aCurrCC != aCCList.end(); )
864 // first check if current element's bounds are
865 // empty. This ensures that empty actions are not
866 // merged into one component, as a matter of fact,
867 // they have no position.
869 // #107169# Wholly transparent objects need
870 // not be considered for connected components,
871 // too. Just put each of them into a separate
872 // component.
873 if( !aCurrCC->aBounds.IsEmpty() &&
874 !aCurrCC->bIsFullyTransparent &&
875 aCurrCC->aBounds.IsOver( aTotalBounds ) )
877 // union the intersecting aCCList element into aTotalComponents
879 // calc union bounding box
880 aTotalBounds.Union( aCurrCC->aBounds );
882 // extract all aCurr actions to aTotalComponents
883 aTotalComponents.aComponentList.splice( aTotalComponents.aComponentList.end(),
884 aCurrCC->aComponentList );
886 if( aCurrCC->bIsSpecial )
887 bTreatSpecial = true;
889 // remove and delete aCurrCC element from list (we've now merged its content)
890 aCurrCC = aCCList.erase( aCurrCC );
892 // at least one component changed, need to rescan everything
893 bSomeComponentsChanged = true;
895 else
897 ++aCurrCC;
901 while( bSomeComponentsChanged );
904 // STAGE 2.2: Determine special state for cc element
906 // now test whether the whole connected component must be
907 // treated specially (i.e. rendered as a bitmap): if the
908 // added action is the very first action, or all actions
909 // before it are completely transparent, the connected
910 // component need not be treated specially, not even if
911 // the added action contains transparency. This is because
912 // painting of transparent objects on _white background_
913 // works without alpha compositing (you just calculate the
914 // color). Note that for the test "all objects before me
915 // are transparent" no sorting is necessary, since the
916 // added metaaction pCurrAct is always in the order the
917 // metafile is painted. Generally, the order of the
918 // metaactions in the ConnectedComponents are not
919 // guaranteed to be the same as in the metafile.
920 if( bTreatSpecial )
922 // prev component(s) special -> this one, too
923 aTotalComponents.bIsSpecial = true;
925 else if(!pCurrAct->IsTransparent())
927 // added action and none of prev components special ->
928 // this one normal, too
929 aTotalComponents.bIsSpecial = false;
931 else
933 // added action is special and none of prev components
934 // special -> do the detailed tests
936 // can the action handle transparency correctly
937 // (i.e. when painted on white background, does the
938 // action still look correct)?
939 if( !DoesActionHandleTransparency( *pCurrAct ) )
941 // no, action cannot handle its transparency on
942 // a printer device, render to bitmap
943 aTotalComponents.bIsSpecial = true;
945 else
947 // yes, action can handle its transparency, so
948 // check whether we're on white background
949 if( aTotalComponents.aComponentList.empty() )
951 // nothing between pCurrAct and page
952 // background -> don't be special
953 aTotalComponents.bIsSpecial = false;
955 else
957 // #107169# Fixes above now ensure that _no_
958 // object in the list is fully transparent. Thus,
959 // if the component list is not empty above, we
960 // must assume that we have to treat this
961 // component special.
963 // there are non-transparent objects between
964 // pCurrAct and the empty sheet of paper -> be
965 // special, then
966 aTotalComponents.bIsSpecial = true;
971 // STAGE 2.3: Add newly generated CC list element
973 // set new bounds and add action to list
974 aTotalComponents.aBounds = aTotalBounds;
975 aTotalComponents.aComponentList.emplace_back(
976 pCurrAct, nActionNum );
978 // add aTotalComponents as a new entry to aCCList
979 aCCList.push_back( aTotalComponents );
981 SAL_WARN_IF( aTotalComponents.aComponentList.empty(), "vcl",
982 "Printer::GetPreparedMetaFile empty component" );
983 SAL_WARN_IF( aTotalComponents.aBounds.IsEmpty() && (aTotalComponents.aComponentList.size() != 1), "vcl",
984 "Printer::GetPreparedMetaFile non-output generating actions must be solitary");
985 SAL_WARN_IF( aTotalComponents.bIsFullyTransparent && (aTotalComponents.aComponentList.size() != 1), "vcl",
986 "Printer::GetPreparedMetaFile fully transparent actions must be solitary");
989 // well now, we've got the list of disjunct connected
990 // components. Now we've got to create a map, which contains
991 // the corresponding aCCList element for every
992 // metaaction. Later on, we always process the complete
993 // metafile for each bitmap to be generated, but switch on
994 // output only for actions contained in the then current
995 // aCCList element. This ensures correct mapmode and attribute
996 // settings for all cases.
998 // maps mtf actions to CC list entries
999 ::std::vector< const ConnectedComponents* > aCCList_MemberMap( rInMtf.GetActionSize() );
1001 // iterate over all aCCList members and their contained metaactions
1002 for (auto const& currentItem : aCCList)
1004 for (auto const& currentAction : currentItem.aComponentList)
1006 // set pointer to aCCList element for corresponding index
1007 aCCList_MemberMap[ currentAction.second ] = &currentItem;
1011 // STAGE 3.1: Output background mtf actions (if there are any)
1013 for (auto & component : aBackgroundComponent.aComponentList)
1015 // simply add this action (above, we inserted the actions
1016 // starting at index 0 up to and including nLastBgAction)
1017 rOutMtf.AddAction( component.first );
1020 // STAGE 3.2: Generate banded bitmaps for special regions
1022 Point aPageOffset;
1023 Size aTmpSize( GetOutputSizePixel() );
1024 if( meOutDevType == OUTDEV_PDF )
1026 auto pPdfWriter = static_cast<vcl::PDFWriterImpl*>(this);
1027 aTmpSize = LogicToPixel(pPdfWriter->getCurPageSize(), MapMode(MapUnit::MapPoint));
1029 // also add error code to PDFWriter
1030 pPdfWriter->insertError(vcl::PDFWriter::Warning_Transparency_Converted);
1032 else if( meOutDevType == OUTDEV_PRINTER )
1034 Printer* pThis = dynamic_cast<Printer*>(this);
1035 assert(pThis);
1036 aPageOffset = pThis->GetPageOffsetPixel();
1037 aPageOffset = Point( 0, 0 ) - aPageOffset;
1038 aTmpSize = pThis->GetPaperSizePixel();
1040 const tools::Rectangle aOutputRect( aPageOffset, aTmpSize );
1041 bool bTiling = dynamic_cast<Printer*>(this) != nullptr;
1043 // iterate over all aCCList members and generate bitmaps for the special ones
1044 for (auto & currentItem : aCCList)
1046 if( currentItem.bIsSpecial )
1048 tools::Rectangle aBoundRect( currentItem.aBounds );
1049 aBoundRect.Intersection( aOutputRect );
1051 const double fBmpArea( static_cast<double>(aBoundRect.GetWidth()) * aBoundRect.GetHeight() );
1052 const double fOutArea( static_cast<double>(aOutputRect.GetWidth()) * aOutputRect.GetHeight() );
1054 // check if output doesn't exceed given size
1055 if( bReduceTransparency && bTransparencyAutoMode && ( fBmpArea > ( fReduceTransparencyMinArea * fOutArea ) ) )
1057 // output normally. Therefore, we simply clear the
1058 // special attribute, as everything non-special is
1059 // copied to rOutMtf further below.
1060 currentItem.bIsSpecial = false;
1062 else
1064 // create new bitmap action first
1065 if( aBoundRect.GetWidth() && aBoundRect.GetHeight() )
1067 Point aDstPtPix( aBoundRect.TopLeft() );
1068 Size aDstSzPix;
1070 ScopedVclPtrInstance<VirtualDevice> aMapVDev; // here, we record only mapmode information
1071 aMapVDev->EnableOutput(false);
1073 ScopedVclPtrInstance<VirtualDevice> aPaintVDev; // into this one, we render.
1074 aPaintVDev->SetBackground( aBackgroundComponent.aBgColor );
1076 rOutMtf.AddAction( new MetaPushAction( PushFlags::MAPMODE ) );
1077 rOutMtf.AddAction( new MetaMapModeAction() );
1079 aPaintVDev->SetDrawMode( GetDrawMode() );
1081 while( aDstPtPix.Y() <= aBoundRect.Bottom() )
1083 aDstPtPix.setX( aBoundRect.Left() );
1084 aDstSzPix = bTiling ? Size( MAX_TILE_WIDTH, MAX_TILE_HEIGHT ) : aBoundRect.GetSize();
1086 if( ( aDstPtPix.Y() + aDstSzPix.Height() - 1 ) > aBoundRect.Bottom() )
1087 aDstSzPix.setHeight( aBoundRect.Bottom() - aDstPtPix.Y() + 1 );
1089 while( aDstPtPix.X() <= aBoundRect.Right() )
1091 if( ( aDstPtPix.X() + aDstSzPix.Width() - 1 ) > aBoundRect.Right() )
1092 aDstSzPix.setWidth( aBoundRect.Right() - aDstPtPix.X() + 1 );
1094 if( !tools::Rectangle( aDstPtPix, aDstSzPix ).Intersection( aBoundRect ).IsEmpty() &&
1095 aPaintVDev->SetOutputSizePixel( aDstSzPix ) )
1097 aPaintVDev->Push();
1098 aMapVDev->Push();
1100 aMapVDev->mnDPIX = aPaintVDev->mnDPIX = mnDPIX;
1101 aMapVDev->mnDPIY = aPaintVDev->mnDPIY = mnDPIY;
1103 aPaintVDev->EnableOutput(false);
1105 // iterate over all actions
1106 for( pCurrAct=const_cast<GDIMetaFile&>(rInMtf).FirstAction(), nActionNum=0;
1107 pCurrAct;
1108 pCurrAct=const_cast<GDIMetaFile&>(rInMtf).NextAction(), ++nActionNum )
1110 // enable output only for
1111 // actions that are members of
1112 // the current aCCList element
1113 // (currentItem)
1114 if( aCCList_MemberMap[nActionNum] == &currentItem )
1115 aPaintVDev->EnableOutput();
1117 // but process every action
1118 const MetaActionType nType( pCurrAct->GetType() );
1120 if( MetaActionType::MAPMODE == nType )
1122 pCurrAct->Execute( aMapVDev.get() );
1124 MapMode aMtfMap( aMapVDev->GetMapMode() );
1125 const Point aNewOrg( aMapVDev->PixelToLogic( aDstPtPix ) );
1127 aMtfMap.SetOrigin( Point( -aNewOrg.X(), -aNewOrg.Y() ) );
1128 aPaintVDev->SetMapMode( aMtfMap );
1130 else if( ( MetaActionType::PUSH == nType ) || MetaActionType::POP == nType )
1132 pCurrAct->Execute( aMapVDev.get() );
1133 pCurrAct->Execute( aPaintVDev.get() );
1135 else if( MetaActionType::GRADIENT == nType )
1137 MetaGradientAction* pGradientAction = static_cast<MetaGradientAction*>(pCurrAct);
1138 Printer* pPrinter = dynamic_cast< Printer* >(this);
1139 if( pPrinter )
1140 pPrinter->DrawGradientEx( aPaintVDev.get(), pGradientAction->GetRect(), pGradientAction->GetGradient() );
1141 else
1142 DrawGradient( pGradientAction->GetRect(), pGradientAction->GetGradient() );
1144 else
1146 pCurrAct->Execute( aPaintVDev.get() );
1149 Application::Reschedule( true );
1152 const bool bOldMap = mbMap;
1153 mbMap = aPaintVDev->mbMap = false;
1155 Bitmap aBandBmp( aPaintVDev->GetBitmap( Point(), aDstSzPix ) );
1157 // scale down bitmap, if requested
1158 if( bDownsampleBitmaps )
1160 aBandBmp = GetDownsampledBitmap( aDstSzPix,
1161 Point(), aBandBmp.GetSizePixel(),
1162 aBandBmp, nMaxBmpDPIX, nMaxBmpDPIY );
1165 rOutMtf.AddAction( new MetaCommentAction( "PRNSPOOL_TRANSPARENTBITMAP_BEGIN" ) );
1166 rOutMtf.AddAction( new MetaBmpScaleAction( aDstPtPix, aDstSzPix, aBandBmp ) );
1167 rOutMtf.AddAction( new MetaCommentAction( "PRNSPOOL_TRANSPARENTBITMAP_END" ) );
1169 aPaintVDev->mbMap = true;
1170 mbMap = bOldMap;
1171 aMapVDev->Pop();
1172 aPaintVDev->Pop();
1175 // overlapping bands to avoid missing lines (e.g. PostScript)
1176 aDstPtPix.AdjustX(aDstSzPix.Width() );
1179 // overlapping bands to avoid missing lines (e.g. PostScript)
1180 aDstPtPix.AdjustY(aDstSzPix.Height() );
1183 rOutMtf.AddAction( new MetaPopAction() );
1189 aMapModeVDev->ClearStack(); // clean up aMapModeVDev
1191 // STAGE 4: Copy actions to output metafile
1193 // iterate over all actions and duplicate the ones not in a
1194 // special aCCList member into rOutMtf
1195 for( pCurrAct=const_cast<GDIMetaFile&>(rInMtf).FirstAction(), nActionNum=0;
1196 pCurrAct;
1197 pCurrAct=const_cast<GDIMetaFile&>(rInMtf).NextAction(), ++nActionNum )
1199 const ConnectedComponents* pCurrAssociatedComponent = aCCList_MemberMap[nActionNum];
1201 // NOTE: This relies on the fact that map-mode or draw
1202 // mode changing actions are solitary aCCList elements and
1203 // have empty bounding boxes, see comment on stage 2.1
1204 // above
1205 if( pCurrAssociatedComponent &&
1206 (pCurrAssociatedComponent->aBounds.IsEmpty() ||
1207 !pCurrAssociatedComponent->bIsSpecial) )
1209 // #107169# Treat transparent bitmaps special, if they
1210 // are the first (or sole) action in their bounds
1211 // list. Note that we previously ensured that no
1212 // fully-transparent objects are before us here.
1213 if( DoesActionHandleTransparency( *pCurrAct ) &&
1214 pCurrAssociatedComponent->aComponentList.begin()->first == pCurrAct )
1216 // convert actions, where masked-out parts are of
1217 // given background color
1218 ImplConvertTransparentAction(rOutMtf,
1219 *pCurrAct,
1220 *aMapModeVDev,
1221 aBackgroundComponent.aBgColor);
1223 else
1225 // simply add this action
1226 rOutMtf.AddAction( pCurrAct );
1229 pCurrAct->Execute(aMapModeVDev.get());
1233 rOutMtf.SetPrefMapMode( rInMtf.GetPrefMapMode() );
1234 rOutMtf.SetPrefSize( rInMtf.GetPrefSize() );
1236 #if OSL_DEBUG_LEVEL > 1
1237 // iterate over all aCCList members and generate rectangles for the bounding boxes
1238 rOutMtf.AddAction( new MetaFillColorAction( COL_WHITE, false ) );
1239 for(auto const& aCurr:aCCList)
1241 if( aCurr.bIsSpecial )
1242 rOutMtf.AddAction( new MetaLineColorAction( COL_RED, true) );
1243 else
1244 rOutMtf.AddAction( new MetaLineColorAction( COL_BLUE, true) );
1246 rOutMtf.AddAction( new MetaRectAction( aMapModeVDev->PixelToLogic( aCurr.aBounds ) ) );
1248 #endif
1250 return bTransparent;
1253 void Printer::DrawGradientEx( OutputDevice* pOut, const tools::Rectangle& rRect, const Gradient& rGradient )
1255 const PrinterOptions& rPrinterOptions = GetPrinterOptions();
1257 if( rPrinterOptions.IsReduceGradients() )
1259 if( PrinterGradientMode::Stripes == rPrinterOptions.GetReducedGradientMode() )
1261 if( !rGradient.GetSteps() || ( rGradient.GetSteps() > rPrinterOptions.GetReducedGradientStepCount() ) )
1263 Gradient aNewGradient( rGradient );
1265 aNewGradient.SetSteps( rPrinterOptions.GetReducedGradientStepCount() );
1266 pOut->DrawGradient( rRect, aNewGradient );
1268 else
1269 pOut->DrawGradient( rRect, rGradient );
1271 else
1273 const Color& rStartColor = rGradient.GetStartColor();
1274 const Color& rEndColor = rGradient.GetEndColor();
1275 const tools::Long nR = ( ( static_cast<tools::Long>(rStartColor.GetRed()) * rGradient.GetStartIntensity() ) / 100 +
1276 ( static_cast<tools::Long>(rEndColor.GetRed()) * rGradient.GetEndIntensity() ) / 100 ) >> 1;
1277 const tools::Long nG = ( ( static_cast<tools::Long>(rStartColor.GetGreen()) * rGradient.GetStartIntensity() ) / 100 +
1278 ( static_cast<tools::Long>(rEndColor.GetGreen()) * rGradient.GetEndIntensity() ) / 100 ) >> 1;
1279 const tools::Long nB = ( ( static_cast<tools::Long>(rStartColor.GetBlue()) * rGradient.GetStartIntensity() ) / 100 +
1280 ( static_cast<tools::Long>(rEndColor.GetBlue()) * rGradient.GetEndIntensity() ) / 100 ) >> 1;
1281 const Color aColor( static_cast<sal_uInt8>(nR), static_cast<sal_uInt8>(nG), static_cast<sal_uInt8>(nB) );
1283 pOut->Push( PushFlags::LINECOLOR | PushFlags::FILLCOLOR );
1284 pOut->SetLineColor( aColor );
1285 pOut->SetFillColor( aColor );
1286 pOut->DrawRect( rRect );
1287 pOut->Pop();
1290 else
1291 pOut->DrawGradient( rRect, rGradient );
1294 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */