diff --git a/src/ImageSharp/Formats/Jpg/Components/Bits.cs b/src/ImageSharp/Formats/Jpg/Components/Bits.cs index 9bec6cd167..975c92aee0 100644 --- a/src/ImageSharp/Formats/Jpg/Components/Bits.cs +++ b/src/ImageSharp/Formats/Jpg/Components/Bits.cs @@ -3,10 +3,10 @@ // Licensed under the Apache License, Version 2.0. // -using System.Runtime.CompilerServices; - namespace ImageSharp.Formats { + using System.Runtime.CompilerServices; + /// /// Holds the unprocessed bits that have been taken from the byte-stream. /// The n least significant bits of a form the unread bits, to be read in MSB to @@ -30,20 +30,20 @@ namespace ImageSharp.Formats /// public int UnreadBits; - /// /// Reads bytes from the byte buffer to ensure that bits.UnreadBits is at /// least n. For best performance (avoiding function calls inside hot loops), /// the caller is the one responsible for first checking that bits.UnreadBits < n. /// /// The number of bits to ensure. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] internal JpegDecoderCore.ErrorCodes EnsureNBits(int n, JpegDecoderCore decoder) { while (true) { JpegDecoderCore.ErrorCodes errorCode; - + byte c = decoder.bytes.ReadByteStuffedByte(decoder.inputStream, out errorCode); if (errorCode != JpegDecoderCore.ErrorCodes.NoError) @@ -51,40 +51,39 @@ namespace ImageSharp.Formats return errorCode; } - Accumulator = (Accumulator << 8) | c; - UnreadBits += 8; - if (Mask == 0) + this.Accumulator = (this.Accumulator << 8) | c; + this.UnreadBits += 8; + if (this.Mask == 0) { - Mask = 1 << 7; + this.Mask = 1 << 7; } else { - Mask <<= 8; + this.Mask <<= 8; } - if (UnreadBits >= n) + if (this.UnreadBits >= n) { return JpegDecoderCore.ErrorCodes.NoError; - //break; } } } - + internal int ReceiveExtend(byte t, JpegDecoderCore decoder) { - if (UnreadBits < t) + if (this.UnreadBits < t) { - var errorCode = EnsureNBits(t, decoder); + var errorCode = this.EnsureNBits(t, decoder); if (errorCode != JpegDecoderCore.ErrorCodes.NoError) { throw new JpegDecoderCore.MissingFF00Exception(); } } - UnreadBits -= t; - Mask >>= t; + this.UnreadBits -= t; + this.Mask >>= t; int s = 1 << t; - int x = (int)((Accumulator >> UnreadBits) & (s - 1)); + int x = (int)((this.Accumulator >> this.UnreadBits) & (s - 1)); if (x < (s >> 1)) { @@ -93,7 +92,5 @@ namespace ImageSharp.Formats return x; } - - } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpg/Components/Block.cs b/src/ImageSharp/Formats/Jpg/Components/Block.cs index 111a1cac57..4d4ad4d81e 100644 --- a/src/ImageSharp/Formats/Jpg/Components/Block.cs +++ b/src/ImageSharp/Formats/Jpg/Components/Block.cs @@ -2,13 +2,12 @@ // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // - -using System; -using System.Buffers; -using System.Runtime.CompilerServices; - namespace ImageSharp.Formats { + using System; + using System.Buffers; + using System.Runtime.CompilerServices; + /// /// Represents an 8x8 block of coefficients to transform and encode. /// @@ -26,17 +25,9 @@ namespace ImageSharp.Formats /// public int[] Data; - /// - /// Initializes a new instance of the class. - /// - //public Block() - //{ - // this.data = new int[BlockSize]; - //} - public void Init() { - //this.Data = new int[BlockSize]; + // this.Data = new int[BlockSize]; this.Data = ArrayPool.Rent(BlockSize); } @@ -54,6 +45,7 @@ namespace ImageSharp.Formats { result[i].Init(); } + return result; } @@ -69,18 +61,25 @@ namespace ImageSharp.Formats public int this[int index] { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get { return this.Data[index]; } + get + { + return this.Data[index]; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] - set { this.Data[index] = value; } + set + { + this.Data[index] = value; + } } // TODO: Refactor Block.Dispose() callers to always use 'using' or 'finally' statement! public void Dispose() { - if (Data != null) + if (this.Data != null) { - ArrayPool.Return(Data, true); - Data = null; + ArrayPool.Return(this.Data, true); + this.Data = null; } } @@ -92,24 +91,24 @@ namespace ImageSharp.Formats } } - public void Clear() { - for (int i = 0; i < Data.Length; i++) + for (int i = 0; i < this.Data.Length; i++) { - Data[i] = 0; + this.Data[i] = 0; } } public Block Clone() { Block clone = Create(); - Array.Copy(Data, clone.Data, BlockSize); + Array.Copy(this.Data, clone.Data, BlockSize); return clone; } } /// + /// TODO: Should be removed, when JpegEncoderCore is refactored to use Block8x8F /// Temporal class to make refactoring easier. /// 1. Refactor Block -> BlockF /// 2. Test @@ -120,7 +119,7 @@ namespace ImageSharp.Formats private static readonly ArrayPool ArrayPool = ArrayPool.Create(BlockSize, 50); /// - /// Gets the size of the block. + /// Size of the block. /// public const int BlockSize = 64; @@ -129,17 +128,9 @@ namespace ImageSharp.Formats /// public float[] Data; - /// - /// Initializes a new instance of the class. - /// - //public Block() - //{ - // this.data = new int[BlockSize]; - //} - public void Init() { - //this.Data = new int[BlockSize]; + // this.Data = new int[BlockSize]; this.Data = ArrayPool.Rent(BlockSize); } @@ -157,6 +148,7 @@ namespace ImageSharp.Formats { result[i].Init(); } + return result; } @@ -172,18 +164,25 @@ namespace ImageSharp.Formats public float this[int index] { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get { return this.Data[index]; } + get + { + return this.Data[index]; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] - set { this.Data[index] = value; } + set + { + this.Data[index] = value; + } } // TODO: Refactor Block.Dispose() callers to always use 'using' or 'finally' statement! public void Dispose() { - if (Data != null) + if (this.Data != null) { - ArrayPool.Return(Data, true); - Data = null; + ArrayPool.Return(this.Data, true); + this.Data = null; } } @@ -195,22 +194,19 @@ namespace ImageSharp.Formats } } - public void Clear() { - for (int i = 0; i < Data.Length; i++) + for (int i = 0; i < this.Data.Length; i++) { - Data[i] = 0; + this.Data[i] = 0; } } public BlockF Clone() { BlockF clone = Create(); - Array.Copy(Data, clone.Data, BlockSize); + Array.Copy(this.Data, clone.Data, BlockSize); return clone; } } - - -} +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpg/Components/Block8x8F.Generated.cs b/src/ImageSharp/Formats/Jpg/Components/Block8x8F.Generated.cs index 2fec402d62..3b48dc572d 100644 --- a/src/ImageSharp/Formats/Jpg/Components/Block8x8F.Generated.cs +++ b/src/ImageSharp/Formats/Jpg/Components/Block8x8F.Generated.cs @@ -1,4 +1,5 @@ - +// + using System; using System.Numerics; using System.Runtime.CompilerServices; @@ -8,6 +9,14 @@ namespace ImageSharp.Formats { internal partial struct Block8x8F { + private static readonly Vector4 CMin4 = new Vector4(-128f); + private static readonly Vector4 CMax4 = new Vector4(127f); + private static readonly Vector4 COff4 = new Vector4(128f); + + /// + /// Transpose the block into d + /// + /// Destination [MethodImpl(MethodImplOptions.AggressiveInlining)] public void TransposeInto(ref Block8x8F d) { @@ -21,24 +30,12 @@ namespace ImageSharp.Formats d.V0R.W = V7L.X; d.V1R.W = V7L.Y; d.V2R.W = V7L.Z; d.V3R.W = V7L.W; d.V4R.W = V7R.X; d.V5R.W = V7R.Y; d.V6R.W = V7R.Z; d.V7R.W = V7R.W; } - - public void CropInto(float min, float max, ref Block8x8F d) - { - Vector4 minVec = new Vector4(min); - Vector4 maxVec = new Vector4(max); - - d.V0L = Vector4.Max(Vector4.Min(V0L, maxVec), minVec);d.V0R = Vector4.Max(Vector4.Min(V0R, maxVec), minVec); - d.V1L = Vector4.Max(Vector4.Min(V1L, maxVec), minVec);d.V1R = Vector4.Max(Vector4.Min(V1R, maxVec), minVec); - d.V2L = Vector4.Max(Vector4.Min(V2L, maxVec), minVec);d.V2R = Vector4.Max(Vector4.Min(V2R, maxVec), minVec); - d.V3L = Vector4.Max(Vector4.Min(V3L, maxVec), minVec);d.V3R = Vector4.Max(Vector4.Min(V3R, maxVec), minVec); - d.V4L = Vector4.Max(Vector4.Min(V4L, maxVec), minVec);d.V4R = Vector4.Max(Vector4.Min(V4R, maxVec), minVec); - d.V5L = Vector4.Max(Vector4.Min(V5L, maxVec), minVec);d.V5R = Vector4.Max(Vector4.Min(V5R, maxVec), minVec); - d.V6L = Vector4.Max(Vector4.Min(V6L, maxVec), minVec);d.V6R = Vector4.Max(Vector4.Min(V6R, maxVec), minVec); - d.V7L = Vector4.Max(Vector4.Min(V7L, maxVec), minVec);d.V7R = Vector4.Max(Vector4.Min(V7R, maxVec), minVec); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void ColorifyInto(ref Block8x8F d) + /// + /// Level shift by +128, clip to [0, 255] + /// + /// Destination + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void TransformByteConvetibleColorValuesInto(ref Block8x8F d) { d.V0L = Vector4.Max(Vector4.Min(V0L, CMax4), CMin4) + COff4;d.V0R = Vector4.Max(Vector4.Min(V0R, CMax4), CMin4) + COff4; d.V1L = Vector4.Max(Vector4.Min(V1L, CMax4), CMin4) + COff4;d.V1R = Vector4.Max(Vector4.Min(V1R, CMax4), CMin4) + COff4; diff --git a/src/ImageSharp/Formats/Jpg/Components/Block8x8F.Generated.tt b/src/ImageSharp/Formats/Jpg/Components/Block8x8F.Generated.tt index 810c4e9047..951d62d914 100644 --- a/src/ImageSharp/Formats/Jpg/Components/Block8x8F.Generated.tt +++ b/src/ImageSharp/Formats/Jpg/Components/Block8x8F.Generated.tt @@ -4,19 +4,28 @@ <#@ import namespace="System.Text" #> <#@ import namespace="System.Collections.Generic" #> <#@ output extension=".cs" #> +// using System; using System.Numerics; using System.Runtime.CompilerServices; <# -char[] coordz = new[] {'X', 'Y', 'Z', 'W'}; +char[] coordz = {'X', 'Y', 'Z', 'W'}; #> namespace ImageSharp.Formats { internal partial struct Block8x8F { + private static readonly Vector4 CMin4 = new Vector4(-128f); + private static readonly Vector4 CMax4 = new Vector4(127f); + private static readonly Vector4 COff4 = new Vector4(128f); + + /// + /// Transpose the block into d + /// + /// Destination [MethodImpl(MethodImplOptions.AggressiveInlining)] public void TransposeInto(ref Block8x8F d) { @@ -34,33 +43,7 @@ namespace ImageSharp.Formats char srcSide = (j / 4) % 2 == 0 ? 'L' : 'R'; string expression = $"d.V{j}{destSide}.{destCoord} = V{i}{srcSide}.{srcCoord}; "; - //bld.Append(expression); Write(expression); - } - //bld.AppendLine(); - WriteLine(""); - } - PopIndent(); - //Write(bld.ToString()); - #> - } - - - public void CropInto(float min, float max, ref Block8x8F d) - { - Vector4 minVec = new Vector4(min); - Vector4 maxVec = new Vector4(max); - - <# - - PushIndent(" "); - - for (int i = 0; i < 8; i++) - { - for (int j = 0; j < 2; j++) - { - char side = j == 0 ? 'L' : 'R'; - Write($"d.V{i}{side} = Vector4.Max(Vector4.Min(V{i}{side}, maxVec), minVec);"); } WriteLine(""); } @@ -68,8 +51,12 @@ namespace ImageSharp.Formats #> } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void ColorifyInto(ref Block8x8F d) + /// + /// Level shift by +128, clip to [0, 255] + /// + /// Destination + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void TransformByteConvetibleColorValuesInto(ref Block8x8F d) { <# diff --git a/src/ImageSharp/Formats/Jpg/Components/Block8x8F.cs b/src/ImageSharp/Formats/Jpg/Components/Block8x8F.cs index 4b9830aa73..6fefb86965 100644 --- a/src/ImageSharp/Formats/Jpg/Components/Block8x8F.cs +++ b/src/ImageSharp/Formats/Jpg/Components/Block8x8F.cs @@ -1,13 +1,15 @@ -using System; -using System.Buffers; -using System.Numerics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// // ReSharper disable InconsistentNaming - namespace ImageSharp.Formats { + using System; + using System.Numerics; + using System.Runtime.CompilerServices; + using System.Runtime.InteropServices; + /// /// DCT code Ported from https://github.com/norishigefukushima/dct_simd /// @@ -37,251 +39,193 @@ namespace ImageSharp.Formats public Vector4 V7L; public Vector4 V7R; - public const int VectorCount = 16; public const int ScalarCount = VectorCount*4; - private static readonly ArrayPool ScalarArrayPool = ArrayPool.Create(ScalarCount, 50); - + /// + /// Load raw 32bit floating point data from source + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void LoadFrom(MutableSpan source) { - fixed (Vector4* ptr = &V0L) + fixed (void* ptr = &this.V0L) { - Marshal.Copy(source.Data, source.Offset, (IntPtr) ptr, ScalarCount); - //float* fp = (float*)ptr; - //for (int i = 0; i < ScalarCount; i++) - //{ - // fp[i] = source[i]; - //} + Marshal.Copy(source.Data, source.Offset, (IntPtr)ptr, ScalarCount); } } + /// + /// Load raw 32bit floating point data from source + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe void CopyTo(MutableSpan dest) + public static unsafe void LoadFrom(Block8x8F* blockPtr, MutableSpan source) { - fixed (Vector4* ptr = &V0L) + Marshal.Copy(source.Data, source.Offset, (IntPtr)blockPtr, ScalarCount); + } + + /// + /// Load raw 32bit floating point data from source + /// + internal unsafe void LoadFrom(MutableSpan source) + { + fixed (Vector4* ptr = &this.V0L) { - Marshal.Copy((IntPtr) ptr, dest.Data, dest.Offset, ScalarCount); + float* fp = (float*)ptr; + for (int i = 0; i < ScalarCount; i++) + { + fp[i] = source[i]; + } } } - + /// + /// Copy raw 32bit floating point data to dest + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe void CopyTo(float[] dest) + public unsafe void CopyTo(MutableSpan dest) { - fixed (Vector4* ptr = &V0L) + fixed (void* ptr = &this.V0L) { - Marshal.Copy((IntPtr) ptr, dest, 0, ScalarCount); + Marshal.Copy((IntPtr)ptr, dest.Data, dest.Offset, ScalarCount); } } - + /// + /// Copy raw 32bit floating point data to dest + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe void LoadFrom(Block8x8F* blockPtr, MutableSpan source) + public unsafe void CopyTo(float[] dest) { - Marshal.Copy(source.Data, source.Offset, (IntPtr) blockPtr, ScalarCount); + fixed (void* ptr = &this.V0L) + { + Marshal.Copy((IntPtr)ptr, dest, 0, ScalarCount); + } } + /// + /// Copy raw 32bit floating point data to dest + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void CopyTo(Block8x8F* blockPtr, MutableSpan dest) { - Marshal.Copy((IntPtr) blockPtr, dest.Data, dest.Offset, ScalarCount); - } - - - internal unsafe void LoadFrom(MutableSpan source) - { - fixed (Vector4* ptr = &V0L) - { - float* fp = (float*) ptr; - for (int i = 0; i < ScalarCount; i++) - { - fp[i] = source[i]; - } - } + Marshal.Copy((IntPtr)blockPtr, dest.Data, dest.Offset, ScalarCount); } + /// + /// Copy raw 32bit floating point data to dest + /// internal unsafe void CopyTo(MutableSpan dest) { - fixed (Vector4* ptr = &V0L) + fixed (Vector4* ptr = &this.V0L) { - float* fp = (float*) ptr; + float* fp = (float*)ptr; for (int i = 0; i < ScalarCount; i++) { - dest[i] = (int) fp[i]; + dest[i] = (int)fp[i]; } } } - public unsafe void TransposeInplace() - { - fixed (Vector4* ptr = &V0L) - { - float* data = (float*) ptr; - - for (int i = 1; i < 8; i++) - { - int i8 = i*8; - for (int j = 0; j < i; j++) - { - float tmp = data[i8 + j]; - data[i8 + j] = data[j*8 + i]; - data[j*8 + i] = tmp; - } - } - } - - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public void MultiplyAllInplace(Vector4 s) { - V0L *= s; - V0R *= s; - V1L *= s; - V1R *= s; - V2L *= s; - V2R *= s; - V3L *= s; - V3R *= s; - V4L *= s; - V4R *= s; - V5L *= s; - V5R *= s; - V6L *= s; - V6R *= s; - V7L *= s; - V7R *= s; + this.V0L *= s; + this.V0R *= s; + this.V1L *= s; + this.V1R *= s; + this.V2L *= s; + this.V2R *= s; + this.V3L *= s; + this.V3R *= s; + this.V4L *= s; + this.V4R *= s; + this.V5L *= s; + this.V5R *= s; + this.V6L *= s; + this.V6R *= s; + this.V7L *= s; + this.V7R *= s; } - // ReSharper disable once InconsistentNaming - public void IDCTInto(ref Block8x8F dest, ref Block8x8F temp) + /// + /// Apply floating point IDCT transformation into dest, using a temporary block 'temp' provided by the caller (optimization) + /// + /// Destination + /// Temporary block provided by the caller + public void TransformIDCTInto(ref Block8x8F dest, ref Block8x8F temp) { - TransposeInto(ref temp); - temp.iDCT2D8x4_LeftPart(ref dest); - temp.iDCT2D8x4_RightPart(ref dest); + this.TransposeInto(ref temp); + temp.IDCT8x4_LeftPart(ref dest); + temp.IDCT8x4_RightPart(ref dest); dest.TransposeInto(ref temp); - temp.iDCT2D8x4_LeftPart(ref dest); - temp.iDCT2D8x4_RightPart(ref dest); - - dest.MultiplyAllInplace(_0_125); - } + temp.IDCT8x4_LeftPart(ref dest); + temp.IDCT8x4_RightPart(ref dest); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void IDCTInplace() - { - Block8x8F result = new Block8x8F(); - Block8x8F temp = new Block8x8F(); - IDCTInto(ref result, ref temp); - this = result; + dest.MultiplyAllInplace(c_0_125); } - private static readonly Vector4 _1_175876 = new Vector4(1.175876f); - private static readonly Vector4 _1_961571 = new Vector4(-1.961571f); - private static readonly Vector4 _0_390181 = new Vector4(-0.390181f); - private static readonly Vector4 _0_899976 = new Vector4(-0.899976f); - private static readonly Vector4 _2_562915 = new Vector4(-2.562915f); - private static readonly Vector4 _0_298631 = new Vector4(0.298631f); - private static readonly Vector4 _2_053120 = new Vector4(2.053120f); - private static readonly Vector4 _3_072711 = new Vector4(3.072711f); - private static readonly Vector4 _1_501321 = new Vector4(1.501321f); - private static readonly Vector4 _0_541196 = new Vector4(0.541196f); - private static readonly Vector4 _1_847759 = new Vector4(-1.847759f); - private static readonly Vector4 _0_765367 = new Vector4(0.765367f); - private static readonly Vector4 _0_125 = new Vector4(0.1250f); + 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); + /// + /// Do IDCT internal operations on the left part of the block. Original source: + /// https://github.com/norishigefukushima/dct_simd/blob/master/dct/dct8x8_simd.cpp#L261 + /// + /// Destination block [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void iDCT2D8x4_LeftPart(ref Block8x8F d) + internal void IDCT8x4_LeftPart(ref Block8x8F d) { - /* - float a0,a1,a2,a3,b0,b1,b2,b3; float z0,z1,z2,z3,z4; float r[8]; int i; - for(i = 0;i < 8;i++){ r[i] = (float)(cos((double)i / 16.0 * M_PI) * M_SQRT2); } - */ - /* - 0: 1.414214 - 1: 1.387040 - 2: 1.306563 - 3: - 4: 1.000000 - 5: 0.785695 - 6: - 7: 0.275899 - */ - - Vector4 my1 = V1L; - Vector4 my7 = V7L; + Vector4 my1 = this.V1L; + Vector4 my7 = this.V7L; Vector4 mz0 = my1 + my7; - Vector4 my3 = V3L; + Vector4 my3 = this.V3L; Vector4 mz2 = my3 + my7; - Vector4 my5 = V5L; + Vector4 my5 = this.V5L; Vector4 mz1 = my3 + my5; Vector4 mz3 = my1 + my5; - Vector4 mz4 = ((mz0 + mz1)*_1_175876); - //z0 = y[1] + y[7]; z1 = y[3] + y[5]; z2 = y[3] + y[7]; z3 = y[1] + y[5]; - //z4 = (z0 + z1) * r[3]; - - mz2 = mz2*_1_961571 + mz4; - mz3 = mz3*_0_390181 + mz4; - mz0 = mz0*_0_899976; - mz1 = mz1*_2_562915; - - /* - -0.899976 - -2.562915 - -1.961571 - -0.390181 - z0 = z0 * (-r[3] + r[7]); - z1 = z1 * (-r[3] - r[1]); - z2 = z2 * (-r[3] - r[5]) + z4; - z3 = z3 * (-r[3] + r[5]) + z4;*/ - - - Vector4 mb3 = my7*_0_298631 + mz0 + mz2; - Vector4 mb2 = my5*_2_053120 + mz1 + mz3; - Vector4 mb1 = my3*_3_072711 + mz1 + mz2; - Vector4 mb0 = my1*_1_501321 + mz0 + mz3; - - /* - 0.298631 - 2.053120 - 3.072711 - 1.501321 - b3 = y[7] * (-r[1] + r[3] + r[5] - r[7]) + z0 + z2; - b2 = y[5] * ( r[1] + r[3] - r[5] + r[7]) + z1 + z3; - b1 = y[3] * ( r[1] + r[3] + r[5] - r[7]) + z1 + z2; - b0 = y[1] * ( r[1] + r[3] - r[5] - r[7]) + z0 + z3; - */ - - Vector4 my2 = V2L; - Vector4 my6 = V6L; - mz4 = (my2 + my6)*_0_541196; - Vector4 my0 = V0L; - Vector4 my4 = V4L; + 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 = this.V2L; + Vector4 my6 = this.V6L; + mz4 = (my2 + my6) * c_0_541196; + Vector4 my0 = this.V0L; + Vector4 my4 = this.V4L; mz0 = my0 + my4; mz1 = my0 - my4; - mz2 = mz4 + my6*_1_847759; - mz3 = mz4 + my2*_0_765367; + mz2 = mz4 + (my6 * c_1_847759); + mz3 = mz4 + (my2 * c_0_765367); my0 = mz0 + mz3; my3 = mz0 - mz3; my1 = mz1 + mz2; my2 = mz1 - mz2; - /* - 1.847759 - 0.765367 - z4 = (y[2] + y[6]) * r[6]; - z0 = y[0] + y[4]; z1 = y[0] - y[4]; - z2 = z4 - y[6] * (r[2] + r[6]); - z3 = z4 + y[2] * (r[2] - r[6]); - a0 = z0 + z3; a3 = z0 - z3; - a1 = z1 + z2; a2 = z1 - z2; - */ d.V0L = my0 + mb0; d.V7L = my0 - mb0; @@ -291,104 +235,53 @@ namespace ImageSharp.Formats d.V5L = my2 - mb2; d.V3L = my3 + mb3; d.V4L = my3 - mb3; - /* - x[0] = a0 + b0; x[7] = a0 - b0; - x[1] = a1 + b1; x[6] = a1 - b1; - x[2] = a2 + b2; x[5] = a2 - b2; - x[3] = a3 + b3; x[4] = a3 - b3; - for(i = 0;i < 8;i++){ x[i] *= 0.353554f; } - */ } + /// + /// Do IDCT internal operations on the right part of the block. + /// Original source: + /// https://github.com/norishigefukushima/dct_simd/blob/master/dct/dct8x8_simd.cpp#L261 + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void iDCT2D8x4_RightPart(ref Block8x8F d) + internal void IDCT8x4_RightPart(ref Block8x8F d) { - /* - float a0,a1,a2,a3,b0,b1,b2,b3; float z0,z1,z2,z3,z4; float r[8]; int i; - for(i = 0;i < 8;i++){ r[i] = (float)(cos((double)i / 16.0 * M_PI) * M_SQRT2); } - */ - /* - 0: 1.414214 - 1: 1.387040 - 2: 1.306563 - 3: - 4: 1.000000 - 5: 0.785695 - 6: - 7: 0.275899 - */ - - Vector4 my1 = V1R; - Vector4 my7 = V7R; + Vector4 my1 = this.V1R; + Vector4 my7 = this.V7R; Vector4 mz0 = my1 + my7; - Vector4 my3 = V3R; + Vector4 my3 = this.V3R; Vector4 mz2 = my3 + my7; - Vector4 my5 = V5R; + Vector4 my5 = this.V5R; Vector4 mz1 = my3 + my5; Vector4 mz3 = my1 + my5; - Vector4 mz4 = ((mz0 + mz1)*_1_175876); - //z0 = y[1] + y[7]; z1 = y[3] + y[5]; z2 = y[3] + y[7]; z3 = y[1] + y[5]; - //z4 = (z0 + z1) * r[3]; - - mz2 = mz2*_1_961571 + mz4; - mz3 = mz3*_0_390181 + mz4; - mz0 = mz0*_0_899976; - mz1 = mz1*_2_562915; - - /* - -0.899976 - -2.562915 - -1.961571 - -0.390181 - z0 = z0 * (-r[3] + r[7]); - z1 = z1 * (-r[3] - r[1]); - z2 = z2 * (-r[3] - r[5]) + z4; - z3 = z3 * (-r[3] + r[5]) + z4;*/ - - - Vector4 mb3 = my7*_0_298631 + mz0 + mz2; - Vector4 mb2 = my5*_2_053120 + mz1 + mz3; - Vector4 mb1 = my3*_3_072711 + mz1 + mz2; - Vector4 mb0 = my1*_1_501321 + mz0 + mz3; - - /* - 0.298631 - 2.053120 - 3.072711 - 1.501321 - b3 = y[7] * (-r[1] + r[3] + r[5] - r[7]) + z0 + z2; - b2 = y[5] * ( r[1] + r[3] - r[5] + r[7]) + z1 + z3; - b1 = y[3] * ( r[1] + r[3] + r[5] - r[7]) + z1 + z2; - b0 = y[1] * ( r[1] + r[3] - r[5] - r[7]) + z0 + z3; - */ - - Vector4 my2 = V2R; - Vector4 my6 = V6R; - mz4 = (my2 + my6)*_0_541196; - Vector4 my0 = V0R; - Vector4 my4 = V4R; + 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 = this.V2R; + Vector4 my6 = this.V6R; + mz4 = (my2 + my6) * c_0_541196; + Vector4 my0 = this.V0R; + Vector4 my4 = this.V4R; mz0 = my0 + my4; mz1 = my0 - my4; - mz2 = mz4 + my6*_1_847759; - mz3 = mz4 + my2*_0_765367; + mz2 = mz4 + (my6 * c_1_847759); + mz3 = mz4 + (my2 * c_0_765367); my0 = mz0 + mz3; my3 = mz0 - mz3; my1 = mz1 + mz2; my2 = mz1 - mz2; - /* - 1.847759 - 0.765367 - z4 = (y[2] + y[6]) * r[6]; - z0 = y[0] + y[4]; z1 = y[0] - y[4]; - z2 = z4 - y[6] * (r[2] + r[6]); - z3 = z4 + y[2] * (r[2] - r[6]); - a0 = z0 + z3; a3 = z0 - z3; - a1 = z1 + z2; a2 = z1 - z2; - */ d.V0R = my0 + mb0; d.V7R = my0 - mb0; @@ -398,15 +291,7 @@ namespace ImageSharp.Formats d.V5R = my2 - mb2; d.V3R = my3 + mb3; d.V4R = my3 - mb3; - /* - x[0] = a0 + b0; x[7] = a0 - b0; - x[1] = a1 + b1; x[6] = a1 - b1; - x[2] = a2 + b2; x[5] = a2 - b2; - x[3] = a3 + b3; x[4] = a3 - b3; - for(i = 0;i < 8;i++){ x[i] *= 0.353554f; } - */ } - public unsafe float this[int idx] { @@ -415,83 +300,94 @@ namespace ImageSharp.Formats { fixed (Block8x8F* p = &this) { - float* fp = (float*) p; + float* fp = (float*)p; return fp[idx]; } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] set { fixed (Block8x8F* p = &this) { - float* fp = (float*) p; + float* fp = (float*)p; fp[idx] = value; } } } + /// + /// Pointer-based "Indexer" (getter part) + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static unsafe float GetScalarAt(Block8x8F* blockPtr, int idx) { - float* fp = (float*) blockPtr; + float* fp = (float*)blockPtr; return fp[idx]; } + /// + /// Pointer-based "Indexer" (setter part) + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static unsafe void SetScalarAt(Block8x8F* blockPtr, int idx, float value) { - float* fp = (float*) blockPtr; + float* fp = (float*)blockPtr; fp[idx] = value; } + /// + /// Fill the block with defaults (zeroes) + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void Clear() { - this = new Block8x8F(); // LOL C# Plz! + // The cheapest way to do this in C#: + this = new Block8x8F(); } + /// + /// TODO: Should be removed when BlockF goes away + /// + /// internal void LoadFrom(ref BlockF legacyBlock) { - LoadFrom(legacyBlock.Data); + this.LoadFrom(legacyBlock.Data); } + /// + /// TODO: Should be removed when BlockF goes away + /// + /// internal void CopyTo(ref BlockF legacyBlock) { - CopyTo(legacyBlock.Data); + this.CopyTo(legacyBlock.Data); } - - private static readonly Vector4 CMin4 = new Vector4(-128f); - private static readonly Vector4 CMax4 = new Vector4(127f); - private static readonly Vector4 COff4 = new Vector4(128f); - + /// - /// Level shift by +128, clip to [0, 255], and write to buffer. + /// Level shift by +128, clip to [0, 255], and write to buffer. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal unsafe void CopyColorsTo( - MutableSpan buffer, - int stride, - Block8x8F* temp) + internal unsafe void CopyColorsTo(MutableSpan buffer, int stride, Block8x8F* temp) { - ColorifyInto(ref *temp); + this.TransformByteConvetibleColorValuesInto(ref *temp); - float* src = (float*) temp; + float* src = (float*)temp; for (int i = 0; i < 8; i++) { - buffer[0] = (byte) src[0]; - buffer[1] = (byte) src[1]; - buffer[2] = (byte) src[2]; - buffer[3] = (byte) src[3]; - buffer[4] = (byte) src[4]; - buffer[5] = (byte) src[5]; - buffer[6] = (byte) src[6]; - buffer[7] = (byte) src[7]; + buffer[0] = (byte)src[0]; + buffer[1] = (byte)src[1]; + buffer[2] = (byte)src[2]; + buffer[3] = (byte)src[3]; + buffer[4] = (byte)src[4]; + buffer[5] = (byte)src[5]; + buffer[6] = (byte)src[6]; + buffer[7] = (byte)src[7]; buffer.AddOffset(stride); src += 8; } } - [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static unsafe void UnZig(Block8x8F* block, Block8x8F* qt, int* unzigPtr) { diff --git a/src/ImageSharp/Formats/Jpg/Components/Bytes.cs b/src/ImageSharp/Formats/Jpg/Components/Bytes.cs index 3377b61558..32a767d586 100644 --- a/src/ImageSharp/Formats/Jpg/Components/Bytes.cs +++ b/src/ImageSharp/Formats/Jpg/Components/Bytes.cs @@ -2,14 +2,13 @@ // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // - -using System; -using System.Buffers; -using System.IO; -using System.Runtime.CompilerServices; - namespace ImageSharp.Formats { + using System; + using System.Buffers; + using System.IO; + using System.Runtime.CompilerServices; + /// /// Bytes is a byte buffer, similar to a stream, except that it /// has to be able to unread more than 1 byte, due to byte stuffing. @@ -17,25 +16,14 @@ namespace ImageSharp.Formats /// internal struct Bytes : IDisposable { - /// - /// Initializes a new instance of the class. - /// - //public Bytes() - //{ - // this.Buffer = new byte[4096]; - // this.I = 0; - // this.J = 0; - // this.UnreadableBytes = 0; - //} - private static readonly ArrayPool ArrayPool = ArrayPool.Create(4096, 50); + /// + /// Creates a new instance of the , and initializes it's buffer. + /// public static Bytes Create() { - return new Bytes - { - Buffer = ArrayPool.Rent(4096) - }; + return new Bytes { Buffer = ArrayPool.Rent(4096) }; } /// @@ -57,15 +45,14 @@ namespace ImageSharp.Formats public void Dispose() { - if (Buffer!= null) ArrayPool.Return(Buffer); - Buffer = null; + if (this.Buffer != null) ArrayPool.Return(this.Buffer); + this.Buffer = null; } /// /// ReadByteStuffedByte is like ReadByte but is for byte-stuffed Huffman data. /// /// The - //[MethodImpl(MethodImplOptions.AggressiveInlining)] internal byte ReadByteStuffedByte(Stream inputStream, out JpegDecoderCore.ErrorCodes errorCode) { byte x; @@ -87,7 +74,8 @@ namespace ImageSharp.Formats { errorCode = JpegDecoderCore.ErrorCodes.MissingFF00; return 0; - //throw new MissingFF00Exception(); + + // throw new MissingFF00Exception(); } this.I++; @@ -110,7 +98,8 @@ namespace ImageSharp.Formats { errorCode = JpegDecoderCore.ErrorCodes.MissingFF00; return 0; - //throw new MissingFF00Exception(); + + // throw new MissingFF00Exception(); } return JpegConstants.Markers.XFF; @@ -165,6 +154,5 @@ namespace ImageSharp.Formats this.J += n; } - } -} +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpg/Components/Huffman.cs b/src/ImageSharp/Formats/Jpg/Components/Huffman.cs index 345b45e0e0..7a6f031e86 100644 --- a/src/ImageSharp/Formats/Jpg/Components/Huffman.cs +++ b/src/ImageSharp/Formats/Jpg/Components/Huffman.cs @@ -2,42 +2,30 @@ // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // - -using System; -using System.Buffers; - namespace ImageSharp.Formats { + using System; + using System.Buffers; + /// /// Represents a Huffman tree /// internal struct Huffman : IDisposable { - private static ArrayPool UshortBuffer = - //ArrayPool.Shared; - ArrayPool.Create(1 << JpegDecoderCore.LutSize, 50); + private static ArrayPool UshortBuffer = ArrayPool.Create(1 << JpegDecoderCore.LutSize, 50); - private static ArrayPool ByteBuffer = - //ArrayPool.Shared; - ArrayPool.Create(JpegDecoderCore.MaxNCodes, 50); + private static ArrayPool ByteBuffer = ArrayPool.Create(JpegDecoderCore.MaxNCodes, 50); - private static readonly ArrayPool IntBuffer = - //ArrayPool.Shared; - ArrayPool.Create(JpegDecoderCore.MaxCodeLength, 50); + private static readonly ArrayPool IntBuffer = ArrayPool.Create(JpegDecoderCore.MaxCodeLength, 50); public void Init(int lutSize, int maxNCodes, int maxCodeLength) { - //this.Lut = new ushort[1 << lutSize]; - //this.Values = new byte[maxNCodes]; - //this.MinCodes = new int[maxCodeLength]; - //this.MaxCodes = new int[maxCodeLength]; - //this.Indices = new int[maxCodeLength]; - this.Lut = UshortBuffer.Rent(1 << lutSize); this.Values = ByteBuffer.Rent(maxNCodes); this.MinCodes = IntBuffer.Rent(maxCodeLength); - this.MaxCodes = IntBuffer.Rent(maxCodeLength); ; - this.Indices = IntBuffer.Rent(maxCodeLength); ; + this.MaxCodes = IntBuffer.Rent(maxCodeLength); + this.Indices = IntBuffer.Rent(maxCodeLength); + } /// @@ -77,13 +65,11 @@ namespace ImageSharp.Formats public void Dispose() { - UshortBuffer.Return(Lut, true); - ByteBuffer.Return(Values, true); - IntBuffer.Return(MinCodes, true); - IntBuffer.Return(MaxCodes, true); - IntBuffer.Return(Indices, true); + UshortBuffer.Return(this.Lut, true); + ByteBuffer.Return(this.Values, true); + IntBuffer.Return(this.MinCodes, true); + IntBuffer.Return(this.MaxCodes, true); + IntBuffer.Return(this.Indices, true); } } - - } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpg/Components/IDCT.cs b/src/ImageSharp/Formats/Jpg/Components/IDCT.cs index 88b493c4b4..b3277e0278 100644 --- a/src/ImageSharp/Formats/Jpg/Components/IDCT.cs +++ b/src/ImageSharp/Formats/Jpg/Components/IDCT.cs @@ -3,8 +3,6 @@ // Licensed under the Apache License, Version 2.0. // -using System.Numerics; - namespace ImageSharp.Formats { /// diff --git a/src/ImageSharp/Formats/Jpg/Components/MutableSpan.cs b/src/ImageSharp/Formats/Jpg/Components/MutableSpan.cs index 0cb11690b0..492f28f5b4 100644 --- a/src/ImageSharp/Formats/Jpg/Components/MutableSpan.cs +++ b/src/ImageSharp/Formats/Jpg/Components/MutableSpan.cs @@ -1,52 +1,67 @@ -using System.Buffers; -using System.Numerics; -using System.Runtime.CompilerServices; +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// namespace ImageSharp.Formats { + using System.Numerics; + using System.Runtime.CompilerServices; + /// /// Like corefxlab Span, but with an AddOffset() method for efficiency. /// TODO: When Span will be official, consider replacing this class! /// - /// + /// + /// https://github.com/dotnet/corefxlab/blob/master/src/System.Slices/System/Span.cs + /// /// internal struct MutableSpan { public T[] Data; + public int Offset; - public int TotalCount => Data.Length - Offset; + public int TotalCount => this.Data.Length - this.Offset; public MutableSpan(int size, int offset = 0) { - Data = new T[size]; - Offset = offset; + this.Data = new T[size]; + this.Offset = offset; } public MutableSpan(T[] data, int offset = 0) { - Data = data; - Offset = offset; + this.Data = data; + this.Offset = offset; } public T this[int idx] { - [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return Data[idx + Offset]; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] set { Data[idx + Offset] = value; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return this.Data[idx + this.Offset]; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set + { + this.Data[idx + this.Offset] = value; + } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public MutableSpan Slice(int offset) { - return new MutableSpan(Data, Offset + offset); + return new MutableSpan(this.Data, this.Offset + offset); } public static implicit operator MutableSpan(T[] data) => new MutableSpan(data, 0); - + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AddOffset(int offset) { - Offset += offset; + this.Offset += offset; } } @@ -89,7 +104,5 @@ namespace ImageSharp.Formats data[2] = (int)v.Z; data[3] = (int)v.W; } - - } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs index e66d3ca219..446da29dbd 100644 --- a/src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs @@ -3,14 +3,12 @@ // Licensed under the Apache License, Version 2.0. // -using System.Diagnostics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - namespace ImageSharp.Formats { using System; using System.IO; + using System.Runtime.CompilerServices; + using System.Runtime.InteropServices; using System.Threading.Tasks; /// @@ -71,12 +69,12 @@ namespace ImageSharp.Formats /// value is 16, which means first column (16%8 == 0) and third row (16/8 == 2). /// 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, - }; + { + 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, + }; /// /// The component array @@ -92,7 +90,6 @@ namespace ImageSharp.Formats /// The huffman trees /// //private readonly Huffman[,] huffmanTrees; - private readonly Huffman[] huffmanTrees; /// @@ -223,7 +220,6 @@ namespace ImageSharp.Formats } } - /// /// Decodes the image from the specified this._stream and sets /// the data to image. @@ -234,8 +230,7 @@ namespace ImageSharp.Formats /// The stream, where the image should be. /// Whether to decode metadata only. public void Decode(Image image, Stream stream, bool configOnly) - where TColor : struct, IPackedPixel - where TPacked : struct + where TColor : struct, IPackedPixel where TPacked : struct { this.inputStream = stream; @@ -373,7 +368,8 @@ namespace ImageSharp.Formats this.ProcessApp14Marker(remaining); break; default: - if ((JpegConstants.Markers.APP0 <= marker && marker <= JpegConstants.Markers.APP15) || marker == JpegConstants.Markers.COM) + if ((JpegConstants.Markers.APP0 <= marker && marker <= JpegConstants.Markers.APP15) + || marker == JpegConstants.Markers.COM) { this.Skip(remaining); } @@ -422,7 +418,7 @@ namespace ImageSharp.Formats throw new ImageFormatException("Missing SOS marker."); } } - + /// /// Processes a Define Huffman Table marker, and initializes a huffman /// struct from its contents. Specified in section B.2.4.2. @@ -451,13 +447,12 @@ namespace ImageSharp.Formats throw new ImageFormatException("Bad Th value"); } - ProcessDefineHuffmanTablesMarkerLoop(ref this.huffmanTrees[tc * ThRowSize + th], ref remaining); + this.ProcessDefineHuffmanTablesMarkerLoop(ref this.huffmanTrees[tc * ThRowSize + th], ref remaining); } } private void ProcessDefineHuffmanTablesMarkerLoop(ref Huffman huffman, ref int remaining) { - // Read nCodes and huffman.Valuess (and derive h.Length). // nCodes[i] is the number of codes with code length i. // h.Length is the total number of codes. @@ -552,7 +547,7 @@ namespace ImageSharp.Formats private byte DecodeHuffman(ref Huffman huffman) { // Copy stuff to the stack: - + if (huffman.Length == 0) { throw new ImageFormatException("Uninitialized Huffman table"); @@ -560,35 +555,24 @@ namespace ImageSharp.Formats if (this.bits.UnreadBits < 8) { - //try - //{ - var errorCode = bits.EnsureNBits(8, this); + var errorCode = this.bits.EnsureNBits(8, this); - if (errorCode == ErrorCodes.NoError) - { - ushort v = huffman.Lut[(this.bits.Accumulator >> (this.bits.UnreadBits - LutSize)) & 0xff]; + if (errorCode == ErrorCodes.NoError) + { + ushort v = huffman.Lut[(this.bits.Accumulator >> (this.bits.UnreadBits - LutSize)) & 0xff]; - if (v != 0) - { - byte n = (byte)((v & 0xff) - 1); - this.bits.UnreadBits -= n; - this.bits.Mask >>= n; - return (byte)(v >> 8); - } - } - else + if (v != 0) { - this.UnreadByteStuffedByte(); + byte n = (byte)((v & 0xff) - 1); + this.bits.UnreadBits -= n; + this.bits.Mask >>= n; + return (byte)(v >> 8); } - - //} - //catch (ShortHuffmanDataException) // TODO: This is actually never thrown! - //{ - // if (this.bytes.UnreadableBytes != 0) - // { - // this.UnreadByteStuffedByte(); - // } - //} + } + else + { + this.UnreadByteStuffedByte(); + } } int code = 0; @@ -666,37 +650,6 @@ namespace ImageSharp.Formats return ret; } - /// - /// Fills up the bytes buffer from the underlying stream. - /// It should only be called when there are no unread bytes in bytes. - /// - //private void Fill() - //{ - // if (this.bytes.I != this.bytes.J) - // { - // throw new ImageFormatException("Fill called when unread bytes exist."); - // } - - // // Move the last 2 bytes to the start of the buffer, in case we need - // // to call UnreadByteStuffedByte. - // if (this.bytes.J > 2) - // { - // this.bytes.Buffer[0] = this.bytes.Buffer[this.bytes.J - 2]; - // this.bytes.Buffer[1] = this.bytes.Buffer[this.bytes.J - 1]; - // this.bytes.I = 2; - // this.bytes.J = 2; - // } - - // // Fill in the rest of the buffer. - // int n = this.inputStream.Read(this.bytes.Buffer, this.bytes.J, this.bytes.Buffer.Length - this.bytes.J); - // if (n == 0) - // { - // throw new EOFException(); - // } - - // this.bytes.J += n; - //} - /// /// Undoes the most recent ReadByteStuffedByte call, /// giving a byte of data back from bits to bytes. The Huffman look-up table @@ -716,7 +669,6 @@ namespace ImageSharp.Formats } } - /// /// Returns the next byte, whether buffered or not buffered. It does not care about byte stuffing. /// @@ -724,74 +676,9 @@ namespace ImageSharp.Formats [MethodImpl(MethodImplOptions.AggressiveInlining)] internal byte ReadByte() { - return bytes.ReadByte(inputStream); - //while (this.bytes.I == this.bytes.J) - //{ - // this.Fill(); - //} - - //byte x = this.bytes.Buffer[this.bytes.I]; - //this.bytes.I++; - //this.bytes.UnreadableBytes = 0; - //return x; + return this.bytes.ReadByte(this.inputStream); } - - - /// - /// ReadByteStuffedByte is like ReadByte but is for byte-stuffed Huffman data. - /// - /// The - //internal byte ReadByteStuffedByte(out ErrorCodes errorCode) - //{ - // byte x; - - // errorCode = ErrorCodes.NoError; - - // // Take the fast path if bytes.buf contains at least two bytes. - // if (this.bytes.I + 2 <= this.bytes.J) - // { - // x = this.bytes.Buffer[this.bytes.I]; - // this.bytes.I++; - // this.bytes.UnreadableBytes = 1; - // if (x != JpegConstants.Markers.XFF) - // { - // return x; - // } - - // if (this.bytes.Buffer[this.bytes.I] != 0x00) - // { - // errorCode = ErrorCodes.MissingFF00; - // return 0; - // //throw new MissingFF00Exception(); - // } - - // this.bytes.I++; - // this.bytes.UnreadableBytes = 2; - // return JpegConstants.Markers.XFF; - // } - - // this.bytes.UnreadableBytes = 0; - - // x = this.ReadByte(); - // this.bytes.UnreadableBytes = 1; - // if (x != JpegConstants.Markers.XFF) - // { - // return x; - // } - - // x = this.ReadByte(); - // this.bytes.UnreadableBytes = 2; - // if (x != 0x00) - // { - // errorCode = ErrorCodes.MissingFF00; - // return 0; - // //throw new MissingFF00Exception(); - // } - - // return JpegConstants.Markers.XFF; - //} - /// /// Reads exactly length bytes into data. It does not care about byte stuffing. /// @@ -805,7 +692,7 @@ namespace ImageSharp.Formats { if (this.bits.UnreadBits >= 8) { - UnreadByteStuffedByte(); + this.UnreadByteStuffedByte(); } this.bytes.UnreadableBytes = 0; @@ -826,7 +713,7 @@ namespace ImageSharp.Formats length -= this.bytes.J - this.bytes.I; this.bytes.I += this.bytes.J - this.bytes.I; - this.bytes.Fill(inputStream); + this.bytes.Fill(this.inputStream); } } } @@ -863,7 +750,7 @@ namespace ImageSharp.Formats break; } - this.bytes.Fill(inputStream); + this.bytes.Fill(this.inputStream); } } @@ -988,7 +875,8 @@ namespace ImageSharp.Formats case 1: { // Cb. - if (this.componentArray[0].HorizontalFactor % h != 0 || this.componentArray[0].VerticalFactor % v != 0) + if (this.componentArray[0].HorizontalFactor % h != 0 + || this.componentArray[0].VerticalFactor % v != 0) { throw new ImageFormatException("Unsupported subsampling ratio"); } @@ -999,7 +887,8 @@ namespace ImageSharp.Formats case 2: { // Cr. - if (this.componentArray[1].HorizontalFactor != h || this.componentArray[1].VerticalFactor != v) + if (this.componentArray[1].HorizontalFactor != h + || this.componentArray[1].VerticalFactor != v) { throw new ImageFormatException("Unsupported subsampling ratio"); } @@ -1039,7 +928,8 @@ namespace ImageSharp.Formats break; case 3: - if (this.componentArray[0].HorizontalFactor != h || this.componentArray[0].VerticalFactor != v) + if (this.componentArray[0].HorizontalFactor != h + || this.componentArray[0].VerticalFactor != v) { throw new ImageFormatException("Unsupported subsampling ratio"); } @@ -1157,11 +1047,8 @@ namespace ImageSharp.Formats remaining -= 13; // TODO: We should be using constants for this. - this.isJfif = this.temp[0] == 'J' && - this.temp[1] == 'F' && - this.temp[2] == 'I' && - this.temp[3] == 'F' && - this.temp[4] == '\x00'; + this.isJfif = this.temp[0] == 'J' && this.temp[1] == 'F' && this.temp[2] == 'I' && this.temp[3] == 'F' + && this.temp[4] == '\x00'; if (this.isJfif) { @@ -1183,8 +1070,7 @@ namespace ImageSharp.Formats /// The remaining bytes in the segment block. /// The image. private void ProcessApp1Marker(int remaining, Image image) - where TColor : struct, IPackedPixel - where TPacked : struct + where TColor : struct, IPackedPixel where TPacked : struct { if (remaining < 6) { @@ -1195,12 +1081,8 @@ namespace ImageSharp.Formats byte[] profile = new byte[remaining]; this.ReadFull(profile, 0, remaining); - if (profile[0] == 'E' && - profile[1] == 'x' && - profile[2] == 'i' && - profile[3] == 'f' && - profile[4] == '\0' && - profile[5] == '\0') + if (profile[0] == 'E' && profile[1] == 'x' && profile[2] == 'i' && profile[3] == 'f' && profile[4] == '\0' + && profile[5] == '\0') { image.ExifProfile = new ExifProfile(profile); } @@ -1223,11 +1105,8 @@ namespace ImageSharp.Formats this.ReadFull(this.temp, 0, 12); remaining -= 12; - if (this.temp[0] == 'A' && - this.temp[1] == 'd' && - this.temp[2] == 'o' && - this.temp[3] == 'b' && - this.temp[4] == 'e') + if (this.temp[0] == 'A' && this.temp[1] == 'd' && this.temp[2] == 'o' && this.temp[3] == 'b' + && this.temp[4] == 'e') { this.adobeTransformValid = true; this.adobeTransform = this.temp[11]; @@ -1248,12 +1127,12 @@ namespace ImageSharp.Formats /// The image height. /// The image. private void ConvertFromCmyk(int width, int height, Image image) - where TColor : struct, IPackedPixel - where TPacked : struct + where TColor : struct, IPackedPixel where TPacked : struct { if (!this.adobeTransformValid) { - throw new ImageFormatException("Unknown color model: 4-component JPEG doesn't have Adobe APP14 metadata"); + throw new ImageFormatException( + "Unknown color model: 4-component JPEG doesn't have Adobe APP14 metadata"); } // If the 4-component JPEG image isn't explicitly marked as "Unknown (RGB @@ -1274,21 +1153,21 @@ namespace ImageSharp.Formats 0, height, y => - { - int yo = this.ycbcrImage.GetRowYOffset(y); - int co = this.ycbcrImage.GetRowCOffset(y); - - for (int x = 0; x < width; x++) { - byte yy = this.ycbcrImage.YChannel[yo + x]; - byte cb = this.ycbcrImage.CbChannel[co + (x / scale)]; - byte cr = this.ycbcrImage.CrChannel[co + (x / scale)]; + int yo = this.ycbcrImage.GetRowYOffset(y); + int co = this.ycbcrImage.GetRowCOffset(y); - TColor packed = default(TColor); - this.PackCmyk(ref packed, yy, cb, cr, x, y); - pixels[x, y] = packed; - } - }); + for (int x = 0; x < width; x++) + { + byte yy = this.ycbcrImage.YChannel[yo + x]; + byte cb = this.ycbcrImage.CbChannel[co + (x / scale)]; + byte cr = this.ycbcrImage.CrChannel[co + (x / scale)]; + + TColor packed = default(TColor); + this.PackCmyk(ref packed, yy, cb, cr, x, y); + pixels[x, y] = packed; + } + }); } this.AssignResolution(image); @@ -1304,8 +1183,7 @@ namespace ImageSharp.Formats /// The image height. /// The image. private void ConvertFromGrayScale(int width, int height, Image image) - where TColor : struct, IPackedPixel - where TPacked : struct + where TColor : struct, IPackedPixel where TPacked : struct { image.InitPixels(width, height); @@ -1316,17 +1194,17 @@ namespace ImageSharp.Formats height, Bootstrapper.Instance.ParallelOptions, y => - { - int yoff = this.grayImage.GetRowOffset(y); - for (int x = 0; x < width; x++) { - byte rgb = this.grayImage.Pixels[yoff + x]; + int yoff = this.grayImage.GetRowOffset(y); + for (int x = 0; x < width; x++) + { + byte rgb = this.grayImage.Pixels[yoff + x]; - TColor packed = default(TColor); - packed.PackFromBytes(rgb, rgb, rgb, 255); - pixels[x, y] = packed; - } - }); + TColor packed = default(TColor); + packed.PackFromBytes(rgb, rgb, rgb, 255); + pixels[x, y] = packed; + } + }); } this.AssignResolution(image); @@ -1341,8 +1219,7 @@ namespace ImageSharp.Formats /// The image height. /// The image. private void ConvertFromYCbCr(int width, int height, Image image) - where TColor : struct, IPackedPixel - where TPacked : struct + where TColor : struct, IPackedPixel where TPacked : struct { int scale = this.componentArray[0].HorizontalFactor / this.componentArray[1].HorizontalFactor; image.InitPixels(width, height); @@ -1354,27 +1231,26 @@ namespace ImageSharp.Formats height, Bootstrapper.Instance.ParallelOptions, y => - { - int yo = this.ycbcrImage.GetRowYOffset(y); - int co = this.ycbcrImage.GetRowCOffset(y); - - for (int x = 0; x < width; x++) { - byte yy = this.ycbcrImage.YChannel[yo + x]; - byte cb = this.ycbcrImage.CbChannel[co + (x / scale)]; - byte cr = this.ycbcrImage.CrChannel[co + (x / scale)]; + int yo = this.ycbcrImage.GetRowYOffset(y); + int co = this.ycbcrImage.GetRowCOffset(y); - TColor packed = default(TColor); - PackYcbCr(ref packed, yy, cb, cr); - pixels[x, y] = packed; - } - }); + for (int x = 0; x < width; x++) + { + byte yy = this.ycbcrImage.YChannel[yo + x]; + byte cb = this.ycbcrImage.CbChannel[co + (x / scale)]; + byte cr = this.ycbcrImage.CrChannel[co + (x / scale)]; + + TColor packed = default(TColor); + PackYcbCr(ref packed, yy, cb, cr); + pixels[x, y] = packed; + } + }); } this.AssignResolution(image); } - /// /// Converts the image from the original RBG image pixels. /// @@ -1384,8 +1260,7 @@ namespace ImageSharp.Formats /// The height. /// The image. private void ConvertFromRGB(int width, int height, Image image) - where TColor : struct, IPackedPixel - where TPacked : struct + where TColor : struct, IPackedPixel where TPacked : struct { int scale = this.componentArray[0].HorizontalFactor / this.componentArray[1].HorizontalFactor; image.InitPixels(width, height); @@ -1397,21 +1272,21 @@ namespace ImageSharp.Formats height, Bootstrapper.Instance.ParallelOptions, y => - { - int yo = this.ycbcrImage.GetRowYOffset(y); - int co = this.ycbcrImage.GetRowCOffset(y); - - for (int x = 0; x < width; x++) { - byte red = this.ycbcrImage.YChannel[yo + x]; - byte green = this.ycbcrImage.CbChannel[co + (x / scale)]; - byte blue = this.ycbcrImage.CrChannel[co + (x / scale)]; + int yo = this.ycbcrImage.GetRowYOffset(y); + int co = this.ycbcrImage.GetRowCOffset(y); - TColor packed = default(TColor); - packed.PackFromBytes(red, green, blue, 255); - pixels[x, y] = packed; - } - }); + for (int x = 0; x < width; x++) + { + byte red = this.ycbcrImage.YChannel[yo + x]; + byte green = this.ycbcrImage.CbChannel[co + (x / scale)]; + byte blue = this.ycbcrImage.CrChannel[co + (x / scale)]; + + TColor packed = default(TColor); + packed.PackFromBytes(red, green, blue, 255); + pixels[x, y] = packed; + } + }); } this.AssignResolution(image); @@ -1424,8 +1299,7 @@ namespace ImageSharp.Formats /// The packed format. uint, long, float. /// The image to assign the resolution to. private void AssignResolution(Image image) - where TColor : struct, IPackedPixel - where TPacked : struct + where TColor : struct, IPackedPixel where TPacked : struct { if (this.isJfif && this.horizontalResolution > 0 && this.verticalResolution > 0) { @@ -1476,7 +1350,7 @@ namespace ImageSharp.Formats for (int i = 0; i < scanComponentCount; i++) { - ProcessScanImpl(i, ref scan[i], scan, ref totalHv); + this.ProcessScanImpl(i, ref scan[i], scan, ref totalHv); } // Section B.2.3 states that if there is more than one component then the @@ -1546,7 +1420,8 @@ namespace ImageSharp.Formats int compIndex = scan[i].Index; if (this.progCoeffs[compIndex] == null) { - var size = mxx * myy * this.componentArray[compIndex].HorizontalFactor * this.componentArray[compIndex].VerticalFactor; + var size = mxx * myy * this.componentArray[compIndex].HorizontalFactor + * this.componentArray[compIndex].VerticalFactor; this.progCoeffs[compIndex] = new Block8x8F[size]; } @@ -1573,7 +1448,7 @@ namespace ImageSharp.Formats // Tricky way to copy contents of the Unzig static variable to the stack: StackallocUnzigData unzigOnStack = new StackallocUnzigData(); int* unzigPtr = unzigOnStack.Data; - Marshal.Copy(Unzig, 0, (IntPtr) unzigPtr, 64); + Marshal.Copy(Unzig, 0, (IntPtr)unzigPtr, 64); for (int my = 0; my < myy; my++) { @@ -1585,7 +1460,6 @@ namespace ImageSharp.Formats int hi = this.componentArray[compIndex].HorizontalFactor; int vi = this.componentArray[compIndex].VerticalFactor; - for (int j = 0; j < hi * vi; j++) { // The blocks are traversed one MCU at a time. For 4:2:0 chroma @@ -1630,43 +1504,59 @@ namespace ImageSharp.Formats var qtIndex = this.componentArray[compIndex].Selector; // TODO: Find a way to clean up this mess - + fixed (Block8x8F* qtp = &this.quantizationTables[qtIndex]) { if (this.isProgressive) // Load the previous partially decoded coefficients, if applicable. { - blockIndex = ((@by*mxx)*hi) + bx; + this.blockIndex = ((@by * mxx) * hi) + bx; - fixed (Block8x8F* bp = &this.progCoeffs[compIndex][blockIndex]) + fixed (Block8x8F* bp = &this.progCoeffs[compIndex][this.blockIndex]) { - ProcessBlockImpl(ah, + this.ProcessBlockImpl( + ah, bp, &temp1, &temp2, unzigPtr, - scan, i, zigStart, zigEnd, al, dc, compIndex, @by, mxx, hi, bx, - qtp - ); + scan, + i, + zigStart, + zigEnd, + al, + dc, + compIndex, + @by, + mxx, + hi, + bx, + qtp); } } else { b.Clear(); - ProcessBlockImpl(ah, + this.ProcessBlockImpl( + ah, &b, &temp1, &temp2, unzigPtr, - scan, i, zigStart, zigEnd, al, dc, compIndex, @by, mxx, hi, - bx, qtp - ); + scan, + i, + zigStart, + zigEnd, + al, + dc, + compIndex, + @by, + mxx, + hi, + bx, + qtp); } } - - - - } // for j @@ -1709,14 +1599,22 @@ namespace ImageSharp.Formats } private void ProcessBlockImpl( - int ah, - Block8x8F* b, + int ah, + Block8x8F* b, Block8x8F* temp1, Block8x8F* temp2, int* unzigPtr, - Scan[] scan, - int i, int zigStart, int zigEnd, int al, - int[] dc, int compIndex, int @by, int mxx, int hi, int bx, + Scan[] scan, + int i, + int zigStart, + int zigEnd, + int al, + int[] dc, + int compIndex, + int @by, + int mxx, + int hi, + int bx, Block8x8F* qt) { var huffmannIdx = AcTable * ThRowSize + scan[i].AcTableSelector; @@ -1732,7 +1630,8 @@ namespace ImageSharp.Formats zig++; // Decode the DC coefficient, as specified in section F.2.2.1. - byte value = this.DecodeHuffman(ref this.huffmanTrees[DcTable * ThRowSize + scan[i].DcTableSelector]); + byte value = this.DecodeHuffman( + ref this.huffmanTrees[DcTable * ThRowSize + scan[i].DcTableSelector]); if (value > 16) { throw new ImageFormatException("Excessive DC component"); @@ -1741,7 +1640,7 @@ namespace ImageSharp.Formats int deltaDC = this.bits.ReceiveExtend(value, this); dc[compIndex] += deltaDC; - //b[0] = dc[compIndex] << al; + // b[0] = dc[compIndex] << al; Block8x8F.SetScalarAt(b, 0, dc[compIndex] << al); } @@ -1752,7 +1651,7 @@ namespace ImageSharp.Formats else { // Decode the AC coefficients, as specified in section F.2.2.2. - //Huffman huffv = ; + // Huffman huffv = ; for (; zig <= zigEnd; zig++) { byte value = this.DecodeHuffman(ref this.huffmanTrees[huffmannIdx]); @@ -1768,7 +1667,7 @@ namespace ImageSharp.Formats int ac = this.bits.ReceiveExtend(val1, this); - //b[Unzig[zig]] = ac << al; + // b[Unzig[zig]] = ac << al; Block8x8F.SetScalarAt(b, unzigPtr[zig], ac << al); } else @@ -1813,8 +1712,8 @@ namespace ImageSharp.Formats // Dequantize, perform the inverse DCT and store the block to the image. Block8x8F.UnZig(b, qt, unzigPtr); - - b->IDCTInto(ref *temp1, ref *temp2); + + b->TransformIDCTInto(ref *temp1, ref *temp2); byte[] dst; int offset; @@ -1861,10 +1760,10 @@ namespace ImageSharp.Formats } // Level shift by +128, clip to [0, 255], and write to dst. - + temp1->CopyColorsTo(new MutableSpan(dst, offset), stride, temp2); } - + private void ProcessScanImpl(int i, ref Scan currentScan, Scan[] scan, ref int totalHv) { // Component selector. @@ -1886,10 +1785,15 @@ namespace ImageSharp.Formats currentScan.Index = (byte)compIndex; - ProcessComponentImpl(i, ref currentScan, scan, ref totalHv, ref this.componentArray[compIndex]); + this.ProcessComponentImpl(i, ref currentScan, scan, ref totalHv, ref this.componentArray[compIndex]); } - private void ProcessComponentImpl(int i, ref Scan currentScan, Scan[] scan, ref int totalHv, ref Component currentComponent) + private void ProcessComponentImpl( + int i, + ref Scan currentScan, + Scan[] scan, + ref int totalHv, + ref Component currentComponent) { // Section B.2.3 states that "the value of Cs_j shall be different from // the values of Cs_1 through Cs_(j-1)". Since we have previously @@ -1904,7 +1808,6 @@ namespace ImageSharp.Formats } } - totalHv += currentComponent.HorizontalFactor * currentComponent.VerticalFactor; currentScan.DcTableSelector = (byte)(this.temp[2 + (2 * i)] >> 4); @@ -1925,6 +1828,7 @@ namespace ImageSharp.Formats /// /// The block of coefficients /// The Huffman tree + /// /// The zig-zag start index /// The zig-zag end index /// The low transform offset @@ -1941,11 +1845,11 @@ namespace ImageSharp.Formats bool bit = this.DecodeBit(); if (bit) { - int stuff = (int) Block8x8F.GetScalarAt(b, 0); + int stuff = (int)Block8x8F.GetScalarAt(b, 0); - //int stuff = (int)b[0]; + // int stuff = (int)b[0]; stuff |= delta; - //b[0] = stuff; + // b[0] = stuff; Block8x8F.SetScalarAt(b, 0, stuff); } @@ -2145,7 +2049,8 @@ namespace ImageSharp.Formats return true; } - return this.componentArray[0].Identifier == 'R' && this.componentArray[1].Identifier == 'G' && this.componentArray[2].Identifier == 'B'; + return this.componentArray[0].Identifier == 'R' && this.componentArray[1].Identifier == 'G' + && this.componentArray[2].Identifier == 'B'; } /// @@ -2160,8 +2065,7 @@ namespace ImageSharp.Formats /// The cr chroma component. [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void PackYcbCr(ref TColor packed, byte y, byte cb, byte cr) - where TColor : struct, IPackedPixel - where TPacked : struct + where TColor : struct, IPackedPixel where TPacked : struct { int ccb = cb - 128; int ccr = cr - 128; @@ -2186,8 +2090,7 @@ namespace ImageSharp.Formats /// The x-position within the image. /// The y-position within the image. private void PackCmyk(ref TColor packed, byte y, byte cb, byte cr, int xx, int yy) - where TColor : struct, IPackedPixel - where TPacked : struct + where TColor : struct, IPackedPixel where TPacked : struct { // TODO: We can speed this up further with Vector4 int ccb = cb - 128; @@ -2265,11 +2168,11 @@ namespace ImageSharp.Formats public void Dispose() { - for (int i = 0; i < huffmanTrees.Length; i++) + for (int i = 0; i < this.huffmanTrees.Length; i++) { - huffmanTrees[i].Dispose(); + this.huffmanTrees[i].Dispose(); } - bytes.Dispose(); + this.bytes.Dispose(); } } } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs index 25d7af8284..524b84082e 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs @@ -31,7 +31,7 @@ namespace ImageSharp.Tests.Formats.Jpg public void Indexer() { float sum = 0; - Measure(Times, () => + this.Measure(Times, () => { Block8x8F block = new Block8x8F(); @@ -44,8 +44,8 @@ namespace ImageSharp.Tests.Formats.Jpg { sum += block[i]; } - }); - Assert.Equal(sum, 64f*63f*0.5f); + }); + Assert.Equal(sum, 64f * 63f * 0.5f); } [Fact] @@ -151,28 +151,12 @@ namespace ImageSharp.Tests.Formats.Jpg v.CopyTo(mirror); }); + Assert.Equal(data, mirror); //PrintLinearData((MutableSpan)mirror); } - - [Fact] - public void TransposeInplace() - { - float[] expected = Create8x8FloatData(); - ReferenceImplementations.Transpose8x8(expected); - - Block8x8F buffer = new Block8x8F(); - buffer.LoadFrom(Create8x8FloatData()); - - buffer.TransposeInplace(); - - float[] actual = new float[64]; - buffer.CopyTo(actual); - - Assert.Equal(expected, actual); - } - + [Fact] public void TransposeInto() { @@ -231,7 +215,7 @@ namespace ImageSharp.Tests.Formats.Jpg Block8x8F dest = new Block8x8F(); - source.iDCT2D8x4_LeftPart(ref dest); + source.IDCT8x4_LeftPart(ref dest); float[] actualDestArray = new float[64]; dest.CopyTo(actualDestArray); @@ -256,7 +240,7 @@ namespace ImageSharp.Tests.Formats.Jpg Block8x8F dest = new Block8x8F(); - source.iDCT2D8x4_RightPart(ref dest); + source.IDCT8x4_RightPart(ref dest); float[] actualDestArray = new float[64]; dest.CopyTo(actualDestArray); @@ -302,7 +286,7 @@ namespace ImageSharp.Tests.Formats.Jpg Block8x8F dest = new Block8x8F(); Block8x8F tempBuffer = new Block8x8F(); - source.IDCTInto(ref dest, ref tempBuffer); + source.TransformIDCTInto(ref dest, ref tempBuffer); float[] actualDestArray = new float[64]; dest.CopyTo(actualDestArray); @@ -342,26 +326,7 @@ namespace ImageSharp.Tests.Formats.Jpg Assert.Equal(colorsExpected, colorsActual); } - - [Fact] - public void CropInto() - { - Block8x8F block = new Block8x8F(); - block.LoadFrom(Create8x8FloatData()); - - Block8x8F dest = new Block8x8F(); - block.CropInto(10, 20, ref dest); - - float[] array = new float[64]; - dest.CopyTo(array); - PrintLinearData(array); - foreach (float val in array) - { - Assert.InRange(val, 10, 20); - } - - } - + private static float[] Create8x8ColorCropTestData() { float[] result = new float[64]; @@ -376,7 +341,7 @@ namespace ImageSharp.Tests.Formats.Jpg } [Fact] - public void ColorifyInto() + public void TransformByteConvetibleColorValuesInto() { Block8x8F block = new Block8x8F(); var input = Create8x8ColorCropTestData(); @@ -386,7 +351,7 @@ namespace ImageSharp.Tests.Formats.Jpg Block8x8F dest = new Block8x8F(); - block.ColorifyInto(ref dest); + block.TransformByteConvetibleColorValuesInto(ref dest); float[] array = new float[64]; dest.CopyTo(array); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementations.cs b/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementations.cs index cf965e1ba0..b620c1d19f 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementations.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementations.cs @@ -1,46 +1,57 @@ -using System; -using System.Buffers; -using System.Numerics; -using System.Runtime.CompilerServices; -using ImageSharp.Formats; - -// ReSharper disable InconsistentNaming +// ReSharper disable InconsistentNaming namespace ImageSharp.Tests.Formats.Jpg { + using System.Numerics; + using System.Runtime.CompilerServices; + + using ImageSharp.Formats; + /// /// This class contains simplified (unefficient) reference implementations to produce verification data for unit tests /// DCT code Ported from https://github.com/norishigefukushima/dct_simd /// internal static class ReferenceImplementations { + /// + /// Transpose 8x8 block stored linearly in a span (inplace) + /// + /// internal static void Transpose8x8(MutableSpan data) { for (int i = 1; i < 8; i++) { - int i8 = i*8; + int i8 = i * 8; for (int j = 0; j < i; j++) { float tmp = data[i8 + j]; - data[i8 + j] = data[j*8 + i]; - data[j*8 + i] = tmp; + data[i8 + j] = data[j * 8 + i]; + data[j * 8 + i] = tmp; } } } + /// + /// Transpose 8x8 block stored linearly in a span + /// internal static void Transpose8x8(MutableSpan src, MutableSpan dest) { for (int i = 0; i < 8; i++) { - int i8 = i*8; + int i8 = i * 8; for (int j = 0; j < 8; j++) { - dest[j*8 + i] = src[i8 + j]; + dest[j * 8 + i] = src[i8 + j]; } } } - internal static void iDCT1Dllm_32f(MutableSpan y, MutableSpan x) + /// + /// https://github.com/norishigefukushima/dct_simd/blob/master/dct/dct8x8_simd.cpp#L200 + /// + /// + /// + private static void iDCT1Dllm_32f(MutableSpan y, MutableSpan x) { float a0, a1, a2, a3, b0, b1, b2, b3; float z0, z1, z2, z3, z4; @@ -58,23 +69,23 @@ namespace ImageSharp.Tests.Formats.Jpg z1 = y[3] + y[5]; z2 = y[3] + y[7]; z3 = y[1] + y[5]; - z4 = (z0 + z1)*r3; + z4 = (z0 + z1) * r3; - z0 = z0*(-r3 + r7); - z1 = z1*(-r3 - r1); - z2 = z2*(-r3 - r5) + z4; - z3 = z3*(-r3 + r5) + z4; + z0 = z0 * (-r3 + r7); + z1 = z1 * (-r3 - r1); + z2 = z2 * (-r3 - r5) + z4; + z3 = z3 * (-r3 + r5) + z4; - b3 = y[7]*(-r1 + r3 + r5 - r7) + z0 + z2; - b2 = y[5]*(r1 + r3 - r5 + r7) + z1 + z3; - b1 = y[3]*(r1 + r3 + r5 - r7) + z1 + z2; - b0 = y[1]*(r1 + r3 - r5 - r7) + z0 + z3; + b3 = y[7] * (-r1 + r3 + r5 - r7) + z0 + z2; + b2 = y[5] * (r1 + r3 - r5 + r7) + z1 + z3; + b1 = y[3] * (r1 + r3 + r5 - r7) + z1 + z2; + b0 = y[1] * (r1 + r3 - r5 - r7) + z0 + z3; - z4 = (y[2] + y[6])*r6; + z4 = (y[2] + y[6]) * r6; z0 = y[0] + y[4]; z1 = y[0] - y[4]; - z2 = z4 - y[6]*(r2 + r6); - z3 = z4 + y[2]*(r2 - r6); + z2 = z4 - y[6] * (r2 + r6); + z3 = z4 + y[2] * (r2 - r6); a0 = z0 + z3; a3 = z0 - z3; a1 = z1 + z2; @@ -90,20 +101,27 @@ namespace ImageSharp.Tests.Formats.Jpg x[4] = a3 - b3; } + /// + /// Original: https://github.com/norishigefukushima/dct_simd/blob/master/dct/dct8x8_simd.cpp#L239 + /// Applyies IDCT transformation on "s" copying transformed values to "d", using temporal block "temp" + /// + /// + /// + /// internal static void iDCT2D_llm(MutableSpan s, MutableSpan d, MutableSpan temp) { int j; for (j = 0; j < 8; j++) { - iDCT1Dllm_32f(s.Slice(j*8), temp.Slice(j*8)); + iDCT1Dllm_32f(s.Slice(j * 8), temp.Slice(j * 8)); } Transpose8x8(temp, d); for (j = 0; j < 8; j++) { - iDCT1Dllm_32f(d.Slice(j*8), temp.Slice(j*8)); + iDCT1Dllm_32f(d.Slice(j * 8), temp.Slice(j * 8)); } Transpose8x8(temp, d); @@ -113,8 +131,6 @@ namespace ImageSharp.Tests.Formats.Jpg d[j] *= 0.125f; } } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] private static Vector4 _mm_load_ps(MutableSpan src, int offset) @@ -123,12 +139,6 @@ namespace ImageSharp.Tests.Formats.Jpg return new Vector4(src[0], src[1], src[2], src[3]); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector4 _mm_load_ps(MutableSpan src) - { - return new Vector4(src[0], src[1], src[2], src[3]); - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void _mm_store_ps(MutableSpan dest, int offset, Vector4 src) { @@ -139,29 +149,37 @@ namespace ImageSharp.Tests.Formats.Jpg dest[3] = src.W; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void _mm_store_ps(MutableSpan dest, Vector4 src) - { - dest[0] = src.X; - dest[1] = src.Y; - dest[2] = src.Z; - dest[3] = src.W; - } - - private static readonly Vector4 _1_175876 = new Vector4(1.175876f); + private static readonly Vector4 _1_961571 = new Vector4(-1.961571f); + private static readonly Vector4 _0_390181 = new Vector4(-0.390181f); + private static readonly Vector4 _0_899976 = new Vector4(-0.899976f); + private static readonly Vector4 _2_562915 = new Vector4(-2.562915f); + private static readonly Vector4 _0_298631 = new Vector4(0.298631f); + private static readonly Vector4 _2_053120 = new Vector4(2.053120f); + private static readonly Vector4 _3_072711 = new Vector4(3.072711f); + private static readonly Vector4 _1_501321 = new Vector4(1.501321f); + private static readonly Vector4 _0_541196 = new Vector4(0.541196f); + private static readonly Vector4 _1_847759 = new Vector4(-1.847759f); + private static readonly Vector4 _0_765367 = new Vector4(0.765367f); + /// + /// Original: + /// https://github.com/norishigefukushima/dct_simd/blob/master/dct/dct8x8_simd.cpp#L261 + /// Does a part of the IDCT job on the given parts of the blocks + /// + /// + /// internal static void iDCT2D8x4_32f(MutableSpan y, MutableSpan x) { /* @@ -178,7 +196,7 @@ namespace ImageSharp.Tests.Formats.Jpg 6: 7: 0.275899 */ - + Vector4 my1 = _mm_load_ps(y, 8); Vector4 my7 = _mm_load_ps(y, 56); Vector4 mz0 = my1 + my7; @@ -188,16 +206,16 @@ namespace ImageSharp.Tests.Formats.Jpg Vector4 my5 = _mm_load_ps(y, 40); Vector4 mz1 = my3 + my5; Vector4 mz3 = my1 + my5; - - Vector4 mz4 = ((mz0 + mz1)* _1_175876); + + Vector4 mz4 = ((mz0 + mz1) * _1_175876); //z0 = y[1] + y[7]; z1 = y[3] + y[5]; z2 = y[3] + y[7]; z3 = y[1] + y[5]; //z4 = (z0 + z1) * r[3]; - - mz2 = mz2* _1_961571 + mz4; - mz3 = mz3* _0_390181 + mz4; - mz0 = mz0* _0_899976; - mz1 = mz1* _2_562915; - + + mz2 = mz2 * _1_961571 + mz4; + mz3 = mz3 * _0_390181 + mz4; + mz0 = mz0 * _0_899976; + mz1 = mz1 * _2_562915; + /* -0.899976 -2.562915 @@ -208,11 +226,10 @@ namespace ImageSharp.Tests.Formats.Jpg z2 = z2 * (-r[3] - r[5]) + z4; z3 = z3 * (-r[3] + r[5]) + z4;*/ - - Vector4 mb3 = my7* _0_298631 + mz0 + mz2; - Vector4 mb2 = my5* _2_053120 + mz1 + mz3; - Vector4 mb1 = my3* _3_072711 + mz1 + mz2; - Vector4 mb0 = my1* _1_501321 + mz0 + mz3; + Vector4 mb3 = my7 * _0_298631 + mz0 + mz2; + Vector4 mb2 = my5 * _2_053120 + mz1 + mz3; + Vector4 mb1 = my3 * _3_072711 + mz1 + mz2; + Vector4 mb0 = my1 * _1_501321 + mz0 + mz3; /* 0.298631 @@ -227,14 +244,14 @@ namespace ImageSharp.Tests.Formats.Jpg Vector4 my2 = _mm_load_ps(y, 16); Vector4 my6 = _mm_load_ps(y, 48); - mz4 = (my2 + my6)* _0_541196; + mz4 = (my2 + my6) * _0_541196; Vector4 my0 = _mm_load_ps(y, 0); Vector4 my4 = _mm_load_ps(y, 32); mz0 = my0 + my4; mz1 = my0 - my4; - mz2 = mz4 + my6* _1_847759; - mz3 = mz4 + my2* _0_765367; + mz2 = mz4 + my6 * _1_847759; + mz3 = mz4 + my2 * _0_765367; my0 = mz0 + mz3; my3 = mz0 - mz3; @@ -275,55 +292,12 @@ namespace ImageSharp.Tests.Formats.Jpg */ } - internal static void iDCT8x8_llm_sse(MutableSpan s, MutableSpan d, MutableSpan temp) - { - Transpose8x8(s, temp); - iDCT2D8x4_32f(temp, d); - - iDCT2D8x4_32f(temp.Slice(4), d.Slice(4)); - - Transpose8x8(d, temp); - - iDCT2D8x4_32f(temp, d); - - iDCT2D8x4_32f(temp.Slice(4), d.Slice(4)); - - Vector4 c = new Vector4(0.1250f); - - _mm_store_ps(d, (_mm_load_ps(d)*c));d.AddOffset(4);//0 - - _mm_store_ps(d, (_mm_load_ps(d)*c));d.AddOffset(4);//1 - - _mm_store_ps(d, (_mm_load_ps(d)*c));d.AddOffset(4);//2 - - _mm_store_ps(d, (_mm_load_ps(d)*c));d.AddOffset(4);//3 - - _mm_store_ps(d, (_mm_load_ps(d)*c));d.AddOffset(4);//4 - - _mm_store_ps(d, (_mm_load_ps(d)*c));d.AddOffset(4);//5 - - _mm_store_ps(d, (_mm_load_ps(d)*c));d.AddOffset(4);//6 - - _mm_store_ps(d, (_mm_load_ps(d)*c));d.AddOffset(4);//7 - - _mm_store_ps(d, (_mm_load_ps(d)*c));d.AddOffset(4);//8 - - _mm_store_ps(d, (_mm_load_ps(d)*c));d.AddOffset(4);//9 - - _mm_store_ps(d, (_mm_load_ps(d)*c));d.AddOffset(4);//10 - - _mm_store_ps(d, (_mm_load_ps(d)*c));d.AddOffset(4);//11 - - _mm_store_ps(d, (_mm_load_ps(d)*c));d.AddOffset(4);//12 - - _mm_store_ps(d, (_mm_load_ps(d)*c));d.AddOffset(4);//13 - - _mm_store_ps(d, (_mm_load_ps(d)*c));d.AddOffset(4);//14 - - _mm_store_ps(d, (_mm_load_ps(d)*c));d.AddOffset(4);//15 - } - - + /// + /// Copies color values from block to the destination image buffer. + /// + /// + /// + /// internal static unsafe void CopyColorsTo(ref Block8x8F block, MutableSpan buffer, int stride) { fixed (Block8x8F* p = &block) @@ -356,9 +330,6 @@ namespace ImageSharp.Tests.Formats.Jpg } } } - - } - } } \ No newline at end of file