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 | |
40 | static 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 | |
51 | SkDraw::SkDraw() {} |
52 | |
53 | 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 | |
72 | 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.setWH(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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 |
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 | |
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 | |
337 | 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.setLTRB(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 | |
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 | |
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 | |
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 | |
587 | static const SkPoint* rect_points(const SkRect& r) { |
588 | return reinterpret_cast<const SkPoint*>(&r); |
589 | } |
590 | |
591 | static SkPoint* rect_points(SkRect& r) { |
592 | return reinterpret_cast<SkPoint*>(&r); |
593 | } |
594 | |
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(SkPathFillType::kWinding); |
602 | draw.drawPath(tmp, paint, nullptr, true); |
603 | } |
604 | |
605 | 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 | |
701 | 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 | |
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 | |
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 | |
765 | 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 | |
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 = std::max(sx, sy); |
811 | if (scale > 0) { |
812 | return scale; |
813 | } |
814 | } |
815 | return 1; |
816 | } |
817 | |
818 | 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 | |
883 | 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 | |
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 | tmpMatrix.setConcat(*matrix, *prePathMatrix); |
913 | matrix = &tmpMatrix; |
914 | } |
915 | } |
916 | // at this point we're done with prePathMatrix |
917 | SkDEBUGCODE(prePathMatrix = (const SkMatrix*)0x50FF8001;) |
918 | |
919 | SkTCopyOnFirstWrite<SkPaint> paint(origPaint); |
920 | |
921 | { |
922 | SkScalar coverage; |
923 | if (SkDrawTreatAsHairline(origPaint, *matrix, &coverage)) { |
924 | if (SK_Scalar1 == coverage) { |
925 | paint.writable()->setStrokeWidth(0); |
926 | } else if (SkBlendMode_SupportsCoverageAsAlpha(origPaint.getBlendMode())) { |
927 | U8CPU newAlpha; |
928 | #if 0 |
929 | newAlpha = SkToU8(SkScalarRoundToInt(coverage * |
930 | origPaint.getAlpha())); |
931 | #else |
932 | // this is the old technique, which we preserve for now so |
933 | // we don't change previous results (testing) |
934 | // the new way seems fine, its just (a tiny bit) different |
935 | int scale = (int)(coverage * 256); |
936 | newAlpha = origPaint.getAlpha() * scale >> 8; |
937 | #endif |
938 | SkPaint* writablePaint = paint.writable(); |
939 | writablePaint->setStrokeWidth(0); |
940 | writablePaint->setAlpha(newAlpha); |
941 | } |
942 | } |
943 | } |
944 | |
945 | if (paint->getPathEffect() || paint->getStyle() != SkPaint::kFill_Style) { |
946 | SkRect cullRect; |
947 | const SkRect* cullRectPtr = nullptr; |
948 | if (this->computeConservativeLocalClipBounds(&cullRect)) { |
949 | cullRectPtr = &cullRect; |
950 | } |
951 | doFill = paint->getFillPath(*pathPtr, tmpPath, cullRectPtr, |
952 | ComputeResScaleForStroking(*fMatrix)); |
953 | pathPtr = tmpPath; |
954 | } |
955 | |
956 | // avoid possibly allocating a new path in transform if we can |
957 | SkPath* devPathPtr = pathIsMutable ? pathPtr : tmpPath; |
958 | |
959 | // transform the path into device space |
960 | pathPtr->transform(*matrix, devPathPtr); |
961 | |
962 | this->drawDevPath(*devPathPtr, *paint, drawCoverage, customBlitter, doFill); |
963 | } |
964 | |
965 | void SkDraw::drawBitmapAsMask(const SkBitmap& bitmap, const SkPaint& paint) const { |
966 | SkASSERT(bitmap.colorType() == kAlpha_8_SkColorType); |
967 | |
968 | // nothing to draw |
969 | if (fRC->isEmpty()) { |
970 | return; |
971 | } |
972 | |
973 | if (SkTreatAsSprite(*fMatrix, bitmap.dimensions(), paint)) { |
974 | int ix = SkScalarRoundToInt(fMatrix->getTranslateX()); |
975 | int iy = SkScalarRoundToInt(fMatrix->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 | fMatrix->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(*fMatrix); |
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 | |
1049 | static 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 | |
1056 | static 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 | |
1063 | static 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 | |
1067 | void 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 | SkMatrix matrix; |
1084 | matrix.setConcat(*fMatrix, prematrix); |
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.fMatrix = &matrix; |
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 | |
1134 | void 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 | matrix.reset(); |
1180 | draw.fMatrix = &matrix; |
1181 | // call ourself with a rect |
1182 | draw.drawRect(r, paintWithShader); |
1183 | } |
1184 | |
1185 | //////////////////////////////////////////////////////////////////////////////////////////////// |
1186 | |
1187 | #ifdef SK_DEBUG |
1188 | |
1189 | void SkDraw::validate() const { |
1190 | SkASSERT(fMatrix != 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 | |
1209 | bool 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 | |
1247 | static 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 | draw.fRC = &clip; |
1263 | draw.fMatrix = &matrix; |
1264 | paint.setAntiAlias(true); |
1265 | switch (style) { |
1266 | case SkStrokeRec::kHairline_InitStyle: |
1267 | SkASSERT(!paint.getStrokeWidth()); |
1268 | paint.setStyle(SkPaint::kStroke_Style); |
1269 | break; |
1270 | case SkStrokeRec::kFill_InitStyle: |
1271 | SkASSERT(paint.getStyle() == SkPaint::kFill_Style); |
1272 | break; |
1273 | |
1274 | } |
1275 | draw.drawPath(devPath, paint); |
1276 | } |
1277 | |
1278 | bool SkDraw::DrawToMask(const SkPath& devPath, const SkIRect* clipBounds, |
1279 | const SkMaskFilter* filter, const SkMatrix* filterMatrix, |
1280 | SkMask* mask, SkMask::CreateMode mode, |
1281 | SkStrokeRec::InitStyle style) { |
1282 | if (devPath.isEmpty()) { |
1283 | return false; |
1284 | } |
1285 | |
1286 | if (SkMask::kJustRenderImage_CreateMode != mode) { |
1287 | if (!ComputeMaskBounds(devPath.getBounds(), clipBounds, filter, |
1288 | filterMatrix, &mask->fBounds)) |
1289 | return false; |
1290 | } |
1291 | |
1292 | if (SkMask::kComputeBoundsAndRenderImage_CreateMode == mode) { |
1293 | mask->fFormat = SkMask::kA8_Format; |
1294 | mask->fRowBytes = mask->fBounds.width(); |
1295 | size_t size = mask->computeImageSize(); |
1296 | if (0 == size) { |
1297 | // we're too big to allocate the mask, abort |
1298 | return false; |
1299 | } |
1300 | mask->fImage = SkMask::AllocImage(size, SkMask::kZeroInit_Alloc); |
1301 | } |
1302 | |
1303 | if (SkMask::kJustComputeBounds_CreateMode != mode) { |
1304 | draw_into_mask(*mask, devPath, style); |
1305 | } |
1306 | |
1307 | return true; |
1308 | } |
1309 | |