From 58727558a0bfdee36366e37934853b4862e1e426 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sun, 26 Jul 2020 22:10:30 +0100 Subject: [PATCH 01/10] Remove most pointer usage --- .../Formats/Jpeg/Components/Block8x8F.cs | 21 +++--- .../Jpeg/Components/Encoder/BlockQuad.cs | 4 +- .../Formats/Jpeg/JpegEncoderCore.cs | 69 +++++++++---------- .../Formats/Jpg/Block8x8FTests.cs | 2 +- 4 files changed, 45 insertions(+), 51 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs index 8809f890fd..26d3e0dbb3 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs @@ -378,20 +378,17 @@ public static unsafe void DequantizeBlock(Block8x8F* blockPtr, Block8x8F* qtPtr, /// The quantization table /// Pointer to elements of public static unsafe void Quantize( - Block8x8F* block, - Block8x8F* dest, - Block8x8F* qt, + ref Block8x8F block, + ref Block8x8F dest, + ref Block8x8F qt, byte* unzigPtr) { - float* s = (float*)block; - float* d = (float*)dest; - for (int zig = 0; zig < Size; zig++) { - d[zig] = s[unzigPtr[zig]]; + dest[zig] = block[unzigPtr[zig]]; } - DivideRoundAll(ref *dest, ref *qt); + DivideRoundAll(ref dest, ref qt); } /// @@ -399,13 +396,11 @@ public static unsafe void Quantize( /// /// The destination block. /// The source block. - public static unsafe void Scale16X16To8X8(Block8x8F* destination, Block8x8F* source) + public static unsafe void Scale16X16To8X8(ref Block8x8F destination, Block8x8F* source) { - float* d = (float*)destination; for (int i = 0; i < 4; i++) { int dstOff = ((i & 2) << 4) | ((i & 1) << 2); - float* iSource = (float*)(source + i); for (int y = 0; y < 4; y++) @@ -414,7 +409,7 @@ public static unsafe void Scale16X16To8X8(Block8x8F* destination, Block8x8F* sou { int j = (16 * y) + (2 * x); float sum = iSource[j] + iSource[j + 1] + iSource[j + 8] + iSource[j + 9]; - d[(8 * y) + x + dstOff] = (sum + 2) / 4; + destination[(8 * y) + x + dstOff] = (sum + 2) * .25F; } } } @@ -589,7 +584,7 @@ private static Vector NormalizeAndRound(Vector row, Vector private static Vector4 DivideRound(Vector4 dividend, Vector4 divisor) { // sign(dividend) = max(min(dividend, 1), -1) - var sign = Vector4Utilities.FastClamp(dividend, NegativeOne, Vector4.One); + Vector4 sign = Vector4Utilities.FastClamp(dividend, NegativeOne, Vector4.One); // AlmostRound(dividend/divisor) = dividend/divisor + 0.5*sign(dividend) return (dividend / divisor) + (sign * Offset); diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/BlockQuad.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/BlockQuad.cs index cda149d29e..57f9a60cb1 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/BlockQuad.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/BlockQuad.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder @@ -14,4 +14,4 @@ internal unsafe struct BlockQuad /// public fixed float Data[4 * Block8x8F.Size]; } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs index f755028dbd..08ffd53343 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs @@ -427,26 +427,26 @@ private void Encode444(Image pixels) prevDCY = this.WriteBlock( QuantIndex.Luminance, prevDCY, - &pixelConverter.Y, - &temp1, - &temp2, - &onStackLuminanceQuantTable, + ref pixelConverter.Y, + ref temp1, + ref temp2, + ref onStackLuminanceQuantTable, unzig.Data); prevDCCb = this.WriteBlock( QuantIndex.Chrominance, prevDCCb, - &pixelConverter.Cb, - &temp1, - &temp2, - &onStackChrominanceQuantTable, + ref pixelConverter.Cb, + ref temp1, + ref temp2, + ref onStackChrominanceQuantTable, unzig.Data); prevDCCr = this.WriteBlock( QuantIndex.Chrominance, prevDCCr, - &pixelConverter.Cr, - &temp1, - &temp2, - &onStackChrominanceQuantTable, + ref pixelConverter.Cr, + ref temp1, + ref temp2, + ref onStackChrominanceQuantTable, unzig.Data); } } @@ -519,18 +519,17 @@ private void WriteApplicationHeader(ImageMetadata meta) private int WriteBlock( QuantIndex index, int prevDC, - Block8x8F* src, - Block8x8F* tempDest1, - Block8x8F* tempDest2, - Block8x8F* quant, + ref Block8x8F src, + ref Block8x8F tempDest1, + ref Block8x8F tempDest2, + ref Block8x8F quant, byte* unzigPtr) { - FastFloatingPointDCT.TransformFDCT(ref *src, ref *tempDest1, ref *tempDest2); + FastFloatingPointDCT.TransformFDCT(ref src, ref tempDest1, ref tempDest2); - Block8x8F.Quantize(tempDest1, tempDest2, quant, unzigPtr); - float* unziggedDestPtr = (float*)tempDest2; + Block8x8F.Quantize(ref tempDest1, ref tempDest2, ref quant, unzigPtr); - int dc = (int)unziggedDestPtr[0]; + int dc = (int)tempDest2[0]; // Emit the DC delta. this.EmitHuffRLE((HuffIndex)((2 * (int)index) + 0), 0, dc - prevDC); @@ -541,7 +540,7 @@ private int WriteBlock( for (int zig = 1; zig < Block8x8F.Size; zig++) { - int ac = (int)unziggedDestPtr[zig]; + int ac = (int)tempDest2[zig]; if (ac == 0) { @@ -1016,31 +1015,31 @@ private void Encode420(Image pixels) prevDCY = this.WriteBlock( QuantIndex.Luminance, prevDCY, - &pixelConverter.Y, - &temp1, - &temp2, - &onStackLuminanceQuantTable, + ref pixelConverter.Y, + ref temp1, + ref temp2, + ref onStackLuminanceQuantTable, unzig.Data); } - Block8x8F.Scale16X16To8X8(&b, cbPtr); + Block8x8F.Scale16X16To8X8(ref b, cbPtr); prevDCCb = this.WriteBlock( QuantIndex.Chrominance, prevDCCb, - &b, - &temp1, - &temp2, - &onStackChrominanceQuantTable, + ref b, + ref temp1, + ref temp2, + ref onStackChrominanceQuantTable, unzig.Data); - Block8x8F.Scale16X16To8X8(&b, crPtr); + Block8x8F.Scale16X16To8X8(ref b, crPtr); prevDCCr = this.WriteBlock( QuantIndex.Chrominance, prevDCCr, - &b, - &temp1, - &temp2, - &onStackChrominanceQuantTable, + ref b, + ref temp1, + ref temp2, + ref onStackChrominanceQuantTable, unzig.Data); } } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs index 3482662698..4c7277e5bc 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs @@ -282,7 +282,7 @@ public unsafe void Quantize(int seed) var actualResults = default(Block8x8F); - Block8x8F.Quantize(&block, &actualResults, &qt, unzig.Data); + Block8x8F.Quantize(ref block, ref actualResults, ref qt, unzig.Data); for (int i = 0; i < Block8x8F.Size; i++) { From be1c1f9793768c30c96627fcfe158058f985fa1b Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 27 Jul 2020 11:08:54 +0100 Subject: [PATCH 02/10] Remove last of pointer code and add benchmarks --- .../Formats/Jpeg/Components/Block8x8F.cs | 10 +++--- .../Formats/Jpeg/JpegEncoderCore.cs | 33 +++++++++---------- .../Codecs/Jpeg/EncodeJpeg.cs | 19 ++++++++++- .../Formats/Jpg/Block8x8FTests.cs | 2 +- 4 files changed, 39 insertions(+), 25 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs index 26d3e0dbb3..b7835d6706 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs @@ -376,16 +376,16 @@ public static unsafe void DequantizeBlock(Block8x8F* blockPtr, Block8x8F* qtPtr, /// Source block /// Destination block /// The quantization table - /// Pointer to elements of + /// The 8x8 Unzig block. public static unsafe void Quantize( ref Block8x8F block, ref Block8x8F dest, ref Block8x8F qt, - byte* unzigPtr) + ref ZigZag unZig) { for (int zig = 0; zig < Size; zig++) { - dest[zig] = block[unzigPtr[zig]]; + dest[zig] = block[unZig[zig]]; } DivideRoundAll(ref dest, ref qt); @@ -396,12 +396,12 @@ public static unsafe void Quantize( /// /// The destination block. /// The source block. - public static unsafe void Scale16X16To8X8(ref Block8x8F destination, Block8x8F* source) + public static unsafe void Scale16X16To8X8(ref Block8x8F destination, ReadOnlySpan source) { for (int i = 0; i < 4; i++) { int dstOff = ((i & 2) << 4) | ((i & 1) << 2); - float* iSource = (float*)(source + i); + Block8x8F iSource = source[i]; for (int y = 0; y < 4; y++) { diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs index 08ffd53343..26e804b923 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs @@ -431,7 +431,7 @@ private void Encode444(Image pixels) ref temp1, ref temp2, ref onStackLuminanceQuantTable, - unzig.Data); + ref unzig); prevDCCb = this.WriteBlock( QuantIndex.Chrominance, prevDCCb, @@ -439,7 +439,7 @@ private void Encode444(Image pixels) ref temp1, ref temp2, ref onStackChrominanceQuantTable, - unzig.Data); + ref unzig); prevDCCr = this.WriteBlock( QuantIndex.Chrominance, prevDCCr, @@ -447,7 +447,7 @@ private void Encode444(Image pixels) ref temp1, ref temp2, ref onStackChrominanceQuantTable, - unzig.Data); + ref unzig); } } } @@ -512,7 +512,7 @@ private void WriteApplicationHeader(ImageMetadata meta) /// Temporal block to be used as FDCT Destination /// Temporal block 2 /// Quantization table - /// The 8x8 Unzig block pointer + /// The 8x8 Unzig block. /// /// The /// @@ -523,11 +523,11 @@ private int WriteBlock( ref Block8x8F tempDest1, ref Block8x8F tempDest2, ref Block8x8F quant, - byte* unzigPtr) + ref ZigZag unZig) { FastFloatingPointDCT.TransformFDCT(ref src, ref tempDest1, ref tempDest2); - Block8x8F.Quantize(ref tempDest1, ref tempDest2, ref quant, unzigPtr); + Block8x8F.Quantize(ref tempDest1, ref tempDest2, ref quant, ref unZig); int dc = (int)tempDest2[0]; @@ -974,11 +974,8 @@ private void Encode420(Image pixels) { // TODO: Need a JpegScanEncoder class or struct that encapsulates the scan-encoding implementation. (Similar to JpegScanDecoder.) Block8x8F b = default; - - BlockQuad cb = default; - BlockQuad cr = default; - var cbPtr = (Block8x8F*)cb.Data; - var crPtr = (Block8x8F*)cr.Data; + Span cb = stackalloc Block8x8F[4]; + Span cr = stackalloc Block8x8F[4]; Block8x8F temp1 = default; Block8x8F temp2 = default; @@ -1009,8 +1006,8 @@ private void Encode420(Image pixels) pixelConverter.Convert(frame, x + xOff, y + yOff, currentRows); - cbPtr[i] = pixelConverter.Cb; - crPtr[i] = pixelConverter.Cr; + cb[i] = pixelConverter.Cb; + cr[i] = pixelConverter.Cr; prevDCY = this.WriteBlock( QuantIndex.Luminance, @@ -1019,10 +1016,10 @@ private void Encode420(Image pixels) ref temp1, ref temp2, ref onStackLuminanceQuantTable, - unzig.Data); + ref unzig); } - Block8x8F.Scale16X16To8X8(ref b, cbPtr); + Block8x8F.Scale16X16To8X8(ref b, cb); prevDCCb = this.WriteBlock( QuantIndex.Chrominance, prevDCCb, @@ -1030,9 +1027,9 @@ private void Encode420(Image pixels) ref temp1, ref temp2, ref onStackChrominanceQuantTable, - unzig.Data); + ref unzig); - Block8x8F.Scale16X16To8X8(ref b, crPtr); + Block8x8F.Scale16X16To8X8(ref b, cr); prevDCCr = this.WriteBlock( QuantIndex.Chrominance, prevDCCr, @@ -1040,7 +1037,7 @@ private void Encode420(Image pixels) ref temp1, ref temp2, ref onStackChrominanceQuantTable, - unzig.Data); + ref unzig); } } } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpeg.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpeg.cs index 0a1fe977f8..b5be18189e 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpeg.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpeg.cs @@ -10,6 +10,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg using System.Drawing; using System.Drawing.Imaging; using System.IO; + using SixLabors.ImageSharp.Tests; using CoreImage = SixLabors.ImageSharp.Image; public class EncodeJpeg : BenchmarkBase @@ -24,7 +25,8 @@ public void ReadImages() { if (this.bmpStream == null) { - this.bmpStream = File.OpenRead("../ImageSharp.Tests/TestImages/Formats/Bmp/Car.bmp"); + const string TestImage = TestImages.Bmp.Car; + this.bmpStream = File.OpenRead(Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, TestImage)); this.bmpCore = CoreImage.Load(this.bmpStream); this.bmpStream.Position = 0; this.bmpDrawing = Image.FromStream(this.bmpStream); @@ -58,3 +60,18 @@ public void JpegCore() } } } + +/* +BenchmarkDotNet=v0.12.1, OS=Windows 10.0.18363.959 (1909/November2018Update/19H2) +Intel Core i7-8650U CPU 1.90GHz(Kaby Lake R), 1 CPU, 8 logical and 4 physical cores +.NET Core SDK = 3.1.302 + + [Host] : .NET Core 3.1.6 (CoreCLR 4.700.20.26901, CoreFX 4.700.20.31603), X64 RyuJIT + DefaultJob : .NET Core 3.1.6 (CoreCLR 4.700.20.26901, CoreFX 4.700.20.31603), X64 RyuJIT + + +| Method | Mean | Error | StdDev | Median | Ratio | RatioSD | +|---------------------- |---------:|----------:|----------:|---------:|------:|--------:| +| 'System.Drawing Jpeg' | 4.473 ms | 0.0847 ms | 0.2012 ms | 4.408 ms | 1.00 | 0.00 | +| 'ImageSharp Jpeg' | 5.409 ms | 0.0379 ms | 0.0316 ms | 5.412 ms | 1.19 | 0.05 | +*/ diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs index 4c7277e5bc..722521f98d 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs @@ -282,7 +282,7 @@ public unsafe void Quantize(int seed) var actualResults = default(Block8x8F); - Block8x8F.Quantize(ref block, ref actualResults, ref qt, unzig.Data); + Block8x8F.Quantize(ref block, ref actualResults, ref qt, ref unzig); for (int i = 0; i < Block8x8F.Size; i++) { From 8e0a6f4598f3a09e2c103169fc6381a2f0a62d59 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 28 Jul 2020 22:31:40 +0100 Subject: [PATCH 03/10] Begin simplifying forward color conversion --- .../Components/Encoder/RgbToYCbCrTables.cs | 19 +++++++++++++------ .../Encoder/YCbCrForwardConverter{TPixel}.cs | 13 +++++++------ 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbToYCbCrTables.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbToYCbCrTables.cs index 3c234ccb1f..e80ac3c98b 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbToYCbCrTables.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbToYCbCrTables.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using System.Runtime.CompilerServices; @@ -96,7 +96,14 @@ public static RgbToYCbCrTables Create() /// Optimized method to allocates the correct y, cb, and cr values to the DCT blocks from the given r, g, b values. /// [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 Block8x8F yResult, + ref Block8x8F cbResult, + ref Block8x8F crResult, + int i) { ref int start = ref Unsafe.As(ref this); @@ -111,9 +118,9 @@ public void ConvertPixelInto(int r, int g, int b, ref float yResult, ref float c ref int crG = ref Unsafe.Add(ref start, 256 * 6); ref int crB = ref Unsafe.Add(ref start, 256 * 7); - yResult = (Unsafe.Add(ref yR, r) + Unsafe.Add(ref yG, g) + Unsafe.Add(ref yB, b)) >> ScaleBits; - cbResult = (Unsafe.Add(ref cbR, r) + Unsafe.Add(ref cbG, g) + Unsafe.Add(ref cbB, b)) >> ScaleBits; - crResult = (Unsafe.Add(ref cbB, r) + Unsafe.Add(ref crG, g) + Unsafe.Add(ref crB, b)) >> ScaleBits; + yResult[i] = (Unsafe.Add(ref yR, r) + Unsafe.Add(ref yG, g) + Unsafe.Add(ref yB, b)) >> ScaleBits; + cbResult[i] = (Unsafe.Add(ref cbR, r) + Unsafe.Add(ref cbG, g) + Unsafe.Add(ref cbB, b)) >> ScaleBits; + crResult[i] = (Unsafe.Add(ref cbB, r) + Unsafe.Add(ref crG, g) + Unsafe.Add(ref crB, b)) >> ScaleBits; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -122,4 +129,4 @@ private static int Fix(float x) return (int)((x * (1L << ScaleBits)) + 0.5F); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter{TPixel}.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter{TPixel}.cs index 52d8a5107c..4d6186e22f 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter{TPixel}.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter{TPixel}.cs @@ -62,9 +62,9 @@ public void Convert(ImageFrame frame, int x, int y, in RowOctet Span rgbSpan = this.rgbBlock.AsSpanUnsafe(); PixelOperations.Instance.ToRgb24(frame.GetConfiguration(), this.pixelBlock.AsSpanUnsafe(), rgbSpan); - 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 Block8x8F yBlock = ref this.Y; + ref Block8x8F cbBlock = ref this.Cb; + ref Block8x8F crBlock = ref this.Cr; ref Rgb24 rgbStart = ref rgbSpan[0]; for (int i = 0; i < 64; i++) @@ -75,9 +75,10 @@ public void Convert(ImageFrame frame, int x, int y, in RowOctet c.R, c.G, c.B, - ref Unsafe.Add(ref yBlockStart, i), - ref Unsafe.Add(ref cbBlockStart, i), - ref Unsafe.Add(ref crBlockStart, i)); + ref yBlock, + ref cbBlock, + ref crBlock, + i); } } } From 70cebed5852970843f31dfcd4a487d336e6d4135 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 28 Jul 2020 22:36:28 +0100 Subject: [PATCH 04/10] Finish simplifying conversion code. --- .../Components/Encoder/RgbToYCbCrTables.cs | 24 ++++++------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbToYCbCrTables.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbToYCbCrTables.cs index e80ac3c98b..236eff27cc 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbToYCbCrTables.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbToYCbCrTables.cs @@ -105,28 +105,18 @@ public void ConvertPixelInto( ref Block8x8F crResult, int i) { - ref int start = ref Unsafe.As(ref this); + // float y = (0.299F * r) + (0.587F * g) + (0.114F * b); + yResult[i] = (this.YRTable[r] + this.YGTable[g] + this.YBTable[b]) >> ScaleBits; - ref int yR = ref start; - ref int yG = ref Unsafe.Add(ref start, 256 * 1); - ref int yB = ref Unsafe.Add(ref start, 256 * 2); + // float cb = 128F + ((-0.168736F * r) - (0.331264F * g) + (0.5F * b)); + cbResult[i] = (this.CbRTable[r] + this.CbGTable[g] + this.CbBTable[b]) >> ScaleBits; - ref int cbR = ref Unsafe.Add(ref start, 256 * 3); - ref int cbG = ref Unsafe.Add(ref start, 256 * 4); - ref int cbB = ref Unsafe.Add(ref start, 256 * 5); - - ref int crG = ref Unsafe.Add(ref start, 256 * 6); - ref int crB = ref Unsafe.Add(ref start, 256 * 7); - - yResult[i] = (Unsafe.Add(ref yR, r) + Unsafe.Add(ref yG, g) + Unsafe.Add(ref yB, b)) >> ScaleBits; - cbResult[i] = (Unsafe.Add(ref cbR, r) + Unsafe.Add(ref cbG, g) + Unsafe.Add(ref cbB, b)) >> ScaleBits; - crResult[i] = (Unsafe.Add(ref cbB, r) + Unsafe.Add(ref crG, g) + Unsafe.Add(ref crB, b)) >> ScaleBits; + // float cr = MathF.Round(y + (1.772F * cb), MidpointRounding.AwayFromZero); + crResult[i] = (this.CbBTable[r] + this.CrGTable[g] + this.CrBTable[b]) >> ScaleBits; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int Fix(float x) - { - return (int)((x * (1L << ScaleBits)) + 0.5F); - } + => (int)((x * (1L << ScaleBits)) + 0.5F); } } From 400b00d02649ea9cc8af56092811989c1947314e Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 28 Jul 2020 22:46:27 +0100 Subject: [PATCH 05/10] Update benchmarks --- .../Codecs/Jpeg/EncodeJpeg.cs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpeg.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpeg.cs index b5be18189e..4c326fb3ac 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpeg.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpeg.cs @@ -63,15 +63,14 @@ public void JpegCore() /* BenchmarkDotNet=v0.12.1, OS=Windows 10.0.18363.959 (1909/November2018Update/19H2) -Intel Core i7-8650U CPU 1.90GHz(Kaby Lake R), 1 CPU, 8 logical and 4 physical cores -.NET Core SDK = 3.1.302 - - [Host] : .NET Core 3.1.6 (CoreCLR 4.700.20.26901, CoreFX 4.700.20.31603), X64 RyuJIT +Intel Core i7-8650U CPU 1.90GHz (Kaby Lake R), 1 CPU, 8 logical and 4 physical cores +.NET Core SDK=3.1.302 + [Host] : .NET Core 3.1.6 (CoreCLR 4.700.20.26901, CoreFX 4.700.20.31603), X64 RyuJIT DefaultJob : .NET Core 3.1.6 (CoreCLR 4.700.20.26901, CoreFX 4.700.20.31603), X64 RyuJIT -| Method | Mean | Error | StdDev | Median | Ratio | RatioSD | -|---------------------- |---------:|----------:|----------:|---------:|------:|--------:| -| 'System.Drawing Jpeg' | 4.473 ms | 0.0847 ms | 0.2012 ms | 4.408 ms | 1.00 | 0.00 | -| 'ImageSharp Jpeg' | 5.409 ms | 0.0379 ms | 0.0316 ms | 5.412 ms | 1.19 | 0.05 | +| Method | Mean | Error | StdDev | Ratio | RatioSD | +|---------------------- |---------:|----------:|----------:|------:|--------:| +| 'System.Drawing Jpeg' | 4.297 ms | 0.0244 ms | 0.0228 ms | 1.00 | 0.00 | +| 'ImageSharp Jpeg' | 5.286 ms | 0.1034 ms | 0.0967 ms | 1.23 | 0.02 | */ From a3a649117abb3751e49ac59eeab438e2933b6247 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 29 Jul 2020 13:46:23 +0100 Subject: [PATCH 06/10] Delete unused BlockQuad struct --- .../Jpeg/Components/Encoder/BlockQuad.cs | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 src/ImageSharp/Formats/Jpeg/Components/Encoder/BlockQuad.cs diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/BlockQuad.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/BlockQuad.cs deleted file mode 100644 index 57f9a60cb1..0000000000 --- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/BlockQuad.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Apache License, Version 2.0. - -namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder -{ - /// - /// Poor man's stackalloc: Contains a value-type buffer sized for 4 instances. - /// Useful for decoder/encoder operations allocating a block for each Jpeg component. - /// - internal unsafe struct BlockQuad - { - /// - /// The value-type buffer sized for 4 instances. - /// - public fixed float Data[4 * Block8x8F.Size]; - } -} From 307668e94b260d422b438378611ed1d46366aa26 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 29 Jul 2020 15:58:04 +0100 Subject: [PATCH 07/10] Use unmanaged constraint. --- src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.cs | 3 +-- src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.cs b/src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.cs index 7fe7d869cd..38b902b88c 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.cs @@ -4,7 +4,6 @@ using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -16,7 +15,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components /// [StructLayout(LayoutKind.Sequential)] internal unsafe partial struct GenericBlock8x8 - where T : struct + where T : unmanaged { public const int Size = 64; diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs index 26e804b923..37bfe6bcf5 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs @@ -6,7 +6,6 @@ using System.IO; using System.Linq; using System.Runtime.CompilerServices; -using System.Threading.Tasks; using SixLabors.ImageSharp.Common.Helpers; using SixLabors.ImageSharp.Formats.Jpeg.Components; using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; From 0d13ad58f3b98c64d5bddb8633c988fb367ceefb Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 29 Jul 2020 16:28:50 +0100 Subject: [PATCH 08/10] Use MemoryMarshal.CreateSpan --- Directory.Build.props | 26 +++++++++++-------- .../Jpeg/Components/GenericBlock8x8.cs | 10 ++++++- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 14c9da79d6..0f9c5bdde2 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -30,17 +30,17 @@ @@ -52,6 +52,7 @@ $(DefineConstants);SUPPORTS_RUNTIME_INTRINSICS $(DefineConstants);SUPPORTS_CODECOVERAGE $(DefineConstants);SUPPORTS_HOTPATH + $(DefineConstants);SUPPORTS_CREATESPAN $(DefineConstants);SUPPORTS_MATHF @@ -60,10 +61,12 @@ $(DefineConstants);SUPPORTS_SPAN_STREAM $(DefineConstants);SUPPORTS_ENCODING_STRING $(DefineConstants);SUPPORTS_CODECOVERAGE + $(DefineConstants);SUPPORTS_CREATESPAN $(DefineConstants);SUPPORTS_MATHF $(DefineConstants);SUPPORTS_CODECOVERAGE + $(DefineConstants);SUPPORTS_CREATESPAN $(DefineConstants);SUPPORTS_MATHF @@ -71,6 +74,7 @@ $(DefineConstants);SUPPORTS_SPAN_STREAM $(DefineConstants);SUPPORTS_ENCODING_STRING $(DefineConstants);SUPPORTS_CODECOVERAGE + $(DefineConstants);SUPPORTS_CREATESPAN $(DefineConstants);SUPPORTS_CODECOVERAGE diff --git a/src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.cs b/src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.cs index 38b902b88c..92ba1afd35 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.cs @@ -109,6 +109,14 @@ public void LoadAndStretchEdges(Buffer2D source, int sourceX, int sourceY, in /// /// Only for on-stack instances! /// - public Span AsSpanUnsafe() => new Span(Unsafe.AsPointer(ref this), Size); + public Span AsSpanUnsafe() + { +#if SUPPORTS_CREATESPAN + Span> s = MemoryMarshal.CreateSpan(ref this, 1); + return MemoryMarshal.Cast, T>(s); +#else + return new Span(Unsafe.AsPointer(ref this), Size); +#endif + } } } From 33aa6441182645c57276f4351f6381de73ef484b Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 6 Aug 2020 21:39:00 +0100 Subject: [PATCH 09/10] Don't use RGB. Fixes #1275 --- .../PixelFormats/PixelImplementations/Rgb24.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs index 60cbccded1..a3924b97dc 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs @@ -201,7 +201,18 @@ public void FromLa32(La32 source) /// [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba32(Rgba32 source) => this = source.Rgb; + public void FromRgba32(Rgba32 source) + { +#if NETSTANDARD2_0 + + // See https://github.com/SixLabors/ImageSharp/issues/1275 + this.R = source.R; + this.G = source.G; + this.B = source.B; +#else + this = source.Rgb; +#endif + } /// [MethodImpl(InliningOptions.ShortMethod)] From 0c524d6a084b6be6e884c0c11a9268c574f7ea86 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 6 Aug 2020 21:45:07 +0100 Subject: [PATCH 10/10] Remove empty line. --- src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs index a3924b97dc..a50c18d42c 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs @@ -204,7 +204,6 @@ public void FromLa32(La32 source) public void FromRgba32(Rgba32 source) { #if NETSTANDARD2_0 - // See https://github.com/SixLabors/ImageSharp/issues/1275 this.R = source.R; this.G = source.G;