Browse Source

Merge remote-tracking branch 'refs/remotes/origin/Core' into Core-Flava

# Conflicts:
#	src/ImageProcessorCore/Image.cs
#	tests/ImageProcessorCore.Tests/FileTestBase.cs


Former-commit-id: df31902b9996a1027ac83bb3f4712cbf81ae6bb0
Former-commit-id: cdea3844dbe5a16b5272519be80d38b3d2d0fae2
Former-commit-id: ec4ffaa3de3f3ebe483e2f632c495406cf6627f3
af/merge-core
James Jackson-South 10 years ago
parent
commit
79c7dbe99a
  1. 39
      src/ImageProcessorCore - Copy/Formats/Jpg/Block.cs
  2. 106
      src/ImageProcessorCore - Copy/Formats/Jpg/FDCT.cs
  3. 34
      src/ImageProcessorCore - Copy/Formats/Jpg/JpegDecoder.cs
  4. 2
      src/ImageProcessorCore - Copy/Formats/Jpg/JpegDecoderCore.cs.REMOVED.git-id
  5. 985
      src/ImageProcessorCore - Copy/Formats/Jpg/JpegEncoderCore.cs
  6. 3
      src/ImageProcessorCore - Copy/ImageExtensions.cs
  7. 2
      src/ImageProcessorCore/Formats/Bmp/BmpDecoderCore.cs
  8. 209
      src/ImageProcessorCore/Formats/Jpg/JpegConstants.cs
  9. 16
      tests/ImageProcessorCore.Tests/FileTestBase.cs

39
src/ImageProcessorCore - Copy/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 - Copy/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);
}
}
}

34
src/ImageProcessorCore - Copy/Formats/Jpg/JpegDecoder.cs

@ -7,7 +7,6 @@ namespace ImageProcessorCore.Formats
{
using System;
using System.IO;
using System.Threading.Tasks;
/// <summary>
/// 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.");
}
}
/// <summary>

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

@ -1 +1 @@
07d53b1f9ee1e867cfd8bb664c2cf3f31c0e8289
7a5076971068e0f389da2fb1e8b25216f4049718

985
src/ImageProcessorCore - Copy/Formats/Jpg/JpegEncoderCore.cs

File diff suppressed because it is too large

3
src/ImageProcessorCore - Copy/ImageExtensions.cs

@ -116,7 +116,7 @@ namespace ImageProcessorCore
/// <returns>The <see cref="Image"/>.</returns>
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));
}
/// <summary>
@ -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,

2
src/ImageProcessorCore/Formats/Bmp/BmpDecoderCore.cs

@ -53,7 +53,7 @@ namespace ImageProcessorCore.Formats
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
/// <param name="image">The image, where the data should be set to.
/// Cannot be null (Nothing in Visual Basic).</param>
/// <param name="stream">The this._stream, where the image should be
/// <param name="stream">The stream, where the image should be
/// decoded from. Cannot be null (Nothing in Visual Basic).</param>
/// <exception cref="ArgumentNullException">
/// <para><paramref name="image"/> is null.</para>

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

@ -0,0 +1,209 @@
// <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 = { 0x11, 0x11, 0x11 };
/// <summary>
/// Represents high detail chroma vertical subsampling.
/// </summary>
public static readonly byte[] ChromaFourFourFourVertical = { 0x11, 0x11, 0x11 };
/// <summary>
/// Represents medium detail chroma horizontal subsampling.
/// </summary>
public static readonly byte[] ChromaFourTwoTwoHorizontal = { 0x22, 0x11, 0x11 };
/// <summary>
/// Represents medium detail chroma vertical subsampling.
/// </summary>
public static readonly byte[] ChromaFourTwoTwoVertical = { 0x11, 0x11, 0x11 };
/// <summary>
/// Represents low detail chroma horizontal subsampling.
/// </summary>
public static readonly byte[] ChromaFourTwoZeroHorizontal = { 0x22, 0x11, 0x11 };
/// <summary>
/// Represents low detail chroma vertical subsampling.
/// </summary>
public static readonly byte[] ChromaFourTwoZeroVertical = { 0x22, 0x11, 0x11 };
/// <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 (Extended Sequential 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 SOF1 = 0xc1;
/// <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 = 0xc2;
/// <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>
/// Define First Restart
/// <remarks>
/// 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.
/// </remarks>
/// </summary>
public const byte RST0 = 0xd0;
/// <summary>
/// Define Eigth Restart
/// <remarks>
/// 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.
/// </remarks>
/// </summary>
public const byte RST7 = 0xd7;
/// <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.
/// <see href="http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html"/>
/// </summary>
public const byte APP0 = 0xe0;
/// <summary>
/// Application specific marker for marking where to store metadata.
/// </summary>
public const byte APP1 = 0xe1;
/// <summary>
/// Application specific marker used by Adobe for storing encoding information for DCT filters.
/// </summary>
public const byte APP14 = 0xee;
/// <summary>
/// Application specific marker used by GraphicConverter to store JPEG quality.
/// </summary>
public const byte APP15 = 0xef;
}
}
}

16
tests/ImageProcessorCore.Tests/FileTestBase.cs

@ -19,17 +19,17 @@ namespace ImageProcessorCore.Tests
/// </summary>
protected static readonly List<string> Files = new List<string>
{
//"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
};

Loading…
Cancel
Save