Browse Source

Perf improvements

Former-commit-id: dcdc915ea97c8e823835fb947730687ecf684a8a
Former-commit-id: 6130e5f2e4bb13fc381d4d7c73d5265015d03068
Former-commit-id: d49d8c70fe7f2833623f87a5e57a0b216a703efd
af/merge-core
James Jackson-South 10 years ago
parent
commit
b8217897d2
  1. 1679
      src/ImageProcessorCore/Formats/Jpg/Decoder.cs
  2. 5
      src/ImageProcessorCore/Formats/Jpg/FDCT.cs
  3. 37
      src/ImageProcessorCore/Formats/Jpg/IDCT.cs
  4. 27
      src/ImageProcessorCore/Formats/Jpg/JpegDecoder.cs
  5. 1
      src/ImageProcessorCore/Formats/Jpg/JpegDecoderCore.cs.REMOVED.git-id
  6. 2
      src/ImageProcessorCore/Formats/Jpg/JpegEncoder.cs
  7. 40
      src/ImageProcessorCore/Formats/Jpg/JpegEncoderCore.cs

1679
src/ImageProcessorCore/Formats/Jpg/Decoder.cs

File diff suppressed because it is too large

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

@ -1,3 +1,8 @@
// <copyright file="FDCT.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Formats
{
internal class FDCT

37
src/ImageProcessorCore/Formats/Jpg/IDCT.cs

@ -1,3 +1,8 @@
// <copyright file="IDCT.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Formats
{
internal class IDCT
@ -107,14 +112,14 @@ namespace ImageProcessorCore.Formats
// we do not bother to check for the all-zero case.
// Prescale.
int y0 = (src[8 * 0 + x] << 8) + 8192;
int y1 = src[8 * 4 + x] << 8;
int y2 = src[8 * 6 + x];
int y3 = src[8 * 2 + x];
int y4 = src[8 * 1 + x];
int y5 = src[8 * 7 + x];
int y6 = src[8 * 5 + x];
int y7 = src[8 * 3 + x];
int y0 = (src[x] << 8) + 8192;
int y1 = src[32 + x] << 8;
int y2 = src[48 + x];
int y3 = src[16 + x];
int y4 = src[8 + x];
int y5 = src[56 + x];
int y6 = src[40 + x];
int y7 = src[24 + x];
// Stage 1.
int y8 = w7 * (y4 + y5) + 4;
@ -144,14 +149,14 @@ namespace ImageProcessorCore.Formats
y4 = (r2 * (y4 - y5) + 128) >> 8;
// Stage 4.
src[8 * 0 + x] = (y7 + y1) >> 14;
src[8 * 1 + x] = (y3 + y2) >> 14;
src[8 * 2 + x] = (y0 + y4) >> 14;
src[8 * 3 + x] = (y8 + y6) >> 14;
src[8 * 4 + x] = (y8 - y6) >> 14;
src[8 * 5 + x] = (y0 - y4) >> 14;
src[8 * 6 + x] = (y3 - y2) >> 14;
src[8 * 7 + x] = (y7 - y1) >> 14;
src[x] = (y7 + y1) >> 14;
src[8 + x] = (y3 + y2) >> 14;
src[16 + x] = (y0 + y4) >> 14;
src[24 + x] = (y8 + y6) >> 14;
src[32 + x] = (y8 - y6) >> 14;
src[40 + x] = (y0 - y4) >> 14;
src[48 + x] = (y3 - y2) >> 14;
src[56 + x] = (y7 - y1) >> 14;
}
}
}

27
src/ImageProcessorCore/Formats/Jpg/JpegDecoder.cs

@ -8,7 +8,6 @@ namespace ImageProcessorCore.Formats
using System;
using System.IO;
using System.Threading.Tasks;
using ImageProcessorCore.Formats;
/// <summary>
/// Image decoder for generating an image out of a jpg stream.
@ -95,8 +94,8 @@ namespace ImageProcessorCore.Formats
Guard.NotNull(image, "image");
Guard.NotNull(stream, "stream");
Decoder decoder = new Decoder();
decoder.decode(stream, false);
JpegDecoderCore decoder = new JpegDecoderCore();
decoder.Decode(stream, image, false);
int pixelWidth = decoder.width;
int pixelHeight = decoder.height;
@ -121,33 +120,19 @@ namespace ImageProcessorCore.Formats
pixels[offset + 3] = 1;
}
});
image.SetPixels(pixelWidth, pixelHeight, pixels);
}
else if (decoder.nComp == 3)
{
Parallel.For(
0,
pixelHeight,
y =>
{
var yoff = decoder.imgrgb.get_row_offset(y);
for (int x = 0; x < pixelWidth; x++)
{
int offset = ((y * pixelWidth) + x) * 4;
int sourceOffset = yoff + (3 * x);
pixels[offset + 0] = decoder.imgrgb.pixels[sourceOffset];
pixels[offset + 1] = decoder.imgrgb.pixels[sourceOffset + 1];
pixels[offset + 2] = decoder.imgrgb.pixels[sourceOffset + 2];
pixels[offset + 3] = 1;
}
});
// pixels = decoder.imgrgb.pixels;
}
else
{
throw new NotSupportedException("JpegDecoder only supports RGB and Grayscale color spaces.");
}
image.SetPixels(pixelWidth, pixelHeight, pixels);
//image.SetPixels(pixelWidth, pixelHeight, pixels);
}
/// <summary>

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

@ -0,0 +1 @@
4b32515bda4abce4bd01c9ad019ce9a581b1c56f

2
src/ImageProcessorCore/Formats/Jpg/JpegEncoder.cs

@ -56,7 +56,7 @@ namespace ImageProcessorCore.Formats
Guard.NotNull(image, nameof(image));
Guard.NotNull(stream, nameof(stream));
Encoder encode = new Encoder();
JpegEncoderCore encode = new JpegEncoderCore();
encode.Encode(stream, image, this.Quality);
}
}

40
src/ImageProcessorCore/Formats/Jpg/Encoder.cs → src/ImageProcessorCore/Formats/Jpg/JpegEncoderCore.cs

@ -3,7 +3,7 @@ namespace ImageProcessorCore.Formats
using System;
using System.IO;
internal partial class Encoder
internal class JpegEncoderCore
{
private const int sof0Marker = 0xc0; // Start Of Frame (Baseline).
private const int sof1Marker = 0xc1; // Start Of Frame (Extended Sequential).
@ -214,7 +214,7 @@ 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 w;
private Stream outputStream;
// buf is a scratch buffer.
private byte[] buf = new byte[16];
// bits and nBits are accumulated bits to write to w.
@ -228,7 +228,7 @@ namespace ImageProcessorCore.Formats
{
var data = new byte[1];
data[0] = b;
w.Write(data, 0, 1);
outputStream.Write(data, 0, 1);
}
// emit emits the least significant nBits bits of bits to the bit-stream.
@ -286,7 +286,7 @@ namespace ImageProcessorCore.Formats
buf[1] = marker;
buf[2] = (byte)(markerlen >> 8);
buf[3] = (byte)(markerlen & 0xff);
w.Write(buf, 0, 4);
outputStream.Write(buf, 0, 4);
}
// writeDQT writes the Define Quantization Table marker.
@ -297,7 +297,7 @@ namespace ImageProcessorCore.Formats
for (int i = 0; i < nQuantIndex; i++)
{
writeByte((byte)i);
w.Write(quant[i], 0, quant[i].Length);
outputStream.Write(quant[i], 0, quant[i].Length);
}
}
@ -332,7 +332,7 @@ namespace ImageProcessorCore.Formats
buf[3 * i + 8] = chroma2[i];
}
}
w.Write(buf, 0, 3 * (nComponent - 1) + 9);
outputStream.Write(buf, 0, 3 * (nComponent - 1) + 9);
}
// writeDHT writes the Define Huffman Table marker.
@ -359,8 +359,8 @@ namespace ImageProcessorCore.Formats
var s = specs[i];
writeByte(headers[i]);
w.Write(s.count, 0, s.count.Length);
w.Write(s.values, 0, s.values.Length);
outputStream.Write(s.count, 0, s.count.Length);
outputStream.Write(s.values, 0, s.values.Length);
}
}
@ -470,7 +470,7 @@ namespace ImageProcessorCore.Formats
// writeSOS writes the StartOfScan marker.
private void writeSOS(ImageBase m)
{
w.Write(sosHeaderYCbCr, 0, sosHeaderYCbCr.Length);
outputStream.Write(sosHeaderYCbCr, 0, sosHeaderYCbCr.Length);
Block b = new Block();
Block[] cb = new Block[4];
@ -505,18 +505,24 @@ namespace ImageProcessorCore.Formats
// 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 w, ImageBase m, int quality)
public void Encode(Stream stream, ImageBase m, int quality)
{
this.w = w;
this.outputStream = stream;
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 (m.Width >= (1 << 16) || m.Height >= (1 << 16))
throw new Exception("jpeg: image is too large to encode");
{
throw new ImageFormatException($"Image is too large to encode at {m.Width}x{m.Height}.");
}
if (quality < 1) quality = 1;
if (quality > 100) quality = 100;
@ -524,9 +530,13 @@ namespace ImageProcessorCore.Formats
// 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++)
@ -547,7 +557,7 @@ namespace ImageProcessorCore.Formats
// Write the Start Of Image marker.
buf[0] = 0xff;
buf[1] = 0xd8;
w.Write(buf, 0, 2);
stream.Write(buf, 0, 2);
// Write the quantization tables.
writeDQT();
@ -564,8 +574,8 @@ namespace ImageProcessorCore.Formats
// Write the End Of Image marker.
buf[0] = 0xff;
buf[1] = 0xd9;
w.Write(buf, 0, 2);
w.Flush();
stream.Write(buf, 0, 2);
stream.Flush();
}
// div returns a/b rounded to the nearest integer, instead of rounded to zero.
Loading…
Cancel
Save