diff --git a/src/ImageSharp/Formats/Jxl/IO/JxlYCbCrChromaSubsampling.cs b/src/ImageSharp/Formats/Jxl/IO/JxlYCbCrChromaSubsampling.cs
new file mode 100644
index 000000000..90d9cf673
--- /dev/null
+++ b/src/ImageSharp/Formats/Jxl/IO/JxlYCbCrChromaSubsampling.cs
@@ -0,0 +1,147 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using SixLabors.ImageSharp.Formats.Jxl.Fields;
+
+namespace SixLabors.ImageSharp.Formats.Jxl.IO;
+
+///
+/// Gets the Y'Cb'Cr chroma subsampling information as part
+/// of the JPEG XL Frame Header.
+///
+internal sealed class JxlYCbCrChromaSubsampling : IJxlFields
+{
+ private readonly int[] channelMode = new int[3];
+
+ private static ReadOnlySpan HShiftData => [0, 1, 1, 0];
+
+ private static ReadOnlySpan VShiftData => [0, 1, 0, 1];
+
+ public byte MaxHShift { get; private set; }
+
+ public byte MaxVShift { get; private set; }
+
+ ///
+ /// Gets a value indicating whether this 4:4:4 chroma subsampling.
+ ///
+ public bool Is444 =>
+ this.HShift(0) == 0 && this.VShift(0) == 0 && // Cb
+ this.HShift(2) == 0 && this.VShift(2) == 0 && // Cr
+ this.HShift(1) == 0 && this.VShift(1) == 0; // Y;
+
+ ///
+ /// Gets a value indicating whether this 4:2:0 chroma subsampling.
+ ///
+ public bool Is420 =>
+ this.HShift(0) == 1 && this.VShift(0) == 1 && // Cb
+ this.HShift(2) == 1 && this.VShift(2) == 1 && // Cr
+ this.HShift(1) == 0 && this.VShift(1) == 0; // Y
+
+ ///
+ /// Gets a value indicating whether this 4:2:2 chroma subsampling.
+ ///
+ public bool Is422 =>
+ this.HShift(0) == 1 && this.VShift(0) == 0 && // Cb
+ this.HShift(2) == 1 && this.VShift(2) == 0 && // Cr
+ this.HShift(1) == 0 && this.VShift(1) == 0; // Y
+
+ ///
+ /// Gets a value indicating whether this 4:4:0 chroma subsampling.
+ ///
+ public bool Is440 =>
+ this.HShift(0) == 0 && this.VShift(0) == 1 && // Cb
+ this.HShift(2) == 0 && this.VShift(2) == 1 && // Cr
+ this.HShift(1) == 0 && this.VShift(1) == 0; // Y
+
+ public byte RawHShift(int c) => HShiftData[this.channelMode[c]];
+
+ public byte RawVShift(int c) => VShiftData[this.channelMode[c]];
+
+ public byte HShift(int c) => (byte)(this.MaxHShift - HShiftData[this.channelMode[c]]);
+
+ public byte VShift(int c) => (byte)(this.MaxVShift - VShiftData[this.channelMode[c]]);
+
+ private void Recompute()
+ {
+ this.MaxHShift = 0;
+ this.MaxVShift = 0;
+
+ for (int i = 0; i < 3; i++)
+ {
+ int ch = this.channelMode[i];
+
+ this.MaxHShift = Math.Max(this.MaxHShift, HShiftData[ch]);
+ this.MaxVShift = Math.Max(this.MaxVShift, VShiftData[ch]);
+ }
+ }
+
+ public bool Set(ReadOnlySpan hsample, ReadOnlySpan vsample)
+ {
+ for (int c = 0; c < 3; c++)
+ {
+ int cjpeg = c < 2 ? (c ^ 1) : c;
+ int i = 0;
+
+ for (; i < 4; i++)
+ {
+ if (1 << HShiftData[i] == hsample[cjpeg] && 1 << VShiftData[i] == vsample[cjpeg])
+ {
+ this.channelMode[c] = i;
+ break;
+ }
+ }
+
+ if (i == 4)
+ {
+ return false;
+ }
+ }
+
+ this.Recompute();
+ return true;
+ }
+
+ public override string ToString()
+ {
+ if (this.Is444)
+ {
+ return "4:4:4";
+ }
+ else if (this.Is420)
+ {
+ return "4:2:0";
+ }
+ else if (this.Is422)
+ {
+ return "4:2:2";
+ }
+ else if (this.Is440)
+ {
+ return "4:4:0";
+ }
+ else
+ {
+ return $"[Custom] {this.channelMode[0]}:{this.channelMode[1]}:{this.channelMode[2]}";
+ }
+ }
+
+ public bool Visit(JxlVisitor visitor)
+ {
+ for (int i = 0; i < 3; i++)
+ {
+ int channel = this.channelMode[i];
+
+ uint unsignedChannel = (uint)channel;
+ bool wroteSuccessfully = visitor.Bits(2, 0, ref unsignedChannel);
+
+ if (!wroteSuccessfully)
+ {
+ return false;
+ }
+
+ this.channelMode[i] = (int)unsignedChannel;
+ }
+
+ return true;
+ }
+}