nss: upgrade to release 3.73
[LibreOffice.git] / drawinglayer / source / processor2d / vclprocessor2d.cxx
blob80c7fbdb65562f26bf1f729a1b56525ff9d9168f
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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"
24 #include <cmath>
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>
54 // control support
55 #include <drawinglayer/primitive2d/textlayoutdevice.hxx>
57 #include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx>
58 #include <drawinglayer/primitive2d/epsprimitive2d.hxx>
60 using namespace com::sun::star;
62 namespace
64 sal_uInt32 calculateStepsForSvgGradient(const basegfx::BColor& rColorA,
65 const basegfx::BColor& rColorB, double fDelta,
66 double fDiscreteUnit)
68 // use color distance, assume to do every color step
69 sal_uInt32 nSteps(basegfx::fround(rColorA.getDistance(rColorB) * 255.0));
71 if (nSteps)
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
80 nSteps /= 2;
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));
86 return nSteps;
90 namespace drawinglayer::processor2d
92 // rendering support
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
97 // for VCL)
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);
120 fRotate += F_PI;
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)
133 return;
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*>(
146 &rTextCandidate);
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())
189 default:
190 SAL_WARN("drawinglayer",
191 "Unknown EmphasisMark style " << pTCPP->getTextEmphasisMark());
192 [[fallthrough]];
193 case primitive2d::TEXT_FONT_EMPHASIS_MARK_NONE:
194 eFontEmphasisMark = FontEmphasisMark::NONE;
195 break;
196 case primitive2d::TEXT_FONT_EMPHASIS_MARK_DOT:
197 eFontEmphasisMark = FontEmphasisMark::Dot;
198 break;
199 case primitive2d::TEXT_FONT_EMPHASIS_MARK_CIRCLE:
200 eFontEmphasisMark = FontEmphasisMark::Circle;
201 break;
202 case primitive2d::TEXT_FONT_EMPHASIS_MARK_DISC:
203 eFontEmphasisMark = FontEmphasisMark::Disc;
204 break;
205 case primitive2d::TEXT_FONT_EMPHASIS_MARK_ACCENT:
206 eFontEmphasisMark = FontEmphasisMark::Accent;
207 break;
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;
216 else
217 eFontEmphasisMark |= FontEmphasisMark::PosBelow;
218 aFont.SetEmphasisMark(eFontEmphasisMark);
221 // set Relief attribute
222 FontRelief eFontRelief = FontRelief::NONE;
223 switch (pTCPP->getTextRelief())
225 default:
226 SAL_WARN("drawinglayer", "Unknown Relief style " << pTCPP->getTextRelief());
227 [[fallthrough]];
228 case primitive2d::TEXT_RELIEF_NONE:
229 eFontRelief = FontRelief::NONE;
230 break;
231 case primitive2d::TEXT_RELIEF_EMBOSSED:
232 eFontRelief = FontRelief::Embossed;
233 break;
234 case primitive2d::TEXT_RELIEF_ENGRAVED:
235 eFontRelief = FontRelief::Engraved;
236 break;
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);
274 nRTLLayoutMode
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());
299 tools::Long nWidth
300 = mpOutputDevice->GetTextArray(rTextCandidate.getText(), pDXArray, 0, 1);
301 tools::Long nChars = 2;
302 if (nWidth)
303 nChars = nWidthToFill / nWidth;
305 OUStringBuffer aFilled;
306 comphelper::string::padToLength(aFilled, nChars, aText[0]);
307 aText = aFilled.makeStringAndClear();
308 nPos = 0;
309 nLen = nChars;
312 if (!aTransformedDXArray.empty())
314 mpOutputDevice->DrawTextArray(aStartPoint, aText, pDXArray, nPos, nLen);
316 else
318 mpOutputDevice->DrawText(aStartPoint, aText, nPos, nLen);
321 if (rTextCandidate.getFontAttribute().getRTL())
323 mpOutputDevice->SetLayoutMode(nOldLayoutMode);
326 bPrimitiveAccepted = true;
330 if (!bPrimitiveAccepted)
332 // let break down
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())
352 // #i98289#
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);
386 return;
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()
449 * aLocalTransform);
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
468 if (bPreScaled)
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);
502 bPainted = true;
506 if (!bPainted)
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()));
512 sal_Int32 nPosX(0);
513 sal_Int32 nPosY(0);
515 if (nBLeft > nOLeft)
517 const sal_Int32 nDiff((nBLeft / nBWidth) + 1);
519 nPosX -= nDiff;
520 nBLeft -= nDiff * nBWidth;
523 if (nBLeft + nBWidth <= nOLeft)
525 const sal_Int32 nDiff(-nBLeft / nBWidth);
527 nPosX += nDiff;
528 nBLeft += nDiff * nBWidth;
531 if (nBTop > nOTop)
533 const sal_Int32 nDiff((nBTop / nBHeight) + 1);
535 nPosY -= nDiff;
536 nBTop -= nDiff * nBHeight;
539 if (nBTop + nBHeight <= nOTop)
541 const sal_Int32 nDiff(-nBTop / nBHeight);
543 nPosY += nDiff;
544 nBTop += nDiff * nBHeight;
547 // prepare OutDev
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));
558 if (nOffsetX)
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
565 : nBLeft);
566 nXPos < nOLeft + nOWidth; nXPos += nBWidth)
568 const ::tools::Rectangle aOutRectPixel(
569 Point(nXPos, nYPos), aNeededBitmapSizePixel);
571 if (aOutRectPixel.IsOver(aVisiblePixel))
573 if (bPreScaled)
575 mpOutputDevice->DrawBitmapEx(
576 aOutRectPixel.TopLeft(), aBitmapEx);
578 else
580 mpOutputDevice->DrawBitmapEx(
581 aOutRectPixel.TopLeft(), aNeededBitmapSizePixel,
582 aBitmapEx);
588 else
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
599 : nBTop);
600 nYPos < nOTop + nOHeight; nYPos += nBHeight)
602 const ::tools::Rectangle aOutRectPixel(
603 Point(nXPos, nYPos), aNeededBitmapSizePixel);
605 if (aOutRectPixel.IsOver(aVisiblePixel))
607 if (bPreScaled)
609 mpOutputDevice->DrawBitmapEx(
610 aOutRectPixel.TopLeft(), aBitmapEx);
612 else
614 mpOutputDevice->DrawBitmapEx(
615 aOutRectPixel.TopLeft(), aNeededBitmapSizePixel,
616 aBitmapEx);
623 // restore OutDev
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)
643 bool bDone(false);
644 const basegfx::B2DPolyPolygon& rPolyPolygon = rPolygonCandidate.getB2DPolyPolygon();
646 // #121194# Todo: check if this works
647 if (!rPolyPolygon.count())
649 // empty polyPolygon, done
650 bDone = true;
652 else
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
664 break;
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
678 - 1);
679 const basegfx::BColorModifier_replace* pReplacer
680 = dynamic_cast<const basegfx::BColorModifier_replace*>(
681 rTopmostModifier.get());
683 if (pReplacer)
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);
697 else
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);
722 if (aTarget.count())
724 aTarget.transform(maCurrentTransformation);
725 mpOutputDevice->SetLineColor();
726 mpOutputDevice->SetFillColor(Color(pReplacer->getBColor()));
727 mpOutputDevice->DrawPolyPolygon(aTarget);
731 // simplified output executed, we are done
732 bDone = true;
736 break;
738 default: //GraphicType::NONE, GraphicType::Default
740 // empty graphic, we are done
741 bDone = true;
742 break;
747 if (!bDone)
749 // use default decomposition
750 process(rPolygonCandidate);
754 // mask group
755 void VclProcessor2D::RenderMaskPrimitive2DPixel(const primitive2d::MaskPrimitive2D& rMaskCandidate)
757 if (rMaskCandidate.getChildren().empty())
758 return;
760 basegfx::B2DPolyPolygon aMask(rMaskCandidate.getMask());
762 if (!aMask.count())
763 return;
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();
774 return;
777 const basegfx::B2DRange aRange(basegfx::utils::getRange(aMask));
778 impBufferDevice aBufferDevice(*mpOutputDevice, aRange);
780 if (!aBufferDevice.isVisible())
781 return;
783 // remember last OutDev and set to content
784 OutputDevice* pLastOutputDevice = mpOutputDevice;
785 mpOutputDevice = &aBufferDevice.getContent();
787 // paint to it
788 process(rMaskCandidate.getChildren());
790 // back to old OutDev
791 mpOutputDevice = pLastOutputDevice;
793 // draw mask
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())
820 return;
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())
857 return;
859 basegfx::B2DRange aRange(rTransCandidate.getChildren().getB2DRange(getViewInformation2D()));
860 aRange.transform(maCurrentTransformation);
861 impBufferDevice aBufferDevice(*mpOutputDevice, aRange);
863 if (!aBufferDevice.isVisible())
864 return;
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());
873 // set to mask
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();
893 // transform group.
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);
911 // process content
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);
941 // marker
942 void VclProcessor2D::RenderMarkerArrayPrimitive2D(
943 const primitive2d::MarkerArrayPrimitive2D& rMarkArrayCandidate)
945 // get data
946 const std::vector<basegfx::B2DPoint>& rPositions = rMarkArrayCandidate.getPositions();
947 const sal_uInt32 nCount(rPositions.size());
949 if (!nCount || rMarkArrayCandidate.getMarker().IsEmpty())
950 return;
952 // get pixel size
953 const BitmapEx& rMarker(rMarkArrayCandidate.getMarker());
954 const Size aBitmapSize(rMarker.GetSizePixel());
956 if (!(aBitmapSize.Width() && aBitmapSize.Height()))
957 return;
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);
986 // point
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());
1012 bool bDone(false);
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());
1033 else
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());
1043 if (nCount)
1045 const bool bAntiAliased(getOptionsDrawinglayer().IsAntiAliasing());
1046 aHairlinePolyPolygon.transform(maCurrentTransformation);
1048 if (bAntiAliased)
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);
1059 bDone = true;
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);
1094 bDone = true;
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);
1130 bDone = true;
1132 else
1134 // #i101491# line width above 3.0
1137 else
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);
1147 bDone = true;
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);
1179 bDone = true;
1181 else
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());
1201 bDone = true;
1206 if (!bDone)
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())
1233 return;
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())
1241 return;
1243 bool bWillReallyRender = mpOutputDevice->IsDeviceOutputNecessary();
1244 // try to paint EPS directly without fallback visualisation
1245 const bool bEPSPaintedDirectly
1246 = bWillReallyRender
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;
1265 // process content
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))
1278 return;
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);
1304 // loop and paint
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))
1322 return;
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());
1357 else
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)))
1377 return;
1379 DrawModeFlags nAdaptedDrawMode(nOriginalDrawMode);
1381 if (nOriginalDrawMode & DrawModeFlags::BlackLine)
1383 nAdaptedDrawMode |= DrawModeFlags::BlackFill;
1385 else
1387 nAdaptedDrawMode &= ~DrawModeFlags::BlackFill;
1390 if (nOriginalDrawMode & DrawModeFlags::GrayLine)
1392 nAdaptedDrawMode |= DrawModeFlags::GrayFill;
1394 else
1396 nAdaptedDrawMode &= ~DrawModeFlags::GrayFill;
1399 if (nOriginalDrawMode & DrawModeFlags::WhiteLine)
1401 nAdaptedDrawMode |= DrawModeFlags::WhiteFill;
1403 else
1405 nAdaptedDrawMode &= ~DrawModeFlags::WhiteFill;
1408 if (nOriginalDrawMode & DrawModeFlags::SettingsLine)
1410 nAdaptedDrawMode |= DrawModeFlags::SettingsFill;
1412 else
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)))
1426 return;
1428 DrawModeFlags nAdaptedDrawMode(nOriginalDrawMode);
1430 if (nOriginalDrawMode & DrawModeFlags::BlackText)
1432 nAdaptedDrawMode |= DrawModeFlags::BlackFill;
1434 else
1436 nAdaptedDrawMode &= ~DrawModeFlags::BlackFill;
1439 if (nOriginalDrawMode & DrawModeFlags::GrayText)
1441 nAdaptedDrawMode |= DrawModeFlags::GrayFill;
1443 else
1445 nAdaptedDrawMode &= ~DrawModeFlags::GrayFill;
1448 if (nOriginalDrawMode & DrawModeFlags::WhiteText)
1450 nAdaptedDrawMode |= DrawModeFlags::WhiteFill;
1452 else
1454 nAdaptedDrawMode &= ~DrawModeFlags::WhiteFill;
1457 if (nOriginalDrawMode & DrawModeFlags::SettingsText)
1459 nAdaptedDrawMode |= DrawModeFlags::SettingsFill;
1461 else
1463 nAdaptedDrawMode &= ~DrawModeFlags::SettingsFill;
1466 mpOutputDevice->SetDrawMode(nAdaptedDrawMode);
1469 // process support
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: */