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