diff --git a/ImageSharp.sln.DotSettings b/ImageSharp.sln.DotSettings
index 22f46979a3..fb607752a9 100644
--- a/ImageSharp.sln.DotSettings
+++ b/ImageSharp.sln.DotSettings
@@ -340,6 +340,7 @@
</Patterns>
True
True
+ AC
DC
FDCT
IDCT
diff --git a/src/ImageSharp/Formats/Jpg/Components/Block8x8F.cs b/src/ImageSharp/Formats/Jpg/Components/Block8x8F.cs
index 4aace549d4..c29591b6a5 100644
--- a/src/ImageSharp/Formats/Jpg/Components/Block8x8F.cs
+++ b/src/ImageSharp/Formats/Jpg/Components/Block8x8F.cs
@@ -319,5 +319,53 @@ namespace ImageSharp.Formats
src += 8;
}
}
+
+ ///
+ /// Unzig the elements of src into dest, while dividing them by elements of qt and rounding the values
+ ///
+ /// Source block
+ /// Destination block
+ /// Quantization table
+ /// Pointer to elements
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static unsafe void UnZigDivRound(Block8x8F* src, Block8x8F* dest, Block8x8F* qt, int* unzigPtr)
+ {
+ float* s = (float*)src;
+ float* d = (float*)dest;
+ float* q = (float*)qt;
+
+ for (int zig = 0; zig < ScalarCount; zig++)
+ {
+ float val = s[unzigPtr[zig]] / q[zig];
+ val = (int)val;
+ d[zig] = val;
+ }
+ }
+
+ ///
+ /// Scales the 16x16 region represented by the 4 source blocks to the 8x8 DST block.
+ ///
+ /// The destination block.
+ /// The source block.
+ public static unsafe void Scale16X16To8X8(Block8x8F* destination, Block8x8F* source)
+ {
+ float* d = (float*)destination;
+ for (int i = 0; i < 4; i++)
+ {
+ int dstOff = ((i & 2) << 4) | ((i & 1) << 2);
+
+ float* iSource = (float*)(source + i);
+
+ for (int y = 0; y < 4; y++)
+ {
+ for (int x = 0; x < 4; x++)
+ {
+ int j = (16 * y) + (2 * x);
+ float sum = iSource[j] + iSource[j + 1] + iSource[j + 8] + iSource[j + 9];
+ d[(8 * y) + x + dstOff] = (sum + 2) / 4;
+ }
+ }
+ }
+ }
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs
index ca2090beed..c8f265ddff 100644
--- a/src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs
+++ b/src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs
@@ -62,20 +62,7 @@ namespace ImageSharp.Formats
/// The AC table index
///
private const int AcTable = 1;
-
- ///
- /// Unzig maps from the zigzag ordering to the natural ordering. For example,
- /// unzig[3] is the column and row of the fourth element in zigzag order. The
- /// value is 16, which means first column (16%8 == 0) and third row (16/8 == 2).
- ///
- private static readonly int[] Unzig =
- {
- 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33,
- 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50,
- 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59, 52, 45, 38, 31, 39, 46,
- 53, 60, 61, 54, 47, 55, 62, 63,
- };
-
+
///
/// The component array
///
@@ -1556,10 +1543,9 @@ namespace ImageSharp.Formats
Block8x8F temp1 = default(Block8x8F);
Block8x8F temp2 = default(Block8x8F);
- // Tricky way to copy contents of the Unzig static variable to the stack:
- StackallocUnzigData unzigOnStack = default(StackallocUnzigData);
- int* unzigPtr = unzigOnStack.Data;
- Marshal.Copy(Unzig, 0, (IntPtr)unzigPtr, 64);
+ UnzigData unzig = UnzigData.Create();
+
+ int* unzigPtr = unzig.Data;
for (int my = 0; my < myy; my++)
{
@@ -2013,7 +1999,7 @@ namespace ImageSharp.Formats
int blah = zig;
- zig = this.RefineNonZeroes(b, zig, zigEnd, val0, delta);
+ zig = this.RefineNonZeroes(b, zig, zigEnd, val0, delta, unzigPtr);
if (zig > zigEnd)
{
throw new ImageFormatException($"Too many coefficients {zig} > {zigEnd}");
@@ -2030,7 +2016,7 @@ namespace ImageSharp.Formats
if (this.eobRun > 0)
{
this.eobRun--;
- this.RefineNonZeroes(b, zig, zigEnd, -1, delta);
+ this.RefineNonZeroes(b, zig, zigEnd, -1, delta, unzigPtr);
}
}
@@ -2043,12 +2029,13 @@ namespace ImageSharp.Formats
/// The zig-zag end index
/// The non-zero entry
/// The low transform offset
+ /// Pointer to the Jpeg Unzig data (data part of )
/// The
- private int RefineNonZeroes(Block8x8F* b, int zig, int zigEnd, int nz, int delta)
+ private int RefineNonZeroes(Block8x8F* b, int zig, int zigEnd, int nz, int delta, int* unzigPtr)
{
for (; zig <= zigEnd; zig++)
{
- int u = Unzig[zig];
+ int u = unzigPtr[zig];
float bu = Block8x8F.GetScalarAt(b, u);
// TODO: Are the equality comparsions OK with floating point values? Isn't an epsilon value necessary?
diff --git a/src/ImageSharp/Formats/Jpg/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpg/JpegEncoderCore.cs
index c1c4e9b57a..548f237b75 100644
--- a/src/ImageSharp/Formats/Jpg/JpegEncoderCore.cs
+++ b/src/ImageSharp/Formats/Jpg/JpegEncoderCore.cs
@@ -7,7 +7,9 @@ namespace ImageSharp.Formats
{
using System;
using System.IO;
-
+ using System.Numerics;
+ using System.Runtime.CompilerServices;
+
///
/// Image encoder for writing an image to a stream as a jpeg.
///
@@ -18,20 +20,8 @@ namespace ImageSharp.Formats
///
private const int QuantizationTableCount = 2;
- ///
- /// Maps from the zig-zag ordering to the natural ordering. For example,
- /// unzig[3] is the column and row of the fourth element in zig-zag order. The
- /// value is 16, which means first column (16%8 == 0) and third row (16/8 == 2).
- ///
- private static readonly int[] Unzig =
- {
- 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26,
- 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57,
- 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59, 52, 45, 38, 31,
- 39, 46, 53, 60, 61, 54, 47, 55, 62, 63,
- };
-
#pragma warning disable SA1118 // ParameterMustNotSpanMultipleLines
+
///
/// The Huffman encoding specifications.
/// This encoder uses the same Huffman encoding for all images.
@@ -44,10 +34,7 @@ namespace ImageSharp.Formats
{
0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0
},
- new byte[]
- {
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
- }),
+ new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }),
new HuffmanSpec(
new byte[]
{
@@ -122,7 +109,7 @@ namespace ImageSharp.Formats
///
/// Counts the number of bits needed to hold an integer.
///
- private readonly byte[] 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,
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,
@@ -143,7 +130,7 @@ namespace ImageSharp.Formats
/// The values are derived from section K.1 after converting from natural to
/// zig-zag order.
///
- private readonly byte[,] unscaledQuant =
+ private static readonly byte[,] UnscaledQuant =
{
{
// Luminance.
@@ -180,7 +167,10 @@ namespace ImageSharp.Formats
///
/// The scaled quantization tables, in zig-zag order.
///
- private readonly byte[][] quant = new byte[QuantizationTableCount][];
+ //private readonly float[][] quant = new float[QuantizationTableCount][];
+ //private readonly float[] quant = new float[QuantizationTableCount* Block8x8F.ScalarCount];
+ private Block8x8F luminanceQuantTable;
+ private Block8x8F chrominanceQuantTable;
///
/// The SOS (Start Of Scan) marker "\xff\xda" followed by 12 bytes:
@@ -251,20 +241,26 @@ namespace ImageSharp.Formats
///
LuminanceDC = 0,
+ // ReSharper disable UnusedMember.Local
+
///
/// The AC luminance huffman table index
///
+
LuminanceAC = 1,
+
///
/// The DC chrominance huffman table index
///
ChrominanceDC = 2,
-
+
///
/// The AC chrominance huffman table index
///
ChrominanceAC = 3,
+
+ // ReSharper restore UnusedMember.Local
}
///
@@ -283,6 +279,26 @@ namespace ImageSharp.Formats
Chrominance = 1,
}
+ private static void InitQuantizationTable(int i, int scale, ref Block8x8F quant)
+ {
+ for (int j = 0; j < Block8x8F.ScalarCount; j++)
+ {
+ int x = UnscaledQuant[i, j];
+ x = ((x * scale) + 50) / 100;
+ if (x < 1)
+ {
+ x = 1;
+ }
+
+ if (x > 255)
+ {
+ x = 255;
+ }
+
+ quant[j] = x;
+ }
+ }
+
///
/// Encode writes the image to the jpeg baseline format with the given options.
///
@@ -306,10 +322,10 @@ namespace ImageSharp.Formats
this.outputStream = stream;
this.subsample = sample;
- for (int i = 0; i < QuantizationTableCount; i++)
- {
- this.quant[i] = new byte[Block.BlockSize];
- }
+ //for (int i = 0; i < QuantizationTableCount; i++)
+ //{
+ // this.quant[i] = new float[];
+ //}
if (quality < 1)
{
@@ -333,26 +349,10 @@ namespace ImageSharp.Formats
}
// Initialize the quantization tables.
- for (int i = 0; i < QuantizationTableCount; i++)
- {
- for (int j = 0; j < Block.BlockSize; j++)
- {
- int x = this.unscaledQuant[i, j];
- x = ((x * scale) + 50) / 100;
- if (x < 1)
- {
- x = 1;
- }
-
- if (x > 255)
- {
- x = 255;
- }
-
- this.quant[i][j] = (byte)x;
- }
- }
+ InitQuantizationTable(0, scale, ref this.luminanceQuantTable);
+ InitQuantizationTable(1, scale, ref this.chrominanceQuantTable);
+
// Compute number of components based on input image type.
int componentCount = 3;
@@ -382,23 +382,7 @@ namespace ImageSharp.Formats
stream.Write(this.buffer, 0, 2);
stream.Flush();
}
-
- ///
- /// Gets the quotient of the two numbers rounded to the nearest integer, instead of rounded to zero.
- ///
- /// The value to divide.
- /// The value to divide by.
- /// The
- private static int Round(int dividend, int divisor)
- {
- if (dividend >= 0)
- {
- return (dividend + (divisor >> 1)) / divisor;
- }
-
- return -((-dividend + (divisor >> 1)) / divisor);
- }
-
+
///
/// Emits the least significant count of bits of bits to the bit-stream.
/// The precondition is bits < 1<<nBits && nBits <= 16.
@@ -444,6 +428,7 @@ namespace ImageSharp.Formats
///
/// The index of the Huffman encoder
/// The value to encode.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
private void EmitHuff(HuffIndex index, int value)
{
uint x = TheHuffmanLut[(int)index].Values[value];
@@ -456,6 +441,7 @@ namespace ImageSharp.Formats
/// The index of the Huffman encoder
/// The number of copies to encode.
/// The value to encode.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
private void EmitHuffRLE(HuffIndex index, int runLength, int value)
{
int a = value;
@@ -469,11 +455,11 @@ namespace ImageSharp.Formats
uint bt;
if (a < 0x100)
{
- bt = this.bitCountLut[a];
+ bt = BitCountLut[a];
}
else
{
- bt = 8 + (uint)this.bitCountLut[a >> 8];
+ bt = 8 + BitCountLut[a >> 8];
}
this.EmitHuff(index, (int)((uint)(runLength << 4) | bt));
@@ -488,25 +474,39 @@ namespace ImageSharp.Formats
/// returning the post-quantized DC value of the DCT-transformed block.
/// The block is in natural (not zig-zag) order.
///
- /// The block to write.
/// The quantization table index.
/// The previous DC value.
+ /// Source block
+ /// Temporal block to be used as FDCT Destination
+ /// Temporal block 2
+ /// Quantization table
+ /// The 8x8 Unzig block ptr
/// The
- private int WriteBlock(ref Block block, QuantIndex index, int prevDC)
+ private float WriteBlock(QuantIndex index, float prevDC, Block8x8F* src, Block8x8F* tempDest, Block8x8F* temp2, Block8x8F* quant, int* unzigPtr)
{
- FDCT.Transform(ref block);
+ 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.
- int dc = Round(block[0], 8 * this.quant[(int)index][0]);
- this.EmitHuffRLE((HuffIndex)((2 * (int)index) + 0), 0, dc - prevDC);
+ //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++)
{
- int ac = Round(block[Unzig[zig]], 8 * this.quant[(int)index][zig]);
+ //float ac = Round(d[unzigPtr[zig]], q[zig]);
+ float ac = d[zig];
if (ac == 0)
{
@@ -520,7 +520,7 @@ namespace ImageSharp.Formats
runLength -= 16;
}
- this.EmitHuffRLE(h, runLength, ac);
+ this.EmitHuffRLE(h, runLength, (int)ac);
runLength = 0;
}
}
@@ -543,58 +543,103 @@ namespace ImageSharp.Formats
/// The luminance block.
/// The red chroma block.
/// The blue chroma block.
- private void ToYCbCr(PixelAccessor pixels, int x, int y, ref Block yBlock, ref Block cbBlock, ref Block crBlock)
+ private static void ToYCbCr(
+ PixelAccessor pixels,
+ int x,
+ int y,
+ Block8x8F* yBlock,
+ Block8x8F* cbBlock,
+ Block8x8F* crBlock)
where TColor : struct, IPackedPixel, IEquatable
{
+ float* yBlockRaw = (float*)yBlock;
+ float* cbBlockRaw = (float*)cbBlock;
+ float* crBlockRaw = (float*)crBlock;
+
+ PixelAccessor asStandardColorAccessor = pixels as PixelAccessor;
+ if (asStandardColorAccessor != null)
+ {
+ ColorRGBToYCbCr(asStandardColorAccessor, x, y, yBlockRaw, cbBlockRaw, crBlockRaw);
+ return;
+ }
+
+
+ Vector4 maxBytes = new Vector4(255f);
+ Vector4 half = new Vector4(0.5f);
int xmax = pixels.Width - 1;
int ymax = pixels.Height - 1;
- byte[] color = new byte[3];
+
for (int j = 0; j < 8; j++)
{
+ int j8 = j * 8;
for (int i = 0; i < 8; i++)
{
- pixels[Math.Min(x + i, xmax), Math.Min(y + j, ymax)].ToBytes(color, 0, ComponentOrder.XYZ);
+ 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)));
- byte r = color[0];
- byte g = color[1];
- byte b = color[2];
+ int index = j8 + i;
- // Convert returned bytes into the YCbCr color space. Assume RGBA
- byte yy = (byte)((0.299F * r) + (0.587F * g) + (0.114F * b));
- byte cb = (byte)(128 + ((-0.168736F * r) - (0.331264F * g) + (0.5F * b)));
- byte cr = (byte)(128 + ((0.5F * r) - (0.418688F * g) - (0.081312F * b)));
-
- int index = (8 * j) + i;
- yBlock[index] = yy;
- cbBlock[index] = cb;
- crBlock[index] = cr;
+ yBlockRaw[index] = yy;
+ cbBlockRaw[index] = cb;
+ crBlockRaw[index] = cr;
}
}
}
- ///
- /// Scales the 16x16 region represented by the 4 source blocks to the 8x8
- /// DST block.
- ///
- /// The destination block array
- /// The source block array.
- private void Scale16X16To8X8(ref Block destination, Block[] source)
+
+
+ private static void ColorRGBToYCbCr(
+ PixelAccessor pixels,
+ int x,
+ int y,
+ float* yBlockRaw,
+ float* cbBlockRaw,
+ float* crBlockRaw)
{
- for (int i = 0; i < 4; i++)
+ 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 dstOff = ((i & 2) << 4) | ((i & 1) << 2);
- for (int y = 0; y < 4; y++)
+ int yPos = Math.Min(y + j, ymax);
+
+ int j8 = j * 8;
+ for (int i = 0; i < 8; i++)
{
- for (int x = 0; x < 4; x++)
- {
- int j = (16 * y) + (2 * x);
- int sum = source[i][j] + source[i][j + 1] + source[i][j + 8] + source[i][j + 9];
- destination[(8 * y) + x + dstOff] = (sum + 2) / 4;
- }
+ 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;
}
}
+
+ //(((y * pixels.Width) + x) * colorSize);
+
+
}
+
+
///
/// Writes the application header containing the JFIF identifier plus extra data.
///
@@ -641,7 +686,7 @@ namespace ImageSharp.Formats
/// The image.
/// The pixel format.
private void WriteProfiles(Image image)
- where TColor : struct, IPackedPixel, IEquatable
+ where TColor : struct, IPackedPixel, IEquatable
{
this.WriteProfile(image.ExifProfile);
}
@@ -691,19 +736,22 @@ namespace ImageSharp.Formats
// This allows us to reduce the number of writes to the stream.
byte[] dqt = new byte[(QuantizationTableCount * Block.BlockSize) + QuantizationTableCount];
int offset = 0;
- for (int i = 0; i < QuantizationTableCount; i++)
- {
- dqt[offset++] = (byte)i;
- int len = this.quant[i].Length;
- for (int j = 0; j < len; j++)
- {
- dqt[offset++] = this.quant[i][j];
- }
- }
+ 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);
}
+ private static void WriteDataToDqt(byte[] dqt, ref int offset, QuantIndex i, ref Block8x8F q)
+ {
+ dqt[offset++] = (byte)i;
+ for (int j = 0; j < Block8x8F.ScalarCount; j++)
+ {
+ dqt[offset++] = (byte)q[j];
+ }
+ }
+
///
/// Writes the Start Of Frame (Baseline) marker
///
@@ -842,27 +890,37 @@ namespace ImageSharp.Formats
private void Encode444(PixelAccessor pixels)
where TColor : struct, IPackedPixel, IEquatable
{
- Block b = Block.Create();
- Block cb = Block.Create();
- Block cr = Block.Create();
+ Block8x8F b = new Block8x8F();
+ Block8x8F cb = new Block8x8F();
+ Block8x8F cr = new Block8x8F();
+
+ Block8x8F temp1 = new Block8x8F();
+ Block8x8F temp2 = new Block8x8F();
+
+ Block8x8F onStackLuminanceQuantTable = this.luminanceQuantTable;
+ Block8x8F onStackChrominanceQuantTable = this.chrominanceQuantTable;
+
+ UnzigData unzig = UnzigData.Create();
// ReSharper disable once InconsistentNaming
- int prevDCY = 0, prevDCCb = 0, prevDCCr = 0;
+ float prevDCY = 0, prevDCCb = 0, prevDCCr = 0;
for (int y = 0; y < pixels.Height; y += 8)
{
for (int x = 0; x < pixels.Width; x += 8)
{
- this.ToYCbCr(pixels, x, y, ref b, ref cb, ref cr);
- prevDCY = this.WriteBlock(ref b, QuantIndex.Luminance, prevDCY);
- prevDCCb = this.WriteBlock(ref cb, QuantIndex.Chrominance, prevDCCb);
- prevDCCr = this.WriteBlock(ref cr, QuantIndex.Chrominance, prevDCCr);
+ 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);
}
}
+ }
- b.Dispose();
- cb.Dispose();
- cr.Dispose();
+ struct BlockQuad
+ {
+ public fixed float Data[4*Block8x8F.ScalarCount];
}
///
@@ -874,12 +932,25 @@ namespace ImageSharp.Formats
private void Encode420(PixelAccessor pixels)
where TColor : struct, IPackedPixel, IEquatable
{
- Block b = Block.Create();
- Block[] cb = Block.CreateArray(4);
- Block[] cr = Block.CreateArray(4);
+ Block8x8F b = new Block8x8F();
+
+
+ BlockQuad cb = new BlockQuad();
+ BlockQuad cr = new BlockQuad();
+ Block8x8F* cbPtr = (Block8x8F*)cb.Data;
+ Block8x8F* crPtr = (Block8x8F*)cr.Data;
+
+
+ Block8x8F temp1 = new Block8x8F();
+ Block8x8F temp2 = new Block8x8F();
+
+ Block8x8F onStackLuminanceQuantTable = this.luminanceQuantTable;
+ Block8x8F onStackChrominanceQuantTable = this.chrominanceQuantTable;
+
+ UnzigData unzig = UnzigData.Create();
// ReSharper disable once InconsistentNaming
- int prevDCY = 0, prevDCCb = 0, prevDCCr = 0;
+ float prevDCY = 0, prevDCCb = 0, prevDCCr = 0;
for (int y = 0; y < pixels.Height; y += 16)
{
@@ -890,20 +961,38 @@ namespace ImageSharp.Formats
int xOff = (i & 1) * 8;
int yOff = (i & 2) * 4;
- this.ToYCbCr(pixels, x + xOff, y + yOff, ref b, ref cb[i], ref cr[i]);
- prevDCY = this.WriteBlock(ref b, QuantIndex.Luminance, prevDCY);
+ ToYCbCr(pixels, x + xOff, y + yOff, &b, cbPtr + i, crPtr + i);
+
+ prevDCY = this.WriteBlock(
+ QuantIndex.Luminance,
+ prevDCY,
+ &b,
+ &temp1,
+ &temp2,
+ &onStackLuminanceQuantTable,
+ unzig.Data);
}
- this.Scale16X16To8X8(ref b, cb);
- prevDCCb = this.WriteBlock(ref b, QuantIndex.Chrominance, prevDCCb);
- this.Scale16X16To8X8(ref b, cr);
- prevDCCr = this.WriteBlock(ref b, QuantIndex.Chrominance, prevDCCr);
+ Block8x8F.Scale16X16To8X8(&b, cbPtr);
+ prevDCCb = this.WriteBlock(
+ QuantIndex.Chrominance,
+ prevDCCb,
+ &b,
+ &temp1,
+ &temp2,
+ &onStackChrominanceQuantTable,
+ unzig.Data);
+ Block8x8F.Scale16X16To8X8(&b, crPtr);
+ prevDCCr = this.WriteBlock(
+ QuantIndex.Chrominance,
+ prevDCCr,
+ &b,
+ &temp1,
+ &temp2,
+ &onStackChrominanceQuantTable,
+ unzig.Data);
}
}
-
- b.Dispose();
- Block.DisposeAll(cb);
- Block.DisposeAll(cr);
}
///
@@ -956,6 +1045,11 @@ namespace ImageSharp.Formats
///
private class HuffmanLut
{
+ ///
+ /// The collection of huffman values.
+ ///
+ public uint[] Values { get; }
+
///
/// Initializes a new instance of the class.
///
@@ -990,11 +1084,6 @@ namespace ImageSharp.Formats
code <<= 1;
}
}
-
- ///
- /// Gets the collection of huffman values.
- ///
- public uint[] Values { get; }
}
}
}
diff --git a/src/ImageSharp/Formats/Jpg/JpegUtils.cs b/src/ImageSharp/Formats/Jpg/JpegUtils.cs
new file mode 100644
index 0000000000..564ab8655f
--- /dev/null
+++ b/src/ImageSharp/Formats/Jpg/JpegUtils.cs
@@ -0,0 +1,53 @@
+namespace ImageSharp.Formats
+{
+ using System;
+ using System.Runtime.CompilerServices;
+
+ internal static unsafe class JpegUtils
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static void CopyRgb(byte* source, byte* dest)
+ {
+ *dest++ = *source++; // R
+ *dest++ = *source++; // G
+ *dest = *source; // B
+ }
+
+ internal static unsafe void RepeatPixelsBottomRight(PixelArea area, int fromX, int fromY)
+ where TColor : struct, IPackedPixel, IEquatable
+ {
+ if (fromX <= 0 || fromY <= 0 || fromX >= area.Width || fromY >= area.Height)
+ {
+ throw new InvalidOperationException();
+ }
+
+ for (int y = 0; y < fromY; y++)
+ {
+ byte* ptrBase = (byte*)area.DataPointer + y * area.RowByteCount;
+
+ for (int x = fromX; x < area.Width; x++)
+ {
+ byte* prevPtr = ptrBase + (x - 1) * 3;
+ byte* currPtr = ptrBase + x * 3;
+
+ CopyRgb(prevPtr, currPtr);
+ }
+ }
+
+ for (int y = fromY; y < area.Height; y++)
+ {
+ byte* currBase = (byte*)area.DataPointer + y * area.RowByteCount;
+ byte* prevBase = (byte*)area.DataPointer + (y - 1) * area.RowByteCount;
+
+ for (int x = 0; x < area.Width; x++)
+ {
+ int x3 = 3 * x;
+ byte* currPtr = currBase + x3;
+ byte* prevPtr = prevBase + x3;
+
+ CopyRgb(prevPtr, currPtr);
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Jpg/UnzigData.cs b/src/ImageSharp/Formats/Jpg/UnzigData.cs
new file mode 100644
index 0000000000..6dae6d9421
--- /dev/null
+++ b/src/ImageSharp/Formats/Jpg/UnzigData.cs
@@ -0,0 +1,38 @@
+namespace ImageSharp.Formats
+{
+ using System;
+ using System.Runtime.InteropServices;
+
+ ///
+ /// Holds the Jpeg UnZig array in a value/stack type.
+ /// Unzig maps from the zigzag ordering to the natural ordering. For example,
+ /// unzig[3] is the column and row of the fourth element in zigzag order. The
+ /// value is 16, which means first column (16%8 == 0) and third row (16/8 == 2).
+ ///
+ 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;
+ }
+
+ ///
+ /// Unzig maps from the zigzag ordering to the natural ordering. For example,
+ /// unzig[3] is the column and row of the fourth element in zigzag order. The
+ /// value is 16, which means first column (16%8 == 0) and third row (16/8 == 2).
+ ///
+ private static readonly int[] Unzig =
+ {
+ 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33,
+ 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50,
+ 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59, 52, 45, 38, 31, 39, 46,
+ 53, 60, 61, 54, 47, 55, 62, 63,
+ };
+
+ }
+}
\ No newline at end of file
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs
index 8939d2c207..7d66f6347a 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs
@@ -3,7 +3,7 @@
// ReSharper disable InconsistentNaming
-namespace ImageSharp.Tests.Formats.Jpg
+namespace ImageSharp.Tests
{
using System.Diagnostics;
using System.Numerics;
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementations.cs b/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementations.cs
index 006e959633..860c1b55ed 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementations.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementations.cs
@@ -1,6 +1,6 @@
// ReSharper disable InconsistentNaming
-namespace ImageSharp.Tests.Formats.Jpg
+namespace ImageSharp.Tests
{
using System;
using System.Numerics;
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/UtilityTestClassBase.cs b/tests/ImageSharp.Tests/Formats/Jpg/UtilityTestClassBase.cs
index 20a9d34b18..6191d221cc 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/UtilityTestClassBase.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/UtilityTestClassBase.cs
@@ -3,7 +3,7 @@ using ImageSharp.Formats;
using Xunit.Abstractions;
// ReSharper disable InconsistentNaming
-namespace ImageSharp.Tests.Formats.Jpg
+namespace ImageSharp.Tests
{
using System;
using System.Collections.Generic;
diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs
index 8a8db3ee89..be5559e2a6 100644
--- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs
+++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs
@@ -80,7 +80,7 @@ namespace ImageSharp.Tests
this.Utility = new ImagingTestCaseUtility()
{
SourceFileOrDescription = this.SourceFileOrDescription,
- PixelTypeName = typeof(TColor).Name
+ PixelTypeName = this.PixelType.ToString()
};
if (testMethod != null)
diff --git a/tests/ImageSharp.Tests46/Formats/Jpg/ReferenceImplementationsTests.cs b/tests/ImageSharp.Tests46/Formats/Jpg/ReferenceImplementationsTests.cs
index 265d6b5f23..e30baddfc8 100644
--- a/tests/ImageSharp.Tests46/Formats/Jpg/ReferenceImplementationsTests.cs
+++ b/tests/ImageSharp.Tests46/Formats/Jpg/ReferenceImplementationsTests.cs
@@ -1,8 +1,9 @@
// ReSharper disable InconsistentNaming
-namespace ImageSharp.Tests.Formats.Jpg
+namespace ImageSharp.Tests
{
using System.Numerics;
using ImageSharp.Formats;
+
using Xunit;
using Xunit.Abstractions;