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:
parent
db0288d747
commit
839fb228ac
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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); }
|
||||
|
||||
|
@ -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>);
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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 {
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user