LibreOffice Module drawinglayer (master) 1
vclprocessor2d.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20#include "vclprocessor2d.hxx"
21
22#include "getdigitlanguage.hxx"
24#include <cmath>
25#include <comphelper/string.hxx>
27#include <tools/debug.hxx>
28#include <tools/fract.hxx>
29#include <utility>
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>
59// for support of Title/Description in all apps when embedding pictures
61// control support
63
66
67using namespace com::sun::star;
68
69namespace
70{
71sal_uInt32 calculateStepsForSvgGradient(const basegfx::BColor& rColorA,
72 const basegfx::BColor& rColorB, double fDelta,
73 double fDiscreteUnit)
74{
75 // use color distance, assume to do every color step
76 sal_uInt32 nSteps(basegfx::fround(rColorA.getDistance(rColorB) * 255.0));
77
78 if (nSteps)
79 {
80 // calc discrete length to change color each discrete unit (pixel)
81 const sal_uInt32 nDistSteps(basegfx::fround(fDelta / fDiscreteUnit));
82
83 nSteps = std::min(nSteps, nDistSteps);
84 }
85
86 // reduce quality to 3 discrete units or every 3rd color step for rendering
87 nSteps /= 2;
88
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));
92
93 return nSteps;
94}
95}
96
97namespace
98{
100basegfx::B2DHomMatrix getTransformFromMapMode(const MapMode& rMapMode)
101{
102 basegfx::B2DHomMatrix aMapping;
103 const Fraction aNoScale(1, 1);
104 const Point& rOrigin(rMapMode.GetOrigin());
105
106 if (0 != rOrigin.X() || 0 != rOrigin.Y())
107 {
108 aMapping.translate(rOrigin.X(), rOrigin.Y());
109 }
110
111 if (rMapMode.GetScaleX() != aNoScale || rMapMode.GetScaleY() != aNoScale)
112 {
113 aMapping.scale(double(rMapMode.GetScaleX()), double(rMapMode.GetScaleY()));
114 }
115
116 return aMapping;
117}
118}
119
121{
122// rendering support
123
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
127// for VCL)
129 const primitive2d::TextSimplePortionPrimitive2D& rTextCandidate)
130{
131 // decompose matrix to have position and size of text
133 * rTextCandidate.getTextTransform());
134 basegfx::B2DVector aFontScaling, aTranslate;
135 double fRotate, fShearX;
136 aLocalTransform.decompose(aFontScaling, aTranslate, fRotate, fShearX);
137
138 bool bPrimitiveAccepted(false);
139
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)
143 {
144 if (basegfx::fTools::less(aFontScaling.getX(), 0.0)
145 && basegfx::fTools::less(aFontScaling.getY(), 0.0))
146 {
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);
151 fRotate += M_PI;
152 }
153
154 if (basegfx::fTools::more(aFontScaling.getX(), 0.0)
155 && basegfx::fTools::more(aFontScaling.getY(), 0.0))
156 {
157 double fIgnoreRotate, fIgnoreShearX;
158
159 basegfx::B2DVector aFontSize, aTextTranslate;
160 rTextCandidate.getTextTransform().decompose(aFontSize, aTextTranslate, fIgnoreRotate,
161 fIgnoreShearX);
162
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()));
166 vcl::Font aFont;
167
168 // Get the VCL font
169 if (!bScaleFont)
170 {
172 rTextCandidate.getFontAttribute(), aFontSize.getX(), aFontSize.getY(), fRotate,
173 rTextCandidate.getLocale());
174 }
175 else
176 {
178 rTextCandidate.getFontAttribute(), aFontScaling.getX(), aFontScaling.getY(),
179 fRotate, rTextCandidate.getLocale());
180 }
181
182 // Don't draw fonts without height
183 if (aFont.GetFontHeight() <= 0)
184 return;
185
186 // set FillColor Attribute
187 const Color aFillColor(rTextCandidate.getTextFillColor());
188 aFont.SetTransparent(aFillColor.IsTransparent());
189 aFont.SetFillColor(aFillColor);
190
191 // handle additional font attributes
194 pTCPP = static_cast<const primitive2d::TextDecoratedPortionPrimitive2D*>(
195 &rTextCandidate);
196
197 if (pTCPP != nullptr)
198 {
199 // set the color of text decorations
200 const basegfx::BColor aTextlineColor
202 mpOutputDevice->SetTextLineColor(Color(aTextlineColor));
203
204 // set Overline attribute
205 const FontLineStyle eFontOverline(
207 if (eFontOverline != LINESTYLE_NONE)
208 {
209 aFont.SetOverline(eFontOverline);
210 const basegfx::BColor aOverlineColor
212 mpOutputDevice->SetOverlineColor(Color(aOverlineColor));
213 if (pTCPP->getWordLineMode())
214 aFont.SetWordLineMode(true);
215 }
216
217 // set Underline attribute
218 const FontLineStyle eFontLineStyle(
220 if (eFontLineStyle != LINESTYLE_NONE)
221 {
222 aFont.SetUnderline(eFontLineStyle);
223 if (pTCPP->getWordLineMode())
224 aFont.SetWordLineMode(true);
225 }
226
227 // set Strikeout attribute
228 const FontStrikeout eFontStrikeout(
230
231 if (eFontStrikeout != STRIKEOUT_NONE)
232 aFont.SetStrikeout(eFontStrikeout);
233
234 // set EmphasisMark attribute
235 FontEmphasisMark eFontEmphasisMark = FontEmphasisMark::NONE;
236 switch (pTCPP->getTextEmphasisMark())
237 {
238 default:
239 SAL_WARN("drawinglayer",
240 "Unknown EmphasisMark style " << pTCPP->getTextEmphasisMark());
241 [[fallthrough]];
243 eFontEmphasisMark = FontEmphasisMark::NONE;
244 break;
246 eFontEmphasisMark = FontEmphasisMark::Dot;
247 break;
249 eFontEmphasisMark = FontEmphasisMark::Circle;
250 break;
252 eFontEmphasisMark = FontEmphasisMark::Disc;
253 break;
255 eFontEmphasisMark = FontEmphasisMark::Accent;
256 break;
257 }
258
259 if (eFontEmphasisMark != FontEmphasisMark::NONE)
260 {
262 "DrawingLayer: Bad EmphasisMark position!");
263 if (pTCPP->getEmphasisMarkAbove())
264 eFontEmphasisMark |= FontEmphasisMark::PosAbove;
265 else
266 eFontEmphasisMark |= FontEmphasisMark::PosBelow;
267 aFont.SetEmphasisMark(eFontEmphasisMark);
268 }
269
270 // set Relief attribute
271 FontRelief eFontRelief = FontRelief::NONE;
272 switch (pTCPP->getTextRelief())
273 {
274 default:
275 SAL_WARN("drawinglayer", "Unknown Relief style " << pTCPP->getTextRelief());
276 [[fallthrough]];
278 eFontRelief = FontRelief::NONE;
279 break;
281 eFontRelief = FontRelief::Embossed;
282 break;
284 eFontRelief = FontRelief::Engraved;
285 break;
286 }
287
288 if (eFontRelief != FontRelief::NONE)
289 aFont.SetRelief(eFontRelief);
290
291 // set Shadow attribute
292 if (pTCPP->getShadow())
293 aFont.SetShadow(true);
294 }
295
296 // create integer DXArray
297 KernArray aDXArray;
298
299 if (!rTextCandidate.getDXArray().empty())
300 {
301 double fPixelVectorFactor(1.0);
302 if (bScaleFont)
303 {
305 * basegfx::B2DVector(1.0, 0.0));
306 fPixelVectorFactor = aPixelVector.getLength();
307 }
308
309 aDXArray.reserve(rTextCandidate.getDXArray().size());
310 for (auto const& elem : rTextCandidate.getDXArray())
311 aDXArray.push_back(basegfx::fround(elem * fPixelVectorFactor));
312 }
313
314 // set parameters and paint text snippet
315 const basegfx::BColor aRGBFontColor(
317 const vcl::text::ComplexTextLayoutFlags nOldLayoutMode(mpOutputDevice->GetLayoutMode());
318
319 if (rTextCandidate.getFontAttribute().getRTL())
320 {
325 mpOutputDevice->SetLayoutMode(nRTLLayoutMode);
326 }
327
328 mpOutputDevice->SetTextColor(Color(aRGBFontColor));
329
330 OUString aText(rTextCandidate.getText());
331 sal_Int32 nPos = rTextCandidate.getTextPosition();
332 sal_Int32 nLen = rTextCandidate.getTextLength();
333
334 // this contraption is used in editeng, with format paragraph used to
335 // set a tab with a tab-fill character
336 if (rTextCandidate.isFilled())
337 {
338 tools::Long nWidthToFill = rTextCandidate.getWidthToFill();
339
340 tools::Long nWidth
341 = mpOutputDevice->GetTextArray(rTextCandidate.getText(), &aDXArray, 0, 1);
342 sal_Int32 nChars = 2;
343 if (nWidth)
344 nChars = nWidthToFill / nWidth;
345
346 OUStringBuffer aFilled(nChars);
347 comphelper::string::padToLength(aFilled, nChars, aText[0]);
348 aText = aFilled.makeStringAndClear();
349 nPos = 0;
350 nLen = nChars;
351
352 if (!aDXArray.empty())
353 {
354 sal_Int32 nDX = aDXArray[0];
355 aDXArray.resize(nLen);
356 for (sal_Int32 i = 1; i < nLen; ++i)
357 aDXArray.set(i, aDXArray[i - 1] + nDX);
358 }
359 }
360
361 Point aStartPoint;
362 bool bChangeMapMode(false);
363 if (!bScaleFont)
364 {
365 basegfx::B2DHomMatrix aCombinedTransform(
366 getTransformFromMapMode(mpOutputDevice->GetMapMode())
368
369 basegfx::B2DVector aCurrentScaling, aCurrentTranslate;
370 double fCurrentRotate;
371 aCombinedTransform.decompose(aCurrentScaling, aCurrentTranslate, fCurrentRotate,
372 fIgnoreShearX);
373
374 const Point aOrigin(
375 basegfx::fround(aCurrentTranslate.getX() / aCurrentScaling.getX()),
376 basegfx::fround(aCurrentTranslate.getY() / aCurrentScaling.getY()));
377
378 Fraction aScaleX(aCurrentScaling.getX());
379 if (!aScaleX.IsValid())
380 {
381 SAL_WARN("drawinglayer", "invalid X Scale");
382 return;
383 }
384
385 Fraction aScaleY(aCurrentScaling.getY());
386 if (!aScaleY.IsValid())
387 {
388 SAL_WARN("drawinglayer", "invalid Y Scale");
389 return;
390 }
391
392 MapMode aMapMode(mpOutputDevice->GetMapMode().GetMapUnit(), aOrigin, aScaleX,
393 aScaleY);
394
395 if (fCurrentRotate)
396 aTextTranslate *= basegfx::utils::createRotateB2DHomMatrix(fCurrentRotate);
397 aStartPoint = Point(aTextTranslate.getX(), aTextTranslate.getY());
398
399 bChangeMapMode = aMapMode != mpOutputDevice->GetMapMode();
400 if (bChangeMapMode)
401 {
403 mpOutputDevice->SetRelativeMapMode(aMapMode);
404 }
405 }
406 else
407 {
408 const basegfx::B2DPoint aPoint(aLocalTransform * basegfx::B2DPoint(0.0, 0.0));
409 aStartPoint = Point(basegfx::fround(aPoint.getX()), basegfx::fround(aPoint.getY()));
410 }
411
412 // tdf#152990 set the font after the MapMode is (potentially) set so canvas uses the desired
413 // font size
414 mpOutputDevice->SetFont(aFont);
415
416 if (!aDXArray.empty())
417 {
419 mpOutputDevice, aText, nPos, nLen);
420 mpOutputDevice->DrawTextArray(aStartPoint, aText, aDXArray,
421 rTextCandidate.getKashidaArray(), nPos, nLen,
422 SalLayoutFlags::NONE, pGlyphs);
423 }
424 else
425 {
426 mpOutputDevice->DrawText(aStartPoint, aText, nPos, nLen);
427 }
428
429 if (rTextCandidate.getFontAttribute().getRTL())
430 {
431 mpOutputDevice->SetLayoutMode(nOldLayoutMode);
432 }
433
434 if (bChangeMapMode)
435 mpOutputDevice->Pop();
436
437 bPrimitiveAccepted = true;
438 }
439 }
440
441 if (!bPrimitiveAccepted)
442 {
443 // let break down
444 process(rTextCandidate);
445 }
446}
447
448// direct draw of hairline
450 const primitive2d::PolygonHairlinePrimitive2D& rPolygonCandidate, bool bPixelBased)
451{
452 const basegfx::BColor aHairlineColor(
454 mpOutputDevice->SetLineColor(Color(aHairlineColor));
455 mpOutputDevice->SetFillColor();
456
457 basegfx::B2DPolygon aLocalPolygon(rPolygonCandidate.getB2DPolygon());
458 aLocalPolygon.transform(maCurrentTransformation);
459
460 if (bPixelBased && getViewInformation2D().getPixelSnapHairline())
461 {
462 // #i98289#
463 // when a Hairline is painted and AntiAliasing is on the option SnapHorVerLinesToDiscrete
464 // allows to suppress AntiAliasing for pure horizontal or vertical lines. This is done since
465 // not-AntiAliased such lines look more pleasing to the eye (e.g. 2D chart content). This
466 // NEEDS to be done in discrete coordinates, so only useful for pixel based rendering.
467 aLocalPolygon = basegfx::utils::snapPointsOfHorizontalOrVerticalEdges(aLocalPolygon);
468 }
469
470 mpOutputDevice->DrawPolyLine(aLocalPolygon, 0.0);
471}
472
473// direct draw of transformed BitmapEx primitive
475{
476 BitmapEx aBitmapEx(rBitmapCandidate.getBitmap());
478 * rBitmapCandidate.getTransform());
479
481 {
482 aBitmapEx = aBitmapEx.ModifyBitmapEx(maBColorModifierStack);
483
484 if (aBitmapEx.IsEmpty())
485 {
486 // color gets completely replaced, get it
487 const basegfx::BColor aModifiedColor(
490 aPolygon.transform(aLocalTransform);
491
492 mpOutputDevice->SetFillColor(Color(aModifiedColor));
493 mpOutputDevice->SetLineColor();
494 mpOutputDevice->DrawPolygon(aPolygon);
495
496 return;
497 }
498 }
499
500 // #122923# do no longer add Alpha channel here; the right place to do this is when really
501 // the own transformer is used (see OutputDevice::DrawTransformedBitmapEx).
502
503 // draw using OutputDevice'sDrawTransformedBitmapEx
504 mpOutputDevice->DrawTransformedBitmapEx(aLocalTransform, aBitmapEx);
505}
506
508 const primitive2d::FillGraphicPrimitive2D& rFillBitmapCandidate)
509{
510 bool bPrimitiveAccepted = RenderFillGraphicPrimitive2DImpl(rFillBitmapCandidate);
511
512 if (!bPrimitiveAccepted)
513 {
514 // do not accept, use decomposition
515 process(rFillBitmapCandidate);
516 }
517}
518
520 const primitive2d::FillGraphicPrimitive2D& rFillBitmapCandidate)
521{
522 const attribute::FillGraphicAttribute& rFillGraphicAttribute(
523 rFillBitmapCandidate.getFillGraphic());
524
525 // #121194# when tiling is used and content is bitmap-based, do direct tiling in the
526 // renderer on pixel base to ensure tight fitting. Do not do this when
527 // the fill is rotated or sheared.
528 if (!rFillGraphicAttribute.getTiling())
529 return false;
530
531 // content is bitmap(ex)
532 //
533 // for Vector Graphic Data (SVG, EMF+) support, force decomposition when present. This will lead to use
534 // the primitive representation of the vector data directly.
535 //
536 // when graphic is animated, force decomposition to use the correct graphic, else
537 // fill style will not be animated
538 if (GraphicType::Bitmap != rFillGraphicAttribute.getGraphic().GetType()
539 || rFillGraphicAttribute.getGraphic().getVectorGraphicData()
540 || rFillGraphicAttribute.getGraphic().IsAnimated())
541 return false;
542
543 // decompose matrix to check for shear, rotate and mirroring
545 * rFillBitmapCandidate.getTransformation());
546 basegfx::B2DVector aScale, aTranslate;
547 double fRotate, fShearX;
548 aLocalTransform.decompose(aScale, aTranslate, fRotate, fShearX);
549
550 // when nopt rotated/sheared
552 return false;
553
554 // no shear or rotate, draw direct in pixel coordinates
555
556 // transform object range to device coordinates (pixels). Use
557 // the device transformation for better accuracy
558 basegfx::B2DRange aObjectRange(aTranslate, aTranslate + aScale);
559 aObjectRange.transform(mpOutputDevice->GetViewTransformation());
560
561 // extract discrete size of object
562 const sal_Int32 nOWidth(basegfx::fround(aObjectRange.getWidth()));
563 const sal_Int32 nOHeight(basegfx::fround(aObjectRange.getHeight()));
564
565 // only do something when object has a size in discrete units
566 if (nOWidth <= 0 || nOHeight <= 0)
567 return true;
568
569 // transform graphic range to device coordinates (pixels). Use
570 // the device transformation for better accuracy
571 basegfx::B2DRange aGraphicRange(rFillGraphicAttribute.getGraphicRange());
572 aGraphicRange.transform(mpOutputDevice->GetViewTransformation() * aLocalTransform);
573
574 // extract discrete size of graphic
575 // caution: when getting to zero, nothing would be painted; thus, do not allow this
576 const sal_Int32 nBWidth(std::max(sal_Int32(1), basegfx::fround(aGraphicRange.getWidth())));
577 const sal_Int32 nBHeight(std::max(sal_Int32(1), basegfx::fround(aGraphicRange.getHeight())));
578
579 // nBWidth, nBHeight is the pixel size of the needed bitmap. To not need to scale it
580 // in vcl many times, create a size-optimized version
581 const Size aNeededBitmapSizePixel(nBWidth, nBHeight);
582 BitmapEx aBitmapEx(rFillGraphicAttribute.getGraphic().GetBitmapEx());
583 const bool bPreScaled(nBWidth * nBHeight < (250 * 250));
584
585 // ... but only up to a maximum size, else it gets too expensive
586 if (bPreScaled)
587 {
588 // if color depth is below 24bit, expand before scaling for better quality.
589 // This is even needed for low colors, else the scale will produce
590 // a bitmap in gray or Black/White (!)
591 if (isPalettePixelFormat(aBitmapEx.getPixelFormat()))
592 {
593 aBitmapEx.Convert(BmpConversion::N24Bit);
594 }
595
596 aBitmapEx.Scale(aNeededBitmapSizePixel, BmpScaleFlag::Interpolate);
597 }
598
600 {
601 // when color modifier, apply to bitmap
602 aBitmapEx = aBitmapEx.ModifyBitmapEx(maBColorModifierStack);
603
604 // ModifyBitmapEx uses empty bitmap as sign to return that
605 // the content will be completely replaced to mono color, use shortcut
606 if (aBitmapEx.IsEmpty())
607 {
608 // color gets completely replaced, get it
609 const basegfx::BColor aModifiedColor(
612 aPolygon.transform(aLocalTransform);
613
614 mpOutputDevice->SetFillColor(Color(aModifiedColor));
615 mpOutputDevice->SetLineColor();
616 mpOutputDevice->DrawPolygon(aPolygon);
617
618 return true;
619 }
620 }
621
622 sal_Int32 nBLeft(basegfx::fround(aGraphicRange.getMinX()));
623 sal_Int32 nBTop(basegfx::fround(aGraphicRange.getMinY()));
624 const sal_Int32 nOLeft(basegfx::fround(aObjectRange.getMinX()));
625 const sal_Int32 nOTop(basegfx::fround(aObjectRange.getMinY()));
626 sal_Int32 nPosX(0);
627 sal_Int32 nPosY(0);
628
629 if (nBLeft > nOLeft)
630 {
631 const sal_Int32 nDiff((nBLeft / nBWidth) + 1);
632
633 nPosX -= nDiff;
634 nBLeft -= nDiff * nBWidth;
635 }
636
637 if (nBLeft + nBWidth <= nOLeft)
638 {
639 const sal_Int32 nDiff(-nBLeft / nBWidth);
640
641 nPosX += nDiff;
642 nBLeft += nDiff * nBWidth;
643 }
644
645 if (nBTop > nOTop)
646 {
647 const sal_Int32 nDiff((nBTop / nBHeight) + 1);
648
649 nPosY -= nDiff;
650 nBTop -= nDiff * nBHeight;
651 }
652
653 if (nBTop + nBHeight <= nOTop)
654 {
655 const sal_Int32 nDiff(-nBTop / nBHeight);
656
657 nPosY += nDiff;
658 nBTop += nDiff * nBHeight;
659 }
660
661 // prepare OutDev
662 const Point aEmptyPoint(0, 0);
663 // the visible rect, in pixels
664 const ::tools::Rectangle aVisiblePixel(aEmptyPoint, mpOutputDevice->GetOutputSizePixel());
665 const bool bWasEnabled(mpOutputDevice->IsMapModeEnabled());
666 mpOutputDevice->EnableMapMode(false);
667
668 // check if offset is used
669 const sal_Int32 nOffsetX(basegfx::fround(rFillGraphicAttribute.getOffsetX() * nBWidth));
670
671 if (nOffsetX)
672 {
673 // offset in X, so iterate over Y first and draw lines
674 for (sal_Int32 nYPos(nBTop); nYPos < nOTop + nOHeight; nYPos += nBHeight, nPosY++)
675 {
676 for (sal_Int32 nXPos((nPosY % 2) ? nBLeft - nBWidth + nOffsetX : nBLeft);
677 nXPos < nOLeft + nOWidth; nXPos += nBWidth)
678 {
679 const ::tools::Rectangle aOutRectPixel(Point(nXPos, nYPos), aNeededBitmapSizePixel);
680
681 if (aOutRectPixel.Overlaps(aVisiblePixel))
682 {
683 if (bPreScaled)
684 {
685 mpOutputDevice->DrawBitmapEx(aOutRectPixel.TopLeft(), aBitmapEx);
686 }
687 else
688 {
689 mpOutputDevice->DrawBitmapEx(aOutRectPixel.TopLeft(),
690 aNeededBitmapSizePixel, aBitmapEx);
691 }
692 }
693 }
694 }
695 }
696 else
697 {
698 // check if offset is used
699 const sal_Int32 nOffsetY(basegfx::fround(rFillGraphicAttribute.getOffsetY() * nBHeight));
700
701 // possible offset in Y, so iterate over X first and draw columns
702 for (sal_Int32 nXPos(nBLeft); nXPos < nOLeft + nOWidth; nXPos += nBWidth, nPosX++)
703 {
704 for (sal_Int32 nYPos((nPosX % 2) ? nBTop - nBHeight + nOffsetY : nBTop);
705 nYPos < nOTop + nOHeight; nYPos += nBHeight)
706 {
707 const ::tools::Rectangle aOutRectPixel(Point(nXPos, nYPos), aNeededBitmapSizePixel);
708
709 if (aOutRectPixel.Overlaps(aVisiblePixel))
710 {
711 if (bPreScaled)
712 {
713 mpOutputDevice->DrawBitmapEx(aOutRectPixel.TopLeft(), aBitmapEx);
714 }
715 else
716 {
717 mpOutputDevice->DrawBitmapEx(aOutRectPixel.TopLeft(),
718 aNeededBitmapSizePixel, aBitmapEx);
719 }
720 }
721 }
722 }
723 }
724
725 // restore OutDev
726 mpOutputDevice->EnableMapMode(bWasEnabled);
727 return true;
728}
729
730// direct draw of Graphic
732 const primitive2d::PolyPolygonGraphicPrimitive2D& rPolygonCandidate)
733{
734 bool bDone(false);
735 const basegfx::B2DPolyPolygon& rPolyPolygon = rPolygonCandidate.getB2DPolyPolygon();
736
737 // #121194# Todo: check if this works
738 if (!rPolyPolygon.count())
739 {
740 // empty polyPolygon, done
741 bDone = true;
742 }
743 else
744 {
745 const attribute::FillGraphicAttribute& rFillGraphicAttribute
746 = rPolygonCandidate.getFillGraphic();
747
748 // try to catch cases where the graphic will be color-modified to a single
749 // color (e.g. shadow)
750 switch (rFillGraphicAttribute.getGraphic().GetType())
751 {
752 case GraphicType::GdiMetafile:
753 {
754 // metafiles are potentially transparent, cannot optimize, not done
755 break;
756 }
757 case GraphicType::Bitmap:
758 {
759 if (!rFillGraphicAttribute.getGraphic().IsTransparent()
760 && !rFillGraphicAttribute.getGraphic().IsAlpha())
761 {
762 // bitmap is not transparent and has no alpha
763 const sal_uInt32 nBColorModifierStackCount(maBColorModifierStack.count());
764
765 if (nBColorModifierStackCount)
766 {
767 const basegfx::BColorModifierSharedPtr& rTopmostModifier
768 = maBColorModifierStack.getBColorModifier(nBColorModifierStackCount
769 - 1);
770 const basegfx::BColorModifier_replace* pReplacer
771 = dynamic_cast<const basegfx::BColorModifier_replace*>(
772 rTopmostModifier.get());
773
774 if (pReplacer)
775 {
776 // the bitmap fill is in unified color, so we can replace it with
777 // a single polygon fill. The form of the fill depends on tiling
778 if (rFillGraphicAttribute.getTiling())
779 {
780 // with tiling, fill the whole tools::PolyPolygon with the modifier color
781 basegfx::B2DPolyPolygon aLocalPolyPolygon(rPolyPolygon);
782
783 aLocalPolyPolygon.transform(maCurrentTransformation);
784 mpOutputDevice->SetLineColor();
785 mpOutputDevice->SetFillColor(Color(pReplacer->getBColor()));
786 mpOutputDevice->DrawPolyPolygon(aLocalPolyPolygon);
787 }
788 else
789 {
790 // without tiling, only the area common to the bitmap tile and the
791 // tools::PolyPolygon is filled. Create the bitmap tile area in object
792 // coordinates. For this, the object transformation needs to be created
793 // from the already scaled PolyPolygon. The tile area in object
794 // coordinates will always be non-rotated, so it's not necessary to
795 // work with a polygon here
796 basegfx::B2DRange aTileRange(
797 rFillGraphicAttribute.getGraphicRange());
798 const basegfx::B2DRange aPolyPolygonRange(
799 rPolyPolygon.getB2DRange());
800 const basegfx::B2DHomMatrix aNewObjectTransform(
802 aPolyPolygonRange.getRange(),
803 aPolyPolygonRange.getMinimum()));
804
805 aTileRange.transform(aNewObjectTransform);
806
807 // now clip the object polyPolygon against the tile range
808 // to get the common area
811 rPolyPolygon, aTileRange, true, false);
812
813 if (aTarget.count())
814 {
816 mpOutputDevice->SetLineColor();
817 mpOutputDevice->SetFillColor(Color(pReplacer->getBColor()));
818 mpOutputDevice->DrawPolyPolygon(aTarget);
819 }
820 }
821
822 // simplified output executed, we are done
823 bDone = true;
824 }
825 }
826 }
827 break;
828 }
829 default: //GraphicType::NONE, GraphicType::Default
830 {
831 // empty graphic, we are done
832 bDone = true;
833 break;
834 }
835 }
836 }
837
838 if (!bDone)
839 {
840 // use default decomposition
841 process(rPolygonCandidate);
842 }
843}
844
845// mask group
847{
848 if (rMaskCandidate.getChildren().empty())
849 return;
850
851 basegfx::B2DPolyPolygon aMask(rMaskCandidate.getMask());
852
853 if (!aMask.count())
854 return;
855
857
858 // Unless smooth edges are needed, simply use clipping.
859 if (basegfx::utils::isRectangle(aMask) || !getViewInformation2D().getUseAntiAliasing())
860 {
862 mpOutputDevice->IntersectClipRegion(vcl::Region(aMask));
863 process(rMaskCandidate.getChildren());
864 mpOutputDevice->Pop();
865 return;
866 }
867
868 const basegfx::B2DRange aRange(basegfx::utils::getRange(aMask));
869 impBufferDevice aBufferDevice(*mpOutputDevice, aRange);
870
871 if (!aBufferDevice.isVisible())
872 return;
873
874 // remember last OutDev and set to content
875 OutputDevice* pLastOutputDevice = mpOutputDevice;
876 mpOutputDevice = &aBufferDevice.getContent();
877
878 // paint to it
879 process(rMaskCandidate.getChildren());
880
881 // back to old OutDev
882 mpOutputDevice = pLastOutputDevice;
883
884 // draw mask
885 VirtualDevice& rMask = aBufferDevice.getTransparence();
886 rMask.SetLineColor();
887 rMask.SetFillColor(COL_BLACK);
888 rMask.DrawPolyPolygon(aMask);
889
890 // dump buffer to outdev
891 aBufferDevice.paint();
892}
893
894// modified color group. Force output to unified color.
896 const primitive2d::ModifiedColorPrimitive2D& rModifiedCandidate)
897{
898 if (!rModifiedCandidate.getChildren().empty())
899 {
900 maBColorModifierStack.push(rModifiedCandidate.getColorModifier());
901 process(rModifiedCandidate.getChildren());
903 }
904}
905
906// unified sub-transparence. Draw to VDev first.
908 const primitive2d::UnifiedTransparencePrimitive2D& rTransCandidate)
909{
910 if (rTransCandidate.getChildren().empty())
911 return;
912
913 if (0.0 == rTransCandidate.getTransparence())
914 {
915 // no transparence used, so just use the content
916 process(rTransCandidate.getChildren());
917 }
918 else if (rTransCandidate.getTransparence() > 0.0 && rTransCandidate.getTransparence() < 1.0)
919 {
920 // transparence is in visible range
923 impBufferDevice aBufferDevice(*mpOutputDevice, aRange);
924
925 if (aBufferDevice.isVisible())
926 {
927 // remember last OutDev and set to content
928 OutputDevice* pLastOutputDevice = mpOutputDevice;
929 mpOutputDevice = &aBufferDevice.getContent();
930
931 // paint content to it
932 process(rTransCandidate.getChildren());
933
934 // back to old OutDev
935 mpOutputDevice = pLastOutputDevice;
936
937 // dump buffer to outdev using given transparence
938 aBufferDevice.paint(rTransCandidate.getTransparence());
939 }
940 }
941}
942
943// sub-transparence group. Draw to VDev first.
945 const primitive2d::TransparencePrimitive2D& rTransCandidate)
946{
947 if (rTransCandidate.getChildren().empty())
948 return;
949
952 impBufferDevice aBufferDevice(*mpOutputDevice, aRange);
953
954 if (!aBufferDevice.isVisible())
955 return;
956
957 // remember last OutDev and set to content
958 OutputDevice* pLastOutputDevice = mpOutputDevice;
959 mpOutputDevice = &aBufferDevice.getContent();
960
961 // paint content to it
962 process(rTransCandidate.getChildren());
963
964 // set to mask
965 mpOutputDevice = &aBufferDevice.getTransparence();
966
967 // when painting transparence masks, reset the color stack
968 basegfx::BColorModifierStack aLastBColorModifierStack(maBColorModifierStack);
970
971 // paint mask to it (always with transparence intensities, evtl. with AA)
972 process(rTransCandidate.getTransparence());
973
974 // back to old color stack
975 maBColorModifierStack = aLastBColorModifierStack;
976
977 // back to old OutDev
978 mpOutputDevice = pLastOutputDevice;
979
980 // dump buffer to outdev
981 aBufferDevice.paint();
982}
983
984// transform group.
986 const primitive2d::TransformPrimitive2D& rTransformCandidate)
987{
988 // remember current transformation and ViewInformation
989 const basegfx::B2DHomMatrix aLastCurrentTransformation(maCurrentTransformation);
990 const geometry::ViewInformation2D aLastViewInformation2D(getViewInformation2D());
991
992 // create new transformations for CurrentTransformation
993 // and for local ViewInformation2D
996 aViewInformation2D.setObjectTransformation(getViewInformation2D().getObjectTransformation()
997 * rTransformCandidate.getTransformation());
998 updateViewInformation(aViewInformation2D);
999
1000 // process content
1001 process(rTransformCandidate.getChildren());
1002
1003 // restore transformations
1004 maCurrentTransformation = aLastCurrentTransformation;
1005 updateViewInformation(aLastViewInformation2D);
1006}
1007
1008// new XDrawPage for ViewInformation2D
1010 const primitive2d::PagePreviewPrimitive2D& rPagePreviewCandidate)
1011{
1012 // remember current transformation and ViewInformation
1013 const geometry::ViewInformation2D aLastViewInformation2D(getViewInformation2D());
1014
1015 // create new local ViewInformation2D
1017 aViewInformation2D.setVisualizedPage(rPagePreviewCandidate.getXDrawPage());
1018 updateViewInformation(aViewInformation2D);
1019
1020 // process decomposed content
1021 process(rPagePreviewCandidate);
1022
1023 // restore transformations
1024 updateViewInformation(aLastViewInformation2D);
1025}
1026
1027// marker
1029 const primitive2d::MarkerArrayPrimitive2D& rMarkArrayCandidate)
1030{
1031 // get data
1032 const std::vector<basegfx::B2DPoint>& rPositions = rMarkArrayCandidate.getPositions();
1033 const sal_uInt32 nCount(rPositions.size());
1034
1035 if (!nCount || rMarkArrayCandidate.getMarker().IsEmpty())
1036 return;
1037
1038 // get pixel size
1039 const BitmapEx& rMarker(rMarkArrayCandidate.getMarker());
1040 const Size aBitmapSize(rMarker.GetSizePixel());
1041
1042 if (!(aBitmapSize.Width() && aBitmapSize.Height()))
1043 return;
1044
1045 // get discrete half size
1046 const basegfx::B2DVector aDiscreteHalfSize((aBitmapSize.getWidth() - 1.0) * 0.5,
1047 (aBitmapSize.getHeight() - 1.0) * 0.5);
1048 const bool bWasEnabled(mpOutputDevice->IsMapModeEnabled());
1049
1050 // do not forget evtl. moved origin in target device MapMode when
1051 // switching it off; it would be missing and lead to wrong positions.
1052 // All his could be done using logic sizes and coordinates, too, but
1053 // we want a 1:1 bitmap rendering here, so it's more safe and faster
1054 // to work with switching off MapMode usage completely.
1055 const Point aOrigin(mpOutputDevice->GetMapMode().GetOrigin());
1056
1057 mpOutputDevice->EnableMapMode(false);
1058
1059 for (auto const& pos : rPositions)
1060 {
1061 const basegfx::B2DPoint aDiscreteTopLeft((maCurrentTransformation * pos)
1062 - aDiscreteHalfSize);
1063 const Point aDiscretePoint(basegfx::fround(aDiscreteTopLeft.getX()),
1064 basegfx::fround(aDiscreteTopLeft.getY()));
1065
1066 mpOutputDevice->DrawBitmapEx(aDiscretePoint + aOrigin, rMarker);
1067 }
1068
1069 mpOutputDevice->EnableMapMode(bWasEnabled);
1070}
1071
1072// point
1074 const primitive2d::PointArrayPrimitive2D& rPointArrayCandidate)
1075{
1076 const std::vector<basegfx::B2DPoint>& rPositions = rPointArrayCandidate.getPositions();
1077 const basegfx::BColor aRGBColor(
1078 maBColorModifierStack.getModifiedColor(rPointArrayCandidate.getRGBColor()));
1079 const Color aVCLColor(aRGBColor);
1080
1081 for (auto const& pos : rPositions)
1082 {
1083 const basegfx::B2DPoint aViewPosition(maCurrentTransformation * pos);
1084 const Point aPos(basegfx::fround(aViewPosition.getX()),
1085 basegfx::fround(aViewPosition.getY()));
1086
1087 mpOutputDevice->DrawPixel(aPos, aVCLColor);
1088 }
1089}
1090
1092 const primitive2d::PolygonStrokePrimitive2D& rPolygonStrokeCandidate)
1093{
1094 // #i101491# method restructured to clearly use the DrawPolyLine
1095 // calls starting from a defined line width
1096 const attribute::LineAttribute& rLineAttribute = rPolygonStrokeCandidate.getLineAttribute();
1097 const double fLineWidth(rLineAttribute.getWidth());
1098 bool bDone(false);
1099
1100 if (basegfx::fTools::more(fLineWidth, 0.0))
1101 {
1103 * basegfx::B2DVector(fLineWidth, 0.0));
1104 const double fDiscreteLineWidth(aDiscreteUnit.getLength());
1105 const attribute::StrokeAttribute& rStrokeAttribute
1106 = rPolygonStrokeCandidate.getStrokeAttribute();
1107 const basegfx::BColor aHairlineColor(
1109 basegfx::B2DPolyPolygon aHairlinePolyPolygon;
1110
1111 mpOutputDevice->SetLineColor(Color(aHairlineColor));
1112 mpOutputDevice->SetFillColor();
1113
1114 if (0.0 == rStrokeAttribute.getFullDotDashLen())
1115 {
1116 // no line dashing, just copy
1117 aHairlinePolyPolygon.append(rPolygonStrokeCandidate.getB2DPolygon());
1118 }
1119 else
1120 {
1121 // else apply LineStyle
1123 rPolygonStrokeCandidate.getB2DPolygon(), rStrokeAttribute.getDotDashArray(),
1124 &aHairlinePolyPolygon, nullptr, rStrokeAttribute.getFullDotDashLen());
1125 }
1126
1127 const sal_uInt32 nCount(aHairlinePolyPolygon.count());
1128
1129 if (nCount)
1130 {
1131 const bool bAntiAliased(getViewInformation2D().getUseAntiAliasing());
1132 aHairlinePolyPolygon.transform(maCurrentTransformation);
1133
1134 if (bAntiAliased)
1135 {
1136 if (basegfx::fTools::lessOrEqual(fDiscreteLineWidth, 1.0))
1137 {
1138 // line in range ]0.0 .. 1.0[
1139 // paint as simple hairline
1140 for (sal_uInt32 a(0); a < nCount; a++)
1141 {
1142 mpOutputDevice->DrawPolyLine(aHairlinePolyPolygon.getB2DPolygon(a), 0.0);
1143 }
1144
1145 bDone = true;
1146 }
1147 else if (basegfx::fTools::lessOrEqual(fDiscreteLineWidth, 2.0))
1148 {
1149 // line in range [1.0 .. 2.0[
1150 // paint as 2x2 with dynamic line distance
1152 const double fDistance(fDiscreteLineWidth - 1.0);
1153 const double fHalfDistance(fDistance * 0.5);
1154
1155 for (sal_uInt32 a(0); a < nCount; a++)
1156 {
1157 basegfx::B2DPolygon aCandidate(aHairlinePolyPolygon.getB2DPolygon(a));
1158
1159 aMat.set(0, 2, -fHalfDistance);
1160 aMat.set(1, 2, -fHalfDistance);
1161 aCandidate.transform(aMat);
1162 mpOutputDevice->DrawPolyLine(aCandidate, 0.0);
1163
1164 aMat.set(0, 2, fDistance);
1165 aMat.set(1, 2, 0.0);
1166 aCandidate.transform(aMat);
1167 mpOutputDevice->DrawPolyLine(aCandidate, 0.0);
1168
1169 aMat.set(0, 2, 0.0);
1170 aMat.set(1, 2, fDistance);
1171 aCandidate.transform(aMat);
1172 mpOutputDevice->DrawPolyLine(aCandidate, 0.0);
1173
1174 aMat.set(0, 2, -fDistance);
1175 aMat.set(1, 2, 0.0);
1176 aCandidate.transform(aMat);
1177 mpOutputDevice->DrawPolyLine(aCandidate, 0.0);
1178 }
1179
1180 bDone = true;
1181 }
1182 else if (basegfx::fTools::lessOrEqual(fDiscreteLineWidth, 3.0))
1183 {
1184 // line in range [2.0 .. 3.0]
1185 // paint as cross in a 3x3 with dynamic line distance
1187 const double fDistance((fDiscreteLineWidth - 1.0) * 0.5);
1188
1189 for (sal_uInt32 a(0); a < nCount; a++)
1190 {
1191 basegfx::B2DPolygon aCandidate(aHairlinePolyPolygon.getB2DPolygon(a));
1192
1193 mpOutputDevice->DrawPolyLine(aCandidate, 0.0);
1194
1195 aMat.set(0, 2, -fDistance);
1196 aMat.set(1, 2, 0.0);
1197 aCandidate.transform(aMat);
1198 mpOutputDevice->DrawPolyLine(aCandidate, 0.0);
1199
1200 aMat.set(0, 2, fDistance);
1201 aMat.set(1, 2, -fDistance);
1202 aCandidate.transform(aMat);
1203 mpOutputDevice->DrawPolyLine(aCandidate, 0.0);
1204
1205 aMat.set(0, 2, fDistance);
1206 aMat.set(1, 2, fDistance);
1207 aCandidate.transform(aMat);
1208 mpOutputDevice->DrawPolyLine(aCandidate, 0.0);
1209
1210 aMat.set(0, 2, -fDistance);
1211 aMat.set(1, 2, fDistance);
1212 aCandidate.transform(aMat);
1213 mpOutputDevice->DrawPolyLine(aCandidate, 0.0);
1214 }
1215
1216 bDone = true;
1217 }
1218 else
1219 {
1220 // #i101491# line width above 3.0
1221 }
1222 }
1223 else
1224 {
1225 if (basegfx::fTools::lessOrEqual(fDiscreteLineWidth, 1.5))
1226 {
1227 // line width below 1.5, draw the basic hairline polygon
1228 for (sal_uInt32 a(0); a < nCount; a++)
1229 {
1230 mpOutputDevice->DrawPolyLine(aHairlinePolyPolygon.getB2DPolygon(a), 0.0);
1231 }
1232
1233 bDone = true;
1234 }
1235 else if (basegfx::fTools::lessOrEqual(fDiscreteLineWidth, 2.5))
1236 {
1237 // line width is in range ]1.5 .. 2.5], use four hairlines
1238 // drawn in a square
1239 for (sal_uInt32 a(0); a < nCount; a++)
1240 {
1241 basegfx::B2DPolygon aCandidate(aHairlinePolyPolygon.getB2DPolygon(a));
1243
1244 mpOutputDevice->DrawPolyLine(aCandidate, 0.0);
1245
1246 aMat.set(0, 2, 1.0);
1247 aMat.set(1, 2, 0.0);
1248 aCandidate.transform(aMat);
1249
1250 mpOutputDevice->DrawPolyLine(aCandidate, 0.0);
1251
1252 aMat.set(0, 2, 0.0);
1253 aMat.set(1, 2, 1.0);
1254 aCandidate.transform(aMat);
1255
1256 mpOutputDevice->DrawPolyLine(aCandidate, 0.0);
1257
1258 aMat.set(0, 2, -1.0);
1259 aMat.set(1, 2, 0.0);
1260 aCandidate.transform(aMat);
1261
1262 mpOutputDevice->DrawPolyLine(aCandidate, 0.0);
1263 }
1264
1265 bDone = true;
1266 }
1267 else
1268 {
1269 // #i101491# line width is above 2.5
1270 }
1271 }
1272
1273 if (!bDone && rPolygonStrokeCandidate.getB2DPolygon().count() > 1000)
1274 {
1275 // #i101491# If the polygon complexity uses more than a given amount, do
1276 // use OutputDevice::DrawPolyLine directly; this will avoid buffering all
1277 // decompositions in primitives (memory) and fallback to old line painting
1278 // for very complex polygons, too
1279 for (sal_uInt32 a(0); a < nCount; a++)
1280 {
1281 mpOutputDevice->DrawPolyLine(aHairlinePolyPolygon.getB2DPolygon(a),
1282 fDiscreteLineWidth, rLineAttribute.getLineJoin(),
1283 rLineAttribute.getLineCap(),
1284 rLineAttribute.getMiterMinimumAngle());
1285 }
1286
1287 bDone = true;
1288 }
1289 }
1290 }
1291
1292 if (!bDone)
1293 {
1294 // remember that we enter a PolygonStrokePrimitive2D decomposition,
1295 // used for AA thick line drawing
1297
1298 // line width is big enough for standard filled polygon visualisation or zero
1299 process(rPolygonStrokeCandidate);
1300
1301 // leave PolygonStrokePrimitive2D
1303 }
1304}
1305
1307{
1308 // The new decomposition of Metafiles made it necessary to add an Eps
1309 // primitive to handle embedded Eps data. On some devices, this can be
1310 // painted directly (mac, printer).
1311 // To be able to handle the replacement correctly, i need to handle it myself
1312 // since DrawEPS will not be able e.g. to rotate the replacement. To be able
1313 // to do that, i added a boolean return to OutputDevice::DrawEPS(..)
1314 // to know when EPS was handled directly already.
1315 basegfx::B2DRange aRange(0.0, 0.0, 1.0, 1.0);
1316 aRange.transform(maCurrentTransformation * rEpsPrimitive2D.getEpsTransform());
1317
1318 if (aRange.isEmpty())
1319 return;
1320
1321 const ::tools::Rectangle aRectangle(static_cast<sal_Int32>(floor(aRange.getMinX())),
1322 static_cast<sal_Int32>(floor(aRange.getMinY())),
1323 static_cast<sal_Int32>(ceil(aRange.getMaxX())),
1324 static_cast<sal_Int32>(ceil(aRange.getMaxY())));
1325
1326 if (aRectangle.IsEmpty())
1327 return;
1328
1329 bool bWillReallyRender = mpOutputDevice->IsDeviceOutputNecessary();
1330 // try to paint EPS directly without fallback visualisation
1331 const bool bEPSPaintedDirectly
1332 = bWillReallyRender
1333 && mpOutputDevice->DrawEPS(aRectangle.TopLeft(), aRectangle.GetSize(),
1334 rEpsPrimitive2D.getGfxLink());
1335
1336 if (!bEPSPaintedDirectly)
1337 {
1338 // use the decomposition which will correctly handle the
1339 // fallback visualisation using full transformation (e.g. rotation)
1340 process(rEpsPrimitive2D);
1341 }
1342}
1343
1345 const primitive2d::SvgLinearAtomPrimitive2D& rCandidate)
1346{
1347 const double fDelta(rCandidate.getOffsetB() - rCandidate.getOffsetA());
1348
1349 if (!basegfx::fTools::more(fDelta, 0.0))
1350 return;
1351
1354
1355 // calculate discrete unit in WorldCoordinates; use diagonal (1.0, 1.0) and divide by sqrt(2)
1356 const basegfx::B2DVector aDiscreteVector(
1357 getViewInformation2D().getInverseObjectToViewTransformation()
1358 * basegfx::B2DVector(1.0, 1.0));
1359 const double fDiscreteUnit(aDiscreteVector.getLength() * (1.0 / M_SQRT2));
1360
1361 // use color distance and discrete lengths to calculate step count
1362 const sal_uInt32 nSteps(calculateStepsForSvgGradient(aColorA, aColorB, fDelta, fDiscreteUnit));
1363
1364 // switch off line painting
1365 mpOutputDevice->SetLineColor();
1366
1367 // prepare polygon in needed width at start position (with discrete overlap)
1369 basegfx::B2DRange(rCandidate.getOffsetA() - fDiscreteUnit, 0.0,
1370 rCandidate.getOffsetA() + (fDelta / nSteps) + fDiscreteUnit, 1.0)));
1371
1372 // prepare loop ([0.0 .. 1.0[)
1373 double fUnitScale(0.0);
1374 const double fUnitStep(1.0 / nSteps);
1375
1376 // loop and paint
1377 for (sal_uInt32 a(0); a < nSteps; a++, fUnitScale += fUnitStep)
1378 {
1379 basegfx::B2DPolygon aNew(aPolygon);
1380
1382 * basegfx::utils::createTranslateB2DHomMatrix(fDelta * fUnitScale, 0.0));
1383 mpOutputDevice->SetFillColor(Color(basegfx::interpolate(aColorA, aColorB, fUnitScale)));
1384 mpOutputDevice->DrawPolyPolygon(basegfx::B2DPolyPolygon(aNew));
1385 }
1386}
1387
1389 const primitive2d::SvgRadialAtomPrimitive2D& rCandidate)
1390{
1391 const double fDeltaScale(rCandidate.getScaleB() - rCandidate.getScaleA());
1392
1393 if (!basegfx::fTools::more(fDeltaScale, 0.0))
1394 return;
1395
1398
1399 // calculate discrete unit in WorldCoordinates; use diagonal (1.0, 1.0) and divide by sqrt(2)
1400 const basegfx::B2DVector aDiscreteVector(
1401 getViewInformation2D().getInverseObjectToViewTransformation()
1402 * basegfx::B2DVector(1.0, 1.0));
1403 const double fDiscreteUnit(aDiscreteVector.getLength() * (1.0 / M_SQRT2));
1404
1405 // use color distance and discrete lengths to calculate step count
1406 const sal_uInt32 nSteps(
1407 calculateStepsForSvgGradient(aColorA, aColorB, fDeltaScale, fDiscreteUnit));
1408
1409 // switch off line painting
1410 mpOutputDevice->SetLineColor();
1411
1412 // prepare loop ([0.0 .. 1.0[, full polygons, no polypolygons with holes)
1413 double fUnitScale(0.0);
1414 const double fUnitStep(1.0 / nSteps);
1415
1416 for (sal_uInt32 a(0); a < nSteps; a++, fUnitScale += fUnitStep)
1417 {
1418 basegfx::B2DHomMatrix aTransform;
1419 const double fEndScale(rCandidate.getScaleB() - (fDeltaScale * fUnitScale));
1420
1421 if (rCandidate.isTranslateSet())
1422 {
1424 rCandidate.getTranslateB(), rCandidate.getTranslateA(), fUnitScale));
1425
1427 fEndScale, fEndScale, aTranslate.getX(), aTranslate.getY());
1428 }
1429 else
1430 {
1431 aTransform = basegfx::utils::createScaleB2DHomMatrix(fEndScale, fEndScale);
1432 }
1433
1435
1436 aNew.transform(maCurrentTransformation * aTransform);
1437 mpOutputDevice->SetFillColor(Color(basegfx::interpolate(aColorB, aColorA, fUnitScale)));
1438 mpOutputDevice->DrawPolyPolygon(basegfx::B2DPolyPolygon(aNew));
1439 }
1440}
1441
1443{
1444 const DrawModeFlags nOriginalDrawMode(mpOutputDevice->GetDrawMode());
1445
1446 if (!(nOriginalDrawMode
1447 & (DrawModeFlags::BlackLine | DrawModeFlags::GrayLine | DrawModeFlags::WhiteLine
1448 | DrawModeFlags::SettingsLine)))
1449 return;
1450
1451 DrawModeFlags nAdaptedDrawMode(nOriginalDrawMode);
1452
1453 if (nOriginalDrawMode & DrawModeFlags::BlackLine)
1454 {
1455 nAdaptedDrawMode |= DrawModeFlags::BlackFill;
1456 }
1457 else
1458 {
1459 nAdaptedDrawMode &= ~DrawModeFlags::BlackFill;
1460 }
1461
1462 if (nOriginalDrawMode & DrawModeFlags::GrayLine)
1463 {
1464 nAdaptedDrawMode |= DrawModeFlags::GrayFill;
1465 }
1466 else
1467 {
1468 nAdaptedDrawMode &= ~DrawModeFlags::GrayFill;
1469 }
1470
1471 if (nOriginalDrawMode & DrawModeFlags::WhiteLine)
1472 {
1473 nAdaptedDrawMode |= DrawModeFlags::WhiteFill;
1474 }
1475 else
1476 {
1477 nAdaptedDrawMode &= ~DrawModeFlags::WhiteFill;
1478 }
1479
1480 if (nOriginalDrawMode & DrawModeFlags::SettingsLine)
1481 {
1482 nAdaptedDrawMode |= DrawModeFlags::SettingsFill;
1483 }
1484 else
1485 {
1486 nAdaptedDrawMode &= ~DrawModeFlags::SettingsFill;
1487 }
1488
1489 mpOutputDevice->SetDrawMode(nAdaptedDrawMode);
1490}
1491
1493{
1494 const DrawModeFlags nOriginalDrawMode(mpOutputDevice->GetDrawMode());
1495 if (!(nOriginalDrawMode
1496 & (DrawModeFlags::BlackText | DrawModeFlags::GrayText | DrawModeFlags::WhiteText
1497 | DrawModeFlags::SettingsText)))
1498 return;
1499
1500 DrawModeFlags nAdaptedDrawMode(nOriginalDrawMode);
1501
1502 if (nOriginalDrawMode & DrawModeFlags::BlackText)
1503 {
1504 nAdaptedDrawMode |= DrawModeFlags::BlackFill;
1505 }
1506 else
1507 {
1508 nAdaptedDrawMode &= ~DrawModeFlags::BlackFill;
1509 }
1510
1511 if (nOriginalDrawMode & DrawModeFlags::GrayText)
1512 {
1513 nAdaptedDrawMode |= DrawModeFlags::GrayFill;
1514 }
1515 else
1516 {
1517 nAdaptedDrawMode &= ~DrawModeFlags::GrayFill;
1518 }
1519
1520 if (nOriginalDrawMode & DrawModeFlags::WhiteText)
1521 {
1522 nAdaptedDrawMode |= DrawModeFlags::WhiteFill;
1523 }
1524 else
1525 {
1526 nAdaptedDrawMode &= ~DrawModeFlags::WhiteFill;
1527 }
1528
1529 if (nOriginalDrawMode & DrawModeFlags::SettingsText)
1530 {
1531 nAdaptedDrawMode |= DrawModeFlags::SettingsFill;
1532 }
1533 else
1534 {
1535 nAdaptedDrawMode &= ~DrawModeFlags::SettingsFill;
1536 }
1537
1538 mpOutputDevice->SetDrawMode(nAdaptedDrawMode);
1539}
1540
1541// process support
1542
1544 OutputDevice& rOutDev, basegfx::BColorModifierStack aInitStack)
1545 : BaseProcessor2D(rViewInformation)
1546 , mpOutputDevice(&rOutDev)
1547 , maBColorModifierStack(std::move(aInitStack))
1548 , mnPolygonStrokePrimitive2D(0)
1549{
1550 // set digit language, derived from SvtCTLOptions to have the correct
1551 // number display for arabic/hindi numerals
1553}
1554
1556}
1557
1558/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
DrawModeFlags
bool Scale(const Size &rNewSize, BmpScaleFlag nScaleFlag=BmpScaleFlag::Default)
vcl::PixelFormat getPixelFormat() const
bool Convert(BmpConversion eConversion)
bool IsEmpty() const
BitmapEx ModifyBitmapEx(const basegfx::BColorModifierStack &rBColorModifierStack) const
const Size & GetSizePixel() const
bool IsTransparent() const
bool IsValid() const
GraphicType GetType() const
bool IsAnimated() const
BitmapEx GetBitmapEx(const GraphicConversionParameters &rParameters=GraphicConversionParameters()) const
bool IsAlpha() const
const std::shared_ptr< VectorGraphicData > & getVectorGraphicData() const
bool IsTransparent() const
void resize(size_t nSize)
void reserve(size_t nCapacity)
void set(size_t nIndex, sal_Int32 nValue)
bool empty() const
void push_back(sal_Int32 nUnit)
const Fraction & GetScaleX() const
const Point & GetOrigin() const
const Fraction & GetScaleY() const
void SetLineColor()
void SetDigitLanguage(LanguageType)
void SetFillColor()
void DrawPolyPolygon(const tools::PolyPolygon &rPolyPoly)
static SalLayoutGlyphsCache * self()
const SalLayoutGlyphs * GetLayoutGlyphs(VclPtr< const OutputDevice > outputDevice, const OUString &text, const vcl::text::TextLayoutCache *layoutCache=nullptr)
constexpr tools::Long getHeight() const
constexpr tools::Long Height() const
constexpr tools::Long getWidth() const
constexpr tools::Long Width() const
bool decompose(B2DTuple &rScale, B2DTuple &rTranslate, double &rRotate, double &rShearX) const
void set(sal_uInt16 nRow, sal_uInt16 nColumn, double fValue)
void translate(double fX, double fY)
void scale(double fX, double fY)
B2DPolygon const & getB2DPolygon(sal_uInt32 nIndex) const
void append(const B2DPolygon &rPolygon, sal_uInt32 nCount=1)
void transform(const basegfx::B2DHomMatrix &rMatrix)
B2DRange getB2DRange() const
sal_uInt32 count() const
void transform(const basegfx::B2DHomMatrix &rMatrix)
sal_uInt32 count() const
BASEGFX_DLLPUBLIC void transform(const B2DHomMatrix &rMatrix)
B2DVector getRange() const
B2DPoint getMinimum() const
double getLength() const
const BColorModifierSharedPtr & getBColorModifier(sal_uInt32 nIndex) const
void push(const BColorModifierSharedPtr &rNew)
::basegfx::BColor getModifiedColor(const ::basegfx::BColor &rSource) const
sal_uInt32 count() const
const ::basegfx::BColor & getBColor() const
double getDistance(const BColor &rColor) const
TYPE getMaxX() const
TYPE getWidth() const
TYPE getMinX() const
TYPE getMinY() const
TYPE getMaxY() const
bool isEmpty() const
TYPE getHeight() const
TYPE getX() const
TYPE getY() const
const basegfx::B2DRange & getGraphicRange() const
basegfx::B2DLineJoin getLineJoin() const
css::drawing::LineCap getLineCap() const
const basegfx::BColor & getColor() const
const ::std::vector< double > & getDotDashArray() const
void setObjectTransformation(const basegfx::B2DHomMatrix &rNew)
void setVisualizedPage(const css::uno::Reference< css::drawing::XDrawPage > &rNew)
const basegfx::B2DHomMatrix & getTransform() const
const BitmapEx & getBitmap() const
data read access
const basegfx::B2DHomMatrix & getEpsTransform() const
data read access
const attribute::FillGraphicAttribute & getFillGraphic() const
const basegfx::B2DHomMatrix & getTransformation() const
data read access
const Primitive2DContainer & getChildren() const
data read access
const std::vector< basegfx::B2DPoint > & getPositions() const
data read access
const basegfx::B2DPolyPolygon & getMask() const
data read access
const basegfx::BColorModifierSharedPtr & getColorModifier() const
data read access
const css::uno::Reference< css::drawing::XDrawPage > & getXDrawPage() const
data read access
const std::vector< basegfx::B2DPoint > & getPositions() const
data read access
const attribute::FillGraphicAttribute & getFillGraphic() const
const basegfx::B2DPolyPolygon & getB2DPolyPolygon() const
data read access
const basegfx::B2DPolygon & getB2DPolygon() const
data read access
const attribute::StrokeAttribute & getStrokeAttribute() const
const basegfx::B2DPolygon & getB2DPolygon() const
data read access
const attribute::LineAttribute & getLineAttribute() const
basegfx::B2DRange getB2DRange(const geometry::ViewInformation2D &aViewInformation) const
const basegfx::BColor & getColorA() const
data read access
const basegfx::BColor & getColorA() const
data read access
virtual sal_uInt32 getPrimitive2DID() const override
provide unique ID
const ::std::vector< sal_Bool > & getKashidaArray() const
const ::std::vector< double > & getDXArray() const
const attribute::FontAttribute & getFontAttribute() const
const basegfx::B2DHomMatrix & getTextTransform() const
data read access
const basegfx::B2DHomMatrix & getTransformation() const
data read access
const Primitive2DContainer & getTransparence() const
data read access
void process(const primitive2d::BasePrimitive2D &rCandidate)
void updateViewInformation(const geometry::ViewInformation2D &rViewInformation2D)
const geometry::ViewInformation2D & getViewInformation2D() const
data read access
void RenderPointArrayPrimitive2D(const primitive2d::PointArrayPrimitive2D &rPointArrayCandidate)
void RenderPolygonStrokePrimitive2D(const primitive2d::PolygonStrokePrimitive2D &rPolygonStrokeCandidate)
void RenderEpsPrimitive2D(const primitive2d::EpsPrimitive2D &rEpsPrimitive2D)
void RenderPolyPolygonGraphicPrimitive2D(const primitive2d::PolyPolygonGraphicPrimitive2D &rPolygonCandidate)
basegfx::BColorModifierStack maBColorModifierStack
void RenderPolygonHairlinePrimitive2D(const primitive2d::PolygonHairlinePrimitive2D &rPolygonCandidate, bool bPixelBased)
void RenderSvgLinearAtomPrimitive2D(const primitive2d::SvgLinearAtomPrimitive2D &rCandidate)
void RenderFillGraphicPrimitive2D(const primitive2d::FillGraphicPrimitive2D &rFillBitmapCandidate)
void RenderUnifiedTransparencePrimitive2D(const primitive2d::UnifiedTransparencePrimitive2D &rTransCandidate)
void RenderTransformPrimitive2D(const primitive2d::TransformPrimitive2D &rTransformCandidate)
VclProcessor2D(const geometry::ViewInformation2D &rViewInformation, OutputDevice &rOutDev, basegfx::BColorModifierStack aInitStack=basegfx::BColorModifierStack())
void RenderPagePreviewPrimitive2D(const primitive2d::PagePreviewPrimitive2D &rPagePreviewCandidate)
void RenderMaskPrimitive2DPixel(const primitive2d::MaskPrimitive2D &rMaskCandidate)
void RenderModifiedColorPrimitive2D(const primitive2d::ModifiedColorPrimitive2D &rModifiedCandidate)
void RenderMarkerArrayPrimitive2D(const primitive2d::MarkerArrayPrimitive2D &rMarkerArrayCandidate)
void RenderSvgRadialAtomPrimitive2D(const primitive2d::SvgRadialAtomPrimitive2D &rCandidate)
void RenderTransparencePrimitive2D(const primitive2d::TransparencePrimitive2D &rTransCandidate)
void RenderTextSimpleOrDecoratedPortionPrimitive2D(const primitive2d::TextSimplePortionPrimitive2D &rTextCandidate)
void RenderBitmapPrimitive2D(const primitive2d::BitmapPrimitive2D &rBitmapCandidate)
bool RenderFillGraphicPrimitive2DImpl(const primitive2d::FillGraphicPrimitive2D &rFillBitmapCandidate)
tools::Long GetFontHeight() const
void SetWordLineMode(bool bWordLine)
void SetTransparent(bool bTransparent)
void SetFillColor(const Color &)
void SetUnderline(FontLineStyle)
void SetOverline(FontLineStyle)
void SetShadow(bool bShadow)
void SetRelief(FontRelief)
void SetEmphasisMark(FontEmphasisMark)
void SetStrikeout(FontStrikeout)
constexpr ::Color COL_BLACK(0x00, 0x00, 0x00)
int nCount
#define DBG_ASSERT(sCon, aError)
#define PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D
FontRelief
FontLineStyle
LINESTYLE_NONE
FontStrikeout
STRIKEOUT_NONE
FontEmphasisMark
uno_Any a
sal_uInt16 nPos
#define SAL_WARN(area, stream)
bool lessOrEqual(const T &rfValA, const T &rfValB)
bool more(const T &rfValA, const T &rfValB)
bool equalZero(const T &rfVal)
bool less(const T &rfValA, const T &rfValB)
B2DHomMatrix createScaleTranslateB2DHomMatrix(double fScaleX, double fScaleY, double fTranslateX, double fTranslateY)
B2DPolygon createPolygonFromRect(const B2DRectangle &rRect, double fRadiusX, double fRadiusY)
void applyLineDashing(const B2DPolygon &rCandidate, const std::vector< double > &rDotDashArray, B2DPolyPolygon *pLineTarget, B2DPolyPolygon *pGapTarget, double fDotDashLength)
B2DPolygon const & createPolygonFromUnitCircle(sal_uInt32 nStartQuadrant=0)
B2DPolyPolygon clipPolyPolygonOnRange(const B2DPolyPolygon &rCandidate, const B2DRange &rRange, bool bInside, bool bStroke)
B2DPolygon snapPointsOfHorizontalOrVerticalEdges(const B2DPolygon &rCandidate)
bool isRectangle(const B2DPolygon &rPoly)
B2DHomMatrix createScaleB2DHomMatrix(double fScaleX, double fScaleY)
B2DHomMatrix createTranslateB2DHomMatrix(double fTranslateX, double fTranslateY)
B2DHomMatrix createRotateB2DHomMatrix(double fRadiant)
B2DPolygon const & createUnitPolygon()
B2DRange getRange(const B2DPolygon &rCandidate)
B2DTuple interpolate(const B2DTuple &rOld1, const B2DTuple &rOld2, double t)
std::shared_ptr< BColorModifier > BColorModifierSharedPtr
B2DTuple absolute(const B2DTuple &rTup)
B2IRange fround(const B2DRange &rRange)
OStringBuffer & padToLength(OStringBuffer &rBuffer, sal_Int32 nLength, char cFill='\0')
LanguageType getDigitLanguage()
Get digit language derived from SvtCTLOptions.
vcl::Font getVclFontFromFontAttribute(const attribute::FontAttribute &rFontAttribute, double fFontScaleX, double fFontScaleY, double fFontRotation, const css::lang::Locale &rLocale)
Create a VCL-Font based on the definitions in FontAttribute and the given FontScaling.
FontLineStyle mapTextLineToFontLineStyle(TextLine eLineStyle)
FontStrikeout mapTextStrikeoutToFontStrikeout(TextStrikeout eTextStrikeout)
int i
long Long
ComplexTextLayoutFlags
constexpr bool isPalettePixelFormat(PixelFormat ePixelFormat)
size_t pos