/src/skia/src/core/SkBitmapDevice.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright 2013 Google Inc. |
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/SkBitmapDevice.h" |
9 | | |
10 | | #include "include/core/SkAlphaType.h" |
11 | | #include "include/core/SkBlender.h" |
12 | | #include "include/core/SkClipOp.h" |
13 | | #include "include/core/SkColorType.h" |
14 | | #include "include/core/SkImage.h" |
15 | | #include "include/core/SkImageInfo.h" |
16 | | #include "include/core/SkMatrix.h" |
17 | | #include "include/core/SkPaint.h" |
18 | | #include "include/core/SkPath.h" |
19 | | #include "include/core/SkPixmap.h" |
20 | | #include "include/core/SkPoint.h" |
21 | | #include "include/core/SkRRect.h" |
22 | | #include "include/core/SkRSXform.h" |
23 | | #include "include/core/SkRasterHandleAllocator.h" |
24 | | #include "include/core/SkRegion.h" |
25 | | #include "include/core/SkScalar.h" |
26 | | #include "include/core/SkShader.h" |
27 | | #include "include/core/SkSurface.h" |
28 | | #include "include/core/SkSurfaceProps.h" |
29 | | #include "include/core/SkTileMode.h" |
30 | | #include "include/private/base/SkAssert.h" |
31 | | #include "include/private/base/SkTo.h" |
32 | | #include "src/base/SkTLazy.h" |
33 | | #include "src/core/SkDraw.h" |
34 | | #include "src/core/SkImagePriv.h" |
35 | | #include "src/core/SkMatrixPriv.h" |
36 | | #include "src/core/SkRasterClip.h" |
37 | | #include "src/core/SkSpecialImage.h" |
38 | | #include "src/image/SkImage_Base.h" |
39 | | #include "src/text/GlyphRun.h" |
40 | | |
41 | | #include <utility> |
42 | | |
43 | | class SkVertices; |
44 | | |
45 | | struct Bounder { |
46 | | SkRect fBounds; |
47 | | bool fHasBounds; |
48 | | |
49 | 137k | Bounder(const SkRect& r, const SkPaint& paint) { |
50 | 137k | if ((fHasBounds = paint.canComputeFastBounds())) { |
51 | 110k | fBounds = paint.computeFastBounds(r, &fBounds); |
52 | 110k | } |
53 | 137k | } |
54 | | |
55 | 0 | bool hasBounds() const { return fHasBounds; } |
56 | 137k | const SkRect* bounds() const { return fHasBounds ? &fBounds : nullptr; } |
57 | 4.30k | operator const SkRect* () const { return this->bounds(); } |
58 | | }; |
59 | | |
60 | | class SkDrawTiler { |
61 | | enum { |
62 | | // 8K is 1 too big, since 8K << supersample == 32768 which is too big for SkFixed |
63 | | kMaxDim = 8192 - 1 |
64 | | }; |
65 | | |
66 | | SkBitmapDevice* fDevice; |
67 | | SkPixmap fRootPixmap; |
68 | | SkIRect fSrcBounds; |
69 | | |
70 | | // Used for tiling and non-tiling |
71 | | SkDraw fDraw; |
72 | | |
73 | | // fTileMatrix... are only used if fNeedTiling |
74 | | SkTLazy<SkMatrix> fTileMatrix; |
75 | | SkRasterClip fTileRC; |
76 | | SkIPoint fOrigin; |
77 | | |
78 | | bool fDone, fNeedsTiling; |
79 | | |
80 | | public: |
81 | 205k | static bool NeedsTiling(SkBitmapDevice* dev) { |
82 | 205k | return dev->width() > kMaxDim || dev->height() > kMaxDim; |
83 | 205k | } |
84 | | |
85 | 317k | SkDrawTiler(SkBitmapDevice* dev, const SkRect* bounds) : fDevice(dev) { |
86 | 317k | fDone = false; |
87 | | |
88 | | // we need fDst to be set, and if we're actually drawing, to dirty the genID |
89 | 317k | if (!dev->accessPixels(&fRootPixmap)) { |
90 | | // NoDrawDevice uses us (why?) so we have to catch this case w/ no pixels |
91 | 0 | fRootPixmap.reset(dev->imageInfo(), nullptr, 0); |
92 | 0 | } |
93 | | |
94 | | // do a quick check, so we don't even have to process "bounds" if there is no need |
95 | 317k | const SkIRect clipR = dev->fRCStack.rc().getBounds(); |
96 | 317k | fNeedsTiling = clipR.right() > kMaxDim || clipR.bottom() > kMaxDim; |
97 | 317k | if (fNeedsTiling) { |
98 | 135k | if (bounds) { |
99 | | // Make sure we round first, and then intersect. We can't rely on promoting the |
100 | | // clipR to floats (and then intersecting with devBounds) since promoting |
101 | | // int --> float can make the float larger than the int. |
102 | | // rounding(out) first runs the risk of clamping if the float is larger an intmax |
103 | | // but our roundOut() is saturating, which is fine for this use case |
104 | | // |
105 | | // e.g. the older version of this code did this: |
106 | | // devBounds = mapRect(bounds); |
107 | | // if (devBounds.intersect(SkRect::Make(clipR))) { |
108 | | // fSrcBounds = devBounds.roundOut(); |
109 | | // The problem being that the promotion of clipR to SkRect was unreliable |
110 | | // |
111 | 107k | fSrcBounds = dev->localToDevice().mapRect(*bounds).roundOut(); |
112 | 107k | if (fSrcBounds.intersect(clipR)) { |
113 | | // Check again, now that we have computed srcbounds. |
114 | 90.9k | fNeedsTiling = fSrcBounds.right() > kMaxDim || fSrcBounds.bottom() > kMaxDim; |
115 | 90.9k | } else { |
116 | 16.5k | fNeedsTiling = false; |
117 | 16.5k | fDone = true; |
118 | 16.5k | } |
119 | 107k | } else { |
120 | 28.0k | fSrcBounds = clipR; |
121 | 28.0k | } |
122 | 135k | } |
123 | | |
124 | 317k | if (fNeedsTiling) { |
125 | | // fDraw.fDst and fCTM are reset each time in setupTileDraw() |
126 | 46.4k | fDraw.fRC = &fTileRC; |
127 | | // we'll step/increase it before using it |
128 | 46.4k | fOrigin.set(fSrcBounds.fLeft - kMaxDim, fSrcBounds.fTop); |
129 | 270k | } else { |
130 | | // don't reference fSrcBounds, as it may not have been set |
131 | 270k | fDraw.fDst = fRootPixmap; |
132 | 270k | fDraw.fCTM = &dev->localToDevice(); |
133 | 270k | fDraw.fRC = &dev->fRCStack.rc(); |
134 | 270k | fOrigin.set(0, 0); |
135 | 270k | } |
136 | | |
137 | 317k | fDraw.fProps = &fDevice->surfaceProps(); |
138 | 317k | } |
139 | | |
140 | 205k | bool needsTiling() const { return fNeedsTiling; } |
141 | | |
142 | 789k | const SkDraw* next() { |
143 | 789k | if (fDone) { |
144 | 317k | return nullptr; |
145 | 317k | } |
146 | 472k | if (fNeedsTiling) { |
147 | 218k | do { |
148 | 218k | this->stepAndSetupTileDraw(); // might set the clip to empty and fDone to true |
149 | 218k | } while (!fDone && fTileRC.isEmpty()); |
150 | | // if we exit the loop and we're still empty, we're (past) done |
151 | 218k | if (fTileRC.isEmpty()) { |
152 | 3 | SkASSERT(fDone); |
153 | 3 | return nullptr; |
154 | 3 | } |
155 | 218k | SkASSERT(!fTileRC.isEmpty()); |
156 | 254k | } else { |
157 | 254k | fDone = true; // only draw untiled once |
158 | 254k | } |
159 | 472k | return &fDraw; |
160 | 472k | } Line | Count | Source | 142 | 789k | const SkDraw* next() { | 143 | 789k | if (fDone) { | 144 | 317k | return nullptr; | 145 | 317k | } | 146 | 472k | if (fNeedsTiling) { | 147 | 218k | do { | 148 | 218k | this->stepAndSetupTileDraw(); // might set the clip to empty and fDone to true | 149 | 218k | } while (!fDone && fTileRC.isEmpty()); | 150 | | // if we exit the loop and we're still empty, we're (past) done | 151 | 218k | if (fTileRC.isEmpty()) { | 152 | 3 | SkASSERT(fDone); | 153 | 3 | return nullptr; | 154 | 3 | } | 155 | 218k | SkASSERT(!fTileRC.isEmpty()); | 156 | 254k | } else { | 157 | 254k | fDone = true; // only draw untiled once | 158 | 254k | } | 159 | 472k | return &fDraw; | 160 | 472k | } |
Unexecuted instantiation: SkDrawTiler::next() |
161 | | |
162 | | private: |
163 | 218k | void stepAndSetupTileDraw() { |
164 | 218k | SkASSERT(!fDone); |
165 | 218k | SkASSERT(fNeedsTiling); |
166 | | |
167 | | // We do fRootPixmap.width() - kMaxDim instead of fOrigin.fX + kMaxDim to avoid overflow. |
168 | 218k | if (fOrigin.fX >= fSrcBounds.fRight - kMaxDim) { // too far |
169 | 7.76k | fOrigin.fX = fSrcBounds.fLeft; |
170 | 7.76k | fOrigin.fY += kMaxDim; |
171 | 210k | } else { |
172 | 210k | fOrigin.fX += kMaxDim; |
173 | 210k | } |
174 | | // fDone = next origin will be invalid. |
175 | 218k | fDone = fOrigin.fX >= fSrcBounds.fRight - kMaxDim && |
176 | 218k | fOrigin.fY >= fSrcBounds.fBottom - kMaxDim; |
177 | | |
178 | 218k | SkIRect bounds = SkIRect::MakeXYWH(fOrigin.x(), fOrigin.y(), kMaxDim, kMaxDim); |
179 | 218k | SkASSERT(!bounds.isEmpty()); |
180 | 218k | bool success = fRootPixmap.extractSubset(&fDraw.fDst, bounds); |
181 | 218k | SkASSERT_RELEASE(success); |
182 | | // now don't use bounds, since fDst has the clipped dimensions. |
183 | | |
184 | 218k | fTileMatrix.init(fDevice->localToDevice()); |
185 | 218k | fTileMatrix->postTranslate(-fOrigin.x(), -fOrigin.y()); |
186 | 218k | fDraw.fCTM = fTileMatrix.get(); |
187 | 218k | fDevice->fRCStack.rc().translate(-fOrigin.x(), -fOrigin.y(), &fTileRC); |
188 | 218k | fTileRC.op(SkIRect::MakeSize(fDraw.fDst.dimensions()), SkClipOp::kIntersect); |
189 | 218k | } SkDrawTiler::stepAndSetupTileDraw() Line | Count | Source | 163 | 218k | void stepAndSetupTileDraw() { | 164 | 218k | SkASSERT(!fDone); | 165 | 218k | SkASSERT(fNeedsTiling); | 166 | | | 167 | | // We do fRootPixmap.width() - kMaxDim instead of fOrigin.fX + kMaxDim to avoid overflow. | 168 | 218k | if (fOrigin.fX >= fSrcBounds.fRight - kMaxDim) { // too far | 169 | 7.76k | fOrigin.fX = fSrcBounds.fLeft; | 170 | 7.76k | fOrigin.fY += kMaxDim; | 171 | 210k | } else { | 172 | 210k | fOrigin.fX += kMaxDim; | 173 | 210k | } | 174 | | // fDone = next origin will be invalid. | 175 | 218k | fDone = fOrigin.fX >= fSrcBounds.fRight - kMaxDim && | 176 | 218k | fOrigin.fY >= fSrcBounds.fBottom - kMaxDim; | 177 | | | 178 | 218k | SkIRect bounds = SkIRect::MakeXYWH(fOrigin.x(), fOrigin.y(), kMaxDim, kMaxDim); | 179 | 218k | SkASSERT(!bounds.isEmpty()); | 180 | 218k | bool success = fRootPixmap.extractSubset(&fDraw.fDst, bounds); | 181 | 218k | SkASSERT_RELEASE(success); | 182 | | // now don't use bounds, since fDst has the clipped dimensions. | 183 | | | 184 | 218k | fTileMatrix.init(fDevice->localToDevice()); | 185 | 218k | fTileMatrix->postTranslate(-fOrigin.x(), -fOrigin.y()); | 186 | 218k | fDraw.fCTM = fTileMatrix.get(); | 187 | 218k | fDevice->fRCStack.rc().translate(-fOrigin.x(), -fOrigin.y(), &fTileRC); | 188 | 218k | fTileRC.op(SkIRect::MakeSize(fDraw.fDst.dimensions()), SkClipOp::kIntersect); | 189 | 218k | } |
Unexecuted instantiation: SkDrawTiler::stepAndSetupTileDraw() |
190 | | }; |
191 | | |
192 | | // Passing a bounds allows the tiler to only visit the dst-tiles that might intersect the |
193 | | // drawing. If null is passed, the tiler has to visit everywhere. The bounds is expected to be |
194 | | // in local coordinates, as the tiler itself will transform that into device coordinates. |
195 | | // |
196 | | #define LOOP_TILER(code, boundsPtr) \ |
197 | 111k | SkDrawTiler priv_tiler(this, boundsPtr); \ |
198 | 230k | while (const SkDraw* priv_draw = priv_tiler.next()) { \ |
199 | 118k | priv_draw->code; \ |
200 | 118k | } |
201 | | |
202 | | // Helper to create an SkDraw from a device |
203 | | class SkBitmapDevice::BDDraw : public SkDraw { |
204 | | public: |
205 | 189k | BDDraw(SkBitmapDevice* dev) { |
206 | | // we need fDst to be set, and if we're actually drawing, to dirty the genID |
207 | 189k | if (!dev->accessPixels(&fDst)) { |
208 | | // NoDrawDevice uses us (why?) so we have to catch this case w/ no pixels |
209 | 0 | fDst.reset(dev->imageInfo(), nullptr, 0); |
210 | 0 | } |
211 | 189k | fCTM = &dev->localToDevice(); |
212 | 189k | fRC = &dev->fRCStack.rc(); |
213 | 189k | } |
214 | | }; |
215 | | |
216 | | static bool valid_for_bitmap_device(const SkImageInfo& info, |
217 | 98.3k | SkAlphaType* newAlphaType) { |
218 | 98.3k | if (info.width() < 0 || info.height() < 0 || kUnknown_SkColorType == info.colorType()) { |
219 | 598 | return false; |
220 | 598 | } |
221 | | |
222 | 97.7k | if (newAlphaType) { |
223 | 97.7k | *newAlphaType = SkColorTypeIsAlwaysOpaque(info.colorType()) ? kOpaque_SkAlphaType |
224 | 97.7k | : info.alphaType(); |
225 | 97.7k | } |
226 | | |
227 | 97.7k | return true; |
228 | 98.3k | } |
229 | | |
230 | | SkBitmapDevice::SkBitmapDevice(const SkBitmap& bitmap) |
231 | | : SkDevice(bitmap.info(), SkSurfaceProps()) |
232 | | , fBitmap(bitmap) |
233 | | , fRCStack(bitmap.width(), bitmap.height()) |
234 | 0 | , fGlyphPainter(this->surfaceProps(), bitmap.colorType(), bitmap.colorSpace()) { |
235 | 0 | SkASSERT(valid_for_bitmap_device(bitmap.info(), nullptr)); |
236 | 0 | } Unexecuted instantiation: SkBitmapDevice::SkBitmapDevice(SkBitmap const&) Unexecuted instantiation: SkBitmapDevice::SkBitmapDevice(SkBitmap const&) |
237 | | |
238 | | SkBitmapDevice::SkBitmapDevice(const SkBitmap& bitmap, const SkSurfaceProps& surfaceProps, |
239 | | SkRasterHandleAllocator::Handle hndl) |
240 | | : SkDevice(bitmap.info(), surfaceProps) |
241 | | , fBitmap(bitmap) |
242 | | , fRasterHandle(hndl) |
243 | | , fRCStack(bitmap.width(), bitmap.height()) |
244 | 192k | , fGlyphPainter(this->surfaceProps(), bitmap.colorType(), bitmap.colorSpace()) { |
245 | 192k | SkASSERT(valid_for_bitmap_device(bitmap.info(), nullptr)); |
246 | 192k | } |
247 | | |
248 | | sk_sp<SkBitmapDevice> SkBitmapDevice::Create(const SkImageInfo& origInfo, |
249 | | const SkSurfaceProps& surfaceProps, |
250 | 98.3k | SkRasterHandleAllocator* allocator) { |
251 | 98.3k | SkAlphaType newAT = origInfo.alphaType(); |
252 | 98.3k | if (!valid_for_bitmap_device(origInfo, &newAT)) { |
253 | 598 | return nullptr; |
254 | 598 | } |
255 | | |
256 | 97.7k | SkRasterHandleAllocator::Handle hndl = nullptr; |
257 | 97.7k | const SkImageInfo info = origInfo.makeAlphaType(newAT); |
258 | 97.7k | SkBitmap bitmap; |
259 | | |
260 | 97.7k | if (kUnknown_SkColorType == info.colorType()) { |
261 | 0 | if (!bitmap.setInfo(info)) { |
262 | 0 | return nullptr; |
263 | 0 | } |
264 | 97.7k | } else if (allocator) { |
265 | 0 | hndl = allocator->allocBitmap(info, &bitmap); |
266 | 0 | if (!hndl) { |
267 | 0 | return nullptr; |
268 | 0 | } |
269 | 97.7k | } else if (info.isOpaque()) { |
270 | | // If this bitmap is opaque, we don't have any sensible default color, |
271 | | // so we just return uninitialized pixels. |
272 | 0 | if (!bitmap.tryAllocPixels(info)) { |
273 | 0 | return nullptr; |
274 | 0 | } |
275 | 97.7k | } else { |
276 | | // This bitmap has transparency, so we'll zero the pixels (to transparent). |
277 | | // We use the flag as a faster alloc-then-eraseColor(SK_ColorTRANSPARENT). |
278 | 97.7k | if (!bitmap.tryAllocPixelsFlags(info, SkBitmap::kZeroPixels_AllocFlag)) { |
279 | 6.41k | return nullptr; |
280 | 6.41k | } |
281 | 97.7k | } |
282 | | |
283 | 91.3k | return sk_make_sp<SkBitmapDevice>(bitmap, surfaceProps, hndl); |
284 | 97.7k | } |
285 | | |
286 | 0 | void SkBitmapDevice::replaceBitmapBackendForRasterSurface(const SkBitmap& bm) { |
287 | 0 | SkASSERT(bm.width() == fBitmap.width()); |
288 | 0 | SkASSERT(bm.height() == fBitmap.height()); |
289 | 0 | fBitmap = bm; // intent is to use bm's pixelRef (and rowbytes/config) |
290 | 0 | } Unexecuted instantiation: SkBitmapDevice::replaceBitmapBackendForRasterSurface(SkBitmap const&) Unexecuted instantiation: SkBitmapDevice::replaceBitmapBackendForRasterSurface(SkBitmap const&) |
291 | | |
292 | 14.8k | sk_sp<SkDevice> SkBitmapDevice::createDevice(const CreateInfo& cinfo, const SkPaint* layerPaint) { |
293 | 14.8k | const SkSurfaceProps surfaceProps = |
294 | 14.8k | this->surfaceProps().cloneWithPixelGeometry(cinfo.fPixelGeometry); |
295 | | |
296 | | // Need to force L32 for now if we have an image filter. |
297 | | // If filters ever support other colortypes, e.g. F16, we can modify this check. |
298 | 14.8k | SkImageInfo info = cinfo.fInfo; |
299 | 14.8k | if (layerPaint && layerPaint->getImageFilter()) { |
300 | | // TODO: can we query the imagefilter, to see if it can handle floats (so we don't always |
301 | | // use N32 when the layer itself was float)? |
302 | 2.11k | info = info.makeColorType(kN32_SkColorType); |
303 | 2.11k | } |
304 | | |
305 | 14.8k | return SkBitmapDevice::Create(info, surfaceProps, cinfo.fAllocator); |
306 | 14.8k | } |
307 | | |
308 | 613k | bool SkBitmapDevice::onAccessPixels(SkPixmap* pmap) { |
309 | 613k | if (this->onPeekPixels(pmap)) { |
310 | 613k | fBitmap.notifyPixelsChanged(); |
311 | 613k | return true; |
312 | 613k | } |
313 | 0 | return false; |
314 | 613k | } |
315 | | |
316 | 613k | bool SkBitmapDevice::onPeekPixels(SkPixmap* pmap) { |
317 | 613k | const SkImageInfo info = fBitmap.info(); |
318 | 613k | if (fBitmap.getPixels() && (kUnknown_SkColorType != info.colorType())) { |
319 | 613k | pmap->reset(fBitmap.info(), fBitmap.getPixels(), fBitmap.rowBytes()); |
320 | 613k | return true; |
321 | 613k | } |
322 | 0 | return false; |
323 | 613k | } |
324 | | |
325 | 0 | bool SkBitmapDevice::onWritePixels(const SkPixmap& pm, int x, int y) { |
326 | | // since we don't stop creating un-pixeled devices yet, check for no pixels here |
327 | 0 | if (nullptr == fBitmap.getPixels()) { |
328 | 0 | return false; |
329 | 0 | } |
330 | | |
331 | 0 | if (fBitmap.writePixels(pm, x, y)) { |
332 | 0 | fBitmap.notifyPixelsChanged(); |
333 | 0 | return true; |
334 | 0 | } |
335 | 0 | return false; |
336 | 0 | } |
337 | | |
338 | 0 | bool SkBitmapDevice::onReadPixels(const SkPixmap& pm, int x, int y) { |
339 | 0 | return fBitmap.readPixels(pm, x, y); |
340 | 0 | } |
341 | | |
342 | | /////////////////////////////////////////////////////////////////////////////// |
343 | | |
344 | 170k | void SkBitmapDevice::drawPaint(const SkPaint& paint) { |
345 | 170k | BDDraw(this).drawPaint(paint); |
346 | 170k | } |
347 | | |
348 | | void SkBitmapDevice::drawPoints(SkCanvas::PointMode mode, size_t count, |
349 | 7.98k | const SkPoint pts[], const SkPaint& paint) { |
350 | 7.98k | LOOP_TILER( drawPoints(mode, count, pts, paint, nullptr), nullptr) |
351 | 7.98k | } |
352 | | |
353 | 3.83k | void SkBitmapDevice::drawRect(const SkRect& r, const SkPaint& paint) { |
354 | 3.83k | LOOP_TILER( drawRect(r, paint), Bounder(r, paint)) |
355 | 3.83k | } |
356 | | |
357 | 1.63k | void SkBitmapDevice::drawOval(const SkRect& oval, const SkPaint& paint) { |
358 | | // call the VIRTUAL version, so any subclasses who do handle drawPath aren't |
359 | | // required to override drawOval. |
360 | 1.63k | this->drawPath(SkPath::Oval(oval), paint, true); |
361 | 1.63k | } |
362 | | |
363 | 469 | void SkBitmapDevice::drawRRect(const SkRRect& rrect, const SkPaint& paint) { |
364 | | #ifdef SK_IGNORE_BLURRED_RRECT_OPT |
365 | | // call the VIRTUAL version, so any subclasses who do handle drawPath aren't |
366 | | // required to override drawRRect. |
367 | | this->drawPath(SkPath::RRect(rrect), paint, true); |
368 | | #else |
369 | 469 | LOOP_TILER( drawRRect(rrect, paint), Bounder(rrect.getBounds(), paint)) |
370 | 469 | #endif |
371 | 469 | } |
372 | | |
373 | | void SkBitmapDevice::drawPath(const SkPath& path, |
374 | | const SkPaint& paint, |
375 | 205k | bool pathIsMutable) { |
376 | 205k | const SkRect* bounds = nullptr; |
377 | 205k | if (SkDrawTiler::NeedsTiling(this) && !path.isInverseFillType()) { |
378 | 133k | bounds = &path.getBounds(); |
379 | 133k | } |
380 | 205k | SkDrawTiler tiler(this, bounds ? Bounder(*bounds, paint).bounds() : nullptr); |
381 | 205k | if (tiler.needsTiling()) { |
382 | 44.5k | pathIsMutable = false; |
383 | 44.5k | } |
384 | 559k | while (const SkDraw* draw = tiler.next()) { |
385 | 354k | draw->drawPath(path, paint, nullptr, pathIsMutable); |
386 | 354k | } |
387 | 205k | } |
388 | | |
389 | | void SkBitmapDevice::drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix, |
390 | | const SkRect* dstOrNull, const SkSamplingOptions& sampling, |
391 | 69.7k | const SkPaint& paint) { |
392 | 69.7k | const SkRect* bounds = dstOrNull; |
393 | 69.7k | SkRect storage; |
394 | 69.7k | if (!bounds && SkDrawTiler::NeedsTiling(this)) { |
395 | 0 | matrix.mapRect(&storage, SkRect::MakeIWH(bitmap.width(), bitmap.height())); |
396 | 0 | Bounder b(storage, paint); |
397 | 0 | if (b.hasBounds()) { |
398 | 0 | storage = *b.bounds(); |
399 | 0 | bounds = &storage; |
400 | 0 | } |
401 | 0 | } |
402 | 69.7k | LOOP_TILER(drawBitmap(bitmap, matrix, dstOrNull, sampling, paint), bounds) |
403 | 69.7k | } |
404 | | |
405 | 69.7k | static inline bool CanApplyDstMatrixAsCTM(const SkMatrix& m, const SkPaint& paint) { |
406 | 69.7k | if (!paint.getMaskFilter()) { |
407 | 69.7k | return true; |
408 | 69.7k | } |
409 | | |
410 | | // Some mask filters parameters (sigma) depend on the CTM/scale. |
411 | 28 | return m.getType() <= SkMatrix::kTranslate_Mask; |
412 | 69.7k | } |
413 | | |
414 | | void SkBitmapDevice::drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst, |
415 | | const SkSamplingOptions& sampling, const SkPaint& paint, |
416 | 74.7k | SkCanvas::SrcRectConstraint constraint) { |
417 | 74.7k | SkASSERT(dst.isFinite()); |
418 | 74.7k | SkASSERT(dst.isSorted()); |
419 | | |
420 | 74.7k | SkBitmap bitmap; |
421 | | // TODO: Elevate direct context requirement to public API and remove cheat. |
422 | 74.7k | auto dContext = as_IB(image)->directContext(); |
423 | 74.7k | if (!as_IB(image)->getROPixels(dContext, &bitmap)) { |
424 | 4.03k | return; |
425 | 4.03k | } |
426 | | |
427 | 70.6k | SkRect bitmapBounds, tmpSrc, tmpDst; |
428 | 70.6k | SkBitmap tmpBitmap; |
429 | | |
430 | 70.6k | bitmapBounds.setIWH(bitmap.width(), bitmap.height()); |
431 | | |
432 | | // Compute matrix from the two rectangles |
433 | 70.6k | if (src) { |
434 | 70.6k | tmpSrc = *src; |
435 | 70.6k | } else { |
436 | 0 | tmpSrc = bitmapBounds; |
437 | 0 | } |
438 | 70.6k | SkMatrix matrix = SkMatrix::RectToRect(tmpSrc, dst); |
439 | | |
440 | 70.6k | const SkRect* dstPtr = &dst; |
441 | 70.6k | const SkBitmap* bitmapPtr = &bitmap; |
442 | | |
443 | | // clip the tmpSrc to the bounds of the bitmap, and recompute dstRect if |
444 | | // needed (if the src was clipped). No check needed if src==null. |
445 | 70.6k | bool srcIsSubset = false; |
446 | 70.6k | if (src) { |
447 | 70.6k | if (!bitmapBounds.contains(*src)) { |
448 | 137 | if (!tmpSrc.intersect(bitmapBounds)) { |
449 | 12 | return; // nothing to draw |
450 | 12 | } |
451 | | // recompute dst, based on the smaller tmpSrc |
452 | 125 | matrix.mapRect(&tmpDst, tmpSrc); |
453 | 125 | if (!tmpDst.isFinite()) { |
454 | 4 | return; |
455 | 4 | } |
456 | 121 | dstPtr = &tmpDst; |
457 | 121 | } |
458 | 70.6k | srcIsSubset = !tmpSrc.contains(bitmapBounds); |
459 | 70.6k | } |
460 | | |
461 | 70.6k | if (srcIsSubset && |
462 | 70.6k | SkCanvas::kFast_SrcRectConstraint == constraint && |
463 | 70.6k | sampling != SkSamplingOptions()) { |
464 | | // src is smaller than the bounds of the bitmap, and we are filtering, so we don't know |
465 | | // how much more of the bitmap we need, so we can't use extractSubset or drawBitmap, |
466 | | // but we must use a shader w/ dst bounds (which can access all of the bitmap needed). |
467 | 0 | goto USE_SHADER; |
468 | 0 | } |
469 | | |
470 | 70.6k | if (srcIsSubset) { |
471 | | // since we may need to clamp to the borders of the src rect within |
472 | | // the bitmap, we extract a subset. |
473 | 914 | const SkIRect srcIR = tmpSrc.roundOut(); |
474 | 914 | if (!bitmap.extractSubset(&tmpBitmap, srcIR)) { |
475 | 0 | return; |
476 | 0 | } |
477 | 914 | bitmapPtr = &tmpBitmap; |
478 | | |
479 | | // Since we did an extract, we need to adjust the matrix accordingly |
480 | 914 | SkScalar dx = 0, dy = 0; |
481 | 914 | if (srcIR.fLeft > 0) { |
482 | 289 | dx = SkIntToScalar(srcIR.fLeft); |
483 | 289 | } |
484 | 914 | if (srcIR.fTop > 0) { |
485 | 299 | dy = SkIntToScalar(srcIR.fTop); |
486 | 299 | } |
487 | 914 | if (dx || dy) { |
488 | 410 | matrix.preTranslate(dx, dy); |
489 | 410 | } |
490 | | |
491 | | #ifdef SK_DRAWBITMAPRECT_FAST_OFFSET |
492 | | SkRect extractedBitmapBounds = SkRect::MakeXYWH(dx, dy, |
493 | | SkIntToScalar(bitmapPtr->width()), |
494 | | SkIntToScalar(bitmapPtr->height())); |
495 | | #else |
496 | 914 | SkRect extractedBitmapBounds; |
497 | 914 | extractedBitmapBounds.setIWH(bitmapPtr->width(), bitmapPtr->height()); |
498 | 914 | #endif |
499 | 914 | if (extractedBitmapBounds == tmpSrc) { |
500 | | // no fractional part in src, we can just call drawBitmap |
501 | 24 | goto USE_DRAWBITMAP; |
502 | 24 | } |
503 | 69.7k | } else { |
504 | 69.7k | USE_DRAWBITMAP: |
505 | | // We can go faster by just calling drawBitmap, which will concat the |
506 | | // matrix with the CTM, and try to call drawSprite if it can. If not, |
507 | | // it will make a shader and call drawRect, as we do below. |
508 | 69.7k | if (CanApplyDstMatrixAsCTM(matrix, paint)) { |
509 | 69.7k | this->drawBitmap(*bitmapPtr, matrix, dstPtr, sampling, paint); |
510 | 69.7k | return; |
511 | 69.7k | } |
512 | 69.7k | } |
513 | | |
514 | 891 | USE_SHADER: |
515 | | |
516 | | // construct a shader, so we can call drawRect with the dst |
517 | 891 | auto s = SkMakeBitmapShaderForPaint(paint, *bitmapPtr, SkTileMode::kClamp, SkTileMode::kClamp, |
518 | 891 | sampling, &matrix, kNever_SkCopyPixelsMode); |
519 | 891 | if (!s) { |
520 | 5 | return; |
521 | 5 | } |
522 | | |
523 | 886 | SkPaint paintWithShader(paint); |
524 | 886 | paintWithShader.setStyle(SkPaint::kFill_Style); |
525 | 886 | paintWithShader.setShader(std::move(s)); |
526 | | |
527 | | // Call ourself, in case the subclass wanted to share this setup code |
528 | | // but handle the drawRect code themselves. |
529 | 886 | this->drawRect(*dstPtr, paintWithShader); |
530 | 886 | } |
531 | | |
532 | | void SkBitmapDevice::onDrawGlyphRunList(SkCanvas* canvas, |
533 | | const sktext::GlyphRunList& glyphRunList, |
534 | 29.8k | const SkPaint& paint) { |
535 | 29.8k | SkASSERT(!glyphRunList.hasRSXForm()); |
536 | 29.8k | LOOP_TILER( drawGlyphRunList(canvas, &fGlyphPainter, glyphRunList, paint), nullptr ) |
537 | 29.8k | } |
538 | | |
539 | | void SkBitmapDevice::drawVertices(const SkVertices* vertices, |
540 | | sk_sp<SkBlender> blender, |
541 | | const SkPaint& paint, |
542 | 18.6k | bool skipColorXform) { |
543 | | #ifdef SK_LEGACY_IGNORE_DRAW_VERTICES_BLEND_WITH_NO_SHADER |
544 | | if (!paint.getShader()) { |
545 | | blender = SkBlender::Mode(SkBlendMode::kDst); |
546 | | } |
547 | | #endif |
548 | 18.6k | BDDraw(this).drawVertices(vertices, std::move(blender), paint, skipColorXform); |
549 | 18.6k | } |
550 | | |
551 | 0 | void SkBitmapDevice::drawMesh(const SkMesh&, sk_sp<SkBlender>, const SkPaint&) { |
552 | | // TODO(brianosman): Implement, maybe with a subclass of BitmapDevice that has SkSL support. |
553 | 0 | } |
554 | | |
555 | | void SkBitmapDevice::drawAtlas(const SkRSXform xform[], |
556 | | const SkRect tex[], |
557 | | const SkColor colors[], |
558 | | int count, |
559 | | sk_sp<SkBlender> blender, |
560 | 33 | const SkPaint& paint) { |
561 | | // set this to true for performance comparisons with the old drawVertices way |
562 | 33 | if ((false)) { |
563 | 0 | this->SkDevice::drawAtlas(xform, tex, colors, count, std::move(blender), paint); |
564 | 0 | return; |
565 | 0 | } |
566 | 33 | BDDraw(this).drawAtlas(xform, tex, colors, count, std::move(blender), paint); |
567 | 33 | } |
568 | | |
569 | | /////////////////////////////////////////////////////////////////////////////// |
570 | | |
571 | | void SkBitmapDevice::drawSpecial(SkSpecialImage* src, |
572 | | const SkMatrix& localToDevice, |
573 | | const SkSamplingOptions& sampling, |
574 | | const SkPaint& paint, |
575 | 106k | SkCanvas::SrcRectConstraint) { |
576 | 106k | SkASSERT(!paint.getImageFilter()); |
577 | 106k | SkASSERT(!paint.getMaskFilter()); |
578 | 106k | SkASSERT(!src->isGaneshBacked()); |
579 | 106k | SkASSERT(!src->isGraphiteBacked()); |
580 | | |
581 | 106k | SkBitmap resultBM; |
582 | 106k | if (SkSpecialImages::AsBitmap(src, &resultBM)) { |
583 | 106k | SkDraw draw; |
584 | 106k | if (!this->accessPixels(&draw.fDst)) { |
585 | 0 | return; // no pixels to draw to so skip it |
586 | 0 | } |
587 | 106k | draw.fCTM = &localToDevice; |
588 | 106k | draw.fRC = &fRCStack.rc(); |
589 | 106k | draw.drawBitmap(resultBM, SkMatrix::I(), nullptr, sampling, paint); |
590 | 106k | } |
591 | 106k | } |
592 | 0 | sk_sp<SkSpecialImage> SkBitmapDevice::makeSpecial(const SkBitmap& bitmap) { |
593 | 0 | return SkSpecialImages::MakeFromRaster(bitmap.bounds(), bitmap, this->surfaceProps()); |
594 | 0 | } |
595 | | |
596 | 0 | sk_sp<SkSpecialImage> SkBitmapDevice::makeSpecial(const SkImage* image) { |
597 | 0 | return SkSpecialImages::MakeFromRaster(SkIRect::MakeWH(image->width(), image->height()), |
598 | 0 | image->makeNonTextureImage(), |
599 | 0 | this->surfaceProps()); |
600 | 0 | } |
601 | | |
602 | 94.9k | sk_sp<SkSpecialImage> SkBitmapDevice::snapSpecial(const SkIRect& bounds, bool forceCopy) { |
603 | 94.9k | if (forceCopy) { |
604 | 597 | return SkSpecialImages::CopyFromRaster(bounds, fBitmap, this->surfaceProps()); |
605 | 94.3k | } else { |
606 | 94.3k | return SkSpecialImages::MakeFromRaster(bounds, fBitmap, this->surfaceProps()); |
607 | 94.3k | } |
608 | 94.9k | } |
609 | | |
610 | | /////////////////////////////////////////////////////////////////////////////// |
611 | | |
612 | 0 | sk_sp<SkSurface> SkBitmapDevice::makeSurface(const SkImageInfo& info, const SkSurfaceProps& props) { |
613 | 0 | return SkSurfaces::Raster(info, &props); |
614 | 0 | } |
615 | | |
616 | | /////////////////////////////////////////////////////////////////////////////////////////////////// |
617 | | |
618 | 136k | void SkBitmapDevice::pushClipStack() { |
619 | 136k | fRCStack.save(); |
620 | 136k | } |
621 | | |
622 | 136k | void SkBitmapDevice::popClipStack() { |
623 | 136k | fRCStack.restore(); |
624 | 136k | } |
625 | | |
626 | 126k | void SkBitmapDevice::clipRect(const SkRect& rect, SkClipOp op, bool aa) { |
627 | 126k | fRCStack.clipRect(this->localToDevice(), rect, op, aa); |
628 | 126k | } |
629 | | |
630 | 2.76k | void SkBitmapDevice::clipRRect(const SkRRect& rrect, SkClipOp op, bool aa) { |
631 | 2.76k | fRCStack.clipRRect(this->localToDevice(), rrect, op, aa); |
632 | 2.76k | } |
633 | | |
634 | 13.4k | void SkBitmapDevice::clipPath(const SkPath& path, SkClipOp op, bool aa) { |
635 | 13.4k | fRCStack.clipPath(this->localToDevice(), path, op, aa); |
636 | 13.4k | } |
637 | | |
638 | 0 | void SkBitmapDevice::onClipShader(sk_sp<SkShader> sh) { |
639 | 0 | fRCStack.clipShader(std::move(sh)); |
640 | 0 | } |
641 | | |
642 | 2.08k | void SkBitmapDevice::clipRegion(const SkRegion& rgn, SkClipOp op) { |
643 | 2.08k | SkIPoint origin = this->getOrigin(); |
644 | 2.08k | SkRegion tmp; |
645 | 2.08k | const SkRegion* ptr = &rgn; |
646 | 2.08k | if (origin.fX | origin.fY) { |
647 | | // translate from "global/canvas" coordinates to relative to this device |
648 | 217 | rgn.translate(-origin.fX, -origin.fY, &tmp); |
649 | 217 | ptr = &tmp; |
650 | 217 | } |
651 | 2.08k | fRCStack.clipRegion(*ptr, op); |
652 | 2.08k | } |
653 | | |
654 | 948 | void SkBitmapDevice::replaceClip(const SkIRect& rect) { |
655 | | // Transform from "global/canvas" coordinates to relative to this device |
656 | 948 | SkRect deviceRect = SkMatrixPriv::MapRect(this->globalToDevice(), SkRect::Make(rect)); |
657 | 948 | fRCStack.replaceClip(deviceRect.round()); |
658 | 948 | } |
659 | | |
660 | 0 | bool SkBitmapDevice::isClipWideOpen() const { |
661 | 0 | const SkRasterClip& rc = fRCStack.rc(); |
662 | | // If we're AA, we can't be wide-open (we would represent that as BW) |
663 | 0 | return rc.isBW() && rc.bwRgn().isRect() && |
664 | 0 | rc.bwRgn().getBounds() == SkIRect{0, 0, this->width(), this->height()}; |
665 | 0 | } |
666 | | |
667 | 635k | bool SkBitmapDevice::isClipEmpty() const { |
668 | 635k | return fRCStack.rc().isEmpty(); |
669 | 635k | } |
670 | | |
671 | 0 | bool SkBitmapDevice::isClipRect() const { |
672 | 0 | const SkRasterClip& rc = fRCStack.rc(); |
673 | 0 | return !rc.isEmpty() && rc.isRect() && !SkToBool(rc.clipShader()); |
674 | 0 | } |
675 | | |
676 | 0 | bool SkBitmapDevice::isClipAntiAliased() const { |
677 | 0 | const SkRasterClip& rc = fRCStack.rc(); |
678 | 0 | return !rc.isEmpty() && rc.isAA(); |
679 | 0 | } |
680 | | |
681 | 0 | void SkBitmapDevice::android_utils_clipAsRgn(SkRegion* rgn) const { |
682 | 0 | const SkRasterClip& rc = fRCStack.rc(); |
683 | 0 | if (rc.isAA()) { |
684 | 0 | rgn->setRect( rc.getBounds()); |
685 | 0 | } else { |
686 | 0 | *rgn = rc.bwRgn(); |
687 | 0 | } |
688 | 0 | } |
689 | | |
690 | 571k | SkIRect SkBitmapDevice::devClipBounds() const { |
691 | 571k | return fRCStack.rc().getBounds(); |
692 | 571k | } |