Skip to content

Commit f9bc50c

Browse files
author
Christian Feldmann
committed
Basic rendering of RGB565 values works.
1 parent 0a9f397 commit f9bc50c

File tree

7 files changed

+205
-75
lines changed

7 files changed

+205
-75
lines changed

YUViewLib/src/playlistitem/playlistItemRawFile.cpp

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -55,17 +55,17 @@ namespace
5555
{
5656

5757
constexpr auto YUV_EXTENSIONS = {"yuv", "nv12", "y4m"};
58-
constexpr auto RGB_EXTENSIONS = {"rgb", "gbr", "bgr", "brg"};
58+
constexpr auto RGB_EXTENSIONS = {"rgb", "gbr", "bgr", "brg", "rgb565", "rgb565be"};
5959
constexpr auto RGBA_EXTENSIONS = {"rgba", "gbra", "bgra", "brga", "argb", "agbr", "abgr", "abrg"};
6060
constexpr auto RAW_BAYER_EXTENSIONS = {"raw"};
6161
constexpr auto CMYK_EXTENSIONS = {"cmyk"};
6262

6363
bool isInExtensions(const QString &testValue, const std::initializer_list<const char *> &extensions)
6464
{
6565
const auto it =
66-
std::find_if(extensions.begin(),
67-
extensions.end(),
68-
[testValue](const char *extension) { return QString(extension) == testValue; });
66+
std::find_if(extensions.begin(),
67+
extensions.end(),
68+
[testValue](const char *extension) { return QString(extension) == testValue; });
6969
return it != extensions.end();
7070
}
7171

@@ -206,7 +206,7 @@ InfoData playlistItemRawFile::getInfo() const
206206
info.items.append(infoItem);
207207

208208
const auto nrFrames =
209-
(this->properties().startEndRange.second - this->properties().startEndRange.first + 1);
209+
(this->properties().startEndRange.second - this->properties().startEndRange.first + 1);
210210
info.items.append(InfoItem("Num Frames", std::to_string(nrFrames)));
211211
info.items.append(InfoItem("Bytes per Frame", std::to_string(this->video->getBytesPerFrame())));
212212

@@ -221,7 +221,7 @@ InfoData playlistItemRawFile::getInfo() const
221221
{
222222
if ((*fileSize % bpf) != 0)
223223
info.items.append(InfoItem(
224-
"Warning"sv, "The file size and the given video size and/or raw format do not match."));
224+
"Warning"sv, "The file size and the given video size and/or raw format do not match."));
225225
}
226226
else
227227
info.items.append(InfoItem("Warning"sv, "Could not obtain file size from input."));
@@ -251,7 +251,7 @@ bool playlistItemRawFile::parseY4MFile()
251251
unsigned width = 0;
252252
unsigned height = 0;
253253
auto format =
254-
video::yuv::PixelFormatYUV(video::yuv::Subsampling::YUV_420, 8, video::yuv::PlaneOrder::YUV);
254+
video::yuv::PixelFormatYUV(video::yuv::Subsampling::YUV_420, 8, video::yuv::PlaneOrder::YUV);
255255

256256
while (rawData.at(offset++) == ' ')
257257
{
@@ -389,7 +389,7 @@ bool playlistItemRawFile::parseY4MFile()
389389

390390
if (width == 0 || height == 0)
391391
return setError(
392-
"Error parsing the Y4M header: The size could not be obtained from the header.");
392+
"Error parsing the Y4M header: The size could not be obtained from the header.");
393393

394394
// Next, all frames should follow. Each frame starts with the sequence 'FRAME', followed by a set
395395
// of paramters for the frame. The 'FRAME' indicator is terminated by a 0x0A. The list of
@@ -452,7 +452,7 @@ bool playlistItemRawFile::parseY4MFile()
452452
void playlistItemRawFile::setFormatFromFileName()
453453
{
454454
const auto fileInfoForGuess = filesource::frameFormatGuess::getFileInfoForGuessFromPath(
455-
this->dataSource.getAbsoluteFilePath());
455+
this->dataSource.getAbsoluteFilePath());
456456

457457
const auto frameFormat = filesource::frameFormatGuess::guessFrameFormat(fileInfoForGuess);
458458

@@ -496,7 +496,7 @@ void playlistItemRawFile::savePlaylist(QDomElement &root, const QDir &playlistDi
496496
QUrl fileURL(QString::fromStdString(dataSource.getAbsoluteFilePath()));
497497
fileURL.setScheme("file");
498498
auto relativePath =
499-
playlistDir.relativeFilePath(QString::fromStdString(dataSource.getAbsoluteFilePath()));
499+
playlistDir.relativeFilePath(QString::fromStdString(dataSource.getAbsoluteFilePath()));
500500

501501
auto d = YUViewDomElement(root.ownerDocument().createElement("playlistItemRawFile"));
502502

@@ -523,7 +523,7 @@ playlistItemRawFile *playlistItemRawFile::newplaylistItemRawFile(const YUViewDom
523523

524524
// check if file with absolute path exists, otherwise check relative path
525525
const auto filePath =
526-
functions::getAbsPathFromAbsAndRel(playlistFilePath, absolutePath, relativePath);
526+
functions::getAbsPathFromAbsAndRel(playlistFilePath, absolutePath, relativePath);
527527
if (filePath.isEmpty())
528528
return nullptr;
529529

@@ -583,7 +583,7 @@ void playlistItemRawFile::getSupportedFileExtensions(QStringList &allExtensions,
583583
allExtensions.append(QString(extension));
584584

585585
filters.append("Raw YUV File (*.yuv *.nv21)");
586-
filters.append("Raw RGB File (*.rgb *.rbg *.grb *.gbr *.brg *.bgr)");
586+
filters.append("Raw RGB File (*.rgb *.rbg *.grb *.gbr *.brg *.bgr, *.rgb565, *.rgb565be)");
587587
filters.append("Raw RGBA File (*.rgba *.rbga *.grba *.gbra *.brga *.bgra *.argb *.arbg *.agrb "
588588
"*.agbr *.abrg *.abgr)");
589589
filters.append("YUV4MPEG2 File (*.y4m)");

YUViewLib/src/video/PixelFormat.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,12 @@ enum class DataLayout
6363
constexpr EnumMapper<DataLayout, 2> DataLayoutMapper = {
6464
std::make_pair(DataLayout::Packed, "Packed"), std::make_pair(DataLayout::Planar, "Planar")};
6565

66+
enum class TextRendering
67+
{
68+
White,
69+
Black
70+
};
71+
6672
} // namespace video
6773

6874
Q_DECLARE_METATYPE(video::DataLayout);

YUViewLib/src/video/rgb/ConversionRGB.cpp

Lines changed: 115 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,77 @@ int getOffsetToFirstByteOfComponent(const Channel channel,
6767
return offset;
6868
}
6969

70+
using RGBTuple = std::tuple<int, int, int>;
71+
RGBTuple extractRGB565Value(const unsigned char *data, const Endianness endianess)
72+
{
73+
int byte1 = *data;
74+
int byte2 = *(data + 1);
75+
76+
if (endianess == Endianness::Big)
77+
std::swap(byte1, byte2);
78+
79+
const auto value = byte1 + (byte2 << 8);
80+
81+
int r = ((value & 0b00000000'00011111) << 3);
82+
int g = ((value & 0b00000111'11100000) >> 3);
83+
int b = ((value & 0b11111000'00000000) >> 8);
84+
85+
return {r, g, b};
86+
}
87+
88+
void convertRGB565ToARGB(const QByteArray &sourceBuffer,
89+
const PixelFormatRGB &srcPixelFormat,
90+
unsigned char *targetBuffer,
91+
const Size frameSize,
92+
const bool componentInvert[4],
93+
const int componentScale[4],
94+
const bool limitedRange)
95+
{
96+
auto rawData = reinterpret_cast<const unsigned char *>(sourceBuffer.data());
97+
98+
const auto endianness = srcPixelFormat.getEndianess();
99+
for (unsigned i = 0; i < frameSize.width * frameSize.height; i++)
100+
{
101+
int byte1 = *rawData;
102+
int byte2 = *(rawData + 1);
103+
104+
if (endianness == Endianness::Big)
105+
std::swap(byte1, byte2);
106+
107+
const auto value = byte1 + (byte2 << 8);
108+
109+
int r = ((value & 0b00000000'00011111) << 3);
110+
int g = ((value & 0b00000111'11100000) >> 3);
111+
int b = ((value & 0b11111000'00000000) >> 8);
112+
113+
r = functions::clip(r * componentScale[0], 0, 255);
114+
g = functions::clip(g * componentScale[1], 0, 255);
115+
b = functions::clip(b * componentScale[2], 0, 255);
116+
117+
if (componentInvert[0])
118+
r = (255 - r);
119+
if (componentInvert[1])
120+
g = (255 - g);
121+
if (componentInvert[2])
122+
b = (255 - b);
123+
124+
if (limitedRange)
125+
{
126+
r = LimitedRangeToFullRange.at(r);
127+
g = LimitedRangeToFullRange.at(g);
128+
b = LimitedRangeToFullRange.at(b);
129+
}
130+
131+
targetBuffer[0] = r;
132+
targetBuffer[1] = g;
133+
targetBuffer[2] = b;
134+
targetBuffer[3] = 255;
135+
136+
rawData += 2;
137+
targetBuffer += 4;
138+
}
139+
}
140+
70141
// Convert the input format to the output RGBA format. Apply inversion, scaling,
71142
// limited range conversion and alpha multiplication. The input can be any supported
72143
// format. The output is always 8 bit ARGB little endian.
@@ -103,10 +174,10 @@ void convertRGBToARGB(const QByteArray &sourceBuffer,
103174
srcA = ((InValueType)sourceBuffer.data()) + offsetA;
104175
}
105176

177+
const auto isBigEndian = bitDepth > 8 && srcPixelFormat.getEndianess() == Endianness::Big;
106178
for (unsigned i = 0; i < frameSize.width * frameSize.height; i++)
107179
{
108-
const auto isBigEndian = bitDepth > 8 && srcPixelFormat.getEndianess() == Endianness::Big;
109-
auto convertValue =
180+
auto convertValue =
110181
[&isBigEndian, &rightShift](const InValueType sourceData, const int scale, const bool invert)
111182
{
112183
auto value = static_cast<int64_t>(sourceData[0]);
@@ -205,6 +276,30 @@ void convertRGBPlaneToARGB(const QByteArray &sourceBuffer,
205276
}
206277
}
207278

279+
rgba_t getPixelValueForPredefiendFormat(const QByteArray &sourceBuffer,
280+
const PixelFormatRGB &srcPixelFormat,
281+
const Size frameSize,
282+
const QPoint &pixelPos)
283+
{
284+
const auto offsetPixelPos = frameSize.width * pixelPos.y() + pixelPos.x();
285+
const auto rawData =
286+
reinterpret_cast<const unsigned char *>(sourceBuffer.data() + offsetPixelPos * 2);
287+
288+
int byte1 = *rawData;
289+
int byte2 = *(rawData + 1);
290+
291+
if (srcPixelFormat.getEndianess() == Endianness::Big)
292+
std::swap(byte1, byte2);
293+
294+
const auto value = byte1 + (byte2 << 8);
295+
296+
int r = ((value & 0b00000000'00011111));
297+
int g = ((value & 0b00000111'11100000) >> 5);
298+
int b = ((value & 0b11111000'00000000) >> 11);
299+
300+
return {static_cast<unsigned>(r), static_cast<unsigned>(g), static_cast<unsigned>(b), 255};
301+
}
302+
208303
template <int bitDepth>
209304
rgba_t getPixelValue(const QByteArray &sourceBuffer,
210305
const PixelFormatRGB &srcPixelFormat,
@@ -251,10 +346,16 @@ void convertInputRGBToARGB(const QByteArray &sourceBuffer,
251346
const bool premultiplyAlpha)
252347
{
253348
const auto bitsPerComponent = srcPixelFormat.getBitsPerComponent();
254-
if (bitsPerComponent < 8 || bitsPerComponent > 32)
255-
throw std::invalid_argument("Invalid bit depth in pixel format for conversion");
256349

257-
if (bitsPerComponent == 8)
350+
if (srcPixelFormat.getPredefinedPixelFormat())
351+
convertRGB565ToARGB(sourceBuffer,
352+
srcPixelFormat,
353+
targetBuffer,
354+
frameSize,
355+
componentInvert,
356+
componentScale,
357+
limitedRange);
358+
else if (bitsPerComponent == 8)
258359
convertRGBToARGB<8>(sourceBuffer,
259360
srcPixelFormat,
260361
targetBuffer,
@@ -274,7 +375,7 @@ void convertInputRGBToARGB(const QByteArray &sourceBuffer,
274375
limitedRange,
275376
outputHasAlpha,
276377
premultiplyAlpha);
277-
else
378+
else if (bitsPerComponent <= 32)
278379
convertRGBToARGB<32>(sourceBuffer,
279380
srcPixelFormat,
280381
targetBuffer,
@@ -284,6 +385,8 @@ void convertInputRGBToARGB(const QByteArray &sourceBuffer,
284385
limitedRange,
285386
outputHasAlpha,
286387
premultiplyAlpha);
388+
else
389+
throw std::invalid_argument("Unable to perform conversion");
287390
}
288391

289392
void convertSinglePlaneOfRGBToGreyscaleARGB(const QByteArray &sourceBuffer,
@@ -334,15 +437,17 @@ rgba_t getPixelValueFromBuffer(const QByteArray &sourceBuffer,
334437
const QPoint &pixelPos)
335438
{
336439
const auto bitsPerComponent = srcPixelFormat.getBitsPerComponent();
337-
if (bitsPerComponent < 8 || bitsPerComponent > 32)
338-
throw std::invalid_argument("Invalid bit depth in pixel format for conversion");
339440

340-
if (bitsPerComponent == 8)
441+
if (srcPixelFormat.getPredefinedPixelFormat())
442+
return getPixelValueForPredefiendFormat(sourceBuffer, srcPixelFormat, frameSize, pixelPos);
443+
else if (bitsPerComponent == 8)
341444
return getPixelValue<8>(sourceBuffer, srcPixelFormat, frameSize, pixelPos);
342445
else if (bitsPerComponent <= 16)
343446
return getPixelValue<16>(sourceBuffer, srcPixelFormat, frameSize, pixelPos);
344-
else
447+
else if (bitsPerComponent <= 32)
345448
return getPixelValue<32>(sourceBuffer, srcPixelFormat, frameSize, pixelPos);
449+
450+
throw std::invalid_argument("Unable to perform conversion");
346451
}
347452

348453
} // namespace video::rgb

YUViewLib/src/video/rgb/PixelFormatRGB.cpp

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -61,16 +61,12 @@ PixelFormatRGB::PixelFormatRGB(const std::string &name)
6161
if (name == UNKNOWN_FORMAT_NAME)
6262
return;
6363

64-
if (name.substr(0, 8) == "RGB565BE")
65-
{
66-
this->predefinedPixelFormat = PredefinedPixelFormat::RGB565BE;
67-
return;
68-
}
69-
if (name.substr(0, 6) == "RGB565")
70-
{
71-
this->predefinedPixelFormat = PredefinedPixelFormat::RGB565;
72-
return;
73-
}
64+
for (const auto predefinedFormat : PredefinedPixelFormatMapper)
65+
if (name == predefinedFormat.second)
66+
{
67+
this->predefinedPixelFormat = predefinedFormat.first;
68+
return;
69+
}
7470

7571
auto channelOrderString = name.substr(0, 3);
7672
if (name[0] == 'a' || name[0] == 'A')
@@ -122,10 +118,8 @@ std::string PixelFormatRGB::getName() const
122118
if (!this->isValid())
123119
return UNKNOWN_FORMAT_NAME;
124120

125-
if (this->predefinedPixelFormat == PredefinedPixelFormat::RGB565)
126-
return "RGB565";
127-
if (this->predefinedPixelFormat == PredefinedPixelFormat::RGB565BE)
128-
return "RGB565BE";
121+
if (this->predefinedPixelFormat)
122+
return std::string(PredefinedPixelFormatMapper.getName(*this->predefinedPixelFormat));
129123

130124
std::string name;
131125
if (this->alphaMode == AlphaMode::First)
@@ -303,6 +297,28 @@ Channel PixelFormatRGB::getChannelAtPosition(int position) const
303297
throw std::invalid_argument("Invalid argument for channel position");
304298
}
305299

300+
TextRendering PixelFormatRGB::getPixelValueTextRendering(rgba_t value) const
301+
{
302+
// Shift the values to 8 bit
303+
if (this->predefinedPixelFormat)
304+
{
305+
value.R = (value.R << 3);
306+
value.G = (value.G << 2);
307+
value.B = (value.B << 3);
308+
}
309+
else if (this->bitsPerComponent > 8)
310+
{
311+
const auto shift = (this->bitsPerComponent - 8);
312+
value.R = (value.R >> shift);
313+
value.G = (value.G >> shift);
314+
value.B = (value.B >> shift);
315+
}
316+
317+
// Approximation of Y = 0.375 R + 0.5 G + 0.125 B to be closer to the percieved brightness.
318+
const auto luminance = (3 * value.R + 4 * value.B + value.B) >> 3;
319+
return luminance < 128 ? TextRendering::White : TextRendering::Black;
320+
}
321+
306322
bool PixelFormatRGB::operator==(const PixelFormatRGB &a) const
307323
{
308324
if (!this->isValid() || !a.isValid())

YUViewLib/src/video/rgb/PixelFormatRGB.h

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,10 @@ enum class PredefinedPixelFormat
119119
RGB565BE, // 16 bits packed as R:5, G:6, B:5 Big Endian
120120
};
121121

122+
constexpr EnumMapper<PredefinedPixelFormat, 2> PredefinedPixelFormatMapper = {
123+
std::make_pair(PredefinedPixelFormat::RGB565, "RGB565"),
124+
std::make_pair(PredefinedPixelFormat::RGB565BE, "RGB565BE")};
125+
122126
enum class ChannelOrder
123127
{
124128
RGB,
@@ -172,10 +176,11 @@ class PixelFormatRGB
172176
[[nodiscard]] Endianness getEndianess() const;
173177
[[nodiscard]] std::optional<PredefinedPixelFormat> getPredefinedPixelFormat() const;
174178

175-
[[nodiscard]] int getNrChannels() const;
176-
[[nodiscard]] int getBytesPerFrame(const Size frameSize) const;
177-
[[nodiscard]] int getChannelPosition(const Channel channel) const;
178-
[[nodiscard]] Channel getChannelAtPosition(const int position) const;
179+
[[nodiscard]] int getNrChannels() const;
180+
[[nodiscard]] int getBytesPerFrame(const Size frameSize) const;
181+
[[nodiscard]] int getChannelPosition(const Channel channel) const;
182+
[[nodiscard]] Channel getChannelAtPosition(const int position) const;
183+
[[nodiscard]] TextRendering getPixelValueTextRendering(rgba_t value) const;
179184

180185
bool operator==(const PixelFormatRGB &a) const;
181186
bool operator!=(const PixelFormatRGB &a) const;

0 commit comments

Comments
 (0)