diff --git a/src/ImageProcessorCore - Copy/Formats/Jpg/Block.cs b/src/ImageProcessorCore - Copy/Formats/Jpg/Block.cs
index 655471a07..35aa10f18 100644
--- a/src/ImageProcessorCore - Copy/Formats/Jpg/Block.cs
+++ b/src/ImageProcessorCore - Copy/Formats/Jpg/Block.cs
@@ -1,19 +1,44 @@
-namespace ImageProcessorCore.Formats
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageProcessorCore.Formats
{
+ ///
+ /// Represents an 8x8 block of coefficients to transform and encode.
+ ///
internal class Block
{
- public const int blockSize = 64;
- private int[] _data;
+ ///
+ /// Gets the size of the block.
+ ///
+ public const int BlockSize = 64;
+
+ ///
+ /// The array of block data.
+ ///
+ private readonly int[] data;
+ ///
+ /// Initializes a new instance of the class.
+ ///
public Block()
{
- _data = new int[blockSize];
+ this.data = new int[BlockSize];
}
- public int this[int idx]
+ ///
+ /// Gets the pixel data at the given block index.
+ ///
+ /// The index of the data to return.
+ ///
+ /// The .
+ ///
+ public int this[int index]
{
- get { return _data[idx]; }
- set { _data[idx] = value; }
+ get { return this.data[index]; }
+ set { this.data[index] = value; }
}
}
}
diff --git a/src/ImageProcessorCore - Copy/Formats/Jpg/FDCT.cs b/src/ImageProcessorCore - Copy/Formats/Jpg/FDCT.cs
index a6073f9c7..e51ea6415 100644
--- a/src/ImageProcessorCore - Copy/Formats/Jpg/FDCT.cs
+++ b/src/ImageProcessorCore - Copy/Formats/Jpg/FDCT.cs
@@ -5,9 +5,14 @@
namespace ImageProcessorCore.Formats
{
+ ///
+ /// Performs a fast, forward descrete cosine transform against the given block
+ /// decomposing it into 64 orthogonal basis signals.
+ ///
internal class FDCT
{
// Trigonometric constants in 13-bit fixed point format.
+ // TODO: Rename and describe these.
private const int fix_0_298631336 = 2446;
private const int fix_0_390180644 = 3196;
private const int fix_0_541196100 = 4433;
@@ -20,27 +25,42 @@ namespace ImageProcessorCore.Formats
private const int fix_2_053119869 = 16819;
private const int fix_2_562915447 = 20995;
private const int fix_3_072711026 = 25172;
- private const int constBits = 13;
- private const int pass1Bits = 2;
- private const int centerJSample = 128;
- // fdct performs a forward DCT on an 8x8 block of coefficients, including a
- // level shift.
- public static void Transform(Block b)
+ ///
+ /// The number of bits
+ ///
+ private const int Bits = 13;
+
+ ///
+ /// The number of bits to shift by on the first pass.
+ ///
+ private const int Pass1Bits = 2;
+
+ ///
+ /// The value to shift by
+ ///
+ private const int CenterJSample = 128;
+
+ ///
+ /// Performs a forward DCT on an 8x8 block of coefficients, including a
+ /// level shift.
+ ///
+ /// The block.
+ public static void Transform(Block block)
{
// Pass 1: process rows.
for (int y = 0; y < 8; y++)
{
int y8 = y * 8;
- int x0 = b[y8 + 0];
- int x1 = b[y8 + 1];
- int x2 = b[y8 + 2];
- int x3 = b[y8 + 3];
- int x4 = b[y8 + 4];
- int x5 = b[y8 + 5];
- int x6 = b[y8 + 6];
- int x7 = b[y8 + 7];
+ int x0 = block[y8];
+ int x1 = block[y8 + 1];
+ int x2 = block[y8 + 2];
+ int x3 = block[y8 + 3];
+ int x4 = block[y8 + 4];
+ int x5 = block[y8 + 5];
+ int x6 = block[y8 + 6];
+ int x7 = block[y8 + 7];
int tmp0 = x0 + x7;
int tmp1 = x1 + x6;
@@ -57,19 +77,19 @@ namespace ImageProcessorCore.Formats
tmp2 = x2 - x5;
tmp3 = x3 - x4;
- b[y8] = (tmp10 + tmp11 - 8 * centerJSample) << pass1Bits;
- b[y8 + 4] = (tmp10 - tmp11) << pass1Bits;
+ block[y8] = (tmp10 + tmp11 - (8 * CenterJSample)) << Pass1Bits;
+ block[y8 + 4] = (tmp10 - tmp11) << Pass1Bits;
int z1 = (tmp12 + tmp13) * fix_0_541196100;
- z1 += 1 << (constBits - pass1Bits - 1);
- b[y8 + 2] = (z1 + tmp12 * fix_0_765366865) >> (constBits - pass1Bits);
- b[y8 + 6] = (z1 - tmp13 * fix_1_847759065) >> (constBits - pass1Bits);
+ z1 += 1 << (Bits - Pass1Bits - 1);
+ block[y8 + 2] = (z1 + (tmp12 * fix_0_765366865)) >> (Bits - Pass1Bits);
+ block[y8 + 6] = (z1 - (tmp13 * fix_1_847759065)) >> (Bits - Pass1Bits);
tmp10 = tmp0 + tmp3;
tmp11 = tmp1 + tmp2;
tmp12 = tmp0 + tmp2;
tmp13 = tmp1 + tmp3;
z1 = (tmp12 + tmp13) * fix_1_175875602;
- z1 += 1 << (constBits - pass1Bits - 1);
+ z1 += 1 << (Bits - Pass1Bits - 1);
tmp0 = tmp0 * fix_1_501321110;
tmp1 = tmp1 * fix_3_072711026;
tmp2 = tmp2 * fix_2_053119869;
@@ -81,45 +101,45 @@ namespace ImageProcessorCore.Formats
tmp12 += z1;
tmp13 += z1;
- b[y8 + 1] = (tmp0 + tmp10 + tmp12) >> (constBits - pass1Bits);
- b[y8 + 3] = (tmp1 + tmp11 + tmp13) >> (constBits - pass1Bits);
- b[y8 + 5] = (tmp2 + tmp11 + tmp12) >> (constBits - pass1Bits);
- b[y8 + 7] = (tmp3 + tmp10 + tmp13) >> (constBits - pass1Bits);
+ block[y8 + 1] = (tmp0 + tmp10 + tmp12) >> (Bits - Pass1Bits);
+ block[y8 + 3] = (tmp1 + tmp11 + tmp13) >> (Bits - Pass1Bits);
+ block[y8 + 5] = (tmp2 + tmp11 + tmp12) >> (Bits - Pass1Bits);
+ block[y8 + 7] = (tmp3 + tmp10 + tmp13) >> (Bits - Pass1Bits);
}
// Pass 2: process columns.
// We remove pass1Bits scaling, but leave results scaled up by an overall factor of 8.
for (int x = 0; x < 8; x++)
{
- int tmp0 = b[x] + b[56 + x];
- int tmp1 = b[8 + x] + b[48 + x];
- int tmp2 = b[16 + x] + b[40 + x];
- int tmp3 = b[24 + x] + b[32 + x];
+ int tmp0 = block[x] + block[56 + x];
+ int tmp1 = block[8 + x] + block[48 + x];
+ int tmp2 = block[16 + x] + block[40 + x];
+ int tmp3 = block[24 + x] + block[32 + x];
- int tmp10 = tmp0 + tmp3 + (1 << (pass1Bits - 1));
+ int tmp10 = tmp0 + tmp3 + (1 << (Pass1Bits - 1));
int tmp12 = tmp0 - tmp3;
int tmp11 = tmp1 + tmp2;
int tmp13 = tmp1 - tmp2;
- tmp0 = b[x] - b[56 + x];
- tmp1 = b[8 + x] - b[48 + x];
- tmp2 = b[16 + x] - b[40 + x];
- tmp3 = b[24 + x] - b[32 + x];
+ tmp0 = block[x] - block[56 + x];
+ tmp1 = block[8 + x] - block[48 + x];
+ tmp2 = block[16 + x] - block[40 + x];
+ tmp3 = block[24 + x] - block[32 + x];
- b[x] = (tmp10 + tmp11) >> pass1Bits;
- b[32 + x] = (tmp10 - tmp11) >> pass1Bits;
+ block[x] = (tmp10 + tmp11) >> Pass1Bits;
+ block[32 + x] = (tmp10 - tmp11) >> Pass1Bits;
int z1 = (tmp12 + tmp13) * fix_0_541196100;
- z1 += 1 << (constBits + pass1Bits - 1);
- b[16 + x] = (z1 + tmp12 * fix_0_765366865) >> (constBits + pass1Bits);
- b[48 + x] = (z1 - tmp13 * fix_1_847759065) >> (constBits + pass1Bits);
+ z1 += 1 << (Bits + Pass1Bits - 1);
+ block[16 + x] = (z1 + (tmp12 * fix_0_765366865)) >> (Bits + Pass1Bits);
+ block[48 + x] = (z1 - (tmp13 * fix_1_847759065)) >> (Bits + Pass1Bits);
tmp10 = tmp0 + tmp3;
tmp11 = tmp1 + tmp2;
tmp12 = tmp0 + tmp2;
tmp13 = tmp1 + tmp3;
z1 = (tmp12 + tmp13) * fix_1_175875602;
- z1 += 1 << (constBits + pass1Bits - 1);
+ z1 += 1 << (Bits + Pass1Bits - 1);
tmp0 = tmp0 * fix_1_501321110;
tmp1 = tmp1 * fix_3_072711026;
tmp2 = tmp2 * fix_2_053119869;
@@ -131,10 +151,10 @@ namespace ImageProcessorCore.Formats
tmp12 += z1;
tmp13 += z1;
- b[8 + x] = (tmp0 + tmp10 + tmp12) >> (constBits + pass1Bits);
- b[24 + x] = (tmp1 + tmp11 + tmp13) >> (constBits + pass1Bits);
- b[40 + x] = (tmp2 + tmp11 + tmp12) >> (constBits + pass1Bits);
- b[56 + x] = (tmp3 + tmp10 + tmp13) >> (constBits + pass1Bits);
+ block[8 + x] = (tmp0 + tmp10 + tmp12) >> (Bits + Pass1Bits);
+ block[24 + x] = (tmp1 + tmp11 + tmp13) >> (Bits + Pass1Bits);
+ block[40 + x] = (tmp2 + tmp11 + tmp12) >> (Bits + Pass1Bits);
+ block[56 + x] = (tmp3 + tmp10 + tmp13) >> (Bits + Pass1Bits);
}
}
}
diff --git a/src/ImageProcessorCore - Copy/Formats/Jpg/JpegDecoder.cs b/src/ImageProcessorCore - Copy/Formats/Jpg/JpegDecoder.cs
index 34e0932dc..89aa4e001 100644
--- a/src/ImageProcessorCore - Copy/Formats/Jpg/JpegDecoder.cs
+++ b/src/ImageProcessorCore - Copy/Formats/Jpg/JpegDecoder.cs
@@ -7,7 +7,6 @@ namespace ImageProcessorCore.Formats
{
using System;
using System.IO;
- using System.Threading.Tasks;
///
/// Image decoder for generating an image out of a jpg stream.
@@ -96,39 +95,6 @@ namespace ImageProcessorCore.Formats
JpegDecoderCore decoder = new JpegDecoderCore();
decoder.Decode(stream, image, false);
-
- // TODO: When nComp is 3 we set the ImageBase pixels internally, Eventually we should
- // do the same here
- if (decoder.nComp == 1)
- {
- int pixelWidth = decoder.width;
- int pixelHeight = decoder.height;
-
- float[] pixels = new float[pixelWidth * pixelHeight * 4];
-
- Parallel.For(
- 0,
- pixelHeight,
- y =>
- {
- var yoff = decoder.img1.get_row_offset(y);
- for (int x = 0; x < pixelWidth; x++)
- {
- int offset = ((y * pixelWidth) + x) * 4;
-
- pixels[offset + 0] = decoder.img1.pixels[yoff + x] / 255f;
- pixels[offset + 1] = decoder.img1.pixels[yoff + x] / 255f;
- pixels[offset + 2] = decoder.img1.pixels[yoff + x] / 255f;
- pixels[offset + 3] = 1;
- }
- });
-
- image.SetPixels(pixelWidth, pixelHeight, pixels);
- }
- else if (decoder.nComp != 3)
- {
- throw new NotSupportedException("JpegDecoder only supports RGB and Grayscale color spaces.");
- }
}
///
diff --git a/src/ImageProcessorCore - Copy/Formats/Jpg/JpegDecoderCore.cs.REMOVED.git-id b/src/ImageProcessorCore - Copy/Formats/Jpg/JpegDecoderCore.cs.REMOVED.git-id
index 6cf3cee55..a5c139745 100644
--- a/src/ImageProcessorCore - Copy/Formats/Jpg/JpegDecoderCore.cs.REMOVED.git-id
+++ b/src/ImageProcessorCore - Copy/Formats/Jpg/JpegDecoderCore.cs.REMOVED.git-id
@@ -1 +1 @@
-07d53b1f9ee1e867cfd8bb664c2cf3f31c0e8289
\ No newline at end of file
+7a5076971068e0f389da2fb1e8b25216f4049718
\ No newline at end of file
diff --git a/src/ImageProcessorCore - Copy/Formats/Jpg/JpegEncoderCore.cs b/src/ImageProcessorCore - Copy/Formats/Jpg/JpegEncoderCore.cs
index a1f49a0c8..384f18b36 100644
--- a/src/ImageProcessorCore - Copy/Formats/Jpg/JpegEncoderCore.cs
+++ b/src/ImageProcessorCore - Copy/Formats/Jpg/JpegEncoderCore.cs
@@ -2,7 +2,6 @@
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
//
-
namespace ImageProcessorCore.Formats
{
using System;
@@ -10,208 +9,171 @@ namespace ImageProcessorCore.Formats
internal class JpegEncoderCore
{
- private const int sof0Marker = 0xc0; // Start Of Frame (Baseline).
- private const int sof1Marker = 0xc1; // Start Of Frame (Extended Sequential).
- private const int sof2Marker = 0xc2; // Start Of Frame (Progressive).
- private const int dhtMarker = 0xc4; // Define Huffman Table.
- private const int rst0Marker = 0xd0; // ReSTart (0).
- private const int rst7Marker = 0xd7; // ReSTart (7).
- private const int soiMarker = 0xd8; // Start Of Image.
- private const int eoiMarker = 0xd9; // End Of Image.
- private const int sosMarker = 0xda; // Start Of Scan.
- private const int dqtMarker = 0xdb; // Define Quantization Table.
- private const int driMarker = 0xdd; // Define Restart Interval.
- private const int comMarker = 0xfe; // COMment.
- // "APPlication specific" markers aren't part of the JPEG spec per se,
- // but in practice, their use is described at
- // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html
- private const int app0Marker = 0xe0;
- private const int app14Marker = 0xee;
- private const int app15Marker = 0xef;
-
- // bitCount counts the number of bits needed to hold an integer.
- private readonly byte[] bitCount = {
- 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, };
-
- // unzig 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 = new int[] {
- 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,
+ ///
+ /// 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,
};
- private const int nQuantIndex = 2;
- private const int nHuffIndex = 4;
-
- private enum quantIndex
- {
- quantIndexLuminance = 0,
- quantIndexChrominance = 1,
- }
+ private const int NQuantIndex = 2;
- private enum huffIndex
+ ///
+ /// Counts the number of bits needed to hold an integer.
+ ///
+ private readonly byte[] bitCount =
{
- huffIndexLuminanceDC = 0,
- huffIndexLuminanceAC = 1,
- huffIndexChrominanceDC = 2,
- huffIndexChrominanceAC = 3,
- }
+ 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,
+ };
- // unscaledQuant are the unscaled quantization tables in zig-zag order. Each
- // encoder copies and scales the tables according to its quality parameter.
- // The values are derived from section K.1 after converting from natural to
- // zig-zag order.
- private byte[,] unscaledQuant = new byte[,] {
- // Luminance.
+ ///
+ /// The unscaled quantization tables in zig-zag order. Each
+ /// encoder copies and scales the tables according to its quality parameter.
+ /// The values are derived from section K.1 after converting from natural to
+ /// zig-zag order.
+ ///
+ private readonly byte[,] unscaledQuant = {
{
- 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,
- },
+ // 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,
+ }
};
- private class huffmanSpec
- {
- public huffmanSpec(byte[] c, byte[] v) { count = c; values = v; }
- public byte[] count;
- public byte[] values;
- }
-
- // theHuffmanSpec is the Huffman encoding specifications.
- // This encoder uses the same Huffman encoding for all images.
- private huffmanSpec[] theHuffmanSpec = new huffmanSpec[] {
+ ///
+ /// The Huffman encoding specifications.
+ /// This encoder uses the same Huffman encoding for all images.
+ ///
+ private readonly HuffmanSpec[] theHuffmanSpec = {
// Luminance DC.
- new huffmanSpec(
- new byte[] { 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 huffmanSpec(
- new byte[] { 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 125 },
+ new HuffmanSpec(
new byte[]
- {
- 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12,
- 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
- 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,
- 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
- 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16,
- 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
- 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
- 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
- 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
- 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
- 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
- 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
- 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
- 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
- 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
- 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5,
- 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4,
- 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
- 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
- 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
- 0xf9, 0xfa}),
- new huffmanSpec(
- new byte[] { 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 },
+ {
+ 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 HuffmanSpec(
+ new byte[]
+ {
+ 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 125
+ },
+ new byte[]
+ {
+ 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21,
+ 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71,
+ 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1,
+ 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72,
+ 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25,
+ 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
+ 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
+ 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a,
+ 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x83,
+ 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93,
+ 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3,
+ 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3,
+ 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3,
+ 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3,
+ 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
+ 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1,
+ 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa
+ }),
+ new HuffmanSpec(
+ new byte[]
+ {
+ 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0
+ },
+ new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }),
// Chrominance AC.
- new huffmanSpec(
- new byte[] { 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 119 },
+ new HuffmanSpec(
new byte[]
- {
- 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21,
- 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
- 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
- 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0,
- 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34,
- 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26,
- 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38,
- 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
- 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
- 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
- 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
- 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
- 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96,
- 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5,
- 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4,
- 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3,
- 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2,
- 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
- 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
- 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
- 0xf9, 0xfa,
+ {
+ 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 119
+ },
+ new byte[]
+ {
+ 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31,
+ 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22,
+ 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1, 0xc1,
+ 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1,
+ 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18,
+ 0x19, 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36,
+ 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
+ 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
+ 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a,
+ 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a,
+ 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a,
+ 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa,
+ 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba,
+ 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca,
+ 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
+ 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
+ 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa,
})
};
- // huffmanLUT is a compiled look-up table representation of a huffmanSpec.
- // Each value maps to a uint32 of which the 8 most significant bits hold the
- // codeword size in bits and the 24 least significant bits hold the codeword.
- // The maximum codeword size is 16 bits.
- private class huffmanLUT
+ ///
+ /// A compiled look-up table representation of a huffmanSpec.
+ /// Each value maps to a uint32 of which the 8 most significant bits hold the
+ /// codeword size in bits and the 24 least significant bits hold the codeword.
+ /// The maximum codeword size is 16 bits.
+ ///
+ private class HuffmanLut
{
- public uint[] values;
+ public readonly uint[] Values;
- public huffmanLUT(huffmanSpec s)
+ public HuffmanLut(HuffmanSpec s)
{
int maxValue = 0;
- foreach (var v in s.values)
+ foreach (var v in s.Values)
{
- if (v > maxValue)
- maxValue = v;
+ if (v > maxValue) maxValue = v;
}
- values = new uint[maxValue + 1];
+ this.Values = new uint[maxValue + 1];
int code = 0;
int k = 0;
- for (int i = 0; i < s.count.Length; i++)
+ for (int i = 0; i < s.Count.Length; i++)
{
int nBits = (i + 1) << 24;
- for (int j = 0; j < s.count[i]; j++)
+ for (int j = 0; j < s.Count[i]; j++)
{
- values[s.values[k]] = (uint)(nBits | code);
+ this.Values[s.Values[k]] = (uint)(nBits | code);
code++;
k++;
}
+
code <<= 1;
}
}
@@ -220,26 +182,53 @@ namespace ImageProcessorCore.Formats
// w is the writer to write to. err is the first error encountered during
// writing. All attempted writes after the first error become no-ops.
private Stream outputStream;
- // buf is a scratch buffer.
- private byte[] buf = new byte[16];
- // bits and nBits are accumulated bits to write to w.
- private uint bits, nBits;
- // quant is the scaled quantization tables, in zig-zag order.
- private byte[][] quant = new byte[nQuantIndex][];//[Block.blockSize];
- // theHuffmanLUT are compiled representations of theHuffmanSpec.
- private huffmanLUT[] theHuffmanLUT = new huffmanLUT[4];
+
+ ///
+ /// A scratch buffer to reduce allocations.
+ ///
+ private readonly byte[] buffer = new byte[16];
+
+ ///
+ /// The accumulated bits to write to the stream.
+ ///
+ private uint bits;
+
+ ///
+ /// The accumulated bits to write to the stream.
+ ///
+ private uint nBits;
+
+ ///
+ /// The scaled quantization tables, in zig-zag order.
+ ///
+ private readonly byte[][] quant = new byte[NQuantIndex][]; // [Block.blockSize];
+
+ // The compiled representations of theHuffmanSpec.
+ private readonly HuffmanLut[] theHuffmanLUT = new HuffmanLut[4];
+
+ ///
+ /// The subsampling method to use.
+ ///
private JpegSubsample subsample;
- private void writeByte(byte b)
+ ///
+ /// Writes the given byte to the stream.
+ ///
+ ///
+ private void WriteByte(byte b)
{
var data = new byte[1];
data[0] = b;
- outputStream.Write(data, 0, 1);
+ this.outputStream.Write(data, 0, 1);
}
- // emit emits the least significant nBits bits of bits to the bit-stream.
- // The precondition is bits < 1<
+ /// Emits the least significant nBits bits of bits to the bit-stream.
+ /// The precondition is bits < 1<<nBits && nBits <= 16.
+ ///
+ ///
+ ///
+ private void Emit(uint bits, uint nBits)
{
nBits += this.nBits;
bits <<= (int)(32 - nBits);
@@ -247,158 +236,85 @@ namespace ImageProcessorCore.Formats
while (nBits >= 8)
{
byte b = (byte)(bits >> 24);
- writeByte(b);
- if (b == 0xff)
- writeByte(0x00);
+ this.WriteByte(b);
+ if (b == 0xff) this.WriteByte(0x00);
bits <<= 8;
nBits -= 8;
}
+
this.bits = bits;
this.nBits = nBits;
}
- // emitHuff emits the given value with the given Huffman encoder.
- private void emitHuff(huffIndex h, int v)
+ ///
+ /// Emits the given value with the given Huffman encoder.
+ ///
+ /// The index of the Huffman encoder
+ /// The value to encode.
+ private void EmitHuff(HuffIndex index, int value)
{
- uint x = theHuffmanLUT[(int)h].values[v];
- emit(x & ((1 << 24) - 1), x >> 24);
+ uint x = this.theHuffmanLUT[(int)index].Values[value];
+ this.Emit(x & ((1 << 24) - 1), x >> 24);
}
- // emitHuffRLE emits a run of runLength copies of value encoded with the given
- // Huffman encoder.
- private void emitHuffRLE(huffIndex h, int runLength, int v)
+ ///
+ /// Emits a run of runLength copies of value encoded with the given Huffman encoder.
+ ///
+ /// The index of the Huffman encoder
+ /// The number of copies to encode.
+ /// The value to encode.
+ private void EmitHuffRLE(HuffIndex index, int runLength, int value)
{
- int a = v;
- int b = v;
+ int a = value;
+ int b = value;
if (a < 0)
{
- a = -v;
- b = v - 1;
- }
- uint nBits = 0;
- if (a < 0x100)
- nBits = bitCount[a];
- else
- nBits = 8 + (uint)bitCount[a >> 8];
-
- emitHuff(h, (int)((uint)(runLength << 4) | nBits));
- if (nBits > 0) emit((uint)b & (uint)((1 << ((int)nBits)) - 1), nBits);
- }
-
- // writeMarkerHeader writes the header for a marker with the given length.
- private void writeMarkerHeader(byte marker, int markerlen)
- {
- buf[0] = 0xff;
- buf[1] = marker;
- buf[2] = (byte)(markerlen >> 8);
- buf[3] = (byte)(markerlen & 0xff);
- outputStream.Write(buf, 0, 4);
- }
-
- // writeDQT writes the Define Quantization Table marker.
- private void writeDQT()
- {
- int markerlen = 2 + nQuantIndex * (1 + Block.blockSize);
- writeMarkerHeader(dqtMarker, markerlen);
- for (int i = 0; i < nQuantIndex; i++)
- {
- writeByte((byte)i);
- outputStream.Write(quant[i], 0, quant[i].Length);
+ a = -value;
+ b = value - 1;
}
- }
-
- // writeSOF0 writes the Start Of Frame (Baseline) marker.
- private void writeSOF0(int wid, int hei, int nComponent)
- {
- //"default" to 4:2:0
- byte[] subsamples = new byte[] { 0x22, 0x11, 0x11 };
- byte[] chroma = new byte[] { 0x00, 0x01, 0x01 };
- switch (subsample)
- {
- case JpegSubsample.Ratio444:
- subsamples = new byte[] { 0x11, 0x11, 0x11 };
- break;
- case JpegSubsample.Ratio420:
- subsamples = new byte[] { 0x22, 0x11, 0x11 };
- break;
- }
-
- int markerlen = 8 + 3 * nComponent;
- writeMarkerHeader(sof0Marker, markerlen);
- buf[0] = 8; // 8-bit color.
- buf[1] = (byte)(hei >> 8);
- buf[2] = (byte)(hei & 0xff);
- buf[3] = (byte)(wid >> 8);
- buf[4] = (byte)(wid & 0xff);
- buf[5] = (byte)(nComponent);
- if (nComponent == 1)
+ uint bt;
+ if (a < 0x100)
{
- buf[6] = 1;
- // No subsampling for grayscale image.
- buf[7] = 0x11;
- buf[8] = 0x00;
+ bt = this.bitCount[a];
}
else
{
- for (int i = 0; i < nComponent; i++)
- {
- buf[3 * i + 6] = (byte)(i + 1);
- // We use 4:2:0 chroma subsampling.
- buf[3 * i + 7] = subsamples[i];
- buf[3 * i + 8] = chroma[i];
- }
+ bt = 8 + (uint)this.bitCount[a >> 8];
}
- outputStream.Write(buf, 0, 3 * (nComponent - 1) + 9);
- }
- // writeDHT writes the Define Huffman Table marker.
- private void writeDHT(int nComponent)
- {
- byte[] headers = new byte[] { 0x00, 0x10, 0x01, 0x11 };
- int markerlen = 2;
- huffmanSpec[] specs = theHuffmanSpec;
-
- if (nComponent == 1)
- {
- // Drop the Chrominance tables.
- specs = new huffmanSpec[] { theHuffmanSpec[0], theHuffmanSpec[1] };
- }
-
- foreach (var s in specs)
+ this.EmitHuff(index, (int)((uint)(runLength << 4) | bt));
+ if (bt > 0)
{
- markerlen += 1 + 16 + s.values.Length;
- }
-
- writeMarkerHeader(dhtMarker, markerlen);
- for (int i = 0; i < specs.Length; i++)
- {
- var s = specs[i];
-
- writeByte(headers[i]);
- outputStream.Write(s.count, 0, s.count.Length);
- outputStream.Write(s.values, 0, s.values.Length);
+ this.Emit((uint)b & (uint)((1 << ((int)bt)) - 1), bt);
}
}
- // writeBlock writes a block of pixel data using the given quantization table,
- // returning the post-quantized DC value of the DCT-transformed block. b is in
- // natural (not zig-zag) order.
- private int writeBlock(Block b, quantIndex q, int prevDC)
+
+ ///
+ /// Writes a block of pixel data using the given quantization table,
+ /// 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.
+ ///
+ private int WriteBlock(Block block, QuantIndex index, int prevDC)
{
- FDCT.Transform(b);
+ FDCT.Transform(block);
// Emit the DC delta.
- int dc = div(b[0], 8 * quant[(int)q][0]);
- emitHuffRLE((huffIndex)(2 * (int)q + 0), 0, dc - prevDC);
+ int dc = Round(block[0], 8 * this.quant[(int)index][0]);
+ this.EmitHuffRLE((HuffIndex)(2 * (int)index + 0), 0, dc - prevDC);
// Emit the AC components.
- var h = (huffIndex)(2 * (int)q + 1);
+ var h = (HuffIndex)(2 * (int)index + 1);
int runLength = 0;
- for (int zig = 1; zig < Block.blockSize; zig++)
+ for (int zig = 1; zig < Block.BlockSize; zig++)
{
- int ac = div(b[unzig[zig]], 8 * quant[(int)q][zig]);
+ int ac = Round(block[Unzig[zig]], 8 * this.quant[(int)index][zig]);
if (ac == 0)
{
@@ -408,22 +324,22 @@ namespace ImageProcessorCore.Formats
{
while (runLength > 15)
{
- emitHuff(h, 0xf0);
+ this.EmitHuff(h, 0xf0);
runLength -= 16;
}
- emitHuffRLE(h, runLength, ac);
+ this.EmitHuffRLE(h, runLength, ac);
runLength = 0;
}
}
- if (runLength > 0)
- emitHuff(h, 0x00);
+
+ if (runLength > 0) this.EmitHuff(h, 0x00);
return dc;
}
// toYCbCr converts the 8x8 region of m whose top-left corner is p to its
// YCbCr values.
- private void toYCbCr(PixelAccessor pixels, int x, int y, Block yBlock, Block cbBlock, Block crBlock)
+ private void ToYCbCr(PixelAccessor pixels, int x, int y, Block yBlock, Block cbBlock, Block crBlock)
{
int xmax = pixels.Width - 1;
int ymax = pixels.Height - 1;
@@ -440,9 +356,13 @@ namespace ImageProcessorCore.Formats
}
}
- // scale scales the 16x16 region represented by the 4 src blocks to the 8x8
- // dst block.
- private void scale_16x16_8x8(Block dst, Block[] src)
+ ///
+ /// Scales the 16x16 region represented by the 4 src blocks to the 8x8
+ /// dst block.
+ ///
+ /// The destination block array
+ /// The source block array.
+ private void Scale16X16_8X8(Block destination, Block[] source)
{
for (int i = 0; i < 4; i++)
{
@@ -452,59 +372,288 @@ namespace ImageProcessorCore.Formats
for (int x = 0; x < 4; x++)
{
int j = 16 * y + 2 * x;
- int sum = src[i][j] + src[i][j + 1] + src[i][j + 8] + src[i][j + 9];
- dst[8 * y + x + dstOff] = (sum + 2) / 4;
+ 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;
}
}
}
}
- // sosHeaderY is the SOS marker "\xff\xda" followed by 8 bytes:
- // - the marker length "\x00\x08",
- // - the number of components "\x01",
- // - component 1 uses DC table 0 and AC table 0 "\x01\x00",
- // - the bytes "\x00\x3f\x00". Section B.2.3 of the spec says that for
- // sequential DCTs, those bytes (8-bit Ss, 8-bit Se, 4-bit Ah, 4-bit Al)
- // should be 0x00, 0x3f, 0x00<<4 | 0x00.
- private readonly byte[] sosHeaderY = new byte[] {
- 0xff, 0xda, 0x00, 0x08, 0x01, 0x01, 0x00, 0x00, 0x3f, 0x00,
+ // The SOS marker "\xff\xda" followed by 8 bytes:
+ // - the marker length "\x00\x08",
+ // - the number of components "\x01",
+ // - component 1 uses DC table 0 and AC table 0 "\x01\x00",
+ // - the bytes "\x00\x3f\x00". Section B.2.3 of the spec says that for
+ // sequential DCTs, those bytes (8-bit Ss, 8-bit Se, 4-bit Ah, 4-bit Al)
+ // should be 0x00, 0x3f, 0x00<<4 | 0x00.
+ private readonly byte[] SOSHeaderY =
+ {
+ JpegConstants.Markers.XFF, JpegConstants.Markers.SOS,
+ 0x00, 0x08, // Length (high byte, low byte), must be 6 + 2 * (number of components in scan)
+ 0x01, // Number of components in a scan, 1
+ 0x01, // Component Id Y
+ 0x00, // 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)
};
- // sosHeaderYCbCr is the SOS marker "\xff\xda" followed by 12 bytes:
- // - the marker length "\x00\x0c",
- // - the number of components "\x03",
- // - component 1 uses DC table 0 and AC table 0 "\x01\x00",
- // - component 2 uses DC table 1 and AC table 1 "\x02\x11",
- // - component 3 uses DC table 1 and AC table 1 "\x03\x11",
- // - the bytes "\x00\x3f\x00". Section B.2.3 of the spec says that for
- // sequential DCTs, those bytes (8-bit Ss, 8-bit Se, 4-bit Ah, 4-bit Al)
- // should be 0x00, 0x3f, 0x00<<4 | 0x00.
- private readonly byte[] sosHeaderYCbCr = new byte[] {
- 0xff, 0xda, 0x00, 0x0c, 0x03, 0x01, 0x00, 0x02,
- 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00,
+ // The SOS marker "\xff\xda" followed by 12 bytes:
+ // - the marker length "\x00\x0c",
+ // - the number of components "\x03",
+ // - component 1 uses DC table 0 and AC table 0 "\x01\x00",
+ // - component 2 uses DC table 1 and AC table 1 "\x02\x11",
+ // - component 3 uses DC table 1 and AC table 1 "\x03\x11",
+ // - the bytes "\x00\x3f\x00". Section B.2.3 of the spec says that for
+ // sequential DCTs, those bytes (8-bit Ss, 8-bit Se, 4-bit Ah, 4-bit Al)
+ // should be 0x00, 0x3f, 0x00<<4 | 0x00.
+ private readonly byte[] SOSHeaderYCbCr =
+ {
+ JpegConstants.Markers.XFF, JpegConstants.Markers.SOS,
+ 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)
};
+ // Encode writes the Image m to w in JPEG 4:2:0 baseline format with the given
+ // options. Default parameters are used if a nil *Options is passed.
+ public void Encode(Stream stream, ImageBase image, int quality, JpegSubsample sample)
+ {
+ Guard.NotNull(image, nameof(image));
+ Guard.NotNull(stream, nameof(stream));
+
+ ushort max = JpegConstants.MaxLength;
+ if (image.Width >= max || image.Height >= max)
+ {
+ throw new ImageFormatException($"Image is too large to encode at {image.Width}x{image.Height}.");
+ }
+
+ this.outputStream = stream;
+ this.subsample = sample;
+
+ // TODO: This should be static should it not?
+ for (int i = 0; i < this.theHuffmanSpec.Length; i++)
+ {
+ this.theHuffmanLUT[i] = new HuffmanLut(this.theHuffmanSpec[i]);
+ }
+
+ for (int i = 0; i < NQuantIndex; i++)
+ {
+ this.quant[i] = new byte[Block.BlockSize];
+ }
+
+ if (quality < 1) quality = 1;
+ if (quality > 100) quality = 100;
+
+ // Convert from a quality rating to a scaling factor.
+ int scale;
+ if (quality < 50)
+ {
+ scale = 5000 / quality;
+ }
+ else
+ {
+ scale = 200 - quality * 2;
+ }
+
+ // Initialize the quantization tables.
+ for (int i = 0; i < NQuantIndex; 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;
+ }
+ }
+
+ // Compute number of components based on input image type.
+ int componentCount = 3;
+
+ // Write the Start Of Image marker.
+ // TODO: JFIF header etc.
+ this.buffer[0] = 0xff;
+ this.buffer[1] = 0xd8;
+ stream.Write(this.buffer, 0, 2);
+
+ // Write the quantization tables.
+ this.WriteDQT();
- // writeSOS writes the StartOfScan marker.
- private void writeSOS(PixelAccessor pixels)
+ // Write the image dimensions.
+ this.WriteSOF0(image.Width, image.Height, componentCount);
+
+ // Write the Huffman tables.
+ this.WriteDHT(componentCount);
+
+ // Write the image data.
+ using (PixelAccessor pixels = image.Lock())
+ {
+ this.WriteSOS(pixels);
+ }
+
+ // Write the End Of Image marker.
+ this.buffer[0] = 0xff;
+ this.buffer[1] = 0xd9;
+ 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);
+ }
+
+ ///
+ /// Writes the Define Quantization Marker and tables.
+ ///
+ private void WriteDQT()
+ {
+ int markerlen = 2 + NQuantIndex * (1 + Block.BlockSize);
+ this.WriteMarkerHeader(JpegConstants.Markers.DQT, markerlen);
+ for (int i = 0; i < NQuantIndex; i++)
+ {
+ this.WriteByte((byte)i);
+ this.outputStream.Write(this.quant[i], 0, this.quant[i].Length);
+ }
+ }
+
+ ///
+ /// Writes the Start Of Frame (Baseline) marker
+ ///
+ /// The width of the image
+ /// The height of the image
+ ///
+ private void WriteSOF0(int width, int height, int componentCount)
+ {
+ // "default" to 4:2:0
+ byte[] subsamples = { 0x22, 0x11, 0x11 };
+ byte[] chroma = { 0x00, 0x01, 0x01 };
+
+ switch (this.subsample)
+ {
+ case JpegSubsample.Ratio444:
+ subsamples = new byte[] { 0x11, 0x11, 0x11 };
+ break;
+ case JpegSubsample.Ratio420:
+ subsamples = new byte[] { 0x22, 0x11, 0x11 };
+ break;
+ }
+
+ // Length (high byte, low byte), 8 + components * 3.
+ int markerlen = 8 + 3 * componentCount;
+ this.WriteMarkerHeader(JpegConstants.Markers.SOF0, markerlen);
+ this.buffer[0] = 8; // Data Precision. 8 for now, 12 and 16 bit jpegs not supported
+ this.buffer[1] = (byte)(height >> 8);
+ 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 = grey scaled, 3 = color YCbCr or YIQ, 4 = color CMYK)
+ if (componentCount == 1)
+ {
+ this.buffer[6] = 1;
+
+ // No subsampling for grayscale images.
+ this.buffer[7] = 0x11;
+ this.buffer[8] = 0x00;
+ }
+ else
+ {
+ for (int i = 0; i < componentCount; i++)
+ {
+ this.buffer[3 * i + 6] = (byte)(i + 1);
+
+ // We use 4:2:0 chroma subsampling by default.
+ this.buffer[3 * i + 7] = subsamples[i];
+ this.buffer[3 * i + 8] = chroma[i];
+ }
+ }
+
+ this.outputStream.Write(this.buffer, 0, 3 * (componentCount - 1) + 9);
+ }
+
+ ///
+ /// Writes the Define Huffman Table marker and tables.
+ ///
+ /// The number of components to write.
+ private void WriteDHT(int nComponent)
+ {
+ byte[] headers = { 0x00, 0x10, 0x01, 0x11 };
+ int markerlen = 2;
+ HuffmanSpec[] specs = this.theHuffmanSpec;
+
+ if (nComponent == 1)
+ {
+ // Drop the Chrominance tables.
+ specs = new[] { this.theHuffmanSpec[0], this.theHuffmanSpec[1] };
+ }
+
+ foreach (var s in specs)
+ {
+ markerlen += 1 + 16 + s.Values.Length;
+ }
+
+ this.WriteMarkerHeader(JpegConstants.Markers.DHT, markerlen);
+ for (int i = 0; i < specs.Length; i++)
+ {
+ HuffmanSpec spec = specs[i];
+
+ this.WriteByte(headers[i]);
+ this.outputStream.Write(spec.Count, 0, spec.Count.Length);
+ this.outputStream.Write(spec.Values, 0, spec.Values.Length);
+ }
+ }
+
+ ///
+ /// Writes the StartOfScan marker.
+ ///
+ /// The pixel accessor providing acces to the image pixels.
+ private void WriteSOS(PixelAccessor pixels)
{
- outputStream.Write(sosHeaderYCbCr, 0, sosHeaderYCbCr.Length);
+ // TODO: We should allow grayscale writing.
+ this.outputStream.Write(this.SOSHeaderYCbCr, 0, this.SOSHeaderYCbCr.Length);
- switch (subsample)
+ switch (this.subsample)
{
case JpegSubsample.Ratio444:
- encode444(pixels);
+ this.Encode444(pixels);
break;
case JpegSubsample.Ratio420:
- encode420(pixels);
+ this.Encode420(pixels);
break;
}
// Pad the last byte with 1's.
- emit(0x7f, 7);
+ this.Emit(0x7f, 7);
}
- private void encode444(PixelAccessor pixels)
+
+
+ ///
+ /// Encodes the image with no subsampling.
+ ///
+ /// The pixel accessor providing acces to the image pixels.
+ private void Encode444(PixelAccessor pixels)
{
Block b = new Block();
Block cb = new Block();
@@ -515,15 +664,20 @@ namespace ImageProcessorCore.Formats
{
for (int x = 0; x < pixels.Width; x += 8)
{
- toYCbCr(pixels, x, y, b, cb, cr);
- prevDCY = writeBlock(b, (quantIndex)0, prevDCY);
- prevDCCb = writeBlock(cb, (quantIndex)1, prevDCCb);
- prevDCCr = writeBlock(cr, (quantIndex)1, prevDCCr);
+ this.ToYCbCr(pixels, x, y, b, cb, cr);
+ prevDCY = this.WriteBlock(b, QuantIndex.Luminance, prevDCY);
+ prevDCCb = this.WriteBlock(cb, QuantIndex.Chrominance, prevDCCb);
+ prevDCCr = this.WriteBlock(cr, QuantIndex.Chrominance, prevDCCr);
}
}
}
- private void encode420(PixelAccessor pixels)
+ ///
+ /// Encodes the image with subsampling. The Cb and Cr components are each subsampled
+ /// at a factor of 2 both horizontally and vertically.
+ ///
+ /// The pixel accessor providing acces to the image pixels.
+ private void Encode420(PixelAccessor pixels)
{
Block b = new Block();
Block[] cb = new Block[4];
@@ -542,103 +696,88 @@ namespace ImageProcessorCore.Formats
int xOff = (i & 1) * 8;
int yOff = (i & 2) * 4;
- toYCbCr(pixels, x + xOff, y + yOff, b, cb[i], cr[i]);
- prevDCY = writeBlock(b, (quantIndex)0, prevDCY);
+ this.ToYCbCr(pixels, x + xOff, y + yOff, b, cb[i], cr[i]);
+ prevDCY = this.WriteBlock(b, QuantIndex.Luminance, prevDCY);
}
- scale_16x16_8x8(b, cb);
- prevDCCb = writeBlock(b, (quantIndex)1, prevDCCb);
- scale_16x16_8x8(b, cr);
- prevDCCr = writeBlock(b, (quantIndex)1, prevDCCr);
+
+ this.Scale16X16_8X8(b, cb);
+ prevDCCb = this.WriteBlock(b, QuantIndex.Chrominance, prevDCCb);
+ this.Scale16X16_8X8(b, cr);
+ prevDCCr = this.WriteBlock(b, QuantIndex.Chrominance, prevDCCr);
}
}
}
- // Encode writes the Image m to w in JPEG 4:2:0 baseline format with the given
- // options. Default parameters are used if a nil *Options is passed.
- public void Encode(Stream stream, ImageBase image, int quality, JpegSubsample subsample)
+ ///
+ /// Writes the header for a marker with the given length.
+ ///
+ /// The marker to write.
+ /// The marker length.
+ private void WriteMarkerHeader(byte marker, int length)
{
- this.outputStream = stream;
- this.subsample = subsample;
-
- for (int i = 0; i < theHuffmanSpec.Length; i++)
- {
- theHuffmanLUT[i] = new huffmanLUT(theHuffmanSpec[i]);
- }
-
- for (int i = 0; i < nQuantIndex; i++)
- {
- quant[i] = new byte[Block.blockSize];
- }
-
- if (image.Width >= (1 << 16) || image.Height >= (1 << 16))
- {
- throw new ImageFormatException($"Image is too large to encode at {image.Width}x{image.Height}.");
- }
-
- if (quality < 1) quality = 1;
- if (quality > 100) quality = 100;
-
- // Convert from a quality rating to a scaling factor.
- int scale;
- if (quality < 50)
- {
- scale = 5000 / quality;
- }
- else
- {
- scale = 200 - quality * 2;
- }
-
- // Initialize the quantization tables.
- for (int i = 0; i < nQuantIndex; i++)
- {
- for (int j = 0; j < Block.blockSize; j++)
- {
- int x = unscaledQuant[i, j];
- x = (x * scale + 50) / 100;
- if (x < 1) x = 1;
- if (x > 255) x = 255;
- quant[i][j] = (byte)x;
- }
- }
+ // Markers are always prefixed with with 0xff.
+ this.buffer[0] = JpegConstants.Markers.XFF;
+ this.buffer[1] = marker;
+ this.buffer[2] = (byte)(length >> 8);
+ this.buffer[3] = (byte)(length & 0xff);
+ this.outputStream.Write(this.buffer, 0, 4);
+ }
- // Compute number of components based on input image type.
- int nComponent = 3;
+ ///
+ /// Enumerates the Huffman tables
+ ///
+ private enum HuffIndex
+ {
+ LuminanceDC = 0,
- // Write the Start Of Image marker.
- buf[0] = 0xff;
- buf[1] = 0xd8;
- stream.Write(buf, 0, 2);
+ LuminanceAC = 1,
- // Write the quantization tables.
- writeDQT();
+ ChrominanceDC = 2,
- // Write the image dimensions.
- writeSOF0(image.Width, image.Height, nComponent);
+ ChrominanceAC = 3,
+ }
- // Write the Huffman tables.
- writeDHT(nComponent);
+ ///
+ /// Enumerates the quantization tables
+ ///
+ private enum QuantIndex
+ {
+ ///
+ /// Luminance
+ ///
+ Luminance = 0,
+
+ ///
+ /// Chrominance
+ ///
+ Chrominance = 1,
+ }
- // Write the image data.
- using (PixelAccessor pixels = image.Lock())
+ ///
+ /// The Huffman encoding specifications.
+ ///
+ private struct HuffmanSpec
+ {
+ ///
+ /// Initializes a n ew instance of the struct.
+ ///
+ /// The number of codes.
+ /// The decoded values.
+ public HuffmanSpec(byte[] count, byte[] values)
{
- writeSOS(pixels);
+ this.Count = count;
+ this.Values = values;
}
- // Write the End Of Image marker.
- buf[0] = 0xff;
- buf[1] = 0xd9;
- stream.Write(buf, 0, 2);
- stream.Flush();
- }
+ ///
+ /// Gets count[i] - The number of codes of length i bits.
+ ///
+ public readonly byte[] Count;
- // div returns a/b rounded to the nearest integer, instead of rounded to zero.
- private static int div(int a, int b)
- {
- if (a >= 0)
- return (a + (b >> 1)) / b;
- else
- return -((-a + (b >> 1)) / b);
+ ///
+ /// Gets value[i] - The decoded value of the i'th codeword.
+ ///
+ public readonly byte[] Values;
}
}
}
diff --git a/src/ImageProcessorCore - Copy/ImageExtensions.cs b/src/ImageProcessorCore - Copy/ImageExtensions.cs
index e7b261d60..ea320aad4 100644
--- a/src/ImageProcessorCore - Copy/ImageExtensions.cs
+++ b/src/ImageProcessorCore - Copy/ImageExtensions.cs
@@ -116,7 +116,7 @@ namespace ImageProcessorCore
/// The .
public static Image Process(this Image source, int width, int height, Rectangle sourceRectangle, Rectangle targetRectangle, IImageSampler sampler)
{
- return PerformAction(source, false, (sourceImage, targetImage) => sampler.Apply(targetImage, sourceImage, width, height, targetRectangle, sourceRectangle));
+ return PerformAction(source, false, (sourceImage, targetImage) => sampler.Apply(targetImage, sourceImage, width, height, targetRectangle, sourceRectangle));
}
///
@@ -135,6 +135,7 @@ namespace ImageProcessorCore
{
// Several properties require copying
// TODO: Check why we need to set these?
+ Quality = source.Quality,
HorizontalResolution = source.HorizontalResolution,
VerticalResolution = source.VerticalResolution,
CurrentImageFormat = source.CurrentImageFormat,
diff --git a/src/ImageProcessorCore/Formats/Bmp/BmpDecoderCore.cs b/src/ImageProcessorCore/Formats/Bmp/BmpDecoderCore.cs
index 645ddbac6..c6704bdb0 100644
--- a/src/ImageProcessorCore/Formats/Bmp/BmpDecoderCore.cs
+++ b/src/ImageProcessorCore/Formats/Bmp/BmpDecoderCore.cs
@@ -53,7 +53,7 @@ namespace ImageProcessorCore.Formats
/// The packed format. long, float.
/// The image, where the data should be set to.
/// Cannot be null (Nothing in Visual Basic).
- /// The this._stream, where the image should be
+ /// The stream, where the image should be
/// decoded from. Cannot be null (Nothing in Visual Basic).
///
/// is null.
diff --git a/src/ImageProcessorCore/Formats/Jpg/JpegConstants.cs b/src/ImageProcessorCore/Formats/Jpg/JpegConstants.cs
new file mode 100644
index 000000000..4df086f29
--- /dev/null
+++ b/src/ImageProcessorCore/Formats/Jpg/JpegConstants.cs
@@ -0,0 +1,209 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageProcessorCore.Formats
+{
+ ///
+ /// Defines jpeg constants defined in the specification.
+ ///
+ internal static class JpegConstants
+ {
+ ///
+ /// The maximum allowable length in each dimension of a jpeg image.
+ ///
+ public const ushort MaxLength = 65535;
+
+ ///
+ /// Represents high detail chroma horizontal subsampling.
+ ///
+ public static readonly byte[] ChromaFourFourFourHorizontal = { 0x11, 0x11, 0x11 };
+
+ ///
+ /// Represents high detail chroma vertical subsampling.
+ ///
+ public static readonly byte[] ChromaFourFourFourVertical = { 0x11, 0x11, 0x11 };
+
+ ///
+ /// Represents medium detail chroma horizontal subsampling.
+ ///
+ public static readonly byte[] ChromaFourTwoTwoHorizontal = { 0x22, 0x11, 0x11 };
+
+ ///
+ /// Represents medium detail chroma vertical subsampling.
+ ///
+ public static readonly byte[] ChromaFourTwoTwoVertical = { 0x11, 0x11, 0x11 };
+
+ ///
+ /// Represents low detail chroma horizontal subsampling.
+ ///
+ public static readonly byte[] ChromaFourTwoZeroHorizontal = { 0x22, 0x11, 0x11 };
+
+ ///
+ /// Represents low detail chroma vertical subsampling.
+ ///
+ public static readonly byte[] ChromaFourTwoZeroVertical = { 0x22, 0x11, 0x11 };
+
+ ///
+ /// Describes component ids for start of frame components.
+ ///
+ internal static class Components
+ {
+ ///
+ /// The YCbCr luminance component id.
+ ///
+ public const byte Y = 1;
+
+ ///
+ /// The YCbCr chroma component id.
+ ///
+ public const byte Cb = 2;
+
+ ///
+ /// The YCbCr chroma component id.
+ ///
+ public const byte Cr = 3;
+
+ ///
+ /// The YIQ x coordinate component id.
+ ///
+ public const byte I = 4;
+
+ ///
+ /// The YIQ y coordinate component id.
+ ///
+ public const byte Q = 5;
+ }
+
+ ///
+ /// Describes common Jpeg markers
+ ///
+ internal static class Markers
+ {
+ ///
+ /// Marker prefix. Next byte is a marker.
+ ///
+ public const byte XFF = 0xff;
+
+ ///
+ /// Start of Image
+ ///
+ public const byte SOI = 0xd8;
+
+ ///
+ /// Start of Frame (baseline DCT)
+ ///
+ /// Indicates that this is a baseline DCT-based JPEG, and specifies the width, height, number of components,
+ /// and component subsampling (e.g., 4:2:0).
+ ///
+ ///
+ public const byte SOF0 = 0xc0;
+
+ ///
+ /// Start Of Frame (Extended Sequential DCT)
+ ///
+ /// Indicates that this is a progressive DCT-based JPEG, and specifies the width, height, number of components,
+ /// and component subsampling (e.g., 4:2:0).
+ ///
+ ///
+ public const byte SOF1 = 0xc1;
+
+ ///
+ /// Start Of Frame (progressive DCT)
+ ///
+ /// Indicates that this is a progressive DCT-based JPEG, and specifies the width, height, number of components,
+ /// and component subsampling (e.g., 4:2:0).
+ ///
+ ///
+ public const byte SOF2 = 0xc2;
+
+ ///
+ /// Define Huffman Table(s)
+ ///
+ /// Specifies one or more Huffman tables.
+ ///
+ ///
+ public const byte DHT = 0xc4;
+
+ ///
+ /// Define Quantization Table(s)
+ ///
+ /// Specifies one or more quantization tables.
+ ///
+ ///
+ public const byte DQT = 0xdb;
+
+ ///
+ /// Define Restart Interval
+ ///
+ /// Specifies the interval between RSTn markers, in macroblocks. This marker is followed by two bytes
+ /// indicating the fixed size so it can be treated like any other variable size segment.
+ ///
+ ///
+ public const byte DRI = 0xdd;
+
+ ///
+ /// Define First Restart
+ ///
+ /// Inserted every r macroblocks, where r is the restart interval set by a DRI marker.
+ /// Not used if there was no DRI marker. The low three bits of the marker code cycle in value from 0 to 7.
+ ///
+ ///
+ public const byte RST0 = 0xd0;
+
+ ///
+ /// Define Eigth Restart
+ ///
+ /// Inserted every r macroblocks, where r is the restart interval set by a DRI marker.
+ /// Not used if there was no DRI marker. The low three bits of the marker code cycle in value from 0 to 7.
+ ///
+ ///
+ public const byte RST7 = 0xd7;
+
+ ///
+ /// Start of Scan
+ ///
+ /// Begins a top-to-bottom scan of the image. In baseline DCT JPEG images, there is generally a single scan.
+ /// Progressive DCT JPEG images usually contain multiple scans. This marker specifies which slice of data it
+ /// will contain, and is immediately followed by entropy-coded data.
+ ///
+ ///
+ public const byte SOS = 0xda;
+
+ ///
+ /// Comment
+ ///
+ /// Contains a text comment.
+ ///
+ ///
+ public const byte COM = 0xfe;
+
+ ///
+ /// End of Image
+ ///
+ public const byte EOI = 0xd9;
+
+ ///
+ /// Application specific marker for marking the jpeg format.
+ ///
+ ///
+ public const byte APP0 = 0xe0;
+
+ ///
+ /// Application specific marker for marking where to store metadata.
+ ///
+ public const byte APP1 = 0xe1;
+
+ ///
+ /// Application specific marker used by Adobe for storing encoding information for DCT filters.
+ ///
+ public const byte APP14 = 0xee;
+
+ ///
+ /// Application specific marker used by GraphicConverter to store JPEG quality.
+ ///
+ public const byte APP15 = 0xef;
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/ImageProcessorCore.Tests/FileTestBase.cs b/tests/ImageProcessorCore.Tests/FileTestBase.cs
index 0ef3403a1..8348d309c 100644
--- a/tests/ImageProcessorCore.Tests/FileTestBase.cs
+++ b/tests/ImageProcessorCore.Tests/FileTestBase.cs
@@ -19,17 +19,17 @@ namespace ImageProcessorCore.Tests
///
protected static readonly List Files = new List
{
- //"TestImages/Formats/Jpg/Floorplan.jpeg", // Perf: Enable for local testing only
- //"TestImages/Formats/Jpg/Calliphora.jpg",
- //"TestImages/Formats/Jpg/fb.jpg", // Perf: Enable for local testing only
- //"TestImages/Formats/Jpg/progress.jpg", // Perf: Enable for local testing only
+ "TestImages/Formats/Jpg/Floorplan.jpeg", // Perf: Enable for local testing only
+ "TestImages/Formats/Jpg/Calliphora.jpg",
+ "TestImages/Formats/Jpg/fb.jpg", // Perf: Enable for local testing only
+ "TestImages/Formats/Jpg/progress.jpg", // Perf: Enable for local testing only
//"TestImages/Formats/Jpg/gamma_dalai_lama_gray.jpg", // Perf: Enable for local testing only
- "TestImages/Formats/Bmp/Car.bmp",
- //"TestImages/Formats/Bmp/neg_height.bmp", // Perf: Enable for local testing only
+ //"TestImages/Formats/Bmp/Car.bmp",
+ // "TestImages/Formats/Bmp/neg_height.bmp", // Perf: Enable for local testing only
//"TestImages/Formats/Png/blur.png", // Perf: Enable for local testing only
//"TestImages/Formats/Png/indexed.png", // Perf: Enable for local testing only
- "TestImages/Formats/Png/splash.png",
- "TestImages/Formats/Gif/rings.gif",
+ //"TestImages/Formats/Png/splash.png",
+ //"TestImages/Formats/Gif/rings.gif",
//"TestImages/Formats/Gif/giphy.gif" // Perf: Enable for local testing only
};