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: implrenderer.cxx,v $
10 * $Revision: 1.25.4.2 $
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_cppcanvas.hxx"
34 #include <canvas/debug.hxx>
35 #include <tools/diagnose_ex.h>
36 #include <canvas/verbosetrace.hxx>
38 #include <osl/mutex.hxx>
39 #include <vos/mutex.hxx>
40 #include <vcl/svapp.hxx>
42 #include <rtl/logfile.hxx>
44 #include <comphelper/sequence.hxx>
45 #include <comphelper/anytostring.hxx>
46 #include <cppuhelper/exc_hlp.hxx>
48 #include <cppcanvas/canvas.hxx>
50 #include <com/sun/star/rendering/XGraphicDevice.hpp>
51 #include <com/sun/star/rendering/TexturingMode.hpp>
52 #include <com/sun/star/rendering/XParametricPolyPolygon2DFactory.hpp>
53 #include <com/sun/star/uno/Sequence.hxx>
54 #include <com/sun/star/geometry/RealPoint2D.hpp>
55 #include <com/sun/star/rendering/ViewState.hpp>
56 #include <com/sun/star/rendering/RenderState.hpp>
57 #include <com/sun/star/rendering/XCanvasFont.hpp>
58 #include <com/sun/star/rendering/XPolyPolygon2D.hpp>
59 #include <com/sun/star/rendering/XCanvas.hpp>
60 #include <com/sun/star/rendering/PathCapType.hpp>
61 #include <com/sun/star/rendering/PathJoinType.hpp>
63 #include <basegfx/tools/canvastools.hxx>
64 #include <basegfx/numeric/ftools.hxx>
65 #include <basegfx/polygon/b2dpolypolygontools.hxx>
66 #include <basegfx/polygon/b2dpolygontools.hxx>
67 #include <basegfx/polygon/b2dpolygon.hxx>
68 #include <basegfx/polygon/b2dpolypolygon.hxx>
69 #include <basegfx/matrix/b2dhommatrix.hxx>
70 #include <basegfx/vector/b2dsize.hxx>
71 #include <basegfx/range/b2drectangle.hxx>
72 #include <basegfx/point/b2dpoint.hxx>
73 #include <basegfx/tuple/b2dtuple.hxx>
74 #include <basegfx/polygon/b2dpolygonclipper.hxx>
75 #include <basegfx/polygon/b2dpolypolygoncutter.hxx>
77 #include <canvas/canvastools.hxx>
78 #include <vcl/canvastools.hxx>
79 #include <vcl/salbtype.hxx>
80 #include <vcl/gdimtf.hxx>
81 #include <vcl/metaact.hxx>
82 #include <vcl/virdev.hxx>
83 #include <vcl/metric.hxx>
84 #include <vcl/graphictools.hxx>
85 #include <tools/poly.hxx>
86 #include <i18npool/mslangid.hxx>
88 #include <implrenderer.hxx>
90 #include <outdevstate.hxx>
93 #include <bitmapaction.hxx>
94 #include <lineaction.hxx>
95 #include <pointaction.hxx>
96 #include <polypolyaction.hxx>
97 #include <textaction.hxx>
98 #include <transparencygroupaction.hxx>
104 #include <boost/scoped_array.hpp>
106 #include "mtftools.hxx"
107 #include "outdevstate.hxx"
109 #define EMFP_DEBUG(x)
110 //#define EMFP_DEBUG(x) x
112 using namespace ::com::sun::star
;
115 // free support functions
116 // ======================
119 template < class MetaActionType
> void setStateColor( MetaActionType
* pAct
,
121 uno::Sequence
< double >& rColorSequence
,
122 const cppcanvas::CanvasSharedPtr
& rCanvas
)
124 // set rIsColorSet and check for true at the same time
125 if( (rIsColorSet
=pAct
->IsSetting()) != false )
127 ::Color
aColor( pAct
->GetColor() );
129 // force alpha part of color to
130 // opaque. transparent painting is done
131 // explicitely via META_TRANSPARENT_ACTION
132 aColor
.SetTransparency(0);
133 //aColor.SetTransparency(128);
135 rColorSequence
= ::vcl::unotools::colorToDoubleSequence(
137 rCanvas
->getUNOCanvas()->getDevice()->getDeviceColorSpace() );
142 // state stack manipulators
143 // ------------------------
144 void clearStateStack( ::cppcanvas::internal::VectorOfOutDevStates
& rStates
)
147 const ::cppcanvas::internal::OutDevState aDefaultState
;
148 rStates
.push_back( aDefaultState
);
151 ::cppcanvas::internal::OutDevState
& getState( ::cppcanvas::internal::VectorOfOutDevStates
& rStates
)
153 return rStates
.back();
156 const ::cppcanvas::internal::OutDevState
& getState( const ::cppcanvas::internal::VectorOfOutDevStates
& rStates
)
158 return rStates
.back();
161 void pushState( ::cppcanvas::internal::VectorOfOutDevStates
& rStates
,
164 rStates
.push_back( getState( rStates
) );
165 getState( rStates
).pushFlags
= nFlags
;
168 void popState( ::cppcanvas::internal::VectorOfOutDevStates
& rStates
)
170 if( getState( rStates
).pushFlags
!= PUSH_ALL
)
172 // a state is pushed which is incomplete, i.e. does not
173 // restore everything to the previous stack level when
175 // That means, we take the old state, and restore every
176 // OutDevState member whose flag is set, from the new to the
177 // old state. Then the new state gets overwritten by the
180 // preset to-be-calculated new state with old state
181 ::cppcanvas::internal::OutDevState
aCalculatedNewState( getState( rStates
) );
183 // selectively copy to-be-restored content over saved old
187 const ::cppcanvas::internal::OutDevState
& rNewState( getState( rStates
) );
189 if( (aCalculatedNewState
.pushFlags
& PUSH_LINECOLOR
) )
191 aCalculatedNewState
.lineColor
= rNewState
.lineColor
;
192 aCalculatedNewState
.isLineColorSet
= rNewState
.isLineColorSet
;
195 if( (aCalculatedNewState
.pushFlags
& PUSH_FILLCOLOR
) )
197 aCalculatedNewState
.fillColor
= rNewState
.fillColor
;
198 aCalculatedNewState
.isFillColorSet
= rNewState
.isFillColorSet
;
201 if( (aCalculatedNewState
.pushFlags
& PUSH_FONT
) )
203 aCalculatedNewState
.xFont
= rNewState
.xFont
;
204 aCalculatedNewState
.fontRotation
= rNewState
.fontRotation
;
205 aCalculatedNewState
.textReliefStyle
= rNewState
.textReliefStyle
;
206 aCalculatedNewState
.textOverlineStyle
= rNewState
.textOverlineStyle
;
207 aCalculatedNewState
.textUnderlineStyle
= rNewState
.textUnderlineStyle
;
208 aCalculatedNewState
.textStrikeoutStyle
= rNewState
.textStrikeoutStyle
;
209 aCalculatedNewState
.textEmphasisMarkStyle
= rNewState
.textEmphasisMarkStyle
;
210 aCalculatedNewState
.isTextEffectShadowSet
= rNewState
.isTextEffectShadowSet
;
211 aCalculatedNewState
.isTextWordUnderlineSet
= rNewState
.isTextWordUnderlineSet
;
212 aCalculatedNewState
.isTextOutlineModeSet
= rNewState
.isTextOutlineModeSet
;
215 if( (aCalculatedNewState
.pushFlags
& PUSH_TEXTCOLOR
) )
217 aCalculatedNewState
.textColor
= rNewState
.textColor
;
220 if( (aCalculatedNewState
.pushFlags
& PUSH_MAPMODE
) )
222 aCalculatedNewState
.mapModeTransform
= rNewState
.mapModeTransform
;
225 if( (aCalculatedNewState
.pushFlags
& PUSH_CLIPREGION
) )
227 aCalculatedNewState
.clip
= rNewState
.clip
;
228 aCalculatedNewState
.clipRect
= rNewState
.clipRect
;
229 aCalculatedNewState
.xClipPoly
= rNewState
.xClipPoly
;
232 // TODO(F2): Raster ops NYI
233 // if( (aCalculatedNewState.pushFlags & PUSH_RASTEROP) )
237 if( (aCalculatedNewState
.pushFlags
& PUSH_TEXTFILLCOLOR
) )
239 aCalculatedNewState
.textFillColor
= rNewState
.textFillColor
;
240 aCalculatedNewState
.isTextFillColorSet
= rNewState
.isTextFillColorSet
;
243 if( (aCalculatedNewState
.pushFlags
& PUSH_TEXTALIGN
) )
245 aCalculatedNewState
.textReferencePoint
= rNewState
.textReferencePoint
;
248 // TODO(F1): Refpoint handling NYI
249 // if( (aCalculatedNewState.pushFlags & PUSH_REFPOINT) )
253 if( (aCalculatedNewState
.pushFlags
& PUSH_TEXTLINECOLOR
) )
255 aCalculatedNewState
.textLineColor
= rNewState
.textLineColor
;
256 aCalculatedNewState
.isTextLineColorSet
= rNewState
.isTextLineColorSet
;
259 if( (aCalculatedNewState
.pushFlags
& PUSH_TEXTLAYOUTMODE
) )
261 aCalculatedNewState
.textAlignment
= rNewState
.textAlignment
;
262 aCalculatedNewState
.textDirection
= rNewState
.textDirection
;
265 // TODO(F2): Text language handling NYI
266 // if( (aCalculatedNewState.pushFlags & PUSH_TEXTLANGUAGE) )
270 // always copy push mode
271 aCalculatedNewState
.pushFlags
= rNewState
.pushFlags
;
274 getState( rStates
) = aCalculatedNewState
;
282 void setupStrokeAttributes( rendering::StrokeAttributes
& o_rStrokeAttributes
,
283 const ::cppcanvas::internal::ActionFactoryParameters
& rParms
,
284 const LineInfo
& rLineInfo
)
286 const ::basegfx::B2DSize
aWidth( rLineInfo
.GetWidth(), 0 );
287 o_rStrokeAttributes
.StrokeWidth
=
288 (getState( rParms
.mrStates
).mapModeTransform
* aWidth
).getX();
290 // setup reasonable defaults
291 o_rStrokeAttributes
.MiterLimit
= 1.0;
292 o_rStrokeAttributes
.StartCapType
= rendering::PathCapType::BUTT
;
293 o_rStrokeAttributes
.EndCapType
= rendering::PathCapType::BUTT
;
294 o_rStrokeAttributes
.JoinType
= rendering::PathJoinType::MITER
;
296 if( LINE_DASH
== rLineInfo
.GetStyle() )
298 const ::cppcanvas::internal::OutDevState
& rState( getState( rParms
.mrStates
) );
300 // TODO(F1): Interpret OutDev::GetRefPoint() for the start of the dashing.
302 // interpret dash info only if explicitely enabled as
304 const ::basegfx::B2DSize
aDistance( rLineInfo
.GetDistance(), 0 );
305 const double nDistance( (rState
.mapModeTransform
* aDistance
).getX() );
307 const ::basegfx::B2DSize
aDashLen( rLineInfo
.GetDashLen(), 0 );
308 const double nDashLen( (rState
.mapModeTransform
* aDashLen
).getX() );
310 const ::basegfx::B2DSize
aDotLen( rLineInfo
.GetDotLen(), 0 );
311 const double nDotLen( (rState
.mapModeTransform
* aDotLen
).getX() );
313 const sal_Int32
nNumArryEntries( 2*rLineInfo
.GetDashCount() +
314 2*rLineInfo
.GetDotCount() );
316 o_rStrokeAttributes
.DashArray
.realloc( nNumArryEntries
);
317 double* pDashArray
= o_rStrokeAttributes
.DashArray
.getArray();
320 // iteratively fill dash array, first with dashs, then
322 // ===================================================
324 sal_Int32 nCurrEntry
=0;
326 for( sal_Int32 i
=0; i
<rLineInfo
.GetDashCount(); ++i
)
328 pDashArray
[nCurrEntry
++] = nDashLen
;
329 pDashArray
[nCurrEntry
++] = nDistance
;
331 for( sal_Int32 i
=0; i
<rLineInfo
.GetDotCount(); ++i
)
333 pDashArray
[nCurrEntry
++] = nDotLen
;
334 pDashArray
[nCurrEntry
++] = nDistance
;
340 /** Create masked BitmapEx, where the white areas of rBitmap are
341 transparent, and the other appear in rMaskColor.
343 BitmapEx
createMaskBmpEx( const Bitmap
& rBitmap
,
344 const ::Color
& rMaskColor
)
346 const ::Color
aWhite( COL_WHITE
);
347 BitmapPalette
aBiLevelPalette(2);
348 aBiLevelPalette
[0] = aWhite
;
349 aBiLevelPalette
[1] = rMaskColor
;
351 Bitmap
aMask( rBitmap
.CreateMask( aWhite
));
352 Bitmap
aSolid( rBitmap
.GetSizePixel(),
355 aSolid
.Erase( rMaskColor
);
357 return BitmapEx( aSolid
, aMask
);
360 /** Shameless rip from vcl/source/gdi/outdev3.cxx
362 Should consolidate, into something like basetxt...
364 sal_Unicode
getLocalizedChar( sal_Unicode nChar
, LanguageType eLang
)
366 // currently only conversion from ASCII digits is interesting
367 if( (nChar
< '0') || ('9' < nChar
) )
370 sal_Unicode
nOffset(0);
371 // eLang & LANGUAGE_MASK_PRIMARY catches language independent of region.
372 // CAVEAT! To some like Mongolian MS assigned the same primary language
373 // although the script type is different!
374 switch( eLang
& LANGUAGE_MASK_PRIMARY
)
379 case LANGUAGE_ARABIC_SAUDI_ARABIA
& LANGUAGE_MASK_PRIMARY
:
380 case LANGUAGE_URDU
& LANGUAGE_MASK_PRIMARY
:
381 case LANGUAGE_PUNJABI
& LANGUAGE_MASK_PRIMARY
: //???
382 nOffset
= 0x0660 - '0'; // arabic/persian/urdu
384 case LANGUAGE_BENGALI
& LANGUAGE_MASK_PRIMARY
:
385 nOffset
= 0x09E6 - '0'; // bengali
387 case LANGUAGE_BURMESE
& LANGUAGE_MASK_PRIMARY
:
388 nOffset
= 0x1040 - '0'; // burmese
390 case LANGUAGE_HINDI
& LANGUAGE_MASK_PRIMARY
:
391 nOffset
= 0x0966 - '0'; // devanagari
393 case LANGUAGE_GUJARATI
& LANGUAGE_MASK_PRIMARY
:
394 nOffset
= 0x0AE6 - '0'; // gujarati
396 case LANGUAGE_KANNADA
& LANGUAGE_MASK_PRIMARY
:
397 nOffset
= 0x0CE6 - '0'; // kannada
399 case LANGUAGE_KHMER
& LANGUAGE_MASK_PRIMARY
:
400 nOffset
= 0x17E0 - '0'; // khmer
402 case LANGUAGE_LAO
& LANGUAGE_MASK_PRIMARY
:
403 nOffset
= 0x0ED0 - '0'; // lao
405 case LANGUAGE_MALAYALAM
& LANGUAGE_MASK_PRIMARY
:
406 nOffset
= 0x0D66 - '0'; // malayalam
408 case LANGUAGE_MONGOLIAN
& LANGUAGE_MASK_PRIMARY
:
409 if (eLang
== LANGUAGE_MONGOLIAN_MONGOLIAN
)
410 nOffset
= 0x1810 - '0'; // mongolian
412 nOffset
= 0; // mongolian cyrillic
414 case LANGUAGE_ORIYA
& LANGUAGE_MASK_PRIMARY
:
415 nOffset
= 0x0B66 - '0'; // oriya
417 case LANGUAGE_TAMIL
& LANGUAGE_MASK_PRIMARY
:
418 nOffset
= 0x0BE7 - '0'; // tamil
420 case LANGUAGE_TELUGU
& LANGUAGE_MASK_PRIMARY
:
421 nOffset
= 0x0C66 - '0'; // telugu
423 case LANGUAGE_THAI
& LANGUAGE_MASK_PRIMARY
:
424 nOffset
= 0x0E50 - '0'; // thai
426 case LANGUAGE_TIBETAN
& LANGUAGE_MASK_PRIMARY
:
427 nOffset
= 0x0F20 - '0'; // tibetan
431 nChar
= sal::static_int_cast
<sal_Unicode
>(nChar
+ nOffset
);
435 void convertToLocalizedNumerals( XubString
& rStr
,
436 LanguageType eTextLanguage
)
438 const sal_Unicode
* pBase
= rStr
.GetBuffer();
439 const sal_Unicode
* pBegin
= pBase
+ 0;
440 const xub_StrLen nEndIndex
= rStr
.Len();
441 const sal_Unicode
* pEnd
= pBase
+ nEndIndex
;
443 for( ; pBegin
< pEnd
; ++pBegin
)
445 // TODO: are there non-digit localizations?
446 if( (*pBegin
>= '0') && (*pBegin
<= '9') )
448 // translate characters to local preference
449 sal_Unicode cChar
= getLocalizedChar( *pBegin
, eTextLanguage
);
450 if( cChar
!= *pBegin
)
451 rStr
.SetChar( sal::static_int_cast
<USHORT
>(pBegin
- pBase
), cChar
);
462 bool ImplRenderer::createFillAndStroke( const ::basegfx::B2DPolyPolygon
& rPolyPoly
,
463 const ActionFactoryParameters
& rParms
)
465 const OutDevState
& rState( getState( rParms
.mrStates
) );
466 if( (!rState
.isLineColorSet
&&
467 !rState
.isFillColorSet
) ||
468 (rState
.lineColor
.getLength() == 0 &&
469 rState
.fillColor
.getLength() == 0) )
474 ActionSharedPtr
pPolyAction(
475 internal::PolyPolyActionFactory::createPolyPolyAction(
476 rPolyPoly
, rParms
.mrCanvas
, rState
) );
483 rParms
.mrCurrActionIndex
) );
485 rParms
.mrCurrActionIndex
+= pPolyAction
->getActionCount()-1;
491 bool ImplRenderer::createFillAndStroke( const ::basegfx::B2DPolygon
& rPoly
,
492 const ActionFactoryParameters
& rParms
)
494 return createFillAndStroke( ::basegfx::B2DPolyPolygon( rPoly
),
498 void ImplRenderer::skipContent( GDIMetaFile
& rMtf
,
499 const char* pCommentString
,
500 sal_Int32
& io_rCurrActionIndex
) const
502 ENSURE_OR_THROW( pCommentString
,
503 "ImplRenderer::skipContent(): NULL string given" );
505 MetaAction
* pCurrAct
;
506 while( (pCurrAct
=rMtf
.NextAction()) != NULL
)
508 // increment action index, we've skipped an action.
509 ++io_rCurrActionIndex
;
511 if( pCurrAct
->GetType() == META_COMMENT_ACTION
&&
512 static_cast<MetaCommentAction
*>(pCurrAct
)->GetComment().CompareIgnoreCaseToAscii(
513 pCommentString
) == COMPARE_EQUAL
)
515 // requested comment found, done
524 bool ImplRenderer::isActionContained( GDIMetaFile
& rMtf
,
525 const char* pCommentString
,
528 ENSURE_OR_THROW( pCommentString
,
529 "ImplRenderer::isActionContained(): NULL string given" );
533 // at least _one_ call to GDIMetaFile::NextAction() is
537 MetaAction
* pCurrAct
;
538 while( (pCurrAct
=rMtf
.NextAction()) != NULL
)
540 if( pCurrAct
->GetType() == nType
)
542 bRet
= true; // action type found
546 if( pCurrAct
->GetType() == META_COMMENT_ACTION
&&
547 static_cast<MetaCommentAction
*>(pCurrAct
)->GetComment().CompareIgnoreCaseToAscii(
548 pCommentString
) == COMPARE_EQUAL
)
550 // delimiting end comment found, done
551 bRet
= false; // not yet found
558 // rewind metafile to previous position (this method must
559 // not change the current metaaction)
565 // EOF, and not yet found
572 void ImplRenderer::createGradientAction( const ::PolyPolygon
& rPoly
,
573 const ::Gradient
& rGradient
,
574 const ActionFactoryParameters
& rParms
,
575 bool bIsPolygonRectangle
,
576 bool bSubsettableActions
)
578 DBG_TESTSOLARMUTEX();
580 ::basegfx::B2DPolyPolygon
aDevicePoly( rPoly
.getB2DPolyPolygon() );
581 aDevicePoly
.transform( getState( rParms
.mrStates
).mapModeTransform
);
583 // decide, whether this gradient can be rendered natively
584 // by the canvas, or must be emulated via VCL gradient
585 // action extraction.
586 const USHORT
nSteps( rGradient
.GetSteps() );
588 if( // step count is infinite, can use native canvas
591 // step count is sufficiently high, such that no
592 // discernible difference should be visible.
595 uno::Reference
< rendering::XParametricPolyPolygon2DFactory
> xFactory(
596 rParms
.mrCanvas
->getUNOCanvas()->getDevice()->getParametricPolyPolygonFactory() );
600 ::basegfx::B2DHomMatrix aTextureTransformation
;
601 rendering::Texture aTexture
;
603 aTexture
.RepeatModeX
= rendering::TexturingMode::CLAMP
;
604 aTexture
.RepeatModeY
= rendering::TexturingMode::CLAMP
;
605 aTexture
.Alpha
= 1.0;
608 // setup start/end color values
609 // ----------------------------
611 // scale color coefficients with gradient intensities
612 const USHORT
nStartIntensity( rGradient
.GetStartIntensity() );
613 ::Color
aVCLStartColor( rGradient
.GetStartColor() );
614 aVCLStartColor
.SetRed( (UINT8
)(aVCLStartColor
.GetRed() * nStartIntensity
/ 100) );
615 aVCLStartColor
.SetGreen( (UINT8
)(aVCLStartColor
.GetGreen() * nStartIntensity
/ 100) );
616 aVCLStartColor
.SetBlue( (UINT8
)(aVCLStartColor
.GetBlue() * nStartIntensity
/ 100) );
618 const USHORT
nEndIntensity( rGradient
.GetEndIntensity() );
619 ::Color
aVCLEndColor( rGradient
.GetEndColor() );
620 aVCLEndColor
.SetRed( (UINT8
)(aVCLEndColor
.GetRed() * nEndIntensity
/ 100) );
621 aVCLEndColor
.SetGreen( (UINT8
)(aVCLEndColor
.GetGreen() * nEndIntensity
/ 100) );
622 aVCLEndColor
.SetBlue( (UINT8
)(aVCLEndColor
.GetBlue() * nEndIntensity
/ 100) );
624 uno::Reference
<rendering::XColorSpace
> xColorSpace(
625 rParms
.mrCanvas
->getUNOCanvas()->getDevice()->getDeviceColorSpace());
626 const uno::Sequence
< double > aStartColor(
627 ::vcl::unotools::colorToDoubleSequence( aVCLStartColor
,
629 const uno::Sequence
< double > aEndColor(
630 ::vcl::unotools::colorToDoubleSequence( aVCLEndColor
,
633 uno::Sequence
< uno::Sequence
< double > > aColors(2);
634 uno::Sequence
< double > aStops(2);
639 aColors
[0] = aStartColor
;
640 aColors
[1] = aEndColor
;
643 // Setup texture transformation
644 // ----------------------------
646 const ::basegfx::B2DRectangle
aBounds(
647 ::basegfx::tools::getRange(aDevicePoly
) );
649 // setup rotation angle. VCL rotates
650 // counter-clockwise, while canvas transformation
652 double nRotation( -rGradient
.GetAngle() * M_PI
/ 1800.0 );
654 switch( rGradient
.GetStyle() )
656 case GRADIENT_LINEAR
:
657 // FALLTHROUGH intended
660 // standard orientation for VCL linear
661 // gradient is vertical, thus, rotate 90
663 nRotation
+= M_PI
/2.0;
665 const double nBorder(
666 ::basegfx::pruneScaleValue(
667 (1.0 - rGradient
.GetBorder() / 100.0) ) );
669 // shrink texture, to account for border
670 // (only in x direction, linear gradient
671 // is constant in y direction, anyway)
672 aTextureTransformation
.scale( nBorder
,
675 // linear gradients don't respect offsets
676 // (they are implicitely assumed to be
677 // 50%). linear gradients don't have
678 // border on both sides, only on the
679 // startColor side, axial gradients have
680 // border on both sides. As both gradients
681 // are invariant in y direction: leave y
683 double nOffsetX( rGradient
.GetBorder() / 200.0 );
685 // determine type of gradient (and necessary
686 // transformation matrix, should it be emulated by a
688 switch( rGradient
.GetStyle() )
690 case GRADIENT_LINEAR
:
691 nOffsetX
= rGradient
.GetBorder() / 100.0;
692 aTexture
.Gradient
= xFactory
->createLinearHorizontalGradient( aColors
,
697 // vcl considers center color as start color
698 ::std::swap(aColors
[0],aColors
[1]);
699 aTexture
.Gradient
= xFactory
->createAxialHorizontalGradient( aColors
,
703 default: // other cases can't happen
707 // apply border offset values
708 aTextureTransformation
.translate( nOffsetX
,
711 // rotate texture according to gradient rotation
712 aTextureTransformation
.translate( -0.5, -0.5 );
713 aTextureTransformation
.rotate( nRotation
);
715 // to let the first strip of a rotated
716 // gradient start at the _edge_ of the
717 // bound rect (and not, due to rotation,
718 // slightly inside), slightly enlarge the
721 // y/2 sin(alpha) + x/2 cos(alpha)
723 // (values to change are not actual
724 // gradient scales, but original bound
725 // rect dimensions. Since we still want
726 // the border setting to apply after that,
727 // we multiply with that as above for
730 ::basegfx::pruneScaleValue(
731 fabs( aBounds
.getHeight()*sin(nRotation
) ) +
732 fabs( aBounds
.getWidth()*cos(nRotation
) )));
734 aTextureTransformation
.scale( nScale
, nScale
);
736 // translate back origin to center of
738 aTextureTransformation
.translate( 0.5*aBounds
.getWidth(),
739 0.5*aBounds
.getHeight() );
743 case GRADIENT_RADIAL
:
744 // FALLTHROUGH intended
745 case GRADIENT_ELLIPTICAL
:
746 // FALLTHROUGH intended
747 case GRADIENT_SQUARE
:
748 // FALLTHROUGH intended
751 // determine scale factors for the gradient (must
752 // be scaled up from [0,1]x[0,1] rect to object
753 // bounds). Will potentially changed in switch
755 // Respect border value, while doing so, the VCL
756 // gradient's border will effectively shrink the
757 // resulting gradient.
758 double nScaleX( aBounds
.getWidth() * (1.0 - rGradient
.GetBorder() / 100.0) );
759 double nScaleY( aBounds
.getHeight()* (1.0 - rGradient
.GetBorder() / 100.0) );
761 // determine offset values. Since the border is
762 // divided half-by-half to both sides of the
763 // gradient, divide translation offset by an
764 // additional 2. Also respect offset here, but
765 // since VCL gradients have their center at [0,0]
766 // for zero offset, but canvas gradients have
767 // their top, left edge aligned with the
768 // primitive, and offset of 50% effectively must
769 // yield zero shift. Both values will potentially
770 // be adapted in switch statement below.
771 double nOffsetX( aBounds
.getWidth() *
772 (2.0 * rGradient
.GetOfsX() - 100.0 + rGradient
.GetBorder()) / 200.0 );
773 double nOffsetY( aBounds
.getHeight() *
774 (2.0 * rGradient
.GetOfsY() - 100.0 + rGradient
.GetBorder()) / 200.0 );
776 // determine type of gradient (and necessary
777 // transformation matrix, should it be emulated by a
779 switch( rGradient
.GetStyle() )
781 case GRADIENT_RADIAL
:
783 // create isotrophic scaling
784 if( nScaleX
> nScaleY
)
786 nOffsetY
-= (nScaleX
- nScaleY
) * 0.5;
791 nOffsetX
-= (nScaleY
- nScaleX
) * 0.5;
795 // enlarge gradient to match bound rect diagonal
796 aTextureTransformation
.translate( -0.5, -0.5 );
797 const double nScale( hypot(aBounds
.getWidth(), aBounds
.getHeight()) / nScaleX
);
798 aTextureTransformation
.scale( nScale
, nScale
);
799 aTextureTransformation
.translate( 0.5, 0.5 );
801 aTexture
.Gradient
= xFactory
->createEllipticalGradient( aColors
,
803 geometry::RealRectangle2D(0.0,0.0,
808 case GRADIENT_ELLIPTICAL
:
810 // enlarge gradient slightly
811 aTextureTransformation
.translate( -0.5, -0.5 );
812 const double nSqrt2( sqrt(2.0) );
813 aTextureTransformation
.scale( nSqrt2
,nSqrt2
);
814 aTextureTransformation
.translate( 0.5, 0.5 );
816 aTexture
.Gradient
= xFactory
->createEllipticalGradient(
819 ::basegfx::unotools::rectangle2DFromB2DRectangle(
824 case GRADIENT_SQUARE
:
825 // create isotrophic scaling
826 if( nScaleX
> nScaleY
)
828 nOffsetY
-= (nScaleX
- nScaleY
) * 0.5;
833 nOffsetX
-= (nScaleY
- nScaleX
) * 0.5;
837 aTexture
.Gradient
= xFactory
->createRectangularGradient( aColors
,
839 geometry::RealRectangle2D(0.0,0.0,
844 aTexture
.Gradient
= xFactory
->createRectangularGradient(
847 ::basegfx::unotools::rectangle2DFromB2DRectangle(
851 default: // other cases can't happen
855 nScaleX
= ::basegfx::pruneScaleValue( nScaleX
);
856 nScaleY
= ::basegfx::pruneScaleValue( nScaleY
);
858 aTextureTransformation
.scale( nScaleX
, nScaleY
);
860 // rotate texture according to gradient rotation
861 aTextureTransformation
.translate( -0.5*nScaleX
, -0.5*nScaleY
);
862 aTextureTransformation
.rotate( nRotation
);
863 aTextureTransformation
.translate( 0.5*nScaleX
, 0.5*nScaleY
);
865 aTextureTransformation
.translate( nOffsetX
, nOffsetY
);
870 ENSURE_OR_THROW( false,
871 "ImplRenderer::createGradientAction(): Unexpected gradient type" );
875 // As the texture coordinate space is relative to
876 // the polygon coordinate space (NOT to the
877 // polygon itself), move gradient to the start of
878 // the actual polygon. If we skip this, the
879 // gradient will always display at the origin, and
880 // not within the polygon bound (which might be
881 // miles away from the origin).
882 aTextureTransformation
.translate( aBounds
.getMinX(),
885 ::basegfx::unotools::affineMatrixFromHomMatrix( aTexture
.AffineTransform
,
886 aTextureTransformation
);
888 ActionSharedPtr
pPolyAction(
889 internal::PolyPolyActionFactory::createPolyPolyAction(
892 getState( rParms
.mrStates
),
900 rParms
.mrCurrActionIndex
) );
902 rParms
.mrCurrActionIndex
+= pPolyAction
->getActionCount()-1;
905 // done, using native gradients
910 // cannot currently use native canvas gradients, as a
911 // finite step size is given (this funny feature is not
912 // supported by the XCanvas API)
913 pushState( rParms
.mrStates
, PUSH_ALL
);
915 if( !bIsPolygonRectangle
)
917 // only clip, if given polygon is not a rectangle in
918 // the first place (the gradient is always limited to
919 // the given bound rect)
927 rParms
.mrVDev
.AddGradientActions( rPoly
.GetBoundRect(),
931 createActions( aTmpMtf
, rParms
, bSubsettableActions
);
933 popState( rParms
.mrStates
);
936 uno::Reference
< rendering::XCanvasFont
> ImplRenderer::createFont( double& o_rFontRotation
,
938 const ActionFactoryParameters
& rParms
) const
940 rendering::FontRequest aFontRequest
;
942 if( rParms
.mrParms
.maFontName
.isValid() )
943 aFontRequest
.FontDescription
.FamilyName
= rParms
.mrParms
.maFontName
.getValue();
945 aFontRequest
.FontDescription
.FamilyName
= rFont
.GetName();
947 aFontRequest
.FontDescription
.StyleName
= rFont
.GetStyleName();
949 aFontRequest
.FontDescription
.IsSymbolFont
= (rFont
.GetCharSet() == RTL_TEXTENCODING_SYMBOL
) ? util::TriState_YES
: util::TriState_NO
;
950 aFontRequest
.FontDescription
.IsVertical
= rFont
.IsVertical() ? util::TriState_YES
: util::TriState_NO
;
952 // TODO(F2): improve vclenum->panose conversion
953 aFontRequest
.FontDescription
.FontDescription
.Weight
=
954 rParms
.mrParms
.maFontWeight
.isValid() ?
955 rParms
.mrParms
.maFontWeight
.getValue() :
956 ::canvas::tools::numeric_cast
<sal_Int8
>( ::basegfx::fround( rFont
.GetWeight() ) );
957 aFontRequest
.FontDescription
.FontDescription
.Letterform
=
958 rParms
.mrParms
.maFontLetterForm
.isValid() ?
959 rParms
.mrParms
.maFontLetterForm
.getValue() :
960 (rFont
.GetItalic() == ITALIC_NONE
) ? 0 : 9;
962 LanguageType aLang
= rFont
.GetLanguage();
963 aFontRequest
.Locale
= MsLangId::convertLanguageToLocale(aLang
, false);
965 // setup state-local text transformation,
966 // if the font be rotated
967 const short nFontAngle( rFont
.GetOrientation() );
968 if( nFontAngle
!= 0 )
970 // set to unity transform rotated by font angle
971 const double nAngle( nFontAngle
* (F_PI
/ 1800.0) );
972 o_rFontRotation
= -nAngle
;
976 o_rFontRotation
= 0.0;
979 geometry::Matrix2D aFontMatrix
;
980 ::canvas::tools::setIdentityMatrix2D( aFontMatrix
);
982 // TODO(F2): use correct scale direction, font
983 // height might be width or anything else
985 // TODO(Q3): This code smells of programming by
986 // coincidence (the next two if statements)
987 const ::Size
rFontSizeLog( rFont
.GetSize() );
988 const sal_Int32 nFontWidthLog
= rFontSizeLog
.Width();
989 if( nFontWidthLog
!= 0 )
991 ::Font aTestFont
= rFont
;
992 aTestFont
.SetWidth( 0 );
993 sal_Int32 nNormalWidth
= rParms
.mrVDev
.GetFontMetric( aTestFont
).GetWidth();
994 if( nNormalWidth
!= nFontWidthLog
)
996 aFontMatrix
.m00
= (double)nFontWidthLog
/ nNormalWidth
;
999 // #i52608# apply map mode scale also to font matrix - an
1000 // anisotrophic mapmode must be reflected in an
1001 // anisotrophic font matrix scale.
1002 const OutDevState
& rState( getState( rParms
.mrStates
) );
1003 if( !::basegfx::fTools::equal(
1004 rState
.mapModeTransform
.get(0,0),
1005 rState
.mapModeTransform
.get(1,1)) )
1007 const double nScaleX( rState
.mapModeTransform
.get(0,0) );
1008 const double nScaleY( rState
.mapModeTransform
.get(1,1) );
1010 // note: no reason to check for division by zero, we
1011 // always have the value closer (or equal) to zero as
1013 if( fabs(nScaleX
) < fabs(nScaleY
) )
1014 aFontMatrix
.m00
*= nScaleX
/ nScaleY
;
1016 aFontMatrix
.m11
*= nScaleY
/ nScaleX
;
1018 aFontRequest
.CellSize
= (rState
.mapModeTransform
* ::vcl::unotools::b2DSizeFromSize(rFontSizeLog
)).getY();
1020 return rParms
.mrCanvas
->getUNOCanvas()->createFont( aFontRequest
,
1021 uno::Sequence
< beans::PropertyValue
>(),
1025 // create text effects such as shadow/relief/embossed
1026 void ImplRenderer::createTextAction( const ::Point
& rStartPoint
,
1027 const String rString
,
1030 const sal_Int32
* pCharWidths
,
1031 const ActionFactoryParameters
& rParms
,
1032 bool bSubsettableActions
)
1034 ENSURE_OR_THROW( nIndex
>= 0 && nLength
<= rString
.Len() + nIndex
,
1035 "ImplRenderer::createTextWithEffectsAction(): Invalid text index" );
1038 return; // zero-length text, no visible output
1040 const OutDevState
& rState( getState( rParms
.mrStates
) );
1042 // TODO(F2): implement all text effects
1043 // if( rState.textAlignment ); // TODO(F2): NYI
1045 ::Color
aShadowColor( COL_AUTO
);
1046 ::Color
aReliefColor( COL_AUTO
);
1047 ::Size aShadowOffset
;
1048 ::Size aReliefOffset
;
1050 uno::Reference
<rendering::XColorSpace
> xColorSpace(
1051 rParms
.mrCanvas
->getUNOCanvas()->getDevice()->getDeviceColorSpace() );
1053 if( rState
.isTextEffectShadowSet
)
1055 // calculate shadow offset (similar to outdev3.cxx)
1056 // TODO(F3): better match with outdev3.cxx
1057 sal_Int32 nShadowOffset
= static_cast<sal_Int32
>(1.5 + ((rParms
.mrVDev
.GetFont().GetHeight()-24.0)/24.0));
1058 if( nShadowOffset
< 1 )
1061 aShadowOffset
.setWidth( nShadowOffset
);
1062 aShadowOffset
.setHeight( nShadowOffset
);
1064 // determine shadow color (from outdev3.cxx)
1065 ::Color aTextColor
= ::vcl::unotools::doubleSequenceToColor(
1066 rState
.textColor
, xColorSpace
);
1067 bool bIsDark
= (aTextColor
.GetColor() == COL_BLACK
)
1068 || (aTextColor
.GetLuminance() < 8);
1070 aShadowColor
= bIsDark
? COL_LIGHTGRAY
: COL_BLACK
;
1071 aShadowColor
.SetTransparency( aTextColor
.GetTransparency() );
1074 if( rState
.textReliefStyle
)
1076 // calculate relief offset (similar to outdev3.cxx)
1077 sal_Int32 nReliefOffset
= rParms
.mrVDev
.PixelToLogic( Size( 1, 1 ) ).Height();
1078 nReliefOffset
+= nReliefOffset
/2;
1079 if( nReliefOffset
< 1 )
1082 if( rState
.textReliefStyle
== RELIEF_ENGRAVED
)
1083 nReliefOffset
= -nReliefOffset
;
1085 aReliefOffset
.setWidth( nReliefOffset
);
1086 aReliefOffset
.setHeight( nReliefOffset
);
1088 // determine relief color (from outdev3.cxx)
1089 ::Color aTextColor
= ::vcl::unotools::doubleSequenceToColor(
1090 rState
.textColor
, xColorSpace
);
1092 aReliefColor
= ::Color( COL_LIGHTGRAY
);
1094 // we don't have a automatic color, so black is always
1095 // drawn on white (literally copied from
1096 // vcl/source/gdi/outdev3.cxx)
1097 if( aTextColor
.GetColor() == COL_BLACK
)
1099 aTextColor
= ::Color( COL_WHITE
);
1100 getState( rParms
.mrStates
).textColor
=
1101 ::vcl::unotools::colorToDoubleSequence(
1102 aTextColor
, xColorSpace
);
1105 if( aTextColor
.GetColor() == COL_WHITE
)
1106 aReliefColor
= ::Color( COL_BLACK
);
1107 aReliefColor
.SetTransparency( aTextColor
.GetTransparency() );
1110 // create the actual text action
1111 ActionSharedPtr
pTextAction(
1112 TextActionFactory::createTextAction(
1126 bSubsettableActions
) );
1128 ActionSharedPtr pStrikeoutTextAction
;
1130 if ( rState
.textStrikeoutStyle
== STRIKEOUT_X
|| rState
.textStrikeoutStyle
== STRIKEOUT_SLASH
)
1132 long nWidth
= rParms
.mrVDev
.GetTextWidth( rString
,nIndex
,nLength
);
1134 xub_Unicode pChars
[5];
1135 if ( rState
.textStrikeoutStyle
== STRIKEOUT_X
)
1139 pChars
[3]=pChars
[2]=pChars
[1]=pChars
[0];
1141 long nStrikeoutWidth
= nWidth
;
1142 String
aStrikeoutTest( pChars
, 4 );
1144 if( aStrikeoutTest
.Len() )
1146 nStrikeoutWidth
= ( rParms
.mrVDev
.GetTextWidth( aStrikeoutTest
) + 2 ) / 4;
1147 aStrikeoutTest
.Erase();
1149 if( nStrikeoutWidth
<= 0 )
1150 nStrikeoutWidth
= 1;
1153 long nMaxWidth
= nStrikeoutWidth
/2;
1154 if ( nMaxWidth
< 2 )
1156 nMaxWidth
+= nWidth
+ 1;
1158 long nFullStrikeoutWidth
= 0;
1159 String
aStrikeoutText( pChars
, 0 );
1160 while( (nFullStrikeoutWidth
+=nStrikeoutWidth
) < nMaxWidth
+1 )
1161 aStrikeoutText
+= pChars
[0];
1164 sal_Int32 nStartPos
= 0;
1165 xub_StrLen nLen
= aStrikeoutText
.Len();
1169 long nInterval
= ( nWidth
- nStrikeoutWidth
* nLen
) / nLen
;
1170 nStrikeoutWidth
+= nInterval
;
1171 sal_Int32
* pStrikeoutCharWidths
= new sal_Int32
[nLen
];
1173 for ( int i
= 0;i
<nLen
; i
++)
1175 pStrikeoutCharWidths
[i
] = nStrikeoutWidth
;
1178 for ( int i
= 1;i
< nLen
; i
++ )
1180 pStrikeoutCharWidths
[ i
] += pStrikeoutCharWidths
[ i
-1 ];
1183 pStrikeoutTextAction
=
1184 TextActionFactory::createTextAction(
1192 aStrikeoutText
.Len(),
1193 pStrikeoutCharWidths
,
1198 bSubsettableActions
) ;
1204 maActions
.push_back(
1207 rParms
.mrCurrActionIndex
) );
1209 if ( pStrikeoutTextAction
)
1211 maActions
.push_back(
1213 pStrikeoutTextAction
,
1214 rParms
.mrCurrActionIndex
) );
1217 rParms
.mrCurrActionIndex
+= pTextAction
->getActionCount()-1;
1221 void ImplRenderer::updateClipping( const ::basegfx::B2DPolyPolygon
& rClipPoly
,
1222 const ActionFactoryParameters
& rParms
,
1225 ::cppcanvas::internal::OutDevState
& rState( getState( rParms
.mrStates
) );
1226 ::basegfx::B2DPolyPolygon
aClipPoly( rClipPoly
);
1228 const bool bEmptyClipRect( rState
.clipRect
.IsEmpty() );
1229 const bool bEmptyClipPoly( rState
.clip
.count() == 0 );
1231 ENSURE_OR_THROW( bEmptyClipPoly
|| bEmptyClipRect
,
1232 "ImplRenderer::updateClipping(): Clip rect and polygon are both set!" );
1235 (bEmptyClipRect
&& bEmptyClipPoly
) )
1237 rState
.clip
= rClipPoly
;
1241 if( !bEmptyClipRect
)
1243 // TODO(P3): Use Liang-Barsky polygon clip here,
1244 // after all, one object is just a rectangle!
1246 // convert rect to polygon beforehand, must revert
1247 // to general polygon clipping here.
1248 rState
.clip
= ::basegfx::B2DPolyPolygon(
1249 ::basegfx::tools::createPolygonFromRect(
1250 // #121100# VCL rectangular clips always
1251 // include one more pixel to the right
1253 ::basegfx::B2DRectangle( rState
.clipRect
.Left(),
1254 rState
.clipRect
.Top(),
1255 rState
.clipRect
.Right()+1,
1256 rState
.clipRect
.Bottom()+1 ) ) );
1260 rState
.clip
= basegfx::tools::clipPolyPolygonOnPolyPolygon(
1261 aClipPoly
, rState
.clip
, true, false);
1264 // by now, our clip resides in the OutDevState::clip
1266 rState
.clipRect
.SetEmpty();
1268 if( rState
.clip
.count() == 0 )
1270 if( rState
.clipRect
.IsEmpty() )
1272 rState
.xClipPoly
.clear();
1276 rState
.xClipPoly
= ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1277 rParms
.mrCanvas
->getUNOCanvas()->getDevice(),
1278 ::basegfx::B2DPolyPolygon(
1279 ::basegfx::tools::createPolygonFromRect(
1280 // #121100# VCL rectangular clips
1281 // always include one more pixel to
1282 // the right and the bottom
1283 ::basegfx::B2DRectangle( rState
.clipRect
.Left(),
1284 rState
.clipRect
.Top(),
1285 rState
.clipRect
.Right()+1,
1286 rState
.clipRect
.Bottom()+1 ) ) ) );
1291 rState
.xClipPoly
= ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1292 rParms
.mrCanvas
->getUNOCanvas()->getDevice(),
1297 void ImplRenderer::updateClipping( const ::Rectangle
& rClipRect
,
1298 const ActionFactoryParameters
& rParms
,
1301 ::cppcanvas::internal::OutDevState
& rState( getState( rParms
.mrStates
) );
1303 const bool bEmptyClipRect( rState
.clipRect
.IsEmpty() );
1304 const bool bEmptyClipPoly( rState
.clip
.count() == 0 );
1306 ENSURE_OR_THROW( bEmptyClipPoly
|| bEmptyClipRect
,
1307 "ImplRenderer::updateClipping(): Clip rect and polygon are both set!" );
1310 (bEmptyClipRect
&& bEmptyClipPoly
) )
1312 rState
.clipRect
= rClipRect
;
1313 rState
.clip
.clear();
1315 else if( bEmptyClipPoly
)
1317 rState
.clipRect
.Intersection( rClipRect
);
1318 rState
.clip
.clear();
1322 // TODO(P3): Handle a fourth case here, when all clip
1323 // polygons are rectangular, once B2DMultiRange's
1324 // sweep line implementation is done.
1326 // general case: convert to polygon and clip
1327 // -----------------------------------------
1329 // convert rect to polygon beforehand, must revert
1330 // to general polygon clipping here.
1331 ::basegfx::B2DPolyPolygon
aClipPoly(
1332 ::basegfx::tools::createPolygonFromRect(
1333 ::basegfx::B2DRectangle( rClipRect
.Left(),
1336 rClipRect
.Bottom() ) ) );
1338 rState
.clipRect
.SetEmpty();
1341 rState
.clip
= basegfx::tools::clipPolyPolygonOnPolyPolygon(
1342 aClipPoly
, rState
.clip
, true, false);
1345 if( rState
.clip
.count() == 0 )
1347 if( rState
.clipRect
.IsEmpty() )
1349 rState
.xClipPoly
.clear();
1353 rState
.xClipPoly
= ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1354 rParms
.mrCanvas
->getUNOCanvas()->getDevice(),
1355 ::basegfx::B2DPolyPolygon(
1356 ::basegfx::tools::createPolygonFromRect(
1357 // #121100# VCL rectangular clips
1358 // always include one more pixel to
1359 // the right and the bottom
1360 ::basegfx::B2DRectangle( rState
.clipRect
.Left(),
1361 rState
.clipRect
.Top(),
1362 rState
.clipRect
.Right()+1,
1363 rState
.clipRect
.Bottom()+1 ) ) ) );
1368 rState
.xClipPoly
= ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1369 rParms
.mrCanvas
->getUNOCanvas()->getDevice(),
1374 bool ImplRenderer::createActions( GDIMetaFile
& rMtf
,
1375 const ActionFactoryParameters
& rFactoryParms
,
1376 bool bSubsettableActions
)
1378 /* TODO(P2): interpret mtf-comments
1379 ================================
1381 - gradient fillings (do that via comments)
1383 - think about mapping. _If_ we do everything in logical
1384 coordinates (which would solve the probs for stroke
1385 widths and text offsets), then we would have to
1386 recalc scaling for every drawing operation. This is
1387 because the outdev map mode might change at any time.
1388 Also keep in mind, that, although we've double precision
1389 float arithmetic now, different offsets might still
1390 generate different roundings (aka
1391 'OutputDevice::SetPixelOffset())
1395 // alias common parameters
1396 VectorOfOutDevStates
& rStates(rFactoryParms
.mrStates
);
1397 const CanvasSharedPtr
& rCanvas(rFactoryParms
.mrCanvas
);
1398 ::VirtualDevice
& rVDev(rFactoryParms
.mrVDev
);
1399 const Parameters
& rParms(rFactoryParms
.mrParms
);
1400 sal_Int32
& io_rCurrActionIndex(rFactoryParms
.mrCurrActionIndex
);
1403 // Loop over every metaaction
1404 // ==========================
1405 MetaAction
* pCurrAct
;
1407 // TODO(P1): think about caching
1408 for( pCurrAct
=rMtf
.FirstAction();
1410 pCurrAct
= rMtf
.NextAction() )
1412 // execute every action, to keep VDev state up-to-date
1413 // currently used only for
1415 // - the line/fill color when processing a META_TRANSPARENT_ACTION
1416 // - SetFont to process font metric specific actions
1417 pCurrAct
->Execute( &rVDev
);
1419 switch( pCurrAct
->GetType() )
1421 // ------------------------------------------------------------
1423 // In the first part of this monster-switch, we
1424 // handle all state-changing meta actions. These
1425 // are all handled locally.
1427 // ------------------------------------------------------------
1429 case META_PUSH_ACTION
:
1431 MetaPushAction
* pPushAction
= static_cast<MetaPushAction
*>(pCurrAct
);
1433 pPushAction
->GetFlags() );
1437 case META_POP_ACTION
:
1438 popState( rStates
);
1441 case META_TEXTLANGUAGE_ACTION
:
1442 // FALLTHROUGH intended
1443 case META_REFPOINT_ACTION
:
1444 // handled via pCurrAct->Execute( &rVDev )
1447 case META_MAPMODE_ACTION
:
1448 // modify current mapModeTransformation
1449 // transformation, such that subsequent
1450 // coordinates map correctly
1451 tools::calcLogic2PixelAffineTransform( getState( rStates
).mapModeTransform
,
1455 // monitor clip regions, to assemble clip polygon on our own
1456 case META_CLIPREGION_ACTION
:
1458 MetaClipRegionAction
* pClipAction
= static_cast<MetaClipRegionAction
*>(pCurrAct
);
1460 if( !pClipAction
->IsClipping() )
1463 getState( rStates
).clip
.clear();
1467 if( !pClipAction
->GetRegion().HasPolyPolygon() )
1469 VERBOSE_TRACE( "ImplRenderer::createActions(): non-polygonal clip "
1470 "region encountered, falling back to bounding box!" );
1472 // #121806# explicitely kept integer
1473 Rectangle
aClipRect(
1475 pClipAction
->GetRegion().GetBoundRect() ) );
1477 // intersect current clip with given rect
1485 // set new clip polygon (don't intersect
1486 // with old one, just set it)
1488 // #121806# explicitely kept integer
1491 pClipAction
->GetRegion().GetPolyPolygon() ).getB2DPolyPolygon(),
1500 case META_ISECTRECTCLIPREGION_ACTION
:
1502 MetaISectRectClipRegionAction
* pClipAction
= static_cast<MetaISectRectClipRegionAction
*>(pCurrAct
);
1504 // #121806# explicitely kept integer
1505 Rectangle
aClipRect(
1506 rVDev
.LogicToPixel( pClipAction
->GetRect() ) );
1508 // intersect current clip with given rect
1517 case META_ISECTREGIONCLIPREGION_ACTION
:
1519 MetaISectRegionClipRegionAction
* pClipAction
= static_cast<MetaISectRegionClipRegionAction
*>(pCurrAct
);
1521 if( !pClipAction
->GetRegion().HasPolyPolygon() )
1523 VERBOSE_TRACE( "ImplRenderer::createActions(): non-polygonal clip "
1524 "region encountered, falling back to bounding box!" );
1526 // #121806# explicitely kept integer
1527 Rectangle
aClipRect(
1528 rVDev
.LogicToPixel( pClipAction
->GetRegion().GetBoundRect() ) );
1530 // intersect current clip with given rect
1538 // intersect current clip with given clip polygon
1540 // #121806# explicitely kept integer
1543 pClipAction
->GetRegion().GetPolyPolygon() ).getB2DPolyPolygon(),
1551 case META_MOVECLIPREGION_ACTION
:
1555 case META_LINECOLOR_ACTION
:
1556 if( !rParms
.maLineColor
.isValid() )
1558 setStateColor( static_cast<MetaLineColorAction
*>(pCurrAct
),
1559 getState( rStates
).isLineColorSet
,
1560 getState( rStates
).lineColor
,
1565 case META_FILLCOLOR_ACTION
:
1566 if( !rParms
.maFillColor
.isValid() )
1568 setStateColor( static_cast<MetaFillColorAction
*>(pCurrAct
),
1569 getState( rStates
).isFillColorSet
,
1570 getState( rStates
).fillColor
,
1575 case META_TEXTCOLOR_ACTION
:
1577 if( !rParms
.maTextColor
.isValid() )
1579 // Text color is set unconditionally, thus, no
1580 // use of setStateColor here
1581 ::Color
aColor( static_cast<MetaTextColorAction
*>(pCurrAct
)->GetColor() );
1583 // force alpha part of color to
1584 // opaque. transparent painting is done
1585 // explicitely via META_TRANSPARENT_ACTION
1586 aColor
.SetTransparency(0);
1588 getState( rStates
).textColor
=
1589 ::vcl::unotools::colorToDoubleSequence(
1591 rCanvas
->getUNOCanvas()->getDevice()->getDeviceColorSpace() );
1596 case META_TEXTFILLCOLOR_ACTION
:
1597 if( !rParms
.maTextColor
.isValid() )
1599 setStateColor( static_cast<MetaTextFillColorAction
*>(pCurrAct
),
1600 getState( rStates
).isTextFillColorSet
,
1601 getState( rStates
).textFillColor
,
1606 case META_TEXTLINECOLOR_ACTION
:
1607 if( !rParms
.maTextColor
.isValid() )
1609 setStateColor( static_cast<MetaTextLineColorAction
*>(pCurrAct
),
1610 getState( rStates
).isTextLineColorSet
,
1611 getState( rStates
).textLineColor
,
1616 case META_TEXTALIGN_ACTION
:
1618 ::cppcanvas::internal::OutDevState
& rState
= getState( rStates
);
1619 const TextAlign
eTextAlign( static_cast<MetaTextAlignAction
*>(pCurrAct
)->GetTextAlign() );
1621 rState
.textReferencePoint
= eTextAlign
;
1625 case META_FONT_ACTION
:
1627 ::cppcanvas::internal::OutDevState
& rState
= getState( rStates
);
1628 const ::Font
& rFont( static_cast<MetaFontAction
*>(pCurrAct
)->GetFont() );
1630 rState
.xFont
= createFont( rState
.fontRotation
,
1634 // TODO(Q2): define and use appropriate enumeration types
1635 rState
.textReliefStyle
= (sal_Int8
)rFont
.GetRelief();
1636 rState
.textOverlineStyle
= (sal_Int8
)rFont
.GetOverline();
1637 rState
.textUnderlineStyle
= rParms
.maFontUnderline
.isValid() ?
1638 (rParms
.maFontUnderline
.getValue() ? (sal_Int8
)UNDERLINE_SINGLE
: (sal_Int8
)UNDERLINE_NONE
) :
1639 (sal_Int8
)rFont
.GetUnderline();
1640 rState
.textStrikeoutStyle
= (sal_Int8
)rFont
.GetStrikeout();
1641 rState
.textEmphasisMarkStyle
= (sal_Int8
)rFont
.GetEmphasisMark();
1642 rState
.isTextEffectShadowSet
= (rFont
.IsShadow() != FALSE
);
1643 rState
.isTextWordUnderlineSet
= (rFont
.IsWordLineMode() != FALSE
);
1644 rState
.isTextOutlineModeSet
= (rFont
.IsOutline() != FALSE
);
1648 case META_RASTEROP_ACTION
:
1652 case META_LAYOUTMODE_ACTION
:
1654 // TODO(F2): A lot is missing here
1655 int nLayoutMode
= static_cast<MetaLayoutModeAction
*>(pCurrAct
)->GetLayoutMode();
1656 ::cppcanvas::internal::OutDevState
& rState
= getState( rStates
);
1657 switch( nLayoutMode
& (TEXT_LAYOUT_BIDI_RTL
|TEXT_LAYOUT_BIDI_STRONG
) )
1659 case TEXT_LAYOUT_BIDI_LTR
:
1660 rState
.textDirection
= rendering::TextDirection::WEAK_LEFT_TO_RIGHT
;
1663 case (TEXT_LAYOUT_BIDI_LTR
| TEXT_LAYOUT_BIDI_STRONG
):
1664 rState
.textDirection
= rendering::TextDirection::STRONG_LEFT_TO_RIGHT
;
1667 case TEXT_LAYOUT_BIDI_RTL
:
1668 rState
.textDirection
= rendering::TextDirection::WEAK_RIGHT_TO_LEFT
;
1671 case (TEXT_LAYOUT_BIDI_RTL
| TEXT_LAYOUT_BIDI_STRONG
):
1672 rState
.textDirection
= rendering::TextDirection::STRONG_RIGHT_TO_LEFT
;
1676 rState
.textAlignment
= 0; // TODO(F2): rendering::TextAlignment::LEFT_ALIGNED;
1677 if( (nLayoutMode
& (TEXT_LAYOUT_BIDI_RTL
| TEXT_LAYOUT_TEXTORIGIN_RIGHT
) )
1678 && !(nLayoutMode
& TEXT_LAYOUT_TEXTORIGIN_LEFT
) )
1680 rState
.textAlignment
= 1; // TODO(F2): rendering::TextAlignment::RIGHT_ALIGNED;
1685 // ------------------------------------------------------------
1687 // In the second part of this monster-switch, we
1688 // handle all recursing meta actions. These are the
1689 // ones generating a metafile by themselves, which is
1690 // then processed by recursively calling this method.
1692 // ------------------------------------------------------------
1694 case META_GRADIENT_ACTION
:
1696 MetaGradientAction
* pGradAct
= static_cast<MetaGradientAction
*>(pCurrAct
);
1697 createGradientAction( ::Polygon( pGradAct
->GetRect() ),
1698 pGradAct
->GetGradient(),
1701 bSubsettableActions
);
1705 case META_HATCH_ACTION
:
1707 // TODO(F2): use native Canvas hatches here
1708 GDIMetaFile aTmpMtf
;
1710 rVDev
.AddHatchActions( static_cast<MetaHatchAction
*>(pCurrAct
)->GetPolyPolygon(),
1711 static_cast<MetaHatchAction
*>(pCurrAct
)->GetHatch(),
1713 createActions( aTmpMtf
, rFactoryParms
,
1714 bSubsettableActions
);
1718 case META_EPS_ACTION
:
1720 MetaEPSAction
* pAct
= static_cast<MetaEPSAction
*>(pCurrAct
);
1721 const GDIMetaFile
& rSubstitute
= pAct
->GetSubstitute();
1723 // #121806# explicitely kept integer
1724 const Size
aMtfSize( rSubstitute
.GetPrefSize() );
1725 const Size
aMtfSizePixPre( rVDev
.LogicToPixel( aMtfSize
,
1726 rSubstitute
.GetPrefMapMode() ) );
1728 // #i44110# correct null-sized output - there
1729 // are metafiles which have zero size in at
1730 // least one dimension
1731 const Size
aMtfSizePix( ::std::max( aMtfSizePixPre
.Width(), 1L ),
1732 ::std::max( aMtfSizePixPre
.Height(), 1L ) );
1734 // Setup local transform, such that the
1735 // metafile renders itself into the given
1737 pushState( rStates
, PUSH_ALL
);
1740 rVDev
.SetMapMode( rSubstitute
.GetPrefMapMode() );
1742 const ::Point
& rPos( rVDev
.LogicToPixel( pAct
->GetPoint() ) );
1743 const ::Size
& rSize( rVDev
.LogicToPixel( pAct
->GetSize() ) );
1745 getState( rStates
).transform
.translate( rPos
.X(),
1747 getState( rStates
).transform
.scale( (double)rSize
.Width() / aMtfSizePix
.Width(),
1748 (double)rSize
.Height() / aMtfSizePix
.Height() );
1750 createActions( const_cast<GDIMetaFile
&>(pAct
->GetSubstitute()),
1752 bSubsettableActions
);
1755 popState( rStates
);
1759 // handle metafile comments, to retrieve
1760 // meta-information for gradients, fills and
1761 // strokes. May skip actions, and may recurse.
1762 case META_COMMENT_ACTION
:
1764 MetaCommentAction
* pAct
= static_cast<MetaCommentAction
*>(pCurrAct
);
1767 if ( pAct
->GetComment().CompareIgnoreCaseToAscii( "XGRAD_SEQ_BEGIN" ) == COMPARE_EQUAL
)
1769 MetaGradientExAction
* pGradAction
= NULL
;
1770 bool bDone( false );
1772 (pCurrAct
=rMtf
.NextAction()) != NULL
)
1774 switch( pCurrAct
->GetType() )
1776 // extract gradient info
1777 case META_GRADIENTEX_ACTION
:
1778 pGradAction
= static_cast<MetaGradientExAction
*>(pCurrAct
);
1781 // skip broken-down rendering, output gradient when sequence is ended
1782 case META_COMMENT_ACTION
:
1783 if( static_cast<MetaCommentAction
*>(pCurrAct
)->GetComment().CompareIgnoreCaseToAscii( "XGRAD_SEQ_END" ) == COMPARE_EQUAL
)
1789 createGradientAction( pGradAction
->GetPolyPolygon(),
1790 pGradAction
->GetGradient(),
1793 bSubsettableActions
);
1800 // TODO(P2): Handle drawing layer strokes, via
1801 // XPATHSTROKE_SEQ_BEGIN comment
1803 // Handle drawing layer fills
1804 else if( pAct
->GetComment().Equals( "XPATHFILL_SEQ_BEGIN" ) )
1806 const BYTE
* pData
= pAct
->GetData();
1809 SvMemoryStream
aMemStm( (void*)pData
, pAct
->GetDataSize(), STREAM_READ
);
1811 SvtGraphicFill aFill
;
1814 // TODO(P2): Also handle gradients and
1815 // hatches like this
1817 // only evaluate comment for pure
1818 // bitmap fills. If a transparency
1819 // gradient is involved (denoted by
1820 // the FloatTransparent action), take
1821 // the normal meta actions.
1822 if( aFill
.getFillType() == SvtGraphicFill::fillTexture
&&
1823 !isActionContained( rMtf
,
1824 "XPATHFILL_SEQ_END",
1825 META_FLOATTRANSPARENT_ACTION
) )
1827 rendering::Texture aTexture
;
1829 // TODO(F1): the SvtGraphicFill
1830 // can also transport metafiles
1831 // here, handle that case, too
1833 aFill
.getGraphic( aGraphic
);
1835 BitmapEx
aBmpEx( aGraphic
.GetBitmapEx() );
1836 const ::Size
aBmpSize( aBmpEx
.GetSizePixel() );
1838 ::SvtGraphicFill::Transform aTransform
;
1839 aFill
.getTransform( aTransform
);
1841 ::basegfx::B2DHomMatrix aMatrix
;
1843 // convert to basegfx matrix
1844 aMatrix
.set(0,0, aTransform
.matrix
[ 0 ] );
1845 aMatrix
.set(0,1, aTransform
.matrix
[ 1 ] );
1846 aMatrix
.set(0,2, aTransform
.matrix
[ 2 ] );
1847 aMatrix
.set(1,0, aTransform
.matrix
[ 3 ] );
1848 aMatrix
.set(1,1, aTransform
.matrix
[ 4 ] );
1849 aMatrix
.set(1,2, aTransform
.matrix
[ 5 ] );
1851 ::basegfx::B2DHomMatrix aScale
;
1852 aScale
.scale( aBmpSize
.Width(),
1853 aBmpSize
.Height() );
1855 // post-multiply with the bitmap
1856 // size (XCanvas' texture assumes
1857 // the given bitmap to be
1858 // normalized to [0,1]x[0,1]
1860 aMatrix
= aMatrix
* aScale
;
1862 // pre-multiply with the
1863 // logic-to-pixel scale factor
1864 // (the metafile comment works in
1865 // logical coordinates).
1866 ::basegfx::B2DHomMatrix aLogic2PixelTransform
;
1867 aMatrix
*= tools::calcLogic2PixelLinearTransform( aLogic2PixelTransform
,
1870 ::basegfx::unotools::affineMatrixFromHomMatrix(
1871 aTexture
.AffineTransform
,
1874 aTexture
.Alpha
= 1.0 - aFill
.getTransparency();
1876 ::vcl::unotools::xBitmapFromBitmapEx(
1877 rCanvas
->getUNOCanvas()->getDevice(),
1879 if( aFill
.isTiling() )
1881 aTexture
.RepeatModeX
= rendering::TexturingMode::REPEAT
;
1882 aTexture
.RepeatModeY
= rendering::TexturingMode::REPEAT
;
1886 aTexture
.RepeatModeX
= rendering::TexturingMode::CLAMP
;
1887 aTexture
.RepeatModeY
= rendering::TexturingMode::CLAMP
;
1890 ::PolyPolygon aPath
;
1891 aFill
.getPath( aPath
);
1893 ::basegfx::B2DPolyPolygon
aPoly( aPath
.getB2DPolyPolygon() );
1894 aPoly
.transform( getState( rStates
).mapModeTransform
);
1895 ActionSharedPtr
pPolyAction(
1896 internal::PolyPolyActionFactory::createPolyPolyAction(
1899 getState( rStates
),
1904 maActions
.push_back(
1907 io_rCurrActionIndex
) );
1909 io_rCurrActionIndex
+= pPolyAction
->getActionCount()-1;
1912 // skip broken-down render output
1914 "XPATHFILL_SEQ_END",
1915 io_rCurrActionIndex
);
1919 // Handle drawing layer fills
1920 else if( pAct
->GetComment().Equals( "EMF_PLUS" ) ) {
1921 static int count
= -1, limit
= 0x7fffffff;
1925 if (env
= getenv ("EMF_PLUS_LIMIT")) {
1927 EMFP_DEBUG (printf ("EMF+ records limit: %d\n", limit
));
1930 EMFP_DEBUG (printf ("EMF+ passed to canvas mtf renderer, size: %d\n", pAct
->GetDataSize ()));
1932 processEMFPlus( pAct
, rFactoryParms
, getState( rStates
), rCanvas
);
1934 } else if( pAct
->GetComment().Equals( "EMF_PLUS_HEADER_INFO" ) ) {
1935 EMFP_DEBUG (printf ("EMF+ passed to canvas mtf renderer - header info, size: %d\n", pAct
->GetDataSize ()));
1937 SvMemoryStream
rMF ((void*) pAct
->GetData (), pAct
->GetDataSize (), STREAM_READ
);
1939 rMF
>> nFrameLeft
>> nFrameTop
>> nFrameRight
>> nFrameBottom
;
1940 EMFP_DEBUG (printf ("EMF+ picture frame: %d,%d - %d,%d\n", nFrameLeft
, nFrameTop
, nFrameRight
, nFrameBottom
));
1941 rMF
>> nPixX
>> nPixY
>> nMmX
>> nMmY
;
1942 EMFP_DEBUG (printf ("EMF+ ref device pixel size: %dx%d mm size: %dx%d\n", nPixX
, nPixY
, nMmX
, nMmY
));
1944 rMF
>> aBaseTransform
;
1945 //aWorldTransform.Set (aBaseTransform);
1950 // ------------------------------------------------------------
1952 // In the third part of this monster-switch, we
1953 // handle all 'acting' meta actions. These are all
1954 // processed by constructing function objects for
1955 // them, which will later ease caching.
1957 // ------------------------------------------------------------
1959 case META_POINT_ACTION
:
1961 const OutDevState
& rState( getState( rStates
) );
1962 if( rState
.lineColor
.getLength() )
1964 ActionSharedPtr
pPointAction(
1965 internal::PointActionFactory::createPointAction(
1966 rState
.mapModeTransform
* ::vcl::unotools::b2DPointFromPoint(
1967 static_cast<MetaPointAction
*>(pCurrAct
)->GetPoint() ),
1973 maActions
.push_back(
1976 io_rCurrActionIndex
) );
1978 io_rCurrActionIndex
+= pPointAction
->getActionCount()-1;
1984 case META_PIXEL_ACTION
:
1986 const OutDevState
& rState( getState( rStates
) );
1987 if( rState
.lineColor
.getLength() )
1989 ActionSharedPtr
pPointAction(
1990 internal::PointActionFactory::createPointAction(
1991 rState
.mapModeTransform
* ::vcl::unotools::b2DPointFromPoint(
1992 static_cast<MetaPixelAction
*>(pCurrAct
)->GetPoint() ),
1995 static_cast<MetaPixelAction
*>(pCurrAct
)->GetColor() ) );
1999 maActions
.push_back(
2002 io_rCurrActionIndex
) );
2004 io_rCurrActionIndex
+= pPointAction
->getActionCount()-1;
2010 case META_LINE_ACTION
:
2012 const OutDevState
& rState( getState( rStates
) );
2013 if( rState
.lineColor
.getLength() )
2015 MetaLineAction
* pLineAct
= static_cast<MetaLineAction
*>(pCurrAct
);
2017 const LineInfo
& rLineInfo( pLineAct
->GetLineInfo() );
2019 const ::basegfx::B2DPoint
aStartPoint(
2020 rState
.mapModeTransform
* ::vcl::unotools::b2DPointFromPoint( pLineAct
->GetStartPoint() ));
2021 const ::basegfx::B2DPoint
aEndPoint(
2022 rState
.mapModeTransform
* ::vcl::unotools::b2DPointFromPoint( pLineAct
->GetEndPoint() ));
2024 ActionSharedPtr pLineAction
;
2026 if( rLineInfo
.IsDefault() )
2030 internal::LineActionFactory::createLineAction(
2038 maActions
.push_back(
2041 io_rCurrActionIndex
) );
2043 io_rCurrActionIndex
+= pLineAction
->getActionCount()-1;
2046 else if( LINE_NONE
!= rLineInfo
.GetStyle() )
2049 rendering::StrokeAttributes aStrokeAttributes
;
2051 setupStrokeAttributes( aStrokeAttributes
,
2055 // XCanvas can only stroke polygons,
2056 // not simple lines - thus, handle
2057 // this case via the polypolygon
2059 ::basegfx::B2DPolygon aPoly
;
2060 aPoly
.append( aStartPoint
);
2061 aPoly
.append( aEndPoint
);
2063 internal::PolyPolyActionFactory::createPolyPolyAction(
2064 ::basegfx::B2DPolyPolygon( aPoly
),
2065 rCanvas
, rState
, aStrokeAttributes
);
2069 maActions
.push_back(
2072 io_rCurrActionIndex
) );
2074 io_rCurrActionIndex
+= pLineAction
->getActionCount()-1;
2077 // else: line style is default
2078 // (i.e. invisible), don't generate action
2083 case META_RECT_ACTION
:
2085 const Rectangle
& rRect(
2086 static_cast<MetaRectAction
*>(pCurrAct
)->GetRect() );
2088 if( rRect
.IsEmpty() )
2091 const OutDevState
& rState( getState( rStates
) );
2092 const ::basegfx::B2DPoint
aTopLeftPixel(
2093 rState
.mapModeTransform
* ::vcl::unotools::b2DPointFromPoint( rRect
.TopLeft() ) );
2094 const ::basegfx::B2DPoint
aBottomRightPixel(
2095 rState
.mapModeTransform
* ::vcl::unotools::b2DPointFromPoint( rRect
.BottomRight() ) +
2096 // #121100# OutputDevice::DrawRect() fills
2097 // rectangles Apple-like, i.e. with one
2098 // additional pixel to the right and bottom.
2099 ::basegfx::B2DPoint(1,1) );
2101 createFillAndStroke( ::basegfx::tools::createPolygonFromRect(
2102 ::basegfx::B2DRange( aTopLeftPixel
,
2103 aBottomRightPixel
)),
2108 case META_ROUNDRECT_ACTION
:
2110 const Rectangle
& rRect(
2111 static_cast<MetaRoundRectAction
*>(pCurrAct
)->GetRect());
2113 if( rRect
.IsEmpty() )
2116 ::basegfx::B2DPolygon
aPoly(
2117 ::basegfx::tools::createPolygonFromRect(
2118 ::basegfx::B2DRange(
2119 ::vcl::unotools::b2DPointFromPoint( rRect
.TopLeft() ),
2120 ::vcl::unotools::b2DPointFromPoint( rRect
.BottomRight() ) +
2121 ::basegfx::B2DPoint(1,1) ),
2122 ( (double) static_cast<MetaRoundRectAction
*>(pCurrAct
)->GetHorzRound() ) / rRect
.GetWidth(),
2123 ( (double) static_cast<MetaRoundRectAction
*>(pCurrAct
)->GetVertRound() ) / rRect
.GetHeight() ) );
2124 aPoly
.transform( getState( rStates
).mapModeTransform
);
2126 createFillAndStroke( aPoly
,
2131 case META_ELLIPSE_ACTION
:
2133 const Rectangle
& rRect(
2134 static_cast<MetaEllipseAction
*>(pCurrAct
)->GetRect() );
2136 if( rRect
.IsEmpty() )
2139 const ::basegfx::B2DRange
aRange(
2140 ::vcl::unotools::b2DPointFromPoint( rRect
.TopLeft() ),
2141 ::vcl::unotools::b2DPointFromPoint( rRect
.BottomRight() ) +
2142 ::basegfx::B2DPoint(1,1) );
2144 ::basegfx::B2DPolygon
aPoly(
2145 ::basegfx::tools::createPolygonFromEllipse(
2148 aRange
.getHeight() ));
2149 aPoly
.transform( getState( rStates
).mapModeTransform
);
2151 createFillAndStroke( aPoly
,
2156 case META_ARC_ACTION
:
2158 // TODO(F1): Missing basegfx functionality. Mind empty rects!
2159 const Polygon
aToolsPoly( static_cast<MetaArcAction
*>(pCurrAct
)->GetRect(),
2160 static_cast<MetaArcAction
*>(pCurrAct
)->GetStartPoint(),
2161 static_cast<MetaArcAction
*>(pCurrAct
)->GetEndPoint(), POLY_ARC
);
2162 ::basegfx::B2DPolygon
aPoly( aToolsPoly
.getB2DPolygon() );
2163 aPoly
.transform( getState( rStates
).mapModeTransform
);
2165 createFillAndStroke( aPoly
,
2170 case META_PIE_ACTION
:
2172 // TODO(F1): Missing basegfx functionality. Mind empty rects!
2173 const Polygon
aToolsPoly( static_cast<MetaPieAction
*>(pCurrAct
)->GetRect(),
2174 static_cast<MetaPieAction
*>(pCurrAct
)->GetStartPoint(),
2175 static_cast<MetaPieAction
*>(pCurrAct
)->GetEndPoint(), POLY_PIE
);
2176 ::basegfx::B2DPolygon
aPoly( aToolsPoly
.getB2DPolygon() );
2177 aPoly
.transform( getState( rStates
).mapModeTransform
);
2179 createFillAndStroke( aPoly
,
2184 case META_CHORD_ACTION
:
2186 // TODO(F1): Missing basegfx functionality. Mind empty rects!
2187 const Polygon
aToolsPoly( static_cast<MetaChordAction
*>(pCurrAct
)->GetRect(),
2188 static_cast<MetaChordAction
*>(pCurrAct
)->GetStartPoint(),
2189 static_cast<MetaChordAction
*>(pCurrAct
)->GetEndPoint(), POLY_CHORD
);
2190 ::basegfx::B2DPolygon
aPoly( aToolsPoly
.getB2DPolygon() );
2191 aPoly
.transform( getState( rStates
).mapModeTransform
);
2193 createFillAndStroke( aPoly
,
2198 case META_POLYLINE_ACTION
:
2200 const OutDevState
& rState( getState( rStates
) );
2201 if( rState
.lineColor
.getLength() ||
2202 rState
.fillColor
.getLength() )
2204 MetaPolyLineAction
* pPolyLineAct
= static_cast<MetaPolyLineAction
*>(pCurrAct
);
2206 const LineInfo
& rLineInfo( pPolyLineAct
->GetLineInfo() );
2207 ::basegfx::B2DPolygon
aPoly( pPolyLineAct
->GetPolygon().getB2DPolygon() );
2208 aPoly
.transform( rState
.mapModeTransform
);
2210 ActionSharedPtr pLineAction
;
2212 if( rLineInfo
.IsDefault() )
2214 // plain hair line polygon
2216 internal::PolyPolyActionFactory::createLinePolyPolyAction(
2217 ::basegfx::B2DPolyPolygon(aPoly
),
2223 maActions
.push_back(
2226 io_rCurrActionIndex
) );
2228 io_rCurrActionIndex
+= pLineAction
->getActionCount()-1;
2231 else if( LINE_NONE
!= rLineInfo
.GetStyle() )
2233 // 'thick' line polygon
2234 rendering::StrokeAttributes aStrokeAttributes
;
2236 setupStrokeAttributes( aStrokeAttributes
,
2241 internal::PolyPolyActionFactory::createPolyPolyAction(
2242 ::basegfx::B2DPolyPolygon(aPoly
),
2245 aStrokeAttributes
) ;
2249 maActions
.push_back(
2252 io_rCurrActionIndex
) );
2254 io_rCurrActionIndex
+= pLineAction
->getActionCount()-1;
2257 // else: line style is default
2258 // (i.e. invisible), don't generate action
2263 case META_POLYGON_ACTION
:
2265 ::basegfx::B2DPolygon
aPoly( static_cast<MetaPolygonAction
*>(pCurrAct
)->GetPolygon().getB2DPolygon() );
2266 aPoly
.transform( getState( rStates
).mapModeTransform
);
2267 createFillAndStroke( aPoly
,
2272 case META_POLYPOLYGON_ACTION
:
2274 ::basegfx::B2DPolyPolygon
aPoly( static_cast<MetaPolyPolygonAction
*>(pCurrAct
)->GetPolyPolygon().getB2DPolyPolygon() );
2275 aPoly
.transform( getState( rStates
).mapModeTransform
);
2276 createFillAndStroke( aPoly
,
2281 case META_BMP_ACTION
:
2283 MetaBmpAction
* pAct
= static_cast<MetaBmpAction
*>(pCurrAct
);
2285 ActionSharedPtr
pBmpAction(
2286 internal::BitmapActionFactory::createBitmapAction(
2288 getState( rStates
).mapModeTransform
*
2289 ::vcl::unotools::b2DPointFromPoint( pAct
->GetPoint() ),
2291 getState( rStates
) ) );
2295 maActions
.push_back(
2298 io_rCurrActionIndex
) );
2300 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2305 case META_BMPSCALE_ACTION
:
2307 MetaBmpScaleAction
* pAct
= static_cast<MetaBmpScaleAction
*>(pCurrAct
);
2309 ActionSharedPtr
pBmpAction(
2310 internal::BitmapActionFactory::createBitmapAction(
2312 getState( rStates
).mapModeTransform
*
2313 ::vcl::unotools::b2DPointFromPoint( pAct
->GetPoint() ),
2314 getState( rStates
).mapModeTransform
*
2315 ::vcl::unotools::b2DSizeFromSize( pAct
->GetSize() ),
2317 getState( rStates
) ) );
2321 maActions
.push_back(
2324 io_rCurrActionIndex
) );
2326 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2331 case META_BMPSCALEPART_ACTION
:
2333 MetaBmpScalePartAction
* pAct
= static_cast<MetaBmpScalePartAction
*>(pCurrAct
);
2335 // crop bitmap to given source rectangle (no
2336 // need to copy and convert the whole bitmap)
2337 Bitmap
aBmp( pAct
->GetBitmap() );
2338 const Rectangle
aCropRect( pAct
->GetSrcPoint(),
2339 pAct
->GetSrcSize() );
2340 aBmp
.Crop( aCropRect
);
2342 ActionSharedPtr
pBmpAction(
2343 internal::BitmapActionFactory::createBitmapAction(
2345 getState( rStates
).mapModeTransform
*
2346 ::vcl::unotools::b2DPointFromPoint( pAct
->GetDestPoint() ),
2347 getState( rStates
).mapModeTransform
*
2348 ::vcl::unotools::b2DSizeFromSize( pAct
->GetDestSize() ),
2350 getState( rStates
) ) );
2354 maActions
.push_back(
2357 io_rCurrActionIndex
) );
2359 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2364 case META_BMPEX_ACTION
:
2366 MetaBmpExAction
* pAct
= static_cast<MetaBmpExAction
*>(pCurrAct
);
2368 ActionSharedPtr
pBmpAction(
2369 internal::BitmapActionFactory::createBitmapAction(
2370 pAct
->GetBitmapEx(),
2371 getState( rStates
).mapModeTransform
*
2372 ::vcl::unotools::b2DPointFromPoint( pAct
->GetPoint() ),
2374 getState( rStates
) ) );
2378 maActions
.push_back(
2381 io_rCurrActionIndex
) );
2383 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2388 case META_BMPEXSCALE_ACTION
:
2390 MetaBmpExScaleAction
* pAct
= static_cast<MetaBmpExScaleAction
*>(pCurrAct
);
2392 ActionSharedPtr
pBmpAction(
2393 internal::BitmapActionFactory::createBitmapAction(
2394 pAct
->GetBitmapEx(),
2395 getState( rStates
).mapModeTransform
*
2396 ::vcl::unotools::b2DPointFromPoint( pAct
->GetPoint() ),
2397 getState( rStates
).mapModeTransform
*
2398 ::vcl::unotools::b2DSizeFromSize( pAct
->GetSize() ),
2400 getState( rStates
) ) );
2404 maActions
.push_back(
2407 io_rCurrActionIndex
) );
2409 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2414 case META_BMPEXSCALEPART_ACTION
:
2416 MetaBmpExScalePartAction
* pAct
= static_cast<MetaBmpExScalePartAction
*>(pCurrAct
);
2418 // crop bitmap to given source rectangle (no
2419 // need to copy and convert the whole bitmap)
2420 BitmapEx
aBmp( pAct
->GetBitmapEx() );
2421 const Rectangle
aCropRect( pAct
->GetSrcPoint(),
2422 pAct
->GetSrcSize() );
2423 aBmp
.Crop( aCropRect
);
2425 ActionSharedPtr
pBmpAction(
2426 internal::BitmapActionFactory::createBitmapAction(
2428 getState( rStates
).mapModeTransform
*
2429 ::vcl::unotools::b2DPointFromPoint( pAct
->GetDestPoint() ),
2430 getState( rStates
).mapModeTransform
*
2431 ::vcl::unotools::b2DSizeFromSize( pAct
->GetDestSize() ),
2433 getState( rStates
) ) );
2437 maActions
.push_back(
2440 io_rCurrActionIndex
) );
2442 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2447 case META_MASK_ACTION
:
2449 MetaMaskAction
* pAct
= static_cast<MetaMaskAction
*>(pCurrAct
);
2451 // create masked BitmapEx right here, as the
2452 // canvas does not provide equivalent
2454 BitmapEx
aBmp( createMaskBmpEx( pAct
->GetBitmap(),
2455 pAct
->GetColor() ));
2457 ActionSharedPtr
pBmpAction(
2458 internal::BitmapActionFactory::createBitmapAction(
2460 getState( rStates
).mapModeTransform
*
2461 ::vcl::unotools::b2DPointFromPoint( pAct
->GetPoint() ),
2463 getState( rStates
) ) );
2467 maActions
.push_back(
2470 io_rCurrActionIndex
) );
2472 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2477 case META_MASKSCALE_ACTION
:
2479 MetaMaskScaleAction
* pAct
= static_cast<MetaMaskScaleAction
*>(pCurrAct
);
2481 // create masked BitmapEx right here, as the
2482 // canvas does not provide equivalent
2484 BitmapEx
aBmp( createMaskBmpEx( pAct
->GetBitmap(),
2485 pAct
->GetColor() ));
2487 ActionSharedPtr
pBmpAction(
2488 internal::BitmapActionFactory::createBitmapAction(
2490 getState( rStates
).mapModeTransform
*
2491 ::vcl::unotools::b2DPointFromPoint( pAct
->GetPoint() ),
2492 getState( rStates
).mapModeTransform
*
2493 ::vcl::unotools::b2DSizeFromSize( pAct
->GetSize() ),
2495 getState( rStates
) ) );
2499 maActions
.push_back(
2502 io_rCurrActionIndex
) );
2504 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2509 case META_MASKSCALEPART_ACTION
:
2511 MetaMaskScalePartAction
* pAct
= static_cast<MetaMaskScalePartAction
*>(pCurrAct
);
2513 // create masked BitmapEx right here, as the
2514 // canvas does not provide equivalent
2516 BitmapEx
aBmp( createMaskBmpEx( pAct
->GetBitmap(),
2517 pAct
->GetColor() ));
2519 // crop bitmap to given source rectangle (no
2520 // need to copy and convert the whole bitmap)
2521 const Rectangle
aCropRect( pAct
->GetSrcPoint(),
2522 pAct
->GetSrcSize() );
2523 aBmp
.Crop( aCropRect
);
2525 ActionSharedPtr
pBmpAction(
2526 internal::BitmapActionFactory::createBitmapAction(
2528 getState( rStates
).mapModeTransform
*
2529 ::vcl::unotools::b2DPointFromPoint( pAct
->GetDestPoint() ),
2530 getState( rStates
).mapModeTransform
*
2531 ::vcl::unotools::b2DSizeFromSize( pAct
->GetDestSize() ),
2533 getState( rStates
) ) );
2537 maActions
.push_back(
2540 io_rCurrActionIndex
) );
2542 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2547 case META_GRADIENTEX_ACTION
:
2548 // TODO(F1): use native Canvas gradients here
2549 // action is ignored here, because redundant to META_GRADIENT_ACTION
2552 case META_WALLPAPER_ACTION
:
2556 case META_TRANSPARENT_ACTION
:
2558 const OutDevState
& rState( getState( rStates
) );
2559 if( rState
.lineColor
.getLength() ||
2560 rState
.fillColor
.getLength() )
2562 MetaTransparentAction
* pAct
= static_cast<MetaTransparentAction
*>(pCurrAct
);
2563 ::basegfx::B2DPolyPolygon
aPoly( pAct
->GetPolyPolygon().getB2DPolyPolygon() );
2564 aPoly
.transform( rState
.mapModeTransform
);
2566 ActionSharedPtr
pPolyAction(
2567 internal::PolyPolyActionFactory::createPolyPolyAction(
2571 pAct
->GetTransparence() ) );
2575 maActions
.push_back(
2578 io_rCurrActionIndex
) );
2580 io_rCurrActionIndex
+= pPolyAction
->getActionCount()-1;
2586 case META_FLOATTRANSPARENT_ACTION
:
2588 MetaFloatTransparentAction
* pAct
= static_cast<MetaFloatTransparentAction
*>(pCurrAct
);
2590 internal::MtfAutoPtr
pMtf(
2591 new ::GDIMetaFile( pAct
->GetGDIMetaFile() ) );
2593 // TODO(P2): Use native canvas gradients here (saves a lot of UNO calls)
2594 internal::GradientAutoPtr
pGradient(
2595 new Gradient( pAct
->GetGradient() ) );
2597 DBG_TESTSOLARMUTEX();
2599 ActionSharedPtr
pFloatTransAction(
2600 internal::TransparencyGroupActionFactory::createTransparencyGroupAction(
2604 getState( rStates
).mapModeTransform
*
2605 ::vcl::unotools::b2DPointFromPoint( pAct
->GetPoint() ),
2606 getState( rStates
).mapModeTransform
*
2607 ::vcl::unotools::b2DSizeFromSize( pAct
->GetSize() ),
2609 getState( rStates
) ) );
2611 if( pFloatTransAction
)
2613 maActions
.push_back(
2616 io_rCurrActionIndex
) );
2618 io_rCurrActionIndex
+= pFloatTransAction
->getActionCount()-1;
2623 case META_TEXT_ACTION
:
2625 MetaTextAction
* pAct
= static_cast<MetaTextAction
*>(pCurrAct
);
2626 XubString sText
= XubString( pAct
->GetText() );
2628 if( rVDev
.GetDigitLanguage())
2629 convertToLocalizedNumerals ( sText
,rVDev
.GetDigitLanguage() );
2635 pAct
->GetLen() == (USHORT
)STRING_LEN
? pAct
->GetText().Len() - pAct
->GetIndex() : pAct
->GetLen(),
2638 bSubsettableActions
);
2642 case META_TEXTARRAY_ACTION
:
2644 MetaTextArrayAction
* pAct
= static_cast<MetaTextArrayAction
*>(pCurrAct
);
2645 XubString sText
= XubString( pAct
->GetText() );
2647 if( rVDev
.GetDigitLanguage())
2648 convertToLocalizedNumerals ( sText
,rVDev
.GetDigitLanguage() );
2654 pAct
->GetLen() == (USHORT
)STRING_LEN
? pAct
->GetText().Len() - pAct
->GetIndex() : pAct
->GetLen(),
2657 bSubsettableActions
);
2661 case META_TEXTLINE_ACTION
:
2663 MetaTextLineAction
* pAct
= static_cast<MetaTextLineAction
*>(pCurrAct
);
2665 const OutDevState
& rState( getState( rStates
) );
2666 const ::Size
aBaselineOffset( tools::getBaselineOffset( rState
,
2668 const ::Point
aStartPoint( pAct
->GetStartPoint() );
2669 const ::basegfx::B2DSize
aSize( rState
.mapModeTransform
*
2670 ::basegfx::B2DSize(pAct
->GetWidth(),
2673 ActionSharedPtr
pPolyAction(
2674 PolyPolyActionFactory::createPolyPolyAction(
2675 tools::createTextLinesPolyPolygon(
2676 rState
.mapModeTransform
*
2677 ::basegfx::B2DPoint(
2678 ::vcl::unotools::b2DPointFromPoint(pAct
->GetStartPoint()) +
2679 ::vcl::unotools::b2DSizeFromSize(aBaselineOffset
)),
2681 tools::createTextLineInfo( rVDev
,
2686 if( pPolyAction
.get() )
2688 maActions
.push_back(
2691 io_rCurrActionIndex
) );
2693 io_rCurrActionIndex
+= pPolyAction
->getActionCount()-1;
2698 case META_TEXTRECT_ACTION
:
2700 MetaTextRectAction
* pAct
= static_cast<MetaTextRectAction
*>(pCurrAct
);
2702 pushState( rStates
, PUSH_ALL
);
2704 // use the VDev to break up the text rect
2705 // action into readily formatted lines
2706 GDIMetaFile aTmpMtf
;
2707 rVDev
.AddTextRectActions( pAct
->GetRect(),
2712 createActions( aTmpMtf
,
2714 bSubsettableActions
);
2716 popState( rStates
);
2721 case META_STRETCHTEXT_ACTION
:
2723 MetaStretchTextAction
* pAct
= static_cast<MetaStretchTextAction
*>(pCurrAct
);
2724 XubString sText
= XubString( pAct
->GetText() );
2726 if( rVDev
.GetDigitLanguage())
2727 convertToLocalizedNumerals ( sText
,rVDev
.GetDigitLanguage() );
2729 const USHORT
nLen( pAct
->GetLen() == (USHORT
)STRING_LEN
?
2730 pAct
->GetText().Len() - pAct
->GetIndex() : pAct
->GetLen() );
2732 // #i70897# Nothing to do, actually...
2736 // have to fit the text into the given
2737 // width. This is achieved by internally
2738 // generating a DX array, and uniformly
2739 // distributing the excess/insufficient width
2740 // to every logical character.
2741 ::boost::scoped_array
< sal_Int32
> pDXArray( new sal_Int32
[nLen
] );
2743 rVDev
.GetTextArray( pAct
->GetText(), pDXArray
.get(),
2744 pAct
->GetIndex(), pAct
->GetLen() );
2746 const sal_Int32
nWidthDifference( pAct
->GetWidth() - pDXArray
[ nLen
-1 ] );
2748 // Last entry of pDXArray contains total width of the text
2749 sal_Int32
* p
=pDXArray
.get();
2750 for( USHORT i
=1; i
<=nLen
; ++i
)
2752 // calc ratio for every array entry, to
2753 // distribute rounding errors 'evenly'
2754 // across the characters. Note that each
2755 // entry represents the 'end' position of
2756 // the corresponding character, thus, we
2757 // let i run from 1 to nLen.
2758 *p
++ += (sal_Int32
)i
*nWidthDifference
/nLen
;
2765 pAct
->GetLen() == (USHORT
)STRING_LEN
? pAct
->GetText().Len() - pAct
->GetIndex() : pAct
->GetLen(),
2768 bSubsettableActions
);
2774 "Unknown meta action type encountered" );
2778 // increment action index (each mtf action counts _at
2779 // least_ one. Some count for more, therefore,
2780 // io_rCurrActionIndex is sometimes incremented by
2781 // pAct->getActionCount()-1 above, the -1 being the
2782 // correction for the unconditional increment here).
2783 ++io_rCurrActionIndex
;
2792 class ActionRenderer
2795 ActionRenderer( const ::basegfx::B2DHomMatrix
& rTransformation
) :
2796 maTransformation( rTransformation
),
2806 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction
& rAction
)
2808 // ANDing the result. We want to fail if at least
2809 // one action failed.
2810 mbRet
&= rAction
.mpAction
->render( maTransformation
);
2813 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction
& rAction
,
2814 const Action::Subset
& rSubset
)
2816 // ANDing the result. We want to fail if at least
2817 // one action failed.
2818 mbRet
&= rAction
.mpAction
->render( maTransformation
,
2823 ::basegfx::B2DHomMatrix maTransformation
;
2830 AreaQuery( const ::basegfx::B2DHomMatrix
& rTransformation
) :
2831 maTransformation( rTransformation
),
2838 return true; // nothing can fail here
2841 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction
& rAction
)
2843 maBounds
.expand( rAction
.mpAction
->getBounds( maTransformation
) );
2846 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction
& rAction
,
2847 const Action::Subset
& rSubset
)
2849 maBounds
.expand( rAction
.mpAction
->getBounds( maTransformation
,
2853 ::basegfx::B2DRange
getBounds() const
2859 ::basegfx::B2DHomMatrix maTransformation
;
2860 ::basegfx::B2DRange maBounds
;
2863 // Doing that via inline class. Compilers tend to not inline free
2865 struct UpperBoundActionIndexComparator
2867 bool operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction
& rLHS
,
2868 const ::cppcanvas::internal::ImplRenderer::MtfAction
& rRHS
)
2870 const sal_Int32
nLHSCount( rLHS
.mpAction
?
2871 rLHS
.mpAction
->getActionCount() : 0 );
2872 const sal_Int32
nRHSCount( rRHS
.mpAction
?
2873 rRHS
.mpAction
->getActionCount() : 0 );
2875 // compare end of action range, to have an action selected
2876 // by lower_bound even if the requested index points in
2877 // the middle of the action's range
2878 return rLHS
.mnOrigIndex
+ nLHSCount
< rRHS
.mnOrigIndex
+ nRHSCount
;
2882 /** Algorithm to apply given functor to a subset range
2886 Functor to call for each element of the subset
2887 range. Must provide the following method signatures:
2888 bool result() (returning false if operation failed)
2891 template< typename Functor
> bool
2892 forSubsetRange( Functor
& rFunctor
,
2893 ImplRenderer::ActionVector::const_iterator aRangeBegin
,
2894 ImplRenderer::ActionVector::const_iterator aRangeEnd
,
2895 sal_Int32 nStartIndex
,
2896 sal_Int32 nEndIndex
,
2897 const ImplRenderer::ActionVector::const_iterator
& rEnd
)
2899 if( aRangeBegin
== aRangeEnd
)
2901 // only a single action. Setup subset, and call functor
2902 Action::Subset aSubset
;
2903 aSubset
.mnSubsetBegin
= ::std::max( sal_Int32( 0 ),
2904 nStartIndex
- aRangeBegin
->mnOrigIndex
);
2905 aSubset
.mnSubsetEnd
= ::std::min( aRangeBegin
->mpAction
->getActionCount(),
2906 nEndIndex
- aRangeBegin
->mnOrigIndex
);
2908 ENSURE_OR_RETURN( aSubset
.mnSubsetBegin
>= 0 && aSubset
.mnSubsetEnd
>= 0,
2909 "ImplRenderer::forSubsetRange(): Invalid indices" );
2911 rFunctor( *aRangeBegin
, aSubset
);
2915 // more than one action.
2917 // render partial first, full intermediate, and
2918 // partial last action
2919 Action::Subset aSubset
;
2920 aSubset
.mnSubsetBegin
= ::std::max( sal_Int32( 0 ),
2921 nStartIndex
- aRangeBegin
->mnOrigIndex
);
2922 aSubset
.mnSubsetEnd
= aRangeBegin
->mpAction
->getActionCount();
2924 ENSURE_OR_RETURN( aSubset
.mnSubsetBegin
>= 0 && aSubset
.mnSubsetEnd
>= 0,
2925 "ImplRenderer::forSubsetRange(): Invalid indices" );
2927 rFunctor( *aRangeBegin
, aSubset
);
2929 // first action rendered, skip to next
2932 // render full middle actions
2933 while( aRangeBegin
!= aRangeEnd
)
2934 rFunctor( *aRangeBegin
++ );
2936 if( aRangeEnd
== rEnd
||
2937 aRangeEnd
->mnOrigIndex
> nEndIndex
)
2939 // aRangeEnd denotes end of action vector,
2943 // nEndIndex references something _after_
2944 // aRangeBegin, but _before_ aRangeEnd
2946 // either way: no partial action left
2947 return rFunctor
.result();
2950 aSubset
.mnSubsetBegin
= 0;
2951 aSubset
.mnSubsetEnd
= nEndIndex
- aRangeEnd
->mnOrigIndex
;
2953 ENSURE_OR_RETURN( aSubset
.mnSubsetBegin
>= 0 && aSubset
.mnSubsetEnd
>= 0,
2954 "ImplRenderer::forSubsetRange(): Invalid indices" );
2956 rFunctor( *aRangeEnd
, aSubset
);
2959 return rFunctor
.result();
2963 bool ImplRenderer::getSubsetIndices( sal_Int32
& io_rStartIndex
,
2964 sal_Int32
& io_rEndIndex
,
2965 ActionVector::const_iterator
& o_rRangeBegin
,
2966 ActionVector::const_iterator
& o_rRangeEnd
) const
2968 ENSURE_OR_RETURN( io_rStartIndex
<=io_rEndIndex
,
2969 "ImplRenderer::getSubsetIndices(): invalid action range" );
2971 ENSURE_OR_RETURN( !maActions
.empty(),
2972 "ImplRenderer::getSubsetIndices(): no actions to render" );
2974 const sal_Int32
nMinActionIndex( maActions
.front().mnOrigIndex
);
2975 const sal_Int32
nMaxActionIndex( maActions
.back().mnOrigIndex
+
2976 maActions
.back().mpAction
->getActionCount() );
2978 // clip given range to permissible values (there might be
2979 // ranges before and behind the valid indices)
2980 io_rStartIndex
= ::std::max( nMinActionIndex
,
2982 io_rEndIndex
= ::std::min( nMaxActionIndex
,
2985 if( io_rStartIndex
== io_rEndIndex
||
2986 io_rStartIndex
> io_rEndIndex
)
2988 // empty range, don't render anything. The second
2989 // condition e.g. happens if the requested range lies
2990 // fully before or behind the valid action indices.
2995 const ActionVector::const_iterator
aBegin( maActions
.begin() );
2996 const ActionVector::const_iterator
aEnd( maActions
.end() );
2999 // find start and end action
3000 // =========================
3001 o_rRangeBegin
= ::std::lower_bound( aBegin
, aEnd
,
3002 MtfAction( ActionSharedPtr(), io_rStartIndex
),
3003 UpperBoundActionIndexComparator() );
3004 o_rRangeEnd
= ::std::lower_bound( aBegin
, aEnd
,
3005 MtfAction( ActionSharedPtr(), io_rEndIndex
),
3006 UpperBoundActionIndexComparator() );
3012 // ====================================================================
3014 ImplRenderer::ImplRenderer( const CanvasSharedPtr
& rCanvas
,
3015 const GDIMetaFile
& rMtf
,
3016 const Parameters
& rParams
) :
3017 CanvasGraphicHelper( rCanvas
),
3020 RTL_LOGFILE_CONTEXT( aLog
, "::cppcanvas::internal::ImplRenderer::ImplRenderer(mtf)" );
3022 OSL_ENSURE( rCanvas
.get() != NULL
&& rCanvas
->getUNOCanvas().is(),
3023 "ImplRenderer::ImplRenderer(): Invalid canvas" );
3024 OSL_ENSURE( rCanvas
->getUNOCanvas()->getDevice().is(),
3025 "ImplRenderer::ImplRenderer(): Invalid graphic device" );
3027 // make sure canvas and graphic device are valid; action
3028 // creation don't check that every time
3029 if( rCanvas
.get() == NULL
||
3030 !rCanvas
->getUNOCanvas().is() ||
3031 !rCanvas
->getUNOCanvas()->getDevice().is() )
3033 // leave actions empty
3037 VectorOfOutDevStates aStateStack
;
3039 VirtualDevice aVDev
;
3040 aVDev
.EnableOutput( FALSE
);
3042 // Setup VDev for state tracking and mapping
3043 // =========================================
3045 aVDev
.SetMapMode( rMtf
.GetPrefMapMode() );
3047 const Size
aMtfSize( rMtf
.GetPrefSize() );
3048 const Size
aMtfSizePixPre( aVDev
.LogicToPixel( aMtfSize
,
3049 rMtf
.GetPrefMapMode() ) );
3050 const Point aEmptyPt
;
3051 const Point
aMtfOriginPix( aVDev
.LogicToPixel( aEmptyPt
) );
3053 // #i44110# correct null-sized output - there are shapes
3054 // which have zero size in at least one dimension
3055 const Size
aMtfSizePix( ::std::max( aMtfSizePixPre
.Width(), 1L ),
3056 ::std::max( aMtfSizePixPre
.Height(), 1L ) );
3058 sal_Int32
nCurrActions(0);
3059 ActionFactoryParameters
aParms(aStateStack
,
3066 clearStateStack( aStateStack
);
3068 // Setup local state, such that the metafile renders
3069 // itself into a one-by-one square at the origin for
3070 // identity view and render transformations
3071 getState( aStateStack
).transform
.scale( 1.0 / aMtfSizePix
.Width(),
3072 1.0 / aMtfSizePix
.Height() );
3074 tools::calcLogic2PixelAffineTransform( getState( aStateStack
).mapModeTransform
,
3077 ColorSharedPtr
pColor( getCanvas()->createColor() );
3079 // setup default text color to black
3080 getState( aStateStack
).textColor
=
3081 getState( aStateStack
).textFillColor
=
3082 getState( aStateStack
).textLineColor
= pColor
->getDeviceColor( 0x000000FF );
3084 // apply overrides from the Parameters struct
3085 if( rParams
.maFillColor
.isValid() )
3087 getState( aStateStack
).isFillColorSet
= true;
3088 getState( aStateStack
).fillColor
= pColor
->getDeviceColor( rParams
.maFillColor
.getValue() );
3090 if( rParams
.maLineColor
.isValid() )
3092 getState( aStateStack
).isLineColorSet
= true;
3093 getState( aStateStack
).lineColor
= pColor
->getDeviceColor( rParams
.maLineColor
.getValue() );
3095 if( rParams
.maTextColor
.isValid() )
3097 getState( aStateStack
).isTextFillColorSet
= true;
3098 getState( aStateStack
).isTextLineColorSet
= true;
3099 getState( aStateStack
).textColor
=
3100 getState( aStateStack
).textFillColor
=
3101 getState( aStateStack
).textLineColor
= pColor
->getDeviceColor( rParams
.maTextColor
.getValue() );
3103 if( rParams
.maFontName
.isValid() ||
3104 rParams
.maFontWeight
.isValid() ||
3105 rParams
.maFontLetterForm
.isValid() ||
3106 rParams
.maFontUnderline
.isValid() )
3108 ::cppcanvas::internal::OutDevState
& rState
= getState( aStateStack
);
3110 rState
.xFont
= createFont( rState
.fontRotation
,
3111 ::Font(), // default font
3116 memset (aObjects
, 0, sizeof (aObjects
));
3117 mbMultipart
= false;
3119 createActions( const_cast<GDIMetaFile
&>(rMtf
), // HACK(Q2):
3128 true // TODO(P1): make subsettability configurable
3132 ImplRenderer::ImplRenderer( const CanvasSharedPtr
& rCanvas
,
3133 const BitmapEx
& rBmpEx
,
3134 const Parameters
& rParams
) :
3135 CanvasGraphicHelper( rCanvas
),
3138 // TODO(F3): property modification parameters are
3139 // currently ignored for Bitmaps
3142 RTL_LOGFILE_CONTEXT( aLog
, "::cppcanvas::internal::ImplRenderer::ImplRenderer(bitmap)" );
3144 OSL_ENSURE( rCanvas
.get() != NULL
&& rCanvas
->getUNOCanvas().is(),
3145 "ImplRenderer::ImplRenderer(): Invalid canvas" );
3146 OSL_ENSURE( rCanvas
->getUNOCanvas()->getDevice().is(),
3147 "ImplRenderer::ImplRenderer(): Invalid graphic device" );
3149 // make sure canvas and graphic device are valid; action
3150 // creation don't check that every time
3151 if( rCanvas
.get() == NULL
||
3152 !rCanvas
->getUNOCanvas().is() ||
3153 !rCanvas
->getUNOCanvas()->getDevice().is() )
3155 // leave actions empty
3161 const Size
aBmpSize( rBmpEx
.GetSizePixel() );
3163 // Setup local state, such that the bitmap renders itself
3164 // into a one-by-one square for identity view and render
3166 aState
.transform
.scale( 1.0 / aBmpSize
.Width(),
3167 1.0 / aBmpSize
.Height() );
3169 // create a single action for the provided BitmapEx
3170 maActions
.push_back(
3172 BitmapActionFactory::createBitmapAction(
3174 ::basegfx::B2DPoint(),
3180 ImplRenderer::~ImplRenderer()
3184 bool ImplRenderer::drawSubset( sal_Int32 nStartIndex
,
3185 sal_Int32 nEndIndex
) const
3187 RTL_LOGFILE_CONTEXT( aLog
, "::cppcanvas::internal::ImplRenderer::drawSubset()" );
3189 ActionVector::const_iterator aRangeBegin
;
3190 ActionVector::const_iterator aRangeEnd
;
3194 if( !getSubsetIndices( nStartIndex
, nEndIndex
,
3195 aRangeBegin
, aRangeEnd
) )
3196 return true; // nothing to render (but _that_ was successful)
3198 // now, aRangeBegin references the action in which the
3199 // subset rendering must start, and aRangeEnd references
3200 // the action in which the subset rendering must end (it
3201 // might also end right at the start of the referenced
3202 // action, such that zero of that action needs to be
3206 // render subset of actions
3207 // ========================
3209 ::basegfx::B2DHomMatrix aMatrix
;
3210 ::canvas::tools::getRenderStateTransform( aMatrix
,
3213 ActionRenderer
aRenderer( aMatrix
);
3215 return forSubsetRange( aRenderer
,
3222 catch( uno::Exception
& )
3225 rtl::OUStringToOString(
3226 comphelper::anyToString( cppu::getCaughtException() ),
3227 RTL_TEXTENCODING_UTF8
).getStr() );
3229 // convert error to return value
3234 ::basegfx::B2DRange
ImplRenderer::getSubsetArea( sal_Int32 nStartIndex
,
3235 sal_Int32 nEndIndex
) const
3237 RTL_LOGFILE_CONTEXT( aLog
, "::cppcanvas::internal::ImplRenderer::getSubsetArea()" );
3239 ActionVector::const_iterator aRangeBegin
;
3240 ActionVector::const_iterator aRangeEnd
;
3242 if( !getSubsetIndices( nStartIndex
, nEndIndex
,
3243 aRangeBegin
, aRangeEnd
) )
3244 return ::basegfx::B2DRange(); // nothing to render -> empty range
3246 // now, aRangeBegin references the action in which the
3247 // subset querying must start, and aRangeEnd references
3248 // the action in which the subset querying must end (it
3249 // might also end right at the start of the referenced
3250 // action, such that zero of that action needs to be
3254 // query bounds for subset of actions
3255 // ==================================
3257 ::basegfx::B2DHomMatrix aMatrix
;
3258 ::canvas::tools::getRenderStateTransform( aMatrix
,
3261 AreaQuery
aQuery( aMatrix
);
3262 forSubsetRange( aQuery
,
3269 return aQuery
.getBounds();
3272 bool ImplRenderer::draw() const
3274 RTL_LOGFILE_CONTEXT( aLog
, "::cppcanvas::internal::ImplRenderer::draw()" );
3276 ::basegfx::B2DHomMatrix aMatrix
;
3277 ::canvas::tools::getRenderStateTransform( aMatrix
,
3282 return ::std::for_each( maActions
.begin(), maActions
.end(), ActionRenderer( aMatrix
) ).result();
3284 catch( uno::Exception
& )
3287 rtl::OUStringToOString(
3288 comphelper::anyToString( cppu::getCaughtException() ),
3289 RTL_TEXTENCODING_UTF8
).getStr() );