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