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