diff --git a/src/ImageProcessorCore/Colors/ColorspaceTransforms.cs b/src/ImageProcessorCore/Colors/ColorspaceTransforms.cs index 4e339ebe3..50d0af315 100644 --- a/src/ImageProcessorCore/Colors/ColorspaceTransforms.cs +++ b/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); } diff --git a/src/ImageProcessorCore/Formats/Jpg/Block.cs b/src/ImageProcessorCore/Formats/Jpg/Block.cs index 3e5a4e534..d70674779 100644 --- a/src/ImageProcessorCore/Formats/Jpg/Block.cs +++ b/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; } + } } } diff --git a/src/ImageProcessorCore/Formats/Jpg/Colors.cs b/src/ImageProcessorCore/Formats/Jpg/Colors.cs deleted file mode 100644 index 696e3645e..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/Colors.cs +++ /dev/null @@ -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; - } - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/Decoder.cs b/src/ImageProcessorCore/Formats/Jpg/Decoder.cs index df37659f2..6612aff5e 100644 --- a/src/ImageProcessorCore/Formats/Jpg/Decoder.cs +++ b/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; diff --git a/src/ImageProcessorCore/Formats/Jpg/Encoder.cs b/src/ImageProcessorCore/Formats/Jpg/Encoder.cs index c9d0e6b41..fb655d9db 100644 --- a/src/ImageProcessorCore/Formats/Jpg/Encoder.cs +++ b/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; diff --git a/src/ImageProcessorCore/Formats/Jpg/FDCT.cs b/src/ImageProcessorCore/Formats/Jpg/FDCT.cs index db609cf26..1c8047ab9 100644 --- a/src/ImageProcessorCore/Formats/Jpg/FDCT.cs +++ b/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); } } } diff --git a/src/ImageProcessorCore/Formats/Jpg/IDCT.cs b/src/ImageProcessorCore/Formats/Jpg/IDCT.cs index 2550b74b3..a64a73a4f 100644 --- a/src/ImageProcessorCore/Formats/Jpg/IDCT.cs +++ b/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; } } } diff --git a/src/ImageProcessorCore/Formats/Jpg/JpegDecoder.cs b/src/ImageProcessorCore/Formats/Jpg/JpegDecoder.cs index 71e1cd016..9c909062a 100644 --- a/src/ImageProcessorCore/Formats/Jpg/JpegDecoder.cs +++ b/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; /// /// 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; } }); diff --git a/src/ImageProcessorCore/Formats/Jpg/JpegEncoder.cs b/src/ImageProcessorCore/Formats/Jpg/JpegEncoder.cs index f6b71c9b1..7f8d38be6 100644 --- a/src/ImageProcessorCore/Formats/Jpg/JpegEncoder.cs +++ b/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; /// /// Encoder for writing the data image to a stream in jpeg format. @@ -16,7 +14,7 @@ namespace ImageProcessorCore.Formats public class JpegEncoder : IImageEncoder { /// - /// The quality. + /// The quality used to encode the image. /// 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); } } diff --git a/src/ImageProcessorCore/Formats/Jpg/README.md b/src/ImageProcessorCore/Formats/Jpg/README.md new file mode 100644 index 000000000..54bc14847 --- /dev/null +++ b/src/ImageProcessorCore/Formats/Jpg/README.md @@ -0,0 +1,3 @@ +Encoder/Decoder adapted and extended from: + +https://golang.org/src/image/jpeg/ \ No newline at end of file