mirror of
https://github.com/esphome/esphome.git
synced 2025-09-23 21:52:23 +01:00
added webp animation support to online_image
This commit is contained in:
@@ -11,6 +11,9 @@ from esphome.components.image import (
|
|||||||
get_image_type_enum,
|
get_image_type_enum,
|
||||||
get_transparency_enum,
|
get_transparency_enum,
|
||||||
)
|
)
|
||||||
|
from esphome.components.animation import (
|
||||||
|
Animation_,
|
||||||
|
)
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_BUFFER_SIZE,
|
CONF_BUFFER_SIZE,
|
||||||
@@ -25,7 +28,7 @@ from esphome.const import (
|
|||||||
CONF_URL,
|
CONF_URL,
|
||||||
)
|
)
|
||||||
|
|
||||||
AUTO_LOAD = ["image"]
|
AUTO_LOAD = ["image", "animation"]
|
||||||
DEPENDENCIES = ["display", "http_request"]
|
DEPENDENCIES = ["display", "http_request"]
|
||||||
CODEOWNERS = ["@guillempages", "@clydebarrow"]
|
CODEOWNERS = ["@guillempages", "@clydebarrow"]
|
||||||
MULTI_CONF = True
|
MULTI_CONF = True
|
||||||
@@ -68,6 +71,13 @@ class JPEGFormat(Format):
|
|||||||
cg.add_define("USE_ONLINE_IMAGE_JPEG_SUPPORT")
|
cg.add_define("USE_ONLINE_IMAGE_JPEG_SUPPORT")
|
||||||
cg.add_library("JPEGDEC", None, "https://github.com/bitbank2/JPEGDEC#ca1e0f2")
|
cg.add_library("JPEGDEC", None, "https://github.com/bitbank2/JPEGDEC#ca1e0f2")
|
||||||
|
|
||||||
|
class WEBPFormat(Format):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__("WEBP")
|
||||||
|
|
||||||
|
def actions(self):
|
||||||
|
cg.add_define("USE_ONLINE_IMAGE_WEBP_SUPPORT")
|
||||||
|
cg.add_library("libwebp", None, "https://github.com/acvigue/libwebp#26b0c4b")
|
||||||
|
|
||||||
class PNGFormat(Format):
|
class PNGFormat(Format):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@@ -83,12 +93,13 @@ IMAGE_FORMATS = {
|
|||||||
for x in (
|
for x in (
|
||||||
BMPFormat(),
|
BMPFormat(),
|
||||||
JPEGFormat(),
|
JPEGFormat(),
|
||||||
|
WEBPFormat(),
|
||||||
PNGFormat(),
|
PNGFormat(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
IMAGE_FORMATS.update({"JPG": IMAGE_FORMATS["JPEG"]})
|
IMAGE_FORMATS.update({"JPG": IMAGE_FORMATS["JPEG"]})
|
||||||
|
|
||||||
OnlineImage = online_image_ns.class_("OnlineImage", cg.PollingComponent, Image_)
|
OnlineImage = online_image_ns.class_("OnlineImage", cg.PollingComponent, Image_, Animation_)
|
||||||
|
|
||||||
# Actions
|
# Actions
|
||||||
SetUrlAction = online_image_ns.class_(
|
SetUrlAction = online_image_ns.class_(
|
||||||
|
@@ -8,19 +8,19 @@ namespace online_image {
|
|||||||
|
|
||||||
static const char *const TAG = "online_image.decoder";
|
static const char *const TAG = "online_image.decoder";
|
||||||
|
|
||||||
bool ImageDecoder::set_size(int width, int height) {
|
bool ImageDecoder::set_size(int width, int height, int frames) {
|
||||||
bool success = this->image_->resize_(width, height) > 0;
|
bool success = this->image_->resize_(width, height, frames) > 0;
|
||||||
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 success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
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, int frame) {
|
||||||
auto width = std::min(this->image_->buffer_width_, static_cast<int>(std::ceil((x + w) * this->x_scale_)));
|
auto width = std::min(this->image_->buffer_width_, static_cast<int>(std::ceil((x + w) * this->x_scale_)));
|
||||||
auto height = std::min(this->image_->buffer_height_, static_cast<int>(std::ceil((y + h) * this->y_scale_)));
|
auto height = std::min(this->image_->buffer_height_, static_cast<int>(std::ceil((y + h) * this->y_scale_)));
|
||||||
for (int i = x * this->x_scale_; i < width; i++) {
|
for (int i = x * this->x_scale_; i < width; i++) {
|
||||||
for (int j = y * this->y_scale_; j < height; j++) {
|
for (int j = y * this->y_scale_; j < height; j++) {
|
||||||
this->image_->draw_pixel_(i, j, color);
|
this->image_->draw_pixel_(i, j, color, frame);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -55,9 +55,10 @@ 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.
|
||||||
|
* @param frames The number of frames in an image if animated.
|
||||||
* @return true if the image was resized, false otherwise.
|
* @return true if the image was resized, false otherwise.
|
||||||
*/
|
*/
|
||||||
bool set_size(int width, int height);
|
bool set_size(int width, int height, int frames = 1);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Fill a rectangle on the display_buffer using the defined color.
|
* @brief Fill a rectangle on the display_buffer using the defined color.
|
||||||
@@ -70,8 +71,9 @@ class ImageDecoder {
|
|||||||
* @param w The width of the rectangle.
|
* @param w The width of the rectangle.
|
||||||
* @param h The height of the rectangle.
|
* @param h The height of the rectangle.
|
||||||
* @param color The fill color
|
* @param color The fill color
|
||||||
|
* @param frame The frame to write to
|
||||||
*/
|
*/
|
||||||
void draw(int x, int y, int w, int h, const Color &color);
|
void draw(int x, int y, int w, int h, const Color &color, int frame = 0);
|
||||||
|
|
||||||
bool is_finished() const { return this->decoded_bytes_ == this->download_size_; }
|
bool is_finished() const { return this->decoded_bytes_ == this->download_size_; }
|
||||||
|
|
||||||
|
@@ -12,6 +12,9 @@ static const char *const TAG = "online_image";
|
|||||||
#ifdef USE_ONLINE_IMAGE_JPEG_SUPPORT
|
#ifdef USE_ONLINE_IMAGE_JPEG_SUPPORT
|
||||||
#include "jpeg_image.h"
|
#include "jpeg_image.h"
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_ONLINE_IMAGE_WEBP_SUPPORT
|
||||||
|
#include "webp_image.h"
|
||||||
|
#endif
|
||||||
#ifdef USE_ONLINE_IMAGE_PNG_SUPPORT
|
#ifdef USE_ONLINE_IMAGE_PNG_SUPPORT
|
||||||
#include "png_image.h"
|
#include "png_image.h"
|
||||||
#endif
|
#endif
|
||||||
@@ -32,7 +35,7 @@ inline bool is_color_on(const Color &color) {
|
|||||||
|
|
||||||
OnlineImage::OnlineImage(const std::string &url, int width, int height, ImageFormat format, ImageType type,
|
OnlineImage::OnlineImage(const std::string &url, int width, int height, ImageFormat format, ImageType type,
|
||||||
image::Transparency transparency, uint32_t download_buffer_size)
|
image::Transparency transparency, uint32_t download_buffer_size)
|
||||||
: Image(nullptr, 0, 0, type, transparency),
|
: Animation(nullptr, 0, 0, 1, type, transparency),
|
||||||
buffer_(nullptr),
|
buffer_(nullptr),
|
||||||
download_buffer_(download_buffer_size),
|
download_buffer_(download_buffer_size),
|
||||||
download_buffer_initial_size_(download_buffer_size),
|
download_buffer_initial_size_(download_buffer_size),
|
||||||
@@ -60,11 +63,12 @@ void OnlineImage::release() {
|
|||||||
this->height_ = 0;
|
this->height_ = 0;
|
||||||
this->buffer_width_ = 0;
|
this->buffer_width_ = 0;
|
||||||
this->buffer_height_ = 0;
|
this->buffer_height_ = 0;
|
||||||
|
this->buffer_frame_size_ = 0;
|
||||||
this->end_connection_();
|
this->end_connection_();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t OnlineImage::resize_(int width_in, int height_in) {
|
size_t OnlineImage::resize_(int width_in, int height_in, int frames) {
|
||||||
int width = this->fixed_width_;
|
int width = this->fixed_width_;
|
||||||
int height = this->fixed_height_;
|
int height = this->fixed_height_;
|
||||||
if (this->is_auto_resize_()) {
|
if (this->is_auto_resize_()) {
|
||||||
@@ -74,7 +78,7 @@ size_t OnlineImage::resize_(int width_in, int height_in) {
|
|||||||
this->release();
|
this->release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
size_t new_size = this->get_buffer_size_(width, height);
|
size_t new_size = this->get_buffer_size_(width, height, frames);
|
||||||
if (this->buffer_) {
|
if (this->buffer_) {
|
||||||
// Buffer already allocated => no need to resize
|
// Buffer already allocated => no need to resize
|
||||||
return new_size;
|
return new_size;
|
||||||
@@ -90,7 +94,10 @@ size_t OnlineImage::resize_(int width_in, int height_in) {
|
|||||||
this->buffer_width_ = width;
|
this->buffer_width_ = width;
|
||||||
this->buffer_height_ = height;
|
this->buffer_height_ = height;
|
||||||
this->width_ = width;
|
this->width_ = width;
|
||||||
ESP_LOGV(TAG, "New size: (%d, %d)", width, height);
|
this->animation_frame_count_ = frames;
|
||||||
|
this->buffer_frame_size_ = new_size / frames;
|
||||||
|
this->current_frame_ = 0;
|
||||||
|
ESP_LOGV(TAG, "New size: (%d, %d, %d)", width, height, frames);
|
||||||
return new_size;
|
return new_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,6 +124,11 @@ void OnlineImage::update() {
|
|||||||
accept_mime_type = "image/jpeg";
|
accept_mime_type = "image/jpeg";
|
||||||
break;
|
break;
|
||||||
#endif // USE_ONLINE_IMAGE_JPEG_SUPPORT
|
#endif // USE_ONLINE_IMAGE_JPEG_SUPPORT
|
||||||
|
#ifdef USE_ONLINE_IMAGE_WEBP_SUPPORT
|
||||||
|
case ImageFormat::WEBP:
|
||||||
|
accept_mime_type = "image/webp";
|
||||||
|
break;
|
||||||
|
#endif // USE_ONLINE_IMAGE_WEBP_SUPPORT
|
||||||
#ifdef USE_ONLINE_IMAGE_PNG_SUPPORT
|
#ifdef USE_ONLINE_IMAGE_PNG_SUPPORT
|
||||||
case ImageFormat::PNG:
|
case ImageFormat::PNG:
|
||||||
accept_mime_type = "image/png";
|
accept_mime_type = "image/png";
|
||||||
@@ -166,6 +178,12 @@ void OnlineImage::update() {
|
|||||||
this->decoder_ = esphome::make_unique<JpegDecoder>(this);
|
this->decoder_ = esphome::make_unique<JpegDecoder>(this);
|
||||||
}
|
}
|
||||||
#endif // USE_ONLINE_IMAGE_JPEG_SUPPORT
|
#endif // USE_ONLINE_IMAGE_JPEG_SUPPORT
|
||||||
|
#ifdef USE_ONLINE_IMAGE_WEBP_SUPPORT
|
||||||
|
if (this->format_ == ImageFormat::WEBP) {
|
||||||
|
ESP_LOGD(TAG, "Allocating WEBP decoder");
|
||||||
|
this->decoder_ = esphome::make_unique<WebpDecoder>(this);
|
||||||
|
}
|
||||||
|
#endif // USE_ONLINE_IMAGE_WEBP_SUPPORT
|
||||||
#ifdef USE_ONLINE_IMAGE_PNG_SUPPORT
|
#ifdef USE_ONLINE_IMAGE_PNG_SUPPORT
|
||||||
if (this->format_ == ImageFormat::PNG) {
|
if (this->format_ == ImageFormat::PNG) {
|
||||||
ESP_LOGD(TAG, "Allocating PNG decoder");
|
ESP_LOGD(TAG, "Allocating PNG decoder");
|
||||||
@@ -196,6 +214,7 @@ void OnlineImage::loop() {
|
|||||||
}
|
}
|
||||||
if (!this->downloader_ || this->decoder_->is_finished()) {
|
if (!this->downloader_ || this->decoder_->is_finished()) {
|
||||||
this->data_start_ = buffer_;
|
this->data_start_ = buffer_;
|
||||||
|
this->animation_data_start_ = this->buffer_;
|
||||||
this->width_ = buffer_width_;
|
this->width_ = buffer_width_;
|
||||||
this->height_ = buffer_height_;
|
this->height_ = buffer_height_;
|
||||||
ESP_LOGD(TAG, "Image fully downloaded, read %zu bytes, width/height = %d/%d", this->downloader_->get_bytes_read(),
|
ESP_LOGD(TAG, "Image fully downloaded, read %zu bytes, width/height = %d/%d", this->downloader_->get_bytes_read(),
|
||||||
@@ -243,16 +262,16 @@ void OnlineImage::map_chroma_key(Color &color) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnlineImage::draw_pixel_(int x, int y, Color color) {
|
void OnlineImage::draw_pixel_(int x, int y, Color color, int frame) {
|
||||||
if (!this->buffer_) {
|
if (!this->buffer_) {
|
||||||
ESP_LOGE(TAG, "Buffer not allocated!");
|
ESP_LOGE(TAG, "Buffer not allocated!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (x < 0 || y < 0 || x >= this->buffer_width_ || y >= this->buffer_height_) {
|
if (x < 0 || y < 0 || frame < 0 || x >= this->buffer_width_ || y >= this->buffer_height_ || frame >= this->animation_frame_count_) {
|
||||||
ESP_LOGE(TAG, "Tried to paint a pixel (%d,%d) outside the image!", x, y);
|
ESP_LOGE(TAG, "Tried to paint a pixel (%d,%d,%d) outside the image!", x, y, frame);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
uint32_t pos = this->get_position_(x, y);
|
uint32_t pos = this->get_position_(x, y, frame);
|
||||||
switch (this->type_) {
|
switch (this->type_) {
|
||||||
case ImageType::IMAGE_TYPE_BINARY: {
|
case ImageType::IMAGE_TYPE_BINARY: {
|
||||||
const uint32_t width_8 = ((this->width_ + 7u) / 8u) * 8u;
|
const uint32_t width_8 = ((this->width_ + 7u) / 8u) * 8u;
|
||||||
|
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "esphome/components/http_request/http_request.h"
|
#include "esphome/components/http_request/http_request.h"
|
||||||
#include "esphome/components/image/image.h"
|
#include "esphome/components/image/image.h"
|
||||||
|
#include "esphome/components/animation/animation.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"
|
||||||
@@ -25,6 +26,8 @@ enum ImageFormat {
|
|||||||
AUTO,
|
AUTO,
|
||||||
/** JPEG format. */
|
/** JPEG format. */
|
||||||
JPEG,
|
JPEG,
|
||||||
|
/** WEBP format. */
|
||||||
|
WEBP,
|
||||||
/** PNG format. */
|
/** PNG format. */
|
||||||
PNG,
|
PNG,
|
||||||
/** BMP format. */
|
/** BMP format. */
|
||||||
@@ -37,7 +40,7 @@ enum ImageFormat {
|
|||||||
* need to re-download or re-decode.
|
* need to re-download or re-decode.
|
||||||
*/
|
*/
|
||||||
class OnlineImage : public PollingComponent,
|
class OnlineImage : public PollingComponent,
|
||||||
public image::Image,
|
public animation::Animation,
|
||||||
public Parented<esphome::http_request::HttpRequestComponent> {
|
public Parented<esphome::http_request::HttpRequestComponent> {
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
@@ -94,10 +97,13 @@ class OnlineImage : public PollingComponent,
|
|||||||
|
|
||||||
RAMAllocator<uint8_t> allocator_{};
|
RAMAllocator<uint8_t> allocator_{};
|
||||||
|
|
||||||
uint32_t get_buffer_size_() const { return get_buffer_size_(this->buffer_width_, this->buffer_height_); }
|
uint32_t get_buffer_size_() const { return get_buffer_size_(this->buffer_width_, this->buffer_height_, this->animation_frame_count_); }
|
||||||
int get_buffer_size_(int width, int height) const { return (this->get_bpp() * width + 7u) / 8u * height; }
|
int get_buffer_size_(int width, int height, int frames) const { return frames * ((this->get_bpp() * width + 7u) / 8u * height); }
|
||||||
|
|
||||||
int get_position_(int x, int y) const { return (x + y * this->buffer_width_) * this->get_bpp() / 8; }
|
int get_position_(int x, int y, int frame = 0) const {
|
||||||
|
int frame_offset = this->buffer_frame_size_ * frame;
|
||||||
|
return ((x + y * this->buffer_width_) * this->get_bpp() / 8) + frame_offset;
|
||||||
|
}
|
||||||
|
|
||||||
ESPHOME_ALWAYS_INLINE bool is_auto_resize_() const { return this->fixed_width_ == 0 || this->fixed_height_ == 0; }
|
ESPHOME_ALWAYS_INLINE bool is_auto_resize_() const { return this->fixed_width_ == 0 || this->fixed_height_ == 0; }
|
||||||
|
|
||||||
@@ -112,9 +118,10 @@ class OnlineImage : public PollingComponent,
|
|||||||
*
|
*
|
||||||
* @param width
|
* @param width
|
||||||
* @param height
|
* @param height
|
||||||
|
* @param frames
|
||||||
* @return 0 if no memory could be allocated, the size of the new buffer otherwise.
|
* @return 0 if no memory could be allocated, the size of the new buffer otherwise.
|
||||||
*/
|
*/
|
||||||
size_t resize_(int width, int height);
|
size_t resize_(int width, int height, int frames = 1);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Draw a pixel into the buffer.
|
* @brief Draw a pixel into the buffer.
|
||||||
@@ -126,8 +133,9 @@ class OnlineImage : public PollingComponent,
|
|||||||
* @param x Horizontal pixel position.
|
* @param x Horizontal pixel position.
|
||||||
* @param y Vertical pixel position.
|
* @param y Vertical pixel position.
|
||||||
* @param color 32 bit color to put into the pixel.
|
* @param color 32 bit color to put into the pixel.
|
||||||
|
* @param frame the frame to draw the image buffer to if animated
|
||||||
*/
|
*/
|
||||||
void draw_pixel_(int x, int y, Color color);
|
void draw_pixel_(int x, int y, Color color, int frame = 0);
|
||||||
|
|
||||||
void end_connection_();
|
void end_connection_();
|
||||||
|
|
||||||
@@ -173,11 +181,13 @@ class OnlineImage : public PollingComponent,
|
|||||||
* decoded images).
|
* decoded images).
|
||||||
*/
|
*/
|
||||||
int buffer_height_;
|
int buffer_height_;
|
||||||
|
/** The calculated size of a single frame for the given width and height in the buffer */
|
||||||
|
int buffer_frame_size_;
|
||||||
|
|
||||||
time_t start_time_;
|
time_t start_time_;
|
||||||
|
|
||||||
friend bool ImageDecoder::set_size(int width, int height);
|
friend bool ImageDecoder::set_size(int width, int height, int frames);
|
||||||
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, int frame);
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename... Ts> class OnlineImageSetUrlAction : public Action<Ts...> {
|
template<typename... Ts> class OnlineImageSetUrlAction : public Action<Ts...> {
|
||||||
|
124
esphome/components/online_image/webp_image.cpp
Normal file
124
esphome/components/online_image/webp_image.cpp
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
#include "webp_image.h"
|
||||||
|
|
||||||
|
#ifdef USE_ONLINE_IMAGE_WEBP_SUPPORT
|
||||||
|
|
||||||
|
#include "esphome/components/display/display_buffer.h"
|
||||||
|
#include "esphome/core/application.h"
|
||||||
|
#include "esphome/core/helpers.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
#include "online_image.h"
|
||||||
|
static const char *const TAG = "online_image.webp";
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace online_image {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief method that will be called to draw each frame to the image buffer.
|
||||||
|
*
|
||||||
|
* @param decoder The ImageDecoder to draw to
|
||||||
|
* @param pix Buffer of pixels for the given frame
|
||||||
|
* @param width Image width
|
||||||
|
* @param height Image Height
|
||||||
|
* @param frame The frame to draw the image to
|
||||||
|
*/
|
||||||
|
void draw_frame(ImageDecoder *decoder, uint8_t *pix, uint32_t width, uint32_t height, int frame) {
|
||||||
|
static int ixR = 0;
|
||||||
|
static int ixG = 1;
|
||||||
|
static int ixB = 2;
|
||||||
|
static int ixA = 3;
|
||||||
|
static int channels = 4;
|
||||||
|
|
||||||
|
for (unsigned int y = 0; y < height; y++) {
|
||||||
|
for (unsigned int x = 0; x < width; x++) {
|
||||||
|
const uint8_t *p = &pix[(y * width + x) * channels];
|
||||||
|
uint8_t r = p[ixR];
|
||||||
|
uint8_t g = p[ixG];
|
||||||
|
uint8_t b = p[ixB];
|
||||||
|
Color color(r, g, b, p[ixA]);
|
||||||
|
decoder->draw(x, y, 1, 1, color, frame);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int WebpDecoder::prepare(size_t download_size) {
|
||||||
|
ImageDecoder::prepare(download_size);
|
||||||
|
auto size = this->image_->resize_download_buffer(download_size);
|
||||||
|
if (size < download_size) {
|
||||||
|
ESP_LOGE(TAG, "Download buffer resize failed!");
|
||||||
|
return DECODE_ERROR_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int HOT WebpDecoder::decode(uint8_t *buffer, size_t size) {
|
||||||
|
ESP_LOGD(TAG, "decode size: %d", size);
|
||||||
|
if (size < this->download_size_) {
|
||||||
|
ESP_LOGV(TAG, "Download not complete. Size: %d/%d", size, this->download_size_);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up WebP decoder
|
||||||
|
WebPData webpData;
|
||||||
|
WebPDataInit(&webpData);
|
||||||
|
webpData.bytes = buffer;
|
||||||
|
webpData.size = size;
|
||||||
|
|
||||||
|
WebPAnimDecoderOptions decoderOptions;
|
||||||
|
WebPAnimDecoderOptionsInit(&decoderOptions);
|
||||||
|
decoderOptions.color_mode = MODE_RGBA;
|
||||||
|
|
||||||
|
this->decoder_ = WebPAnimDecoderNew(&webpData, &decoderOptions);
|
||||||
|
if (this->decoder_ == NULL) {
|
||||||
|
ESP_LOGE(TAG, "Could not create WebP decoder");
|
||||||
|
return DECODE_ERROR_UNSUPPORTED_FORMAT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!WebPAnimDecoderGetInfo(this->decoder_, &this->animation_)) {
|
||||||
|
ESP_LOGE(TAG, "Could not get WebP animation");
|
||||||
|
WebPAnimDecoderDelete(this->decoder_);
|
||||||
|
this->decoder_ = NULL;
|
||||||
|
return DECODE_ERROR_UNSUPPORTED_FORMAT;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGD(TAG, "WebPAnimDecode size: (%dx%d), loops: %d, frames: %d, bgcolor: #%X", animation_.canvas_width, animation_.canvas_height, animation_.loop_count, animation_.frame_count, animation_.bgcolor);
|
||||||
|
|
||||||
|
if (!this->set_size(animation_.canvas_width, animation_.canvas_height, animation_.frame_count)) {
|
||||||
|
ESP_LOGE(TAG,"could not allocate enough memory");
|
||||||
|
WebPAnimDecoderDelete(this->decoder_);
|
||||||
|
this->decoder_ = NULL;
|
||||||
|
return DECODE_ERROR_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (animation_.frame_count == 0) {
|
||||||
|
ESP_LOGE(TAG, "Animation has 0 frames");
|
||||||
|
WebPAnimDecoderDelete(this->decoder_);
|
||||||
|
this->decoder_ = NULL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// iterate over all frames
|
||||||
|
for (uint frame = 0; frame < animation_.frame_count; frame++) {
|
||||||
|
uint8_t *pix;
|
||||||
|
int timestamp;
|
||||||
|
if (!WebPAnimDecoderGetNext(this->decoder_, &pix, ×tamp)) {
|
||||||
|
ESP_LOGE(TAG,"error parsing webp frame %u/%u", frame, animation_.frame_count);
|
||||||
|
WebPAnimDecoderDelete(this->decoder_);
|
||||||
|
this->decoder_ = NULL;
|
||||||
|
return DECODE_ERROR_UNSUPPORTED_FORMAT;
|
||||||
|
}
|
||||||
|
|
||||||
|
draw_frame(this, pix, this->animation_.canvas_width, this->animation_.canvas_height, frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
WebPAnimDecoderDelete(this->decoder_);
|
||||||
|
this->decoder_ = NULL;
|
||||||
|
|
||||||
|
this->decoded_bytes_ = size;
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace online_image
|
||||||
|
} // namespace esphome
|
||||||
|
|
||||||
|
#endif // USE_ONLINE_IMAGE_WEBP_SUPPORT
|
35
esphome/components/online_image/webp_image.h
Normal file
35
esphome/components/online_image/webp_image.h
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "image_decoder.h"
|
||||||
|
#include "esphome/core/defines.h"
|
||||||
|
#ifdef USE_ONLINE_IMAGE_WEBP_SUPPORT
|
||||||
|
#include <webp/demux.h>
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace online_image {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Image decoder specialization for WEBP images.
|
||||||
|
*/
|
||||||
|
class WebpDecoder : public ImageDecoder {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Construct a new WEBP Decoder object.
|
||||||
|
*
|
||||||
|
* @param display The image to decode the stream into.
|
||||||
|
*/
|
||||||
|
WebpDecoder(OnlineImage *image) : ImageDecoder(image) {}
|
||||||
|
~WebpDecoder() override {}
|
||||||
|
|
||||||
|
int prepare(size_t download_size) override;
|
||||||
|
int HOT decode(uint8_t *buffer, size_t size) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
WebPAnimInfo animation_;
|
||||||
|
WebPAnimDecoder *decoder_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace online_image
|
||||||
|
} // namespace esphome
|
||||||
|
|
||||||
|
#endif // USE_ONLINE_IMAGE_WEBP_SUPPORT
|
@@ -68,6 +68,7 @@
|
|||||||
#define USE_ONLINE_IMAGE_BMP_SUPPORT
|
#define USE_ONLINE_IMAGE_BMP_SUPPORT
|
||||||
#define USE_ONLINE_IMAGE_PNG_SUPPORT
|
#define USE_ONLINE_IMAGE_PNG_SUPPORT
|
||||||
#define USE_ONLINE_IMAGE_JPEG_SUPPORT
|
#define USE_ONLINE_IMAGE_JPEG_SUPPORT
|
||||||
|
#define USE_ONLINE_IMAGE_WEBP_SUPPORT
|
||||||
#define USE_OTA
|
#define USE_OTA
|
||||||
#define USE_OTA_PASSWORD
|
#define USE_OTA_PASSWORD
|
||||||
#define USE_OTA_STATE_CALLBACK
|
#define USE_OTA_STATE_CALLBACK
|
||||||
|
@@ -43,6 +43,7 @@ lib_deps =
|
|||||||
kikuchan98/pngle@1.0.2 ; online_image
|
kikuchan98/pngle@1.0.2 ; online_image
|
||||||
; Using the repository directly, otherwise ESP-IDF can't use the library
|
; Using the repository directly, otherwise ESP-IDF can't use the library
|
||||||
https://github.com/bitbank2/JPEGDEC.git#ca1e0f2 ; online_image
|
https://github.com/bitbank2/JPEGDEC.git#ca1e0f2 ; online_image
|
||||||
|
https://github.com/acvigue/libwebp#26b0c4b ; online_image
|
||||||
; This is using the repository until a new release is published to PlatformIO
|
; This is using the repository until a new release is published to PlatformIO
|
||||||
https://github.com/Sensirion/arduino-gas-index-algorithm.git#3.2.1 ; Sensirion Gas Index Algorithm Arduino Library
|
https://github.com/Sensirion/arduino-gas-index-algorithm.git#3.2.1 ; Sensirion Gas Index Algorithm Arduino Library
|
||||||
lvgl/lvgl@8.4.0 ; lvgl
|
lvgl/lvgl@8.4.0 ; lvgl
|
||||||
|
@@ -38,6 +38,10 @@ online_image:
|
|||||||
url: http://www.faqs.org/images/library.jpg
|
url: http://www.faqs.org/images/library.jpg
|
||||||
format: JPG
|
format: JPG
|
||||||
type: RGB565
|
type: RGB565
|
||||||
|
- id: online_webp_image
|
||||||
|
url: https://samples-files.com/samples/images/webp/480-360-sample.webp
|
||||||
|
format: WEBP
|
||||||
|
type: RGB
|
||||||
|
|
||||||
# Check the set_url action
|
# Check the set_url action
|
||||||
esphome:
|
esphome:
|
||||||
|
Reference in New Issue
Block a user