Skip to content

Instantly share code, notes, and snippets.

@JimBobSquarePants
Created April 24, 2017 23:19
Show Gist options
  • Save JimBobSquarePants/2d7c2d0e62df76ef9c24ebd12ea62ad6 to your computer and use it in GitHub Desktop.
Save JimBobSquarePants/2d7c2d0e62df76ef9c24ebd12ea62ad6 to your computer and use it in GitHub Desktop.
LUT Tables for YCbCr -> Rgb conversion method
// <copyright file="YCbCrToRgbTables.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats.Jpeg.Components.Decoder
{
using System.Runtime.CompilerServices;
using ImageSharp.PixelFormats;
/// <summary>
/// Provides lookup tables for converting from YCbCr to Rgb colorspace.
/// Methods to build the tables are identical to libjpeg.
/// </summary>
internal struct YCbCrToRgbTables
{
private const int ScaleBits = 16;
private const int Half = 1 << (ScaleBits - 1);
private static readonly int[] CrRTable = new int[256];
private static readonly int[] CbBTable = new int[256];
private static readonly int[] CrGTable = new int[256];
private static readonly int[] CbGTable = new int[256];
/// <summary>
/// Optimized method to pack bytes to the image from the YCbCr color space.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="packed">The packed pixel.</param>
/// <param name="y">The y luminance component.</param>
/// <param name="cb">The cb chroma component.</param>
/// <param name="cr">The cr chroma component.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Pack<TPixel>(ref TPixel packed, byte y, byte cb, byte cr)
where TPixel : struct, IPixel<TPixel>
{
byte r = (byte)(y + CrRTable[cr]).Clamp(0, 255);
// The values for the G calculation are left scaled up, since we must add them together before rounding.
byte g = (byte)(y + RightShift(CbGTable[cb] + CrGTable[cr])).Clamp(0, 255);
byte b = (byte)(y + CbBTable[cb]).Clamp(0, 255);
packed.PackFromBytes(r, g, b, byte.MaxValue);
}
/// <summary>
/// Initializes the YCbCr tables
/// </summary>
/// <returns>The intialized <see cref="YCbCrToRgbTables"/></returns>
public YCbCrToRgbTables Init()
{
for (int i = 0, x = -128; i <= 255; i++, x++)
{
// i is the actual input pixel value, in the range 0..255
// The Cb or Cr value we are thinking of is x = i - 128
// Cr=>R value is nearest int to 1.402 * x
CrRTable[i] = RightShift((Fix(1.402F) * x) + Half);
// Cb=>B value is nearest int to 1.772 * x
CbBTable[i] = RightShift((Fix(1.772F) * x) + Half);
// Cr=>G value is scaled-up -0.714136286
CrGTable[i] = (-Fix(0.714136286F)) * x;
// Cb => G value is scaled - up - 0.344136286 * x
// We also add in Half so that need not do it in inner loop
CbGTable[i] = ((-Fix(0.344136286F)) * x) + Half;
}
return this;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int Fix(float x)
{
return (int)((x * (1L << ScaleBits)) + 0.5F);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int RightShift(int x)
{
return x >> ScaleBits;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment