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