Haio
0.2.X
Homebrew Asset IO/CDN
Toggle main menu visibility
Loading...
Searching...
No Matches
haio_codec.hpp
Go to the documentation of this file.
1
#pragma once
2
3
#include "
haio_common.hpp
"
4
#include "
haio_formats.hpp
"
5
#include "
haio_object.hpp
"
6
7
#include <expected>
8
#include <meta>
9
#include <optional>
10
#include <span>
11
12
namespace
Haio
{
13
14
using
Bytes
= std::span<const uint8_t>;
15
16
enum class
ErrorCode
{
17
Internal
,
18
InvalidInput
,
19
UnsupportedFormat
,
20
NotFound
,
21
Io
,
22
Upstream
,
23
Timeout
,
24
};
25
26
struct
Error
{
27
ErrorCode
code
=
ErrorCode::Internal
;
28
std::string
message
;
29
};
30
31
template
<
typename
T>
32
using
Result
= std::expected<T, Error>;
33
34
/** pixels in memory. it has a colour and no container, because it is not a file yet */
35
template
<Color P>
36
struct
Image
{
37
static
constexpr
Color
color
= P;
38
int
width
= 0;
39
int
height
= 0;
40
std::vector<uint8_t>
data
;
41
};
42
43
/**
44
* how wide one pixel is, or zero when the colour has no such number: etc1 packs four
45
* by four blocks and yuv420 splits into planes, so neither can be measured a pixel at
46
* a time. sizeOf reads this too, so the widths are written down once.
47
*/
48
constexpr
size_t
strideOf
(
Color
color) {
49
switch
(color) {
50
case
Color::RGBA8888
:
return
4;
51
case
Color::RGB888
:
return
3;
52
case
Color::RGB565
:
return
2;
53
case
Color::RGB555
:
return
2;
54
case
Color::RGBA5551
:
return
2;
55
case
Color::BGR888
:
return
3;
56
case
Color::BGRA8888
:
return
4;
57
case
Color::GRAY8
:
return
1;
58
// one byte per pixel, so a crop or a resize can index it like any other
59
case
Color::PALETTE
:
return
1;
60
// tiled and planar, so no pixel has an address of its own
61
case
Color::CHR_NES
:
break
;
62
case
Color::ETC1
:
break
;
63
case
Color::YUV420
:
break
;
64
}
65
return
0;
66
}
67
68
/** a transform can index these; having no stride is exactly what rules the others out */
69
template
<Color P>
concept
Addressable
=
strideOf
(P) != 0;
70
71
/**
72
* which byte of a pixel holds its alpha, or a negative when the colour has none.
73
* the two eight bit colours answer the same, because where the alpha sits is all a
74
* masking loop ever wanted to know and neither of them moved it. written as a rule so
75
* that the day another arrives, rgba4444 in a ktx or a palette that keeps a
76
* transparent entry, it is one line here rather than a second copy of the loop.
77
*/
78
constexpr
int
alphaOffsetOf
(
Color
color) {
79
switch
(color) {
80
case
Color::RGBA8888
:
return
3;
81
case
Color::BGRA8888
:
return
3;
82
case
Color::RGB888
:
break
;
83
case
Color::RGB565
:
break
;
84
case
Color::BGR888
:
break
;
85
case
Color::RGB555
:
break
;
86
// it has an alpha, but it is one bit sharing a byte with the blue, so there
87
// is nothing here to clear on its own
88
case
Color::RGBA5551
:
break
;
89
case
Color::GRAY8
:
break
;
90
// tiled and planar, so no pixel has an address of its own
91
case
Color::CHR_NES
:
break
;
92
case
Color::ETC1
:
break
;
93
case
Color::YUV420
:
break
;
94
}
95
return
-1;
96
}
97
98
/** and these can have a corner rounded away, because there is an alpha to clear */
99
template
<Color P>
concept
Maskable
=
Addressable<P>
&&
alphaOffsetOf
(P) >= 0;
100
101
/**
102
* the one colour that is not self describing: a byte per pixel means nothing without
103
* the colours it points at, so the palette travels with the picture.
104
*
105
* it is a specialisation rather than a member on every Image, because every other
106
* colour would carry an empty vector around for nothing. the cost is that Move, which
107
* moves spans of bytes, has no way to carry the entries: palette conversions go
108
* through Convert directly and never through convertVia.
109
*/
110
template
<>
111
struct
Image
<
Color
::
PALETTE
> {
112
static
constexpr
Color
color
=
Color::PALETTE
;
113
int
width
= 0;
114
int
height
= 0;
115
std::vector<uint8_t>
data
;
116
117
/** 0xAARRGGBB, at most 256 of them, and an index past the end is an error */
118
std::vector<uint32_t>
entries
;
119
};
120
121
/** bytes of a file. it has a container and the colour it was found to hold */
122
struct
Blob
{
123
Format
format
=
Format::RAW
;
124
Color
color
=
Color::RGBA8888
;
125
std::string
contentType
=
"application/octet-stream"
;
126
std::string
path
;
127
std::vector<uint8_t>
data
;
128
};
129
130
/**
131
* the verbs. they live apart from the vocabulary above because the runtime facade in
132
* haio_registry.hpp offers Decode(const Blob&) and Detect(Bytes) with the very same
133
* parameter lists: without a namespace between them, Decode(blob) and
134
* Decode<Format::PNG>(blob) read alike and do different things.
135
*/
136
namespace
Codecs
{
137
138
/**
139
* the colour a format carries when nobody says otherwise. only formats with a real
140
* answer declare it: a ktx2 holds any of three, so Decode<Format::KTX2> alone is a
141
* compile error telling the caller to be specific.
142
*/
143
template
<Format F>
struct
DefaultColor
;
144
145
/**
146
* deleted on purpose. a codec declares only the pairs it can do, and naming one it
147
* left out is ill-formed, which is what lets the concepts below answer for themselves
148
* instead of failing at link time. drop a codec from the build and its format stays
149
* in the enum, naming itself, with every capability gone.
150
*/
151
template <Format F, Color P = DefaultColor<F>::value>
bool
Detect
(
Bytes
) =
delete
;
152
template <Format F, Color P = DefaultColor<F>::value>
Result<Image<P>
>
Decode
(
const
Blob
&) =
delete
;
153
template
<Format F, Color P>
Result<Blob>
Encode
(
Image<P>
) =
delete
;
154
155
/**
156
* @name Convert
157
* @{
158
*/
159
/** colour to colour, no file involved. the heavy ones live behind Move */
160
template
<Color From, Color To>
Result<Image<To>
>
Convert
(
Image<From>
) =
delete
;
161
/**
162
* @}
163
*/
164
165
/**
166
* the hot loop under Convert, and what a codec calls when it wants to write pixels
167
* straight into a buffer it already owns. dst is sized by the caller so the simd
168
* paths can rely on it, and size is here because block formats like etc1 cannot be
169
* transformed pixel by pixel.
170
*
171
* it reports rather than assumes: the loop runs once per image, so one Result on the
172
* way out costs nothing next to the work inside it.
173
*/
174
template
<Color From, Color To>
Result<void>
Move
(
Bytes
src, std::span<uint8_t> dst,
Size
size) =
delete
;
175
176
/**
177
* @defgroup detect Detect
178
* every container and colour pair this build can recognise. these are exactly the
179
* specialisations that satisfy Detectable, so a codec dropped from the build leaves
180
* this page and the concept together, while its format keeps its name in the enum.
181
*/
182
183
/**
184
* @defgroup decode Decode
185
* every pair this build can read into pixels. a format may be recognised here and
186
* still be missing from @ref decode, which is how haio names a file it cannot open.
187
*/
188
189
/**
190
* @defgroup encode Encode
191
* every pair this build can write out. the colour is the one stored inside the
192
* container, not the one the caller happens to hold.
193
*/
194
195
/**
196
* @defgroup convert Convert
197
* colour to colour with a whole image in hand. each one delegates to convertVia,
198
* which sizes the destination and hands the work to the matching @ref move.
199
*/
200
201
/**
202
* @defgroup move Move
203
* the hot loops, writing pixels into a buffer the caller already owns. this is where
204
* the simd lives.
205
*/
206
207
/**
208
* does this format name a colour of its own. a container that holds any of three has
209
* nothing to declare, so this is asked rather than assumed.
210
*/
211
template
<Format F>
concept
HasDefaultColor
=
requires
{
DefaultColor<F>::value
; };
212
213
/**
214
* @ingroup detect
215
* can these bytes be recognised as this pair, asked of the pair rather than answered by hand.
216
*/
217
template
<Format F, Color P>
concept
Detectable
=
requires
(
Bytes
d) { {
Detect<F, P>
(d) } -> std::same_as<bool>; };
218
/**
219
* @ingroup decode
220
* can this pair be read into pixels, asked of the pair rather than answered by hand.
221
*/
222
template
<Format F, Color P>
concept
Decodable
=
requires
(
const
Blob
& b) { {
Decode<F, P>
(b) } -> std::same_as<Result<Image<P>>>; };
223
/**
224
* @ingroup encode
225
* can an image of this colour be written into this container, asked of the pair rather than answered by hand.
226
*/
227
template
<Format F, Color P>
concept
Encodable
=
requires
(
Image<P>
i) { {
Encode<F, P>
(std::move(i)) } -> std::same_as<Result<Blob>>; };
228
/**
229
* @ingroup move
230
* is there a loop from one colour to the other, asked of the pair rather than answered by hand.
231
*/
232
template
<Color From, Color To>
concept
Movable
=
requires
(
Bytes
s, std::span<uint8_t> d,
Size
z) { {
Move<From, To>
(s, d, z) } -> std::same_as<Result<void>>; };
233
/**
234
* @ingroup convert
235
* is there a whole image conversion from one colour to the other, asked of the pair rather than answered by hand.
236
*/
237
template
<Color From, Color To>
concept
Convertible
=
requires
(
Image<From>
i) { {
Convert<From, To>
(std::move(i)) } -> std::same_as<Result<Image<To>>>; };
238
239
}
240
241
/**
242
* go style early return. c++26 still has no "?" operator, so unwrapping a Result
243
* and leaving on failure is either this or a monadic chain that reads badly when
244
* the steps are independent.
245
*/
246
#define HAIO_TRY(name, expr) \
247
auto name##Result = (expr); \
248
if (!name##Result) return std::unexpected(name##Result.error()); \
249
auto& name = *name##Result
250
251
/** for a check that only reports a problem and produces no value */
252
#define HAIO_CHECK(expr) \
253
do { \
254
if (auto problem = (expr)) return std::unexpected(*problem); \
255
} while (false)
256
257
/** a plain failure, for the common case of a message and a cause */
258
#define HAIO_FAIL(why, message) \
259
return std::unexpected(::Haio::Error{::Haio::ErrorCode::why, message})
260
261
/** the enums are the loops: a new enumerator grows every table on its own */
262
#define HAIO_FOR_EACH_FORMAT(e) \
263
template for (constexpr auto e : std::define_static_array(std::meta::enumerators_of(^^Haio::Format)))
264
265
#define HAIO_FOR_EACH_COLOR(e) \
266
template for (constexpr auto e : std::define_static_array(std::meta::enumerators_of(^^Haio::Color)))
267
268
/** the enumerator is PNG, the name people type is png */
269
consteval
std::string_view
lowerOf
(std::meta::info enumerator) {
270
std::string out{std::meta::identifier_of(enumerator)};
271
for
(
auto
& c : out) {
272
if
(c >=
'A'
&& c <=
'Z'
) c =
static_cast<
char
>
(c -
'A'
+
'a'
);
273
}
274
return
std::define_static_string(out);
275
}
276
277
}
Haio::Addressable
Definition
haio_codec.hpp:69
Haio::Codecs::Convertible
Definition
haio_codec.hpp:237
Haio::Codecs::Decodable
Definition
haio_codec.hpp:222
Haio::Codecs::Detectable
Definition
haio_codec.hpp:217
Haio::Codecs::Encodable
Definition
haio_codec.hpp:227
Haio::Codecs::HasDefaultColor
Definition
haio_codec.hpp:211
Haio::Codecs::Movable
Definition
haio_codec.hpp:232
Haio::Maskable
Definition
haio_codec.hpp:99
haio_common.hpp
haio_formats.hpp
haio_object.hpp
Haio::Codecs
Definition
haio_codec.hpp:136
Haio::Codecs::Move
Result< void > Move(Bytes src, std::span< uint8_t > dst, Size size)=delete
Haio::Codecs::Convert
Result< Image< To > > Convert(Image< From >)=delete
Haio::Codecs::Decode
Result< Image< P > > Decode(const Blob &)=delete
Haio::Codecs::Detect
bool Detect(Bytes)=delete
Haio::Codecs::Encode
Result< Blob > Encode(Image< P >)=delete
Haio::Codecs::DefaultColor
Definition
haio_codec.hpp:143
Haio
Definition
haio.hpp:11
Haio::Error::code
ErrorCode code
Definition
haio_codec.hpp:27
Haio::Error::message
std::string message
Definition
haio_codec.hpp:28
Haio::Color
Color
Definition
haio_formats.hpp:106
Haio::Color::BGRA8888
@ BGRA8888
Definition
haio_formats.hpp:135
Haio::Color::RGBA8888
@ RGBA8888
Definition
haio_formats.hpp:107
Haio::Color::CHR_NES
@ CHR_NES
Definition
haio_formats.hpp:155
Haio::Color::RGB888
@ RGB888
Definition
haio_formats.hpp:108
Haio::Color::RGB565
@ RGB565
Definition
haio_formats.hpp:109
Haio::Color::PALETTE
@ PALETTE
Definition
haio_formats.hpp:145
Haio::Color::BGR888
@ BGR888
Definition
haio_formats.hpp:134
Haio::Color::YUV420
@ YUV420
Definition
haio_formats.hpp:139
Haio::Color::ETC1
@ ETC1
Definition
haio_formats.hpp:138
Haio::Color::RGBA5551
@ RGBA5551
Definition
haio_formats.hpp:127
Haio::Color::GRAY8
@ GRAY8
Definition
haio_formats.hpp:137
Haio::Color::RGB555
@ RGB555
Definition
haio_formats.hpp:117
Haio::Blob::data
std::vector< uint8_t > data
Definition
haio_codec.hpp:127
Haio::Result
std::expected< T, Error > Result
Definition
haio_codec.hpp:32
Haio::lowerOf
consteval std::string_view lowerOf(std::meta::info enumerator)
Definition
haio_codec.hpp:269
Haio::strideOf
constexpr size_t strideOf(Color color)
Definition
haio_codec.hpp:48
Haio::Blob::contentType
std::string contentType
Definition
haio_codec.hpp:125
Haio::alphaOffsetOf
constexpr int alphaOffsetOf(Color color)
Definition
haio_codec.hpp:78
Haio::Format
Format
Definition
haio_formats.hpp:17
Haio::Format::RAW
@ RAW
Definition
haio_formats.hpp:19
Haio::Blob::color
Color color
Definition
haio_codec.hpp:124
Haio::ErrorCode
ErrorCode
Definition
haio_codec.hpp:16
Haio::ErrorCode::NotFound
@ NotFound
Definition
haio_codec.hpp:20
Haio::ErrorCode::Io
@ Io
Definition
haio_codec.hpp:21
Haio::ErrorCode::Upstream
@ Upstream
Definition
haio_codec.hpp:22
Haio::ErrorCode::Internal
@ Internal
Definition
haio_codec.hpp:17
Haio::ErrorCode::UnsupportedFormat
@ UnsupportedFormat
Definition
haio_codec.hpp:19
Haio::ErrorCode::Timeout
@ Timeout
Definition
haio_codec.hpp:23
Haio::ErrorCode::InvalidInput
@ InvalidInput
Definition
haio_codec.hpp:18
Haio::Bytes
std::span< const uint8_t > Bytes
Definition
haio_codec.hpp:14
Haio::Blob::path
std::string path
Definition
haio_codec.hpp:126
Haio::Blob::format
Format format
Definition
haio_codec.hpp:123
Haio::Blob
Definition
haio_codec.hpp:122
Haio::Error
Definition
haio_codec.hpp:26
Haio::Size
Definition
haio_object.hpp:12
Haio::Image< Color::PALETTE >::width
int width
Definition
haio_codec.hpp:113
Haio::Image< Color::PALETTE >::entries
std::vector< uint32_t > entries
Definition
haio_codec.hpp:118
Haio::Image< Color::PALETTE >::color
static constexpr Color color
Definition
haio_codec.hpp:112
Haio::Image< Color::PALETTE >::height
int height
Definition
haio_codec.hpp:114
Haio::Image< Color::PALETTE >::data
std::vector< uint8_t > data
Definition
haio_codec.hpp:115
Haio::Image
Definition
haio_codec.hpp:36
Haio::Image::color
static constexpr Color color
Definition
haio_codec.hpp:37
Haio::Image::height
int height
Definition
haio_codec.hpp:39
Haio::Image::width
int width
Definition
haio_codec.hpp:38
Haio::Image::data
std::vector< uint8_t > data
Definition
haio_codec.hpp:40
include
haio_codec.hpp
Generated by
1.18.0