2 * Copyright 2016 Google Inc.
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file
8 #include "SkSpecialImage.h"
11 #include "SkBitmapCache.h"
13 #include "SkImage_Base.h"
14 #include "SkSpecialSurface.h"
15 #include "SkSurfacePriv.h"
16 #include "SkPixelRef.h"
19 #include "GrContext.h"
20 #include "GrTexture.h"
21 #include "GrSamplerParams.h"
22 #include "GrTextureProxy.h"
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;
34 ///////////////////////////////////////////////////////////////////////////////
35 class SkSpecialImage_Base : public SkSpecialImage {
37 SkSpecialImage_Base(const SkIRect& subset, uint32_t uniqueID, const SkSurfaceProps* props)
38 : INHERITED(subset, uniqueID, props) {
40 ~SkSpecialImage_Base() override { }
42 virtual void onDraw(SkCanvas*, SkScalar x, SkScalar y, const SkPaint*) const = 0;
44 virtual bool onGetROPixels(SkBitmap*) const = 0;
46 virtual GrContext* onGetContext() const { return nullptr; }
48 virtual SkColorSpace* onGetColorSpace() const = 0;
51 virtual sk_sp<GrTexture> onAsTextureRef(GrContext* context) const = 0;
52 virtual sk_sp<GrTextureProxy> onAsTextureProxy(GrContext* context) const = 0;
55 virtual sk_sp<SkSpecialImage> onMakeSubset(const SkIRect& subset) const = 0;
57 virtual sk_sp<SkSpecialSurface> onMakeSurface(const SkImageFilter::OutputProperties& outProps,
58 const SkISize& size, SkAlphaType at) const = 0;
60 virtual sk_sp<SkImage> onMakeTightSubset(const SkIRect& subset) const = 0;
62 virtual sk_sp<SkSurface> onMakeTightSurface(const SkImageFilter::OutputProperties& outProps,
63 const SkISize& size, SkAlphaType at) const = 0;
66 typedef SkSpecialImage INHERITED;
69 ///////////////////////////////////////////////////////////////////////////////
70 static inline const SkSpecialImage_Base* as_SIB(const SkSpecialImage* image) {
71 return static_cast<const SkSpecialImage_Base*>(image);
74 SkSpecialImage::SkSpecialImage(const SkIRect& subset,
76 const SkSurfaceProps* props)
77 : fProps(SkSurfacePropsCopyOrDefault(props))
79 , fUniqueID(kNeedNewImageUniqueID_SpecialImage == uniqueID ? SkNextID::ImageID() : uniqueID) {
82 sk_sp<SkSpecialImage> SkSpecialImage::makeTextureImage(GrContext* context) {
87 if (GrContext* curContext = as_SIB(this)->onGetContext()) {
88 return curContext == context ? sk_sp<SkSpecialImage>(SkRef(this)) : nullptr;
92 // At this point, we are definitely not texture-backed, so we must be raster or generator
93 // backed. If we remove the special-wrapping-an-image subclass, we may be able to assert that
94 // we are strictly raster-backed (i.e. generator images become raster when they are specialized)
95 // in which case getROPixels could turn into peekPixels...
96 if (!this->getROPixels(&bmp)) {
101 return SkSpecialImage::MakeFromRaster(SkIRect::MakeEmpty(), bmp, &this->props());
104 sk_sp<GrTexture> resultTex(
105 GrRefCachedBitmapTexture(context, bmp, GrSamplerParams::ClampNoFilter()));
110 return SkSpecialImage::MakeFromGpu(SkIRect::MakeWH(resultTex->width(), resultTex->height()),
112 resultTex, sk_ref_sp(this->getColorSpace()), &this->props(),
119 void SkSpecialImage::draw(SkCanvas* canvas, SkScalar x, SkScalar y, const SkPaint* paint) const {
120 return as_SIB(this)->onDraw(canvas, x, y, paint);
123 bool SkSpecialImage::getROPixels(SkBitmap* bm) const {
124 return as_SIB(this)->onGetROPixels(bm);
127 bool SkSpecialImage::isTextureBacked() const {
128 return SkToBool(as_SIB(this)->onGetContext());
131 GrContext* SkSpecialImage::getContext() const {
132 return as_SIB(this)->onGetContext();
135 SkColorSpace* SkSpecialImage::getColorSpace() const {
136 return as_SIB(this)->onGetColorSpace();
140 sk_sp<GrTexture> SkSpecialImage::asTextureRef(GrContext* context) const {
141 return as_SIB(this)->onAsTextureRef(context);
144 sk_sp<GrTextureProxy> SkSpecialImage::asTextureProxy(GrContext* context) const {
145 return as_SIB(this)->onAsTextureProxy(context);
149 sk_sp<SkSpecialSurface> SkSpecialImage::makeSurface(const SkImageFilter::OutputProperties& outProps,
150 const SkISize& size, SkAlphaType at) const {
151 return as_SIB(this)->onMakeSurface(outProps, size, at);
154 sk_sp<SkSurface> SkSpecialImage::makeTightSurface(const SkImageFilter::OutputProperties& outProps,
155 const SkISize& size, SkAlphaType at) const {
156 return as_SIB(this)->onMakeTightSurface(outProps, size, at);
159 sk_sp<SkSpecialImage> SkSpecialImage::makeSubset(const SkIRect& subset) const {
160 return as_SIB(this)->onMakeSubset(subset);
163 sk_sp<SkImage> SkSpecialImage::makeTightSubset(const SkIRect& subset) const {
164 return as_SIB(this)->onMakeTightSubset(subset);
168 static bool rect_fits(const SkIRect& rect, int width, int height) {
169 if (0 == width && 0 == height) {
170 SkASSERT(0 == rect.fLeft && 0 == rect.fRight && 0 == rect.fTop && 0 == rect.fBottom);
174 return rect.fLeft >= 0 && rect.fLeft < width && rect.fLeft < rect.fRight &&
175 rect.fRight >= 0 && rect.fRight <= width &&
176 rect.fTop >= 0 && rect.fTop < height && rect.fTop < rect.fBottom &&
177 rect.fBottom >= 0 && rect.fBottom <= height;
181 sk_sp<SkSpecialImage> SkSpecialImage::MakeFromImage(const SkIRect& subset,
182 sk_sp<SkImage> image,
183 SkColorSpace* dstColorSpace,
184 const SkSurfaceProps* props) {
185 SkASSERT(rect_fits(subset, image->width(), image->height()));
188 if (GrTexture* texture = as_IB(image)->peekTexture()) {
189 return MakeFromGpu(subset, image->uniqueID(), sk_ref_sp(texture),
190 sk_ref_sp(as_IB(image)->onImageInfo().colorSpace()), props);
195 if (as_IB(image)->getROPixels(&bm, dstColorSpace)) {
196 return MakeFromRaster(subset, bm, props);
202 ///////////////////////////////////////////////////////////////////////////////
204 class SkSpecialImage_Raster : public SkSpecialImage_Base {
206 SkSpecialImage_Raster(const SkIRect& subset, const SkBitmap& bm, const SkSurfaceProps* props)
207 : INHERITED(subset, bm.getGenerationID(), props)
210 SkASSERT(bm.pixelRef());
212 // We have to lock now, while bm is still in scope, since it may have come from our
213 // cache, which means we need to keep it locked until we (the special) are done, since
214 // we cannot re-generate the cache entry (if bm came from a generator).
215 fBitmap.lockPixels();
216 SkASSERT(fBitmap.getPixels());
219 SkAlphaType alphaType() const override { return fBitmap.alphaType(); }
221 size_t getSize() const override { return fBitmap.getSize(); }
223 void onDraw(SkCanvas* canvas, SkScalar x, SkScalar y, const SkPaint* paint) const override {
224 SkRect dst = SkRect::MakeXYWH(x, y,
225 this->subset().width(), this->subset().height());
227 canvas->drawBitmapRect(fBitmap, this->subset(),
228 dst, paint, SkCanvas::kStrict_SrcRectConstraint);
231 bool onGetROPixels(SkBitmap* bm) const override {
236 SkColorSpace* onGetColorSpace() const override {
237 return fBitmap.colorSpace();
241 sk_sp<GrTexture> onAsTextureRef(GrContext* context) const override {
244 GrRefCachedBitmapTexture(context, fBitmap, GrSamplerParams::ClampNoFilter()));
250 sk_sp<GrTextureProxy> onAsTextureProxy(GrContext* context) const override {
252 sk_sp<GrTexture> tex(sk_ref_sp(GrRefCachedBitmapTexture(
253 context, fBitmap, GrSamplerParams::ClampNoFilter())));
254 sk_sp<GrSurfaceProxy> sProxy = GrSurfaceProxy::MakeWrapped(std::move(tex));
255 return sk_ref_sp(sProxy->asTextureProxy());
262 // TODO: The raster implementations of image filters all currently assume that the pixels are
263 // legacy N32. Until they actually check the format and operate on sRGB or F16 data appropriately,
264 // we can't enable this. (They will continue to produce incorrect results, but less-so).
265 #define RASTER_IMAGE_FILTERS_SUPPORT_SRGB_AND_F16 0
267 sk_sp<SkSpecialSurface> onMakeSurface(const SkImageFilter::OutputProperties& outProps,
268 const SkISize& size, SkAlphaType at) const override {
269 #if RASTER_IMAGE_FILTERS_SUPPORT_SRGB_AND_F16
270 SkColorSpace* colorSpace = outProps.colorSpace();
272 SkColorSpace* colorSpace = nullptr;
274 SkColorType colorType = colorSpace && colorSpace->gammaIsLinear()
275 ? kRGBA_F16_SkColorType : kN32_SkColorType;
276 SkImageInfo info = SkImageInfo::Make(size.width(), size.height(), colorType, at,
277 sk_ref_sp(colorSpace));
278 return SkSpecialSurface::MakeRaster(info, nullptr);
281 sk_sp<SkSpecialImage> onMakeSubset(const SkIRect& subset) const override {
284 if (!fBitmap.extractSubset(&subsetBM, subset)) {
288 return SkSpecialImage::MakeFromRaster(SkIRect::MakeWH(subset.width(), subset.height()),
293 sk_sp<SkImage> onMakeTightSubset(const SkIRect& subset) const override {
296 if (!fBitmap.extractSubset(&subsetBM, subset)) {
300 return SkImage::MakeFromBitmap(subsetBM);
303 sk_sp<SkSurface> onMakeTightSurface(const SkImageFilter::OutputProperties& outProps,
304 const SkISize& size, SkAlphaType at) const override {
305 #if RASTER_IMAGE_FILTERS_SUPPORT_SRGB_AND_F16
306 SkColorSpace* colorSpace = outProps.colorSpace();
308 SkColorSpace* colorSpace = nullptr;
310 SkColorType colorType = colorSpace && colorSpace->gammaIsLinear()
311 ? kRGBA_F16_SkColorType : kN32_SkColorType;
312 SkImageInfo info = SkImageInfo::Make(size.width(), size.height(), colorType, at,
313 sk_ref_sp(colorSpace));
314 return SkSurface::MakeRaster(info);
320 typedef SkSpecialImage_Base INHERITED;
323 sk_sp<SkSpecialImage> SkSpecialImage::MakeFromRaster(const SkIRect& subset,
325 const SkSurfaceProps* props) {
326 SkASSERT(rect_fits(subset, bm.width(), bm.height()));
328 if (!bm.pixelRef()) {
332 const SkBitmap* srcBM = &bm;
334 // ImageFilters only handle N32 at the moment, so force our src to be that
335 if (!valid_for_imagefilters(bm.info())) {
336 if (!bm.copyTo(&tmpStorage, kN32_SkColorType)) {
341 return sk_make_sp<SkSpecialImage_Raster>(subset, *srcBM, props);
345 ///////////////////////////////////////////////////////////////////////////////
346 #include "GrTexture.h"
347 #include "SkImage_Gpu.h"
349 class SkSpecialImage_Gpu : public SkSpecialImage_Base {
351 SkSpecialImage_Gpu(const SkIRect& subset,
352 uint32_t uniqueID, sk_sp<GrTexture> tex, SkAlphaType at,
353 sk_sp<SkColorSpace> colorSpace, const SkSurfaceProps* props)
354 : INHERITED(subset, uniqueID, props)
355 , fContext(tex->getContext())
357 , fColorSpace(std::move(colorSpace))
358 , fAddedRasterVersionToCache(false) {
359 fSurfaceProxy = GrSurfaceProxy::MakeWrapped(std::move(tex));
362 SkSpecialImage_Gpu(GrContext* context, const SkIRect& subset,
363 uint32_t uniqueID, sk_sp<GrSurfaceProxy> proxy, SkAlphaType at,
364 sk_sp<SkColorSpace> colorSpace, const SkSurfaceProps* props)
365 : INHERITED(subset, uniqueID, props)
367 , fSurfaceProxy(std::move(proxy))
369 , fColorSpace(std::move(colorSpace))
370 , fAddedRasterVersionToCache(false) {
373 ~SkSpecialImage_Gpu() override {
374 if (fAddedRasterVersionToCache.load()) {
375 SkNotifyBitmapGenIDIsStale(this->uniqueID());
379 SkAlphaType alphaType() const override { return fAlphaType; }
381 size_t getSize() const override { return fSurfaceProxy->gpuMemorySize(); }
383 void onDraw(SkCanvas* canvas, SkScalar x, SkScalar y, const SkPaint* paint) const override {
384 SkRect dst = SkRect::MakeXYWH(x, y,
385 this->subset().width(), this->subset().height());
387 // TODO: add GrTextureProxy-backed SkImage_Gpus
388 GrSurface* surf = fSurfaceProxy->instantiate(fContext->textureProvider());
393 // TODO: In this instance we know we're going to draw a sub-portion of the backing
394 // texture into the canvas so it is okay to wrap it in an SkImage. This poses
395 // some problems for full deferral however in that when the deferred SkImage_Gpu
396 // instantiates itself it is going to have to either be okay with having a larger
397 // than expected backing texture (unlikely) or the 'fit' of the SurfaceProxy needs
398 // to be tightened (if it is deferred).
399 auto img = sk_sp<SkImage>(new SkImage_Gpu(surf->width(), surf->height(),
400 this->uniqueID(), fAlphaType,
401 sk_ref_sp(surf->asTexture()),
402 fColorSpace, SkBudgeted::kNo));
404 canvas->drawImageRect(img, this->subset(),
405 dst, paint, SkCanvas::kStrict_SrcRectConstraint);
408 GrContext* onGetContext() const override { return fContext; }
410 // This entry point should go away in favor of asTextureProxy
411 sk_sp<GrTexture> onAsTextureRef(GrContext* context) const override {
412 GrSurface* surf = fSurfaceProxy->instantiate(context->textureProvider());
416 return sk_ref_sp(surf->asTexture());
419 sk_sp<GrTextureProxy> onAsTextureProxy(GrContext*) const override {
420 return sk_ref_sp(fSurfaceProxy->asTextureProxy());
423 bool onGetROPixels(SkBitmap* dst) const override {
424 if (SkBitmapCache::Find(this->uniqueID(), dst)) {
425 SkASSERT(dst->getGenerationID() == this->uniqueID());
426 SkASSERT(dst->isImmutable());
427 SkASSERT(dst->getPixels());
431 SkImageInfo info = SkImageInfo::MakeN32(this->width(), this->height(),
432 this->alphaType(), fColorSpace);
434 if (!dst->tryAllocPixels(info)) {
438 // Reading back to an SkBitmap ends deferral
439 GrSurface* surface = fSurfaceProxy->instantiate(fContext->textureProvider());
444 if (!surface->readPixels(0, 0, dst->width(), dst->height(), kSkia8888_GrPixelConfig,
445 dst->getPixels(), dst->rowBytes())) {
449 dst->pixelRef()->setImmutableWithID(this->uniqueID());
450 SkBitmapCache::Add(this->uniqueID(), *dst);
451 fAddedRasterVersionToCache.store(true);
455 SkColorSpace* onGetColorSpace() const override {
456 return fColorSpace.get();
459 sk_sp<SkSpecialSurface> onMakeSurface(const SkImageFilter::OutputProperties& outProps,
460 const SkISize& size, SkAlphaType at) const override {
465 SkColorSpace* colorSpace = outProps.colorSpace();
466 return SkSpecialSurface::MakeRenderTarget(
467 fContext, size.width(), size.height(),
468 GrRenderableConfigForColorSpace(colorSpace), sk_ref_sp(colorSpace));
471 sk_sp<SkSpecialImage> onMakeSubset(const SkIRect& subset) const override {
472 return SkSpecialImage::MakeDeferredFromGpu(fContext,
481 sk_sp<SkImage> onMakeTightSubset(const SkIRect& subset) const override {
482 // TODO: add GrTextureProxy-backed SkImage_Gpus
483 GrSurface* surf = fSurfaceProxy->instantiate(fContext->textureProvider());
488 if (0 == subset.fLeft && 0 == subset.fTop &&
489 fSurfaceProxy->width() == subset.width() &&
490 fSurfaceProxy->height() == subset.height()) {
491 // The existing GrTexture is already tight so reuse it in the SkImage
492 return sk_make_sp<SkImage_Gpu>(surf->width(), surf->height(),
493 kNeedNewImageUniqueID, fAlphaType,
494 sk_ref_sp(surf->asTexture()),
495 fColorSpace, SkBudgeted::kYes);
498 GrSurfaceDesc desc = fSurfaceProxy->desc();
499 desc.fWidth = subset.width();
500 desc.fHeight = subset.height();
502 sk_sp<GrTexture> subTx(fContext->textureProvider()->createTexture(desc, SkBudgeted::kYes));
506 fContext->copySurface(subTx.get(), surf, subset, SkIPoint::Make(0, 0));
507 return sk_make_sp<SkImage_Gpu>(desc.fWidth, desc.fHeight, kNeedNewImageUniqueID,
508 fAlphaType, std::move(subTx), fColorSpace, SkBudgeted::kYes);
511 sk_sp<SkSurface> onMakeTightSurface(const SkImageFilter::OutputProperties& outProps,
512 const SkISize& size, SkAlphaType at) const override {
513 SkColorSpace* colorSpace = outProps.colorSpace();
514 SkColorType colorType = colorSpace && colorSpace->gammaIsLinear()
515 ? kRGBA_F16_SkColorType : kRGBA_8888_SkColorType;
516 SkImageInfo info = SkImageInfo::Make(size.width(), size.height(), colorType, at,
517 sk_ref_sp(colorSpace));
518 return SkSurface::MakeRenderTarget(fContext, SkBudgeted::kYes, info);
523 sk_sp<GrSurfaceProxy> fSurfaceProxy;
524 const SkAlphaType fAlphaType;
525 sk_sp<SkColorSpace> fColorSpace;
526 mutable SkAtomic<bool> fAddedRasterVersionToCache;
528 typedef SkSpecialImage_Base INHERITED;
531 sk_sp<SkSpecialImage> SkSpecialImage::MakeFromGpu(const SkIRect& subset,
533 sk_sp<GrTexture> tex,
534 sk_sp<SkColorSpace> colorSpace,
535 const SkSurfaceProps* props,
537 SkASSERT(rect_fits(subset, tex->width(), tex->height()));
538 return sk_make_sp<SkSpecialImage_Gpu>(subset, uniqueID, std::move(tex), at,
539 std::move(colorSpace), props);
542 sk_sp<SkSpecialImage> SkSpecialImage::MakeDeferredFromGpu(GrContext* context,
543 const SkIRect& subset,
545 sk_sp<GrSurfaceProxy> proxy,
546 sk_sp<SkColorSpace> colorSpace,
547 const SkSurfaceProps* props,
549 SkASSERT(rect_fits(subset, proxy->width(), proxy->height()));
550 return sk_make_sp<SkSpecialImage_Gpu>(context, subset, uniqueID, std::move(proxy), at,
551 std::move(colorSpace), props);