123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545 |
- /*
- * Copyright 2016 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file
- */
- #include "include/core/SkBitmap.h"
- #include "include/core/SkCanvas.h"
- #include "include/core/SkImage.h"
- #include "src/core/SkBitmapCache.h"
- #include "src/core/SkSpecialImage.h"
- #include "src/core/SkSpecialSurface.h"
- #include "src/core/SkSurfacePriv.h"
- #include "src/image/SkImage_Base.h"
- #include <atomic>
- #if SK_SUPPORT_GPU
- #include "include/gpu/GrContext.h"
- #include "include/private/GrRecordingContext.h"
- #include "src/gpu/GrContextPriv.h"
- #include "src/gpu/GrProxyProvider.h"
- #include "src/gpu/GrRecordingContextPriv.h"
- #include "src/gpu/GrSurfaceContext.h"
- #include "src/gpu/GrTextureProxy.h"
- #include "src/image/SkImage_Gpu.h"
- #endif
- // Currently the raster imagefilters can only handle certain imageinfos. Call this to know if
- // a given info is supported.
- static bool valid_for_imagefilters(const SkImageInfo& info) {
- // no support for other swizzles/depths yet
- return info.colorType() == kN32_SkColorType;
- }
- ///////////////////////////////////////////////////////////////////////////////
- class SkSpecialImage_Base : public SkSpecialImage {
- public:
- SkSpecialImage_Base(const SkIRect& subset, uint32_t uniqueID, const SkSurfaceProps* props)
- : INHERITED(subset, uniqueID, props) {
- }
- ~SkSpecialImage_Base() override { }
- virtual void onDraw(SkCanvas*, SkScalar x, SkScalar y, const SkPaint*) const = 0;
- virtual bool onGetROPixels(SkBitmap*) const = 0;
- virtual GrRecordingContext* onGetContext() const { return nullptr; }
- virtual SkColorSpace* onGetColorSpace() const = 0;
- #if SK_SUPPORT_GPU
- virtual sk_sp<GrTextureProxy> onAsTextureProxyRef(GrRecordingContext* context) const = 0;
- #endif
- // This subset is relative to the backing store's coordinate frame, it has already been mapped
- // from the content rect by the non-virtual makeSubset().
- virtual sk_sp<SkSpecialImage> onMakeSubset(const SkIRect& subset) const = 0;
- virtual sk_sp<SkSpecialSurface> onMakeSurface(const SkImageFilter::OutputProperties& outProps,
- const SkISize& size, SkAlphaType at,
- const SkSurfaceProps* = nullptr) const = 0;
- // This subset (when not null) is relative to the backing store's coordinate frame, it has
- // already been mapped from the content rect by the non-virtual asImage().
- virtual sk_sp<SkImage> onAsImage(const SkIRect* subset) const = 0;
- virtual sk_sp<SkSurface> onMakeTightSurface(const SkImageFilter::OutputProperties& outProps,
- const SkISize& size, SkAlphaType at) const = 0;
- private:
- typedef SkSpecialImage INHERITED;
- };
- ///////////////////////////////////////////////////////////////////////////////
- static inline const SkSpecialImage_Base* as_SIB(const SkSpecialImage* image) {
- return static_cast<const SkSpecialImage_Base*>(image);
- }
- SkSpecialImage::SkSpecialImage(const SkIRect& subset,
- uint32_t uniqueID,
- const SkSurfaceProps* props)
- : fProps(SkSurfacePropsCopyOrDefault(props))
- , fSubset(subset)
- , fUniqueID(kNeedNewImageUniqueID_SpecialImage == uniqueID ? SkNextID::ImageID() : uniqueID) {
- }
- sk_sp<SkSpecialImage> SkSpecialImage::makeTextureImage(GrRecordingContext* context) {
- #if SK_SUPPORT_GPU
- if (!context) {
- return nullptr;
- }
- if (GrRecordingContext* curContext = as_SIB(this)->onGetContext()) {
- return curContext->priv().matches(context) ? sk_sp<SkSpecialImage>(SkRef(this)) : nullptr;
- }
- auto proxyProvider = context->priv().proxyProvider();
- SkBitmap bmp;
- // At this point, we are definitely not texture-backed, so we must be raster or generator
- // backed. If we remove the special-wrapping-an-image subclass, we may be able to assert that
- // we are strictly raster-backed (i.e. generator images become raster when they are specialized)
- // in which case getROPixels could turn into peekPixels...
- if (!this->getROPixels(&bmp)) {
- return nullptr;
- }
- if (bmp.empty()) {
- return SkSpecialImage::MakeFromRaster(SkIRect::MakeEmpty(), bmp, &this->props());
- }
- // TODO: this is a tight copy of 'bmp' but it doesn't have to be (given SkSpecialImage's
- // semantics). Since this is cached though we would have to bake the fit into the cache key.
- sk_sp<GrTextureProxy> proxy = GrMakeCachedBitmapProxy(proxyProvider, bmp);
- if (!proxy) {
- return nullptr;
- }
- const SkIRect rect = SkIRect::MakeWH(proxy->width(), proxy->height());
- // GrMakeCachedBitmapProxy has uploaded only the specified subset of 'bmp' so we need not
- // bother with SkBitmap::getSubset
- return SkSpecialImage::MakeDeferredFromGpu(context,
- rect,
- this->uniqueID(),
- std::move(proxy),
- sk_ref_sp(this->getColorSpace()),
- &this->props(),
- this->alphaType());
- #else
- return nullptr;
- #endif
- }
- void SkSpecialImage::draw(SkCanvas* canvas, SkScalar x, SkScalar y, const SkPaint* paint) const {
- return as_SIB(this)->onDraw(canvas, x, y, paint);
- }
- bool SkSpecialImage::getROPixels(SkBitmap* bm) const {
- return as_SIB(this)->onGetROPixels(bm);
- }
- bool SkSpecialImage::isTextureBacked() const {
- return SkToBool(as_SIB(this)->onGetContext());
- }
- GrRecordingContext* SkSpecialImage::getContext() const {
- return as_SIB(this)->onGetContext();
- }
- SkColorSpace* SkSpecialImage::getColorSpace() const {
- return as_SIB(this)->onGetColorSpace();
- }
- #if SK_SUPPORT_GPU
- sk_sp<GrTextureProxy> SkSpecialImage::asTextureProxyRef(GrRecordingContext* context) const {
- return as_SIB(this)->onAsTextureProxyRef(context);
- }
- #endif
- sk_sp<SkSpecialSurface> SkSpecialImage::makeSurface(const SkImageFilter::OutputProperties& outProps,
- const SkISize& size, SkAlphaType at,
- const SkSurfaceProps* props) const {
- return as_SIB(this)->onMakeSurface(outProps, size, at, props);
- }
- sk_sp<SkSurface> SkSpecialImage::makeTightSurface(const SkImageFilter::OutputProperties& outProps,
- const SkISize& size, SkAlphaType at) const {
- return as_SIB(this)->onMakeTightSurface(outProps, size, at);
- }
- sk_sp<SkSpecialImage> SkSpecialImage::makeSubset(const SkIRect& subset) const {
- SkIRect absolute = subset.makeOffset(this->subset().x(), this->subset().y());
- return as_SIB(this)->onMakeSubset(absolute);
- }
- sk_sp<SkImage> SkSpecialImage::asImage(const SkIRect* subset) const {
- if (subset) {
- SkIRect absolute = subset->makeOffset(this->subset().x(), this->subset().y());
- return as_SIB(this)->onAsImage(&absolute);
- } else {
- return as_SIB(this)->onAsImage(nullptr);
- }
- }
- #if defined(SK_DEBUG) || SK_SUPPORT_GPU
- static bool rect_fits(const SkIRect& rect, int width, int height) {
- if (0 == width && 0 == height) {
- SkASSERT(0 == rect.fLeft && 0 == rect.fRight && 0 == rect.fTop && 0 == rect.fBottom);
- return true;
- }
- return rect.fLeft >= 0 && rect.fLeft < width && rect.fLeft < rect.fRight &&
- rect.fRight >= 0 && rect.fRight <= width &&
- rect.fTop >= 0 && rect.fTop < height && rect.fTop < rect.fBottom &&
- rect.fBottom >= 0 && rect.fBottom <= height;
- }
- #endif
- sk_sp<SkSpecialImage> SkSpecialImage::MakeFromImage(GrRecordingContext* context,
- const SkIRect& subset,
- sk_sp<SkImage> image,
- const SkSurfaceProps* props) {
- SkASSERT(rect_fits(subset, image->width(), image->height()));
- #if SK_SUPPORT_GPU
- if (sk_sp<GrTextureProxy> proxy = as_IB(image)->asTextureProxyRef(context)) {
- if (!as_IB(image)->context()->priv().matches(context)) {
- return nullptr;
- }
- return MakeDeferredFromGpu(context, subset, image->uniqueID(), std::move(proxy),
- image->refColorSpace(), props);
- } else
- #endif
- {
- SkBitmap bm;
- if (as_IB(image)->getROPixels(&bm)) {
- return MakeFromRaster(subset, bm, props);
- }
- }
- return nullptr;
- }
- ///////////////////////////////////////////////////////////////////////////////
- class SkSpecialImage_Raster : public SkSpecialImage_Base {
- public:
- SkSpecialImage_Raster(const SkIRect& subset, const SkBitmap& bm, const SkSurfaceProps* props)
- : INHERITED(subset, bm.getGenerationID(), props)
- , fBitmap(bm)
- {
- SkASSERT(bm.pixelRef());
- SkASSERT(fBitmap.getPixels());
- }
- SkAlphaType alphaType() const override { return fBitmap.alphaType(); }
- size_t getSize() const override { return fBitmap.computeByteSize(); }
- void onDraw(SkCanvas* canvas, SkScalar x, SkScalar y, const SkPaint* paint) const override {
- SkRect dst = SkRect::MakeXYWH(x, y,
- this->subset().width(), this->subset().height());
- canvas->drawBitmapRect(fBitmap, this->subset(),
- dst, paint, SkCanvas::kStrict_SrcRectConstraint);
- }
- bool onGetROPixels(SkBitmap* bm) const override {
- return fBitmap.extractSubset(bm, this->subset());
- }
- SkColorSpace* onGetColorSpace() const override {
- return fBitmap.colorSpace();
- }
- #if SK_SUPPORT_GPU
- sk_sp<GrTextureProxy> onAsTextureProxyRef(GrRecordingContext* context) const override {
- if (context) {
- return GrMakeCachedBitmapProxy(context->priv().proxyProvider(), fBitmap);
- }
- return nullptr;
- }
- #endif
- sk_sp<SkSpecialSurface> onMakeSurface(const SkImageFilter::OutputProperties& outProps,
- const SkISize& size, SkAlphaType at,
- const SkSurfaceProps* props) const override {
- SkColorSpace* colorSpace = outProps.colorSpace();
- SkColorType colorType = kN32_SkColorType; // TODO: find ways to allow f16
- SkImageInfo info = SkImageInfo::Make(size.width(), size.height(), colorType, at,
- sk_ref_sp(colorSpace));
- return SkSpecialSurface::MakeRaster(info, props);
- }
- sk_sp<SkSpecialImage> onMakeSubset(const SkIRect& subset) const override {
- // No need to extract subset, onGetROPixels handles that when needed
- return SkSpecialImage::MakeFromRaster(subset, fBitmap, &this->props());
- }
- sk_sp<SkImage> onAsImage(const SkIRect* subset) const override {
- if (subset) {
- SkBitmap subsetBM;
- if (!fBitmap.extractSubset(&subsetBM, *subset)) {
- return nullptr;
- }
- return SkImage::MakeFromBitmap(subsetBM);
- }
- return SkImage::MakeFromBitmap(fBitmap);
- }
- sk_sp<SkSurface> onMakeTightSurface(const SkImageFilter::OutputProperties& outProps,
- const SkISize& size, SkAlphaType at) const override {
- SkColorSpace* colorSpace = outProps.colorSpace();
- SkColorType colorType = kN32_SkColorType; // TODO: find ways to allow f16
- SkImageInfo info = SkImageInfo::Make(size.width(), size.height(), colorType, at,
- sk_ref_sp(colorSpace));
- return SkSurface::MakeRaster(info);
- }
- private:
- SkBitmap fBitmap;
- typedef SkSpecialImage_Base INHERITED;
- };
- sk_sp<SkSpecialImage> SkSpecialImage::MakeFromRaster(const SkIRect& subset,
- const SkBitmap& bm,
- const SkSurfaceProps* props) {
- SkASSERT(rect_fits(subset, bm.width(), bm.height()));
- if (!bm.pixelRef()) {
- return nullptr;
- }
- const SkBitmap* srcBM = &bm;
- SkBitmap tmp;
- // ImageFilters only handle N32 at the moment, so force our src to be that
- if (!valid_for_imagefilters(bm.info())) {
- if (!tmp.tryAllocPixels(bm.info().makeColorType(kN32_SkColorType)) ||
- !bm.readPixels(tmp.info(), tmp.getPixels(), tmp.rowBytes(), 0, 0))
- {
- return nullptr;
- }
- srcBM = &tmp;
- }
- return sk_make_sp<SkSpecialImage_Raster>(subset, *srcBM, props);
- }
- sk_sp<SkSpecialImage> SkSpecialImage::CopyFromRaster(const SkIRect& subset,
- const SkBitmap& bm,
- const SkSurfaceProps* props) {
- SkASSERT(rect_fits(subset, bm.width(), bm.height()));
- if (!bm.pixelRef()) {
- return nullptr;
- }
- SkBitmap tmp;
- SkImageInfo info = bm.info().makeWH(subset.width(), subset.height());
- // As in MakeFromRaster, must force src to N32 for ImageFilters
- if (!valid_for_imagefilters(bm.info())) {
- info = info.makeColorType(kN32_SkColorType);
- }
- if (!tmp.tryAllocPixels(info)) {
- return nullptr;
- }
- if (!bm.readPixels(tmp.info(), tmp.getPixels(), tmp.rowBytes(), subset.x(), subset.y())) {
- return nullptr;
- }
- // Since we're making a copy of the raster, the resulting special image is the exact size
- // of the requested subset of the original and no longer needs to be offset by subset's left
- // and top, since those were relative to the original's buffer.
- return sk_make_sp<SkSpecialImage_Raster>(
- SkIRect::MakeWH(subset.width(), subset.height()), tmp, props);
- }
- #if SK_SUPPORT_GPU
- ///////////////////////////////////////////////////////////////////////////////
- static sk_sp<SkImage> wrap_proxy_in_image(GrRecordingContext* context, sk_sp<GrTextureProxy> proxy,
- SkAlphaType alphaType, sk_sp<SkColorSpace> colorSpace) {
- // CONTEXT TODO: remove this use of 'backdoor' to create an SkImage
- return sk_make_sp<SkImage_Gpu>(sk_ref_sp(context->priv().backdoor()),
- kNeedNewImageUniqueID, alphaType,
- std::move(proxy), std::move(colorSpace));
- }
- class SkSpecialImage_Gpu : public SkSpecialImage_Base {
- public:
- SkSpecialImage_Gpu(GrRecordingContext* context, const SkIRect& subset,
- uint32_t uniqueID, sk_sp<GrTextureProxy> proxy, SkAlphaType at,
- sk_sp<SkColorSpace> colorSpace, const SkSurfaceProps* props)
- : INHERITED(subset, uniqueID, props)
- , fContext(context)
- , fTextureProxy(std::move(proxy))
- , fAlphaType(at)
- , fColorSpace(std::move(colorSpace))
- , fAddedRasterVersionToCache(false) {
- }
- ~SkSpecialImage_Gpu() override {
- if (fAddedRasterVersionToCache.load()) {
- SkNotifyBitmapGenIDIsStale(this->uniqueID());
- }
- }
- SkAlphaType alphaType() const override { return fAlphaType; }
- size_t getSize() const override { return fTextureProxy->gpuMemorySize(); }
- void onDraw(SkCanvas* canvas, SkScalar x, SkScalar y, const SkPaint* paint) const override {
- SkRect dst = SkRect::MakeXYWH(x, y,
- this->subset().width(), this->subset().height());
- // TODO: In this instance we know we're going to draw a sub-portion of the backing
- // texture into the canvas so it is okay to wrap it in an SkImage. This poses
- // some problems for full deferral however in that when the deferred SkImage_Gpu
- // instantiates itself it is going to have to either be okay with having a larger
- // than expected backing texture (unlikely) or the 'fit' of the SurfaceProxy needs
- // to be tightened (if it is deferred).
- sk_sp<SkImage> img =
- sk_sp<SkImage>(new SkImage_Gpu(sk_ref_sp(canvas->getGrContext()), this->uniqueID(),
- fAlphaType, fTextureProxy, fColorSpace));
- canvas->drawImageRect(img, this->subset(),
- dst, paint, SkCanvas::kStrict_SrcRectConstraint);
- }
- GrRecordingContext* onGetContext() const override { return fContext; }
- sk_sp<GrTextureProxy> onAsTextureProxyRef(GrRecordingContext*) const override {
- return fTextureProxy;
- }
- bool onGetROPixels(SkBitmap* dst) const override {
- const auto desc = SkBitmapCacheDesc::Make(this->uniqueID(), this->subset());
- if (SkBitmapCache::Find(desc, dst)) {
- SkASSERT(dst->getGenerationID() == this->uniqueID());
- SkASSERT(dst->isImmutable());
- SkASSERT(dst->getPixels());
- return true;
- }
- SkPixmap pmap;
- SkImageInfo info = SkImageInfo::MakeN32(this->width(), this->height(),
- this->alphaType(), fColorSpace);
- auto rec = SkBitmapCache::Alloc(desc, info, &pmap);
- if (!rec) {
- return false;
- }
- sk_sp<GrSurfaceContext> sContext = fContext->priv().makeWrappedSurfaceContext(
- fTextureProxy, SkColorTypeToGrColorType(info.colorType()), this->alphaType(),
- fColorSpace);
- if (!sContext) {
- return false;
- }
- if (!sContext->readPixels(info, pmap.writable_addr(), pmap.rowBytes(),
- {this->subset().left(), this->subset().top()})) {
- return false;
- }
- SkBitmapCache::Add(std::move(rec), dst);
- fAddedRasterVersionToCache.store(true);
- return true;
- }
- SkColorSpace* onGetColorSpace() const override {
- return fColorSpace.get();
- }
- sk_sp<SkSpecialSurface> onMakeSurface(const SkImageFilter::OutputProperties& outProps,
- const SkISize& size, SkAlphaType at,
- const SkSurfaceProps* props) const override {
- if (!fContext) {
- return nullptr;
- }
- return SkSpecialSurface::MakeRenderTarget(fContext, size.width(), size.height(),
- SkColorTypeToGrColorType(outProps.colorType()),
- sk_ref_sp(outProps.colorSpace()), props);
- }
- sk_sp<SkSpecialImage> onMakeSubset(const SkIRect& subset) const override {
- return SkSpecialImage::MakeDeferredFromGpu(fContext,
- subset,
- this->uniqueID(),
- fTextureProxy,
- fColorSpace,
- &this->props(),
- fAlphaType);
- }
- // TODO: move all the logic here into the subset-flavor GrSurfaceProxy::copy?
- sk_sp<SkImage> onAsImage(const SkIRect* subset) const override {
- if (subset) {
- // TODO: if this becomes a bottle neck we could base this logic on what the size
- // will be when it is finally instantiated - but that is more fraught.
- if (GrProxyProvider::IsFunctionallyExact(fTextureProxy.get()) &&
- 0 == subset->fLeft && 0 == subset->fTop &&
- fTextureProxy->width() == subset->width() &&
- fTextureProxy->height() == subset->height()) {
- fTextureProxy->priv().exactify();
- // The existing GrTexture is already tight so reuse it in the SkImage
- return wrap_proxy_in_image(fContext, fTextureProxy, fAlphaType, fColorSpace);
- }
- sk_sp<GrTextureProxy> subsetProxy(
- GrSurfaceProxy::Copy(fContext, fTextureProxy.get(), GrMipMapped::kNo, *subset,
- SkBackingFit::kExact, SkBudgeted::kYes));
- if (!subsetProxy) {
- return nullptr;
- }
- SkASSERT(subsetProxy->priv().isExact());
- // MDB: this is acceptable (wrapping subsetProxy in an SkImage) bc Copy will
- // return a kExact-backed proxy
- return wrap_proxy_in_image(fContext, std::move(subsetProxy), fAlphaType, fColorSpace);
- }
- fTextureProxy->priv().exactify();
- return wrap_proxy_in_image(fContext, fTextureProxy, fAlphaType, fColorSpace);
- }
- sk_sp<SkSurface> onMakeTightSurface(const SkImageFilter::OutputProperties& outProps,
- const SkISize& size, SkAlphaType at) const override {
- SkColorSpace* colorSpace = outProps.colorSpace();
- SkColorType colorType = colorSpace && colorSpace->gammaIsLinear()
- ? kRGBA_F16_SkColorType : kRGBA_8888_SkColorType;
- SkImageInfo info = SkImageInfo::Make(size.width(), size.height(), colorType, at,
- sk_ref_sp(colorSpace));
- // CONTEXT TODO: remove this use of 'backdoor' to create an SkSurface
- return SkSurface::MakeRenderTarget(fContext->priv().backdoor(), SkBudgeted::kYes, info);
- }
- private:
- GrRecordingContext* fContext;
- sk_sp<GrTextureProxy> fTextureProxy;
- const SkAlphaType fAlphaType;
- sk_sp<SkColorSpace> fColorSpace;
- mutable std::atomic<bool> fAddedRasterVersionToCache;
- typedef SkSpecialImage_Base INHERITED;
- };
- sk_sp<SkSpecialImage> SkSpecialImage::MakeDeferredFromGpu(GrRecordingContext* context,
- const SkIRect& subset,
- uint32_t uniqueID,
- sk_sp<GrTextureProxy> proxy,
- sk_sp<SkColorSpace> colorSpace,
- const SkSurfaceProps* props,
- SkAlphaType at) {
- if (!context || context->priv().abandoned() || !proxy) {
- return nullptr;
- }
- SkASSERT_RELEASE(rect_fits(subset, proxy->width(), proxy->height()));
- return sk_make_sp<SkSpecialImage_Gpu>(context, subset, uniqueID, std::move(proxy), at,
- std::move(colorSpace), props);
- }
- #endif
|