diff options
| author | Stanislaw Halik <sthalik@misaki.pl> | 2022-06-11 10:56:22 +0200 |
|---|---|---|
| committer | Stanislaw Halik <sthalik@misaki.pl> | 2022-06-11 10:56:22 +0200 |
| commit | 954c223d3276ae312274e9f0a3c47396b8cf9d60 (patch) | |
| tree | d9b54e3f210bcc073e44a4809298a650a94fdb52 /crop-tool | |
| parent | b0925a6a5e16dce10cb9b5bd99b62f96b2953441 (diff) | |
.
Diffstat (limited to 'crop-tool')
| -rw-r--r-- | crop-tool/CMakeLists.txt | 12 | ||||
| -rw-r--r-- | crop-tool/atlas.cpp | 61 | ||||
| -rw-r--r-- | crop-tool/atlas.hpp | 36 | ||||
| -rw-r--r-- | crop-tool/crop-tool.cpp | 58 | ||||
| -rw-r--r-- | crop-tool/serialize.cpp | 127 | ||||
| -rw-r--r-- | crop-tool/serialize.hpp | 41 |
6 files changed, 35 insertions, 300 deletions
diff --git a/crop-tool/CMakeLists.txt b/crop-tool/CMakeLists.txt index cc38c51b..974a07e9 100644 --- a/crop-tool/CMakeLists.txt +++ b/crop-tool/CMakeLists.txt @@ -1,6 +1,8 @@ -find_package(OpenCV QUIET REQUIRED COMPONENTS core imgcodecs imgproc highgui) +find_package(OpenCV QUIET REQUIRED COMPONENTS core imgcodecs imgproc) +set(self "${PROJECT_NAME}-crop-tool") file(GLOB sources "*.cpp" CONFIGURE_ARGS) -add_executable(crop-tool ${sources}) -target_include_directories(crop-tool SYSTEM PRIVATE ${OpenCV_INCLUDE_DIRS}) -target_link_libraries(crop-tool opencv_highgui opencv_imgproc opencv_imgcodecs opencv_core) -target_link_libraries(crop-tool Corrade::Utility Magnum::Trade) + +add_executable(${self} ${sources}) +target_include_directories(${self} SYSTEM PRIVATE ${OpenCV_INCLUDE_DIRS}) +target_link_libraries(${self} Corrade::Utility) +target_link_libraries(${self} game-anim) diff --git a/crop-tool/atlas.cpp b/crop-tool/atlas.cpp deleted file mode 100644 index ca05cd15..00000000 --- a/crop-tool/atlas.cpp +++ /dev/null @@ -1,61 +0,0 @@ -#include "atlas.hpp" -#include "serialize.hpp" -#include "../defs.hpp" - -#include <filesystem> -#include <opencv2/imgcodecs.hpp> - -void anim_atlas_row::add_entry(const anim_atlas_entry& x) -{ - auto& frame = *x.frame; - const auto& mat = x.mat; - frame.offset = {xpos, ypos}; - frame.size = {mat.cols, mat.rows}; - - ASSERT(mat.rows > 0 && mat.cols > 0); - data.push_back(x); - xpos += mat.cols; - max_height = std::max(mat.rows, max_height); -} - -void anim_atlas::advance_row() -{ - auto& row = rows.back(); - if (row.data.empty()) - return; - ASSERT(row.xpos); ASSERT(row.max_height); - ypos += row.max_height; - maxx = std::max(row.xpos, maxx); - rows.push_back({{}, 0, 0, ypos}); -} - -Magnum::Vector2i anim_atlas::offset() const -{ - const auto& row = rows.back(); - return {row.xpos, row.ypos}; -} - -Magnum::Vector2i anim_atlas::size() const -{ - const anim_atlas_row& row = rows.back(); - // prevent accidentally writing out of bounds by forgetting to call - // anim_atlas::advance_row() one last time prior to anim_atlas::size() - return {std::max(maxx, row.xpos), ypos + row.max_height}; -} - -bool anim_atlas::dump(const std::filesystem::path& filename) const -{ - auto sz = size(); - cv::Mat4b mat(sz[1], sz[0]); - mat.setTo(0); - - for (const anim_atlas_row& row : rows) - for (const anim_atlas_entry& x : row.data) - { - auto offset = x.frame->offset, size = x.frame->size; - cv::Rect roi = {offset[0], offset[1], size[0], size[1]}; - x.mat.copyTo(mat(roi)); - } - - return cv::imwrite(filename.string(), mat); -} diff --git a/crop-tool/atlas.hpp b/crop-tool/atlas.hpp deleted file mode 100644 index 675b6ff9..00000000 --- a/crop-tool/atlas.hpp +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once -#include <vector> -#include <Magnum/Magnum.h> -#include <Magnum/Math/Vector2.h> -#include <opencv2/core/mat.hpp> - -struct anim_frame; - -namespace std::filesystem { class path; } - -struct anim_atlas_entry -{ - anim_frame* frame; - const cv::Mat4b mat; -}; - -struct anim_atlas_row -{ - std::vector<anim_atlas_entry> data; - int max_height = 0, xpos = 0, ypos = 0; - - void add_entry(const anim_atlas_entry& x); -}; - -class anim_atlas -{ - std::vector<anim_atlas_row> rows = {{}}; - int ypos = 0, maxx = 0; - -public: - void add_entry(const anim_atlas_entry& x) { rows.back().add_entry(x); } - void advance_row(); - Magnum::Vector2i offset() const; - Magnum::Vector2i size() const; - [[nodiscard]] bool dump(const std::filesystem::path& filename) const; -}; diff --git a/crop-tool/crop-tool.cpp b/crop-tool/crop-tool.cpp index 35089dd6..099ebfa1 100644 --- a/crop-tool/crop-tool.cpp +++ b/crop-tool/crop-tool.cpp @@ -1,13 +1,12 @@ #include "../defs.hpp" -#include "atlas.hpp" -#include "serialize.hpp" +#include "anim/atlas.hpp" +#include "anim/serialize.hpp" #include <Corrade/Utility/Arguments.h> #include <Corrade/Utility/Debug.h> #include <Corrade/Utility/DebugStl.h> #include <opencv2/core/mat.hpp> #include <opencv2/imgcodecs/imgcodecs.hpp> #include <opencv2/imgproc/imgproc.hpp> -#include <opencv2/highgui.hpp> #include <optional> #include <tuple> #include <filesystem> @@ -35,9 +34,10 @@ using Corrade::Utility::Debug; using std::filesystem::path; -struct options_ { - std::optional<unsigned> width, height; - std::optional<double> scale; +struct options +{ + unsigned width = 0, height = 0; + double scale = 0; path input_dir, output_dir; }; @@ -71,7 +71,7 @@ static std::tuple<cv::Vec2i, cv::Vec2i, bool> find_image_bounds(const path& path } [[nodiscard]] -static bool load_file(anim_group& group, options_& opts, anim_atlas& atlas, const path& filename) +static bool load_file(anim_group& group, options& opts, anim_atlas& atlas, const path& filename) { auto mat = progn( cv::Mat mat_ = cv::imread(filename.string(), cv::IMREAD_UNCHANGED); @@ -93,18 +93,18 @@ static bool load_file(anim_group& group, options_& opts, anim_atlas& atlas, cons cv::Size size{end - start}, dest_size; - if (!opts.scale) + if (opts.scale == 0.0) { + ASSERT(opts.width || opts.height); if (opts.width) - opts.scale = (double)*opts.width / size.width; - else if (opts.height) - opts.scale = (double)*opts.height / size.height; + opts.scale = (double)opts.width / size.width; else - std::abort(); + opts.scale = (double)opts.height / size.height; + ASSERT(opts.scale > 1e-6); } - dest_size = {(int)std::round(*opts.scale * size.width), - (int)std::round(*opts.scale * size.height)}; + dest_size = {(int)std::round(opts.scale * size.width), + (int)std::round(opts.scale * size.height)}; if (size.width < dest_size.width || size.height < dest_size.height) { @@ -122,19 +122,17 @@ static bool load_file(anim_group& group, options_& opts, anim_atlas& atlas, cons } #endif Magnum::Vector2i ground = { - (int)std::round((group.ground[0] - start[0]) * *opts.scale), - (int)std::round((group.ground[1] - start[1]) * *opts.scale), + (int)std::round((group.ground[0] - start[0]) * opts.scale), + (int)std::round((group.ground[1] - start[1]) * opts.scale), }; - auto offset = atlas.offset(); - - group.frames.push_back({ground, offset, {dest_size.width, dest_size.height}}); + group.frames.push_back({ground, atlas.offset(), {dest_size.width, dest_size.height}}); atlas.add_entry({&group.frames.back(), std::move(mat)}); return true; } [[nodiscard]] -static bool load_directory(anim_group& group, options_& opts, anim_atlas& atlas, const path& input_dir) +static bool load_directory(anim_group& group, options& opts, anim_atlas& atlas, const path& input_dir) { if (std::error_code ec{}; !std::filesystem::exists(input_dir/".", ec)) { @@ -184,7 +182,7 @@ static char* fix_argv0(char* argv0) return argv0; } -static std::tuple<options_, int> parse_cmdline(int argc, const char* const* argv) +static std::tuple<options, int> parse_cmdline(int argc, const char* const* argv) { Corrade::Utility::Arguments args{}; args.addOption('o', "output") @@ -192,23 +190,23 @@ static std::tuple<options_, int> parse_cmdline(int argc, const char* const* argv .addOption('W', "width", "") .addOption('H', "height", ""); args.parse(argc, argv); - options_ options; + options opts; if (unsigned w = args.value<unsigned>("width"); w != 0) - options.width = w; + opts.width = w; if (unsigned h = args.value<unsigned>("height"); h != 0) - options.height = h; - if (!(!options.width ^ !options.height)) + opts.height = h; + if (!(!opts.width ^ !opts.height)) { Error{} << "exactly one of --width, --height must be given"; goto usage; } - options.output_dir = args.value<std::string>("output"); - options.input_dir = args.value<std::string>("directory"); + opts.output_dir = args.value<std::string>("output"); + opts.input_dir = args.value<std::string>("directory"); - if (options.output_dir.empty()) - options.output_dir = options.input_dir; + if (opts.output_dir.empty()) + opts.output_dir = opts.input_dir; - return {options, 0}; + return { opts, 0}; usage: Error{Error::Flag::NoNewlineAtTheEnd} << Corrade::Containers::StringView{args.usage()}; return {{}, EX_USAGE}; diff --git a/crop-tool/serialize.cpp b/crop-tool/serialize.cpp deleted file mode 100644 index acefe436..00000000 --- a/crop-tool/serialize.cpp +++ /dev/null @@ -1,127 +0,0 @@ -#include "serialize.hpp" -#include "../json.hpp" - -#include <algorithm> -#include <utility> -#include <fstream> -#include <Corrade/Utility/Debug.h> -#include <Corrade/Utility/DebugStl.h> - -using Corrade::Utility::Debug; -using Corrade::Utility::Error; - -static constexpr -std::pair<anim_direction, const char*> anim_direction_map[] = { - { anim_direction::N, "n" }, - { anim_direction::NE, "ne" }, - { anim_direction::E, "e" }, - { anim_direction::SE, "se" }, - { anim_direction::S, "s" }, - { anim_direction::SW, "sw" }, - { anim_direction::W, "w" }, - { anim_direction::NW, "nw" }, -}; - -const char* anim_group::direction_to_string(anim_direction group) -{ - auto it = std::find_if(std::cbegin(anim_direction_map), std::cend(anim_direction_map), - [=](const auto& pair) { return group == pair.first; }); - if (it != std::cend(anim_direction_map)) - return it->second; - else - return "(unknown)"; -} - -anim_direction anim_group::string_to_direction(const std::string& str) -{ - auto it = std::find_if(std::cbegin(anim_direction_map), std::cend(anim_direction_map), - [&](const auto& pair) { return str == pair.second; }); - if (it != std::cend(anim_direction_map)) - return it->first; - else - return (anim_direction)0; -} - -namespace nlohmann { - -template<> -struct adl_serializer<anim_direction> final { - static void to_json(json& j, anim_direction x); - static void from_json(const json& j, anim_direction& x); -}; - -template<> -struct adl_serializer<Magnum::Vector2i> final { - static void to_json(json& j, const Magnum::Vector2i& x); - static void from_json(const json& j, Magnum::Vector2i& x); -}; - -void adl_serializer<Magnum::Vector2i>::to_json(json& j, const Magnum::Vector2i& x) -{ - j["x"] = x[0]; - j["y"] = x[1]; -} - -void adl_serializer<Magnum::Vector2i>::from_json(const json& j, Magnum::Vector2i& x) -{ - j.at("x").get_to(x[0]); - j.at("y").get_to(x[1]); -} - -void adl_serializer<anim_direction>::to_json(json& j, anim_direction x) -{ - j = anim_group::direction_to_string(x); -} - -void adl_serializer<anim_direction>::from_json(const json& j, anim_direction& x) -{ - x = anim_group::string_to_direction(j); -} - -} // namespace nlohmann - -NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(anim_frame, ground, offset, size); -NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(anim_group, name, frames, ground); -NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(anim, name, nframes, actionframe, fps, groups); - -std::optional<anim> anim::from_json(const std::filesystem::path& pathname) -{ - using namespace nlohmann; - std::ifstream s; - s.exceptions(s.exceptions() | std::ios::failbit | std::ios::badbit); - try { - s.open(pathname, std::ios_base::in); - } catch (const std::ios::failure& e) { - Error{} << "failed to open" << pathname << ':' << e.what(); - return std::nullopt; - } - anim ret; - try { - json j; - s >> j; - using nlohmann::from_json; - from_json(j, ret); - } catch (const std::exception& e) { - Error{} << "failed to parse" << pathname << ':' << e.what(); - return std::nullopt; - } - return std::make_optional(std::move(ret)); -} - -bool anim::to_json(const std::filesystem::path& pathname) -{ - nlohmann::json j = *this; - - std::ofstream s; - s.exceptions(s.exceptions() | std::ios::failbit | std::ios::badbit); - try { - s.open(pathname, std::ios_base::out | std::ios_base::trunc); - } catch (const std::ios::failure& e) { - Error{} << "failed to open" << pathname << "for writing:" << e.what(); - return false; - } - s << j.dump(4); - s.flush(); - - return true; -} diff --git a/crop-tool/serialize.hpp b/crop-tool/serialize.hpp deleted file mode 100644 index 10fd9e18..00000000 --- a/crop-tool/serialize.hpp +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -#include "../defs.hpp" -#include <string> -#include <array> -#include <vector> -#include <optional> -#include <filesystem> -#include <Magnum/Trade/ImageData.h> - -struct anim_frame final -{ - Magnum::Vector2i ground, offset, size; -}; - -enum class anim_direction : unsigned char -{ - N, NE, E, SE, S, SW, W, NW, - COUNT = NW + 1, -}; - -struct anim_group final -{ - std::string name; - std::vector<anim_frame> frames; - Magnum::Vector2i ground; - - static const char* direction_to_string(anim_direction group); - static anim_direction string_to_direction(const std::string& str); -}; - -struct anim final -{ - static std::optional<anim> from_json(const std::filesystem::path& pathname); - bool to_json(const std::filesystem::path& pathname); - static constexpr int default_fps = 24; - - std::string name; - std::array<anim_group, (std::size_t)anim_direction::COUNT> groups; - int nframes = 0, actionframe = -1, fps = default_fps; -}; |
