From 8e74f80ca1bd32581a583e2325daf4b46d892ee5 Mon Sep 17 00:00:00 2001 From: bsalomon Date: Sat, 30 Jan 2016 10:01:40 -0800 Subject: [PATCH] Add SkImage factory method that forces image to be resolved to a texture. GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1631053003 Review URL: https://codereview.chromium.org/1631053003 --- gm/image.cpp | 84 +++++++++++++++++++++++++++++++++++++++++++++++ include/core/SkImage.h | 7 ++++ src/image/SkImage.cpp | 4 +++ src/image/SkImage_Gpu.cpp | 31 +++++++++++++++++ tests/ImageTest.cpp | 76 ++++++++++++++++++++++++++++++++++++++---- 5 files changed, 196 insertions(+), 6 deletions(-) diff --git a/gm/image.cpp b/gm/image.cpp index 60c1f11..e79968a 100644 --- a/gm/image.cpp +++ b/gm/image.cpp @@ -5,6 +5,7 @@ * found in the LICENSE file. */ +#include #include "gm.h" #include "SkData.h" #include "SkCanvas.h" @@ -429,3 +430,86 @@ private: typedef skiagm::GM INHERITED; }; DEF_GM( return new ScaleGeneratorGM; ) + +#if SK_SUPPORT_GPU +#include "GrContextFactory.h" +#endif + +DEF_SIMPLE_GM(new_texture_image, canvas, 225, 60) { + GrContext* context = nullptr; +#if SK_SUPPORT_GPU + context = canvas->getGrContext(); + GrContextFactory factory; +#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 const int kSize = 50; + SkBitmap bmp; + bmp.allocN32Pixels(kSize, kSize); + SkCanvas bmpCanvas(bmp); + render_image(&bmpCanvas); + + std::function imageFactories[] = { + // Create sw raster image. + [bmp] { + return SkImage::NewFromBitmap(bmp); + }, + // Create encoded image. + [bmp] { + SkAutoTUnref src( + SkImageEncoder::EncodeData(bmp, SkImageEncoder::kPNG_Type, 100)); + return SkImage::NewFromEncoded(src); + }, + // Create a picture image. + [render_image] { + SkPictureRecorder recorder; + SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(kSize), SkIntToScalar(kSize)); + render_image(canvas); + SkAutoTUnref picture(recorder.endRecording()); + return SkImage::NewFromPicture(picture, SkISize::Make(kSize, kSize), nullptr, nullptr); + }, + // Create a texture image + [context, render_image]() -> SkImage* { + SkAutoTUnref surface( + SkSurface::NewRenderTarget(context, SkSurface::kYes_Budgeted, + SkImageInfo::MakeN32Premul(kSize, kSize))); + if (!surface) { + return nullptr; + } + render_image(surface->getCanvas()); + return surface->newImageSnapshot(); + } + }; + + static const SkScalar kPad = 5.f; + canvas->translate(kPad, kPad); + for (auto factory : imageFactories) { + SkAutoTUnref image(factory()); + if (!image) { + continue; + } + if (context) { + SkAutoTUnref texImage(image->newTextureImage(context)); + 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 62a9e9c..e60902f 100644 --- a/include/core/SkImage.h +++ b/include/core/SkImage.h @@ -300,6 +300,13 @@ public: */ SkImage* newSubset(const SkIRect& subset) const; + /** + * Ensures that an image is backed by a texture (when GrContext is non-null). If no + * transformation is required, the returned image may be the same as this image. If the this + * image is from a different GrContext, this will fail. + */ + SkImage* newTextureImage(GrContext*) const; + // Helper functions to convert to SkBitmap enum LegacyBitmapMode { diff --git a/src/image/SkImage.cpp b/src/image/SkImage.cpp index 25370bc..4f31fc5 100644 --- a/src/image/SkImage.cpp +++ b/src/image/SkImage.cpp @@ -339,4 +339,8 @@ SkImage* SkImage::NewFromTextureCopy(GrContext*, const GrBackendTextureDesc&, Sk return nullptr; } +SkImage* SkImage::newTextureImage(GrContext*) const { + return nullptr; +} + #endif diff --git a/src/image/SkImage_Gpu.cpp b/src/image/SkImage_Gpu.cpp index a758efe..1956958 100644 --- a/src/image/SkImage_Gpu.cpp +++ b/src/image/SkImage_Gpu.cpp @@ -282,6 +282,37 @@ SkImage* SkImage::NewFromYUVTexturesCopy(GrContext* ctx , SkYUVColorSpace colorS kOpaque_SkAlphaType, dst, budgeted); } +static SkImage* create_image_from_maker(GrTextureMaker* maker, SkAlphaType at, uint32_t id) { + SkAutoTUnref texture(maker->refTextureForParams(GrTextureParams::ClampNoFilter())); + if (!texture) { + return nullptr; + } + return new SkImage_Gpu(texture->width(), texture->height(), id, at, texture, + SkSurface::kNo_Budgeted); +} + +SkImage* SkImage::newTextureImage(GrContext *context) const { + if (!context) { + return nullptr; + } + if (GrTexture* peek = as_IB(this)->peekTexture()) { + return peek->getContext() == context ? SkRef(const_cast(this)) : nullptr; + } + // No way to check whether a image is premul or not? + SkAlphaType at = this->isOpaque() ? kOpaque_SkAlphaType : kPremul_SkAlphaType; + + if (SkImageCacherator* cacher = as_IB(this)->peekCacherator()) { + GrImageTextureMaker maker(context, cacher, this, kDisallow_CachingHint); + return create_image_from_maker(&maker, at, this->uniqueID()); + } + SkBitmap bmp; + if (!this->asLegacyBitmap(&bmp, kRO_LegacyBitmapMode)) { + return nullptr; + } + GrBitmapTextureMaker maker(context, bmp); + return create_image_from_maker(&maker, at, this->uniqueID()); +} + /////////////////////////////////////////////////////////////////////////////////////////////////// GrTexture* GrDeepCopyTexture(GrTexture* src, bool budgeted) { diff --git a/tests/ImageTest.cpp b/tests/ImageTest.cpp index 575fc1c..1bf56c5 100644 --- a/tests/ImageTest.cpp +++ b/tests/ImageTest.cpp @@ -5,6 +5,9 @@ * found in the LICENSE file. */ +#include +#include "DMGpuSupport.h" + #include "SkBitmap.h" #include "SkCanvas.h" #include "SkData.h" @@ -21,12 +24,6 @@ #include "SkUtils.h" #include "Test.h" -#if SK_SUPPORT_GPU -#include "GrContext.h" -#include "gl/GrGLInterface.h" -#include "gl/GrGLUtil.h" -#endif - static void assert_equal(skiatest::Reporter* reporter, SkImage* a, const SkIRect* subsetA, SkImage* b) { const int widthA = subsetA ? subsetA->width() : a->width(); @@ -87,6 +84,15 @@ static SkImage* create_data_image() { SkAutoTUnref data(create_image_data(&info)); return SkImage::NewRasterData(info, data, info.minRowBytes()); } +#if SK_SUPPORT_GPU // not gpu-specific but currently only used in GPU tests +static SkImage* create_picture_image() { + SkPictureRecorder recorder; + SkCanvas* canvas = recorder.beginRecording(10, 10); + canvas->clear(SK_ColorCYAN); + SkAutoTUnref picture(recorder.endRecording()); + return SkImage::NewFromPicture(picture, SkISize::Make(10, 10), nullptr, nullptr); +}; +#endif // Want to ensure that our Release is called when the owning image is destroyed struct RasterDataHolder { RasterDataHolder() : fReleaseCount(0) {} @@ -376,6 +382,64 @@ DEF_GPUTEST_FOR_NATIVE_CONTEXT(SkImage_Gpu2Cpu, reporter, context) { REPORTER_ASSERT(reporter, !SkBitmapCache::Find(uniqueID, &cachedBitmap)); } } + +DEF_GPUTEST_FOR_NATIVE_CONTEXT(SkImage_newTextureImage, reporter, context, glContext) { + GrContextFactory otherFactory; + GrContextFactory::ContextInfo otherContextInfo = + otherFactory.getContextInfo(GrContextFactory::kNative_GLContextType); + glContext->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. + [glContext, otherContextInfo] { + otherContextInfo.fGLContext->makeCurrent(); + SkImage* otherContextImage = create_gpu_image(otherContextInfo.fGrContext); + glContext->makeCurrent(); + return otherContextImage; + } + }; + + for (auto factory : imageFactories) { + SkAutoTUnref image(factory()); + if (!image) { + ERRORF(reporter, "Error creating image."); + continue; + } + GrTexture* origTexture = as_IB(image)->peekTexture(); + + SkAutoTUnref texImage(image->newTextureImage(context)); + if (!texImage) { + // We execpt to fail if image comes from a different GrContext. + if (!origTexture || origTexture->getContext() == context) { + ERRORF(reporter, "newTextureImage failed."); + } + continue; + } + GrTexture* copyTexture = as_IB(texImage)->peekTexture(); + if (!copyTexture) { + ERRORF(reporter, "newTextureImage returned non-texture image."); + continue; + } + if (origTexture) { + if (origTexture != copyTexture) { + ERRORF(reporter, "newTextureImage made unnecessary texture copy."); + } + } + if (image->width() != texImage->width() || image->height() != texImage->height()) { + ERRORF(reporter, "newTextureImage changed the image size."); + } + if (image->isOpaque() != texImage->isOpaque()) { + ERRORF(reporter, "newTextureImage changed image opaqueness."); + } + } +} #endif // https://bug.skia.org/4390 -- 2.7.4