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