#pragma once #include "esphome/core/color.h" 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; /** * @brief Class to abstract decoding different image formats. */ class ImageDecoder { public: /** * @brief Construct a new Image Decoder object * * @param image The image to decode the stream into. */ ImageDecoder(OnlineImage *image) : image_(image) {} virtual ~ImageDecoder() = default; /** * @brief Initialize the decoder. * * @param download_size The total number of bytes that need to be downloaded for the image. * @return int Returns 0 on success, a {@see DecodeError} value in case of an error. */ virtual int prepare(size_t download_size) { this->download_size_ = download_size; return 0; } /** * @brief Decode a part of the image. It will try reading from the buffer. * There is no guarantee that the whole available buffer will be read/decoded; * the method will return the amount of bytes actually decoded, so that the * unread content can be moved to the beginning. * * @param buffer The buffer to read from. * @param size The maximum amount of bytes that can be read from the buffer. * @return int The amount of bytes read. It can be 0 if the buffer does not have enough content to meaningfully * decode anything, or negative in case of a decoding error. */ virtual int decode(uint8_t *buffer, size_t size) = 0; /** * @brief Request the image to be resized once the actual dimensions are known. * Called by the callback functions, to be able to access the parent Image class. * * @param width The image's width. * @param height The image's height. * @return true if the image was resized, false otherwise. */ bool set_size(int width, int height); /** * @brief Fill a rectangle on the display_buffer using the defined color. * Will check the given coordinates for out-of-bounds, and clip the rectangle accordingly. * In case of binary displays, the color will be converted to binary as well. * Called by the callback functions, to be able to access the parent Image class. * * @param x The left-most coordinate of the rectangle. * @param y The top-most coordinate of the rectangle. * @param w The width of the rectangle. * @param h The height of the rectangle. * @param color The fill color */ void draw(int x, int y, int w, int h, const Color &color); bool is_finished() const { return this->decoded_bytes_ == this->download_size_; } protected: OnlineImage *image_; // Initializing to 1, to ensure it is distinguishable from initial "decoded_bytes_". // Will be overwritten anyway once the download size is known. size_t download_size_ = 1; size_t decoded_bytes_ = 0; double x_scale_ = 1.0; double y_scale_ = 1.0; }; class DownloadBuffer { public: DownloadBuffer(size_t size); virtual ~DownloadBuffer() { this->allocator_.deallocate(this->buffer_, this->size_); } uint8_t *data(size_t offset = 0); uint8_t *append() { return this->data(this->unread_); } size_t unread() const { return this->unread_; } size_t size() const { return this->size_; } size_t free_capacity() const { return this->size_ - this->unread_; } size_t read(size_t len); size_t write(size_t len) { this->unread_ += len; return this->unread_; } void reset() { this->unread_ = 0; } size_t resize(size_t size); protected: RAMAllocator allocator_{}; uint8_t *buffer_; size_t size_; /** Total number of downloaded bytes not yet read. */ size_t unread_; }; } // namespace online_image } // namespace esphome