Haio 0.2.X
Homebrew Asset IO/CDN
Loading...
Searching...
No Matches
yuv420.cpp
Go to the documentation of this file.
2
3#include <utility>
4
5namespace Haio::Codecs {
6
7/**
8 * the way back: full range bt.601 again, and the chroma of a two by two block is the
9 * average of its four pixels rather than the top left one.
10 *
11 * taking one pixel and calling it the block would be cheaper and is what a naive
12 * encoder does; it shows up as ragged colour on edges, because the sample that
13 * happened to be first decides for three others.
14 */
15template <>
16Result<void> Move<Color::RGBA8888, Color::YUV420>(Bytes src, std::span<uint8_t> dst, Size size) {
17 if (size.width % 2 != 0 || size.height % 2 != 0) {
18 HAIO_FAIL(InvalidInput, "yuv420 needs even width and height");
19 }
20
21 const auto width = static_cast<size_t>(size.width);
22 const auto height = static_cast<size_t>(size.height);
23 const auto pixels = width * height;
24 const auto half = width / 2;
25 const auto chroma = half * (height / 2);
26
27 if (src.size() < pixels * 4 || dst.size() < pixels + chroma * 2) {
28 HAIO_FAIL(InvalidInput, "rgba8888 to yuv420 got a buffer that is too small");
29 }
30
31 auto* luma = dst.data();
32 auto* blue = luma + pixels;
33 auto* red = blue + chroma;
34
35 const auto clamp = [](int value) {
36 return static_cast<uint8_t>(value < 0 ? 0 : value > 255 ? 255 : value);
37 };
38
39 for (size_t y = 0; y < height; y++) {
40 for (size_t x = 0; x < width; x++) {
41 const auto at = (y * width + x) * 4;
42 const int r = src[at + 0];
43 const int g = src[at + 1];
44 const int b = src[at + 2];
45 luma[y * width + x] = clamp((306 * r + 601 * g + 117 * b) >> 10);
46 }
47 }
48
49 for (size_t y = 0; y < height; y += 2) {
50 for (size_t x = 0; x < width; x += 2) {
51 int r = 0;
52 int g = 0;
53 int b = 0;
54 for (const auto corner : {std::pair{y, x}, std::pair{y, x + 1},
55 std::pair{y + 1, x}, std::pair{y + 1, x + 1}}) {
56 const auto at = (corner.first * width + corner.second) * 4;
57 r += src[at + 0];
58 g += src[at + 1];
59 b += src[at + 2];
60 }
61 r /= 4;
62 g /= 4;
63 b /= 4;
64
65 const auto at = (y / 2) * half + (x / 2);
66 blue[at] = clamp(128 + ((-173 * r - 339 * g + 512 * b) >> 10));
67 red[at] = clamp(128 + ((512 * r - 429 * g - 83 * b) >> 10));
68 }
69 }
70 return {};
71}
72
73template <>
75 return convertVia<Color::RGBA8888, Color::YUV420>(std::move(src));
76}
77
78}
#define HAIO_FAIL(why, message)
Result< void > Move< Color::RGBA8888, Color::YUV420 >(Bytes src, std::span< uint8_t > dst, Size size)
Definition yuv420.cpp:16
Result< Image< To > > convertVia(Image< From > src)
Result< Image< Color::YUV420 > > Convert< Color::RGBA8888, Color::YUV420 >(Image< Color::RGBA8888 > src)
Definition yuv420.cpp:74
std::expected< T, Error > Result
std::span< const uint8_t > Bytes