tdf#149890 SVG: pattern is displayed with black fill
[LibreOffice.git] / drawinglayer / source / processor2d / vclpixelprocessor2d.cxx
blobdf2642c9fdbe889be8bc66903c953359657b9777
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "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);
103 else
105 mpOutputDevice->SetAntialiasing(m_pImpl->m_nOrigAntiAliasing & ~AntialiasingFlags::Enable);
109 VclPixelProcessor2D::~VclPixelProcessor2D()
111 // restore MapMode
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)
123 // no geometry, done
124 return;
127 const basegfx::BColor aPolygonColor(
128 maBColorModifierStack.getModifiedColor(rSource.getBColor()));
130 mpOutputDevice->SetFillColor(Color(aPolygonColor));
131 mpOutputDevice->SetLineColor();
132 mpOutputDevice->DrawTransparent(maCurrentTransformation, rSource.getB2DPolyPolygon(),
133 fTransparency);
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)
143 // no geometry, done
144 return true;
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,
155 fTransparency);
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)
165 // no geometry, done
166 return true;
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
174 return false;
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());
200 namespace
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;
218 default:
219 assert(false);
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));
234 break;
236 case PRIMITIVE2D_ID_TEXTSIMPLEPORTIONPRIMITIVE2D:
238 processTextSimplePortionPrimitive2D(
239 static_cast<const primitive2d::TextSimplePortionPrimitive2D&>(rCandidate));
240 break;
242 case PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D:
244 processTextDecoratedPortionPrimitive2D(
245 static_cast<const primitive2d::TextSimplePortionPrimitive2D&>(rCandidate));
246 break;
248 case PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D:
250 processPolygonHairlinePrimitive2D(
251 static_cast<const primitive2d::PolygonHairlinePrimitive2D&>(rCandidate));
252 break;
254 case PRIMITIVE2D_ID_BITMAPPRIMITIVE2D:
256 // direct draw of transformed BitmapEx primitive
257 processBitmapPrimitive2D(
258 static_cast<const primitive2d::BitmapPrimitive2D&>(rCandidate));
259 break;
261 case PRIMITIVE2D_ID_FILLGRAPHICPRIMITIVE2D:
263 // direct draw of fillBitmapPrimitive
264 RenderFillGraphicPrimitive2D(
265 static_cast<const primitive2d::FillGraphicPrimitive2D&>(rCandidate));
266 break;
268 case PRIMITIVE2D_ID_POLYPOLYGONGRADIENTPRIMITIVE2D:
270 processPolyPolygonGradientPrimitive2D(
271 static_cast<const primitive2d::PolyPolygonGradientPrimitive2D&>(rCandidate));
272 break;
274 case PRIMITIVE2D_ID_POLYPOLYGONGRAPHICPRIMITIVE2D:
276 // direct draw of bitmap
277 RenderPolyPolygonGraphicPrimitive2D(
278 static_cast<const primitive2d::PolyPolygonGraphicPrimitive2D&>(rCandidate));
279 break;
281 case PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D:
283 processPolyPolygonColorPrimitive2D(
284 static_cast<const primitive2d::PolyPolygonColorPrimitive2D&>(rCandidate));
285 break;
287 case PRIMITIVE2D_ID_METAFILEPRIMITIVE2D:
289 processMetaFilePrimitive2D(rCandidate);
290 break;
292 case PRIMITIVE2D_ID_MASKPRIMITIVE2D:
294 // mask group.
295 RenderMaskPrimitive2DPixel(
296 static_cast<const primitive2d::MaskPrimitive2D&>(rCandidate));
297 break;
299 case PRIMITIVE2D_ID_MODIFIEDCOLORPRIMITIVE2D:
301 // modified color group. Force output to unified color.
302 RenderModifiedColorPrimitive2D(
303 static_cast<const primitive2d::ModifiedColorPrimitive2D&>(rCandidate));
304 break;
306 case PRIMITIVE2D_ID_UNIFIEDTRANSPARENCEPRIMITIVE2D:
308 processUnifiedTransparencePrimitive2D(
309 static_cast<const primitive2d::UnifiedTransparencePrimitive2D&>(rCandidate));
310 break;
312 case PRIMITIVE2D_ID_TRANSPARENCEPRIMITIVE2D:
314 // sub-transparence group. Draw to VDev first.
315 RenderTransparencePrimitive2D(
316 static_cast<const primitive2d::TransparencePrimitive2D&>(rCandidate));
317 break;
319 case PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D:
321 // transform group.
322 RenderTransformPrimitive2D(
323 static_cast<const primitive2d::TransformPrimitive2D&>(rCandidate));
324 break;
326 case PRIMITIVE2D_ID_PAGEPREVIEWPRIMITIVE2D:
328 // new XDrawPage for ViewInformation2D
329 RenderPagePreviewPrimitive2D(
330 static_cast<const primitive2d::PagePreviewPrimitive2D&>(rCandidate));
331 break;
333 case PRIMITIVE2D_ID_MARKERARRAYPRIMITIVE2D:
335 // marker array
336 RenderMarkerArrayPrimitive2D(
337 static_cast<const primitive2d::MarkerArrayPrimitive2D&>(rCandidate));
338 break;
340 case PRIMITIVE2D_ID_POINTARRAYPRIMITIVE2D:
342 // point array
343 RenderPointArrayPrimitive2D(
344 static_cast<const primitive2d::PointArrayPrimitive2D&>(rCandidate));
345 break;
347 case PRIMITIVE2D_ID_CONTROLPRIMITIVE2D:
349 processControlPrimitive2D(
350 static_cast<const primitive2d::ControlPrimitive2D&>(rCandidate));
351 break;
353 case PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D:
355 processPolygonStrokePrimitive2D(
356 static_cast<const primitive2d::PolygonStrokePrimitive2D&>(rCandidate));
357 break;
359 case PRIMITIVE2D_ID_FILLHATCHPRIMITIVE2D:
361 processFillHatchPrimitive2D(
362 static_cast<const primitive2d::FillHatchPrimitive2D&>(rCandidate));
363 break;
365 case PRIMITIVE2D_ID_BACKGROUNDCOLORPRIMITIVE2D:
367 processBackgroundColorPrimitive2D(
368 static_cast<const primitive2d::BackgroundColorPrimitive2D&>(rCandidate));
369 break;
371 case PRIMITIVE2D_ID_TEXTHIERARCHYEDITPRIMITIVE2D:
373 // #i97628#
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.
380 break;
382 case PRIMITIVE2D_ID_INVERTPRIMITIVE2D:
384 processInvertPrimitive2D(rCandidate);
385 break;
387 case PRIMITIVE2D_ID_EPSPRIMITIVE2D:
389 RenderEpsPrimitive2D(static_cast<const primitive2d::EpsPrimitive2D&>(rCandidate));
390 break;
392 case PRIMITIVE2D_ID_SVGLINEARATOMPRIMITIVE2D:
394 RenderSvgLinearAtomPrimitive2D(
395 static_cast<const primitive2d::SvgLinearAtomPrimitive2D&>(rCandidate));
396 break;
398 case PRIMITIVE2D_ID_SVGRADIALATOMPRIMITIVE2D:
400 RenderSvgRadialAtomPrimitive2D(
401 static_cast<const primitive2d::SvgRadialAtomPrimitive2D&>(rCandidate));
402 break;
404 case PRIMITIVE2D_ID_BORDERLINEPRIMITIVE2D:
406 processBorderLinePrimitive2D(
407 static_cast<const drawinglayer::primitive2d::BorderLinePrimitive2D&>(rCandidate));
408 break;
410 case PRIMITIVE2D_ID_GLOWPRIMITIVE2D:
412 processGlowPrimitive2D(
413 static_cast<const drawinglayer::primitive2d::GlowPrimitive2D&>(rCandidate));
414 break;
416 case PRIMITIVE2D_ID_SOFTEDGEPRIMITIVE2D:
418 processSoftEdgePrimitive2D(
419 static_cast<const drawinglayer::primitive2d::SoftEdgePrimitive2D&>(rCandidate));
420 break;
422 case PRIMITIVE2D_ID_SHADOWPRIMITIVE2D:
424 processShadowPrimitive2D(
425 static_cast<const drawinglayer::primitive2d::ShadowPrimitive2D&>(rCandidate));
426 break;
428 case PRIMITIVE2D_ID_FILLGRADIENTPRIMITIVE2D:
430 processFillGradientPrimitive2D(
431 static_cast<const drawinglayer::primitive2d::FillGradientPrimitive2D&>(rCandidate));
432 break;
434 case PRIMITIVE2D_ID_PATTERNFILLPRIMITIVE2D:
436 processPatternFillPrimitive2D(
437 static_cast<const drawinglayer::primitive2d::PatternFillPrimitive2D&>(rCandidate));
438 break;
440 default:
442 SAL_INFO("drawinglayer", "default case for " << drawinglayer::primitive2d::idToString(
443 rCandidate.getPrimitive2DID()));
444 // process recursively
445 process(rCandidate);
446 break;
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);
473 else
475 process(rCandidate);
478 // restore DrawMode
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);
493 else
495 process(rCandidate);
498 // restore DrawMode
499 mpOutputDevice->SetDrawMode(nOriginalDrawMode);
502 void VclPixelProcessor2D::processPolygonHairlinePrimitive2D(
503 const primitive2d::PolygonHairlinePrimitive2D& rPolygonHairlinePrimitive2D)
505 if (tryDrawPolygonHairlinePrimitive2DDirect(rPolygonHairlinePrimitive2D, 0.0))
507 return;
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
531 return;
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())
548 return;
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);
558 else
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)))
579 return;
581 const basegfx::BColor aPolygonColor(
582 maBColorModifierStack.getModifiedColor(rPolyPolygonColorPrimitive2D.getBColor()));
583 sal_uInt32 nCount(aLocalPolyPolygon.count());
585 if (!nCount)
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())
609 return;
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*>(
632 pBasePrimitive);
633 SAL_WARN_IF(!pPoPoColor, "drawinglayer",
634 "OOps, PrimitiveID and PrimitiveType do not match (!)");
635 bDrawTransparentUsed = true;
636 tryDrawPolyPolygonColorPrimitive2DDirect(
637 *pPoPoColor, rUniTransparenceCandidate.getTransparence());
638 break;
640 case PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D:
642 // single transparent PolygonHairlinePrimitive2D identified, use directly
643 const primitive2d::PolygonHairlinePrimitive2D* pPoHair
644 = static_cast<const primitive2d::PolygonHairlinePrimitive2D*>(
645 pBasePrimitive);
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;
655 break;
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;
671 break;
673 default:
674 SAL_INFO("drawinglayer",
675 "default case for " << drawinglayer::primitive2d::idToString(
676 rUniTransparenceCandidate.getPrimitive2DID()));
677 break;
681 if (!bDrawTransparentUsed)
683 // unified sub-transparence. Draw to VDev first.
684 RenderUnifiedTransparencePrimitive2D(rUniTransparenceCandidate);
689 void VclPixelProcessor2D::processControlPrimitive2D(
690 const primitive2d::ControlPrimitive2D& rControlPrimitive)
692 // control primitive
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
715 // flickering, too).
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
720 // office does.
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);
734 // get position
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))
767 return;
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);
785 // restore DrawMode
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);
798 else
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));
829 // get hatch style
830 HatchStyle eHatchStyle(HatchStyle::Single);
832 switch (rFillHatchAttributes.getStyle())
834 default: // HatchStyle::Single
836 break;
838 case attribute::HatchStyle::Double:
840 eHatchStyle = HatchStyle::Double;
841 break;
843 case attribute::HatchStyle::Triple:
845 eHatchStyle = HatchStyle::Triple;
846 break;
850 // create hatch
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,
857 Degree10(nAngle10));
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
908 // renderers
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);
929 process(rBorder);
930 mpOutputDevice->SetAntialiasing(nAntiAliasing);
932 else
934 process(rBorder);
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
949 process(rCandidate);
951 // restore OutDev
952 mpOutputDevice->Pop();
953 mpOutputDevice->SetAntialiasing(nAntiAliasing);
956 void VclPixelProcessor2D::processMetaFilePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate)
958 // #i98289#
959 const bool bForceLineSnap(SvtOptionsDrawinglayer::IsAntiAliasing()
960 && SvtOptionsDrawinglayer::IsSnapHorVerLinesToDiscrete());
961 const AntialiasingFlags nOldAntiAliase(mpOutputDevice->GetAntialiasing());
963 if (bForceLineSnap)
965 mpOutputDevice->SetAntialiasing(nOldAntiAliase | AntialiasingFlags::PixelSnapHairline);
968 process(rCandidate);
970 if (bForceLineSnap)
972 mpOutputDevice->SetAntialiasing(nOldAntiAliase);
976 namespace
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();
994 double fScale = 1.0;
995 while (fBlurRadius > 254 || aSize.Height() > 1000 || aSize.Width() > 1000)
997 fScale /= 2;
998 fBlurRadius /= 2;
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));
1012 if (nTransparency)
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);
1070 AlphaMask mask
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);
1084 else
1086 mpOutputDevice = pLastOutputDevice;
1089 else
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);
1143 else
1145 mpOutputDevice = pLastOutputDevice;
1148 else
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);
1157 return;
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);
1193 else
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);
1211 return;
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
1219 // DefinitionRange.
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);
1230 return;
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)
1268 return;
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)
1275 return;
1277 sal_uInt32 nTileWidth, nTileHeight;
1278 rPrimitive.getTileSize(nTileWidth, nTileHeight, getViewInformation2D());
1279 if (nTileWidth == 0 || nTileHeight == 0)
1280 return;
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();
1293 return;
1296 impBufferDevice aBufferDevice(*mpOutputDevice, aMaskRange);
1298 if (!aBufferDevice.isVisible())
1299 return;
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);
1313 else
1315 Wallpaper aWallpaper(aTileImage);
1316 aWallpaper.SetColor(COL_TRANSPARENT);
1317 mpOutputDevice->DrawWallpaper(aMaskRect, aWallpaper);
1320 // back to old OutDev
1321 mpOutputDevice = pLastOutputDevice;
1323 // draw mask
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: */