1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 .
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
44 // List of (intersecting) actions, plus overall bounds
45 struct ConnectedComponents
47 ConnectedComponents() :
52 bIsFullyTransparent(false)
55 ::std::list
< Component
> aComponentList
;
56 tools::Rectangle aBounds
;
59 bool bIsFullyTransparent
;
66 /** Determines whether the action can handle transparency correctly
67 (i.e. when painted on white background, does the action still look
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
79 switch( rAct
.GetType() )
81 case MetaActionType::Transparent
:
82 case MetaActionType::BMPEX
:
83 case MetaActionType::BMPEXSCALE
:
84 case MetaActionType::BMPEXSCALEPART
:
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
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
,
108 const tools::Rectangle
& rCurrRect
,
109 OutputDevice
const & rMapModeVDev
)
111 bool bRet
= doesRectCoverWithUniformColor(io_rPrevRect
, rCurrRect
, rMapModeVDev
);
115 io_rPrevRect
= rCurrRect
;
116 o_rBgColor
= rMapModeVDev
.GetFillColor();
122 /** #107169# Convert BitmapEx to Bitmap with appropriately blended
123 color. Convert MetaTransparentAction to plain polygon,
124 appropriately colored
127 Add converted actions to this metafile
129 void ImplConvertTransparentAction( GDIMetaFile
& o_rMtf
,
130 const MetaAction
& rAct
,
131 const OutputDevice
& rStateOutDev
,
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
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()));
161 o_rMtf
.AddAction(new MetaPopAction());
167 switch (rAct
.GetType())
169 case MetaActionType::BMPEX
:
170 aBmpEx
= static_cast<const MetaBmpExAction
&>(rAct
).GetBitmapEx();
173 case MetaActionType::BMPEXSCALE
:
174 aBmpEx
= static_cast<const MetaBmpExScaleAction
&>(rAct
).GetBitmapEx();
177 case MetaActionType::BMPEXSCALEPART
:
178 aBmpEx
= static_cast<const MetaBmpExScaleAction
&>(rAct
).GetBitmapEx();
181 case MetaActionType::Transparent
:
184 OSL_FAIL("Printer::GetPreparedMetafile impossible state reached");
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(),
204 case MetaActionType::BMPEXSCALE
:
205 o_rMtf
.AddAction(new MetaBmpScaleAction(
206 static_cast<const MetaBmpExScaleAction
&>(rAct
).GetPoint(),
207 static_cast<const MetaBmpExScaleAction
&>(rAct
).GetSize(),
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(),
219 OSL_FAIL("Unexpected case");
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() );
233 switch( rAct
.GetType() )
235 case MetaActionType::POINT
:
236 if( !bLineTransparency
)
240 case MetaActionType::LINE
:
241 if( !bLineTransparency
)
245 case MetaActionType::RECT
:
246 if( !bLineTransparency
|| !bFillTransparency
)
250 case MetaActionType::ROUNDRECT
:
251 if( !bLineTransparency
|| !bFillTransparency
)
255 case MetaActionType::ELLIPSE
:
256 if( !bLineTransparency
|| !bFillTransparency
)
260 case MetaActionType::ARC
:
261 if( !bLineTransparency
|| !bFillTransparency
)
265 case MetaActionType::PIE
:
266 if( !bLineTransparency
|| !bFillTransparency
)
270 case MetaActionType::CHORD
:
271 if( !bLineTransparency
|| !bFillTransparency
)
275 case MetaActionType::POLYLINE
:
276 if( !bLineTransparency
)
280 case MetaActionType::POLYGON
:
281 if( !bLineTransparency
|| !bFillTransparency
)
285 case MetaActionType::POLYPOLYGON
:
286 if( !bLineTransparency
|| !bFillTransparency
)
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())
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())
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
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 ) );
350 case MetaActionType::POINT
:
351 aActionBounds
= tools::Rectangle( static_cast<const MetaPointAction
&>(rAct
).GetPoint(), Size( 1, 1 ) );
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());
362 const tools::Long
nHalfLineWidth((nLineWidth
+ 1) / 2);
363 aActionBounds
.AdjustLeft( -nHalfLineWidth
);
364 aActionBounds
.AdjustTop( -nHalfLineWidth
);
365 aActionBounds
.AdjustRight(nHalfLineWidth
);
366 aActionBounds
.AdjustBottom(nHalfLineWidth
);
371 case MetaActionType::RECT
:
372 aActionBounds
= static_cast<const MetaRectAction
&>(rAct
).GetRect();
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();
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();
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();
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();
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();
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());
415 const tools::Long
nHalfLineWidth((nLineWidth
+ 1) / 2);
416 aActionBounds
.AdjustLeft( -nHalfLineWidth
);
417 aActionBounds
.AdjustTop( -nHalfLineWidth
);
418 aActionBounds
.AdjustRight(nHalfLineWidth
);
419 aActionBounds
.AdjustBottom(nHalfLineWidth
);
424 case MetaActionType::POLYGON
:
425 aActionBounds
= static_cast<const MetaPolygonAction
&>(rAct
).GetPolygon().GetBoundRect();
428 case MetaActionType::POLYPOLYGON
:
429 aActionBounds
= static_cast<const MetaPolyPolygonAction
&>(rAct
).GetPolyPolygon().GetBoundRect();
432 case MetaActionType::BMP
:
433 aActionBounds
= tools::Rectangle( static_cast<const MetaBmpAction
&>(rAct
).GetPoint(),
434 rOut
.PixelToLogic( static_cast<const MetaBmpAction
&>(rAct
).GetBitmap().GetSizePixel() ) );
437 case MetaActionType::BMPSCALE
:
438 aActionBounds
= tools::Rectangle( static_cast<const MetaBmpScaleAction
&>(rAct
).GetPoint(),
439 static_cast<const MetaBmpScaleAction
&>(rAct
).GetSize() );
442 case MetaActionType::BMPSCALEPART
:
443 aActionBounds
= tools::Rectangle( static_cast<const MetaBmpScalePartAction
&>(rAct
).GetDestPoint(),
444 static_cast<const MetaBmpScalePartAction
&>(rAct
).GetDestSize() );
447 case MetaActionType::BMPEX
:
448 aActionBounds
= tools::Rectangle( static_cast<const MetaBmpExAction
&>(rAct
).GetPoint(),
449 rOut
.PixelToLogic( static_cast<const MetaBmpExAction
&>(rAct
).GetBitmapEx().GetSizePixel() ) );
452 case MetaActionType::BMPEXSCALE
:
453 aActionBounds
= tools::Rectangle( static_cast<const MetaBmpExScaleAction
&>(rAct
).GetPoint(),
454 static_cast<const MetaBmpExScaleAction
&>(rAct
).GetSize() );
457 case MetaActionType::BMPEXSCALEPART
:
458 aActionBounds
= tools::Rectangle( static_cast<const MetaBmpExScalePartAction
&>(rAct
).GetDestPoint(),
459 static_cast<const MetaBmpExScalePartAction
&>(rAct
).GetDestSize() );
462 case MetaActionType::MASK
:
463 aActionBounds
= tools::Rectangle( static_cast<const MetaMaskAction
&>(rAct
).GetPoint(),
464 rOut
.PixelToLogic( static_cast<const MetaMaskAction
&>(rAct
).GetBitmap().GetSizePixel() ) );
467 case MetaActionType::MASKSCALE
:
468 aActionBounds
= tools::Rectangle( static_cast<const MetaMaskScaleAction
&>(rAct
).GetPoint(),
469 static_cast<const MetaMaskScaleAction
&>(rAct
).GetSize() );
472 case MetaActionType::MASKSCALEPART
:
473 aActionBounds
= tools::Rectangle( static_cast<const MetaMaskScalePartAction
&>(rAct
).GetDestPoint(),
474 static_cast<const MetaMaskScalePartAction
&>(rAct
).GetDestSize() );
477 case MetaActionType::GRADIENT
:
478 aActionBounds
= static_cast<const MetaGradientAction
&>(rAct
).GetRect();
481 case MetaActionType::GRADIENTEX
:
482 aActionBounds
= static_cast<const MetaGradientExAction
&>(rAct
).GetPolyPolygon().GetBoundRect();
485 case MetaActionType::HATCH
:
486 aActionBounds
= static_cast<const MetaHatchAction
&>(rAct
).GetPolyPolygon().GetBoundRect();
489 case MetaActionType::WALLPAPER
:
490 aActionBounds
= static_cast<const MetaWallpaperAction
&>(rAct
).GetRect();
493 case MetaActionType::Transparent
:
494 aActionBounds
= static_cast<const MetaTransparentAction
&>(rAct
).GetPolyPolygon().GetBoundRect();
497 case MetaActionType::FLOATTRANSPARENT
:
498 aActionBounds
= tools::Rectangle( static_cast<const MetaFloatTransparentAction
&>(rAct
).GetPoint(),
499 static_cast<const MetaFloatTransparentAction
&>(rAct
).GetSize() );
502 case MetaActionType::EPS
:
503 aActionBounds
= tools::Rectangle( static_cast<const MetaEPSAction
&>(rAct
).GetPoint(),
504 static_cast<const MetaEPSAction
&>(rAct
).GetSize() );
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() );
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() );
538 tools::Rectangle
aBoundRect( const_cast<OutputDevice
&>(rOut
).ImplGetTextBoundRect( *pSalLayout
) );
539 aActionBounds
= rOut
.PixelToLogic( aBoundRect
);
545 case MetaActionType::TEXTRECT
:
546 aActionBounds
= static_cast<const MetaTextRectAction
&>(rAct
).GetRect();
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() );
567 tools::Rectangle
aBoundRect( const_cast<OutputDevice
&>(rOut
).ImplGetTextBoundRect( *pSalLayout
) );
568 aActionBounds
= rOut
.PixelToLogic( aBoundRect
);
574 case MetaActionType::TEXTLINE
:
575 OSL_FAIL("MetaActionType::TEXTLINE not supported");
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
) );
589 return rOut
.LogicToPixel( aActionBounds
);
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 );
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.
616 // nothing transparent -> just copy
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
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
:
685 aBackgroundComponent
.aBounds
,
686 aBackgroundComponent
.aBgColor
,
687 static_cast<const MetaRectAction
*>(pCurrAct
)->GetRect(),
689 bStillBackground
=false; // incomplete occlusion of background
691 nLastBgAction
=nActionNum
; // this _is_ background
694 case MetaActionType::POLYGON
:
696 const tools::Polygon
aPoly(
697 static_cast<const MetaPolygonAction
*>(pCurrAct
)->GetPolygon());
698 if( !basegfx::utils::isRectangle(
699 aPoly
.getB2DPolygon()) ||
701 aBackgroundComponent
.aBounds
,
702 aBackgroundComponent
.aBgColor
,
703 aPoly
.GetBoundRect(),
705 bStillBackground
=false; // incomplete occlusion of background
707 nLastBgAction
=nActionNum
; // this _is_ background
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()) ||
718 aBackgroundComponent
.aBounds
,
719 aBackgroundComponent
.aBgColor
,
720 aPoly
.GetBoundRect(),
722 bStillBackground
=false; // incomplete occlusion of background
724 nLastBgAction
=nActionNum
; // this _is_ background
727 case MetaActionType::WALLPAPER
:
730 aBackgroundComponent
.aBounds
,
731 aBackgroundComponent
.aBgColor
,
732 static_cast<const MetaWallpaperAction
*>(pCurrAct
)->GetRect(),
734 bStillBackground
=false; // incomplete occlusion of background
736 nLastBgAction
=nActionNum
; // this _is_ background
741 if( ImplIsNotTransparent( *pCurrAct
,
743 bStillBackground
=false; // non-transparent action, possibly
746 // extend current bounds (next uniform action
747 // needs to fully cover this area)
748 aBackgroundComponent
.aBounds
.Union(
749 ImplCalcActionBounds(*pCurrAct
, *aMapModeVDev
) );
754 // execute action to get correct MapModes etc.
755 pCurrAct
->Execute( aMapModeVDev
.get() );
757 pCurrAct
=const_cast<GDIMetaFile
&>(rInMtf
).NextAction();
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
)
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)
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();
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
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
823 // #107169# Wholly transparent objects need
824 // not be considered for connected components,
825 // too. Just put each of them into a separate
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
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
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;
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.
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;
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;
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;
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
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
] = ¤tItem
;
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
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);
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;
1064 // create new bitmap action first
1065 if( aBoundRect
.GetWidth() && aBoundRect
.GetHeight() )
1067 Point
aDstPtPix( aBoundRect
.TopLeft() );
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
) )
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;
1108 pCurrAct
=const_cast<GDIMetaFile
&>(rInMtf
).NextAction(), ++nActionNum
)
1110 // enable output only for
1111 // actions that are members of
1112 // the current aCCList element
1114 if( aCCList_MemberMap
[nActionNum
] == ¤tItem
)
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);
1140 pPrinter
->DrawGradientEx( aPaintVDev
.get(), pGradientAction
->GetRect(), pGradientAction
->GetGradient() );
1142 DrawGradient( pGradientAction
->GetRect(), pGradientAction
->GetGradient() );
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;
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;
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
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
,
1221 aBackgroundComponent
.aBgColor
);
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) );
1244 rOutMtf
.AddAction( new MetaLineColorAction( COL_BLUE
, true) );
1246 rOutMtf
.AddAction( new MetaRectAction( aMapModeVDev
->PixelToLogic( aCurr
.aBounds
) ) );
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
);
1269 pOut
->DrawGradient( rRect
, rGradient
);
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
);
1291 pOut
->DrawGradient( rRect
, rGradient
);
1294 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */