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 .
21 #include <comphelper/string.hxx>
22 #include "vclprocessor2d.hxx"
23 #include <drawinglayer/primitive2d/textprimitive2d.hxx>
24 #include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx>
25 #include <tools/debug.hxx>
26 #include <vcl/outdev.hxx>
27 #include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
28 #include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
29 #include <basegfx/polygon/b2dpolygontools.hxx>
30 #include <drawinglayer/attribute/sdrfillgraphicattribute.hxx>
31 #include <drawinglayer/primitive2d/fillgraphicprimitive2d.hxx>
32 #include <drawinglayer/primitive2d/polypolygonprimitive2d.hxx>
33 #include <drawinglayer/primitive2d/metafileprimitive2d.hxx>
34 #include <drawinglayer/primitive2d/maskprimitive2d.hxx>
35 #include <basegfx/polygon/b2dpolypolygontools.hxx>
36 #include <vclhelperbufferdevice.hxx>
37 #include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx>
38 #include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
39 #include <drawinglayer/primitive2d/transparenceprimitive2d.hxx>
40 #include <drawinglayer/primitive2d/transformprimitive2d.hxx>
41 #include <drawinglayer/primitive2d/markerarrayprimitive2d.hxx>
42 #include <drawinglayer/primitive2d/pointarrayprimitive2d.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 <drawinglayer/primitive2d/controlprimitive2d.hxx>
59 #include <drawinglayer/primitive2d/textlayoutdevice.hxx>
61 #include <basegfx/polygon/b2dpolygonclipper.hxx>
62 #include <basegfx/polygon/b2dtrapezoid.hxx>
64 using namespace com::sun::star
;
68 sal_uInt32
calculateStepsForSvgGradient(const basegfx::BColor
& rColorA
, const basegfx::BColor
& rColorB
, double fDelta
, double fDiscreteUnit
)
70 // use color distance, assume to do every color step
71 sal_uInt32
nSteps(basegfx::fround(rColorA
.getDistance(rColorB
) * 255.0));
75 // calc discrete length to change color each disctete unit (pixel)
76 const sal_uInt32
nDistSteps(basegfx::fround(fDelta
/ fDiscreteUnit
));
78 nSteps
= std::min(nSteps
, nDistSteps
);
81 // reduce quality to 3 discrete units or every 3rd color step for rendering
84 // roughly cut when too big or too small (not full quality, reduce complexity)
85 nSteps
= std::min(nSteps
, sal_uInt32(255));
86 nSteps
= std::max(nSteps
, sal_uInt32(1));
92 namespace drawinglayer
98 // directdraw of text simple portion or decorated portion primitive. When decorated, all the extra
99 // information is translated to VCL parameters and set at the font.
100 // Acceptance is restricted to no shearing and positive scaling in X and Y (no font mirroring
102 void VclProcessor2D::RenderTextSimpleOrDecoratedPortionPrimitive2D(const primitive2d::TextSimplePortionPrimitive2D
& rTextCandidate
)
104 // decompose matrix to have position and size of text
105 basegfx::B2DHomMatrix
aLocalTransform(maCurrentTransformation
* rTextCandidate
.getTextTransform());
106 basegfx::B2DVector aFontScaling
, aTranslate
;
107 double fRotate
, fShearX
;
108 aLocalTransform
.decompose(aFontScaling
, aTranslate
, fRotate
, fShearX
);
109 bool bPrimitiveAccepted(false);
111 // tdf#95581: Assume tiny shears are rounding artefacts or whatever and can be ignored,
112 // especially if the effect is less than a pixel.
113 if(std::abs(aFontScaling
.getY() * fShearX
) < 1)
115 if(basegfx::fTools::less(aFontScaling
.getX(), 0.0) && basegfx::fTools::less(aFontScaling
.getY(), 0.0))
117 // handle special case: If scale is negative in (x,y) (3rd quadrant), it can
118 // be expressed as rotation by PI. Use this since the Font rendering will not
119 // apply the negative scales in any form
120 aFontScaling
= basegfx::absolute(aFontScaling
);
124 if(basegfx::fTools::more(aFontScaling
.getX(), 0.0) && basegfx::fTools::more(aFontScaling
.getY(), 0.0))
126 // Get the VCL font (use FontHeight as FontWidth)
127 vcl::Font
aFont(primitive2d::getVclFontFromFontAttribute(
128 rTextCandidate
.getFontAttribute(),
132 rTextCandidate
.getLocale()));
134 // set FillColor Attribute
135 const Color
aFillColor( rTextCandidate
.getTextFillColor() );
136 if( aFillColor
!= COL_TRANSPARENT
)
138 aFont
.SetFillColor(aFillColor
);
139 aFont
.SetTransparent(false);
142 // Don't draw fonts without height
143 if( aFont
.GetFontHeight() <= 0 )
146 // handle additional font attributes
147 const primitive2d::TextDecoratedPortionPrimitive2D
* pTCPP
=
148 dynamic_cast<const primitive2d::TextDecoratedPortionPrimitive2D
*>( &rTextCandidate
);
150 if( pTCPP
!= nullptr )
153 // set the color of text decorations
154 const basegfx::BColor aTextlineColor
= maBColorModifierStack
.getModifiedColor(pTCPP
->getTextlineColor());
155 mpOutputDevice
->SetTextLineColor( Color(aTextlineColor
) );
157 // set Overline attribute
158 const FontLineStyle
eFontOverline(primitive2d::mapTextLineToFontLineStyle( pTCPP
->getFontOverline() ));
159 if( eFontOverline
!= LINESTYLE_NONE
)
161 aFont
.SetOverline( eFontOverline
);
162 const basegfx::BColor aOverlineColor
= maBColorModifierStack
.getModifiedColor(pTCPP
->getOverlineColor());
163 mpOutputDevice
->SetOverlineColor( Color(aOverlineColor
) );
164 if( pTCPP
->getWordLineMode() )
165 aFont
.SetWordLineMode( true );
168 // set Underline attribute
169 const FontLineStyle
eFontLineStyle(primitive2d::mapTextLineToFontLineStyle( pTCPP
->getFontUnderline() ));
170 if( eFontLineStyle
!= LINESTYLE_NONE
)
172 aFont
.SetUnderline( eFontLineStyle
);
173 if( pTCPP
->getWordLineMode() )
174 aFont
.SetWordLineMode( true );
177 // set Strikeout attribute
178 const FontStrikeout
eFontStrikeout(primitive2d::mapTextStrikeoutToFontStrikeout(pTCPP
->getTextStrikeout()));
180 if( eFontStrikeout
!= STRIKEOUT_NONE
)
181 aFont
.SetStrikeout( eFontStrikeout
);
184 // set EmphasisMark attribute
185 FontEmphasisMark eFontEmphasisMark
= FontEmphasisMark::NONE
;
186 switch( pTCPP
->getTextEmphasisMark() )
189 SAL_WARN("drawinglayer", "Unknown EmphasisMark style " << pTCPP
->getTextEmphasisMark() );
191 case primitive2d::TEXT_FONT_EMPHASIS_MARK_NONE
: eFontEmphasisMark
= FontEmphasisMark::NONE
; break;
192 case primitive2d::TEXT_FONT_EMPHASIS_MARK_DOT
: eFontEmphasisMark
= FontEmphasisMark::Dot
; break;
193 case primitive2d::TEXT_FONT_EMPHASIS_MARK_CIRCLE
: eFontEmphasisMark
= FontEmphasisMark::Circle
; break;
194 case primitive2d::TEXT_FONT_EMPHASIS_MARK_DISC
: eFontEmphasisMark
= FontEmphasisMark::Disc
; break;
195 case primitive2d::TEXT_FONT_EMPHASIS_MARK_ACCENT
: eFontEmphasisMark
= FontEmphasisMark::Accent
; break;
198 if( eFontEmphasisMark
!= FontEmphasisMark::NONE
)
200 DBG_ASSERT( (pTCPP
->getEmphasisMarkAbove() != pTCPP
->getEmphasisMarkBelow()),
201 "DrawingLayer: Bad EmphasisMark position!" );
202 if( pTCPP
->getEmphasisMarkAbove() )
203 eFontEmphasisMark
|= FontEmphasisMark::PosAbove
;
205 eFontEmphasisMark
|= FontEmphasisMark::PosBelow
;
206 aFont
.SetEmphasisMark( eFontEmphasisMark
);
209 // set Relief attribute
210 FontRelief eFontRelief
= RELIEF_NONE
;
211 switch( pTCPP
->getTextRelief() )
214 SAL_WARN( "drawinglayer", "Unknown Relief style " << pTCPP
->getTextRelief() );
216 case primitive2d::TEXT_RELIEF_NONE
: eFontRelief
= RELIEF_NONE
; break;
217 case primitive2d::TEXT_RELIEF_EMBOSSED
: eFontRelief
= RELIEF_EMBOSSED
; break;
218 case primitive2d::TEXT_RELIEF_ENGRAVED
: eFontRelief
= RELIEF_ENGRAVED
; break;
221 if( eFontRelief
!= RELIEF_NONE
)
222 aFont
.SetRelief( eFontRelief
);
224 // set Shadow attribute
225 if( pTCPP
->getShadow() )
226 aFont
.SetShadow( true );
229 // create transformed integer DXArray in view coordinate system
230 ::std::vector
< long > aTransformedDXArray
;
232 if(rTextCandidate
.getDXArray().size())
234 aTransformedDXArray
.reserve(rTextCandidate
.getDXArray().size());
235 const basegfx::B2DVector
aPixelVector(maCurrentTransformation
* basegfx::B2DVector(1.0, 0.0));
236 const double fPixelVectorFactor(aPixelVector
.getLength());
238 for(::std::vector
< double >::const_iterator
aStart(rTextCandidate
.getDXArray().begin());
239 aStart
!= rTextCandidate
.getDXArray().end(); ++aStart
)
241 aTransformedDXArray
.push_back(basegfx::fround((*aStart
) * fPixelVectorFactor
));
245 // set parameters and paint text snippet
246 const basegfx::BColor
aRGBFontColor(maBColorModifierStack
.getModifiedColor(rTextCandidate
.getFontColor()));
247 const basegfx::B2DPoint
aPoint(aLocalTransform
* basegfx::B2DPoint(0.0, 0.0));
248 const Point
aStartPoint(basegfx::fround(aPoint
.getX()), basegfx::fround(aPoint
.getY()));
249 const ComplexTextLayoutMode
nOldLayoutMode(mpOutputDevice
->GetLayoutMode());
251 if(rTextCandidate
.getFontAttribute().getRTL())
253 ComplexTextLayoutMode
nRTLLayoutMode(nOldLayoutMode
& ~(TEXT_LAYOUT_COMPLEX_DISABLED
|TEXT_LAYOUT_BIDI_STRONG
));
254 nRTLLayoutMode
|= TEXT_LAYOUT_BIDI_RTL
|TEXT_LAYOUT_TEXTORIGIN_LEFT
;
255 mpOutputDevice
->SetLayoutMode(nRTLLayoutMode
);
258 mpOutputDevice
->SetFont(aFont
);
259 mpOutputDevice
->SetTextColor(Color(aRGBFontColor
));
261 OUString
aText( rTextCandidate
.getText() );
262 sal_Int32 nPos
= rTextCandidate
.getTextPosition();
263 sal_Int32 nLen
= rTextCandidate
.getTextLength();
265 long* pDXArray
= aTransformedDXArray
.size() ? &(aTransformedDXArray
[0]) : nullptr ;
267 if ( rTextCandidate
.isFilled() )
269 basegfx::B2DVector aOldFontScaling
, aOldTranslate
;
270 double fOldRotate
, fOldShearX
;
271 rTextCandidate
.getTextTransform().decompose(aOldFontScaling
, aOldTranslate
, fOldRotate
, fOldShearX
);
273 long nWidthToFill
= static_cast<long>(rTextCandidate
.getWidthToFill( ) * aFontScaling
.getX() / aOldFontScaling
.getX());
275 long nWidth
= mpOutputDevice
->GetTextArray( rTextCandidate
.getText(), pDXArray
, 0, 1 );
278 nChars
= nWidthToFill
/ nWidth
;
280 OUStringBuffer aFilled
;
281 comphelper::string::padToLength(aFilled
, nChars
, aText
[0]);
282 aText
= aFilled
.makeStringAndClear();
287 if(!aTransformedDXArray
.empty())
289 mpOutputDevice
->DrawTextArray(
298 mpOutputDevice
->DrawText(
305 if(rTextCandidate
.getFontAttribute().getRTL())
307 mpOutputDevice
->SetLayoutMode(nOldLayoutMode
);
310 bPrimitiveAccepted
= true;
314 if(!bPrimitiveAccepted
)
317 process(rTextCandidate
.get2DDecomposition(getViewInformation2D()));
321 // direct draw of hairline
322 void VclProcessor2D::RenderPolygonHairlinePrimitive2D(const primitive2d::PolygonHairlinePrimitive2D
& rPolygonCandidate
, bool bPixelBased
)
324 const basegfx::BColor
aHairlineColor(maBColorModifierStack
.getModifiedColor(rPolygonCandidate
.getBColor()));
325 mpOutputDevice
->SetLineColor(Color(aHairlineColor
));
326 mpOutputDevice
->SetFillColor();
328 basegfx::B2DPolygon
aLocalPolygon(rPolygonCandidate
.getB2DPolygon());
329 aLocalPolygon
.transform(maCurrentTransformation
);
331 static bool bCheckTrapezoidDecomposition(false);
332 static bool bShowOutlinesThere(false);
333 if(bCheckTrapezoidDecomposition
)
335 // clip against discrete ViewPort
336 const basegfx::B2DRange
& rDiscreteViewport
= getViewInformation2D().getDiscreteViewport();
337 basegfx::B2DPolyPolygon
aLocalPolyPolygon(basegfx::tools::clipPolygonOnRange(
338 aLocalPolygon
, rDiscreteViewport
, true, false));
340 if(aLocalPolyPolygon
.count())
343 aLocalPolyPolygon
= basegfx::tools::adaptiveSubdivideByDistance(
344 aLocalPolyPolygon
, 0.5);
347 static double fLineWidth(2.0);
348 basegfx::B2DTrapezoidVector aB2DTrapezoidVector
;
349 basegfx::tools::createLineTrapezoidFromB2DPolyPolygon(aB2DTrapezoidVector
, aLocalPolyPolygon
, fLineWidth
);
351 const sal_uInt32
nCount(aB2DTrapezoidVector
.size());
355 basegfx::BColor
aInvPolygonColor(aHairlineColor
);
356 aInvPolygonColor
.invert();
358 for(sal_uInt32
a(0); a
< nCount
; a
++)
360 const basegfx::B2DPolygon
aTempPolygon(aB2DTrapezoidVector
[a
].getB2DPolygon());
362 if(bShowOutlinesThere
)
364 mpOutputDevice
->SetFillColor(Color(aHairlineColor
));
365 mpOutputDevice
->SetLineColor();
368 mpOutputDevice
->DrawPolygon(aTempPolygon
);
370 if(bShowOutlinesThere
)
372 mpOutputDevice
->SetFillColor();
373 mpOutputDevice
->SetLineColor(Color(aInvPolygonColor
));
374 mpOutputDevice
->DrawPolyLine(aTempPolygon
, 0.0);
382 if(bPixelBased
&& getOptionsDrawinglayer().IsAntiAliasing() && getOptionsDrawinglayer().IsSnapHorVerLinesToDiscrete())
385 // when a Hairline is painted and AntiAliasing is on the option SnapHorVerLinesToDiscrete
386 // allows to suppress AntiAliasing for pure horizontal or vertical lines. This is done since
387 // not-AntiAliased such lines look more pleasing to the eye (e.g. 2D chart content). This
388 // NEEDS to be done in discrete coordinates, so only useful for pixel based rendering.
389 aLocalPolygon
= basegfx::tools::snapPointsOfHorizontalOrVerticalEdges(aLocalPolygon
);
392 mpOutputDevice
->DrawPolyLine(aLocalPolygon
, 0.0);
396 // direct draw of transformed BitmapEx primitive
397 void VclProcessor2D::RenderBitmapPrimitive2D(const primitive2d::BitmapPrimitive2D
& rBitmapCandidate
)
399 BitmapEx
aBitmapEx(rBitmapCandidate
.getBitmapEx());
400 const basegfx::B2DHomMatrix
aLocalTransform(maCurrentTransformation
* rBitmapCandidate
.getTransform());
402 if(maBColorModifierStack
.count())
404 aBitmapEx
= aBitmapEx
.ModifyBitmapEx(maBColorModifierStack
);
406 if(aBitmapEx
.IsEmpty())
408 // color gets completely replaced, get it
409 const basegfx::BColor
aModifiedColor(maBColorModifierStack
.getModifiedColor(basegfx::BColor()));
410 basegfx::B2DPolygon
aPolygon(basegfx::tools::createUnitPolygon());
411 aPolygon
.transform(aLocalTransform
);
413 mpOutputDevice
->SetFillColor(Color(aModifiedColor
));
414 mpOutputDevice
->SetLineColor();
415 mpOutputDevice
->DrawPolygon(aPolygon
);
421 // #122923# do no longer add Alpha channel here; the right place to do this is when really
422 // the own transformer is used (see OutputDevice::DrawTransformedBitmapEx).
424 // draw using OutputDevice'sDrawTransformedBitmapEx
425 mpOutputDevice
->DrawTransformedBitmapEx(aLocalTransform
, aBitmapEx
);
428 void VclProcessor2D::RenderFillGraphicPrimitive2D(const primitive2d::FillGraphicPrimitive2D
& rFillBitmapCandidate
)
430 const attribute::FillGraphicAttribute
& rFillGraphicAttribute(rFillBitmapCandidate
.getFillGraphic());
431 bool bPrimitiveAccepted(false);
432 static bool bTryTilingDirect
= true;
434 // #121194# when tiling is used and content is bitmap-based, do direct tiling in the
435 // renderer on pixel base to ensure tight fitting. Do not do this when
436 // the fill is rotated or sheared.
438 // override static bool (for debug) and tiling is active
439 if(bTryTilingDirect
&& rFillGraphicAttribute
.getTiling())
441 // content is bitmap(ex)
443 // for SVG support, force decomposition when SVG is present. This will lead to use
444 // the primitive representation of the svg directly.
446 // when graphic is animated, force decomposition to use the correct graphic, else
447 // fill style will not be animated
448 if(GRAPHIC_BITMAP
== rFillGraphicAttribute
.getGraphic().GetType()
449 && !rFillGraphicAttribute
.getGraphic().getSvgData().get()
450 && !rFillGraphicAttribute
.getGraphic().IsAnimated())
452 // decompose matrix to check for shear, rotate and mirroring
453 basegfx::B2DHomMatrix
aLocalTransform(maCurrentTransformation
* rFillBitmapCandidate
.getTransformation());
454 basegfx::B2DVector aScale
, aTranslate
;
455 double fRotate
, fShearX
;
456 aLocalTransform
.decompose(aScale
, aTranslate
, fRotate
, fShearX
);
458 // when nopt rotated/sheared
459 if(basegfx::fTools::equalZero(fRotate
) && basegfx::fTools::equalZero(fShearX
))
461 // no shear or rotate, draw direct in pixel coordinates
462 bPrimitiveAccepted
= true;
464 // transform object range to device coordinates (pixels). Use
465 // the device transformation for better accuracy
466 basegfx::B2DRange
aObjectRange(aTranslate
, aTranslate
+ aScale
);
467 aObjectRange
.transform(mpOutputDevice
->GetViewTransformation());
469 // extract discrete size of object
470 const sal_Int32
nOWidth(basegfx::fround(aObjectRange
.getWidth()));
471 const sal_Int32
nOHeight(basegfx::fround(aObjectRange
.getHeight()));
473 // only do something when object has a size in discrete units
474 if(nOWidth
> 0 && nOHeight
> 0)
476 // transform graphic range to device coordinates (pixels). Use
477 // the device transformation for better accuracy
478 basegfx::B2DRange
aGraphicRange(rFillGraphicAttribute
.getGraphicRange());
479 aGraphicRange
.transform(mpOutputDevice
->GetViewTransformation() * aLocalTransform
);
481 // extract discrete size of graphic
482 // caution: when getting to zero, nothing would be painted; thus, do not allow this
483 const sal_Int32
nBWidth(std::max(sal_Int32(1), basegfx::fround(aGraphicRange
.getWidth())));
484 const sal_Int32
nBHeight(std::max(sal_Int32(1), basegfx::fround(aGraphicRange
.getHeight())));
486 // only do something when bitmap fill has a size in discrete units
487 if(nBWidth
> 0 && nBHeight
> 0)
489 // nBWidth, nBHeight is the pixel size of the neede bitmap. To not need to scale it
490 // in vcl many times, create a size-optimized version
491 const Size
aNeededBitmapSizePixel(nBWidth
, nBHeight
);
492 BitmapEx
aBitmapEx(rFillGraphicAttribute
.getGraphic().GetBitmapEx());
493 static bool bEnablePreScaling(true);
494 const bool bPreScaled(bEnablePreScaling
&& nBWidth
* nBHeight
< (250 * 250));
496 // ... but only up to a maximum size, else it gets too expensive
499 // if color depth is below 24bit, expand before scaling for better quality.
500 // This is even needed for low colors, else the scale will produce
501 // a bitmap in gray or Black/White (!)
502 if(aBitmapEx
.GetBitCount() < 24)
504 aBitmapEx
.Convert(BMP_CONVERSION_24BIT
);
507 aBitmapEx
.Scale(aNeededBitmapSizePixel
, BmpScaleFlag::Interpolate
);
510 bool bPainted(false);
512 if(maBColorModifierStack
.count())
514 // when color modifier, apply to bitmap
515 aBitmapEx
= aBitmapEx
.ModifyBitmapEx(maBColorModifierStack
);
517 // impModifyBitmapEx uses empty bitmap as sign to return that
518 // the content will be completely replaced to mono color, use shortcut
519 if(aBitmapEx
.IsEmpty())
521 // color gets completely replaced, get it
522 const basegfx::BColor
aModifiedColor(maBColorModifierStack
.getModifiedColor(basegfx::BColor()));
523 basegfx::B2DPolygon
aPolygon(basegfx::tools::createUnitPolygon());
524 aPolygon
.transform(aLocalTransform
);
526 mpOutputDevice
->SetFillColor(Color(aModifiedColor
));
527 mpOutputDevice
->SetLineColor();
528 mpOutputDevice
->DrawPolygon(aPolygon
);
536 sal_Int32
nBLeft(basegfx::fround(aGraphicRange
.getMinX()));
537 sal_Int32
nBTop(basegfx::fround(aGraphicRange
.getMinY()));
538 const sal_Int32
nOLeft(basegfx::fround(aObjectRange
.getMinX()));
539 const sal_Int32
nOTop(basegfx::fround(aObjectRange
.getMinY()));
545 const sal_Int32
nDiff((nBLeft
/ nBWidth
) + 1);
548 nBLeft
-= nDiff
* nBWidth
;
551 if(nBLeft
+ nBWidth
<= nOLeft
)
553 const sal_Int32
nDiff(-nBLeft
/ nBWidth
);
556 nBLeft
+= nDiff
* nBWidth
;
561 const sal_Int32
nDiff((nBTop
/ nBHeight
) + 1);
564 nBTop
-= nDiff
* nBHeight
;
567 if(nBTop
+ nBHeight
<= nOTop
)
569 const sal_Int32
nDiff(-nBTop
/ nBHeight
);
572 nBTop
+= nDiff
* nBHeight
;
576 const Point
aEmptyPoint(0, 0);
577 const Rectangle
aVisiblePixel(aEmptyPoint
, mpOutputDevice
->GetOutputSizePixel());
578 const bool bWasEnabled(mpOutputDevice
->IsMapModeEnabled());
579 mpOutputDevice
->EnableMapMode(false);
581 // check if offset is used
582 const sal_Int32
nOffsetX(basegfx::fround(rFillGraphicAttribute
.getOffsetX() * nBWidth
));
586 // offset in X, so iterate over Y first and draw lines
587 for(sal_Int32
nYPos(nBTop
); nYPos
< nOTop
+ nOHeight
; nYPos
+= nBHeight
, nPosY
++)
589 for(sal_Int32
nXPos((nPosY
% 2) ? nBLeft
- nBWidth
+ nOffsetX
: nBLeft
);
590 nXPos
< nOLeft
+ nOWidth
; nXPos
+= nBWidth
)
592 const Rectangle
aOutRectPixel(Point(nXPos
, nYPos
), aNeededBitmapSizePixel
);
594 if(aOutRectPixel
.IsOver(aVisiblePixel
))
598 mpOutputDevice
->DrawBitmapEx(aOutRectPixel
.TopLeft(), aBitmapEx
);
602 mpOutputDevice
->DrawBitmapEx(aOutRectPixel
.TopLeft(), aNeededBitmapSizePixel
, aBitmapEx
);
610 // check if offset is used
611 const sal_Int32
nOffsetY(basegfx::fround(rFillGraphicAttribute
.getOffsetY() * nBHeight
));
613 // possible offset in Y, so iterate over X first and draw columns
614 for(sal_Int32
nXPos(nBLeft
); nXPos
< nOLeft
+ nOWidth
; nXPos
+= nBWidth
, nPosX
++)
616 for(sal_Int32
nYPos((nPosX
% 2) ? nBTop
- nBHeight
+ nOffsetY
: nBTop
);
617 nYPos
< nOTop
+ nOHeight
; nYPos
+= nBHeight
)
619 const Rectangle
aOutRectPixel(Point(nXPos
, nYPos
), aNeededBitmapSizePixel
);
621 if(aOutRectPixel
.IsOver(aVisiblePixel
))
625 mpOutputDevice
->DrawBitmapEx(aOutRectPixel
.TopLeft(), aBitmapEx
);
629 mpOutputDevice
->DrawBitmapEx(aOutRectPixel
.TopLeft(), aNeededBitmapSizePixel
, aBitmapEx
);
637 mpOutputDevice
->EnableMapMode(bWasEnabled
);
645 if(!bPrimitiveAccepted
)
647 // do not accept, use decomposition
648 process(rFillBitmapCandidate
.get2DDecomposition(getViewInformation2D()));
652 // direct draw of Graphic
653 void VclProcessor2D::RenderPolyPolygonGraphicPrimitive2D(const primitive2d::PolyPolygonGraphicPrimitive2D
& rPolygonCandidate
)
656 const basegfx::B2DPolyPolygon
& rPolyPolygon
= rPolygonCandidate
.getB2DPolyPolygon();
658 // #121194# Todo: check if this works
659 if(!rPolyPolygon
.count())
661 // empty polyPolygon, done
666 const attribute::FillGraphicAttribute
& rFillGraphicAttribute
= rPolygonCandidate
.getFillGraphic();
668 // try to catch cases where the graphic will be color-modified to a single
669 // color (e.g. shadow)
670 switch(rFillGraphicAttribute
.getGraphic().GetType())
672 case GRAPHIC_GDIMETAFILE
:
674 // metafiles are potentially transparent, cannot optimize, not done
679 if(!rFillGraphicAttribute
.getGraphic().IsTransparent() && !rFillGraphicAttribute
.getGraphic().IsAlpha())
681 // bitmap is not transparent and has no alpha
682 const sal_uInt32
nBColorModifierStackCount(maBColorModifierStack
.count());
684 if(nBColorModifierStackCount
)
686 const basegfx::BColorModifierSharedPtr
& rTopmostModifier
= maBColorModifierStack
.getBColorModifier(nBColorModifierStackCount
- 1);
687 const basegfx::BColorModifier_replace
* pReplacer
= dynamic_cast< const basegfx::BColorModifier_replace
* >(rTopmostModifier
.get());
691 // the bitmap fill is in unified color, so we can replace it with
692 // a single polygon fill. The form of the fill depends on tiling
693 if(rFillGraphicAttribute
.getTiling())
695 // with tiling, fill the whole tools::PolyPolygon with the modifier color
696 basegfx::B2DPolyPolygon
aLocalPolyPolygon(rPolyPolygon
);
698 aLocalPolyPolygon
.transform(maCurrentTransformation
);
699 mpOutputDevice
->SetLineColor();
700 mpOutputDevice
->SetFillColor(Color(pReplacer
->getBColor()));
701 mpOutputDevice
->DrawPolyPolygon(aLocalPolyPolygon
);
705 // without tiling, only the area common to the bitmap tile and the
706 // tools::PolyPolygon is filled. Create the bitmap tile area in object
707 // coordinates. For this, the object transformation needs to be created
708 // from the already scaled PolyPolygon. The tile area in object
709 // coordinates wil always be non-rotated, so it's not necessary to
710 // work with a polygon here
711 basegfx::B2DRange
aTileRange(rFillGraphicAttribute
.getGraphicRange());
712 const basegfx::B2DRange
aPolyPolygonRange(rPolyPolygon
.getB2DRange());
713 const basegfx::B2DHomMatrix
aNewObjectTransform(
714 basegfx::tools::createScaleTranslateB2DHomMatrix(
715 aPolyPolygonRange
.getRange(),
716 aPolyPolygonRange
.getMinimum()));
718 aTileRange
.transform(aNewObjectTransform
);
720 // now clip the object polyPolygon against the tile range
721 // to get the common area
722 basegfx::B2DPolyPolygon aTarget
= basegfx::tools::clipPolyPolygonOnRange(
730 aTarget
.transform(maCurrentTransformation
);
731 mpOutputDevice
->SetLineColor();
732 mpOutputDevice
->SetFillColor(Color(pReplacer
->getBColor()));
733 mpOutputDevice
->DrawPolyPolygon(aTarget
);
737 // simplified output executed, we are done
744 default: //GRAPHIC_NONE, GRAPHIC_DEFAULT
746 // empty graphic, we are done
755 // use default decomposition
756 process(rPolygonCandidate
.get2DDecomposition(getViewInformation2D()));
760 // direct draw of MetaFile
761 void VclProcessor2D::RenderMetafilePrimitive2D(const primitive2d::MetafilePrimitive2D
& rMetaCandidate
)
763 // decompose matrix to check for shear, rotate and mirroring
764 basegfx::B2DHomMatrix
aLocalTransform(maCurrentTransformation
* rMetaCandidate
.getTransform());
765 basegfx::B2DVector aScale
, aTranslate
;
766 double fRotate
, fShearX
;
767 aLocalTransform
.decompose(aScale
, aTranslate
, fRotate
, fShearX
);
769 if(basegfx::fTools::less(aScale
.getX(), 0.0) && basegfx::fTools::less(aScale
.getY(), 0.0))
771 // #i102175# handle special case: If scale is negative in (x,y) (3rd quadrant), it can
772 // be expressed as rotation by PI. This needs to be done for Metafiles since
773 // these can be rotated, but not really mirrored
774 aScale
= basegfx::absolute(aScale
);
779 basegfx::B2DRange
aOutlineRange(rMetaCandidate
.getB2DRange(getViewInformation2D()));
780 aOutlineRange
.transform(maCurrentTransformation
);
782 // Due to the integer MapModes used from VCL aind inside MetaFiles errors of up to three
783 // pixels in size may happen. As long as there is no better way (e.g. convert the MetaFile
784 // to primitives) it is necessary to reduce maximum pixel size by 1 in X and Y and to use
785 // the inner pixel bounds accordingly (ceil resp. floor). This will also be done for logic
786 // units e.g. when creating a new MetaFile, but since much huger value ranges are used
787 // there typically will be okay for this compromise.
788 Rectangle
aDestRectView(
789 // !!CAUTION!! Here, ceil and floor are exchanged BY PURPOSE, do NOT copy when
790 // looking for a standard conversion to rectangle (!)
791 (sal_Int32
)ceil(aOutlineRange
.getMinX()), (sal_Int32
)ceil(aOutlineRange
.getMinY()),
792 (sal_Int32
)floor(aOutlineRange
.getMaxX()), (sal_Int32
)floor(aOutlineRange
.getMaxY()));
794 // get metafile (copy it)
795 GDIMetaFile aMetaFile
;
797 if(maBColorModifierStack
.count())
799 const basegfx::BColor
aRGBBaseColor(0, 0, 0);
800 const basegfx::BColor
aRGBColor(maBColorModifierStack
.getModifiedColor(aRGBBaseColor
));
801 aMetaFile
= rMetaCandidate
.getMetaFile().GetMonochromeMtf(Color(aRGBColor
));
805 aMetaFile
= rMetaCandidate
.getMetaFile();
809 if(!basegfx::fTools::equalZero(fRotate
))
812 // MetaFile::Rotate has no input parameter check, so the parameter needs to be
813 // well-aligned to the old range [0..3600] 10th degrees with inverse orientation
814 sal_Int16
nRotation((sal_Int16
)((fRotate
/ F_PI180
) * -10.0));
819 while(nRotation
>= 3600)
822 aMetaFile
.Rotate(nRotation
);
825 // Prepare target output size
826 Size
aDestSize(aDestRectView
.GetSize());
828 if(aDestSize
.getWidth() > 0 && aDestSize
.getHeight() > 0)
830 // Get preferred Metafile output size. When it's very equal to the output size, it's probably
831 // a rounding error somewhere, so correct it to get a 1:1 output without single pixel scalings
832 // of the Metafile (esp. for contaned Bitmaps, e.g 3D charts)
833 const Size
aPrefSize(mpOutputDevice
->LogicToPixel(aMetaFile
.GetPrefSize(), aMetaFile
.GetPrefMapMode()));
835 if(aPrefSize
.getWidth() && (aPrefSize
.getWidth() - 1 == aDestSize
.getWidth() || aPrefSize
.getWidth() + 1 == aDestSize
.getWidth()))
837 aDestSize
.setWidth(aPrefSize
.getWidth());
840 if(aPrefSize
.getHeight() && (aPrefSize
.getHeight() - 1 == aDestSize
.getHeight() || aPrefSize
.getHeight() + 1 == aDestSize
.getHeight()))
842 aDestSize
.setHeight(aPrefSize
.getHeight());
846 aMetaFile
.WindStart();
847 aMetaFile
.Play(mpOutputDevice
, aDestRectView
.TopLeft(), aDestSize
);
851 // mask group. Force output to VDev and create mask from given mask
852 void VclProcessor2D::RenderMaskPrimitive2DPixel(const primitive2d::MaskPrimitive2D
& rMaskCandidate
)
854 if(!rMaskCandidate
.getChildren().empty())
856 basegfx::B2DPolyPolygon
aMask(rMaskCandidate
.getMask());
860 aMask
.transform(maCurrentTransformation
);
861 const basegfx::B2DRange
aRange(basegfx::tools::getRange(aMask
));
862 impBufferDevice
aBufferDevice(*mpOutputDevice
, aRange
);
864 if(aBufferDevice
.isVisible())
866 // remember last OutDev and set to content
867 OutputDevice
* pLastOutputDevice
= mpOutputDevice
;
868 mpOutputDevice
= &aBufferDevice
.getContent();
871 process(rMaskCandidate
.getChildren());
873 // back to old OutDev
874 mpOutputDevice
= pLastOutputDevice
;
877 if(getOptionsDrawinglayer().IsAntiAliasing())
879 // with AA, use 8bit AlphaMask to get nice borders
880 VirtualDevice
& rTransparence
= aBufferDevice
.getTransparence();
881 rTransparence
.SetLineColor();
882 rTransparence
.SetFillColor(COL_BLACK
);
883 rTransparence
.DrawPolyPolygon(aMask
);
885 // dump buffer to outdev
886 aBufferDevice
.paint();
890 // No AA, use 1bit mask
891 VirtualDevice
& rMask
= aBufferDevice
.getMask();
892 rMask
.SetLineColor();
893 rMask
.SetFillColor(COL_BLACK
);
894 rMask
.DrawPolyPolygon(aMask
);
896 // dump buffer to outdev
897 aBufferDevice
.paint();
904 // modified color group. Force output to unified color.
905 void VclProcessor2D::RenderModifiedColorPrimitive2D(const primitive2d::ModifiedColorPrimitive2D
& rModifiedCandidate
)
907 if(!rModifiedCandidate
.getChildren().empty())
909 maBColorModifierStack
.push(rModifiedCandidate
.getColorModifier());
910 process(rModifiedCandidate
.getChildren());
911 maBColorModifierStack
.pop();
915 // unified sub-transparence. Draw to VDev first.
916 void VclProcessor2D::RenderUnifiedTransparencePrimitive2D(const primitive2d::UnifiedTransparencePrimitive2D
& rTransCandidate
)
918 static bool bForceToDecomposition(false);
920 if(!rTransCandidate
.getChildren().empty())
922 if(bForceToDecomposition
)
925 process(rTransCandidate
.get2DDecomposition(getViewInformation2D()));
929 if(0.0 == rTransCandidate
.getTransparence())
931 // no transparence used, so just use the content
932 process(rTransCandidate
.getChildren());
934 else if(rTransCandidate
.getTransparence() > 0.0 && rTransCandidate
.getTransparence() < 1.0)
936 // transparence is in visible range
937 basegfx::B2DRange
aRange(rTransCandidate
.getChildren().getB2DRange(getViewInformation2D()));
938 aRange
.transform(maCurrentTransformation
);
939 impBufferDevice
aBufferDevice(*mpOutputDevice
, aRange
);
941 if(aBufferDevice
.isVisible())
943 // remember last OutDev and set to content
944 OutputDevice
* pLastOutputDevice
= mpOutputDevice
;
945 mpOutputDevice
= &aBufferDevice
.getContent();
947 // paint content to it
948 process(rTransCandidate
.getChildren());
950 // back to old OutDev
951 mpOutputDevice
= pLastOutputDevice
;
953 // dump buffer to outdev using given transparence
954 aBufferDevice
.paint(rTransCandidate
.getTransparence());
961 // sub-transparence group. Draw to VDev first.
962 void VclProcessor2D::RenderTransparencePrimitive2D(const primitive2d::TransparencePrimitive2D
& rTransCandidate
)
964 if(!rTransCandidate
.getChildren().empty())
966 basegfx::B2DRange
aRange(rTransCandidate
.getChildren().getB2DRange(getViewInformation2D()));
967 aRange
.transform(maCurrentTransformation
);
968 impBufferDevice
aBufferDevice(*mpOutputDevice
, aRange
);
970 if(aBufferDevice
.isVisible())
972 // remember last OutDev and set to content
973 OutputDevice
* pLastOutputDevice
= mpOutputDevice
;
974 mpOutputDevice
= &aBufferDevice
.getContent();
976 // paint content to it
977 process(rTransCandidate
.getChildren());
980 mpOutputDevice
= &aBufferDevice
.getTransparence();
982 // when painting transparence masks, reset the color stack
983 basegfx::BColorModifierStack
aLastBColorModifierStack(maBColorModifierStack
);
984 maBColorModifierStack
= basegfx::BColorModifierStack();
986 // paint mask to it (always with transparence intensities, evtl. with AA)
987 process(rTransCandidate
.getTransparence());
989 // back to old color stack
990 maBColorModifierStack
= aLastBColorModifierStack
;
992 // back to old OutDev
993 mpOutputDevice
= pLastOutputDevice
;
995 // dump buffer to outdev
996 aBufferDevice
.paint();
1002 void VclProcessor2D::RenderTransformPrimitive2D(const primitive2d::TransformPrimitive2D
& rTransformCandidate
)
1004 // remember current transformation and ViewInformation
1005 const basegfx::B2DHomMatrix
aLastCurrentTransformation(maCurrentTransformation
);
1006 const geometry::ViewInformation2D
aLastViewInformation2D(getViewInformation2D());
1008 // create new transformations for CurrentTransformation
1009 // and for local ViewInformation2D
1010 maCurrentTransformation
= maCurrentTransformation
* rTransformCandidate
.getTransformation();
1011 const geometry::ViewInformation2D
aViewInformation2D(
1012 getViewInformation2D().getObjectTransformation() * rTransformCandidate
.getTransformation(),
1013 getViewInformation2D().getViewTransformation(),
1014 getViewInformation2D().getViewport(),
1015 getViewInformation2D().getVisualizedPage(),
1016 getViewInformation2D().getViewTime(),
1017 getViewInformation2D().getExtendedInformationSequence());
1018 updateViewInformation(aViewInformation2D
);
1021 process(rTransformCandidate
.getChildren());
1023 // restore transformations
1024 maCurrentTransformation
= aLastCurrentTransformation
;
1025 updateViewInformation(aLastViewInformation2D
);
1028 // new XDrawPage for ViewInformation2D
1029 void VclProcessor2D::RenderPagePreviewPrimitive2D(const primitive2d::PagePreviewPrimitive2D
& rPagePreviewCandidate
)
1031 // remember current transformation and ViewInformation
1032 const geometry::ViewInformation2D
aLastViewInformation2D(getViewInformation2D());
1034 // create new local ViewInformation2D
1035 const geometry::ViewInformation2D
aViewInformation2D(
1036 getViewInformation2D().getObjectTransformation(),
1037 getViewInformation2D().getViewTransformation(),
1038 getViewInformation2D().getViewport(),
1039 rPagePreviewCandidate
.getXDrawPage(),
1040 getViewInformation2D().getViewTime(),
1041 getViewInformation2D().getExtendedInformationSequence());
1042 updateViewInformation(aViewInformation2D
);
1044 // process decomposed content
1045 process(rPagePreviewCandidate
.get2DDecomposition(getViewInformation2D()));
1047 // restore transformations
1048 updateViewInformation(aLastViewInformation2D
);
1052 void VclProcessor2D::RenderMarkerArrayPrimitive2D(const primitive2d::MarkerArrayPrimitive2D
& rMarkArrayCandidate
)
1054 static bool bCheckCompleteMarkerDecompose(false);
1055 if(bCheckCompleteMarkerDecompose
)
1057 process(rMarkArrayCandidate
.get2DDecomposition(getViewInformation2D()));
1062 const std::vector
< basegfx::B2DPoint
>& rPositions
= rMarkArrayCandidate
.getPositions();
1063 const sal_uInt32
nCount(rPositions
.size());
1065 if(nCount
&& !rMarkArrayCandidate
.getMarker().IsEmpty())
1068 const BitmapEx
& rMarker(rMarkArrayCandidate
.getMarker());
1069 const Size
aBitmapSize(rMarker
.GetSizePixel());
1071 if(aBitmapSize
.Width() && aBitmapSize
.Height())
1073 // get discrete half size
1074 const basegfx::B2DVector
aDiscreteHalfSize(
1075 (aBitmapSize
.getWidth() - 1.0) * 0.5,
1076 (aBitmapSize
.getHeight() - 1.0) * 0.5);
1077 const bool bWasEnabled(mpOutputDevice
->IsMapModeEnabled());
1079 // do not forget evtl. moved origin in target device MapMode when
1080 // switching it off; it would be missing and lead to wrong positions.
1081 // All his could be done using logic sizes and coordinates, too, but
1082 // we want a 1:1 bitmap rendering here, so it's more safe and faster
1083 // to work with switching off MapMode usage completely.
1084 const Point
aOrigin(mpOutputDevice
->GetMapMode().GetOrigin());
1086 mpOutputDevice
->EnableMapMode(false);
1088 for(std::vector
< basegfx::B2DPoint
>::const_iterator
aIter(rPositions
.begin()); aIter
!= rPositions
.end(); ++aIter
)
1090 const basegfx::B2DPoint
aDiscreteTopLeft((maCurrentTransformation
* (*aIter
)) - aDiscreteHalfSize
);
1091 const Point
aDiscretePoint(basegfx::fround(aDiscreteTopLeft
.getX()), basegfx::fround(aDiscreteTopLeft
.getY()));
1093 mpOutputDevice
->DrawBitmapEx(aDiscretePoint
+ aOrigin
, rMarker
);
1096 mpOutputDevice
->EnableMapMode(bWasEnabled
);
1102 void VclProcessor2D::RenderPointArrayPrimitive2D(const primitive2d::PointArrayPrimitive2D
& rPointArrayCandidate
)
1104 const std::vector
< basegfx::B2DPoint
>& rPositions
= rPointArrayCandidate
.getPositions();
1105 const basegfx::BColor
aRGBColor(maBColorModifierStack
.getModifiedColor(rPointArrayCandidate
.getRGBColor()));
1106 const Color
aVCLColor(aRGBColor
);
1108 for(std::vector
< basegfx::B2DPoint
>::const_iterator
aIter(rPositions
.begin()); aIter
!= rPositions
.end(); ++aIter
)
1110 const basegfx::B2DPoint
aViewPosition(maCurrentTransformation
* (*aIter
));
1111 const Point
aPos(basegfx::fround(aViewPosition
.getX()), basegfx::fround(aViewPosition
.getY()));
1113 mpOutputDevice
->DrawPixel(aPos
, aVCLColor
);
1117 void VclProcessor2D::RenderPolygonStrokePrimitive2D(const primitive2d::PolygonStrokePrimitive2D
& rPolygonStrokeCandidate
)
1119 // #i101491# method restructured to clearly use the DrawPolyLine
1120 // calls starting from a defined line width
1121 const attribute::LineAttribute
& rLineAttribute
= rPolygonStrokeCandidate
.getLineAttribute();
1122 const double fLineWidth(rLineAttribute
.getWidth());
1125 if(basegfx::fTools::more(fLineWidth
, 0.0))
1127 const basegfx::B2DVector
aDiscreteUnit(maCurrentTransformation
* basegfx::B2DVector(fLineWidth
, 0.0));
1128 const double fDiscreteLineWidth(aDiscreteUnit
.getLength());
1129 const attribute::StrokeAttribute
& rStrokeAttribute
= rPolygonStrokeCandidate
.getStrokeAttribute();
1130 const basegfx::BColor
aHairlineColor(maBColorModifierStack
.getModifiedColor(rLineAttribute
.getColor()));
1131 basegfx::B2DPolyPolygon aHairlinePolyPolygon
;
1133 mpOutputDevice
->SetLineColor(Color(aHairlineColor
));
1134 mpOutputDevice
->SetFillColor();
1136 if(0.0 == rStrokeAttribute
.getFullDotDashLen())
1138 // no line dashing, just copy
1139 aHairlinePolyPolygon
.append(rPolygonStrokeCandidate
.getB2DPolygon());
1143 // else apply LineStyle
1144 basegfx::tools::applyLineDashing(rPolygonStrokeCandidate
.getB2DPolygon(),
1145 rStrokeAttribute
.getDotDashArray(),
1146 &aHairlinePolyPolygon
, nullptr, rStrokeAttribute
.getFullDotDashLen());
1149 const sal_uInt32
nCount(aHairlinePolyPolygon
.count());
1153 const bool bAntiAliased(getOptionsDrawinglayer().IsAntiAliasing());
1154 aHairlinePolyPolygon
.transform(maCurrentTransformation
);
1158 if(basegfx::fTools::lessOrEqual(fDiscreteLineWidth
, 1.0))
1160 // line in range ]0.0 .. 1.0[
1161 // paint as simple hairline
1162 for(sal_uInt32
a(0); a
< nCount
; a
++)
1164 mpOutputDevice
->DrawPolyLine(aHairlinePolyPolygon
.getB2DPolygon(a
), 0.0);
1169 else if(basegfx::fTools::lessOrEqual(fDiscreteLineWidth
, 2.0))
1171 // line in range [1.0 .. 2.0[
1172 // paint as 2x2 with dynamic line distance
1173 basegfx::B2DHomMatrix aMat
;
1174 const double fDistance(fDiscreteLineWidth
- 1.0);
1175 const double fHalfDistance(fDistance
* 0.5);
1177 for(sal_uInt32
a(0); a
< nCount
; a
++)
1179 basegfx::B2DPolygon
aCandidate(aHairlinePolyPolygon
.getB2DPolygon(a
));
1181 aMat
.set(0, 2, -fHalfDistance
);
1182 aMat
.set(1, 2, -fHalfDistance
);
1183 aCandidate
.transform(aMat
);
1184 mpOutputDevice
->DrawPolyLine(aCandidate
, 0.0);
1186 aMat
.set(0, 2, fDistance
);
1187 aMat
.set(1, 2, 0.0);
1188 aCandidate
.transform(aMat
);
1189 mpOutputDevice
->DrawPolyLine(aCandidate
, 0.0);
1191 aMat
.set(0, 2, 0.0);
1192 aMat
.set(1, 2, fDistance
);
1193 aCandidate
.transform(aMat
);
1194 mpOutputDevice
->DrawPolyLine(aCandidate
, 0.0);
1196 aMat
.set(0, 2, -fDistance
);
1197 aMat
.set(1, 2, 0.0);
1198 aCandidate
.transform(aMat
);
1199 mpOutputDevice
->DrawPolyLine(aCandidate
, 0.0);
1204 else if(basegfx::fTools::lessOrEqual(fDiscreteLineWidth
, 3.0))
1206 // line in range [2.0 .. 3.0]
1207 // paint as cross in a 3x3 with dynamic line distance
1208 basegfx::B2DHomMatrix aMat
;
1209 const double fDistance((fDiscreteLineWidth
- 1.0) * 0.5);
1211 for(sal_uInt32
a(0); a
< nCount
; a
++)
1213 basegfx::B2DPolygon
aCandidate(aHairlinePolyPolygon
.getB2DPolygon(a
));
1215 mpOutputDevice
->DrawPolyLine(aCandidate
, 0.0);
1217 aMat
.set(0, 2, -fDistance
);
1218 aMat
.set(1, 2, 0.0);
1219 aCandidate
.transform(aMat
);
1220 mpOutputDevice
->DrawPolyLine(aCandidate
, 0.0);
1222 aMat
.set(0, 2, fDistance
);
1223 aMat
.set(1, 2, -fDistance
);
1224 aCandidate
.transform(aMat
);
1225 mpOutputDevice
->DrawPolyLine(aCandidate
, 0.0);
1227 aMat
.set(0, 2, fDistance
);
1228 aMat
.set(1, 2, fDistance
);
1229 aCandidate
.transform(aMat
);
1230 mpOutputDevice
->DrawPolyLine(aCandidate
, 0.0);
1232 aMat
.set(0, 2, -fDistance
);
1233 aMat
.set(1, 2, fDistance
);
1234 aCandidate
.transform(aMat
);
1235 mpOutputDevice
->DrawPolyLine(aCandidate
, 0.0);
1242 // #i101491# line width above 3.0
1247 if(basegfx::fTools::lessOrEqual(fDiscreteLineWidth
, 1.5))
1249 // line width below 1.5, draw the basic hairline polygon
1250 for(sal_uInt32
a(0); a
< nCount
; a
++)
1252 mpOutputDevice
->DrawPolyLine(aHairlinePolyPolygon
.getB2DPolygon(a
), 0.0);
1257 else if(basegfx::fTools::lessOrEqual(fDiscreteLineWidth
, 2.5))
1259 // line width is in range ]1.5 .. 2.5], use four hairlines
1260 // drawn in a square
1261 for(sal_uInt32
a(0); a
< nCount
; a
++)
1263 basegfx::B2DPolygon
aCandidate(aHairlinePolyPolygon
.getB2DPolygon(a
));
1264 basegfx::B2DHomMatrix aMat
;
1266 mpOutputDevice
->DrawPolyLine(aCandidate
, 0.0);
1268 aMat
.set(0, 2, 1.0);
1269 aMat
.set(1, 2, 0.0);
1270 aCandidate
.transform(aMat
);
1272 mpOutputDevice
->DrawPolyLine(aCandidate
, 0.0);
1274 aMat
.set(0, 2, 0.0);
1275 aMat
.set(1, 2, 1.0);
1276 aCandidate
.transform(aMat
);
1278 mpOutputDevice
->DrawPolyLine(aCandidate
, 0.0);
1280 aMat
.set(0, 2, -1.0);
1281 aMat
.set(1, 2, 0.0);
1282 aCandidate
.transform(aMat
);
1284 mpOutputDevice
->DrawPolyLine(aCandidate
, 0.0);
1291 // #i101491# line width is above 2.5
1295 if(!bDone
&& rPolygonStrokeCandidate
.getB2DPolygon().count() > 1000)
1297 // #i101491# If the polygon complexity uses more than a given amount, do
1298 // use OuputDevice::DrawPolyLine directly; this will avoid buffering all
1299 // decompositions in primitives (memory) and fallback to old line painting
1300 // for very complex polygons, too
1301 for(sal_uInt32
a(0); a
< nCount
; a
++)
1303 mpOutputDevice
->DrawPolyLine(
1304 aHairlinePolyPolygon
.getB2DPolygon(a
),
1306 rLineAttribute
.getLineJoin(),
1307 rLineAttribute
.getLineCap(),
1308 rLineAttribute
.getMiterMinimumAngle());
1318 // remember that we enter a PolygonStrokePrimitive2D decomposition,
1319 // used for AA thick line drawing
1320 mnPolygonStrokePrimitive2D
++;
1322 // line width is big enough for standard filled polygon visualisation or zero
1323 process(rPolygonStrokeCandidate
.get2DDecomposition(getViewInformation2D()));
1325 // leave PolygonStrokePrimitive2D
1326 mnPolygonStrokePrimitive2D
--;
1330 void VclProcessor2D::RenderEpsPrimitive2D(const primitive2d::EpsPrimitive2D
& rEpsPrimitive2D
)
1332 // The new decomposition of Metafiles made it necessary to add an Eps
1333 // primitive to handle embedded Eps data. On some devices, this can be
1334 // painted directly (mac, printer).
1335 // To be able to handle the replacement correctly, i need to handle it myself
1336 // since DrawEPS will not be able e.g. to rotate the replacement. To be able
1337 // to do that, i added a boolean return to OutputDevice::DrawEPS(..)
1338 // to know when EPS was handled directly already.
1339 basegfx::B2DRange
aRange(0.0, 0.0, 1.0, 1.0);
1340 aRange
.transform(maCurrentTransformation
* rEpsPrimitive2D
.getEpsTransform());
1342 if(!aRange
.isEmpty())
1344 const Rectangle
aRectangle(
1345 (sal_Int32
)floor(aRange
.getMinX()), (sal_Int32
)floor(aRange
.getMinY()),
1346 (sal_Int32
)ceil(aRange
.getMaxX()), (sal_Int32
)ceil(aRange
.getMaxY()));
1348 if(!aRectangle
.IsEmpty())
1350 bool bWillReallyRender
= mpOutputDevice
->IsDeviceOutputNecessary();
1351 // try to paint EPS directly without fallback visualisation
1352 const bool bEPSPaintedDirectly
= bWillReallyRender
&&
1353 mpOutputDevice
->DrawEPS(
1354 aRectangle
.TopLeft(),
1355 aRectangle
.GetSize(),
1356 rEpsPrimitive2D
.getGfxLink());
1358 if(!bEPSPaintedDirectly
)
1360 // use the decomposition which will correctly handle the
1361 // fallback visualisation using full transformation (e.g. rotation)
1362 process(rEpsPrimitive2D
.get2DDecomposition(getViewInformation2D()));
1368 void VclProcessor2D::RenderSvgLinearAtomPrimitive2D(const primitive2d::SvgLinearAtomPrimitive2D
& rCandidate
)
1370 const double fDelta(rCandidate
.getOffsetB() - rCandidate
.getOffsetA());
1372 if(basegfx::fTools::more(fDelta
, 0.0))
1374 const basegfx::BColor
aColorA(maBColorModifierStack
.getModifiedColor(rCandidate
.getColorA()));
1375 const basegfx::BColor
aColorB(maBColorModifierStack
.getModifiedColor(rCandidate
.getColorB()));
1377 // calculate discrete unit in WorldCoordinates; use diagonal (1.0, 1.0) and divide by sqrt(2)
1378 const basegfx::B2DVector
aDiscreteVector(getViewInformation2D().getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 1.0));
1379 const double fDiscreteUnit(aDiscreteVector
.getLength() * (1.0 / 1.414213562373));
1381 // use color distance and discrete lengths to calculate step count
1382 const sal_uInt32
nSteps(calculateStepsForSvgGradient(aColorA
, aColorB
, fDelta
, fDiscreteUnit
));
1384 // switch off line painting
1385 mpOutputDevice
->SetLineColor();
1387 // prepare polygon in needed width at start position (with discrete overlap)
1388 const basegfx::B2DPolygon
aPolygon(
1389 basegfx::tools::createPolygonFromRect(
1391 rCandidate
.getOffsetA() - fDiscreteUnit
,
1393 rCandidate
.getOffsetA() + (fDelta
/ nSteps
) + fDiscreteUnit
,
1397 // prepare loop ([0.0 .. 1.0[)
1398 double fUnitScale(0.0);
1399 const double fUnitStep(1.0 / nSteps
);
1402 for(sal_uInt32
a(0); a
< nSteps
; a
++, fUnitScale
+= fUnitStep
)
1404 basegfx::B2DPolygon
aNew(aPolygon
);
1406 aNew
.transform(maCurrentTransformation
* basegfx::tools::createTranslateB2DHomMatrix(fDelta
* fUnitScale
, 0.0));
1407 mpOutputDevice
->SetFillColor(Color(basegfx::interpolate(aColorA
, aColorB
, fUnitScale
)));
1408 mpOutputDevice
->DrawPolyPolygon(basegfx::B2DPolyPolygon(aNew
));
1413 void VclProcessor2D::RenderSvgRadialAtomPrimitive2D(const primitive2d::SvgRadialAtomPrimitive2D
& rCandidate
)
1415 const double fDeltaScale(rCandidate
.getScaleB() - rCandidate
.getScaleA());
1417 if(basegfx::fTools::more(fDeltaScale
, 0.0))
1419 const basegfx::BColor
aColorA(maBColorModifierStack
.getModifiedColor(rCandidate
.getColorA()));
1420 const basegfx::BColor
aColorB(maBColorModifierStack
.getModifiedColor(rCandidate
.getColorB()));
1422 // calculate discrete unit in WorldCoordinates; use diagonal (1.0, 1.0) and divide by sqrt(2)
1423 const basegfx::B2DVector
aDiscreteVector(getViewInformation2D().getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 1.0));
1424 const double fDiscreteUnit(aDiscreteVector
.getLength() * (1.0 / 1.414213562373));
1426 // use color distance and discrete lengths to calculate step count
1427 const sal_uInt32
nSteps(calculateStepsForSvgGradient(aColorA
, aColorB
, fDeltaScale
, fDiscreteUnit
));
1429 // switch off line painting
1430 mpOutputDevice
->SetLineColor();
1432 // prepare loop ([0.0 .. 1.0[, full polygons, no polypolygons with holes)
1433 double fUnitScale(0.0);
1434 const double fUnitStep(1.0 / nSteps
);
1436 for(sal_uInt32
a(0); a
< nSteps
; a
++, fUnitScale
+= fUnitStep
)
1438 basegfx::B2DHomMatrix aTransform
;
1439 const double fEndScale(rCandidate
.getScaleB() - (fDeltaScale
* fUnitScale
));
1441 if(rCandidate
.isTranslateSet())
1443 const basegfx::B2DVector
aTranslate(
1444 basegfx::interpolate(
1445 rCandidate
.getTranslateB(),
1446 rCandidate
.getTranslateA(),
1449 aTransform
= basegfx::tools::createScaleTranslateB2DHomMatrix(
1457 aTransform
= basegfx::tools::createScaleB2DHomMatrix(
1462 basegfx::B2DPolygon
aNew(basegfx::tools::createPolygonFromUnitCircle());
1464 aNew
.transform(maCurrentTransformation
* aTransform
);
1465 mpOutputDevice
->SetFillColor(Color(basegfx::interpolate(aColorB
, aColorA
, fUnitScale
)));
1466 mpOutputDevice
->DrawPolyPolygon(basegfx::B2DPolyPolygon(aNew
));
1471 void VclProcessor2D::adaptLineToFillDrawMode() const
1473 const DrawModeFlags
nOriginalDrawMode(mpOutputDevice
->GetDrawMode());
1475 if(nOriginalDrawMode
& (DrawModeFlags::BlackLine
|DrawModeFlags::GrayLine
|DrawModeFlags::GhostedLine
|DrawModeFlags::WhiteLine
|DrawModeFlags::SettingsLine
))
1477 DrawModeFlags
nAdaptedDrawMode(nOriginalDrawMode
);
1479 if(nOriginalDrawMode
& DrawModeFlags::BlackLine
)
1481 nAdaptedDrawMode
|= DrawModeFlags::BlackFill
;
1485 nAdaptedDrawMode
&= ~DrawModeFlags::BlackFill
;
1488 if(nOriginalDrawMode
& DrawModeFlags::GrayLine
)
1490 nAdaptedDrawMode
|= DrawModeFlags::GrayFill
;
1494 nAdaptedDrawMode
&= ~DrawModeFlags::GrayFill
;
1497 if(nOriginalDrawMode
& DrawModeFlags::GhostedLine
)
1499 nAdaptedDrawMode
|= DrawModeFlags::GhostedFill
;
1503 nAdaptedDrawMode
&= ~DrawModeFlags::GhostedFill
;
1506 if(nOriginalDrawMode
& DrawModeFlags::WhiteLine
)
1508 nAdaptedDrawMode
|= DrawModeFlags::WhiteFill
;
1512 nAdaptedDrawMode
&= ~DrawModeFlags::WhiteFill
;
1515 if(nOriginalDrawMode
& DrawModeFlags::SettingsLine
)
1517 nAdaptedDrawMode
|= DrawModeFlags::SettingsFill
;
1521 nAdaptedDrawMode
&= ~DrawModeFlags::SettingsFill
;
1524 mpOutputDevice
->SetDrawMode(nAdaptedDrawMode
);
1528 void VclProcessor2D::adaptTextToFillDrawMode() const
1530 const DrawModeFlags
nOriginalDrawMode(mpOutputDevice
->GetDrawMode());
1531 if(nOriginalDrawMode
& (DrawModeFlags::BlackText
|DrawModeFlags::GrayText
|DrawModeFlags::GhostedText
|DrawModeFlags::WhiteText
|DrawModeFlags::SettingsText
))
1533 DrawModeFlags
nAdaptedDrawMode(nOriginalDrawMode
);
1535 if(nOriginalDrawMode
& DrawModeFlags::BlackText
)
1537 nAdaptedDrawMode
|= DrawModeFlags::BlackFill
;
1541 nAdaptedDrawMode
&= ~DrawModeFlags::BlackFill
;
1544 if(nOriginalDrawMode
& DrawModeFlags::GrayText
)
1546 nAdaptedDrawMode
|= DrawModeFlags::GrayFill
;
1550 nAdaptedDrawMode
&= ~DrawModeFlags::GrayFill
;
1553 if(nOriginalDrawMode
& DrawModeFlags::GhostedText
)
1555 nAdaptedDrawMode
|= DrawModeFlags::GhostedFill
;
1559 nAdaptedDrawMode
&= ~DrawModeFlags::GhostedFill
;
1562 if(nOriginalDrawMode
& DrawModeFlags::WhiteText
)
1564 nAdaptedDrawMode
|= DrawModeFlags::WhiteFill
;
1568 nAdaptedDrawMode
&= ~DrawModeFlags::WhiteFill
;
1571 if(nOriginalDrawMode
& DrawModeFlags::SettingsText
)
1573 nAdaptedDrawMode
|= DrawModeFlags::SettingsFill
;
1577 nAdaptedDrawMode
&= ~DrawModeFlags::SettingsFill
;
1580 mpOutputDevice
->SetDrawMode(nAdaptedDrawMode
);
1586 VclProcessor2D::VclProcessor2D(
1587 const geometry::ViewInformation2D
& rViewInformation
,
1588 OutputDevice
& rOutDev
)
1589 : BaseProcessor2D(rViewInformation
),
1590 mpOutputDevice(&rOutDev
),
1591 maBColorModifierStack(),
1592 maCurrentTransformation(),
1593 maDrawinglayerOpt(),
1594 mnPolygonStrokePrimitive2D(0)
1596 // set digit language, derived from SvtCTLOptions to have the correct
1597 // number display for arabic/hindi numerals
1598 rOutDev
.SetDigitLanguage(drawinglayer::detail::getDigitLanguage());
1601 VclProcessor2D::~VclProcessor2D()
1607 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */