1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: print2.cxx,v $
10 * $Revision: 1.24.86.3 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_vcl.hxx"
34 #define _SPOOLPRINTER_EXT
41 #include <basegfx/polygon/b2dpolygon.hxx>
42 #include <basegfx/polygon/b2dpolygontools.hxx>
43 #include <tools/debug.hxx>
44 #include <vcl/virdev.hxx>
45 #include <vcl/metaact.hxx>
46 #include <vcl/gdimtf.hxx>
47 #include <vcl/print.h>
48 #include <vcl/salbtype.hxx>
49 #include <vcl/print.hxx>
50 #include <vcl/svapp.hxx>
51 #include <vcl/sallayout.hxx>
52 #include <vcl/bmpacc.hxx>
54 #include "pdfwriter_impl.hxx"
60 #define MAX_TILE_WIDTH 1024
61 #define MAX_TILE_HEIGHT 1024
67 typedef ::std::pair
< MetaAction
*, int > Component
; // MetaAction plus index in metafile
69 typedef ::std::list
< Component
> ComponentList
;
71 // List of (intersecting) actions, plus overall bounds
72 struct ConnectedComponents
74 ConnectedComponents() :
79 bIsFullyTransparent(false)
82 ComponentList aComponentList
;
86 bool bIsFullyTransparent
;
89 typedef ::std::list
< ConnectedComponents
> ConnectedComponentsList
;
96 /** #i10613# Extracted from Printer::GetPreparedMetaFile. Returns true
97 if given action requires special handling (usually because of
100 static bool ImplIsActionSpecial( const MetaAction
& rAct
)
102 switch( rAct
.GetType() )
104 case META_TRANSPARENT_ACTION
:
107 case META_FLOATTRANSPARENT_ACTION
:
110 case META_BMPEX_ACTION
:
111 return static_cast<const MetaBmpExAction
&>(rAct
).GetBitmapEx().IsTransparent();
113 case META_BMPEXSCALE_ACTION
:
114 return static_cast<const MetaBmpExScaleAction
&>(rAct
).GetBitmapEx().IsTransparent();
116 case META_BMPEXSCALEPART_ACTION
:
117 return static_cast<const MetaBmpExScalePartAction
&>(rAct
).GetBitmapEx().IsTransparent();
124 /** Check whether rCurrRect rectangle fully covers io_rPrevRect - if
125 yes, return true and update o_rBgColor
127 static bool checkRect( Rectangle
& io_rPrevRect
,
129 const Rectangle
& rCurrRect
,
130 OutputDevice
& rMapModeVDev
)
132 // shape needs to fully cover previous content, and have uniform
135 rMapModeVDev
.LogicToPixel(rCurrRect
).IsInside(io_rPrevRect
) &&
136 rMapModeVDev
.IsFillColor() );
140 io_rPrevRect
= rCurrRect
;
141 o_rBgColor
= rMapModeVDev
.GetFillColor();
147 /** #107169# Convert BitmapEx to Bitmap with appropriately blended
148 color. Convert MetaTransparentAction to plain polygon,
149 appropriately colored
152 Add converted actions to this metafile
154 static void ImplConvertTransparentAction( GDIMetaFile
& o_rMtf
,
155 const MetaAction
& rAct
,
156 const OutputDevice
& rStateOutDev
,
159 if( rAct
.GetType() == META_TRANSPARENT_ACTION
)
161 const MetaTransparentAction
* pTransAct
= static_cast<const MetaTransparentAction
*>(&rAct
);
162 USHORT
nTransparency( pTransAct
->GetTransparence() );
164 // #i10613# Respect transparency for draw color
167 o_rMtf
.AddAction( new MetaPushAction( PUSH_LINECOLOR
|PUSH_FILLCOLOR
) );
169 // assume white background for alpha blending
170 Color
aLineColor( rStateOutDev
.GetLineColor() );
171 aLineColor
.SetRed( static_cast<UINT8
>( (255L*nTransparency
+ (100L - nTransparency
)*aLineColor
.GetRed()) / 100L ) );
172 aLineColor
.SetGreen( static_cast<UINT8
>( (255L*nTransparency
+ (100L - nTransparency
)*aLineColor
.GetGreen()) / 100L ) );
173 aLineColor
.SetBlue( static_cast<UINT8
>( (255L*nTransparency
+ (100L - nTransparency
)*aLineColor
.GetBlue()) / 100L ) );
174 o_rMtf
.AddAction( new MetaLineColorAction(aLineColor
, TRUE
) );
176 Color
aFillColor( rStateOutDev
.GetFillColor() );
177 aFillColor
.SetRed( static_cast<UINT8
>( (255L*nTransparency
+ (100L - nTransparency
)*aFillColor
.GetRed()) / 100L ) );
178 aFillColor
.SetGreen( static_cast<UINT8
>( (255L*nTransparency
+ (100L - nTransparency
)*aFillColor
.GetGreen()) / 100L ) );
179 aFillColor
.SetBlue( static_cast<UINT8
>( (255L*nTransparency
+ (100L - nTransparency
)*aFillColor
.GetBlue()) / 100L ) );
180 o_rMtf
.AddAction( new MetaFillColorAction(aFillColor
, TRUE
) );
183 o_rMtf
.AddAction( new MetaPolyPolygonAction(pTransAct
->GetPolyPolygon()) );
186 o_rMtf
.AddAction( new MetaPopAction() );
192 switch( rAct
.GetType() )
194 case META_BMPEX_ACTION
:
195 aBmpEx
= static_cast<const MetaBmpExAction
&>(rAct
).GetBitmapEx();
198 case META_BMPEXSCALE_ACTION
:
199 aBmpEx
= static_cast<const MetaBmpExScaleAction
&>(rAct
).GetBitmapEx();
202 case META_BMPEXSCALEPART_ACTION
:
203 aBmpEx
= static_cast<const MetaBmpExScaleAction
&>(rAct
).GetBitmapEx();
206 case META_TRANSPARENT_ACTION
:
209 DBG_ERROR("Printer::GetPreparedMetafile impossible state reached");
213 Bitmap
aBmp( aBmpEx
.GetBitmap() );
214 if( !aBmpEx
.IsAlpha() )
217 BitmapReadAccess
* pRA
= aBmp
.AcquireReadAccess();
220 return; // what else should I do?
222 Color
aActualColor( aBgColor
);
224 if( pRA
->HasPalette() )
225 aActualColor
= pRA
->GetBestPaletteColor( aBgColor
).operator Color();
227 aBmp
.ReleaseAccess(pRA
);
229 // did we get true white?
230 if( aActualColor
.GetColorError( aBgColor
) )
232 // no, create truecolor bitmap, then
233 aBmp
.Convert( BMP_CONVERSION_24BIT
);
235 // fill masked out areas white
236 aBmp
.Replace( aBmpEx
.GetMask(), aBgColor
);
240 // fill masked out areas white
241 aBmp
.Replace( aBmpEx
.GetMask(), aActualColor
);
246 // blend with alpha channel
247 aBmp
.Convert( BMP_CONVERSION_24BIT
);
248 aBmp
.Blend(aBmpEx
.GetAlpha(),aBgColor
);
251 // add corresponding action
252 switch( rAct
.GetType() )
254 case META_BMPEX_ACTION
:
255 o_rMtf
.AddAction( new MetaBmpAction(
256 static_cast<const MetaBmpExAction
&>(rAct
).GetPoint(),
259 case META_BMPEXSCALE_ACTION
:
260 o_rMtf
.AddAction( new MetaBmpScaleAction(
261 static_cast<const MetaBmpExScaleAction
&>(rAct
).GetPoint(),
262 static_cast<const MetaBmpExScaleAction
&>(rAct
).GetSize(),
265 case META_BMPEXSCALEPART_ACTION
:
266 o_rMtf
.AddAction( new MetaBmpScalePartAction(
267 static_cast<const MetaBmpExScalePartAction
&>(rAct
).GetDestPoint(),
268 static_cast<const MetaBmpExScalePartAction
&>(rAct
).GetDestSize(),
269 static_cast<const MetaBmpExScalePartAction
&>(rAct
).GetSrcPoint(),
270 static_cast<const MetaBmpExScalePartAction
&>(rAct
).GetSrcSize(),
274 DBG_ERROR("Unexpected case");
280 // #i10613# Extracted from ImplCheckRect::ImplCreate
281 // Returns true, if given action creates visible (i.e. non-transparent) output
282 static bool ImplIsNotTransparent( const MetaAction
& rAct
, const OutputDevice
& rOut
)
284 const bool bLineTransparency( rOut
.IsLineColor() ? rOut
.GetLineColor().GetTransparency() == 255 : true );
285 const bool bFillTransparency( rOut
.IsFillColor() ? rOut
.GetFillColor().GetTransparency() == 255 : true );
288 switch( rAct
.GetType() )
290 case META_POINT_ACTION
:
291 if( !bLineTransparency
)
295 case META_LINE_ACTION
:
296 if( !bLineTransparency
)
300 case META_RECT_ACTION
:
301 if( !bLineTransparency
|| !bFillTransparency
)
305 case META_ROUNDRECT_ACTION
:
306 if( !bLineTransparency
|| !bFillTransparency
)
310 case META_ELLIPSE_ACTION
:
311 if( !bLineTransparency
|| !bFillTransparency
)
315 case META_ARC_ACTION
:
316 if( !bLineTransparency
|| !bFillTransparency
)
320 case META_PIE_ACTION
:
321 if( !bLineTransparency
|| !bFillTransparency
)
325 case META_CHORD_ACTION
:
326 if( !bLineTransparency
|| !bFillTransparency
)
330 case META_POLYLINE_ACTION
:
331 if( !bLineTransparency
)
335 case META_POLYGON_ACTION
:
336 if( !bLineTransparency
|| !bFillTransparency
)
340 case META_POLYPOLYGON_ACTION
:
341 if( !bLineTransparency
|| !bFillTransparency
)
345 case META_TEXT_ACTION
:
347 const MetaTextAction
& rTextAct
= static_cast<const MetaTextAction
&>(rAct
);
348 const XubString
aString( rTextAct
.GetText(), rTextAct
.GetIndex(), rTextAct
.GetLen() );
355 case META_TEXTARRAY_ACTION
:
357 const MetaTextArrayAction
& rTextAct
= static_cast<const MetaTextArrayAction
&>(rAct
);
358 const XubString
aString( rTextAct
.GetText(), rTextAct
.GetIndex(), rTextAct
.GetLen() );
365 case META_PIXEL_ACTION
:
366 case META_BMP_ACTION
:
367 case META_BMPSCALE_ACTION
:
368 case META_BMPSCALEPART_ACTION
:
369 case META_BMPEX_ACTION
:
370 case META_BMPEXSCALE_ACTION
:
371 case META_BMPEXSCALEPART_ACTION
:
372 case META_MASK_ACTION
:
373 case META_MASKSCALE_ACTION
:
374 case META_MASKSCALEPART_ACTION
:
375 case META_GRADIENT_ACTION
:
376 case META_GRADIENTEX_ACTION
:
377 case META_HATCH_ACTION
:
378 case META_WALLPAPER_ACTION
:
379 case META_TRANSPARENT_ACTION
:
380 case META_FLOATTRANSPARENT_ACTION
:
381 case META_EPS_ACTION
:
382 case META_TEXTRECT_ACTION
:
383 case META_STRETCHTEXT_ACTION
:
384 case META_TEXTLINE_ACTION
:
385 // all other actions: generate non-transparent output
396 // #i10613# Extracted from ImplCheckRect::ImplCreate
397 static Rectangle
ImplCalcActionBounds( const MetaAction
& rAct
, const OutputDevice
& rOut
)
399 Rectangle aActionBounds
;
401 switch( rAct
.GetType() )
403 case META_PIXEL_ACTION
:
404 aActionBounds
= Rectangle( static_cast<const MetaPixelAction
&>(rAct
).GetPoint(), Size( 1, 1 ) );
407 case META_POINT_ACTION
:
408 aActionBounds
= Rectangle( static_cast<const MetaPointAction
&>(rAct
).GetPoint(), Size( 1, 1 ) );
411 case META_LINE_ACTION
:
412 aActionBounds
= Rectangle( static_cast<const MetaLineAction
&>(rAct
).GetStartPoint(),
413 static_cast<const MetaLineAction
&>(rAct
).GetEndPoint() );
416 case META_RECT_ACTION
:
417 aActionBounds
= static_cast<const MetaRectAction
&>(rAct
).GetRect();
420 case META_ROUNDRECT_ACTION
:
421 aActionBounds
= Polygon( static_cast<const MetaRoundRectAction
&>(rAct
).GetRect(),
422 static_cast<const MetaRoundRectAction
&>(rAct
).GetHorzRound(),
423 static_cast<const MetaRoundRectAction
&>(rAct
).GetVertRound() ).GetBoundRect();
426 case META_ELLIPSE_ACTION
:
428 const Rectangle
& rRect
= static_cast<const MetaEllipseAction
&>(rAct
).GetRect();
429 aActionBounds
= Polygon( rRect
.Center(),
430 rRect
.GetWidth() >> 1,
431 rRect
.GetHeight() >> 1 ).GetBoundRect();
435 case META_ARC_ACTION
:
436 aActionBounds
= Polygon( static_cast<const MetaArcAction
&>(rAct
).GetRect(),
437 static_cast<const MetaArcAction
&>(rAct
).GetStartPoint(),
438 static_cast<const MetaArcAction
&>(rAct
).GetEndPoint(), POLY_ARC
).GetBoundRect();
441 case META_PIE_ACTION
:
442 aActionBounds
= Polygon( static_cast<const MetaPieAction
&>(rAct
).GetRect(),
443 static_cast<const MetaPieAction
&>(rAct
).GetStartPoint(),
444 static_cast<const MetaPieAction
&>(rAct
).GetEndPoint(), POLY_PIE
).GetBoundRect();
447 case META_CHORD_ACTION
:
448 aActionBounds
= Polygon( static_cast<const MetaChordAction
&>(rAct
).GetRect(),
449 static_cast<const MetaChordAction
&>(rAct
).GetStartPoint(),
450 static_cast<const MetaChordAction
&>(rAct
).GetEndPoint(), POLY_CHORD
).GetBoundRect();
453 case META_POLYLINE_ACTION
:
454 aActionBounds
= static_cast<const MetaPolyLineAction
&>(rAct
).GetPolygon().GetBoundRect();
457 case META_POLYGON_ACTION
:
458 aActionBounds
= static_cast<const MetaPolygonAction
&>(rAct
).GetPolygon().GetBoundRect();
461 case META_POLYPOLYGON_ACTION
:
462 aActionBounds
= static_cast<const MetaPolyPolygonAction
&>(rAct
).GetPolyPolygon().GetBoundRect();
465 case META_BMP_ACTION
:
466 aActionBounds
= Rectangle( static_cast<const MetaBmpAction
&>(rAct
).GetPoint(),
467 rOut
.PixelToLogic( static_cast<const MetaBmpAction
&>(rAct
).GetBitmap().GetSizePixel() ) );
470 case META_BMPSCALE_ACTION
:
471 aActionBounds
= Rectangle( static_cast<const MetaBmpScaleAction
&>(rAct
).GetPoint(),
472 static_cast<const MetaBmpScaleAction
&>(rAct
).GetSize() );
475 case META_BMPSCALEPART_ACTION
:
476 aActionBounds
= Rectangle( static_cast<const MetaBmpScalePartAction
&>(rAct
).GetDestPoint(),
477 static_cast<const MetaBmpScalePartAction
&>(rAct
).GetDestSize() );
480 case META_BMPEX_ACTION
:
481 aActionBounds
= Rectangle( static_cast<const MetaBmpExAction
&>(rAct
).GetPoint(),
482 rOut
.PixelToLogic( static_cast<const MetaBmpExAction
&>(rAct
).GetBitmapEx().GetSizePixel() ) );
485 case META_BMPEXSCALE_ACTION
:
486 aActionBounds
= Rectangle( static_cast<const MetaBmpExScaleAction
&>(rAct
).GetPoint(),
487 static_cast<const MetaBmpExScaleAction
&>(rAct
).GetSize() );
490 case META_BMPEXSCALEPART_ACTION
:
491 aActionBounds
= Rectangle( static_cast<const MetaBmpExScalePartAction
&>(rAct
).GetDestPoint(),
492 static_cast<const MetaBmpExScalePartAction
&>(rAct
).GetDestSize() );
495 case META_MASK_ACTION
:
496 aActionBounds
= Rectangle( static_cast<const MetaMaskAction
&>(rAct
).GetPoint(),
497 rOut
.PixelToLogic( static_cast<const MetaMaskAction
&>(rAct
).GetBitmap().GetSizePixel() ) );
500 case META_MASKSCALE_ACTION
:
501 aActionBounds
= Rectangle( static_cast<const MetaMaskScaleAction
&>(rAct
).GetPoint(),
502 static_cast<const MetaMaskScaleAction
&>(rAct
).GetSize() );
505 case META_MASKSCALEPART_ACTION
:
506 aActionBounds
= Rectangle( static_cast<const MetaMaskScalePartAction
&>(rAct
).GetDestPoint(),
507 static_cast<const MetaMaskScalePartAction
&>(rAct
).GetDestSize() );
510 case META_GRADIENT_ACTION
:
511 aActionBounds
= static_cast<const MetaGradientAction
&>(rAct
).GetRect();
514 case META_GRADIENTEX_ACTION
:
515 aActionBounds
= static_cast<const MetaGradientExAction
&>(rAct
).GetPolyPolygon().GetBoundRect();
518 case META_HATCH_ACTION
:
519 aActionBounds
= static_cast<const MetaHatchAction
&>(rAct
).GetPolyPolygon().GetBoundRect();
522 case META_WALLPAPER_ACTION
:
523 aActionBounds
= static_cast<const MetaWallpaperAction
&>(rAct
).GetRect();
526 case META_TRANSPARENT_ACTION
:
527 aActionBounds
= static_cast<const MetaTransparentAction
&>(rAct
).GetPolyPolygon().GetBoundRect();
530 case META_FLOATTRANSPARENT_ACTION
:
531 aActionBounds
= Rectangle( static_cast<const MetaFloatTransparentAction
&>(rAct
).GetPoint(),
532 static_cast<const MetaFloatTransparentAction
&>(rAct
).GetSize() );
535 case META_EPS_ACTION
:
536 aActionBounds
= Rectangle( static_cast<const MetaEPSAction
&>(rAct
).GetPoint(),
537 static_cast<const MetaEPSAction
&>(rAct
).GetSize() );
540 case META_TEXT_ACTION
:
542 const MetaTextAction
& rTextAct
= static_cast<const MetaTextAction
&>(rAct
);
543 const XubString
aString( rTextAct
.GetText(), rTextAct
.GetIndex(), rTextAct
.GetLen() );
547 const Point
aPtLog( rTextAct
.GetPoint() );
549 // #105987# Use API method instead of Impl* methods
550 // #107490# Set base parameter equal to index parameter
551 rOut
.GetTextBoundRect( aActionBounds
, rTextAct
.GetText(), rTextAct
.GetIndex(),
552 rTextAct
.GetIndex(), rTextAct
.GetLen() );
553 aActionBounds
.Move( aPtLog
.X(), aPtLog
.Y() );
558 case META_TEXTARRAY_ACTION
:
560 const MetaTextArrayAction
& rTextAct
= static_cast<const MetaTextArrayAction
&>(rAct
);
561 const XubString
aString( rTextAct
.GetText(), rTextAct
.GetIndex(), rTextAct
.GetLen() );
562 const long nLen
= aString
.Len();
566 // #105987# ImplLayout takes everything in logical coordinates
567 SalLayout
* pSalLayout
= rOut
.ImplLayout( rTextAct
.GetText(), rTextAct
.GetIndex(),
568 rTextAct
.GetLen(), rTextAct
.GetPoint(),
569 0, rTextAct
.GetDXArray() );
572 Rectangle
aBoundRect( const_cast<OutputDevice
&>(rOut
).ImplGetTextBoundRect( *pSalLayout
) );
573 aActionBounds
= rOut
.PixelToLogic( aBoundRect
);
574 pSalLayout
->Release();
580 case META_TEXTRECT_ACTION
:
581 aActionBounds
= static_cast<const MetaTextRectAction
&>(rAct
).GetRect();
584 case META_STRETCHTEXT_ACTION
:
586 const MetaStretchTextAction
& rTextAct
= static_cast<const MetaStretchTextAction
&>(rAct
);
587 const XubString
aString( rTextAct
.GetText(), rTextAct
.GetIndex(), rTextAct
.GetLen() );
588 const long nLen
= aString
.Len();
590 // #i16195# Literate copy from TextArray action, the
591 // semantics for the ImplLayout call are copied from the
592 // OutDev::DrawStretchText() code. Unfortunately, also in
593 // this case, public outdev methods such as GetTextWidth()
594 // don't provide enough info.
597 // #105987# ImplLayout takes everything in logical coordinates
598 SalLayout
* pSalLayout
= rOut
.ImplLayout( rTextAct
.GetText(), rTextAct
.GetIndex(),
599 rTextAct
.GetLen(), rTextAct
.GetPoint(),
600 rTextAct
.GetWidth() );
603 Rectangle
aBoundRect( const_cast<OutputDevice
&>(rOut
).ImplGetTextBoundRect( *pSalLayout
) );
604 aActionBounds
= rOut
.PixelToLogic( aBoundRect
);
605 pSalLayout
->Release();
611 case META_TEXTLINE_ACTION
:
612 DBG_ERROR("META_TEXTLINE_ACTION not supported");
619 if( !aActionBounds
.IsEmpty() )
620 return rOut
.LogicToPixel( aActionBounds
);
625 static bool ImplIsActionHandlingTransparency( const MetaAction
& rAct
)
627 // META_FLOATTRANSPARENT_ACTION can contain a whole metafile,
628 // which is to be rendered with the given transparent gradient. We
629 // currently cannot emulate transparent painting on a white
630 // background reliably.
632 // the remainder can handle printing itself correctly on a uniform
634 switch( rAct
.GetType() )
636 case META_TRANSPARENT_ACTION
:
637 case META_BMPEX_ACTION
:
638 case META_BMPEXSCALE_ACTION
:
639 case META_BMPEXSCALEPART_ACTION
:
647 // remove comment to enable highlighting of generated output
648 bool OutputDevice::RemoveTransparenciesFromMetaFile( const GDIMetaFile
& rInMtf
, GDIMetaFile
& rOutMtf
,
649 long nMaxBmpDPIX
, long nMaxBmpDPIY
,
650 bool bReduceTransparency
, bool bTransparencyAutoMode
,
651 bool bDownsampleBitmaps
)
653 MetaAction
* pCurrAct
;
654 bool bTransparent( false );
658 if( ! bReduceTransparency
|| bTransparencyAutoMode
)
660 // watch for transparent drawing actions
661 for( pCurrAct
= ( (GDIMetaFile
&) rInMtf
).FirstAction();
662 pCurrAct
&& !bTransparent
;
663 pCurrAct
= ( (GDIMetaFile
&) rInMtf
).NextAction() )
665 // #i10613# Extracted "specialness" predicate into extra method
667 // #107169# Also examine metafiles with masked bitmaps in
668 // detail. Further down, this is optimized in such a way
669 // that there's no unnecessary painting of masked bitmaps
670 // (which are _always_ subdivided into rectangular regions
671 // of uniform opacity): if a masked bitmap is printed over
672 // empty background, we convert to a plain bitmap with
674 if( ImplIsActionSpecial( *pCurrAct
) )
681 // #i10613# Determine set of connected components containing transparent objects. These are
682 // then processed as bitmaps, the original actions are removed from the metafile.
685 // nothing transparent -> just copy
691 // This works as follows: we want a number of distinct sets of
692 // connected components, where each set contains metafile
693 // actions that are intersecting (note: there are possibly
694 // more actions contained as are directly intersecting,
695 // because we can only produce rectangular bitmaps later
696 // on. Thus, each set of connected components is the smallest
697 // enclosing, axis-aligned rectangle that completely bounds a
698 // number of intersecting metafile actions, plus any action
699 // that would otherwise be cut in two). Therefore, we
700 // iteratively add metafile actions from the original metafile
701 // to this connected components list (aCCList), by checking
702 // each element's bounding box against intersection with the
703 // metaaction at hand.
704 // All those intersecting elements are removed from aCCList
705 // and collected in a temporary list (aCCMergeList). After all
706 // elements have been checked, the aCCMergeList elements are
707 // merged with the metaaction at hand into one resulting
708 // connected component, with one big bounding box, and
709 // inserted into aCCList again.
710 // The time complexity of this algorithm is O(n^3), where n is
711 // the number of metafile actions, and it finds all distinct
712 // regions of rectangle-bounded connected components. This
713 // algorithm was designed by AF.
717 // STAGE 1: Detect background
718 // ==========================
721 // Receives uniform background content, and is _not_ merged
722 // nor checked for intersection against other aCCList elements
723 ConnectedComponents aBackgroundComponent
;
725 // create an OutputDevice to record mapmode changes and the like
726 VirtualDevice aMapModeVDev
;
727 aMapModeVDev
.mnDPIX
= mnDPIX
;
728 aMapModeVDev
.mnDPIY
= mnDPIY
;
729 aMapModeVDev
.EnableOutput(FALSE
);
731 int nLastBgAction
, nActionNum
;
733 // weed out page-filling background objects (if they are
734 // uniformly coloured). Keeping them outside the other
735 // connected components often prevents whole-page bitmap
737 bool bStillBackground
=true; // true until first non-bg action
738 nActionNum
=0; nLastBgAction
=-1;
739 pCurrAct
=const_cast<GDIMetaFile
&>(rInMtf
).FirstAction();
740 while( pCurrAct
&& bStillBackground
)
742 switch( pCurrAct
->GetType() )
744 case META_RECT_ACTION
:
747 aBackgroundComponent
.aBounds
,
748 aBackgroundComponent
.aBgColor
,
749 static_cast<const MetaRectAction
*>(pCurrAct
)->GetRect(),
751 bStillBackground
=false; // incomplete occlusion of background
753 nLastBgAction
=nActionNum
; // this _is_ background
756 case META_POLYGON_ACTION
:
759 static_cast<const MetaPolygonAction
*>(pCurrAct
)->GetPolygon());
760 if( !basegfx::tools::isRectangle(
761 aPoly
.getB2DPolygon()) ||
763 aBackgroundComponent
.aBounds
,
764 aBackgroundComponent
.aBgColor
,
765 aPoly
.GetBoundRect(),
767 bStillBackground
=false; // incomplete occlusion of background
769 nLastBgAction
=nActionNum
; // this _is_ background
772 case META_POLYPOLYGON_ACTION
:
774 const PolyPolygon
aPoly(
775 static_cast<const MetaPolyPolygonAction
*>(pCurrAct
)->GetPolyPolygon());
776 if( aPoly
.Count() != 1 ||
777 !basegfx::tools::isRectangle(
778 aPoly
[0].getB2DPolygon()) ||
780 aBackgroundComponent
.aBounds
,
781 aBackgroundComponent
.aBgColor
,
782 aPoly
.GetBoundRect(),
784 bStillBackground
=false; // incomplete occlusion of background
786 nLastBgAction
=nActionNum
; // this _is_ background
789 case META_WALLPAPER_ACTION
:
792 aBackgroundComponent
.aBounds
,
793 aBackgroundComponent
.aBgColor
,
794 static_cast<const MetaWallpaperAction
*>(pCurrAct
)->GetRect(),
796 bStillBackground
=false; // incomplete occlusion of background
798 nLastBgAction
=nActionNum
; // this _is_ background
803 if( ImplIsNotTransparent( *pCurrAct
,
805 bStillBackground
=false; // non-transparent action, possibly
808 // extend current bounds (next uniform action
809 // needs to fully cover this area)
810 aBackgroundComponent
.aBounds
.Union(
811 ImplCalcActionBounds(*pCurrAct
, aMapModeVDev
) );
816 // execute action to get correct MapModes etc.
817 pCurrAct
->Execute( &aMapModeVDev
);
819 pCurrAct
=const_cast<GDIMetaFile
&>(rInMtf
).NextAction();
823 ConnectedComponentsList aCCList
; // list containing distinct sets of connected components as elements.
825 // create an OutputDevice to record mapmode changes and the like
826 VirtualDevice aMapModeVDev2
;
827 aMapModeVDev2
.mnDPIX
= mnDPIX
;
828 aMapModeVDev2
.mnDPIY
= mnDPIY
;
829 aMapModeVDev2
.EnableOutput(FALSE
);
831 // fast-forward until one after the last background action
832 // (need to reconstruct map mode vdev state)
834 pCurrAct
=const_cast<GDIMetaFile
&>(rInMtf
).FirstAction();
835 while( pCurrAct
&& nActionNum
<=nLastBgAction
)
837 // up to and including last ink-generating background
838 // action go to background component
839 aBackgroundComponent
.aComponentList
.push_back(
841 pCurrAct
, nActionNum
) );
843 // execute action to get correct MapModes etc.
844 pCurrAct
->Execute( &aMapModeVDev2
);
845 pCurrAct
=const_cast<GDIMetaFile
&>(rInMtf
).NextAction();
850 // STAGE 2: Generate connected components list
851 // ===========================================
854 // iterate over all actions (start where background action
858 pCurrAct
=const_cast<GDIMetaFile
&>(rInMtf
).NextAction(), ++nActionNum
)
860 // execute action to get correct MapModes etc.
861 pCurrAct
->Execute( &aMapModeVDev2
);
863 // cache bounds of current action
864 const Rectangle
aBBCurrAct( ImplCalcActionBounds(*pCurrAct
, aMapModeVDev2
) );
866 // accumulate collected bounds here, initialize with current action
867 Rectangle
aTotalBounds( aBBCurrAct
); // thus,
868 // aTotalComponents.aBounds
872 // non-output-generating
874 bool bTreatSpecial( false );
875 ConnectedComponents aTotalComponents
;
878 // STAGE 2.1: Search for intersecting cc entries
879 // =============================================
882 // if aBBCurrAct is empty, it will intersect with no
883 // aCCList member. Thus, we can safe us the check.
884 // Furthermore, this ensures that non-output-generating
885 // actions get their own aCCList entry, which is necessary
886 // when copying them to the output metafile (see stage 4
889 // #107169# Wholly transparent objects need
890 // not be considered for connected components,
891 // too. Just put each of them into a separate
893 aTotalComponents
.bIsFullyTransparent
= !ImplIsNotTransparent(*pCurrAct
, aMapModeVDev2
);
895 if( !aBBCurrAct
.IsEmpty() &&
896 !aTotalComponents
.bIsFullyTransparent
)
898 if( !aBackgroundComponent
.aComponentList
.empty() &&
899 !aBackgroundComponent
.aBounds
.IsInside(aTotalBounds
) )
901 // it seems the background is not large enough. to
902 // be on the safe side, combine with this component.
903 aTotalBounds
.Union( aBackgroundComponent
.aBounds
);
905 // extract all aCurr actions to aTotalComponents
906 aTotalComponents
.aComponentList
.splice( aTotalComponents
.aComponentList
.end(),
907 aBackgroundComponent
.aComponentList
);
909 if( aBackgroundComponent
.bIsSpecial
)
910 bTreatSpecial
= true;
913 ConnectedComponentsList::iterator aCurrCC
;
914 const ConnectedComponentsList::iterator
aLastCC( aCCList
.end() );
915 bool bSomeComponentsChanged
;
917 // now, this is unfortunate: since changing anyone of
918 // the aCCList elements (e.g. by merging or addition
919 // of an action) might generate new intersection with
920 // other aCCList elements, have to repeat the whole
921 // element scanning, until nothing changes anymore.
922 // Thus, this loop here makes us O(n^3) in the worst
926 // only loop here if 'intersects' branch below was hit
927 bSomeComponentsChanged
= false;
929 // iterate over all current members of aCCList
930 for( aCurrCC
=aCCList
.begin(); aCurrCC
!= aLastCC
; )
932 // first check if current element's bounds are
933 // empty. This ensures that empty actions are not
934 // merged into one component, as a matter of fact,
935 // they have no position.
937 // #107169# Wholly transparent objects need
938 // not be considered for connected components,
939 // too. Just put each of them into a separate
941 if( !aCurrCC
->aBounds
.IsEmpty() &&
942 !aCurrCC
->bIsFullyTransparent
&&
943 aCurrCC
->aBounds
.IsOver( aTotalBounds
) )
945 // union the intersecting aCCList element into aTotalComponents
947 // calc union bounding box
948 aTotalBounds
.Union( aCurrCC
->aBounds
);
950 // extract all aCurr actions to aTotalComponents
951 aTotalComponents
.aComponentList
.splice( aTotalComponents
.aComponentList
.end(),
952 aCurrCC
->aComponentList
);
954 if( aCurrCC
->bIsSpecial
)
955 bTreatSpecial
= true;
957 // remove and delete aCurrCC element from list (we've now merged its content)
958 aCurrCC
= aCCList
.erase( aCurrCC
);
960 // at least one component changed, need to rescan everything
961 bSomeComponentsChanged
= true;
969 while( bSomeComponentsChanged
);
973 // STAGE 2.2: Determine special state for cc element
974 // =================================================
977 // now test whether the whole connected component must be
978 // treated specially (i.e. rendered as a bitmap): if the
979 // added action is the very first action, or all actions
980 // before it are completely transparent, the connected
981 // component need not be treated specially, not even if
982 // the added action contains transparency. This is because
983 // painting of transparent objects on _white background_
984 // works without alpha compositing (you just calculate the
985 // color). Note that for the test "all objects before me
986 // are transparent" no sorting is necessary, since the
987 // added metaaction pCurrAct is always in the order the
988 // metafile is painted. Generally, the order of the
989 // metaactions in the ConnectedComponents are not
990 // guaranteed to be the same as in the metafile.
993 // prev component(s) special -> this one, too
994 aTotalComponents
.bIsSpecial
= true;
996 else if( !ImplIsActionSpecial( *pCurrAct
) )
998 // added action and none of prev components special ->
999 // this one normal, too
1000 aTotalComponents
.bIsSpecial
= false;
1004 // added action is special and none of prev components
1005 // special -> do the detailed tests
1007 // can the action handle transparency correctly
1008 // (i.e. when painted on white background, does the
1009 // action still look correct)?
1010 if( !ImplIsActionHandlingTransparency( *pCurrAct
) )
1012 // no, action cannot handle its transparency on
1013 // a printer device, render to bitmap
1014 aTotalComponents
.bIsSpecial
= true;
1018 // yes, action can handle its transparency, so
1019 // check whether we're on white background
1020 if( aTotalComponents
.aComponentList
.empty() )
1022 // nothing between pCurrAct and page
1023 // background -> don't be special
1024 aTotalComponents
.bIsSpecial
= false;
1028 // #107169# Fixes abnove now ensure that _no_
1029 // object in the list is fully transparent. Thus,
1030 // if the component list is not empty above, we
1031 // must assume that we have to treat this
1032 // component special.
1034 // there are non-transparent objects between
1035 // pCurrAct and the empty sheet of paper -> be
1037 aTotalComponents
.bIsSpecial
= true;
1044 // STAGE 2.3: Add newly generated CC list element
1045 // ==============================================
1048 // set new bounds and add action to list
1049 aTotalComponents
.aBounds
= aTotalBounds
;
1050 aTotalComponents
.aComponentList
.push_back(
1052 pCurrAct
, nActionNum
) );
1054 // add aTotalComponents as a new entry to aCCList
1055 aCCList
.push_back( aTotalComponents
);
1057 DBG_ASSERT( !aTotalComponents
.aComponentList
.empty(),
1058 "Printer::GetPreparedMetaFile empty component" );
1059 DBG_ASSERT( !aTotalComponents
.aBounds
.IsEmpty() ||
1060 (aTotalComponents
.aBounds
.IsEmpty() && aTotalComponents
.aComponentList
.size() == 1),
1061 "Printer::GetPreparedMetaFile non-output generating actions must be solitary");
1062 DBG_ASSERT( !aTotalComponents
.bIsFullyTransparent
||
1063 (aTotalComponents
.bIsFullyTransparent
&& aTotalComponents
.aComponentList
.size() == 1),
1064 "Printer::GetPreparedMetaFile fully transparent actions must be solitary");
1067 // well now, we've got the list of disjunct connected
1068 // components. Now we've got to create a map, which contains
1069 // the corresponding aCCList element for every
1070 // metaaction. Later on, we always process the complete
1071 // metafile for each bitmap to be generated, but switch on
1072 // output only for actions contained in the then current
1073 // aCCList element. This ensures correct mapmode and attribute
1074 // settings for all cases.
1076 // maps mtf actions to CC list entries
1077 ::std::vector
< const ConnectedComponents
* > aCCList_MemberMap( rInMtf
.GetActionCount() );
1079 // iterate over all aCCList members and their contained metaactions
1080 ConnectedComponentsList::iterator
aCurr( aCCList
.begin() );
1081 const ConnectedComponentsList::iterator
aLast( aCCList
.end() );
1082 for( ; aCurr
!= aLast
; ++aCurr
)
1084 ComponentList::iterator
aCurrentAction( aCurr
->aComponentList
.begin() );
1085 const ComponentList::iterator
aLastAction( aCurr
->aComponentList
.end() );
1086 for( ; aCurrentAction
!= aLastAction
; ++aCurrentAction
)
1088 // set pointer to aCCList element for corresponding index
1089 aCCList_MemberMap
[ aCurrentAction
->second
] = &(*aCurr
);
1094 // STAGE 3.1: Output background mtf actions (if there are any)
1095 // ===========================================================
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
) );
1109 // STAGE 3.2: Generate banded bitmaps for special regions
1110 // ====================================================
1114 Size
aTmpSize( GetOutputSizePixel() );
1117 aTmpSize
= mpPDFWriter
->getCurPageSize();
1118 aTmpSize
= LogicToPixel( aTmpSize
, MapMode( MAP_POINT
) );
1120 // also add error code to PDFWriter
1121 mpPDFWriter
->insertError( vcl::PDFWriter::Warning_Transparency_Converted
);
1123 const Rectangle
aOutputRect( aTmpPoint
, aTmpSize
);
1124 bool bTiling
= dynamic_cast<Printer
*>(this) != NULL
;
1126 // iterate over all aCCList members and generate bitmaps for the special ones
1127 for( aCurr
= aCCList
.begin(); aCurr
!= aLast
; ++aCurr
)
1129 if( aCurr
->bIsSpecial
)
1131 Rectangle
aBoundRect( aCurr
->aBounds
);
1132 aBoundRect
.Intersection( aOutputRect
);
1134 const double fBmpArea( (double) aBoundRect
.GetWidth() * aBoundRect
.GetHeight() );
1135 const double fOutArea( (double) aOutputRect
.GetWidth() * aOutputRect
.GetHeight() );
1137 // check if output doesn't exceed given size
1138 if( bReduceTransparency
&& bTransparencyAutoMode
&& ( fBmpArea
> ( 0.25 * fOutArea
) ) )
1140 // output normally. Therefore, we simply clear the
1141 // special attribute, as everything non-special is
1142 // copied to rOutMtf further below.
1143 aCurr
->bIsSpecial
= false;
1147 // create new bitmap action first
1148 if( aBoundRect
.GetWidth() && aBoundRect
.GetHeight() )
1150 Point
aDstPtPix( aBoundRect
.TopLeft() );
1153 VirtualDevice aMapVDev
; // here, we record only mapmode information
1154 aMapVDev
.EnableOutput(FALSE
);
1156 VirtualDevice aPaintVDev
; // into this one, we render.
1158 rOutMtf
.AddAction( new MetaPushAction( PUSH_MAPMODE
) );
1159 rOutMtf
.AddAction( new MetaMapModeAction() );
1161 aPaintVDev
.SetDrawMode( GetDrawMode() );
1163 while( aDstPtPix
.Y() <= aBoundRect
.Bottom() )
1165 aDstPtPix
.X() = aBoundRect
.Left();
1166 aDstSzPix
= bTiling
? Size( MAX_TILE_WIDTH
, MAX_TILE_HEIGHT
) : aBoundRect
.GetSize();
1168 if( ( aDstPtPix
.Y() + aDstSzPix
.Height() - 1L ) > aBoundRect
.Bottom() )
1169 aDstSzPix
.Height() = aBoundRect
.Bottom() - aDstPtPix
.Y() + 1L;
1171 while( aDstPtPix
.X() <= aBoundRect
.Right() )
1173 if( ( aDstPtPix
.X() + aDstSzPix
.Width() - 1L ) > aBoundRect
.Right() )
1174 aDstSzPix
.Width() = aBoundRect
.Right() - aDstPtPix
.X() + 1L;
1176 if( !Rectangle( aDstPtPix
, aDstSzPix
).Intersection( aBoundRect
).IsEmpty() &&
1177 aPaintVDev
.SetOutputSizePixel( aDstSzPix
) )
1182 aMapVDev
.mnDPIX
= aPaintVDev
.mnDPIX
= mnDPIX
;
1183 aMapVDev
.mnDPIY
= aPaintVDev
.mnDPIY
= mnDPIY
;
1185 aPaintVDev
.EnableOutput(FALSE
);
1187 // iterate over all actions
1188 for( pCurrAct
=const_cast<GDIMetaFile
&>(rInMtf
).FirstAction(), nActionNum
=0;
1190 pCurrAct
=const_cast<GDIMetaFile
&>(rInMtf
).NextAction(), ++nActionNum
)
1192 // enable output only for
1193 // actions that are members of
1194 // the current aCCList element
1196 if( aCCList_MemberMap
[nActionNum
] == &(*aCurr
) )
1197 aPaintVDev
.EnableOutput(TRUE
);
1199 // but process every action
1200 const USHORT
nType( pCurrAct
->GetType() );
1202 if( META_MAPMODE_ACTION
== nType
)
1204 pCurrAct
->Execute( &aMapVDev
);
1206 MapMode
aMtfMap( aMapVDev
.GetMapMode() );
1207 const Point
aNewOrg( aMapVDev
.PixelToLogic( aDstPtPix
) );
1209 aMtfMap
.SetOrigin( Point( -aNewOrg
.X(), -aNewOrg
.Y() ) );
1210 aPaintVDev
.SetMapMode( aMtfMap
);
1212 else if( ( META_PUSH_ACTION
== nType
) || ( META_POP_ACTION
) == nType
)
1214 pCurrAct
->Execute( &aMapVDev
);
1215 pCurrAct
->Execute( &aPaintVDev
);
1217 else if( META_GRADIENT_ACTION
== nType
)
1219 MetaGradientAction
* pGradientAction
= static_cast<MetaGradientAction
*>(pCurrAct
);
1220 Printer
* pPrinter
= dynamic_cast< Printer
* >(this);
1222 pPrinter
->DrawGradientEx( &aPaintVDev
, pGradientAction
->GetRect(), pGradientAction
->GetGradient() );
1224 DrawGradient( pGradientAction
->GetRect(), pGradientAction
->GetGradient() );
1228 pCurrAct
->Execute( &aPaintVDev
);
1231 if( !( nActionNum
% 4 ) )
1232 Application::Reschedule();
1235 const BOOL bOldMap
= mbMap
;
1236 mbMap
= aPaintVDev
.mbMap
= FALSE
;
1238 Bitmap
aBandBmp( aPaintVDev
.GetBitmap( Point(), aDstSzPix
) );
1240 // scale down bitmap, if requested
1241 if( bDownsampleBitmaps
)
1243 aBandBmp
= GetDownsampledBitmap( aDstSzPix
,
1244 Point(), aBandBmp
.GetSizePixel(),
1245 aBandBmp
, nMaxBmpDPIX
, nMaxBmpDPIY
);
1248 rOutMtf
.AddAction( new MetaCommentAction( "PRNSPOOL_TRANSPARENTBITMAP_BEGIN" ) );
1249 rOutMtf
.AddAction( new MetaBmpScaleAction( aDstPtPix
, aDstSzPix
, aBandBmp
) );
1250 rOutMtf
.AddAction( new MetaCommentAction( "PRNSPOOL_TRANSPARENTBITMAP_END" ) );
1252 aPaintVDev
.mbMap
= TRUE
;
1258 // overlapping bands to avoid missing lines (e.g. PostScript)
1259 aDstPtPix
.X() += aDstSzPix
.Width();
1262 // overlapping bands to avoid missing lines (e.g. PostScript)
1263 aDstPtPix
.Y() += aDstSzPix
.Height();
1266 rOutMtf
.AddAction( new MetaPopAction() );
1273 // STAGE 4: Copy actions to output metafile
1274 // ========================================
1277 // create an OutputDevice to record color settings, mapmode
1278 // changes and the like
1279 VirtualDevice aMapModeVDev3
;
1280 aMapModeVDev3
.mnDPIX
= mnDPIX
;
1281 aMapModeVDev3
.mnDPIY
= mnDPIY
;
1282 aMapModeVDev3
.EnableOutput(FALSE
);
1284 // iterate over all actions and duplicate the ones not in a
1285 // special aCCList member into rOutMtf
1286 for( pCurrAct
=const_cast<GDIMetaFile
&>(rInMtf
).FirstAction(), nActionNum
=0;
1288 pCurrAct
=const_cast<GDIMetaFile
&>(rInMtf
).NextAction(), ++nActionNum
)
1290 const ConnectedComponents
* pCurrAssociatedComponent
= aCCList_MemberMap
[nActionNum
];
1292 // NOTE: This relies on the fact that map-mode or draw
1293 // mode changing actions are solitary aCCList elements and
1294 // have empty bounding boxes, see comment on stage 2.1
1296 if( pCurrAssociatedComponent
&&
1297 (pCurrAssociatedComponent
->aBounds
.IsEmpty() ||
1298 !pCurrAssociatedComponent
->bIsSpecial
) )
1300 // #107169# Treat transparent bitmaps special, if they
1301 // are the first (or sole) action in their bounds
1302 // list. Note that we previously ensured that no
1303 // fully-transparent objects are before us here.
1304 if( ImplIsActionHandlingTransparency( *pCurrAct
) &&
1305 pCurrAssociatedComponent
->aComponentList
.begin()->first
== pCurrAct
)
1307 // convert actions, where masked-out parts are of
1308 // given background color
1309 ImplConvertTransparentAction(rOutMtf
,
1312 aBackgroundComponent
.aBgColor
);
1316 // simply add this action
1317 rOutMtf
.AddAction( ( pCurrAct
->Duplicate(), pCurrAct
) );
1320 pCurrAct
->Execute(&aMapModeVDev3
);
1324 rOutMtf
.SetPrefMapMode( rInMtf
.GetPrefMapMode() );
1325 rOutMtf
.SetPrefSize( rInMtf
.GetPrefSize() );
1327 return bTransparent
;
1330 // -----------------------------------------------------------------------------
1332 Bitmap
OutputDevice::GetDownsampledBitmap( const Size
& rDstSz
,
1333 const Point
& rSrcPt
, const Size
& rSrcSz
,
1334 const Bitmap
& rBmp
, long nMaxBmpDPIX
, long nMaxBmpDPIY
)
1336 Bitmap
aBmp( rBmp
);
1338 if( !aBmp
.IsEmpty() )
1341 const Rectangle
aBmpRect( aPoint
, aBmp
.GetSizePixel() );
1342 Rectangle
aSrcRect( rSrcPt
, rSrcSz
);
1344 // do cropping if neccessary
1345 if( aSrcRect
.Intersection( aBmpRect
) != aBmpRect
)
1347 if( !aSrcRect
.IsEmpty() )
1348 aBmp
.Crop( aSrcRect
);
1353 if( !aBmp
.IsEmpty() )
1355 // do downsampling if neccessary
1356 Size
aDstSizeTwip( PixelToLogic( LogicToPixel( rDstSz
), MAP_TWIP
) );
1358 // #103209# Normalize size (mirroring has to happen outside of this method)
1359 aDstSizeTwip
= Size( labs(aDstSizeTwip
.Width()), labs(aDstSizeTwip
.Height()) );
1361 const Size
aBmpSize( aBmp
.GetSizePixel() );
1362 const double fBmpPixelX
= aBmpSize
.Width();
1363 const double fBmpPixelY
= aBmpSize
.Height();
1364 const double fMaxPixelX
= aDstSizeTwip
.Width() * nMaxBmpDPIX
/ 1440.0;
1365 const double fMaxPixelY
= aDstSizeTwip
.Height() * nMaxBmpDPIY
/ 1440.0;
1367 // check, if the bitmap DPI exceeds the maximum DPI (allow 4 pixel rounding tolerance)
1368 if( ( ( fBmpPixelX
> ( fMaxPixelX
+ 4 ) ) ||
1369 ( fBmpPixelY
> ( fMaxPixelY
+ 4 ) ) ) &&
1370 ( fBmpPixelY
> 0.0 ) && ( fMaxPixelY
> 0.0 ) )
1374 const double fBmpWH
= fBmpPixelX
/ fBmpPixelY
;
1375 const double fMaxWH
= fMaxPixelX
/ fMaxPixelY
;
1377 if( fBmpWH
< fMaxWH
)
1379 aNewBmpSize
.Width() = FRound( fMaxPixelY
* fBmpWH
);
1380 aNewBmpSize
.Height() = FRound( fMaxPixelY
);
1382 else if( fBmpWH
> 0.0 )
1384 aNewBmpSize
.Width() = FRound( fMaxPixelX
);
1385 aNewBmpSize
.Height() = FRound( fMaxPixelX
/ fBmpWH
);
1388 if( aNewBmpSize
.Width() && aNewBmpSize
.Height() )
1389 aBmp
.Scale( aNewBmpSize
);
1399 // -----------------------------------------------------------------------------
1401 BitmapEx
OutputDevice::GetDownsampledBitmapEx( const Size
& rDstSz
,
1402 const Point
& rSrcPt
, const Size
& rSrcSz
,
1403 const BitmapEx
& rBmpEx
, long nMaxBmpDPIX
, long nMaxBmpDPIY
)
1405 BitmapEx
aBmpEx( rBmpEx
);
1407 if( !aBmpEx
.IsEmpty() )
1410 const Rectangle
aBmpRect( aPoint
, aBmpEx
.GetSizePixel() );
1411 Rectangle
aSrcRect( rSrcPt
, rSrcSz
);
1413 // do cropping if neccessary
1414 if( aSrcRect
.Intersection( aBmpRect
) != aBmpRect
)
1416 if( !aSrcRect
.IsEmpty() )
1417 aBmpEx
.Crop( aSrcRect
);
1422 if( !aBmpEx
.IsEmpty() )
1424 // do downsampling if neccessary
1425 Size
aDstSizeTwip( PixelToLogic( LogicToPixel( rDstSz
), MAP_TWIP
) );
1427 // #103209# Normalize size (mirroring has to happen outside of this method)
1428 aDstSizeTwip
= Size( labs(aDstSizeTwip
.Width()), labs(aDstSizeTwip
.Height()) );
1430 const Size
aBmpSize( aBmpEx
.GetSizePixel() );
1431 const double fBmpPixelX
= aBmpSize
.Width();
1432 const double fBmpPixelY
= aBmpSize
.Height();
1433 const double fMaxPixelX
= aDstSizeTwip
.Width() * nMaxBmpDPIX
/ 1440.0;
1434 const double fMaxPixelY
= aDstSizeTwip
.Height() * nMaxBmpDPIY
/ 1440.0;
1436 // check, if the bitmap DPI exceeds the maximum DPI (allow 4 pixel rounding tolerance)
1437 if( ( ( fBmpPixelX
> ( fMaxPixelX
+ 4 ) ) ||
1438 ( fBmpPixelY
> ( fMaxPixelY
+ 4 ) ) ) &&
1439 ( fBmpPixelY
> 0.0 ) && ( fMaxPixelY
> 0.0 ) )
1443 const double fBmpWH
= fBmpPixelX
/ fBmpPixelY
;
1444 const double fMaxWH
= fMaxPixelX
/ fMaxPixelY
;
1446 if( fBmpWH
< fMaxWH
)
1448 aNewBmpSize
.Width() = FRound( fMaxPixelY
* fBmpWH
);
1449 aNewBmpSize
.Height() = FRound( fMaxPixelY
);
1451 else if( fBmpWH
> 0.0 )
1453 aNewBmpSize
.Width() = FRound( fMaxPixelX
);
1454 aNewBmpSize
.Height() = FRound( fMaxPixelX
/ fBmpWH
);
1457 if( aNewBmpSize
.Width() && aNewBmpSize
.Height() )
1458 aBmpEx
.Scale( aNewBmpSize
);
1468 // -----------------------------------------------------------------------------
1470 void Printer::DrawGradientEx( OutputDevice
* pOut
, const Rectangle
& rRect
, const Gradient
& rGradient
)
1472 const PrinterOptions
& rPrinterOptions
= GetPrinterOptions();
1474 if( rPrinterOptions
.IsReduceGradients() )
1476 if( PRINTER_GRADIENT_STRIPES
== rPrinterOptions
.GetReducedGradientMode() )
1478 if( !rGradient
.GetSteps() || ( rGradient
.GetSteps() > rPrinterOptions
.GetReducedGradientStepCount() ) )
1480 Gradient
aNewGradient( rGradient
);
1482 aNewGradient
.SetSteps( rPrinterOptions
.GetReducedGradientStepCount() );
1483 pOut
->DrawGradient( rRect
, aNewGradient
);
1486 pOut
->DrawGradient( rRect
, rGradient
);
1490 const Color
& rStartColor
= rGradient
.GetStartColor();
1491 const Color
& rEndColor
= rGradient
.GetEndColor();
1492 const long nR
= ( ( (long) rStartColor
.GetRed() * rGradient
.GetStartIntensity() ) / 100L +
1493 ( (long) rEndColor
.GetRed() * rGradient
.GetEndIntensity() ) / 100L ) >> 1;
1494 const long nG
= ( ( (long) rStartColor
.GetGreen() * rGradient
.GetStartIntensity() ) / 100L +
1495 ( (long) rEndColor
.GetGreen() * rGradient
.GetEndIntensity() ) / 100L ) >> 1;
1496 const long nB
= ( ( (long) rStartColor
.GetBlue() * rGradient
.GetStartIntensity() ) / 100L +
1497 ( (long) rEndColor
.GetBlue() * rGradient
.GetEndIntensity() ) / 100L ) >> 1;
1498 const Color
aColor( (BYTE
) nR
, (BYTE
) nG
, (BYTE
) nB
);
1500 pOut
->Push( PUSH_LINECOLOR
| PUSH_FILLCOLOR
);
1501 pOut
->SetLineColor( aColor
);
1502 pOut
->SetFillColor( aColor
);
1503 pOut
->DrawRect( rRect
);
1508 pOut
->DrawGradient( rRect
, rGradient
);
1511 // -----------------------------------------------------------------------------
1513 void Printer::DrawGradientEx( OutputDevice
* pOut
, const PolyPolygon
& rPolyPoly
, const Gradient
& rGradient
)
1515 const PrinterOptions
& rPrinterOptions
= GetPrinterOptions();
1517 if( rPrinterOptions
.IsReduceGradients() )
1519 if( PRINTER_GRADIENT_STRIPES
== rPrinterOptions
.GetReducedGradientMode() )
1521 if( !rGradient
.GetSteps() || ( rGradient
.GetSteps() > rPrinterOptions
.GetReducedGradientStepCount() ) )
1523 Gradient
aNewGradient( rGradient
);
1525 aNewGradient
.SetSteps( rPrinterOptions
.GetReducedGradientStepCount() );
1526 pOut
->DrawGradient( rPolyPoly
, aNewGradient
);
1529 pOut
->DrawGradient( rPolyPoly
, rGradient
);
1533 const Color
& rStartColor
= rGradient
.GetStartColor();
1534 const Color
& rEndColor
= rGradient
.GetEndColor();
1535 const long nR
= ( ( (long) rStartColor
.GetRed() * rGradient
.GetStartIntensity() ) / 100L +
1536 ( (long) rEndColor
.GetRed() * rGradient
.GetEndIntensity() ) / 100L ) >> 1;
1537 const long nG
= ( ( (long) rStartColor
.GetGreen() * rGradient
.GetStartIntensity() ) / 100L +
1538 ( (long) rEndColor
.GetGreen() * rGradient
.GetEndIntensity() ) / 100L ) >> 1;
1539 const long nB
= ( ( (long) rStartColor
.GetBlue() * rGradient
.GetStartIntensity() ) / 100L +
1540 ( (long) rEndColor
.GetBlue() * rGradient
.GetEndIntensity() ) / 100L ) >> 1;
1541 const Color
aColor( (BYTE
) nR
, (BYTE
) nG
, (BYTE
) nB
);
1543 pOut
->Push( PUSH_LINECOLOR
| PUSH_FILLCOLOR
);
1544 pOut
->SetLineColor( aColor
);
1545 pOut
->SetFillColor( aColor
);
1546 pOut
->DrawPolyPolygon( rPolyPoly
);
1551 pOut
->DrawGradient( rPolyPoly
, rGradient
);