Bug Summary

File:home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx
Warning:line 163, column 36
Division by zero

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name gdiimpl.cxx -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -mframe-pointer=all -fmath-errno -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib64/clang/11.0.0 -isystem /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/glm -isystem /usr/include/glib-2.0 -isystem /usr/lib64/glib-2.0/include -isystem /usr/include/libmount -isystem /usr/include/blkid -isystem /usr/include/cairo -isystem /usr/include/glib-2.0 -isystem /usr/lib64/glib-2.0/include -isystem /usr/include/pixman-1 -isystem /usr/include/freetype2 -isystem /usr/include/libpng16 -isystem /usr/include/libxml2 -isystem /usr/include/freetype2 -isystem /usr/include/libpng16 -isystem /usr/include/dbus-1.0 -isystem /usr/lib64/dbus-1.0/include -isystem /usr/include/freetype2 -isystem /usr/include/libpng16 -D BOOST_ERROR_CODE_HEADER_ONLY -D BOOST_SYSTEM_NO_DEPRECATED -D CPPU_ENV=gcc3 -D LINUX -D OSL_DEBUG_LEVEL=1 -D SAL_LOG_INFO -D SAL_LOG_WARN -D UNIX -D UNX -D X86_64 -D _PTHREADS -D _REENTRANT -D VCL_DLLIMPLEMENTATION -D DLLIMPLEMENTATION_UITEST -D CUI_DLL_NAME="libcuilo.so" -D DESKTOP_DETECTOR_DLL_NAME="libdesktop_detectorlo.so" -D TK_DLL_NAME="libtklo.so" -D SYSTEM_ZLIB -D GLM_FORCE_CTOR_INIT -D SK_USER_CONFIG_HEADER=</home/maarten/src/libreoffice/core/config_host/config_skia.h> -D SKIA_DLL -D ENABLE_CUPS -D HAVE_VALGRIND_HEADERS -D EXCEPTIONS_ON -D LIBO_INTERNAL_ONLY -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/epoxy/include -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/skia/include/core -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/skia/include/effects -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/skia/include/gpu -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/skia/include/config -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/skia/include/ports -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/skia/include/third_party/vulkan -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/skia/tools/gpu -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/skia -I /home/maarten/src/libreoffice/core/external/skia/inc/ -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/mdds/include -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/lcms2/include -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/icu/source -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/icu/source/i18n -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/icu/source/common -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/harfbuzz/src -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/graphite/include -I /home/maarten/src/libreoffice/core/external/boost/include -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/boost -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/pdfium -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/pdfium/public -D COMPONENT_BUILD -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/libpng -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/libjpeg-turbo -I /home/maarten/src/libreoffice/core/include -I /usr/lib/jvm/java-11-openjdk-11.0.9.10-0.0.ea.fc33.x86_64/include -I /usr/lib/jvm/java-11-openjdk-11.0.9.10-0.0.ea.fc33.x86_64/include/linux -I /home/maarten/src/libreoffice/core/config_host -I /home/maarten/src/libreoffice/core/vcl/inc -I /home/maarten/src/libreoffice/core/workdir/UnoApiHeadersTarget/udkapi/normal -I /home/maarten/src/libreoffice/core/workdir/UnoApiHeadersTarget/offapi/normal -I /home/maarten/src/libreoffice/core/workdir/CustomTarget/officecfg/registry -I /usr/include/freetype2 -I /usr/include/libpng16 -I /usr/include/libxml2 -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/10/../../../../include/c++/10 -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/10/../../../../include/c++/10/x86_64-redhat-linux -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/10/../../../../include/c++/10/backward -internal-isystem /usr/local/include -internal-isystem /usr/lib64/clang/11.0.0/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -O0 -Wno-missing-braces -std=c++17 -fdeprecated-macro -fdebug-compilation-dir /home/maarten/src/libreoffice/core -ferror-limit 19 -fvisibility hidden -fvisibility-inlines-hidden -stack-protector 2 -fgnuc-version=4.2.1 -fcxx-exceptions -fexceptions -debug-info-kind=constructor -analyzer-output=html -faddrsig -o /home/maarten/tmp/wis/scan-build-libreoffice/output/report/2020-10-07-141433-9725-1 -x c++ /home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx
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 <skia/gdiimpl.hxx>
21
22#include <salgdi.hxx>
23#include <skia/salbmp.hxx>
24#include <vcl/idle.hxx>
25#include <vcl/svapp.hxx>
26#include <vcl/lazydelete.hxx>
27#include <vcl/gradient.hxx>
28#include <vcl/skia/SkiaHelper.hxx>
29#include <skia/utils.hxx>
30#include <skia/zone.hxx>
31
32#include <SkCanvas.h>
33#include <SkGradientShader.h>
34#include <SkPath.h>
35#include <SkRegion.h>
36#include <SkDashPathEffect.h>
37#include <GrBackendSurface.h>
38#include <SkTextBlob.h>
39#include <SkRSXform.h>
40
41#include <numeric>
42#include <basegfx/polygon/b2dpolygontools.hxx>
43#include <basegfx/polygon/b2dpolypolygontools.hxx>
44#include <basegfx/polygon/b2dpolypolygoncutter.hxx>
45#include <o3tl/sorted_vector.hxx>
46
47namespace
48{
49// Create Skia Path from B2DPolygon
50// Note that polygons generally have the complication that when used
51// for area (fill) operations they usually miss the right-most and
52// bottom-most line of pixels of the bounding rectangle (see
53// https://lists.freedesktop.org/archives/libreoffice/2019-November/083709.html).
54// So be careful with rectangle->polygon conversions (generally avoid them).
55void addPolygonToPath(const basegfx::B2DPolygon& rPolygon, SkPath& rPath)
56{
57 const sal_uInt32 nPointCount(rPolygon.count());
58
59 if (nPointCount <= 1)
60 return;
61
62 const bool bClosePath(rPolygon.isClosed());
63 const bool bHasCurves(rPolygon.areControlPointsUsed());
64
65 bool bFirst = true;
66
67 sal_uInt32 nCurrentIndex = 0;
68 sal_uInt32 nPreviousIndex = nPointCount - 1;
69
70 basegfx::B2DPoint aCurrentPoint;
71 basegfx::B2DPoint aPreviousPoint;
72
73 for (sal_uInt32 nIndex = 0; nIndex <= nPointCount; nIndex++)
74 {
75 if (nIndex == nPointCount && !bClosePath)
76 continue;
77
78 // Make sure we loop the last point to first point
79 nCurrentIndex = nIndex % nPointCount;
80 aCurrentPoint = rPolygon.getB2DPoint(nCurrentIndex);
81
82 if (bFirst)
83 {
84 rPath.moveTo(aCurrentPoint.getX(), aCurrentPoint.getY());
85 bFirst = false;
86 }
87 else if (!bHasCurves)
88 {
89 rPath.lineTo(aCurrentPoint.getX(), aCurrentPoint.getY());
90 }
91 else
92 {
93 basegfx::B2DPoint aPreviousControlPoint = rPolygon.getNextControlPoint(nPreviousIndex);
94 basegfx::B2DPoint aCurrentControlPoint = rPolygon.getPrevControlPoint(nCurrentIndex);
95
96 if (aPreviousControlPoint.equal(aPreviousPoint)
97 && aCurrentControlPoint.equal(aCurrentPoint))
98 rPath.lineTo(aCurrentPoint.getX(), aCurrentPoint.getY()); // a straight line
99 else
100 {
101 if (aPreviousControlPoint.equal(aPreviousPoint))
102 {
103 aPreviousControlPoint
104 = aPreviousPoint + ((aPreviousControlPoint - aCurrentPoint) * 0.0005);
105 }
106 if (aCurrentControlPoint.equal(aCurrentPoint))
107 {
108 aCurrentControlPoint
109 = aCurrentPoint + ((aCurrentControlPoint - aPreviousPoint) * 0.0005);
110 }
111 rPath.cubicTo(aPreviousControlPoint.getX(), aPreviousControlPoint.getY(),
112 aCurrentControlPoint.getX(), aCurrentControlPoint.getY(),
113 aCurrentPoint.getX(), aCurrentPoint.getY());
114 }
115 }
116 aPreviousPoint = aCurrentPoint;
117 nPreviousIndex = nCurrentIndex;
118 }
119 if (bClosePath)
120 {
121 rPath.close();
122 }
123}
124
125void addPolyPolygonToPath(const basegfx::B2DPolyPolygon& rPolyPolygon, SkPath& rPath)
126{
127 const sal_uInt32 nPolygonCount(rPolyPolygon.count());
128
129 if (nPolygonCount == 0)
130 return;
131
132 for (const auto& rPolygon : rPolyPolygon)
133 {
134 addPolygonToPath(rPolygon, rPath);
135 }
136}
137
138// Check if the given polygon contains a straight line. If not, it consists
139// solely of curves.
140bool polygonContainsLine(const basegfx::B2DPolyPolygon& rPolyPolygon)
141{
142 if (!rPolyPolygon.areControlPointsUsed())
20
Assuming the condition is false
21
Taking false branch
143 return true; // no curves at all
144 for (const auto& rPolygon : rPolyPolygon)
22
Assuming '__begin1' is not equal to '__end1'
145 {
146 const sal_uInt32 nPointCount(rPolygon.count());
23
'nPointCount' initialized here
147 bool bFirst = true;
148
149 const bool bClosePath(rPolygon.isClosed());
150
151 sal_uInt32 nCurrentIndex = 0;
152 sal_uInt32 nPreviousIndex = nPointCount - 1;
153
154 basegfx::B2DPoint aCurrentPoint;
155 basegfx::B2DPoint aPreviousPoint;
156
157 for (sal_uInt32 nIndex = 0; nIndex
23.1
'nIndex' is <= 'nPointCount'
<= nPointCount; nIndex++)
24
Loop condition is true. Entering loop body
158 {
159 if (nIndex == nPointCount && !bClosePath)
25
Assuming 'nIndex' is equal to 'nPointCount'
26
Assuming 'bClosePath' is true
27
Taking false branch
160 continue;
161
162 // Make sure we loop the last point to first point
163 nCurrentIndex = nIndex % nPointCount;
28
Division by zero
164 if (bFirst)
165 bFirst = false;
166 else
167 {
168 basegfx::B2DPoint aPreviousControlPoint
169 = rPolygon.getNextControlPoint(nPreviousIndex);
170 basegfx::B2DPoint aCurrentControlPoint
171 = rPolygon.getPrevControlPoint(nCurrentIndex);
172
173 if (aPreviousControlPoint.equal(aPreviousPoint)
174 && aCurrentControlPoint.equal(aCurrentPoint))
175 {
176 return true; // found a straight line
177 }
178 }
179 aPreviousPoint = aCurrentPoint;
180 nPreviousIndex = nCurrentIndex;
181 }
182 }
183 return false; // no straight line found
184}
185
186SkColor toSkColor(Color color)
187{
188 return SkColorSetARGB(255 - color.GetTransparency(), color.GetRed(), color.GetGreen(),
189 color.GetBlue());
190}
191
192SkColor toSkColorWithTransparency(Color aColor, double fTransparency)
193{
194 return SkColorSetA(toSkColor(aColor), 255 * (1.0 - fTransparency));
195}
196
197SkColor toSkColorWithIntensity(Color color, int intensity)
198{
199 return SkColorSetARGB(255 - color.GetTransparency(), color.GetRed() * intensity / 100,
200 color.GetGreen() * intensity / 100, color.GetBlue() * intensity / 100);
201}
202
203Color fromSkColor(SkColor color)
204{
205 return Color(255 - SkColorGetA(color)(((color) >> 24) & 0xFF), SkColorGetR(color)(((color) >> 16) & 0xFF), SkColorGetG(color)(((color) >> 8) & 0xFF),
206 SkColorGetB(color)(((color) >> 0) & 0xFF));
207}
208
209// returns true if the source or destination rectangles are invalid
210bool checkInvalidSourceOrDestination(SalTwoRect const& rPosAry)
211{
212 return rPosAry.mnSrcWidth <= 0 || rPosAry.mnSrcHeight <= 0 || rPosAry.mnDestWidth <= 0
213 || rPosAry.mnDestHeight <= 0;
214}
215
216} // end anonymous namespace
217
218// Class that triggers flushing the backing buffer when idle.
219class SkiaFlushIdle : public Idle
220{
221 SkiaSalGraphicsImpl* mpGraphics;
222#ifndef NDEBUG
223 char* debugname;
224#endif
225
226public:
227 explicit SkiaFlushIdle(SkiaSalGraphicsImpl* pGraphics)
228 : Idle(get_debug_name(pGraphics))
229 , mpGraphics(pGraphics)
230 {
231 // We don't want to be swapping before we've painted.
232 SetPriority(TaskPriority::POST_PAINT);
233 }
234#ifndef NDEBUG
235 virtual ~SkiaFlushIdle() { free(debugname); }
236#endif
237 const char* get_debug_name(SkiaSalGraphicsImpl* pGraphics)
238 {
239#ifndef NDEBUG
240 // Idle keeps just a pointer, so we need to store the string
241 debugname = strdup(
242 OString("skia idle 0x" + OString::number(reinterpret_cast<sal_uIntPtr>(pGraphics), 16))
243 .getStr());
244 return debugname;
245#else
246 (void)pGraphics;
247 return "skia idle";
248#endif
249 }
250
251 virtual void Invoke() override
252 {
253 mpGraphics->performFlush();
254 Stop();
255 SetPriority(TaskPriority::HIGHEST);
256 }
257};
258
259SkiaSalGraphicsImpl::SkiaSalGraphicsImpl(SalGraphics& rParent, SalGeometryProvider* pProvider)
260 : mParent(rParent)
261 , mProvider(pProvider)
262 , mIsGPU(false)
263 , mLineColor(SALCOLOR_NONE)
264 , mFillColor(SALCOLOR_NONE)
265 , mXorMode(false)
266 , mFlush(new SkiaFlushIdle(this))
267 , mPendingOperationsToFlush(0)
268{
269}
270
271SkiaSalGraphicsImpl::~SkiaSalGraphicsImpl()
272{
273 assert(!mSurface)(static_cast <bool> (!mSurface) ? void (0) : __assert_fail
("!mSurface", "/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
, 273, __extension__ __PRETTY_FUNCTION__))
;
274 assert(!mWindowContext)(static_cast <bool> (!mWindowContext) ? void (0) : __assert_fail
("!mWindowContext", "/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
, 274, __extension__ __PRETTY_FUNCTION__))
;
275}
276
277void SkiaSalGraphicsImpl::Init() {}
278
279void SkiaSalGraphicsImpl::recreateSurface()
280{
281 destroySurface();
282 createSurface();
283}
284
285void SkiaSalGraphicsImpl::createSurface()
286{
287 SkiaZone zone;
288 if (isOffscreen())
289 createOffscreenSurface();
290 else
291 createWindowSurface();
292 mSurface->getCanvas()->save(); // see SetClipRegion()
293 mClipRegion = vcl::Region(tools::Rectangle(0, 0, GetWidth(), GetHeight()));
294
295 // We don't want to be swapping before we've painted.
296 mFlush->Stop();
297 mFlush->SetPriority(TaskPriority::POST_PAINT);
298}
299
300void SkiaSalGraphicsImpl::createWindowSurface(bool forceRaster)
301{
302 SkiaZone zone;
303 assert(!isOffscreen())(static_cast <bool> (!isOffscreen()) ? void (0) : __assert_fail
("!isOffscreen()", "/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
, 303, __extension__ __PRETTY_FUNCTION__))
;
304 assert(!mSurface)(static_cast <bool> (!mSurface) ? void (0) : __assert_fail
("!mSurface", "/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
, 304, __extension__ __PRETTY_FUNCTION__))
;
305 assert(!mWindowContext)(static_cast <bool> (!mWindowContext) ? void (0) : __assert_fail
("!mWindowContext", "/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
, 305, __extension__ __PRETTY_FUNCTION__))
;
306 createWindowContext(forceRaster);
307 if (mWindowContext)
308 mSurface = mWindowContext->getBackbufferSurface();
309 if (!mSurface)
310 {
311 switch (SkiaHelper::renderMethodToUse())
312 {
313 case SkiaHelper::RenderVulkan:
314 SAL_WARN("vcl.skia",do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_WARN
, "vcl.skia")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break; case
SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "cannot create Vulkan GPU window surface, falling back to Raster"
) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("vcl.skia"
), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "315" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "cannot create Vulkan GPU window surface, falling back to Raster"
), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "cannot create Vulkan GPU window surface, falling back to Raster"
; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("vcl.skia"
), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "315" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "cannot create Vulkan GPU window surface, falling back to Raster"
) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("vcl.skia"
), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "315" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "cannot create Vulkan GPU window surface, falling back to Raster"
), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "cannot create Vulkan GPU window surface, falling back to Raster"
; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("vcl.skia"
), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "315" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
315 "cannot create Vulkan GPU window surface, falling back to Raster")do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_WARN
, "vcl.skia")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break; case
SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "cannot create Vulkan GPU window surface, falling back to Raster"
) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("vcl.skia"
), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "315" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "cannot create Vulkan GPU window surface, falling back to Raster"
), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "cannot create Vulkan GPU window surface, falling back to Raster"
; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("vcl.skia"
), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "315" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "cannot create Vulkan GPU window surface, falling back to Raster"
) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("vcl.skia"
), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "315" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "cannot create Vulkan GPU window surface, falling back to Raster"
), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "cannot create Vulkan GPU window surface, falling back to Raster"
; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("vcl.skia"
), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "315" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
;
316 destroySurface(); // destroys also WindowContext
317 return createWindowSurface(true); // try again
318 case SkiaHelper::RenderRaster:
319 abort(); // This should not really happen, do not even try to cope with it.
320 }
321 }
322 mIsGPU = mSurface->getCanvas()->getGrContext() != nullptr;
323#ifdef DBG_UTIL
324 SkiaHelper::prefillSurface(mSurface);
325#endif
326}
327
328void SkiaSalGraphicsImpl::createOffscreenSurface()
329{
330 SkiaZone zone;
331 assert(isOffscreen())(static_cast <bool> (isOffscreen()) ? void (0) : __assert_fail
("isOffscreen()", "/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
, 331, __extension__ __PRETTY_FUNCTION__))
;
332 assert(!mSurface)(static_cast <bool> (!mSurface) ? void (0) : __assert_fail
("!mSurface", "/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
, 332, __extension__ __PRETTY_FUNCTION__))
;
333 assert(!mWindowContext)(static_cast <bool> (!mWindowContext) ? void (0) : __assert_fail
("!mWindowContext", "/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
, 333, __extension__ __PRETTY_FUNCTION__))
;
334 // When created (especially on Windows), Init() gets called with size (0,0), which is invalid size
335 // for Skia. May happen also in rare cases such as shutting down (tdf#131939).
336 int width = std::max(1, GetWidth());
337 int height = std::max(1, GetHeight());
338 switch (SkiaHelper::renderMethodToUse())
339 {
340 case SkiaHelper::RenderVulkan:
341 {
342 if (SkiaHelper::getSharedGrDirectContext())
343 {
344 mSurface = SkiaHelper::createSkSurface(width, height);
345 if (mSurface)
346 {
347 mIsGPU = mSurface->getCanvas()->getGrContext() != nullptr;
348 return;
349 }
350 }
351 break;
352 }
353 default:
354 break;
355 }
356 // Create raster surface as a fallback.
357 mSurface = SkiaHelper::createSkSurface(width, height);
358 assert(mSurface)(static_cast <bool> (mSurface) ? void (0) : __assert_fail
("mSurface", "/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
, 358, __extension__ __PRETTY_FUNCTION__))
;
359 assert(!mSurface->getCanvas()->getGrContext())(static_cast <bool> (!mSurface->getCanvas()->getGrContext
()) ? void (0) : __assert_fail ("!mSurface->getCanvas()->getGrContext()"
, "/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx", 359
, __extension__ __PRETTY_FUNCTION__))
; // is not GPU-backed
360 mIsGPU = false;
361}
362
363void SkiaSalGraphicsImpl::destroySurface()
364{
365 SkiaZone zone;
366 if (mSurface)
367 {
368 // check setClipRegion() invariant
369 assert(mSurface->getCanvas()->getSaveCount() == 2)(static_cast <bool> (mSurface->getCanvas()->getSaveCount
() == 2) ? void (0) : __assert_fail ("mSurface->getCanvas()->getSaveCount() == 2"
, "/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx", 369
, __extension__ __PRETTY_FUNCTION__))
;
370 // if this fails, something forgot to use SkAutoCanvasRestore
371 assert(mSurface->getCanvas()->getTotalMatrix().isIdentity())(static_cast <bool> (mSurface->getCanvas()->getTotalMatrix
().isIdentity()) ? void (0) : __assert_fail ("mSurface->getCanvas()->getTotalMatrix().isIdentity()"
, "/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx", 371
, __extension__ __PRETTY_FUNCTION__))
;
372 }
373 // If we use e.g. Vulkan, we must destroy the surface before the context,
374 // otherwise destroying the surface will reference the context. This is
375 // handled by calling destroySurface() before destroying the context.
376 // However we also need to flush the surface before destroying it,
377 // otherwise when destroying the context later there still could be queued
378 // commands referring to the surface data. This is probably a Skia bug,
379 // but work around it here.
380 if (mSurface)
381 mSurface->flushAndSubmit();
382 mSurface.reset();
383 mWindowContext.reset();
384 mIsGPU = false;
385}
386
387void SkiaSalGraphicsImpl::DeInit() { destroySurface(); }
388
389void SkiaSalGraphicsImpl::preDraw()
390{
391 assert(comphelper::SolarMutex::get()->IsCurrentThread())(static_cast <bool> (comphelper::SolarMutex::get()->
IsCurrentThread()) ? void (0) : __assert_fail ("comphelper::SolarMutex::get()->IsCurrentThread()"
, "/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx", 391
, __extension__ __PRETTY_FUNCTION__))
;
392 SkiaZone::enter(); // matched in postDraw()
393 checkSurface();
394 checkPendingDrawing();
395}
396
397void SkiaSalGraphicsImpl::postDraw()
398{
399 scheduleFlush();
400 // Skia (at least when using Vulkan) queues drawing commands and executes them only later.
401 // But tdf#136369 leads to creating and queueing many tiny bitmaps, which makes
402 // Skia slow, and may make it even run out of memory. So force a flush if such
403 // a problematic operation has been performed too many times without a flush.
404 if (mPendingOperationsToFlush > 1000)
405 {
406 mSurface->flushAndSubmit();
407 mPendingOperationsToFlush = 0;
408 }
409 SkiaZone::leave(); // matched in preDraw()
410 // If there's a problem with the GPU context, abort.
411 if (GrContext* context = mSurface->getCanvas()->getGrContext())
412 {
413 // Running out of memory on the GPU technically could be possibly recoverable,
414 // but we don't know the exact status of the surface (and what has or has not been drawn to it),
415 // so in practice this is unrecoverable without possible data loss.
416 if (context->oomed())
417 {
418 SAL_WARN("vcl.skia", "GPU context has run out of memory, aborting.")do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_WARN
, "vcl.skia")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break; case
SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "GPU context has run out of memory, aborting."
) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("vcl.skia"
), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "418" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "GPU context has run out of memory, aborting."
), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "GPU context has run out of memory, aborting."; ::sal
::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("vcl.skia"), (
"/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx" ":"
"418" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "GPU context has run out of memory, aborting.") ==
1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("vcl.skia"
), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "418" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "GPU context has run out of memory, aborting."
), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "GPU context has run out of memory, aborting."; ::sal
::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("vcl.skia"), (
"/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx" ":"
"418" ": "), sal_detail_stream, 0); }; std::abort(); break; }
} } while (false)
;
419 abort();
420 }
421 // Unrecoverable problem.
422 if (context->abandoned())
423 {
424 SAL_WARN("vcl.skia", "GPU context has been abandoned, aborting.")do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_WARN
, "vcl.skia")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break; case
SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "GPU context has been abandoned, aborting."
) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("vcl.skia"
), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "424" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "GPU context has been abandoned, aborting."
), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "GPU context has been abandoned, aborting."; ::sal::
detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("vcl.skia"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "424" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "GPU context has been abandoned, aborting.") == 1
) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("vcl.skia"
), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "424" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "GPU context has been abandoned, aborting."
), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "GPU context has been abandoned, aborting."; ::sal::
detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("vcl.skia"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "424" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
;
425 abort();
426 }
427 }
428}
429
430void SkiaSalGraphicsImpl::scheduleFlush()
431{
432 if (!isOffscreen())
433 {
434 if (!Application::IsInExecute())
435 performFlush(); // otherwise nothing would trigger idle rendering
436 else if (!mFlush->IsActive())
437 mFlush->Start();
438 }
439}
440
441// VCL can sometimes resize us without telling us, update the surface if needed.
442// Also create the surface on demand if it has not been created yet (it is a waste
443// to create it in Init() if it gets recreated later anyway).
444void SkiaSalGraphicsImpl::checkSurface()
445{
446 if (!mSurface)
447 {
448 createSurface();
449 SAL_INFO("vcl.skia.trace",do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_INFO
, "vcl.skia.trace")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break
; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "create(" << this
<< "): " << Size(mSurface->width(), mSurface->
height())) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "450" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "create(" << this << "): "
<< Size(mSurface->width(), mSurface->height())),
0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "create(" << this << "): " << Size
(mSurface->width(), mSurface->height()); ::sal::detail::
log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "450" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "create(" << this << "): " << Size
(mSurface->width(), mSurface->height())) == 1) { ::sal_detail_log
( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "450" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "create(" << this << "): "
<< Size(mSurface->width(), mSurface->height())),
0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "create(" << this << "): " << Size
(mSurface->width(), mSurface->height()); ::sal::detail::
log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "450" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
450 "create(" << this << "): " << Size(mSurface->width(), mSurface->height()))do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_INFO
, "vcl.skia.trace")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break
; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "create(" << this
<< "): " << Size(mSurface->width(), mSurface->
height())) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "450" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "create(" << this << "): "
<< Size(mSurface->width(), mSurface->height())),
0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "create(" << this << "): " << Size
(mSurface->width(), mSurface->height()); ::sal::detail::
log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "450" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "create(" << this << "): " << Size
(mSurface->width(), mSurface->height())) == 1) { ::sal_detail_log
( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "450" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "create(" << this << "): "
<< Size(mSurface->width(), mSurface->height())),
0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "create(" << this << "): " << Size
(mSurface->width(), mSurface->height()); ::sal::detail::
log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "450" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
;
451 }
452 else if (GetWidth() != mSurface->width() || GetHeight() != mSurface->height())
453 {
454 if (!avoidRecreateByResize())
455 {
456 Size oldSize(mSurface->width(), mSurface->height());
457 // Recreating a surface means that the old SkSurface contents will be lost.
458 // But if a window has been resized the windowing system may send repaint events
459 // only for changed parts and VCL would not repaint the whole area, assuming
460 // that some parts have not changed (this is what seems to cause tdf#131952).
461 // So carry over the old contents for windows, even though generally everything
462 // will be usually repainted anyway.
463 sk_sp<SkImage> snapshot;
464 if (!isOffscreen())
465 {
466 flushDrawing();
467 snapshot = SkiaHelper::makeCheckedImageSnapshot(mSurface);
468 }
469 recreateSurface();
470 if (snapshot)
471 {
472 SkPaint paint;
473 paint.setBlendMode(SkBlendMode::kSrc); // copy as is
474 mSurface->getCanvas()->drawImage(snapshot, 0, 0, &paint);
475 }
476 SAL_INFO("vcl.skia.trace", "recreate(" << this << "): old " << oldSize << " new "do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_INFO
, "vcl.skia.trace")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break
; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "recreate(" << this
<< "): old " << oldSize << " new " <<
Size(mSurface->width(), mSurface->height()) << " requested "
<< Size(GetWidth(), GetHeight())) == 1) { ::sal_detail_log
( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "479" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "recreate(" << this << "): old "
<< oldSize << " new " << Size(mSurface->
width(), mSurface->height()) << " requested " <<
Size(GetWidth(), GetHeight())), 0); } else { ::std::ostringstream
sal_detail_stream; sal_detail_stream << "recreate(" <<
this << "): old " << oldSize << " new " <<
Size(mSurface->width(), mSurface->height()) << " requested "
<< Size(GetWidth(), GetHeight()); ::sal::detail::log( (
::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "479" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "recreate(" << this << "): old " <<
oldSize << " new " << Size(mSurface->width(),
mSurface->height()) << " requested " << Size(
GetWidth(), GetHeight())) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "479" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "recreate(" << this << "): old "
<< oldSize << " new " << Size(mSurface->
width(), mSurface->height()) << " requested " <<
Size(GetWidth(), GetHeight())), 0); } else { ::std::ostringstream
sal_detail_stream; sal_detail_stream << "recreate(" <<
this << "): old " << oldSize << " new " <<
Size(mSurface->width(), mSurface->height()) << " requested "
<< Size(GetWidth(), GetHeight()); ::sal::detail::log( (
::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "479" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
477 << Size(mSurface->width(), mSurface->height())do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_INFO
, "vcl.skia.trace")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break
; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "recreate(" << this
<< "): old " << oldSize << " new " <<
Size(mSurface->width(), mSurface->height()) << " requested "
<< Size(GetWidth(), GetHeight())) == 1) { ::sal_detail_log
( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "479" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "recreate(" << this << "): old "
<< oldSize << " new " << Size(mSurface->
width(), mSurface->height()) << " requested " <<
Size(GetWidth(), GetHeight())), 0); } else { ::std::ostringstream
sal_detail_stream; sal_detail_stream << "recreate(" <<
this << "): old " << oldSize << " new " <<
Size(mSurface->width(), mSurface->height()) << " requested "
<< Size(GetWidth(), GetHeight()); ::sal::detail::log( (
::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "479" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "recreate(" << this << "): old " <<
oldSize << " new " << Size(mSurface->width(),
mSurface->height()) << " requested " << Size(
GetWidth(), GetHeight())) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "479" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "recreate(" << this << "): old "
<< oldSize << " new " << Size(mSurface->
width(), mSurface->height()) << " requested " <<
Size(GetWidth(), GetHeight())), 0); } else { ::std::ostringstream
sal_detail_stream; sal_detail_stream << "recreate(" <<
this << "): old " << oldSize << " new " <<
Size(mSurface->width(), mSurface->height()) << " requested "
<< Size(GetWidth(), GetHeight()); ::sal::detail::log( (
::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "479" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
478 << " requested "do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_INFO
, "vcl.skia.trace")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break
; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "recreate(" << this
<< "): old " << oldSize << " new " <<
Size(mSurface->width(), mSurface->height()) << " requested "
<< Size(GetWidth(), GetHeight())) == 1) { ::sal_detail_log
( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "479" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "recreate(" << this << "): old "
<< oldSize << " new " << Size(mSurface->
width(), mSurface->height()) << " requested " <<
Size(GetWidth(), GetHeight())), 0); } else { ::std::ostringstream
sal_detail_stream; sal_detail_stream << "recreate(" <<
this << "): old " << oldSize << " new " <<
Size(mSurface->width(), mSurface->height()) << " requested "
<< Size(GetWidth(), GetHeight()); ::sal::detail::log( (
::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "479" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "recreate(" << this << "): old " <<
oldSize << " new " << Size(mSurface->width(),
mSurface->height()) << " requested " << Size(
GetWidth(), GetHeight())) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "479" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "recreate(" << this << "): old "
<< oldSize << " new " << Size(mSurface->
width(), mSurface->height()) << " requested " <<
Size(GetWidth(), GetHeight())), 0); } else { ::std::ostringstream
sal_detail_stream; sal_detail_stream << "recreate(" <<
this << "): old " << oldSize << " new " <<
Size(mSurface->width(), mSurface->height()) << " requested "
<< Size(GetWidth(), GetHeight()); ::sal::detail::log( (
::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "479" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
479 << Size(GetWidth(), GetHeight()))do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_INFO
, "vcl.skia.trace")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break
; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "recreate(" << this
<< "): old " << oldSize << " new " <<
Size(mSurface->width(), mSurface->height()) << " requested "
<< Size(GetWidth(), GetHeight())) == 1) { ::sal_detail_log
( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "479" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "recreate(" << this << "): old "
<< oldSize << " new " << Size(mSurface->
width(), mSurface->height()) << " requested " <<
Size(GetWidth(), GetHeight())), 0); } else { ::std::ostringstream
sal_detail_stream; sal_detail_stream << "recreate(" <<
this << "): old " << oldSize << " new " <<
Size(mSurface->width(), mSurface->height()) << " requested "
<< Size(GetWidth(), GetHeight()); ::sal::detail::log( (
::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "479" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "recreate(" << this << "): old " <<
oldSize << " new " << Size(mSurface->width(),
mSurface->height()) << " requested " << Size(
GetWidth(), GetHeight())) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "479" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "recreate(" << this << "): old "
<< oldSize << " new " << Size(mSurface->
width(), mSurface->height()) << " requested " <<
Size(GetWidth(), GetHeight())), 0); } else { ::std::ostringstream
sal_detail_stream; sal_detail_stream << "recreate(" <<
this << "): old " << oldSize << " new " <<
Size(mSurface->width(), mSurface->height()) << " requested "
<< Size(GetWidth(), GetHeight()); ::sal::detail::log( (
::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "479" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
;
480 }
481 }
482}
483
484void SkiaSalGraphicsImpl::flushDrawing()
485{
486 if (!mSurface)
487 return;
488 checkPendingDrawing();
489 if (mXorMode)
490 applyXor();
491 mSurface->flushAndSubmit();
492 mPendingOperationsToFlush = 0;
493}
494
495bool SkiaSalGraphicsImpl::setClipRegion(const vcl::Region& region)
496{
497 if (mClipRegion == region)
498 return true;
499 SkiaZone zone;
500 checkPendingDrawing();
501 checkSurface();
502 mClipRegion = region;
503 SAL_INFO("vcl.skia.trace", "setclipregion(" << this << "): " << region)do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_INFO
, "vcl.skia.trace")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break
; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "setclipregion(" <<
this << "): " << region) == 1) { ::sal_detail_log
( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "503" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "setclipregion(" << this <<
"): " << region), 0); } else { ::std::ostringstream sal_detail_stream
; sal_detail_stream << "setclipregion(" << this <<
"): " << region; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "503" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "setclipregion(" << this << "): " <<
region) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "503" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "setclipregion(" << this <<
"): " << region), 0); } else { ::std::ostringstream sal_detail_stream
; sal_detail_stream << "setclipregion(" << this <<
"): " << region; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "503" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
;
504 SkCanvas* canvas = mSurface->getCanvas();
505 // SkCanvas::clipRegion() can only further reduce the clip region,
506 // but we need to set the given region, which may extend it.
507 // So handle that by always having the full clip region saved on the stack
508 // and always go back to that. SkCanvas::restore() only affects the clip
509 // and the matrix.
510 assert(canvas->getSaveCount() == 2)(static_cast <bool> (canvas->getSaveCount() == 2) ? void
(0) : __assert_fail ("canvas->getSaveCount() == 2", "/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
, 510, __extension__ __PRETTY_FUNCTION__))
; // = there is just one save()
511 canvas->restore();
512 canvas->save();
513 setCanvasClipRegion(canvas, region);
514 return true;
515}
516
517void SkiaSalGraphicsImpl::setCanvasClipRegion(SkCanvas* canvas, const vcl::Region& region)
518{
519 SkiaZone zone;
520 SkPath path;
521 // Always use region rectangles, regardless of what the region uses internally.
522 // That's what other VCL backends do, and trying to use addPolyPolygonToPath()
523 // in case a polygon is used leads to off-by-one errors such as tdf#133208.
524 RectangleVector rectangles;
525 region.GetRegionRectangles(rectangles);
526 for (const tools::Rectangle& rectangle : rectangles)
527 path.addRect(SkRect::MakeXYWH(rectangle.getX(), rectangle.getY(), rectangle.GetWidth(),
528 rectangle.GetHeight()));
529 path.setFillType(SkPathFillType::kEvenOdd);
530 canvas->clipPath(path);
531}
532
533void SkiaSalGraphicsImpl::ResetClipRegion()
534{
535 setClipRegion(vcl::Region(tools::Rectangle(0, 0, GetWidth(), GetHeight())));
536}
537
538const vcl::Region& SkiaSalGraphicsImpl::getClipRegion() const { return mClipRegion; }
539
540sal_uInt16 SkiaSalGraphicsImpl::GetBitCount() const { return 32; }
541
542long SkiaSalGraphicsImpl::GetGraphicsWidth() const { return GetWidth(); }
543
544void SkiaSalGraphicsImpl::SetLineColor()
545{
546 checkPendingDrawing();
547 mLineColor = SALCOLOR_NONE;
548}
549
550void SkiaSalGraphicsImpl::SetLineColor(Color nColor)
551{
552 checkPendingDrawing();
553 mLineColor = nColor;
554}
555
556void SkiaSalGraphicsImpl::SetFillColor()
557{
558 checkPendingDrawing();
559 mFillColor = SALCOLOR_NONE;
560}
561
562void SkiaSalGraphicsImpl::SetFillColor(Color nColor)
563{
564 checkPendingDrawing();
565 mFillColor = nColor;
566}
567
568void SkiaSalGraphicsImpl::SetXORMode(bool set, bool)
569{
570 if (mXorMode == set)
571 return;
572 checkPendingDrawing();
573 SAL_INFO("vcl.skia.trace", "setxormode(" << this << "): " << set)do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_INFO
, "vcl.skia.trace")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break
; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "setxormode(" <<
this << "): " << set) == 1) { ::sal_detail_log( (
::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "573" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "setxormode(" << this << "): "
<< set), 0); } else { ::std::ostringstream sal_detail_stream
; sal_detail_stream << "setxormode(" << this <<
"): " << set; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "573" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "setxormode(" << this << "): " <<
set) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO)
, ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "573" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "setxormode(" << this << "): "
<< set), 0); } else { ::std::ostringstream sal_detail_stream
; sal_detail_stream << "setxormode(" << this <<
"): " << set; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "573" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
;
574 if (set)
575 mXorRegion.setEmpty();
576 else
577 applyXor();
578 mXorMode = set;
579}
580
581SkCanvas* SkiaSalGraphicsImpl::getXorCanvas()
582{
583 SkiaZone zone;
584 assert(mXorMode)(static_cast <bool> (mXorMode) ? void (0) : __assert_fail
("mXorMode", "/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
, 584, __extension__ __PRETTY_FUNCTION__))
;
585 // Skia does not implement xor drawing, so we need to handle it manually by redirecting
586 // to a temporary SkBitmap and then doing the xor operation on the data ourselves.
587 // There's no point in using SkSurface for GPU, we'd immediately need to get the pixels back.
588 if (!mXorCanvas)
589 {
590 // Use unpremultiplied alpha (see xor applying in applyXor()).
591 if (!mXorBitmap.tryAllocPixels(mSurface->imageInfo().makeAlphaType(kUnpremul_SkAlphaType)))
592 abort();
593 mXorBitmap.eraseARGB(0, 0, 0, 0);
594 mXorCanvas = std::make_unique<SkCanvas>(mXorBitmap);
595 setCanvasClipRegion(mXorCanvas.get(), mClipRegion);
596 }
597 return mXorCanvas.get();
598}
599
600void SkiaSalGraphicsImpl::applyXor()
601{
602 // Apply the result from the temporary bitmap manually. This is indeed
603 // slow, but it doesn't seem to be needed often and is optimized
604 // in each operation by extending mXorRegion with the area that should be
605 // updated.
606 assert(mXorMode)(static_cast <bool> (mXorMode) ? void (0) : __assert_fail
("mXorMode", "/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
, 606, __extension__ __PRETTY_FUNCTION__))
;
607 if (!mSurface || !mXorCanvas
608 || !mXorRegion.op(SkIRect::MakeXYWH(0, 0, mSurface->width(), mSurface->height()),
609 SkRegion::kIntersect_Op))
610 {
611 mXorRegion.setEmpty();
612 return;
613 }
614 SAL_INFO("vcl.skia.trace", "applyxor(" << this << "): " << mXorRegion)do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_INFO
, "vcl.skia.trace")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break
; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "applyxor(" << this
<< "): " << mXorRegion) == 1) { ::sal_detail_log
( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "614" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "applyxor(" << this << "): "
<< mXorRegion), 0); } else { ::std::ostringstream sal_detail_stream
; sal_detail_stream << "applyxor(" << this <<
"): " << mXorRegion; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "614" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "applyxor(" << this << "): " <<
mXorRegion) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "614" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "applyxor(" << this << "): "
<< mXorRegion), 0); } else { ::std::ostringstream sal_detail_stream
; sal_detail_stream << "applyxor(" << this <<
"): " << mXorRegion; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "614" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
;
615 // Copy the surface contents to another pixmap.
616 SkBitmap surfaceBitmap;
617 // Use unpremultiplied alpha format, so that we do not have to do the conversions to get
618 // the RGB and back (Skia will do it when converting, but it'll be presumably faster at it).
619 if (!surfaceBitmap.tryAllocPixels(mSurface->imageInfo().makeAlphaType(kUnpremul_SkAlphaType)))
620 abort();
621 SkPaint paint;
622 paint.setBlendMode(SkBlendMode::kSrc); // copy as is
623 SkCanvas canvas(surfaceBitmap);
624 canvas.drawImageRect(SkiaHelper::makeCheckedImageSnapshot(mSurface), mXorRegion.getBounds(),
625 SkRect::Make(mXorRegion.getBounds()), &paint);
626 // xor to surfaceBitmap
627 assert(surfaceBitmap.info().alphaType() == kUnpremul_SkAlphaType)(static_cast <bool> (surfaceBitmap.info().alphaType() ==
kUnpremul_SkAlphaType) ? void (0) : __assert_fail ("surfaceBitmap.info().alphaType() == kUnpremul_SkAlphaType"
, "/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx", 627
, __extension__ __PRETTY_FUNCTION__))
;
628 assert(mXorBitmap.info().alphaType() == kUnpremul_SkAlphaType)(static_cast <bool> (mXorBitmap.info().alphaType() == kUnpremul_SkAlphaType
) ? void (0) : __assert_fail ("mXorBitmap.info().alphaType() == kUnpremul_SkAlphaType"
, "/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx", 628
, __extension__ __PRETTY_FUNCTION__))
;
629 assert(surfaceBitmap.bytesPerPixel() == 4)(static_cast <bool> (surfaceBitmap.bytesPerPixel() == 4
) ? void (0) : __assert_fail ("surfaceBitmap.bytesPerPixel() == 4"
, "/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx", 629
, __extension__ __PRETTY_FUNCTION__))
;
630 assert(mXorBitmap.bytesPerPixel() == 4)(static_cast <bool> (mXorBitmap.bytesPerPixel() == 4) ?
void (0) : __assert_fail ("mXorBitmap.bytesPerPixel() == 4",
"/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx", 630
, __extension__ __PRETTY_FUNCTION__))
;
631 for (SkRegion::Iterator it(mXorRegion); !it.done(); it.next())
632 {
633 for (int y = it.rect().top(); y < it.rect().bottom(); ++y)
634 {
635 uint8_t* data = static_cast<uint8_t*>(surfaceBitmap.getAddr(it.rect().x(), y));
636 const uint8_t* xordata = static_cast<uint8_t*>(mXorBitmap.getAddr(it.rect().x(), y));
637 for (int x = 0; x < it.rect().width(); ++x)
638 {
639 *data++ ^= *xordata++;
640 *data++ ^= *xordata++;
641 *data++ ^= *xordata++;
642 // alpha is not xor-ed
643 data++;
644 xordata++;
645 }
646 }
647 }
648 surfaceBitmap.notifyPixelsChanged();
649 mSurface->getCanvas()->drawBitmapRect(surfaceBitmap, mXorRegion.getBounds(),
650 SkRect::Make(mXorRegion.getBounds()), &paint);
651 mXorCanvas.reset();
652 mXorBitmap.reset();
653 mXorRegion.setEmpty();
654}
655
656void SkiaSalGraphicsImpl::SetROPLineColor(SalROPColor nROPColor)
657{
658 checkPendingDrawing();
659 switch (nROPColor)
660 {
661 case SalROPColor::N0:
662 mLineColor = Color(0, 0, 0);
663 break;
664 case SalROPColor::N1:
665 mLineColor = Color(0xff, 0xff, 0xff);
666 break;
667 case SalROPColor::Invert:
668 mLineColor = Color(0xff, 0xff, 0xff);
669 break;
670 }
671}
672
673void SkiaSalGraphicsImpl::SetROPFillColor(SalROPColor nROPColor)
674{
675 checkPendingDrawing();
676 switch (nROPColor)
677 {
678 case SalROPColor::N0:
679 mFillColor = Color(0, 0, 0);
680 break;
681 case SalROPColor::N1:
682 mFillColor = Color(0xff, 0xff, 0xff);
683 break;
684 case SalROPColor::Invert:
685 mFillColor = Color(0xff, 0xff, 0xff);
686 break;
687 }
688}
689
690void SkiaSalGraphicsImpl::drawPixel(long nX, long nY) { drawPixel(nX, nY, mLineColor); }
691
692void SkiaSalGraphicsImpl::drawPixel(long nX, long nY, Color nColor)
693{
694 if (nColor == SALCOLOR_NONE)
695 return;
696 preDraw();
697 SAL_INFO("vcl.skia.trace", "drawpixel(" << this << "): " << Point(nX, nY) << ":" << nColor)do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_INFO
, "vcl.skia.trace")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break
; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "drawpixel(" <<
this << "): " << Point(nX, nY) << ":" <<
nColor) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "697" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "drawpixel(" << this << "): "
<< Point(nX, nY) << ":" << nColor), 0); } else
{ ::std::ostringstream sal_detail_stream; sal_detail_stream <<
"drawpixel(" << this << "): " << Point(nX,
nY) << ":" << nColor; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "697" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "drawpixel(" << this << "): " <<
Point(nX, nY) << ":" << nColor) == 1) { ::sal_detail_log
( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "697" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "drawpixel(" << this << "): "
<< Point(nX, nY) << ":" << nColor), 0); } else
{ ::std::ostringstream sal_detail_stream; sal_detail_stream <<
"drawpixel(" << this << "): " << Point(nX,
nY) << ":" << nColor; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "697" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
;
698 addXorRegion(SkRect::MakeXYWH(nX, nY, 1, 1));
699 SkPaint paint;
700 paint.setColor(toSkColor(nColor));
701 // Apparently drawPixel() is actually expected to set the pixel and not draw it.
702 paint.setBlendMode(SkBlendMode::kSrc); // set as is, including alpha
703 getDrawCanvas()->drawPoint(toSkX(nX), toSkY(nY), paint);
704 postDraw();
705}
706
707void SkiaSalGraphicsImpl::drawLine(long nX1, long nY1, long nX2, long nY2)
708{
709 if (mLineColor == SALCOLOR_NONE)
710 return;
711 preDraw();
712 SAL_INFO("vcl.skia.trace", "drawline(" << this << "): " << Point(nX1, nY1) << "->"do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_INFO
, "vcl.skia.trace")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break
; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "drawline(" << this
<< "): " << Point(nX1, nY1) << "->" <<
Point(nX2, nY2) << ":" << mLineColor) == 1) { ::
sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"
), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "713" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "drawline(" << this << "): "
<< Point(nX1, nY1) << "->" << Point(nX2
, nY2) << ":" << mLineColor), 0); } else { ::std::
ostringstream sal_detail_stream; sal_detail_stream << "drawline("
<< this << "): " << Point(nX1, nY1) <<
"->" << Point(nX2, nY2) << ":" << mLineColor
; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"
), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "713" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "drawline(" << this << "): " <<
Point(nX1, nY1) << "->" << Point(nX2, nY2) <<
":" << mLineColor) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "713" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "drawline(" << this << "): "
<< Point(nX1, nY1) << "->" << Point(nX2
, nY2) << ":" << mLineColor), 0); } else { ::std::
ostringstream sal_detail_stream; sal_detail_stream << "drawline("
<< this << "): " << Point(nX1, nY1) <<
"->" << Point(nX2, nY2) << ":" << mLineColor
; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"
), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "713" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
713 << Point(nX2, nY2) << ":" << mLineColor)do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_INFO
, "vcl.skia.trace")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break
; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "drawline(" << this
<< "): " << Point(nX1, nY1) << "->" <<
Point(nX2, nY2) << ":" << mLineColor) == 1) { ::
sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"
), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "713" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "drawline(" << this << "): "
<< Point(nX1, nY1) << "->" << Point(nX2
, nY2) << ":" << mLineColor), 0); } else { ::std::
ostringstream sal_detail_stream; sal_detail_stream << "drawline("
<< this << "): " << Point(nX1, nY1) <<
"->" << Point(nX2, nY2) << ":" << mLineColor
; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"
), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "713" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "drawline(" << this << "): " <<
Point(nX1, nY1) << "->" << Point(nX2, nY2) <<
":" << mLineColor) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "713" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "drawline(" << this << "): "
<< Point(nX1, nY1) << "->" << Point(nX2
, nY2) << ":" << mLineColor), 0); } else { ::std::
ostringstream sal_detail_stream; sal_detail_stream << "drawline("
<< this << "): " << Point(nX1, nY1) <<
"->" << Point(nX2, nY2) << ":" << mLineColor
; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"
), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "713" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
;
714 addXorRegion(SkRect::MakeLTRB(nX1, nY1, nX2, nY2).makeSorted());
715 SkPaint paint;
716 paint.setColor(toSkColor(mLineColor));
717 paint.setAntiAlias(mParent.getAntiAlias());
718 getDrawCanvas()->drawLine(toSkX(nX1), toSkY(nY1), toSkX(nX2), toSkY(nY2), paint);
719 postDraw();
720}
721
722void SkiaSalGraphicsImpl::privateDrawAlphaRect(long nX, long nY, long nWidth, long nHeight,
723 double fTransparency, bool blockAA)
724{
725 preDraw();
726 SAL_INFO("vcl.skia.trace",do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_INFO
, "vcl.skia.trace")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break
; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "privatedrawrect(" <<
this << "): " << SkIRect::MakeXYWH(nX, nY, nWidth
, nHeight) << ":" << mLineColor << ":" <<
mFillColor << ":" << fTransparency) == 1) { ::sal_detail_log
( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "728" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "privatedrawrect(" << this <<
"): " << SkIRect::MakeXYWH(nX, nY, nWidth, nHeight) <<
":" << mLineColor << ":" << mFillColor <<
":" << fTransparency), 0); } else { ::std::ostringstream
sal_detail_stream; sal_detail_stream << "privatedrawrect("
<< this << "): " << SkIRect::MakeXYWH(nX, nY
, nWidth, nHeight) << ":" << mLineColor << ":"
<< mFillColor << ":" << fTransparency; ::sal
::detail::log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"
), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "728" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "privatedrawrect(" << this << "): " <<
SkIRect::MakeXYWH(nX, nY, nWidth, nHeight) << ":" <<
mLineColor << ":" << mFillColor << ":" <<
fTransparency) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "728" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "privatedrawrect(" << this <<
"): " << SkIRect::MakeXYWH(nX, nY, nWidth, nHeight) <<
":" << mLineColor << ":" << mFillColor <<
":" << fTransparency), 0); } else { ::std::ostringstream
sal_detail_stream; sal_detail_stream << "privatedrawrect("
<< this << "): " << SkIRect::MakeXYWH(nX, nY
, nWidth, nHeight) << ":" << mLineColor << ":"
<< mFillColor << ":" << fTransparency; ::sal
::detail::log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"
), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "728" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
727 "privatedrawrect(" << this << "): " << SkIRect::MakeXYWH(nX, nY, nWidth, nHeight)do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_INFO
, "vcl.skia.trace")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break
; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "privatedrawrect(" <<
this << "): " << SkIRect::MakeXYWH(nX, nY, nWidth
, nHeight) << ":" << mLineColor << ":" <<
mFillColor << ":" << fTransparency) == 1) { ::sal_detail_log
( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "728" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "privatedrawrect(" << this <<
"): " << SkIRect::MakeXYWH(nX, nY, nWidth, nHeight) <<
":" << mLineColor << ":" << mFillColor <<
":" << fTransparency), 0); } else { ::std::ostringstream
sal_detail_stream; sal_detail_stream << "privatedrawrect("
<< this << "): " << SkIRect::MakeXYWH(nX, nY
, nWidth, nHeight) << ":" << mLineColor << ":"
<< mFillColor << ":" << fTransparency; ::sal
::detail::log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"
), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "728" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "privatedrawrect(" << this << "): " <<
SkIRect::MakeXYWH(nX, nY, nWidth, nHeight) << ":" <<
mLineColor << ":" << mFillColor << ":" <<
fTransparency) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "728" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "privatedrawrect(" << this <<
"): " << SkIRect::MakeXYWH(nX, nY, nWidth, nHeight) <<
":" << mLineColor << ":" << mFillColor <<
":" << fTransparency), 0); } else { ::std::ostringstream
sal_detail_stream; sal_detail_stream << "privatedrawrect("
<< this << "): " << SkIRect::MakeXYWH(nX, nY
, nWidth, nHeight) << ":" << mLineColor << ":"
<< mFillColor << ":" << fTransparency; ::sal
::detail::log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"
), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "728" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
728 << ":" << mLineColor << ":" << mFillColor << ":" << fTransparency)do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_INFO
, "vcl.skia.trace")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break
; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "privatedrawrect(" <<
this << "): " << SkIRect::MakeXYWH(nX, nY, nWidth
, nHeight) << ":" << mLineColor << ":" <<
mFillColor << ":" << fTransparency) == 1) { ::sal_detail_log
( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "728" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "privatedrawrect(" << this <<
"): " << SkIRect::MakeXYWH(nX, nY, nWidth, nHeight) <<
":" << mLineColor << ":" << mFillColor <<
":" << fTransparency), 0); } else { ::std::ostringstream
sal_detail_stream; sal_detail_stream << "privatedrawrect("
<< this << "): " << SkIRect::MakeXYWH(nX, nY
, nWidth, nHeight) << ":" << mLineColor << ":"
<< mFillColor << ":" << fTransparency; ::sal
::detail::log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"
), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "728" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "privatedrawrect(" << this << "): " <<
SkIRect::MakeXYWH(nX, nY, nWidth, nHeight) << ":" <<
mLineColor << ":" << mFillColor << ":" <<
fTransparency) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "728" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "privatedrawrect(" << this <<
"): " << SkIRect::MakeXYWH(nX, nY, nWidth, nHeight) <<
":" << mLineColor << ":" << mFillColor <<
":" << fTransparency), 0); } else { ::std::ostringstream
sal_detail_stream; sal_detail_stream << "privatedrawrect("
<< this << "): " << SkIRect::MakeXYWH(nX, nY
, nWidth, nHeight) << ":" << mLineColor << ":"
<< mFillColor << ":" << fTransparency; ::sal
::detail::log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"
), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "728" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
;
729 addXorRegion(SkRect::MakeXYWH(nX, nY, nWidth, nHeight));
730 SkCanvas* canvas = getDrawCanvas();
731 SkPaint paint;
732 paint.setAntiAlias(!blockAA && mParent.getAntiAlias());
733 if (mFillColor != SALCOLOR_NONE)
734 {
735 paint.setColor(toSkColorWithTransparency(mFillColor, fTransparency));
736 paint.setStyle(SkPaint::kFill_Style);
737 // HACK: If the polygon is just a line, it still should be drawn. But when filling
738 // Skia doesn't draw empty polygons, so in that case ensure the line is drawn.
739 if (mLineColor == SALCOLOR_NONE && SkSize::Make(nWidth, nHeight).isEmpty())
740 paint.setStyle(SkPaint::kStroke_Style);
741 canvas->drawIRect(SkIRect::MakeXYWH(nX, nY, nWidth, nHeight), paint);
742 }
743 if (mLineColor != SALCOLOR_NONE)
744 {
745 paint.setColor(toSkColorWithTransparency(mLineColor, fTransparency));
746 paint.setStyle(SkPaint::kStroke_Style);
747 // The obnoxious "-1 DrawRect()" hack that I don't understand the purpose of (and I'm not sure
748 // if anybody does), but without it some cases do not work. The max() is needed because Skia
749 // will not draw anything if width or height is 0.
750 canvas->drawIRect(
751 SkIRect::MakeXYWH(nX, nY, std::max(1L, nWidth - 1), std::max(1L, nHeight - 1)), paint);
752 }
753 postDraw();
754}
755
756void SkiaSalGraphicsImpl::drawRect(long nX, long nY, long nWidth, long nHeight)
757{
758 privateDrawAlphaRect(nX, nY, nWidth, nHeight, 0.0, true);
759}
760
761void SkiaSalGraphicsImpl::drawPolyLine(sal_uInt32 nPoints, const SalPoint* pPtAry)
762{
763 basegfx::B2DPolygon aPolygon;
764 aPolygon.append(basegfx::B2DPoint(pPtAry->mnX, pPtAry->mnY), nPoints);
765 for (sal_uInt32 i = 1; i < nPoints; ++i)
766 aPolygon.setB2DPoint(i, basegfx::B2DPoint(pPtAry[i].mnX, pPtAry[i].mnY));
767 aPolygon.setClosed(false);
768
769 drawPolyLine(basegfx::B2DHomMatrix(), aPolygon, 0.0, 1.0, nullptr, basegfx::B2DLineJoin::Miter,
770 css::drawing::LineCap_BUTT, basegfx::deg2rad(15.0) /*default*/, false);
771}
772
773void SkiaSalGraphicsImpl::drawPolygon(sal_uInt32 nPoints, const SalPoint* pPtAry)
774{
775 basegfx::B2DPolygon aPolygon;
776 aPolygon.append(basegfx::B2DPoint(pPtAry->mnX, pPtAry->mnY), nPoints);
777 for (sal_uInt32 i = 1; i < nPoints; ++i)
778 aPolygon.setB2DPoint(i, basegfx::B2DPoint(pPtAry[i].mnX, pPtAry[i].mnY));
779
780 drawPolyPolygon(basegfx::B2DHomMatrix(), basegfx::B2DPolyPolygon(aPolygon), 0.0);
781}
782
783void SkiaSalGraphicsImpl::drawPolyPolygon(sal_uInt32 nPoly, const sal_uInt32* pPoints,
784 PCONSTSALPOINT* pPtAry)
785{
786 basegfx::B2DPolyPolygon aPolyPolygon;
787 for (sal_uInt32 nPolygon = 0; nPolygon < nPoly; ++nPolygon)
1
Assuming 'nPolygon' is >= 'nPoly'
2
Loop condition is false. Execution continues on line 802
788 {
789 sal_uInt32 nPoints = pPoints[nPolygon];
790 if (nPoints)
791 {
792 PCONSTSALPOINT pSalPoints = pPtAry[nPolygon];
793 basegfx::B2DPolygon aPolygon;
794 aPolygon.append(basegfx::B2DPoint(pSalPoints->mnX, pSalPoints->mnY), nPoints);
795 for (sal_uInt32 i = 1; i < nPoints; ++i)
796 aPolygon.setB2DPoint(i, basegfx::B2DPoint(pSalPoints[i].mnX, pSalPoints[i].mnY));
797
798 aPolyPolygon.append(aPolygon);
799 }
800 }
801
802 drawPolyPolygon(basegfx::B2DHomMatrix(), aPolyPolygon, 0.0);
3
Calling 'SkiaSalGraphicsImpl::drawPolyPolygon'
803}
804
805bool SkiaSalGraphicsImpl::drawPolyPolygon(const basegfx::B2DHomMatrix& rObjectToDevice,
806 const basegfx::B2DPolyPolygon& rPolyPolygon,
807 double fTransparency)
808{
809 const bool bHasFill(mFillColor != SALCOLOR_NONE);
810 const bool bHasLine(mLineColor != SALCOLOR_NONE);
811
812 if (rPolyPolygon.count() == 0 || !(bHasFill
4.1
'bHasFill' is true
|| bHasLine) || fTransparency < 0.0
4
Assuming the condition is false
5
Assuming the condition is false
7
Taking false branch
813 || fTransparency >= 1.0)
6
Assuming the condition is false
814 return true;
815
816 basegfx::B2DPolyPolygon aPolyPolygon(rPolyPolygon);
817 aPolyPolygon.transform(rObjectToDevice);
818
819 SAL_INFO("vcl.skia.trace", "drawpolypolygon(" << this << "): " << aPolyPolygon << ":"do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_INFO
, "vcl.skia.trace")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break
; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "drawpolypolygon(" <<
this << "): " << aPolyPolygon << ":" <<
mLineColor << ":" << mFillColor) == 1) { ::sal_detail_log
( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "820" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "drawpolypolygon(" << this <<
"): " << aPolyPolygon << ":" << mLineColor
<< ":" << mFillColor), 0); } else { ::std::ostringstream
sal_detail_stream; sal_detail_stream << "drawpolypolygon("
<< this << "): " << aPolyPolygon << ":"
<< mLineColor << ":" << mFillColor; ::sal::
detail::log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"
), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "820" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "drawpolypolygon(" << this << "): " <<
aPolyPolygon << ":" << mLineColor << ":" <<
mFillColor) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "820" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "drawpolypolygon(" << this <<
"): " << aPolyPolygon << ":" << mLineColor
<< ":" << mFillColor), 0); } else { ::std::ostringstream
sal_detail_stream; sal_detail_stream << "drawpolypolygon("
<< this << "): " << aPolyPolygon << ":"
<< mLineColor << ":" << mFillColor; ::sal::
detail::log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"
), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "820" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
8
Taking true branch
9
'Default' branch taken. Execution continues on line 819
10
Loop condition is false. Exiting loop
820 << mLineColor << ":" << mFillColor)do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_INFO
, "vcl.skia.trace")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break
; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "drawpolypolygon(" <<
this << "): " << aPolyPolygon << ":" <<
mLineColor << ":" << mFillColor) == 1) { ::sal_detail_log
( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "820" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "drawpolypolygon(" << this <<
"): " << aPolyPolygon << ":" << mLineColor
<< ":" << mFillColor), 0); } else { ::std::ostringstream
sal_detail_stream; sal_detail_stream << "drawpolypolygon("
<< this << "): " << aPolyPolygon << ":"
<< mLineColor << ":" << mFillColor; ::sal::
detail::log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"
), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "820" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "drawpolypolygon(" << this << "): " <<
aPolyPolygon << ":" << mLineColor << ":" <<
mFillColor) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "820" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "drawpolypolygon(" << this <<
"): " << aPolyPolygon << ":" << mLineColor
<< ":" << mFillColor), 0); } else { ::std::ostringstream
sal_detail_stream; sal_detail_stream << "drawpolypolygon("
<< this << "): " << aPolyPolygon << ":"
<< mLineColor << ":" << mFillColor; ::sal::
detail::log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"
), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "820" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
;
821
822 if (delayDrawPolyPolygon(aPolyPolygon, fTransparency))
11
Calling 'SkiaSalGraphicsImpl::delayDrawPolyPolygon'
823 {
824 scheduleFlush();
825 return true;
826 }
827
828 performDrawPolyPolygon(aPolyPolygon, fTransparency, mParent.getAntiAlias());
829 return true;
830}
831
832void SkiaSalGraphicsImpl::performDrawPolyPolygon(const basegfx::B2DPolyPolygon& aPolyPolygon,
833 double fTransparency, bool useAA)
834{
835 preDraw();
836
837 SkPath polygonPath;
838 addPolyPolygonToPath(aPolyPolygon, polygonPath);
839 polygonPath.setFillType(SkPathFillType::kEvenOdd);
840 addXorRegion(polygonPath.getBounds());
841
842 SkPaint aPaint;
843 aPaint.setAntiAlias(useAA);
844 // We normally use pixel at their center positions, but slightly off (see toSkX/Y()).
845 // With AA lines that "slightly off" causes tiny changes of color, making some tests
846 // fail. Since moving AA-ed line slightly to a side doesn't cause any real visual
847 // difference, just place exactly at the center. tdf#134346
848 const SkScalar posFix = useAA ? toSkXYFix : 0;
849 if (mFillColor != SALCOLOR_NONE)
850 {
851 SkPath path;
852 polygonPath.offset(toSkX(0) + posFix, toSkY(0) + posFix, &path);
853 aPaint.setColor(toSkColorWithTransparency(mFillColor, fTransparency));
854 aPaint.setStyle(SkPaint::kFill_Style);
855 // HACK: If the polygon is just a line, it still should be drawn. But when filling
856 // Skia doesn't draw empty polygons, so in that case ensure the line is drawn.
857 if (mLineColor == SALCOLOR_NONE && path.getBounds().isEmpty())
858 aPaint.setStyle(SkPaint::kStroke_Style);
859 getDrawCanvas()->drawPath(path, aPaint);
860 }
861 if (mLineColor != SALCOLOR_NONE)
862 {
863 SkPath path;
864 polygonPath.offset(toSkX(0) + posFix, toSkY(0) + posFix, &path);
865 aPaint.setColor(toSkColorWithTransparency(mLineColor, fTransparency));
866 aPaint.setStyle(SkPaint::kStroke_Style);
867 getDrawCanvas()->drawPath(path, aPaint);
868 }
869 postDraw();
870#if defined LINUX1
871 // WORKAROUND: The logo in the about dialog has drawing errors. This seems to happen
872 // only on Linux (not Windows on the same machine), with both AMDGPU and Mesa,
873 // and only when antialiasing is enabled. Flushing seems to avoid the problem.
874 if (useAA && SkiaHelper::getVendor() == DriverBlocklist::VendorAMD)
875 mSurface->flushAndSubmit();
876#endif
877}
878
879namespace
880{
881struct LessThan
882{
883 bool operator()(const basegfx::B2DPoint& point1, const basegfx::B2DPoint& point2) const
884 {
885 if (basegfx::fTools::equal(point1.getX(), point2.getX()))
886 return basegfx::fTools::less(point1.getY(), point2.getY());
887 return basegfx::fTools::less(point1.getX(), point2.getX());
888 }
889};
890} // namespace
891
892bool SkiaSalGraphicsImpl::delayDrawPolyPolygon(const basegfx::B2DPolyPolygon& aPolyPolygon,
893 double fTransparency)
894{
895 // There is some code that needlessly subdivides areas into adjacent rectangles,
896 // but Skia doesn't line them up perfectly if AA is enabled (e.g. Cairo, Qt5 do,
897 // but Skia devs claim it's working as intended
898 // https://groups.google.com/d/msg/skia-discuss/NlKpD2X_5uc/Vuwd-kyYBwAJ).
899 // An example is tdf#133016, which triggers SvgStyleAttributes::add_stroke()
900 // implementing a line stroke as a bunch of polygons instead of just one, and
901 // SvgLinearAtomPrimitive2D::create2DDecomposition() creates a gradient
902 // as a series of polygons of gradually changing color. Those places should be
903 // changed, but try to merge those split polygons back into the original one,
904 // where the needlessly created edges causing problems will not exist.
905 // This means drawing of such polygons needs to be delayed, so that they can
906 // be possibly merged with the next one.
907 // Merge only polygons of the same properties (color, etc.), so the gradient problem
908 // actually isn't handled here.
909
910 // Only AA polygons need merging, because they do not line up well because of the AA of the edges.
911 if (!mParent.getAntiAlias())
12
Assuming the condition is false
13
Taking false branch
912 return false;
913 // Only filled polygons without an outline are problematic.
914 if (mFillColor == SALCOLOR_NONE || mLineColor != SALCOLOR_NONE)
14
Taking false branch
915 return false;
916 // Merge only simple polygons, real polypolygons most likely aren't needlessly split,
917 // so they do not need joining.
918 if (aPolyPolygon.count() != 1)
15
Assuming the condition is false
16
Taking false branch
919 return false;
920 // If the polygon is not closed, it doesn't mark an area to be filled.
921 if (!aPolyPolygon.isClosed())
17
Assuming the condition is false
18
Taking false branch
922 return false;
923 // If a polygon does not contain a straight line, i.e. it's all curves, then do not merge.
924 // First of all that's even more expensive, and second it's very unlikely that it's a polygon
925 // split into more polygons.
926 if (!polygonContainsLine(aPolyPolygon))
19
Calling 'polygonContainsLine'
927 return false;
928
929 if (mLastPolyPolygonInfo.polygons.size() != 0
930 && (mLastPolyPolygonInfo.transparency != fTransparency
931 || !mLastPolyPolygonInfo.bounds.overlaps(aPolyPolygon.getB2DRange())))
932 {
933 checkPendingDrawing(); // Cannot be parts of the same larger polygon, draw the last and reset.
934 }
935 if (!mLastPolyPolygonInfo.polygons.empty())
936 {
937 assert(aPolyPolygon.count() == 1)(static_cast <bool> (aPolyPolygon.count() == 1) ? void (
0) : __assert_fail ("aPolyPolygon.count() == 1", "/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
, 937, __extension__ __PRETTY_FUNCTION__))
;
938 assert(mLastPolyPolygonInfo.polygons.back().count() == 1)(static_cast <bool> (mLastPolyPolygonInfo.polygons.back
().count() == 1) ? void (0) : __assert_fail ("mLastPolyPolygonInfo.polygons.back().count() == 1"
, "/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx", 938
, __extension__ __PRETTY_FUNCTION__))
;
939 // Check if the new and the previous polygon share at least one point. If not, then they
940 // cannot be adjacent polygons, so there's no point in trying to merge them.
941 bool sharePoint = false;
942 const basegfx::B2DPolygon& poly1 = aPolyPolygon.getB2DPolygon(0);
943 const basegfx::B2DPolygon& poly2 = mLastPolyPolygonInfo.polygons.back().getB2DPolygon(0);
944 o3tl::sorted_vector<basegfx::B2DPoint, LessThan> poly1Points; // for O(n log n)
945 poly1Points.reserve(poly1.count());
946 for (sal_uInt32 i = 0; i < poly1.count(); ++i)
947 poly1Points.insert(poly1.getB2DPoint(i));
948 for (sal_uInt32 i = 0; i < poly2.count(); ++i)
949 if (poly1Points.find(poly2.getB2DPoint(i)) != poly1Points.end())
950 {
951 sharePoint = true;
952 break;
953 }
954 if (!sharePoint)
955 checkPendingDrawing(); // Draw the previous one and reset.
956 }
957 // Collect the polygons that can be possibly merged. Do the merging only once at the end,
958 // because it's not a cheap operation.
959 mLastPolyPolygonInfo.polygons.push_back(aPolyPolygon);
960 mLastPolyPolygonInfo.bounds.expand(aPolyPolygon.getB2DRange());
961 mLastPolyPolygonInfo.transparency = fTransparency;
962 return true;
963}
964
965void SkiaSalGraphicsImpl::checkPendingDrawing()
966{
967 if (mLastPolyPolygonInfo.polygons.size() != 0)
968 { // Flush any pending polygon drawing.
969 basegfx::B2DPolyPolygonVector polygons;
970 std::swap(polygons, mLastPolyPolygonInfo.polygons);
971 double transparency = mLastPolyPolygonInfo.transparency;
972 mLastPolyPolygonInfo.bounds.reset();
973 if (polygons.size() == 1)
974 performDrawPolyPolygon(polygons.front(), transparency, true);
975 else
976 // TODO: tdf#136222 shows that basegfx::utils::mergeToSinglePolyPolygon() is unreliable
977 // in corner cases, possibly either a bug or rounding errors somewhere.
978 performDrawPolyPolygon(basegfx::utils::mergeToSinglePolyPolygon(polygons), transparency,
979 true);
980 }
981}
982
983bool SkiaSalGraphicsImpl::drawPolyLine(const basegfx::B2DHomMatrix& rObjectToDevice,
984 const basegfx::B2DPolygon& rPolyLine, double fTransparency,
985 double fLineWidth, const std::vector<double>* pStroke,
986 basegfx::B2DLineJoin eLineJoin,
987 css::drawing::LineCap eLineCap, double fMiterMinimumAngle,
988 bool bPixelSnapHairline)
989{
990 if (!rPolyLine.count() || fTransparency < 0.0 || fTransparency > 1.0
991 || mLineColor == SALCOLOR_NONE)
992 {
993 return true;
994 }
995
996 preDraw();
997 SAL_INFO("vcl.skia.trace", "drawpolyline(" << this << "): " << rPolyLine << ":" << mLineColor)do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_INFO
, "vcl.skia.trace")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break
; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "drawpolyline(" <<
this << "): " << rPolyLine << ":" <<
mLineColor) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "997" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "drawpolyline(" << this <<
"): " << rPolyLine << ":" << mLineColor), 0
); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "drawpolyline(" << this << "): " <<
rPolyLine << ":" << mLineColor; ::sal::detail::log
( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "997" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "drawpolyline(" << this << "): " <<
rPolyLine << ":" << mLineColor) == 1) { ::sal_detail_log
( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "997" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "drawpolyline(" << this <<
"): " << rPolyLine << ":" << mLineColor), 0
); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "drawpolyline(" << this << "): " <<
rPolyLine << ":" << mLineColor; ::sal::detail::log
( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "997" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
;
998
999 // tdf#124848 get correct LineWidth in discrete coordinates,
1000 if (fLineWidth == 0) // hairline
1001 fLineWidth = 1.0;
1002 else // Adjust line width for object-to-device scale.
1003 fLineWidth = (rObjectToDevice * basegfx::B2DVector(fLineWidth, 0)).getLength();
1004
1005 // Transform to DeviceCoordinates, get DeviceLineWidth, execute PixelSnapHairline
1006 basegfx::B2DPolyPolygon aPolyPolygonLine;
1007 aPolyPolygonLine.append(rPolyLine);
1008 aPolyPolygonLine.transform(rObjectToDevice);
1009 if (bPixelSnapHairline)
1010 {
1011 aPolyPolygonLine = basegfx::utils::snapPointsOfHorizontalOrVerticalEdges(aPolyPolygonLine);
1012 }
1013
1014 // Setup Line Join
1015 SkPaint::Join eSkLineJoin = SkPaint::kMiter_Join;
1016 switch (eLineJoin)
1017 {
1018 case basegfx::B2DLineJoin::Bevel:
1019 eSkLineJoin = SkPaint::kBevel_Join;
1020 break;
1021 case basegfx::B2DLineJoin::Round:
1022 eSkLineJoin = SkPaint::kRound_Join;
1023 break;
1024 case basegfx::B2DLineJoin::NONE:
1025 case basegfx::B2DLineJoin::Miter:
1026 eSkLineJoin = SkPaint::kMiter_Join;
1027 break;
1028 }
1029
1030 // convert miter minimum angle to miter limit
1031 double fMiterLimit = 1.0 / std::sin(fMiterMinimumAngle / 2.0);
1032
1033 // Setup Line Cap
1034 SkPaint::Cap eSkLineCap(SkPaint::kButt_Cap);
1035
1036 switch (eLineCap)
1037 {
1038 case css::drawing::LineCap_ROUND:
1039 eSkLineCap = SkPaint::kRound_Cap;
1040 break;
1041 case css::drawing::LineCap_SQUARE:
1042 eSkLineCap = SkPaint::kSquare_Cap;
1043 break;
1044 default: // css::drawing::LineCap_BUTT:
1045 eSkLineCap = SkPaint::kButt_Cap;
1046 break;
1047 }
1048
1049 SkPaint aPaint;
1050 aPaint.setStyle(SkPaint::kStroke_Style);
1051 aPaint.setStrokeCap(eSkLineCap);
1052 aPaint.setStrokeJoin(eSkLineJoin);
1053 aPaint.setColor(toSkColorWithTransparency(mLineColor, fTransparency));
1054 aPaint.setStrokeMiter(fMiterLimit);
1055 aPaint.setStrokeWidth(fLineWidth);
1056 aPaint.setAntiAlias(mParent.getAntiAlias());
1057 // See the tdf#134346 comment above.
1058 const SkScalar posFix = mParent.getAntiAlias() ? toSkXYFix : 0;
1059
1060 if (pStroke && std::accumulate(pStroke->begin(), pStroke->end(), 0.0) != 0)
1061 {
1062 std::vector<SkScalar> intervals;
1063 // Transform size by the matrix.
1064 for (double stroke : *pStroke)
1065 intervals.push_back((rObjectToDevice * basegfx::B2DVector(stroke, 0)).getLength());
1066 aPaint.setPathEffect(SkDashPathEffect::Make(intervals.data(), intervals.size(), 0));
1067 }
1068
1069 // Skia does not support basegfx::B2DLineJoin::NONE, so in that case batch only if lines
1070 // are not wider than a pixel.
1071 if (eLineJoin != basegfx::B2DLineJoin::NONE || fLineWidth <= 1.0)
1072 {
1073 SkPath aPath;
1074 aPath.setFillType(SkPathFillType::kEvenOdd);
1075 for (sal_uInt32 a(0); a < aPolyPolygonLine.count(); a++)
1076 addPolygonToPath(aPolyPolygonLine.getB2DPolygon(a), aPath);
1077 aPath.offset(toSkX(0) + posFix, toSkY(0) + posFix, nullptr);
1078 addXorRegion(aPath.getBounds());
1079 getDrawCanvas()->drawPath(aPath, aPaint);
1080 }
1081 else
1082 {
1083 for (sal_uInt32 i = 0; i < aPolyPolygonLine.count(); ++i)
1084 {
1085 const basegfx::B2DPolygon& rPolygon = aPolyPolygonLine.getB2DPolygon(i);
1086 sal_uInt32 nPoints = rPolygon.count();
1087 bool bClosed = rPolygon.isClosed();
1088 for (sal_uInt32 j = 0; j < (bClosed ? nPoints : nPoints - 1); ++j)
1089 {
1090 sal_uInt32 index1 = (j + 0) % nPoints;
1091 sal_uInt32 index2 = (j + 1) % nPoints;
1092 SkPath aPath;
1093 aPath.moveTo(rPolygon.getB2DPoint(index1).getX(),
1094 rPolygon.getB2DPoint(index1).getY());
1095 aPath.lineTo(rPolygon.getB2DPoint(index2).getX(),
1096 rPolygon.getB2DPoint(index2).getY());
1097
1098 aPath.offset(toSkX(0) + posFix, toSkY(0) + posFix, nullptr);
1099 addXorRegion(aPath.getBounds());
1100 getDrawCanvas()->drawPath(aPath, aPaint);
1101 }
1102 }
1103 }
1104
1105 postDraw();
1106
1107 return true;
1108}
1109
1110bool SkiaSalGraphicsImpl::drawPolyLineBezier(sal_uInt32, const SalPoint*, const PolyFlags*)
1111{
1112 return false;
1113}
1114
1115bool SkiaSalGraphicsImpl::drawPolygonBezier(sal_uInt32, const SalPoint*, const PolyFlags*)
1116{
1117 return false;
1118}
1119
1120bool SkiaSalGraphicsImpl::drawPolyPolygonBezier(sal_uInt32, const sal_uInt32*,
1121 const SalPoint* const*, const PolyFlags* const*)
1122{
1123 return false;
1124}
1125
1126static void copyArea(SkCanvas* canvas, sk_sp<SkSurface> surface, long nDestX, long nDestY,
1127 long nSrcX, long nSrcY, long nSrcWidth, long nSrcHeight, bool srcIsRaster,
1128 bool destIsRaster)
1129{
1130 // Using SkSurface::draw() should be more efficient than SkSurface::makeImageSnapshot(),
1131 // because it may detect copying to itself and avoid some needless copies.
1132 // But it has problems with drawing to itself
1133 // (https://groups.google.com/forum/#!topic/skia-discuss/6yiuw24jv0I) and also
1134 // raster surfaces do not avoid a copy of the source
1135 // (https://groups.google.com/forum/#!topic/skia-discuss/S3FMpCi82k0).
1136 // Finally, there's not much point if one of them is raster and the other is not (chrome/m86 even crashes).
1137 if (canvas == surface->getCanvas() || srcIsRaster || (srcIsRaster != destIsRaster))
1138 {
1139 SkPaint paint;
1140 paint.setBlendMode(SkBlendMode::kSrc); // copy as is, including alpha
1141 canvas->drawImageRect(SkiaHelper::makeCheckedImageSnapshot(surface),
1142 SkIRect::MakeXYWH(nSrcX, nSrcY, nSrcWidth, nSrcHeight),
1143 SkRect::MakeXYWH(nDestX, nDestY, nSrcWidth, nSrcHeight), &paint);
1144 return;
1145 }
1146 // SkCanvas::draw() cannot do a subrectangle, so clip.
1147 canvas->save();
1148 canvas->clipRect(SkRect::MakeXYWH(nDestX, nDestY, nSrcWidth, nSrcHeight));
1149 SkPaint paint;
1150 paint.setBlendMode(SkBlendMode::kSrc); // copy as is, including alpha
1151 surface->draw(canvas, nDestX - nSrcX, nDestY - nSrcY, &paint);
1152 canvas->restore();
1153}
1154
1155void SkiaSalGraphicsImpl::copyArea(long nDestX, long nDestY, long nSrcX, long nSrcY, long nSrcWidth,
1156 long nSrcHeight, bool /*bWindowInvalidate*/)
1157{
1158 if (nDestX == nSrcX && nDestY == nSrcY)
1159 return;
1160 preDraw();
1161 SAL_INFO("vcl.skia.trace", "copyarea("do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_INFO
, "vcl.skia.trace")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break
; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "copyarea(" << this
<< "): " << Point(nSrcX, nSrcY) << "->"
<< SkIRect::MakeXYWH(nDestX, nDestY, nSrcWidth, nSrcHeight
)) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO), (
"vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1163" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "copyarea(" << this << "): "
<< Point(nSrcX, nSrcY) << "->" << SkIRect
::MakeXYWH(nDestX, nDestY, nSrcWidth, nSrcHeight)), 0); } else
{ ::std::ostringstream sal_detail_stream; sal_detail_stream <<
"copyarea(" << this << "): " << Point(nSrcX
, nSrcY) << "->" << SkIRect::MakeXYWH(nDestX, nDestY
, nSrcWidth, nSrcHeight); ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1163" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "copyarea(" << this << "): " <<
Point(nSrcX, nSrcY) << "->" << SkIRect::MakeXYWH
(nDestX, nDestY, nSrcWidth, nSrcHeight)) == 1) { ::sal_detail_log
( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1163" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "copyarea(" << this << "): "
<< Point(nSrcX, nSrcY) << "->" << SkIRect
::MakeXYWH(nDestX, nDestY, nSrcWidth, nSrcHeight)), 0); } else
{ ::std::ostringstream sal_detail_stream; sal_detail_stream <<
"copyarea(" << this << "): " << Point(nSrcX
, nSrcY) << "->" << SkIRect::MakeXYWH(nDestX, nDestY
, nSrcWidth, nSrcHeight); ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1163" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
1162 << this << "): " << Point(nSrcX, nSrcY) << "->"do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_INFO
, "vcl.skia.trace")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break
; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "copyarea(" << this
<< "): " << Point(nSrcX, nSrcY) << "->"
<< SkIRect::MakeXYWH(nDestX, nDestY, nSrcWidth, nSrcHeight
)) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO), (
"vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1163" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "copyarea(" << this << "): "
<< Point(nSrcX, nSrcY) << "->" << SkIRect
::MakeXYWH(nDestX, nDestY, nSrcWidth, nSrcHeight)), 0); } else
{ ::std::ostringstream sal_detail_stream; sal_detail_stream <<
"copyarea(" << this << "): " << Point(nSrcX
, nSrcY) << "->" << SkIRect::MakeXYWH(nDestX, nDestY
, nSrcWidth, nSrcHeight); ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1163" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "copyarea(" << this << "): " <<
Point(nSrcX, nSrcY) << "->" << SkIRect::MakeXYWH
(nDestX, nDestY, nSrcWidth, nSrcHeight)) == 1) { ::sal_detail_log
( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1163" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "copyarea(" << this << "): "
<< Point(nSrcX, nSrcY) << "->" << SkIRect
::MakeXYWH(nDestX, nDestY, nSrcWidth, nSrcHeight)), 0); } else
{ ::std::ostringstream sal_detail_stream; sal_detail_stream <<
"copyarea(" << this << "): " << Point(nSrcX
, nSrcY) << "->" << SkIRect::MakeXYWH(nDestX, nDestY
, nSrcWidth, nSrcHeight); ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1163" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
1163 << SkIRect::MakeXYWH(nDestX, nDestY, nSrcWidth, nSrcHeight))do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_INFO
, "vcl.skia.trace")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break
; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "copyarea(" << this
<< "): " << Point(nSrcX, nSrcY) << "->"
<< SkIRect::MakeXYWH(nDestX, nDestY, nSrcWidth, nSrcHeight
)) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO), (
"vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1163" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "copyarea(" << this << "): "
<< Point(nSrcX, nSrcY) << "->" << SkIRect
::MakeXYWH(nDestX, nDestY, nSrcWidth, nSrcHeight)), 0); } else
{ ::std::ostringstream sal_detail_stream; sal_detail_stream <<
"copyarea(" << this << "): " << Point(nSrcX
, nSrcY) << "->" << SkIRect::MakeXYWH(nDestX, nDestY
, nSrcWidth, nSrcHeight); ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1163" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "copyarea(" << this << "): " <<
Point(nSrcX, nSrcY) << "->" << SkIRect::MakeXYWH
(nDestX, nDestY, nSrcWidth, nSrcHeight)) == 1) { ::sal_detail_log
( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1163" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "copyarea(" << this << "): "
<< Point(nSrcX, nSrcY) << "->" << SkIRect
::MakeXYWH(nDestX, nDestY, nSrcWidth, nSrcHeight)), 0); } else
{ ::std::ostringstream sal_detail_stream; sal_detail_stream <<
"copyarea(" << this << "): " << Point(nSrcX
, nSrcY) << "->" << SkIRect::MakeXYWH(nDestX, nDestY
, nSrcWidth, nSrcHeight); ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1163" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
;
1164 assert(!mXorMode)(static_cast <bool> (!mXorMode) ? void (0) : __assert_fail
("!mXorMode", "/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
, 1164, __extension__ __PRETTY_FUNCTION__))
;
1165 ::copyArea(getDrawCanvas(), mSurface, nDestX, nDestY, nSrcX, nSrcY, nSrcWidth, nSrcHeight,
1166 !isGPU(), !isGPU());
1167 postDraw();
1168}
1169
1170void SkiaSalGraphicsImpl::copyBits(const SalTwoRect& rPosAry, SalGraphics* pSrcGraphics)
1171{
1172 preDraw();
1173 SkiaSalGraphicsImpl* src;
1174 if (pSrcGraphics)
1175 {
1176 assert(dynamic_cast<SkiaSalGraphicsImpl*>(pSrcGraphics->GetImpl()))(static_cast <bool> (dynamic_cast<SkiaSalGraphicsImpl
*>(pSrcGraphics->GetImpl())) ? void (0) : __assert_fail
("dynamic_cast<SkiaSalGraphicsImpl*>(pSrcGraphics->GetImpl())"
, "/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx", 1176
, __extension__ __PRETTY_FUNCTION__))
;
1177 src = static_cast<SkiaSalGraphicsImpl*>(pSrcGraphics->GetImpl());
1178 src->checkSurface();
1179 src->flushDrawing();
1180 }
1181 else
1182 {
1183 src = this;
1184 assert(!mXorMode)(static_cast <bool> (!mXorMode) ? void (0) : __assert_fail
("!mXorMode", "/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
, 1184, __extension__ __PRETTY_FUNCTION__))
;
1185 }
1186 if (rPosAry.mnSrcWidth == rPosAry.mnDestWidth && rPosAry.mnSrcHeight == rPosAry.mnDestHeight)
1187 {
1188 auto srcDebug = [&]() -> std::string {
1189 if (src == this)
1190 return "(self)";
1191 else
1192 {
1193 std::ostringstream stream;
1194 stream << "(" << src << ")";
1195 return stream.str();
1196 }
1197 };
1198 SAL_INFO("vcl.skia.trace",do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_INFO
, "vcl.skia.trace")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break
; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "copybits(" << this
<< "): " << srcDebug() << " copy area: " <<
rPosAry) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1199" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "copybits(" << this << "): "
<< srcDebug() << " copy area: " << rPosAry
), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "copybits(" << this << "): " << srcDebug
() << " copy area: " << rPosAry; ::sal::detail::log
( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1199" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "copybits(" << this << "): " <<
srcDebug() << " copy area: " << rPosAry) == 1) {
::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"
), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1199" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "copybits(" << this << "): "
<< srcDebug() << " copy area: " << rPosAry
), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "copybits(" << this << "): " << srcDebug
() << " copy area: " << rPosAry; ::sal::detail::log
( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1199" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
1199 "copybits(" << this << "): " << srcDebug() << " copy area: " << rPosAry)do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_INFO
, "vcl.skia.trace")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break
; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "copybits(" << this
<< "): " << srcDebug() << " copy area: " <<
rPosAry) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1199" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "copybits(" << this << "): "
<< srcDebug() << " copy area: " << rPosAry
), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "copybits(" << this << "): " << srcDebug
() << " copy area: " << rPosAry; ::sal::detail::log
( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1199" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "copybits(" << this << "): " <<
srcDebug() << " copy area: " << rPosAry) == 1) {
::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"
), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1199" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "copybits(" << this << "): "
<< srcDebug() << " copy area: " << rPosAry
), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "copybits(" << this << "): " << srcDebug
() << " copy area: " << rPosAry; ::sal::detail::log
( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1199" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
;
1200 ::copyArea(getDrawCanvas(), src->mSurface, rPosAry.mnDestX, rPosAry.mnDestY, rPosAry.mnSrcX,
1201 rPosAry.mnSrcY, rPosAry.mnDestWidth, rPosAry.mnDestHeight, !src->isGPU(),
1202 !isGPU());
1203 }
1204 else
1205 {
1206 SAL_INFO("vcl.skia.trace", "copybits(" << this << "): (" << src << "): " << rPosAry)do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_INFO
, "vcl.skia.trace")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break
; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "copybits(" << this
<< "): (" << src << "): " << rPosAry
) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"
), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1206" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "copybits(" << this << "): ("
<< src << "): " << rPosAry), 0); } else { ::
std::ostringstream sal_detail_stream; sal_detail_stream <<
"copybits(" << this << "): (" << src <<
"): " << rPosAry; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1206" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "copybits(" << this << "): (" <<
src << "): " << rPosAry) == 1) { ::sal_detail_log
( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1206" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "copybits(" << this << "): ("
<< src << "): " << rPosAry), 0); } else { ::
std::ostringstream sal_detail_stream; sal_detail_stream <<
"copybits(" << this << "): (" << src <<
"): " << rPosAry; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1206" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
;
1207 // Do not use makeImageSnapshot(rect), as that one may make a needless data copy.
1208 sk_sp<SkImage> image = SkiaHelper::makeCheckedImageSnapshot(src->mSurface);
1209 SkPaint paint;
1210 paint.setBlendMode(SkBlendMode::kSrc); // copy as is, including alpha
1211 if (rPosAry.mnSrcWidth != rPosAry.mnDestWidth
1212 || rPosAry.mnSrcHeight != rPosAry.mnDestHeight)
1213 paint.setFilterQuality(kHigh_SkFilterQuality);
1214 getDrawCanvas()->drawImageRect(image,
1215 SkIRect::MakeXYWH(rPosAry.mnSrcX, rPosAry.mnSrcY,
1216 rPosAry.mnSrcWidth, rPosAry.mnSrcHeight),
1217 SkRect::MakeXYWH(rPosAry.mnDestX, rPosAry.mnDestY,
1218 rPosAry.mnDestWidth, rPosAry.mnDestHeight),
1219 &paint);
1220 }
1221 assert(!mXorMode)(static_cast <bool> (!mXorMode) ? void (0) : __assert_fail
("!mXorMode", "/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
, 1221, __extension__ __PRETTY_FUNCTION__))
;
1222 postDraw();
1223}
1224
1225bool SkiaSalGraphicsImpl::blendBitmap(const SalTwoRect& rPosAry, const SalBitmap& rBitmap)
1226{
1227 if (checkInvalidSourceOrDestination(rPosAry))
1228 return false;
1229
1230 assert(dynamic_cast<const SkiaSalBitmap*>(&rBitmap))(static_cast <bool> (dynamic_cast<const SkiaSalBitmap
*>(&rBitmap)) ? void (0) : __assert_fail ("dynamic_cast<const SkiaSalBitmap*>(&rBitmap)"
, "/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx", 1230
, __extension__ __PRETTY_FUNCTION__))
;
1231 const SkiaSalBitmap& rSkiaBitmap = static_cast<const SkiaSalBitmap&>(rBitmap);
1232 // This is used by VirtualDevice in the alpha mode for the "alpha" layer which
1233 // is actually one-minus-alpha (opacity). Therefore white=0xff=transparent,
1234 // black=0x00=opaque. So the result is transparent only if both the inputs
1235 // are transparent. Since for blending operations white=1.0 and black=0.0,
1236 // kMultiply should handle exactly that (transparent*transparent=transparent,
1237 // opaque*transparent=opaque). And guessing from the "floor" in TYPE_BLEND in opengl's
1238 // combinedTextureFragmentShader.glsl, the layer is not even alpha values but
1239 // simply yes-or-no mask.
1240 // See also blendAlphaBitmap().
1241 if (rSkiaBitmap.IsFullyOpaqueAsAlpha())
1242 {
1243 // Optimization. If the bitmap means fully opaque, it's all zero's. In CPU
1244 // mode it should be faster to just copy instead of SkBlendMode::kMultiply.
1245 drawBitmap(rPosAry, rSkiaBitmap);
1246 }
1247 else
1248 drawBitmap(rPosAry, rSkiaBitmap, SkBlendMode::kMultiply);
1249 return true;
1250}
1251
1252bool SkiaSalGraphicsImpl::blendAlphaBitmap(const SalTwoRect& rPosAry,
1253 const SalBitmap& rSourceBitmap,
1254 const SalBitmap& rMaskBitmap,
1255 const SalBitmap& rAlphaBitmap)
1256{
1257 if (checkInvalidSourceOrDestination(rPosAry))
1258 return false;
1259
1260 assert(dynamic_cast<const SkiaSalBitmap*>(&rSourceBitmap))(static_cast <bool> (dynamic_cast<const SkiaSalBitmap
*>(&rSourceBitmap)) ? void (0) : __assert_fail ("dynamic_cast<const SkiaSalBitmap*>(&rSourceBitmap)"
, "/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx", 1260
, __extension__ __PRETTY_FUNCTION__))
;
1261 assert(dynamic_cast<const SkiaSalBitmap*>(&rMaskBitmap))(static_cast <bool> (dynamic_cast<const SkiaSalBitmap
*>(&rMaskBitmap)) ? void (0) : __assert_fail ("dynamic_cast<const SkiaSalBitmap*>(&rMaskBitmap)"
, "/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx", 1261
, __extension__ __PRETTY_FUNCTION__))
;
1262 assert(dynamic_cast<const SkiaSalBitmap*>(&rAlphaBitmap))(static_cast <bool> (dynamic_cast<const SkiaSalBitmap
*>(&rAlphaBitmap)) ? void (0) : __assert_fail ("dynamic_cast<const SkiaSalBitmap*>(&rAlphaBitmap)"
, "/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx", 1262
, __extension__ __PRETTY_FUNCTION__))
;
1263 const SkiaSalBitmap& rSkiaSourceBitmap = static_cast<const SkiaSalBitmap&>(rSourceBitmap);
1264 const SkiaSalBitmap& rSkiaMaskBitmap = static_cast<const SkiaSalBitmap&>(rMaskBitmap);
1265 const SkiaSalBitmap& rSkiaAlphaBitmap = static_cast<const SkiaSalBitmap&>(rAlphaBitmap);
1266
1267 if (rSkiaMaskBitmap.IsFullyOpaqueAsAlpha())
1268 {
1269 // Optimization. If the mask of the bitmap to be blended means it's actually opaque,
1270 // just draw the bitmap directly (that's what the math below will result in).
1271 drawBitmap(rPosAry, rSkiaSourceBitmap);
1272 return true;
1273 }
1274 // This was originally implemented for the OpenGL drawing method and it is poorly documented.
1275 // The source and mask bitmaps are the usual data and alpha bitmaps, and 'alpha'
1276 // is the "alpha" layer of the VirtualDevice (the alpha in VirtualDevice is also stored
1277 // as a separate bitmap). Now if I understand it correctly these two alpha masks first need
1278 // to be combined into the actual alpha mask to be used. The formula for TYPE_BLEND
1279 // in opengl's combinedTextureFragmentShader.glsl is
1280 // "result_alpha = 1.0 - (1.0 - floor(alpha)) * mask".
1281 // See also blendBitmap().
1282
1283 // First do the "( 1 - alpha ) * mask"
1284 // (no idea how to do "floor", but hopefully not needed in practice).
1285 sk_sp<SkShader> shaderAlpha
1286 = SkShaders::Blend(SkBlendMode::kDstOut, rSkiaMaskBitmap.GetAlphaSkShader(),
1287 rSkiaAlphaBitmap.GetAlphaSkShader());
1288 // And now draw the bitmap with "1 - x", where x is the "( 1 - alpha ) * mask".
1289 sk_sp<SkShader> shader
1290 = SkShaders::Blend(SkBlendMode::kSrcOut, shaderAlpha, rSkiaSourceBitmap.GetSkShader());
1291 drawShader(rPosAry, shader);
1292 return true;
1293}
1294
1295void SkiaSalGraphicsImpl::drawBitmap(const SalTwoRect& rPosAry, const SalBitmap& rSalBitmap)
1296{
1297 if (checkInvalidSourceOrDestination(rPosAry))
1298 return;
1299
1300 assert(dynamic_cast<const SkiaSalBitmap*>(&rSalBitmap))(static_cast <bool> (dynamic_cast<const SkiaSalBitmap
*>(&rSalBitmap)) ? void (0) : __assert_fail ("dynamic_cast<const SkiaSalBitmap*>(&rSalBitmap)"
, "/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx", 1300
, __extension__ __PRETTY_FUNCTION__))
;
1301 const SkiaSalBitmap& rSkiaSourceBitmap = static_cast<const SkiaSalBitmap&>(rSalBitmap);
1302
1303 drawBitmap(rPosAry, rSkiaSourceBitmap);
1304}
1305
1306void SkiaSalGraphicsImpl::drawBitmap(const SalTwoRect& rPosAry, const SalBitmap& rSalBitmap,
1307 const SalBitmap& rMaskBitmap)
1308{
1309 drawAlphaBitmap(rPosAry, rSalBitmap, rMaskBitmap);
1310}
1311
1312void SkiaSalGraphicsImpl::drawMask(const SalTwoRect& rPosAry, const SalBitmap& rSalBitmap,
1313 Color nMaskColor)
1314{
1315 assert(dynamic_cast<const SkiaSalBitmap*>(&rSalBitmap))(static_cast <bool> (dynamic_cast<const SkiaSalBitmap
*>(&rSalBitmap)) ? void (0) : __assert_fail ("dynamic_cast<const SkiaSalBitmap*>(&rSalBitmap)"
, "/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx", 1315
, __extension__ __PRETTY_FUNCTION__))
;
1316 const SkiaSalBitmap& skiaBitmap = static_cast<const SkiaSalBitmap&>(rSalBitmap);
1317 drawShader(rPosAry,
1318 SkShaders::Blend(SkBlendMode::kDstOut, // VCL alpha is one-minus-alpha.
1319 SkShaders::Color(toSkColor(nMaskColor)),
1320 skiaBitmap.GetAlphaSkShader()));
1321}
1322
1323std::shared_ptr<SalBitmap> SkiaSalGraphicsImpl::getBitmap(long nX, long nY, long nWidth,
1324 long nHeight)
1325{
1326 SkiaZone zone;
1327 checkSurface();
1328 SAL_INFO("vcl.skia.trace",do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_INFO
, "vcl.skia.trace")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break
; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "getbitmap(" <<
this << "): " << SkIRect::MakeXYWH(nX, nY, nWidth
, nHeight)) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1329" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "getbitmap(" << this << "): "
<< SkIRect::MakeXYWH(nX, nY, nWidth, nHeight)), 0); } else
{ ::std::ostringstream sal_detail_stream; sal_detail_stream <<
"getbitmap(" << this << "): " << SkIRect::
MakeXYWH(nX, nY, nWidth, nHeight); ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1329" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "getbitmap(" << this << "): " <<
SkIRect::MakeXYWH(nX, nY, nWidth, nHeight)) == 1) { ::sal_detail_log
( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1329" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "getbitmap(" << this << "): "
<< SkIRect::MakeXYWH(nX, nY, nWidth, nHeight)), 0); } else
{ ::std::ostringstream sal_detail_stream; sal_detail_stream <<
"getbitmap(" << this << "): " << SkIRect::
MakeXYWH(nX, nY, nWidth, nHeight); ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1329" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
1329 "getbitmap(" << this << "): " << SkIRect::MakeXYWH(nX, nY, nWidth, nHeight))do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_INFO
, "vcl.skia.trace")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break
; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "getbitmap(" <<
this << "): " << SkIRect::MakeXYWH(nX, nY, nWidth
, nHeight)) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1329" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "getbitmap(" << this << "): "
<< SkIRect::MakeXYWH(nX, nY, nWidth, nHeight)), 0); } else
{ ::std::ostringstream sal_detail_stream; sal_detail_stream <<
"getbitmap(" << this << "): " << SkIRect::
MakeXYWH(nX, nY, nWidth, nHeight); ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1329" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "getbitmap(" << this << "): " <<
SkIRect::MakeXYWH(nX, nY, nWidth, nHeight)) == 1) { ::sal_detail_log
( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1329" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "getbitmap(" << this << "): "
<< SkIRect::MakeXYWH(nX, nY, nWidth, nHeight)), 0); } else
{ ::std::ostringstream sal_detail_stream; sal_detail_stream <<
"getbitmap(" << this << "): " << SkIRect::
MakeXYWH(nX, nY, nWidth, nHeight); ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1329" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
;
1330 flushDrawing();
1331 // TODO makeImageSnapshot(rect) may copy the data, which may be a waste if this is used
1332 // e.g. for VirtualDevice's lame alpha blending, in which case the image will eventually end up
1333 // in blendAlphaBitmap(), where we could simply use the proper rect of the image.
1334 sk_sp<SkImage> image = SkiaHelper::makeCheckedImageSnapshot(
1335 mSurface, SkIRect::MakeXYWH(nX, nY, nWidth, nHeight));
1336 return std::make_shared<SkiaSalBitmap>(image);
1337}
1338
1339Color SkiaSalGraphicsImpl::getPixel(long nX, long nY)
1340{
1341 SkiaZone zone;
1342 checkSurface();
1343 SAL_INFO("vcl.skia.trace", "getpixel(" << this << "): " << Point(nX, nY))do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_INFO
, "vcl.skia.trace")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break
; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "getpixel(" << this
<< "): " << Point(nX, nY)) == 1) { ::sal_detail_log
( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1343" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "getpixel(" << this << "): "
<< Point(nX, nY)), 0); } else { ::std::ostringstream sal_detail_stream
; sal_detail_stream << "getpixel(" << this <<
"): " << Point(nX, nY); ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1343" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "getpixel(" << this << "): " <<
Point(nX, nY)) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1343" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "getpixel(" << this << "): "
<< Point(nX, nY)), 0); } else { ::std::ostringstream sal_detail_stream
; sal_detail_stream << "getpixel(" << this <<
"): " << Point(nX, nY); ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1343" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
;
1344 flushDrawing();
1345 // This is presumably slow, but getPixel() should be generally used only by unit tests.
1346 SkBitmap bitmap;
1347 if (!bitmap.tryAllocN32Pixels(GetWidth(), GetHeight()))
1348 abort();
1349 if (!mSurface->readPixels(bitmap, 0, 0))
1350 abort();
1351 return fromSkColor(bitmap.getColor(nX, nY));
1352}
1353
1354void SkiaSalGraphicsImpl::invert(basegfx::B2DPolygon const& rPoly, SalInvert eFlags)
1355{
1356 preDraw();
1357 SAL_INFO("vcl.skia.trace", "invert(" << this << "): " << rPoly << ":" << int(eFlags))do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_INFO
, "vcl.skia.trace")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break
; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "invert(" << this
<< "): " << rPoly << ":" << int(eFlags
)) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO), (
"vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1357" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "invert(" << this << "): "
<< rPoly << ":" << int(eFlags)), 0); } else
{ ::std::ostringstream sal_detail_stream; sal_detail_stream <<
"invert(" << this << "): " << rPoly <<
":" << int(eFlags); ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1357" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "invert(" << this << "): " << rPoly
<< ":" << int(eFlags)) == 1) { ::sal_detail_log(
(::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1357" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "invert(" << this << "): "
<< rPoly << ":" << int(eFlags)), 0); } else
{ ::std::ostringstream sal_detail_stream; sal_detail_stream <<
"invert(" << this << "): " << rPoly <<
":" << int(eFlags); ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1357" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
;
1358 assert(!mXorMode)(static_cast <bool> (!mXorMode) ? void (0) : __assert_fail
("!mXorMode", "/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
, 1358, __extension__ __PRETTY_FUNCTION__))
;
1359 // Intel Vulkan drivers (up to current 0.401.3889) have a problem
1360 // with SkBlendMode::kDifference(?) and surfaces wider than 1024 pixels, resulting
1361 // in drawing errors. Work that around by fetching the relevant part of the surface
1362 // and drawing using CPU.
1363 bool intelHack
1364 = (isGPU() && SkiaHelper::getVendor() == DriverBlocklist::VendorIntel && !mXorMode);
1365 // TrackFrame just inverts a dashed path around the polygon
1366 if (eFlags == SalInvert::TrackFrame)
1367 {
1368 SkPath aPath;
1369 addPolygonToPath(rPoly, aPath);
1370 aPath.setFillType(SkPathFillType::kEvenOdd);
1371 // TrackFrame is not supposed to paint outside of the polygon (usually rectangle),
1372 // but wider stroke width usually results in that, so ensure the requirement
1373 // by clipping.
1374 SkAutoCanvasRestore autoRestore(getDrawCanvas(), true);
1375 getDrawCanvas()->clipRect(aPath.getBounds(), SkClipOp::kIntersect, false);
1376 SkPaint aPaint;
1377 aPaint.setStrokeWidth(2);
1378 float intervals[] = { 4.0f, 4.0f };
1379 aPaint.setStyle(SkPaint::kStroke_Style);
1380 aPaint.setPathEffect(SkDashPathEffect::Make(intervals, SK_ARRAY_COUNT(intervals)(sizeof(SkArrayCountHelper(intervals))), 0));
1381 aPaint.setColor(SkColorSetARGB(255, 255, 255, 255));
1382 aPaint.setBlendMode(SkBlendMode::kDifference);
1383 if (!intelHack)
1384 getDrawCanvas()->drawPath(aPath, aPaint);
1385 else
1386 {
1387 SkRect area;
1388 aPath.getBounds().roundOut(&area);
1389 SkRect size = SkRect::MakeWH(area.width(), area.height());
1390 sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(area.width(), area.height());
1391 SkPaint copy;
1392 copy.setBlendMode(SkBlendMode::kSrc);
1393 flushDrawing();
1394 surface->getCanvas()->drawImageRect(SkiaHelper::makeCheckedImageSnapshot(mSurface),
1395 area, size, &copy);
1396 aPath.offset(-area.x(), -area.y());
1397 surface->getCanvas()->drawPath(aPath, aPaint);
1398 getDrawCanvas()->drawImageRect(SkiaHelper::makeCheckedImageSnapshot(surface), size,
1399 area, &copy);
1400 }
1401 }
1402 else
1403 {
1404 SkPath aPath;
1405 addPolygonToPath(rPoly, aPath);
1406 aPath.setFillType(SkPathFillType::kEvenOdd);
1407 SkPaint aPaint;
1408 aPaint.setColor(SkColorSetARGB(255, 255, 255, 255));
1409 aPaint.setStyle(SkPaint::kFill_Style);
1410 aPaint.setBlendMode(SkBlendMode::kDifference);
1411
1412 // N50 inverts in checker pattern
1413 if (eFlags == SalInvert::N50)
1414 {
1415 // This creates 2x2 checker pattern bitmap
1416 // TODO Use SkiaHelper::createSkSurface() and cache the image
1417 SkBitmap aBitmap;
1418 aBitmap.allocN32Pixels(2, 2);
1419 const SkPMColor white = SkPreMultiplyARGB(0xFF, 0xFF, 0xFF, 0xFF);
1420 const SkPMColor black = SkPreMultiplyARGB(0xFF, 0x00, 0x00, 0x00);
1421 SkPMColor* scanline;
1422 scanline = aBitmap.getAddr32(0, 0);
1423 *scanline++ = white;
1424 *scanline++ = black;
1425 scanline = aBitmap.getAddr32(0, 1);
1426 *scanline++ = black;
1427 *scanline++ = white;
1428 aBitmap.setImmutable();
1429 // The bitmap is repeated in both directions the checker pattern is as big
1430 // as the polygon (usually rectangle)
1431 aPaint.setShader(aBitmap.makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat));
1432 }
1433 if (!intelHack)
1434 getDrawCanvas()->drawPath(aPath, aPaint);
1435 else
1436 {
1437 SkRect area;
1438 aPath.getBounds().roundOut(&area);
1439 SkRect size = SkRect::MakeWH(area.width(), area.height());
1440 sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(area.width(), area.height());
1441 SkPaint copy;
1442 copy.setBlendMode(SkBlendMode::kSrc);
1443 flushDrawing();
1444 surface->getCanvas()->drawImageRect(SkiaHelper::makeCheckedImageSnapshot(mSurface),
1445 area, size, &copy);
1446 aPath.offset(-area.x(), -area.y());
1447 surface->getCanvas()->drawPath(aPath, aPaint);
1448 getDrawCanvas()->drawImageRect(SkiaHelper::makeCheckedImageSnapshot(surface), size,
1449 area, &copy);
1450 }
1451 }
1452 postDraw();
1453}
1454
1455void SkiaSalGraphicsImpl::invert(long nX, long nY, long nWidth, long nHeight, SalInvert eFlags)
1456{
1457 basegfx::B2DRectangle aRectangle(nX, nY, nX + nWidth, nY + nHeight);
1458 auto aRect = basegfx::utils::createPolygonFromRect(aRectangle);
1459 invert(aRect, eFlags);
1460}
1461
1462void SkiaSalGraphicsImpl::invert(sal_uInt32 nPoints, const SalPoint* pPointArray, SalInvert eFlags)
1463{
1464 basegfx::B2DPolygon aPolygon;
1465 aPolygon.append(basegfx::B2DPoint(pPointArray[0].mnX, pPointArray[0].mnY), nPoints);
1466 for (sal_uInt32 i = 1; i < nPoints; ++i)
1467 {
1468 aPolygon.setB2DPoint(i, basegfx::B2DPoint(pPointArray[i].mnX, pPointArray[i].mnY));
1469 }
1470 aPolygon.setClosed(true);
1471
1472 invert(aPolygon, eFlags);
1473}
1474
1475bool SkiaSalGraphicsImpl::drawEPS(long, long, long, long, void*, sal_uInt32) { return false; }
1476
1477// Create SkImage from a bitmap and possibly an alpha mask (the usual VCL one-minus-alpha),
1478// with the given target size. Result will be possibly cached, unless disabled.
1479sk_sp<SkImage> SkiaSalGraphicsImpl::mergeCacheBitmaps(const SkiaSalBitmap& bitmap,
1480 const SkiaSalBitmap* alphaBitmap,
1481 const Size targetSize)
1482{
1483 sk_sp<SkImage> image;
1484 // GPU-accelerated drawing with SkShader should be fast enough to not need caching.
1485 if (isGPU())
1486 return image;
1487 if (targetSize.IsEmpty())
1488 return image;
1489 if (alphaBitmap && alphaBitmap->IsFullyOpaqueAsAlpha())
1490 alphaBitmap = nullptr; // the alpha can be ignored
1491 // Probably not much point in caching of just doing a copy.
1492 if (alphaBitmap == nullptr && targetSize == bitmap.GetSize())
1493 return image;
1494 // Image too small to be worth caching if not scaling.
1495 if (targetSize == bitmap.GetSize() && targetSize.Width() < 100 && targetSize.Height() < 100)
1496 return image;
1497 // In some cases (tdf#134237) the target size may be very large. In that case it's
1498 // better to rely on Skia to clip and draw only the necessary, rather than prepare
1499 // a very large image only to not use most of it.
1500 const Size drawAreaSize = mClipRegion.GetBoundRect().GetSize();
1501 if (targetSize.Width() > drawAreaSize.Width() || targetSize.Height() > drawAreaSize.Height())
1502 {
1503 // This is a bit tricky. The condition above just checks that at least a part of the resulting
1504 // image will not be used (it's larger then our drawing area). But this may often happen
1505 // when just scrolling a document with a large image, where the caching may very well be worth it.
1506 // Since the problem is mainly the cost of upscaling and then the size of the resulting bitmap,
1507 // compute a ratio of how much this is going to be scaled up, how much this is larger than
1508 // the drawing area, and then refuse to cache if it's too much.
1509 const double upscaleRatio
1510 = std::max(1.0, 1.0 * targetSize.Width() / bitmap.GetSize().Width()
1511 * targetSize.Height() / bitmap.GetSize().Height());
1512 const double oversizeRatio = 1.0 * targetSize.Width() / drawAreaSize.Width()
1513 * targetSize.Height() / drawAreaSize.Height();
1514 const double ratio = upscaleRatio * oversizeRatio;
1515 if (ratio > 4)
1516 {
1517 SAL_INFO("vcl.skia.trace", "mergecachebitmaps("do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_INFO
, "vcl.skia.trace")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break
; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "mergecachebitmaps(" <<
this << "): not caching, ratio:" << ratio <<
", " << bitmap.GetSize() << "->" << targetSize
<< " in " << drawAreaSize) == 1) { ::sal_detail_log
( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1520" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "mergecachebitmaps(" << this <<
"): not caching, ratio:" << ratio << ", " <<
bitmap.GetSize() << "->" << targetSize <<
" in " << drawAreaSize), 0); } else { ::std::ostringstream
sal_detail_stream; sal_detail_stream << "mergecachebitmaps("
<< this << "): not caching, ratio:" << ratio
<< ", " << bitmap.GetSize() << "->" <<
targetSize << " in " << drawAreaSize; ::sal::detail
::log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1520" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "mergecachebitmaps(" << this << "): not caching, ratio:"
<< ratio << ", " << bitmap.GetSize() <<
"->" << targetSize << " in " << drawAreaSize
) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"
), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1520" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "mergecachebitmaps(" << this <<
"): not caching, ratio:" << ratio << ", " <<
bitmap.GetSize() << "->" << targetSize <<
" in " << drawAreaSize), 0); } else { ::std::ostringstream
sal_detail_stream; sal_detail_stream << "mergecachebitmaps("
<< this << "): not caching, ratio:" << ratio
<< ", " << bitmap.GetSize() << "->" <<
targetSize << " in " << drawAreaSize; ::sal::detail
::log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1520" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
1518 << this << "): not caching, ratio:" << ratio << ", "do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_INFO
, "vcl.skia.trace")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break
; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "mergecachebitmaps(" <<
this << "): not caching, ratio:" << ratio <<
", " << bitmap.GetSize() << "->" << targetSize
<< " in " << drawAreaSize) == 1) { ::sal_detail_log
( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1520" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "mergecachebitmaps(" << this <<
"): not caching, ratio:" << ratio << ", " <<
bitmap.GetSize() << "->" << targetSize <<
" in " << drawAreaSize), 0); } else { ::std::ostringstream
sal_detail_stream; sal_detail_stream << "mergecachebitmaps("
<< this << "): not caching, ratio:" << ratio
<< ", " << bitmap.GetSize() << "->" <<
targetSize << " in " << drawAreaSize; ::sal::detail
::log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1520" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "mergecachebitmaps(" << this << "): not caching, ratio:"
<< ratio << ", " << bitmap.GetSize() <<
"->" << targetSize << " in " << drawAreaSize
) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"
), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1520" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "mergecachebitmaps(" << this <<
"): not caching, ratio:" << ratio << ", " <<
bitmap.GetSize() << "->" << targetSize <<
" in " << drawAreaSize), 0); } else { ::std::ostringstream
sal_detail_stream; sal_detail_stream << "mergecachebitmaps("
<< this << "): not caching, ratio:" << ratio
<< ", " << bitmap.GetSize() << "->" <<
targetSize << " in " << drawAreaSize; ::sal::detail
::log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1520" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
1519 << bitmap.GetSize() << "->" << targetSize << " in "do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_INFO
, "vcl.skia.trace")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break
; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "mergecachebitmaps(" <<
this << "): not caching, ratio:" << ratio <<
", " << bitmap.GetSize() << "->" << targetSize
<< " in " << drawAreaSize) == 1) { ::sal_detail_log
( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1520" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "mergecachebitmaps(" << this <<
"): not caching, ratio:" << ratio << ", " <<
bitmap.GetSize() << "->" << targetSize <<
" in " << drawAreaSize), 0); } else { ::std::ostringstream
sal_detail_stream; sal_detail_stream << "mergecachebitmaps("
<< this << "): not caching, ratio:" << ratio
<< ", " << bitmap.GetSize() << "->" <<
targetSize << " in " << drawAreaSize; ::sal::detail
::log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1520" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "mergecachebitmaps(" << this << "): not caching, ratio:"
<< ratio << ", " << bitmap.GetSize() <<
"->" << targetSize << " in " << drawAreaSize
) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"
), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1520" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "mergecachebitmaps(" << this <<
"): not caching, ratio:" << ratio << ", " <<
bitmap.GetSize() << "->" << targetSize <<
" in " << drawAreaSize), 0); } else { ::std::ostringstream
sal_detail_stream; sal_detail_stream << "mergecachebitmaps("
<< this << "): not caching, ratio:" << ratio
<< ", " << bitmap.GetSize() << "->" <<
targetSize << " in " << drawAreaSize; ::sal::detail
::log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1520" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
1520 << drawAreaSize)do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_INFO
, "vcl.skia.trace")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break
; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "mergecachebitmaps(" <<
this << "): not caching, ratio:" << ratio <<
", " << bitmap.GetSize() << "->" << targetSize
<< " in " << drawAreaSize) == 1) { ::sal_detail_log
( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1520" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "mergecachebitmaps(" << this <<
"): not caching, ratio:" << ratio << ", " <<
bitmap.GetSize() << "->" << targetSize <<
" in " << drawAreaSize), 0); } else { ::std::ostringstream
sal_detail_stream; sal_detail_stream << "mergecachebitmaps("
<< this << "): not caching, ratio:" << ratio
<< ", " << bitmap.GetSize() << "->" <<
targetSize << " in " << drawAreaSize; ::sal::detail
::log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1520" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "mergecachebitmaps(" << this << "): not caching, ratio:"
<< ratio << ", " << bitmap.GetSize() <<
"->" << targetSize << " in " << drawAreaSize
) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"
), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1520" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "mergecachebitmaps(" << this <<
"): not caching, ratio:" << ratio << ", " <<
bitmap.GetSize() << "->" << targetSize <<
" in " << drawAreaSize), 0); } else { ::std::ostringstream
sal_detail_stream; sal_detail_stream << "mergecachebitmaps("
<< this << "): not caching, ratio:" << ratio
<< ", " << bitmap.GetSize() << "->" <<
targetSize << " in " << drawAreaSize; ::sal::detail
::log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1520" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
;
1521 return image;
1522 }
1523 }
1524 // Do not cache the result if it would take most of the cache and thus get evicted soon.
1525 if (targetSize.Width() * targetSize.Height() * 4 > SkiaHelper::MAX_CACHE_SIZE * 0.7)
1526 return image;
1527 OString key;
1528 OStringBuffer keyBuf;
1529 keyBuf.append(targetSize.Width())
1530 .append("x")
1531 .append(targetSize.Height())
1532 .append("_")
1533 .append(bitmap.GetImageKey());
1534 if (alphaBitmap)
1535 keyBuf.append("_").append(alphaBitmap->GetAlphaImageKey());
1536 key = keyBuf.makeStringAndClear();
1537 image = SkiaHelper::findCachedImage(key);
1538 if (image)
1539 {
1540 assert(image->width() == targetSize.Width() && image->height() == targetSize.Height())(static_cast <bool> (image->width() == targetSize.Width
() && image->height() == targetSize.Height()) ? void
(0) : __assert_fail ("image->width() == targetSize.Width() && image->height() == targetSize.Height()"
, "/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx", 1540
, __extension__ __PRETTY_FUNCTION__))
;
1541 return image;
1542 }
1543 sk_sp<SkSurface> tmpSurface = SkiaHelper::createSkSurface(targetSize);
1544 if (!tmpSurface)
1545 return nullptr;
1546 SkCanvas* canvas = tmpSurface->getCanvas();
1547 SkAutoCanvasRestore autoRestore(canvas, true);
1548 SkPaint paint;
1549 if (targetSize != bitmap.GetSize())
1550 {
1551 SkMatrix matrix;
1552 matrix.set(SkMatrix::kMScaleX, 1.0 * targetSize.Width() / bitmap.GetSize().Width());
1553 matrix.set(SkMatrix::kMScaleY, 1.0 * targetSize.Height() / bitmap.GetSize().Height());
1554 canvas->concat(matrix);
1555 paint.setFilterQuality(kHigh_SkFilterQuality);
1556 }
1557 if (alphaBitmap != nullptr)
1558 {
1559 canvas->clear(SK_ColorTRANSPARENT);
1560 paint.setShader(SkShaders::Blend(SkBlendMode::kDstOut, bitmap.GetSkShader(),
1561 alphaBitmap->GetAlphaSkShader()));
1562 canvas->drawPaint(paint);
1563 }
1564 else if (bitmap.PreferSkShader())
1565 {
1566 paint.setShader(bitmap.GetSkShader());
1567 canvas->drawPaint(paint);
1568 }
1569 else
1570 canvas->drawImage(bitmap.GetSkImage(), 0, 0, &paint);
1571 image = SkiaHelper::makeCheckedImageSnapshot(tmpSurface);
1572 SkiaHelper::addCachedImage(key, image);
1573 return image;
1574}
1575
1576bool SkiaSalGraphicsImpl::drawAlphaBitmap(const SalTwoRect& rPosAry, const SalBitmap& rSourceBitmap,
1577 const SalBitmap& rAlphaBitmap)
1578{
1579 assert(dynamic_cast<const SkiaSalBitmap*>(&rSourceBitmap))(static_cast <bool> (dynamic_cast<const SkiaSalBitmap
*>(&rSourceBitmap)) ? void (0) : __assert_fail ("dynamic_cast<const SkiaSalBitmap*>(&rSourceBitmap)"
, "/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx", 1579
, __extension__ __PRETTY_FUNCTION__))
;
1580 assert(dynamic_cast<const SkiaSalBitmap*>(&rAlphaBitmap))(static_cast <bool> (dynamic_cast<const SkiaSalBitmap
*>(&rAlphaBitmap)) ? void (0) : __assert_fail ("dynamic_cast<const SkiaSalBitmap*>(&rAlphaBitmap)"
, "/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx", 1580
, __extension__ __PRETTY_FUNCTION__))
;
1581 const SkiaSalBitmap& rSkiaSourceBitmap = static_cast<const SkiaSalBitmap&>(rSourceBitmap);
1582 const SkiaSalBitmap& rSkiaAlphaBitmap = static_cast<const SkiaSalBitmap&>(rAlphaBitmap);
1583 // In raster mode use mergeCacheBitmaps(), which will cache the result, avoiding repeated
1584 // alpha blending or scaling. In GPU mode it is simpler to just use SkShader.
1585 SalTwoRect imagePosAry(rPosAry);
1586 Size imageSize = rSourceBitmap.GetSize();
1587 // If the bitmap will be scaled, prefer to do it in mergeCacheBitmaps(), if possible.
1588 if ((rPosAry.mnSrcWidth != rPosAry.mnDestWidth || rPosAry.mnSrcHeight != rPosAry.mnDestHeight)
1589 && rPosAry.mnSrcX == 0 && rPosAry.mnSrcY == 0
1590 && rPosAry.mnSrcWidth == rSourceBitmap.GetSize().Width()
1591 && rPosAry.mnSrcHeight == rSourceBitmap.GetSize().Height())
1592 {
1593 imagePosAry.mnSrcWidth = imagePosAry.mnDestWidth;
1594 imagePosAry.mnSrcHeight = imagePosAry.mnDestHeight;
1595 imageSize = Size(imagePosAry.mnSrcWidth, imagePosAry.mnSrcHeight);
1596 }
1597 sk_sp<SkImage> image = mergeCacheBitmaps(rSkiaSourceBitmap, &rSkiaAlphaBitmap, imageSize);
1598 if (image)
1599 drawImage(imagePosAry, image);
1600 else if (rSkiaAlphaBitmap.IsFullyOpaqueAsAlpha()) // alpha can be ignored
1601 drawBitmap(rPosAry, rSkiaSourceBitmap);
1602 else
1603 drawShader(rPosAry,
1604 SkShaders::Blend(SkBlendMode::kDstOut, // VCL alpha is one-minus-alpha.
1605 rSkiaSourceBitmap.GetSkShader(),
1606 rSkiaAlphaBitmap.GetAlphaSkShader()));
1607 return true;
1608}
1609
1610void SkiaSalGraphicsImpl::drawBitmap(const SalTwoRect& rPosAry, const SkiaSalBitmap& bitmap,
1611 SkBlendMode blendMode)
1612{
1613 if (bitmap.PreferSkShader())
1614 drawShader(rPosAry, bitmap.GetSkShader(), blendMode);
1615 else
1616 drawImage(rPosAry, bitmap.GetSkImage(), blendMode);
1617}
1618
1619void SkiaSalGraphicsImpl::drawImage(const SalTwoRect& rPosAry, const sk_sp<SkImage>& aImage,
1620 SkBlendMode eBlendMode)
1621{
1622 SkRect aSourceRect
1623 = SkRect::MakeXYWH(rPosAry.mnSrcX, rPosAry.mnSrcY, rPosAry.mnSrcWidth, rPosAry.mnSrcHeight);
1624 SkRect aDestinationRect = SkRect::MakeXYWH(rPosAry.mnDestX, rPosAry.mnDestY,
1625 rPosAry.mnDestWidth, rPosAry.mnDestHeight);
1626
1627 SkPaint aPaint;
1628 aPaint.setBlendMode(eBlendMode);
1629 if (rPosAry.mnSrcWidth != rPosAry.mnDestWidth || rPosAry.mnSrcHeight != rPosAry.mnDestHeight)
1630 aPaint.setFilterQuality(kHigh_SkFilterQuality);
1631
1632 preDraw();
1633 SAL_INFO("vcl.skia.trace",do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_INFO
, "vcl.skia.trace")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break
; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "drawimage(" <<
this << "): " << rPosAry << ":" << SkBlendMode_Name
(eBlendMode)) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1634" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "drawimage(" << this << "): "
<< rPosAry << ":" << SkBlendMode_Name(eBlendMode
)), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "drawimage(" << this << "): " << rPosAry
<< ":" << SkBlendMode_Name(eBlendMode); ::sal::detail
::log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1634" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "drawimage(" << this << "): " <<
rPosAry << ":" << SkBlendMode_Name(eBlendMode)) ==
1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"
), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1634" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "drawimage(" << this << "): "
<< rPosAry << ":" << SkBlendMode_Name(eBlendMode
)), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "drawimage(" << this << "): " << rPosAry
<< ":" << SkBlendMode_Name(eBlendMode); ::sal::detail
::log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1634" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
1634 "drawimage(" << this << "): " << rPosAry << ":" << SkBlendMode_Name(eBlendMode))do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_INFO
, "vcl.skia.trace")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break
; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "drawimage(" <<
this << "): " << rPosAry << ":" << SkBlendMode_Name
(eBlendMode)) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1634" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "drawimage(" << this << "): "
<< rPosAry << ":" << SkBlendMode_Name(eBlendMode
)), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "drawimage(" << this << "): " << rPosAry
<< ":" << SkBlendMode_Name(eBlendMode); ::sal::detail
::log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1634" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "drawimage(" << this << "): " <<
rPosAry << ":" << SkBlendMode_Name(eBlendMode)) ==
1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"
), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1634" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "drawimage(" << this << "): "
<< rPosAry << ":" << SkBlendMode_Name(eBlendMode
)), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "drawimage(" << this << "): " << rPosAry
<< ":" << SkBlendMode_Name(eBlendMode); ::sal::detail
::log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1634" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
;
1635 addXorRegion(aDestinationRect);
1636 getDrawCanvas()->drawImageRect(aImage, aSourceRect, aDestinationRect, &aPaint);
1637 ++mPendingOperationsToFlush; // tdf#136369
1638 postDraw();
1639}
1640
1641// SkShader can be used to merge multiple bitmaps with appropriate blend modes (e.g. when
1642// merging a bitmap with its alpha mask).
1643void SkiaSalGraphicsImpl::drawShader(const SalTwoRect& rPosAry, const sk_sp<SkShader>& shader,
1644 SkBlendMode blendMode)
1645{
1646 preDraw();
1647 SAL_INFO("vcl.skia.trace", "drawshader(" << this << "): " << rPosAry)do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_INFO
, "vcl.skia.trace")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break
; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "drawshader(" <<
this << "): " << rPosAry) == 1) { ::sal_detail_log
( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1647" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "drawshader(" << this <<
"): " << rPosAry), 0); } else { ::std::ostringstream sal_detail_stream
; sal_detail_stream << "drawshader(" << this <<
"): " << rPosAry; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1647" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "drawshader(" << this << "): " <<
rPosAry) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1647" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "drawshader(" << this <<
"): " << rPosAry), 0); } else { ::std::ostringstream sal_detail_stream
; sal_detail_stream << "drawshader(" << this <<
"): " << rPosAry; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1647" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
;
1648 SkRect destinationRect = SkRect::MakeXYWH(rPosAry.mnDestX, rPosAry.mnDestY, rPosAry.mnDestWidth,
1649 rPosAry.mnDestHeight);
1650 addXorRegion(destinationRect);
1651 SkPaint paint;
1652 paint.setBlendMode(blendMode);
1653 paint.setShader(shader);
1654 if (rPosAry.mnSrcWidth != rPosAry.mnDestWidth || rPosAry.mnSrcHeight != rPosAry.mnDestHeight)
1655 paint.setFilterQuality(kHigh_SkFilterQuality);
1656 SkCanvas* canvas = getDrawCanvas();
1657 // Scaling needs to be done explicitly using a matrix.
1658 SkAutoCanvasRestore autoRestore(canvas, true);
1659 SkMatrix matrix = SkMatrix::Translate(rPosAry.mnDestX, rPosAry.mnDestY)
1660 * SkMatrix::Scale(1.0 * rPosAry.mnDestWidth / rPosAry.mnSrcWidth,
1661 1.0 * rPosAry.mnDestHeight / rPosAry.mnSrcHeight)
1662 * SkMatrix::Translate(-rPosAry.mnSrcX, -rPosAry.mnSrcY);
1663 assert(matrix.mapXY(rPosAry.mnSrcX, rPosAry.mnSrcY)(static_cast <bool> (matrix.mapXY(rPosAry.mnSrcX, rPosAry
.mnSrcY) == SkPoint::Make(rPosAry.mnDestX, rPosAry.mnDestY)) ?
void (0) : __assert_fail ("matrix.mapXY(rPosAry.mnSrcX, rPosAry.mnSrcY) == SkPoint::Make(rPosAry.mnDestX, rPosAry.mnDestY)"
, "/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx", 1664
, __extension__ __PRETTY_FUNCTION__))
1664 == SkPoint::Make(rPosAry.mnDestX, rPosAry.mnDestY))(static_cast <bool> (matrix.mapXY(rPosAry.mnSrcX, rPosAry
.mnSrcY) == SkPoint::Make(rPosAry.mnDestX, rPosAry.mnDestY)) ?
void (0) : __assert_fail ("matrix.mapXY(rPosAry.mnSrcX, rPosAry.mnSrcY) == SkPoint::Make(rPosAry.mnDestX, rPosAry.mnDestY)"
, "/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx", 1664
, __extension__ __PRETTY_FUNCTION__))
;
1665 assert(matrix.mapXY(rPosAry.mnSrcX + rPosAry.mnSrcWidth, rPosAry.mnSrcY + rPosAry.mnSrcHeight)(static_cast <bool> (matrix.mapXY(rPosAry.mnSrcX + rPosAry
.mnSrcWidth, rPosAry.mnSrcY + rPosAry.mnSrcHeight) == SkPoint
::Make(rPosAry.mnDestX + rPosAry.mnDestWidth, rPosAry.mnDestY
+ rPosAry.mnDestHeight)) ? void (0) : __assert_fail ("matrix.mapXY(rPosAry.mnSrcX + rPosAry.mnSrcWidth, rPosAry.mnSrcY + rPosAry.mnSrcHeight) == SkPoint::Make(rPosAry.mnDestX + rPosAry.mnDestWidth, rPosAry.mnDestY + rPosAry.mnDestHeight)"
, "/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx", 1667
, __extension__ __PRETTY_FUNCTION__))
1666 == SkPoint::Make(rPosAry.mnDestX + rPosAry.mnDestWidth,(static_cast <bool> (matrix.mapXY(rPosAry.mnSrcX + rPosAry
.mnSrcWidth, rPosAry.mnSrcY + rPosAry.mnSrcHeight) == SkPoint
::Make(rPosAry.mnDestX + rPosAry.mnDestWidth, rPosAry.mnDestY
+ rPosAry.mnDestHeight)) ? void (0) : __assert_fail ("matrix.mapXY(rPosAry.mnSrcX + rPosAry.mnSrcWidth, rPosAry.mnSrcY + rPosAry.mnSrcHeight) == SkPoint::Make(rPosAry.mnDestX + rPosAry.mnDestWidth, rPosAry.mnDestY + rPosAry.mnDestHeight)"
, "/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx", 1667
, __extension__ __PRETTY_FUNCTION__))
1667 rPosAry.mnDestY + rPosAry.mnDestHeight))(static_cast <bool> (matrix.mapXY(rPosAry.mnSrcX + rPosAry
.mnSrcWidth, rPosAry.mnSrcY + rPosAry.mnSrcHeight) == SkPoint
::Make(rPosAry.mnDestX + rPosAry.mnDestWidth, rPosAry.mnDestY
+ rPosAry.mnDestHeight)) ? void (0) : __assert_fail ("matrix.mapXY(rPosAry.mnSrcX + rPosAry.mnSrcWidth, rPosAry.mnSrcY + rPosAry.mnSrcHeight) == SkPoint::Make(rPosAry.mnDestX + rPosAry.mnDestWidth, rPosAry.mnDestY + rPosAry.mnDestHeight)"
, "/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx", 1667
, __extension__ __PRETTY_FUNCTION__))
;
1668 canvas->concat(matrix);
1669 SkRect sourceRect
1670 = SkRect::MakeXYWH(rPosAry.mnSrcX, rPosAry.mnSrcY, rPosAry.mnSrcWidth, rPosAry.mnSrcHeight);
1671 canvas->drawRect(sourceRect, paint);
1672 postDraw();
1673}
1674
1675bool SkiaSalGraphicsImpl::drawTransformedBitmap(const basegfx::B2DPoint& rNull,
1676 const basegfx::B2DPoint& rX,
1677 const basegfx::B2DPoint& rY,
1678 const SalBitmap& rSourceBitmap,
1679 const SalBitmap* pAlphaBitmap)
1680{
1681 assert(dynamic_cast<const SkiaSalBitmap*>(&rSourceBitmap))(static_cast <bool> (dynamic_cast<const SkiaSalBitmap
*>(&rSourceBitmap)) ? void (0) : __assert_fail ("dynamic_cast<const SkiaSalBitmap*>(&rSourceBitmap)"
, "/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx", 1681
, __extension__ __PRETTY_FUNCTION__))
;
1682 assert(!pAlphaBitmap || dynamic_cast<const SkiaSalBitmap*>(pAlphaBitmap))(static_cast <bool> (!pAlphaBitmap || dynamic_cast<const
SkiaSalBitmap*>(pAlphaBitmap)) ? void (0) : __assert_fail
("!pAlphaBitmap || dynamic_cast<const SkiaSalBitmap*>(pAlphaBitmap)"
, "/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx", 1682
, __extension__ __PRETTY_FUNCTION__))
;
1683
1684 const SkiaSalBitmap& rSkiaBitmap = static_cast<const SkiaSalBitmap&>(rSourceBitmap);
1685 const SkiaSalBitmap* pSkiaAlphaBitmap = static_cast<const SkiaSalBitmap*>(pAlphaBitmap);
1686
1687 if (pSkiaAlphaBitmap && pSkiaAlphaBitmap->IsFullyOpaqueAsAlpha())
1688 pSkiaAlphaBitmap = nullptr; // the alpha can be ignored
1689
1690 // Setup the image transformation,
1691 // using the rNull, rX, rY points as destinations for the (0,0), (Width,0), (0,Height) source points.
1692 const basegfx::B2DVector aXRel = rX - rNull;
1693 const basegfx::B2DVector aYRel = rY - rNull;
1694
1695 preDraw();
1696 SAL_INFO("vcl.skia.trace", "drawtransformedbitmap(" << this << "): " << rSourceBitmap.GetSize()do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_INFO
, "vcl.skia.trace")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break
; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "drawtransformedbitmap("
<< this << "): " << rSourceBitmap.GetSize(
) << " " << rNull << ":" << rX <<
":" << rY) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1697" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "drawtransformedbitmap(" << this
<< "): " << rSourceBitmap.GetSize() << " "
<< rNull << ":" << rX << ":" <<
rY), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "drawtransformedbitmap(" << this << "): "
<< rSourceBitmap.GetSize() << " " << rNull
<< ":" << rX << ":" << rY; ::sal::detail
::log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1697" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "drawtransformedbitmap(" << this << "): "
<< rSourceBitmap.GetSize() << " " << rNull
<< ":" << rX << ":" << rY) == 1) { ::
sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"
), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1697" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "drawtransformedbitmap(" << this
<< "): " << rSourceBitmap.GetSize() << " "
<< rNull << ":" << rX << ":" <<
rY), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "drawtransformedbitmap(" << this << "): "
<< rSourceBitmap.GetSize() << " " << rNull
<< ":" << rX << ":" << rY; ::sal::detail
::log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1697" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
1697 << " " << rNull << ":" << rX << ":" << rY)do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_INFO
, "vcl.skia.trace")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break
; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "drawtransformedbitmap("
<< this << "): " << rSourceBitmap.GetSize(
) << " " << rNull << ":" << rX <<
":" << rY) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1697" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "drawtransformedbitmap(" << this
<< "): " << rSourceBitmap.GetSize() << " "
<< rNull << ":" << rX << ":" <<
rY), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "drawtransformedbitmap(" << this << "): "
<< rSourceBitmap.GetSize() << " " << rNull
<< ":" << rX << ":" << rY; ::sal::detail
::log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1697" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "drawtransformedbitmap(" << this << "): "
<< rSourceBitmap.GetSize() << " " << rNull
<< ":" << rX << ":" << rY) == 1) { ::
sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"
), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1697" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "drawtransformedbitmap(" << this
<< "): " << rSourceBitmap.GetSize() << " "
<< rNull << ":" << rX << ":" <<
rY), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "drawtransformedbitmap(" << this << "): "
<< rSourceBitmap.GetSize() << " " << rNull
<< ":" << rX << ":" << rY; ::sal::detail
::log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1697" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
;
1698
1699 addXorRegion(SkRect::MakeWH(GetWidth(), GetHeight())); // can't tell, use whole area
1700 // In raster mode scaling and alpha blending is still somewhat expensive if done repeatedly,
1701 // so use mergeCacheBitmaps(), which will cache the result if useful.
1702 // It is better to use SkShader if in GPU mode, if the operation is simple or if the temporary
1703 // image would be very large.
1704 sk_sp<SkImage> imageToDraw = mergeCacheBitmaps(
1705 rSkiaBitmap, pSkiaAlphaBitmap, Size(round(aXRel.getLength()), round(aYRel.getLength())));
1706 if (imageToDraw)
1707 {
1708 SkMatrix matrix;
1709 // Round sizes for scaling, so that sub-pixel differences don't
1710 // trigger unnecessary scaling. Image has already been scaled
1711 // by mergeCacheBitmaps() and we shouldn't scale here again
1712 // unless the drawing is also skewed.
1713 matrix.set(SkMatrix::kMScaleX, round(aXRel.getX()) / imageToDraw->width());
1714 matrix.set(SkMatrix::kMScaleY, round(aYRel.getY()) / imageToDraw->height());
1715 matrix.set(SkMatrix::kMSkewY, aXRel.getY() / imageToDraw->width());
1716 matrix.set(SkMatrix::kMSkewX, aYRel.getX() / imageToDraw->height());
1717 matrix.set(SkMatrix::kMTransX, rNull.getX());
1718 matrix.set(SkMatrix::kMTransY, rNull.getY());
1719 SkCanvas* canvas = getDrawCanvas();
1720 SkAutoCanvasRestore autoRestore(canvas, true);
1721 canvas->concat(matrix);
1722 SkPaint paint;
1723 if (!matrix.isTranslate())
1724 paint.setFilterQuality(kHigh_SkFilterQuality);
1725 canvas->drawImage(imageToDraw, 0, 0, &paint);
1726 }
1727 else
1728 {
1729 SkMatrix matrix;
1730 const Size aSize = rSourceBitmap.GetSize();
1731 matrix.set(SkMatrix::kMScaleX, aXRel.getX() / aSize.Width());
1732 matrix.set(SkMatrix::kMScaleY, aYRel.getY() / aSize.Height());
1733 matrix.set(SkMatrix::kMSkewY, aXRel.getY() / aSize.Width());
1734 matrix.set(SkMatrix::kMSkewX, aYRel.getX() / aSize.Height());
1735 matrix.set(SkMatrix::kMTransX, rNull.getX());
1736 matrix.set(SkMatrix::kMTransY, rNull.getY());
1737 SkCanvas* canvas = getDrawCanvas();
1738 SkAutoCanvasRestore autoRestore(canvas, true);
1739 canvas->concat(matrix);
1740 SkPaint paint;
1741 if (!matrix.isTranslate())
1742 paint.setFilterQuality(kHigh_SkFilterQuality);
1743 if (pSkiaAlphaBitmap)
1744 {
1745 paint.setShader(SkShaders::Blend(SkBlendMode::kDstOut, // VCL alpha is one-minus-alpha.
1746 rSkiaBitmap.GetSkShader(),
1747 pSkiaAlphaBitmap->GetAlphaSkShader()));
1748 canvas->drawRect(SkRect::MakeWH(aSize.Width(), aSize.Height()), paint);
1749 }
1750 else if (rSkiaBitmap.PreferSkShader())
1751 {
1752 paint.setShader(rSkiaBitmap.GetSkShader());
1753 canvas->drawRect(SkRect::MakeWH(aSize.Width(), aSize.Height()), paint);
1754 }
1755 else
1756 {
1757 canvas->drawImage(rSkiaBitmap.GetSkImage(), 0, 0, &paint);
1758 }
1759 }
1760 postDraw();
1761 return true;
1762}
1763
1764bool SkiaSalGraphicsImpl::drawAlphaRect(long nX, long nY, long nWidth, long nHeight,
1765 sal_uInt8 nTransparency)
1766{
1767 privateDrawAlphaRect(nX, nY, nWidth, nHeight, nTransparency / 100.0);
1768 return true;
1769}
1770
1771bool SkiaSalGraphicsImpl::drawGradient(const tools::PolyPolygon& rPolyPolygon,
1772 const Gradient& rGradient)
1773{
1774 if (rGradient.GetStyle() != GradientStyle::Linear
1775 && rGradient.GetStyle() != GradientStyle::Radial)
1776 return false; // unsupported
1777 if (rGradient.GetSteps() != 0)
1778 return false; // We can't tell Skia how many colors to use in the gradient.
1779 preDraw();
1780 SAL_INFO("vcl.skia.trace", "drawgradient(" << this << "): " << rPolyPolygon.getB2DPolyPolygon()do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_INFO
, "vcl.skia.trace")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break
; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "drawgradient(" <<
this << "): " << rPolyPolygon.getB2DPolyPolygon(
) << ":" << static_cast<int>(rGradient.GetStyle
())) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO),
("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1781" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "drawgradient(" << this <<
"): " << rPolyPolygon.getB2DPolyPolygon() << ":"
<< static_cast<int>(rGradient.GetStyle())), 0); }
else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "drawgradient(" << this << "): " <<
rPolyPolygon.getB2DPolyPolygon() << ":" << static_cast
<int>(rGradient.GetStyle()); ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1781" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "drawgradient(" << this << "): " <<
rPolyPolygon.getB2DPolyPolygon() << ":" << static_cast
<int>(rGradient.GetStyle())) == 1) { ::sal_detail_log( (
::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1781" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "drawgradient(" << this <<
"): " << rPolyPolygon.getB2DPolyPolygon() << ":"
<< static_cast<int>(rGradient.GetStyle())), 0); }
else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "drawgradient(" << this << "): " <<
rPolyPolygon.getB2DPolyPolygon() << ":" << static_cast
<int>(rGradient.GetStyle()); ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1781" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
1781 << ":" << static_cast<int>(rGradient.GetStyle()))do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_INFO
, "vcl.skia.trace")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break
; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "drawgradient(" <<
this << "): " << rPolyPolygon.getB2DPolyPolygon(
) << ":" << static_cast<int>(rGradient.GetStyle
())) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO),
("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1781" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "drawgradient(" << this <<
"): " << rPolyPolygon.getB2DPolyPolygon() << ":"
<< static_cast<int>(rGradient.GetStyle())), 0); }
else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "drawgradient(" << this << "): " <<
rPolyPolygon.getB2DPolyPolygon() << ":" << static_cast
<int>(rGradient.GetStyle()); ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1781" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "drawgradient(" << this << "): " <<
rPolyPolygon.getB2DPolyPolygon() << ":" << static_cast
<int>(rGradient.GetStyle())) == 1) { ::sal_detail_log( (
::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1781" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "drawgradient(" << this <<
"): " << rPolyPolygon.getB2DPolyPolygon() << ":"
<< static_cast<int>(rGradient.GetStyle())), 0); }
else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "drawgradient(" << this << "): " <<
rPolyPolygon.getB2DPolyPolygon() << ":" << static_cast
<int>(rGradient.GetStyle()); ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1781" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
;
1782 tools::Rectangle boundRect(rPolyPolygon.GetBoundRect());
1783 if (boundRect.IsEmpty())
1784 return true;
1785 SkPath path;
1786 if (rPolyPolygon.IsRect())
1787 {
1788 // Rect->Polygon conversion loses the right and bottom edge, fix that.
1789 path.addRect(SkRect::MakeXYWH(boundRect.getX(), boundRect.getY(), boundRect.GetWidth(),
1790 boundRect.GetHeight()));
1791 boundRect.AdjustRight(1);
1792 boundRect.AdjustBottom(1);
1793 }
1794 else
1795 addPolyPolygonToPath(rPolyPolygon.getB2DPolyPolygon(), path);
1796 path.setFillType(SkPathFillType::kEvenOdd);
1797 addXorRegion(path.getBounds());
1798
1799 Gradient aGradient(rGradient);
1800 tools::Rectangle aBoundRect;
1801 Point aCenter;
1802 aGradient.SetAngle(aGradient.GetAngle() + 2700);
1803 aGradient.GetBoundRect(boundRect, aBoundRect, aCenter);
1804
1805 SkColor startColor
1806 = toSkColorWithIntensity(rGradient.GetStartColor(), rGradient.GetStartIntensity());
1807 SkColor endColor = toSkColorWithIntensity(rGradient.GetEndColor(), rGradient.GetEndIntensity());
1808
1809 sk_sp<SkShader> shader;
1810 if (rGradient.GetStyle() == GradientStyle::Linear)
1811 {
1812 tools::Polygon aPoly(aBoundRect);
1813 aPoly.Rotate(aCenter, aGradient.GetAngle() % 3600);
1814 SkPoint points[2] = { SkPoint::Make(toSkX(aPoly[0].X()), toSkY(aPoly[0].Y())),
1815 SkPoint::Make(toSkX(aPoly[1].X()), toSkY(aPoly[1].Y())) };
1816 SkColor colors[2] = { startColor, endColor };
1817 SkScalar pos[2] = { SkDoubleToScalar(aGradient.GetBorder() / 100.0)sk_double_to_float(aGradient.GetBorder() / 100.0), 1.0 };
1818 shader = SkGradientShader::MakeLinear(points, colors, pos, 2, SkTileMode::kClamp);
1819 }
1820 else
1821 {
1822 // Move the center by (-1,-1) (the default VCL algorithm is a bit off-center that way,
1823 // Skia is the opposite way).
1824 SkPoint center = SkPoint::Make(toSkX(aCenter.X()) - 1, toSkY(aCenter.Y()) - 1);
1825 SkScalar radius = std::max(aBoundRect.GetWidth() / 2.0, aBoundRect.GetHeight() / 2.0);
1826 SkColor colors[2] = { endColor, startColor };
1827 SkScalar pos[2] = { SkDoubleToScalar(aGradient.GetBorder() / 100.0)sk_double_to_float(aGradient.GetBorder() / 100.0), 1.0 };
1828 shader = SkGradientShader::MakeRadial(center, radius, colors, pos, 2, SkTileMode::kClamp);
1829 }
1830
1831 SkPaint paint;
1832 paint.setAntiAlias(mParent.getAntiAlias());
1833 paint.setShader(shader);
1834 getDrawCanvas()->drawPath(path, paint);
1835 postDraw();
1836 return true;
1837}
1838
1839bool SkiaSalGraphicsImpl::implDrawGradient(const basegfx::B2DPolyPolygon& rPolyPolygon,
1840 const SalGradient& rGradient)
1841{
1842 preDraw();
1843 SAL_INFO("vcl.skia.trace",do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_INFO
, "vcl.skia.trace")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break
; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "impldrawgradient(" <<
this << "): " << rPolyPolygon << ":" <<
rGradient.maPoint1 << "->" << rGradient.maPoint2
<< ":" << rGradient.maStops.size()) == 1) { ::sal_detail_log
( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1845" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "impldrawgradient(" << this <<
"): " << rPolyPolygon << ":" << rGradient.
maPoint1 << "->" << rGradient.maPoint2 <<
":" << rGradient.maStops.size()), 0); } else { ::std::
ostringstream sal_detail_stream; sal_detail_stream << "impldrawgradient("
<< this << "): " << rPolyPolygon << ":"
<< rGradient.maPoint1 << "->" << rGradient
.maPoint2 << ":" << rGradient.maStops.size(); ::sal
::detail::log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"
), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1845" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "impldrawgradient(" << this << "): " <<
rPolyPolygon << ":" << rGradient.maPoint1 <<
"->" << rGradient.maPoint2 << ":" << rGradient
.maStops.size()) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1845" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "impldrawgradient(" << this <<
"): " << rPolyPolygon << ":" << rGradient.
maPoint1 << "->" << rGradient.maPoint2 <<
":" << rGradient.maStops.size()), 0); } else { ::std::
ostringstream sal_detail_stream; sal_detail_stream << "impldrawgradient("
<< this << "): " << rPolyPolygon << ":"
<< rGradient.maPoint1 << "->" << rGradient
.maPoint2 << ":" << rGradient.maStops.size(); ::sal
::detail::log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"
), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1845" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
1844 "impldrawgradient(" << this << "): " << rPolyPolygon << ":" << rGradient.maPoint1do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_INFO
, "vcl.skia.trace")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break
; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "impldrawgradient(" <<
this << "): " << rPolyPolygon << ":" <<
rGradient.maPoint1 << "->" << rGradient.maPoint2
<< ":" << rGradient.maStops.size()) == 1) { ::sal_detail_log
( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1845" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "impldrawgradient(" << this <<
"): " << rPolyPolygon << ":" << rGradient.
maPoint1 << "->" << rGradient.maPoint2 <<
":" << rGradient.maStops.size()), 0); } else { ::std::
ostringstream sal_detail_stream; sal_detail_stream << "impldrawgradient("
<< this << "): " << rPolyPolygon << ":"
<< rGradient.maPoint1 << "->" << rGradient
.maPoint2 << ":" << rGradient.maStops.size(); ::sal
::detail::log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"
), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1845" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "impldrawgradient(" << this << "): " <<
rPolyPolygon << ":" << rGradient.maPoint1 <<
"->" << rGradient.maPoint2 << ":" << rGradient
.maStops.size()) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1845" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "impldrawgradient(" << this <<
"): " << rPolyPolygon << ":" << rGradient.
maPoint1 << "->" << rGradient.maPoint2 <<
":" << rGradient.maStops.size()), 0); } else { ::std::
ostringstream sal_detail_stream; sal_detail_stream << "impldrawgradient("
<< this << "): " << rPolyPolygon << ":"
<< rGradient.maPoint1 << "->" << rGradient
.maPoint2 << ":" << rGradient.maStops.size(); ::sal
::detail::log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"
), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1845" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
1845 << "->" << rGradient.maPoint2 << ":" << rGradient.maStops.size())do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_INFO
, "vcl.skia.trace")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break
; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "impldrawgradient(" <<
this << "): " << rPolyPolygon << ":" <<
rGradient.maPoint1 << "->" << rGradient.maPoint2
<< ":" << rGradient.maStops.size()) == 1) { ::sal_detail_log
( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1845" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "impldrawgradient(" << this <<
"): " << rPolyPolygon << ":" << rGradient.
maPoint1 << "->" << rGradient.maPoint2 <<
":" << rGradient.maStops.size()), 0); } else { ::std::
ostringstream sal_detail_stream; sal_detail_stream << "impldrawgradient("
<< this << "): " << rPolyPolygon << ":"
<< rGradient.maPoint1 << "->" << rGradient
.maPoint2 << ":" << rGradient.maStops.size(); ::sal
::detail::log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"
), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1845" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "impldrawgradient(" << this << "): " <<
rPolyPolygon << ":" << rGradient.maPoint1 <<
"->" << rGradient.maPoint2 << ":" << rGradient
.maStops.size()) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1845" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "impldrawgradient(" << this <<
"): " << rPolyPolygon << ":" << rGradient.
maPoint1 << "->" << rGradient.maPoint2 <<
":" << rGradient.maStops.size()), 0); } else { ::std::
ostringstream sal_detail_stream; sal_detail_stream << "impldrawgradient("
<< this << "): " << rPolyPolygon << ":"
<< rGradient.maPoint1 << "->" << rGradient
.maPoint2 << ":" << rGradient.maStops.size(); ::sal
::detail::log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"
), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1845" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
;
1846
1847 SkPath path;
1848 addPolyPolygonToPath(rPolyPolygon, path);
1849 path.setFillType(SkPathFillType::kEvenOdd);
1850
1851 SkPoint points[2]
1852 = { SkPoint::Make(toSkX(rGradient.maPoint1.getX()), toSkY(rGradient.maPoint1.getY())),
1853 SkPoint::Make(toSkX(rGradient.maPoint2.getX()), toSkY(rGradient.maPoint2.getY())) };
1854 std::vector<SkColor> colors;
1855 std::vector<SkScalar> pos;
1856 for (const SalGradientStop& stop : rGradient.maStops)
1857 {
1858 colors.emplace_back(toSkColor(stop.maColor));
1859 pos.emplace_back(stop.mfOffset);
1860 }
1861 sk_sp<SkShader> shader = SkGradientShader::MakeLinear(points, colors.data(), pos.data(),
1862 colors.size(), SkTileMode::kDecal);
1863 SkPaint paint;
1864 paint.setAntiAlias(mParent.getAntiAlias());
1865 paint.setShader(shader);
1866 getDrawCanvas()->drawPath(path, paint);
1867 addXorRegion(path.getBounds());
1868 postDraw();
1869 return true;
1870}
1871
1872static double toRadian(int degree10th) { return (3600 - degree10th) * M_PI3.14159265358979323846 / 1800.0; }
1873static double toCos(int degree10th) { return SkScalarCos(toRadian(degree10th))(float)cosf(toRadian(degree10th)); }
1874static double toSin(int degree10th) { return SkScalarSin(toRadian(degree10th))(float)sinf(toRadian(degree10th)); }
1875
1876void SkiaSalGraphicsImpl::drawGenericLayout(const GenericSalLayout& layout, Color textColor,
1877 const SkFont& font, GlyphOrientation glyphOrientation)
1878{
1879 SkiaZone zone;
1880 std::vector<SkGlyphID> glyphIds;
1881 std::vector<SkRSXform> glyphForms;
1882 glyphIds.reserve(256);
1883 glyphForms.reserve(256);
1884 Point aPos;
1885 const GlyphItem* pGlyph;
1886 int nStart = 0;
1887 while (layout.GetNextGlyph(&pGlyph, aPos, nStart))
1888 {
1889 glyphIds.push_back(pGlyph->glyphId());
1890 int angle = 0; // 10th of degree
1891 if (glyphOrientation == GlyphOrientation::Apply)
1892 {
1893 angle = layout.GetOrientation();
1894 if (pGlyph->IsVertical())
1895 angle += 900; // 90 degree
1896 }
1897 SkRSXform form = SkRSXform::Make(toCos(angle), toSin(angle), aPos.X(), aPos.Y());
1898 glyphForms.emplace_back(std::move(form));
1899 }
1900 if (glyphIds.empty())
1901 return;
1902 sk_sp<SkTextBlob> textBlob
1903 = SkTextBlob::MakeFromRSXform(glyphIds.data(), glyphIds.size() * sizeof(SkGlyphID),
1904 glyphForms.data(), font, SkTextEncoding::kGlyphID);
1905 preDraw();
1906 SAL_INFO("vcl.skia.trace",do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_INFO
, "vcl.skia.trace")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break
; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "drawtextblob(" <<
this << "): " << textBlob->bounds() << ":"
<< textColor) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1907" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "drawtextblob(" << this <<
"): " << textBlob->bounds() << ":" << textColor
), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "drawtextblob(" << this << "): " <<
textBlob->bounds() << ":" << textColor; ::sal
::detail::log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"
), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1907" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "drawtextblob(" << this << "): " <<
textBlob->bounds() << ":" << textColor) == 1)
{ ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"
), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1907" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "drawtextblob(" << this <<
"): " << textBlob->bounds() << ":" << textColor
), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "drawtextblob(" << this << "): " <<
textBlob->bounds() << ":" << textColor; ::sal
::detail::log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"
), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1907" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
1907 "drawtextblob(" << this << "): " << textBlob->bounds() << ":" << textColor)do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_INFO
, "vcl.skia.trace")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break
; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "drawtextblob(" <<
this << "): " << textBlob->bounds() << ":"
<< textColor) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl.skia.trace"), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1907" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "drawtextblob(" << this <<
"): " << textBlob->bounds() << ":" << textColor
), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "drawtextblob(" << this << "): " <<
textBlob->bounds() << ":" << textColor; ::sal
::detail::log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"
), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1907" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "drawtextblob(" << this << "): " <<
textBlob->bounds() << ":" << textColor) == 1)
{ ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"
), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1907" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "drawtextblob(" << this <<
"): " << textBlob->bounds() << ":" << textColor
), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "drawtextblob(" << this << "): " <<
textBlob->bounds() << ":" << textColor; ::sal
::detail::log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl.skia.trace"
), ("/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
":" "1907" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
;
1908 addXorRegion(textBlob->bounds());
1909 SkPaint paint;
1910 paint.setColor(toSkColor(textColor));
1911 getDrawCanvas()->drawTextBlob(textBlob, 0, 0, paint);
1912 postDraw();
1913}
1914
1915bool SkiaSalGraphicsImpl::supportsOperation(OutDevSupportType eType) const
1916{
1917 switch (eType)
1918 {
1919 case OutDevSupportType::B2DDraw:
1920 case OutDevSupportType::TransparentRect:
1921 return true;
1922 default:
1923 return false;
1924 }
1925}
1926
1927#ifdef DBG_UTIL
1928void SkiaSalGraphicsImpl::dump(const char* file) const
1929{
1930 assert(mSurface.get())(static_cast <bool> (mSurface.get()) ? void (0) : __assert_fail
("mSurface.get()", "/home/maarten/src/libreoffice/core/vcl/skia/gdiimpl.cxx"
, 1930, __extension__ __PRETTY_FUNCTION__))
;
1931 SkiaHelper::dump(mSurface, file);
1932}
1933#endif
1934
1935/* vim:set shiftwidth=4 softtabstop=4 expandtab: */