1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*************************************************************************
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6 * Copyright 2000, 2010 Oracle and/or its affiliates.
8 * OpenOffice.org - a multi-platform office productivity suite
10 * This file is part of OpenOffice.org.
12 * OpenOffice.org is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU Lesser General Public License version 3
14 * only, as published by the Free Software Foundation.
16 * OpenOffice.org is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Lesser General Public License version 3 for more details
20 * (a copy is included in the LICENSE file that accompanied this code).
22 * You should have received a copy of the GNU Lesser General Public License
23 * version 3 along with OpenOffice.org. If not, see
24 * <http://www.openoffice.org/license.html>
25 * for a copy of the LGPLv3 License.
27 ************************************************************************/
30 #include <canvas/debug.hxx>
31 #include <tools/diagnose_ex.h>
32 #include <canvas/verbosetrace.hxx>
33 #include <osl/mutex.hxx>
34 #include <vcl/svapp.hxx>
35 #include <rtl/logfile.hxx>
36 #include <comphelper/sequence.hxx>
37 #include <comphelper/anytostring.hxx>
38 #include <cppuhelper/exc_hlp.hxx>
39 #include <cppcanvas/canvas.hxx>
40 #include <com/sun/star/rendering/XGraphicDevice.hpp>
41 #include <com/sun/star/rendering/TexturingMode.hpp>
42 #include <com/sun/star/uno/Sequence.hxx>
43 #include <com/sun/star/geometry/RealPoint2D.hpp>
44 #include <com/sun/star/rendering/PanoseProportion.hpp>
45 #include <com/sun/star/rendering/ViewState.hpp>
46 #include <com/sun/star/rendering/RenderState.hpp>
47 #include <com/sun/star/rendering/XCanvasFont.hpp>
48 #include <com/sun/star/rendering/XPolyPolygon2D.hpp>
49 #include <com/sun/star/rendering/XCanvas.hpp>
50 #include <com/sun/star/rendering/PathCapType.hpp>
51 #include <com/sun/star/rendering/PathJoinType.hpp>
52 #include <basegfx/tools/canvastools.hxx>
53 #include <basegfx/tools/gradienttools.hxx>
54 #include <basegfx/numeric/ftools.hxx>
55 #include <basegfx/polygon/b2dpolypolygontools.hxx>
56 #include <basegfx/polygon/b2dpolygontools.hxx>
57 #include <basegfx/polygon/b2dpolygon.hxx>
58 #include <basegfx/polygon/b2dpolypolygon.hxx>
59 #include <basegfx/matrix/b2dhommatrix.hxx>
60 #include <basegfx/vector/b2dsize.hxx>
61 #include <basegfx/range/b2drectangle.hxx>
62 #include <basegfx/point/b2dpoint.hxx>
63 #include <basegfx/tuple/b2dtuple.hxx>
64 #include <basegfx/polygon/b2dpolygonclipper.hxx>
65 #include <basegfx/polygon/b2dpolypolygoncutter.hxx>
66 #include <canvas/canvastools.hxx>
67 #include <vcl/canvastools.hxx>
68 #include <vcl/salbtype.hxx>
69 #include <vcl/gdimtf.hxx>
70 #include <vcl/metaact.hxx>
71 #include <vcl/virdev.hxx>
72 #include <vcl/metric.hxx>
73 #include <vcl/graphictools.hxx>
74 #include <tools/poly.hxx>
75 #include <i18npool/mslangid.hxx>
76 #include <implrenderer.hxx>
78 #include <outdevstate.hxx>
80 #include <bitmapaction.hxx>
81 #include <lineaction.hxx>
82 #include <pointaction.hxx>
83 #include <polypolyaction.hxx>
84 #include <rendergraphicaction.hxx>
85 #include <textaction.hxx>
86 #include <transparencygroupaction.hxx>
90 #include <boost/scoped_array.hpp>
91 #include "mtftools.hxx"
92 #include "outdevstate.hxx"
93 #include <basegfx/matrix/b2dhommatrixtools.hxx>
95 #if OSL_DEBUG_LEVEL > 1
96 #define EMFP_DEBUG(x) x
101 using namespace ::com::sun::star
;
104 // free support functions
105 // ======================
108 template < class MetaActionType
> void setStateColor( MetaActionType
* pAct
,
110 uno::Sequence
< double >& rColorSequence
,
111 const cppcanvas::CanvasSharedPtr
& rCanvas
)
113 // set rIsColorSet and check for true at the same time
114 if( (rIsColorSet
=pAct
->IsSetting()) != false )
116 ::Color
aColor( pAct
->GetColor() );
118 // force alpha part of color to
119 // opaque. transparent painting is done
120 // explicitly via META_TRANSPARENT_ACTION
121 aColor
.SetTransparency(0);
122 //aColor.SetTransparency(128);
124 rColorSequence
= ::vcl::unotools::colorToDoubleSequence(
126 rCanvas
->getUNOCanvas()->getDevice()->getDeviceColorSpace() );
130 void setupStrokeAttributes( rendering::StrokeAttributes
& o_rStrokeAttributes
,
131 const ::cppcanvas::internal::ActionFactoryParameters
& rParms
,
132 const LineInfo
& rLineInfo
)
134 const ::basegfx::B2DSize
aWidth( rLineInfo
.GetWidth(), 0 );
135 o_rStrokeAttributes
.StrokeWidth
=
136 (rParms
.mrStates
.getState().mapModeTransform
* aWidth
).getX();
138 // setup reasonable defaults
139 o_rStrokeAttributes
.MiterLimit
= 15.0; // 1.0 was no good default; GDI+'s limit is 10.0, our's is 15.0
140 o_rStrokeAttributes
.StartCapType
= rendering::PathCapType::BUTT
;
141 o_rStrokeAttributes
.EndCapType
= rendering::PathCapType::BUTT
;
143 switch(rLineInfo
.GetLineJoin())
145 default: // B2DLINEJOIN_NONE, B2DLINEJOIN_MIDDLE
146 o_rStrokeAttributes
.JoinType
= rendering::PathJoinType::NONE
;
148 case basegfx::B2DLINEJOIN_BEVEL
:
149 o_rStrokeAttributes
.JoinType
= rendering::PathJoinType::BEVEL
;
151 case basegfx::B2DLINEJOIN_MITER
:
152 o_rStrokeAttributes
.JoinType
= rendering::PathJoinType::MITER
;
154 case basegfx::B2DLINEJOIN_ROUND
:
155 o_rStrokeAttributes
.JoinType
= rendering::PathJoinType::ROUND
;
159 if( LINE_DASH
== rLineInfo
.GetStyle() )
161 const ::cppcanvas::internal::OutDevState
& rState( rParms
.mrStates
.getState() );
163 // TODO(F1): Interpret OutDev::GetRefPoint() for the start of the dashing.
165 // interpret dash info only if explicitly enabled as
167 const ::basegfx::B2DSize
aDistance( rLineInfo
.GetDistance(), 0 );
168 const double nDistance( (rState
.mapModeTransform
* aDistance
).getX() );
170 const ::basegfx::B2DSize
aDashLen( rLineInfo
.GetDashLen(), 0 );
171 const double nDashLen( (rState
.mapModeTransform
* aDashLen
).getX() );
173 const ::basegfx::B2DSize
aDotLen( rLineInfo
.GetDotLen(), 0 );
174 const double nDotLen( (rState
.mapModeTransform
* aDotLen
).getX() );
176 const sal_Int32
nNumArryEntries( 2*rLineInfo
.GetDashCount() +
177 2*rLineInfo
.GetDotCount() );
179 o_rStrokeAttributes
.DashArray
.realloc( nNumArryEntries
);
180 double* pDashArray
= o_rStrokeAttributes
.DashArray
.getArray();
183 // iteratively fill dash array, first with dashs, then
185 // ===================================================
187 sal_Int32 nCurrEntry
=0;
189 for( sal_Int32 i
=0; i
<rLineInfo
.GetDashCount(); ++i
)
191 pDashArray
[nCurrEntry
++] = nDashLen
;
192 pDashArray
[nCurrEntry
++] = nDistance
;
194 for( sal_Int32 i
=0; i
<rLineInfo
.GetDotCount(); ++i
)
196 pDashArray
[nCurrEntry
++] = nDotLen
;
197 pDashArray
[nCurrEntry
++] = nDistance
;
203 /** Create masked BitmapEx, where the white areas of rBitmap are
204 transparent, and the other appear in rMaskColor.
206 BitmapEx
createMaskBmpEx( const Bitmap
& rBitmap
,
207 const ::Color
& rMaskColor
)
209 const ::Color
aWhite( COL_WHITE
);
210 BitmapPalette
aBiLevelPalette(2);
211 aBiLevelPalette
[0] = aWhite
;
212 aBiLevelPalette
[1] = rMaskColor
;
214 Bitmap
aMask( rBitmap
.CreateMask( aWhite
));
215 Bitmap
aSolid( rBitmap
.GetSizePixel(),
218 aSolid
.Erase( rMaskColor
);
220 return BitmapEx( aSolid
, aMask
);
223 /** Shameless rip from vcl/source/gdi/outdev3.cxx
225 Should consolidate, into something like basetxt...
227 sal_Unicode
getLocalizedChar( sal_Unicode nChar
, LanguageType eLang
)
229 // currently only conversion from ASCII digits is interesting
230 if( (nChar
< '0') || ('9' < nChar
) )
233 sal_Unicode
nOffset(0);
234 // eLang & LANGUAGE_MASK_PRIMARY catches language independent of region.
235 // CAVEAT! To some like Mongolian MS assigned the same primary language
236 // although the script type is different!
237 switch( eLang
& LANGUAGE_MASK_PRIMARY
)
242 case LANGUAGE_ARABIC_SAUDI_ARABIA
& LANGUAGE_MASK_PRIMARY
:
243 case LANGUAGE_URDU
& LANGUAGE_MASK_PRIMARY
:
244 case LANGUAGE_PUNJABI
& LANGUAGE_MASK_PRIMARY
: //???
245 nOffset
= 0x0660 - '0'; // arabic/persian/urdu
247 case LANGUAGE_BENGALI
& LANGUAGE_MASK_PRIMARY
:
248 nOffset
= 0x09E6 - '0'; // bengali
250 case LANGUAGE_BURMESE
& LANGUAGE_MASK_PRIMARY
:
251 nOffset
= 0x1040 - '0'; // burmese
253 case LANGUAGE_HINDI
& LANGUAGE_MASK_PRIMARY
:
254 nOffset
= 0x0966 - '0'; // devanagari
256 case LANGUAGE_GUJARATI
& LANGUAGE_MASK_PRIMARY
:
257 nOffset
= 0x0AE6 - '0'; // gujarati
259 case LANGUAGE_KANNADA
& LANGUAGE_MASK_PRIMARY
:
260 nOffset
= 0x0CE6 - '0'; // kannada
262 case LANGUAGE_KHMER
& LANGUAGE_MASK_PRIMARY
:
263 nOffset
= 0x17E0 - '0'; // khmer
265 case LANGUAGE_LAO
& LANGUAGE_MASK_PRIMARY
:
266 nOffset
= 0x0ED0 - '0'; // lao
268 case LANGUAGE_MALAYALAM
& LANGUAGE_MASK_PRIMARY
:
269 nOffset
= 0x0D66 - '0'; // malayalam
271 case LANGUAGE_MONGOLIAN
& LANGUAGE_MASK_PRIMARY
:
272 if (eLang
== LANGUAGE_MONGOLIAN_MONGOLIAN
)
273 nOffset
= 0x1810 - '0'; // mongolian
275 nOffset
= 0; // mongolian cyrillic
277 case LANGUAGE_ORIYA
& LANGUAGE_MASK_PRIMARY
:
278 nOffset
= 0x0B66 - '0'; // oriya
280 case LANGUAGE_TAMIL
& LANGUAGE_MASK_PRIMARY
:
281 nOffset
= 0x0BE7 - '0'; // tamil
283 case LANGUAGE_TELUGU
& LANGUAGE_MASK_PRIMARY
:
284 nOffset
= 0x0C66 - '0'; // telugu
286 case LANGUAGE_THAI
& LANGUAGE_MASK_PRIMARY
:
287 nOffset
= 0x0E50 - '0'; // thai
289 case LANGUAGE_TIBETAN
& LANGUAGE_MASK_PRIMARY
:
290 nOffset
= 0x0F20 - '0'; // tibetan
294 nChar
= sal::static_int_cast
<sal_Unicode
>(nChar
+ nOffset
);
298 void convertToLocalizedNumerals( XubString
& rStr
,
299 LanguageType eTextLanguage
)
301 const sal_Unicode
* pBase
= rStr
.GetBuffer();
302 const sal_Unicode
* pBegin
= pBase
+ 0;
303 const xub_StrLen nEndIndex
= rStr
.Len();
304 const sal_Unicode
* pEnd
= pBase
+ nEndIndex
;
306 for( ; pBegin
< pEnd
; ++pBegin
)
308 // TODO: are there non-digit localizations?
309 if( (*pBegin
>= '0') && (*pBegin
<= '9') )
311 // translate characters to local preference
312 sal_Unicode cChar
= getLocalizedChar( *pBegin
, eTextLanguage
);
313 if( cChar
!= *pBegin
)
314 rStr
.SetChar( sal::static_int_cast
<sal_uInt16
>(pBegin
- pBase
), cChar
);
325 // state stack manipulators
326 // ------------------------
327 void VectorOfOutDevStates::clearStateStack()
330 const OutDevState aDefaultState
;
331 m_aStates
.push_back(aDefaultState
);
334 OutDevState
& VectorOfOutDevStates::getState()
336 return m_aStates
.back();
339 const OutDevState
& VectorOfOutDevStates::getState() const
341 return m_aStates
.back();
344 void VectorOfOutDevStates::pushState(sal_uInt16 nFlags
)
346 m_aStates
.push_back( getState() );
347 getState().pushFlags
= nFlags
;
350 void VectorOfOutDevStates::popState()
352 if( getState().pushFlags
!= PUSH_ALL
)
354 // a state is pushed which is incomplete, i.e. does not
355 // restore everything to the previous stack level when
357 // That means, we take the old state, and restore every
358 // OutDevState member whose flag is set, from the new to the
359 // old state. Then the new state gets overwritten by the
362 // preset to-be-calculated new state with old state
363 OutDevState
aCalculatedNewState( getState() );
365 // selectively copy to-be-restored content over saved old
367 m_aStates
.pop_back();
369 const OutDevState
& rNewState( getState() );
371 if( (aCalculatedNewState
.pushFlags
& PUSH_LINECOLOR
) )
373 aCalculatedNewState
.lineColor
= rNewState
.lineColor
;
374 aCalculatedNewState
.isLineColorSet
= rNewState
.isLineColorSet
;
377 if( (aCalculatedNewState
.pushFlags
& PUSH_FILLCOLOR
) )
379 aCalculatedNewState
.fillColor
= rNewState
.fillColor
;
380 aCalculatedNewState
.isFillColorSet
= rNewState
.isFillColorSet
;
383 if( (aCalculatedNewState
.pushFlags
& PUSH_FONT
) )
385 aCalculatedNewState
.xFont
= rNewState
.xFont
;
386 aCalculatedNewState
.fontRotation
= rNewState
.fontRotation
;
387 aCalculatedNewState
.textReliefStyle
= rNewState
.textReliefStyle
;
388 aCalculatedNewState
.textOverlineStyle
= rNewState
.textOverlineStyle
;
389 aCalculatedNewState
.textUnderlineStyle
= rNewState
.textUnderlineStyle
;
390 aCalculatedNewState
.textStrikeoutStyle
= rNewState
.textStrikeoutStyle
;
391 aCalculatedNewState
.textEmphasisMarkStyle
= rNewState
.textEmphasisMarkStyle
;
392 aCalculatedNewState
.isTextEffectShadowSet
= rNewState
.isTextEffectShadowSet
;
393 aCalculatedNewState
.isTextWordUnderlineSet
= rNewState
.isTextWordUnderlineSet
;
394 aCalculatedNewState
.isTextOutlineModeSet
= rNewState
.isTextOutlineModeSet
;
397 if( (aCalculatedNewState
.pushFlags
& PUSH_TEXTCOLOR
) )
399 aCalculatedNewState
.textColor
= rNewState
.textColor
;
402 if( (aCalculatedNewState
.pushFlags
& PUSH_MAPMODE
) )
404 aCalculatedNewState
.mapModeTransform
= rNewState
.mapModeTransform
;
407 if( (aCalculatedNewState
.pushFlags
& PUSH_CLIPREGION
) )
409 aCalculatedNewState
.clip
= rNewState
.clip
;
410 aCalculatedNewState
.clipRect
= rNewState
.clipRect
;
411 aCalculatedNewState
.xClipPoly
= rNewState
.xClipPoly
;
414 // TODO(F2): Raster ops NYI
415 // if( (aCalculatedNewState.pushFlags & PUSH_RASTEROP) )
419 if( (aCalculatedNewState
.pushFlags
& PUSH_TEXTFILLCOLOR
) )
421 aCalculatedNewState
.textFillColor
= rNewState
.textFillColor
;
422 aCalculatedNewState
.isTextFillColorSet
= rNewState
.isTextFillColorSet
;
425 if( (aCalculatedNewState
.pushFlags
& PUSH_TEXTALIGN
) )
427 aCalculatedNewState
.textReferencePoint
= rNewState
.textReferencePoint
;
430 // TODO(F1): Refpoint handling NYI
431 // if( (aCalculatedNewState.pushFlags & PUSH_REFPOINT) )
435 if( (aCalculatedNewState
.pushFlags
& PUSH_TEXTLINECOLOR
) )
437 aCalculatedNewState
.textLineColor
= rNewState
.textLineColor
;
438 aCalculatedNewState
.isTextLineColorSet
= rNewState
.isTextLineColorSet
;
441 if( (aCalculatedNewState
.pushFlags
& PUSH_TEXTLAYOUTMODE
) )
443 aCalculatedNewState
.textAlignment
= rNewState
.textAlignment
;
444 aCalculatedNewState
.textDirection
= rNewState
.textDirection
;
447 // TODO(F2): Text language handling NYI
448 // if( (aCalculatedNewState.pushFlags & PUSH_TEXTLANGUAGE) )
452 // always copy push mode
453 aCalculatedNewState
.pushFlags
= rNewState
.pushFlags
;
456 getState() = aCalculatedNewState
;
460 m_aStates
.pop_back();
464 bool ImplRenderer::createFillAndStroke( const ::basegfx::B2DPolyPolygon
& rPolyPoly
,
465 const ActionFactoryParameters
& rParms
)
467 const OutDevState
& rState( rParms
.mrStates
.getState() );
468 if( (!rState
.isLineColorSet
&&
469 !rState
.isFillColorSet
) ||
470 (rState
.lineColor
.getLength() == 0 &&
471 rState
.fillColor
.getLength() == 0) )
476 ActionSharedPtr
pPolyAction(
477 internal::PolyPolyActionFactory::createPolyPolyAction(
478 rPolyPoly
, rParms
.mrCanvas
, rState
) );
485 rParms
.mrCurrActionIndex
) );
487 rParms
.mrCurrActionIndex
+= pPolyAction
->getActionCount()-1;
493 bool ImplRenderer::createFillAndStroke( const ::basegfx::B2DPolygon
& rPoly
,
494 const ActionFactoryParameters
& rParms
)
496 return createFillAndStroke( ::basegfx::B2DPolyPolygon( rPoly
),
500 void ImplRenderer::skipContent( GDIMetaFile
& rMtf
,
501 const char* pCommentString
,
502 sal_Int32
& io_rCurrActionIndex
) const
504 ENSURE_OR_THROW( pCommentString
,
505 "ImplRenderer::skipContent(): NULL string given" );
507 MetaAction
* pCurrAct
;
508 while( (pCurrAct
=rMtf
.NextAction()) != NULL
)
510 // increment action index, we've skipped an action.
511 ++io_rCurrActionIndex
;
513 if( pCurrAct
->GetType() == META_COMMENT_ACTION
&&
514 static_cast<MetaCommentAction
*>(pCurrAct
)->GetComment().equalsIgnoreAsciiCase(
517 // requested comment found, done
526 bool ImplRenderer::isActionContained( GDIMetaFile
& rMtf
,
527 const char* pCommentString
,
528 sal_uInt16 nType
) const
530 ENSURE_OR_THROW( pCommentString
,
531 "ImplRenderer::isActionContained(): NULL string given" );
535 // at least _one_ call to GDIMetaFile::NextAction() is
537 sal_uIntPtr
nPos( 1 );
539 MetaAction
* pCurrAct
;
540 while( (pCurrAct
=rMtf
.NextAction()) != NULL
)
542 if( pCurrAct
->GetType() == nType
)
544 bRet
= true; // action type found
548 if( pCurrAct
->GetType() == META_COMMENT_ACTION
&&
549 static_cast<MetaCommentAction
*>(pCurrAct
)->GetComment().equalsIgnoreAsciiCase(
552 // delimiting end comment found, done
553 bRet
= false; // not yet found
560 // rewind metafile to previous position (this method must
561 // not change the current metaaction)
567 // EOF, and not yet found
574 void ImplRenderer::createGradientAction( const ::PolyPolygon
& rPoly
,
575 const ::Gradient
& rGradient
,
576 const ActionFactoryParameters
& rParms
,
577 bool bIsPolygonRectangle
,
578 bool bSubsettableActions
)
580 DBG_TESTSOLARMUTEX();
582 ::basegfx::B2DPolyPolygon
aDevicePoly( rPoly
.getB2DPolyPolygon() );
583 aDevicePoly
.transform( rParms
.mrStates
.getState().mapModeTransform
);
585 // decide, whether this gradient can be rendered natively
586 // by the canvas, or must be emulated via VCL gradient
587 // action extraction.
588 const sal_uInt16
nSteps( rGradient
.GetSteps() );
590 if( // step count is infinite, can use native canvas
593 // step count is sufficiently high, such that no
594 // discernible difference should be visible.
597 uno::Reference
< lang::XMultiServiceFactory
> xFactory(
598 rParms
.mrCanvas
->getUNOCanvas()->getDevice()->getParametricPolyPolygonFactory() );
602 rendering::Texture aTexture
;
604 aTexture
.RepeatModeX
= rendering::TexturingMode::CLAMP
;
605 aTexture
.RepeatModeY
= rendering::TexturingMode::CLAMP
;
606 aTexture
.Alpha
= 1.0;
609 // setup start/end color values
610 // ----------------------------
612 // scale color coefficients with gradient intensities
613 const sal_uInt16
nStartIntensity( rGradient
.GetStartIntensity() );
614 ::Color
aVCLStartColor( rGradient
.GetStartColor() );
615 aVCLStartColor
.SetRed( (sal_uInt8
)(aVCLStartColor
.GetRed() * nStartIntensity
/ 100) );
616 aVCLStartColor
.SetGreen( (sal_uInt8
)(aVCLStartColor
.GetGreen() * nStartIntensity
/ 100) );
617 aVCLStartColor
.SetBlue( (sal_uInt8
)(aVCLStartColor
.GetBlue() * nStartIntensity
/ 100) );
619 const sal_uInt16
nEndIntensity( rGradient
.GetEndIntensity() );
620 ::Color
aVCLEndColor( rGradient
.GetEndColor() );
621 aVCLEndColor
.SetRed( (sal_uInt8
)(aVCLEndColor
.GetRed() * nEndIntensity
/ 100) );
622 aVCLEndColor
.SetGreen( (sal_uInt8
)(aVCLEndColor
.GetGreen() * nEndIntensity
/ 100) );
623 aVCLEndColor
.SetBlue( (sal_uInt8
)(aVCLEndColor
.GetBlue() * nEndIntensity
/ 100) );
625 uno::Reference
<rendering::XColorSpace
> xColorSpace(
626 rParms
.mrCanvas
->getUNOCanvas()->getDevice()->getDeviceColorSpace());
627 const uno::Sequence
< double > aStartColor(
628 ::vcl::unotools::colorToDoubleSequence( aVCLStartColor
,
630 const uno::Sequence
< double > aEndColor(
631 ::vcl::unotools::colorToDoubleSequence( aVCLEndColor
,
634 uno::Sequence
< uno::Sequence
< double > > aColors(2);
635 uno::Sequence
< double > aStops(2);
637 if( rGradient
.GetStyle() == GradientStyle_AXIAL
)
646 aColors
[0] = aEndColor
;
647 aColors
[1] = aStartColor
;
648 aColors
[2] = aEndColor
;
655 aColors
[0] = aStartColor
;
656 aColors
[1] = aEndColor
;
659 const ::basegfx::B2DRectangle
aBounds(
660 ::basegfx::tools::getRange(aDevicePoly
) );
661 const ::basegfx::B2DVector
aOffset(
662 rGradient
.GetOfsX() / 100.0,
663 rGradient
.GetOfsY() / 100.0);
664 double fRotation( rGradient
.GetAngle() * M_PI
/ 1800.0 );
665 const double fBorder( rGradient
.GetBorder() / 100.0 );
667 basegfx::B2DHomMatrix aRot90
;
668 aRot90
.rotate(M_PI_2
);
670 basegfx::ODFGradientInfo aGradInfo
;
671 rtl::OUString aGradientService
;
672 switch( rGradient
.GetStyle() )
674 case GradientStyle_LINEAR
:
675 basegfx::tools::createLinearODFGradientInfo(aGradInfo
,
680 // map odf to svg gradient orientation - x
681 // instead of y direction
682 aGradInfo
.maTextureTransform
= aGradInfo
.maTextureTransform
* aRot90
;
683 aGradientService
= "LinearGradient";
686 case GradientStyle_AXIAL
:
688 // Adapt the border so that it is suitable
689 // for the axial gradient. An axial
690 // gradient consists of two linear
691 // gradients. Each of those covers half
692 // of the total size. In order to
693 // compensate for the condensed display of
694 // the linear gradients, we have to
695 // enlarge the area taken up by the actual
696 // gradient (1-fBorder). After that we
697 // have to turn the result back into a
698 // border value, hence the second (left
700 const double fAxialBorder (1-2*(1-fBorder
));
701 basegfx::tools::createAxialODFGradientInfo(aGradInfo
,
706 // map odf to svg gradient orientation - x
707 // instead of y direction
708 aGradInfo
.maTextureTransform
= aGradInfo
.maTextureTransform
* aRot90
;
710 // map odf axial gradient to 3-stop linear
711 // gradient - shift left by 0.5
712 basegfx::B2DHomMatrix aShift
;
713 aShift
.translate(-0.5,0);
714 aGradInfo
.maTextureTransform
= aGradInfo
.maTextureTransform
* aShift
;
716 aGradientService
= "LinearGradient";
720 case GradientStyle_RADIAL
:
721 basegfx::tools::createRadialODFGradientInfo(aGradInfo
,
726 aGradientService
= "EllipticalGradient";
729 case GradientStyle_ELLIPTICAL
:
730 basegfx::tools::createEllipticalODFGradientInfo(aGradInfo
,
736 aGradientService
= "EllipticalGradient";
739 case GradientStyle_SQUARE
:
740 basegfx::tools::createSquareODFGradientInfo(aGradInfo
,
746 aGradientService
= "RectangularGradient";
749 case GradientStyle_RECT
:
750 basegfx::tools::createRectangularODFGradientInfo(aGradInfo
,
756 aGradientService
= "RectangularGradient";
760 ENSURE_OR_THROW( false,
761 "ImplRenderer::createGradientAction(): Unexpected gradient type" );
765 // As the texture coordinate space is relative to
766 // the polygon coordinate space (NOT to the
767 // polygon itself), move gradient to the start of
768 // the actual polygon. If we skip this, the
769 // gradient will always display at the origin, and
770 // not within the polygon bound (which might be
771 // miles away from the origin).
772 aGradInfo
.maTextureTransform
.translate( aBounds
.getMinX(),
774 ::basegfx::unotools::affineMatrixFromHomMatrix( aTexture
.AffineTransform
,
775 aGradInfo
.maTextureTransform
);
777 uno::Sequence
<uno::Any
> args(3);
778 beans::PropertyValue aProp
;
779 aProp
.Name
= "Colors";
780 aProp
.Value
<<= aColors
;
782 aProp
.Name
= "Stops";
783 aProp
.Value
<<= aStops
;
785 aProp
.Name
= "AspectRatio";
786 aProp
.Value
<<= aGradInfo
.mfAspectRatio
;
789 aTexture
.Gradient
.set(
790 xFactory
->createInstanceWithArguments(aGradientService
,
793 if( aTexture
.Gradient
.is() )
795 ActionSharedPtr
pPolyAction(
796 internal::PolyPolyActionFactory::createPolyPolyAction(
799 rParms
.mrStates
.getState(),
807 rParms
.mrCurrActionIndex
) );
809 rParms
.mrCurrActionIndex
+= pPolyAction
->getActionCount()-1;
812 // done, using native gradients
818 // cannot currently use native canvas gradients, as a
819 // finite step size is given (this funny feature is not
820 // supported by the XCanvas API)
821 rParms
.mrStates
.pushState(PUSH_ALL
);
823 if( !bIsPolygonRectangle
)
825 // only clip, if given polygon is not a rectangle in
826 // the first place (the gradient is always limited to
827 // the given bound rect)
835 rParms
.mrVDev
.AddGradientActions( rPoly
.GetBoundRect(),
839 createActions( aTmpMtf
, rParms
, bSubsettableActions
);
841 rParms
.mrStates
.popState();
844 uno::Reference
< rendering::XCanvasFont
> ImplRenderer::createFont( double& o_rFontRotation
,
846 const ActionFactoryParameters
& rParms
) const
848 rendering::FontRequest aFontRequest
;
850 if( rParms
.mrParms
.maFontName
.is_initialized() )
851 aFontRequest
.FontDescription
.FamilyName
= *rParms
.mrParms
.maFontName
;
853 aFontRequest
.FontDescription
.FamilyName
= rFont
.GetName();
855 aFontRequest
.FontDescription
.StyleName
= rFont
.GetStyleName();
857 aFontRequest
.FontDescription
.IsSymbolFont
= (rFont
.GetCharSet() == RTL_TEXTENCODING_SYMBOL
) ? util::TriState_YES
: util::TriState_NO
;
858 aFontRequest
.FontDescription
.IsVertical
= rFont
.IsVertical() ? util::TriState_YES
: util::TriState_NO
;
860 // TODO(F2): improve vclenum->panose conversion
861 aFontRequest
.FontDescription
.FontDescription
.Weight
=
862 rParms
.mrParms
.maFontWeight
.is_initialized() ?
863 *rParms
.mrParms
.maFontWeight
:
864 ::canvas::tools::numeric_cast
<sal_Int8
>( ::basegfx::fround( rFont
.GetWeight() ) );
865 aFontRequest
.FontDescription
.FontDescription
.Letterform
=
866 rParms
.mrParms
.maFontLetterForm
.is_initialized() ?
867 *rParms
.mrParms
.maFontLetterForm
:
868 (rFont
.GetItalic() == ITALIC_NONE
) ? 0 : 9;
869 aFontRequest
.FontDescription
.FontDescription
.Proportion
=
870 rParms
.mrParms
.maFontProportion
.is_initialized() ?
871 *rParms
.mrParms
.maFontProportion
:
872 (rFont
.GetPitch() == PITCH_FIXED
)
873 ? rendering::PanoseProportion::MONO_SPACED
874 : rendering::PanoseProportion::ANYTHING
;
876 LanguageType aLang
= rFont
.GetLanguage();
877 aFontRequest
.Locale
= MsLangId::convertLanguageToLocale(aLang
, false);
879 // setup state-local text transformation,
880 // if the font be rotated
881 const short nFontAngle( rFont
.GetOrientation() );
882 if( nFontAngle
!= 0 )
884 // set to unity transform rotated by font angle
885 const double nAngle( nFontAngle
* (F_PI
/ 1800.0) );
886 o_rFontRotation
= -nAngle
;
890 o_rFontRotation
= 0.0;
893 geometry::Matrix2D aFontMatrix
;
894 ::canvas::tools::setIdentityMatrix2D( aFontMatrix
);
896 // TODO(F2): use correct scale direction, font
897 // height might be width or anything else
899 // TODO(Q3): This code smells of programming by
900 // coincidence (the next two if statements)
901 const ::Size
rFontSizeLog( rFont
.GetSize() );
902 const sal_Int32 nFontWidthLog
= rFontSizeLog
.Width();
903 if( nFontWidthLog
!= 0 )
905 ::Font aTestFont
= rFont
;
906 aTestFont
.SetWidth( 0 );
907 sal_Int32 nNormalWidth
= rParms
.mrVDev
.GetFontMetric( aTestFont
).GetWidth();
908 if( nNormalWidth
!= nFontWidthLog
)
910 aFontMatrix
.m00
= (double)nFontWidthLog
/ nNormalWidth
;
913 // #i52608# apply map mode scale also to font matrix - an
914 // anisotrophic mapmode must be reflected in an
915 // anisotrophic font matrix scale.
916 const OutDevState
& rState( rParms
.mrStates
.getState() );
917 if( !::basegfx::fTools::equal(
918 rState
.mapModeTransform
.get(0,0),
919 rState
.mapModeTransform
.get(1,1)) )
921 const double nScaleX( rState
.mapModeTransform
.get(0,0) );
922 const double nScaleY( rState
.mapModeTransform
.get(1,1) );
924 // note: no reason to check for division by zero, we
925 // always have the value closer (or equal) to zero as
927 if( fabs(nScaleX
) < fabs(nScaleY
) )
928 aFontMatrix
.m00
*= nScaleX
/ nScaleY
;
930 aFontMatrix
.m11
*= nScaleY
/ nScaleX
;
932 aFontRequest
.CellSize
= (rState
.mapModeTransform
* ::vcl::unotools::b2DSizeFromSize(rFontSizeLog
)).getY();
934 return rParms
.mrCanvas
->getUNOCanvas()->createFont( aFontRequest
,
935 uno::Sequence
< beans::PropertyValue
>(),
939 // create text effects such as shadow/relief/embossed
940 void ImplRenderer::createTextAction( const ::Point
& rStartPoint
,
941 const String rString
,
944 const sal_Int32
* pCharWidths
,
945 const ActionFactoryParameters
& rParms
,
946 bool bSubsettableActions
)
948 ENSURE_OR_THROW( nIndex
>= 0 && nLength
<= rString
.Len() + nIndex
,
949 "ImplRenderer::createTextWithEffectsAction(): Invalid text index" );
952 return; // zero-length text, no visible output
954 const OutDevState
& rState( rParms
.mrStates
.getState() );
956 // TODO(F2): implement all text effects
957 // if( rState.textAlignment ); // TODO(F2): NYI
959 ::Color
aShadowColor( COL_AUTO
);
960 ::Color
aReliefColor( COL_AUTO
);
961 ::Size aShadowOffset
;
962 ::Size aReliefOffset
;
964 uno::Reference
<rendering::XColorSpace
> xColorSpace(
965 rParms
.mrCanvas
->getUNOCanvas()->getDevice()->getDeviceColorSpace() );
967 if( rState
.isTextEffectShadowSet
)
969 // calculate shadow offset (similar to outdev3.cxx)
970 // TODO(F3): better match with outdev3.cxx
971 sal_Int32 nShadowOffset
= static_cast<sal_Int32
>(1.5 + ((rParms
.mrVDev
.GetFont().GetHeight()-24.0)/24.0));
972 if( nShadowOffset
< 1 )
975 aShadowOffset
.setWidth( nShadowOffset
);
976 aShadowOffset
.setHeight( nShadowOffset
);
978 // determine shadow color (from outdev3.cxx)
979 ::Color aTextColor
= ::vcl::unotools::doubleSequenceToColor(
980 rState
.textColor
, xColorSpace
);
981 bool bIsDark
= (aTextColor
.GetColor() == COL_BLACK
)
982 || (aTextColor
.GetLuminance() < 8);
984 aShadowColor
= bIsDark
? COL_LIGHTGRAY
: COL_BLACK
;
985 aShadowColor
.SetTransparency( aTextColor
.GetTransparency() );
988 if( rState
.textReliefStyle
)
990 // calculate relief offset (similar to outdev3.cxx)
991 sal_Int32 nReliefOffset
= rParms
.mrVDev
.PixelToLogic( Size( 1, 1 ) ).Height();
992 nReliefOffset
+= nReliefOffset
/2;
993 if( nReliefOffset
< 1 )
996 if( rState
.textReliefStyle
== RELIEF_ENGRAVED
)
997 nReliefOffset
= -nReliefOffset
;
999 aReliefOffset
.setWidth( nReliefOffset
);
1000 aReliefOffset
.setHeight( nReliefOffset
);
1002 // determine relief color (from outdev3.cxx)
1003 ::Color aTextColor
= ::vcl::unotools::doubleSequenceToColor(
1004 rState
.textColor
, xColorSpace
);
1006 aReliefColor
= ::Color( COL_LIGHTGRAY
);
1008 // we don't have a automatic color, so black is always
1009 // drawn on white (literally copied from
1010 // vcl/source/gdi/outdev3.cxx)
1011 if( aTextColor
.GetColor() == COL_BLACK
)
1013 aTextColor
= ::Color( COL_WHITE
);
1014 rParms
.mrStates
.getState().textColor
=
1015 ::vcl::unotools::colorToDoubleSequence(
1016 aTextColor
, xColorSpace
);
1019 if( aTextColor
.GetColor() == COL_WHITE
)
1020 aReliefColor
= ::Color( COL_BLACK
);
1021 aReliefColor
.SetTransparency( aTextColor
.GetTransparency() );
1024 // create the actual text action
1025 ActionSharedPtr
pTextAction(
1026 TextActionFactory::createTextAction(
1040 bSubsettableActions
) );
1042 ActionSharedPtr pStrikeoutTextAction
;
1044 if ( rState
.textStrikeoutStyle
== STRIKEOUT_X
|| rState
.textStrikeoutStyle
== STRIKEOUT_SLASH
)
1046 long nWidth
= rParms
.mrVDev
.GetTextWidth( rString
,nIndex
,nLength
);
1048 xub_Unicode pChars
[5];
1049 if ( rState
.textStrikeoutStyle
== STRIKEOUT_X
)
1053 pChars
[3]=pChars
[2]=pChars
[1]=pChars
[0];
1055 long nStrikeoutWidth
= nWidth
;
1056 String
aStrikeoutTest( pChars
, 4 );
1058 if( aStrikeoutTest
.Len() )
1060 nStrikeoutWidth
= ( rParms
.mrVDev
.GetTextWidth( aStrikeoutTest
) + 2 ) / 4;
1061 aStrikeoutTest
.Erase();
1063 if( nStrikeoutWidth
<= 0 )
1064 nStrikeoutWidth
= 1;
1067 long nMaxWidth
= nStrikeoutWidth
/2;
1068 if ( nMaxWidth
< 2 )
1070 nMaxWidth
+= nWidth
+ 1;
1072 long nFullStrikeoutWidth
= 0;
1073 String
aStrikeoutText( pChars
, 0 );
1074 while( (nFullStrikeoutWidth
+=nStrikeoutWidth
) < nMaxWidth
+1 )
1075 aStrikeoutText
+= pChars
[0];
1078 xub_StrLen nLen
= aStrikeoutText
.Len();
1082 long nInterval
= ( nWidth
- nStrikeoutWidth
* nLen
) / nLen
;
1083 nStrikeoutWidth
+= nInterval
;
1084 sal_Int32
* pStrikeoutCharWidths
= new sal_Int32
[nLen
];
1086 for ( int i
= 0;i
<nLen
; i
++)
1088 pStrikeoutCharWidths
[i
] = nStrikeoutWidth
;
1091 for ( int i
= 1;i
< nLen
; i
++ )
1093 pStrikeoutCharWidths
[ i
] += pStrikeoutCharWidths
[ i
-1 ];
1096 sal_Int32 nStartPos
= 0;
1098 pStrikeoutTextAction
=
1099 TextActionFactory::createTextAction(
1107 aStrikeoutText
.Len(),
1108 pStrikeoutCharWidths
,
1113 bSubsettableActions
) ;
1119 maActions
.push_back(
1122 rParms
.mrCurrActionIndex
) );
1124 if ( pStrikeoutTextAction
)
1126 maActions
.push_back(
1128 pStrikeoutTextAction
,
1129 rParms
.mrCurrActionIndex
) );
1132 rParms
.mrCurrActionIndex
+= pTextAction
->getActionCount()-1;
1136 void ImplRenderer::updateClipping( const ::basegfx::B2DPolyPolygon
& rClipPoly
,
1137 const ActionFactoryParameters
& rParms
,
1140 ::cppcanvas::internal::OutDevState
& rState( rParms
.mrStates
.getState() );
1141 ::basegfx::B2DPolyPolygon
aClipPoly( rClipPoly
);
1143 const bool bEmptyClipRect( rState
.clipRect
.IsEmpty() );
1144 const bool bEmptyClipPoly( rState
.clip
.count() == 0 );
1146 ENSURE_OR_THROW( bEmptyClipPoly
|| bEmptyClipRect
,
1147 "ImplRenderer::updateClipping(): Clip rect and polygon are both set!" );
1150 (bEmptyClipRect
&& bEmptyClipPoly
) )
1152 rState
.clip
= rClipPoly
;
1156 if( !bEmptyClipRect
)
1158 // TODO(P3): Use Liang-Barsky polygon clip here,
1159 // after all, one object is just a rectangle!
1161 // convert rect to polygon beforehand, must revert
1162 // to general polygon clipping here.
1163 rState
.clip
= ::basegfx::B2DPolyPolygon(
1164 ::basegfx::tools::createPolygonFromRect(
1165 // #121100# VCL rectangular clips always
1166 // include one more pixel to the right
1168 ::basegfx::B2DRectangle( rState
.clipRect
.Left(),
1169 rState
.clipRect
.Top(),
1170 rState
.clipRect
.Right()+1,
1171 rState
.clipRect
.Bottom()+1 ) ) );
1175 rState
.clip
= basegfx::tools::clipPolyPolygonOnPolyPolygon(
1176 aClipPoly
, rState
.clip
, true, false);
1179 // by now, our clip resides in the OutDevState::clip
1181 rState
.clipRect
.SetEmpty();
1183 if( rState
.clip
.count() == 0 )
1185 if( rState
.clipRect
.IsEmpty() )
1187 rState
.xClipPoly
.clear();
1191 rState
.xClipPoly
= ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1192 rParms
.mrCanvas
->getUNOCanvas()->getDevice(),
1193 ::basegfx::B2DPolyPolygon(
1194 ::basegfx::tools::createPolygonFromRect(
1195 // #121100# VCL rectangular clips
1196 // always include one more pixel to
1197 // the right and the bottom
1198 ::basegfx::B2DRectangle( rState
.clipRect
.Left(),
1199 rState
.clipRect
.Top(),
1200 rState
.clipRect
.Right()+1,
1201 rState
.clipRect
.Bottom()+1 ) ) ) );
1206 rState
.xClipPoly
= ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1207 rParms
.mrCanvas
->getUNOCanvas()->getDevice(),
1212 void ImplRenderer::updateClipping( const ::Rectangle
& rClipRect
,
1213 const ActionFactoryParameters
& rParms
,
1216 ::cppcanvas::internal::OutDevState
& rState( rParms
.mrStates
.getState() );
1218 const bool bEmptyClipRect( rState
.clipRect
.IsEmpty() );
1219 const bool bEmptyClipPoly( rState
.clip
.count() == 0 );
1221 ENSURE_OR_THROW( bEmptyClipPoly
|| bEmptyClipRect
,
1222 "ImplRenderer::updateClipping(): Clip rect and polygon are both set!" );
1225 (bEmptyClipRect
&& bEmptyClipPoly
) )
1227 rState
.clipRect
= rClipRect
;
1228 rState
.clip
.clear();
1230 else if( bEmptyClipPoly
)
1232 rState
.clipRect
.Intersection( rClipRect
);
1233 rState
.clip
.clear();
1237 // TODO(P3): Handle a fourth case here, when all clip
1238 // polygons are rectangular, once B2DMultiRange's
1239 // sweep line implementation is done.
1241 // general case: convert to polygon and clip
1242 // -----------------------------------------
1244 // convert rect to polygon beforehand, must revert
1245 // to general polygon clipping here.
1246 ::basegfx::B2DPolyPolygon
aClipPoly(
1247 ::basegfx::tools::createPolygonFromRect(
1248 ::basegfx::B2DRectangle( rClipRect
.Left(),
1251 rClipRect
.Bottom() ) ) );
1253 rState
.clipRect
.SetEmpty();
1256 rState
.clip
= basegfx::tools::clipPolyPolygonOnPolyPolygon(
1257 aClipPoly
, rState
.clip
, true, false);
1260 if( rState
.clip
.count() == 0 )
1262 if( rState
.clipRect
.IsEmpty() )
1264 rState
.xClipPoly
.clear();
1268 rState
.xClipPoly
= ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1269 rParms
.mrCanvas
->getUNOCanvas()->getDevice(),
1270 ::basegfx::B2DPolyPolygon(
1271 ::basegfx::tools::createPolygonFromRect(
1272 // #121100# VCL rectangular clips
1273 // always include one more pixel to
1274 // the right and the bottom
1275 ::basegfx::B2DRectangle( rState
.clipRect
.Left(),
1276 rState
.clipRect
.Top(),
1277 rState
.clipRect
.Right()+1,
1278 rState
.clipRect
.Bottom()+1 ) ) ) );
1283 rState
.xClipPoly
= ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1284 rParms
.mrCanvas
->getUNOCanvas()->getDevice(),
1289 bool ImplRenderer::createActions( GDIMetaFile
& rMtf
,
1290 const ActionFactoryParameters
& rFactoryParms
,
1291 bool bSubsettableActions
)
1293 /* TODO(P2): interpret mtf-comments
1294 ================================
1296 - gradient fillings (do that via comments)
1298 - think about mapping. _If_ we do everything in logical
1299 coordinates (which would solve the probs for stroke
1300 widths and text offsets), then we would have to
1301 recalc scaling for every drawing operation. This is
1302 because the outdev map mode might change at any time.
1303 Also keep in mind, that, although we've double precision
1304 float arithmetic now, different offsets might still
1305 generate different roundings (aka
1306 'OutputDevice::SetPixelOffset())
1310 // alias common parameters
1311 VectorOfOutDevStates
& rStates(rFactoryParms
.mrStates
);
1312 const CanvasSharedPtr
& rCanvas(rFactoryParms
.mrCanvas
);
1313 ::VirtualDevice
& rVDev(rFactoryParms
.mrVDev
);
1314 const Parameters
& rParms(rFactoryParms
.mrParms
);
1315 sal_Int32
& io_rCurrActionIndex(rFactoryParms
.mrCurrActionIndex
);
1318 // Loop over every metaaction
1319 // ==========================
1320 MetaAction
* pCurrAct
;
1322 // TODO(P1): think about caching
1323 for( pCurrAct
=rMtf
.FirstAction();
1325 pCurrAct
= rMtf
.NextAction() )
1327 // execute every action, to keep VDev state up-to-date
1328 // currently used only for
1330 // - the line/fill color when processing a META_TRANSPARENT_ACTION
1331 // - SetFont to process font metric specific actions
1332 pCurrAct
->Execute( &rVDev
);
1334 EMFP_DEBUG(printf("MTF\trecord type: %x\n", pCurrAct
->GetType()));
1336 switch( pCurrAct
->GetType() )
1338 // ------------------------------------------------------------
1340 // In the first part of this monster-switch, we
1341 // handle all state-changing meta actions. These
1342 // are all handled locally.
1344 // ------------------------------------------------------------
1346 case META_PUSH_ACTION
:
1348 MetaPushAction
* pPushAction
= static_cast<MetaPushAction
*>(pCurrAct
);
1349 rStates
.pushState(pPushAction
->GetFlags());
1353 case META_POP_ACTION
:
1357 case META_TEXTLANGUAGE_ACTION
:
1358 // FALLTHROUGH intended
1359 case META_REFPOINT_ACTION
:
1360 // handled via pCurrAct->Execute( &rVDev )
1363 case META_MAPMODE_ACTION
:
1364 // modify current mapModeTransformation
1365 // transformation, such that subsequent
1366 // coordinates map correctly
1367 tools::calcLogic2PixelAffineTransform( rStates
.getState().mapModeTransform
,
1371 // monitor clip regions, to assemble clip polygon on our own
1372 case META_CLIPREGION_ACTION
:
1374 MetaClipRegionAction
* pClipAction
= static_cast<MetaClipRegionAction
*>(pCurrAct
);
1376 if( !pClipAction
->IsClipping() )
1379 rStates
.getState().clip
.clear();
1383 if( !pClipAction
->GetRegion().HasPolyPolygon() )
1385 VERBOSE_TRACE( "ImplRenderer::createActions(): non-polygonal clip "
1386 "region encountered, falling back to bounding box!" );
1388 // #121806# explicitly kept integer
1389 Rectangle
aClipRect(
1391 pClipAction
->GetRegion().GetBoundRect() ) );
1393 // intersect current clip with given rect
1401 // set new clip polygon (don't intersect
1402 // with old one, just set it)
1404 // #121806# explicitly kept integer
1407 pClipAction
->GetRegion().GetPolyPolygon() ).getB2DPolyPolygon(),
1416 case META_ISECTRECTCLIPREGION_ACTION
:
1418 MetaISectRectClipRegionAction
* pClipAction
= static_cast<MetaISectRectClipRegionAction
*>(pCurrAct
);
1420 // #121806# explicitly kept integer
1421 Rectangle
aClipRect(
1422 rVDev
.LogicToPixel( pClipAction
->GetRect() ) );
1424 // intersect current clip with given rect
1433 case META_ISECTREGIONCLIPREGION_ACTION
:
1435 MetaISectRegionClipRegionAction
* pClipAction
= static_cast<MetaISectRegionClipRegionAction
*>(pCurrAct
);
1437 if( !pClipAction
->GetRegion().HasPolyPolygon() )
1439 VERBOSE_TRACE( "ImplRenderer::createActions(): non-polygonal clip "
1440 "region encountered, falling back to bounding box!" );
1442 // #121806# explicitly kept integer
1443 Rectangle
aClipRect(
1444 rVDev
.LogicToPixel( pClipAction
->GetRegion().GetBoundRect() ) );
1446 // intersect current clip with given rect
1454 // intersect current clip with given clip polygon
1456 // #121806# explicitly kept integer
1459 pClipAction
->GetRegion().GetPolyPolygon() ).getB2DPolyPolygon(),
1467 case META_MOVECLIPREGION_ACTION
:
1471 case META_LINECOLOR_ACTION
:
1472 if( !rParms
.maLineColor
.is_initialized() )
1474 setStateColor( static_cast<MetaLineColorAction
*>(pCurrAct
),
1475 rStates
.getState().isLineColorSet
,
1476 rStates
.getState().lineColor
,
1481 case META_FILLCOLOR_ACTION
:
1482 if( !rParms
.maFillColor
.is_initialized() )
1484 setStateColor( static_cast<MetaFillColorAction
*>(pCurrAct
),
1485 rStates
.getState().isFillColorSet
,
1486 rStates
.getState().fillColor
,
1491 case META_TEXTCOLOR_ACTION
:
1493 if( !rParms
.maTextColor
.is_initialized() )
1495 // Text color is set unconditionally, thus, no
1496 // use of setStateColor here
1497 ::Color
aColor( static_cast<MetaTextColorAction
*>(pCurrAct
)->GetColor() );
1499 // force alpha part of color to
1500 // opaque. transparent painting is done
1501 // explicitly via META_TRANSPARENT_ACTION
1502 aColor
.SetTransparency(0);
1504 rStates
.getState().textColor
=
1505 ::vcl::unotools::colorToDoubleSequence(
1507 rCanvas
->getUNOCanvas()->getDevice()->getDeviceColorSpace() );
1512 case META_TEXTFILLCOLOR_ACTION
:
1513 if( !rParms
.maTextColor
.is_initialized() )
1515 setStateColor( static_cast<MetaTextFillColorAction
*>(pCurrAct
),
1516 rStates
.getState().isTextFillColorSet
,
1517 rStates
.getState().textFillColor
,
1522 case META_TEXTLINECOLOR_ACTION
:
1523 if( !rParms
.maTextColor
.is_initialized() )
1525 setStateColor( static_cast<MetaTextLineColorAction
*>(pCurrAct
),
1526 rStates
.getState().isTextLineColorSet
,
1527 rStates
.getState().textLineColor
,
1532 case META_TEXTALIGN_ACTION
:
1534 ::cppcanvas::internal::OutDevState
& rState
= rStates
.getState();
1535 const TextAlign
eTextAlign( static_cast<MetaTextAlignAction
*>(pCurrAct
)->GetTextAlign() );
1537 rState
.textReferencePoint
= eTextAlign
;
1541 case META_FONT_ACTION
:
1543 ::cppcanvas::internal::OutDevState
& rState
= rStates
.getState();
1544 const ::Font
& rFont( static_cast<MetaFontAction
*>(pCurrAct
)->GetFont() );
1546 rState
.xFont
= createFont( rState
.fontRotation
,
1550 // TODO(Q2): define and use appropriate enumeration types
1551 rState
.textReliefStyle
= (sal_Int8
)rFont
.GetRelief();
1552 rState
.textOverlineStyle
= (sal_Int8
)rFont
.GetOverline();
1553 rState
.textUnderlineStyle
= rParms
.maFontUnderline
.is_initialized() ?
1554 (*rParms
.maFontUnderline
? (sal_Int8
)UNDERLINE_SINGLE
: (sal_Int8
)UNDERLINE_NONE
) :
1555 (sal_Int8
)rFont
.GetUnderline();
1556 rState
.textStrikeoutStyle
= (sal_Int8
)rFont
.GetStrikeout();
1557 rState
.textEmphasisMarkStyle
= (sal_Int8
)rFont
.GetEmphasisMark();
1558 rState
.isTextEffectShadowSet
= (rFont
.IsShadow() != sal_False
);
1559 rState
.isTextWordUnderlineSet
= (rFont
.IsWordLineMode() != sal_False
);
1560 rState
.isTextOutlineModeSet
= (rFont
.IsOutline() != sal_False
);
1564 case META_RASTEROP_ACTION
:
1568 case META_LAYOUTMODE_ACTION
:
1570 // TODO(F2): A lot is missing here
1571 int nLayoutMode
= static_cast<MetaLayoutModeAction
*>(pCurrAct
)->GetLayoutMode();
1572 ::cppcanvas::internal::OutDevState
& rState
= rStates
.getState();
1573 switch( nLayoutMode
& (TEXT_LAYOUT_BIDI_RTL
|TEXT_LAYOUT_BIDI_STRONG
) )
1575 case TEXT_LAYOUT_BIDI_LTR
:
1576 rState
.textDirection
= rendering::TextDirection::WEAK_LEFT_TO_RIGHT
;
1579 case (TEXT_LAYOUT_BIDI_LTR
| TEXT_LAYOUT_BIDI_STRONG
):
1580 rState
.textDirection
= rendering::TextDirection::STRONG_LEFT_TO_RIGHT
;
1583 case TEXT_LAYOUT_BIDI_RTL
:
1584 rState
.textDirection
= rendering::TextDirection::WEAK_RIGHT_TO_LEFT
;
1587 case (TEXT_LAYOUT_BIDI_RTL
| TEXT_LAYOUT_BIDI_STRONG
):
1588 rState
.textDirection
= rendering::TextDirection::STRONG_RIGHT_TO_LEFT
;
1592 rState
.textAlignment
= 0; // TODO(F2): rendering::TextAlignment::LEFT_ALIGNED;
1593 if( (nLayoutMode
& (TEXT_LAYOUT_BIDI_RTL
| TEXT_LAYOUT_TEXTORIGIN_RIGHT
) )
1594 && !(nLayoutMode
& TEXT_LAYOUT_TEXTORIGIN_LEFT
) )
1596 rState
.textAlignment
= 1; // TODO(F2): rendering::TextAlignment::RIGHT_ALIGNED;
1601 // ------------------------------------------------------------
1603 // In the second part of this monster-switch, we
1604 // handle all recursing meta actions. These are the
1605 // ones generating a metafile by themselves, which is
1606 // then processed by recursively calling this method.
1608 // ------------------------------------------------------------
1610 case META_GRADIENT_ACTION
:
1612 MetaGradientAction
* pGradAct
= static_cast<MetaGradientAction
*>(pCurrAct
);
1613 createGradientAction( ::Polygon( pGradAct
->GetRect() ),
1614 pGradAct
->GetGradient(),
1617 bSubsettableActions
);
1621 case META_HATCH_ACTION
:
1623 // TODO(F2): use native Canvas hatches here
1624 GDIMetaFile aTmpMtf
;
1626 rVDev
.AddHatchActions( static_cast<MetaHatchAction
*>(pCurrAct
)->GetPolyPolygon(),
1627 static_cast<MetaHatchAction
*>(pCurrAct
)->GetHatch(),
1629 createActions( aTmpMtf
, rFactoryParms
,
1630 bSubsettableActions
);
1634 case META_EPS_ACTION
:
1636 MetaEPSAction
* pAct
= static_cast<MetaEPSAction
*>(pCurrAct
);
1637 const GDIMetaFile
& rSubstitute
= pAct
->GetSubstitute();
1639 // #121806# explicitly kept integer
1640 const Size
aMtfSize( rSubstitute
.GetPrefSize() );
1641 const Size
aMtfSizePixPre( rVDev
.LogicToPixel( aMtfSize
,
1642 rSubstitute
.GetPrefMapMode() ) );
1644 // #i44110# correct null-sized output - there
1645 // are metafiles which have zero size in at
1646 // least one dimension
1647 const Size
aMtfSizePix( ::std::max( aMtfSizePixPre
.Width(), 1L ),
1648 ::std::max( aMtfSizePixPre
.Height(), 1L ) );
1650 // Setup local transform, such that the
1651 // metafile renders itself into the given
1653 rStates
.pushState(PUSH_ALL
);
1656 rVDev
.SetMapMode( rSubstitute
.GetPrefMapMode() );
1658 const ::Point
& rPos( rVDev
.LogicToPixel( pAct
->GetPoint() ) );
1659 const ::Size
& rSize( rVDev
.LogicToPixel( pAct
->GetSize() ) );
1661 rStates
.getState().transform
.translate( rPos
.X(),
1663 rStates
.getState().transform
.scale( (double)rSize
.Width() / aMtfSizePix
.Width(),
1664 (double)rSize
.Height() / aMtfSizePix
.Height() );
1666 createActions( const_cast<GDIMetaFile
&>(pAct
->GetSubstitute()),
1668 bSubsettableActions
);
1675 // handle metafile comments, to retrieve
1676 // meta-information for gradients, fills and
1677 // strokes. May skip actions, and may recurse.
1678 case META_COMMENT_ACTION
:
1680 MetaCommentAction
* pAct
= static_cast<MetaCommentAction
*>(pCurrAct
);
1683 if (pAct
->GetComment().equalsIgnoreAsciiCaseL(RTL_CONSTASCII_STRINGPARAM("XGRAD_SEQ_BEGIN")))
1685 MetaGradientExAction
* pGradAction
= NULL
;
1686 bool bDone( false );
1688 (pCurrAct
=rMtf
.NextAction()) != NULL
)
1690 switch( pCurrAct
->GetType() )
1692 // extract gradient info
1693 case META_GRADIENTEX_ACTION
:
1694 pGradAction
= static_cast<MetaGradientExAction
*>(pCurrAct
);
1697 // skip broken-down rendering, output gradient when sequence is ended
1698 case META_COMMENT_ACTION
:
1699 if( static_cast<MetaCommentAction
*>(pCurrAct
)->GetComment().equalsIgnoreAsciiCaseL(RTL_CONSTASCII_STRINGPARAM("XGRAD_SEQ_END")) )
1705 createGradientAction( pGradAction
->GetPolyPolygon(),
1706 pGradAction
->GetGradient(),
1709 bSubsettableActions
);
1716 // TODO(P2): Handle drawing layer strokes, via
1717 // XPATHSTROKE_SEQ_BEGIN comment
1719 // Handle drawing layer fills
1720 else if( pAct
->GetComment().equalsL(RTL_CONSTASCII_STRINGPARAM("XPATHFILL_SEQ_BEGIN")) )
1722 const sal_uInt8
* pData
= pAct
->GetData();
1725 SvMemoryStream
aMemStm( (void*)pData
, pAct
->GetDataSize(), STREAM_READ
);
1727 SvtGraphicFill aFill
;
1730 // TODO(P2): Also handle gradients and
1731 // hatches like this
1733 // only evaluate comment for pure
1734 // bitmap fills. If a transparency
1735 // gradient is involved (denoted by
1736 // the FloatTransparent action), take
1737 // the normal meta actions.
1738 if( aFill
.getFillType() == SvtGraphicFill::fillTexture
&&
1739 !isActionContained( rMtf
,
1740 "XPATHFILL_SEQ_END",
1741 META_FLOATTRANSPARENT_ACTION
) )
1743 rendering::Texture aTexture
;
1745 // TODO(F1): the SvtGraphicFill
1746 // can also transport metafiles
1747 // here, handle that case, too
1749 aFill
.getGraphic( aGraphic
);
1751 BitmapEx
aBmpEx( aGraphic
.GetBitmapEx() );
1752 const ::Size
aBmpSize( aBmpEx
.GetSizePixel() );
1754 ::SvtGraphicFill::Transform aTransform
;
1755 aFill
.getTransform( aTransform
);
1757 ::basegfx::B2DHomMatrix aMatrix
;
1759 // convert to basegfx matrix
1760 aMatrix
.set(0,0, aTransform
.matrix
[ 0 ] );
1761 aMatrix
.set(0,1, aTransform
.matrix
[ 1 ] );
1762 aMatrix
.set(0,2, aTransform
.matrix
[ 2 ] );
1763 aMatrix
.set(1,0, aTransform
.matrix
[ 3 ] );
1764 aMatrix
.set(1,1, aTransform
.matrix
[ 4 ] );
1765 aMatrix
.set(1,2, aTransform
.matrix
[ 5 ] );
1767 ::basegfx::B2DHomMatrix aScale
;
1768 aScale
.scale( aBmpSize
.Width(),
1769 aBmpSize
.Height() );
1771 // post-multiply with the bitmap
1772 // size (XCanvas' texture assumes
1773 // the given bitmap to be
1774 // normalized to [0,1]x[0,1]
1776 aMatrix
= aMatrix
* aScale
;
1778 // pre-multiply with the
1779 // logic-to-pixel scale factor
1780 // (the metafile comment works in
1781 // logical coordinates).
1782 ::basegfx::B2DHomMatrix aLogic2PixelTransform
;
1783 aMatrix
*= tools::calcLogic2PixelLinearTransform( aLogic2PixelTransform
,
1786 ::basegfx::unotools::affineMatrixFromHomMatrix(
1787 aTexture
.AffineTransform
,
1790 aTexture
.Alpha
= 1.0 - aFill
.getTransparency();
1792 ::vcl::unotools::xBitmapFromBitmapEx(
1793 rCanvas
->getUNOCanvas()->getDevice(),
1795 if( aFill
.isTiling() )
1797 aTexture
.RepeatModeX
= rendering::TexturingMode::REPEAT
;
1798 aTexture
.RepeatModeY
= rendering::TexturingMode::REPEAT
;
1802 aTexture
.RepeatModeX
= rendering::TexturingMode::NONE
;
1803 aTexture
.RepeatModeY
= rendering::TexturingMode::NONE
;
1806 ::PolyPolygon aPath
;
1807 aFill
.getPath( aPath
);
1809 ::basegfx::B2DPolyPolygon
aPoly( aPath
.getB2DPolyPolygon() );
1810 aPoly
.transform( rStates
.getState().mapModeTransform
);
1811 ActionSharedPtr
pPolyAction(
1812 internal::PolyPolyActionFactory::createPolyPolyAction(
1820 maActions
.push_back(
1823 io_rCurrActionIndex
) );
1825 io_rCurrActionIndex
+= pPolyAction
->getActionCount()-1;
1828 // skip broken-down render output
1830 "XPATHFILL_SEQ_END",
1831 io_rCurrActionIndex
);
1835 // Handle drawing layer fills
1836 else if( pAct
->GetComment().equalsL(RTL_CONSTASCII_STRINGPARAM("EMF_PLUS")) ) {
1837 static int count
= -1, limit
= 0x7fffffff;
1840 if (char *env
= getenv ("EMF_PLUS_LIMIT")) {
1842 EMFP_DEBUG (printf ("EMF+ records limit: %d\n", limit
));
1845 EMFP_DEBUG (printf ("EMF+ passed to canvas mtf renderer, size: %u\n", (unsigned int)pAct
->GetDataSize ()));
1847 processEMFPlus( pAct
, rFactoryParms
, rStates
.getState(), rCanvas
);
1849 } else if( pAct
->GetComment().equalsL(RTL_CONSTASCII_STRINGPARAM("EMF_PLUS_HEADER_INFO")) ) {
1850 EMFP_DEBUG (printf ("EMF+ passed to canvas mtf renderer - header info, size: %u\n", (unsigned int)pAct
->GetDataSize ()));
1852 SvMemoryStream
rMF ((void*) pAct
->GetData (), pAct
->GetDataSize (), STREAM_READ
);
1854 rMF
>> nFrameLeft
>> nFrameTop
>> nFrameRight
>> nFrameBottom
;
1855 EMFP_DEBUG (printf ("EMF+ picture frame: %d,%d - %d,%d\n", (int)nFrameLeft
, (int)nFrameTop
, (int)nFrameRight
, (int)nFrameBottom
));
1856 rMF
>> nPixX
>> nPixY
>> nMmX
>> nMmY
;
1857 EMFP_DEBUG (printf ("EMF+ ref device pixel size: %dx%d mm size: %dx%d\n", (int)nPixX
, (int)nPixY
, (int)nMmX
, (int)nMmY
));
1859 rMF
>> aBaseTransform
;
1860 //aWorldTransform.Set (aBaseTransform);
1865 // ------------------------------------------------------------
1867 // In the third part of this monster-switch, we
1868 // handle all 'acting' meta actions. These are all
1869 // processed by constructing function objects for
1870 // them, which will later ease caching.
1872 // ------------------------------------------------------------
1874 case META_POINT_ACTION
:
1876 const OutDevState
& rState( rStates
.getState() );
1877 if( rState
.lineColor
.getLength() )
1879 ActionSharedPtr
pPointAction(
1880 internal::PointActionFactory::createPointAction(
1881 rState
.mapModeTransform
* ::vcl::unotools::b2DPointFromPoint(
1882 static_cast<MetaPointAction
*>(pCurrAct
)->GetPoint() ),
1888 maActions
.push_back(
1891 io_rCurrActionIndex
) );
1893 io_rCurrActionIndex
+= pPointAction
->getActionCount()-1;
1899 case META_PIXEL_ACTION
:
1901 const OutDevState
& rState( rStates
.getState() );
1902 if( rState
.lineColor
.getLength() )
1904 ActionSharedPtr
pPointAction(
1905 internal::PointActionFactory::createPointAction(
1906 rState
.mapModeTransform
* ::vcl::unotools::b2DPointFromPoint(
1907 static_cast<MetaPixelAction
*>(pCurrAct
)->GetPoint() ),
1910 static_cast<MetaPixelAction
*>(pCurrAct
)->GetColor() ) );
1914 maActions
.push_back(
1917 io_rCurrActionIndex
) );
1919 io_rCurrActionIndex
+= pPointAction
->getActionCount()-1;
1925 case META_LINE_ACTION
:
1927 const OutDevState
& rState( rStates
.getState() );
1928 if( rState
.lineColor
.getLength() )
1930 MetaLineAction
* pLineAct
= static_cast<MetaLineAction
*>(pCurrAct
);
1932 const LineInfo
& rLineInfo( pLineAct
->GetLineInfo() );
1934 const ::basegfx::B2DPoint
aStartPoint(
1935 rState
.mapModeTransform
* ::vcl::unotools::b2DPointFromPoint( pLineAct
->GetStartPoint() ));
1936 const ::basegfx::B2DPoint
aEndPoint(
1937 rState
.mapModeTransform
* ::vcl::unotools::b2DPointFromPoint( pLineAct
->GetEndPoint() ));
1939 ActionSharedPtr pLineAction
;
1941 if( rLineInfo
.IsDefault() )
1945 internal::LineActionFactory::createLineAction(
1953 maActions
.push_back(
1956 io_rCurrActionIndex
) );
1958 io_rCurrActionIndex
+= pLineAction
->getActionCount()-1;
1961 else if( LINE_NONE
!= rLineInfo
.GetStyle() )
1964 rendering::StrokeAttributes aStrokeAttributes
;
1966 setupStrokeAttributes( aStrokeAttributes
,
1970 // XCanvas can only stroke polygons,
1971 // not simple lines - thus, handle
1972 // this case via the polypolygon
1974 ::basegfx::B2DPolygon aPoly
;
1975 aPoly
.append( aStartPoint
);
1976 aPoly
.append( aEndPoint
);
1978 internal::PolyPolyActionFactory::createPolyPolyAction(
1979 ::basegfx::B2DPolyPolygon( aPoly
),
1980 rCanvas
, rState
, aStrokeAttributes
);
1984 maActions
.push_back(
1987 io_rCurrActionIndex
) );
1989 io_rCurrActionIndex
+= pLineAction
->getActionCount()-1;
1992 // else: line style is default
1993 // (i.e. invisible), don't generate action
1998 case META_RECT_ACTION
:
2000 const Rectangle
& rRect(
2001 static_cast<MetaRectAction
*>(pCurrAct
)->GetRect() );
2003 if( rRect
.IsEmpty() )
2006 const OutDevState
& rState( rStates
.getState() );
2007 const ::basegfx::B2DPoint
aTopLeftPixel(
2008 rState
.mapModeTransform
* ::vcl::unotools::b2DPointFromPoint( rRect
.TopLeft() ) );
2009 const ::basegfx::B2DPoint
aBottomRightPixel(
2010 rState
.mapModeTransform
* ::vcl::unotools::b2DPointFromPoint( rRect
.BottomRight() ) +
2011 // #121100# OutputDevice::DrawRect() fills
2012 // rectangles Apple-like, i.e. with one
2013 // additional pixel to the right and bottom.
2014 ::basegfx::B2DPoint(1,1) );
2016 createFillAndStroke( ::basegfx::tools::createPolygonFromRect(
2017 ::basegfx::B2DRange( aTopLeftPixel
,
2018 aBottomRightPixel
)),
2023 case META_ROUNDRECT_ACTION
:
2025 const Rectangle
& rRect(
2026 static_cast<MetaRoundRectAction
*>(pCurrAct
)->GetRect());
2028 if( rRect
.IsEmpty() )
2031 ::basegfx::B2DPolygon
aPoly(
2032 ::basegfx::tools::createPolygonFromRect(
2033 ::basegfx::B2DRange(
2034 ::vcl::unotools::b2DPointFromPoint( rRect
.TopLeft() ),
2035 ::vcl::unotools::b2DPointFromPoint( rRect
.BottomRight() ) +
2036 ::basegfx::B2DPoint(1,1) ),
2037 ( (double) static_cast<MetaRoundRectAction
*>(pCurrAct
)->GetHorzRound() ) / rRect
.GetWidth(),
2038 ( (double) static_cast<MetaRoundRectAction
*>(pCurrAct
)->GetVertRound() ) / rRect
.GetHeight() ) );
2039 aPoly
.transform( rStates
.getState().mapModeTransform
);
2041 createFillAndStroke( aPoly
,
2046 case META_ELLIPSE_ACTION
:
2048 const Rectangle
& rRect(
2049 static_cast<MetaEllipseAction
*>(pCurrAct
)->GetRect() );
2051 if( rRect
.IsEmpty() )
2054 const ::basegfx::B2DRange
aRange(
2055 ::vcl::unotools::b2DPointFromPoint( rRect
.TopLeft() ),
2056 ::vcl::unotools::b2DPointFromPoint( rRect
.BottomRight() ) +
2057 ::basegfx::B2DPoint(1,1) );
2059 ::basegfx::B2DPolygon
aPoly(
2060 ::basegfx::tools::createPolygonFromEllipse(
2063 aRange
.getHeight() ));
2064 aPoly
.transform( rStates
.getState().mapModeTransform
);
2066 createFillAndStroke( aPoly
,
2071 case META_ARC_ACTION
:
2073 // TODO(F1): Missing basegfx functionality. Mind empty rects!
2074 const Polygon
aToolsPoly( static_cast<MetaArcAction
*>(pCurrAct
)->GetRect(),
2075 static_cast<MetaArcAction
*>(pCurrAct
)->GetStartPoint(),
2076 static_cast<MetaArcAction
*>(pCurrAct
)->GetEndPoint(), POLY_ARC
);
2077 ::basegfx::B2DPolygon
aPoly( aToolsPoly
.getB2DPolygon() );
2078 aPoly
.transform( rStates
.getState().mapModeTransform
);
2080 createFillAndStroke( aPoly
,
2085 case META_PIE_ACTION
:
2087 // TODO(F1): Missing basegfx functionality. Mind empty rects!
2088 const Polygon
aToolsPoly( static_cast<MetaPieAction
*>(pCurrAct
)->GetRect(),
2089 static_cast<MetaPieAction
*>(pCurrAct
)->GetStartPoint(),
2090 static_cast<MetaPieAction
*>(pCurrAct
)->GetEndPoint(), POLY_PIE
);
2091 ::basegfx::B2DPolygon
aPoly( aToolsPoly
.getB2DPolygon() );
2092 aPoly
.transform( rStates
.getState().mapModeTransform
);
2094 createFillAndStroke( aPoly
,
2099 case META_CHORD_ACTION
:
2101 // TODO(F1): Missing basegfx functionality. Mind empty rects!
2102 const Polygon
aToolsPoly( static_cast<MetaChordAction
*>(pCurrAct
)->GetRect(),
2103 static_cast<MetaChordAction
*>(pCurrAct
)->GetStartPoint(),
2104 static_cast<MetaChordAction
*>(pCurrAct
)->GetEndPoint(), POLY_CHORD
);
2105 ::basegfx::B2DPolygon
aPoly( aToolsPoly
.getB2DPolygon() );
2106 aPoly
.transform( rStates
.getState().mapModeTransform
);
2108 createFillAndStroke( aPoly
,
2113 case META_POLYLINE_ACTION
:
2115 const OutDevState
& rState( rStates
.getState() );
2116 if( rState
.lineColor
.getLength() ||
2117 rState
.fillColor
.getLength() )
2119 MetaPolyLineAction
* pPolyLineAct
= static_cast<MetaPolyLineAction
*>(pCurrAct
);
2121 const LineInfo
& rLineInfo( pPolyLineAct
->GetLineInfo() );
2122 ::basegfx::B2DPolygon
aPoly( pPolyLineAct
->GetPolygon().getB2DPolygon() );
2123 aPoly
.transform( rState
.mapModeTransform
);
2125 ActionSharedPtr pLineAction
;
2127 if( rLineInfo
.IsDefault() )
2129 // plain hair line polygon
2131 internal::PolyPolyActionFactory::createLinePolyPolyAction(
2132 ::basegfx::B2DPolyPolygon(aPoly
),
2138 maActions
.push_back(
2141 io_rCurrActionIndex
) );
2143 io_rCurrActionIndex
+= pLineAction
->getActionCount()-1;
2146 else if( LINE_NONE
!= rLineInfo
.GetStyle() )
2148 // 'thick' line polygon
2149 rendering::StrokeAttributes aStrokeAttributes
;
2151 setupStrokeAttributes( aStrokeAttributes
,
2156 internal::PolyPolyActionFactory::createPolyPolyAction(
2157 ::basegfx::B2DPolyPolygon(aPoly
),
2160 aStrokeAttributes
) ;
2164 maActions
.push_back(
2167 io_rCurrActionIndex
) );
2169 io_rCurrActionIndex
+= pLineAction
->getActionCount()-1;
2172 // else: line style is default
2173 // (i.e. invisible), don't generate action
2178 case META_POLYGON_ACTION
:
2180 ::basegfx::B2DPolygon
aPoly( static_cast<MetaPolygonAction
*>(pCurrAct
)->GetPolygon().getB2DPolygon() );
2181 aPoly
.transform( rStates
.getState().mapModeTransform
);
2182 createFillAndStroke( aPoly
,
2187 case META_POLYPOLYGON_ACTION
:
2189 ::basegfx::B2DPolyPolygon
aPoly( static_cast<MetaPolyPolygonAction
*>(pCurrAct
)->GetPolyPolygon().getB2DPolyPolygon() );
2190 aPoly
.transform( rStates
.getState().mapModeTransform
);
2191 createFillAndStroke( aPoly
,
2196 case META_BMP_ACTION
:
2198 MetaBmpAction
* pAct
= static_cast<MetaBmpAction
*>(pCurrAct
);
2200 ActionSharedPtr
pBmpAction(
2201 internal::BitmapActionFactory::createBitmapAction(
2203 rStates
.getState().mapModeTransform
*
2204 ::vcl::unotools::b2DPointFromPoint( pAct
->GetPoint() ),
2206 rStates
.getState() ) );
2210 maActions
.push_back(
2213 io_rCurrActionIndex
) );
2215 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2220 case META_BMPSCALE_ACTION
:
2222 MetaBmpScaleAction
* pAct
= static_cast<MetaBmpScaleAction
*>(pCurrAct
);
2224 ActionSharedPtr
pBmpAction(
2225 internal::BitmapActionFactory::createBitmapAction(
2227 rStates
.getState().mapModeTransform
*
2228 ::vcl::unotools::b2DPointFromPoint( pAct
->GetPoint() ),
2229 rStates
.getState().mapModeTransform
*
2230 ::vcl::unotools::b2DSizeFromSize( pAct
->GetSize() ),
2232 rStates
.getState() ) );
2236 maActions
.push_back(
2239 io_rCurrActionIndex
) );
2241 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2246 case META_BMPSCALEPART_ACTION
:
2248 MetaBmpScalePartAction
* pAct
= static_cast<MetaBmpScalePartAction
*>(pCurrAct
);
2250 // crop bitmap to given source rectangle (no
2251 // need to copy and convert the whole bitmap)
2252 Bitmap
aBmp( pAct
->GetBitmap() );
2253 const Rectangle
aCropRect( pAct
->GetSrcPoint(),
2254 pAct
->GetSrcSize() );
2255 aBmp
.Crop( aCropRect
);
2257 ActionSharedPtr
pBmpAction(
2258 internal::BitmapActionFactory::createBitmapAction(
2260 rStates
.getState().mapModeTransform
*
2261 ::vcl::unotools::b2DPointFromPoint( pAct
->GetDestPoint() ),
2262 rStates
.getState().mapModeTransform
*
2263 ::vcl::unotools::b2DSizeFromSize( pAct
->GetDestSize() ),
2265 rStates
.getState() ) );
2269 maActions
.push_back(
2272 io_rCurrActionIndex
) );
2274 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2279 case META_BMPEX_ACTION
:
2281 MetaBmpExAction
* pAct
= static_cast<MetaBmpExAction
*>(pCurrAct
);
2283 ActionSharedPtr
pBmpAction(
2284 internal::BitmapActionFactory::createBitmapAction(
2285 pAct
->GetBitmapEx(),
2286 rStates
.getState().mapModeTransform
*
2287 ::vcl::unotools::b2DPointFromPoint( pAct
->GetPoint() ),
2289 rStates
.getState() ) );
2293 maActions
.push_back(
2296 io_rCurrActionIndex
) );
2298 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2303 case META_BMPEXSCALE_ACTION
:
2305 MetaBmpExScaleAction
* pAct
= static_cast<MetaBmpExScaleAction
*>(pCurrAct
);
2307 ActionSharedPtr
pBmpAction(
2308 internal::BitmapActionFactory::createBitmapAction(
2309 pAct
->GetBitmapEx(),
2310 rStates
.getState().mapModeTransform
*
2311 ::vcl::unotools::b2DPointFromPoint( pAct
->GetPoint() ),
2312 rStates
.getState().mapModeTransform
*
2313 ::vcl::unotools::b2DSizeFromSize( pAct
->GetSize() ),
2315 rStates
.getState() ) );
2319 maActions
.push_back(
2322 io_rCurrActionIndex
) );
2324 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2329 case META_BMPEXSCALEPART_ACTION
:
2331 MetaBmpExScalePartAction
* pAct
= static_cast<MetaBmpExScalePartAction
*>(pCurrAct
);
2333 // crop bitmap to given source rectangle (no
2334 // need to copy and convert the whole bitmap)
2335 BitmapEx
aBmp( pAct
->GetBitmapEx() );
2336 const Rectangle
aCropRect( pAct
->GetSrcPoint(),
2337 pAct
->GetSrcSize() );
2338 aBmp
.Crop( aCropRect
);
2340 ActionSharedPtr
pBmpAction(
2341 internal::BitmapActionFactory::createBitmapAction(
2343 rStates
.getState().mapModeTransform
*
2344 ::vcl::unotools::b2DPointFromPoint( pAct
->GetDestPoint() ),
2345 rStates
.getState().mapModeTransform
*
2346 ::vcl::unotools::b2DSizeFromSize( pAct
->GetDestSize() ),
2348 rStates
.getState() ) );
2352 maActions
.push_back(
2355 io_rCurrActionIndex
) );
2357 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2362 case META_MASK_ACTION
:
2364 MetaMaskAction
* pAct
= static_cast<MetaMaskAction
*>(pCurrAct
);
2366 // create masked BitmapEx right here, as the
2367 // canvas does not provide equivalent
2369 BitmapEx
aBmp( createMaskBmpEx( pAct
->GetBitmap(),
2370 pAct
->GetColor() ));
2372 ActionSharedPtr
pBmpAction(
2373 internal::BitmapActionFactory::createBitmapAction(
2375 rStates
.getState().mapModeTransform
*
2376 ::vcl::unotools::b2DPointFromPoint( pAct
->GetPoint() ),
2378 rStates
.getState() ) );
2382 maActions
.push_back(
2385 io_rCurrActionIndex
) );
2387 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2392 case META_MASKSCALE_ACTION
:
2394 MetaMaskScaleAction
* pAct
= static_cast<MetaMaskScaleAction
*>(pCurrAct
);
2396 // create masked BitmapEx right here, as the
2397 // canvas does not provide equivalent
2399 BitmapEx
aBmp( createMaskBmpEx( pAct
->GetBitmap(),
2400 pAct
->GetColor() ));
2402 ActionSharedPtr
pBmpAction(
2403 internal::BitmapActionFactory::createBitmapAction(
2405 rStates
.getState().mapModeTransform
*
2406 ::vcl::unotools::b2DPointFromPoint( pAct
->GetPoint() ),
2407 rStates
.getState().mapModeTransform
*
2408 ::vcl::unotools::b2DSizeFromSize( pAct
->GetSize() ),
2410 rStates
.getState() ) );
2414 maActions
.push_back(
2417 io_rCurrActionIndex
) );
2419 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2424 case META_MASKSCALEPART_ACTION
:
2426 MetaMaskScalePartAction
* pAct
= static_cast<MetaMaskScalePartAction
*>(pCurrAct
);
2428 // create masked BitmapEx right here, as the
2429 // canvas does not provide equivalent
2431 BitmapEx
aBmp( createMaskBmpEx( pAct
->GetBitmap(),
2432 pAct
->GetColor() ));
2434 // crop bitmap to given source rectangle (no
2435 // need to copy and convert the whole bitmap)
2436 const Rectangle
aCropRect( pAct
->GetSrcPoint(),
2437 pAct
->GetSrcSize() );
2438 aBmp
.Crop( aCropRect
);
2440 ActionSharedPtr
pBmpAction(
2441 internal::BitmapActionFactory::createBitmapAction(
2443 rStates
.getState().mapModeTransform
*
2444 ::vcl::unotools::b2DPointFromPoint( pAct
->GetDestPoint() ),
2445 rStates
.getState().mapModeTransform
*
2446 ::vcl::unotools::b2DSizeFromSize( pAct
->GetDestSize() ),
2448 rStates
.getState() ) );
2452 maActions
.push_back(
2455 io_rCurrActionIndex
) );
2457 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2462 case META_GRADIENTEX_ACTION
:
2463 // TODO(F1): use native Canvas gradients here
2464 // action is ignored here, because redundant to META_GRADIENT_ACTION
2467 case META_WALLPAPER_ACTION
:
2471 case META_TRANSPARENT_ACTION
:
2473 const OutDevState
& rState( rStates
.getState() );
2474 if( rState
.lineColor
.getLength() ||
2475 rState
.fillColor
.getLength() )
2477 MetaTransparentAction
* pAct
= static_cast<MetaTransparentAction
*>(pCurrAct
);
2478 ::basegfx::B2DPolyPolygon
aPoly( pAct
->GetPolyPolygon().getB2DPolyPolygon() );
2479 aPoly
.transform( rState
.mapModeTransform
);
2481 ActionSharedPtr
pPolyAction(
2482 internal::PolyPolyActionFactory::createPolyPolyAction(
2486 pAct
->GetTransparence() ) );
2490 maActions
.push_back(
2493 io_rCurrActionIndex
) );
2495 io_rCurrActionIndex
+= pPolyAction
->getActionCount()-1;
2501 case META_FLOATTRANSPARENT_ACTION
:
2503 MetaFloatTransparentAction
* pAct
= static_cast<MetaFloatTransparentAction
*>(pCurrAct
);
2505 internal::MtfAutoPtr
pMtf(
2506 new ::GDIMetaFile( pAct
->GetGDIMetaFile() ) );
2508 // TODO(P2): Use native canvas gradients here (saves a lot of UNO calls)
2509 internal::GradientAutoPtr
pGradient(
2510 new Gradient( pAct
->GetGradient() ) );
2512 DBG_TESTSOLARMUTEX();
2514 ActionSharedPtr
pFloatTransAction(
2515 internal::TransparencyGroupActionFactory::createTransparencyGroupAction(
2519 rStates
.getState().mapModeTransform
*
2520 ::vcl::unotools::b2DPointFromPoint( pAct
->GetPoint() ),
2521 rStates
.getState().mapModeTransform
*
2522 ::vcl::unotools::b2DSizeFromSize( pAct
->GetSize() ),
2524 rStates
.getState() ) );
2526 if( pFloatTransAction
)
2528 maActions
.push_back(
2531 io_rCurrActionIndex
) );
2533 io_rCurrActionIndex
+= pFloatTransAction
->getActionCount()-1;
2538 case META_TEXT_ACTION
:
2540 MetaTextAction
* pAct
= static_cast<MetaTextAction
*>(pCurrAct
);
2541 XubString sText
= XubString( pAct
->GetText() );
2543 if( rVDev
.GetDigitLanguage())
2544 convertToLocalizedNumerals ( sText
,rVDev
.GetDigitLanguage() );
2550 pAct
->GetLen() == (sal_uInt16
)STRING_LEN
? pAct
->GetText().getLength() - pAct
->GetIndex() : pAct
->GetLen(),
2553 bSubsettableActions
);
2557 case META_TEXTARRAY_ACTION
:
2559 MetaTextArrayAction
* pAct
= static_cast<MetaTextArrayAction
*>(pCurrAct
);
2560 XubString sText
= XubString( pAct
->GetText() );
2562 if( rVDev
.GetDigitLanguage())
2563 convertToLocalizedNumerals ( sText
,rVDev
.GetDigitLanguage() );
2569 pAct
->GetLen() == (sal_uInt16
)STRING_LEN
? pAct
->GetText().getLength() - pAct
->GetIndex() : pAct
->GetLen(),
2572 bSubsettableActions
);
2576 case META_TEXTLINE_ACTION
:
2578 MetaTextLineAction
* pAct
= static_cast<MetaTextLineAction
*>(pCurrAct
);
2580 const OutDevState
& rState( rStates
.getState() );
2581 const ::Size
aBaselineOffset( tools::getBaselineOffset( rState
,
2583 const ::Point
aStartPoint( pAct
->GetStartPoint() );
2584 const ::basegfx::B2DSize
aSize( rState
.mapModeTransform
*
2585 ::basegfx::B2DSize(pAct
->GetWidth(),
2588 ActionSharedPtr
pPolyAction(
2589 PolyPolyActionFactory::createPolyPolyAction(
2590 tools::createTextLinesPolyPolygon(
2591 rState
.mapModeTransform
*
2592 ::basegfx::B2DPoint(
2593 ::vcl::unotools::b2DPointFromPoint(pAct
->GetStartPoint()) +
2594 ::vcl::unotools::b2DSizeFromSize(aBaselineOffset
)),
2596 tools::createTextLineInfo( rVDev
,
2601 if( pPolyAction
.get() )
2603 maActions
.push_back(
2606 io_rCurrActionIndex
) );
2608 io_rCurrActionIndex
+= pPolyAction
->getActionCount()-1;
2613 case META_TEXTRECT_ACTION
:
2615 MetaTextRectAction
* pAct
= static_cast<MetaTextRectAction
*>(pCurrAct
);
2617 rStates
.pushState(PUSH_ALL
);
2619 // use the VDev to break up the text rect
2620 // action into readily formatted lines
2621 GDIMetaFile aTmpMtf
;
2622 rVDev
.AddTextRectActions( pAct
->GetRect(),
2627 createActions( aTmpMtf
,
2629 bSubsettableActions
);
2636 case META_STRETCHTEXT_ACTION
:
2638 MetaStretchTextAction
* pAct
= static_cast<MetaStretchTextAction
*>(pCurrAct
);
2639 XubString sText
= XubString( pAct
->GetText() );
2641 if( rVDev
.GetDigitLanguage())
2642 convertToLocalizedNumerals ( sText
,rVDev
.GetDigitLanguage() );
2644 const sal_uInt16
nLen( pAct
->GetLen() == (sal_uInt16
)STRING_LEN
?
2645 pAct
->GetText().getLength() - pAct
->GetIndex() : pAct
->GetLen() );
2647 // #i70897# Nothing to do, actually...
2651 // have to fit the text into the given
2652 // width. This is achieved by internally
2653 // generating a DX array, and uniformly
2654 // distributing the excess/insufficient width
2655 // to every logical character.
2656 ::boost::scoped_array
< sal_Int32
> pDXArray( new sal_Int32
[nLen
] );
2658 rVDev
.GetTextArray( pAct
->GetText(), pDXArray
.get(),
2659 pAct
->GetIndex(), pAct
->GetLen() );
2661 const sal_Int32
nWidthDifference( pAct
->GetWidth() - pDXArray
[ nLen
-1 ] );
2663 // Last entry of pDXArray contains total width of the text
2664 sal_Int32
* p
=pDXArray
.get();
2665 for( sal_uInt16 i
=1; i
<=nLen
; ++i
)
2667 // calc ratio for every array entry, to
2668 // distribute rounding errors 'evenly'
2669 // across the characters. Note that each
2670 // entry represents the 'end' position of
2671 // the corresponding character, thus, we
2672 // let i run from 1 to nLen.
2673 *p
++ += (sal_Int32
)i
*nWidthDifference
/nLen
;
2680 pAct
->GetLen() == (sal_uInt16
)STRING_LEN
? pAct
->GetText().getLength() - pAct
->GetIndex() : pAct
->GetLen(),
2683 bSubsettableActions
);
2687 case META_RENDERGRAPHIC_ACTION
:
2689 MetaRenderGraphicAction
* pAct
= static_cast<MetaRenderGraphicAction
*>(pCurrAct
);
2691 ActionSharedPtr
pRenderGraphicAction(
2692 internal::RenderGraphicActionFactory::createRenderGraphicAction(
2693 pAct
->GetRenderGraphic(),
2694 rStates
.getState().mapModeTransform
*
2695 ::vcl::unotools::b2DPointFromPoint( pAct
->GetPoint() ),
2696 rStates
.getState().mapModeTransform
*
2697 ::vcl::unotools::b2DSizeFromSize( pAct
->GetSize() ),
2699 rStates
.getState() ) );
2701 if( pRenderGraphicAction
)
2703 maActions
.push_back(
2705 pRenderGraphicAction
,
2706 io_rCurrActionIndex
) );
2708 io_rCurrActionIndex
+= pRenderGraphicAction
->getActionCount()-1;
2714 OSL_FAIL( "Unknown meta action type encountered" );
2718 // increment action index (each mtf action counts _at
2719 // least_ one. Some count for more, therefore,
2720 // io_rCurrActionIndex is sometimes incremented by
2721 // pAct->getActionCount()-1 above, the -1 being the
2722 // correction for the unconditional increment here).
2723 ++io_rCurrActionIndex
;
2732 class ActionRenderer
2735 ActionRenderer( const ::basegfx::B2DHomMatrix
& rTransformation
) :
2736 maTransformation( rTransformation
),
2746 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction
& rAction
)
2748 // ANDing the result. We want to fail if at least
2749 // one action failed.
2750 mbRet
&= rAction
.mpAction
->render( maTransformation
);
2753 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction
& rAction
,
2754 const Action::Subset
& rSubset
)
2756 // ANDing the result. We want to fail if at least
2757 // one action failed.
2758 mbRet
&= rAction
.mpAction
->renderSubset( maTransformation
,
2763 ::basegfx::B2DHomMatrix maTransformation
;
2770 AreaQuery( const ::basegfx::B2DHomMatrix
& rTransformation
) :
2771 maTransformation( rTransformation
),
2778 return true; // nothing can fail here
2781 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction
& rAction
)
2783 maBounds
.expand( rAction
.mpAction
->getBounds( maTransformation
) );
2786 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction
& rAction
,
2787 const Action::Subset
& rSubset
)
2789 maBounds
.expand( rAction
.mpAction
->getBounds( maTransformation
,
2793 ::basegfx::B2DRange
getBounds() const
2799 ::basegfx::B2DHomMatrix maTransformation
;
2800 ::basegfx::B2DRange maBounds
;
2803 // Doing that via inline class. Compilers tend to not inline free
2805 struct UpperBoundActionIndexComparator
2807 bool operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction
& rLHS
,
2808 const ::cppcanvas::internal::ImplRenderer::MtfAction
& rRHS
)
2810 const sal_Int32
nLHSCount( rLHS
.mpAction
?
2811 rLHS
.mpAction
->getActionCount() : 0 );
2812 const sal_Int32
nRHSCount( rRHS
.mpAction
?
2813 rRHS
.mpAction
->getActionCount() : 0 );
2815 // compare end of action range, to have an action selected
2816 // by lower_bound even if the requested index points in
2817 // the middle of the action's range
2818 return rLHS
.mnOrigIndex
+ nLHSCount
< rRHS
.mnOrigIndex
+ nRHSCount
;
2822 /** Algorithm to apply given functor to a subset range
2826 Functor to call for each element of the subset
2827 range. Must provide the following method signatures:
2828 bool result() (returning false if operation failed)
2831 template< typename Functor
> bool
2832 forSubsetRange( Functor
& rFunctor
,
2833 ImplRenderer::ActionVector::const_iterator aRangeBegin
,
2834 ImplRenderer::ActionVector::const_iterator aRangeEnd
,
2835 sal_Int32 nStartIndex
,
2836 sal_Int32 nEndIndex
,
2837 const ImplRenderer::ActionVector::const_iterator
& rEnd
)
2839 if( aRangeBegin
== aRangeEnd
)
2841 // only a single action. Setup subset, and call functor
2842 Action::Subset aSubset
;
2843 aSubset
.mnSubsetBegin
= ::std::max( sal_Int32( 0 ),
2844 nStartIndex
- aRangeBegin
->mnOrigIndex
);
2845 aSubset
.mnSubsetEnd
= ::std::min( aRangeBegin
->mpAction
->getActionCount(),
2846 nEndIndex
- aRangeBegin
->mnOrigIndex
);
2848 ENSURE_OR_RETURN_FALSE( aSubset
.mnSubsetBegin
>= 0 && aSubset
.mnSubsetEnd
>= 0,
2849 "ImplRenderer::forSubsetRange(): Invalid indices" );
2851 rFunctor( *aRangeBegin
, aSubset
);
2855 // more than one action.
2857 // render partial first, full intermediate, and
2858 // partial last action
2859 Action::Subset aSubset
;
2860 aSubset
.mnSubsetBegin
= ::std::max( sal_Int32( 0 ),
2861 nStartIndex
- aRangeBegin
->mnOrigIndex
);
2862 aSubset
.mnSubsetEnd
= aRangeBegin
->mpAction
->getActionCount();
2864 ENSURE_OR_RETURN_FALSE( aSubset
.mnSubsetBegin
>= 0 && aSubset
.mnSubsetEnd
>= 0,
2865 "ImplRenderer::forSubsetRange(): Invalid indices" );
2867 rFunctor( *aRangeBegin
, aSubset
);
2869 // first action rendered, skip to next
2872 // render full middle actions
2873 while( aRangeBegin
!= aRangeEnd
)
2874 rFunctor( *aRangeBegin
++ );
2876 if( aRangeEnd
== rEnd
||
2877 aRangeEnd
->mnOrigIndex
> nEndIndex
)
2879 // aRangeEnd denotes end of action vector,
2883 // nEndIndex references something _after_
2884 // aRangeBegin, but _before_ aRangeEnd
2886 // either way: no partial action left
2887 return rFunctor
.result();
2890 aSubset
.mnSubsetBegin
= 0;
2891 aSubset
.mnSubsetEnd
= nEndIndex
- aRangeEnd
->mnOrigIndex
;
2893 ENSURE_OR_RETURN_FALSE( aSubset
.mnSubsetBegin
>= 0 && aSubset
.mnSubsetEnd
>= 0,
2894 "ImplRenderer::forSubsetRange(): Invalid indices" );
2896 rFunctor( *aRangeEnd
, aSubset
);
2899 return rFunctor
.result();
2903 bool ImplRenderer::getSubsetIndices( sal_Int32
& io_rStartIndex
,
2904 sal_Int32
& io_rEndIndex
,
2905 ActionVector::const_iterator
& o_rRangeBegin
,
2906 ActionVector::const_iterator
& o_rRangeEnd
) const
2908 ENSURE_OR_RETURN_FALSE( io_rStartIndex
<=io_rEndIndex
,
2909 "ImplRenderer::getSubsetIndices(): invalid action range" );
2911 ENSURE_OR_RETURN_FALSE( !maActions
.empty(),
2912 "ImplRenderer::getSubsetIndices(): no actions to render" );
2914 const sal_Int32
nMinActionIndex( maActions
.front().mnOrigIndex
);
2915 const sal_Int32
nMaxActionIndex( maActions
.back().mnOrigIndex
+
2916 maActions
.back().mpAction
->getActionCount() );
2918 // clip given range to permissible values (there might be
2919 // ranges before and behind the valid indices)
2920 io_rStartIndex
= ::std::max( nMinActionIndex
,
2922 io_rEndIndex
= ::std::min( nMaxActionIndex
,
2925 if( io_rStartIndex
== io_rEndIndex
||
2926 io_rStartIndex
> io_rEndIndex
)
2928 // empty range, don't render anything. The second
2929 // condition e.g. happens if the requested range lies
2930 // fully before or behind the valid action indices.
2935 const ActionVector::const_iterator
aBegin( maActions
.begin() );
2936 const ActionVector::const_iterator
aEnd( maActions
.end() );
2939 // find start and end action
2940 // =========================
2941 o_rRangeBegin
= ::std::lower_bound( aBegin
, aEnd
,
2942 MtfAction( ActionSharedPtr(), io_rStartIndex
),
2943 UpperBoundActionIndexComparator() );
2944 o_rRangeEnd
= ::std::lower_bound( aBegin
, aEnd
,
2945 MtfAction( ActionSharedPtr(), io_rEndIndex
),
2946 UpperBoundActionIndexComparator() );
2952 // ====================================================================
2954 ImplRenderer::ImplRenderer( const CanvasSharedPtr
& rCanvas
,
2955 const GDIMetaFile
& rMtf
,
2956 const Parameters
& rParams
) :
2957 CanvasGraphicHelper( rCanvas
),
2960 RTL_LOGFILE_CONTEXT( aLog
, "::cppcanvas::internal::ImplRenderer::ImplRenderer(mtf)" );
2962 OSL_ENSURE( rCanvas
.get() != NULL
&& rCanvas
->getUNOCanvas().is(),
2963 "ImplRenderer::ImplRenderer(): Invalid canvas" );
2964 OSL_ENSURE( rCanvas
->getUNOCanvas()->getDevice().is(),
2965 "ImplRenderer::ImplRenderer(): Invalid graphic device" );
2967 // make sure canvas and graphic device are valid; action
2968 // creation don't check that every time
2969 if( rCanvas
.get() == NULL
||
2970 !rCanvas
->getUNOCanvas().is() ||
2971 !rCanvas
->getUNOCanvas()->getDevice().is() )
2973 // leave actions empty
2977 VectorOfOutDevStates aStateStack
;
2979 VirtualDevice aVDev
;
2980 aVDev
.EnableOutput( sal_False
);
2982 // Setup VDev for state tracking and mapping
2983 // =========================================
2985 aVDev
.SetMapMode( rMtf
.GetPrefMapMode() );
2987 const Size
aMtfSize( rMtf
.GetPrefSize() );
2988 const Size
aMtfSizePixPre( aVDev
.LogicToPixel( aMtfSize
,
2989 rMtf
.GetPrefMapMode() ) );
2990 const Point aEmptyPt
;
2991 const Point
aMtfOriginPix( aVDev
.LogicToPixel( aEmptyPt
) );
2993 // #i44110# correct null-sized output - there are shapes
2994 // which have zero size in at least one dimension
2995 const Size
aMtfSizePix( ::std::max( aMtfSizePixPre
.Width(), 1L ),
2996 ::std::max( aMtfSizePixPre
.Height(), 1L ) );
2998 sal_Int32
nCurrActions(0);
2999 ActionFactoryParameters
aParms(aStateStack
,
3006 aStateStack
.clearStateStack();
3008 // Setup local state, such that the metafile renders
3009 // itself into a one-by-one square at the origin for
3010 // identity view and render transformations
3011 aStateStack
.getState().transform
.scale( 1.0 / aMtfSizePix
.Width(),
3012 1.0 / aMtfSizePix
.Height() );
3014 tools::calcLogic2PixelAffineTransform( aStateStack
.getState().mapModeTransform
,
3017 ColorSharedPtr
pColor( getCanvas()->createColor() );
3020 ::cppcanvas::internal::OutDevState
& rState
= aStateStack
.getState();
3021 // setup default text color to black
3023 rState
.textFillColor
=
3024 rState
.textLineColor
= pColor
->getDeviceColor( 0x000000FF );
3027 // apply overrides from the Parameters struct
3028 if( rParams
.maFillColor
.is_initialized() )
3030 ::cppcanvas::internal::OutDevState
& rState
= aStateStack
.getState();
3031 rState
.isFillColorSet
= true;
3032 rState
.fillColor
= pColor
->getDeviceColor( *rParams
.maFillColor
);
3034 if( rParams
.maLineColor
.is_initialized() )
3036 ::cppcanvas::internal::OutDevState
& rState
= aStateStack
.getState();
3037 rState
.isLineColorSet
= true;
3038 rState
.lineColor
= pColor
->getDeviceColor( *rParams
.maLineColor
);
3040 if( rParams
.maTextColor
.is_initialized() )
3042 ::cppcanvas::internal::OutDevState
& rState
= aStateStack
.getState();
3043 rState
.isTextFillColorSet
= true;
3044 rState
.isTextLineColorSet
= true;
3046 rState
.textFillColor
=
3047 rState
.textLineColor
= pColor
->getDeviceColor( *rParams
.maTextColor
);
3049 if( rParams
.maFontName
.is_initialized() ||
3050 rParams
.maFontWeight
.is_initialized() ||
3051 rParams
.maFontLetterForm
.is_initialized() ||
3052 rParams
.maFontUnderline
.is_initialized() ||
3053 rParams
.maFontProportion
.is_initialized() )
3055 ::cppcanvas::internal::OutDevState
& rState
= aStateStack
.getState();
3057 rState
.xFont
= createFont( rState
.fontRotation
,
3058 ::Font(), // default font
3063 memset (aObjects
, 0, sizeof (aObjects
));
3064 mbMultipart
= false;
3066 createActions( const_cast<GDIMetaFile
&>(rMtf
), // HACK(Q2):
3075 true // TODO(P1): make subsettability configurable
3079 ImplRenderer::~ImplRenderer()
3083 bool ImplRenderer::drawSubset( sal_Int32 nStartIndex
,
3084 sal_Int32 nEndIndex
) const
3086 RTL_LOGFILE_CONTEXT( aLog
, "::cppcanvas::internal::ImplRenderer::drawSubset()" );
3088 ActionVector::const_iterator aRangeBegin
;
3089 ActionVector::const_iterator aRangeEnd
;
3093 if( !getSubsetIndices( nStartIndex
, nEndIndex
,
3094 aRangeBegin
, aRangeEnd
) )
3095 return true; // nothing to render (but _that_ was successful)
3097 // now, aRangeBegin references the action in which the
3098 // subset rendering must start, and aRangeEnd references
3099 // the action in which the subset rendering must end (it
3100 // might also end right at the start of the referenced
3101 // action, such that zero of that action needs to be
3105 // render subset of actions
3106 // ========================
3108 ::basegfx::B2DHomMatrix aMatrix
;
3109 ::canvas::tools::getRenderStateTransform( aMatrix
,
3112 ActionRenderer
aRenderer( aMatrix
);
3114 return forSubsetRange( aRenderer
,
3121 catch( uno::Exception
& )
3123 OSL_FAIL( rtl::OUStringToOString(
3124 comphelper::anyToString( cppu::getCaughtException() ),
3125 RTL_TEXTENCODING_UTF8
).getStr() );
3127 // convert error to return value
3132 ::basegfx::B2DRange
ImplRenderer::getSubsetArea( sal_Int32 nStartIndex
,
3133 sal_Int32 nEndIndex
) const
3135 RTL_LOGFILE_CONTEXT( aLog
, "::cppcanvas::internal::ImplRenderer::getSubsetArea()" );
3137 ActionVector::const_iterator aRangeBegin
;
3138 ActionVector::const_iterator aRangeEnd
;
3140 if( !getSubsetIndices( nStartIndex
, nEndIndex
,
3141 aRangeBegin
, aRangeEnd
) )
3142 return ::basegfx::B2DRange(); // nothing to render -> empty range
3144 // now, aRangeBegin references the action in which the
3145 // subset querying must start, and aRangeEnd references
3146 // the action in which the subset querying must end (it
3147 // might also end right at the start of the referenced
3148 // action, such that zero of that action needs to be
3152 // query bounds for subset of actions
3153 // ==================================
3155 ::basegfx::B2DHomMatrix aMatrix
;
3156 ::canvas::tools::getRenderStateTransform( aMatrix
,
3159 AreaQuery
aQuery( aMatrix
);
3160 forSubsetRange( aQuery
,
3167 return aQuery
.getBounds();
3170 bool ImplRenderer::draw() const
3172 RTL_LOGFILE_CONTEXT( aLog
, "::cppcanvas::internal::ImplRenderer::draw()" );
3174 ::basegfx::B2DHomMatrix aMatrix
;
3175 ::canvas::tools::getRenderStateTransform( aMatrix
,
3180 return ::std::for_each( maActions
.begin(), maActions
.end(), ActionRenderer( aMatrix
) ).result();
3182 catch( uno::Exception
& )
3184 OSL_FAIL( rtl::OUStringToOString(
3185 comphelper::anyToString( cppu::getCaughtException() ),
3186 RTL_TEXTENCODING_UTF8
).getStr() );
3194 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */