Simplify nearest neighbor rescale (#8240)

This commit is contained in:
Eugene 2024-01-21 02:50:14 +04:00 committed by GitHub
parent c25fc888a7
commit 9f99a4ef7d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 175 additions and 96 deletions

View File

@ -235,6 +235,10 @@ rng/test_mt_rng_reproducibility5
rng/test_mt_rng_reproducibility_coverage
rng/validate_get_random_int
rng/validate_get_random_int2
sdl/test_scale_sharp_nullptr
sdl/test_scale_sharp_zero
sdl/test_scale_sharp_round
sdl/test_scale_sharp_fractional
test_serialization_utils_and_unicode/utils_join_test
test_serialization_utils_and_unicode/utils_split_test
test_serialization_utils_and_unicode/utils_quoted_split_test

View File

@ -1211,6 +1211,7 @@
<Unit filename="../../src/tests/test_mp_connect.cpp" />
<Unit filename="../../src/tests/test_recall_list.cpp" />
<Unit filename="../../src/tests/test_rng.cpp" />
<Unit filename="../../src/tests/test_sdl.cpp" />
<Unit filename="../../src/tests/test_serialization.cpp" />
<Unit filename="../../src/tests/test_simple_wml.cpp" />
<Unit filename="../../src/tests/test_team.cpp" />

View File

@ -628,6 +628,7 @@
62114CC61750B99800CA42C7 /* mouse_action_item.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 62114CC41750B99800CA42C7 /* mouse_action_item.cpp */; };
62114CC91750B9AF00CA42C7 /* item_palette.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 62114CC71750B9AF00CA42C7 /* item_palette.cpp */; };
621DEB8415A7BA1F00FEE18A /* highlighter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 621DEB8215A7BA1F00FEE18A /* highlighter.cpp */; };
62714C2FBE84B66CF14E3722 /* test_sdl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D911474D925FA88D5B856A0E /* test_sdl.cpp */; };
627F1EDB175AF35C000042E0 /* aspect_advancements.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 627F1ED9175AF35C000042E0 /* aspect_advancements.cpp */; };
6295C3B5150FC95C0077D8C5 /* action.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6295C3A7150FC95C0077D8C5 /* action.cpp */; };
6295C3B6150FC95C0077D8C5 /* action_label.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6295C3AA150FC95C0077D8C5 /* action_label.cpp */; };
@ -2706,6 +2707,7 @@
C61F473D9AC43768A445E218 /* tod_new_schedule.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = tod_new_schedule.cpp; sourceTree = "<group>"; };
C679447D91FD3623CC852FF8 /* edit_pbl_translation.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = edit_pbl_translation.hpp; sourceTree = "<group>"; };
D4594633BF3F8A06D6AE752F /* prompt.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = prompt.hpp; sourceTree = "<group>"; };
D911474D925FA88D5B856A0E /* test_sdl.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = test_sdl.cpp; path = test_sdl.cpp; sourceTree = "<group>"; };
D9A141EAAE90E98B6F6171D6 /* choose_addon.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = choose_addon.hpp; sourceTree = "<group>"; };
EC0341DF1ECF46FE000F2E2B /* config_attribute_value.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = config_attribute_value.cpp; sourceTree = "<group>"; };
EC0341E01ECF46FE000F2E2B /* config_attribute_value.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = config_attribute_value.hpp; sourceTree = "<group>"; };
@ -4796,6 +4798,7 @@
91E356131CACA6CB00774252 /* test_recall_list.cpp */,
91E356141CACA6CB00774252 /* test_rng.cpp */,
B597C4AA0FACD42E00CE81F5 /* test_serialization.cpp */,
D911474D925FA88D5B856A0E /* test_sdl.cpp */,
B597C4A60FACD42E00CE81F5 /* test_team.cpp */,
91E356171CACA6CB00774252 /* test_unit_map.cpp */,
B597C4A50FACD42E00CE81F5 /* test_util.cpp */,
@ -6540,6 +6543,7 @@
EBB44A70837B84A928CB6424 /* edit_pbl_translation.cpp in Sources */,
7BFC4DF5BFF8CF75855BA662 /* prompt.cpp in Sources */,
C93048A9AE576B6AD016DFA4 /* tod_new_schedule.cpp in Sources */,
62714C2FBE84B66CF14E3722 /* test_sdl.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -6643,15 +6647,15 @@
"$(PROJECT_DIR)/Headers/SDL2",
"$(PROJECT_DIR)/Headers/harfbuzz",
);
LD_CLASSIC_1000 = "";
LD_CLASSIC_1100 = "";
LD_CLASSIC_1200 = "";
LD_CLASSIC_1300 = "";
LD_CLASSIC_1400 = "";
LD_CLASSIC_1500 = "-ld_classic";
LD_RUNPATH_SEARCH_PATHS = "@loader_path/../Frameworks";
LIBRARY_SEARCH_PATHS = "$(PROJECT_DIR)/lib";
MACOSX_DEPLOYMENT_TARGET = 10.12;
"LD_CLASSIC_1000" = "";
"LD_CLASSIC_1100" = "";
"LD_CLASSIC_1200" = "";
"LD_CLASSIC_1300" = "";
"LD_CLASSIC_1400" = "";
"LD_CLASSIC_1500" = "-ld_classic";
OTHER_LDFLAGS = (
"-lz",
"-lbz2",
@ -6717,16 +6721,16 @@
GCC_WARN_UNUSED_VARIABLE = YES;
INFOPLIST_FILE = Info.plist;
INSTALL_PATH = /Applications;
LD_CLASSIC_1000 = "";
LD_CLASSIC_1100 = "";
LD_CLASSIC_1200 = "";
LD_CLASSIC_1300 = "";
LD_CLASSIC_1400 = "";
LD_CLASSIC_1500 = "-ld_classic";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
LLVM_LTO = YES_THIN;
OTHER_CFLAGS = "-Wall";
OTHER_CPLUSPLUSFLAGS = "-Wall";
"LD_CLASSIC_1000" = "";
"LD_CLASSIC_1100" = "";
"LD_CLASSIC_1200" = "";
"LD_CLASSIC_1300" = "";
"LD_CLASSIC_1400" = "";
"LD_CLASSIC_1500" = "-ld_classic";
OTHER_LDFLAGS = (
"-lz",
"-lbz2",
@ -7112,15 +7116,15 @@
GCC_WARN_UNUSED_VARIABLE = YES;
INFOPLIST_FILE = Info.plist;
INSTALL_PATH = /Applications;
LD_CLASSIC_1000 = "";
LD_CLASSIC_1100 = "";
LD_CLASSIC_1200 = "";
LD_CLASSIC_1300 = "";
LD_CLASSIC_1400 = "";
LD_CLASSIC_1500 = "-ld_classic";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
OTHER_CFLAGS = "-Wall";
OTHER_CPLUSPLUSFLAGS = "-Wall";
"LD_CLASSIC_1000" = "";
"LD_CLASSIC_1100" = "";
"LD_CLASSIC_1200" = "";
"LD_CLASSIC_1300" = "";
"LD_CLASSIC_1400" = "";
"LD_CLASSIC_1500" = "-ld_classic";
OTHER_LDFLAGS = (
"-lz",
"-lbz2",
@ -7178,16 +7182,16 @@
GCC_WARN_UNUSED_VARIABLE = YES;
INFOPLIST_FILE = Info.plist;
INSTALL_PATH = /Applications;
LD_CLASSIC_1000 = "";
LD_CLASSIC_1100 = "";
LD_CLASSIC_1200 = "";
LD_CLASSIC_1300 = "";
LD_CLASSIC_1400 = "";
LD_CLASSIC_1500 = "-ld_classic";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
LLVM_LTO = YES_THIN;
OTHER_CFLAGS = "-Wall";
OTHER_CPLUSPLUSFLAGS = "-Wall";
"LD_CLASSIC_1000" = "";
"LD_CLASSIC_1100" = "";
"LD_CLASSIC_1200" = "";
"LD_CLASSIC_1300" = "";
"LD_CLASSIC_1400" = "";
"LD_CLASSIC_1500" = "-ld_classic";
OTHER_LDFLAGS = (
"-lz",
"-lbz2",
@ -7263,17 +7267,17 @@
"$(PROJECT_DIR)/Headers/SDL2",
"$(PROJECT_DIR)/Headers/harfbuzz",
);
LD_CLASSIC_1000 = "";
LD_CLASSIC_1100 = "";
LD_CLASSIC_1200 = "";
LD_CLASSIC_1300 = "";
LD_CLASSIC_1400 = "";
LD_CLASSIC_1500 = "-ld_classic";
LD_RUNPATH_SEARCH_PATHS = "@loader_path/../Frameworks";
LIBRARY_SEARCH_PATHS = "$(PROJECT_DIR)/lib";
MACOSX_DEPLOYMENT_TARGET = 10.12;
ONLY_ACTIVE_ARCH = YES;
OTHER_CFLAGS = "-Wall";
"LD_CLASSIC_1000" = "";
"LD_CLASSIC_1100" = "";
"LD_CLASSIC_1200" = "";
"LD_CLASSIC_1300" = "";
"LD_CLASSIC_1400" = "";
"LD_CLASSIC_1500" = "-ld_classic";
OTHER_LDFLAGS = (
"-lz",
"-lbz2",
@ -7318,15 +7322,15 @@
"$(PROJECT_DIR)/Headers/SDL2",
"$(PROJECT_DIR)/Headers/harfbuzz",
);
LD_CLASSIC_1000 = "";
LD_CLASSIC_1100 = "";
LD_CLASSIC_1200 = "";
LD_CLASSIC_1300 = "";
LD_CLASSIC_1400 = "";
LD_CLASSIC_1500 = "-ld_classic";
LD_RUNPATH_SEARCH_PATHS = "@loader_path/../Frameworks";
LIBRARY_SEARCH_PATHS = "$(PROJECT_DIR)/lib";
MACOSX_DEPLOYMENT_TARGET = 10.12;
"LD_CLASSIC_1000" = "";
"LD_CLASSIC_1100" = "";
"LD_CLASSIC_1200" = "";
"LD_CLASSIC_1300" = "";
"LD_CLASSIC_1400" = "";
"LD_CLASSIC_1500" = "-ld_classic";
OTHER_LDFLAGS = (
"-lz",
"-lbz2",

View File

@ -19,6 +19,7 @@ tests/test_map_location.cpp
tests/test_mp_connect.cpp
tests/test_recall_list.cpp
tests/test_rng.cpp
tests/test_sdl.cpp
tests/test_serialization.cpp
tests/test_simple_wml.cpp
tests/test_team.cpp

View File

@ -395,29 +395,28 @@ surface scale_surface_legacy(const surface &surf, int w, int h)
surface scale_surface_sharp(const surface& surf, int w, int h)
{
// Since SDL version 1.1.5 0 is transparent, before 255 was transparent.
assert(SDL_ALPHA_TRANSPARENT==0);
assert(SDL_ALPHA_TRANSPARENT == 0);
if(surf == nullptr)
if(surf == nullptr) {
return nullptr;
}
if(w == surf->w && h == surf->h) {
return surf;
}
assert(w >= 0);
assert(h >= 0);
surface dst(w,h);
if (w == 0 || h ==0) {
PLAIN_LOG << "Create an empty image";
return dst;
}
if(surf == nullptr || dst == nullptr) {
surface dst(w, h);
if(dst == nullptr) {
PLAIN_LOG << "Could not create surface to scale onto";
return nullptr;
}
if(w == 0 || h == 0) {
PLAIN_LOG << "Creating an empty image";
return dst;
}
{
const_surface_lock src_lock(surf);
surface_lock dst_lock(dst);
@ -425,56 +424,18 @@ surface scale_surface_sharp(const surface& surf, int w, int h)
const uint32_t* const src_pixels = src_lock.pixels();
uint32_t* const dst_pixels = dst_lock.pixels();
float xratio = static_cast<float>(surf->w) / w;
float yratio = static_cast<float>(surf->h) / h;
const int src_w = surf->w;
const int src_h = surf->h;
float ysrc = 0.0f;
for(int ydst = 0; ydst != h; ++ydst, ysrc += yratio) {
float xsrc = 0.0f;
for(int xdst = 0; xdst != w; ++xdst, xsrc += xratio) {
float red = 0.0f, green = 0.0f, blue = 0.0f, alpha = 0.0f;
float summation = 0.0f;
// We now have a rectangle, (xsrc,ysrc,xratio,yratio)
// which we want to derive the pixel from
for(float xloc = xsrc; xloc < xsrc+xratio; xloc += 1) {
const float xsize = std::min<float>(std::floor(xloc+1)-xloc,xsrc+xratio-xloc);
for(float yloc = ysrc; yloc < ysrc+yratio; yloc += 1) {
const int xsrcint = std::max<int>(0,std::min<int>(surf->w-1,static_cast<int>(xsrc)));
const int ysrcint = std::max<int>(0,std::min<int>(surf->h-1,static_cast<int>(ysrc)));
const float ysize = std::min<float>(std::floor(yloc+1)-yloc,ysrc+yratio-yloc);
uint8_t r,g,b,a;
SDL_GetRGBA(src_pixels[ysrcint*surf->w + xsrcint],surf->format,&r,&g,&b,&a);
float value = xsize * ysize;
summation += value;
if (!a) continue;
value *= a;
alpha += value;
red += r * value;
green += g * value;
blue += b * value;
}
}
if (alpha != 0) {
red = red / alpha + 0.5f;
green = green / alpha + 0.5f;
blue = blue / alpha + 0.5f;
alpha = alpha / summation + 0.5f;
}
dst_pixels[ydst*dst->w + xdst] = SDL_MapRGBA(
dst->format
, static_cast<uint8_t>(red)
, static_cast<uint8_t>(green)
, static_cast<uint8_t>(blue)
, static_cast<uint8_t>(alpha));
const float xratio = static_cast<float>(src_w) / static_cast<float>(w);
const float yratio = static_cast<float>(src_h) / static_cast<float>(h);
for(int ydst = 0; ydst != h; ++ydst) {
for(int xdst = 0; xdst != w; ++xdst) {
// Project dst pixel to a single corresponding src pixel by scale and simply take it
const int xsrc = std::floor(static_cast<float>(xdst) * xratio);
const int ysrc = std::floor(static_cast<float>(ydst) * yratio);
dst_pixels[ydst * dst->w + xdst] = src_pixels[ysrc * src_w + xsrc];
}
}
}

108
src/tests/test_sdl.cpp Normal file
View File

@ -0,0 +1,108 @@
/*
Copyright (C) 2023
Part of the Battle for Wesnoth Project https://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
#define GETTEXT_DOMAIN "wesnoth-test"
#include "sdl/surface.hpp"
#include "sdl/utils.hpp"
#include <algorithm>
#include <array>
#include <boost/test/unit_test.hpp>
constexpr uint32_t red = 0xFF'FF'00'00;
constexpr uint32_t green = 0xFF'00'FF'00;
constexpr uint32_t blue = 0xFF'00'00'FF;
constexpr uint32_t yellow = 0xFF'FF'FF'00;
constexpr uint32_t white = 0xFF'FF'FF'FF;
constexpr uint32_t black = 0xFF'00'00'00;
constexpr std::array<uint32_t, 16> img_4x4 {
red, white, green, black,
black, black, black, black,
blue, white, yellow, black,
black, black, black, black,
};
constexpr std::array<uint32_t, 4> img_4x4_to_2x2_result {
red, green,
blue, yellow,
};
constexpr std::array<uint32_t, 6> img_4x4_to_3x2_result {
red, white, green,
blue, white, yellow
};
template<size_t w, size_t h>
surface array_to_surface(const std::array<uint32_t, w * h>& arr)
{
surface surf{w, h};
{
surface_lock surf_lock{surf};
uint32_t* const pixels = surf_lock.pixels();
for(size_t i = 0; i < w * h; ++i) {
pixels[i] = arr[i];
}
}
return surf;
}
std::vector<uint32_t> surface_to_vec(const surface& surf)
{
const_surface_lock lock{surf};
const uint32_t* const pixels = lock.pixels();
std::vector<uint32_t> pixel_vec;
const int surf_size = surf->w * surf->h;
std::copy(pixels, pixels + surf_size, std::back_inserter(pixel_vec));
return pixel_vec;
}
BOOST_AUTO_TEST_SUITE(sdl)
BOOST_AUTO_TEST_CASE(test_scale_sharp_nullptr)
{
surface result = scale_surface_sharp(nullptr, 2, 2);
BOOST_CHECK_EQUAL(result, nullptr);
}
BOOST_AUTO_TEST_CASE(test_scale_sharp_zero)
{
surface src = array_to_surface<4, 4>(img_4x4);
surface result = scale_surface_sharp(src, 0, 0);
BOOST_CHECK_EQUAL(result->w, 0);
BOOST_CHECK_EQUAL(result->h, 0);
}
BOOST_AUTO_TEST_CASE(test_scale_sharp_round)
{
surface src = array_to_surface<4, 4>(img_4x4);
surface result = scale_surface_sharp(src, 2, 2);
std::vector<uint32_t> result_pixels = surface_to_vec(result);
BOOST_CHECK_EQUAL_COLLECTIONS(
result_pixels.begin(), result_pixels.end(), img_4x4_to_2x2_result.begin(), img_4x4_to_2x2_result.end());
}
BOOST_AUTO_TEST_CASE(test_scale_sharp_fractional)
{
surface src = array_to_surface<4, 4>(img_4x4);
surface result = scale_surface_sharp(src, 3, 2);
std::vector<uint32_t> result_pixels = surface_to_vec(result);
BOOST_CHECK_EQUAL_COLLECTIONS(
result_pixels.begin(), result_pixels.end(), img_4x4_to_3x2_result.begin(), img_4x4_to_3x2_result.end());
}
BOOST_AUTO_TEST_SUITE_END()