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/BitmapBasicMorphologyFilter.hxx>
27 #include <vcl/BitmapFilterStackBlur.hxx>
28 #include <vcl/outdev.hxx>
29 #include <vcl/hatch.hxx>
30 #include <vcl/canvastools.hxx>
31 #include <basegfx/polygon/b2dpolygontools.hxx>
32 #include <basegfx/polygon/b2dpolypolygontools.hxx>
34 #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
35 #include <drawinglayer/primitive2d/Tools.hxx>
36 #include <drawinglayer/primitive2d/textprimitive2d.hxx>
37 #include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
38 #include <drawinglayer/primitive2d/PolygonStrokePrimitive2D.hxx>
39 #include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
40 #include <drawinglayer/primitive2d/PolyPolygonGradientPrimitive2D.hxx>
41 #include <drawinglayer/primitive2d/PolyPolygonGraphicPrimitive2D.hxx>
42 #include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
43 #include <drawinglayer/primitive2d/fillgraphicprimitive2d.hxx>
44 #include <drawinglayer/primitive2d/maskprimitive2d.hxx>
45 #include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx>
46 #include <drawinglayer/primitive2d/transparenceprimitive2d.hxx>
47 #include <drawinglayer/primitive2d/transformprimitive2d.hxx>
48 #include <drawinglayer/primitive2d/markerarrayprimitive2d.hxx>
49 #include <drawinglayer/primitive2d/glowprimitive2d.hxx>
50 #include <drawinglayer/primitive2d/wrongspellprimitive2d.hxx>
51 #include <drawinglayer/primitive2d/controlprimitive2d.hxx>
52 #include <drawinglayer/primitive2d/borderlineprimitive2d.hxx>
53 #include <drawinglayer/primitive2d/fillgradientprimitive2d.hxx>
54 #include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
55 #include <drawinglayer/primitive2d/pagepreviewprimitive2d.hxx>
56 #include <drawinglayer/primitive2d/backgroundcolorprimitive2d.hxx>
57 #include <drawinglayer/primitive2d/svggradientprimitive2d.hxx>
58 #include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx>
59 #include <drawinglayer/primitive2d/fillhatchprimitive2d.hxx>
60 #include <drawinglayer/primitive2d/epsprimitive2d.hxx>
61 #include <drawinglayer/primitive2d/softedgeprimitive2d.hxx>
62 #include <drawinglayer/primitive2d/shadowprimitive2d.hxx>
63 #include <drawinglayer/primitive2d/patternfillprimitive2d.hxx>
65 #include <com/sun/star/awt/XWindow2.hpp>
66 #include <com/sun/star/awt/XControl.hpp>
68 #include <svtools/optionsdrawinglayer.hxx>
69 #include <vcl/gradient.hxx>
71 using namespace com::sun::star
;
73 namespace drawinglayer::processor2d
75 struct VclPixelProcessor2D::Impl
77 AntialiasingFlags m_nOrigAntiAliasing
;
79 explicit Impl(OutputDevice
const& rOutDev
)
80 : m_nOrigAntiAliasing(rOutDev
.GetAntialiasing())
85 VclPixelProcessor2D::VclPixelProcessor2D(const geometry::ViewInformation2D
& rViewInformation
,
86 OutputDevice
& rOutDev
,
87 const basegfx::BColorModifierStack
& rInitStack
)
88 : VclProcessor2D(rViewInformation
, rOutDev
, rInitStack
)
89 , m_pImpl(new Impl(rOutDev
))
91 // prepare maCurrentTransformation matrix with viewTransformation to target directly to pixels
92 maCurrentTransformation
= rViewInformation
.getObjectToViewTransformation();
94 // prepare output directly to pixels
95 mpOutputDevice
->Push(vcl::PushFlags::MAPMODE
);
96 mpOutputDevice
->SetMapMode();
98 // react on AntiAliasing settings
99 if (SvtOptionsDrawinglayer::IsAntiAliasing())
101 mpOutputDevice
->SetAntialiasing(m_pImpl
->m_nOrigAntiAliasing
| AntialiasingFlags::Enable
);
105 mpOutputDevice
->SetAntialiasing(m_pImpl
->m_nOrigAntiAliasing
& ~AntialiasingFlags::Enable
);
109 VclPixelProcessor2D::~VclPixelProcessor2D()
112 mpOutputDevice
->Pop();
114 // restore AntiAliasing
115 mpOutputDevice
->SetAntialiasing(m_pImpl
->m_nOrigAntiAliasing
);
118 void VclPixelProcessor2D::tryDrawPolyPolygonColorPrimitive2DDirect(
119 const drawinglayer::primitive2d::PolyPolygonColorPrimitive2D
& rSource
, double fTransparency
)
121 if (!rSource
.getB2DPolyPolygon().count() || fTransparency
< 0.0 || fTransparency
>= 1.0)
127 const basegfx::BColor
aPolygonColor(
128 maBColorModifierStack
.getModifiedColor(rSource
.getBColor()));
130 mpOutputDevice
->SetFillColor(Color(aPolygonColor
));
131 mpOutputDevice
->SetLineColor();
132 mpOutputDevice
->DrawTransparent(maCurrentTransformation
, rSource
.getB2DPolyPolygon(),
136 bool VclPixelProcessor2D::tryDrawPolygonHairlinePrimitive2DDirect(
137 const drawinglayer::primitive2d::PolygonHairlinePrimitive2D
& rSource
, double fTransparency
)
139 const basegfx::B2DPolygon
& rLocalPolygon(rSource
.getB2DPolygon());
141 if (!rLocalPolygon
.count() || fTransparency
< 0.0 || fTransparency
>= 1.0)
147 const basegfx::BColor
aLineColor(maBColorModifierStack
.getModifiedColor(rSource
.getBColor()));
149 mpOutputDevice
->SetFillColor();
150 mpOutputDevice
->SetLineColor(Color(aLineColor
));
151 //aLocalPolygon.transform(maCurrentTransformation);
153 // try drawing; if it did not work, use standard fallback
154 return mpOutputDevice
->DrawPolyLineDirect(maCurrentTransformation
, rLocalPolygon
, 0.0,
158 bool VclPixelProcessor2D::tryDrawPolygonStrokePrimitive2DDirect(
159 const drawinglayer::primitive2d::PolygonStrokePrimitive2D
& rSource
, double fTransparency
)
161 const basegfx::B2DPolygon
& rLocalPolygon(rSource
.getB2DPolygon());
163 if (!rLocalPolygon
.count() || fTransparency
< 0.0 || fTransparency
>= 1.0)
169 if (basegfx::B2DLineJoin::NONE
== rSource
.getLineAttribute().getLineJoin()
170 && css::drawing::LineCap_BUTT
!= rSource
.getLineAttribute().getLineCap())
172 // better use decompose to get that combination done for now, see discussion
173 // at https://bugs.documentfoundation.org/show_bug.cgi?id=130478#c17 and ff
177 // MM01: Radically change here - no dismantle/applyLineDashing,
178 // let that happen low-level at DrawPolyLineDirect implementations
179 // to open up for buffering and evtl. direct draw with sys-dep
180 // graphic systems. Check for stroke is in use
181 const bool bStrokeAttributeNotUsed(rSource
.getStrokeAttribute().isDefault()
182 || 0.0 == rSource
.getStrokeAttribute().getFullDotDashLen());
184 const basegfx::BColor
aLineColor(
185 maBColorModifierStack
.getModifiedColor(rSource
.getLineAttribute().getColor()));
187 mpOutputDevice
->SetFillColor();
188 mpOutputDevice
->SetLineColor(Color(aLineColor
));
190 // MM01 draw direct, hand over dash data if available
191 return mpOutputDevice
->DrawPolyLineDirect(
192 maCurrentTransformation
, rLocalPolygon
,
193 // tdf#124848 use LineWidth direct, do not try to solve for zero-case (aka hairline)
194 rSource
.getLineAttribute().getWidth(), fTransparency
,
195 bStrokeAttributeNotUsed
? nullptr : &rSource
.getStrokeAttribute().getDotDashArray(),
196 rSource
.getLineAttribute().getLineJoin(), rSource
.getLineAttribute().getLineCap(),
197 rSource
.getLineAttribute().getMiterMinimumAngle());
202 GradientStyle
convertGradientStyle(drawinglayer::attribute::GradientStyle eGradientStyle
)
204 switch (eGradientStyle
)
206 case drawinglayer::attribute::GradientStyle::Axial
:
207 return GradientStyle::Axial
;
208 case drawinglayer::attribute::GradientStyle::Radial
:
209 return GradientStyle::Radial
;
210 case drawinglayer::attribute::GradientStyle::Elliptical
:
211 return GradientStyle::Elliptical
;
212 case drawinglayer::attribute::GradientStyle::Square
:
213 return GradientStyle::Square
;
214 case drawinglayer::attribute::GradientStyle::Rect
:
215 return GradientStyle::Rect
;
216 case drawinglayer::attribute::GradientStyle::Linear
:
217 return GradientStyle::Linear
;
220 return GradientStyle::Linear
;
224 } // end anonymous namespace
226 void VclPixelProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimitive2D
& rCandidate
)
228 switch (rCandidate
.getPrimitive2DID())
230 case PRIMITIVE2D_ID_WRONGSPELLPRIMITIVE2D
:
232 processWrongSpellPrimitive2D(
233 static_cast<const primitive2d::WrongSpellPrimitive2D
&>(rCandidate
));
236 case PRIMITIVE2D_ID_TEXTSIMPLEPORTIONPRIMITIVE2D
:
238 processTextSimplePortionPrimitive2D(
239 static_cast<const primitive2d::TextSimplePortionPrimitive2D
&>(rCandidate
));
242 case PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D
:
244 processTextDecoratedPortionPrimitive2D(
245 static_cast<const primitive2d::TextSimplePortionPrimitive2D
&>(rCandidate
));
248 case PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D
:
250 processPolygonHairlinePrimitive2D(
251 static_cast<const primitive2d::PolygonHairlinePrimitive2D
&>(rCandidate
));
254 case PRIMITIVE2D_ID_BITMAPPRIMITIVE2D
:
256 // direct draw of transformed BitmapEx primitive
257 processBitmapPrimitive2D(
258 static_cast<const primitive2d::BitmapPrimitive2D
&>(rCandidate
));
261 case PRIMITIVE2D_ID_FILLGRAPHICPRIMITIVE2D
:
263 // direct draw of fillBitmapPrimitive
264 RenderFillGraphicPrimitive2D(
265 static_cast<const primitive2d::FillGraphicPrimitive2D
&>(rCandidate
));
268 case PRIMITIVE2D_ID_POLYPOLYGONGRADIENTPRIMITIVE2D
:
270 processPolyPolygonGradientPrimitive2D(
271 static_cast<const primitive2d::PolyPolygonGradientPrimitive2D
&>(rCandidate
));
274 case PRIMITIVE2D_ID_POLYPOLYGONGRAPHICPRIMITIVE2D
:
276 // direct draw of bitmap
277 RenderPolyPolygonGraphicPrimitive2D(
278 static_cast<const primitive2d::PolyPolygonGraphicPrimitive2D
&>(rCandidate
));
281 case PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D
:
283 processPolyPolygonColorPrimitive2D(
284 static_cast<const primitive2d::PolyPolygonColorPrimitive2D
&>(rCandidate
));
287 case PRIMITIVE2D_ID_METAFILEPRIMITIVE2D
:
289 processMetaFilePrimitive2D(rCandidate
);
292 case PRIMITIVE2D_ID_MASKPRIMITIVE2D
:
295 RenderMaskPrimitive2DPixel(
296 static_cast<const primitive2d::MaskPrimitive2D
&>(rCandidate
));
299 case PRIMITIVE2D_ID_MODIFIEDCOLORPRIMITIVE2D
:
301 // modified color group. Force output to unified color.
302 RenderModifiedColorPrimitive2D(
303 static_cast<const primitive2d::ModifiedColorPrimitive2D
&>(rCandidate
));
306 case PRIMITIVE2D_ID_UNIFIEDTRANSPARENCEPRIMITIVE2D
:
308 processUnifiedTransparencePrimitive2D(
309 static_cast<const primitive2d::UnifiedTransparencePrimitive2D
&>(rCandidate
));
312 case PRIMITIVE2D_ID_TRANSPARENCEPRIMITIVE2D
:
314 // sub-transparence group. Draw to VDev first.
315 RenderTransparencePrimitive2D(
316 static_cast<const primitive2d::TransparencePrimitive2D
&>(rCandidate
));
319 case PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D
:
322 RenderTransformPrimitive2D(
323 static_cast<const primitive2d::TransformPrimitive2D
&>(rCandidate
));
326 case PRIMITIVE2D_ID_PAGEPREVIEWPRIMITIVE2D
:
328 // new XDrawPage for ViewInformation2D
329 RenderPagePreviewPrimitive2D(
330 static_cast<const primitive2d::PagePreviewPrimitive2D
&>(rCandidate
));
333 case PRIMITIVE2D_ID_MARKERARRAYPRIMITIVE2D
:
336 RenderMarkerArrayPrimitive2D(
337 static_cast<const primitive2d::MarkerArrayPrimitive2D
&>(rCandidate
));
340 case PRIMITIVE2D_ID_POINTARRAYPRIMITIVE2D
:
343 RenderPointArrayPrimitive2D(
344 static_cast<const primitive2d::PointArrayPrimitive2D
&>(rCandidate
));
347 case PRIMITIVE2D_ID_CONTROLPRIMITIVE2D
:
349 processControlPrimitive2D(
350 static_cast<const primitive2d::ControlPrimitive2D
&>(rCandidate
));
353 case PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D
:
355 processPolygonStrokePrimitive2D(
356 static_cast<const primitive2d::PolygonStrokePrimitive2D
&>(rCandidate
));
359 case PRIMITIVE2D_ID_FILLHATCHPRIMITIVE2D
:
361 processFillHatchPrimitive2D(
362 static_cast<const primitive2d::FillHatchPrimitive2D
&>(rCandidate
));
365 case PRIMITIVE2D_ID_BACKGROUNDCOLORPRIMITIVE2D
:
367 processBackgroundColorPrimitive2D(
368 static_cast<const primitive2d::BackgroundColorPrimitive2D
&>(rCandidate
));
371 case PRIMITIVE2D_ID_TEXTHIERARCHYEDITPRIMITIVE2D
:
374 // This primitive means that the content is derived from an active text edit,
375 // not from model data itself. Some renderers need to suppress this content, e.g.
376 // the pixel renderer used for displaying the edit view (like this one). It's
377 // not to be suppressed by the MetaFile renderers, so that the edited text is
378 // part of the MetaFile, e.g. needed for presentation previews.
379 // Action: Ignore here, do nothing.
382 case PRIMITIVE2D_ID_INVERTPRIMITIVE2D
:
384 processInvertPrimitive2D(rCandidate
);
387 case PRIMITIVE2D_ID_EPSPRIMITIVE2D
:
389 RenderEpsPrimitive2D(static_cast<const primitive2d::EpsPrimitive2D
&>(rCandidate
));
392 case PRIMITIVE2D_ID_SVGLINEARATOMPRIMITIVE2D
:
394 RenderSvgLinearAtomPrimitive2D(
395 static_cast<const primitive2d::SvgLinearAtomPrimitive2D
&>(rCandidate
));
398 case PRIMITIVE2D_ID_SVGRADIALATOMPRIMITIVE2D
:
400 RenderSvgRadialAtomPrimitive2D(
401 static_cast<const primitive2d::SvgRadialAtomPrimitive2D
&>(rCandidate
));
404 case PRIMITIVE2D_ID_BORDERLINEPRIMITIVE2D
:
406 processBorderLinePrimitive2D(
407 static_cast<const drawinglayer::primitive2d::BorderLinePrimitive2D
&>(rCandidate
));
410 case PRIMITIVE2D_ID_GLOWPRIMITIVE2D
:
412 processGlowPrimitive2D(
413 static_cast<const drawinglayer::primitive2d::GlowPrimitive2D
&>(rCandidate
));
416 case PRIMITIVE2D_ID_SOFTEDGEPRIMITIVE2D
:
418 processSoftEdgePrimitive2D(
419 static_cast<const drawinglayer::primitive2d::SoftEdgePrimitive2D
&>(rCandidate
));
422 case PRIMITIVE2D_ID_SHADOWPRIMITIVE2D
:
424 processShadowPrimitive2D(
425 static_cast<const drawinglayer::primitive2d::ShadowPrimitive2D
&>(rCandidate
));
428 case PRIMITIVE2D_ID_FILLGRADIENTPRIMITIVE2D
:
430 processFillGradientPrimitive2D(
431 static_cast<const drawinglayer::primitive2d::FillGradientPrimitive2D
&>(rCandidate
));
434 case PRIMITIVE2D_ID_PATTERNFILLPRIMITIVE2D
:
436 processPatternFillPrimitive2D(
437 static_cast<const drawinglayer::primitive2d::PatternFillPrimitive2D
&>(rCandidate
));
442 SAL_INFO("drawinglayer", "default case for " << drawinglayer::primitive2d::idToString(
443 rCandidate
.getPrimitive2DID()));
444 // process recursively
451 void VclPixelProcessor2D::processWrongSpellPrimitive2D(
452 const primitive2d::WrongSpellPrimitive2D
& rWrongSpellPrimitive
)
454 if (!renderWrongSpellPrimitive2D(rWrongSpellPrimitive
, *mpOutputDevice
, maCurrentTransformation
,
455 maBColorModifierStack
))
457 // fallback to decomposition (MetaFile)
458 process(rWrongSpellPrimitive
);
462 void VclPixelProcessor2D::processTextSimplePortionPrimitive2D(
463 const primitive2d::TextSimplePortionPrimitive2D
& rCandidate
)
465 // Adapt evtl. used special DrawMode
466 const DrawModeFlags
nOriginalDrawMode(mpOutputDevice
->GetDrawMode());
467 adaptTextToFillDrawMode();
469 if (SvtOptionsDrawinglayer::IsRenderSimpleTextDirect())
471 RenderTextSimpleOrDecoratedPortionPrimitive2D(rCandidate
);
479 mpOutputDevice
->SetDrawMode(nOriginalDrawMode
);
482 void VclPixelProcessor2D::processTextDecoratedPortionPrimitive2D(
483 const primitive2d::TextSimplePortionPrimitive2D
& rCandidate
)
485 // Adapt evtl. used special DrawMode
486 const DrawModeFlags
nOriginalDrawMode(mpOutputDevice
->GetDrawMode());
487 adaptTextToFillDrawMode();
489 if (SvtOptionsDrawinglayer::IsRenderDecoratedTextDirect())
491 RenderTextSimpleOrDecoratedPortionPrimitive2D(rCandidate
);
499 mpOutputDevice
->SetDrawMode(nOriginalDrawMode
);
502 void VclPixelProcessor2D::processPolygonHairlinePrimitive2D(
503 const primitive2d::PolygonHairlinePrimitive2D
& rPolygonHairlinePrimitive2D
)
505 if (tryDrawPolygonHairlinePrimitive2DDirect(rPolygonHairlinePrimitive2D
, 0.0))
510 // direct draw of hairline
511 RenderPolygonHairlinePrimitive2D(rPolygonHairlinePrimitive2D
, true);
514 void VclPixelProcessor2D::processBitmapPrimitive2D(
515 const primitive2d::BitmapPrimitive2D
& rBitmapCandidate
)
517 // check if graphic content is inside discrete local ViewPort
518 const basegfx::B2DRange
& rDiscreteViewPort(getViewInformation2D().getDiscreteViewport());
519 const basegfx::B2DHomMatrix
aLocalTransform(maCurrentTransformation
520 * rBitmapCandidate
.getTransform());
522 if (!rDiscreteViewPort
.isEmpty())
524 basegfx::B2DRange
aUnitRange(0.0, 0.0, 1.0, 1.0);
526 aUnitRange
.transform(aLocalTransform
);
528 if (!aUnitRange
.overlaps(rDiscreteViewPort
))
530 // content is outside discrete local ViewPort
535 RenderBitmapPrimitive2D(rBitmapCandidate
);
538 void VclPixelProcessor2D::processPolyPolygonGradientPrimitive2D(
539 const primitive2d::PolyPolygonGradientPrimitive2D
& rPolygonCandidate
)
541 // direct draw of gradient
542 const attribute::FillGradientAttribute
& rGradient(rPolygonCandidate
.getFillGradient());
543 basegfx::BColor
aStartColor(maBColorModifierStack
.getModifiedColor(rGradient
.getStartColor()));
544 basegfx::BColor
aEndColor(maBColorModifierStack
.getModifiedColor(rGradient
.getEndColor()));
545 basegfx::B2DPolyPolygon
aLocalPolyPolygon(rPolygonCandidate
.getB2DPolyPolygon());
547 if (!aLocalPolyPolygon
.count())
550 if (aStartColor
== aEndColor
)
552 // no gradient at all, draw as polygon in AA and non-AA case
553 aLocalPolyPolygon
.transform(maCurrentTransformation
);
554 mpOutputDevice
->SetLineColor();
555 mpOutputDevice
->SetFillColor(Color(aStartColor
));
556 mpOutputDevice
->DrawPolyPolygon(aLocalPolyPolygon
);
560 // use the primitive decomposition of the metafile
561 process(rPolygonCandidate
);
565 void VclPixelProcessor2D::processPolyPolygonColorPrimitive2D(
566 const primitive2d::PolyPolygonColorPrimitive2D
& rPolyPolygonColorPrimitive2D
)
568 // try to use directly
569 basegfx::B2DPolyPolygon aLocalPolyPolygon
;
571 tryDrawPolyPolygonColorPrimitive2DDirect(rPolyPolygonColorPrimitive2D
, 0.0);
572 // okay, done. In this case no gaps should have to be repaired, too
574 // when AA is on and this filled polygons are the result of stroked line geometry,
575 // draw the geometry once extra as lines to avoid AA 'gaps' between partial polygons
576 // Caution: This is needed in both cases (!)
577 if (!(mnPolygonStrokePrimitive2D
&& SvtOptionsDrawinglayer::IsAntiAliasing()
578 && (mpOutputDevice
->GetAntialiasing() & AntialiasingFlags::Enable
)))
581 const basegfx::BColor
aPolygonColor(
582 maBColorModifierStack
.getModifiedColor(rPolyPolygonColorPrimitive2D
.getBColor()));
583 sal_uInt32
nCount(aLocalPolyPolygon
.count());
587 aLocalPolyPolygon
= rPolyPolygonColorPrimitive2D
.getB2DPolyPolygon();
588 aLocalPolyPolygon
.transform(maCurrentTransformation
);
589 nCount
= aLocalPolyPolygon
.count();
592 mpOutputDevice
->SetFillColor();
593 mpOutputDevice
->SetLineColor(Color(aPolygonColor
));
595 for (sal_uInt32
a(0); a
< nCount
; a
++)
597 mpOutputDevice
->DrawPolyLine(aLocalPolyPolygon
.getB2DPolygon(a
), 0.0);
601 void VclPixelProcessor2D::processUnifiedTransparencePrimitive2D(
602 const primitive2d::UnifiedTransparencePrimitive2D
& rUniTransparenceCandidate
)
604 // Detect if a single PolyPolygonColorPrimitive2D is contained; in that case,
605 // use the faster OutputDevice::DrawTransparent method
606 const primitive2d::Primitive2DContainer
& rContent
= rUniTransparenceCandidate
.getChildren();
608 if (rContent
.empty())
611 if (0.0 == rUniTransparenceCandidate
.getTransparence())
613 // not transparent at all, use content
614 process(rUniTransparenceCandidate
.getChildren());
616 else if (rUniTransparenceCandidate
.getTransparence() > 0.0
617 && rUniTransparenceCandidate
.getTransparence() < 1.0)
619 bool bDrawTransparentUsed(false);
621 if (1 == rContent
.size())
623 const primitive2d::BasePrimitive2D
* pBasePrimitive
= rContent
[0].get();
625 switch (pBasePrimitive
->getPrimitive2DID())
627 case PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D
:
629 // single transparent tools::PolyPolygon identified, use directly
630 const primitive2d::PolyPolygonColorPrimitive2D
* pPoPoColor
631 = static_cast<const primitive2d::PolyPolygonColorPrimitive2D
*>(
633 SAL_WARN_IF(!pPoPoColor
, "drawinglayer",
634 "OOps, PrimitiveID and PrimitiveType do not match (!)");
635 bDrawTransparentUsed
= true;
636 tryDrawPolyPolygonColorPrimitive2DDirect(
637 *pPoPoColor
, rUniTransparenceCandidate
.getTransparence());
640 case PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D
:
642 // single transparent PolygonHairlinePrimitive2D identified, use directly
643 const primitive2d::PolygonHairlinePrimitive2D
* pPoHair
644 = static_cast<const primitive2d::PolygonHairlinePrimitive2D
*>(
646 SAL_WARN_IF(!pPoHair
, "drawinglayer",
647 "OOps, PrimitiveID and PrimitiveType do not match (!)");
649 // do no tallow by default - problem is that self-overlapping parts of this geometry will
650 // not be in an all-same transparency but will already alpha-cover themselves with blending.
651 // This is not what the UnifiedTransparencePrimitive2D defines: It requires all its
652 // content to be uniformly transparent.
653 // For hairline the effect is pretty minimal, but still not correct.
654 bDrawTransparentUsed
= false;
657 case PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D
:
659 // single transparent PolygonStrokePrimitive2D identified, use directly
660 const primitive2d::PolygonStrokePrimitive2D
* pPoStroke
661 = static_cast<const primitive2d::PolygonStrokePrimitive2D
*>(pBasePrimitive
);
662 SAL_WARN_IF(!pPoStroke
, "drawinglayer",
663 "OOps, PrimitiveID and PrimitiveType do not match (!)");
665 // do no tallow by default - problem is that self-overlapping parts of this geometry will
666 // not be in an all-same transparency but will already alpha-cover themselves with blending.
667 // This is not what the UnifiedTransparencePrimitive2D defines: It requires all its
668 // content to be uniformly transparent.
669 // To check, activate and draw a wide transparent self-crossing line/curve
670 bDrawTransparentUsed
= false;
674 SAL_INFO("drawinglayer",
675 "default case for " << drawinglayer::primitive2d::idToString(
676 rUniTransparenceCandidate
.getPrimitive2DID()));
681 if (!bDrawTransparentUsed
)
683 // unified sub-transparence. Draw to VDev first.
684 RenderUnifiedTransparencePrimitive2D(rUniTransparenceCandidate
);
689 void VclPixelProcessor2D::processControlPrimitive2D(
690 const primitive2d::ControlPrimitive2D
& rControlPrimitive
)
693 const uno::Reference
<awt::XControl
>& rXControl(rControlPrimitive
.getXControl());
697 // remember old graphics and create new
698 uno::Reference
<awt::XView
> xControlView(rXControl
, uno::UNO_QUERY_THROW
);
699 const uno::Reference
<awt::XGraphics
> xOriginalGraphics(xControlView
->getGraphics());
700 const uno::Reference
<awt::XGraphics
> xNewGraphics(mpOutputDevice
->CreateUnoGraphics());
702 if (xNewGraphics
.is())
704 // find out if the control is already visualized as a VCL-ChildWindow. If yes,
705 // it does not need to be painted at all.
706 uno::Reference
<awt::XWindow2
> xControlWindow(rXControl
, uno::UNO_QUERY_THROW
);
707 bool bControlIsVisibleAsChildWindow(rXControl
->getPeer().is()
708 && xControlWindow
->isVisible());
710 // tdf#131281 The FormControls are not painted when using the Tiled Rendering for a simple
711 // reason: when e.g. bControlIsVisibleAsChildWindow is true. This is the case because the
712 // office is in non-layout mode (default for controls at startup). For the common office
713 // this means that there exists a real VCL-System-Window for the control, so it is *not*
714 // painted here due to being exactly obscured by that real Window (and creates danger of
716 // Tiled Rendering clients usually do *not* have real VCL-Windows for the controls, but
717 // exactly that would be needed on each client displaying the tiles (what would be hard
718 // to do but also would have advantages - the clients would have real controls in the
719 // shape of their target system which could be interacted with...). It is also what the
721 // For now, fallback to just render these controls when Tiled Rendering is active to just
722 // have them displayed on all clients.
723 if (bControlIsVisibleAsChildWindow
&& comphelper::LibreOfficeKit::isActive())
725 // Do force paint when we are in Tiled Renderer and FormControl is 'visible'
726 bControlIsVisibleAsChildWindow
= false;
729 if (!bControlIsVisibleAsChildWindow
)
731 // Needs to be drawn. Link new graphics and view
732 xControlView
->setGraphics(xNewGraphics
);
735 const basegfx::B2DHomMatrix
aObjectToPixel(maCurrentTransformation
736 * rControlPrimitive
.getTransform());
737 const basegfx::B2DPoint
aTopLeftPixel(aObjectToPixel
* basegfx::B2DPoint(0.0, 0.0));
739 // Do not forget to use the evtl. offsetted origin of the target device,
740 // e.g. when used with mask/transparence buffer device
741 const Point
aOrigin(mpOutputDevice
->GetMapMode().GetOrigin());
742 xControlView
->draw(aOrigin
.X() + basegfx::fround(aTopLeftPixel
.getX()),
743 aOrigin
.Y() + basegfx::fround(aTopLeftPixel
.getY()));
745 // restore original graphics
746 xControlView
->setGraphics(xOriginalGraphics
);
750 catch (const uno::Exception
&)
752 // #i116763# removing since there is a good alternative when the xControlView
753 // is not found and it is allowed to happen
754 // DBG_UNHANDLED_EXCEPTION();
756 // process recursively and use the decomposition as Bitmap
757 process(rControlPrimitive
);
761 void VclPixelProcessor2D::processPolygonStrokePrimitive2D(
762 const primitive2d::PolygonStrokePrimitive2D
& rPolygonStrokePrimitive2D
)
764 // try to use directly
765 if (tryDrawPolygonStrokePrimitive2DDirect(rPolygonStrokePrimitive2D
, 0.0))
770 // the stroke primitive may be decomposed to filled polygons. To keep
771 // evtl. set DrawModes aka DrawModeFlags::BlackLine, DrawModeFlags::GrayLine,
772 // DrawModeFlags::GhostedLine, DrawModeFlags::WhiteLine or DrawModeFlags::SettingsLine
773 // working, these need to be copied to the corresponding fill modes
774 const DrawModeFlags
nOriginalDrawMode(mpOutputDevice
->GetDrawMode());
775 adaptLineToFillDrawMode();
777 // polygon stroke primitive
779 // Lines with 1 and 2 pixel width without AA need special treatment since their visualization
780 // as filled polygons is geometrically correct but looks wrong since polygon filling avoids
781 // the right and bottom pixels. The used method evaluates that and takes the correct action,
782 // including calling recursively with decomposition if line is wide enough
783 RenderPolygonStrokePrimitive2D(rPolygonStrokePrimitive2D
);
786 mpOutputDevice
->SetDrawMode(nOriginalDrawMode
);
789 void VclPixelProcessor2D::processFillHatchPrimitive2D(
790 const primitive2d::FillHatchPrimitive2D
& rFillHatchPrimitive
)
792 if (SvtOptionsDrawinglayer::IsAntiAliasing())
794 // if AA is used (or ignore smoothing is on), there is no need to smooth
795 // hatch painting, use decomposition
796 process(rFillHatchPrimitive
);
800 // without AA, use VCL to draw the hatch. It snaps hatch distances to the next pixel
801 // and forces hatch distance to be >= 3 pixels to make the hatch display look smoother.
802 // This is wrong in principle, but looks nicer. This could also be done here directly
803 // without VCL usage if needed
804 const attribute::FillHatchAttribute
& rFillHatchAttributes
805 = rFillHatchPrimitive
.getFillHatch();
807 // create hatch polygon in range size and discrete coordinates
808 basegfx::B2DRange
aHatchRange(rFillHatchPrimitive
.getOutputRange());
809 aHatchRange
.transform(maCurrentTransformation
);
810 const basegfx::B2DPolygon
aHatchPolygon(basegfx::utils::createPolygonFromRect(aHatchRange
));
812 if (rFillHatchAttributes
.isFillBackground())
814 // #i111846# background fill is active; draw fill polygon
815 const basegfx::BColor
aPolygonColor(
816 maBColorModifierStack
.getModifiedColor(rFillHatchPrimitive
.getBColor()));
818 mpOutputDevice
->SetFillColor(Color(aPolygonColor
));
819 mpOutputDevice
->SetLineColor();
820 mpOutputDevice
->DrawPolygon(aHatchPolygon
);
823 // set hatch line color
824 const basegfx::BColor
aHatchColor(
825 maBColorModifierStack
.getModifiedColor(rFillHatchPrimitive
.getBColor()));
826 mpOutputDevice
->SetFillColor();
827 mpOutputDevice
->SetLineColor(Color(aHatchColor
));
830 HatchStyle
eHatchStyle(HatchStyle::Single
);
832 switch (rFillHatchAttributes
.getStyle())
834 default: // HatchStyle::Single
838 case attribute::HatchStyle::Double
:
840 eHatchStyle
= HatchStyle::Double
;
843 case attribute::HatchStyle::Triple
:
845 eHatchStyle
= HatchStyle::Triple
;
851 const basegfx::B2DVector
aDiscreteDistance(
852 maCurrentTransformation
* basegfx::B2DVector(rFillHatchAttributes
.getDistance(), 0.0));
853 const sal_uInt32
nDistance(basegfx::fround(aDiscreteDistance
.getLength()));
854 const sal_uInt32
nAngle10(
855 basegfx::rad2deg
<10>(basegfx::fround(rFillHatchAttributes
.getAngle())));
856 ::Hatch
aVCLHatch(eHatchStyle
, Color(rFillHatchAttributes
.getColor()), nDistance
,
859 // draw hatch using VCL
860 mpOutputDevice
->DrawHatch(::tools::PolyPolygon(::tools::Polygon(aHatchPolygon
)), aVCLHatch
);
864 void VclPixelProcessor2D::processBackgroundColorPrimitive2D(
865 const primitive2d::BackgroundColorPrimitive2D
& rPrimitive
)
867 // #i98404# Handle directly, especially when AA is active
868 const AntialiasingFlags
nOriginalAA(mpOutputDevice
->GetAntialiasing());
870 // switch AA off in all cases
871 mpOutputDevice
->SetAntialiasing(mpOutputDevice
->GetAntialiasing() & ~AntialiasingFlags::Enable
);
873 // create color for fill
874 const basegfx::BColor
aPolygonColor(
875 maBColorModifierStack
.getModifiedColor(rPrimitive
.getBColor()));
876 Color
aFillColor(aPolygonColor
);
877 aFillColor
.SetAlpha(255 - sal_uInt8((rPrimitive
.getTransparency() * 255.0) + 0.5));
878 mpOutputDevice
->SetFillColor(aFillColor
);
879 mpOutputDevice
->SetLineColor();
881 // create rectangle for fill
882 const basegfx::B2DRange
& aViewport(getViewInformation2D().getDiscreteViewport());
883 const ::tools::Rectangle
aRectangle(static_cast<sal_Int32
>(floor(aViewport
.getMinX())),
884 static_cast<sal_Int32
>(floor(aViewport
.getMinY())),
885 static_cast<sal_Int32
>(ceil(aViewport
.getMaxX())),
886 static_cast<sal_Int32
>(ceil(aViewport
.getMaxY())));
887 mpOutputDevice
->DrawRect(aRectangle
);
889 // restore AA setting
890 mpOutputDevice
->SetAntialiasing(nOriginalAA
);
893 void VclPixelProcessor2D::processBorderLinePrimitive2D(
894 const drawinglayer::primitive2d::BorderLinePrimitive2D
& rBorder
)
896 // Process recursively, but switch off AntiAliasing for
897 // horizontal/vertical lines (*not* diagonal lines).
898 // Checked using AntialiasingFlags::PixelSnapHairline instead,
899 // but with AntiAliasing on the display really is too 'ghosty' when
900 // using fine stroking. Correct, but 'ghosty'.
902 // It has shown that there are quite some problems here:
903 // - vcl OutDev renderer methods still use fallbacks to paint
904 // multiple single lines between discrete sizes of < 3.5 what
905 // looks bad and does not match
906 // - mix of filled Polygons and Lines is bad when AA switched off
907 // - Alignment of AA with non-AA may be bad in diverse different
910 // Due to these reasons I change the strategy: Always draw AAed, but
911 // allow fallback to test/check and if needed. The normal case
912 // where BorderLines will be system-dependently snapped to have at
913 // least a single discrete width per partial line (there may be up to
914 // three) works well nowadays due to most renderers moving the AA stuff
915 // by 0.5 pixels (discrete units) to match well with the non-AAed parts.
917 // Env-Switch for steering this, default is off.
918 // Enable by setting at all (and to something)
919 static const char* pSwitchOffAntiAliasingForHorVerBorderlines(
920 getenv("SAL_SWITCH_OFF_ANTIALIASING_FOR_HOR_VER_BORTDERLINES"));
921 static bool bSwitchOffAntiAliasingForHorVerBorderlines(
922 nullptr != pSwitchOffAntiAliasingForHorVerBorderlines
);
924 if (bSwitchOffAntiAliasingForHorVerBorderlines
925 && rBorder
.isHorizontalOrVertical(getViewInformation2D()))
927 AntialiasingFlags nAntiAliasing
= mpOutputDevice
->GetAntialiasing();
928 mpOutputDevice
->SetAntialiasing(nAntiAliasing
& ~AntialiasingFlags::Enable
);
930 mpOutputDevice
->SetAntialiasing(nAntiAliasing
);
938 void VclPixelProcessor2D::processInvertPrimitive2D(const primitive2d::BasePrimitive2D
& rCandidate
)
940 // invert primitive (currently only used for HighContrast fallback for selection in SW and SC).
941 // (Not true, also used at least for the drawing of dragged column and row boundaries in SC.)
942 // Set OutDev to XOR and switch AA off (XOR does not work with AA)
943 mpOutputDevice
->Push();
944 mpOutputDevice
->SetRasterOp(RasterOp::Xor
);
945 const AntialiasingFlags
nAntiAliasing(mpOutputDevice
->GetAntialiasing());
946 mpOutputDevice
->SetAntialiasing(nAntiAliasing
& ~AntialiasingFlags::Enable
);
948 // process content recursively
952 mpOutputDevice
->Pop();
953 mpOutputDevice
->SetAntialiasing(nAntiAliasing
);
956 void VclPixelProcessor2D::processMetaFilePrimitive2D(const primitive2d::BasePrimitive2D
& rCandidate
)
959 const bool bForceLineSnap(SvtOptionsDrawinglayer::IsAntiAliasing()
960 && SvtOptionsDrawinglayer::IsSnapHorVerLinesToDiscrete());
961 const AntialiasingFlags
nOldAntiAliase(mpOutputDevice
->GetAntialiasing());
965 mpOutputDevice
->SetAntialiasing(nOldAntiAliase
| AntialiasingFlags::PixelSnapHairline
);
972 mpOutputDevice
->SetAntialiasing(nOldAntiAliase
);
978 /* Returns 8-bit alpha mask created from passed mask.
980 Negative fErodeDilateRadius values mean erode, positive - dilate.
981 nTransparency defines minimal transparency level.
983 AlphaMask
ProcessAndBlurAlphaMask(const Bitmap
& rMask
, double fErodeDilateRadius
,
984 double fBlurRadius
, sal_uInt8 nTransparency
,
985 bool bConvertTo1Bit
= true)
987 // Only completely white pixels on the initial mask must be considered for transparency. Any
988 // other color must be treated as black. This creates 1-bit B&W bitmap.
989 BitmapEx
mask(bConvertTo1Bit
? rMask
.CreateMask(COL_WHITE
) : rMask
);
991 // Scaling down increases performance without noticeable quality loss. Additionally,
992 // current blur implementation can only handle blur radius between 2 and 254.
993 Size aSize
= mask
.GetSizePixel();
995 while (fBlurRadius
> 254 || aSize
.Height() > 1000 || aSize
.Width() > 1000)
999 fErodeDilateRadius
/= 2;
1000 aSize
.setHeight(aSize
.Height() / 2);
1001 aSize
.setWidth(aSize
.Width() / 2);
1004 // BmpScaleFlag::Fast is important for following color replacement
1005 mask
.Scale(fScale
, fScale
, BmpScaleFlag::Fast
);
1007 if (fErodeDilateRadius
> 0)
1008 BitmapFilter::Filter(mask
, BitmapDilateFilter(fErodeDilateRadius
));
1009 else if (fErodeDilateRadius
< 0)
1010 BitmapFilter::Filter(mask
, BitmapErodeFilter(-fErodeDilateRadius
, 0xFF));
1014 const Color
aTransparency(nTransparency
, nTransparency
, nTransparency
);
1015 mask
.Replace(COL_BLACK
, aTransparency
);
1018 // We need 8-bit grey mask for blurring
1019 mask
.Convert(BmpConversion::N8BitGreys
);
1021 // calculate blurry effect
1022 BitmapFilter::Filter(mask
, BitmapFilterStackBlur(fBlurRadius
));
1024 mask
.Scale(rMask
.GetSizePixel());
1026 return AlphaMask(mask
.GetBitmap());
1030 void VclPixelProcessor2D::processGlowPrimitive2D(const primitive2d::GlowPrimitive2D
& rCandidate
)
1032 basegfx::B2DRange
aRange(rCandidate
.getB2DRange(getViewInformation2D()));
1033 aRange
.transform(maCurrentTransformation
);
1034 basegfx::B2DVector
aGlowRadiusVector(rCandidate
.getGlowRadius(), 0);
1035 // Calculate the pixel size of glow radius in current transformation
1036 aGlowRadiusVector
*= maCurrentTransformation
;
1037 // Glow radius is the size of the halo from each side of the object. The halo is the
1038 // border of glow color that fades from glow transparency level to fully transparent
1039 // When blurring a sharp boundary (our case), it gets 50% of original intensity, and
1040 // fades to both sides by the blur radius; thus blur radius is half of glow radius.
1041 const double fBlurRadius
= aGlowRadiusVector
.getLength() / 2;
1042 // Consider glow transparency (initial transparency near the object edge)
1043 const sal_uInt8 nAlpha
= rCandidate
.getGlowColor().GetAlpha();
1045 impBufferDevice
aBufferDevice(*mpOutputDevice
, aRange
);
1046 if (aBufferDevice
.isVisible())
1048 // remember last OutDev and set to content
1049 OutputDevice
* pLastOutputDevice
= mpOutputDevice
;
1050 mpOutputDevice
= &aBufferDevice
.getContent();
1051 // We don't need antialiased mask here, which would only make effect thicker
1052 const auto aPrevAA
= mpOutputDevice
->GetAntialiasing();
1053 mpOutputDevice
->SetAntialiasing(AntialiasingFlags::NONE
);
1054 process(rCandidate
);
1056 // Limit the bitmap size to the visible area.
1057 basegfx::B2DRange
viewRange(getViewInformation2D().getDiscreteViewport());
1058 basegfx::B2DRange
bitmapRange(aRange
);
1059 bitmapRange
.intersect(viewRange
);
1060 if (!bitmapRange
.isEmpty())
1062 const tools::Rectangle
aRect(
1063 static_cast<tools::Long
>(std::floor(bitmapRange
.getMinX())),
1064 static_cast<tools::Long
>(std::floor(bitmapRange
.getMinY())),
1065 static_cast<tools::Long
>(std::ceil(bitmapRange
.getMaxX())),
1066 static_cast<tools::Long
>(std::ceil(bitmapRange
.getMaxY())));
1067 BitmapEx bmpEx
= mpOutputDevice
->GetBitmapEx(aRect
.TopLeft(), aRect
.GetSize());
1068 mpOutputDevice
->SetAntialiasing(aPrevAA
);
1071 = ProcessAndBlurAlphaMask(bmpEx
.GetAlpha(), fBlurRadius
, fBlurRadius
, 255 - nAlpha
);
1073 // The end result is the bitmap filled with glow color and blurred 8-bit alpha mask
1074 const basegfx::BColor
aGlowColor(
1075 maBColorModifierStack
.getModifiedColor(rCandidate
.getGlowColor().getBColor()));
1076 Bitmap bmp
= bmpEx
.GetBitmap();
1077 bmp
.Erase(Color(aGlowColor
));
1078 BitmapEx
result(bmp
, mask
);
1080 // back to old OutDev
1081 mpOutputDevice
= pLastOutputDevice
;
1082 mpOutputDevice
->DrawBitmapEx(aRect
.TopLeft(), result
);
1086 mpOutputDevice
= pLastOutputDevice
;
1090 SAL_WARN("drawinglayer", "Temporary buffered virtual device is not visible");
1093 void VclPixelProcessor2D::processSoftEdgePrimitive2D(
1094 const primitive2d::SoftEdgePrimitive2D
& rCandidate
)
1096 // TODO: don't limit the object at view range. This is needed to not blur objects at window
1097 // borders, where they don't end. Ideally, process the full object once at maximal reasonable
1098 // resolution, and store the resulting alpha mask in primitive's cache; then reuse it later,
1099 // applying the transform.
1100 basegfx::B2DRange
aRange(rCandidate
.getB2DRange(getViewInformation2D()));
1101 aRange
.transform(maCurrentTransformation
);
1102 basegfx::B2DVector
aRadiusVector(rCandidate
.getRadius(), 0);
1103 // Calculate the pixel size of soft edge radius in current transformation
1104 aRadiusVector
*= maCurrentTransformation
;
1105 // Blur radius is equal to soft edge radius
1106 const double fBlurRadius
= aRadiusVector
.getLength();
1108 impBufferDevice
aBufferDevice(*mpOutputDevice
, aRange
);
1109 if (aBufferDevice
.isVisible())
1111 // remember last OutDev and set to content
1112 OutputDevice
* pLastOutputDevice
= mpOutputDevice
;
1113 mpOutputDevice
= &aBufferDevice
.getContent();
1114 // Since the effect converts all children to bitmap, we can't disable antialiasing here,
1115 // because it would result in poor quality in areas not affected by the effect
1116 process(rCandidate
);
1118 // Limit the bitmap size to the visible area.
1119 basegfx::B2DRange
viewRange(getViewInformation2D().getDiscreteViewport());
1120 basegfx::B2DRange
bitmapRange(aRange
);
1121 bitmapRange
.intersect(viewRange
);
1122 if (!bitmapRange
.isEmpty())
1124 const tools::Rectangle
aRect(
1125 static_cast<tools::Long
>(std::floor(bitmapRange
.getMinX())),
1126 static_cast<tools::Long
>(std::floor(bitmapRange
.getMinY())),
1127 static_cast<tools::Long
>(std::ceil(bitmapRange
.getMaxX())),
1128 static_cast<tools::Long
>(std::ceil(bitmapRange
.getMaxY())));
1129 BitmapEx bitmap
= mpOutputDevice
->GetBitmapEx(aRect
.TopLeft(), aRect
.GetSize());
1131 AlphaMask aMask
= bitmap
.GetAlpha();
1132 AlphaMask blurMask
= ProcessAndBlurAlphaMask(aMask
, -fBlurRadius
, fBlurRadius
, 0);
1134 aMask
.BlendWith(blurMask
);
1136 // The end result is the original bitmap with blurred 8-bit alpha mask
1137 BitmapEx
result(bitmap
.GetBitmap(), aMask
);
1139 // back to old OutDev
1140 mpOutputDevice
= pLastOutputDevice
;
1141 mpOutputDevice
->DrawBitmapEx(aRect
.TopLeft(), result
);
1145 mpOutputDevice
= pLastOutputDevice
;
1149 SAL_WARN("drawinglayer", "Temporary buffered virtual device is not visible");
1152 void VclPixelProcessor2D::processShadowPrimitive2D(const primitive2d::ShadowPrimitive2D
& rCandidate
)
1154 if (rCandidate
.getShadowBlur() == 0)
1156 process(rCandidate
);
1160 basegfx::B2DRange
aRange(rCandidate
.getB2DRange(getViewInformation2D()));
1161 aRange
.transform(maCurrentTransformation
);
1162 basegfx::B2DVector
aBlurRadiusVector(rCandidate
.getShadowBlur(), 0);
1163 aBlurRadiusVector
*= maCurrentTransformation
;
1164 const double fBlurRadius
= aBlurRadiusVector
.getLength();
1166 impBufferDevice
aBufferDevice(*mpOutputDevice
, aRange
);
1167 if (aBufferDevice
.isVisible() && !aRange
.isEmpty())
1169 OutputDevice
* pLastOutputDevice
= mpOutputDevice
;
1170 mpOutputDevice
= &aBufferDevice
.getContent();
1172 process(rCandidate
);
1174 const tools::Rectangle
aRect(static_cast<tools::Long
>(std::floor(aRange
.getMinX())),
1175 static_cast<tools::Long
>(std::floor(aRange
.getMinY())),
1176 static_cast<tools::Long
>(std::ceil(aRange
.getMaxX())),
1177 static_cast<tools::Long
>(std::ceil(aRange
.getMaxY())));
1179 BitmapEx bitmapEx
= mpOutputDevice
->GetBitmapEx(aRect
.TopLeft(), aRect
.GetSize());
1181 AlphaMask mask
= ProcessAndBlurAlphaMask(bitmapEx
.GetAlpha(), 0, fBlurRadius
, 0, false);
1183 const basegfx::BColor
aShadowColor(
1184 maBColorModifierStack
.getModifiedColor(rCandidate
.getShadowColor()));
1186 Bitmap bitmap
= bitmapEx
.GetBitmap();
1187 bitmap
.Erase(Color(aShadowColor
));
1188 BitmapEx
result(bitmap
, mask
);
1190 mpOutputDevice
= pLastOutputDevice
;
1191 mpOutputDevice
->DrawBitmapEx(aRect
.TopLeft(), result
);
1194 SAL_WARN("drawinglayer", "Temporary buffered virtual device is not visible");
1197 void VclPixelProcessor2D::processFillGradientPrimitive2D(
1198 const primitive2d::FillGradientPrimitive2D
& rPrimitive
)
1200 const attribute::FillGradientAttribute
& rFillGradient
= rPrimitive
.getFillGradient();
1202 // VCL should be able to handle all styles, but for tdf#133477 the VCL result
1203 // is different from processing the gradient manually by drawinglayer
1204 // (and the Writer unittest for it fails). Keep using the drawinglayer code
1205 // until somebody founds out what's wrong and fixes it.
1206 if (rFillGradient
.getStyle() != drawinglayer::attribute::GradientStyle::Linear
1207 && rFillGradient
.getStyle() != drawinglayer::attribute::GradientStyle::Axial
1208 && rFillGradient
.getStyle() != drawinglayer::attribute::GradientStyle::Radial
)
1210 process(rPrimitive
);
1214 // tdf#149754 VCL gradient draw is not capable to handle all primitive gradient definitions,
1215 // what should be clear due to being developed to extend/replace them in
1216 // capabilities & precision.
1217 // It is e.g. not capable to correctly paint if the OutputRange is not completely
1218 // inside the DefinitionRange, thus forcing to paint gradient parts *outside* the
1220 // This happens for Writer with Frames anchored in Frames (and was broken due to
1221 // falling back to VCL Gradient paint here), and for the new SlideBackgroundFill
1222 // Fill mode for objects using it that reach outside the page (which is the
1223 // DefinitionRange in that case).
1224 // I see no real reason to fallback here to OutputDevice::DrawGradient and VCL
1225 // gradient paint at all (system-dependent renderers wouldn't in the future), but
1226 // will for convenience only add that needed additional correcting case
1227 if (!rPrimitive
.getDefinitionRange().isInside(rPrimitive
.getOutputRange()))
1229 process(rPrimitive
);
1233 GradientStyle eGradientStyle
= convertGradientStyle(rFillGradient
.getStyle());
1235 Gradient
aGradient(eGradientStyle
, Color(rFillGradient
.getStartColor()),
1236 Color(rFillGradient
.getEndColor()));
1238 aGradient
.SetAngle(Degree10(static_cast<int>(basegfx::rad2deg
<10>(rFillGradient
.getAngle()))));
1239 aGradient
.SetBorder(rFillGradient
.getBorder() * 100);
1240 aGradient
.SetOfsX(rFillGradient
.getOffsetX() * 100.0);
1241 aGradient
.SetOfsY(rFillGradient
.getOffsetY() * 100.0);
1242 aGradient
.SetSteps(rFillGradient
.getSteps());
1244 basegfx::B2DRange
aOutputRange(rPrimitive
.getOutputRange());
1245 aOutputRange
.transform(maCurrentTransformation
);
1246 basegfx::B2DRange
aFullRange(rPrimitive
.getDefinitionRange());
1247 aFullRange
.transform(maCurrentTransformation
);
1249 const tools::Rectangle
aOutputRectangle(
1250 std::floor(aOutputRange
.getMinX()), std::floor(aOutputRange
.getMinY()),
1251 std::ceil(aOutputRange
.getMaxX()), std::ceil(aOutputRange
.getMaxY()));
1252 const tools::Rectangle
aFullRectangle(
1253 std::floor(aFullRange
.getMinX()), std::floor(aFullRange
.getMinY()),
1254 std::ceil(aFullRange
.getMaxX()), std::ceil(aFullRange
.getMaxY()));
1256 mpOutputDevice
->Push(vcl::PushFlags::CLIPREGION
);
1257 mpOutputDevice
->IntersectClipRegion(aOutputRectangle
);
1258 mpOutputDevice
->DrawGradient(aFullRectangle
, aGradient
);
1259 mpOutputDevice
->Pop();
1262 void VclPixelProcessor2D::processPatternFillPrimitive2D(
1263 const primitive2d::PatternFillPrimitive2D
& rPrimitive
)
1265 const basegfx::B2DRange
& rReferenceRange
= rPrimitive
.getReferenceRange();
1266 if (rReferenceRange
.isEmpty() || rReferenceRange
.getWidth() <= 0.0
1267 || rReferenceRange
.getHeight() <= 0.0)
1270 basegfx::B2DPolyPolygon aMask
= rPrimitive
.getMask();
1271 aMask
.transform(maCurrentTransformation
);
1272 const basegfx::B2DRange
aMaskRange(aMask
.getB2DRange());
1274 if (aMaskRange
.isEmpty() || aMaskRange
.getWidth() <= 0.0 || aMaskRange
.getHeight() <= 0.0)
1277 sal_uInt32 nTileWidth
, nTileHeight
;
1278 rPrimitive
.getTileSize(nTileWidth
, nTileHeight
, getViewInformation2D());
1279 if (nTileWidth
== 0 || nTileHeight
== 0)
1281 BitmapEx aTileImage
= rPrimitive
.createTileImage(nTileWidth
, nTileHeight
);
1282 tools::Rectangle aMaskRect
= vcl::unotools::rectangleFromB2DRectangle(aMaskRange
);
1284 // Unless smooth edges are needed, simply use clipping.
1285 if (basegfx::utils::isRectangle(aMask
) || !SvtOptionsDrawinglayer::IsAntiAliasing())
1287 mpOutputDevice
->Push(vcl::PushFlags::CLIPREGION
);
1288 mpOutputDevice
->IntersectClipRegion(vcl::Region(aMask
));
1289 Wallpaper
aWallpaper(aTileImage
);
1290 aWallpaper
.SetColor(COL_TRANSPARENT
);
1291 mpOutputDevice
->DrawWallpaper(aMaskRect
, aWallpaper
);
1292 mpOutputDevice
->Pop();
1296 impBufferDevice
aBufferDevice(*mpOutputDevice
, aMaskRange
);
1298 if (!aBufferDevice
.isVisible())
1301 // remember last OutDev and set to content
1302 OutputDevice
* pLastOutputDevice
= mpOutputDevice
;
1303 mpOutputDevice
= &aBufferDevice
.getContent();
1305 // if the tile is a single pixel big, just flood fill with that pixel color
1306 if (nTileWidth
== 1 && nTileHeight
== 1)
1308 Color col
= aTileImage
.GetPixelColor(0, 0);
1309 mpOutputDevice
->SetLineColor(col
);
1310 mpOutputDevice
->SetFillColor(col
);
1311 mpOutputDevice
->DrawRect(aMaskRect
);
1315 Wallpaper
aWallpaper(aTileImage
);
1316 aWallpaper
.SetColor(COL_TRANSPARENT
);
1317 mpOutputDevice
->DrawWallpaper(aMaskRect
, aWallpaper
);
1320 // back to old OutDev
1321 mpOutputDevice
= pLastOutputDevice
;
1324 VirtualDevice
& rMask
= aBufferDevice
.getTransparence();
1325 rMask
.SetLineColor();
1326 rMask
.SetFillColor(COL_BLACK
);
1327 rMask
.DrawPolyPolygon(aMask
);
1329 // dump buffer to outdev
1330 aBufferDevice
.paint();
1333 } // end of namespace
1335 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */