1 /*
2 * Copyright 2016 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/SkSpecialImage.h"
9
10 #include "include/core/SkBitmap.h"
11 #include "include/core/SkCanvas.h"
12 #include "include/core/SkImage.h"
13 #include "src/core/SkSpecialSurface.h"
14 #include "src/core/SkSurfacePriv.h"
15 #include "src/image/SkImage_Base.h"
16
17 #if SK_SUPPORT_GPU
18 #include "include/gpu/GrDirectContext.h"
19 #include "include/gpu/GrRecordingContext.h"
20 #include "src/gpu/GrImageInfo.h"
21 #include "src/gpu/GrProxyProvider.h"
22 #include "src/gpu/GrRecordingContextPriv.h"
23 #include "src/gpu/GrTextureProxy.h"
24 #include "src/image/SkImage_Gpu.h"
25 #endif
26
27 // Currently the raster imagefilters can only handle certain imageinfos. Call this to know if
28 // a given info is supported.
valid_for_imagefilters(const SkImageInfo & info)29 static bool valid_for_imagefilters(const SkImageInfo& info) {
30 // no support for other swizzles/depths yet
31 return info.colorType() == kN32_SkColorType;
32 }
33
34 ///////////////////////////////////////////////////////////////////////////////
35 class SkSpecialImage_Base : public SkSpecialImage {
36 public:
SkSpecialImage_Base(const SkIRect & subset,uint32_t uniqueID,const SkSurfaceProps & props)37 SkSpecialImage_Base(const SkIRect& subset, uint32_t uniqueID, const SkSurfaceProps& props)
38 : INHERITED(subset, uniqueID, props) {
39 }
~SkSpecialImage_Base()40 ~SkSpecialImage_Base() override { }
41
42 virtual void onDraw(SkCanvas*, SkScalar x, SkScalar y, const SkSamplingOptions&,
43 const SkPaint*) const = 0;
44
45 virtual bool onGetROPixels(SkBitmap*) const = 0;
46
onGetContext() const47 virtual GrRecordingContext* onGetContext() const { return nullptr; }
48
49 virtual SkColorSpace* onGetColorSpace() const = 0;
50
51 #if SK_SUPPORT_GPU
52 virtual GrSurfaceProxyView onView(GrRecordingContext* context) const = 0;
53 #endif
54
55 // This subset is relative to the backing store's coordinate frame, it has already been mapped
56 // from the content rect by the non-virtual makeSubset().
57 virtual sk_sp<SkSpecialImage> onMakeSubset(const SkIRect& subset) const = 0;
58
59 virtual sk_sp<SkSpecialSurface> onMakeSurface(
60 SkColorType colorType, const SkColorSpace* colorSpace, const SkISize& size,
61 SkAlphaType at, const SkSurfaceProps&) const = 0;
62
63 // This subset (when not null) is relative to the backing store's coordinate frame, it has
64 // already been mapped from the content rect by the non-virtual asImage().
65 virtual sk_sp<SkImage> onAsImage(const SkIRect* subset) const = 0;
66
67 virtual sk_sp<SkSurface> onMakeTightSurface(
68 SkColorType colorType, const SkColorSpace* colorSpace,
69 const SkISize& size, SkAlphaType at) const = 0;
70
71 private:
72 using INHERITED = SkSpecialImage;
73 };
74
75 ///////////////////////////////////////////////////////////////////////////////
as_SIB(const SkSpecialImage * image)76 static inline const SkSpecialImage_Base* as_SIB(const SkSpecialImage* image) {
77 return static_cast<const SkSpecialImage_Base*>(image);
78 }
79
SkSpecialImage(const SkIRect & subset,uint32_t uniqueID,const SkSurfaceProps & props)80 SkSpecialImage::SkSpecialImage(const SkIRect& subset,
81 uint32_t uniqueID,
82 const SkSurfaceProps& props)
83 : fProps(props)
84 , fSubset(subset)
85 , fUniqueID(kNeedNewImageUniqueID_SpecialImage == uniqueID ? SkNextID::ImageID() : uniqueID) {
86 }
87
draw(SkCanvas * canvas,SkScalar x,SkScalar y,const SkSamplingOptions & sampling,const SkPaint * paint) const88 void SkSpecialImage::draw(SkCanvas* canvas, SkScalar x, SkScalar y,
89 const SkSamplingOptions& sampling, const SkPaint* paint) const {
90 return as_SIB(this)->onDraw(canvas, x, y, sampling, paint);
91 }
92
getROPixels(SkBitmap * bm) const93 bool SkSpecialImage::getROPixels(SkBitmap* bm) const {
94 return as_SIB(this)->onGetROPixels(bm);
95 }
96
isTextureBacked() const97 bool SkSpecialImage::isTextureBacked() const {
98 return SkToBool(as_SIB(this)->onGetContext());
99 }
100
getContext() const101 GrRecordingContext* SkSpecialImage::getContext() const {
102 return as_SIB(this)->onGetContext();
103 }
104
getColorSpace() const105 SkColorSpace* SkSpecialImage::getColorSpace() const {
106 return as_SIB(this)->onGetColorSpace();
107 }
108
109 #if SK_SUPPORT_GPU
view(GrRecordingContext * context) const110 GrSurfaceProxyView SkSpecialImage::view(GrRecordingContext* context) const {
111 return as_SIB(this)->onView(context);
112 }
113 #endif
114
makeSurface(SkColorType colorType,const SkColorSpace * colorSpace,const SkISize & size,SkAlphaType at,const SkSurfaceProps & props) const115 sk_sp<SkSpecialSurface> SkSpecialImage::makeSurface(
116 SkColorType colorType, const SkColorSpace* colorSpace, const SkISize& size,
117 SkAlphaType at, const SkSurfaceProps& props) const {
118 return as_SIB(this)->onMakeSurface(colorType, colorSpace, size, at, props);
119 }
120
makeTightSurface(SkColorType colorType,const SkColorSpace * colorSpace,const SkISize & size,SkAlphaType at) const121 sk_sp<SkSurface> SkSpecialImage::makeTightSurface(
122 SkColorType colorType, const SkColorSpace* colorSpace, const SkISize& size,
123 SkAlphaType at) const {
124 return as_SIB(this)->onMakeTightSurface(colorType, colorSpace, size, at);
125 }
126
makeSubset(const SkIRect & subset) const127 sk_sp<SkSpecialImage> SkSpecialImage::makeSubset(const SkIRect& subset) const {
128 SkIRect absolute = subset.makeOffset(this->subset().topLeft());
129 return as_SIB(this)->onMakeSubset(absolute);
130 }
131
asImage(const SkIRect * subset) const132 sk_sp<SkImage> SkSpecialImage::asImage(const SkIRect* subset) const {
133 if (subset) {
134 SkIRect absolute = subset->makeOffset(this->subset().topLeft());
135 return as_SIB(this)->onAsImage(&absolute);
136 } else {
137 return as_SIB(this)->onAsImage(nullptr);
138 }
139 }
140
141 #if defined(SK_DEBUG) || SK_SUPPORT_GPU
rect_fits(const SkIRect & rect,int width,int height)142 static bool rect_fits(const SkIRect& rect, int width, int height) {
143 if (0 == width && 0 == height) {
144 SkASSERT(0 == rect.fLeft && 0 == rect.fRight && 0 == rect.fTop && 0 == rect.fBottom);
145 return true;
146 }
147
148 return rect.fLeft >= 0 && rect.fLeft < width && rect.fLeft < rect.fRight &&
149 rect.fRight >= 0 && rect.fRight <= width &&
150 rect.fTop >= 0 && rect.fTop < height && rect.fTop < rect.fBottom &&
151 rect.fBottom >= 0 && rect.fBottom <= height;
152 }
153 #endif
154
MakeFromImage(GrRecordingContext * rContext,const SkIRect & subset,sk_sp<SkImage> image,const SkSurfaceProps & props)155 sk_sp<SkSpecialImage> SkSpecialImage::MakeFromImage(GrRecordingContext* rContext,
156 const SkIRect& subset,
157 sk_sp<SkImage> image,
158 const SkSurfaceProps& props) {
159 SkASSERT(rect_fits(subset, image->width(), image->height()));
160
161 #if SK_SUPPORT_GPU
162 if (rContext) {
163 auto [view, ct] = as_IB(image)->asView(rContext, GrMipmapped::kNo);
164 return MakeDeferredFromGpu(rContext,
165 subset,
166 image->uniqueID(),
167 std::move(view),
168 ct,
169 image->refColorSpace(),
170 props);
171 }
172 #endif
173
174 // raster to gpu is supported here, but gpu to raster is not
175 SkBitmap bm;
176 if (as_IB(image)->getROPixels(nullptr, &bm)) {
177 return MakeFromRaster(subset, bm, props);
178 }
179 return nullptr;
180 }
181
182 ///////////////////////////////////////////////////////////////////////////////
183
184 class SkSpecialImage_Raster : public SkSpecialImage_Base {
185 public:
SkSpecialImage_Raster(const SkIRect & subset,const SkBitmap & bm,const SkSurfaceProps & props)186 SkSpecialImage_Raster(const SkIRect& subset, const SkBitmap& bm, const SkSurfaceProps& props)
187 : INHERITED(subset, bm.getGenerationID(), props)
188 , fBitmap(bm)
189 {
190 SkASSERT(bm.pixelRef());
191 SkASSERT(fBitmap.getPixels());
192 }
193
alphaType() const194 SkAlphaType alphaType() const override { return fBitmap.alphaType(); }
195
colorType() const196 SkColorType colorType() const override { return fBitmap.colorType(); }
197
getSize() const198 size_t getSize() const override { return fBitmap.computeByteSize(); }
199
onDraw(SkCanvas * canvas,SkScalar x,SkScalar y,const SkSamplingOptions & sampling,const SkPaint * paint) const200 void onDraw(SkCanvas* canvas, SkScalar x, SkScalar y, const SkSamplingOptions& sampling,
201 const SkPaint* paint) const override {
202 SkRect dst = SkRect::MakeXYWH(x, y,
203 this->subset().width(), this->subset().height());
204
205 canvas->drawImageRect(fBitmap.asImage(), SkRect::Make(this->subset()), dst,
206 sampling, paint, SkCanvas::kStrict_SrcRectConstraint);
207 }
208
onGetROPixels(SkBitmap * bm) const209 bool onGetROPixels(SkBitmap* bm) const override {
210 return fBitmap.extractSubset(bm, this->subset());
211 }
212
onGetColorSpace() const213 SkColorSpace* onGetColorSpace() const override {
214 return fBitmap.colorSpace();
215 }
216
217 #if SK_SUPPORT_GPU
onView(GrRecordingContext * context) const218 GrSurfaceProxyView onView(GrRecordingContext* context) const override {
219 if (context) {
220 return std::get<0>(GrMakeCachedBitmapProxyView(context, fBitmap, GrMipmapped::kNo));
221 }
222
223 return {};
224 }
225 #endif
226
onMakeSurface(SkColorType colorType,const SkColorSpace * colorSpace,const SkISize & size,SkAlphaType at,const SkSurfaceProps & props) const227 sk_sp<SkSpecialSurface> onMakeSurface(SkColorType colorType, const SkColorSpace* colorSpace,
228 const SkISize& size, SkAlphaType at,
229 const SkSurfaceProps& props) const override {
230 // Ignore the requested color type, the raster backend currently only supports N32
231 colorType = kN32_SkColorType; // TODO: find ways to allow f16
232 SkImageInfo info = SkImageInfo::Make(size, colorType, at, sk_ref_sp(colorSpace));
233 return SkSpecialSurface::MakeRaster(info, props);
234 }
235
onMakeSubset(const SkIRect & subset) const236 sk_sp<SkSpecialImage> onMakeSubset(const SkIRect& subset) const override {
237 // No need to extract subset, onGetROPixels handles that when needed
238 return SkSpecialImage::MakeFromRaster(subset, fBitmap, this->props());
239 }
240
onAsImage(const SkIRect * subset) const241 sk_sp<SkImage> onAsImage(const SkIRect* subset) const override {
242 if (subset) {
243 SkBitmap subsetBM;
244
245 if (!fBitmap.extractSubset(&subsetBM, *subset)) {
246 return nullptr;
247 }
248
249 return subsetBM.asImage();
250 }
251
252 return fBitmap.asImage();
253 }
254
onMakeTightSurface(SkColorType colorType,const SkColorSpace * colorSpace,const SkISize & size,SkAlphaType at) const255 sk_sp<SkSurface> onMakeTightSurface(SkColorType colorType, const SkColorSpace* colorSpace,
256 const SkISize& size, SkAlphaType at) const override {
257 // Ignore the requested color type, the raster backend currently only supports N32
258 colorType = kN32_SkColorType; // TODO: find ways to allow f16
259 SkImageInfo info = SkImageInfo::Make(size, colorType, at, sk_ref_sp(colorSpace));
260 return SkSurface::MakeRaster(info);
261 }
262
263 private:
264 SkBitmap fBitmap;
265
266 using INHERITED = SkSpecialImage_Base;
267 };
268
MakeFromRaster(const SkIRect & subset,const SkBitmap & bm,const SkSurfaceProps & props)269 sk_sp<SkSpecialImage> SkSpecialImage::MakeFromRaster(const SkIRect& subset,
270 const SkBitmap& bm,
271 const SkSurfaceProps& props) {
272 SkASSERT(rect_fits(subset, bm.width(), bm.height()));
273
274 if (!bm.pixelRef()) {
275 return nullptr;
276 }
277
278 const SkBitmap* srcBM = &bm;
279 SkBitmap tmp;
280 // ImageFilters only handle N32 at the moment, so force our src to be that
281 if (!valid_for_imagefilters(bm.info())) {
282 if (!tmp.tryAllocPixels(bm.info().makeColorType(kN32_SkColorType)) ||
283 !bm.readPixels(tmp.info(), tmp.getPixels(), tmp.rowBytes(), 0, 0))
284 {
285 return nullptr;
286 }
287 srcBM = &tmp;
288 }
289 return sk_make_sp<SkSpecialImage_Raster>(subset, *srcBM, props);
290 }
291
CopyFromRaster(const SkIRect & subset,const SkBitmap & bm,const SkSurfaceProps & props)292 sk_sp<SkSpecialImage> SkSpecialImage::CopyFromRaster(const SkIRect& subset,
293 const SkBitmap& bm,
294 const SkSurfaceProps& props) {
295 SkASSERT(rect_fits(subset, bm.width(), bm.height()));
296
297 if (!bm.pixelRef()) {
298 return nullptr;
299 }
300
301 SkBitmap tmp;
302 SkImageInfo info = bm.info().makeDimensions(subset.size());
303 // As in MakeFromRaster, must force src to N32 for ImageFilters
304 if (!valid_for_imagefilters(bm.info())) {
305 info = info.makeColorType(kN32_SkColorType);
306 }
307 if (!tmp.tryAllocPixels(info)) {
308 return nullptr;
309 }
310 if (!bm.readPixels(tmp.info(), tmp.getPixels(), tmp.rowBytes(), subset.x(), subset.y())) {
311 return nullptr;
312 }
313
314 // Since we're making a copy of the raster, the resulting special image is the exact size
315 // of the requested subset of the original and no longer needs to be offset by subset's left
316 // and top, since those were relative to the original's buffer.
317 return sk_make_sp<SkSpecialImage_Raster>(
318 SkIRect::MakeWH(subset.width(), subset.height()), tmp, props);
319 }
320
321 #if SK_SUPPORT_GPU
322 ///////////////////////////////////////////////////////////////////////////////
wrap_proxy_in_image(GrRecordingContext * context,GrSurfaceProxyView view,SkColorType colorType,SkAlphaType alphaType,sk_sp<SkColorSpace> colorSpace)323 static sk_sp<SkImage> wrap_proxy_in_image(GrRecordingContext* context, GrSurfaceProxyView view,
324 SkColorType colorType, SkAlphaType alphaType,
325 sk_sp<SkColorSpace> colorSpace) {
326 return sk_make_sp<SkImage_Gpu>(sk_ref_sp(context),
327 kNeedNewImageUniqueID,
328 std::move(view),
329 SkColorInfo(colorType, alphaType, std::move(colorSpace)));
330 }
331
332 class SkSpecialImage_Gpu : public SkSpecialImage_Base {
333 public:
SkSpecialImage_Gpu(GrRecordingContext * context,const SkIRect & subset,uint32_t uniqueID,GrSurfaceProxyView view,GrColorType ct,SkAlphaType at,sk_sp<SkColorSpace> colorSpace,const SkSurfaceProps & props)334 SkSpecialImage_Gpu(GrRecordingContext* context, const SkIRect& subset,
335 uint32_t uniqueID, GrSurfaceProxyView view, GrColorType ct,
336 SkAlphaType at, sk_sp<SkColorSpace> colorSpace, const SkSurfaceProps& props)
337 : INHERITED(subset, uniqueID, props)
338 , fContext(context)
339 , fView(std::move(view))
340 , fColorType(ct)
341 , fAlphaType(at)
342 , fColorSpace(std::move(colorSpace)) {
343 }
344
alphaType() const345 SkAlphaType alphaType() const override { return fAlphaType; }
346
colorType() const347 SkColorType colorType() const override { return GrColorTypeToSkColorType(fColorType); }
348
getSize() const349 size_t getSize() const override {
350 return fView.proxy()->gpuMemorySize();
351 }
352
onDraw(SkCanvas * canvas,SkScalar x,SkScalar y,const SkSamplingOptions & sampling,const SkPaint * paint) const353 void onDraw(SkCanvas* canvas, SkScalar x, SkScalar y, const SkSamplingOptions& sampling,
354 const SkPaint* paint) const override {
355 SkRect dst = SkRect::MakeXYWH(x, y,
356 this->subset().width(), this->subset().height());
357
358 // TODO: In this instance we know we're going to draw a sub-portion of the backing
359 // texture into the canvas so it is okay to wrap it in an SkImage. This poses
360 // some problems for full deferral however in that when the deferred SkImage_Gpu
361 // instantiates itself it is going to have to either be okay with having a larger
362 // than expected backing texture (unlikely) or the 'fit' of the SurfaceProxy needs
363 // to be tightened (if it is deferred).
364 sk_sp<SkImage> img = sk_sp<SkImage>(
365 new SkImage_Gpu(sk_ref_sp(canvas->recordingContext()),
366 this->uniqueID(),
367 fView,
368 SkColorInfo(this->colorType(), fAlphaType, fColorSpace)));
369
370 canvas->drawImageRect(img, SkRect::Make(this->subset()), dst,
371 sampling, paint, SkCanvas::kStrict_SrcRectConstraint);
372 }
373
onGetContext() const374 GrRecordingContext* onGetContext() const override { return fContext; }
375
onView(GrRecordingContext * context) const376 GrSurfaceProxyView onView(GrRecordingContext* context) const override { return fView; }
377
onGetROPixels(SkBitmap * dst) const378 bool onGetROPixels(SkBitmap* dst) const override {
379 // This should never be called: All GPU image filters are implemented entirely on the GPU,
380 // so we never perform read-back.
381 SkASSERT(false);
382 return false;
383 }
384
onGetColorSpace() const385 SkColorSpace* onGetColorSpace() const override {
386 return fColorSpace.get();
387 }
388
onMakeSurface(SkColorType colorType,const SkColorSpace * colorSpace,const SkISize & size,SkAlphaType at,const SkSurfaceProps & props) const389 sk_sp<SkSpecialSurface> onMakeSurface(SkColorType colorType, const SkColorSpace* colorSpace,
390 const SkISize& size, SkAlphaType at,
391 const SkSurfaceProps& props) const override {
392 if (!fContext) {
393 return nullptr;
394 }
395
396 SkImageInfo ii = SkImageInfo::Make(size, colorType, at, sk_ref_sp(colorSpace));
397
398 return SkSpecialSurface::MakeRenderTarget(fContext, ii, props);
399 }
400
onMakeSubset(const SkIRect & subset) const401 sk_sp<SkSpecialImage> onMakeSubset(const SkIRect& subset) const override {
402 return SkSpecialImage::MakeDeferredFromGpu(fContext,
403 subset,
404 this->uniqueID(),
405 fView,
406 fColorType,
407 fColorSpace,
408 this->props(),
409 fAlphaType);
410 }
411
onAsImage(const SkIRect * subset) const412 sk_sp<SkImage> onAsImage(const SkIRect* subset) const override {
413 GrSurfaceProxy* proxy = fView.proxy();
414 if (subset) {
415 if (proxy->isFunctionallyExact() && *subset == SkIRect::MakeSize(proxy->dimensions())) {
416 proxy->priv().exactify(false);
417 // The existing GrTexture is already tight so reuse it in the SkImage
418 return wrap_proxy_in_image(fContext, fView, this->colorType(), fAlphaType,
419 fColorSpace);
420 }
421
422 auto subsetView = GrSurfaceProxyView::Copy(fContext, fView, GrMipmapped::kNo, *subset,
423 SkBackingFit::kExact, SkBudgeted::kYes);
424 if (!subsetView) {
425 return nullptr;
426 }
427 SkASSERT(subsetView.asTextureProxy());
428 SkASSERT(subsetView.proxy()->priv().isExact());
429
430 // MDB: this is acceptable (wrapping subsetProxy in an SkImage) bc Copy will
431 // return a kExact-backed proxy
432 return wrap_proxy_in_image(fContext, std::move(subsetView), this->colorType(),
433 fAlphaType, fColorSpace);
434 }
435
436 proxy->priv().exactify(true);
437
438 return wrap_proxy_in_image(fContext, fView, this->colorType(), fAlphaType, fColorSpace);
439 }
440
onMakeTightSurface(SkColorType colorType,const SkColorSpace * colorSpace,const SkISize & size,SkAlphaType at) const441 sk_sp<SkSurface> onMakeTightSurface(SkColorType colorType, const SkColorSpace* colorSpace,
442 const SkISize& size, SkAlphaType at) const override {
443 // TODO (michaelludwig): Why does this ignore colorType but onMakeSurface doesn't ignore it?
444 // Once makeTightSurface() goes away, should this type overriding behavior be moved into
445 // onMakeSurface() or is this unnecessary?
446 colorType = colorSpace && colorSpace->gammaIsLinear()
447 ? kRGBA_F16_SkColorType : kRGBA_8888_SkColorType;
448 SkImageInfo info = SkImageInfo::Make(size, colorType, at, sk_ref_sp(colorSpace));
449 return SkSurface::MakeRenderTarget(fContext, SkBudgeted::kYes, info);
450 }
451
452 private:
453 GrRecordingContext* fContext;
454 GrSurfaceProxyView fView;
455 const GrColorType fColorType;
456 const SkAlphaType fAlphaType;
457 sk_sp<SkColorSpace> fColorSpace;
458
459 using INHERITED = SkSpecialImage_Base;
460 };
461
MakeDeferredFromGpu(GrRecordingContext * context,const SkIRect & subset,uint32_t uniqueID,GrSurfaceProxyView view,GrColorType colorType,sk_sp<SkColorSpace> colorSpace,const SkSurfaceProps & props,SkAlphaType at)462 sk_sp<SkSpecialImage> SkSpecialImage::MakeDeferredFromGpu(GrRecordingContext* context,
463 const SkIRect& subset,
464 uint32_t uniqueID,
465 GrSurfaceProxyView view,
466 GrColorType colorType,
467 sk_sp<SkColorSpace> colorSpace,
468 const SkSurfaceProps& props,
469 SkAlphaType at) {
470 if (!context || context->abandoned() || !view.asTextureProxy()) {
471 return nullptr;
472 }
473 SkASSERT_RELEASE(rect_fits(subset, view.proxy()->width(), view.proxy()->height()));
474 return sk_make_sp<SkSpecialImage_Gpu>(context, subset, uniqueID, std::move(view), colorType,
475 at, std::move(colorSpace), props);
476 }
477 #endif
478