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 | |
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
-rw-r--r-- | gm/imagetoyuvplanes.cpp | 111 | ||||
-rw-r--r-- | gm/yuvtorgbeffect.cpp | 14 | ||||
-rw-r--r-- | gyp/gpu.gypi | 6 | ||||
-rw-r--r-- | gyp/utils.gypi | 4 | ||||
-rw-r--r-- | include/core/SkImage.h | 7 | ||||
-rw-r--r-- | src/core/SkColorMatrixFilterRowMajor255.cpp | 21 | ||||
-rw-r--r-- | src/core/SkColorMatrixFilterRowMajor255.h | 7 | ||||
-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 | ||||
-rw-r--r-- | src/image/SkImage.cpp | 18 | ||||
-rw-r--r-- | src/image/SkImage_Gpu.cpp | 6 | ||||
-rw-r--r-- | src/utils/SkRGBAToYUV.cpp | 58 | ||||
-rw-r--r-- | src/utils/SkRGBAToYUV.h | 21 |
19 files changed, 970 insertions, 236 deletions
diff --git a/gm/imagetoyuvplanes.cpp b/gm/imagetoyuvplanes.cpp new file mode 100644 index 0000000000..4883a682f8 --- /dev/null +++ b/gm/imagetoyuvplanes.cpp @@ -0,0 +1,111 @@ +/* + * 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 <SkSurface.h> +#include "gm.h" +#include "SkBitmap.h" +#include "SkGradientShader.h" +#include "SkImage.h" + +static SkImage* create_image(GrContext* context, int width, int height) { + SkAutoTUnref<SkSurface> surface; + SkImageInfo info = SkImageInfo::MakeN32Premul(width, height); + if (context) { + surface.reset(SkSurface::NewRenderTarget(context, SkSurface::kYes_Budgeted, info, 0)); + } else { + surface.reset(SkSurface::NewRaster(info)); + } + if (!surface) { + return nullptr; + } + // Create an RGB image from which we will extract planes + SkPaint paint; + static const SkColor kColors[] = + { SK_ColorBLUE, SK_ColorYELLOW, SK_ColorGREEN, SK_ColorWHITE }; + SkScalar r = (width + height) / 4.f; + paint.setShader(SkGradientShader::CreateRadial(SkPoint::Make(0,0), r, kColors, + nullptr, SK_ARRAY_COUNT(kColors), + SkShader::kMirror_TileMode))->unref(); + + surface->getCanvas()->drawPaint(paint); + return surface->newImageSnapshot(); +} + +DEF_SIMPLE_GM(image_to_yuv_planes, canvas, 120, 525) { + static const SkScalar kPad = 5.f; + static const int kImageSize = 32; + + GrContext *context = canvas->getGrContext(); + SkAutoTUnref<SkImage> rgbImage(create_image(context, kImageSize, kImageSize)); + if (!rgbImage) { + return; + } + + canvas->drawImage(rgbImage, kPad, kPad); + // Test cases where all three planes are the same size, where just u and v are the same size, + // and where all differ. + static const SkISize kSizes[][3] = { + {{kImageSize, kImageSize}, {kImageSize , kImageSize }, {kImageSize, kImageSize }}, + {{kImageSize, kImageSize}, {kImageSize/2, kImageSize/2}, {kImageSize/2, kImageSize/2}}, + {{kImageSize, kImageSize}, {kImageSize/2, kImageSize/2}, {kImageSize/3, kImageSize/3}} + }; + + // A mix of rowbytes triples to go with the above sizes. + static const size_t kRowBytes[][3] { + {0, 0, 0}, + {kImageSize, kImageSize/2 + 1, kImageSize}, + {kImageSize + 13, kImageSize, kImageSize/3 + 8} + }; + + + SkScalar x = kPad; + for (size_t s = 0; s < SK_ARRAY_COUNT(kSizes); ++s) { + SkScalar y = rgbImage->height() + 2 * kPad; + + const SkISize *sizes = kSizes[s]; + size_t realRowBytes[3]; + for (int i = 0; i < 3; ++i) { + realRowBytes[i] = kRowBytes[s][i] ? kRowBytes[s][i] : kSizes[s][i].fWidth; + } + SkAutoTDeleteArray<uint8_t> yPlane(new uint8_t[realRowBytes[0] * sizes[0].fHeight]); + SkAutoTDeleteArray<uint8_t> uPlane(new uint8_t[realRowBytes[1] * sizes[1].fHeight]); + SkAutoTDeleteArray<uint8_t> vPlane(new uint8_t[realRowBytes[2] * sizes[2].fHeight]); + + void *planes[3] = {yPlane.get(), uPlane.get(), vPlane.get()}; + + // Convert the RGB image to YUV planes using each YUV color space and draw the YUV planes + // to the canvas. + SkBitmap yuvBmps[3]; + yuvBmps[0].setInfo(SkImageInfo::MakeA8(sizes[0].fWidth, sizes[0].fHeight), kRowBytes[s][0]); + yuvBmps[1].setInfo(SkImageInfo::MakeA8(sizes[1].fWidth, sizes[1].fHeight), kRowBytes[s][1]); + yuvBmps[2].setInfo(SkImageInfo::MakeA8(sizes[2].fWidth, sizes[2].fHeight), kRowBytes[s][2]); + yuvBmps[0].setPixels(yPlane.get()); + yuvBmps[1].setPixels(uPlane.get()); + yuvBmps[2].setPixels(vPlane.get()); + + for (int space = kJPEG_SkYUVColorSpace; space <= kLastEnum_SkYUVColorSpace; ++space) { + // Clear the planes so we don't accidentally see the old values if there is a bug in + // readYUV8Planes(). + memset(yPlane.get(), 0, realRowBytes[0] * sizes[0].fHeight); + memset(uPlane.get(), 0, realRowBytes[1] * sizes[1].fHeight); + memset(vPlane.get(), 0, realRowBytes[2] * sizes[2].fHeight); + if (rgbImage->readYUV8Planes(sizes, planes, kRowBytes[s], + static_cast<SkYUVColorSpace>(space))) { + yuvBmps[0].notifyPixelsChanged(); + yuvBmps[1].notifyPixelsChanged(); + yuvBmps[2].notifyPixelsChanged(); + for (int i = 0; i < 3; ++i) { + canvas->drawBitmap(yuvBmps[i], x, y); + y += kPad + yuvBmps[i].height(); + } + } + } + + x += rgbImage->width() + kPad; + } +} diff --git a/gm/yuvtorgbeffect.cpp b/gm/yuvtorgbeffect.cpp index 18d799c3f7..bf4094cf4f 100644 --- a/gm/yuvtorgbeffect.cpp +++ b/gm/yuvtorgbeffect.cpp @@ -20,7 +20,7 @@ #include "SkGradientShader.h" #include "batches/GrDrawBatch.h" #include "batches/GrRectBatchFactory.h" -#include "effects/GrYUVtoRGBEffect.h" +#include "effects/GrYUVEffect.h" #define YSIZE 8 #define USIZE 4 @@ -118,12 +118,12 @@ protected: GrPipelineBuilder pipelineBuilder; pipelineBuilder.setXPFactory( GrPorterDuffXPFactory::Create(SkXfermode::kSrc_Mode))->unref(); - SkAutoTUnref<GrFragmentProcessor> fp( - GrYUVtoRGBEffect::Create(texture[indices[i][0]], - texture[indices[i][1]], - texture[indices[i][2]], - sizes, - static_cast<SkYUVColorSpace>(space))); + SkAutoTUnref<const GrFragmentProcessor> fp( + GrYUVEffect::CreateYUVToRGB(texture[indices[i][0]], + texture[indices[i][1]], + texture[indices[i][2]], + sizes, + static_cast<SkYUVColorSpace>(space))); if (fp) { SkMatrix viewMatrix; viewMatrix.setTranslate(x, y); diff --git a/gyp/gpu.gypi b/gyp/gpu.gypi index 2fa0e8700d..e955445c8c 100644 --- a/gyp/gpu.gypi +++ b/gyp/gpu.gypi @@ -182,6 +182,8 @@ '<(skia_src_path)/gpu/GrTextureParamsAdjuster.cpp', '<(skia_src_path)/gpu/GrTextureProvider.cpp', '<(skia_src_path)/gpu/GrTexturePriv.h', + '<(skia_src_path)/gpu/GrTextureToYUVPlanes.cpp', + '<(skia_src_path)/gpu/GrTextureToYUVPlanes.h', '<(skia_src_path)/gpu/GrTextureAccess.cpp', '<(skia_src_path)/gpu/GrTransferBuffer.h', '<(skia_src_path)/gpu/GrTRecorder.h', @@ -285,8 +287,8 @@ '<(skia_src_path)/gpu/effects/GrTextureStripAtlas.cpp', '<(skia_src_path)/gpu/effects/GrTextureStripAtlas.h', '<(skia_src_path)/gpu/effects/GrXfermodeFragmentProcessor.cpp', - '<(skia_src_path)/gpu/effects/GrYUVtoRGBEffect.cpp', - '<(skia_src_path)/gpu/effects/GrYUVtoRGBEffect.h', + '<(skia_src_path)/gpu/effects/GrYUVEffect.cpp', + '<(skia_src_path)/gpu/effects/GrYUVEffect.h', # text '<(skia_src_path)/gpu/text/GrAtlasTextBlob.cpp', diff --git a/gyp/utils.gypi b/gyp/utils.gypi index 3f6c995e67..7af1fd6f34 100644 --- a/gyp/utils.gypi +++ b/gyp/utils.gypi @@ -73,9 +73,11 @@ '<(skia_src_path)/utils/SkPatchGrid.h', '<(skia_src_path)/utils/SkPatchUtils.cpp', '<(skia_src_path)/utils/SkPatchUtils.h', + '<(skia_src_path)/utils/SkRGBAToYUV.cpp', + '<(skia_src_path)/utils/SkRGBAToYUV.h', + '<(skia_src_path)/utils/SkRTConf.cpp', '<(skia_src_path)/utils/SkSHA1.cpp', '<(skia_src_path)/utils/SkSHA1.h', - '<(skia_src_path)/utils/SkRTConf.cpp', '<(skia_src_path)/utils/SkTextBox.cpp', '<(skia_src_path)/utils/SkTextureCompressor.cpp', '<(skia_src_path)/utils/SkTextureCompressor.h', diff --git a/include/core/SkImage.h b/include/core/SkImage.h index e60902fc07..84f4daf2e8 100644 --- a/include/core/SkImage.h +++ b/include/core/SkImage.h @@ -154,6 +154,13 @@ public: uint32_t uniqueID() const { return fUniqueID; } virtual bool isOpaque() const { return false; } + /** + * Extracts YUV planes from the SkImage and stores them in client-provided memory. The sizes + * planes and rowBytes arrays are ordered [y, u, v]. + */ + bool readYUV8Planes(const SkISize[3], void* const planes[3], const size_t rowBytes[3], + SkYUVColorSpace); + virtual SkShader* newShader(SkShader::TileMode, SkShader::TileMode, const SkMatrix* localMatrix = NULL) const; diff --git a/src/core/SkColorMatrixFilterRowMajor255.cpp b/src/core/SkColorMatrixFilterRowMajor255.cpp index 6fdfa0b446..9106a279c0 100644 --- a/src/core/SkColorMatrixFilterRowMajor255.cpp +++ b/src/core/SkColorMatrixFilterRowMajor255.cpp @@ -32,10 +32,8 @@ static void transpose_to_pmorder(float dst[20], const float src[20]) { } } -// src is [20] but some compilers won't accept __restrict__ on anything -// but an raw pointer or reference -void SkColorMatrixFilterRowMajor255::initState(const SkScalar* SK_RESTRICT src) { - transpose_to_pmorder(fTranspose, src); +void SkColorMatrixFilterRowMajor255::initState() { + transpose_to_pmorder(fTranspose, fMatrix); const float* array = fMatrix; @@ -55,7 +53,7 @@ void SkColorMatrixFilterRowMajor255::initState(const SkScalar* SK_RESTRICT src) SkColorMatrixFilterRowMajor255::SkColorMatrixFilterRowMajor255(const SkScalar array[20]) { memcpy(fMatrix, array, 20 * sizeof(SkScalar)); - this->initState(array); + this->initState(); } uint32_t SkColorMatrixFilterRowMajor255::getFlags() const { @@ -420,3 +418,16 @@ void SkColorMatrixFilterRowMajor255::toString(SkString* str) const { SkColorFilter* SkColorFilter::CreateMatrixFilterRowMajor255(const SkScalar array[20]) { return new SkColorMatrixFilterRowMajor255(array); } + +/////////////////////////////////////////////////////////////////////////////// + +SkColorFilter* SkColorMatrixFilterRowMajor255::CreateSingleChannelOutput(const SkScalar row[5]) { + SkASSERT(row); + SkColorMatrixFilterRowMajor255* cf = new SkColorMatrixFilterRowMajor255(); + static_assert(sizeof(SkScalar) * 5 * 4 == sizeof(cf->fMatrix), "sizes don't match"); + for (int i = 0; i < 4; ++i) { + memcpy(cf->fMatrix + 5 * i, row, sizeof(SkScalar) * 5); + } + cf->initState(); + return cf; +} diff --git a/src/core/SkColorMatrixFilterRowMajor255.h b/src/core/SkColorMatrixFilterRowMajor255.h index 453dd41bff..106d2bd882 100644 --- a/src/core/SkColorMatrixFilterRowMajor255.h +++ b/src/core/SkColorMatrixFilterRowMajor255.h @@ -14,6 +14,9 @@ class SK_API SkColorMatrixFilterRowMajor255 : public SkColorFilter { public: explicit SkColorMatrixFilterRowMajor255(const SkScalar array[20]); + /** Creates a color matrix filter that returns the same value in all four channels. */ + static SkColorFilter* CreateSingleChannelOutput(const SkScalar row[5]); + void filterSpan(const SkPMColor src[], int count, SkPMColor[]) const override; void filterSpan4f(const SkPM4f src[], int count, SkPM4f[]) const override; uint32_t getFlags() const override; @@ -32,11 +35,13 @@ protected: void flatten(SkWriteBuffer&) const override; private: + SkColorMatrixFilterRowMajor255() {}; + SkScalar fMatrix[20]; float fTranspose[20]; // for Sk4s uint32_t fFlags; - void initState(const SkScalar array[20]); + void initState(); typedef SkColorFilter INHERITED; }; 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 diff --git a/src/image/SkImage.cpp b/src/image/SkImage.cpp index 4f31fc5672..7507591ee5 100644 --- a/src/image/SkImage.cpp +++ b/src/image/SkImage.cpp @@ -258,6 +258,24 @@ bool SkImage::readPixels(const SkPixmap& pmap, int srcX, int srcY, CachingHint c return this->readPixels(pmap.info(), pmap.writable_addr(), pmap.rowBytes(), srcX, srcY, chint); } +#if SK_SUPPORT_GPU +#include "GrTextureToYUVPlanes.h" +#endif + +#include "SkRGBAToYUV.h" + +bool SkImage::readYUV8Planes(const SkISize sizes[3], void* const planes[3], + const size_t rowBytes[3], SkYUVColorSpace colorSpace) { +#if SK_SUPPORT_GPU + if (GrTexture* texture = as_IB(this)->peekTexture()) { + if (GrTextureToYUVPlanes(texture, sizes, planes, rowBytes, colorSpace)) { + return true; + } + } +#endif + return SkRGBAToYUV(this, sizes, planes, rowBytes, colorSpace); +} + /////////////////////////////////////////////////////////////////////////////////////////////////// SkImage* SkImage::NewFromBitmap(const SkBitmap& bm) { diff --git a/src/image/SkImage_Gpu.cpp b/src/image/SkImage_Gpu.cpp index 1956958b3c..04939e36f1 100644 --- a/src/image/SkImage_Gpu.cpp +++ b/src/image/SkImage_Gpu.cpp @@ -9,7 +9,7 @@ #include "GrContext.h" #include "GrDrawContext.h" #include "GrImageIDTextureAdjuster.h" -#include "effects/GrYUVtoRGBEffect.h" +#include "effects/GrYUVEffect.h" #include "SkCanvas.h" #include "SkBitmapCache.h" #include "SkGpuDevice.h" @@ -266,8 +266,8 @@ SkImage* SkImage::NewFromYUVTexturesCopy(GrContext* ctx , SkYUVColorSpace colorS GrPaint paint; paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode); - paint.addColorFragmentProcessor(GrYUVtoRGBEffect::Create(yTex, uTex, vTex, yuvSizes, - colorSpace))->unref(); + paint.addColorFragmentProcessor(GrYUVEffect::CreateYUVToRGB(yTex, uTex, vTex, yuvSizes, + colorSpace))->unref(); const SkRect rect = SkRect::MakeWH(SkIntToScalar(dstDesc.fWidth), SkIntToScalar(dstDesc.fHeight)); diff --git a/src/utils/SkRGBAToYUV.cpp b/src/utils/SkRGBAToYUV.cpp new file mode 100644 index 0000000000..b7f78bac14 --- /dev/null +++ b/src/utils/SkRGBAToYUV.cpp @@ -0,0 +1,58 @@ +/* + * 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 "SkRGBAToYUV.h" +#include "SkCanvas.h" +#include "SkColorMatrixFilterRowMajor255.h" +#include "SkImage.h" +#include "SkPaint.h" +#include "SkSurface.h" + +bool SkRGBAToYUV(const SkImage* image, const SkISize sizes[3], void* const planes[3], + const size_t rowBytes[3], SkYUVColorSpace colorSpace) { + // Matrices that go from RGBA to YUV. + static const SkScalar kYUVColorSpaceInvMatrices[][15] = { + // kJPEG_SkYUVColorSpace + { 0.299001f, 0.586998f, 0.114001f, 0.f, 0.0000821798f * 255.f, + -0.168736f, -0.331263f, 0.499999f, 0.f, 0.499954f * 255.f, + 0.499999f, -0.418686f, -0.0813131f, 0.f, 0.499941f * 255.f}, + + // kRec601_SkYUVColorSpace + { 0.256951f, 0.504421f, 0.0977346f, 0.f, 0.0625f * 255.f, + -0.148212f, -0.290954f, 0.439166f, 0.f, 0.5f * 255.f, + 0.439166f, -0.367886f, -0.0712802f, 0.f, 0.5f * 255.f}, + + // kRec709_SkYUVColorSpace + { 0.182663f, 0.614473f, 0.061971f, 0.f, 0.0625f * 255.f, + -0.100672f, -0.338658f, 0.43933f, 0.f, 0.5f * 255.f, + 0.439142f, -0.39891f, -0.040231f, 0.f, 0.5f * 255.f}, + }; + static_assert(kLastEnum_SkYUVColorSpace == 2, "yuv color matrix array problem"); + static_assert(kJPEG_SkYUVColorSpace == 0, "yuv color matrix array problem"); + static_assert(kRec601_SkYUVColorSpace == 1, "yuv color matrix array problem"); + static_assert(kRec709_SkYUVColorSpace == 2, "yuv color matrix array problem"); + + for (int i = 0; i < 3; ++i) { + size_t rb = rowBytes[i] ? rowBytes[i] : sizes[i].fWidth; + SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterDirect( + SkImageInfo::MakeA8(sizes[i].fWidth, sizes[i].fHeight), planes[i], rb)); + if (!surface) { + return false; + } + SkPaint paint; + paint.setFilterQuality(kLow_SkFilterQuality); + paint.setXfermodeMode(SkXfermode::kSrc_Mode); + int rowStartIdx = 5 * i; + const SkScalar* row = kYUVColorSpaceInvMatrices[colorSpace] + rowStartIdx; + paint.setColorFilter( + SkColorMatrixFilterRowMajor255::CreateSingleChannelOutput(row))->unref(); + surface->getCanvas()->drawImageRect(image, SkIRect::MakeWH(image->width(), image->height()), + SkRect::MakeIWH(surface->width(), surface->height()), + &paint); + } + return true; +} diff --git a/src/utils/SkRGBAToYUV.h b/src/utils/SkRGBAToYUV.h new file mode 100644 index 0000000000..5c3c1b1465 --- /dev/null +++ b/src/utils/SkRGBAToYUV.h @@ -0,0 +1,21 @@ +/* + * 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 SkRGBAToYUV_DEFINED +#define SkRGBAToYUV_DEFINED + +#include "SkPixmap.h" +#include "SkSize.h" + +class SkImage; +// Works with any image type at the moment, but in the future it may only work with raster-backed +// images. This really should take a SkPixmap for the input, however the implementation for the +// time being requires an image. +bool SkRGBAToYUV(const SkImage*, const SkISize [3], void* const planes[3], + const size_t rowBytes[3], SkYUVColorSpace); + +#endif |