10#include "TinyGPU/Vector.h"
15
16
17
18
19
20
21template <
typename RGB_T =
RGB565>
47 if (data ==
nullptr || length == 0 || status_ != Status::Collecting) {
51 buffer_.insert(buffer_.end(), data, data + length);
58 return write(
static_cast<
const uint8_t*>(data), length);
114 if (buffer_.size() < 26) {
124 if (requiredSize == 0 || buffer_.size() < requiredSize) {
136 if (buffer_.size() < 54) {
140 if (readU16(0) != 0x4D42) {
145 header.fileSize = readU32(2);
146 header.pixelOffset = readU32(10);
147 header.dibHeaderSize = readU32(14);
148 if (header.dibHeaderSize < 40) {
153 if (buffer_.size() < 14 + header.dibHeaderSize) {
157 header.width = readS32(18);
158 header.height = readS32(22);
159 header.planes = readU16(26);
160 header.bitsPerPixel = readU16(28);
161 header.compression = readU32(30);
162 header.imageSize = readU32(34);
163 header.colorsUsed = readU32(46);
165 if (header.planes != 1) {
169 if (header.width <= 0 || header.height == 0) {
174 if (header.bitsPerPixel != 8 && header.bitsPerPixel != 16 &&
175 header.bitsPerPixel != 24 && header.bitsPerPixel != 32) {
187 imageWidth_ =
static_cast<size_t>(header.width);
189 static_cast<size_t>(topDown_ ? -header.height : header.height);
198 if (header.bitsPerPixel == 16 || header.bitsPerPixel == 32) {
200 const size_t maskOffset =
201 14 + (header.dibHeaderSize >= 52 ? 40 : header.dibHeaderSize);
202 const size_t requiredMasks = header.dibHeaderSize >= 56 ? 16 : 12;
203 if (buffer_.size() < maskOffset + requiredMasks) {
207 header.redMask = readU32(maskOffset);
208 header.greenMask = readU32(maskOffset + 4);
209 header.blueMask = readU32(maskOffset + 8);
210 if (requiredMasks == 16) {
211 header.alphaMask = readU32(maskOffset + 12);
213 }
else if (header.bitsPerPixel == 16) {
214 header.redMask = 0x7C00;
215 header.greenMask = 0x03E0;
216 header.blueMask = 0x001F;
218 header.redMask = 0x00FF0000;
219 header.greenMask = 0x0000FF00;
220 header.blueMask = 0x000000FF;
229 if (header.bitsPerPixel != 8) {
233 const uint32_t paletteEntryCount =
234 header.colorsUsed != 0 ? header.colorsUsed : 256;
235 const size_t paletteOffset = 14 + header.dibHeaderSize;
236 const size_t paletteSize =
static_cast<size_t>(paletteEntryCount) * 4;
237 if (buffer_.size() < paletteOffset + paletteSize) {
241 palette_.reserve(paletteEntryCount);
242 for (uint32_t index = 0; index < paletteEntryCount; ++index) {
243 const size_t entryOffset =
244 paletteOffset + (
static_cast<size_t>(index) * 4);
245 palette_.push_back(RGB565(buffer_[entryOffset + 2],
246 buffer_[entryOffset + 1],
247 buffer_[entryOffset]));
253 const uint32_t rowStride = bmpRowStride(header.bitsPerPixel, imageWidth_);
254 const uint32_t computedImageSize =
255 rowStride *
static_cast<uint32_t>(imageHeight_);
256 const uint32_t pixelBytes =
257 header.imageSize != 0 ? header.imageSize : computedImageSize;
258 const uint32_t computedSize = header.pixelOffset + pixelBytes;
260 if (header.fileSize != 0) {
261 return std::max(header.fileSize, computedSize);
267 if (header.pixelOffset >= buffer_.size()) {
272 target_.resize(imageWidth_, imageHeight_);
273 const uint32_t rowStride = bmpRowStride(header.bitsPerPixel, imageWidth_);
274 for (size_t row = 0; row < imageHeight_; ++row) {
275 const size_t sourceRow = topDown_ ? row : (imageHeight_ - 1 - row);
276 const size_t rowOffset = header.pixelOffset + (sourceRow * rowStride);
277 if (rowOffset + rowStride > buffer_.size()) {
278 setError(
"Incomplete BMP pixel data");
282 if (!decodeRow(header, rowOffset, row)) {
291 switch (header.bitsPerPixel) {
293 return decode8BitRow(rowOffset, targetY);
295 return decode16BitRow(header, rowOffset, targetY);
297 return decode24BitRow(rowOffset, targetY);
299 return decode32BitRow(header, rowOffset, targetY);
307 if (palette_.empty()) {
312 for (size_t x = 0; x < imageWidth_; ++x) {
313 const uint8_t paletteIndex = buffer_[rowOffset + x];
314 if (paletteIndex >= palette_.size()) {
315 setError(
"BMP palette index out of range");
318 target_.setPixel(x, targetY, palette_[paletteIndex]);
325 for (size_t x = 0; x < imageWidth_; ++x) {
326 const uint16_t pixelValue =
327 static_cast<uint16_t>(buffer_[rowOffset + (x * 2)]) |
328 (
static_cast<uint16_t>(buffer_[rowOffset + (x * 2) + 1]) << 8);
329 target_.setPixel(x, targetY, decodeMaskedPixel(pixelValue, header));
335 for (size_t x = 0; x < imageWidth_; ++x) {
336 const size_t pixelOffset = rowOffset + (x * 3);
337 target_.setPixel(x, targetY,
338 RGB565(buffer_[pixelOffset + 2],
339 buffer_[pixelOffset + 1], buffer_[pixelOffset]));
346 for (size_t x = 0; x < imageWidth_; ++x) {
347 const size_t pixelOffset = rowOffset + (x * 4);
348 const uint32_t pixelValue =
349 static_cast<uint32_t>(buffer_[pixelOffset]) |
350 (
static_cast<uint32_t>(buffer_[pixelOffset + 1]) << 8) |
351 (
static_cast<uint32_t>(buffer_[pixelOffset + 2]) << 16) |
352 (
static_cast<uint32_t>(buffer_[pixelOffset + 3]) << 24);
353 target_.setPixel(x, targetY, decodeMaskedPixel(pixelValue, header));
373 while (((mask >> shift) & 0x1U) == 0U) {
376 while (((mask >> (shift + bits)) & 0x1U) != 0U) {
380 const uint32_t value = (pixelValue & mask) >> shift;
381 const uint32_t maxValue = (1UL << bits) - 1UL;
382 return maxValue == 0 ? 0 :
static_cast<uint8_t>((value * 255UL) / maxValue);
386 const uint32_t bitsPerRow =
static_cast<uint32_t>(width) * bitsPerPixel;
387 return ((bitsPerRow + 31U) / 32U) * 4U;
391 return static_cast<uint16_t>(buffer_[offset]) |
392 (
static_cast<uint16_t>(buffer_[offset + 1]) << 8);
396 return static_cast<uint32_t>(buffer_[offset]) |
397 (
static_cast<uint32_t>(buffer_[offset + 1]) << 8) |
398 (
static_cast<uint32_t>(buffer_[offset + 2]) << 16) |
399 (
static_cast<uint32_t>(buffer_[offset + 3]) << 24);
403 return static_cast<int32_t>(readU32(offset));
Incremental BMP decoder for TinyGPU framebuffers.
Definition: BMPParser.h:22
int32_t readS32(size_t offset) const
Definition: BMPParser.h:402
static uint8_t extractChannel(uint32_t pixelValue, uint32_t mask)
Definition: BMPParser.h:366
bool decode32BitRow(const HeaderInfo &header, size_t rowOffset, size_t targetY)
Definition: BMPParser.h:344
size_t write(const uint8_t *data, size_t length)
Writes BMP data incrementally to the parser.
Definition: BMPParser.h:46
bool topDown_
Definition: BMPParser.h:105
bool parseBitMasks(HeaderInfo &header)
Definition: BMPParser.h:197
bool decode8BitRow(size_t rowOffset, size_t targetY)
Definition: BMPParser.h:306
bool isComplete() const
Returns true when the full image has been decoded.
Definition: BMPParser.h:62
Vector< RGB_T > palette_
Definition: BMPParser.h:100
Status
Represents the parser status.
Definition: BMPParser.h:25
size_t write(uint8_t *data, size_t length)
Writes BMP data incrementally to the parser.
Definition: BMPParser.h:57
void tryDecode()
Definition: BMPParser.h:110
bool parseHeader(HeaderInfo &header)
Definition: BMPParser.h:135
size_t imageHeight_
Definition: BMPParser.h:104
bool hasError() const
Returns true when parsing failed.
Definition: BMPParser.h:65
uint32_t readU32(size_t offset) const
Definition: BMPParser.h:395
size_t imageWidth_
Definition: BMPParser.h:103
void setError(const char *message)
Definition: BMPParser.h:406
size_t width() const
Returns the decoded image width in pixels.
Definition: BMPParser.h:71
uint32_t requiredDataSize(const HeaderInfo &header) const
Definition: BMPParser.h:252
Vector< uint8_t > buffer_
Definition: BMPParser.h:99
bool decode16BitRow(const HeaderInfo &header, size_t rowOffset, size_t targetY)
Definition: BMPParser.h:323
static constexpr uint32_t kCompressionBitfields
Definition: BMPParser.h:108
Status status_
Definition: BMPParser.h:101
const char * errorMessage_
Definition: BMPParser.h:102
static uint32_t bmpRowStride(uint16_t bitsPerPixel, size_t width)
Definition: BMPParser.h:385
bool parsePalette(const HeaderInfo &header)
Definition: BMPParser.h:227
ISurface< RGB_T > & target_
Definition: BMPParser.h:98
bool decode24BitRow(size_t rowOffset, size_t targetY)
Definition: BMPParser.h:334
bool decode(const HeaderInfo &header)
Definition: BMPParser.h:266
void reset()
Resets the parser to decode a new BMP image.
Definition: BMPParser.h:35
RGB565 decodeMaskedPixel(uint32_t pixelValue, const HeaderInfo &header) const
Definition: BMPParser.h:358
Status status() const
Returns the current parser status.
Definition: BMPParser.h:68
size_t height() const
Returns the decoded image height in pixels.
Definition: BMPParser.h:74
const char * errorMessage() const
Returns the latest error message, if any.
Definition: BMPParser.h:77
static constexpr uint32_t kCompressionRgb
Definition: BMPParser.h:107
bool decodeRow(const HeaderInfo &header, size_t rowOffset, size_t targetY)
Definition: BMPParser.h:290
BMPParser(ISurface< RGB_T > &target)
Creates a parser that decodes into the provided framebuffer target.
Definition: BMPParser.h:32
uint16_t readU16(size_t offset) const
Definition: BMPParser.h:390
RGB color stored in 16-bit RGB565 format.
Definition: RGB565.h:13
RGB565(uint8_t r, uint8_t g, uint8_t b)
Creates a color from 8-bit red, green, and blue components.
Definition: RGB565.h:20
Definition: AVIWriter.h:9