Add NV12 texture conversion support.

If textures 1 and 2 passed into MakeFromYUVTexturesCopy are the same,
then treat the input as NV12 and sample from the g component of texture
2.

BUG=skia:5209
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2016593002

Review-Url: https://codereview.chromium.org/2016593002
This commit is contained in:
jbauman 2016-06-09 13:24:48 -07:00 committed by Commit bot
parent 9a3fbf7e55
commit b445a57e6c
6 changed files with 208 additions and 61 deletions

View File

@ -115,12 +115,9 @@ protected:
for (int i = 0; i < 6; ++i) { for (int i = 0; i < 6; ++i) {
GrPipelineBuilder pipelineBuilder; GrPipelineBuilder pipelineBuilder;
pipelineBuilder.setXPFactory(GrPorterDuffXPFactory::Make(SkXfermode::kSrc_Mode)); pipelineBuilder.setXPFactory(GrPorterDuffXPFactory::Make(SkXfermode::kSrc_Mode));
sk_sp<GrFragmentProcessor> fp( sk_sp<GrFragmentProcessor> fp(GrYUVEffect::MakeYUVToRGB(
GrYUVEffect::MakeYUVToRGB(texture[indices[i][0]], texture[indices[i][0]], texture[indices[i][1]], texture[indices[i][2]], sizes,
texture[indices[i][1]], static_cast<SkYUVColorSpace>(space), false));
texture[indices[i][2]],
sizes,
static_cast<SkYUVColorSpace>(space)));
if (fp) { if (fp) {
SkMatrix viewMatrix; SkMatrix viewMatrix;
viewMatrix.setTranslate(x, y); viewMatrix.setTranslate(x, y);
@ -142,6 +139,115 @@ private:
}; };
DEF_GM(return new YUVtoRGBEffect;) DEF_GM(return new YUVtoRGBEffect;)
//////////////////////////////////////////////////////////////////////////////
class YUVNV12toRGBEffect : public GM {
public:
YUVNV12toRGBEffect() {
this->setBGColor(0xFFFFFFFF);
}
protected:
SkString onShortName() override {
return SkString("yuv_nv12_to_rgb_effect");
}
SkISize onISize() override {
return SkISize::Make(48, 120);
}
void onOnceBeforeDraw() override {
SkImageInfo yinfo = SkImageInfo::MakeA8(YSIZE, YSIZE);
fBmp[0].allocPixels(yinfo);
SkImageInfo uvinfo = SkImageInfo::MakeN32Premul(USIZE, USIZE);
fBmp[1].allocPixels(uvinfo);
int color[] = {0, 85, 170};
const int limit[] = {255, 0, 255};
const int invl[] = {0, 255, 0};
const int inc[] = {1, -1, 1};
{
unsigned char* pixels = (unsigned char*)fBmp[0].getPixels();
const size_t nbBytes = fBmp[0].rowBytes() * fBmp[0].height();
for (size_t j = 0; j < nbBytes; ++j) {
pixels[j] = (unsigned char)color[0];
color[0] = (color[0] == limit[0]) ? invl[0] : color[0] + inc[0];
}
}
{
for (int y = 0; y < fBmp[1].height(); ++y) {
uint32_t* pixels = fBmp[1].getAddr32(0, y);
for (int j = 0; j < fBmp[1].width(); ++j) {
pixels[j] = SkColorSetARGB(0, color[1], color[2], 0);
color[1] = (color[1] == limit[1]) ? invl[1] : color[1] + inc[1];
color[2] = (color[2] == limit[2]) ? invl[2] : color[2] + inc[2];
}
}
}
}
void onDraw(SkCanvas* canvas) override {
GrDrawContext* drawContext = canvas->internal_private_accessTopLayerDrawContext();
if (!drawContext) {
skiagm::GM::DrawGpuOnlyMessage(canvas);
return;
}
GrContext* context = canvas->getGrContext();
if (!context) {
return;
}
SkAutoTUnref<GrTexture> texture[3];
texture[0].reset(GrRefCachedBitmapTexture(context, fBmp[0], GrTextureParams::ClampBilerp(),
SkSourceGammaTreatment::kRespect));
texture[1].reset(GrRefCachedBitmapTexture(context, fBmp[1], GrTextureParams::ClampBilerp(),
SkSourceGammaTreatment::kRespect));
texture[2].reset(GrRefCachedBitmapTexture(context, fBmp[1], GrTextureParams::ClampBilerp(),
SkSourceGammaTreatment::kRespect));
if (!texture[0] || !texture[1] || !texture[2]) {
return;
}
static const SkScalar kDrawPad = 10.f;
static const SkScalar kTestPad = 10.f;
static const SkScalar kColorSpaceOffset = 36.f;
SkISize sizes[3] = {{YSIZE, YSIZE}, {USIZE, USIZE}, {VSIZE, VSIZE}};
for (int space = kJPEG_SkYUVColorSpace; space <= kLastEnum_SkYUVColorSpace; ++space) {
SkRect renderRect =
SkRect::MakeWH(SkIntToScalar(fBmp[0].width()), SkIntToScalar(fBmp[0].height()));
renderRect.outset(kDrawPad, kDrawPad);
SkScalar y = kDrawPad + kTestPad + space * kColorSpaceOffset;
SkScalar x = kDrawPad + kTestPad;
GrPipelineBuilder pipelineBuilder;
pipelineBuilder.setXPFactory(GrPorterDuffXPFactory::Make(SkXfermode::kSrc_Mode));
sk_sp<GrFragmentProcessor> fp(
GrYUVEffect::MakeYUVToRGB(texture[0], texture[1], texture[2], sizes,
static_cast<SkYUVColorSpace>(space), true));
if (fp) {
SkMatrix viewMatrix;
viewMatrix.setTranslate(x, y);
pipelineBuilder.addColorFragmentProcessor(fp);
SkAutoTUnref<GrDrawBatch> batch(GrRectBatchFactory::CreateNonAAFill(
GrColor_WHITE, viewMatrix, renderRect, nullptr, nullptr));
drawContext->drawContextPriv().testingOnly_drawBatch(pipelineBuilder, batch);
}
}
}
private:
SkBitmap fBmp[2];
typedef GM INHERITED;
};
DEF_GM(return new YUVNV12toRGBEffect;)
} }
#endif #endif

View File

@ -138,6 +138,15 @@ public:
const SkISize yuvSizes[3], const SkISize yuvSizes[3],
GrSurfaceOrigin); GrSurfaceOrigin);
/**
* Create a new image by copying the pixels from the specified y and uv textures. The data
* from the textures is immediately ingested into the image and the textures can be modified or
* deleted after the function returns. The image will have the dimensions of the y texture.
*/
static sk_sp<SkImage> MakeFromNV12TexturesCopy(GrContext*, SkYUVColorSpace,
const GrBackendObject nv12TextureHandles[2],
const SkISize nv12Sizes[2], GrSurfaceOrigin);
static sk_sp<SkImage> MakeFromPicture(sk_sp<SkPicture>, const SkISize& dimensions, static sk_sp<SkImage> MakeFromPicture(sk_sp<SkPicture>, const SkISize& dimensions,
const SkMatrix*, const SkPaint*); const SkMatrix*, const SkPaint*);

View File

@ -121,11 +121,9 @@ sk_sp<GrTexture> GrYUVProvider::refAsTexture(GrContext* ctx,
} }
GrPaint paint; GrPaint paint;
sk_sp<GrFragmentProcessor> yuvToRgbProcessor(GrYUVEffect::MakeYUVToRGB(yuvTextures[0], sk_sp<GrFragmentProcessor> yuvToRgbProcessor(
yuvTextures[1], GrYUVEffect::MakeYUVToRGB(yuvTextures[0], yuvTextures[1], yuvTextures[2],
yuvTextures[2], yuvInfo.fSizeInfo.fSizes, yuvInfo.fColorSpace, false));
yuvInfo.fSizeInfo.fSizes,
yuvInfo.fColorSpace));
paint.addColorFragmentProcessor(std::move(yuvToRgbProcessor)); paint.addColorFragmentProcessor(std::move(yuvToRgbProcessor));
// If we're decoding an sRGB image, the result of our linear math on the YUV planes is already // If we're decoding an sRGB image, the result of our linear math on the YUV planes is already

View File

@ -64,7 +64,7 @@ class YUVtoRGBEffect : public GrFragmentProcessor {
public: public:
static sk_sp<GrFragmentProcessor> Make(GrTexture* yTexture, GrTexture* uTexture, static sk_sp<GrFragmentProcessor> Make(GrTexture* yTexture, GrTexture* uTexture,
GrTexture* vTexture, const SkISize sizes[3], GrTexture* vTexture, const SkISize sizes[3],
SkYUVColorSpace colorSpace) { SkYUVColorSpace colorSpace, bool nv12) {
SkScalar w[3], h[3]; SkScalar w[3], h[3];
w[0] = SkIntToScalar(sizes[0].fWidth) / SkIntToScalar(yTexture->width()); w[0] = SkIntToScalar(sizes[0].fWidth) / SkIntToScalar(yTexture->width());
h[0] = SkIntToScalar(sizes[0].fHeight) / SkIntToScalar(yTexture->height()); h[0] = SkIntToScalar(sizes[0].fHeight) / SkIntToScalar(yTexture->height());
@ -85,21 +85,23 @@ public:
(sizes[2].fHeight != sizes[0].fHeight)) ? (sizes[2].fHeight != sizes[0].fHeight)) ?
GrTextureParams::kBilerp_FilterMode : GrTextureParams::kBilerp_FilterMode :
GrTextureParams::kNone_FilterMode; GrTextureParams::kNone_FilterMode;
return sk_sp<GrFragmentProcessor>( return sk_sp<GrFragmentProcessor>(new YUVtoRGBEffect(
new YUVtoRGBEffect(yTexture, uTexture, vTexture, yuvMatrix, uvFilterMode, colorSpace)); yTexture, uTexture, vTexture, yuvMatrix, uvFilterMode, colorSpace, nv12));
} }
const char* name() const override { return "YUV to RGB"; } const char* name() const override { return "YUV to RGB"; }
SkYUVColorSpace getColorSpace() const { return fColorSpace; } SkYUVColorSpace getColorSpace() const { return fColorSpace; }
bool isNV12() const {
return fNV12;
}
class GLSLProcessor : public GrGLSLFragmentProcessor { class GLSLProcessor : public GrGLSLFragmentProcessor {
public: public:
// this class always generates the same code.
static void GenKey(const GrProcessor&, const GrGLSLCaps&, GrProcessorKeyBuilder*) {}
void emitCode(EmitArgs& args) override { void emitCode(EmitArgs& args) override {
GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
const YUVtoRGBEffect& effect = args.fFp.cast<YUVtoRGBEffect>();
const char* colorSpaceMatrix = nullptr; const char* colorSpaceMatrix = nullptr;
fMatrixUni = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, fMatrixUni = args.fUniformHandler->addUniform(kFragment_GrShaderFlag,
@ -111,10 +113,15 @@ public:
fragBuilder->codeAppend(".r,"); fragBuilder->codeAppend(".r,");
fragBuilder->appendTextureLookup(args.fTexSamplers[1], args.fCoords[1].c_str(), fragBuilder->appendTextureLookup(args.fTexSamplers[1], args.fCoords[1].c_str(),
args.fCoords[1].getType()); args.fCoords[1].getType());
fragBuilder->codeAppend(".r,"); if (effect.fNV12) {
fragBuilder->appendTextureLookup(args.fTexSamplers[2], args.fCoords[2].c_str(), fragBuilder->codeAppendf(".rg,");
args.fCoords[2].getType()); } else {
fragBuilder->codeAppendf(".r, 1.0) * %s;", colorSpaceMatrix); fragBuilder->codeAppend(".r,");
fragBuilder->appendTextureLookup(args.fTexSamplers[2], args.fCoords[2].c_str(),
args.fCoords[2].getType());
fragBuilder->codeAppendf(".g,");
}
fragBuilder->codeAppendf("1.0) * %s;", colorSpaceMatrix);
} }
protected: protected:
@ -143,21 +150,24 @@ public:
private: private:
YUVtoRGBEffect(GrTexture* yTexture, GrTexture* uTexture, GrTexture* vTexture, YUVtoRGBEffect(GrTexture* yTexture, GrTexture* uTexture, GrTexture* vTexture,
const SkMatrix yuvMatrix[3], GrTextureParams::FilterMode uvFilterMode, const SkMatrix yuvMatrix[3], GrTextureParams::FilterMode uvFilterMode,
SkYUVColorSpace colorSpace) SkYUVColorSpace colorSpace, bool nv12)
: fYTransform(kLocal_GrCoordSet, yuvMatrix[0], yTexture, GrTextureParams::kNone_FilterMode) : fYTransform(kLocal_GrCoordSet, yuvMatrix[0], yTexture, GrTextureParams::kNone_FilterMode)
, fYAccess(yTexture) , fYAccess(yTexture)
, fUTransform(kLocal_GrCoordSet, yuvMatrix[1], uTexture, uvFilterMode) , fUTransform(kLocal_GrCoordSet, yuvMatrix[1], uTexture, uvFilterMode)
, fUAccess(uTexture, uvFilterMode) , fUAccess(uTexture, uvFilterMode)
, fVTransform(kLocal_GrCoordSet, yuvMatrix[2], vTexture, uvFilterMode) , fVAccess(vTexture, uvFilterMode)
, fVAccess(vTexture, uvFilterMode) , fColorSpace(colorSpace)
, fColorSpace(colorSpace) { , fNV12(nv12) {
this->initClassID<YUVtoRGBEffect>(); this->initClassID<YUVtoRGBEffect>();
this->addCoordTransform(&fYTransform); this->addCoordTransform(&fYTransform);
this->addTextureAccess(&fYAccess); this->addTextureAccess(&fYAccess);
this->addCoordTransform(&fUTransform); this->addCoordTransform(&fUTransform);
this->addTextureAccess(&fUAccess); this->addTextureAccess(&fUAccess);
this->addCoordTransform(&fVTransform); if (!fNV12) {
this->addTextureAccess(&fVAccess); fVTransform = GrCoordTransform(kLocal_GrCoordSet, yuvMatrix[2], vTexture, uvFilterMode);
this->addCoordTransform(&fVTransform);
this->addTextureAccess(&fVAccess);
}
} }
GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { GrGLSLFragmentProcessor* onCreateGLSLInstance() const override {
@ -165,12 +175,12 @@ private:
} }
void onGetGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override { void onGetGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
GLSLProcessor::GenKey(*this, caps, b); b->add32(fNV12);
} }
bool onIsEqual(const GrFragmentProcessor& sBase) const override { bool onIsEqual(const GrFragmentProcessor& sBase) const override {
const YUVtoRGBEffect& s = sBase.cast<YUVtoRGBEffect>(); const YUVtoRGBEffect& s = sBase.cast<YUVtoRGBEffect>();
return fColorSpace == s.getColorSpace(); return (fColorSpace == s.getColorSpace()) && (fNV12 == s.isNV12());
} }
void onComputeInvariantOutput(GrInvariantOutput* inout) const override { void onComputeInvariantOutput(GrInvariantOutput* inout) const override {
@ -186,6 +196,7 @@ private:
GrCoordTransform fVTransform; GrCoordTransform fVTransform;
GrTextureAccess fVAccess; GrTextureAccess fVAccess;
SkYUVColorSpace fColorSpace; SkYUVColorSpace fColorSpace;
bool fNV12;
typedef GrFragmentProcessor INHERITED; typedef GrFragmentProcessor INHERITED;
}; };
@ -350,11 +361,11 @@ private:
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
sk_sp<GrFragmentProcessor> sk_sp<GrFragmentProcessor> GrYUVEffect::MakeYUVToRGB(GrTexture* yTexture, GrTexture* uTexture,
GrYUVEffect::MakeYUVToRGB(GrTexture* yTexture, GrTexture* uTexture, GrTexture* vTexture, GrTexture* vTexture, const SkISize sizes[3],
const SkISize sizes[3], SkYUVColorSpace colorSpace) { SkYUVColorSpace colorSpace, bool nv12) {
SkASSERT(yTexture && uTexture && vTexture && sizes); SkASSERT(yTexture && uTexture && vTexture && sizes);
return YUVtoRGBEffect::Make(yTexture, uTexture, vTexture, sizes, colorSpace); return YUVtoRGBEffect::Make(yTexture, uTexture, vTexture, sizes, colorSpace, nv12);
} }
sk_sp<GrFragmentProcessor> sk_sp<GrFragmentProcessor>

View File

@ -20,7 +20,7 @@ namespace GrYUVEffect {
*/ */
sk_sp<GrFragmentProcessor> MakeYUVToRGB(GrTexture* yTexture, GrTexture* uTexture, sk_sp<GrFragmentProcessor> MakeYUVToRGB(GrTexture* yTexture, GrTexture* uTexture,
GrTexture* vTexture, const SkISize sizes[3], GrTexture* vTexture, const SkISize sizes[3],
SkYUVColorSpace colorSpace); SkYUVColorSpace colorSpace, bool nv12);
/** /**
* Creates a processor that performs color conversion from the passed in processor's RGB * Creates a processor that performs color conversion from the passed in processor's RGB

View File

@ -205,18 +205,23 @@ sk_sp<SkImage> SkImage::MakeFromTextureCopy(GrContext* ctx, const GrBackendTextu
SkBudgeted::kYes); SkBudgeted::kYes);
} }
sk_sp<SkImage> SkImage::MakeFromYUVTexturesCopy(GrContext* ctx , SkYUVColorSpace colorSpace, static sk_sp<SkImage> make_from_yuv_textures_copy(GrContext* ctx, SkYUVColorSpace colorSpace,
const GrBackendObject yuvTextureHandles[3], bool nv12,
const SkISize yuvSizes[3], const GrBackendObject yuvTextureHandles[],
GrSurfaceOrigin origin) { const SkISize yuvSizes[],
GrSurfaceOrigin origin) {
const SkBudgeted budgeted = SkBudgeted::kYes; const SkBudgeted budgeted = SkBudgeted::kYes;
if (yuvSizes[0].fWidth <= 0 || yuvSizes[0].fHeight <= 0 || if (yuvSizes[0].fWidth <= 0 || yuvSizes[0].fHeight <= 0 || yuvSizes[1].fWidth <= 0 ||
yuvSizes[1].fWidth <= 0 || yuvSizes[1].fHeight <= 0 || yuvSizes[1].fHeight <= 0) {
yuvSizes[2].fWidth <= 0 || yuvSizes[2].fHeight <= 0) {
return nullptr; return nullptr;
} }
static const GrPixelConfig kConfig = kAlpha_8_GrPixelConfig; if (!nv12 && (yuvSizes[2].fWidth <= 0 || yuvSizes[2].fHeight <= 0)) {
return nullptr;
}
const GrPixelConfig kConfig = nv12 ? kRGBA_8888_GrPixelConfig : kAlpha_8_GrPixelConfig;
GrBackendTextureDesc yDesc; GrBackendTextureDesc yDesc;
yDesc.fConfig = kConfig; yDesc.fConfig = kConfig;
yDesc.fOrigin = origin; yDesc.fOrigin = origin;
@ -233,20 +238,25 @@ sk_sp<SkImage> SkImage::MakeFromYUVTexturesCopy(GrContext* ctx , SkYUVColorSpace
uDesc.fWidth = yuvSizes[1].fWidth; uDesc.fWidth = yuvSizes[1].fWidth;
uDesc.fHeight = yuvSizes[1].fHeight; uDesc.fHeight = yuvSizes[1].fHeight;
GrBackendTextureDesc vDesc; sk_sp<GrTexture> yTex(
vDesc.fConfig = kConfig; ctx->textureProvider()->wrapBackendTexture(yDesc, kBorrow_GrWrapOwnership));
vDesc.fOrigin = origin; sk_sp<GrTexture> uTex(
vDesc.fSampleCnt = 0; ctx->textureProvider()->wrapBackendTexture(uDesc, kBorrow_GrWrapOwnership));
vDesc.fTextureHandle = yuvTextureHandles[2]; sk_sp<GrTexture> vTex;
vDesc.fWidth = yuvSizes[2].fWidth; if (nv12) {
vDesc.fHeight = yuvSizes[2].fHeight; vTex = uTex;
} else {
GrBackendTextureDesc vDesc;
vDesc.fConfig = kConfig;
vDesc.fOrigin = origin;
vDesc.fSampleCnt = 0;
vDesc.fTextureHandle = yuvTextureHandles[2];
vDesc.fWidth = yuvSizes[2].fWidth;
vDesc.fHeight = yuvSizes[2].fHeight;
SkAutoTUnref<GrTexture> yTex(ctx->textureProvider()->wrapBackendTexture( vTex = sk_sp<GrTexture>(
yDesc, kBorrow_GrWrapOwnership)); ctx->textureProvider()->wrapBackendTexture(vDesc, kBorrow_GrWrapOwnership));
SkAutoTUnref<GrTexture> uTex(ctx->textureProvider()->wrapBackendTexture( }
uDesc, kBorrow_GrWrapOwnership));
SkAutoTUnref<GrTexture> vTex(ctx->textureProvider()->wrapBackendTexture(
vDesc, kBorrow_GrWrapOwnership));
if (!yTex || !uTex || !vTex) { if (!yTex || !uTex || !vTex) {
return nullptr; return nullptr;
} }
@ -266,8 +276,8 @@ sk_sp<SkImage> SkImage::MakeFromYUVTexturesCopy(GrContext* ctx , SkYUVColorSpace
GrPaint paint; GrPaint paint;
paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode); paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode);
paint.addColorFragmentProcessor(GrYUVEffect::MakeYUVToRGB(yTex, uTex, vTex, yuvSizes, paint.addColorFragmentProcessor(
colorSpace)); GrYUVEffect::MakeYUVToRGB(yTex.get(), uTex.get(), vTex.get(), yuvSizes, colorSpace, nv12));
const SkRect rect = SkRect::MakeWH(SkIntToScalar(width), SkIntToScalar(height)); const SkRect rect = SkRect::MakeWH(SkIntToScalar(width), SkIntToScalar(height));
@ -278,6 +288,19 @@ sk_sp<SkImage> SkImage::MakeFromYUVTexturesCopy(GrContext* ctx , SkYUVColorSpace
drawContext->asTexture().get(), budgeted); drawContext->asTexture().get(), budgeted);
} }
sk_sp<SkImage> SkImage::MakeFromYUVTexturesCopy(GrContext* ctx, SkYUVColorSpace colorSpace,
const GrBackendObject yuvTextureHandles[3],
const SkISize yuvSizes[3], GrSurfaceOrigin origin) {
return make_from_yuv_textures_copy(ctx, colorSpace, false, yuvTextureHandles, yuvSizes, origin);
}
sk_sp<SkImage> SkImage::MakeFromNV12TexturesCopy(GrContext* ctx, SkYUVColorSpace colorSpace,
const GrBackendObject yuvTextureHandles[2],
const SkISize yuvSizes[2],
GrSurfaceOrigin origin) {
return make_from_yuv_textures_copy(ctx, colorSpace, true, yuvTextureHandles, yuvSizes, origin);
}
static sk_sp<SkImage> create_image_from_maker(GrTextureMaker* maker, SkAlphaType at, uint32_t id) { static sk_sp<SkImage> create_image_from_maker(GrTextureMaker* maker, SkAlphaType at, uint32_t id) {
SkAutoTUnref<GrTexture> texture(maker->refTextureForParams(GrTextureParams::ClampNoFilter(), SkAutoTUnref<GrTexture> texture(maker->refTextureForParams(GrTextureParams::ClampNoFilter(),
SkSourceGammaTreatment::kRespect)); SkSourceGammaTreatment::kRespect));