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