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 "GrContextPriv.h"
21 #include "GrProxyProvider.h"
22 #include "GrSurfaceContext.h"
23 #include "GrTextureProxy.h"
24 #include "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 SkPaint*) const = 0;
43
44 virtual bool onGetROPixels(SkBitmap*) const = 0;
45
onGetContext() const46 virtual GrContext* onGetContext() const { return nullptr; }
47
48 virtual SkColorSpace* onGetColorSpace() const = 0;
49
50 #if SK_SUPPORT_GPU
51 virtual sk_sp<GrTextureProxy> onAsTextureProxyRef(GrContext* context) const = 0;
52 #endif
53
54 virtual sk_sp<SkSpecialImage> onMakeSubset(const SkIRect& subset) const = 0;
55
56 virtual sk_sp<SkSpecialSurface> onMakeSurface(const SkImageFilter::OutputProperties& outProps,
57 const SkISize& size, SkAlphaType at) const = 0;
58
59 virtual sk_sp<SkImage> onAsImage(const SkIRect* subset) const = 0;
60
61 virtual sk_sp<SkSurface> onMakeTightSurface(const SkImageFilter::OutputProperties& outProps,
62 const SkISize& size, SkAlphaType at) const = 0;
63
64 private:
65 typedef SkSpecialImage INHERITED;
66 };
67
68 ///////////////////////////////////////////////////////////////////////////////
as_SIB(const SkSpecialImage * image)69 static inline const SkSpecialImage_Base* as_SIB(const SkSpecialImage* image) {
70 return static_cast<const SkSpecialImage_Base*>(image);
71 }
72
SkSpecialImage(const SkIRect & subset,uint32_t uniqueID,const SkSurfaceProps * props)73 SkSpecialImage::SkSpecialImage(const SkIRect& subset,
74 uint32_t uniqueID,
75 const SkSurfaceProps* props)
76 : fProps(SkSurfacePropsCopyOrDefault(props))
77 , fSubset(subset)
78 , fUniqueID(kNeedNewImageUniqueID_SpecialImage == uniqueID ? SkNextID::ImageID() : uniqueID) {
79 }
80
makeTextureImage(GrContext * context)81 sk_sp<SkSpecialImage> SkSpecialImage::makeTextureImage(GrContext* context) {
82 #if SK_SUPPORT_GPU
83 if (!context) {
84 return nullptr;
85 }
86 if (GrContext* curContext = as_SIB(this)->onGetContext()) {
87 return curContext == context ? sk_sp<SkSpecialImage>(SkRef(this)) : nullptr;
88 }
89
90 auto proxyProvider = context->contextPriv().proxyProvider();
91 SkBitmap bmp;
92 // At this point, we are definitely not texture-backed, so we must be raster or generator
93 // backed. If we remove the special-wrapping-an-image subclass, we may be able to assert that
94 // we are strictly raster-backed (i.e. generator images become raster when they are specialized)
95 // in which case getROPixels could turn into peekPixels...
96 if (!this->getROPixels(&bmp)) {
97 return nullptr;
98 }
99
100 if (bmp.empty()) {
101 return SkSpecialImage::MakeFromRaster(SkIRect::MakeEmpty(), bmp, &this->props());
102 }
103
104 // TODO: this is a tight copy of 'bmp' but it doesn't have to be (given SkSpecialImage's
105 // semantics). Since this is cached though we would have to bake the fit into the cache key.
106 sk_sp<GrTextureProxy> proxy = GrMakeCachedBitmapProxy(proxyProvider, bmp);
107 if (!proxy) {
108 return nullptr;
109 }
110
111 const SkIRect rect = SkIRect::MakeWH(proxy->width(), proxy->height());
112
113 // GrMakeCachedBitmapProxy has uploaded only the specified subset of 'bmp' so we need not
114 // bother with SkBitmap::getSubset
115 return SkSpecialImage::MakeDeferredFromGpu(context,
116 rect,
117 this->uniqueID(),
118 std::move(proxy),
119 sk_ref_sp(this->getColorSpace()),
120 &this->props(),
121 this->alphaType());
122 #else
123 return nullptr;
124 #endif
125 }
126
draw(SkCanvas * canvas,SkScalar x,SkScalar y,const SkPaint * paint) const127 void SkSpecialImage::draw(SkCanvas* canvas, SkScalar x, SkScalar y, const SkPaint* paint) const {
128 return as_SIB(this)->onDraw(canvas, x, y, paint);
129 }
130
getROPixels(SkBitmap * bm) const131 bool SkSpecialImage::getROPixels(SkBitmap* bm) const {
132 return as_SIB(this)->onGetROPixels(bm);
133 }
134
isTextureBacked() const135 bool SkSpecialImage::isTextureBacked() const {
136 return SkToBool(as_SIB(this)->onGetContext());
137 }
138
getContext() const139 GrContext* SkSpecialImage::getContext() const {
140 return as_SIB(this)->onGetContext();
141 }
142
getColorSpace() const143 SkColorSpace* SkSpecialImage::getColorSpace() const {
144 return as_SIB(this)->onGetColorSpace();
145 }
146
147 #if SK_SUPPORT_GPU
asTextureProxyRef(GrContext * context) const148 sk_sp<GrTextureProxy> SkSpecialImage::asTextureProxyRef(GrContext* context) const {
149 return as_SIB(this)->onAsTextureProxyRef(context);
150 }
151 #endif
152
makeSurface(const SkImageFilter::OutputProperties & outProps,const SkISize & size,SkAlphaType at) const153 sk_sp<SkSpecialSurface> SkSpecialImage::makeSurface(const SkImageFilter::OutputProperties& outProps,
154 const SkISize& size, SkAlphaType at) const {
155 return as_SIB(this)->onMakeSurface(outProps, size, at);
156 }
157
makeTightSurface(const SkImageFilter::OutputProperties & outProps,const SkISize & size,SkAlphaType at) const158 sk_sp<SkSurface> SkSpecialImage::makeTightSurface(const SkImageFilter::OutputProperties& outProps,
159 const SkISize& size, SkAlphaType at) const {
160 return as_SIB(this)->onMakeTightSurface(outProps, size, at);
161 }
162
makeSubset(const SkIRect & subset) const163 sk_sp<SkSpecialImage> SkSpecialImage::makeSubset(const SkIRect& subset) const {
164 return as_SIB(this)->onMakeSubset(subset);
165 }
166
asImage(const SkIRect * subset) const167 sk_sp<SkImage> SkSpecialImage::asImage(const SkIRect* subset) const {
168 return as_SIB(this)->onAsImage(subset);
169 }
170
171
172 #ifdef SK_DEBUG
rect_fits(const SkIRect & rect,int width,int height)173 static bool rect_fits(const SkIRect& rect, int width, int height) {
174 if (0 == width && 0 == height) {
175 SkASSERT(0 == rect.fLeft && 0 == rect.fRight && 0 == rect.fTop && 0 == rect.fBottom);
176 return true;
177 }
178
179 return rect.fLeft >= 0 && rect.fLeft < width && rect.fLeft < rect.fRight &&
180 rect.fRight >= 0 && rect.fRight <= width &&
181 rect.fTop >= 0 && rect.fTop < height && rect.fTop < rect.fBottom &&
182 rect.fBottom >= 0 && rect.fBottom <= height;
183 }
184 #endif
185
MakeFromImage(const SkIRect & subset,sk_sp<SkImage> image,SkColorSpace * dstColorSpace,const SkSurfaceProps * props)186 sk_sp<SkSpecialImage> SkSpecialImage::MakeFromImage(const SkIRect& subset,
187 sk_sp<SkImage> image,
188 SkColorSpace* dstColorSpace,
189 const SkSurfaceProps* props) {
190 SkASSERT(rect_fits(subset, image->width(), image->height()));
191
192 #if SK_SUPPORT_GPU
193 if (sk_sp<GrTextureProxy> proxy = as_IB(image)->asTextureProxyRef()) {
194 GrContext* context = ((SkImage_Gpu*) as_IB(image))->context();
195
196 return MakeDeferredFromGpu(context, subset, image->uniqueID(), std::move(proxy),
197 as_IB(image)->onImageInfo().refColorSpace(), props);
198 } else
199 #endif
200 {
201 SkBitmap bm;
202 if (as_IB(image)->getROPixels(&bm, dstColorSpace)) {
203 return MakeFromRaster(subset, bm, props);
204 }
205 }
206 return nullptr;
207 }
208
209 ///////////////////////////////////////////////////////////////////////////////
210
211 class SkSpecialImage_Raster : public SkSpecialImage_Base {
212 public:
SkSpecialImage_Raster(const SkIRect & subset,const SkBitmap & bm,const SkSurfaceProps * props)213 SkSpecialImage_Raster(const SkIRect& subset, const SkBitmap& bm, const SkSurfaceProps* props)
214 : INHERITED(subset, bm.getGenerationID(), props)
215 , fBitmap(bm)
216 {
217 SkASSERT(bm.pixelRef());
218 SkASSERT(fBitmap.getPixels());
219 }
220
alphaType() const221 SkAlphaType alphaType() const override { return fBitmap.alphaType(); }
222
getSize() const223 size_t getSize() const override { return fBitmap.computeByteSize(); }
224
onDraw(SkCanvas * canvas,SkScalar x,SkScalar y,const SkPaint * paint) const225 void onDraw(SkCanvas* canvas, SkScalar x, SkScalar y, const SkPaint* paint) const override {
226 SkRect dst = SkRect::MakeXYWH(x, y,
227 this->subset().width(), this->subset().height());
228
229 canvas->drawBitmapRect(fBitmap, this->subset(),
230 dst, paint, SkCanvas::kStrict_SrcRectConstraint);
231 }
232
onGetROPixels(SkBitmap * bm) const233 bool onGetROPixels(SkBitmap* bm) const override {
234 *bm = fBitmap;
235 return true;
236 }
237
onGetColorSpace() const238 SkColorSpace* onGetColorSpace() const override {
239 return fBitmap.colorSpace();
240 }
241
242 #if SK_SUPPORT_GPU
onAsTextureProxyRef(GrContext * context) const243 sk_sp<GrTextureProxy> onAsTextureProxyRef(GrContext* context) const override {
244 if (context) {
245 return GrMakeCachedBitmapProxy(context->contextPriv().proxyProvider(), fBitmap);
246 }
247
248 return nullptr;
249 }
250 #endif
251
252 // TODO: The raster implementations of image filters all currently assume that the pixels are
253 // legacy N32. Until they actually check the format and operate on sRGB or F16 data appropriately,
254 // we can't enable this. (They will continue to produce incorrect results, but less-so).
255 #define RASTER_IMAGE_FILTERS_SUPPORT_SRGB_AND_F16 0
256
onMakeSurface(const SkImageFilter::OutputProperties & outProps,const SkISize & size,SkAlphaType at) const257 sk_sp<SkSpecialSurface> onMakeSurface(const SkImageFilter::OutputProperties& outProps,
258 const SkISize& size, SkAlphaType at) const override {
259 #if RASTER_IMAGE_FILTERS_SUPPORT_SRGB_AND_F16
260 SkColorSpace* colorSpace = outProps.colorSpace();
261 #else
262 SkColorSpace* colorSpace = nullptr;
263 #endif
264 SkColorType colorType = colorSpace && colorSpace->gammaIsLinear()
265 ? kRGBA_F16_SkColorType : kN32_SkColorType;
266 SkImageInfo info = SkImageInfo::Make(size.width(), size.height(), colorType, at,
267 sk_ref_sp(colorSpace));
268 return SkSpecialSurface::MakeRaster(info, nullptr);
269 }
270
onMakeSubset(const SkIRect & subset) const271 sk_sp<SkSpecialImage> onMakeSubset(const SkIRect& subset) const override {
272 SkBitmap subsetBM;
273
274 if (!fBitmap.extractSubset(&subsetBM, subset)) {
275 return nullptr;
276 }
277
278 return SkSpecialImage::MakeFromRaster(SkIRect::MakeWH(subset.width(), subset.height()),
279 subsetBM,
280 &this->props());
281 }
282
onAsImage(const SkIRect * subset) const283 sk_sp<SkImage> onAsImage(const SkIRect* subset) const override {
284 if (subset) {
285 SkBitmap subsetBM;
286
287 if (!fBitmap.extractSubset(&subsetBM, *subset)) {
288 return nullptr;
289 }
290
291 return SkImage::MakeFromBitmap(subsetBM);
292 }
293
294 return SkImage::MakeFromBitmap(fBitmap);
295 }
296
onMakeTightSurface(const SkImageFilter::OutputProperties & outProps,const SkISize & size,SkAlphaType at) const297 sk_sp<SkSurface> onMakeTightSurface(const SkImageFilter::OutputProperties& outProps,
298 const SkISize& size, SkAlphaType at) const override {
299 #if RASTER_IMAGE_FILTERS_SUPPORT_SRGB_AND_F16
300 SkColorSpace* colorSpace = outProps.colorSpace();
301 #else
302 SkColorSpace* colorSpace = nullptr;
303 #endif
304 SkColorType colorType = colorSpace && colorSpace->gammaIsLinear()
305 ? kRGBA_F16_SkColorType : kN32_SkColorType;
306 SkImageInfo info = SkImageInfo::Make(size.width(), size.height(), colorType, at,
307 sk_ref_sp(colorSpace));
308 return SkSurface::MakeRaster(info);
309 }
310
311 private:
312 SkBitmap fBitmap;
313
314 typedef SkSpecialImage_Base INHERITED;
315 };
316
MakeFromRaster(const SkIRect & subset,const SkBitmap & bm,const SkSurfaceProps * props)317 sk_sp<SkSpecialImage> SkSpecialImage::MakeFromRaster(const SkIRect& subset,
318 const SkBitmap& bm,
319 const SkSurfaceProps* props) {
320 SkASSERT(rect_fits(subset, bm.width(), bm.height()));
321
322 if (!bm.pixelRef()) {
323 return nullptr;
324 }
325
326 const SkBitmap* srcBM = &bm;
327 SkBitmap tmp;
328 // ImageFilters only handle N32 at the moment, so force our src to be that
329 if (!valid_for_imagefilters(bm.info())) {
330 if (!tmp.tryAllocPixels(bm.info().makeColorType(kN32_SkColorType)) ||
331 !bm.readPixels(tmp.info(), tmp.getPixels(), tmp.rowBytes(), 0, 0))
332 {
333 return nullptr;
334 }
335 srcBM = &tmp;
336 }
337 return sk_make_sp<SkSpecialImage_Raster>(subset, *srcBM, props);
338 }
339
340 #if SK_SUPPORT_GPU
341 ///////////////////////////////////////////////////////////////////////////////
wrap_proxy_in_image(GrContext * context,sk_sp<GrTextureProxy> proxy,SkAlphaType alphaType,sk_sp<SkColorSpace> colorSpace)342 static sk_sp<SkImage> wrap_proxy_in_image(GrContext* context, sk_sp<GrTextureProxy> proxy,
343 SkAlphaType alphaType, sk_sp<SkColorSpace> colorSpace) {
344 return sk_make_sp<SkImage_Gpu>(context, kNeedNewImageUniqueID, alphaType,
345 std::move(proxy), std::move(colorSpace), SkBudgeted::kYes);
346 }
347
348 class SkSpecialImage_Gpu : public SkSpecialImage_Base {
349 public:
SkSpecialImage_Gpu(GrContext * context,const SkIRect & subset,uint32_t uniqueID,sk_sp<GrTextureProxy> proxy,SkAlphaType at,sk_sp<SkColorSpace> colorSpace,const SkSurfaceProps * props)350 SkSpecialImage_Gpu(GrContext* context, const SkIRect& subset,
351 uint32_t uniqueID, sk_sp<GrTextureProxy> proxy, SkAlphaType at,
352 sk_sp<SkColorSpace> colorSpace, const SkSurfaceProps* props)
353 : INHERITED(subset, uniqueID, props)
354 , fContext(context)
355 , fTextureProxy(std::move(proxy))
356 , fAlphaType(at)
357 , fColorSpace(std::move(colorSpace))
358 , fAddedRasterVersionToCache(false) {
359 }
360
~SkSpecialImage_Gpu()361 ~SkSpecialImage_Gpu() override {
362 if (fAddedRasterVersionToCache.load()) {
363 SkNotifyBitmapGenIDIsStale(this->uniqueID());
364 }
365 }
366
alphaType() const367 SkAlphaType alphaType() const override { return fAlphaType; }
368
getSize() const369 size_t getSize() const override { return fTextureProxy->gpuMemorySize(); }
370
onDraw(SkCanvas * canvas,SkScalar x,SkScalar y,const SkPaint * paint) const371 void onDraw(SkCanvas* canvas, SkScalar x, SkScalar y, const SkPaint* paint) const override {
372 SkRect dst = SkRect::MakeXYWH(x, y,
373 this->subset().width(), this->subset().height());
374
375 // TODO: In this instance we know we're going to draw a sub-portion of the backing
376 // texture into the canvas so it is okay to wrap it in an SkImage. This poses
377 // some problems for full deferral however in that when the deferred SkImage_Gpu
378 // instantiates itself it is going to have to either be okay with having a larger
379 // than expected backing texture (unlikely) or the 'fit' of the SurfaceProxy needs
380 // to be tightened (if it is deferred).
381 sk_sp<SkImage> img = sk_sp<SkImage>(new SkImage_Gpu(canvas->getGrContext(),
382 this->uniqueID(), fAlphaType,
383 fTextureProxy,
384 fColorSpace, SkBudgeted::kNo));
385
386 canvas->drawImageRect(img, this->subset(),
387 dst, paint, SkCanvas::kStrict_SrcRectConstraint);
388 }
389
onGetContext() const390 GrContext* onGetContext() const override { return fContext; }
391
onAsTextureProxyRef(GrContext *) const392 sk_sp<GrTextureProxy> onAsTextureProxyRef(GrContext*) const override {
393 return fTextureProxy;
394 }
395
onGetROPixels(SkBitmap * dst) const396 bool onGetROPixels(SkBitmap* dst) const override {
397 const auto desc = SkBitmapCacheDesc::Make(this->uniqueID(), this->width(), this->height());
398 if (SkBitmapCache::Find(desc, dst)) {
399 SkASSERT(dst->getGenerationID() == this->uniqueID());
400 SkASSERT(dst->isImmutable());
401 SkASSERT(dst->getPixels());
402 return true;
403 }
404
405 SkPixmap pmap;
406 SkImageInfo info = SkImageInfo::MakeN32(this->width(), this->height(),
407 this->alphaType(), fColorSpace);
408 auto rec = SkBitmapCache::Alloc(desc, info, &pmap);
409 if (!rec) {
410 return false;
411 }
412 sk_sp<SkColorSpace> colorSpace;
413 if (GrPixelConfigIsSRGB(fTextureProxy->config())) {
414 colorSpace = SkColorSpace::MakeSRGB();
415 }
416 sk_sp<GrSurfaceContext> sContext = fContext->contextPriv().makeWrappedSurfaceContext(
417 fTextureProxy, std::move(colorSpace));
418 if (!sContext) {
419 return false;
420 }
421
422 if (!sContext->readPixels(info, pmap.writable_addr(), pmap.rowBytes(), 0, 0)) {
423 return false;
424 }
425
426 SkBitmapCache::Add(std::move(rec), dst);
427 fAddedRasterVersionToCache.store(true);
428 return true;
429 }
430
onGetColorSpace() const431 SkColorSpace* onGetColorSpace() const override {
432 return fColorSpace.get();
433 }
434
onMakeSurface(const SkImageFilter::OutputProperties & outProps,const SkISize & size,SkAlphaType at) const435 sk_sp<SkSpecialSurface> onMakeSurface(const SkImageFilter::OutputProperties& outProps,
436 const SkISize& size, SkAlphaType at) const override {
437 if (!fContext) {
438 return nullptr;
439 }
440
441 SkColorSpace* colorSpace = outProps.colorSpace();
442 return SkSpecialSurface::MakeRenderTarget(
443 fContext, size.width(), size.height(),
444 GrRenderableConfigForColorSpace(colorSpace), sk_ref_sp(colorSpace));
445 }
446
onMakeSubset(const SkIRect & subset) const447 sk_sp<SkSpecialImage> onMakeSubset(const SkIRect& subset) const override {
448 return SkSpecialImage::MakeDeferredFromGpu(fContext,
449 subset,
450 this->uniqueID(),
451 fTextureProxy,
452 fColorSpace,
453 &this->props(),
454 fAlphaType);
455 }
456
457 // TODO: move all the logic here into the subset-flavor GrSurfaceProxy::copy?
onAsImage(const SkIRect * subset) const458 sk_sp<SkImage> onAsImage(const SkIRect* subset) const override {
459 if (subset) {
460 // TODO: if this becomes a bottle neck we could base this logic on what the size
461 // will be when it is finally instantiated - but that is more fraught.
462 if (GrProxyProvider::IsFunctionallyExact(fTextureProxy.get()) &&
463 0 == subset->fLeft && 0 == subset->fTop &&
464 fTextureProxy->width() == subset->width() &&
465 fTextureProxy->height() == subset->height()) {
466 // The existing GrTexture is already tight so reuse it in the SkImage
467 return wrap_proxy_in_image(fContext, fTextureProxy, fAlphaType, fColorSpace);
468 }
469
470 sk_sp<GrTextureProxy> subsetProxy(GrSurfaceProxy::Copy(fContext, fTextureProxy.get(),
471 GrMipMapped::kNo, *subset,
472 SkBudgeted::kYes));
473 if (!subsetProxy) {
474 return nullptr;
475 }
476
477 SkASSERT(subsetProxy->priv().isExact());
478 // MDB: this is acceptable (wrapping subsetProxy in an SkImage) bc Copy will
479 // return a kExact-backed proxy
480 return wrap_proxy_in_image(fContext, std::move(subsetProxy), fAlphaType, fColorSpace);
481 }
482
483 fTextureProxy->priv().exactify();
484
485 return wrap_proxy_in_image(fContext, fTextureProxy, fAlphaType, fColorSpace);
486 }
487
onMakeTightSurface(const SkImageFilter::OutputProperties & outProps,const SkISize & size,SkAlphaType at) const488 sk_sp<SkSurface> onMakeTightSurface(const SkImageFilter::OutputProperties& outProps,
489 const SkISize& size, SkAlphaType at) const override {
490 SkColorSpace* colorSpace = outProps.colorSpace();
491 SkColorType colorType = colorSpace && colorSpace->gammaIsLinear()
492 ? kRGBA_F16_SkColorType : kRGBA_8888_SkColorType;
493 SkImageInfo info = SkImageInfo::Make(size.width(), size.height(), colorType, at,
494 sk_ref_sp(colorSpace));
495 return SkSurface::MakeRenderTarget(fContext, SkBudgeted::kYes, info);
496 }
497
498 private:
499 GrContext* fContext;
500 sk_sp<GrTextureProxy> fTextureProxy;
501 const SkAlphaType fAlphaType;
502 sk_sp<SkColorSpace> fColorSpace;
503 mutable SkAtomic<bool> fAddedRasterVersionToCache;
504
505 typedef SkSpecialImage_Base INHERITED;
506 };
507
MakeDeferredFromGpu(GrContext * context,const SkIRect & subset,uint32_t uniqueID,sk_sp<GrTextureProxy> proxy,sk_sp<SkColorSpace> colorSpace,const SkSurfaceProps * props,SkAlphaType at)508 sk_sp<SkSpecialImage> SkSpecialImage::MakeDeferredFromGpu(GrContext* context,
509 const SkIRect& subset,
510 uint32_t uniqueID,
511 sk_sp<GrTextureProxy> proxy,
512 sk_sp<SkColorSpace> colorSpace,
513 const SkSurfaceProps* props,
514 SkAlphaType at) {
515 SkASSERT(rect_fits(subset, proxy->width(), proxy->height()));
516 return sk_make_sp<SkSpecialImage_Gpu>(context, subset, uniqueID, std::move(proxy), at,
517 std::move(colorSpace), props);
518 }
519 #endif
520