mirror of https://github.com/SixLabors/ImageSharp
47 changed files with 3487 additions and 1835 deletions
@ -1,6 +1,6 @@ |
|||
{ |
|||
"projects": [ "src" ], |
|||
"sdk": { |
|||
"version": "1.0.0-preview2-003121" |
|||
"version": "1.0.0-preview2-003131" |
|||
} |
|||
} |
|||
@ -1,272 +0,0 @@ |
|||
// <copyright file="Block.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp.Formats |
|||
{ |
|||
using System; |
|||
using System.Buffers; |
|||
using System.Runtime.CompilerServices; |
|||
|
|||
/// <summary>
|
|||
/// Represents an 8x8 block of coefficients to transform and encode.
|
|||
/// </summary>
|
|||
internal struct Block : IDisposable |
|||
{ |
|||
/// <summary>
|
|||
/// Gets the size of the block.
|
|||
/// </summary>
|
|||
public const int BlockSize = 64; |
|||
|
|||
/// <summary>
|
|||
/// Gets the array of block data.
|
|||
/// </summary>
|
|||
public int[] Data; |
|||
|
|||
/// <summary>
|
|||
/// A pool of reusable buffers.
|
|||
/// </summary>
|
|||
private static readonly ArrayPool<int> ArrayPool = ArrayPool<int>.Create(BlockSize, 50); |
|||
|
|||
/// <summary>
|
|||
/// Gets a value indicating whether the block is initialized
|
|||
/// </summary>
|
|||
public bool IsInitialized => this.Data != null; |
|||
|
|||
/// <summary>
|
|||
/// Gets the pixel data at the given block index.
|
|||
/// </summary>
|
|||
/// <param name="index">The index of the data to return.</param>
|
|||
/// <returns>
|
|||
/// The <see cref="int"/>.
|
|||
/// </returns>
|
|||
public int this[int index] |
|||
{ |
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
get |
|||
{ |
|||
return this.Data[index]; |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
set |
|||
{ |
|||
this.Data[index] = value; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Creates a new block
|
|||
/// </summary>
|
|||
/// <returns>The <see cref="Block"/></returns>
|
|||
public static Block Create() |
|||
{ |
|||
Block block = default(Block); |
|||
block.Init(); |
|||
return block; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns an array of blocks of the given length.
|
|||
/// </summary>
|
|||
/// <param name="count">The number to create.</param>
|
|||
/// <returns>The <see cref="T:Block[]"/></returns>
|
|||
public static Block[] CreateArray(int count) |
|||
{ |
|||
Block[] result = new Block[count]; |
|||
for (int i = 0; i < result.Length; i++) |
|||
{ |
|||
result[i].Init(); |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Disposes of the collection of blocks
|
|||
/// </summary>
|
|||
/// <param name="blocks">The blocks.</param>
|
|||
public static void DisposeAll(Block[] blocks) |
|||
{ |
|||
for (int i = 0; i < blocks.Length; i++) |
|||
{ |
|||
blocks[i].Dispose(); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes the new block.
|
|||
/// </summary>
|
|||
public void Init() |
|||
{ |
|||
this.Data = ArrayPool.Rent(BlockSize); |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
public void Dispose() |
|||
{ |
|||
// TODO: Refactor Block.Dispose() callers to always use 'using' or 'finally' statement!
|
|||
if (this.Data != null) |
|||
{ |
|||
ArrayPool.Return(this.Data, true); |
|||
this.Data = null; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Clears the block data
|
|||
/// </summary>
|
|||
public void Clear() |
|||
{ |
|||
for (int i = 0; i < this.Data.Length; i++) |
|||
{ |
|||
this.Data[i] = 0; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Clones the current block
|
|||
/// </summary>
|
|||
/// <returns>The <see cref="Block"/></returns>
|
|||
public Block Clone() |
|||
{ |
|||
Block clone = Create(); |
|||
Array.Copy(this.Data, clone.Data, BlockSize); |
|||
return clone; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// TODO: Should be removed, when JpegEncoderCore is refactored to use Block8x8F
|
|||
/// Temporal class to make refactoring easier.
|
|||
/// 1. Refactor Block -> BlockF
|
|||
/// 2. Test
|
|||
/// 3. Refactor BlockF -> Block8x8F
|
|||
/// </summary>
|
|||
internal struct BlockF : IDisposable |
|||
{ |
|||
/// <summary>
|
|||
/// Size of the block.
|
|||
/// </summary>
|
|||
public const int BlockSize = 64; |
|||
|
|||
/// <summary>
|
|||
/// The array of block data.
|
|||
/// </summary>
|
|||
public float[] Data; |
|||
|
|||
/// <summary>
|
|||
/// A pool of reusable buffers.
|
|||
/// </summary>
|
|||
private static readonly ArrayPool<float> ArrayPool = ArrayPool<float>.Create(BlockSize, 50); |
|||
|
|||
/// <summary>
|
|||
/// Gets a value indicating whether the block is initialized
|
|||
/// </summary>
|
|||
public bool IsInitialized => this.Data != null; |
|||
|
|||
/// <summary>
|
|||
/// Gets the pixel data at the given block index.
|
|||
/// </summary>
|
|||
/// <param name="index">The index of the data to return.</param>
|
|||
/// <returns>
|
|||
/// The <see cref="int"/>.
|
|||
/// </returns>
|
|||
public float this[int index] |
|||
{ |
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
get |
|||
{ |
|||
return this.Data[index]; |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
set |
|||
{ |
|||
this.Data[index] = value; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Creates a new block
|
|||
/// </summary>
|
|||
/// <returns>The <see cref="BlockF"/></returns>
|
|||
public static BlockF Create() |
|||
{ |
|||
var block = default(BlockF); |
|||
block.Init(); |
|||
return block; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns an array of blocks of the given length.
|
|||
/// </summary>
|
|||
/// <param name="count">The number to create.</param>
|
|||
/// <returns>The <see cref="T:BlockF[]"/></returns>
|
|||
public static BlockF[] CreateArray(int count) |
|||
{ |
|||
BlockF[] result = new BlockF[count]; |
|||
for (int i = 0; i < result.Length; i++) |
|||
{ |
|||
result[i].Init(); |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Disposes of the collection of blocks
|
|||
/// </summary>
|
|||
/// <param name="blocks">The blocks.</param>
|
|||
public static void DisposeAll(BlockF[] blocks) |
|||
{ |
|||
for (int i = 0; i < blocks.Length; i++) |
|||
{ |
|||
blocks[i].Dispose(); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Clears the block data
|
|||
/// </summary>
|
|||
public void Clear() |
|||
{ |
|||
for (int i = 0; i < this.Data.Length; i++) |
|||
{ |
|||
this.Data[i] = 0; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Clones the current block
|
|||
/// </summary>
|
|||
/// <returns>The <see cref="Block"/></returns>
|
|||
public BlockF Clone() |
|||
{ |
|||
BlockF clone = Create(); |
|||
Array.Copy(this.Data, clone.Data, BlockSize); |
|||
return clone; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes the new block.
|
|||
/// </summary>
|
|||
public void Init() |
|||
{ |
|||
// this.Data = new int[BlockSize];
|
|||
this.Data = ArrayPool.Rent(BlockSize); |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
public void Dispose() |
|||
{ |
|||
// TODO: Refactor Block.Dispose() callers to always use 'using' or 'finally' statement!
|
|||
if (this.Data != null) |
|||
{ |
|||
ArrayPool.Return(this.Data, true); |
|||
this.Data = null; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,340 @@ |
|||
// <copyright file="DCT.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
// ReSharper disable InconsistentNaming
|
|||
|
|||
namespace ImageSharp.Formats |
|||
{ |
|||
using System.Numerics; |
|||
using System.Runtime.CompilerServices; |
|||
|
|||
/// <summary>
|
|||
/// Contains forward and inverse DCT implementations
|
|||
/// </summary>
|
|||
internal static class DCT |
|||
{ |
|||
#pragma warning disable SA1310 // FieldNamesMustNotContainUnderscore
|
|||
private static readonly Vector4 C_1_175876 = new Vector4(1.175876f); |
|||
|
|||
private static readonly Vector4 C_1_961571 = new Vector4(-1.961571f); |
|||
|
|||
private static readonly Vector4 C_0_390181 = new Vector4(-0.390181f); |
|||
|
|||
private static readonly Vector4 C_0_899976 = new Vector4(-0.899976f); |
|||
|
|||
private static readonly Vector4 C_2_562915 = new Vector4(-2.562915f); |
|||
|
|||
private static readonly Vector4 C_0_298631 = new Vector4(0.298631f); |
|||
|
|||
private static readonly Vector4 C_2_053120 = new Vector4(2.053120f); |
|||
|
|||
private static readonly Vector4 C_3_072711 = new Vector4(3.072711f); |
|||
|
|||
private static readonly Vector4 C_1_501321 = new Vector4(1.501321f); |
|||
|
|||
private static readonly Vector4 C_0_541196 = new Vector4(0.541196f); |
|||
|
|||
private static readonly Vector4 C_1_847759 = new Vector4(-1.847759f); |
|||
|
|||
private static readonly Vector4 C_0_765367 = new Vector4(0.765367f); |
|||
|
|||
private static readonly Vector4 C_0_125 = new Vector4(0.1250f); |
|||
#pragma warning restore SA1310 // FieldNamesMustNotContainUnderscore
|
|||
private static readonly Vector4 InvSqrt2 = new Vector4(0.707107f); |
|||
|
|||
/// <summary>
|
|||
/// Apply floating point IDCT transformation into dest, using a temporary block 'temp' provided by the caller (optimization)
|
|||
/// </summary>
|
|||
/// <param name="src">Source</param>
|
|||
/// <param name="dest">Destination</param>
|
|||
/// <param name="temp">Temporary block provided by the caller</param>
|
|||
public static void TransformIDCT(ref Block8x8F src, ref Block8x8F dest, ref Block8x8F temp) |
|||
{ |
|||
src.TransposeInto(ref temp); |
|||
IDCT8x4_LeftPart(ref temp, ref dest); |
|||
IDCT8x4_RightPart(ref temp, ref dest); |
|||
|
|||
dest.TransposeInto(ref temp); |
|||
|
|||
IDCT8x4_LeftPart(ref temp, ref dest); |
|||
IDCT8x4_RightPart(ref temp, ref dest); |
|||
|
|||
dest.MultiplyAllInplace(C_0_125); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Do IDCT internal operations on the left part of the block. Original src:
|
|||
/// https://github.com/norishigefukushima/dct_simd/blob/master/dct/dct8x8_simd.cpp#L261
|
|||
/// </summary>
|
|||
/// <param name="s">The source block</param>
|
|||
/// <param name="d">Destination block</param>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static void IDCT8x4_LeftPart(ref Block8x8F s, ref Block8x8F d) |
|||
{ |
|||
Vector4 my1 = s.V1L; |
|||
Vector4 my7 = s.V7L; |
|||
Vector4 mz0 = my1 + my7; |
|||
|
|||
Vector4 my3 = s.V3L; |
|||
Vector4 mz2 = my3 + my7; |
|||
Vector4 my5 = s.V5L; |
|||
Vector4 mz1 = my3 + my5; |
|||
Vector4 mz3 = my1 + my5; |
|||
|
|||
Vector4 mz4 = (mz0 + mz1) * C_1_175876; |
|||
|
|||
mz2 = (mz2 * C_1_961571) + mz4; |
|||
mz3 = (mz3 * C_0_390181) + mz4; |
|||
mz0 = mz0 * C_0_899976; |
|||
mz1 = mz1 * C_2_562915; |
|||
|
|||
Vector4 mb3 = (my7 * C_0_298631) + mz0 + mz2; |
|||
Vector4 mb2 = (my5 * C_2_053120) + mz1 + mz3; |
|||
Vector4 mb1 = (my3 * C_3_072711) + mz1 + mz2; |
|||
Vector4 mb0 = (my1 * C_1_501321) + mz0 + mz3; |
|||
|
|||
Vector4 my2 = s.V2L; |
|||
Vector4 my6 = s.V6L; |
|||
mz4 = (my2 + my6) * C_0_541196; |
|||
Vector4 my0 = s.V0L; |
|||
Vector4 my4 = s.V4L; |
|||
mz0 = my0 + my4; |
|||
mz1 = my0 - my4; |
|||
|
|||
mz2 = mz4 + (my6 * C_1_847759); |
|||
mz3 = mz4 + (my2 * C_0_765367); |
|||
|
|||
my0 = mz0 + mz3; |
|||
my3 = mz0 - mz3; |
|||
my1 = mz1 + mz2; |
|||
my2 = mz1 - mz2; |
|||
|
|||
d.V0L = my0 + mb0; |
|||
d.V7L = my0 - mb0; |
|||
d.V1L = my1 + mb1; |
|||
d.V6L = my1 - mb1; |
|||
d.V2L = my2 + mb2; |
|||
d.V5L = my2 - mb2; |
|||
d.V3L = my3 + mb3; |
|||
d.V4L = my3 - mb3; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Do IDCT internal operations on the right part of the block.
|
|||
/// Original src:
|
|||
/// https://github.com/norishigefukushima/dct_simd/blob/master/dct/dct8x8_simd.cpp#L261
|
|||
/// </summary>
|
|||
/// <param name="s">The source block</param>
|
|||
/// <param name="d">The destination block</param>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static void IDCT8x4_RightPart(ref Block8x8F s, ref Block8x8F d) |
|||
{ |
|||
Vector4 my1 = s.V1R; |
|||
Vector4 my7 = s.V7R; |
|||
Vector4 mz0 = my1 + my7; |
|||
|
|||
Vector4 my3 = s.V3R; |
|||
Vector4 mz2 = my3 + my7; |
|||
Vector4 my5 = s.V5R; |
|||
Vector4 mz1 = my3 + my5; |
|||
Vector4 mz3 = my1 + my5; |
|||
|
|||
Vector4 mz4 = (mz0 + mz1) * C_1_175876; |
|||
|
|||
mz2 = (mz2 * C_1_961571) + mz4; |
|||
mz3 = (mz3 * C_0_390181) + mz4; |
|||
mz0 = mz0 * C_0_899976; |
|||
mz1 = mz1 * C_2_562915; |
|||
|
|||
Vector4 mb3 = (my7 * C_0_298631) + mz0 + mz2; |
|||
Vector4 mb2 = (my5 * C_2_053120) + mz1 + mz3; |
|||
Vector4 mb1 = (my3 * C_3_072711) + mz1 + mz2; |
|||
Vector4 mb0 = (my1 * C_1_501321) + mz0 + mz3; |
|||
|
|||
Vector4 my2 = s.V2R; |
|||
Vector4 my6 = s.V6R; |
|||
mz4 = (my2 + my6) * C_0_541196; |
|||
Vector4 my0 = s.V0R; |
|||
Vector4 my4 = s.V4R; |
|||
mz0 = my0 + my4; |
|||
mz1 = my0 - my4; |
|||
|
|||
mz2 = mz4 + (my6 * C_1_847759); |
|||
mz3 = mz4 + (my2 * C_0_765367); |
|||
|
|||
my0 = mz0 + mz3; |
|||
my3 = mz0 - mz3; |
|||
my1 = mz1 + mz2; |
|||
my2 = mz1 - mz2; |
|||
|
|||
d.V0R = my0 + mb0; |
|||
d.V7R = my0 - mb0; |
|||
d.V1R = my1 + mb1; |
|||
d.V6R = my1 - mb1; |
|||
d.V2R = my2 + mb2; |
|||
d.V5R = my2 - mb2; |
|||
d.V3R = my3 + mb3; |
|||
d.V4R = my3 - mb3; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Original:
|
|||
/// <see>
|
|||
/// <cref>https://github.com/norishigefukushima/dct_simd/blob/master/dct/dct8x8_simd.cpp#L15</cref>
|
|||
/// </see>
|
|||
/// </summary>
|
|||
/// <param name="s">Source</param>
|
|||
/// <param name="d">Destination</param>
|
|||
public static void FDCT8x4_LeftPart(ref Block8x8F s, ref Block8x8F d) |
|||
{ |
|||
Vector4 c0 = s.V0L; |
|||
Vector4 c1 = s.V7L; |
|||
Vector4 t0 = c0 + c1; |
|||
Vector4 t7 = c0 - c1; |
|||
|
|||
c1 = s.V6L; |
|||
c0 = s.V1L; |
|||
Vector4 t1 = c0 + c1; |
|||
Vector4 t6 = c0 - c1; |
|||
|
|||
c1 = s.V5L; |
|||
c0 = s.V2L; |
|||
Vector4 t2 = c0 + c1; |
|||
Vector4 t5 = c0 - c1; |
|||
|
|||
c0 = s.V3L; |
|||
c1 = s.V4L; |
|||
Vector4 t3 = c0 + c1; |
|||
Vector4 t4 = c0 - c1; |
|||
|
|||
c0 = t0 + t3; |
|||
Vector4 c3 = t0 - t3; |
|||
c1 = t1 + t2; |
|||
Vector4 c2 = t1 - t2; |
|||
|
|||
d.V0L = c0 + c1; |
|||
d.V4L = c0 - c1; |
|||
|
|||
Vector4 w0 = new Vector4(0.541196f); |
|||
Vector4 w1 = new Vector4(1.306563f); |
|||
|
|||
d.V2L = (w0 * c2) + (w1 * c3); |
|||
d.V6L = (w0 * c3) - (w1 * c2); |
|||
|
|||
w0 = new Vector4(1.175876f); |
|||
w1 = new Vector4(0.785695f); |
|||
c3 = (w0 * t4) + (w1 * t7); |
|||
c0 = (w0 * t7) - (w1 * t4); |
|||
|
|||
w0 = new Vector4(1.387040f); |
|||
w1 = new Vector4(0.275899f); |
|||
c2 = (w0 * t5) + (w1 * t6); |
|||
c1 = (w0 * t6) - (w1 * t5); |
|||
|
|||
d.V3L = c0 - c2; |
|||
d.V5L = c3 - c1; |
|||
|
|||
Vector4 invsqrt2 = new Vector4(0.707107f); |
|||
c0 = (c0 + c2) * invsqrt2; |
|||
c3 = (c3 + c1) * invsqrt2; |
|||
|
|||
d.V1L = c0 + c3; |
|||
d.V7L = c0 - c3; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Original:
|
|||
/// <see>
|
|||
/// <cref>https://github.com/norishigefukushima/dct_simd/blob/master/dct/dct8x8_simd.cpp#L15</cref>
|
|||
/// </see>
|
|||
/// </summary>
|
|||
/// <param name="s">Source</param>
|
|||
/// <param name="d">Destination</param>
|
|||
public static void FDCT8x4_RightPart(ref Block8x8F s, ref Block8x8F d) |
|||
{ |
|||
Vector4 c0 = s.V0R; |
|||
Vector4 c1 = s.V7R; |
|||
Vector4 t0 = c0 + c1; |
|||
Vector4 t7 = c0 - c1; |
|||
|
|||
c1 = s.V6R; |
|||
c0 = s.V1R; |
|||
Vector4 t1 = c0 + c1; |
|||
Vector4 t6 = c0 - c1; |
|||
|
|||
c1 = s.V5R; |
|||
c0 = s.V2R; |
|||
Vector4 t2 = c0 + c1; |
|||
Vector4 t5 = c0 - c1; |
|||
|
|||
c0 = s.V3R; |
|||
c1 = s.V4R; |
|||
Vector4 t3 = c0 + c1; |
|||
Vector4 t4 = c0 - c1; |
|||
|
|||
c0 = t0 + t3; |
|||
Vector4 c3 = t0 - t3; |
|||
c1 = t1 + t2; |
|||
Vector4 c2 = t1 - t2; |
|||
|
|||
d.V0R = c0 + c1; |
|||
d.V4R = c0 - c1; |
|||
|
|||
Vector4 w0 = new Vector4(0.541196f); |
|||
Vector4 w1 = new Vector4(1.306563f); |
|||
|
|||
d.V2R = (w0 * c2) + (w1 * c3); |
|||
d.V6R = (w0 * c3) - (w1 * c2); |
|||
|
|||
w0 = new Vector4(1.175876f); |
|||
w1 = new Vector4(0.785695f); |
|||
c3 = (w0 * t4) + (w1 * t7); |
|||
c0 = (w0 * t7) - (w1 * t4); |
|||
|
|||
w0 = new Vector4(1.387040f); |
|||
w1 = new Vector4(0.275899f); |
|||
c2 = (w0 * t5) + (w1 * t6); |
|||
c1 = (w0 * t6) - (w1 * t5); |
|||
|
|||
d.V3R = c0 - c2; |
|||
d.V5R = c3 - c1; |
|||
|
|||
c0 = (c0 + c2) * InvSqrt2; |
|||
c3 = (c3 + c1) * InvSqrt2; |
|||
|
|||
d.V1R = c0 + c3; |
|||
d.V7R = c0 - c3; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Apply floating point IDCT transformation into dest, using a temporary block 'temp' provided by the caller (optimization)
|
|||
/// </summary>
|
|||
/// <param name="src">Source</param>
|
|||
/// <param name="dest">Destination</param>
|
|||
/// <param name="temp">Temporary block provided by the caller</param>
|
|||
/// <param name="offsetSourceByNeg128">If true, a constant -128.0 offset is applied for all values before FDCT </param>
|
|||
public static void TransformFDCT( |
|||
ref Block8x8F src, |
|||
ref Block8x8F dest, |
|||
ref Block8x8F temp, |
|||
bool offsetSourceByNeg128 = true) |
|||
{ |
|||
src.TransposeInto(ref temp); |
|||
if (offsetSourceByNeg128) |
|||
{ |
|||
temp.AddToAllInplace(new Vector4(-128)); |
|||
} |
|||
|
|||
FDCT8x4_LeftPart(ref temp, ref dest); |
|||
FDCT8x4_RightPart(ref temp, ref dest); |
|||
|
|||
dest.TransposeInto(ref temp); |
|||
|
|||
FDCT8x4_LeftPart(ref temp, ref dest); |
|||
FDCT8x4_RightPart(ref temp, ref dest); |
|||
|
|||
dest.MultiplyAllInplace(C_0_125); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,36 @@ |
|||
// <copyright file="HuffIndex.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
namespace ImageSharp.Formats.Jpg.Components.Encoder |
|||
{ |
|||
/// <summary>
|
|||
/// Enumerates the Huffman tables
|
|||
/// </summary>
|
|||
internal enum HuffIndex |
|||
{ |
|||
/// <summary>
|
|||
/// The DC luminance huffman table index
|
|||
/// </summary>
|
|||
LuminanceDC = 0, |
|||
|
|||
// ReSharper disable UnusedMember.Local
|
|||
|
|||
/// <summary>
|
|||
/// The AC luminance huffman table index
|
|||
/// </summary>
|
|||
LuminanceAC = 1, |
|||
|
|||
/// <summary>
|
|||
/// The DC chrominance huffman table index
|
|||
/// </summary>
|
|||
ChrominanceDC = 2, |
|||
|
|||
/// <summary>
|
|||
/// The AC chrominance huffman table index
|
|||
/// </summary>
|
|||
ChrominanceAC = 3, |
|||
|
|||
// ReSharper restore UnusedMember.Local
|
|||
} |
|||
} |
|||
@ -0,0 +1,73 @@ |
|||
// <copyright file="HuffmanLut.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp.Formats.Jpg.Components.Encoder |
|||
{ |
|||
/// <summary>
|
|||
/// A compiled look-up table representation of a huffmanSpec.
|
|||
/// Each value maps to a uint32 of which the 8 most significant bits hold the
|
|||
/// codeword size in bits and the 24 least significant bits hold the codeword.
|
|||
/// The maximum codeword size is 16 bits.
|
|||
/// </summary>
|
|||
internal struct HuffmanLut |
|||
{ |
|||
/// <summary>
|
|||
/// The compiled representations of theHuffmanSpec.
|
|||
/// </summary>
|
|||
public static readonly HuffmanLut[] TheHuffmanLut = new HuffmanLut[4]; |
|||
|
|||
/// <summary>
|
|||
/// Initializes static members of the <see cref="HuffmanLut"/> struct.
|
|||
/// </summary>
|
|||
static HuffmanLut() |
|||
{ |
|||
// Initialize the Huffman tables
|
|||
for (int i = 0; i < HuffmanSpec.TheHuffmanSpecs.Length; i++) |
|||
{ |
|||
TheHuffmanLut[i] = new HuffmanLut(HuffmanSpec.TheHuffmanSpecs[i]); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="HuffmanLut"/> struct.
|
|||
/// </summary>
|
|||
/// <param name="spec">dasd</param>
|
|||
public HuffmanLut(HuffmanSpec spec) |
|||
{ |
|||
int maxValue = 0; |
|||
|
|||
foreach (byte v in spec.Values) |
|||
{ |
|||
if (v > maxValue) |
|||
{ |
|||
maxValue = v; |
|||
} |
|||
} |
|||
|
|||
this.Values = new uint[maxValue + 1]; |
|||
|
|||
int code = 0; |
|||
int k = 0; |
|||
|
|||
for (int i = 0; i < spec.Count.Length; i++) |
|||
{ |
|||
int bits = (i + 1) << 24; |
|||
for (int j = 0; j < spec.Count[i]; j++) |
|||
{ |
|||
this.Values[spec.Values[k]] = (uint)(bits | code); |
|||
code++; |
|||
k++; |
|||
} |
|||
|
|||
code <<= 1; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the collection of huffman values.
|
|||
/// </summary>
|
|||
public uint[] Values { get; } |
|||
} |
|||
} |
|||
@ -0,0 +1,136 @@ |
|||
// <copyright file="HuffmanSpec.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
namespace ImageSharp.Formats.Jpg.Components.Encoder |
|||
{ |
|||
/// <summary>
|
|||
/// The Huffman encoding specifications.
|
|||
/// </summary>
|
|||
internal struct HuffmanSpec |
|||
{ |
|||
#pragma warning disable SA1118 // ParameterMustNotSpanMultipleLines
|
|||
|
|||
/// <summary>
|
|||
/// The Huffman encoding specifications.
|
|||
/// This encoder uses the same Huffman encoding for all images.
|
|||
/// </summary>
|
|||
public static readonly HuffmanSpec[] TheHuffmanSpecs = |
|||
{ |
|||
// Luminance DC.
|
|||
new HuffmanSpec( |
|||
new byte[] |
|||
{ |
|||
0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, |
|||
0, 0, 0 |
|||
}, |
|||
new byte[] |
|||
{ |
|||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 |
|||
}), |
|||
new HuffmanSpec( |
|||
new byte[] |
|||
{ |
|||
0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, |
|||
0, 1, 125 |
|||
}, |
|||
new byte[] |
|||
{ |
|||
0x01, 0x02, 0x03, 0x00, 0x04, 0x11, |
|||
0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, |
|||
0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, |
|||
0x81, 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1, |
|||
0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, |
|||
0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, |
|||
0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, |
|||
0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, |
|||
0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, |
|||
0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, |
|||
0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, |
|||
0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, |
|||
0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x83, |
|||
0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, |
|||
0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, |
|||
0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, |
|||
0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, |
|||
0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, |
|||
0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, |
|||
0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, |
|||
0xd8, 0xd9, 0xda, 0xe1, 0xe2, 0xe3, 0xe4, |
|||
0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, |
|||
0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, |
|||
0xf9, 0xfa |
|||
}), |
|||
new HuffmanSpec( |
|||
new byte[] |
|||
{ |
|||
0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, |
|||
0, 0, 0 |
|||
}, |
|||
new byte[] |
|||
{ |
|||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 |
|||
}), |
|||
|
|||
// Chrominance AC.
|
|||
new HuffmanSpec( |
|||
new byte[] |
|||
{ |
|||
0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, |
|||
1, 2, 119 |
|||
}, |
|||
new byte[] |
|||
{ |
|||
0x00, 0x01, 0x02, 0x03, 0x11, 0x04, |
|||
0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, |
|||
0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, |
|||
0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1, 0xc1, |
|||
0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, |
|||
0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, |
|||
0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, |
|||
0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, |
|||
0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, |
|||
0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, |
|||
0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, |
|||
0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, |
|||
0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, |
|||
0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, |
|||
0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, |
|||
0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, |
|||
0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, |
|||
0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, |
|||
0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, |
|||
0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, |
|||
0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe2, 0xe3, |
|||
0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, |
|||
0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, |
|||
0xf9, 0xfa |
|||
}) |
|||
}; |
|||
#pragma warning restore SA1118 // ParameterMustNotSpanMultipleLines
|
|||
/// <summary>
|
|||
/// Gets count[i] - The number of codes of length i bits.
|
|||
/// </summary>
|
|||
public readonly byte[] Count; |
|||
|
|||
/// <summary>
|
|||
/// Gets value[i] - The decoded value of the codeword at the given index.
|
|||
/// </summary>
|
|||
public readonly byte[] Values; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="HuffmanSpec"/> struct.
|
|||
/// </summary>
|
|||
/// <param name="count">
|
|||
/// The number of codes.
|
|||
/// </param>
|
|||
/// <param name="values">
|
|||
/// The decoded values.
|
|||
/// </param>
|
|||
public HuffmanSpec(byte[] count, byte[] values) |
|||
{ |
|||
this.Count = count; |
|||
this.Values = values; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
// <copyright file="QuantIndex.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
namespace ImageSharp.Formats.Jpg.Components.Encoder |
|||
{ |
|||
/// <summary>
|
|||
/// Enumerates the quantization tables
|
|||
/// </summary>
|
|||
internal enum QuantIndex |
|||
{ |
|||
/// <summary>
|
|||
/// The luminance quantization table index
|
|||
/// </summary>
|
|||
Luminance = 0, |
|||
|
|||
/// <summary>
|
|||
/// The chrominance quantization table index
|
|||
/// </summary>
|
|||
Chrominance = 1, |
|||
} |
|||
} |
|||
@ -1,162 +0,0 @@ |
|||
// <copyright file="FDCT.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp.Formats |
|||
{ |
|||
/// <summary>
|
|||
/// Performs a fast, forward discrete cosine transform against the given block
|
|||
/// decomposing it into 64 orthogonal basis signals.
|
|||
/// </summary>
|
|||
internal class FDCT |
|||
{ |
|||
// Trigonometric constants in 13-bit fixed point format.
|
|||
// TODO: Rename and describe these.
|
|||
#pragma warning disable SA1310 // FieldNamesMustNotContainUnderscore
|
|||
private const int Fix_0_298631336 = 2446; |
|||
private const int Fix_0_390180644 = 3196; |
|||
private const int Fix_0_541196100 = 4433; |
|||
private const int Fix_0_765366865 = 6270; |
|||
private const int Fix_0_899976223 = 7373; |
|||
private const int Fix_1_175875602 = 9633; |
|||
private const int Fix_1_501321110 = 12299; |
|||
private const int Fix_1_847759065 = 15137; |
|||
private const int Fix_1_961570560 = 16069; |
|||
private const int Fix_2_053119869 = 16819; |
|||
private const int Fix_2_562915447 = 20995; |
|||
private const int Fix_3_072711026 = 25172; |
|||
#pragma warning restore SA1310 // FieldNamesMustNotContainUnderscore
|
|||
|
|||
/// <summary>
|
|||
/// The number of bits
|
|||
/// </summary>
|
|||
private const int Bits = 13; |
|||
|
|||
/// <summary>
|
|||
/// The number of bits to shift by on the first pass.
|
|||
/// </summary>
|
|||
private const int Pass1Bits = 2; |
|||
|
|||
/// <summary>
|
|||
/// The value to shift by
|
|||
/// </summary>
|
|||
private const int CenterJSample = 128; |
|||
|
|||
/// <summary>
|
|||
/// Performs a forward DCT on an 8x8 block of coefficients, including a level shift.
|
|||
/// </summary>
|
|||
/// <param name="block">The block of coefficients.</param>
|
|||
public static void Transform(ref Block block) |
|||
{ |
|||
// Pass 1: process rows.
|
|||
for (int y = 0; y < 8; y++) |
|||
{ |
|||
int y8 = y * 8; |
|||
|
|||
int x0 = block[y8]; |
|||
int x1 = block[y8 + 1]; |
|||
int x2 = block[y8 + 2]; |
|||
int x3 = block[y8 + 3]; |
|||
int x4 = block[y8 + 4]; |
|||
int x5 = block[y8 + 5]; |
|||
int x6 = block[y8 + 6]; |
|||
int x7 = block[y8 + 7]; |
|||
|
|||
int tmp0 = x0 + x7; |
|||
int tmp1 = x1 + x6; |
|||
int tmp2 = x2 + x5; |
|||
int tmp3 = x3 + x4; |
|||
|
|||
int tmp10 = tmp0 + tmp3; |
|||
int tmp12 = tmp0 - tmp3; |
|||
int tmp11 = tmp1 + tmp2; |
|||
int tmp13 = tmp1 - tmp2; |
|||
|
|||
tmp0 = x0 - x7; |
|||
tmp1 = x1 - x6; |
|||
tmp2 = x2 - x5; |
|||
tmp3 = x3 - x4; |
|||
|
|||
block[y8] = (tmp10 + tmp11 - (8 * CenterJSample)) << Pass1Bits; |
|||
block[y8 + 4] = (tmp10 - tmp11) << Pass1Bits; |
|||
int z1 = (tmp12 + tmp13) * Fix_0_541196100; |
|||
z1 += 1 << (Bits - Pass1Bits - 1); |
|||
block[y8 + 2] = (z1 + (tmp12 * Fix_0_765366865)) >> (Bits - Pass1Bits); |
|||
block[y8 + 6] = (z1 - (tmp13 * Fix_1_847759065)) >> (Bits - Pass1Bits); |
|||
|
|||
tmp10 = tmp0 + tmp3; |
|||
tmp11 = tmp1 + tmp2; |
|||
tmp12 = tmp0 + tmp2; |
|||
tmp13 = tmp1 + tmp3; |
|||
z1 = (tmp12 + tmp13) * Fix_1_175875602; |
|||
z1 += 1 << (Bits - Pass1Bits - 1); |
|||
tmp0 = tmp0 * Fix_1_501321110; |
|||
tmp1 = tmp1 * Fix_3_072711026; |
|||
tmp2 = tmp2 * Fix_2_053119869; |
|||
tmp3 = tmp3 * Fix_0_298631336; |
|||
tmp10 = tmp10 * -Fix_0_899976223; |
|||
tmp11 = tmp11 * -Fix_2_562915447; |
|||
tmp12 = tmp12 * -Fix_0_390180644; |
|||
tmp13 = tmp13 * -Fix_1_961570560; |
|||
|
|||
tmp12 += z1; |
|||
tmp13 += z1; |
|||
block[y8 + 1] = (tmp0 + tmp10 + tmp12) >> (Bits - Pass1Bits); |
|||
block[y8 + 3] = (tmp1 + tmp11 + tmp13) >> (Bits - Pass1Bits); |
|||
block[y8 + 5] = (tmp2 + tmp11 + tmp12) >> (Bits - Pass1Bits); |
|||
block[y8 + 7] = (tmp3 + tmp10 + tmp13) >> (Bits - Pass1Bits); |
|||
} |
|||
|
|||
// Pass 2: process columns.
|
|||
// We remove pass1Bits scaling, but leave results scaled up by an overall factor of 8.
|
|||
for (int x = 0; x < 8; x++) |
|||
{ |
|||
int tmp0 = block[x] + block[56 + x]; |
|||
int tmp1 = block[8 + x] + block[48 + x]; |
|||
int tmp2 = block[16 + x] + block[40 + x]; |
|||
int tmp3 = block[24 + x] + block[32 + x]; |
|||
|
|||
int tmp10 = tmp0 + tmp3 + (1 << (Pass1Bits - 1)); |
|||
int tmp12 = tmp0 - tmp3; |
|||
int tmp11 = tmp1 + tmp2; |
|||
int tmp13 = tmp1 - tmp2; |
|||
|
|||
tmp0 = block[x] - block[56 + x]; |
|||
tmp1 = block[8 + x] - block[48 + x]; |
|||
tmp2 = block[16 + x] - block[40 + x]; |
|||
tmp3 = block[24 + x] - block[32 + x]; |
|||
|
|||
block[x] = (tmp10 + tmp11) >> Pass1Bits; |
|||
block[32 + x] = (tmp10 - tmp11) >> Pass1Bits; |
|||
|
|||
int z1 = (tmp12 + tmp13) * Fix_0_541196100; |
|||
z1 += 1 << (Bits + Pass1Bits - 1); |
|||
block[16 + x] = (z1 + (tmp12 * Fix_0_765366865)) >> (Bits + Pass1Bits); |
|||
block[48 + x] = (z1 - (tmp13 * Fix_1_847759065)) >> (Bits + Pass1Bits); |
|||
|
|||
tmp10 = tmp0 + tmp3; |
|||
tmp11 = tmp1 + tmp2; |
|||
tmp12 = tmp0 + tmp2; |
|||
tmp13 = tmp1 + tmp3; |
|||
z1 = (tmp12 + tmp13) * Fix_1_175875602; |
|||
z1 += 1 << (Bits + Pass1Bits - 1); |
|||
tmp0 = tmp0 * Fix_1_501321110; |
|||
tmp1 = tmp1 * Fix_3_072711026; |
|||
tmp2 = tmp2 * Fix_2_053119869; |
|||
tmp3 = tmp3 * Fix_0_298631336; |
|||
tmp10 = tmp10 * -Fix_0_899976223; |
|||
tmp11 = tmp11 * -Fix_2_562915447; |
|||
tmp12 = tmp12 * -Fix_0_390180644; |
|||
tmp13 = tmp13 * -Fix_1_961570560; |
|||
|
|||
tmp12 += z1; |
|||
tmp13 += z1; |
|||
block[8 + x] = (tmp0 + tmp10 + tmp12) >> (Bits + Pass1Bits); |
|||
block[24 + x] = (tmp1 + tmp11 + tmp13) >> (Bits + Pass1Bits); |
|||
block[40 + x] = (tmp2 + tmp11 + tmp12) >> (Bits + Pass1Bits); |
|||
block[56 + x] = (tmp3 + tmp10 + tmp13) >> (Bits + Pass1Bits); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,169 +0,0 @@ |
|||
// <copyright file="IDCT.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp.Formats |
|||
{ |
|||
/// <summary>
|
|||
/// Performs a 2-D Inverse Discrete Cosine Transformation.
|
|||
/// </summary>
|
|||
internal class IDCT |
|||
{ |
|||
private const int W1 = 2841; // 2048*sqrt(2)*cos(1*pi/16)
|
|||
private const int W2 = 2676; // 2048*sqrt(2)*cos(2*pi/16)
|
|||
private const int W3 = 2408; // 2048*sqrt(2)*cos(3*pi/16)
|
|||
private const int W5 = 1609; // 2048*sqrt(2)*cos(5*pi/16)
|
|||
private const int W6 = 1108; // 2048*sqrt(2)*cos(6*pi/16)
|
|||
private const int W7 = 565; // 2048*sqrt(2)*cos(7*pi/16)
|
|||
|
|||
private const int W1pw7 = W1 + W7; |
|||
private const int W1mw7 = W1 - W7; |
|||
private const int W2pw6 = W2 + W6; |
|||
private const int W2mw6 = W2 - W6; |
|||
private const int W3pw5 = W3 + W5; |
|||
private const int W3mw5 = W3 - W5; |
|||
|
|||
private const int R2 = 181; // 256/sqrt(2)
|
|||
|
|||
/// <summary>
|
|||
/// Performs a 2-D Inverse Discrete Cosine Transformation.
|
|||
/// <para>
|
|||
/// The input coefficients should already have been multiplied by the
|
|||
/// appropriate quantization table. We use fixed-point computation, with the
|
|||
/// number of bits for the fractional component varying over the intermediate
|
|||
/// stages.
|
|||
/// </para>
|
|||
/// For more on the actual algorithm, see Z. Wang, "Fast algorithms for the
|
|||
/// discrete W transform and for the discrete Fourier transform", IEEE Trans. on
|
|||
/// ASSP, Vol. ASSP- 32, pp. 803-816, Aug. 1984.
|
|||
/// </summary>
|
|||
/// <param name="src">The source block of coefficients</param>
|
|||
public static void Transform(ref Block src) |
|||
{ |
|||
// Horizontal 1-D IDCT.
|
|||
for (int y = 0; y < 8; y++) |
|||
{ |
|||
int y8 = y * 8; |
|||
|
|||
// If all the AC components are zero, then the IDCT is trivial.
|
|||
if (src[y8 + 1] == 0 && src[y8 + 2] == 0 && src[y8 + 3] == 0 && |
|||
src[y8 + 4] == 0 && src[y8 + 5] == 0 && src[y8 + 6] == 0 && src[y8 + 7] == 0) |
|||
{ |
|||
int dc = src[y8 + 0] << 3; |
|||
src[y8 + 0] = dc; |
|||
src[y8 + 1] = dc; |
|||
src[y8 + 2] = dc; |
|||
src[y8 + 3] = dc; |
|||
src[y8 + 4] = dc; |
|||
src[y8 + 5] = dc; |
|||
src[y8 + 6] = dc; |
|||
src[y8 + 7] = dc; |
|||
continue; |
|||
} |
|||
|
|||
// Prescale.
|
|||
int x0 = (src[y8 + 0] << 11) + 128; |
|||
int x1 = src[y8 + 4] << 11; |
|||
int x2 = src[y8 + 6]; |
|||
int x3 = src[y8 + 2]; |
|||
int x4 = src[y8 + 1]; |
|||
int x5 = src[y8 + 7]; |
|||
int x6 = src[y8 + 5]; |
|||
int x7 = src[y8 + 3]; |
|||
|
|||
// Stage 1.
|
|||
int x8 = W7 * (x4 + x5); |
|||
x4 = x8 + (W1mw7 * x4); |
|||
x5 = x8 - (W1pw7 * x5); |
|||
x8 = W3 * (x6 + x7); |
|||
x6 = x8 - (W3mw5 * x6); |
|||
x7 = x8 - (W3pw5 * x7); |
|||
|
|||
// Stage 2.
|
|||
x8 = x0 + x1; |
|||
x0 -= x1; |
|||
x1 = W6 * (x3 + x2); |
|||
x2 = x1 - (W2pw6 * x2); |
|||
x3 = x1 + (W2mw6 * x3); |
|||
x1 = x4 + x6; |
|||
x4 -= x6; |
|||
x6 = x5 + x7; |
|||
x5 -= x7; |
|||
|
|||
// Stage 3.
|
|||
x7 = x8 + x3; |
|||
x8 -= x3; |
|||
x3 = x0 + x2; |
|||
x0 -= x2; |
|||
x2 = ((R2 * (x4 + x5)) + 128) >> 8; |
|||
x4 = ((R2 * (x4 - x5)) + 128) >> 8; |
|||
|
|||
// Stage 4.
|
|||
src[y8 + 0] = (x7 + x1) >> 8; |
|||
src[y8 + 1] = (x3 + x2) >> 8; |
|||
src[y8 + 2] = (x0 + x4) >> 8; |
|||
src[y8 + 3] = (x8 + x6) >> 8; |
|||
src[y8 + 4] = (x8 - x6) >> 8; |
|||
src[y8 + 5] = (x0 - x4) >> 8; |
|||
src[y8 + 6] = (x3 - x2) >> 8; |
|||
src[y8 + 7] = (x7 - x1) >> 8; |
|||
} |
|||
|
|||
// Vertical 1-D IDCT.
|
|||
for (int x = 0; x < 8; x++) |
|||
{ |
|||
// Similar to the horizontal 1-D IDCT case, if all the AC components are zero, then the IDCT is trivial.
|
|||
// However, after performing the horizontal 1-D IDCT, there are typically non-zero AC components, so
|
|||
// we do not bother to check for the all-zero case.
|
|||
|
|||
// Prescale.
|
|||
int y0 = (src[x] << 8) + 8192; |
|||
int y1 = src[32 + x] << 8; |
|||
int y2 = src[48 + x]; |
|||
int y3 = src[16 + x]; |
|||
int y4 = src[8 + x]; |
|||
int y5 = src[56 + x]; |
|||
int y6 = src[40 + x]; |
|||
int y7 = src[24 + x]; |
|||
|
|||
// Stage 1.
|
|||
int y8 = (W7 * (y4 + y5)) + 4; |
|||
y4 = (y8 + (W1mw7 * y4)) >> 3; |
|||
y5 = (y8 - (W1pw7 * y5)) >> 3; |
|||
y8 = (W3 * (y6 + y7)) + 4; |
|||
y6 = (y8 - (W3mw5 * y6)) >> 3; |
|||
y7 = (y8 - (W3pw5 * y7)) >> 3; |
|||
|
|||
// Stage 2.
|
|||
y8 = y0 + y1; |
|||
y0 -= y1; |
|||
y1 = (W6 * (y3 + y2)) + 4; |
|||
y2 = (y1 - (W2pw6 * y2)) >> 3; |
|||
y3 = (y1 + (W2mw6 * y3)) >> 3; |
|||
y1 = y4 + y6; |
|||
y4 -= y6; |
|||
y6 = y5 + y7; |
|||
y5 -= y7; |
|||
|
|||
// Stage 3.
|
|||
y7 = y8 + y3; |
|||
y8 -= y3; |
|||
y3 = y0 + y2; |
|||
y0 -= y2; |
|||
y2 = ((R2 * (y4 + y5)) + 128) >> 8; |
|||
y4 = ((R2 * (y4 - y5)) + 128) >> 8; |
|||
|
|||
// Stage 4.
|
|||
src[x] = (y7 + y1) >> 14; |
|||
src[8 + x] = (y3 + y2) >> 14; |
|||
src[16 + x] = (y0 + y4) >> 14; |
|||
src[24 + x] = (y8 + y6) >> 14; |
|||
src[32 + x] = (y8 - y6) >> 14; |
|||
src[40 + x] = (y0 - y4) >> 14; |
|||
src[48 + x] = (y3 - y2) >> 14; |
|||
src[56 + x] = (y7 - y1) >> 14; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,81 +0,0 @@ |
|||
// <copyright file="MutableSpanExtensions.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp.Formats.Jpg.Components |
|||
{ |
|||
using System.Numerics; |
|||
using System.Runtime.CompilerServices; |
|||
|
|||
/// <summary>
|
|||
/// MutableSpan Extensions
|
|||
/// </summary>
|
|||
internal static class MutableSpanExtensions |
|||
{ |
|||
/// <summary>
|
|||
/// Slice <see cref="MutableSpan{T}"/>
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The type of the data in the span</typeparam>
|
|||
/// <param name="array">The data array</param>
|
|||
/// <param name="offset">The offset</param>
|
|||
/// <returns>The new <see cref="MutableSpan{T}"/></returns>
|
|||
public static MutableSpan<T> Slice<T>(this T[] array, int offset) => new MutableSpan<T>(array, offset); |
|||
|
|||
/// <summary>
|
|||
/// Save to a Vector4
|
|||
/// </summary>
|
|||
/// <param name="data">The data</param>
|
|||
/// <param name="v">The vector to save to</param>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static void SaveTo(this MutableSpan<float> data, ref Vector4 v) |
|||
{ |
|||
v.X = data[0]; |
|||
v.Y = data[1]; |
|||
v.Z = data[2]; |
|||
v.W = data[3]; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Save to a Vector4
|
|||
/// </summary>
|
|||
/// <param name="data">The data</param>
|
|||
/// <param name="v">The vector to save to</param>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static void SaveTo(this MutableSpan<int> data, ref Vector4 v) |
|||
{ |
|||
v.X = data[0]; |
|||
v.Y = data[1]; |
|||
v.Z = data[2]; |
|||
v.W = data[3]; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Load from Vector4
|
|||
/// </summary>
|
|||
/// <param name="data">The data</param>
|
|||
/// <param name="v">The vector to load from</param>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static void LoadFrom(this MutableSpan<float> data, ref Vector4 v) |
|||
{ |
|||
data[0] = v.X; |
|||
data[1] = v.Y; |
|||
data[2] = v.Z; |
|||
data[3] = v.W; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Load from Vector4
|
|||
/// </summary>
|
|||
/// <param name="data">The data</param>
|
|||
/// <param name="v">The vector to load from</param>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static void LoadFrom(this MutableSpan<int> data, ref Vector4 v) |
|||
{ |
|||
data[0] = (int)v.X; |
|||
data[1] = (int)v.Y; |
|||
data[2] = (int)v.Z; |
|||
data[3] = (int)v.W; |
|||
} |
|||
} |
|||
} |
|||
File diff suppressed because it is too large
@ -0,0 +1,48 @@ |
|||
// <copyright file="UnzigData.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
namespace ImageSharp.Formats |
|||
{ |
|||
using System; |
|||
using System.Runtime.InteropServices; |
|||
|
|||
/// <summary>
|
|||
/// Holds the Jpeg UnZig array in a value/stack type.
|
|||
/// Unzig maps from the zigzag ordering to the natural ordering. For example,
|
|||
/// unzig[3] is the column and row of the fourth element in zigzag order. The
|
|||
/// value is 16, which means first column (16%8 == 0) and third row (16/8 == 2).
|
|||
/// </summary>
|
|||
internal unsafe struct UnzigData |
|||
{ |
|||
/// <summary>
|
|||
/// Copy of <see cref="Unzig"/> in a value type
|
|||
/// </summary>
|
|||
public fixed int Data[64]; |
|||
|
|||
/// <summary>
|
|||
/// Unzig maps from the zigzag ordering to the natural ordering. For example,
|
|||
/// unzig[3] is the column and row of the fourth element in zigzag order. The
|
|||
/// value is 16, which means first column (16%8 == 0) and third row (16/8 == 2).
|
|||
/// </summary>
|
|||
private static readonly int[] Unzig = |
|||
{ |
|||
0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33, |
|||
40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50, |
|||
43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59, 52, 45, 38, 31, 39, 46, |
|||
53, 60, 61, 54, 47, 55, 62, 63, |
|||
}; |
|||
|
|||
/// <summary>
|
|||
/// Creates and fills an instance of <see cref="UnzigData"/> with Jpeg unzig indices
|
|||
/// </summary>
|
|||
/// <returns>The new instance</returns>
|
|||
public static UnzigData Create() |
|||
{ |
|||
UnzigData result = default(UnzigData); |
|||
int* unzigPtr = result.Data; |
|||
Marshal.Copy(Unzig, 0, (IntPtr)unzigPtr, 64); |
|||
return result; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,134 @@ |
|||
// <copyright file="JpegUtils.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
namespace ImageSharp.Formats.Jpg.Utils |
|||
{ |
|||
using System; |
|||
using System.Runtime.CompilerServices; |
|||
|
|||
using ImageSharp.Formats.Jpg.Components.Encoder; |
|||
|
|||
/// <summary>
|
|||
/// Jpeg specific utilities and extension methods
|
|||
/// </summary>
|
|||
internal static unsafe class JpegUtils |
|||
{ |
|||
/// <summary>
|
|||
/// Copy a region of an image into dest. De "outlier" area will be stretched out with pixels on the right and bottom of
|
|||
/// the image.
|
|||
/// </summary>
|
|||
/// <typeparam name="TColor">
|
|||
/// The pixel type
|
|||
/// </typeparam>
|
|||
/// <param name="pixels">
|
|||
/// The input pixel acessor
|
|||
/// </param>
|
|||
/// <param name="dest">
|
|||
/// The destination <see cref="PixelArea{TColor}"/>
|
|||
/// </param>
|
|||
/// <param name="sourceY">
|
|||
/// Starting Y coord
|
|||
/// </param>
|
|||
/// <param name="sourceX">
|
|||
/// Starting X coord
|
|||
/// </param>
|
|||
public static void CopyRGBBytesStretchedTo<TColor>( |
|||
this PixelAccessor<TColor> pixels, |
|||
PixelArea<TColor> dest, |
|||
int sourceY, |
|||
int sourceX) |
|||
where TColor : struct, IPackedPixel, IEquatable<TColor> |
|||
{ |
|||
pixels.CopyTo(dest, sourceY, sourceX); |
|||
int stretchFromX = pixels.Width - sourceX; |
|||
int stretchFromY = pixels.Height - sourceY; |
|||
StretchPixels(dest, stretchFromX, stretchFromY); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Copy an RGB value
|
|||
/// </summary>
|
|||
/// <param name="source">
|
|||
/// Source pointer
|
|||
/// </param>
|
|||
/// <param name="dest">
|
|||
/// Destination pointer
|
|||
/// </param>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
internal static void CopyRgb(byte* source, byte* dest) |
|||
{ |
|||
*dest++ = *source++; // R
|
|||
*dest++ = *source++; // G
|
|||
*dest = *source; // B
|
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes data to "Define Quantization Tables" block for QuantIndex
|
|||
/// </summary>
|
|||
/// <param name="dqt">
|
|||
/// The "Define Quantization Tables" block
|
|||
/// </param>
|
|||
/// <param name="offset">
|
|||
/// Offset in dqt
|
|||
/// </param>
|
|||
/// <param name="i">
|
|||
/// The quantization index
|
|||
/// </param>
|
|||
/// <param name="q">
|
|||
/// The quantazation table to copy data from
|
|||
/// </param>
|
|||
internal static void WriteDataToDqt(byte[] dqt, ref int offset, QuantIndex i, ref Block8x8F q) |
|||
{ |
|||
dqt[offset++] = (byte)i; |
|||
for (int j = 0; j < Block8x8F.ScalarCount; j++) |
|||
{ |
|||
dqt[offset++] = (byte)q[j]; |
|||
} |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
private static bool IsInvalidStretchArea<TColor>(PixelArea<TColor> area, int fromX, int fromY) |
|||
where TColor : struct, IPackedPixel, IEquatable<TColor> |
|||
{ |
|||
return fromX <= 0 || fromY <= 0 || fromX >= area.Width || fromY >= area.Height; |
|||
} |
|||
|
|||
private static void StretchPixels<TColor>(PixelArea<TColor> area, int fromX, int fromY) |
|||
where TColor : struct, IPackedPixel, IEquatable<TColor> |
|||
{ |
|||
if (IsInvalidStretchArea(area, fromX, fromY)) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
for (int y = 0; y < fromY; y++) |
|||
{ |
|||
byte* ptrBase = (byte*)area.DataPointer + (y * area.RowByteCount); |
|||
|
|||
for (int x = fromX; x < area.Width; x++) |
|||
{ |
|||
byte* prevPtr = ptrBase + ((x - 1) * 3); |
|||
byte* currPtr = ptrBase + (x * 3); |
|||
|
|||
CopyRgb(prevPtr, currPtr); |
|||
} |
|||
} |
|||
|
|||
for (int y = fromY; y < area.Height; y++) |
|||
{ |
|||
byte* currBase = (byte*)area.DataPointer + (y * area.RowByteCount); |
|||
byte* prevBase = (byte*)area.DataPointer + ((y - 1) * area.RowByteCount); |
|||
|
|||
for (int x = 0; x < area.Width; x++) |
|||
{ |
|||
int x3 = 3 * x; |
|||
byte* currPtr = currBase + x3; |
|||
byte* prevPtr = prevBase + x3; |
|||
|
|||
CopyRgb(prevPtr, currPtr); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,164 @@ |
|||
// <copyright file="MutableSpanExtensions.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp.Formats.Jpg.Utils |
|||
{ |
|||
using System.Numerics; |
|||
using System.Runtime.CompilerServices; |
|||
|
|||
/// <summary>
|
|||
/// MutableSpan Extensions
|
|||
/// </summary>
|
|||
internal static class MutableSpanExtensions |
|||
{ |
|||
/// <summary>
|
|||
/// Slice <see cref="MutableSpan{T}"/>
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The type of the data in the span</typeparam>
|
|||
/// <param name="array">The data array</param>
|
|||
/// <param name="offset">The offset</param>
|
|||
/// <returns>The new <see cref="MutableSpan{T}"/></returns>
|
|||
public static MutableSpan<T> Slice<T>(this T[] array, int offset) => new MutableSpan<T>(array, offset); |
|||
|
|||
/// <summary>
|
|||
/// Save to a Vector4
|
|||
/// </summary>
|
|||
/// <param name="data">The data</param>
|
|||
/// <param name="v">The vector to save to</param>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static void SaveTo(this MutableSpan<float> data, ref Vector4 v) |
|||
{ |
|||
v.X = data[0]; |
|||
v.Y = data[1]; |
|||
v.Z = data[2]; |
|||
v.W = data[3]; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Save to a Vector4
|
|||
/// </summary>
|
|||
/// <param name="data">The data</param>
|
|||
/// <param name="v">The vector to save to</param>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static void SaveTo(this MutableSpan<int> data, ref Vector4 v) |
|||
{ |
|||
v.X = data[0]; |
|||
v.Y = data[1]; |
|||
v.Z = data[2]; |
|||
v.W = data[3]; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Load from Vector4
|
|||
/// </summary>
|
|||
/// <param name="data">The data</param>
|
|||
/// <param name="v">The vector to load from</param>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static void LoadFrom(this MutableSpan<float> data, ref Vector4 v) |
|||
{ |
|||
data[0] = v.X; |
|||
data[1] = v.Y; |
|||
data[2] = v.Z; |
|||
data[3] = v.W; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Load from Vector4
|
|||
/// </summary>
|
|||
/// <param name="data">The data</param>
|
|||
/// <param name="v">The vector to load from</param>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static void LoadFrom(this MutableSpan<int> data, ref Vector4 v) |
|||
{ |
|||
data[0] = (int)v.X; |
|||
data[1] = (int)v.Y; |
|||
data[2] = (int)v.Z; |
|||
data[3] = (int)v.W; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Converts all int values of src to float
|
|||
/// </summary>
|
|||
/// <param name="src">Source</param>
|
|||
/// <returns>A new <see cref="MutableSpan{T}"/> with float values</returns>
|
|||
public static MutableSpan<float> ConvertToFloat32MutableSpan(this MutableSpan<int> src) |
|||
{ |
|||
MutableSpan<float> result = new MutableSpan<float>(src.TotalCount); |
|||
for (int i = 0; i < src.TotalCount; i++) |
|||
{ |
|||
result[i] = (float)src[i]; |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Converts all float values of src to int
|
|||
/// </summary>
|
|||
/// <param name="src">Source</param>
|
|||
/// <returns>A new <see cref="MutableSpan{T}"/> with float values</returns>
|
|||
public static MutableSpan<int> ConvertToInt32MutableSpan(this MutableSpan<float> src) |
|||
{ |
|||
MutableSpan<int> result = new MutableSpan<int>(src.TotalCount); |
|||
for (int i = 0; i < src.TotalCount; i++) |
|||
{ |
|||
result[i] = (int)src[i]; |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Add a scalar to all values of src
|
|||
/// </summary>
|
|||
/// <param name="src">The source</param>
|
|||
/// <param name="scalar">The scalar value to add</param>
|
|||
/// <returns>A new instance of <see cref="MutableSpan{T}"/></returns>
|
|||
public static MutableSpan<float> AddScalarToAllValues(this MutableSpan<float> src, float scalar) |
|||
{ |
|||
MutableSpan<float> result = new MutableSpan<float>(src.TotalCount); |
|||
for (int i = 0; i < src.TotalCount; i++) |
|||
{ |
|||
result[i] = src[i] + scalar; |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Add a scalar to all values of src
|
|||
/// </summary>
|
|||
/// <param name="src">The source</param>
|
|||
/// <param name="scalar">The scalar value to add</param>
|
|||
/// <returns>A new instance of <see cref="MutableSpan{T}"/></returns>
|
|||
public static MutableSpan<int> AddScalarToAllValues(this MutableSpan<int> src, int scalar) |
|||
{ |
|||
MutableSpan<int> result = new MutableSpan<int>(src.TotalCount); |
|||
for (int i = 0; i < src.TotalCount; i++) |
|||
{ |
|||
result[i] = src[i] + scalar; |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Copy all values in src to a new <see cref="MutableSpan{T}"/> instance
|
|||
/// </summary>
|
|||
/// <typeparam name="T">Element type</typeparam>
|
|||
/// <param name="src">The source</param>
|
|||
/// <returns>A new instance of <see cref="MutableSpan{T}"/></returns>
|
|||
public static MutableSpan<T> Copy<T>(this MutableSpan<T> src) |
|||
{ |
|||
MutableSpan<T> result = new MutableSpan<T>(src.TotalCount); |
|||
for (int i = 0; i < src.TotalCount; i++) |
|||
{ |
|||
result[i] = src[i]; |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
} |
|||
} |
|||
@ -1,106 +1,38 @@ |
|||
// <copyright file="DecodeJpegMultiple.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
using System.Collections.Generic; |
|||
|
|||
namespace ImageSharp.Benchmarks.Image |
|||
{ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Drawing; |
|||
using System.IO; |
|||
using System.Linq; |
|||
|
|||
using BenchmarkDotNet.Attributes; |
|||
|
|||
using Image = ImageSharp.Image; |
|||
using ImageSharpSize = ImageSharp.Size; |
|||
|
|||
public class DecodeJpegMultiple |
|||
public class DecodeJpegMultiple : MultiImageBenchmarkBase |
|||
{ |
|||
private const string Folder = "../ImageSharp.Tests/TestImages/Formats/Jpg/"; |
|||
|
|||
private Dictionary<string, byte[]> fileNamesToBytes; |
|||
|
|||
public enum JpegTestingMode |
|||
protected override IEnumerable<string> InputImageSubfolders => new[] |
|||
{ |
|||
All, |
|||
|
|||
SmallImagesOnly, |
|||
|
|||
LargeImagesOnly, |
|||
"Formats/Jpg/" |
|||
}; |
|||
|
|||
CalliphoraOnly, |
|||
} |
|||
|
|||
[Params(JpegTestingMode.All, JpegTestingMode.SmallImagesOnly, JpegTestingMode.LargeImagesOnly, |
|||
JpegTestingMode.CalliphoraOnly)] |
|||
public JpegTestingMode Mode { get; set; } |
|||
|
|||
private IEnumerable<KeyValuePair<string, byte[]>> RequestedImages |
|||
{ |
|||
get |
|||
{ |
|||
int thresholdInBytes = 100000; |
|||
|
|||
switch (this.Mode) |
|||
{ |
|||
case JpegTestingMode.All: |
|||
return this.fileNamesToBytes; |
|||
case JpegTestingMode.SmallImagesOnly: |
|||
return this.fileNamesToBytes.Where(kv => kv.Value.Length < thresholdInBytes); |
|||
case JpegTestingMode.LargeImagesOnly: |
|||
return this.fileNamesToBytes.Where(kv => kv.Value.Length >= thresholdInBytes); |
|||
case JpegTestingMode.CalliphoraOnly: |
|||
return new[] { this.fileNamesToBytes.First(kv => kv.Key.ToLower().Contains("calliphora")) }; |
|||
default: |
|||
throw new ArgumentOutOfRangeException(); |
|||
} |
|||
} |
|||
} |
|||
protected override IEnumerable<string> FileFilters => new[] { "*.jpg" }; |
|||
|
|||
[Benchmark(Description = "DecodeJpegMultiple - ImageSharp")] |
|||
public ImageSharpSize JpegImageSharp() |
|||
public void DecodeJpegImageSharp() |
|||
{ |
|||
ImageSharpSize lastSize = new ImageSharpSize(); |
|||
foreach (var kv in this.RequestedImages) |
|||
{ |
|||
using (MemoryStream memoryStream = new MemoryStream(kv.Value)) |
|||
{ |
|||
Image image = new Image(memoryStream); |
|||
lastSize = new ImageSharpSize(image.Width, image.Height); |
|||
} |
|||
} |
|||
|
|||
return lastSize; |
|||
this.ForEachStream( |
|||
ms => new ImageSharp.Image(ms) |
|||
); |
|||
} |
|||
|
|||
[Benchmark(Baseline = true, Description = "DecodeJpegMultiple - System.Drawing")] |
|||
public Size JpegSystemDrawing() |
|||
public void DecodeJpegSystemDrawing() |
|||
{ |
|||
Size lastSize = new Size(); |
|||
foreach (var kv in this.RequestedImages) |
|||
{ |
|||
using (MemoryStream memoryStream = new MemoryStream(kv.Value)) |
|||
{ |
|||
using (System.Drawing.Image image = System.Drawing.Image.FromStream(memoryStream)) |
|||
{ |
|||
lastSize = image.Size; |
|||
} |
|||
} |
|||
} |
|||
|
|||
return lastSize; |
|||
this.ForEachStream( |
|||
System.Drawing.Image.FromStream |
|||
); |
|||
} |
|||
|
|||
[Setup] |
|||
public void ReadImages() |
|||
{ |
|||
if (this.fileNamesToBytes != null) return; |
|||
|
|||
var allFiles = Directory.EnumerateFiles(Folder, "*.jpg", SearchOption.AllDirectories).ToArray(); |
|||
|
|||
this.fileNamesToBytes = allFiles.ToDictionary(fn => fn, File.ReadAllBytes); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,50 @@ |
|||
namespace ImageSharp.Benchmarks.Image |
|||
{ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Drawing.Imaging; |
|||
using System.IO; |
|||
using System.Linq; |
|||
|
|||
using BenchmarkDotNet.Attributes; |
|||
using BenchmarkDotNet.Attributes.Jobs; |
|||
using BenchmarkDotNet.Engines; |
|||
|
|||
using ImageSharp.Formats; |
|||
|
|||
public class EncodeJpegMultiple : MultiImageBenchmarkBase.WithImagesPreloaded |
|||
{ |
|||
protected override IEnumerable<string> InputImageSubfolders => new[] |
|||
{ |
|||
"Formats/Bmp/", |
|||
"Formats/Jpg/baseline" |
|||
}; |
|||
|
|||
protected override IEnumerable<string> FileFilters => new[] { "*.bmp", "*.jpg" }; |
|||
|
|||
[Benchmark(Description = "EncodeJpegMultiple - ImageSharp")] |
|||
public void EncodeJpegImageSharp() |
|||
{ |
|||
this.ForEachImageSharpImage( |
|||
img => |
|||
{ |
|||
MemoryStream ms = new MemoryStream(); |
|||
img.Save(ms, new JpegEncoder()); |
|||
return ms; |
|||
}); |
|||
} |
|||
|
|||
[Benchmark(Baseline = true, Description = "EncodeJpegMultiple - System.Drawing")] |
|||
public void EncodeJpegSystemDrawing() |
|||
{ |
|||
this.ForEachSystemDrawingImage( |
|||
img => |
|||
{ |
|||
MemoryStream ms = new MemoryStream(); |
|||
img.Save(ms, ImageFormat.Jpeg); |
|||
return ms; |
|||
}); |
|||
} |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,185 @@ |
|||
namespace ImageSharp.Benchmarks.Image |
|||
{ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Drawing; |
|||
using System.IO; |
|||
using System.Linq; |
|||
using System.Numerics; |
|||
|
|||
using BenchmarkDotNet.Attributes; |
|||
|
|||
using Image = ImageSharp.Image; |
|||
|
|||
public abstract class MultiImageBenchmarkBase |
|||
{ |
|||
protected Dictionary<string, byte[]> FileNamesToBytes = new Dictionary<string, byte[]>(); |
|||
|
|||
protected Dictionary<string, Image> FileNamesToImageSharpImages = new Dictionary<string, Image>(); |
|||
protected Dictionary<string, System.Drawing.Bitmap> FileNamesToSystemDrawingImages = new Dictionary<string, System.Drawing.Bitmap>(); |
|||
|
|||
public enum TestingMode |
|||
{ |
|||
All, |
|||
|
|||
SmallImagesOnly, |
|||
|
|||
LargeImagesOnly |
|||
} |
|||
|
|||
[Params(TestingMode.All, TestingMode.SmallImagesOnly, TestingMode.LargeImagesOnly)] |
|||
public TestingMode Mode { get; set; } |
|||
|
|||
protected virtual string BaseFolder => "../ImageSharp.Tests/TestImages/"; |
|||
|
|||
protected abstract IEnumerable<string> FileFilters { get; } |
|||
|
|||
protected IEnumerable<string> FilterWords => new string[] { }; |
|||
|
|||
protected virtual IEnumerable<string> Folders => this.InputImageSubfolders.Select(f => Path.Combine(this.BaseFolder, f)); |
|||
|
|||
protected virtual int LargeImageThresholdInBytes => 100000; |
|||
|
|||
protected IEnumerable<KeyValuePair<string, T>> EnumeratePairsByBenchmarkSettings<T>( |
|||
Dictionary<string, T> input, |
|||
Predicate<T> checkIfSmall) |
|||
{ |
|||
switch (this.Mode) |
|||
{ |
|||
case TestingMode.All: |
|||
return input; |
|||
case TestingMode.SmallImagesOnly: |
|||
return input.Where(kv => checkIfSmall(kv.Value)); |
|||
case TestingMode.LargeImagesOnly: |
|||
return input.Where(kv => !checkIfSmall(kv.Value)); |
|||
default: |
|||
throw new ArgumentOutOfRangeException(); |
|||
} |
|||
} |
|||
|
|||
protected IEnumerable<KeyValuePair<string, byte[]>> FileNames2Bytes |
|||
=> |
|||
this.EnumeratePairsByBenchmarkSettings( |
|||
this.FileNamesToBytes, |
|||
arr => arr.Length < this.LargeImageThresholdInBytes); |
|||
|
|||
protected abstract IEnumerable<string> InputImageSubfolders { get; } |
|||
|
|||
[Setup] |
|||
public void ReadImages() |
|||
{ |
|||
//Console.WriteLine("Vector.IsHardwareAccelerated: " + Vector.IsHardwareAccelerated);
|
|||
this.ReadImagesImpl(); |
|||
} |
|||
|
|||
protected virtual void ReadImagesImpl() |
|||
{ |
|||
foreach (string folder in this.Folders) |
|||
{ |
|||
var allFiles = |
|||
this.FileFilters.SelectMany( |
|||
f => |
|||
Directory.EnumerateFiles(folder, f, SearchOption.AllDirectories) |
|||
.Where(fn => !this.FilterWords.Any(w => fn.ToLower().Contains(w)))).ToArray(); |
|||
foreach (var fn in allFiles) |
|||
{ |
|||
this.FileNamesToBytes[fn] = File.ReadAllBytes(fn); |
|||
} |
|||
} |
|||
} |
|||
|
|||
protected void ForEachStream(Func<MemoryStream, object> operation) |
|||
{ |
|||
foreach (var kv in this.FileNames2Bytes) |
|||
{ |
|||
using (MemoryStream memoryStream = new MemoryStream(kv.Value)) |
|||
{ |
|||
try |
|||
{ |
|||
var obj = operation(memoryStream); |
|||
(obj as IDisposable)?.Dispose(); |
|||
|
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
Console.WriteLine($"Operation on {kv.Key} failed with {ex.Message}"); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
public abstract class WithImagesPreloaded : MultiImageBenchmarkBase |
|||
{ |
|||
protected override void ReadImagesImpl() |
|||
{ |
|||
base.ReadImagesImpl(); |
|||
|
|||
foreach (var kv in this.FileNamesToBytes) |
|||
{ |
|||
byte[] bytes = kv.Value; |
|||
string fn = kv.Key; |
|||
|
|||
using (var ms1 = new MemoryStream(bytes)) |
|||
{ |
|||
this.FileNamesToImageSharpImages[fn] = new Image(ms1); |
|||
|
|||
} |
|||
|
|||
this.FileNamesToSystemDrawingImages[fn] = new Bitmap(new MemoryStream(bytes)); |
|||
} |
|||
} |
|||
|
|||
protected IEnumerable<KeyValuePair<string, ImageSharp.Image>> FileNames2ImageSharpImages |
|||
=> |
|||
this.EnumeratePairsByBenchmarkSettings( |
|||
this.FileNamesToImageSharpImages, |
|||
img => img.Width * img.Height < this.LargeImageThresholdInPixels); |
|||
|
|||
protected IEnumerable<KeyValuePair<string, System.Drawing.Bitmap>> FileNames2SystemDrawingImages |
|||
=> |
|||
this.EnumeratePairsByBenchmarkSettings( |
|||
this.FileNamesToSystemDrawingImages, |
|||
img => img.Width * img.Height < this.LargeImageThresholdInPixels); |
|||
|
|||
protected virtual int LargeImageThresholdInPixels => 700000; |
|||
|
|||
protected void ForEachImageSharpImage(Func<Image, object> operation) |
|||
{ |
|||
foreach (var kv in this.FileNames2ImageSharpImages) |
|||
{ |
|||
try |
|||
{ |
|||
var obj = operation(kv.Value); |
|||
(obj as IDisposable)?.Dispose(); |
|||
|
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
Console.WriteLine($"Operation on {kv.Key} failed with {ex.Message}"); |
|||
} |
|||
|
|||
} |
|||
} |
|||
|
|||
protected void ForEachSystemDrawingImage(Func<System.Drawing.Bitmap, object> operation) |
|||
{ |
|||
foreach (var kv in this.FileNames2SystemDrawingImages) |
|||
{ |
|||
try |
|||
{ |
|||
var obj = operation(kv.Value); |
|||
(obj as IDisposable)?.Dispose(); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
Console.WriteLine($"Operation on {kv.Key} failed with {ex.Message}"); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
} |
|||
|
|||
|
|||
} |
|||
@ -0,0 +1,127 @@ |
|||
// ReSharper disable InconsistentNaming
|
|||
namespace ImageSharp.Tests.Formats.Jpg |
|||
{ |
|||
using System.Numerics; |
|||
using ImageSharp.Formats; |
|||
using ImageSharp.Formats.Jpg.Utils; |
|||
|
|||
using Xunit; |
|||
using Xunit.Abstractions; |
|||
|
|||
public class ReferenceImplementationsTests : UtilityTestClassBase |
|||
{ |
|||
public ReferenceImplementationsTests(ITestOutputHelper output) |
|||
: base(output) |
|||
{ |
|||
} |
|||
|
|||
|
|||
[Theory] |
|||
[InlineData(42)] |
|||
[InlineData(1)] |
|||
[InlineData(2)] |
|||
public void Idct_FloatingPointReferenceImplementation_IsEquivalentToIntegerImplementation(int seed) |
|||
{ |
|||
MutableSpan<int> intData = Create8x8RandomIntData(-200, 200, seed); |
|||
MutableSpan<float> floatSrc = intData.ConvertToFloat32MutableSpan(); |
|||
|
|||
ReferenceImplementations.IntegerReferenceDCT.TransformIDCTInplace(intData); |
|||
|
|||
MutableSpan<float> dest = new MutableSpan<float>(64); |
|||
MutableSpan<float> temp = new MutableSpan<float>(64); |
|||
|
|||
ReferenceImplementations.iDCT2D_llm(floatSrc, dest, temp); |
|||
|
|||
for (int i = 0; i < 64; i++) |
|||
{ |
|||
float expected = intData[i]; |
|||
float actual = dest[i]; |
|||
|
|||
Assert.Equal(expected, actual, new ApproximateFloatComparer(1f)); |
|||
} |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(42, 0)] |
|||
[InlineData(1, 0)] |
|||
[InlineData(2, 0)] |
|||
public void IntegerDCT_ForwardThenInverse(int seed, int startAt) |
|||
{ |
|||
MutableSpan<int> original = Create8x8RandomIntData(-200, 200, seed); |
|||
|
|||
var block = original.AddScalarToAllValues(128); |
|||
|
|||
ReferenceImplementations.IntegerReferenceDCT.TransformFDCTInplace(block); |
|||
|
|||
for (int i = 0; i < 64; i++) |
|||
{ |
|||
block[i] /= 8; |
|||
} |
|||
|
|||
ReferenceImplementations.IntegerReferenceDCT.TransformIDCTInplace(block); |
|||
|
|||
for (int i = startAt; i < 64; i++) |
|||
{ |
|||
float expected = original[i]; |
|||
float actual = (float)block[i]; |
|||
|
|||
Assert.Equal(expected, actual, new ApproximateFloatComparer(3f)); |
|||
} |
|||
|
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(42, 0)] |
|||
[InlineData(1, 0)] |
|||
[InlineData(2, 0)] |
|||
public void FloatingPointDCT_ReferenceImplementation_ForwardThenInverse(int seed, int startAt) |
|||
{ |
|||
var data = Create8x8RandomIntData(-200, 200, seed); |
|||
MutableSpan<float> src = new MutableSpan<int>(data).ConvertToFloat32MutableSpan(); |
|||
MutableSpan<float> dest = new MutableSpan<float>(64); |
|||
MutableSpan<float> temp = new MutableSpan<float>(64); |
|||
|
|||
ReferenceImplementations.fDCT2D_llm(src, dest, temp, true); |
|||
ReferenceImplementations.iDCT2D_llm(dest, src, temp); |
|||
|
|||
for (int i = startAt; i < 64; i++) |
|||
{ |
|||
float expected = data[i]; |
|||
float actual = (float)src[i]; |
|||
|
|||
Assert.Equal(expected, actual, new ApproximateFloatComparer(2f)); |
|||
} |
|||
} |
|||
|
|||
[Fact] |
|||
public void HowMuchIsTheFish() |
|||
{ |
|||
Output.WriteLine(Vector<int>.Count.ToString()); |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(42)] |
|||
[InlineData(1)] |
|||
[InlineData(2)] |
|||
public void Fdct_FloatingPointReferenceImplementation_IsEquivalentToIntegerImplementation(int seed) |
|||
{ |
|||
MutableSpan<int> intData = Create8x8RandomIntData(-200, 200, seed); |
|||
MutableSpan<float> floatSrc = intData.ConvertToFloat32MutableSpan(); |
|||
|
|||
ReferenceImplementations.IntegerReferenceDCT.TransformFDCTInplace(intData); |
|||
|
|||
MutableSpan<float> dest = new MutableSpan<float>(64); |
|||
MutableSpan<float> temp = new MutableSpan<float>(64); |
|||
|
|||
ReferenceImplementations.fDCT2D_llm(floatSrc, dest, temp, offsetSourceByNeg128: true); |
|||
|
|||
for (int i = 0; i < 64; i++) |
|||
{ |
|||
float expected = intData[i]; |
|||
float actual = dest[i]; |
|||
|
|||
Assert.Equal(expected, actual, new ApproximateFloatComparer(1f)); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace ImageSharp.Tests |
|||
{ |
|||
using Xunit; |
|||
using Xunit.Abstractions; |
|||
|
|||
public class HelloTest |
|||
{ |
|||
private ITestOutputHelper output; |
|||
|
|||
public HelloTest(ITestOutputHelper output) |
|||
{ |
|||
this.output = output; |
|||
} |
|||
|
|||
[Fact] |
|||
public void HelloFoo() |
|||
{ |
|||
TestFile file = TestFile.Create(TestImages.Jpeg.Calliphora); |
|||
var img = file.CreateImage(); |
|||
this.output.WriteLine(img.Width.ToString()); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,179 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
|||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> |
|||
<PropertyGroup> |
|||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> |
|||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> |
|||
<ProjectGuid>{88C5FB74-5845-4CC0-8F1E-A25EBABC95C2}</ProjectGuid> |
|||
<OutputType>Library</OutputType> |
|||
<AppDesignerFolder>Properties</AppDesignerFolder> |
|||
<RootNamespace>ImageSharp.Tests</RootNamespace> |
|||
<AssemblyName>ImageSharp.Tests46</AssemblyName> |
|||
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion> |
|||
<FileAlignment>512</FileAlignment> |
|||
</PropertyGroup> |
|||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> |
|||
<DebugSymbols>true</DebugSymbols> |
|||
<DebugType>full</DebugType> |
|||
<Optimize>false</Optimize> |
|||
<OutputPath>bin\Debug\</OutputPath> |
|||
<DefineConstants>DEBUG;TRACE</DefineConstants> |
|||
<ErrorReport>prompt</ErrorReport> |
|||
<WarningLevel>4</WarningLevel> |
|||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> |
|||
</PropertyGroup> |
|||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> |
|||
<DebugType>pdbonly</DebugType> |
|||
<Optimize>true</Optimize> |
|||
<OutputPath>bin\Release\</OutputPath> |
|||
<DefineConstants>TRACE</DefineConstants> |
|||
<ErrorReport>prompt</ErrorReport> |
|||
<WarningLevel>4</WarningLevel> |
|||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> |
|||
</PropertyGroup> |
|||
<ItemGroup> |
|||
<Reference Include="ImageSharp" Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> |
|||
<HintPath>..\..\src\ImageSharp\bin\Debug\net45\ImageSharp.dll</HintPath> |
|||
</Reference> |
|||
</ItemGroup> |
|||
<ItemGroup> |
|||
<Reference Include="ImageSharp" Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> |
|||
<HintPath>..\..\src\ImageSharp\bin\Release\net45\ImageSharp.dll</HintPath> |
|||
</Reference> |
|||
</ItemGroup> |
|||
<ItemGroup> |
|||
<Reference Include="System" /> |
|||
<Reference Include="System.Core" /> |
|||
<Reference Include="System.Numerics" /> |
|||
<Reference Include="System.Numerics.Vectors, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> |
|||
<HintPath>..\..\packages\System.Numerics.Vectors.4.1.1\lib\net46\System.Numerics.Vectors.dll</HintPath> |
|||
<Private>True</Private> |
|||
</Reference> |
|||
<Reference Include="System.Xml.Linq" /> |
|||
<Reference Include="System.Data.DataSetExtensions" /> |
|||
<Reference Include="Microsoft.CSharp" /> |
|||
<Reference Include="System.Data" /> |
|||
<Reference Include="System.Net.Http" /> |
|||
<Reference Include="System.Xml" /> |
|||
<Reference Include="xunit.abstractions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL"> |
|||
<HintPath>..\..\packages\xunit.abstractions.2.0.1\lib\net35\xunit.abstractions.dll</HintPath> |
|||
<Private>True</Private> |
|||
</Reference> |
|||
<Reference Include="xunit.assert, Version=2.2.0.3444, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL"> |
|||
<HintPath>..\..\packages\xunit.assert.2.2.0-beta4-build3444\lib\netstandard1.0\xunit.assert.dll</HintPath> |
|||
<Private>True</Private> |
|||
</Reference> |
|||
<Reference Include="xunit.core, Version=2.2.0.3444, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL"> |
|||
<HintPath>..\..\packages\xunit.extensibility.core.2.2.0-beta4-build3444\lib\net45\xunit.core.dll</HintPath> |
|||
<Private>True</Private> |
|||
</Reference> |
|||
<Reference Include="xunit.execution.desktop, Version=2.2.0.3444, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL"> |
|||
<HintPath>..\..\packages\xunit.extensibility.execution.2.2.0-beta4-build3444\lib\net45\xunit.execution.desktop.dll</HintPath> |
|||
<Private>True</Private> |
|||
</Reference> |
|||
</ItemGroup> |
|||
<ItemGroup> |
|||
<Compile Include="..\ImageSharp.Tests\Formats\Jpg\Block8x8FTests.cs"> |
|||
<Link>Formats\Jpg\Block8x8FTests.cs</Link> |
|||
</Compile> |
|||
<Compile Include="..\ImageSharp.Tests\Formats\Jpg\JpegTests.cs"> |
|||
<Link>Formats\Jpg\JpegTests.cs</Link> |
|||
</Compile> |
|||
<Compile Include="..\ImageSharp.Tests\Formats\Jpg\ReferenceImplementations.cs"> |
|||
<Link>Formats\Jpg\ReferenceImplementations.cs</Link> |
|||
</Compile> |
|||
<Compile Include="..\ImageSharp.Tests\Formats\Jpg\ReferenceImplementationsTests.cs"> |
|||
<Link>Formats\Jpg\ReferenceImplementationsTests.cs</Link> |
|||
</Compile> |
|||
<Compile Include="..\ImageSharp.Tests\Formats\Jpg\UtilityTestClassBase.cs"> |
|||
<Link>Formats\Jpg\UtilityTestClassBase.cs</Link> |
|||
</Compile> |
|||
<Compile Include="..\ImageSharp.Tests\Image\ImagePropertyTests.cs"> |
|||
<Link>Image\ImagePropertyTests.cs</Link> |
|||
</Compile> |
|||
<Compile Include="..\ImageSharp.Tests\Image\ImageTests.cs"> |
|||
<Link>Image\ImageTests.cs</Link> |
|||
</Compile> |
|||
<Compile Include="..\ImageSharp.Tests\Image\PixelAccessorTests.cs"> |
|||
<Link>Image\PixelAccessorTests.cs</Link> |
|||
</Compile> |
|||
<Compile Include="..\ImageSharp.Tests\TestBase.cs"> |
|||
<Link>TestBase.cs</Link> |
|||
</Compile> |
|||
<Compile Include="..\ImageSharp.Tests\TestImages.cs"> |
|||
<Link>TestImages.cs</Link> |
|||
</Compile> |
|||
<Compile Include="..\ImageSharp.Tests\TestUtilities\Attributes\ImageDataAttributeBase.cs"> |
|||
<Link>TestUtilities\ImageDataAttributeBase.cs</Link> |
|||
</Compile> |
|||
<Compile Include="..\ImageSharp.Tests\TestUtilities\Attributes\WithBlankImageAttribute.cs"> |
|||
<Link>TestUtilities\WithBlankImageAttribute.cs</Link> |
|||
</Compile> |
|||
<Compile Include="..\ImageSharp.Tests\TestUtilities\Attributes\WithFileAttribute.cs"> |
|||
<Link>TestUtilities\WithFileAttribute.cs</Link> |
|||
</Compile> |
|||
<Compile Include="..\ImageSharp.Tests\TestUtilities\Attributes\WithFileCollectionAttribute.cs"> |
|||
<Link>TestUtilities\WithFileCollectionAttribute.cs</Link> |
|||
</Compile> |
|||
<Compile Include="..\ImageSharp.Tests\TestUtilities\Attributes\WithMemberFactoryAttribute.cs"> |
|||
<Link>TestUtilities\WithMemberFactoryAttribute.cs</Link> |
|||
</Compile> |
|||
<Compile Include="..\ImageSharp.Tests\TestUtilities\Attributes\WithSolidFilledImagesAttribute.cs"> |
|||
<Link>TestUtilities\WithSolidFilledImagesAttribute.cs</Link> |
|||
</Compile> |
|||
<Compile Include="..\ImageSharp.Tests\TestUtilities\EnumHelper.cs"> |
|||
<Link>TestUtilities\EnumHelper.cs</Link> |
|||
</Compile> |
|||
<Compile Include="..\ImageSharp.Tests\TestUtilities\Factories\GenericFactory.cs"> |
|||
<Link>TestUtilities\GenericFactory.cs</Link> |
|||
</Compile> |
|||
<Compile Include="..\ImageSharp.Tests\TestUtilities\Factories\ImageFactory.cs"> |
|||
<Link>TestUtilities\ImageFactory.cs</Link> |
|||
</Compile> |
|||
<Compile Include="..\ImageSharp.Tests\TestUtilities\ImageProviders\BlankProvider.cs"> |
|||
<Link>TestUtilities\BlankProvider.cs</Link> |
|||
</Compile> |
|||
<Compile Include="..\ImageSharp.Tests\TestUtilities\ImageProviders\FileProvider.cs"> |
|||
<Link>TestUtilities\FileProvider.cs</Link> |
|||
</Compile> |
|||
<Compile Include="..\ImageSharp.Tests\TestUtilities\ImageProviders\LambdaProvider.cs"> |
|||
<Link>TestUtilities\LambdaProvider.cs</Link> |
|||
</Compile> |
|||
<Compile Include="..\ImageSharp.Tests\TestUtilities\ImageProviders\SolidProvider.cs"> |
|||
<Link>TestUtilities\SolidProvider.cs</Link> |
|||
</Compile> |
|||
<Compile Include="..\ImageSharp.Tests\TestUtilities\ImageProviders\TestImageProvider.cs"> |
|||
<Link>TestUtilities\TestImageProvider.cs</Link> |
|||
</Compile> |
|||
<Compile Include="..\ImageSharp.Tests\TestUtilities\ImagingTestCaseUtility.cs"> |
|||
<Link>TestUtilities\ImagingTestCaseUtility.cs</Link> |
|||
</Compile> |
|||
<Compile Include="..\ImageSharp.Tests\TestUtilities\PixelTypes.cs"> |
|||
<Link>TestUtilities\PixelTypes.cs</Link> |
|||
</Compile> |
|||
<Compile Include="..\ImageSharp.Tests\TestUtilities\Tests\TestImageProviderTests.cs"> |
|||
<Link>TestUtilities\TestImageProviderTests.cs</Link> |
|||
</Compile> |
|||
<Compile Include="..\ImageSharp.Tests\TestUtilities\Tests\TestUtilityExtensionsTests.cs"> |
|||
<Link>TestUtilities\TestUtilityExtensionsTests.cs</Link> |
|||
</Compile> |
|||
<Compile Include="..\ImageSharp.Tests\TestUtilities\TestUtilityExtensions.cs"> |
|||
<Link>TestUtilities\TestUtilityExtensions.cs</Link> |
|||
</Compile> |
|||
<Compile Include="HelloTest.cs" /> |
|||
<Compile Include="Properties\AssemblyInfo.cs" /> |
|||
<Compile Include="TestFile.cs" /> |
|||
</ItemGroup> |
|||
<ItemGroup> |
|||
<None Include="packages.config" /> |
|||
</ItemGroup> |
|||
<ItemGroup /> |
|||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> |
|||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. |
|||
Other similar extension points exist, see Microsoft.Common.targets. |
|||
<Target Name="BeforeBuild"> |
|||
</Target> |
|||
<Target Name="AfterBuild"> |
|||
</Target> |
|||
--> |
|||
</Project> |
|||
@ -0,0 +1,36 @@ |
|||
using System.Reflection; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
|
|||
// General Information about an assembly is controlled through the following
|
|||
// set of attributes. Change these attribute values to modify the information
|
|||
// associated with an assembly.
|
|||
[assembly: AssemblyTitle("ImageSharp.Tests46")] |
|||
[assembly: AssemblyDescription("")] |
|||
[assembly: AssemblyConfiguration("")] |
|||
[assembly: AssemblyCompany("Sapa")] |
|||
[assembly: AssemblyProduct("ImageSharp.Tests46")] |
|||
[assembly: AssemblyCopyright("Copyright © Sapa 2016")] |
|||
[assembly: AssemblyTrademark("")] |
|||
[assembly: AssemblyCulture("")] |
|||
|
|||
// Setting ComVisible to false makes the types in this assembly not visible
|
|||
// to COM components. If you need to access a type in this assembly from
|
|||
// COM, set the ComVisible attribute to true on that type.
|
|||
[assembly: ComVisible(false)] |
|||
|
|||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
|||
[assembly: Guid("88c5fb74-5845-4cc0-8f1e-a25ebabc95c2")] |
|||
|
|||
// Version information for an assembly consists of the following four values:
|
|||
//
|
|||
// Major Version
|
|||
// Minor Version
|
|||
// Build Number
|
|||
// Revision
|
|||
//
|
|||
// You can specify all the values or you can default the Build and Revision Numbers
|
|||
// by using the '*' as shown below:
|
|||
// [assembly: AssemblyVersion("1.0.*")]
|
|||
[assembly: AssemblyVersion("1.0.0.0")] |
|||
[assembly: AssemblyFileVersion("1.0.0.0")] |
|||
@ -0,0 +1,73 @@ |
|||
// <copyright file="TestImage.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
namespace ImageSharp.Tests |
|||
{ |
|||
using System; |
|||
using System.Collections.Concurrent; |
|||
using System.IO; |
|||
|
|||
public class TestFile |
|||
{ |
|||
private static readonly ConcurrentDictionary<string, TestFile> cache = new ConcurrentDictionary<string, TestFile>(); |
|||
private static readonly string FormatsDirectory = GetFormatsDirectory(); |
|||
|
|||
private static string GetFormatsDirectory() |
|||
{ |
|||
return "../../../ImageSharp.Tests/TestImages/Formats/"; |
|||
} |
|||
|
|||
private readonly Image image; |
|||
private readonly string file; |
|||
|
|||
private TestFile(string file) |
|||
{ |
|||
this.file = file; |
|||
|
|||
this.Bytes = File.ReadAllBytes(file); |
|||
this.image = new Image(this.Bytes); |
|||
} |
|||
|
|||
public static TestFile Create(string file) |
|||
{ |
|||
return cache.GetOrAdd(file, (string fileName) => |
|||
{ |
|||
return new TestFile(FormatsDirectory + fileName); |
|||
}); |
|||
} |
|||
|
|||
public byte[] Bytes { get; } |
|||
|
|||
public string FileName |
|||
{ |
|||
get |
|||
{ |
|||
return Path.GetFileName(this.file); |
|||
} |
|||
} |
|||
|
|||
public string FileNameWithoutExtension |
|||
{ |
|||
get |
|||
{ |
|||
return Path.GetFileNameWithoutExtension(this.file); |
|||
} |
|||
} |
|||
|
|||
public string GetFileName(object value) |
|||
{ |
|||
return this.FileNameWithoutExtension + "-" + value + Path.GetExtension(this.file); |
|||
} |
|||
|
|||
public string GetFileNameWithoutExtension(object value) |
|||
{ |
|||
return this.FileNameWithoutExtension + "-" + value; |
|||
} |
|||
|
|||
public Image CreateImage() |
|||
{ |
|||
return new Image(this.image); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,10 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<packages> |
|||
<package id="System.Numerics.Vectors" version="4.1.1" targetFramework="net461" /> |
|||
<package id="xunit" version="2.2.0-beta4-build3444" targetFramework="net461" /> |
|||
<package id="xunit.abstractions" version="2.0.1" targetFramework="net461" /> |
|||
<package id="xunit.assert" version="2.2.0-beta4-build3444" targetFramework="net461" /> |
|||
<package id="xunit.core" version="2.2.0-beta4-build3444" targetFramework="net461" /> |
|||
<package id="xunit.extensibility.core" version="2.2.0-beta4-build3444" targetFramework="net461" /> |
|||
<package id="xunit.extensibility.execution" version="2.2.0-beta4-build3444" targetFramework="net461" /> |
|||
</packages> |
|||
Loading…
Reference in new issue