/src/libreoffice/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx
Line | Count | Source |
1 | | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | /* |
3 | | * This file is part of the LibreOffice project. |
4 | | * |
5 | | * This Source Code Form is subject to the terms of the Mozilla Public |
6 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
7 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
8 | | * |
9 | | * This file incorporates work covered by the following license notice: |
10 | | * |
11 | | * Licensed to the Apache Software Foundation (ASF) under one or more |
12 | | * contributor license agreements. See the NOTICE file distributed |
13 | | * with this work for additional information regarding copyright |
14 | | * ownership. The ASF licenses this file to you under the Apache |
15 | | * License, Version 2.0 (the "License"); you may not use this file |
16 | | * except in compliance with the License. You may obtain a copy of |
17 | | * the License at http://www.apache.org/licenses/LICENSE-2.0 . |
18 | | */ |
19 | | |
20 | | #include "vclpixelprocessor2d.hxx" |
21 | | #include "vclhelperbufferdevice.hxx" |
22 | | #include <comphelper/lok.hxx> |
23 | | |
24 | | #include <sal/log.hxx> |
25 | | #include <vcl/outdev.hxx> |
26 | | #include <vcl/hatch.hxx> |
27 | | #include <vcl/canvastools.hxx> |
28 | | #include <vcl/vclenum.hxx> |
29 | | #include <basegfx/polygon/b2dpolygontools.hxx> |
30 | | #include <basegfx/polygon/b2dpolypolygontools.hxx> |
31 | | #include <basegfx/utils/bgradient.hxx> |
32 | | |
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/controlprimitive2d.hxx> |
49 | | #include <drawinglayer/primitive2d/borderlineprimitive2d.hxx> |
50 | | #include <drawinglayer/primitive2d/fillgradientprimitive2d.hxx> |
51 | | #include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx> |
52 | | #include <drawinglayer/primitive2d/pagepreviewprimitive2d.hxx> |
53 | | #include <drawinglayer/primitive2d/backgroundcolorprimitive2d.hxx> |
54 | | #include <drawinglayer/primitive2d/svggradientprimitive2d.hxx> |
55 | | #include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx> |
56 | | #include <drawinglayer/primitive2d/fillhatchprimitive2d.hxx> |
57 | | #include <drawinglayer/primitive2d/epsprimitive2d.hxx> |
58 | | #include <drawinglayer/primitive2d/patternfillprimitive2d.hxx> |
59 | | |
60 | | #include <com/sun/star/awt/XControl.hpp> |
61 | | |
62 | | #include <officecfg/Office/Common.hxx> |
63 | | #include <vcl/gradient.hxx> |
64 | | |
65 | | using namespace com::sun::star; |
66 | | |
67 | | namespace drawinglayer::processor2d |
68 | | { |
69 | | VclPixelProcessor2D::VclPixelProcessor2D(const geometry::ViewInformation2D& rViewInformation, |
70 | | OutputDevice& rOutDev) |
71 | 0 | : VclProcessor2D(rViewInformation, rOutDev) |
72 | 0 | , m_nOrigAntiAliasing(rOutDev.GetAntialiasing()) |
73 | | , m_bRenderSimpleTextDirect( |
74 | 0 | officecfg::Office::Common::Drawinglayer::RenderSimpleTextDirect::get()) |
75 | | , m_bRenderDecoratedTextDirect( |
76 | 0 | officecfg::Office::Common::Drawinglayer::RenderDecoratedTextDirect::get()) |
77 | 0 | { |
78 | | // prepare maCurrentTransformation matrix with viewTransformation to target directly to pixels |
79 | 0 | maCurrentTransformation = rViewInformation.getObjectToViewTransformation(); |
80 | | |
81 | | // prepare output directly to pixels |
82 | 0 | mpOutputDevice->Push(vcl::PushFlags::MAPMODE); |
83 | 0 | mpOutputDevice->SetMapMode(); |
84 | | |
85 | | // react on AntiAliasing settings |
86 | 0 | if (rViewInformation.getUseAntiAliasing()) |
87 | 0 | { |
88 | 0 | mpOutputDevice->SetAntialiasing(m_nOrigAntiAliasing | AntialiasingFlags::Enable); |
89 | 0 | } |
90 | 0 | else |
91 | 0 | { |
92 | 0 | mpOutputDevice->SetAntialiasing(m_nOrigAntiAliasing & ~AntialiasingFlags::Enable); |
93 | 0 | } |
94 | 0 | } |
95 | | |
96 | | VclPixelProcessor2D::~VclPixelProcessor2D() |
97 | 0 | { |
98 | | // restore MapMode |
99 | 0 | mpOutputDevice->Pop(); |
100 | | |
101 | | // restore AntiAliasing |
102 | 0 | mpOutputDevice->SetAntialiasing(m_nOrigAntiAliasing); |
103 | 0 | } |
104 | | |
105 | | void VclPixelProcessor2D::tryDrawPolyPolygonColorPrimitive2DDirect( |
106 | | const drawinglayer::primitive2d::PolyPolygonColorPrimitive2D& rSource, double fTransparency) |
107 | 0 | { |
108 | 0 | if (!rSource.getB2DPolyPolygon().count() || fTransparency < 0.0 || fTransparency >= 1.0) |
109 | 0 | { |
110 | | // no geometry, done |
111 | 0 | return; |
112 | 0 | } |
113 | | |
114 | 0 | const basegfx::BColor aPolygonColor( |
115 | 0 | maBColorModifierStack.getModifiedColor(rSource.getBColor())); |
116 | |
|
117 | 0 | if (comphelper::LibreOfficeKit::isActive() && aPolygonColor.isAutomatic()) |
118 | 0 | mpOutputDevice->SetFillColor(getViewInformation2D().getAutoColor()); |
119 | 0 | else |
120 | 0 | mpOutputDevice->SetFillColor(Color(aPolygonColor)); |
121 | |
|
122 | 0 | mpOutputDevice->SetLineColor(); |
123 | 0 | mpOutputDevice->DrawTransparent(maCurrentTransformation, rSource.getB2DPolyPolygon(), |
124 | 0 | fTransparency); |
125 | 0 | } |
126 | | |
127 | | bool VclPixelProcessor2D::tryDrawPolygonHairlinePrimitive2DDirect( |
128 | | const drawinglayer::primitive2d::PolygonHairlinePrimitive2D& rSource, double fTransparency) |
129 | 0 | { |
130 | 0 | const basegfx::B2DPolygon& rLocalPolygon(rSource.getB2DPolygon()); |
131 | |
|
132 | 0 | if (!rLocalPolygon.count() || fTransparency < 0.0 || fTransparency >= 1.0) |
133 | 0 | { |
134 | | // no geometry, done |
135 | 0 | return true; |
136 | 0 | } |
137 | | |
138 | 0 | const basegfx::BColor aLineColor(maBColorModifierStack.getModifiedColor(rSource.getBColor())); |
139 | |
|
140 | 0 | mpOutputDevice->SetFillColor(); |
141 | 0 | mpOutputDevice->SetLineColor(Color(aLineColor)); |
142 | | //aLocalPolygon.transform(maCurrentTransformation); |
143 | | |
144 | | // try drawing; if it did not work, use standard fallback |
145 | 0 | return mpOutputDevice->DrawPolyLineDirect(maCurrentTransformation, rLocalPolygon, 0.0, |
146 | 0 | fTransparency); |
147 | 0 | } |
148 | | |
149 | | bool VclPixelProcessor2D::tryDrawPolygonStrokePrimitive2DDirect( |
150 | | const drawinglayer::primitive2d::PolygonStrokePrimitive2D& rSource, double fTransparency) |
151 | 0 | { |
152 | 0 | const basegfx::B2DPolygon& rLocalPolygon(rSource.getB2DPolygon()); |
153 | |
|
154 | 0 | if (!rLocalPolygon.count() || fTransparency < 0.0 || fTransparency >= 1.0) |
155 | 0 | { |
156 | | // no geometry, done |
157 | 0 | return true; |
158 | 0 | } |
159 | | |
160 | 0 | if (basegfx::B2DLineJoin::NONE == rSource.getLineAttribute().getLineJoin() |
161 | 0 | && css::drawing::LineCap_BUTT != rSource.getLineAttribute().getLineCap()) |
162 | 0 | { |
163 | | // better use decompose to get that combination done for now, see discussion |
164 | | // at https://bugs.documentfoundation.org/show_bug.cgi?id=130478#c17 and ff |
165 | 0 | return false; |
166 | 0 | } |
167 | | |
168 | | // MM01: Radically change here - no dismantle/applyLineDashing, |
169 | | // let that happen low-level at DrawPolyLineDirect implementations |
170 | | // to open up for buffering and evtl. direct draw with sys-dep |
171 | | // graphic systems. Check for stroke is in use |
172 | 0 | const bool bStrokeAttributeNotUsed(rSource.getStrokeAttribute().isDefault() |
173 | 0 | || 0.0 == rSource.getStrokeAttribute().getFullDotDashLen()); |
174 | |
|
175 | 0 | const basegfx::BColor aLineColor( |
176 | 0 | maBColorModifierStack.getModifiedColor(rSource.getLineAttribute().getColor())); |
177 | |
|
178 | 0 | mpOutputDevice->SetFillColor(); |
179 | 0 | mpOutputDevice->SetLineColor(Color(aLineColor)); |
180 | | |
181 | | // MM01 draw direct, hand over dash data if available |
182 | 0 | return mpOutputDevice->DrawPolyLineDirect( |
183 | 0 | maCurrentTransformation, rLocalPolygon, |
184 | | // tdf#124848 use LineWidth direct, do not try to solve for zero-case (aka hairline) |
185 | 0 | rSource.getLineAttribute().getWidth(), fTransparency, |
186 | 0 | bStrokeAttributeNotUsed ? nullptr : &rSource.getStrokeAttribute().getDotDashArray(), |
187 | 0 | rSource.getLineAttribute().getLineJoin(), rSource.getLineAttribute().getLineCap(), |
188 | 0 | rSource.getLineAttribute().getMiterMinimumAngle()); |
189 | 0 | } |
190 | | |
191 | | void VclPixelProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate) |
192 | 0 | { |
193 | 0 | switch (rCandidate.getPrimitive2DID()) |
194 | 0 | { |
195 | 0 | case PRIMITIVE2D_ID_TEXTSIMPLEPORTIONPRIMITIVE2D: |
196 | 0 | { |
197 | 0 | processTextSimplePortionPrimitive2D( |
198 | 0 | static_cast<const primitive2d::TextSimplePortionPrimitive2D&>(rCandidate)); |
199 | 0 | break; |
200 | 0 | } |
201 | 0 | case PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D: |
202 | 0 | { |
203 | 0 | processTextDecoratedPortionPrimitive2D( |
204 | 0 | static_cast<const primitive2d::TextSimplePortionPrimitive2D&>(rCandidate)); |
205 | 0 | break; |
206 | 0 | } |
207 | 0 | case PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D: |
208 | 0 | { |
209 | 0 | processPolygonHairlinePrimitive2D( |
210 | 0 | static_cast<const primitive2d::PolygonHairlinePrimitive2D&>(rCandidate)); |
211 | 0 | break; |
212 | 0 | } |
213 | 0 | case PRIMITIVE2D_ID_BITMAPPRIMITIVE2D: |
214 | 0 | { |
215 | | // direct draw of transformed Bitmap primitive |
216 | 0 | processBitmapPrimitive2D( |
217 | 0 | static_cast<const primitive2d::BitmapPrimitive2D&>(rCandidate)); |
218 | 0 | break; |
219 | 0 | } |
220 | 0 | case PRIMITIVE2D_ID_FILLGRAPHICPRIMITIVE2D: |
221 | 0 | { |
222 | | // direct draw of fillBitmapPrimitive |
223 | 0 | RenderFillGraphicPrimitive2D( |
224 | 0 | static_cast<const primitive2d::FillGraphicPrimitive2D&>(rCandidate)); |
225 | 0 | break; |
226 | 0 | } |
227 | 0 | case PRIMITIVE2D_ID_POLYPOLYGONGRADIENTPRIMITIVE2D: |
228 | 0 | { |
229 | 0 | processPolyPolygonGradientPrimitive2D( |
230 | 0 | static_cast<const primitive2d::PolyPolygonGradientPrimitive2D&>(rCandidate)); |
231 | 0 | break; |
232 | 0 | } |
233 | 0 | case PRIMITIVE2D_ID_POLYPOLYGONGRAPHICPRIMITIVE2D: |
234 | 0 | { |
235 | | // direct draw of bitmap |
236 | 0 | RenderPolyPolygonGraphicPrimitive2D( |
237 | 0 | static_cast<const primitive2d::PolyPolygonGraphicPrimitive2D&>(rCandidate)); |
238 | 0 | break; |
239 | 0 | } |
240 | 0 | case PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D: |
241 | 0 | { |
242 | 0 | processPolyPolygonColorPrimitive2D( |
243 | 0 | static_cast<const primitive2d::PolyPolygonColorPrimitive2D&>(rCandidate)); |
244 | 0 | break; |
245 | 0 | } |
246 | 0 | case PRIMITIVE2D_ID_METAFILEPRIMITIVE2D: |
247 | 0 | { |
248 | 0 | processMetaFilePrimitive2D(rCandidate); |
249 | 0 | break; |
250 | 0 | } |
251 | 0 | case PRIMITIVE2D_ID_MASKPRIMITIVE2D: |
252 | 0 | { |
253 | | // mask group. |
254 | 0 | RenderMaskPrimitive2DPixel( |
255 | 0 | static_cast<const primitive2d::MaskPrimitive2D&>(rCandidate)); |
256 | 0 | break; |
257 | 0 | } |
258 | 0 | case PRIMITIVE2D_ID_MODIFIEDCOLORPRIMITIVE2D: |
259 | 0 | { |
260 | | // modified color group. Force output to unified color. |
261 | 0 | RenderModifiedColorPrimitive2D( |
262 | 0 | static_cast<const primitive2d::ModifiedColorPrimitive2D&>(rCandidate)); |
263 | 0 | break; |
264 | 0 | } |
265 | 0 | case PRIMITIVE2D_ID_UNIFIEDTRANSPARENCEPRIMITIVE2D: |
266 | 0 | { |
267 | 0 | processUnifiedTransparencePrimitive2D( |
268 | 0 | static_cast<const primitive2d::UnifiedTransparencePrimitive2D&>(rCandidate)); |
269 | 0 | break; |
270 | 0 | } |
271 | 0 | case PRIMITIVE2D_ID_TRANSPARENCEPRIMITIVE2D: |
272 | 0 | { |
273 | | // sub-transparence group. Draw to VDev first. |
274 | 0 | RenderTransparencePrimitive2D( |
275 | 0 | static_cast<const primitive2d::TransparencePrimitive2D&>(rCandidate)); |
276 | 0 | break; |
277 | 0 | } |
278 | 0 | case PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D: |
279 | 0 | { |
280 | | // transform group. |
281 | 0 | RenderTransformPrimitive2D( |
282 | 0 | static_cast<const primitive2d::TransformPrimitive2D&>(rCandidate)); |
283 | 0 | break; |
284 | 0 | } |
285 | 0 | case PRIMITIVE2D_ID_PAGEPREVIEWPRIMITIVE2D: |
286 | 0 | { |
287 | | // new XDrawPage for ViewInformation2D |
288 | 0 | RenderPagePreviewPrimitive2D( |
289 | 0 | static_cast<const primitive2d::PagePreviewPrimitive2D&>(rCandidate)); |
290 | 0 | break; |
291 | 0 | } |
292 | 0 | case PRIMITIVE2D_ID_MARKERARRAYPRIMITIVE2D: |
293 | 0 | { |
294 | | // marker array |
295 | 0 | RenderMarkerArrayPrimitive2D( |
296 | 0 | static_cast<const primitive2d::MarkerArrayPrimitive2D&>(rCandidate)); |
297 | 0 | break; |
298 | 0 | } |
299 | 0 | case PRIMITIVE2D_ID_POINTARRAYPRIMITIVE2D: |
300 | 0 | { |
301 | | // point array |
302 | 0 | RenderPointArrayPrimitive2D( |
303 | 0 | static_cast<const primitive2d::PointArrayPrimitive2D&>(rCandidate)); |
304 | 0 | break; |
305 | 0 | } |
306 | 0 | case PRIMITIVE2D_ID_CONTROLPRIMITIVE2D: |
307 | 0 | { |
308 | 0 | processControlPrimitive2D( |
309 | 0 | static_cast<const primitive2d::ControlPrimitive2D&>(rCandidate)); |
310 | 0 | break; |
311 | 0 | } |
312 | 0 | case PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D: |
313 | 0 | { |
314 | 0 | processPolygonStrokePrimitive2D( |
315 | 0 | static_cast<const primitive2d::PolygonStrokePrimitive2D&>(rCandidate)); |
316 | 0 | break; |
317 | 0 | } |
318 | 0 | case PRIMITIVE2D_ID_FILLHATCHPRIMITIVE2D: |
319 | 0 | { |
320 | 0 | processFillHatchPrimitive2D( |
321 | 0 | static_cast<const primitive2d::FillHatchPrimitive2D&>(rCandidate)); |
322 | 0 | break; |
323 | 0 | } |
324 | 0 | case PRIMITIVE2D_ID_BACKGROUNDCOLORPRIMITIVE2D: |
325 | 0 | { |
326 | 0 | processBackgroundColorPrimitive2D( |
327 | 0 | static_cast<const primitive2d::BackgroundColorPrimitive2D&>(rCandidate)); |
328 | 0 | break; |
329 | 0 | } |
330 | 0 | case PRIMITIVE2D_ID_INVERTPRIMITIVE2D: |
331 | 0 | { |
332 | 0 | processInvertPrimitive2D(rCandidate); |
333 | 0 | break; |
334 | 0 | } |
335 | 0 | case PRIMITIVE2D_ID_EPSPRIMITIVE2D: |
336 | 0 | { |
337 | 0 | RenderEpsPrimitive2D(static_cast<const primitive2d::EpsPrimitive2D&>(rCandidate)); |
338 | 0 | break; |
339 | 0 | } |
340 | 0 | case PRIMITIVE2D_ID_SVGLINEARATOMPRIMITIVE2D: |
341 | 0 | { |
342 | 0 | RenderSvgLinearAtomPrimitive2D( |
343 | 0 | static_cast<const primitive2d::SvgLinearAtomPrimitive2D&>(rCandidate)); |
344 | 0 | break; |
345 | 0 | } |
346 | 0 | case PRIMITIVE2D_ID_SVGRADIALATOMPRIMITIVE2D: |
347 | 0 | { |
348 | 0 | RenderSvgRadialAtomPrimitive2D( |
349 | 0 | static_cast<const primitive2d::SvgRadialAtomPrimitive2D&>(rCandidate)); |
350 | 0 | break; |
351 | 0 | } |
352 | 0 | case PRIMITIVE2D_ID_BORDERLINEPRIMITIVE2D: |
353 | 0 | { |
354 | 0 | processBorderLinePrimitive2D( |
355 | 0 | static_cast<const drawinglayer::primitive2d::BorderLinePrimitive2D&>(rCandidate)); |
356 | 0 | break; |
357 | 0 | } |
358 | 0 | case PRIMITIVE2D_ID_FILLGRADIENTPRIMITIVE2D: |
359 | 0 | { |
360 | 0 | processFillGradientPrimitive2D( |
361 | 0 | static_cast<const drawinglayer::primitive2d::FillGradientPrimitive2D&>(rCandidate)); |
362 | 0 | break; |
363 | 0 | } |
364 | 0 | case PRIMITIVE2D_ID_PATTERNFILLPRIMITIVE2D: |
365 | 0 | { |
366 | 0 | processPatternFillPrimitive2D( |
367 | 0 | static_cast<const drawinglayer::primitive2d::PatternFillPrimitive2D&>(rCandidate)); |
368 | 0 | break; |
369 | 0 | } |
370 | 0 | default: |
371 | 0 | { |
372 | 0 | SAL_INFO("drawinglayer", "default case for " << drawinglayer::primitive2d::idToString( |
373 | 0 | rCandidate.getPrimitive2DID())); |
374 | | // process recursively |
375 | 0 | process(rCandidate); |
376 | 0 | break; |
377 | 0 | } |
378 | 0 | } |
379 | 0 | } |
380 | | |
381 | | void VclPixelProcessor2D::processTextSimplePortionPrimitive2D( |
382 | | const primitive2d::TextSimplePortionPrimitive2D& rCandidate) |
383 | 0 | { |
384 | | // Adapt evtl. used special DrawMode |
385 | 0 | const DrawModeFlags nOriginalDrawMode(mpOutputDevice->GetDrawMode()); |
386 | 0 | adaptTextToFillDrawMode(); |
387 | |
|
388 | 0 | if (SAL_LIKELY(m_bRenderSimpleTextDirect)) |
389 | 0 | { |
390 | 0 | RenderTextSimpleOrDecoratedPortionPrimitive2D(rCandidate); |
391 | 0 | } |
392 | 0 | else |
393 | 0 | { |
394 | 0 | process(rCandidate); |
395 | 0 | } |
396 | | |
397 | | // restore DrawMode |
398 | 0 | mpOutputDevice->SetDrawMode(nOriginalDrawMode); |
399 | 0 | } |
400 | | |
401 | | void VclPixelProcessor2D::processTextDecoratedPortionPrimitive2D( |
402 | | const primitive2d::TextSimplePortionPrimitive2D& rCandidate) |
403 | 0 | { |
404 | | // Adapt evtl. used special DrawMode |
405 | 0 | const DrawModeFlags nOriginalDrawMode(mpOutputDevice->GetDrawMode()); |
406 | 0 | adaptTextToFillDrawMode(); |
407 | |
|
408 | 0 | if (SAL_LIKELY(m_bRenderDecoratedTextDirect)) |
409 | 0 | { |
410 | 0 | RenderTextSimpleOrDecoratedPortionPrimitive2D(rCandidate); |
411 | 0 | } |
412 | 0 | else |
413 | 0 | { |
414 | 0 | process(rCandidate); |
415 | 0 | } |
416 | | |
417 | | // restore DrawMode |
418 | 0 | mpOutputDevice->SetDrawMode(nOriginalDrawMode); |
419 | 0 | } |
420 | | |
421 | | void VclPixelProcessor2D::processPolygonHairlinePrimitive2D( |
422 | | const primitive2d::PolygonHairlinePrimitive2D& rPolygonHairlinePrimitive2D) |
423 | 0 | { |
424 | 0 | if (tryDrawPolygonHairlinePrimitive2DDirect(rPolygonHairlinePrimitive2D, 0.0)) |
425 | 0 | { |
426 | 0 | return; |
427 | 0 | } |
428 | | |
429 | | // direct draw of hairline |
430 | 0 | RenderPolygonHairlinePrimitive2D(rPolygonHairlinePrimitive2D, true); |
431 | 0 | } |
432 | | |
433 | | void VclPixelProcessor2D::processBitmapPrimitive2D( |
434 | | const primitive2d::BitmapPrimitive2D& rBitmapCandidate) |
435 | 0 | { |
436 | | // check if graphic content is inside discrete local ViewPort |
437 | 0 | const basegfx::B2DRange& rDiscreteViewPort(getViewInformation2D().getDiscreteViewport()); |
438 | 0 | const basegfx::B2DHomMatrix aLocalTransform(maCurrentTransformation |
439 | 0 | * rBitmapCandidate.getTransform()); |
440 | |
|
441 | 0 | if (!rDiscreteViewPort.isEmpty()) |
442 | 0 | { |
443 | 0 | basegfx::B2DRange aUnitRange(0.0, 0.0, 1.0, 1.0); |
444 | |
|
445 | 0 | aUnitRange.transform(aLocalTransform); |
446 | |
|
447 | 0 | if (!aUnitRange.overlaps(rDiscreteViewPort)) |
448 | 0 | { |
449 | | // content is outside discrete local ViewPort |
450 | 0 | return; |
451 | 0 | } |
452 | 0 | } |
453 | | |
454 | 0 | RenderBitmapPrimitive2D(rBitmapCandidate); |
455 | 0 | } |
456 | | |
457 | | void VclPixelProcessor2D::processPolyPolygonGradientPrimitive2D( |
458 | | const primitive2d::PolyPolygonGradientPrimitive2D& rPolygonCandidate) |
459 | 0 | { |
460 | 0 | basegfx::B2DPolyPolygon aLocalPolyPolygon(rPolygonCandidate.getB2DPolyPolygon()); |
461 | | |
462 | | // no geometry, no need to render, done |
463 | 0 | if (!aLocalPolyPolygon.count()) |
464 | 0 | return; |
465 | | |
466 | | // *try* direct draw (AKA using old VCL stuff) to render gradient |
467 | 0 | const attribute::FillGradientAttribute& rGradient(rPolygonCandidate.getFillGradient()); |
468 | | |
469 | | // MCGR: *many* - and not only GradientStops - cases cannot be handled by VCL |
470 | | // so use decomposition |
471 | | // NOTE: There may be even more reasons to detect, e.g. a ViewTransformation |
472 | | // that uses shear/rotate/mirror (what VCL cannot handle at all), see |
473 | | // other checks already in processFillGradientPrimitive2D |
474 | 0 | if (rGradient.cannotBeHandledByVCL()) |
475 | 0 | { |
476 | 0 | process(rPolygonCandidate); |
477 | 0 | return; |
478 | 0 | } |
479 | | |
480 | 0 | basegfx::BColor aStartColor( |
481 | 0 | maBColorModifierStack.getModifiedColor(rGradient.getColorStops().front().getStopColor())); |
482 | 0 | basegfx::BColor aEndColor( |
483 | 0 | maBColorModifierStack.getModifiedColor(rGradient.getColorStops().back().getStopColor())); |
484 | |
|
485 | 0 | if (aStartColor == aEndColor) |
486 | 0 | { |
487 | | // no gradient at all, draw as polygon in AA and non-AA case |
488 | 0 | aLocalPolyPolygon.transform(maCurrentTransformation); |
489 | 0 | mpOutputDevice->SetLineColor(); |
490 | 0 | mpOutputDevice->SetFillColor(Color(aStartColor)); |
491 | 0 | mpOutputDevice->DrawPolyPolygon(aLocalPolyPolygon); |
492 | 0 | return; |
493 | 0 | } |
494 | | |
495 | | // use the primitive decomposition |
496 | 0 | process(rPolygonCandidate); |
497 | 0 | } |
498 | | |
499 | | void VclPixelProcessor2D::processPolyPolygonColorPrimitive2D( |
500 | | const primitive2d::PolyPolygonColorPrimitive2D& rPolyPolygonColorPrimitive2D) |
501 | 0 | { |
502 | | // try to use directly |
503 | 0 | basegfx::B2DPolyPolygon aLocalPolyPolygon; |
504 | |
|
505 | 0 | tryDrawPolyPolygonColorPrimitive2DDirect(rPolyPolygonColorPrimitive2D, 0.0); |
506 | | // okay, done. In this case no gaps should have to be repaired, too |
507 | | |
508 | | // when AA is on and this filled polygons are the result of stroked line geometry, |
509 | | // draw the geometry once extra as lines to avoid AA 'gaps' between partial polygons |
510 | | // Caution: This is needed in both cases (!) |
511 | 0 | if (!(mnPolygonStrokePrimitive2D && getViewInformation2D().getUseAntiAliasing() |
512 | 0 | && (mpOutputDevice->GetAntialiasing() & AntialiasingFlags::Enable))) |
513 | 0 | return; |
514 | | |
515 | 0 | const basegfx::BColor aPolygonColor( |
516 | 0 | maBColorModifierStack.getModifiedColor(rPolyPolygonColorPrimitive2D.getBColor())); |
517 | 0 | sal_uInt32 nCount(aLocalPolyPolygon.count()); |
518 | |
|
519 | 0 | if (!nCount) |
520 | 0 | { |
521 | 0 | aLocalPolyPolygon = rPolyPolygonColorPrimitive2D.getB2DPolyPolygon(); |
522 | 0 | aLocalPolyPolygon.transform(maCurrentTransformation); |
523 | 0 | nCount = aLocalPolyPolygon.count(); |
524 | 0 | } |
525 | |
|
526 | 0 | mpOutputDevice->SetFillColor(); |
527 | 0 | mpOutputDevice->SetLineColor(Color(aPolygonColor)); |
528 | |
|
529 | 0 | for (sal_uInt32 a(0); a < nCount; a++) |
530 | 0 | { |
531 | 0 | mpOutputDevice->DrawPolyLine(aLocalPolyPolygon.getB2DPolygon(a), 0.0); |
532 | 0 | } |
533 | 0 | } |
534 | | |
535 | | void VclPixelProcessor2D::processUnifiedTransparencePrimitive2D( |
536 | | const primitive2d::UnifiedTransparencePrimitive2D& rUniTransparenceCandidate) |
537 | 0 | { |
538 | | // Detect if a single PolyPolygonColorPrimitive2D is contained; in that case, |
539 | | // use the faster OutputDevice::DrawTransparent method |
540 | 0 | const primitive2d::Primitive2DContainer& rContent = rUniTransparenceCandidate.getChildren(); |
541 | |
|
542 | 0 | if (rContent.empty()) |
543 | 0 | return; |
544 | | |
545 | 0 | if (0.0 == rUniTransparenceCandidate.getTransparence()) |
546 | 0 | { |
547 | | // not transparent at all, use content |
548 | 0 | process(rUniTransparenceCandidate.getChildren()); |
549 | 0 | } |
550 | 0 | else if (rUniTransparenceCandidate.getTransparence() > 0.0 |
551 | 0 | && rUniTransparenceCandidate.getTransparence() < 1.0) |
552 | 0 | { |
553 | 0 | bool bDrawTransparentUsed(false); |
554 | |
|
555 | 0 | if (1 == rContent.size()) |
556 | 0 | { |
557 | 0 | const primitive2d::BasePrimitive2D* pBasePrimitive = rContent.front().get(); |
558 | |
|
559 | 0 | switch (pBasePrimitive->getPrimitive2DID()) |
560 | 0 | { |
561 | 0 | case PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D: |
562 | 0 | { |
563 | | // single transparent tools::PolyPolygon identified, use directly |
564 | 0 | const primitive2d::PolyPolygonColorPrimitive2D* pPoPoColor |
565 | 0 | = static_cast<const primitive2d::PolyPolygonColorPrimitive2D*>( |
566 | 0 | pBasePrimitive); |
567 | 0 | assert(pPoPoColor && "OOps, PrimitiveID and PrimitiveType do not match (!)"); |
568 | 0 | bDrawTransparentUsed = true; |
569 | 0 | tryDrawPolyPolygonColorPrimitive2DDirect( |
570 | 0 | *pPoPoColor, rUniTransparenceCandidate.getTransparence()); |
571 | 0 | break; |
572 | 0 | } |
573 | 0 | case PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D: |
574 | 0 | { |
575 | | // single transparent PolygonHairlinePrimitive2D identified, use directly |
576 | 0 | const primitive2d::PolygonHairlinePrimitive2D* pPoHair |
577 | 0 | = static_cast<const primitive2d::PolygonHairlinePrimitive2D*>( |
578 | 0 | pBasePrimitive); |
579 | 0 | SAL_WARN_IF(!pPoHair, "drawinglayer", |
580 | 0 | "OOps, PrimitiveID and PrimitiveType do not match (!)"); |
581 | | |
582 | | // do no tallow by default - problem is that self-overlapping parts of this geometry will |
583 | | // not be in an all-same transparency but will already alpha-cover themselves with blending. |
584 | | // This is not what the UnifiedTransparencePrimitive2D defines: It requires all its |
585 | | // content to be uniformly transparent. |
586 | | // For hairline the effect is pretty minimal, but still not correct. |
587 | 0 | bDrawTransparentUsed = false; |
588 | 0 | break; |
589 | 0 | } |
590 | 0 | case PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D: |
591 | 0 | { |
592 | | // single transparent PolygonStrokePrimitive2D identified, use directly |
593 | 0 | const primitive2d::PolygonStrokePrimitive2D* pPoStroke |
594 | 0 | = static_cast<const primitive2d::PolygonStrokePrimitive2D*>(pBasePrimitive); |
595 | 0 | SAL_WARN_IF(!pPoStroke, "drawinglayer", |
596 | 0 | "OOps, PrimitiveID and PrimitiveType do not match (!)"); |
597 | | |
598 | | // do no tallow by default - problem is that self-overlapping parts of this geometry will |
599 | | // not be in an all-same transparency but will already alpha-cover themselves with blending. |
600 | | // This is not what the UnifiedTransparencePrimitive2D defines: It requires all its |
601 | | // content to be uniformly transparent. |
602 | | // To check, activate and draw a wide transparent self-crossing line/curve |
603 | 0 | bDrawTransparentUsed = false; |
604 | 0 | break; |
605 | 0 | } |
606 | 0 | default: |
607 | 0 | SAL_INFO("drawinglayer", |
608 | 0 | "default case for " << drawinglayer::primitive2d::idToString( |
609 | 0 | rUniTransparenceCandidate.getPrimitive2DID())); |
610 | 0 | break; |
611 | 0 | } |
612 | 0 | } |
613 | | |
614 | 0 | if (!bDrawTransparentUsed) |
615 | 0 | { |
616 | | // unified sub-transparence. Draw to VDev first. |
617 | 0 | RenderUnifiedTransparencePrimitive2D(rUniTransparenceCandidate); |
618 | 0 | } |
619 | 0 | } |
620 | 0 | } |
621 | | |
622 | | void VclPixelProcessor2D::processControlPrimitive2D( |
623 | | const primitive2d::ControlPrimitive2D& rControlPrimitive) |
624 | 0 | { |
625 | | // find out if the control is already visualized as a VCL-ChildWindow |
626 | 0 | bool bControlIsVisibleAsChildWindow(rControlPrimitive.isVisibleAsChildWindow()); |
627 | | |
628 | | // tdf#131281 The FormControls are not painted when using the Tiled Rendering for a simple |
629 | | // reason: when e.g. bControlIsVisibleAsChildWindow is true. This is the case because the |
630 | | // office is in non-layout mode (default for controls at startup). For the common office |
631 | | // this means that there exists a real VCL-System-Window for the control, so it is *not* |
632 | | // painted here due to being exactly obscured by that real Window (and creates danger of |
633 | | // flickering, too). |
634 | | // Tiled Rendering clients usually do *not* have real VCL-Windows for the controls, but |
635 | | // exactly that would be needed on each client displaying the tiles (what would be hard |
636 | | // to do but also would have advantages - the clients would have real controls in the |
637 | | // shape of their target system which could be interacted with...). It is also what the |
638 | | // office does. |
639 | | // For now, fallback to just render these controls when Tiled Rendering is active to just |
640 | | // have them displayed on all clients. |
641 | 0 | if (bControlIsVisibleAsChildWindow && comphelper::LibreOfficeKit::isActive()) |
642 | 0 | { |
643 | | // Do force paint when we are in Tiled Renderer and FormControl is 'visible' |
644 | 0 | bControlIsVisibleAsChildWindow = false; |
645 | 0 | } |
646 | |
|
647 | 0 | if (bControlIsVisibleAsChildWindow) |
648 | 0 | { |
649 | | // f the control is already visualized as a VCL-ChildWindow it |
650 | | // does not need to be painted at all |
651 | 0 | return; |
652 | 0 | } |
653 | | |
654 | 0 | bool bDone(false); |
655 | |
|
656 | 0 | try |
657 | 0 | { |
658 | 0 | const uno::Reference<awt::XGraphics> xTargetGraphics(mpOutputDevice->CreateUnoGraphics()); |
659 | |
|
660 | 0 | if (xTargetGraphics.is()) |
661 | 0 | { |
662 | | // Needs to be drawn. Link new graphics and view |
663 | 0 | const uno::Reference<awt::XControl>& rXControl(rControlPrimitive.getXControl()); |
664 | 0 | uno::Reference<awt::XView> xControlView(rXControl, uno::UNO_QUERY_THROW); |
665 | 0 | const uno::Reference<awt::XGraphics> xOriginalGraphics(xControlView->getGraphics()); |
666 | 0 | xControlView->setGraphics(xTargetGraphics); |
667 | | |
668 | | // get position |
669 | 0 | const basegfx::B2DHomMatrix aObjectToPixel(maCurrentTransformation |
670 | 0 | * rControlPrimitive.getTransform()); |
671 | 0 | const basegfx::B2DPoint aTopLeftPixel(aObjectToPixel * basegfx::B2DPoint(0.0, 0.0)); |
672 | | |
673 | | // Do not forget to use the evtl. offsetted origin of the target device, |
674 | | // e.g. when used with mask/transparence buffer device |
675 | 0 | const Point aOrigin(mpOutputDevice->GetMapMode().GetOrigin()); |
676 | 0 | xControlView->draw(aOrigin.X() + basegfx::fround(aTopLeftPixel.getX()), |
677 | 0 | aOrigin.Y() + basegfx::fround(aTopLeftPixel.getY())); |
678 | | |
679 | | // restore original graphics |
680 | 0 | xControlView->setGraphics(xOriginalGraphics); |
681 | 0 | bDone = true; |
682 | 0 | } |
683 | 0 | } |
684 | 0 | catch (const uno::Exception&) |
685 | 0 | { |
686 | | // #i116763# removing since there is a good alternative when the xControlView |
687 | | // is not found and it is allowed to happen |
688 | | // DBG_UNHANDLED_EXCEPTION(); |
689 | 0 | } |
690 | |
|
691 | 0 | if (!bDone) |
692 | 0 | { |
693 | | // process recursively and use the decomposition as Bitmap |
694 | 0 | process(rControlPrimitive); |
695 | 0 | } |
696 | 0 | } |
697 | | |
698 | | void VclPixelProcessor2D::processPolygonStrokePrimitive2D( |
699 | | const primitive2d::PolygonStrokePrimitive2D& rPolygonStrokePrimitive2D) |
700 | 0 | { |
701 | | // try to use directly |
702 | 0 | if (tryDrawPolygonStrokePrimitive2DDirect(rPolygonStrokePrimitive2D, 0.0)) |
703 | 0 | { |
704 | 0 | return; |
705 | 0 | } |
706 | | |
707 | | // the stroke primitive may be decomposed to filled polygons. To keep |
708 | | // evtl. set DrawModes aka DrawModeFlags::BlackLine, DrawModeFlags::GrayLine, |
709 | | // DrawModeFlags::GhostedLine, DrawModeFlags::WhiteLine or DrawModeFlags::SettingsLine |
710 | | // working, these need to be copied to the corresponding fill modes |
711 | 0 | const DrawModeFlags nOriginalDrawMode(mpOutputDevice->GetDrawMode()); |
712 | 0 | adaptLineToFillDrawMode(); |
713 | | |
714 | | // polygon stroke primitive |
715 | | |
716 | | // Lines with 1 and 2 pixel width without AA need special treatment since their visualization |
717 | | // as filled polygons is geometrically correct but looks wrong since polygon filling avoids |
718 | | // the right and bottom pixels. The used method evaluates that and takes the correct action, |
719 | | // including calling recursively with decomposition if line is wide enough |
720 | 0 | RenderPolygonStrokePrimitive2D(rPolygonStrokePrimitive2D); |
721 | | |
722 | | // restore DrawMode |
723 | 0 | mpOutputDevice->SetDrawMode(nOriginalDrawMode); |
724 | 0 | } |
725 | | |
726 | | void VclPixelProcessor2D::processFillHatchPrimitive2D( |
727 | | const primitive2d::FillHatchPrimitive2D& rFillHatchPrimitive) |
728 | 0 | { |
729 | 0 | if (getViewInformation2D().getUseAntiAliasing()) |
730 | 0 | { |
731 | | // if AA is used (or ignore smoothing is on), there is no need to smooth |
732 | | // hatch painting, use decomposition |
733 | 0 | process(rFillHatchPrimitive); |
734 | 0 | } |
735 | 0 | else |
736 | 0 | { |
737 | | // without AA, use VCL to draw the hatch. It snaps hatch distances to the next pixel |
738 | | // and forces hatch distance to be >= 3 pixels to make the hatch display look smoother. |
739 | | // This is wrong in principle, but looks nicer. This could also be done here directly |
740 | | // without VCL usage if needed |
741 | 0 | const attribute::FillHatchAttribute& rFillHatchAttributes |
742 | 0 | = rFillHatchPrimitive.getFillHatch(); |
743 | | |
744 | | // create hatch polygon in range size and discrete coordinates |
745 | 0 | basegfx::B2DRange aHatchRange(rFillHatchPrimitive.getOutputRange()); |
746 | 0 | aHatchRange.transform(maCurrentTransformation); |
747 | 0 | const basegfx::B2DPolygon aHatchPolygon(basegfx::utils::createPolygonFromRect(aHatchRange)); |
748 | |
|
749 | 0 | if (rFillHatchAttributes.isFillBackground()) |
750 | 0 | { |
751 | | // #i111846# background fill is active; draw fill polygon |
752 | 0 | const basegfx::BColor aPolygonColor( |
753 | 0 | maBColorModifierStack.getModifiedColor(rFillHatchPrimitive.getBColor())); |
754 | |
|
755 | 0 | mpOutputDevice->SetFillColor(Color(aPolygonColor)); |
756 | 0 | mpOutputDevice->SetLineColor(); |
757 | 0 | mpOutputDevice->DrawPolygon(aHatchPolygon); |
758 | 0 | } |
759 | | |
760 | | // set hatch line color |
761 | 0 | const basegfx::BColor aHatchColor( |
762 | 0 | maBColorModifierStack.getModifiedColor(rFillHatchPrimitive.getBColor())); |
763 | 0 | mpOutputDevice->SetFillColor(); |
764 | 0 | mpOutputDevice->SetLineColor(Color(aHatchColor)); |
765 | | |
766 | | // get hatch style |
767 | 0 | HatchStyle eHatchStyle(HatchStyle::Single); |
768 | |
|
769 | 0 | switch (rFillHatchAttributes.getStyle()) |
770 | 0 | { |
771 | 0 | default: // HatchStyle::Single |
772 | 0 | { |
773 | 0 | break; |
774 | 0 | } |
775 | 0 | case attribute::HatchStyle::Double: |
776 | 0 | { |
777 | 0 | eHatchStyle = HatchStyle::Double; |
778 | 0 | break; |
779 | 0 | } |
780 | 0 | case attribute::HatchStyle::Triple: |
781 | 0 | { |
782 | 0 | eHatchStyle = HatchStyle::Triple; |
783 | 0 | break; |
784 | 0 | } |
785 | 0 | } |
786 | | |
787 | | // create hatch |
788 | 0 | const basegfx::B2DVector aDiscreteDistance( |
789 | 0 | maCurrentTransformation * basegfx::B2DVector(rFillHatchAttributes.getDistance(), 0.0)); |
790 | 0 | const sal_uInt32 nDistance(basegfx::fround(aDiscreteDistance.getLength())); |
791 | 0 | const sal_uInt32 nAngle10( |
792 | 0 | basegfx::fround(basegfx::rad2deg<10>(rFillHatchAttributes.getAngle()))); |
793 | 0 | ::Hatch aVCLHatch(eHatchStyle, Color(rFillHatchAttributes.getColor()), nDistance, |
794 | 0 | Degree10(nAngle10)); |
795 | | |
796 | | // draw hatch using VCL |
797 | 0 | mpOutputDevice->DrawHatch(::tools::PolyPolygon(::tools::Polygon(aHatchPolygon)), aVCLHatch); |
798 | 0 | } |
799 | 0 | } |
800 | | |
801 | | void VclPixelProcessor2D::processBackgroundColorPrimitive2D( |
802 | | const primitive2d::BackgroundColorPrimitive2D& rPrimitive) |
803 | 0 | { |
804 | | // #i98404# Handle directly, especially when AA is active |
805 | 0 | const AntialiasingFlags nOriginalAA(mpOutputDevice->GetAntialiasing()); |
806 | | |
807 | | // switch AA off in all cases |
808 | 0 | mpOutputDevice->SetAntialiasing(mpOutputDevice->GetAntialiasing() & ~AntialiasingFlags::Enable); |
809 | | |
810 | | // create color for fill |
811 | 0 | const basegfx::BColor aPolygonColor( |
812 | 0 | maBColorModifierStack.getModifiedColor(rPrimitive.getBColor())); |
813 | 0 | Color aFillColor(aPolygonColor); |
814 | 0 | aFillColor.SetAlpha(255 - sal_uInt8((rPrimitive.getTransparency() * 255.0) + 0.5)); |
815 | 0 | mpOutputDevice->SetFillColor(aFillColor); |
816 | 0 | mpOutputDevice->SetLineColor(); |
817 | | |
818 | | // create rectangle for fill |
819 | 0 | const basegfx::B2DRange& aViewport(getViewInformation2D().getDiscreteViewport()); |
820 | 0 | const ::tools::Rectangle aRectangle(static_cast<sal_Int32>(floor(aViewport.getMinX())), |
821 | 0 | static_cast<sal_Int32>(floor(aViewport.getMinY())), |
822 | 0 | static_cast<sal_Int32>(ceil(aViewport.getMaxX())), |
823 | 0 | static_cast<sal_Int32>(ceil(aViewport.getMaxY()))); |
824 | 0 | mpOutputDevice->DrawRect(aRectangle); |
825 | | |
826 | | // restore AA setting |
827 | 0 | mpOutputDevice->SetAntialiasing(nOriginalAA); |
828 | 0 | } |
829 | | |
830 | | void VclPixelProcessor2D::processBorderLinePrimitive2D( |
831 | | const drawinglayer::primitive2d::BorderLinePrimitive2D& rBorder) |
832 | 0 | { |
833 | | // Process recursively, but switch off AntiAliasing for |
834 | | // horizontal/vertical lines (*not* diagonal lines). |
835 | | // Checked using AntialiasingFlags::PixelSnapHairline instead, |
836 | | // but with AntiAliasing on the display really is too 'ghosty' when |
837 | | // using fine stroking. Correct, but 'ghosty'. |
838 | | |
839 | | // It has shown that there are quite some problems here: |
840 | | // - vcl OutDev renderer methods still use fallbacks to paint |
841 | | // multiple single lines between discrete sizes of < 3.5 what |
842 | | // looks bad and does not match |
843 | | // - mix of filled Polygons and Lines is bad when AA switched off |
844 | | // - Alignment of AA with non-AA may be bad in diverse different |
845 | | // renderers |
846 | | // |
847 | | // Due to these reasons I change the strategy: Always draw AAed, but |
848 | | // allow fallback to test/check and if needed. The normal case |
849 | | // where BorderLines will be system-dependently snapped to have at |
850 | | // least a single discrete width per partial line (there may be up to |
851 | | // three) works well nowadays due to most renderers moving the AA stuff |
852 | | // by 0.5 pixels (discrete units) to match well with the non-AAed parts. |
853 | | // |
854 | | // Env-Switch for steering this, default is off. |
855 | | // Enable by setting at all (and to something) |
856 | 0 | static const char* pSwitchOffAntiAliasingForHorVerBorderlines( |
857 | 0 | getenv("SAL_SWITCH_OFF_ANTIALIASING_FOR_HOR_VER_BORTDERLINES")); |
858 | 0 | static bool bSwitchOffAntiAliasingForHorVerBorderlines( |
859 | 0 | nullptr != pSwitchOffAntiAliasingForHorVerBorderlines); |
860 | |
|
861 | 0 | if (bSwitchOffAntiAliasingForHorVerBorderlines |
862 | 0 | && rBorder.isHorizontalOrVertical(getViewInformation2D())) |
863 | 0 | { |
864 | 0 | AntialiasingFlags nAntiAliasing = mpOutputDevice->GetAntialiasing(); |
865 | 0 | mpOutputDevice->SetAntialiasing(nAntiAliasing & ~AntialiasingFlags::Enable); |
866 | 0 | process(rBorder); |
867 | 0 | mpOutputDevice->SetAntialiasing(nAntiAliasing); |
868 | 0 | } |
869 | 0 | else |
870 | 0 | { |
871 | 0 | process(rBorder); |
872 | 0 | } |
873 | 0 | } |
874 | | |
875 | | void VclPixelProcessor2D::processInvertPrimitive2D(const primitive2d::BasePrimitive2D& rCandidate) |
876 | 0 | { |
877 | | // invert primitive (currently only used for HighContrast fallback for selection in SW and SC). |
878 | | // (Not true, also used at least for the drawing of dragged column and row boundaries in SC.) |
879 | | // Set OutDev to XOR and switch AA off (XOR does not work with AA) |
880 | 0 | auto popIt = mpOutputDevice->ScopedPush(); |
881 | 0 | mpOutputDevice->SetRasterOp(RasterOp::Xor); |
882 | 0 | const AntialiasingFlags nAntiAliasing(mpOutputDevice->GetAntialiasing()); |
883 | 0 | mpOutputDevice->SetAntialiasing(nAntiAliasing & ~AntialiasingFlags::Enable); |
884 | | |
885 | | // process content recursively |
886 | 0 | process(rCandidate); |
887 | | |
888 | | // restore OutDev |
889 | 0 | mpOutputDevice->SetAntialiasing(nAntiAliasing); |
890 | 0 | } |
891 | | |
892 | | void VclPixelProcessor2D::processMetaFilePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate) |
893 | 0 | { |
894 | | // #i98289# |
895 | 0 | const bool bForceLineSnap(getViewInformation2D().getPixelSnapHairline()); |
896 | 0 | const AntialiasingFlags nOldAntiAliase(mpOutputDevice->GetAntialiasing()); |
897 | |
|
898 | 0 | if (bForceLineSnap) |
899 | 0 | { |
900 | 0 | mpOutputDevice->SetAntialiasing(nOldAntiAliase | AntialiasingFlags::PixelSnapHairline); |
901 | 0 | } |
902 | |
|
903 | 0 | process(rCandidate); |
904 | |
|
905 | 0 | if (bForceLineSnap) |
906 | 0 | { |
907 | 0 | mpOutputDevice->SetAntialiasing(nOldAntiAliase); |
908 | 0 | } |
909 | 0 | } |
910 | | |
911 | | void VclPixelProcessor2D::processFillGradientPrimitive2D( |
912 | | const primitive2d::FillGradientPrimitive2D& rPrimitive) |
913 | 0 | { |
914 | 0 | if (rPrimitive.hasAlphaGradient() || rPrimitive.hasTransparency()) |
915 | 0 | { |
916 | | // SDPR: As long as direct alpha is not supported by this |
917 | | // renderer we need to work on the decomposition, so call it |
918 | 0 | process(rPrimitive); |
919 | 0 | return; |
920 | 0 | } |
921 | | |
922 | 0 | const attribute::FillGradientAttribute& rFillGradient = rPrimitive.getFillGradient(); |
923 | 0 | bool useDecompose(false); |
924 | | |
925 | | // MCGR: *many* - and not only GradientStops - cases cannot be handled by VCL |
926 | | // so use decomposition |
927 | 0 | if (rFillGradient.cannotBeHandledByVCL()) |
928 | 0 | { |
929 | 0 | useDecompose = true; |
930 | 0 | } |
931 | | |
932 | | // tdf#149754 VCL gradient draw is not capable to handle all primitive gradient definitions, |
933 | | // what should be clear due to being developed to extend/replace them in |
934 | | // capabilities & precision. |
935 | | // It is e.g. not capable to correctly paint if the OutputRange is not completely |
936 | | // inside the DefinitionRange, thus forcing to paint gradient parts *outside* the |
937 | | // DefinitionRange. |
938 | | // This happens for Writer with Frames anchored in Frames (and was broken due to |
939 | | // falling back to VCL Gradient paint here), and for the new SlideBackgroundFill |
940 | | // Fill mode for objects using it that reach outside the page (which is the |
941 | | // DefinitionRange in that case). |
942 | | // I see no real reason to fallback here to OutputDevice::DrawGradient and VCL |
943 | | // gradient paint at all (system-dependent renderers wouldn't in the future), but |
944 | | // will for convenience only add that needed additional correcting case |
945 | 0 | if (!useDecompose && !rPrimitive.getDefinitionRange().isInside(rPrimitive.getOutputRange())) |
946 | 0 | { |
947 | 0 | useDecompose = true; |
948 | 0 | } |
949 | | |
950 | | // tdf#151081 need to use regular primitive decomposition when the gradient |
951 | | // is transformed in any other way then just translate & scale |
952 | 0 | if (!useDecompose) |
953 | 0 | { |
954 | 0 | basegfx::B2DVector aScale, aTranslate; |
955 | 0 | double fRotate, fShearX; |
956 | |
|
957 | 0 | maCurrentTransformation.decompose(aScale, aTranslate, fRotate, fShearX); |
958 | | |
959 | | // detect if transformation is rotated, sheared or mirrored in X and/or Y |
960 | 0 | if (!basegfx::fTools::equalZero(fRotate) || !basegfx::fTools::equalZero(fShearX) |
961 | 0 | || aScale.getX() < 0.0 || aScale.getY() < 0.0) |
962 | 0 | { |
963 | 0 | useDecompose = true; |
964 | 0 | } |
965 | 0 | } |
966 | |
|
967 | 0 | if (useDecompose) |
968 | 0 | { |
969 | | // default is to use the direct render below. For security, |
970 | | // keep the (simple) fallback to decompose in place here |
971 | 0 | static bool bTryDirectRender(true); |
972 | |
|
973 | 0 | if (bTryDirectRender) |
974 | 0 | { |
975 | | // MCGR: Avoid one level of primitive creation, use FillGradientPrimitive2D |
976 | | // tooling to directly create needed geometry & color for getting better |
977 | | // performance (partially compensate for potentially more expensive multi |
978 | | // color gradients). |
979 | | // To handle a primitive that needs paint, either use decompose, or - when you |
980 | | // do not want that for any reason, e.g. extra primitives created - implement |
981 | | // a direct handling in your primitive renderer. This is always possible |
982 | | // since primitives by definition are self-contained what means they have all |
983 | | // needed data locally available to do so. |
984 | | // The question is the complexity to invest - the implemented decompose |
985 | | // is always a good hint of what is needed to do this. In this case I decided |
986 | | // to add some tooling methods to the primitive itself to support this. These |
987 | | // are used in decompose and can be used - as here now - for direct handling, |
988 | | // too. This is always a possibility in primitive handling - you can, but do not |
989 | | // have to. |
990 | 0 | mpOutputDevice->SetFillColor( |
991 | 0 | Color(maBColorModifierStack.getModifiedColor(rPrimitive.getOuterColor()))); |
992 | 0 | mpOutputDevice->SetLineColor(); |
993 | 0 | mpOutputDevice->DrawTransparent( |
994 | 0 | maCurrentTransformation, |
995 | 0 | basegfx::B2DPolyPolygon( |
996 | 0 | basegfx::utils::createPolygonFromRect(rPrimitive.getOutputRange())), |
997 | 0 | 0.0); |
998 | | |
999 | | // paint solid fill steps by providing callback as lambda |
1000 | 0 | auto aCallback([&rPrimitive, this](const basegfx::B2DHomMatrix& rMatrix, |
1001 | 0 | const basegfx::BColor& rColor) { |
1002 | | // create part polygon |
1003 | 0 | basegfx::B2DPolygon aNewPoly(rPrimitive.getUnitPolygon()); |
1004 | 0 | aNewPoly.transform(rMatrix); |
1005 | | |
1006 | | // create solid fill |
1007 | 0 | mpOutputDevice->SetFillColor(Color(maBColorModifierStack.getModifiedColor(rColor))); |
1008 | 0 | mpOutputDevice->DrawTransparent(maCurrentTransformation, |
1009 | 0 | basegfx::B2DPolyPolygon(aNewPoly), 0.0); |
1010 | 0 | }); |
1011 | | |
1012 | | // call value generator to trigger callbacks |
1013 | 0 | rPrimitive.generateMatricesAndColors(aCallback); |
1014 | 0 | } |
1015 | 0 | else |
1016 | 0 | { |
1017 | | // use the decompose |
1018 | 0 | process(rPrimitive); |
1019 | 0 | } |
1020 | |
|
1021 | 0 | return; |
1022 | 0 | } |
1023 | | |
1024 | | // try to use vcl - since vcl uses the old gradient paint mechanisms this may |
1025 | | // create wrong geometries. If so, add another case above for useDecompose |
1026 | 0 | Gradient aGradient(rFillGradient.getStyle(), |
1027 | 0 | Color(rFillGradient.getColorStops().front().getStopColor()), |
1028 | 0 | Color(rFillGradient.getColorStops().back().getStopColor())); |
1029 | |
|
1030 | 0 | aGradient.SetAngle(Degree10(static_cast<int>(basegfx::rad2deg<10>(rFillGradient.getAngle())))); |
1031 | 0 | aGradient.SetBorder(rFillGradient.getBorder() * 100); |
1032 | 0 | aGradient.SetOfsX(rFillGradient.getOffsetX() * 100.0); |
1033 | 0 | aGradient.SetOfsY(rFillGradient.getOffsetY() * 100.0); |
1034 | 0 | aGradient.SetSteps(rFillGradient.getSteps()); |
1035 | |
|
1036 | 0 | basegfx::B2DRange aOutputRange(rPrimitive.getOutputRange()); |
1037 | 0 | aOutputRange.transform(maCurrentTransformation); |
1038 | 0 | basegfx::B2DRange aFullRange(rPrimitive.getDefinitionRange()); |
1039 | 0 | aFullRange.transform(maCurrentTransformation); |
1040 | |
|
1041 | 0 | const tools::Rectangle aOutputRectangle( |
1042 | 0 | std::floor(aOutputRange.getMinX()), std::floor(aOutputRange.getMinY()), |
1043 | 0 | std::ceil(aOutputRange.getMaxX()), std::ceil(aOutputRange.getMaxY())); |
1044 | 0 | const tools::Rectangle aFullRectangle( |
1045 | 0 | std::floor(aFullRange.getMinX()), std::floor(aFullRange.getMinY()), |
1046 | 0 | std::ceil(aFullRange.getMaxX()), std::ceil(aFullRange.getMaxY())); |
1047 | |
|
1048 | 0 | auto popIt = mpOutputDevice->ScopedPush(vcl::PushFlags::CLIPREGION); |
1049 | 0 | mpOutputDevice->IntersectClipRegion(aOutputRectangle); |
1050 | 0 | mpOutputDevice->DrawGradient(aFullRectangle, aGradient); |
1051 | 0 | } |
1052 | | |
1053 | | void VclPixelProcessor2D::processPatternFillPrimitive2D( |
1054 | | const primitive2d::PatternFillPrimitive2D& rPrimitive) |
1055 | 0 | { |
1056 | 0 | const basegfx::B2DRange& rReferenceRange = rPrimitive.getReferenceRange(); |
1057 | 0 | if (rReferenceRange.isEmpty() || rReferenceRange.getWidth() <= 0.0 |
1058 | 0 | || rReferenceRange.getHeight() <= 0.0) |
1059 | 0 | return; |
1060 | | |
1061 | 0 | basegfx::B2DPolyPolygon aMask = rPrimitive.getMask(); |
1062 | 0 | aMask.transform(maCurrentTransformation); |
1063 | 0 | const basegfx::B2DRange aMaskRange(aMask.getB2DRange()); |
1064 | |
|
1065 | 0 | if (aMaskRange.isEmpty() || aMaskRange.getWidth() <= 0.0 || aMaskRange.getHeight() <= 0.0) |
1066 | 0 | return; |
1067 | | |
1068 | 0 | sal_uInt32 nTileWidth, nTileHeight; |
1069 | 0 | rPrimitive.getTileSize(nTileWidth, nTileHeight, getViewInformation2D()); |
1070 | 0 | if (nTileWidth == 0 || nTileHeight == 0) |
1071 | 0 | return; |
1072 | 0 | Bitmap aTileImage = rPrimitive.createTileImage(nTileWidth, nTileHeight); |
1073 | 0 | tools::Rectangle aMaskRect = vcl::unotools::rectangleFromB2DRectangle(aMaskRange); |
1074 | | |
1075 | | // Unless smooth edges are needed, simply use clipping. |
1076 | 0 | if (basegfx::utils::isRectangle(aMask) || !getViewInformation2D().getUseAntiAliasing()) |
1077 | 0 | { |
1078 | 0 | auto popIt = mpOutputDevice->ScopedPush(vcl::PushFlags::CLIPREGION); |
1079 | 0 | mpOutputDevice->IntersectClipRegion(vcl::Region(aMask)); |
1080 | 0 | Wallpaper aWallpaper(aTileImage); |
1081 | 0 | aWallpaper.SetColor(COL_TRANSPARENT); |
1082 | 0 | Point aPaperPt(aMaskRect.getX() % nTileWidth, aMaskRect.getY() % nTileHeight); |
1083 | 0 | tools::Rectangle aPaperRect(aPaperPt, aTileImage.GetSizePixel()); |
1084 | 0 | aWallpaper.SetRect(aPaperRect); |
1085 | 0 | mpOutputDevice->DrawWallpaper(aMaskRect, aWallpaper); |
1086 | 0 | return; |
1087 | 0 | } |
1088 | | |
1089 | | // if the tile is a single pixel big, just flood fill with that pixel color |
1090 | 0 | if (nTileWidth == 1 && nTileHeight == 1) |
1091 | 0 | { |
1092 | 0 | Color col = aTileImage.GetPixelColor(0, 0); |
1093 | 0 | mpOutputDevice->SetLineColor(col); |
1094 | 0 | mpOutputDevice->SetFillColor(col); |
1095 | 0 | mpOutputDevice->DrawPolyPolygon(aMask); |
1096 | 0 | return; |
1097 | 0 | } |
1098 | | |
1099 | 0 | impBufferDevice aBufferDevice(*mpOutputDevice, aMaskRect); |
1100 | |
|
1101 | 0 | if (!aBufferDevice.isVisible()) |
1102 | 0 | return; |
1103 | | |
1104 | | // remember last OutDev and set to content |
1105 | 0 | OutputDevice* pLastOutputDevice = mpOutputDevice; |
1106 | 0 | mpOutputDevice = &aBufferDevice.getContent(); |
1107 | |
|
1108 | 0 | Wallpaper aWallpaper(aTileImage); |
1109 | 0 | aWallpaper.SetColor(COL_TRANSPARENT); |
1110 | 0 | Point aPaperPt(aMaskRect.getX() % nTileWidth, aMaskRect.getY() % nTileHeight); |
1111 | 0 | tools::Rectangle aPaperRect(aPaperPt, aTileImage.GetSizePixel()); |
1112 | 0 | aWallpaper.SetRect(aPaperRect); |
1113 | 0 | mpOutputDevice->DrawWallpaper(aMaskRect, aWallpaper); |
1114 | | |
1115 | | // back to old OutDev |
1116 | 0 | mpOutputDevice = pLastOutputDevice; |
1117 | | |
1118 | | // draw mask |
1119 | 0 | VirtualDevice& rMask = aBufferDevice.getTransparence(); |
1120 | 0 | rMask.SetLineColor(); |
1121 | 0 | rMask.SetFillColor(COL_BLACK); |
1122 | 0 | rMask.DrawPolyPolygon(aMask); |
1123 | | |
1124 | | // dump buffer to outdev |
1125 | 0 | aBufferDevice.paint(); |
1126 | 0 | } |
1127 | | |
1128 | | } // end of namespace |
1129 | | |
1130 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |