diff options
-rw-r--r-- | anim-crop-tool/main.cpp | 56 | ||||
-rw-r--r-- | serialize/anim.cpp | 42 | ||||
-rw-r--r-- | serialize/anim.hpp | 6 | ||||
-rw-r--r-- | src/anim.cpp | 33 | ||||
-rw-r--r-- | src/anim.hpp | 19 |
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 |