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") | ||||
|  | ||||
|  | ||||
| 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. | ||||
| 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_) | ||||
|  | ||||
|   | ||||
							
								
								
									
										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"; | ||||
|  | ||||
| void ImageDecoder::set_size(int width, int height) { | ||||
|   this->image_->resize_(width, height); | ||||
| bool ImageDecoder::set_size(int width, int height) { | ||||
|   bool resized = this->image_->resize_(width, height); | ||||
|   this->x_scale_ = static_cast<double>(this->image_->buffer_width_) / width; | ||||
|   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) { | ||||
|   | ||||
| @@ -4,6 +4,12 @@ | ||||
| namespace esphome { | ||||
| namespace online_image { | ||||
|  | ||||
| enum DecodeError : int { | ||||
|   DECODE_ERROR_INVALID_TYPE = -1, | ||||
|   DECODE_ERROR_UNSUPPORTED_FORMAT = -2, | ||||
|   DECODE_ERROR_OUT_OF_MEMORY = -3, | ||||
| }; | ||||
|  | ||||
| class OnlineImage; | ||||
|  | ||||
| /** | ||||
| @@ -45,8 +51,9 @@ class ImageDecoder { | ||||
|    * | ||||
|    * @param width The image's width. | ||||
|    * @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. | ||||
|   | ||||
| @@ -10,6 +10,10 @@ static const char *const TAG = "online_image"; | ||||
| #include "png_image.h" | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_ONLINE_IMAGE_BMP_SUPPORT | ||||
| #include "bmp_image.h" | ||||
| #endif | ||||
|  | ||||
| namespace esphome { | ||||
| namespace online_image { | ||||
|  | ||||
| @@ -120,9 +124,14 @@ void OnlineImage::update() { | ||||
|  | ||||
| #ifdef USE_ONLINE_IMAGE_PNG_SUPPORT | ||||
|   if (this->format_ == ImageFormat::PNG) { | ||||
|     this->decoder_ = esphome::make_unique<PngDecoder>(this); | ||||
|     this->decoder_ = make_unique<PngDecoder>(this); | ||||
|   } | ||||
| #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_) { | ||||
|     ESP_LOGE(TAG, "Could not instantiate decoder. Image format unsupported."); | ||||
|   | ||||
| @@ -1,10 +1,10 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "esphome/components/http_request/http_request.h" | ||||
| #include "esphome/components/image/image.h" | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/core/defines.h" | ||||
| #include "esphome/core/helpers.h" | ||||
| #include "esphome/components/http_request/http_request.h" | ||||
| #include "esphome/components/image/image.h" | ||||
|  | ||||
| #include "image_decoder.h" | ||||
|  | ||||
| @@ -27,6 +27,8 @@ enum ImageFormat { | ||||
|   JPEG, | ||||
|   /** PNG format. */ | ||||
|   PNG, | ||||
|   /** BMP format. */ | ||||
|   BMP, | ||||
| }; | ||||
|  | ||||
| /** | ||||
| @@ -146,7 +148,7 @@ class OnlineImage : public PollingComponent, | ||||
|    */ | ||||
|   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); | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -60,6 +60,7 @@ | ||||
| #define USE_NETWORK | ||||
| #define USE_NEXTION_TFT_UPLOAD | ||||
| #define USE_NUMBER | ||||
| #define USE_ONLINE_IMAGE_BMP_SUPPORT | ||||
| #define USE_ONLINE_IMAGE_PNG_SUPPORT | ||||
| #define USE_OTA | ||||
| #define USE_OTA_PASSWORD | ||||
|   | ||||
| @@ -26,6 +26,10 @@ online_image: | ||||
|     format: PNG | ||||
|     type: RGB | ||||
|     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 | ||||
| esphome: | ||||
|   | ||||
		Reference in New Issue
	
	Block a user