• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* libs/graphics/sgl/SkScan_Path.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 "SkScanPriv.h"
19 #include "SkBlitter.h"
20 #include "SkEdge.h"
21 #include "SkGeometry.h"
22 #include "SkPath.h"
23 #include "SkQuadClipper.h"
24 #include "SkRegion.h"
25 #include "SkTemplates.h"
26 
27 #define kEDGE_HEAD_Y    SK_MinS32
28 #define kEDGE_TAIL_Y    SK_MaxS32
29 
30 #ifdef SK_DEBUG
validate_sort(const SkEdge * edge)31     static void validate_sort(const SkEdge* edge)
32     {
33         int y = kEDGE_HEAD_Y;
34 
35         while (edge->fFirstY != SK_MaxS32)
36         {
37             edge->validate();
38             SkASSERT(y <= edge->fFirstY);
39 
40             y = edge->fFirstY;
41             edge = edge->fNext;
42         }
43     }
44 #else
45     #define validate_sort(edge)
46 #endif
47 
remove_edge(SkEdge * edge)48 static inline void remove_edge(SkEdge* edge)
49 {
50     edge->fPrev->fNext = edge->fNext;
51     edge->fNext->fPrev = edge->fPrev;
52 }
53 
swap_edges(SkEdge * prev,SkEdge * next)54 static inline void swap_edges(SkEdge* prev, SkEdge* next)
55 {
56     SkASSERT(prev->fNext == next && next->fPrev == prev);
57 
58     // remove prev from the list
59     prev->fPrev->fNext = next;
60     next->fPrev = prev->fPrev;
61 
62     // insert prev after next
63     prev->fNext = next->fNext;
64     next->fNext->fPrev = prev;
65     next->fNext = prev;
66     prev->fPrev = next;
67 }
68 
backward_insert_edge_based_on_x(SkEdge * edge SkDECLAREPARAM (int,curr_y))69 static void backward_insert_edge_based_on_x(SkEdge* edge SkDECLAREPARAM(int, curr_y))
70 {
71     SkFixed x = edge->fX;
72 
73     for (;;)
74     {
75         SkEdge* prev = edge->fPrev;
76 
77         // add 1 to curr_y since we may have added new edges (built from curves)
78         // that start on the next scanline
79         SkASSERT(prev && prev->fFirstY <= curr_y + 1);
80 
81         if (prev->fX <= x)
82             break;
83 
84         swap_edges(prev, edge);
85     }
86 }
87 
insert_new_edges(SkEdge * newEdge,int curr_y)88 static void insert_new_edges(SkEdge* newEdge, int curr_y)
89 {
90     SkASSERT(newEdge->fFirstY >= curr_y);
91 
92     while (newEdge->fFirstY == curr_y)
93     {
94         SkEdge* next = newEdge->fNext;
95         backward_insert_edge_based_on_x(newEdge  SkPARAM(curr_y));
96         newEdge = next;
97     }
98 }
99 
100 #ifdef SK_DEBUG
validate_edges_for_y(const SkEdge * edge,int curr_y)101 static void validate_edges_for_y(const SkEdge* edge, int curr_y)
102 {
103     while (edge->fFirstY <= curr_y)
104     {
105         SkASSERT(edge->fPrev && edge->fNext);
106         SkASSERT(edge->fPrev->fNext == edge);
107         SkASSERT(edge->fNext->fPrev == edge);
108         SkASSERT(edge->fFirstY <= edge->fLastY);
109 
110         SkASSERT(edge->fPrev->fX <= edge->fX);
111         edge = edge->fNext;
112     }
113 }
114 #else
115     #define validate_edges_for_y(edge, curr_y)
116 #endif
117 
118 #if defined _WIN32 && _MSC_VER >= 1300  // disable warning : local variable used without having been initialized
119 #pragma warning ( push )
120 #pragma warning ( disable : 4701 )
121 #endif
122 
123 typedef void (*PrePostProc)(SkBlitter* blitter, int y, bool isStartOfScanline);
124 #define PREPOST_START   true
125 #define PREPOST_END     false
126 
walk_edges(SkEdge * prevHead,SkPath::FillType fillType,SkBlitter * blitter,int stop_y,PrePostProc proc)127 static void walk_edges(SkEdge* prevHead, SkPath::FillType fillType,
128                        SkBlitter* blitter, int stop_y, PrePostProc proc)
129 {
130     validate_sort(prevHead->fNext);
131 
132     int curr_y = prevHead->fNext->fFirstY;
133     // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
134     int windingMask = (fillType & 1) ? 1 : -1;
135 
136     for (;;)
137     {
138         int     w = 0;
139         int     left SK_INIT_TO_AVOID_WARNING;
140         bool    in_interval = false;
141         SkEdge* currE = prevHead->fNext;
142         SkFixed prevX = prevHead->fX;
143 
144         validate_edges_for_y(currE, curr_y);
145 
146         if (proc) {
147             proc(blitter, curr_y, PREPOST_START);    // pre-proc
148         }
149 
150         while (currE->fFirstY <= curr_y)
151         {
152             SkASSERT(currE->fLastY >= curr_y);
153 
154             int x = (currE->fX + SK_Fixed1/2) >> 16;
155             w += currE->fWinding;
156             if ((w & windingMask) == 0) // we finished an interval
157             {
158                 SkASSERT(in_interval);
159                 int width = x - left;
160                 SkASSERT(width >= 0);
161                 if (width)
162                     blitter->blitH(left, curr_y, width);
163                 in_interval = false;
164             }
165             else if (!in_interval)
166             {
167                 left = x;
168                 in_interval = true;
169             }
170 
171             SkEdge* next = currE->fNext;
172             SkFixed newX;
173 
174             if (currE->fLastY == curr_y)    // are we done with this edge?
175             {
176                 if (currE->fCurveCount < 0)
177                 {
178                     if (((SkCubicEdge*)currE)->updateCubic())
179                     {
180                         SkASSERT(currE->fFirstY == curr_y + 1);
181 
182                         newX = currE->fX;
183                         goto NEXT_X;
184                     }
185                 }
186                 else if (currE->fCurveCount > 0)
187                 {
188                     if (((SkQuadraticEdge*)currE)->updateQuadratic())
189                     {
190                         newX = currE->fX;
191                         goto NEXT_X;
192                     }
193                 }
194                 remove_edge(currE);
195             }
196             else
197             {
198                 SkASSERT(currE->fLastY > curr_y);
199                 newX = currE->fX + currE->fDX;
200                 currE->fX = newX;
201             NEXT_X:
202                 if (newX < prevX)   // ripple currE backwards until it is x-sorted
203                     backward_insert_edge_based_on_x(currE  SkPARAM(curr_y));
204                 else
205                     prevX = newX;
206             }
207             currE = next;
208             SkASSERT(currE);
209         }
210 
211         if (proc) {
212             proc(blitter, curr_y, PREPOST_END);    // post-proc
213         }
214 
215         curr_y += 1;
216         if (curr_y >= stop_y)
217             break;
218 
219         // now currE points to the first edge with a Yint larger than curr_y
220         insert_new_edges(currE, curr_y);
221     }
222 }
223 
224 ///////////////////////////////////////////////////////////////////////////////
225 
226 // this guy overrides blitH, and will call its proxy blitter with the inverse
227 // of the spans it is given (clipped to the left/right of the cliprect)
228 //
229 // used to implement inverse filltypes on paths
230 //
231 class InverseBlitter : public SkBlitter {
232 public:
setBlitter(SkBlitter * blitter,const SkIRect & clip,int shift)233     void setBlitter(SkBlitter* blitter, const SkIRect& clip, int shift) {
234         fBlitter = blitter;
235         fFirstX = clip.fLeft << shift;
236         fLastX = clip.fRight << shift;
237     }
prepost(int y,bool isStart)238     void prepost(int y, bool isStart) {
239         if (isStart) {
240             fPrevX = fFirstX;
241         } else {
242             int invWidth = fLastX - fPrevX;
243             if (invWidth > 0) {
244                 fBlitter->blitH(fPrevX, y, invWidth);
245             }
246         }
247     }
248 
249     // overrides
blitH(int x,int y,int width)250     virtual void blitH(int x, int y, int width) {
251         int invWidth = x - fPrevX;
252         if (invWidth > 0) {
253             fBlitter->blitH(fPrevX, y, invWidth);
254         }
255         fPrevX = x + width;
256     }
257 
258     // we do not expect to get called with these entrypoints
blitAntiH(int,int,const SkAlpha[],const int16_t runs[])259     virtual void blitAntiH(int, int, const SkAlpha[], const int16_t runs[]) {
260         SkASSERT(!"blitAntiH unexpected");
261     }
blitV(int x,int y,int height,SkAlpha alpha)262     virtual void blitV(int x, int y, int height, SkAlpha alpha) {
263         SkASSERT(!"blitV unexpected");
264     }
blitRect(int x,int y,int width,int height)265     virtual void blitRect(int x, int y, int width, int height) {
266         SkASSERT(!"blitRect unexpected");
267     }
blitMask(const SkMask &,const SkIRect & clip)268     virtual void blitMask(const SkMask&, const SkIRect& clip) {
269         SkASSERT(!"blitMask unexpected");
270     }
justAnOpaqueColor(uint32_t * value)271     virtual const SkBitmap* justAnOpaqueColor(uint32_t* value) {
272         SkASSERT(!"justAnOpaqueColor unexpected");
273         return NULL;
274     }
275 
276 private:
277     SkBlitter*  fBlitter;
278     int         fFirstX, fLastX, fPrevX;
279 };
280 
PrePostInverseBlitterProc(SkBlitter * blitter,int y,bool isStart)281 static void PrePostInverseBlitterProc(SkBlitter* blitter, int y, bool isStart) {
282     ((InverseBlitter*)blitter)->prepost(y, isStart);
283 }
284 
285 ///////////////////////////////////////////////////////////////////////////////
286 
287 #if defined _WIN32 && _MSC_VER >= 1300
288 #pragma warning ( pop )
289 #endif
290 
291 /*  Our line edge relies on the maximum span being <= 512, so that it can
292     use FDot6 and keep the dx,dy in 16bits (for much faster slope divide).
293     This function returns true if the specified line is too big.
294 */
line_too_big(const SkPoint pts[2])295 static inline bool line_too_big(const SkPoint pts[2])
296 {
297     SkScalar dx = pts[1].fX - pts[0].fX;
298     SkScalar dy = pts[1].fY - pts[0].fY;
299 
300     return  SkScalarAbs(dx) > SkIntToScalar(511) ||
301             SkScalarAbs(dy) > SkIntToScalar(511);
302 }
303 
build_edges(SkEdge edge[],const SkPath & path,const SkIRect * clipRect,SkEdge * list[],int shiftUp)304 static int build_edges(SkEdge edge[], const SkPath& path,
305                        const SkIRect* clipRect, SkEdge* list[], int shiftUp) {
306     SkEdge**        start = list;
307     SkPath::Iter    iter(path, true);
308     SkPoint         pts[4];
309     SkPath::Verb    verb;
310 
311     SkQuadClipper qclipper;
312     if (clipRect) {
313         SkIRect r;
314         r.set(clipRect->fLeft >> shiftUp, clipRect->fTop >> shiftUp,
315               clipRect->fRight >> shiftUp, clipRect->fBottom >> shiftUp);
316         qclipper.setClip(r);
317     }
318 
319     while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
320         switch (verb) {
321             case SkPath::kLine_Verb:
322                 if (edge->setLine(pts[0], pts[1], clipRect, shiftUp)) {
323                     *list++ = edge;
324                     edge = (SkEdge*)((char*)edge + sizeof(SkEdge));
325                 }
326                 break;
327             case SkPath::kQuad_Verb: {
328                 SkPoint tmp[5], clippedPts[3];
329                 SkPoint* p = tmp;
330                 int     count = SkChopQuadAtYExtrema(pts, tmp);
331 
332                 do {
333                     const SkPoint* qpts = p;
334                     if (clipRect) {
335                         if (!qclipper.clipQuad(p, clippedPts)) {
336                             goto NEXT_CHOPPED_QUAD;
337                         }
338                         qpts = clippedPts;
339                     }
340                     if (((SkQuadraticEdge*)edge)->setQuadratic(qpts, shiftUp)) {
341                         *list++ = edge;
342                         edge = (SkEdge*)((char*)edge + sizeof(SkQuadraticEdge));
343                     }
344                 NEXT_CHOPPED_QUAD:
345                     p += 2;
346                 } while (--count >= 0);
347                 break;
348             }
349             case SkPath::kCubic_Verb: {
350                 SkPoint tmp[10];
351                 SkPoint* p = tmp;
352                 int     count = SkChopCubicAtYExtrema(pts, tmp);
353                 SkASSERT(count >= 0 && count <= 2);
354 
355                 do {
356                     if (((SkCubicEdge*)edge)->setCubic(p, clipRect, shiftUp))
357                     {
358                         *list++ = edge;
359                         edge = (SkEdge*)((char*)edge + sizeof(SkCubicEdge));
360                     }
361                     p += 3;
362                 } while (--count >= 0);
363                 break;
364             }
365         default:
366             break;
367         }
368     }
369     return (int)(list - start);
370 }
371 
372 extern "C" {
edge_compare(const void * a,const void * b)373     static int edge_compare(const void* a, const void* b)
374     {
375         const SkEdge* edgea = *(const SkEdge**)a;
376         const SkEdge* edgeb = *(const SkEdge**)b;
377 
378         int valuea = edgea->fFirstY;
379         int valueb = edgeb->fFirstY;
380 
381         if (valuea == valueb)
382         {
383             valuea = edgea->fX;
384             valueb = edgeb->fX;
385         }
386 
387         // this overflows if valuea >>> valueb or vice-versa
388         //     return valuea - valueb;
389         // do perform the slower but safe compares
390         return (valuea < valueb) ? -1 : (valuea > valueb);
391     }
392 }
393 
sort_edges(SkEdge * list[],int count,SkEdge ** last)394 static SkEdge* sort_edges(SkEdge* list[], int count, SkEdge** last)
395 {
396     qsort(list, count, sizeof(SkEdge*), edge_compare);
397 
398     // now make the edges linked in sorted order
399     for (int i = 1; i < count; i++)
400     {
401         list[i - 1]->fNext = list[i];
402         list[i]->fPrev = list[i - 1];
403     }
404 
405     *last = list[count - 1];
406     return list[0];
407 }
408 
409 #ifdef SK_DEBUG
410 /* 'quick' computation of the max sized needed to allocated for
411     our edgelist.
412 */
worst_case_edge_count(const SkPath & path,size_t * storage)413 static int worst_case_edge_count(const SkPath& path, size_t* storage)
414 {
415     size_t  size = 0;
416     int     edgeCount = 0;
417 
418     SkPath::Iter    iter(path, true);
419     SkPath::Verb    verb;
420 
421     while ((verb = iter.next(NULL)) != SkPath::kDone_Verb)
422     {
423         switch (verb) {
424         case SkPath::kLine_Verb:
425             edgeCount += 1;
426             size += sizeof(SkQuadraticEdge);    // treat line like Quad (in case its > 512)
427             break;
428         case SkPath::kQuad_Verb:
429             edgeCount += 2;                     // might need 2 edges when we chop on Y extrema
430             size += 2 * sizeof(SkQuadraticEdge);
431             break;
432         case SkPath::kCubic_Verb:
433             edgeCount += 3;                     // might need 3 edges when we chop on Y extrema
434             size += 3 * sizeof(SkCubicEdge);
435             break;
436         default:
437             break;
438         }
439     }
440 
441     SkASSERT(storage);
442     *storage = size;
443     return edgeCount;
444 }
445 #endif
446 
447 /* Much faster than worst_case_edge_count, but over estimates even more
448 */
cheap_worst_case_edge_count(const SkPath & path,size_t * storage)449 static int cheap_worst_case_edge_count(const SkPath& path, size_t* storage)
450 {
451     int ptCount = path.getPoints(NULL, 0);
452     int edgeCount = ptCount;
453     *storage = edgeCount * sizeof(SkCubicEdge);
454     return edgeCount;
455 }
456 
457 // clipRect may be null, even though we always have a clip. This indicates that
458 // the path is contained in the clip, and so we can ignore it during the blit
459 //
460 // clipRect (if no null) has already been shifted up
461 //
sk_fill_path(const SkPath & path,const SkIRect * clipRect,SkBlitter * blitter,int stop_y,int shiftEdgesUp,const SkRegion & clipRgn)462 void sk_fill_path(const SkPath& path, const SkIRect* clipRect, SkBlitter* blitter,
463                   int stop_y, int shiftEdgesUp, const SkRegion& clipRgn)
464 {
465     SkASSERT(&path && blitter);
466 
467     size_t  size;
468     int     maxCount = cheap_worst_case_edge_count(path, &size);
469 
470 #ifdef SK_DEBUG
471     {
472         size_t  size2;
473         int     maxCount2 = worst_case_edge_count(path, &size2);
474 
475         SkASSERT(maxCount >= maxCount2 && size >= size2);
476     }
477 #endif
478 
479     SkAutoMalloc    memory(maxCount * sizeof(SkEdge*) + size);
480     SkEdge**        list = (SkEdge**)memory.get();
481     SkEdge*         edge = (SkEdge*)(list + maxCount);
482     int             count = build_edges(edge, path, clipRect, list, shiftEdgesUp);
483     SkEdge          headEdge, tailEdge, *last;
484 
485     SkASSERT(count <= maxCount);
486     if (count < 2) {
487         return;
488     }
489 
490     // this returns the first and last edge after they're sorted into a dlink list
491     edge = sort_edges(list, count, &last);
492 
493     headEdge.fPrev = NULL;
494     headEdge.fNext = edge;
495     headEdge.fFirstY = kEDGE_HEAD_Y;
496     headEdge.fX = SK_MinS32;
497     edge->fPrev = &headEdge;
498 
499     tailEdge.fPrev = last;
500     tailEdge.fNext = NULL;
501     tailEdge.fFirstY = kEDGE_TAIL_Y;
502     last->fNext = &tailEdge;
503 
504     // now edge is the head of the sorted linklist
505 
506     stop_y <<= shiftEdgesUp;
507     if (clipRect && stop_y > clipRect->fBottom) {
508         stop_y = clipRect->fBottom;
509     }
510 
511     InverseBlitter  ib;
512     PrePostProc     proc = NULL;
513 
514     if (path.isInverseFillType()) {
515         ib.setBlitter(blitter, clipRgn.getBounds(), shiftEdgesUp);
516         blitter = &ib;
517         proc = PrePostInverseBlitterProc;
518     }
519 
520     walk_edges(&headEdge, path.getFillType(), blitter, stop_y, proc);
521 }
522 
sk_blit_above_and_below(SkBlitter * blitter,const SkIRect & ir,const SkRegion & clip)523 void sk_blit_above_and_below(SkBlitter* blitter, const SkIRect& ir,
524                              const SkRegion& clip) {
525     const SkIRect& cr = clip.getBounds();
526     SkIRect tmp;
527 
528     tmp.fLeft = cr.fLeft;
529     tmp.fRight = cr.fRight;
530 
531     tmp.fTop = cr.fTop;
532     tmp.fBottom = ir.fTop;
533     if (!tmp.isEmpty()) {
534         blitter->blitRectRegion(tmp, clip);
535     }
536 
537     tmp.fTop = ir.fBottom;
538     tmp.fBottom = cr.fBottom;
539     if (!tmp.isEmpty()) {
540         blitter->blitRectRegion(tmp, clip);
541     }
542 }
543 
544 /////////////////////////////////////////////////////////////////////////////////////
545 
SkScanClipper(SkBlitter * blitter,const SkRegion * clip,const SkIRect & ir)546 SkScanClipper::SkScanClipper(SkBlitter* blitter, const SkRegion* clip, const SkIRect& ir)
547 {
548     fBlitter = NULL;     // null means blit nothing
549     fClipRect = NULL;
550 
551     if (clip)
552     {
553         fClipRect = &clip->getBounds();
554         if (!SkIRect::Intersects(*fClipRect, ir))  // completely clipped out
555             return;
556 
557         if (clip->isRect())
558         {
559             if (fClipRect->contains(ir))
560                 fClipRect = NULL;
561             else
562             {
563                 // only need a wrapper blitter if we're horizontally clipped
564                 if (fClipRect->fLeft > ir.fLeft || fClipRect->fRight < ir.fRight)
565                 {
566                     fRectBlitter.init(blitter, *fClipRect);
567                     blitter = &fRectBlitter;
568                 }
569             }
570         }
571         else
572         {
573             fRgnBlitter.init(blitter, clip);
574             blitter = &fRgnBlitter;
575         }
576     }
577     fBlitter = blitter;
578 }
579 
580 ///////////////////////////////////////////////////////////////////////////////
581 
FillPath(const SkPath & path,const SkRegion & clip,SkBlitter * blitter)582 void SkScan::FillPath(const SkPath& path, const SkRegion& clip,
583                       SkBlitter* blitter) {
584     if (clip.isEmpty()) {
585         return;
586     }
587 
588     SkIRect ir;
589     path.getBounds().round(&ir);
590     if (ir.isEmpty()) {
591         if (path.isInverseFillType()) {
592             blitter->blitRegion(clip);
593         }
594         return;
595     }
596 
597     SkScanClipper   clipper(blitter, &clip, ir);
598 
599     blitter = clipper.getBlitter();
600     if (blitter) {
601         if (path.isInverseFillType()) {
602             sk_blit_above_and_below(blitter, ir, clip);
603         }
604         sk_fill_path(path, clipper.getClipRect(), blitter, ir.fBottom, 0, clip);
605     } else {
606         // what does it mean to not have a blitter if path.isInverseFillType???
607     }
608 }
609 
610 ///////////////////////////////////////////////////////////////////////////////
611 
build_tri_edges(SkEdge edge[],const SkPoint pts[],const SkIRect * clipRect,SkEdge * list[])612 static int build_tri_edges(SkEdge edge[], const SkPoint pts[],
613                            const SkIRect* clipRect, SkEdge* list[]) {
614     SkEdge** start = list;
615 
616     if (edge->setLine(pts[0], pts[1], clipRect, 0)) {
617         *list++ = edge;
618         edge = (SkEdge*)((char*)edge + sizeof(SkEdge));
619     }
620     if (edge->setLine(pts[1], pts[2], clipRect, 0)) {
621         *list++ = edge;
622         edge = (SkEdge*)((char*)edge + sizeof(SkEdge));
623     }
624     if (edge->setLine(pts[2], pts[0], clipRect, 0)) {
625         *list++ = edge;
626     }
627     return (int)(list - start);
628 }
629 
630 
sk_fill_triangle(const SkPoint pts[],const SkIRect * clipRect,SkBlitter * blitter,const SkIRect & ir)631 void sk_fill_triangle(const SkPoint pts[], const SkIRect* clipRect,
632                       SkBlitter* blitter, const SkIRect& ir) {
633     SkASSERT(pts && blitter);
634 
635     SkEdge edgeStorage[3];
636     SkEdge* list[3];
637 
638     int count = build_tri_edges(edgeStorage, pts, clipRect, list);
639     if (count < 2) {
640         return;
641     }
642 
643     SkEdge headEdge, tailEdge, *last;
644 
645     // this returns the first and last edge after they're sorted into a dlink list
646     SkEdge* edge = sort_edges(list, count, &last);
647 
648     headEdge.fPrev = NULL;
649     headEdge.fNext = edge;
650     headEdge.fFirstY = kEDGE_HEAD_Y;
651     headEdge.fX = SK_MinS32;
652     edge->fPrev = &headEdge;
653 
654     tailEdge.fPrev = last;
655     tailEdge.fNext = NULL;
656     tailEdge.fFirstY = kEDGE_TAIL_Y;
657     last->fNext = &tailEdge;
658 
659     // now edge is the head of the sorted linklist
660     int stop_y = ir.fBottom;
661     if (clipRect && stop_y > clipRect->fBottom) {
662         stop_y = clipRect->fBottom;
663     }
664     walk_edges(&headEdge, SkPath::kEvenOdd_FillType, blitter, stop_y, NULL);
665 }
666 
FillTriangle(const SkPoint pts[],const SkRegion * clip,SkBlitter * blitter)667 void SkScan::FillTriangle(const SkPoint pts[], const SkRegion* clip,
668                           SkBlitter* blitter) {
669     if (clip && clip->isEmpty()) {
670         return;
671     }
672 
673     SkRect  r;
674     SkIRect ir;
675     r.set(pts, 3);
676     r.round(&ir);
677     if (ir.isEmpty()) {
678         return;
679     }
680 
681     SkScanClipper   clipper(blitter, clip, ir);
682 
683     blitter = clipper.getBlitter();
684     if (NULL != blitter) {
685         sk_fill_triangle(pts, clipper.getClipRect(), blitter, ir);
686     }
687 }
688 
689