Add ktx
This commit is contained in:
@@ -0,0 +1,765 @@
|
||||
/* -*- tab-width: 4; -*- */
|
||||
/* vi: set sw=2 ts=4 expandtab: */
|
||||
|
||||
/*
|
||||
* Copyright 2023 The Khronos Group Inc.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @file
|
||||
* @~English
|
||||
*
|
||||
* @brief Data Format Descriptor for imageio.
|
||||
*
|
||||
* @author Mark Callow
|
||||
*/
|
||||
|
||||
#include <exception>
|
||||
#include <iterator>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <KHR/khr_df.h>
|
||||
|
||||
|
||||
/// @brief Image format descriptor
|
||||
///
|
||||
/// Based on Khronos Data Format specification. Omits the parts needed for
|
||||
/// serialization (size, descriptorType, etc.) and, since there is no worry
|
||||
/// about writing & reading across compilers and platforms, uses a struct
|
||||
/// instead of the khr\_df.h macros.
|
||||
///
|
||||
/// Note that @e samples are not @e channels, a.k.a @e components. @e samples
|
||||
/// represent a series of contiguous bits in the bitstream representing
|
||||
/// a pixel of the image. Since the various ImageInput derived classes
|
||||
/// convert incoming data to local endianness most @e channels need only
|
||||
/// a single sample. Only formats such as those with a shared exponent
|
||||
/// need multiple samples per component.
|
||||
///
|
||||
/// This descriptor is way more general than is needed by the current set of
|
||||
/// supported input formats for which there will always be one sample per
|
||||
/// channel. We use this because it is familiar from use elsewhere in
|
||||
/// KTX-Software and because of the large number of useful enums provided
|
||||
/// by khr\_df.h.
|
||||
///
|
||||
/// @note This class uses the Data Format Specification nomenclature of
|
||||
/// @e channel for consistency with thar spec. Elsewhere in KTX-Software
|
||||
/// @e component is widely used.
|
||||
///
|
||||
struct FormatDescriptor {
|
||||
/// @internal
|
||||
/// @brief Basic descriptor.
|
||||
struct basicDescriptor {
|
||||
uint32_t colorModel: 8;
|
||||
uint32_t colorPrimaries: 8;
|
||||
uint32_t transferFunction: 8;
|
||||
uint32_t dataFlags: 8;
|
||||
uint32_t texelBlockDimension0: 8;
|
||||
uint32_t texelBlockDimension1: 8;
|
||||
uint32_t texelBlockDimension2: 8;
|
||||
uint32_t texelBlockDimension3: 8;
|
||||
uint32_t bytesPlane0: 8;
|
||||
uint32_t bytesPlane1: 8;
|
||||
uint32_t bytesPlane2: 8;
|
||||
uint32_t bytesPlane3: 8;
|
||||
uint32_t bytesPlane4: 8;
|
||||
uint32_t bytesPlane5: 8;
|
||||
uint32_t bytesPlane6: 8;
|
||||
uint32_t bytesPlane7: 8;
|
||||
|
||||
/// @brief Default constructor
|
||||
basicDescriptor() {
|
||||
memset(this, 0, sizeof(*this));
|
||||
}
|
||||
|
||||
/// @brief Constructor for unpacked, non-compressed textures.
|
||||
basicDescriptor(uint32_t pixelByteCount,
|
||||
khr_df_transfer_e t = KHR_DF_TRANSFER_UNSPECIFIED,
|
||||
khr_df_primaries_e p = KHR_DF_PRIMARIES_BT709,
|
||||
khr_df_model_e m = KHR_DF_MODEL_RGBSDA,
|
||||
khr_df_flags_e f = KHR_DF_FLAG_ALPHA_STRAIGHT) {
|
||||
colorModel = m;
|
||||
colorPrimaries = p;
|
||||
transferFunction = t;
|
||||
dataFlags = f;
|
||||
texelBlockDimension0 = 0; // Uncompressed means only 1x1x1x1 blocks.
|
||||
texelBlockDimension1 = 0;
|
||||
texelBlockDimension2 = 0;
|
||||
texelBlockDimension3 = 0;
|
||||
bytesPlane0 = pixelByteCount;
|
||||
bytesPlane1 = bytesPlane2 = bytesPlane3 = 0;
|
||||
bytesPlane4 = bytesPlane5 = bytesPlane6 = bytesPlane7 = 0;
|
||||
}
|
||||
|
||||
bool operator==(const basicDescriptor& rhs) const {
|
||||
const uint32_t* a = reinterpret_cast<const uint32_t*>(this);
|
||||
const uint32_t* b = reinterpret_cast<const uint32_t*>(&rhs);
|
||||
for (uint32_t i = 0; i < 4; i++) {
|
||||
if (a[i] != b[i]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool operator!=(const basicDescriptor& rhs) const {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
khr_df_model_e model() const noexcept {
|
||||
return static_cast<khr_df_model_e>(colorModel);
|
||||
}
|
||||
khr_df_primaries_e primaries() const noexcept {
|
||||
return static_cast<khr_df_primaries_e>(colorPrimaries);
|
||||
}
|
||||
khr_df_transfer_e transfer() const noexcept {
|
||||
return static_cast<khr_df_transfer_e>(transferFunction);
|
||||
}
|
||||
khr_df_flags_e flags() const noexcept {
|
||||
return static_cast<khr_df_flags_e>(dataFlags);
|
||||
}
|
||||
} basic;
|
||||
|
||||
/// @internal
|
||||
/// @brief Extended descriptor.
|
||||
///
|
||||
/// In a true DFD this would be an extension descriptor type complete with
|
||||
/// size, vendorId, descriptorType, etc.
|
||||
struct extendedDescriptor {
|
||||
uint32_t channelCount; /// Saved channel count to avoid having to loop
|
||||
/// over samples to figure out the count.
|
||||
bool sameUnitAllChannels = false; /// All samples have same types and sizes.
|
||||
float oeGamma = -1; /// Power function exponent used when the image was
|
||||
/// encoded, if one was used. -1 otherwise.
|
||||
/// @internal
|
||||
/// @brief ICC profile descriptor.
|
||||
struct iccProfileDescriptor {
|
||||
std::string name;
|
||||
std::vector<uint8_t> profile;
|
||||
|
||||
iccProfileDescriptor() { }
|
||||
iccProfileDescriptor(std::string& n, uint8_t* p, size_t ps) {
|
||||
name = n;
|
||||
profile.resize(ps);
|
||||
profile.insert(profile.begin(), p, &p[ps]);
|
||||
}
|
||||
|
||||
bool operator==(const iccProfileDescriptor& rhs) const {
|
||||
bool result = !this->name.compare(rhs.name);
|
||||
return result && (this->profile == rhs.profile);
|
||||
}
|
||||
bool operator!=(const iccProfileDescriptor& rhs) const {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
} iccProfile;
|
||||
|
||||
extendedDescriptor(uint32_t cc = 0) :
|
||||
channelCount(cc) { }
|
||||
|
||||
bool operator==(const extendedDescriptor& rhs) const {
|
||||
if (this->channelCount != rhs.channelCount) return false;
|
||||
if (this->sameUnitAllChannels != rhs.sameUnitAllChannels) return false;
|
||||
if (this->oeGamma != rhs.oeGamma) return false;
|
||||
return this->iccProfile == rhs.iccProfile;
|
||||
}
|
||||
bool operator!=(const extendedDescriptor& rhs) const {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
} extended;
|
||||
|
||||
struct sample {
|
||||
uint32_t bitOffset: 16;
|
||||
uint32_t bitLength: 8;
|
||||
// uint32_t channelType: 8;
|
||||
uint32_t channelType: 4;
|
||||
uint32_t qualifierLinear: 1;
|
||||
uint32_t qualifierExponent: 1;
|
||||
uint32_t qualifierSigned: 1;
|
||||
uint32_t qualifierFloat: 1;
|
||||
uint32_t samplePosition0: 8;
|
||||
uint32_t samplePosition1: 8;
|
||||
uint32_t samplePosition2: 8;
|
||||
uint32_t samplePosition3: 8;
|
||||
uint32_t lower;
|
||||
uint32_t upper;
|
||||
|
||||
bool operator==(const sample& rhs) const {
|
||||
const uint32_t* a = reinterpret_cast<const uint32_t*>(this);
|
||||
const uint32_t* b = reinterpret_cast<const uint32_t*>(&rhs);
|
||||
for (uint32_t i = 0; i < 4; i++) {
|
||||
if (a[i] != b[i]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// @brief Constructs an uninitialized sample object
|
||||
sample() = default;
|
||||
|
||||
/// @brief Construct a sample with default sampleUpper and sampleLower.
|
||||
///
|
||||
/// For uncompressed formats. Handle integer data as normalized. For
|
||||
/// unsigned use the full range of the number of bits. For signed set
|
||||
/// sampleUpper and sampleLower so 0 is representable.
|
||||
sample(uint32_t chanType,
|
||||
uint32_t bLength, uint32_t offset,
|
||||
khr_df_sample_datatype_qualifiers_e dataType
|
||||
= static_cast<khr_df_sample_datatype_qualifiers_e>(0),
|
||||
khr_df_transfer_e tf = KHR_DF_TRANSFER_UNSPECIFIED,
|
||||
khr_df_model_e m = KHR_DF_MODEL_RGBSDA) {
|
||||
bitOffset = offset;
|
||||
bitLength = bLength - 1;
|
||||
channelType = chanType;
|
||||
if (channelType == 3 && m != KHR_DF_MODEL_XYZW) {
|
||||
/// XYZW does not have an alpha chennel. *_ALPHA has the same
|
||||
/// value for all other 4-channel-capable uncompressed models.
|
||||
channelType = KHR_DF_CHANNEL_RGBSDA_ALPHA;
|
||||
}
|
||||
qualifierFloat = (dataType & KHR_DF_SAMPLE_DATATYPE_FLOAT) != 0;
|
||||
qualifierSigned = (dataType & KHR_DF_SAMPLE_DATATYPE_SIGNED) != 0;
|
||||
qualifierExponent = (dataType & KHR_DF_SAMPLE_DATATYPE_EXPONENT) != 0;
|
||||
qualifierLinear = (dataType & KHR_DF_SAMPLE_DATATYPE_LINEAR) != 0;
|
||||
if (tf > KHR_DF_TRANSFER_LINEAR
|
||||
&& channelType == KHR_DF_CHANNEL_RGBSDA_ALPHA) {
|
||||
qualifierLinear = 1;
|
||||
}
|
||||
|
||||
union {
|
||||
uint32_t i;
|
||||
float f;
|
||||
} uLower, uUpper;
|
||||
if (qualifierFloat) {
|
||||
if (qualifierSigned) {
|
||||
uUpper.f = 1.0f;
|
||||
uLower.f = -1.0f;
|
||||
} else {
|
||||
uUpper.f = 1.0f;
|
||||
uLower.f = 0.0f;
|
||||
}
|
||||
} else {
|
||||
if (qualifierSigned) {
|
||||
// signed normalized
|
||||
if (bitLength > 32) {
|
||||
uUpper.i = 0x7FFFFFFF;
|
||||
} else {
|
||||
uUpper.i = (1U << (bLength - 1)) - 1;
|
||||
}
|
||||
uLower.i = ~uUpper.i;
|
||||
uLower.i += 1;
|
||||
} else {
|
||||
// unsigned normalized
|
||||
if (bitLength > 32) {
|
||||
uUpper.i = 0xFFFFFFFFU;
|
||||
} else {
|
||||
uUpper.i = (uint32_t)((1U << bLength) - 1U);
|
||||
}
|
||||
uLower.i = 0U;
|
||||
}
|
||||
}
|
||||
lower = uLower.i;
|
||||
upper = uUpper.i;
|
||||
}
|
||||
|
||||
/// @brief Construct a sample with custom sampleLower and sampleUpper.
|
||||
///
|
||||
/// For uncompressed formats.
|
||||
sample(uint32_t chanType,
|
||||
uint32_t bitLength, uint32_t offset,
|
||||
uint32_t sampleLower, uint32_t sampleUpper,
|
||||
khr_df_sample_datatype_qualifiers_e dataType
|
||||
= static_cast<khr_df_sample_datatype_qualifiers_e>(0),
|
||||
khr_df_transfer_e tf = KHR_DF_TRANSFER_UNSPECIFIED,
|
||||
khr_df_model_e m = KHR_DF_MODEL_RGBSDA)
|
||||
: sample(chanType, bitLength, offset, dataType, tf, m)
|
||||
{
|
||||
if (qualifierFloat) {
|
||||
throw std::runtime_error(
|
||||
"Invalid use of constructor for float data"
|
||||
);
|
||||
}
|
||||
lower = sampleLower;
|
||||
upper = sampleUpper;
|
||||
}
|
||||
};
|
||||
std::vector<sample> samples;
|
||||
|
||||
/// @brief Default constructor
|
||||
///
|
||||
/// Will have zero samples which means format unknown.
|
||||
FormatDescriptor() { }
|
||||
|
||||
/// @brief Constructor for unpacked, non-compressed data.
|
||||
///
|
||||
/// All channels have the same number of bits and basic data type.
|
||||
/// As all wide data types will be in local endianness we need only
|
||||
/// one sample per channel.
|
||||
///
|
||||
/// Data is assumed to be unsigned normalized. @c sampleUpper will be
|
||||
/// set to the max value representable by @a channelBitLength.
|
||||
///
|
||||
/// @c channelType will be set to the standard channel types for @a channelCount
|
||||
/// and @a m.
|
||||
FormatDescriptor(uint32_t channelCount, uint32_t channelBitLength,
|
||||
khr_df_sample_datatype_qualifiers_e dt
|
||||
= static_cast<khr_df_sample_datatype_qualifiers_e>(0),
|
||||
khr_df_transfer_e t = KHR_DF_TRANSFER_UNSPECIFIED,
|
||||
khr_df_primaries_e p = KHR_DF_PRIMARIES_BT709,
|
||||
khr_df_model_e m = KHR_DF_MODEL_RGBSDA,
|
||||
khr_df_flags_e f = KHR_DF_FLAG_ALPHA_STRAIGHT)
|
||||
: basic((channelBitLength * channelCount) / 8, t, p, m, f),
|
||||
extended(channelCount)
|
||||
{
|
||||
for (uint32_t s = 0; s < channelCount; s++) {
|
||||
samples.push_back(sample(s, channelBitLength,
|
||||
s * channelBitLength,
|
||||
dt, t, m));
|
||||
}
|
||||
if (m == KHR_DF_MODEL_YUVSDA && channelCount == 2) {
|
||||
samples[1].channelType = KHR_DF_CHANNEL_YUVSDA_ALPHA;
|
||||
}
|
||||
extended.sameUnitAllChannels = true;
|
||||
}
|
||||
|
||||
/// @brief Constructor for unpacked, non-compressed data with custom
|
||||
/// sampleLower and sampleUpper
|
||||
///
|
||||
/// All channels have the same number of bits and basic data type.
|
||||
/// Use this for unnormalized integer data or normalized data that does not
|
||||
/// use the full range representable by @a channelBitLength.
|
||||
FormatDescriptor(uint32_t channelCount, uint32_t channelBitLength,
|
||||
uint32_t sampleLower, uint32_t sampleUpper,
|
||||
khr_df_sample_datatype_qualifiers_e dt
|
||||
= static_cast<khr_df_sample_datatype_qualifiers_e>(0),
|
||||
khr_df_transfer_e t = KHR_DF_TRANSFER_UNSPECIFIED,
|
||||
khr_df_primaries_e p = KHR_DF_PRIMARIES_BT709,
|
||||
khr_df_model_e m = KHR_DF_MODEL_RGBSDA,
|
||||
khr_df_flags_e f = KHR_DF_FLAG_ALPHA_STRAIGHT)
|
||||
: basic((channelBitLength * channelCount) / 8, t, p, m, f),
|
||||
extended(channelCount)
|
||||
{
|
||||
for (uint32_t s = 0; s < channelCount; s++) {
|
||||
samples.push_back(sample(s, channelBitLength,
|
||||
s * channelBitLength,
|
||||
sampleLower, sampleUpper,
|
||||
dt, t, m));
|
||||
}
|
||||
if (m == KHR_DF_MODEL_YUVSDA && channelCount == 2) {
|
||||
samples[1].channelType = KHR_DF_CHANNEL_YUVSDA_ALPHA;
|
||||
}
|
||||
extended.sameUnitAllChannels = true;
|
||||
}
|
||||
|
||||
static uint32_t totalBits(uint32_t sampleCount, std::vector<uint32_t>& bits) {
|
||||
uint32_t totalBits = 0;
|
||||
for (uint32_t s = 0; s < sampleCount; s++) {
|
||||
totalBits += bits[s];
|
||||
}
|
||||
return totalBits;
|
||||
}
|
||||
|
||||
/// @brief Constructor for non-compressed textures with varying bit lengths or channel types.
|
||||
///
|
||||
/// Each channel has the same basic data type.
|
||||
FormatDescriptor(uint32_t channelCount,
|
||||
std::vector<uint32_t>& channelBitLengths,
|
||||
std::vector<khr_df_model_channels_e>& channelTypes,
|
||||
khr_df_sample_datatype_qualifiers_e dt
|
||||
= static_cast<khr_df_sample_datatype_qualifiers_e>(0),
|
||||
khr_df_transfer_e t = KHR_DF_TRANSFER_UNSPECIFIED,
|
||||
khr_df_primaries_e p = KHR_DF_PRIMARIES_BT709,
|
||||
khr_df_model_e m = KHR_DF_MODEL_RGBSDA,
|
||||
khr_df_flags_e f = KHR_DF_FLAG_ALPHA_STRAIGHT)
|
||||
: basic(totalBits(channelCount, channelBitLengths) >> 3, t, p, m, f),
|
||||
extended(channelCount)
|
||||
{
|
||||
if (channelCount > channelBitLengths.size()
|
||||
|| channelCount > channelTypes.size()) {
|
||||
throw std::runtime_error(
|
||||
"Not enough channelBits or channelType specfications."
|
||||
);
|
||||
}
|
||||
uint32_t bitOffset = 0;
|
||||
bool bitLengthsEqual = true;
|
||||
uint32_t firstBitLength = channelBitLengths[0];
|
||||
for (uint32_t s = 0; s < channelCount; s++) {
|
||||
samples.push_back(sample(channelTypes[s], channelBitLengths[s],
|
||||
bitOffset, dt, t, m));
|
||||
bitOffset += channelBitLengths[s];
|
||||
if (firstBitLength != channelBitLengths[s]) {
|
||||
bitLengthsEqual = false;
|
||||
}
|
||||
}
|
||||
if (bitLengthsEqual) {
|
||||
extended.sameUnitAllChannels = true;
|
||||
}
|
||||
if (m == KHR_DF_MODEL_YUVSDA && channelCount == 2) {
|
||||
samples[1].channelType = KHR_DF_CHANNEL_YUVSDA_ALPHA;
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Constructor for non-compressed textures with varying bit lengths
|
||||
/// or channel types and custom sampleLower and sampleUpper.
|
||||
///
|
||||
/// Each channel has the same basic data type. Use this for unnormalized
|
||||
/// integer data or normalized data that does not use the full bit range.
|
||||
FormatDescriptor(uint32_t channelCount,
|
||||
std::vector<uint32_t>& channelBitLengths,
|
||||
std::vector<khr_df_model_channels_e>& channelTypes,
|
||||
std::vector<uint32_t>& samplesLower,
|
||||
std::vector<uint32_t>& samplesUpper,
|
||||
khr_df_sample_datatype_qualifiers_e dt
|
||||
= static_cast<khr_df_sample_datatype_qualifiers_e>(0),
|
||||
khr_df_transfer_e t = KHR_DF_TRANSFER_UNSPECIFIED,
|
||||
khr_df_primaries_e p = KHR_DF_PRIMARIES_BT709,
|
||||
khr_df_model_e m = KHR_DF_MODEL_RGBSDA,
|
||||
khr_df_flags_e f = KHR_DF_FLAG_ALPHA_STRAIGHT)
|
||||
: basic(totalBits(channelCount, channelBitLengths) >> 3, t, p, m, f),
|
||||
extended(channelCount)
|
||||
{
|
||||
if (channelCount > channelBitLengths.size()
|
||||
|| channelCount > channelTypes.size()) {
|
||||
throw std::runtime_error(
|
||||
"Not enough channelBits or channelType specfications."
|
||||
);
|
||||
}
|
||||
uint32_t bitOffset = 0;
|
||||
bool bitLengthsEqual = true;
|
||||
uint32_t firstBitLength = channelBitLengths[0];
|
||||
for (uint32_t s = 0; s < channelCount; s++) {
|
||||
samples.push_back(sample(channelTypes[s], channelBitLengths[s],
|
||||
samplesLower[s], samplesUpper[s],
|
||||
bitOffset, dt, t, m));
|
||||
bitOffset += channelBitLengths[s];
|
||||
if (firstBitLength != channelBitLengths[s]) {
|
||||
bitLengthsEqual = false;
|
||||
}
|
||||
}
|
||||
if (bitLengthsEqual) {
|
||||
extended.sameUnitAllChannels = true;
|
||||
}
|
||||
if (m == KHR_DF_MODEL_YUVSDA && channelCount == 2) {
|
||||
samples[1].channelType = KHR_DF_CHANNEL_YUVSDA_ALPHA;
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Constructor for non-compressed, shared exponent format.
|
||||
///
|
||||
/// Each channel is a floating point. All channels share the same exponent
|
||||
/// and have the same number of mantissa bits.
|
||||
// TODO: Handle whether there is an implicit 1 and a sign bit.
|
||||
FormatDescriptor(uint32_t channelCount,
|
||||
uint32_t mantissaBitLength,
|
||||
uint32_t exponentBitLength,
|
||||
khr_df_sample_datatype_qualifiers_e dt
|
||||
= static_cast<khr_df_sample_datatype_qualifiers_e>(0),
|
||||
khr_df_transfer_e t = KHR_DF_TRANSFER_LINEAR,
|
||||
khr_df_primaries_e p = KHR_DF_PRIMARIES_BT709,
|
||||
khr_df_model_e m = KHR_DF_MODEL_RGBSDA,
|
||||
khr_df_flags_e f = KHR_DF_FLAG_ALPHA_STRAIGHT)
|
||||
: basic((channelCount * mantissaBitLength + exponentBitLength) >> 3,
|
||||
t, p, m, f),
|
||||
extended(channelCount)
|
||||
{
|
||||
if (dt & KHR_DF_SAMPLE_DATATYPE_FLOAT) {
|
||||
throw std::runtime_error(
|
||||
"DATATYPE_FLOAT is set for a shared exponent format");
|
||||
}
|
||||
for (uint32_t s = 0; s < channelCount; s++) {
|
||||
uint32_t sampleLower = 0, sampleUpper;
|
||||
// sampleUpper and sampleLower values for the mantissa should be
|
||||
// set to indicate the representation of 1.0 and 0.0 (for unsigned
|
||||
// formats) or -1.0 (for signed formats) respectively when the
|
||||
// exponent is in a 0 position after any bias has been corrected.
|
||||
// If there is an implicit 1 bit, these values for the mantissa
|
||||
// will exceed what can be represented in the number of available
|
||||
// mantissa bits.
|
||||
sampleUpper = 1U << mantissaBitLength;
|
||||
samples.push_back(sample(s, mantissaBitLength,
|
||||
s * mantissaBitLength,
|
||||
sampleLower, sampleUpper,
|
||||
dt, t, m));
|
||||
// The sampleLower for the exponent should indicate the exponent
|
||||
// bias. That is, the mantissa should be scaled by two raised to
|
||||
// the power of the stored exponent minus this sampleLower value.
|
||||
//
|
||||
// The sampleUpper for the exponent indicates the maximum legal
|
||||
// exponent value. Values above this are used to encode infinities
|
||||
// and not-a-number (NaN) values. sampleUpper can therefore be used
|
||||
// to indicate whether or not the format supports these encodings.
|
||||
//sampleLower = exponentBias;
|
||||
//sampleUpper = maxLegalExponentValue;
|
||||
samples.push_back(sample(s, exponentBitLength,
|
||||
channelCount * mantissaBitLength,
|
||||
sampleLower, sampleUpper,
|
||||
static_cast<khr_df_sample_datatype_qualifiers_e>(dt | KHR_DF_SAMPLE_DATATYPE_EXPONENT),
|
||||
t, m));
|
||||
}
|
||||
extended.sameUnitAllChannels = true;
|
||||
}
|
||||
|
||||
/// @brief Constructor from pre-constructed basic and sample descriptors
|
||||
FormatDescriptor(
|
||||
FormatDescriptor::basicDescriptor basic,
|
||||
std::vector<FormatDescriptor::sample> samples_)
|
||||
: basic(basic),
|
||||
extended(static_cast<uint32_t>(samples_.size())),
|
||||
samples(std::move(samples_))
|
||||
{
|
||||
extended.sameUnitAllChannels = true;
|
||||
|
||||
if (!samples.empty()) {
|
||||
for (uint32_t i = 1; i < static_cast<uint32_t>(samples.size()); ++i) {
|
||||
if (samples[0].bitLength != samples[i].bitLength
|
||||
|| samples[0].qualifierLinear != samples[i].qualifierLinear
|
||||
|| samples[0].qualifierExponent != samples[i].qualifierExponent
|
||||
|| samples[0].qualifierSigned != samples[i].qualifierSigned
|
||||
|| samples[0].qualifierFloat != samples[i].qualifierFloat) {
|
||||
extended.sameUnitAllChannels = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool isUnknown() const noexcept {
|
||||
return samples.size() == 0;
|
||||
}
|
||||
bool sameUnitAllChannels() const noexcept {
|
||||
return extended.sameUnitAllChannels;
|
||||
}
|
||||
|
||||
bool operator==(const FormatDescriptor& rhs) const {
|
||||
if (this->basic != rhs.basic) return false;
|
||||
if (this->extended != rhs.extended) return false;
|
||||
return this->samples == rhs.samples;
|
||||
}
|
||||
bool operator!=(const FormatDescriptor& rhs) const {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
khr_df_model_e model() const noexcept {
|
||||
return basic.model();
|
||||
}
|
||||
khr_df_primaries_e primaries() const noexcept {
|
||||
return basic.primaries();
|
||||
}
|
||||
khr_df_transfer_e transfer() const noexcept {
|
||||
return basic.transfer();
|
||||
}
|
||||
khr_df_flags_e flags() const noexcept {
|
||||
return basic.flags();
|
||||
}
|
||||
float oeGamma() const noexcept {
|
||||
return extended.oeGamma;
|
||||
}
|
||||
const std::string& iccProfileName() const noexcept {
|
||||
return extended.iccProfile.name;
|
||||
}
|
||||
const std::vector<uint8_t>& iccProfile() const noexcept {
|
||||
return extended.iccProfile.profile;
|
||||
}
|
||||
void setModel(khr_df_model_e m) {
|
||||
basic.colorModel = m;
|
||||
}
|
||||
void setPrimaries(khr_df_primaries_e p) {
|
||||
basic.colorPrimaries = p;
|
||||
}
|
||||
void setTransfer(khr_df_transfer_e t) {
|
||||
khr_df_transfer_e oldOetf = transfer();
|
||||
basic.transferFunction = t;
|
||||
if ((oldOetf <= KHR_DF_TRANSFER_LINEAR) != (t <= KHR_DF_TRANSFER_LINEAR))
|
||||
{
|
||||
std::vector<sample>::iterator sit = samples.begin();
|
||||
for (; sit < samples.end(); sit++) {
|
||||
if (sit->channelType == KHR_DF_CHANNEL_RGBSDA_ALPHA) {
|
||||
sit->qualifierLinear = t > KHR_DF_TRANSFER_LINEAR;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
uint32_t pixelByteCount() const noexcept {
|
||||
return basic.bytesPlane0;
|
||||
}
|
||||
uint32_t sampleCount() const noexcept {
|
||||
return static_cast<uint32_t>(samples.size());
|
||||
}
|
||||
uint32_t sampleBitLength(uint32_t s) const noexcept {
|
||||
return samples[s].bitLength + 1;
|
||||
}
|
||||
// TODO: remove?
|
||||
uint32_t sampleByteCount(uint32_t s) const noexcept {
|
||||
// Use integer division so 0 is returned when length is < a byte.
|
||||
return sampleBitLength(s) / 8;
|
||||
}
|
||||
uint32_t sampleUpper(uint32_t s) const noexcept {
|
||||
return samples[s].upper;
|
||||
}
|
||||
uint32_t channelCount() const noexcept {
|
||||
return extended.channelCount;
|
||||
}
|
||||
uint32_t channelBitLength(khr_df_model_channels_e c) const {
|
||||
std::vector<sample>::const_iterator it = samples.begin();
|
||||
uint32_t bitLength = 0;
|
||||
for (; it < samples.end(); it++) {
|
||||
if (it->channelType == static_cast<uint32_t>(c)) {
|
||||
bitLength += it->bitLength + 1;
|
||||
}
|
||||
}
|
||||
if (bitLength == 0) {
|
||||
throw std::runtime_error("No such channel.");
|
||||
}
|
||||
return bitLength;
|
||||
}
|
||||
uint32_t channelBitLength() const {
|
||||
if (channelCount() == 1)
|
||||
return samples[0].bitLength + 1;
|
||||
|
||||
if (!extended.sameUnitAllChannels) {
|
||||
throw std::runtime_error(
|
||||
"Differing size channels. Specify channel to query."
|
||||
);
|
||||
}
|
||||
return channelBitLength(KHR_DF_CHANNEL_RGBSDA_R);
|
||||
}
|
||||
uint32_t largestChannelBitLength() const {
|
||||
uint32_t maxBitLength = 0;
|
||||
for (uint32_t i = 0; i < 16; ++i) {
|
||||
uint32_t bitLength = 0;
|
||||
for (const auto& sample : samples)
|
||||
if (sample.channelType == i)
|
||||
bitLength += sample.bitLength + 1;
|
||||
|
||||
if (bitLength > maxBitLength)
|
||||
maxBitLength = bitLength;
|
||||
}
|
||||
return maxBitLength;
|
||||
}
|
||||
bool anyChannelBitLengthNotEqual(uint32_t bitLength) const {
|
||||
for (uint32_t i = 0; i < 16; ++i) {
|
||||
uint32_t channelBitLength = 0;
|
||||
for (const auto& sample : samples)
|
||||
if (sample.channelType == i)
|
||||
channelBitLength += sample.bitLength + 1;
|
||||
|
||||
if (bitLength != channelBitLength)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
khr_df_sample_datatype_qualifiers_e
|
||||
channelDataType(khr_df_model_channels_e c) const {
|
||||
// TODO: Fix for shared exponent case...
|
||||
std::vector<sample>::const_iterator it = samples.begin();
|
||||
for (; it < samples.end(); it++) {
|
||||
if (it->channelType == static_cast<uint32_t>(c)) {
|
||||
return static_cast<khr_df_sample_datatype_qualifiers_e>
|
||||
(it->channelType & KHR_DF_SAMPLEMASK_QUALIFIERS);
|
||||
}
|
||||
}
|
||||
throw std::runtime_error("No such channel.");
|
||||
}
|
||||
|
||||
khr_df_sample_datatype_qualifiers_e channelDataType() const {
|
||||
if (!extended.sameUnitAllChannels) {
|
||||
throw std::runtime_error(
|
||||
"Differing size channels. Specify channel to query."
|
||||
);
|
||||
}
|
||||
return channelDataType(KHR_DF_CHANNEL_RGBSDA_R);
|
||||
}
|
||||
uint32_t channelUpper() const {
|
||||
if (extended.channelCount != samples.size()) {
|
||||
throw std::runtime_error(
|
||||
"Multiple samples per channel. Call sampleUpper(uint32_t s)."
|
||||
);
|
||||
}
|
||||
if (!extended.sameUnitAllChannels) {
|
||||
throw std::runtime_error(
|
||||
"Differing size channels. Call sampleUpper(uint32_t s)."
|
||||
);
|
||||
}
|
||||
return sampleUpper(KHR_DF_CHANNEL_RGBSDA_R);
|
||||
}
|
||||
void updateSampleInfo(uint32_t channelCount, uint32_t channelBitLength,
|
||||
uint32_t sampleLower, uint32_t sampleUpper,
|
||||
khr_df_sample_datatype_qualifiers_e dt
|
||||
= static_cast<khr_df_sample_datatype_qualifiers_e>(0))
|
||||
{
|
||||
samples.clear();
|
||||
for (uint32_t s = 0; s < channelCount; s++) {
|
||||
samples.push_back(sample(s, channelBitLength,
|
||||
s * channelBitLength,
|
||||
sampleLower, sampleUpper,
|
||||
dt,
|
||||
basic.transfer(),
|
||||
basic.model()));
|
||||
}
|
||||
if (basic.model() == KHR_DF_MODEL_YUVSDA && channelCount == 2) {
|
||||
samples[1].channelType = KHR_DF_CHANNEL_YUVSDA_ALPHA;
|
||||
}
|
||||
extended.channelCount = channelCount;
|
||||
extended.sameUnitAllChannels = true;
|
||||
}
|
||||
|
||||
void updateSampleBitCounts(std::vector<uint32_t>& bits) {
|
||||
uint32_t b, s;
|
||||
uint32_t offset = 0;
|
||||
for (b = 0, s = 0; s < samples.size(); s++) {
|
||||
samples[s].bitLength = bits[b] - 1;
|
||||
samples[s].bitOffset = offset;
|
||||
offset += bits[b];
|
||||
if (b < bits.size() - 1)
|
||||
b++;
|
||||
// else set remaining sample sizes to last available bits value.
|
||||
}
|
||||
}
|
||||
void updateChannelCount(uint32_t newCount) {
|
||||
if (newCount == extended.channelCount)
|
||||
return;
|
||||
if (extended.channelCount != samples.size()) {
|
||||
// TODO: Either fix error handling or implement
|
||||
throw std::runtime_error(
|
||||
"changeChannelCount not supported when # samples != # channels");
|
||||
}
|
||||
extended.channelCount = newCount;
|
||||
if (newCount < samples.size()) {
|
||||
samples.erase(samples.begin() + newCount, samples.end());
|
||||
return;
|
||||
}
|
||||
uint32_t firstNewIndex = static_cast<uint32_t>(samples.size());
|
||||
uint32_t offset = samples.back().bitOffset
|
||||
+ samples.back().bitLength + 1;
|
||||
samples.resize(newCount, samples.back());
|
||||
std::vector<sample>::iterator sit = samples.begin() + firstNewIndex;
|
||||
for (; sit < samples.end(); sit++) {
|
||||
sit->bitOffset = offset;
|
||||
offset += (sit->bitLength + 1);
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] const sample* find(khr_df_model_channels_e channel) const {
|
||||
for (const auto& sample : samples)
|
||||
if (sample.channelType == static_cast<uint32_t>(channel))
|
||||
return &sample;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void removeLastChannel() {
|
||||
const auto numChannels = static_cast<uint32_t>(samples.size());
|
||||
assert(numChannels > 1);
|
||||
assert(basic.bytesPlane0 % numChannels == 0);
|
||||
samples.pop_back();
|
||||
basic.bytesPlane0 = basic.bytesPlane0 / numChannels * (numChannels - 1u);
|
||||
if (extended.channelCount != 0)
|
||||
--extended.channelCount;
|
||||
}
|
||||
|
||||
friend std::ostream& operator<< (std::ostream& o, khr_df_sample_datatype_qualifiers_e q) {
|
||||
if (q & KHR_DF_SAMPLE_DATATYPE_SIGNED)
|
||||
o << " signed ";
|
||||
if (q & KHR_DF_SAMPLE_DATATYPE_FLOAT) {
|
||||
if (!(q & KHR_DF_SAMPLE_DATATYPE_SIGNED))
|
||||
o << " ";
|
||||
o << "float ";
|
||||
}
|
||||
return o;
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user