2 * Copyright 2006 The Android Open Source Project
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
8 #include "src/core/SkDraw.h"
10 #include "include/core/SkBitmap.h"
11 #include "include/core/SkCanvas.h"
12 #include "include/core/SkMatrix.h"
13 #include "include/core/SkPaint.h"
14 #include "include/core/SkPathEffect.h"
15 #include "include/core/SkRRect.h"
16 #include "include/core/SkShader.h"
17 #include "include/core/SkString.h"
18 #include "include/core/SkStrokeRec.h"
19 #include "include/private/SkColorData.h"
20 #include "include/private/SkImageInfoPriv.h"
21 #include "include/private/SkMacros.h"
22 #include "include/private/SkTemplates.h"
23 #include "include/private/SkTo.h"
24 #include "src/core/SkArenaAlloc.h"
25 #include "src/core/SkAutoBlitterChoose.h"
26 #include "src/core/SkBlendModePriv.h"
27 #include "src/core/SkBlitter.h"
28 #include "src/core/SkDevice.h"
29 #include "src/core/SkDrawProcs.h"
30 #include "src/core/SkMaskFilterBase.h"
31 #include "src/core/SkMatrixUtils.h"
32 #include "src/core/SkPathEffectBase.h"
33 #include "src/core/SkPathPriv.h"
34 #include "src/core/SkRasterClip.h"
35 #include "src/core/SkRectPriv.h"
36 #include "src/core/SkSamplingPriv.h"
37 #include "src/core/SkScan.h"
38 #include "src/core/SkStroke.h"
39 #include "src/core/SkTLazy.h"
40 #include "src/core/SkUtils.h"
44 static SkPaint make_paint_with_image(const SkPaint& origPaint, const SkBitmap& bitmap,
45 const SkSamplingOptions& sampling,
46 SkMatrix* matrix = nullptr) {
47 SkPaint paint(origPaint);
48 paint.setShader(SkMakeBitmapShaderForPaint(origPaint, bitmap, SkTileMode::kClamp,
49 SkTileMode::kClamp, sampling, matrix,
50 kNever_SkCopyPixelsMode));
54 ///////////////////////////////////////////////////////////////////////////////
58 bool SkDraw::computeConservativeLocalClipBounds(SkRect* localBounds) const {
64 if (!fMatrixProvider->localToDevice().invert(&inverse)) {
68 SkIRect devBounds = fRC->getBounds();
69 // outset to have slop for antialasing and hairlines
70 devBounds.outset(1, 1);
71 inverse.mapRect(localBounds, SkRect::Make(devBounds));
75 ///////////////////////////////////////////////////////////////////////////////
77 void SkDraw::drawPaint(const SkPaint& paint) const {
78 SkDEBUGCODE(this->validate();)
85 devRect.setWH(fDst.width(), fDst.height());
87 SkAutoBlitterChoose blitter(*this, nullptr, paint);
88 SkScan::FillIRect(devRect, *fRC, blitter.get());
91 ///////////////////////////////////////////////////////////////////////////////
94 SkCanvas::PointMode fMode;
95 const SkPaint* fPaint;
96 const SkRegion* fClip;
97 const SkRasterClip* fRC;
103 typedef void (*Proc)(const PtProcRec&, const SkPoint devPts[], int count,
106 bool init(SkCanvas::PointMode, const SkPaint&, const SkMatrix* matrix,
107 const SkRasterClip*);
108 Proc chooseProc(SkBlitter** blitter);
111 SkAAClipBlitterWrapper fWrapper;
114 static void bw_pt_rect_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
115 int count, SkBlitter* blitter) {
116 SkASSERT(rec.fClip->isRect());
117 const SkIRect& r = rec.fClip->getBounds();
119 for (int i = 0; i < count; i++) {
120 int x = SkScalarFloorToInt(devPts[i].fX);
121 int y = SkScalarFloorToInt(devPts[i].fY);
122 if (r.contains(x, y)) {
123 blitter->blitH(x, y, 1);
128 static void bw_pt_rect_16_hair_proc(const PtProcRec& rec,
129 const SkPoint devPts[], int count,
130 SkBlitter* blitter) {
131 SkASSERT(rec.fRC->isRect());
132 const SkIRect& r = rec.fRC->getBounds();
134 const SkPixmap* dst = blitter->justAnOpaqueColor(&value);
137 uint16_t* addr = dst->writable_addr16(0, 0);
138 size_t rb = dst->rowBytes();
140 for (int i = 0; i < count; i++) {
141 int x = SkScalarFloorToInt(devPts[i].fX);
142 int y = SkScalarFloorToInt(devPts[i].fY);
143 if (r.contains(x, y)) {
144 ((uint16_t*)((char*)addr + y * rb))[x] = SkToU16(value);
149 static void bw_pt_rect_32_hair_proc(const PtProcRec& rec,
150 const SkPoint devPts[], int count,
151 SkBlitter* blitter) {
152 SkASSERT(rec.fRC->isRect());
153 const SkIRect& r = rec.fRC->getBounds();
155 const SkPixmap* dst = blitter->justAnOpaqueColor(&value);
158 SkPMColor* addr = dst->writable_addr32(0, 0);
159 size_t rb = dst->rowBytes();
161 for (int i = 0; i < count; i++) {
162 int x = SkScalarFloorToInt(devPts[i].fX);
163 int y = SkScalarFloorToInt(devPts[i].fY);
164 if (r.contains(x, y)) {
165 ((SkPMColor*)((char*)addr + y * rb))[x] = value;
170 static void bw_pt_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
171 int count, SkBlitter* blitter) {
172 for (int i = 0; i < count; i++) {
173 int x = SkScalarFloorToInt(devPts[i].fX);
174 int y = SkScalarFloorToInt(devPts[i].fY);
175 if (rec.fClip->contains(x, y)) {
176 blitter->blitH(x, y, 1);
181 static void bw_line_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
182 int count, SkBlitter* blitter) {
183 for (int i = 0; i < count; i += 2) {
184 SkScan::HairLine(&devPts[i], 2, *rec.fRC, blitter);
188 static void bw_poly_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
189 int count, SkBlitter* blitter) {
190 SkScan::HairLine(devPts, count, *rec.fRC, blitter);
195 static void aa_line_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
196 int count, SkBlitter* blitter) {
197 for (int i = 0; i < count; i += 2) {
198 SkScan::AntiHairLine(&devPts[i], 2, *rec.fRC, blitter);
202 static void aa_poly_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
203 int count, SkBlitter* blitter) {
204 SkScan::AntiHairLine(devPts, count, *rec.fRC, blitter);
207 // square procs (strokeWidth > 0 but matrix is square-scale (sx == sy)
209 static SkRect make_square_rad(SkPoint center, SkScalar radius) {
211 center.fX - radius, center.fY - radius,
212 center.fX + radius, center.fY + radius
216 static SkXRect make_xrect(const SkRect& r) {
217 SkASSERT(SkRectPriv::FitsInFixed(r));
219 SkScalarToFixed(r.fLeft), SkScalarToFixed(r.fTop),
220 SkScalarToFixed(r.fRight), SkScalarToFixed(r.fBottom)
224 static void bw_square_proc(const PtProcRec& rec, const SkPoint devPts[],
225 int count, SkBlitter* blitter) {
226 for (int i = 0; i < count; i++) {
227 SkRect r = make_square_rad(devPts[i], rec.fRadius);
228 if (r.intersect(rec.fClipBounds)) {
229 SkScan::FillXRect(make_xrect(r), *rec.fRC, blitter);
234 static void aa_square_proc(const PtProcRec& rec, const SkPoint devPts[],
235 int count, SkBlitter* blitter) {
236 for (int i = 0; i < count; i++) {
237 SkRect r = make_square_rad(devPts[i], rec.fRadius);
238 if (r.intersect(rec.fClipBounds)) {
239 SkScan::AntiFillXRect(make_xrect(r), *rec.fRC, blitter);
244 // If this returns true, then chooseProc() must return a valid proc
245 bool PtProcRec::init(SkCanvas::PointMode mode, const SkPaint& paint,
246 const SkMatrix* matrix, const SkRasterClip* rc) {
247 if ((unsigned)mode > (unsigned)SkCanvas::kPolygon_PointMode) {
250 if (paint.getPathEffect() || paint.getMaskFilter()) {
253 SkScalar width = paint.getStrokeWidth();
254 SkScalar radius = -1; // sentinel value, a "valid" value must be > 0
258 } else if (paint.getStrokeCap() != SkPaint::kRound_Cap &&
259 matrix->isScaleTranslate() && SkCanvas::kPoints_PointMode == mode) {
260 SkScalar sx = matrix->get(SkMatrix::kMScaleX);
261 SkScalar sy = matrix->get(SkMatrix::kMScaleY);
262 if (SkScalarNearlyZero(sx - sy)) {
263 radius = SkScalarHalf(width * SkScalarAbs(sx));
267 SkRect clipBounds = SkRect::Make(rc->getBounds());
268 // if we return true, the caller may assume that the constructed shapes can be represented
269 // using SkFixed (after clipping), so we preflight that here.
270 if (!SkRectPriv::FitsInFixed(clipBounds)) {
277 fClipBounds = clipBounds;
284 PtProcRec::Proc PtProcRec::chooseProc(SkBlitter** blitterPtr) {
287 SkBlitter* blitter = *blitterPtr;
289 fClip = &fRC->bwRgn();
291 fWrapper.init(*fRC, blitter);
292 fClip = &fWrapper.getRgn();
293 blitter = fWrapper.getBlitter();
294 *blitterPtr = blitter;
298 SkASSERT(0 == SkCanvas::kPoints_PointMode);
299 SkASSERT(1 == SkCanvas::kLines_PointMode);
300 SkASSERT(2 == SkCanvas::kPolygon_PointMode);
301 SkASSERT((unsigned)fMode <= (unsigned)SkCanvas::kPolygon_PointMode);
303 if (fPaint->isAntiAlias()) {
304 if (0 == fPaint->getStrokeWidth()) {
305 static const Proc gAAProcs[] = {
306 aa_square_proc, aa_line_hair_proc, aa_poly_hair_proc
308 proc = gAAProcs[fMode];
309 } else if (fPaint->getStrokeCap() != SkPaint::kRound_Cap) {
310 SkASSERT(SkCanvas::kPoints_PointMode == fMode);
311 proc = aa_square_proc;
314 if (fRadius <= 0.5f) { // small radii and hairline
315 if (SkCanvas::kPoints_PointMode == fMode && fClip->isRect()) {
317 const SkPixmap* bm = blitter->justAnOpaqueColor(&value);
318 if (bm && kRGB_565_SkColorType == bm->colorType()) {
319 proc = bw_pt_rect_16_hair_proc;
320 } else if (bm && kN32_SkColorType == bm->colorType()) {
321 proc = bw_pt_rect_32_hair_proc;
323 proc = bw_pt_rect_hair_proc;
326 static Proc gBWProcs[] = {
327 bw_pt_hair_proc, bw_line_hair_proc, bw_poly_hair_proc
329 proc = gBWProcs[fMode];
332 proc = bw_square_proc;
338 // each of these costs 8-bytes of stack space, so don't make it too large
339 // must be even for lines/polygon to work
340 #define MAX_DEV_PTS 32
342 void SkDraw::drawPoints(SkCanvas::PointMode mode, size_t count,
343 const SkPoint pts[], const SkPaint& paint,
344 SkBaseDevice* device) const {
345 // if we're in lines mode, force count to be even
346 if (SkCanvas::kLines_PointMode == mode) {
350 if ((long)count <= 0) {
354 SkASSERT(pts != nullptr);
355 SkDEBUGCODE(this->validate();)
358 if (fRC->isEmpty()) {
362 if (!SkScalarsAreFinite(&pts[0].fX, count * 2)) {
366 SkMatrix ctm = fMatrixProvider->localToDevice();
368 if (!device && rec.init(mode, paint, &ctm, fRC)) {
369 SkAutoBlitterChoose blitter(*this, nullptr, paint);
371 SkPoint devPts[MAX_DEV_PTS];
372 SkBlitter* bltr = blitter.get();
373 PtProcRec::Proc proc = rec.chooseProc(&bltr);
374 // we have to back up subsequent passes if we're in polygon mode
375 const size_t backup = (SkCanvas::kPolygon_PointMode == mode);
378 int n = SkToInt(count);
379 if (n > MAX_DEV_PTS) {
382 ctm.mapPoints(devPts, pts, n);
383 if (!SkScalarsAreFinite(&devPts[0].fX, n * 2)) {
386 proc(rec, devPts, n, bltr);
388 SkASSERT(SkToInt(count) >= n);
393 } while (count != 0);
396 case SkCanvas::kPoints_PointMode: {
397 // temporarily mark the paint as filling.
398 SkPaint newPaint(paint);
399 newPaint.setStyle(SkPaint::kFill_Style);
401 SkScalar width = newPaint.getStrokeWidth();
402 SkScalar radius = SkScalarHalf(width);
404 if (newPaint.getStrokeCap() == SkPaint::kRound_Cap) {
406 for (size_t i = 0; i < count; ++i) {
407 SkRect r = SkRect::MakeLTRB(pts[i].fX - radius, pts[i].fY - radius,
408 pts[i].fX + radius, pts[i].fY + radius);
409 device->drawOval(r, newPaint);
415 path.addCircle(0, 0, radius);
416 for (size_t i = 0; i < count; i++) {
417 preMatrix.setTranslate(pts[i].fX, pts[i].fY);
418 // pass true for the last point, since we can modify
420 path.setIsVolatile((count-1) == i);
421 this->drawPath(path, newPaint, &preMatrix, (count-1) == i);
427 for (size_t i = 0; i < count; i++) {
428 r.fLeft = pts[i].fX - radius;
429 r.fTop = pts[i].fY - radius;
430 r.fRight = r.fLeft + width;
431 r.fBottom = r.fTop + width;
433 device->drawRect(r, newPaint);
435 this->drawRect(r, newPaint);
441 case SkCanvas::kLines_PointMode:
442 if (2 == count && paint.getPathEffect()) {
443 // most likely a dashed line - see if it is one of the ones
445 SkStrokeRec stroke(paint);
446 SkPathEffectBase::PointData pointData;
448 SkPath path = SkPath::Line(pts[0], pts[1]);
450 SkRect cullRect = SkRect::Make(fRC->getBounds());
452 if (as_PEB(paint.getPathEffect())->asPoints(&pointData, path, stroke, ctm,
454 // 'asPoints' managed to find some fast path
457 newP.setPathEffect(nullptr);
458 newP.setStyle(SkPaint::kFill_Style);
460 if (!pointData.fFirst.isEmpty()) {
462 device->drawPath(pointData.fFirst, newP);
464 this->drawPath(pointData.fFirst, newP);
468 if (!pointData.fLast.isEmpty()) {
470 device->drawPath(pointData.fLast, newP);
472 this->drawPath(pointData.fLast, newP);
476 if (pointData.fSize.fX == pointData.fSize.fY) {
477 // The rest of the dashed line can just be drawn as points
478 SkASSERT(pointData.fSize.fX == SkScalarHalf(newP.getStrokeWidth()));
480 if (SkPathEffectBase::PointData::kCircles_PointFlag & pointData.fFlags) {
481 newP.setStrokeCap(SkPaint::kRound_Cap);
483 newP.setStrokeCap(SkPaint::kButt_Cap);
487 device->drawPoints(SkCanvas::kPoints_PointMode,
488 pointData.fNumPoints,
492 this->drawPoints(SkCanvas::kPoints_PointMode,
493 pointData.fNumPoints,
500 // The rest of the dashed line must be drawn as rects
501 SkASSERT(!(SkPathEffectBase::PointData::kCircles_PointFlag &
506 for (int i = 0; i < pointData.fNumPoints; ++i) {
507 r.setLTRB(pointData.fPoints[i].fX - pointData.fSize.fX,
508 pointData.fPoints[i].fY - pointData.fSize.fY,
509 pointData.fPoints[i].fX + pointData.fSize.fX,
510 pointData.fPoints[i].fY + pointData.fSize.fY);
512 device->drawRect(r, newP);
514 this->drawRect(r, newP);
522 [[fallthrough]]; // couldn't take fast path
523 case SkCanvas::kPolygon_PointMode: {
527 p.setStyle(SkPaint::kStroke_Style);
528 size_t inc = (SkCanvas::kLines_PointMode == mode) ? 2 : 1;
529 path.setIsVolatile(true);
530 for (size_t i = 0; i < count; i += inc) {
532 path.lineTo(pts[i+1]);
534 device->drawPath(path, p, true);
536 this->drawPath(path, p, nullptr, true);
546 static inline SkPoint compute_stroke_size(const SkPaint& paint, const SkMatrix& matrix) {
547 SkASSERT(matrix.rectStaysRect());
548 SkASSERT(SkPaint::kFill_Style != paint.getStyle());
551 SkPoint pt = { paint.getStrokeWidth(), paint.getStrokeWidth() };
552 matrix.mapVectors(&size, &pt, 1);
553 return SkPoint::Make(SkScalarAbs(size.fX), SkScalarAbs(size.fY));
556 static bool easy_rect_join(const SkRect& rect, const SkPaint& paint, const SkMatrix& matrix,
557 SkPoint* strokeSize) {
558 if (rect.isEmpty() || SkPaint::kMiter_Join != paint.getStrokeJoin() ||
559 paint.getStrokeMiter() < SK_ScalarSqrt2) {
563 *strokeSize = compute_stroke_size(paint, matrix);
567 SkDraw::RectType SkDraw::ComputeRectType(const SkRect& rect,
568 const SkPaint& paint,
569 const SkMatrix& matrix,
570 SkPoint* strokeSize) {
572 const SkScalar width = paint.getStrokeWidth();
573 const bool zeroWidth = (0 == width);
574 SkPaint::Style style = paint.getStyle();
576 if ((SkPaint::kStrokeAndFill_Style == style) && zeroWidth) {
577 style = SkPaint::kFill_Style;
580 if (paint.getPathEffect() || paint.getMaskFilter() ||
581 !matrix.rectStaysRect() || SkPaint::kStrokeAndFill_Style == style) {
582 rtype = kPath_RectType;
583 } else if (SkPaint::kFill_Style == style) {
584 rtype = kFill_RectType;
585 } else if (zeroWidth) {
586 rtype = kHair_RectType;
587 } else if (easy_rect_join(rect, paint, matrix, strokeSize)) {
588 rtype = kStroke_RectType;
590 rtype = kPath_RectType;
595 static const SkPoint* rect_points(const SkRect& r) {
596 return reinterpret_cast<const SkPoint*>(&r);
599 static SkPoint* rect_points(SkRect& r) {
600 return reinterpret_cast<SkPoint*>(&r);
603 static void draw_rect_as_path(const SkDraw& orig, const SkRect& prePaintRect,
604 const SkPaint& paint, const SkMatrixProvider* matrixProvider) {
606 draw.fMatrixProvider = matrixProvider;
608 tmp.addRect(prePaintRect);
609 tmp.setFillType(SkPathFillType::kWinding);
610 draw.drawPath(tmp, paint, nullptr, true);
613 void SkDraw::drawRect(const SkRect& prePaintRect, const SkPaint& paint,
614 const SkMatrix* paintMatrix, const SkRect* postPaintRect) const {
615 SkDEBUGCODE(this->validate();)
618 if (fRC->isEmpty()) {
622 const SkMatrixProvider* matrixProvider = fMatrixProvider;
623 SkTLazy<SkPreConcatMatrixProvider> preConcatMatrixProvider;
625 SkASSERT(postPaintRect);
626 matrixProvider = preConcatMatrixProvider.init(*matrixProvider, *paintMatrix);
628 SkASSERT(!postPaintRect);
631 SkMatrix ctm = fMatrixProvider->localToDevice();
633 RectType rtype = ComputeRectType(prePaintRect, paint, ctm, &strokeSize);
635 if (kPath_RectType == rtype) {
636 draw_rect_as_path(*this, prePaintRect, paint, matrixProvider);
641 const SkRect& paintRect = paintMatrix ? *postPaintRect : prePaintRect;
642 // skip the paintMatrix when transforming the rect by the CTM
643 ctm.mapPoints(rect_points(devRect), rect_points(paintRect), 2);
646 // look for the quick exit, before we build a blitter
647 SkRect bbox = devRect;
648 if (paint.getStyle() != SkPaint::kFill_Style) {
649 // extra space for hairlines
650 if (paint.getStrokeWidth() == 0) {
653 // For kStroke_RectType, strokeSize is already computed.
654 const SkPoint& ssize = (kStroke_RectType == rtype)
656 : compute_stroke_size(paint, ctm);
657 bbox.outset(SkScalarHalf(ssize.x()), SkScalarHalf(ssize.y()));
660 if (SkPathPriv::TooBigForMath(bbox)) {
664 if (!SkRectPriv::FitsInFixed(bbox) && rtype != kHair_RectType) {
665 draw_rect_as_path(*this, prePaintRect, paint, matrixProvider);
669 SkIRect ir = bbox.roundOut();
670 if (fRC->quickReject(ir)) {
674 SkAutoBlitterChoose blitterStorage(*this, matrixProvider, paint);
675 const SkRasterClip& clip = *fRC;
676 SkBlitter* blitter = blitterStorage.get();
678 // we want to "fill" if we are kFill or kStrokeAndFill, since in the latter
679 // case we are also hairline (if we've gotten to here), which devolves to
680 // effectively just kFill
683 if (paint.isAntiAlias()) {
684 SkScan::AntiFillRect(devRect, clip, blitter);
686 SkScan::FillRect(devRect, clip, blitter);
689 case kStroke_RectType:
690 if (paint.isAntiAlias()) {
691 SkScan::AntiFrameRect(devRect, strokeSize, clip, blitter);
693 SkScan::FrameRect(devRect, strokeSize, clip, blitter);
697 if (paint.isAntiAlias()) {
698 SkScan::AntiHairRect(devRect, clip, blitter);
700 SkScan::HairRect(devRect, clip, blitter);
704 SkDEBUGFAIL("bad rtype");
708 static SkScalar fast_len(const SkVector& vec) {
709 SkScalar x = SkScalarAbs(vec.fX);
710 SkScalar y = SkScalarAbs(vec.fY);
715 return x + SkScalarHalf(y);
718 bool SkDrawTreatAAStrokeAsHairline(SkScalar strokeWidth, const SkMatrix& matrix,
719 SkScalar* coverage) {
720 SkASSERT(strokeWidth > 0);
721 // We need to try to fake a thick-stroke with a modulated hairline.
723 if (matrix.hasPerspective()) {
727 SkVector src[2], dst[2];
728 src[0].set(strokeWidth, 0);
729 src[1].set(0, strokeWidth);
730 matrix.mapVectors(dst, src, 2);
731 SkScalar len0 = fast_len(dst[0]);
732 SkScalar len1 = fast_len(dst[1]);
733 if (len0 <= SK_Scalar1 && len1 <= SK_Scalar1) {
735 *coverage = SkScalarAve(len0, len1);
742 void SkDraw::drawRRect(const SkRRect& rrect, const SkPaint& paint) const {
743 SkDEBUGCODE(this->validate());
745 if (fRC->isEmpty()) {
749 SkMatrix ctm = fMatrixProvider->localToDevice();
751 // TODO: Investigate optimizing these options. They are in the same
752 // order as SkDraw::drawPath, which handles each case. It may be
753 // that there is no way to optimize for these using the SkRRect path.
755 if (SkDrawTreatAsHairline(paint, ctm, &coverage)) {
759 if (paint.getPathEffect() || paint.getStyle() != SkPaint::kFill_Style) {
764 if (paint.getMaskFilter()) {
765 // Transform the rrect into device space.
767 if (rrect.transform(ctm, &devRRect)) {
768 SkAutoBlitterChoose blitter(*this, nullptr, paint);
769 if (as_MFB(paint.getMaskFilter())->filterRRect(devRRect, ctm, *fRC, blitter.get())) {
770 return; // filterRRect() called the blitter, so we're done
776 // Now fall back to the default case of using a path.
778 path.addRRect(rrect);
779 this->drawPath(path, paint, nullptr, true);
782 void SkDraw::drawDevPath(const SkPath& devPath, const SkPaint& paint, bool drawCoverage,
783 SkBlitter* customBlitter, bool doFill) const {
784 if (SkPathPriv::TooBigForMath(devPath)) {
787 SkBlitter* blitter = nullptr;
788 SkAutoBlitterChoose blitterStorage;
789 if (nullptr == customBlitter) {
790 blitter = blitterStorage.choose(*this, nullptr, paint, drawCoverage);
792 blitter = customBlitter;
795 if (paint.getMaskFilter()) {
796 SkStrokeRec::InitStyle style = doFill ? SkStrokeRec::kFill_InitStyle
797 : SkStrokeRec::kHairline_InitStyle;
798 if (as_MFB(paint.getMaskFilter())
799 ->filterPath(devPath, fMatrixProvider->localToDevice(), *fRC, blitter, style)) {
800 return; // filterPath() called the blitter, so we're done
804 void (*proc)(const SkPath&, const SkRasterClip&, SkBlitter*);
806 if (paint.isAntiAlias()) {
807 proc = SkScan::AntiFillPath;
809 proc = SkScan::FillPath;
812 if (paint.isAntiAlias()) {
813 switch (paint.getStrokeCap()) {
814 case SkPaint::kButt_Cap:
815 proc = SkScan::AntiHairPath;
817 case SkPaint::kSquare_Cap:
818 proc = SkScan::AntiHairSquarePath;
820 case SkPaint::kRound_Cap:
821 proc = SkScan::AntiHairRoundPath;
825 switch (paint.getStrokeCap()) {
826 case SkPaint::kButt_Cap:
827 proc = SkScan::HairPath;
829 case SkPaint::kSquare_Cap:
830 proc = SkScan::HairSquarePath;
832 case SkPaint::kRound_Cap:
833 proc = SkScan::HairRoundPath;
839 proc(devPath, *fRC, blitter);
842 void SkDraw::drawPath(const SkPath& origSrcPath, const SkPaint& origPaint,
843 const SkMatrix* prePathMatrix, bool pathIsMutable,
844 bool drawCoverage, SkBlitter* customBlitter) const {
845 SkDEBUGCODE(this->validate();)
848 if (fRC->isEmpty()) {
852 SkPath* pathPtr = (SkPath*)&origSrcPath;
854 SkPath tmpPathStorage;
855 SkPath* tmpPath = &tmpPathStorage;
856 const SkMatrixProvider* matrixProvider = fMatrixProvider;
857 SkTLazy<SkPreConcatMatrixProvider> preConcatMatrixProvider;
858 tmpPath->setIsVolatile(true);
861 if (origPaint.getPathEffect() || origPaint.getStyle() != SkPaint::kFill_Style) {
862 SkPath* result = pathPtr;
864 if (!pathIsMutable) {
866 pathIsMutable = true;
868 pathPtr->transform(*prePathMatrix, result);
871 matrixProvider = preConcatMatrixProvider.init(*matrixProvider, *prePathMatrix);
875 SkTCopyOnFirstWrite<SkPaint> paint(origPaint);
879 if (SkDrawTreatAsHairline(origPaint, matrixProvider->localToDevice(), &coverage)) {
880 const auto bm = origPaint.asBlendMode();
881 if (SK_Scalar1 == coverage) {
882 paint.writable()->setStrokeWidth(0);
883 } else if (bm && SkBlendMode_SupportsCoverageAsAlpha(bm.value())) {
886 newAlpha = SkToU8(SkScalarRoundToInt(coverage *
887 origPaint.getAlpha()));
889 // this is the old technique, which we preserve for now so
890 // we don't change previous results (testing)
891 // the new way seems fine, its just (a tiny bit) different
892 int scale = (int)(coverage * 256);
893 newAlpha = origPaint.getAlpha() * scale >> 8;
895 SkPaint* writablePaint = paint.writable();
896 writablePaint->setStrokeWidth(0);
897 writablePaint->setAlpha(newAlpha);
902 if (paint->getPathEffect() || paint->getStyle() != SkPaint::kFill_Style) {
904 const SkRect* cullRectPtr = nullptr;
905 if (this->computeConservativeLocalClipBounds(&cullRect)) {
906 cullRectPtr = &cullRect;
908 doFill = paint->getFillPath(*pathPtr, tmpPath, cullRectPtr,
909 fMatrixProvider->localToDevice());
913 // avoid possibly allocating a new path in transform if we can
914 SkPath* devPathPtr = pathIsMutable ? pathPtr : tmpPath;
916 // transform the path into device space
917 pathPtr->transform(matrixProvider->localToDevice(), devPathPtr);
919 #if defined(SK_BUILD_FOR_FUZZER)
920 if (devPathPtr->countPoints() > 1000) {
925 this->drawDevPath(*devPathPtr, *paint, drawCoverage, customBlitter, doFill);
928 static bool clipped_out(const SkMatrix& m, const SkRasterClip& c,
929 const SkRect& srcR) {
931 m.mapRect(&dstR, srcR);
932 return c.quickReject(dstR.roundOut());
935 static bool clipped_out(const SkMatrix& matrix, const SkRasterClip& clip,
936 int width, int height) {
938 r.setIWH(width, height);
939 return clipped_out(matrix, clip, r);
942 static bool clipHandlesSprite(const SkRasterClip& clip, int x, int y, const SkPixmap& pmap) {
943 return clip.isBW() || clip.quickContains(SkIRect::MakeXYWH(x, y, pmap.width(), pmap.height()));
946 void SkDraw::drawBitmap(const SkBitmap& bitmap, const SkMatrix& prematrix,
947 const SkRect* dstBounds, const SkSamplingOptions& sampling,
948 const SkPaint& origPaint) const {
949 SkDEBUGCODE(this->validate();)
952 if (fRC->isEmpty() ||
953 bitmap.width() == 0 || bitmap.height() == 0 ||
954 bitmap.colorType() == kUnknown_SkColorType) {
958 SkTCopyOnFirstWrite<SkPaint> paint(origPaint);
959 if (origPaint.getStyle() != SkPaint::kFill_Style) {
960 paint.writable()->setStyle(SkPaint::kFill_Style);
963 SkPreConcatMatrixProvider matrixProvider(*fMatrixProvider, prematrix);
964 SkMatrix matrix = matrixProvider.localToDevice();
966 if (clipped_out(matrix, *fRC, bitmap.width(), bitmap.height())) {
970 if (!SkColorTypeIsAlphaOnly(bitmap.colorType()) &&
971 SkTreatAsSprite(matrix, bitmap.dimensions(), sampling, *paint)) {
973 // It is safe to call lock pixels now, since we know the matrix is
974 // (more or less) identity.
977 if (!bitmap.peekPixels(&pmap)) {
980 int ix = SkScalarRoundToInt(matrix.getTranslateX());
981 int iy = SkScalarRoundToInt(matrix.getTranslateY());
982 if (clipHandlesSprite(*fRC, ix, iy, pmap)) {
983 SkSTArenaAlloc<kSkBlitterContextSize> allocator;
984 // blitter will be owned by the allocator.
985 SkBlitter* blitter = SkBlitter::ChooseSprite(fDst, *paint, pmap, ix, iy, &allocator,
988 SkScan::FillIRect(SkIRect::MakeXYWH(ix, iy, pmap.width(), pmap.height()),
992 // if !blitter, then we fall-through to the slower case
996 // now make a temp draw on the stack, and use it
999 draw.fMatrixProvider = &matrixProvider;
1001 SkPaint paintWithShader = make_paint_with_image(*paint, bitmap, sampling);
1002 const SkRect srcBounds = SkRect::MakeIWH(bitmap.width(), bitmap.height());
1004 this->drawRect(srcBounds, paintWithShader, &prematrix, dstBounds);
1006 draw.drawRect(srcBounds, paintWithShader);
1010 void SkDraw::drawSprite(const SkBitmap& bitmap, int x, int y, const SkPaint& origPaint) const {
1011 SkDEBUGCODE(this->validate();)
1014 if (fRC->isEmpty() ||
1015 bitmap.width() == 0 || bitmap.height() == 0 ||
1016 bitmap.colorType() == kUnknown_SkColorType) {
1020 const SkIRect bounds = SkIRect::MakeXYWH(x, y, bitmap.width(), bitmap.height());
1022 if (fRC->quickReject(bounds)) {
1023 return; // nothing to draw
1026 SkPaint paint(origPaint);
1027 paint.setStyle(SkPaint::kFill_Style);
1030 if (!bitmap.peekPixels(&pmap)) {
1034 if (nullptr == paint.getColorFilter() && clipHandlesSprite(*fRC, x, y, pmap)) {
1035 // blitter will be owned by the allocator.
1036 SkSTArenaAlloc<kSkBlitterContextSize> allocator;
1037 SkBlitter* blitter = SkBlitter::ChooseSprite(fDst, paint, pmap, x, y, &allocator,
1040 SkScan::FillIRect(bounds, *fRC, blitter);
1048 // get a scalar version of our rect
1051 // create shader with offset
1052 matrix.setTranslate(r.fLeft, r.fTop);
1053 SkPaint paintWithShader = make_paint_with_image(paint, bitmap, SkSamplingOptions(), &matrix);
1055 SkOverrideDeviceMatrixProvider matrixProvider(SkMatrix::I());
1056 draw.fMatrixProvider = &matrixProvider;
1057 // call ourself with a rect
1058 draw.drawRect(r, paintWithShader);
1061 ////////////////////////////////////////////////////////////////////////////////////////////////
1065 void SkDraw::validate() const {
1066 SkASSERT(fMatrixProvider != nullptr);
1067 SkASSERT(fRC != nullptr);
1069 const SkIRect& cr = fRC->getBounds();
1072 br.setWH(fDst.width(), fDst.height());
1073 SkASSERT(cr.isEmpty() || br.contains(cr));
1078 ////////////////////////////////////////////////////////////////////////////////////////////////
1080 #include "include/core/SkPath.h"
1081 #include "include/core/SkRegion.h"
1082 #include "src/core/SkBlitter.h"
1083 #include "src/core/SkDraw.h"
1085 bool SkDraw::ComputeMaskBounds(const SkRect& devPathBounds, const SkIRect& clipBounds,
1086 const SkMaskFilter* filter, const SkMatrix* filterMatrix,
1088 // init our bounds from the path
1089 *bounds = devPathBounds.makeOutset(SK_ScalarHalf, SK_ScalarHalf).roundOut();
1091 SkIPoint margin = SkIPoint::Make(0, 0);
1093 SkASSERT(filterMatrix);
1097 srcM.fBounds = *bounds;
1098 srcM.fFormat = SkMask::kA8_Format;
1099 if (!as_MFB(filter)->filterMask(&dstM, srcM, *filterMatrix, &margin)) {
1104 // trim the bounds to reflect the clip (plus whatever slop the filter needs)
1105 // Ugh. Guard against gigantic margins from wacky filters. Without this
1106 // check we can request arbitrary amounts of slop beyond our visible
1107 // clip, and bring down the renderer (at least on finite RAM machines
1108 // like handsets, etc.). Need to balance this invented value between
1109 // quality of large filters like blurs, and the corresponding memory
1111 static constexpr int kMaxMargin = 128;
1112 if (!bounds->intersect(clipBounds.makeOutset(std::min(margin.fX, kMaxMargin),
1113 std::min(margin.fY, kMaxMargin)))) {
1120 static void draw_into_mask(const SkMask& mask, const SkPath& devPath,
1121 SkStrokeRec::InitStyle style) {
1123 if (!draw.fDst.reset(mask)) {
1131 clip.setRect(SkIRect::MakeWH(mask.fBounds.width(), mask.fBounds.height()));
1132 matrix.setTranslate(-SkIntToScalar(mask.fBounds.fLeft),
1133 -SkIntToScalar(mask.fBounds.fTop));
1135 SkMatrixProvider matrixProvider(matrix);
1137 draw.fMatrixProvider = &matrixProvider;
1138 paint.setAntiAlias(true);
1140 case SkStrokeRec::kHairline_InitStyle:
1141 SkASSERT(!paint.getStrokeWidth());
1142 paint.setStyle(SkPaint::kStroke_Style);
1144 case SkStrokeRec::kFill_InitStyle:
1145 SkASSERT(paint.getStyle() == SkPaint::kFill_Style);
1149 draw.drawPath(devPath, paint);
1152 bool SkDraw::DrawToMask(const SkPath& devPath, const SkIRect& clipBounds,
1153 const SkMaskFilter* filter, const SkMatrix* filterMatrix,
1154 SkMask* mask, SkMask::CreateMode mode,
1155 SkStrokeRec::InitStyle style) {
1156 if (devPath.isEmpty()) {
1160 if (SkMask::kJustRenderImage_CreateMode != mode) {
1161 // By using infinite bounds for inverse fills, ComputeMaskBounds is able to clip it to
1162 // 'clipBounds' outset by whatever extra margin the mask filter requires.
1163 static const SkRect kInverseBounds = { SK_ScalarNegativeInfinity, SK_ScalarNegativeInfinity,
1164 SK_ScalarInfinity, SK_ScalarInfinity};
1165 SkRect pathBounds = devPath.isInverseFillType() ? kInverseBounds
1166 : devPath.getBounds();
1167 if (!ComputeMaskBounds(pathBounds, clipBounds, filter,
1168 filterMatrix, &mask->fBounds))
1172 if (SkMask::kComputeBoundsAndRenderImage_CreateMode == mode) {
1173 mask->fFormat = SkMask::kA8_Format;
1174 mask->fRowBytes = mask->fBounds.width();
1175 size_t size = mask->computeImageSize();
1177 // we're too big to allocate the mask, abort
1180 mask->fImage = SkMask::AllocImage(size, SkMask::kZeroInit_Alloc);
1183 if (SkMask::kJustComputeBounds_CreateMode != mode) {
1184 draw_into_mask(*mask, devPath, style);