Browse Source

All PixelArea<T> usages in JpegEncoder replaced by GenericBlock8x8<TPixel> + YCbCrForwardConverter<TPixel>

pull/475/head
Anton Firszov 8 years ago
parent
commit
62f8ba211b
  1. 5
      src/ImageSharp/Formats/Jpeg/Common/GenericBlock8x8.cs
  2. 78
      src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/RgbToYCbCrTables.cs
  3. 83
      src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/YCbCrForwardConverter{TPixel}.cs
  4. 100
      src/ImageSharp/Formats/Jpeg/GolangPort/JpegEncoderCore.cs

5
src/ImageSharp/Formats/Jpeg/Common/GenericBlock8x8.cs

@ -7,9 +7,12 @@ using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Formats.Jpeg.Common namespace SixLabors.ImageSharp.Formats.Jpeg.Common
{ {
using System.Runtime.InteropServices;
/// <summary> /// <summary>
/// A generic 8x8 block implementation, useful for manipulating custom 8x8 pixel data. /// A generic 8x8 block implementation, useful for manipulating custom 8x8 pixel data.
/// </summary> /// </summary>
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming // ReSharper disable once InconsistentNaming
internal unsafe partial struct GenericBlock8x8<T> internal unsafe partial struct GenericBlock8x8<T>
where T : struct where T : struct
@ -87,7 +90,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
} }
/// <summary> /// <summary>
/// ONLY FOR GenericBlock instances living on the stack! /// Only for on-stack instances!
/// </summary> /// </summary>
public Span<T> AsSpanUnsafe() => new Span<T>(Unsafe.AsPointer(ref this), Size); public Span<T> AsSpanUnsafe() => new Span<T>(Unsafe.AsPointer(ref this), Size);

78
src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/RgbToYCbCrTables.cs

@ -2,15 +2,11 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Formats.Jpeg.Common;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Encoder namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Encoder
{ {
using System;
/// <summary> /// <summary>
/// Provides 8-bit lookup tables for converting from Rgb to YCbCr colorspace. /// Provides 8-bit lookup tables for converting from Rgb to YCbCr colorspace.
/// Methods to build the tables are based on libjpeg implementation. /// Methods to build the tables are based on libjpeg implementation.
@ -101,27 +97,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Encoder
/// TODO: Replace this logic with SIMD conversion (similar to the one in the decoder)! /// TODO: Replace this logic with SIMD conversion (similar to the one in the decoder)!
/// Optimized method to allocates the correct y, cb, and cr values to the DCT blocks from the given r, g, b values. /// Optimized method to allocates the correct y, cb, and cr values to the DCT blocks from the given r, g, b values.
/// </summary> /// </summary>
/// <param name="tables">The reference to the tables instance.</param>
/// <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 static void Rgb2YCbCr(RgbToYCbCrTables* tables, float* yBlockRaw, float* cbBlockRaw, float* crBlockRaw, int index, int r, int g, int b)
{
// float y = (0.299F * r) + (0.587F * g) + (0.114F * b);
yBlockRaw[index] = (tables->YRTable[r] + tables->YGTable[g] + tables->YBTable[b]) >> ScaleBits;
// float cb = 128F + ((-0.168736F * r) - (0.331264F * g) + (0.5F * b));
cbBlockRaw[index] = (tables->CbRTable[r] + tables->CbGTable[g] + tables->CbBTable[b]) >> ScaleBits;
// float b = MathF.Round(y + (1.772F * cb), MidpointRounding.AwayFromZero);
crBlockRaw[index] = (tables->CbBTable[r] + tables->CrGTable[g] + tables->CrBTable[b]) >> ScaleBits;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ConvertPixelInto(int r, int g, int b, ref float yResult, ref float cbResult, ref float crResult) public void ConvertPixelInto(int r, int g, int b, ref float yResult, ref float cbResult, ref float crResult)
{ {
@ -155,55 +130,4 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Encoder
return x >> ScaleBits; return x >> ScaleBits;
} }
} }
// TODO!
internal struct YCbCrForwardConverter<TPixel>
where TPixel : struct, IPixel<TPixel>
{
public Block8x8F Y;
public Block8x8F Cb;
public Block8x8F Cr;
private RgbToYCbCrTables colorTables;
private GenericBlock8x8<TPixel> pixelBlock;
private GenericBlock8x8<Rgb24> rgbBlock;
public static YCbCrForwardConverter<TPixel> Create()
{
var result = default(YCbCrForwardConverter<TPixel>);
result.colorTables = RgbToYCbCrTables.Create();
return result;
}
public void Convert(IPixelSource<TPixel> pixels, int x, int y)
{
this.pixelBlock.LoadAndStretchEdges(pixels, x, y);
Span<Rgb24> rgbSpan = this.rgbBlock.AsSpanUnsafe();
PixelOperations<TPixel>.Instance.ToRgb24(this.pixelBlock.AsSpanUnsafe(), rgbSpan, 64);
ref float yBlockStart = ref Unsafe.As<Block8x8F, float>(ref this.Y);
ref float cbBlockStart = ref Unsafe.As<Block8x8F, float>(ref this.Cb);
ref float crBlockStart = ref Unsafe.As<Block8x8F, float>(ref this.Cr);
ref Rgb24 rgbStart = ref rgbSpan[0];
for (int i = 0; i < 64; i++)
{
ref Rgb24 c = ref Unsafe.Add(ref rgbStart, i);
this.colorTables.ConvertPixelInto(
c.R,
c.G,
c.B,
ref Unsafe.Add(ref yBlockStart, i),
ref Unsafe.Add(ref cbBlockStart, i),
ref Unsafe.Add(ref crBlockStart, i)
);
}
}
}
} }

83
src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/YCbCrForwardConverter{TPixel}.cs

@ -0,0 +1,83 @@
using System;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Formats.Jpeg.Common;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Encoder
{
/// <summary>
/// On-stack worker struct to efficiently encapsulate the TPixel -> Rgb24 -> YCbCr conversion chain of 8x8 pixel blocks.
/// </summary>
/// <typeparam name="TPixel">The pixel type to work on</typeparam>
internal struct YCbCrForwardConverter<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// The Y component
/// </summary>
public Block8x8F Y;
/// <summary>
/// The Cb component
/// </summary>
public Block8x8F Cb;
/// <summary>
/// The Cr component
/// </summary>
public Block8x8F Cr;
/// <summary>
/// The color conversion tables
/// </summary>
private RgbToYCbCrTables colorTables;
/// <summary>
/// Temporal 8x8 block to hold TPixel data
/// </summary>
private GenericBlock8x8<TPixel> pixelBlock;
/// <summary>
/// Temporal RGB block
/// </summary>
private GenericBlock8x8<Rgb24> rgbBlock;
public static YCbCrForwardConverter<TPixel> Create()
{
var result = default(YCbCrForwardConverter<TPixel>);
result.colorTables = RgbToYCbCrTables.Create();
return result;
}
/// <summary>
/// Converts a 8x8 image area inside 'pixels' at position (x,y) placing the result members of the structure (<see cref="Y"/>, <see cref="Cb"/>, <see cref="Cr"/>)
/// </summary>
public void Convert(IPixelSource<TPixel> pixels, int x, int y)
{
this.pixelBlock.LoadAndStretchEdges(pixels, x, y);
Span<Rgb24> rgbSpan = this.rgbBlock.AsSpanUnsafe();
PixelOperations<TPixel>.Instance.ToRgb24(this.pixelBlock.AsSpanUnsafe(), rgbSpan, 64);
ref float yBlockStart = ref Unsafe.As<Block8x8F, float>(ref this.Y);
ref float cbBlockStart = ref Unsafe.As<Block8x8F, float>(ref this.Cb);
ref float crBlockStart = ref Unsafe.As<Block8x8F, float>(ref this.Cr);
ref Rgb24 rgbStart = ref rgbSpan[0];
for (int i = 0; i < 64; i++)
{
ref Rgb24 c = ref Unsafe.Add(ref rgbStart, i);
this.colorTables.ConvertPixelInto(
c.R,
c.G,
c.B,
ref Unsafe.Add(ref yBlockStart, i),
ref Unsafe.Add(ref cbBlockStart, i),
ref Unsafe.Add(ref crBlockStart, i)
);
}
}
}
}

100
src/ImageSharp/Formats/Jpeg/GolangPort/JpegEncoderCore.cs

@ -868,11 +868,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
this.Encode444(image); this.Encode444(image);
break; break;
case JpegSubsample.Ratio420: case JpegSubsample.Ratio420:
using (var pixels = image.Lock()) this.Encode420(image);
{
this.Encode420(pixels);
}
break; break;
} }
@ -886,7 +882,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="pixels">The pixel accessor providing access to the image pixels.</param> /// <param name="pixels">The pixel accessor providing access to the image pixels.</param>
private void Encode420<TPixel>(PixelAccessor<TPixel> pixels) private void Encode420<TPixel>(Image<TPixel> pixels)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
// TODO: Need a JpegScanEncoder<TPixel> class or struct that encapsulates the scan-encoding implementation. (Similar to JpegScanDecoder.) // TODO: Need a JpegScanEncoder<TPixel> class or struct that encapsulates the scan-encoding implementation. (Similar to JpegScanDecoder.)
@ -905,62 +901,54 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
ZigZag unzig = ZigZag.CreateUnzigTable(); ZigZag unzig = ZigZag.CreateUnzigTable();
var pixelConverter = YCbCrForwardConverter<TPixel>.Create();
// ReSharper disable once InconsistentNaming // ReSharper disable once InconsistentNaming
int prevDCY = 0, prevDCCb = 0, prevDCCr = 0; int prevDCY = 0, prevDCCb = 0, prevDCCr = 0;
fixed (RgbToYCbCrTables* tables = &rgbToYCbCrTables)
for (int y = 0; y < pixels.Height; y += 16)
{ {
using (PixelArea<TPixel> rgbBytes = new PixelArea<TPixel>(8, 8, ComponentOrder.Xyz)) for (int x = 0; x < pixels.Width; x += 16)
{ {
for (int y = 0; y < pixels.Height; y += 16) for (int i = 0; i < 4; i++)
{ {
for (int x = 0; x < pixels.Width; x += 16) int xOff = (i & 1) * 8;
{ int yOff = (i & 2) * 4;
for (int i = 0; i < 4; i++)
{ pixelConverter.Convert(pixels.Frames.RootFrame, x + xOff, y + yOff);
int xOff = (i & 1) * 8;
int yOff = (i & 2) * 4; cbPtr[i] = pixelConverter.Cb;
crPtr[i] = pixelConverter.Cr;
ToYCbCr(
pixels, prevDCY = this.WriteBlock(
tables, QuantIndex.Luminance,
x + xOff, prevDCY,
y + yOff, &pixelConverter.Y,
ref b, &temp1,
ref Unsafe.AsRef<Block8x8F>(cbPtr + i), &temp2,
ref Unsafe.AsRef<Block8x8F>(crPtr + i), &onStackLuminanceQuantTable,
rgbBytes); unzig.Data);
prevDCY = this.WriteBlock(
QuantIndex.Luminance,
prevDCY,
&b,
&temp1,
&temp2,
&onStackLuminanceQuantTable,
unzig.Data);
}
Block8x8F.Scale16X16To8X8(&b, cbPtr);
prevDCCb = this.WriteBlock(
QuantIndex.Chrominance,
prevDCCb,
&b,
&temp1,
&temp2,
&onStackChrominanceQuantTable,
unzig.Data);
Block8x8F.Scale16X16To8X8(&b, crPtr);
prevDCCr = this.WriteBlock(
QuantIndex.Chrominance,
prevDCCr,
&b,
&temp1,
&temp2,
&onStackChrominanceQuantTable,
unzig.Data);
}
} }
Block8x8F.Scale16X16To8X8(&b, cbPtr);
prevDCCb = this.WriteBlock(
QuantIndex.Chrominance,
prevDCCb,
&b,
&temp1,
&temp2,
&onStackChrominanceQuantTable,
unzig.Data);
Block8x8F.Scale16X16To8X8(&b, crPtr);
prevDCCr = this.WriteBlock(
QuantIndex.Chrominance,
prevDCCr,
&b,
&temp1,
&temp2,
&onStackChrominanceQuantTable,
unzig.Data);
} }
} }
} }

Loading…
Cancel
Save