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 <svtools/optionsdrawinglayer.hxx>
27 #include <tools/debug.hxx>
28 #include <tools/fract.hxx>
30 #include <vcl/glyphitemcache.hxx>
31 #include <vcl/graph.hxx>
32 #include <vcl/kernarray.hxx>
33 #include <vcl/outdev.hxx>
34 #include <rtl/ustrbuf.hxx>
35 #include <sal/log.hxx>
36 #include <toolkit/helper/vclunohelper.hxx>
37 #include <basegfx/polygon/b2dpolygontools.hxx>
38 #include <basegfx/polygon/b2dpolypolygontools.hxx>
39 #include <basegfx/polygon/b2dpolygonclipper.hxx>
40 #include <basegfx/color/bcolor.hxx>
41 #include <basegfx/matrix/b2dhommatrixtools.hxx>
42 #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
43 #include <drawinglayer/primitive2d/textprimitive2d.hxx>
44 #include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx>
45 #include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
46 #include <drawinglayer/primitive2d/fillgraphicprimitive2d.hxx>
47 #include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
48 #include <drawinglayer/primitive2d/PolygonStrokePrimitive2D.hxx>
49 #include <drawinglayer/primitive2d/PolyPolygonGraphicPrimitive2D.hxx>
50 #include <drawinglayer/primitive2d/maskprimitive2d.hxx>
51 #include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx>
52 #include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
53 #include <drawinglayer/primitive2d/transparenceprimitive2d.hxx>
54 #include <drawinglayer/primitive2d/transformprimitive2d.hxx>
55 #include <drawinglayer/primitive2d/markerarrayprimitive2d.hxx>
56 #include <drawinglayer/primitive2d/pagepreviewprimitive2d.hxx>
57 #include <drawinglayer/primitive2d/textenumsprimitive2d.hxx>
58 #include <drawinglayer/primitive2d/svggradientprimitive2d.hxx>
59 // for support of Title/Description in all apps when embedding pictures
60 #include <drawinglayer/primitive2d/objectinfoprimitive2d.hxx>
62 #include <drawinglayer/primitive2d/textlayoutdevice.hxx>
64 #include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx>
65 #include <drawinglayer/primitive2d/epsprimitive2d.hxx>
67 using namespace com::sun::star
;
71 sal_uInt32
calculateStepsForSvgGradient(const basegfx::BColor
& rColorA
,
72 const basegfx::BColor
& rColorB
, double fDelta
,
75 // use color distance, assume to do every color step
76 sal_uInt32
nSteps(basegfx::fround(rColorA
.getDistance(rColorB
) * 255.0));
80 // calc discrete length to change color each discrete unit (pixel)
81 const sal_uInt32
nDistSteps(basegfx::fround(fDelta
/ fDiscreteUnit
));
83 nSteps
= std::min(nSteps
, nDistSteps
);
86 // reduce quality to 3 discrete units or every 3rd color step for rendering
89 // roughly cut when too big or too small (not full quality, reduce complexity)
90 nSteps
= std::min(nSteps
, sal_uInt32(255));
91 nSteps
= std::max(nSteps
, sal_uInt32(1));
99 /** helper to convert a MapMode to a transformation */
100 basegfx::B2DHomMatrix
getTransformFromMapMode(const MapMode
& rMapMode
)
102 basegfx::B2DHomMatrix aMapping
;
103 const Fraction
aNoScale(1, 1);
104 const Point
& rOrigin(rMapMode
.GetOrigin());
106 if (0 != rOrigin
.X() || 0 != rOrigin
.Y())
108 aMapping
.translate(rOrigin
.X(), rOrigin
.Y());
111 if (rMapMode
.GetScaleX() != aNoScale
|| rMapMode
.GetScaleY() != aNoScale
)
113 aMapping
.scale(double(rMapMode
.GetScaleX()), double(rMapMode
.GetScaleY()));
120 namespace drawinglayer::processor2d
124 // directdraw of text simple portion or decorated portion primitive. When decorated, all the extra
125 // information is translated to VCL parameters and set at the font.
126 // Acceptance is restricted to no shearing and positive scaling in X and Y (no font mirroring
128 void VclProcessor2D::RenderTextSimpleOrDecoratedPortionPrimitive2D(
129 const primitive2d::TextSimplePortionPrimitive2D
& rTextCandidate
)
131 // decompose matrix to have position and size of text
132 basegfx::B2DHomMatrix
aLocalTransform(maCurrentTransformation
133 * rTextCandidate
.getTextTransform());
134 basegfx::B2DVector aFontScaling
, aTranslate
;
135 double fRotate
, fShearX
;
136 aLocalTransform
.decompose(aFontScaling
, aTranslate
, fRotate
, fShearX
);
138 bool bPrimitiveAccepted(false);
140 // tdf#95581: Assume tiny shears are rounding artefacts or whatever and can be ignored,
141 // especially if the effect is less than a pixel.
142 if (std::abs(aFontScaling
.getY() * fShearX
) < 1)
144 if (basegfx::fTools::less(aFontScaling
.getX(), 0.0)
145 && basegfx::fTools::less(aFontScaling
.getY(), 0.0))
147 // handle special case: If scale is negative in (x,y) (3rd quadrant), it can
148 // be expressed as rotation by PI. Use this since the Font rendering will not
149 // apply the negative scales in any form
150 aFontScaling
= basegfx::absolute(aFontScaling
);
154 if (basegfx::fTools::more(aFontScaling
.getX(), 0.0)
155 && basegfx::fTools::more(aFontScaling
.getY(), 0.0))
157 double fIgnoreRotate
, fIgnoreShearX
;
159 basegfx::B2DVector aFontSize
, aTextTranslate
;
160 rTextCandidate
.getTextTransform().decompose(aFontSize
, aTextTranslate
, fIgnoreRotate
,
163 // tdf#153092 Ideally we don't have to scale the font and dxarray, but we might have
164 // to nevertheless if dealing with non integer sizes
165 const bool bScaleFont(aFontSize
.getY() != std::round(aFontSize
.getY()));
171 aFont
= primitive2d::getVclFontFromFontAttribute(
172 rTextCandidate
.getFontAttribute(), aFontSize
.getX(), aFontSize
.getY(), fRotate
,
173 rTextCandidate
.getLocale());
177 aFont
= primitive2d::getVclFontFromFontAttribute(
178 rTextCandidate
.getFontAttribute(), aFontScaling
.getX(), aFontScaling
.getY(),
179 fRotate
, rTextCandidate
.getLocale());
182 // Don't draw fonts without height
183 if (aFont
.GetFontHeight() <= 0)
186 // set FillColor Attribute
187 const Color
aFillColor(rTextCandidate
.getTextFillColor());
188 if (aFillColor
!= COL_TRANSPARENT
)
190 aFont
.SetFillColor(aFillColor
);
191 aFont
.SetTransparent(false);
194 // handle additional font attributes
195 const primitive2d::TextDecoratedPortionPrimitive2D
* pTCPP
= nullptr;
196 if (rTextCandidate
.getPrimitive2DID() == PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D
)
197 pTCPP
= static_cast<const primitive2d::TextDecoratedPortionPrimitive2D
*>(
200 if (pTCPP
!= nullptr)
202 // set the color of text decorations
203 const basegfx::BColor aTextlineColor
204 = maBColorModifierStack
.getModifiedColor(pTCPP
->getTextlineColor());
205 mpOutputDevice
->SetTextLineColor(Color(aTextlineColor
));
207 // set Overline attribute
208 const FontLineStyle
eFontOverline(
209 primitive2d::mapTextLineToFontLineStyle(pTCPP
->getFontOverline()));
210 if (eFontOverline
!= LINESTYLE_NONE
)
212 aFont
.SetOverline(eFontOverline
);
213 const basegfx::BColor aOverlineColor
214 = maBColorModifierStack
.getModifiedColor(pTCPP
->getOverlineColor());
215 mpOutputDevice
->SetOverlineColor(Color(aOverlineColor
));
216 if (pTCPP
->getWordLineMode())
217 aFont
.SetWordLineMode(true);
220 // set Underline attribute
221 const FontLineStyle
eFontLineStyle(
222 primitive2d::mapTextLineToFontLineStyle(pTCPP
->getFontUnderline()));
223 if (eFontLineStyle
!= LINESTYLE_NONE
)
225 aFont
.SetUnderline(eFontLineStyle
);
226 if (pTCPP
->getWordLineMode())
227 aFont
.SetWordLineMode(true);
230 // set Strikeout attribute
231 const FontStrikeout
eFontStrikeout(
232 primitive2d::mapTextStrikeoutToFontStrikeout(pTCPP
->getTextStrikeout()));
234 if (eFontStrikeout
!= STRIKEOUT_NONE
)
235 aFont
.SetStrikeout(eFontStrikeout
);
237 // set EmphasisMark attribute
238 FontEmphasisMark eFontEmphasisMark
= FontEmphasisMark::NONE
;
239 switch (pTCPP
->getTextEmphasisMark())
242 SAL_WARN("drawinglayer",
243 "Unknown EmphasisMark style " << pTCPP
->getTextEmphasisMark());
245 case primitive2d::TEXT_FONT_EMPHASIS_MARK_NONE
:
246 eFontEmphasisMark
= FontEmphasisMark::NONE
;
248 case primitive2d::TEXT_FONT_EMPHASIS_MARK_DOT
:
249 eFontEmphasisMark
= FontEmphasisMark::Dot
;
251 case primitive2d::TEXT_FONT_EMPHASIS_MARK_CIRCLE
:
252 eFontEmphasisMark
= FontEmphasisMark::Circle
;
254 case primitive2d::TEXT_FONT_EMPHASIS_MARK_DISC
:
255 eFontEmphasisMark
= FontEmphasisMark::Disc
;
257 case primitive2d::TEXT_FONT_EMPHASIS_MARK_ACCENT
:
258 eFontEmphasisMark
= FontEmphasisMark::Accent
;
262 if (eFontEmphasisMark
!= FontEmphasisMark::NONE
)
264 DBG_ASSERT((pTCPP
->getEmphasisMarkAbove() != pTCPP
->getEmphasisMarkBelow()),
265 "DrawingLayer: Bad EmphasisMark position!");
266 if (pTCPP
->getEmphasisMarkAbove())
267 eFontEmphasisMark
|= FontEmphasisMark::PosAbove
;
269 eFontEmphasisMark
|= FontEmphasisMark::PosBelow
;
270 aFont
.SetEmphasisMark(eFontEmphasisMark
);
273 // set Relief attribute
274 FontRelief eFontRelief
= FontRelief::NONE
;
275 switch (pTCPP
->getTextRelief())
278 SAL_WARN("drawinglayer", "Unknown Relief style " << pTCPP
->getTextRelief());
280 case primitive2d::TEXT_RELIEF_NONE
:
281 eFontRelief
= FontRelief::NONE
;
283 case primitive2d::TEXT_RELIEF_EMBOSSED
:
284 eFontRelief
= FontRelief::Embossed
;
286 case primitive2d::TEXT_RELIEF_ENGRAVED
:
287 eFontRelief
= FontRelief::Engraved
;
291 if (eFontRelief
!= FontRelief::NONE
)
292 aFont
.SetRelief(eFontRelief
);
294 // set Shadow attribute
295 if (pTCPP
->getShadow())
296 aFont
.SetShadow(true);
299 // create integer DXArray
302 if (!rTextCandidate
.getDXArray().empty())
304 double fPixelVectorFactor(1.0);
307 const basegfx::B2DVector
aPixelVector(maCurrentTransformation
308 * basegfx::B2DVector(1.0, 0.0));
309 fPixelVectorFactor
= aPixelVector
.getLength();
312 aDXArray
.reserve(rTextCandidate
.getDXArray().size());
313 for (auto const& elem
: rTextCandidate
.getDXArray())
314 aDXArray
.push_back(basegfx::fround(elem
* fPixelVectorFactor
));
317 // set parameters and paint text snippet
318 const basegfx::BColor
aRGBFontColor(
319 maBColorModifierStack
.getModifiedColor(rTextCandidate
.getFontColor()));
320 const vcl::text::ComplexTextLayoutFlags
nOldLayoutMode(mpOutputDevice
->GetLayoutMode());
322 if (rTextCandidate
.getFontAttribute().getRTL())
324 vcl::text::ComplexTextLayoutFlags
nRTLLayoutMode(
325 nOldLayoutMode
& ~vcl::text::ComplexTextLayoutFlags::BiDiStrong
);
326 nRTLLayoutMode
|= vcl::text::ComplexTextLayoutFlags::BiDiRtl
327 | vcl::text::ComplexTextLayoutFlags::TextOriginLeft
;
328 mpOutputDevice
->SetLayoutMode(nRTLLayoutMode
);
331 mpOutputDevice
->SetTextColor(Color(aRGBFontColor
));
333 OUString
aText(rTextCandidate
.getText());
334 sal_Int32 nPos
= rTextCandidate
.getTextPosition();
335 sal_Int32 nLen
= rTextCandidate
.getTextLength();
337 // this contraption is used in editeng, with format paragraph used to
338 // set a tab with a tab-fill character
339 if (rTextCandidate
.isFilled())
341 tools::Long nWidthToFill
= rTextCandidate
.getWidthToFill();
344 = mpOutputDevice
->GetTextArray(rTextCandidate
.getText(), &aDXArray
, 0, 1);
345 sal_Int32 nChars
= 2;
347 nChars
= nWidthToFill
/ nWidth
;
349 OUStringBuffer
aFilled(nChars
);
350 comphelper::string::padToLength(aFilled
, nChars
, aText
[0]);
351 aText
= aFilled
.makeStringAndClear();
355 if (!aDXArray
.empty())
357 sal_Int32 nDX
= aDXArray
[0];
358 aDXArray
.resize(nLen
);
359 for (sal_Int32 i
= 1; i
< nLen
; ++i
)
360 aDXArray
.set(i
, aDXArray
[i
- 1] + nDX
);
365 bool bChangeMapMode(false);
368 basegfx::B2DHomMatrix
aCombinedTransform(
369 getTransformFromMapMode(mpOutputDevice
->GetMapMode())
370 * maCurrentTransformation
);
372 basegfx::B2DVector aCurrentScaling
, aCurrentTranslate
;
373 double fCurrentRotate
;
374 aCombinedTransform
.decompose(aCurrentScaling
, aCurrentTranslate
, fCurrentRotate
,
378 basegfx::fround(aCurrentTranslate
.getX() / aCurrentScaling
.getX()),
379 basegfx::fround(aCurrentTranslate
.getY() / aCurrentScaling
.getY()));
381 Fraction
aScaleX(aCurrentScaling
.getX());
382 if (!aScaleX
.IsValid())
384 SAL_WARN("drawinglayer", "invalid X Scale");
388 Fraction
aScaleY(aCurrentScaling
.getY());
389 if (!aScaleY
.IsValid())
391 SAL_WARN("drawinglayer", "invalid Y Scale");
395 MapMode
aMapMode(mpOutputDevice
->GetMapMode().GetMapUnit(), aOrigin
, aScaleX
,
399 aTextTranslate
*= basegfx::utils::createRotateB2DHomMatrix(fCurrentRotate
);
400 aStartPoint
= Point(aTextTranslate
.getX(), aTextTranslate
.getY());
402 bChangeMapMode
= aMapMode
!= mpOutputDevice
->GetMapMode();
405 mpOutputDevice
->Push(vcl::PushFlags::MAPMODE
);
406 mpOutputDevice
->SetRelativeMapMode(aMapMode
);
411 const basegfx::B2DPoint
aPoint(aLocalTransform
* basegfx::B2DPoint(0.0, 0.0));
412 aStartPoint
= Point(basegfx::fround(aPoint
.getX()), basegfx::fround(aPoint
.getY()));
415 // tdf#152990 set the font after the MapMode is (potentially) set so canvas uses the desired
417 mpOutputDevice
->SetFont(aFont
);
419 if (!aDXArray
.empty())
421 const SalLayoutGlyphs
* pGlyphs
= SalLayoutGlyphsCache::self()->GetLayoutGlyphs(
422 mpOutputDevice
, aText
, nPos
, nLen
);
423 mpOutputDevice
->DrawTextArray(aStartPoint
, aText
, aDXArray
,
424 rTextCandidate
.getKashidaArray(), nPos
, nLen
,
425 SalLayoutFlags::NONE
, pGlyphs
);
429 mpOutputDevice
->DrawText(aStartPoint
, aText
, nPos
, nLen
);
432 if (rTextCandidate
.getFontAttribute().getRTL())
434 mpOutputDevice
->SetLayoutMode(nOldLayoutMode
);
438 mpOutputDevice
->Pop();
440 bPrimitiveAccepted
= true;
444 if (!bPrimitiveAccepted
)
447 process(rTextCandidate
);
451 // direct draw of hairline
452 void VclProcessor2D::RenderPolygonHairlinePrimitive2D(
453 const primitive2d::PolygonHairlinePrimitive2D
& rPolygonCandidate
, bool bPixelBased
)
455 const basegfx::BColor
aHairlineColor(
456 maBColorModifierStack
.getModifiedColor(rPolygonCandidate
.getBColor()));
457 mpOutputDevice
->SetLineColor(Color(aHairlineColor
));
458 mpOutputDevice
->SetFillColor();
460 basegfx::B2DPolygon
aLocalPolygon(rPolygonCandidate
.getB2DPolygon());
461 aLocalPolygon
.transform(maCurrentTransformation
);
463 if (bPixelBased
&& getViewInformation2D().getPixelSnapHairline())
466 // when a Hairline is painted and AntiAliasing is on the option SnapHorVerLinesToDiscrete
467 // allows to suppress AntiAliasing for pure horizontal or vertical lines. This is done since
468 // not-AntiAliased such lines look more pleasing to the eye (e.g. 2D chart content). This
469 // NEEDS to be done in discrete coordinates, so only useful for pixel based rendering.
470 aLocalPolygon
= basegfx::utils::snapPointsOfHorizontalOrVerticalEdges(aLocalPolygon
);
473 mpOutputDevice
->DrawPolyLine(aLocalPolygon
, 0.0);
476 // direct draw of transformed BitmapEx primitive
477 void VclProcessor2D::RenderBitmapPrimitive2D(const primitive2d::BitmapPrimitive2D
& rBitmapCandidate
)
479 BitmapEx
aBitmapEx(rBitmapCandidate
.getBitmap());
480 const basegfx::B2DHomMatrix
aLocalTransform(maCurrentTransformation
481 * rBitmapCandidate
.getTransform());
483 if (maBColorModifierStack
.count())
485 aBitmapEx
= aBitmapEx
.ModifyBitmapEx(maBColorModifierStack
);
487 if (aBitmapEx
.IsEmpty())
489 // color gets completely replaced, get it
490 const basegfx::BColor
aModifiedColor(
491 maBColorModifierStack
.getModifiedColor(basegfx::BColor()));
492 basegfx::B2DPolygon
aPolygon(basegfx::utils::createUnitPolygon());
493 aPolygon
.transform(aLocalTransform
);
495 mpOutputDevice
->SetFillColor(Color(aModifiedColor
));
496 mpOutputDevice
->SetLineColor();
497 mpOutputDevice
->DrawPolygon(aPolygon
);
503 // #122923# do no longer add Alpha channel here; the right place to do this is when really
504 // the own transformer is used (see OutputDevice::DrawTransformedBitmapEx).
506 // draw using OutputDevice'sDrawTransformedBitmapEx
507 mpOutputDevice
->DrawTransformedBitmapEx(aLocalTransform
, aBitmapEx
);
510 void VclProcessor2D::RenderFillGraphicPrimitive2D(
511 const primitive2d::FillGraphicPrimitive2D
& rFillBitmapCandidate
)
513 bool bPrimitiveAccepted
= RenderFillGraphicPrimitive2DImpl(rFillBitmapCandidate
);
515 if (!bPrimitiveAccepted
)
517 // do not accept, use decomposition
518 process(rFillBitmapCandidate
);
522 bool VclProcessor2D::RenderFillGraphicPrimitive2DImpl(
523 const primitive2d::FillGraphicPrimitive2D
& rFillBitmapCandidate
)
525 const attribute::FillGraphicAttribute
& rFillGraphicAttribute(
526 rFillBitmapCandidate
.getFillGraphic());
528 // #121194# when tiling is used and content is bitmap-based, do direct tiling in the
529 // renderer on pixel base to ensure tight fitting. Do not do this when
530 // the fill is rotated or sheared.
531 if (!rFillGraphicAttribute
.getTiling())
534 // content is bitmap(ex)
536 // for Vector Graphic Data (SVG, EMF+) support, force decomposition when present. This will lead to use
537 // the primitive representation of the vector data directly.
539 // when graphic is animated, force decomposition to use the correct graphic, else
540 // fill style will not be animated
541 if (GraphicType::Bitmap
!= rFillGraphicAttribute
.getGraphic().GetType()
542 || rFillGraphicAttribute
.getGraphic().getVectorGraphicData()
543 || rFillGraphicAttribute
.getGraphic().IsAnimated())
546 // decompose matrix to check for shear, rotate and mirroring
547 basegfx::B2DHomMatrix
aLocalTransform(maCurrentTransformation
548 * rFillBitmapCandidate
.getTransformation());
549 basegfx::B2DVector aScale
, aTranslate
;
550 double fRotate
, fShearX
;
551 aLocalTransform
.decompose(aScale
, aTranslate
, fRotate
, fShearX
);
553 // when nopt rotated/sheared
554 if (!basegfx::fTools::equalZero(fRotate
) || !basegfx::fTools::equalZero(fShearX
))
557 // no shear or rotate, draw direct in pixel coordinates
559 // transform object range to device coordinates (pixels). Use
560 // the device transformation for better accuracy
561 basegfx::B2DRange
aObjectRange(aTranslate
, aTranslate
+ aScale
);
562 aObjectRange
.transform(mpOutputDevice
->GetViewTransformation());
564 // extract discrete size of object
565 const sal_Int32
nOWidth(basegfx::fround(aObjectRange
.getWidth()));
566 const sal_Int32
nOHeight(basegfx::fround(aObjectRange
.getHeight()));
568 // only do something when object has a size in discrete units
569 if (nOWidth
<= 0 || nOHeight
<= 0)
572 // transform graphic range to device coordinates (pixels). Use
573 // the device transformation for better accuracy
574 basegfx::B2DRange
aGraphicRange(rFillGraphicAttribute
.getGraphicRange());
575 aGraphicRange
.transform(mpOutputDevice
->GetViewTransformation() * aLocalTransform
);
577 // extract discrete size of graphic
578 // caution: when getting to zero, nothing would be painted; thus, do not allow this
579 const sal_Int32
nBWidth(std::max(sal_Int32(1), basegfx::fround(aGraphicRange
.getWidth())));
580 const sal_Int32
nBHeight(std::max(sal_Int32(1), basegfx::fround(aGraphicRange
.getHeight())));
582 // nBWidth, nBHeight is the pixel size of the needed bitmap. To not need to scale it
583 // in vcl many times, create a size-optimized version
584 const Size
aNeededBitmapSizePixel(nBWidth
, nBHeight
);
585 BitmapEx
aBitmapEx(rFillGraphicAttribute
.getGraphic().GetBitmapEx());
586 const bool bPreScaled(nBWidth
* nBHeight
< (250 * 250));
588 // ... but only up to a maximum size, else it gets too expensive
591 // if color depth is below 24bit, expand before scaling for better quality.
592 // This is even needed for low colors, else the scale will produce
593 // a bitmap in gray or Black/White (!)
594 if (isPalettePixelFormat(aBitmapEx
.getPixelFormat()))
596 aBitmapEx
.Convert(BmpConversion::N24Bit
);
599 aBitmapEx
.Scale(aNeededBitmapSizePixel
, BmpScaleFlag::Interpolate
);
602 if (maBColorModifierStack
.count())
604 // when color modifier, apply to bitmap
605 aBitmapEx
= aBitmapEx
.ModifyBitmapEx(maBColorModifierStack
);
607 // ModifyBitmapEx uses empty bitmap as sign to return that
608 // the content will be completely replaced to mono color, use shortcut
609 if (aBitmapEx
.IsEmpty())
611 // color gets completely replaced, get it
612 const basegfx::BColor
aModifiedColor(
613 maBColorModifierStack
.getModifiedColor(basegfx::BColor()));
614 basegfx::B2DPolygon
aPolygon(basegfx::utils::createUnitPolygon());
615 aPolygon
.transform(aLocalTransform
);
617 mpOutputDevice
->SetFillColor(Color(aModifiedColor
));
618 mpOutputDevice
->SetLineColor();
619 mpOutputDevice
->DrawPolygon(aPolygon
);
625 sal_Int32
nBLeft(basegfx::fround(aGraphicRange
.getMinX()));
626 sal_Int32
nBTop(basegfx::fround(aGraphicRange
.getMinY()));
627 const sal_Int32
nOLeft(basegfx::fround(aObjectRange
.getMinX()));
628 const sal_Int32
nOTop(basegfx::fround(aObjectRange
.getMinY()));
634 const sal_Int32
nDiff((nBLeft
/ nBWidth
) + 1);
637 nBLeft
-= nDiff
* nBWidth
;
640 if (nBLeft
+ nBWidth
<= nOLeft
)
642 const sal_Int32
nDiff(-nBLeft
/ nBWidth
);
645 nBLeft
+= nDiff
* nBWidth
;
650 const sal_Int32
nDiff((nBTop
/ nBHeight
) + 1);
653 nBTop
-= nDiff
* nBHeight
;
656 if (nBTop
+ nBHeight
<= nOTop
)
658 const sal_Int32
nDiff(-nBTop
/ nBHeight
);
661 nBTop
+= nDiff
* nBHeight
;
665 const Point
aEmptyPoint(0, 0);
666 // the visible rect, in pixels
667 const ::tools::Rectangle
aVisiblePixel(aEmptyPoint
, mpOutputDevice
->GetOutputSizePixel());
668 const bool bWasEnabled(mpOutputDevice
->IsMapModeEnabled());
669 mpOutputDevice
->EnableMapMode(false);
671 // check if offset is used
672 const sal_Int32
nOffsetX(basegfx::fround(rFillGraphicAttribute
.getOffsetX() * nBWidth
));
676 // offset in X, so iterate over Y first and draw lines
677 for (sal_Int32
nYPos(nBTop
); nYPos
< nOTop
+ nOHeight
; nYPos
+= nBHeight
, nPosY
++)
679 for (sal_Int32
nXPos((nPosY
% 2) ? nBLeft
- nBWidth
+ nOffsetX
: nBLeft
);
680 nXPos
< nOLeft
+ nOWidth
; nXPos
+= nBWidth
)
682 const ::tools::Rectangle
aOutRectPixel(Point(nXPos
, nYPos
), aNeededBitmapSizePixel
);
684 if (aOutRectPixel
.Overlaps(aVisiblePixel
))
688 mpOutputDevice
->DrawBitmapEx(aOutRectPixel
.TopLeft(), aBitmapEx
);
692 mpOutputDevice
->DrawBitmapEx(aOutRectPixel
.TopLeft(),
693 aNeededBitmapSizePixel
, aBitmapEx
);
701 // check if offset is used
702 const sal_Int32
nOffsetY(basegfx::fround(rFillGraphicAttribute
.getOffsetY() * nBHeight
));
704 // possible offset in Y, so iterate over X first and draw columns
705 for (sal_Int32
nXPos(nBLeft
); nXPos
< nOLeft
+ nOWidth
; nXPos
+= nBWidth
, nPosX
++)
707 for (sal_Int32
nYPos((nPosX
% 2) ? nBTop
- nBHeight
+ nOffsetY
: nBTop
);
708 nYPos
< nOTop
+ nOHeight
; nYPos
+= nBHeight
)
710 const ::tools::Rectangle
aOutRectPixel(Point(nXPos
, nYPos
), aNeededBitmapSizePixel
);
712 if (aOutRectPixel
.Overlaps(aVisiblePixel
))
716 mpOutputDevice
->DrawBitmapEx(aOutRectPixel
.TopLeft(), aBitmapEx
);
720 mpOutputDevice
->DrawBitmapEx(aOutRectPixel
.TopLeft(),
721 aNeededBitmapSizePixel
, aBitmapEx
);
729 mpOutputDevice
->EnableMapMode(bWasEnabled
);
733 // direct draw of Graphic
734 void VclProcessor2D::RenderPolyPolygonGraphicPrimitive2D(
735 const primitive2d::PolyPolygonGraphicPrimitive2D
& rPolygonCandidate
)
738 const basegfx::B2DPolyPolygon
& rPolyPolygon
= rPolygonCandidate
.getB2DPolyPolygon();
740 // #121194# Todo: check if this works
741 if (!rPolyPolygon
.count())
743 // empty polyPolygon, done
748 const attribute::FillGraphicAttribute
& rFillGraphicAttribute
749 = rPolygonCandidate
.getFillGraphic();
751 // try to catch cases where the graphic will be color-modified to a single
752 // color (e.g. shadow)
753 switch (rFillGraphicAttribute
.getGraphic().GetType())
755 case GraphicType::GdiMetafile
:
757 // metafiles are potentially transparent, cannot optimize, not done
760 case GraphicType::Bitmap
:
762 if (!rFillGraphicAttribute
.getGraphic().IsTransparent()
763 && !rFillGraphicAttribute
.getGraphic().IsAlpha())
765 // bitmap is not transparent and has no alpha
766 const sal_uInt32
nBColorModifierStackCount(maBColorModifierStack
.count());
768 if (nBColorModifierStackCount
)
770 const basegfx::BColorModifierSharedPtr
& rTopmostModifier
771 = maBColorModifierStack
.getBColorModifier(nBColorModifierStackCount
773 const basegfx::BColorModifier_replace
* pReplacer
774 = dynamic_cast<const basegfx::BColorModifier_replace
*>(
775 rTopmostModifier
.get());
779 // the bitmap fill is in unified color, so we can replace it with
780 // a single polygon fill. The form of the fill depends on tiling
781 if (rFillGraphicAttribute
.getTiling())
783 // with tiling, fill the whole tools::PolyPolygon with the modifier color
784 basegfx::B2DPolyPolygon
aLocalPolyPolygon(rPolyPolygon
);
786 aLocalPolyPolygon
.transform(maCurrentTransformation
);
787 mpOutputDevice
->SetLineColor();
788 mpOutputDevice
->SetFillColor(Color(pReplacer
->getBColor()));
789 mpOutputDevice
->DrawPolyPolygon(aLocalPolyPolygon
);
793 // without tiling, only the area common to the bitmap tile and the
794 // tools::PolyPolygon is filled. Create the bitmap tile area in object
795 // coordinates. For this, the object transformation needs to be created
796 // from the already scaled PolyPolygon. The tile area in object
797 // coordinates will always be non-rotated, so it's not necessary to
798 // work with a polygon here
799 basegfx::B2DRange
aTileRange(
800 rFillGraphicAttribute
.getGraphicRange());
801 const basegfx::B2DRange
aPolyPolygonRange(
802 rPolyPolygon
.getB2DRange());
803 const basegfx::B2DHomMatrix
aNewObjectTransform(
804 basegfx::utils::createScaleTranslateB2DHomMatrix(
805 aPolyPolygonRange
.getRange(),
806 aPolyPolygonRange
.getMinimum()));
808 aTileRange
.transform(aNewObjectTransform
);
810 // now clip the object polyPolygon against the tile range
811 // to get the common area
812 basegfx::B2DPolyPolygon aTarget
813 = basegfx::utils::clipPolyPolygonOnRange(
814 rPolyPolygon
, aTileRange
, true, false);
818 aTarget
.transform(maCurrentTransformation
);
819 mpOutputDevice
->SetLineColor();
820 mpOutputDevice
->SetFillColor(Color(pReplacer
->getBColor()));
821 mpOutputDevice
->DrawPolyPolygon(aTarget
);
825 // simplified output executed, we are done
832 default: //GraphicType::NONE, GraphicType::Default
834 // empty graphic, we are done
843 // use default decomposition
844 process(rPolygonCandidate
);
849 void VclProcessor2D::RenderMaskPrimitive2DPixel(const primitive2d::MaskPrimitive2D
& rMaskCandidate
)
851 if (rMaskCandidate
.getChildren().empty())
854 basegfx::B2DPolyPolygon
aMask(rMaskCandidate
.getMask());
859 aMask
.transform(maCurrentTransformation
);
861 // Unless smooth edges are needed, simply use clipping.
862 if (basegfx::utils::isRectangle(aMask
) || !getViewInformation2D().getUseAntiAliasing())
864 mpOutputDevice
->Push(vcl::PushFlags::CLIPREGION
);
865 mpOutputDevice
->IntersectClipRegion(vcl::Region(aMask
));
866 process(rMaskCandidate
.getChildren());
867 mpOutputDevice
->Pop();
871 const basegfx::B2DRange
aRange(basegfx::utils::getRange(aMask
));
872 impBufferDevice
aBufferDevice(*mpOutputDevice
, aRange
);
874 if (!aBufferDevice
.isVisible())
877 // remember last OutDev and set to content
878 OutputDevice
* pLastOutputDevice
= mpOutputDevice
;
879 mpOutputDevice
= &aBufferDevice
.getContent();
882 process(rMaskCandidate
.getChildren());
884 // back to old OutDev
885 mpOutputDevice
= pLastOutputDevice
;
888 VirtualDevice
& rMask
= aBufferDevice
.getTransparence();
889 rMask
.SetLineColor();
890 rMask
.SetFillColor(COL_BLACK
);
891 rMask
.DrawPolyPolygon(aMask
);
893 // dump buffer to outdev
894 aBufferDevice
.paint();
897 // modified color group. Force output to unified color.
898 void VclProcessor2D::RenderModifiedColorPrimitive2D(
899 const primitive2d::ModifiedColorPrimitive2D
& rModifiedCandidate
)
901 if (!rModifiedCandidate
.getChildren().empty())
903 maBColorModifierStack
.push(rModifiedCandidate
.getColorModifier());
904 process(rModifiedCandidate
.getChildren());
905 maBColorModifierStack
.pop();
909 // unified sub-transparence. Draw to VDev first.
910 void VclProcessor2D::RenderUnifiedTransparencePrimitive2D(
911 const primitive2d::UnifiedTransparencePrimitive2D
& rTransCandidate
)
913 if (rTransCandidate
.getChildren().empty())
916 if (0.0 == rTransCandidate
.getTransparence())
918 // no transparence used, so just use the content
919 process(rTransCandidate
.getChildren());
921 else if (rTransCandidate
.getTransparence() > 0.0 && rTransCandidate
.getTransparence() < 1.0)
923 // transparence is in visible range
924 basegfx::B2DRange
aRange(rTransCandidate
.getChildren().getB2DRange(getViewInformation2D()));
925 aRange
.transform(maCurrentTransformation
);
926 impBufferDevice
aBufferDevice(*mpOutputDevice
, aRange
);
928 if (aBufferDevice
.isVisible())
930 // remember last OutDev and set to content
931 OutputDevice
* pLastOutputDevice
= mpOutputDevice
;
932 mpOutputDevice
= &aBufferDevice
.getContent();
934 // paint content to it
935 process(rTransCandidate
.getChildren());
937 // back to old OutDev
938 mpOutputDevice
= pLastOutputDevice
;
940 // dump buffer to outdev using given transparence
941 aBufferDevice
.paint(rTransCandidate
.getTransparence());
946 // sub-transparence group. Draw to VDev first.
947 void VclProcessor2D::RenderTransparencePrimitive2D(
948 const primitive2d::TransparencePrimitive2D
& rTransCandidate
)
950 if (rTransCandidate
.getChildren().empty())
953 basegfx::B2DRange
aRange(rTransCandidate
.getChildren().getB2DRange(getViewInformation2D()));
954 aRange
.transform(maCurrentTransformation
);
955 impBufferDevice
aBufferDevice(*mpOutputDevice
, aRange
);
957 if (!aBufferDevice
.isVisible())
960 // remember last OutDev and set to content
961 OutputDevice
* pLastOutputDevice
= mpOutputDevice
;
962 mpOutputDevice
= &aBufferDevice
.getContent();
964 // paint content to it
965 process(rTransCandidate
.getChildren());
968 mpOutputDevice
= &aBufferDevice
.getTransparence();
970 // when painting transparence masks, reset the color stack
971 basegfx::BColorModifierStack
aLastBColorModifierStack(maBColorModifierStack
);
972 maBColorModifierStack
= basegfx::BColorModifierStack();
974 // paint mask to it (always with transparence intensities, evtl. with AA)
975 process(rTransCandidate
.getTransparence());
977 // back to old color stack
978 maBColorModifierStack
= aLastBColorModifierStack
;
980 // back to old OutDev
981 mpOutputDevice
= pLastOutputDevice
;
983 // dump buffer to outdev
984 aBufferDevice
.paint();
988 void VclProcessor2D::RenderTransformPrimitive2D(
989 const primitive2d::TransformPrimitive2D
& rTransformCandidate
)
991 // remember current transformation and ViewInformation
992 const basegfx::B2DHomMatrix
aLastCurrentTransformation(maCurrentTransformation
);
993 const geometry::ViewInformation2D
aLastViewInformation2D(getViewInformation2D());
995 // create new transformations for CurrentTransformation
996 // and for local ViewInformation2D
997 maCurrentTransformation
= maCurrentTransformation
* rTransformCandidate
.getTransformation();
998 geometry::ViewInformation2D
aViewInformation2D(getViewInformation2D());
999 aViewInformation2D
.setObjectTransformation(getViewInformation2D().getObjectTransformation()
1000 * rTransformCandidate
.getTransformation());
1001 updateViewInformation(aViewInformation2D
);
1004 process(rTransformCandidate
.getChildren());
1006 // restore transformations
1007 maCurrentTransformation
= aLastCurrentTransformation
;
1008 updateViewInformation(aLastViewInformation2D
);
1011 // new XDrawPage for ViewInformation2D
1012 void VclProcessor2D::RenderPagePreviewPrimitive2D(
1013 const primitive2d::PagePreviewPrimitive2D
& rPagePreviewCandidate
)
1015 // remember current transformation and ViewInformation
1016 const geometry::ViewInformation2D
aLastViewInformation2D(getViewInformation2D());
1018 // create new local ViewInformation2D
1019 geometry::ViewInformation2D
aViewInformation2D(getViewInformation2D());
1020 aViewInformation2D
.setVisualizedPage(rPagePreviewCandidate
.getXDrawPage());
1021 updateViewInformation(aViewInformation2D
);
1023 // process decomposed content
1024 process(rPagePreviewCandidate
);
1026 // restore transformations
1027 updateViewInformation(aLastViewInformation2D
);
1031 void VclProcessor2D::RenderMarkerArrayPrimitive2D(
1032 const primitive2d::MarkerArrayPrimitive2D
& rMarkArrayCandidate
)
1035 const std::vector
<basegfx::B2DPoint
>& rPositions
= rMarkArrayCandidate
.getPositions();
1036 const sal_uInt32
nCount(rPositions
.size());
1038 if (!nCount
|| rMarkArrayCandidate
.getMarker().IsEmpty())
1042 const BitmapEx
& rMarker(rMarkArrayCandidate
.getMarker());
1043 const Size
aBitmapSize(rMarker
.GetSizePixel());
1045 if (!(aBitmapSize
.Width() && aBitmapSize
.Height()))
1048 // get discrete half size
1049 const basegfx::B2DVector
aDiscreteHalfSize((aBitmapSize
.getWidth() - 1.0) * 0.5,
1050 (aBitmapSize
.getHeight() - 1.0) * 0.5);
1051 const bool bWasEnabled(mpOutputDevice
->IsMapModeEnabled());
1053 // do not forget evtl. moved origin in target device MapMode when
1054 // switching it off; it would be missing and lead to wrong positions.
1055 // All his could be done using logic sizes and coordinates, too, but
1056 // we want a 1:1 bitmap rendering here, so it's more safe and faster
1057 // to work with switching off MapMode usage completely.
1058 const Point
aOrigin(mpOutputDevice
->GetMapMode().GetOrigin());
1060 mpOutputDevice
->EnableMapMode(false);
1062 for (auto const& pos
: rPositions
)
1064 const basegfx::B2DPoint
aDiscreteTopLeft((maCurrentTransformation
* pos
)
1065 - aDiscreteHalfSize
);
1066 const Point
aDiscretePoint(basegfx::fround(aDiscreteTopLeft
.getX()),
1067 basegfx::fround(aDiscreteTopLeft
.getY()));
1069 mpOutputDevice
->DrawBitmapEx(aDiscretePoint
+ aOrigin
, rMarker
);
1072 mpOutputDevice
->EnableMapMode(bWasEnabled
);
1076 void VclProcessor2D::RenderPointArrayPrimitive2D(
1077 const primitive2d::PointArrayPrimitive2D
& rPointArrayCandidate
)
1079 const std::vector
<basegfx::B2DPoint
>& rPositions
= rPointArrayCandidate
.getPositions();
1080 const basegfx::BColor
aRGBColor(
1081 maBColorModifierStack
.getModifiedColor(rPointArrayCandidate
.getRGBColor()));
1082 const Color
aVCLColor(aRGBColor
);
1084 for (auto const& pos
: rPositions
)
1086 const basegfx::B2DPoint
aViewPosition(maCurrentTransformation
* pos
);
1087 const Point
aPos(basegfx::fround(aViewPosition
.getX()),
1088 basegfx::fround(aViewPosition
.getY()));
1090 mpOutputDevice
->DrawPixel(aPos
, aVCLColor
);
1094 void VclProcessor2D::RenderPolygonStrokePrimitive2D(
1095 const primitive2d::PolygonStrokePrimitive2D
& rPolygonStrokeCandidate
)
1097 // #i101491# method restructured to clearly use the DrawPolyLine
1098 // calls starting from a defined line width
1099 const attribute::LineAttribute
& rLineAttribute
= rPolygonStrokeCandidate
.getLineAttribute();
1100 const double fLineWidth(rLineAttribute
.getWidth());
1103 if (basegfx::fTools::more(fLineWidth
, 0.0))
1105 const basegfx::B2DVector
aDiscreteUnit(maCurrentTransformation
1106 * basegfx::B2DVector(fLineWidth
, 0.0));
1107 const double fDiscreteLineWidth(aDiscreteUnit
.getLength());
1108 const attribute::StrokeAttribute
& rStrokeAttribute
1109 = rPolygonStrokeCandidate
.getStrokeAttribute();
1110 const basegfx::BColor
aHairlineColor(
1111 maBColorModifierStack
.getModifiedColor(rLineAttribute
.getColor()));
1112 basegfx::B2DPolyPolygon aHairlinePolyPolygon
;
1114 mpOutputDevice
->SetLineColor(Color(aHairlineColor
));
1115 mpOutputDevice
->SetFillColor();
1117 if (0.0 == rStrokeAttribute
.getFullDotDashLen())
1119 // no line dashing, just copy
1120 aHairlinePolyPolygon
.append(rPolygonStrokeCandidate
.getB2DPolygon());
1124 // else apply LineStyle
1125 basegfx::utils::applyLineDashing(
1126 rPolygonStrokeCandidate
.getB2DPolygon(), rStrokeAttribute
.getDotDashArray(),
1127 &aHairlinePolyPolygon
, nullptr, rStrokeAttribute
.getFullDotDashLen());
1130 const sal_uInt32
nCount(aHairlinePolyPolygon
.count());
1134 const bool bAntiAliased(getViewInformation2D().getUseAntiAliasing());
1135 aHairlinePolyPolygon
.transform(maCurrentTransformation
);
1139 if (basegfx::fTools::lessOrEqual(fDiscreteLineWidth
, 1.0))
1141 // line in range ]0.0 .. 1.0[
1142 // paint as simple hairline
1143 for (sal_uInt32
a(0); a
< nCount
; a
++)
1145 mpOutputDevice
->DrawPolyLine(aHairlinePolyPolygon
.getB2DPolygon(a
), 0.0);
1150 else if (basegfx::fTools::lessOrEqual(fDiscreteLineWidth
, 2.0))
1152 // line in range [1.0 .. 2.0[
1153 // paint as 2x2 with dynamic line distance
1154 basegfx::B2DHomMatrix aMat
;
1155 const double fDistance(fDiscreteLineWidth
- 1.0);
1156 const double fHalfDistance(fDistance
* 0.5);
1158 for (sal_uInt32
a(0); a
< nCount
; a
++)
1160 basegfx::B2DPolygon
aCandidate(aHairlinePolyPolygon
.getB2DPolygon(a
));
1162 aMat
.set(0, 2, -fHalfDistance
);
1163 aMat
.set(1, 2, -fHalfDistance
);
1164 aCandidate
.transform(aMat
);
1165 mpOutputDevice
->DrawPolyLine(aCandidate
, 0.0);
1167 aMat
.set(0, 2, fDistance
);
1168 aMat
.set(1, 2, 0.0);
1169 aCandidate
.transform(aMat
);
1170 mpOutputDevice
->DrawPolyLine(aCandidate
, 0.0);
1172 aMat
.set(0, 2, 0.0);
1173 aMat
.set(1, 2, fDistance
);
1174 aCandidate
.transform(aMat
);
1175 mpOutputDevice
->DrawPolyLine(aCandidate
, 0.0);
1177 aMat
.set(0, 2, -fDistance
);
1178 aMat
.set(1, 2, 0.0);
1179 aCandidate
.transform(aMat
);
1180 mpOutputDevice
->DrawPolyLine(aCandidate
, 0.0);
1185 else if (basegfx::fTools::lessOrEqual(fDiscreteLineWidth
, 3.0))
1187 // line in range [2.0 .. 3.0]
1188 // paint as cross in a 3x3 with dynamic line distance
1189 basegfx::B2DHomMatrix aMat
;
1190 const double fDistance((fDiscreteLineWidth
- 1.0) * 0.5);
1192 for (sal_uInt32
a(0); a
< nCount
; a
++)
1194 basegfx::B2DPolygon
aCandidate(aHairlinePolyPolygon
.getB2DPolygon(a
));
1196 mpOutputDevice
->DrawPolyLine(aCandidate
, 0.0);
1198 aMat
.set(0, 2, -fDistance
);
1199 aMat
.set(1, 2, 0.0);
1200 aCandidate
.transform(aMat
);
1201 mpOutputDevice
->DrawPolyLine(aCandidate
, 0.0);
1203 aMat
.set(0, 2, fDistance
);
1204 aMat
.set(1, 2, -fDistance
);
1205 aCandidate
.transform(aMat
);
1206 mpOutputDevice
->DrawPolyLine(aCandidate
, 0.0);
1208 aMat
.set(0, 2, fDistance
);
1209 aMat
.set(1, 2, fDistance
);
1210 aCandidate
.transform(aMat
);
1211 mpOutputDevice
->DrawPolyLine(aCandidate
, 0.0);
1213 aMat
.set(0, 2, -fDistance
);
1214 aMat
.set(1, 2, fDistance
);
1215 aCandidate
.transform(aMat
);
1216 mpOutputDevice
->DrawPolyLine(aCandidate
, 0.0);
1223 // #i101491# line width above 3.0
1228 if (basegfx::fTools::lessOrEqual(fDiscreteLineWidth
, 1.5))
1230 // line width below 1.5, draw the basic hairline polygon
1231 for (sal_uInt32
a(0); a
< nCount
; a
++)
1233 mpOutputDevice
->DrawPolyLine(aHairlinePolyPolygon
.getB2DPolygon(a
), 0.0);
1238 else if (basegfx::fTools::lessOrEqual(fDiscreteLineWidth
, 2.5))
1240 // line width is in range ]1.5 .. 2.5], use four hairlines
1241 // drawn in a square
1242 for (sal_uInt32
a(0); a
< nCount
; a
++)
1244 basegfx::B2DPolygon
aCandidate(aHairlinePolyPolygon
.getB2DPolygon(a
));
1245 basegfx::B2DHomMatrix aMat
;
1247 mpOutputDevice
->DrawPolyLine(aCandidate
, 0.0);
1249 aMat
.set(0, 2, 1.0);
1250 aMat
.set(1, 2, 0.0);
1251 aCandidate
.transform(aMat
);
1253 mpOutputDevice
->DrawPolyLine(aCandidate
, 0.0);
1255 aMat
.set(0, 2, 0.0);
1256 aMat
.set(1, 2, 1.0);
1257 aCandidate
.transform(aMat
);
1259 mpOutputDevice
->DrawPolyLine(aCandidate
, 0.0);
1261 aMat
.set(0, 2, -1.0);
1262 aMat
.set(1, 2, 0.0);
1263 aCandidate
.transform(aMat
);
1265 mpOutputDevice
->DrawPolyLine(aCandidate
, 0.0);
1272 // #i101491# line width is above 2.5
1276 if (!bDone
&& rPolygonStrokeCandidate
.getB2DPolygon().count() > 1000)
1278 // #i101491# If the polygon complexity uses more than a given amount, do
1279 // use OutputDevice::DrawPolyLine directly; this will avoid buffering all
1280 // decompositions in primitives (memory) and fallback to old line painting
1281 // for very complex polygons, too
1282 for (sal_uInt32
a(0); a
< nCount
; a
++)
1284 mpOutputDevice
->DrawPolyLine(aHairlinePolyPolygon
.getB2DPolygon(a
),
1285 fDiscreteLineWidth
, rLineAttribute
.getLineJoin(),
1286 rLineAttribute
.getLineCap(),
1287 rLineAttribute
.getMiterMinimumAngle());
1297 // remember that we enter a PolygonStrokePrimitive2D decomposition,
1298 // used for AA thick line drawing
1299 mnPolygonStrokePrimitive2D
++;
1301 // line width is big enough for standard filled polygon visualisation or zero
1302 process(rPolygonStrokeCandidate
);
1304 // leave PolygonStrokePrimitive2D
1305 mnPolygonStrokePrimitive2D
--;
1309 void VclProcessor2D::RenderEpsPrimitive2D(const primitive2d::EpsPrimitive2D
& rEpsPrimitive2D
)
1311 // The new decomposition of Metafiles made it necessary to add an Eps
1312 // primitive to handle embedded Eps data. On some devices, this can be
1313 // painted directly (mac, printer).
1314 // To be able to handle the replacement correctly, i need to handle it myself
1315 // since DrawEPS will not be able e.g. to rotate the replacement. To be able
1316 // to do that, i added a boolean return to OutputDevice::DrawEPS(..)
1317 // to know when EPS was handled directly already.
1318 basegfx::B2DRange
aRange(0.0, 0.0, 1.0, 1.0);
1319 aRange
.transform(maCurrentTransformation
* rEpsPrimitive2D
.getEpsTransform());
1321 if (aRange
.isEmpty())
1324 const ::tools::Rectangle
aRectangle(static_cast<sal_Int32
>(floor(aRange
.getMinX())),
1325 static_cast<sal_Int32
>(floor(aRange
.getMinY())),
1326 static_cast<sal_Int32
>(ceil(aRange
.getMaxX())),
1327 static_cast<sal_Int32
>(ceil(aRange
.getMaxY())));
1329 if (aRectangle
.IsEmpty())
1332 bool bWillReallyRender
= mpOutputDevice
->IsDeviceOutputNecessary();
1333 // try to paint EPS directly without fallback visualisation
1334 const bool bEPSPaintedDirectly
1336 && mpOutputDevice
->DrawEPS(aRectangle
.TopLeft(), aRectangle
.GetSize(),
1337 rEpsPrimitive2D
.getGfxLink());
1339 if (!bEPSPaintedDirectly
)
1341 // use the decomposition which will correctly handle the
1342 // fallback visualisation using full transformation (e.g. rotation)
1343 process(rEpsPrimitive2D
);
1347 void VclProcessor2D::RenderSvgLinearAtomPrimitive2D(
1348 const primitive2d::SvgLinearAtomPrimitive2D
& rCandidate
)
1350 const double fDelta(rCandidate
.getOffsetB() - rCandidate
.getOffsetA());
1352 if (!basegfx::fTools::more(fDelta
, 0.0))
1355 const basegfx::BColor
aColorA(maBColorModifierStack
.getModifiedColor(rCandidate
.getColorA()));
1356 const basegfx::BColor
aColorB(maBColorModifierStack
.getModifiedColor(rCandidate
.getColorB()));
1358 // calculate discrete unit in WorldCoordinates; use diagonal (1.0, 1.0) and divide by sqrt(2)
1359 const basegfx::B2DVector
aDiscreteVector(
1360 getViewInformation2D().getInverseObjectToViewTransformation()
1361 * basegfx::B2DVector(1.0, 1.0));
1362 const double fDiscreteUnit(aDiscreteVector
.getLength() * (1.0 / M_SQRT2
));
1364 // use color distance and discrete lengths to calculate step count
1365 const sal_uInt32
nSteps(calculateStepsForSvgGradient(aColorA
, aColorB
, fDelta
, fDiscreteUnit
));
1367 // switch off line painting
1368 mpOutputDevice
->SetLineColor();
1370 // prepare polygon in needed width at start position (with discrete overlap)
1371 const basegfx::B2DPolygon
aPolygon(basegfx::utils::createPolygonFromRect(
1372 basegfx::B2DRange(rCandidate
.getOffsetA() - fDiscreteUnit
, 0.0,
1373 rCandidate
.getOffsetA() + (fDelta
/ nSteps
) + fDiscreteUnit
, 1.0)));
1375 // prepare loop ([0.0 .. 1.0[)
1376 double fUnitScale(0.0);
1377 const double fUnitStep(1.0 / nSteps
);
1380 for (sal_uInt32
a(0); a
< nSteps
; a
++, fUnitScale
+= fUnitStep
)
1382 basegfx::B2DPolygon
aNew(aPolygon
);
1384 aNew
.transform(maCurrentTransformation
1385 * basegfx::utils::createTranslateB2DHomMatrix(fDelta
* fUnitScale
, 0.0));
1386 mpOutputDevice
->SetFillColor(Color(basegfx::interpolate(aColorA
, aColorB
, fUnitScale
)));
1387 mpOutputDevice
->DrawPolyPolygon(basegfx::B2DPolyPolygon(aNew
));
1391 void VclProcessor2D::RenderSvgRadialAtomPrimitive2D(
1392 const primitive2d::SvgRadialAtomPrimitive2D
& rCandidate
)
1394 const double fDeltaScale(rCandidate
.getScaleB() - rCandidate
.getScaleA());
1396 if (!basegfx::fTools::more(fDeltaScale
, 0.0))
1399 const basegfx::BColor
aColorA(maBColorModifierStack
.getModifiedColor(rCandidate
.getColorA()));
1400 const basegfx::BColor
aColorB(maBColorModifierStack
.getModifiedColor(rCandidate
.getColorB()));
1402 // calculate discrete unit in WorldCoordinates; use diagonal (1.0, 1.0) and divide by sqrt(2)
1403 const basegfx::B2DVector
aDiscreteVector(
1404 getViewInformation2D().getInverseObjectToViewTransformation()
1405 * basegfx::B2DVector(1.0, 1.0));
1406 const double fDiscreteUnit(aDiscreteVector
.getLength() * (1.0 / M_SQRT2
));
1408 // use color distance and discrete lengths to calculate step count
1409 const sal_uInt32
nSteps(
1410 calculateStepsForSvgGradient(aColorA
, aColorB
, fDeltaScale
, fDiscreteUnit
));
1412 // switch off line painting
1413 mpOutputDevice
->SetLineColor();
1415 // prepare loop ([0.0 .. 1.0[, full polygons, no polypolygons with holes)
1416 double fUnitScale(0.0);
1417 const double fUnitStep(1.0 / nSteps
);
1419 for (sal_uInt32
a(0); a
< nSteps
; a
++, fUnitScale
+= fUnitStep
)
1421 basegfx::B2DHomMatrix aTransform
;
1422 const double fEndScale(rCandidate
.getScaleB() - (fDeltaScale
* fUnitScale
));
1424 if (rCandidate
.isTranslateSet())
1426 const basegfx::B2DVector
aTranslate(basegfx::interpolate(
1427 rCandidate
.getTranslateB(), rCandidate
.getTranslateA(), fUnitScale
));
1429 aTransform
= basegfx::utils::createScaleTranslateB2DHomMatrix(
1430 fEndScale
, fEndScale
, aTranslate
.getX(), aTranslate
.getY());
1434 aTransform
= basegfx::utils::createScaleB2DHomMatrix(fEndScale
, fEndScale
);
1437 basegfx::B2DPolygon
aNew(basegfx::utils::createPolygonFromUnitCircle());
1439 aNew
.transform(maCurrentTransformation
* aTransform
);
1440 mpOutputDevice
->SetFillColor(Color(basegfx::interpolate(aColorB
, aColorA
, fUnitScale
)));
1441 mpOutputDevice
->DrawPolyPolygon(basegfx::B2DPolyPolygon(aNew
));
1445 void VclProcessor2D::adaptLineToFillDrawMode() const
1447 const DrawModeFlags
nOriginalDrawMode(mpOutputDevice
->GetDrawMode());
1449 if (!(nOriginalDrawMode
1450 & (DrawModeFlags::BlackLine
| DrawModeFlags::GrayLine
| DrawModeFlags::WhiteLine
1451 | DrawModeFlags::SettingsLine
)))
1454 DrawModeFlags
nAdaptedDrawMode(nOriginalDrawMode
);
1456 if (nOriginalDrawMode
& DrawModeFlags::BlackLine
)
1458 nAdaptedDrawMode
|= DrawModeFlags::BlackFill
;
1462 nAdaptedDrawMode
&= ~DrawModeFlags::BlackFill
;
1465 if (nOriginalDrawMode
& DrawModeFlags::GrayLine
)
1467 nAdaptedDrawMode
|= DrawModeFlags::GrayFill
;
1471 nAdaptedDrawMode
&= ~DrawModeFlags::GrayFill
;
1474 if (nOriginalDrawMode
& DrawModeFlags::WhiteLine
)
1476 nAdaptedDrawMode
|= DrawModeFlags::WhiteFill
;
1480 nAdaptedDrawMode
&= ~DrawModeFlags::WhiteFill
;
1483 if (nOriginalDrawMode
& DrawModeFlags::SettingsLine
)
1485 nAdaptedDrawMode
|= DrawModeFlags::SettingsFill
;
1489 nAdaptedDrawMode
&= ~DrawModeFlags::SettingsFill
;
1492 mpOutputDevice
->SetDrawMode(nAdaptedDrawMode
);
1495 void VclProcessor2D::adaptTextToFillDrawMode() const
1497 const DrawModeFlags
nOriginalDrawMode(mpOutputDevice
->GetDrawMode());
1498 if (!(nOriginalDrawMode
1499 & (DrawModeFlags::BlackText
| DrawModeFlags::GrayText
| DrawModeFlags::WhiteText
1500 | DrawModeFlags::SettingsText
)))
1503 DrawModeFlags
nAdaptedDrawMode(nOriginalDrawMode
);
1505 if (nOriginalDrawMode
& DrawModeFlags::BlackText
)
1507 nAdaptedDrawMode
|= DrawModeFlags::BlackFill
;
1511 nAdaptedDrawMode
&= ~DrawModeFlags::BlackFill
;
1514 if (nOriginalDrawMode
& DrawModeFlags::GrayText
)
1516 nAdaptedDrawMode
|= DrawModeFlags::GrayFill
;
1520 nAdaptedDrawMode
&= ~DrawModeFlags::GrayFill
;
1523 if (nOriginalDrawMode
& DrawModeFlags::WhiteText
)
1525 nAdaptedDrawMode
|= DrawModeFlags::WhiteFill
;
1529 nAdaptedDrawMode
&= ~DrawModeFlags::WhiteFill
;
1532 if (nOriginalDrawMode
& DrawModeFlags::SettingsText
)
1534 nAdaptedDrawMode
|= DrawModeFlags::SettingsFill
;
1538 nAdaptedDrawMode
&= ~DrawModeFlags::SettingsFill
;
1541 mpOutputDevice
->SetDrawMode(nAdaptedDrawMode
);
1546 VclProcessor2D::VclProcessor2D(const geometry::ViewInformation2D
& rViewInformation
,
1547 OutputDevice
& rOutDev
, basegfx::BColorModifierStack aInitStack
)
1548 : BaseProcessor2D(rViewInformation
)
1549 , mpOutputDevice(&rOutDev
)
1550 , maBColorModifierStack(std::move(aInitStack
))
1551 , mnPolygonStrokePrimitive2D(0)
1553 // set digit language, derived from SvtCTLOptions to have the correct
1554 // number display for arabic/hindi numerals
1555 rOutDev
.SetDigitLanguage(drawinglayer::detail::getDigitLanguage());
1558 VclProcessor2D::~VclProcessor2D() {}
1561 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */