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/SkColorType.h"
12 #include "include/core/SkMatrix.h"
13 #include "include/core/SkPaint.h"
14 #include "include/core/SkPixmap.h"
15 #include "include/core/SkPoint.h"
16 #include "include/core/SkRect.h"
17 #include "include/core/SkRegion.h"
18 #include "include/core/SkScalar.h"
19 #include "include/core/SkTileMode.h"
20 #include "include/private/base/SkAssert.h"
21 #include "include/private/base/SkDebug.h"
22 #include "include/private/base/SkFixed.h"
23 #include "include/private/base/SkFloatingPoint.h"
24 #include "include/private/base/SkTemplates.h"
25 #include "include/private/base/SkTo.h"
26 #include "src/base/SkArenaAlloc.h"
27 #include "src/base/SkTLazy.h"
28 #include "src/core/SkAutoBlitterChoose.h"
29 #include "src/core/SkBlitter.h"
30 #include "src/core/SkDrawTypes.h"
31 #include "src/core/SkImageInfoPriv.h"
32 #include "src/core/SkImagePriv.h"
33 #include "src/core/SkMatrixUtils.h"
34 #include "src/core/SkRasterClip.h"
35 #include "src/core/SkRectPriv.h"
36 #include "src/core/SkScan.h"
37
38 #if defined(SK_SUPPORT_LEGACY_ALPHA_BITMAP_AS_COVERAGE)
39 #include "src/core/SkMaskFilterBase.h"
40 #endif
41
42 using namespace skia_private;
43
make_paint_with_image(const SkPaint & origPaint,const SkBitmap & bitmap,const SkSamplingOptions & sampling,SkMatrix * matrix=nullptr)44 static SkPaint make_paint_with_image(const SkPaint& origPaint, const SkBitmap& bitmap,
45 const SkSamplingOptions& sampling,
46 SkMatrix* matrix = nullptr) {
47 SkPaint paint(origPaint);
48 paint.setShader(SkMakeBitmapShaderForPaint(origPaint, bitmap, SkTileMode::kClamp,
49 SkTileMode::kClamp, sampling, matrix,
50 kNever_SkCopyPixelsMode));
51 return paint;
52 }
53
SkDraw()54 SkDraw::SkDraw() {
55 fBlitterChooser = SkBlitter::Choose;
56 }
57
58 struct PtProcRec {
59 SkCanvas::PointMode fMode;
60 const SkPaint* fPaint;
61 const SkRegion* fClip;
62 const SkRasterClip* fRC;
63
64 // computed values
65 SkRect fClipBounds;
66 SkScalar fRadius;
67
68 typedef void (*Proc)(const PtProcRec&, const SkPoint devPts[], int count,
69 SkBlitter*);
70
71 bool init(SkCanvas::PointMode, const SkPaint&, const SkMatrix* matrix,
72 const SkRasterClip*);
73 Proc chooseProc(SkBlitter** blitter);
74
75 private:
76 SkAAClipBlitterWrapper fWrapper;
77 };
78
bw_pt_hair_proc(const PtProcRec & rec,const SkPoint devPts[],int count,SkBlitter * blitter)79 static void bw_pt_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
80 int count, SkBlitter* blitter) {
81 for (int i = 0; i < count; i++) {
82 int x = SkScalarFloorToInt(devPts[i].fX);
83 int y = SkScalarFloorToInt(devPts[i].fY);
84 if (rec.fClip->contains(x, y)) {
85 blitter->blitH(x, y, 1);
86 }
87 }
88 }
89
bw_line_hair_proc(const PtProcRec & rec,const SkPoint devPts[],int count,SkBlitter * blitter)90 static void bw_line_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
91 int count, SkBlitter* blitter) {
92 for (int i = 0; i < count; i += 2) {
93 SkScan::HairLine(&devPts[i], 2, *rec.fRC, blitter);
94 }
95 }
96
bw_poly_hair_proc(const PtProcRec & rec,const SkPoint devPts[],int count,SkBlitter * blitter)97 static void bw_poly_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
98 int count, SkBlitter* blitter) {
99 SkScan::HairLine(devPts, count, *rec.fRC, blitter);
100 }
101
102 // aa versions
103
aa_line_hair_proc(const PtProcRec & rec,const SkPoint devPts[],int count,SkBlitter * blitter)104 static void aa_line_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
105 int count, SkBlitter* blitter) {
106 for (int i = 0; i < count; i += 2) {
107 SkScan::AntiHairLine(&devPts[i], 2, *rec.fRC, blitter);
108 }
109 }
110
aa_poly_hair_proc(const PtProcRec & rec,const SkPoint devPts[],int count,SkBlitter * blitter)111 static void aa_poly_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
112 int count, SkBlitter* blitter) {
113 SkScan::AntiHairLine(devPts, count, *rec.fRC, blitter);
114 }
115
116 // square procs (strokeWidth > 0 but matrix is square-scale (sx == sy)
117
make_square_rad(SkPoint center,SkScalar radius)118 static SkRect make_square_rad(SkPoint center, SkScalar radius) {
119 return {
120 center.fX - radius, center.fY - radius,
121 center.fX + radius, center.fY + radius
122 };
123 }
124
make_xrect(const SkRect & r)125 static SkXRect make_xrect(const SkRect& r) {
126 SkASSERT(SkRectPriv::FitsInFixed(r));
127 return {
128 SkScalarToFixed(r.fLeft), SkScalarToFixed(r.fTop),
129 SkScalarToFixed(r.fRight), SkScalarToFixed(r.fBottom)
130 };
131 }
132
bw_square_proc(const PtProcRec & rec,const SkPoint devPts[],int count,SkBlitter * blitter)133 static void bw_square_proc(const PtProcRec& rec, const SkPoint devPts[],
134 int count, SkBlitter* blitter) {
135 for (int i = 0; i < count; i++) {
136 SkRect r = make_square_rad(devPts[i], rec.fRadius);
137 if (r.intersect(rec.fClipBounds)) {
138 SkScan::FillXRect(make_xrect(r), *rec.fRC, blitter);
139 }
140 }
141 }
142
aa_square_proc(const PtProcRec & rec,const SkPoint devPts[],int count,SkBlitter * blitter)143 static void aa_square_proc(const PtProcRec& rec, const SkPoint devPts[],
144 int count, SkBlitter* blitter) {
145 for (int i = 0; i < count; i++) {
146 SkRect r = make_square_rad(devPts[i], rec.fRadius);
147 if (r.intersect(rec.fClipBounds)) {
148 SkScan::AntiFillXRect(make_xrect(r), *rec.fRC, blitter);
149 }
150 }
151 }
152
153 // If this returns true, then chooseProc() must return a valid proc
init(SkCanvas::PointMode mode,const SkPaint & paint,const SkMatrix * matrix,const SkRasterClip * rc)154 bool PtProcRec::init(SkCanvas::PointMode mode, const SkPaint& paint,
155 const SkMatrix* matrix, const SkRasterClip* rc) {
156 if ((unsigned)mode > (unsigned)SkCanvas::kPolygon_PointMode) {
157 return false;
158 }
159 if (paint.getPathEffect() || paint.getMaskFilter()) {
160 return false;
161 }
162 SkScalar width = paint.getStrokeWidth();
163 SkScalar radius = -1; // sentinel value, a "valid" value must be > 0
164
165 if (0 == width) {
166 radius = 0.5f;
167 } else if (paint.getStrokeCap() != SkPaint::kRound_Cap &&
168 matrix->isScaleTranslate() && SkCanvas::kPoints_PointMode == mode) {
169 SkScalar sx = matrix->get(SkMatrix::kMScaleX);
170 SkScalar sy = matrix->get(SkMatrix::kMScaleY);
171 if (SkScalarNearlyZero(sx - sy)) {
172 radius = SkScalarHalf(width * SkScalarAbs(sx));
173 }
174 }
175 if (radius > 0) {
176 SkRect clipBounds = SkRect::Make(rc->getBounds());
177 // if we return true, the caller may assume that the constructed shapes can be represented
178 // using SkFixed (after clipping), so we preflight that here.
179 if (!SkRectPriv::FitsInFixed(clipBounds)) {
180 return false;
181 }
182 fMode = mode;
183 fPaint = &paint;
184 fClip = nullptr;
185 fRC = rc;
186 fClipBounds = clipBounds;
187 fRadius = radius;
188 return true;
189 }
190 return false;
191 }
192
chooseProc(SkBlitter ** blitterPtr)193 PtProcRec::Proc PtProcRec::chooseProc(SkBlitter** blitterPtr) {
194 Proc proc = nullptr;
195
196 SkBlitter* blitter = *blitterPtr;
197 if (fRC->isBW()) {
198 fClip = &fRC->bwRgn();
199 } else {
200 fWrapper.init(*fRC, blitter);
201 fClip = &fWrapper.getRgn();
202 blitter = fWrapper.getBlitter();
203 *blitterPtr = blitter;
204 }
205
206 // for our arrays
207 SkASSERT(0 == SkCanvas::kPoints_PointMode);
208 SkASSERT(1 == SkCanvas::kLines_PointMode);
209 SkASSERT(2 == SkCanvas::kPolygon_PointMode);
210 SkASSERT((unsigned)fMode <= (unsigned)SkCanvas::kPolygon_PointMode);
211
212 if (fPaint->isAntiAlias()) {
213 if (0 == fPaint->getStrokeWidth()) {
214 static const Proc gAAProcs[] = {
215 aa_square_proc, aa_line_hair_proc, aa_poly_hair_proc
216 };
217 proc = gAAProcs[fMode];
218 } else if (fPaint->getStrokeCap() != SkPaint::kRound_Cap) {
219 SkASSERT(SkCanvas::kPoints_PointMode == fMode);
220 proc = aa_square_proc;
221 }
222 } else { // BW
223 if (fRadius <= 0.5f) { // small radii and hairline
224 static const Proc gBWProcs[] = {
225 bw_pt_hair_proc, bw_line_hair_proc, bw_poly_hair_proc
226 };
227 proc = gBWProcs[fMode];
228 } else {
229 proc = bw_square_proc;
230 }
231 }
232 return proc;
233 }
234
235 // each of these costs 8-bytes of stack space, so don't make it too large
236 // must be even for lines/polygon to work
237 #define MAX_DEV_PTS 32
238
drawPoints(SkCanvas::PointMode mode,size_t count,const SkPoint pts[],const SkPaint & paint,SkDevice * device) const239 void SkDraw::drawPoints(SkCanvas::PointMode mode, size_t count,
240 const SkPoint pts[], const SkPaint& paint,
241 SkDevice* device) const {
242 // if we're in lines mode, force count to be even
243 if (SkCanvas::kLines_PointMode == mode) {
244 count &= ~(size_t)1;
245 }
246
247 SkASSERT(pts != nullptr);
248 SkDEBUGCODE(this->validate();)
249
250 // nothing to draw
251 if (!count || fRC->isEmpty()) {
252 return;
253 }
254
255 PtProcRec rec;
256 if (!device && rec.init(mode, paint, fCTM, fRC)) {
257 SkAutoBlitterChoose blitter(*this, nullptr, paint);
258
259 SkPoint devPts[MAX_DEV_PTS];
260 SkBlitter* bltr = blitter.get();
261 PtProcRec::Proc proc = rec.chooseProc(&bltr);
262 // we have to back up subsequent passes if we're in polygon mode
263 const size_t backup = (SkCanvas::kPolygon_PointMode == mode);
264
265 do {
266 int n = SkToInt(count);
267 if (n > MAX_DEV_PTS) {
268 n = MAX_DEV_PTS;
269 }
270 fCTM->mapPoints(devPts, pts, n);
271 if (!SkIsFinite(&devPts[0].fX, n * 2)) {
272 return;
273 }
274 proc(rec, devPts, n, bltr);
275 pts += n - backup;
276 SkASSERT(SkToInt(count) >= n);
277 count -= n;
278 if (count > 0) {
279 count += backup;
280 }
281 } while (count != 0);
282 } else {
283 this->drawDevicePoints(mode, count, pts, paint, device);
284 }
285 }
286
clipped_out(const SkMatrix & m,const SkRasterClip & c,const SkRect & srcR)287 static bool clipped_out(const SkMatrix& m, const SkRasterClip& c,
288 const SkRect& srcR) {
289 SkRect dstR;
290 m.mapRect(&dstR, srcR);
291 return c.quickReject(dstR.roundOut());
292 }
293
clipped_out(const SkMatrix & matrix,const SkRasterClip & clip,int width,int height)294 static bool clipped_out(const SkMatrix& matrix, const SkRasterClip& clip,
295 int width, int height) {
296 SkRect r;
297 r.setIWH(width, height);
298 return clipped_out(matrix, clip, r);
299 }
300
clipHandlesSprite(const SkRasterClip & clip,int x,int y,const SkPixmap & pmap)301 static bool clipHandlesSprite(const SkRasterClip& clip, int x, int y, const SkPixmap& pmap) {
302 return clip.isBW() || clip.quickContains(SkIRect::MakeXYWH(x, y, pmap.width(), pmap.height()));
303 }
304
drawBitmap(const SkBitmap & bitmap,const SkMatrix & prematrix,const SkRect * dstBounds,const SkSamplingOptions & sampling,const SkPaint & origPaint) const305 void SkDraw::drawBitmap(const SkBitmap& bitmap, const SkMatrix& prematrix,
306 const SkRect* dstBounds, const SkSamplingOptions& sampling,
307 const SkPaint& origPaint) const {
308 SkDEBUGCODE(this->validate();)
309
310 // nothing to draw
311 if (fRC->isEmpty() ||
312 bitmap.width() == 0 || bitmap.height() == 0 ||
313 bitmap.colorType() == kUnknown_SkColorType) {
314 return;
315 }
316
317 SkTCopyOnFirstWrite<SkPaint> paint(origPaint);
318 if (origPaint.getStyle() != SkPaint::kFill_Style) {
319 paint.writable()->setStyle(SkPaint::kFill_Style);
320 }
321
322 SkMatrix matrix = *fCTM * prematrix;
323
324 if (clipped_out(matrix, *fRC, bitmap.width(), bitmap.height())) {
325 return;
326 }
327
328 if (!SkColorTypeIsAlphaOnly(bitmap.colorType()) &&
329 SkTreatAsSprite(matrix, bitmap.dimensions(), sampling, paint->isAntiAlias())) {
330 //
331 // It is safe to call lock pixels now, since we know the matrix is
332 // (more or less) identity.
333 //
334 SkPixmap pmap;
335 if (!bitmap.peekPixels(&pmap)) {
336 return;
337 }
338 int ix = SkScalarRoundToInt(matrix.getTranslateX());
339 int iy = SkScalarRoundToInt(matrix.getTranslateY());
340 if (clipHandlesSprite(*fRC, ix, iy, pmap)) {
341 SkSTArenaAlloc<kSkBlitterContextSize> allocator;
342 // blitter will be owned by the allocator.
343 SkBlitter* blitter = SkBlitter::ChooseSprite(fDst, *paint, pmap, ix, iy, &allocator,
344 fRC->clipShader());
345 if (blitter) {
346 SkScan::FillIRect(SkIRect::MakeXYWH(ix, iy, pmap.width(), pmap.height()),
347 *fRC, blitter);
348 return;
349 }
350 // if !blitter, then we fall-through to the slower case
351 }
352 }
353
354 // now make a temp draw on the stack, and use it
355 //
356 SkDraw draw(*this);
357 draw.fCTM = &matrix;
358
359 // For a long time, the CPU backend treated A8 bitmaps as coverage, rather than alpha. This was
360 // inconsistent with the GPU backend (skbug.com/9692). When this was fixed, it altered behavior
361 // for some Android apps (b/231400686). Thus: keep the old behavior in the framework.
362 #if defined(SK_SUPPORT_LEGACY_ALPHA_BITMAP_AS_COVERAGE)
363 if (bitmap.colorType() == kAlpha_8_SkColorType && !paint->getColorFilter()) {
364 draw.drawBitmapAsMask(bitmap, sampling, *paint);
365 return;
366 }
367 #endif
368
369 SkPaint paintWithShader = make_paint_with_image(*paint, bitmap, sampling);
370 const SkRect srcBounds = SkRect::MakeIWH(bitmap.width(), bitmap.height());
371 if (dstBounds) {
372 this->drawRect(srcBounds, paintWithShader, &prematrix, dstBounds);
373 } else {
374 draw.drawRect(srcBounds, paintWithShader);
375 }
376 }
377
drawSprite(const SkBitmap & bitmap,int x,int y,const SkPaint & origPaint) const378 void SkDraw::drawSprite(const SkBitmap& bitmap, int x, int y, const SkPaint& origPaint) const {
379 SkDEBUGCODE(this->validate();)
380
381 // nothing to draw
382 if (fRC->isEmpty() ||
383 bitmap.width() == 0 || bitmap.height() == 0 ||
384 bitmap.colorType() == kUnknown_SkColorType) {
385 return;
386 }
387
388 const SkIRect bounds = SkIRect::MakeXYWH(x, y, bitmap.width(), bitmap.height());
389
390 if (fRC->quickReject(bounds)) {
391 return; // nothing to draw
392 }
393
394 SkPaint paint(origPaint);
395 paint.setStyle(SkPaint::kFill_Style);
396
397 SkPixmap pmap;
398 if (!bitmap.peekPixels(&pmap)) {
399 return;
400 }
401
402 if (nullptr == paint.getColorFilter() && clipHandlesSprite(*fRC, x, y, pmap)) {
403 // blitter will be owned by the allocator.
404 SkSTArenaAlloc<kSkBlitterContextSize> allocator;
405 SkBlitter* blitter = SkBlitter::ChooseSprite(fDst, paint, pmap, x, y, &allocator,
406 fRC->clipShader());
407 if (blitter) {
408 SkScan::FillIRect(bounds, *fRC, blitter);
409 return;
410 }
411 }
412
413 SkMatrix matrix;
414 SkRect r;
415
416 // get a scalar version of our rect
417 r.set(bounds);
418
419 // create shader with offset
420 matrix.setTranslate(r.fLeft, r.fTop);
421 SkPaint paintWithShader = make_paint_with_image(paint, bitmap, SkSamplingOptions(), &matrix);
422 SkDraw draw(*this);
423 draw.fCTM = &SkMatrix::I();
424 // call ourself with a rect
425 draw.drawRect(r, paintWithShader);
426 }
427
428 #if defined(SK_SUPPORT_LEGACY_ALPHA_BITMAP_AS_COVERAGE)
drawDevMask(const SkMask & srcM,const SkPaint & paint) const429 void SkDraw::drawDevMask(const SkMask& srcM, const SkPaint& paint) const {
430 if (srcM.fBounds.isEmpty()) {
431 return;
432 }
433
434 const SkMask* mask = &srcM;
435
436 SkMaskBuilder dstM;
437 if (paint.getMaskFilter() &&
438 as_MFB(paint.getMaskFilter())->filterMask(&dstM, srcM, *fCTM, nullptr)) {
439 mask = &dstM;
440 }
441 SkAutoMaskFreeImage ami(dstM.image());
442
443 SkAutoBlitterChoose blitterChooser(*this, nullptr, paint);
444 SkBlitter* blitter = blitterChooser.get();
445
446 SkAAClipBlitterWrapper wrapper;
447 const SkRegion* clipRgn;
448
449 if (fRC->isBW()) {
450 clipRgn = &fRC->bwRgn();
451 } else {
452 wrapper.init(*fRC, blitter);
453 clipRgn = &wrapper.getRgn();
454 blitter = wrapper.getBlitter();
455 }
456 blitter->blitMaskRegion(*mask, *clipRgn);
457 }
458
drawBitmapAsMask(const SkBitmap & bitmap,const SkSamplingOptions & sampling,const SkPaint & paint) const459 void SkDraw::drawBitmapAsMask(const SkBitmap& bitmap, const SkSamplingOptions& sampling,
460 const SkPaint& paint) const {
461 SkASSERT(bitmap.colorType() == kAlpha_8_SkColorType);
462
463 // nothing to draw
464 if (fRC->isEmpty()) {
465 return;
466 }
467
468 if (SkTreatAsSprite(*fCTM, bitmap.dimensions(), sampling, paint.isAntiAlias()))
469 {
470 int ix = SkScalarRoundToInt(fCTM->getTranslateX());
471 int iy = SkScalarRoundToInt(fCTM->getTranslateY());
472
473 SkPixmap pmap;
474 if (!bitmap.peekPixels(&pmap)) {
475 return;
476 }
477 SkMask mask(pmap.addr8(0, 0),
478 SkIRect::MakeXYWH(ix, iy, pmap.width(), pmap.height()),
479 SkToU32(pmap.rowBytes()),
480 SkMask::kA8_Format);
481
482 this->drawDevMask(mask, paint);
483 } else { // need to xform the bitmap first
484 SkRect r;
485 SkMaskBuilder mask;
486
487 r.setIWH(bitmap.width(), bitmap.height());
488 fCTM->mapRect(&r);
489 r.round(&mask.bounds());
490
491 // set the mask's bounds to the transformed bitmap-bounds,
492 // clipped to the actual device and further limited by the clip bounds
493 {
494 SkASSERT(fDst.bounds().contains(fRC->getBounds()));
495 SkIRect devBounds = fDst.bounds();
496 devBounds.intersect(fRC->getBounds().makeOutset(1, 1));
497 // need intersect(l, t, r, b) on irect
498 if (!mask.bounds().intersect(devBounds)) {
499 return;
500 }
501 }
502
503 mask.format() = SkMask::kA8_Format;
504 mask.rowBytes() = SkAlign4(mask.fBounds.width());
505 size_t size = mask.computeImageSize();
506 if (0 == size) {
507 // the mask is too big to allocated, draw nothing
508 return;
509 }
510
511 // allocate (and clear) our temp buffer to hold the transformed bitmap
512 AutoTMalloc<uint8_t> storage(size);
513 mask.image() = storage.get();
514 memset(mask.image(), 0, size);
515
516 // now draw our bitmap(src) into mask(dst), transformed by the matrix
517 {
518 SkBitmap device;
519 device.installPixels(SkImageInfo::MakeA8(mask.fBounds.width(), mask.fBounds.height()),
520 mask.image(), mask.fRowBytes);
521
522 SkCanvas c(device);
523 // need the unclipped top/left for the translate
524 c.translate(-SkIntToScalar(mask.fBounds.fLeft),
525 -SkIntToScalar(mask.fBounds.fTop));
526 c.concat(*fCTM);
527
528 // We can't call drawBitmap, or we'll infinitely recurse. Instead
529 // we manually build a shader and draw that into our new mask
530 SkPaint tmpPaint;
531 tmpPaint.setAntiAlias(paint.isAntiAlias());
532 tmpPaint.setDither(paint.isDither());
533 SkPaint paintWithShader = make_paint_with_image(tmpPaint, bitmap, sampling);
534 SkRect rr;
535 rr.setIWH(bitmap.width(), bitmap.height());
536 c.drawRect(rr, paintWithShader);
537 }
538 this->drawDevMask(mask, paint);
539 }
540 }
541 #endif
542