Browse Source

StyleCop :S

pull/58/head
Anton Firszov 10 years ago
parent
commit
446b6c7337
  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.
// </copyright>
// ReSharper disable InconsistentNaming
namespace ImageSharp.Formats
{
using System;
@ -15,6 +16,7 @@ namespace ImageSharp.Formats
/// </summary>
internal partial struct Block8x8F
{
#pragma warning disable SA1204 // Static members must appear before non-static members
/// <summary>
/// Vector count
/// </summary>
@ -141,7 +143,7 @@ namespace ImageSharp.Formats
}
/// <summary>
/// Multiply in place
/// Multiply all elements of the block.
/// </summary>
/// <param name="scaleVec">Vector to multiply by</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -165,6 +167,10 @@ namespace ImageSharp.Formats
this.V7R *= scaleVec;
}
/// <summary>
/// Adds a vector to all elements of the block.
/// </summary>
/// <param name="diff">The added vector</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddToAllInplace(Vector4 diff)
{
@ -185,7 +191,7 @@ namespace ImageSharp.Formats
this.V7L += diff;
this.V7R += diff;
}
/// <summary>
/// Pointer-based "Indexer" (getter part)
/// </summary>
@ -193,7 +199,7 @@ namespace ImageSharp.Formats
/// <param name="idx">Index</param>
/// <returns>The scaleVec value at the specified index</returns>
[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
/// <param name="idx">Index</param>
/// <param name="value">Value</param>
[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
/// <param name="qtPtr">Qt pointer</param>
/// <param name="unzigPtr">Unzig pointer</param>
[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
/// </summary>
/// <param name="dest">Destination</param>
internal unsafe void CopyTo(MutableSpan<int> dest)
public unsafe void CopyTo(MutableSpan<int> dest)
{
fixed (Vector4* ptr = &this.V0L)
{
@ -252,7 +258,7 @@ namespace ImageSharp.Formats
/// Load raw 32bit floating point data from source
/// </summary>
/// <param name="source">Source</param>
internal unsafe void LoadFrom(MutableSpan<int> source)
public unsafe void LoadFrom(MutableSpan<int> source)
{
fixed (Vector4* ptr = &this.V0L)
{
@ -263,12 +269,12 @@ namespace ImageSharp.Formats
}
}
}
/// <summary>
/// Fill the block with defaults (zeroes)
/// </summary>
[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
/// </summary>
/// <param name="legacyBlock">Legacy block</param>
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
/// </summary>
/// <param name="legacyBlock">Legacy block</param>
internal void CopyTo(ref BlockF legacyBlock)
public void CopyTo(ref BlockF legacyBlock)
{
this.CopyTo(legacyBlock.Data);
}
/// <summary>
/// Level shift by +128, clip to [0, 255], and write to buffer.
/// </summary>
@ -300,7 +305,7 @@ namespace ImageSharp.Formats
/// <param name="stride">Stride offset</param>
/// <param name="tempBlockPtr">Temp Block pointer</param>
[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);
@ -328,7 +333,7 @@ namespace ImageSharp.Formats
/// <param name="qt">Quantization table</param>
/// <param name="unzigPtr">Pointer to <see cref="UnzigData"/> elements</param>
[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;

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

@ -14,26 +14,6 @@ namespace ImageSharp.Formats
/// </summary>
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
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);
/// <summary>
/// Apply floating point IDCT transformation into dest, using a temporary block 'temp' provided by the caller (optimization)
/// </summary>
/// <param name="src">Source</param>
/// <param name="dest">Destination</param>
/// <param name="temp">Temporary block provided by the caller</param>
public static void TransformIDCT(ref Block8x8F src, ref Block8x8F dest, ref Block8x8F temp)
{
src.TransposeInto(ref temp);
IDCT8x4_LeftPart(ref temp, ref dest);
IDCT8x4_RightPart(ref temp, ref dest);
dest.TransposeInto(ref temp);
IDCT8x4_LeftPart(ref temp, ref dest);
IDCT8x4_RightPart(ref temp, ref dest);
dest.MultiplyAllInplace(C_0_125);
}
/// <summary>
/// Do IDCT internal operations on the left part of the block. Original src:
/// https://github.com/norishigefukushima/dct_simd/blob/master/dct/dct8x8_simd.cpp#L261
/// </summary>
/// <param name="s">The source block</param>
/// <param name="d">Destination block</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
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
/// </summary>
/// <param name="s">The source block</param>
/// <param name="d">The destination block</param>
[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;
}
/// <summary>
@ -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;
}
/// <summary>

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

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

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

@ -62,7 +62,7 @@ namespace ImageSharp.Formats
/// The AC table index
/// </summary>
private const int AcTable = 1;
/// <summary>
/// The component array
/// </summary>
@ -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;

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

@ -9,7 +9,7 @@ namespace ImageSharp.Formats
using System.IO;
using System.Numerics;
using System.Runtime.CompilerServices;
/// <summary>
/// Image encoder for writing an image to a stream as a jpeg.
/// </summary>
@ -113,19 +113,21 @@ namespace ImageSharp.Formats
/// Counts the number of bits needed to hold an integer.
/// </summary>
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,
};
/// <summary>
/// The unscaled quantization tables in zig-zag order. Each
@ -134,23 +136,53 @@ namespace ImageSharp.Formats
/// zig-zag order.
/// </summary>
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,
}
};
/// <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.
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)
};
/// <summary>
/// A scratch buffer to reduce allocations.
@ -168,39 +200,14 @@ namespace ImageSharp.Formats
private readonly byte[] huffmanBuffer = new byte[179];
/// <summary>
/// The scaled quantization tables, in zig-zag order.
/// The scaled luminance table, in zig-zag order.
/// </summary>
//private readonly float[][] quant = new float[QuantizationTableCount][];
//private readonly float[] quant = new float[QuantizationTableCount* Block8x8F.ScalarCount];
private Block8x8F luminanceQuantTable;
private Block8x8F chrominanceQuantTable;
/// <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.
/// The scaled chrominance table, in zig-zag order.
/// </summary>
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;
/// <summary>
/// The accumulated bits to write to the stream.
@ -249,15 +256,13 @@ namespace ImageSharp.Formats
/// <summary>
/// The AC luminance huffman table index
/// </summary>
LuminanceAC = 1,
/// <summary>
/// The DC chrominance huffman table index
/// </summary>
ChrominanceDC = 2,
/// <summary>
/// The AC chrominance huffman table index
/// </summary>
@ -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;
}
}
/// <summary>
/// Encode writes the image to the jpeg baseline format with the given options.
/// </summary>
@ -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;
}
}
/// <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>
/// 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>.
@ -485,30 +537,32 @@ namespace ImageSharp.Formats
/// <param name="quant">Quantization table</param>
/// <param name="unzigPtr">The 8x8 Unzig block ptr</param>
/// <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);
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;
}
/// <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>
/// Writes the application header containing the JFIF identifier plus extra data.
/// </summary>
@ -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
/// <summary>
/// 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<TColor>
{
// 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<TColor>(PixelAccessor<TColor> pixels)
where TColor : struct, IPackedPixel, IEquatable<TColor>
{
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
/// <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>
@ -929,15 +912,15 @@ namespace ImageSharp.Formats
private void Encode420<TColor>(PixelAccessor<TColor> pixels)
where TColor : struct, IPackedPixel, IEquatable<TColor>
{
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
/// <summary>
/// Writes the header for a marker with the given length.
/// </summary>
@ -1040,11 +1025,6 @@ namespace ImageSharp.Formats
/// </summary>
private class HuffmanLut
{
/// <summary>
/// The collection of huffman values.
/// </summary>
public uint[] Values { get; }
/// <summary>
/// Initializes a new instance of the <see cref="HuffmanLut"/> class.
/// </summary>
@ -1079,6 +1059,11 @@ namespace ImageSharp.Formats
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.Runtime.CompilerServices;
/// <summary>
/// Jpeg specific utilities and extension methods
/// </summary>
internal static unsafe class JpegUtils
{
/// <summary>
/// Copy a region of an image into dest. De "outlier" area will be stretched out with pixels on the right and bottom of the image.
/// </summary>
/// <typeparam name="TColor">The pixel type</typeparam>
/// <param name="pixels">The input pixel acessor</param>
/// <param name="dest">The destination <see cref="PixelArea{TColor}"/> </param>
/// <param name="sourceY">Starting Y coord</param>
/// <param name="sourceX">Starting X coord</param>
public static void CopyRGBBytesStretchedTo<TColor>(
this PixelAccessor<TColor> pixels,
PixelArea<TColor> dest,
int sourceY,
int sourceX)
where TColor : struct, IPackedPixel, IEquatable<TColor>
{
pixels.CopyTo(dest, sourceY, sourceX);
int stretchFromX = pixels.Width - sourceX;
int stretchFromY = pixels.Height - sourceY;
StretchPixels(dest, stretchFromX, stretchFromY);
}
/// <summary>
/// Copy an RGB value
/// </summary>
/// <param name="source">Source pointer</param>
/// <param name="dest">Destination pointer</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void CopyRgb(byte* source, byte* dest)
{
@ -13,7 +46,7 @@
*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>
{
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<TColor>(PixelArea<TColor> area, int fromX, int fromY) where TColor : struct, IPackedPixel, IEquatable<TColor>
{
return fromX <= 0 || fromY <= 0 || fromX >= area.Width || fromY >= area.Height;
}
public static void CopyRGBBytesStretchedTo<TColor>(
this PixelAccessor<TColor> pixels,
PixelArea<TColor> dest,
int sourceY,
int sourceX)
private static bool IsInvalidStretchArea<TColor>(PixelArea<TColor> area, int fromX, int fromY)
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);
return fromX <= 0 || fromY <= 0 || fromX >= area.Width || fromY >= area.Height;
}
}
}

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.Runtime.InteropServices;
@ -11,15 +15,10 @@
/// </summary>
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;
}
/// <summary>
/// Copy of <see cref="Unzig"/> in a value type
/// </summary>
public fixed int Data[64];
/// <summary>
/// Unzig maps from the zigzag ordering to the natural ordering. For example,
@ -34,5 +33,16 @@
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 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:

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 />
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)
where TColor : struct, IPackedPixel, IEquatable<TColor>
{

6
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}]";
}
}
}
Loading…
Cancel
Save