blob: a552253ffaa12b227d383caaaa1e8242ad87adad [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001/*
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
reed@android.com8a1c16f2008-12-17 15:59:43 +00008
9#include "SkScan.h"
10#include "SkBlitter.h"
Cary Clarka4083c92017-09-15 11:59:23 -040011#include "SkColorData.h"
reed@android.come28ff552009-11-19 20:46:39 +000012#include "SkLineClipper.h"
reed@google.com045e62d2011-10-24 12:19:46 +000013#include "SkRasterClip.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000014#include "SkFDot6.h"
15
reed@android.com88983632009-03-23 16:05:19 +000016/* Our attempt to compute the worst case "bounds" for the horizontal and
17 vertical cases has some numerical bug in it, and we sometimes undervalue
18 our extends. The bug is that when this happens, we will set the clip to
halcanary96fcdcc2015-08-27 07:41:13 -070019 nullptr (for speed), and thus draw outside of the clip by a pixel, which might
reed@android.com88983632009-03-23 16:05:19 +000020 only look bad, but it might also access memory outside of the valid range
21 allcoated for the device bitmap.
reed@android.com6c14b432009-03-23 20:11:11 +000022
reed@android.com88983632009-03-23 16:05:19 +000023 This define enables our fix to outset our "bounds" by 1, thus avoiding the
24 chance of the bug, but at the cost of sometimes taking the rectblitter
halcanary96fcdcc2015-08-27 07:41:13 -070025 case (i.e. not setting the clip to nullptr) when we might not actually need
reed@android.com88983632009-03-23 16:05:19 +000026 to. If we can improve/fix the actual calculations, then we can remove this
27 step.
28 */
29#define OUTSET_BEFORE_CLIP_TEST true
30
reed@android.com8a1c16f2008-12-17 15:59:43 +000031#define HLINE_STACK_BUFFER 100
32
33static inline int SmallDot6Scale(int value, int dot6) {
34 SkASSERT((int16_t)value == value);
35 SkASSERT((unsigned)dot6 <= 64);
mtklein38484272015-08-07 08:48:12 -070036 return (value * dot6) >> 6;
reed@android.com8a1c16f2008-12-17 15:59:43 +000037}
38
39//#define TEST_GAMMA
40
41#ifdef TEST_GAMMA
42 static uint8_t gGammaTable[256];
43 #define ApplyGamma(table, alpha) (table)[alpha]
44
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +000045 static void build_gamma_table() {
reed@android.com8a1c16f2008-12-17 15:59:43 +000046 static bool gInit = false;
47
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +000048 if (gInit == false) {
49 for (int i = 0; i < 256; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000050 SkFixed n = i * 257;
51 n += n >> 15;
52 SkASSERT(n >= 0 && n <= SK_Fixed1);
53 n = SkFixedSqrt(n);
54 n = n * 255 >> 16;
55 // SkDebugf("morph %d -> %d\n", i, n);
56 gGammaTable[i] = SkToU8(n);
57 }
58 gInit = true;
59 }
60 }
61#else
62 #define ApplyGamma(table, alpha) SkToU8(alpha)
63#endif
64
65///////////////////////////////////////////////////////////////////////////////
66
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +000067static void call_hline_blitter(SkBlitter* blitter, int x, int y, int count,
68 U8CPU alpha) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000069 SkASSERT(count > 0);
skia.committer@gmail.com27b40e92012-10-26 02:01:24 +000070
reed@android.com8a1c16f2008-12-17 15:59:43 +000071 int16_t runs[HLINE_STACK_BUFFER + 1];
72 uint8_t aa[HLINE_STACK_BUFFER];
skia.committer@gmail.com27b40e92012-10-26 02:01:24 +000073
reed@android.com8a1c16f2008-12-17 15:59:43 +000074 aa[0] = ApplyGamma(gGammaTable, alpha);
75 do {
76 int n = count;
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +000077 if (n > HLINE_STACK_BUFFER) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000078 n = HLINE_STACK_BUFFER;
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +000079 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000080 runs[0] = SkToS16(n);
81 runs[n] = 0;
82 blitter->blitAntiH(x, y, aa, runs);
83 x += n;
84 count -= n;
85 } while (count > 0);
86}
87
reed@google.comb03fe422012-10-25 17:37:03 +000088class SkAntiHairBlitter {
89public:
halcanary96fcdcc2015-08-27 07:41:13 -070090 SkAntiHairBlitter() : fBlitter(nullptr) {}
djsollen@google.comade109f2013-01-04 15:29:06 +000091 virtual ~SkAntiHairBlitter() {}
reed@android.com8a1c16f2008-12-17 15:59:43 +000092
reed@google.comb03fe422012-10-25 17:37:03 +000093 SkBlitter* getBlitter() const { return fBlitter; }
reed@android.com8a1c16f2008-12-17 15:59:43 +000094
reed@google.comb03fe422012-10-25 17:37:03 +000095 void setup(SkBlitter* blitter) {
96 fBlitter = blitter;
reed@android.com8a1c16f2008-12-17 15:59:43 +000097 }
98
reed@google.comb03fe422012-10-25 17:37:03 +000099 virtual SkFixed drawCap(int x, SkFixed fy, SkFixed slope, int mod64) = 0;
100 virtual SkFixed drawLine(int x, int stopx, SkFixed fy, SkFixed slope) = 0;
101
102private:
103 SkBlitter* fBlitter;
104};
105
106class HLine_SkAntiHairBlitter : public SkAntiHairBlitter {
107public:
mtklein36352bf2015-03-25 18:17:31 -0700108 SkFixed drawCap(int x, SkFixed fy, SkFixed slope, int mod64) override {
reed@google.comb03fe422012-10-25 17:37:03 +0000109 fy += SK_Fixed1/2;
skia.committer@gmail.com27b40e92012-10-26 02:01:24 +0000110
reed@google.comb03fe422012-10-25 17:37:03 +0000111 int y = fy >> 16;
112 uint8_t a = (uint8_t)(fy >> 8);
skia.committer@gmail.com27b40e92012-10-26 02:01:24 +0000113
reed@google.comb03fe422012-10-25 17:37:03 +0000114 // lower line
115 unsigned ma = SmallDot6Scale(a, mod64);
116 if (ma) {
117 call_hline_blitter(this->getBlitter(), x, y, 1, ma);
118 }
skia.committer@gmail.com27b40e92012-10-26 02:01:24 +0000119
reed@google.comb03fe422012-10-25 17:37:03 +0000120 // upper line
121 ma = SmallDot6Scale(255 - a, mod64);
122 if (ma) {
123 call_hline_blitter(this->getBlitter(), x, y - 1, 1, ma);
124 }
skia.committer@gmail.com27b40e92012-10-26 02:01:24 +0000125
reed@google.comb03fe422012-10-25 17:37:03 +0000126 return fy - SK_Fixed1/2;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000127 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000128
reed@google.comb03fe422012-10-25 17:37:03 +0000129 virtual SkFixed drawLine(int x, int stopx, SkFixed fy,
mtklein36352bf2015-03-25 18:17:31 -0700130 SkFixed slope) override {
reed@google.comb03fe422012-10-25 17:37:03 +0000131 SkASSERT(x < stopx);
132 int count = stopx - x;
133 fy += SK_Fixed1/2;
skia.committer@gmail.com27b40e92012-10-26 02:01:24 +0000134
reed@google.comb03fe422012-10-25 17:37:03 +0000135 int y = fy >> 16;
136 uint8_t a = (uint8_t)(fy >> 8);
skia.committer@gmail.com27b40e92012-10-26 02:01:24 +0000137
reed@google.comb03fe422012-10-25 17:37:03 +0000138 // lower line
139 if (a) {
140 call_hline_blitter(this->getBlitter(), x, y, count, a);
141 }
skia.committer@gmail.com27b40e92012-10-26 02:01:24 +0000142
reed@google.comb03fe422012-10-25 17:37:03 +0000143 // upper line
144 a = 255 - a;
145 if (a) {
146 call_hline_blitter(this->getBlitter(), x, y - 1, count, a);
147 }
skia.committer@gmail.com27b40e92012-10-26 02:01:24 +0000148
reed@google.comb03fe422012-10-25 17:37:03 +0000149 return fy - SK_Fixed1/2;
150 }
151};
reed@android.com8a1c16f2008-12-17 15:59:43 +0000152
reed@google.comb03fe422012-10-25 17:37:03 +0000153class Horish_SkAntiHairBlitter : public SkAntiHairBlitter {
154public:
mtklein36352bf2015-03-25 18:17:31 -0700155 SkFixed drawCap(int x, SkFixed fy, SkFixed dy, int mod64) override {
reed@google.comb03fe422012-10-25 17:37:03 +0000156 fy += SK_Fixed1/2;
mtklein38484272015-08-07 08:48:12 -0700157
reed@android.com8a1c16f2008-12-17 15:59:43 +0000158 int lower_y = fy >> 16;
159 uint8_t a = (uint8_t)(fy >> 8);
reed793a6dd2015-04-15 07:51:15 -0700160 unsigned a0 = SmallDot6Scale(255 - a, mod64);
161 unsigned a1 = SmallDot6Scale(a, mod64);
162 this->getBlitter()->blitAntiV2(x, lower_y - 1, a0, a1);
mtklein38484272015-08-07 08:48:12 -0700163
reed793a6dd2015-04-15 07:51:15 -0700164 return fy + dy - SK_Fixed1/2;
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000165 }
mtklein38484272015-08-07 08:48:12 -0700166
mtklein36352bf2015-03-25 18:17:31 -0700167 SkFixed drawLine(int x, int stopx, SkFixed fy, SkFixed dy) override {
reed@google.comb03fe422012-10-25 17:37:03 +0000168 SkASSERT(x < stopx);
mtklein38484272015-08-07 08:48:12 -0700169
reed@google.comb03fe422012-10-25 17:37:03 +0000170 fy += SK_Fixed1/2;
171 SkBlitter* blitter = this->getBlitter();
172 do {
173 int lower_y = fy >> 16;
174 uint8_t a = (uint8_t)(fy >> 8);
reed793a6dd2015-04-15 07:51:15 -0700175 blitter->blitAntiV2(x, lower_y - 1, 255 - a, a);
reed@google.comb03fe422012-10-25 17:37:03 +0000176 fy += dy;
177 } while (++x < stopx);
mtklein38484272015-08-07 08:48:12 -0700178
reed@google.comb03fe422012-10-25 17:37:03 +0000179 return fy - SK_Fixed1/2;
180 }
181};
182
183class VLine_SkAntiHairBlitter : public SkAntiHairBlitter {
184public:
mtklein36352bf2015-03-25 18:17:31 -0700185 SkFixed drawCap(int y, SkFixed fx, SkFixed dx, int mod64) override {
reed@google.comb03fe422012-10-25 17:37:03 +0000186 SkASSERT(0 == dx);
187 fx += SK_Fixed1/2;
skia.committer@gmail.com27b40e92012-10-26 02:01:24 +0000188
reed@google.comb03fe422012-10-25 17:37:03 +0000189 int x = fx >> 16;
190 int a = (uint8_t)(fx >> 8);
skia.committer@gmail.com27b40e92012-10-26 02:01:24 +0000191
reed@google.comb03fe422012-10-25 17:37:03 +0000192 unsigned ma = SmallDot6Scale(a, mod64);
193 if (ma) {
194 this->getBlitter()->blitV(x, y, 1, ma);
195 }
196 ma = SmallDot6Scale(255 - a, mod64);
197 if (ma) {
198 this->getBlitter()->blitV(x - 1, y, 1, ma);
199 }
skia.committer@gmail.com27b40e92012-10-26 02:01:24 +0000200
reed@google.comb03fe422012-10-25 17:37:03 +0000201 return fx - SK_Fixed1/2;
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000202 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000203
mtklein36352bf2015-03-25 18:17:31 -0700204 SkFixed drawLine(int y, int stopy, SkFixed fx, SkFixed dx) override {
reed@google.comb03fe422012-10-25 17:37:03 +0000205 SkASSERT(y < stopy);
206 SkASSERT(0 == dx);
207 fx += SK_Fixed1/2;
skia.committer@gmail.com27b40e92012-10-26 02:01:24 +0000208
reed@google.comb03fe422012-10-25 17:37:03 +0000209 int x = fx >> 16;
210 int a = (uint8_t)(fx >> 8);
skia.committer@gmail.com27b40e92012-10-26 02:01:24 +0000211
reed@google.comb03fe422012-10-25 17:37:03 +0000212 if (a) {
213 this->getBlitter()->blitV(x, y, stopy - y, a);
214 }
215 a = 255 - a;
216 if (a) {
217 this->getBlitter()->blitV(x - 1, y, stopy - y, a);
218 }
skia.committer@gmail.com27b40e92012-10-26 02:01:24 +0000219
reed@google.comb03fe422012-10-25 17:37:03 +0000220 return fx - SK_Fixed1/2;
221 }
222};
reed@android.com8a1c16f2008-12-17 15:59:43 +0000223
reed@google.comb03fe422012-10-25 17:37:03 +0000224class Vertish_SkAntiHairBlitter : public SkAntiHairBlitter {
225public:
mtklein36352bf2015-03-25 18:17:31 -0700226 SkFixed drawCap(int y, SkFixed fx, SkFixed dx, int mod64) override {
reed@google.comb03fe422012-10-25 17:37:03 +0000227 fx += SK_Fixed1/2;
mtklein38484272015-08-07 08:48:12 -0700228
reed@android.com8a1c16f2008-12-17 15:59:43 +0000229 int x = fx >> 16;
reed793a6dd2015-04-15 07:51:15 -0700230 uint8_t a = (uint8_t)(fx >> 8);
231 this->getBlitter()->blitAntiH2(x - 1, y,
232 SmallDot6Scale(255 - a, mod64), SmallDot6Scale(a, mod64));
mtklein38484272015-08-07 08:48:12 -0700233
reed793a6dd2015-04-15 07:51:15 -0700234 return fx + dx - SK_Fixed1/2;
reed@google.comb03fe422012-10-25 17:37:03 +0000235 }
mtklein38484272015-08-07 08:48:12 -0700236
mtklein36352bf2015-03-25 18:17:31 -0700237 SkFixed drawLine(int y, int stopy, SkFixed fx, SkFixed dx) override {
reed@google.comb03fe422012-10-25 17:37:03 +0000238 SkASSERT(y < stopy);
reed@google.comb03fe422012-10-25 17:37:03 +0000239 fx += SK_Fixed1/2;
240 do {
241 int x = fx >> 16;
reed793a6dd2015-04-15 07:51:15 -0700242 uint8_t a = (uint8_t)(fx >> 8);
243 this->getBlitter()->blitAntiH2(x - 1, y, 255 - a, a);
reed@google.comb03fe422012-10-25 17:37:03 +0000244 fx += dx;
245 } while (++y < stopy);
mtklein38484272015-08-07 08:48:12 -0700246
reed@google.comb03fe422012-10-25 17:37:03 +0000247 return fx - SK_Fixed1/2;
248 }
249};
reed@android.com8a1c16f2008-12-17 15:59:43 +0000250
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000251static inline SkFixed fastfixdiv(SkFDot6 a, SkFDot6 b) {
caryclark3127c992015-12-09 12:02:30 -0800252 SkASSERT((SkLeftShift(a, 16) >> 16) == a);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000253 SkASSERT(b != 0);
caryclark3127c992015-12-09 12:02:30 -0800254 return SkLeftShift(a, 16) / b;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000255}
256
reed@google.com8a812c22012-03-30 16:24:00 +0000257#define SkBITCOUNT(x) (sizeof(x) << 3)
258
259#if 1
260// returns high-bit set iff x==0x8000...
261static inline int bad_int(int x) {
262 return x & -x;
263}
264
265static int any_bad_ints(int a, int b, int c, int d) {
266 return (bad_int(a) | bad_int(b) | bad_int(c) | bad_int(d)) >> (SkBITCOUNT(int) - 1);
267}
268#else
269static inline int good_int(int x) {
270 return x ^ (1 << (SkBITCOUNT(x) - 1));
271}
272
273static int any_bad_ints(int a, int b, int c, int d) {
274 return !(good_int(a) & good_int(b) & good_int(c) & good_int(d));
275}
276#endif
277
humper@google.com0e515772013-01-07 19:54:40 +0000278#ifdef SK_DEBUG
reed@google.com99b300e2012-04-17 20:43:47 +0000279static bool canConvertFDot6ToFixed(SkFDot6 x) {
280 const int maxDot6 = SK_MaxS32 >> (16 - 6);
281 return SkAbs32(x) <= maxDot6;
282}
humper@google.com0e515772013-01-07 19:54:40 +0000283#endif
reed@google.com99b300e2012-04-17 20:43:47 +0000284
reed@google.comc1f6db82012-10-31 17:57:01 +0000285/*
286 * We want the fractional part of ordinate, but we want multiples of 64 to
287 * return 64, not 0, so we can't just say (ordinate & 63).
288 * We basically want to compute those bits, and if they're 0, return 64.
289 * We can do that w/o a branch with an extra sub and add.
290 */
291static int contribution_64(SkFDot6 ordinate) {
292#if 0
293 int result = ordinate & 63;
294 if (0 == result) {
295 result = 64;
296 }
297#else
298 int result = ((ordinate - 1) & 63) + 1;
299#endif
300 SkASSERT(result > 0 && result <= 64);
301 return result;
302}
303
reed@android.com8a1c16f2008-12-17 15:59:43 +0000304static void do_anti_hairline(SkFDot6 x0, SkFDot6 y0, SkFDot6 x1, SkFDot6 y1,
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000305 const SkIRect* clip, SkBlitter* blitter) {
reed@google.com8a812c22012-03-30 16:24:00 +0000306 // check for integer NaN (0x80000000) which we can't handle (can't negate it)
307 // It appears typically from a huge float (inf or nan) being converted to int.
308 // If we see it, just don't draw.
309 if (any_bad_ints(x0, y0, x1, y1)) {
310 return;
311 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000312
reed@google.com99b300e2012-04-17 20:43:47 +0000313 // The caller must clip the line to [-32767.0 ... 32767.0] ahead of time
314 // (in dot6 format)
315 SkASSERT(canConvertFDot6ToFixed(x0));
316 SkASSERT(canConvertFDot6ToFixed(y0));
317 SkASSERT(canConvertFDot6ToFixed(x1));
318 SkASSERT(canConvertFDot6ToFixed(y1));
319
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000320 if (SkAbs32(x1 - x0) > SkIntToFDot6(511) || SkAbs32(y1 - y0) > SkIntToFDot6(511)) {
reed@android.com35555912009-03-16 18:46:55 +0000321 /* instead of (x0 + x1) >> 1, we shift each separately. This is less
322 precise, but avoids overflowing the intermediate result if the
323 values are huge. A better fix might be to clip the original pts
324 directly (i.e. do the divide), so we don't spend time subdividing
325 huge lines at all.
326 */
327 int hx = (x0 >> 1) + (x1 >> 1);
328 int hy = (y0 >> 1) + (y1 >> 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000329 do_anti_hairline(x0, y0, hx, hy, clip, blitter);
330 do_anti_hairline(hx, hy, x1, y1, clip, blitter);
331 return;
332 }
333
334 int scaleStart, scaleStop;
335 int istart, istop;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000336 SkFixed fstart, slope;
reed@google.comb03fe422012-10-25 17:37:03 +0000337
338 HLine_SkAntiHairBlitter hline_blitter;
339 Horish_SkAntiHairBlitter horish_blitter;
340 VLine_SkAntiHairBlitter vline_blitter;
341 Vertish_SkAntiHairBlitter vertish_blitter;
halcanary96fcdcc2015-08-27 07:41:13 -0700342 SkAntiHairBlitter* hairBlitter = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000343
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000344 if (SkAbs32(x1 - x0) > SkAbs32(y1 - y0)) { // mostly horizontal
reed@android.com8a1c16f2008-12-17 15:59:43 +0000345 if (x0 > x1) { // we want to go left-to-right
346 SkTSwap<SkFDot6>(x0, x1);
347 SkTSwap<SkFDot6>(y0, y1);
348 }
349
350 istart = SkFDot6Floor(x0);
351 istop = SkFDot6Ceil(x1);
352 fstart = SkFDot6ToFixed(y0);
353 if (y0 == y1) { // completely horizontal, take fast case
354 slope = 0;
reed@google.comb03fe422012-10-25 17:37:03 +0000355 hairBlitter = &hline_blitter;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000356 } else {
357 slope = fastfixdiv(y1 - y0, x1 - x0);
358 SkASSERT(slope >= -SK_Fixed1 && slope <= SK_Fixed1);
359 fstart += (slope * (32 - (x0 & 63)) + 32) >> 6;
reed@google.comb03fe422012-10-25 17:37:03 +0000360 hairBlitter = &horish_blitter;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000361 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000362
reed@android.com8a1c16f2008-12-17 15:59:43 +0000363 SkASSERT(istop > istart);
364 if (istop - istart == 1) {
reed@google.comc1f6db82012-10-31 17:57:01 +0000365 // we are within a single pixel
reed@android.com8a1c16f2008-12-17 15:59:43 +0000366 scaleStart = x1 - x0;
367 SkASSERT(scaleStart >= 0 && scaleStart <= 64);
368 scaleStop = 0;
369 } else {
370 scaleStart = 64 - (x0 & 63);
371 scaleStop = x1 & 63;
372 }
373
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000374 if (clip){
375 if (istart >= clip->fRight || istop <= clip->fLeft) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000376 return;
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000377 }
378 if (istart < clip->fLeft) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000379 fstart += slope * (clip->fLeft - istart);
380 istart = clip->fLeft;
381 scaleStart = 64;
reed@google.comc1f6db82012-10-31 17:57:01 +0000382 if (istop - istart == 1) {
383 // we are within a single pixel
384 scaleStart = contribution_64(x1);
385 scaleStop = 0;
386 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000387 }
reed@google.com0724f432012-03-15 14:34:40 +0000388 if (istop > clip->fRight) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000389 istop = clip->fRight;
reed@google.comadb2e242012-03-12 20:26:17 +0000390 scaleStop = 0; // so we don't draw this last column
reed@android.com8a1c16f2008-12-17 15:59:43 +0000391 }
reed@google.combbbe9ed2012-03-30 18:56:08 +0000392
reed@android.com8a1c16f2008-12-17 15:59:43 +0000393 SkASSERT(istart <= istop);
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000394 if (istart == istop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000395 return;
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000396 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000397 // now test if our Y values are completely inside the clip
398 int top, bottom;
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000399 if (slope >= 0) { // T2B
mike@reedtribe.org9fb00412014-01-06 03:02:37 +0000400 top = SkFixedFloorToInt(fstart - SK_FixedHalf);
401 bottom = SkFixedCeilToInt(fstart + (istop - istart - 1) * slope + SK_FixedHalf);
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000402 } else { // B2T
mike@reedtribe.org9fb00412014-01-06 03:02:37 +0000403 bottom = SkFixedCeilToInt(fstart + SK_FixedHalf);
404 top = SkFixedFloorToInt(fstart + (istop - istart - 1) * slope - SK_FixedHalf);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000405 }
reed@android.com6c14b432009-03-23 20:11:11 +0000406#ifdef OUTSET_BEFORE_CLIP_TEST
407 top -= 1;
408 bottom += 1;
409#endif
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000410 if (top >= clip->fBottom || bottom <= clip->fTop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000411 return;
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000412 }
413 if (clip->fTop <= top && clip->fBottom >= bottom) {
halcanary96fcdcc2015-08-27 07:41:13 -0700414 clip = nullptr;
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000415 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000416 }
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000417 } else { // mostly vertical
418 if (y0 > y1) { // we want to go top-to-bottom
reed@android.com8a1c16f2008-12-17 15:59:43 +0000419 SkTSwap<SkFDot6>(x0, x1);
420 SkTSwap<SkFDot6>(y0, y1);
421 }
422
423 istart = SkFDot6Floor(y0);
424 istop = SkFDot6Ceil(y1);
425 fstart = SkFDot6ToFixed(x0);
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000426 if (x0 == x1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000427 if (y0 == y1) { // are we zero length?
428 return; // nothing to do
429 }
430 slope = 0;
reed@google.comb03fe422012-10-25 17:37:03 +0000431 hairBlitter = &vline_blitter;
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000432 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000433 slope = fastfixdiv(x1 - x0, y1 - y0);
434 SkASSERT(slope <= SK_Fixed1 && slope >= -SK_Fixed1);
435 fstart += (slope * (32 - (y0 & 63)) + 32) >> 6;
reed@google.comb03fe422012-10-25 17:37:03 +0000436 hairBlitter = &vertish_blitter;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000437 }
438
439 SkASSERT(istop > istart);
440 if (istop - istart == 1) {
reed@google.comc1f6db82012-10-31 17:57:01 +0000441 // we are within a single pixel
reed@android.com8a1c16f2008-12-17 15:59:43 +0000442 scaleStart = y1 - y0;
443 SkASSERT(scaleStart >= 0 && scaleStart <= 64);
444 scaleStop = 0;
445 } else {
446 scaleStart = 64 - (y0 & 63);
447 scaleStop = y1 & 63;
448 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000449
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000450 if (clip) {
451 if (istart >= clip->fBottom || istop <= clip->fTop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000452 return;
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000453 }
454 if (istart < clip->fTop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000455 fstart += slope * (clip->fTop - istart);
456 istart = clip->fTop;
457 scaleStart = 64;
reed@google.comc1f6db82012-10-31 17:57:01 +0000458 if (istop - istart == 1) {
459 // we are within a single pixel
460 scaleStart = contribution_64(y1);
461 scaleStop = 0;
462 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000463 }
reed@google.com0724f432012-03-15 14:34:40 +0000464 if (istop > clip->fBottom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000465 istop = clip->fBottom;
reed@google.comadb2e242012-03-12 20:26:17 +0000466 scaleStop = 0; // so we don't draw this last row
reed@android.com8a1c16f2008-12-17 15:59:43 +0000467 }
reed@google.combbbe9ed2012-03-30 18:56:08 +0000468
reed@android.com8a1c16f2008-12-17 15:59:43 +0000469 SkASSERT(istart <= istop);
470 if (istart == istop)
471 return;
472
473 // now test if our X values are completely inside the clip
474 int left, right;
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000475 if (slope >= 0) { // L2R
mike@reedtribe.org9fb00412014-01-06 03:02:37 +0000476 left = SkFixedFloorToInt(fstart - SK_FixedHalf);
477 right = SkFixedCeilToInt(fstart + (istop - istart - 1) * slope + SK_FixedHalf);
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000478 } else { // R2L
mike@reedtribe.org9fb00412014-01-06 03:02:37 +0000479 right = SkFixedCeilToInt(fstart + SK_FixedHalf);
480 left = SkFixedFloorToInt(fstart + (istop - istart - 1) * slope - SK_FixedHalf);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000481 }
reed@android.com6c14b432009-03-23 20:11:11 +0000482#ifdef OUTSET_BEFORE_CLIP_TEST
483 left -= 1;
484 right += 1;
485#endif
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000486 if (left >= clip->fRight || right <= clip->fLeft) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000487 return;
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000488 }
489 if (clip->fLeft <= left && clip->fRight >= right) {
halcanary96fcdcc2015-08-27 07:41:13 -0700490 clip = nullptr;
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000491 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000492 }
493 }
494
495 SkRectClipBlitter rectClipper;
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000496 if (clip) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000497 rectClipper.init(blitter, *clip);
498 blitter = &rectClipper;
499 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000500
reed@google.comb03fe422012-10-25 17:37:03 +0000501 SkASSERT(hairBlitter);
502 hairBlitter->setup(blitter);
503
reed@google.comc1f6db82012-10-31 17:57:01 +0000504#ifdef SK_DEBUG
505 if (scaleStart > 0 && scaleStop > 0) {
506 // be sure we don't draw twice in the same pixel
507 SkASSERT(istart < istop - 1);
508 }
509#endif
510
reed@google.comb03fe422012-10-25 17:37:03 +0000511 fstart = hairBlitter->drawCap(istart, fstart, slope, scaleStart);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000512 istart += 1;
reed@android.com28937282009-08-28 15:34:46 +0000513 int fullSpans = istop - istart - (scaleStop > 0);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000514 if (fullSpans > 0) {
reed@google.comb03fe422012-10-25 17:37:03 +0000515 fstart = hairBlitter->drawLine(istart, istart + fullSpans, fstart, slope);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000516 }
517 if (scaleStop > 0) {
reed@google.comb03fe422012-10-25 17:37:03 +0000518 hairBlitter->drawCap(istop - 1, fstart, slope, scaleStop);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000519 }
520}
521
reed5dc6b7d2015-04-14 10:40:44 -0700522void SkScan::AntiHairLineRgn(const SkPoint array[], int arrayCount, const SkRegion* clip,
523 SkBlitter* blitter) {
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000524 if (clip && clip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000525 return;
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000526 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000527
halcanary96fcdcc2015-08-27 07:41:13 -0700528 SkASSERT(clip == nullptr || !clip->getBounds().isEmpty());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000529
530#ifdef TEST_GAMMA
531 build_gamma_table();
532#endif
533
reed5dc6b7d2015-04-14 10:40:44 -0700534 const SkScalar max = SkIntToScalar(32767);
535 const SkRect fixedBounds = SkRect::MakeLTRB(-max, -max, max, max);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000536
reed5dc6b7d2015-04-14 10:40:44 -0700537 SkRect clipBounds;
reed@android.come28ff552009-11-19 20:46:39 +0000538 if (clip) {
reed@android.come28ff552009-11-19 20:46:39 +0000539 clipBounds.set(clip->getBounds());
reed@android.coma3d90102009-11-30 12:48:33 +0000540 /* We perform integral clipping later on, but we do a scalar clip first
reed5dc6b7d2015-04-14 10:40:44 -0700541 to ensure that our coordinates are expressible in fixed/integers.
mtklein38484272015-08-07 08:48:12 -0700542
reed5dc6b7d2015-04-14 10:40:44 -0700543 antialiased hairlines can draw up to 1/2 of a pixel outside of
544 their bounds, so we need to outset the clip before calling the
545 clipper. To make the numerics safer, we outset by a whole pixel,
546 since the 1/2 pixel boundary is important to the antihair blitter,
547 we don't want to risk numerical fate by chopping on that edge.
reed@android.coma3d90102009-11-30 12:48:33 +0000548 */
reed11fa2242015-03-13 06:08:28 -0700549 clipBounds.outset(SK_Scalar1, SK_Scalar1);
reed@android.come28ff552009-11-19 20:46:39 +0000550 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000551
reed5dc6b7d2015-04-14 10:40:44 -0700552 for (int i = 0; i < arrayCount - 1; ++i) {
553 SkPoint pts[2];
reed@android.come28ff552009-11-19 20:46:39 +0000554
reed5dc6b7d2015-04-14 10:40:44 -0700555 // We have to pre-clip the line to fit in a SkFixed, so we just chop
556 // the line. TODO find a way to actually draw beyond that range.
557 if (!SkLineClipper::IntersectLine(&array[i], fixedBounds, pts)) {
558 continue;
reed@android.come28ff552009-11-19 20:46:39 +0000559 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000560
reed5dc6b7d2015-04-14 10:40:44 -0700561 if (clip && !SkLineClipper::IntersectLine(pts, clipBounds, pts)) {
562 continue;
563 }
564
565 SkFDot6 x0 = SkScalarToFDot6(pts[0].fX);
566 SkFDot6 y0 = SkScalarToFDot6(pts[0].fY);
567 SkFDot6 x1 = SkScalarToFDot6(pts[1].fX);
568 SkFDot6 y1 = SkScalarToFDot6(pts[1].fY);
569
570 if (clip) {
571 SkFDot6 left = SkMin32(x0, x1);
572 SkFDot6 top = SkMin32(y0, y1);
573 SkFDot6 right = SkMax32(x0, x1);
574 SkFDot6 bottom = SkMax32(y0, y1);
575 SkIRect ir;
576
577 ir.set( SkFDot6Floor(left) - 1,
578 SkFDot6Floor(top) - 1,
579 SkFDot6Ceil(right) + 1,
580 SkFDot6Ceil(bottom) + 1);
581
582 if (clip->quickReject(ir)) {
583 continue;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000584 }
reed5dc6b7d2015-04-14 10:40:44 -0700585 if (!clip->quickContains(ir)) {
586 SkRegion::Cliperator iter(*clip, ir);
587 const SkIRect* r = &iter.rect();
588
589 while (!iter.done()) {
590 do_anti_hairline(x0, y0, x1, y1, r, blitter);
591 iter.next();
592 }
593 continue;
594 }
595 // fall through to no-clip case
reed@android.com8a1c16f2008-12-17 15:59:43 +0000596 }
halcanary96fcdcc2015-08-27 07:41:13 -0700597 do_anti_hairline(x0, y0, x1, y1, nullptr, blitter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000598 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000599}
600
reed@google.com045e62d2011-10-24 12:19:46 +0000601void SkScan::AntiHairRect(const SkRect& rect, const SkRasterClip& clip,
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000602 SkBlitter* blitter) {
reed5dc6b7d2015-04-14 10:40:44 -0700603 SkPoint pts[5];
reed@android.com8a1c16f2008-12-17 15:59:43 +0000604
reed5dc6b7d2015-04-14 10:40:44 -0700605 pts[0].set(rect.fLeft, rect.fTop);
606 pts[1].set(rect.fRight, rect.fTop);
607 pts[2].set(rect.fRight, rect.fBottom);
608 pts[3].set(rect.fLeft, rect.fBottom);
609 pts[4] = pts[0];
610 SkScan::AntiHairLine(pts, 5, clip, blitter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000611}
612
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000613///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000614
615typedef int FDot8; // 24.8 integer fixed point
616
617static inline FDot8 SkFixedToFDot8(SkFixed x) {
618 return (x + 0x80) >> 8;
619}
620
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000621static void do_scanline(FDot8 L, int top, FDot8 R, U8CPU alpha,
622 SkBlitter* blitter) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000623 SkASSERT(L < R);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000624
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000625 if ((L >> 8) == ((R - 1) >> 8)) { // 1x1 pixel
reed@android.com8a1c16f2008-12-17 15:59:43 +0000626 blitter->blitV(L >> 8, top, 1, SkAlphaMul(alpha, R - L));
627 return;
628 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000629
reed@android.com8a1c16f2008-12-17 15:59:43 +0000630 int left = L >> 8;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000631
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000632 if (L & 0xFF) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000633 blitter->blitV(left, top, 1, SkAlphaMul(alpha, 256 - (L & 0xFF)));
634 left += 1;
635 }
636
637 int rite = R >> 8;
638 int width = rite - left;
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000639 if (width > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000640 call_hline_blitter(blitter, left, top, width, alpha);
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000641 }
642 if (R & 0xFF) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000643 blitter->blitV(rite, top, 1, SkAlphaMul(alpha, R & 0xFF));
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000644 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000645}
646
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000647static void antifilldot8(FDot8 L, FDot8 T, FDot8 R, FDot8 B, SkBlitter* blitter,
648 bool fillInner) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000649 // check for empty now that we're in our reduced precision space
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000650 if (L >= R || T >= B) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000651 return;
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000652 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000653 int top = T >> 8;
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000654 if (top == ((B - 1) >> 8)) { // just one scanline high
reed@android.com8a1c16f2008-12-17 15:59:43 +0000655 do_scanline(L, top, R, B - T - 1, blitter);
656 return;
657 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000658
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000659 if (T & 0xFF) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000660 do_scanline(L, top, R, 256 - (T & 0xFF), blitter);
661 top += 1;
662 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000663
reed@android.com8a1c16f2008-12-17 15:59:43 +0000664 int bot = B >> 8;
665 int height = bot - top;
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000666 if (height > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000667 int left = L >> 8;
reed@google.comf7398c32011-05-05 14:24:47 +0000668 if (left == ((R - 1) >> 8)) { // just 1-pixel wide
669 blitter->blitV(left, top, height, R - L - 1);
670 } else {
671 if (L & 0xFF) {
672 blitter->blitV(left, top, height, 256 - (L & 0xFF));
673 left += 1;
674 }
675 int rite = R >> 8;
676 int width = rite - left;
677 if (width > 0 && fillInner) {
678 blitter->blitRect(left, top, width, height);
679 }
680 if (R & 0xFF) {
681 blitter->blitV(rite, top, height, R & 0xFF);
682 }
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000683 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000684 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000685
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000686 if (B & 0xFF) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000687 do_scanline(L, bot, R, B & 0xFF, blitter);
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000688 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000689}
690
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000691static void antifillrect(const SkXRect& xr, SkBlitter* blitter) {
692 antifilldot8(SkFixedToFDot8(xr.fLeft), SkFixedToFDot8(xr.fTop),
693 SkFixedToFDot8(xr.fRight), SkFixedToFDot8(xr.fBottom),
694 blitter, true);
695}
696
reed@android.com8a1c16f2008-12-17 15:59:43 +0000697///////////////////////////////////////////////////////////////////////////////
698
reed@google.com67ba5fa2011-10-24 12:56:20 +0000699void SkScan::AntiFillXRect(const SkXRect& xr, const SkRegion* clip,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000700 SkBlitter* blitter) {
halcanary96fcdcc2015-08-27 07:41:13 -0700701 if (nullptr == clip) {
reed@google.com67ba5fa2011-10-24 12:56:20 +0000702 antifillrect(xr, blitter);
703 } else {
704 SkIRect outerBounds;
705 XRect_roundOut(xr, &outerBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000706
reed@google.com67ba5fa2011-10-24 12:56:20 +0000707 if (clip->isRect()) {
708 const SkIRect& clipBounds = clip->getBounds();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000709
reed@google.com67ba5fa2011-10-24 12:56:20 +0000710 if (clipBounds.contains(outerBounds)) {
711 antifillrect(xr, blitter);
712 } else {
713 SkXRect tmpR;
714 // this keeps our original edges fractional
715 XRect_set(&tmpR, clipBounds);
716 if (tmpR.intersect(xr)) {
717 antifillrect(tmpR, blitter);
718 }
719 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000720 } else {
reed@google.com67ba5fa2011-10-24 12:56:20 +0000721 SkRegion::Cliperator clipper(*clip, outerBounds);
722 const SkIRect& rr = clipper.rect();
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000723
reed@google.com67ba5fa2011-10-24 12:56:20 +0000724 while (!clipper.done()) {
725 SkXRect tmpR;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000726
reed@google.com67ba5fa2011-10-24 12:56:20 +0000727 // this keeps our original edges fractional
728 XRect_set(&tmpR, rr);
729 if (tmpR.intersect(xr)) {
730 antifillrect(tmpR, blitter);
731 }
732 clipper.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000733 }
734 }
reed@google.com67ba5fa2011-10-24 12:56:20 +0000735 }
736}
737
738void SkScan::AntiFillXRect(const SkXRect& xr, const SkRasterClip& clip,
739 SkBlitter* blitter) {
740 if (clip.isBW()) {
741 AntiFillXRect(xr, &clip.bwRgn(), blitter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000742 } else {
reed@google.com67ba5fa2011-10-24 12:56:20 +0000743 SkIRect outerBounds;
744 XRect_roundOut(xr, &outerBounds);
745
746 if (clip.quickContains(outerBounds)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700747 AntiFillXRect(xr, nullptr, blitter);
reed@google.com67ba5fa2011-10-24 12:56:20 +0000748 } else {
749 SkAAClipBlitterWrapper wrapper(clip, blitter);
reed@google.com67ba5fa2011-10-24 12:56:20 +0000750 AntiFillXRect(xr, &wrapper.getRgn(), wrapper.getBlitter());
reed@google.com045e62d2011-10-24 12:19:46 +0000751 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000752 }
753}
754
reed@android.com8a1c16f2008-12-17 15:59:43 +0000755/* This guy takes a float-rect, but with the key improvement that it has
756 already been clipped, so we know that it is safe to convert it into a
757 XRect (fixedpoint), as it won't overflow.
758*/
759static void antifillrect(const SkRect& r, SkBlitter* blitter) {
760 SkXRect xr;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000761
reed@android.com8a1c16f2008-12-17 15:59:43 +0000762 XRect_set(&xr, r);
763 antifillrect(xr, blitter);
764}
765
766/* We repeat the clipping logic of AntiFillXRect because the float rect might
767 overflow if we blindly converted it to an XRect. This sucks that we have to
768 repeat the clipping logic, but I don't see how to share the code/logic.
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000769
reed@android.com8a1c16f2008-12-17 15:59:43 +0000770 We clip r (as needed) into one or more (smaller) float rects, and then pass
771 those to our version of antifillrect, which converts it into an XRect and
772 then calls the blit.
773*/
reed@android.come28ff552009-11-19 20:46:39 +0000774void SkScan::AntiFillRect(const SkRect& origR, const SkRegion* clip,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000775 SkBlitter* blitter) {
776 if (clip) {
reed@android.come28ff552009-11-19 20:46:39 +0000777 SkRect newR;
778 newR.set(clip->getBounds());
779 if (!newR.intersect(origR)) {
780 return;
781 }
782
reedb07a94f2014-11-19 05:03:18 -0800783 const SkIRect outerBounds = newR.roundOut();
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000784
reed@android.com8a1c16f2008-12-17 15:59:43 +0000785 if (clip->isRect()) {
reed@android.come28ff552009-11-19 20:46:39 +0000786 antifillrect(newR, blitter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000787 } else {
788 SkRegion::Cliperator clipper(*clip, outerBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000789 while (!clipper.done()) {
reed@android.come28ff552009-11-19 20:46:39 +0000790 newR.set(clipper.rect());
791 if (newR.intersect(origR)) {
792 antifillrect(newR, blitter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000793 }
794 clipper.next();
795 }
796 }
797 } else {
reed@android.come28ff552009-11-19 20:46:39 +0000798 antifillrect(origR, blitter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000799 }
800}
801
reed@google.com045e62d2011-10-24 12:19:46 +0000802void SkScan::AntiFillRect(const SkRect& r, const SkRasterClip& clip,
803 SkBlitter* blitter) {
804 if (clip.isBW()) {
805 AntiFillRect(r, &clip.bwRgn(), blitter);
806 } else {
807 SkAAClipBlitterWrapper wrap(clip, blitter);
808 AntiFillRect(r, &wrap.getRgn(), wrap.getBlitter());
809 }
810}
811
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000812///////////////////////////////////////////////////////////////////////////////
813
reed@google.com3a968752011-04-07 13:31:48 +0000814#define SkAlphaMulRound(a, b) SkMulDiv255Round(a, b)
815
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000816// calls blitRect() if the rectangle is non-empty
817static void fillcheckrect(int L, int T, int R, int B, SkBlitter* blitter) {
818 if (L < R && T < B) {
819 blitter->blitRect(L, T, R - L, B - T);
820 }
821}
822
823static inline FDot8 SkScalarToFDot8(SkScalar x) {
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000824 return (int)(x * 256);
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000825}
826
827static inline int FDot8Floor(FDot8 x) {
828 return x >> 8;
829}
830
831static inline int FDot8Ceil(FDot8 x) {
832 return (x + 0xFF) >> 8;
833}
834
835// 1 - (1 - a)*(1 - b)
836static inline U8CPU InvAlphaMul(U8CPU a, U8CPU b) {
reed@google.com3a968752011-04-07 13:31:48 +0000837 // need precise rounding (not just SkAlphaMul) so that values like
838 // a=228, b=252 don't overflow the result
839 return SkToU8(a + b - SkAlphaMulRound(a, b));
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000840}
841
842static void inner_scanline(FDot8 L, int top, FDot8 R, U8CPU alpha,
843 SkBlitter* blitter) {
844 SkASSERT(L < R);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000845
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000846 if ((L >> 8) == ((R - 1) >> 8)) { // 1x1 pixel
aleksandar.stojiljkovic76001832015-11-02 13:28:51 -0800847 FDot8 widClamp = R - L;
848 // border case clamp 256 to 255 instead of going through call_hline_blitter
849 // see skbug/4406
850 widClamp = widClamp - (widClamp >> 8);
851 blitter->blitV(L >> 8, top, 1, InvAlphaMul(alpha, widClamp));
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000852 return;
853 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000854
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000855 int left = L >> 8;
856 if (L & 0xFF) {
857 blitter->blitV(left, top, 1, InvAlphaMul(alpha, L & 0xFF));
858 left += 1;
859 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000860
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000861 int rite = R >> 8;
862 int width = rite - left;
863 if (width > 0) {
864 call_hline_blitter(blitter, left, top, width, alpha);
865 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000866
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000867 if (R & 0xFF) {
868 blitter->blitV(rite, top, 1, InvAlphaMul(alpha, ~R & 0xFF));
869 }
870}
871
872static void innerstrokedot8(FDot8 L, FDot8 T, FDot8 R, FDot8 B,
873 SkBlitter* blitter) {
874 SkASSERT(L < R && T < B);
875
876 int top = T >> 8;
877 if (top == ((B - 1) >> 8)) { // just one scanline high
reed@google.com10d02b62012-04-16 17:12:38 +0000878 // We want the inverse of B-T, since we're the inner-stroke
879 int alpha = 256 - (B - T);
880 if (alpha) {
881 inner_scanline(L, top, R, alpha, blitter);
882 }
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000883 return;
884 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000885
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000886 if (T & 0xFF) {
887 inner_scanline(L, top, R, T & 0xFF, blitter);
888 top += 1;
889 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000890
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000891 int bot = B >> 8;
892 int height = bot - top;
893 if (height > 0) {
894 if (L & 0xFF) {
895 blitter->blitV(L >> 8, top, height, L & 0xFF);
896 }
897 if (R & 0xFF) {
898 blitter->blitV(R >> 8, top, height, ~R & 0xFF);
899 }
900 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000901
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000902 if (B & 0xFF) {
903 inner_scanline(L, bot, R, ~B & 0xFF, blitter);
904 }
905}
906
fmalitab2806462015-01-05 11:31:02 -0800907static inline void align_thin_stroke(FDot8& edge1, FDot8& edge2) {
908 SkASSERT(edge1 <= edge2);
909
910 if (FDot8Floor(edge1) == FDot8Floor(edge2)) {
911 edge2 -= (edge1 & 0xFF);
912 edge1 &= ~0xFF;
913 }
914}
915
reed@google.com761fb622011-04-04 18:58:05 +0000916void SkScan::AntiFrameRect(const SkRect& r, const SkPoint& strokeSize,
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000917 const SkRegion* clip, SkBlitter* blitter) {
reed@google.com761fb622011-04-04 18:58:05 +0000918 SkASSERT(strokeSize.fX >= 0 && strokeSize.fY >= 0);
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000919
reed@google.com761fb622011-04-04 18:58:05 +0000920 SkScalar rx = SkScalarHalf(strokeSize.fX);
921 SkScalar ry = SkScalarHalf(strokeSize.fY);
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000922
923 // outset by the radius
fmalitab2806462015-01-05 11:31:02 -0800924 FDot8 outerL = SkScalarToFDot8(r.fLeft - rx);
925 FDot8 outerT = SkScalarToFDot8(r.fTop - ry);
926 FDot8 outerR = SkScalarToFDot8(r.fRight + rx);
927 FDot8 outerB = SkScalarToFDot8(r.fBottom + ry);
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000928
929 SkIRect outer;
930 // set outer to the outer rect of the outer section
fmalitab2806462015-01-05 11:31:02 -0800931 outer.set(FDot8Floor(outerL), FDot8Floor(outerT), FDot8Ceil(outerR), FDot8Ceil(outerB));
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000932
933 SkBlitterClipper clipper;
934 if (clip) {
935 if (clip->quickReject(outer)) {
936 return;
937 }
938 if (!clip->contains(outer)) {
939 blitter = clipper.apply(blitter, clip, &outer);
940 }
941 // now we can ignore clip for the rest of the function
942 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000943
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000944 // in case we lost a bit with diameter/2
reed@google.com761fb622011-04-04 18:58:05 +0000945 rx = strokeSize.fX - rx;
946 ry = strokeSize.fY - ry;
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000947
fmalitab2806462015-01-05 11:31:02 -0800948 // inset by the radius
949 FDot8 innerL = SkScalarToFDot8(r.fLeft + rx);
950 FDot8 innerT = SkScalarToFDot8(r.fTop + ry);
951 FDot8 innerR = SkScalarToFDot8(r.fRight - rx);
952 FDot8 innerB = SkScalarToFDot8(r.fBottom - ry);
953
954 // For sub-unit strokes, tweak the hulls such that one of the edges coincides with the pixel
955 // edge. This ensures that the general rect stroking logic below
956 // a) doesn't blit the same scanline twice
957 // b) computes the correct coverage when both edges fall within the same pixel
958 if (strokeSize.fX < 1 || strokeSize.fY < 1) {
959 align_thin_stroke(outerL, innerL);
960 align_thin_stroke(outerT, innerT);
961 align_thin_stroke(innerR, outerR);
962 align_thin_stroke(innerB, outerB);
963 }
964
965 // stroke the outer hull
966 antifilldot8(outerL, outerT, outerR, outerB, blitter, false);
967
968 // set outer to the outer rect of the middle section
969 outer.set(FDot8Ceil(outerL), FDot8Ceil(outerT), FDot8Floor(outerR), FDot8Floor(outerB));
970
971 if (innerL >= innerR || innerT >= innerB) {
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000972 fillcheckrect(outer.fLeft, outer.fTop, outer.fRight, outer.fBottom,
973 blitter);
974 } else {
975 SkIRect inner;
976 // set inner to the inner rect of the middle section
fmalitab2806462015-01-05 11:31:02 -0800977 inner.set(FDot8Floor(innerL), FDot8Floor(innerT), FDot8Ceil(innerR), FDot8Ceil(innerB));
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000978
979 // draw the frame in 4 pieces
980 fillcheckrect(outer.fLeft, outer.fTop, outer.fRight, inner.fTop,
981 blitter);
982 fillcheckrect(outer.fLeft, inner.fTop, inner.fLeft, inner.fBottom,
983 blitter);
984 fillcheckrect(inner.fRight, inner.fTop, outer.fRight, inner.fBottom,
985 blitter);
986 fillcheckrect(outer.fLeft, inner.fBottom, outer.fRight, outer.fBottom,
987 blitter);
988
989 // now stroke the inner rect, which is similar to antifilldot8() except that
990 // it treats the fractional coordinates with the inverse bias (since its
991 // inner).
fmalitab2806462015-01-05 11:31:02 -0800992 innerstrokedot8(innerL, innerT, innerR, innerB, blitter);
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000993 }
994}
reed@google.com045e62d2011-10-24 12:19:46 +0000995
996void SkScan::AntiFrameRect(const SkRect& r, const SkPoint& strokeSize,
997 const SkRasterClip& clip, SkBlitter* blitter) {
998 if (clip.isBW()) {
999 AntiFrameRect(r, strokeSize, &clip.bwRgn(), blitter);
1000 } else {
1001 SkAAClipBlitterWrapper wrap(clip, blitter);
1002 AntiFrameRect(r, strokeSize, &wrap.getRgn(), wrap.getBlitter());
1003 }
1004}