diff options
author | bsalomon <bsalomon@google.com> | 2016-02-01 13:16:14 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2016-02-01 13:16:14 -0800 |
commit | f267c1efe7de7a8e71404afde6cbf93c3808d267 (patch) | |
tree | 3ca07e8c6e5526ccfe4166953631006dd8b71845 /src/gpu | |
parent | 9d02b264b72be64702e43cb797b7899a8a6926ca (diff) |
Add ability to extract YUV planes from SkImage
GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1513393002
Review URL: https://codereview.chromium.org/1513393002
Diffstat (limited to 'src/gpu')
-rw-r--r-- | src/gpu/GrTextureToYUVPlanes.cpp | 248 | ||||
-rw-r--r-- | src/gpu/GrTextureToYUVPlanes.h | 19 | ||||
-rw-r--r-- | src/gpu/GrYUVProvider.cpp | 14 | ||||
-rw-r--r-- | src/gpu/SkGr.cpp | 2 | ||||
-rw-r--r-- | src/gpu/effects/GrYUVEffect.cpp | 391 | ||||
-rw-r--r-- | src/gpu/effects/GrYUVEffect.h | 50 | ||||
-rw-r--r-- | src/gpu/effects/GrYUVtoRGBEffect.cpp | 185 | ||||
-rw-r--r-- | src/gpu/effects/GrYUVtoRGBEffect.h | 24 |
8 files changed, 716 insertions, 217 deletions
diff --git a/src/gpu/GrTextureToYUVPlanes.cpp b/src/gpu/GrTextureToYUVPlanes.cpp new file mode 100644 index 0000000000..6a8d7b6676 --- /dev/null +++ b/src/gpu/GrTextureToYUVPlanes.cpp @@ -0,0 +1,248 @@ +/* + * 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 "GrTextureToYUVPlanes.h" +#include "effects/GrSimpleTextureEffect.h" +#include "effects/GrYUVEffect.h" +#include "GrClip.h" +#include "GrContext.h" +#include "GrDrawContext.h" +#include "GrPaint.h" +#include "GrTextureProvider.h" + +namespace { + using CreateFPProc = const GrFragmentProcessor* (*)(const GrFragmentProcessor*, + SkYUVColorSpace colorSpace); +}; + +static bool convert_texture(GrTexture* src, GrDrawContext* dst, int dstW, int dstH, + SkYUVColorSpace colorSpace, CreateFPProc proc) { + + SkScalar xScale = SkIntToScalar(src->width()) / dstW / src->width(); + SkScalar yScale = SkIntToScalar(src->height()) / dstH / src->height(); + GrTextureParams::FilterMode filter; + if (dstW == src->width() && dstW == src->height()) { + filter = GrTextureParams::kNone_FilterMode; + } else { + filter = GrTextureParams::kBilerp_FilterMode; + } + + SkAutoTUnref<const GrFragmentProcessor> fp( + GrSimpleTextureEffect::Create(src, SkMatrix::MakeScale(xScale, yScale), filter)); + if (!fp) { + return false; + } + fp.reset(proc(fp, colorSpace)); + if (!fp) { + return false; + } + GrPaint paint; + paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode); + paint.addColorFragmentProcessor(fp); + dst->drawRect(GrClip::WideOpen(), paint, SkMatrix::I(), SkRect::MakeIWH(dstW, dstH)); + return true; +} + +bool GrTextureToYUVPlanes(GrTexture* texture, const SkISize sizes[3], void* const planes[3], + const size_t rowBytes[3], SkYUVColorSpace colorSpace) { + if (GrContext* context = texture->getContext()) { + // Depending on the relative sizes of the y, u, and v planes we may do 1 to 3 draws/ + // readbacks. + SkAutoTUnref<GrTexture> yuvTex; + SkAutoTUnref<GrTexture> yTex; + SkAutoTUnref<GrTexture> uvTex; + SkAutoTUnref<GrTexture> uTex; + SkAutoTUnref<GrTexture> vTex; + + GrPixelConfig singleChannelPixelConfig; + if (context->caps()->isConfigRenderable(kAlpha_8_GrPixelConfig, false)) { + singleChannelPixelConfig = kAlpha_8_GrPixelConfig; + } else { + singleChannelPixelConfig = kRGBA_8888_GrPixelConfig; + } + + // We issue draw(s) to convert from RGBA to Y, U, and V. All three planes may have different + // sizes however we optimize for two other cases - all planes are the same (1 draw to YUV), + // and U and V are the same but Y differs (2 draws, one for Y, one for UV). + if (sizes[0] == sizes[1] && sizes[1] == sizes[2]) { + GrSurfaceDesc yuvDesc; + yuvDesc.fConfig = kRGBA_8888_GrPixelConfig; + yuvDesc.fFlags = kRenderTarget_GrSurfaceFlag; + yuvDesc.fWidth = sizes[0].fWidth; + yuvDesc.fHeight = sizes[0].fHeight; + yuvTex.reset(context->textureProvider()->createApproxTexture(yuvDesc)); + if (!yuvTex) { + return false; + } + } else { + GrSurfaceDesc yDesc; + yDesc.fConfig = singleChannelPixelConfig; + yDesc.fFlags = kRenderTarget_GrSurfaceFlag; + yDesc.fWidth = sizes[0].fWidth; + yDesc.fHeight = sizes[0].fHeight; + yTex.reset(context->textureProvider()->createApproxTexture(yDesc)); + if (!yTex) { + return false; + } + if (sizes[1] == sizes[2]) { + GrSurfaceDesc uvDesc; + // TODO: Add support for GL_RG when available. + uvDesc.fConfig = kRGBA_8888_GrPixelConfig; + uvDesc.fFlags = kRenderTarget_GrSurfaceFlag; + uvDesc.fWidth = sizes[1].fWidth; + uvDesc.fHeight = sizes[1].fHeight; + uvTex.reset(context->textureProvider()->createApproxTexture(uvDesc)); + if (!uvTex) { + return false; + } + } else { + GrSurfaceDesc uvDesc; + uvDesc.fConfig = singleChannelPixelConfig; + uvDesc.fFlags = kRenderTarget_GrSurfaceFlag; + uvDesc.fWidth = sizes[1].fWidth; + uvDesc.fHeight = sizes[1].fHeight; + uTex.reset(context->textureProvider()->createApproxTexture(uvDesc)); + uvDesc.fWidth = sizes[2].fWidth; + uvDesc.fHeight = sizes[2].fHeight; + vTex.reset(context->textureProvider()->createApproxTexture(uvDesc)); + if (!uTex || !vTex) { + return false; + } + } + } + + // Do all the draws before any readback. + if (yuvTex) { + SkAutoTUnref<GrDrawContext> dc(context->drawContext(yuvTex->asRenderTarget())); + if (!dc) { + return false; + } + if (!convert_texture(texture, dc, sizes[0].fWidth, sizes[0].fHeight, colorSpace, + GrYUVEffect::CreateRGBToYUV)) { + return false; + } + + } else { + SkASSERT(yTex); + SkAutoTUnref<GrDrawContext> dc(context->drawContext(yTex->asRenderTarget())); + if (!dc) { + return false; + } + if (!convert_texture(texture, dc, sizes[0].fWidth, sizes[0].fHeight, colorSpace, + GrYUVEffect::CreateRGBToY)) { + return false; + } + if (uvTex) { + dc.reset(context->drawContext(uvTex->asRenderTarget())); + if (!dc) { + return false; + } + if (!convert_texture(texture, dc, sizes[1].fWidth, sizes[1].fHeight, + colorSpace, GrYUVEffect::CreateRGBToUV)) { + return false; + } + } else { + SkASSERT(uTex && vTex); + dc.reset(context->drawContext(uTex->asRenderTarget())); + if (!dc) { + return false; + } + if (!convert_texture(texture, dc, sizes[1].fWidth, sizes[1].fHeight, + colorSpace, GrYUVEffect::CreateRGBToU)) { + return false; + } + dc.reset(context->drawContext(vTex->asRenderTarget())); + if (!dc) { + return false; + } + if (!convert_texture(texture, dc, sizes[2].fWidth, sizes[2].fHeight, + colorSpace, GrYUVEffect::CreateRGBToV)) { + return false; + } + } + } + + if (yuvTex) { + SkASSERT(sizes[0] == sizes[1] && sizes[1] == sizes[2]); + SkISize yuvSize = sizes[0]; + // We have no kRGB_888 pixel format, so readback rgba and then copy three channels. + SkAutoSTMalloc<128 * 128, uint32_t> tempYUV(yuvSize.fWidth * yuvSize.fHeight); + if (!yuvTex->readPixels(0, 0, yuvSize.fWidth, yuvSize.fHeight, + kRGBA_8888_GrPixelConfig, tempYUV.get(), 0)) { + return false; + } + size_t yRowBytes = rowBytes[0] ? rowBytes[0] : yuvSize.fWidth; + size_t uRowBytes = rowBytes[1] ? rowBytes[1] : yuvSize.fWidth; + size_t vRowBytes = rowBytes[2] ? rowBytes[2] : yuvSize.fWidth; + if (yRowBytes < (size_t)yuvSize.fWidth || uRowBytes < (size_t)yuvSize.fWidth || + vRowBytes < (size_t)yuvSize.fWidth) { + return false; + } + for (int j = 0; j < yuvSize.fHeight; ++j) { + for (int i = 0; i < yuvSize.fWidth; ++i) { + // These writes could surely be made more efficient. + uint32_t y = GrColorUnpackR(tempYUV.get()[j * yuvSize.fWidth + i]); + uint32_t u = GrColorUnpackG(tempYUV.get()[j * yuvSize.fWidth + i]); + uint32_t v = GrColorUnpackB(tempYUV.get()[j * yuvSize.fWidth + i]); + uint8_t* yLoc = ((uint8_t*)planes[0]) + j * yRowBytes + i; + uint8_t* uLoc = ((uint8_t*)planes[1]) + j * uRowBytes + i; + uint8_t* vLoc = ((uint8_t*)planes[2]) + j * vRowBytes + i; + *yLoc = y; + *uLoc = u; + *vLoc = v; + } + } + return true; + } else { + SkASSERT(yTex); + if (!yTex->readPixels(0, 0, sizes[0].fWidth, sizes[0].fHeight, + kAlpha_8_GrPixelConfig, planes[0], rowBytes[0])) { + return false; + } + if (uvTex) { + SkASSERT(sizes[1].fWidth == sizes[2].fWidth); + SkISize uvSize = sizes[1]; + // We have no kRG_88 pixel format, so readback rgba and then copy two channels. + SkAutoSTMalloc<128 * 128, uint32_t> tempUV(uvSize.fWidth * uvSize.fHeight); + if (!uvTex->readPixels(0, 0, uvSize.fWidth, uvSize.fHeight, + kRGBA_8888_GrPixelConfig, tempUV.get(), 0)) { + return false; + } + + size_t uRowBytes = rowBytes[1] ? rowBytes[1] : uvSize.fWidth; + size_t vRowBytes = rowBytes[2] ? rowBytes[2] : uvSize.fWidth; + if (uRowBytes < (size_t)uvSize.fWidth || vRowBytes < (size_t)uvSize.fWidth) { + return false; + } + for (int j = 0; j < uvSize.fHeight; ++j) { + for (int i = 0; i < uvSize.fWidth; ++i) { + // These writes could surely be made more efficient. + uint32_t u = GrColorUnpackR(tempUV.get()[j * uvSize.fWidth + i]); + uint32_t v = GrColorUnpackG(tempUV.get()[j * uvSize.fWidth + i]); + uint8_t* uLoc = ((uint8_t*)planes[1]) + j * uRowBytes + i; + uint8_t* vLoc = ((uint8_t*)planes[2]) + j * vRowBytes + i; + *uLoc = u; + *vLoc = v; + } + } + return true; + } else { + SkASSERT(uTex && vTex); + if (!uTex->readPixels(0, 0, sizes[1].fWidth, sizes[1].fHeight, + kAlpha_8_GrPixelConfig, planes[1], rowBytes[1])) { + return false; + } + if (!vTex->readPixels(0, 0, sizes[2].fWidth, sizes[2].fHeight, + kAlpha_8_GrPixelConfig, planes[2], rowBytes[2])) { + return false; + } + return true; + } + } + } + return false; +} diff --git a/src/gpu/GrTextureToYUVPlanes.h b/src/gpu/GrTextureToYUVPlanes.h new file mode 100644 index 0000000000..67b6fce2eb --- /dev/null +++ b/src/gpu/GrTextureToYUVPlanes.h @@ -0,0 +1,19 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrTextureToYUVPlanes_DEFINED +#define GrTextureToYUVPlanes_DEFINED + +#include "SkImageInfo.h" +#include "SkSize.h" + +class GrTexture; + +bool GrTextureToYUVPlanes(GrTexture* texture, const SkISize[3], void* const planes[3], + const size_t rowBytes[3], SkYUVColorSpace); + +#endif diff --git a/src/gpu/GrYUVProvider.cpp b/src/gpu/GrYUVProvider.cpp index 014c30574a..844849a575 100644 --- a/src/gpu/GrYUVProvider.cpp +++ b/src/gpu/GrYUVProvider.cpp @@ -8,7 +8,7 @@ #include "GrContext.h" #include "GrDrawContext.h" #include "GrYUVProvider.h" -#include "effects/GrYUVtoRGBEffect.h" +#include "effects/GrYUVEffect.h" #include "SkCachedData.h" #include "SkRefCnt.h" @@ -121,12 +121,12 @@ GrTexture* GrYUVProvider::refAsTexture(GrContext* ctx, const GrSurfaceDesc& desc SkASSERT(renderTarget); GrPaint paint; - SkAutoTUnref<GrFragmentProcessor> yuvToRgbProcessor( - GrYUVtoRGBEffect::Create(yuvTextures[0], - yuvTextures[1], - yuvTextures[2], - yuvInfo.fSize, - yuvInfo.fColorSpace)); + SkAutoTUnref<const GrFragmentProcessor> yuvToRgbProcessor( + GrYUVEffect::CreateYUVToRGB(yuvTextures[0], + yuvTextures[1], + yuvTextures[2], + yuvInfo.fSize, + yuvInfo.fColorSpace)); paint.addColorFragmentProcessor(yuvToRgbProcessor); paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode); const SkRect r = SkRect::MakeIWH(yuvInfo.fSize[0].fWidth, yuvInfo.fSize[0].fHeight); diff --git a/src/gpu/SkGr.cpp b/src/gpu/SkGr.cpp index 954bb80852..62b7ff14ea 100644 --- a/src/gpu/SkGr.cpp +++ b/src/gpu/SkGr.cpp @@ -32,7 +32,7 @@ #include "effects/GrDitherEffect.h" #include "effects/GrPorterDuffXferProcessor.h" #include "effects/GrXfermodeFragmentProcessor.h" -#include "effects/GrYUVtoRGBEffect.h" +#include "effects/GrYUVEffect.h" #ifndef SK_IGNORE_ETC1_SUPPORT # include "ktx.h" diff --git a/src/gpu/effects/GrYUVEffect.cpp b/src/gpu/effects/GrYUVEffect.cpp new file mode 100644 index 0000000000..e4d9d61bdb --- /dev/null +++ b/src/gpu/effects/GrYUVEffect.cpp @@ -0,0 +1,391 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "GrYUVEffect.h" + +#include "GrCoordTransform.h" +#include "GrFragmentProcessor.h" +#include "GrInvariantOutput.h" +#include "GrProcessor.h" +#include "glsl/GrGLSLFragmentProcessor.h" +#include "glsl/GrGLSLFragmentShaderBuilder.h" +#include "glsl/GrGLSLProgramDataManager.h" +#include "glsl/GrGLSLUniformHandler.h" + +namespace { + +static const float kJPEGConversionMatrix[16] = { + 1.0f, 0.0f, 1.402f, -0.701f, + 1.0f, -0.34414f, -0.71414f, 0.529f, + 1.0f, 1.772f, 0.0f, -0.886f, + 0.0f, 0.0f, 0.0f, 1.0 +}; + +static const float kRec601ConversionMatrix[16] = { + 1.164f, 0.0f, 1.596f, -0.87075f, + 1.164f, -0.391f, -0.813f, 0.52925f, + 1.164f, 2.018f, 0.0f, -1.08175f, + 0.0f, 0.0f, 0.0f, 1.0} +; + +static const float kRec709ConversionMatrix[16] = { + 1.164f, 0.0f, 1.793f, -0.96925f, + 1.164f, -0.213f, -0.533f, 0.30025f, + 1.164f, 2.112f, 0.0f, -1.12875f, + 0.0f, 0.0f, 0.0f, 1.0f} +; + +static const float kJPEGInverseConversionMatrix[16] = { + 0.299001f, 0.586998f, 0.114001f, 0.0000821798f, + -0.168736f, -0.331263f, 0.499999f, 0.499954f, + 0.499999f, -0.418686f, -0.0813131f, 0.499941f, + 0.f, 0.f, 0.f, 1.f +}; + +static const float kRec601InverseConversionMatrix[16] = { + 0.256951f, 0.504421f, 0.0977346f, 0.0625f, + -0.148212f, -0.290954f, 0.439166f, 0.5f, + 0.439166f, -0.367886f, -0.0712802f, 0.5f, + 0.f, 0.f, 0.f, 1.f +}; + +static const float kRec709InverseConversionMatrix[16] = { + 0.182663f, 0.614473f, 0.061971f, 0.0625f, + -0.100672f, -0.338658f, 0.43933f, 0.5f, + 0.439142f, -0.39891f, -0.040231f, 0.5f, + 0.f, 0.f, 0.f, 1. +}; + +class YUVtoRGBEffect : public GrFragmentProcessor { +public: + static GrFragmentProcessor* Create(GrTexture* yTexture, GrTexture* uTexture, + GrTexture* vTexture, const SkISize sizes[3], + SkYUVColorSpace colorSpace) { + SkScalar w[3], h[3]; + w[0] = SkIntToScalar(sizes[0].fWidth) / SkIntToScalar(yTexture->width()); + h[0] = SkIntToScalar(sizes[0].fHeight) / SkIntToScalar(yTexture->height()); + w[1] = SkIntToScalar(sizes[1].fWidth) / SkIntToScalar(uTexture->width()); + h[1] = SkIntToScalar(sizes[1].fHeight) / SkIntToScalar(uTexture->height()); + w[2] = SkIntToScalar(sizes[2].fWidth) / SkIntToScalar(vTexture->width()); + h[2] = SkIntToScalar(sizes[2].fHeight) / SkIntToScalar(vTexture->height()); + SkMatrix yuvMatrix[3]; + yuvMatrix[0] = GrCoordTransform::MakeDivByTextureWHMatrix(yTexture); + yuvMatrix[1] = yuvMatrix[0]; + yuvMatrix[1].preScale(w[1] / w[0], h[1] / h[0]); + yuvMatrix[2] = yuvMatrix[0]; + yuvMatrix[2].preScale(w[2] / w[0], h[2] / h[0]); + GrTextureParams::FilterMode uvFilterMode = + ((sizes[1].fWidth != sizes[0].fWidth) || + (sizes[1].fHeight != sizes[0].fHeight) || + (sizes[2].fWidth != sizes[0].fWidth) || + (sizes[2].fHeight != sizes[0].fHeight)) ? + GrTextureParams::kBilerp_FilterMode : + GrTextureParams::kNone_FilterMode; + return new YUVtoRGBEffect(yTexture, uTexture, vTexture, yuvMatrix, uvFilterMode, + colorSpace); + } + + const char* name() const override { return "YUV to RGB"; } + + SkYUVColorSpace getColorSpace() const { return fColorSpace; } + + class GLSLProcessor : public GrGLSLFragmentProcessor { + public: + // this class always generates the same code. + static void GenKey(const GrProcessor&, const GrGLSLCaps&, GrProcessorKeyBuilder*) {} + + GLSLProcessor(const GrProcessor&) {} + + void emitCode(EmitArgs& args) override { + GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; + + const char* colorSpaceMatrix = nullptr; + fMatrixUni = args.fUniformHandler->addUniform( + GrGLSLUniformHandler::kFragment_Visibility, + kMat44f_GrSLType, kDefault_GrSLPrecision, + "ColorSpaceMatrix", &colorSpaceMatrix); + fragBuilder->codeAppendf("%s = vec4(", args.fOutputColor); + fragBuilder->appendTextureLookup(args.fSamplers[0], args.fCoords[0].c_str(), + args.fCoords[0].getType()); + fragBuilder->codeAppend(".r,"); + fragBuilder->appendTextureLookup(args.fSamplers[1], args.fCoords[1].c_str(), + args.fCoords[1].getType()); + fragBuilder->codeAppend(".r,"); + fragBuilder->appendTextureLookup(args.fSamplers[2], args.fCoords[2].c_str(), + args.fCoords[2].getType()); + fragBuilder->codeAppendf(".r, 1.0) * %s;", colorSpaceMatrix); + } + + protected: + void onSetData(const GrGLSLProgramDataManager& pdman, + const GrProcessor& processor) override { + const YUVtoRGBEffect& yuvEffect = processor.cast<YUVtoRGBEffect>(); + switch (yuvEffect.getColorSpace()) { + case kJPEG_SkYUVColorSpace: + pdman.setMatrix4f(fMatrixUni, kJPEGConversionMatrix); + break; + case kRec601_SkYUVColorSpace: + pdman.setMatrix4f(fMatrixUni, kRec601ConversionMatrix); + break; + case kRec709_SkYUVColorSpace: + pdman.setMatrix4f(fMatrixUni, kRec709ConversionMatrix); + break; + } + } + + private: + GrGLSLProgramDataManager::UniformHandle fMatrixUni; + + typedef GrGLSLFragmentProcessor INHERITED; + }; + +private: + YUVtoRGBEffect(GrTexture* yTexture, GrTexture* uTexture, GrTexture* vTexture, + const SkMatrix yuvMatrix[3], GrTextureParams::FilterMode uvFilterMode, + SkYUVColorSpace colorSpace) + : fYTransform(kLocal_GrCoordSet, yuvMatrix[0], yTexture, GrTextureParams::kNone_FilterMode) + , fYAccess(yTexture) + , fUTransform(kLocal_GrCoordSet, yuvMatrix[1], uTexture, uvFilterMode) + , fUAccess(uTexture, uvFilterMode) + , fVTransform(kLocal_GrCoordSet, yuvMatrix[2], vTexture, uvFilterMode) + , fVAccess(vTexture, uvFilterMode) + , fColorSpace(colorSpace) { + this->initClassID<YUVtoRGBEffect>(); + this->addCoordTransform(&fYTransform); + this->addTextureAccess(&fYAccess); + this->addCoordTransform(&fUTransform); + this->addTextureAccess(&fUAccess); + this->addCoordTransform(&fVTransform); + this->addTextureAccess(&fVAccess); + } + + GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { + return new GLSLProcessor(*this); + } + + void onGetGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override { + GLSLProcessor::GenKey(*this, caps, b); + } + + bool onIsEqual(const GrFragmentProcessor& sBase) const override { + const YUVtoRGBEffect& s = sBase.cast<YUVtoRGBEffect>(); + return fColorSpace == s.getColorSpace(); + } + + void onComputeInvariantOutput(GrInvariantOutput* inout) const override { + // YUV is opaque + inout->setToOther(kA_GrColorComponentFlag, 0xFF << GrColor_SHIFT_A, + GrInvariantOutput::kWillNot_ReadInput); + } + + GrCoordTransform fYTransform; + GrTextureAccess fYAccess; + GrCoordTransform fUTransform; + GrTextureAccess fUAccess; + GrCoordTransform fVTransform; + GrTextureAccess fVAccess; + SkYUVColorSpace fColorSpace; + + typedef GrFragmentProcessor INHERITED; +}; + + +class RGBToYUVEffect : public GrFragmentProcessor { +public: + enum OutputChannels { + // output color r = y, g = u, b = v, a = a + kYUV_OutputChannels, + // output color rgba = y + kY_OutputChannels, + // output color r = u, g = v, b = 0, a = a + kUV_OutputChannels, + // output color rgba = u + kU_OutputChannels, + // output color rgba = v + kV_OutputChannels + }; + + RGBToYUVEffect(const GrFragmentProcessor* rgbFP, SkYUVColorSpace colorSpace, + OutputChannels output) + : fColorSpace(colorSpace) + , fOutputChannels(output) { + this->initClassID<RGBToYUVEffect>(); + this->registerChildProcessor(rgbFP); + } + + const char* name() const override { return "RGBToYUV"; } + + SkYUVColorSpace getColorSpace() const { return fColorSpace; } + + OutputChannels outputChannels() const { return fOutputChannels; } + + class GLSLProcessor : public GrGLSLFragmentProcessor { + public: + GLSLProcessor(const GrProcessor&) : fLastColorSpace(-1), fLastOutputChannels(-1) {} + + void emitCode(EmitArgs& args) override { + GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; + OutputChannels oc = args.fFp.cast<RGBToYUVEffect>().outputChannels(); + + SkString outputColor("rgbColor"); + this->emitChild(0, args.fInputColor, &outputColor, args); + + const char* uniName; + switch (oc) { + case kYUV_OutputChannels: + fRGBToYUVUni = args.fUniformHandler->addUniformArray( + GrGLSLUniformHandler::kFragment_Visibility, + kVec4f_GrSLType, kDefault_GrSLPrecision, + "RGBToYUV", 3, &uniName); + fragBuilder->codeAppendf("%s = vec4(dot(rgbColor.rgb, %s[0].rgb) + %s[0].a," + "dot(rgbColor.rgb, %s[1].rgb) + %s[1].a," + "dot(rgbColor.rgb, %s[2].rgb) + %s[2].a," + "rgbColor.a);", + args.fOutputColor, uniName, uniName, uniName, uniName, + uniName, uniName); + break; + case kUV_OutputChannels: + fRGBToYUVUni = args.fUniformHandler->addUniformArray( + GrGLSLUniformHandler::kFragment_Visibility, + kVec4f_GrSLType, kDefault_GrSLPrecision, + "RGBToUV", 2, &uniName); + fragBuilder->codeAppendf("%s = vec4(dot(rgbColor.rgb, %s[0].rgb) + %s[0].a," + "dot(rgbColor.rgb, %s[1].rgb) + %s[1].a," + "0.0," + "rgbColor.a);", + args.fOutputColor, uniName, uniName, uniName, uniName); + break; + case kY_OutputChannels: + case kU_OutputChannels: + case kV_OutputChannels: + fRGBToYUVUni = args.fUniformHandler->addUniform( + GrGLSLUniformHandler::kFragment_Visibility, + kVec4f_GrSLType, kDefault_GrSLPrecision, + "RGBToYUorV", &uniName); + fragBuilder->codeAppendf("%s = vec4(dot(rgbColor.rgb, %s.rgb) + %s.a);\n", + args.fOutputColor, uniName, uniName); + break; + } + } + + private: + void onSetData(const GrGLSLProgramDataManager& pdman, + const GrProcessor& processor) override { + const RGBToYUVEffect& effect = processor.cast<RGBToYUVEffect>(); + OutputChannels oc = effect.outputChannels(); + if (effect.getColorSpace() != fLastColorSpace || oc != fLastOutputChannels) { + + const float* matrix = nullptr; + switch (effect.getColorSpace()) { + case kJPEG_SkYUVColorSpace: + matrix = kJPEGInverseConversionMatrix; + break; + case kRec601_SkYUVColorSpace: + matrix = kRec601InverseConversionMatrix; + break; + case kRec709_SkYUVColorSpace: + matrix = kRec709InverseConversionMatrix; + break; + } + switch (oc) { + case kYUV_OutputChannels: + pdman.set4fv(fRGBToYUVUni, 3, matrix); + break; + case kUV_OutputChannels: + pdman.set4fv(fRGBToYUVUni, 2, matrix + 4); + break; + case kY_OutputChannels: + pdman.set4fv(fRGBToYUVUni, 1, matrix); + break; + case kU_OutputChannels: + pdman.set4fv(fRGBToYUVUni, 1, matrix + 4); + break; + case kV_OutputChannels: + pdman.set4fv(fRGBToYUVUni, 1, matrix + 8); + break; + } + fLastColorSpace = effect.getColorSpace(); + } + } + GrGLSLProgramDataManager::UniformHandle fRGBToYUVUni; + int fLastColorSpace; + int fLastOutputChannels; + + typedef GrGLSLFragmentProcessor INHERITED; + }; + +private: + GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { + return new GLSLProcessor(*this); + } + + void onGetGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override { + // kY, kU, and kV all generate the same code, just upload different coefficients. + if (kU_OutputChannels == fOutputChannels || kV_OutputChannels == fOutputChannels) { + b->add32(kY_OutputChannels); + } else { + b->add32(fOutputChannels); + } + } + + bool onIsEqual(const GrFragmentProcessor& sBase) const override { + const RGBToYUVEffect& s = sBase.cast<RGBToYUVEffect>(); + return fColorSpace == s.getColorSpace() && fOutputChannels == s.outputChannels(); + } + + void onComputeInvariantOutput(GrInvariantOutput* inout) const override { + inout->setToUnknown(GrInvariantOutput::kWillNot_ReadInput); + } + + GrCoordTransform fTransform; + GrTextureAccess fAccess; + SkYUVColorSpace fColorSpace; + OutputChannels fOutputChannels; + + typedef GrFragmentProcessor INHERITED; +}; + +} + +////////////////////////////////////////////////////////////////////////////// + +const GrFragmentProcessor* +GrYUVEffect::CreateYUVToRGB(GrTexture* yTexture, GrTexture* uTexture, GrTexture* vTexture, + const SkISize sizes[3], SkYUVColorSpace colorSpace) { + SkASSERT(yTexture && uTexture && vTexture && sizes); + return YUVtoRGBEffect::Create(yTexture, uTexture, vTexture, sizes, colorSpace); +} + +const GrFragmentProcessor* +GrYUVEffect::CreateRGBToYUV(const GrFragmentProcessor* rgbFP, SkYUVColorSpace colorSpace) { + SkASSERT(rgbFP); + return new RGBToYUVEffect(rgbFP, colorSpace, RGBToYUVEffect::kYUV_OutputChannels); +} + +const GrFragmentProcessor* +GrYUVEffect::CreateRGBToY(const GrFragmentProcessor* rgbFP, SkYUVColorSpace colorSpace) { + SkASSERT(rgbFP); + return new RGBToYUVEffect(rgbFP, colorSpace, RGBToYUVEffect::kY_OutputChannels); +} + +const GrFragmentProcessor* +GrYUVEffect::CreateRGBToUV(const GrFragmentProcessor* rgbFP, SkYUVColorSpace colorSpace) { + SkASSERT(rgbFP); + return new RGBToYUVEffect(rgbFP, colorSpace, RGBToYUVEffect::kUV_OutputChannels); +} + +const GrFragmentProcessor* +GrYUVEffect::CreateRGBToU(const GrFragmentProcessor* rgbFP, SkYUVColorSpace colorSpace) { + SkASSERT(rgbFP); + return new RGBToYUVEffect(rgbFP, colorSpace, RGBToYUVEffect::kU_OutputChannels); +} + +const GrFragmentProcessor* +GrYUVEffect::CreateRGBToV(const GrFragmentProcessor* rgbFP, SkYUVColorSpace colorSpace) { + SkASSERT(rgbFP); + return new RGBToYUVEffect(rgbFP, colorSpace, RGBToYUVEffect::kV_OutputChannels); +} diff --git a/src/gpu/effects/GrYUVEffect.h b/src/gpu/effects/GrYUVEffect.h new file mode 100644 index 0000000000..4f3848e532 --- /dev/null +++ b/src/gpu/effects/GrYUVEffect.h @@ -0,0 +1,50 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrYUVEffect_DEFINED +#define GrYUVEffect_DEFINED + +#include "SkImageInfo.h" + +class GrFragmentProcessor; +class GrTexture; + +namespace GrYUVEffect { + /** + * Creates an effect that performs color conversion from YUV to RGB. The input textures are + * assumed to be kA8_GrPixelConfig. + */ + const GrFragmentProcessor* CreateYUVToRGB(GrTexture* yTexture, GrTexture* uTexture, + GrTexture* vTexture, const SkISize sizes[3], + SkYUVColorSpace colorSpace); + + /** + * Creates a processor that performs color conversion from the passed in processor's RGB + * channels to Y, U ,and V channels. The output color is (y, u, v, a) where a is the passed in + * processor's alpha output. + */ + const GrFragmentProcessor* CreateRGBToYUV(const GrFragmentProcessor*, + SkYUVColorSpace colorSpace); + + /** + * Creates a processor that performs color conversion from the passed in processor's RGB + * channels to U and V channels. The output color is (u, v, 0, a) where a is the passed in + * processor's alpha output. + */ + const GrFragmentProcessor* CreateRGBToUV(const GrFragmentProcessor*, + SkYUVColorSpace colorSpace); + /** + * Creates a processor that performs color conversion from the passed in fragment processors's + * RGB channels to Y, U, or V (replicated across all four output color channels). The alpha + * output of the passed in fragment processor is ignored. + */ + const GrFragmentProcessor* CreateRGBToY(const GrFragmentProcessor*, SkYUVColorSpace colorSpace); + const GrFragmentProcessor* CreateRGBToU(const GrFragmentProcessor*, SkYUVColorSpace colorSpace); + const GrFragmentProcessor* CreateRGBToV(const GrFragmentProcessor*, SkYUVColorSpace colorSpace); +}; + +#endif diff --git a/src/gpu/effects/GrYUVtoRGBEffect.cpp b/src/gpu/effects/GrYUVtoRGBEffect.cpp deleted file mode 100644 index 16dad8635c..0000000000 --- a/src/gpu/effects/GrYUVtoRGBEffect.cpp +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright 2014 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "GrYUVtoRGBEffect.h" - -#include "GrCoordTransform.h" -#include "GrFragmentProcessor.h" -#include "GrInvariantOutput.h" -#include "GrProcessor.h" -#include "glsl/GrGLSLFragmentProcessor.h" -#include "glsl/GrGLSLFragmentShaderBuilder.h" -#include "glsl/GrGLSLProgramDataManager.h" -#include "glsl/GrGLSLUniformHandler.h" - -namespace { - -class YUVtoRGBEffect : public GrFragmentProcessor { -public: - static GrFragmentProcessor* Create(GrTexture* yTexture, GrTexture* uTexture, - GrTexture* vTexture, const SkISize sizes[3], - SkYUVColorSpace colorSpace) { - SkScalar w[3], h[3]; - w[0] = SkIntToScalar(sizes[0].fWidth) / SkIntToScalar(yTexture->width()); - h[0] = SkIntToScalar(sizes[0].fHeight) / SkIntToScalar(yTexture->height()); - w[1] = SkIntToScalar(sizes[1].fWidth) / SkIntToScalar(uTexture->width()); - h[1] = SkIntToScalar(sizes[1].fHeight) / SkIntToScalar(uTexture->height()); - w[2] = SkIntToScalar(sizes[2].fWidth) / SkIntToScalar(vTexture->width()); - h[2] = SkIntToScalar(sizes[2].fHeight) / SkIntToScalar(vTexture->height()); - SkMatrix yuvMatrix[3]; - yuvMatrix[0] = GrCoordTransform::MakeDivByTextureWHMatrix(yTexture); - yuvMatrix[1] = yuvMatrix[0]; - yuvMatrix[1].preScale(w[1] / w[0], h[1] / h[0]); - yuvMatrix[2] = yuvMatrix[0]; - yuvMatrix[2].preScale(w[2] / w[0], h[2] / h[0]); - GrTextureParams::FilterMode uvFilterMode = - ((sizes[1].fWidth != sizes[0].fWidth) || - (sizes[1].fHeight != sizes[0].fHeight) || - (sizes[2].fWidth != sizes[0].fWidth) || - (sizes[2].fHeight != sizes[0].fHeight)) ? - GrTextureParams::kBilerp_FilterMode : - GrTextureParams::kNone_FilterMode; - return new YUVtoRGBEffect(yTexture, uTexture, vTexture, yuvMatrix, uvFilterMode, - colorSpace); - } - - const char* name() const override { return "YUV to RGB"; } - - SkYUVColorSpace getColorSpace() const { - return fColorSpace; - } - - class GLSLProcessor : public GrGLSLFragmentProcessor { - public: - static const float kJPEGConversionMatrix[16]; - static const float kRec601ConversionMatrix[16]; - static const float kRec709ConversionMatrix[16]; - - // this class always generates the same code. - static void GenKey(const GrProcessor&, const GrGLSLCaps&, GrProcessorKeyBuilder*) {} - - GLSLProcessor(const GrProcessor&) {} - - virtual void emitCode(EmitArgs& args) override { - GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; - - const char* yuvMatrix = nullptr; - fMatrixUni = args.fUniformHandler->addUniform( - GrGLSLUniformHandler::kFragment_Visibility, - kMat44f_GrSLType, kDefault_GrSLPrecision, - "YUVMatrix", &yuvMatrix); - fragBuilder->codeAppendf("\t%s = vec4(\n\t\t", args.fOutputColor); - fragBuilder->appendTextureLookup(args.fSamplers[0], args.fCoords[0].c_str(), - args.fCoords[0].getType()); - fragBuilder->codeAppend(".r,\n\t\t"); - fragBuilder->appendTextureLookup(args.fSamplers[1], args.fCoords[1].c_str(), - args.fCoords[1].getType()); - fragBuilder->codeAppend(".r,\n\t\t"); - fragBuilder->appendTextureLookup(args.fSamplers[2], args.fCoords[2].c_str(), - args.fCoords[2].getType()); - fragBuilder->codeAppendf(".r,\n\t\t1.0) * %s;\n", yuvMatrix); - } - - protected: - virtual void onSetData(const GrGLSLProgramDataManager& pdman, - const GrProcessor& processor) override { - const YUVtoRGBEffect& yuvEffect = processor.cast<YUVtoRGBEffect>(); - switch (yuvEffect.getColorSpace()) { - case kJPEG_SkYUVColorSpace: - pdman.setMatrix4f(fMatrixUni, kJPEGConversionMatrix); - break; - case kRec601_SkYUVColorSpace: - pdman.setMatrix4f(fMatrixUni, kRec601ConversionMatrix); - break; - case kRec709_SkYUVColorSpace: - pdman.setMatrix4f(fMatrixUni, kRec709ConversionMatrix); - break; - } - } - - private: - GrGLSLProgramDataManager::UniformHandle fMatrixUni; - - typedef GrGLSLFragmentProcessor INHERITED; - }; - -private: - YUVtoRGBEffect(GrTexture* yTexture, GrTexture* uTexture, GrTexture* vTexture, - const SkMatrix yuvMatrix[3], GrTextureParams::FilterMode uvFilterMode, - SkYUVColorSpace colorSpace) - : fYTransform(kLocal_GrCoordSet, yuvMatrix[0], yTexture, GrTextureParams::kNone_FilterMode) - , fYAccess(yTexture) - , fUTransform(kLocal_GrCoordSet, yuvMatrix[1], uTexture, uvFilterMode) - , fUAccess(uTexture, uvFilterMode) - , fVTransform(kLocal_GrCoordSet, yuvMatrix[2], vTexture, uvFilterMode) - , fVAccess(vTexture, uvFilterMode) - , fColorSpace(colorSpace) { - this->initClassID<YUVtoRGBEffect>(); - this->addCoordTransform(&fYTransform); - this->addTextureAccess(&fYAccess); - this->addCoordTransform(&fUTransform); - this->addTextureAccess(&fUAccess); - this->addCoordTransform(&fVTransform); - this->addTextureAccess(&fVAccess); - } - - GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { - return new GLSLProcessor(*this); - } - - virtual void onGetGLSLProcessorKey(const GrGLSLCaps& caps, - GrProcessorKeyBuilder* b) const override { - GLSLProcessor::GenKey(*this, caps, b); - } - - bool onIsEqual(const GrFragmentProcessor& sBase) const override { - const YUVtoRGBEffect& s = sBase.cast<YUVtoRGBEffect>(); - return fColorSpace == s.getColorSpace(); - } - - void onComputeInvariantOutput(GrInvariantOutput* inout) const override { - // YUV is opaque - inout->setToOther(kA_GrColorComponentFlag, 0xFF << GrColor_SHIFT_A, - GrInvariantOutput::kWillNot_ReadInput); - } - - GrCoordTransform fYTransform; - GrTextureAccess fYAccess; - GrCoordTransform fUTransform; - GrTextureAccess fUAccess; - GrCoordTransform fVTransform; - GrTextureAccess fVAccess; - SkYUVColorSpace fColorSpace; - - typedef GrFragmentProcessor INHERITED; -}; - -const float YUVtoRGBEffect::GLSLProcessor::kJPEGConversionMatrix[16] = { - 1.0f, 0.0f, 1.402f, -0.701f, - 1.0f, -0.34414f, -0.71414f, 0.529f, - 1.0f, 1.772f, 0.0f, -0.886f, - 0.0f, 0.0f, 0.0f, 1.0}; -const float YUVtoRGBEffect::GLSLProcessor::kRec601ConversionMatrix[16] = { - 1.164f, 0.0f, 1.596f, -0.87075f, - 1.164f, -0.391f, -0.813f, 0.52925f, - 1.164f, 2.018f, 0.0f, -1.08175f, - 0.0f, 0.0f, 0.0f, 1.0}; -const float YUVtoRGBEffect::GLSLProcessor::kRec709ConversionMatrix[16] = { - 1.164f, 0.0f, 1.793f, -0.96925f, - 1.164f, -0.213f, -0.533f, 0.30025f, - 1.164f, 2.112f, 0.0f, -1.12875f, - 0.0f, 0.0f, 0.0f, 1.0f}; -} - -////////////////////////////////////////////////////////////////////////////// - -GrFragmentProcessor* -GrYUVtoRGBEffect::Create(GrTexture* yTexture, GrTexture* uTexture, GrTexture* vTexture, - 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 deleted file mode 100644 index 03679788dd..0000000000 --- a/src/gpu/effects/GrYUVtoRGBEffect.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2014 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef GrYUVtoRGBEffect_DEFINED -#define GrYUVtoRGBEffect_DEFINED - -#include "SkImageInfo.h" - -class GrFragmentProcessor; -class GrTexture; - -namespace GrYUVtoRGBEffect { - /** - * Creates an effect that performs color conversion from YUV to RGB - */ - GrFragmentProcessor* Create(GrTexture* yTexture, GrTexture* uTexture, GrTexture* vTexture, - const SkISize sizes[3], SkYUVColorSpace colorSpace); -}; - -#endif |