xref: /aosp_15_r20/external/skia/src/core/SkDraw.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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