Home | History | Annotate | Download | only in core
      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 <atomic>
     17 
     18 #if SK_SUPPORT_GPU
     19 #include "GrContext.h"
     20 #include "GrContextPriv.h"
     21 #include "GrProxyProvider.h"
     22 #include "GrRecordingContext.h"
     23 #include "GrRecordingContextPriv.h"
     24 #include "GrSurfaceContext.h"
     25 #include "GrTextureProxy.h"
     26 #include "SkImage_Gpu.h"
     27 #endif
     28 
     29 // Currently the raster imagefilters can only handle certain imageinfos. Call this to know if
     30 // a given info is supported.
     31 static bool valid_for_imagefilters(const SkImageInfo& info) {
     32     // no support for other swizzles/depths yet
     33     return info.colorType() == kN32_SkColorType;
     34 }
     35 
     36 ///////////////////////////////////////////////////////////////////////////////
     37 class SkSpecialImage_Base : public SkSpecialImage {
     38 public:
     39     SkSpecialImage_Base(const SkIRect& subset, uint32_t uniqueID, const SkSurfaceProps* props)
     40         : INHERITED(subset, uniqueID, props) {
     41     }
     42     ~SkSpecialImage_Base() override { }
     43 
     44     virtual void onDraw(SkCanvas*, SkScalar x, SkScalar y, const SkPaint*) const = 0;
     45 
     46     virtual bool onGetROPixels(SkBitmap*) const = 0;
     47 
     48     virtual GrRecordingContext* onGetContext() const { return nullptr; }
     49 
     50     virtual SkColorSpace* onGetColorSpace() const = 0;
     51 
     52 #if SK_SUPPORT_GPU
     53     virtual sk_sp<GrTextureProxy> onAsTextureProxyRef(GrRecordingContext* context) const = 0;
     54 #endif
     55 
     56     virtual sk_sp<SkSpecialImage> onMakeSubset(const SkIRect& subset) const = 0;
     57 
     58     virtual sk_sp<SkSpecialSurface> onMakeSurface(const SkImageFilter::OutputProperties& outProps,
     59                                                   const SkISize& size, SkAlphaType at,
     60                                                   const SkSurfaceProps* = nullptr) 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 ///////////////////////////////////////////////////////////////////////////////
     72 static inline const SkSpecialImage_Base* as_SIB(const SkSpecialImage* image) {
     73     return static_cast<const SkSpecialImage_Base*>(image);
     74 }
     75 
     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 
     84 sk_sp<SkSpecialImage> SkSpecialImage::makeTextureImage(GrRecordingContext* context) {
     85 #if SK_SUPPORT_GPU
     86     if (!context) {
     87         return nullptr;
     88     }
     89     if (GrRecordingContext* curContext = as_SIB(this)->onGetContext()) {
     90         return curContext->priv().matches(context) ? sk_sp<SkSpecialImage>(SkRef(this)) : nullptr;
     91     }
     92 
     93     auto proxyProvider = context->priv().proxyProvider();
     94     SkBitmap bmp;
     95     // At this point, we are definitely not texture-backed, so we must be raster or generator
     96     // backed. If we remove the special-wrapping-an-image subclass, we may be able to assert that
     97     // we are strictly raster-backed (i.e. generator images become raster when they are specialized)
     98     // in which case getROPixels could turn into peekPixels...
     99     if (!this->getROPixels(&bmp)) {
    100         return nullptr;
    101     }
    102 
    103     if (bmp.empty()) {
    104         return SkSpecialImage::MakeFromRaster(SkIRect::MakeEmpty(), bmp, &this->props());
    105     }
    106 
    107     // TODO: this is a tight copy of 'bmp' but it doesn't have to be (given SkSpecialImage's
    108     // semantics). Since this is cached though we would have to bake the fit into the cache key.
    109     sk_sp<GrTextureProxy> proxy = GrMakeCachedBitmapProxy(proxyProvider, bmp);
    110     if (!proxy) {
    111         return nullptr;
    112     }
    113 
    114     const SkIRect rect = SkIRect::MakeWH(proxy->width(), proxy->height());
    115 
    116     // GrMakeCachedBitmapProxy has uploaded only the specified subset of 'bmp' so we need not
    117     // bother with SkBitmap::getSubset
    118     return SkSpecialImage::MakeDeferredFromGpu(context,
    119                                                rect,
    120                                                this->uniqueID(),
    121                                                std::move(proxy),
    122                                                sk_ref_sp(this->getColorSpace()),
    123                                                &this->props(),
    124                                                this->alphaType());
    125 #else
    126     return nullptr;
    127 #endif
    128 }
    129 
    130 void SkSpecialImage::draw(SkCanvas* canvas, SkScalar x, SkScalar y, const SkPaint* paint) const {
    131     return as_SIB(this)->onDraw(canvas, x, y, paint);
    132 }
    133 
    134 bool SkSpecialImage::getROPixels(SkBitmap* bm) const {
    135     return as_SIB(this)->onGetROPixels(bm);
    136 }
    137 
    138 bool SkSpecialImage::isTextureBacked() const {
    139     return SkToBool(as_SIB(this)->onGetContext());
    140 }
    141 
    142 GrRecordingContext* SkSpecialImage::getContext() const {
    143     return as_SIB(this)->onGetContext();
    144 }
    145 
    146 SkColorSpace* SkSpecialImage::getColorSpace() const {
    147     return as_SIB(this)->onGetColorSpace();
    148 }
    149 
    150 #if SK_SUPPORT_GPU
    151 sk_sp<GrTextureProxy> SkSpecialImage::asTextureProxyRef(GrRecordingContext* context) const {
    152     return as_SIB(this)->onAsTextureProxyRef(context);
    153 }
    154 #endif
    155 
    156 sk_sp<SkSpecialSurface> SkSpecialImage::makeSurface(const SkImageFilter::OutputProperties& outProps,
    157                                                     const SkISize& size, SkAlphaType at,
    158                                                     const SkSurfaceProps* props) const {
    159     return as_SIB(this)->onMakeSurface(outProps, size, at, props);
    160 }
    161 
    162 sk_sp<SkSurface> SkSpecialImage::makeTightSurface(const SkImageFilter::OutputProperties& outProps,
    163                                                   const SkISize& size, SkAlphaType at) const {
    164     return as_SIB(this)->onMakeTightSurface(outProps, size, at);
    165 }
    166 
    167 sk_sp<SkSpecialImage> SkSpecialImage::makeSubset(const SkIRect& subset) const {
    168     return as_SIB(this)->onMakeSubset(subset);
    169 }
    170 
    171 sk_sp<SkImage> SkSpecialImage::asImage(const SkIRect* subset) const {
    172     return as_SIB(this)->onAsImage(subset);
    173 }
    174 
    175 #if defined(SK_DEBUG) || SK_SUPPORT_GPU
    176 static bool rect_fits(const SkIRect& rect, int width, int height) {
    177     if (0 == width && 0 == height) {
    178         SkASSERT(0 == rect.fLeft && 0 == rect.fRight && 0 == rect.fTop && 0 == rect.fBottom);
    179         return true;
    180     }
    181 
    182     return rect.fLeft >= 0 && rect.fLeft < width && rect.fLeft < rect.fRight &&
    183            rect.fRight >= 0 && rect.fRight <= width &&
    184            rect.fTop >= 0 && rect.fTop < height && rect.fTop < rect.fBottom &&
    185            rect.fBottom >= 0 && rect.fBottom <= height;
    186 }
    187 #endif
    188 
    189 sk_sp<SkSpecialImage> SkSpecialImage::MakeFromImage(GrRecordingContext* context,
    190                                                     const SkIRect& subset,
    191                                                     sk_sp<SkImage> image,
    192                                                     const SkSurfaceProps* props) {
    193     SkASSERT(rect_fits(subset, image->width(), image->height()));
    194 
    195 #if SK_SUPPORT_GPU
    196     if (sk_sp<GrTextureProxy> proxy = as_IB(image)->asTextureProxyRef(context)) {
    197         if (!as_IB(image)->context()->priv().matches(context)) {
    198             return nullptr;
    199         }
    200 
    201         return MakeDeferredFromGpu(context, subset, image->uniqueID(), std::move(proxy),
    202                                    as_IB(image)->onImageInfo().refColorSpace(), props);
    203     } else
    204 #endif
    205     {
    206         SkBitmap bm;
    207         if (as_IB(image)->getROPixels(&bm)) {
    208             return MakeFromRaster(subset, bm, props);
    209         }
    210     }
    211     return nullptr;
    212 }
    213 
    214 ///////////////////////////////////////////////////////////////////////////////
    215 
    216 class SkSpecialImage_Raster : public SkSpecialImage_Base {
    217 public:
    218     SkSpecialImage_Raster(const SkIRect& subset, const SkBitmap& bm, const SkSurfaceProps* props)
    219         : INHERITED(subset, bm.getGenerationID(), props)
    220         , fBitmap(bm)
    221     {
    222         SkASSERT(bm.pixelRef());
    223         SkASSERT(fBitmap.getPixels());
    224     }
    225 
    226     SkAlphaType alphaType() const override { return fBitmap.alphaType(); }
    227 
    228     size_t getSize() const override { return fBitmap.computeByteSize(); }
    229 
    230     void onDraw(SkCanvas* canvas, SkScalar x, SkScalar y, const SkPaint* paint) const override {
    231         SkRect dst = SkRect::MakeXYWH(x, y,
    232                                       this->subset().width(), this->subset().height());
    233 
    234         canvas->drawBitmapRect(fBitmap, this->subset(),
    235                                dst, paint, SkCanvas::kStrict_SrcRectConstraint);
    236     }
    237 
    238     bool onGetROPixels(SkBitmap* bm) const override {
    239         *bm = fBitmap;
    240         return true;
    241     }
    242 
    243     SkColorSpace* onGetColorSpace() const override {
    244         return fBitmap.colorSpace();
    245     }
    246 
    247 #if SK_SUPPORT_GPU
    248     sk_sp<GrTextureProxy> onAsTextureProxyRef(GrRecordingContext* context) const override {
    249         if (context) {
    250             return GrMakeCachedBitmapProxy(context->priv().proxyProvider(), fBitmap);
    251         }
    252 
    253         return nullptr;
    254     }
    255 #endif
    256 
    257     sk_sp<SkSpecialSurface> onMakeSurface(const SkImageFilter::OutputProperties& outProps,
    258                                           const SkISize& size, SkAlphaType at,
    259                                           const SkSurfaceProps* props) const override {
    260 #ifdef SK_SUPPORT_LEGACY_RASTERLAYERCOLORSPACE
    261         SkColorSpace* colorSpace = nullptr;
    262         SkColorType colorType = colorSpace && colorSpace->gammaIsLinear()
    263                                 ? kRGBA_F16_SkColorType : kN32_SkColorType;
    264 #else
    265         SkColorSpace* colorSpace = outProps.colorSpace();
    266         SkColorType colorType = kN32_SkColorType;   // TODO: find ways to allow f16
    267 #endif
    268         SkImageInfo info = SkImageInfo::Make(size.width(), size.height(), colorType, at,
    269                                              sk_ref_sp(colorSpace));
    270         return SkSpecialSurface::MakeRaster(info, props);
    271     }
    272 
    273     sk_sp<SkSpecialImage> onMakeSubset(const SkIRect& subset) const override {
    274         SkBitmap subsetBM;
    275 
    276         if (!fBitmap.extractSubset(&subsetBM, subset)) {
    277             return nullptr;
    278         }
    279 
    280         return SkSpecialImage::MakeFromRaster(SkIRect::MakeWH(subset.width(), subset.height()),
    281                                               subsetBM,
    282                                               &this->props());
    283     }
    284 
    285     sk_sp<SkImage> onAsImage(const SkIRect* subset) const override {
    286         if (subset) {
    287             SkBitmap subsetBM;
    288 
    289             if (!fBitmap.extractSubset(&subsetBM, *subset)) {
    290                 return nullptr;
    291             }
    292 
    293             return SkImage::MakeFromBitmap(subsetBM);
    294         }
    295 
    296         return SkImage::MakeFromBitmap(fBitmap);
    297     }
    298 
    299     sk_sp<SkSurface> onMakeTightSurface(const SkImageFilter::OutputProperties& outProps,
    300                                         const SkISize& size, SkAlphaType at) const override {
    301 #ifdef SK_SUPPORT_LEGACY_RASTERLAYERCOLORSPACE
    302         SkColorSpace* colorSpace = nullptr;
    303         SkColorType colorType = colorSpace && colorSpace->gammaIsLinear()
    304         ? kRGBA_F16_SkColorType : kN32_SkColorType;
    305 #else
    306         SkColorSpace* colorSpace = outProps.colorSpace();
    307         SkColorType colorType = kN32_SkColorType;   // TODO: find ways to allow f16
    308 #endif
    309         SkImageInfo info = SkImageInfo::Make(size.width(), size.height(), colorType, at,
    310                                              sk_ref_sp(colorSpace));
    311         return SkSurface::MakeRaster(info);
    312     }
    313 
    314 private:
    315     SkBitmap fBitmap;
    316 
    317     typedef SkSpecialImage_Base INHERITED;
    318 };
    319 
    320 sk_sp<SkSpecialImage> SkSpecialImage::MakeFromRaster(const SkIRect& subset,
    321                                                      const SkBitmap& bm,
    322                                                      const SkSurfaceProps* props) {
    323     SkASSERT(rect_fits(subset, bm.width(), bm.height()));
    324 
    325     if (!bm.pixelRef()) {
    326         return nullptr;
    327     }
    328 
    329     const SkBitmap* srcBM = &bm;
    330     SkBitmap tmp;
    331     // ImageFilters only handle N32 at the moment, so force our src to be that
    332     if (!valid_for_imagefilters(bm.info())) {
    333         if (!tmp.tryAllocPixels(bm.info().makeColorType(kN32_SkColorType)) ||
    334             !bm.readPixels(tmp.info(), tmp.getPixels(), tmp.rowBytes(), 0, 0))
    335         {
    336             return nullptr;
    337         }
    338         srcBM = &tmp;
    339     }
    340     return sk_make_sp<SkSpecialImage_Raster>(subset, *srcBM, props);
    341 }
    342 
    343 sk_sp<SkSpecialImage> SkSpecialImage::CopyFromRaster(const SkIRect& subset,
    344                                                      const SkBitmap& bm,
    345                                                      const SkSurfaceProps* props) {
    346     SkASSERT(rect_fits(subset, bm.width(), bm.height()));
    347 
    348     if (!bm.pixelRef()) {
    349         return nullptr;
    350     }
    351 
    352     SkBitmap tmp;
    353     if (!tmp.tryAllocPixels(bm.info().makeWH(subset.width(), subset.height()))) {
    354         return nullptr;
    355     }
    356     if (!bm.readPixels(tmp.info(), tmp.getPixels(), tmp.rowBytes(), subset.x(), subset.y())) {
    357         return nullptr;
    358     }
    359     return sk_make_sp<SkSpecialImage_Raster>(subset, tmp, props);
    360 }
    361 
    362 #if SK_SUPPORT_GPU
    363 ///////////////////////////////////////////////////////////////////////////////
    364 static sk_sp<SkImage> wrap_proxy_in_image(GrRecordingContext* context, sk_sp<GrTextureProxy> proxy,
    365                                           SkAlphaType alphaType, sk_sp<SkColorSpace> colorSpace) {
    366     // CONTEXT TODO: remove this use of 'backdoor' to create an SkImage
    367     return sk_make_sp<SkImage_Gpu>(sk_ref_sp(context->priv().backdoor()),
    368                                    kNeedNewImageUniqueID, alphaType,
    369                                    std::move(proxy), std::move(colorSpace));
    370 }
    371 
    372 class SkSpecialImage_Gpu : public SkSpecialImage_Base {
    373 public:
    374     SkSpecialImage_Gpu(GrRecordingContext* context, const SkIRect& subset,
    375                        uint32_t uniqueID, sk_sp<GrTextureProxy> proxy, SkAlphaType at,
    376                        sk_sp<SkColorSpace> colorSpace, const SkSurfaceProps* props)
    377         : INHERITED(subset, uniqueID, props)
    378         , fContext(context)
    379         , fTextureProxy(std::move(proxy))
    380         , fAlphaType(at)
    381         , fColorSpace(std::move(colorSpace))
    382         , fAddedRasterVersionToCache(false) {
    383     }
    384 
    385     ~SkSpecialImage_Gpu() override {
    386         if (fAddedRasterVersionToCache.load()) {
    387             SkNotifyBitmapGenIDIsStale(this->uniqueID());
    388         }
    389     }
    390 
    391     SkAlphaType alphaType() const override { return fAlphaType; }
    392 
    393     size_t getSize() const override { return fTextureProxy->gpuMemorySize(); }
    394 
    395     void onDraw(SkCanvas* canvas, SkScalar x, SkScalar y, const SkPaint* paint) const override {
    396         SkRect dst = SkRect::MakeXYWH(x, y,
    397                                       this->subset().width(), this->subset().height());
    398 
    399         // TODO: In this instance we know we're going to draw a sub-portion of the backing
    400         // texture into the canvas so it is okay to wrap it in an SkImage. This poses
    401         // some problems for full deferral however in that when the deferred SkImage_Gpu
    402         // instantiates itself it is going to have to either be okay with having a larger
    403         // than expected backing texture (unlikely) or the 'fit' of the SurfaceProxy needs
    404         // to be tightened (if it is deferred).
    405         sk_sp<SkImage> img =
    406                 sk_sp<SkImage>(new SkImage_Gpu(sk_ref_sp(canvas->getGrContext()), this->uniqueID(),
    407                                                fAlphaType, fTextureProxy, fColorSpace));
    408 
    409         canvas->drawImageRect(img, this->subset(),
    410                               dst, paint, SkCanvas::kStrict_SrcRectConstraint);
    411     }
    412 
    413     GrRecordingContext* onGetContext() const override { return fContext; }
    414 
    415     sk_sp<GrTextureProxy> onAsTextureProxyRef(GrRecordingContext*) const override {
    416         return fTextureProxy;
    417     }
    418 
    419     bool onGetROPixels(SkBitmap* dst) const override {
    420         const auto desc = SkBitmapCacheDesc::Make(this->uniqueID(), this->subset());
    421         if (SkBitmapCache::Find(desc, dst)) {
    422             SkASSERT(dst->getGenerationID() == this->uniqueID());
    423             SkASSERT(dst->isImmutable());
    424             SkASSERT(dst->getPixels());
    425             return true;
    426         }
    427 
    428         SkPixmap pmap;
    429         SkImageInfo info = SkImageInfo::MakeN32(this->width(), this->height(),
    430                                                 this->alphaType(), fColorSpace);
    431         auto rec = SkBitmapCache::Alloc(desc, info, &pmap);
    432         if (!rec) {
    433             return false;
    434         }
    435         sk_sp<GrSurfaceContext> sContext = fContext->priv().makeWrappedSurfaceContext(
    436                 fTextureProxy, fColorSpace);
    437         if (!sContext) {
    438             return false;
    439         }
    440 
    441         if (!sContext->readPixels(info, pmap.writable_addr(), pmap.rowBytes(),
    442                                   this->subset().left(), this->subset().top())) {
    443             return false;
    444         }
    445 
    446         SkBitmapCache::Add(std::move(rec), dst);
    447         fAddedRasterVersionToCache.store(true);
    448         return true;
    449     }
    450 
    451     SkColorSpace* onGetColorSpace() const override {
    452         return fColorSpace.get();
    453     }
    454 
    455     sk_sp<SkSpecialSurface> onMakeSurface(const SkImageFilter::OutputProperties& outProps,
    456                                           const SkISize& size, SkAlphaType at,
    457                                           const SkSurfaceProps* props) const override {
    458         if (!fContext) {
    459             return nullptr;
    460         }
    461 
    462         GrBackendFormat format =
    463             fContext->priv().caps()->getBackendFormatFromColorType(outProps.colorType());
    464 
    465         return SkSpecialSurface::MakeRenderTarget(
    466             fContext, format, size.width(), size.height(),
    467             SkColorType2GrPixelConfig(outProps.colorType()), sk_ref_sp(outProps.colorSpace()),
    468             props);
    469     }
    470 
    471     sk_sp<SkSpecialImage> onMakeSubset(const SkIRect& subset) const override {
    472         return SkSpecialImage::MakeDeferredFromGpu(fContext,
    473                                                    subset,
    474                                                    this->uniqueID(),
    475                                                    fTextureProxy,
    476                                                    fColorSpace,
    477                                                    &this->props(),
    478                                                    fAlphaType);
    479     }
    480 
    481     // TODO: move all the logic here into the subset-flavor GrSurfaceProxy::copy?
    482     sk_sp<SkImage> onAsImage(const SkIRect* subset) const override {
    483         if (subset) {
    484             // TODO: if this becomes a bottle neck we could base this logic on what the size
    485             // will be when it is finally instantiated - but that is more fraught.
    486             if (GrProxyProvider::IsFunctionallyExact(fTextureProxy.get()) &&
    487                 0 == subset->fLeft && 0 == subset->fTop &&
    488                 fTextureProxy->width() == subset->width() &&
    489                 fTextureProxy->height() == subset->height()) {
    490                 fTextureProxy->priv().exactify();
    491                 // The existing GrTexture is already tight so reuse it in the SkImage
    492                 return wrap_proxy_in_image(fContext, fTextureProxy, fAlphaType, fColorSpace);
    493             }
    494 
    495             sk_sp<GrTextureProxy> subsetProxy(
    496                     GrSurfaceProxy::Copy(fContext, fTextureProxy.get(), GrMipMapped::kNo, *subset,
    497                                          SkBackingFit::kExact, SkBudgeted::kYes));
    498             if (!subsetProxy) {
    499                 return nullptr;
    500             }
    501 
    502             SkASSERT(subsetProxy->priv().isExact());
    503             // MDB: this is acceptable (wrapping subsetProxy in an SkImage) bc Copy will
    504             // return a kExact-backed proxy
    505             return wrap_proxy_in_image(fContext, std::move(subsetProxy), fAlphaType, fColorSpace);
    506         }
    507 
    508         fTextureProxy->priv().exactify();
    509 
    510         return wrap_proxy_in_image(fContext, fTextureProxy, fAlphaType, fColorSpace);
    511     }
    512 
    513     sk_sp<SkSurface> onMakeTightSurface(const SkImageFilter::OutputProperties& outProps,
    514                                         const SkISize& size, SkAlphaType at) const override {
    515         SkColorSpace* colorSpace = outProps.colorSpace();
    516         SkColorType colorType = colorSpace && colorSpace->gammaIsLinear()
    517             ? kRGBA_F16_SkColorType : kRGBA_8888_SkColorType;
    518         SkImageInfo info = SkImageInfo::Make(size.width(), size.height(), colorType, at,
    519                                              sk_ref_sp(colorSpace));
    520         // CONTEXT TODO: remove this use of 'backdoor' to create an SkSurface
    521         return SkSurface::MakeRenderTarget(fContext->priv().backdoor(), SkBudgeted::kYes, info);
    522     }
    523 
    524 private:
    525     GrRecordingContext*       fContext;
    526     sk_sp<GrTextureProxy>     fTextureProxy;
    527     const SkAlphaType         fAlphaType;
    528     sk_sp<SkColorSpace>       fColorSpace;
    529     mutable std::atomic<bool> fAddedRasterVersionToCache;
    530 
    531     typedef SkSpecialImage_Base INHERITED;
    532 };
    533 
    534 sk_sp<SkSpecialImage> SkSpecialImage::MakeDeferredFromGpu(GrRecordingContext* context,
    535                                                           const SkIRect& subset,
    536                                                           uint32_t uniqueID,
    537                                                           sk_sp<GrTextureProxy> proxy,
    538                                                           sk_sp<SkColorSpace> colorSpace,
    539                                                           const SkSurfaceProps* props,
    540                                                           SkAlphaType at) {
    541     if (!context || context->priv().abandoned() || !proxy) {
    542         return nullptr;
    543     }
    544     SkASSERT_RELEASE(rect_fits(subset, proxy->width(), proxy->height()));
    545     return sk_make_sp<SkSpecialImage_Gpu>(context, subset, uniqueID, std::move(proxy), at,
    546                                           std::move(colorSpace), props);
    547 }
    548 #endif
    549