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