From 9d281793c8041547dbcc52fd2552e76e22038853 Mon Sep 17 00:00:00 2001 From: Joel Madigan Date: Mon, 2 Dec 2013 00:23:05 -0500 Subject: [PATCH] Re-implement nearest-neighbor interpolation Fix for issue 295, there was, what amounts to an off by one error in the original code, which was causing the problem in issue 295. This error was probably not very noticable in larger images, but as pointed out by the bug report smaller images may contain more distortion than necessary. Wasn't entirely sure how to correct the original code, so replaced it with entirely new code. Also added unit test for resize methods, however the bilinear test only checks using RGB, and not the other ImageTrait types. --- src/raster/algorithm/resize_image.cpp | 29 ++++--- src/raster/resize_image_unittest.cpp | 111 ++++++++++++++++++++++++++ 2 files changed, 125 insertions(+), 15 deletions(-) create mode 100644 src/raster/resize_image_unittest.cpp diff --git a/src/raster/algorithm/resize_image.cpp b/src/raster/algorithm/resize_image.cpp index b536b9768..ee412ba8b 100644 --- a/src/raster/algorithm/resize_image.cpp +++ b/src/raster/algorithm/resize_image.cpp @@ -35,23 +35,22 @@ void resize_image(const Image* src, Image* dst, ResizeMethod method, const Palet switch (method) { // TODO optimize this - case RESIZE_METHOD_NEAREST_NEIGHBOR: { - uint32_t color; - double u, v, du, dv; - int x, y; + case RESIZE_METHOD_NEAREST_NEIGHBOR: + { + int o_width = src->getWidth(), o_height = src->getHeight(); + int n_width = dst->getWidth(), n_height = dst->getHeight(); + double x_ratio = o_width / (double)n_width; + double y_ratio = o_height / (double)n_height; + double px, py; + int i; - u = v = 0.0; - du = src->getWidth() * 1.0 / dst->getWidth(); - dv = src->getHeight() * 1.0 / dst->getHeight(); - for (y=0; ygetHeight(); ++y) { - for (x=0; xgetWidth(); ++x) { - color = src->getPixel(MID(0, u, src->getWidth()-1), - MID(0, v, src->getHeight()-1)); - dst->putPixel(x, y, color); - u += du; + for (int y = 0; y < n_height; y++) { + for (int x = 0; x < n_width; x++) { + px = floor(x * x_ratio); + py = floor(y * y_ratio); + i = (int)(py * o_width + px); + dst->putPixel(x, y, src->getPixel(i % o_width, i / o_width)); } - u = 0.0; - v += dv; } break; } diff --git a/src/raster/resize_image_unittest.cpp b/src/raster/resize_image_unittest.cpp new file mode 100644 index 000000000..6427aee78 --- /dev/null +++ b/src/raster/resize_image_unittest.cpp @@ -0,0 +1,111 @@ +// Aseprite Gfx Library +// Copyright (C) 2001-2013 David Capello +// +// This source file is distributed under MIT license, +// please read LICENSE.txt for more information. + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "raster/color.h" +#include "raster/image.h" +#include "raster/algorithm/resize_image.h" + +using namespace std; +using namespace raster; + +/*************************** + * Test dat + */ + +// Base image +color_t test_image_base_3x3[9] = +{ + 0x000000, 0xffffff, 0x000000, + 0xffffff, 0xffffff, 0xffffff, + 0x000000, 0xffffff, 0x000000 +}; + +// Base image scaled to 9x9 with nearest neighbor interpolation +color_t test_image_scaled_9x9_nearest[81] = +{ + 0x000000, 0x000000, 0x000000, 0xffffff, 0xffffff, 0xffffff, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0xffffff, 0xffffff, 0xffffff, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0xffffff, 0xffffff, 0xffffff, 0x000000, 0x000000, 0x000000, + 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, + 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, + 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, + 0x000000, 0x000000, 0x000000, 0xffffff, 0xffffff, 0xffffff, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0xffffff, 0xffffff, 0xffffff, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0xffffff, 0xffffff, 0xffffff, 0x000000, 0x000000, 0x000000 +}; + +// Base image scalled to 9x9 with bilinear interpolation +color_t test_image_scaled_9x9_bilinear[81] = +{ + 0x000000, 0x000000, 0x565656, 0xa9a9a9, 0xffffff, 0xa9a9a9, 0x565656, 0x000000, 0x000000, + 0x000000, 0x000000, 0x565656, 0xa9a9a9, 0xffffff, 0xa9a9a9, 0x565656, 0x000000, 0x000000, + 0x565656, 0x565656, 0x8f8f8f, 0xc6c6c6, 0xffffff, 0xc6c6c6, 0x8f8f8f, 0x565656, 0x565656, + 0xa9a9a9, 0xa9a9a9, 0xc6c6c6, 0xe2e2e2, 0xffffff, 0xe2e2e2, 0xc6c6c6, 0xa9a9a9, 0xa9a9a9, + 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, + 0xa9a9a9, 0xa9a9a9, 0xc6c6c6, 0xe2e2e2, 0xffffff, 0xe2e2e2, 0xc6c6c6, 0xa9a9a9, 0xa9a9a9, + 0x565656, 0x565656, 0x8f8f8f, 0xc6c6c6, 0xffffff, 0xc6c6c6, 0x8f8f8f, 0x565656, 0x565656, + 0x000000, 0x000000, 0x565656, 0xa9a9a9, 0xffffff, 0xa9a9a9, 0x565656, 0x000000, 0x000000, + 0x000000, 0x000000, 0x565656, 0xa9a9a9, 0xffffff, 0xa9a9a9, 0x565656, 0x000000, 0x000000 +}; + +Image* create_image_from_data(PixelFormat format, color_t* data, int width, int height) +{ + Image* new_image = Image::create(format, width, height); + for (int i = 0; i < width * height; i++) { + new_image->putPixel(i % width, i / width, data[i]); + } + + return new_image; +} + +// Simple pixel to pixel image comparison +bool compare_images(Image* a, Image* b) +{ + for (int y = 0; y < a->getHeight(); y++) { + for (int x = 0; x < a->getWidth(); x++) { + if (!(a->getPixel(x, y) == b->getPixel(x, y))) + return false; + } + } + return true; +} + +TEST(ResizeImage, NearestNeighborInterp) +{ + Image* src = create_image_from_data(IMAGE_RGB, test_image_base_3x3, 3, 3); + Image* dst = Image::create(IMAGE_RGB, 9, 9); + + // Pre-rendered test image for comparison + Image* test_dst = create_image_from_data(IMAGE_RGB, test_image_scaled_9x9_nearest, 9, 9); + + algorithm::resize_image(src, dst, algorithm::ResizeMethod::RESIZE_METHOD_NEAREST_NEIGHBOR, NULL, NULL); + + ASSERT_TRUE(compare_images(dst, test_dst)) << "resize_image() result does not match test image!"; +} + +TEST(ResizeImage, BilinearInterpRGBType) +{ + Image* src = create_image_from_data(IMAGE_RGB, test_image_base_3x3, 3, 3); + Image* dst = Image::create(IMAGE_RGB, 9, 9); + + // Pre-rendered test image for comparison + Image* test_dst = create_image_from_data(IMAGE_RGB, test_image_scaled_9x9_bilinear, 9, 9); + + algorithm::resize_image(src, dst, algorithm::ResizeMethod::RESIZE_METHOD_BILINEAR, NULL, NULL); + + ASSERT_TRUE(compare_images(dst, test_dst)) << "resize_image() result does not match test image!"; +} +int main(int argc, char** argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +}