diff options
author | bsalomon <bsalomon@google.com> | 2015-05-29 11:37:25 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-05-29 11:37:25 -0700 |
commit | 993a4216a6014b9de8f4d8120360c94550dc6761 (patch) | |
tree | 75e4863a6fa83d35c519a3103a9d2c94520dbd1e | |
parent | 4061b1262e931be19e1176cbd0e93b8c268eb131 (diff) |
SkImage::NewFromYUVTexturesCopy
Review URL: https://codereview.chromium.org/1149553002
-rw-r--r-- | gm/imagefromyuvtextures.cpp | 204 | ||||
-rw-r--r-- | include/core/SkImage.h | 10 | ||||
-rw-r--r-- | src/gpu/GrGpu.h | 4 | ||||
-rw-r--r-- | src/gpu/GrTest.cpp | 7 | ||||
-rw-r--r-- | src/gpu/GrTest.h | 7 | ||||
-rw-r--r-- | src/gpu/effects/GrYUVtoRGBEffect.cpp | 6 | ||||
-rw-r--r-- | src/gpu/effects/GrYUVtoRGBEffect.h | 2 | ||||
-rw-r--r-- | src/gpu/gl/GrGLGpu.h | 4 | ||||
-rw-r--r-- | src/image/SkImage_Gpu.cpp | 77 |
9 files changed, 312 insertions, 9 deletions
diff --git a/gm/imagefromyuvtextures.cpp b/gm/imagefromyuvtextures.cpp new file mode 100644 index 0000000000..b618abe2a4 --- /dev/null +++ b/gm/imagefromyuvtextures.cpp @@ -0,0 +1,204 @@ + +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +// This test only works with the GPU backend. + +#include "gm.h" + +#if SK_SUPPORT_GPU + +#include "GrContext.h" +#include "gl/GrGLInterface.h" +#include "gl/GrGLUtil.h" +#include "GrTest.h" +#include "SkBitmap.h" +#include "SkGradientShader.h" +#include "SkImage.h" + +namespace skiagm { +class ImageFromYUVTextures : public GM { +public: + ImageFromYUVTextures() { + this->setBGColor(0xFFFFFFFF); + } + +protected: + SkString onShortName() override { + return SkString("image_from_yuv_textures"); + } + + SkISize onISize() override { + return SkISize::Make(50, 135); + } + + void onOnceBeforeDraw() override { + // We create an RGB bitmap and then extract YUV bmps where the U and V bitmaps are + // subsampled by 2 in both dimensions. + SkPaint paint; + static const SkColor kColors[] = + { SK_ColorBLUE, SK_ColorYELLOW, SK_ColorGREEN, SK_ColorWHITE }; + paint.setShader(SkGradientShader::CreateRadial(SkPoint::Make(0,0), kBmpSize / 2.f, kColors, + NULL, SK_ARRAY_COUNT(kColors), + SkShader::kMirror_TileMode))->unref(); + SkBitmap rgbBmp; + rgbBmp.allocN32Pixels(kBmpSize, kBmpSize, true); + SkCanvas canvas(rgbBmp); + canvas.drawPaint(paint); + SkPMColor* rgbColors = static_cast<SkPMColor*>(rgbBmp.getPixels()); + + SkImageInfo yinfo = SkImageInfo::MakeA8(kBmpSize, kBmpSize); + fYUVBmps[0].allocPixels(yinfo); + SkImageInfo uinfo = SkImageInfo::MakeA8(kBmpSize / 2, kBmpSize / 2); + fYUVBmps[1].allocPixels(uinfo); + SkImageInfo vinfo = SkImageInfo::MakeA8(kBmpSize / 2, kBmpSize / 2); + fYUVBmps[2].allocPixels(vinfo); + unsigned char* yPixels; + signed char* uvPixels[2]; + yPixels = static_cast<unsigned char*>(fYUVBmps[0].getPixels()); + uvPixels[0] = static_cast<signed char*>(fYUVBmps[1].getPixels()); + uvPixels[1] = static_cast<signed char*>(fYUVBmps[2 + ].getPixels()); + + // Here we encode using the NTC encoding (even though we will draw it with all the supported + // yuv color spaces when converted back to RGB) + for (int i = 0; i < kBmpSize * kBmpSize; ++i) { + yPixels[i] = static_cast<unsigned char>(0.299f * SkGetPackedR32(rgbColors[i]) + + 0.587f * SkGetPackedG32(rgbColors[i]) + + 0.114f * SkGetPackedB32(rgbColors[i])); + } + for (int j = 0; j < kBmpSize / 2; ++j) { + for (int i = 0; i < kBmpSize / 2; ++i) { + // Average together 4 pixels of RGB. + int rgb[] = { 0, 0, 0 }; + for (int y = 0; y < 2; ++y) { + for (int x = 0; x < 2; ++x) { + int rgbIndex = (2 * j + y) * kBmpSize + 2 * i + x; + rgb[0] += SkGetPackedR32(rgbColors[rgbIndex]); + rgb[1] += SkGetPackedG32(rgbColors[rgbIndex]); + rgb[2] += SkGetPackedB32(rgbColors[rgbIndex]); + } + } + for (int c = 0; c < 3; ++c) { + rgb[c] /= 4; + } + int uvIndex = j * kBmpSize / 2 + i; + uvPixels[0][uvIndex] = static_cast<signed char>( + ((-38 * rgb[0] - 74 * rgb[1] + 112 * rgb[2] + 128) >> 8) + 128); + uvPixels[1][uvIndex] = static_cast<signed char>( + ((112 * rgb[0] - 94 * rgb[1] - 18 * rgb[2] + 128) >> 8) + 128); + } + } + fRGBImage.reset(SkImage::NewRasterCopy(rgbBmp.info(), rgbColors, rgbBmp.rowBytes())); + } + + void createYUVTextures(GrContext* context, GrGLuint yuvIDs[3]) { + GrTestTarget tt; + context->getTestTarget(&tt); + if (!tt.target()) { + SkDEBUGFAIL("Couldn't get Gr test target."); + return; + } + + // We currently hav only implemented the texture uploads for GL. + const GrGLInterface* gl = tt.glInterface(); + if (!gl) { + return; + } + + GR_GL_CALL(gl, GenTextures(3, yuvIDs)); + GR_GL_CALL(gl, ActiveTexture(GR_GL_TEXTURE0)); + GR_GL_CALL(gl, PixelStorei(GR_GL_UNPACK_ALIGNMENT, 1)); + for (int i = 0; i < 3; ++i) { + GR_GL_CALL(gl, BindTexture(GR_GL_TEXTURE_2D, yuvIDs[i])); + GR_GL_CALL(gl, TexParameteri(GR_GL_TEXTURE_2D, GR_GL_TEXTURE_MAG_FILTER, + GR_GL_NEAREST)); + GR_GL_CALL(gl, TexParameteri(GR_GL_TEXTURE_2D, GR_GL_TEXTURE_MIN_FILTER, + GR_GL_NEAREST)); + GR_GL_CALL(gl, TexParameteri(GR_GL_TEXTURE_2D, GR_GL_TEXTURE_WRAP_S, + GR_GL_CLAMP_TO_EDGE)); + GR_GL_CALL(gl, TexParameteri(GR_GL_TEXTURE_2D, GR_GL_TEXTURE_WRAP_T, + GR_GL_CLAMP_TO_EDGE)); + SkASSERT(fYUVBmps[i].width() == SkToInt(fYUVBmps[i].rowBytes())); + GR_GL_CALL(gl, TexImage2D(GR_GL_TEXTURE_2D, 0, GR_GL_RED, fYUVBmps[i].width(), + fYUVBmps[i].height(), 0, GR_GL_RED, GR_GL_UNSIGNED_BYTE, + fYUVBmps[i].getPixels())); + } + context->resetContext(); + } + + void deleteYUVTextures(GrContext* context, const GrGLuint yuvIDs[3]) { + GrTestTarget tt; + context->getTestTarget(&tt); + if (!tt.target()) { + SkDEBUGFAIL("Couldn't get Gr test target."); + return; + } + + const GrGLInterface* gl = tt.glInterface(); + if (!gl) { + return; + } + GR_GL_CALL(gl, DeleteTextures(3, yuvIDs)); + context->resetContext(); + } + + void onDraw(SkCanvas* canvas) override { + GrRenderTarget* rt = canvas->internal_private_accessTopLayerRenderTarget(); + GrContext* context; + if (!rt || !(context = rt->getContext())) { + this->drawGpuOnlyMessage(canvas); + return; + } + + GrGLuint yuvIDs[3]; + this->createYUVTextures(context, yuvIDs); + + static const SkScalar kPad = 10.f; + + GrBackendObject backendTextureObjects[] = { + static_cast<GrBackendObject>(yuvIDs[0]), + static_cast<GrBackendObject>(yuvIDs[1]), + static_cast<GrBackendObject>(yuvIDs[2]) + }; + SkISize sizes[] = { + { fYUVBmps[0].width(), fYUVBmps[0].height()}, + { fYUVBmps[1].width(), fYUVBmps[1].height()}, + { fYUVBmps[2].width(), fYUVBmps[2].height()}, + }; + SkTArray<SkImage*> images; + images.push_back(SkRef(fRGBImage.get())); + for (int space = kJPEG_SkYUVColorSpace; space <= kLastEnum_SkYUVColorSpace; ++space) { + images.push_back(SkImage::NewFromYUVTexturesCopy(context, + static_cast<SkYUVColorSpace>(space), + backendTextureObjects, sizes, + kTopLeft_GrSurfaceOrigin)); + } + this->deleteYUVTextures(context, yuvIDs); + for (int i = 0; i < images.count(); ++ i) { + SkScalar y = (i + 1) * kPad + i * fYUVBmps[0].height(); + SkScalar x = kPad; + + canvas->drawImage(images[i], x, y); + images[i]->unref(); + images[i] = NULL; + } + } + +private: + SkAutoTUnref<SkImage> fRGBImage; + SkBitmap fYUVBmps[3]; + + static const int kBmpSize = 32; + + typedef GM INHERITED; +}; + +DEF_GM( return SkNEW(ImageFromYUVTextures); ) +} + +#endif diff --git a/include/core/SkImage.h b/include/core/SkImage.h index 7128f6ea3a..33ae448072 100644 --- a/include/core/SkImage.h +++ b/include/core/SkImage.h @@ -82,6 +82,16 @@ public: static SkImage* NewFromTextureCopy(GrContext*, const GrBackendTextureDesc&, SkAlphaType = kPremul_SkAlphaType); + /** + * Create a new image by copying the pixels from the specified y, u, v textures. The data + * from the textures is immediately ingested into the image and the textures can be modified or + * deleted after the function returns. The image will have the dimensions of the y texture. + */ + static SkImage* NewFromYUVTexturesCopy(GrContext*, SkYUVColorSpace, + const GrBackendObject yuvTextureHandles[3], + const SkISize yuvSizes[3], + GrSurfaceOrigin); + int width() const { return fWidth; } int height() const { return fHeight; } uint32_t uniqueID() const { return fUniqueID; } diff --git a/src/gpu/GrGpu.h b/src/gpu/GrGpu.h index 5b5be1b69b..539a6a7a60 100644 --- a/src/gpu/GrGpu.h +++ b/src/gpu/GrGpu.h @@ -14,6 +14,7 @@ #include "SkPath.h" class GrContext; +struct GrGLInterface; class GrNonInstancedVertices; class GrPath; class GrPathRange; @@ -386,6 +387,9 @@ public: // Given a rt, find or create a stencil buffer and attach it bool attachStencilAttachmentToRenderTarget(GrRenderTarget* target); + // This is only to be used in tests. + virtual const GrGLInterface* glInterfaceForTesting() const { return NULL; } + protected: // Functions used to map clip-respecting stencil tests into normal // stencil funcs supported by GPUs. diff --git a/src/gpu/GrTest.cpp b/src/gpu/GrTest.cpp index 37a0feb6fa..449a76300a 100644 --- a/src/gpu/GrTest.cpp +++ b/src/gpu/GrTest.cpp @@ -8,17 +8,18 @@ #include "GrTest.h" #include "GrContextOptions.h" - #include "GrGpuResourceCacheAccess.h" #include "GrInOrderDrawBuffer.h" #include "GrResourceCache.h" +#include "gl/GrGLInterface.h" #include "SkString.h" -void GrTestTarget::init(GrContext* ctx, GrDrawTarget* target) { +void GrTestTarget::init(GrContext* ctx, GrDrawTarget* target, const GrGLInterface* gl) { SkASSERT(!fContext); fContext.reset(SkRef(ctx)); fDrawTarget.reset(SkRef(target)); + fGLInterface.reset(SkSafeRef(gl)); } void GrContext::getTestTarget(GrTestTarget* tar) { @@ -27,7 +28,7 @@ void GrContext::getTestTarget(GrTestTarget* tar) { // then disconnects. This would help prevent test writers from mixing using the returned // GrDrawTarget and regular drawing. We could also assert or fail in GrContext drawing methods // until ~GrTestTarget(). - tar->init(this, fDrawingMgr.fDrawTarget); + tar->init(this, fDrawingMgr.fDrawTarget, fGpu->glInterfaceForTesting()); } /////////////////////////////////////////////////////////////////////////////// diff --git a/src/gpu/GrTest.h b/src/gpu/GrTest.h index cf9a53ec77..f1f3cf6c14 100644 --- a/src/gpu/GrTest.h +++ b/src/gpu/GrTest.h @@ -11,6 +11,7 @@ #include "GrContext.h" #include "GrDrawTarget.h" +#include "gl/GrGLInterface.h" /** Allows a test to temporarily draw to a GrDrawTarget owned by a GrContext. Tests that use this should be careful not to mix using the GrDrawTarget directly and drawing via SkCanvas or @@ -19,13 +20,17 @@ class GrTestTarget { public: GrTestTarget() {}; - void init(GrContext*, GrDrawTarget*); + void init(GrContext*, GrDrawTarget*, const GrGLInterface*); GrDrawTarget* target() { return fDrawTarget.get(); } + /** Returns a GrGLInterface if the GrContext is backed by OpenGL. */ + const GrGLInterface* glInterface() { return fGLInterface.get(); } + private: SkAutoTUnref<GrDrawTarget> fDrawTarget; SkAutoTUnref<GrContext> fContext; + SkAutoTUnref<const GrGLInterface> fGLInterface; }; #endif diff --git a/src/gpu/effects/GrYUVtoRGBEffect.cpp b/src/gpu/effects/GrYUVtoRGBEffect.cpp index 60c39bf4a7..92acab3725 100644 --- a/src/gpu/effects/GrYUVtoRGBEffect.cpp +++ b/src/gpu/effects/GrYUVtoRGBEffect.cpp @@ -18,7 +18,7 @@ namespace { class YUVtoRGBEffect : public GrFragmentProcessor { public: static GrFragmentProcessor* Create(GrTexture* yTexture, GrTexture* uTexture, - GrTexture* vTexture, SkISize sizes[3], + GrTexture* vTexture, const SkISize sizes[3], SkYUVColorSpace colorSpace) { SkScalar w[3], h[3]; w[0] = SkIntToScalar(sizes[0].fWidth) / SkIntToScalar(yTexture->width()); @@ -111,7 +111,7 @@ public: private: YUVtoRGBEffect(GrTexture* yTexture, GrTexture* uTexture, GrTexture* vTexture, - SkMatrix yuvMatrix[3], GrTextureParams::FilterMode uvFilterMode, + const SkMatrix yuvMatrix[3], GrTextureParams::FilterMode uvFilterMode, SkYUVColorSpace colorSpace) : fYTransform(kLocal_GrCoordSet, yuvMatrix[0], yTexture, GrTextureParams::kNone_FilterMode) , fYAccess(yTexture) @@ -167,7 +167,7 @@ const GrGLfloat YUVtoRGBEffect::GLProcessor::kRec601ConversionMatrix[16] = { GrFragmentProcessor* GrYUVtoRGBEffect::Create(GrTexture* yTexture, GrTexture* uTexture, GrTexture* vTexture, - SkISize sizes[3], SkYUVColorSpace colorSpace) { + const SkISize sizes[3], SkYUVColorSpace colorSpace) { SkASSERT(yTexture && uTexture && vTexture && sizes); return YUVtoRGBEffect::Create(yTexture, uTexture, vTexture, sizes, colorSpace); } diff --git a/src/gpu/effects/GrYUVtoRGBEffect.h b/src/gpu/effects/GrYUVtoRGBEffect.h index 2a2e74a26d..03679788dd 100644 --- a/src/gpu/effects/GrYUVtoRGBEffect.h +++ b/src/gpu/effects/GrYUVtoRGBEffect.h @@ -18,7 +18,7 @@ namespace GrYUVtoRGBEffect { * Creates an effect that performs color conversion from YUV to RGB */ GrFragmentProcessor* Create(GrTexture* yTexture, GrTexture* uTexture, GrTexture* vTexture, - SkISize sizes[3], SkYUVColorSpace colorSpace); + const SkISize sizes[3], SkYUVColorSpace colorSpace); }; #endif diff --git a/src/gpu/gl/GrGLGpu.h b/src/gpu/gl/GrGLGpu.h index c777f7792a..cd9496c7a0 100644 --- a/src/gpu/gl/GrGLGpu.h +++ b/src/gpu/gl/GrGLGpu.h @@ -114,6 +114,10 @@ public: const GrPipeline&, const GrBatchTracker&) const override; + virtual const GrGLInterface* glInterfaceForTesting() const { + return this->glInterface(); + } + private: GrGLGpu(GrGLContext* ctx, GrContext* context); diff --git a/src/image/SkImage_Gpu.cpp b/src/image/SkImage_Gpu.cpp index 06ecda10e2..4c3e1300c5 100644 --- a/src/image/SkImage_Gpu.cpp +++ b/src/image/SkImage_Gpu.cpp @@ -6,10 +6,13 @@ */ #include "SkImage_Gpu.h" -#include "SkCanvas.h" #include "GrContext.h" +#include "GrDrawContext.h" +#include "effects/GrYUVtoRGBEffect.h" +#include "SkCanvas.h" #include "SkGpuDevice.h" + SkImage_Gpu::SkImage_Gpu(int w, int h, SkAlphaType at, GrTexture* tex, int sampleCountForNewSurfaces, SkSurface::Budgeted budgeted) : INHERITED(w, h, NULL) @@ -159,3 +162,75 @@ SkImage* SkImage::NewFromTextureCopy(GrContext* ctx, const GrBackendTextureDesc& return SkNEW_ARGS(SkImage_Gpu, (dstDesc.fWidth, dstDesc.fHeight, at, dst, sampleCount, budgeted)); } + +SkImage* SkImage::NewFromYUVTexturesCopy(GrContext* ctx , SkYUVColorSpace colorSpace, + const GrBackendObject yuvTextureHandles[3], + const SkISize yuvSizes[3], + GrSurfaceOrigin origin) { + const SkSurface::Budgeted budgeted = SkSurface::kYes_Budgeted; + + if (yuvSizes[0].fWidth <= 0 || yuvSizes[0].fHeight <= 0 || + yuvSizes[1].fWidth <= 0 || yuvSizes[1].fHeight <= 0 || + yuvSizes[2].fWidth <= 0 || yuvSizes[2].fHeight <= 0) { + return NULL; + } + static const GrPixelConfig kConfig = kAlpha_8_GrPixelConfig; + GrBackendTextureDesc yDesc; + yDesc.fConfig = kConfig; + yDesc.fOrigin = origin; + yDesc.fSampleCnt = 0; + yDesc.fTextureHandle = yuvTextureHandles[0]; + yDesc.fWidth = yuvSizes[0].fWidth; + yDesc.fHeight = yuvSizes[0].fHeight; + + GrBackendTextureDesc uDesc; + uDesc.fConfig = kConfig; + uDesc.fOrigin = origin; + uDesc.fSampleCnt = 0; + uDesc.fTextureHandle = yuvTextureHandles[1]; + uDesc.fWidth = yuvSizes[1].fWidth; + uDesc.fHeight = yuvSizes[1].fHeight; + + GrBackendTextureDesc vDesc; + vDesc.fConfig = kConfig; + vDesc.fOrigin = origin; + vDesc.fSampleCnt = 0; + vDesc.fTextureHandle = yuvTextureHandles[2]; + vDesc.fWidth = yuvSizes[2].fWidth; + vDesc.fHeight = yuvSizes[2].fHeight; + + SkAutoTUnref<GrTexture> yTex(ctx->textureProvider()->wrapBackendTexture(yDesc)); + SkAutoTUnref<GrTexture> uTex(ctx->textureProvider()->wrapBackendTexture(uDesc)); + SkAutoTUnref<GrTexture> vTex(ctx->textureProvider()->wrapBackendTexture(vDesc)); + if (!yTex || !uTex || !vTex) { + return NULL; + } + + GrSurfaceDesc dstDesc; + // Needs to be a render target in order to draw to it for the yuv->rgb conversion. + dstDesc.fFlags = kRenderTarget_GrSurfaceFlag; + dstDesc.fOrigin = origin; + dstDesc.fWidth = yuvSizes[0].fWidth; + dstDesc.fHeight = yuvSizes[0].fHeight; + dstDesc.fConfig = kRGBA_8888_GrPixelConfig; + dstDesc.fSampleCnt = 0; + + SkAutoTUnref<GrTexture> dst(ctx->textureProvider()->refScratchTexture( + dstDesc, GrTextureProvider::kExact_ScratchTexMatch)); + if (!dst) { + return NULL; + } + + GrPaint paint; + paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode); + paint.addColorProcessor(GrYUVtoRGBEffect::Create(yTex, uTex, vTex, yuvSizes, + colorSpace))->unref(); + + const SkRect rect = SkRect::MakeWH(SkIntToScalar(dstDesc.fWidth), + SkIntToScalar(dstDesc.fHeight)); + ctx->drawContext()->drawRect(dst->asRenderTarget(), GrClip::WideOpen(), paint, SkMatrix::I(), + rect); + ctx->flushSurfaceWrites(dst); + return SkNEW_ARGS(SkImage_Gpu, (dstDesc.fWidth, dstDesc.fHeight, kOpaque_SkAlphaType, dst, 0, + budgeted)); +} |