• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
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     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 
97 class SkAntiHairBlitter {
98 public:
SkAntiHairBlitter()99     SkAntiHairBlitter() : fBlitter(nullptr) {}
~SkAntiHairBlitter()100     virtual ~SkAntiHairBlitter() {}
101 
getBlitter() const102     SkBlitter* getBlitter() const { return fBlitter; }
103 
setup(SkBlitter * blitter)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 
111 private:
112     SkBlitter*  fBlitter;
113 };
114 
115 class HLine_SkAntiHairBlitter : public SkAntiHairBlitter {
116 public:
drawCap(int x,SkFixed fy,SkFixed slope,int mod64)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 
drawLine(int x,int stopx,SkFixed fy,SkFixed slope)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 
161 class Horish_SkAntiHairBlitter : public SkAntiHairBlitter {
162 public:
drawCap(int x,SkFixed fy,SkFixed dy,int mod64)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 
drawLine(int x,int stopx,SkFixed fy,SkFixed dy)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 
191 class VLine_SkAntiHairBlitter : public SkAntiHairBlitter {
192 public:
drawCap(int y,SkFixed fx,SkFixed dx,int mod64)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 
drawLine(int y,int stopy,SkFixed fx,SkFixed dx)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 
232 class Vertish_SkAntiHairBlitter : public SkAntiHairBlitter {
233 public:
drawCap(int y,SkFixed fx,SkFixed dx,int mod64)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 
drawLine(int y,int stopy,SkFixed fx,SkFixed dx)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 
fastfixdiv(SkFDot6 a,SkFDot6 b)259 static 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...
bad_int(int x)269 static inline int bad_int(int x) {
270     return x & -x;
271 }
272 
any_bad_ints(int a,int b,int c,int d)273 static 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
good_int(int x)277 static inline int good_int(int x) {
278     return x ^ (1 << (SkBITCOUNT(x) - 1));
279 }
280 
any_bad_ints(int a,int b,int c,int d)281 static 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
canConvertFDot6ToFixed(SkFDot6 x)287 static 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  */
contribution_64(SkFDot6 ordinate)299 static 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 
do_anti_hairline(SkFDot6 x0,SkFDot6 y0,SkFDot6 x1,SkFDot6 y1,const SkIRect * clip,SkBlitter * blitter)312 static 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 
AntiHairLineRgn(const SkPoint array[],int arrayCount,const SkRegion * clip,SkBlitter * blitter)532 void 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 
AntiHairRect(const SkRect & rect,const SkRasterClip & clip,SkBlitter * blitter)611 void 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 
625 typedef int FDot8;  // 24.8 integer fixed point
626 
SkFixedToFDot8(SkFixed x)627 static inline FDot8 SkFixedToFDot8(SkFixed x) {
628     return (x + 0x80) >> 8;
629 }
630 
do_scanline(FDot8 L,int top,FDot8 R,U8CPU alpha,SkBlitter * blitter)631 static 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 
antifilldot8(FDot8 L,FDot8 T,FDot8 R,FDot8 B,SkBlitter * blitter,bool fillInner)657 static 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 
antifillrect(const SkXRect & xr,SkBlitter * blitter)701 static 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 
AntiFillXRect(const SkXRect & xr,const SkRegion * clip,SkBlitter * blitter)709 void 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 
AntiFillXRect(const SkXRect & xr,const SkRasterClip & clip,SkBlitter * blitter)748 void 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 */
antifillrect(const SkRect & r,SkBlitter * blitter)769 static 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 */
AntiFillRect(const SkRect & origR,const SkRegion * clip,SkBlitter * blitter)784 void 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 
AntiFillRect(const SkRect & r,const SkRasterClip & clip,SkBlitter * blitter)812 void 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
fillcheckrect(int L,int T,int R,int B,SkBlitter * blitter)827 static 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 
SkScalarToFDot8(SkScalar x)833 static inline FDot8 SkScalarToFDot8(SkScalar x) {
834     return (int)(x * 256);
835 }
836 
FDot8Floor(FDot8 x)837 static inline int FDot8Floor(FDot8 x) {
838     return x >> 8;
839 }
840 
FDot8Ceil(FDot8 x)841 static inline int FDot8Ceil(FDot8 x) {
842     return (x + 0xFF) >> 8;
843 }
844 
845 // 1 - (1 - a)*(1 - b)
InvAlphaMul(U8CPU a,U8CPU b)846 static 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 
inner_scanline(FDot8 L,int top,FDot8 R,U8CPU alpha,SkBlitter * blitter)852 static 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 
innerstrokedot8(FDot8 L,FDot8 T,FDot8 R,FDot8 B,SkBlitter * blitter)882 static 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 
align_thin_stroke(FDot8 & edge1,FDot8 & edge2)917 static 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 
AntiFrameRect(const SkRect & r,const SkPoint & strokeSize,const SkRegion * clip,SkBlitter * blitter)926 void 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 
AntiFrameRect(const SkRect & r,const SkPoint & strokeSize,const SkRasterClip & clip,SkBlitter * blitter)1006 void 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