From e47c8fcb905ef9eeb338a91e83b784795ce47627 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 3 Feb 2021 04:11:00 +0000 Subject: [PATCH] Remove bounds checks during Emit --- .../Formats/Jpeg/JpegEncoderCore.cs | 77 +++++++++++-------- 1 file changed, 45 insertions(+), 32 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs index 31f2cfc3f..422c7bd7e 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs @@ -6,6 +6,7 @@ using System.Buffers.Binary; using System.IO; using System.Linq; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Threading; using SixLabors.ImageSharp.Common.Helpers; using SixLabors.ImageSharp.Formats.Jpeg.Components; @@ -313,7 +314,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// /// The packed bits. /// The number of bits - private void Emit(uint bits, uint count) + /// The reference to the emitBuffer. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void Emit(uint bits, uint count, ref byte emitBufferBase) { count += this.bitCount; bits <<= (int)(32 - count); @@ -327,10 +330,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg while (count >= 8) { byte b = (byte)(bits >> 24); - this.emitBuffer[len++] = b; - if (b == 0xff) + Unsafe.Add(ref emitBufferBase, len++) = b; + if (b == byte.MaxValue) { - this.emitBuffer[len++] = 0x00; + Unsafe.Add(ref emitBufferBase, len++) = byte.MinValue; } bits <<= 8; @@ -352,11 +355,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// /// The index of the Huffman encoder /// The value to encode. + /// The reference to the emit buffer. [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void EmitHuff(HuffIndex index, int value) + private void EmitHuff(HuffIndex index, int value, ref byte emitBufferBase) { uint x = HuffmanLut.TheHuffmanLut[(int)index].Values[value]; - this.Emit(x & ((1 << 24) - 1), x >> 24); + this.Emit(x & ((1 << 24) - 1), x >> 24, ref emitBufferBase); } /// @@ -365,8 +369,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// The index of the Huffman encoder /// The number of copies to encode. /// The value to encode. + /// The reference to the emit buffer. [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void EmitHuffRLE(HuffIndex index, int runLength, int value) + private void EmitHuffRLE(HuffIndex index, int runLength, int value, ref byte emitBufferBase) { int a = value; int b = value; @@ -386,10 +391,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg bt = 8 + (uint)BitCountLut[a >> 8]; } - this.EmitHuff(index, (int)((uint)(runLength << 4) | bt)); + this.EmitHuff(index, (int)((uint)(runLength << 4) | bt), ref emitBufferBase); if (bt > 0) { - this.Emit((uint)b & (uint)((1 << ((int)bt)) - 1), bt); + this.Emit((uint)b & (uint)((1 << ((int)bt)) - 1), bt, ref emitBufferBase); } } @@ -399,7 +404,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// The pixel format. /// The pixel accessor providing access to the image pixels. /// The token to monitor for cancellation. - private void Encode444(Image pixels, CancellationToken cancellationToken) + /// The reference to the emit buffer. + private void Encode444(Image pixels, CancellationToken cancellationToken, ref byte emitBufferBase) where TPixel : unmanaged, IPixel { // TODO: Need a JpegScanEncoder class or struct that encapsulates the scan-encoding implementation. (Similar to JpegScanDecoder.) @@ -436,7 +442,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg ref temp1, ref temp2, ref onStackLuminanceQuantTable, - ref unzig); + ref unzig, + ref emitBufferBase); + prevDCCb = this.WriteBlock( QuantIndex.Chrominance, prevDCCb, @@ -444,7 +452,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg ref temp1, ref temp2, ref onStackChrominanceQuantTable, - ref unzig); + ref unzig, + ref emitBufferBase); + prevDCCr = this.WriteBlock( QuantIndex.Chrominance, prevDCCr, @@ -452,7 +462,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg ref temp1, ref temp2, ref onStackChrominanceQuantTable, - ref unzig); + ref unzig, + ref emitBufferBase); } } } @@ -518,9 +529,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// Temporal block 2 /// Quantization table /// The 8x8 Unzig block. - /// - /// The - /// + /// The reference to the emit buffer. + /// The . private int WriteBlock( QuantIndex index, int prevDC, @@ -528,7 +538,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg ref Block8x8F tempDest1, ref Block8x8F tempDest2, ref Block8x8F quant, - ref ZigZag unZig) + ref ZigZag unZig, + ref byte emitBufferBase) { FastFloatingPointDCT.TransformFDCT(ref src, ref tempDest1, ref tempDest2); @@ -537,7 +548,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg int dc = (int)tempDest2[0]; // Emit the DC delta. - this.EmitHuffRLE((HuffIndex)((2 * (int)index) + 0), 0, dc - prevDC); + this.EmitHuffRLE((HuffIndex)((2 * (int)index) + 0), 0, dc - prevDC, ref emitBufferBase); // Emit the AC components. var h = (HuffIndex)((2 * (int)index) + 1); @@ -555,18 +566,18 @@ namespace SixLabors.ImageSharp.Formats.Jpeg { while (runLength > 15) { - this.EmitHuff(h, 0xf0); + this.EmitHuff(h, 0xf0, ref emitBufferBase); runLength -= 16; } - this.EmitHuffRLE(h, runLength, ac); + this.EmitHuffRLE(h, runLength, ac, ref emitBufferBase); runLength = 0; } } if (runLength > 0) { - this.EmitHuff(h, 0x00); + this.EmitHuff(h, 0x00, ref emitBufferBase); } return dc; @@ -748,9 +759,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// /// The length of the data the app1 marker contains. private void WriteApp1Header(int app1Length) - { - this.WriteAppHeader(app1Length, JpegConstants.Markers.APP1); - } + => this.WriteAppHeader(app1Length, JpegConstants.Markers.APP1); /// /// Writes a AppX header. @@ -954,19 +963,19 @@ namespace SixLabors.ImageSharp.Formats.Jpeg // TODO: Need a JpegScanEncoder class or struct that encapsulates the scan-encoding implementation. (Similar to JpegScanDecoder.) // TODO: We should allow grayscale writing. this.outputStream.Write(SosHeaderYCbCr); - + ref byte emitBufferBase = ref MemoryMarshal.GetReference(this.emitBuffer); switch (this.subsample) { case JpegSubsample.Ratio444: - this.Encode444(image, cancellationToken); + this.Encode444(image, cancellationToken, ref emitBufferBase); break; case JpegSubsample.Ratio420: - this.Encode420(image, cancellationToken); + this.Encode420(image, cancellationToken, ref emitBufferBase); break; } // Pad the last byte with 1's. - this.Emit(0x7f, 7); + this.Emit(0x7f, 7, ref emitBufferBase); } /// @@ -976,7 +985,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// The pixel format. /// The pixel accessor providing access to the image pixels. /// The token to monitor for cancellation. - private void Encode420(Image pixels, CancellationToken cancellationToken) + /// The reference to the emit buffer. + private void Encode420(Image pixels, CancellationToken cancellationToken, ref byte emitBufferBase) where TPixel : unmanaged, IPixel { // TODO: Need a JpegScanEncoder class or struct that encapsulates the scan-encoding implementation. (Similar to JpegScanDecoder.) @@ -1023,7 +1033,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg ref temp1, ref temp2, ref onStackLuminanceQuantTable, - ref unzig); + ref unzig, + ref emitBufferBase); } Block8x8F.Scale16X16To8X8(ref b, cb); @@ -1034,7 +1045,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg ref temp1, ref temp2, ref onStackChrominanceQuantTable, - ref unzig); + ref unzig, + ref emitBufferBase); Block8x8F.Scale16X16To8X8(ref b, cr); prevDCCr = this.WriteBlock( @@ -1044,7 +1056,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg ref temp1, ref temp2, ref onStackChrominanceQuantTable, - ref unzig); + ref unzig, + ref emitBufferBase); } } }