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