• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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