Browse Source

StyleCop :S

af/merge-core
Anton Firszov 10 years ago
parent
commit
1e05986ddb
  1. 33
      src/ImageSharp/Formats/Jpg/Components/Block8x8F.cs
  2. 219
      src/ImageSharp/Formats/Jpg/Components/DCT.cs
  3. 36
      src/ImageSharp/Formats/Jpg/Components/MutableSpanExtensions.cs
  4. 4
      src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs
  5. 433
      src/ImageSharp/Formats/Jpg/JpegEncoderCore.cs
  6. 63
      src/ImageSharp/Formats/Jpg/JpegUtils.cs
  7. 30
      src/ImageSharp/Formats/Jpg/UnzigData.cs
  8. 6
      src/ImageSharp/Image/PixelAccessor{TColor}.cs
  9. 20
      src/ImageSharp/PixelAccessor.cs
  10. 28
      tests/ImageSharp.Tests/Formats/Jpg/JpegTests.cs
  11. 6
      tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs

33
src/ImageSharp/Formats/Jpg/Components/Block8x8F.cs

@ -3,6 +3,7 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright> // </copyright>
// ReSharper disable InconsistentNaming // ReSharper disable InconsistentNaming
namespace ImageSharp.Formats namespace ImageSharp.Formats
{ {
using System; using System;
@ -15,6 +16,7 @@ namespace ImageSharp.Formats
/// </summary> /// </summary>
internal partial struct Block8x8F internal partial struct Block8x8F
{ {
#pragma warning disable SA1204 // Static members must appear before non-static members
/// <summary> /// <summary>
/// Vector count /// Vector count
/// </summary> /// </summary>
@ -141,7 +143,7 @@ namespace ImageSharp.Formats
} }
/// <summary> /// <summary>
/// Multiply in place /// Multiply all elements of the block.
/// </summary> /// </summary>
/// <param name="scaleVec">Vector to multiply by</param> /// <param name="scaleVec">Vector to multiply by</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -165,6 +167,10 @@ namespace ImageSharp.Formats
this.V7R *= scaleVec; this.V7R *= scaleVec;
} }
/// <summary>
/// Adds a vector to all elements of the block.
/// </summary>
/// <param name="diff">The added vector</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddToAllInplace(Vector4 diff) public void AddToAllInplace(Vector4 diff)
{ {
@ -185,7 +191,7 @@ namespace ImageSharp.Formats
this.V7L += diff; this.V7L += diff;
this.V7R += diff; this.V7R += diff;
} }
/// <summary> /// <summary>
/// Pointer-based "Indexer" (getter part) /// Pointer-based "Indexer" (getter part)
/// </summary> /// </summary>
@ -193,7 +199,7 @@ namespace ImageSharp.Formats
/// <param name="idx">Index</param> /// <param name="idx">Index</param>
/// <returns>The scaleVec value at the specified index</returns> /// <returns>The scaleVec value at the specified index</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [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; float* fp = (float*)blockPtr;
return fp[idx]; return fp[idx];
@ -206,7 +212,7 @@ namespace ImageSharp.Formats
/// <param name="idx">Index</param> /// <param name="idx">Index</param>
/// <param name="value">Value</param> /// <param name="value">Value</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [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; float* fp = (float*)blockPtr;
fp[idx] = value; fp[idx] = value;
@ -219,7 +225,7 @@ namespace ImageSharp.Formats
/// <param name="qtPtr">Qt pointer</param> /// <param name="qtPtr">Qt pointer</param>
/// <param name="unzigPtr">Unzig pointer</param> /// <param name="unzigPtr">Unzig pointer</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [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* b = (float*)blockPtr;
float* qtp = (float*)qtPtr; float* qtp = (float*)qtPtr;
@ -236,7 +242,7 @@ namespace ImageSharp.Formats
/// Copy raw 32bit floating point data to dest /// Copy raw 32bit floating point data to dest
/// </summary> /// </summary>
/// <param name="dest">Destination</param> /// <param name="dest">Destination</param>
internal unsafe void CopyTo(MutableSpan<int> dest) public unsafe void CopyTo(MutableSpan<int> dest)
{ {
fixed (Vector4* ptr = &this.V0L) fixed (Vector4* ptr = &this.V0L)
{ {
@ -252,7 +258,7 @@ namespace ImageSharp.Formats
/// Load raw 32bit floating point data from source /// Load raw 32bit floating point data from source
/// </summary> /// </summary>
/// <param name="source">Source</param> /// <param name="source">Source</param>
internal unsafe void LoadFrom(MutableSpan<int> source) public unsafe void LoadFrom(MutableSpan<int> source)
{ {
fixed (Vector4* ptr = &this.V0L) fixed (Vector4* ptr = &this.V0L)
{ {
@ -263,12 +269,12 @@ namespace ImageSharp.Formats
} }
} }
} }
/// <summary> /// <summary>
/// Fill the block with defaults (zeroes) /// Fill the block with defaults (zeroes)
/// </summary> /// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void Clear() public void Clear()
{ {
// The cheapest way to do this in C#: // The cheapest way to do this in C#:
this = default(Block8x8F); this = default(Block8x8F);
@ -278,7 +284,7 @@ namespace ImageSharp.Formats
/// TODO: Should be removed when BlockF goes away /// TODO: Should be removed when BlockF goes away
/// </summary> /// </summary>
/// <param name="legacyBlock">Legacy block</param> /// <param name="legacyBlock">Legacy block</param>
internal void LoadFrom(ref BlockF legacyBlock) public void LoadFrom(ref BlockF legacyBlock)
{ {
this.LoadFrom(legacyBlock.Data); this.LoadFrom(legacyBlock.Data);
} }
@ -287,12 +293,11 @@ namespace ImageSharp.Formats
/// TODO: Should be removed when BlockF goes away /// TODO: Should be removed when BlockF goes away
/// </summary> /// </summary>
/// <param name="legacyBlock">Legacy block</param> /// <param name="legacyBlock">Legacy block</param>
internal void CopyTo(ref BlockF legacyBlock) public void CopyTo(ref BlockF legacyBlock)
{ {
this.CopyTo(legacyBlock.Data); this.CopyTo(legacyBlock.Data);
} }
/// <summary> /// <summary>
/// Level shift by +128, clip to [0, 255], and write to buffer. /// Level shift by +128, clip to [0, 255], and write to buffer.
/// </summary> /// </summary>
@ -300,7 +305,7 @@ namespace ImageSharp.Formats
/// <param name="stride">Stride offset</param> /// <param name="stride">Stride offset</param>
/// <param name="tempBlockPtr">Temp Block pointer</param> /// <param name="tempBlockPtr">Temp Block pointer</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal unsafe void CopyColorsTo(MutableSpan<byte> buffer, int stride, Block8x8F* tempBlockPtr) public unsafe void CopyColorsTo(MutableSpan<byte> buffer, int stride, Block8x8F* tempBlockPtr)
{ {
this.TransformByteConvetibleColorValuesInto(ref *tempBlockPtr); this.TransformByteConvetibleColorValuesInto(ref *tempBlockPtr);
@ -328,7 +333,7 @@ namespace ImageSharp.Formats
/// <param name="qt">Quantization table</param> /// <param name="qt">Quantization table</param>
/// <param name="unzigPtr">Pointer to <see cref="UnzigData"/> elements</param> /// <param name="unzigPtr">Pointer to <see cref="UnzigData"/> elements</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [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* s = (float*)src;
float* d = (float*)dest; float* d = (float*)dest;

219
src/ImageSharp/Formats/Jpg/Components/DCT.cs

@ -14,26 +14,6 @@ namespace ImageSharp.Formats
/// </summary> /// </summary>
internal static class DCT internal static class DCT
{ {
/// <summary>
/// Apply floating point IDCT transformation into dest, using a temporary block 'temp' provided by the caller (optimization)
/// </summary>
/// <param name="src">Source</param>
/// <param name="dest">Destination</param>
/// <param name="temp">Temporary block provided by the caller</param>
public static void TransformIDCT(ref Block8x8F src, ref Block8x8F dest, ref Block8x8F temp)
{
src.TransposeInto(ref temp);
IDCT8x4_LeftPart(ref temp, ref dest);
IDCT8x4_RightPart(ref temp, ref dest);
dest.TransposeInto(ref temp);
IDCT8x4_LeftPart(ref temp, ref dest);
IDCT8x4_RightPart(ref temp, ref dest);
dest.MultiplyAllInplace(C_0_125);
}
#pragma warning disable SA1310 // FieldNamesMustNotContainUnderscore #pragma warning disable SA1310 // FieldNamesMustNotContainUnderscore
private static readonly Vector4 C_1_175876 = new Vector4(1.175876f); private static readonly Vector4 C_1_175876 = new Vector4(1.175876f);
@ -63,13 +43,34 @@ namespace ImageSharp.Formats
#pragma warning restore SA1310 // FieldNamesMustNotContainUnderscore #pragma warning restore SA1310 // FieldNamesMustNotContainUnderscore
private static readonly Vector4 InvSqrt2 = new Vector4(0.707107f); private static readonly Vector4 InvSqrt2 = new Vector4(0.707107f);
/// <summary>
/// Apply floating point IDCT transformation into dest, using a temporary block 'temp' provided by the caller (optimization)
/// </summary>
/// <param name="src">Source</param>
/// <param name="dest">Destination</param>
/// <param name="temp">Temporary block provided by the caller</param>
public static void TransformIDCT(ref Block8x8F src, ref Block8x8F dest, ref Block8x8F temp)
{
src.TransposeInto(ref temp);
IDCT8x4_LeftPart(ref temp, ref dest);
IDCT8x4_RightPart(ref temp, ref dest);
dest.TransposeInto(ref temp);
IDCT8x4_LeftPart(ref temp, ref dest);
IDCT8x4_RightPart(ref temp, ref dest);
dest.MultiplyAllInplace(C_0_125);
}
/// <summary> /// <summary>
/// Do IDCT internal operations on the left part of the block. Original src: /// 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 /// https://github.com/norishigefukushima/dct_simd/blob/master/dct/dct8x8_simd.cpp#L261
/// </summary> /// </summary>
/// <param name="s">The source block</param>
/// <param name="d">Destination block</param> /// <param name="d">Destination block</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [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 my1 = s.V1L;
Vector4 my7 = s.V7L; Vector4 my7 = s.V7L;
@ -81,7 +82,7 @@ namespace ImageSharp.Formats
Vector4 mz1 = my3 + my5; Vector4 mz1 = my3 + my5;
Vector4 mz3 = my1 + 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; mz2 = (mz2 * C_1_961571) + mz4;
mz3 = (mz3 * C_0_390181) + mz4; mz3 = (mz3 * C_0_390181) + mz4;
@ -124,8 +125,10 @@ namespace ImageSharp.Formats
/// Original src: /// Original src:
/// https://github.com/norishigefukushima/dct_simd/blob/master/dct/dct8x8_simd.cpp#L261 /// https://github.com/norishigefukushima/dct_simd/blob/master/dct/dct8x8_simd.cpp#L261
/// </summary> /// </summary>
/// <param name="s">The source block</param>
/// <param name="d">The destination block</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [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 my1 = s.V1R;
Vector4 my7 = s.V7R; Vector4 my7 = s.V7R;
@ -187,96 +190,57 @@ namespace ImageSharp.Formats
{ {
Vector4 c0 = s.V0L; Vector4 c0 = s.V0L;
Vector4 c1 = s.V7L; Vector4 c1 = s.V7L;
Vector4 t0 = (c0 + c1); Vector4 t0 = c0 + c1;
Vector4 t7 = (c0 - c1); Vector4 t7 = c0 - c1;
c1 = s.V6L; c1 = s.V6L;
c0 = s.V1L; c0 = s.V1L;
Vector4 t1 = (c0 + c1); Vector4 t1 = c0 + c1;
Vector4 t6 = (c0 - c1); Vector4 t6 = c0 - c1;
c1 = s.V5L; c1 = s.V5L;
c0 = s.V2L; c0 = s.V2L;
Vector4 t2 = (c0 + c1); Vector4 t2 = c0 + c1;
Vector4 t5 = (c0 - c1); Vector4 t5 = c0 - c1;
c0 = s.V3L; c0 = s.V3L;
c1 = s.V4L; c1 = s.V4L;
Vector4 t3 = (c0 + c1); Vector4 t3 = c0 + c1;
Vector4 t4 = (c0 - c1); Vector4 t4 = c0 - c1;
/* c0 = t0 + t3;
c1 = x[0]; c2 = x[7]; t0 = c1 + c2; t7 = c1 - c2; Vector4 c3 = t0 - t3;
c1 = x[1]; c2 = x[6]; t1 = c1 + c2; t6 = c1 - c2; c1 = t1 + t2;
c1 = x[2]; c2 = x[5]; t2 = c1 + c2; t5 = c1 - c2; Vector4 c2 = t1 - t2;
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;
*/
d.V0L = c0 + c1; d.V0L = c0 + c1;
d.V4L = c0 - c1; d.V4L = c0 - c1;
/*y[0] = c0 + c1;
y[4] = c0 - c1;*/
Vector4 w0 = new Vector4(0.541196f); Vector4 w0 = new Vector4(0.541196f);
Vector4 w1 = new Vector4(1.306563f); Vector4 w1 = new Vector4(1.306563f);
d.V2L = (w0 * c2) + (w1 * c3); d.V2L = (w0 * c2) + (w1 * c3);
d.V6L = (w0 * c3) - (w1 * c2); 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); w0 = new Vector4(1.175876f);
w1 = new Vector4(0.785695f); w1 = new Vector4(0.785695f);
c3 = ((w0 * t4) + (w1 * t7)); c3 = (w0 * t4) + (w1 * t7);
c0 = ((w0 * t7) - (w1 * t4)); c0 = (w0 * t7) - (w1 * t4);
/*
c3 = t4 * r[3] + t7 * r[5];
c0 = t7 * r[3] - t4 * r[5];
*/
w0 = new Vector4(1.387040f); w0 = new Vector4(1.387040f);
w1 = new Vector4(0.275899f); w1 = new Vector4(0.275899f);
c2 = ((w0 * t5) + (w1 * t6)); c2 = (w0 * t5) + (w1 * t6);
c1 = ((w0 * t6) - (w1 * t5)); c1 = (w0 * t6) - (w1 * t5);
/*
c2 = t5 * r[1] + t6 * r[7];
c1 = t6 * r[1] - t5 * r[7];
*/
d.V3L = (c0 - c2); d.V3L = c0 - c2;
d.V5L = c3 - c1;
d.V5L = (c3 - c1);
//y[5] = c3 - c1; y[3] = c0 - c2;
Vector4 invsqrt2 = new Vector4(0.707107f); Vector4 invsqrt2 = new Vector4(0.707107f);
c0 = ((c0 + c2) * invsqrt2); c0 = (c0 + c2) * invsqrt2;
c3 = ((c3 + c1) * 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;
/*for(i = 0;i < 8;i++) d.V1L = c0 + c3;
{ d.V7L = c0 - c3;
y[i] *= invsqrt2h;
}*/
} }
/// <summary> /// <summary>
@ -291,95 +255,56 @@ namespace ImageSharp.Formats
{ {
Vector4 c0 = s.V0R; Vector4 c0 = s.V0R;
Vector4 c1 = s.V7R; Vector4 c1 = s.V7R;
Vector4 t0 = (c0 + c1); Vector4 t0 = c0 + c1;
Vector4 t7 = (c0 - c1); Vector4 t7 = c0 - c1;
c1 = s.V6R; c1 = s.V6R;
c0 = s.V1R; c0 = s.V1R;
Vector4 t1 = (c0 + c1); Vector4 t1 = c0 + c1;
Vector4 t6 = (c0 - c1); Vector4 t6 = c0 - c1;
c1 = s.V5R; c1 = s.V5R;
c0 = s.V2R; c0 = s.V2R;
Vector4 t2 = (c0 + c1); Vector4 t2 = c0 + c1;
Vector4 t5 = (c0 - c1); Vector4 t5 = c0 - c1;
c0 = s.V3R; c0 = s.V3R;
c1 = s.V4R; c1 = s.V4R;
Vector4 t3 = (c0 + c1); Vector4 t3 = c0 + c1;
Vector4 t4 = (c0 - c1); Vector4 t4 = c0 - c1;
/* c0 = t0 + t3;
c1 = x[0]; c2 = x[7]; t0 = c1 + c2; t7 = c1 - c2; Vector4 c3 = t0 - t3;
c1 = x[1]; c2 = x[6]; t1 = c1 + c2; t6 = c1 - c2; c1 = t1 + t2;
c1 = x[2]; c2 = x[5]; t2 = c1 + c2; t5 = c1 - c2; Vector4 c2 = t1 - t2;
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;
*/
d.V0R = c0 + c1; d.V0R = c0 + c1;
d.V4R = c0 - c1; d.V4R = c0 - c1;
/*y[0] = c0 + c1;
y[4] = c0 - c1;*/
Vector4 w0 = new Vector4(0.541196f); Vector4 w0 = new Vector4(0.541196f);
Vector4 w1 = new Vector4(1.306563f); Vector4 w1 = new Vector4(1.306563f);
d.V2R = (w0 * c2) + (w1 * c3); d.V2R = (w0 * c2) + (w1 * c3);
d.V6R = (w0 * c3) - (w1 * c2); 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); w0 = new Vector4(1.175876f);
w1 = new Vector4(0.785695f); w1 = new Vector4(0.785695f);
c3 = ((w0 * t4) + (w1 * t7)); c3 = (w0 * t4) + (w1 * t7);
c0 = ((w0 * t7) - (w1 * t4)); c0 = (w0 * t7) - (w1 * t4);
/*
c3 = t4 * r[3] + t7 * r[5];
c0 = t7 * r[3] - t4 * r[5];
*/
w0 = new Vector4(1.387040f); w0 = new Vector4(1.387040f);
w1 = new Vector4(0.275899f); w1 = new Vector4(0.275899f);
c2 = ((w0 * t5) + (w1 * t6)); c2 = (w0 * t5) + (w1 * t6);
c1 = ((w0 * t6) - (w1 * t5)); 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;
d.V1R = (c0 + c3); d.V3R = c0 - c2;
d.V5R = c3 - c1;
d.V7R = (c0 - c3); c0 = (c0 + c2) * InvSqrt2;
//y[1] = c0 + c3; y[7] = c0 - c3; c3 = (c3 + c1) * InvSqrt2;
/*for(i = 0;i < 8;i++) d.V1R = c0 + c3;
{ d.V7R = c0 - c3;
y[i] *= invsqrt2h;
}*/
} }
/// <summary> /// <summary>

36
src/ImageSharp/Formats/Jpg/Components/MutableSpanExtensions.cs

@ -78,8 +78,11 @@ namespace ImageSharp.Formats
data[3] = (int)v.W; data[3] = (int)v.W;
} }
/// <summary>
/// Converts all int values of src to float
/// </summary>
/// <param name="src">Source</param>
/// <returns>A new <see cref="MutableSpan{T}"/> with float values</returns>
public static MutableSpan<float> ConvertToFloat32MutableSpan(this MutableSpan<int> src) public static MutableSpan<float> ConvertToFloat32MutableSpan(this MutableSpan<int> src)
{ {
MutableSpan<float> result = new MutableSpan<float>(src.TotalCount); MutableSpan<float> result = new MutableSpan<float>(src.TotalCount);
@ -87,9 +90,15 @@ namespace ImageSharp.Formats
{ {
result[i] = (float)src[i]; result[i] = (float)src[i];
} }
return result; return result;
} }
/// <summary>
/// Converts all float values of src to int
/// </summary>
/// <param name="src">Source</param>
/// <returns>A new <see cref="MutableSpan{T}"/> with float values</returns>
public static MutableSpan<int> ConvertToInt32MutableSpan(this MutableSpan<float> src) public static MutableSpan<int> ConvertToInt32MutableSpan(this MutableSpan<float> src)
{ {
MutableSpan<int> result = new MutableSpan<int>(src.TotalCount); MutableSpan<int> result = new MutableSpan<int>(src.TotalCount);
@ -97,9 +106,16 @@ namespace ImageSharp.Formats
{ {
result[i] = (int)src[i]; result[i] = (int)src[i];
} }
return result; return result;
} }
/// <summary>
/// Add a scalar to all values of src
/// </summary>
/// <param name="src">The source</param>
/// <param name="scalar">The scalar value to add</param>
/// <returns>A new instance of <see cref="MutableSpan{T}"/></returns>
public static MutableSpan<float> AddScalarToAllValues(this MutableSpan<float> src, float scalar) public static MutableSpan<float> AddScalarToAllValues(this MutableSpan<float> src, float scalar)
{ {
MutableSpan<float> result = new MutableSpan<float>(src.TotalCount); MutableSpan<float> result = new MutableSpan<float>(src.TotalCount);
@ -107,9 +123,16 @@ namespace ImageSharp.Formats
{ {
result[i] = src[i] + scalar; result[i] = src[i] + scalar;
} }
return result; return result;
} }
/// <summary>
/// Add a scalar to all values of src
/// </summary>
/// <param name="src">The source</param>
/// <param name="scalar">The scalar value to add</param>
/// <returns>A new instance of <see cref="MutableSpan{T}"/></returns>
public static MutableSpan<int> AddScalarToAllValues(this MutableSpan<int> src, int scalar) public static MutableSpan<int> AddScalarToAllValues(this MutableSpan<int> src, int scalar)
{ {
MutableSpan<int> result = new MutableSpan<int>(src.TotalCount); MutableSpan<int> result = new MutableSpan<int>(src.TotalCount);
@ -117,10 +140,16 @@ namespace ImageSharp.Formats
{ {
result[i] = src[i] + scalar; result[i] = src[i] + scalar;
} }
return result; return result;
} }
/// <summary>
/// Copy all values in src to a new <see cref="MutableSpan{T}"/> instance
/// </summary>
/// <typeparam name="T">Element type</typeparam>
/// <param name="src">The source</param>
/// <returns>A new instance of <see cref="MutableSpan{T}"/></returns>
public static MutableSpan<T> Copy<T>(this MutableSpan<T> src) public static MutableSpan<T> Copy<T>(this MutableSpan<T> src)
{ {
MutableSpan<T> result = new MutableSpan<T>(src.TotalCount); MutableSpan<T> result = new MutableSpan<T>(src.TotalCount);
@ -128,6 +157,7 @@ namespace ImageSharp.Formats
{ {
result[i] = src[i]; result[i] = src[i];
} }
return result; return result;
} }
} }

4
src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs

@ -62,7 +62,7 @@ namespace ImageSharp.Formats
/// The AC table index /// The AC table index
/// </summary> /// </summary>
private const int AcTable = 1; private const int AcTable = 1;
/// <summary> /// <summary>
/// The component array /// The component array
/// </summary> /// </summary>
@ -1809,7 +1809,7 @@ namespace ImageSharp.Formats
// Dequantize, perform the inverse DCT and store the block to the image. // Dequantize, perform the inverse DCT and store the block to the image.
Block8x8F.UnZig(b, qt, unzigPtr); Block8x8F.UnZig(b, qt, unzigPtr);
DCT.TransformIDCT(ref* b, ref *temp1, ref *temp2); DCT.TransformIDCT(ref *b, ref *temp1, ref *temp2);
byte[] dst; byte[] dst;
int offset; int offset;

433
src/ImageSharp/Formats/Jpg/JpegEncoderCore.cs

@ -9,7 +9,7 @@ namespace ImageSharp.Formats
using System.IO; using System.IO;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
/// <summary> /// <summary>
/// Image encoder for writing an image to a stream as a jpeg. /// Image encoder for writing an image to a stream as a jpeg.
/// </summary> /// </summary>
@ -113,19 +113,21 @@ namespace ImageSharp.Formats
/// Counts the number of bits needed to hold an integer. /// Counts the number of bits needed to hold an integer.
/// </summary> /// </summary>
private static readonly uint[] BitCountLut = 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, 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, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 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, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 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, 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, 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, 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, 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,
};
/// <summary> /// <summary>
/// The unscaled quantization tables in zig-zag order. Each /// The unscaled quantization tables in zig-zag order. Each
@ -134,23 +136,53 @@ namespace ImageSharp.Formats
/// zig-zag order. /// zig-zag order.
/// </summary> /// </summary>
private static readonly byte[,] UnscaledQuant = private static readonly byte[,] UnscaledQuant =
{
{ {
// Luminance. {
16, 11, 12, 14, 12, 10, 16, 14, 13, 14, 18, 17, 16, 19, 24, 40, // Luminance.
26, 24, 22, 22, 24, 49, 35, 37, 29, 40, 58, 51, 61, 60, 57, 51, 16, 11, 12, 14, 12, 10, 16, 14, 13, 14, 18, 17, 16, 19, 24,
56, 55, 64, 72, 92, 78, 64, 68, 87, 69, 55, 56, 80, 109, 81, 40, 26, 24, 22, 22, 24, 49, 35, 37, 29, 40, 58, 51, 61, 60,
87, 95, 98, 103, 104, 103, 62, 77, 113, 121, 112, 100, 120, 92, 57, 51, 56, 55, 64, 72, 92, 78, 64, 68, 87, 69, 55, 56, 80,
101, 103, 99, 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,
}
};
/// <summary>
/// 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&lt;&lt;4 | 0x00.
/// </summary>
private static readonly byte[] SosHeaderYCbCr =
{ {
// Chrominance. JpegConstants.Markers.XFF, JpegConstants.Markers.SOS, // Marker
17, 18, 18, 24, 21, 24, 47, 26, 26, 47, 99, 66, 56, 66, 99, 99, 0x00, 0x0c,
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, // Length (high byte, low byte), must be 6 + 2 * (number of components in scan)
99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 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)
};
/// <summary> /// <summary>
/// A scratch buffer to reduce allocations. /// A scratch buffer to reduce allocations.
@ -168,39 +200,14 @@ namespace ImageSharp.Formats
private readonly byte[] huffmanBuffer = new byte[179]; private readonly byte[] huffmanBuffer = new byte[179];
/// <summary> /// <summary>
/// The scaled quantization tables, in zig-zag order. /// The scaled luminance table, in zig-zag order.
/// </summary> /// </summary>
//private readonly float[][] quant = new float[QuantizationTableCount][];
//private readonly float[] quant = new float[QuantizationTableCount* Block8x8F.ScalarCount];
private Block8x8F luminanceQuantTable; private Block8x8F luminanceQuantTable;
private Block8x8F chrominanceQuantTable;
/// <summary> /// <summary>
/// The SOS (Start Of Scan) marker "\xff\xda" followed by 12 bytes: /// The scaled chrominance table, in zig-zag order.
/// - 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&lt;&lt;4 | 0x00.
/// </summary> /// </summary>
private readonly byte[] sosHeaderYCbCr = private Block8x8F chrominanceQuantTable;
{
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)
};
/// <summary> /// <summary>
/// The accumulated bits to write to the stream. /// The accumulated bits to write to the stream.
@ -249,15 +256,13 @@ namespace ImageSharp.Formats
/// <summary> /// <summary>
/// The AC luminance huffman table index /// The AC luminance huffman table index
/// </summary> /// </summary>
LuminanceAC = 1, LuminanceAC = 1,
/// <summary> /// <summary>
/// The DC chrominance huffman table index /// The DC chrominance huffman table index
/// </summary> /// </summary>
ChrominanceDC = 2, ChrominanceDC = 2,
/// <summary> /// <summary>
/// The AC chrominance huffman table index /// The AC chrominance huffman table index
/// </summary> /// </summary>
@ -282,26 +287,6 @@ namespace ImageSharp.Formats
Chrominance = 1, 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;
}
}
/// <summary> /// <summary>
/// Encode writes the image to the jpeg baseline format with the given options. /// Encode writes the image to the jpeg baseline format with the given options.
/// </summary> /// </summary>
@ -325,11 +310,6 @@ namespace ImageSharp.Formats
this.outputStream = stream; this.outputStream = stream;
this.subsample = sample; this.subsample = sample;
//for (int i = 0; i < QuantizationTableCount; i++)
//{
// this.quant[i] = new float[];
//}
if (quality < 1) if (quality < 1)
{ {
quality = 1; quality = 1;
@ -352,10 +332,9 @@ namespace ImageSharp.Formats
} }
// Initialize the quantization tables. // Initialize the quantization tables.
InitQuantizationTable(0, scale, ref this.luminanceQuantTable); InitQuantizationTable(0, scale, ref this.luminanceQuantTable);
InitQuantizationTable(1, scale, ref this.chrominanceQuantTable); InitQuantizationTable(1, scale, ref this.chrominanceQuantTable);
// Compute number of components based on input image type. // Compute number of components based on input image type.
int componentCount = 3; int componentCount = 3;
@ -385,7 +364,80 @@ namespace ImageSharp.Formats
stream.Write(this.buffer, 0, 2); stream.Write(this.buffer, 0, 2);
stream.Flush(); 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;
}
}
/// <summary>
/// Converts the 8x8 region of the image whose top-left corner is x,y to its YCbCr values.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="pixels">The pixel accessor.</param>
/// <param name="x">The x-position within the image.</param>
/// <param name="y">The y-position within the image.</param>
/// <param name="yBlock">The luminance block.</param>
/// <param name="cbBlock">The red chroma block.</param>
/// <param name="crBlock">The blue chroma block.</param>
private static void ToYCbCr<TColor>(
PixelAccessor<TColor> pixels,
int x,
int y,
Block8x8F* yBlock,
Block8x8F* cbBlock,
Block8x8F* crBlock)
where TColor : struct, IPackedPixel, IEquatable<TColor>
{
float* yBlockRaw = (float*)yBlock;
float* cbBlockRaw = (float*)cbBlock;
float* crBlockRaw = (float*)crBlock;
using (PixelArea<TColor> rgbBytes = new PixelArea<TColor>(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;
}
}
}
}
/// <summary> /// <summary>
/// Emits the least significant count of bits of bits to the bit-stream. /// Emits the least significant count of bits of bits to the bit-stream.
/// The precondition is bits <example>&lt; 1&lt;&lt;nBits &amp;&amp; nBits &lt;= 16</example>. /// The precondition is bits <example>&lt; 1&lt;&lt;nBits &amp;&amp; nBits &lt;= 16</example>.
@ -485,30 +537,32 @@ namespace ImageSharp.Formats
/// <param name="quant">Quantization table</param> /// <param name="quant">Quantization table</param>
/// <param name="unzigPtr">The 8x8 Unzig block ptr</param> /// <param name="unzigPtr">The 8x8 Unzig block ptr</param>
/// <returns>The <see cref="int"/></returns> /// <returns>The <see cref="int"/></returns>
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); DCT.TransformFDCT(ref *src, ref *tempDest, ref *temp2);
Block8x8F.UnZigDivRound(tempDest, temp2, quant, unzigPtr); Block8x8F.UnZigDivRound(tempDest, temp2, quant, unzigPtr);
//Block8x8F.RoundAll(tempDest);
float* d = (float*)temp2; float* d = (float*)temp2;
float* q = (float*)quant;
// Emit the DC delta. // Emit the DC delta.
//float dc = Round(d[0], q[0]);
float dc = d[0]; float dc = d[0];
this.EmitHuffRLE((HuffIndex)((2 * (int)index) + 0), 0, (int)(dc - prevDC)); this.EmitHuffRLE((HuffIndex)((2 * (int)index) + 0), 0, (int)(dc - prevDC));
// Emit the AC components. // Emit the AC components.
HuffIndex h = (HuffIndex)((2 * (int)index) + 1); HuffIndex h = (HuffIndex)((2 * (int)index) + 1);
int runLength = 0; int runLength = 0;
for (int zig = 1; zig < Block.BlockSize; zig++) for (int zig = 1; zig < Block.BlockSize; zig++)
{ {
//float ac = Round(d[unzigPtr[zig]], q[zig]);
float ac = d[zig]; float ac = d[zig];
if (ac == 0) if (ac == 0)
@ -536,107 +590,6 @@ namespace ImageSharp.Formats
return dc; return dc;
} }
/// <summary>
/// Converts the 8x8 region of the image whose top-left corner is x,y to its YCbCr values.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="pixels">The pixel accessor.</param>
/// <param name="x">The x-position within the image.</param>
/// <param name="y">The y-position within the image.</param>
/// <param name="yBlock">The luminance block.</param>
/// <param name="cbBlock">The red chroma block.</param>
/// <param name="crBlock">The blue chroma block.</param>
private static void ToYCbCr<TColor>(
PixelAccessor<TColor> pixels,
int x,
int y,
Block8x8F* yBlock,
Block8x8F* cbBlock,
Block8x8F* crBlock)
where TColor : struct, IPackedPixel, IEquatable<TColor>
{
float* yBlockRaw = (float*)yBlock;
float* cbBlockRaw = (float*)cbBlock;
float* crBlockRaw = (float*)crBlock;
PixelAccessor<Color> asStandardColorAccessor = pixels as PixelAccessor<Color>;
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<Color> 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;
}
}
}
/// <summary> /// <summary>
/// Writes the application header containing the JFIF identifier plus extra data. /// Writes the application header containing the JFIF identifier plus extra data.
/// </summary> /// </summary>
@ -736,10 +689,11 @@ namespace ImageSharp.Formats
WriteDataToDqt(dqt, ref offset, QuantIndex.Luminance, ref this.luminanceQuantTable); WriteDataToDqt(dqt, ref offset, QuantIndex.Luminance, ref this.luminanceQuantTable);
WriteDataToDqt(dqt, ref offset, QuantIndex.Chrominance, ref this.chrominanceQuantTable); WriteDataToDqt(dqt, ref offset, QuantIndex.Chrominance, ref this.chrominanceQuantTable);
this.outputStream.Write(dqt, 0, dqt.Length); 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) private static void WriteDataToDqt(byte[] dqt, ref int offset, QuantIndex i, ref Block8x8F q)
{ {
dqt[offset++] = (byte)i; dqt[offset++] = (byte)i;
@ -748,6 +702,7 @@ namespace ImageSharp.Formats
dqt[offset++] = (byte)q[j]; dqt[offset++] = (byte)q[j];
} }
} }
#pragma warning restore SA1204
/// <summary> /// <summary>
/// Writes the Start Of Frame (Baseline) marker /// 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[2] = (byte)(height & 0xff); // (2 bytes, Hi-Lo), must be > 0 if DNL not supported
this.buffer[3] = (byte)(width >> 8); 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[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) if (componentCount == 1)
{ {
this.buffer[6] = 1; this.buffer[6] = 1;
@ -863,7 +820,7 @@ namespace ImageSharp.Formats
where TColor : struct, IPackedPixel, IEquatable<TColor> where TColor : struct, IPackedPixel, IEquatable<TColor>
{ {
// TODO: We should allow grayscale writing. // 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) switch (this.subsample)
{ {
@ -887,12 +844,12 @@ namespace ImageSharp.Formats
private void Encode444<TColor>(PixelAccessor<TColor> pixels) private void Encode444<TColor>(PixelAccessor<TColor> pixels)
where TColor : struct, IPackedPixel, IEquatable<TColor> where TColor : struct, IPackedPixel, IEquatable<TColor>
{ {
Block8x8F b = new Block8x8F(); Block8x8F b = default(Block8x8F);
Block8x8F cb = new Block8x8F(); Block8x8F cb = default(Block8x8F);
Block8x8F cr = new Block8x8F(); Block8x8F cr = default(Block8x8F);
Block8x8F temp1 = new Block8x8F(); Block8x8F temp1 = default(Block8x8F);
Block8x8F temp2 = new Block8x8F(); Block8x8F temp2 = default(Block8x8F);
Block8x8F onStackLuminanceQuantTable = this.luminanceQuantTable; Block8x8F onStackLuminanceQuantTable = this.luminanceQuantTable;
Block8x8F onStackChrominanceQuantTable = this.chrominanceQuantTable; Block8x8F onStackChrominanceQuantTable = this.chrominanceQuantTable;
@ -908,16 +865,42 @@ namespace ImageSharp.Formats
{ {
ToYCbCr(pixels, x, y, &b, &cb, &cr); ToYCbCr(pixels, x, y, &b, &cb, &cr);
prevDCY = this.WriteBlock(QuantIndex.Luminance, prevDCY, &b, &temp1, &temp2, &onStackLuminanceQuantTable, unzig.Data); prevDCY = this.WriteBlock(
prevDCCb = this.WriteBlock(QuantIndex.Chrominance, prevDCCb, &cb, &temp1, &temp2, &onStackChrominanceQuantTable, unzig.Data); QuantIndex.Luminance,
prevDCCr = this.WriteBlock(QuantIndex.Chrominance, prevDCCr, &cr, &temp1, &temp2, &onStackChrominanceQuantTable, unzig.Data); 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
/// <summary>
/// This struct belongs to Encode420. Much easeier to understand code if they are together. Why should I move it Up? :P
/// </summary>
private struct BlockQuad
{ {
public fixed float Data[4*Block8x8F.ScalarCount]; public fixed float Data[4 * Block8x8F.ScalarCount];
} }
/// <summary> /// <summary>
@ -929,15 +912,15 @@ namespace ImageSharp.Formats
private void Encode420<TColor>(PixelAccessor<TColor> pixels) private void Encode420<TColor>(PixelAccessor<TColor> pixels)
where TColor : struct, IPackedPixel, IEquatable<TColor> where TColor : struct, IPackedPixel, IEquatable<TColor>
{ {
Block8x8F b = new Block8x8F(); Block8x8F b = default(Block8x8F);
BlockQuad cb = new BlockQuad(); BlockQuad cb = default(BlockQuad);
BlockQuad cr = new BlockQuad(); BlockQuad cr = default(BlockQuad);
Block8x8F* cbPtr = (Block8x8F*)cb.Data; Block8x8F* cbPtr = (Block8x8F*)cb.Data;
Block8x8F* crPtr = (Block8x8F*)cr.Data; Block8x8F* crPtr = (Block8x8F*)cr.Data;
Block8x8F temp1 = new Block8x8F(); Block8x8F temp1 = default(Block8x8F);
Block8x8F temp2 = new Block8x8F(); Block8x8F temp2 = default(Block8x8F);
Block8x8F onStackLuminanceQuantTable = this.luminanceQuantTable; Block8x8F onStackLuminanceQuantTable = this.luminanceQuantTable;
Block8x8F onStackChrominanceQuantTable = this.chrominanceQuantTable; Block8x8F onStackChrominanceQuantTable = this.chrominanceQuantTable;
@ -990,6 +973,8 @@ namespace ImageSharp.Formats
} }
} }
#pragma warning restore SA1201
/// <summary> /// <summary>
/// Writes the header for a marker with the given length. /// Writes the header for a marker with the given length.
/// </summary> /// </summary>
@ -1040,11 +1025,6 @@ namespace ImageSharp.Formats
/// </summary> /// </summary>
private class HuffmanLut private class HuffmanLut
{ {
/// <summary>
/// The collection of huffman values.
/// </summary>
public uint[] Values { get; }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="HuffmanLut"/> class. /// Initializes a new instance of the <see cref="HuffmanLut"/> class.
/// </summary> /// </summary>
@ -1079,6 +1059,11 @@ namespace ImageSharp.Formats
code <<= 1; code <<= 1;
} }
} }
/// <summary>
/// Gets the collection of huffman values.
/// </summary>
public uint[] Values { get; }
} }
} }
} }

63
src/ImageSharp/Formats/Jpg/JpegUtils.cs

@ -1,10 +1,43 @@
namespace ImageSharp.Formats // <copyright file="JpegUtils.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats
{ {
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
/// <summary>
/// Jpeg specific utilities and extension methods
/// </summary>
internal static unsafe class JpegUtils internal static unsafe class JpegUtils
{ {
/// <summary>
/// Copy a region of an image into dest. De "outlier" area will be stretched out with pixels on the right and bottom of the image.
/// </summary>
/// <typeparam name="TColor">The pixel type</typeparam>
/// <param name="pixels">The input pixel acessor</param>
/// <param name="dest">The destination <see cref="PixelArea{TColor}"/> </param>
/// <param name="sourceY">Starting Y coord</param>
/// <param name="sourceX">Starting X coord</param>
public static void CopyRGBBytesStretchedTo<TColor>(
this PixelAccessor<TColor> pixels,
PixelArea<TColor> dest,
int sourceY,
int sourceX)
where TColor : struct, IPackedPixel, IEquatable<TColor>
{
pixels.CopyTo(dest, sourceY, sourceX);
int stretchFromX = pixels.Width - sourceX;
int stretchFromY = pixels.Height - sourceY;
StretchPixels(dest, stretchFromX, stretchFromY);
}
/// <summary>
/// Copy an RGB value
/// </summary>
/// <param name="source">Source pointer</param>
/// <param name="dest">Destination pointer</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void CopyRgb(byte* source, byte* dest) internal static void CopyRgb(byte* source, byte* dest)
{ {
@ -13,7 +46,7 @@
*dest = *source; // B *dest = *source; // B
} }
private static unsafe void StretchPixels<TColor>(PixelArea<TColor> area, int fromX, int fromY) private static void StretchPixels<TColor>(PixelArea<TColor> area, int fromX, int fromY)
where TColor : struct, IPackedPixel, IEquatable<TColor> where TColor : struct, IPackedPixel, IEquatable<TColor>
{ {
if (IsInvalidStretchArea(area, fromX, fromY)) if (IsInvalidStretchArea(area, fromX, fromY))
@ -23,12 +56,12 @@
for (int y = 0; y < fromY; y++) 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++) for (int x = fromX; x < area.Width; x++)
{ {
byte* prevPtr = ptrBase + (x - 1) * 3; byte* prevPtr = ptrBase + ((x - 1) * 3);
byte* currPtr = ptrBase + x * 3; byte* currPtr = ptrBase + (x * 3);
CopyRgb(prevPtr, currPtr); CopyRgb(prevPtr, currPtr);
} }
@ -36,8 +69,8 @@
for (int y = fromY; y < area.Height; y++) for (int y = fromY; y < area.Height; y++)
{ {
byte* currBase = (byte*)area.DataPointer + y * area.RowByteCount; byte* currBase = (byte*)area.DataPointer + (y * area.RowByteCount);
byte* prevBase = (byte*)area.DataPointer + (y - 1) * area.RowByteCount; byte* prevBase = (byte*)area.DataPointer + ((y - 1) * area.RowByteCount);
for (int x = 0; x < area.Width; x++) for (int x = 0; x < area.Width; x++)
{ {
@ -51,22 +84,10 @@
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool IsInvalidStretchArea<TColor>(PixelArea<TColor> area, int fromX, int fromY) where TColor : struct, IPackedPixel, IEquatable<TColor> private static bool IsInvalidStretchArea<TColor>(PixelArea<TColor> area, int fromX, int fromY)
{
return fromX <= 0 || fromY <= 0 || fromX >= area.Width || fromY >= area.Height;
}
public static void CopyRGBBytesStretchedTo<TColor>(
this PixelAccessor<TColor> pixels,
PixelArea<TColor> dest,
int sourceY,
int sourceX)
where TColor : struct, IPackedPixel, IEquatable<TColor> where TColor : struct, IPackedPixel, IEquatable<TColor>
{ {
pixels.CopyTo(dest, sourceY, sourceX); return fromX <= 0 || fromY <= 0 || fromX >= area.Width || fromY >= area.Height;
int stretchFromX = pixels.Width - sourceX;
int stretchFromY = pixels.Height - sourceY;
StretchPixels(dest, stretchFromX, stretchFromY);
} }
} }
} }

30
src/ImageSharp/Formats/Jpg/UnzigData.cs

@ -1,4 +1,8 @@
namespace ImageSharp.Formats // <copyright file="UnzigData.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats
{ {
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@ -11,15 +15,10 @@
/// </summary> /// </summary>
internal unsafe struct UnzigData internal unsafe struct UnzigData
{ {
internal fixed int Data[64]; /// <summary>
/// Copy of <see cref="Unzig"/> in a value type
public static UnzigData Create() /// </summary>
{ public fixed int Data[64];
UnzigData result = new UnzigData();
int* unzigPtr = result.Data;
Marshal.Copy(Unzig, 0, (IntPtr)unzigPtr, 64);
return result;
}
/// <summary> /// <summary>
/// Unzig maps from the zigzag ordering to the natural ordering. For example, /// Unzig maps from the zigzag ordering to the natural ordering. For example,
@ -34,5 +33,16 @@
53, 60, 61, 54, 47, 55, 62, 63, 53, 60, 61, 54, 47, 55, 62, 63,
}; };
/// <summary>
/// Creates and fills an instance of <see cref="UnzigData"/> with Jpeg unzig indices
/// </summary>
/// <returns>The new instance</returns>
public static UnzigData Create()
{
UnzigData result = default(UnzigData);
int* unzigPtr = result.Data;
Marshal.Copy(Unzig, 0, (IntPtr)unzigPtr, 64);
return result;
}
} }
} }

6
src/ImageSharp/Image/PixelAccessor{TColor}.cs

@ -186,8 +186,7 @@ namespace ImageSharp
int width = Math.Min(area.Width, this.Width - targetX); int width = Math.Min(area.Width, this.Width - targetX);
int height = Math.Min(area.Height, this.Height - targetY); 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) switch (area.ComponentOrder)
{ {
case ComponentOrder.ZYX: case ComponentOrder.ZYX:
@ -221,8 +220,7 @@ namespace ImageSharp
int width = Math.Min(area.Width, this.Width - sourceX); int width = Math.Min(area.Width, this.Width - sourceX);
int height = Math.Min(area.Height, this.Height - sourceY); 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) switch (area.ComponentOrder)
{ {
case ComponentOrder.ZYX: case ComponentOrder.ZYX:

20
src/ImageSharp/PixelAccessor.cs

@ -109,6 +109,26 @@ namespace ImageSharp
} }
} }
/// <inheritdoc />
protected override unsafe void CopyToXYZ(PixelArea<Color> 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;
}
}
}
/// <inheritdoc /> /// <inheritdoc />
protected override void CopyToZYXW(PixelArea<Color> area, int sourceY, int sourceX, int width, int height) protected override void CopyToZYXW(PixelArea<Color> area, int sourceY, int sourceX, int width, int height)
{ {

28
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<TColor>(TestImageProvider<TColor> provider, JpegSubsample subSample, int quality)
where TColor : struct, IPackedPixel, IEquatable<TColor>
{
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<TColor> CreateTestImage<TColor>(GenericFactory<TColor> factory) public static Image<TColor> CreateTestImage<TColor>(GenericFactory<TColor> factory)
where TColor : struct, IPackedPixel, IEquatable<TColor> where TColor : struct, IPackedPixel, IEquatable<TColor>
{ {

6
tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs

@ -90,5 +90,11 @@ namespace ImageSharp.Tests
return this; return this;
} }
public override string ToString()
{
string provName = this.GetType().Name.Replace("Provider", "");
return $"{this.SourceFileOrDescription}[{this.PixelType}]";
}
} }
} }
Loading…
Cancel
Save