Skia side RGB to YUV gpu conversion

This code is the one that's currently working in my local chromium build. A few things still need to be addressed and I'll highlight these directly in the code.

BUG=skia:
R=reed@google.com, bsalomon@google.com, senorblanco@google.com, senorblanco@chromium.org, robertphillips@google.com, scroggo@google.com, halcanary@google.com

Author: sugoi@chromium.org

Review URL: https://codereview.chromium.org/374743003
This commit is contained in:
sugoi 2014-07-21 11:37:39 -07:00 committed by Commit bot
parent 02cafcc1bf
commit 518d83dbc1
8 changed files with 193 additions and 0 deletions

View File

@ -103,6 +103,7 @@
'../tests/ImageCacheTest.cpp',
'../tests/ImageDecodingTest.cpp',
'../tests/ImageFilterTest.cpp',
'../tests/ImageGeneratorTest.cpp',
'../tests/ImageNewShaderTest.cpp',
'../tests/InfRectTest.cpp',
'../tests/InterpolatorTest.cpp',

View File

@ -116,12 +116,26 @@ public:
bool getPixels(const SkImageInfo& info, void* pixels, size_t rowBytes);
#endif
/**
* If planes or rowBytes is NULL or if any entry in planes is NULL or if any entry in rowBytes
* is 0, this imagegenerator should output the sizes and return true if it can efficiently
* return YUV planar data. If it cannot, it should return false. Note that either planes and
* rowBytes are both fully defined and non NULL/non 0 or they are both NULL or have NULL or 0
* entries only. Having only partial planes/rowBytes information is not supported.
*
* If all planes and rowBytes entries are non NULL or non 0, then it should copy the
* associated YUV data into those planes of memory supplied by the caller. It should validate
* that the sizes match what it expected. If the sizes do not match, it should return false.
*/
bool getYUV8Planes(SkISize sizes[3], void* planes[3], size_t rowBytes[3]);
protected:
virtual SkData* onRefEncodedData();
virtual bool onGetInfo(SkImageInfo* info);
virtual bool onGetPixels(const SkImageInfo& info,
void* pixels, size_t rowBytes,
SkPMColor ctable[], int* ctableCount);
virtual bool onGetYUV8Planes(SkISize sizes[3], void* planes[3], size_t rowBytes[3]);
};
#endif // SkImageGenerator_DEFINED

View File

@ -13,6 +13,7 @@
#include "SkRefCnt.h"
#include "SkString.h"
#include "SkImageInfo.h"
#include "SkSize.h"
#include "SkTDArray.h"
//#define xed
@ -219,6 +220,18 @@ public:
*/
virtual GrTexture* getTexture() { return NULL; }
/**
* If any planes or rowBytes is NULL, this should output the sizes and return true
* if it can efficiently return YUV planar data. If it cannot, it should return false.
*
* If all planes and rowBytes are not NULL, then it should copy the associated Y,U,V data
* into those planes of memory supplied by the caller. It should validate that the sizes
* match what it expected. If the sizes do not match, it should return false.
*/
bool getYUV8Planes(SkISize sizes[3], void* planes[3], size_t rowBytes[3]) {
return this->onGetYUV8Planes(sizes, planes, rowBytes);
}
bool readPixels(SkBitmap* dst, const SkIRect* subset = NULL);
/**
@ -305,6 +318,9 @@ protected:
// default impl returns NULL.
virtual SkData* onRefEncodedData();
// default impl returns false.
virtual bool onGetYUV8Planes(SkISize sizes[3], void* planes[3], size_t rowBytes[3]);
/**
* Returns the size (in bytes) of the internally allocated memory.
* This should be implemented in all serializable SkPixelRef derived classes.

View File

@ -57,6 +57,45 @@ bool SkImageGenerator::getPixels(const SkImageInfo& info, void* pixels, size_t r
}
#endif
bool SkImageGenerator::getYUV8Planes(SkISize sizes[3], void* planes[3], size_t rowBytes[3]) {
#ifdef SK_DEBUG
// In all cases, we need the sizes array
SkASSERT(NULL != sizes);
bool isValidWithPlanes = (NULL != planes) && (NULL != rowBytes) &&
((NULL != planes[0]) && (NULL != planes[1]) && (NULL != planes[2]) &&
(0 != rowBytes[0]) && (0 != rowBytes[1]) && (0 != rowBytes[2]));
bool isValidWithoutPlanes =
((NULL == planes) ||
((NULL == planes[0]) && (NULL == planes[1]) && (NULL == planes[2]))) &&
((NULL == rowBytes) ||
((0 == rowBytes[0]) && (0 == rowBytes[1]) && (0 == rowBytes[2])));
// Either we have all planes and rowBytes information or we have none of it
// Having only partial information is not supported
SkASSERT(isValidWithPlanes || isValidWithoutPlanes);
// If we do have planes information, make sure all sizes are non 0
// and all rowBytes are valid
SkASSERT(!isValidWithPlanes ||
((sizes[0].fWidth >= 0) &&
(sizes[0].fHeight >= 0) &&
(sizes[1].fWidth >= 0) &&
(sizes[1].fHeight >= 0) &&
(sizes[2].fWidth >= 0) &&
(sizes[2].fHeight >= 0) &&
(rowBytes[0] >= (size_t)sizes[0].fWidth) &&
(rowBytes[1] >= (size_t)sizes[1].fWidth) &&
(rowBytes[2] >= (size_t)sizes[2].fWidth)));
#endif
return this->onGetYUV8Planes(sizes, planes, rowBytes);
}
bool SkImageGenerator::onGetYUV8Planes(SkISize sizes[3], void* planes[3], size_t rowBytes[3]) {
return false;
}
/////////////////////////////////////////////////////////////////////////////////////////////
SkData* SkImageGenerator::onRefEncodedData() {

View File

@ -253,6 +253,10 @@ SkData* SkPixelRef::onRefEncodedData() {
return NULL;
}
bool SkPixelRef::onGetYUV8Planes(SkISize sizes[3], void* planes[3], size_t rowBytes[3]) {
return false;
}
size_t SkPixelRef::getAllocatedSizeInBytes() const {
return 0;
}

View File

@ -15,6 +15,7 @@
#include "GrGpu.h"
#include "effects/GrDitherEffect.h"
#include "GrDrawTargetCaps.h"
#include "effects/GrYUVtoRGBEffect.h"
#ifndef SK_IGNORE_ETC1_SUPPORT
# include "ktx.h"
@ -193,6 +194,81 @@ static GrTexture *load_etc1_texture(GrContext* ctx,
}
#endif // SK_IGNORE_ETC1_SUPPORT
static GrTexture *load_yuv_texture(GrContext* ctx, const GrTextureParams* params,
const SkBitmap& bm, const GrTextureDesc& desc) {
GrTexture* result = NULL;
SkPixelRef* pixelRef = bm.pixelRef();
SkISize yuvSizes[3];
if ((NULL == pixelRef) || !pixelRef->getYUV8Planes(yuvSizes, NULL, NULL)) {
return NULL;
}
// Allocate the memory for YUV
size_t totalSize(0);
size_t sizes[3], rowBytes[3];
for (int i = 0; i < 3; ++i) {
rowBytes[i] = yuvSizes[i].fWidth;
totalSize += sizes[i] = rowBytes[i] * yuvSizes[i].fHeight;
}
SkAutoMalloc storage(totalSize);
void* planes[3];
planes[0] = storage.get();
planes[1] = (uint8_t*)planes[0] + sizes[0];
planes[2] = (uint8_t*)planes[1] + sizes[1];
// Get the YUV planes
if (!pixelRef->getYUV8Planes(yuvSizes, planes, rowBytes)) {
return NULL;
}
GrTextureDesc yuvDesc;
yuvDesc.fConfig = kAlpha_8_GrPixelConfig;
GrAutoScratchTexture yuvTextures[3];
for (int i = 0; i < 3; ++i) {
yuvDesc.fWidth = yuvSizes[i].fWidth;
yuvDesc.fHeight = yuvSizes[i].fHeight;
yuvTextures[i].set(ctx, yuvDesc);
if ((NULL == yuvTextures[i].texture()) ||
!ctx->writeTexturePixels(yuvTextures[i].texture(),
0, 0, yuvDesc.fWidth, yuvDesc.fHeight,
yuvDesc.fConfig, planes[i], rowBytes[i])) {
return NULL;
}
}
GrTextureDesc rtDesc = desc;
rtDesc.fFlags = rtDesc.fFlags |
kRenderTarget_GrTextureFlagBit |
kNoStencil_GrTextureFlagBit;
// This texture is likely to be used again so leave it in the cache
GrCacheID cacheID;
generate_bitmap_cache_id(bm, &cacheID);
GrResourceKey key;
result = ctx->createTexture(params, rtDesc, cacheID, NULL, 0, &key);
GrRenderTarget* renderTarget = result ? result->asRenderTarget() : NULL;
if (NULL != renderTarget) {
add_genID_listener(key, bm.pixelRef());
SkAutoTUnref<GrEffect> yuvToRgbEffect(GrYUVtoRGBEffect::Create(
yuvTextures[0].texture(), yuvTextures[1].texture(), yuvTextures[2].texture()));
GrPaint paint;
paint.addColorEffect(yuvToRgbEffect);
SkRect r = SkRect::MakeWH(SkIntToScalar(yuvSizes[0].fWidth),
SkIntToScalar(yuvSizes[0].fHeight));
GrContext::AutoRenderTarget autoRT(ctx, renderTarget);
GrContext::AutoMatrix am;
am.setIdentity(ctx);
GrContext::AutoClip ac(ctx, GrContext::AutoClip::kWideOpen_InitialClip);
ctx->drawRect(paint, r);
} else {
SkSafeSetNull(result);
}
return result;
}
static GrTexture* sk_gr_create_bitmap_texture(GrContext* ctx,
bool cache,
const GrTextureParams* params,
@ -264,6 +340,12 @@ static GrTexture* sk_gr_create_bitmap_texture(GrContext* ctx,
}
#endif // SK_IGNORE_ETC1_SUPPORT
else {
GrTexture *texture = load_yuv_texture(ctx, params, *bitmap, desc);
if (NULL != texture) {
return texture;
}
}
SkAutoLockPixels alp(*bitmap);
if (!bitmap->readyToDraw()) {
return NULL;

View File

@ -48,6 +48,12 @@ private:
size_t rowBytes,
SkDiscardableMemory::Factory* factory);
virtual bool onGetYUV8Planes(SkISize sizes[3],
void* planes[3],
size_t rowBytes[3]) SK_OVERRIDE {
return fGenerator->getYUV8Planes(sizes, planes, rowBytes);
}
friend bool SkInstallDiscardablePixelRef(SkImageGenerator*, SkBitmap*,
SkDiscardableMemory::Factory*);

View File

@ -0,0 +1,31 @@
/*
* 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 "SkImageGenerator.h"
#include "Test.h"
DEF_TEST(ImageGenerator, reporter) {
SkImageGenerator ig;
SkISize sizes[3];
sizes[0] = SkISize::Make(200, 200);
sizes[1] = SkISize::Make(100, 100);
sizes[2] = SkISize::Make( 50, 50);
void* planes[3] = { NULL };
size_t rowBytes[3] = { 0 };
// Check that the YUV decoding API does not cause any crashes
ig.getYUV8Planes(sizes, NULL, NULL);
ig.getYUV8Planes(sizes, planes, NULL);
ig.getYUV8Planes(sizes, NULL, rowBytes);
ig.getYUV8Planes(sizes, planes, rowBytes);
int dummy;
planes[0] = planes[1] = planes[2] = &dummy;
rowBytes[0] = rowBytes[1] = rowBytes[2] = 250;
ig.getYUV8Planes(sizes, planes, rowBytes);
}