diff --git a/esphome/components/image/image.cpp b/esphome/components/image/image.cpp index bfe311be284..8b373ef1ab4 100644 --- a/esphome/components/image/image.cpp +++ b/esphome/components/image/image.cpp @@ -25,8 +25,8 @@ void Image::draw(int x, int y, display::Display *display, Color color_on, Color switch (type_) { case IMAGE_TYPE_BINARY: { - for (int img_x = img_x0; img_x < w; img_x++) { - for (int img_y = img_y0; img_y < h; img_y++) { + for (int img_y = img_y0; img_y < h; img_y++) { + for (int img_x = img_x0; img_x < w; img_x++) { if (this->get_binary_pixel_(img_x, img_y)) { display->draw_pixel_at(x + img_x, y + img_y, color_on); } else if (!this->transparency_) { @@ -37,8 +37,8 @@ void Image::draw(int x, int y, display::Display *display, Color color_on, Color break; } case IMAGE_TYPE_GRAYSCALE: - for (int img_x = img_x0; img_x < w; img_x++) { - for (int img_y = img_y0; img_y < h; img_y++) { + for (int img_y = img_y0; img_y < h; img_y++) { + for (int img_x = img_x0; img_x < w; img_x++) { const uint32_t pos = (img_x + img_y * this->width_); const uint8_t gray = progmem_read_byte(this->data_start_ + pos); Color color = Color(gray, gray, gray, 0xFF); @@ -62,8 +62,8 @@ void Image::draw(int x, int y, display::Display *display, Color color_on, Color } break; case IMAGE_TYPE_RGB565: - for (int img_x = img_x0; img_x < w; img_x++) { - for (int img_y = img_y0; img_y < h; img_y++) { + for (int img_y = img_y0; img_y < h; img_y++) { + for (int img_x = img_x0; img_x < w; img_x++) { auto color = this->get_rgb565_pixel_(img_x, img_y); if (color.w >= 0x80) { display->draw_pixel_at(x + img_x, y + img_y, color); @@ -72,8 +72,8 @@ void Image::draw(int x, int y, display::Display *display, Color color_on, Color } break; case IMAGE_TYPE_RGB: - for (int img_x = img_x0; img_x < w; img_x++) { - for (int img_y = img_y0; img_y < h; img_y++) { + for (int img_y = img_y0; img_y < h; img_y++) { + for (int img_x = img_x0; img_x < w; img_x++) { auto color = this->get_rgb_pixel_(img_x, img_y); if (color.w >= 0x80) { display->draw_pixel_at(x + img_x, y + img_y, color); diff --git a/tests/benchmarks/components/image/__init__.py b/tests/benchmarks/components/image/__init__.py new file mode 100644 index 00000000000..b08f67a0956 --- /dev/null +++ b/tests/benchmarks/components/image/__init__.py @@ -0,0 +1,5 @@ +from tests.testing_helpers import ComponentManifestOverride + + +def override_manifest(manifest: ComponentManifestOverride) -> None: + manifest.enable_codegen() diff --git a/tests/benchmarks/components/image/bench_image_draw.cpp b/tests/benchmarks/components/image/bench_image_draw.cpp new file mode 100644 index 00000000000..d16a890f64f --- /dev/null +++ b/tests/benchmarks/components/image/bench_image_draw.cpp @@ -0,0 +1,80 @@ +#include + +#include +#include + +#include "esphome/components/display/display_buffer.h" +#include "esphome/components/image/image.h" + +namespace esphome::image::benchmarks { + +static constexpr int kWidth = 128; +static constexpr int kHeight = 96; + +// One byte per pixel frame buffer with no hardware behind it, so the +// measurement is Image::draw() plus the DisplayBuffer pixel path every +// buffered display shares. +class BenchDisplay : public display::DisplayBuffer { + public: + BenchDisplay() { this->init_internal_(kWidth * kHeight); } + void update() override {} + display::DisplayType get_display_type() override { return display::DisplayType::DISPLAY_TYPE_COLOR; } + int get_width_internal() override { return kWidth; } + int get_height_internal() override { return kHeight; } + + protected: + void draw_absolute_pixel_internal(int x, int y, Color color) override { + this->buffer_[y * kWidth + x] = color.r ^ color.g ^ color.b; + } +}; + +// Deterministic pseudo random pixel bytes; the values only need to vary so +// the chroma key and alpha branches see a mix of pixels. +static std::unique_ptr make_pixels(size_t bytes) { + auto data = std::make_unique(bytes); + uint32_t seed = 11897; + for (size_t i = 0; i < bytes; i++) { + seed = seed * 1103515245u + 12345u; + data[i] = static_cast(seed >> 16); + } + return data; +} + +static void draw_image(benchmark::State &state, ImageType type, Transparency transparency, size_t bytes) { + auto data = make_pixels(bytes); + Image image(data.get(), kWidth, kHeight, type, transparency); + BenchDisplay display; + + for (auto _ : state) { + image.draw(0, 0, &display, display::COLOR_ON, display::COLOR_OFF); + benchmark::DoNotOptimize(&display); + } + state.SetItemsProcessed(state.iterations() * kWidth * kHeight); +} + +static void ImageDraw_Binary(benchmark::State &state) { + draw_image(state, IMAGE_TYPE_BINARY, TRANSPARENCY_OPAQUE, (kWidth + 7) / 8 * kHeight); +} +BENCHMARK(ImageDraw_Binary); + +static void ImageDraw_Grayscale(benchmark::State &state) { + draw_image(state, IMAGE_TYPE_GRAYSCALE, TRANSPARENCY_OPAQUE, kWidth * kHeight); +} +BENCHMARK(ImageDraw_Grayscale); + +static void ImageDraw_RGB565(benchmark::State &state) { + draw_image(state, IMAGE_TYPE_RGB565, TRANSPARENCY_OPAQUE, kWidth * kHeight * 2); +} +BENCHMARK(ImageDraw_RGB565); + +static void ImageDraw_RGB(benchmark::State &state) { + draw_image(state, IMAGE_TYPE_RGB, TRANSPARENCY_OPAQUE, kWidth * kHeight * 3); +} +BENCHMARK(ImageDraw_RGB); + +static void ImageDraw_RGB_Alpha(benchmark::State &state) { + draw_image(state, IMAGE_TYPE_RGB, TRANSPARENCY_ALPHA_CHANNEL, kWidth * kHeight * 4); +} +BENCHMARK(ImageDraw_RGB_Alpha); + +} // namespace esphome::image::benchmarks