1/*
2 * Copyright 2006 The Android Open Source Project
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 "src/core/SkDraw.h"
9
10#include "include/core/SkCanvas.h"
11#include "include/core/SkMatrix.h"
12#include "include/core/SkPaint.h"
13#include "include/core/SkPathEffect.h"
14#include "include/core/SkRRect.h"
15#include "include/core/SkShader.h"
16#include "include/core/SkString.h"
17#include "include/core/SkStrokeRec.h"
18#include "include/private/SkColorData.h"
19#include "include/private/SkMacros.h"
20#include "include/private/SkTemplates.h"
21#include "include/private/SkTo.h"
22#include "src/core/SkArenaAlloc.h"
23#include "src/core/SkAutoBlitterChoose.h"
24#include "src/core/SkBlendModePriv.h"
25#include "src/core/SkBlitter.h"
26#include "src/core/SkDevice.h"
27#include "src/core/SkDrawProcs.h"
28#include "src/core/SkMaskFilterBase.h"
29#include "src/core/SkMatrixUtils.h"
30#include "src/core/SkPathPriv.h"
31#include "src/core/SkRasterClip.h"
32#include "src/core/SkRectPriv.h"
33#include "src/core/SkScan.h"
34#include "src/core/SkStroke.h"
35#include "src/core/SkTLazy.h"
36#include "src/core/SkUtils.h"
37
38#include <utility>
39
40static SkPaint make_paint_with_image(
41 const SkPaint& origPaint, const SkBitmap& bitmap, SkMatrix* matrix = nullptr) {
42 SkPaint paint(origPaint);
43 paint.setShader(SkMakeBitmapShaderForPaint(origPaint, bitmap, SkTileMode::kClamp,
44 SkTileMode::kClamp, matrix,
45 kNever_SkCopyPixelsMode));
46 return paint;
47}
48
49///////////////////////////////////////////////////////////////////////////////
50
51SkDraw::SkDraw() {}
52
53bool SkDraw::computeConservativeLocalClipBounds(SkRect* localBounds) const {
54 if (fRC->isEmpty()) {
55 return false;
56 }
57
58 SkMatrix inverse;
59 if (!fMatrixProvider->localToDevice().invert(&inverse)) {
60 return false;
61 }
62
63 SkIRect devBounds = fRC->getBounds();
64 // outset to have slop for antialasing and hairlines
65 devBounds.outset(1, 1);
66 inverse.mapRect(localBounds, SkRect::Make(devBounds));
67 return true;
68}
69
70///////////////////////////////////////////////////////////////////////////////
71
72void SkDraw::drawPaint(const SkPaint& paint) const {
73 SkDEBUGCODE(this->validate();)
74
75 if (fRC->isEmpty()) {
76 return;
77 }
78
79 SkIRect devRect;
80 devRect.setWH(fDst.width(), fDst.height());
81
82 SkAutoBlitterChoose blitter(*this, nullptr, paint);
83 SkScan::FillIRect(devRect, *fRC, blitter.get());
84}
85
86///////////////////////////////////////////////////////////////////////////////
87
88struct PtProcRec {
89 SkCanvas::PointMode fMode;
90 const SkPaint* fPaint;
91 const SkRegion* fClip;
92 const SkRasterClip* fRC;
93
94 // computed values
95 SkRect fClipBounds;
96 SkScalar fRadius;
97
98 typedef void (*Proc)(const PtProcRec&, const SkPoint devPts[], int count,
99 SkBlitter*);
100
101 bool init(SkCanvas::PointMode, const SkPaint&, const SkMatrix* matrix,
102 const SkRasterClip*);
103 Proc chooseProc(SkBlitter** blitter);
104
105private:
106 SkAAClipBlitterWrapper fWrapper;
107};
108
109static void bw_pt_rect_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
110 int count, SkBlitter* blitter) {
111 SkASSERT(rec.fClip->isRect());
112 const SkIRect& r = rec.fClip->getBounds();
113
114 for (int i = 0; i < count; i++) {
115 int x = SkScalarFloorToInt(devPts[i].fX);
116 int y = SkScalarFloorToInt(devPts[i].fY);
117 if (r.contains(x, y)) {
118 blitter->blitH(x, y, 1);
119 }
120 }
121}
122
123static void bw_pt_rect_16_hair_proc(const PtProcRec& rec,
124 const SkPoint devPts[], int count,
125 SkBlitter* blitter) {
126 SkASSERT(rec.fRC->isRect());
127 const SkIRect& r = rec.fRC->getBounds();
128 uint32_t value;
129 const SkPixmap* dst = blitter->justAnOpaqueColor(&value);
130 SkASSERT(dst);
131
132 uint16_t* addr = dst->writable_addr16(0, 0);
133 size_t rb = dst->rowBytes();
134
135 for (int i = 0; i < count; i++) {
136 int x = SkScalarFloorToInt(devPts[i].fX);
137 int y = SkScalarFloorToInt(devPts[i].fY);
138 if (r.contains(x, y)) {
139 ((uint16_t*)((char*)addr + y * rb))[x] = SkToU16(value);
140 }
141 }
142}
143
144static void bw_pt_rect_32_hair_proc(const PtProcRec& rec,
145 const SkPoint devPts[], int count,
146 SkBlitter* blitter) {
147 SkASSERT(rec.fRC->isRect());
148 const SkIRect& r = rec.fRC->getBounds();
149 uint32_t value;
150 const SkPixmap* dst = blitter->justAnOpaqueColor(&value);
151 SkASSERT(dst);
152
153 SkPMColor* addr = dst->writable_addr32(0, 0);
154 size_t rb = dst->rowBytes();
155
156 for (int i = 0; i < count; i++) {
157 int x = SkScalarFloorToInt(devPts[i].fX);
158 int y = SkScalarFloorToInt(devPts[i].fY);
159 if (r.contains(x, y)) {
160 ((SkPMColor*)((char*)addr + y * rb))[x] = value;
161 }
162 }
163}
164
165static void bw_pt_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
166 int count, SkBlitter* blitter) {
167 for (int i = 0; i < count; i++) {
168 int x = SkScalarFloorToInt(devPts[i].fX);
169 int y = SkScalarFloorToInt(devPts[i].fY);
170 if (rec.fClip->contains(x, y)) {
171 blitter->blitH(x, y, 1);
172 }
173 }
174}
175
176static void bw_line_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
177 int count, SkBlitter* blitter) {
178 for (int i = 0; i < count; i += 2) {
179 SkScan::HairLine(&devPts[i], 2, *rec.fRC, blitter);
180 }
181}
182
183static void bw_poly_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
184 int count, SkBlitter* blitter) {
185 SkScan::HairLine(devPts, count, *rec.fRC, blitter);
186}
187
188// aa versions
189
190static void aa_line_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
191 int count, SkBlitter* blitter) {
192 for (int i = 0; i < count; i += 2) {
193 SkScan::AntiHairLine(&devPts[i], 2, *rec.fRC, blitter);
194 }
195}
196
197static void aa_poly_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
198 int count, SkBlitter* blitter) {
199 SkScan::AntiHairLine(devPts, count, *rec.fRC, blitter);
200}
201
202// square procs (strokeWidth > 0 but matrix is square-scale (sx == sy)
203
204static SkRect make_square_rad(SkPoint center, SkScalar radius) {
205 return {
206 center.fX - radius, center.fY - radius,
207 center.fX + radius, center.fY + radius
208 };
209}
210
211static SkXRect make_xrect(const SkRect& r) {
212 SkASSERT(SkRectPriv::FitsInFixed(r));
213 return {
214 SkScalarToFixed(r.fLeft), SkScalarToFixed(r.fTop),
215 SkScalarToFixed(r.fRight), SkScalarToFixed(r.fBottom)
216 };
217}
218
219static void bw_square_proc(const PtProcRec& rec, const SkPoint devPts[],
220 int count, SkBlitter* blitter) {
221 for (int i = 0; i < count; i++) {
222 SkRect r = make_square_rad(devPts[i], rec.fRadius);
223 if (r.intersect(rec.fClipBounds)) {
224 SkScan::FillXRect(make_xrect(r), *rec.fRC, blitter);
225 }
226 }
227}
228
229static void aa_square_proc(const PtProcRec& rec, const SkPoint devPts[],
230 int count, SkBlitter* blitter) {
231 for (int i = 0; i < count; i++) {
232 SkRect r = make_square_rad(devPts[i], rec.fRadius);
233 if (r.intersect(rec.fClipBounds)) {
234 SkScan::AntiFillXRect(make_xrect(r), *rec.fRC, blitter);
235 }
236 }
237}
238
239// If this returns true, then chooseProc() must return a valid proc
240bool PtProcRec::init(SkCanvas::PointMode mode, const SkPaint& paint,
241 const SkMatrix* matrix, const SkRasterClip* rc) {
242 if ((unsigned)mode > (unsigned)SkCanvas::kPolygon_PointMode) {
243 return false;
244 }
245 if (paint.getPathEffect()) {
246 return false;
247 }
248 SkScalar width = paint.getStrokeWidth();
249 SkScalar radius = -1; // sentinel value, a "valid" value must be > 0
250
251 if (0 == width) {
252 radius = 0.5f;
253 } else if (paint.getStrokeCap() != SkPaint::kRound_Cap &&
254 matrix->isScaleTranslate() && SkCanvas::kPoints_PointMode == mode) {
255 SkScalar sx = matrix->get(SkMatrix::kMScaleX);
256 SkScalar sy = matrix->get(SkMatrix::kMScaleY);
257 if (SkScalarNearlyZero(sx - sy)) {
258 radius = SkScalarHalf(width * SkScalarAbs(sx));
259 }
260 }
261 if (radius > 0) {
262 SkRect clipBounds = SkRect::Make(rc->getBounds());
263 // if we return true, the caller may assume that the constructed shapes can be represented
264 // using SkFixed (after clipping), so we preflight that here.
265 if (!SkRectPriv::FitsInFixed(clipBounds)) {
266 return false;
267 }
268 fMode = mode;
269 fPaint = &paint;
270 fClip = nullptr;
271 fRC = rc;
272 fClipBounds = clipBounds;
273 fRadius = radius;
274 return true;
275 }
276 return false;
277}
278
279PtProcRec::Proc PtProcRec::chooseProc(SkBlitter** blitterPtr) {
280 Proc proc = nullptr;
281
282 SkBlitter* blitter = *blitterPtr;
283 if (fRC->isBW()) {
284 fClip = &fRC->bwRgn();
285 } else {
286 fWrapper.init(*fRC, blitter);
287 fClip = &fWrapper.getRgn();
288 blitter = fWrapper.getBlitter();
289 *blitterPtr = blitter;
290 }
291
292 // for our arrays
293 SkASSERT(0 == SkCanvas::kPoints_PointMode);
294 SkASSERT(1 == SkCanvas::kLines_PointMode);
295 SkASSERT(2 == SkCanvas::kPolygon_PointMode);
296 SkASSERT((unsigned)fMode <= (unsigned)SkCanvas::kPolygon_PointMode);
297
298 if (fPaint->isAntiAlias()) {
299 if (0 == fPaint->getStrokeWidth()) {
300 static const Proc gAAProcs[] = {
301 aa_square_proc, aa_line_hair_proc, aa_poly_hair_proc
302 };
303 proc = gAAProcs[fMode];
304 } else if (fPaint->getStrokeCap() != SkPaint::kRound_Cap) {
305 SkASSERT(SkCanvas::kPoints_PointMode == fMode);
306 proc = aa_square_proc;
307 }
308 } else { // BW
309 if (fRadius <= 0.5f) { // small radii and hairline
310 if (SkCanvas::kPoints_PointMode == fMode && fClip->isRect()) {
311 uint32_t value;
312 const SkPixmap* bm = blitter->justAnOpaqueColor(&value);
313 if (bm && kRGB_565_SkColorType == bm->colorType()) {
314 proc = bw_pt_rect_16_hair_proc;
315 } else if (bm && kN32_SkColorType == bm->colorType()) {
316 proc = bw_pt_rect_32_hair_proc;
317 } else {
318 proc = bw_pt_rect_hair_proc;
319 }
320 } else {
321 static Proc gBWProcs[] = {
322 bw_pt_hair_proc, bw_line_hair_proc, bw_poly_hair_proc
323 };
324 proc = gBWProcs[fMode];
325 }
326 } else {
327 proc = bw_square_proc;
328 }
329 }
330 return proc;
331}
332
333// each of these costs 8-bytes of stack space, so don't make it too large
334// must be even for lines/polygon to work
335#define MAX_DEV_PTS 32
336
337void SkDraw::drawPoints(SkCanvas::PointMode mode, size_t count,
338 const SkPoint pts[], const SkPaint& paint,
339 SkBaseDevice* device) const {
340 // if we're in lines mode, force count to be even
341 if (SkCanvas::kLines_PointMode == mode) {
342 count &= ~(size_t)1;
343 }
344
345 if ((long)count <= 0) {
346 return;
347 }
348
349 SkASSERT(pts != nullptr);
350 SkDEBUGCODE(this->validate();)
351
352 // nothing to draw
353 if (fRC->isEmpty()) {
354 return;
355 }
356
357 SkMatrix ctm = fMatrixProvider->localToDevice();
358 PtProcRec rec;
359 if (!device && rec.init(mode, paint, &ctm, fRC)) {
360 SkAutoBlitterChoose blitter(*this, nullptr, paint);
361
362 SkPoint devPts[MAX_DEV_PTS];
363 SkBlitter* bltr = blitter.get();
364 PtProcRec::Proc proc = rec.chooseProc(&bltr);
365 // we have to back up subsequent passes if we're in polygon mode
366 const size_t backup = (SkCanvas::kPolygon_PointMode == mode);
367
368 do {
369 int n = SkToInt(count);
370 if (n > MAX_DEV_PTS) {
371 n = MAX_DEV_PTS;
372 }
373 ctm.mapPoints(devPts, pts, n);
374 if (!SkScalarsAreFinite(&devPts[0].fX, n * 2)) {
375 return;
376 }
377 proc(rec, devPts, n, bltr);
378 pts += n - backup;
379 SkASSERT(SkToInt(count) >= n);
380 count -= n;
381 if (count > 0) {
382 count += backup;
383 }
384 } while (count != 0);
385 } else {
386 switch (mode) {
387 case SkCanvas::kPoints_PointMode: {
388 // temporarily mark the paint as filling.
389 SkPaint newPaint(paint);
390 newPaint.setStyle(SkPaint::kFill_Style);
391
392 SkScalar width = newPaint.getStrokeWidth();
393 SkScalar radius = SkScalarHalf(width);
394
395 if (newPaint.getStrokeCap() == SkPaint::kRound_Cap) {
396 if (device) {
397 for (size_t i = 0; i < count; ++i) {
398 SkRect r = SkRect::MakeLTRB(pts[i].fX - radius, pts[i].fY - radius,
399 pts[i].fX + radius, pts[i].fY + radius);
400 device->drawOval(r, newPaint);
401 }
402 } else {
403 SkPath path;
404 SkMatrix preMatrix;
405
406 path.addCircle(0, 0, radius);
407 for (size_t i = 0; i < count; i++) {
408 preMatrix.setTranslate(pts[i].fX, pts[i].fY);
409 // pass true for the last point, since we can modify
410 // then path then
411 path.setIsVolatile((count-1) == i);
412 this->drawPath(path, newPaint, &preMatrix, (count-1) == i);
413 }
414 }
415 } else {
416 SkRect r;
417
418 for (size_t i = 0; i < count; i++) {
419 r.fLeft = pts[i].fX - radius;
420 r.fTop = pts[i].fY - radius;
421 r.fRight = r.fLeft + width;
422 r.fBottom = r.fTop + width;
423 if (device) {
424 device->drawRect(r, newPaint);
425 } else {
426 this->drawRect(r, newPaint);
427 }
428 }
429 }
430 break;
431 }
432 case SkCanvas::kLines_PointMode:
433 if (2 == count && paint.getPathEffect()) {
434 // most likely a dashed line - see if it is one of the ones
435 // we can accelerate
436 SkStrokeRec rec(paint);
437 SkPathEffect::PointData pointData;
438
439 SkPath path;
440 path.moveTo(pts[0]);
441 path.lineTo(pts[1]);
442
443 SkRect cullRect = SkRect::Make(fRC->getBounds());
444
445 if (paint.getPathEffect()->asPoints(&pointData, path, rec, ctm, &cullRect)) {
446 // 'asPoints' managed to find some fast path
447
448 SkPaint newP(paint);
449 newP.setPathEffect(nullptr);
450 newP.setStyle(SkPaint::kFill_Style);
451
452 if (!pointData.fFirst.isEmpty()) {
453 if (device) {
454 device->drawPath(pointData.fFirst, newP);
455 } else {
456 this->drawPath(pointData.fFirst, newP);
457 }
458 }
459
460 if (!pointData.fLast.isEmpty()) {
461 if (device) {
462 device->drawPath(pointData.fLast, newP);
463 } else {
464 this->drawPath(pointData.fLast, newP);
465 }
466 }
467
468 if (pointData.fSize.fX == pointData.fSize.fY) {
469 // The rest of the dashed line can just be drawn as points
470 SkASSERT(pointData.fSize.fX == SkScalarHalf(newP.getStrokeWidth()));
471
472 if (SkPathEffect::PointData::kCircles_PointFlag & pointData.fFlags) {
473 newP.setStrokeCap(SkPaint::kRound_Cap);
474 } else {
475 newP.setStrokeCap(SkPaint::kButt_Cap);
476 }
477
478 if (device) {
479 device->drawPoints(SkCanvas::kPoints_PointMode,
480 pointData.fNumPoints,
481 pointData.fPoints,
482 newP);
483 } else {
484 this->drawPoints(SkCanvas::kPoints_PointMode,
485 pointData.fNumPoints,
486 pointData.fPoints,
487 newP,
488 device);
489 }
490 break;
491 } else {
492 // The rest of the dashed line must be drawn as rects
493 SkASSERT(!(SkPathEffect::PointData::kCircles_PointFlag &
494 pointData.fFlags));
495
496 SkRect r;
497
498 for (int i = 0; i < pointData.fNumPoints; ++i) {
499 r.setLTRB(pointData.fPoints[i].fX - pointData.fSize.fX,
500 pointData.fPoints[i].fY - pointData.fSize.fY,
501 pointData.fPoints[i].fX + pointData.fSize.fX,
502 pointData.fPoints[i].fY + pointData.fSize.fY);
503 if (device) {
504 device->drawRect(r, newP);
505 } else {
506 this->drawRect(r, newP);
507 }
508 }
509 }
510
511 break;
512 }
513 }
514 [[fallthrough]]; // couldn't take fast path
515 case SkCanvas::kPolygon_PointMode: {
516 count -= 1;
517 SkPath path;
518 SkPaint p(paint);
519 p.setStyle(SkPaint::kStroke_Style);
520 size_t inc = (SkCanvas::kLines_PointMode == mode) ? 2 : 1;
521 path.setIsVolatile(true);
522 for (size_t i = 0; i < count; i += inc) {
523 path.moveTo(pts[i]);
524 path.lineTo(pts[i+1]);
525 if (device) {
526 device->drawPath(path, p, true);
527 } else {
528 this->drawPath(path, p, nullptr, true);
529 }
530 path.rewind();
531 }
532 break;
533 }
534 }
535 }
536}
537
538static inline SkPoint compute_stroke_size(const SkPaint& paint, const SkMatrix& matrix) {
539 SkASSERT(matrix.rectStaysRect());
540 SkASSERT(SkPaint::kFill_Style != paint.getStyle());
541
542 SkVector size;
543 SkPoint pt = { paint.getStrokeWidth(), paint.getStrokeWidth() };
544 matrix.mapVectors(&size, &pt, 1);
545 return SkPoint::Make(SkScalarAbs(size.fX), SkScalarAbs(size.fY));
546}
547
548static bool easy_rect_join(const SkPaint& paint, const SkMatrix& matrix,
549 SkPoint* strokeSize) {
550 if (SkPaint::kMiter_Join != paint.getStrokeJoin() ||
551 paint.getStrokeMiter() < SK_ScalarSqrt2) {
552 return false;
553 }
554
555 *strokeSize = compute_stroke_size(paint, matrix);
556 return true;
557}
558
559SkDraw::RectType SkDraw::ComputeRectType(const SkPaint& paint,
560 const SkMatrix& matrix,
561 SkPoint* strokeSize) {
562 RectType rtype;
563 const SkScalar width = paint.getStrokeWidth();
564 const bool zeroWidth = (0 == width);
565 SkPaint::Style style = paint.getStyle();
566
567 if ((SkPaint::kStrokeAndFill_Style == style) && zeroWidth) {
568 style = SkPaint::kFill_Style;
569 }
570
571 if (paint.getPathEffect() || paint.getMaskFilter() ||
572 !matrix.rectStaysRect() || SkPaint::kStrokeAndFill_Style == style) {
573 rtype = kPath_RectType;
574 } else if (SkPaint::kFill_Style == style) {
575 rtype = kFill_RectType;
576 } else if (zeroWidth) {
577 rtype = kHair_RectType;
578 } else if (easy_rect_join(paint, matrix, strokeSize)) {
579 rtype = kStroke_RectType;
580 } else {
581 rtype = kPath_RectType;
582 }
583 return rtype;
584}
585
586static const SkPoint* rect_points(const SkRect& r) {
587 return reinterpret_cast<const SkPoint*>(&r);
588}
589
590static SkPoint* rect_points(SkRect& r) {
591 return reinterpret_cast<SkPoint*>(&r);
592}
593
594static void draw_rect_as_path(const SkDraw& orig, const SkRect& prePaintRect,
595 const SkPaint& paint, const SkMatrixProvider* matrixProvider) {
596 SkDraw draw(orig);
597 draw.fMatrixProvider = matrixProvider;
598 SkPath tmp;
599 tmp.addRect(prePaintRect);
600 tmp.setFillType(SkPathFillType::kWinding);
601 draw.drawPath(tmp, paint, nullptr, true);
602}
603
604void SkDraw::drawRect(const SkRect& prePaintRect, const SkPaint& paint,
605 const SkMatrix* paintMatrix, const SkRect* postPaintRect) const {
606 SkDEBUGCODE(this->validate();)
607
608 // nothing to draw
609 if (fRC->isEmpty()) {
610 return;
611 }
612
613 const SkMatrixProvider* matrixProvider = fMatrixProvider;
614 SkTLazy<SkPreConcatMatrixProvider> preConcatMatrixProvider;
615 if (paintMatrix) {
616 SkASSERT(postPaintRect);
617 matrixProvider = preConcatMatrixProvider.init(*matrixProvider, *paintMatrix);
618 } else {
619 SkASSERT(!postPaintRect);
620 }
621
622 SkMatrix ctm = fMatrixProvider->localToDevice();
623 SkPoint strokeSize;
624 RectType rtype = ComputeRectType(paint, ctm, &strokeSize);
625
626 if (kPath_RectType == rtype) {
627 draw_rect_as_path(*this, prePaintRect, paint, matrixProvider);
628 return;
629 }
630
631 SkRect devRect;
632 const SkRect& paintRect = paintMatrix ? *postPaintRect : prePaintRect;
633 // skip the paintMatrix when transforming the rect by the CTM
634 ctm.mapPoints(rect_points(devRect), rect_points(paintRect), 2);
635 devRect.sort();
636
637 // look for the quick exit, before we build a blitter
638 SkRect bbox = devRect;
639 if (paint.getStyle() != SkPaint::kFill_Style) {
640 // extra space for hairlines
641 if (paint.getStrokeWidth() == 0) {
642 bbox.outset(1, 1);
643 } else {
644 // For kStroke_RectType, strokeSize is already computed.
645 const SkPoint& ssize = (kStroke_RectType == rtype)
646 ? strokeSize
647 : compute_stroke_size(paint, ctm);
648 bbox.outset(SkScalarHalf(ssize.x()), SkScalarHalf(ssize.y()));
649 }
650 }
651 if (SkPathPriv::TooBigForMath(bbox)) {
652 return;
653 }
654
655 if (!SkRectPriv::FitsInFixed(bbox) && rtype != kHair_RectType) {
656 draw_rect_as_path(*this, prePaintRect, paint, matrixProvider);
657 return;
658 }
659
660 SkIRect ir = bbox.roundOut();
661 if (fRC->quickReject(ir)) {
662 return;
663 }
664
665 SkAutoBlitterChoose blitterStorage(*this, matrixProvider, paint);
666 const SkRasterClip& clip = *fRC;
667 SkBlitter* blitter = blitterStorage.get();
668
669 // we want to "fill" if we are kFill or kStrokeAndFill, since in the latter
670 // case we are also hairline (if we've gotten to here), which devolves to
671 // effectively just kFill
672 switch (rtype) {
673 case kFill_RectType:
674 if (paint.isAntiAlias()) {
675 SkScan::AntiFillRect(devRect, clip, blitter);
676 } else {
677 SkScan::FillRect(devRect, clip, blitter);
678 }
679 break;
680 case kStroke_RectType:
681 if (paint.isAntiAlias()) {
682 SkScan::AntiFrameRect(devRect, strokeSize, clip, blitter);
683 } else {
684 SkScan::FrameRect(devRect, strokeSize, clip, blitter);
685 }
686 break;
687 case kHair_RectType:
688 if (paint.isAntiAlias()) {
689 SkScan::AntiHairRect(devRect, clip, blitter);
690 } else {
691 SkScan::HairRect(devRect, clip, blitter);
692 }
693 break;
694 default:
695 SkDEBUGFAIL("bad rtype");
696 }
697}
698
699void SkDraw::drawDevMask(const SkMask& srcM, const SkPaint& paint) const {
700 if (srcM.fBounds.isEmpty()) {
701 return;
702 }
703
704 const SkMask* mask = &srcM;
705
706 SkMask dstM;
707 if (paint.getMaskFilter() &&
708 as_MFB(paint.getMaskFilter())
709 ->filterMask(&dstM, srcM, fMatrixProvider->localToDevice(), nullptr)) {
710 mask = &dstM;
711 }
712 SkAutoMaskFreeImage ami(dstM.fImage);
713
714 SkAutoBlitterChoose blitterChooser(*this, nullptr, paint);
715 SkBlitter* blitter = blitterChooser.get();
716
717 SkAAClipBlitterWrapper wrapper;
718 const SkRegion* clipRgn;
719
720 if (fRC->isBW()) {
721 clipRgn = &fRC->bwRgn();
722 } else {
723 wrapper.init(*fRC, blitter);
724 clipRgn = &wrapper.getRgn();
725 blitter = wrapper.getBlitter();
726 }
727 blitter->blitMaskRegion(*mask, *clipRgn);
728}
729
730static SkScalar fast_len(const SkVector& vec) {
731 SkScalar x = SkScalarAbs(vec.fX);
732 SkScalar y = SkScalarAbs(vec.fY);
733 if (x < y) {
734 using std::swap;
735 swap(x, y);
736 }
737 return x + SkScalarHalf(y);
738}
739
740bool SkDrawTreatAAStrokeAsHairline(SkScalar strokeWidth, const SkMatrix& matrix,
741 SkScalar* coverage) {
742 SkASSERT(strokeWidth > 0);
743 // We need to try to fake a thick-stroke with a modulated hairline.
744
745 if (matrix.hasPerspective()) {
746 return false;
747 }
748
749 SkVector src[2], dst[2];
750 src[0].set(strokeWidth, 0);
751 src[1].set(0, strokeWidth);
752 matrix.mapVectors(dst, src, 2);
753 SkScalar len0 = fast_len(dst[0]);
754 SkScalar len1 = fast_len(dst[1]);
755 if (len0 <= SK_Scalar1 && len1 <= SK_Scalar1) {
756 if (coverage) {
757 *coverage = SkScalarAve(len0, len1);
758 }
759 return true;
760 }
761 return false;
762}
763
764void SkDraw::drawRRect(const SkRRect& rrect, const SkPaint& paint) const {
765 SkDEBUGCODE(this->validate());
766
767 if (fRC->isEmpty()) {
768 return;
769 }
770
771 SkMatrix ctm = fMatrixProvider->localToDevice();
772 {
773 // TODO: Investigate optimizing these options. They are in the same
774 // order as SkDraw::drawPath, which handles each case. It may be
775 // that there is no way to optimize for these using the SkRRect path.
776 SkScalar coverage;
777 if (SkDrawTreatAsHairline(paint, ctm, &coverage)) {
778 goto DRAW_PATH;
779 }
780
781 if (paint.getPathEffect() || paint.getStyle() != SkPaint::kFill_Style) {
782 goto DRAW_PATH;
783 }
784 }
785
786 if (paint.getMaskFilter()) {
787 // Transform the rrect into device space.
788 SkRRect devRRect;
789 if (rrect.transform(ctm, &devRRect)) {
790 SkAutoBlitterChoose blitter(*this, nullptr, paint);
791 if (as_MFB(paint.getMaskFilter())->filterRRect(devRRect, ctm, *fRC, blitter.get())) {
792 return; // filterRRect() called the blitter, so we're done
793 }
794 }
795 }
796
797DRAW_PATH:
798 // Now fall back to the default case of using a path.
799 SkPath path;
800 path.addRRect(rrect);
801 this->drawPath(path, paint, nullptr, true);
802}
803
804SkScalar SkDraw::ComputeResScaleForStroking(const SkMatrix& matrix) {
805 // Not sure how to handle perspective differently, so we just don't try (yet)
806 SkScalar sx = SkPoint::Length(matrix[SkMatrix::kMScaleX], matrix[SkMatrix::kMSkewY]);
807 SkScalar sy = SkPoint::Length(matrix[SkMatrix::kMSkewX], matrix[SkMatrix::kMScaleY]);
808 if (SkScalarsAreFinite(sx, sy)) {
809 SkScalar scale = std::max(sx, sy);
810 if (scale > 0) {
811 return scale;
812 }
813 }
814 return 1;
815}
816
817void SkDraw::drawDevPath(const SkPath& devPath, const SkPaint& paint, bool drawCoverage,
818 SkBlitter* customBlitter, bool doFill) const {
819 if (SkPathPriv::TooBigForMath(devPath)) {
820 return;
821 }
822 SkBlitter* blitter = nullptr;
823 SkAutoBlitterChoose blitterStorage;
824 if (nullptr == customBlitter) {
825 blitter = blitterStorage.choose(*this, nullptr, paint, drawCoverage);
826 } else {
827 blitter = customBlitter;
828 }
829
830 if (paint.getMaskFilter()) {
831 SkStrokeRec::InitStyle style = doFill ? SkStrokeRec::kFill_InitStyle
832 : SkStrokeRec::kHairline_InitStyle;
833 if (as_MFB(paint.getMaskFilter())
834 ->filterPath(devPath, fMatrixProvider->localToDevice(), *fRC, blitter, style)) {
835 return; // filterPath() called the blitter, so we're done
836 }
837 }
838
839 void (*proc)(const SkPathView&, const SkRasterClip&, SkBlitter*);
840 if (doFill) {
841 if (paint.isAntiAlias()) {
842 proc = SkScan::AntiFillPath;
843 } else {
844 proc = SkScan::FillPath;
845 }
846 } else { // hairline
847 if (paint.isAntiAlias()) {
848 switch (paint.getStrokeCap()) {
849 case SkPaint::kButt_Cap:
850 proc = SkScan::AntiHairPath;
851 break;
852 case SkPaint::kSquare_Cap:
853 proc = SkScan::AntiHairSquarePath;
854 break;
855 case SkPaint::kRound_Cap:
856 proc = SkScan::AntiHairRoundPath;
857 break;
858 default:
859 proc SK_INIT_TO_AVOID_WARNING;
860 SkDEBUGFAIL("unknown paint cap type");
861 }
862 } else {
863 switch (paint.getStrokeCap()) {
864 case SkPaint::kButt_Cap:
865 proc = SkScan::HairPath;
866 break;
867 case SkPaint::kSquare_Cap:
868 proc = SkScan::HairSquarePath;
869 break;
870 case SkPaint::kRound_Cap:
871 proc = SkScan::HairRoundPath;
872 break;
873 default:
874 proc SK_INIT_TO_AVOID_WARNING;
875 SkDEBUGFAIL("unknown paint cap type");
876 }
877 }
878 }
879
880 proc(devPath.view(), *fRC, blitter);
881}
882
883void SkDraw::drawPath(const SkPath& origSrcPath, const SkPaint& origPaint,
884 const SkMatrix* prePathMatrix, bool pathIsMutable,
885 bool drawCoverage, SkBlitter* customBlitter) const {
886 SkDEBUGCODE(this->validate();)
887
888 // nothing to draw
889 if (fRC->isEmpty()) {
890 return;
891 }
892
893 SkPath* pathPtr = (SkPath*)&origSrcPath;
894 bool doFill = true;
895 SkPath tmpPathStorage;
896 SkPath* tmpPath = &tmpPathStorage;
897 const SkMatrixProvider* matrixProvider = fMatrixProvider;
898 SkTLazy<SkPreConcatMatrixProvider> preConcatMatrixProvider;
899 tmpPath->setIsVolatile(true);
900
901 if (prePathMatrix) {
902 if (origPaint.getPathEffect() || origPaint.getStyle() != SkPaint::kFill_Style) {
903 SkPath* result = pathPtr;
904
905 if (!pathIsMutable) {
906 result = tmpPath;
907 pathIsMutable = true;
908 }
909 pathPtr->transform(*prePathMatrix, result);
910 pathPtr = result;
911 } else {
912 matrixProvider = preConcatMatrixProvider.init(*matrixProvider, *prePathMatrix);
913 }
914 }
915 // at this point we're done with prePathMatrix
916 SkDEBUGCODE(prePathMatrix = (const SkMatrix*)0x50FF8001;)
917
918 SkTCopyOnFirstWrite<SkPaint> paint(origPaint);
919
920 {
921 SkScalar coverage;
922 if (SkDrawTreatAsHairline(origPaint, matrixProvider->localToDevice(), &coverage)) {
923 if (SK_Scalar1 == coverage) {
924 paint.writable()->setStrokeWidth(0);
925 } else if (SkBlendMode_SupportsCoverageAsAlpha(origPaint.getBlendMode())) {
926 U8CPU newAlpha;
927#if 0
928 newAlpha = SkToU8(SkScalarRoundToInt(coverage *
929 origPaint.getAlpha()));
930#else
931 // this is the old technique, which we preserve for now so
932 // we don't change previous results (testing)
933 // the new way seems fine, its just (a tiny bit) different
934 int scale = (int)(coverage * 256);
935 newAlpha = origPaint.getAlpha() * scale >> 8;
936#endif
937 SkPaint* writablePaint = paint.writable();
938 writablePaint->setStrokeWidth(0);
939 writablePaint->setAlpha(newAlpha);
940 }
941 }
942 }
943
944 if (paint->getPathEffect() || paint->getStyle() != SkPaint::kFill_Style) {
945 SkRect cullRect;
946 const SkRect* cullRectPtr = nullptr;
947 if (this->computeConservativeLocalClipBounds(&cullRect)) {
948 cullRectPtr = &cullRect;
949 }
950 doFill = paint->getFillPath(*pathPtr, tmpPath, cullRectPtr,
951 ComputeResScaleForStroking(fMatrixProvider->localToDevice()));
952 pathPtr = tmpPath;
953 }
954
955 // avoid possibly allocating a new path in transform if we can
956 SkPath* devPathPtr = pathIsMutable ? pathPtr : tmpPath;
957
958 // transform the path into device space
959 pathPtr->transform(matrixProvider->localToDevice(), devPathPtr);
960
961 this->drawDevPath(*devPathPtr, *paint, drawCoverage, customBlitter, doFill);
962}
963
964void SkDraw::drawBitmapAsMask(const SkBitmap& bitmap, const SkPaint& paint) const {
965 SkASSERT(bitmap.colorType() == kAlpha_8_SkColorType);
966
967 // nothing to draw
968 if (fRC->isEmpty()) {
969 return;
970 }
971
972 SkMatrix ctm = fMatrixProvider->localToDevice();
973 if (SkTreatAsSprite(ctm, bitmap.dimensions(), paint)) {
974 int ix = SkScalarRoundToInt(ctm.getTranslateX());
975 int iy = SkScalarRoundToInt(ctm.getTranslateY());
976
977 SkPixmap pmap;
978 if (!bitmap.peekPixels(&pmap)) {
979 return;
980 }
981 SkMask mask;
982 mask.fBounds.setXYWH(ix, iy, pmap.width(), pmap.height());
983 mask.fFormat = SkMask::kA8_Format;
984 mask.fRowBytes = SkToU32(pmap.rowBytes());
985 // fImage is typed as writable, but in this case it is used read-only
986 mask.fImage = (uint8_t*)pmap.addr8(0, 0);
987
988 this->drawDevMask(mask, paint);
989 } else { // need to xform the bitmap first
990 SkRect r;
991 SkMask mask;
992
993 r.setIWH(bitmap.width(), bitmap.height());
994 ctm.mapRect(&r);
995 r.round(&mask.fBounds);
996
997 // set the mask's bounds to the transformed bitmap-bounds,
998 // clipped to the actual device and further limited by the clip bounds
999 {
1000 SkASSERT(fDst.bounds().contains(fRC->getBounds()));
1001 SkIRect devBounds = fDst.bounds();
1002 devBounds.intersect(fRC->getBounds().makeOutset(1, 1));
1003 // need intersect(l, t, r, b) on irect
1004 if (!mask.fBounds.intersect(devBounds)) {
1005 return;
1006 }
1007 }
1008
1009 mask.fFormat = SkMask::kA8_Format;
1010 mask.fRowBytes = SkAlign4(mask.fBounds.width());
1011 size_t size = mask.computeImageSize();
1012 if (0 == size) {
1013 // the mask is too big to allocated, draw nothing
1014 return;
1015 }
1016
1017 // allocate (and clear) our temp buffer to hold the transformed bitmap
1018 SkAutoTMalloc<uint8_t> storage(size);
1019 mask.fImage = storage.get();
1020 memset(mask.fImage, 0, size);
1021
1022 // now draw our bitmap(src) into mask(dst), transformed by the matrix
1023 {
1024 SkBitmap device;
1025 device.installPixels(SkImageInfo::MakeA8(mask.fBounds.width(), mask.fBounds.height()),
1026 mask.fImage, mask.fRowBytes);
1027
1028 SkCanvas c(device);
1029 // need the unclipped top/left for the translate
1030 c.translate(-SkIntToScalar(mask.fBounds.fLeft),
1031 -SkIntToScalar(mask.fBounds.fTop));
1032 c.concat(ctm);
1033
1034 // We can't call drawBitmap, or we'll infinitely recurse. Instead
1035 // we manually build a shader and draw that into our new mask
1036 SkPaint tmpPaint;
1037 tmpPaint.setAntiAlias(paint.isAntiAlias());
1038 tmpPaint.setDither(paint.isDither());
1039 tmpPaint.setFilterQuality(paint.getFilterQuality());
1040 SkPaint paintWithShader = make_paint_with_image(tmpPaint, bitmap);
1041 SkRect rr;
1042 rr.setIWH(bitmap.width(), bitmap.height());
1043 c.drawRect(rr, paintWithShader);
1044 }
1045 this->drawDevMask(mask, paint);
1046 }
1047}
1048
1049static bool clipped_out(const SkMatrix& m, const SkRasterClip& c,
1050 const SkRect& srcR) {
1051 SkRect dstR;
1052 m.mapRect(&dstR, srcR);
1053 return c.quickReject(dstR.roundOut());
1054}
1055
1056static bool clipped_out(const SkMatrix& matrix, const SkRasterClip& clip,
1057 int width, int height) {
1058 SkRect r;
1059 r.setIWH(width, height);
1060 return clipped_out(matrix, clip, r);
1061}
1062
1063static bool clipHandlesSprite(const SkRasterClip& clip, int x, int y, const SkPixmap& pmap) {
1064 return clip.isBW() || clip.quickContains(x, y, x + pmap.width(), y + pmap.height());
1065}
1066
1067void SkDraw::drawBitmap(const SkBitmap& bitmap, const SkMatrix& prematrix,
1068 const SkRect* dstBounds, const SkPaint& origPaint) const {
1069 SkDEBUGCODE(this->validate();)
1070
1071 // nothing to draw
1072 if (fRC->isEmpty() ||
1073 bitmap.width() == 0 || bitmap.height() == 0 ||
1074 bitmap.colorType() == kUnknown_SkColorType) {
1075 return;
1076 }
1077
1078 SkTCopyOnFirstWrite<SkPaint> paint(origPaint);
1079 if (origPaint.getStyle() != SkPaint::kFill_Style) {
1080 paint.writable()->setStyle(SkPaint::kFill_Style);
1081 }
1082
1083 SkPreConcatMatrixProvider matrixProvider(*fMatrixProvider, prematrix);
1084 SkMatrix matrix = matrixProvider.localToDevice();
1085
1086 if (clipped_out(matrix, *fRC, bitmap.width(), bitmap.height())) {
1087 return;
1088 }
1089
1090 if (bitmap.colorType() != kAlpha_8_SkColorType
1091 && SkTreatAsSprite(matrix, bitmap.dimensions(), *paint)) {
1092 //
1093 // It is safe to call lock pixels now, since we know the matrix is
1094 // (more or less) identity.
1095 //
1096 SkPixmap pmap;
1097 if (!bitmap.peekPixels(&pmap)) {
1098 return;
1099 }
1100 int ix = SkScalarRoundToInt(matrix.getTranslateX());
1101 int iy = SkScalarRoundToInt(matrix.getTranslateY());
1102 if (clipHandlesSprite(*fRC, ix, iy, pmap)) {
1103 SkSTArenaAlloc<kSkBlitterContextSize> allocator;
1104 // blitter will be owned by the allocator.
1105 SkBlitter* blitter = SkBlitter::ChooseSprite(fDst, *paint, pmap, ix, iy, &allocator,
1106 fRC->clipShader());
1107 if (blitter) {
1108 SkScan::FillIRect(SkIRect::MakeXYWH(ix, iy, pmap.width(), pmap.height()),
1109 *fRC, blitter);
1110 return;
1111 }
1112 // if !blitter, then we fall-through to the slower case
1113 }
1114 }
1115
1116 // now make a temp draw on the stack, and use it
1117 //
1118 SkDraw draw(*this);
1119 draw.fMatrixProvider = &matrixProvider;
1120
1121 if (bitmap.colorType() == kAlpha_8_SkColorType && !paint->getColorFilter()) {
1122 draw.drawBitmapAsMask(bitmap, *paint);
1123 } else {
1124 SkPaint paintWithShader = make_paint_with_image(*paint, bitmap);
1125 const SkRect srcBounds = SkRect::MakeIWH(bitmap.width(), bitmap.height());
1126 if (dstBounds) {
1127 this->drawRect(srcBounds, paintWithShader, &prematrix, dstBounds);
1128 } else {
1129 draw.drawRect(srcBounds, paintWithShader);
1130 }
1131 }
1132}
1133
1134void SkDraw::drawSprite(const SkBitmap& bitmap, int x, int y, const SkPaint& origPaint) const {
1135 SkDEBUGCODE(this->validate();)
1136
1137 // nothing to draw
1138 if (fRC->isEmpty() ||
1139 bitmap.width() == 0 || bitmap.height() == 0 ||
1140 bitmap.colorType() == kUnknown_SkColorType) {
1141 return;
1142 }
1143
1144 const SkIRect bounds = SkIRect::MakeXYWH(x, y, bitmap.width(), bitmap.height());
1145
1146 if (fRC->quickReject(bounds)) {
1147 return; // nothing to draw
1148 }
1149
1150 SkPaint paint(origPaint);
1151 paint.setStyle(SkPaint::kFill_Style);
1152
1153 SkPixmap pmap;
1154 if (!bitmap.peekPixels(&pmap)) {
1155 return;
1156 }
1157
1158 if (nullptr == paint.getColorFilter() && clipHandlesSprite(*fRC, x, y, pmap)) {
1159 // blitter will be owned by the allocator.
1160 SkSTArenaAlloc<kSkBlitterContextSize> allocator;
1161 SkBlitter* blitter = SkBlitter::ChooseSprite(fDst, paint, pmap, x, y, &allocator,
1162 fRC->clipShader());
1163 if (blitter) {
1164 SkScan::FillIRect(bounds, *fRC, blitter);
1165 return;
1166 }
1167 }
1168
1169 SkMatrix matrix;
1170 SkRect r;
1171
1172 // get a scalar version of our rect
1173 r.set(bounds);
1174
1175 // create shader with offset
1176 matrix.setTranslate(r.fLeft, r.fTop);
1177 SkPaint paintWithShader = make_paint_with_image(paint, bitmap, &matrix);
1178 SkDraw draw(*this);
1179 SkOverrideDeviceMatrixProvider matrixProvider(*fMatrixProvider, SkMatrix::I());
1180 draw.fMatrixProvider = &matrixProvider;
1181 // call ourself with a rect
1182 draw.drawRect(r, paintWithShader);
1183}
1184
1185////////////////////////////////////////////////////////////////////////////////////////////////
1186
1187#ifdef SK_DEBUG
1188
1189void SkDraw::validate() const {
1190 SkASSERT(fMatrixProvider != nullptr);
1191 SkASSERT(fRC != nullptr);
1192
1193 const SkIRect& cr = fRC->getBounds();
1194 SkIRect br;
1195
1196 br.setWH(fDst.width(), fDst.height());
1197 SkASSERT(cr.isEmpty() || br.contains(cr));
1198}
1199
1200#endif
1201
1202////////////////////////////////////////////////////////////////////////////////////////////////
1203
1204#include "include/core/SkPath.h"
1205#include "include/core/SkRegion.h"
1206#include "src/core/SkBlitter.h"
1207#include "src/core/SkDraw.h"
1208
1209bool SkDraw::ComputeMaskBounds(const SkRect& devPathBounds, const SkIRect* clipBounds,
1210 const SkMaskFilter* filter, const SkMatrix* filterMatrix,
1211 SkIRect* bounds) {
1212 // init our bounds from the path
1213 *bounds = devPathBounds.makeOutset(SK_ScalarHalf, SK_ScalarHalf).roundOut();
1214
1215 SkIPoint margin = SkIPoint::Make(0, 0);
1216 if (filter) {
1217 SkASSERT(filterMatrix);
1218
1219 SkMask srcM, dstM;
1220
1221 srcM.fBounds = *bounds;
1222 srcM.fFormat = SkMask::kA8_Format;
1223 if (!as_MFB(filter)->filterMask(&dstM, srcM, *filterMatrix, &margin)) {
1224 return false;
1225 }
1226 }
1227
1228 // (possibly) trim the bounds to reflect the clip
1229 // (plus whatever slop the filter needs)
1230 if (clipBounds) {
1231 // Ugh. Guard against gigantic margins from wacky filters. Without this
1232 // check we can request arbitrary amounts of slop beyond our visible
1233 // clip, and bring down the renderer (at least on finite RAM machines
1234 // like handsets, etc.). Need to balance this invented value between
1235 // quality of large filters like blurs, and the corresponding memory
1236 // requests.
1237 static const int MAX_MARGIN = 128;
1238 if (!bounds->intersect(clipBounds->makeOutset(std::min(margin.fX, MAX_MARGIN),
1239 std::min(margin.fY, MAX_MARGIN)))) {
1240 return false;
1241 }
1242 }
1243
1244 return true;
1245}
1246
1247static void draw_into_mask(const SkMask& mask, const SkPath& devPath,
1248 SkStrokeRec::InitStyle style) {
1249 SkDraw draw;
1250 if (!draw.fDst.reset(mask)) {
1251 return;
1252 }
1253
1254 SkRasterClip clip;
1255 SkMatrix matrix;
1256 SkPaint paint;
1257
1258 clip.setRect(SkIRect::MakeWH(mask.fBounds.width(), mask.fBounds.height()));
1259 matrix.setTranslate(-SkIntToScalar(mask.fBounds.fLeft),
1260 -SkIntToScalar(mask.fBounds.fTop));
1261
1262 SkSimpleMatrixProvider matrixProvider(matrix);
1263 draw.fRC = &clip;
1264 draw.fMatrixProvider = &matrixProvider;
1265 paint.setAntiAlias(true);
1266 switch (style) {
1267 case SkStrokeRec::kHairline_InitStyle:
1268 SkASSERT(!paint.getStrokeWidth());
1269 paint.setStyle(SkPaint::kStroke_Style);
1270 break;
1271 case SkStrokeRec::kFill_InitStyle:
1272 SkASSERT(paint.getStyle() == SkPaint::kFill_Style);
1273 break;
1274
1275 }
1276 draw.drawPath(devPath, paint);
1277}
1278
1279bool SkDraw::DrawToMask(const SkPath& devPath, const SkIRect* clipBounds,
1280 const SkMaskFilter* filter, const SkMatrix* filterMatrix,
1281 SkMask* mask, SkMask::CreateMode mode,
1282 SkStrokeRec::InitStyle style) {
1283 if (devPath.isEmpty()) {
1284 return false;
1285 }
1286
1287 if (SkMask::kJustRenderImage_CreateMode != mode) {
1288 if (!ComputeMaskBounds(devPath.getBounds(), clipBounds, filter,
1289 filterMatrix, &mask->fBounds))
1290 return false;
1291 }
1292
1293 if (SkMask::kComputeBoundsAndRenderImage_CreateMode == mode) {
1294 mask->fFormat = SkMask::kA8_Format;
1295 mask->fRowBytes = mask->fBounds.width();
1296 size_t size = mask->computeImageSize();
1297 if (0 == size) {
1298 // we're too big to allocate the mask, abort
1299 return false;
1300 }
1301 mask->fImage = SkMask::AllocImage(size, SkMask::kZeroInit_Alloc);
1302 }
1303
1304 if (SkMask::kJustComputeBounds_CreateMode != mode) {
1305 draw_into_mask(*mask, devPath, style);
1306 }
1307
1308 return true;
1309}
1310