summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorStanislaw Halik <sthalik@misaki.pl>2023-02-08 21:00:22 +0100
committerStanislaw Halik <sthalik@misaki.pl>2023-02-08 21:00:22 +0100
commit55809dbf9b750b24321f72a0394b432316943a66 (patch)
tree19b42caef085b7c2da6ebabcfa066215eec108e3
parent1a3a4c6c03e6d975f059c5eb3271006eb4b5a926 (diff)
{src,serialize}/anim, crop-tool: add more scaling modes
-rw-r--r--anim-crop-tool/main.cpp56
-rw-r--r--serialize/anim.cpp42
-rw-r--r--serialize/anim.hpp6
-rw-r--r--src/anim.cpp33
-rw-r--r--src/anim.hpp19
5 files changed, 119 insertions, 37 deletions
diff --git a/anim-crop-tool/main.cpp b/anim-crop-tool/main.cpp
index 4e7dbbc4..e4878480 100644
--- a/anim-crop-tool/main.cpp
+++ b/anim-crop-tool/main.cpp
@@ -35,9 +35,9 @@ using floormat::Serialize::anim_atlas_;
struct options
{
- double scale = 0;
String input_dir, input_file, output_dir;
- std::size_t width = 0, height = 0, nframes = 0;
+ std::size_t nframes = 0;
+ anim_scale scale;
};
[[nodiscard]]
@@ -91,20 +91,11 @@ static bool load_file(anim_group& group, options& opts, anim_atlas_& atlas, Stri
cv::Size size{end - start};
- if (opts.scale == 0.0)
- {
- fm_assert(opts.width || opts.height);
- if (opts.width)
- opts.scale = (double)opts.width / size.width;
- else
- opts.scale = (double)opts.height / size.height;
- fm_assert(opts.scale > 1e-6);
- }
-
- const cv::Size dest_size = {
- (int)std::round(opts.scale * size.width),
- (int)std::round(opts.scale * size.height)
- };
+ const auto dest_size = fm_begin(
+ auto xy = opts.scale.scale_to({(unsigned)size.width, (unsigned)size.height});
+ return cv::Size{(int)xy[0], (int)xy[1]};
+ );
+ const auto factor = (float)dest_size.width / (float)size.width;
if (size.width < dest_size.width || size.height < dest_size.height)
{
@@ -116,8 +107,8 @@ static bool load_file(anim_group& group, options& opts, anim_atlas_& atlas, Stri
cv::resize(mat({start, size}), resized, dest_size, 0, 0, cv::INTER_LANCZOS4);
const Vector2i ground = {
- (int)std::round(((int)group.ground[0] - start[0]) * opts.scale),
- (int)std::round(((int)group.ground[1] - start[1]) * opts.scale),
+ (int)std::round(((int)group.ground[0] - start[0]) * factor),
+ (int)std::round(((int)group.ground[1] - start[1]) * factor),
};
const Vector2ui dest_size_ = { (unsigned)dest_size.width, (unsigned)dest_size.height };
@@ -207,13 +198,19 @@ static std::tuple<options, Arguments, bool> parse_cmdline(int argc, const char*
args.addOption('o', "output")
.addArgument("input")
.addOption('W', "width", "")
- .addOption('H', "height", "");
+ .addOption('H', "height", "")
+ .addOption('F', "scale", "");
+
args.parse(argc, argv);
options opts;
- if (auto w = args.value<unsigned>("width"); w != 0)
- opts.width = w;
- if (auto h = args.value<unsigned>("height"); h != 0)
- opts.height = h;
+
+ if (!args.value<StringView>("width").isEmpty())
+ opts.scale = { { .f = {true, args.value<unsigned>("width")} }, anim_scale_type::fixed };
+ else if (!args.value<StringView>("height").isEmpty())
+ opts.scale = { { .f = {false, args.value<unsigned>("height")} }, anim_scale_type::fixed };
+ else if (!args.value<StringView>("scale").isEmpty())
+ opts.scale = { { .r = {args.value<float>("scale")} } , anim_scale_type::ratio };
+
opts.output_dir = args.value<StringView>("output");
opts.input_file = args.value<StringView>("input");
opts.input_dir = Path::split(opts.input_file).first();
@@ -263,18 +260,9 @@ int main(int argc, char** argv)
return EX_DATAERR;
}
- if (!opts.width)
- opts.width = anim_info.width;
- if (!opts.height)
- opts.height = anim_info.height;
opts.nframes = anim_info.nframes;
-
- if (!(opts.width ^ opts.height))
- {
- Error{} << "error: exactly one of --width, --height must be specified";
- return usage(args);
- }
-
+ if (opts.scale.type == anim_scale_type::invalid)
+ opts.scale = anim_info.scale;
anim_atlas_ atlas;
for (anim_group& group : anim_info.groups)
diff --git a/serialize/anim.cpp b/serialize/anim.cpp
index fd5e7315..b9cfe975 100644
--- a/serialize/anim.cpp
+++ b/serialize/anim.cpp
@@ -2,19 +2,57 @@
#include "serialize/magnum-vector2i.hpp"
#include "serialize/corrade-string.hpp"
#include "serialize/anim.hpp"
+#include <tuple>
namespace floormat {
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(anim_frame, ground, offset, size)
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(anim_group, name, frames, ground, offset)
-NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(anim_def, object_name, anim_name, pixel_size, nframes, actionframe, fps, groups, width, height)
+NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(anim_def, object_name, anim_name, pixel_size, nframes, actionframe, fps, groups, scale)
-} // namespace floormat::Serialize
+} // namespace floormat
using namespace floormat;
namespace nlohmann {
+void adl_serializer<floormat::anim_scale>::to_json(json& j, const floormat::anim_scale val)
+{
+ switch (val.type)
+ {
+ default:
+ fm_throw("invalid anim_scale_type '{}"_cf, (unsigned)val.type);
+ case anim_scale_type::invalid:
+ fm_throw("anim_scale is invalid"_cf);
+ case anim_scale_type::fixed:
+ j = std::tuple<StringView, unsigned>{val.f.is_width ? "width"_s : "height"_s, val.f.width_or_height};
+ break;
+ case anim_scale_type::ratio:
+ j = std::tuple<StringView, float>{"factor"_s, val.r.f};
+ break;
+ }
+}
+
+void adl_serializer<floormat::anim_scale>::from_json(const json& j, floormat::anim_scale& val)
+{
+ fm_soft_assert(j.is_array());
+ fm_soft_assert(j.size() == 2);
+ String type = j[0];
+ if (type == "factor"_s)
+ {
+ auto factor = (float)j[1];
+ fm_soft_assert(factor > 0 && factor <= 1);
+ val = { { .r = {factor} }, anim_scale_type::ratio };
+ }
+ else if (bool is_width = type == "width"_s; is_width || type == "height"_s)
+ {
+ val = { { .f = {is_width, (unsigned)j[1]} }, anim_scale_type::fixed };
+ fm_soft_assert(val.f.width_or_height > 0);
+ }
+ else
+ fm_throw("invalid anim_scale_type '{}'"_cf, type);
+}
+
void adl_serializer<anim_frame>::to_json(json& j, const anim_frame& val) { using nlohmann::to_json; to_json(j, val); }
void adl_serializer<anim_frame>::from_json(const json& j, anim_frame& val) { using nlohmann::from_json; from_json(j, val); }
diff --git a/serialize/anim.hpp b/serialize/anim.hpp
index 48d97451..6cfa0c75 100644
--- a/serialize/anim.hpp
+++ b/serialize/anim.hpp
@@ -18,6 +18,12 @@ struct adl_serializer<floormat::anim_group> {
};
template<>
+struct adl_serializer<floormat::anim_scale> {
+ static void to_json(json& j, floormat::anim_scale val);
+ static void from_json(const json& j, floormat::anim_scale& val);
+};
+
+template<>
struct adl_serializer<floormat::anim_def> {
static void to_json(json& j, const floormat::anim_def& val);
static void from_json(const json& j, floormat::anim_def& val);
diff --git a/src/anim.cpp b/src/anim.cpp
new file mode 100644
index 00000000..f4850deb
--- /dev/null
+++ b/src/anim.cpp
@@ -0,0 +1,33 @@
+#include "anim.hpp"
+#include "compat/exception.hpp"
+#include <cmath>
+
+namespace floormat {
+
+Vector2ui anim_scale::scale_to(Vector2ui image_size) const
+{
+ fm_soft_assert(image_size.product() > 0);
+ Vector2ui ret;
+ switch (type)
+ {
+ default:
+ fm_throw("invalid anim_scale_type '{}'"_cf, (unsigned)type);
+ case anim_scale_type::invalid:
+ fm_throw("anim_scale is invalid"_cf);
+ case anim_scale_type::fixed:
+ fm_soft_assert(f.width_or_height > 0);
+ if (f.is_width)
+ ret = { f.width_or_height, (unsigned)std::round((float)f.width_or_height * (float)image_size.y()/(float)image_size.x()) };
+ else
+ ret = { (unsigned)std::round((float)f.width_or_height * (float)image_size.x()/(float)image_size.y()), f.width_or_height };
+ break;
+ case anim_scale_type::ratio:
+ fm_soft_assert(r.f > 0 && r.f <= 1);
+ ret = { (unsigned)std::round(image_size.x() * r.f), (unsigned)std::round(image_size.y() * r.f) };
+ break;
+ }
+ fm_soft_assert(ret.product() > 0);
+ return ret;
+}
+
+} // namespace floormat
diff --git a/src/anim.hpp b/src/anim.hpp
index 674895ed..a8fb63a3 100644
--- a/src/anim.hpp
+++ b/src/anim.hpp
@@ -28,6 +28,22 @@ struct anim_group final
Vector3b offset;
};
+enum class anim_scale_type : std::uint8_t { invalid, ratio, fixed, };
+
+struct anim_scale final
+{
+ struct ratio { float f; };
+ struct fixed { bool is_width; unsigned width_or_height; };
+ struct empty {};
+ union {
+ struct ratio r;
+ struct fixed f;
+ struct empty e = {};
+ };
+ anim_scale_type type = anim_scale_type::invalid;
+ Vector2ui scale_to(Vector2ui image_size) const;
+};
+
struct anim_def final
{
static constexpr int default_fps = 24;
@@ -35,7 +51,8 @@ struct anim_def final
String object_name, anim_name;
std::vector<anim_group> groups;
Vector2ui pixel_size;
- std::size_t nframes = 0, width = 0, height = 0, fps = default_fps, actionframe = 0;
+ anim_scale scale;
+ std::size_t nframes = 0, fps = default_fps, actionframe = 0;
};
} // namespace floormat