ESPHome 2026.4.0-dev
Loading...
Searching...
No Matches
bmp_decoder.cpp
Go to the documentation of this file.
1#include "bmp_decoder.h"
2
3#ifdef USE_RUNTIME_IMAGE_BMP
4
7#include "esphome/core/log.h"
8
9#include <cinttypes>
10
12
13static const char *const TAG = "image_decoder.bmp";
14
15int HOT BmpDecoder::decode(uint8_t *buffer, size_t size) {
16 size_t index = 0;
17 if (this->current_index_ == 0) {
18 if (size <= 14) {
19 return 0; // Need more data for file header
20 }
31 // Check if the file is a BMP image
32 if (buffer[0] != 'B' || buffer[1] != 'M') {
33 ESP_LOGE(TAG, "Not a BMP file");
35 }
36
37 // BMP file contains its own size in the header
38 size_t file_size = encode_uint32(buffer[5], buffer[4], buffer[3], buffer[2]);
39 if (this->expected_size_ == 0) {
40 this->expected_size_ = file_size; // Use file header size if not provided
41 }
42 this->data_offset_ = encode_uint32(buffer[13], buffer[12], buffer[11], buffer[10]);
43
44 this->current_index_ = 14;
45 index = 14;
46 }
47 if (this->current_index_ == 14) {
49 return 0; // Need more data for DIB header and color table
50 }
65 this->width_ = encode_uint32(buffer[21], buffer[20], buffer[19], buffer[18]);
66 this->height_ = encode_uint32(buffer[25], buffer[24], buffer[23], buffer[22]);
67 this->bits_per_pixel_ = encode_uint16(buffer[29], buffer[28]);
68 this->compression_method_ = encode_uint32(buffer[33], buffer[32], buffer[31], buffer[30]);
69 this->image_data_size_ = encode_uint32(buffer[37], buffer[36], buffer[35], buffer[34]);
70 this->color_table_entries_ = encode_uint32(buffer[49], buffer[48], buffer[47], buffer[46]);
71
72 switch (this->bits_per_pixel_) {
73 case 1:
74 this->width_bytes_ = (this->width_ + 7) / 8;
75 this->padding_bytes_ = (4 - (this->width_bytes_ % 4)) % 4;
76 break;
77 case 8: {
78 this->width_bytes_ = this->width_;
79 if (this->color_table_entries_ == 0) {
80 this->color_table_entries_ = 256;
81 } else if (this->color_table_entries_ > 256) {
82 ESP_LOGE(TAG, "Too many color table entries: %" PRIu32, this->color_table_entries_);
84 }
85 size_t header_size = encode_uint32(buffer[17], buffer[16], buffer[15], buffer[14]);
86 size_t offset = 14 + header_size;
87
88 this->color_table_ = std::make_unique<uint32_t[]>(this->color_table_entries_);
89
90 for (size_t i = 0; i < this->color_table_entries_; i++) {
91 this->color_table_[i] = encode_uint32(buffer[offset + i * 4 + 3], buffer[offset + i * 4 + 2],
92 buffer[offset + i * 4 + 1], buffer[offset + i * 4]);
93 }
94
95 this->padding_bytes_ = (4 - (this->width_bytes_ % 4)) % 4;
96
97 break;
98 }
99 case 24:
100 this->width_bytes_ = this->width_ * 3;
101 if (this->width_bytes_ % 4 != 0) {
102 this->padding_bytes_ = 4 - (this->width_bytes_ % 4);
103 this->width_bytes_ += this->padding_bytes_;
104 }
105 break;
106 default:
107 ESP_LOGE(TAG, "Unsupported bits per pixel: %d", this->bits_per_pixel_);
109 }
110
111 if (this->compression_method_ != 0) {
112 ESP_LOGE(TAG, "Unsupported compression method: %" PRIu32, this->compression_method_);
114 }
115
116 if (!this->set_size(this->width_, this->height_)) {
118 }
119 this->current_index_ = this->data_offset_;
120 index = this->data_offset_;
121 }
122 switch (this->bits_per_pixel_) {
123 case 1: {
124 size_t width = static_cast<size_t>(this->width_);
125 while (index < size) {
126 size_t x = this->paint_index_ % width;
127 size_t y = static_cast<size_t>(this->height_ - 1) - (this->paint_index_ / width);
128 size_t remaining_in_row = width - x;
129 uint8_t pixels_in_byte = std::min<size_t>(remaining_in_row, 8);
130 bool end_of_row = remaining_in_row <= 8;
131 size_t needed = 1 + (end_of_row ? this->padding_bytes_ : 0);
132 if (index + needed > size) {
133 this->decoded_bytes_ += index;
134 return index;
135 }
136 uint8_t current_byte = buffer[index];
137 for (uint8_t i = 0; i < pixels_in_byte; i++) {
138 Color c = (current_byte & (1 << (7 - i))) ? display::COLOR_ON : display::COLOR_OFF;
139 this->draw(x + i, y, 1, 1, c);
140 }
141 this->paint_index_ += pixels_in_byte;
142 this->current_index_++;
143 index++;
144 // End of pixel row: skip row padding bytes (4-byte alignment)
145 if (end_of_row && this->padding_bytes_ > 0) {
146 index += this->padding_bytes_;
147 this->current_index_ += this->padding_bytes_;
148 }
149 }
150 break;
151 }
152 case 8: {
153 size_t width = static_cast<size_t>(this->width_);
154 size_t last_col = width - 1;
155 while (index < size) {
156 size_t x = this->paint_index_ % width;
157 size_t y = static_cast<size_t>(this->height_ - 1) - (this->paint_index_ / width);
158 size_t needed = 1 + ((x == last_col) ? this->padding_bytes_ : 0);
159 if (index + needed > size) {
160 this->decoded_bytes_ += index;
161 return index;
162 }
163
164 uint8_t color_index = buffer[index];
165 if (color_index >= this->color_table_entries_) {
166 ESP_LOGE(TAG, "Invalid color index: %u", color_index);
168 }
169
170 uint32_t rgb = this->color_table_[color_index];
171 uint8_t b = rgb & 0xff;
172 uint8_t g = (rgb >> 8) & 0xff;
173 uint8_t r = (rgb >> 16) & 0xff;
174 this->draw(x, y, 1, 1, Color(r, g, b));
175 this->paint_index_++;
176 this->current_index_++;
177 index++;
178 if (x == last_col && this->padding_bytes_ > 0) {
179 index += this->padding_bytes_;
180 this->current_index_ += this->padding_bytes_;
181 }
182 }
183 break;
184 }
185 case 24: {
186 size_t width = static_cast<size_t>(this->width_);
187 size_t last_col = width - 1;
188 while (index < size) {
189 size_t x = this->paint_index_ % width;
190 size_t y = static_cast<size_t>(this->height_ - 1) - (this->paint_index_ / width);
191 size_t needed = 3 + ((x == last_col) ? this->padding_bytes_ : 0);
192 if (index + needed > size) {
193 this->decoded_bytes_ += index;
194 return index;
195 }
196 uint8_t b = buffer[index];
197 uint8_t g = buffer[index + 1];
198 uint8_t r = buffer[index + 2];
199 this->draw(x, y, 1, 1, Color(r, g, b));
200 this->paint_index_++;
201 this->current_index_ += 3;
202 index += 3;
203 if (x == last_col && this->padding_bytes_ > 0) {
204 index += this->padding_bytes_;
205 this->current_index_ += this->padding_bytes_;
206 }
207 }
208 break;
209 }
210 default:
211 ESP_LOGE(TAG, "Unsupported bits per pixel: %d", this->bits_per_pixel_);
213 }
214 this->decoded_bytes_ += size;
215 return size;
216};
217
218} // namespace esphome::runtime_image
219
220#endif // USE_RUNTIME_IMAGE_BMP
std::unique_ptr< uint32_t[]> color_table_
Definition bmp_decoder.h:46
int HOT decode(uint8_t *buffer, size_t size) override
void draw(int x, int y, int w, int h, const Color &color)
Fill a rectangle on the display_buffer using the defined color.
bool set_size(int width, int height)
Request the image to be resized once the actual dimensions are known.
constexpr Color COLOR_ON(255, 255, 255, 255)
Turn the pixel ON.
constexpr Color COLOR_OFF(0, 0, 0, 0)
Turn the pixel OFF.
size_t size
Definition helpers.h:1082
constexpr uint32_t encode_uint32(uint8_t byte1, uint8_t byte2, uint8_t byte3, uint8_t byte4)
Encode a 32-bit value given four bytes in most to least significant byte order.
Definition helpers.h:889
constexpr uint16_t encode_uint16(uint8_t msb, uint8_t lsb)
Encode a 16-bit value given the most and least significant byte.
Definition helpers.h:881
static void uint32_t
uint16_t x
Definition tt21100.cpp:5
uint16_t y
Definition tt21100.cpp:6