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>
diff --git a/gm/asyncrescaleandread.cpp b/gm/asyncrescaleandread.cpp
index d6f151b..b4d55c9 100644
--- a/gm/asyncrescaleandread.cpp
+++ b/gm/asyncrescaleandread.cpp
@@ -11,14 +11,15 @@
#include "include/core/SkPaint.h"
#include "include/core/SkRect.h"
#include "include/core/SkSurface.h"
-#include "include/core/SkYUVAIndex.h"
+#include "include/core/SkYUVAInfo.h"
+#include "include/core/SkYUVAPixmaps.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 {
@@ -93,33 +94,17 @@
if (!asyncContext.fResult) {
return nullptr;
}
- 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}
+ 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)}
};
-
- *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());
+ 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);
}
// Draws a grid of rescales. The columns are none, low, and high filter quality. The rows are
diff --git a/gm/compositor_quads.cpp b/gm/compositor_quads.cpp
index 934afba..f3a684d 100644
--- a/gm/compositor_quads.cpp
+++ b/gm/compositor_quads.cpp
@@ -897,7 +897,8 @@
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());
+ fImage = fYUVData->refImage(canvas->recordingContext(),
+ sk_gpu_test::LazyYUVImage::Type::kFromPixmaps);
if (!fImage) {
return 0;
}
diff --git a/gm/ducky_yuv_blend.cpp b/gm/ducky_yuv_blend.cpp
index af45b89..bd39353 100644
--- a/gm/ducky_yuv_blend.cpp
+++ b/gm/ducky_yuv_blend.cpp
@@ -33,7 +33,7 @@
auto lazyYUV = sk_gpu_test::LazyYUVImage::Make(GetResourceAsData("images/ducky.jpg"),
GrMipmapped::kYes);
if (lazyYUV) {
- duckyFG[1] = lazyYUV->refImage(rContext);
+ duckyFG[1] = lazyYUV->refImage(rContext, sk_gpu_test::LazyYUVImage::Type::kFromPixmaps);
}
if (!duckyFG[1]) {
return skiagm::DrawResult::kFail;
diff --git a/gm/imagefromyuvtextures.cpp b/gm/imagefromyuvtextures.cpp
index ede9586..2a50973 100644
--- a/gm/imagefromyuvtextures.cpp
+++ b/gm/imagefromyuvtextures.cpp
@@ -32,8 +32,6 @@
#include "tools/Resources.h"
#include "tools/gpu/YUVUtils.h"
-using sk_gpu_test::YUVABackendReleaseContext;
-
class GrRenderTargetContext;
namespace skiagm {
@@ -50,128 +48,90 @@
SkISize onISize() override { return {1420, 610}; }
- static SkBitmap CreateBmpAndPlanes(const char* name, SkBitmap yuvaBmps[4]) {
+ static std::unique_ptr<sk_gpu_test::LazyYUVImage> CreatePlanes(const char* name) {
SkBitmap bmp;
if (!GetResourceAsBitmap(name, &bmp)) {
return {};
}
- 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);
+ 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);
unsigned char* yuvPixels[] = {
- 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()),
+ 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()),
};
float m[20];
- SkColorMatrix_RGB2YUV(kJPEG_SkYUVColorSpace, m);
+ SkColorMatrix_RGB2YUV(pixmaps.yuvaInfo().yuvColorSpace(), 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 < yaInfo.height(); ++j) {
- for (int i = 0; i < yaInfo.width(); ++i) {
- auto rgba = *rgbaBmp.getAddr32(i, j);
+ 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);
auto r = (rgba & 0x000000ff) >> 0;
auto g = (rgba & 0x0000ff00) >> 8;
auto b = (rgba & 0x00ff0000) >> 16;
auto a = (rgba & 0xff000000) >> 24;
- yuvPixels[0][j*yaInfo.width() + i] = SkToU8(
+ yuvPixels[0][j*pixmaps.planes()[0].width() + i] = SkToU8(
sk_float_round2int(m[0]*r + m[1]*g + m[2]*b + m[3]*a + 255*m[4]));
- yuvPixels[3][j*yaInfo.width() + i] = SkToU8(sk_float_round2int(
+ yuvPixels[3][j*pixmaps.planes()[0].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 < uvInfo.height(); ++j) {
- for (int i = 0; i < uvInfo.width(); ++i) {
+ for (int j = 0; j < pixmaps.planes()[1].height(); ++j) {
+ for (int i = 0; i < pixmaps.planes()[1].width(); ++i) {
// Average together 4 pixels of RGB.
int rgba[] = {0, 0, 0, 0};
- for (int y = 0; y < 2; ++y) {
- for (int x = 0; x < 2; ++x) {
- auto src = *rgbaBmp.getAddr32(2 * i + x, 2 * j + y);
+ 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);
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] /= 4;
+ rgba[c] /= denom;
}
- int uvIndex = j*uvInfo.width() + i;
+ int uvIndex = j*pixmaps.planes()[1].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 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;
+ return sk_gpu_test::LazyYUVImage::Make(std::move(pixmaps));
}
sk_sp<SkImage> makeYUVAImage(GrDirectContext* context) {
- auto releaseContext = new YUVABackendReleaseContext(context);
- SkYUVAIndex indices[4];
-
- if (!CreateYUVBackendTextures(context, fYUVABmps, indices, releaseContext)) {
- YUVABackendReleaseContext::Unwind(context, releaseContext, false);
- return nullptr;
- }
-
- return SkImage::MakeFromYUVATextures(context,
- kJPEG_SkYUVColorSpace,
- releaseContext->beTextures(),
- indices,
- fRGBABmp.dimensions(),
- kTopLeft_GrSurfaceOrigin,
- nullptr,
- YUVABackendReleaseContext::Release,
- releaseContext);
+ return fLazyYUVImage->refImage(context, sk_gpu_test::LazyYUVImage::Type::kFromTextures);
}
sk_sp<SkImage> createReferenceImage(GrDirectContext* dContext) {
- auto resultInfo = SkImageInfo::Make(fRGBABmp.dimensions(),
+ auto planarImage = this->makeYUVAImage(dContext);
+ if (!planarImage) {
+ return nullptr;
+ }
+
+ auto resultInfo = SkImageInfo::Make(fLazyYUVImage->dimensions(),
kRGBA_8888_SkColorType,
kPremul_SkAlphaType);
auto resultSurface = SkSurface::MakeRenderTarget(dContext,
@@ -184,27 +144,7 @@
return nullptr;
}
- 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);
+ resultSurface->getCanvas()->drawImage(std::move(planarImage), 0, 0);
return resultSurface->makeImageSnapshot();
}
@@ -213,7 +153,9 @@
return DrawResult::kSkip;
}
- fRGBABmp = CreateBmpAndPlanes("images/mandrill_32.png", fYUVABmps);
+ if (!fLazyYUVImage) {
+ fLazyYUVImage = CreatePlanes("images/mandrill_32.png");
+ }
// 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.
@@ -320,8 +262,7 @@
}
private:
- SkBitmap fRGBABmp; // TODO: oddly, it looks like this could just be an SkISize
- SkBitmap fYUVABmps[4];
+ std::unique_ptr<sk_gpu_test::LazyYUVImage> fLazyYUVImage;
// 3 draws x 3 scales x 4 filter qualities
static constexpr int kNumImages = 3 * 3 * 4;
diff --git a/gm/wacky_yuv_formats.cpp b/gm/wacky_yuv_formats.cpp
index 559bdc8..c198715 100644
--- a/gm/wacky_yuv_formats.cpp
+++ b/gm/wacky_yuv_formats.cpp
@@ -57,7 +57,6 @@
#include <memory>
#include <utility>
-using sk_gpu_test::YUVABackendReleaseContext;
class GrRenderTargetContext;
static const int kTileWidthHeight = 128;
@@ -116,77 +115,51 @@
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;
@@ -195,129 +168,20 @@
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 = this->getYUVAInfo(dimensions, yuvColorSpace);
+ SkYUVAInfo info(dimensions, fPlanarConfig, yuvColorSpace);
SkPixmap pmaps[SkYUVAInfo::kMaxPlanes];
int n = info.numPlanes();
if (numBitmaps < n) {
@@ -329,38 +193,6 @@
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;
@@ -527,19 +359,6 @@
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
@@ -829,118 +648,6 @@
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",
@@ -988,13 +695,6 @@
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,
@@ -1026,18 +726,10 @@
// YV12
class WackyYUVFormatsGM : public GM {
public:
- // 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.
- };
+ using Type = sk_gpu_test::LazyYUVImage::Type;
- WackyYUVFormatsGM(bool useTargetColorSpace, bool useSubset, ImageType imageType)
- : fUseTargetColorSpace(useTargetColorSpace)
- , fUseSubset(useSubset)
- , fImageType(imageType) {
+ WackyYUVFormatsGM(bool useTargetColorSpace, bool useSubset, Type type)
+ : fUseTargetColorSpace(useTargetColorSpace), fUseSubset(useSubset), fImageType(type) {
this->setBGColor(0xFFCCCCCC);
}
@@ -1051,12 +743,12 @@
name += "_domain";
}
switch (fImageType) {
- case ImageType::kYUVAPixmaps:
+ case Type::kFromPixmaps:
name += "_frompixmaps";
break;
- case ImageType::kYUVATextures:
+ case Type::kFromTextures:
break;
- case ImageType::kGenerator:
+ case Type::kFromGenerator:
name += "_imggen";
break;
}
@@ -1108,65 +800,14 @@
int numPlanes = create_YUV(planes, format, resultBMs, opaque);
const YUVAPlanarConfig planarConfig(format, opaque);
- SkASSERT(numPlanes == planarConfig.numPlanes());
+ SkYUVAPixmaps pixmaps =
+ planarConfig.makeYUVAPixmaps(fOriginalBMs[opaque].dimensions(),
+ static_cast<SkYUVColorSpace>(cs),
+ resultBMs,
+ numPlanes);
+ auto lazyYUV = sk_gpu_test::LazyYUVImage::Make(std::move(pixmaps));
- 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);
- }
+ fImages[opaque][cs][format] = lazyYUV->refImage(dContext, fImageType);
}
}
}
@@ -1192,7 +833,7 @@
}
// Only the generator is expected to work with the CPU backend.
- if (fImageType != ImageType::kGenerator && !dContext) {
+ if (fImageType != Type::kFromGenerator && !dContext) {
return DrawResult::kSkip;
}
@@ -1278,7 +919,7 @@
sk_sp<SkImage> fImages[2][kLastEnum_SkYUVColorSpace + 1][kLast_YUVFormat + 1];
bool fUseTargetColorSpace;
bool fUseSubset;
- ImageType fImageType;
+ Type fImageType;
sk_sp<SkColorSpace> fTargetColorSpace;
using INHERITED = GM;
@@ -1288,19 +929,19 @@
DEF_GM(return new WackyYUVFormatsGM(/* target cs */ false,
/* subset */ false,
- WackyYUVFormatsGM::ImageType::kYUVATextures);)
+ WackyYUVFormatsGM::Type::kFromTextures);)
DEF_GM(return new WackyYUVFormatsGM(/* target cs */ false,
/* subset */ true,
- WackyYUVFormatsGM::ImageType::kYUVATextures);)
+ WackyYUVFormatsGM::Type::kFromTextures);)
DEF_GM(return new WackyYUVFormatsGM(/* target cs */ true,
/* subset */ false,
- WackyYUVFormatsGM::ImageType::kYUVATextures);)
+ WackyYUVFormatsGM::Type::kFromTextures);)
DEF_GM(return new WackyYUVFormatsGM(/* target cs */ false,
/* subset */ false,
- WackyYUVFormatsGM::ImageType::kGenerator);)
+ WackyYUVFormatsGM::Type::kFromGenerator);)
DEF_GM(return new WackyYUVFormatsGM(/* target cs */ false,
/* subset */ false,
- WackyYUVFormatsGM::ImageType::kYUVAPixmaps);)
+ WackyYUVFormatsGM::Type::kFromPixmaps);)
class YUVMakeColorSpaceGM : public GpuGM {
public:
@@ -1352,52 +993,21 @@
create_YUV(planes, kAYUV_YUVFormat, resultBMs, opaque);
YUVAPlanarConfig planarConfig(kAYUV_YUVFormat, opaque);
- int numPlanes = planarConfig.numPlanes();
- auto releaseContext = new YUVABackendReleaseContext(context);
- auto srgbReleaseContext = new YUVABackendReleaseContext(context);
+ auto yuvaPixmaps = planarConfig.makeYUVAPixmaps(fOriginalBMs[opaque].dimensions(),
+ kJPEG_Full_SkYUVColorSpace,
+ resultBMs,
+ SK_ARRAY_COUNT(resultBMs));
- 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);
+ 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);
}
-
- 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
diff --git a/gm/yuv420_odd_dim.cpp b/gm/yuv420_odd_dim.cpp
index 91647cf..0277208 100644
--- a/gm/yuv420_odd_dim.cpp
+++ b/gm/yuv420_odd_dim.cpp
@@ -49,7 +49,7 @@
if (!imageHelper) {
return nullptr;
}
- return imageHelper->refImage(rContext);
+ return imageHelper->refImage(rContext, sk_gpu_test::LazyYUVImage::Type::kFromPixmaps);
}
// This GM tests that the YUVA image code path in the GPU backend handles odd sized images with
diff --git a/include/core/SkYUVAInfo.h b/include/core/SkYUVAInfo.h
index d2659c4..da94ef7 100644
--- a/include/core/SkYUVAInfo.h
+++ b/include/core/SkYUVAInfo.h
@@ -11,6 +11,7 @@
#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
@@ -97,6 +98,15 @@
*/
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);
@@ -155,6 +165,15 @@
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); }
diff --git a/include/core/SkYUVAPixmaps.h b/include/core/SkYUVAPixmaps.h
index 4a6601e..69e1085 100644
--- a/include/core/SkYUVAPixmaps.h
+++ b/include/core/SkYUVAPixmaps.h
@@ -183,6 +183,12 @@
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
* SkYUVAPixmapInfo::computeTotalBytes() allocated starting at memory.
@@ -211,6 +217,8 @@
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; }
@@ -227,9 +235,18 @@
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[4]) const;
+ bool toLegacy(SkYUVASizeInfo*, SkYUVAIndex[SkYUVAIndex::kIndexCount]) const;
private:
SkYUVAPixmaps(const SkYUVAPixmapInfo&, sk_sp<SkData>);
diff --git a/src/core/SkYUVAInfo.cpp b/src/core/SkYUVAInfo.cpp
index a43b477..5c46e0f 100644
--- a/src/core/SkYUVAInfo.cpp
+++ b/src/core/SkYUVAInfo.cpp
@@ -79,6 +79,135 @@
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;
diff --git a/src/core/SkYUVAPixmaps.cpp b/src/core/SkYUVAPixmaps.cpp
index d7bdef9..e840fc5 100644
--- a/src/core/SkYUVAPixmaps.cpp
+++ b/src/core/SkYUVAPixmaps.cpp
@@ -10,6 +10,7 @@
#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"
@@ -96,7 +97,10 @@
bool ok = true;
for (size_t i = 0; i < static_cast<size_t>(n); ++i) {
fRowBytes[i] = rowBytes[i];
- fPlaneInfos[i] = SkImageInfo::Make(planeDimensions[i], colorTypes[i], kPremul_SkAlphaType);
+ // Use kUnpremul so that we never multiply alpha when copying data in.
+ fPlaneInfos[i] = SkImageInfo::Make(planeDimensions[i],
+ colorTypes[i],
+ kUnpremul_SkAlphaType);
int numRequiredChannels = yuvaInfo.numChannelsInPlane(i);
SkASSERT(numRequiredChannels > 0);
auto [numColorTypeChannels, colorTypeDataType] = NumChannelsAndDataType(colorTypes[i]);
@@ -191,6 +195,27 @@
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()) {
@@ -230,168 +255,43 @@
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;
}
- 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;
+ SkYUVAIndex tempIndices[4];
+ if (!yuvaIndices) {
+ yuvaIndices = tempIndices;
}
- if (!ok) {
+ if (!this->toYUVAIndices(yuvaIndices)) {
return false;
}
+
if (yuvaSizeInfo) {
yuvaSizeInfo->fOrigin = fYUVAInfo.origin();
int n = fYUVAInfo.numPlanes();
diff --git a/tools/gpu/ManagedBackendTexture.cpp b/tools/gpu/ManagedBackendTexture.cpp
index 192a57c..a1a7449 100644
--- a/tools/gpu/ManagedBackendTexture.cpp
+++ b/tools/gpu/ManagedBackendTexture.cpp
@@ -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,7 +40,16 @@
GrGpuFinishedContext wrappedCtx) const {
// Make sure we don't get a wrapped ctx without a wrapped proc
SkASSERT(!wrappedCtx || wrappedProc);
- return new Context{sk_ref_sp(this), wrappedProc, wrappedCtx};
+ 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;
}
sk_sp<GrRefCntedCallback> ManagedBackendTexture::refCountedCallback() const {
diff --git a/tools/gpu/ManagedBackendTexture.h b/tools/gpu/ManagedBackendTexture.h
index 931e71b..7c65d54 100644
--- a/tools/gpu/ManagedBackendTexture.h
+++ b/tools/gpu/ManagedBackendTexture.h
@@ -9,6 +9,7 @@
#define ManagedBackendTexture_DEFINED
#include "include/core/SkRefCnt.h"
+#include "include/core/SkYUVAInfo.h"
#include "include/gpu/GrDirectContext.h"
class GrRefCntedCallback;
@@ -70,6 +71,14 @@
*/
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:
diff --git a/tools/gpu/YUVUtils.cpp b/tools/gpu/YUVUtils.cpp
index 4e8ad24..93c011b 100644
--- a/tools/gpu/YUVUtils.cpp
+++ b/tools/gpu/YUVUtils.cpp
@@ -7,40 +7,154 @@
#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) {
+std::unique_ptr<LazyYUVImage> LazyYUVImage::Make(sk_sp<SkData> data,
+ GrMipmapped mipmapped,
+ sk_sp<SkColorSpace> cs) {
std::unique_ptr<LazyYUVImage> image(new LazyYUVImage());
- if (image->reset(std::move(data), mipmapped)) {
+ if (image->reset(std::move(data), mipmapped, std::move(cs))) {
return image;
} else {
return nullptr;
}
}
-sk_sp<SkImage> LazyYUVImage::refImage(GrRecordingContext* rContext) {
- if (this->ensureYUVImage(rContext)) {
- return fYUVImage;
+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;
} else {
return nullptr;
}
}
-const SkImage* LazyYUVImage::getImage(GrRecordingContext* rContext) {
- if (this->ensureYUVImage(rContext)) {
- return fYUVImage.get();
+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];
} else {
return nullptr;
}
}
-bool LazyYUVImage::reset(sk_sp<SkData> data, GrMipmapped mipmapped) {
+bool LazyYUVImage::reset(sk_sp<SkData> data, GrMipmapped mipmapped, sk_sp<SkColorSpace> cs) {
fMipmapped = mipmapped;
auto codec = SkCodecImageGenerator::MakeFromEncodedCodec(data);
if (!codec) {
@@ -60,78 +174,87 @@
return false;
}
- if (!fPixmaps.toLegacy(&fSizeInfo, fComponents)) {
- return false;
- }
+ 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) {
- if (!rContext) {
- return false; // Cannot make a YUV image from planes
+bool LazyYUVImage::reset(SkYUVAPixmaps pixmaps, GrMipmapped mipmapped, sk_sp<SkColorSpace> cs) {
+ if (!pixmaps.isValid()) {
+ return false;
}
- if (fYUVImage && fYUVImage->isValid(rContext)) {
- return true; // Have already made a YUV image valid for this context.
+ 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.
}
// Try to make a new YUV image for this context.
- 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();
+ 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);
+ }
}
-
- delete beContext;
+ return fYUVImage[idx] != nullptr;
}
-
-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
diff --git a/tools/gpu/YUVUtils.h b/tools/gpu/YUVUtils.h
index 8363032..e9dad4f 100644
--- a/tools/gpu/YUVUtils.h
+++ b/tools/gpu/YUVUtils.h
@@ -25,93 +25,43 @@
// the image if the context has changed, as in Viewer)
class LazyYUVImage {
public:
- // 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);
+ // 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);
- sk_sp<SkImage> refImage(GrRecordingContext* rContext);
+ enum class Type {
+ kFromPixmaps,
+ kFromGenerator,
+ kFromTextures,
+ };
- const SkImage* getImage(GrRecordingContext* rContext);
+ SkISize dimensions() const { return fPixmaps.yuvaInfo().dimensions(); }
+
+ sk_sp<SkImage> refImage(GrRecordingContext* rContext, Type);
private:
// Decoded YUV data
SkYUVAPixmaps fPixmaps;
- // Legacy representation used to import to SkImage.
- SkYUVASizeInfo fSizeInfo;
- SkYUVAIndex fComponents[SkYUVAIndex::kIndexCount];
-
GrMipmapped fMipmapped;
- // Memoized SkImage formed with planes
- sk_sp<SkImage> fYUVImage;
+ sk_sp<SkColorSpace> fColorSpace;
+
+ // Memoized SkImages formed with planes, one for each Type.
+ sk_sp<SkImage> fYUVImage[3];
LazyYUVImage() = default;
- bool reset(sk_sp<SkData> data, GrMipmapped);
+ bool reset(sk_sp<SkData> data, GrMipmapped, sk_sp<SkColorSpace>);
+ bool reset(SkYUVAPixmaps pixmaps, GrMipmapped, sk_sp<SkColorSpace>);
- bool ensureYUVImage(GrRecordingContext* rContext);
+ bool ensureYUVImage(GrRecordingContext* rContext, Type type);
};
-// 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