diff --git a/README.md b/README.md
index 6b2fa5d0f5..14352bd80e 100644
--- a/README.md
+++ b/README.md
@@ -70,7 +70,7 @@ To clone ImageSharp locally, click the "Clone in [YOUR_OS]" button above or run
git clone https://github.com/SixLabors/ImageSharp
```
-If working with Windows please ensure that you have enabled log file paths in git (run as Administrator).
+If working with Windows please ensure that you have enabled long file paths in git (run as Administrator).
```bash
git config --system core.longpaths true
@@ -130,4 +130,4 @@ Become a bronze sponsor with a monthly donation of $100 and get your logo (small
-
\ No newline at end of file
+
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs
index ddbac2d072..56afae68c7 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs
@@ -59,7 +59,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// The float value at the specified index
public float this[int idx]
{
- [MethodImpl(InliningOptions.ShortMethod)]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
GuardBlockIndex(idx);
@@ -67,7 +67,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
return Unsafe.Add(ref selfRef, idx);
}
- [MethodImpl(InliningOptions.ShortMethod)]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
set
{
GuardBlockIndex(idx);
diff --git a/src/ImageSharp/Formats/Jpeg/Components/RowOctet.cs b/src/ImageSharp/Formats/Jpeg/Components/RowOctet.cs
index f35bb44682..16d24cf814 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/RowOctet.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/RowOctet.cs
@@ -75,7 +75,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
}
}
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(InliningOptions.ShortMethod)]
public void Update(Buffer2D buffer, int startY)
{
// We don't actually have to assign values outside of the
diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
index 422c7bd7ec..d26fbb936d 100644
--- a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
+++ b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
@@ -315,7 +315,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// The packed bits.
/// The number of bits
/// The reference to the emitBuffer.
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(InliningOptions.ShortMethod)]
private void Emit(uint bits, uint count, ref byte emitBufferBase)
{
count += this.bitCount;
@@ -356,7 +356,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// The index of the Huffman encoder
/// The value to encode.
/// The reference to the emit buffer.
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(InliningOptions.ShortMethod)]
private void EmitHuff(HuffIndex index, int value, ref byte emitBufferBase)
{
uint x = HuffmanLut.TheHuffmanLut[(int)index].Values[value];
@@ -370,7 +370,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// The number of copies to encode.
/// The value to encode.
/// The reference to the emit buffer.
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(InliningOptions.ShortMethod)]
private void EmitHuffRLE(HuffIndex index, int runLength, int value, ref byte emitBufferBase)
{
int a = value;
diff --git a/src/ImageSharp/Formats/Tiff/Compression/Compressors/TiffLzwEncoder.cs b/src/ImageSharp/Formats/Tiff/Compression/Compressors/TiffLzwEncoder.cs
index f6a74c166a..0e73b9fb46 100644
--- a/src/ImageSharp/Formats/Tiff/Compression/Compressors/TiffLzwEncoder.cs
+++ b/src/ImageSharp/Formats/Tiff/Compression/Compressors/TiffLzwEncoder.cs
@@ -258,7 +258,6 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors
{
this.children.GetSpan().Fill(0);
this.siblings.GetSpan().Fill(0);
-
this.bitsPerCode = MinBits;
this.maxCode = MaxValue(this.bitsPerCode);
this.nextValidCode = EoiCode + 1;
diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/LzwString.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/LzwString.cs
new file mode 100644
index 0000000000..ebe7319413
--- /dev/null
+++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/LzwString.cs
@@ -0,0 +1,95 @@
+// Copyright (c) Six Labors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+
+namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompressors
+{
+ ///
+ /// Represents a lzw string with a code word and a code length.
+ ///
+ public class LzwString
+ {
+ private static readonly LzwString Empty = new LzwString(0, 0, 0, null);
+
+ private readonly LzwString previous;
+ private readonly byte value;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The code word.
+ public LzwString(byte code)
+ : this(code, code, 1, null)
+ {
+ }
+
+ private LzwString(byte value, byte firstChar, int length, LzwString previous)
+ {
+ this.value = value;
+ this.FirstChar = firstChar;
+ this.Length = length;
+ this.previous = previous;
+ }
+
+ ///
+ /// Gets the code length;
+ ///
+ public int Length { get; }
+
+ ///
+ /// Gets the first character of the codeword.
+ ///
+ public byte FirstChar { get; }
+
+ ///
+ /// Concatenates two code words.
+ ///
+ /// The code word to concatenate.
+ /// A concatenated lzw string.
+ public LzwString Concatenate(byte other)
+ {
+ if (this == Empty)
+ {
+ return new LzwString(other);
+ }
+
+ return new LzwString(other, this.FirstChar, this.Length + 1, this);
+ }
+
+ ///
+ /// Writes decoded pixel to buffer at a given position.
+ ///
+ /// The buffer to write to.
+ /// The position to write to.
+ /// The number of bytes written.
+ public int WriteTo(Span buffer, int offset)
+ {
+ if (this.Length == 0)
+ {
+ return 0;
+ }
+
+ if (this.Length == 1)
+ {
+ buffer[offset] = this.value;
+ return 1;
+ }
+
+ LzwString e = this;
+ var endIdx = this.Length - 1;
+ if (endIdx >= buffer.Length)
+ {
+ TiffThrowHelper.ThrowImageFormatException("Error reading lzw compressed stream. Either pixel buffer to write to is to small or code length is invalid!");
+ }
+
+ for (int i = endIdx; i >= 0; i--)
+ {
+ buffer[offset + i] = e.value;
+ e = e.previous;
+ }
+
+ return this.Length;
+ }
+ }
+}
diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/LzwTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/LzwTiffCompression.cs
index 82640dfed3..b85aa3a22b 100644
--- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/LzwTiffCompression.cs
+++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/LzwTiffCompression.cs
@@ -29,8 +29,8 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompresso
///
protected override void Decompress(BufferedReadStream stream, int byteCount, Span buffer)
{
- var decoder = new TiffLzwDecoder(stream, this.Allocator);
- decoder.DecodePixels(buffer.Length, 8, buffer);
+ var decoder = new TiffLzwDecoder(stream);
+ decoder.DecodePixels(buffer);
if (this.Predictor == TiffPredictor.Horizontal)
{
diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/TiffLzwDecoder.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/TiffLzwDecoder.cs
index d8150bea77..2f7ff0ee36 100644
--- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/TiffLzwDecoder.cs
+++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/TiffLzwDecoder.cs
@@ -2,193 +2,254 @@
// Licensed under the Apache License, Version 2.0.
using System;
-using System.Buffers;
using System.IO;
-using SixLabors.ImageSharp.Formats.Gif;
-using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompressors
{
+ /*
+ This implementation is based on a port of a java tiff decoder by Harald Kuhr: https://github.com/haraldk/TwelveMonkeys
+
+ Original licence:
+
+ BSD 3-Clause License
+
+ * Copyright (c) 2015, Harald Kuhr
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ ** Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
///
- /// Decompresses and decodes data using the dynamic LZW algorithms.
+ /// Decompresses and decodes data using the dynamic LZW algorithms, see TIFF spec Section 13.
///
- ///
- /// This code is based on the used for GIF decoding. There is potential
- /// for a shared implementation. Differences between the GIF and TIFF implementations of the LZW
- /// encoding are: (i) The GIF implementation includes an initial 'data size' byte, whilst this is
- /// always 8 for TIFF. (ii) The GIF implementation writes a number of sub-blocks with an initial
- /// byte indicating the length of the sub-block. In TIFF the data is written as a single block
- /// with no length indicator (this can be determined from the 'StripByteCounts' entry).
- ///
internal sealed class TiffLzwDecoder
{
///
- /// The max decoder pixel stack size.
+ /// The stream to decode.
///
- private const int MaxStackSize = 4096;
+ private readonly Stream stream;
///
- /// The null code.
+ /// As soon as we use entry 4094 of the table (maxTableSize - 2), the lzw compressor write out a (12-bit) ClearCode.
+ /// At this point, the compressor reinitializes the string table and then writes out 9-bit codes again.
///
- private const int NullCode = -1;
+ private const int ClearCode = 256;
///
- /// The stream to decode.
+ /// End of Information.
///
- private readonly Stream stream;
+ private const int EoiCode = 257;
///
- /// The memory allocator.
+ /// Minimum code length of 9 bits.
///
- private readonly MemoryAllocator allocator;
+ private const int MinBits = 9;
+
+ ///
+ /// Maximum code length of 12 bits.
+ ///
+ private const int MaxBits = 12;
+
+ ///
+ /// Maximum table size of 4096.
+ ///
+ private const int TableSize = 1 << MaxBits;
+
+ private readonly LzwString[] table;
+
+ private int tableLength;
+ private int bitsPerCode;
+ private int oldCode = ClearCode;
+ private int maxCode;
+ private int bitMask;
+ private int maxString;
+ private bool eofReached;
+ private int nextData;
+ private int nextBits;
///
/// Initializes a new instance of the class
/// and sets the stream, where the compressed data should be read from.
///
/// The stream to read from.
- /// The memory allocator.
/// is null.
- public TiffLzwDecoder(Stream stream, MemoryAllocator allocator)
+ public TiffLzwDecoder(Stream stream)
{
Guard.NotNull(stream, nameof(stream));
this.stream = stream;
- this.allocator = allocator;
+ this.table = new LzwString[TableSize];
+ for (int i = 0; i < 256; i++)
+ {
+ this.table[i] = new LzwString((byte)i);
+ }
+
+ this.Init();
+ }
+
+ private void Init()
+ {
+ // Table length is 256 + 2, because of special clear code and end of information code.
+ this.tableLength = 258;
+ this.bitsPerCode = MinBits;
+ this.bitMask = BitmaskFor(this.bitsPerCode);
+ this.maxCode = this.MaxCode();
+ this.maxString = 1;
}
///
/// Decodes and decompresses all pixel indices from the stream.
///
- /// The length of the compressed data.
- /// Size of the data.
/// The pixel array to decode to.
- public void DecodePixels(int length, int dataSize, Span pixels)
+ public void DecodePixels(Span pixels)
{
- Guard.MustBeLessThan(dataSize, int.MaxValue, nameof(dataSize));
-
- // Initialize buffers
- using IMemoryOwner prefixMemory = this.allocator.Allocate(MaxStackSize, AllocationOptions.Clean);
- using IMemoryOwner suffixMemory = this.allocator.Allocate(MaxStackSize, AllocationOptions.Clean);
- using IMemoryOwner pixelStackMemory = this.allocator.Allocate(MaxStackSize + 1, AllocationOptions.Clean);
-
- Span prefix = prefixMemory.GetSpan();
- Span suffix = suffixMemory.GetSpan();
- Span pixelStack = pixelStackMemory.GetSpan();
-
- // Calculate the clear code. The value of the clear code is 2 ^ dataSize
- int clearCode = 1 << dataSize;
-
- int codeSize = dataSize + 1;
-
- // Calculate the end code
- int endCode = clearCode + 1;
-
- // Calculate the available code.
- int availableCode = clearCode + 2;
-
- // Jillzhangs Code see: http://giflib.codeplex.com/
- // Adapted from John Cristy's ImageMagick.
+ // Adapted from the pseudo-code example found in the TIFF 6.0 Specification, 1992.
+ // See Section 13: "LZW Compression"/"LZW Decoding", page 61+
int code;
- int oldCode = NullCode;
- int codeMask = (1 << codeSize) - 1;
-
- int inputByte = 0;
- int bits = 0;
-
- int top = 0;
- int xyz = 0;
-
- int first = 0;
+ int offset = 0;
- for (code = 0; code < clearCode; code++)
+ while ((code = this.GetNextCode()) != EoiCode)
{
- prefix[code] = 0;
- suffix[code] = (byte)code;
- }
-
- // Decoding process
- while (xyz < length)
- {
- if (top == 0)
+ if (code == ClearCode)
{
- // Get the next code
- int data = inputByte & ((1 << bits) - 1);
+ this.Init();
+ code = this.GetNextCode();
- while (bits < codeSize)
- {
- inputByte = this.stream.ReadByte();
- data = (data << 8) | inputByte;
- bits += 8;
- }
-
- data >>= bits - codeSize;
- bits -= codeSize;
- code = data & codeMask;
-
- // Interpret the code
- if (code > availableCode || code == endCode)
+ if (code == EoiCode)
{
break;
}
- if (code == clearCode)
+ if (this.table[code] == null)
{
- // Reset the decoder
- codeSize = dataSize + 1;
- codeMask = (1 << codeSize) - 1;
- availableCode = clearCode + 2;
- oldCode = NullCode;
- continue;
+ TiffThrowHelper.ThrowImageFormatException($"Corrupted TIFF LZW: code {code} (table size: {this.tableLength})");
}
- if (oldCode == NullCode)
+ offset += this.table[code].WriteTo(pixels, offset);
+ }
+ else
+ {
+ if (this.table[this.oldCode] == null)
{
- pixelStack[top++] = suffix[code];
- oldCode = code;
- first = code;
- continue;
+ TiffThrowHelper.ThrowImageFormatException($"Corrupted TIFF LZW: code {this.oldCode} (table size: {this.tableLength})");
}
- int inCode = code;
- if (code == availableCode)
+ if (this.IsInTable(code))
{
- pixelStack[top++] = (byte)first;
+ offset += this.table[code].WriteTo(pixels, offset);
- code = oldCode;
+ this.AddStringToTable(this.table[this.oldCode].Concatenate(this.table[code].FirstChar));
}
-
- while (code > clearCode)
+ else
{
- pixelStack[top++] = suffix[code];
- code = prefix[code];
+ LzwString outString = this.table[this.oldCode].Concatenate(this.table[this.oldCode].FirstChar);
+
+ offset += outString.WriteTo(pixels, offset);
+ this.AddStringToTable(outString);
}
+ }
- first = suffix[code];
+ this.oldCode = code;
- pixelStack[top++] = suffix[code];
+ if (offset >= pixels.Length)
+ {
+ break;
+ }
+ }
+ }
- if (availableCode < MaxStackSize)
- {
- prefix[availableCode] = oldCode;
- suffix[availableCode] = first;
- availableCode++;
- if (availableCode > codeMask - 1 && availableCode < MaxStackSize)
- {
- codeSize++;
- codeMask = (1 << codeSize) - 1;
- }
- }
+ private void AddStringToTable(LzwString lzwString)
+ {
+ if (this.tableLength > this.table.Length)
+ {
+ TiffThrowHelper.ThrowImageFormatException($"TIFF LZW with more than {MaxBits} bits per code encountered (table overflow)");
+ }
+
+ this.table[this.tableLength++] = lzwString;
+
+ if (this.tableLength > this.maxCode)
+ {
+ this.bitsPerCode++;
- oldCode = inCode;
+ if (this.bitsPerCode > MaxBits)
+ {
+ // Continue reading MaxBits (12 bit) length codes.
+ this.bitsPerCode = MaxBits;
}
- // Pop a pixel off the pixel stack.
- top--;
+ this.bitMask = BitmaskFor(this.bitsPerCode);
+ this.maxCode = this.MaxCode();
+ }
+
+ if (lzwString.Length > this.maxString)
+ {
+ this.maxString = lzwString.Length;
+ }
+ }
+
+ private int GetNextCode()
+ {
+ if (this.eofReached)
+ {
+ return EoiCode;
+ }
- // Clear missing pixels
- pixels[xyz++] = (byte)pixelStack[top];
+ int read = this.stream.ReadByte();
+ if (read < 0)
+ {
+ this.eofReached = true;
+ return EoiCode;
}
+
+ this.nextData = (this.nextData << 8) | read;
+ this.nextBits += 8;
+
+ if (this.nextBits < this.bitsPerCode)
+ {
+ read = this.stream.ReadByte();
+ if (read < 0)
+ {
+ this.eofReached = true;
+ return EoiCode;
+ }
+
+ this.nextData = (this.nextData << 8) | read;
+ this.nextBits += 8;
+ }
+
+ var code = (this.nextData >> (this.nextBits - this.bitsPerCode)) & this.bitMask;
+ this.nextBits -= this.bitsPerCode;
+
+ return code;
}
+
+ private bool IsInTable(int code) => code < this.tableLength;
+
+ private int MaxCode() => this.bitMask - 1;
+
+ private static int BitmaskFor(int bits) => (1 << bits) - 1;
}
}
diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernel.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernel.cs
index 979206ad5c..66f885f23a 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernel.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernel.cs
@@ -72,7 +72,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
public Vector4 ConvolveCore(ref Vector4 rowStartRef)
{
#if SUPPORTS_RUNTIME_INTRINSICS
- if (Fma.IsSupported)
+ if (Avx2.IsSupported && Fma.IsSupported)
{
float* bufferStart = this.bufferPtr;
float* bufferEnd = bufferStart + (this.Length & ~3);
diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpeg.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpeg.cs
index 81a5604f1e..5a9ceea946 100644
--- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpeg.cs
+++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpeg.cs
@@ -16,17 +16,20 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
private Stream bmpStream;
private SDImage bmpDrawing;
private Image bmpCore;
+ private MemoryStream destinationStream;
[GlobalSetup]
public void ReadImages()
{
if (this.bmpStream == null)
{
- const string TestImage = TestImages.Bmp.NegHeight;
+ const string TestImage = TestImages.Jpeg.BenchmarkSuite.Jpeg420Exif_MidSizeYCbCr;
this.bmpStream = File.OpenRead(Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, TestImage));
this.bmpCore = Image.Load(this.bmpStream);
+ this.bmpCore.Metadata.ExifProfile = null;
this.bmpStream.Position = 0;
this.bmpDrawing = SDImage.FromStream(this.bmpStream);
+ this.destinationStream = new MemoryStream();
}
}
@@ -42,15 +45,15 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
[Benchmark(Baseline = true, Description = "System.Drawing Jpeg")]
public void JpegSystemDrawing()
{
- using var stream = new MemoryStream();
- this.bmpDrawing.Save(stream, ImageFormat.Jpeg);
+ this.bmpDrawing.Save(this.destinationStream, ImageFormat.Jpeg);
+ this.destinationStream.Seek(0, SeekOrigin.Begin);
}
[Benchmark(Description = "ImageSharp Jpeg")]
public void JpegCore()
{
- using var stream = new MemoryStream();
- this.bmpCore.SaveAsJpeg(stream);
+ this.bmpCore.SaveAsJpeg(this.destinationStream);
+ this.destinationStream.Seek(0, SeekOrigin.Begin);
}
}
}
diff --git a/tests/ImageSharp.Tests.ProfilingSandbox/Program.cs b/tests/ImageSharp.Tests.ProfilingSandbox/Program.cs
index 5504a99784..50a930b6f1 100644
--- a/tests/ImageSharp.Tests.ProfilingSandbox/Program.cs
+++ b/tests/ImageSharp.Tests.ProfilingSandbox/Program.cs
@@ -31,14 +31,21 @@ namespace SixLabors.ImageSharp.Tests.ProfilingSandbox
///
public static void Main(string[] args)
{
+ RunJpegEncoderProfilingTests();
// RunJpegColorProfilingTests();
- RunDecodeJpegProfilingTests();
+ // RunDecodeJpegProfilingTests();
// RunToVector4ProfilingTest();
// RunResizeProfilingTest();
Console.ReadLine();
}
+ private static void RunJpegEncoderProfilingTests()
+ {
+ var benchmarks = new JpegProfilingBenchmarks(new ConsoleOutput());
+ benchmarks.EncodeJpeg_SingleMidSize();
+ }
+
private static void RunJpegColorProfilingTests()
{
new JpegColorConverterTests(new ConsoleOutput()).BenchmarkYCbCr(false);
diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs
index fcce507d8d..9108cdaf8a 100644
--- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs
@@ -28,6 +28,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Compression
[Theory]
[InlineData(new byte[] { })]
[InlineData(new byte[] { 42 })] // One byte
+ [InlineData(new byte[] { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 })]
[InlineData(new byte[] { 42, 16, 128, 53, 96, 218, 7, 64, 3, 4, 97 })] // Random bytes
[InlineData(new byte[] { 1, 2, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 3, 4 })] // Repeated bytes
[InlineData(new byte[] { 1, 2, 42, 53, 42, 53, 42, 53, 42, 53, 42, 53, 3, 4 })] // Repeated sequence
diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs
index b9368e4ca6..6efe92b232 100644
--- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs
@@ -196,79 +196,38 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
[Theory]
[WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)]
public void TiffEncoder_EncodeColorPalette_Works(TestImageProvider provider)
- where TPixel : unmanaged, IPixel
- {
- var encoder = new TiffEncoder { Mode = TiffEncodingMode.ColorPalette, Compression = TiffEncoderCompression.None };
-
- this.TiffEncoderPaletteTest(provider, encoder);
- }
+ where TPixel : unmanaged, IPixel =>
+ TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.ColorPalette, useExactComparer: false, compareTolerance: 0.001f);
[Theory]
[WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)]
public void TiffEncoder_EncodeColorPalette_WithDeflateCompression_Works(TestImageProvider provider)
- where TPixel : unmanaged, IPixel
- {
- var encoder = new TiffEncoder { Mode = TiffEncodingMode.ColorPalette, Compression = TiffEncoderCompression.Deflate };
-
- this.TiffEncoderPaletteTest(provider, encoder);
- }
+ where TPixel : unmanaged, IPixel =>
+ TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.ColorPalette, TiffEncoderCompression.Deflate, useExactComparer: false, compareTolerance: 0.001f);
[Theory]
[WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)]
public void TiffEncoder_EncodeColorPalette_WithDeflateCompressionAndPredictor_Works(TestImageProvider provider)
- where TPixel : unmanaged, IPixel
- {
- var encoder = new TiffEncoder { Mode = TiffEncodingMode.ColorPalette, Compression = TiffEncoderCompression.Deflate, UseHorizontalPredictor = true };
-
- this.TiffEncoderPaletteTest(provider, encoder);
- }
+ where TPixel : unmanaged, IPixel =>
+ TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.ColorPalette, TiffEncoderCompression.Deflate, usePredictor: true, useExactComparer: false, compareTolerance: 0.001f);
[Theory]
[WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)]
public void TiffEncoder_EncodeColorPalette_WithLzwCompression_Works_Bug(TestImageProvider provider)
- where TPixel : unmanaged, IPixel
- {
- var encoder = new TiffEncoder { Mode = TiffEncodingMode.ColorPalette, Compression = TiffEncoderCompression.Lzw };
-
- this.TiffEncoderPaletteTest(provider, encoder);
- }
+ where TPixel : unmanaged, IPixel =>
+ TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.ColorPalette, TiffEncoderCompression.Lzw, useExactComparer: false, compareTolerance: 0.001f);
[Theory]
[WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)]
public void TiffEncoder_EncodeColorPalette_WithLzwCompressionAndPredictor_Works_Bug(TestImageProvider provider)
- where TPixel : unmanaged, IPixel
- {
- var encoder = new TiffEncoder { Mode = TiffEncodingMode.ColorPalette, Compression = TiffEncoderCompression.Lzw, UseHorizontalPredictor = true };
-
- Assert.Throws(() => this.TiffEncoderPaletteTest(provider, encoder));
- }
+ where TPixel : unmanaged, IPixel =>
+ TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.ColorPalette, TiffEncoderCompression.Lzw, usePredictor: true, useExactComparer: false, compareTolerance: 0.001f);
[Theory]
[WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)]
public void TiffEncoder_EncodeColorPalette_WithPackBitsCompression_Works(TestImageProvider provider)
- where TPixel : unmanaged, IPixel
- {
- var encoder = new TiffEncoder { Mode = TiffEncodingMode.ColorPalette, Compression = TiffEncoderCompression.PackBits };
-
- this.TiffEncoderPaletteTest(provider, encoder);
- }
-
- private void TiffEncoderPaletteTest(TestImageProvider provider, TiffEncoder encoder)
- where TPixel : unmanaged, IPixel
- {
- // Because a quantizer is used to create the palette (and therefore changes to the original are expected),
- // we do not compare the encoded image against the original:
- // Instead we load the encoded image with a reference decoder and compare against that image.
- using Image image = provider.GetImage();
- using var memStream = new MemoryStream();
-
- image.Save(memStream, encoder);
- memStream.Position = 0;
-
- using var encodedImage = (Image)Image.Load(Configuration, memStream);
- var encodedImagePath = provider.Utility.SaveTestOutputFile(encodedImage, "tiff", encoder);
- TiffTestUtils.CompareWithReferenceDecoder(encodedImagePath, encodedImage);
- }
+ where TPixel : unmanaged, IPixel =>
+ TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.ColorPalette, TiffEncoderCompression.PackBits, useExactComparer: false, compareTolerance: 0.001f);
[Theory]
[WithFile(Calliphora_BiColorUncompressed, PixelTypes.Rgba32)]
diff --git a/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs
index bf412739d9..5f426083c2 100644
--- a/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs
+++ b/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs
@@ -1,8 +1,10 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
+using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors.Transforms;
+using SixLabors.ImageSharp.Tests.TestUtilities;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Processing.Transforms
@@ -85,5 +87,24 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
Assert.Equal(compand, resizeOptions.Compand);
Assert.Equal(mode, resizeOptions.Mode);
}
+
+#if SUPPORTS_RUNTIME_INTRINSICS
+ [Fact]
+ public void HwIntrinsics_Resize()
+ {
+ static void RunTest()
+ {
+ using var image = new Image(50, 50);
+ image.Mutate(img => img.Resize(25, 25));
+
+ Assert.Equal(25, image.Width);
+ Assert.Equal(25, image.Height);
+ }
+
+ FeatureTestRunner.RunWithHwIntrinsicsFeature(
+ RunTest,
+ HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableFMA);
+ }
+#endif
}
}
diff --git a/tests/ImageSharp.Tests/ProfilingBenchmarks/JpegProfilingBenchmarks.cs b/tests/ImageSharp.Tests/ProfilingBenchmarks/JpegProfilingBenchmarks.cs
index 78fb99802e..9de3fc8dfe 100644
--- a/tests/ImageSharp.Tests/ProfilingBenchmarks/JpegProfilingBenchmarks.cs
+++ b/tests/ImageSharp.Tests/ProfilingBenchmarks/JpegProfilingBenchmarks.cs
@@ -75,6 +75,21 @@ namespace SixLabors.ImageSharp.Tests.ProfilingBenchmarks
#pragma warning restore SA1515 // Single-line comment should be preceded by blank line
}
+ [Fact(Skip = ProfilingSetup.SkipProfilingTests)]
+ public void EncodeJpeg_SingleMidSize()
+ {
+ string path = TestFile.GetInputFileFullPath(TestImages.Jpeg.BenchmarkSuite.Jpeg420Exif_MidSizeYCbCr);
+ using var image = Image.Load(path);
+ image.Metadata.ExifProfile = null;
+
+ using var ms = new MemoryStream();
+ for (int i = 0; i < 30; i++)
+ {
+ image.SaveAsJpeg(ms);
+ ms.Seek(0, SeekOrigin.Begin);
+ }
+ }
+
// Benchmark, enable manually!
[Theory(Skip = ProfilingSetup.SkipProfilingTests)]
[InlineData(1, 75, JpegSubsample.Ratio420)]
diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/FeatureTestRunnerTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/FeatureTestRunnerTests.cs
index 4cbbefe686..a2f36c85a8 100644
--- a/tests/ImageSharp.Tests/TestUtilities/Tests/FeatureTestRunnerTests.cs
+++ b/tests/ImageSharp.Tests/TestUtilities/Tests/FeatureTestRunnerTests.cs
@@ -25,9 +25,9 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.Tests
[Theory]
[MemberData(nameof(Intrinsics))]
- public void ToFeatureCollectionReturnsExpectedResult(HwIntrinsics expectedItrinsics, string[] expectedValues)
+ public void ToFeatureCollectionReturnsExpectedResult(HwIntrinsics expectedIntrinsics, string[] expectedValues)
{
- Dictionary features = expectedItrinsics.ToFeatureKeyValueCollection();
+ Dictionary features = expectedIntrinsics.ToFeatureKeyValueCollection();
HwIntrinsics[] keys = features.Keys.ToArray();
HwIntrinsics actualIntrinsics = keys[0];
@@ -36,7 +36,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.Tests
actualIntrinsics |= keys[i];
}
- Assert.Equal(expectedItrinsics, actualIntrinsics);
+ Assert.Equal(expectedIntrinsics, actualIntrinsics);
IEnumerable actualValues = features.Select(x => x.Value);
Assert.Equal(expectedValues, actualValues);