1
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
9
10 #include "SkScan.h"
11 #include "SkBlitter.h"
12 #include "SkColorPriv.h"
13 #include "SkLineClipper.h"
14 #include "SkRasterClip.h"
15 #include "SkFDot6.h"
16
17 /* 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.
23
24 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
32 #define HLINE_STACK_BUFFER 100
33
SmallDot6Scale(int value,int dot6)34 static 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
build_gamma_table()46 static void build_gamma_table() {
47 static bool gInit = false;
48
49 if (gInit == false) {
50 for (int i = 0; i < 256; i++) {
51 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
call_hline_blitter(SkBlitter * blitter,int x,int y,int count,U8CPU alpha)68 static void call_hline_blitter(SkBlitter* blitter, int x, int y, int count,
69 U8CPU alpha) {
70 SkASSERT(count > 0);
71
72 int16_t runs[HLINE_STACK_BUFFER + 1];
73 uint8_t aa[HLINE_STACK_BUFFER];
74
75 aa[0] = ApplyGamma(gGammaTable, alpha);
76 do {
77 int n = count;
78 if (n > HLINE_STACK_BUFFER) {
79 n = HLINE_STACK_BUFFER;
80 }
81 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
89 class SkAntiHairBlitter {
90 public:
SkAntiHairBlitter()91 SkAntiHairBlitter() : fBlitter(NULL) {}
~SkAntiHairBlitter()92 virtual ~SkAntiHairBlitter() {}
93
getBlitter() const94 SkBlitter* getBlitter() const { return fBlitter; }
95
setup(SkBlitter * blitter)96 void setup(SkBlitter* blitter) {
97 fBlitter = blitter;
98 }
99
100 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
103 private:
104 SkBlitter* fBlitter;
105 };
106
107 class HLine_SkAntiHairBlitter : public SkAntiHairBlitter {
108 public:
drawCap(int x,SkFixed fy,SkFixed slope,int mod64)109 virtual SkFixed drawCap(int x, SkFixed fy, SkFixed slope, int mod64) SK_OVERRIDE {
110 fy += SK_Fixed1/2;
111
112 int y = fy >> 16;
113 uint8_t a = (uint8_t)(fy >> 8);
114
115 // lower line
116 unsigned ma = SmallDot6Scale(a, mod64);
117 if (ma) {
118 call_hline_blitter(this->getBlitter(), x, y, 1, ma);
119 }
120
121 // upper line
122 ma = SmallDot6Scale(255 - a, mod64);
123 if (ma) {
124 call_hline_blitter(this->getBlitter(), x, y - 1, 1, ma);
125 }
126
127 return fy - SK_Fixed1/2;
128 }
129
drawLine(int x,int stopx,SkFixed fy,SkFixed slope)130 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;
135
136 int y = fy >> 16;
137 uint8_t a = (uint8_t)(fy >> 8);
138
139 // lower line
140 if (a) {
141 call_hline_blitter(this->getBlitter(), x, y, count, a);
142 }
143
144 // upper line
145 a = 255 - a;
146 if (a) {
147 call_hline_blitter(this->getBlitter(), x, y - 1, count, a);
148 }
149
150 return fy - SK_Fixed1/2;
151 }
152 };
153
154 class Horish_SkAntiHairBlitter : public SkAntiHairBlitter {
155 public:
drawCap(int x,SkFixed fy,SkFixed dy,int mod64)156 virtual SkFixed drawCap(int x, SkFixed fy, SkFixed dy, int mod64) SK_OVERRIDE {
157 int16_t runs[2];
158 uint8_t aa[1];
159
160 runs[0] = 1;
161 runs[1] = 0;
162
163 fy += SK_Fixed1/2;
164 SkBlitter* blitter = this->getBlitter();
165
166 int lower_y = fy >> 16;
167 uint8_t a = (uint8_t)(fy >> 8);
168 unsigned ma = SmallDot6Scale(a, mod64);
169 if (ma) {
170 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);
177 if (ma) {
178 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;
185
186 return fy - SK_Fixed1/2;
187 }
188
drawLine(int x,int stopx,SkFixed fy,SkFixed dy)189 virtual SkFixed drawLine(int x, int stopx, SkFixed fy, SkFixed dy) SK_OVERRIDE {
190 SkASSERT(x < stopx);
191
192 int16_t runs[2];
193 uint8_t aa[1];
194
195 runs[0] = 1;
196 runs[1] = 0;
197
198 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);
220
221 return fy - SK_Fixed1/2;
222 }
223 };
224
225 class VLine_SkAntiHairBlitter : public SkAntiHairBlitter {
226 public:
drawCap(int y,SkFixed fx,SkFixed dx,int mod64)227 virtual SkFixed drawCap(int y, SkFixed fx, SkFixed dx, int mod64) SK_OVERRIDE {
228 SkASSERT(0 == dx);
229 fx += SK_Fixed1/2;
230
231 int x = fx >> 16;
232 int a = (uint8_t)(fx >> 8);
233
234 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 }
242
243 return fx - SK_Fixed1/2;
244 }
245
drawLine(int y,int stopy,SkFixed fx,SkFixed dx)246 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;
250
251 int x = fx >> 16;
252 int a = (uint8_t)(fx >> 8);
253
254 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 }
261
262 return fx - SK_Fixed1/2;
263 }
264 };
265
266 class Vertish_SkAntiHairBlitter : public SkAntiHairBlitter {
267 public:
drawCap(int y,SkFixed fx,SkFixed dx,int mod64)268 virtual SkFixed drawCap(int y, SkFixed fx, SkFixed dx, int mod64) SK_OVERRIDE {
269 int16_t runs[3];
270 uint8_t aa[2];
271
272 runs[0] = 1;
273 runs[2] = 0;
274
275 fx += SK_Fixed1/2;
276 int x = fx >> 16;
277 uint8_t a = (uint8_t)(fx >> 8);
278
279 aa[0] = SmallDot6Scale(255 - a, mod64);
280 aa[1] = SmallDot6Scale(a, mod64);
281 // the clippng blitters might overwrite this guy, so we have to reset it each time
282 runs[1] = 1;
283 this->getBlitter()->blitAntiH(x - 1, y, aa, runs);
284 // the clipping blitters might edit runs, but should not affect us
285 SkASSERT(runs[0] == 1);
286 SkASSERT(runs[2] == 0);
287 fx += dx;
288
289 return fx - SK_Fixed1/2;
290 }
291
drawLine(int y,int stopy,SkFixed fx,SkFixed dx)292 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];
296
297 runs[0] = 1;
298 runs[2] = 0;
299
300 fx += SK_Fixed1/2;
301 do {
302 int x = fx >> 16;
303 uint8_t a = (uint8_t)(fx >> 8);
304
305 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);
315
316 return fx - SK_Fixed1/2;
317 }
318 };
319
fastfixdiv(SkFDot6 a,SkFDot6 b)320 static inline SkFixed fastfixdiv(SkFDot6 a, SkFDot6 b) {
321 SkASSERT((a << 16 >> 16) == a);
322 SkASSERT(b != 0);
323 return (a << 16) / b;
324 }
325
326 #define SkBITCOUNT(x) (sizeof(x) << 3)
327
328 #if 1
329 // returns high-bit set iff x==0x8000...
bad_int(int x)330 static inline int bad_int(int x) {
331 return x & -x;
332 }
333
any_bad_ints(int a,int b,int c,int d)334 static 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
good_int(int x)338 static inline int good_int(int x) {
339 return x ^ (1 << (SkBITCOUNT(x) - 1));
340 }
341
any_bad_ints(int a,int b,int c,int d)342 static 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
347 #ifdef SK_DEBUG
canConvertFDot6ToFixed(SkFDot6 x)348 static bool canConvertFDot6ToFixed(SkFDot6 x) {
349 const int maxDot6 = SK_MaxS32 >> (16 - 6);
350 return SkAbs32(x) <= maxDot6;
351 }
352 #endif
353
354 /*
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 */
contribution_64(SkFDot6 ordinate)360 static 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
do_anti_hairline(SkFDot6 x0,SkFDot6 y0,SkFDot6 x1,SkFDot6 y1,const SkIRect * clip,SkBlitter * blitter)373 static void do_anti_hairline(SkFDot6 x0, SkFDot6 y0, SkFDot6 x1, SkFDot6 y1,
374 const SkIRect* clip, SkBlitter* blitter) {
375 // 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 }
381
382 // 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
389 if (SkAbs32(x1 - x0) > SkIntToFDot6(511) || SkAbs32(y1 - y0) > SkIntToFDot6(511)) {
390 /* 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);
398 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;
405 SkFixed fstart, slope;
406
407 HLine_SkAntiHairBlitter hline_blitter;
408 Horish_SkAntiHairBlitter horish_blitter;
409 VLine_SkAntiHairBlitter vline_blitter;
410 Vertish_SkAntiHairBlitter vertish_blitter;
411 SkAntiHairBlitter* hairBlitter = NULL;
412
413 if (SkAbs32(x1 - x0) > SkAbs32(y1 - y0)) { // mostly horizontal
414 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;
424 hairBlitter = &hline_blitter;
425 } else {
426 slope = fastfixdiv(y1 - y0, x1 - x0);
427 SkASSERT(slope >= -SK_Fixed1 && slope <= SK_Fixed1);
428 fstart += (slope * (32 - (x0 & 63)) + 32) >> 6;
429 hairBlitter = &horish_blitter;
430 }
431
432 SkASSERT(istop > istart);
433 if (istop - istart == 1) {
434 // we are within a single pixel
435 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
443 if (clip){
444 if (istart >= clip->fRight || istop <= clip->fLeft) {
445 return;
446 }
447 if (istart < clip->fLeft) {
448 fstart += slope * (clip->fLeft - istart);
449 istart = clip->fLeft;
450 scaleStart = 64;
451 if (istop - istart == 1) {
452 // we are within a single pixel
453 scaleStart = contribution_64(x1);
454 scaleStop = 0;
455 }
456 }
457 if (istop > clip->fRight) {
458 istop = clip->fRight;
459 scaleStop = 0; // so we don't draw this last column
460 }
461
462 SkASSERT(istart <= istop);
463 if (istart == istop) {
464 return;
465 }
466 // now test if our Y values are completely inside the clip
467 int top, bottom;
468 if (slope >= 0) { // T2B
469 top = SkFixedFloor(fstart - SK_FixedHalf);
470 bottom = SkFixedCeil(fstart + (istop - istart - 1) * slope + SK_FixedHalf);
471 } else { // B2T
472 bottom = SkFixedCeil(fstart + SK_FixedHalf);
473 top = SkFixedFloor(fstart + (istop - istart - 1) * slope - SK_FixedHalf);
474 }
475 #ifdef OUTSET_BEFORE_CLIP_TEST
476 top -= 1;
477 bottom += 1;
478 #endif
479 if (top >= clip->fBottom || bottom <= clip->fTop) {
480 return;
481 }
482 if (clip->fTop <= top && clip->fBottom >= bottom) {
483 clip = NULL;
484 }
485 }
486 } else { // mostly vertical
487 if (y0 > y1) { // we want to go top-to-bottom
488 SkTSwap<SkFDot6>(x0, x1);
489 SkTSwap<SkFDot6>(y0, y1);
490 }
491
492 istart = SkFDot6Floor(y0);
493 istop = SkFDot6Ceil(y1);
494 fstart = SkFDot6ToFixed(x0);
495 if (x0 == x1) {
496 if (y0 == y1) { // are we zero length?
497 return; // nothing to do
498 }
499 slope = 0;
500 hairBlitter = &vline_blitter;
501 } else {
502 slope = fastfixdiv(x1 - x0, y1 - y0);
503 SkASSERT(slope <= SK_Fixed1 && slope >= -SK_Fixed1);
504 fstart += (slope * (32 - (y0 & 63)) + 32) >> 6;
505 hairBlitter = &vertish_blitter;
506 }
507
508 SkASSERT(istop > istart);
509 if (istop - istart == 1) {
510 // we are within a single pixel
511 scaleStart = y1 - y0;
512 SkASSERT(scaleStart >= 0 && scaleStart <= 64);
513 scaleStop = 0;
514 } else {
515 scaleStart = 64 - (y0 & 63);
516 scaleStop = y1 & 63;
517 }
518
519 if (clip) {
520 if (istart >= clip->fBottom || istop <= clip->fTop) {
521 return;
522 }
523 if (istart < clip->fTop) {
524 fstart += slope * (clip->fTop - istart);
525 istart = clip->fTop;
526 scaleStart = 64;
527 if (istop - istart == 1) {
528 // we are within a single pixel
529 scaleStart = contribution_64(y1);
530 scaleStop = 0;
531 }
532 }
533 if (istop > clip->fBottom) {
534 istop = clip->fBottom;
535 scaleStop = 0; // so we don't draw this last row
536 }
537
538 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;
544 if (slope >= 0) { // L2R
545 left = SkFixedFloor(fstart - SK_FixedHalf);
546 right = SkFixedCeil(fstart + (istop - istart - 1) * slope + SK_FixedHalf);
547 } else { // R2L
548 right = SkFixedCeil(fstart + SK_FixedHalf);
549 left = SkFixedFloor(fstart + (istop - istart - 1) * slope - SK_FixedHalf);
550 }
551 #ifdef OUTSET_BEFORE_CLIP_TEST
552 left -= 1;
553 right += 1;
554 #endif
555 if (left >= clip->fRight || right <= clip->fLeft) {
556 return;
557 }
558 if (clip->fLeft <= left && clip->fRight >= right) {
559 clip = NULL;
560 }
561 }
562 }
563
564 SkRectClipBlitter rectClipper;
565 if (clip) {
566 rectClipper.init(blitter, *clip);
567 blitter = &rectClipper;
568 }
569
570 SkASSERT(hairBlitter);
571 hairBlitter->setup(blitter);
572
573 #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
580 fstart = hairBlitter->drawCap(istart, fstart, slope, scaleStart);
581 istart += 1;
582 int fullSpans = istop - istart - (scaleStop > 0);
583 if (fullSpans > 0) {
584 fstart = hairBlitter->drawLine(istart, istart + fullSpans, fstart, slope);
585 }
586 if (scaleStop > 0) {
587 hairBlitter->drawCap(istop - 1, fstart, slope, scaleStop);
588 }
589 }
590
AntiHairLineRgn(const SkPoint & pt0,const SkPoint & pt1,const SkRegion * clip,SkBlitter * blitter)591 void SkScan::AntiHairLineRgn(const SkPoint& pt0, const SkPoint& pt1,
592 const SkRegion* clip, SkBlitter* blitter) {
593 if (clip && clip->isEmpty()) {
594 return;
595 }
596
597 SkASSERT(clip == NULL || !clip->getBounds().isEmpty());
598
599 #ifdef TEST_GAMMA
600 build_gamma_table();
601 #endif
602
603 SkPoint pts[2] = { pt0, pt1 };
604
605 #ifdef SK_SCALAR_IS_FLOAT
606 // We have to pre-clip the line to fit in a SkFixed, so we just chop
607 // the line. TODO find a way to actually draw beyond that range.
608 {
609 SkRect fixedBounds;
610 const SkScalar max = SkIntToScalar(32767);
611 fixedBounds.set(-max, -max, max, max);
612 if (!SkLineClipper::IntersectLine(pts, fixedBounds, pts)) {
613 return;
614 }
615 }
616 #endif
617
618 if (clip) {
619 SkRect clipBounds;
620 clipBounds.set(clip->getBounds());
621 /* We perform integral clipping later on, but we do a scalar clip first
622 to ensure that our coordinates are expressible in fixed/integers.
623
624 antialiased hairlines can draw up to 1/2 of a pixel outside of
625 their bounds, so we need to outset the clip before calling the
626 clipper. To make the numerics safer, we outset by a whole pixel,
627 since the 1/2 pixel boundary is important to the antihair blitter,
628 we don't want to risk numerical fate by chopping on that edge.
629 */
630 clipBounds.inset(-SK_Scalar1, -SK_Scalar1);
631
632 if (!SkLineClipper::IntersectLine(pts, clipBounds, pts)) {
633 return;
634 }
635 }
636
637 SkFDot6 x0 = SkScalarToFDot6(pts[0].fX);
638 SkFDot6 y0 = SkScalarToFDot6(pts[0].fY);
639 SkFDot6 x1 = SkScalarToFDot6(pts[1].fX);
640 SkFDot6 y1 = SkScalarToFDot6(pts[1].fY);
641
642 if (clip) {
643 SkFDot6 left = SkMin32(x0, x1);
644 SkFDot6 top = SkMin32(y0, y1);
645 SkFDot6 right = SkMax32(x0, x1);
646 SkFDot6 bottom = SkMax32(y0, y1);
647 SkIRect ir;
648
649 ir.set( SkFDot6Floor(left) - 1,
650 SkFDot6Floor(top) - 1,
651 SkFDot6Ceil(right) + 1,
652 SkFDot6Ceil(bottom) + 1);
653
654 if (clip->quickReject(ir)) {
655 return;
656 }
657 if (!clip->quickContains(ir)) {
658 SkRegion::Cliperator iter(*clip, ir);
659 const SkIRect* r = &iter.rect();
660
661 while (!iter.done()) {
662 do_anti_hairline(x0, y0, x1, y1, r, blitter);
663 iter.next();
664 }
665 return;
666 }
667 // fall through to no-clip case
668 }
669 do_anti_hairline(x0, y0, x1, y1, NULL, blitter);
670 }
671
AntiHairRect(const SkRect & rect,const SkRasterClip & clip,SkBlitter * blitter)672 void SkScan::AntiHairRect(const SkRect& rect, const SkRasterClip& clip,
673 SkBlitter* blitter) {
674 SkPoint p0, p1;
675
676 p0.set(rect.fLeft, rect.fTop);
677 p1.set(rect.fRight, rect.fTop);
678 SkScan::AntiHairLine(p0, p1, clip, blitter);
679 p0.set(rect.fRight, rect.fBottom);
680 SkScan::AntiHairLine(p0, p1, clip, blitter);
681 p1.set(rect.fLeft, rect.fBottom);
682 SkScan::AntiHairLine(p0, p1, clip, blitter);
683 p0.set(rect.fLeft, rect.fTop);
684 SkScan::AntiHairLine(p0, p1, clip, blitter);
685 }
686
687 ///////////////////////////////////////////////////////////////////////////////
688
689 typedef int FDot8; // 24.8 integer fixed point
690
SkFixedToFDot8(SkFixed x)691 static inline FDot8 SkFixedToFDot8(SkFixed x) {
692 return (x + 0x80) >> 8;
693 }
694
do_scanline(FDot8 L,int top,FDot8 R,U8CPU alpha,SkBlitter * blitter)695 static void do_scanline(FDot8 L, int top, FDot8 R, U8CPU alpha,
696 SkBlitter* blitter) {
697 SkASSERT(L < R);
698
699 if ((L >> 8) == ((R - 1) >> 8)) { // 1x1 pixel
700 blitter->blitV(L >> 8, top, 1, SkAlphaMul(alpha, R - L));
701 return;
702 }
703
704 int left = L >> 8;
705
706 if (L & 0xFF) {
707 blitter->blitV(left, top, 1, SkAlphaMul(alpha, 256 - (L & 0xFF)));
708 left += 1;
709 }
710
711 int rite = R >> 8;
712 int width = rite - left;
713 if (width > 0) {
714 call_hline_blitter(blitter, left, top, width, alpha);
715 }
716 if (R & 0xFF) {
717 blitter->blitV(rite, top, 1, SkAlphaMul(alpha, R & 0xFF));
718 }
719 }
720
antifilldot8(FDot8 L,FDot8 T,FDot8 R,FDot8 B,SkBlitter * blitter,bool fillInner)721 static void antifilldot8(FDot8 L, FDot8 T, FDot8 R, FDot8 B, SkBlitter* blitter,
722 bool fillInner) {
723 // check for empty now that we're in our reduced precision space
724 if (L >= R || T >= B) {
725 return;
726 }
727 int top = T >> 8;
728 if (top == ((B - 1) >> 8)) { // just one scanline high
729 do_scanline(L, top, R, B - T - 1, blitter);
730 return;
731 }
732
733 if (T & 0xFF) {
734 do_scanline(L, top, R, 256 - (T & 0xFF), blitter);
735 top += 1;
736 }
737
738 int bot = B >> 8;
739 int height = bot - top;
740 if (height > 0) {
741 int left = L >> 8;
742 if (left == ((R - 1) >> 8)) { // just 1-pixel wide
743 blitter->blitV(left, top, height, R - L - 1);
744 } else {
745 if (L & 0xFF) {
746 blitter->blitV(left, top, height, 256 - (L & 0xFF));
747 left += 1;
748 }
749 int rite = R >> 8;
750 int width = rite - left;
751 if (width > 0 && fillInner) {
752 blitter->blitRect(left, top, width, height);
753 }
754 if (R & 0xFF) {
755 blitter->blitV(rite, top, height, R & 0xFF);
756 }
757 }
758 }
759
760 if (B & 0xFF) {
761 do_scanline(L, bot, R, B & 0xFF, blitter);
762 }
763 }
764
antifillrect(const SkXRect & xr,SkBlitter * blitter)765 static void antifillrect(const SkXRect& xr, SkBlitter* blitter) {
766 antifilldot8(SkFixedToFDot8(xr.fLeft), SkFixedToFDot8(xr.fTop),
767 SkFixedToFDot8(xr.fRight), SkFixedToFDot8(xr.fBottom),
768 blitter, true);
769 }
770
771 ///////////////////////////////////////////////////////////////////////////////
772
AntiFillXRect(const SkXRect & xr,const SkRegion * clip,SkBlitter * blitter)773 void SkScan::AntiFillXRect(const SkXRect& xr, const SkRegion* clip,
774 SkBlitter* blitter) {
775 if (NULL == clip) {
776 antifillrect(xr, blitter);
777 } else {
778 SkIRect outerBounds;
779 XRect_roundOut(xr, &outerBounds);
780
781 if (clip->isRect()) {
782 const SkIRect& clipBounds = clip->getBounds();
783
784 if (clipBounds.contains(outerBounds)) {
785 antifillrect(xr, blitter);
786 } else {
787 SkXRect tmpR;
788 // this keeps our original edges fractional
789 XRect_set(&tmpR, clipBounds);
790 if (tmpR.intersect(xr)) {
791 antifillrect(tmpR, blitter);
792 }
793 }
794 } else {
795 SkRegion::Cliperator clipper(*clip, outerBounds);
796 const SkIRect& rr = clipper.rect();
797
798 while (!clipper.done()) {
799 SkXRect tmpR;
800
801 // this keeps our original edges fractional
802 XRect_set(&tmpR, rr);
803 if (tmpR.intersect(xr)) {
804 antifillrect(tmpR, blitter);
805 }
806 clipper.next();
807 }
808 }
809 }
810 }
811
AntiFillXRect(const SkXRect & xr,const SkRasterClip & clip,SkBlitter * blitter)812 void SkScan::AntiFillXRect(const SkXRect& xr, const SkRasterClip& clip,
813 SkBlitter* blitter) {
814 if (clip.isBW()) {
815 AntiFillXRect(xr, &clip.bwRgn(), blitter);
816 } else {
817 SkIRect outerBounds;
818 XRect_roundOut(xr, &outerBounds);
819
820 if (clip.quickContains(outerBounds)) {
821 AntiFillXRect(xr, NULL, blitter);
822 } else {
823 SkAAClipBlitterWrapper wrapper(clip, blitter);
824 blitter = wrapper.getBlitter();
825
826 AntiFillXRect(xr, &wrapper.getRgn(), wrapper.getBlitter());
827 }
828 }
829 }
830
831 #ifdef SK_SCALAR_IS_FLOAT
832
833 /* This guy takes a float-rect, but with the key improvement that it has
834 already been clipped, so we know that it is safe to convert it into a
835 XRect (fixedpoint), as it won't overflow.
836 */
antifillrect(const SkRect & r,SkBlitter * blitter)837 static void antifillrect(const SkRect& r, SkBlitter* blitter) {
838 SkXRect xr;
839
840 XRect_set(&xr, r);
841 antifillrect(xr, blitter);
842 }
843
844 /* We repeat the clipping logic of AntiFillXRect because the float rect might
845 overflow if we blindly converted it to an XRect. This sucks that we have to
846 repeat the clipping logic, but I don't see how to share the code/logic.
847
848 We clip r (as needed) into one or more (smaller) float rects, and then pass
849 those to our version of antifillrect, which converts it into an XRect and
850 then calls the blit.
851 */
AntiFillRect(const SkRect & origR,const SkRegion * clip,SkBlitter * blitter)852 void SkScan::AntiFillRect(const SkRect& origR, const SkRegion* clip,
853 SkBlitter* blitter) {
854 if (clip) {
855 SkRect newR;
856 newR.set(clip->getBounds());
857 if (!newR.intersect(origR)) {
858 return;
859 }
860
861 SkIRect outerBounds;
862 newR.roundOut(&outerBounds);
863
864 if (clip->isRect()) {
865 antifillrect(newR, blitter);
866 } else {
867 SkRegion::Cliperator clipper(*clip, outerBounds);
868 while (!clipper.done()) {
869 newR.set(clipper.rect());
870 if (newR.intersect(origR)) {
871 antifillrect(newR, blitter);
872 }
873 clipper.next();
874 }
875 }
876 } else {
877 antifillrect(origR, blitter);
878 }
879 }
880
AntiFillRect(const SkRect & r,const SkRasterClip & clip,SkBlitter * blitter)881 void SkScan::AntiFillRect(const SkRect& r, const SkRasterClip& clip,
882 SkBlitter* blitter) {
883 if (clip.isBW()) {
884 AntiFillRect(r, &clip.bwRgn(), blitter);
885 } else {
886 SkAAClipBlitterWrapper wrap(clip, blitter);
887 AntiFillRect(r, &wrap.getRgn(), wrap.getBlitter());
888 }
889 }
890
891 #endif // SK_SCALAR_IS_FLOAT
892
893 ///////////////////////////////////////////////////////////////////////////////
894
895 #define SkAlphaMulRound(a, b) SkMulDiv255Round(a, b)
896
897 // calls blitRect() if the rectangle is non-empty
fillcheckrect(int L,int T,int R,int B,SkBlitter * blitter)898 static void fillcheckrect(int L, int T, int R, int B, SkBlitter* blitter) {
899 if (L < R && T < B) {
900 blitter->blitRect(L, T, R - L, B - T);
901 }
902 }
903
SkScalarToFDot8(SkScalar x)904 static inline FDot8 SkScalarToFDot8(SkScalar x) {
905 #ifdef SK_SCALAR_IS_FLOAT
906 return (int)(x * 256);
907 #else
908 return x >> 8;
909 #endif
910 }
911
FDot8Floor(FDot8 x)912 static inline int FDot8Floor(FDot8 x) {
913 return x >> 8;
914 }
915
FDot8Ceil(FDot8 x)916 static inline int FDot8Ceil(FDot8 x) {
917 return (x + 0xFF) >> 8;
918 }
919
920 // 1 - (1 - a)*(1 - b)
InvAlphaMul(U8CPU a,U8CPU b)921 static inline U8CPU InvAlphaMul(U8CPU a, U8CPU b) {
922 // need precise rounding (not just SkAlphaMul) so that values like
923 // a=228, b=252 don't overflow the result
924 return SkToU8(a + b - SkAlphaMulRound(a, b));
925 }
926
inner_scanline(FDot8 L,int top,FDot8 R,U8CPU alpha,SkBlitter * blitter)927 static void inner_scanline(FDot8 L, int top, FDot8 R, U8CPU alpha,
928 SkBlitter* blitter) {
929 SkASSERT(L < R);
930
931 if ((L >> 8) == ((R - 1) >> 8)) { // 1x1 pixel
932 blitter->blitV(L >> 8, top, 1, InvAlphaMul(alpha, R - L));
933 return;
934 }
935
936 int left = L >> 8;
937 if (L & 0xFF) {
938 blitter->blitV(left, top, 1, InvAlphaMul(alpha, L & 0xFF));
939 left += 1;
940 }
941
942 int rite = R >> 8;
943 int width = rite - left;
944 if (width > 0) {
945 call_hline_blitter(blitter, left, top, width, alpha);
946 }
947
948 if (R & 0xFF) {
949 blitter->blitV(rite, top, 1, InvAlphaMul(alpha, ~R & 0xFF));
950 }
951 }
952
innerstrokedot8(FDot8 L,FDot8 T,FDot8 R,FDot8 B,SkBlitter * blitter)953 static void innerstrokedot8(FDot8 L, FDot8 T, FDot8 R, FDot8 B,
954 SkBlitter* blitter) {
955 SkASSERT(L < R && T < B);
956
957 int top = T >> 8;
958 if (top == ((B - 1) >> 8)) { // just one scanline high
959 // We want the inverse of B-T, since we're the inner-stroke
960 int alpha = 256 - (B - T);
961 if (alpha) {
962 inner_scanline(L, top, R, alpha, blitter);
963 }
964 return;
965 }
966
967 if (T & 0xFF) {
968 inner_scanline(L, top, R, T & 0xFF, blitter);
969 top += 1;
970 }
971
972 int bot = B >> 8;
973 int height = bot - top;
974 if (height > 0) {
975 if (L & 0xFF) {
976 blitter->blitV(L >> 8, top, height, L & 0xFF);
977 }
978 if (R & 0xFF) {
979 blitter->blitV(R >> 8, top, height, ~R & 0xFF);
980 }
981 }
982
983 if (B & 0xFF) {
984 inner_scanline(L, bot, R, ~B & 0xFF, blitter);
985 }
986 }
987
AntiFrameRect(const SkRect & r,const SkPoint & strokeSize,const SkRegion * clip,SkBlitter * blitter)988 void SkScan::AntiFrameRect(const SkRect& r, const SkPoint& strokeSize,
989 const SkRegion* clip, SkBlitter* blitter) {
990 SkASSERT(strokeSize.fX >= 0 && strokeSize.fY >= 0);
991
992 SkScalar rx = SkScalarHalf(strokeSize.fX);
993 SkScalar ry = SkScalarHalf(strokeSize.fY);
994
995 // outset by the radius
996 FDot8 L = SkScalarToFDot8(r.fLeft - rx);
997 FDot8 T = SkScalarToFDot8(r.fTop - ry);
998 FDot8 R = SkScalarToFDot8(r.fRight + rx);
999 FDot8 B = SkScalarToFDot8(r.fBottom + ry);
1000
1001 SkIRect outer;
1002 // set outer to the outer rect of the outer section
1003 outer.set(FDot8Floor(L), FDot8Floor(T), FDot8Ceil(R), FDot8Ceil(B));
1004
1005 SkBlitterClipper clipper;
1006 if (clip) {
1007 if (clip->quickReject(outer)) {
1008 return;
1009 }
1010 if (!clip->contains(outer)) {
1011 blitter = clipper.apply(blitter, clip, &outer);
1012 }
1013 // now we can ignore clip for the rest of the function
1014 }
1015
1016 // stroke the outer hull
1017 antifilldot8(L, T, R, B, blitter, false);
1018
1019 // set outer to the outer rect of the middle section
1020 outer.set(FDot8Ceil(L), FDot8Ceil(T), FDot8Floor(R), FDot8Floor(B));
1021
1022 // in case we lost a bit with diameter/2
1023 rx = strokeSize.fX - rx;
1024 ry = strokeSize.fY - ry;
1025 // inset by the radius
1026 L = SkScalarToFDot8(r.fLeft + rx);
1027 T = SkScalarToFDot8(r.fTop + ry);
1028 R = SkScalarToFDot8(r.fRight - rx);
1029 B = SkScalarToFDot8(r.fBottom - ry);
1030
1031 if (L >= R || T >= B) {
1032 fillcheckrect(outer.fLeft, outer.fTop, outer.fRight, outer.fBottom,
1033 blitter);
1034 } else {
1035 SkIRect inner;
1036 // set inner to the inner rect of the middle section
1037 inner.set(FDot8Floor(L), FDot8Floor(T), FDot8Ceil(R), FDot8Ceil(B));
1038
1039 // draw the frame in 4 pieces
1040 fillcheckrect(outer.fLeft, outer.fTop, outer.fRight, inner.fTop,
1041 blitter);
1042 fillcheckrect(outer.fLeft, inner.fTop, inner.fLeft, inner.fBottom,
1043 blitter);
1044 fillcheckrect(inner.fRight, inner.fTop, outer.fRight, inner.fBottom,
1045 blitter);
1046 fillcheckrect(outer.fLeft, inner.fBottom, outer.fRight, outer.fBottom,
1047 blitter);
1048
1049 // now stroke the inner rect, which is similar to antifilldot8() except that
1050 // it treats the fractional coordinates with the inverse bias (since its
1051 // inner).
1052 innerstrokedot8(L, T, R, B, blitter);
1053 }
1054 }
1055
AntiFrameRect(const SkRect & r,const SkPoint & strokeSize,const SkRasterClip & clip,SkBlitter * blitter)1056 void SkScan::AntiFrameRect(const SkRect& r, const SkPoint& strokeSize,
1057 const SkRasterClip& clip, SkBlitter* blitter) {
1058 if (clip.isBW()) {
1059 AntiFrameRect(r, strokeSize, &clip.bwRgn(), blitter);
1060 } else {
1061 SkAAClipBlitterWrapper wrap(clip, blitter);
1062 AntiFrameRect(r, strokeSize, &wrap.getRgn(), wrap.getBlitter());
1063 }
1064 }
1065