summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
m---------corrade0
-rw-r--r--crop-tool/crop-tool.cpp139
-rw-r--r--crop-tool/serialize.cpp132
-rw-r--r--crop-tool/serialize.hpp27
-rw-r--r--doc/atlas.json18
-rw-r--r--doc/atlas.json.example18
m---------magnum0
m---------magnum-plugins0
8 files changed, 190 insertions, 144 deletions
diff --git a/corrade b/corrade
-Subproject d44ca683c12ed28fce1d27662f24edee2d7e2c6
+Subproject 9f559f59af07fee2a36b1377b6a7a1e8a6e6e81
diff --git a/crop-tool/crop-tool.cpp b/crop-tool/crop-tool.cpp
index 6de44a96..0e92946b 100644
--- a/crop-tool/crop-tool.cpp
+++ b/crop-tool/crop-tool.cpp
@@ -21,32 +21,18 @@
#ifdef _WIN32
# define EX_OK 0 /* successful termination */
# define EX_USAGE 64 /* command line usage error */
+# define EX_DATAERR 65 /* data format error */
# define EX_SOFTWARE 70 /* internal software error */
+# define EX_CANTCREAT 73 /* can't create (user) output file */
# define EX_IOERR 74 /* input/output error */
#else
# include <sysexits.h>
#endif
-#if 0
-static std::string fix_path_separators(const std::filesystem::path& path)
-{
- auto str = path.string();
- std::replace(str.begin(), str.end(), '\\', '/');
- return str;
-}
-#endif
-
struct file
{
- std::filesystem::path name;
cv::Mat4b mat;
- cv::Point2i offset;
-};
-
-struct dir
-{
- std::filesystem::path name;
- std::vector<file> files;
+ Magnum::Vector2i ground_offset;
};
using Corrade::Utility::Error;
@@ -55,12 +41,13 @@ using Corrade::Utility::Debug;
static struct options_ {
std::optional<unsigned> width, height;
std::optional<double> scale;
- cv::Vec2i offset{-1, -1};
} options;
+using std::filesystem::path;
+
static
std::tuple<cv::Vec2i, cv::Vec2i, bool>
-find_image_bounds(const std::filesystem::path& path, const cv::Mat4b& mat)
+find_image_bounds(const path& path, const cv::Mat4b& mat)
{
cv::Vec2i start{mat.cols, mat.rows}, end{0, 0};
for (int y = 0; y < mat.rows; y++)
@@ -81,27 +68,27 @@ find_image_bounds(const std::filesystem::path& path, const cv::Mat4b& mat)
}
if (start[0] >= end[0] || start[1] >= end[1])
{
- Error{} << "image" << path.string() << "contains only fully transparent pixels!";
+ Error{} << "image" << path << "contains only fully transparent pixels!";
return {{}, {}, false};
}
return {start, end, true};
}
-static std::optional<file> load_file(const std::filesystem::path& filename)
+static bool load_file(anim_group& group, const path& filename, const path& output_filename)
{
auto mat = progn(
cv::Mat mat_ = cv::imread(filename.string(), cv::IMREAD_UNCHANGED);
if (mat_.empty() || mat_.type() != CV_8UC4)
{
- Error{} << "failed to load" << filename.string() << "as RGBA32 image";
+ Error{} << "failed to load" << filename << "as RGBA32 image";
return cv::Mat4b{};
}
return cv::Mat4b(std::move(mat_));
);
if (mat.empty())
- return {};
+ return false;
auto [start, end, bounds_ok] = find_image_bounds(filename, mat);
@@ -120,55 +107,56 @@ static std::optional<file> load_file(const std::filesystem::path& filename)
std::abort();
}
- cv::Vec2i offset = {
- (int)std::round((options.offset[0] - start[0]) * *options.scale),
- (int)std::round((options.offset[1] - start[1]) * *options.scale),
- };
-
dest_size = {(int)std::round(*options.scale * size.width),
(int)std::round(*options.scale * size.height)};
if (size.width < dest_size.width || size.height < dest_size.height)
{
- Error{} << "refusing to upscale image" << filename.string();
+ Error{} << "refusing to upscale image" << filename;
return {};
}
- Debug{} << "file" << filename.string() << offset[0] << offset[1];
-
cv::Mat4b resized{size};
cv::resize(mat({start, size}), resized, dest_size, 0, 0, cv::INTER_LANCZOS4);
- file ret { filename, resized.clone(), offset };
- return std::make_optional(std::move(ret));
+ if (!cv::imwrite(output_filename.string(), resized))
+ {
+ Error{} << "failed writing image" << output_filename;
+ return false;
+ }
+ Magnum::Vector2i ground = {
+ (int)std::round((group.ground[0] - start[0]) * *options.scale),
+ (int)std::round((group.ground[1] - start[1]) * *options.scale),
+ };
+ group.frames.push_back({ground});
+ return true;
}
-static std::optional<dir> load_directory(const std::filesystem::path& dirname)
+static bool load_directory(anim_group& group, const path& dirname, const path& output_dir)
{
if (std::error_code ec{}; !std::filesystem::exists(dirname / ".", ec))
{
- Error{} << "can't open directory" << dirname.string() << ":" << ec.message();
- return std::nullopt;
+ Error{} << "can't open directory" << dirname << ':' << ec.message();
+ return {};
}
- dir ret;
- for (int i = 1; i <= 9999; i++)
+ int i;
+ for (i = 1; i <= 9999; i++)
{
char buf[9];
sprintf(buf, "%04d.png", i);
- auto path = dirname / buf;
- if (!std::filesystem::exists(path))
+ if (!std::filesystem::exists(dirname/buf))
break;
- auto file = load_file(path);
- if (!file)
- return std::nullopt;
- ret.files.push_back(std::move(*file));
+ if (!load_file(group, dirname/buf, output_dir/buf))
+ return false;
}
- if (ret.files.empty())
+
+ if (i == 1)
{
- Error{} << "directory" << dirname.string() << "is empty!";
- return std::nullopt;
+ Error{} << "no files in anim group directory" << dirname;
+ return false;
}
- return std::make_optional(std::move(ret));
+
+ return true;
}
int main(int argc, char** argv)
@@ -188,14 +176,12 @@ int main(int argc, char** argv)
args.addOption('o', "output", "./output")
.addArgument("directory")
.addOption('W', "width", "")
- .addOption('H', "height", "")
- .addOption('x', "offset")
- .setHelp("offset", {}, "WxH");
+ .addOption('H', "height", "");
args.parse(argc, argv);
- std::filesystem::path output = args.value<std::string>("output");
- std::filesystem::path pathname = args.value<std::string>("directory");
- std::vector<dir> dirs;
- auto anim_info = anim::from_json(pathname / "atlas.json");
+ const path output_dir = args.value<std::string>("output");
+ const path input_dir = args.value<std::string>("directory");
+ auto anim_info = anim::from_json(input_dir / "atlas.json");
+ //std::vector<dir> dirs; dirs.reserve((std::size_t)anim_direction::COUNT);
if (!anim_info)
goto usage;
@@ -210,34 +196,33 @@ int main(int argc, char** argv)
goto usage;
}
- {
- auto str = args.value<std::string>("offset");
- if (str.empty())
- {
- Error{} << "offset argument is required";
- goto usage;
- }
- int x, y;
- int ret = std::sscanf(str.c_str(), "%dx%d", &x, &y);
- if (ret != 2)
- {
- Error{} << "can't parse offset --" << str;
- goto usage;
- }
- options.offset = {x, y};
+ try {
+ std::filesystem::create_directory(output_dir);
+ } catch (const std::filesystem::filesystem_error& error) {
+ Error{} << "failed to create output directory" << output_dir << ':' << error.what();
+ return EX_CANTCREAT;
}
- using anim_dir_t = std::underlying_type_t<anim_direction>;
- for (auto i = (anim_dir_t)anim_direction::MIN; i < (anim_dir_t)anim_direction::MAX; i++)
+ for (std::size_t i = 0; i < (std::size_t)anim_direction::COUNT; i++)
{
- auto name = anim_direction_group::anim_direction_string((anim_direction)i);
- auto result = load_directory(pathname / name);
- if (!result)
- goto usage;
- dirs.push_back(std::move(*result));
+ auto group_name = anim_group::direction_to_string((anim_direction)i);
+ try {
+ std::filesystem::remove_all(output_dir/group_name);
+ std::filesystem::create_directory(output_dir/group_name);
+ } catch (const std::filesystem::filesystem_error& e) {
+ Error{} << "failed creating output directory" << group_name << ':' << e.what();
+ return EX_CANTCREAT;
+ }
+ auto& group = anim_info->groups[i];
+ group.frames.clear(); group.frames.reserve(64);
+ if (!load_directory(group, input_dir/group_name, input_dir/group_name))
+ return EX_DATAERR;
+ if (!anim_info->to_json(output_dir/"atlas.json"))
+ return EX_CANTCREAT;
}
return 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
index 8d3a5cf7..34d70faa 100644
--- a/crop-tool/serialize.cpp
+++ b/crop-tool/serialize.cpp
@@ -12,72 +12,87 @@ using Corrade::Utility::Error;
static constexpr
std::pair<anim_direction, const char*> anim_direction_map[] = {
- { anim_direction::Invalid, "x" },
- { 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" },
+ { 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<Magnum::Vector2i> final {
- static void to_json(json& j, const Magnum::Vector2i& x)
- {
- j["x"] = x[0];
- j["y"] = x[1];
- }
-
- static void from_json(const json& j, Magnum::Vector2i& x)
- {
- j.at("x").get_to(x[0]);
- j.at("y").get_to(x[1]);
- }
+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<anim_direction> final {
- static void to_json(json& j, anim_direction x)
- {
- auto it = std::find_if(std::cbegin(anim_direction_map), std::cend(anim_direction_map),
- [=](const auto& pair) { return x == pair.first; });
- if (it != std::cend(anim_direction_map))
- j = it->second;
- else
- j = anim_direction_map[0].second;
- }
- static void from_json(const json& j, anim_direction& x)
- {
- std::string str = j;
- 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))
- x = it->first;
- else
- x = anim_direction::Invalid;
- }
+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);
-NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(anim_direction_group, group, frames);
-NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(anim, name, nframes, actionframe, fps, directions);
+NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(anim_group, direction, 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);
+ s.exceptions(s.exceptions() | std::ios::failbit | std::ios::badbit);
try {
s.open(pathname, std::ios_base::in);
- } catch (const std::ios_base::failure& e) {
- Error{} << "failed to open" << pathname << ":" << e.what();
+ } catch (const std::ios::failure& e) {
+ Error{} << "failed to open" << pathname << ':' << e.what();
return std::nullopt;
}
anim ret;
@@ -87,17 +102,26 @@ std::optional<anim> anim::from_json(const std::filesystem::path& pathname)
using nlohmann::from_json;
from_json(j, ret);
} catch (const std::exception& e) {
- Error{} << "failed to parse" << pathname.string() << ":" << e.what();
+ Error{} << "failed to parse" << pathname << ':' << e.what();
+ return std::nullopt;
}
return std::make_optional(std::move(ret));
}
-const char* anim_direction_group::anim_direction_string(anim_direction group)
+bool anim::to_json(const std::filesystem::path& pathname)
{
- 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)";
+ 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
index e93cf6f4..1b98d1bb 100644
--- a/crop-tool/serialize.hpp
+++ b/crop-tool/serialize.hpp
@@ -1,5 +1,6 @@
#pragma once
+#include "../defs.hpp"
#include <string>
#include <array>
#include <vector>
@@ -7,34 +8,34 @@
#include <filesystem>
#include <Magnum/Trade/ImageData.h>
-struct anim_frame
+struct anim_frame final
{
Magnum::Vector2i ground = {};
};
enum class anim_direction : unsigned char
{
- Invalid,
N, NE, E, SE, S, SW, W, NW,
- MIN = N, MAX = NW,
+ COUNT = NW + 1,
};
-struct anim_direction_group
+struct anim_group final
{
- static const char* anim_direction_string(anim_direction group);
- anim_direction group;
+ anim_direction direction = (anim_direction)-1;
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
+struct anim final
{
- std::string name;
- std::array<anim_direction_group, (unsigned)anim_direction::MAX> directions;
- int nframes = 0, actionframe = 0, fps = default_fps;
-
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;
};
diff --git a/doc/atlas.json b/doc/atlas.json
new file mode 100644
index 00000000..53f9722c
--- /dev/null
+++ b/doc/atlas.json
@@ -0,0 +1,18 @@
+{
+ "name": "character",
+ "nframes": 24,
+ "actionframe": 0,
+ "fps": 1,
+ "groups": [
+ { "direction": "N", "frames": [], "ground": {"x":484, "y":488} },
+ { "direction": "NE", "frames": [], "ground": {"x": 0, "y": 0} },
+ { "direction": "E", "frames": [], "ground": {"x": 0, "y": 0} },
+ { "direction": "SE", "frames": [], "ground": {"x": 0, "y": 0} },
+ { "direction": "S", "frames": [], "ground": {"x": 0, "y": 0} },
+ { "direction": "SW", "frames": [], "ground": {"x": 0, "y": 0} },
+ { "direction": "W", "frames": [], "ground": {"x": 0, "y": 0} },
+ { "direction": "NW", "frames": [], "ground": {"x": 0, "y": 0} }
+ ]
+}
+
+// vim: ft=javascript
diff --git a/doc/atlas.json.example b/doc/atlas.json.example
new file mode 100644
index 00000000..9d54b5be
--- /dev/null
+++ b/doc/atlas.json.example
@@ -0,0 +1,18 @@
+{
+ "name": "character",
+ "nframes": 24,
+ "actionframe": 0,
+ "fps": 24,
+ "directions": [
+ { "direction": "N", "frames": [], "ground": {"x": 0, "y": 0} },
+ { "direction": "NE", "frames": [], "ground": {"x": 0, "y": 0} },
+ { "direction": "E", "frames": [], "ground": {"x": 0, "y": 0} },
+ { "direction": "SE", "frames": [], "ground": {"x": 0, "y": 0} },
+ { "direction": "S", "frames": [], "ground": {"x": 0, "y": 0} },
+ { "direction": "SW", "frames": [], "ground": {"x": 0, "y": 0} },
+ { "direction": "W", "frames": [], "ground": {"x": 0, "y": 0} },
+ { "direction": "NW", "frames": [], "ground": {"x": 0, "y": 0} }
+ ]
+}
+
+// vim: ft=javascript
diff --git a/magnum b/magnum
-Subproject a218ddfa9434201aaca207ea9c8e87fea91a075
+Subproject b6739751d2ab957bfc171177bb60d87ed4deb06
diff --git a/magnum-plugins b/magnum-plugins
-Subproject 4100de3b63beac723690adfa4a3fd368b5d664b
+Subproject d8b016d033944175f3530a369506debe51eebd4