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 "SkSpecialImage.h"
9 #include "SkBitmap.h"
10 #include "SkImage.h"
11 #include "SkBitmapCache.h"
12 #include "SkCanvas.h"
13 #include "SkImage_Base.h"
14 #include "SkSpecialSurface.h"
15 #include "SkSurfacePriv.h"
16 #include "SkPixelRef.h"
17
18 #if SK_SUPPORT_GPU
19 #include "GrContext.h"
20 #include "GrResourceProvider.h"
21 #include "GrSurfaceContext.h"
22 #include "GrSurfaceProxyPriv.h"
23 #include "GrTexture.h"
24 #include "GrSamplerParams.h"
25 #include "GrTextureProxy.h"
26 #include "SkGr.h"
27 #include "SkImage_Gpu.h"
28 #endif
29
30 // Currently the raster imagefilters can only handle certain imageinfos. Call this to know if
31 // a given info is supported.
valid_for_imagefilters(const SkImageInfo & info)32 static bool valid_for_imagefilters(const SkImageInfo& info) {
33 // no support for other swizzles/depths yet
34 return info.colorType() == kN32_SkColorType;
35 }
36
37 ///////////////////////////////////////////////////////////////////////////////
38 class SkSpecialImage_Base : public SkSpecialImage {
39 public:
SkSpecialImage_Base(const SkIRect & subset,uint32_t uniqueID,const SkSurfaceProps * props)40 SkSpecialImage_Base(const SkIRect& subset, uint32_t uniqueID, const SkSurfaceProps* props)
41 : INHERITED(subset, uniqueID, props) {
42 }
~SkSpecialImage_Base()43 ~SkSpecialImage_Base() override { }
44
45 virtual void onDraw(SkCanvas*, SkScalar x, SkScalar y, const SkPaint*) const = 0;
46
47 virtual bool onGetROPixels(SkBitmap*) const = 0;
48
onGetContext() const49 virtual GrContext* onGetContext() const { return nullptr; }
50
51 virtual SkColorSpace* onGetColorSpace() const = 0;
52
53 #if SK_SUPPORT_GPU
54 virtual sk_sp<GrTextureProxy> onAsTextureProxyRef(GrContext* context) const = 0;
55 #endif
56
57 virtual sk_sp<SkSpecialImage> onMakeSubset(const SkIRect& subset) const = 0;
58
59 virtual sk_sp<SkSpecialSurface> onMakeSurface(const SkImageFilter::OutputProperties& outProps,
60 const SkISize& size, SkAlphaType at) const = 0;
61
62 virtual sk_sp<SkImage> onAsImage(const SkIRect* subset) const = 0;
63
64 virtual sk_sp<SkSurface> onMakeTightSurface(const SkImageFilter::OutputProperties& outProps,
65 const SkISize& size, SkAlphaType at) const = 0;
66
67 private:
68 typedef SkSpecialImage INHERITED;
69 };
70
71 ///////////////////////////////////////////////////////////////////////////////
as_SIB(const SkSpecialImage * image)72 static inline const SkSpecialImage_Base* as_SIB(const SkSpecialImage* image) {
73 return static_cast<const SkSpecialImage_Base*>(image);
74 }
75
SkSpecialImage(const SkIRect & subset,uint32_t uniqueID,const SkSurfaceProps * props)76 SkSpecialImage::SkSpecialImage(const SkIRect& subset,
77 uint32_t uniqueID,
78 const SkSurfaceProps* props)
79 : fProps(SkSurfacePropsCopyOrDefault(props))
80 , fSubset(subset)
81 , fUniqueID(kNeedNewImageUniqueID_SpecialImage == uniqueID ? SkNextID::ImageID() : uniqueID) {
82 }
83
makeTextureImage(GrContext * context)84 sk_sp<SkSpecialImage> SkSpecialImage::makeTextureImage(GrContext* context) {
85 #if SK_SUPPORT_GPU
86 if (!context) {
87 return nullptr;
88 }
89 if (GrContext* curContext = as_SIB(this)->onGetContext()) {
90 return curContext == context ? sk_sp<SkSpecialImage>(SkRef(this)) : nullptr;
91 }
92
93 SkBitmap bmp;
94 // At this point, we are definitely not texture-backed, so we must be raster or generator
95 // backed. If we remove the special-wrapping-an-image subclass, we may be able to assert that
96 // we are strictly raster-backed (i.e. generator images become raster when they are specialized)
97 // in which case getROPixels could turn into peekPixels...
98 if (!this->getROPixels(&bmp)) {
99 return nullptr;
100 }
101
102 if (bmp.empty()) {
103 return SkSpecialImage::MakeFromRaster(SkIRect::MakeEmpty(), bmp, &this->props());
104 }
105
106 // TODO: this is a tight copy of 'bmp' but it doesn't have to be (given SkSpecialImage's
107 // semantics). Since this is cached though we would have to bake the fit into the cache key.
108 sk_sp<GrTextureProxy> proxy = GrMakeCachedBitmapProxy(context->resourceProvider(), bmp);
109 if (!proxy) {
110 return nullptr;
111 }
112
113 const SkIRect rect = SkIRect::MakeWH(proxy->width(), proxy->height());
114
115 // GrMakeCachedBitmapProxy has uploaded only the specified subset of 'bmp' so we need not
116 // bother with SkBitmap::getSubset
117 return SkSpecialImage::MakeDeferredFromGpu(context,
118 rect,
119 this->uniqueID(),
120 std::move(proxy),
121 sk_ref_sp(this->getColorSpace()),
122 &this->props(),
123 this->alphaType());
124 #else
125 return nullptr;
126 #endif
127 }
128
draw(SkCanvas * canvas,SkScalar x,SkScalar y,const SkPaint * paint) const129 void SkSpecialImage::draw(SkCanvas* canvas, SkScalar x, SkScalar y, const SkPaint* paint) const {
130 return as_SIB(this)->onDraw(canvas, x, y, paint);
131 }
132
getROPixels(SkBitmap * bm) const133 bool SkSpecialImage::getROPixels(SkBitmap* bm) const {
134 return as_SIB(this)->onGetROPixels(bm);
135 }
136
isTextureBacked() const137 bool SkSpecialImage::isTextureBacked() const {
138 return SkToBool(as_SIB(this)->onGetContext());
139 }
140
getContext() const141 GrContext* SkSpecialImage::getContext() const {
142 return as_SIB(this)->onGetContext();
143 }
144
getColorSpace() const145 SkColorSpace* SkSpecialImage::getColorSpace() const {
146 return as_SIB(this)->onGetColorSpace();
147 }
148
149 #if SK_SUPPORT_GPU
asTextureProxyRef(GrContext * context) const150 sk_sp<GrTextureProxy> SkSpecialImage::asTextureProxyRef(GrContext* context) const {
151 return as_SIB(this)->onAsTextureProxyRef(context);
152 }
153 #endif
154
makeSurface(const SkImageFilter::OutputProperties & outProps,const SkISize & size,SkAlphaType at) const155 sk_sp<SkSpecialSurface> SkSpecialImage::makeSurface(const SkImageFilter::OutputProperties& outProps,
156 const SkISize& size, SkAlphaType at) const {
157 return as_SIB(this)->onMakeSurface(outProps, size, at);
158 }
159
makeTightSurface(const SkImageFilter::OutputProperties & outProps,const SkISize & size,SkAlphaType at) const160 sk_sp<SkSurface> SkSpecialImage::makeTightSurface(const SkImageFilter::OutputProperties& outProps,
161 const SkISize& size, SkAlphaType at) const {
162 return as_SIB(this)->onMakeTightSurface(outProps, size, at);
163 }
164
makeSubset(const SkIRect & subset) const165 sk_sp<SkSpecialImage> SkSpecialImage::makeSubset(const SkIRect& subset) const {
166 return as_SIB(this)->onMakeSubset(subset);
167 }
168
asImage(const SkIRect * subset) const169 sk_sp<SkImage> SkSpecialImage::asImage(const SkIRect* subset) const {
170 return as_SIB(this)->onAsImage(subset);
171 }
172
173
174 #ifdef SK_DEBUG
rect_fits(const SkIRect & rect,int width,int height)175 static bool rect_fits(const SkIRect& rect, int width, int height) {
176 if (0 == width && 0 == height) {
177 SkASSERT(0 == rect.fLeft && 0 == rect.fRight && 0 == rect.fTop && 0 == rect.fBottom);
178 return true;
179 }
180
181 return rect.fLeft >= 0 && rect.fLeft < width && rect.fLeft < rect.fRight &&
182 rect.fRight >= 0 && rect.fRight <= width &&
183 rect.fTop >= 0 && rect.fTop < height && rect.fTop < rect.fBottom &&
184 rect.fBottom >= 0 && rect.fBottom <= height;
185 }
186 #endif
187
MakeFromImage(const SkIRect & subset,sk_sp<SkImage> image,SkColorSpace * dstColorSpace,const SkSurfaceProps * props)188 sk_sp<SkSpecialImage> SkSpecialImage::MakeFromImage(const SkIRect& subset,
189 sk_sp<SkImage> image,
190 SkColorSpace* dstColorSpace,
191 const SkSurfaceProps* props) {
192 SkASSERT(rect_fits(subset, image->width(), image->height()));
193
194 #if SK_SUPPORT_GPU
195 if (sk_sp<GrTextureProxy> proxy = as_IB(image)->asTextureProxyRef()) {
196 GrContext* context = ((SkImage_Gpu*) as_IB(image))->context();
197
198 return MakeDeferredFromGpu(context, subset, image->uniqueID(), std::move(proxy),
199 as_IB(image)->onImageInfo().refColorSpace(), props);
200 } else
201 #endif
202 {
203 SkBitmap bm;
204 if (as_IB(image)->getROPixels(&bm, dstColorSpace)) {
205 return MakeFromRaster(subset, bm, props);
206 }
207 }
208 return nullptr;
209 }
210
211 ///////////////////////////////////////////////////////////////////////////////
212
213 class SkSpecialImage_Raster : public SkSpecialImage_Base {
214 public:
SkSpecialImage_Raster(const SkIRect & subset,const SkBitmap & bm,const SkSurfaceProps * props)215 SkSpecialImage_Raster(const SkIRect& subset, const SkBitmap& bm, const SkSurfaceProps* props)
216 : INHERITED(subset, bm.getGenerationID(), props)
217 , fBitmap(bm)
218 {
219 SkASSERT(bm.pixelRef());
220
221 // We have to lock now, while bm is still in scope, since it may have come from our
222 // cache, which means we need to keep it locked until we (the special) are done, since
223 // we cannot re-generate the cache entry (if bm came from a generator).
224 fBitmap.lockPixels();
225 SkASSERT(fBitmap.getPixels());
226 }
227
alphaType() const228 SkAlphaType alphaType() const override { return fBitmap.alphaType(); }
229
getSize() const230 size_t getSize() const override { return fBitmap.getSize(); }
231
onDraw(SkCanvas * canvas,SkScalar x,SkScalar y,const SkPaint * paint) const232 void onDraw(SkCanvas* canvas, SkScalar x, SkScalar y, const SkPaint* paint) const override {
233 SkRect dst = SkRect::MakeXYWH(x, y,
234 this->subset().width(), this->subset().height());
235
236 canvas->drawBitmapRect(fBitmap, this->subset(),
237 dst, paint, SkCanvas::kStrict_SrcRectConstraint);
238 }
239
onGetROPixels(SkBitmap * bm) const240 bool onGetROPixels(SkBitmap* bm) const override {
241 *bm = fBitmap;
242 return true;
243 }
244
onGetColorSpace() const245 SkColorSpace* onGetColorSpace() const override {
246 return fBitmap.colorSpace();
247 }
248
249 #if SK_SUPPORT_GPU
onAsTextureProxyRef(GrContext * context) const250 sk_sp<GrTextureProxy> onAsTextureProxyRef(GrContext* context) const override {
251 if (context) {
252 return GrMakeCachedBitmapProxy(context->resourceProvider(), fBitmap);
253 }
254
255 return nullptr;
256 }
257 #endif
258
259 // TODO: The raster implementations of image filters all currently assume that the pixels are
260 // legacy N32. Until they actually check the format and operate on sRGB or F16 data appropriately,
261 // we can't enable this. (They will continue to produce incorrect results, but less-so).
262 #define RASTER_IMAGE_FILTERS_SUPPORT_SRGB_AND_F16 0
263
onMakeSurface(const SkImageFilter::OutputProperties & outProps,const SkISize & size,SkAlphaType at) const264 sk_sp<SkSpecialSurface> onMakeSurface(const SkImageFilter::OutputProperties& outProps,
265 const SkISize& size, SkAlphaType at) const override {
266 #if RASTER_IMAGE_FILTERS_SUPPORT_SRGB_AND_F16
267 SkColorSpace* colorSpace = outProps.colorSpace();
268 #else
269 SkColorSpace* colorSpace = nullptr;
270 #endif
271 SkColorType colorType = colorSpace && colorSpace->gammaIsLinear()
272 ? kRGBA_F16_SkColorType : kN32_SkColorType;
273 SkImageInfo info = SkImageInfo::Make(size.width(), size.height(), colorType, at,
274 sk_ref_sp(colorSpace));
275 return SkSpecialSurface::MakeRaster(info, nullptr);
276 }
277
onMakeSubset(const SkIRect & subset) const278 sk_sp<SkSpecialImage> onMakeSubset(const SkIRect& subset) const override {
279 SkBitmap subsetBM;
280
281 if (!fBitmap.extractSubset(&subsetBM, subset)) {
282 return nullptr;
283 }
284
285 return SkSpecialImage::MakeFromRaster(SkIRect::MakeWH(subset.width(), subset.height()),
286 subsetBM,
287 &this->props());
288 }
289
onAsImage(const SkIRect * subset) const290 sk_sp<SkImage> onAsImage(const SkIRect* subset) const override {
291 if (subset) {
292 SkBitmap subsetBM;
293
294 if (!fBitmap.extractSubset(&subsetBM, *subset)) {
295 return nullptr;
296 }
297
298 return SkImage::MakeFromBitmap(subsetBM);
299 }
300
301 return SkImage::MakeFromBitmap(fBitmap);
302 }
303
onMakeTightSurface(const SkImageFilter::OutputProperties & outProps,const SkISize & size,SkAlphaType at) const304 sk_sp<SkSurface> onMakeTightSurface(const SkImageFilter::OutputProperties& outProps,
305 const SkISize& size, SkAlphaType at) const override {
306 #if RASTER_IMAGE_FILTERS_SUPPORT_SRGB_AND_F16
307 SkColorSpace* colorSpace = outProps.colorSpace();
308 #else
309 SkColorSpace* colorSpace = nullptr;
310 #endif
311 SkColorType colorType = colorSpace && colorSpace->gammaIsLinear()
312 ? kRGBA_F16_SkColorType : kN32_SkColorType;
313 SkImageInfo info = SkImageInfo::Make(size.width(), size.height(), colorType, at,
314 sk_ref_sp(colorSpace));
315 return SkSurface::MakeRaster(info);
316 }
317
318 private:
319 SkBitmap fBitmap;
320
321 typedef SkSpecialImage_Base INHERITED;
322 };
323
MakeFromRaster(const SkIRect & subset,const SkBitmap & bm,const SkSurfaceProps * props)324 sk_sp<SkSpecialImage> SkSpecialImage::MakeFromRaster(const SkIRect& subset,
325 const SkBitmap& bm,
326 const SkSurfaceProps* props) {
327 SkASSERT(rect_fits(subset, bm.width(), bm.height()));
328
329 if (!bm.pixelRef()) {
330 return nullptr;
331 }
332
333 const SkBitmap* srcBM = &bm;
334 SkBitmap tmpStorage;
335 // ImageFilters only handle N32 at the moment, so force our src to be that
336 if (!valid_for_imagefilters(bm.info())) {
337 if (!bm.copyTo(&tmpStorage, kN32_SkColorType)) {
338 return nullptr;
339 }
340 srcBM = &tmpStorage;
341 }
342 return sk_make_sp<SkSpecialImage_Raster>(subset, *srcBM, props);
343 }
344
345 #if SK_SUPPORT_GPU
346 ///////////////////////////////////////////////////////////////////////////////
347 #include "GrTexture.h"
348
wrap_proxy_in_image(GrContext * context,sk_sp<GrTextureProxy> proxy,SkAlphaType alphaType,sk_sp<SkColorSpace> colorSpace)349 static sk_sp<SkImage> wrap_proxy_in_image(GrContext* context, sk_sp<GrTextureProxy> proxy,
350 SkAlphaType alphaType, sk_sp<SkColorSpace> colorSpace) {
351 return sk_make_sp<SkImage_Gpu>(context, kNeedNewImageUniqueID, alphaType,
352 std::move(proxy), std::move(colorSpace), SkBudgeted::kYes);
353 }
354
355 class SkSpecialImage_Gpu : public SkSpecialImage_Base {
356 public:
SkSpecialImage_Gpu(GrContext * context,const SkIRect & subset,uint32_t uniqueID,sk_sp<GrTextureProxy> proxy,SkAlphaType at,sk_sp<SkColorSpace> colorSpace,const SkSurfaceProps * props)357 SkSpecialImage_Gpu(GrContext* context, const SkIRect& subset,
358 uint32_t uniqueID, sk_sp<GrTextureProxy> proxy, SkAlphaType at,
359 sk_sp<SkColorSpace> colorSpace, const SkSurfaceProps* props)
360 : INHERITED(subset, uniqueID, props)
361 , fContext(context)
362 , fTextureProxy(std::move(proxy))
363 , fAlphaType(at)
364 , fColorSpace(std::move(colorSpace))
365 , fAddedRasterVersionToCache(false) {
366 }
367
~SkSpecialImage_Gpu()368 ~SkSpecialImage_Gpu() override {
369 if (fAddedRasterVersionToCache.load()) {
370 SkNotifyBitmapGenIDIsStale(this->uniqueID());
371 }
372 }
373
alphaType() const374 SkAlphaType alphaType() const override { return fAlphaType; }
375
getSize() const376 size_t getSize() const override { return fTextureProxy->gpuMemorySize(); }
377
onDraw(SkCanvas * canvas,SkScalar x,SkScalar y,const SkPaint * paint) const378 void onDraw(SkCanvas* canvas, SkScalar x, SkScalar y, const SkPaint* paint) const override {
379 SkRect dst = SkRect::MakeXYWH(x, y,
380 this->subset().width(), this->subset().height());
381
382 // TODO: In this instance we know we're going to draw a sub-portion of the backing
383 // texture into the canvas so it is okay to wrap it in an SkImage. This poses
384 // some problems for full deferral however in that when the deferred SkImage_Gpu
385 // instantiates itself it is going to have to either be okay with having a larger
386 // than expected backing texture (unlikely) or the 'fit' of the SurfaceProxy needs
387 // to be tightened (if it is deferred).
388 sk_sp<SkImage> img = sk_sp<SkImage>(new SkImage_Gpu(canvas->getGrContext(),
389 this->uniqueID(), fAlphaType,
390 fTextureProxy,
391 fColorSpace, SkBudgeted::kNo));
392
393 canvas->drawImageRect(img, this->subset(),
394 dst, paint, SkCanvas::kStrict_SrcRectConstraint);
395 }
396
onGetContext() const397 GrContext* onGetContext() const override { return fContext; }
398
onAsTextureProxyRef(GrContext *) const399 sk_sp<GrTextureProxy> onAsTextureProxyRef(GrContext*) const override {
400 return fTextureProxy;
401 }
402
onGetROPixels(SkBitmap * dst) const403 bool onGetROPixels(SkBitmap* dst) const override {
404 const auto desc = SkBitmapCacheDesc::Make(this->uniqueID(), this->width(), this->height());
405 if (SkBitmapCache::Find(desc, dst)) {
406 SkASSERT(dst->getGenerationID() == this->uniqueID());
407 SkASSERT(dst->isImmutable());
408 SkASSERT(dst->getPixels());
409 return true;
410 }
411
412 SkImageInfo info = SkImageInfo::MakeN32(this->width(), this->height(),
413 this->alphaType(), fColorSpace);
414
415 if (!dst->tryAllocPixels(info)) {
416 return false;
417 }
418
419 // Reading back to an SkBitmap ends deferral
420 GrTexture* texture = fTextureProxy->instantiate(fContext->resourceProvider());
421 if (!texture) {
422 return false;
423 }
424
425 if (!texture->readPixels(0, 0, dst->width(), dst->height(), kSkia8888_GrPixelConfig,
426 dst->getPixels(), dst->rowBytes())) {
427 return false;
428 }
429
430 dst->pixelRef()->setImmutableWithID(this->uniqueID());
431 SkBitmapCache::Add(desc, *dst);
432 fAddedRasterVersionToCache.store(true);
433 return true;
434 }
435
onGetColorSpace() const436 SkColorSpace* onGetColorSpace() const override {
437 return fColorSpace.get();
438 }
439
onMakeSurface(const SkImageFilter::OutputProperties & outProps,const SkISize & size,SkAlphaType at) const440 sk_sp<SkSpecialSurface> onMakeSurface(const SkImageFilter::OutputProperties& outProps,
441 const SkISize& size, SkAlphaType at) const override {
442 if (!fContext) {
443 return nullptr;
444 }
445
446 SkColorSpace* colorSpace = outProps.colorSpace();
447 return SkSpecialSurface::MakeRenderTarget(
448 fContext, size.width(), size.height(),
449 GrRenderableConfigForColorSpace(colorSpace), sk_ref_sp(colorSpace));
450 }
451
onMakeSubset(const SkIRect & subset) const452 sk_sp<SkSpecialImage> onMakeSubset(const SkIRect& subset) const override {
453 return SkSpecialImage::MakeDeferredFromGpu(fContext,
454 subset,
455 this->uniqueID(),
456 fTextureProxy,
457 fColorSpace,
458 &this->props(),
459 fAlphaType);
460 }
461
462 // TODO: move all the logic here into the subset-flavor GrSurfaceProxy::copy?
onAsImage(const SkIRect * subset) const463 sk_sp<SkImage> onAsImage(const SkIRect* subset) const override {
464 if (subset) {
465 // TODO: if this becomes a bottle neck we could base this logic on what the size
466 // will be when it is finally instantiated - but that is more fraught.
467 if (GrResourceProvider::IsFunctionallyExact(fTextureProxy.get()) &&
468 0 == subset->fLeft && 0 == subset->fTop &&
469 fTextureProxy->width() == subset->width() &&
470 fTextureProxy->height() == subset->height()) {
471 // The existing GrTexture is already tight so reuse it in the SkImage
472 return wrap_proxy_in_image(fContext, fTextureProxy, fAlphaType, fColorSpace);
473 }
474
475 sk_sp<GrTextureProxy> subsetProxy(GrSurfaceProxy::Copy(fContext, fTextureProxy.get(),
476 *subset, SkBudgeted::kYes));
477 if (!subsetProxy) {
478 return nullptr;
479 }
480
481 SkASSERT(subsetProxy->priv().isExact());
482 // MDB: this is acceptable (wrapping subsetProxy in an SkImage) bc Copy will
483 // return a kExact-backed proxy
484 return wrap_proxy_in_image(fContext, std::move(subsetProxy), fAlphaType, fColorSpace);
485 }
486
487 fTextureProxy->priv().exactify();
488
489 return wrap_proxy_in_image(fContext, fTextureProxy, fAlphaType, fColorSpace);
490 }
491
onMakeTightSurface(const SkImageFilter::OutputProperties & outProps,const SkISize & size,SkAlphaType at) const492 sk_sp<SkSurface> onMakeTightSurface(const SkImageFilter::OutputProperties& outProps,
493 const SkISize& size, SkAlphaType at) const override {
494 SkColorSpace* colorSpace = outProps.colorSpace();
495 SkColorType colorType = colorSpace && colorSpace->gammaIsLinear()
496 ? kRGBA_F16_SkColorType : kRGBA_8888_SkColorType;
497 SkImageInfo info = SkImageInfo::Make(size.width(), size.height(), colorType, at,
498 sk_ref_sp(colorSpace));
499 return SkSurface::MakeRenderTarget(fContext, SkBudgeted::kYes, info);
500 }
501
502 private:
503 GrContext* fContext;
504 sk_sp<GrTextureProxy> fTextureProxy;
505 const SkAlphaType fAlphaType;
506 sk_sp<SkColorSpace> fColorSpace;
507 mutable SkAtomic<bool> fAddedRasterVersionToCache;
508
509 typedef SkSpecialImage_Base INHERITED;
510 };
511
MakeDeferredFromGpu(GrContext * context,const SkIRect & subset,uint32_t uniqueID,sk_sp<GrTextureProxy> proxy,sk_sp<SkColorSpace> colorSpace,const SkSurfaceProps * props,SkAlphaType at)512 sk_sp<SkSpecialImage> SkSpecialImage::MakeDeferredFromGpu(GrContext* context,
513 const SkIRect& subset,
514 uint32_t uniqueID,
515 sk_sp<GrTextureProxy> proxy,
516 sk_sp<SkColorSpace> colorSpace,
517 const SkSurfaceProps* props,
518 SkAlphaType at) {
519 SkASSERT(rect_fits(subset, proxy->width(), proxy->height()));
520 return sk_make_sp<SkSpecialImage_Gpu>(context, subset, uniqueID, std::move(proxy), at,
521 std::move(colorSpace), props);
522 }
523 #endif
524