blob: 175f085f912ccce1455ebdb327f3caf3441c0344 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
2/*
3 * Copyright 2011 The Android Open Source Project
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
reed@android.com8a1c16f2008-12-17 15:59:43 +00009
10#include "SkScan.h"
11#include "SkBlitter.h"
12#include "SkColorPriv.h"
reed@android.come28ff552009-11-19 20:46:39 +000013#include "SkLineClipper.h"
reed@google.com045e62d2011-10-24 12:19:46 +000014#include "SkRasterClip.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000015#include "SkFDot6.h"
16
reed@android.com88983632009-03-23 16:05:19 +000017/* Our attempt to compute the worst case "bounds" for the horizontal and
18 vertical cases has some numerical bug in it, and we sometimes undervalue
19 our extends. The bug is that when this happens, we will set the clip to
20 NULL (for speed), and thus draw outside of the clip by a pixel, which might
21 only look bad, but it might also access memory outside of the valid range
22 allcoated for the device bitmap.
reed@android.com6c14b432009-03-23 20:11:11 +000023
reed@android.com88983632009-03-23 16:05:19 +000024 This define enables our fix to outset our "bounds" by 1, thus avoiding the
25 chance of the bug, but at the cost of sometimes taking the rectblitter
26 case (i.e. not setting the clip to NULL) when we might not actually need
27 to. If we can improve/fix the actual calculations, then we can remove this
28 step.
29 */
30#define OUTSET_BEFORE_CLIP_TEST true
31
reed@android.com8a1c16f2008-12-17 15:59:43 +000032#define HLINE_STACK_BUFFER 100
33
34static inline int SmallDot6Scale(int value, int dot6) {
35 SkASSERT((int16_t)value == value);
36 SkASSERT((unsigned)dot6 <= 64);
37 return SkMulS16(value, dot6) >> 6;
38}
39
40//#define TEST_GAMMA
41
42#ifdef TEST_GAMMA
43 static uint8_t gGammaTable[256];
44 #define ApplyGamma(table, alpha) (table)[alpha]
45
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +000046 static void build_gamma_table() {
reed@android.com8a1c16f2008-12-17 15:59:43 +000047 static bool gInit = false;
48
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +000049 if (gInit == false) {
50 for (int i = 0; i < 256; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000051 SkFixed n = i * 257;
52 n += n >> 15;
53 SkASSERT(n >= 0 && n <= SK_Fixed1);
54 n = SkFixedSqrt(n);
55 n = n * 255 >> 16;
56 // SkDebugf("morph %d -> %d\n", i, n);
57 gGammaTable[i] = SkToU8(n);
58 }
59 gInit = true;
60 }
61 }
62#else
63 #define ApplyGamma(table, alpha) SkToU8(alpha)
64#endif
65
66///////////////////////////////////////////////////////////////////////////////
67
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +000068static void call_hline_blitter(SkBlitter* blitter, int x, int y, int count,
69 U8CPU alpha) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000070 SkASSERT(count > 0);
skia.committer@gmail.com27b40e92012-10-26 02:01:24 +000071
reed@android.com8a1c16f2008-12-17 15:59:43 +000072 int16_t runs[HLINE_STACK_BUFFER + 1];
73 uint8_t aa[HLINE_STACK_BUFFER];
skia.committer@gmail.com27b40e92012-10-26 02:01:24 +000074
reed@android.com8a1c16f2008-12-17 15:59:43 +000075 aa[0] = ApplyGamma(gGammaTable, alpha);
76 do {
77 int n = count;
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +000078 if (n > HLINE_STACK_BUFFER) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000079 n = HLINE_STACK_BUFFER;
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +000080 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000081 runs[0] = SkToS16(n);
82 runs[n] = 0;
83 blitter->blitAntiH(x, y, aa, runs);
84 x += n;
85 count -= n;
86 } while (count > 0);
87}
88
reed@google.comb03fe422012-10-25 17:37:03 +000089class SkAntiHairBlitter {
90public:
91 SkAntiHairBlitter() : fBlitter(NULL) {}
djsollen@google.comade109f2013-01-04 15:29:06 +000092 virtual ~SkAntiHairBlitter() {}
reed@android.com8a1c16f2008-12-17 15:59:43 +000093
reed@google.comb03fe422012-10-25 17:37:03 +000094 SkBlitter* getBlitter() const { return fBlitter; }
reed@android.com8a1c16f2008-12-17 15:59:43 +000095
reed@google.comb03fe422012-10-25 17:37:03 +000096 void setup(SkBlitter* blitter) {
97 fBlitter = blitter;
reed@android.com8a1c16f2008-12-17 15:59:43 +000098 }
99
reed@google.comb03fe422012-10-25 17:37:03 +0000100 virtual SkFixed drawCap(int x, SkFixed fy, SkFixed slope, int mod64) = 0;
101 virtual SkFixed drawLine(int x, int stopx, SkFixed fy, SkFixed slope) = 0;
102
103private:
104 SkBlitter* fBlitter;
105};
106
107class HLine_SkAntiHairBlitter : public SkAntiHairBlitter {
108public:
109 virtual SkFixed drawCap(int x, SkFixed fy, SkFixed slope, int mod64) SK_OVERRIDE {
110 fy += SK_Fixed1/2;
skia.committer@gmail.com27b40e92012-10-26 02:01:24 +0000111
reed@google.comb03fe422012-10-25 17:37:03 +0000112 int y = fy >> 16;
113 uint8_t a = (uint8_t)(fy >> 8);
skia.committer@gmail.com27b40e92012-10-26 02:01:24 +0000114
reed@google.comb03fe422012-10-25 17:37:03 +0000115 // lower line
116 unsigned ma = SmallDot6Scale(a, mod64);
117 if (ma) {
118 call_hline_blitter(this->getBlitter(), x, y, 1, ma);
119 }
skia.committer@gmail.com27b40e92012-10-26 02:01:24 +0000120
reed@google.comb03fe422012-10-25 17:37:03 +0000121 // upper line
122 ma = SmallDot6Scale(255 - a, mod64);
123 if (ma) {
124 call_hline_blitter(this->getBlitter(), x, y - 1, 1, ma);
125 }
skia.committer@gmail.com27b40e92012-10-26 02:01:24 +0000126
reed@google.comb03fe422012-10-25 17:37:03 +0000127 return fy - SK_Fixed1/2;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000128 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000129
reed@google.comb03fe422012-10-25 17:37:03 +0000130 virtual SkFixed drawLine(int x, int stopx, SkFixed fy,
131 SkFixed slope) SK_OVERRIDE {
132 SkASSERT(x < stopx);
133 int count = stopx - x;
134 fy += SK_Fixed1/2;
skia.committer@gmail.com27b40e92012-10-26 02:01:24 +0000135
reed@google.comb03fe422012-10-25 17:37:03 +0000136 int y = fy >> 16;
137 uint8_t a = (uint8_t)(fy >> 8);
skia.committer@gmail.com27b40e92012-10-26 02:01:24 +0000138
reed@google.comb03fe422012-10-25 17:37:03 +0000139 // lower line
140 if (a) {
141 call_hline_blitter(this->getBlitter(), x, y, count, a);
142 }
skia.committer@gmail.com27b40e92012-10-26 02:01:24 +0000143
reed@google.comb03fe422012-10-25 17:37:03 +0000144 // upper line
145 a = 255 - a;
146 if (a) {
147 call_hline_blitter(this->getBlitter(), x, y - 1, count, a);
148 }
skia.committer@gmail.com27b40e92012-10-26 02:01:24 +0000149
reed@google.comb03fe422012-10-25 17:37:03 +0000150 return fy - SK_Fixed1/2;
151 }
152};
reed@android.com8a1c16f2008-12-17 15:59:43 +0000153
reed@google.comb03fe422012-10-25 17:37:03 +0000154class Horish_SkAntiHairBlitter : public SkAntiHairBlitter {
155public:
156 virtual SkFixed drawCap(int x, SkFixed fy, SkFixed dy, int mod64) SK_OVERRIDE {
157 int16_t runs[2];
158 uint8_t aa[1];
skia.committer@gmail.com27b40e92012-10-26 02:01:24 +0000159
reed@google.comb03fe422012-10-25 17:37:03 +0000160 runs[0] = 1;
161 runs[1] = 0;
skia.committer@gmail.com27b40e92012-10-26 02:01:24 +0000162
reed@google.comb03fe422012-10-25 17:37:03 +0000163 fy += SK_Fixed1/2;
164 SkBlitter* blitter = this->getBlitter();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000165
reed@android.com8a1c16f2008-12-17 15:59:43 +0000166 int lower_y = fy >> 16;
167 uint8_t a = (uint8_t)(fy >> 8);
168 unsigned ma = SmallDot6Scale(a, mod64);
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000169 if (ma) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000170 aa[0] = ApplyGamma(gamma, ma);
171 blitter->blitAntiH(x, lower_y, aa, runs);
172 // the clipping blitters might edit runs, but should not affect us
173 SkASSERT(runs[0] == 1);
174 SkASSERT(runs[1] == 0);
175 }
176 ma = SmallDot6Scale(255 - a, mod64);
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000177 if (ma) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000178 aa[0] = ApplyGamma(gamma, ma);
179 blitter->blitAntiH(x, lower_y - 1, aa, runs);
180 // the clipping blitters might edit runs, but should not affect us
181 SkASSERT(runs[0] == 1);
182 SkASSERT(runs[1] == 0);
183 }
184 fy += dy;
skia.committer@gmail.com27b40e92012-10-26 02:01:24 +0000185
reed@google.comb03fe422012-10-25 17:37:03 +0000186 return fy - SK_Fixed1/2;
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000187 }
skia.committer@gmail.com27b40e92012-10-26 02:01:24 +0000188
reed@google.comb03fe422012-10-25 17:37:03 +0000189 virtual SkFixed drawLine(int x, int stopx, SkFixed fy, SkFixed dy) SK_OVERRIDE {
190 SkASSERT(x < stopx);
skia.committer@gmail.com27b40e92012-10-26 02:01:24 +0000191
reed@google.comb03fe422012-10-25 17:37:03 +0000192 int16_t runs[2];
193 uint8_t aa[1];
skia.committer@gmail.com27b40e92012-10-26 02:01:24 +0000194
reed@google.comb03fe422012-10-25 17:37:03 +0000195 runs[0] = 1;
196 runs[1] = 0;
skia.committer@gmail.com27b40e92012-10-26 02:01:24 +0000197
reed@google.comb03fe422012-10-25 17:37:03 +0000198 fy += SK_Fixed1/2;
199 SkBlitter* blitter = this->getBlitter();
200 do {
201 int lower_y = fy >> 16;
202 uint8_t a = (uint8_t)(fy >> 8);
203 if (a) {
204 aa[0] = a;
205 blitter->blitAntiH(x, lower_y, aa, runs);
206 // the clipping blitters might edit runs, but should not affect us
207 SkASSERT(runs[0] == 1);
208 SkASSERT(runs[1] == 0);
209 }
210 a = 255 - a;
211 if (a) {
212 aa[0] = a;
213 blitter->blitAntiH(x, lower_y - 1, aa, runs);
214 // the clipping blitters might edit runs, but should not affect us
215 SkASSERT(runs[0] == 1);
216 SkASSERT(runs[1] == 0);
217 }
218 fy += dy;
219 } while (++x < stopx);
skia.committer@gmail.com27b40e92012-10-26 02:01:24 +0000220
reed@google.comb03fe422012-10-25 17:37:03 +0000221 return fy - SK_Fixed1/2;
222 }
223};
224
225class VLine_SkAntiHairBlitter : public SkAntiHairBlitter {
226public:
227 virtual SkFixed drawCap(int y, SkFixed fx, SkFixed dx, int mod64) SK_OVERRIDE {
228 SkASSERT(0 == dx);
229 fx += SK_Fixed1/2;
skia.committer@gmail.com27b40e92012-10-26 02:01:24 +0000230
reed@google.comb03fe422012-10-25 17:37:03 +0000231 int x = fx >> 16;
232 int a = (uint8_t)(fx >> 8);
skia.committer@gmail.com27b40e92012-10-26 02:01:24 +0000233
reed@google.comb03fe422012-10-25 17:37:03 +0000234 unsigned ma = SmallDot6Scale(a, mod64);
235 if (ma) {
236 this->getBlitter()->blitV(x, y, 1, ma);
237 }
238 ma = SmallDot6Scale(255 - a, mod64);
239 if (ma) {
240 this->getBlitter()->blitV(x - 1, y, 1, ma);
241 }
skia.committer@gmail.com27b40e92012-10-26 02:01:24 +0000242
reed@google.comb03fe422012-10-25 17:37:03 +0000243 return fx - SK_Fixed1/2;
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000244 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000245
reed@google.comb03fe422012-10-25 17:37:03 +0000246 virtual SkFixed drawLine(int y, int stopy, SkFixed fx, SkFixed dx) SK_OVERRIDE {
247 SkASSERT(y < stopy);
248 SkASSERT(0 == dx);
249 fx += SK_Fixed1/2;
skia.committer@gmail.com27b40e92012-10-26 02:01:24 +0000250
reed@google.comb03fe422012-10-25 17:37:03 +0000251 int x = fx >> 16;
252 int a = (uint8_t)(fx >> 8);
skia.committer@gmail.com27b40e92012-10-26 02:01:24 +0000253
reed@google.comb03fe422012-10-25 17:37:03 +0000254 if (a) {
255 this->getBlitter()->blitV(x, y, stopy - y, a);
256 }
257 a = 255 - a;
258 if (a) {
259 this->getBlitter()->blitV(x - 1, y, stopy - y, a);
260 }
skia.committer@gmail.com27b40e92012-10-26 02:01:24 +0000261
reed@google.comb03fe422012-10-25 17:37:03 +0000262 return fx - SK_Fixed1/2;
263 }
264};
reed@android.com8a1c16f2008-12-17 15:59:43 +0000265
reed@google.comb03fe422012-10-25 17:37:03 +0000266class Vertish_SkAntiHairBlitter : public SkAntiHairBlitter {
267public:
268 virtual SkFixed drawCap(int y, SkFixed fx, SkFixed dx, int mod64) SK_OVERRIDE {
269 int16_t runs[3];
270 uint8_t aa[2];
skia.committer@gmail.com27b40e92012-10-26 02:01:24 +0000271
reed@google.comb03fe422012-10-25 17:37:03 +0000272 runs[0] = 1;
273 runs[2] = 0;
skia.committer@gmail.com27b40e92012-10-26 02:01:24 +0000274
reed@google.comb03fe422012-10-25 17:37:03 +0000275 fx += SK_Fixed1/2;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000276 int x = fx >> 16;
277 uint8_t a = (uint8_t)(fx >> 8);
skia.committer@gmail.com27b40e92012-10-26 02:01:24 +0000278
reed@google.comb03fe422012-10-25 17:37:03 +0000279 aa[0] = SmallDot6Scale(255 - a, mod64);
280 aa[1] = SmallDot6Scale(a, mod64);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000281 // the clippng blitters might overwrite this guy, so we have to reset it each time
282 runs[1] = 1;
reed@google.comb03fe422012-10-25 17:37:03 +0000283 this->getBlitter()->blitAntiH(x - 1, y, aa, runs);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000284 // the clipping blitters might edit runs, but should not affect us
285 SkASSERT(runs[0] == 1);
286 SkASSERT(runs[2] == 0);
287 fx += dx;
skia.committer@gmail.com27b40e92012-10-26 02:01:24 +0000288
reed@google.comb03fe422012-10-25 17:37:03 +0000289 return fx - SK_Fixed1/2;
290 }
skia.committer@gmail.com27b40e92012-10-26 02:01:24 +0000291
reed@google.comb03fe422012-10-25 17:37:03 +0000292 virtual SkFixed drawLine(int y, int stopy, SkFixed fx, SkFixed dx) SK_OVERRIDE {
293 SkASSERT(y < stopy);
294 int16_t runs[3];
295 uint8_t aa[2];
skia.committer@gmail.com27b40e92012-10-26 02:01:24 +0000296
reed@google.comb03fe422012-10-25 17:37:03 +0000297 runs[0] = 1;
298 runs[2] = 0;
skia.committer@gmail.com27b40e92012-10-26 02:01:24 +0000299
reed@google.comb03fe422012-10-25 17:37:03 +0000300 fx += SK_Fixed1/2;
301 do {
302 int x = fx >> 16;
303 uint8_t a = (uint8_t)(fx >> 8);
skia.committer@gmail.com27b40e92012-10-26 02:01:24 +0000304
reed@google.comb03fe422012-10-25 17:37:03 +0000305 aa[0] = 255 - a;
306 aa[1] = a;
307 // the clippng blitters might overwrite this guy, so we have to reset it each time
308 runs[1] = 1;
309 this->getBlitter()->blitAntiH(x - 1, y, aa, runs);
310 // the clipping blitters might edit runs, but should not affect us
311 SkASSERT(runs[0] == 1);
312 SkASSERT(runs[2] == 0);
313 fx += dx;
314 } while (++y < stopy);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000315
reed@google.comb03fe422012-10-25 17:37:03 +0000316 return fx - SK_Fixed1/2;
317 }
318};
reed@android.com8a1c16f2008-12-17 15:59:43 +0000319
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000320static inline SkFixed fastfixdiv(SkFDot6 a, SkFDot6 b) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000321 SkASSERT((a << 16 >> 16) == a);
322 SkASSERT(b != 0);
323 return (a << 16) / b;
324}
325
reed@google.com8a812c22012-03-30 16:24:00 +0000326#define SkBITCOUNT(x) (sizeof(x) << 3)
327
328#if 1
329// returns high-bit set iff x==0x8000...
330static inline int bad_int(int x) {
331 return x & -x;
332}
333
334static int any_bad_ints(int a, int b, int c, int d) {
335 return (bad_int(a) | bad_int(b) | bad_int(c) | bad_int(d)) >> (SkBITCOUNT(int) - 1);
336}
337#else
338static inline int good_int(int x) {
339 return x ^ (1 << (SkBITCOUNT(x) - 1));
340}
341
342static int any_bad_ints(int a, int b, int c, int d) {
343 return !(good_int(a) & good_int(b) & good_int(c) & good_int(d));
344}
345#endif
346
humper@google.com0e515772013-01-07 19:54:40 +0000347#ifdef SK_DEBUG
reed@google.com99b300e2012-04-17 20:43:47 +0000348static bool canConvertFDot6ToFixed(SkFDot6 x) {
349 const int maxDot6 = SK_MaxS32 >> (16 - 6);
350 return SkAbs32(x) <= maxDot6;
351}
humper@google.com0e515772013-01-07 19:54:40 +0000352#endif
reed@google.com99b300e2012-04-17 20:43:47 +0000353
reed@google.comc1f6db82012-10-31 17:57:01 +0000354/*
355 * We want the fractional part of ordinate, but we want multiples of 64 to
356 * return 64, not 0, so we can't just say (ordinate & 63).
357 * We basically want to compute those bits, and if they're 0, return 64.
358 * We can do that w/o a branch with an extra sub and add.
359 */
360static int contribution_64(SkFDot6 ordinate) {
361#if 0
362 int result = ordinate & 63;
363 if (0 == result) {
364 result = 64;
365 }
366#else
367 int result = ((ordinate - 1) & 63) + 1;
368#endif
369 SkASSERT(result > 0 && result <= 64);
370 return result;
371}
372
reed@android.com8a1c16f2008-12-17 15:59:43 +0000373static void do_anti_hairline(SkFDot6 x0, SkFDot6 y0, SkFDot6 x1, SkFDot6 y1,
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000374 const SkIRect* clip, SkBlitter* blitter) {
reed@google.com8a812c22012-03-30 16:24:00 +0000375 // check for integer NaN (0x80000000) which we can't handle (can't negate it)
376 // It appears typically from a huge float (inf or nan) being converted to int.
377 // If we see it, just don't draw.
378 if (any_bad_ints(x0, y0, x1, y1)) {
379 return;
380 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000381
reed@google.com99b300e2012-04-17 20:43:47 +0000382 // The caller must clip the line to [-32767.0 ... 32767.0] ahead of time
383 // (in dot6 format)
384 SkASSERT(canConvertFDot6ToFixed(x0));
385 SkASSERT(canConvertFDot6ToFixed(y0));
386 SkASSERT(canConvertFDot6ToFixed(x1));
387 SkASSERT(canConvertFDot6ToFixed(y1));
388
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000389 if (SkAbs32(x1 - x0) > SkIntToFDot6(511) || SkAbs32(y1 - y0) > SkIntToFDot6(511)) {
reed@android.com35555912009-03-16 18:46:55 +0000390 /* instead of (x0 + x1) >> 1, we shift each separately. This is less
391 precise, but avoids overflowing the intermediate result if the
392 values are huge. A better fix might be to clip the original pts
393 directly (i.e. do the divide), so we don't spend time subdividing
394 huge lines at all.
395 */
396 int hx = (x0 >> 1) + (x1 >> 1);
397 int hy = (y0 >> 1) + (y1 >> 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000398 do_anti_hairline(x0, y0, hx, hy, clip, blitter);
399 do_anti_hairline(hx, hy, x1, y1, clip, blitter);
400 return;
401 }
402
403 int scaleStart, scaleStop;
404 int istart, istop;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000405 SkFixed fstart, slope;
reed@google.comb03fe422012-10-25 17:37:03 +0000406
407 HLine_SkAntiHairBlitter hline_blitter;
408 Horish_SkAntiHairBlitter horish_blitter;
409 VLine_SkAntiHairBlitter vline_blitter;
410 Vertish_SkAntiHairBlitter vertish_blitter;
411 SkAntiHairBlitter* hairBlitter = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000412
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000413 if (SkAbs32(x1 - x0) > SkAbs32(y1 - y0)) { // mostly horizontal
reed@android.com8a1c16f2008-12-17 15:59:43 +0000414 if (x0 > x1) { // we want to go left-to-right
415 SkTSwap<SkFDot6>(x0, x1);
416 SkTSwap<SkFDot6>(y0, y1);
417 }
418
419 istart = SkFDot6Floor(x0);
420 istop = SkFDot6Ceil(x1);
421 fstart = SkFDot6ToFixed(y0);
422 if (y0 == y1) { // completely horizontal, take fast case
423 slope = 0;
reed@google.comb03fe422012-10-25 17:37:03 +0000424 hairBlitter = &hline_blitter;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000425 } else {
426 slope = fastfixdiv(y1 - y0, x1 - x0);
427 SkASSERT(slope >= -SK_Fixed1 && slope <= SK_Fixed1);
428 fstart += (slope * (32 - (x0 & 63)) + 32) >> 6;
reed@google.comb03fe422012-10-25 17:37:03 +0000429 hairBlitter = &horish_blitter;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000430 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000431
reed@android.com8a1c16f2008-12-17 15:59:43 +0000432 SkASSERT(istop > istart);
433 if (istop - istart == 1) {
reed@google.comc1f6db82012-10-31 17:57:01 +0000434 // we are within a single pixel
reed@android.com8a1c16f2008-12-17 15:59:43 +0000435 scaleStart = x1 - x0;
436 SkASSERT(scaleStart >= 0 && scaleStart <= 64);
437 scaleStop = 0;
438 } else {
439 scaleStart = 64 - (x0 & 63);
440 scaleStop = x1 & 63;
441 }
442
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000443 if (clip){
444 if (istart >= clip->fRight || istop <= clip->fLeft) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000445 return;
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000446 }
447 if (istart < clip->fLeft) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000448 fstart += slope * (clip->fLeft - istart);
449 istart = clip->fLeft;
450 scaleStart = 64;
reed@google.comc1f6db82012-10-31 17:57:01 +0000451 if (istop - istart == 1) {
452 // we are within a single pixel
453 scaleStart = contribution_64(x1);
454 scaleStop = 0;
455 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000456 }
reed@google.com0724f432012-03-15 14:34:40 +0000457 if (istop > clip->fRight) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000458 istop = clip->fRight;
reed@google.comadb2e242012-03-12 20:26:17 +0000459 scaleStop = 0; // so we don't draw this last column
reed@android.com8a1c16f2008-12-17 15:59:43 +0000460 }
reed@google.combbbe9ed2012-03-30 18:56:08 +0000461
reed@android.com8a1c16f2008-12-17 15:59:43 +0000462 SkASSERT(istart <= istop);
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000463 if (istart == istop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000464 return;
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000465 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000466 // now test if our Y values are completely inside the clip
467 int top, bottom;
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000468 if (slope >= 0) { // T2B
reed@android.com8a1c16f2008-12-17 15:59:43 +0000469 top = SkFixedFloor(fstart - SK_FixedHalf);
470 bottom = SkFixedCeil(fstart + (istop - istart - 1) * slope + SK_FixedHalf);
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000471 } else { // B2T
reed@android.com8a1c16f2008-12-17 15:59:43 +0000472 bottom = SkFixedCeil(fstart + SK_FixedHalf);
473 top = SkFixedFloor(fstart + (istop - istart - 1) * slope - SK_FixedHalf);
474 }
reed@android.com6c14b432009-03-23 20:11:11 +0000475#ifdef OUTSET_BEFORE_CLIP_TEST
476 top -= 1;
477 bottom += 1;
478#endif
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000479 if (top >= clip->fBottom || bottom <= clip->fTop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000480 return;
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000481 }
482 if (clip->fTop <= top && clip->fBottom >= bottom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000483 clip = NULL;
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000484 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000485 }
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000486 } else { // mostly vertical
487 if (y0 > y1) { // we want to go top-to-bottom
reed@android.com8a1c16f2008-12-17 15:59:43 +0000488 SkTSwap<SkFDot6>(x0, x1);
489 SkTSwap<SkFDot6>(y0, y1);
490 }
491
492 istart = SkFDot6Floor(y0);
493 istop = SkFDot6Ceil(y1);
494 fstart = SkFDot6ToFixed(x0);
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000495 if (x0 == x1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000496 if (y0 == y1) { // are we zero length?
497 return; // nothing to do
498 }
499 slope = 0;
reed@google.comb03fe422012-10-25 17:37:03 +0000500 hairBlitter = &vline_blitter;
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000501 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000502 slope = fastfixdiv(x1 - x0, y1 - y0);
503 SkASSERT(slope <= SK_Fixed1 && slope >= -SK_Fixed1);
504 fstart += (slope * (32 - (y0 & 63)) + 32) >> 6;
reed@google.comb03fe422012-10-25 17:37:03 +0000505 hairBlitter = &vertish_blitter;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000506 }
507
508 SkASSERT(istop > istart);
509 if (istop - istart == 1) {
reed@google.comc1f6db82012-10-31 17:57:01 +0000510 // we are within a single pixel
reed@android.com8a1c16f2008-12-17 15:59:43 +0000511 scaleStart = y1 - y0;
512 SkASSERT(scaleStart >= 0 && scaleStart <= 64);
513 scaleStop = 0;
514 } else {
515 scaleStart = 64 - (y0 & 63);
516 scaleStop = y1 & 63;
517 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000518
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000519 if (clip) {
520 if (istart >= clip->fBottom || istop <= clip->fTop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000521 return;
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000522 }
523 if (istart < clip->fTop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000524 fstart += slope * (clip->fTop - istart);
525 istart = clip->fTop;
526 scaleStart = 64;
reed@google.comc1f6db82012-10-31 17:57:01 +0000527 if (istop - istart == 1) {
528 // we are within a single pixel
529 scaleStart = contribution_64(y1);
530 scaleStop = 0;
531 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000532 }
reed@google.com0724f432012-03-15 14:34:40 +0000533 if (istop > clip->fBottom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000534 istop = clip->fBottom;
reed@google.comadb2e242012-03-12 20:26:17 +0000535 scaleStop = 0; // so we don't draw this last row
reed@android.com8a1c16f2008-12-17 15:59:43 +0000536 }
reed@google.combbbe9ed2012-03-30 18:56:08 +0000537
reed@android.com8a1c16f2008-12-17 15:59:43 +0000538 SkASSERT(istart <= istop);
539 if (istart == istop)
540 return;
541
542 // now test if our X values are completely inside the clip
543 int left, right;
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000544 if (slope >= 0) { // L2R
reed@android.com8a1c16f2008-12-17 15:59:43 +0000545 left = SkFixedFloor(fstart - SK_FixedHalf);
546 right = SkFixedCeil(fstart + (istop - istart - 1) * slope + SK_FixedHalf);
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000547 } else { // R2L
reed@android.com8a1c16f2008-12-17 15:59:43 +0000548 right = SkFixedCeil(fstart + SK_FixedHalf);
549 left = SkFixedFloor(fstart + (istop - istart - 1) * slope - SK_FixedHalf);
550 }
reed@android.com6c14b432009-03-23 20:11:11 +0000551#ifdef OUTSET_BEFORE_CLIP_TEST
552 left -= 1;
553 right += 1;
554#endif
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000555 if (left >= clip->fRight || right <= clip->fLeft) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000556 return;
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000557 }
558 if (clip->fLeft <= left && clip->fRight >= right) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000559 clip = NULL;
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000560 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000561 }
562 }
563
564 SkRectClipBlitter rectClipper;
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000565 if (clip) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000566 rectClipper.init(blitter, *clip);
567 blitter = &rectClipper;
568 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000569
reed@google.comb03fe422012-10-25 17:37:03 +0000570 SkASSERT(hairBlitter);
571 hairBlitter->setup(blitter);
572
reed@google.comc1f6db82012-10-31 17:57:01 +0000573#ifdef SK_DEBUG
574 if (scaleStart > 0 && scaleStop > 0) {
575 // be sure we don't draw twice in the same pixel
576 SkASSERT(istart < istop - 1);
577 }
578#endif
579
reed@google.comb03fe422012-10-25 17:37:03 +0000580 fstart = hairBlitter->drawCap(istart, fstart, slope, scaleStart);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000581 istart += 1;
reed@android.com28937282009-08-28 15:34:46 +0000582 int fullSpans = istop - istart - (scaleStop > 0);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000583 if (fullSpans > 0) {
reed@google.comb03fe422012-10-25 17:37:03 +0000584 fstart = hairBlitter->drawLine(istart, istart + fullSpans, fstart, slope);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000585 }
586 if (scaleStop > 0) {
reed@google.comb03fe422012-10-25 17:37:03 +0000587 hairBlitter->drawCap(istop - 1, fstart, slope, scaleStop);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000588 }
589}
590
reed@google.com045e62d2011-10-24 12:19:46 +0000591void SkScan::AntiHairLineRgn(const SkPoint& pt0, const SkPoint& pt1,
592 const SkRegion* clip, SkBlitter* blitter) {
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000593 if (clip && clip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000594 return;
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000595 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000596
597 SkASSERT(clip == NULL || !clip->getBounds().isEmpty());
598
599#ifdef TEST_GAMMA
600 build_gamma_table();
601#endif
602
reed@android.come28ff552009-11-19 20:46:39 +0000603 SkPoint pts[2] = { pt0, pt1 };
reed@android.com8a1c16f2008-12-17 15:59:43 +0000604
reed@google.com99b300e2012-04-17 20:43:47 +0000605 // We have to pre-clip the line to fit in a SkFixed, so we just chop
606 // the line. TODO find a way to actually draw beyond that range.
607 {
608 SkRect fixedBounds;
609 const SkScalar max = SkIntToScalar(32767);
610 fixedBounds.set(-max, -max, max, max);
611 if (!SkLineClipper::IntersectLine(pts, fixedBounds, pts)) {
612 return;
613 }
614 }
reed@google.com99b300e2012-04-17 20:43:47 +0000615
reed@android.come28ff552009-11-19 20:46:39 +0000616 if (clip) {
617 SkRect clipBounds;
618 clipBounds.set(clip->getBounds());
reed@android.coma3d90102009-11-30 12:48:33 +0000619 /* We perform integral clipping later on, but we do a scalar clip first
620 to ensure that our coordinates are expressible in fixed/integers.
621
622 antialiased hairlines can draw up to 1/2 of a pixel outside of
623 their bounds, so we need to outset the clip before calling the
624 clipper. To make the numerics safer, we outset by a whole pixel,
625 since the 1/2 pixel boundary is important to the antihair blitter,
626 we don't want to risk numerical fate by chopping on that edge.
627 */
628 clipBounds.inset(-SK_Scalar1, -SK_Scalar1);
629
reed@android.come28ff552009-11-19 20:46:39 +0000630 if (!SkLineClipper::IntersectLine(pts, clipBounds, pts)) {
631 return;
632 }
633 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000634
reed@android.come28ff552009-11-19 20:46:39 +0000635 SkFDot6 x0 = SkScalarToFDot6(pts[0].fX);
636 SkFDot6 y0 = SkScalarToFDot6(pts[0].fY);
637 SkFDot6 x1 = SkScalarToFDot6(pts[1].fX);
638 SkFDot6 y1 = SkScalarToFDot6(pts[1].fY);
639
640 if (clip) {
641 SkFDot6 left = SkMin32(x0, x1);
642 SkFDot6 top = SkMin32(y0, y1);
643 SkFDot6 right = SkMax32(x0, x1);
644 SkFDot6 bottom = SkMax32(y0, y1);
645 SkIRect ir;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000646
647 ir.set( SkFDot6Floor(left) - 1,
648 SkFDot6Floor(top) - 1,
649 SkFDot6Ceil(right) + 1,
650 SkFDot6Ceil(bottom) + 1);
651
reed@android.come28ff552009-11-19 20:46:39 +0000652 if (clip->quickReject(ir)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000653 return;
reed@android.come28ff552009-11-19 20:46:39 +0000654 }
655 if (!clip->quickContains(ir)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000656 SkRegion::Cliperator iter(*clip, ir);
657 const SkIRect* r = &iter.rect();
658
reed@android.come28ff552009-11-19 20:46:39 +0000659 while (!iter.done()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000660 do_anti_hairline(x0, y0, x1, y1, r, blitter);
661 iter.next();
662 }
663 return;
664 }
665 // fall through to no-clip case
666 }
667 do_anti_hairline(x0, y0, x1, y1, NULL, blitter);
668}
669
reed@google.com045e62d2011-10-24 12:19:46 +0000670void SkScan::AntiHairRect(const SkRect& rect, const SkRasterClip& clip,
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000671 SkBlitter* blitter) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000672 SkPoint p0, p1;
673
674 p0.set(rect.fLeft, rect.fTop);
675 p1.set(rect.fRight, rect.fTop);
676 SkScan::AntiHairLine(p0, p1, clip, blitter);
677 p0.set(rect.fRight, rect.fBottom);
678 SkScan::AntiHairLine(p0, p1, clip, blitter);
679 p1.set(rect.fLeft, rect.fBottom);
680 SkScan::AntiHairLine(p0, p1, clip, blitter);
681 p0.set(rect.fLeft, rect.fTop);
682 SkScan::AntiHairLine(p0, p1, clip, blitter);
683}
684
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000685///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000686
687typedef int FDot8; // 24.8 integer fixed point
688
689static inline FDot8 SkFixedToFDot8(SkFixed x) {
690 return (x + 0x80) >> 8;
691}
692
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000693static void do_scanline(FDot8 L, int top, FDot8 R, U8CPU alpha,
694 SkBlitter* blitter) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000695 SkASSERT(L < R);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000696
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000697 if ((L >> 8) == ((R - 1) >> 8)) { // 1x1 pixel
reed@android.com8a1c16f2008-12-17 15:59:43 +0000698 blitter->blitV(L >> 8, top, 1, SkAlphaMul(alpha, R - L));
699 return;
700 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000701
reed@android.com8a1c16f2008-12-17 15:59:43 +0000702 int left = L >> 8;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000703
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000704 if (L & 0xFF) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000705 blitter->blitV(left, top, 1, SkAlphaMul(alpha, 256 - (L & 0xFF)));
706 left += 1;
707 }
708
709 int rite = R >> 8;
710 int width = rite - left;
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000711 if (width > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000712 call_hline_blitter(blitter, left, top, width, alpha);
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000713 }
714 if (R & 0xFF) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000715 blitter->blitV(rite, top, 1, SkAlphaMul(alpha, R & 0xFF));
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000716 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000717}
718
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000719static void antifilldot8(FDot8 L, FDot8 T, FDot8 R, FDot8 B, SkBlitter* blitter,
720 bool fillInner) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000721 // check for empty now that we're in our reduced precision space
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000722 if (L >= R || T >= B) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000723 return;
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000724 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000725 int top = T >> 8;
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000726 if (top == ((B - 1) >> 8)) { // just one scanline high
reed@android.com8a1c16f2008-12-17 15:59:43 +0000727 do_scanline(L, top, R, B - T - 1, blitter);
728 return;
729 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000730
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000731 if (T & 0xFF) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000732 do_scanline(L, top, R, 256 - (T & 0xFF), blitter);
733 top += 1;
734 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000735
reed@android.com8a1c16f2008-12-17 15:59:43 +0000736 int bot = B >> 8;
737 int height = bot - top;
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000738 if (height > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000739 int left = L >> 8;
reed@google.comf7398c32011-05-05 14:24:47 +0000740 if (left == ((R - 1) >> 8)) { // just 1-pixel wide
741 blitter->blitV(left, top, height, R - L - 1);
742 } else {
743 if (L & 0xFF) {
744 blitter->blitV(left, top, height, 256 - (L & 0xFF));
745 left += 1;
746 }
747 int rite = R >> 8;
748 int width = rite - left;
749 if (width > 0 && fillInner) {
750 blitter->blitRect(left, top, width, height);
751 }
752 if (R & 0xFF) {
753 blitter->blitV(rite, top, height, R & 0xFF);
754 }
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000755 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000756 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000757
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000758 if (B & 0xFF) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000759 do_scanline(L, bot, R, B & 0xFF, blitter);
mike@reedtribe.orgbcc1d332011-04-09 19:16:54 +0000760 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000761}
762
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000763static void antifillrect(const SkXRect& xr, SkBlitter* blitter) {
764 antifilldot8(SkFixedToFDot8(xr.fLeft), SkFixedToFDot8(xr.fTop),
765 SkFixedToFDot8(xr.fRight), SkFixedToFDot8(xr.fBottom),
766 blitter, true);
767}
768
reed@android.com8a1c16f2008-12-17 15:59:43 +0000769///////////////////////////////////////////////////////////////////////////////
770
reed@google.com67ba5fa2011-10-24 12:56:20 +0000771void SkScan::AntiFillXRect(const SkXRect& xr, const SkRegion* clip,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000772 SkBlitter* blitter) {
reed@google.com67ba5fa2011-10-24 12:56:20 +0000773 if (NULL == clip) {
774 antifillrect(xr, blitter);
775 } else {
776 SkIRect outerBounds;
777 XRect_roundOut(xr, &outerBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000778
reed@google.com67ba5fa2011-10-24 12:56:20 +0000779 if (clip->isRect()) {
780 const SkIRect& clipBounds = clip->getBounds();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000781
reed@google.com67ba5fa2011-10-24 12:56:20 +0000782 if (clipBounds.contains(outerBounds)) {
783 antifillrect(xr, blitter);
784 } else {
785 SkXRect tmpR;
786 // this keeps our original edges fractional
787 XRect_set(&tmpR, clipBounds);
788 if (tmpR.intersect(xr)) {
789 antifillrect(tmpR, blitter);
790 }
791 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000792 } else {
reed@google.com67ba5fa2011-10-24 12:56:20 +0000793 SkRegion::Cliperator clipper(*clip, outerBounds);
794 const SkIRect& rr = clipper.rect();
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000795
reed@google.com67ba5fa2011-10-24 12:56:20 +0000796 while (!clipper.done()) {
797 SkXRect tmpR;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000798
reed@google.com67ba5fa2011-10-24 12:56:20 +0000799 // this keeps our original edges fractional
800 XRect_set(&tmpR, rr);
801 if (tmpR.intersect(xr)) {
802 antifillrect(tmpR, blitter);
803 }
804 clipper.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000805 }
806 }
reed@google.com67ba5fa2011-10-24 12:56:20 +0000807 }
808}
809
810void SkScan::AntiFillXRect(const SkXRect& xr, const SkRasterClip& clip,
811 SkBlitter* blitter) {
812 if (clip.isBW()) {
813 AntiFillXRect(xr, &clip.bwRgn(), blitter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000814 } else {
reed@google.com67ba5fa2011-10-24 12:56:20 +0000815 SkIRect outerBounds;
816 XRect_roundOut(xr, &outerBounds);
817
818 if (clip.quickContains(outerBounds)) {
819 AntiFillXRect(xr, NULL, blitter);
820 } else {
821 SkAAClipBlitterWrapper wrapper(clip, blitter);
822 blitter = wrapper.getBlitter();
823
824 AntiFillXRect(xr, &wrapper.getRgn(), wrapper.getBlitter());
reed@google.com045e62d2011-10-24 12:19:46 +0000825 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000826 }
827}
828
reed@android.com8a1c16f2008-12-17 15:59:43 +0000829/* This guy takes a float-rect, but with the key improvement that it has
830 already been clipped, so we know that it is safe to convert it into a
831 XRect (fixedpoint), as it won't overflow.
832*/
833static void antifillrect(const SkRect& r, SkBlitter* blitter) {
834 SkXRect xr;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000835
reed@android.com8a1c16f2008-12-17 15:59:43 +0000836 XRect_set(&xr, r);
837 antifillrect(xr, blitter);
838}
839
840/* We repeat the clipping logic of AntiFillXRect because the float rect might
841 overflow if we blindly converted it to an XRect. This sucks that we have to
842 repeat the clipping logic, but I don't see how to share the code/logic.
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000843
reed@android.com8a1c16f2008-12-17 15:59:43 +0000844 We clip r (as needed) into one or more (smaller) float rects, and then pass
845 those to our version of antifillrect, which converts it into an XRect and
846 then calls the blit.
847*/
reed@android.come28ff552009-11-19 20:46:39 +0000848void SkScan::AntiFillRect(const SkRect& origR, const SkRegion* clip,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000849 SkBlitter* blitter) {
850 if (clip) {
reed@android.come28ff552009-11-19 20:46:39 +0000851 SkRect newR;
852 newR.set(clip->getBounds());
853 if (!newR.intersect(origR)) {
854 return;
855 }
856
reed@android.com8a1c16f2008-12-17 15:59:43 +0000857 SkIRect outerBounds;
reed@android.come28ff552009-11-19 20:46:39 +0000858 newR.roundOut(&outerBounds);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000859
reed@android.com8a1c16f2008-12-17 15:59:43 +0000860 if (clip->isRect()) {
reed@android.come28ff552009-11-19 20:46:39 +0000861 antifillrect(newR, blitter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000862 } else {
863 SkRegion::Cliperator clipper(*clip, outerBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000864 while (!clipper.done()) {
reed@android.come28ff552009-11-19 20:46:39 +0000865 newR.set(clipper.rect());
866 if (newR.intersect(origR)) {
867 antifillrect(newR, blitter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000868 }
869 clipper.next();
870 }
871 }
872 } else {
reed@android.come28ff552009-11-19 20:46:39 +0000873 antifillrect(origR, blitter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000874 }
875}
876
reed@google.com045e62d2011-10-24 12:19:46 +0000877void SkScan::AntiFillRect(const SkRect& r, const SkRasterClip& clip,
878 SkBlitter* blitter) {
879 if (clip.isBW()) {
880 AntiFillRect(r, &clip.bwRgn(), blitter);
881 } else {
882 SkAAClipBlitterWrapper wrap(clip, blitter);
883 AntiFillRect(r, &wrap.getRgn(), wrap.getBlitter());
884 }
885}
886
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000887///////////////////////////////////////////////////////////////////////////////
888
reed@google.com3a968752011-04-07 13:31:48 +0000889#define SkAlphaMulRound(a, b) SkMulDiv255Round(a, b)
890
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000891// calls blitRect() if the rectangle is non-empty
892static void fillcheckrect(int L, int T, int R, int B, SkBlitter* blitter) {
893 if (L < R && T < B) {
894 blitter->blitRect(L, T, R - L, B - T);
895 }
896}
897
898static inline FDot8 SkScalarToFDot8(SkScalar x) {
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000899 return (int)(x * 256);
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000900}
901
902static inline int FDot8Floor(FDot8 x) {
903 return x >> 8;
904}
905
906static inline int FDot8Ceil(FDot8 x) {
907 return (x + 0xFF) >> 8;
908}
909
910// 1 - (1 - a)*(1 - b)
911static inline U8CPU InvAlphaMul(U8CPU a, U8CPU b) {
reed@google.com3a968752011-04-07 13:31:48 +0000912 // need precise rounding (not just SkAlphaMul) so that values like
913 // a=228, b=252 don't overflow the result
914 return SkToU8(a + b - SkAlphaMulRound(a, b));
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000915}
916
917static void inner_scanline(FDot8 L, int top, FDot8 R, U8CPU alpha,
918 SkBlitter* blitter) {
919 SkASSERT(L < R);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000920
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000921 if ((L >> 8) == ((R - 1) >> 8)) { // 1x1 pixel
922 blitter->blitV(L >> 8, top, 1, InvAlphaMul(alpha, R - L));
923 return;
924 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000925
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000926 int left = L >> 8;
927 if (L & 0xFF) {
928 blitter->blitV(left, top, 1, InvAlphaMul(alpha, L & 0xFF));
929 left += 1;
930 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000931
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000932 int rite = R >> 8;
933 int width = rite - left;
934 if (width > 0) {
935 call_hline_blitter(blitter, left, top, width, alpha);
936 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000937
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000938 if (R & 0xFF) {
939 blitter->blitV(rite, top, 1, InvAlphaMul(alpha, ~R & 0xFF));
940 }
941}
942
943static void innerstrokedot8(FDot8 L, FDot8 T, FDot8 R, FDot8 B,
944 SkBlitter* blitter) {
945 SkASSERT(L < R && T < B);
946
947 int top = T >> 8;
948 if (top == ((B - 1) >> 8)) { // just one scanline high
reed@google.com10d02b62012-04-16 17:12:38 +0000949 // We want the inverse of B-T, since we're the inner-stroke
950 int alpha = 256 - (B - T);
951 if (alpha) {
952 inner_scanline(L, top, R, alpha, blitter);
953 }
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000954 return;
955 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000956
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000957 if (T & 0xFF) {
958 inner_scanline(L, top, R, T & 0xFF, blitter);
959 top += 1;
960 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000961
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000962 int bot = B >> 8;
963 int height = bot - top;
964 if (height > 0) {
965 if (L & 0xFF) {
966 blitter->blitV(L >> 8, top, height, L & 0xFF);
967 }
968 if (R & 0xFF) {
969 blitter->blitV(R >> 8, top, height, ~R & 0xFF);
970 }
971 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000972
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000973 if (B & 0xFF) {
974 inner_scanline(L, bot, R, ~B & 0xFF, blitter);
975 }
976}
977
reed@google.com761fb622011-04-04 18:58:05 +0000978void SkScan::AntiFrameRect(const SkRect& r, const SkPoint& strokeSize,
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000979 const SkRegion* clip, SkBlitter* blitter) {
reed@google.com761fb622011-04-04 18:58:05 +0000980 SkASSERT(strokeSize.fX >= 0 && strokeSize.fY >= 0);
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000981
reed@google.com761fb622011-04-04 18:58:05 +0000982 SkScalar rx = SkScalarHalf(strokeSize.fX);
983 SkScalar ry = SkScalarHalf(strokeSize.fY);
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000984
985 // outset by the radius
reed@google.com761fb622011-04-04 18:58:05 +0000986 FDot8 L = SkScalarToFDot8(r.fLeft - rx);
987 FDot8 T = SkScalarToFDot8(r.fTop - ry);
988 FDot8 R = SkScalarToFDot8(r.fRight + rx);
989 FDot8 B = SkScalarToFDot8(r.fBottom + ry);
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000990
991 SkIRect outer;
992 // set outer to the outer rect of the outer section
993 outer.set(FDot8Floor(L), FDot8Floor(T), FDot8Ceil(R), FDot8Ceil(B));
994
995 SkBlitterClipper clipper;
996 if (clip) {
997 if (clip->quickReject(outer)) {
998 return;
999 }
1000 if (!clip->contains(outer)) {
1001 blitter = clipper.apply(blitter, clip, &outer);
1002 }
1003 // now we can ignore clip for the rest of the function
1004 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001005
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +00001006 // stroke the outer hull
1007 antifilldot8(L, T, R, B, blitter, false);
1008
1009 // set outer to the outer rect of the middle section
1010 outer.set(FDot8Ceil(L), FDot8Ceil(T), FDot8Floor(R), FDot8Floor(B));
1011
1012 // in case we lost a bit with diameter/2
reed@google.com761fb622011-04-04 18:58:05 +00001013 rx = strokeSize.fX - rx;
1014 ry = strokeSize.fY - ry;
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +00001015 // inset by the radius
reed@google.com761fb622011-04-04 18:58:05 +00001016 L = SkScalarToFDot8(r.fLeft + rx);
1017 T = SkScalarToFDot8(r.fTop + ry);
1018 R = SkScalarToFDot8(r.fRight - rx);
1019 B = SkScalarToFDot8(r.fBottom - ry);
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +00001020
1021 if (L >= R || T >= B) {
1022 fillcheckrect(outer.fLeft, outer.fTop, outer.fRight, outer.fBottom,
1023 blitter);
1024 } else {
1025 SkIRect inner;
1026 // set inner to the inner rect of the middle section
1027 inner.set(FDot8Floor(L), FDot8Floor(T), FDot8Ceil(R), FDot8Ceil(B));
1028
1029 // draw the frame in 4 pieces
1030 fillcheckrect(outer.fLeft, outer.fTop, outer.fRight, inner.fTop,
1031 blitter);
1032 fillcheckrect(outer.fLeft, inner.fTop, inner.fLeft, inner.fBottom,
1033 blitter);
1034 fillcheckrect(inner.fRight, inner.fTop, outer.fRight, inner.fBottom,
1035 blitter);
1036 fillcheckrect(outer.fLeft, inner.fBottom, outer.fRight, outer.fBottom,
1037 blitter);
1038
1039 // now stroke the inner rect, which is similar to antifilldot8() except that
1040 // it treats the fractional coordinates with the inverse bias (since its
1041 // inner).
1042 innerstrokedot8(L, T, R, B, blitter);
1043 }
1044}
reed@google.com045e62d2011-10-24 12:19:46 +00001045
1046void SkScan::AntiFrameRect(const SkRect& r, const SkPoint& strokeSize,
1047 const SkRasterClip& clip, SkBlitter* blitter) {
1048 if (clip.isBW()) {
1049 AntiFrameRect(r, strokeSize, &clip.bwRgn(), blitter);
1050 } else {
1051 SkAAClipBlitterWrapper wrap(clip, blitter);
1052 AntiFrameRect(r, strokeSize, &wrap.getRgn(), wrap.getBlitter());
1053 }
1054}