summaryrefslogtreecommitdiff
path: root/subprojects/pixpat/pixpat-native/src/io/packed_yuv.h
blob: 90c8b2f44e825e394a90dfaac492e8cee7dd22b8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
#pragma once

// Packed YUV 4:2:2 (YUYV / YVYU / UYVY / VYUY): two pixels per 32-bit
// word, one shared chroma pair. The Layout uses two C::Y entries plus
// one each of C::U / C::V; we resolve the duplicate Y via
// find_pos<C::Y>(n).

#include <array>

#include "../layout.h"
#include "detail.h"

namespace pixpat
{

template <typename L>
struct PackedYUVSource {
	using Layout = L;
	using Pixel  = YUV16;

	static_assert(L::kind == ColorKind::YUV);
	static_assert(L::num_planes == 1);
	static_assert(L::h_sub == 2 && L::v_sub == 1);

	using P = typename L::template plane<0>;
	static constexpr size_t y0_idx = P::template find_pos<C::Y>(0);
	static constexpr size_t y1_idx = P::template find_pos<C::Y>(1);
	static constexpr size_t u_idx  = P::template find_pos<C::U>();
	static constexpr size_t v_idx  = P::template find_pos<C::V>();

	static YUV16 read(const Buffer<1>& buf, size_t x, size_t y,
	                  [[maybe_unused]] size_t W,
	                  [[maybe_unused]] size_t H) noexcept
	{
		const uint8_t* p = buf.data[0] + y * buf.stride[0]
		                   + (x / 2) * P::bytes_per_pixel;
		const auto vals = P::unpack(detail::load_word<P>(p));
		const size_t y_pick = (x & 1) ? y1_idx : y0_idx;
		// Both Y components share the same bit width, so the bit-width
		// for y0 and y1 is identical — pick either.
		return YUV16{
		        detail::decode_norm(P::comps[y0_idx].bits, vals[y_pick]),
		        detail::decode_norm(P::comps[u_idx].bits, vals[u_idx]),
		        detail::decode_norm(P::comps[v_idx].bits, vals[v_idx]),
		        uint16_t(0),
		};
	}
};

template <typename L>
struct PackedYUVSink {
	using Layout = L;
	using Pixel  = YUV16;

	static_assert(L::kind == ColorKind::YUV);
	static_assert(L::num_planes == 1);
	static_assert(L::h_sub == 2 && L::v_sub == 1);

	using P = typename L::template plane<0>;
	static constexpr size_t y0_idx = P::template find_pos<C::Y>(0);
	static constexpr size_t y1_idx = P::template find_pos<C::Y>(1);
	static constexpr size_t u_idx  = P::template find_pos<C::U>();
	static constexpr size_t v_idx  = P::template find_pos<C::V>();

	static constexpr size_t block_h = 1;
	static constexpr size_t block_w = 2;

	static void write_block(Buffer<1>& buf, size_t bx, size_t by,
	                        const YUV16 (&block)[1][2]) noexcept
	{
		std::array<uint16_t, P::num_comps> v{};
		v[y0_idx] = detail::encode_norm(P::comps[y0_idx].bits, block[0][0].y);
		v[y1_idx] = detail::encode_norm(P::comps[y1_idx].bits, block[0][1].y);
		// Integer chroma averaging in normalized-16 space. Truncates
		// (no round-half-up).
		v[u_idx]  = detail::encode_norm(P::comps[u_idx].bits, uint16_t(
							(uint32_t(block[0][0].u) +
							 uint32_t(block[0][1].u)) / 2));
		v[v_idx]  = detail::encode_norm(P::comps[v_idx].bits, uint16_t(
							(uint32_t(block[0][0].v) +
							 uint32_t(block[0][1].v)) / 2));

		uint8_t* p = buf.data[0] + by * buf.stride[0]
		             + (bx / 2) * P::bytes_per_pixel;
		detail::store_word<P>(p, P::pack(v));
	}
};

} // namespace pixpat