blob: c956596ed67a9764c3a9bccfae601667618ffd3f [file] [log] [blame]
/*
* Copyright 2020 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "include/core/SkYUVAPixmaps.h"
#include "include/private/SkImageInfoPriv.h"
#include "src/core/SkConvertPixels.h"
#include "src/core/SkYUVAInfoLocation.h"
#if SK_SUPPORT_GPU
#include "include/private/GrImageContext.h"
#endif
SkYUVAPixmapInfo::SupportedDataTypes::SupportedDataTypes(const GrImageContext& context) {
#if SK_SUPPORT_GPU
for (int n = 1; n <= 4; ++n) {
if (context.defaultBackendFormat(DefaultColorTypeForDataType(DataType::kUnorm8, n),
GrRenderable::kNo).isValid()) {
this->enableDataType(DataType::kUnorm8, n);
}
if (context.defaultBackendFormat(DefaultColorTypeForDataType(DataType::kUnorm16, n),
GrRenderable::kNo).isValid()) {
this->enableDataType(DataType::kUnorm16, n);
}
if (context.defaultBackendFormat(DefaultColorTypeForDataType(DataType::kFloat16, n),
GrRenderable::kNo).isValid()) {
this->enableDataType(DataType::kFloat16, n);
}
if (context.defaultBackendFormat(DefaultColorTypeForDataType(DataType::kUnorm10_Unorm2, n),
GrRenderable::kNo).isValid()) {
this->enableDataType(DataType::kUnorm10_Unorm2, n);
}
}
#endif
}
void SkYUVAPixmapInfo::SupportedDataTypes::enableDataType(DataType type, int numChannels) {
if (numChannels < 1 || numChannels > 4) {
return;
}
fDataTypeSupport[static_cast<size_t>(type) + (numChannels - 1)*kDataTypeCnt] = true;
}
//////////////////////////////////////////////////////////////////////////////
std::tuple<int, SkYUVAPixmapInfo::DataType> SkYUVAPixmapInfo::NumChannelsAndDataType(
SkColorType ct) {
// We could allow BGR[A] color types, but then we'd have to decide whether B should be the 0th
// or 2nd channel. Our docs currently say channel order is always R=0, G=1, B=2[, A=3].
switch (ct) {
case kAlpha_8_SkColorType:
case kGray_8_SkColorType: return {1, DataType::kUnorm8 };
case kA16_unorm_SkColorType: return {1, DataType::kUnorm16};
case kA16_float_SkColorType: return {1, DataType::kFloat16};
case kR8G8_unorm_SkColorType: return {2, DataType::kUnorm8 };
case kR16G16_unorm_SkColorType: return {2, DataType::kUnorm16 };
case kR16G16_float_SkColorType: return {2, DataType::kFloat16 };
case kRGB_888x_SkColorType: return {3, DataType::kUnorm8 };
case kRGB_101010x_SkColorType: return {3, DataType::kUnorm10_Unorm2 };
case kRGBA_8888_SkColorType: return {4, DataType::kUnorm8 };
case kR16G16B16A16_unorm_SkColorType: return {4, DataType::kUnorm16 };
case kRGBA_F16_SkColorType: return {4, DataType::kFloat16 };
case kRGBA_F16Norm_SkColorType: return {4, DataType::kFloat16 };
case kRGBA_1010102_SkColorType: return {4, DataType::kUnorm10_Unorm2 };
default: return {0, DataType::kUnorm8 };
}
}
SkYUVAPixmapInfo::SkYUVAPixmapInfo(const SkYUVAInfo& yuvaInfo,
const SkColorType colorTypes[kMaxPlanes],
const size_t rowBytes[kMaxPlanes])
: fYUVAInfo(yuvaInfo) {
if (!yuvaInfo.isValid()) {
*this = {};
SkASSERT(!this->isValid());
return;
}
SkISize planeDimensions[4];
int n = yuvaInfo.planeDimensions(planeDimensions);
size_t tempRowBytes[kMaxPlanes];
if (!rowBytes) {
for (int i = 0; i < n; ++i) {
tempRowBytes[i] = SkColorTypeBytesPerPixel(colorTypes[i]) * planeDimensions[i].width();
}
rowBytes = tempRowBytes;
}
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);
int numRequiredChannels = yuvaInfo.numChannelsInPlane(i);
SkASSERT(numRequiredChannels > 0);
auto [numColorTypeChannels, colorTypeDataType] = NumChannelsAndDataType(colorTypes[i]);
ok &= i == 0 || colorTypeDataType == fDataType;
ok &= numColorTypeChannels >= numRequiredChannels;
ok &= fPlaneInfos[i].validRowBytes(fRowBytes[i]);
fDataType = colorTypeDataType;
}
if (!ok) {
*this = {};
SkASSERT(!this->isValid());
} else {
SkASSERT(this->isValid());
}
}
SkYUVAPixmapInfo::SkYUVAPixmapInfo(const SkYUVAInfo& yuvaInfo,
DataType dataType,
const size_t rowBytes[kMaxPlanes]) {
SkColorType colorTypes[kMaxPlanes] = {};
int numPlanes = yuvaInfo.numPlanes();
for (int i = 0; i < numPlanes; ++i) {
int numChannels = yuvaInfo.numChannelsInPlane(i);
colorTypes[i] = DefaultColorTypeForDataType(dataType, numChannels);
}
*this = SkYUVAPixmapInfo(yuvaInfo, colorTypes, rowBytes);
}
bool SkYUVAPixmapInfo::operator==(const SkYUVAPixmapInfo& that) const {
bool result = fYUVAInfo == that.fYUVAInfo &&
fPlaneInfos == that.fPlaneInfos &&
fRowBytes == that.fRowBytes;
SkASSERT(!result || fDataType == that.fDataType);
return result;
}
size_t SkYUVAPixmapInfo::computeTotalBytes(size_t planeSizes[kMaxPlanes]) const {
if (!this->isValid()) {
if (planeSizes) {
std::fill_n(planeSizes, kMaxPlanes, 0);
}
return 0;
}
return fYUVAInfo.computeTotalBytes(fRowBytes.data(), planeSizes);
}
bool SkYUVAPixmapInfo::initPixmapsFromSingleAllocation(void* memory,
SkPixmap pixmaps[kMaxPlanes]) const {
if (!this->isValid()) {
return false;
}
SkASSERT(pixmaps);
char* addr = static_cast<char*>(memory);
int n = this->numPlanes();
for (int i = 0; i < n; ++i) {
SkASSERT(fPlaneInfos[i].validRowBytes(fRowBytes[i]));
pixmaps[i].reset(fPlaneInfos[i], addr, fRowBytes[i]);
size_t planeSize = pixmaps[i].rowBytes()*pixmaps[i].height();
SkASSERT(planeSize);
addr += planeSize;
}
for (int i = n; i < kMaxPlanes; ++i) {
pixmaps[i] = {};
}
return true;
}
bool SkYUVAPixmapInfo::isSupported(const SupportedDataTypes& supportedDataTypes) const {
if (!this->isValid()) {
return false;
}
return supportedDataTypes.supported(fYUVAInfo.planeConfig(), fDataType);
}
//////////////////////////////////////////////////////////////////////////////
SkColorType SkYUVAPixmaps::RecommendedRGBAColorType(DataType dataType) {
switch (dataType) {
case DataType::kUnorm8: return kRGBA_8888_SkColorType;
// F16 has better GPU support than 16 bit unorm. Often "16" bit unorm values are actually
// lower precision.
case DataType::kUnorm16: return kRGBA_F16_SkColorType;
case DataType::kFloat16: return kRGBA_F16_SkColorType;
case DataType::kUnorm10_Unorm2: return kRGBA_1010102_SkColorType;
}
SkUNREACHABLE;
}
SkYUVAPixmaps SkYUVAPixmaps::Allocate(const SkYUVAPixmapInfo& yuvaPixmapInfo) {
if (!yuvaPixmapInfo.isValid()) {
return {};
}
return SkYUVAPixmaps(yuvaPixmapInfo,
SkData::MakeUninitialized(yuvaPixmapInfo.computeTotalBytes()));
}
SkYUVAPixmaps SkYUVAPixmaps::FromData(const SkYUVAPixmapInfo& yuvaPixmapInfo, sk_sp<SkData> data) {
if (!yuvaPixmapInfo.isValid()) {
return {};
}
if (yuvaPixmapInfo.computeTotalBytes() > data->size()) {
return {};
}
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()) {
return {};
}
SkPixmap pixmaps[kMaxPlanes];
yuvaPixmapInfo.initPixmapsFromSingleAllocation(memory, pixmaps);
return SkYUVAPixmaps(yuvaPixmapInfo.yuvaInfo(), yuvaPixmapInfo.dataType(), pixmaps);
}
SkYUVAPixmaps SkYUVAPixmaps::FromExternalPixmaps(const SkYUVAInfo& yuvaInfo,
const SkPixmap pixmaps[kMaxPlanes]) {
SkColorType colorTypes[kMaxPlanes] = {};
size_t rowBytes[kMaxPlanes] = {};
int numPlanes = yuvaInfo.numPlanes();
for (int i = 0; i < numPlanes; ++i) {
colorTypes[i] = pixmaps[i].colorType();
rowBytes[i] = pixmaps[i].rowBytes();
}
SkYUVAPixmapInfo yuvaPixmapInfo(yuvaInfo, colorTypes, rowBytes);
if (!yuvaPixmapInfo.isValid()) {
return {};
}
return SkYUVAPixmaps(yuvaInfo, yuvaPixmapInfo.dataType(), pixmaps);
}
SkYUVAPixmaps::SkYUVAPixmaps(const SkYUVAPixmapInfo& yuvaPixmapInfo, sk_sp<SkData> data)
: fData(std::move(data))
, fYUVAInfo(yuvaPixmapInfo.yuvaInfo())
, fDataType(yuvaPixmapInfo.dataType()) {
SkASSERT(yuvaPixmapInfo.isValid());
SkASSERT(yuvaPixmapInfo.computeTotalBytes() <= fData->size());
SkAssertResult(yuvaPixmapInfo.initPixmapsFromSingleAllocation(fData->writable_data(),
fPlanes.data()));
}
SkYUVAPixmaps::SkYUVAPixmaps(const SkYUVAInfo& yuvaInfo,
DataType dataType,
const SkPixmap pixmaps[kMaxPlanes])
: fYUVAInfo(yuvaInfo), fDataType(dataType) {
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};
}
SkYUVAInfo::YUVALocations SkYUVAPixmaps::toYUVALocations() const {
uint32_t channelFlags[] = {SkColorTypeChannelFlags(fPlanes[0].colorType()),
SkColorTypeChannelFlags(fPlanes[1].colorType()),
SkColorTypeChannelFlags(fPlanes[2].colorType()),
SkColorTypeChannelFlags(fPlanes[3].colorType())};
auto result = fYUVAInfo.toYUVALocations(channelFlags);
SkDEBUGCODE(int numPlanes;)
SkASSERT(SkYUVAInfo::YUVALocation::AreValidLocations(result, &numPlanes));
SkASSERT(numPlanes == this->numPlanes());
return result;
}