1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "vclpixelprocessor2d.hxx"
21 #include "vclhelperbufferdevice.hxx"
22 #include "helperwrongspellrenderer.hxx"
23 #include <comphelper/lok.hxx>
25 #include <sal/log.hxx>
26 #include <vcl/outdev.hxx>
27 #include <vcl/hatch.hxx>
28 #include <vcl/canvastools.hxx>
29 #include <basegfx/polygon/b2dpolygontools.hxx>
30 #include <basegfx/polygon/b2dpolypolygontools.hxx>
31 #include <basegfx/utils/gradienttools.hxx>
33 #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
34 #include <drawinglayer/primitive2d/Tools.hxx>
35 #include <drawinglayer/primitive2d/textprimitive2d.hxx>
36 #include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
37 #include <drawinglayer/primitive2d/PolygonStrokePrimitive2D.hxx>
38 #include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
39 #include <drawinglayer/primitive2d/PolyPolygonGradientPrimitive2D.hxx>
40 #include <drawinglayer/primitive2d/PolyPolygonGraphicPrimitive2D.hxx>
41 #include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
42 #include <drawinglayer/primitive2d/fillgraphicprimitive2d.hxx>
43 #include <drawinglayer/primitive2d/maskprimitive2d.hxx>
44 #include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx>
45 #include <drawinglayer/primitive2d/transparenceprimitive2d.hxx>
46 #include <drawinglayer/primitive2d/transformprimitive2d.hxx>
47 #include <drawinglayer/primitive2d/markerarrayprimitive2d.hxx>
48 #include <drawinglayer/primitive2d/glowprimitive2d.hxx>
49 #include <drawinglayer/primitive2d/wrongspellprimitive2d.hxx>
50 #include <drawinglayer/primitive2d/controlprimitive2d.hxx>
51 #include <drawinglayer/primitive2d/borderlineprimitive2d.hxx>
52 #include <drawinglayer/primitive2d/fillgradientprimitive2d.hxx>
53 #include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
54 #include <drawinglayer/primitive2d/pagepreviewprimitive2d.hxx>
55 #include <drawinglayer/primitive2d/backgroundcolorprimitive2d.hxx>
56 #include <drawinglayer/primitive2d/svggradientprimitive2d.hxx>
57 #include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx>
58 #include <drawinglayer/primitive2d/fillhatchprimitive2d.hxx>
59 #include <drawinglayer/primitive2d/epsprimitive2d.hxx>
60 #include <drawinglayer/primitive2d/shadowprimitive2d.hxx>
61 #include <drawinglayer/primitive2d/patternfillprimitive2d.hxx>
63 #include <com/sun/star/awt/XWindow2.hpp>
64 #include <com/sun/star/awt/XControl.hpp>
66 #include <svtools/optionsdrawinglayer.hxx>
67 #include <vcl/gradient.hxx>
69 using namespace com::sun::star
;
71 namespace drawinglayer::processor2d
73 VclPixelProcessor2D::VclPixelProcessor2D(const geometry::ViewInformation2D
& rViewInformation
,
74 OutputDevice
& rOutDev
,
75 const basegfx::BColorModifierStack
& rInitStack
)
76 : VclProcessor2D(rViewInformation
, rOutDev
, rInitStack
)
77 , m_nOrigAntiAliasing(rOutDev
.GetAntialiasing())
79 // prepare maCurrentTransformation matrix with viewTransformation to target directly to pixels
80 maCurrentTransformation
= rViewInformation
.getObjectToViewTransformation();
82 // prepare output directly to pixels
83 mpOutputDevice
->Push(vcl::PushFlags::MAPMODE
);
84 mpOutputDevice
->SetMapMode();
86 // react on AntiAliasing settings
87 if (rViewInformation
.getUseAntiAliasing())
89 mpOutputDevice
->SetAntialiasing(m_nOrigAntiAliasing
| AntialiasingFlags::Enable
);
93 mpOutputDevice
->SetAntialiasing(m_nOrigAntiAliasing
& ~AntialiasingFlags::Enable
);
97 VclPixelProcessor2D::~VclPixelProcessor2D()
100 mpOutputDevice
->Pop();
102 // restore AntiAliasing
103 mpOutputDevice
->SetAntialiasing(m_nOrigAntiAliasing
);
106 void VclPixelProcessor2D::tryDrawPolyPolygonColorPrimitive2DDirect(
107 const drawinglayer::primitive2d::PolyPolygonColorPrimitive2D
& rSource
, double fTransparency
)
109 if (!rSource
.getB2DPolyPolygon().count() || fTransparency
< 0.0 || fTransparency
>= 1.0)
115 const basegfx::BColor
aPolygonColor(
116 maBColorModifierStack
.getModifiedColor(rSource
.getBColor()));
118 mpOutputDevice
->SetFillColor(Color(aPolygonColor
));
119 mpOutputDevice
->SetLineColor();
120 mpOutputDevice
->DrawTransparent(maCurrentTransformation
, rSource
.getB2DPolyPolygon(),
124 bool VclPixelProcessor2D::tryDrawPolygonHairlinePrimitive2DDirect(
125 const drawinglayer::primitive2d::PolygonHairlinePrimitive2D
& rSource
, double fTransparency
)
127 const basegfx::B2DPolygon
& rLocalPolygon(rSource
.getB2DPolygon());
129 if (!rLocalPolygon
.count() || fTransparency
< 0.0 || fTransparency
>= 1.0)
135 const basegfx::BColor
aLineColor(maBColorModifierStack
.getModifiedColor(rSource
.getBColor()));
137 mpOutputDevice
->SetFillColor();
138 mpOutputDevice
->SetLineColor(Color(aLineColor
));
139 //aLocalPolygon.transform(maCurrentTransformation);
141 // try drawing; if it did not work, use standard fallback
142 return mpOutputDevice
->DrawPolyLineDirect(maCurrentTransformation
, rLocalPolygon
, 0.0,
146 bool VclPixelProcessor2D::tryDrawPolygonStrokePrimitive2DDirect(
147 const drawinglayer::primitive2d::PolygonStrokePrimitive2D
& rSource
, double fTransparency
)
149 const basegfx::B2DPolygon
& rLocalPolygon(rSource
.getB2DPolygon());
151 if (!rLocalPolygon
.count() || fTransparency
< 0.0 || fTransparency
>= 1.0)
157 if (basegfx::B2DLineJoin::NONE
== rSource
.getLineAttribute().getLineJoin()
158 && css::drawing::LineCap_BUTT
!= rSource
.getLineAttribute().getLineCap())
160 // better use decompose to get that combination done for now, see discussion
161 // at https://bugs.documentfoundation.org/show_bug.cgi?id=130478#c17 and ff
165 // MM01: Radically change here - no dismantle/applyLineDashing,
166 // let that happen low-level at DrawPolyLineDirect implementations
167 // to open up for buffering and evtl. direct draw with sys-dep
168 // graphic systems. Check for stroke is in use
169 const bool bStrokeAttributeNotUsed(rSource
.getStrokeAttribute().isDefault()
170 || 0.0 == rSource
.getStrokeAttribute().getFullDotDashLen());
172 const basegfx::BColor
aLineColor(
173 maBColorModifierStack
.getModifiedColor(rSource
.getLineAttribute().getColor()));
175 mpOutputDevice
->SetFillColor();
176 mpOutputDevice
->SetLineColor(Color(aLineColor
));
178 // MM01 draw direct, hand over dash data if available
179 return mpOutputDevice
->DrawPolyLineDirect(
180 maCurrentTransformation
, rLocalPolygon
,
181 // tdf#124848 use LineWidth direct, do not try to solve for zero-case (aka hairline)
182 rSource
.getLineAttribute().getWidth(), fTransparency
,
183 bStrokeAttributeNotUsed
? nullptr : &rSource
.getStrokeAttribute().getDotDashArray(),
184 rSource
.getLineAttribute().getLineJoin(), rSource
.getLineAttribute().getLineCap(),
185 rSource
.getLineAttribute().getMiterMinimumAngle());
188 void VclPixelProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimitive2D
& rCandidate
)
190 switch (rCandidate
.getPrimitive2DID())
192 case PRIMITIVE2D_ID_WRONGSPELLPRIMITIVE2D
:
194 processWrongSpellPrimitive2D(
195 static_cast<const primitive2d::WrongSpellPrimitive2D
&>(rCandidate
));
198 case PRIMITIVE2D_ID_TEXTSIMPLEPORTIONPRIMITIVE2D
:
200 processTextSimplePortionPrimitive2D(
201 static_cast<const primitive2d::TextSimplePortionPrimitive2D
&>(rCandidate
));
204 case PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D
:
206 processTextDecoratedPortionPrimitive2D(
207 static_cast<const primitive2d::TextSimplePortionPrimitive2D
&>(rCandidate
));
210 case PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D
:
212 processPolygonHairlinePrimitive2D(
213 static_cast<const primitive2d::PolygonHairlinePrimitive2D
&>(rCandidate
));
216 case PRIMITIVE2D_ID_BITMAPPRIMITIVE2D
:
218 // direct draw of transformed BitmapEx primitive
219 processBitmapPrimitive2D(
220 static_cast<const primitive2d::BitmapPrimitive2D
&>(rCandidate
));
223 case PRIMITIVE2D_ID_FILLGRAPHICPRIMITIVE2D
:
225 // direct draw of fillBitmapPrimitive
226 RenderFillGraphicPrimitive2D(
227 static_cast<const primitive2d::FillGraphicPrimitive2D
&>(rCandidate
));
230 case PRIMITIVE2D_ID_POLYPOLYGONGRADIENTPRIMITIVE2D
:
232 processPolyPolygonGradientPrimitive2D(
233 static_cast<const primitive2d::PolyPolygonGradientPrimitive2D
&>(rCandidate
));
236 case PRIMITIVE2D_ID_POLYPOLYGONGRAPHICPRIMITIVE2D
:
238 // direct draw of bitmap
239 RenderPolyPolygonGraphicPrimitive2D(
240 static_cast<const primitive2d::PolyPolygonGraphicPrimitive2D
&>(rCandidate
));
243 case PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D
:
245 processPolyPolygonColorPrimitive2D(
246 static_cast<const primitive2d::PolyPolygonColorPrimitive2D
&>(rCandidate
));
249 case PRIMITIVE2D_ID_METAFILEPRIMITIVE2D
:
251 processMetaFilePrimitive2D(rCandidate
);
254 case PRIMITIVE2D_ID_MASKPRIMITIVE2D
:
257 RenderMaskPrimitive2DPixel(
258 static_cast<const primitive2d::MaskPrimitive2D
&>(rCandidate
));
261 case PRIMITIVE2D_ID_MODIFIEDCOLORPRIMITIVE2D
:
263 // modified color group. Force output to unified color.
264 RenderModifiedColorPrimitive2D(
265 static_cast<const primitive2d::ModifiedColorPrimitive2D
&>(rCandidate
));
268 case PRIMITIVE2D_ID_UNIFIEDTRANSPARENCEPRIMITIVE2D
:
270 processUnifiedTransparencePrimitive2D(
271 static_cast<const primitive2d::UnifiedTransparencePrimitive2D
&>(rCandidate
));
274 case PRIMITIVE2D_ID_TRANSPARENCEPRIMITIVE2D
:
276 // sub-transparence group. Draw to VDev first.
277 RenderTransparencePrimitive2D(
278 static_cast<const primitive2d::TransparencePrimitive2D
&>(rCandidate
));
281 case PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D
:
284 RenderTransformPrimitive2D(
285 static_cast<const primitive2d::TransformPrimitive2D
&>(rCandidate
));
288 case PRIMITIVE2D_ID_PAGEPREVIEWPRIMITIVE2D
:
290 // new XDrawPage for ViewInformation2D
291 RenderPagePreviewPrimitive2D(
292 static_cast<const primitive2d::PagePreviewPrimitive2D
&>(rCandidate
));
295 case PRIMITIVE2D_ID_MARKERARRAYPRIMITIVE2D
:
298 RenderMarkerArrayPrimitive2D(
299 static_cast<const primitive2d::MarkerArrayPrimitive2D
&>(rCandidate
));
302 case PRIMITIVE2D_ID_POINTARRAYPRIMITIVE2D
:
305 RenderPointArrayPrimitive2D(
306 static_cast<const primitive2d::PointArrayPrimitive2D
&>(rCandidate
));
309 case PRIMITIVE2D_ID_CONTROLPRIMITIVE2D
:
311 processControlPrimitive2D(
312 static_cast<const primitive2d::ControlPrimitive2D
&>(rCandidate
));
315 case PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D
:
317 processPolygonStrokePrimitive2D(
318 static_cast<const primitive2d::PolygonStrokePrimitive2D
&>(rCandidate
));
321 case PRIMITIVE2D_ID_FILLHATCHPRIMITIVE2D
:
323 processFillHatchPrimitive2D(
324 static_cast<const primitive2d::FillHatchPrimitive2D
&>(rCandidate
));
327 case PRIMITIVE2D_ID_BACKGROUNDCOLORPRIMITIVE2D
:
329 processBackgroundColorPrimitive2D(
330 static_cast<const primitive2d::BackgroundColorPrimitive2D
&>(rCandidate
));
333 case PRIMITIVE2D_ID_INVERTPRIMITIVE2D
:
335 processInvertPrimitive2D(rCandidate
);
338 case PRIMITIVE2D_ID_EPSPRIMITIVE2D
:
340 RenderEpsPrimitive2D(static_cast<const primitive2d::EpsPrimitive2D
&>(rCandidate
));
343 case PRIMITIVE2D_ID_SVGLINEARATOMPRIMITIVE2D
:
345 RenderSvgLinearAtomPrimitive2D(
346 static_cast<const primitive2d::SvgLinearAtomPrimitive2D
&>(rCandidate
));
349 case PRIMITIVE2D_ID_SVGRADIALATOMPRIMITIVE2D
:
351 RenderSvgRadialAtomPrimitive2D(
352 static_cast<const primitive2d::SvgRadialAtomPrimitive2D
&>(rCandidate
));
355 case PRIMITIVE2D_ID_BORDERLINEPRIMITIVE2D
:
357 processBorderLinePrimitive2D(
358 static_cast<const drawinglayer::primitive2d::BorderLinePrimitive2D
&>(rCandidate
));
361 case PRIMITIVE2D_ID_FILLGRADIENTPRIMITIVE2D
:
363 processFillGradientPrimitive2D(
364 static_cast<const drawinglayer::primitive2d::FillGradientPrimitive2D
&>(rCandidate
));
367 case PRIMITIVE2D_ID_PATTERNFILLPRIMITIVE2D
:
369 processPatternFillPrimitive2D(
370 static_cast<const drawinglayer::primitive2d::PatternFillPrimitive2D
&>(rCandidate
));
375 SAL_INFO("drawinglayer", "default case for " << drawinglayer::primitive2d::idToString(
376 rCandidate
.getPrimitive2DID()));
377 // process recursively
384 void VclPixelProcessor2D::processWrongSpellPrimitive2D(
385 const primitive2d::WrongSpellPrimitive2D
& rWrongSpellPrimitive
)
387 if (!renderWrongSpellPrimitive2D(rWrongSpellPrimitive
, *mpOutputDevice
, maCurrentTransformation
,
388 maBColorModifierStack
))
390 // fallback to decomposition (MetaFile)
391 process(rWrongSpellPrimitive
);
395 void VclPixelProcessor2D::processTextSimplePortionPrimitive2D(
396 const primitive2d::TextSimplePortionPrimitive2D
& rCandidate
)
398 // Adapt evtl. used special DrawMode
399 const DrawModeFlags
nOriginalDrawMode(mpOutputDevice
->GetDrawMode());
400 adaptTextToFillDrawMode();
402 if (SvtOptionsDrawinglayer::IsRenderSimpleTextDirect())
404 RenderTextSimpleOrDecoratedPortionPrimitive2D(rCandidate
);
412 mpOutputDevice
->SetDrawMode(nOriginalDrawMode
);
415 void VclPixelProcessor2D::processTextDecoratedPortionPrimitive2D(
416 const primitive2d::TextSimplePortionPrimitive2D
& rCandidate
)
418 // Adapt evtl. used special DrawMode
419 const DrawModeFlags
nOriginalDrawMode(mpOutputDevice
->GetDrawMode());
420 adaptTextToFillDrawMode();
422 if (SvtOptionsDrawinglayer::IsRenderDecoratedTextDirect())
424 RenderTextSimpleOrDecoratedPortionPrimitive2D(rCandidate
);
432 mpOutputDevice
->SetDrawMode(nOriginalDrawMode
);
435 void VclPixelProcessor2D::processPolygonHairlinePrimitive2D(
436 const primitive2d::PolygonHairlinePrimitive2D
& rPolygonHairlinePrimitive2D
)
438 if (tryDrawPolygonHairlinePrimitive2DDirect(rPolygonHairlinePrimitive2D
, 0.0))
443 // direct draw of hairline
444 RenderPolygonHairlinePrimitive2D(rPolygonHairlinePrimitive2D
, true);
447 void VclPixelProcessor2D::processBitmapPrimitive2D(
448 const primitive2d::BitmapPrimitive2D
& rBitmapCandidate
)
450 // check if graphic content is inside discrete local ViewPort
451 const basegfx::B2DRange
& rDiscreteViewPort(getViewInformation2D().getDiscreteViewport());
452 const basegfx::B2DHomMatrix
aLocalTransform(maCurrentTransformation
453 * rBitmapCandidate
.getTransform());
455 if (!rDiscreteViewPort
.isEmpty())
457 basegfx::B2DRange
aUnitRange(0.0, 0.0, 1.0, 1.0);
459 aUnitRange
.transform(aLocalTransform
);
461 if (!aUnitRange
.overlaps(rDiscreteViewPort
))
463 // content is outside discrete local ViewPort
468 RenderBitmapPrimitive2D(rBitmapCandidate
);
471 void VclPixelProcessor2D::processPolyPolygonGradientPrimitive2D(
472 const primitive2d::PolyPolygonGradientPrimitive2D
& rPolygonCandidate
)
474 basegfx::B2DPolyPolygon
aLocalPolyPolygon(rPolygonCandidate
.getB2DPolyPolygon());
476 // no geometry, no need to render, done
477 if (!aLocalPolyPolygon
.count())
480 // *try* direct draw (AKA using old VCL stuff) to render gradient
481 const attribute::FillGradientAttribute
& rGradient(rPolygonCandidate
.getFillGradient());
483 // MCGR: *many* - and not only GradientStops - cases cannot be handled by VCL
484 // so use decomposition
485 // NOTE: There may be even more reasons to detect, e.g. a ViewTransformation
486 // that uses shear/rotate/mirror (what VCL cannot handle at all), see
487 // other checks already in processFillGradientPrimitive2D
488 if (rGradient
.cannotBeHandledByVCL())
490 process(rPolygonCandidate
);
494 basegfx::BColor
aStartColor(
495 maBColorModifierStack
.getModifiedColor(rGradient
.getColorStops().front().getStopColor()));
496 basegfx::BColor
aEndColor(
497 maBColorModifierStack
.getModifiedColor(rGradient
.getColorStops().back().getStopColor()));
499 if (aStartColor
== aEndColor
)
501 // no gradient at all, draw as polygon in AA and non-AA case
502 aLocalPolyPolygon
.transform(maCurrentTransformation
);
503 mpOutputDevice
->SetLineColor();
504 mpOutputDevice
->SetFillColor(Color(aStartColor
));
505 mpOutputDevice
->DrawPolyPolygon(aLocalPolyPolygon
);
509 // use the primitive decomposition
510 process(rPolygonCandidate
);
513 void VclPixelProcessor2D::processPolyPolygonColorPrimitive2D(
514 const primitive2d::PolyPolygonColorPrimitive2D
& rPolyPolygonColorPrimitive2D
)
516 // try to use directly
517 basegfx::B2DPolyPolygon aLocalPolyPolygon
;
519 tryDrawPolyPolygonColorPrimitive2DDirect(rPolyPolygonColorPrimitive2D
, 0.0);
520 // okay, done. In this case no gaps should have to be repaired, too
522 // when AA is on and this filled polygons are the result of stroked line geometry,
523 // draw the geometry once extra as lines to avoid AA 'gaps' between partial polygons
524 // Caution: This is needed in both cases (!)
525 if (!(mnPolygonStrokePrimitive2D
&& getViewInformation2D().getUseAntiAliasing()
526 && (mpOutputDevice
->GetAntialiasing() & AntialiasingFlags::Enable
)))
529 const basegfx::BColor
aPolygonColor(
530 maBColorModifierStack
.getModifiedColor(rPolyPolygonColorPrimitive2D
.getBColor()));
531 sal_uInt32
nCount(aLocalPolyPolygon
.count());
535 aLocalPolyPolygon
= rPolyPolygonColorPrimitive2D
.getB2DPolyPolygon();
536 aLocalPolyPolygon
.transform(maCurrentTransformation
);
537 nCount
= aLocalPolyPolygon
.count();
540 mpOutputDevice
->SetFillColor();
541 mpOutputDevice
->SetLineColor(Color(aPolygonColor
));
543 for (sal_uInt32
a(0); a
< nCount
; a
++)
545 mpOutputDevice
->DrawPolyLine(aLocalPolyPolygon
.getB2DPolygon(a
), 0.0);
549 void VclPixelProcessor2D::processUnifiedTransparencePrimitive2D(
550 const primitive2d::UnifiedTransparencePrimitive2D
& rUniTransparenceCandidate
)
552 // Detect if a single PolyPolygonColorPrimitive2D is contained; in that case,
553 // use the faster OutputDevice::DrawTransparent method
554 const primitive2d::Primitive2DContainer
& rContent
= rUniTransparenceCandidate
.getChildren();
556 if (rContent
.empty())
559 if (0.0 == rUniTransparenceCandidate
.getTransparence())
561 // not transparent at all, use content
562 process(rUniTransparenceCandidate
.getChildren());
564 else if (rUniTransparenceCandidate
.getTransparence() > 0.0
565 && rUniTransparenceCandidate
.getTransparence() < 1.0)
567 bool bDrawTransparentUsed(false);
569 if (1 == rContent
.size())
571 const primitive2d::BasePrimitive2D
* pBasePrimitive
= rContent
[0].get();
573 switch (pBasePrimitive
->getPrimitive2DID())
575 case PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D
:
577 // single transparent tools::PolyPolygon identified, use directly
578 const primitive2d::PolyPolygonColorPrimitive2D
* pPoPoColor
579 = static_cast<const primitive2d::PolyPolygonColorPrimitive2D
*>(
581 SAL_WARN_IF(!pPoPoColor
, "drawinglayer",
582 "OOps, PrimitiveID and PrimitiveType do not match (!)");
583 bDrawTransparentUsed
= true;
584 tryDrawPolyPolygonColorPrimitive2DDirect(
585 *pPoPoColor
, rUniTransparenceCandidate
.getTransparence());
588 case PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D
:
590 // single transparent PolygonHairlinePrimitive2D identified, use directly
591 const primitive2d::PolygonHairlinePrimitive2D
* pPoHair
592 = static_cast<const primitive2d::PolygonHairlinePrimitive2D
*>(
594 SAL_WARN_IF(!pPoHair
, "drawinglayer",
595 "OOps, PrimitiveID and PrimitiveType do not match (!)");
597 // do no tallow by default - problem is that self-overlapping parts of this geometry will
598 // not be in an all-same transparency but will already alpha-cover themselves with blending.
599 // This is not what the UnifiedTransparencePrimitive2D defines: It requires all its
600 // content to be uniformly transparent.
601 // For hairline the effect is pretty minimal, but still not correct.
602 bDrawTransparentUsed
= false;
605 case PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D
:
607 // single transparent PolygonStrokePrimitive2D identified, use directly
608 const primitive2d::PolygonStrokePrimitive2D
* pPoStroke
609 = static_cast<const primitive2d::PolygonStrokePrimitive2D
*>(pBasePrimitive
);
610 SAL_WARN_IF(!pPoStroke
, "drawinglayer",
611 "OOps, PrimitiveID and PrimitiveType do not match (!)");
613 // do no tallow by default - problem is that self-overlapping parts of this geometry will
614 // not be in an all-same transparency but will already alpha-cover themselves with blending.
615 // This is not what the UnifiedTransparencePrimitive2D defines: It requires all its
616 // content to be uniformly transparent.
617 // To check, activate and draw a wide transparent self-crossing line/curve
618 bDrawTransparentUsed
= false;
622 SAL_INFO("drawinglayer",
623 "default case for " << drawinglayer::primitive2d::idToString(
624 rUniTransparenceCandidate
.getPrimitive2DID()));
629 if (!bDrawTransparentUsed
)
631 // unified sub-transparence. Draw to VDev first.
632 RenderUnifiedTransparencePrimitive2D(rUniTransparenceCandidate
);
637 void VclPixelProcessor2D::processControlPrimitive2D(
638 const primitive2d::ControlPrimitive2D
& rControlPrimitive
)
641 const uno::Reference
<awt::XControl
>& rXControl(rControlPrimitive
.getXControl());
645 // remember old graphics and create new
646 uno::Reference
<awt::XView
> xControlView(rXControl
, uno::UNO_QUERY_THROW
);
647 const uno::Reference
<awt::XGraphics
> xOriginalGraphics(xControlView
->getGraphics());
648 const uno::Reference
<awt::XGraphics
> xNewGraphics(mpOutputDevice
->CreateUnoGraphics());
650 if (xNewGraphics
.is())
652 // find out if the control is already visualized as a VCL-ChildWindow. If yes,
653 // it does not need to be painted at all.
654 uno::Reference
<awt::XWindow2
> xControlWindow(rXControl
, uno::UNO_QUERY_THROW
);
655 bool bControlIsVisibleAsChildWindow(rXControl
->getPeer().is()
656 && xControlWindow
->isVisible());
658 // tdf#131281 The FormControls are not painted when using the Tiled Rendering for a simple
659 // reason: when e.g. bControlIsVisibleAsChildWindow is true. This is the case because the
660 // office is in non-layout mode (default for controls at startup). For the common office
661 // this means that there exists a real VCL-System-Window for the control, so it is *not*
662 // painted here due to being exactly obscured by that real Window (and creates danger of
664 // Tiled Rendering clients usually do *not* have real VCL-Windows for the controls, but
665 // exactly that would be needed on each client displaying the tiles (what would be hard
666 // to do but also would have advantages - the clients would have real controls in the
667 // shape of their target system which could be interacted with...). It is also what the
669 // For now, fallback to just render these controls when Tiled Rendering is active to just
670 // have them displayed on all clients.
671 if (bControlIsVisibleAsChildWindow
&& comphelper::LibreOfficeKit::isActive())
673 // Do force paint when we are in Tiled Renderer and FormControl is 'visible'
674 bControlIsVisibleAsChildWindow
= false;
677 if (!bControlIsVisibleAsChildWindow
)
679 // Needs to be drawn. Link new graphics and view
680 xControlView
->setGraphics(xNewGraphics
);
683 const basegfx::B2DHomMatrix
aObjectToPixel(maCurrentTransformation
684 * rControlPrimitive
.getTransform());
685 const basegfx::B2DPoint
aTopLeftPixel(aObjectToPixel
* basegfx::B2DPoint(0.0, 0.0));
687 // Do not forget to use the evtl. offsetted origin of the target device,
688 // e.g. when used with mask/transparence buffer device
689 const Point
aOrigin(mpOutputDevice
->GetMapMode().GetOrigin());
690 xControlView
->draw(aOrigin
.X() + basegfx::fround(aTopLeftPixel
.getX()),
691 aOrigin
.Y() + basegfx::fround(aTopLeftPixel
.getY()));
693 // restore original graphics
694 xControlView
->setGraphics(xOriginalGraphics
);
698 catch (const uno::Exception
&)
700 // #i116763# removing since there is a good alternative when the xControlView
701 // is not found and it is allowed to happen
702 // DBG_UNHANDLED_EXCEPTION();
704 // process recursively and use the decomposition as Bitmap
705 process(rControlPrimitive
);
709 void VclPixelProcessor2D::processPolygonStrokePrimitive2D(
710 const primitive2d::PolygonStrokePrimitive2D
& rPolygonStrokePrimitive2D
)
712 // try to use directly
713 if (tryDrawPolygonStrokePrimitive2DDirect(rPolygonStrokePrimitive2D
, 0.0))
718 // the stroke primitive may be decomposed to filled polygons. To keep
719 // evtl. set DrawModes aka DrawModeFlags::BlackLine, DrawModeFlags::GrayLine,
720 // DrawModeFlags::GhostedLine, DrawModeFlags::WhiteLine or DrawModeFlags::SettingsLine
721 // working, these need to be copied to the corresponding fill modes
722 const DrawModeFlags
nOriginalDrawMode(mpOutputDevice
->GetDrawMode());
723 adaptLineToFillDrawMode();
725 // polygon stroke primitive
727 // Lines with 1 and 2 pixel width without AA need special treatment since their visualization
728 // as filled polygons is geometrically correct but looks wrong since polygon filling avoids
729 // the right and bottom pixels. The used method evaluates that and takes the correct action,
730 // including calling recursively with decomposition if line is wide enough
731 RenderPolygonStrokePrimitive2D(rPolygonStrokePrimitive2D
);
734 mpOutputDevice
->SetDrawMode(nOriginalDrawMode
);
737 void VclPixelProcessor2D::processFillHatchPrimitive2D(
738 const primitive2d::FillHatchPrimitive2D
& rFillHatchPrimitive
)
740 if (getViewInformation2D().getUseAntiAliasing())
742 // if AA is used (or ignore smoothing is on), there is no need to smooth
743 // hatch painting, use decomposition
744 process(rFillHatchPrimitive
);
748 // without AA, use VCL to draw the hatch. It snaps hatch distances to the next pixel
749 // and forces hatch distance to be >= 3 pixels to make the hatch display look smoother.
750 // This is wrong in principle, but looks nicer. This could also be done here directly
751 // without VCL usage if needed
752 const attribute::FillHatchAttribute
& rFillHatchAttributes
753 = rFillHatchPrimitive
.getFillHatch();
755 // create hatch polygon in range size and discrete coordinates
756 basegfx::B2DRange
aHatchRange(rFillHatchPrimitive
.getOutputRange());
757 aHatchRange
.transform(maCurrentTransformation
);
758 const basegfx::B2DPolygon
aHatchPolygon(basegfx::utils::createPolygonFromRect(aHatchRange
));
760 if (rFillHatchAttributes
.isFillBackground())
762 // #i111846# background fill is active; draw fill polygon
763 const basegfx::BColor
aPolygonColor(
764 maBColorModifierStack
.getModifiedColor(rFillHatchPrimitive
.getBColor()));
766 mpOutputDevice
->SetFillColor(Color(aPolygonColor
));
767 mpOutputDevice
->SetLineColor();
768 mpOutputDevice
->DrawPolygon(aHatchPolygon
);
771 // set hatch line color
772 const basegfx::BColor
aHatchColor(
773 maBColorModifierStack
.getModifiedColor(rFillHatchPrimitive
.getBColor()));
774 mpOutputDevice
->SetFillColor();
775 mpOutputDevice
->SetLineColor(Color(aHatchColor
));
778 HatchStyle
eHatchStyle(HatchStyle::Single
);
780 switch (rFillHatchAttributes
.getStyle())
782 default: // HatchStyle::Single
786 case attribute::HatchStyle::Double
:
788 eHatchStyle
= HatchStyle::Double
;
791 case attribute::HatchStyle::Triple
:
793 eHatchStyle
= HatchStyle::Triple
;
799 const basegfx::B2DVector
aDiscreteDistance(
800 maCurrentTransformation
* basegfx::B2DVector(rFillHatchAttributes
.getDistance(), 0.0));
801 const sal_uInt32
nDistance(basegfx::fround(aDiscreteDistance
.getLength()));
802 const sal_uInt32
nAngle10(
803 basegfx::rad2deg
<10>(basegfx::fround(rFillHatchAttributes
.getAngle())));
804 ::Hatch
aVCLHatch(eHatchStyle
, Color(rFillHatchAttributes
.getColor()), nDistance
,
807 // draw hatch using VCL
808 mpOutputDevice
->DrawHatch(::tools::PolyPolygon(::tools::Polygon(aHatchPolygon
)), aVCLHatch
);
812 void VclPixelProcessor2D::processBackgroundColorPrimitive2D(
813 const primitive2d::BackgroundColorPrimitive2D
& rPrimitive
)
815 // #i98404# Handle directly, especially when AA is active
816 const AntialiasingFlags
nOriginalAA(mpOutputDevice
->GetAntialiasing());
818 // switch AA off in all cases
819 mpOutputDevice
->SetAntialiasing(mpOutputDevice
->GetAntialiasing() & ~AntialiasingFlags::Enable
);
821 // create color for fill
822 const basegfx::BColor
aPolygonColor(
823 maBColorModifierStack
.getModifiedColor(rPrimitive
.getBColor()));
824 Color
aFillColor(aPolygonColor
);
825 aFillColor
.SetAlpha(255 - sal_uInt8((rPrimitive
.getTransparency() * 255.0) + 0.5));
826 mpOutputDevice
->SetFillColor(aFillColor
);
827 mpOutputDevice
->SetLineColor();
829 // create rectangle for fill
830 const basegfx::B2DRange
& aViewport(getViewInformation2D().getDiscreteViewport());
831 const ::tools::Rectangle
aRectangle(static_cast<sal_Int32
>(floor(aViewport
.getMinX())),
832 static_cast<sal_Int32
>(floor(aViewport
.getMinY())),
833 static_cast<sal_Int32
>(ceil(aViewport
.getMaxX())),
834 static_cast<sal_Int32
>(ceil(aViewport
.getMaxY())));
835 mpOutputDevice
->DrawRect(aRectangle
);
837 // restore AA setting
838 mpOutputDevice
->SetAntialiasing(nOriginalAA
);
841 void VclPixelProcessor2D::processBorderLinePrimitive2D(
842 const drawinglayer::primitive2d::BorderLinePrimitive2D
& rBorder
)
844 // Process recursively, but switch off AntiAliasing for
845 // horizontal/vertical lines (*not* diagonal lines).
846 // Checked using AntialiasingFlags::PixelSnapHairline instead,
847 // but with AntiAliasing on the display really is too 'ghosty' when
848 // using fine stroking. Correct, but 'ghosty'.
850 // It has shown that there are quite some problems here:
851 // - vcl OutDev renderer methods still use fallbacks to paint
852 // multiple single lines between discrete sizes of < 3.5 what
853 // looks bad and does not match
854 // - mix of filled Polygons and Lines is bad when AA switched off
855 // - Alignment of AA with non-AA may be bad in diverse different
858 // Due to these reasons I change the strategy: Always draw AAed, but
859 // allow fallback to test/check and if needed. The normal case
860 // where BorderLines will be system-dependently snapped to have at
861 // least a single discrete width per partial line (there may be up to
862 // three) works well nowadays due to most renderers moving the AA stuff
863 // by 0.5 pixels (discrete units) to match well with the non-AAed parts.
865 // Env-Switch for steering this, default is off.
866 // Enable by setting at all (and to something)
867 static const char* pSwitchOffAntiAliasingForHorVerBorderlines(
868 getenv("SAL_SWITCH_OFF_ANTIALIASING_FOR_HOR_VER_BORTDERLINES"));
869 static bool bSwitchOffAntiAliasingForHorVerBorderlines(
870 nullptr != pSwitchOffAntiAliasingForHorVerBorderlines
);
872 if (bSwitchOffAntiAliasingForHorVerBorderlines
873 && rBorder
.isHorizontalOrVertical(getViewInformation2D()))
875 AntialiasingFlags nAntiAliasing
= mpOutputDevice
->GetAntialiasing();
876 mpOutputDevice
->SetAntialiasing(nAntiAliasing
& ~AntialiasingFlags::Enable
);
878 mpOutputDevice
->SetAntialiasing(nAntiAliasing
);
886 void VclPixelProcessor2D::processInvertPrimitive2D(const primitive2d::BasePrimitive2D
& rCandidate
)
888 // invert primitive (currently only used for HighContrast fallback for selection in SW and SC).
889 // (Not true, also used at least for the drawing of dragged column and row boundaries in SC.)
890 // Set OutDev to XOR and switch AA off (XOR does not work with AA)
891 mpOutputDevice
->Push();
892 mpOutputDevice
->SetRasterOp(RasterOp::Xor
);
893 const AntialiasingFlags
nAntiAliasing(mpOutputDevice
->GetAntialiasing());
894 mpOutputDevice
->SetAntialiasing(nAntiAliasing
& ~AntialiasingFlags::Enable
);
896 // process content recursively
900 mpOutputDevice
->Pop();
901 mpOutputDevice
->SetAntialiasing(nAntiAliasing
);
904 void VclPixelProcessor2D::processMetaFilePrimitive2D(const primitive2d::BasePrimitive2D
& rCandidate
)
907 const bool bForceLineSnap(getViewInformation2D().getPixelSnapHairline());
908 const AntialiasingFlags
nOldAntiAliase(mpOutputDevice
->GetAntialiasing());
912 mpOutputDevice
->SetAntialiasing(nOldAntiAliase
| AntialiasingFlags::PixelSnapHairline
);
919 mpOutputDevice
->SetAntialiasing(nOldAntiAliase
);
923 void VclPixelProcessor2D::processFillGradientPrimitive2D(
924 const primitive2d::FillGradientPrimitive2D
& rPrimitive
)
926 const attribute::FillGradientAttribute
& rFillGradient
= rPrimitive
.getFillGradient();
927 bool useDecompose(false);
929 // MCGR: *many* - and not only GradientStops - cases cannot be handled by VCL
930 // so use decomposition
931 if (rFillGradient
.cannotBeHandledByVCL())
936 // tdf#149754 VCL gradient draw is not capable to handle all primitive gradient definitions,
937 // what should be clear due to being developed to extend/replace them in
938 // capabilities & precision.
939 // It is e.g. not capable to correctly paint if the OutputRange is not completely
940 // inside the DefinitionRange, thus forcing to paint gradient parts *outside* the
942 // This happens for Writer with Frames anchored in Frames (and was broken due to
943 // falling back to VCL Gradient paint here), and for the new SlideBackgroundFill
944 // Fill mode for objects using it that reach outside the page (which is the
945 // DefinitionRange in that case).
946 // I see no real reason to fallback here to OutputDevice::DrawGradient and VCL
947 // gradient paint at all (system-dependent renderers wouldn't in the future), but
948 // will for convenience only add that needed additional correcting case
949 if (!useDecompose
&& !rPrimitive
.getDefinitionRange().isInside(rPrimitive
.getOutputRange()))
954 // tdf#151081 need to use regular primitive decomposition when the gradient
955 // is transformed in any other way then just translate & scale
958 basegfx::B2DVector aScale
, aTranslate
;
959 double fRotate
, fShearX
;
961 maCurrentTransformation
.decompose(aScale
, aTranslate
, fRotate
, fShearX
);
963 // detect if transformation is rotated, sheared or mirrored in X and/or Y
964 if (!basegfx::fTools::equalZero(fRotate
) || !basegfx::fTools::equalZero(fShearX
)
965 || aScale
.getX() < 0.0 || aScale
.getY() < 0.0)
973 // default is to use the direct render below. For security,
974 // keep the (simple) fallback to decompose in place here
975 static bool bTryDirectRender(true);
977 if (bTryDirectRender
)
979 // MCGR: Avoid one level of primitive creation, use FillGradientPrimitive2D
980 // tooling to directly create needed geometry & color for getting better
981 // performance (partially compensate for potentially more expensive multi
983 // To handle a primitive that needs paint, either use decompose, or - when you
984 // do not want that for any reason, e.g. extra primitives created - implement
985 // a direct handling in your primitive renderer. This is always possible
986 // since primitives by definition are self-contained what means they have all
987 // needed data locally available to do so.
988 // The question is the complexity to invest - the implemented decompose
989 // is always a good hint of what is needed to do this. In this case I decided
990 // to add some tooling methods to the primitive itself to support this. These
991 // are used in decompose and can be used - as here now - for direct handling,
992 // too. This is always a possibility in primitive handling - you can, but do not
994 mpOutputDevice
->SetFillColor(
995 Color(maBColorModifierStack
.getModifiedColor(rPrimitive
.getOuterColor())));
996 mpOutputDevice
->SetLineColor();
997 mpOutputDevice
->DrawTransparent(
998 maCurrentTransformation
,
999 basegfx::B2DPolyPolygon(
1000 basegfx::utils::createPolygonFromRect(rPrimitive
.getOutputRange())),
1003 // paint solid fill steps by providing callback as lambda
1004 auto aCallback([&rPrimitive
, this](const basegfx::B2DHomMatrix
& rMatrix
,
1005 const basegfx::BColor
& rColor
) {
1006 // create part polygon
1007 basegfx::B2DPolygon
aNewPoly(rPrimitive
.getUnitPolygon());
1008 aNewPoly
.transform(rMatrix
);
1010 // create solid fill
1011 mpOutputDevice
->SetFillColor(Color(maBColorModifierStack
.getModifiedColor(rColor
)));
1012 mpOutputDevice
->DrawTransparent(maCurrentTransformation
,
1013 basegfx::B2DPolyPolygon(aNewPoly
), 0.0);
1016 // call value generator to trigger callbacks
1017 rPrimitive
.generateMatricesAndColors(aCallback
);
1021 // use the decompose
1022 process(rPrimitive
);
1028 // try to use vcl - since vcl uses the old gradient paint mechanisms this may
1029 // create wrong geometries. If so, add another case above for useDecompose
1030 Gradient
aGradient(rFillGradient
.getStyle(),
1031 Color(rFillGradient
.getColorStops().front().getStopColor()),
1032 Color(rFillGradient
.getColorStops().back().getStopColor()));
1034 aGradient
.SetAngle(Degree10(static_cast<int>(basegfx::rad2deg
<10>(rFillGradient
.getAngle()))));
1035 aGradient
.SetBorder(rFillGradient
.getBorder() * 100);
1036 aGradient
.SetOfsX(rFillGradient
.getOffsetX() * 100.0);
1037 aGradient
.SetOfsY(rFillGradient
.getOffsetY() * 100.0);
1038 aGradient
.SetSteps(rFillGradient
.getSteps());
1040 basegfx::B2DRange
aOutputRange(rPrimitive
.getOutputRange());
1041 aOutputRange
.transform(maCurrentTransformation
);
1042 basegfx::B2DRange
aFullRange(rPrimitive
.getDefinitionRange());
1043 aFullRange
.transform(maCurrentTransformation
);
1045 const tools::Rectangle
aOutputRectangle(
1046 std::floor(aOutputRange
.getMinX()), std::floor(aOutputRange
.getMinY()),
1047 std::ceil(aOutputRange
.getMaxX()), std::ceil(aOutputRange
.getMaxY()));
1048 const tools::Rectangle
aFullRectangle(
1049 std::floor(aFullRange
.getMinX()), std::floor(aFullRange
.getMinY()),
1050 std::ceil(aFullRange
.getMaxX()), std::ceil(aFullRange
.getMaxY()));
1052 mpOutputDevice
->Push(vcl::PushFlags::CLIPREGION
);
1053 mpOutputDevice
->IntersectClipRegion(aOutputRectangle
);
1054 mpOutputDevice
->DrawGradient(aFullRectangle
, aGradient
);
1055 mpOutputDevice
->Pop();
1058 void VclPixelProcessor2D::processPatternFillPrimitive2D(
1059 const primitive2d::PatternFillPrimitive2D
& rPrimitive
)
1061 const basegfx::B2DRange
& rReferenceRange
= rPrimitive
.getReferenceRange();
1062 if (rReferenceRange
.isEmpty() || rReferenceRange
.getWidth() <= 0.0
1063 || rReferenceRange
.getHeight() <= 0.0)
1066 basegfx::B2DPolyPolygon aMask
= rPrimitive
.getMask();
1067 aMask
.transform(maCurrentTransformation
);
1068 const basegfx::B2DRange
aMaskRange(aMask
.getB2DRange());
1070 if (aMaskRange
.isEmpty() || aMaskRange
.getWidth() <= 0.0 || aMaskRange
.getHeight() <= 0.0)
1073 sal_uInt32 nTileWidth
, nTileHeight
;
1074 rPrimitive
.getTileSize(nTileWidth
, nTileHeight
, getViewInformation2D());
1075 if (nTileWidth
== 0 || nTileHeight
== 0)
1077 BitmapEx aTileImage
= rPrimitive
.createTileImage(nTileWidth
, nTileHeight
);
1078 tools::Rectangle aMaskRect
= vcl::unotools::rectangleFromB2DRectangle(aMaskRange
);
1080 // Unless smooth edges are needed, simply use clipping.
1081 if (basegfx::utils::isRectangle(aMask
) || !getViewInformation2D().getUseAntiAliasing())
1083 mpOutputDevice
->Push(vcl::PushFlags::CLIPREGION
);
1084 mpOutputDevice
->IntersectClipRegion(vcl::Region(aMask
));
1085 Wallpaper
aWallpaper(aTileImage
);
1086 aWallpaper
.SetColor(COL_TRANSPARENT
);
1087 mpOutputDevice
->DrawWallpaper(aMaskRect
, aWallpaper
);
1088 mpOutputDevice
->Pop();
1092 impBufferDevice
aBufferDevice(*mpOutputDevice
, aMaskRange
);
1094 if (!aBufferDevice
.isVisible())
1097 // remember last OutDev and set to content
1098 OutputDevice
* pLastOutputDevice
= mpOutputDevice
;
1099 mpOutputDevice
= &aBufferDevice
.getContent();
1101 // if the tile is a single pixel big, just flood fill with that pixel color
1102 if (nTileWidth
== 1 && nTileHeight
== 1)
1104 Color col
= aTileImage
.GetPixelColor(0, 0);
1105 mpOutputDevice
->SetLineColor(col
);
1106 mpOutputDevice
->SetFillColor(col
);
1107 mpOutputDevice
->DrawRect(aMaskRect
);
1111 Wallpaper
aWallpaper(aTileImage
);
1112 aWallpaper
.SetColor(COL_TRANSPARENT
);
1113 mpOutputDevice
->DrawWallpaper(aMaskRect
, aWallpaper
);
1116 // back to old OutDev
1117 mpOutputDevice
= pLastOutputDevice
;
1120 VirtualDevice
& rMask
= aBufferDevice
.getTransparence();
1121 rMask
.SetLineColor();
1122 rMask
.SetFillColor(COL_BLACK
);
1123 rMask
.DrawPolyPolygon(aMask
);
1125 // dump buffer to outdev
1126 aBufferDevice
.paint();
1129 } // end of namespace
1131 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */