Browse Source

Merge remote-tracking branch 'upstream/tiff-format' into tiff-format

pull/1570/head
Ildar Khayrutdinov 5 years ago
parent
commit
f17c117096
  1. 4
      README.md
  2. 4
      src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs
  3. 2
      src/ImageSharp/Formats/Jpeg/Components/RowOctet.cs
  4. 6
      src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
  5. 1
      src/ImageSharp/Formats/Tiff/Compression/Compressors/TiffLzwEncoder.cs
  6. 95
      src/ImageSharp/Formats/Tiff/Compression/Decompressors/LzwString.cs
  7. 4
      src/ImageSharp/Formats/Tiff/Compression/Decompressors/LzwTiffCompression.cs
  8. 305
      src/ImageSharp/Formats/Tiff/Compression/Decompressors/TiffLzwDecoder.cs
  9. 2
      src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernel.cs
  10. 13
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpeg.cs
  11. 9
      tests/ImageSharp.Tests.ProfilingSandbox/Program.cs
  12. 1
      tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs
  13. 65
      tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs
  14. 21
      tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs
  15. 15
      tests/ImageSharp.Tests/ProfilingBenchmarks/JpegProfilingBenchmarks.cs
  16. 6
      tests/ImageSharp.Tests/TestUtilities/Tests/FeatureTestRunnerTests.cs

4
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
<a href="https://opencollective.com/sixlabors/tiers/bronze-sponsors/7/website" target="_blank"><img src="https://opencollective.com/sixlabors/tiers/bronze-sponsors/7/avatar.svg?avatarHeight=96"></a>
<a href="https://opencollective.com/sixlabors/tiers/bronze-sponsors/8/website" target="_blank"><img src="https://opencollective.com/sixlabors/tiers/bronze-sponsors/8/avatar.svg?avatarHeight=96"></a>
<a href="https://opencollective.com/sixlabors/tiers/bronze-sponsors/9/website" target="_blank"><img src="https://opencollective.com/sixlabors/tiers/bronze-sponsors/9/avatar.svg?avatarHeight=96"></a>
<a href="https://opencollective.com/sixlabors/tiers/bronze-sponsors/10/website" target="_blank"><img src="https://opencollective.com/sixlabors/tiers/bronze-sponsors/10/avatar.svg?avatarHeight=96"></a>
<a href="https://opencollective.com/sixlabors/tiers/bronze-sponsors/10/website" target="_blank"><img src="https://opencollective.com/sixlabors/tiers/bronze-sponsors/10/avatar.svg?avatarHeight=96"></a>

4
src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs

@ -59,7 +59,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// <returns>The float value at the specified index</returns>
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);

2
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<T> buffer, int startY)
{
// We don't actually have to assign values outside of the

6
src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs

@ -315,7 +315,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// <param name="bits">The packed bits.</param>
/// <param name="count">The number of bits</param>
/// <param name="emitBufferBase">The reference to the emitBuffer.</param>
[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
/// <param name="index">The index of the Huffman encoder</param>
/// <param name="value">The value to encode.</param>
/// <param name="emitBufferBase">The reference to the emit buffer.</param>
[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
/// <param name="runLength">The number of copies to encode.</param>
/// <param name="value">The value to encode.</param>
/// <param name="emitBufferBase">The reference to the emit buffer.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
private void EmitHuffRLE(HuffIndex index, int runLength, int value, ref byte emitBufferBase)
{
int a = value;

1
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;

95
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
{
/// <summary>
/// Represents a lzw string with a code word and a code length.
/// </summary>
public class LzwString
{
private static readonly LzwString Empty = new LzwString(0, 0, 0, null);
private readonly LzwString previous;
private readonly byte value;
/// <summary>
/// Initializes a new instance of the <see cref="LzwString"/> class.
/// </summary>
/// <param name="code">The code word.</param>
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;
}
/// <summary>
/// Gets the code length;
/// </summary>
public int Length { get; }
/// <summary>
/// Gets the first character of the codeword.
/// </summary>
public byte FirstChar { get; }
/// <summary>
/// Concatenates two code words.
/// </summary>
/// <param name="other">The code word to concatenate.</param>
/// <returns>A concatenated lzw string.</returns>
public LzwString Concatenate(byte other)
{
if (this == Empty)
{
return new LzwString(other);
}
return new LzwString(other, this.FirstChar, this.Length + 1, this);
}
/// <summary>
/// Writes decoded pixel to buffer at a given position.
/// </summary>
/// <param name="buffer">The buffer to write to.</param>
/// <param name="offset">The position to write to.</param>
/// <returns>The number of bytes written.</returns>
public int WriteTo(Span<byte> 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;
}
}
}

4
src/ImageSharp/Formats/Tiff/Compression/Decompressors/LzwTiffCompression.cs

@ -29,8 +29,8 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompresso
/// <inheritdoc/>
protected override void Decompress(BufferedReadStream stream, int byteCount, Span<byte> 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)
{

305
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.
*/
/// <summary>
/// Decompresses and decodes data using the dynamic LZW algorithms.
/// Decompresses and decodes data using the dynamic LZW algorithms, see TIFF spec Section 13.
/// </summary>
/// <remarks>
/// This code is based on the <see cref="LzwDecoder"/> 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).
/// </remarks>
internal sealed class TiffLzwDecoder
{
/// <summary>
/// The max decoder pixel stack size.
/// The stream to decode.
/// </summary>
private const int MaxStackSize = 4096;
private readonly Stream stream;
/// <summary>
/// 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.
/// </summary>
private const int NullCode = -1;
private const int ClearCode = 256;
/// <summary>
/// The stream to decode.
/// End of Information.
/// </summary>
private readonly Stream stream;
private const int EoiCode = 257;
/// <summary>
/// The memory allocator.
/// Minimum code length of 9 bits.
/// </summary>
private readonly MemoryAllocator allocator;
private const int MinBits = 9;
/// <summary>
/// Maximum code length of 12 bits.
/// </summary>
private const int MaxBits = 12;
/// <summary>
/// Maximum table size of 4096.
/// </summary>
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;
/// <summary>
/// Initializes a new instance of the <see cref="TiffLzwDecoder" /> class
/// and sets the stream, where the compressed data should be read from.
/// </summary>
/// <param name="stream">The stream to read from.</param>
/// <param name="allocator">The memory allocator.</param>
/// <exception cref="System.ArgumentNullException"><paramref name="stream" /> is null.</exception>
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;
}
/// <summary>
/// Decodes and decompresses all pixel indices from the stream.
/// </summary>
/// <param name="length">The length of the compressed data.</param>
/// <param name="dataSize">Size of the data.</param>
/// <param name="pixels">The pixel array to decode to.</param>
public void DecodePixels(int length, int dataSize, Span<byte> pixels)
public void DecodePixels(Span<byte> pixels)
{
Guard.MustBeLessThan(dataSize, int.MaxValue, nameof(dataSize));
// Initialize buffers
using IMemoryOwner<int> prefixMemory = this.allocator.Allocate<int>(MaxStackSize, AllocationOptions.Clean);
using IMemoryOwner<int> suffixMemory = this.allocator.Allocate<int>(MaxStackSize, AllocationOptions.Clean);
using IMemoryOwner<int> pixelStackMemory = this.allocator.Allocate<int>(MaxStackSize + 1, AllocationOptions.Clean);
Span<int> prefix = prefixMemory.GetSpan();
Span<int> suffix = suffixMemory.GetSpan();
Span<int> 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;
}
}

2
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);

13
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<Rgba32> 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<Rgba32>(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);
}
}
}

9
tests/ImageSharp.Tests.ProfilingSandbox/Program.cs

@ -31,14 +31,21 @@ namespace SixLabors.ImageSharp.Tests.ProfilingSandbox
/// </param>
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);

1
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

65
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<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
var encoder = new TiffEncoder { Mode = TiffEncodingMode.ColorPalette, Compression = TiffEncoderCompression.None };
this.TiffEncoderPaletteTest(provider, encoder);
}
where TPixel : unmanaged, IPixel<TPixel> =>
TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.ColorPalette, useExactComparer: false, compareTolerance: 0.001f);
[Theory]
[WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)]
public void TiffEncoder_EncodeColorPalette_WithDeflateCompression_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
var encoder = new TiffEncoder { Mode = TiffEncodingMode.ColorPalette, Compression = TiffEncoderCompression.Deflate };
this.TiffEncoderPaletteTest(provider, encoder);
}
where TPixel : unmanaged, IPixel<TPixel> =>
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<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
var encoder = new TiffEncoder { Mode = TiffEncodingMode.ColorPalette, Compression = TiffEncoderCompression.Deflate, UseHorizontalPredictor = true };
this.TiffEncoderPaletteTest(provider, encoder);
}
where TPixel : unmanaged, IPixel<TPixel> =>
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<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
var encoder = new TiffEncoder { Mode = TiffEncodingMode.ColorPalette, Compression = TiffEncoderCompression.Lzw };
this.TiffEncoderPaletteTest(provider, encoder);
}
where TPixel : unmanaged, IPixel<TPixel> =>
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<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
var encoder = new TiffEncoder { Mode = TiffEncodingMode.ColorPalette, Compression = TiffEncoderCompression.Lzw, UseHorizontalPredictor = true };
Assert.Throws<ImageDifferenceIsOverThresholdException>(() => this.TiffEncoderPaletteTest(provider, encoder));
}
where TPixel : unmanaged, IPixel<TPixel> =>
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<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
var encoder = new TiffEncoder { Mode = TiffEncodingMode.ColorPalette, Compression = TiffEncoderCompression.PackBits };
this.TiffEncoderPaletteTest(provider, encoder);
}
private void TiffEncoderPaletteTest<TPixel>(TestImageProvider<TPixel> provider, TiffEncoder encoder)
where TPixel : unmanaged, IPixel<TPixel>
{
// 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<TPixel> image = provider.GetImage();
using var memStream = new MemoryStream();
image.Save(memStream, encoder);
memStream.Position = 0;
using var encodedImage = (Image<TPixel>)Image.Load(Configuration, memStream);
var encodedImagePath = provider.Utility.SaveTestOutputFile(encodedImage, "tiff", encoder);
TiffTestUtils.CompareWithReferenceDecoder(encodedImagePath, encodedImage);
}
where TPixel : unmanaged, IPixel<TPixel> =>
TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.ColorPalette, TiffEncoderCompression.PackBits, useExactComparer: false, compareTolerance: 0.001f);
[Theory]
[WithFile(Calliphora_BiColorUncompressed, PixelTypes.Rgba32)]

21
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<Rgba32>(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
}
}

15
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)]

6
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<HwIntrinsics, string> features = expectedItrinsics.ToFeatureKeyValueCollection();
Dictionary<HwIntrinsics, string> 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<string> actualValues = features.Select(x => x.Value);
Assert.Equal(expectedValues, actualValues);

Loading…
Cancel
Save