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