From 62f8ba211b9fa7976a0a18702a84378647fbfbc9 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Thu, 22 Feb 2018 20:49:06 +0100 Subject: [PATCH] All PixelArea usages in JpegEncoder replaced by GenericBlock8x8 + YCbCrForwardConverter --- .../Formats/Jpeg/Common/GenericBlock8x8.cs | 5 +- .../Components/Encoder/RgbToYCbCrTables.cs | 78 +------------- .../Encoder/YCbCrForwardConverter{TPixel}.cs | 83 +++++++++++++++ .../Jpeg/GolangPort/JpegEncoderCore.cs | 100 ++++++++---------- 4 files changed, 132 insertions(+), 134 deletions(-) create mode 100644 src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/YCbCrForwardConverter{TPixel}.cs 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); } } }