mirror of https://github.com/SixLabors/ImageSharp
47 changed files with 3487 additions and 1835 deletions
@ -1,6 +1,6 @@ |
|||||
{ |
{ |
||||
"projects": [ "src" ], |
"projects": [ "src" ], |
||||
"sdk": { |
"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">
|
using System.Collections.Generic; |
||||
// Copyright (c) James Jackson-South and contributors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
// </copyright>
|
|
||||
|
|
||||
namespace ImageSharp.Benchmarks.Image |
namespace ImageSharp.Benchmarks.Image |
||||
{ |
{ |
||||
using System; |
|
||||
using System.Collections.Generic; |
|
||||
using System.Drawing; |
using System.Drawing; |
||||
using System.IO; |
using System.IO; |
||||
using System.Linq; |
|
||||
|
|
||||
using BenchmarkDotNet.Attributes; |
using BenchmarkDotNet.Attributes; |
||||
|
|
||||
using Image = ImageSharp.Image; |
using Image = ImageSharp.Image; |
||||
using ImageSharpSize = ImageSharp.Size; |
using ImageSharpSize = ImageSharp.Size; |
||||
|
|
||||
public class DecodeJpegMultiple |
public class DecodeJpegMultiple : MultiImageBenchmarkBase |
||||
{ |
{ |
||||
private const string Folder = "../ImageSharp.Tests/TestImages/Formats/Jpg/"; |
protected override IEnumerable<string> InputImageSubfolders => new[] |
||||
|
|
||||
private Dictionary<string, byte[]> fileNamesToBytes; |
|
||||
|
|
||||
public enum JpegTestingMode |
|
||||
{ |
{ |
||||
All, |
"Formats/Jpg/" |
||||
|
}; |
||||
SmallImagesOnly, |
|
||||
|
|
||||
LargeImagesOnly, |
|
||||
|
|
||||
CalliphoraOnly, |
protected override IEnumerable<string> FileFilters => new[] { "*.jpg" }; |
||||
} |
|
||||
|
|
||||
[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(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
[Benchmark(Description = "DecodeJpegMultiple - ImageSharp")] |
[Benchmark(Description = "DecodeJpegMultiple - ImageSharp")] |
||||
public ImageSharpSize JpegImageSharp() |
public void DecodeJpegImageSharp() |
||||
{ |
{ |
||||
ImageSharpSize lastSize = new ImageSharpSize(); |
this.ForEachStream( |
||||
foreach (var kv in this.RequestedImages) |
ms => new ImageSharp.Image(ms) |
||||
{ |
); |
||||
using (MemoryStream memoryStream = new MemoryStream(kv.Value)) |
|
||||
{ |
|
||||
Image image = new Image(memoryStream); |
|
||||
lastSize = new ImageSharpSize(image.Width, image.Height); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return lastSize; |
|
||||
} |
} |
||||
|
|
||||
[Benchmark(Baseline = true, Description = "DecodeJpegMultiple - System.Drawing")] |
[Benchmark(Baseline = true, Description = "DecodeJpegMultiple - System.Drawing")] |
||||
public Size JpegSystemDrawing() |
public void DecodeJpegSystemDrawing() |
||||
{ |
{ |
||||
Size lastSize = new Size(); |
this.ForEachStream( |
||||
foreach (var kv in this.RequestedImages) |
System.Drawing.Image.FromStream |
||||
{ |
); |
||||
using (MemoryStream memoryStream = new MemoryStream(kv.Value)) |
|
||||
{ |
|
||||
using (System.Drawing.Image image = System.Drawing.Image.FromStream(memoryStream)) |
|
||||
{ |
|
||||
lastSize = image.Size; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return lastSize; |
|
||||
} |
} |
||||
|
|
||||
[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