From 041f7dfc577822f1e97721a865f86a9a02660cf9 Mon Sep 17 00:00:00 2001 From: Brian Osman Date: Tue, 7 Feb 2017 11:23:28 -0500 Subject: [PATCH] Bring back SkImage::makeTextureImage Ensures that an image is GPU backed on the passed-in GrContxt. The new version requires a destination color space (intended usage of the image), so we can make a proper decision about decoded format. This reverts commit d263413a2a92cafe3fd3b051c67d00206c9a0e4d. BUG=skia: Change-Id: Ibccddbafc301779559592045ed5a5fa9264e7432 Reviewed-on: https://skia-review.googlesource.com/8116 Commit-Queue: Brian Osman Reviewed-by: Brian Salomon --- gm/image.cpp | 81 +++++++++++++++++++++++++++++++++++++++++++++++ include/core/SkImage.h | 8 +++++ src/image/SkImage.cpp | 4 +++ src/image/SkImage_Gpu.cpp | 32 +++++++++++++++++++ tests/DeviceTest.cpp | 4 +-- tests/ImageTest.cpp | 68 ++++++++++++++++++++++++++++++++++++++- 6 files changed, 194 insertions(+), 3 deletions(-) diff --git a/gm/image.cpp b/gm/image.cpp index 595a5ef..1a4b712 100644 --- a/gm/image.cpp +++ b/gm/image.cpp @@ -427,3 +427,84 @@ private: typedef skiagm::GM INHERITED; }; DEF_GM( return new ScaleGeneratorGM; ) + +DEF_SIMPLE_GM(new_texture_image, canvas, 225, 60) { + GrContext* context = nullptr; +#if SK_SUPPORT_GPU + context = canvas->getGrContext(); +#endif + if (!context) { + skiagm::GM::DrawGpuOnlyMessage(canvas); + return; + } + + auto render_image = [](SkCanvas* canvas) { + canvas->clear(SK_ColorBLUE); + SkPaint paint; + paint.setColor(SK_ColorRED); + canvas->drawRect(SkRect::MakeXYWH(10.f,10.f,10.f,10.f), paint); + paint.setColor(SK_ColorGREEN); + canvas->drawRect(SkRect::MakeXYWH(30.f,10.f,10.f,10.f), paint); + paint.setColor(SK_ColorYELLOW); + canvas->drawRect(SkRect::MakeXYWH(10.f,30.f,10.f,10.f), paint); + paint.setColor(SK_ColorCYAN); + canvas->drawRect(SkRect::MakeXYWH(30.f,30.f,10.f,10.f), paint); + }; + + static constexpr int kSize = 50; + SkBitmap bmp; + bmp.allocPixels(SkImageInfo::MakeS32(kSize, kSize, kPremul_SkAlphaType)); + SkCanvas bmpCanvas(bmp); + render_image(&bmpCanvas); + + std::function()> imageFactories[] = { + // Create sw raster image. + [bmp] { + return SkImage::MakeFromBitmap(bmp); + }, + // Create encoded image. + [bmp] { + sk_sp src( + sk_tool_utils::EncodeImageToData(bmp, SkEncodedImageFormat::kPNG, 100)); + return SkImage::MakeFromEncoded(std::move(src)); + }, + // Create a picture image. + [render_image] { + SkPictureRecorder recorder; + SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(kSize), SkIntToScalar(kSize)); + render_image(canvas); + sk_sp srgbColorSpace = SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named); + return SkImage::MakeFromPicture(recorder.finishRecordingAsPicture(), + SkISize::Make(kSize, kSize), nullptr, nullptr, + SkImage::BitDepth::kU8, srgbColorSpace); + }, + // Create a texture image + [context, render_image]() -> sk_sp { + auto surface(SkSurface::MakeRenderTarget(context, SkBudgeted::kYes, + SkImageInfo::MakeS32(kSize, kSize, + kPremul_SkAlphaType))); + if (!surface) { + return nullptr; + } + render_image(surface->getCanvas()); + return surface->makeImageSnapshot(); + } + }; + + constexpr SkScalar kPad = 5.f; + canvas->translate(kPad, kPad); + for (auto factory : imageFactories) { + auto image(factory()); + if (!image) { + continue; + } + if (context) { + sk_sp texImage(image->makeTextureImage(context, + canvas->imageInfo().colorSpace())); + if (texImage) { + canvas->drawImage(texImage, 0, 0); + } + } + canvas->translate(image->width() + kPad, 0); + } +} diff --git a/include/core/SkImage.h b/include/core/SkImage.h index 2e421c9..532708a 100644 --- a/include/core/SkImage.h +++ b/include/core/SkImage.h @@ -323,6 +323,14 @@ public: sk_sp makeSubset(const SkIRect& subset) const; /** + * Ensures that an image is backed by a texture (when GrContext is non-null), suitable for use + * with surfaces that have the supplied destination color space. If no transformation is + * required, the returned image may be the same as this image. If this image is from a + * different GrContext, this will fail. + */ + sk_sp makeTextureImage(GrContext*, SkColorSpace* dstColorSpace) const; + + /** * If the image is texture-backed this will make a raster copy of it (or nullptr if reading back * the pixels fails). Otherwise, it returns the original image. */ diff --git a/src/image/SkImage.cpp b/src/image/SkImage.cpp index 40586fd..67c96d7 100644 --- a/src/image/SkImage.cpp +++ b/src/image/SkImage.cpp @@ -370,6 +370,10 @@ sk_sp SkImage::MakeFromYUVTexturesCopy(GrContext* ctx, SkYUVColorSpace return nullptr; } +sk_sp SkImage::makeTextureImage(GrContext*, SkColorSpace* dstColorSpace) const { + return nullptr; +} + sk_sp SkImage::makeNonTextureImage() const { return sk_ref_sp(const_cast(this)); } diff --git a/src/image/SkImage_Gpu.cpp b/src/image/SkImage_Gpu.cpp index ef25844..98a7101 100644 --- a/src/image/SkImage_Gpu.cpp +++ b/src/image/SkImage_Gpu.cpp @@ -350,6 +350,38 @@ sk_sp SkImage::MakeFromNV12TexturesCopy(GrContext* ctx, SkYUVColorSpace std::move(imageColorSpace)); } +static sk_sp create_image_from_maker(GrTextureMaker* maker, SkAlphaType at, uint32_t id, + SkColorSpace* dstColorSpace) { + sk_sp texColorSpace; + sk_sp texture(maker->refTextureForParams(GrSamplerParams::ClampNoFilter(), + dstColorSpace, &texColorSpace, nullptr)); + if (!texture) { + return nullptr; + } + return sk_make_sp(texture->width(), texture->height(), id, at, std::move(texture), + std::move(texColorSpace), SkBudgeted::kNo); +} + +sk_sp SkImage::makeTextureImage(GrContext *context, SkColorSpace* dstColorSpace) const { + if (!context) { + return nullptr; + } + if (GrTexture* peek = as_IB(this)->peekTexture()) { + return peek->getContext() == context ? sk_ref_sp(const_cast(this)) : nullptr; + } + + if (SkImageCacherator* cacher = as_IB(this)->peekCacherator()) { + GrImageTextureMaker maker(context, cacher, this, kDisallow_CachingHint); + return create_image_from_maker(&maker, this->alphaType(), this->uniqueID(), dstColorSpace); + } + + if (const SkBitmap* bmp = as_IB(this)->onPeekBitmap()) { + GrBitmapTextureMaker maker(context, *bmp); + return create_image_from_maker(&maker, this->alphaType(), this->uniqueID(), dstColorSpace); + } + return nullptr; +} + sk_sp SkImage::makeNonTextureImage() const { if (!this->isTextureBacked()) { return sk_ref_sp(const_cast(this)); diff --git a/tests/DeviceTest.cpp b/tests/DeviceTest.cpp index dd1a9f2..55c9421 100644 --- a/tests/DeviceTest.cpp +++ b/tests/DeviceTest.cpp @@ -106,8 +106,8 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SpecialImage_GPUDevice, reporter, ctxInfo) { SkASSERT(SkIRect::MakeWH(kWidth, kHeight) == special->subset()); // Create a gpu-backed special image from a gpu-backed SkImage - sk_sp surface(SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, bm.info())); - image = surface->makeImageSnapshot(); + SkColorSpace* legacyColorSpace = nullptr; + image = image->makeTextureImage(context, legacyColorSpace); special = DeviceTestingAccess::MakeSpecial(gpuDev.get(), image.get()); SkASSERT(special->isTextureBacked()); SkASSERT(kWidth == special->width()); diff --git a/tests/ImageTest.cpp b/tests/ImageTest.cpp index 76ddd96..500efef 100644 --- a/tests/ImageTest.cpp +++ b/tests/ImageTest.cpp @@ -457,6 +457,69 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(c, reporter, ctxInfo) { } } +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkImage_makeTextureImage, reporter, contextInfo) { + GrContext* context = contextInfo.grContext(); + sk_gpu_test::TestContext* testContext = contextInfo.testContext(); + + GrContextFactory otherFactory; + GrContextFactory::ContextType otherContextType = + GrContextFactory::NativeContextTypeForBackend(testContext->backend()); + ContextInfo otherContextInfo = otherFactory.getContextInfo(otherContextType); + testContext->makeCurrent(); + + std::function()> imageFactories[] = { + create_image, + create_codec_image, + create_data_image, + // Create an image from a picture. + create_picture_image, + // Create a texture image. + [context] { return create_gpu_image(context); }, + // Create a texture image in a another GrContext. + [testContext, otherContextInfo] { + otherContextInfo.testContext()->makeCurrent(); + sk_sp otherContextImage = create_gpu_image(otherContextInfo.grContext()); + testContext->makeCurrent(); + return otherContextImage; + } + }; + + SkColorSpace* legacyColorSpace = nullptr; + for (auto factory : imageFactories) { + sk_sp image(factory()); + if (!image) { + ERRORF(reporter, "Error creating image."); + continue; + } + GrTexture* origTexture = as_IB(image)->peekTexture(); + + sk_sp texImage(image->makeTextureImage(context, legacyColorSpace)); + if (!texImage) { + // We execpt to fail if image comes from a different GrContext. + if (!origTexture || origTexture->getContext() == context) { + ERRORF(reporter, "makeTextureImage failed."); + } + continue; + } + GrTexture* copyTexture = as_IB(texImage)->peekTexture(); + if (!copyTexture) { + ERRORF(reporter, "makeTextureImage returned non-texture image."); + continue; + } + if (origTexture) { + if (origTexture != copyTexture) { + ERRORF(reporter, "makeTextureImage made unnecessary texture copy."); + } + } + if (image->width() != texImage->width() || image->height() != texImage->height()) { + ERRORF(reporter, "makeTextureImage changed the image size."); + } + if (image->alphaType() != texImage->alphaType()) { + ERRORF(reporter, "makeTextureImage changed image alpha type."); + } + } +} + DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkImage_makeNonTextureImage, reporter, contextInfo) { GrContext* context = contextInfo.grContext(); @@ -467,11 +530,14 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkImage_makeNonTextureImage, reporter, contex create_picture_image, [context] { return create_gpu_image(context); }, }; + SkColorSpace* legacyColorSpace = nullptr; for (auto factory : imageFactories) { sk_sp image = factory(); if (!image->isTextureBacked()) { REPORTER_ASSERT(reporter, image->makeNonTextureImage().get() == image.get()); - continue; + if (!(image = image->makeTextureImage(context, legacyColorSpace))) { + continue; + } } auto rasterImage = image->makeNonTextureImage(); if (!rasterImage) { -- 2.7.4