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 "vclprocessor2d.hxx"
22 #include "getdigitlanguage.hxx"
23 #include "vclhelperbufferdevice.hxx"
25 #include <comphelper/string.hxx>
26 #include <tools/debug.hxx>
27 #include <vcl/graph.hxx>
28 #include <vcl/outdev.hxx>
29 #include <rtl/ustrbuf.hxx>
30 #include <sal/log.hxx>
31 #include <toolkit/helper/vclunohelper.hxx>
32 #include <basegfx/polygon/b2dpolygontools.hxx>
33 #include <basegfx/polygon/b2dpolypolygontools.hxx>
34 #include <basegfx/polygon/b2dpolygonclipper.hxx>
35 #include <basegfx/color/bcolor.hxx>
36 #include <basegfx/matrix/b2dhommatrixtools.hxx>
37 #include <drawinglayer/primitive2d/textprimitive2d.hxx>
38 #include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx>
39 #include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
40 #include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
41 #include <drawinglayer/primitive2d/fillgraphicprimitive2d.hxx>
42 #include <drawinglayer/primitive2d/PolyPolygonGraphicPrimitive2D.hxx>
43 #include <drawinglayer/primitive2d/maskprimitive2d.hxx>
44 #include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx>
45 #include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
46 #include <drawinglayer/primitive2d/transparenceprimitive2d.hxx>
47 #include <drawinglayer/primitive2d/transformprimitive2d.hxx>
48 #include <drawinglayer/primitive2d/markerarrayprimitive2d.hxx>
49 #include <drawinglayer/primitive2d/pagepreviewprimitive2d.hxx>
50 #include <drawinglayer/primitive2d/textenumsprimitive2d.hxx>
51 #include <drawinglayer/primitive2d/svggradientprimitive2d.hxx>
52 // for support of Title/Description in all apps when embedding pictures
53 #include <drawinglayer/primitive2d/objectinfoprimitive2d.hxx>
55 #include <drawinglayer/primitive2d/textlayoutdevice.hxx>
57 #include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx>
58 #include <drawinglayer/primitive2d/epsprimitive2d.hxx>
60 using namespace com::sun::star
;
64 sal_uInt32
calculateStepsForSvgGradient(const basegfx::BColor
& rColorA
,
65 const basegfx::BColor
& rColorB
, double fDelta
,
68 // use color distance, assume to do every color step
69 sal_uInt32
nSteps(basegfx::fround(rColorA
.getDistance(rColorB
) * 255.0));
73 // calc discrete length to change color each discrete unit (pixel)
74 const sal_uInt32
nDistSteps(basegfx::fround(fDelta
/ fDiscreteUnit
));
76 nSteps
= std::min(nSteps
, nDistSteps
);
79 // reduce quality to 3 discrete units or every 3rd color step for rendering
82 // roughly cut when too big or too small (not full quality, reduce complexity)
83 nSteps
= std::min(nSteps
, sal_uInt32(255));
84 nSteps
= std::max(nSteps
, sal_uInt32(1));
90 namespace drawinglayer::processor2d
94 // directdraw of text simple portion or decorated portion primitive. When decorated, all the extra
95 // information is translated to VCL parameters and set at the font.
96 // Acceptance is restricted to no shearing and positive scaling in X and Y (no font mirroring
98 void VclProcessor2D::RenderTextSimpleOrDecoratedPortionPrimitive2D(
99 const primitive2d::TextSimplePortionPrimitive2D
& rTextCandidate
)
101 // decompose matrix to have position and size of text
102 basegfx::B2DHomMatrix
aLocalTransform(maCurrentTransformation
103 * rTextCandidate
.getTextTransform());
104 basegfx::B2DVector aFontScaling
, aTranslate
;
105 double fRotate
, fShearX
;
106 aLocalTransform
.decompose(aFontScaling
, aTranslate
, fRotate
, fShearX
);
107 bool bPrimitiveAccepted(false);
109 // tdf#95581: Assume tiny shears are rounding artefacts or whatever and can be ignored,
110 // especially if the effect is less than a pixel.
111 if (std::abs(aFontScaling
.getY() * fShearX
) < 1)
113 if (basegfx::fTools::less(aFontScaling
.getX(), 0.0)
114 && basegfx::fTools::less(aFontScaling
.getY(), 0.0))
116 // handle special case: If scale is negative in (x,y) (3rd quadrant), it can
117 // be expressed as rotation by PI. Use this since the Font rendering will not
118 // apply the negative scales in any form
119 aFontScaling
= basegfx::absolute(aFontScaling
);
123 if (basegfx::fTools::more(aFontScaling
.getX(), 0.0)
124 && 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(), aFontScaling
.getX(), aFontScaling
.getY(),
129 fRotate
, rTextCandidate
.getLocale()));
131 // Don't draw fonts without height
132 if (aFont
.GetFontHeight() <= 0)
135 // set FillColor Attribute
136 const Color
aFillColor(rTextCandidate
.getTextFillColor());
137 if (aFillColor
!= COL_TRANSPARENT
)
139 aFont
.SetFillColor(aFillColor
);
140 aFont
.SetTransparent(false);
143 // handle additional font attributes
144 const primitive2d::TextDecoratedPortionPrimitive2D
* pTCPP
145 = dynamic_cast<const primitive2d::TextDecoratedPortionPrimitive2D
*>(
148 if (pTCPP
!= nullptr)
150 // set the color of text decorations
151 const basegfx::BColor aTextlineColor
152 = maBColorModifierStack
.getModifiedColor(pTCPP
->getTextlineColor());
153 mpOutputDevice
->SetTextLineColor(Color(aTextlineColor
));
155 // set Overline attribute
156 const FontLineStyle
eFontOverline(
157 primitive2d::mapTextLineToFontLineStyle(pTCPP
->getFontOverline()));
158 if (eFontOverline
!= LINESTYLE_NONE
)
160 aFont
.SetOverline(eFontOverline
);
161 const basegfx::BColor aOverlineColor
162 = 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(
170 primitive2d::mapTextLineToFontLineStyle(pTCPP
->getFontUnderline()));
171 if (eFontLineStyle
!= LINESTYLE_NONE
)
173 aFont
.SetUnderline(eFontLineStyle
);
174 if (pTCPP
->getWordLineMode())
175 aFont
.SetWordLineMode(true);
178 // set Strikeout attribute
179 const FontStrikeout
eFontStrikeout(
180 primitive2d::mapTextStrikeoutToFontStrikeout(pTCPP
->getTextStrikeout()));
182 if (eFontStrikeout
!= STRIKEOUT_NONE
)
183 aFont
.SetStrikeout(eFontStrikeout
);
185 // set EmphasisMark attribute
186 FontEmphasisMark eFontEmphasisMark
= FontEmphasisMark::NONE
;
187 switch (pTCPP
->getTextEmphasisMark())
190 SAL_WARN("drawinglayer",
191 "Unknown EmphasisMark style " << pTCPP
->getTextEmphasisMark());
193 case primitive2d::TEXT_FONT_EMPHASIS_MARK_NONE
:
194 eFontEmphasisMark
= FontEmphasisMark::NONE
;
196 case primitive2d::TEXT_FONT_EMPHASIS_MARK_DOT
:
197 eFontEmphasisMark
= FontEmphasisMark::Dot
;
199 case primitive2d::TEXT_FONT_EMPHASIS_MARK_CIRCLE
:
200 eFontEmphasisMark
= FontEmphasisMark::Circle
;
202 case primitive2d::TEXT_FONT_EMPHASIS_MARK_DISC
:
203 eFontEmphasisMark
= FontEmphasisMark::Disc
;
205 case primitive2d::TEXT_FONT_EMPHASIS_MARK_ACCENT
:
206 eFontEmphasisMark
= FontEmphasisMark::Accent
;
210 if (eFontEmphasisMark
!= FontEmphasisMark::NONE
)
212 DBG_ASSERT((pTCPP
->getEmphasisMarkAbove() != pTCPP
->getEmphasisMarkBelow()),
213 "DrawingLayer: Bad EmphasisMark position!");
214 if (pTCPP
->getEmphasisMarkAbove())
215 eFontEmphasisMark
|= FontEmphasisMark::PosAbove
;
217 eFontEmphasisMark
|= FontEmphasisMark::PosBelow
;
218 aFont
.SetEmphasisMark(eFontEmphasisMark
);
221 // set Relief attribute
222 FontRelief eFontRelief
= FontRelief::NONE
;
223 switch (pTCPP
->getTextRelief())
226 SAL_WARN("drawinglayer", "Unknown Relief style " << pTCPP
->getTextRelief());
228 case primitive2d::TEXT_RELIEF_NONE
:
229 eFontRelief
= FontRelief::NONE
;
231 case primitive2d::TEXT_RELIEF_EMBOSSED
:
232 eFontRelief
= FontRelief::Embossed
;
234 case primitive2d::TEXT_RELIEF_ENGRAVED
:
235 eFontRelief
= FontRelief::Engraved
;
239 if (eFontRelief
!= FontRelief::NONE
)
240 aFont
.SetRelief(eFontRelief
);
242 // set Shadow attribute
243 if (pTCPP
->getShadow())
244 aFont
.SetShadow(true);
247 // create transformed integer DXArray in view coordinate system
248 std::vector
<tools::Long
> aTransformedDXArray
;
250 if (!rTextCandidate
.getDXArray().empty())
252 aTransformedDXArray
.reserve(rTextCandidate
.getDXArray().size());
253 const basegfx::B2DVector
aPixelVector(maCurrentTransformation
254 * basegfx::B2DVector(1.0, 0.0));
255 const double fPixelVectorFactor(aPixelVector
.getLength());
257 for (auto const& elem
: rTextCandidate
.getDXArray())
259 aTransformedDXArray
.push_back(basegfx::fround(elem
* fPixelVectorFactor
));
263 // set parameters and paint text snippet
264 const basegfx::BColor
aRGBFontColor(
265 maBColorModifierStack
.getModifiedColor(rTextCandidate
.getFontColor()));
266 const basegfx::B2DPoint
aPoint(aLocalTransform
* basegfx::B2DPoint(0.0, 0.0));
267 const Point
aStartPoint(basegfx::fround(aPoint
.getX()), basegfx::fround(aPoint
.getY()));
268 const ComplexTextLayoutFlags
nOldLayoutMode(mpOutputDevice
->GetLayoutMode());
270 if (rTextCandidate
.getFontAttribute().getRTL())
272 ComplexTextLayoutFlags
nRTLLayoutMode(nOldLayoutMode
273 & ~ComplexTextLayoutFlags::BiDiStrong
);
275 |= ComplexTextLayoutFlags::BiDiRtl
| ComplexTextLayoutFlags::TextOriginLeft
;
276 mpOutputDevice
->SetLayoutMode(nRTLLayoutMode
);
279 mpOutputDevice
->SetFont(aFont
);
280 mpOutputDevice
->SetTextColor(Color(aRGBFontColor
));
282 OUString
aText(rTextCandidate
.getText());
283 sal_Int32 nPos
= rTextCandidate
.getTextPosition();
284 sal_Int32 nLen
= rTextCandidate
.getTextLength();
286 tools::Long
* pDXArray
287 = !aTransformedDXArray
.empty() ? aTransformedDXArray
.data() : nullptr;
289 if (rTextCandidate
.isFilled())
291 basegfx::B2DVector aOldFontScaling
, aOldTranslate
;
292 double fOldRotate
, fOldShearX
;
293 rTextCandidate
.getTextTransform().decompose(aOldFontScaling
, aOldTranslate
,
294 fOldRotate
, fOldShearX
);
296 tools::Long nWidthToFill
= static_cast<tools::Long
>(
297 rTextCandidate
.getWidthToFill() * aFontScaling
.getX() / aOldFontScaling
.getX());
300 = mpOutputDevice
->GetTextArray(rTextCandidate
.getText(), pDXArray
, 0, 1);
301 tools::Long nChars
= 2;
303 nChars
= nWidthToFill
/ nWidth
;
305 OUStringBuffer aFilled
;
306 comphelper::string::padToLength(aFilled
, nChars
, aText
[0]);
307 aText
= aFilled
.makeStringAndClear();
312 if (!aTransformedDXArray
.empty())
314 mpOutputDevice
->DrawTextArray(aStartPoint
, aText
, pDXArray
, nPos
, nLen
);
318 mpOutputDevice
->DrawText(aStartPoint
, aText
, nPos
, nLen
);
321 if (rTextCandidate
.getFontAttribute().getRTL())
323 mpOutputDevice
->SetLayoutMode(nOldLayoutMode
);
326 bPrimitiveAccepted
= true;
330 if (!bPrimitiveAccepted
)
333 process(rTextCandidate
);
337 // direct draw of hairline
338 void VclProcessor2D::RenderPolygonHairlinePrimitive2D(
339 const primitive2d::PolygonHairlinePrimitive2D
& rPolygonCandidate
, bool bPixelBased
)
341 const basegfx::BColor
aHairlineColor(
342 maBColorModifierStack
.getModifiedColor(rPolygonCandidate
.getBColor()));
343 mpOutputDevice
->SetLineColor(Color(aHairlineColor
));
344 mpOutputDevice
->SetFillColor();
346 basegfx::B2DPolygon
aLocalPolygon(rPolygonCandidate
.getB2DPolygon());
347 aLocalPolygon
.transform(maCurrentTransformation
);
349 if (bPixelBased
&& getOptionsDrawinglayer().IsAntiAliasing()
350 && getOptionsDrawinglayer().IsSnapHorVerLinesToDiscrete())
353 // when a Hairline is painted and AntiAliasing is on the option SnapHorVerLinesToDiscrete
354 // allows to suppress AntiAliasing for pure horizontal or vertical lines. This is done since
355 // not-AntiAliased such lines look more pleasing to the eye (e.g. 2D chart content). This
356 // NEEDS to be done in discrete coordinates, so only useful for pixel based rendering.
357 aLocalPolygon
= basegfx::utils::snapPointsOfHorizontalOrVerticalEdges(aLocalPolygon
);
360 mpOutputDevice
->DrawPolyLine(aLocalPolygon
, 0.0);
363 // direct draw of transformed BitmapEx primitive
364 void VclProcessor2D::RenderBitmapPrimitive2D(const primitive2d::BitmapPrimitive2D
& rBitmapCandidate
)
366 BitmapEx
aBitmapEx(VCLUnoHelper::GetBitmap(rBitmapCandidate
.getXBitmap()));
367 const basegfx::B2DHomMatrix
aLocalTransform(maCurrentTransformation
368 * rBitmapCandidate
.getTransform());
370 if (maBColorModifierStack
.count())
372 aBitmapEx
= aBitmapEx
.ModifyBitmapEx(maBColorModifierStack
);
374 if (aBitmapEx
.IsEmpty())
376 // color gets completely replaced, get it
377 const basegfx::BColor
aModifiedColor(
378 maBColorModifierStack
.getModifiedColor(basegfx::BColor()));
379 basegfx::B2DPolygon
aPolygon(basegfx::utils::createUnitPolygon());
380 aPolygon
.transform(aLocalTransform
);
382 mpOutputDevice
->SetFillColor(Color(aModifiedColor
));
383 mpOutputDevice
->SetLineColor();
384 mpOutputDevice
->DrawPolygon(aPolygon
);
390 // #122923# do no longer add Alpha channel here; the right place to do this is when really
391 // the own transformer is used (see OutputDevice::DrawTransformedBitmapEx).
393 // draw using OutputDevice'sDrawTransformedBitmapEx
394 mpOutputDevice
->DrawTransformedBitmapEx(aLocalTransform
, aBitmapEx
);
397 void VclProcessor2D::RenderFillGraphicPrimitive2D(
398 const primitive2d::FillGraphicPrimitive2D
& rFillBitmapCandidate
)
400 const attribute::FillGraphicAttribute
& rFillGraphicAttribute(
401 rFillBitmapCandidate
.getFillGraphic());
402 bool bPrimitiveAccepted(false);
404 // #121194# when tiling is used and content is bitmap-based, do direct tiling in the
405 // renderer on pixel base to ensure tight fitting. Do not do this when
406 // the fill is rotated or sheared.
407 if (rFillGraphicAttribute
.getTiling())
409 // content is bitmap(ex)
411 // for Vector Graphic Data (SVG, EMF+) support, force decomposition when present. This will lead to use
412 // the primitive representation of the vector data directly.
414 // when graphic is animated, force decomposition to use the correct graphic, else
415 // fill style will not be animated
416 if (GraphicType::Bitmap
== rFillGraphicAttribute
.getGraphic().GetType()
417 && !rFillGraphicAttribute
.getGraphic().getVectorGraphicData()
418 && !rFillGraphicAttribute
.getGraphic().IsAnimated())
420 // decompose matrix to check for shear, rotate and mirroring
421 basegfx::B2DHomMatrix
aLocalTransform(maCurrentTransformation
422 * rFillBitmapCandidate
.getTransformation());
423 basegfx::B2DVector aScale
, aTranslate
;
424 double fRotate
, fShearX
;
425 aLocalTransform
.decompose(aScale
, aTranslate
, fRotate
, fShearX
);
427 // when nopt rotated/sheared
428 if (basegfx::fTools::equalZero(fRotate
) && basegfx::fTools::equalZero(fShearX
))
430 // no shear or rotate, draw direct in pixel coordinates
431 bPrimitiveAccepted
= true;
433 // transform object range to device coordinates (pixels). Use
434 // the device transformation for better accuracy
435 basegfx::B2DRange
aObjectRange(aTranslate
, aTranslate
+ aScale
);
436 aObjectRange
.transform(mpOutputDevice
->GetViewTransformation());
438 // extract discrete size of object
439 const sal_Int32
nOWidth(basegfx::fround(aObjectRange
.getWidth()));
440 const sal_Int32
nOHeight(basegfx::fround(aObjectRange
.getHeight()));
442 // only do something when object has a size in discrete units
443 if (nOWidth
> 0 && nOHeight
> 0)
445 // transform graphic range to device coordinates (pixels). Use
446 // the device transformation for better accuracy
447 basegfx::B2DRange
aGraphicRange(rFillGraphicAttribute
.getGraphicRange());
448 aGraphicRange
.transform(mpOutputDevice
->GetViewTransformation()
451 // extract discrete size of graphic
452 // caution: when getting to zero, nothing would be painted; thus, do not allow this
453 const sal_Int32
nBWidth(
454 std::max(sal_Int32(1), basegfx::fround(aGraphicRange
.getWidth())));
455 const sal_Int32
nBHeight(
456 std::max(sal_Int32(1), basegfx::fround(aGraphicRange
.getHeight())));
458 // only do something when bitmap fill has a size in discrete units
459 if (nBWidth
> 0 && nBHeight
> 0)
461 // nBWidth, nBHeight is the pixel size of the needed bitmap. To not need to scale it
462 // in vcl many times, create a size-optimized version
463 const Size
aNeededBitmapSizePixel(nBWidth
, nBHeight
);
464 BitmapEx
aBitmapEx(rFillGraphicAttribute
.getGraphic().GetBitmapEx());
465 const bool bPreScaled(nBWidth
* nBHeight
< (250 * 250));
467 // ... but only up to a maximum size, else it gets too expensive
470 // if color depth is below 24bit, expand before scaling for better quality.
471 // This is even needed for low colors, else the scale will produce
472 // a bitmap in gray or Black/White (!)
473 if (aBitmapEx
.GetBitCount() < 24)
475 aBitmapEx
.Convert(BmpConversion::N24Bit
);
478 aBitmapEx
.Scale(aNeededBitmapSizePixel
, BmpScaleFlag::Interpolate
);
481 bool bPainted(false);
483 if (maBColorModifierStack
.count())
485 // when color modifier, apply to bitmap
486 aBitmapEx
= aBitmapEx
.ModifyBitmapEx(maBColorModifierStack
);
488 // impModifyBitmapEx uses empty bitmap as sign to return that
489 // the content will be completely replaced to mono color, use shortcut
490 if (aBitmapEx
.IsEmpty())
492 // color gets completely replaced, get it
493 const basegfx::BColor
aModifiedColor(
494 maBColorModifierStack
.getModifiedColor(basegfx::BColor()));
495 basegfx::B2DPolygon
aPolygon(basegfx::utils::createUnitPolygon());
496 aPolygon
.transform(aLocalTransform
);
498 mpOutputDevice
->SetFillColor(Color(aModifiedColor
));
499 mpOutputDevice
->SetLineColor();
500 mpOutputDevice
->DrawPolygon(aPolygon
);
508 sal_Int32
nBLeft(basegfx::fround(aGraphicRange
.getMinX()));
509 sal_Int32
nBTop(basegfx::fround(aGraphicRange
.getMinY()));
510 const sal_Int32
nOLeft(basegfx::fround(aObjectRange
.getMinX()));
511 const sal_Int32
nOTop(basegfx::fround(aObjectRange
.getMinY()));
517 const sal_Int32
nDiff((nBLeft
/ nBWidth
) + 1);
520 nBLeft
-= nDiff
* nBWidth
;
523 if (nBLeft
+ nBWidth
<= nOLeft
)
525 const sal_Int32
nDiff(-nBLeft
/ nBWidth
);
528 nBLeft
+= nDiff
* nBWidth
;
533 const sal_Int32
nDiff((nBTop
/ nBHeight
) + 1);
536 nBTop
-= nDiff
* nBHeight
;
539 if (nBTop
+ nBHeight
<= nOTop
)
541 const sal_Int32
nDiff(-nBTop
/ nBHeight
);
544 nBTop
+= nDiff
* nBHeight
;
548 const Point
aEmptyPoint(0, 0);
549 const ::tools::Rectangle
aVisiblePixel(
550 aEmptyPoint
, mpOutputDevice
->GetOutputSizePixel());
551 const bool bWasEnabled(mpOutputDevice
->IsMapModeEnabled());
552 mpOutputDevice
->EnableMapMode(false);
554 // check if offset is used
555 const sal_Int32
nOffsetX(
556 basegfx::fround(rFillGraphicAttribute
.getOffsetX() * nBWidth
));
560 // offset in X, so iterate over Y first and draw lines
561 for (sal_Int32
nYPos(nBTop
); nYPos
< nOTop
+ nOHeight
;
562 nYPos
+= nBHeight
, nPosY
++)
564 for (sal_Int32
nXPos((nPosY
% 2) ? nBLeft
- nBWidth
+ nOffsetX
566 nXPos
< nOLeft
+ nOWidth
; nXPos
+= nBWidth
)
568 const ::tools::Rectangle
aOutRectPixel(
569 Point(nXPos
, nYPos
), aNeededBitmapSizePixel
);
571 if (aOutRectPixel
.IsOver(aVisiblePixel
))
575 mpOutputDevice
->DrawBitmapEx(
576 aOutRectPixel
.TopLeft(), aBitmapEx
);
580 mpOutputDevice
->DrawBitmapEx(
581 aOutRectPixel
.TopLeft(), aNeededBitmapSizePixel
,
590 // check if offset is used
591 const sal_Int32
nOffsetY(
592 basegfx::fround(rFillGraphicAttribute
.getOffsetY() * nBHeight
));
594 // possible offset in Y, so iterate over X first and draw columns
595 for (sal_Int32
nXPos(nBLeft
); nXPos
< nOLeft
+ nOWidth
;
596 nXPos
+= nBWidth
, nPosX
++)
598 for (sal_Int32
nYPos((nPosX
% 2) ? nBTop
- nBHeight
+ nOffsetY
600 nYPos
< nOTop
+ nOHeight
; nYPos
+= nBHeight
)
602 const ::tools::Rectangle
aOutRectPixel(
603 Point(nXPos
, nYPos
), aNeededBitmapSizePixel
);
605 if (aOutRectPixel
.IsOver(aVisiblePixel
))
609 mpOutputDevice
->DrawBitmapEx(
610 aOutRectPixel
.TopLeft(), aBitmapEx
);
614 mpOutputDevice
->DrawBitmapEx(
615 aOutRectPixel
.TopLeft(), aNeededBitmapSizePixel
,
624 mpOutputDevice
->EnableMapMode(bWasEnabled
);
632 if (!bPrimitiveAccepted
)
634 // do not accept, use decomposition
635 process(rFillBitmapCandidate
);
639 // direct draw of Graphic
640 void VclProcessor2D::RenderPolyPolygonGraphicPrimitive2D(
641 const primitive2d::PolyPolygonGraphicPrimitive2D
& rPolygonCandidate
)
644 const basegfx::B2DPolyPolygon
& rPolyPolygon
= rPolygonCandidate
.getB2DPolyPolygon();
646 // #121194# Todo: check if this works
647 if (!rPolyPolygon
.count())
649 // empty polyPolygon, done
654 const attribute::FillGraphicAttribute
& rFillGraphicAttribute
655 = rPolygonCandidate
.getFillGraphic();
657 // try to catch cases where the graphic will be color-modified to a single
658 // color (e.g. shadow)
659 switch (rFillGraphicAttribute
.getGraphic().GetType())
661 case GraphicType::GdiMetafile
:
663 // metafiles are potentially transparent, cannot optimize, not done
666 case GraphicType::Bitmap
:
668 if (!rFillGraphicAttribute
.getGraphic().IsTransparent()
669 && !rFillGraphicAttribute
.getGraphic().IsAlpha())
671 // bitmap is not transparent and has no alpha
672 const sal_uInt32
nBColorModifierStackCount(maBColorModifierStack
.count());
674 if (nBColorModifierStackCount
)
676 const basegfx::BColorModifierSharedPtr
& rTopmostModifier
677 = maBColorModifierStack
.getBColorModifier(nBColorModifierStackCount
679 const basegfx::BColorModifier_replace
* pReplacer
680 = dynamic_cast<const basegfx::BColorModifier_replace
*>(
681 rTopmostModifier
.get());
685 // the bitmap fill is in unified color, so we can replace it with
686 // a single polygon fill. The form of the fill depends on tiling
687 if (rFillGraphicAttribute
.getTiling())
689 // with tiling, fill the whole tools::PolyPolygon with the modifier color
690 basegfx::B2DPolyPolygon
aLocalPolyPolygon(rPolyPolygon
);
692 aLocalPolyPolygon
.transform(maCurrentTransformation
);
693 mpOutputDevice
->SetLineColor();
694 mpOutputDevice
->SetFillColor(Color(pReplacer
->getBColor()));
695 mpOutputDevice
->DrawPolyPolygon(aLocalPolyPolygon
);
699 // without tiling, only the area common to the bitmap tile and the
700 // tools::PolyPolygon is filled. Create the bitmap tile area in object
701 // coordinates. For this, the object transformation needs to be created
702 // from the already scaled PolyPolygon. The tile area in object
703 // coordinates will always be non-rotated, so it's not necessary to
704 // work with a polygon here
705 basegfx::B2DRange
aTileRange(
706 rFillGraphicAttribute
.getGraphicRange());
707 const basegfx::B2DRange
aPolyPolygonRange(
708 rPolyPolygon
.getB2DRange());
709 const basegfx::B2DHomMatrix
aNewObjectTransform(
710 basegfx::utils::createScaleTranslateB2DHomMatrix(
711 aPolyPolygonRange
.getRange(),
712 aPolyPolygonRange
.getMinimum()));
714 aTileRange
.transform(aNewObjectTransform
);
716 // now clip the object polyPolygon against the tile range
717 // to get the common area
718 basegfx::B2DPolyPolygon aTarget
719 = basegfx::utils::clipPolyPolygonOnRange(
720 rPolyPolygon
, aTileRange
, true, false);
724 aTarget
.transform(maCurrentTransformation
);
725 mpOutputDevice
->SetLineColor();
726 mpOutputDevice
->SetFillColor(Color(pReplacer
->getBColor()));
727 mpOutputDevice
->DrawPolyPolygon(aTarget
);
731 // simplified output executed, we are done
738 default: //GraphicType::NONE, GraphicType::Default
740 // empty graphic, we are done
749 // use default decomposition
750 process(rPolygonCandidate
);
755 void VclProcessor2D::RenderMaskPrimitive2DPixel(const primitive2d::MaskPrimitive2D
& rMaskCandidate
)
757 if (rMaskCandidate
.getChildren().empty())
760 basegfx::B2DPolyPolygon
aMask(rMaskCandidate
.getMask());
765 aMask
.transform(maCurrentTransformation
);
767 // Unless smooth edges are needed, simply use clipping.
768 if (basegfx::utils::isRectangle(aMask
) || !getOptionsDrawinglayer().IsAntiAliasing())
770 mpOutputDevice
->Push(PushFlags::CLIPREGION
);
771 mpOutputDevice
->IntersectClipRegion(vcl::Region(aMask
));
772 process(rMaskCandidate
.getChildren());
773 mpOutputDevice
->Pop();
777 const basegfx::B2DRange
aRange(basegfx::utils::getRange(aMask
));
778 impBufferDevice
aBufferDevice(*mpOutputDevice
, aRange
);
780 if (!aBufferDevice
.isVisible())
783 // remember last OutDev and set to content
784 OutputDevice
* pLastOutputDevice
= mpOutputDevice
;
785 mpOutputDevice
= &aBufferDevice
.getContent();
788 process(rMaskCandidate
.getChildren());
790 // back to old OutDev
791 mpOutputDevice
= pLastOutputDevice
;
794 VirtualDevice
& rMask
= aBufferDevice
.getTransparence();
795 rMask
.SetLineColor();
796 rMask
.SetFillColor(COL_BLACK
);
797 rMask
.DrawPolyPolygon(aMask
);
799 // dump buffer to outdev
800 aBufferDevice
.paint();
803 // modified color group. Force output to unified color.
804 void VclProcessor2D::RenderModifiedColorPrimitive2D(
805 const primitive2d::ModifiedColorPrimitive2D
& rModifiedCandidate
)
807 if (!rModifiedCandidate
.getChildren().empty())
809 maBColorModifierStack
.push(rModifiedCandidate
.getColorModifier());
810 process(rModifiedCandidate
.getChildren());
811 maBColorModifierStack
.pop();
815 // unified sub-transparence. Draw to VDev first.
816 void VclProcessor2D::RenderUnifiedTransparencePrimitive2D(
817 const primitive2d::UnifiedTransparencePrimitive2D
& rTransCandidate
)
819 if (rTransCandidate
.getChildren().empty())
822 if (0.0 == rTransCandidate
.getTransparence())
824 // no transparence used, so just use the content
825 process(rTransCandidate
.getChildren());
827 else if (rTransCandidate
.getTransparence() > 0.0 && rTransCandidate
.getTransparence() < 1.0)
829 // transparence is in visible range
830 basegfx::B2DRange
aRange(rTransCandidate
.getChildren().getB2DRange(getViewInformation2D()));
831 aRange
.transform(maCurrentTransformation
);
832 impBufferDevice
aBufferDevice(*mpOutputDevice
, aRange
);
834 if (aBufferDevice
.isVisible())
836 // remember last OutDev and set to content
837 OutputDevice
* pLastOutputDevice
= mpOutputDevice
;
838 mpOutputDevice
= &aBufferDevice
.getContent();
840 // paint content to it
841 process(rTransCandidate
.getChildren());
843 // back to old OutDev
844 mpOutputDevice
= pLastOutputDevice
;
846 // dump buffer to outdev using given transparence
847 aBufferDevice
.paint(rTransCandidate
.getTransparence());
852 // sub-transparence group. Draw to VDev first.
853 void VclProcessor2D::RenderTransparencePrimitive2D(
854 const primitive2d::TransparencePrimitive2D
& rTransCandidate
)
856 if (rTransCandidate
.getChildren().empty())
859 basegfx::B2DRange
aRange(rTransCandidate
.getChildren().getB2DRange(getViewInformation2D()));
860 aRange
.transform(maCurrentTransformation
);
861 impBufferDevice
aBufferDevice(*mpOutputDevice
, aRange
);
863 if (!aBufferDevice
.isVisible())
866 // remember last OutDev and set to content
867 OutputDevice
* pLastOutputDevice
= mpOutputDevice
;
868 mpOutputDevice
= &aBufferDevice
.getContent();
870 // paint content to it
871 process(rTransCandidate
.getChildren());
874 mpOutputDevice
= &aBufferDevice
.getTransparence();
876 // when painting transparence masks, reset the color stack
877 basegfx::BColorModifierStack
aLastBColorModifierStack(maBColorModifierStack
);
878 maBColorModifierStack
= basegfx::BColorModifierStack();
880 // paint mask to it (always with transparence intensities, evtl. with AA)
881 process(rTransCandidate
.getTransparence());
883 // back to old color stack
884 maBColorModifierStack
= aLastBColorModifierStack
;
886 // back to old OutDev
887 mpOutputDevice
= pLastOutputDevice
;
889 // dump buffer to outdev
890 aBufferDevice
.paint();
894 void VclProcessor2D::RenderTransformPrimitive2D(
895 const primitive2d::TransformPrimitive2D
& rTransformCandidate
)
897 // remember current transformation and ViewInformation
898 const basegfx::B2DHomMatrix
aLastCurrentTransformation(maCurrentTransformation
);
899 const geometry::ViewInformation2D
aLastViewInformation2D(getViewInformation2D());
901 // create new transformations for CurrentTransformation
902 // and for local ViewInformation2D
903 maCurrentTransformation
= maCurrentTransformation
* rTransformCandidate
.getTransformation();
904 const geometry::ViewInformation2D
aViewInformation2D(
905 getViewInformation2D().getObjectTransformation() * rTransformCandidate
.getTransformation(),
906 getViewInformation2D().getViewTransformation(), getViewInformation2D().getViewport(),
907 getViewInformation2D().getVisualizedPage(), getViewInformation2D().getViewTime(),
908 getViewInformation2D().getExtendedInformationSequence());
909 updateViewInformation(aViewInformation2D
);
912 process(rTransformCandidate
.getChildren());
914 // restore transformations
915 maCurrentTransformation
= aLastCurrentTransformation
;
916 updateViewInformation(aLastViewInformation2D
);
919 // new XDrawPage for ViewInformation2D
920 void VclProcessor2D::RenderPagePreviewPrimitive2D(
921 const primitive2d::PagePreviewPrimitive2D
& rPagePreviewCandidate
)
923 // remember current transformation and ViewInformation
924 const geometry::ViewInformation2D
aLastViewInformation2D(getViewInformation2D());
926 // create new local ViewInformation2D
927 const geometry::ViewInformation2D
aViewInformation2D(
928 getViewInformation2D().getObjectTransformation(),
929 getViewInformation2D().getViewTransformation(), getViewInformation2D().getViewport(),
930 rPagePreviewCandidate
.getXDrawPage(), getViewInformation2D().getViewTime(),
931 getViewInformation2D().getExtendedInformationSequence());
932 updateViewInformation(aViewInformation2D
);
934 // process decomposed content
935 process(rPagePreviewCandidate
);
937 // restore transformations
938 updateViewInformation(aLastViewInformation2D
);
942 void VclProcessor2D::RenderMarkerArrayPrimitive2D(
943 const primitive2d::MarkerArrayPrimitive2D
& rMarkArrayCandidate
)
946 const std::vector
<basegfx::B2DPoint
>& rPositions
= rMarkArrayCandidate
.getPositions();
947 const sal_uInt32
nCount(rPositions
.size());
949 if (!nCount
|| rMarkArrayCandidate
.getMarker().IsEmpty())
953 const BitmapEx
& rMarker(rMarkArrayCandidate
.getMarker());
954 const Size
aBitmapSize(rMarker
.GetSizePixel());
956 if (!(aBitmapSize
.Width() && aBitmapSize
.Height()))
959 // get discrete half size
960 const basegfx::B2DVector
aDiscreteHalfSize((aBitmapSize
.getWidth() - 1.0) * 0.5,
961 (aBitmapSize
.getHeight() - 1.0) * 0.5);
962 const bool bWasEnabled(mpOutputDevice
->IsMapModeEnabled());
964 // do not forget evtl. moved origin in target device MapMode when
965 // switching it off; it would be missing and lead to wrong positions.
966 // All his could be done using logic sizes and coordinates, too, but
967 // we want a 1:1 bitmap rendering here, so it's more safe and faster
968 // to work with switching off MapMode usage completely.
969 const Point
aOrigin(mpOutputDevice
->GetMapMode().GetOrigin());
971 mpOutputDevice
->EnableMapMode(false);
973 for (auto const& pos
: rPositions
)
975 const basegfx::B2DPoint
aDiscreteTopLeft((maCurrentTransformation
* pos
)
976 - aDiscreteHalfSize
);
977 const Point
aDiscretePoint(basegfx::fround(aDiscreteTopLeft
.getX()),
978 basegfx::fround(aDiscreteTopLeft
.getY()));
980 mpOutputDevice
->DrawBitmapEx(aDiscretePoint
+ aOrigin
, rMarker
);
983 mpOutputDevice
->EnableMapMode(bWasEnabled
);
987 void VclProcessor2D::RenderPointArrayPrimitive2D(
988 const primitive2d::PointArrayPrimitive2D
& rPointArrayCandidate
)
990 const std::vector
<basegfx::B2DPoint
>& rPositions
= rPointArrayCandidate
.getPositions();
991 const basegfx::BColor
aRGBColor(
992 maBColorModifierStack
.getModifiedColor(rPointArrayCandidate
.getRGBColor()));
993 const Color
aVCLColor(aRGBColor
);
995 for (auto const& pos
: rPositions
)
997 const basegfx::B2DPoint
aViewPosition(maCurrentTransformation
* pos
);
998 const Point
aPos(basegfx::fround(aViewPosition
.getX()),
999 basegfx::fround(aViewPosition
.getY()));
1001 mpOutputDevice
->DrawPixel(aPos
, aVCLColor
);
1005 void VclProcessor2D::RenderPolygonStrokePrimitive2D(
1006 const primitive2d::PolygonStrokePrimitive2D
& rPolygonStrokeCandidate
)
1008 // #i101491# method restructured to clearly use the DrawPolyLine
1009 // calls starting from a defined line width
1010 const attribute::LineAttribute
& rLineAttribute
= rPolygonStrokeCandidate
.getLineAttribute();
1011 const double fLineWidth(rLineAttribute
.getWidth());
1014 if (basegfx::fTools::more(fLineWidth
, 0.0))
1016 const basegfx::B2DVector
aDiscreteUnit(maCurrentTransformation
1017 * basegfx::B2DVector(fLineWidth
, 0.0));
1018 const double fDiscreteLineWidth(aDiscreteUnit
.getLength());
1019 const attribute::StrokeAttribute
& rStrokeAttribute
1020 = rPolygonStrokeCandidate
.getStrokeAttribute();
1021 const basegfx::BColor
aHairlineColor(
1022 maBColorModifierStack
.getModifiedColor(rLineAttribute
.getColor()));
1023 basegfx::B2DPolyPolygon aHairlinePolyPolygon
;
1025 mpOutputDevice
->SetLineColor(Color(aHairlineColor
));
1026 mpOutputDevice
->SetFillColor();
1028 if (0.0 == rStrokeAttribute
.getFullDotDashLen())
1030 // no line dashing, just copy
1031 aHairlinePolyPolygon
.append(rPolygonStrokeCandidate
.getB2DPolygon());
1035 // else apply LineStyle
1036 basegfx::utils::applyLineDashing(
1037 rPolygonStrokeCandidate
.getB2DPolygon(), rStrokeAttribute
.getDotDashArray(),
1038 &aHairlinePolyPolygon
, nullptr, rStrokeAttribute
.getFullDotDashLen());
1041 const sal_uInt32
nCount(aHairlinePolyPolygon
.count());
1045 const bool bAntiAliased(getOptionsDrawinglayer().IsAntiAliasing());
1046 aHairlinePolyPolygon
.transform(maCurrentTransformation
);
1050 if (basegfx::fTools::lessOrEqual(fDiscreteLineWidth
, 1.0))
1052 // line in range ]0.0 .. 1.0[
1053 // paint as simple hairline
1054 for (sal_uInt32
a(0); a
< nCount
; a
++)
1056 mpOutputDevice
->DrawPolyLine(aHairlinePolyPolygon
.getB2DPolygon(a
), 0.0);
1061 else if (basegfx::fTools::lessOrEqual(fDiscreteLineWidth
, 2.0))
1063 // line in range [1.0 .. 2.0[
1064 // paint as 2x2 with dynamic line distance
1065 basegfx::B2DHomMatrix aMat
;
1066 const double fDistance(fDiscreteLineWidth
- 1.0);
1067 const double fHalfDistance(fDistance
* 0.5);
1069 for (sal_uInt32
a(0); a
< nCount
; a
++)
1071 basegfx::B2DPolygon
aCandidate(aHairlinePolyPolygon
.getB2DPolygon(a
));
1073 aMat
.set(0, 2, -fHalfDistance
);
1074 aMat
.set(1, 2, -fHalfDistance
);
1075 aCandidate
.transform(aMat
);
1076 mpOutputDevice
->DrawPolyLine(aCandidate
, 0.0);
1078 aMat
.set(0, 2, fDistance
);
1079 aMat
.set(1, 2, 0.0);
1080 aCandidate
.transform(aMat
);
1081 mpOutputDevice
->DrawPolyLine(aCandidate
, 0.0);
1083 aMat
.set(0, 2, 0.0);
1084 aMat
.set(1, 2, fDistance
);
1085 aCandidate
.transform(aMat
);
1086 mpOutputDevice
->DrawPolyLine(aCandidate
, 0.0);
1088 aMat
.set(0, 2, -fDistance
);
1089 aMat
.set(1, 2, 0.0);
1090 aCandidate
.transform(aMat
);
1091 mpOutputDevice
->DrawPolyLine(aCandidate
, 0.0);
1096 else if (basegfx::fTools::lessOrEqual(fDiscreteLineWidth
, 3.0))
1098 // line in range [2.0 .. 3.0]
1099 // paint as cross in a 3x3 with dynamic line distance
1100 basegfx::B2DHomMatrix aMat
;
1101 const double fDistance((fDiscreteLineWidth
- 1.0) * 0.5);
1103 for (sal_uInt32
a(0); a
< nCount
; a
++)
1105 basegfx::B2DPolygon
aCandidate(aHairlinePolyPolygon
.getB2DPolygon(a
));
1107 mpOutputDevice
->DrawPolyLine(aCandidate
, 0.0);
1109 aMat
.set(0, 2, -fDistance
);
1110 aMat
.set(1, 2, 0.0);
1111 aCandidate
.transform(aMat
);
1112 mpOutputDevice
->DrawPolyLine(aCandidate
, 0.0);
1114 aMat
.set(0, 2, fDistance
);
1115 aMat
.set(1, 2, -fDistance
);
1116 aCandidate
.transform(aMat
);
1117 mpOutputDevice
->DrawPolyLine(aCandidate
, 0.0);
1119 aMat
.set(0, 2, fDistance
);
1120 aMat
.set(1, 2, fDistance
);
1121 aCandidate
.transform(aMat
);
1122 mpOutputDevice
->DrawPolyLine(aCandidate
, 0.0);
1124 aMat
.set(0, 2, -fDistance
);
1125 aMat
.set(1, 2, fDistance
);
1126 aCandidate
.transform(aMat
);
1127 mpOutputDevice
->DrawPolyLine(aCandidate
, 0.0);
1134 // #i101491# line width above 3.0
1139 if (basegfx::fTools::lessOrEqual(fDiscreteLineWidth
, 1.5))
1141 // line width below 1.5, draw the basic hairline polygon
1142 for (sal_uInt32
a(0); a
< nCount
; a
++)
1144 mpOutputDevice
->DrawPolyLine(aHairlinePolyPolygon
.getB2DPolygon(a
), 0.0);
1149 else if (basegfx::fTools::lessOrEqual(fDiscreteLineWidth
, 2.5))
1151 // line width is in range ]1.5 .. 2.5], use four hairlines
1152 // drawn in a square
1153 for (sal_uInt32
a(0); a
< nCount
; a
++)
1155 basegfx::B2DPolygon
aCandidate(aHairlinePolyPolygon
.getB2DPolygon(a
));
1156 basegfx::B2DHomMatrix aMat
;
1158 mpOutputDevice
->DrawPolyLine(aCandidate
, 0.0);
1160 aMat
.set(0, 2, 1.0);
1161 aMat
.set(1, 2, 0.0);
1162 aCandidate
.transform(aMat
);
1164 mpOutputDevice
->DrawPolyLine(aCandidate
, 0.0);
1166 aMat
.set(0, 2, 0.0);
1167 aMat
.set(1, 2, 1.0);
1168 aCandidate
.transform(aMat
);
1170 mpOutputDevice
->DrawPolyLine(aCandidate
, 0.0);
1172 aMat
.set(0, 2, -1.0);
1173 aMat
.set(1, 2, 0.0);
1174 aCandidate
.transform(aMat
);
1176 mpOutputDevice
->DrawPolyLine(aCandidate
, 0.0);
1183 // #i101491# line width is above 2.5
1187 if (!bDone
&& rPolygonStrokeCandidate
.getB2DPolygon().count() > 1000)
1189 // #i101491# If the polygon complexity uses more than a given amount, do
1190 // use OutputDevice::DrawPolyLine directly; this will avoid buffering all
1191 // decompositions in primitives (memory) and fallback to old line painting
1192 // for very complex polygons, too
1193 for (sal_uInt32
a(0); a
< nCount
; a
++)
1195 mpOutputDevice
->DrawPolyLine(aHairlinePolyPolygon
.getB2DPolygon(a
),
1196 fDiscreteLineWidth
, rLineAttribute
.getLineJoin(),
1197 rLineAttribute
.getLineCap(),
1198 rLineAttribute
.getMiterMinimumAngle());
1208 // remember that we enter a PolygonStrokePrimitive2D decomposition,
1209 // used for AA thick line drawing
1210 mnPolygonStrokePrimitive2D
++;
1212 // line width is big enough for standard filled polygon visualisation or zero
1213 process(rPolygonStrokeCandidate
);
1215 // leave PolygonStrokePrimitive2D
1216 mnPolygonStrokePrimitive2D
--;
1220 void VclProcessor2D::RenderEpsPrimitive2D(const primitive2d::EpsPrimitive2D
& rEpsPrimitive2D
)
1222 // The new decomposition of Metafiles made it necessary to add an Eps
1223 // primitive to handle embedded Eps data. On some devices, this can be
1224 // painted directly (mac, printer).
1225 // To be able to handle the replacement correctly, i need to handle it myself
1226 // since DrawEPS will not be able e.g. to rotate the replacement. To be able
1227 // to do that, i added a boolean return to OutputDevice::DrawEPS(..)
1228 // to know when EPS was handled directly already.
1229 basegfx::B2DRange
aRange(0.0, 0.0, 1.0, 1.0);
1230 aRange
.transform(maCurrentTransformation
* rEpsPrimitive2D
.getEpsTransform());
1232 if (aRange
.isEmpty())
1235 const ::tools::Rectangle
aRectangle(static_cast<sal_Int32
>(floor(aRange
.getMinX())),
1236 static_cast<sal_Int32
>(floor(aRange
.getMinY())),
1237 static_cast<sal_Int32
>(ceil(aRange
.getMaxX())),
1238 static_cast<sal_Int32
>(ceil(aRange
.getMaxY())));
1240 if (aRectangle
.IsEmpty())
1243 bool bWillReallyRender
= mpOutputDevice
->IsDeviceOutputNecessary();
1244 // try to paint EPS directly without fallback visualisation
1245 const bool bEPSPaintedDirectly
1247 && mpOutputDevice
->DrawEPS(aRectangle
.TopLeft(), aRectangle
.GetSize(),
1248 rEpsPrimitive2D
.getGfxLink());
1250 if (!bEPSPaintedDirectly
)
1252 // use the decomposition which will correctly handle the
1253 // fallback visualisation using full transformation (e.g. rotation)
1254 process(rEpsPrimitive2D
);
1258 void VclProcessor2D::RenderObjectInfoPrimitive2D(
1259 const primitive2d::ObjectInfoPrimitive2D
& rObjectInfoPrimitive2D
)
1261 // remember current ObjectInfoPrimitive2D and set new current one (build a stack - push)
1262 const primitive2d::ObjectInfoPrimitive2D
* pLast(getObjectInfoPrimitive2D());
1263 mpObjectInfoPrimitive2D
= &rObjectInfoPrimitive2D
;
1266 process(rObjectInfoPrimitive2D
.getChildren());
1268 // restore current ObjectInfoPrimitive2D (pop)
1269 mpObjectInfoPrimitive2D
= pLast
;
1272 void VclProcessor2D::RenderSvgLinearAtomPrimitive2D(
1273 const primitive2d::SvgLinearAtomPrimitive2D
& rCandidate
)
1275 const double fDelta(rCandidate
.getOffsetB() - rCandidate
.getOffsetA());
1277 if (!basegfx::fTools::more(fDelta
, 0.0))
1280 const basegfx::BColor
aColorA(maBColorModifierStack
.getModifiedColor(rCandidate
.getColorA()));
1281 const basegfx::BColor
aColorB(maBColorModifierStack
.getModifiedColor(rCandidate
.getColorB()));
1283 // calculate discrete unit in WorldCoordinates; use diagonal (1.0, 1.0) and divide by sqrt(2)
1284 const basegfx::B2DVector
aDiscreteVector(
1285 getViewInformation2D().getInverseObjectToViewTransformation()
1286 * basegfx::B2DVector(1.0, 1.0));
1287 const double fDiscreteUnit(aDiscreteVector
.getLength() * (1.0 / 1.414213562373));
1289 // use color distance and discrete lengths to calculate step count
1290 const sal_uInt32
nSteps(calculateStepsForSvgGradient(aColorA
, aColorB
, fDelta
, fDiscreteUnit
));
1292 // switch off line painting
1293 mpOutputDevice
->SetLineColor();
1295 // prepare polygon in needed width at start position (with discrete overlap)
1296 const basegfx::B2DPolygon
aPolygon(basegfx::utils::createPolygonFromRect(
1297 basegfx::B2DRange(rCandidate
.getOffsetA() - fDiscreteUnit
, 0.0,
1298 rCandidate
.getOffsetA() + (fDelta
/ nSteps
) + fDiscreteUnit
, 1.0)));
1300 // prepare loop ([0.0 .. 1.0[)
1301 double fUnitScale(0.0);
1302 const double fUnitStep(1.0 / nSteps
);
1305 for (sal_uInt32
a(0); a
< nSteps
; a
++, fUnitScale
+= fUnitStep
)
1307 basegfx::B2DPolygon
aNew(aPolygon
);
1309 aNew
.transform(maCurrentTransformation
1310 * basegfx::utils::createTranslateB2DHomMatrix(fDelta
* fUnitScale
, 0.0));
1311 mpOutputDevice
->SetFillColor(Color(basegfx::interpolate(aColorA
, aColorB
, fUnitScale
)));
1312 mpOutputDevice
->DrawPolyPolygon(basegfx::B2DPolyPolygon(aNew
));
1316 void VclProcessor2D::RenderSvgRadialAtomPrimitive2D(
1317 const primitive2d::SvgRadialAtomPrimitive2D
& rCandidate
)
1319 const double fDeltaScale(rCandidate
.getScaleB() - rCandidate
.getScaleA());
1321 if (!basegfx::fTools::more(fDeltaScale
, 0.0))
1324 const basegfx::BColor
aColorA(maBColorModifierStack
.getModifiedColor(rCandidate
.getColorA()));
1325 const basegfx::BColor
aColorB(maBColorModifierStack
.getModifiedColor(rCandidate
.getColorB()));
1327 // calculate discrete unit in WorldCoordinates; use diagonal (1.0, 1.0) and divide by sqrt(2)
1328 const basegfx::B2DVector
aDiscreteVector(
1329 getViewInformation2D().getInverseObjectToViewTransformation()
1330 * basegfx::B2DVector(1.0, 1.0));
1331 const double fDiscreteUnit(aDiscreteVector
.getLength() * (1.0 / 1.414213562373));
1333 // use color distance and discrete lengths to calculate step count
1334 const sal_uInt32
nSteps(
1335 calculateStepsForSvgGradient(aColorA
, aColorB
, fDeltaScale
, fDiscreteUnit
));
1337 // switch off line painting
1338 mpOutputDevice
->SetLineColor();
1340 // prepare loop ([0.0 .. 1.0[, full polygons, no polypolygons with holes)
1341 double fUnitScale(0.0);
1342 const double fUnitStep(1.0 / nSteps
);
1344 for (sal_uInt32
a(0); a
< nSteps
; a
++, fUnitScale
+= fUnitStep
)
1346 basegfx::B2DHomMatrix aTransform
;
1347 const double fEndScale(rCandidate
.getScaleB() - (fDeltaScale
* fUnitScale
));
1349 if (rCandidate
.isTranslateSet())
1351 const basegfx::B2DVector
aTranslate(basegfx::interpolate(
1352 rCandidate
.getTranslateB(), rCandidate
.getTranslateA(), fUnitScale
));
1354 aTransform
= basegfx::utils::createScaleTranslateB2DHomMatrix(
1355 fEndScale
, fEndScale
, aTranslate
.getX(), aTranslate
.getY());
1359 aTransform
= basegfx::utils::createScaleB2DHomMatrix(fEndScale
, fEndScale
);
1362 basegfx::B2DPolygon
aNew(basegfx::utils::createPolygonFromUnitCircle());
1364 aNew
.transform(maCurrentTransformation
* aTransform
);
1365 mpOutputDevice
->SetFillColor(Color(basegfx::interpolate(aColorB
, aColorA
, fUnitScale
)));
1366 mpOutputDevice
->DrawPolyPolygon(basegfx::B2DPolyPolygon(aNew
));
1370 void VclProcessor2D::adaptLineToFillDrawMode() const
1372 const DrawModeFlags
nOriginalDrawMode(mpOutputDevice
->GetDrawMode());
1374 if (!(nOriginalDrawMode
1375 & (DrawModeFlags::BlackLine
| DrawModeFlags::GrayLine
| DrawModeFlags::WhiteLine
1376 | DrawModeFlags::SettingsLine
)))
1379 DrawModeFlags
nAdaptedDrawMode(nOriginalDrawMode
);
1381 if (nOriginalDrawMode
& DrawModeFlags::BlackLine
)
1383 nAdaptedDrawMode
|= DrawModeFlags::BlackFill
;
1387 nAdaptedDrawMode
&= ~DrawModeFlags::BlackFill
;
1390 if (nOriginalDrawMode
& DrawModeFlags::GrayLine
)
1392 nAdaptedDrawMode
|= DrawModeFlags::GrayFill
;
1396 nAdaptedDrawMode
&= ~DrawModeFlags::GrayFill
;
1399 if (nOriginalDrawMode
& DrawModeFlags::WhiteLine
)
1401 nAdaptedDrawMode
|= DrawModeFlags::WhiteFill
;
1405 nAdaptedDrawMode
&= ~DrawModeFlags::WhiteFill
;
1408 if (nOriginalDrawMode
& DrawModeFlags::SettingsLine
)
1410 nAdaptedDrawMode
|= DrawModeFlags::SettingsFill
;
1414 nAdaptedDrawMode
&= ~DrawModeFlags::SettingsFill
;
1417 mpOutputDevice
->SetDrawMode(nAdaptedDrawMode
);
1420 void VclProcessor2D::adaptTextToFillDrawMode() const
1422 const DrawModeFlags
nOriginalDrawMode(mpOutputDevice
->GetDrawMode());
1423 if (!(nOriginalDrawMode
1424 & (DrawModeFlags::BlackText
| DrawModeFlags::GrayText
| DrawModeFlags::WhiteText
1425 | DrawModeFlags::SettingsText
)))
1428 DrawModeFlags
nAdaptedDrawMode(nOriginalDrawMode
);
1430 if (nOriginalDrawMode
& DrawModeFlags::BlackText
)
1432 nAdaptedDrawMode
|= DrawModeFlags::BlackFill
;
1436 nAdaptedDrawMode
&= ~DrawModeFlags::BlackFill
;
1439 if (nOriginalDrawMode
& DrawModeFlags::GrayText
)
1441 nAdaptedDrawMode
|= DrawModeFlags::GrayFill
;
1445 nAdaptedDrawMode
&= ~DrawModeFlags::GrayFill
;
1448 if (nOriginalDrawMode
& DrawModeFlags::WhiteText
)
1450 nAdaptedDrawMode
|= DrawModeFlags::WhiteFill
;
1454 nAdaptedDrawMode
&= ~DrawModeFlags::WhiteFill
;
1457 if (nOriginalDrawMode
& DrawModeFlags::SettingsText
)
1459 nAdaptedDrawMode
|= DrawModeFlags::SettingsFill
;
1463 nAdaptedDrawMode
&= ~DrawModeFlags::SettingsFill
;
1466 mpOutputDevice
->SetDrawMode(nAdaptedDrawMode
);
1471 VclProcessor2D::VclProcessor2D(const geometry::ViewInformation2D
& rViewInformation
,
1472 OutputDevice
& rOutDev
,
1473 const basegfx::BColorModifierStack
& rInitStack
)
1474 : BaseProcessor2D(rViewInformation
)
1475 , mpOutputDevice(&rOutDev
)
1476 , maBColorModifierStack(rInitStack
)
1477 , maCurrentTransformation()
1478 , maDrawinglayerOpt()
1479 , mnPolygonStrokePrimitive2D(0)
1480 , mpObjectInfoPrimitive2D(nullptr)
1482 // set digit language, derived from SvtCTLOptions to have the correct
1483 // number display for arabic/hindi numerals
1484 rOutDev
.SetDigitLanguage(drawinglayer::detail::getDigitLanguage());
1487 VclProcessor2D::~VclProcessor2D() {}
1490 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */