• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* libs/graphics/sgl/SkScan_Antihair.cpp
2 **
3 ** Copyright 2006, The Android Open Source Project
4 **
5 ** Licensed under the Apache License, Version 2.0 (the "License");
6 ** you may not use this file except in compliance with the License.
7 ** You may obtain a copy of the License at
8 **
9 **     http://www.apache.org/licenses/LICENSE-2.0
10 **
11 ** Unless required by applicable law or agreed to in writing, software
12 ** distributed under the License is distributed on an "AS IS" BASIS,
13 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 ** See the License for the specific language governing permissions and
15 ** limitations under the License.
16 */
17 
18 #include "SkScan.h"
19 #include "SkBlitter.h"
20 #include "SkColorPriv.h"
21 #include "SkRegion.h"
22 #include "SkFDot6.h"
23 
24 /*  Our attempt to compute the worst case "bounds" for the horizontal and
25     vertical cases has some numerical bug in it, and we sometimes undervalue
26     our extends. The bug is that when this happens, we will set the clip to
27     NULL (for speed), and thus draw outside of the clip by a pixel, which might
28     only look bad, but it might also access memory outside of the valid range
29     allcoated for the device bitmap.
30 
31     This define enables our fix to outset our "bounds" by 1, thus avoiding the
32     chance of the bug, but at the cost of sometimes taking the rectblitter
33     case (i.e. not setting the clip to NULL) when we might not actually need
34     to. If we can improve/fix the actual calculations, then we can remove this
35     step.
36  */
37 #define OUTSET_BEFORE_CLIP_TEST     true
38 
39 #define HLINE_STACK_BUFFER      100
40 
SmallDot6Scale(int value,int dot6)41 static inline int SmallDot6Scale(int value, int dot6) {
42     SkASSERT((int16_t)value == value);
43     SkASSERT((unsigned)dot6 <= 64);
44     return SkMulS16(value, dot6) >> 6;
45 }
46 
47 //#define TEST_GAMMA
48 
49 #ifdef TEST_GAMMA
50     static uint8_t gGammaTable[256];
51     #define ApplyGamma(table, alpha)    (table)[alpha]
52 
build_gamma_table()53     static void build_gamma_table()
54     {
55         static bool gInit = false;
56 
57         if (gInit == false)
58         {
59             for (int i = 0; i < 256; i++)
60             {
61                 SkFixed n = i * 257;
62                 n += n >> 15;
63                 SkASSERT(n >= 0 && n <= SK_Fixed1);
64                 n = SkFixedSqrt(n);
65                 n = n * 255 >> 16;
66             //  SkDebugf("morph %d -> %d\n", i, n);
67                 gGammaTable[i] = SkToU8(n);
68             }
69             gInit = true;
70         }
71     }
72 #else
73     #define ApplyGamma(table, alpha)    SkToU8(alpha)
74 #endif
75 
76 ///////////////////////////////////////////////////////////////////////////////
77 
call_hline_blitter(SkBlitter * blitter,int x,int y,int count,U8CPU alpha)78 static void call_hline_blitter(SkBlitter* blitter, int x, int y, int count, U8CPU alpha)
79 {
80     SkASSERT(count > 0);
81 
82     int16_t runs[HLINE_STACK_BUFFER + 1];
83     uint8_t  aa[HLINE_STACK_BUFFER];
84 
85     aa[0] = ApplyGamma(gGammaTable, alpha);
86     do {
87         int n = count;
88         if (n > HLINE_STACK_BUFFER)
89             n = HLINE_STACK_BUFFER;
90 
91         runs[0] = SkToS16(n);
92         runs[n] = 0;
93         blitter->blitAntiH(x, y, aa, runs);
94         x += n;
95         count -= n;
96     } while (count > 0);
97 }
98 
hline(int x,int stopx,SkFixed fy,SkFixed,SkBlitter * blitter,int mod64)99 static SkFixed hline(int x, int stopx, SkFixed fy, SkFixed /*slope*/, SkBlitter* blitter, int mod64)
100 {
101     SkASSERT(x < stopx);
102     int count = stopx - x;
103     fy += SK_Fixed1/2;
104 
105     int y = fy >> 16;
106     uint8_t  a = (uint8_t)(fy >> 8);
107 
108     // lower line
109     unsigned ma = SmallDot6Scale(a, mod64);
110     if (ma) {
111         call_hline_blitter(blitter, x, y, count, ma);
112     }
113 
114     // upper line
115     ma = SmallDot6Scale(255 - a, mod64);
116     if (ma) {
117         call_hline_blitter(blitter, x, y - 1, count, ma);
118     }
119 
120     return fy - SK_Fixed1/2;
121 }
122 
horish(int x,int stopx,SkFixed fy,SkFixed dy,SkBlitter * blitter,int mod64)123 static SkFixed horish(int x, int stopx, SkFixed fy, SkFixed dy, SkBlitter* blitter, int mod64)
124 {
125     SkASSERT(x < stopx);
126 
127 #ifdef TEST_GAMMA
128     const uint8_t* gamma = gGammaTable;
129 #endif
130     int16_t runs[2];
131     uint8_t  aa[1];
132 
133     runs[0] = 1;
134     runs[1] = 0;
135 
136     fy += SK_Fixed1/2;
137     do {
138         int lower_y = fy >> 16;
139         uint8_t  a = (uint8_t)(fy >> 8);
140         unsigned ma = SmallDot6Scale(a, mod64);
141         if (ma)
142         {
143             aa[0] = ApplyGamma(gamma, ma);
144             blitter->blitAntiH(x, lower_y, aa, runs);
145             // the clipping blitters might edit runs, but should not affect us
146             SkASSERT(runs[0] == 1);
147             SkASSERT(runs[1] == 0);
148         }
149         ma = SmallDot6Scale(255 - a, mod64);
150         if (ma)
151         {
152             aa[0] = ApplyGamma(gamma, ma);
153             blitter->blitAntiH(x, lower_y - 1, aa, runs);
154             // the clipping blitters might edit runs, but should not affect us
155             SkASSERT(runs[0] == 1);
156             SkASSERT(runs[1] == 0);
157         }
158         fy += dy;
159     } while (++x < stopx);
160 
161     return fy - SK_Fixed1/2;
162 }
163 
vline(int y,int stopy,SkFixed fx,SkFixed,SkBlitter * blitter,int mod64)164 static SkFixed vline(int y, int stopy, SkFixed fx, SkFixed /*slope*/, SkBlitter* blitter, int mod64)
165 {
166     SkASSERT(y < stopy);
167     fx += SK_Fixed1/2;
168 
169     int x = fx >> 16;
170     int a = (uint8_t)(fx >> 8);
171 
172     unsigned ma = SmallDot6Scale(a, mod64);
173     if (ma)
174         blitter->blitV(x, y, stopy - y, ApplyGamma(gGammaTable, ma));
175     ma = SmallDot6Scale(255 - a, mod64);
176     if (ma)
177         blitter->blitV(x - 1, y, stopy - y, ApplyGamma(gGammaTable, ma));
178 
179     return fx - SK_Fixed1/2;
180 }
181 
vertish(int y,int stopy,SkFixed fx,SkFixed dx,SkBlitter * blitter,int mod64)182 static SkFixed vertish(int y, int stopy, SkFixed fx, SkFixed dx, SkBlitter* blitter, int mod64)
183 {
184     SkASSERT(y < stopy);
185 #ifdef TEST_GAMMA
186     const uint8_t* gamma = gGammaTable;
187 #endif
188     int16_t runs[3];
189     uint8_t  aa[2];
190 
191     runs[0] = 1;
192     runs[2] = 0;
193 
194     fx += SK_Fixed1/2;
195     do {
196         int x = fx >> 16;
197         uint8_t  a = (uint8_t)(fx >> 8);
198 
199         aa[0] = ApplyGamma(gamma, SmallDot6Scale(255 - a, mod64));
200         aa[1] = ApplyGamma(gamma, SmallDot6Scale(a, mod64));
201         // the clippng blitters might overwrite this guy, so we have to reset it each time
202         runs[1] = 1;
203         blitter->blitAntiH(x - 1, y, aa, runs);
204         // the clipping blitters might edit runs, but should not affect us
205         SkASSERT(runs[0] == 1);
206         SkASSERT(runs[2] == 0);
207         fx += dx;
208     } while (++y < stopy);
209 
210     return fx - SK_Fixed1/2;
211 }
212 
213 typedef SkFixed (*LineProc)(int istart, int istop, SkFixed fstart, SkFixed slope, SkBlitter*, int);
214 
fastfixdiv(SkFDot6 a,SkFDot6 b)215 static inline SkFixed fastfixdiv(SkFDot6 a, SkFDot6 b)
216 {
217     SkASSERT((a << 16 >> 16) == a);
218     SkASSERT(b != 0);
219     return (a << 16) / b;
220 }
221 
do_anti_hairline(SkFDot6 x0,SkFDot6 y0,SkFDot6 x1,SkFDot6 y1,const SkIRect * clip,SkBlitter * blitter)222 static void do_anti_hairline(SkFDot6 x0, SkFDot6 y0, SkFDot6 x1, SkFDot6 y1,
223                              const SkIRect* clip, SkBlitter* blitter)
224 {
225     // check that we're no larger than 511 pixels (so we can do a faster div).
226     // if we are, subdivide and call again
227 
228     if (SkAbs32(x1 - x0) > SkIntToFDot6(511) || SkAbs32(y1 - y0) > SkIntToFDot6(511))
229     {
230         /*  instead of (x0 + x1) >> 1, we shift each separately. This is less
231             precise, but avoids overflowing the intermediate result if the
232             values are huge. A better fix might be to clip the original pts
233             directly (i.e. do the divide), so we don't spend time subdividing
234             huge lines at all.
235          */
236         int hx = (x0 >> 1) + (x1 >> 1);
237         int hy = (y0 >> 1) + (y1 >> 1);
238         do_anti_hairline(x0, y0, hx, hy, clip, blitter);
239         do_anti_hairline(hx, hy, x1, y1, clip, blitter);
240         return;
241     }
242 
243     int         scaleStart, scaleStop;
244     int         istart, istop;
245     SkFixed     fstart, slope;
246     LineProc    proc;
247 
248     if (SkAbs32(x1 - x0) > SkAbs32(y1 - y0))    // mostly horizontal
249     {
250         if (x0 > x1) {    // we want to go left-to-right
251             SkTSwap<SkFDot6>(x0, x1);
252             SkTSwap<SkFDot6>(y0, y1);
253         }
254 
255         istart = SkFDot6Floor(x0);
256         istop = SkFDot6Ceil(x1);
257         fstart = SkFDot6ToFixed(y0);
258         if (y0 == y1) {   // completely horizontal, take fast case
259             slope = 0;
260             proc = hline;
261         } else {
262             slope = fastfixdiv(y1 - y0, x1 - x0);
263             SkASSERT(slope >= -SK_Fixed1 && slope <= SK_Fixed1);
264             fstart += (slope * (32 - (x0 & 63)) + 32) >> 6;
265             proc = horish;
266         }
267 
268         SkASSERT(istop > istart);
269         if (istop - istart == 1) {
270             scaleStart = x1 - x0;
271             SkASSERT(scaleStart >= 0 && scaleStart <= 64);
272             scaleStop = 0;
273         } else {
274             scaleStart = 64 - (x0 & 63);
275             scaleStop = x1 & 63;
276         }
277 
278         if (clip)
279         {
280             if (istart >= clip->fRight || istop <= clip->fLeft)
281                 return;
282             if (istart < clip->fLeft)
283             {
284                 fstart += slope * (clip->fLeft - istart);
285                 istart = clip->fLeft;
286                 scaleStart = 64;
287             }
288             if (istop > clip->fRight) {
289                 istop = clip->fRight;
290                 scaleStop = 64;
291             }
292             SkASSERT(istart <= istop);
293             if (istart == istop)
294                 return;
295 
296             // now test if our Y values are completely inside the clip
297             int top, bottom;
298             if (slope >= 0) // T2B
299             {
300                 top = SkFixedFloor(fstart - SK_FixedHalf);
301                 bottom = SkFixedCeil(fstart + (istop - istart - 1) * slope + SK_FixedHalf);
302             }
303             else            // B2T
304             {
305                 bottom = SkFixedCeil(fstart + SK_FixedHalf);
306                 top = SkFixedFloor(fstart + (istop - istart - 1) * slope - SK_FixedHalf);
307             }
308 #ifdef OUTSET_BEFORE_CLIP_TEST
309             top -= 1;
310             bottom += 1;
311 #endif
312             if (top >= clip->fBottom || bottom <= clip->fTop)
313                 return;
314             if (clip->fTop <= top && clip->fBottom >= bottom)
315                 clip = NULL;
316         }
317     }
318     else    // mostly vertical
319     {
320         if (y0 > y1)    // we want to go top-to-bottom
321         {
322             SkTSwap<SkFDot6>(x0, x1);
323             SkTSwap<SkFDot6>(y0, y1);
324         }
325 
326         istart = SkFDot6Floor(y0);
327         istop = SkFDot6Ceil(y1);
328         fstart = SkFDot6ToFixed(x0);
329         if (x0 == x1)
330         {
331             if (y0 == y1) { // are we zero length?
332                 return;     // nothing to do
333             }
334             slope = 0;
335             proc = vline;
336         }
337         else
338         {
339             slope = fastfixdiv(x1 - x0, y1 - y0);
340             SkASSERT(slope <= SK_Fixed1 && slope >= -SK_Fixed1);
341             fstart += (slope * (32 - (y0 & 63)) + 32) >> 6;
342             proc = vertish;
343         }
344 
345         SkASSERT(istop > istart);
346         if (istop - istart == 1) {
347             scaleStart = y1 - y0;
348             SkASSERT(scaleStart >= 0 && scaleStart <= 64);
349             scaleStop = 0;
350         } else {
351             scaleStart = 64 - (y0 & 63);
352             scaleStop = y1 & 63;
353         }
354 
355         if (clip)
356         {
357             if (istart >= clip->fBottom || istop <= clip->fTop)
358                 return;
359             if (istart < clip->fTop)
360             {
361                 fstart += slope * (clip->fTop - istart);
362                 istart = clip->fTop;
363                 scaleStart = 64;
364             }
365             if (istop > clip->fBottom) {
366                 istop = clip->fBottom;
367                 scaleStop = 64;
368             }
369             SkASSERT(istart <= istop);
370             if (istart == istop)
371                 return;
372 
373             // now test if our X values are completely inside the clip
374             int left, right;
375             if (slope >= 0) // L2R
376             {
377                 left = SkFixedFloor(fstart - SK_FixedHalf);
378                 right = SkFixedCeil(fstart + (istop - istart - 1) * slope + SK_FixedHalf);
379             }
380             else            // R2L
381             {
382                 right = SkFixedCeil(fstart + SK_FixedHalf);
383                 left = SkFixedFloor(fstart + (istop - istart - 1) * slope - SK_FixedHalf);
384             }
385 #ifdef OUTSET_BEFORE_CLIP_TEST
386             left -= 1;
387             right += 1;
388 #endif
389             if (left >= clip->fRight || right <= clip->fLeft)
390                 return;
391             if (clip->fLeft <= left && clip->fRight >= right)
392                 clip = NULL;
393         }
394     }
395 
396     SkRectClipBlitter   rectClipper;
397     if (clip)
398     {
399         rectClipper.init(blitter, *clip);
400         blitter = &rectClipper;
401     }
402 
403     fstart = proc(istart, istart + 1, fstart, slope, blitter, scaleStart);
404     istart += 1;
405     int fullSpans = istop - istart - (scaleStop > 0);
406     if (fullSpans > 0) {
407         fstart = proc(istart, istart + fullSpans, fstart, slope, blitter, 64);
408     }
409     if (scaleStop > 0) {
410         proc(istop - 1, istop, fstart, slope, blitter, scaleStop);
411     }
412 }
413 
AntiHairLine(const SkPoint & pt0,const SkPoint & pt1,const SkRegion * clip,SkBlitter * blitter)414 void SkScan::AntiHairLine(const SkPoint& pt0, const SkPoint& pt1,
415                           const SkRegion* clip, SkBlitter* blitter)
416 {
417     if (clip && clip->isEmpty())
418         return;
419 
420     SkASSERT(clip == NULL || !clip->getBounds().isEmpty());
421 
422 #ifdef TEST_GAMMA
423     build_gamma_table();
424 #endif
425 
426     SkFDot6 x0 = SkScalarToFDot6(pt0.fX);
427     SkFDot6 y0 = SkScalarToFDot6(pt0.fY);
428     SkFDot6 x1 = SkScalarToFDot6(pt1.fX);
429     SkFDot6 y1 = SkScalarToFDot6(pt1.fY);
430 
431     if (clip)
432     {
433         SkFDot6     left = SkMin32(x0, x1);
434         SkFDot6     top = SkMin32(y0, y1);
435         SkFDot6     right = SkMax32(x0, x1);
436         SkFDot6     bottom = SkMax32(y0, y1);
437         SkIRect     ir;
438 
439         ir.set( SkFDot6Floor(left) - 1,
440                 SkFDot6Floor(top) - 1,
441                 SkFDot6Ceil(right) + 1,
442                 SkFDot6Ceil(bottom) + 1);
443 
444         if (clip->quickReject(ir))
445             return;
446         if (!clip->quickContains(ir))
447         {
448             SkRegion::Cliperator iter(*clip, ir);
449             const SkIRect*       r = &iter.rect();
450 
451             while (!iter.done())
452             {
453                 do_anti_hairline(x0, y0, x1, y1, r, blitter);
454                 iter.next();
455             }
456             return;
457         }
458         // fall through to no-clip case
459     }
460     do_anti_hairline(x0, y0, x1, y1, NULL, blitter);
461 }
462 
AntiHairRect(const SkRect & rect,const SkRegion * clip,SkBlitter * blitter)463 void SkScan::AntiHairRect(const SkRect& rect, const SkRegion* clip, SkBlitter* blitter)
464 {
465     if (clip)
466     {
467         SkIRect ir;
468         SkRect  r = rect;
469 
470         r.inset(-SK_Scalar1/2, -SK_Scalar1/2);
471         r.roundOut(&ir);
472         if (clip->quickReject(ir))
473             return;
474         if (clip->quickContains(ir))
475             clip = NULL;
476     }
477 
478     SkPoint p0, p1;
479 
480     p0.set(rect.fLeft, rect.fTop);
481     p1.set(rect.fRight, rect.fTop);
482     SkScan::AntiHairLine(p0, p1, clip, blitter);
483     p0.set(rect.fRight, rect.fBottom);
484     SkScan::AntiHairLine(p0, p1, clip, blitter);
485     p1.set(rect.fLeft, rect.fBottom);
486     SkScan::AntiHairLine(p0, p1, clip, blitter);
487     p0.set(rect.fLeft, rect.fTop);
488     SkScan::AntiHairLine(p0, p1, clip, blitter);
489 }
490 
491 //////////////////////////////////////////////////////////////////////////////////////////
492 
493 typedef int FDot8;  // 24.8 integer fixed point
494 
SkFixedToFDot8(SkFixed x)495 static inline FDot8 SkFixedToFDot8(SkFixed x) {
496     return (x + 0x80) >> 8;
497 }
498 
do_scanline(FDot8 L,int top,FDot8 R,U8CPU alpha,SkBlitter * blitter)499 static void do_scanline(FDot8 L, int top, FDot8 R, U8CPU alpha, SkBlitter* blitter)
500 {
501     SkASSERT(L < R);
502 
503     if ((L >> 8) == ((R - 1) >> 8))  // 1x1 pixel
504     {
505         blitter->blitV(L >> 8, top, 1, SkAlphaMul(alpha, R - L));
506         return;
507     }
508 
509     int left = L >> 8;
510 
511     if (L & 0xFF)
512     {
513         blitter->blitV(left, top, 1, SkAlphaMul(alpha, 256 - (L & 0xFF)));
514         left += 1;
515     }
516 
517     int rite = R >> 8;
518     int width = rite - left;
519     if (width > 0)
520         call_hline_blitter(blitter, left, top, width, alpha);
521 
522     if (R & 0xFF)
523         blitter->blitV(rite, top, 1, SkAlphaMul(alpha, R & 0xFF));
524 }
525 
antifillrect(const SkXRect & xr,SkBlitter * blitter)526 static void antifillrect(const SkXRect& xr, SkBlitter* blitter)
527 {
528     FDot8 L = SkFixedToFDot8(xr.fLeft);
529     FDot8 T = SkFixedToFDot8(xr.fTop);
530     FDot8 R = SkFixedToFDot8(xr.fRight);
531     FDot8 B = SkFixedToFDot8(xr.fBottom);
532 
533     // check for empty now that we're in our reduced precision space
534     if (L >= R || T >= B)
535         return;
536 
537     int top = T >> 8;
538     if (top == ((B - 1) >> 8))   // just one scanline high
539     {
540         do_scanline(L, top, R, B - T - 1, blitter);
541         return;
542     }
543 
544     if (T & 0xFF)
545     {
546         do_scanline(L, top, R, 256 - (T & 0xFF), blitter);
547         top += 1;
548     }
549 
550     int bot = B >> 8;
551     int height = bot - top;
552     if (height > 0)
553     {
554         int left = L >> 8;
555         if (L & 0xFF)
556         {
557             blitter->blitV(left, top, height, 256 - (L & 0xFF));
558             left += 1;
559         }
560         int rite = R >> 8;
561         int width = rite - left;
562         if (width > 0)
563             blitter->blitRect(left, top, width, height);
564         if (R & 0xFF)
565             blitter->blitV(rite, top, height, R & 0xFF);
566     }
567 
568     if (B & 0xFF)
569         do_scanline(L, bot, R, B & 0xFF, blitter);
570 }
571 
572 ///////////////////////////////////////////////////////////////////////////////
573 
AntiFillXRect(const SkXRect & xr,const SkRegion * clip,SkBlitter * blitter)574 void SkScan::AntiFillXRect(const SkXRect& xr, const SkRegion* clip,
575                           SkBlitter* blitter) {
576     if (clip) {
577         SkIRect outerBounds;
578         XRect_roundOut(xr, &outerBounds);
579 
580         if (clip->isRect()) {
581             const SkIRect& clipBounds = clip->getBounds();
582 
583             if (clipBounds.contains(outerBounds)) {
584                 antifillrect(xr, blitter);
585             } else {
586                 SkXRect tmpR;
587                 // this keeps our original edges fractional
588                 XRect_set(&tmpR, clipBounds);
589                 if (tmpR.intersect(xr)) {
590                     antifillrect(tmpR, blitter);
591                 }
592             }
593         } else {
594             SkRegion::Cliperator clipper(*clip, outerBounds);
595             const SkIRect&       rr = clipper.rect();
596 
597             while (!clipper.done()) {
598                 SkXRect  tmpR;
599 
600                 // this keeps our original edges fractional
601                 XRect_set(&tmpR, rr);
602                 if (tmpR.intersect(xr)) {
603                     antifillrect(tmpR, blitter);
604                 }
605                 clipper.next();
606             }
607         }
608     } else {
609         antifillrect(xr, blitter);
610     }
611 }
612 
613 #ifdef SK_SCALAR_IS_FLOAT
614 
615 /*  This guy takes a float-rect, but with the key improvement that it has
616     already been clipped, so we know that it is safe to convert it into a
617     XRect (fixedpoint), as it won't overflow.
618 */
antifillrect(const SkRect & r,SkBlitter * blitter)619 static void antifillrect(const SkRect& r, SkBlitter* blitter) {
620     SkXRect xr;
621 
622     XRect_set(&xr, r);
623     antifillrect(xr, blitter);
624 }
625 
626 /*  We repeat the clipping logic of AntiFillXRect because the float rect might
627     overflow if we blindly converted it to an XRect. This sucks that we have to
628     repeat the clipping logic, but I don't see how to share the code/logic.
629 
630     We clip r (as needed) into one or more (smaller) float rects, and then pass
631     those to our version of antifillrect, which converts it into an XRect and
632     then calls the blit.
633 */
AntiFillRect(const SkRect & r,const SkRegion * clip,SkBlitter * blitter)634 void SkScan::AntiFillRect(const SkRect& r, const SkRegion* clip,
635                           SkBlitter* blitter) {
636     if (clip) {
637         SkIRect outerBounds;
638         r.roundOut(&outerBounds);
639 
640         if (clip->isRect()) {
641             const SkIRect& clipBounds = clip->getBounds();
642 
643             if (clipBounds.contains(outerBounds)) {
644                 antifillrect(r, blitter);
645             } else {
646                 SkRect tmpR;
647                 // this keeps our original edges fractional
648                 tmpR.set(clipBounds);
649                 if (tmpR.intersect(r)) {
650                     antifillrect(tmpR, blitter);
651                 }
652             }
653         } else {
654             SkRegion::Cliperator clipper(*clip, outerBounds);
655             const SkIRect&       rr = clipper.rect();
656 
657             while (!clipper.done()) {
658                 SkRect  tmpR;
659                 // this keeps our original edges fractional
660                 tmpR.set(rr);
661                 if (tmpR.intersect(r)) {
662                     antifillrect(tmpR, blitter);
663                 }
664                 clipper.next();
665             }
666         }
667     } else {
668         antifillrect(r, blitter);
669     }
670 }
671 
672 #endif
673 
674 
675