blob: 07b7c82db9848fd3473dd320ccfc031ffce45019 [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/SkYUVAInfo.h"
#include "src/core/SkSafeMath.h"
int SkYUVAInfo::PlaneDimensions(SkISize imageDimensions,
PlanarConfig planarConfig,
SkEncodedOrigin origin,
SkISize* planeDimensions) {
int w = imageDimensions.width();
int h = imageDimensions.height();
if (origin >= kLeftTop_SkEncodedOrigin) {
using std::swap;
swap(w, h);
}
auto down2 = [](int x) { return (x + 1)/2; };
auto down4 = [](int x) { return (x + 3)/4; };
switch (planarConfig) {
case PlanarConfig::kY_U_V_444:
planeDimensions[0] = planeDimensions[1] = planeDimensions[2] = {w, h};
planeDimensions[3] = {0, 0};
return 3;
case PlanarConfig::kY_U_V_422:
planeDimensions[0] = {w, h};
planeDimensions[1] = planeDimensions[2] = {down2(w), h};
planeDimensions[3] = {0, 0};
return 3;
case PlanarConfig::kY_U_V_420:
case PlanarConfig::kY_V_U_420:
planeDimensions[0] = {w, h};
planeDimensions[1] = planeDimensions[2] = {down2(w), down2(h)};
planeDimensions[3] = {0, 0};
return 3;
case PlanarConfig::kY_UV_A_4204:
case PlanarConfig::kY_VU_A_4204:
planeDimensions[0] = planeDimensions[2] = {w, h};
planeDimensions[1] = {down2(w), down2(h)};
planeDimensions[3] = {0, 0};
return 3;
case PlanarConfig::kY_U_V_440:
planeDimensions[0] = {w, h};
planeDimensions[1] = planeDimensions[2] = {w, down2(h)};
planeDimensions[3] = {0, 0};
return 3;
case PlanarConfig::kY_U_V_411:
planeDimensions[0] = {w, h};
planeDimensions[1] = planeDimensions[2] = {down4(w), h};
planeDimensions[3] = {0, 0};
return 3;
case PlanarConfig::kY_U_V_410:
planeDimensions[0] = {w, h};
planeDimensions[1] = planeDimensions[2] = {down4(w), down2(h)};
planeDimensions[3] = {0, 0};
return 3;
case PlanarConfig::kY_U_V_A_4204:
case PlanarConfig::kY_V_U_A_4204:
planeDimensions[0] = planeDimensions[3] = {w, h};
planeDimensions[1] = planeDimensions[2] = {down2(w), down2(h)};
return 4;
case PlanarConfig::kY_UV_420:
case PlanarConfig::kY_VU_420:
planeDimensions[0] = {w, h};
planeDimensions[1] = {down2(w), down2(h)};
planeDimensions[2] = planeDimensions[3] = {0, 0};
return 2;
case PlanarConfig::kYUV_444:
case PlanarConfig::kUYV_444:
case PlanarConfig::kYUVA_4444:
case PlanarConfig::kUYVA_4444:
planeDimensions[0] = {w, h};
planeDimensions[1] = planeDimensions[2] = planeDimensions[3] = {0, 0};
return 1;
}
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) {
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;
case PlanarConfig::kY_U_V_422: return false;
case PlanarConfig::kY_U_V_420: return false;
case PlanarConfig::kY_V_U_420: return false;
case PlanarConfig::kY_U_V_440: return false;
case PlanarConfig::kY_U_V_411: return false;
case PlanarConfig::kY_U_V_410: return false;
case PlanarConfig::kY_U_V_A_4204: return true;
case PlanarConfig::kY_V_U_A_4204: return true;
case PlanarConfig::kY_UV_420: return false;
case PlanarConfig::kY_VU_420: return false;
case PlanarConfig::kY_UV_A_4204: return true;
case PlanarConfig::kY_VU_A_4204: return true;
case PlanarConfig::kYUV_444: return false;
case PlanarConfig::kUYV_444: return false;
case PlanarConfig::kYUVA_4444: return true;
case PlanarConfig::kUYVA_4444: return true;
}
SkUNREACHABLE;
}
SkYUVAInfo::SkYUVAInfo(SkISize dimensions,
PlanarConfig planarConfig,
SkYUVColorSpace yuvColorSpace,
SkEncodedOrigin origin,
Siting sitingX,
Siting sitingY)
: fDimensions(dimensions)
, fPlanarConfig(planarConfig)
, fYUVColorSpace(yuvColorSpace)
, fOrigin(origin)
, fSitingX(sitingX)
, fSitingY(sitingY) {}
size_t SkYUVAInfo::computeTotalBytes(const size_t rowBytes[kMaxPlanes],
size_t planeSizes[kMaxPlanes]) const {
if (fDimensions.isEmpty()) {
return 0;
}
SkSafeMath safe;
size_t totalBytes = 0;
SkISize planeDimensions[kMaxPlanes];
int n = this->planeDimensions(planeDimensions);
for (int i = 0; i < n; ++i) {
SkASSERT(!planeDimensions[i].isEmpty());
SkASSERT(rowBytes[i]);
size_t size = safe.mul(rowBytes[i], planeDimensions[i].height());
if (planeSizes) {
planeSizes[i] = size;
}
totalBytes = safe.add(totalBytes, size);
}
if (planeSizes) {
if (safe.ok()) {
for (int i = n; i < kMaxPlanes; ++i) {
planeSizes[i] = 0;
}
} else {
for (int i = 0; n < kMaxPlanes; ++i) {
planeSizes[i] = SIZE_MAX;
}
}
}
return safe.ok() ? totalBytes : SIZE_MAX;
}
bool SkYUVAInfo::operator==(const SkYUVAInfo& that) const {
return fPlanarConfig == that.fPlanarConfig &&
fYUVColorSpace == that.fYUVColorSpace &&
fDimensions == that.fDimensions &&
fSitingX == that.fSitingX &&
fSitingY == that.fSitingY &&
fOrigin == that.fOrigin;
}