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