diff --git a/esphome/components/image/image.cpp b/esphome/components/image/image.cpp index c0592b33fe6..2ecbe836b79 100644 --- a/esphome/components/image/image.cpp +++ b/esphome/components/image/image.cpp @@ -6,9 +6,9 @@ namespace esphome::image { // Walks the clipped image range with rows (ROWS_OUTER) or columns as the outer -// loop. The selects fold at compile time and the pixel body must be -// ESPHOME_ALWAYS_INLINE: at -Os GCC otherwise leaves it out of line and calls it -// once per pixel. +// loop. The coordinate selection folds at compile time, and the pixel body must +// be ESPHOME_ALWAYS_INLINE: at -Os GCC otherwise leaves it out of line and calls +// it once per pixel. template static void for_each_pixel(int x_start, int x_end, int y_start, int y_end, F &&f) { const int outer_start = ROWS_OUTER ? y_start : x_start; diff --git a/tests/benchmarks/components/image/bench_image_draw.cpp b/tests/benchmarks/components/image/bench_image_draw.cpp index 83530e0653c..9bab48ae274 100644 --- a/tests/benchmarks/components/image/bench_image_draw.cpp +++ b/tests/benchmarks/components/image/bench_image_draw.cpp @@ -29,8 +29,29 @@ class BenchDisplay : public display::DisplayBuffer { void draw_absolute_pixel_internal(int x, int y, Color color) override { this->frame_[y * kStride + x] = color.r; } std::unique_ptr frame_; + + public: + uint8_t pixel(int x, int y) const { return this->frame_[y * kStride + x]; } }; +// Draws the image at rotation 0 and 90 and checks the second frame is the +// transpose of the first, which is what a wrong traversal order would break. +static bool frames_match(const Image &image) { + BenchDisplay plain; + BenchDisplay rotated; + rotated.set_rotation(display::DISPLAY_ROTATION_90_DEGREES); + const_cast(image).draw(0, 0, &plain, display::COLOR_ON, display::COLOR_OFF); + const_cast(image).draw(0, 0, &rotated, display::COLOR_ON, display::COLOR_OFF); + for (int y = 0; y < kHeight; y++) { + for (int x = 0; x < kWidth; x++) { + // DisplayBuffer maps (x, y) to (width - y - 1, x) at 90 degrees + if (plain.pixel(x, y) != rotated.pixel(kStride - y - 1, x)) + return false; + } + } + return true; +} + // Deterministic pseudo random pixel bytes so the alpha branch sees a mix of // pixels. Sized for the widest format, the image reads only what it needs. static std::unique_ptr make_pixels() { @@ -50,6 +71,10 @@ static void draw_image(benchmark::State &state, ImageType type, Transparency tra Image image(data.get(), kWidth, kHeight, type, transparency); BenchDisplay display; display.set_rotation(rotation); + if (!frames_match(image)) { + state.SkipWithError("rotated draw is not the transpose of the plain draw"); + return; + } for (auto _ : state) { image.draw(0, 0, &display, display::COLOR_ON, display::COLOR_OFF);