YUVUtils function for splitting image into yuva planes.

Makes the code from yuv_splitter reusable and able to produce subsampled
planes.

Bug: chromium:1210557
Change-Id: Icce112658bbdb866c3ecb9dcff1a5e8d0d30135a
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/411297
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
This commit is contained in:
Brian Salomon 2021-05-21 14:00:44 -04:00 committed by Skia Commit-Bot
parent cd7fc79579
commit 5cc24c07bb
3 changed files with 84 additions and 46 deletions

View File

@ -1153,29 +1153,6 @@ DEF_GM(return new YUVMakeColorSpaceGM();)
#include "src/core/SkAutoPixmapStorage.h"
#include "tools/Resources.h"
static void draw_into_alpha(const SkImage* img, sk_sp<SkColorFilter> cf, const SkPixmap& dst) {
auto canvas = SkCanvas::MakeRasterDirect(dst.info(), dst.writable_addr(), dst.rowBytes());
canvas->scale(1.0f * dst.width() / img->width(), 1.0f * dst.height() / img->height());
SkPaint paint;
paint.setColorFilter(cf);
paint.setBlendMode(SkBlendMode::kSrc);
canvas->drawImage(img, 0, 0, SkSamplingOptions(SkFilterMode::kLinear), &paint);
}
static void split_into_yuv(const SkImage* img, SkYUVColorSpace cs, const SkPixmap dst[3]) {
float m[20];
SkColorMatrix_RGB2YUV(cs, m);
memcpy(m + 15, m + 0, 5 * sizeof(float)); // copy Y into A
draw_into_alpha(img, SkColorFilters::Matrix(m), dst[0]);
memcpy(m + 15, m + 5, 5 * sizeof(float)); // copy U into A
draw_into_alpha(img, SkColorFilters::Matrix(m), dst[1]);
memcpy(m + 15, m + 10, 5 * sizeof(float)); // copy V into A
draw_into_alpha(img, SkColorFilters::Matrix(m), dst[2]);
}
static void draw_diff(SkCanvas* canvas, SkScalar x, SkScalar y,
const SkImage* a, const SkImage* b) {
auto sh = SkShaders::Blend(SkBlendMode::kDifference,
@ -1200,9 +1177,7 @@ static void draw_diff(SkCanvas* canvas, SkScalar x, SkScalar y,
// resulting (recombined) images (gpu only for now).
//
class YUVSplitterGM : public skiagm::GM {
sk_sp<SkImage> fOrig;
SkAutoPixmapStorage fStorage[3];
SkPixmap fPM[3];
sk_sp<SkImage> fOrig;
public:
YUVSplitterGM() {}
@ -1219,27 +1194,26 @@ protected:
void onOnceBeforeDraw() override {
fOrig = GetResourceAsImage("images/mandrill_256.png");
SkImageInfo info = SkImageInfo::MakeA8(fOrig->dimensions());
fStorage[0].alloc(info);
fStorage[1].alloc(info);
fStorage[2].alloc(info);
for (int i = 0; i < 3; ++i) {
fPM[i] = fStorage[i];
}
}
void onDraw(SkCanvas* canvas) override {
canvas->translate(fOrig->width(), 0);
canvas->save();
for (auto cs : {kRec709_SkYUVColorSpace, kRec601_SkYUVColorSpace, kJPEG_SkYUVColorSpace,
SkYUVAInfo info;
std::array<sk_sp<SkImage>, SkYUVAInfo::kMaxPlanes> planes;
for (auto cs : {kRec709_SkYUVColorSpace,
kRec601_SkYUVColorSpace,
kJPEG_SkYUVColorSpace,
kBT2020_SkYUVColorSpace}) {
split_into_yuv(fOrig.get(), cs, fPM);
SkYUVAInfo yuvaInfo(fOrig->dimensions(),
SkYUVAInfo::PlaneConfig::kY_U_V,
SkYUVAInfo::Subsampling::k444,
cs);
auto yuvaPixmaps = SkYUVAPixmaps::FromExternalPixmaps(yuvaInfo, fPM);
std::tie(planes, info) = sk_gpu_test::MakeYUVAPlanesAsA8(fOrig.get(),
cs,
SkYUVAInfo::Subsampling::k444,
/*recording context*/ nullptr);
SkPixmap pixmaps[4];
for (int i = 0; i < info.numPlanes(); ++i) {
planes[i]->peekPixels(&pixmaps[i]);
}
auto yuvaPixmaps = SkYUVAPixmaps::FromExternalPixmaps(info, pixmaps);
auto img = SkImage::MakeFromYUVAPixmaps(canvas->recordingContext(),
yuvaPixmaps,
GrMipMapped::kNo,
@ -1253,11 +1227,11 @@ protected:
}
canvas->restore();
canvas->translate(-fOrig->width(), 0);
canvas->drawImage(SkImage::MakeRasterCopy(fPM[0]), 0, 0);
canvas->drawImage(SkImage::MakeRasterCopy(fPM[1]), 0, fPM[0].height());
canvas->drawImage(SkImage::MakeRasterCopy(fPM[2]),
0, fPM[0].height() + fPM[1].height());
int y = 0;
for (int i = 0; i < info.numPlanes(); ++i) {
canvas->drawImage(planes[i], 0, y);
y += planes[i]->height();
}
}
private:

View File

@ -7,8 +7,11 @@
#include "tools/gpu/YUVUtils.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkColorFilter.h"
#include "include/core/SkColorPriv.h"
#include "include/core/SkData.h"
#include "include/core/SkSurface.h"
#include "include/gpu/GrRecordingContext.h"
#include "include/gpu/GrYUVABackendTextures.h"
#include "src/codec/SkCodecImageGenerator.h"
@ -134,6 +137,55 @@ private:
namespace sk_gpu_test {
std::tuple<std::array<sk_sp<SkImage>, SkYUVAInfo::kMaxPlanes>, SkYUVAInfo>
MakeYUVAPlanesAsA8(SkImage* src,
SkYUVColorSpace cs,
SkYUVAInfo::Subsampling ss,
GrRecordingContext* rContext) {
float rgbToYUV[20];
SkColorMatrix_RGB2YUV(cs, rgbToYUV);
SkYUVAInfo::PlaneConfig config = src->isOpaque() ? SkYUVAInfo::PlaneConfig::kY_U_V
: SkYUVAInfo::PlaneConfig::kY_U_V_A;
SkISize dims[SkYUVAInfo::kMaxPlanes];
int n = SkYUVAInfo::PlaneDimensions(src->dimensions(),
config,
ss,
kTopLeft_SkEncodedOrigin,
dims);
std::array<sk_sp<SkImage>, 4> planes;
for (int i = 0; i < n; ++i) {
SkImageInfo info = SkImageInfo::MakeA8(dims[i]);
sk_sp<SkSurface> surf;
if (rContext) {
surf = SkSurface::MakeRenderTarget(rContext, SkBudgeted::kYes, info, 1, nullptr);
} else {
surf = SkSurface::MakeRaster(info);
}
if (!surf) {
return {};
}
SkPaint paint;
paint.setBlendMode(SkBlendMode::kSrc);
// Make a matrix with the ith row of rgbToYUV copied to the A row since we're drawing to A8.
float m[20] = {};
std::copy_n(rgbToYUV + 5*i, 5, m + 15);
paint.setColorFilter(SkColorFilters::Matrix(m));
surf->getCanvas()->drawImageRect(src,
SkRect::Make(dims[i]),
SkSamplingOptions(SkFilterMode::kLinear),
&paint);
planes[i] = surf->makeImageSnapshot();
if (!planes[i]) {
return {};
}
}
SkYUVAInfo info(src->dimensions(), config, ss, cs);
return {planes, info};
}
std::unique_ptr<LazyYUVImage> LazyYUVImage::Make(sk_sp<SkData> data,
GrMipmapped mipmapped,
sk_sp<SkColorSpace> cs) {

View File

@ -13,10 +13,22 @@
#include "include/gpu/GrBackendSurface.h"
#include "src/core/SkAutoMalloc.h"
#include <tuple>
class SkData;
namespace sk_gpu_test {
// Splits an input image into A8 YUV[A] planes using the passed subsampling and YUV color space. If
// the src image is opaque there will be three planes (Y, U, and V) and if not there will be a
// fourth A plane. The planes are returned along with a SkYUVAInfo describing the resulting planar
// image. Images are made as textures if GrRecordingContext is not null, otherwise as cpu images.
std::tuple<std::array<sk_sp<SkImage>, SkYUVAInfo::kMaxPlanes>, SkYUVAInfo>
MakeYUVAPlanesAsA8(SkImage*,
SkYUVColorSpace,
SkYUVAInfo::Subsampling,
GrRecordingContext*);
// Utility that decodes a JPEG but preserves the YUVA8 planes in the image, and uses
// MakeFromYUVAPixmaps to create a GPU multiplane YUVA image for a context. It extracts the planar
// data once, and lazily creates the actual SkImage when the GrContext is provided (and refreshes