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