Add kIdentity_SkYUVColorSpace

This utility color space just maps Y to R, U to G and V to B when flattening or accessing the YUV planes. Clients can then add a colorFilter to directly manipulate the YUV values.

This cannot land in Skia until the following CL lands in Chrome:

https://chromium-review.googlesource.com/c/chromium/src/+/1506004 (Update usage of Skia's SkYUVColorSpace enum to allow the addition of a new value)

Change-Id: Id9403ebbd009b45281d4d53fca52f68692d6c69f
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/198160
Reviewed-by: Jim Van Verth <jvanverth@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Robert Phillips <robertphillips@google.com>
This commit is contained in:
Robert Phillips 2019-03-06 12:36:47 -05:00 committed by Skia Commit-Bot
parent e66a0b212a
commit 0a22ba84e0
6 changed files with 111 additions and 48 deletions

View File

@ -18,6 +18,17 @@
#include "SkImage.h"
#include "SkTo.h"
static sk_sp<SkColorFilter> yuv_to_rgb_colorfilter() {
static const float kJPEGConversionMatrix[20] = {
1.0f, 0.0f, 1.402f, 0.0f, -180.0f,
1.0f, -0.344136f, -0.714136f, 0.0f, 136.0f,
1.0f, 1.772f, 0.0f, 0.0f, -227.6f,
0.0f, 0.0f, 0.0f, 1.0f, 0.0f
};
return SkColorFilter::MakeMatrixFilterRowMajor255(kJPEGConversionMatrix);
}
namespace skiagm {
class ImageFromYUVTextures : public GpuGM {
public:
@ -31,7 +42,7 @@ protected:
}
SkISize onISize() override {
return SkISize::Make(50, 300);
return SkISize::Make(kBmpSize + 2 * kPad, 390);
}
void onOnceBeforeDraw() override {
@ -61,8 +72,8 @@ protected:
uvPixels[0] = static_cast<signed char*>(fYUVBmps[1].getPixels());
uvPixels[1] = static_cast<signed char*>(fYUVBmps[2].getPixels());
// Here we encode using the NTC encoding (even though we will draw it with all the supported
// yuv color spaces when converted back to RGB)
// Here we encode using the kJPEG_SkYUVColorSpace (i.e., full-swing Rec 601) even though
// we will draw it with all the supported yuv color spaces when converted back to RGB
for (int i = 0; i < kBmpSize * kBmpSize; ++i) {
yPixels[i] = static_cast<unsigned char>(0.299f * SkGetPackedR32(rgbColors[i]) +
0.587f * SkGetPackedG32(rgbColors[i]) +
@ -145,45 +156,51 @@ protected:
}
void onDraw(GrContext* context, GrRenderTargetContext*, SkCanvas* canvas) override {
constexpr SkScalar kPad = 10.f;
// draw the original
SkScalar yOffset = kPad;
canvas->drawImage(fRGBImage.get(), kPad, yOffset);
yOffset += kBmpSize + kPad;
SkTArray<sk_sp<SkImage>> images;
images.push_back(fRGBImage);
for (int space = kJPEG_SkYUVColorSpace; space <= kLastEnum_SkYUVColorSpace; ++space) {
GrBackendTexture yuvTextures[3];
this->createYUVTextures(context, yuvTextures);
images.push_back(SkImage::MakeFromYUVTexturesCopy(context,
static_cast<SkYUVColorSpace>(space),
yuvTextures,
kTopLeft_GrSurfaceOrigin));
auto image = SkImage::MakeFromYUVTexturesCopy(context,
static_cast<SkYUVColorSpace>(space),
yuvTextures,
kTopLeft_GrSurfaceOrigin);
this->deleteBackendTextures(context, yuvTextures, 3);
}
for (int i = 0; i < images.count(); ++ i) {
SkScalar y = (i + 1) * kPad + i * fYUVBmps[0].height();
SkScalar x = kPad;
canvas->drawImage(images[i].get(), x, y);
SkPaint paint;
if (kIdentity_SkYUVColorSpace == space) {
// The identity color space needs post-processing to appear correct
paint.setColorFilter(yuv_to_rgb_colorfilter());
}
canvas->drawImage(image.get(), kPad, yOffset, &paint);
yOffset += kBmpSize + kPad;
}
sk_sp<SkImage> image;
for (int space = kJPEG_SkYUVColorSpace, i = images.count();
space <= kLastEnum_SkYUVColorSpace; ++space, ++i) {
for (int space = kJPEG_SkYUVColorSpace; space <= kLastEnum_SkYUVColorSpace; ++space) {
GrBackendTexture yuvTextures[3];
GrBackendTexture resultTexture;
this->createYUVTextures(context, yuvTextures);
this->createResultTexture(
context, yuvTextures[0].width(), yuvTextures[0].height(), &resultTexture);
image = SkImage::MakeFromYUVTexturesCopyWithExternalBackend(
context,
static_cast<SkYUVColorSpace>(space),
yuvTextures,
kTopLeft_GrSurfaceOrigin,
resultTexture);
auto image = SkImage::MakeFromYUVTexturesCopyWithExternalBackend(
context,
static_cast<SkYUVColorSpace>(space),
yuvTextures,
kTopLeft_GrSurfaceOrigin,
resultTexture);
SkScalar y = (i + 1) * kPad + i * fYUVBmps[0].height();
SkScalar x = kPad;
SkPaint paint;
if (kIdentity_SkYUVColorSpace == space) {
// The identity color space needs post-processing to appear correct
paint.setColorFilter(yuv_to_rgb_colorfilter());
}
canvas->drawImage(image.get(), kPad, yOffset, &paint);
yOffset += kBmpSize + kPad;
canvas->drawImage(image.get(), x, y);
GrBackendTexture texturesToDelete[4]{
yuvTextures[0],
yuvTextures[1],
@ -198,7 +215,8 @@ private:
sk_sp<SkImage> fRGBImage;
SkBitmap fYUVBmps[3];
static constexpr int kBmpSize = 32;
static constexpr SkScalar kPad = 10.0f;
static constexpr int kBmpSize = 32;
typedef GM INHERITED;
};

View File

@ -276,6 +276,11 @@ static SkPMColor convert_yuva_to_rgba_709(uint8_t y, uint8_t u, uint8_t v, uint8
}
static void extract_planes(const SkBitmap& bm, SkYUVColorSpace yuvColorSpace, PlaneData* planes) {
if (kIdentity_SkYUVColorSpace == yuvColorSpace) {
// To test the identity color space we use JPEG YUV planes
yuvColorSpace = kJPEG_SkYUVColorSpace;
}
SkASSERT(!(bm.width() % 2));
SkASSERT(!(bm.height() % 2));
@ -561,6 +566,9 @@ protected:
case kRec709_SkYUVColorSpace:
*fFlattened.getAddr32(x, y) = convert_yuva_to_rgba_709(Y, U, V, A);
break;
case kIdentity_SkYUVColorSpace:
*fFlattened.getAddr32(x, y) = SkPremultiplyARGBInline(A, Y, U, V);
break;
}
}
}
@ -619,7 +627,7 @@ static sk_sp<SkImage> make_yuv_gen_image(const SkImageInfo& ii,
}
static void draw_col_label(SkCanvas* canvas, int x, int yuvColorSpace, bool opaque) {
static const char* kYUVColorSpaceNames[] = { "JPEG", "601", "709" };
static const char* kYUVColorSpaceNames[] = { "JPEG", "601", "709", "Identity" };
GR_STATIC_ASSERT(SK_ARRAY_COUNT(kYUVColorSpaceNames) == kLastEnum_SkYUVColorSpace+1);
SkPaint paint;
@ -706,6 +714,17 @@ static GrBackendTexture create_yuva_texture(GrGpu* gpu, const SkBitmap& bm,
return tex;
}
static sk_sp<SkColorFilter> yuv_to_rgb_colorfilter() {
static const float kJPEGConversionMatrix[20] = {
1.0f, 0.0f, 1.402f, 0.0f, -180.0f,
1.0f, -0.344136f, -0.714136f, 0.0f, 136.0f,
1.0f, 1.772f, 0.0f, 0.0f, -227.6f,
0.0f, 0.0f, 0.0f, 1.0f, 0.0f
};
return SkColorFilter::MakeMatrixFilterRowMajor255(kJPEGConversionMatrix);
}
namespace skiagm {
// This GM creates an opaque and transparent bitmap, extracts the planes and then recombines
@ -852,6 +871,12 @@ protected:
int x = kLabelWidth;
for (int cs = kJPEG_SkYUVColorSpace; cs <= kLastEnum_SkYUVColorSpace; ++cs) {
SkPaint paint;
if (kIdentity_SkYUVColorSpace == cs) {
// The identity color space needs post processing to appear correctly
paint.setColorFilter(yuv_to_rgb_colorfilter());
}
for (int opaque : { 0, 1 }) {
int y = kLabelHeight;
@ -863,11 +888,14 @@ protected:
for (int format = kAYUV_YUVFormat; format <= kLast_YUVFormat; ++format) {
draw_row_label(canvas, y, format);
if (fUseTargetColorSpace && fImages[opaque][cs][format]) {
// Making a CS-specific version of a kIdentity_SkYUVColorSpace YUV image
// doesn't make a whole lot of sense. The colorSpace conversion will
// operate on the YUV components rather than the RGB components.
sk_sp<SkImage> csImage =
fImages[opaque][cs][format]->makeColorSpace(fTargetColorSpace);
canvas->drawImage(csImage, x, y);
canvas->drawImage(csImage, x, y, &paint);
} else {
canvas->drawImage(fImages[opaque][cs][format], x, y);
canvas->drawImage(fImages[opaque][cs][format], x, y, &paint);
}
y += kTileWidthHeight + kPad;
}

View File

@ -41,7 +41,8 @@ protected:
}
SkISize onISize() override {
return SkISize::Make(238, 120);
int numRows = kLastEnum_SkYUVColorSpace + 1;
return SkISize::Make(238, kDrawPad + numRows * kColorSpaceOffset);
}
void onOnceBeforeDraw() override {
@ -86,10 +87,6 @@ protected:
}
}
constexpr SkScalar kDrawPad = 10.f;
constexpr SkScalar kTestPad = 10.f;
constexpr SkScalar kColorSpaceOffset = 36.f;
for (int space = kJPEG_SkYUVColorSpace; space <= kLastEnum_SkYUVColorSpace; ++space) {
SkRect renderRect = SkRect::MakeWH(SkIntToScalar(fImage[0]->width()),
SkIntToScalar(fImage[0]->height()));
@ -132,6 +129,10 @@ protected:
private:
sk_sp<SkImage> fImage[3];
static constexpr SkScalar kDrawPad = 10.f;
static constexpr SkScalar kTestPad = 10.f;
static constexpr SkScalar kColorSpaceOffset = 36.f;
typedef GM INHERITED;
};
@ -151,7 +152,8 @@ protected:
}
SkISize onISize() override {
return SkISize::Make(48, 120);
int numRows = kLastEnum_SkYUVColorSpace + 1;
return SkISize::Make(48, kDrawPad + numRows * kColorSpaceOffset);
}
void onOnceBeforeDraw() override {
@ -211,10 +213,6 @@ protected:
{ -1, SkColorChannel::kA }
};
constexpr SkScalar kDrawPad = 10.f;
constexpr SkScalar kTestPad = 10.f;
constexpr SkScalar kColorSpaceOffset = 36.f;
for (int space = kJPEG_SkYUVColorSpace; space <= kLastEnum_SkYUVColorSpace; ++space) {
SkRect renderRect = SkRect::MakeWH(SkIntToScalar(fImage[0]->width()),
SkIntToScalar(fImage[0]->height()));
@ -243,6 +241,10 @@ protected:
private:
sk_sp<SkImage> fImage[2];
static constexpr SkScalar kDrawPad = 10.f;
static constexpr SkScalar kTestPad = 10.f;
static constexpr SkScalar kColorSpaceOffset = 36.f;
typedef GM INHERITED;
};

View File

@ -359,7 +359,7 @@ public:
@param context GPU context
@param yuvColorSpace How the YUV values are converted to RGB. One of:
kJPEG_SkYUVColorSpace, kRec601_SkYUVColorSpace,
kRec709_SkYUVColorSpace
kRec709_SkYUVColorSpace, kIdentity_SkYUVColorSpace
@param yuvaTextures array of (up to four) YUVA textures on GPU which contain the,
possibly interleaved, YUVA planes
@param yuvaIndices array indicating which texture in yuvaTextures, and channel
@ -384,7 +384,7 @@ public:
@param context GPU context
@param yuvColorSpace How the YUV values are converted to RGB. One of:
kJPEG_SkYUVColorSpace, kRec601_SkYUVColorSpace,
kRec709_SkYUVColorSpace
kRec709_SkYUVColorSpace, kIdentity_SkYUVColorSpace
@param yuvaTextures array of (up to four) YUVA textures on GPU which contain the,
possibly interleaved, YUVA planes
@param yuvaIndices array indicating which texture in yuvaTextures, and channel
@ -412,7 +412,7 @@ public:
@param context GPU context
@param yuvColorSpace How the YUV values are converted to RGB. One of:
kJPEG_SkYUVColorSpace, kRec601_SkYUVColorSpace,
kRec709_SkYUVColorSpace
kRec709_SkYUVColorSpace, kIdentity_SkYUVColorSpace
@param yuvaTextures array of (up to four) YUVA textures on GPU which contain the,
possibly interleaved, YUVA planes
@param yuvaIndices array indicating which texture in yuvaTextures, and channel
@ -444,7 +444,7 @@ public:
@param context GPU context
@param yuvColorSpace How the YUV values are converted to RGB. One of:
kJPEG_SkYUVColorSpace, kRec601_SkYUVColorSpace,
kRec709_SkYUVColorSpace
kRec709_SkYUVColorSpace, kIdentity_SkYUVColorSpace
@param yuvaPixmaps array of (up to four) SkPixmap which contain the,
possibly interleaved, YUVA planes
@param yuvaIndices array indicating which pixmap in yuvaPixmaps, and channel
@ -486,7 +486,7 @@ public:
@param context GPU context
@param yuvColorSpace one of: kJPEG_SkYUVColorSpace, kRec601_SkYUVColorSpace,
kRec709_SkYUVColorSpace
kRec709_SkYUVColorSpace, kIdentity_SkYUVColorSpace
@param nv12Textures array of YUV textures on GPU
@param imageOrigin one of: kBottomLeft_GrSurfaceOrigin, kTopLeft_GrSurfaceOrigin
@param imageColorSpace range of colors; may be nullptr
@ -507,7 +507,7 @@ public:
@param context GPU context
@param yuvColorSpace one of: kJPEG_SkYUVColorSpace, kRec601_SkYUVColorSpace,
kRec709_SkYUVColorSpace
kRec709_SkYUVColorSpace, kIdentity_SkYUVColorSpace
@param nv12Textures array of YUV textures on GPU
@param imageOrigin one of: kBottomLeft_GrSurfaceOrigin, kTopLeft_GrSurfaceOrigin
@param backendTexture the resource that stores the final pixels

View File

@ -172,12 +172,17 @@ SK_API bool SkColorTypeValidateAlphaType(SkColorType colorType, SkAlphaType alph
JPEG YUV values encode the full range of 0 to 255 for all three components.
Video YUV values range from 16 to 235 for all three components. Details of
encoding and conversion to RGB are described in YCbCr color space.
The identity colorspace exists to provide a utility mapping from Y to R, U to G and V to B.
It can be used to visualize the YUV planes or to explicitly post process the YUV channels.
*/
enum SkYUVColorSpace {
kJPEG_SkYUVColorSpace, //!< describes full range
kRec601_SkYUVColorSpace, //!< describes SDTV range
kRec709_SkYUVColorSpace, //!< describes HDTV range
kLastEnum_SkYUVColorSpace = kRec709_SkYUVColorSpace, //!< last valid value
kIdentity_SkYUVColorSpace, //!< maps Y->R, U->G, V->B
kLastEnum_SkYUVColorSpace = kIdentity_SkYUVColorSpace, //!< last valid value
};
/** \struct SkImageInfo

View File

@ -28,6 +28,13 @@ static const float kRec709ConversionMatrix[16] = {
0.0f, 0.0f, 0.0f, 1.0f
};
static const float kIdentityConversionMatrix[16] = {
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
};
std::unique_ptr<GrFragmentProcessor> GrYUVtoRGBEffect::Make(const sk_sp<GrTextureProxy> proxies[],
const SkYUVAIndex yuvaIndices[4],
SkYUVColorSpace yuvColorSpace,
@ -61,6 +68,9 @@ std::unique_ptr<GrFragmentProcessor> GrYUVtoRGBEffect::Make(const sk_sp<GrTextur
case kRec709_SkYUVColorSpace:
mat.setColMajorf(kRec709ConversionMatrix);
break;
case kIdentity_SkYUVColorSpace:
mat.setColMajorf(kIdentityConversionMatrix);
break;
}
return std::unique_ptr<GrFragmentProcessor>(new GrYUVtoRGBEffect(
proxies, scales, filterModes, numPlanes, yuvaIndices, mat));