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 "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.
     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:
     40     SkSpecialImage_Base(const SkIRect& subset, uint32_t uniqueID, const SkSurfaceProps* props)
     41         : INHERITED(subset, uniqueID, props) {
     42     }
     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 
     49     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 ///////////////////////////////////////////////////////////////////////////////
     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(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 
    129 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 
    133 bool SkSpecialImage::getROPixels(SkBitmap* bm) const {
    134     return as_SIB(this)->onGetROPixels(bm);
    135 }
    136 
    137 bool SkSpecialImage::isTextureBacked() const {
    138     return SkToBool(as_SIB(this)->onGetContext());
    139 }
    140 
    141 GrContext* SkSpecialImage::getContext() const {
    142     return as_SIB(this)->onGetContext();
    143 }
    144 
    145 SkColorSpace* SkSpecialImage::getColorSpace() const {
    146     return as_SIB(this)->onGetColorSpace();
    147 }
    148 
    149 #if SK_SUPPORT_GPU
    150 sk_sp<GrTextureProxy> SkSpecialImage::asTextureProxyRef(GrContext* context) const {
    151     return as_SIB(this)->onAsTextureProxyRef(context);
    152 }
    153 #endif
    154 
    155 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 
    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 
    174 #ifdef SK_DEBUG
    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 
    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:
    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 
    228     SkAlphaType alphaType() const override { return fBitmap.alphaType(); }
    229 
    230     size_t getSize() const override { return fBitmap.getSize(); }
    231 
    232     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 
    240     bool onGetROPixels(SkBitmap* bm) const override {
    241         *bm = fBitmap;
    242         return true;
    243     }
    244 
    245     SkColorSpace* onGetColorSpace() const override {
    246         return fBitmap.colorSpace();
    247     }
    248 
    249 #if SK_SUPPORT_GPU
    250     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 
    264     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 
    278     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 
    290     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 
    304     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 
    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 
    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:
    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 
    368     ~SkSpecialImage_Gpu() override {
    369         if (fAddedRasterVersionToCache.load()) {
    370             SkNotifyBitmapGenIDIsStale(this->uniqueID());
    371         }
    372     }
    373 
    374     SkAlphaType alphaType() const override { return fAlphaType; }
    375 
    376     size_t getSize() const override { return fTextureProxy->gpuMemorySize(); }
    377 
    378     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 
    397     GrContext* onGetContext() const override { return fContext; }
    398 
    399     sk_sp<GrTextureProxy> onAsTextureProxyRef(GrContext*) const override {
    400         return fTextureProxy;
    401     }
    402 
    403     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 
    436     SkColorSpace* onGetColorSpace() const override {
    437         return fColorSpace.get();
    438     }
    439 
    440     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 
    452     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?
    463     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 
    492     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 
    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