summaryrefslogtreecommitdiff
path: root/subprojects/pixpat/pixpat-native/src/formats
diff options
context:
space:
mode:
authorTomi Valkeinen <tomi.valkeinen@ideasonboard.com>2026-05-08 17:22:58 +0300
committerTomi Valkeinen <tomi.valkeinen@ideasonboard.com>2026-05-08 17:22:58 +0300
commit4e2b291a4acdc2cbd39f005c88bda363bc06bd34 (patch)
treee90048d5973ad1164b109d575cf577af7daf50be /subprojects/pixpat/pixpat-native/src/formats
parent8f94b39040e79eccd9312ed1e467fe8ebfab8860 (diff)
parente0b7d30fd437292c88141fb08d60681870b86c6e (diff)
Merge commit 'e0b7d30fd437292c88141fb08d60681870b86c6e' as 'subprojects/pixpat'
Diffstat (limited to 'subprojects/pixpat/pixpat-native/src/formats')
-rw-r--r--subprojects/pixpat/pixpat-native/src/formats/bayer.h97
-rw-r--r--subprojects/pixpat/pixpat-native/src/formats/grayscale.h78
-rw-r--r--subprojects/pixpat/pixpat-native/src/formats/rgb.h267
-rw-r--r--subprojects/pixpat/pixpat-native/src/formats/yuv_packed.h136
-rw-r--r--subprojects/pixpat/pixpat-native/src/formats/yuv_planar.h76
-rw-r--r--subprojects/pixpat/pixpat-native/src/formats/yuv_semiplanar.h79
6 files changed, 733 insertions, 0 deletions
diff --git a/subprojects/pixpat/pixpat-native/src/formats/bayer.h b/subprojects/pixpat/pixpat-native/src/formats/bayer.h
new file mode 100644
index 0000000..057c342
--- /dev/null
+++ b/subprojects/pixpat/pixpat-native/src/formats/bayer.h
@@ -0,0 +1,97 @@
+#pragma once
+
+// Bayer raw layouts. Each pixel carries one of R/G/B selected by
+// (x mod 2, y mod 2) and BayerOrder; the pattern is on the
+// BayerSource/BayerSink template, not the layout itself. Storage shape
+// is single-component (C::Y reused as the storage tag) so the same
+// 8/10/12/16-bit shapes apply across all four phase patterns.
+//
+// Each format is a distinct struct (rather than a type alias of one
+// another) so each format type can carry its own pattern-specific
+// Source/Sink aliases. The shared bit layout lives in a base struct per
+// (depth,packing) combination.
+//
+// ColorKind is RGB because the normalized pixel passed through ColorXfm
+// is RGB16 — the sink picks one of r/g/b at write time, and the
+// source nearest-neighbor demosaics into RGB16 at read time.
+
+#include "../layout.h"
+#include "../io/bayer.h"
+
+namespace pixpat::formats
+{
+
+namespace bayer_detail
+{
+
+// Per-(depth,packing) base layouts. Every Bayer format derives from
+// one of these and pins its own pattern-specific I/O templates.
+using Bayer8 = Layout<ColorKind::RGB, 1, 1,
+ Plane<uint8_t, Comp { C::Y, 8, 0 }> >;
+using Bayer10 = Layout<ColorKind::RGB, 1, 1,
+ Plane<uint16_t, Comp { C::Y, 10, 0 }, Comp { C::X, 6, 10 }> >;
+using Bayer12 = Layout<ColorKind::RGB, 1, 1,
+ Plane<uint16_t, Comp { C::Y, 12, 0 }, Comp { C::X, 4, 12 }> >;
+using Bayer16 = Layout<ColorKind::RGB, 1, 1,
+ Plane<uint16_t, Comp { C::Y, 16, 0 }> >;
+// MIPI CSI-2 packed Bayer (10P: 4 pix in 5 bytes; 12P: 2 pix in 3
+// bytes). The Layout doesn't capture the packed bit layout — the
+// BayerPackedSink hand-rolls the byte writes. uint8_t plane shape is
+// a placeholder so the dispatch plumbing is uniform.
+using Bayer10P = Layout<ColorKind::RGB, 1, 1,
+ Plane<uint8_t, Comp { C::Y, 8, 0 }> >;
+using Bayer12P = Layout<ColorKind::RGB, 1, 1,
+ Plane<uint8_t, Comp { C::Y, 8, 0 }> >;
+
+} // namespace bayer_detail
+
+// Unpacked Bayer (4 patterns × 4 bit depths).
+#define PIXPAT_BAYER(name, base, pat) \
+ struct name : bayer_detail::base { \
+ using Source = BayerSource_ ## pat<name>; \
+ using Sink = BayerSink_ ## pat<name>; \
+ }
+
+PIXPAT_BAYER(SRGGB8, Bayer8, RGGB);
+PIXPAT_BAYER(SBGGR8, Bayer8, BGGR);
+PIXPAT_BAYER(SGRBG8, Bayer8, GRBG);
+PIXPAT_BAYER(SGBRG8, Bayer8, GBRG);
+
+PIXPAT_BAYER(SRGGB10, Bayer10, RGGB);
+PIXPAT_BAYER(SBGGR10, Bayer10, BGGR);
+PIXPAT_BAYER(SGRBG10, Bayer10, GRBG);
+PIXPAT_BAYER(SGBRG10, Bayer10, GBRG);
+
+PIXPAT_BAYER(SRGGB12, Bayer12, RGGB);
+PIXPAT_BAYER(SBGGR12, Bayer12, BGGR);
+PIXPAT_BAYER(SGRBG12, Bayer12, GRBG);
+PIXPAT_BAYER(SGBRG12, Bayer12, GBRG);
+
+PIXPAT_BAYER(SRGGB16, Bayer16, RGGB);
+PIXPAT_BAYER(SBGGR16, Bayer16, BGGR);
+PIXPAT_BAYER(SGRBG16, Bayer16, GRBG);
+PIXPAT_BAYER(SGBRG16, Bayer16, GBRG);
+
+#undef PIXPAT_BAYER
+
+// MIPI-packed Bayer: pattern + bit depth both encoded in the I/O
+// template name (BayerPackedSource_RGGB10, ...).
+#define PIXPAT_BAYER_PACKED(name, base, pat_depth) \
+ struct name : bayer_detail::base { \
+ using Source = BayerPackedSource_ ## pat_depth<name>; \
+ using Sink = BayerPackedSink_ ## pat_depth<name>; \
+ }
+
+PIXPAT_BAYER_PACKED(SRGGB10P, Bayer10P, RGGB10);
+PIXPAT_BAYER_PACKED(SBGGR10P, Bayer10P, BGGR10);
+PIXPAT_BAYER_PACKED(SGRBG10P, Bayer10P, GRBG10);
+PIXPAT_BAYER_PACKED(SGBRG10P, Bayer10P, GBRG10);
+
+PIXPAT_BAYER_PACKED(SRGGB12P, Bayer12P, RGGB12);
+PIXPAT_BAYER_PACKED(SBGGR12P, Bayer12P, BGGR12);
+PIXPAT_BAYER_PACKED(SGRBG12P, Bayer12P, GRBG12);
+PIXPAT_BAYER_PACKED(SGBRG12P, Bayer12P, GBRG12);
+
+#undef PIXPAT_BAYER_PACKED
+
+} // namespace pixpat::formats
diff --git a/subprojects/pixpat/pixpat-native/src/formats/grayscale.h b/subprojects/pixpat/pixpat-native/src/formats/grayscale.h
new file mode 100644
index 0000000..b1cd294
--- /dev/null
+++ b/subprojects/pixpat/pixpat-native/src/formats/grayscale.h
@@ -0,0 +1,78 @@
+#pragma once
+
+// Single-component-per-pixel formats. Most are grayscale (Y) modeled as
+// a YUV format with synthesized neutral chroma; R8 is the RGB-kind
+// counterpart, modeled grey-style with G=B=R on read. Y10/Y12 carry an
+// explicit X padding bitfield. XYYY2101010 is multi-pixel-per-word: 3 Y
+// samples in 32 bits.
+
+#include "../layout.h"
+#include "../io/gray.h"
+#include "../io/gray_packed.h"
+#include "../io/mono_rgb.h"
+
+namespace pixpat::formats
+{
+
+#define PIXPAT_GRAY(name, ...) \
+ struct name : Layout<ColorKind::YUV, 1, 1, __VA_ARGS__> { \
+ using Source = GraySource<name>; \
+ using Sink = GraySink<name>; \
+ }
+
+PIXPAT_GRAY(Y8,
+ Plane<uint8_t, Comp{ C::Y, 8, 0 }>);
+
+PIXPAT_GRAY(Y10,
+ Plane<uint16_t, Comp{ C::Y, 10, 0 }, Comp{ C::X, 6, 10 }>);
+
+PIXPAT_GRAY(Y12,
+ Plane<uint16_t, Comp{ C::Y, 12, 0 }, Comp{ C::X, 4, 12 }>);
+
+PIXPAT_GRAY(Y16,
+ Plane<uint16_t, Comp{ C::Y, 16, 0 }>);
+
+#undef PIXPAT_GRAY
+
+// R8: single 8-bit R channel. Read synthesizes G=B=R; write encodes R
+// and drops G/B/A. Symmetric to Y8 but ColorKind::RGB so cross-pipeline
+// conversions go through the RGB->YUV ColorXfm direction.
+struct R8 : Layout<ColorKind::RGB, 1, 1,
+ Plane<uint8_t, Comp{ C::R, 8, 0 }> > {
+ using Source = MonoRGBSource<R8>;
+ using Sink = MonoRGBSink<R8>;
+};
+
+struct XYYY2101010 : Layout<ColorKind::YUV, 1, 1,
+ Plane<uint32_t,
+ Comp{ C::Y, 10, 0 },
+ Comp{ C::Y, 10, 10 },
+ Comp{ C::Y, 10, 20 },
+ Comp{ C::X, 2, 30 }> > {
+ using Source = MultiPixelGraySource<XYYY2101010>;
+ using Sink = MultiPixelGraySink<XYYY2101010>;
+};
+
+// MIPI CSI-2 packed grayscale (Y10P / Y12P). The Layout doesn't capture
+// the packed bit layout — GrayPackedSource/Sink delegate to the shared
+// CSI-2 helper (io/csi2.h). uint8_t plane shape is a placeholder so
+// dispatch plumbing is uniform (mirrors bayer_detail::Bayer10P/12P).
+namespace gray_csi2_detail
+{
+using Gray10P = Layout<ColorKind::YUV, 1, 1,
+ Plane<uint8_t, Comp { C::Y, 8, 0 }> >;
+using Gray12P = Layout<ColorKind::YUV, 1, 1,
+ Plane<uint8_t, Comp { C::Y, 8, 0 }> >;
+} // namespace gray_csi2_detail
+
+struct Y10P : gray_csi2_detail::Gray10P {
+ using Source = GrayPackedSource<Y10P, 10>;
+ using Sink = GrayPackedSink<Y10P, 10>;
+};
+
+struct Y12P : gray_csi2_detail::Gray12P {
+ using Source = GrayPackedSource<Y12P, 12>;
+ using Sink = GrayPackedSink<Y12P, 12>;
+};
+
+} // namespace pixpat::formats
diff --git a/subprojects/pixpat/pixpat-native/src/formats/rgb.h b/subprojects/pixpat/pixpat-native/src/formats/rgb.h
new file mode 100644
index 0000000..19d007a
--- /dev/null
+++ b/subprojects/pixpat/pixpat-native/src/formats/rgb.h
@@ -0,0 +1,267 @@
+#pragma once
+
+// RGB packed layouts: 8-bit / 16-bit (sub-byte) / 32-bit (10-bit) /
+// 64-bit-normalized, all single-plane single-pixel-per-storage-word.
+// Names follow the kms++/pixutils register-order convention (MSB-first
+// in the storage word), so XRGB8888 has X at bits 31..24 and B at 7..0.
+
+#include "../layout.h"
+#include "../io/packed.h"
+
+namespace pixpat::formats
+{
+
+// Helper: every format in this file pairs with PackedSource/PackedSink.
+// Each format struct exposes Source / Sink aliases so the catalog row
+// in format_catalog.h can stay name-only.
+#define PIXPAT_RGB_PACKED(name, ...) \
+ struct name : Layout<ColorKind::RGB, 1, 1, __VA_ARGS__> { \
+ using Source = PackedSource<name>; \
+ using Sink = PackedSink<name>; \
+ }
+
+// ---------------------------------------------------------------------
+// 32-bit packed RGB, 8-bit components.
+// ---------------------------------------------------------------------
+
+PIXPAT_RGB_PACKED(XRGB8888,
+ Plane<uint32_t,
+ Comp{ C::B, 8, 0 },
+ Comp{ C::G, 8, 8 },
+ Comp{ C::R, 8, 16 },
+ Comp{ C::X, 8, 24 }>);
+
+PIXPAT_RGB_PACKED(ARGB8888,
+ Plane<uint32_t,
+ Comp{ C::B, 8, 0 },
+ Comp{ C::G, 8, 8 },
+ Comp{ C::R, 8, 16 },
+ Comp{ C::A, 8, 24 }>);
+
+PIXPAT_RGB_PACKED(XBGR8888,
+ Plane<uint32_t,
+ Comp{ C::R, 8, 0 },
+ Comp{ C::G, 8, 8 },
+ Comp{ C::B, 8, 16 },
+ Comp{ C::X, 8, 24 }>);
+
+PIXPAT_RGB_PACKED(ABGR8888,
+ Plane<uint32_t,
+ Comp{ C::R, 8, 0 },
+ Comp{ C::G, 8, 8 },
+ Comp{ C::B, 8, 16 },
+ Comp{ C::A, 8, 24 }>);
+
+PIXPAT_RGB_PACKED(RGBX8888,
+ Plane<uint32_t,
+ Comp{ C::X, 8, 0 },
+ Comp{ C::B, 8, 8 },
+ Comp{ C::G, 8, 16 },
+ Comp{ C::R, 8, 24 }>);
+
+PIXPAT_RGB_PACKED(RGBA8888,
+ Plane<uint32_t,
+ Comp{ C::A, 8, 0 },
+ Comp{ C::B, 8, 8 },
+ Comp{ C::G, 8, 16 },
+ Comp{ C::R, 8, 24 }>);
+
+PIXPAT_RGB_PACKED(BGRX8888,
+ Plane<uint32_t,
+ Comp{ C::X, 8, 0 },
+ Comp{ C::R, 8, 8 },
+ Comp{ C::G, 8, 16 },
+ Comp{ C::B, 8, 24 }>);
+
+PIXPAT_RGB_PACKED(BGRA8888,
+ Plane<uint32_t,
+ Comp{ C::A, 8, 0 },
+ Comp{ C::R, 8, 8 },
+ Comp{ C::G, 8, 16 },
+ Comp{ C::B, 8, 24 }>);
+
+// ---------------------------------------------------------------------
+// 24-bit packed RGB, three bytes per pixel. storage_t is uint32_t but
+// only bytes_per_pixel = 3 are read/written via memcpy.
+// ---------------------------------------------------------------------
+
+PIXPAT_RGB_PACKED(RGB888,
+ Plane<uint32_t,
+ Comp{ C::B, 8, 0 },
+ Comp{ C::G, 8, 8 },
+ Comp{ C::R, 8, 16 }>);
+
+PIXPAT_RGB_PACKED(BGR888,
+ Plane<uint32_t,
+ Comp{ C::R, 8, 0 },
+ Comp{ C::G, 8, 8 },
+ Comp{ C::B, 8, 16 }>);
+
+// ---------------------------------------------------------------------
+// 16-bit packed RGB, sub-byte components.
+// ---------------------------------------------------------------------
+
+PIXPAT_RGB_PACKED(RGB565,
+ Plane<uint16_t,
+ Comp{ C::B, 5, 0 },
+ Comp{ C::G, 6, 5 },
+ Comp{ C::R, 5, 11 }>);
+
+PIXPAT_RGB_PACKED(BGR565,
+ Plane<uint16_t,
+ Comp{ C::R, 5, 0 },
+ Comp{ C::G, 6, 5 },
+ Comp{ C::B, 5, 11 }>);
+
+// 8-bit packed RGB: 3-bit R / 3-bit G / 2-bit B in a single byte.
+
+PIXPAT_RGB_PACKED(RGB332,
+ Plane<uint8_t,
+ Comp{ C::B, 2, 0 },
+ Comp{ C::G, 3, 2 },
+ Comp{ C::R, 3, 5 }>);
+
+PIXPAT_RGB_PACKED(XRGB1555,
+ Plane<uint16_t,
+ Comp{ C::B, 5, 0 },
+ Comp{ C::G, 5, 5 },
+ Comp{ C::R, 5, 10 },
+ Comp{ C::X, 1, 15 }>);
+
+PIXPAT_RGB_PACKED(ARGB1555,
+ Plane<uint16_t,
+ Comp{ C::B, 5, 0 },
+ Comp{ C::G, 5, 5 },
+ Comp{ C::R, 5, 10 },
+ Comp{ C::A, 1, 15 }>);
+
+PIXPAT_RGB_PACKED(XBGR1555,
+ Plane<uint16_t,
+ Comp{ C::R, 5, 0 },
+ Comp{ C::G, 5, 5 },
+ Comp{ C::B, 5, 10 },
+ Comp{ C::X, 1, 15 }>);
+
+PIXPAT_RGB_PACKED(ABGR1555,
+ Plane<uint16_t,
+ Comp{ C::R, 5, 0 },
+ Comp{ C::G, 5, 5 },
+ Comp{ C::B, 5, 10 },
+ Comp{ C::A, 1, 15 }>);
+
+PIXPAT_RGB_PACKED(XRGB4444,
+ Plane<uint16_t,
+ Comp{ C::B, 4, 0 },
+ Comp{ C::G, 4, 4 },
+ Comp{ C::R, 4, 8 },
+ Comp{ C::X, 4, 12 }>);
+
+PIXPAT_RGB_PACKED(ARGB4444,
+ Plane<uint16_t,
+ Comp{ C::B, 4, 0 },
+ Comp{ C::G, 4, 4 },
+ Comp{ C::R, 4, 8 },
+ Comp{ C::A, 4, 12 }>);
+
+PIXPAT_RGB_PACKED(XBGR4444,
+ Plane<uint16_t,
+ Comp{ C::R, 4, 0 },
+ Comp{ C::G, 4, 4 },
+ Comp{ C::B, 4, 8 },
+ Comp{ C::X, 4, 12 }>);
+
+PIXPAT_RGB_PACKED(ABGR4444,
+ Plane<uint16_t,
+ Comp{ C::R, 4, 0 },
+ Comp{ C::G, 4, 4 },
+ Comp{ C::B, 4, 8 },
+ Comp{ C::A, 4, 12 }>);
+
+PIXPAT_RGB_PACKED(RGBX4444,
+ Plane<uint16_t,
+ Comp{ C::X, 4, 0 },
+ Comp{ C::B, 4, 4 },
+ Comp{ C::G, 4, 8 },
+ Comp{ C::R, 4, 12 }>);
+
+PIXPAT_RGB_PACKED(RGBA4444,
+ Plane<uint16_t,
+ Comp{ C::A, 4, 0 },
+ Comp{ C::B, 4, 4 },
+ Comp{ C::G, 4, 8 },
+ Comp{ C::R, 4, 12 }>);
+
+// ---------------------------------------------------------------------
+// 32-bit packed RGB, 10-bit components.
+// ---------------------------------------------------------------------
+
+PIXPAT_RGB_PACKED(XRGB2101010,
+ Plane<uint32_t,
+ Comp{ C::B, 10, 0 },
+ Comp{ C::G, 10, 10 },
+ Comp{ C::R, 10, 20 },
+ Comp{ C::X, 2, 30 }>);
+
+PIXPAT_RGB_PACKED(ARGB2101010,
+ Plane<uint32_t,
+ Comp{ C::B, 10, 0 },
+ Comp{ C::G, 10, 10 },
+ Comp{ C::R, 10, 20 },
+ Comp{ C::A, 2, 30 }>);
+
+PIXPAT_RGB_PACKED(XBGR2101010,
+ Plane<uint32_t,
+ Comp{ C::R, 10, 0 },
+ Comp{ C::G, 10, 10 },
+ Comp{ C::B, 10, 20 },
+ Comp{ C::X, 2, 30 }>);
+
+PIXPAT_RGB_PACKED(ABGR2101010,
+ Plane<uint32_t,
+ Comp{ C::R, 10, 0 },
+ Comp{ C::G, 10, 10 },
+ Comp{ C::B, 10, 20 },
+ Comp{ C::A, 2, 30 }>);
+
+PIXPAT_RGB_PACKED(RGBX1010102,
+ Plane<uint32_t,
+ Comp{ C::X, 2, 0 },
+ Comp{ C::B, 10, 2 },
+ Comp{ C::G, 10, 12 },
+ Comp{ C::R, 10, 22 }>);
+
+PIXPAT_RGB_PACKED(RGBA1010102,
+ Plane<uint32_t,
+ Comp{ C::A, 2, 0 },
+ Comp{ C::B, 10, 2 },
+ Comp{ C::G, 10, 12 },
+ Comp{ C::R, 10, 22 }>);
+
+PIXPAT_RGB_PACKED(BGRX1010102,
+ Plane<uint32_t,
+ Comp{ C::X, 2, 0 },
+ Comp{ C::R, 10, 2 },
+ Comp{ C::G, 10, 12 },
+ Comp{ C::B, 10, 22 }>);
+
+PIXPAT_RGB_PACKED(BGRA1010102,
+ Plane<uint32_t,
+ Comp{ C::A, 2, 0 },
+ Comp{ C::R, 10, 2 },
+ Comp{ C::G, 10, 12 },
+ Comp{ C::B, 10, 22 }>);
+
+// ---------------------------------------------------------------------
+// 64-bit normalized wide RGB (16 bits per component).
+// ---------------------------------------------------------------------
+
+PIXPAT_RGB_PACKED(ABGR16161616,
+ Plane<uint64_t,
+ Comp{ C::R, 16, 0 },
+ Comp{ C::G, 16, 16 },
+ Comp{ C::B, 16, 32 },
+ Comp{ C::A, 16, 48 }>);
+
+#undef PIXPAT_RGB_PACKED
+
+} // namespace pixpat::formats
diff --git a/subprojects/pixpat/pixpat-native/src/formats/yuv_packed.h b/subprojects/pixpat/pixpat-native/src/formats/yuv_packed.h
new file mode 100644
index 0000000..8e88f10
--- /dev/null
+++ b/subprojects/pixpat/pixpat-native/src/formats/yuv_packed.h
@@ -0,0 +1,136 @@
+#pragma once
+
+// Packed YUV layouts:
+// VUY888 — 1 pixel / 24-bit, 8-bit Y/U/V (storage uint32_t,
+// bytes_per_pixel = 3; parallels BGR888 in the YUV
+// register order)
+// XVUY8888 — 1 pixel / 32-bit word, 8-bit Y/U/V + 8-bit padding
+// XVUY2101010 — 1 pixel / 32-bit word, 10-bit Y/U/V + 2-bit padding
+// AVUY16161616 — 1 pixel / 64-bit word, 16-bit Y/U/V/A (normalized)
+// YUYV / YVYU / UYVY / VYUY — 4:2:2, 2 pixels / 32-bit word
+// Y210 / Y212 / Y216 — 4:2:2, 2 pixels / 64-bit word, with
+// each component MSB-aligned in a 16-bit slot
+//
+// XVUY/AVUY name is register MSB-first (X/A in the top bits). The
+// YUYV names follow V4L2 / pixpat memory-byte order (Y0 in byte 0),
+// so shifts ascend in name order — opposite of XRGB-style.
+
+#include "../layout.h"
+#include "../io/packed.h"
+#include "../io/packed_yuv.h"
+
+namespace pixpat::formats
+{
+
+// 1-pixel-per-word packed (single Pixel/Word; uses PackedSource/Sink).
+
+struct VUY888 : Layout<ColorKind::YUV, 1, 1,
+ Plane<uint32_t,
+ Comp{ C::Y, 8, 0 },
+ Comp{ C::U, 8, 8 },
+ Comp{ C::V, 8, 16 }> > {
+ using Source = PackedSource<VUY888>;
+ using Sink = PackedSink<VUY888>;
+};
+
+struct XVUY8888 : Layout<ColorKind::YUV, 1, 1,
+ Plane<uint32_t,
+ Comp{ C::Y, 8, 0 },
+ Comp{ C::U, 8, 8 },
+ Comp{ C::V, 8, 16 },
+ Comp{ C::X, 8, 24 }> > {
+ using Source = PackedSource<XVUY8888>;
+ using Sink = PackedSink<XVUY8888>;
+};
+
+struct XVUY2101010 : Layout<ColorKind::YUV, 1, 1,
+ Plane<uint32_t,
+ Comp{ C::Y, 10, 0 },
+ Comp{ C::U, 10, 10 },
+ Comp{ C::V, 10, 20 },
+ Comp{ C::X, 2, 30 }> > {
+ using Source = PackedSource<XVUY2101010>;
+ using Sink = PackedSink<XVUY2101010>;
+};
+
+struct AVUY16161616 : Layout<ColorKind::YUV, 1, 1,
+ Plane<uint64_t,
+ Comp{ C::Y, 16, 0 },
+ Comp{ C::U, 16, 16 },
+ Comp{ C::V, 16, 32 },
+ Comp{ C::A, 16, 48 }> > {
+ using Source = PackedSource<AVUY16161616>;
+ using Sink = PackedSink<AVUY16161616>;
+};
+
+// 2-pixel-per-word 4:2:2 (uses PackedYUVSource/Sink).
+
+#define PIXPAT_PACKED_YUV422(name, ...) \
+ struct name : Layout<ColorKind::YUV, 2, 1, \
+ Plane<uint32_t, __VA_ARGS__> > { \
+ using Source = PackedYUVSource<name>; \
+ using Sink = PackedYUVSink<name>; \
+ }
+
+PIXPAT_PACKED_YUV422(YUYV,
+ Comp{ C::Y, 8, 0 }, Comp{ C::U, 8, 8 },
+ Comp{ C::Y, 8, 16 }, Comp{ C::V, 8, 24 });
+
+PIXPAT_PACKED_YUV422(YVYU,
+ Comp{ C::Y, 8, 0 }, Comp{ C::V, 8, 8 },
+ Comp{ C::Y, 8, 16 }, Comp{ C::U, 8, 24 });
+
+PIXPAT_PACKED_YUV422(UYVY,
+ Comp{ C::U, 8, 0 }, Comp{ C::Y, 8, 8 },
+ Comp{ C::V, 8, 16 }, Comp{ C::Y, 8, 24 });
+
+PIXPAT_PACKED_YUV422(VYUY,
+ Comp{ C::V, 8, 0 }, Comp{ C::Y, 8, 8 },
+ Comp{ C::U, 8, 16 }, Comp{ C::Y, 8, 24 });
+
+#undef PIXPAT_PACKED_YUV422
+
+// Y210 / Y212 / Y216: 4:2:2, 2 pixels per 64-bit word, MSB-aligned in
+// 16-bit slots. Y210 has 6 unused LSBs per slot, Y212 has 4, Y216 has
+// none. The X padding entries pad total_bits to 64 so bytes_per_pixel
+// resolves to 8; PackedYUVSink leaves their slots zero via the
+// value-array zero-init (see io/packed_yuv.h).
+struct Y210 : Layout<ColorKind::YUV, 2, 1,
+ Plane<uint64_t,
+ Comp{ C::X, 6, 0 },
+ Comp{ C::Y, 10, 6 },
+ Comp{ C::X, 6, 16 },
+ Comp{ C::U, 10, 22 },
+ Comp{ C::X, 6, 32 },
+ Comp{ C::Y, 10, 38 },
+ Comp{ C::X, 6, 48 },
+ Comp{ C::V, 10, 54 }> > {
+ using Source = PackedYUVSource<Y210>;
+ using Sink = PackedYUVSink<Y210>;
+};
+
+struct Y212 : Layout<ColorKind::YUV, 2, 1,
+ Plane<uint64_t,
+ Comp{ C::X, 4, 0 },
+ Comp{ C::Y, 12, 4 },
+ Comp{ C::X, 4, 16 },
+ Comp{ C::U, 12, 20 },
+ Comp{ C::X, 4, 32 },
+ Comp{ C::Y, 12, 36 },
+ Comp{ C::X, 4, 48 },
+ Comp{ C::V, 12, 52 }> > {
+ using Source = PackedYUVSource<Y212>;
+ using Sink = PackedYUVSink<Y212>;
+};
+
+struct Y216 : Layout<ColorKind::YUV, 2, 1,
+ Plane<uint64_t,
+ Comp{ C::Y, 16, 0 },
+ Comp{ C::U, 16, 16 },
+ Comp{ C::Y, 16, 32 },
+ Comp{ C::V, 16, 48 }> > {
+ using Source = PackedYUVSource<Y216>;
+ using Sink = PackedYUVSink<Y216>;
+};
+
+} // namespace pixpat::formats
diff --git a/subprojects/pixpat/pixpat-native/src/formats/yuv_planar.h b/subprojects/pixpat/pixpat-native/src/formats/yuv_planar.h
new file mode 100644
index 0000000..bb6a415
--- /dev/null
+++ b/subprojects/pixpat/pixpat-native/src/formats/yuv_planar.h
@@ -0,0 +1,76 @@
+#pragma once
+
+// YUV planar layouts: 3 separate planes (Y, then U/V or V/U), 8-bit
+// components.
+// YUV420/YVU420 — h_sub=2, v_sub=2 (a.k.a. I420 / YV12)
+// YUV422/YVU422 — h_sub=2, v_sub=1
+// YUV444/YVU444 — h_sub=1, v_sub=1
+// T430 — multi-pixel-per-word planar 4:4:4.
+
+#include "../layout.h"
+#include "../io/planar.h"
+
+namespace pixpat::formats
+{
+
+#define PIXPAT_PLANAR(name, ...) \
+ struct name : Layout<ColorKind::YUV, __VA_ARGS__> { \
+ using Source = PlanarSource<name>; \
+ using Sink = PlanarSink<name>; \
+ }
+
+PIXPAT_PLANAR(YUV420, 2, 2,
+ Plane<uint8_t, Comp{ C::Y, 8, 0 }>,
+ Plane<uint8_t, Comp{ C::U, 8, 0 }>,
+ Plane<uint8_t, Comp{ C::V, 8, 0 }>);
+
+PIXPAT_PLANAR(YVU420, 2, 2,
+ Plane<uint8_t, Comp{ C::Y, 8, 0 }>,
+ Plane<uint8_t, Comp{ C::V, 8, 0 }>,
+ Plane<uint8_t, Comp{ C::U, 8, 0 }>);
+
+PIXPAT_PLANAR(YUV422, 2, 1,
+ Plane<uint8_t, Comp{ C::Y, 8, 0 }>,
+ Plane<uint8_t, Comp{ C::U, 8, 0 }>,
+ Plane<uint8_t, Comp{ C::V, 8, 0 }>);
+
+PIXPAT_PLANAR(YVU422, 2, 1,
+ Plane<uint8_t, Comp{ C::Y, 8, 0 }>,
+ Plane<uint8_t, Comp{ C::V, 8, 0 }>,
+ Plane<uint8_t, Comp{ C::U, 8, 0 }>);
+
+PIXPAT_PLANAR(YUV444, 1, 1,
+ Plane<uint8_t, Comp{ C::Y, 8, 0 }>,
+ Plane<uint8_t, Comp{ C::U, 8, 0 }>,
+ Plane<uint8_t, Comp{ C::V, 8, 0 }>);
+
+PIXPAT_PLANAR(YVU444, 1, 1,
+ Plane<uint8_t, Comp{ C::Y, 8, 0 }>,
+ Plane<uint8_t, Comp{ C::V, 8, 0 }>,
+ Plane<uint8_t, Comp{ C::U, 8, 0 }>);
+
+#undef PIXPAT_PLANAR
+
+// T430: 3-plane multi-pixel-per-word planar 4:4:4. Each plane carries
+// 3 × 10-bit samples per uint32_t plus a 2-bit X padding bit-field.
+struct T430 : Layout<ColorKind::YUV, 1, 1,
+ Plane<uint32_t,
+ Comp{ C::Y, 10, 0 },
+ Comp{ C::Y, 10, 10 },
+ Comp{ C::Y, 10, 20 },
+ Comp{ C::X, 2, 30 }>,
+ Plane<uint32_t,
+ Comp{ C::U, 10, 0 },
+ Comp{ C::U, 10, 10 },
+ Comp{ C::U, 10, 20 },
+ Comp{ C::X, 2, 30 }>,
+ Plane<uint32_t,
+ Comp{ C::V, 10, 0 },
+ Comp{ C::V, 10, 10 },
+ Comp{ C::V, 10, 20 },
+ Comp{ C::X, 2, 30 }> > {
+ using Source = MultiPixelPlanarSource<T430>;
+ using Sink = MultiPixelPlanarSink<T430>;
+};
+
+} // namespace pixpat::formats
diff --git a/subprojects/pixpat/pixpat-native/src/formats/yuv_semiplanar.h b/subprojects/pixpat/pixpat-native/src/formats/yuv_semiplanar.h
new file mode 100644
index 0000000..34aea22
--- /dev/null
+++ b/subprojects/pixpat/pixpat-native/src/formats/yuv_semiplanar.h
@@ -0,0 +1,79 @@
+#pragma once
+
+// YUV semiplanar layouts: Y plane + interleaved UV plane.
+// NV12/NV21 — 4:2:0 (h_sub=2, v_sub=2)
+// NV16/NV61 — 4:2:2 (h_sub=2, v_sub=1)
+// P030/P230 — multi-pixel-per-word semiplanar (10-bit Y triplets).
+
+#include "../layout.h"
+#include "../io/semiplanar.h"
+
+namespace pixpat::formats
+{
+
+struct NV12 : Layout<ColorKind::YUV, 2, 2,
+ Plane<uint8_t, Comp{ C::Y, 8, 0 }>,
+ Plane<uint16_t, Comp{ C::U, 8, 0 }, Comp{ C::V, 8, 8 }> > {
+ using Source = SemiplanarSource<NV12>;
+ using Sink = SemiplanarSink<NV12>;
+};
+
+struct NV21 : Layout<ColorKind::YUV, 2, 2,
+ Plane<uint8_t, Comp{ C::Y, 8, 0 }>,
+ Plane<uint16_t, Comp{ C::V, 8, 0 }, Comp{ C::U, 8, 8 }> > {
+ using Source = SemiplanarSource<NV21>;
+ using Sink = SemiplanarSink<NV21>;
+};
+
+struct NV16 : Layout<ColorKind::YUV, 2, 1,
+ Plane<uint8_t, Comp{ C::Y, 8, 0 }>,
+ Plane<uint16_t, Comp{ C::U, 8, 0 }, Comp{ C::V, 8, 8 }> > {
+ using Source = SemiplanarSource<NV16>;
+ using Sink = SemiplanarSink<NV16>;
+};
+
+struct NV61 : Layout<ColorKind::YUV, 2, 1,
+ Plane<uint8_t, Comp{ C::Y, 8, 0 }>,
+ Plane<uint16_t, Comp{ C::V, 8, 0 }, Comp{ C::U, 8, 8 }> > {
+ using Source = SemiplanarSource<NV61>;
+ using Sink = SemiplanarSink<NV61>;
+};
+
+// Multi-pixel-per-word semiplanar (P030: 4:2:0, P230: 4:2:2). Y plane
+// holds 3 × 10-bit Y samples per uint32_t (top 2 bits unused). UV plane
+// holds 3 × (Cb,Cr) pairs per uint64_t (10 bits each, with 2-bit gaps
+// at bits 30-31 and 62-63 — left implicit, no X declared).
+
+struct P030 : Layout<ColorKind::YUV, 2, 2,
+ Plane<uint32_t,
+ Comp{ C::Y, 10, 0 },
+ Comp{ C::Y, 10, 10 },
+ Comp{ C::Y, 10, 20 }>,
+ Plane<uint64_t,
+ Comp{ C::U, 10, 0 },
+ Comp{ C::V, 10, 10 },
+ Comp{ C::U, 10, 20 },
+ Comp{ C::V, 10, 32 },
+ Comp{ C::U, 10, 42 },
+ Comp{ C::V, 10, 52 }> > {
+ using Source = MultiPixelSemiplanarSource<P030>;
+ using Sink = MultiPixelSemiplanarSink<P030>;
+};
+
+struct P230 : Layout<ColorKind::YUV, 2, 1,
+ Plane<uint32_t,
+ Comp{ C::Y, 10, 0 },
+ Comp{ C::Y, 10, 10 },
+ Comp{ C::Y, 10, 20 }>,
+ Plane<uint64_t,
+ Comp{ C::U, 10, 0 },
+ Comp{ C::V, 10, 10 },
+ Comp{ C::U, 10, 20 },
+ Comp{ C::V, 10, 32 },
+ Comp{ C::U, 10, 42 },
+ Comp{ C::V, 10, 52 }> > {
+ using Source = MultiPixelSemiplanarSource<P230>;
+ using Sink = MultiPixelSemiplanarSink<P230>;
+};
+
+} // namespace pixpat::formats