mirror of
https://github.com/aseprite/aseprite.git
synced 2025-03-17 13:20:45 +00:00
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.
This commit is contained in:
parent
12a46f5ca3
commit
9d281793c8
@ -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; y<dst->getHeight(); ++y) {
|
||||
for (x=0; x<dst->getWidth(); ++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;
|
||||
}
|
||||
|
111
src/raster/resize_image_unittest.cpp
Normal file
111
src/raster/resize_image_unittest.cpp
Normal file
@ -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 <gtest/gtest.h>
|
||||
|
||||
#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();
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user