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