|
|
|
@ -9,6 +9,13 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1; |
|
|
|
|
|
|
|
internal class Av1YuvConverter |
|
|
|
{ |
|
|
|
// BT.709 SPecificatiuon constants.
|
|
|
|
private const int UMax = (int)(0.436 * 255); |
|
|
|
private const int VMax = (int)(0.615 * 255); |
|
|
|
private const int Wr = (int)(0.2126 * 255); |
|
|
|
private const int Wb = (int)(0.0722 * 255); |
|
|
|
private const int Wg = 255 - Wr - Wb; |
|
|
|
|
|
|
|
public static void ConvertToRgb<TPixel>(Configuration configuration, Av1FrameBuffer<byte> frameBuffer, ImageFrame<TPixel> image) |
|
|
|
where TPixel : unmanaged, IPixel<TPixel> |
|
|
|
{ |
|
|
|
@ -57,10 +64,10 @@ internal class Av1YuvConverter |
|
|
|
{ |
|
|
|
// Weight multiplied by 256 to exploit full byte resolution, rounded to the nearest integer.
|
|
|
|
// Using BT.709 specification
|
|
|
|
const int rvWeight = (int)(1.28033 * 256); |
|
|
|
const int guWeight = (int)(-0.21482 * 256); |
|
|
|
const int gvWeight = (int)(-0.38059 * 256); |
|
|
|
const int buWeight = (int)(2.12798 * 256); |
|
|
|
const int rvWeight = (int)(1.28033 * 255); |
|
|
|
const int guWeight = (int)(-0.21482 * 255); |
|
|
|
const int gvWeight = (int)(-0.38059 * 255); |
|
|
|
const int buWeight = (int)(2.12798 * 255); |
|
|
|
Guard.NotNull(buffer.BufferY); |
|
|
|
Guard.NotNull(buffer.BufferCb); |
|
|
|
Guard.NotNull(buffer.BufferCr); |
|
|
|
@ -98,9 +105,11 @@ internal class Av1YuvConverter |
|
|
|
ref byte vRef = ref vSpan[0]; |
|
|
|
for (int x = 0; x < image.Width; x++) |
|
|
|
{ |
|
|
|
pixel.R = (byte)Av1Math.Clip3(0, 255, ((yRef << 8) + (rvWeight * vRef)) >> 8); |
|
|
|
pixel.G = (byte)Av1Math.Clip3(0, 255, ((yRef << 8) + (guWeight * uRef) + (gvWeight * vRef)) >> 8); |
|
|
|
pixel.B = (byte)Av1Math.Clip3(0, 255, ((yRef << 8) + (buWeight * uRef)) >> 8); |
|
|
|
int u = uRef; // ((uRef - 127) * 2 * UMax) / 255;
|
|
|
|
int v = vRef; // ((vRef - 127) * 2 * VMax) / 255;
|
|
|
|
pixel.R = (byte)Av1Math.Clip3(0, 255, yRef + (v * (255 - Wr) / VMax)); |
|
|
|
pixel.G = (byte)Av1Math.Clip3(0, 255, yRef - ((u * Wb * (255 - Wb)) / (UMax * Wg)) - ((v * Wr * (255 - Wr)) / (VMax * Wg))); |
|
|
|
pixel.B = (byte)Av1Math.Clip3(0, 255, yRef + ((u * (255 - Wb)) / UMax)); |
|
|
|
pixel = ref Unsafe.Add(ref pixel, 1); |
|
|
|
yRef = ref Unsafe.Add(ref yRef, 1); |
|
|
|
uRef = ref Unsafe.Add(ref uRef, 1); |
|
|
|
@ -113,15 +122,15 @@ internal class Av1YuvConverter |
|
|
|
private static void ConvertRgbToYuv444(ImageFrame<Rgb24> image, Av1FrameBuffer<byte> buffer) |
|
|
|
{ |
|
|
|
// Weight multiplied by 256 to exploit full byte resolution, rounded to the nearest integer.
|
|
|
|
const int yrWeight = (int)(0.2126 * 256); |
|
|
|
const int ygWeight = (int)(0.7152 * 256); |
|
|
|
const int ybWeight = (int)(0.0722 * 256); |
|
|
|
const int urWeight = (int)(-0.09991 * 256); |
|
|
|
const int ugWeight = (int)(-0.33609 * 256); |
|
|
|
const int ubWeight = (int)(0.436 * 256); |
|
|
|
const int vrWeight = (int)(0.615 * 256); |
|
|
|
const int vgWeight = (int)(-0.55861 * 256); |
|
|
|
const int vbWeight = (int)(-0.05639 * 256); |
|
|
|
const int yrWeight = (int)(0.2126 * 255); |
|
|
|
const int ygWeight = (int)(0.7152 * 255); |
|
|
|
const int ybWeight = (int)(0.0722 * 255); |
|
|
|
const int urWeight = (int)(-0.09991 * 255); |
|
|
|
const int ugWeight = (int)(-0.33609 * 255); |
|
|
|
const int ubWeight = (int)(0.436 * 255); |
|
|
|
const int vrWeight = (int)(0.615 * 255); |
|
|
|
const int vgWeight = (int)(-0.55861 * 255); |
|
|
|
const int vbWeight = (int)(-0.05639 * 255); |
|
|
|
Guard.NotNull(buffer.BufferY); |
|
|
|
Guard.NotNull(buffer.BufferCb); |
|
|
|
Guard.NotNull(buffer.BufferCr); |
|
|
|
@ -149,9 +158,15 @@ internal class Av1YuvConverter |
|
|
|
ref byte vRef = ref vSpan[0]; |
|
|
|
for (int x = 0; x < image.Width; x++) |
|
|
|
{ |
|
|
|
yRef = (byte)Av1Math.Clip3(0, 255, ((yrWeight * pixel.R) + (ygWeight * pixel.G) + (ybWeight * pixel.B)) >> 8); |
|
|
|
uRef = (byte)Av1Math.Clip3(0, 255, ((urWeight * pixel.R) + (ugWeight * pixel.G) + (ubWeight * pixel.B)) >> 8); |
|
|
|
vRef = (byte)Av1Math.Clip3(0, 255, ((vrWeight * pixel.R) + (vgWeight * pixel.G) + (vbWeight * pixel.B)) >> 8); |
|
|
|
yRef = (byte)Av1Math.Clip3(0, 255, ((Wr * pixel.R) + (Wg * pixel.G) + (Wb * pixel.B)) / 255); |
|
|
|
|
|
|
|
// Not normalized, where range is [-UMax, UMax] or [-VMax, VMax]
|
|
|
|
// uRef = (byte)((UMax * (pixel.B - y)) / (255 - Wb));
|
|
|
|
// vRef = (byte)((VMax * (pixel.R - y)) / (255 - Wr));
|
|
|
|
|
|
|
|
// Normalized calculations
|
|
|
|
uRef = (byte)Av1Math.Clip3(0, 255, ((UMax * (pixel.B - yRef) / (255 - Wb)) + UMax) * 255 / (2 * UMax)); |
|
|
|
vRef = (byte)Av1Math.Clip3(0, 255, ((VMax * (pixel.R - yRef) / (255 - Wr)) + VMax) * 255 / (2 * VMax)); |
|
|
|
pixel = ref Unsafe.Add(ref pixel, 1); |
|
|
|
yRef = ref Unsafe.Add(ref yRef, 1); |
|
|
|
uRef = ref Unsafe.Add(ref uRef, 1); |
|
|
|
|