Haio 0.2.X
Homebrew Asset IO/CDN
Loading...
Searching...
No Matches
encode.cpp
Go to the documentation of this file.
1#include <haio_codec.hpp>
2#include <haio_codecs.hpp>
3#include <haio_convert.hpp>
4#include <haio_util.hpp>
5
6#include <algorithm>
7#include <array>
8#include <cstdio>
9#include <optional>
10#include <string>
11#include <string_view>
12
13namespace {
14
15constexpr std::string_view base62 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
16constexpr int base62PairMax = 62 * 62 - 1;
17
18/**
19 * zcis splits the canvas into two layers of alternating rows, so that each layer
20 * stays a decodable image on its own and both together rebuild the original
21 * without losing a pixel.
22 *
23 * @note ZCIS.md marks "B" with the vertical bar glyph, which reads as alternating
24 * columns; the row split described here is the "C" glyph. Flip these two constants
25 * (and rowLayers) if the table is meant literally.
26 */
27constexpr char baseCommand = 'A';
28constexpr char refineCommand = 'B';
29
30constexpr std::string_view indexMemberName = "000000000000.txt";
31
32std::string base62Pair(int value) {
33 return {base62[static_cast<size_t>(value) / 62], base62[static_cast<size_t>(value) % 62]};
34}
35
36/** ar members sit on even offsets, padded with a newline. */
37std::optional<Haio::Error> appendMember(std::vector<uint8_t>& out, const std::string& name, const std::vector<uint8_t>& body) {
38 std::array<char, 61> header{};
39 const int written = std::snprintf(
40 header.data(),
41 header.size(),
42 "%-16s%-12u%-6u%-6u%-8o%-10zu`\n",
43 name.c_str(),
44 0u,
45 0u,
46 0u,
47 0644u,
48 body.size()
49 );
50 if (written != 60) return Haio::Error{Haio::ErrorCode::InvalidInput, "zcis member header overflow: " + name};
51
52 out.insert(out.end(), header.begin(), header.begin() + 60);
53 out.insert(out.end(), body.begin(), body.end());
54 if (body.size() % 2 != 0) out.push_back('\n');
55 return std::nullopt;
56}
57
58void appendBytes(std::vector<uint8_t>& out, std::string_view text) {
59 out.insert(out.end(), text.begin(), text.end());
60}
61
63 const int height = (image.height - parity + 1) / 2;
64 const auto stride = static_cast<size_t>(image.width) * 4;
65
66 std::vector<uint8_t> data(stride * static_cast<size_t>(height));
67 for (int y = 0; y < height; y++) {
68 const auto src = static_cast<size_t>(2 * y + parity) * stride;
69 std::copy_n(image.data.data() + src, stride, data.data() + static_cast<size_t>(y) * stride);
70 }
71 return Haio::Image<Haio::Color::RGBA8888>{image.width, height, std::move(data)};
72}
73
74}
75
76namespace Haio::Codecs {
77
78/**
79 * @addtogroup encode
80 * @{
81 */
82template <>
83Result<Blob> Encode<Format::ZCIS, Color::RGBA8888>(Image<Color::RGBA8888> img) {
84 const auto expected = static_cast<size_t>(img.width) * static_cast<size_t>(img.height) * 4;
85 if (img.width <= 0 || img.height <= 0 || img.data.size() != expected) {
86 HAIO_FAIL(InvalidInput, "invalid rgba8888 image for zcis encode");
87 }
88 if (img.width > base62PairMax || img.height > base62PairMax) {
89 HAIO_FAIL(InvalidInput, "zcis dimensions exceed base62 limit");
90 }
91
92 std::vector<uint8_t> out;
93 appendBytes(out, "!<arch>\n");
94
95 const auto index = "0 0 " + std::to_string(img.width) + " " + std::to_string(img.height) + "\r\n";
96 HAIO_CHECK(appendMember(out, std::string(indexMemberName), {index.begin(), index.end()}));
97
98 const auto target = base62Pair(0) + base62Pair(0) + base62Pair(img.width) + base62Pair(img.height);
99
100 int order = 1;
101 const auto appendLayer = [&](char command, const Image<Color::RGBA8888>& layer) -> std::optional<Error> {
102 if (layer.height <= 0) return std::nullopt;
103
104 auto payload = Encode<Format::PPM, Color::RGBA8888>(Image<Color::RGBA8888>{layer});
105 if (!payload) return payload.error();
106
107 const auto name = std::string(1, base62[static_cast<size_t>(order++)]) + command + target + ".ppm/";
108 return appendMember(out, name, payload->data);
109 };
110
111 HAIO_CHECK(appendLayer(baseCommand, selectRows(img, 0)));
112 HAIO_CHECK(appendLayer(refineCommand, selectRows(img, 1)));
113
114 return Blob{Format::ZCIS, Color::RGBA8888, "image/x-zcis", {}, std::move(out)};
115}
116/** @} */
117
118}
Result< Blob > Encode< Format::PPM, Color::RGBA8888 >(Image< Color::RGBA8888 > img)
Definition encode.cpp:19
Result< Blob > Encode< Format::ZCIS, Color::RGBA8888 >(Image< Color::RGBA8888 > img)
Definition encode.cpp:83
#define HAIO_FAIL(why, message)
#define HAIO_CHECK(expr)
std::expected< T, Error > Result
std::vector< uint8_t > data