aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar bsalomon <bsalomon@google.com>2016-02-01 13:16:14 -0800
committerGravatar Commit bot <commit-bot@chromium.org>2016-02-01 13:16:14 -0800
commitf267c1efe7de7a8e71404afde6cbf93c3808d267 (patch)
tree3ca07e8c6e5526ccfe4166953631006dd8b71845
parent9d02b264b72be64702e43cb797b7899a8a6926ca (diff)
Add ability to extract YUV planes from SkImage
-rw-r--r--gm/imagetoyuvplanes.cpp111
-rw-r--r--gm/yuvtorgbeffect.cpp14
-rw-r--r--gyp/gpu.gypi6
-rw-r--r--gyp/utils.gypi4
-rw-r--r--include/core/SkImage.h7
-rw-r--r--src/core/SkColorMatrixFilterRowMajor255.cpp21
-rw-r--r--src/core/SkColorMatrixFilterRowMajor255.h7
-rw-r--r--src/gpu/GrTextureToYUVPlanes.cpp248
-rw-r--r--src/gpu/GrTextureToYUVPlanes.h19
-rw-r--r--src/gpu/GrYUVProvider.cpp14
-rw-r--r--src/gpu/SkGr.cpp2
-rw-r--r--src/gpu/effects/GrYUVEffect.cpp391
-rw-r--r--src/gpu/effects/GrYUVEffect.h50
-rw-r--r--src/gpu/effects/GrYUVtoRGBEffect.cpp185
-rw-r--r--src/gpu/effects/GrYUVtoRGBEffect.h24
-rw-r--r--src/image/SkImage.cpp18
-rw-r--r--src/image/SkImage_Gpu.cpp6
-rw-r--r--src/utils/SkRGBAToYUV.cpp58
-rw-r--r--src/utils/SkRGBAToYUV.h21
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