1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <comphelper/string.hxx>
21 #include "vclprocessor2d.hxx"
22 #include <drawinglayer/primitive2d/textprimitive2d.hxx>
23 #include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx>
24 #include <tools/debug.hxx>
25 #include <vcl/outdev.hxx>
26 #include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
27 #include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
28 #include <basegfx/polygon/b2dpolygontools.hxx>
29 #include <drawinglayer/attribute/sdrfillgraphicattribute.hxx>
30 #include <drawinglayer/primitive2d/fillgraphicprimitive2d.hxx>
31 #include <drawinglayer/primitive2d/polypolygonprimitive2d.hxx>
32 #include <drawinglayer/primitive2d/metafileprimitive2d.hxx>
33 #include <drawinglayer/primitive2d/maskprimitive2d.hxx>
34 #include <basegfx/polygon/b2dpolypolygontools.hxx>
35 #include <vclhelperbufferdevice.hxx>
36 #include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx>
37 #include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
38 #include <drawinglayer/primitive2d/transparenceprimitive2d.hxx>
39 #include <drawinglayer/primitive2d/transformprimitive2d.hxx>
40 #include <drawinglayer/primitive2d/markerarrayprimitive2d.hxx>
41 #include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx>
42 #include <drawinglayer/primitive2d/wrongspellprimitive2d.hxx>
43 #include <drawinglayer/primitive2d/pagepreviewprimitive2d.hxx>
44 #include <tools/diagnose_ex.h>
45 #include <rtl/ustrbuf.hxx>
46 #include <vcl/metric.hxx>
47 #include <drawinglayer/primitive2d/textenumsprimitive2d.hxx>
48 #include <drawinglayer/primitive2d/epsprimitive2d.hxx>
49 #include <drawinglayer/primitive2d/svggradientprimitive2d.hxx>
50 #include <basegfx/color/bcolor.hxx>
51 #include <basegfx/matrix/b2dhommatrixtools.hxx>
52 #include <vcl/graph.hxx>
54 #include "getdigitlanguage.hxx"
58 #include <com/sun/star/awt/XWindow2.hpp>
59 #include <com/sun/star/awt/PosSize.hpp>
60 #include <com/sun/star/awt/XView.hpp>
61 #include <drawinglayer/primitive2d/controlprimitive2d.hxx>
62 #include <drawinglayer/primitive2d/textlayoutdevice.hxx>
64 // for test, can be removed again
65 #include <basegfx/polygon/b2dpolygonclipper.hxx>
66 #include <basegfx/polygon/b2dtrapezoid.hxx>
69 #include <drawinglayer/primitive2d/openglprimitive2d.hxx>
71 using namespace com::sun::star
;
75 sal_uInt32
calculateStepsForSvgGradient(const basegfx::BColor
& rColorA
, const basegfx::BColor
& rColorB
, double fDelta
, double fDiscreteUnit
)
77 // use color distance, assume to do every color step
78 sal_uInt32
nSteps(basegfx::fround(rColorA
.getDistance(rColorB
) * 255.0));
82 // calc discrete length to change color each disctete unit (pixel)
83 const sal_uInt32
nDistSteps(basegfx::fround(fDelta
/ fDiscreteUnit
));
85 nSteps
= std::min(nSteps
, nDistSteps
);
88 // reduce quality to 3 discrete units or every 3rd color step for rendering
91 // roughly cut when too big or too small (not full quality, reduce complexity)
92 nSteps
= std::min(nSteps
, sal_uInt32(255));
93 nSteps
= std::max(nSteps
, sal_uInt32(1));
99 namespace drawinglayer
101 namespace processor2d
104 using ::com::sun::star::uno::Reference
;
105 using ::com::sun::star::uno::UNO_QUERY
;
106 using ::com::sun::star::uno::UNO_QUERY_THROW
;
107 using ::com::sun::star::uno::Exception
;
108 using ::com::sun::star::awt::XView
;
109 using ::com::sun::star::awt::XGraphics
;
110 using ::com::sun::star::awt::XWindow
;
111 using ::com::sun::star::awt::PosSize::POSSIZE
;
115 // directdraw of text simple portion or decorated portion primitive. When decorated, all the extra
116 // information is translated to VCL parameters and set at the font.
117 // Acceptance is restricted to no shearing and positive scaling in X and Y (no font mirroring
119 void VclProcessor2D::RenderTextSimpleOrDecoratedPortionPrimitive2D(const primitive2d::TextSimplePortionPrimitive2D
& rTextCandidate
)
121 // decompose matrix to have position and size of text
122 basegfx::B2DHomMatrix
aLocalTransform(maCurrentTransformation
* rTextCandidate
.getTextTransform());
123 basegfx::B2DVector aFontScaling
, aTranslate
;
124 double fRotate
, fShearX
;
125 aLocalTransform
.decompose(aFontScaling
, aTranslate
, fRotate
, fShearX
);
126 bool bPrimitiveAccepted(false);
128 if(basegfx::fTools::equalZero(fShearX
))
130 if(basegfx::fTools::less(aFontScaling
.getX(), 0.0) && basegfx::fTools::less(aFontScaling
.getY(), 0.0))
132 // handle special case: If scale is negative in (x,y) (3rd quadrant), it can
133 // be expressed as rotation by PI. Use this since the Font rendering will not
134 // apply the negative scales in any form
135 aFontScaling
= basegfx::absolute(aFontScaling
);
139 if(basegfx::fTools::more(aFontScaling
.getX(), 0.0) && basegfx::fTools::more(aFontScaling
.getY(), 0.0))
141 // Get the VCL font (use FontHeight as FontWidth)
142 vcl::Font
aFont(primitive2d::getVclFontFromFontAttribute(
143 rTextCandidate
.getFontAttribute(),
147 rTextCandidate
.getLocale()));
149 // set FillColor Attribute
150 const Color
aFillColor( rTextCandidate
.getTextFillColor() );
151 if( aFillColor
!= COL_TRANSPARENT
)
153 aFont
.SetFillColor(aFillColor
);
154 aFont
.SetTransparent(false);
157 // Don't draw fonts without height
158 if( aFont
.GetHeight() <= 0 )
161 // handle additional font attributes
162 const primitive2d::TextDecoratedPortionPrimitive2D
* pTCPP
=
163 dynamic_cast<const primitive2d::TextDecoratedPortionPrimitive2D
*>( &rTextCandidate
);
168 // set the color of text decorations
169 const basegfx::BColor aTextlineColor
= maBColorModifierStack
.getModifiedColor(pTCPP
->getTextlineColor());
170 mpOutputDevice
->SetTextLineColor( Color(aTextlineColor
) );
172 // set Overline attribute
173 const FontUnderline
eFontOverline(primitive2d::mapTextLineToFontUnderline( pTCPP
->getFontOverline() ));
174 if( eFontOverline
!= UNDERLINE_NONE
)
176 aFont
.SetOverline( eFontOverline
);
177 const basegfx::BColor aOverlineColor
= maBColorModifierStack
.getModifiedColor(pTCPP
->getOverlineColor());
178 mpOutputDevice
->SetOverlineColor( Color(aOverlineColor
) );
179 if( pTCPP
->getWordLineMode() )
180 aFont
.SetWordLineMode( true );
183 // set Underline attribute
184 const FontUnderline
eFontUnderline(primitive2d::mapTextLineToFontUnderline( pTCPP
->getFontUnderline() ));
185 if( eFontUnderline
!= UNDERLINE_NONE
)
187 aFont
.SetUnderline( eFontUnderline
);
188 if( pTCPP
->getWordLineMode() )
189 aFont
.SetWordLineMode( true );
192 // set Strikeout attribute
193 const FontStrikeout
eFontStrikeout(primitive2d::mapTextStrikeoutToFontStrikeout(pTCPP
->getTextStrikeout()));
195 if( eFontStrikeout
!= STRIKEOUT_NONE
)
196 aFont
.SetStrikeout( eFontStrikeout
);
199 // set EmphasisMark attribute
200 FontEmphasisMark eFontEmphasisMark
= EMPHASISMARK_NONE
;
201 switch( pTCPP
->getTextEmphasisMark() )
204 SAL_WARN("drawinglayer", "Unknown EmphasisMark style " << pTCPP
->getTextEmphasisMark() );
206 case primitive2d::TEXT_EMPHASISMARK_NONE
: eFontEmphasisMark
= EMPHASISMARK_NONE
; break;
207 case primitive2d::TEXT_EMPHASISMARK_DOT
: eFontEmphasisMark
= EMPHASISMARK_DOT
; break;
208 case primitive2d::TEXT_EMPHASISMARK_CIRCLE
: eFontEmphasisMark
= EMPHASISMARK_CIRCLE
; break;
209 case primitive2d::TEXT_EMPHASISMARK_DISC
: eFontEmphasisMark
= EMPHASISMARK_DISC
; break;
210 case primitive2d::TEXT_EMPHASISMARK_ACCENT
: eFontEmphasisMark
= EMPHASISMARK_ACCENT
; break;
213 if( eFontEmphasisMark
!= EMPHASISMARK_NONE
)
215 DBG_ASSERT( (pTCPP
->getEmphasisMarkAbove() != pTCPP
->getEmphasisMarkBelow()),
216 "DrawingLayer: Bad EmphasisMark position!" );
217 if( pTCPP
->getEmphasisMarkAbove() )
218 eFontEmphasisMark
|= EMPHASISMARK_POS_ABOVE
;
220 eFontEmphasisMark
|= EMPHASISMARK_POS_BELOW
;
221 aFont
.SetEmphasisMark( eFontEmphasisMark
);
224 // set Relief attribute
225 FontRelief eFontRelief
= RELIEF_NONE
;
226 switch( pTCPP
->getTextRelief() )
229 SAL_WARN( "drawinglayer", "Unknown Relief style " << pTCPP
->getTextRelief() );
231 case primitive2d::TEXT_RELIEF_NONE
: eFontRelief
= RELIEF_NONE
; break;
232 case primitive2d::TEXT_RELIEF_EMBOSSED
: eFontRelief
= RELIEF_EMBOSSED
; break;
233 case primitive2d::TEXT_RELIEF_ENGRAVED
: eFontRelief
= RELIEF_ENGRAVED
; break;
236 if( eFontRelief
!= RELIEF_NONE
)
237 aFont
.SetRelief( eFontRelief
);
239 // set Shadow attribute
240 if( pTCPP
->getShadow() )
241 aFont
.SetShadow( true );
244 // create transformed integer DXArray in view coordinate system
245 ::std::vector
< long > aTransformedDXArray
;
247 if(rTextCandidate
.getDXArray().size())
249 aTransformedDXArray
.reserve(rTextCandidate
.getDXArray().size());
250 const basegfx::B2DVector
aPixelVector(maCurrentTransformation
* basegfx::B2DVector(1.0, 0.0));
251 const double fPixelVectorFactor(aPixelVector
.getLength());
253 for(::std::vector
< double >::const_iterator
aStart(rTextCandidate
.getDXArray().begin());
254 aStart
!= rTextCandidate
.getDXArray().end(); ++aStart
)
256 aTransformedDXArray
.push_back(basegfx::fround((*aStart
) * fPixelVectorFactor
));
260 // set parameters and paint text snippet
261 const basegfx::BColor
aRGBFontColor(maBColorModifierStack
.getModifiedColor(rTextCandidate
.getFontColor()));
262 const basegfx::B2DPoint
aPoint(aLocalTransform
* basegfx::B2DPoint(0.0, 0.0));
263 const Point
aStartPoint(basegfx::fround(aPoint
.getX()), basegfx::fround(aPoint
.getY()));
264 const ComplexTextLayoutMode
nOldLayoutMode(mpOutputDevice
->GetLayoutMode());
266 if(rTextCandidate
.getFontAttribute().getRTL())
268 ComplexTextLayoutMode
nRTLLayoutMode(nOldLayoutMode
& ~(TEXT_LAYOUT_COMPLEX_DISABLED
|TEXT_LAYOUT_BIDI_STRONG
));
269 nRTLLayoutMode
|= TEXT_LAYOUT_BIDI_RTL
|TEXT_LAYOUT_TEXTORIGIN_LEFT
;
270 mpOutputDevice
->SetLayoutMode(nRTLLayoutMode
);
273 mpOutputDevice
->SetFont(aFont
);
274 mpOutputDevice
->SetTextColor(Color(aRGBFontColor
));
276 OUString
aText( rTextCandidate
.getText() );
277 sal_Int32 nPos
= rTextCandidate
.getTextPosition();
278 sal_Int32 nLen
= rTextCandidate
.getTextLength();
280 long* pDXArray
= aTransformedDXArray
.size() ? &(aTransformedDXArray
[0]) : NULL
;
282 if ( rTextCandidate
.isFilled() )
284 basegfx::B2DVector aOldFontScaling
, aOldTranslate
;
285 double fOldRotate
, fOldShearX
;
286 rTextCandidate
.getTextTransform().decompose(aOldFontScaling
, aOldTranslate
, fOldRotate
, fOldShearX
);
288 long nWidthToFill
= static_cast<long>(rTextCandidate
.getWidthToFill( ) * aFontScaling
.getX() / aOldFontScaling
.getX());
290 long nWidth
= mpOutputDevice
->GetTextArray( rTextCandidate
.getText(), pDXArray
, 0, 1 );
293 nChars
= nWidthToFill
/ nWidth
;
295 OUStringBuffer aFilled
;
296 comphelper::string::padToLength(aFilled
, nChars
, aText
[0]);
297 aText
= aFilled
.makeStringAndClear();
302 if(!aTransformedDXArray
.empty())
304 mpOutputDevice
->DrawTextArray(
313 mpOutputDevice
->DrawText(
320 if(rTextCandidate
.getFontAttribute().getRTL())
322 mpOutputDevice
->SetLayoutMode(nOldLayoutMode
);
325 bPrimitiveAccepted
= true;
329 if(!bPrimitiveAccepted
)
332 process(rTextCandidate
.get2DDecomposition(getViewInformation2D()));
336 // direct draw of hairline
337 void VclProcessor2D::RenderPolygonHairlinePrimitive2D(const primitive2d::PolygonHairlinePrimitive2D
& rPolygonCandidate
, bool bPixelBased
)
339 const basegfx::BColor
aHairlineColor(maBColorModifierStack
.getModifiedColor(rPolygonCandidate
.getBColor()));
340 mpOutputDevice
->SetLineColor(Color(aHairlineColor
));
341 mpOutputDevice
->SetFillColor();
343 basegfx::B2DPolygon
aLocalPolygon(rPolygonCandidate
.getB2DPolygon());
344 aLocalPolygon
.transform(maCurrentTransformation
);
346 static bool bCheckTrapezoidDecomposition(false);
347 static bool bShowOutlinesThere(false);
348 if(bCheckTrapezoidDecomposition
)
350 // clip against discrete ViewPort
351 const basegfx::B2DRange
& rDiscreteViewport
= getViewInformation2D().getDiscreteViewport();
352 basegfx::B2DPolyPolygon
aLocalPolyPolygon(basegfx::tools::clipPolygonOnRange(
353 aLocalPolygon
, rDiscreteViewport
, true, false));
355 if(aLocalPolyPolygon
.count())
358 aLocalPolyPolygon
= basegfx::tools::adaptiveSubdivideByDistance(
359 aLocalPolyPolygon
, 0.5);
362 static double fLineWidth(2.0);
363 basegfx::B2DTrapezoidVector aB2DTrapezoidVector
;
364 basegfx::tools::createLineTrapezoidFromB2DPolyPolygon(aB2DTrapezoidVector
, aLocalPolyPolygon
, fLineWidth
);
366 const sal_uInt32
nCount(aB2DTrapezoidVector
.size());
370 basegfx::BColor
aInvPolygonColor(aHairlineColor
);
371 aInvPolygonColor
.invert();
373 for(sal_uInt32
a(0); a
< nCount
; a
++)
375 const basegfx::B2DPolygon
aTempPolygon(aB2DTrapezoidVector
[a
].getB2DPolygon());
377 if(bShowOutlinesThere
)
379 mpOutputDevice
->SetFillColor(Color(aHairlineColor
));
380 mpOutputDevice
->SetLineColor();
383 mpOutputDevice
->DrawPolygon(aTempPolygon
);
385 if(bShowOutlinesThere
)
387 mpOutputDevice
->SetFillColor();
388 mpOutputDevice
->SetLineColor(Color(aInvPolygonColor
));
389 mpOutputDevice
->DrawPolyLine(aTempPolygon
, 0.0);
397 if(bPixelBased
&& getOptionsDrawinglayer().IsAntiAliasing() && getOptionsDrawinglayer().IsSnapHorVerLinesToDiscrete())
400 // when a Hairline is painted and AntiAliasing is on the option SnapHorVerLinesToDiscrete
401 // allows to suppress AntiAliasing for pure horizontal or vertical lines. This is done since
402 // not-AntiAliased such lines look more pleasing to the eye (e.g. 2D chart content). This
403 // NEEDS to be done in discrete coordinates, so only useful for pixel based rendering.
404 aLocalPolygon
= basegfx::tools::snapPointsOfHorizontalOrVerticalEdges(aLocalPolygon
);
407 mpOutputDevice
->DrawPolyLine(aLocalPolygon
, 0.0);
411 // direct draw of transformed BitmapEx primitive
412 void VclProcessor2D::RenderBitmapPrimitive2D(const primitive2d::BitmapPrimitive2D
& rBitmapCandidate
)
414 BitmapEx
aBitmapEx(rBitmapCandidate
.getBitmapEx());
415 const basegfx::B2DHomMatrix
aLocalTransform(maCurrentTransformation
* rBitmapCandidate
.getTransform());
417 if(maBColorModifierStack
.count())
419 aBitmapEx
= aBitmapEx
.ModifyBitmapEx(maBColorModifierStack
);
421 if(aBitmapEx
.IsEmpty())
423 // color gets completely replaced, get it
424 const basegfx::BColor
aModifiedColor(maBColorModifierStack
.getModifiedColor(basegfx::BColor()));
425 basegfx::B2DPolygon
aPolygon(basegfx::tools::createUnitPolygon());
426 aPolygon
.transform(aLocalTransform
);
428 mpOutputDevice
->SetFillColor(Color(aModifiedColor
));
429 mpOutputDevice
->SetLineColor();
430 mpOutputDevice
->DrawPolygon(aPolygon
);
436 // #122923# do no longer add Alpha channel here; the right place to do this is when really
437 // the own transformer is used (see OutputDevice::DrawTransformedBitmapEx).
439 // draw using OutputDevice'sDrawTransformedBitmapEx
440 mpOutputDevice
->DrawTransformedBitmapEx(aLocalTransform
, aBitmapEx
);
443 void VclProcessor2D::RenderFillGraphicPrimitive2D(const primitive2d::FillGraphicPrimitive2D
& rFillBitmapCandidate
)
445 const attribute::FillGraphicAttribute
& rFillGraphicAttribute(rFillBitmapCandidate
.getFillGraphic());
446 bool bPrimitiveAccepted(false);
447 static bool bTryTilingDirect
= true;
449 // #121194# when tiling is used and content is bitmap-based, do direct tiling in the
450 // renderer on pixel base to ensure tight fitting. Do not do this when
451 // the fill is rotated or sheared.
453 // ovveride static bool (for debug) and tiling is active
454 if(bTryTilingDirect
&& rFillGraphicAttribute
.getTiling())
456 // content is bitmap(ex)
458 // for SVG support, force decomposition when SVG is present. This will lead to use
459 // the primitive representation of the svg directly.
461 // when graphic is animated, force decomposition to use the correct graphic, else
462 // fill style will not be animated
463 if(GRAPHIC_BITMAP
== rFillGraphicAttribute
.getGraphic().GetType()
464 && !rFillGraphicAttribute
.getGraphic().getSvgData().get()
465 && !rFillGraphicAttribute
.getGraphic().IsAnimated())
467 // decompose matrix to check for shear, rotate and mirroring
468 basegfx::B2DHomMatrix
aLocalTransform(maCurrentTransformation
* rFillBitmapCandidate
.getTransformation());
469 basegfx::B2DVector aScale
, aTranslate
;
470 double fRotate
, fShearX
;
471 aLocalTransform
.decompose(aScale
, aTranslate
, fRotate
, fShearX
);
473 // when nopt rotated/sheared
474 if(basegfx::fTools::equalZero(fRotate
) && basegfx::fTools::equalZero(fShearX
))
476 // no shear or rotate, draw direct in pixel coordinates
477 bPrimitiveAccepted
= true;
479 // transform object range to device coordinates (pixels). Use
480 // the device transformation for better accuracy
481 basegfx::B2DRange
aObjectRange(aTranslate
, aTranslate
+ aScale
);
482 aObjectRange
.transform(mpOutputDevice
->GetViewTransformation());
484 // extract discrete size of object
485 const sal_Int32
nOWidth(basegfx::fround(aObjectRange
.getWidth()));
486 const sal_Int32
nOHeight(basegfx::fround(aObjectRange
.getHeight()));
488 // only do something when object has a size in discrete units
489 if(nOWidth
> 0 && nOHeight
> 0)
491 // transform graphic range to device coordinates (pixels). Use
492 // the device transformation for better accuracy
493 basegfx::B2DRange
aGraphicRange(rFillGraphicAttribute
.getGraphicRange());
494 aGraphicRange
.transform(mpOutputDevice
->GetViewTransformation() * aLocalTransform
);
496 // extract discrete size of graphic
497 // caution: when getting to zero, nothing would be painted; thus, do not allow this
498 const sal_Int32
nBWidth(std::max(sal_Int32(1), basegfx::fround(aGraphicRange
.getWidth())));
499 const sal_Int32
nBHeight(std::max(sal_Int32(1), basegfx::fround(aGraphicRange
.getHeight())));
501 // only do something when bitmap fill has a size in discrete units
502 if(nBWidth
> 0 && nBHeight
> 0)
504 // nBWidth, nBHeight is the pixel size of the neede bitmap. To not need to scale it
505 // in vcl many times, create a size-optimized version
506 const Size
aNeededBitmapSizePixel(nBWidth
, nBHeight
);
507 BitmapEx
aBitmapEx(rFillGraphicAttribute
.getGraphic().GetBitmapEx());
508 static bool bEnablePreScaling(true);
509 const bool bPreScaled(bEnablePreScaling
&& nBWidth
* nBHeight
< (250 * 250));
511 // ... but only up to a maximum size, else it gets too expensive
514 // if color depth is below 24bit, expand before scaling for better quality.
515 // This is even needed for low colors, else the scale will produce
516 // a bitmap in gray or Black/White (!)
517 if(aBitmapEx
.GetBitCount() < 24)
519 aBitmapEx
.Convert(BMP_CONVERSION_24BIT
);
522 aBitmapEx
.Scale(aNeededBitmapSizePixel
, BmpScaleFlag::Interpolate
);
525 bool bPainted(false);
527 if(maBColorModifierStack
.count())
529 // when color modifier, apply to bitmap
530 aBitmapEx
= aBitmapEx
.ModifyBitmapEx(maBColorModifierStack
);
532 // impModifyBitmapEx uses empty bitmap as sign to return that
533 // the content will be completely replaced to mono color, use shortcut
534 if(aBitmapEx
.IsEmpty())
536 // color gets completely replaced, get it
537 const basegfx::BColor
aModifiedColor(maBColorModifierStack
.getModifiedColor(basegfx::BColor()));
538 basegfx::B2DPolygon
aPolygon(basegfx::tools::createUnitPolygon());
539 aPolygon
.transform(aLocalTransform
);
541 mpOutputDevice
->SetFillColor(Color(aModifiedColor
));
542 mpOutputDevice
->SetLineColor();
543 mpOutputDevice
->DrawPolygon(aPolygon
);
551 sal_Int32
nBLeft(basegfx::fround(aGraphicRange
.getMinX()));
552 sal_Int32
nBTop(basegfx::fround(aGraphicRange
.getMinY()));
553 const sal_Int32
nOLeft(basegfx::fround(aObjectRange
.getMinX()));
554 const sal_Int32
nOTop(basegfx::fround(aObjectRange
.getMinY()));
560 const sal_Int32
nDiff((nBLeft
/ nBWidth
) + 1);
563 nBLeft
-= nDiff
* nBWidth
;
566 if(nBLeft
+ nBWidth
<= nOLeft
)
568 const sal_Int32
nDiff(-nBLeft
/ nBWidth
);
571 nBLeft
+= nDiff
* nBWidth
;
576 const sal_Int32
nDiff((nBTop
/ nBHeight
) + 1);
579 nBTop
-= nDiff
* nBHeight
;
582 if(nBTop
+ nBHeight
<= nOTop
)
584 const sal_Int32
nDiff(-nBTop
/ nBHeight
);
587 nBTop
+= nDiff
* nBHeight
;
591 const Point
aEmptyPoint(0, 0);
592 const Rectangle
aVisiblePixel(aEmptyPoint
, mpOutputDevice
->GetOutputSizePixel());
593 const bool bWasEnabled(mpOutputDevice
->IsMapModeEnabled());
594 mpOutputDevice
->EnableMapMode(false);
596 // check if offset is used
597 const sal_Int32
nOffsetX(basegfx::fround(rFillGraphicAttribute
.getOffsetX() * nBWidth
));
601 // offset in X, so iterate over Y first and draw lines
602 for(sal_Int32
nYPos(nBTop
); nYPos
< nOTop
+ nOHeight
; nYPos
+= nBHeight
, nPosY
++)
604 for(sal_Int32
nXPos(nPosY
% 2 ? nBLeft
- nBWidth
+ nOffsetX
: nBLeft
);
605 nXPos
< nOLeft
+ nOWidth
; nXPos
+= nBWidth
)
607 const Rectangle
aOutRectPixel(Point(nXPos
, nYPos
), aNeededBitmapSizePixel
);
609 if(aOutRectPixel
.IsOver(aVisiblePixel
))
613 mpOutputDevice
->DrawBitmapEx(aOutRectPixel
.TopLeft(), aBitmapEx
);
617 mpOutputDevice
->DrawBitmapEx(aOutRectPixel
.TopLeft(), aNeededBitmapSizePixel
, aBitmapEx
);
625 // check if offset is used
626 const sal_Int32
nOffsetY(basegfx::fround(rFillGraphicAttribute
.getOffsetY() * nBHeight
));
628 // possible offset in Y, so iterate over X first and draw columns
629 for(sal_Int32
nXPos(nBLeft
); nXPos
< nOLeft
+ nOWidth
; nXPos
+= nBWidth
, nPosX
++)
631 for(sal_Int32
nYPos(nPosX
% 2 ? nBTop
- nBHeight
+ nOffsetY
: nBTop
);
632 nYPos
< nOTop
+ nOHeight
; nYPos
+= nBHeight
)
634 const Rectangle
aOutRectPixel(Point(nXPos
, nYPos
), aNeededBitmapSizePixel
);
636 if(aOutRectPixel
.IsOver(aVisiblePixel
))
640 mpOutputDevice
->DrawBitmapEx(aOutRectPixel
.TopLeft(), aBitmapEx
);
644 mpOutputDevice
->DrawBitmapEx(aOutRectPixel
.TopLeft(), aNeededBitmapSizePixel
, aBitmapEx
);
652 mpOutputDevice
->EnableMapMode(bWasEnabled
);
660 if(!bPrimitiveAccepted
)
662 // do not accept, use decomposition
663 process(rFillBitmapCandidate
.get2DDecomposition(getViewInformation2D()));
667 // direct draw of Graphic
668 void VclProcessor2D::RenderPolyPolygonGraphicPrimitive2D(const primitive2d::PolyPolygonGraphicPrimitive2D
& rPolygonCandidate
)
671 const basegfx::B2DPolyPolygon
& rPolyPolygon
= rPolygonCandidate
.getB2DPolyPolygon();
673 // #121194# Todo: check if this works
674 if(!rPolyPolygon
.count())
676 // empty polyPolygon, done
681 const attribute::FillGraphicAttribute
& rFillGraphicAttribute
= rPolygonCandidate
.getFillGraphic();
683 // try to catch cases where the graphic will be color-modified to a single
684 // color (e.g. shadow)
685 switch(rFillGraphicAttribute
.getGraphic().GetType())
687 case GRAPHIC_GDIMETAFILE
:
689 // metafiles are potentially transparent, cannot optimize, not done
694 if(!rFillGraphicAttribute
.getGraphic().IsTransparent() && !rFillGraphicAttribute
.getGraphic().IsAlpha())
696 // bitmap is not transparent and has no alpha
697 const sal_uInt32
nBColorModifierStackCount(maBColorModifierStack
.count());
699 if(nBColorModifierStackCount
)
701 const basegfx::BColorModifierSharedPtr
& rTopmostModifier
= maBColorModifierStack
.getBColorModifier(nBColorModifierStackCount
- 1);
702 const basegfx::BColorModifier_replace
* pReplacer
= dynamic_cast< const basegfx::BColorModifier_replace
* >(rTopmostModifier
.get());
706 // the bitmap fill is in unified color, so we can replace it with
707 // a single polygon fill. The form of the fill depends on tiling
708 if(rFillGraphicAttribute
.getTiling())
710 // with tiling, fill the whole tools::PolyPolygon with the modifier color
711 basegfx::B2DPolyPolygon
aLocalPolyPolygon(rPolyPolygon
);
713 aLocalPolyPolygon
.transform(maCurrentTransformation
);
714 mpOutputDevice
->SetLineColor();
715 mpOutputDevice
->SetFillColor(Color(pReplacer
->getBColor()));
716 mpOutputDevice
->DrawPolyPolygon(aLocalPolyPolygon
);
720 // without tiling, only the area common to the bitmap tile and the
721 // tools::PolyPolygon is filled. Create the bitmap tile area in object
722 // coordinates. For this, the object transformation needs to be created
723 // from the already scaled PolyPolygon. The tile area in object
724 // coordinates wil always be non-rotated, so it's not necessary to
725 // work with a polygon here
726 basegfx::B2DRange
aTileRange(rFillGraphicAttribute
.getGraphicRange());
727 const basegfx::B2DRange
aPolyPolygonRange(rPolyPolygon
.getB2DRange());
728 const basegfx::B2DHomMatrix
aNewObjectTransform(
729 basegfx::tools::createScaleTranslateB2DHomMatrix(
730 aPolyPolygonRange
.getRange(),
731 aPolyPolygonRange
.getMinimum()));
733 aTileRange
.transform(aNewObjectTransform
);
735 // now clip the object polyPolygon against the tile range
736 // to get the common area
737 basegfx::B2DPolyPolygon aTarget
= basegfx::tools::clipPolyPolygonOnRange(
745 aTarget
.transform(maCurrentTransformation
);
746 mpOutputDevice
->SetLineColor();
747 mpOutputDevice
->SetFillColor(Color(pReplacer
->getBColor()));
748 mpOutputDevice
->DrawPolyPolygon(aTarget
);
752 // simplified output executed, we are done
759 default: //GRAPHIC_NONE, GRAPHIC_DEFAULT
761 // empty graphic, we are done
770 // use default decomposition
771 process(rPolygonCandidate
.get2DDecomposition(getViewInformation2D()));
775 // direct draw of MetaFile
776 void VclProcessor2D::RenderMetafilePrimitive2D(const primitive2d::MetafilePrimitive2D
& rMetaCandidate
)
778 // decompose matrix to check for shear, rotate and mirroring
779 basegfx::B2DHomMatrix
aLocalTransform(maCurrentTransformation
* rMetaCandidate
.getTransform());
780 basegfx::B2DVector aScale
, aTranslate
;
781 double fRotate
, fShearX
;
782 aLocalTransform
.decompose(aScale
, aTranslate
, fRotate
, fShearX
);
784 if(basegfx::fTools::less(aScale
.getX(), 0.0) && basegfx::fTools::less(aScale
.getY(), 0.0))
786 // #i102175# handle special case: If scale is negative in (x,y) (3rd quadrant), it can
787 // be expressed as rotation by PI. This needs to be done for Metafiles since
788 // these can be rotated, but not really mirrored
789 aScale
= basegfx::absolute(aScale
);
794 basegfx::B2DRange
aOutlineRange(rMetaCandidate
.getB2DRange(getViewInformation2D()));
795 aOutlineRange
.transform(maCurrentTransformation
);
797 // Due to the integer MapModes used from VCL aind inside MetaFiles errors of up to three
798 // pixels in size may happen. As long as there is no better way (e.g. convert the MetaFile
799 // to primitives) it is necessary to reduce maximum pixel size by 1 in X and Y and to use
800 // the inner pixel bounds accordingly (ceil resp. floor). This will also be done for logic
801 // units e.g. when creating a new MetaFile, but since much huger value ranges are used
802 // there typically will be okay for this compromize.
803 Rectangle
aDestRectView(
804 // !!CAUTION!! Here, ceil and floor are exchanged BY PURPOSE, do NOT copy when
805 // looking for a standard conversion to rectangle (!)
806 (sal_Int32
)ceil(aOutlineRange
.getMinX()), (sal_Int32
)ceil(aOutlineRange
.getMinY()),
807 (sal_Int32
)floor(aOutlineRange
.getMaxX()), (sal_Int32
)floor(aOutlineRange
.getMaxY()));
809 // get metafile (copy it)
810 GDIMetaFile aMetaFile
;
812 if(maBColorModifierStack
.count())
814 const basegfx::BColor
aRGBBaseColor(0, 0, 0);
815 const basegfx::BColor
aRGBColor(maBColorModifierStack
.getModifiedColor(aRGBBaseColor
));
816 aMetaFile
= rMetaCandidate
.getMetaFile().GetMonochromeMtf(Color(aRGBColor
));
820 aMetaFile
= rMetaCandidate
.getMetaFile();
824 if(!basegfx::fTools::equalZero(fRotate
))
827 // MetaFile::Rotate has no input parameter check, so the parameter needs to be
828 // well-aligned to the old range [0..3600] 10th degrees with inverse orientation
829 sal_Int16
nRotation((sal_Int16
)((fRotate
/ F_PI180
) * -10.0));
834 while(nRotation
>= 3600)
837 aMetaFile
.Rotate(nRotation
);
840 // Prepare target output size
841 Size
aDestSize(aDestRectView
.GetSize());
843 if(aDestSize
.getWidth() > 0 && aDestSize
.getHeight() > 0)
845 // Get preferred Metafile output size. When it's very equal to the output size, it's probably
846 // a rounding error somewhere, so correct it to get a 1:1 output without single pixel scalings
847 // of the Metafile (esp. for contaned Bitmaps, e.g 3D charts)
848 const Size
aPrefSize(mpOutputDevice
->LogicToPixel(aMetaFile
.GetPrefSize(), aMetaFile
.GetPrefMapMode()));
850 if(aPrefSize
.getWidth() && (aPrefSize
.getWidth() - 1 == aDestSize
.getWidth() || aPrefSize
.getWidth() + 1 == aDestSize
.getWidth()))
852 aDestSize
.setWidth(aPrefSize
.getWidth());
855 if(aPrefSize
.getHeight() && (aPrefSize
.getHeight() - 1 == aDestSize
.getHeight() || aPrefSize
.getHeight() + 1 == aDestSize
.getHeight()))
857 aDestSize
.setHeight(aPrefSize
.getHeight());
861 aMetaFile
.WindStart();
862 aMetaFile
.Play(mpOutputDevice
, aDestRectView
.TopLeft(), aDestSize
);
866 // mask group. Force output to VDev and create mask from given mask
867 void VclProcessor2D::RenderMaskPrimitive2DPixel(const primitive2d::MaskPrimitive2D
& rMaskCandidate
)
869 if(rMaskCandidate
.getChildren().hasElements())
871 basegfx::B2DPolyPolygon
aMask(rMaskCandidate
.getMask());
875 aMask
.transform(maCurrentTransformation
);
876 const basegfx::B2DRange
aRange(basegfx::tools::getRange(aMask
));
877 impBufferDevice
aBufferDevice(*mpOutputDevice
, aRange
, true);
879 if(aBufferDevice
.isVisible())
881 // remember last OutDev and set to content
882 OutputDevice
* pLastOutputDevice
= mpOutputDevice
;
883 mpOutputDevice
= &aBufferDevice
.getContent();
886 process(rMaskCandidate
.getChildren());
888 // back to old OutDev
889 mpOutputDevice
= pLastOutputDevice
;
892 if(getOptionsDrawinglayer().IsAntiAliasing())
894 // with AA, use 8bit AlphaMask to get nice borders
895 VirtualDevice
& rTransparence
= aBufferDevice
.getTransparence();
896 rTransparence
.SetLineColor();
897 rTransparence
.SetFillColor(COL_BLACK
);
898 rTransparence
.DrawPolyPolygon(aMask
);
900 // dump buffer to outdev
901 aBufferDevice
.paint();
905 // No AA, use 1bit mask
906 VirtualDevice
& rMask
= aBufferDevice
.getMask();
907 rMask
.SetLineColor();
908 rMask
.SetFillColor(COL_BLACK
);
909 rMask
.DrawPolyPolygon(aMask
);
911 // dump buffer to outdev
912 aBufferDevice
.paint();
919 // modified color group. Force output to unified color.
920 void VclProcessor2D::RenderModifiedColorPrimitive2D(const primitive2d::ModifiedColorPrimitive2D
& rModifiedCandidate
)
922 if(rModifiedCandidate
.getChildren().hasElements())
924 maBColorModifierStack
.push(rModifiedCandidate
.getColorModifier());
925 process(rModifiedCandidate
.getChildren());
926 maBColorModifierStack
.pop();
930 // unified sub-transparence. Draw to VDev first.
931 void VclProcessor2D::RenderUnifiedTransparencePrimitive2D(const primitive2d::UnifiedTransparencePrimitive2D
& rTransCandidate
)
933 static bool bForceToDecomposition(false);
935 if(rTransCandidate
.getChildren().hasElements())
937 if(bForceToDecomposition
)
940 process(rTransCandidate
.get2DDecomposition(getViewInformation2D()));
944 if(0.0 == rTransCandidate
.getTransparence())
946 // no transparence used, so just use the content
947 process(rTransCandidate
.getChildren());
949 else if(rTransCandidate
.getTransparence() > 0.0 && rTransCandidate
.getTransparence() < 1.0)
951 // transparence is in visible range
952 basegfx::B2DRange
aRange(primitive2d::getB2DRangeFromPrimitive2DSequence(rTransCandidate
.getChildren(), getViewInformation2D()));
953 aRange
.transform(maCurrentTransformation
);
954 impBufferDevice
aBufferDevice(*mpOutputDevice
, aRange
, true);
956 if(aBufferDevice
.isVisible())
958 // remember last OutDev and set to content
959 OutputDevice
* pLastOutputDevice
= mpOutputDevice
;
960 mpOutputDevice
= &aBufferDevice
.getContent();
962 // paint content to it
963 process(rTransCandidate
.getChildren());
965 // back to old OutDev
966 mpOutputDevice
= pLastOutputDevice
;
968 // dump buffer to outdev using given transparence
969 aBufferDevice
.paint(rTransCandidate
.getTransparence());
976 // sub-transparence group. Draw to VDev first.
977 void VclProcessor2D::RenderTransparencePrimitive2D(const primitive2d::TransparencePrimitive2D
& rTransCandidate
)
979 if(rTransCandidate
.getChildren().hasElements())
981 basegfx::B2DRange
aRange(primitive2d::getB2DRangeFromPrimitive2DSequence(rTransCandidate
.getChildren(), getViewInformation2D()));
982 aRange
.transform(maCurrentTransformation
);
983 impBufferDevice
aBufferDevice(*mpOutputDevice
, aRange
, true);
985 if(aBufferDevice
.isVisible())
987 // remember last OutDev and set to content
988 OutputDevice
* pLastOutputDevice
= mpOutputDevice
;
989 mpOutputDevice
= &aBufferDevice
.getContent();
991 // paint content to it
992 process(rTransCandidate
.getChildren());
995 mpOutputDevice
= &aBufferDevice
.getTransparence();
997 // when painting transparence masks, reset the color stack
998 basegfx::BColorModifierStack
aLastBColorModifierStack(maBColorModifierStack
);
999 maBColorModifierStack
= basegfx::BColorModifierStack();
1001 // paint mask to it (always with transparence intensities, evtl. with AA)
1002 process(rTransCandidate
.getTransparence());
1004 // back to old color stack
1005 maBColorModifierStack
= aLastBColorModifierStack
;
1007 // back to old OutDev
1008 mpOutputDevice
= pLastOutputDevice
;
1010 // dump buffer to outdev
1011 aBufferDevice
.paint();
1017 void VclProcessor2D::RenderTransformPrimitive2D(const primitive2d::TransformPrimitive2D
& rTransformCandidate
)
1019 // remember current transformation and ViewInformation
1020 const basegfx::B2DHomMatrix
aLastCurrentTransformation(maCurrentTransformation
);
1021 const geometry::ViewInformation2D
aLastViewInformation2D(getViewInformation2D());
1023 // create new transformations for CurrentTransformation
1024 // and for local ViewInformation2D
1025 maCurrentTransformation
= maCurrentTransformation
* rTransformCandidate
.getTransformation();
1026 const geometry::ViewInformation2D
aViewInformation2D(
1027 getViewInformation2D().getObjectTransformation() * rTransformCandidate
.getTransformation(),
1028 getViewInformation2D().getViewTransformation(),
1029 getViewInformation2D().getViewport(),
1030 getViewInformation2D().getVisualizedPage(),
1031 getViewInformation2D().getViewTime(),
1032 getViewInformation2D().getExtendedInformationSequence());
1033 updateViewInformation(aViewInformation2D
);
1036 process(rTransformCandidate
.getChildren());
1038 // restore transformations
1039 maCurrentTransformation
= aLastCurrentTransformation
;
1040 updateViewInformation(aLastViewInformation2D
);
1043 // new XDrawPage for ViewInformation2D
1044 void VclProcessor2D::RenderPagePreviewPrimitive2D(const primitive2d::PagePreviewPrimitive2D
& rPagePreviewCandidate
)
1046 // remember current transformation and ViewInformation
1047 const geometry::ViewInformation2D
aLastViewInformation2D(getViewInformation2D());
1049 // create new local ViewInformation2D
1050 const geometry::ViewInformation2D
aViewInformation2D(
1051 getViewInformation2D().getObjectTransformation(),
1052 getViewInformation2D().getViewTransformation(),
1053 getViewInformation2D().getViewport(),
1054 rPagePreviewCandidate
.getXDrawPage(),
1055 getViewInformation2D().getViewTime(),
1056 getViewInformation2D().getExtendedInformationSequence());
1057 updateViewInformation(aViewInformation2D
);
1059 // process decomposed content
1060 process(rPagePreviewCandidate
.get2DDecomposition(getViewInformation2D()));
1062 // restore transformations
1063 updateViewInformation(aLastViewInformation2D
);
1067 void VclProcessor2D::RenderMarkerArrayPrimitive2D(const primitive2d::MarkerArrayPrimitive2D
& rMarkArrayCandidate
)
1069 static bool bCheckCompleteMarkerDecompose(false);
1070 if(bCheckCompleteMarkerDecompose
)
1072 process(rMarkArrayCandidate
.get2DDecomposition(getViewInformation2D()));
1077 const std::vector
< basegfx::B2DPoint
>& rPositions
= rMarkArrayCandidate
.getPositions();
1078 const sal_uInt32
nCount(rPositions
.size());
1080 if(nCount
&& !rMarkArrayCandidate
.getMarker().IsEmpty())
1083 const BitmapEx
& rMarker(rMarkArrayCandidate
.getMarker());
1084 const Size
aBitmapSize(rMarker
.GetSizePixel());
1086 if(aBitmapSize
.Width() && aBitmapSize
.Height())
1088 // get discrete half size
1089 const basegfx::B2DVector
aDiscreteHalfSize(
1090 (aBitmapSize
.getWidth() - 1.0) * 0.5,
1091 (aBitmapSize
.getHeight() - 1.0) * 0.5);
1092 const bool bWasEnabled(mpOutputDevice
->IsMapModeEnabled());
1094 // do not forget evtl. moved origin in target device MapMode when
1095 // switching it off; it would be missing and lead to wrong positions.
1096 // All his could be done using logic sizes and coordinates, too, but
1097 // we want a 1:1 bitmap rendering here, so it's more safe and faster
1098 // to work with switching off MapMode usage completely.
1099 const Point
aOrigin(mpOutputDevice
->GetMapMode().GetOrigin());
1101 mpOutputDevice
->EnableMapMode(false);
1103 for(std::vector
< basegfx::B2DPoint
>::const_iterator
aIter(rPositions
.begin()); aIter
!= rPositions
.end(); ++aIter
)
1105 const basegfx::B2DPoint
aDiscreteTopLeft((maCurrentTransformation
* (*aIter
)) - aDiscreteHalfSize
);
1106 const Point
aDiscretePoint(basegfx::fround(aDiscreteTopLeft
.getX()), basegfx::fround(aDiscreteTopLeft
.getY()));
1108 mpOutputDevice
->DrawBitmapEx(aDiscretePoint
+ aOrigin
, rMarker
);
1111 mpOutputDevice
->EnableMapMode(bWasEnabled
);
1117 void VclProcessor2D::RenderPointArrayPrimitive2D(const primitive2d::PointArrayPrimitive2D
& rPointArrayCandidate
)
1119 const std::vector
< basegfx::B2DPoint
>& rPositions
= rPointArrayCandidate
.getPositions();
1120 const basegfx::BColor
aRGBColor(maBColorModifierStack
.getModifiedColor(rPointArrayCandidate
.getRGBColor()));
1121 const Color
aVCLColor(aRGBColor
);
1123 for(std::vector
< basegfx::B2DPoint
>::const_iterator
aIter(rPositions
.begin()); aIter
!= rPositions
.end(); ++aIter
)
1125 const basegfx::B2DPoint
aViewPosition(maCurrentTransformation
* (*aIter
));
1126 const Point
aPos(basegfx::fround(aViewPosition
.getX()), basegfx::fround(aViewPosition
.getY()));
1128 mpOutputDevice
->DrawPixel(aPos
, aVCLColor
);
1132 void VclProcessor2D::RenderPolygonStrokePrimitive2D(const primitive2d::PolygonStrokePrimitive2D
& rPolygonStrokeCandidate
)
1134 // #i101491# method restructured to clearly use the DrawPolyLine
1135 // calls starting from a deined line width
1136 const attribute::LineAttribute
& rLineAttribute
= rPolygonStrokeCandidate
.getLineAttribute();
1137 const double fLineWidth(rLineAttribute
.getWidth());
1140 if(basegfx::fTools::more(fLineWidth
, 0.0))
1142 const basegfx::B2DVector
aDiscreteUnit(maCurrentTransformation
* basegfx::B2DVector(fLineWidth
, 0.0));
1143 const double fDiscreteLineWidth(aDiscreteUnit
.getLength());
1144 const attribute::StrokeAttribute
& rStrokeAttribute
= rPolygonStrokeCandidate
.getStrokeAttribute();
1145 const basegfx::BColor
aHairlineColor(maBColorModifierStack
.getModifiedColor(rLineAttribute
.getColor()));
1146 basegfx::B2DPolyPolygon aHairlinePolyPolygon
;
1148 mpOutputDevice
->SetLineColor(Color(aHairlineColor
));
1149 mpOutputDevice
->SetFillColor();
1151 if(0.0 == rStrokeAttribute
.getFullDotDashLen())
1153 // no line dashing, just copy
1154 aHairlinePolyPolygon
.append(rPolygonStrokeCandidate
.getB2DPolygon());
1158 // else apply LineStyle
1159 basegfx::tools::applyLineDashing(rPolygonStrokeCandidate
.getB2DPolygon(),
1160 rStrokeAttribute
.getDotDashArray(),
1161 &aHairlinePolyPolygon
, 0, rStrokeAttribute
.getFullDotDashLen());
1164 const sal_uInt32
nCount(aHairlinePolyPolygon
.count());
1168 const bool bAntiAliased(getOptionsDrawinglayer().IsAntiAliasing());
1169 aHairlinePolyPolygon
.transform(maCurrentTransformation
);
1173 if(basegfx::fTools::lessOrEqual(fDiscreteLineWidth
, 1.0))
1175 // line in range ]0.0 .. 1.0[
1176 // paint as simple hairline
1177 for(sal_uInt32
a(0); a
< nCount
; a
++)
1179 mpOutputDevice
->DrawPolyLine(aHairlinePolyPolygon
.getB2DPolygon(a
), 0.0);
1184 else if(basegfx::fTools::lessOrEqual(fDiscreteLineWidth
, 2.0))
1186 // line in range [1.0 .. 2.0[
1187 // paint as 2x2 with dynamic line distance
1188 basegfx::B2DHomMatrix aMat
;
1189 const double fDistance(fDiscreteLineWidth
- 1.0);
1190 const double fHalfDistance(fDistance
* 0.5);
1192 for(sal_uInt32
a(0); a
< nCount
; a
++)
1194 basegfx::B2DPolygon
aCandidate(aHairlinePolyPolygon
.getB2DPolygon(a
));
1196 aMat
.set(0, 2, -fHalfDistance
);
1197 aMat
.set(1, 2, -fHalfDistance
);
1198 aCandidate
.transform(aMat
);
1199 mpOutputDevice
->DrawPolyLine(aCandidate
, 0.0);
1201 aMat
.set(0, 2, fDistance
);
1202 aMat
.set(1, 2, 0.0);
1203 aCandidate
.transform(aMat
);
1204 mpOutputDevice
->DrawPolyLine(aCandidate
, 0.0);
1206 aMat
.set(0, 2, 0.0);
1207 aMat
.set(1, 2, fDistance
);
1208 aCandidate
.transform(aMat
);
1209 mpOutputDevice
->DrawPolyLine(aCandidate
, 0.0);
1211 aMat
.set(0, 2, -fDistance
);
1212 aMat
.set(1, 2, 0.0);
1213 aCandidate
.transform(aMat
);
1214 mpOutputDevice
->DrawPolyLine(aCandidate
, 0.0);
1219 else if(basegfx::fTools::lessOrEqual(fDiscreteLineWidth
, 3.0))
1221 // line in range [2.0 .. 3.0]
1222 // paint as cross in a 3x3 with dynamic line distance
1223 basegfx::B2DHomMatrix aMat
;
1224 const double fDistance((fDiscreteLineWidth
- 1.0) * 0.5);
1226 for(sal_uInt32
a(0); a
< nCount
; a
++)
1228 basegfx::B2DPolygon
aCandidate(aHairlinePolyPolygon
.getB2DPolygon(a
));
1230 mpOutputDevice
->DrawPolyLine(aCandidate
, 0.0);
1232 aMat
.set(0, 2, -fDistance
);
1233 aMat
.set(1, 2, 0.0);
1234 aCandidate
.transform(aMat
);
1235 mpOutputDevice
->DrawPolyLine(aCandidate
, 0.0);
1237 aMat
.set(0, 2, fDistance
);
1238 aMat
.set(1, 2, -fDistance
);
1239 aCandidate
.transform(aMat
);
1240 mpOutputDevice
->DrawPolyLine(aCandidate
, 0.0);
1242 aMat
.set(0, 2, fDistance
);
1243 aMat
.set(1, 2, fDistance
);
1244 aCandidate
.transform(aMat
);
1245 mpOutputDevice
->DrawPolyLine(aCandidate
, 0.0);
1247 aMat
.set(0, 2, -fDistance
);
1248 aMat
.set(1, 2, fDistance
);
1249 aCandidate
.transform(aMat
);
1250 mpOutputDevice
->DrawPolyLine(aCandidate
, 0.0);
1257 // #i101491# line width above 3.0
1262 if(basegfx::fTools::lessOrEqual(fDiscreteLineWidth
, 1.5))
1264 // line width below 1.5, draw the basic hairline polygon
1265 for(sal_uInt32
a(0); a
< nCount
; a
++)
1267 mpOutputDevice
->DrawPolyLine(aHairlinePolyPolygon
.getB2DPolygon(a
), 0.0);
1272 else if(basegfx::fTools::lessOrEqual(fDiscreteLineWidth
, 2.5))
1274 // line width is in range ]1.5 .. 2.5], use four hairlines
1275 // drawn in a square
1276 for(sal_uInt32
a(0); a
< nCount
; a
++)
1278 basegfx::B2DPolygon
aCandidate(aHairlinePolyPolygon
.getB2DPolygon(a
));
1279 basegfx::B2DHomMatrix aMat
;
1281 mpOutputDevice
->DrawPolyLine(aCandidate
, 0.0);
1283 aMat
.set(0, 2, 1.0);
1284 aMat
.set(1, 2, 0.0);
1285 aCandidate
.transform(aMat
);
1287 mpOutputDevice
->DrawPolyLine(aCandidate
, 0.0);
1289 aMat
.set(0, 2, 0.0);
1290 aMat
.set(1, 2, 1.0);
1291 aCandidate
.transform(aMat
);
1293 mpOutputDevice
->DrawPolyLine(aCandidate
, 0.0);
1295 aMat
.set(0, 2, -1.0);
1296 aMat
.set(1, 2, 0.0);
1297 aCandidate
.transform(aMat
);
1299 mpOutputDevice
->DrawPolyLine(aCandidate
, 0.0);
1306 // #i101491# line width is above 2.5
1310 if(!bDone
&& rPolygonStrokeCandidate
.getB2DPolygon().count() > 1000)
1312 // #i101491# If the polygon complexity uses more than a given amount, do
1313 // use OuputDevice::DrawPolyLine directly; this will avoid buffering all
1314 // decompositions in primitives (memory) and fallback to old line painting
1315 // for very complex polygons, too
1316 for(sal_uInt32
a(0); a
< nCount
; a
++)
1318 mpOutputDevice
->DrawPolyLine(
1319 aHairlinePolyPolygon
.getB2DPolygon(a
),
1321 rLineAttribute
.getLineJoin(),
1322 rLineAttribute
.getLineCap());
1332 // remember that we enter a PolygonStrokePrimitive2D decomposition,
1333 // used for AA thick line drawing
1334 mnPolygonStrokePrimitive2D
++;
1336 // line width is big enough for standard filled polygon visualisation or zero
1337 process(rPolygonStrokeCandidate
.get2DDecomposition(getViewInformation2D()));
1339 // leave PolygonStrokePrimitive2D
1340 mnPolygonStrokePrimitive2D
--;
1344 void VclProcessor2D::RenderEpsPrimitive2D(const primitive2d::EpsPrimitive2D
& rEpsPrimitive2D
)
1346 // The new decomposition of Metafiles made it necessary to add an Eps
1347 // primitive to handle embedded Eps data. On some devices, this can be
1348 // painted directly (mac, printer).
1349 // To be able to handle the replacement correctly, i need to handle it myself
1350 // since DrawEPS will not be able e.g. to rotate the replacement. To be able
1351 // to do that, i added a boolean return to OutputDevice::DrawEPS(..)
1352 // to know when EPS was handled directly already.
1353 basegfx::B2DRange
aRange(0.0, 0.0, 1.0, 1.0);
1354 aRange
.transform(maCurrentTransformation
* rEpsPrimitive2D
.getEpsTransform());
1356 if(!aRange
.isEmpty())
1358 const Rectangle
aRectangle(
1359 (sal_Int32
)floor(aRange
.getMinX()), (sal_Int32
)floor(aRange
.getMinY()),
1360 (sal_Int32
)ceil(aRange
.getMaxX()), (sal_Int32
)ceil(aRange
.getMaxY()));
1362 if(!aRectangle
.IsEmpty())
1364 bool bWillReallyRender
= mpOutputDevice
->IsDeviceOutputNecessary();
1365 // try to paint EPS directly without fallback visualisation
1366 const bool bEPSPaintedDirectly
= bWillReallyRender
&&
1367 mpOutputDevice
->DrawEPS(
1368 aRectangle
.TopLeft(),
1369 aRectangle
.GetSize(),
1370 rEpsPrimitive2D
.getGfxLink(),
1373 if(!bEPSPaintedDirectly
)
1375 // use the decomposition which will correctly handle the
1376 // fallback visualisation using full transformation (e.g. rotation)
1377 process(rEpsPrimitive2D
.get2DDecomposition(getViewInformation2D()));
1383 void VclProcessor2D::RenderSvgLinearAtomPrimitive2D(const primitive2d::SvgLinearAtomPrimitive2D
& rCandidate
)
1385 const double fDelta(rCandidate
.getOffsetB() - rCandidate
.getOffsetA());
1387 if(basegfx::fTools::more(fDelta
, 0.0))
1389 const basegfx::BColor
aColorA(maBColorModifierStack
.getModifiedColor(rCandidate
.getColorA()));
1390 const basegfx::BColor
aColorB(maBColorModifierStack
.getModifiedColor(rCandidate
.getColorB()));
1392 // calculate discrete unit in WorldCoordinates; use diagonal (1.0, 1.0) and divide by sqrt(2)
1393 const basegfx::B2DVector
aDiscreteVector(getViewInformation2D().getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 1.0));
1394 const double fDiscreteUnit(aDiscreteVector
.getLength() * (1.0 / 1.414213562373));
1396 // use color distance and discrete lengths to calculate step count
1397 const sal_uInt32
nSteps(calculateStepsForSvgGradient(aColorA
, aColorB
, fDelta
, fDiscreteUnit
));
1399 // switch off line painting
1400 mpOutputDevice
->SetLineColor();
1402 // prepare polygon in needed width at start position (with discrete overlap)
1403 const basegfx::B2DPolygon
aPolygon(
1404 basegfx::tools::createPolygonFromRect(
1406 rCandidate
.getOffsetA() - fDiscreteUnit
,
1408 rCandidate
.getOffsetA() + (fDelta
/ nSteps
) + fDiscreteUnit
,
1412 // prepare loop ([0.0 .. 1.0[)
1413 double fUnitScale(0.0);
1414 const double fUnitStep(1.0 / nSteps
);
1417 for(sal_uInt32
a(0); a
< nSteps
; a
++, fUnitScale
+= fUnitStep
)
1419 basegfx::B2DPolygon
aNew(aPolygon
);
1421 aNew
.transform(maCurrentTransformation
* basegfx::tools::createTranslateB2DHomMatrix(fDelta
* fUnitScale
, 0.0));
1422 mpOutputDevice
->SetFillColor(Color(basegfx::interpolate(aColorA
, aColorB
, fUnitScale
)));
1423 mpOutputDevice
->DrawPolyPolygon(basegfx::B2DPolyPolygon(aNew
));
1428 void VclProcessor2D::RenderSvgRadialAtomPrimitive2D(const primitive2d::SvgRadialAtomPrimitive2D
& rCandidate
)
1430 const double fDeltaScale(rCandidate
.getScaleB() - rCandidate
.getScaleA());
1432 if(basegfx::fTools::more(fDeltaScale
, 0.0))
1434 const basegfx::BColor
aColorA(maBColorModifierStack
.getModifiedColor(rCandidate
.getColorA()));
1435 const basegfx::BColor
aColorB(maBColorModifierStack
.getModifiedColor(rCandidate
.getColorB()));
1437 // calculate discrete unit in WorldCoordinates; use diagonal (1.0, 1.0) and divide by sqrt(2)
1438 const basegfx::B2DVector
aDiscreteVector(getViewInformation2D().getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 1.0));
1439 const double fDiscreteUnit(aDiscreteVector
.getLength() * (1.0 / 1.414213562373));
1441 // use color distance and discrete lengths to calculate step count
1442 const sal_uInt32
nSteps(calculateStepsForSvgGradient(aColorA
, aColorB
, fDeltaScale
, fDiscreteUnit
));
1444 // switch off line painting
1445 mpOutputDevice
->SetLineColor();
1447 // prepare loop ([0.0 .. 1.0[, full polygons, no polypolygons with holes)
1448 double fUnitScale(0.0);
1449 const double fUnitStep(1.0 / nSteps
);
1451 for(sal_uInt32
a(0); a
< nSteps
; a
++, fUnitScale
+= fUnitStep
)
1453 basegfx::B2DHomMatrix aTransform
;
1454 const double fEndScale(rCandidate
.getScaleB() - (fDeltaScale
* fUnitScale
));
1456 if(rCandidate
.isTranslateSet())
1458 const basegfx::B2DVector
aTranslate(
1459 basegfx::interpolate(
1460 rCandidate
.getTranslateB(),
1461 rCandidate
.getTranslateA(),
1464 aTransform
= basegfx::tools::createScaleTranslateB2DHomMatrix(
1472 aTransform
= basegfx::tools::createScaleB2DHomMatrix(
1477 basegfx::B2DPolygon
aNew(basegfx::tools::createPolygonFromUnitCircle());
1479 aNew
.transform(maCurrentTransformation
* aTransform
);
1480 mpOutputDevice
->SetFillColor(Color(basegfx::interpolate(aColorB
, aColorA
, fUnitScale
)));
1481 mpOutputDevice
->DrawPolyPolygon(basegfx::B2DPolyPolygon(aNew
));
1486 void VclProcessor2D::adaptLineToFillDrawMode() const
1488 const DrawModeFlags
nOriginalDrawMode(mpOutputDevice
->GetDrawMode());
1490 if(nOriginalDrawMode
& (DrawModeFlags::BlackLine
|DrawModeFlags::GrayLine
|DrawModeFlags::GhostedLine
|DrawModeFlags::WhiteLine
|DrawModeFlags::SettingsLine
))
1492 DrawModeFlags
nAdaptedDrawMode(nOriginalDrawMode
);
1494 if(nOriginalDrawMode
& DrawModeFlags::BlackLine
)
1496 nAdaptedDrawMode
|= DrawModeFlags::BlackFill
;
1500 nAdaptedDrawMode
&= ~DrawModeFlags::BlackFill
;
1503 if(nOriginalDrawMode
& DrawModeFlags::GrayLine
)
1505 nAdaptedDrawMode
|= DrawModeFlags::GrayFill
;
1509 nAdaptedDrawMode
&= ~DrawModeFlags::GrayFill
;
1512 if(nOriginalDrawMode
& DrawModeFlags::GhostedLine
)
1514 nAdaptedDrawMode
|= DrawModeFlags::GhostedFill
;
1518 nAdaptedDrawMode
&= ~DrawModeFlags::GhostedFill
;
1521 if(nOriginalDrawMode
& DrawModeFlags::WhiteLine
)
1523 nAdaptedDrawMode
|= DrawModeFlags::WhiteFill
;
1527 nAdaptedDrawMode
&= ~DrawModeFlags::WhiteFill
;
1530 if(nOriginalDrawMode
& DrawModeFlags::SettingsLine
)
1532 nAdaptedDrawMode
|= DrawModeFlags::SettingsFill
;
1536 nAdaptedDrawMode
&= ~DrawModeFlags::SettingsFill
;
1539 mpOutputDevice
->SetDrawMode(nAdaptedDrawMode
);
1543 void VclProcessor2D::adaptTextToFillDrawMode() const
1545 const DrawModeFlags
nOriginalDrawMode(mpOutputDevice
->GetDrawMode());
1546 if(nOriginalDrawMode
& (DrawModeFlags::BlackText
|DrawModeFlags::GrayText
|DrawModeFlags::GhostedText
|DrawModeFlags::WhiteText
|DrawModeFlags::SettingsText
))
1548 DrawModeFlags
nAdaptedDrawMode(nOriginalDrawMode
);
1550 if(nOriginalDrawMode
& DrawModeFlags::BlackText
)
1552 nAdaptedDrawMode
|= DrawModeFlags::BlackFill
;
1556 nAdaptedDrawMode
&= ~DrawModeFlags::BlackFill
;
1559 if(nOriginalDrawMode
& DrawModeFlags::GrayText
)
1561 nAdaptedDrawMode
|= DrawModeFlags::GrayFill
;
1565 nAdaptedDrawMode
&= ~DrawModeFlags::GrayFill
;
1568 if(nOriginalDrawMode
& DrawModeFlags::GhostedText
)
1570 nAdaptedDrawMode
|= DrawModeFlags::GhostedFill
;
1574 nAdaptedDrawMode
&= ~DrawModeFlags::GhostedFill
;
1577 if(nOriginalDrawMode
& DrawModeFlags::WhiteText
)
1579 nAdaptedDrawMode
|= DrawModeFlags::WhiteFill
;
1583 nAdaptedDrawMode
&= ~DrawModeFlags::WhiteFill
;
1586 if(nOriginalDrawMode
& DrawModeFlags::SettingsText
)
1588 nAdaptedDrawMode
|= DrawModeFlags::SettingsFill
;
1592 nAdaptedDrawMode
&= ~DrawModeFlags::SettingsFill
;
1595 mpOutputDevice
->SetDrawMode(nAdaptedDrawMode
);
1599 void VclProcessor2D::RenderOpenGLPrimitive2D(const primitive2d::OpenGLPrimitive2D
& rCandidate
)
1601 // Just draw a dummy rect to see primitive rendering is working.
1602 mpOutputDevice
->SetLineColor(COL_BLACK
);
1603 mpOutputDevice
->SetFillColor(COL_RED
);
1604 mpOutputDevice
->DrawRect(Rectangle(rCandidate
.getPos(),Size(2000,2000)));
1609 VclProcessor2D::VclProcessor2D(
1610 const geometry::ViewInformation2D
& rViewInformation
,
1611 OutputDevice
& rOutDev
)
1612 : BaseProcessor2D(rViewInformation
),
1613 mpOutputDevice(&rOutDev
),
1614 maBColorModifierStack(),
1615 maCurrentTransformation(),
1616 maDrawinglayerOpt(),
1617 mnPolygonStrokePrimitive2D(0)
1619 // set digit language, derived from SvtCTLOptions to have the correct
1620 // number display for arabic/hindi numerals
1621 rOutDev
.SetDigitLanguage(drawinglayer::detail::getDigitLanguage());
1624 VclProcessor2D::~VclProcessor2D()
1630 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */