mirror of https://github.com/SixLabors/ImageSharp
1 changed files with 147 additions and 0 deletions
@ -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; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the Y'Cb'Cr chroma subsampling information as part
|
||||
|
/// of the JPEG XL Frame Header.
|
||||
|
/// </summary>
|
||||
|
internal sealed class JxlYCbCrChromaSubsampling : IJxlFields |
||||
|
{ |
||||
|
private readonly int[] channelMode = new int[3]; |
||||
|
|
||||
|
private static ReadOnlySpan<byte> HShiftData => [0, 1, 1, 0]; |
||||
|
|
||||
|
private static ReadOnlySpan<byte> VShiftData => [0, 1, 0, 1]; |
||||
|
|
||||
|
public byte MaxHShift { get; private set; } |
||||
|
|
||||
|
public byte MaxVShift { get; private set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets a value indicating whether this 4:4:4 chroma subsampling.
|
||||
|
/// </summary>
|
||||
|
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;
|
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets a value indicating whether this 4:2:0 chroma subsampling.
|
||||
|
/// </summary>
|
||||
|
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
|
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets a value indicating whether this 4:2:2 chroma subsampling.
|
||||
|
/// </summary>
|
||||
|
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
|
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets a value indicating whether this 4:4:0 chroma subsampling.
|
||||
|
/// </summary>
|
||||
|
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<byte> hsample, ReadOnlySpan<byte> 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; |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue