Haio 0.2.X
Homebrew Asset IO/CDN
Loading...
Searching...
No Matches
decode.cpp
Go to the documentation of this file.
1#include <haio_codec.hpp>
2#include <haio_codecs.hpp>
3#include <haio_convert.hpp>
4
5#include <turbojpeg.h>
6
7#include <memory>
8
9namespace {
10
11/** the handle owns a decompressor, and there is no path out of here that leaks it */
12struct Handle {
13 tjhandle raw = nullptr;
14 ~Handle() { if (raw) tj3Destroy(raw); }
15};
16
17}
18
19namespace Haio::Codecs {
20
21/**
22 * straight to yuv planes, which is what the file already holds.
23 *
24 * a jpeg is yuv all the way down, so a decoder that hands back rgb has both thrown
25 * away the chroma layout and spent the work doing it. asking for the planes means the
26 * picture arrives as it was stored, and whoever wants rgb converts once, visibly.
27 *
28 * only 4:2:0 is read. it is what cameras and ad creatives produce, and reading 4:4:4
29 * or 4:2:2 into a buffer sized for 4:2:0 would quietly truncate the chroma rather
30 * than fail, so those are refused instead.
31 */
32template <>
34 Handle handle{tj3Init(TJINIT_DECOMPRESS)};
35 if (!handle.raw) HAIO_FAIL(Internal, "cannot start the jpeg decoder");
36
37 if (tj3DecompressHeader(handle.raw, blob.data.data(), blob.data.size()) != 0) {
38 HAIO_FAIL(InvalidInput, "this is not a jpeg haio can read");
39 }
40
41 const int width = tj3Get(handle.raw, TJPARAM_JPEGWIDTH);
42 const int height = tj3Get(handle.raw, TJPARAM_JPEGHEIGHT);
43 const int sampling = tj3Get(handle.raw, TJPARAM_SUBSAMP);
44
45 if (width <= 0 || height <= 0) HAIO_FAIL(InvalidInput, "the jpeg has no area");
46 if (sampling != TJSAMP_420) {
47 HAIO_FAIL(UnsupportedFormat, "haio reads 4:2:0 jpegs, and this one is not");
48 }
49 if (width % 2 != 0 || height % 2 != 0) {
50 // 4:2:0 stores chroma for whole two by two blocks, so an odd side means the
51 // file's own planes are padded and would not line up with ours
52 HAIO_FAIL(UnsupportedFormat, "haio reads 4:2:0 jpegs with even sides, and this one is odd");
53 }
54
55 HAIO_TRY(bytes, sizeOf(Color::YUV420, Size{width, height}));
56 std::vector<uint8_t> planes(bytes);
57
58 // align 1 means the planes sit back to back with no padding, which is the layout
59 // Color::YUV420 is defined as
60 if (tj3DecompressToYUV8(handle.raw, blob.data.data(), blob.data.size(), planes.data(), 1) != 0) {
61 HAIO_FAIL(InvalidInput, "the jpeg could not be decoded");
62 }
63 return Image<Color::YUV420>{width, height, std::move(planes)};
64}
65
66}
#define HAIO_TRY(name, expr)
#define HAIO_FAIL(why, message)
Result< Image< Color::YUV420 > > Decode< Format::JPEG, Color::YUV420 >(const Blob &blob)
Definition decode.cpp:33
std::expected< T, Error > Result
Result< size_t > sizeOf(Color color, Size size)
Definition sizes.cpp:6