4#include <haio_codecs.hpp>
16constexpr uint8_t expand5(
unsigned value) {
return static_cast<uint8_t
>((value << 3) | (value >> 2)); }
27 const auto unit = header.unit();
28 std::vector<uint8_t> out(header.pixels() * unit);
32 std::copy_n(data.begin() +
static_cast<ptrdiff_t
>(header.dataOffset), out.size(), out.begin());
36 size_t at = header.dataOffset;
38 while (to < out.size()) {
39 if (at >= data.size()) {
43 const uint8_t packet = data[at++];
44 const size_t run = (packet & 0x7fu) + 1u;
45 const size_t bytes = run * unit;
46 if (to + bytes > out.size()) {
50 if ((packet & 0x80u) != 0) {
54 for (
size_t k = 0; k < run; k++, to += unit) {
55 std::copy_n(data.begin() +
static_cast<ptrdiff_t
>(at), unit, out.begin() +
static_cast<ptrdiff_t
>(to));
62 std::copy_n(data.begin() +
static_cast<ptrdiff_t
>(at), bytes, out.begin() +
static_cast<ptrdiff_t
>(to));
78void orient(std::vector<uint8_t>& pixels,
const Header& header) {
79 const auto unit = header.unit();
80 const auto width =
static_cast<size_t>(header.width);
81 const auto height =
static_cast<size_t>(header.height);
82 const auto row = width * unit;
85 for (
size_t y = 0; y < height; y++) {
86 auto* line = pixels.data() + y * row;
87 for (
size_t x = 0; x < width / 2; x++) {
88 std::swap_ranges(line + x * unit, line + (x + 1) * unit, line + (width - 1 - x) * unit);
94 for (
size_t y = 0; y < height / 2; y++) {
95 std::swap_ranges(pixels.data() + y * row, pixels.data() + (y + 1) * row,
96 pixels.data() + (height - 1 - y) * row);
106void rotateToRgba5551(std::vector<uint8_t>& pixels) {
107 for (
size_t at = 0; at + 1 < pixels.size(); at += 2) {
108 const auto packed =
static_cast<uint16_t
>(pixels[at] | (pixels[at + 1] << 8));
109 const auto rotated =
static_cast<uint16_t
>((packed << 1) | (packed >> 15));
110 pixels[at] =
static_cast<uint8_t
>(rotated);
111 pixels[at + 1] =
static_cast<uint8_t
>(rotated >> 8);
124 const auto entries =
static_cast<size_t>(header.mapFirst) +
static_cast<size_t>(header.mapCount);
127 "this tga names colour " + std::to_string(entries - 1)
128 +
" and an index of one byte reaches 255"});
131 const auto width = (
static_cast<size_t>(header.mapBits) + 7) / 8;
132 if (!
Util::hasBytes(data, header.mapOffset,
static_cast<size_t>(header.mapCount) * width)) {
136 std::vector<uint32_t> out(entries, 0xff000000u);
137 for (
size_t i = 0; i < header.mapCount; i++) {
138 const auto at = header.mapOffset + i * width;
139 uint32_t colour = 0xff000000u;
141 const auto packed =
static_cast<unsigned>(data[at] | (data[at + 1] << 8));
142 colour |=
static_cast<uint32_t
>(expand5((packed >> 10) & 0x1f)) << 16;
143 colour |=
static_cast<uint32_t
>(expand5((packed >> 5) & 0x1f)) << 8;
144 colour |= expand5(packed & 0x1f);
146 colour |=
static_cast<uint32_t
>(data[at + 2]) << 16;
147 colour |=
static_cast<uint32_t
>(data[at + 1]) << 8;
148 colour |= data[at + 0];
152 if (width == 4 && header.attributeBits() != 0) {
153 colour = (colour & 0x00ffffffu) | (
static_cast<uint32_t
>(data[at + 3]) << 24);
156 out[
static_cast<size_t>(header.mapFirst) + i] = colour;
173 if (!colour || *colour != P) {
176 static constexpr const char* kinds[] = {
"",
"mapped",
"truecolor",
"grey"};
178 + std::to_string(header.depth) +
" bits a pixel, which is not the colour it was asked to be");
181 if (header.depth != 8) {
182 HAIO_FAIL(
UnsupportedFormat,
"this tga indexes its colours two bytes at a time, and a palette picture here is one index per pixel");
187 orient(pixels, header);
191 Image<P> out{header.width, header.height, std::move(pixels)};
194 out.entries = std::move(entries);
209 return decodeTga<Color::BGRA8888>(blob);
219 return decodeTga<Color::BGR888>(blob);
229 return decodeTga<Color::RGBA5551>(blob);
239 return decodeTga<Color::RGB555>(blob);
250 return decodeTga<Color::PALETTE>(blob);
260 return decodeTga<Color::GRAY8>(blob);
Result< Image< Color::RGBA5551 > > Decode< Format::TGA, Color::RGBA5551 >(const Blob &blob)
Result< Image< Color::PALETTE > > Decode< Format::TGA, Color::PALETTE >(const Blob &blob)
Result< Image< Color::GRAY8 > > Decode< Format::TGA, Color::GRAY8 >(const Blob &blob)
Result< Image< Color::BGRA8888 > > Decode< Format::TGA, Color::BGRA8888 >(const Blob &blob)
Result< Image< Color::BGR888 > > Decode< Format::TGA, Color::BGR888 >(const Blob &blob)
Result< Image< Color::RGB555 > > Decode< Format::TGA, Color::RGB555 >(const Blob &blob)
#define HAIO_TRY(name, expr)
#define HAIO_FAIL(why, message)
std::optional< Color > colorOf(const Header &header)
Result< Header > readHeader(Bytes data)
bool hasBytes(Bytes data, size_t off, size_t len)
std::vector< uint8_t > data
std::expected< T, Error > Result
std::span< const uint8_t > Bytes