Browse Source

Merge branch 'master' into webp

pull/1552/head
Brian Popow 5 years ago
committed by GitHub
parent
commit
e32c3c08e1
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      README.md
  2. 3
      src/ImageSharp/Formats/Gif/GifDecoderCore.cs
  3. 106
      src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs
  4. 6
      src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter{TPixel}.cs
  5. 2
      src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.cs
  6. 93
      src/ImageSharp/Formats/Jpeg/Components/RowOctet.cs
  7. 93
      src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
  8. 2
      src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernel.cs
  9. 5
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Scale16X16To8X8.cs
  10. 13
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpeg.cs
  11. 9
      tests/ImageSharp.Tests.ProfilingSandbox/Program.cs
  12. 12
      tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs
  13. 11
      tests/ImageSharp.Tests/Formats/Jpg/GenericBlock8x8Tests.cs
  14. 2
      tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs
  15. 21
      tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs
  16. 15
      tests/ImageSharp.Tests/ProfilingBenchmarks/JpegProfilingBenchmarks.cs
  17. 1
      tests/ImageSharp.Tests/TestImages.cs
  18. 2
      tests/ImageSharp.Tests/TestUtilities/TestEnvironment.cs
  19. 6
      tests/ImageSharp.Tests/TestUtilities/Tests/FeatureTestRunnerTests.cs
  20. 2
      tests/Images/External
  21. 3
      tests/Images/Input/Gif/issues/issue1530.gif

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 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 ```bash
git config --system core.longpaths true 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/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/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/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>

3
src/ImageSharp/Formats/Gif/GifDecoderCore.cs

@ -535,7 +535,8 @@ namespace SixLabors.ImageSharp.Formats.Gif
return; return;
} }
Buffer2DRegion<TPixel> pixelRegion = frame.PixelBuffer.GetRegion(this.restoreArea.Value); var interest = Rectangle.Intersect(frame.Bounds(), this.restoreArea.Value);
Buffer2DRegion<TPixel> pixelRegion = frame.PixelBuffer.GetRegion(interest);
pixelRegion.Clear(); pixelRegion.Clear();
this.restoreArea = null; this.restoreArea = null;

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

@ -18,6 +18,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// <summary> /// <summary>
/// Represents a Jpeg block with <see cref="float"/> coefficients. /// Represents a Jpeg block with <see cref="float"/> coefficients.
/// </summary> /// </summary>
[StructLayout(LayoutKind.Sequential)]
internal partial struct Block8x8F : IEquatable<Block8x8F> internal partial struct Block8x8F : IEquatable<Block8x8F>
{ {
/// <summary> /// <summary>
@ -51,9 +52,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
public Vector4 V7R; public Vector4 V7R;
#pragma warning restore SA1600 // ElementsMustBeDocumented #pragma warning restore SA1600 // ElementsMustBeDocumented
private static readonly Vector4 NegativeOne = new Vector4(-1);
private static readonly Vector4 Offset = new Vector4(.5F);
/// <summary> /// <summary>
/// Get/Set scalar elements at a given index /// Get/Set scalar elements at a given index
/// </summary> /// </summary>
@ -61,7 +59,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// <returns>The float value at the specified index</returns> /// <returns>The float value at the specified index</returns>
public float this[int idx] public float this[int idx]
{ {
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
get get
{ {
GuardBlockIndex(idx); GuardBlockIndex(idx);
@ -69,7 +67,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
return Unsafe.Add(ref selfRef, idx); return Unsafe.Add(ref selfRef, idx);
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
set set
{ {
GuardBlockIndex(idx); GuardBlockIndex(idx);
@ -155,10 +153,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// </summary> /// </summary>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public void Clear() public void Clear()
{ => this = default; // The cheapest way to do this in C#:
// The cheapest way to do this in C#:
this = default;
}
/// <summary> /// <summary>
/// Load raw 32bit floating point data from source. /// Load raw 32bit floating point data from source.
@ -180,9 +175,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// <param name="source">Source</param> /// <param name="source">Source</param>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static unsafe void LoadFrom(Block8x8F* blockPtr, Span<float> source) public static unsafe void LoadFrom(Block8x8F* blockPtr, Span<float> source)
{ => blockPtr->LoadFrom(source);
blockPtr->LoadFrom(source);
}
/// <summary> /// <summary>
/// Load raw 32bit floating point data from source /// Load raw 32bit floating point data from source
@ -236,9 +229,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// <param name="dest">The destination.</param> /// <param name="dest">The destination.</param>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static unsafe void ScaledCopyTo(Block8x8F* blockPtr, Span<float> dest) public static unsafe void ScaledCopyTo(Block8x8F* blockPtr, Span<float> dest)
{ => blockPtr->ScaledCopyTo(dest);
blockPtr->ScaledCopyTo(dest);
}
/// <summary> /// <summary>
/// Copy raw 32bit floating point data to dest /// Copy raw 32bit floating point data to dest
@ -439,7 +430,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// <param name="blockPtr">The block pointer.</param> /// <param name="blockPtr">The block pointer.</param>
/// <param name="qtPtr">The qt pointer.</param> /// <param name="qtPtr">The qt pointer.</param>
/// <param name="unzigPtr">Unzig pointer</param> /// <param name="unzigPtr">Unzig pointer</param>
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe void DequantizeBlock(Block8x8F* blockPtr, Block8x8F* qtPtr, byte* unzigPtr) public static unsafe void DequantizeBlock(Block8x8F* blockPtr, Block8x8F* qtPtr, byte* unzigPtr)
{ {
float* b = (float*)blockPtr; float* b = (float*)blockPtr;
@ -556,22 +546,60 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private static void DivideRoundAll(ref Block8x8F a, ref Block8x8F b) private static void DivideRoundAll(ref Block8x8F a, ref Block8x8F b)
{ {
a.V0L = DivideRound(a.V0L, b.V0L); #if SUPPORTS_RUNTIME_INTRINSICS
a.V0R = DivideRound(a.V0R, b.V0R); if (Avx.IsSupported)
a.V1L = DivideRound(a.V1L, b.V1L); {
a.V1R = DivideRound(a.V1R, b.V1R); var vnegOne = Vector256.Create(-1f);
a.V2L = DivideRound(a.V2L, b.V2L); var vadd = Vector256.Create(.5F);
a.V2R = DivideRound(a.V2R, b.V2R); var vone = Vector256.Create(1f);
a.V3L = DivideRound(a.V3L, b.V3L);
a.V3R = DivideRound(a.V3R, b.V3R); ref Vector256<float> aBase = ref Unsafe.AsRef(Unsafe.As<Vector4, Vector256<float>>(ref a.V0L));
a.V4L = DivideRound(a.V4L, b.V4L); ref Vector256<float> bBase = ref Unsafe.AsRef(Unsafe.As<Vector4, Vector256<float>>(ref b.V0L));
a.V4R = DivideRound(a.V4R, b.V4R); ref Vector256<float> aEnd = ref Unsafe.Add(ref aBase, 8);
a.V5L = DivideRound(a.V5L, b.V5L);
a.V5R = DivideRound(a.V5R, b.V5R); do
a.V6L = DivideRound(a.V6L, b.V6L); {
a.V6R = DivideRound(a.V6R, b.V6R); Vector256<float> voff = Avx.Multiply(Avx.Min(Avx.Max(vnegOne, aBase), vone), vadd);
a.V7L = DivideRound(a.V7L, b.V7L); Unsafe.Add(ref aBase, 0) = Avx.Add(Avx.Divide(aBase, bBase), voff);
a.V7R = DivideRound(a.V7R, b.V7R);
aBase = ref Unsafe.Add(ref aBase, 1);
bBase = ref Unsafe.Add(ref bBase, 1);
}
while (Unsafe.IsAddressLessThan(ref aBase, ref aEnd));
}
else
#endif
{
a.V0L = DivideRound(a.V0L, b.V0L);
a.V0R = DivideRound(a.V0R, b.V0R);
a.V1L = DivideRound(a.V1L, b.V1L);
a.V1R = DivideRound(a.V1R, b.V1R);
a.V2L = DivideRound(a.V2L, b.V2L);
a.V2R = DivideRound(a.V2R, b.V2R);
a.V3L = DivideRound(a.V3L, b.V3L);
a.V3R = DivideRound(a.V3R, b.V3R);
a.V4L = DivideRound(a.V4L, b.V4L);
a.V4R = DivideRound(a.V4R, b.V4R);
a.V5L = DivideRound(a.V5L, b.V5L);
a.V5R = DivideRound(a.V5R, b.V5R);
a.V6L = DivideRound(a.V6L, b.V6L);
a.V6R = DivideRound(a.V6R, b.V6R);
a.V7L = DivideRound(a.V7L, b.V7L);
a.V7R = DivideRound(a.V7R, b.V7R);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Vector4 DivideRound(Vector4 dividend, Vector4 divisor)
{
var neg = new Vector4(-1);
var add = new Vector4(.5F);
// sign(dividend) = max(min(dividend, 1), -1)
Vector4 sign = Numerics.Clamp(dividend, neg, Vector4.One);
// AlmostRound(dividend/divisor) = dividend/divisor + 0.5*sign(dividend)
return (dividend / divisor) + (sign * add);
} }
public void RoundInto(ref Block8x8 dest) public void RoundInto(ref Block8x8 dest)
@ -673,8 +701,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// <inheritdoc /> /// <inheritdoc />
public bool Equals(Block8x8F other) public bool Equals(Block8x8F other)
{ => this.V0L == other.V0L
return this.V0L == other.V0L
&& this.V0R == other.V0R && this.V0R == other.V0R
&& this.V1L == other.V1L && this.V1L == other.V1L
&& this.V1R == other.V1R && this.V1R == other.V1R
@ -690,7 +717,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
&& this.V6R == other.V6R && this.V6R == other.V6R
&& this.V7L == other.V7L && this.V7L == other.V7L
&& this.V7R == other.V7R; && this.V7R == other.V7R;
}
/// <inheritdoc /> /// <inheritdoc />
public override string ToString() public override string ToString()
@ -718,16 +744,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
return row.FastRound(); return row.FastRound();
} }
[MethodImpl(InliningOptions.ShortMethod)]
private static Vector4 DivideRound(Vector4 dividend, Vector4 divisor)
{
// sign(dividend) = max(min(dividend, 1), -1)
Vector4 sign = Numerics.Clamp(dividend, NegativeOne, Vector4.One);
// AlmostRound(dividend/divisor) = dividend/divisor + 0.5*sign(dividend)
return (dividend / divisor) + (sign * Offset);
}
[Conditional("DEBUG")] [Conditional("DEBUG")]
private static void GuardBlockIndex(int idx) private static void GuardBlockIndex(int idx)
{ {

6
src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter{TPixel}.cs

@ -59,9 +59,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
/// <summary> /// <summary>
/// Converts a 8x8 image area inside 'pixels' at position (x,y) placing the result members of the structure (<see cref="Y"/>, <see cref="Cb"/>, <see cref="Cr"/>) /// Converts a 8x8 image area inside 'pixels' at position (x,y) placing the result members of the structure (<see cref="Y"/>, <see cref="Cb"/>, <see cref="Cr"/>)
/// </summary> /// </summary>
public void Convert(ImageFrame<TPixel> frame, int x, int y, in RowOctet<TPixel> currentRows) public void Convert(ImageFrame<TPixel> frame, int x, int y, ref RowOctet<TPixel> currentRows)
{ {
this.pixelBlock.LoadAndStretchEdges(frame.PixelBuffer, x, y, currentRows); this.pixelBlock.LoadAndStretchEdges(frame.PixelBuffer, x, y, ref currentRows);
Span<Rgb24> rgbSpan = this.rgbBlock.AsSpanUnsafe(); Span<Rgb24> rgbSpan = this.rgbBlock.AsSpanUnsafe();
PixelOperations<TPixel>.Instance.ToRgb24(frame.GetConfiguration(), this.pixelBlock.AsSpanUnsafe(), rgbSpan); PixelOperations<TPixel>.Instance.ToRgb24(frame.GetConfiguration(), this.pixelBlock.AsSpanUnsafe(), rgbSpan);
@ -76,7 +76,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
} }
else else
{ {
this.colorTables.Convert(rgbSpan, ref yBlock, ref cbBlock, ref crBlock); this.colorTables.Convert(rgbSpan, ref yBlock, ref cbBlock, ref crBlock);
} }
} }
} }

2
src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.cs

@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// Load a 8x8 region of an image into the block. /// Load a 8x8 region of an image into the block.
/// The "outlying" area of the block will be stretched out with pixels on the right and bottom edge of the image. /// The "outlying" area of the block will be stretched out with pixels on the right and bottom edge of the image.
/// </summary> /// </summary>
public void LoadAndStretchEdges(Buffer2D<T> source, int sourceX, int sourceY, in RowOctet<T> currentRows) public void LoadAndStretchEdges(Buffer2D<T> source, int sourceX, int sourceY, ref RowOctet<T> currentRows)
{ {
int width = Math.Min(8, source.Width - sourceX); int width = Math.Min(8, source.Width - sourceX);
int height = Math.Min(8, source.Height - sourceY); int height = Math.Min(8, source.Height - sourceY);

93
src/ImageSharp/Formats/Jpeg/Components/RowOctet.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
@ -12,39 +12,24 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// Cache 8 pixel rows on the stack, which may originate from different buffers of a <see cref="MemoryGroup{T}"/>. /// Cache 8 pixel rows on the stack, which may originate from different buffers of a <see cref="MemoryGroup{T}"/>.
/// </summary> /// </summary>
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
internal readonly ref struct RowOctet<T> internal ref struct RowOctet<T>
where T : struct where T : struct
{ {
private readonly Span<T> row0; private Span<T> row0;
private readonly Span<T> row1; private Span<T> row1;
private readonly Span<T> row2; private Span<T> row2;
private readonly Span<T> row3; private Span<T> row3;
private readonly Span<T> row4; private Span<T> row4;
private readonly Span<T> row5; private Span<T> row5;
private readonly Span<T> row6; private Span<T> row6;
private readonly Span<T> row7; private Span<T> row7;
public RowOctet(Buffer2D<T> buffer, int startY)
{
int y = startY;
int height = buffer.Height;
this.row0 = y < height ? buffer.GetRowSpan(y++) : default;
this.row1 = y < height ? buffer.GetRowSpan(y++) : default;
this.row2 = y < height ? buffer.GetRowSpan(y++) : default;
this.row3 = y < height ? buffer.GetRowSpan(y++) : default;
this.row4 = y < height ? buffer.GetRowSpan(y++) : default;
this.row5 = y < height ? buffer.GetRowSpan(y++) : default;
this.row6 = y < height ? buffer.GetRowSpan(y++) : default;
this.row7 = y < height ? buffer.GetRowSpan(y) : default;
}
// No unsafe tricks, since Span<T> can't be used as a generic argument
public Span<T> this[int y] public Span<T> this[int y]
{ {
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
get get =>
{ y switch
// No unsafe tricks, since Span<T> can't be used as a generic argument
return y switch
{ {
0 => this.row0, 0 => this.row0,
1 => this.row1, 1 => this.row1,
@ -56,13 +41,57 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
7 => this.row7, 7 => this.row7,
_ => ThrowIndexOutOfRangeException() _ => ThrowIndexOutOfRangeException()
}; };
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private set
{
switch (y)
{
case 0:
this.row0 = value;
break;
case 1:
this.row1 = value;
break;
case 2:
this.row2 = value;
break;
case 3:
this.row3 = value;
break;
case 4:
this.row4 = value;
break;
case 5:
this.row5 = value;
break;
case 6:
this.row6 = value;
break;
default:
this.row7 = value;
break;
}
} }
} }
[MethodImpl(InliningOptions.ColdPath)] [MethodImpl(InliningOptions.ShortMethod)]
private static Span<T> ThrowIndexOutOfRangeException() public void Update(Buffer2D<T> buffer, int startY)
{ {
throw new IndexOutOfRangeException(); // We don't actually have to assign values outside of the
// frame pixel buffer since they are never requested.
int y = startY;
int yEnd = Math.Min(y + 8, buffer.Height);
int i = 0;
while (y < yEnd)
{
this[i++] = buffer.GetRowSpan(y++);
}
} }
[MethodImpl(MethodImplOptions.NoInlining)]
private static Span<T> ThrowIndexOutOfRangeException()
=> throw new IndexOutOfRangeException();
} }
} }

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

@ -6,6 +6,7 @@ using System.Buffers.Binary;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading; using System.Threading;
using SixLabors.ImageSharp.Common.Helpers; using SixLabors.ImageSharp.Common.Helpers;
using SixLabors.ImageSharp.Formats.Jpeg.Components; using SixLabors.ImageSharp.Formats.Jpeg.Components;
@ -313,7 +314,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// </summary> /// </summary>
/// <param name="bits">The packed bits.</param> /// <param name="bits">The packed bits.</param>
/// <param name="count">The number of bits</param> /// <param name="count">The number of bits</param>
private void Emit(uint bits, uint count) /// <param name="emitBufferBase">The reference to the emitBuffer.</param>
[MethodImpl(InliningOptions.ShortMethod)]
private void Emit(uint bits, uint count, ref byte emitBufferBase)
{ {
count += this.bitCount; count += this.bitCount;
bits <<= (int)(32 - count); bits <<= (int)(32 - count);
@ -327,10 +330,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
while (count >= 8) while (count >= 8)
{ {
byte b = (byte)(bits >> 24); byte b = (byte)(bits >> 24);
this.emitBuffer[len++] = b; Unsafe.Add(ref emitBufferBase, len++) = b;
if (b == 0xff) if (b == byte.MaxValue)
{ {
this.emitBuffer[len++] = 0x00; Unsafe.Add(ref emitBufferBase, len++) = byte.MinValue;
} }
bits <<= 8; bits <<= 8;
@ -352,11 +355,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// </summary> /// </summary>
/// <param name="index">The index of the Huffman encoder</param> /// <param name="index">The index of the Huffman encoder</param>
/// <param name="value">The value to encode.</param> /// <param name="value">The value to encode.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] /// <param name="emitBufferBase">The reference to the emit buffer.</param>
private void EmitHuff(HuffIndex index, int value) [MethodImpl(InliningOptions.ShortMethod)]
private void EmitHuff(HuffIndex index, int value, ref byte emitBufferBase)
{ {
uint x = HuffmanLut.TheHuffmanLut[(int)index].Values[value]; 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);
} }
/// <summary> /// <summary>
@ -365,8 +369,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// <param name="index">The index of the Huffman encoder</param> /// <param name="index">The index of the Huffman encoder</param>
/// <param name="runLength">The number of copies to encode.</param> /// <param name="runLength">The number of copies to encode.</param>
/// <param name="value">The value to encode.</param> /// <param name="value">The value to encode.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] /// <param name="emitBufferBase">The reference to the emit buffer.</param>
private void EmitHuffRLE(HuffIndex index, int runLength, int value) [MethodImpl(InliningOptions.ShortMethod)]
private void EmitHuffRLE(HuffIndex index, int runLength, int value, ref byte emitBufferBase)
{ {
int a = value; int a = value;
int b = value; int b = value;
@ -386,10 +391,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
bt = 8 + (uint)BitCountLut[a >> 8]; 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) 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
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="pixels">The pixel accessor providing access to the image pixels.</param> /// <param name="pixels">The pixel accessor providing access to the image pixels.</param>
/// <param name="cancellationToken">The token to monitor for cancellation.</param> /// <param name="cancellationToken">The token to monitor for cancellation.</param>
private void Encode444<TPixel>(Image<TPixel> pixels, CancellationToken cancellationToken) /// <param name="emitBufferBase">The reference to the emit buffer.</param>
private void Encode444<TPixel>(Image<TPixel> pixels, CancellationToken cancellationToken, ref byte emitBufferBase)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
// TODO: Need a JpegScanEncoder<TPixel> class or struct that encapsulates the scan-encoding implementation. (Similar to JpegScanDecoder.) // TODO: Need a JpegScanEncoder<TPixel> class or struct that encapsulates the scan-encoding implementation. (Similar to JpegScanDecoder.)
@ -418,15 +424,16 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
var pixelConverter = YCbCrForwardConverter<TPixel>.Create(); var pixelConverter = YCbCrForwardConverter<TPixel>.Create();
ImageFrame<TPixel> frame = pixels.Frames.RootFrame; ImageFrame<TPixel> frame = pixels.Frames.RootFrame;
Buffer2D<TPixel> pixelBuffer = frame.PixelBuffer; Buffer2D<TPixel> pixelBuffer = frame.PixelBuffer;
RowOctet<TPixel> currentRows = default;
for (int y = 0; y < pixels.Height; y += 8) for (int y = 0; y < pixels.Height; y += 8)
{ {
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
var currentRows = new RowOctet<TPixel>(pixelBuffer, y); currentRows.Update(pixelBuffer, y);
for (int x = 0; x < pixels.Width; x += 8) for (int x = 0; x < pixels.Width; x += 8)
{ {
pixelConverter.Convert(frame, x, y, currentRows); pixelConverter.Convert(frame, x, y, ref currentRows);
prevDCY = this.WriteBlock( prevDCY = this.WriteBlock(
QuantIndex.Luminance, QuantIndex.Luminance,
@ -435,7 +442,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
ref temp1, ref temp1,
ref temp2, ref temp2,
ref onStackLuminanceQuantTable, ref onStackLuminanceQuantTable,
ref unzig); ref unzig,
ref emitBufferBase);
prevDCCb = this.WriteBlock( prevDCCb = this.WriteBlock(
QuantIndex.Chrominance, QuantIndex.Chrominance,
prevDCCb, prevDCCb,
@ -443,7 +452,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
ref temp1, ref temp1,
ref temp2, ref temp2,
ref onStackChrominanceQuantTable, ref onStackChrominanceQuantTable,
ref unzig); ref unzig,
ref emitBufferBase);
prevDCCr = this.WriteBlock( prevDCCr = this.WriteBlock(
QuantIndex.Chrominance, QuantIndex.Chrominance,
prevDCCr, prevDCCr,
@ -451,7 +462,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
ref temp1, ref temp1,
ref temp2, ref temp2,
ref onStackChrominanceQuantTable, ref onStackChrominanceQuantTable,
ref unzig); ref unzig,
ref emitBufferBase);
} }
} }
} }
@ -517,9 +529,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// <param name="tempDest2">Temporal block 2</param> /// <param name="tempDest2">Temporal block 2</param>
/// <param name="quant">Quantization table</param> /// <param name="quant">Quantization table</param>
/// <param name="unZig">The 8x8 Unzig block.</param> /// <param name="unZig">The 8x8 Unzig block.</param>
/// <returns> /// <param name="emitBufferBase">The reference to the emit buffer.</param>
/// The <see cref="int"/> /// <returns>The <see cref="int"/>.</returns>
/// </returns>
private int WriteBlock( private int WriteBlock(
QuantIndex index, QuantIndex index,
int prevDC, int prevDC,
@ -527,7 +538,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
ref Block8x8F tempDest1, ref Block8x8F tempDest1,
ref Block8x8F tempDest2, ref Block8x8F tempDest2,
ref Block8x8F quant, ref Block8x8F quant,
ref ZigZag unZig) ref ZigZag unZig,
ref byte emitBufferBase)
{ {
FastFloatingPointDCT.TransformFDCT(ref src, ref tempDest1, ref tempDest2); FastFloatingPointDCT.TransformFDCT(ref src, ref tempDest1, ref tempDest2);
@ -536,7 +548,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
int dc = (int)tempDest2[0]; int dc = (int)tempDest2[0];
// Emit the DC delta. // 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. // Emit the AC components.
var h = (HuffIndex)((2 * (int)index) + 1); var h = (HuffIndex)((2 * (int)index) + 1);
@ -554,18 +566,18 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
{ {
while (runLength > 15) while (runLength > 15)
{ {
this.EmitHuff(h, 0xf0); this.EmitHuff(h, 0xf0, ref emitBufferBase);
runLength -= 16; runLength -= 16;
} }
this.EmitHuffRLE(h, runLength, ac); this.EmitHuffRLE(h, runLength, ac, ref emitBufferBase);
runLength = 0; runLength = 0;
} }
} }
if (runLength > 0) if (runLength > 0)
{ {
this.EmitHuff(h, 0x00); this.EmitHuff(h, 0x00, ref emitBufferBase);
} }
return dc; return dc;
@ -747,9 +759,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// </summary> /// </summary>
/// <param name="app1Length">The length of the data the app1 marker contains.</param> /// <param name="app1Length">The length of the data the app1 marker contains.</param>
private void WriteApp1Header(int app1Length) private void WriteApp1Header(int app1Length)
{ => this.WriteAppHeader(app1Length, JpegConstants.Markers.APP1);
this.WriteAppHeader(app1Length, JpegConstants.Markers.APP1);
}
/// <summary> /// <summary>
/// Writes a AppX header. /// Writes a AppX header.
@ -953,19 +963,19 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
// TODO: Need a JpegScanEncoder<TPixel> class or struct that encapsulates the scan-encoding implementation. (Similar to JpegScanDecoder.) // TODO: Need a JpegScanEncoder<TPixel> class or struct that encapsulates the scan-encoding implementation. (Similar to JpegScanDecoder.)
// TODO: We should allow grayscale writing. // TODO: We should allow grayscale writing.
this.outputStream.Write(SosHeaderYCbCr); this.outputStream.Write(SosHeaderYCbCr);
ref byte emitBufferBase = ref MemoryMarshal.GetReference<byte>(this.emitBuffer);
switch (this.subsample) switch (this.subsample)
{ {
case JpegSubsample.Ratio444: case JpegSubsample.Ratio444:
this.Encode444(image, cancellationToken); this.Encode444(image, cancellationToken, ref emitBufferBase);
break; break;
case JpegSubsample.Ratio420: case JpegSubsample.Ratio420:
this.Encode420(image, cancellationToken); this.Encode420(image, cancellationToken, ref emitBufferBase);
break; break;
} }
// Pad the last byte with 1's. // Pad the last byte with 1's.
this.Emit(0x7f, 7); this.Emit(0x7f, 7, ref emitBufferBase);
} }
/// <summary> /// <summary>
@ -975,7 +985,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="pixels">The pixel accessor providing access to the image pixels.</param> /// <param name="pixels">The pixel accessor providing access to the image pixels.</param>
/// <param name="cancellationToken">The token to monitor for cancellation.</param> /// <param name="cancellationToken">The token to monitor for cancellation.</param>
private void Encode420<TPixel>(Image<TPixel> pixels, CancellationToken cancellationToken) /// <param name="emitBufferBase">The reference to the emit buffer.</param>
private void Encode420<TPixel>(Image<TPixel> pixels, CancellationToken cancellationToken, ref byte emitBufferBase)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
// TODO: Need a JpegScanEncoder<TPixel> class or struct that encapsulates the scan-encoding implementation. (Similar to JpegScanDecoder.) // TODO: Need a JpegScanEncoder<TPixel> class or struct that encapsulates the scan-encoding implementation. (Similar to JpegScanDecoder.)
@ -997,6 +1008,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
int prevDCY = 0, prevDCCb = 0, prevDCCr = 0; int prevDCY = 0, prevDCCb = 0, prevDCCr = 0;
ImageFrame<TPixel> frame = pixels.Frames.RootFrame; ImageFrame<TPixel> frame = pixels.Frames.RootFrame;
Buffer2D<TPixel> pixelBuffer = frame.PixelBuffer; Buffer2D<TPixel> pixelBuffer = frame.PixelBuffer;
RowOctet<TPixel> currentRows = default;
for (int y = 0; y < pixels.Height; y += 16) for (int y = 0; y < pixels.Height; y += 16)
{ {
@ -1008,10 +1020,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
int xOff = (i & 1) * 8; int xOff = (i & 1) * 8;
int yOff = (i & 2) * 4; int yOff = (i & 2) * 4;
// TODO: Try pushing this to the outer loop! currentRows.Update(pixelBuffer, y + yOff);
var currentRows = new RowOctet<TPixel>(pixelBuffer, y + yOff); pixelConverter.Convert(frame, x + xOff, y + yOff, ref currentRows);
pixelConverter.Convert(frame, x + xOff, y + yOff, currentRows);
cb[i] = pixelConverter.Cb; cb[i] = pixelConverter.Cb;
cr[i] = pixelConverter.Cr; cr[i] = pixelConverter.Cr;
@ -1023,7 +1033,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
ref temp1, ref temp1,
ref temp2, ref temp2,
ref onStackLuminanceQuantTable, ref onStackLuminanceQuantTable,
ref unzig); ref unzig,
ref emitBufferBase);
} }
Block8x8F.Scale16X16To8X8(ref b, cb); Block8x8F.Scale16X16To8X8(ref b, cb);
@ -1034,7 +1045,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
ref temp1, ref temp1,
ref temp2, ref temp2,
ref onStackChrominanceQuantTable, ref onStackChrominanceQuantTable,
ref unzig); ref unzig,
ref emitBufferBase);
Block8x8F.Scale16X16To8X8(ref b, cr); Block8x8F.Scale16X16To8X8(ref b, cr);
prevDCCr = this.WriteBlock( prevDCCr = this.WriteBlock(
@ -1044,7 +1056,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
ref temp1, ref temp1,
ref temp2, ref temp2,
ref onStackChrominanceQuantTable, ref onStackChrominanceQuantTable,
ref unzig); ref unzig,
ref emitBufferBase);
} }
} }
} }

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) public Vector4 ConvolveCore(ref Vector4 rowStartRef)
{ {
#if SUPPORTS_RUNTIME_INTRINSICS #if SUPPORTS_RUNTIME_INTRINSICS
if (Fma.IsSupported) if (Avx2.IsSupported && Fma.IsSupported)
{ {
float* bufferStart = this.bufferPtr; float* bufferStart = this.bufferPtr;
float* bufferEnd = bufferStart + (this.Length & ~3); float* bufferEnd = bufferStart + (this.Length & ~3);

5
tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Scale16X16To8X8.cs

@ -1,3 +1,6 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System; using System;
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
using SixLabors.ImageSharp.Formats.Jpeg.Components; using SixLabors.ImageSharp.Formats.Jpeg.Components;
@ -15,7 +18,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Format.Jpeg.Components
{ {
var random = new Random(); var random = new Random();
float[] f = new float[8*8]; float[] f = new float[8 * 8];
for (int i = 0; i < f.Length; i++) for (int i = 0; i < f.Length; i++)
{ {
f[i] = (float)random.NextDouble(); f[i] = (float)random.NextDouble();

13
tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpeg.cs

@ -16,17 +16,20 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
private Stream bmpStream; private Stream bmpStream;
private SDImage bmpDrawing; private SDImage bmpDrawing;
private Image<Rgba32> bmpCore; private Image<Rgba32> bmpCore;
private MemoryStream destinationStream;
[GlobalSetup] [GlobalSetup]
public void ReadImages() public void ReadImages()
{ {
if (this.bmpStream == null) if (this.bmpStream == null)
{ {
const string TestImage = TestImages.Bmp.Car; const string TestImage = TestImages.Jpeg.BenchmarkSuite.Jpeg420Exif_MidSizeYCbCr;
this.bmpStream = File.OpenRead(Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, TestImage)); this.bmpStream = File.OpenRead(Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, TestImage));
this.bmpCore = Image.Load<Rgba32>(this.bmpStream); this.bmpCore = Image.Load<Rgba32>(this.bmpStream);
this.bmpCore.Metadata.ExifProfile = null;
this.bmpStream.Position = 0; this.bmpStream.Position = 0;
this.bmpDrawing = SDImage.FromStream(this.bmpStream); 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")] [Benchmark(Baseline = true, Description = "System.Drawing Jpeg")]
public void JpegSystemDrawing() public void JpegSystemDrawing()
{ {
using var stream = new MemoryStream(); this.bmpDrawing.Save(this.destinationStream, ImageFormat.Jpeg);
this.bmpDrawing.Save(stream, ImageFormat.Jpeg); this.destinationStream.Seek(0, SeekOrigin.Begin);
} }
[Benchmark(Description = "ImageSharp Jpeg")] [Benchmark(Description = "ImageSharp Jpeg")]
public void JpegCore() public void JpegCore()
{ {
using var stream = new MemoryStream(); this.bmpCore.SaveAsJpeg(this.destinationStream);
this.bmpCore.SaveAsJpeg(stream); this.destinationStream.Seek(0, SeekOrigin.Begin);
} }
} }
} }

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

@ -31,14 +31,21 @@ namespace SixLabors.ImageSharp.Tests.ProfilingSandbox
/// </param> /// </param>
public static void Main(string[] args) public static void Main(string[] args)
{ {
RunJpegEncoderProfilingTests();
// RunJpegColorProfilingTests(); // RunJpegColorProfilingTests();
RunDecodeJpegProfilingTests(); // RunDecodeJpegProfilingTests();
// RunToVector4ProfilingTest(); // RunToVector4ProfilingTest();
// RunResizeProfilingTest(); // RunResizeProfilingTest();
Console.ReadLine(); Console.ReadLine();
} }
private static void RunJpegEncoderProfilingTests()
{
var benchmarks = new JpegProfilingBenchmarks(new ConsoleOutput());
benchmarks.EncodeJpeg_SingleMidSize();
}
private static void RunJpegColorProfilingTests() private static void RunJpegColorProfilingTests()
{ {
new JpegColorConverterTests(new ConsoleOutput()).BenchmarkYCbCr(false); new JpegColorConverterTests(new ConsoleOutput()).BenchmarkYCbCr(false);

12
tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs

@ -171,6 +171,17 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif
} }
} }
// https://github.com/SixLabors/ImageSharp/issues/1503
[Theory]
[WithFile(TestImages.Gif.Issues.Issue1530, PixelTypes.Rgba32)]
public void Issue1530_BadDescriptorDimensions<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage();
image.DebugSaveMultiFrame(provider);
image.CompareToReferenceOutputMultiFrame(provider, ImageComparer.Exact);
}
// https://github.com/SixLabors/ImageSharp/issues/405 // https://github.com/SixLabors/ImageSharp/issues/405
[Theory] [Theory]
[WithFile(TestImages.Gif.Issues.BadAppExtLength, PixelTypes.Rgba32)] [WithFile(TestImages.Gif.Issues.BadAppExtLength, PixelTypes.Rgba32)]
@ -181,6 +192,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif
using (Image<TPixel> image = provider.GetImage()) using (Image<TPixel> image = provider.GetImage())
{ {
image.DebugSave(provider); image.DebugSave(provider);
image.CompareFirstFrameToReferenceOutput(ImageComparer.Exact, provider); image.CompareFirstFrameToReferenceOutput(ImageComparer.Exact, provider);
} }
} }

11
tests/ImageSharp.Tests/Formats/Jpg/GenericBlock8x8Tests.cs

@ -42,8 +42,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
using (Image<TPixel> s = provider.GetImage()) using (Image<TPixel> s = provider.GetImage())
{ {
var d = default(GenericBlock8x8<TPixel>); var d = default(GenericBlock8x8<TPixel>);
var rowOctet = new RowOctet<TPixel>(s.GetRootFramePixelBuffer(), 0); RowOctet<TPixel> rowOctet = default;
d.LoadAndStretchEdges(s.Frames.RootFrame.PixelBuffer, 0, 0, rowOctet); rowOctet.Update(s.GetRootFramePixelBuffer(), 0);
d.LoadAndStretchEdges(s.Frames.RootFrame.PixelBuffer, 0, 0, ref rowOctet);
TPixel a = s.Frames.RootFrame[0, 0]; TPixel a = s.Frames.RootFrame[0, 0];
TPixel b = d[0, 0]; TPixel b = d[0, 0];
@ -67,8 +68,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
using (Image<TPixel> s = provider.GetImage()) using (Image<TPixel> s = provider.GetImage())
{ {
var d = default(GenericBlock8x8<TPixel>); var d = default(GenericBlock8x8<TPixel>);
var rowOctet = new RowOctet<TPixel>(s.GetRootFramePixelBuffer(), 7); RowOctet<TPixel> rowOctet = default;
d.LoadAndStretchEdges(s.Frames.RootFrame.PixelBuffer, 6, 7, rowOctet); rowOctet.Update(s.GetRootFramePixelBuffer(), 7);
d.LoadAndStretchEdges(s.Frames.RootFrame.PixelBuffer, 6, 7, ref rowOctet);
Assert.Equal(s[6, 7], d[0, 0]); Assert.Equal(s[6, 7], d[0, 0]);
Assert.Equal(s[6, 8], d[0, 1]); Assert.Equal(s[6, 8], d[0, 1]);

2
tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs

@ -375,7 +375,7 @@ namespace SixLabors.ImageSharp.Tests
var array = new Rgba32[size]; var array = new Rgba32[size];
var memory = new Memory<Rgba32>(array); var memory = new Memory<Rgba32>(array);
Image.WrapMemory(memory, height, width); Image.WrapMemory(memory, height, width);
} }
private class TestMemoryOwner<T> : IMemoryOwner<T> private class TestMemoryOwner<T> : IMemoryOwner<T>

21
tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs

@ -1,8 +1,10 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors.Transforms; using SixLabors.ImageSharp.Processing.Processors.Transforms;
using SixLabors.ImageSharp.Tests.TestUtilities;
using Xunit; using Xunit;
namespace SixLabors.ImageSharp.Tests.Processing.Transforms namespace SixLabors.ImageSharp.Tests.Processing.Transforms
@ -85,5 +87,24 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
Assert.Equal(compand, resizeOptions.Compand); Assert.Equal(compand, resizeOptions.Compand);
Assert.Equal(mode, resizeOptions.Mode); 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 #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! // Benchmark, enable manually!
[Theory(Skip = ProfilingSetup.SkipProfilingTests)] [Theory(Skip = ProfilingSetup.SkipProfilingTests)]
[InlineData(1, 75, JpegSubsample.Ratio420)] [InlineData(1, 75, JpegSubsample.Ratio420)]

1
tests/ImageSharp.Tests/TestImages.cs

@ -416,6 +416,7 @@ namespace SixLabors.ImageSharp.Tests
public const string BadAppExtLength_2 = "Gif/issues/issue405_badappextlength252-2.gif"; public const string BadAppExtLength_2 = "Gif/issues/issue405_badappextlength252-2.gif";
public const string BadDescriptorWidth = "Gif/issues/issue403_baddescriptorwidth.gif"; public const string BadDescriptorWidth = "Gif/issues/issue403_baddescriptorwidth.gif";
public const string Issue1505 = "Gif/issues/issue1505_argumentoutofrange.png"; public const string Issue1505 = "Gif/issues/issue1505_argumentoutofrange.png";
public const string Issue1530 = "Gif/issues/issue1530.gif";
} }
public static readonly string[] All = { Rings, Giphy, Cheers, Trans, Kumin, Leo, Ratio4x1, Ratio1x4 }; public static readonly string[] All = { Rings, Giphy, Cheers, Trans, Kumin, Leo, Ratio4x1, Ratio1x4 };

2
tests/ImageSharp.Tests/TestUtilities/TestEnvironment.cs

@ -263,7 +263,7 @@ namespace SixLabors.ImageSharp.Tests
private static string GetNetCoreVersion() private static string GetNetCoreVersion()
{ {
Assembly assembly = typeof(System.Runtime.GCSettings).GetTypeInfo().Assembly; Assembly assembly = typeof(System.Runtime.GCSettings).GetTypeInfo().Assembly;
string[] assemblyPath = assembly.CodeBase.Split(new[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries); string[] assemblyPath = assembly.Location.Split(new[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries);
int netCoreAppIndex = Array.IndexOf(assemblyPath, "Microsoft.NETCore.App"); int netCoreAppIndex = Array.IndexOf(assemblyPath, "Microsoft.NETCore.App");
if (netCoreAppIndex > 0 && netCoreAppIndex < assemblyPath.Length - 2) if (netCoreAppIndex > 0 && netCoreAppIndex < assemblyPath.Length - 2)
{ {

6
tests/ImageSharp.Tests/TestUtilities/Tests/FeatureTestRunnerTests.cs

@ -25,9 +25,9 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.Tests
[Theory] [Theory]
[MemberData(nameof(Intrinsics))] [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[] keys = features.Keys.ToArray();
HwIntrinsics actualIntrinsics = keys[0]; HwIntrinsics actualIntrinsics = keys[0];
@ -36,7 +36,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.Tests
actualIntrinsics |= keys[i]; actualIntrinsics |= keys[i];
} }
Assert.Equal(expectedItrinsics, actualIntrinsics); Assert.Equal(expectedIntrinsics, actualIntrinsics);
IEnumerable<string> actualValues = features.Select(x => x.Value); IEnumerable<string> actualValues = features.Select(x => x.Value);
Assert.Equal(expectedValues, actualValues); Assert.Equal(expectedValues, actualValues);

2
tests/Images/External

@ -1 +1 @@
Subproject commit 346070e5ba538f1a3bbafc0ea7367404c5f8c9ab Subproject commit 1df65162448996332449387a46da942e181043c8

3
tests/Images/Input/Gif/issues/issue1530.gif

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:08b33cab34de2622a7d22edff25fe479a5018c2e0fbc65870b2f21ea7901fc61
size 1844876
Loading…
Cancel
Save