Haio
0.2.X
Homebrew Asset IO/CDN
Toggle main menu visibility
Loading...
Searching...
No Matches
header.hpp
Go to the documentation of this file.
1
#pragma once
2
3
#include <
haio_codec.hpp
>
4
5
#include <optional>
6
#include <string_view>
7
8
/**
9
* the eighteen bytes every tga starts with, read once for the three files that need
10
* them. it is a header beside the sources rather than a third .cpp because nothing
11
* outside this directory has any use for a targa header, and the build scans .cpp
12
* files for capabilities: a helper that lived in one of them would be shared by
13
* accident or copied on purpose.
14
*/
15
namespace
Haio::Codecs::Tga
{
16
17
inline
constexpr
size_t
headerBytes
= 18;
18
19
/**
20
* an image this large would not fit anyway, and a header claims it in four bytes.
21
*
22
* the same number the netpbm decoder uses, for the same reason: the dimensions are
23
* read before there is any picture to measure them against, and a run length payload
24
* is not bounded by the size of the file the way a plain one is.
25
*/
26
inline
constexpr
size_t
maxPixels
=
size_t
{1} << 28;
27
28
/**
29
* what a tga 2.0 writer leaves at the end of the file: two offsets nobody here uses
30
* and a signature that is the only thing about the format anybody can recognise
31
* outright. haio writes one so its own files are named by that rather than by the
32
* guesswork below.
33
*/
34
inline
constexpr
std::string_view
footerSignature
=
"TRUEVISION-XFILE."
;
35
inline
constexpr
size_t
footerBytes
= 26;
36
37
struct
Header
{
38
uint8_t
idLength
= 0;
39
uint8_t
mapType
= 0;
40
uint8_t
imageType
= 0;
41
uint16_t
mapFirst
= 0;
42
uint16_t
mapCount
= 0;
43
uint8_t
mapBits
= 0;
44
uint16_t
width
= 0;
45
uint16_t
height
= 0;
46
uint8_t
depth
= 0;
47
uint8_t
descriptor
= 0;
48
49
size_t
mapOffset
= 0;
/**< the colour map, when there is one */
50
size_t
dataOffset
= 0;
/**< the pixels, whatever came before them */
51
52
/** mapped, truecolor or grey, with the run length flavours folded onto their own */
53
int
kind
()
const
{
return
imageType
> 8 ?
imageType
- 8 :
imageType
; }
54
bool
rle
()
const
{
return
imageType
> 8; }
55
56
/** one stored unit: a colour for a truecolor picture, an index for a mapped one */
57
size_t
unit
()
const
{
return
(
static_cast<
size_t
>
(
depth
) + 7) / 8; }
58
size_t
pixels
()
const
{
return
static_cast<
size_t
>
(
width
) *
static_cast<
size_t
>
(
height
); }
59
60
/**
61
* how many of the sixteen bits are alpha, which for this format is a claim rather
62
* than a layout: the bit is in the same place either way, and this says whether
63
* anybody meant anything by it.
64
*/
65
int
attributeBits
()
const
{
return
descriptor
& 0x0f; }
66
67
/** the origin bits, which say where the first stored pixel belongs on screen */
68
bool
flipX
()
const
{
return
(
descriptor
& 0x10) != 0; }
69
bool
flipY
()
const
{
return
(
descriptor
& 0x20) == 0; }
70
};
71
72
/** the signature sits eight bytes into the footer, after the two offsets */
73
inline
bool
hasFooter
(
Bytes
data) {
74
if
(data.size() <
footerBytes
)
return
false
;
75
const
auto
at = data.size() -
footerBytes
+ 8;
76
for
(
size_t
i = 0; i <
footerSignature
.size(); i++) {
77
if
(data[at + i] !=
static_cast<
uint8_t
>
(
footerSignature
[i]))
return
false
;
78
}
79
return
true
;
80
}
81
82
/**
83
* the header, made to prove itself.
84
*
85
* every other container haio reads begins with bytes that mean nothing else. a tga
86
* begins with its own dimensions, so there is nothing to compare against and the only
87
* question that can be asked is whether the eighteen bytes are consistent: a type it
88
* names, a depth that type can have, a colour map exactly when one is called for, and
89
* a file long enough to hold what all of that promises. anything less strict would
90
* make tga the answer for every unrecognised file.
91
*/
92
inline
Result<Header>
readHeader
(
Bytes
data) {
93
if
(data.size() <
headerBytes
) {
94
return
std::unexpected(
Error
{
ErrorCode::InvalidInput
,
"this file is too short to be a tga"
});
95
}
96
97
const
auto
u16 = [data](
size_t
at) {
98
return
static_cast<
uint16_t
>
(data[at] | (data[at + 1] << 8));
99
};
100
101
Header
header;
102
header.
idLength
= data[0];
103
header.
mapType
= data[1];
104
header.
imageType
= data[2];
105
header.
mapFirst
= u16(3);
106
header.
mapCount
= u16(5);
107
header.
mapBits
= data[7];
108
// 8 to 11 are where the picture sits on a screen, which is not part of the picture
109
header.
width
= u16(12);
110
header.
height
= u16(14);
111
header.
depth
= data[16];
112
header.
descriptor
= data[17];
113
114
const
auto
fail = [](
const
char
* why) {
115
return
std::unexpected(
Error
{
ErrorCode::InvalidInput
, why});
116
};
117
118
if
(header.
mapType
> 1)
return
fail(
"this tga names a colour map type nobody defined"
);
119
if
(header.
kind
() < 1 || header.
kind
() > 3)
return
fail(
"this tga names an image type nobody defined"
);
120
// the top two descriptor bits were interleaving in the first version and have to
121
// be zero in this one, which makes them a cheap way to tell noise from a header
122
if
((header.
descriptor
& 0xc0) != 0)
return
fail(
"this tga sets descriptor bits that 2.0 reserved"
);
123
if
(header.
width
== 0 || header.
height
== 0)
return
fail(
"this tga has no area"
);
124
125
if
(header.
kind
() == 1) {
126
if
(header.
mapType
!= 1 || header.
mapCount
== 0)
return
fail(
"this tga indexes a colour map it does not carry"
);
127
if
(header.
mapBits
!= 15 && header.
mapBits
!= 16 && header.
mapBits
!= 24 && header.
mapBits
!= 32) {
128
return
fail(
"this tga names a colour map entry size nobody defined"
);
129
}
130
if
(header.
depth
!= 8 && header.
depth
!= 16)
return
fail(
"a mapped tga indexes one or two bytes at a time"
);
131
}
else
if
(header.
mapType
== 0 && (header.
mapFirst
!= 0 || header.
mapCount
!= 0 || header.
mapBits
!= 0)) {
132
return
fail(
"this tga describes a colour map and then says it has none"
);
133
}
134
135
if
(header.
kind
() == 2 && header.
depth
!= 15 && header.
depth
!= 16 && header.
depth
!= 24 && header.
depth
!= 32) {
136
return
fail(
"this tga names a truecolor depth nobody defined"
);
137
}
138
if
(header.
kind
() == 3 && header.
depth
!= 8)
return
fail(
"a grey tga is one byte a pixel"
);
139
140
header.
mapOffset
=
headerBytes
+
static_cast<
size_t
>
(header.
idLength
);
141
header.
dataOffset
= header.
mapOffset
;
142
if
(header.
mapType
== 1) {
143
header.
dataOffset
+=
static_cast<
size_t
>
(header.
mapCount
) * ((
static_cast<
size_t
>
(header.
mapBits
) + 7) / 8);
144
}
145
146
if
(header.
pixels
() >
maxPixels
)
return
fail(
"this tga is larger than haio will decode"
);
147
148
if
(header.
dataOffset
>= data.size())
return
fail(
"this tga header promises pixels the file does not reach"
);
149
150
const
auto
available = data.size() - header.
dataOffset
;
151
if
(!header.
rle
()) {
152
if
(available < header.
pixels
() * header.
unit
()) {
153
return
fail(
"this tga holds fewer pixels than its header claims"
);
154
}
155
}
else
if
(header.
pixels
() / 128 > available / (1 + header.
unit
())) {
156
/**
157
* a compressed picture is as long as it turns out to be, so it cannot be
158
* measured the way a plain one can. what can be said is the most it could
159
* possibly be: a packet is a count byte and at least one unit, and it covers
160
* at most 128 pixels. a file that does not reach even that is either truncated
161
* or claiming a size it means to be expanded into.
162
*/
163
return
fail(
"this tga claims more picture than its runs could possibly hold"
);
164
}
165
166
return
header;
167
}
168
169
/**
170
* which of haio's colours these bytes already are.
171
*
172
* a tga stores what haio calls bgr888, bgra8888 and the two fives-and-ones outright,
173
* so the answer is a layout rather than a conversion: that is the whole reason those
174
* colours exist, and why decoding one is a copy and not a loop.
175
*/
176
inline
std::optional<Color>
colorOf
(
const
Header
& header) {
177
switch
(header.
kind
()) {
178
case
1:
return
Color::PALETTE
;
179
case
3:
return
Color::GRAY8
;
180
case
2:
181
if
(header.
depth
== 24)
return
Color::BGR888
;
182
if
(header.
depth
== 32)
return
Color::BGRA8888
;
183
// the same two bytes either way: the attribute bits are what decides
184
// whether the top one is an alpha or nothing at all
185
return
header.
attributeBits
() != 0 ?
Color::RGBA5551
:
Color::RGB555
;
186
default
:
break
;
187
}
188
return
std::nullopt;
189
}
190
191
}
haio_codec.hpp
Haio::Codecs::Tga
Definition
header.hpp:15
Haio::Codecs::Tga::footerBytes
constexpr size_t footerBytes
Definition
header.hpp:35
Haio::Codecs::Tga::colorOf
std::optional< Color > colorOf(const Header &header)
Definition
header.hpp:176
Haio::Codecs::Tga::footerSignature
constexpr std::string_view footerSignature
Definition
header.hpp:34
Haio::Codecs::Tga::hasFooter
bool hasFooter(Bytes data)
Definition
header.hpp:73
Haio::Codecs::Tga::headerBytes
constexpr size_t headerBytes
Definition
header.hpp:17
Haio::Codecs::Tga::readHeader
Result< Header > readHeader(Bytes data)
Definition
header.hpp:92
Haio::Codecs::Tga::maxPixels
constexpr size_t maxPixels
Definition
header.hpp:26
Haio::Color::BGRA8888
@ BGRA8888
Definition
haio_formats.hpp:135
Haio::Color::PALETTE
@ PALETTE
Definition
haio_formats.hpp:145
Haio::Color::BGR888
@ BGR888
Definition
haio_formats.hpp:134
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::Result
std::expected< T, Error > Result
Definition
haio_codec.hpp:32
Haio::ErrorCode::InvalidInput
@ InvalidInput
Definition
haio_codec.hpp:18
Haio::Bytes
std::span< const uint8_t > Bytes
Definition
haio_codec.hpp:14
Haio::Error
Definition
haio_codec.hpp:26
Haio::Codecs::Tga::Header
Definition
header.hpp:37
Haio::Codecs::Tga::Header::mapOffset
size_t mapOffset
Definition
header.hpp:49
Haio::Codecs::Tga::Header::kind
int kind() const
Definition
header.hpp:53
Haio::Codecs::Tga::Header::depth
uint8_t depth
Definition
header.hpp:46
Haio::Codecs::Tga::Header::mapType
uint8_t mapType
Definition
header.hpp:39
Haio::Codecs::Tga::Header::flipY
bool flipY() const
Definition
header.hpp:69
Haio::Codecs::Tga::Header::dataOffset
size_t dataOffset
Definition
header.hpp:50
Haio::Codecs::Tga::Header::idLength
uint8_t idLength
Definition
header.hpp:38
Haio::Codecs::Tga::Header::height
uint16_t height
Definition
header.hpp:45
Haio::Codecs::Tga::Header::mapCount
uint16_t mapCount
Definition
header.hpp:42
Haio::Codecs::Tga::Header::pixels
size_t pixels() const
Definition
header.hpp:58
Haio::Codecs::Tga::Header::descriptor
uint8_t descriptor
Definition
header.hpp:47
Haio::Codecs::Tga::Header::mapBits
uint8_t mapBits
Definition
header.hpp:43
Haio::Codecs::Tga::Header::rle
bool rle() const
Definition
header.hpp:54
Haio::Codecs::Tga::Header::mapFirst
uint16_t mapFirst
Definition
header.hpp:41
Haio::Codecs::Tga::Header::attributeBits
int attributeBits() const
Definition
header.hpp:65
Haio::Codecs::Tga::Header::imageType
uint8_t imageType
Definition
header.hpp:40
Haio::Codecs::Tga::Header::flipX
bool flipX() const
Definition
header.hpp:68
Haio::Codecs::Tga::Header::width
uint16_t width
Definition
header.hpp:44
Haio::Codecs::Tga::Header::unit
size_t unit() const
Definition
header.hpp:57
library
backend
codecs
container
tga
header.hpp
Generated by
1.18.0