• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "GrResourceProvider.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     SkBitmap bmp;
91     // At this point, we are definitely not texture-backed, so we must be raster or generator
92     // backed. If we remove the special-wrapping-an-image subclass, we may be able to assert that
93     // we are strictly raster-backed (i.e. generator images become raster when they are specialized)
94     // in which case getROPixels could turn into peekPixels...
95     if (!this->getROPixels(&bmp)) {
96         return nullptr;
97     }
98 
99     if (bmp.empty()) {
100         return SkSpecialImage::MakeFromRaster(SkIRect::MakeEmpty(), bmp, &this->props());
101     }
102 
103     // TODO: this is a tight copy of 'bmp' but it doesn't have to be (given SkSpecialImage's
104     // semantics). Since this is cached though we would have to bake the fit into the cache key.
105     sk_sp<GrTextureProxy> proxy = GrMakeCachedBitmapProxy(context->resourceProvider(), bmp);
106     if (!proxy) {
107         return nullptr;
108     }
109 
110     const SkIRect rect = SkIRect::MakeWH(proxy->width(), proxy->height());
111 
112     // GrMakeCachedBitmapProxy has uploaded only the specified subset of 'bmp' so we need not
113     // bother with SkBitmap::getSubset
114     return SkSpecialImage::MakeDeferredFromGpu(context,
115                                                rect,
116                                                this->uniqueID(),
117                                                std::move(proxy),
118                                                sk_ref_sp(this->getColorSpace()),
119                                                &this->props(),
120                                                this->alphaType());
121 #else
122     return nullptr;
123 #endif
124 }
125 
draw(SkCanvas * canvas,SkScalar x,SkScalar y,const SkPaint * paint) const126 void SkSpecialImage::draw(SkCanvas* canvas, SkScalar x, SkScalar y, const SkPaint* paint) const {
127     return as_SIB(this)->onDraw(canvas, x, y, paint);
128 }
129 
getROPixels(SkBitmap * bm) const130 bool SkSpecialImage::getROPixels(SkBitmap* bm) const {
131     return as_SIB(this)->onGetROPixels(bm);
132 }
133 
isTextureBacked() const134 bool SkSpecialImage::isTextureBacked() const {
135     return SkToBool(as_SIB(this)->onGetContext());
136 }
137 
getContext() const138 GrContext* SkSpecialImage::getContext() const {
139     return as_SIB(this)->onGetContext();
140 }
141 
getColorSpace() const142 SkColorSpace* SkSpecialImage::getColorSpace() const {
143     return as_SIB(this)->onGetColorSpace();
144 }
145 
146 #if SK_SUPPORT_GPU
asTextureProxyRef(GrContext * context) const147 sk_sp<GrTextureProxy> SkSpecialImage::asTextureProxyRef(GrContext* context) const {
148     return as_SIB(this)->onAsTextureProxyRef(context);
149 }
150 #endif
151 
makeSurface(const SkImageFilter::OutputProperties & outProps,const SkISize & size,SkAlphaType at) const152 sk_sp<SkSpecialSurface> SkSpecialImage::makeSurface(const SkImageFilter::OutputProperties& outProps,
153                                                     const SkISize& size, SkAlphaType at) const {
154     return as_SIB(this)->onMakeSurface(outProps, size, at);
155 }
156 
makeTightSurface(const SkImageFilter::OutputProperties & outProps,const SkISize & size,SkAlphaType at) const157 sk_sp<SkSurface> SkSpecialImage::makeTightSurface(const SkImageFilter::OutputProperties& outProps,
158                                                   const SkISize& size, SkAlphaType at) const {
159     return as_SIB(this)->onMakeTightSurface(outProps, size, at);
160 }
161 
makeSubset(const SkIRect & subset) const162 sk_sp<SkSpecialImage> SkSpecialImage::makeSubset(const SkIRect& subset) const {
163     return as_SIB(this)->onMakeSubset(subset);
164 }
165 
asImage(const SkIRect * subset) const166 sk_sp<SkImage> SkSpecialImage::asImage(const SkIRect* subset) const {
167     return as_SIB(this)->onAsImage(subset);
168 }
169 
170 
171 #ifdef SK_DEBUG
rect_fits(const SkIRect & rect,int width,int height)172 static bool rect_fits(const SkIRect& rect, int width, int height) {
173     if (0 == width && 0 == height) {
174         SkASSERT(0 == rect.fLeft && 0 == rect.fRight && 0 == rect.fTop && 0 == rect.fBottom);
175         return true;
176     }
177 
178     return rect.fLeft >= 0 && rect.fLeft < width && rect.fLeft < rect.fRight &&
179            rect.fRight >= 0 && rect.fRight <= width &&
180            rect.fTop >= 0 && rect.fTop < height && rect.fTop < rect.fBottom &&
181            rect.fBottom >= 0 && rect.fBottom <= height;
182 }
183 #endif
184 
MakeFromImage(const SkIRect & subset,sk_sp<SkImage> image,SkColorSpace * dstColorSpace,const SkSurfaceProps * props)185 sk_sp<SkSpecialImage> SkSpecialImage::MakeFromImage(const SkIRect& subset,
186                                                     sk_sp<SkImage> image,
187                                                     SkColorSpace* dstColorSpace,
188                                                     const SkSurfaceProps* props) {
189     SkASSERT(rect_fits(subset, image->width(), image->height()));
190 
191 #if SK_SUPPORT_GPU
192     if (sk_sp<GrTextureProxy> proxy = as_IB(image)->asTextureProxyRef()) {
193         GrContext* context = ((SkImage_Gpu*) as_IB(image))->context();
194 
195         return MakeDeferredFromGpu(context, subset, image->uniqueID(), std::move(proxy),
196                                    as_IB(image)->onImageInfo().refColorSpace(), props);
197     } else
198 #endif
199     {
200         SkBitmap bm;
201         if (as_IB(image)->getROPixels(&bm, dstColorSpace)) {
202             return MakeFromRaster(subset, bm, props);
203         }
204     }
205     return nullptr;
206 }
207 
208 ///////////////////////////////////////////////////////////////////////////////
209 
210 class SkSpecialImage_Raster : public SkSpecialImage_Base {
211 public:
SkSpecialImage_Raster(const SkIRect & subset,const SkBitmap & bm,const SkSurfaceProps * props)212     SkSpecialImage_Raster(const SkIRect& subset, const SkBitmap& bm, const SkSurfaceProps* props)
213         : INHERITED(subset, bm.getGenerationID(), props)
214         , fBitmap(bm)
215     {
216         SkASSERT(bm.pixelRef());
217         SkASSERT(fBitmap.getPixels());
218     }
219 
alphaType() const220     SkAlphaType alphaType() const override { return fBitmap.alphaType(); }
221 
getSize() const222     size_t getSize() const override { return fBitmap.getSize(); }
223 
onDraw(SkCanvas * canvas,SkScalar x,SkScalar y,const SkPaint * paint) const224     void onDraw(SkCanvas* canvas, SkScalar x, SkScalar y, const SkPaint* paint) const override {
225         SkRect dst = SkRect::MakeXYWH(x, y,
226                                       this->subset().width(), this->subset().height());
227 
228         canvas->drawBitmapRect(fBitmap, this->subset(),
229                                dst, paint, SkCanvas::kStrict_SrcRectConstraint);
230     }
231 
onGetROPixels(SkBitmap * bm) const232     bool onGetROPixels(SkBitmap* bm) const override {
233         *bm = fBitmap;
234         return true;
235     }
236 
onGetColorSpace() const237     SkColorSpace* onGetColorSpace() const override {
238         return fBitmap.colorSpace();
239     }
240 
241 #if SK_SUPPORT_GPU
onAsTextureProxyRef(GrContext * context) const242     sk_sp<GrTextureProxy> onAsTextureProxyRef(GrContext* context) const override {
243         if (context) {
244             return GrMakeCachedBitmapProxy(context->resourceProvider(), fBitmap);
245         }
246 
247         return nullptr;
248     }
249 #endif
250 
251 // TODO: The raster implementations of image filters all currently assume that the pixels are
252 // legacy N32. Until they actually check the format and operate on sRGB or F16 data appropriately,
253 // we can't enable this. (They will continue to produce incorrect results, but less-so).
254 #define RASTER_IMAGE_FILTERS_SUPPORT_SRGB_AND_F16 0
255 
onMakeSurface(const SkImageFilter::OutputProperties & outProps,const SkISize & size,SkAlphaType at) const256     sk_sp<SkSpecialSurface> onMakeSurface(const SkImageFilter::OutputProperties& outProps,
257                                           const SkISize& size, SkAlphaType at) const override {
258 #if RASTER_IMAGE_FILTERS_SUPPORT_SRGB_AND_F16
259         SkColorSpace* colorSpace = outProps.colorSpace();
260 #else
261         SkColorSpace* colorSpace = nullptr;
262 #endif
263         SkColorType colorType = colorSpace && colorSpace->gammaIsLinear()
264             ? kRGBA_F16_SkColorType : kN32_SkColorType;
265         SkImageInfo info = SkImageInfo::Make(size.width(), size.height(), colorType, at,
266                                              sk_ref_sp(colorSpace));
267         return SkSpecialSurface::MakeRaster(info, nullptr);
268     }
269 
onMakeSubset(const SkIRect & subset) const270     sk_sp<SkSpecialImage> onMakeSubset(const SkIRect& subset) const override {
271         SkBitmap subsetBM;
272 
273         if (!fBitmap.extractSubset(&subsetBM, subset)) {
274             return nullptr;
275         }
276 
277         return SkSpecialImage::MakeFromRaster(SkIRect::MakeWH(subset.width(), subset.height()),
278                                               subsetBM,
279                                               &this->props());
280     }
281 
onAsImage(const SkIRect * subset) const282     sk_sp<SkImage> onAsImage(const SkIRect* subset) const override {
283         if (subset) {
284             SkBitmap subsetBM;
285 
286             if (!fBitmap.extractSubset(&subsetBM, *subset)) {
287                 return nullptr;
288             }
289 
290             return SkImage::MakeFromBitmap(subsetBM);
291         }
292 
293         return SkImage::MakeFromBitmap(fBitmap);
294     }
295 
onMakeTightSurface(const SkImageFilter::OutputProperties & outProps,const SkISize & size,SkAlphaType at) const296     sk_sp<SkSurface> onMakeTightSurface(const SkImageFilter::OutputProperties& outProps,
297                                         const SkISize& size, SkAlphaType at) const override {
298 #if RASTER_IMAGE_FILTERS_SUPPORT_SRGB_AND_F16
299         SkColorSpace* colorSpace = outProps.colorSpace();
300 #else
301         SkColorSpace* colorSpace = nullptr;
302 #endif
303         SkColorType colorType = colorSpace && colorSpace->gammaIsLinear()
304             ? kRGBA_F16_SkColorType : kN32_SkColorType;
305         SkImageInfo info = SkImageInfo::Make(size.width(), size.height(), colorType, at,
306                                              sk_ref_sp(colorSpace));
307         return SkSurface::MakeRaster(info);
308     }
309 
310 private:
311     SkBitmap fBitmap;
312 
313     typedef SkSpecialImage_Base INHERITED;
314 };
315 
MakeFromRaster(const SkIRect & subset,const SkBitmap & bm,const SkSurfaceProps * props)316 sk_sp<SkSpecialImage> SkSpecialImage::MakeFromRaster(const SkIRect& subset,
317                                                      const SkBitmap& bm,
318                                                      const SkSurfaceProps* props) {
319     SkASSERT(rect_fits(subset, bm.width(), bm.height()));
320 
321     if (!bm.pixelRef()) {
322         return nullptr;
323     }
324 
325     const SkBitmap* srcBM = &bm;
326     SkBitmap tmp;
327     // ImageFilters only handle N32 at the moment, so force our src to be that
328     if (!valid_for_imagefilters(bm.info())) {
329         if (!tmp.tryAllocPixels(bm.info().makeColorType(kN32_SkColorType)) ||
330             !bm.readPixels(tmp.info(), tmp.getPixels(), tmp.rowBytes(), 0, 0))
331         {
332             return nullptr;
333         }
334         srcBM = &tmp;
335     }
336     return sk_make_sp<SkSpecialImage_Raster>(subset, *srcBM, props);
337 }
338 
339 #if SK_SUPPORT_GPU
340 ///////////////////////////////////////////////////////////////////////////////
wrap_proxy_in_image(GrContext * context,sk_sp<GrTextureProxy> proxy,SkAlphaType alphaType,sk_sp<SkColorSpace> colorSpace)341 static sk_sp<SkImage> wrap_proxy_in_image(GrContext* context, sk_sp<GrTextureProxy> proxy,
342                                           SkAlphaType alphaType, sk_sp<SkColorSpace> colorSpace) {
343     return sk_make_sp<SkImage_Gpu>(context, kNeedNewImageUniqueID, alphaType,
344                                    std::move(proxy), std::move(colorSpace), SkBudgeted::kYes);
345 }
346 
347 class SkSpecialImage_Gpu : public SkSpecialImage_Base {
348 public:
SkSpecialImage_Gpu(GrContext * context,const SkIRect & subset,uint32_t uniqueID,sk_sp<GrTextureProxy> proxy,SkAlphaType at,sk_sp<SkColorSpace> colorSpace,const SkSurfaceProps * props)349     SkSpecialImage_Gpu(GrContext* context, const SkIRect& subset,
350                        uint32_t uniqueID, sk_sp<GrTextureProxy> proxy, SkAlphaType at,
351                        sk_sp<SkColorSpace> colorSpace, const SkSurfaceProps* props)
352         : INHERITED(subset, uniqueID, props)
353         , fContext(context)
354         , fTextureProxy(std::move(proxy))
355         , fAlphaType(at)
356         , fColorSpace(std::move(colorSpace))
357         , fAddedRasterVersionToCache(false) {
358     }
359 
~SkSpecialImage_Gpu()360     ~SkSpecialImage_Gpu() override {
361         if (fAddedRasterVersionToCache.load()) {
362             SkNotifyBitmapGenIDIsStale(this->uniqueID());
363         }
364     }
365 
alphaType() const366     SkAlphaType alphaType() const override { return fAlphaType; }
367 
getSize() const368     size_t getSize() const override { return fTextureProxy->gpuMemorySize(); }
369 
onDraw(SkCanvas * canvas,SkScalar x,SkScalar y,const SkPaint * paint) const370     void onDraw(SkCanvas* canvas, SkScalar x, SkScalar y, const SkPaint* paint) const override {
371         SkRect dst = SkRect::MakeXYWH(x, y,
372                                       this->subset().width(), this->subset().height());
373 
374         // TODO: In this instance we know we're going to draw a sub-portion of the backing
375         // texture into the canvas so it is okay to wrap it in an SkImage. This poses
376         // some problems for full deferral however in that when the deferred SkImage_Gpu
377         // instantiates itself it is going to have to either be okay with having a larger
378         // than expected backing texture (unlikely) or the 'fit' of the SurfaceProxy needs
379         // to be tightened (if it is deferred).
380         sk_sp<SkImage> img = sk_sp<SkImage>(new SkImage_Gpu(canvas->getGrContext(),
381                                                             this->uniqueID(), fAlphaType,
382                                                             fTextureProxy,
383                                                             fColorSpace, SkBudgeted::kNo));
384 
385         canvas->drawImageRect(img, this->subset(),
386                               dst, paint, SkCanvas::kStrict_SrcRectConstraint);
387     }
388 
onGetContext() const389     GrContext* onGetContext() const override { return fContext; }
390 
onAsTextureProxyRef(GrContext *) const391     sk_sp<GrTextureProxy> onAsTextureProxyRef(GrContext*) const override {
392         return fTextureProxy;
393     }
394 
onGetROPixels(SkBitmap * dst) const395     bool onGetROPixels(SkBitmap* dst) const override {
396         const auto desc = SkBitmapCacheDesc::Make(this->uniqueID(), this->width(), this->height());
397         if (SkBitmapCache::Find(desc, dst)) {
398             SkASSERT(dst->getGenerationID() == this->uniqueID());
399             SkASSERT(dst->isImmutable());
400             SkASSERT(dst->getPixels());
401             return true;
402         }
403 
404         SkPixmap pmap;
405         SkImageInfo info = SkImageInfo::MakeN32(this->width(), this->height(),
406                                                 this->alphaType(), fColorSpace);
407         auto rec = SkBitmapCache::Alloc(desc, info, &pmap);
408         if (!rec) {
409             return false;
410         }
411 
412         sk_sp<GrSurfaceContext> sContext = fContext->contextPriv().makeWrappedSurfaceContext(
413                                                                           fTextureProxy, nullptr);
414         if (!sContext) {
415             return false;
416         }
417 
418         if (!sContext->readPixels(info, pmap.writable_addr(), pmap.rowBytes(), 0, 0)) {
419             return false;
420         }
421 
422         SkBitmapCache::Add(std::move(rec), dst);
423         fAddedRasterVersionToCache.store(true);
424         return true;
425     }
426 
onGetColorSpace() const427     SkColorSpace* onGetColorSpace() const override {
428         return fColorSpace.get();
429     }
430 
onMakeSurface(const SkImageFilter::OutputProperties & outProps,const SkISize & size,SkAlphaType at) const431     sk_sp<SkSpecialSurface> onMakeSurface(const SkImageFilter::OutputProperties& outProps,
432                                           const SkISize& size, SkAlphaType at) const override {
433         if (!fContext) {
434             return nullptr;
435         }
436 
437         SkColorSpace* colorSpace = outProps.colorSpace();
438         return SkSpecialSurface::MakeRenderTarget(
439             fContext, size.width(), size.height(),
440             GrRenderableConfigForColorSpace(colorSpace), sk_ref_sp(colorSpace));
441     }
442 
onMakeSubset(const SkIRect & subset) const443     sk_sp<SkSpecialImage> onMakeSubset(const SkIRect& subset) const override {
444         return SkSpecialImage::MakeDeferredFromGpu(fContext,
445                                                    subset,
446                                                    this->uniqueID(),
447                                                    fTextureProxy,
448                                                    fColorSpace,
449                                                    &this->props(),
450                                                    fAlphaType);
451     }
452 
453     // TODO: move all the logic here into the subset-flavor GrSurfaceProxy::copy?
onAsImage(const SkIRect * subset) const454     sk_sp<SkImage> onAsImage(const SkIRect* subset) const override {
455         if (subset) {
456             // TODO: if this becomes a bottle neck we could base this logic on what the size
457             // will be when it is finally instantiated - but that is more fraught.
458             if (GrResourceProvider::IsFunctionallyExact(fTextureProxy.get()) &&
459                 0 == subset->fLeft && 0 == subset->fTop &&
460                 fTextureProxy->width() == subset->width() &&
461                 fTextureProxy->height() == subset->height()) {
462                 // The existing GrTexture is already tight so reuse it in the SkImage
463                 return wrap_proxy_in_image(fContext, fTextureProxy, fAlphaType, fColorSpace);
464             }
465 
466             sk_sp<GrTextureProxy> subsetProxy(GrSurfaceProxy::Copy(fContext, fTextureProxy.get(),
467                                                                    *subset, SkBudgeted::kYes));
468             if (!subsetProxy) {
469                 return nullptr;
470             }
471 
472             SkASSERT(subsetProxy->priv().isExact());
473             // MDB: this is acceptable (wrapping subsetProxy in an SkImage) bc Copy will
474             // return a kExact-backed proxy
475             return wrap_proxy_in_image(fContext, std::move(subsetProxy), fAlphaType, fColorSpace);
476         }
477 
478         fTextureProxy->priv().exactify();
479 
480         return wrap_proxy_in_image(fContext, fTextureProxy, fAlphaType, fColorSpace);
481     }
482 
onMakeTightSurface(const SkImageFilter::OutputProperties & outProps,const SkISize & size,SkAlphaType at) const483     sk_sp<SkSurface> onMakeTightSurface(const SkImageFilter::OutputProperties& outProps,
484                                         const SkISize& size, SkAlphaType at) const override {
485         SkColorSpace* colorSpace = outProps.colorSpace();
486         SkColorType colorType = colorSpace && colorSpace->gammaIsLinear()
487             ? kRGBA_F16_SkColorType : kRGBA_8888_SkColorType;
488         SkImageInfo info = SkImageInfo::Make(size.width(), size.height(), colorType, at,
489                                              sk_ref_sp(colorSpace));
490         return SkSurface::MakeRenderTarget(fContext, SkBudgeted::kYes, info);
491     }
492 
493 private:
494     GrContext*              fContext;
495     sk_sp<GrTextureProxy>   fTextureProxy;
496     const SkAlphaType       fAlphaType;
497     sk_sp<SkColorSpace>     fColorSpace;
498     mutable SkAtomic<bool>  fAddedRasterVersionToCache;
499 
500     typedef SkSpecialImage_Base INHERITED;
501 };
502 
MakeDeferredFromGpu(GrContext * context,const SkIRect & subset,uint32_t uniqueID,sk_sp<GrTextureProxy> proxy,sk_sp<SkColorSpace> colorSpace,const SkSurfaceProps * props,SkAlphaType at)503 sk_sp<SkSpecialImage> SkSpecialImage::MakeDeferredFromGpu(GrContext* context,
504                                                           const SkIRect& subset,
505                                                           uint32_t uniqueID,
506                                                           sk_sp<GrTextureProxy> proxy,
507                                                           sk_sp<SkColorSpace> colorSpace,
508                                                           const SkSurfaceProps* props,
509                                                           SkAlphaType at) {
510     SkASSERT(rect_fits(subset, proxy->width(), proxy->height()));
511     return sk_make_sp<SkSpecialImage_Gpu>(context, subset, uniqueID, std::move(proxy), at,
512                                           std::move(colorSpace), props);
513 }
514 #endif
515