• 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 "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