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