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