Skip to content

Commit 41b1dbe

Browse files
committed
Add support for 16+ bits YUV files (up to 32-bit)
- Extend BitDepthList to support 8-32 bit depth - Add get_min_standard_bytes() for correct bytes-per-sample calculation - Fix type overflows by upgrading to int64_t - Fix setFormatFromCorrelation loop to test 24/32-bit formats - Use bytesPerSample consistently instead of hardcoded (bps > 8) checks
1 parent a72eb34 commit 41b1dbe

5 files changed

Lines changed: 245 additions & 162 deletions

File tree

YUViewLib/src/video/yuv/PixelFormatYUV.cpp

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ PixelFormatYUV::PixelFormatYUV(const std::string_view name)
175175
auto bitdepthStr = sm.str(3);
176176
size_t sz;
177177
int bitDepth = std::stoi(bitdepthStr, &sz);
178-
if (sz > 0 && bitDepth >= 8 && bitDepth <= 16)
178+
if (sz > 0 && bitDepth >= 8 && bitDepth <= 32)
179179
newFormat.bitsPerSample = bitDepth;
180180
}
181181

@@ -318,7 +318,7 @@ bool PixelFormatYUV::canConvertToRGB(Size imageSize, std::string *whyNot) const
318318
// Check the bit depth
319319
const int bps = this->bitsPerSample;
320320
bool canConvert = true;
321-
if (bps < 8 || bps > 16)
321+
if (bps < 8 || bps > 32)
322322
{
323323
if (whyNot)
324324
{
@@ -383,28 +383,28 @@ int64_t PixelFormatYUV::bytesPerFrame(const Size &frameSize) const
383383
}
384384

385385
int64_t bytes = 0;
386+
const auto bytesPerSample = get_min_standard_bytes(this->bitsPerSample); // Round to bytes
386387

387388
if (this->planar || !this->bytePacking)
388389
{
389390
// Add the bytes of the 3 (or 4) planes.
390391
// This also works for packed formats without byte packing. For these formats the number of
391392
// bytes are identical to the not packed formats, the bytes are just sorted in another way.
392393

393-
const auto bytesPerSample = (this->bitsPerSample + 7) / 8; // Round to bytes
394-
bytes += frameSize.width * frameSize.height * bytesPerSample; // Luma plane
394+
bytes += (int64_t)frameSize.width * frameSize.height * bytesPerSample; // Luma plane
395395
if (this->subsampling == Subsampling::YUV_444)
396-
bytes += frameSize.width * frameSize.height * bytesPerSample * 2; // U/V planes
396+
bytes += (int64_t)frameSize.width * frameSize.height * bytesPerSample * 2; // U/V planes
397397
else if (this->subsampling == Subsampling::YUV_422 || this->subsampling == Subsampling::YUV_440)
398398
bytes +=
399-
(frameSize.width / 2) * frameSize.height * bytesPerSample * 2; // U/V planes, half the width
399+
(int64_t)(frameSize.width / 2) * frameSize.height * bytesPerSample * 2; // U/V planes, half the width
400400
else if (this->subsampling == Subsampling::YUV_420)
401-
bytes += (frameSize.width / 2) * (frameSize.height / 2) * bytesPerSample *
401+
bytes += (int64_t)(frameSize.width / 2) * (frameSize.height / 2) * bytesPerSample *
402402
2; // U/V planes, half the width and height
403403
else if (this->subsampling == Subsampling::YUV_410)
404-
bytes += (frameSize.width / 4) * (frameSize.height / 4) * bytesPerSample *
404+
bytes += (int64_t)(frameSize.width / 4) * (frameSize.height / 4) * bytesPerSample *
405405
2; // U/V planes, half the width and height
406406
else if (this->subsampling == Subsampling::YUV_411)
407-
bytes += (frameSize.width / 4) * frameSize.height * bytesPerSample *
407+
bytes += (int64_t)(frameSize.width / 4) * frameSize.height * bytesPerSample *
408408
2; // U/V planes, quarter the width
409409
else if (this->subsampling == Subsampling::YUV_400)
410410
bytes += 0; // No chroma components
@@ -414,30 +414,29 @@ int64_t PixelFormatYUV::bytesPerFrame(const Size &frameSize) const
414414
if (this->planar &&
415415
(this->planeOrder == PlaneOrder::YUVA || this->planeOrder == PlaneOrder::YVUA))
416416
// There is an additional alpha plane. The alpha plane is not subsampled
417-
bytes += frameSize.width * frameSize.height * bytesPerSample; // Alpha plane
417+
bytes += (int64_t)frameSize.width * frameSize.height * bytesPerSample; // Alpha plane
418418
if (!this->planar && this->subsampling == Subsampling::YUV_444 &&
419419
(this->packingOrder == PackingOrder::AYUV || this->packingOrder == PackingOrder::YUVA ||
420420
this->packingOrder == PackingOrder::VUYA))
421421
// There is an additional alpha plane. The alpha plane is not subsampled
422-
bytes += frameSize.width * frameSize.height * bytesPerSample; // Alpha plane
422+
bytes += (int64_t)frameSize.width * frameSize.height * bytesPerSample; // Alpha plane
423423
}
424424
else
425425
{
426426
// This is a packed format with byte packing
427427
if (this->subsampling == Subsampling::YUV_422)
428428
{
429429
// All packing orders have 4 values per packed value (which has 2 Y samples)
430-
const auto bitsPerPixel = this->bitsPerSample * 4;
431-
return ((bitsPerPixel + 7) / 8) * (frameSize.width / 2) * frameSize.height;
430+
return 4 * bytesPerSample * (frameSize.width / 2) * frameSize.height;
432431
}
433432
// This is a packed format. The added number of bytes might be lower because of the packing.
434433
if (this->subsampling == Subsampling::YUV_444)
435434
{
436-
auto bitsPerPixel = this->bitsPerSample * 3;
435+
auto channels = 3;
437436
if (this->packingOrder == PackingOrder::AYUV || this->packingOrder == PackingOrder::YUVA ||
438437
this->packingOrder == PackingOrder::VUYA)
439-
bitsPerPixel += this->bitsPerSample;
440-
return ((bitsPerPixel + 7) / 8) * frameSize.width * frameSize.height;
438+
channels += 1;
439+
return channels * bytesPerSample * frameSize.width * frameSize.height;
441440
}
442441
// else if (subsampling == Subsampling::YUV_422 || subsampling == Subsampling::YUV_440)
443442
//{

YUViewLib/src/video/yuv/PixelFormatYUV.h

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -95,15 +95,15 @@ class MathParameters
9595
{
9696
public:
9797
MathParameters() = default;
98-
MathParameters(int scale, int offset, bool invert) : scale(scale), offset(offset), invert(invert)
98+
MathParameters(int scale, int64_t offset, bool invert) : scale(scale), offset(offset), invert(invert)
9999
{
100100
}
101101
// Do we need to apply any transform to the raw YUV data before conversion to RGB?
102102
bool mathRequired() const { return scale != 1 || invert; }
103103

104-
int scale{1};
105-
int offset{128};
106-
bool invert{};
104+
int scale{1};
105+
int64_t offset{128};
106+
bool invert{};
107107
};
108108

109109
enum class PredefinedPixelFormat
@@ -184,7 +184,7 @@ constexpr EnumMapper<PlaneOrder, 4> PlaneOrderMapper = {std::make_pair(PlaneOrde
184184
std::make_pair(PlaneOrder::YUVA, "YUVA"),
185185
std::make_pair(PlaneOrder::YVUA, "YVUA")};
186186

187-
const auto BitDepthList = std::vector<unsigned>({8, 9, 10, 12, 14, 16});
187+
const auto BitDepthList = std::vector<unsigned>({8, 9, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32});
188188

189189
// This class defines a specific YUV format with all properties like pixels per sample, subsampling
190190
// of chroma components and so on.
@@ -264,4 +264,21 @@ class PixelFormatYUV
264264
bool bytePacking{};
265265
};
266266

267+
inline uint64_t get_min_standard_bytes(uint64_t x) {
268+
if (x == 0)
269+
return 0;
270+
271+
if (x >= UINT64_MAX / 8) {
272+
return UINT64_MAX;
273+
}
274+
275+
x--;
276+
277+
for (int current_shift = 1; current_shift < 64; current_shift <<= 1) {
278+
x |= x >> current_shift;
279+
}
280+
281+
return (x + 8) >> 3;
282+
}
283+
267284
} // namespace video::yuv

YUViewLib/src/video/yuv/PixelFormatYUVGuess.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ std::vector<unsigned> getDetectionBitDepthList(const std::optional<unsigned> &de
6666
{
6767
if (detectedBitrate)
6868
return {*detectedBitrate};
69-
return {10u, 12u, 14u, 16u, 8u};
69+
return BitDepthList;
7070
}
7171

7272
std::vector<Subsampling> getDetectionSubsamplingList(Subsampling subsamplingToForceAsFirst,

0 commit comments

Comments
 (0)