Browse Source

Clean up block and FDCT

Former-commit-id: af0313bdaeecaa56e576f25a1fa688a333468759
Former-commit-id: 64319b3f8faaa68997ce43ffb284a1d5d4937a78
Former-commit-id: 971cc28d2e1a019c99800f231c3451c89d1bca49
pull/1/head
James Jackson-South 10 years ago
parent
commit
baf0443f24
  1. 39
      src/ImageProcessorCore/Formats/Jpg/Block.cs
  2. 106
      src/ImageProcessorCore/Formats/Jpg/FDCT.cs
  3. 171
      src/ImageProcessorCore/Formats/Jpg/JpegConstants.cs
  4. 2
      src/ImageProcessorCore/Formats/Jpg/JpegDecoderCore.cs.REMOVED.git-id
  5. 8
      src/ImageProcessorCore/Formats/Jpg/JpegEncoderCore.cs

39
src/ImageProcessorCore/Formats/Jpg/Block.cs

@ -1,19 +1,44 @@
namespace ImageProcessorCore.Formats
// <copyright file="Block.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Formats
{
/// <summary>
/// Represents an 8x8 block of coefficients to transform and encode.
/// </summary>
internal class Block
{
public const int blockSize = 64;
private int[] _data;
/// <summary>
/// Gets the size of the block.
/// </summary>
public const int BlockSize = 64;
/// <summary>
/// The array of block data.
/// </summary>
private readonly int[] data;
/// <summary>
/// Initializes a new instance of the <see cref="Block"/> class.
/// </summary>
public Block()
{
_data = new int[blockSize];
this.data = new int[BlockSize];
}
public int this[int idx]
/// <summary>
/// Gets the pixel data at the given block index.
/// </summary>
/// <param name="index">The index of the data to return.</param>
/// <returns>
/// The <see cref="int"/>.
/// </returns>
public int this[int index]
{
get { return _data[idx]; }
set { _data[idx] = value; }
get { return this.data[index]; }
set { this.data[index] = value; }
}
}
}

106
src/ImageProcessorCore/Formats/Jpg/FDCT.cs

@ -5,9 +5,14 @@
namespace ImageProcessorCore.Formats
{
/// <summary>
/// Performs a fast, forward descrete cosine transform against the given block
/// decomposing it into 64 orthogonal basis signals.
/// </summary>
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)
/// <summary>
/// The number of bits
/// </summary>
private const int Bits = 13;
/// <summary>
/// The number of bits to shift by on the first pass.
/// </summary>
private const int Pass1Bits = 2;
/// <summary>
/// The value to shift by
/// </summary>
private const int CenterJSample = 128;
/// <summary>
/// Performs a forward DCT on an 8x8 block of coefficients, including a
/// level shift.
/// </summary>
/// <param name="block">The block.</param>
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);
}
}
}

171
src/ImageProcessorCore/Formats/Jpg/JpegConstants.cs

@ -0,0 +1,171 @@
// <copyright file="JpegConstants.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Formats
{
/// <summary>
/// Defines jpeg constants defined in the specification.
/// </summary>
internal static class JpegConstants
{
/// <summary>
/// The maximum allowable length in each dimension of a jpeg image.
/// </summary>
public const ushort MaxLength = 65535;
/// <summary>
/// Represents high detail chroma horizontal subsampling.
/// </summary>
public static readonly byte[] ChromaFourFourFourHorizontal = { 1, 1, 1 };
/// <summary>
/// Represents high detail chroma vertical subsampling.
/// </summary>
public static readonly byte[] ChromaFourFourFourVertical = { 1, 1, 1 };
/// <summary>
/// Represents medium detail chroma horizontal subsampling.
/// </summary>
public static readonly byte[] ChromaFourTwoTwoHorizontal = { 2, 1, 1 };
/// <summary>
/// Represents medium detail chroma vertical subsampling.
/// </summary>
public static readonly byte[] ChromaFourTwoTwoVertical = { 1, 1, 1 };
/// <summary>
/// Represents low detail chroma horizontal subsampling.
/// </summary>
public static readonly byte[] ChromaFourTwoZeroHorizontal = { 2, 1, 1 };
/// <summary>
/// Represents low detail chroma vertical subsampling.
/// </summary>
public static readonly byte[] ChromaFourTwoZeroVertical = { 2, 1, 1 };
/// <summary>
/// Describes component ids for start of frame components.
/// </summary>
internal static class Components
{
/// <summary>
/// The YCbCr luminance component id.
/// </summary>
public const byte Y = 1;
/// <summary>
/// The YCbCr chroma component id.
/// </summary>
public const byte Cb = 2;
/// <summary>
/// The YCbCr chroma component id.
/// </summary>
public const byte Cr = 3;
/// <summary>
/// The YIQ x coordinate component id.
/// </summary>
public const byte I = 4;
/// <summary>
/// The YIQ y coordinate component id.
/// </summary>
public const byte Q = 5;
}
/// <summary>
/// Describes common Jpeg markers
/// </summary>
internal static class Markers
{
/// <summary>
/// Marker prefix. Next byte is a marker.
/// </summary>
public const byte XFF = 0xff;
/// <summary>
/// Start of Image
/// </summary>
public const byte SOI = 0xd8;
/// <summary>
/// Start of Frame (baseline DCT)
/// <remarks>
/// 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).
/// </remarks>
/// </summary>
public const byte SOF0 = 0xc0;
/// <summary>
/// Start Of Frame (progressive DCT)
/// <remarks>
/// 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).
/// </remarks>
/// </summary>
public const byte SOF2 = 0xc0;
/// <summary>
/// Define Huffman Table(s)
/// <remarks>
/// Specifies one or more Huffman tables.
/// </remarks>
/// </summary>
public const byte DHT = 0xc4;
/// <summary>
/// Define Quantization Table(s)
/// <remarks>
/// Specifies one or more quantization tables.
/// </remarks>
/// </summary>
public const byte DQT = 0xdb;
/// <summary>
/// Define Restart Interval
/// <remarks>
/// 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.
/// </remarks>
/// </summary>
public const byte DRI = 0xdd;
/// <summary>
/// Start of Scan
/// <remarks>
/// 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.
/// </remarks>
/// </summary>
public const byte SOS = 0xda;
/// <summary>
/// Comment
/// <remarks>
/// Contains a text comment.
/// </remarks>
/// </summary>
public const byte COM = 0xfe;
/// <summary>
/// End of Image
/// </summary>
public const byte EOI = 0xd9;
/// <summary>
/// Application specific marker for marking the jpeg format.
/// </summary>
public const byte APP0 = 0xe0;
/// <summary>
/// Application specific marker for marking where to store metadata.
/// </summary>
public const byte APP1 = 0xe1;
}
}
}

2
src/ImageProcessorCore/Formats/Jpg/JpegDecoderCore.cs.REMOVED.git-id

@ -1 +1 @@
07d53b1f9ee1e867cfd8bb664c2cf3f31c0e8289
af4a353d1d290be5dd231656bdb558fcdc143805

8
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;

Loading…
Cancel
Save