diff --git a/src/ImageProcessorCore/Formats/Jpg/Block.cs b/src/ImageProcessorCore/Formats/Jpg/Block.cs index 655471a07d..35aa10f181 100644 --- a/src/ImageProcessorCore/Formats/Jpg/Block.cs +++ b/src/ImageProcessorCore/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/Formats/Jpg/FDCT.cs b/src/ImageProcessorCore/Formats/Jpg/FDCT.cs index a6073f9c79..e51ea64151 100644 --- a/src/ImageProcessorCore/Formats/Jpg/FDCT.cs +++ b/src/ImageProcessorCore/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/Formats/Jpg/JpegConstants.cs b/src/ImageProcessorCore/Formats/Jpg/JpegConstants.cs new file mode 100644 index 0000000000..27514d667f --- /dev/null +++ b/src/ImageProcessorCore/Formats/Jpg/JpegConstants.cs @@ -0,0 +1,171 @@ +// +// 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 = { 1, 1, 1 }; + + /// + /// Represents high detail chroma vertical subsampling. + /// + public static readonly byte[] ChromaFourFourFourVertical = { 1, 1, 1 }; + + /// + /// Represents medium detail chroma horizontal subsampling. + /// + public static readonly byte[] ChromaFourTwoTwoHorizontal = { 2, 1, 1 }; + + /// + /// Represents medium detail chroma vertical subsampling. + /// + public static readonly byte[] ChromaFourTwoTwoVertical = { 1, 1, 1 }; + + /// + /// Represents low detail chroma horizontal subsampling. + /// + public static readonly byte[] ChromaFourTwoZeroHorizontal = { 2, 1, 1 }; + + /// + /// Represents low detail chroma vertical subsampling. + /// + public static readonly byte[] ChromaFourTwoZeroVertical = { 2, 1, 1 }; + + /// + /// 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 (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 = 0xc0; + + /// + /// 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; + + /// + /// 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; + } + } +} \ No newline at end of file diff --git a/src/ImageProcessorCore/Formats/Jpg/JpegDecoderCore.cs.REMOVED.git-id b/src/ImageProcessorCore/Formats/Jpg/JpegDecoderCore.cs.REMOVED.git-id index 6cf3cee55e..8d3e51867e 100644 --- a/src/ImageProcessorCore/Formats/Jpg/JpegDecoderCore.cs.REMOVED.git-id +++ b/src/ImageProcessorCore/Formats/Jpg/JpegDecoderCore.cs.REMOVED.git-id @@ -1 +1 @@ -07d53b1f9ee1e867cfd8bb664c2cf3f31c0e8289 \ No newline at end of file +af4a353d1d290be5dd231656bdb558fcdc143805 \ No newline at end of file diff --git a/src/ImageProcessorCore/Formats/Jpg/JpegEncoderCore.cs b/src/ImageProcessorCore/Formats/Jpg/JpegEncoderCore.cs index a1f49a0c83..f211e05f8b 100644 --- a/src/ImageProcessorCore/Formats/Jpg/JpegEncoderCore.cs +++ b/src/ImageProcessorCore/Formats/Jpg/JpegEncoderCore.cs @@ -298,7 +298,7 @@ namespace ImageProcessorCore.Formats // writeDQT writes the Define Quantization Table marker. private void writeDQT() { - int markerlen = 2 + nQuantIndex * (1 + Block.blockSize); + int markerlen = 2 + nQuantIndex * (1 + Block.BlockSize); writeMarkerHeader(dqtMarker, markerlen); for (int i = 0; i < nQuantIndex; i++) { @@ -396,7 +396,7 @@ namespace ImageProcessorCore.Formats var h = (huffIndex)(2 * (int)q + 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]); @@ -567,7 +567,7 @@ namespace ImageProcessorCore.Formats for (int i = 0; i < nQuantIndex; i++) { - quant[i] = new byte[Block.blockSize]; + quant[i] = new byte[Block.BlockSize]; } if (image.Width >= (1 << 16) || image.Height >= (1 << 16)) @@ -592,7 +592,7 @@ namespace ImageProcessorCore.Formats // Initialize the quantization tables. for (int i = 0; i < nQuantIndex; i++) { - for (int j = 0; j < Block.blockSize; j++) + for (int j = 0; j < Block.BlockSize; j++) { int x = unscaledQuant[i, j]; x = (x * scale + 50) / 100;