Skip to content

Commit ab6ea9e

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 4b0e543 commit ab6ea9e

5 files changed

Lines changed: 246 additions & 166 deletions

File tree

YUViewLib/src/video/yuv/PixelFormatYUV.cpp

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

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

384384
int64_t bytes = 0;
385+
const auto bytesPerSample = get_min_standard_bytes(this->bitsPerSample); // Round to bytes
385386

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

392-
const auto bytesPerSample = (this->bitsPerSample + 7) / 8; // Round to bytes
393-
bytes += frameSize.width * frameSize.height * bytesPerSample; // Luma plane
393+
bytes += (int64_t)frameSize.width * frameSize.height * bytesPerSample; // Luma plane
394394
if (this->subsampling == Subsampling::YUV_444)
395-
bytes += frameSize.width * frameSize.height * bytesPerSample * 2; // U/V planes
395+
bytes += (int64_t)frameSize.width * frameSize.height * bytesPerSample * 2; // U/V planes
396396
else if (this->subsampling == Subsampling::YUV_422 || this->subsampling == Subsampling::YUV_440)
397-
bytes += (frameSize.width / 2) * frameSize.height * bytesPerSample *
397+
bytes += (int64_t)(frameSize.width / 2) * frameSize.height * bytesPerSample *
398398
2; // U/V planes, half the width
399399
else if (this->subsampling == Subsampling::YUV_420)
400-
bytes += (frameSize.width / 2) * (frameSize.height / 2) * bytesPerSample *
400+
bytes += (int64_t)(frameSize.width / 2) * (frameSize.height / 2) * bytesPerSample *
401401
2; // U/V planes, half the width and height
402402
else if (this->subsampling == Subsampling::YUV_410)
403-
bytes += (frameSize.width / 4) * (frameSize.height / 4) * bytesPerSample *
403+
bytes += (int64_t)(frameSize.width / 4) * (frameSize.height / 4) * bytesPerSample *
404404
2; // U/V planes, half the width and height
405405
else if (this->subsampling == Subsampling::YUV_411)
406-
bytes += (frameSize.width / 4) * frameSize.height * bytesPerSample *
406+
bytes += (int64_t)(frameSize.width / 4) * frameSize.height * bytesPerSample *
407407
2; // U/V planes, quarter the width
408408
else if (this->subsampling == Subsampling::YUV_400)
409409
bytes += 0; // No chroma components
@@ -413,30 +413,29 @@ int64_t PixelFormatYUV::bytesPerFrame(const Size &frameSize) const
413413
if (this->planar &&
414414
(this->planeOrder == PlaneOrder::YUVA || this->planeOrder == PlaneOrder::YVUA))
415415
// There is an additional alpha plane. The alpha plane is not subsampled
416-
bytes += frameSize.width * frameSize.height * bytesPerSample; // Alpha plane
416+
bytes += (int64_t)frameSize.width * frameSize.height * bytesPerSample; // Alpha plane
417417
if (!this->planar && this->subsampling == Subsampling::YUV_444 &&
418418
(this->packingOrder == PackingOrder::AYUV || this->packingOrder == PackingOrder::YUVA ||
419419
this->packingOrder == PackingOrder::VUYA))
420420
// There is an additional alpha plane. The alpha plane is not subsampled
421-
bytes += frameSize.width * frameSize.height * bytesPerSample; // Alpha plane
421+
bytes += (int64_t)frameSize.width * frameSize.height * bytesPerSample; // Alpha plane
422422
}
423423
else
424424
{
425425
// This is a packed format with byte packing
426426
if (this->subsampling == Subsampling::YUV_422)
427427
{
428428
// All packing orders have 4 values per packed value (which has 2 Y samples)
429-
const auto bitsPerPixel = this->bitsPerSample * 4;
430-
return ((bitsPerPixel + 7) / 8) * (frameSize.width / 2) * frameSize.height;
429+
return 4 * bytesPerSample * (frameSize.width / 2) * frameSize.height;
431430
}
432431
// This is a packed format. The added number of bytes might be lower because of the packing.
433432
if (this->subsampling == Subsampling::YUV_444)
434433
{
435-
auto bitsPerPixel = this->bitsPerSample * 3;
434+
auto channels = 3;
436435
if (this->packingOrder == PackingOrder::AYUV || this->packingOrder == PackingOrder::YUVA ||
437436
this->packingOrder == PackingOrder::VUYA)
438-
bitsPerPixel += this->bitsPerSample;
439-
return ((bitsPerPixel + 7) / 8) * frameSize.width * frameSize.height;
437+
channels += 1;
438+
return channels * bytesPerSample * frameSize.width * frameSize.height;
440439
}
441440
// else if (subsampling == Subsampling::YUV_422 || subsampling == Subsampling::YUV_440)
442441
//{

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)