Browse Source

Use in built color transforms.

Plus some cleanup.


Former-commit-id: c5b1812f853366b9f5871d0a6f679637f3dfbcff
Former-commit-id: aabf62cb1f686ad62cf4691fd83375ce18ba474b
Former-commit-id: a5afb4446cde846ab68a36ad3a6c69d840fd8850
af/merge-core
James Jackson-South 10 years ago
parent
commit
eae5800b01
  1. 6
      src/ImageProcessorCore/Colors/ColorspaceTransforms.cs
  2. 11
      src/ImageProcessorCore/Formats/Jpg/Block.cs
  3. 45
      src/ImageProcessorCore/Formats/Jpg/Colors.cs
  4. 104
      src/ImageProcessorCore/Formats/Jpg/Decoder.cs
  5. 242
      src/ImageProcessorCore/Formats/Jpg/Encoder.cs
  6. 77
      src/ImageProcessorCore/Formats/Jpg/FDCT.cs
  7. 133
      src/ImageProcessorCore/Formats/Jpg/IDCT.cs
  8. 15
      src/ImageProcessorCore/Formats/Jpg/JpegDecoder.cs
  9. 6
      src/ImageProcessorCore/Formats/Jpg/JpegEncoder.cs
  10. 3
      src/ImageProcessorCore/Formats/Jpg/README.md

6
src/ImageProcessorCore/Colors/ColorspaceTransforms.cs

@ -60,9 +60,9 @@ namespace ImageProcessorCore
float cb = color.Cb - 128;
float cr = color.Cr - 128;
float r = (float)(y + (1.402 * cr)) / 255f;
float g = (float)(y - (0.34414 * cb) - (0.71414 * cr)) / 255f;
float b = (float)(y + (1.772 * cb)) / 255f;
float r = (float)(y + (1.402 * cr)).Clamp(0, 255) / 255f;
float g = (float)(y - (0.34414 * cb) - (0.71414 * cr)).Clamp(0, 255) / 255f;
float b = (float)(y + (1.772 * cb)).Clamp(0, 255) / 255f;
return new Color(r, g, b);
}

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

@ -1,8 +1,5 @@
namespace ImageProcessorCore.Formats.Jpg
namespace ImageProcessorCore.Formats
{
using System;
using System.IO;
internal class Block
{
public const int blockSize = 64;
@ -13,6 +10,10 @@ namespace ImageProcessorCore.Formats.Jpg
_data = new int[blockSize];
}
public int this[int idx] { get { return _data[idx]; } set { _data[idx] = value; } }
public int this[int idx]
{
get { return _data[idx]; }
set { _data[idx] = value; }
}
}
}

45
src/ImageProcessorCore/Formats/Jpg/Colors.cs

@ -1,45 +0,0 @@
namespace ImageProcessorCore.Formats.Jpg
{
using System;
using System.IO;
internal static class Colors
{
public static void RGBToYCbCr(byte r, byte g, byte b, out byte yy, out byte cb, out byte cr)
{
// The JFIF specification says:
// Y' = 0.2990*R + 0.5870*G + 0.1140*B
// Cb = -0.1687*R - 0.3313*G + 0.5000*B + 128
// Cr = 0.5000*R - 0.4187*G - 0.0813*B + 128
// http://www.w3.org/Graphics/JPEG/jfif3.pdf says Y but means Y'.
int iyy = (19595*r + 38470*g + 7471*b + (1<<15)) >> 16;
int icb = (-11056*r - 21712*g + 32768*b + (257<<15)) >> 16;
int icr = (32768*r - 27440*g - 5328*b + (257<<15)) >> 16;
if (iyy < 0) yy = 0; else if (iyy > 255) yy = 255; else yy = (byte)iyy;
if (icb < 0) cb = 0; else if (icb > 255) cb = 255; else cb = (byte)icb;
if (icr < 0) cr = 0; else if (icr > 255) cr = 255; else cr = (byte)icr;
}
public static void YCbCrToRGB(byte yy, byte cb, byte cr, out byte r, out byte g, out byte b)
{
// The JFIF specification says:
// R = Y' + 1.40200*(Cr-128)
// G = Y' - 0.34414*(Cb-128) - 0.71414*(Cr-128)
// B = Y' + 1.77200*(Cb-128)
// http://www.w3.org/Graphics/JPEG/jfif3.pdf says Y but means Y'.
int yy1 = yy * 0x10100; // Convert 0x12 to 0x121200.
int cb1 = cb - 128;
int cr1 = cr - 128;
int ir = (yy1 + 91881*cr1) >> 16;
int ig = (yy1 - 22554*cb1 - 46802*cr1) >> 16;
int ib = (yy1 + 116130*cb1) >> 16;
if (ir < 0) r = 0; else if (ir > 255) r = 255; else r = (byte)ir;
if (ig < 0) g = 0; else if (ig > 255) g = 255; else g = (byte)ig;
if (ib < 0) b = 0; else if (ib > 255) b = 255; else b = (byte)ib;
}
}
}

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

@ -1,10 +1,10 @@
namespace ImageProcessorCore.Formats.Jpg
namespace ImageProcessorCore.Formats
{
using System;
using System.IO;
using System.Threading.Tasks;
internal partial class Decoder
internal class Decoder
{
private class errMissingFF00 : Exception { }
private class errShortHuffmanData : Exception { }
@ -203,7 +203,7 @@ namespace ImageProcessorCore.Formats.Jpg
public class img_rgb
{
public byte[] pixels;
public float[] pixels;
public int stride;
public int w, h;
@ -215,7 +215,7 @@ namespace ImageProcessorCore.Formats.Jpg
{
this.w = w;
this.h = h;
this.pixels = new byte[w * h * 3];
this.pixels = new float[w * h * 3];
this.stride = w * 3;
}
@ -1062,11 +1062,20 @@ namespace ImageProcessorCore.Formats.Jpg
}
if (img1 != null)
{
return;
}
else if (img3 != null)
{
if (comp[0].c == 'R' && comp[1].c == 'G' && comp[2].c == 'B') imgrgb = convert_direct_to_rgb(width, height);
else imgrgb = convert_to_rgb(width, height);
if (comp[0].c == 'R' && comp[1].c == 'G' && comp[2].c == 'B')
{
imgrgb = convert_direct_to_rgb(width, height);
}
else
{
imgrgb = convert_to_rgb(width, height);
}
}
else
{
@ -1074,41 +1083,41 @@ namespace ImageProcessorCore.Formats.Jpg
}
}
private img_rgb convert_to_rgb(int w, int h)
private img_rgb convert_to_rgb(int weight, int height)
{
var ret = new img_rgb(w, h);
img_rgb ret = new img_rgb(weight, height);
int cScale = comp[0].h / comp[1].h;
Parallel.For(
0,
h,
height,
y =>
{
int po = ret.get_row_offset(y);
int yo = img3.get_row_y_offset(y);
int co = img3.get_row_c_offset(y);
for (int x = 0; x < w; x++)
for (int x = 0; x < weight; x++)
{
byte yy = img3.pix_y[yo + x];
byte cb = img3.pix_cb[co + x / cScale];
byte cr = img3.pix_cr[co + x / cScale];
int index = po + (3 * x);
byte r, g, b;
Colors.YCbCrToRGB(yy, cb, cr, out r, out g, out b);
ret.pixels[po + 3 * x + 0] = r;
ret.pixels[po + 3 * x + 1] = g;
ret.pixels[po + 3 * x + 2] = b;
// Implicit casting FTW
Color color = new YCbCr(yy, cb, cr);
ret.pixels[index] = color.R;
ret.pixels[index + 1] = color.G;
ret.pixels[index + 2] = color.B;
}
}
);
});
return ret;
}
private img_rgb convert_direct_to_rgb(int w, int h)
{
var ret = new img_rgb(w, h);
img_rgb ret = new img_rgb(w, h);
int cScale = comp[0].h / comp[1].h;
for (var y = 0; y < h; y++)
@ -1116,11 +1125,17 @@ namespace ImageProcessorCore.Formats.Jpg
int po = ret.get_row_offset(y);
int yo = img3.get_row_y_offset(y);
int co = img3.get_row_c_offset(y);
for (int x = 0; x < w; x++)
{
ret.pixels[po + 3 * x + 0] = img3.pix_y[yo + x];
ret.pixels[po + 3 * x + 1] = img3.pix_cb[co + x / cScale];
ret.pixels[po + 3 * x + 2] = img3.pix_cr[co + x / cScale];
byte red = img3.pix_y[yo + x];
byte green = img3.pix_cb[co + x / cScale];
byte blue = img3.pix_cr[co + x / cScale];
int index = po + (3 * x);
Color color = new Bgra32(red, green, blue);
ret.pixels[index] = color.R;
ret.pixels[index + 1] = color.G;
ret.pixels[index + 2] = color.B;
}
}
@ -1138,32 +1153,46 @@ namespace ImageProcessorCore.Formats.Jpg
void processSOS(int n)
{
if (nComp == 0)
throw new Exception("missing SOF marker");
{
throw new ImageFormatException("missing SOF marker");
}
if (n < 6 || 4 + 2 * nComp < n || n % 2 != 0)
throw new Exception("SOS has wrong length");
{
throw new ImageFormatException("SOS has wrong length");
}
readFull(tmp, 0, n);
nComp = tmp[0];
if (n != 4 + 2 * nComp)
throw new Exception("SOS length inconsistent with number of components");
{
throw new ImageFormatException("SOS length inconsistent with number of components");
}
var scan = new scan_scruct[maxComponents];
int totalHV = 0;
for (int i = 0; i < nComp; i++)
{
int cs = tmp[1 + 2 * i]; // Component selector.
// Component selector.
int cs = tmp[1 + (2 * i)];
int compIndex = -1;
for (int j = 0; j < nComp; j++)
{
var compv = comp[j];
if (cs == compv.c)
{
compIndex = j;
}
}
if (compIndex < 0)
throw new Exception("unknown component selector");
{
throw new ImageFormatException("Unknown component selector");
}
scan[i].compIndex = (byte)compIndex;
// Section B.2.3 states that "the value of Cs_j shall be different from
// the values of Cs_1 through Cs_(j-1)". Since we have previously
// verified that a frame's component identifiers (C_i values in section
@ -1172,24 +1201,32 @@ namespace ImageProcessorCore.Formats.Jpg
for (int j = 0; j < i; j++)
{
if (scan[i].compIndex == scan[j].compIndex)
{
throw new Exception("repeated component selector");
}
}
totalHV += comp[compIndex].h * comp[compIndex].v;
scan[i].td = (byte)(tmp[2 + 2 * i] >> 4);
if (scan[i].td > maxTh)
throw new Exception("bad Td value");
{
throw new ImageFormatException("bad Td value");
}
scan[i].ta = (byte)(tmp[2 + 2 * i] & 0x0f);
if (scan[i].ta > maxTh)
throw new Exception("bad Ta value");
{
throw new ImageFormatException("bad Ta value");
}
}
// Section B.2.3 states that if there is more than one component then the
// total H*V values in a scan must be <= 10.
if (nComp > 1 && totalHV > 10)
throw new Exception("total sampling factors too large");
{
throw new ImageFormatException("Total sampling factors too large.");
}
// zigStart and zigEnd are the spectral selection bounds.
// ah and al are the successive approximation high and low values.
@ -1233,7 +1270,9 @@ namespace ImageProcessorCore.Formats.Jpg
int myy = (height + 8 * v0 - 1) / (8 * v0);
if (img1 == null && img3 == null)
{
makeImg(mxx, myy);
}
if (progressive)
{
@ -1258,6 +1297,7 @@ namespace ImageProcessorCore.Formats.Jpg
// b is the decoded coefficients, in natural (not zig-zag) order.
Block b = new Block();
int[] dc = new int[maxComponents];
// bx and by are the location of the current block, in units of 8x8
// blocks: the third block in the first row has (bx, by) = (2, 0).
int bx, by, blockCount = 0;
@ -1369,8 +1409,7 @@ namespace ImageProcessorCore.Formats.Jpg
eobRun = (ushort)(1 << val0);
if (val0 != 0)
{
uint bits = decodeBits(val0);
eobRun |= (ushort)(bits);
eobRun |= (ushort)decodeBits(val0);
}
eobRun--;
break;
@ -1387,6 +1426,7 @@ namespace ImageProcessorCore.Formats.Jpg
{
// We haven't completely decoded this 8x8 block. Save the coefficients.
progCoeffs[compIndex][by * mxx * hi + bx] = b;
// At this point, we could execute the rest of the loop body to dequantize and
// perform the inverse DCT, to save early stages of a progressive image to the
// *image.YCbCr buffers (the whole point of progressive encoding), but in Go,
@ -1400,7 +1440,7 @@ namespace ImageProcessorCore.Formats.Jpg
for (int zig = 0; zig < Block.blockSize; zig++)
b[unzig[zig]] *= qt[zig];
IDCT(b);
IDCT.Transform(b);
byte[] dst = null;
int offset = 0;

242
src/ImageProcessorCore/Formats/Jpg/Encoder.cs

@ -1,4 +1,4 @@
namespace ImageProcessorCore.Formats.Jpg
namespace ImageProcessorCore.Formats
{
using System;
using System.IO;
@ -8,19 +8,19 @@ namespace ImageProcessorCore.Formats.Jpg
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 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.
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 app0Marker = 0xe0;
private const int app14Marker = 0xee;
private const int app15Marker = 0xef;
@ -108,73 +108,71 @@ namespace ImageProcessorCore.Formats.Jpg
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[] {
// 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 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 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 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}
),
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 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,
}
),
new byte[] { 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.
@ -195,14 +193,14 @@ namespace ImageProcessorCore.Formats.Jpg
maxValue = v;
}
values = new uint[maxValue+1];
values = new uint[maxValue + 1];
int code = 0;
int k = 0;
for (int i = 0; i < s.count.Length; i++)
{
int nBits = (i+1) << 24;
int nBits = (i + 1) << 24;
for (int j = 0; j < s.count[i]; j++)
{
values[s.values[k]] = (uint)(nBits | code);
@ -257,7 +255,7 @@ namespace ImageProcessorCore.Formats.Jpg
private void emitHuff(huffIndex h, int v)
{
uint x = theHuffmanLUT[(int)h].values[v];
emit(x&((1<<24)-1), x>>24);
emit(x & ((1 << 24) - 1), x >> 24);
}
// emitHuffRLE emits a run of runLength copies of value encoded with the given
@ -269,17 +267,16 @@ namespace ImageProcessorCore.Formats.Jpg
if (a < 0)
{
a = -v;
b = v-1;
b = v - 1;
}
uint nBits = 0;
if (a < 0x100)
nBits = bitCount[a];
else
nBits = 8 + (uint)bitCount[a>>8];
nBits = 8 + (uint)bitCount[a >> 8];
emitHuff(h, (int)((runLength<<4)|nBits));
if (nBits > 0)
emit((uint)b & (uint)((1 << ((int)nBits)) - 1), nBits);
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.
@ -295,10 +292,10 @@ namespace ImageProcessorCore.Formats.Jpg
// 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++)
{
{
writeByte((byte)i);
w.Write(quant[i], 0, quant[i].Length);
}
@ -310,7 +307,7 @@ namespace ImageProcessorCore.Formats.Jpg
byte[] chroma1 = new byte[] { 0x22, 0x11, 0x11 };
byte[] chroma2 = new byte[] { 0x00, 0x01, 0x01 };
int markerlen = 8 + 3*nComponent;
int markerlen = 8 + 3 * nComponent;
writeMarkerHeader(sof0Marker, markerlen);
buf[0] = 8; // 8-bit color.
buf[1] = (byte)(hei >> 8);
@ -329,13 +326,13 @@ namespace ImageProcessorCore.Formats.Jpg
{
for (int i = 0; i < nComponent; i++)
{
buf[3*i+6] = (byte)(i + 1);
buf[3 * i + 6] = (byte)(i + 1);
// We use 4:2:0 chroma subsampling.
buf[3*i+7] = chroma1[i];
buf[3*i+8] = chroma2[i];
buf[3 * i + 7] = chroma1[i];
buf[3 * i + 8] = chroma2[i];
}
}
w.Write(buf, 0, 3*(nComponent-1)+9);
w.Write(buf, 0, 3 * (nComponent - 1) + 9);
}
// writeDHT writes the Define Huffman Table marker.
@ -352,7 +349,9 @@ namespace ImageProcessorCore.Formats.Jpg
}
foreach (var s in specs)
{
markerlen += 1 + 16 + s.values.Length;
}
writeMarkerHeader(dhtMarker, markerlen);
for (int i = 0; i < specs.Length; i++)
@ -370,19 +369,19 @@ namespace ImageProcessorCore.Formats.Jpg
// natural (not zig-zag) order.
private int writeBlock(Block b, quantIndex q, int prevDC)
{
FDCT(b);
FDCT.Transform(b);
// 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 = div(b[0], 8 * quant[(int)q][0]);
emitHuffRLE((huffIndex)(2 * (int)q + 0), 0, dc - prevDC);
// Emit the AC components.
var h = (huffIndex)(2*(int)q+1);
var h = (huffIndex)(2 * (int)q + 1);
int runLength = 0;
for (int zig = 1; zig < Block.blockSize; zig++)
{
int ac = div(b[unzig[zig]], 8*quant[(int)q][zig]);
int ac = div(b[unzig[zig]], 8 * quant[(int)q][zig]);
if (ac == 0)
{
@ -415,70 +414,29 @@ namespace ImageProcessorCore.Formats.Jpg
{
for (int i = 0; i < 8; i++)
{
byte yy, cb, cr;
var c = m[Math.Min(x+i, xmax), Math.Min(y+j, ymax)];
Colors.RGBToYCbCr((byte)(c.R*255), (byte)(c.G*255), (byte)(c.B*255), out yy, out cb, out cr);
yBlock[8*j+i] = yy;
cbBlock[8*j+i] = cb;
crBlock[8*j+i] = cr;
}
}
}
// grayToY stores the 8x8 region of m whose top-left corner is p in yBlock.
/*func grayToY(m *image.Gray, p image.Point, yBlock *block) {
b := m.Bounds()
xmax := b.Max.X - 1
ymax := b.Max.Y - 1
pix := m.Pix
for j := 0; j < 8; j++ {
for i := 0; i < 8; i++ {
idx := m.PixOffset(min(p.X+i, xmax), min(p.Y+j, ymax))
yBlock[8*j+i] = int32(pix[idx])
YCbCr color = m[Math.Min(x + i, xmax), Math.Min(y + j, ymax)];
int index = (8 * j) + i;
yBlock[index] = (int)color.Y;
cbBlock[index] = (int)color.Cb;
crBlock[index] = (int)color.Cr;
}
}
}
// rgbaToYCbCr is a specialized version of toYCbCr for image.RGBA images.
func rgbaToYCbCr(m *image.RGBA, p image.Point, yBlock, cbBlock, crBlock *block) {
b := m.Bounds()
xmax := b.Max.X - 1
ymax := b.Max.Y - 1
for j := 0; j < 8; j++ {
sj := p.Y + j
if sj > ymax {
sj = ymax
}
offset := (sj-b.Min.Y)*m.Stride - b.Min.X*4
for i := 0; i < 8; i++ {
sx := p.X + i
if sx > xmax {
sx = xmax
}
pix := m.Pix[offset+sx*4:]
yy, cb, cr := color.RGBToYCbCr(pix[0], pix[1], pix[2])
yBlock[8*j+i] = int32(yy)
cbBlock[8*j+i] = int32(cb)
crBlock[8*j+i] = int32(cr)
}
}
}*/
// scale scales the 16x16 region represented by the 4 src blocks to the 8x8
// dst block.
private void scale(Block dst, Block[] src)
{
for (int i = 0; i < 4; i++)
{
int dstOff = ((i&2)<<4) | ((i&1)<<2);
int dstOff = ((i & 2) << 4) | ((i & 1) << 2);
for (int y = 0; y < 4; y++)
{
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) >> 2;
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) >> 2;
}
}
}
@ -531,7 +489,7 @@ namespace ImageProcessorCore.Formats.Jpg
int xOff = (i & 1) * 8;
int yOff = (i & 2) * 4;
toYCbCr(m, x+xOff, y+yOff, b, cb[i], cr[i]);
toYCbCr(m, x + xOff, y + yOff, b, cb[i], cr[i]);
prevDCY = writeBlock(b, 0, prevDCY);
}
scale(b, cb);
@ -557,7 +515,7 @@ namespace ImageProcessorCore.Formats.Jpg
for (int i = 0; i < nQuantIndex; i++)
quant[i] = new byte[Block.blockSize];
if (m.Width >= (1<<16) || m.Height >= (1<<16))
if (m.Width >= (1 << 16) || m.Height >= (1 << 16))
throw new Exception("jpeg: image is too large to encode");
if (quality < 1) quality = 1;
@ -568,15 +526,15 @@ namespace ImageProcessorCore.Formats.Jpg
if (quality < 50)
scale = 5000 / quality;
else
scale = 200 - quality*2;
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;
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;

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

@ -1,9 +1,6 @@
namespace ImageProcessorCore.Formats.Jpg
namespace ImageProcessorCore.Formats
{
using System;
using System.IO;
internal partial class Encoder
internal class FDCT
{
// Trigonometric constants in 13-bit fixed point format.
private const int fix_0_298631336 = 2446;
@ -24,19 +21,21 @@ namespace ImageProcessorCore.Formats.Jpg
// fdct performs a forward DCT on an 8x8 block of coefficients, including a
// level shift.
private static void FDCT(Block b)
public static void Transform(Block b)
{
// Pass 1: process rows.
for (int y = 0; y < 8; y++)
{
int x0 = b[y*8+0];
int x1 = b[y*8+1];
int x2 = b[y*8+2];
int x3 = b[y*8+3];
int x4 = b[y*8+4];
int x5 = b[y*8+5];
int x6 = b[y*8+6];
int x7 = b[y*8+7];
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 tmp0 = x0 + x7;
int tmp1 = x1 + x6;
@ -53,12 +52,12 @@ namespace ImageProcessorCore.Formats.Jpg
tmp2 = x2 - x5;
tmp3 = x3 - x4;
b[y*8+0] = (tmp10 + tmp11 - 8*centerJSample) << pass1Bits;
b[y*8+4] = (tmp10 - tmp11) << pass1Bits;
b[y8] = (tmp10 + tmp11 - 8 * centerJSample) << pass1Bits;
b[y8 + 4] = (tmp10 - tmp11) << pass1Bits;
int z1 = (tmp12 + tmp13) * fix_0_541196100;
z1 += 1 << (constBits - pass1Bits - 1);
b[y*8+2] = (z1 + tmp12*fix_0_765366865) >> (constBits - pass1Bits);
b[y*8+6] = (z1 - tmp13*fix_1_847759065) >> (constBits - pass1Bits);
b[y8 + 2] = (z1 + tmp12 * fix_0_765366865) >> (constBits - pass1Bits);
b[y8 + 6] = (z1 - tmp13 * fix_1_847759065) >> (constBits - pass1Bits);
tmp10 = tmp0 + tmp3;
tmp11 = tmp1 + tmp2;
@ -77,38 +76,38 @@ namespace ImageProcessorCore.Formats.Jpg
tmp12 += z1;
tmp13 += z1;
b[y*8+1] = (tmp0 + tmp10 + tmp12) >> (constBits - pass1Bits);
b[y*8+3] = (tmp1 + tmp11 + tmp13) >> (constBits - pass1Bits);
b[y*8+5] = (tmp2 + tmp11 + tmp12) >> (constBits - pass1Bits);
b[y*8+7] = (tmp3 + tmp10 + tmp13) >> (constBits - pass1Bits);
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);
}
// 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[0*8+x] + b[7*8+x];
int tmp1 = b[1*8+x] + b[6*8+x];
int tmp2 = b[2*8+x] + b[5*8+x];
int tmp3 = b[3*8+x] + b[4*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 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[0*8+x] - b[7*8+x];
tmp1 = b[1*8+x] - b[6*8+x];
tmp2 = b[2*8+x] - b[5*8+x];
tmp3 = b[3*8+x] - b[4*8+x];
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];
b[0*8+x] = (tmp10 + tmp11) >> pass1Bits;
b[4*8+x] = (tmp10 - tmp11) >> pass1Bits;
b[x] = (tmp10 + tmp11) >> pass1Bits;
b[32 + x] = (tmp10 - tmp11) >> pass1Bits;
int z1 = (tmp12 + tmp13) * fix_0_541196100;
z1 += 1 << (constBits + pass1Bits - 1);
b[2*8+x] = (z1 + tmp12*fix_0_765366865) >> (constBits + pass1Bits);
b[6*8+x] = (z1 - tmp13*fix_1_847759065) >> (constBits + pass1Bits);
b[16 + x] = (z1 + tmp12 * fix_0_765366865) >> (constBits + pass1Bits);
b[48 + x] = (z1 - tmp13 * fix_1_847759065) >> (constBits + pass1Bits);
tmp10 = tmp0 + tmp3;
tmp11 = tmp1 + tmp2;
@ -127,10 +126,10 @@ namespace ImageProcessorCore.Formats.Jpg
tmp12 += z1;
tmp13 += z1;
b[1*8+x] = (tmp0 + tmp10 + tmp12) >> (constBits + pass1Bits);
b[3*8+x] = (tmp1 + tmp11 + tmp13) >> (constBits + pass1Bits);
b[5*8+x] = (tmp2 + tmp11 + tmp12) >> (constBits + pass1Bits);
b[7*8+x] = (tmp3 + tmp10 + tmp13) >> (constBits + pass1Bits);
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);
}
}
}

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

@ -1,9 +1,6 @@
namespace ImageProcessorCore.Formats.Jpg
namespace ImageProcessorCore.Formats
{
using System;
using System.IO;
internal partial class Decoder
internal class IDCT
{
private const int w1 = 2841; // 2048*sqrt(2)*cos(1*pi/16)
private const int w2 = 2676; // 2048*sqrt(2)*cos(2*pi/16)
@ -31,7 +28,7 @@ namespace ImageProcessorCore.Formats.Jpg
// For more on the actual algorithm, see Z. Wang, "Fast algorithms for the
// discrete W transform and for the discrete Fourier transform", IEEE Trans. on
// ASSP, Vol. ASSP- 32, pp. 803-816, Aug. 1984.
private static void IDCT(Block src)
public static void Transform(Block src)
{
// Horizontal 1-D IDCT.
for (int y = 0; y < 8; y++)
@ -39,45 +36,45 @@ namespace ImageProcessorCore.Formats.Jpg
int y8 = y * 8;
// If all the AC components are zero, then the IDCT is trivial.
if (src[y8+1] == 0 && src[y8+2] == 0 && src[y8+3] == 0 &&
src[y8+4] == 0 && src[y8+5] == 0 && src[y8+6] == 0 && src[y8+7] == 0)
if (src[y8 + 1] == 0 && src[y8 + 2] == 0 && src[y8 + 3] == 0 &&
src[y8 + 4] == 0 && src[y8 + 5] == 0 && src[y8 + 6] == 0 && src[y8 + 7] == 0)
{
int dc = src[y8+0] << 3;
src[y8+0] = dc;
src[y8+1] = dc;
src[y8+2] = dc;
src[y8+3] = dc;
src[y8+4] = dc;
src[y8+5] = dc;
src[y8+6] = dc;
src[y8+7] = dc;
int dc = src[y8 + 0] << 3;
src[y8 + 0] = dc;
src[y8 + 1] = dc;
src[y8 + 2] = dc;
src[y8 + 3] = dc;
src[y8 + 4] = dc;
src[y8 + 5] = dc;
src[y8 + 6] = dc;
src[y8 + 7] = dc;
continue;
}
// Prescale.
int x0 = (src[y8+0] << 11) + 128;
int x1 = src[y8+4] << 11;
int x2 = src[y8+6];
int x3 = src[y8+2];
int x4 = src[y8+1];
int x5 = src[y8+7];
int x6 = src[y8+5];
int x7 = src[y8+3];
int x0 = (src[y8 + 0] << 11) + 128;
int x1 = src[y8 + 4] << 11;
int x2 = src[y8 + 6];
int x3 = src[y8 + 2];
int x4 = src[y8 + 1];
int x5 = src[y8 + 7];
int x6 = src[y8 + 5];
int x7 = src[y8 + 3];
// Stage 1.
int x8 = w7 * (x4 + x5);
x4 = x8 + w1mw7*x4;
x5 = x8 - w1pw7*x5;
x4 = x8 + w1mw7 * x4;
x5 = x8 - w1pw7 * x5;
x8 = w3 * (x6 + x7);
x6 = x8 - w3mw5*x6;
x7 = x8 - w3pw5*x7;
x6 = x8 - w3mw5 * x6;
x7 = x8 - w3pw5 * x7;
// Stage 2.
x8 = x0 + x1;
x0 -= x1;
x1 = w6 * (x3 + x2);
x2 = x1 - w2pw6*x2;
x3 = x1 + w2mw6*x3;
x2 = x1 - w2pw6 * x2;
x3 = x1 + w2mw6 * x3;
x1 = x4 + x6;
x4 -= x6;
x6 = x5 + x7;
@ -88,18 +85,18 @@ namespace ImageProcessorCore.Formats.Jpg
x8 -= x3;
x3 = x0 + x2;
x0 -= x2;
x2 = (r2*(x4+x5) + 128) >> 8;
x4 = (r2*(x4-x5) + 128) >> 8;
x2 = (r2 * (x4 + x5) + 128) >> 8;
x4 = (r2 * (x4 - x5) + 128) >> 8;
// Stage 4.
src[y8+0] = (x7 + x1) >> 8;
src[y8+1] = (x3 + x2) >> 8;
src[y8+2] = (x0 + x4) >> 8;
src[y8+3] = (x8 + x6) >> 8;
src[y8+4] = (x8 - x6) >> 8;
src[y8+5] = (x0 - x4) >> 8;
src[y8+6] = (x3 - x2) >> 8;
src[y8+7] = (x7 - x1) >> 8;
src[y8 + 0] = (x7 + x1) >> 8;
src[y8 + 1] = (x3 + x2) >> 8;
src[y8 + 2] = (x0 + x4) >> 8;
src[y8 + 3] = (x8 + x6) >> 8;
src[y8 + 4] = (x8 - x6) >> 8;
src[y8 + 5] = (x0 - x4) >> 8;
src[y8 + 6] = (x3 - x2) >> 8;
src[y8 + 7] = (x7 - x1) >> 8;
}
// Vertical 1-D IDCT.
@ -110,29 +107,29 @@ namespace ImageProcessorCore.Formats.Jpg
// 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[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];
// Stage 1.
int y8 = w7*(y4+y5) + 4;
y4 = (y8 + w1mw7*y4) >> 3;
y5 = (y8 - w1pw7*y5) >> 3;
y8 = w3*(y6+y7) + 4;
y6 = (y8 - w3mw5*y6) >> 3;
y7 = (y8 - w3pw5*y7) >> 3;
int y8 = w7 * (y4 + y5) + 4;
y4 = (y8 + w1mw7 * y4) >> 3;
y5 = (y8 - w1pw7 * y5) >> 3;
y8 = w3 * (y6 + y7) + 4;
y6 = (y8 - w3mw5 * y6) >> 3;
y7 = (y8 - w3pw5 * y7) >> 3;
// Stage 2.
y8 = y0 + y1;
y0 -= y1;
y1 = w6*(y3+y2) + 4;
y2 = (y1 - w2pw6*y2) >> 3;
y3 = (y1 + w2mw6*y3) >> 3;
y1 = w6 * (y3 + y2) + 4;
y2 = (y1 - w2pw6 * y2) >> 3;
y3 = (y1 + w2mw6 * y3) >> 3;
y1 = y4 + y6;
y4 -= y6;
y6 = y5 + y7;
@ -143,18 +140,18 @@ namespace ImageProcessorCore.Formats.Jpg
y8 -= y3;
y3 = y0 + y2;
y0 -= y2;
y2 = (r2*(y4+y5) + 128) >> 8;
y4 = (r2*(y4-y5) + 128) >> 8;
y2 = (r2 * (y4 + y5) + 128) >> 8;
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[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;
}
}
}

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

@ -8,7 +8,7 @@ namespace ImageProcessorCore.Formats
using System;
using System.IO;
using System.Threading.Tasks;
using ImageProcessorCore.Formats.Jpg;
using ImageProcessorCore.Formats;
/// <summary>
/// Image decoder for generating an image out of a jpg stream.
@ -115,9 +115,9 @@ namespace ImageProcessorCore.Formats
{
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 + 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;
}
});
@ -133,10 +133,11 @@ namespace ImageProcessorCore.Formats
for (int x = 0; x < pixelWidth; x++)
{
int offset = ((y * pixelWidth) + x) * 4;
int sourceOffset = yoff + (3 * x);
pixels[offset + 0] = decoder.imgrgb.pixels[yoff+3*x+0] / 255f;
pixels[offset + 1] = decoder.imgrgb.pixels[yoff+3*x+1] / 255f;
pixels[offset + 2] = decoder.imgrgb.pixels[yoff+3*x+2] / 255f;
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;
}
});

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

@ -7,8 +7,6 @@ namespace ImageProcessorCore.Formats
{
using System;
using System.IO;
using System.Threading.Tasks;
using ImageProcessorCore.Formats.Jpg;
/// <summary>
/// Encoder for writing the data image to a stream in jpeg format.
@ -16,7 +14,7 @@ namespace ImageProcessorCore.Formats
public class JpegEncoder : IImageEncoder
{
/// <summary>
/// The quality.
/// The quality used to encode the image.
/// </summary>
private int quality = 75;
@ -58,7 +56,7 @@ namespace ImageProcessorCore.Formats
Guard.NotNull(image, nameof(image));
Guard.NotNull(stream, nameof(stream));
var encode = new Encoder();
Encoder encode = new Encoder();
encode.Encode(stream, image, this.Quality);
}
}

3
src/ImageProcessorCore/Formats/Jpg/README.md

@ -0,0 +1,3 @@
Encoder/Decoder adapted and extended from:
https://golang.org/src/image/jpeg/
Loading…
Cancel
Save