Bug Summary

File:root/firefox-clang/gfx/skia/skia/src/core/SkScan_Antihair.cpp
Warning:line 251, column 31
Division by zero

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -O2 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name SkScan_Antihair.cpp -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 -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -ffp-contract=off -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/gfx/skia -fcoverage-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/gfx/skia -resource-dir /usr/lib/llvm-23/lib/clang/23 -include /root/firefox-clang/config/gcc_hidden.h -include /root/firefox-clang/obj-x86_64-pc-linux-gnu/mozilla-config.h -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/stl_wrappers -D _GLIBCXX_ASSERTIONS=1 -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/system_wrappers -U _FORTIFY_SOURCE -D _FORTIFY_SOURCE=2 -D DEBUG=1 -D MOZ_SKIA -D SKIA_IMPLEMENTATION=1 -D SK_PDF_USE_HARFBUZZ_SUBSET=1 -D MOZ_HAS_MOZGLUE -D MOZILLA_INTERNAL_API -D IMPL_LIBXUL -D MOZ_SUPPORT_LEAKCHECKING -D STATIC_EXPORTABLE_JS_API -I /root/firefox-clang/gfx/skia -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/gfx/skia -I /root/firefox-clang/gfx/skia/skia -I /root/firefox-clang/gfx/harfbuzz/src -I /root/firefox-clang/gfx/cairo/cairo/src -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nspr -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nss -D MOZILLA_CLIENT -I /usr/include/freetype2 -I /usr/include/libpng16 -I /usr/include/freetype2 -I /usr/include/libpng16 -I /usr/include/pango-1.0 -I /usr/include/harfbuzz -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I /usr/include/libmount -I /usr/include/blkid -I /usr/include/fribidi -I /usr/include/cairo -I /usr/include/freetype2 -I /usr/include/libpng16 -I /usr/include/pixman-1 -I /usr/include/sysprof-6 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/c++/16 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/x86_64-linux-gnu/c++/16 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/c++/16/backward -internal-isystem /usr/lib/llvm-23/lib/clang/23/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wno-error=pessimizing-move -Wno-error=large-by-value-copy=128 -Wno-error=implicit-int-float-conversion -Wno-error=thread-safety-analysis -Wno-error=tautological-type-limit-compare -Wno-invalid-offsetof -Wno-range-loop-analysis -Wno-deprecated-anon-enum-enum-conversion -Wno-deprecated-enum-enum-conversion -Wno-inline-new-delete -Wno-error=deprecated-declarations -Wno-error=array-bounds -Wno-error=free-nonheap-object -Wno-error=atomic-alignment -Wno-error=deprecated-builtins -Wno-psabi -Wno-error=builtin-macro-redefined -Wno-vla-cxx-extension -Wno-unknown-warning-option -Wno-character-conversion -Wno-deprecated-declarations -Wno-overloaded-virtual -Wno-sign-compare -Wno-unreachable-code -Wno-unused-function -Wno-implicit-fallthrough -Wno-inconsistent-missing-override -Wno-macro-redefined -Wno-unused-private-field -std=gnu++20 -fdeprecated-macro -ferror-limit 19 -fstrict-flex-arrays=1 -stack-protector 2 -fstack-clash-protection -ftrivial-auto-var-init=pattern -fno-rtti -fgnuc-version=4.2.1 -fno-implicit-modules -fskip-odr-check-in-gmf -fno-sized-deallocation -fno-aligned-allocation -fdiagnostics-absolute-paths -vectorize-loops -vectorize-slp -analyzer-checker optin.performance.Padding -analyzer-output=html -analyzer-config stable-report-filename=true -mllvm -dwarf-linkage-names=Abstract -faddrsig -fdwarf2-cfi-asm -o /tmp/scan-build-2026-09-01-224014-2642839-1 -x c++ /root/firefox-clang/gfx/skia/skia/src/core/SkScan_Antihair.cpp
1/*
2 * Copyright 2011 The Android Open Source Project
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "include/core/SkPoint.h"
9#include "include/core/SkRect.h"
10#include "include/core/SkRegion.h"
11#include "include/core/SkScalar.h"
12#include "include/private/base/SkAssert.h"
13#include "include/private/base/SkCPUTypes.h"
14#include "include/private/base/SkDebug.h"
15#include "include/private/base/SkFixed.h"
16#include "include/private/base/SkMath.h"
17#include "include/private/base/SkSafe32.h"
18#include "include/private/base/SkTo.h"
19#include "src/core/SkBlitter.h"
20#include "src/core/SkColorPriv.h"
21#include "src/core/SkFDot6.h"
22#include "src/core/SkLineClipper.h"
23#include "src/core/SkRasterClip.h"
24#include "src/core/SkScan.h"
25
26#include <algorithm>
27#include <cstdint>
28
29#define HLINE_STACK_BUFFER100 100
30
31static inline U8CPU scale_alpha_by_coverage(U8CPU value, SkFDot6 coverage) {
32 SkASSERT(value <= 255)static_cast<void>( __builtin_expect(static_cast<bool
>(value <= 255), 1) ? static_cast<void>(0) : []{ do
{ if (sk_abort_is_enabled()) { do { SkDebugf("%s:%d" ": fatal error: \""
"check(%s)" "\"\n", "/root/firefox-clang/gfx/skia/skia/src/core/SkScan_Antihair.cpp"
, 32, "value <= 255"); ; sk_abort_no_print(); } while (false
); } } while(false); }() )
;
33 SkASSERT(coverage >= 0 && coverage <= SK_FDot6One)static_cast<void>( __builtin_expect(static_cast<bool
>(coverage >= 0 && coverage <= (64)), 1) ? static_cast
<void>(0) : []{ do { if (sk_abort_is_enabled()) { do { SkDebugf
("%s:%d" ": fatal error: \"" "check(%s)" "\"\n", "/root/firefox-clang/gfx/skia/skia/src/core/SkScan_Antihair.cpp"
, 33, "coverage >= 0 && coverage <= (64)"); ; sk_abort_no_print
(); } while (false); } } while(false); }() )
;
34 return (value * coverage) >> 6;
35}
36
37// Extracts the high 8 bits of the fractional part of a 16.16 fixed-point
38// number, returning an 8-bit alpha value.
39static inline U8CPU fixed_to_alpha(SkFixed f) {
40 return (f >> 8) & 0xFF;
41}
42
43static void call_hline_blitter(SkBlitter* blitter, int x, int y, int count,
44 U8CPU alpha) {
45 SkASSERT(count > 0)static_cast<void>( __builtin_expect(static_cast<bool
>(count > 0), 1) ? static_cast<void>(0) : []{ do {
if (sk_abort_is_enabled()) { do { SkDebugf("%s:%d" ": fatal error: \""
"check(%s)" "\"\n", "/root/firefox-clang/gfx/skia/skia/src/core/SkScan_Antihair.cpp"
, 45, "count > 0"); ; sk_abort_no_print(); } while (false)
; } } while(false); }() )
;
46
47 int16_t runs[HLINE_STACK_BUFFER100 + 1];
48 uint8_t aa[HLINE_STACK_BUFFER100];
49
50 do {
51 // In theory, we should be able to just do this once (outside of the loop),
52 // since aa[] and runs[] are "supposed" to be const when we call the blitter.
53 // In reality, some wrapper-blitters (e.g. SkRgnClipBlitter) cast away that
54 // constness, and modify the buffers in-place. Hence the need to be defensive
55 // here and reseed the aa value.
56 aa[0] = SkToU8(alpha);
57
58 int n = count;
59 if (n > HLINE_STACK_BUFFER100) {
60 n = HLINE_STACK_BUFFER100;
61 }
62 runs[0] = SkToS16(n);
63 runs[n] = 0;
64 blitter->blitAntiH(x, y, aa, runs);
65 x += n;
66 count -= n;
67 } while (count > 0);
68}
69
70// This is an abstract class that defines the blitter interface for drawing
71// anti-aliased hairlines. There are concrete implementations for different
72// line orientations. The do_anti_hairline function chooses the appropriate
73// implementation based on the line's slope.
74class SkAntiHairBlitter {
75public:
76 SkAntiHairBlitter() : fBlitter(nullptr) {}
77 virtual ~SkAntiHairBlitter() {}
78
79 SkBlitter* getBlitter() const { return fBlitter; }
80
81 void setup(SkBlitter* blitter) {
82 fBlitter = blitter;
83 }
84
85 virtual SkFixed drawCap(int x, SkFixed fy, SkFixed slope, SkFDot6 coverage) = 0;
86 virtual SkFixed drawLine(int x, int stopx, SkFixed fy, SkFixed slope) = 0;
87
88private:
89 SkBlitter* fBlitter;
90};
91
92// This class is responsible for drawing perfectly horizontal hairlines.
93// Such hairlines will be over two rows. One row may be 100% coverage
94// if the line was exactly on a pixel row boundary.
95class HLine_SkAntiHairBlitter : public SkAntiHairBlitter {
96public:
97 SkFixed drawCap(int x, SkFixed fy, SkFixed, SkFDot6 coverage) override {
98 fy += SK_FixedHalf(1 << 15);
99
100 int y = SkFixedFloorToInt(fy)((fy) >> 16);
101 // Compute an alpha va lue based on the fractional part of fy
102 // 0 means fy was at NN.5 and we'll only be drawing the upper line.
103 // 128 means fy was at NN.0 and we'll be coloring both lines approximately
104 // the same opacity.
105 U8CPU a = fixed_to_alpha(fy);
106
107 // lower line
108 U8CPU ma = scale_alpha_by_coverage(a, coverage);
109 if (ma) {
110 call_hline_blitter(this->getBlitter(), x, y, 1, ma);
111 }
112
113 // upper line
114 ma = scale_alpha_by_coverage(255 - a, coverage);
115 if (ma) {
116 call_hline_blitter(this->getBlitter(), x, y - 1, 1, ma);
117 }
118
119 return fy - SK_FixedHalf(1 << 15);
120 }
121
122 SkFixed drawLine(int x, int stopx, SkFixed fy, SkFixed) override {
123 SkASSERT(x < stopx)static_cast<void>( __builtin_expect(static_cast<bool
>(x < stopx), 1) ? static_cast<void>(0) : []{ do {
if (sk_abort_is_enabled()) { do { SkDebugf("%s:%d" ": fatal error: \""
"check(%s)" "\"\n", "/root/firefox-clang/gfx/skia/skia/src/core/SkScan_Antihair.cpp"
, 123, "x < stopx"); ; sk_abort_no_print(); } while (false
); } } while(false); }() )
;
124 int count = stopx - x;
125 fy += SK_FixedHalf(1 << 15);
126
127 int y = SkFixedFloorToInt(fy)((fy) >> 16);
128 U8CPU a = fixed_to_alpha(fy);
129
130 // lower line
131 if (a) {
132 call_hline_blitter(this->getBlitter(), x, y, count, a);
133 }
134
135 // upper line
136 a = 255 - a;
137 if (a) {
138 call_hline_blitter(this->getBlitter(), x, y - 1, count, a);
139 }
140
141 return fy - SK_FixedHalf(1 << 15);
142 }
143};
144
145// This class handles lines that are mostly horizontal (i.e., their slope is
146// between -1 and 1).
147class Horish_SkAntiHairBlitter : public SkAntiHairBlitter {
148public:
149 SkFixed drawCap(int x, SkFixed fy, SkFixed dy, SkFDot6 coverage) override {
150 fy += SK_FixedHalf(1 << 15);
151
152 int lower_y = SkFixedFloorToInt(fy)((fy) >> 16);
153 U8CPU a = fixed_to_alpha(fy);
154 U8CPU a0 = scale_alpha_by_coverage(255 - a, coverage);
155 U8CPU a1 = scale_alpha_by_coverage(a, coverage);
156 this->getBlitter()->blitAntiV2(x, lower_y - 1, a0, a1);
157
158 return fy + dy - SK_FixedHalf(1 << 15);
159 }
160
161 SkFixed drawLine(int x, int stopx, SkFixed fy, SkFixed dy) override {
162 SkASSERT(x < stopx)static_cast<void>( __builtin_expect(static_cast<bool
>(x < stopx), 1) ? static_cast<void>(0) : []{ do {
if (sk_abort_is_enabled()) { do { SkDebugf("%s:%d" ": fatal error: \""
"check(%s)" "\"\n", "/root/firefox-clang/gfx/skia/skia/src/core/SkScan_Antihair.cpp"
, 162, "x < stopx"); ; sk_abort_no_print(); } while (false
); } } while(false); }() )
;
163
164 fy += SK_FixedHalf(1 << 15);
165 SkBlitter* blitter = this->getBlitter();
166 do {
167 int lower_y = SkFixedFloorToInt(fy)((fy) >> 16);
168 U8CPU a = fixed_to_alpha(fy);
169 blitter->blitAntiV2(x, lower_y - 1, 255 - a, a);
170 fy += dy;
171 } while (++x < stopx);
172
173 return fy - SK_FixedHalf(1 << 15);
174 }
175};
176
177// This class is responsible for drawing perfectly vertical hairlines.
178class VLine_SkAntiHairBlitter : public SkAntiHairBlitter {
179public:
180 SkFixed drawCap(int y, SkFixed fx, SkFixed dx, SkFDot6 coverage) override {
181 SkASSERT(0 == dx)static_cast<void>( __builtin_expect(static_cast<bool
>(0 == dx), 1) ? static_cast<void>(0) : []{ do { if (
sk_abort_is_enabled()) { do { SkDebugf("%s:%d" ": fatal error: \""
"check(%s)" "\"\n", "/root/firefox-clang/gfx/skia/skia/src/core/SkScan_Antihair.cpp"
, 181, "0 == dx"); ; sk_abort_no_print(); } while (false); } }
while(false); }() )
;
182 fx += SK_FixedHalf(1 << 15);
183
184 int x = SkFixedFloorToInt(fx)((fx) >> 16);
185 U8CPU a = fixed_to_alpha(fx);
186
187 U8CPU ma = scale_alpha_by_coverage(a, coverage);
188 if (ma) {
189 this->getBlitter()->blitV(x, y, 1, ma);
190 }
191 ma = scale_alpha_by_coverage(255 - a, coverage);
192 if (ma) {
193 this->getBlitter()->blitV(x - 1, y, 1, ma);
194 }
195
196 return fx - SK_FixedHalf(1 << 15);
197 }
198
199 SkFixed drawLine(int y, int stopy, SkFixed fx, SkFixed dx) override {
200 SkASSERT(y < stopy)static_cast<void>( __builtin_expect(static_cast<bool
>(y < stopy), 1) ? static_cast<void>(0) : []{ do {
if (sk_abort_is_enabled()) { do { SkDebugf("%s:%d" ": fatal error: \""
"check(%s)" "\"\n", "/root/firefox-clang/gfx/skia/skia/src/core/SkScan_Antihair.cpp"
, 200, "y < stopy"); ; sk_abort_no_print(); } while (false
); } } while(false); }() )
;
201 SkASSERT(0 == dx)static_cast<void>( __builtin_expect(static_cast<bool
>(0 == dx), 1) ? static_cast<void>(0) : []{ do { if (
sk_abort_is_enabled()) { do { SkDebugf("%s:%d" ": fatal error: \""
"check(%s)" "\"\n", "/root/firefox-clang/gfx/skia/skia/src/core/SkScan_Antihair.cpp"
, 201, "0 == dx"); ; sk_abort_no_print(); } while (false); } }
while(false); }() )
;
202 fx += SK_FixedHalf(1 << 15);
203
204 int x = SkFixedFloorToInt(fx)((fx) >> 16);
205 U8CPU a = fixed_to_alpha(fx);
206
207 if (a) {
208 this->getBlitter()->blitV(x, y, stopy - y, a);
209 }
210 a = 255 - a;
211 if (a) {
212 this->getBlitter()->blitV(x - 1, y, stopy - y, a);
213 }
214
215 return fx - SK_FixedHalf(1 << 15);
216 }
217};
218
219// This class handles lines that are mostly vertical (i.e., their slope is
220// greater than 1 or less than -1).
221class Vertish_SkAntiHairBlitter : public SkAntiHairBlitter {
222public:
223 SkFixed drawCap(int y, SkFixed fx, SkFixed dx, SkFDot6 coverage) override {
224 fx += SK_FixedHalf(1 << 15);
225
226 int x = SkFixedFloorToInt(fx)((fx) >> 16);
227 U8CPU a = fixed_to_alpha(fx);
228 this->getBlitter()->blitAntiH2(x - 1, y,
229 scale_alpha_by_coverage(255 - a, coverage), scale_alpha_by_coverage(a, coverage));
230
231 return fx + dx - SK_FixedHalf(1 << 15);
232 }
233
234 SkFixed drawLine(int y, int stopy, SkFixed fx, SkFixed dx) override {
235 SkASSERT(y < stopy)static_cast<void>( __builtin_expect(static_cast<bool
>(y < stopy), 1) ? static_cast<void>(0) : []{ do {
if (sk_abort_is_enabled()) { do { SkDebugf("%s:%d" ": fatal error: \""
"check(%s)" "\"\n", "/root/firefox-clang/gfx/skia/skia/src/core/SkScan_Antihair.cpp"
, 235, "y < stopy"); ; sk_abort_no_print(); } while (false
); } } while(false); }() )
;
236 fx += SK_FixedHalf(1 << 15);
237 do {
238 int x = SkFixedFloorToInt(fx)((fx) >> 16);
239 U8CPU a = fixed_to_alpha(fx);
240 this->getBlitter()->blitAntiH2(x - 1, y, 255 - a, a);
241 fx += dx;
242 } while (++y < stopy);
243
244 return fx - SK_FixedHalf(1 << 15);
245 }
246};
247
248static inline SkFixed fastfixdiv(SkFDot6 a, SkFDot6 b) {
249 SkASSERT((SkLeftShift(a, 16) >> 16) == a)static_cast<void>( __builtin_expect(static_cast<bool
>((SkLeftShift(a, 16) >> 16) == a), 1) ? static_cast
<void>(0) : []{ do { if (sk_abort_is_enabled()) { do { SkDebugf
("%s:%d" ": fatal error: \"" "check(%s)" "\"\n", "/root/firefox-clang/gfx/skia/skia/src/core/SkScan_Antihair.cpp"
, 249, "(SkLeftShift(a, 16) >> 16) == a"); ; sk_abort_no_print
(); } while (false); } } while(false); }() )
;
15
Assuming the condition is true
16
'?' condition is true
250 SkASSERT(b != 0)static_cast<void>( __builtin_expect(static_cast<bool
>(b != 0), 1) ? static_cast<void>(0) : []{ do { if (
sk_abort_is_enabled()) { do { SkDebugf("%s:%d" ": fatal error: \""
"check(%s)" "\"\n", "/root/firefox-clang/gfx/skia/skia/src/core/SkScan_Antihair.cpp"
, 250, "b != 0"); ; sk_abort_no_print(); } while (false); } }
while(false); }() )
;
17
'?' condition is false
251 return SkLeftShift(a, 16) / b;
18
Division by zero
252}
253
254#define SkBITCOUNT(x)(sizeof(x) << 3) (sizeof(x) << 3)
255
256#if 1
257// returns high-bit set iff x==0x8000...
258static inline int bad_int(int x) {
259 return x & -x;
260}
261
262static int any_bad_ints(int a, int b, int c, int d) {
263 return (bad_int(a) | bad_int(b) | bad_int(c) | bad_int(d)) >> (SkBITCOUNT(int)(sizeof(int) << 3) - 1);
264}
265#else
266static inline int good_int(int x) {
267 return x ^ (1 << (SkBITCOUNT(x)(sizeof(x) << 3) - 1));
268}
269
270static int any_bad_ints(int a, int b, int c, int d) {
271 return !(good_int(a) & good_int(b) & good_int(c) & good_int(d));
272}
273#endif
274
275#ifdef SK_DEBUG
276static bool canConvertFDot6ToFixed(SkFDot6 x) {
277 const int maxDot6 = SK_MaxS32 >> (16 - 6);
278 return SkAbs32(x) <= maxDot6;
279}
280#endif
281
282// Returns the fractional part of the passed in number.
283// e.g. 2.75 -> 0.75
284static inline SkFDot6 fd6_frac(SkFDot6 x) {
285 return x & (SK_FDot6One(64) - 1);
286}
287
288/*
289 * We want the fractional part of x or y, but we want multiples of 64 to
290 * return 64, not 0, so we can't just say take the fractional component.
291 * We basically want to compute those bits, and if they're 0, return 64.
292 * We can do that w/o a branch with an extra sub and add.
293 */
294static SkFDot6 partial_pixel_coverage(SkFDot6 pos) {
295#if 0
296 int result = fd6_frac(pos);
297 if (0 == result) {
298 result = SK_FDot6One(64);
299 }
300#else
301 int result = fd6_frac(pos - 1) + 1;
302#endif
303 SkASSERT(result > 0 && result <= SK_FDot6One)static_cast<void>( __builtin_expect(static_cast<bool
>(result > 0 && result <= (64)), 1) ? static_cast
<void>(0) : []{ do { if (sk_abort_is_enabled()) { do { SkDebugf
("%s:%d" ": fatal error: \"" "check(%s)" "\"\n", "/root/firefox-clang/gfx/skia/skia/src/core/SkScan_Antihair.cpp"
, 303, "result > 0 && result <= (64)"); ; sk_abort_no_print
(); } while (false); } } while(false); }() )
;
304 return result;
305}
306
307static void do_anti_hairline(SkFDot6 x0, SkFDot6 y0, SkFDot6 x1, SkFDot6 y1,
308 const SkIRect* clip, SkBlitter* blitter) {
309 // check for integer NaN (0x80000000) which we can't handle (can't negate it)
310 // It appears typically from a huge float (inf or nan) being converted to int.
311 // If we see it, just don't draw.
312 if (any_bad_ints(x0, y0, x1, y1)) {
1
Assuming the condition is false
2
Taking false branch
313 return;
314 }
315
316 // The caller must clip the line to [-32767.0 ... 32767.0] ahead of time
317 // (in dot6 format)
318 SkASSERT(canConvertFDot6ToFixed(x0))static_cast<void>( __builtin_expect(static_cast<bool
>(canConvertFDot6ToFixed(x0)), 1) ? static_cast<void>
(0) : []{ do { if (sk_abort_is_enabled()) { do { SkDebugf("%s:%d"
": fatal error: \"" "check(%s)" "\"\n", "/root/firefox-clang/gfx/skia/skia/src/core/SkScan_Antihair.cpp"
, 318, "canConvertFDot6ToFixed(x0)"); ; sk_abort_no_print(); }
while (false); } } while(false); }() )
;
3
'?' condition is true
319 SkASSERT(canConvertFDot6ToFixed(y0))static_cast<void>( __builtin_expect(static_cast<bool
>(canConvertFDot6ToFixed(y0)), 1) ? static_cast<void>
(0) : []{ do { if (sk_abort_is_enabled()) { do { SkDebugf("%s:%d"
": fatal error: \"" "check(%s)" "\"\n", "/root/firefox-clang/gfx/skia/skia/src/core/SkScan_Antihair.cpp"
, 319, "canConvertFDot6ToFixed(y0)"); ; sk_abort_no_print(); }
while (false); } } while(false); }() )
;
4
'?' condition is true
320 SkASSERT(canConvertFDot6ToFixed(x1))static_cast<void>( __builtin_expect(static_cast<bool
>(canConvertFDot6ToFixed(x1)), 1) ? static_cast<void>
(0) : []{ do { if (sk_abort_is_enabled()) { do { SkDebugf("%s:%d"
": fatal error: \"" "check(%s)" "\"\n", "/root/firefox-clang/gfx/skia/skia/src/core/SkScan_Antihair.cpp"
, 320, "canConvertFDot6ToFixed(x1)"); ; sk_abort_no_print(); }
while (false); } } while(false); }() )
;
5
'?' condition is true
321 SkASSERT(canConvertFDot6ToFixed(y1))static_cast<void>( __builtin_expect(static_cast<bool
>(canConvertFDot6ToFixed(y1)), 1) ? static_cast<void>
(0) : []{ do { if (sk_abort_is_enabled()) { do { SkDebugf("%s:%d"
": fatal error: \"" "check(%s)" "\"\n", "/root/firefox-clang/gfx/skia/skia/src/core/SkScan_Antihair.cpp"
, 321, "canConvertFDot6ToFixed(y1)"); ; sk_abort_no_print(); }
while (false); } } while(false); }() )
;
6
'?' condition is true
322
323 if (SkAbs32(x1 - x0) > SkIntToFDot6(511) || SkAbs32(y1 - y0) > SkIntToFDot6(511)) {
7
Assuming the condition is false
8
Taking false branch
324 /* instead of (x0 + x1) >> 1, we shift each separately. This is less
325 precise, but avoids overflowing the intermediate result if the
326 values are huge. A better fix might be to clip the original pts
327 directly (i.e. do the divide), so we don't spend time subdividing
328 huge lines at all.
329 */
330 int hx = (x0 >> 1) + (x1 >> 1);
331 int hy = (y0 >> 1) + (y1 >> 1);
332 do_anti_hairline(x0, y0, hx, hy, clip, blitter);
333 do_anti_hairline(hx, hy, x1, y1, clip, blitter);
334 return;
335 }
336
337 int startCoverage, stopCoverage;
338 int istart, istop;
339 SkFixed fstart, slope;
340
341 HLine_SkAntiHairBlitter hline_blitter;
342 Horish_SkAntiHairBlitter horish_blitter;
343 VLine_SkAntiHairBlitter vline_blitter;
344 Vertish_SkAntiHairBlitter vertish_blitter;
345 SkAntiHairBlitter* hairBlitter = nullptr;
346
347 if (SkAbs32(x1 - x0) > SkAbs32(y1 - y0)) { // mostly horizontal
9
Assuming the condition is true
10
Taking true branch
348 if (x0
10.1
'x0' is <= 'x1'
> x1) { // we want to go left-to-right
11
Taking false branch
349 using std::swap;
350 swap(x0, x1);
351 swap(y0, y1);
352 }
353
354 istart = SkFDot6Floor(x0)((x0) >> 6);
355 istop = SkFDot6Ceil(x1)(((x1) + 63) >> 6);
356 if (y0
11.1
'y0' is not equal to 'y1'
== y1) { // completely horizontal, take fast case
12
Taking false branch
357 slope = 0;
358 hairBlitter = &hline_blitter;
359 fstart = SkFDot6ToFixed(y0);
360 } else {
361 slope = fastfixdiv(y1 - y0, x1 - x0);
13
Passing the value 0 via 2nd parameter 'b'
14
Calling 'fastfixdiv'
362 SkASSERTF(slope >= -SK_Fixed1 && slope <= SK_Fixed1,static_cast<void>( __builtin_expect(static_cast<bool
>(slope >= -(1 << 16) && slope <= (1 <<
16)), 1) ? static_cast<void>(0) : [&]{ do { if (sk_abort_is_enabled
()) { do { SkDebugf("%s:%d" ": fatal error: \"" "assertf(%s): "
"should be vertical or mostly vertical" "\"\n", "/root/firefox-clang/gfx/skia/skia/src/core/SkScan_Antihair.cpp"
, 363, "slope >= -(1 << 16) && slope <= (1 << 16)"
); ; sk_abort_no_print(); } while (false); } } while(false); }
() )
363 "should be vertical or mostly vertical")static_cast<void>( __builtin_expect(static_cast<bool
>(slope >= -(1 << 16) && slope <= (1 <<
16)), 1) ? static_cast<void>(0) : [&]{ do { if (sk_abort_is_enabled
()) { do { SkDebugf("%s:%d" ": fatal error: \"" "assertf(%s): "
"should be vertical or mostly vertical" "\"\n", "/root/firefox-clang/gfx/skia/skia/src/core/SkScan_Antihair.cpp"
, 363, "slope >= -(1 << 16) && slope <= (1 << 16)"
); ; sk_abort_no_print(); } while (false); } } while(false); }
() )
;
364 // Adjust fstart to be the Y-intercept at the center of the first pixel.
365 SkFDot6 dx_to_center = SK_FDot6Half(32) - fd6_frac(x0);
366 fstart = SkFDot6ToFixed(y0) + ((slope * dx_to_center + SK_FDot6Half(32)) >> 6);
367 hairBlitter = &horish_blitter;
368 }
369
370 SkASSERT(istop > istart)static_cast<void>( __builtin_expect(static_cast<bool
>(istop > istart), 1) ? static_cast<void>(0) : []
{ do { if (sk_abort_is_enabled()) { do { SkDebugf("%s:%d" ": fatal error: \""
"check(%s)" "\"\n", "/root/firefox-clang/gfx/skia/skia/src/core/SkScan_Antihair.cpp"
, 370, "istop > istart"); ; sk_abort_no_print(); } while (
false); } } while(false); }() )
;
371 if (istop - istart == 1) {
372 // we are within a single pixel
373 startCoverage = x1 - x0;
374 SkASSERT(startCoverage >= 0 && startCoverage <= SK_FDot6One)static_cast<void>( __builtin_expect(static_cast<bool
>(startCoverage >= 0 && startCoverage <= (64
)), 1) ? static_cast<void>(0) : []{ do { if (sk_abort_is_enabled
()) { do { SkDebugf("%s:%d" ": fatal error: \"" "check(%s)" "\"\n"
, "/root/firefox-clang/gfx/skia/skia/src/core/SkScan_Antihair.cpp"
, 374, "startCoverage >= 0 && startCoverage <= (64)"
); ; sk_abort_no_print(); } while (false); } } while(false); }
() )
;
375 stopCoverage = 0;
376 } else {
377 startCoverage = SK_FDot6One(64) - fd6_frac(x0);
378 stopCoverage = fd6_frac(x1);
379 }
380
381 if (clip){
382 if (istart >= clip->fRight || istop <= clip->fLeft) {
383 return;
384 }
385 if (istart < clip->fLeft) {
386 fstart += slope * (clip->fLeft - istart);
387 istart = clip->fLeft;
388 startCoverage = SK_FDot6One(64);
389 if (istop - istart == 1) {
390 // we are within a single pixel
391 startCoverage = partial_pixel_coverage(x1);
392 stopCoverage = 0;
393 }
394 }
395 if (istop > clip->fRight) {
396 istop = clip->fRight;
397 stopCoverage = 0; // so we don't draw this last column
398 }
399
400 SkASSERT(istart <= istop)static_cast<void>( __builtin_expect(static_cast<bool
>(istart <= istop), 1) ? static_cast<void>(0) : [
]{ do { if (sk_abort_is_enabled()) { do { SkDebugf("%s:%d" ": fatal error: \""
"check(%s)" "\"\n", "/root/firefox-clang/gfx/skia/skia/src/core/SkScan_Antihair.cpp"
, 400, "istart <= istop"); ; sk_abort_no_print(); } while (
false); } } while(false); }() )
;
401 if (istart == istop) {
402 return;
403 }
404 // now test if our Y values are completely inside the clip
405 int top, bottom;
406 if (slope >= 0) { // T2B
407 top = SkFixedFloorToInt(fstart - SK_FixedHalf)((fstart - (1 << 15)) >> 16);
408 bottom = SkFixedCeilToInt(fstart + (istop - istart - 1) * slope + SK_FixedHalf)(((fstart + (istop - istart - 1) * slope + (1 << 15)) +
(1 << 16) - 1) >> 16)
;
409 } else { // B2T
410 bottom = SkFixedCeilToInt(fstart + SK_FixedHalf)(((fstart + (1 << 15)) + (1 << 16) - 1) >> 16
)
;
411 top = SkFixedFloorToInt(fstart + (istop - istart - 1) * slope - SK_FixedHalf)((fstart + (istop - istart - 1) * slope - (1 << 15)) >>
16)
;
412 }
413 // Expand outset to work around possible numerical calculation bug that lead to overflow
414 top -= 1;
415 bottom += 1;
416
417 if (top >= clip->fBottom || bottom <= clip->fTop) {
418 return;
419 }
420 if (clip->fTop <= top && clip->fBottom >= bottom) {
421 clip = nullptr;
422 }
423 }
424 } else { // mostly vertical
425 if (y0 > y1) { // we want to go top-to-bottom
426 using std::swap;
427 swap(x0, x1);
428 swap(y0, y1);
429 }
430
431 istart = SkFDot6Floor(y0)((y0) >> 6);
432 istop = SkFDot6Ceil(y1)(((y1) + 63) >> 6);
433 if (x0 == x1) {
434 if (y0 == y1) { // are we zero length?
435 return; // nothing to do
436 }
437 slope = 0;
438 hairBlitter = &vline_blitter;
439 fstart = SkFDot6ToFixed(x0);
440 } else {
441 slope = fastfixdiv(x1 - x0, y1 - y0);
442 SkASSERTF(slope <= SK_Fixed1 && slope >= -SK_Fixed1,static_cast<void>( __builtin_expect(static_cast<bool
>(slope <= (1 << 16) && slope >= -(1 <<
16)), 1) ? static_cast<void>(0) : [&]{ do { if (sk_abort_is_enabled
()) { do { SkDebugf("%s:%d" ": fatal error: \"" "assertf(%s): "
"should be horizontal or mostly horizontal" "\"\n", "/root/firefox-clang/gfx/skia/skia/src/core/SkScan_Antihair.cpp"
, 443, "slope <= (1 << 16) && slope >= -(1 << 16)"
); ; sk_abort_no_print(); } while (false); } } while(false); }
() )
443 "should be horizontal or mostly horizontal")static_cast<void>( __builtin_expect(static_cast<bool
>(slope <= (1 << 16) && slope >= -(1 <<
16)), 1) ? static_cast<void>(0) : [&]{ do { if (sk_abort_is_enabled
()) { do { SkDebugf("%s:%d" ": fatal error: \"" "assertf(%s): "
"should be horizontal or mostly horizontal" "\"\n", "/root/firefox-clang/gfx/skia/skia/src/core/SkScan_Antihair.cpp"
, 443, "slope <= (1 << 16) && slope >= -(1 << 16)"
); ; sk_abort_no_print(); } while (false); } } while(false); }
() )
;
444 // Adjust fstart to be the X-intercept at the center of the first pixel row.
445 SkFDot6 dy_to_center = SK_FDot6Half(32) - fd6_frac(y0);
446 fstart = SkFDot6ToFixed(x0) + ((slope * dy_to_center + SK_FDot6Half(32)) >> 6);
447 hairBlitter = &vertish_blitter;
448 }
449
450 SkASSERT(istop > istart)static_cast<void>( __builtin_expect(static_cast<bool
>(istop > istart), 1) ? static_cast<void>(0) : []
{ do { if (sk_abort_is_enabled()) { do { SkDebugf("%s:%d" ": fatal error: \""
"check(%s)" "\"\n", "/root/firefox-clang/gfx/skia/skia/src/core/SkScan_Antihair.cpp"
, 450, "istop > istart"); ; sk_abort_no_print(); } while (
false); } } while(false); }() )
;
451 if (istop - istart == 1) {
452 // we are within a single pixel
453 startCoverage = y1 - y0;
454 SkASSERT(startCoverage >= 0 && startCoverage <= SK_FDot6One)static_cast<void>( __builtin_expect(static_cast<bool
>(startCoverage >= 0 && startCoverage <= (64
)), 1) ? static_cast<void>(0) : []{ do { if (sk_abort_is_enabled
()) { do { SkDebugf("%s:%d" ": fatal error: \"" "check(%s)" "\"\n"
, "/root/firefox-clang/gfx/skia/skia/src/core/SkScan_Antihair.cpp"
, 454, "startCoverage >= 0 && startCoverage <= (64)"
); ; sk_abort_no_print(); } while (false); } } while(false); }
() )
;
455 stopCoverage = 0;
456 } else {
457 startCoverage = SK_FDot6One(64) - fd6_frac(y0);
458 stopCoverage = fd6_frac(y1);
459 }
460
461 if (clip) {
462 if (istart >= clip->fBottom || istop <= clip->fTop) {
463 return;
464 }
465 if (istart < clip->fTop) {
466 fstart += slope * (clip->fTop - istart);
467 istart = clip->fTop;
468 startCoverage = SK_FDot6One(64);
469 if (istop - istart == 1) {
470 // we are within a single pixel
471 startCoverage = partial_pixel_coverage(y1);
472 stopCoverage = 0;
473 }
474 }
475 if (istop > clip->fBottom) {
476 istop = clip->fBottom;
477 stopCoverage = 0; // so we don't draw this last row
478 }
479
480 SkASSERT(istart <= istop)static_cast<void>( __builtin_expect(static_cast<bool
>(istart <= istop), 1) ? static_cast<void>(0) : [
]{ do { if (sk_abort_is_enabled()) { do { SkDebugf("%s:%d" ": fatal error: \""
"check(%s)" "\"\n", "/root/firefox-clang/gfx/skia/skia/src/core/SkScan_Antihair.cpp"
, 480, "istart <= istop"); ; sk_abort_no_print(); } while (
false); } } while(false); }() )
;
481 if (istart == istop)
482 return;
483
484 // now test if our X values are completely inside the clip
485 int left, right;
486 if (slope >= 0) { // L2R
487 left = SkFixedFloorToInt(fstart - SK_FixedHalf)((fstart - (1 << 15)) >> 16);
488 right = SkFixedCeilToInt(fstart + (istop - istart - 1) * slope + SK_FixedHalf)(((fstart + (istop - istart - 1) * slope + (1 << 15)) +
(1 << 16) - 1) >> 16)
;
489 } else { // R2L
490 right = SkFixedCeilToInt(fstart + SK_FixedHalf)(((fstart + (1 << 15)) + (1 << 16) - 1) >> 16
)
;
491 left = SkFixedFloorToInt(fstart + (istop - istart - 1) * slope - SK_FixedHalf)((fstart + (istop - istart - 1) * slope - (1 << 15)) >>
16)
;
492 }
493 // Expand outset to work around possible numerical calculation bug that lead to overflow
494 left -= 1;
495 right += 1;
496
497 if (left >= clip->fRight || right <= clip->fLeft) {
498 return;
499 }
500 if (clip->fLeft <= left && clip->fRight >= right) {
501 clip = nullptr;
502 }
503 }
504 }
505
506 SkRectClipBlitter rectClipper;
507 if (clip) {
508 rectClipper.init(blitter, *clip);
509 blitter = &rectClipper;
510 }
511
512 SkASSERT(hairBlitter)static_cast<void>( __builtin_expect(static_cast<bool
>(hairBlitter), 1) ? static_cast<void>(0) : []{ do {
if (sk_abort_is_enabled()) { do { SkDebugf("%s:%d" ": fatal error: \""
"check(%s)" "\"\n", "/root/firefox-clang/gfx/skia/skia/src/core/SkScan_Antihair.cpp"
, 512, "hairBlitter"); ; sk_abort_no_print(); } while (false)
; } } while(false); }() )
;
513 hairBlitter->setup(blitter);
514
515#ifdef SK_DEBUG
516 if (startCoverage > 0 && stopCoverage > 0) {
517 // be sure we don't draw twice in the same pixel
518 SkASSERT(istart < istop - 1)static_cast<void>( __builtin_expect(static_cast<bool
>(istart < istop - 1), 1) ? static_cast<void>(0) :
[]{ do { if (sk_abort_is_enabled()) { do { SkDebugf("%s:%d" ": fatal error: \""
"check(%s)" "\"\n", "/root/firefox-clang/gfx/skia/skia/src/core/SkScan_Antihair.cpp"
, 518, "istart < istop - 1"); ; sk_abort_no_print(); } while
(false); } } while(false); }() )
;
519 }
520#endif
521
522 fstart = hairBlitter->drawCap(istart, fstart, slope, startCoverage);
523 istart += 1;
524 int fullSpans = istop - istart - (stopCoverage > 0);
525 if (fullSpans > 0) {
526 fstart = hairBlitter->drawLine(istart, istart + fullSpans, fstart, slope);
527 }
528 if (stopCoverage > 0) {
529 hairBlitter->drawCap(istop - 1, fstart, slope, stopCoverage);
530 }
531}
532
533void SkScan::AntiHairLineRgn(SkSpan<const SkPoint> src, const SkRegion* clip, SkBlitter* blitter) {
534 if (src.empty() || (clip && clip->isEmpty())) {
535 return;
536 }
537
538 SkASSERT(clip == nullptr || !clip->getBounds().isEmpty())static_cast<void>( __builtin_expect(static_cast<bool
>(clip == nullptr || !clip->getBounds().isEmpty()), 1) ?
static_cast<void>(0) : []{ do { if (sk_abort_is_enabled
()) { do { SkDebugf("%s:%d" ": fatal error: \"" "check(%s)" "\"\n"
, "/root/firefox-clang/gfx/skia/skia/src/core/SkScan_Antihair.cpp"
, 538, "clip == nullptr || !clip->getBounds().isEmpty()");
; sk_abort_no_print(); } while (false); } } while(false); }(
) )
;
539
540#ifdef TEST_GAMMA
541 build_gamma_table();
542#endif
543
544 const SkScalar max = SkIntToScalar(32767)static_cast<SkScalar>(32767);
545 const SkRect fixedBounds = SkRect::MakeLTRB(-max, -max, max, max);
546
547 SkRect clipBounds;
548 if (clip) {
549 clipBounds.set(clip->getBounds());
550 /* We perform integral clipping later on, but we do a scalar clip first
551 to ensure that our coordinates are expressible in fixed/integers.
552
553 antialiased hairlines can draw up to 1/2 of a pixel outside of
554 their bounds, so we need to outset the clip before calling the
555 clipper. To make the numerics safer, we outset by a whole pixel,
556 since the 1/2 pixel boundary is important to the antihair blitter,
557 we don't want to risk numerical fate by chopping on that edge.
558 */
559 clipBounds.outset(SK_Scalar11.0f, SK_Scalar11.0f);
560 }
561
562 for (size_t i = 0; i < src.size() - 1; ++i) {
563 SkPoint pts[2];
564
565 // We have to pre-clip the line to fit in a SkFixed, so we just chop
566 // the line. TODO find a way to actually draw beyond that range.
567 if (!SkLineClipper::IntersectLine(&src[i], fixedBounds, pts)) {
568 continue;
569 }
570
571 if (clip && !SkLineClipper::IntersectLine(pts, clipBounds, pts)) {
572 continue;
573 }
574
575 SkFDot6 x0 = SkScalarToFDot6(pts[0].fX)(SkFDot6)((pts[0].fX) * (64));
576 SkFDot6 y0 = SkScalarToFDot6(pts[0].fY)(SkFDot6)((pts[0].fY) * (64));
577 SkFDot6 x1 = SkScalarToFDot6(pts[1].fX)(SkFDot6)((pts[1].fX) * (64));
578 SkFDot6 y1 = SkScalarToFDot6(pts[1].fY)(SkFDot6)((pts[1].fY) * (64));
579
580 if (clip) {
581 SkFDot6 left = std::min(x0, x1);
582 SkFDot6 top = std::min(y0, y1);
583 SkFDot6 right = std::max(x0, x1);
584 SkFDot6 bottom = std::max(y0, y1);
585 SkIRect ir;
586
587 ir.setLTRB(SkFDot6Floor(left)((left) >> 6) - 1,
588 SkFDot6Floor(top)((top) >> 6) - 1,
589 SkFDot6Ceil(right)(((right) + 63) >> 6) + 1,
590 SkFDot6Ceil(bottom)(((bottom) + 63) >> 6) + 1);
591
592 if (clip->quickReject(ir)) {
593 continue;
594 }
595 if (!clip->quickContains(ir)) {
596 SkRegion::Cliperator iter(*clip, ir);
597 const SkIRect* r = &iter.rect();
598
599 while (!iter.done()) {
600 do_anti_hairline(x0, y0, x1, y1, r, blitter);
601 iter.next();
602 }
603 continue;
604 }
605 // fall through to no-clip case
606 }
607 do_anti_hairline(x0, y0, x1, y1, nullptr, blitter);
608 }
609}
610
611void SkScan::AntiHairRect(const SkRect& rect, const SkRasterClip& clip,
612 SkBlitter* blitter) {
613 SkPoint pts[5];
614
615 pts[0].set(rect.fLeft, rect.fTop);
616 pts[1].set(rect.fRight, rect.fTop);
617 pts[2].set(rect.fRight, rect.fBottom);
618 pts[3].set(rect.fLeft, rect.fBottom);
619 pts[4] = pts[0];
620 SkScan::AntiHairLine(pts, clip, blitter);
621}
622
623///////////////////////////////////////////////////////////////////////////////
624
625typedef int FDot8; // 24.8 integer fixed point
626
627static inline FDot8 SkFixedToFDot8(SkFixed x) {
628 return (x + 0x80) >> 8;
629}
630
631static void do_scanline(FDot8 L, int top, FDot8 R, U8CPU alpha,
632 SkBlitter* blitter) {
633 SkASSERT(L < R)static_cast<void>( __builtin_expect(static_cast<bool
>(L < R), 1) ? static_cast<void>(0) : []{ do { if
(sk_abort_is_enabled()) { do { SkDebugf("%s:%d" ": fatal error: \""
"check(%s)" "\"\n", "/root/firefox-clang/gfx/skia/skia/src/core/SkScan_Antihair.cpp"
, 633, "L < R"); ; sk_abort_no_print(); } while (false); }
} while(false); }() )
;
634
635 if ((L >> 8) == ((R - 1) >> 8)) { // 1x1 pixel
636 blitter->blitV(L >> 8, top, 1, SkAlphaMul(alpha, R - L)(((alpha) * (R - L)) >> 8));
637 return;
638 }
639
640 int left = L >> 8;
641
642 if (L & 0xFF) {
643 blitter->blitV(left, top, 1, SkAlphaMul(alpha, 256 - (L & 0xFF))(((alpha) * (256 - (L & 0xFF))) >> 8));
644 left += 1;
645 }
646
647 int rite = R >> 8;
648 int width = rite - left;
649 if (width > 0) {
650 call_hline_blitter(blitter, left, top, width, alpha);
651 }
652 if (R & 0xFF) {
653 blitter->blitV(rite, top, 1, SkAlphaMul(alpha, R & 0xFF)(((alpha) * (R & 0xFF)) >> 8));
654 }
655}
656
657static void antifilldot8(FDot8 L, FDot8 T, FDot8 R, FDot8 B, SkBlitter* blitter,
658 bool fillInner) {
659 // check for empty now that we're in our reduced precision space
660 if (L >= R || T >= B) {
661 return;
662 }
663 int top = T >> 8;
664 if (top == ((B - 1) >> 8)) { // just one scanline high
665 do_scanline(L, top, R, B - T - 1, blitter);
666 return;
667 }
668
669 if (T & 0xFF) {
670 do_scanline(L, top, R, 256 - (T & 0xFF), blitter);
671 top += 1;
672 }
673
674 int bot = B >> 8;
675 int height = bot - top;
676 if (height > 0) {
677 int left = L >> 8;
678 if (left == ((R - 1) >> 8)) { // just 1-pixel wide
679 blitter->blitV(left, top, height, R - L - 1);
680 } else {
681 if (L & 0xFF) {
682 blitter->blitV(left, top, height, 256 - (L & 0xFF));
683 left += 1;
684 }
685 int rite = R >> 8;
686 int width = rite - left;
687 if (width > 0 && fillInner) {
688 blitter->blitRect(left, top, width, height);
689 }
690 if (R & 0xFF) {
691 blitter->blitV(rite, top, height, R & 0xFF);
692 }
693 }
694 }
695
696 if (B & 0xFF) {
697 do_scanline(L, bot, R, B & 0xFF, blitter);
698 }
699}
700
701static void antifillrect(const SkXRect& xr, SkBlitter* blitter) {
702 antifilldot8(SkFixedToFDot8(xr.fLeft), SkFixedToFDot8(xr.fTop),
703 SkFixedToFDot8(xr.fRight), SkFixedToFDot8(xr.fBottom),
704 blitter, true);
705}
706
707///////////////////////////////////////////////////////////////////////////////
708
709void SkScan::AntiFillXRect(const SkXRect& xr, const SkRegion* clip,
710 SkBlitter* blitter) {
711 if (nullptr == clip) {
712 antifillrect(xr, blitter);
713 } else {
714 SkIRect outerBounds;
715 XRect_roundOut(xr, &outerBounds);
716
717 if (clip->isRect()) {
718 const SkIRect& clipBounds = clip->getBounds();
719
720 if (clipBounds.contains(outerBounds)) {
721 antifillrect(xr, blitter);
722 } else {
723 SkXRect tmpR;
724 // this keeps our original edges fractional
725 XRect_set(&tmpR, clipBounds);
726 if (tmpR.intersect(xr)) {
727 antifillrect(tmpR, blitter);
728 }
729 }
730 } else {
731 SkRegion::Cliperator clipper(*clip, outerBounds);
732 const SkIRect& rr = clipper.rect();
733
734 while (!clipper.done()) {
735 SkXRect tmpR;
736
737 // this keeps our original edges fractional
738 XRect_set(&tmpR, rr);
739 if (tmpR.intersect(xr)) {
740 antifillrect(tmpR, blitter);
741 }
742 clipper.next();
743 }
744 }
745 }
746}
747
748void SkScan::AntiFillXRect(const SkXRect& xr, const SkRasterClip& clip,
749 SkBlitter* blitter) {
750 if (clip.isBW()) {
751 AntiFillXRect(xr, &clip.bwRgn(), blitter);
752 } else {
753 SkIRect outerBounds;
754 XRect_roundOut(xr, &outerBounds);
755
756 if (clip.quickContains(outerBounds)) {
757 AntiFillXRect(xr, nullptr, blitter);
758 } else {
759 SkAAClipBlitterWrapper wrapper(clip, blitter);
760 AntiFillXRect(xr, &wrapper.getRgn(), wrapper.getBlitter());
761 }
762 }
763}
764
765/* This takes a float-rect, but with the key improvement that it has
766 already been clipped, so we know that it is safe to convert it into a
767 XRect (fixedpoint), as it won't overflow.
768*/
769static void antifillrect(const SkRect& r, SkBlitter* blitter) {
770 SkXRect xr;
771
772 XRect_set(&xr, r);
773 antifillrect(xr, blitter);
774}
775
776/* We repeat the clipping logic of AntiFillXRect because the float rect might
777 overflow if we blindly converted it to an XRect. This sucks that we have to
778 repeat the clipping logic, but I don't see how to share the code/logic.
779
780 We clip r (as needed) into one or more (smaller) float rects, and then pass
781 those to our version of antifillrect, which converts it into an XRect and
782 then calls the blit.
783*/
784void SkScan::AntiFillRect(const SkRect& origR, const SkRegion* clip,
785 SkBlitter* blitter) {
786 if (clip) {
787 SkRect newR;
788 newR.set(clip->getBounds());
789 if (!newR.intersect(origR)) {
790 return;
791 }
792
793 const SkIRect outerBounds = newR.roundOut();
794
795 if (clip->isRect()) {
796 antifillrect(newR, blitter);
797 } else {
798 SkRegion::Cliperator clipper(*clip, outerBounds);
799 while (!clipper.done()) {
800 newR.set(clipper.rect());
801 if (newR.intersect(origR)) {
802 antifillrect(newR, blitter);
803 }
804 clipper.next();
805 }
806 }
807 } else {
808 antifillrect(origR, blitter);
809 }
810}
811
812void SkScan::AntiFillRect(const SkRect& r, const SkRasterClip& clip,
813 SkBlitter* blitter) {
814 if (clip.isBW()) {
815 AntiFillRect(r, &clip.bwRgn(), blitter);
816 } else {
817 SkAAClipBlitterWrapper wrap(clip, blitter);
818 AntiFillRect(r, &wrap.getRgn(), wrap.getBlitter());
819 }
820}
821
822///////////////////////////////////////////////////////////////////////////////
823
824#define SkAlphaMulRound(a, b)SkMulDiv255Round(a, b) SkMulDiv255Round(a, b)
825
826// calls blitRect() if the rectangle is non-empty
827static void fillcheckrect(int L, int T, int R, int B, SkBlitter* blitter) {
828 if (L < R && T < B) {
829 blitter->blitRect(L, T, R - L, B - T);
830 }
831}
832
833static inline FDot8 SkScalarToFDot8(SkScalar x) {
834 return (int)(x * 256);
835}
836
837static inline int FDot8Floor(FDot8 x) {
838 return x >> 8;
839}
840
841static inline int FDot8Ceil(FDot8 x) {
842 return (x + 0xFF) >> 8;
843}
844
845// 1 - (1 - a)*(1 - b)
846static inline U8CPU InvAlphaMul(U8CPU a, U8CPU b) {
847 // need precise rounding (not just SkAlphaMul) so that values like
848 // a=228, b=252 don't overflow the result
849 return SkToU8(a + b - SkAlphaMulRound(a, b)SkMulDiv255Round(a, b));
850}
851
852static void inner_scanline(FDot8 L, int top, FDot8 R, U8CPU alpha,
853 SkBlitter* blitter) {
854 SkASSERT(L < R)static_cast<void>( __builtin_expect(static_cast<bool
>(L < R), 1) ? static_cast<void>(0) : []{ do { if
(sk_abort_is_enabled()) { do { SkDebugf("%s:%d" ": fatal error: \""
"check(%s)" "\"\n", "/root/firefox-clang/gfx/skia/skia/src/core/SkScan_Antihair.cpp"
, 854, "L < R"); ; sk_abort_no_print(); } while (false); }
} while(false); }() )
;
855
856 if ((L >> 8) == ((R - 1) >> 8)) { // 1x1 pixel
857 FDot8 widClamp = R - L;
858 // border case clamp 256 to 255 instead of going through call_hline_blitter
859 // see skbug/4406
860 widClamp = widClamp - (widClamp >> 8);
861 blitter->blitV(L >> 8, top, 1, InvAlphaMul(alpha, widClamp));
862 return;
863 }
864
865 int left = L >> 8;
866 if (L & 0xFF) {
867 blitter->blitV(left, top, 1, InvAlphaMul(alpha, L & 0xFF));
868 left += 1;
869 }
870
871 int rite = R >> 8;
872 int width = rite - left;
873 if (width > 0) {
874 call_hline_blitter(blitter, left, top, width, alpha);
875 }
876
877 if (R & 0xFF) {
878 blitter->blitV(rite, top, 1, InvAlphaMul(alpha, ~R & 0xFF));
879 }
880}
881
882static void innerstrokedot8(FDot8 L, FDot8 T, FDot8 R, FDot8 B,
883 SkBlitter* blitter) {
884 SkASSERT(L < R && T < B)static_cast<void>( __builtin_expect(static_cast<bool
>(L < R && T < B), 1) ? static_cast<void>
(0) : []{ do { if (sk_abort_is_enabled()) { do { SkDebugf("%s:%d"
": fatal error: \"" "check(%s)" "\"\n", "/root/firefox-clang/gfx/skia/skia/src/core/SkScan_Antihair.cpp"
, 884, "L < R && T < B"); ; sk_abort_no_print()
; } while (false); } } while(false); }() )
;
885
886 int top = T >> 8;
887 if (top == ((B - 1) >> 8)) { // just one scanline high
888 // We want the inverse of B-T, since we're the inner-stroke
889 int alpha = 256 - (B - T);
890 if (alpha) {
891 inner_scanline(L, top, R, alpha, blitter);
892 }
893 return;
894 }
895
896 if (T & 0xFF) {
897 inner_scanline(L, top, R, T & 0xFF, blitter);
898 top += 1;
899 }
900
901 int bot = B >> 8;
902 int height = bot - top;
903 if (height > 0) {
904 if (L & 0xFF) {
905 blitter->blitV(L >> 8, top, height, L & 0xFF);
906 }
907 if (R & 0xFF) {
908 blitter->blitV(R >> 8, top, height, ~R & 0xFF);
909 }
910 }
911
912 if (B & 0xFF) {
913 inner_scanline(L, bot, R, ~B & 0xFF, blitter);
914 }
915}
916
917static inline void align_thin_stroke(FDot8& edge1, FDot8& edge2) {
918 SkASSERT(edge1 <= edge2)static_cast<void>( __builtin_expect(static_cast<bool
>(edge1 <= edge2), 1) ? static_cast<void>(0) : []
{ do { if (sk_abort_is_enabled()) { do { SkDebugf("%s:%d" ": fatal error: \""
"check(%s)" "\"\n", "/root/firefox-clang/gfx/skia/skia/src/core/SkScan_Antihair.cpp"
, 918, "edge1 <= edge2"); ; sk_abort_no_print(); } while (
false); } } while(false); }() )
;
919
920 if (FDot8Floor(edge1) == FDot8Floor(edge2)) {
921 edge2 -= (edge1 & 0xFF);
922 edge1 &= ~0xFF;
923 }
924}
925
926void SkScan::AntiFrameRect(const SkRect& r, const SkPoint& strokeSize,
927 const SkRegion* clip, SkBlitter* blitter) {
928 SkASSERT(strokeSize.fX >= 0 && strokeSize.fY >= 0)static_cast<void>( __builtin_expect(static_cast<bool
>(strokeSize.fX >= 0 && strokeSize.fY >= 0),
1) ? static_cast<void>(0) : []{ do { if (sk_abort_is_enabled
()) { do { SkDebugf("%s:%d" ": fatal error: \"" "check(%s)" "\"\n"
, "/root/firefox-clang/gfx/skia/skia/src/core/SkScan_Antihair.cpp"
, 928, "strokeSize.fX >= 0 && strokeSize.fY >= 0"
); ; sk_abort_no_print(); } while (false); } } while(false); }
() )
;
929
930 SkScalar rx = SkScalarHalf(strokeSize.fX)((strokeSize.fX) * 0.5f);
931 SkScalar ry = SkScalarHalf(strokeSize.fY)((strokeSize.fY) * 0.5f);
932
933 // If we're empty on either axis, we remove the outset amount, to be sure
934 // we stroke the same way a polygon would (i.e. it would just see a "line"
935 // and not extend it for the miter join).
936 if (r.width() == 0) {
937 ry = 0;
938 }
939 if (r.height() == 0) {
940 rx = 0;
941 }
942
943 // outset by the radius
944 FDot8 outerL = SkScalarToFDot8(r.fLeft - rx);
945 FDot8 outerT = SkScalarToFDot8(r.fTop - ry);
946 FDot8 outerR = SkScalarToFDot8(r.fRight + rx);
947 FDot8 outerB = SkScalarToFDot8(r.fBottom + ry);
948
949 SkIRect outer;
950 // set outer to the outer rect of the outer section
951 outer.setLTRB(FDot8Floor(outerL), FDot8Floor(outerT), FDot8Ceil(outerR), FDot8Ceil(outerB));
952
953
954 SkBlitterClipper clipper;
955 if (clip) {
956 if (clip->quickReject(outer)) {
957 return;
958 }
959 if (!clip->contains(outer)) {
960 blitter = clipper.apply(blitter, clip, &outer);
961 }
962 // now we can ignore clip for the rest of the function
963 }
964
965 // in case we lost a bit with diameter/2
966 rx = strokeSize.fX - rx;
967 ry = strokeSize.fY - ry;
968
969 // inset by the radius
970 FDot8 innerL = SkScalarToFDot8(r.fLeft + rx);
971 FDot8 innerT = SkScalarToFDot8(r.fTop + ry);
972 FDot8 innerR = SkScalarToFDot8(r.fRight - rx);
973 FDot8 innerB = SkScalarToFDot8(r.fBottom - ry);
974
975 // For sub-unit strokes, tweak the hulls such that one of the edges coincides with the pixel
976 // edge. This ensures that the general rect stroking logic below
977 // a) doesn't blit the same scanline twice
978 // b) computes the correct coverage when both edges fall within the same pixel
979 if (strokeSize.fX < 1 || strokeSize.fY < 1) {
980 align_thin_stroke(outerL, innerL);
981 align_thin_stroke(outerT, innerT);
982 align_thin_stroke(innerR, outerR);
983 align_thin_stroke(innerB, outerB);
984 }
985
986 // stroke the outer hull
987 antifilldot8(outerL, outerT, outerR, outerB, blitter, false);
988
989 // set outer to the outer rect of the middle section
990 outer.setLTRB(FDot8Ceil(outerL), FDot8Ceil(outerT), FDot8Floor(outerR), FDot8Floor(outerB));
991
992 if (innerL >= innerR || innerT >= innerB) {
993 fillcheckrect(outer.fLeft, outer.fTop, outer.fRight, outer.fBottom,
994 blitter);
995 } else {
996 SkIRect inner;
997 // set inner to the inner rect of the middle section
998 inner.setLTRB(FDot8Floor(innerL), FDot8Floor(innerT), FDot8Ceil(innerR), FDot8Ceil(innerB));
999
1000 // draw the frame in 4 pieces
1001 fillcheckrect(outer.fLeft, outer.fTop, outer.fRight, inner.fTop,
1002 blitter);
1003 fillcheckrect(outer.fLeft, inner.fTop, inner.fLeft, inner.fBottom,
1004 blitter);
1005 fillcheckrect(inner.fRight, inner.fTop, outer.fRight, inner.fBottom,
1006 blitter);
1007 fillcheckrect(outer.fLeft, inner.fBottom, outer.fRight, outer.fBottom,
1008 blitter);
1009
1010 // now stroke the inner rect, which is similar to antifilldot8() except that
1011 // it treats the fractional coordinates with the inverse bias (since its
1012 // inner).
1013 innerstrokedot8(innerL, innerT, innerR, innerB, blitter);
1014 }
1015}
1016
1017void SkScan::AntiFrameRect(const SkRect& r, const SkPoint& strokeSize,
1018 const SkRasterClip& clip, SkBlitter* blitter) {
1019 if (clip.isBW()) {
1020 AntiFrameRect(r, strokeSize, &clip.bwRgn(), blitter);
1021 } else {
1022 SkAAClipBlitterWrapper wrap(clip, blitter);
1023 AntiFrameRect(r, strokeSize, &wrap.getRgn(), wrap.getBlitter());
1024 }
1025}