diff --git a/src/ImageSharp/Formats/Jpeg/Common/GenericBlock8x8.cs b/src/ImageSharp/Formats/Jpeg/Common/GenericBlock8x8.cs
index bc5838e9b..aefc86eb1 100644
--- a/src/ImageSharp/Formats/Jpeg/Common/GenericBlock8x8.cs
+++ b/src/ImageSharp/Formats/Jpeg/Common/GenericBlock8x8.cs
@@ -7,9 +7,12 @@ using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Formats.Jpeg.Common
{
+ using System.Runtime.InteropServices;
+
///
/// A generic 8x8 block implementation, useful for manipulating custom 8x8 pixel data.
///
+ [StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
internal unsafe partial struct GenericBlock8x8
where T : struct
@@ -87,7 +90,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
}
///
- /// ONLY FOR GenericBlock instances living on the stack!
+ /// Only for on-stack instances!
///
public Span AsSpanUnsafe() => new Span(Unsafe.AsPointer(ref this), Size);
diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/RgbToYCbCrTables.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/RgbToYCbCrTables.cs
index 86b3fc903..923fe244e 100644
--- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/RgbToYCbCrTables.cs
+++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/RgbToYCbCrTables.cs
@@ -2,15 +2,11 @@
// Licensed under the Apache License, Version 2.0.
using System.Runtime.CompilerServices;
-using SixLabors.ImageSharp.Advanced;
-using SixLabors.ImageSharp.Formats.Jpeg.Common;
+
using SixLabors.ImageSharp.Memory;
-using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Encoder
{
- using System;
-
///
/// Provides 8-bit lookup tables for converting from Rgb to YCbCr colorspace.
/// 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)!
/// Optimized method to allocates the correct y, cb, and cr values to the DCT blocks from the given r, g, b values.
///
- /// The reference to the tables instance.
- /// The The luminance block.
- /// The red chroma block.
- /// The blue chroma block.
- /// The current index.
- /// The red value.
- /// The green value.
- /// The blue value.
- [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)]
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;
}
}
-
- // TODO!
- internal struct YCbCrForwardConverter
- where TPixel : struct, IPixel
- {
- public Block8x8F Y;
-
- public Block8x8F Cb;
-
- public Block8x8F Cr;
-
- private RgbToYCbCrTables colorTables;
-
- private GenericBlock8x8 pixelBlock;
-
- private GenericBlock8x8 rgbBlock;
-
- public static YCbCrForwardConverter Create()
- {
- var result = default(YCbCrForwardConverter);
- result.colorTables = RgbToYCbCrTables.Create();
- return result;
- }
-
- public void Convert(IPixelSource pixels, int x, int y)
- {
- this.pixelBlock.LoadAndStretchEdges(pixels, x, y);
-
- Span rgbSpan = this.rgbBlock.AsSpanUnsafe();
- PixelOperations.Instance.ToRgb24(this.pixelBlock.AsSpanUnsafe(), rgbSpan, 64);
-
- ref float yBlockStart = ref Unsafe.As(ref this.Y);
- ref float cbBlockStart = ref Unsafe.As(ref this.Cb);
- ref float crBlockStart = ref Unsafe.As(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)
- );
- }
- }
- }
}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/YCbCrForwardConverter{TPixel}.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/YCbCrForwardConverter{TPixel}.cs
new file mode 100644
index 000000000..e3d7cd3e5
--- /dev/null
+++ b/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
+{
+ ///
+ /// On-stack worker struct to efficiently encapsulate the TPixel -> Rgb24 -> YCbCr conversion chain of 8x8 pixel blocks.
+ ///
+ /// The pixel type to work on
+ internal struct YCbCrForwardConverter
+ where TPixel : struct, IPixel
+ {
+ ///
+ /// The Y component
+ ///
+ public Block8x8F Y;
+
+ ///
+ /// The Cb component
+ ///
+ public Block8x8F Cb;
+
+ ///
+ /// The Cr component
+ ///
+ public Block8x8F Cr;
+
+ ///
+ /// The color conversion tables
+ ///
+ private RgbToYCbCrTables colorTables;
+
+ ///
+ /// Temporal 8x8 block to hold TPixel data
+ ///
+ private GenericBlock8x8 pixelBlock;
+
+ ///
+ /// Temporal RGB block
+ ///
+ private GenericBlock8x8 rgbBlock;
+
+ public static YCbCrForwardConverter Create()
+ {
+ var result = default(YCbCrForwardConverter);
+ result.colorTables = RgbToYCbCrTables.Create();
+ return result;
+ }
+
+ ///
+ /// Converts a 8x8 image area inside 'pixels' at position (x,y) placing the result members of the structure (, , )
+ ///
+ public void Convert(IPixelSource pixels, int x, int y)
+ {
+ this.pixelBlock.LoadAndStretchEdges(pixels, x, y);
+
+ Span rgbSpan = this.rgbBlock.AsSpanUnsafe();
+ PixelOperations.Instance.ToRgb24(this.pixelBlock.AsSpanUnsafe(), rgbSpan, 64);
+
+ ref float yBlockStart = ref Unsafe.As(ref this.Y);
+ ref float cbBlockStart = ref Unsafe.As(ref this.Cb);
+ ref float crBlockStart = ref Unsafe.As(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)
+ );
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/JpegEncoderCore.cs
index c8be8b02f..d9ebd265c 100644
--- a/src/ImageSharp/Formats/Jpeg/GolangPort/JpegEncoderCore.cs
+++ b/src/ImageSharp/Formats/Jpeg/GolangPort/JpegEncoderCore.cs
@@ -868,11 +868,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
this.Encode444(image);
break;
case JpegSubsample.Ratio420:
- using (var pixels = image.Lock())
- {
- this.Encode420(pixels);
- }
-
+ this.Encode420(image);
break;
}
@@ -886,7 +882,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
///
/// The pixel format.
/// The pixel accessor providing access to the image pixels.
- private void Encode420(PixelAccessor pixels)
+ private void Encode420(Image pixels)
where TPixel : struct, IPixel
{
// TODO: Need a JpegScanEncoder 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();
+ var pixelConverter = YCbCrForwardConverter.Create();
+
// ReSharper disable once InconsistentNaming
int prevDCY = 0, prevDCCb = 0, prevDCCr = 0;
- fixed (RgbToYCbCrTables* tables = &rgbToYCbCrTables)
+
+ for (int y = 0; y < pixels.Height; y += 16)
{
- using (PixelArea rgbBytes = new PixelArea(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)
- {
- for (int i = 0; i < 4; i++)
- {
- int xOff = (i & 1) * 8;
- int yOff = (i & 2) * 4;
-
- ToYCbCr(
- pixels,
- tables,
- x + xOff,
- y + yOff,
- ref b,
- ref Unsafe.AsRef(cbPtr + i),
- ref Unsafe.AsRef(crPtr + i),
- rgbBytes);
-
- 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);
- }
+ int xOff = (i & 1) * 8;
+ int yOff = (i & 2) * 4;
+
+ pixelConverter.Convert(pixels.Frames.RootFrame, x + xOff, y + yOff);
+
+ cbPtr[i] = pixelConverter.Cb;
+ crPtr[i] = pixelConverter.Cr;
+
+ prevDCY = this.WriteBlock(
+ QuantIndex.Luminance,
+ prevDCY,
+ &pixelConverter.Y,
+ &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);
}
}
}