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 /anim | |
parent | b0925a6a5e16dce10cb9b5bd99b62f96b2953441 (diff) |
.
Diffstat (limited to 'anim')
-rw-r--r-- | anim/CMakeLists.txt | 9 | ||||
-rw-r--r-- | anim/atlas.cpp | 61 | ||||
-rw-r--r-- | anim/atlas.hpp | 36 | ||||
-rw-r--r-- | anim/serialize.cpp | 129 | ||||
-rw-r--r-- | anim/serialize.hpp | 41 |
5 files changed, 276 insertions, 0 deletions
diff --git a/anim/CMakeLists.txt b/anim/CMakeLists.txt new file mode 100644 index 00000000..ae4bc542 --- /dev/null +++ b/anim/CMakeLists.txt @@ -0,0 +1,9 @@ +find_package(OpenCV QUIET REQUIRED COMPONENTS core imgcodecs imgproc) + +file(GLOB sources "*.cpp" CONFIGURE_ARGS) +set(self "${PROJECT_NAME}-anim") +add_library(${self} STATIC ${sources}) + +target_include_directories(${self} SYSTEM PRIVATE ${OpenCV_INCLUDE_DIRS}) +target_link_libraries(${self} opencv_imgproc opencv_imgcodecs opencv_core) +target_link_libraries(${self} Magnum::Magnum) diff --git a/anim/atlas.cpp b/anim/atlas.cpp new file mode 100644 index 00000000..ca05cd15 --- /dev/null +++ b/anim/atlas.cpp @@ -0,0 +1,61 @@ +#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/anim/atlas.hpp b/anim/atlas.hpp new file mode 100644 index 00000000..dd6efabc --- /dev/null +++ b/anim/atlas.hpp @@ -0,0 +1,36 @@ +#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; + 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/anim/serialize.cpp b/anim/serialize.cpp new file mode 100644 index 00000000..2a0b5a45 --- /dev/null +++ b/anim/serialize.cpp @@ -0,0 +1,129 @@ +#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]); +} + +#if 0 +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); +} +#endif + +} // 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/anim/serialize.hpp b/anim/serialize.hpp new file mode 100644 index 00000000..10fd9e18 --- /dev/null +++ b/anim/serialize.hpp @@ -0,0 +1,41 @@ +#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; +}; |