mirror of https://github.com/SixLabors/ImageSharp
4 changed files with 213 additions and 54 deletions
@ -0,0 +1,95 @@ |
|||
// <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.Jpg |
|||
{ |
|||
using System.Runtime.CompilerServices; |
|||
|
|||
using ImageSharp.PixelFormats; |
|||
|
|||
/// <summary>
|
|||
/// Provides 8-bit lookup tables for converting from YCbCr to Rgb colorspace.
|
|||
/// Methods to build the tables are based on libjpeg implementation.
|
|||
/// </summary>
|
|||
internal struct YCbCrToRgbTables |
|||
{ |
|||
// Speediest right-shift on some machines and gives us enough accuracy at 4 decimal places.
|
|||
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> |
|||
{ |
|||
// float r = MathF.Round(y + (1.402F * cr), MidpointRounding.AwayFromZero);
|
|||
byte r = (byte)(y + CrRTable[cr]).Clamp(0, 255); |
|||
|
|||
// float g = MathF.Round(y - (0.344136F * cb) - (0.714136F * cr), MidpointRounding.AwayFromZero);
|
|||
// 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); |
|||
|
|||
// float b = MathF.Round(y + (1.772F * cb), MidpointRounding.AwayFromZero);
|
|||
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; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,106 @@ |
|||
// <copyright file="RgbToYCbCrTables.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp.Formats.Jpg |
|||
{ |
|||
using System.Runtime.CompilerServices; |
|||
|
|||
/// <summary>
|
|||
/// Provides 8-bit lookup tables for converting from Rgb to YCbCr colorspace.
|
|||
/// Methods to build the tables are based on libjpeg implementation.
|
|||
/// </summary>
|
|||
internal struct RgbToYCbCrTables |
|||
{ |
|||
// Speediest right-shift on some machines and gives us enough accuracy at 4 decimal places.
|
|||
private const int ScaleBits = 16; |
|||
|
|||
private const int GYOffset = 256; |
|||
|
|||
private const int BYOffset = 2 * 256; |
|||
|
|||
private const int RCbOffset = 3 * 256; |
|||
|
|||
private const int GCbOffset = 4 * 256; |
|||
|
|||
private const int BCbOffset = 5 * 256; |
|||
|
|||
// B=>Cb and R=>Cr are the same
|
|||
private const int RCrOffset = BCbOffset; |
|||
|
|||
private const int GCrOffset = 6 * 256; |
|||
|
|||
private const int BCrOffset = 7 * 256; |
|||
|
|||
private const int CBCrOffset = 128 << ScaleBits; |
|||
|
|||
private const int Half = 1 << (ScaleBits - 1); |
|||
|
|||
private static readonly int[] YCbCrTable = new int[8 * 256]; |
|||
|
|||
/// <summary>
|
|||
/// Optimized method to allocates the correct y, cb, and cr values to the DCT blocks from the given r, g, b values.
|
|||
/// </summary>
|
|||
/// <param name="yBlockRaw">The The luminance block.</param>
|
|||
/// <param name="cbBlockRaw">The red chroma block.</param>
|
|||
/// <param name="crBlockRaw">The blue chroma block.</param>
|
|||
/// <param name="index">The current index.</param>
|
|||
/// <param name="r">The red value.</param>
|
|||
/// <param name="g">The green value.</param>
|
|||
/// <param name="b">The blue value.</param>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public unsafe void Allocate(ref float* yBlockRaw, ref float* cbBlockRaw, ref float* crBlockRaw, int index, int r, int g, int b) |
|||
{ |
|||
// float y = (0.299F * r) + (0.587F * g) + (0.114F * b);
|
|||
yBlockRaw[index] = (YCbCrTable[r] + YCbCrTable[g + GYOffset] + YCbCrTable[b + BYOffset]) >> ScaleBits; |
|||
|
|||
// float cb = 128F + ((-0.168736F * r) - (0.331264F * g) + (0.5F * b));
|
|||
cbBlockRaw[index] = (YCbCrTable[r + RCbOffset] + YCbCrTable[g + GCbOffset] + YCbCrTable[b + BCbOffset]) >> ScaleBits; |
|||
|
|||
// float b = MathF.Round(y + (1.772F * cb), MidpointRounding.AwayFromZero);
|
|||
crBlockRaw[index] = (YCbCrTable[r + RCrOffset] + YCbCrTable[g + GCrOffset] + YCbCrTable[b + BCrOffset]) >> ScaleBits; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes the YCbCr tables
|
|||
/// </summary>
|
|||
/// <returns>The intialized <see cref="RgbToYCbCrTables"/></returns>
|
|||
public RgbToYCbCrTables Init() |
|||
{ |
|||
for (int i = 0; i <= 255; i++) |
|||
{ |
|||
// The values for the calculations are left scaled up since we must add them together before rounding.
|
|||
YCbCrTable[i] = Fix(0.299F) * i; |
|||
YCbCrTable[i + GYOffset] = Fix(0.587F) * i; |
|||
YCbCrTable[i + BYOffset] = (Fix(0.114F) * i) + Half; |
|||
YCbCrTable[i + RCbOffset] = (-Fix(0.168735892F)) * i; |
|||
YCbCrTable[i + GCbOffset] = (-Fix(0.331264108F)) * i; |
|||
|
|||
// We use a rounding fudge - factor of 0.5 - epsilon for Cb and Cr.
|
|||
// This ensures that the maximum output will round to 255
|
|||
// not 256, and thus that we don't have to range-limit.
|
|||
//
|
|||
// B=>Cb and R=>Cr tables are the same
|
|||
YCbCrTable[i + BCbOffset] = (Fix(0.5F) * i) + CBCrOffset + Half - 1; |
|||
|
|||
YCbCrTable[i + GCrOffset] = (-Fix(0.418687589F)) * i; |
|||
YCbCrTable[i + BCrOffset] = (-Fix(0.081312411F)) * i; |
|||
} |
|||
|
|||
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; |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue