Revert "Move all YUVA image creation in GMs into sk_gpu_test::LazyYUVImage."

This reverts commit db0288d747.

Reason for revert: undeclared tuple size

Original change's description:
> Move all YUVA image creation in GMs into sk_gpu_test::LazyYUVImage.
>
> LazyYUVImage now supports making images from a generator and from
> textures. It uses ManagedBackendTexture to manage texture plane
> lifetime via ref-counting.
>
> Adds some supporting utility functions to SkYUVAInfo and
> SkYUVAPixmaps.
>
> Eases transition of forthcoming MakeFromYUVATextures API change.
>
> Bug: skia:10632
>
> Change-Id: I8cfd747c27076d1627da6ea8a169e554a96049e0
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/326720
> Reviewed-by: Robert Phillips <robertphillips@google.com>
> Commit-Queue: Brian Salomon <bsalomon@google.com>

TBR=bsalomon@google.com,robertphillips@google.com

Change-Id: Icdfb70f7dadd97eace8f88d5a886d31534102f5f
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: skia:10632
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/327622
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
This commit is contained in:
Brian Salomon 2020-10-16 13:30:54 +00:00 committed by Skia Commit-Bot
parent db0288d747
commit 839fb228ac
14 changed files with 865 additions and 558 deletions

View File

@ -11,15 +11,14 @@
#include "include/core/SkPaint.h"
#include "include/core/SkRect.h"
#include "include/core/SkSurface.h"
#include "include/core/SkYUVAInfo.h"
#include "include/core/SkYUVAPixmaps.h"
#include "include/core/SkYUVAIndex.h"
#include "include/gpu/GrDirectContext.h"
#include "include/gpu/GrRecordingContext.h"
#include "src/core/SkAutoPixmapStorage.h"
#include "src/core/SkConvertPixels.h"
#include "src/core/SkScopeExit.h"
#include "tools/Resources.h"
#include "tools/ToolUtils.h"
#include "tools/gpu/YUVUtils.h"
namespace {
struct AsyncContext {
@ -94,17 +93,33 @@ static sk_sp<SkImage> do_read_and_scale_yuv(Src* src,
if (!asyncContext.fResult) {
return nullptr;
}
SkYUVAInfo yuvaInfo(size, SkYUVAInfo::PlanarConfig::kY_U_V_420, yuvCS);
SkPixmap yuvPMs[] = {
{yII, asyncContext.fResult->data(0), asyncContext.fResult->rowBytes(0)},
{uvII, asyncContext.fResult->data(1), asyncContext.fResult->rowBytes(1)},
{uvII, asyncContext.fResult->data(2), asyncContext.fResult->rowBytes(2)}
GrBackendTexture backendTextures[3];
SkPixmap yPM(yII, asyncContext.fResult->data(0), asyncContext.fResult->rowBytes(0));
SkPixmap uPM(uvII, asyncContext.fResult->data(1), asyncContext.fResult->rowBytes(1));
SkPixmap vPM(uvII, asyncContext.fResult->data(2), asyncContext.fResult->rowBytes(2));
backendTextures[0] = direct->createBackendTexture(yPM, GrRenderable::kNo, GrProtected::kNo);
backendTextures[1] = direct->createBackendTexture(uPM, GrRenderable::kNo, GrProtected::kNo);
backendTextures[2] = direct->createBackendTexture(vPM, GrRenderable::kNo, GrProtected::kNo);
SkYUVAIndex indices[4] = {
{ 0, SkColorChannel::kR},
{ 1, SkColorChannel::kR},
{ 2, SkColorChannel::kR},
{-1, SkColorChannel::kR}
};
auto pixmaps = SkYUVAPixmaps::FromExternalPixmaps(yuvaInfo, yuvPMs);
SkASSERT(pixmaps.isValid());
auto lazyYUVImage = sk_gpu_test::LazyYUVImage::Make(pixmaps);
SkASSERT(lazyYUVImage);
return lazyYUVImage->refImage(direct, sk_gpu_test::LazyYUVImage::Type::kFromTextures);
*cleanup = {[direct, backendTextures] {
direct->flush();
direct->submit(true);
direct->deleteBackendTexture(backendTextures[0]);
direct->deleteBackendTexture(backendTextures[1]);
direct->deleteBackendTexture(backendTextures[2]);
}};
return SkImage::MakeFromYUVATextures(direct, yuvCS, backendTextures, indices, size,
kTopLeft_GrSurfaceOrigin, SkColorSpace::MakeSRGB());
}
// Draws a grid of rescales. The columns are none, low, and high filter quality. The rows are

View File

@ -897,8 +897,7 @@ public:
int drawTiles(SkCanvas* canvas) override {
// Refresh the SkImage at the start, so that it's not attempted for every set entry
if (fYUVData) {
fImage = fYUVData->refImage(canvas->recordingContext(),
sk_gpu_test::LazyYUVImage::Type::kFromPixmaps);
fImage = fYUVData->refImage(canvas->recordingContext());
if (!fImage) {
return 0;
}

View File

@ -33,7 +33,7 @@ DEF_SIMPLE_GM_CAN_FAIL(ducky_yuv_blend, canvas, errorMsg, 560, 1130) {
auto lazyYUV = sk_gpu_test::LazyYUVImage::Make(GetResourceAsData("images/ducky.jpg"),
GrMipmapped::kYes);
if (lazyYUV) {
duckyFG[1] = lazyYUV->refImage(rContext, sk_gpu_test::LazyYUVImage::Type::kFromPixmaps);
duckyFG[1] = lazyYUV->refImage(rContext);
}
if (!duckyFG[1]) {
return skiagm::DrawResult::kFail;

View File

@ -32,6 +32,8 @@
#include "tools/Resources.h"
#include "tools/gpu/YUVUtils.h"
using sk_gpu_test::YUVABackendReleaseContext;
class GrRenderTargetContext;
namespace skiagm {
@ -48,90 +50,128 @@ protected:
SkISize onISize() override { return {1420, 610}; }
static std::unique_ptr<sk_gpu_test::LazyYUVImage> CreatePlanes(const char* name) {
static SkBitmap CreateBmpAndPlanes(const char* name, SkBitmap yuvaBmps[4]) {
SkBitmap bmp;
if (!GetResourceAsBitmap(name, &bmp)) {
return {};
}
if (bmp.colorType() != kRGBA_8888_SkColorType) {
auto info = bmp.info().makeColorType(kRGBA_8888_SkColorType);
SkBitmap copy;
copy.allocPixels(info);
SkAssertResult(bmp.readPixels(copy.pixmap()));
bmp = copy;
}
SkYUVAPixmapInfo pixmapInfo({bmp.dimensions(),
SkYUVAInfo::PlanarConfig::kY_U_V_A_4204,
kJPEG_Full_SkYUVColorSpace},
SkYUVAPixmapInfo::DataType::kUnorm8,
nullptr);
auto pixmaps = SkYUVAPixmaps::Allocate(pixmapInfo);
auto ii = SkImageInfo::Make(bmp.dimensions(), kRGBA_8888_SkColorType, kPremul_SkAlphaType);
SkBitmap rgbaBmp;
rgbaBmp.allocPixels(ii);
bmp.readPixels(rgbaBmp.pixmap(), 0, 0);
SkImageInfo yaInfo = SkImageInfo::Make(rgbaBmp.dimensions(), kAlpha_8_SkColorType,
kUnpremul_SkAlphaType);
yuvaBmps[0].allocPixels(yaInfo);
SkISize uvSize = {rgbaBmp.width()/2, rgbaBmp.height()/2};
SkImageInfo uvInfo = SkImageInfo::Make(uvSize, kAlpha_8_SkColorType, kUnpremul_SkAlphaType);
yuvaBmps[1].allocPixels(uvInfo);
yuvaBmps[2].allocPixels(uvInfo);
yuvaBmps[3].allocPixels(yaInfo);
unsigned char* yuvPixels[] = {
static_cast<unsigned char*>(pixmaps.planes()[0].writable_addr()),
static_cast<unsigned char*>(pixmaps.planes()[1].writable_addr()),
static_cast<unsigned char*>(pixmaps.planes()[2].writable_addr()),
static_cast<unsigned char*>(pixmaps.planes()[3].writable_addr()),
static_cast<unsigned char*>(yuvaBmps[0].getPixels()),
static_cast<unsigned char*>(yuvaBmps[1].getPixels()),
static_cast<unsigned char*>(yuvaBmps[2].getPixels()),
static_cast<unsigned char*>(yuvaBmps[3].getPixels()),
};
float m[20];
SkColorMatrix_RGB2YUV(pixmaps.yuvaInfo().yuvColorSpace(), m);
SkColorMatrix_RGB2YUV(kJPEG_SkYUVColorSpace, m);
// 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 j = 0; j < pixmaps.planes()[0].height(); ++j) {
for (int i = 0; i < pixmaps.planes()[0].width(); ++i) {
auto rgba = *bmp.getAddr32(i, j);
for (int j = 0; j < yaInfo.height(); ++j) {
for (int i = 0; i < yaInfo.width(); ++i) {
auto rgba = *rgbaBmp.getAddr32(i, j);
auto r = (rgba & 0x000000ff) >> 0;
auto g = (rgba & 0x0000ff00) >> 8;
auto b = (rgba & 0x00ff0000) >> 16;
auto a = (rgba & 0xff000000) >> 24;
yuvPixels[0][j*pixmaps.planes()[0].width() + i] = SkToU8(
yuvPixels[0][j*yaInfo.width() + i] = SkToU8(
sk_float_round2int(m[0]*r + m[1]*g + m[2]*b + m[3]*a + 255*m[4]));
yuvPixels[3][j*pixmaps.planes()[0].width() + i] = SkToU8(sk_float_round2int(
yuvPixels[3][j*yaInfo.width() + i] = SkToU8(sk_float_round2int(
m[15]*r + m[16]*g + m[17]*b + m[18]*a + 255*m[19]));
}
}
for (int j = 0; j < pixmaps.planes()[1].height(); ++j) {
for (int i = 0; i < pixmaps.planes()[1].width(); ++i) {
for (int j = 0; j < uvInfo.height(); ++j) {
for (int i = 0; i < uvInfo.width(); ++i) {
// Average together 4 pixels of RGB.
int rgba[] = {0, 0, 0, 0};
int denom = 0;
int ylimit = std::min(2*j + 2, pixmaps.planes()[0].height());
int xlimit = std::min(2*i + 2, pixmaps.planes()[0].width());
for (int y = 2*j; y < ylimit; ++y) {
for (int x = 2*i; x < xlimit; ++x) {
auto src = *bmp.getAddr32(x, y);
for (int y = 0; y < 2; ++y) {
for (int x = 0; x < 2; ++x) {
auto src = *rgbaBmp.getAddr32(2 * i + x, 2 * j + y);
rgba[0] += (src & 0x000000ff) >> 0;
rgba[1] += (src & 0x0000ff00) >> 8;
rgba[2] += (src & 0x00ff0000) >> 16;
rgba[3] += (src & 0xff000000) >> 24;
++denom;
}
}
for (int c = 0; c < 4; ++c) {
rgba[c] /= denom;
rgba[c] /= 4;
}
int uvIndex = j*pixmaps.planes()[1].width() + i;
int uvIndex = j*uvInfo.width() + i;
yuvPixels[1][uvIndex] = SkToU8(sk_float_round2int(
m[5]*rgba[0] + m[6]*rgba[1] + m[7]*rgba[2] + m[8]*rgba[3] + 255*m[9]));
yuvPixels[2][uvIndex] = SkToU8(sk_float_round2int(
m[10]*rgba[0] + m[11]*rgba[1] + m[12]*rgba[2] + m[13]*rgba[3] + 255*m[14]));
}
}
return sk_gpu_test::LazyYUVImage::Make(std::move(pixmaps));
return rgbaBmp;
}
static bool CreateYUVBackendTextures(GrDirectContext* context, SkBitmap bmps[4],
SkYUVAIndex indices[4],
YUVABackendReleaseContext* beContext) {
for (int i = 0; i < 4; ++i) {
GrBackendTexture tmp = context->createBackendTexture(
bmps[i].pixmap(), GrRenderable::kNo, GrProtected::kNo,
YUVABackendReleaseContext::CreationCompleteProc(i),
beContext);
if (!tmp.isValid()) {
return false;
}
beContext->set(i, tmp);
}
for (int i = 0; i < 4; ++i) {
auto chanMask = beContext->beTexture(i).getBackendFormat().channelMask();
// We expect the single channel bitmaps to produce single channel textures.
SkASSERT(chanMask && SkIsPow2(chanMask));
if (chanMask & kGray_SkColorChannelFlag) {
indices[i].fChannel = SkColorChannel::kR;
} else {
indices[i].fChannel = static_cast<SkColorChannel>(31 - SkCLZ(chanMask));
}
indices[i].fIndex = i;
}
return true;
}
sk_sp<SkImage> makeYUVAImage(GrDirectContext* context) {
return fLazyYUVImage->refImage(context, sk_gpu_test::LazyYUVImage::Type::kFromTextures);
}
auto releaseContext = new YUVABackendReleaseContext(context);
SkYUVAIndex indices[4];
sk_sp<SkImage> createReferenceImage(GrDirectContext* dContext) {
auto planarImage = this->makeYUVAImage(dContext);
if (!planarImage) {
if (!CreateYUVBackendTextures(context, fYUVABmps, indices, releaseContext)) {
YUVABackendReleaseContext::Unwind(context, releaseContext, false);
return nullptr;
}
auto resultInfo = SkImageInfo::Make(fLazyYUVImage->dimensions(),
return SkImage::MakeFromYUVATextures(context,
kJPEG_SkYUVColorSpace,
releaseContext->beTextures(),
indices,
fRGBABmp.dimensions(),
kTopLeft_GrSurfaceOrigin,
nullptr,
YUVABackendReleaseContext::Release,
releaseContext);
}
sk_sp<SkImage> createReferenceImage(GrDirectContext* dContext) {
auto resultInfo = SkImageInfo::Make(fRGBABmp.dimensions(),
kRGBA_8888_SkColorType,
kPremul_SkAlphaType);
auto resultSurface = SkSurface::MakeRenderTarget(dContext,
@ -144,7 +184,27 @@ protected:
return nullptr;
}
resultSurface->getCanvas()->drawImage(std::move(planarImage), 0, 0);
auto planeReleaseContext = new YUVABackendReleaseContext(dContext);
SkYUVAIndex indices[4];
if (!CreateYUVBackendTextures(dContext, fYUVABmps, indices, planeReleaseContext)) {
YUVABackendReleaseContext::Unwind(dContext, planeReleaseContext, false);
return nullptr;
}
auto tmp = SkImage::MakeFromYUVATextures(dContext,
kJPEG_SkYUVColorSpace,
planeReleaseContext->beTextures(),
indices,
fRGBABmp.dimensions(),
kTopLeft_GrSurfaceOrigin,
nullptr);
if (!tmp) {
YUVABackendReleaseContext::Unwind(dContext, planeReleaseContext, false);
return nullptr;
}
resultSurface->getCanvas()->drawImage(std::move(tmp), 0, 0);
YUVABackendReleaseContext::Unwind(dContext, planeReleaseContext, true);
return resultSurface->makeImageSnapshot();
}
@ -153,9 +213,7 @@ protected:
return DrawResult::kSkip;
}
if (!fLazyYUVImage) {
fLazyYUVImage = CreatePlanes("images/mandrill_32.png");
}
fRGBABmp = CreateBmpAndPlanes("images/mandrill_32.png", fYUVABmps);
// We make a version of this image for each draw because, if any draw flattens it to
// RGBA, then all subsequent draws would use the RGBA texture.
@ -262,7 +320,8 @@ protected:
}
private:
std::unique_ptr<sk_gpu_test::LazyYUVImage> fLazyYUVImage;
SkBitmap fRGBABmp; // TODO: oddly, it looks like this could just be an SkISize
SkBitmap fYUVABmps[4];
// 3 draws x 3 scales x 4 filter qualities
static constexpr int kNumImages = 3 * 3 * 4;

View File

@ -57,6 +57,7 @@
#include <memory>
#include <utility>
using sk_gpu_test::YUVABackendReleaseContext;
class GrRenderTargetContext;
static const int kTileWidthHeight = 128;
@ -115,51 +116,77 @@ static bool has_alpha_channel(YUVFormat format) {
class YUVAPlanarConfig {
public:
enum class YUVAChannel { kY, kU, kV, kA };
YUVAPlanarConfig(YUVFormat format, bool opaque) {
switch (format) {
case kP016_YUVFormat:
case kP010_YUVFormat:
case kP016F_YUVFormat:
case kNV12_YUVFormat:
fLocations[0] = {0, 0};
fLocations[1] = {1, 0};
fLocations[2] = {1, 1};
if (opaque) {
fPlanarConfig = SkYUVAInfo::PlanarConfig::kY_UV_420;
} else {
fLocations[3] = {2, 0};
fPlanarConfig = SkYUVAInfo::PlanarConfig::kY_UV_A_4204;
}
break;
case kY416_YUVFormat:
case kY410_YUVFormat:
fLocations[0] = {0, 1};
fLocations[1] = {0, 0};
fLocations[2] = {0, 2};
if (opaque) {
fPlanarConfig = SkYUVAInfo::PlanarConfig::kUYV_444;
} else {
fLocations[3] = {0, 3};
fPlanarConfig = SkYUVAInfo::PlanarConfig::kUYVA_4444;
}
break;
case kAYUV_YUVFormat:
fLocations[0] = {0, 0};
fLocations[1] = {0, 1};
fLocations[2] = {0, 2};
if (opaque) {
fPlanarConfig = SkYUVAInfo::PlanarConfig::kYUV_444;
} else {
fLocations[3] = {0, 3};
fPlanarConfig = SkYUVAInfo::PlanarConfig::kYUVA_4444;
}
break;
case kNV21_YUVFormat:
fLocations[0] = {0, 0};
fLocations[1] = {1, 1};
fLocations[2] = {1, 0};
if (opaque) {
fPlanarConfig = SkYUVAInfo::PlanarConfig::kY_VU_420;
} else {
fLocations[3] = {2, 0};
fPlanarConfig = SkYUVAInfo::PlanarConfig::kY_VU_A_4204;
}
break;
case kI420_YUVFormat:
fLocations[0] = {0, 0};
fLocations[1] = {1, 0};
fLocations[2] = {2, 0};
if (opaque) {
fPlanarConfig = SkYUVAInfo::PlanarConfig::kY_U_V_420;
} else {
fLocations[3] = {3, 0};
fPlanarConfig = SkYUVAInfo::PlanarConfig::kY_U_V_A_4204;
}
break;
case kYV12_YUVFormat:
fLocations[0] = {0, 0};
fLocations[1] = {2, 0};
fLocations[2] = {1, 0};
if (opaque) {
fPlanarConfig = SkYUVAInfo::PlanarConfig::kY_V_U_420;
} else {
fLocations[3] = {3, 0};
fPlanarConfig = SkYUVAInfo::PlanarConfig::kY_V_U_A_4204;
}
break;
@ -168,20 +195,129 @@ public:
int numPlanes() const { return SkYUVAInfo::NumPlanes(fPlanarConfig); }
int planeIndex(YUVAChannel c) const { return fLocations[static_cast<int>(c)].fPlaneIdx; }
int channelIndex(YUVAChannel c) const { return fLocations[static_cast<int>(c)].fChannelIdx; }
bool hasAlpha() const { return SkYUVAInfo::HasAlpha(fPlanarConfig); }
/**
* Given a mask of SkColorChannelFlags choose a channel by index. Legal 'channelMask' values
* are:
* kAlpha, kGray, kRed, kRG, kRGB, kRGBA.
* The channel index must be less than the number of bits set in the mask. The index order is
* the order listed above (e.g. if 'channelMask' is kRGB and 'channelIdx' is 1 then
* SkColorChannel::kG is returned as 'channel'). The function fails if 'channelMask' is not one
* of the listed allowed values or 'channelIdx' is invalid for the mask.
*/
static bool ChannelIndexToChannel(uint32_t channelMask,
int channelIdx,
SkColorChannel* channel);
/**
* Goes from channel indices to actual channels given texture formats. Also supports adding
* on an external alpha plane if this format doesn't already have alpha. The extra alpha texture
* must be the last texture and the channel index is assumed to be 0.
*/
bool getYUVAIndices(const GrBackendTexture textures[],
int numTextures,
SkYUVAIndex indices[4]) const;
SkYUVAInfo getYUVAInfo(SkISize dimensions, SkYUVColorSpace yuvColorSpace) const;
SkYUVAPixmaps makeYUVAPixmaps(SkISize dimensions,
SkYUVColorSpace yuvColorSpace,
const SkBitmap bitmaps[],
int numBitmaps) const;
private:
struct YUVALocation {
int fPlaneIdx = -1;
int fChannelIdx = -1;
};
SkYUVAInfo::PlanarConfig fPlanarConfig;
YUVALocation fLocations[4] = {};
};
bool YUVAPlanarConfig::ChannelIndexToChannel(uint32_t channelFlags,
int channelIdx,
SkColorChannel* channel) {
switch (channelFlags) {
case kGray_SkColorChannelFlag: // For gray returning any of R, G, or B for index 0 is ok.
case kRed_SkColorChannelFlag:
if (channelIdx == 0) {
*channel = SkColorChannel::kR;
return true;
}
return false;
case kAlpha_SkColorChannelFlag:
if (channelIdx == 0) {
*channel = SkColorChannel::kA;
return true;
}
return false;
case kRG_SkColorChannelFlags:
if (channelIdx == 0 || channelIdx == 1) {
*channel = static_cast<SkColorChannel>(channelIdx);
return true;
}
return false;
case kRGB_SkColorChannelFlags:
if (channelIdx >= 0 && channelIdx <= 2) {
*channel = static_cast<SkColorChannel>(channelIdx);
return true;
}
return false;
case kRGBA_SkColorChannelFlags:
if (channelIdx >= 0 && channelIdx <= 3) {
*channel = static_cast<SkColorChannel>(channelIdx);
return true;
}
return false;
default:
return false;
}
}
bool YUVAPlanarConfig::getYUVAIndices(const GrBackendTexture textures[],
int numTextures,
SkYUVAIndex indices[4]) const {
if (numTextures != this->numPlanes()) {
return false;
}
uint32_t channelMasks[4] = {};
for (int i = 0; i < numTextures; ++i) {
channelMasks[i] = textures[i].getBackendFormat().channelMask();
}
for (int i = 0; i < 4; ++i) {
int plane = fLocations[i].fPlaneIdx;
if (plane < 0) {
indices[i].fIndex = -1;
indices[i].fChannel = SkColorChannel::kR;
} else {
indices[i].fIndex = plane;
if (!ChannelIndexToChannel(channelMasks[plane], fLocations[i].fChannelIdx,
&indices[i].fChannel)) {
return false;
}
}
}
SkDEBUGCODE(int checkNumPlanes;)
SkASSERT(SkYUVAIndex::AreValidIndices(indices, &checkNumPlanes));
SkASSERT(checkNumPlanes == this->numPlanes());
return true;
}
SkYUVAInfo YUVAPlanarConfig::getYUVAInfo(SkISize dimensions, SkYUVColorSpace yuvColorSpace) const {
return SkYUVAInfo(dimensions, fPlanarConfig, yuvColorSpace);
}
SkYUVAPixmaps YUVAPlanarConfig::makeYUVAPixmaps(SkISize dimensions,
SkYUVColorSpace yuvColorSpace,
const SkBitmap bitmaps[],
int numBitmaps) const {
SkYUVAInfo info(dimensions, fPlanarConfig, yuvColorSpace);
SkYUVAInfo info = this->getYUVAInfo(dimensions, yuvColorSpace);
SkPixmap pmaps[SkYUVAInfo::kMaxPlanes];
int n = info.numPlanes();
if (numBitmaps < n) {
@ -193,6 +329,38 @@ SkYUVAPixmaps YUVAPlanarConfig::makeYUVAPixmaps(SkISize dimensions,
return SkYUVAPixmaps::FromExternalPixmaps(info, pmaps);
}
static bool is_colorType_texturable(const GrCaps* caps, GrColorType ct) {
GrBackendFormat format = caps->getDefaultBackendFormat(ct, GrRenderable::kNo);
if (!format.isValid()) {
return false;
}
return caps->isFormatTexturable(format);
}
static bool is_format_natively_supported(GrRecordingContext* context, YUVFormat yuvFormat) {
const GrCaps* caps = context->priv().caps();
switch (yuvFormat) {
case kP016_YUVFormat: // fall through
case kP010_YUVFormat: return is_colorType_texturable(caps, GrColorType::kAlpha_16) &&
is_colorType_texturable(caps, GrColorType::kRG_1616);
case kP016F_YUVFormat: return is_colorType_texturable(caps, GrColorType::kAlpha_F16) &&
is_colorType_texturable(caps, GrColorType::kRG_F16);
case kY416_YUVFormat: return is_colorType_texturable(caps, GrColorType::kRGBA_16161616);
case kAYUV_YUVFormat: return is_colorType_texturable(caps, GrColorType::kRGBA_8888);
case kY410_YUVFormat: return is_colorType_texturable(caps, GrColorType::kRGBA_1010102);
case kNV12_YUVFormat: // fall through
case kNV21_YUVFormat: return is_colorType_texturable(caps, GrColorType::kGray_8) &&
is_colorType_texturable(caps, GrColorType::kRG_88);
case kI420_YUVFormat: // fall through
case kYV12_YUVFormat: return is_colorType_texturable(caps, GrColorType::kGray_8);
}
SkUNREACHABLE;
}
// All the planes we need to construct the various YUV formats
struct PlaneData {
SkBitmap fYFull;
@ -359,6 +527,19 @@ static void convert_rgba_to_yuva(const float mtx[20], SkColor col, uint8_t yuv[4
yuv[3] = SkColorGetA(col);
}
static SkPMColor convert_yuva_to_rgba(const float mtx[20], uint8_t yuva[4]) {
uint8_t y = yuva[0];
uint8_t u = yuva[1];
uint8_t v = yuva[2];
uint8_t a = yuva[3];
uint8_t r = SkTPin(SkScalarRoundToInt(mtx[ 0]*y + mtx[ 1]*u + mtx[ 2]*v + mtx[ 4]*255), 0, 255);
uint8_t g = SkTPin(SkScalarRoundToInt(mtx[ 5]*y + mtx[ 6]*u + mtx[ 7]*v + mtx[ 9]*255), 0, 255);
uint8_t b = SkTPin(SkScalarRoundToInt(mtx[10]*y + mtx[11]*u + mtx[12]*v + mtx[14]*255), 0, 255);
return SkPremultiplyARGBInline(a, r, g, b);
}
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
@ -648,6 +829,118 @@ static int create_YUV(const PlaneData& planes,
return nextLayer;
}
static uint8_t look_up(float x1, float y1, const SkBitmap& bm, int channelIdx) {
SkASSERT(x1 > 0 && x1 < 1.0f);
SkASSERT(y1 > 0 && y1 < 1.0f);
int x = SkScalarFloorToInt(x1 * bm.width());
int y = SkScalarFloorToInt(y1 * bm.height());
auto channelMask = SkColorTypeChannelFlags(bm.colorType());
SkColorChannel channel;
SkAssertResult(YUVAPlanarConfig::ChannelIndexToChannel(channelMask, channelIdx, &channel));
auto ii = SkImageInfo::Make(1, 1, kRGBA_8888_SkColorType, bm.alphaType(), bm.refColorSpace());
uint32_t pixel;
SkAssertResult(bm.readPixels(ii, &pixel, sizeof(pixel), x, y));
int shift = static_cast<int>(channel) * 8;
return static_cast<uint8_t>((pixel >> shift) & 0xff);
}
class YUVGenerator : public SkImageGenerator {
public:
YUVGenerator(const SkImageInfo& ii,
YUVAPlanarConfig planarConfig,
SkYUVColorSpace yuvColorSpace,
SkBitmap bitmaps[SkYUVASizeInfo::kMaxCount])
: SkImageGenerator(ii), fPlanarConfig(planarConfig), fYUVColorSpace(yuvColorSpace) {
int numPlanes = fPlanarConfig.numPlanes();
for (int i = 0; i < numPlanes; ++i) {
fYUVBitmaps[i] = bitmaps[i];
SkASSERT(!bitmaps[i].drawsNothing());
}
}
protected:
bool onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
const Options&) override {
if (kUnknown_SkColorType == fFlattened.colorType()) {
fFlattened.allocPixels(info);
SkASSERT(kN32_SkColorType == info.colorType());
SkASSERT(kPremul_SkAlphaType == info.alphaType());
float mtx[20];
SkColorMatrix_YUV2RGB(fYUVColorSpace, mtx);
for (int y = 0; y < info.height(); ++y) {
for (int x = 0; x < info.width(); ++x) {
float x1 = (x + 0.5f) / info.width();
float y1 = (y + 0.5f) / info.height();
uint8_t yuva[4] = {0, 0, 0, 255};
using YUVAChannel = YUVAPlanarConfig::YUVAChannel;
for (auto c : {YUVAChannel::kY, YUVAChannel::kU, YUVAChannel::kV}) {
const auto& bmp = fYUVBitmaps[fPlanarConfig.planeIndex(c)];
int channelIdx = fPlanarConfig.channelIndex(c);
yuva[static_cast<int>(c)] = look_up(x1, y1, bmp, channelIdx);
}
if (fPlanarConfig.hasAlpha()) {
const auto& bmp = fYUVBitmaps[fPlanarConfig.planeIndex(YUVAChannel::kA)];
int channelIdx = fPlanarConfig.channelIndex(YUVAChannel::kA);
yuva[3] = look_up(x1, y1, bmp, channelIdx);
}
// Making premul here.
*fFlattened.getAddr32(x, y) = convert_yuva_to_rgba(mtx, yuva);
}
}
}
return fFlattened.readPixels(info, pixels, rowBytes, 0, 0);
}
bool onQueryYUVAInfo(const SkYUVAPixmapInfo::SupportedDataTypes& types,
SkYUVAPixmapInfo* info) const override {
SkYUVAInfo yuvaInfo =
fPlanarConfig.getYUVAInfo(this->getInfo().dimensions(), fYUVColorSpace);
SkColorType colorTypes[SkYUVAInfo::kMaxPlanes] = {};
for (int i = 0; i < yuvaInfo.numPlanes(); ++i) {
colorTypes[i] = fYUVBitmaps[i].colorType();
}
*info = SkYUVAPixmapInfo(yuvaInfo, colorTypes, /* row bytes */ nullptr);
SkASSERT(info->isValid());
return true;
}
bool onGetYUVAPlanes(const SkYUVAPixmaps& pixmaps) override {
int n = pixmaps.numPlanes();
for (int i = 0; i < n; ++i) {
SkASSERT(pixmaps.plane(i).dimensions() == fYUVBitmaps[i].dimensions());
SkASSERT(pixmaps.plane(i).colorType() == fYUVBitmaps[i].colorType());
SkRectMemcpy(pixmaps.plane(i).writable_addr(), pixmaps.plane(i).rowBytes(),
fYUVBitmaps[i].getPixels(), fYUVBitmaps[i].rowBytes(),
fYUVBitmaps[i].info().minRowBytes(), fYUVBitmaps[i].height());
}
return true;
}
private:
YUVAPlanarConfig fPlanarConfig;
SkYUVColorSpace fYUVColorSpace;
SkBitmap fYUVBitmaps[SkYUVAInfo::kMaxPlanes];
SkBitmap fFlattened;
};
static sk_sp<SkImage> make_yuv_gen_image(const SkImageInfo& ii,
YUVAPlanarConfig planarConfig,
SkYUVColorSpace yuvColorSpace,
SkBitmap bitmaps[]) {
auto gen = std::make_unique<YUVGenerator>(ii, planarConfig, yuvColorSpace, bitmaps);
return SkImage::MakeFromGenerator(std::move(gen));
}
static void draw_col_label(SkCanvas* canvas, int x, int yuvColorSpace, bool opaque) {
static const char* kYUVColorSpaceNames[] = {"JPEG", "601", "709F", "709L",
"2020_8F", "2020_8L", "2020_10F", "2020_10L",
@ -695,6 +988,13 @@ static void draw_row_label(SkCanvas* canvas, int y, int yuvFormat) {
canvas->drawString(rowLabel, 0, y, font, paint);
}
static GrBackendTexture create_yuva_texture(GrDirectContext* context, const SkBitmap& bm, int index,
YUVABackendReleaseContext* releaseContext) {
return context->createBackendTexture(bm.pixmap(), GrRenderable::kNo, GrProtected::kNo,
YUVABackendReleaseContext::CreationCompleteProc(index),
releaseContext);
}
static sk_sp<SkColorFilter> yuv_to_rgb_colorfilter() {
static const float kJPEGConversionMatrix[20] = {
1.0f, 0.0f, 1.402f, 0.0f, -180.0f/255,
@ -726,10 +1026,18 @@ namespace skiagm {
// YV12
class WackyYUVFormatsGM : public GM {
public:
using Type = sk_gpu_test::LazyYUVImage::Type;
// This GM has a variety of ways in which the test images can be constructed.
enum class ImageType {
kYUVAPixmaps, // SkImage::MakeFromYUVAPixmaps.
kYUVATextures, // SkImage::MakeFromYUVATextures.
kGenerator, // SkImage_Lazy backed by generator that supports YUVA. This is the only
// mode that runs on CPU but CPU uses the flattening onGetPixels.
};
WackyYUVFormatsGM(bool useTargetColorSpace, bool useSubset, Type type)
: fUseTargetColorSpace(useTargetColorSpace), fUseSubset(useSubset), fImageType(type) {
WackyYUVFormatsGM(bool useTargetColorSpace, bool useSubset, ImageType imageType)
: fUseTargetColorSpace(useTargetColorSpace)
, fUseSubset(useSubset)
, fImageType(imageType) {
this->setBGColor(0xFFCCCCCC);
}
@ -743,12 +1051,12 @@ protected:
name += "_domain";
}
switch (fImageType) {
case Type::kFromPixmaps:
case ImageType::kYUVAPixmaps:
name += "_frompixmaps";
break;
case Type::kFromTextures:
case ImageType::kYUVATextures:
break;
case Type::kFromGenerator:
case ImageType::kGenerator:
name += "_imggen";
break;
}
@ -800,14 +1108,65 @@ protected:
int numPlanes = create_YUV(planes, format, resultBMs, opaque);
const YUVAPlanarConfig planarConfig(format, opaque);
SkYUVAPixmaps pixmaps =
planarConfig.makeYUVAPixmaps(fOriginalBMs[opaque].dimensions(),
static_cast<SkYUVColorSpace>(cs),
resultBMs,
numPlanes);
auto lazyYUV = sk_gpu_test::LazyYUVImage::Make(std::move(pixmaps));
SkASSERT(numPlanes == planarConfig.numPlanes());
fImages[opaque][cs][format] = lazyYUV->refImage(dContext, fImageType);
if (fImageType == ImageType::kYUVATextures) {
SkASSERT(dContext);
if (dContext->abandoned()) {
return false;
}
if (!is_format_natively_supported(dContext, format)) {
continue;
}
auto releaseCtx = new YUVABackendReleaseContext(dContext);
for (int i = 0; i < numPlanes; ++i) {
GrBackendTexture tmp = create_yuva_texture(dContext, resultBMs[i], i,
releaseCtx);
if (!tmp.isValid()) {
YUVABackendReleaseContext::Unwind(dContext, releaseCtx, false);
return false;
}
releaseCtx->set(i, tmp);
}
SkYUVAIndex yuvaIndices[4];
if (!planarConfig.getYUVAIndices(releaseCtx->beTextures(),
numPlanes,
yuvaIndices)) {
YUVABackendReleaseContext::Unwind(dContext, releaseCtx, false);
continue;
}
fImages[opaque][cs][format] =
SkImage::MakeFromYUVATextures(dContext,
(SkYUVColorSpace)cs,
releaseCtx->beTextures(),
yuvaIndices,
fOriginalBMs[opaque].dimensions(),
kTopLeft_GrSurfaceOrigin,
nullptr,
YUVABackendReleaseContext::Release,
releaseCtx);
} else if (fImageType == ImageType::kGenerator) {
SkImageInfo ii = SkImageInfo::MakeN32(fOriginalBMs[opaque].width(),
fOriginalBMs[opaque].height(),
kPremul_SkAlphaType);
fImages[opaque][cs][format] = make_yuv_gen_image(
ii, planarConfig, (SkYUVColorSpace)cs, resultBMs);
} else if (fImageType == ImageType::kYUVAPixmaps) {
SkASSERT(dContext);
SkYUVAPixmaps pixmaps =
planarConfig.makeYUVAPixmaps(fOriginalBMs[opaque].dimensions(),
static_cast<SkYUVColorSpace>(cs),
resultBMs,
numPlanes);
fImages[opaque][cs][format] =
SkImage::MakeFromYUVAPixmaps(dContext, pixmaps);
}
}
}
}
@ -833,7 +1192,7 @@ protected:
}
// Only the generator is expected to work with the CPU backend.
if (fImageType != Type::kFromGenerator && !dContext) {
if (fImageType != ImageType::kGenerator && !dContext) {
return DrawResult::kSkip;
}
@ -919,7 +1278,7 @@ private:
sk_sp<SkImage> fImages[2][kLastEnum_SkYUVColorSpace + 1][kLast_YUVFormat + 1];
bool fUseTargetColorSpace;
bool fUseSubset;
Type fImageType;
ImageType fImageType;
sk_sp<SkColorSpace> fTargetColorSpace;
using INHERITED = GM;
@ -929,19 +1288,19 @@ private:
DEF_GM(return new WackyYUVFormatsGM(/* target cs */ false,
/* subset */ false,
WackyYUVFormatsGM::Type::kFromTextures);)
WackyYUVFormatsGM::ImageType::kYUVATextures);)
DEF_GM(return new WackyYUVFormatsGM(/* target cs */ false,
/* subset */ true,
WackyYUVFormatsGM::Type::kFromTextures);)
WackyYUVFormatsGM::ImageType::kYUVATextures);)
DEF_GM(return new WackyYUVFormatsGM(/* target cs */ true,
/* subset */ false,
WackyYUVFormatsGM::Type::kFromTextures);)
WackyYUVFormatsGM::ImageType::kYUVATextures);)
DEF_GM(return new WackyYUVFormatsGM(/* target cs */ false,
/* subset */ false,
WackyYUVFormatsGM::Type::kFromGenerator);)
WackyYUVFormatsGM::ImageType::kGenerator);)
DEF_GM(return new WackyYUVFormatsGM(/* target cs */ false,
/* subset */ false,
WackyYUVFormatsGM::Type::kFromPixmaps);)
WackyYUVFormatsGM::ImageType::kYUVAPixmaps);)
class YUVMakeColorSpaceGM : public GpuGM {
public:
@ -993,21 +1352,52 @@ protected:
create_YUV(planes, kAYUV_YUVFormat, resultBMs, opaque);
YUVAPlanarConfig planarConfig(kAYUV_YUVFormat, opaque);
int numPlanes = planarConfig.numPlanes();
auto yuvaPixmaps = planarConfig.makeYUVAPixmaps(fOriginalBMs[opaque].dimensions(),
kJPEG_Full_SkYUVColorSpace,
resultBMs,
SK_ARRAY_COUNT(resultBMs));
auto releaseContext = new YUVABackendReleaseContext(context);
auto srgbReleaseContext = new YUVABackendReleaseContext(context);
int i = 0;
for (sk_sp<SkColorSpace> cs : {sk_sp<SkColorSpace>(nullptr),
SkColorSpace::MakeSRGB()}) {
auto lazyYUV = sk_gpu_test::LazyYUVImage::Make(yuvaPixmaps,
GrMipmapped::kNo,
std::move(cs));
fImages[opaque][i++] =
lazyYUV->refImage(context, sk_gpu_test::LazyYUVImage::Type::kFromTextures);
for (int i = 0; i < numPlanes; ++i) {
GrBackendTexture tmp = create_yuva_texture(context, resultBMs[i], i,
releaseContext);
if (!tmp.isValid()) {
YUVABackendReleaseContext::Unwind(context, releaseContext, false);
YUVABackendReleaseContext::Unwind(context, srgbReleaseContext, false);
return false;
}
releaseContext->set(i, tmp);
tmp = create_yuva_texture(context, resultBMs[i], i, srgbReleaseContext);
if (!tmp.isValid()) {
YUVABackendReleaseContext::Unwind(context, releaseContext, false);
YUVABackendReleaseContext::Unwind(context, srgbReleaseContext, false);
return false;
}
srgbReleaseContext->set(i, tmp);
}
SkYUVAIndex yuvaIndices[4];
planarConfig.getYUVAIndices(releaseContext->beTextures(), numPlanes, yuvaIndices);
fImages[opaque][0] = SkImage::MakeFromYUVATextures(
context,
kJPEG_SkYUVColorSpace,
releaseContext->beTextures(),
yuvaIndices,
{ fOriginalBMs[opaque].width(), fOriginalBMs[opaque].height() },
kTopLeft_GrSurfaceOrigin, nullptr,
YUVABackendReleaseContext::Release, releaseContext);
fImages[opaque][1] = SkImage::MakeFromYUVATextures(
context,
kJPEG_SkYUVColorSpace,
srgbReleaseContext->beTextures(),
yuvaIndices,
{ fOriginalBMs[opaque].width(), fOriginalBMs[opaque].height() },
kTopLeft_GrSurfaceOrigin,
SkColorSpace::MakeSRGB(),
YUVABackendReleaseContext::Release, srgbReleaseContext);
}
// Some backends (e.g., Vulkan) require all work be completed for backend textures before

View File

@ -49,7 +49,7 @@ static sk_sp<SkImage> make_image(GrRecordingContext* rContext) {
if (!imageHelper) {
return nullptr;
}
return imageHelper->refImage(rContext, sk_gpu_test::LazyYUVImage::Type::kFromPixmaps);
return imageHelper->refImage(rContext);
}
// This GM tests that the YUVA image code path in the GPU backend handles odd sized images with

View File

@ -11,7 +11,6 @@
#include "include/codec/SkEncodedOrigin.h"
#include "include/core/SkImageInfo.h"
#include "include/core/SkSize.h"
#include "include/core/SkYUVAIndex.h"
/**
* Specifies the structure of planes for a YUV image with optional alpha. The actual planar data
@ -98,15 +97,6 @@ public:
*/
static constexpr int NumChannelsInPlane(PlanarConfig, int i);
/**
* Given a PlanarConfig and a set of channel flags for each plane, convert to SkYUVAIndex
* representation. Fails if channel flags aren't valid for the PlanarConfig (i.e. don't have
* enough channels in a plane).
*/
static bool GetYUVAIndices(PlanarConfig,
const uint32_t planeChannelFlags[kMaxPlanes],
SkYUVAIndex indices[SkYUVAIndex::kIndexCount]);
/** Does the PlanarConfig have alpha values? */
static bool HasAlpha(PlanarConfig);
@ -165,15 +155,6 @@ public:
int numChannelsInPlane(int i) const { return NumChannelsInPlane(fPlanarConfig, i); }
/**
* Given a set of channel flags for each plane, converts this->planarConfig() to SkYUVAIndex
* representation. Fails if the channel flags aren't valid for the PlanarConfig (i.e. don't have
* enough channels in a plane).
*/
bool toYUVAIndices(const uint32_t channelFlags[4], SkYUVAIndex indices[4]) const {
return GetYUVAIndices(fPlanarConfig, channelFlags, indices);
}
bool operator==(const SkYUVAInfo& that) const;
bool operator!=(const SkYUVAInfo& that) const { return !(*this == that); }

View File

@ -182,12 +182,6 @@ public:
*/
static SkYUVAPixmaps FromData(const SkYUVAPixmapInfo&, sk_sp<SkData>);
/**
* Makes a deep copy of the src SkYUVAPixmaps. The returned SkYUVAPixmaps owns its planes'
* backing stores.
*/
static SkYUVAPixmaps MakeCopy(const SkYUVAPixmaps& src);
/**
* Use passed in memory as backing store for pixmaps' pixels. Caller must ensure memory remains
* allocated while pixmaps are in use. There must be at least
@ -217,8 +211,6 @@ public:
const SkYUVAInfo& yuvaInfo() const { return fYUVAInfo; }
SkYUVAPixmapInfo pixmapsInfo() const;
/** Number of pixmap planes or 0 if this SkYUVAPixmaps is invalid. */
int numPlanes() const { return this->isValid() ? fYUVAInfo.numPlanes() : 0; }
@ -234,19 +226,10 @@ public:
*/
const SkPixmap& plane(int i) const { return fPlanes[SkToSizeT(i)]; }
/**
* Computes a SkYUVAIndex representation of the planar layout. Returns true on success and
* false on failure. Will succeed whenever this->isValid() is true.
*/
bool toYUVAIndices(SkYUVAIndex[SkYUVAIndex::kIndexCount]) const;
/** Does this SkPixmaps own the backing store of the planes? */
bool ownsStorage() const { return SkToBool(fData); }
/**
* Conversion to legacy SkYUVA data structures.
*/
bool toLegacy(SkYUVASizeInfo*, SkYUVAIndex[SkYUVAIndex::kIndexCount]) const;
bool toLegacy(SkYUVASizeInfo*, SkYUVAIndex[4]) const;
private:
SkYUVAPixmaps(const SkYUVAPixmapInfo&, sk_sp<SkData>);

View File

@ -79,135 +79,6 @@ int SkYUVAInfo::PlaneDimensions(SkISize imageDimensions,
SkUNREACHABLE;
}
static bool channel_index_to_channel(uint32_t channelFlags,
int channelIdx,
SkColorChannel* channel) {
switch (channelFlags) {
case kGray_SkColorChannelFlag: // For gray returning any of R, G, or B for index 0 is ok.
case kRed_SkColorChannelFlag:
if (channelIdx == 0) {
*channel = SkColorChannel::kR;
return true;
}
return false;
case kAlpha_SkColorChannelFlag:
if (channelIdx == 0) {
*channel = SkColorChannel::kA;
return true;
}
return false;
case kRG_SkColorChannelFlags:
if (channelIdx == 0 || channelIdx == 1) {
*channel = static_cast<SkColorChannel>(channelIdx);
return true;
}
return false;
case kRGB_SkColorChannelFlags:
if (channelIdx >= 0 && channelIdx <= 2) {
*channel = static_cast<SkColorChannel>(channelIdx);
return true;
}
return false;
case kRGBA_SkColorChannelFlags:
if (channelIdx >= 0 && channelIdx <= 3) {
*channel = static_cast<SkColorChannel>(channelIdx);
return true;
}
return false;
default:
return false;
}
}
bool SkYUVAInfo::GetYUVAIndices(PlanarConfig config,
const uint32_t planeChannelFlags[kMaxPlanes],
SkYUVAIndex indices[SkYUVAIndex::kIndexCount]) {
struct Location {int plane, chanIdx;};
const Location* locations = nullptr;
switch (config) {
case PlanarConfig::kY_U_V_444:
case PlanarConfig::kY_U_V_422:
case PlanarConfig::kY_U_V_420:
case PlanarConfig::kY_U_V_440:
case PlanarConfig::kY_U_V_411:
case PlanarConfig::kY_U_V_410: {
static constexpr Location kLocations[] = {{0, 0}, {1, 0}, {2, 0}, {-1, -1}};
locations = kLocations;
break;
}
case PlanarConfig::kY_V_U_420: {
static constexpr Location kLocations[] = {{0, 0}, {2, 0}, {1, 0}, {-1, -1}};
locations = kLocations;
break;
}
case PlanarConfig::kY_U_V_A_4204: {
static constexpr Location kLocations[] = {{0, 0}, {1, 0}, {2, 0}, {3, 0}};
locations = kLocations;
break;
}
case PlanarConfig::kY_V_U_A_4204: {
static constexpr Location kLocations[] = {{0, 0}, {2, 0}, {1, 0}, {3, 0}};
locations = kLocations;
break;
}
case PlanarConfig::kY_UV_420: {
static constexpr Location kLocations[] = {{0, 0}, {1, 0}, {1, 1}, {-1, -1}};
locations = kLocations;
break;
}
case PlanarConfig::kY_VU_420: {
static constexpr Location kLocations[] = {{0, 0}, {1, 1}, {1, 0}, {-1, -1}};
locations = kLocations;
break;
}
case PlanarConfig::kY_UV_A_4204: {
static constexpr Location kLocations[] = {{0, 0}, {1, 0}, {1, 1}, {2, 0}};
locations = kLocations;
break;
}
case PlanarConfig::kY_VU_A_4204: {
static constexpr Location kLocations[] = {{0, 0}, {1, 1}, {1, 0}, {2, 0}};
locations = kLocations;
break;
}
case PlanarConfig::kYUV_444: {
static constexpr Location kLocations[] = {{0, 0}, {0, 1}, {0, 2}, {-1, -1}};
locations = kLocations;
break;
}
case PlanarConfig::kUYV_444: {
static constexpr Location kLocations[] = {{0, 1}, {0, 0}, {0, 2}, {-1, -1}};
locations = kLocations;
break;
}
case PlanarConfig::kYUVA_4444: {
static constexpr Location kLocations[] = {{0, 0}, {0, 1}, {0, 2}, {0, 3}};
locations = kLocations;
break;
}
case PlanarConfig::kUYVA_4444: {
static constexpr Location kLocations[] = {{0, 1}, {0, 0}, {0, 2}, {0, 3}};
locations = kLocations;
break;
}
}
SkASSERT(locations);
for (int i = 0; i < SkYUVAIndex::kIndexCount; ++i) {
const auto& [plane, chanIdx] = locations[i];
SkColorChannel channel;
if (plane >= 0) {
if (!channel_index_to_channel(planeChannelFlags[plane], chanIdx, &channel)) {
return false;
}
indices[i] = {plane, channel};
} else {
SkASSERT(i == 3);
indices[i] = {-1, SkColorChannel::kR};
}
}
return true;
}
bool SkYUVAInfo::HasAlpha(PlanarConfig planarConfig) {
switch (planarConfig) {
case PlanarConfig::kY_U_V_444: return false;

View File

@ -10,7 +10,6 @@
#include "include/core/SkYUVAIndex.h"
#include "include/core/SkYUVASizeInfo.h"
#include "include/private/SkImageInfoPriv.h"
#include "src/core/SkConvertPixels.h"
#if SK_SUPPORT_GPU
#include "include/private/GrImageContext.h"
@ -97,10 +96,7 @@ SkYUVAPixmapInfo::SkYUVAPixmapInfo(const SkYUVAInfo& yuvaInfo,
bool ok = true;
for (size_t i = 0; i < static_cast<size_t>(n); ++i) {
fRowBytes[i] = rowBytes[i];
// Use kUnpremul so that we never multiply alpha when copying data in.
fPlaneInfos[i] = SkImageInfo::Make(planeDimensions[i],
colorTypes[i],
kUnpremul_SkAlphaType);
fPlaneInfos[i] = SkImageInfo::Make(planeDimensions[i], colorTypes[i], kPremul_SkAlphaType);
int numRequiredChannels = yuvaInfo.numChannelsInPlane(i);
SkASSERT(numRequiredChannels > 0);
auto [numColorTypeChannels, colorTypeDataType] = NumChannelsAndDataType(colorTypes[i]);
@ -195,27 +191,6 @@ SkYUVAPixmaps SkYUVAPixmaps::FromData(const SkYUVAPixmapInfo& yuvaPixmapInfo, sk
return SkYUVAPixmaps(yuvaPixmapInfo, std::move(data));
}
SkYUVAPixmaps SkYUVAPixmaps::MakeCopy(const SkYUVAPixmaps& src) {
if (!src.isValid()) {
return {};
}
SkYUVAPixmaps result = Allocate(src.pixmapsInfo());
int n = result.numPlanes();
for (int i = 0; i < n; ++i) {
// We use SkRectMemCpy rather than readPixels to ensure that we don't do any alpha type
// conversion.
const SkPixmap& s = src.plane(i);
const SkPixmap& d = result.plane(i);
SkRectMemcpy(d.writable_addr(),
d.rowBytes(),
s.addr(),
s.rowBytes(),
s.info().minRowBytes(),
s.height());
}
return result;
}
SkYUVAPixmaps SkYUVAPixmaps::FromExternalMemory(const SkYUVAPixmapInfo& yuvaPixmapInfo,
void* memory) {
if (!yuvaPixmapInfo.isValid()) {
@ -255,43 +230,168 @@ SkYUVAPixmaps::SkYUVAPixmaps(const SkYUVAInfo& yuvaInfo, const SkPixmap pixmaps[
std::copy_n(pixmaps, yuvaInfo.numPlanes(), fPlanes.data());
}
SkYUVAPixmapInfo SkYUVAPixmaps::pixmapsInfo() const {
if (!this->isValid()) {
return {};
}
SkColorType colorTypes[kMaxPlanes] = {};
size_t rowBytes[kMaxPlanes] = {};
int numPlanes = this->numPlanes();
for (int i = 0; i < numPlanes; ++i) {
colorTypes[i] = fPlanes[i].colorType();
rowBytes[i] = fPlanes[i].rowBytes();
}
return {fYUVAInfo, colorTypes, rowBytes};
}
bool SkYUVAPixmaps::toYUVAIndices(SkYUVAIndex yuvaIndices[SkYUVAIndex::kIndexCount]) const {
SkASSERT(yuvaIndices);
uint32_t channelFlags[] = {SkColorTypeChannelFlags(fPlanes[0].colorType()),
SkColorTypeChannelFlags(fPlanes[1].colorType()),
SkColorTypeChannelFlags(fPlanes[2].colorType()),
SkColorTypeChannelFlags(fPlanes[3].colorType())};
bool result = fYUVAInfo.toYUVAIndices(channelFlags, yuvaIndices);
SkASSERT(result == this->isValid());
return result;
}
bool SkYUVAPixmaps::toLegacy(SkYUVASizeInfo* yuvaSizeInfo, SkYUVAIndex yuvaIndices[4]) const {
if (!this->isValid()) {
return false;
}
SkYUVAIndex tempIndices[4];
if (!yuvaIndices) {
yuvaIndices = tempIndices;
bool ok = true;
auto getIthChannel = [&ok](SkColorType ct, int idx) -> SkColorChannel {
switch (SkColorTypeChannelFlags(ct)) {
case kAlpha_SkColorChannelFlag:
ok &= idx == 0;
return SkColorChannel::kA;
case kGray_SkColorChannelFlag:
case kRed_SkColorChannelFlag:
ok &= idx == 0;
return SkColorChannel::kR;
case kRG_SkColorChannelFlags:
ok &= idx < 2;
return static_cast<SkColorChannel>(idx);
case kRGB_SkColorChannelFlags:
ok &= idx < 3;
return static_cast<SkColorChannel>(idx);
case kRGBA_SkColorChannelFlags:
ok &= idx < 4;
return static_cast<SkColorChannel>(idx);
default:
ok = false;
return SkColorChannel::kR;
}
};
SkColorType cts[] = {fPlanes[0].colorType(),
fPlanes[1].colorType(),
fPlanes[2].colorType(),
fPlanes[3].colorType()};
switch (fYUVAInfo.planarConfig()) {
case SkYUVAInfo::PlanarConfig::kY_U_V_444:
case SkYUVAInfo::PlanarConfig::kY_U_V_422:
case SkYUVAInfo::PlanarConfig::kY_U_V_420:
case SkYUVAInfo::PlanarConfig::kY_U_V_440:
case SkYUVAInfo::PlanarConfig::kY_U_V_411:
case SkYUVAInfo::PlanarConfig::kY_U_V_410:
yuvaIndices[SkYUVAIndex::kY_Index].fIndex = 0;
yuvaIndices[SkYUVAIndex::kU_Index].fIndex = 1;
yuvaIndices[SkYUVAIndex::kV_Index].fIndex = 2;
yuvaIndices[SkYUVAIndex::kA_Index].fIndex = -1;
yuvaIndices[SkYUVAIndex::kY_Index].fChannel = getIthChannel(cts[0], 0);
yuvaIndices[SkYUVAIndex::kU_Index].fChannel = getIthChannel(cts[1], 0);
yuvaIndices[SkYUVAIndex::kV_Index].fChannel = getIthChannel(cts[2], 0);
yuvaIndices[SkYUVAIndex::kA_Index].fChannel = SkColorChannel::kR; // arbitrary
break;
case SkYUVAInfo::PlanarConfig::kY_V_U_420:
yuvaIndices[SkYUVAIndex::kY_Index].fIndex = 0;
yuvaIndices[SkYUVAIndex::kU_Index].fIndex = 2;
yuvaIndices[SkYUVAIndex::kV_Index].fIndex = 1;
yuvaIndices[SkYUVAIndex::kA_Index].fIndex = -1;
yuvaIndices[SkYUVAIndex::kY_Index].fChannel = getIthChannel(cts[0], 0);
yuvaIndices[SkYUVAIndex::kU_Index].fChannel = getIthChannel(cts[2], 0);
yuvaIndices[SkYUVAIndex::kV_Index].fChannel = getIthChannel(cts[1], 0);
yuvaIndices[SkYUVAIndex::kA_Index].fChannel = SkColorChannel::kR; // arbitrary
break;
case SkYUVAInfo::PlanarConfig::kY_U_V_A_4204:
yuvaIndices[SkYUVAIndex::kY_Index].fIndex = 0;
yuvaIndices[SkYUVAIndex::kU_Index].fIndex = 1;
yuvaIndices[SkYUVAIndex::kV_Index].fIndex = 2;
yuvaIndices[SkYUVAIndex::kA_Index].fIndex = 3;
yuvaIndices[SkYUVAIndex::kY_Index].fChannel = getIthChannel(cts[0], 0);
yuvaIndices[SkYUVAIndex::kU_Index].fChannel = getIthChannel(cts[1], 0);
yuvaIndices[SkYUVAIndex::kV_Index].fChannel = getIthChannel(cts[2], 0);
yuvaIndices[SkYUVAIndex::kA_Index].fChannel = getIthChannel(cts[3], 0);
break;
case SkYUVAInfo::PlanarConfig::kY_V_U_A_4204:
yuvaIndices[SkYUVAIndex::kY_Index].fIndex = 0;
yuvaIndices[SkYUVAIndex::kU_Index].fIndex = 2;
yuvaIndices[SkYUVAIndex::kV_Index].fIndex = 1;
yuvaIndices[SkYUVAIndex::kA_Index].fIndex = 3;
yuvaIndices[SkYUVAIndex::kY_Index].fChannel = getIthChannel(cts[0], 0);
yuvaIndices[SkYUVAIndex::kU_Index].fChannel = getIthChannel(cts[2], 0);
yuvaIndices[SkYUVAIndex::kV_Index].fChannel = getIthChannel(cts[1], 0);
yuvaIndices[SkYUVAIndex::kA_Index].fChannel = getIthChannel(cts[3], 0);
break;
case SkYUVAInfo::PlanarConfig::kY_UV_420:
yuvaIndices[SkYUVAIndex::kY_Index].fIndex = 0;
yuvaIndices[SkYUVAIndex::kU_Index].fIndex = 1;
yuvaIndices[SkYUVAIndex::kV_Index].fIndex = 1;
yuvaIndices[SkYUVAIndex::kA_Index].fIndex = -1;
yuvaIndices[SkYUVAIndex::kY_Index].fChannel = getIthChannel(cts[0], 0);
yuvaIndices[SkYUVAIndex::kU_Index].fChannel = getIthChannel(cts[1], 0);
yuvaIndices[SkYUVAIndex::kV_Index].fChannel = getIthChannel(cts[1], 1);
yuvaIndices[SkYUVAIndex::kA_Index].fChannel = SkColorChannel::kR; // arbitrary
break;
case SkYUVAInfo::PlanarConfig::kY_VU_420:
yuvaIndices[SkYUVAIndex::kY_Index].fIndex = 0;
yuvaIndices[SkYUVAIndex::kU_Index].fIndex = 1;
yuvaIndices[SkYUVAIndex::kV_Index].fIndex = 1;
yuvaIndices[SkYUVAIndex::kA_Index].fIndex = -1;
yuvaIndices[SkYUVAIndex::kY_Index].fChannel = getIthChannel(cts[0], 0);
yuvaIndices[SkYUVAIndex::kU_Index].fChannel = getIthChannel(cts[1], 1);
yuvaIndices[SkYUVAIndex::kV_Index].fChannel = getIthChannel(cts[1], 0);
yuvaIndices[SkYUVAIndex::kA_Index].fChannel = SkColorChannel::kR; // arbitrary
break;
case SkYUVAInfo::PlanarConfig::kY_UV_A_4204:
yuvaIndices[SkYUVAIndex::kY_Index].fIndex = 0;
yuvaIndices[SkYUVAIndex::kU_Index].fIndex = 1;
yuvaIndices[SkYUVAIndex::kV_Index].fIndex = 1;
yuvaIndices[SkYUVAIndex::kA_Index].fIndex = 2;
yuvaIndices[SkYUVAIndex::kY_Index].fChannel = getIthChannel(cts[0], 0);
yuvaIndices[SkYUVAIndex::kU_Index].fChannel = getIthChannel(cts[1], 0);
yuvaIndices[SkYUVAIndex::kV_Index].fChannel = getIthChannel(cts[1], 1);
yuvaIndices[SkYUVAIndex::kA_Index].fChannel = getIthChannel(cts[2], 0);
break;
case SkYUVAInfo::PlanarConfig::kY_VU_A_4204:
yuvaIndices[SkYUVAIndex::kY_Index].fIndex = 0;
yuvaIndices[SkYUVAIndex::kU_Index].fIndex = 1;
yuvaIndices[SkYUVAIndex::kV_Index].fIndex = 1;
yuvaIndices[SkYUVAIndex::kA_Index].fIndex = 2;
yuvaIndices[SkYUVAIndex::kY_Index].fChannel = getIthChannel(cts[0], 0);
yuvaIndices[SkYUVAIndex::kU_Index].fChannel = getIthChannel(cts[1], 1);
yuvaIndices[SkYUVAIndex::kV_Index].fChannel = getIthChannel(cts[1], 0);
yuvaIndices[SkYUVAIndex::kA_Index].fChannel = getIthChannel(cts[2], 0);
break;
case SkYUVAInfo::PlanarConfig::kYUV_444:
yuvaIndices[SkYUVAIndex::kY_Index].fIndex = 0;
yuvaIndices[SkYUVAIndex::kU_Index].fIndex = 0;
yuvaIndices[SkYUVAIndex::kV_Index].fIndex = 0;
yuvaIndices[SkYUVAIndex::kA_Index].fIndex = -1;
yuvaIndices[SkYUVAIndex::kY_Index].fChannel = getIthChannel(cts[0], 0);
yuvaIndices[SkYUVAIndex::kU_Index].fChannel = getIthChannel(cts[0], 1);
yuvaIndices[SkYUVAIndex::kV_Index].fChannel = getIthChannel(cts[0], 2);
yuvaIndices[SkYUVAIndex::kA_Index].fChannel = SkColorChannel::kR; // arbitrary
break;
case SkYUVAInfo::PlanarConfig::kUYV_444:
yuvaIndices[SkYUVAIndex::kY_Index].fIndex = 0;
yuvaIndices[SkYUVAIndex::kU_Index].fIndex = 0;
yuvaIndices[SkYUVAIndex::kV_Index].fIndex = 0;
yuvaIndices[SkYUVAIndex::kA_Index].fIndex = -1;
yuvaIndices[SkYUVAIndex::kY_Index].fChannel = getIthChannel(cts[0], 1);
yuvaIndices[SkYUVAIndex::kU_Index].fChannel = getIthChannel(cts[0], 0);
yuvaIndices[SkYUVAIndex::kV_Index].fChannel = getIthChannel(cts[0], 2);
yuvaIndices[SkYUVAIndex::kA_Index].fChannel = SkColorChannel::kR; // arbitrary
break;
case SkYUVAInfo::PlanarConfig::kYUVA_4444:
yuvaIndices[SkYUVAIndex::kY_Index].fIndex = 0;
yuvaIndices[SkYUVAIndex::kU_Index].fIndex = 0;
yuvaIndices[SkYUVAIndex::kV_Index].fIndex = 0;
yuvaIndices[SkYUVAIndex::kA_Index].fIndex = 0;
yuvaIndices[SkYUVAIndex::kY_Index].fChannel = getIthChannel(cts[0], 0);
yuvaIndices[SkYUVAIndex::kU_Index].fChannel = getIthChannel(cts[0], 1);
yuvaIndices[SkYUVAIndex::kV_Index].fChannel = getIthChannel(cts[0], 2);
yuvaIndices[SkYUVAIndex::kA_Index].fChannel = getIthChannel(cts[0], 3);
break;
case SkYUVAInfo::PlanarConfig::kUYVA_4444:
yuvaIndices[SkYUVAIndex::kY_Index].fIndex = 0;
yuvaIndices[SkYUVAIndex::kU_Index].fIndex = 0;
yuvaIndices[SkYUVAIndex::kV_Index].fIndex = 0;
yuvaIndices[SkYUVAIndex::kA_Index].fIndex = 0;
yuvaIndices[SkYUVAIndex::kY_Index].fChannel = getIthChannel(cts[0], 1);
yuvaIndices[SkYUVAIndex::kU_Index].fChannel = getIthChannel(cts[0], 0);
yuvaIndices[SkYUVAIndex::kV_Index].fChannel = getIthChannel(cts[0], 2);
yuvaIndices[SkYUVAIndex::kA_Index].fChannel = getIthChannel(cts[0], 3);
break;
}
if (!this->toYUVAIndices(yuvaIndices)) {
if (!ok) {
return false;
}
if (yuvaSizeInfo) {
yuvaSizeInfo->fOrigin = fYUVAInfo.origin();
int n = fYUVAInfo.numPlanes();

View File

@ -14,9 +14,9 @@
namespace {
struct Context {
sk_sp<sk_gpu_test::ManagedBackendTexture> fMBET;
GrGpuFinishedProc fWrappedProc = nullptr;
GrGpuFinishedContext fWrappedContext = nullptr;
sk_sp<sk_gpu_test::ManagedBackendTexture> fMBETs[SkYUVAInfo::kMaxPlanes];
};
} // anonymous namespace
@ -40,16 +40,7 @@ void* ManagedBackendTexture::releaseContext(GrGpuFinishedProc wrappedProc,
GrGpuFinishedContext wrappedCtx) const {
// Make sure we don't get a wrapped ctx without a wrapped proc
SkASSERT(!wrappedCtx || wrappedProc);
return new Context{wrappedProc, wrappedCtx, {sk_ref_sp(this)}};
}
void* ManagedBackendTexture::MakeYUVAReleaseContext(
const sk_sp<ManagedBackendTexture> mbets[SkYUVAInfo::kMaxPlanes]) {
auto context = new Context;
for (int i = 0; i < SkYUVAInfo::kMaxPlanes; ++i) {
context->fMBETs[i] = mbets[i];
}
return context;
return new Context{sk_ref_sp(this), wrappedProc, wrappedCtx};
}
sk_sp<GrRefCntedCallback> ManagedBackendTexture::refCountedCallback() const {

View File

@ -9,7 +9,6 @@
#define ManagedBackendTexture_DEFINED
#include "include/core/SkRefCnt.h"
#include "include/core/SkYUVAInfo.h"
#include "include/gpu/GrDirectContext.h"
class GrRefCntedCallback;
@ -71,14 +70,6 @@ public:
*/
void wasAdopted();
/**
* SkImage::MakeFromYUVATextures takes a single release proc that is called once for all the
* textures. This makes a single release context for the group of textures. It's used with the
* standard ReleaseProc. Like releaseContext(), it must be balanced by a ReleaseProc call for
* proper ref counting.
*/
static void* MakeYUVAReleaseContext(const sk_sp<ManagedBackendTexture>[SkYUVAInfo::kMaxPlanes]);
const GrBackendTexture& texture() { return fTexture; }
private:

View File

@ -7,154 +7,40 @@
#include "tools/gpu/YUVUtils.h"
#include "include/core/SkColorPriv.h"
#include "include/core/SkData.h"
#include "include/gpu/GrRecordingContext.h"
#include "src/codec/SkCodecImageGenerator.h"
#include "src/core/SkYUVMath.h"
#include "src/gpu/GrDirectContextPriv.h"
#include "src/gpu/GrRecordingContextPriv.h"
#include "tools/gpu/ManagedBackendTexture.h"
namespace {
static SkPMColor convert_yuva_to_rgba(const float mtx[20], uint8_t yuva[4]) {
uint8_t y = yuva[0];
uint8_t u = yuva[1];
uint8_t v = yuva[2];
uint8_t a = yuva[3];
uint8_t r = SkTPin(SkScalarRoundToInt(mtx[ 0]*y + mtx[ 1]*u + mtx[ 2]*v + mtx[ 4]*255), 0, 255);
uint8_t g = SkTPin(SkScalarRoundToInt(mtx[ 5]*y + mtx[ 6]*u + mtx[ 7]*v + mtx[ 9]*255), 0, 255);
uint8_t b = SkTPin(SkScalarRoundToInt(mtx[10]*y + mtx[11]*u + mtx[12]*v + mtx[14]*255), 0, 255);
return SkPremultiplyARGBInline(a, r, g, b);
}
static uint8_t look_up(float x1, float y1, const SkPixmap& pmap, SkColorChannel channel) {
SkASSERT(x1 > 0 && x1 < 1.0f);
SkASSERT(y1 > 0 && y1 < 1.0f);
int x = SkScalarFloorToInt(x1 * pmap.width());
int y = SkScalarFloorToInt(y1 * pmap.height());
auto ii = pmap.info().makeColorType(kRGBA_8888_SkColorType).makeWH(1, 1);
uint32_t pixel;
SkAssertResult(pmap.readPixels(ii, &pixel, sizeof(pixel), x, y));
int shift = static_cast<int>(channel) * 8;
return static_cast<uint8_t>((pixel >> shift) & 0xff);
}
class Generator : public SkImageGenerator {
public:
Generator(SkYUVAPixmaps pixmaps, sk_sp<SkColorSpace> cs)
: SkImageGenerator(SkImageInfo::Make(pixmaps.yuvaInfo().dimensions(),
kN32_SkColorType,
kPremul_SkAlphaType,
std::move(cs)))
, fPixmaps(std::move(pixmaps)) {}
protected:
bool onGetPixels(const SkImageInfo& info,
void* pixels,
size_t rowBytes,
const Options&) override {
if (kUnknown_SkColorType == fFlattened.colorType()) {
fFlattened.allocPixels(info);
SkASSERT(info == this->getInfo());
float mtx[20];
SkColorMatrix_YUV2RGB(fPixmaps.yuvaInfo().yuvColorSpace(), mtx);
SkYUVAIndex yuvaIndices[SkYUVAIndex::kIndexCount];
SkAssertResult(fPixmaps.toYUVAIndices(yuvaIndices));
for (int y = 0; y < info.height(); ++y) {
for (int x = 0; x < info.width(); ++x) {
float x1 = (x + 0.5f) / info.width();
float y1 = (y + 0.5f) / info.height();
uint8_t yuva[4] = {0, 0, 0, 255};
for (auto c : {SkYUVAIndex::kY_Index,
SkYUVAIndex::kU_Index,
SkYUVAIndex::kV_Index}) {
const auto& pmap = fPixmaps.plane(yuvaIndices[c].fIndex);
yuva[c] = look_up(x1, y1, pmap, yuvaIndices[c].fChannel);
}
if (yuvaIndices[SkYUVAIndex::kA_Index].fIndex >= 0) {
const auto& pmap =
fPixmaps.plane(yuvaIndices[SkYUVAIndex::kA_Index].fIndex);
yuva[3] =
look_up(x1, y1, pmap, yuvaIndices[SkYUVAIndex::kA_Index].fChannel);
}
// Making premul here.
*fFlattened.getAddr32(x, y) = convert_yuva_to_rgba(mtx, yuva);
}
}
}
return fFlattened.readPixels(info, pixels, rowBytes, 0, 0);
}
bool onQueryYUVAInfo(const SkYUVAPixmapInfo::SupportedDataTypes& types,
SkYUVAPixmapInfo* info) const override {
*info = fPixmaps.pixmapsInfo();
return info->isValid();
}
bool onGetYUVAPlanes(const SkYUVAPixmaps& pixmaps) override {
SkASSERT(pixmaps.yuvaInfo() == fPixmaps.yuvaInfo());
for (int i = 0; i < pixmaps.numPlanes(); ++i) {
SkASSERT(fPixmaps.plane(i).colorType() == pixmaps.plane(i).colorType());
SkASSERT(fPixmaps.plane(i).dimensions() == pixmaps.plane(i).dimensions());
SkASSERT(fPixmaps.plane(i).rowBytes() == pixmaps.plane(i).rowBytes());
fPixmaps.plane(i).readPixels(pixmaps.plane(i));
}
return true;
}
private:
SkYUVAPixmaps fPixmaps;
SkBitmap fFlattened;
};
} // anonymous namespace
namespace sk_gpu_test {
std::unique_ptr<LazyYUVImage> LazyYUVImage::Make(sk_sp<SkData> data,
GrMipmapped mipmapped,
sk_sp<SkColorSpace> cs) {
std::unique_ptr<LazyYUVImage> LazyYUVImage::Make(sk_sp<SkData> data, GrMipmapped mipmapped) {
std::unique_ptr<LazyYUVImage> image(new LazyYUVImage());
if (image->reset(std::move(data), mipmapped, std::move(cs))) {
if (image->reset(std::move(data), mipmapped)) {
return image;
} else {
return nullptr;
}
}
std::unique_ptr<LazyYUVImage> LazyYUVImage::Make(SkYUVAPixmaps pixmaps,
GrMipmapped mipmapped,
sk_sp<SkColorSpace> cs) {
std::unique_ptr<LazyYUVImage> image(new LazyYUVImage());
if (image->reset(std::move(pixmaps), mipmapped, std::move(cs))) {
return image;
sk_sp<SkImage> LazyYUVImage::refImage(GrRecordingContext* rContext) {
if (this->ensureYUVImage(rContext)) {
return fYUVImage;
} else {
return nullptr;
}
}
sk_sp<SkImage> LazyYUVImage::refImage(GrRecordingContext* rContext, Type type) {
if (this->ensureYUVImage(rContext, type)) {
size_t idx = static_cast<size_t>(type);
SkASSERT(idx >= 0 && idx < SK_ARRAY_COUNT(fYUVImage));
return fYUVImage[idx];
const SkImage* LazyYUVImage::getImage(GrRecordingContext* rContext) {
if (this->ensureYUVImage(rContext)) {
return fYUVImage.get();
} else {
return nullptr;
}
}
bool LazyYUVImage::reset(sk_sp<SkData> data, GrMipmapped mipmapped, sk_sp<SkColorSpace> cs) {
bool LazyYUVImage::reset(sk_sp<SkData> data, GrMipmapped mipmapped) {
fMipmapped = mipmapped;
auto codec = SkCodecImageGenerator::MakeFromEncodedCodec(data);
if (!codec) {
@ -174,87 +60,78 @@ bool LazyYUVImage::reset(sk_sp<SkData> data, GrMipmapped mipmapped, sk_sp<SkColo
return false;
}
fColorSpace = std::move(cs);
// The SkPixmap data is fully configured now for MakeFromYUVAPixmaps once we get a GrContext
return true;
}
bool LazyYUVImage::reset(SkYUVAPixmaps pixmaps, GrMipmapped mipmapped, sk_sp<SkColorSpace> cs) {
if (!pixmaps.isValid()) {
if (!fPixmaps.toLegacy(&fSizeInfo, fComponents)) {
return false;
}
fMipmapped = mipmapped;
if (pixmaps.ownsStorage()) {
fPixmaps = std::move(pixmaps);
} else {
fPixmaps = SkYUVAPixmaps::MakeCopy(std::move(pixmaps));
}
fColorSpace = std::move(cs);
// The SkPixmap data is fully configured now for MakeFromYUVAPixmaps once we get a GrContext
return true;
}
bool LazyYUVImage::ensureYUVImage(GrRecordingContext* rContext, Type type) {
size_t idx = static_cast<size_t>(type);
SkASSERT(idx >= 0 && idx < SK_ARRAY_COUNT(fYUVImage));
if (fYUVImage[idx] && fYUVImage[idx]->isValid(rContext)) {
return true; // Have already made a YUV image valid for this context.
bool LazyYUVImage::ensureYUVImage(GrRecordingContext* rContext) {
if (!rContext) {
return false; // Cannot make a YUV image from planes
}
if (fYUVImage && fYUVImage->isValid(rContext)) {
return true; // Have already made a YUV image valid for this context.
}
// Try to make a new YUV image for this context.
switch (type) {
case Type::kFromPixmaps:
if (!rContext || rContext->abandoned()) {
return false;
}
fYUVImage[idx] = SkImage::MakeFromYUVAPixmaps(rContext,
fPixmaps,
fMipmapped,
/*limit to max tex size*/ false,
fColorSpace);
break;
case Type::kFromGenerator: {
// Make sure the generator has ownership of its backing planes.
auto generator = std::make_unique<Generator>(fPixmaps, fColorSpace);
fYUVImage[idx] = SkImage::MakeFromGenerator(std::move(generator));
break;
}
case Type::kFromTextures:
if (!rContext || rContext->abandoned()) {
return false;
}
if (auto direct = rContext->asDirectContext()) {
sk_sp<sk_gpu_test::ManagedBackendTexture> mbets[SkYUVAInfo::kMaxPlanes];
GrBackendTexture textures[SkYUVAInfo::kMaxPlanes];
uint32_t componentFlags[SkYUVAInfo::kMaxPlanes] = {};
for (int i = 0; i < fPixmaps.numPlanes(); ++i) {
mbets[i] = sk_gpu_test::ManagedBackendTexture::MakeWithData(
direct, fPixmaps.plane(i), GrRenderable::kNo, GrProtected::kNo);
if (mbets[i]) {
textures[i] = mbets[i]->texture();
componentFlags[i] = textures[i].getBackendFormat().channelMask();
} else {
return false;
}
}
SkYUVAIndex indices[SkYUVAIndex::kIndexCount];
if (!fPixmaps.yuvaInfo().toYUVAIndices(componentFlags, indices)) {
return false;
}
void* relContext =
sk_gpu_test::ManagedBackendTexture::MakeYUVAReleaseContext(mbets);
fYUVImage[idx] = SkImage::MakeFromYUVATextures(
direct,
fPixmaps.yuvaInfo().yuvColorSpace(),
textures,
indices,
fPixmaps.yuvaInfo().dimensions(),
kTopLeft_GrSurfaceOrigin,
fColorSpace,
sk_gpu_test::ManagedBackendTexture::ReleaseProc,
relContext);
}
}
return fYUVImage[idx] != nullptr;
fYUVImage = SkImage::MakeFromYUVAPixmaps(rContext, fPixmaps, fMipmapped, false, nullptr);
return fYUVImage != nullptr;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
void YUVABackendReleaseContext::Unwind(GrDirectContext* dContext,
YUVABackendReleaseContext* beContext,
bool fullFlush) {
// Some backends (e.g., Vulkan) require that all work associated w/ texture
// creation be completed before deleting the textures.
if (fullFlush) {
// If the release context client performed some operations other than backend texture
// creation then we may require a full flush to ensure that all the work is completed.
dContext->flush();
dContext->submit(true);
} else {
dContext->submit();
while (!beContext->creationCompleted()) {
dContext->checkAsyncWorkCompletion();
}
}
delete beContext;
}
YUVABackendReleaseContext::YUVABackendReleaseContext(GrDirectContext* dContext)
: fDContext(dContext) {
}
YUVABackendReleaseContext::~YUVABackendReleaseContext() {
for (int i = 0; i < 4; ++i) {
if (fBETextures[i].isValid()) {
SkASSERT(fCreationComplete[i]);
fDContext->deleteBackendTexture(fBETextures[i]);
}
}
}
template<int I> static void CreationComplete(void* releaseContext) {
auto beContext = reinterpret_cast<YUVABackendReleaseContext*>(releaseContext);
beContext->setCreationComplete(I);
}
GrGpuFinishedProc YUVABackendReleaseContext::CreationCompleteProc(int index) {
SkASSERT(index >= 0 && index < 4);
switch (index) {
case 0: return CreationComplete<0>;
case 1: return CreationComplete<1>;
case 2: return CreationComplete<2>;
case 3: return CreationComplete<3>;
}
SK_ABORT("Invalid YUVA Index.");
return nullptr;
}
} // namespace sk_gpu_test

View File

@ -25,43 +25,93 @@ namespace sk_gpu_test {
// the image if the context has changed, as in Viewer)
class LazyYUVImage {
public:
// Returns null if the data could not be extracted into YUVA planes
static std::unique_ptr<LazyYUVImage> Make(sk_sp<SkData> data,
GrMipmapped = GrMipmapped::kNo,
sk_sp<SkColorSpace> = nullptr);
static std::unique_ptr<LazyYUVImage> Make(SkYUVAPixmaps,
GrMipmapped = GrMipmapped::kNo,
sk_sp<SkColorSpace> = nullptr);
// Returns null if the data could not be extracted into YUVA8 planes
static std::unique_ptr<LazyYUVImage> Make(sk_sp<SkData> data, GrMipmapped = GrMipmapped::kNo);
enum class Type {
kFromPixmaps,
kFromGenerator,
kFromTextures,
};
sk_sp<SkImage> refImage(GrRecordingContext* rContext);
SkISize dimensions() const { return fPixmaps.yuvaInfo().dimensions(); }
sk_sp<SkImage> refImage(GrRecordingContext* rContext, Type);
const SkImage* getImage(GrRecordingContext* rContext);
private:
// Decoded YUV data
SkYUVAPixmaps fPixmaps;
// Legacy representation used to import to SkImage.
SkYUVASizeInfo fSizeInfo;
SkYUVAIndex fComponents[SkYUVAIndex::kIndexCount];
GrMipmapped fMipmapped;
sk_sp<SkColorSpace> fColorSpace;
// Memoized SkImages formed with planes, one for each Type.
sk_sp<SkImage> fYUVImage[3];
// Memoized SkImage formed with planes
sk_sp<SkImage> fYUVImage;
LazyYUVImage() = default;
bool reset(sk_sp<SkData> data, GrMipmapped, sk_sp<SkColorSpace>);
bool reset(SkYUVAPixmaps pixmaps, GrMipmapped, sk_sp<SkColorSpace>);
bool reset(sk_sp<SkData> data, GrMipmapped);
bool ensureYUVImage(GrRecordingContext* rContext, Type type);
bool ensureYUVImage(GrRecordingContext* rContext);
};
// A helper for managing the lifetime of backend textures for YUVA images.
class YUVABackendReleaseContext {
public:
static GrGpuFinishedProc CreationCompleteProc(int index);
// A stock 'TextureReleaseProc' to use with this class
static void Release(void* releaseContext) {
auto beContext = reinterpret_cast<YUVABackendReleaseContext*>(releaseContext);
delete beContext;
}
// Given how and when backend textures are created, just deleting this object often
// isn't enough. This helper encapsulates the extra work needed.
static void Unwind(GrDirectContext*, YUVABackendReleaseContext* beContext, bool fullFlush);
YUVABackendReleaseContext(GrDirectContext*);
~YUVABackendReleaseContext();
void set(int index, const GrBackendTexture& beTex) {
SkASSERT(index >= 0 && index < 4);
SkASSERT(!fBETextures[index].isValid());
SkASSERT(beTex.isValid());
fBETextures[index] = beTex;
}
void setCreationComplete(int index) {
SkASSERT(index >= 0 && index < 4);
// In GL, the finished proc can fire before the backend texture is returned to the client
// SkASSERT(fBETextures[index].isValid());
fCreationComplete[index] = true;
}
bool creationCompleted() const {
for (int i = 0; i < 4; ++i) {
if (fBETextures[i].isValid() && !fCreationComplete[i]) {
return false;
}
}
return true;
}
const GrBackendTexture* beTextures() const { return fBETextures; }
const GrBackendTexture& beTexture(int index) {
SkASSERT(index >= 0 && index < 4);
SkASSERT(fBETextures[index].isValid());
return fBETextures[index];
}
private:
GrDirectContext* fDContext;
GrBackendTexture fBETextures[4];
bool fCreationComplete[4] = { false };
};
} // namespace sk_gpu_test
#endif // YUVUtils_DEFINED