• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2023 Google LLC
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "include/core/SkMatrix.h"
9 #include "include/core/SkPaint.h"
10 #include "include/core/SkPath.h"
11 #include "include/core/SkPathEffect.h"
12 #include "include/core/SkPathTypes.h"
13 #include "include/core/SkPathUtils.h"
14 #include "include/core/SkPixmap.h"
15 #include "include/core/SkPoint.h"
16 #include "include/core/SkRRect.h"
17 #include "include/core/SkRect.h"
18 #include "include/core/SkScalar.h"
19 #include "include/core/SkStrokeRec.h"
20 #include "include/private/base/SkAssert.h"
21 #include "include/private/base/SkCPUTypes.h"
22 #include "include/private/base/SkDebug.h"
23 #include "include/private/base/SkFloatingPoint.h"
24 #include "include/private/base/SkTemplates.h"
25 #include "src/base/SkTLazy.h"
26 #include "src/base/SkZip.h"
27 #include "src/core/SkAutoBlitterChoose.h"
28 #include "src/core/SkBlendModePriv.h"
29 #include "src/core/SkBlitter_A8.h"
30 #include "src/core/SkDevice.h"
31 #include "src/core/SkDrawBase.h"
32 #include "src/core/SkDrawProcs.h"
33 #include "src/core/SkMask.h"
34 #include "src/core/SkMaskFilterBase.h"
35 #include "src/core/SkPathEffectBase.h"
36 #include "src/core/SkPathPriv.h"
37 #include "src/core/SkRasterClip.h"
38 #include "src/core/SkRectPriv.h"
39 #include "src/core/SkScan.h"
40 
41 #include <algorithm>
42 #include <cstddef>
43 #include <optional>
44 
45 class SkBitmap;
46 class SkBlitter;
47 class SkGlyph;
48 class SkMaskFilter;
49 
50 using namespace skia_private;
51 
52 ///////////////////////////////////////////////////////////////////////////////
53 
SkDrawBase()54 SkDrawBase::SkDrawBase() {}
55 
computeConservativeLocalClipBounds(SkRect * localBounds) const56 bool SkDrawBase::computeConservativeLocalClipBounds(SkRect* localBounds) const {
57     if (fRC->isEmpty()) {
58         return false;
59     }
60 
61     SkMatrix inverse;
62     if (!fCTM->invert(&inverse)) {
63         return false;
64     }
65 
66     SkIRect devBounds = fRC->getBounds();
67     // outset to have slop for antialasing and hairlines
68     devBounds.outset(1, 1);
69     inverse.mapRect(localBounds, SkRect::Make(devBounds));
70     return true;
71 }
72 
73 ///////////////////////////////////////////////////////////////////////////////
74 
drawPaint(const SkPaint & paint) const75 void SkDrawBase::drawPaint(const SkPaint& paint) const {
76     SkDEBUGCODE(this->validate();)
77 
78     if (fRC->isEmpty()) {
79         return;
80     }
81 
82     SkIRect    devRect;
83     devRect.setWH(fDst.width(), fDst.height());
84 
85     SkAutoBlitterChoose blitter(*this, nullptr, paint);
86     SkScan::FillIRect(devRect, *fRC, blitter.get());
87 }
88 
89 ///////////////////////////////////////////////////////////////////////////////
90 
compute_stroke_size(const SkPaint & paint,const SkMatrix & matrix)91 static inline SkPoint compute_stroke_size(const SkPaint& paint, const SkMatrix& matrix) {
92     SkASSERT(matrix.rectStaysRect());
93     SkASSERT(SkPaint::kFill_Style != paint.getStyle());
94 
95     SkVector size;
96     SkPoint pt = { paint.getStrokeWidth(), paint.getStrokeWidth() };
97     matrix.mapVectors(&size, &pt, 1);
98     return SkPoint::Make(SkScalarAbs(size.fX), SkScalarAbs(size.fY));
99 }
100 
easy_rect_join(const SkRect & rect,const SkPaint & paint,const SkMatrix & matrix,SkPoint * strokeSize)101 static bool easy_rect_join(const SkRect& rect, const SkPaint& paint, const SkMatrix& matrix,
102                            SkPoint* strokeSize) {
103     if (rect.isEmpty() || SkPaint::kMiter_Join != paint.getStrokeJoin() ||
104         paint.getStrokeMiter() < SK_ScalarSqrt2) {
105         return false;
106     }
107 
108     *strokeSize = compute_stroke_size(paint, matrix);
109     return true;
110 }
111 
ComputeRectType(const SkRect & rect,const SkPaint & paint,const SkMatrix & matrix,SkPoint * strokeSize)112 SkDrawBase::RectType SkDrawBase::ComputeRectType(const SkRect& rect,
113                                          const SkPaint& paint,
114                                          const SkMatrix& matrix,
115                                          SkPoint* strokeSize) {
116     RectType rtype;
117     const SkScalar width = paint.getStrokeWidth();
118     const bool zeroWidth = (0 == width);
119     SkPaint::Style style = paint.getStyle();
120 
121     if ((SkPaint::kStrokeAndFill_Style == style) && zeroWidth) {
122         style = SkPaint::kFill_Style;
123     }
124 
125     if (paint.getPathEffect() || paint.getMaskFilter() ||
126         !matrix.rectStaysRect() || SkPaint::kStrokeAndFill_Style == style) {
127         rtype = kPath_RectType;
128     } else if (SkPaint::kFill_Style == style) {
129         rtype = kFill_RectType;
130     } else if (zeroWidth) {
131         rtype = kHair_RectType;
132     } else if (easy_rect_join(rect, paint, matrix, strokeSize)) {
133         rtype = kStroke_RectType;
134     } else {
135         rtype = kPath_RectType;
136     }
137     return rtype;
138 }
139 
rect_points(const SkRect & r)140 static const SkPoint* rect_points(const SkRect& r) {
141     return reinterpret_cast<const SkPoint*>(&r);
142 }
143 
rect_points(SkRect & r)144 static SkPoint* rect_points(SkRect& r) {
145     return reinterpret_cast<SkPoint*>(&r);
146 }
147 
draw_rect_as_path(const SkDrawBase & orig,const SkRect & prePaintRect,const SkPaint & paint,const SkMatrix & ctm)148 static void draw_rect_as_path(const SkDrawBase& orig,
149                               const SkRect& prePaintRect,
150                               const SkPaint& paint,
151                               const SkMatrix& ctm) {
152     SkDrawBase draw(orig);
153     draw.fCTM = &ctm;
154     SkPath  tmp;
155     tmp.addRect(prePaintRect);
156     tmp.setFillType(SkPathFillType::kWinding);
157     draw.drawPath(tmp, paint, nullptr, true);
158 }
159 
drawRect(const SkRect & prePaintRect,const SkPaint & paint,const SkMatrix * paintMatrix,const SkRect * postPaintRect) const160 void SkDrawBase::drawRect(const SkRect& prePaintRect, const SkPaint& paint,
161                       const SkMatrix* paintMatrix, const SkRect* postPaintRect) const {
162     SkDEBUGCODE(this->validate();)
163 
164     // nothing to draw
165     if (fRC->isEmpty()) {
166         return;
167     }
168 
169     SkTCopyOnFirstWrite<SkMatrix> matrix(fCTM);
170     if (paintMatrix) {
171         SkASSERT(postPaintRect);
172         matrix.writable()->preConcat(*paintMatrix);
173     } else {
174         SkASSERT(!postPaintRect);
175     }
176 
177     SkPoint strokeSize;
178     RectType rtype = ComputeRectType(prePaintRect, paint, *fCTM, &strokeSize);
179 
180     if (kPath_RectType == rtype) {
181         draw_rect_as_path(*this, prePaintRect, paint, *matrix);
182         return;
183     }
184 
185     SkRect devRect;
186     const SkRect& paintRect = paintMatrix ? *postPaintRect : prePaintRect;
187     // skip the paintMatrix when transforming the rect by the CTM
188     fCTM->mapPoints(rect_points(devRect), rect_points(paintRect), 2);
189     devRect.sort();
190 
191     // look for the quick exit, before we build a blitter
192     SkRect bbox = devRect;
193     if (paint.getStyle() != SkPaint::kFill_Style) {
194         // extra space for hairlines
195         if (paint.getStrokeWidth() == 0) {
196             bbox.outset(1, 1);
197         } else {
198             // For kStroke_RectType, strokeSize is already computed.
199             const SkPoint& ssize = (kStroke_RectType == rtype)
200                 ? strokeSize
201                 : compute_stroke_size(paint, *fCTM);
202             bbox.outset(SkScalarHalf(ssize.x()), SkScalarHalf(ssize.y()));
203         }
204     }
205     if (SkPathPriv::TooBigForMath(bbox)) {
206         return;
207     }
208 
209     if (!SkRectPriv::FitsInFixed(bbox) && rtype != kHair_RectType) {
210         draw_rect_as_path(*this, prePaintRect, paint, *matrix);
211         return;
212     }
213 
214     SkIRect ir = bbox.roundOut();
215     if (fRC->quickReject(ir)) {
216         return;
217     }
218 
219     SkAutoBlitterChoose blitterStorage(*this, matrix, paint);
220     const SkRasterClip& clip = *fRC;
221     SkBlitter*          blitter = blitterStorage.get();
222 
223     // we want to "fill" if we are kFill or kStrokeAndFill, since in the latter
224     // case we are also hairline (if we've gotten to here), which devolves to
225     // effectively just kFill
226     switch (rtype) {
227         case kFill_RectType:
228             if (paint.isAntiAlias()) {
229                 SkScan::AntiFillRect(devRect, clip, blitter);
230             } else {
231                 SkScan::FillRect(devRect, clip, blitter);
232             }
233             break;
234         case kStroke_RectType:
235             if (paint.isAntiAlias()) {
236                 SkScan::AntiFrameRect(devRect, strokeSize, clip, blitter);
237             } else {
238                 SkScan::FrameRect(devRect, strokeSize, clip, blitter);
239             }
240             break;
241         case kHair_RectType:
242             if (paint.isAntiAlias()) {
243                 SkScan::AntiHairRect(devRect, clip, blitter);
244             } else {
245                 SkScan::HairRect(devRect, clip, blitter);
246             }
247             break;
248         default:
249             SkDEBUGFAIL("bad rtype");
250     }
251 }
252 
fast_len(const SkVector & vec)253 static SkScalar fast_len(const SkVector& vec) {
254     SkScalar x = SkScalarAbs(vec.fX);
255     SkScalar y = SkScalarAbs(vec.fY);
256     if (x < y) {
257         using std::swap;
258         swap(x, y);
259     }
260     return x + SkScalarHalf(y);
261 }
262 
SkDrawTreatAAStrokeAsHairline(SkScalar strokeWidth,const SkMatrix & matrix,SkScalar * coverage)263 bool SkDrawTreatAAStrokeAsHairline(SkScalar strokeWidth, const SkMatrix& matrix,
264                                    SkScalar* coverage) {
265     SkASSERT(strokeWidth > 0);
266     // We need to try to fake a thick-stroke with a modulated hairline.
267 
268     if (matrix.hasPerspective()) {
269         return false;
270     }
271 
272     SkVector src[2], dst[2];
273     src[0].set(strokeWidth, 0);
274     src[1].set(0, strokeWidth);
275     matrix.mapVectors(dst, src, 2);
276     SkScalar len0 = fast_len(dst[0]);
277     SkScalar len1 = fast_len(dst[1]);
278     if (len0 <= SK_Scalar1 && len1 <= SK_Scalar1) {
279         if (coverage) {
280             *coverage = SkScalarAve(len0, len1);
281         }
282         return true;
283     }
284     return false;
285 }
286 
drawRRect(const SkRRect & rrect,const SkPaint & paint) const287 void SkDrawBase::drawRRect(const SkRRect& rrect, const SkPaint& paint) const {
288     SkDEBUGCODE(this->validate());
289 
290     if (fRC->isEmpty()) {
291         return;
292     }
293 
294     {
295         // TODO: Investigate optimizing these options. They are in the same
296         // order as SkDrawBase::drawPath, which handles each case. It may be
297         // that there is no way to optimize for these using the SkRRect path.
298         SkScalar coverage;
299         if (SkDrawTreatAsHairline(paint, *fCTM, &coverage)) {
300             goto DRAW_PATH;
301         }
302 
303         if (paint.getPathEffect() || paint.getStyle() != SkPaint::kFill_Style) {
304             goto DRAW_PATH;
305         }
306     }
307 
308     if (paint.getMaskFilter()) {
309         // Transform the rrect into device space.
310         SkRRect devRRect;
311         if (rrect.transform(*fCTM, &devRRect)) {
312             SkAutoBlitterChoose blitter(*this, nullptr, paint);
313             if (as_MFB(paint.getMaskFilter())->filterRRect(devRRect, *fCTM, *fRC, blitter.get())) {
314                 return;  // filterRRect() called the blitter, so we're done
315             }
316         }
317     }
318 
319 DRAW_PATH:
320     // Now fall back to the default case of using a path.
321     SkPath path;
322     path.addRRect(rrect);
323     this->drawPath(path, paint, nullptr, true);
324 }
325 
drawDevPath(const SkPath & devPath,const SkPaint & paint,SkDrawCoverage drawCoverage,SkBlitter * customBlitter,bool doFill) const326 void SkDrawBase::drawDevPath(const SkPath& devPath,
327                              const SkPaint& paint,
328                              SkDrawCoverage drawCoverage,
329                              SkBlitter* customBlitter,
330                              bool doFill) const {
331     if (SkPathPriv::TooBigForMath(devPath)) {
332         return;
333     }
334     SkBlitter* blitter = nullptr;
335     SkAutoBlitterChoose blitterStorage;
336     if (nullptr == customBlitter) {
337         blitter = blitterStorage.choose(*this, nullptr, paint, drawCoverage);
338     } else {
339         blitter = customBlitter;
340     }
341 
342     if (paint.getMaskFilter()) {
343         SkStrokeRec::InitStyle style = doFill ? SkStrokeRec::kFill_InitStyle
344                                               : SkStrokeRec::kHairline_InitStyle;
345         if (as_MFB(paint.getMaskFilter())->filterPath(devPath, *fCTM, *fRC, blitter, style)) {
346             return;  // filterPath() called the blitter, so we're done
347         }
348     }
349 
350     void (*proc)(const SkPath&, const SkRasterClip&, SkBlitter*);
351     if (doFill) {
352         if (paint.isAntiAlias()) {
353             proc = SkScan::AntiFillPath;
354         } else {
355             proc = SkScan::FillPath;
356         }
357     } else {    // hairline
358         if (paint.isAntiAlias()) {
359             switch (paint.getStrokeCap()) {
360                 case SkPaint::kButt_Cap:
361                     proc = SkScan::AntiHairPath;
362                     break;
363                 case SkPaint::kSquare_Cap:
364                     proc = SkScan::AntiHairSquarePath;
365                     break;
366                 case SkPaint::kRound_Cap:
367                     proc = SkScan::AntiHairRoundPath;
368                     break;
369             }
370         } else {
371             switch (paint.getStrokeCap()) {
372                 case SkPaint::kButt_Cap:
373                     proc = SkScan::HairPath;
374                     break;
375                 case SkPaint::kSquare_Cap:
376                     proc = SkScan::HairSquarePath;
377                     break;
378                 case SkPaint::kRound_Cap:
379                     proc = SkScan::HairRoundPath;
380                     break;
381             }
382         }
383     }
384 
385     proc(devPath, *fRC, blitter);
386 }
387 
drawPath(const SkPath & origSrcPath,const SkPaint & origPaint,const SkMatrix * prePathMatrix,bool pathIsMutable,SkDrawCoverage drawCoverage,SkBlitter * customBlitter) const388 void SkDrawBase::drawPath(const SkPath& origSrcPath,
389                           const SkPaint& origPaint,
390                           const SkMatrix* prePathMatrix,
391                           bool pathIsMutable,
392                           SkDrawCoverage drawCoverage,
393                           SkBlitter* customBlitter) const {
394     SkDEBUGCODE(this->validate();)
395 
396     // nothing to draw
397     if (fRC->isEmpty()) {
398         return;
399     }
400 
401     SkPath*         pathPtr = const_cast<SkPath*>(&origSrcPath);
402     bool            doFill = true;
403     SkPath          tmpPathStorage;
404     SkPath*         tmpPath = &tmpPathStorage;
405     SkTCopyOnFirstWrite<SkMatrix> matrix(fCTM);
406     tmpPath->setIsVolatile(true);
407 
408     if (prePathMatrix) {
409         if (origPaint.getPathEffect() || origPaint.getStyle() != SkPaint::kFill_Style) {
410             SkPath* result = pathPtr;
411 
412             if (!pathIsMutable) {
413                 result = tmpPath;
414                 pathIsMutable = true;
415             }
416             pathPtr->transform(*prePathMatrix, result);
417             pathPtr = result;
418         } else {
419             matrix.writable()->preConcat(*prePathMatrix);
420         }
421     }
422 
423     SkTCopyOnFirstWrite<SkPaint> paint(origPaint);
424 
425     {
426         SkScalar coverage;
427         if (SkDrawTreatAsHairline(origPaint, *matrix, &coverage)) {
428             const auto bm = origPaint.asBlendMode();
429             if (SK_Scalar1 == coverage) {
430                 paint.writable()->setStrokeWidth(0);
431             } else if (bm && SkBlendMode_SupportsCoverageAsAlpha(bm.value())) {
432                 U8CPU newAlpha;
433 #if 0
434                 newAlpha = SkToU8(SkScalarRoundToInt(coverage * origPaint.getAlpha()));
435 #else
436                 // this is the old technique, which we preserve for now so
437                 // we don't change previous results (testing)
438                 // the new way seems fine, its just (a tiny bit) different
439                 int scale = (int)(coverage * 256);
440                 newAlpha = origPaint.getAlpha() * scale >> 8;
441 #endif
442                 SkPaint* writablePaint = paint.writable();
443                 writablePaint->setStrokeWidth(0);
444                 writablePaint->setAlpha(newAlpha);
445             }
446         }
447     }
448 
449     if (paint->getPathEffect() || paint->getStyle() != SkPaint::kFill_Style) {
450         SkRect cullRect;
451         const SkRect* cullRectPtr = nullptr;
452         if (this->computeConservativeLocalClipBounds(&cullRect)) {
453             cullRectPtr = &cullRect;
454         }
455         doFill = skpathutils::FillPathWithPaint(*pathPtr, *paint, tmpPath, cullRectPtr, *fCTM);
456         pathPtr = tmpPath;
457     }
458 
459     // avoid possibly allocating a new path in transform if we can
460     SkPath* devPathPtr = pathIsMutable ? pathPtr : tmpPath;
461 
462     // transform the path into device space
463     pathPtr->transform(*matrix, devPathPtr);
464 
465 #if defined(SK_BUILD_FOR_FUZZER)
466     if (devPathPtr->countPoints() > 1000) {
467         return;
468     }
469 #endif
470 
471     this->drawDevPath(*devPathPtr, *paint, drawCoverage, customBlitter, doFill);
472 }
473 
paintMasks(SkZip<const SkGlyph *,SkPoint>,const SkPaint &) const474 void SkDrawBase::paintMasks(SkZip<const SkGlyph*, SkPoint>, const SkPaint&) const {
475     SkASSERT(false);
476 }
drawBitmap(const SkBitmap &,const SkMatrix &,const SkRect *,const SkSamplingOptions &,const SkPaint &) const477 void SkDrawBase::drawBitmap(const SkBitmap&, const SkMatrix&, const SkRect*,
478                             const SkSamplingOptions&, const SkPaint&) const {
479     SkASSERT(false);
480 }
481 
482 ////////////////////////////////////////////////////////////////////////////////////////////////
483 
484 #ifdef SK_DEBUG
485 
validate() const486 void SkDrawBase::validate() const {
487     SkASSERT(fCTM != nullptr);
488     SkASSERT(fRC  != nullptr);
489 
490     const SkIRect&  cr = fRC->getBounds();
491     SkIRect         br;
492 
493     br.setWH(fDst.width(), fDst.height());
494     SkASSERT(cr.isEmpty() || br.contains(cr));
495 }
496 
497 #endif
498 
499 ////////////////////////////////////////////////////////////////////////////////////////////////
500 
compute_mask_bounds(const SkRect & devPathBounds,const SkIRect & clipBounds,const SkMaskFilter * filter,const SkMatrix * filterMatrix,SkIRect * bounds)501 static bool compute_mask_bounds(const SkRect& devPathBounds, const SkIRect& clipBounds,
502                                 const SkMaskFilter* filter, const SkMatrix* filterMatrix,
503                                 SkIRect* bounds) {
504     SkASSERT(filter);
505     SkASSERT(filterMatrix);
506     //  init our bounds from the path
507     *bounds = devPathBounds.makeOutset(SK_ScalarHalf, SK_ScalarHalf).roundOut();
508 
509     SkIVector margin = SkIPoint::Make(0, 0);
510     SkMask srcM(nullptr, *bounds, 0, SkMask::kA8_Format);
511     SkMaskBuilder dstM;
512     if (!as_MFB(filter)->filterMask(&dstM, srcM, *filterMatrix, &margin)) {
513         return false;
514     }
515 
516     // trim the bounds to reflect the clip (plus whatever slop the filter needs)
517     // Ugh. Guard against gigantic margins from wacky filters. Without this
518     // check we can request arbitrary amounts of slop beyond our visible
519     // clip, and bring down the renderer (at least on finite RAM machines
520     // like handsets, etc.). Need to balance this invented value between
521     // quality of large filters like blurs, and the corresponding memory
522     // requests.
523     static constexpr int kMaxMargin = 128;
524     if (!bounds->intersect(clipBounds.makeOutset(std::min(margin.fX, kMaxMargin),
525                                                  std::min(margin.fY, kMaxMargin)))) {
526         return false;
527     }
528 
529     return true;
530 }
531 
draw_into_mask(const SkMask & mask,const SkPath & devPath,SkStrokeRec::InitStyle style)532 static void draw_into_mask(const SkMask& mask, const SkPath& devPath,
533                            SkStrokeRec::InitStyle style) {
534     SkDrawBase draw;
535     draw.fBlitterChooser = SkA8Blitter_Choose;
536     if (!draw.fDst.reset(mask)) {
537         return;
538     }
539 
540     SkRasterClip    clip;
541     SkMatrix        matrix;
542     SkPaint         paint;
543 
544     clip.setRect(SkIRect::MakeWH(mask.fBounds.width(), mask.fBounds.height()));
545     matrix.setTranslate(-SkIntToScalar(mask.fBounds.fLeft),
546                         -SkIntToScalar(mask.fBounds.fTop));
547 
548     draw.fRC  = &clip;
549     draw.fCTM = &matrix;
550     paint.setAntiAlias(true);
551     switch (style) {
552         case SkStrokeRec::kHairline_InitStyle:
553             SkASSERT(!paint.getStrokeWidth());
554             paint.setStyle(SkPaint::kStroke_Style);
555             break;
556         case SkStrokeRec::kFill_InitStyle:
557             SkASSERT(paint.getStyle() == SkPaint::kFill_Style);
558             break;
559 
560     }
561     draw.drawPath(devPath, paint, nullptr, false);
562 }
563 
DrawToMask(const SkPath & devPath,const SkIRect & clipBounds,const SkMaskFilter * filter,const SkMatrix * filterMatrix,SkMaskBuilder * dst,SkMaskBuilder::CreateMode mode,SkStrokeRec::InitStyle style)564 bool SkDrawBase::DrawToMask(const SkPath& devPath, const SkIRect& clipBounds,
565                         const SkMaskFilter* filter, const SkMatrix* filterMatrix,
566                         SkMaskBuilder* dst, SkMaskBuilder::CreateMode mode,
567                         SkStrokeRec::InitStyle style) {
568     SkASSERT(filter);
569     if (devPath.isEmpty()) {
570         return false;
571     }
572 
573     if (SkMaskBuilder::kJustRenderImage_CreateMode != mode) {
574         // By using infinite bounds for inverse fills, compute_mask_bounds is able to clip it to
575         // 'clipBounds' outset by whatever extra margin the mask filter requires.
576         static const SkRect kInverseBounds = { SK_ScalarNegativeInfinity, SK_ScalarNegativeInfinity,
577                                                SK_ScalarInfinity, SK_ScalarInfinity};
578         SkRect pathBounds = devPath.isInverseFillType() ? kInverseBounds
579                                                         : devPath.getBounds();
580         if (!compute_mask_bounds(pathBounds, clipBounds, filter,
581                                filterMatrix, &dst->bounds())) {
582             return false;
583         }
584     }
585 
586     if (SkMaskBuilder::kComputeBoundsAndRenderImage_CreateMode == mode) {
587         dst->format() = SkMask::kA8_Format;
588         dst->rowBytes() = dst->fBounds.width();
589         size_t size = dst->computeImageSize();
590         if (0 == size) {
591             // we're too big to allocate the mask, abort
592             return false;
593         }
594         dst->image() = SkMaskBuilder::AllocImage(size, SkMaskBuilder::kZeroInit_Alloc);
595     }
596 
597     if (SkMaskBuilder::kJustComputeBounds_CreateMode != mode) {
598         draw_into_mask(*dst, devPath, style);
599     }
600     return true;
601 }
602 
drawDevicePoints(SkCanvas::PointMode mode,size_t count,const SkPoint pts[],const SkPaint & paint,SkDevice * device) const603 void SkDrawBase::drawDevicePoints(SkCanvas::PointMode mode, size_t count,
604                                   const SkPoint pts[], const SkPaint& paint,
605                                   SkDevice* device) const {
606     // if we're in lines mode, force count to be even
607     if (SkCanvas::kLines_PointMode == mode) {
608         count &= ~(size_t)1;
609     }
610 
611     SkASSERT(pts != nullptr);
612     SkDEBUGCODE(this->validate();)
613 
614      // nothing to draw
615     if (!count || fRC->isEmpty()) {
616         return;
617     }
618 
619     // needed?
620     if (!SkIsFinite(&pts[0].fX, count * 2)) {
621         return;
622     }
623 
624     switch (mode) {
625         case SkCanvas::kPoints_PointMode: {
626             // temporarily mark the paint as filling.
627             SkPaint newPaint(paint);
628             newPaint.setStyle(SkPaint::kFill_Style);
629 
630             SkScalar width = newPaint.getStrokeWidth();
631             SkScalar radius = SkScalarHalf(width);
632 
633             if (newPaint.getStrokeCap() == SkPaint::kRound_Cap) {
634                 if (device) {
635                     for (size_t i = 0; i < count; ++i) {
636                         SkRect r = SkRect::MakeLTRB(pts[i].fX - radius, pts[i].fY - radius,
637                                                     pts[i].fX + radius, pts[i].fY + radius);
638                         device->drawOval(r, newPaint);
639                     }
640                 } else {
641                     SkPath     path;
642                     SkMatrix   preMatrix;
643 
644                     path.addCircle(0, 0, radius);
645                     for (size_t i = 0; i < count; i++) {
646                         preMatrix.setTranslate(pts[i].fX, pts[i].fY);
647                         // pass true for the last point, since we can modify
648                         // then path then
649                         path.setIsVolatile((count-1) == i);
650                         this->drawPath(path, newPaint, &preMatrix, (count-1) == i);
651                     }
652                 }
653             } else {
654                 SkRect  r;
655 
656                 for (size_t i = 0; i < count; i++) {
657                     r.fLeft = pts[i].fX - radius;
658                     r.fTop = pts[i].fY - radius;
659                     r.fRight = r.fLeft + width;
660                     r.fBottom = r.fTop + width;
661                     if (device) {
662                         device->drawRect(r, newPaint);
663                     } else {
664                         this->drawRect(r, newPaint);
665                     }
666                 }
667             }
668             break;
669         }
670         case SkCanvas::kLines_PointMode:
671             if (2 == count && paint.getPathEffect()) {
672                 // most likely a dashed line - see if it is one of the ones
673                 // we can accelerate
674                 SkStrokeRec stroke(paint);
675                 SkPathEffectBase::PointData pointData;
676 
677                 SkPath path = SkPath::Line(pts[0], pts[1]);
678 
679                 SkRect cullRect = SkRect::Make(fRC->getBounds());
680 
681                 if (as_PEB(paint.getPathEffect())->asPoints(&pointData, path, stroke, *fCTM,
682                                                             &cullRect)) {
683                     // 'asPoints' managed to find some fast path
684 
685                     SkPaint newP(paint);
686                     newP.setPathEffect(nullptr);
687                     newP.setStyle(SkPaint::kFill_Style);
688 
689                     if (!pointData.fFirst.isEmpty()) {
690                         if (device) {
691                             device->drawPath(pointData.fFirst, newP, true);
692                         } else {
693                             this->drawPath(pointData.fFirst, newP, nullptr, true);
694                         }
695                     }
696 
697                     if (!pointData.fLast.isEmpty()) {
698                         if (device) {
699                             device->drawPath(pointData.fLast, newP, true);
700                         } else {
701                             this->drawPath(pointData.fLast, newP, nullptr, true);
702                         }
703                     }
704 
705                     if (pointData.fSize.fX == pointData.fSize.fY) {
706                         // The rest of the dashed line can just be drawn as points
707                         SkASSERT(pointData.fSize.fX == SkScalarHalf(newP.getStrokeWidth()));
708 
709                         if (SkPathEffectBase::PointData::kCircles_PointFlag & pointData.fFlags) {
710                             newP.setStrokeCap(SkPaint::kRound_Cap);
711                         } else {
712                             newP.setStrokeCap(SkPaint::kButt_Cap);
713                         }
714 
715                         if (device) {
716                             device->drawPoints(SkCanvas::kPoints_PointMode,
717                                                pointData.fNumPoints,
718                                                pointData.fPoints,
719                                                newP);
720                         } else {
721                             this->drawDevicePoints(SkCanvas::kPoints_PointMode,
722                                                    pointData.fNumPoints,
723                                                    pointData.fPoints,
724                                                    newP,
725                                                    device);
726                         }
727                         break;
728                     } else {
729                         // The rest of the dashed line must be drawn as rects
730                         SkASSERT(!(SkPathEffectBase::PointData::kCircles_PointFlag &
731                                   pointData.fFlags));
732 
733                         SkRect r;
734 
735                         for (int i = 0; i < pointData.fNumPoints; ++i) {
736                             r.setLTRB(pointData.fPoints[i].fX - pointData.fSize.fX,
737                                       pointData.fPoints[i].fY - pointData.fSize.fY,
738                                       pointData.fPoints[i].fX + pointData.fSize.fX,
739                                       pointData.fPoints[i].fY + pointData.fSize.fY);
740                             if (device) {
741                                 device->drawRect(r, newP);
742                             } else {
743                                 this->drawRect(r, newP);
744                             }
745                         }
746                     }
747 
748                     break;
749                 }
750             }
751             [[fallthrough]]; // couldn't take fast path
752         case SkCanvas::kPolygon_PointMode: {
753             count -= 1;
754             SkPath path;
755             SkPaint p(paint);
756             p.setStyle(SkPaint::kStroke_Style);
757             size_t inc = (SkCanvas::kLines_PointMode == mode) ? 2 : 1;
758             path.setIsVolatile(true);
759             for (size_t i = 0; i < count; i += inc) {
760                 path.moveTo(pts[i]);
761                 path.lineTo(pts[i+1]);
762                 if (device) {
763                     device->drawPath(path, p, true);
764                 } else {
765                     this->drawPath(path, p, nullptr, true);
766                 }
767                 path.rewind();
768             }
769             break;
770         }
771     }
772 }
773