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 "SkDraw.h"
9
10 #include "SkArenaAlloc.h"
11 #include "SkAutoBlitterChoose.h"
12 #include "SkBlendModePriv.h"
13 #include "SkBlitter.h"
14 #include "SkCanvas.h"
15 #include "SkColorData.h"
16 #include "SkDevice.h"
17 #include "SkDrawProcs.h"
18 #include "SkMaskFilterBase.h"
19 #include "SkMacros.h"
20 #include "SkMatrix.h"
21 #include "SkMatrixUtils.h"
22 #include "SkPaint.h"
23 #include "SkPathEffect.h"
24 #include "SkPathPriv.h"
25 #include "SkRRect.h"
26 #include "SkRasterClip.h"
27 #include "SkRectPriv.h"
28 #include "SkScan.h"
29 #include "SkShader.h"
30 #include "SkString.h"
31 #include "SkStroke.h"
32 #include "SkStrokeRec.h"
33 #include "SkTLazy.h"
34 #include "SkTemplates.h"
35 #include "SkTo.h"
36 #include "SkUtils.h"
37
38 #include <utility>
39
make_paint_with_image(const SkPaint & origPaint,const SkBitmap & bitmap,SkMatrix * matrix=nullptr)40 static SkPaint make_paint_with_image(
41 const SkPaint& origPaint, const SkBitmap& bitmap, SkMatrix* matrix = nullptr) {
42 SkPaint paint(origPaint);
43 paint.setShader(SkMakeBitmapShader(bitmap, SkShader::kClamp_TileMode,
44 SkShader::kClamp_TileMode, matrix,
45 kNever_SkCopyPixelsMode));
46 return paint;
47 }
48
49 ///////////////////////////////////////////////////////////////////////////////
50
SkDraw()51 SkDraw::SkDraw() {}
52
computeConservativeLocalClipBounds(SkRect * localBounds) const53 bool SkDraw::computeConservativeLocalClipBounds(SkRect* localBounds) const {
54 if (fRC->isEmpty()) {
55 return false;
56 }
57
58 SkMatrix inverse;
59 if (!fMatrix->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
drawPaint(const SkPaint & paint) const72 void SkDraw::drawPaint(const SkPaint& paint) const {
73 SkDEBUGCODE(this->validate();)
74
75 if (fRC->isEmpty()) {
76 return;
77 }
78
79 SkIRect devRect;
80 devRect.set(0, 0, fDst.width(), fDst.height());
81
82 SkAutoBlitterChoose blitter(*this, nullptr, paint);
83 SkScan::FillIRect(devRect, *fRC, blitter.get());
84 }
85
86 ///////////////////////////////////////////////////////////////////////////////
87
88 struct 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
105 private:
106 SkAAClipBlitterWrapper fWrapper;
107 };
108
bw_pt_rect_hair_proc(const PtProcRec & rec,const SkPoint devPts[],int count,SkBlitter * blitter)109 static 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
bw_pt_rect_16_hair_proc(const PtProcRec & rec,const SkPoint devPts[],int count,SkBlitter * blitter)123 static 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
bw_pt_rect_32_hair_proc(const PtProcRec & rec,const SkPoint devPts[],int count,SkBlitter * blitter)144 static 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
bw_pt_hair_proc(const PtProcRec & rec,const SkPoint devPts[],int count,SkBlitter * blitter)165 static 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
bw_line_hair_proc(const PtProcRec & rec,const SkPoint devPts[],int count,SkBlitter * blitter)176 static 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
bw_poly_hair_proc(const PtProcRec & rec,const SkPoint devPts[],int count,SkBlitter * blitter)183 static 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
aa_line_hair_proc(const PtProcRec & rec,const SkPoint devPts[],int count,SkBlitter * blitter)190 static 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
aa_poly_hair_proc(const PtProcRec & rec,const SkPoint devPts[],int count,SkBlitter * blitter)197 static 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
make_square_rad(SkPoint center,SkScalar radius)204 static 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
make_xrect(const SkRect & r)211 static 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
bw_square_proc(const PtProcRec & rec,const SkPoint devPts[],int count,SkBlitter * blitter)219 static 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
aa_square_proc(const PtProcRec & rec,const SkPoint devPts[],int count,SkBlitter * blitter)229 static 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 guy returns true, then chooseProc() must return a valid proc
init(SkCanvas::PointMode mode,const SkPaint & paint,const SkMatrix * matrix,const SkRasterClip * rc)240 bool 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
chooseProc(SkBlitter ** blitterPtr)279 PtProcRec::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
drawPoints(SkCanvas::PointMode mode,size_t count,const SkPoint pts[],const SkPaint & paint,SkBaseDevice * device) const337 void 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 PtProcRec rec;
358 if (!device && rec.init(mode, paint, fMatrix, fRC)) {
359 SkAutoBlitterChoose blitter(*this, nullptr, paint);
360
361 SkPoint devPts[MAX_DEV_PTS];
362 const SkMatrix* matrix = fMatrix;
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 matrix->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,
446 *fMatrix, &cullRect)) {
447 // 'asPoints' managed to find some fast path
448
449 SkPaint newP(paint);
450 newP.setPathEffect(nullptr);
451 newP.setStyle(SkPaint::kFill_Style);
452
453 if (!pointData.fFirst.isEmpty()) {
454 if (device) {
455 device->drawPath(pointData.fFirst, newP);
456 } else {
457 this->drawPath(pointData.fFirst, newP);
458 }
459 }
460
461 if (!pointData.fLast.isEmpty()) {
462 if (device) {
463 device->drawPath(pointData.fLast, newP);
464 } else {
465 this->drawPath(pointData.fLast, newP);
466 }
467 }
468
469 if (pointData.fSize.fX == pointData.fSize.fY) {
470 // The rest of the dashed line can just be drawn as points
471 SkASSERT(pointData.fSize.fX == SkScalarHalf(newP.getStrokeWidth()));
472
473 if (SkPathEffect::PointData::kCircles_PointFlag & pointData.fFlags) {
474 newP.setStrokeCap(SkPaint::kRound_Cap);
475 } else {
476 newP.setStrokeCap(SkPaint::kButt_Cap);
477 }
478
479 if (device) {
480 device->drawPoints(SkCanvas::kPoints_PointMode,
481 pointData.fNumPoints,
482 pointData.fPoints,
483 newP);
484 } else {
485 this->drawPoints(SkCanvas::kPoints_PointMode,
486 pointData.fNumPoints,
487 pointData.fPoints,
488 newP,
489 device);
490 }
491 break;
492 } else {
493 // The rest of the dashed line must be drawn as rects
494 SkASSERT(!(SkPathEffect::PointData::kCircles_PointFlag &
495 pointData.fFlags));
496
497 SkRect r;
498
499 for (int i = 0; i < pointData.fNumPoints; ++i) {
500 r.set(pointData.fPoints[i].fX - pointData.fSize.fX,
501 pointData.fPoints[i].fY - pointData.fSize.fY,
502 pointData.fPoints[i].fX + pointData.fSize.fX,
503 pointData.fPoints[i].fY + pointData.fSize.fY);
504 if (device) {
505 device->drawRect(r, newP);
506 } else {
507 this->drawRect(r, newP);
508 }
509 }
510 }
511
512 break;
513 }
514 }
515 // couldn't take fast path so fall through!
516 case SkCanvas::kPolygon_PointMode: {
517 count -= 1;
518 SkPath path;
519 SkPaint p(paint);
520 p.setStyle(SkPaint::kStroke_Style);
521 size_t inc = (SkCanvas::kLines_PointMode == mode) ? 2 : 1;
522 path.setIsVolatile(true);
523 for (size_t i = 0; i < count; i += inc) {
524 path.moveTo(pts[i]);
525 path.lineTo(pts[i+1]);
526 if (device) {
527 device->drawPath(path, p, true);
528 } else {
529 this->drawPath(path, p, nullptr, true);
530 }
531 path.rewind();
532 }
533 break;
534 }
535 }
536 }
537 }
538
compute_stroke_size(const SkPaint & paint,const SkMatrix & matrix)539 static inline SkPoint compute_stroke_size(const SkPaint& paint, const SkMatrix& matrix) {
540 SkASSERT(matrix.rectStaysRect());
541 SkASSERT(SkPaint::kFill_Style != paint.getStyle());
542
543 SkVector size;
544 SkPoint pt = { paint.getStrokeWidth(), paint.getStrokeWidth() };
545 matrix.mapVectors(&size, &pt, 1);
546 return SkPoint::Make(SkScalarAbs(size.fX), SkScalarAbs(size.fY));
547 }
548
easy_rect_join(const SkPaint & paint,const SkMatrix & matrix,SkPoint * strokeSize)549 static bool easy_rect_join(const SkPaint& paint, const SkMatrix& matrix,
550 SkPoint* strokeSize) {
551 if (SkPaint::kMiter_Join != paint.getStrokeJoin() ||
552 paint.getStrokeMiter() < SK_ScalarSqrt2) {
553 return false;
554 }
555
556 *strokeSize = compute_stroke_size(paint, matrix);
557 return true;
558 }
559
ComputeRectType(const SkPaint & paint,const SkMatrix & matrix,SkPoint * strokeSize)560 SkDraw::RectType SkDraw::ComputeRectType(const SkPaint& paint,
561 const SkMatrix& matrix,
562 SkPoint* strokeSize) {
563 RectType rtype;
564 const SkScalar width = paint.getStrokeWidth();
565 const bool zeroWidth = (0 == width);
566 SkPaint::Style style = paint.getStyle();
567
568 if ((SkPaint::kStrokeAndFill_Style == style) && zeroWidth) {
569 style = SkPaint::kFill_Style;
570 }
571
572 if (paint.getPathEffect() || paint.getMaskFilter() ||
573 !matrix.rectStaysRect() || SkPaint::kStrokeAndFill_Style == style) {
574 rtype = kPath_RectType;
575 } else if (SkPaint::kFill_Style == style) {
576 rtype = kFill_RectType;
577 } else if (zeroWidth) {
578 rtype = kHair_RectType;
579 } else if (easy_rect_join(paint, matrix, strokeSize)) {
580 rtype = kStroke_RectType;
581 } else {
582 rtype = kPath_RectType;
583 }
584 return rtype;
585 }
586
rect_points(const SkRect & r)587 static const SkPoint* rect_points(const SkRect& r) {
588 return reinterpret_cast<const SkPoint*>(&r);
589 }
590
rect_points(SkRect & r)591 static SkPoint* rect_points(SkRect& r) {
592 return reinterpret_cast<SkPoint*>(&r);
593 }
594
draw_rect_as_path(const SkDraw & orig,const SkRect & prePaintRect,const SkPaint & paint,const SkMatrix * matrix)595 static void draw_rect_as_path(const SkDraw& orig, const SkRect& prePaintRect,
596 const SkPaint& paint, const SkMatrix* matrix) {
597 SkDraw draw(orig);
598 draw.fMatrix = matrix;
599 SkPath tmp;
600 tmp.addRect(prePaintRect);
601 tmp.setFillType(SkPath::kWinding_FillType);
602 draw.drawPath(tmp, paint, nullptr, true);
603 }
604
drawRect(const SkRect & prePaintRect,const SkPaint & paint,const SkMatrix * paintMatrix,const SkRect * postPaintRect) const605 void SkDraw::drawRect(const SkRect& prePaintRect, const SkPaint& paint,
606 const SkMatrix* paintMatrix, const SkRect* postPaintRect) const {
607 SkDEBUGCODE(this->validate();)
608
609 // nothing to draw
610 if (fRC->isEmpty()) {
611 return;
612 }
613
614 const SkMatrix* matrix;
615 SkMatrix combinedMatrixStorage;
616 if (paintMatrix) {
617 SkASSERT(postPaintRect);
618 combinedMatrixStorage.setConcat(*fMatrix, *paintMatrix);
619 matrix = &combinedMatrixStorage;
620 } else {
621 SkASSERT(!postPaintRect);
622 matrix = fMatrix;
623 }
624
625 SkPoint strokeSize;
626 RectType rtype = ComputeRectType(paint, *fMatrix, &strokeSize);
627
628 if (kPath_RectType == rtype) {
629 draw_rect_as_path(*this, prePaintRect, paint, matrix);
630 return;
631 }
632
633 SkRect devRect;
634 const SkRect& paintRect = paintMatrix ? *postPaintRect : prePaintRect;
635 // skip the paintMatrix when transforming the rect by the CTM
636 fMatrix->mapPoints(rect_points(devRect), rect_points(paintRect), 2);
637 devRect.sort();
638
639 // look for the quick exit, before we build a blitter
640 SkRect bbox = devRect;
641 if (paint.getStyle() != SkPaint::kFill_Style) {
642 // extra space for hairlines
643 if (paint.getStrokeWidth() == 0) {
644 bbox.outset(1, 1);
645 } else {
646 // For kStroke_RectType, strokeSize is already computed.
647 const SkPoint& ssize = (kStroke_RectType == rtype)
648 ? strokeSize
649 : compute_stroke_size(paint, *fMatrix);
650 bbox.outset(SkScalarHalf(ssize.x()), SkScalarHalf(ssize.y()));
651 }
652 }
653 if (SkPathPriv::TooBigForMath(bbox)) {
654 return;
655 }
656
657 if (!SkRectPriv::FitsInFixed(bbox) && rtype != kHair_RectType) {
658 draw_rect_as_path(*this, prePaintRect, paint, matrix);
659 return;
660 }
661
662 SkIRect ir = bbox.roundOut();
663 if (fRC->quickReject(ir)) {
664 return;
665 }
666
667 SkAutoBlitterChoose blitterStorage(*this, matrix, paint);
668 const SkRasterClip& clip = *fRC;
669 SkBlitter* blitter = blitterStorage.get();
670
671 // we want to "fill" if we are kFill or kStrokeAndFill, since in the latter
672 // case we are also hairline (if we've gotten to here), which devolves to
673 // effectively just kFill
674 switch (rtype) {
675 case kFill_RectType:
676 if (paint.isAntiAlias()) {
677 SkScan::AntiFillRect(devRect, clip, blitter);
678 } else {
679 SkScan::FillRect(devRect, clip, blitter);
680 }
681 break;
682 case kStroke_RectType:
683 if (paint.isAntiAlias()) {
684 SkScan::AntiFrameRect(devRect, strokeSize, clip, blitter);
685 } else {
686 SkScan::FrameRect(devRect, strokeSize, clip, blitter);
687 }
688 break;
689 case kHair_RectType:
690 if (paint.isAntiAlias()) {
691 SkScan::AntiHairRect(devRect, clip, blitter);
692 } else {
693 SkScan::HairRect(devRect, clip, blitter);
694 }
695 break;
696 default:
697 SkDEBUGFAIL("bad rtype");
698 }
699 }
700
drawDevMask(const SkMask & srcM,const SkPaint & paint) const701 void SkDraw::drawDevMask(const SkMask& srcM, const SkPaint& paint) const {
702 if (srcM.fBounds.isEmpty()) {
703 return;
704 }
705
706 const SkMask* mask = &srcM;
707
708 SkMask dstM;
709 if (paint.getMaskFilter() &&
710 as_MFB(paint.getMaskFilter())->filterMask(&dstM, srcM, *fMatrix, nullptr)) {
711 mask = &dstM;
712 }
713 SkAutoMaskFreeImage ami(dstM.fImage);
714
715 SkAutoBlitterChoose blitterChooser(*this, nullptr, paint);
716 SkBlitter* blitter = blitterChooser.get();
717
718 SkAAClipBlitterWrapper wrapper;
719 const SkRegion* clipRgn;
720
721 if (fRC->isBW()) {
722 clipRgn = &fRC->bwRgn();
723 } else {
724 wrapper.init(*fRC, blitter);
725 clipRgn = &wrapper.getRgn();
726 blitter = wrapper.getBlitter();
727 }
728 blitter->blitMaskRegion(*mask, *clipRgn);
729 }
730
fast_len(const SkVector & vec)731 static SkScalar fast_len(const SkVector& vec) {
732 SkScalar x = SkScalarAbs(vec.fX);
733 SkScalar y = SkScalarAbs(vec.fY);
734 if (x < y) {
735 using std::swap;
736 swap(x, y);
737 }
738 return x + SkScalarHalf(y);
739 }
740
SkDrawTreatAAStrokeAsHairline(SkScalar strokeWidth,const SkMatrix & matrix,SkScalar * coverage)741 bool SkDrawTreatAAStrokeAsHairline(SkScalar strokeWidth, const SkMatrix& matrix,
742 SkScalar* coverage) {
743 SkASSERT(strokeWidth > 0);
744 // We need to try to fake a thick-stroke with a modulated hairline.
745
746 if (matrix.hasPerspective()) {
747 return false;
748 }
749
750 SkVector src[2], dst[2];
751 src[0].set(strokeWidth, 0);
752 src[1].set(0, strokeWidth);
753 matrix.mapVectors(dst, src, 2);
754 SkScalar len0 = fast_len(dst[0]);
755 SkScalar len1 = fast_len(dst[1]);
756 if (len0 <= SK_Scalar1 && len1 <= SK_Scalar1) {
757 if (coverage) {
758 *coverage = SkScalarAve(len0, len1);
759 }
760 return true;
761 }
762 return false;
763 }
764
drawRRect(const SkRRect & rrect,const SkPaint & paint) const765 void SkDraw::drawRRect(const SkRRect& rrect, const SkPaint& paint) const {
766 SkDEBUGCODE(this->validate());
767
768 if (fRC->isEmpty()) {
769 return;
770 }
771
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, *fMatrix, &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(*fMatrix, &devRRect)) {
790 SkAutoBlitterChoose blitter(*this, nullptr, paint);
791 if (as_MFB(paint.getMaskFilter())->filterRRect(devRRect, *fMatrix,
792 *fRC, blitter.get())) {
793 return; // filterRRect() called the blitter, so we're done
794 }
795 }
796 }
797
798 DRAW_PATH:
799 // Now fall back to the default case of using a path.
800 SkPath path;
801 path.addRRect(rrect);
802 this->drawPath(path, paint, nullptr, true);
803 }
804
ComputeResScaleForStroking(const SkMatrix & matrix)805 SkScalar SkDraw::ComputeResScaleForStroking(const SkMatrix& matrix) {
806 // Not sure how to handle perspective differently, so we just don't try (yet)
807 SkScalar sx = SkPoint::Length(matrix[SkMatrix::kMScaleX], matrix[SkMatrix::kMSkewY]);
808 SkScalar sy = SkPoint::Length(matrix[SkMatrix::kMSkewX], matrix[SkMatrix::kMScaleY]);
809 if (SkScalarsAreFinite(sx, sy)) {
810 SkScalar scale = SkTMax(sx, sy);
811 if (scale > 0) {
812 return scale;
813 }
814 }
815 return 1;
816 }
817
drawDevPath(const SkPath & devPath,const SkPaint & paint,bool drawCoverage,SkBlitter * customBlitter,bool doFill) const818 void SkDraw::drawDevPath(const SkPath& devPath, const SkPaint& paint, bool drawCoverage,
819 SkBlitter* customBlitter, bool doFill) const {
820 if (SkPathPriv::TooBigForMath(devPath)) {
821 return;
822 }
823 SkBlitter* blitter = nullptr;
824 SkAutoBlitterChoose blitterStorage;
825 if (nullptr == customBlitter) {
826 blitter = blitterStorage.choose(*this, nullptr, paint, drawCoverage);
827 } else {
828 blitter = customBlitter;
829 }
830
831 if (paint.getMaskFilter()) {
832 SkStrokeRec::InitStyle style = doFill ? SkStrokeRec::kFill_InitStyle
833 : SkStrokeRec::kHairline_InitStyle;
834 if (as_MFB(paint.getMaskFilter())->filterPath(devPath, *fMatrix, *fRC, blitter, style)) {
835 return; // filterPath() called the blitter, so we're done
836 }
837 }
838
839 void (*proc)(const SkPath&, 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, *fRC, blitter);
881 }
882
drawPath(const SkPath & origSrcPath,const SkPaint & origPaint,const SkMatrix * prePathMatrix,bool pathIsMutable,bool drawCoverage,SkBlitter * customBlitter) const883 void 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 SkMatrix tmpMatrix;
898 const SkMatrix* matrix = fMatrix;
899 tmpPath->setIsVolatile(true);
900 SkPathPriv::SetIsBadForDAA(*tmpPath, SkPathPriv::IsBadForDAA(origSrcPath));
901
902 if (prePathMatrix) {
903 if (origPaint.getPathEffect() || origPaint.getStyle() != SkPaint::kFill_Style) {
904 SkPath* result = pathPtr;
905
906 if (!pathIsMutable) {
907 result = tmpPath;
908 pathIsMutable = true;
909 }
910 pathPtr->transform(*prePathMatrix, result);
911 pathPtr = result;
912 } else {
913 tmpMatrix.setConcat(*matrix, *prePathMatrix);
914 matrix = &tmpMatrix;
915 }
916 }
917 // at this point we're done with prePathMatrix
918 SkDEBUGCODE(prePathMatrix = (const SkMatrix*)0x50FF8001;)
919
920 SkTCopyOnFirstWrite<SkPaint> paint(origPaint);
921
922 {
923 SkScalar coverage;
924 if (SkDrawTreatAsHairline(origPaint, *matrix, &coverage)) {
925 if (SK_Scalar1 == coverage) {
926 paint.writable()->setStrokeWidth(0);
927 } else if (SkBlendMode_SupportsCoverageAsAlpha(origPaint.getBlendMode())) {
928 U8CPU newAlpha;
929 #if 0
930 newAlpha = SkToU8(SkScalarRoundToInt(coverage *
931 origPaint.getAlpha()));
932 #else
933 // this is the old technique, which we preserve for now so
934 // we don't change previous results (testing)
935 // the new way seems fine, its just (a tiny bit) different
936 int scale = (int)(coverage * 256);
937 newAlpha = origPaint.getAlpha() * scale >> 8;
938 #endif
939 SkPaint* writablePaint = paint.writable();
940 writablePaint->setStrokeWidth(0);
941 writablePaint->setAlpha(newAlpha);
942 }
943 }
944 }
945
946 if (paint->getPathEffect() || paint->getStyle() != SkPaint::kFill_Style) {
947 SkRect cullRect;
948 const SkRect* cullRectPtr = nullptr;
949 if (this->computeConservativeLocalClipBounds(&cullRect)) {
950 cullRectPtr = &cullRect;
951 }
952 doFill = paint->getFillPath(*pathPtr, tmpPath, cullRectPtr,
953 ComputeResScaleForStroking(*fMatrix));
954 pathPtr = tmpPath;
955 }
956
957 // avoid possibly allocating a new path in transform if we can
958 SkPath* devPathPtr = pathIsMutable ? pathPtr : tmpPath;
959
960 // transform the path into device space
961 pathPtr->transform(*matrix, devPathPtr);
962
963 this->drawDevPath(*devPathPtr, *paint, drawCoverage, customBlitter, doFill);
964 }
965
drawBitmapAsMask(const SkBitmap & bitmap,const SkPaint & paint) const966 void SkDraw::drawBitmapAsMask(const SkBitmap& bitmap, const SkPaint& paint) const {
967 SkASSERT(bitmap.colorType() == kAlpha_8_SkColorType);
968
969 if (SkTreatAsSprite(*fMatrix, bitmap.dimensions(), paint)) {
970 int ix = SkScalarRoundToInt(fMatrix->getTranslateX());
971 int iy = SkScalarRoundToInt(fMatrix->getTranslateY());
972
973 SkPixmap pmap;
974 if (!bitmap.peekPixels(&pmap)) {
975 return;
976 }
977 SkMask mask;
978 mask.fBounds.set(ix, iy, ix + pmap.width(), iy + pmap.height());
979 mask.fFormat = SkMask::kA8_Format;
980 mask.fRowBytes = SkToU32(pmap.rowBytes());
981 // fImage is typed as writable, but in this case it is used read-only
982 mask.fImage = (uint8_t*)pmap.addr8(0, 0);
983
984 this->drawDevMask(mask, paint);
985 } else { // need to xform the bitmap first
986 SkRect r;
987 SkMask mask;
988
989 r.set(0, 0,
990 SkIntToScalar(bitmap.width()), SkIntToScalar(bitmap.height()));
991 fMatrix->mapRect(&r);
992 r.round(&mask.fBounds);
993
994 // set the mask's bounds to the transformed bitmap-bounds,
995 // clipped to the actual device
996 {
997 SkIRect devBounds;
998 devBounds.set(0, 0, fDst.width(), fDst.height());
999 // need intersect(l, t, r, b) on irect
1000 if (!mask.fBounds.intersect(devBounds)) {
1001 return;
1002 }
1003 }
1004
1005 mask.fFormat = SkMask::kA8_Format;
1006 mask.fRowBytes = SkAlign4(mask.fBounds.width());
1007 size_t size = mask.computeImageSize();
1008 if (0 == size) {
1009 // the mask is too big to allocated, draw nothing
1010 return;
1011 }
1012
1013 // allocate (and clear) our temp buffer to hold the transformed bitmap
1014 SkAutoTMalloc<uint8_t> storage(size);
1015 mask.fImage = storage.get();
1016 memset(mask.fImage, 0, size);
1017
1018 // now draw our bitmap(src) into mask(dst), transformed by the matrix
1019 {
1020 SkBitmap device;
1021 device.installPixels(SkImageInfo::MakeA8(mask.fBounds.width(), mask.fBounds.height()),
1022 mask.fImage, mask.fRowBytes);
1023
1024 SkCanvas c(device);
1025 // need the unclipped top/left for the translate
1026 c.translate(-SkIntToScalar(mask.fBounds.fLeft),
1027 -SkIntToScalar(mask.fBounds.fTop));
1028 c.concat(*fMatrix);
1029
1030 // We can't call drawBitmap, or we'll infinitely recurse. Instead
1031 // we manually build a shader and draw that into our new mask
1032 SkPaint tmpPaint;
1033 tmpPaint.setAntiAlias(paint.isAntiAlias());
1034 tmpPaint.setDither(paint.isDither());
1035 tmpPaint.setFilterQuality(paint.getFilterQuality());
1036 SkPaint paintWithShader = make_paint_with_image(tmpPaint, bitmap);
1037 SkRect rr;
1038 rr.set(0, 0, SkIntToScalar(bitmap.width()),
1039 SkIntToScalar(bitmap.height()));
1040 c.drawRect(rr, paintWithShader);
1041 }
1042 this->drawDevMask(mask, paint);
1043 }
1044 }
1045
clipped_out(const SkMatrix & m,const SkRasterClip & c,const SkRect & srcR)1046 static bool clipped_out(const SkMatrix& m, const SkRasterClip& c,
1047 const SkRect& srcR) {
1048 SkRect dstR;
1049 m.mapRect(&dstR, srcR);
1050 return c.quickReject(dstR.roundOut());
1051 }
1052
clipped_out(const SkMatrix & matrix,const SkRasterClip & clip,int width,int height)1053 static bool clipped_out(const SkMatrix& matrix, const SkRasterClip& clip,
1054 int width, int height) {
1055 SkRect r;
1056 r.set(0, 0, SkIntToScalar(width), SkIntToScalar(height));
1057 return clipped_out(matrix, clip, r);
1058 }
1059
clipHandlesSprite(const SkRasterClip & clip,int x,int y,const SkPixmap & pmap)1060 static bool clipHandlesSprite(const SkRasterClip& clip, int x, int y, const SkPixmap& pmap) {
1061 return clip.isBW() || clip.quickContains(x, y, x + pmap.width(), y + pmap.height());
1062 }
1063
drawBitmap(const SkBitmap & bitmap,const SkMatrix & prematrix,const SkRect * dstBounds,const SkPaint & origPaint) const1064 void SkDraw::drawBitmap(const SkBitmap& bitmap, const SkMatrix& prematrix,
1065 const SkRect* dstBounds, const SkPaint& origPaint) const {
1066 SkDEBUGCODE(this->validate();)
1067
1068 // nothing to draw
1069 if (fRC->isEmpty() ||
1070 bitmap.width() == 0 || bitmap.height() == 0 ||
1071 bitmap.colorType() == kUnknown_SkColorType) {
1072 return;
1073 }
1074
1075 SkTCopyOnFirstWrite<SkPaint> paint(origPaint);
1076 if (origPaint.getStyle() != SkPaint::kFill_Style) {
1077 paint.writable()->setStyle(SkPaint::kFill_Style);
1078 }
1079
1080 SkMatrix matrix;
1081 matrix.setConcat(*fMatrix, prematrix);
1082
1083 if (clipped_out(matrix, *fRC, bitmap.width(), bitmap.height())) {
1084 return;
1085 }
1086
1087 if (bitmap.colorType() != kAlpha_8_SkColorType
1088 && SkTreatAsSprite(matrix, bitmap.dimensions(), *paint)) {
1089 //
1090 // It is safe to call lock pixels now, since we know the matrix is
1091 // (more or less) identity.
1092 //
1093 SkPixmap pmap;
1094 if (!bitmap.peekPixels(&pmap)) {
1095 return;
1096 }
1097 int ix = SkScalarRoundToInt(matrix.getTranslateX());
1098 int iy = SkScalarRoundToInt(matrix.getTranslateY());
1099 if (clipHandlesSprite(*fRC, ix, iy, pmap)) {
1100 SkSTArenaAlloc<kSkBlitterContextSize> allocator;
1101 // blitter will be owned by the allocator.
1102 SkBlitter* blitter = SkBlitter::ChooseSprite(fDst, *paint, pmap, ix, iy, &allocator);
1103 if (blitter) {
1104 SkScan::FillIRect(SkIRect::MakeXYWH(ix, iy, pmap.width(), pmap.height()),
1105 *fRC, blitter);
1106 return;
1107 }
1108 // if !blitter, then we fall-through to the slower case
1109 }
1110 }
1111
1112 // now make a temp draw on the stack, and use it
1113 //
1114 SkDraw draw(*this);
1115 draw.fMatrix = &matrix;
1116
1117 if (bitmap.colorType() == kAlpha_8_SkColorType && !paint->getColorFilter()) {
1118 draw.drawBitmapAsMask(bitmap, *paint);
1119 } else {
1120 SkPaint paintWithShader = make_paint_with_image(*paint, bitmap);
1121 const SkRect srcBounds = SkRect::MakeIWH(bitmap.width(), bitmap.height());
1122 if (dstBounds) {
1123 this->drawRect(srcBounds, paintWithShader, &prematrix, dstBounds);
1124 } else {
1125 draw.drawRect(srcBounds, paintWithShader);
1126 }
1127 }
1128 }
1129
drawSprite(const SkBitmap & bitmap,int x,int y,const SkPaint & origPaint) const1130 void SkDraw::drawSprite(const SkBitmap& bitmap, int x, int y, const SkPaint& origPaint) const {
1131 SkDEBUGCODE(this->validate();)
1132
1133 // nothing to draw
1134 if (fRC->isEmpty() ||
1135 bitmap.width() == 0 || bitmap.height() == 0 ||
1136 bitmap.colorType() == kUnknown_SkColorType) {
1137 return;
1138 }
1139
1140 const SkIRect bounds = SkIRect::MakeXYWH(x, y, bitmap.width(), bitmap.height());
1141
1142 if (fRC->quickReject(bounds)) {
1143 return; // nothing to draw
1144 }
1145
1146 SkPaint paint(origPaint);
1147 paint.setStyle(SkPaint::kFill_Style);
1148
1149 SkPixmap pmap;
1150 if (!bitmap.peekPixels(&pmap)) {
1151 return;
1152 }
1153
1154 if (nullptr == paint.getColorFilter() && clipHandlesSprite(*fRC, x, y, pmap)) {
1155 // blitter will be owned by the allocator.
1156 SkSTArenaAlloc<kSkBlitterContextSize> allocator;
1157 SkBlitter* blitter = SkBlitter::ChooseSprite(fDst, paint, pmap, x, y, &allocator);
1158 if (blitter) {
1159 SkScan::FillIRect(bounds, *fRC, blitter);
1160 return;
1161 }
1162 }
1163
1164 SkMatrix matrix;
1165 SkRect r;
1166
1167 // get a scalar version of our rect
1168 r.set(bounds);
1169
1170 // create shader with offset
1171 matrix.setTranslate(r.fLeft, r.fTop);
1172 SkPaint paintWithShader = make_paint_with_image(paint, bitmap, &matrix);
1173 SkDraw draw(*this);
1174 matrix.reset();
1175 draw.fMatrix = &matrix;
1176 // call ourself with a rect
1177 draw.drawRect(r, paintWithShader);
1178 }
1179
1180 ////////////////////////////////////////////////////////////////////////////////////////////////
1181
1182 #ifdef SK_DEBUG
1183
validate() const1184 void SkDraw::validate() const {
1185 SkASSERT(fMatrix != nullptr);
1186 SkASSERT(fRC != nullptr);
1187
1188 const SkIRect& cr = fRC->getBounds();
1189 SkIRect br;
1190
1191 br.set(0, 0, fDst.width(), fDst.height());
1192 SkASSERT(cr.isEmpty() || br.contains(cr));
1193 }
1194
1195 #endif
1196
1197 ////////////////////////////////////////////////////////////////////////////////////////////////
1198
1199 #include "SkPath.h"
1200 #include "SkDraw.h"
1201 #include "SkRegion.h"
1202 #include "SkBlitter.h"
1203
ComputeMaskBounds(const SkRect & devPathBounds,const SkIRect * clipBounds,const SkMaskFilter * filter,const SkMatrix * filterMatrix,SkIRect * bounds)1204 bool SkDraw::ComputeMaskBounds(const SkRect& devPathBounds, const SkIRect* clipBounds,
1205 const SkMaskFilter* filter, const SkMatrix* filterMatrix,
1206 SkIRect* bounds) {
1207 // init our bounds from the path
1208 *bounds = devPathBounds.makeOutset(SK_ScalarHalf, SK_ScalarHalf).roundOut();
1209
1210 SkIPoint margin = SkIPoint::Make(0, 0);
1211 if (filter) {
1212 SkASSERT(filterMatrix);
1213
1214 SkMask srcM, dstM;
1215
1216 srcM.fBounds = *bounds;
1217 srcM.fFormat = SkMask::kA8_Format;
1218 if (!as_MFB(filter)->filterMask(&dstM, srcM, *filterMatrix, &margin)) {
1219 return false;
1220 }
1221 }
1222
1223 // (possibly) trim the bounds to reflect the clip
1224 // (plus whatever slop the filter needs)
1225 if (clipBounds) {
1226 // Ugh. Guard against gigantic margins from wacky filters. Without this
1227 // check we can request arbitrary amounts of slop beyond our visible
1228 // clip, and bring down the renderer (at least on finite RAM machines
1229 // like handsets, etc.). Need to balance this invented value between
1230 // quality of large filters like blurs, and the corresponding memory
1231 // requests.
1232 static const int MAX_MARGIN = 128;
1233 if (!bounds->intersect(clipBounds->makeOutset(SkMin32(margin.fX, MAX_MARGIN),
1234 SkMin32(margin.fY, MAX_MARGIN)))) {
1235 return false;
1236 }
1237 }
1238
1239 return true;
1240 }
1241
draw_into_mask(const SkMask & mask,const SkPath & devPath,SkStrokeRec::InitStyle style)1242 static void draw_into_mask(const SkMask& mask, const SkPath& devPath,
1243 SkStrokeRec::InitStyle style) {
1244 SkDraw draw;
1245 if (!draw.fDst.reset(mask)) {
1246 return;
1247 }
1248
1249 SkRasterClip clip;
1250 SkMatrix matrix;
1251 SkPaint paint;
1252
1253 clip.setRect(SkIRect::MakeWH(mask.fBounds.width(), mask.fBounds.height()));
1254 matrix.setTranslate(-SkIntToScalar(mask.fBounds.fLeft),
1255 -SkIntToScalar(mask.fBounds.fTop));
1256
1257 draw.fRC = &clip;
1258 draw.fMatrix = &matrix;
1259 paint.setAntiAlias(true);
1260 switch (style) {
1261 case SkStrokeRec::kHairline_InitStyle:
1262 SkASSERT(!paint.getStrokeWidth());
1263 paint.setStyle(SkPaint::kStroke_Style);
1264 break;
1265 case SkStrokeRec::kFill_InitStyle:
1266 SkASSERT(paint.getStyle() == SkPaint::kFill_Style);
1267 break;
1268
1269 }
1270 draw.drawPath(devPath, paint);
1271 }
1272
DrawToMask(const SkPath & devPath,const SkIRect * clipBounds,const SkMaskFilter * filter,const SkMatrix * filterMatrix,SkMask * mask,SkMask::CreateMode mode,SkStrokeRec::InitStyle style)1273 bool SkDraw::DrawToMask(const SkPath& devPath, const SkIRect* clipBounds,
1274 const SkMaskFilter* filter, const SkMatrix* filterMatrix,
1275 SkMask* mask, SkMask::CreateMode mode,
1276 SkStrokeRec::InitStyle style) {
1277 if (devPath.isEmpty()) {
1278 return false;
1279 }
1280
1281 if (SkMask::kJustRenderImage_CreateMode != mode) {
1282 if (!ComputeMaskBounds(devPath.getBounds(), clipBounds, filter,
1283 filterMatrix, &mask->fBounds))
1284 return false;
1285 }
1286
1287 if (SkMask::kComputeBoundsAndRenderImage_CreateMode == mode) {
1288 mask->fFormat = SkMask::kA8_Format;
1289 mask->fRowBytes = mask->fBounds.width();
1290 size_t size = mask->computeImageSize();
1291 if (0 == size) {
1292 // we're too big to allocate the mask, abort
1293 return false;
1294 }
1295 mask->fImage = SkMask::AllocImage(size, SkMask::kZeroInit_Alloc);
1296 }
1297
1298 if (SkMask::kJustComputeBounds_CreateMode != mode) {
1299 draw_into_mask(*mask, devPath, style);
1300 }
1301
1302 return true;
1303 }
1304