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