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