mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 07:03:55 +00:00 
			
		
		
		
	[online_image] Add binary bmp support (#8116)
Co-authored-by: guillempages <guillempages@users.noreply.github.com>
This commit is contained in:
		| @@ -61,8 +61,22 @@ class PNGFormat(Format): | |||||||
|         cg.add_library("pngle", "1.0.2") |         cg.add_library("pngle", "1.0.2") | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class BMPFormat(Format): | ||||||
|  |     def __init__(self): | ||||||
|  |         super().__init__("BMP") | ||||||
|  |  | ||||||
|  |     def actions(self): | ||||||
|  |         cg.add_define("USE_ONLINE_IMAGE_BMP_SUPPORT") | ||||||
|  |  | ||||||
|  |  | ||||||
| # New formats can be added here. | # New formats can be added here. | ||||||
| IMAGE_FORMATS = {x.image_type: x for x in (PNGFormat(),)} | IMAGE_FORMATS = { | ||||||
|  |     x.image_type: x | ||||||
|  |     for x in ( | ||||||
|  |         PNGFormat(), | ||||||
|  |         BMPFormat(), | ||||||
|  |     ) | ||||||
|  | } | ||||||
|  |  | ||||||
| OnlineImage = online_image_ns.class_("OnlineImage", cg.PollingComponent, Image_) | OnlineImage = online_image_ns.class_("OnlineImage", cg.PollingComponent, Image_) | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										101
									
								
								esphome/components/online_image/bmp_image.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								esphome/components/online_image/bmp_image.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,101 @@ | |||||||
|  | #include "bmp_image.h" | ||||||
|  |  | ||||||
|  | #ifdef USE_ONLINE_IMAGE_BMP_SUPPORT | ||||||
|  |  | ||||||
|  | #include "esphome/components/display/display.h" | ||||||
|  | #include "esphome/core/helpers.h" | ||||||
|  | #include "esphome/core/log.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace online_image { | ||||||
|  |  | ||||||
|  | static const char *const TAG = "online_image.bmp"; | ||||||
|  |  | ||||||
|  | int HOT BmpDecoder::decode(uint8_t *buffer, size_t size) { | ||||||
|  |   size_t index = 0; | ||||||
|  |   if (this->current_index_ == 0 && index == 0 && size > 14) { | ||||||
|  |     /** | ||||||
|  |      * BMP file format: | ||||||
|  |      * 0-1: Signature (BM) | ||||||
|  |      * 2-5: File size | ||||||
|  |      * 6-9: Reserved | ||||||
|  |      * 10-13: Pixel data offset | ||||||
|  |      * | ||||||
|  |      * Integer values are stored in little-endian format. | ||||||
|  |      */ | ||||||
|  |  | ||||||
|  |     // Check if the file is a BMP image | ||||||
|  |     if (buffer[0] != 'B' || buffer[1] != 'M') { | ||||||
|  |       ESP_LOGE(TAG, "Not a BMP file"); | ||||||
|  |       return DECODE_ERROR_INVALID_TYPE; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     this->download_size_ = encode_uint32(buffer[5], buffer[4], buffer[3], buffer[2]); | ||||||
|  |     this->data_offset_ = encode_uint32(buffer[13], buffer[12], buffer[11], buffer[10]); | ||||||
|  |  | ||||||
|  |     this->current_index_ = 14; | ||||||
|  |     index = 14; | ||||||
|  |   } | ||||||
|  |   if (this->current_index_ == 14 && index == 14 && size > this->data_offset_) { | ||||||
|  |     /** | ||||||
|  |      * BMP DIB header: | ||||||
|  |      * 14-17: DIB header size | ||||||
|  |      * 18-21: Image width | ||||||
|  |      * 22-25: Image height | ||||||
|  |      * 26-27: Number of color planes | ||||||
|  |      * 28-29: Bits per pixel | ||||||
|  |      * 30-33: Compression method | ||||||
|  |      * 34-37: Image data size | ||||||
|  |      * 38-41: Horizontal resolution | ||||||
|  |      * 42-45: Vertical resolution | ||||||
|  |      * 46-49: Number of colors in the color table | ||||||
|  |      */ | ||||||
|  |  | ||||||
|  |     this->width_ = encode_uint32(buffer[21], buffer[20], buffer[19], buffer[18]); | ||||||
|  |     this->height_ = encode_uint32(buffer[25], buffer[24], buffer[23], buffer[22]); | ||||||
|  |     this->bits_per_pixel_ = encode_uint16(buffer[29], buffer[28]); | ||||||
|  |     this->compression_method_ = encode_uint32(buffer[33], buffer[32], buffer[31], buffer[30]); | ||||||
|  |     this->image_data_size_ = encode_uint32(buffer[37], buffer[36], buffer[35], buffer[34]); | ||||||
|  |     this->color_table_entries_ = encode_uint32(buffer[49], buffer[48], buffer[47], buffer[46]); | ||||||
|  |  | ||||||
|  |     switch (this->bits_per_pixel_) { | ||||||
|  |       case 1: | ||||||
|  |         this->width_bytes_ = (this->width_ % 8 == 0) ? (this->width_ / 8) : (this->width_ / 8 + 1); | ||||||
|  |         break; | ||||||
|  |       default: | ||||||
|  |         ESP_LOGE(TAG, "Unsupported bits per pixel: %d", this->bits_per_pixel_); | ||||||
|  |         return DECODE_ERROR_UNSUPPORTED_FORMAT; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (this->compression_method_ != 0) { | ||||||
|  |       ESP_LOGE(TAG, "Unsupported compression method: %d", this->compression_method_); | ||||||
|  |       return DECODE_ERROR_UNSUPPORTED_FORMAT; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (!this->set_size(this->width_, this->height_)) { | ||||||
|  |       return DECODE_ERROR_OUT_OF_MEMORY; | ||||||
|  |     } | ||||||
|  |     this->current_index_ = this->data_offset_; | ||||||
|  |     index = this->data_offset_; | ||||||
|  |   } | ||||||
|  |   while (index < size) { | ||||||
|  |     size_t paint_index = this->current_index_ - this->data_offset_; | ||||||
|  |  | ||||||
|  |     uint8_t current_byte = buffer[index]; | ||||||
|  |     for (uint8_t i = 0; i < 8; i++) { | ||||||
|  |       size_t x = (paint_index * 8) % this->width_ + i; | ||||||
|  |       size_t y = (this->height_ - 1) - (paint_index / this->width_bytes_); | ||||||
|  |       Color c = (current_byte & (1 << (7 - i))) ? display::COLOR_ON : display::COLOR_OFF; | ||||||
|  |       this->draw(x, y, 1, 1, c); | ||||||
|  |     } | ||||||
|  |     this->current_index_++; | ||||||
|  |     index++; | ||||||
|  |   } | ||||||
|  |   this->decoded_bytes_ += size; | ||||||
|  |   return size; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace online_image | ||||||
|  | }  // namespace esphome | ||||||
|  |  | ||||||
|  | #endif  // USE_ONLINE_IMAGE_BMP_SUPPORT | ||||||
							
								
								
									
										40
									
								
								esphome/components/online_image/bmp_image.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								esphome/components/online_image/bmp_image.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,40 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "esphome/core/defines.h" | ||||||
|  | #ifdef USE_ONLINE_IMAGE_BMP_SUPPORT | ||||||
|  |  | ||||||
|  | #include "image_decoder.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace online_image { | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @brief Image decoder specialization for PNG images. | ||||||
|  |  */ | ||||||
|  | class BmpDecoder : public ImageDecoder { | ||||||
|  |  public: | ||||||
|  |   /** | ||||||
|  |    * @brief Construct a new BMP Decoder object. | ||||||
|  |    * | ||||||
|  |    * @param display The image to decode the stream into. | ||||||
|  |    */ | ||||||
|  |   BmpDecoder(OnlineImage *image) : ImageDecoder(image) {} | ||||||
|  |  | ||||||
|  |   int HOT decode(uint8_t *buffer, size_t size) override; | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   size_t current_index_{0}; | ||||||
|  |   ssize_t width_{0}; | ||||||
|  |   ssize_t height_{0}; | ||||||
|  |   uint16_t bits_per_pixel_{0}; | ||||||
|  |   uint32_t compression_method_{0}; | ||||||
|  |   uint32_t image_data_size_{0}; | ||||||
|  |   uint32_t color_table_entries_{0}; | ||||||
|  |   size_t width_bytes_{0}; | ||||||
|  |   size_t data_offset_{0}; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace online_image | ||||||
|  | }  // namespace esphome | ||||||
|  |  | ||||||
|  | #endif  // USE_ONLINE_IMAGE_BMP_SUPPORT | ||||||
| @@ -8,10 +8,11 @@ namespace online_image { | |||||||
|  |  | ||||||
| static const char *const TAG = "online_image.decoder"; | static const char *const TAG = "online_image.decoder"; | ||||||
|  |  | ||||||
| void ImageDecoder::set_size(int width, int height) { | bool ImageDecoder::set_size(int width, int height) { | ||||||
|   this->image_->resize_(width, height); |   bool resized = this->image_->resize_(width, height); | ||||||
|   this->x_scale_ = static_cast<double>(this->image_->buffer_width_) / width; |   this->x_scale_ = static_cast<double>(this->image_->buffer_width_) / width; | ||||||
|   this->y_scale_ = static_cast<double>(this->image_->buffer_height_) / height; |   this->y_scale_ = static_cast<double>(this->image_->buffer_height_) / height; | ||||||
|  |   return resized; | ||||||
| } | } | ||||||
|  |  | ||||||
| void ImageDecoder::draw(int x, int y, int w, int h, const Color &color) { | void ImageDecoder::draw(int x, int y, int w, int h, const Color &color) { | ||||||
|   | |||||||
| @@ -4,6 +4,12 @@ | |||||||
| namespace esphome { | namespace esphome { | ||||||
| namespace online_image { | namespace online_image { | ||||||
|  |  | ||||||
|  | enum DecodeError : int { | ||||||
|  |   DECODE_ERROR_INVALID_TYPE = -1, | ||||||
|  |   DECODE_ERROR_UNSUPPORTED_FORMAT = -2, | ||||||
|  |   DECODE_ERROR_OUT_OF_MEMORY = -3, | ||||||
|  | }; | ||||||
|  |  | ||||||
| class OnlineImage; | class OnlineImage; | ||||||
|  |  | ||||||
| /** | /** | ||||||
| @@ -45,8 +51,9 @@ class ImageDecoder { | |||||||
|    * |    * | ||||||
|    * @param width The image's width. |    * @param width The image's width. | ||||||
|    * @param height The image's height. |    * @param height The image's height. | ||||||
|  |    * @return true if the image was resized, false otherwise. | ||||||
|    */ |    */ | ||||||
|   void set_size(int width, int height); |   bool set_size(int width, int height); | ||||||
|  |  | ||||||
|   /** |   /** | ||||||
|    * @brief Fill a rectangle on the display_buffer using the defined color. |    * @brief Fill a rectangle on the display_buffer using the defined color. | ||||||
|   | |||||||
| @@ -10,6 +10,10 @@ static const char *const TAG = "online_image"; | |||||||
| #include "png_image.h" | #include "png_image.h" | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | #ifdef USE_ONLINE_IMAGE_BMP_SUPPORT | ||||||
|  | #include "bmp_image.h" | ||||||
|  | #endif | ||||||
|  |  | ||||||
| namespace esphome { | namespace esphome { | ||||||
| namespace online_image { | namespace online_image { | ||||||
|  |  | ||||||
| @@ -120,9 +124,14 @@ void OnlineImage::update() { | |||||||
|  |  | ||||||
| #ifdef USE_ONLINE_IMAGE_PNG_SUPPORT | #ifdef USE_ONLINE_IMAGE_PNG_SUPPORT | ||||||
|   if (this->format_ == ImageFormat::PNG) { |   if (this->format_ == ImageFormat::PNG) { | ||||||
|     this->decoder_ = esphome::make_unique<PngDecoder>(this); |     this->decoder_ = make_unique<PngDecoder>(this); | ||||||
|   } |   } | ||||||
| #endif  // ONLINE_IMAGE_PNG_SUPPORT | #endif  // ONLINE_IMAGE_PNG_SUPPORT | ||||||
|  | #ifdef USE_ONLINE_IMAGE_BMP_SUPPORT | ||||||
|  |   if (this->format_ == ImageFormat::BMP) { | ||||||
|  |     this->decoder_ = make_unique<BmpDecoder>(this); | ||||||
|  |   } | ||||||
|  | #endif  // ONLINE_IMAGE_BMP_SUPPORT | ||||||
|  |  | ||||||
|   if (!this->decoder_) { |   if (!this->decoder_) { | ||||||
|     ESP_LOGE(TAG, "Could not instantiate decoder. Image format unsupported."); |     ESP_LOGE(TAG, "Could not instantiate decoder. Image format unsupported."); | ||||||
|   | |||||||
| @@ -1,10 +1,10 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
|  | #include "esphome/components/http_request/http_request.h" | ||||||
|  | #include "esphome/components/image/image.h" | ||||||
| #include "esphome/core/component.h" | #include "esphome/core/component.h" | ||||||
| #include "esphome/core/defines.h" | #include "esphome/core/defines.h" | ||||||
| #include "esphome/core/helpers.h" | #include "esphome/core/helpers.h" | ||||||
| #include "esphome/components/http_request/http_request.h" |  | ||||||
| #include "esphome/components/image/image.h" |  | ||||||
|  |  | ||||||
| #include "image_decoder.h" | #include "image_decoder.h" | ||||||
|  |  | ||||||
| @@ -27,6 +27,8 @@ enum ImageFormat { | |||||||
|   JPEG, |   JPEG, | ||||||
|   /** PNG format. */ |   /** PNG format. */ | ||||||
|   PNG, |   PNG, | ||||||
|  |   /** BMP format. */ | ||||||
|  |   BMP, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| /** | /** | ||||||
| @@ -146,7 +148,7 @@ class OnlineImage : public PollingComponent, | |||||||
|    */ |    */ | ||||||
|   int buffer_height_; |   int buffer_height_; | ||||||
|  |  | ||||||
|   friend void ImageDecoder::set_size(int width, int height); |   friend bool ImageDecoder::set_size(int width, int height); | ||||||
|   friend void ImageDecoder::draw(int x, int y, int w, int h, const Color &color); |   friend void ImageDecoder::draw(int x, int y, int w, int h, const Color &color); | ||||||
| }; | }; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -60,6 +60,7 @@ | |||||||
| #define USE_NETWORK | #define USE_NETWORK | ||||||
| #define USE_NEXTION_TFT_UPLOAD | #define USE_NEXTION_TFT_UPLOAD | ||||||
| #define USE_NUMBER | #define USE_NUMBER | ||||||
|  | #define USE_ONLINE_IMAGE_BMP_SUPPORT | ||||||
| #define USE_ONLINE_IMAGE_PNG_SUPPORT | #define USE_ONLINE_IMAGE_PNG_SUPPORT | ||||||
| #define USE_OTA | #define USE_OTA | ||||||
| #define USE_OTA_PASSWORD | #define USE_OTA_PASSWORD | ||||||
|   | |||||||
| @@ -26,6 +26,10 @@ online_image: | |||||||
|     format: PNG |     format: PNG | ||||||
|     type: RGB |     type: RGB | ||||||
|     transparency: chroma_key |     transparency: chroma_key | ||||||
|  |   - id: online_binary_bmp | ||||||
|  |     url: https://samples-files.com/samples/images/bmp/480-360-sample.bmp | ||||||
|  |     format: BMP | ||||||
|  |     type: BINARY | ||||||
|  |  | ||||||
| # Check the set_url action | # Check the set_url action | ||||||
| esphome: | esphome: | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user