diff --git a/src/ImageSharp/Formats/Jpg/Components/Block8x8F.cs b/src/ImageSharp/Formats/Jpg/Components/Block8x8F.cs index c29591b6a5..89b838289b 100644 --- a/src/ImageSharp/Formats/Jpg/Components/Block8x8F.cs +++ b/src/ImageSharp/Formats/Jpg/Components/Block8x8F.cs @@ -3,6 +3,7 @@ // Licensed under the Apache License, Version 2.0. // // ReSharper disable InconsistentNaming + namespace ImageSharp.Formats { using System; @@ -15,6 +16,7 @@ namespace ImageSharp.Formats /// internal partial struct Block8x8F { +#pragma warning disable SA1204 // Static members must appear before non-static members /// /// Vector count /// @@ -141,7 +143,7 @@ namespace ImageSharp.Formats } /// - /// Multiply in place + /// Multiply all elements of the block. /// /// Vector to multiply by [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -165,6 +167,10 @@ namespace ImageSharp.Formats this.V7R *= scaleVec; } + /// + /// Adds a vector to all elements of the block. + /// + /// The added vector [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AddToAllInplace(Vector4 diff) { @@ -185,7 +191,7 @@ namespace ImageSharp.Formats this.V7L += diff; this.V7R += diff; } - + /// /// Pointer-based "Indexer" (getter part) /// @@ -193,7 +199,7 @@ namespace ImageSharp.Formats /// Index /// The scaleVec value at the specified index [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static unsafe float GetScalarAt(Block8x8F* blockPtr, int idx) + public static unsafe float GetScalarAt(Block8x8F* blockPtr, int idx) { float* fp = (float*)blockPtr; return fp[idx]; @@ -206,7 +212,7 @@ namespace ImageSharp.Formats /// Index /// Value [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static unsafe void SetScalarAt(Block8x8F* blockPtr, int idx, float value) + public static unsafe void SetScalarAt(Block8x8F* blockPtr, int idx, float value) { float* fp = (float*)blockPtr; fp[idx] = value; @@ -219,7 +225,7 @@ namespace ImageSharp.Formats /// Qt pointer /// Unzig pointer [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static unsafe void UnZig(Block8x8F* blockPtr, Block8x8F* qtPtr, int* unzigPtr) + public static unsafe void UnZig(Block8x8F* blockPtr, Block8x8F* qtPtr, int* unzigPtr) { float* b = (float*)blockPtr; float* qtp = (float*)qtPtr; @@ -236,7 +242,7 @@ namespace ImageSharp.Formats /// Copy raw 32bit floating point data to dest /// /// Destination - internal unsafe void CopyTo(MutableSpan dest) + public unsafe void CopyTo(MutableSpan dest) { fixed (Vector4* ptr = &this.V0L) { @@ -252,7 +258,7 @@ namespace ImageSharp.Formats /// Load raw 32bit floating point data from source /// /// Source - internal unsafe void LoadFrom(MutableSpan source) + public unsafe void LoadFrom(MutableSpan source) { fixed (Vector4* ptr = &this.V0L) { @@ -263,12 +269,12 @@ namespace ImageSharp.Formats } } } - + /// /// Fill the block with defaults (zeroes) /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void Clear() + public void Clear() { // The cheapest way to do this in C#: this = default(Block8x8F); @@ -278,7 +284,7 @@ namespace ImageSharp.Formats /// TODO: Should be removed when BlockF goes away /// /// Legacy block - internal void LoadFrom(ref BlockF legacyBlock) + public void LoadFrom(ref BlockF legacyBlock) { this.LoadFrom(legacyBlock.Data); } @@ -287,12 +293,11 @@ namespace ImageSharp.Formats /// TODO: Should be removed when BlockF goes away /// /// Legacy block - internal void CopyTo(ref BlockF legacyBlock) + public void CopyTo(ref BlockF legacyBlock) { this.CopyTo(legacyBlock.Data); } - /// /// Level shift by +128, clip to [0, 255], and write to buffer. /// @@ -300,7 +305,7 @@ namespace ImageSharp.Formats /// Stride offset /// Temp Block pointer [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal unsafe void CopyColorsTo(MutableSpan buffer, int stride, Block8x8F* tempBlockPtr) + public unsafe void CopyColorsTo(MutableSpan buffer, int stride, Block8x8F* tempBlockPtr) { this.TransformByteConvetibleColorValuesInto(ref *tempBlockPtr); @@ -328,7 +333,7 @@ namespace ImageSharp.Formats /// Quantization table /// Pointer to elements [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static unsafe void UnZigDivRound(Block8x8F* src, Block8x8F* dest, Block8x8F* qt, int* unzigPtr) + public static unsafe void UnZigDivRound(Block8x8F* src, Block8x8F* dest, Block8x8F* qt, int* unzigPtr) { float* s = (float*)src; float* d = (float*)dest; diff --git a/src/ImageSharp/Formats/Jpg/Components/DCT.cs b/src/ImageSharp/Formats/Jpg/Components/DCT.cs index 6b2344b39d..a207df6708 100644 --- a/src/ImageSharp/Formats/Jpg/Components/DCT.cs +++ b/src/ImageSharp/Formats/Jpg/Components/DCT.cs @@ -14,26 +14,6 @@ namespace ImageSharp.Formats /// internal static class DCT { - /// - /// Apply floating point IDCT transformation into dest, using a temporary block 'temp' provided by the caller (optimization) - /// - /// Source - /// Destination - /// Temporary block provided by the caller - 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); - } - #pragma warning disable SA1310 // FieldNamesMustNotContainUnderscore private static readonly Vector4 C_1_175876 = new Vector4(1.175876f); @@ -63,13 +43,34 @@ namespace ImageSharp.Formats #pragma warning restore SA1310 // FieldNamesMustNotContainUnderscore private static readonly Vector4 InvSqrt2 = new Vector4(0.707107f); + /// + /// Apply floating point IDCT transformation into dest, using a temporary block 'temp' provided by the caller (optimization) + /// + /// Source + /// Destination + /// Temporary block provided by the caller + 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); + } + /// /// 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 /// + /// The source block /// Destination block [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static void IDCT8x4_LeftPart(ref Block8x8F s, ref Block8x8F d) + public static void IDCT8x4_LeftPart(ref Block8x8F s, ref Block8x8F d) { Vector4 my1 = s.V1L; Vector4 my7 = s.V7L; @@ -81,7 +82,7 @@ namespace ImageSharp.Formats Vector4 mz1 = my3 + my5; Vector4 mz3 = my1 + my5; - Vector4 mz4 = ((mz0 + mz1) * C_1_175876); + Vector4 mz4 = (mz0 + mz1) * C_1_175876; mz2 = (mz2 * C_1_961571) + mz4; mz3 = (mz3 * C_0_390181) + mz4; @@ -124,8 +125,10 @@ namespace ImageSharp.Formats /// Original src: /// https://github.com/norishigefukushima/dct_simd/blob/master/dct/dct8x8_simd.cpp#L261 /// + /// The source block + /// The destination block [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static void IDCT8x4_RightPart(ref Block8x8F s, ref Block8x8F d) + public static void IDCT8x4_RightPart(ref Block8x8F s, ref Block8x8F d) { Vector4 my1 = s.V1R; Vector4 my7 = s.V7R; @@ -187,96 +190,57 @@ namespace ImageSharp.Formats { Vector4 c0 = s.V0L; Vector4 c1 = s.V7L; - Vector4 t0 = (c0 + c1); - Vector4 t7 = (c0 - c1); + Vector4 t0 = c0 + c1; + Vector4 t7 = c0 - c1; c1 = s.V6L; c0 = s.V1L; - Vector4 t1 = (c0 + c1); - Vector4 t6 = (c0 - c1); + Vector4 t1 = c0 + c1; + Vector4 t6 = c0 - c1; c1 = s.V5L; c0 = s.V2L; - Vector4 t2 = (c0 + c1); - Vector4 t5 = (c0 - c1); + Vector4 t2 = c0 + c1; + Vector4 t5 = c0 - c1; c0 = s.V3L; c1 = s.V4L; - Vector4 t3 = (c0 + c1); - Vector4 t4 = (c0 - c1); - - /* - c1 = x[0]; c2 = x[7]; t0 = c1 + c2; t7 = c1 - c2; - c1 = x[1]; c2 = x[6]; t1 = c1 + c2; t6 = c1 - c2; - c1 = x[2]; c2 = x[5]; t2 = c1 + c2; t5 = c1 - c2; - c1 = x[3]; c2 = x[4]; t3 = c1 + c2; t4 = c1 - c2; - */ - - c0 = (t0 + t3); - Vector4 c3 = (t0 - t3); - c1 = (t1 + t2); - Vector4 c2 = (t1 - t2); - - /* - c0 = t0 + t3; c3 = t0 - t3; - c1 = t1 + t2; c2 = t1 - t2; - */ + 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; - /*y[0] = c0 + c1; - y[4] = 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); - /* - y[2] = c2 * r[6] + c3 * r[2]; - y[6] = c3 * r[6] - c2 * r[2]; - */ w0 = new Vector4(1.175876f); w1 = new Vector4(0.785695f); - c3 = ((w0 * t4) + (w1 * t7)); - c0 = ((w0 * t7) - (w1 * t4)); - /* - c3 = t4 * r[3] + t7 * r[5]; - c0 = t7 * r[3] - t4 * r[5]; - */ + 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)); - /* - c2 = t5 * r[1] + t6 * r[7]; - c1 = t6 * r[1] - t5 * r[7]; - */ + c2 = (w0 * t5) + (w1 * t6); + c1 = (w0 * t6) - (w1 * t5); - d.V3L = (c0 - c2); - - d.V5L = (c3 - c1); - //y[5] = c3 - c1; y[3] = c0 - c2; + d.V3L = c0 - c2; + d.V5L = c3 - c1; Vector4 invsqrt2 = new Vector4(0.707107f); - c0 = ((c0 + c2) * invsqrt2); - c3 = ((c3 + c1) * invsqrt2); - //c0 = (c0 + c2) * invsqrt2; - //c3 = (c3 + c1) * invsqrt2; - - d.V1L = (c0 + c3); - - d.V7L = (c0 - c3); - //y[1] = c0 + c3; y[7] = c0 - c3; + c0 = (c0 + c2) * invsqrt2; + c3 = (c3 + c1) * invsqrt2; - /*for(i = 0;i < 8;i++) - { - y[i] *= invsqrt2h; - }*/ + d.V1L = c0 + c3; + d.V7L = c0 - c3; } /// @@ -291,95 +255,56 @@ namespace ImageSharp.Formats { Vector4 c0 = s.V0R; Vector4 c1 = s.V7R; - Vector4 t0 = (c0 + c1); - Vector4 t7 = (c0 - c1); + Vector4 t0 = c0 + c1; + Vector4 t7 = c0 - c1; c1 = s.V6R; c0 = s.V1R; - Vector4 t1 = (c0 + c1); - Vector4 t6 = (c0 - c1); + Vector4 t1 = c0 + c1; + Vector4 t6 = c0 - c1; c1 = s.V5R; c0 = s.V2R; - Vector4 t2 = (c0 + c1); - Vector4 t5 = (c0 - c1); + Vector4 t2 = c0 + c1; + Vector4 t5 = c0 - c1; c0 = s.V3R; c1 = s.V4R; - Vector4 t3 = (c0 + c1); - Vector4 t4 = (c0 - c1); - - /* - c1 = x[0]; c2 = x[7]; t0 = c1 + c2; t7 = c1 - c2; - c1 = x[1]; c2 = x[6]; t1 = c1 + c2; t6 = c1 - c2; - c1 = x[2]; c2 = x[5]; t2 = c1 + c2; t5 = c1 - c2; - c1 = x[3]; c2 = x[4]; t3 = c1 + c2; t4 = c1 - c2; - */ - - c0 = (t0 + t3); - Vector4 c3 = (t0 - t3); - c1 = (t1 + t2); - Vector4 c2 = (t1 - t2); - - /* - c0 = t0 + t3; c3 = t0 - t3; - c1 = t1 + t2; c2 = t1 - t2; - */ + 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; - /*y[0] = c0 + c1; - y[4] = 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); - /* - y[2] = c2 * r[6] + c3 * r[2]; - y[6] = c3 * r[6] - c2 * r[2]; - */ w0 = new Vector4(1.175876f); w1 = new Vector4(0.785695f); - c3 = ((w0 * t4) + (w1 * t7)); - c0 = ((w0 * t7) - (w1 * t4)); - /* - c3 = t4 * r[3] + t7 * r[5]; - c0 = t7 * r[3] - t4 * r[5]; - */ + 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)); - /* - c2 = t5 * r[1] + t6 * r[7]; - c1 = t6 * r[1] - t5 * r[7]; - */ - - d.V3R = (c0 - c2); - - d.V5R = (c3 - c1); - //y[5] = c3 - c1; y[3] = c0 - c2; - - c0 = ((c0 + c2) * InvSqrt2); - c3 = ((c3 + c1) * InvSqrt2); - //c0 = (c0 + c2) * invsqrt2; - //c3 = (c3 + c1) * invsqrt2; + c2 = (w0 * t5) + (w1 * t6); + c1 = (w0 * t6) - (w1 * t5); - d.V1R = (c0 + c3); + d.V3R = c0 - c2; + d.V5R = c3 - c1; - d.V7R = (c0 - c3); - //y[1] = c0 + c3; y[7] = c0 - c3; + c0 = (c0 + c2) * InvSqrt2; + c3 = (c3 + c1) * InvSqrt2; - /*for(i = 0;i < 8;i++) - { - y[i] *= invsqrt2h; - }*/ + d.V1R = c0 + c3; + d.V7R = c0 - c3; } /// diff --git a/src/ImageSharp/Formats/Jpg/Components/MutableSpanExtensions.cs b/src/ImageSharp/Formats/Jpg/Components/MutableSpanExtensions.cs index 7253a816e1..31df4863c2 100644 --- a/src/ImageSharp/Formats/Jpg/Components/MutableSpanExtensions.cs +++ b/src/ImageSharp/Formats/Jpg/Components/MutableSpanExtensions.cs @@ -78,8 +78,11 @@ namespace ImageSharp.Formats data[3] = (int)v.W; } - - + /// + /// Converts all int values of src to float + /// + /// Source + /// A new with float values public static MutableSpan ConvertToFloat32MutableSpan(this MutableSpan src) { MutableSpan result = new MutableSpan(src.TotalCount); @@ -87,9 +90,15 @@ namespace ImageSharp.Formats { result[i] = (float)src[i]; } + return result; } + /// + /// Converts all float values of src to int + /// + /// Source + /// A new with float values public static MutableSpan ConvertToInt32MutableSpan(this MutableSpan src) { MutableSpan result = new MutableSpan(src.TotalCount); @@ -97,9 +106,16 @@ namespace ImageSharp.Formats { result[i] = (int)src[i]; } + return result; } + /// + /// Add a scalar to all values of src + /// + /// The source + /// The scalar value to add + /// A new instance of public static MutableSpan AddScalarToAllValues(this MutableSpan src, float scalar) { MutableSpan result = new MutableSpan(src.TotalCount); @@ -107,9 +123,16 @@ namespace ImageSharp.Formats { result[i] = src[i] + scalar; } + return result; } + /// + /// Add a scalar to all values of src + /// + /// The source + /// The scalar value to add + /// A new instance of public static MutableSpan AddScalarToAllValues(this MutableSpan src, int scalar) { MutableSpan result = new MutableSpan(src.TotalCount); @@ -117,10 +140,16 @@ namespace ImageSharp.Formats { result[i] = src[i] + scalar; } + return result; } - + /// + /// Copy all values in src to a new instance + /// + /// Element type + /// The source + /// A new instance of public static MutableSpan Copy(this MutableSpan src) { MutableSpan result = new MutableSpan(src.TotalCount); @@ -128,6 +157,7 @@ namespace ImageSharp.Formats { result[i] = src[i]; } + return result; } } diff --git a/src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs index c8f265ddff..9604055301 100644 --- a/src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs @@ -62,7 +62,7 @@ namespace ImageSharp.Formats /// The AC table index /// private const int AcTable = 1; - + /// /// The component array /// @@ -1809,7 +1809,7 @@ namespace ImageSharp.Formats // Dequantize, perform the inverse DCT and store the block to the image. Block8x8F.UnZig(b, qt, unzigPtr); - DCT.TransformIDCT(ref* b, ref *temp1, ref *temp2); + DCT.TransformIDCT(ref *b, ref *temp1, ref *temp2); byte[] dst; int offset; diff --git a/src/ImageSharp/Formats/Jpg/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpg/JpegEncoderCore.cs index 0010ff98fc..4444e50bf0 100644 --- a/src/ImageSharp/Formats/Jpg/JpegEncoderCore.cs +++ b/src/ImageSharp/Formats/Jpg/JpegEncoderCore.cs @@ -9,7 +9,7 @@ namespace ImageSharp.Formats using System.IO; using System.Numerics; using System.Runtime.CompilerServices; - + /// /// Image encoder for writing an image to a stream as a jpeg. /// @@ -113,19 +113,21 @@ namespace ImageSharp.Formats /// Counts the number of bits needed to hold an integer. /// private static readonly uint[] BitCountLut = - { - 0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, - 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, - }; + { + 0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, + }; /// /// The unscaled quantization tables in zig-zag order. Each @@ -134,23 +136,53 @@ namespace ImageSharp.Formats /// zig-zag order. /// private static readonly byte[,] UnscaledQuant = - { { - // Luminance. - 16, 11, 12, 14, 12, 10, 16, 14, 13, 14, 18, 17, 16, 19, 24, 40, - 26, 24, 22, 22, 24, 49, 35, 37, 29, 40, 58, 51, 61, 60, 57, 51, - 56, 55, 64, 72, 92, 78, 64, 68, 87, 69, 55, 56, 80, 109, 81, - 87, 95, 98, 103, 104, 103, 62, 77, 113, 121, 112, 100, 120, 92, - 101, 103, 99, - }, + { + // Luminance. + 16, 11, 12, 14, 12, 10, 16, 14, 13, 14, 18, 17, 16, 19, 24, + 40, 26, 24, 22, 22, 24, 49, 35, 37, 29, 40, 58, 51, 61, 60, + 57, 51, 56, 55, 64, 72, 92, 78, 64, 68, 87, 69, 55, 56, 80, + 109, 81, 87, 95, 98, 103, 104, 103, 62, 77, 113, 121, 112, + 100, 120, 92, 101, 103, 99, + }, + { + // Chrominance. + 17, 18, 18, 24, 21, 24, 47, 26, 26, 47, 99, 66, 56, 66, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + } + }; + + /// + /// The SOS (Start Of Scan) marker "\xff\xda" followed by 12 bytes: + /// - the marker length "\x00\x0c", + /// - the number of components "\x03", + /// - component 1 uses DC table 0 and AC table 0 "\x01\x00", + /// - component 2 uses DC table 1 and AC table 1 "\x02\x11", + /// - component 3 uses DC table 1 and AC table 1 "\x03\x11", + /// - the bytes "\x00\x3f\x00". Section B.2.3 of the spec says that for + /// sequential DCTs, those bytes (8-bit Ss, 8-bit Se, 4-bit Ah, 4-bit Al) + /// should be 0x00, 0x3f, 0x00<<4 | 0x00. + /// + private static readonly byte[] SosHeaderYCbCr = { - // Chrominance. - 17, 18, 18, 24, 21, 24, 47, 26, 26, 47, 99, 66, 56, 66, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, - } - }; + JpegConstants.Markers.XFF, JpegConstants.Markers.SOS, // Marker + 0x00, 0x0c, + + // Length (high byte, low byte), must be 6 + 2 * (number of components in scan) + 0x03, // Number of components in a scan, 3 + 0x01, // Component Id Y + 0x00, // DC/AC Huffman table + 0x02, // Component Id Cb + 0x11, // DC/AC Huffman table + 0x03, // Component Id Cr + 0x11, // DC/AC Huffman table + 0x00, // Ss - Start of spectral selection. + 0x3f, // Se - End of spectral selection. + 0x00 // Ah + Ah (Successive approximation bit position high + low) + }; /// /// A scratch buffer to reduce allocations. @@ -168,39 +200,14 @@ namespace ImageSharp.Formats private readonly byte[] huffmanBuffer = new byte[179]; /// - /// The scaled quantization tables, in zig-zag order. + /// The scaled luminance table, in zig-zag order. /// - //private readonly float[][] quant = new float[QuantizationTableCount][]; - //private readonly float[] quant = new float[QuantizationTableCount* Block8x8F.ScalarCount]; private Block8x8F luminanceQuantTable; - private Block8x8F chrominanceQuantTable; /// - /// The SOS (Start Of Scan) marker "\xff\xda" followed by 12 bytes: - /// - the marker length "\x00\x0c", - /// - the number of components "\x03", - /// - component 1 uses DC table 0 and AC table 0 "\x01\x00", - /// - component 2 uses DC table 1 and AC table 1 "\x02\x11", - /// - component 3 uses DC table 1 and AC table 1 "\x03\x11", - /// - the bytes "\x00\x3f\x00". Section B.2.3 of the spec says that for - /// sequential DCTs, those bytes (8-bit Ss, 8-bit Se, 4-bit Ah, 4-bit Al) - /// should be 0x00, 0x3f, 0x00<<4 | 0x00. + /// The scaled chrominance table, in zig-zag order. /// - private readonly byte[] sosHeaderYCbCr = - { - JpegConstants.Markers.XFF, JpegConstants.Markers.SOS, // Marker - 0x00, 0x0c, // Length (high byte, low byte), must be 6 + 2 * (number of components in scan) - 0x03, // Number of components in a scan, 3 - 0x01, // Component Id Y - 0x00, // DC/AC Huffman table - 0x02, // Component Id Cb - 0x11, // DC/AC Huffman table - 0x03, // Component Id Cr - 0x11, // DC/AC Huffman table - 0x00, // Ss - Start of spectral selection. - 0x3f, // Se - End of spectral selection. - 0x00 // Ah + Ah (Successive approximation bit position high + low) - }; + private Block8x8F chrominanceQuantTable; /// /// The accumulated bits to write to the stream. @@ -249,15 +256,13 @@ namespace ImageSharp.Formats /// /// The AC luminance huffman table index /// - LuminanceAC = 1, - /// /// The DC chrominance huffman table index /// ChrominanceDC = 2, - + /// /// The AC chrominance huffman table index /// @@ -282,26 +287,6 @@ namespace ImageSharp.Formats Chrominance = 1, } - private static void InitQuantizationTable(int i, int scale, ref Block8x8F quant) - { - for (int j = 0; j < Block8x8F.ScalarCount; j++) - { - int x = UnscaledQuant[i, j]; - x = ((x * scale) + 50) / 100; - if (x < 1) - { - x = 1; - } - - if (x > 255) - { - x = 255; - } - - quant[j] = x; - } - } - /// /// Encode writes the image to the jpeg baseline format with the given options. /// @@ -325,11 +310,6 @@ namespace ImageSharp.Formats this.outputStream = stream; this.subsample = sample; - //for (int i = 0; i < QuantizationTableCount; i++) - //{ - // this.quant[i] = new float[]; - //} - if (quality < 1) { quality = 1; @@ -352,10 +332,9 @@ namespace ImageSharp.Formats } // Initialize the quantization tables. - InitQuantizationTable(0, scale, ref this.luminanceQuantTable); InitQuantizationTable(1, scale, ref this.chrominanceQuantTable); - + // Compute number of components based on input image type. int componentCount = 3; @@ -385,7 +364,80 @@ namespace ImageSharp.Formats stream.Write(this.buffer, 0, 2); stream.Flush(); } - + + private static void InitQuantizationTable(int i, int scale, ref Block8x8F quant) + { + for (int j = 0; j < Block8x8F.ScalarCount; j++) + { + int x = UnscaledQuant[i, j]; + x = ((x * scale) + 50) / 100; + if (x < 1) + { + x = 1; + } + + if (x > 255) + { + x = 255; + } + + quant[j] = x; + } + } + + /// + /// Converts the 8x8 region of the image whose top-left corner is x,y to its YCbCr values. + /// + /// The pixel format. + /// The pixel accessor. + /// The x-position within the image. + /// The y-position within the image. + /// The luminance block. + /// The red chroma block. + /// The blue chroma block. + private static void ToYCbCr( + PixelAccessor pixels, + int x, + int y, + Block8x8F* yBlock, + Block8x8F* cbBlock, + Block8x8F* crBlock) + where TColor : struct, IPackedPixel, IEquatable + { + float* yBlockRaw = (float*)yBlock; + float* cbBlockRaw = (float*)cbBlock; + float* crBlockRaw = (float*)crBlock; + + using (PixelArea rgbBytes = new PixelArea(8, 8, ComponentOrder.XYZ)) + { + pixels.CopyRGBBytesStretchedTo(rgbBytes, y, x); + + byte* data = (byte*)rgbBytes.DataPointer; + + for (int j = 0; j < 8; j++) + { + int j8 = j * 8; + for (int i = 0; i < 8; i++) + { + Vector3 v = new Vector3(data[0], data[1], data[2]); + + // Convert returned bytes into the YCbCr color space. Assume RGBA + float yy = (0.299F * v.X) + (0.587F * v.Y) + (0.114F * v.Z); + float cb = 128 + ((-0.168736F * v.X) - (0.331264F * v.Y) + (0.5F * v.Z)); + float cr = 128 + ((0.5F * v.X) - (0.418688F * v.Y) - (0.081312F * v.Z)); + + int index = j8 + i; + + yBlockRaw[index] = yy; + cbBlockRaw[index] = cb; + crBlockRaw[index] = cr; + + data += 3; + } + } + } + } + /// /// Emits the least significant count of bits of bits to the bit-stream. /// The precondition is bits < 1<<nBits && nBits <= 16. @@ -485,30 +537,32 @@ namespace ImageSharp.Formats /// Quantization table /// The 8x8 Unzig block ptr /// The - private float WriteBlock(QuantIndex index, float prevDC, Block8x8F* src, Block8x8F* tempDest, Block8x8F* temp2, Block8x8F* quant, int* unzigPtr) + private float WriteBlock( + QuantIndex index, + float prevDC, + Block8x8F* src, + Block8x8F* tempDest, + Block8x8F* temp2, + Block8x8F* quant, + int* unzigPtr) { DCT.TransformFDCT(ref *src, ref *tempDest, ref *temp2); - + Block8x8F.UnZigDivRound(tempDest, temp2, quant, unzigPtr); - //Block8x8F.RoundAll(tempDest); - + float* d = (float*)temp2; - float* q = (float*)quant; // Emit the DC delta. - //float dc = Round(d[0], q[0]); float dc = d[0]; - this.EmitHuffRLE((HuffIndex)((2 * (int)index) + 0), 0, (int)(dc - prevDC)); - + // Emit the AC components. HuffIndex h = (HuffIndex)((2 * (int)index) + 1); int runLength = 0; - + for (int zig = 1; zig < Block.BlockSize; zig++) { - //float ac = Round(d[unzigPtr[zig]], q[zig]); float ac = d[zig]; if (ac == 0) @@ -536,107 +590,6 @@ namespace ImageSharp.Formats return dc; } - /// - /// Converts the 8x8 region of the image whose top-left corner is x,y to its YCbCr values. - /// - /// The pixel format. - /// The pixel accessor. - /// The x-position within the image. - /// The y-position within the image. - /// The luminance block. - /// The red chroma block. - /// The blue chroma block. - private static void ToYCbCr( - PixelAccessor pixels, - int x, - int y, - Block8x8F* yBlock, - Block8x8F* cbBlock, - Block8x8F* crBlock) - where TColor : struct, IPackedPixel, IEquatable - { - float* yBlockRaw = (float*)yBlock; - float* cbBlockRaw = (float*)cbBlock; - float* crBlockRaw = (float*)crBlock; - - PixelAccessor asStandardColorAccessor = pixels as PixelAccessor; - if (asStandardColorAccessor != null) - { - ColorRGBToYCbCr(asStandardColorAccessor, x, y, yBlockRaw, cbBlockRaw, crBlockRaw); - return; - } - - Vector4 maxBytes = new Vector4(255f); - Vector4 half = new Vector4(0.5f); - int xmax = pixels.Width - 1; - int ymax = pixels.Height - 1; - - for (int j = 0; j < 8; j++) - { - int j8 = j * 8; - for (int i = 0; i < 8; i++) - { - Vector4 v = pixels[Math.Min(x + i, xmax), Math.Min(y + j, ymax)].ToVector4(); - v = v * maxBytes + half; - - // Convert returned bytes into the YCbCr color space. Assume RGBA - float yy = ((0.299F * v.X) + (0.587F * v.Y) + (0.114F * v.Z)); - float cb = (128 + ((-0.168736F * v.X) - (0.331264F * v.Y) + (0.5F * v.Z))); - float cr = (128 + ((0.5F * v.X) - (0.418688F * v.Y) - (0.081312F * v.Z))); - - int index = j8 + i; - - yBlockRaw[index] = yy; - cbBlockRaw[index] = cb; - crBlockRaw[index] = cr; - } - } - } - - // ReSharper disable once InconsistentNaming - private static void ColorRGBToYCbCr( - PixelAccessor pixels, - int x, - int y, - float* yBlockRaw, - float* cbBlockRaw, - float* crBlockRaw) - { - int colorSize = sizeof(Color); - - int xmax = pixels.Width - 1; - int ymax = pixels.Height - 1; - - byte* data = (byte*)pixels.DataPointer; - - for (int j = 0; j < 8; j++) - { - int yPos = Math.Min(y + j, ymax); - - int j8 = j * 8; - for (int i = 0; i < 8; i++) - { - int xPos = Math.Min(x + i, xmax); - - byte* dataPos = data + (((yPos * pixels.Width) + xPos) * colorSize); - Vector3 v = new Vector3(dataPos[0], dataPos[1], dataPos[2]); - - // Convert returned bytes into the YCbCr color space. Assume RGBA - float yy = ((0.299F * v.X) + (0.587F * v.Y) + (0.114F * v.Z)); - float cb = (128 + ((-0.168736F * v.X) - (0.331264F * v.Y) + (0.5F * v.Z))); - float cr = (128 + ((0.5F * v.X) - (0.418688F * v.Y) - (0.081312F * v.Z))); - - int index = j8 + i; - - yBlockRaw[index] = yy; - cbBlockRaw[index] = cb; - crBlockRaw[index] = cr; - } - } - } - - - /// /// Writes the application header containing the JFIF identifier plus extra data. /// @@ -736,10 +689,11 @@ namespace ImageSharp.Formats WriteDataToDqt(dqt, ref offset, QuantIndex.Luminance, ref this.luminanceQuantTable); WriteDataToDqt(dqt, ref offset, QuantIndex.Chrominance, ref this.chrominanceQuantTable); - + this.outputStream.Write(dqt, 0, dqt.Length); } +#pragma warning disable SA1204 private static void WriteDataToDqt(byte[] dqt, ref int offset, QuantIndex i, ref Block8x8F q) { dqt[offset++] = (byte)i; @@ -748,6 +702,7 @@ namespace ImageSharp.Formats dqt[offset++] = (byte)q[j]; } } +#pragma warning restore SA1204 /// /// Writes the Start Of Frame (Baseline) marker @@ -779,7 +734,9 @@ namespace ImageSharp.Formats this.buffer[2] = (byte)(height & 0xff); // (2 bytes, Hi-Lo), must be > 0 if DNL not supported this.buffer[3] = (byte)(width >> 8); this.buffer[4] = (byte)(width & 0xff); // (2 bytes, Hi-Lo), must be > 0 if DNL not supported - this.buffer[5] = (byte)componentCount; // Number of components (1 byte), usually 1 = Gray scaled, 3 = color YCbCr or YIQ, 4 = color CMYK) + this.buffer[5] = (byte)componentCount; + + // Number of components (1 byte), usually 1 = Gray scaled, 3 = color YCbCr or YIQ, 4 = color CMYK) if (componentCount == 1) { this.buffer[6] = 1; @@ -863,7 +820,7 @@ namespace ImageSharp.Formats where TColor : struct, IPackedPixel, IEquatable { // TODO: We should allow grayscale writing. - this.outputStream.Write(this.sosHeaderYCbCr, 0, this.sosHeaderYCbCr.Length); + this.outputStream.Write(SosHeaderYCbCr, 0, SosHeaderYCbCr.Length); switch (this.subsample) { @@ -887,12 +844,12 @@ namespace ImageSharp.Formats private void Encode444(PixelAccessor pixels) where TColor : struct, IPackedPixel, IEquatable { - Block8x8F b = new Block8x8F(); - Block8x8F cb = new Block8x8F(); - Block8x8F cr = new Block8x8F(); + Block8x8F b = default(Block8x8F); + Block8x8F cb = default(Block8x8F); + Block8x8F cr = default(Block8x8F); - Block8x8F temp1 = new Block8x8F(); - Block8x8F temp2 = new Block8x8F(); + Block8x8F temp1 = default(Block8x8F); + Block8x8F temp2 = default(Block8x8F); Block8x8F onStackLuminanceQuantTable = this.luminanceQuantTable; Block8x8F onStackChrominanceQuantTable = this.chrominanceQuantTable; @@ -908,16 +865,42 @@ namespace ImageSharp.Formats { ToYCbCr(pixels, x, y, &b, &cb, &cr); - prevDCY = this.WriteBlock(QuantIndex.Luminance, prevDCY, &b, &temp1, &temp2, &onStackLuminanceQuantTable, unzig.Data); - prevDCCb = this.WriteBlock(QuantIndex.Chrominance, prevDCCb, &cb, &temp1, &temp2, &onStackChrominanceQuantTable, unzig.Data); - prevDCCr = this.WriteBlock(QuantIndex.Chrominance, prevDCCr, &cr, &temp1, &temp2, &onStackChrominanceQuantTable, unzig.Data); + prevDCY = this.WriteBlock( + QuantIndex.Luminance, + prevDCY, + &b, + &temp1, + &temp2, + &onStackLuminanceQuantTable, + unzig.Data); + prevDCCb = this.WriteBlock( + QuantIndex.Chrominance, + prevDCCb, + &cb, + &temp1, + &temp2, + &onStackChrominanceQuantTable, + unzig.Data); + prevDCCr = this.WriteBlock( + QuantIndex.Chrominance, + prevDCCr, + &cr, + &temp1, + &temp2, + &onStackChrominanceQuantTable, + unzig.Data); } } } - struct BlockQuad +#pragma warning disable SA1201 // MethodShouldNotFollowAStruct + + /// + /// This struct belongs to Encode420. Much easeier to understand code if they are together. Why should I move it Up? :P + /// + private struct BlockQuad { - public fixed float Data[4*Block8x8F.ScalarCount]; + public fixed float Data[4 * Block8x8F.ScalarCount]; } /// @@ -929,15 +912,15 @@ namespace ImageSharp.Formats private void Encode420(PixelAccessor pixels) where TColor : struct, IPackedPixel, IEquatable { - Block8x8F b = new Block8x8F(); - - BlockQuad cb = new BlockQuad(); - BlockQuad cr = new BlockQuad(); + Block8x8F b = default(Block8x8F); + + BlockQuad cb = default(BlockQuad); + BlockQuad cr = default(BlockQuad); Block8x8F* cbPtr = (Block8x8F*)cb.Data; Block8x8F* crPtr = (Block8x8F*)cr.Data; - - Block8x8F temp1 = new Block8x8F(); - Block8x8F temp2 = new Block8x8F(); + + Block8x8F temp1 = default(Block8x8F); + Block8x8F temp2 = default(Block8x8F); Block8x8F onStackLuminanceQuantTable = this.luminanceQuantTable; Block8x8F onStackChrominanceQuantTable = this.chrominanceQuantTable; @@ -990,6 +973,8 @@ namespace ImageSharp.Formats } } +#pragma warning restore SA1201 + /// /// Writes the header for a marker with the given length. /// @@ -1040,11 +1025,6 @@ namespace ImageSharp.Formats /// private class HuffmanLut { - /// - /// The collection of huffman values. - /// - public uint[] Values { get; } - /// /// Initializes a new instance of the class. /// @@ -1079,6 +1059,11 @@ namespace ImageSharp.Formats code <<= 1; } } + + /// + /// Gets the collection of huffman values. + /// + public uint[] Values { get; } } } } diff --git a/src/ImageSharp/Formats/Jpg/JpegUtils.cs b/src/ImageSharp/Formats/Jpg/JpegUtils.cs index f881a40888..67b33ea256 100644 --- a/src/ImageSharp/Formats/Jpg/JpegUtils.cs +++ b/src/ImageSharp/Formats/Jpg/JpegUtils.cs @@ -1,10 +1,43 @@ -namespace ImageSharp.Formats +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// +namespace ImageSharp.Formats { using System; using System.Runtime.CompilerServices; + /// + /// Jpeg specific utilities and extension methods + /// internal static unsafe class JpegUtils { + /// + /// 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. + /// + /// The pixel type + /// The input pixel acessor + /// The destination + /// Starting Y coord + /// Starting X coord + public static void CopyRGBBytesStretchedTo( + this PixelAccessor pixels, + PixelArea dest, + int sourceY, + int sourceX) + where TColor : struct, IPackedPixel, IEquatable + { + pixels.CopyTo(dest, sourceY, sourceX); + int stretchFromX = pixels.Width - sourceX; + int stretchFromY = pixels.Height - sourceY; + StretchPixels(dest, stretchFromX, stretchFromY); + } + + /// + /// Copy an RGB value + /// + /// Source pointer + /// Destination pointer [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static void CopyRgb(byte* source, byte* dest) { @@ -13,7 +46,7 @@ *dest = *source; // B } - private static unsafe void StretchPixels(PixelArea area, int fromX, int fromY) + private static void StretchPixels(PixelArea area, int fromX, int fromY) where TColor : struct, IPackedPixel, IEquatable { if (IsInvalidStretchArea(area, fromX, fromY)) @@ -23,12 +56,12 @@ for (int y = 0; y < fromY; y++) { - byte* ptrBase = (byte*)area.DataPointer + y * area.RowByteCount; + 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; + byte* prevPtr = ptrBase + ((x - 1) * 3); + byte* currPtr = ptrBase + (x * 3); CopyRgb(prevPtr, currPtr); } @@ -36,8 +69,8 @@ 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; + 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++) { @@ -51,22 +84,10 @@ } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool IsInvalidStretchArea(PixelArea area, int fromX, int fromY) where TColor : struct, IPackedPixel, IEquatable - { - return fromX <= 0 || fromY <= 0 || fromX >= area.Width || fromY >= area.Height; - } - - public static void CopyRGBBytesStretchedTo( - this PixelAccessor pixels, - PixelArea dest, - int sourceY, - int sourceX) + private static bool IsInvalidStretchArea(PixelArea area, int fromX, int fromY) where TColor : struct, IPackedPixel, IEquatable { - pixels.CopyTo(dest, sourceY, sourceX); - int stretchFromX = pixels.Width - sourceX; - int stretchFromY = pixels.Height - sourceY; - StretchPixels(dest, stretchFromX, stretchFromY); + return fromX <= 0 || fromY <= 0 || fromX >= area.Width || fromY >= area.Height; } } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpg/UnzigData.cs b/src/ImageSharp/Formats/Jpg/UnzigData.cs index 6dae6d9421..e74dd5c73c 100644 --- a/src/ImageSharp/Formats/Jpg/UnzigData.cs +++ b/src/ImageSharp/Formats/Jpg/UnzigData.cs @@ -1,4 +1,8 @@ -namespace ImageSharp.Formats +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// +namespace ImageSharp.Formats { using System; using System.Runtime.InteropServices; @@ -11,15 +15,10 @@ /// internal unsafe struct UnzigData { - internal fixed int Data[64]; - - public static UnzigData Create() - { - UnzigData result = new UnzigData(); - int* unzigPtr = result.Data; - Marshal.Copy(Unzig, 0, (IntPtr)unzigPtr, 64); - return result; - } + /// + /// Copy of in a value type + /// + public fixed int Data[64]; /// /// Unzig maps from the zigzag ordering to the natural ordering. For example, @@ -34,5 +33,16 @@ 53, 60, 61, 54, 47, 55, 62, 63, }; + /// + /// Creates and fills an instance of with Jpeg unzig indices + /// + /// The new instance + public static UnzigData Create() + { + UnzigData result = default(UnzigData); + int* unzigPtr = result.Data; + Marshal.Copy(Unzig, 0, (IntPtr)unzigPtr, 64); + return result; + } } } \ No newline at end of file diff --git a/src/ImageSharp/Image/PixelAccessor{TColor}.cs b/src/ImageSharp/Image/PixelAccessor{TColor}.cs index 3642d3942f..1dd3edf940 100644 --- a/src/ImageSharp/Image/PixelAccessor{TColor}.cs +++ b/src/ImageSharp/Image/PixelAccessor{TColor}.cs @@ -186,8 +186,7 @@ namespace ImageSharp int width = Math.Min(area.Width, this.Width - targetX); int height = Math.Min(area.Height, this.Height - targetY); - this.CheckDimensions(width, height); - + // this.CheckDimensions(width, height); TODO: Why was width == 0 or height == 0 considered a problem? Copy implementations do not fail on this (just do nothing)! switch (area.ComponentOrder) { case ComponentOrder.ZYX: @@ -221,8 +220,7 @@ namespace ImageSharp int width = Math.Min(area.Width, this.Width - sourceX); int height = Math.Min(area.Height, this.Height - sourceY); - this.CheckDimensions(width, height); - + // this.CheckDimensions(width, height); TODO: Why was width == 0 or height == 0 considered a problem? Copy implementations do not fail on this (just do nothing)! switch (area.ComponentOrder) { case ComponentOrder.ZYX: diff --git a/src/ImageSharp/PixelAccessor.cs b/src/ImageSharp/PixelAccessor.cs index 378ec55dc4..c91f10cbdb 100644 --- a/src/ImageSharp/PixelAccessor.cs +++ b/src/ImageSharp/PixelAccessor.cs @@ -109,6 +109,26 @@ namespace ImageSharp } } + /// + protected override unsafe void CopyToXYZ(PixelArea area, int sourceY, int sourceX, int width, int height) + { + for (int y = 0; y < height; y++) + { + byte* source = this.GetRowPointer(sourceX, sourceY + y); + byte* destination = area.PixelBase + (y * area.RowByteCount); + + for (int x = 0; x < width; x++) + { + *destination = *(source + 0); + *(destination + 1) = *(source + 1); + *(destination + 2) = *(source + 2); + + source += 4; + destination += 3; + } + } + } + /// protected override void CopyToZYXW(PixelArea area, int sourceY, int sourceX, int width, int height) { diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegTests.cs index fb1ca9371c..13d31ce518 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegTests.cs @@ -57,6 +57,34 @@ namespace ImageSharp.Tests } } + private const int BenchmarkExecTimes = 2; + + [Theory( + //Skip = "Benchmark, enable manually!" + )] + [WithFileCollection(nameof(AllBmpFiles), PixelTypes.Color | PixelTypes.StandardImageClass | PixelTypes.Argb, JpegSubsample.Ratio420, 75)] + [WithFileCollection(nameof(AllBmpFiles), PixelTypes.Color | PixelTypes.StandardImageClass | PixelTypes.Argb, JpegSubsample.Ratio444, 75)] + public void Benchmark_JpegEncoder(TestImageProvider provider, JpegSubsample subSample, int quality) + where TColor : struct, IPackedPixel, IEquatable + { + var image = provider.GetImage(); + + using (var outputStream = new MemoryStream()) + { + var encoder = new JpegEncoder() + { + Subsample = subSample, + Quality = quality + }; + + for (int i = 0; i < BenchmarkExecTimes; i++) + { + image.Save(outputStream, encoder); + outputStream.Seek(0, SeekOrigin.Begin); + } + } + } + public static Image CreateTestImage(GenericFactory factory) where TColor : struct, IPackedPixel, IEquatable { diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs index be5559e2a6..911719afaf 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs @@ -90,5 +90,11 @@ namespace ImageSharp.Tests return this; } + + public override string ToString() + { + string provName = this.GetType().Name.Replace("Provider", ""); + return $"{this.SourceFileOrDescription}[{this.PixelType}]"; + } } } \ No newline at end of file