diff --git a/src/ImageProcessorCore/Formats/Jpg/Colors.cs b/src/ImageProcessorCore/Formats/Jpg/Colors.cs index 0c00a51bc2..de936268a9 100644 --- a/src/ImageProcessorCore/Formats/Jpg/Colors.cs +++ b/src/ImageProcessorCore/Formats/Jpg/Colors.cs @@ -5,7 +5,7 @@ namespace ImageProcessorCore.Formats.Jpg public static class Colors { - public static void RGBToYCbCr(byte r, byte g, byte b, out byte yy, out byte cr, out byte cb) + 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 @@ -13,9 +13,9 @@ namespace ImageProcessorCore.Formats.Jpg // 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; + 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; diff --git a/src/ImageProcessorCore/Formats/Jpg/Encoder.cs b/src/ImageProcessorCore/Formats/Jpg/Encoder.cs new file mode 100644 index 0000000000..49fe2c8633 --- /dev/null +++ b/src/ImageProcessorCore/Formats/Jpg/Encoder.cs @@ -0,0 +1,623 @@ +namespace ImageProcessorCore.Formats.Jpg +{ + using System; + using System.IO; + + public partial class Encoder + { + 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 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. + // "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 app14Marker = 0xee; + private const int app15Marker = 0xef; + + // bitCount counts the number of bits needed to hold an integer. + private readonly byte[] bitCount = { + 0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, }; + + // unzig maps from the zig-zag ordering to the natural ordering. For example, + // unzig[3] is the column and row of the fourth element in zig-zag order. The + // value is 16, which means first column (16%8 == 0) and third row (16/8 == 2). + private static readonly int[] unzig = new int[] { + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63, + }; + + private const int nQuantIndex = 2; + private const int nHuffIndex = 4; + + private enum quantIndex + { + quantIndexLuminance = 0, + quantIndexChrominance = 1, + } + + private enum huffIndex + { + huffIndexLuminanceDC = 0, + huffIndexLuminanceAC = 1, + huffIndexChrominanceDC = 2, + huffIndexChrominanceAC = 3, + } + + // unscaledQuant are the unscaled quantization tables in zig-zag order. Each + // encoder copies and scales the tables according to its quality parameter. + // The values are derived from section K.1 after converting from natural to + // zig-zag order. + private byte[,] unscaledQuant = new byte[,] { + // Luminance. + { + 16, 11, 12, 14, 12, 10, 16, 14, + 13, 14, 18, 17, 16, 19, 24, 40, + 26, 24, 22, 22, 24, 49, 35, 37, + 29, 40, 58, 51, 61, 60, 57, 51, + 56, 55, 64, 72, 92, 78, 64, 68, + 87, 69, 55, 56, 80, 109, 81, 87, + 95, 98, 103, 104, 103, 62, 77, 113, + 121, 112, 100, 120, 92, 101, 103, 99, + }, + // Chrominance. + { + 17, 18, 18, 24, 21, 24, 47, 26, + 26, 47, 99, 66, 56, 66, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + }, + }; + + private class huffmanSpec + { + 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 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 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} + ), + // 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, + } + ), + }; + + // huffmanLUT is a compiled look-up table representation of a huffmanSpec. + // Each value maps to a uint32 of which the 8 most significant bits hold the + // codeword size in bits and the 24 least significant bits hold the codeword. + // The maximum codeword size is 16 bits. + private class huffmanLUT + { + public uint[] values; + + public huffmanLUT(huffmanSpec s) + { + int maxValue = 0; + + foreach(var v in s.values) + { + if(v > maxValue) + maxValue = v; + } + + 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; + for(int j = 0; j < s.count[i]; j++) + { + values[s.values[k]] = (uint)(nBits | code); + code++; + k++; + } + code <<= 1; + } + } + } + + // 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; + // buf is a scratch buffer. + private byte[] buf = new byte[16]; + // bits and nBits are accumulated bits to write to w. + private uint bits, nBits; + // quant is the scaled quantization tables, in zig-zag order. + private byte[][] quant = new byte[nQuantIndex][];//[Block.blockSize]; + // theHuffmanLUT are compiled representations of theHuffmanSpec. + private huffmanLUT[] theHuffmanLUT = new huffmanLUT[4]; + + private void writeByte(byte b) + { + var data = new byte[1]; + data[0] = b; + w.Write(data, 0, 1); + } + + // emit emits the least significant nBits bits of bits to the bit-stream. + // The precondition is bits < 1<= 8) + { + byte b = (byte)(bits >> 24); + writeByte(b); + if(b == 0xff) + writeByte(0x00); + bits <<= 8; + nBits -= 8; + } + this.bits = bits; + this.nBits = nBits; + } + + // emitHuff emits the given value with the given Huffman encoder. + private void emitHuff(huffIndex h, int v) + { + uint x = theHuffmanLUT[(int)h].values[v]; + emit(x&((1<<24)-1), x>>24); + } + + // emitHuffRLE emits a run of runLength copies of value encoded with the given + // Huffman encoder. + private void emitHuffRLE(huffIndex h, int runLength, int v) + { + int a = v; + int b = v; + if(a < 0) + { + a = -v; + b = v-1; + } + uint nBits = 0; + if(a < 0x100) + nBits = bitCount[a]; + else + nBits = 8 + (uint)bitCount[a>>8]; + + emitHuff(h, (int)((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. + private void writeMarkerHeader(byte marker, int markerlen) + { + buf[0] = 0xff; + buf[1] = marker; + buf[2] = (byte)(markerlen >> 8); + buf[3] = (byte)(markerlen & 0xff); + w.Write(buf, 0, 4); + } + + // writeDQT writes the Define Quantization Table marker. + private void writeDQT() + { + 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); + } + } + + // writeSOF0 writes the Start Of Frame (Baseline) marker. + private void writeSOF0(int wid, int hei, int nComponent) + { + byte[] chroma1 = new byte[] { 0x22, 0x11, 0x11 }; + byte[] chroma2 = new byte[] { 0x00, 0x01, 0x01 }; + + int markerlen = 8 + 3*nComponent; + writeMarkerHeader(sof0Marker, markerlen); + buf[0] = 8; // 8-bit color. + buf[1] = (byte)(hei >> 8); + buf[2] = (byte)(hei & 0xff); + buf[3] = (byte)(wid >> 8); + buf[4] = (byte)(wid & 0xff); + buf[5] = (byte)(nComponent); + if(nComponent == 1) + { + buf[6] = 1; + // No subsampling for grayscale image. + buf[7] = 0x11; + buf[8] = 0x00; + } + else + { + for(int i = 0; i < nComponent; i++) + { + 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]; + } + } + w.Write(buf, 0, 3*(nComponent-1)+9); + } + + // writeDHT writes the Define Huffman Table marker. + private void writeDHT(int nComponent) + { + byte[] headers = new byte[] { 0x00, 0x10, 0x01, 0x11 }; + int markerlen = 2; + huffmanSpec[] specs = theHuffmanSpec; + + if(nComponent == 1) + { + // Drop the Chrominance tables. + specs = new huffmanSpec[] { theHuffmanSpec[0], theHuffmanSpec[1] }; + } + + foreach(var s in specs) + markerlen += 1 + 16 + s.values.Length; + + writeMarkerHeader(dhtMarker, markerlen); + for(int i = 0; i < specs.Length; i++) + { + var s = specs[i]; + + writeByte(headers[i]); + w.Write(s.count, 0, s.count.Length); + w.Write(s.values, 0, s.values.Length); + } + } + + // writeBlock writes a block of pixel data using the given quantization table, + // returning the post-quantized DC value of the DCT-transformed block. b is in + // natural (not zig-zag) order. + private int writeBlock(Block b, quantIndex q, int prevDC) + { + FDCT(b); + + // Emit the DC delta. + 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); + int runLength = 0; + + for(int zig = 1; zig < Block.blockSize; zig++) + { + int ac = div(b[unzig[zig]], 8*quant[(int)q][zig]); + + if(ac == 0) + { + runLength++; + } + else + { + while(runLength > 15) + { + emitHuff(h, 0xf0); + runLength -= 16; + } + + emitHuffRLE(h, runLength, ac); + runLength = 0; + } + } + if(runLength > 0) + emitHuff(h, 0x00); + return dc; + } + + // toYCbCr converts the 8x8 region of m whose top-left corner is p to its + // YCbCr values. + private void toYCbCr(ImageBase m, int x, int y, Block yBlock, Block cbBlock, Block crBlock) + { + int xmax = m.Width - 1; + int ymax = m.Height - 1; + for(int j = 0; j < 8; j++) + { + 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]) + } + } + } + + // 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); + 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; + } + } + } + } + + // sosHeaderY is the SOS marker "\xff\xda" followed by 8 bytes: + // - the marker length "\x00\x08", + // - the number of components "\x01", + // - component 1 uses DC table 0 and AC table 0 "\x01\x00", + // - the bytes "\x00\x3f\x00". Section B.2.3 of the spec says that for + // sequential DCTs, those bytes (8-bit Ss, 8-bit Se, 4-bit Ah, 4-bit Al) + // should be 0x00, 0x3f, 0x00<<4 | 0x00. + private readonly byte[] sosHeaderY = new byte[] { + 0xff, 0xda, 0x00, 0x08, 0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, + }; + + // sosHeaderYCbCr is the SOS marker "\xff\xda" followed by 12 bytes: + // - the marker length "\x00\x0c", + // - the number of components "\x03", + // - component 1 uses DC table 0 and AC table 0 "\x01\x00", + // - component 2 uses DC table 1 and AC table 1 "\x02\x11", + // - component 3 uses DC table 1 and AC table 1 "\x03\x11", + // - the bytes "\x00\x3f\x00". Section B.2.3 of the spec says that for + // sequential DCTs, those bytes (8-bit Ss, 8-bit Se, 4-bit Ah, 4-bit Al) + // should be 0x00, 0x3f, 0x00<<4 | 0x00. + private readonly byte[] sosHeaderYCbCr = new byte[] { + 0xff, 0xda, 0x00, 0x0c, 0x03, 0x01, 0x00, 0x02, + 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00, + }; + + // writeSOS writes the StartOfScan marker. + private void writeSOS(ImageBase m) + { + w.Write(sosHeaderYCbCr, 0, sosHeaderYCbCr.Length); + + Block b = new Block(); + Block[] cb = new Block[4]; + Block[] cr = new Block[4]; + int prevDCY = 0, prevDCCb = 0, prevDCCr = 0; + + for(int i = 0; i < 4; i++) cb[i] = new Block(); + for(int i = 0; i < 4; i++) cr[i] = new Block(); + + for(int y = 0; y < m.Height; y += 16) + { + for(int x = 0; x < m.Width; x += 16) + { + for(int i = 0; i < 4; i++) + { + int xOff = (i & 1) * 8; + int yOff = (i & 2) * 4; + + toYCbCr(m, x+xOff, y+yOff, b, cb[i], cr[i]); + prevDCY = writeBlock(b, 0, prevDCY); + } + scale(b, cb); + prevDCCb = writeBlock(b, (quantIndex)1, prevDCCb); + scale(b, cr); + prevDCCr = writeBlock(b, (quantIndex)1, prevDCCr); + } + } + + // Pad the last byte with 1's. + emit(0x7f, 7); + } + + // 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) + { + this.w = w; + + 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"); + + if(quality < 1) quality = 1; + if(quality > 100) quality = 100; + + // 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++) + { + for(int j = 0; j < Block.blockSize; j++) + { + 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; + } + } + + // Compute number of components based on input image type. + int nComponent = 3; + + // Write the Start Of Image marker. + buf[0] = 0xff; + buf[1] = 0xd8; + w.Write(buf, 0, 2); + + // Write the quantization tables. + writeDQT(); + + // Write the image dimensions. + writeSOF0(m.Width, m.Height, nComponent); + + // Write the Huffman tables. + writeDHT(nComponent); + + // Write the image data. + writeSOS(m); + + // Write the End Of Image marker. + buf[0] = 0xff; + buf[1] = 0xd9; + w.Write(buf, 0, 2); + w.Flush(); + } + + // div returns a/b rounded to the nearest integer, instead of rounded to zero. + private static int div(int a, int b) + { + if(a >= 0) + return (a + (b >> 1)) / b; + else + return -((-a + (b >> 1)) / b); + } + } +} diff --git a/src/ImageProcessorCore/Formats/Jpg/FDCT.cs b/src/ImageProcessorCore/Formats/Jpg/FDCT.cs index a3202d57c7..267a3177b6 100644 --- a/src/ImageProcessorCore/Formats/Jpg/FDCT.cs +++ b/src/ImageProcessorCore/Formats/Jpg/FDCT.cs @@ -3,7 +3,7 @@ namespace ImageProcessorCore.Formats.Jpg using System; using System.IO; - public partial class JpegGo + public partial class Encoder { // Trigonometric constants in 13-bit fixed point format. private const int fix_0_298631336 = 2446; @@ -92,7 +92,7 @@ namespace ImageProcessorCore.Formats.Jpg int tmp2 = b[2*8+x] + b[5*8+x]; int tmp3 = b[3*8+x] + b[4*8+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; diff --git a/src/ImageProcessorCore/Formats/Jpg/JpegEncoder.cs b/src/ImageProcessorCore/Formats/Jpg/JpegEncoder.cs index b6e5250d85..83f220e356 100644 --- a/src/ImageProcessorCore/Formats/Jpg/JpegEncoder.cs +++ b/src/ImageProcessorCore/Formats/Jpg/JpegEncoder.cs @@ -8,6 +8,7 @@ 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. @@ -57,35 +58,8 @@ namespace ImageProcessorCore.Formats Guard.NotNull(image, nameof(image)); Guard.NotNull(stream, nameof(stream)); - int imageWidth = image.Width; - int imageHeight = image.Height; - - /*SampleRow[] rows = new SampleRow[imageHeight]; - - Parallel.For( - 0, - imageHeight, - y => - { - byte[] samples = new byte[imageWidth * 3]; - - for (int x = 0; x < imageWidth; x++) - { - Bgra32 color = Color.ToNonPremultiplied(image[x, y]); - - int start = x * 3; - samples[start] = color.R; - samples[start + 1] = color.G; - samples[start + 2] = color.B; - } - - rows[y] = new SampleRow(samples, imageWidth, 8, 3); - }); - - using (JpegImage jpg = new JpegImage(rows, Colorspace.RGB)) - { - jpg.WriteJpeg(stream, new CompressionParameters { Quality = this.Quality }); - }*/ + var encode = new Encoder(); + encode.Encode(stream, image, this.Quality); } } } diff --git a/src/ImageProcessorCore/Formats/Jpg/JpegFormat.cs b/src/ImageProcessorCore/Formats/Jpg/JpegFormat.cs index 5216575243..ec9ceb6dc0 100644 --- a/src/ImageProcessorCore/Formats/Jpg/JpegFormat.cs +++ b/src/ImageProcessorCore/Formats/Jpg/JpegFormat.cs @@ -14,6 +14,6 @@ namespace ImageProcessorCore.Formats public IImageDecoder Decoder => new JpegDecoder(); /// - public IImageEncoder Encoder => null; + public IImageEncoder Encoder => new JpegEncoder(); } } diff --git a/src/ImageProcessorCore/Formats/Jpg/jpeg2/fdct.goa b/src/ImageProcessorCore/Formats/Jpg/jpeg2/fdct.goa deleted file mode 100644 index a92204cf9a..0000000000 --- a/src/ImageProcessorCore/Formats/Jpg/jpeg2/fdct.goa +++ /dev/null @@ -1,190 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package golang - -// This file implements a Forward Discrete Cosine Transformation. - -/* -It is based on the code in jfdctint.c from the Independent JPEG Group, -found at http://www.ijg.org/files/jpegsrc.v8c.tar.gz. - -The "LEGAL ISSUES" section of the README in that archive says: - -In plain English: - -1. We don't promise that this software works. (But if you find any bugs, - please let us know!) -2. You can use this software for whatever you want. You don't have to pay us. -3. You may not pretend that you wrote this software. If you use it in a - program, you must acknowledge somewhere in your documentation that - you've used the IJG code. - -In legalese: - -The authors make NO WARRANTY or representation, either express or implied, -with respect to this software, its quality, accuracy, merchantability, or -fitness for a particular purpose. This software is provided "AS IS", and you, -its user, assume the entire risk as to its quality and accuracy. - -This software is copyright (C) 1991-2011, Thomas G. Lane, Guido Vollbeding. -All Rights Reserved except as specified below. - -Permission is hereby granted to use, copy, modify, and distribute this -software (or portions thereof) for any purpose, without fee, subject to these -conditions: -(1) If any part of the source code for this software is distributed, then this -README file must be included, with this copyright and no-warranty notice -unaltered; and any additions, deletions, or changes to the original files -must be clearly indicated in accompanying documentation. -(2) If only executable code is distributed, then the accompanying -documentation must state that "this software is based in part on the work of -the Independent JPEG Group". -(3) Permission for use of this software is granted only if the user accepts -full responsibility for any undesirable consequences; the authors accept -NO LIABILITY for damages of any kind. - -These conditions apply to any software derived from or based on the IJG code, -not just to the unmodified library. If you use our work, you ought to -acknowledge us. - -Permission is NOT granted for the use of any IJG author's name or company name -in advertising or publicity relating to this software or products derived from -it. This software may be referred to only as "the Independent JPEG Group's -software". - -We specifically permit and encourage the use of this software as the basis of -commercial products, provided that all warranty or liability claims are -assumed by the product vendor. -*/ - -// Trigonometric constants in 13-bit fixed point format. -const ( - fix_0_298631336 = 2446 - fix_0_390180644 = 3196 - fix_0_541196100 = 4433 - fix_0_765366865 = 6270 - fix_0_899976223 = 7373 - fix_1_175875602 = 9633 - fix_1_501321110 = 12299 - fix_1_847759065 = 15137 - fix_1_961570560 = 16069 - fix_2_053119869 = 16819 - fix_2_562915447 = 20995 - fix_3_072711026 = 25172 -) - -const ( - constBits = 13 - pass1Bits = 2 - centerJSample = 128 -) - -// fdct performs a forward DCT on an 8x8 block of coefficients, including a -// level shift. -func fdct(b *block) { - // Pass 1: process rows. - for y := 0; y < 8; y++ { - x0 := b[y*8+0] - x1 := b[y*8+1] - x2 := b[y*8+2] - x3 := b[y*8+3] - x4 := b[y*8+4] - x5 := b[y*8+5] - x6 := b[y*8+6] - x7 := b[y*8+7] - - tmp0 := x0 + x7 - tmp1 := x1 + x6 - tmp2 := x2 + x5 - tmp3 := x3 + x4 - - tmp10 := tmp0 + tmp3 - tmp12 := tmp0 - tmp3 - tmp11 := tmp1 + tmp2 - tmp13 := tmp1 - tmp2 - - tmp0 = x0 - x7 - tmp1 = x1 - x6 - tmp2 = x2 - x5 - tmp3 = x3 - x4 - - b[y*8+0] = (tmp10 + tmp11 - 8*centerJSample) << pass1Bits - b[y*8+4] = (tmp10 - tmp11) << pass1Bits - 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) - - tmp10 = tmp0 + tmp3 - tmp11 = tmp1 + tmp2 - tmp12 = tmp0 + tmp2 - tmp13 = tmp1 + tmp3 - z1 = (tmp12 + tmp13) * fix_1_175875602 - z1 += 1 << (constBits - pass1Bits - 1) - tmp0 = tmp0 * fix_1_501321110 - tmp1 = tmp1 * fix_3_072711026 - tmp2 = tmp2 * fix_2_053119869 - tmp3 = tmp3 * fix_0_298631336 - tmp10 = tmp10 * -fix_0_899976223 - tmp11 = tmp11 * -fix_2_562915447 - tmp12 = tmp12 * -fix_0_390180644 - tmp13 = tmp13 * -fix_1_961570560 - - 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) - } - // Pass 2: process columns. - // We remove pass1Bits scaling, but leave results scaled up by an overall factor of 8. - for x := 0; x < 8; x++ { - 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] - - tmp10 := tmp0 + tmp3 + 1<<(pass1Bits-1) - tmp12 := tmp0 - tmp3 - tmp11 := tmp1 + tmp2 - 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] - - b[0*8+x] = (tmp10 + tmp11) >> pass1Bits - b[4*8+x] = (tmp10 - tmp11) >> pass1Bits - - 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) - - tmp10 = tmp0 + tmp3 - tmp11 = tmp1 + tmp2 - tmp12 = tmp0 + tmp2 - tmp13 = tmp1 + tmp3 - z1 = (tmp12 + tmp13) * fix_1_175875602 - z1 += 1 << (constBits + pass1Bits - 1) - tmp0 = tmp0 * fix_1_501321110 - tmp1 = tmp1 * fix_3_072711026 - tmp2 = tmp2 * fix_2_053119869 - tmp3 = tmp3 * fix_0_298631336 - tmp10 = tmp10 * -fix_0_899976223 - tmp11 = tmp11 * -fix_2_562915447 - tmp12 = tmp12 * -fix_0_390180644 - tmp13 = tmp13 * -fix_1_961570560 - - 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) - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/jpeg2/reader.go b/src/ImageProcessorCore/Formats/Jpg/jpeg2/reader.go deleted file mode 100644 index e3ebf589ed..0000000000 --- a/src/ImageProcessorCore/Formats/Jpg/jpeg2/reader.go +++ /dev/null @@ -1,1665 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package golang implements a JPEG image decoder and encoder. -// -// JPEG is defined in ITU-T T.81: http://www.w3.org/Graphics/JPEG/itu-t81.pdf. -package jpeg2 - -import ( - "fmt" - "image" - "image/color" - "io" -) - -// TODO(nigeltao): fix up the doc comment style so that sentences start with -// the name of the type or function that they annotate. - -// A FormatError reports that the input is not a valid JPEG. -type FormatError string - -func (e FormatError) Error() string { return "invalid JPEG format: " + string(e) } - -// An UnsupportedError reports that the input uses a valid but unimplemented JPEG feature. -type UnsupportedError string - -func (e UnsupportedError) Error() string { return "unsupported JPEG feature: " + string(e) } - -var errUnsupportedSubsamplingRatio = UnsupportedError("luma/chroma subsampling ratio") - -// Component specification, specified in section B.2.2. -type component struct { - h int // Horizontal sampling factor. - v int // Vertical sampling factor. - c uint8 // Component identifier. - tq uint8 // Quantization table destination selector. -} - -const ( - dcTable = 0 - acTable = 1 - maxTc = 1 - maxTh = 3 - maxTq = 3 - - maxComponents = 4 -) - -const ( - sof0Marker = 0xc0 // Start Of Frame (Baseline). - sof1Marker = 0xc1 // Start Of Frame (Extended Sequential). - sof2Marker = 0xc2 // Start Of Frame (Progressive). - dhtMarker = 0xc4 // Define Huffman Table. - rst0Marker = 0xd0 // ReSTart (0). - rst7Marker = 0xd7 // ReSTart (7). - soiMarker = 0xd8 // Start Of Image. - eoiMarker = 0xd9 // End Of Image. - sosMarker = 0xda // Start Of Scan. - dqtMarker = 0xdb // Define Quantization Table. - driMarker = 0xdd // Define Restart Interval. - 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 - app0Marker = 0xe0 - app14Marker = 0xee - app15Marker = 0xef -) - -// See http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html#Adobe -const ( - adobeTransformUnknown = 0 - adobeTransformYCbCr = 1 - adobeTransformYCbCrK = 2 -) - -// unzig maps from the zig-zag ordering to the natural ordering. For example, -// unzig[3] is the column and row of the fourth element in zig-zag order. The -// value is 16, which means first column (16%8 == 0) and third row (16/8 == 2). -var unzig = [blockSize]int{ - 0, 1, 8, 16, 9, 2, 3, 10, - 17, 24, 32, 25, 18, 11, 4, 5, - 12, 19, 26, 33, 40, 48, 41, 34, - 27, 20, 13, 6, 7, 14, 21, 28, - 35, 42, 49, 56, 57, 50, 43, 36, - 29, 22, 15, 23, 30, 37, 44, 51, - 58, 59, 52, 45, 38, 31, 39, 46, - 53, 60, 61, 54, 47, 55, 62, 63, -} - -// Deprecated: Reader is deprecated. -type Reader interface { - io.ByteReader - io.Reader -} - -// bits holds the unprocessed bits that have been taken from the byte-stream. -// The n least significant bits of a form the unread bits, to be read in MSB to -// LSB order. -type bits struct { - a uint32 // accumulator. - m uint32 // mask. m==1<<(n-1) when n>0, with m==0 when n==0. - n int32 // the number of unread bits in a. -} - -type decoder struct { - r io.Reader - bits bits - // bytes is a byte buffer, similar to a bufio.Reader, except that it - // has to be able to unread more than 1 byte, due to byte stuffing. - // Byte stuffing is specified in section F.1.2.3. - bytes struct { - // buf[i:j] are the buffered bytes read from the underlying - // io.Reader that haven't yet been passed further on. - buf [4096]byte - i, j int - // nUnreadable is the number of bytes to back up i after - // overshooting. It can be 0, 1 or 2. - nUnreadable int - } - width, height int - - img1 *image.Gray - img3 *image.YCbCr - blackPix []byte - blackStride int - - ri int // Restart Interval. - nComp int - progressive bool - jfif bool - adobeTransformValid bool - adobeTransform uint8 - eobRun uint16 // End-of-Band run, specified in section G.1.2.2. - - comp [maxComponents]component - progCoeffs [maxComponents][]block // Saved state between progressive-mode scans. - huff [maxTc + 1][maxTh + 1]huffman - quant [maxTq + 1]block // Quantization tables, in zig-zag order. - tmp [2 * blockSize]byte -} - -// fill fills up the d.bytes.buf buffer from the underlying io.Reader. It -// should only be called when there are no unread bytes in d.bytes. -func (d *decoder) fill() error { - if d.bytes.i != d.bytes.j { - panic("jpeg: fill called when unread bytes exist") - } - // Move the last 2 bytes to the start of the buffer, in case we need - // to call unreadByteStuffedByte. - if d.bytes.j > 2 { - d.bytes.buf[0] = d.bytes.buf[d.bytes.j-2] - d.bytes.buf[1] = d.bytes.buf[d.bytes.j-1] - d.bytes.i, d.bytes.j = 2, 2 - } - // Fill in the rest of the buffer. - n, err := d.r.Read(d.bytes.buf[d.bytes.j:]) - d.bytes.j += n - if n > 0 { - err = nil - } - return err -} - -// unreadByteStuffedByte undoes the most recent readByteStuffedByte call, -// giving a byte of data back from d.bits to d.bytes. The Huffman look-up table -// requires at least 8 bits for look-up, which means that Huffman decoding can -// sometimes overshoot and read one or two too many bytes. Two-byte overshoot -// can happen when expecting to read a 0xff 0x00 byte-stuffed byte. -func (d *decoder) unreadByteStuffedByte() { - d.bytes.i -= d.bytes.nUnreadable - d.bytes.nUnreadable = 0 - if d.bits.n >= 8 { - d.bits.a >>= 8 - d.bits.n -= 8 - d.bits.m >>= 8 - } -} - -// readByte returns the next byte, whether buffered or not buffered. It does -// not care about byte stuffing. -func (d *decoder) readByte() (x byte, err error) { - for d.bytes.i == d.bytes.j { - if err = d.fill(); err != nil { - return 0, err - } - } - x = d.bytes.buf[d.bytes.i] - d.bytes.i++ - d.bytes.nUnreadable = 0 - return x, nil -} - -// errMissingFF00 means that readByteStuffedByte encountered an 0xff byte (a -// marker byte) that wasn't the expected byte-stuffed sequence 0xff, 0x00. -var errMissingFF00 = FormatError("missing 0xff00 sequence") - -// readByteStuffedByte is like readByte but is for byte-stuffed Huffman data. -func (d *decoder) readByteStuffedByte() (x byte, err error) { - // Take the fast path if d.bytes.buf contains at least two bytes. - if d.bytes.i+2 <= d.bytes.j { - x = d.bytes.buf[d.bytes.i] - d.bytes.i++ - d.bytes.nUnreadable = 1 - if x != 0xff { - return x, err - } - if d.bytes.buf[d.bytes.i] != 0x00 { - return 0, errMissingFF00 - } - d.bytes.i++ - d.bytes.nUnreadable = 2 - return 0xff, nil - } - - d.bytes.nUnreadable = 0 - - x, err = d.readByte() - if err != nil { - return 0, err - } - d.bytes.nUnreadable = 1 - if x != 0xff { - return x, nil - } - - x, err = d.readByte() - if err != nil { - return 0, err - } - d.bytes.nUnreadable = 2 - if x != 0x00 { - return 0, errMissingFF00 - } - return 0xff, nil -} - -// readFull reads exactly len(p) bytes into p. It does not care about byte -// stuffing. -func (d *decoder) readFull(p []byte) error { - // Unread the overshot bytes, if any. - if d.bytes.nUnreadable != 0 { - if d.bits.n >= 8 { - d.unreadByteStuffedByte() - } - d.bytes.nUnreadable = 0 - } - - for { - n := copy(p, d.bytes.buf[d.bytes.i:d.bytes.j]) - p = p[n:] - d.bytes.i += n - if len(p) == 0 { - break - } - if err := d.fill(); err != nil { - if err == io.EOF { - err = io.ErrUnexpectedEOF - } - return err - } - } - return nil -} - -// ignore ignores the next n bytes. -func (d *decoder) ignore(n int) error { - // Unread the overshot bytes, if any. - if d.bytes.nUnreadable != 0 { - if d.bits.n >= 8 { - d.unreadByteStuffedByte() - } - d.bytes.nUnreadable = 0 - } - - for { - m := d.bytes.j - d.bytes.i - if m > n { - m = n - } - d.bytes.i += m - n -= m - if n == 0 { - break - } - if err := d.fill(); err != nil { - if err == io.EOF { - err = io.ErrUnexpectedEOF - } - return err - } - } - return nil -} - -// Specified in section B.2.2. -func (d *decoder) processSOF(n int) error { - if d.nComp != 0 { - return FormatError("multiple SOF markers") - } - switch n { - case 6 + 3*1: // Grayscale image. - d.nComp = 1 - case 6 + 3*3: // YCbCr or RGB image. - d.nComp = 3 - case 6 + 3*4: // YCbCrK or CMYK image. - d.nComp = 4 - default: - return UnsupportedError("number of components") - } - if err := d.readFull(d.tmp[:n]); err != nil { - return err - } - // We only support 8-bit precision. - if d.tmp[0] != 8 { - return UnsupportedError("precision") - } - d.height = int(d.tmp[1])<<8 + int(d.tmp[2]) - d.width = int(d.tmp[3])<<8 + int(d.tmp[4]) - if int(d.tmp[5]) != d.nComp { - return FormatError("SOF has wrong length") - } - - for i := 0; i < d.nComp; i++ { - d.comp[i].c = d.tmp[6+3*i] - // Section B.2.2 states that "the value of C_i shall be different from - // the values of C_1 through C_(i-1)". - for j := 0; j < i; j++ { - if d.comp[i].c == d.comp[j].c { - return FormatError("repeated component identifier") - } - } - - d.comp[i].tq = d.tmp[8+3*i] - if d.comp[i].tq > maxTq { - return FormatError("bad Tq value") - } - - hv := d.tmp[7+3*i] - h, v := int(hv>>4), int(hv&0x0f) - if h < 1 || 4 < h || v < 1 || 4 < v { - return FormatError("luma/chroma subsampling ratio") - } - if h == 3 || v == 3 { - return errUnsupportedSubsamplingRatio - } - switch d.nComp { - case 1: - // If a JPEG image has only one component, section A.2 says "this data - // is non-interleaved by definition" and section A.2.2 says "[in this - // case...] the order of data units within a scan shall be left-to-right - // and top-to-bottom... regardless of the values of H_1 and V_1". Section - // 4.8.2 also says "[for non-interleaved data], the MCU is defined to be - // one data unit". Similarly, section A.1.1 explains that it is the ratio - // of H_i to max_j(H_j) that matters, and similarly for V. For grayscale - // images, H_1 is the maximum H_j for all components j, so that ratio is - // always 1. The component's (h, v) is effectively always (1, 1): even if - // the nominal (h, v) is (2, 1), a 20x5 image is encoded in three 8x8 - // MCUs, not two 16x8 MCUs. - h, v = 1, 1 - - case 3: - // For YCbCr images, we only support 4:4:4, 4:4:0, 4:2:2, 4:2:0, - // 4:1:1 or 4:1:0 chroma subsampling ratios. This implies that the - // (h, v) values for the Y component are either (1, 1), (1, 2), - // (2, 1), (2, 2), (4, 1) or (4, 2), and the Y component's values - // must be a multiple of the Cb and Cr component's values. We also - // assume that the two chroma components have the same subsampling - // ratio. - switch i { - case 0: // Y. - // We have already verified, above, that h and v are both - // either 1, 2 or 4, so invalid (h, v) combinations are those - // with v == 4. - if v == 4 { - return errUnsupportedSubsamplingRatio - } - case 1: // Cb. - if d.comp[0].h%h != 0 || d.comp[0].v%v != 0 { - return errUnsupportedSubsamplingRatio - } - case 2: // Cr. - if d.comp[1].h != h || d.comp[1].v != v { - return errUnsupportedSubsamplingRatio - } - } - - case 4: - // For 4-component images (either CMYK or YCbCrK), we only support two - // hv vectors: [0x11 0x11 0x11 0x11] and [0x22 0x11 0x11 0x22]. - // Theoretically, 4-component JPEG images could mix and match hv values - // but in practice, those two combinations are the only ones in use, - // and it simplifies the applyBlack code below if we can assume that: - // - for CMYK, the C and K channels have full samples, and if the M - // and Y channels subsample, they subsample both horizontally and - // vertically. - // - for YCbCrK, the Y and K channels have full samples. - switch i { - case 0: - if hv != 0x11 && hv != 0x22 { - return errUnsupportedSubsamplingRatio - } - case 1, 2: - if hv != 0x11 { - return errUnsupportedSubsamplingRatio - } - case 3: - if d.comp[0].h != h || d.comp[0].v != v { - return errUnsupportedSubsamplingRatio - } - } - } - - d.comp[i].h = h - d.comp[i].v = v - } - return nil -} - -// Specified in section B.2.4.1. -func (d *decoder) processDQT(n int) error { -loop: - for n > 0 { - n-- - x, err := d.readByte() - if err != nil { - return err - } - tq := x & 0x0f - if tq > maxTq { - return FormatError("bad Tq value") - } - switch x >> 4 { - default: - return FormatError("bad Pq value") - case 0: - if n < blockSize { - break loop - } - n -= blockSize - if err := d.readFull(d.tmp[:blockSize]); err != nil { - return err - } - for i := range d.quant[tq] { - d.quant[tq][i] = int32(d.tmp[i]) - } - case 1: - if n < 2*blockSize { - break loop - } - n -= 2 * blockSize - if err := d.readFull(d.tmp[:2*blockSize]); err != nil { - return err - } - for i := range d.quant[tq] { - d.quant[tq][i] = int32(d.tmp[2*i])<<8 | int32(d.tmp[2*i+1]) - } - } - } - if n != 0 { - return FormatError("DQT has wrong length") - } - return nil -} - -// Specified in section B.2.4.4. -func (d *decoder) processDRI(n int) error { - if n != 2 { - return FormatError("DRI has wrong length") - } - if err := d.readFull(d.tmp[:2]); err != nil { - return err - } - d.ri = int(d.tmp[0])<<8 + int(d.tmp[1]) - return nil -} - -func (d *decoder) processApp0Marker(n int) error { - if n < 5 { - return d.ignore(n) - } - if err := d.readFull(d.tmp[:5]); err != nil { - return err - } - n -= 5 - - d.jfif = d.tmp[0] == 'J' && d.tmp[1] == 'F' && d.tmp[2] == 'I' && d.tmp[3] == 'F' && d.tmp[4] == '\x00' - - if n > 0 { - return d.ignore(n) - } - return nil -} - -func (d *decoder) processApp14Marker(n int) error { - if n < 12 { - return d.ignore(n) - } - if err := d.readFull(d.tmp[:12]); err != nil { - return err - } - n -= 12 - - if d.tmp[0] == 'A' && d.tmp[1] == 'd' && d.tmp[2] == 'o' && d.tmp[3] == 'b' && d.tmp[4] == 'e' { - d.adobeTransformValid = true - d.adobeTransform = d.tmp[11] - } - - if n > 0 { - return d.ignore(n) - } - return nil -} - -// decode reads a JPEG image from r and returns it as an image.Image. -func (d *decoder) decode(r io.Reader, configOnly bool) (image.Image, error) { - d.r = r - - // Check for the Start Of Image marker. - if err := d.readFull(d.tmp[:2]); err != nil { - return nil, err - } - if d.tmp[0] != 0xff || d.tmp[1] != soiMarker { - return nil, FormatError("missing SOI marker") - } - - // Process the remaining segments until the End Of Image marker. - for { - err := d.readFull(d.tmp[:2]) - if err != nil { - return nil, err - } - for d.tmp[0] != 0xff { - // Strictly speaking, this is a format error. However, libjpeg is - // liberal in what it accepts. As of version 9, next_marker in - // jdmarker.c treats this as a warning (JWRN_EXTRANEOUS_DATA) and - // continues to decode the stream. Even before next_marker sees - // extraneous data, jpeg_fill_bit_buffer in jdhuff.c reads as many - // bytes as it can, possibly past the end of a scan's data. It - // effectively puts back any markers that it overscanned (e.g. an - // "\xff\xd9" EOI marker), but it does not put back non-marker data, - // and thus it can silently ignore a small number of extraneous - // non-marker bytes before next_marker has a chance to see them (and - // print a warning). - // - // We are therefore also liberal in what we accept. Extraneous data - // is silently ignored. - // - // This is similar to, but not exactly the same as, the restart - // mechanism within a scan (the RST[0-7] markers). - // - // Note that extraneous 0xff bytes in e.g. SOS data are escaped as - // "\xff\x00", and so are detected a little further down below. - d.tmp[0] = d.tmp[1] - d.tmp[1], err = d.readByte() - if err != nil { - return nil, err - } - } - marker := d.tmp[1] - if marker == 0 { - // Treat "\xff\x00" as extraneous data. - continue - } - for marker == 0xff { - // Section B.1.1.2 says, "Any marker may optionally be preceded by any - // number of fill bytes, which are bytes assigned code X'FF'". - marker, err = d.readByte() - if err != nil { - return nil, err - } - } - if marker == eoiMarker { // End Of Image. - break - } - if rst0Marker <= marker && marker <= rst7Marker { - // Figures B.2 and B.16 of the specification suggest that restart markers should - // only occur between Entropy Coded Segments and not after the final ECS. - // However, some encoders may generate incorrect JPEGs with a final restart - // marker. That restart marker will be seen here instead of inside the processSOS - // method, and is ignored as a harmless error. Restart markers have no extra data, - // so we check for this before we read the 16-bit length of the segment. - continue - } - - // Read the 16-bit length of the segment. The value includes the 2 bytes for the - // length itself, so we subtract 2 to get the number of remaining bytes. - if err = d.readFull(d.tmp[:2]); err != nil { - return nil, err - } - n := int(d.tmp[0])<<8 + int(d.tmp[1]) - 2 - if n < 0 { - return nil, FormatError("short segment length") - } - - switch marker { - case sof0Marker, sof1Marker, sof2Marker: - d.progressive = marker == sof2Marker - err = d.processSOF(n) - if configOnly && d.jfif { - return nil, err - } - case dhtMarker: - if configOnly { - err = d.ignore(n) - } else { - err = d.processDHT(n) - } - case dqtMarker: - if configOnly { - err = d.ignore(n) - } else { - err = d.processDQT(n) - } - case sosMarker: - if configOnly { - return nil, nil - } - err = d.processSOS(n) - case driMarker: - if configOnly { - err = d.ignore(n) - } else { - err = d.processDRI(n) - } - case app0Marker: - err = d.processApp0Marker(n) - case app14Marker: - err = d.processApp14Marker(n) - default: - if app0Marker <= marker && marker <= app15Marker || marker == comMarker { - err = d.ignore(n) - } else if marker < 0xc0 { // See Table B.1 "Marker code assignments". - err = FormatError("unknown marker") - } else { - err = UnsupportedError("unknown marker") - } - } - if err != nil { - return nil, err - } - } - if d.img1 != nil { - return d.img1, nil - } - if d.img3 != nil { - if d.blackPix != nil { - return d.applyBlack() - } else if d.isRGB() { - return d.convertToRGB() - } - return d.img3, nil - } - return nil, FormatError("missing SOS marker") -} - -// applyBlack combines d.img3 and d.blackPix into a CMYK image. The formula -// used depends on whether the JPEG image is stored as CMYK or YCbCrK, -// indicated by the APP14 (Adobe) metadata. -// -// Adobe CMYK JPEG images are inverted, where 255 means no ink instead of full -// ink, so we apply "v = 255 - v" at various points. Note that a double -// inversion is a no-op, so inversions might be implicit in the code below. -func (d *decoder) applyBlack() (image.Image, error) { - /*if !d.adobeTransformValid { - return nil, UnsupportedError("unknown color model: 4-component JPEG doesn't have Adobe APP14 metadata") - } - - // If the 4-component JPEG image isn't explicitly marked as "Unknown (RGB - // or CMYK)" as per - // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html#Adobe - // we assume that it is YCbCrK. This matches libjpeg's jdapimin.c. - if d.adobeTransform != adobeTransformUnknown { - // Convert the YCbCr part of the YCbCrK to RGB, invert the RGB to get - // CMY, and patch in the original K. The RGB to CMY inversion cancels - // out the 'Adobe inversion' described in the applyBlack doc comment - // above, so in practice, only the fourth channel (black) is inverted. - bounds := d.img3.Bounds() - img := image.NewRGBA(bounds) - imageutil.DrawYCbCr(img, bounds, d.img3, bounds.Min) - for iBase, y := 0, bounds.Min.Y; y < bounds.Max.Y; iBase, y = iBase+img.Stride, y+1 { - for i, x := iBase+3, bounds.Min.X; x < bounds.Max.X; i, x = i+4, x+1 { - img.Pix[i] = 255 - d.blackPix[(y-bounds.Min.Y)*d.blackStride+(x-bounds.Min.X)] - } - } - return &image.CMYK{ - Pix: img.Pix, - Stride: img.Stride, - Rect: img.Rect, - }, nil - } - - // The first three channels (cyan, magenta, yellow) of the CMYK - // were decoded into d.img3, but each channel was decoded into a separate - // []byte slice, and some channels may be subsampled. We interleave the - // separate channels into an image.CMYK's single []byte slice containing 4 - // contiguous bytes per pixel. - bounds := d.img3.Bounds() - img := image.NewCMYK(bounds) - - translations := [4]struct { - src []byte - stride int - }{ - {d.img3.Y, d.img3.YStride}, - {d.img3.Cb, d.img3.CStride}, - {d.img3.Cr, d.img3.CStride}, - {d.blackPix, d.blackStride}, - } - for t, translation := range translations { - subsample := d.comp[t].h != d.comp[0].h || d.comp[t].v != d.comp[0].v - for iBase, y := 0, bounds.Min.Y; y < bounds.Max.Y; iBase, y = iBase+img.Stride, y+1 { - sy := y - bounds.Min.Y - if subsample { - sy /= 2 - } - for i, x := iBase+t, bounds.Min.X; x < bounds.Max.X; i, x = i+4, x+1 { - sx := x - bounds.Min.X - if subsample { - sx /= 2 - } - img.Pix[i] = 255 - translation.src[sy*translation.stride+sx] - } - } - }*/ - return nil, nil -} - -func (d *decoder) isRGB() bool { - if d.jfif { - return false - } - if d.adobeTransformValid && d.adobeTransform == adobeTransformUnknown { - // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html#Adobe - // says that 0 means Unknown (and in practice RGB) and 1 means YCbCr. - return true - } - return d.comp[0].c == 'R' && d.comp[1].c == 'G' && d.comp[2].c == 'B' -} - -func (d *decoder) convertToRGB() (image.Image, error) { - cScale := d.comp[0].h / d.comp[1].h - bounds := d.img3.Bounds() - img := image.NewRGBA(bounds) - for y := bounds.Min.Y; y < bounds.Max.Y; y++ { - po := img.PixOffset(bounds.Min.X, y) - yo := d.img3.YOffset(bounds.Min.X, y) - co := d.img3.COffset(bounds.Min.X, y) - for i, iMax := 0, bounds.Max.X-bounds.Min.X; i < iMax; i++ { - img.Pix[po+4*i+0] = d.img3.Y[yo+i] - img.Pix[po+4*i+1] = d.img3.Cb[co+i/cScale] - img.Pix[po+4*i+2] = d.img3.Cr[co+i/cScale] - img.Pix[po+4*i+3] = 255 - } - } - return img, nil -} - -// Decode reads a JPEG image from r and returns it as an image.Image. -func Decode(r io.Reader) (image.Image, error) { - var d decoder - return d.decode(r, false) -} - -// DecodeConfig returns the color model and dimensions of a JPEG image without -// decoding the entire image. -func DecodeConfig(r io.Reader) (image.Config, error) { - var d decoder - if _, err := d.decode(r, true); err != nil { - return image.Config{}, err - } - switch d.nComp { - case 1: - return image.Config{ - ColorModel: color.GrayModel, - Width: d.width, - Height: d.height, - }, nil - case 3: - cm := color.YCbCrModel - if d.isRGB() { - cm = color.RGBAModel - } - return image.Config{ - ColorModel: cm, - Width: d.width, - Height: d.height, - }, nil - case 4: - return image.Config{ - ColorModel: color.CMYKModel, - Width: d.width, - Height: d.height, - }, nil - } - return image.Config{}, FormatError("missing SOF marker") -} - -func init() { - image.RegisterFormat("jpeg", "\xff\xd8", Decode, DecodeConfig) -} - -const blockSize = 64 // A DCT block is 8x8. - -type block [blockSize]int32 - -const ( - w1 = 2841 // 2048*sqrt(2)*cos(1*pi/16) - w2 = 2676 // 2048*sqrt(2)*cos(2*pi/16) - w3 = 2408 // 2048*sqrt(2)*cos(3*pi/16) - w5 = 1609 // 2048*sqrt(2)*cos(5*pi/16) - w6 = 1108 // 2048*sqrt(2)*cos(6*pi/16) - w7 = 565 // 2048*sqrt(2)*cos(7*pi/16) - - w1pw7 = w1 + w7 - w1mw7 = w1 - w7 - w2pw6 = w2 + w6 - w2mw6 = w2 - w6 - w3pw5 = w3 + w5 - w3mw5 = w3 - w5 - - r2 = 181 // 256/sqrt(2) -) - -// idct performs a 2-D Inverse Discrete Cosine Transformation. -// -// The input coefficients should already have been multiplied by the -// appropriate quantization table. We use fixed-point computation, with the -// number of bits for the fractional component varying over the intermediate -// stages. -// -// 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. -func idct(src *block) { - // Horizontal 1-D IDCT. - for y := 0; y < 8; y++ { - 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 { - 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. - x0 := (src[y8+0] << 11) + 128 - x1 := src[y8+4] << 11 - x2 := src[y8+6] - x3 := src[y8+2] - x4 := src[y8+1] - x5 := src[y8+7] - x6 := src[y8+5] - x7 := src[y8+3] - - // Stage 1. - x8 := w7 * (x4 + x5) - x4 = x8 + w1mw7*x4 - x5 = x8 - w1pw7*x5 - x8 = w3 * (x6 + 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 - x1 = x4 + x6 - x4 -= x6 - x6 = x5 + x7 - x5 -= x7 - - // Stage 3. - x7 = x8 + x3 - x8 -= x3 - x3 = x0 + x2 - x0 -= x2 - 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 - } - - // Vertical 1-D IDCT. - for x := 0; x < 8; x++ { - // Similar to the horizontal 1-D IDCT case, if all the AC components are zero, then the IDCT is trivial. - // However, after performing the horizontal 1-D IDCT, there are typically non-zero AC components, so - // we do not bother to check for the all-zero case. - - // Prescale. - y0 := (src[8*0+x] << 8) + 8192 - y1 := src[8*4+x] << 8 - y2 := src[8*6+x] - y3 := src[8*2+x] - y4 := src[8*1+x] - y5 := src[8*7+x] - y6 := src[8*5+x] - y7 := src[8*3+x] - - // Stage 1. - 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 = y4 + y6 - y4 -= y6 - y6 = y5 + y7 - y5 -= y7 - - // Stage 3. - y7 = y8 + y3 - y8 -= y3 - y3 = y0 + y2 - y0 -= y2 - 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 - } -} - -// makeImg allocates and initializes the destination image. -func (d *decoder) makeImg(mxx, myy int) { - if d.nComp == 1 { - m := image.NewGray(image.Rect(0, 0, 8*mxx, 8*myy)) - d.img1 = m.SubImage(image.Rect(0, 0, d.width, d.height)).(*image.Gray) - return - } - - h0 := d.comp[0].h - v0 := d.comp[0].v - hRatio := h0 / d.comp[1].h - vRatio := v0 / d.comp[1].v - var subsampleRatio image.YCbCrSubsampleRatio - switch hRatio<<4 | vRatio { - case 0x11: - subsampleRatio = image.YCbCrSubsampleRatio444 - case 0x12: - subsampleRatio = image.YCbCrSubsampleRatio440 - case 0x21: - subsampleRatio = image.YCbCrSubsampleRatio422 - case 0x22: - subsampleRatio = image.YCbCrSubsampleRatio420 - case 0x41: - subsampleRatio = image.YCbCrSubsampleRatio411 - case 0x42: - subsampleRatio = image.YCbCrSubsampleRatio410 - default: - panic("unreachable") - } - m := image.NewYCbCr(image.Rect(0, 0, 8*h0*mxx, 8*v0*myy), subsampleRatio) - d.img3 = m.SubImage(image.Rect(0, 0, d.width, d.height)).(*image.YCbCr) - - if d.nComp == 4 { - h3, v3 := d.comp[3].h, d.comp[3].v - d.blackPix = make([]byte, 8*h3*mxx*8*v3*myy) - d.blackStride = 8 * h3 * mxx - } -} - -// Specified in section B.2.3. -func (d *decoder) processSOS(n int) error { - if d.nComp == 0 { - return FormatError("missing SOF marker") - } - if n < 6 || 4+2*d.nComp < n || n%2 != 0 { - return FormatError("SOS has wrong length") - } - if err := d.readFull(d.tmp[:n]); err != nil { - return err - } - nComp := int(d.tmp[0]) - if n != 4+2*nComp { - return FormatError("SOS length inconsistent with number of components") - } - var scan [maxComponents]struct { - compIndex uint8 - td uint8 // DC table selector. - ta uint8 // AC table selector. - } - totalHV := 0 - for i := 0; i < nComp; i++ { - cs := d.tmp[1+2*i] // Component selector. - compIndex := -1 - for j, comp := range d.comp[:d.nComp] { - if cs == comp.c { - compIndex = j - } - } - if compIndex < 0 { - return FormatError("unknown component selector") - } - scan[i].compIndex = uint8(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 - // B.2.2) are unique, it suffices to check that the implicit indexes - // into d.comp are unique. - for j := 0; j < i; j++ { - if scan[i].compIndex == scan[j].compIndex { - return FormatError("repeated component selector") - } - } - totalHV += d.comp[compIndex].h * d.comp[compIndex].v - - scan[i].td = d.tmp[2+2*i] >> 4 - if scan[i].td > maxTh { - return FormatError("bad Td value") - } - scan[i].ta = d.tmp[2+2*i] & 0x0f - if scan[i].ta > maxTh { - return FormatError("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 d.nComp > 1 && totalHV > 10 { - return FormatError("total sampling factors too large") - } - - // zigStart and zigEnd are the spectral selection bounds. - // ah and al are the successive approximation high and low values. - // The spec calls these values Ss, Se, Ah and Al. - // - // For progressive JPEGs, these are the two more-or-less independent - // aspects of progression. Spectral selection progression is when not - // all of a block's 64 DCT coefficients are transmitted in one pass. - // For example, three passes could transmit coefficient 0 (the DC - // component), coefficients 1-5, and coefficients 6-63, in zig-zag - // order. Successive approximation is when not all of the bits of a - // band of coefficients are transmitted in one pass. For example, - // three passes could transmit the 6 most significant bits, followed - // by the second-least significant bit, followed by the least - // significant bit. - // - // For baseline JPEGs, these parameters are hard-coded to 0/63/0/0. - zigStart, zigEnd, ah, al := int32(0), int32(blockSize-1), uint32(0), uint32(0) - if d.progressive { - zigStart = int32(d.tmp[1+2*nComp]) - zigEnd = int32(d.tmp[2+2*nComp]) - ah = uint32(d.tmp[3+2*nComp] >> 4) - al = uint32(d.tmp[3+2*nComp] & 0x0f) - if (zigStart == 0 && zigEnd != 0) || zigStart > zigEnd || blockSize <= zigEnd { - return FormatError("bad spectral selection bounds") - } - if zigStart != 0 && nComp != 1 { - return FormatError("progressive AC coefficients for more than one component") - } - if ah != 0 && ah != al+1 { - return FormatError("bad successive approximation values") - } - } - - // mxx and myy are the number of MCUs (Minimum Coded Units) in the image. - h0, v0 := d.comp[0].h, d.comp[0].v // The h and v values from the Y components. -fmt.Printf("w=%d h=%d h0=%d v0=%d\n", d.width, d.height, h0, v0); - mxx := (d.width + 8*h0 - 1) / (8 * h0) - myy := (d.height + 8*v0 - 1) / (8 * v0) - if d.img1 == nil && d.img3 == nil { - d.makeImg(mxx, myy) - } - if d.progressive { - for i := 0; i < nComp; i++ { - compIndex := scan[i].compIndex - if d.progCoeffs[compIndex] == nil { - d.progCoeffs[compIndex] = make([]block, mxx*myy*d.comp[compIndex].h*d.comp[compIndex].v) - } - } - } - - d.bits = bits{} - mcu, expectedRST := 0, uint8(rst0Marker) - var ( - // b is the decoded coefficients, in natural (not zig-zag) order. - b block - dc [maxComponents]int32 - // 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). - bx, by int - blockCount int - ) -fmt.Printf("mxx=%d myy=%d\n", mxx, myy); - for my := 0; my < myy; my++ { - for mx := 0; mx < mxx; mx++ { - for i := 0; i < nComp; i++ { - compIndex := scan[i].compIndex - hi := d.comp[compIndex].h - vi := d.comp[compIndex].v - qt := &d.quant[d.comp[compIndex].tq] - for j := 0; j < hi*vi; j++ { - -fmt.Printf("my=%d mx=%d i=%d j=%d\n", my, mx, i, j); - // The blocks are traversed one MCU at a time. For 4:2:0 chroma - // subsampling, there are four Y 8x8 blocks in every 16x16 MCU. - // - // For a baseline 32x16 pixel image, the Y blocks visiting order is: - // 0 1 4 5 - // 2 3 6 7 - // - // For progressive images, the interleaved scans (those with nComp > 1) - // are traversed as above, but non-interleaved scans are traversed left - // to right, top to bottom: - // 0 1 2 3 - // 4 5 6 7 - // Only DC scans (zigStart == 0) can be interleaved. AC scans must have - // only one component. - // - // To further complicate matters, for non-interleaved scans, there is no - // data for any blocks that are inside the image at the MCU level but - // outside the image at the pixel level. For example, a 24x16 pixel 4:2:0 - // progressive image consists of two 16x16 MCUs. The interleaved scans - // will process 8 Y blocks: - // 0 1 4 5 - // 2 3 6 7 - // The non-interleaved scans will process only 6 Y blocks: - // 0 1 2 - // 3 4 5 - if nComp != 1 { - bx = hi*mx + j%hi - by = vi*my + j/hi - } else { - q := mxx * hi - bx = blockCount % q - by = blockCount / q - blockCount++ - if bx*8 >= d.width || by*8 >= d.height { - continue - } - } - - // Load the previous partially decoded coefficients, if applicable. - if d.progressive { - b = d.progCoeffs[compIndex][by*mxx*hi+bx] - } else { - b = block{} - } - - if ah != 0 { - if err := d.refine(&b, &d.huff[acTable][scan[i].ta], zigStart, zigEnd, 1< 16 { - return UnsupportedError("excessive DC component") - } - dcDelta, err := d.receiveExtend(value) - if err != nil { - return err - } - dc[compIndex] += dcDelta - b[0] = dc[compIndex] << al - } - - if zig <= zigEnd && d.eobRun > 0 { - d.eobRun-- - } else { - // Decode the AC coefficients, as specified in section F.2.2.2. - huff := &d.huff[acTable][scan[i].ta] - for ; zig <= zigEnd; zig++ { - value, err := d.decodeHuffman(huff) -fmt.Printf("B %02X\n", value); - if err != nil { - return err - } - val0 := value >> 4 - val1 := value & 0x0f - if val1 != 0 { - zig += int32(val0) - if zig > zigEnd { - break - } - ac, err := d.receiveExtend(val1) - if err != nil { - return err - } - b[unzig[zig]] = ac << al - } else { - if val0 != 0x0f { - d.eobRun = uint16(1 << val0) - if val0 != 0 { - bits, err := d.decodeBits(int32(val0)) - if err != nil { - return err - } - d.eobRun |= uint16(bits) - } - d.eobRun-- - break - } - zig += 0x0f - } - } - } - } - - if d.progressive { - if zigEnd != blockSize-1 || al != 0 { - // We haven't completely decoded this 8x8 block. Save the coefficients. - d.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, - // the jpeg.Decode function does not return until the entire image is decoded, - // so we "continue" here to avoid wasted computation. - continue - } - } - - // Dequantize, perform the inverse DCT and store the block to the image. - for zig := 0; zig < blockSize; zig++ { - b[unzig[zig]] *= qt[zig] - } - idct(&b) - dst, stride := []byte(nil), 0 - if d.nComp == 1 { - dst, stride = d.img1.Pix[8*(by*d.img1.Stride+bx):], d.img1.Stride - } else { - switch compIndex { - case 0: - dst, stride = d.img3.Y[8*(by*d.img3.YStride+bx):], d.img3.YStride - case 1: - dst, stride = d.img3.Cb[8*(by*d.img3.CStride+bx):], d.img3.CStride - case 2: - dst, stride = d.img3.Cr[8*(by*d.img3.CStride+bx):], d.img3.CStride - case 3: - dst, stride = d.blackPix[8*(by*d.blackStride+bx):], d.blackStride - default: - return UnsupportedError("too many components") - } - } - // Level shift by +128, clip to [0, 255], and write to dst. - for y := 0; y < 8; y++ { - y8 := y * 8 - yStride := y * stride - for x := 0; x < 8; x++ { - c := b[y8+x] - if c < -128 { - c = 0 - } else if c > 127 { - c = 255 - } else { - c += 128 - } - dst[yStride+x] = uint8(c) - } - } - } // for j - } // for i - mcu++ - if d.ri > 0 && mcu%d.ri == 0 && mcu < mxx*myy { - // A more sophisticated decoder could use RST[0-7] markers to resynchronize from corrupt input, - // but this one assumes well-formed input, and hence the restart marker follows immediately. - if err := d.readFull(d.tmp[:2]); err != nil { - return err - } - if d.tmp[0] != 0xff || d.tmp[1] != expectedRST { - return FormatError("bad RST marker") - } - expectedRST++ - if expectedRST == rst7Marker+1 { - expectedRST = rst0Marker - } - // Reset the Huffman decoder. - d.bits = bits{} - // Reset the DC components, as per section F.2.1.3.1. - dc = [maxComponents]int32{} - // Reset the progressive decoder state, as per section G.1.2.2. - d.eobRun = 0 - } - } // for mx - } // for my - - return nil -} - -// refine decodes a successive approximation refinement block, as specified in -// section G.1.2. -func (d *decoder) refine(b *block, h *huffman, zigStart, zigEnd, delta int32) error { - // Refining a DC component is trivial. - if zigStart == 0 { - if zigEnd != 0 { - panic("unreachable") - } - bit, err := d.decodeBit() - if err != nil { - return err - } - if bit { - b[0] |= delta - } - return nil - } - - // Refining AC components is more complicated; see sections G.1.2.2 and G.1.2.3. - zig := zigStart - if d.eobRun == 0 { - loop: - for ; zig <= zigEnd; zig++ { - z := int32(0) - value, err := d.decodeHuffman(h) - if err != nil { - return err - } - val0 := value >> 4 - val1 := value & 0x0f - - switch val1 { - case 0: - if val0 != 0x0f { - d.eobRun = uint16(1 << val0) - if val0 != 0 { - bits, err := d.decodeBits(int32(val0)) - if err != nil { - return err - } - d.eobRun |= uint16(bits) - } - break loop - } - case 1: - z = delta - bit, err := d.decodeBit() - if err != nil { - return err - } - if !bit { - z = -z - } - default: - return FormatError("unexpected Huffman code") - } - - zig, err = d.refineNonZeroes(b, zig, zigEnd, int32(val0), delta) - if err != nil { - return err - } - if zig > zigEnd { - return FormatError("too many coefficients") - } - if z != 0 { - b[unzig[zig]] = z - } - } - } - if d.eobRun > 0 { - d.eobRun-- - if _, err := d.refineNonZeroes(b, zig, zigEnd, -1, delta); err != nil { - return err - } - } - return nil -} - -// refineNonZeroes refines non-zero entries of b in zig-zag order. If nz >= 0, -// the first nz zero entries are skipped over. -func (d *decoder) refineNonZeroes(b *block, zig, zigEnd, nz, delta int32) (int32, error) { - for ; zig <= zigEnd; zig++ { - u := unzig[zig] - if b[u] == 0 { - if nz == 0 { - break - } - nz-- - continue - } - bit, err := d.decodeBit() - if err != nil { - return 0, err - } - if !bit { - continue - } - if b[u] >= 0 { - b[u] += delta - } else { - b[u] -= delta - } - } - return zig, nil -} - - -// maxCodeLength is the maximum (inclusive) number of bits in a Huffman code. -const maxCodeLength = 16 - -// maxNCodes is the maximum (inclusive) number of codes in a Huffman tree. -const maxNCodes = 256 - -// lutSize is the log-2 size of the Huffman decoder's look-up table. -const lutSize = 8 - -// huffman is a Huffman decoder, specified in section C. -type huffman struct { - // length is the number of codes in the tree. - nCodes int32 - // lut is the look-up table for the next lutSize bits in the bit-stream. - // The high 8 bits of the uint16 are the encoded value. The low 8 bits - // are 1 plus the code length, or 0 if the value is too large to fit in - // lutSize bits. - lut [1 << lutSize]uint16 - // vals are the decoded values, sorted by their encoding. - vals [maxNCodes]uint8 - // minCodes[i] is the minimum code of length i, or -1 if there are no - // codes of that length. - minCodes [maxCodeLength]int32 - // maxCodes[i] is the maximum code of length i, or -1 if there are no - // codes of that length. - maxCodes [maxCodeLength]int32 - // valsIndices[i] is the index into vals of minCodes[i]. - valsIndices [maxCodeLength]int32 -} - -// errShortHuffmanData means that an unexpected EOF occurred while decoding -// Huffman data. -var errShortHuffmanData = FormatError("short Huffman data") - -// ensureNBits reads bytes from the byte buffer to ensure that d.bits.n is at -// least n. For best performance (avoiding function calls inside hot loops), -// the caller is the one responsible for first checking that d.bits.n < n. -func (d *decoder) ensureNBits(n int32) error { - for { - c, err := d.readByteStuffedByte() - if err != nil { - if err == io.EOF { - return errShortHuffmanData - } - return err - } - d.bits.a = d.bits.a<<8 | uint32(c) - d.bits.n += 8 - if d.bits.m == 0 { - d.bits.m = 1 << 7 - } else { - d.bits.m <<= 8 - } - if d.bits.n >= n { - break - } - } - return nil -} - -// receiveExtend is the composition of RECEIVE and EXTEND, specified in section -// F.2.2.1. -func (d *decoder) receiveExtend(t uint8) (int32, error) { - if d.bits.n < int32(t) { - if err := d.ensureNBits(int32(t)); err != nil { - return 0, err - } - } - d.bits.n -= int32(t) - d.bits.m >>= t - s := int32(1) << t - x := int32(d.bits.a>>uint8(d.bits.n)) & (s - 1) - if x < s>>1 { - x += ((-1) << t) + 1 - } - return x, nil -} - -// processDHT processes a Define Huffman Table marker, and initializes a huffman -// struct from its contents. Specified in section B.2.4.2. -func (d *decoder) processDHT(n int) error { - for n > 0 { - if n < 17 { - return FormatError("DHT has wrong length") - } - if err := d.readFull(d.tmp[:17]); err != nil { - return err - } - tc := d.tmp[0] >> 4 - if tc > maxTc { - return FormatError("bad Tc value") - } - th := d.tmp[0] & 0x0f - if th > maxTh || !d.progressive && th > 1 { - return FormatError("bad Th value") - } - h := &d.huff[tc][th] - - // Read nCodes and h.vals (and derive h.nCodes). - // nCodes[i] is the number of codes with code length i. - // h.nCodes is the total number of codes. - h.nCodes = 0 - var nCodes [maxCodeLength]int32 - for i := range nCodes { - nCodes[i] = int32(d.tmp[i+1]) - h.nCodes += nCodes[i] - } - if h.nCodes == 0 { - return FormatError("Huffman table has zero length") - } - if h.nCodes > maxNCodes { - return FormatError("Huffman table has excessive length") - } - n -= int(h.nCodes) + 17 - if n < 0 { - return FormatError("DHT has wrong length") - } - if err := d.readFull(h.vals[:h.nCodes]); err != nil { - return err - } - - // Derive the look-up table. - for i := range h.lut { - h.lut[i] = 0 - } - var x, code uint32 - for i := uint32(0); i < lutSize; i++ { - code <<= 1 - for j := int32(0); j < nCodes[i]; j++ { - // The codeLength is 1+i, so shift code by 8-(1+i) to - // calculate the high bits for every 8-bit sequence - // whose codeLength's high bits matches code. - // The high 8 bits of lutValue are the encoded value. - // The low 8 bits are 1 plus the codeLength. - base := uint8(code << (7 - i)) - lutValue := uint16(h.vals[x])<<8 | uint16(2+i) - for k := uint8(0); k < 1<<(7-i); k++ { - h.lut[base|k] = lutValue - } - code++ - x++ - } - } - - // Derive minCodes, maxCodes, and valsIndices. - var c, index int32 - for i, n := range nCodes { - if n == 0 { - h.minCodes[i] = -1 - h.maxCodes[i] = -1 - h.valsIndices[i] = -1 - } else { - h.minCodes[i] = c - h.maxCodes[i] = c + n - 1 - h.valsIndices[i] = index - c += n - index += n - } - c <<= 1 - } - } - return nil -} - -// decodeHuffman returns the next Huffman-coded value from the bit-stream, -// decoded according to h. -func (d *decoder) decodeHuffman(h *huffman) (uint8, error) { - if h.nCodes == 0 { - return 0, FormatError("uninitialized Huffman table") - } - - if d.bits.n < 8 { - if err := d.ensureNBits(8); err != nil { - if err != errMissingFF00 && err != errShortHuffmanData { - return 0, err - } - // There are no more bytes of data in this segment, but we may still - // be able to read the next symbol out of the previously read bits. - // First, undo the readByte that the ensureNBits call made. - if d.bytes.nUnreadable != 0 { - d.unreadByteStuffedByte() - } - goto slowPath - } - } - if v := h.lut[(d.bits.a>>uint32(d.bits.n-lutSize))&0xff]; v != 0 { - fmt.Printf("HD a=%08X idx=%d v=%04X\n", d.bits.a, (d.bits.a>>uint32(d.bits.n-lutSize))&0xff, v); - n := (v & 0xff) - 1 - d.bits.n -= int32(n) - d.bits.m >>= n - return uint8(v >> 8), nil - } - -slowPath: -fmt.Printf("slow\n"); - for i, code := 0, int32(0); i < maxCodeLength; i++ { - if d.bits.n == 0 { - if err := d.ensureNBits(1); err != nil { - return 0, err - } - } - if d.bits.a&d.bits.m != 0 { - code |= 1 - } - d.bits.n-- - d.bits.m >>= 1 - if code <= h.maxCodes[i] { - return h.vals[h.valsIndices[i]+code-h.minCodes[i]], nil - } - code <<= 1 - } - return 0, FormatError("bad Huffman code") -} - -func (d *decoder) decodeBit() (bool, error) { - if d.bits.n == 0 { - if err := d.ensureNBits(1); err != nil { - return false, err - } - } - ret := d.bits.a&d.bits.m != 0 - d.bits.n-- - d.bits.m >>= 1 - return ret, nil -} - -func (d *decoder) decodeBits(n int32) (uint32, error) { - if d.bits.n < n { - if err := d.ensureNBits(n); err != nil { - return 0, err - } - } - ret := d.bits.a >> uint32(d.bits.n-n) - ret &= (1 << uint32(n)) - 1 - d.bits.n -= n - d.bits.m >>= uint32(n) - return ret, nil -} diff --git a/src/ImageProcessorCore/Formats/Jpg/jpeg2/reader.go.REMOVED.git-id b/src/ImageProcessorCore/Formats/Jpg/jpeg2/reader.go.REMOVED.git-id new file mode 100644 index 0000000000..93ce9e6a39 --- /dev/null +++ b/src/ImageProcessorCore/Formats/Jpg/jpeg2/reader.go.REMOVED.git-id @@ -0,0 +1 @@ +388f7cd6fbe96c7f904ad8994ae174b9878efa4d \ No newline at end of file diff --git a/src/ImageProcessorCore/Formats/Jpg/jpeg2/writer.goa b/src/ImageProcessorCore/Formats/Jpg/jpeg2/writer.goa deleted file mode 100644 index 03c7181dfc..0000000000 --- a/src/ImageProcessorCore/Formats/Jpg/jpeg2/writer.goa +++ /dev/null @@ -1,614 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package golang - -import ( - "bufio" - "errors" - "image" - "image/color" - "io" -) - -// min returns the minimum of two integers. -func min(x, y int) int { - if x < y { - return x - } - return y -} - -// div returns a/b rounded to the nearest integer, instead of rounded to zero. -func div(a, b int32) int32 { - if a >= 0 { - return (a + (b >> 1)) / b - } - return -((-a + (b >> 1)) / b) -} - -// bitCount counts the number of bits needed to hold an integer. -var bitCount = [256]byte{ - 0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, - 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, -} - -type quantIndex int - -const ( - quantIndexLuminance quantIndex = iota - quantIndexChrominance - nQuantIndex -) - -// unscaledQuant are the unscaled quantization tables in zig-zag order. Each -// encoder copies and scales the tables according to its quality parameter. -// The values are derived from section K.1 after converting from natural to -// zig-zag order. -var unscaledQuant = [nQuantIndex][blockSize]byte{ - // Luminance. - { - 16, 11, 12, 14, 12, 10, 16, 14, - 13, 14, 18, 17, 16, 19, 24, 40, - 26, 24, 22, 22, 24, 49, 35, 37, - 29, 40, 58, 51, 61, 60, 57, 51, - 56, 55, 64, 72, 92, 78, 64, 68, - 87, 69, 55, 56, 80, 109, 81, 87, - 95, 98, 103, 104, 103, 62, 77, 113, - 121, 112, 100, 120, 92, 101, 103, 99, - }, - // Chrominance. - { - 17, 18, 18, 24, 21, 24, 47, 26, - 26, 47, 99, 66, 56, 66, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - }, -} - -type huffIndex int - -const ( - huffIndexLuminanceDC huffIndex = iota - huffIndexLuminanceAC - huffIndexChrominanceDC - huffIndexChrominanceAC - nHuffIndex -) - -// huffmanSpec specifies a Huffman encoding. -type huffmanSpec struct { - // count[i] is the number of codes of length i bits. - count [16]byte - // value[i] is the decoded value of the i'th codeword. - value []byte -} - -// theHuffmanSpec is the Huffman encoding specifications. -// This encoder uses the same Huffman encoding for all images. -var theHuffmanSpec = [nHuffIndex]huffmanSpec{ - // Luminance DC. - { - [16]byte{0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}, - []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, - }, - // Luminance AC. - { - [16]byte{0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 125}, - []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, - }, - }, - // Chrominance DC. - { - [16]byte{0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}, - []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, - }, - // Chrominance AC. - { - [16]byte{0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 119}, - []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. -// Each value maps to a uint32 of which the 8 most significant bits hold the -// codeword size in bits and the 24 least significant bits hold the codeword. -// The maximum codeword size is 16 bits. -type huffmanLUT []uint32 - -func (h *huffmanLUT) init(s huffmanSpec) { - maxValue := 0 - for _, v := range s.value { - if int(v) > maxValue { - maxValue = int(v) - } - } - *h = make([]uint32, maxValue+1) - code, k := uint32(0), 0 - for i := 0; i < len(s.count); i++ { - nBits := uint32(i+1) << 24 - for j := uint8(0); j < s.count[i]; j++ { - (*h)[s.value[k]] = nBits | code - code++ - k++ - } - code <<= 1 - } -} - -// theHuffmanLUT are compiled representations of theHuffmanSpec. -var theHuffmanLUT [4]huffmanLUT - -func init() { - for i, s := range theHuffmanSpec { - theHuffmanLUT[i].init(s) - } -} - -// writer is a buffered writer. -type writer interface { - Flush() error - io.Writer - io.ByteWriter -} - -// encoder encodes an image to the JPEG format. -type encoder struct { - // 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. - w writer - err error - // buf is a scratch buffer. - buf [16]byte - // bits and nBits are accumulated bits to write to w. - bits, nBits uint32 - // quant is the scaled quantization tables, in zig-zag order. - quant [nQuantIndex][blockSize]byte -} - -func (e *encoder) flush() { - if e.err != nil { - return - } - e.err = e.w.Flush() -} - -func (e *encoder) write(p []byte) { - if e.err != nil { - return - } - _, e.err = e.w.Write(p) -} - -func (e *encoder) writeByte(b byte) { - if e.err != nil { - return - } - e.err = e.w.WriteByte(b) -} - -// emit emits the least significant nBits bits of bits to the bit-stream. -// The precondition is bits < 1<= 8 { - b := uint8(bits >> 24) - e.writeByte(b) - if b == 0xff { - e.writeByte(0x00) - } - bits <<= 8 - nBits -= 8 - } - e.bits, e.nBits = bits, nBits -} - -// emitHuff emits the given value with the given Huffman encoder. -func (e *encoder) emitHuff(h huffIndex, value int32) { - x := theHuffmanLUT[h][value] - e.emit(x&(1<<24-1), x>>24) -} - -// emitHuffRLE emits a run of runLength copies of value encoded with the given -// Huffman encoder. -func (e *encoder) emitHuffRLE(h huffIndex, runLength, value int32) { - a, b := value, value - if a < 0 { - a, b = -value, value-1 - } - var nBits uint32 - if a < 0x100 { - nBits = uint32(bitCount[a]) - } else { - nBits = 8 + uint32(bitCount[a>>8]) - } - e.emitHuff(h, runLength<<4|int32(nBits)) - if nBits > 0 { - e.emit(uint32(b)&(1<> 8) - e.buf[3] = uint8(markerlen & 0xff) - e.write(e.buf[:4]) -} - -// writeDQT writes the Define Quantization Table marker. -func (e *encoder) writeDQT() { - const markerlen = 2 + int(nQuantIndex)*(1+blockSize) - e.writeMarkerHeader(dqtMarker, markerlen) - for i := range e.quant { - e.writeByte(uint8(i)) - e.write(e.quant[i][:]) - } -} - -// writeSOF0 writes the Start Of Frame (Baseline) marker. -func (e *encoder) writeSOF0(size image.Point, nComponent int) { - markerlen := 8 + 3*nComponent - e.writeMarkerHeader(sof0Marker, markerlen) - e.buf[0] = 8 // 8-bit color. - e.buf[1] = uint8(size.Y >> 8) - e.buf[2] = uint8(size.Y & 0xff) - e.buf[3] = uint8(size.X >> 8) - e.buf[4] = uint8(size.X & 0xff) - e.buf[5] = uint8(nComponent) - if nComponent == 1 { - e.buf[6] = 1 - // No subsampling for grayscale image. - e.buf[7] = 0x11 - e.buf[8] = 0x00 - } else { - for i := 0; i < nComponent; i++ { - e.buf[3*i+6] = uint8(i + 1) - // We use 4:2:0 chroma subsampling. - e.buf[3*i+7] = "\x22\x11\x11"[i] - e.buf[3*i+8] = "\x00\x01\x01"[i] - } - } - e.write(e.buf[:3*(nComponent-1)+9]) -} - -// writeDHT writes the Define Huffman Table marker. -func (e *encoder) writeDHT(nComponent int) { - markerlen := 2 - specs := theHuffmanSpec[:] - if nComponent == 1 { - // Drop the Chrominance tables. - specs = specs[:2] - } - for _, s := range specs { - markerlen += 1 + 16 + len(s.value) - } - e.writeMarkerHeader(dhtMarker, markerlen) - for i, s := range specs { - e.writeByte("\x00\x10\x01\x11"[i]) - e.write(s.count[:]) - e.write(s.value) - } -} - -// writeBlock writes a block of pixel data using the given quantization table, -// returning the post-quantized DC value of the DCT-transformed block. b is in -// natural (not zig-zag) order. -func (e *encoder) writeBlock(b *block, q quantIndex, prevDC int32) int32 { - fdct(b) - // Emit the DC delta. - dc := div(b[0], 8*int32(e.quant[q][0])) - e.emitHuffRLE(huffIndex(2*q+0), 0, dc-prevDC) - // Emit the AC components. - h, runLength := huffIndex(2*q+1), int32(0) - for zig := 1; zig < blockSize; zig++ { - ac := div(b[unzig[zig]], 8*int32(e.quant[q][zig])) - if ac == 0 { - runLength++ - } else { - for runLength > 15 { - e.emitHuff(h, 0xf0) - runLength -= 16 - } - e.emitHuffRLE(h, runLength, ac) - runLength = 0 - } - } - if runLength > 0 { - e.emitHuff(h, 0x00) - } - return dc -} - -// toYCbCr converts the 8x8 region of m whose top-left corner is p to its -// YCbCr values. -func toYCbCr(m image.Image, 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++ { - for i := 0; i < 8; i++ { - r, g, b, _ := m.At(min(p.X+i, xmax), min(p.Y+j, ymax)).RGBA() - yy, cb, cr := color.RGBToYCbCr(uint8(r>>8), uint8(g>>8), uint8(b>>8)) - yBlock[8*j+i] = int32(yy) - cbBlock[8*j+i] = int32(cb) - crBlock[8*j+i] = int32(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]) - } - } -} - -// 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. -func scale(dst *block, src *[4]block) { - for i := 0; i < 4; i++ { - dstOff := (i&2)<<4 | (i&1)<<2 - for y := 0; y < 4; y++ { - for x := 0; x < 4; x++ { - j := 16*y + 2*x - sum := src[i][j] + src[i][j+1] + src[i][j+8] + src[i][j+9] - dst[8*y+x+dstOff] = (sum + 2) >> 2 - } - } - } -} - -// sosHeaderY is the SOS marker "\xff\xda" followed by 8 bytes: -// - the marker length "\x00\x08", -// - the number of components "\x01", -// - component 1 uses DC table 0 and AC table 0 "\x01\x00", -// - the bytes "\x00\x3f\x00". Section B.2.3 of the spec says that for -// sequential DCTs, those bytes (8-bit Ss, 8-bit Se, 4-bit Ah, 4-bit Al) -// should be 0x00, 0x3f, 0x00<<4 | 0x00. -var sosHeaderY = []byte{ - 0xff, 0xda, 0x00, 0x08, 0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, -} - -// sosHeaderYCbCr is the SOS marker "\xff\xda" followed by 12 bytes: -// - the marker length "\x00\x0c", -// - the number of components "\x03", -// - component 1 uses DC table 0 and AC table 0 "\x01\x00", -// - component 2 uses DC table 1 and AC table 1 "\x02\x11", -// - component 3 uses DC table 1 and AC table 1 "\x03\x11", -// - the bytes "\x00\x3f\x00". Section B.2.3 of the spec says that for -// sequential DCTs, those bytes (8-bit Ss, 8-bit Se, 4-bit Ah, 4-bit Al) -// should be 0x00, 0x3f, 0x00<<4 | 0x00. -var sosHeaderYCbCr = []byte{ - 0xff, 0xda, 0x00, 0x0c, 0x03, 0x01, 0x00, 0x02, - 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00, -} - -// writeSOS writes the StartOfScan marker. -func (e *encoder) writeSOS(m image.Image) { - switch m.(type) { - case *image.Gray: - e.write(sosHeaderY) - default: - e.write(sosHeaderYCbCr) - } - var ( - // Scratch buffers to hold the YCbCr values. - // The blocks are in natural (not zig-zag) order. - b block - cb, cr [4]block - // DC components are delta-encoded. - prevDCY, prevDCCb, prevDCCr int32 - ) - bounds := m.Bounds() - switch m := m.(type) { - // TODO(wathiede): switch on m.ColorModel() instead of type. - case *image.Gray: - for y := bounds.Min.Y; y < bounds.Max.Y; y += 8 { - for x := bounds.Min.X; x < bounds.Max.X; x += 8 { - p := image.Pt(x, y) - grayToY(m, p, &b) - prevDCY = e.writeBlock(&b, 0, prevDCY) - } - } - default: - rgba, _ := m.(*image.RGBA) - for y := bounds.Min.Y; y < bounds.Max.Y; y += 16 { - for x := bounds.Min.X; x < bounds.Max.X; x += 16 { - for i := 0; i < 4; i++ { - xOff := (i & 1) * 8 - yOff := (i & 2) * 4 - p := image.Pt(x+xOff, y+yOff) - if rgba != nil { - rgbaToYCbCr(rgba, p, &b, &cb[i], &cr[i]) - } else { - toYCbCr(m, p, &b, &cb[i], &cr[i]) - } - prevDCY = e.writeBlock(&b, 0, prevDCY) - } - scale(&b, &cb) - prevDCCb = e.writeBlock(&b, 1, prevDCCb) - scale(&b, &cr) - prevDCCr = e.writeBlock(&b, 1, prevDCCr) - } - } - } - // Pad the last byte with 1's. - e.emit(0x7f, 7) -} - -// DefaultQuality is the default quality encoding parameter. -const DefaultQuality = 75 - -// Options are the encoding parameters. -// Quality ranges from 1 to 100 inclusive, higher is better. -type Options struct { - Quality int -} - -// 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. -func Encode(w io.Writer, m image.Image, o *Options) error { - b := m.Bounds() - if b.Dx() >= 1<<16 || b.Dy() >= 1<<16 { - return errors.New("jpeg: image is too large to encode") - } - var e encoder - if ww, ok := w.(writer); ok { - e.w = ww - } else { - e.w = bufio.NewWriter(w) - } - // Clip quality to [1, 100]. - quality := DefaultQuality - if o != nil { - quality = o.Quality - if quality < 1 { - quality = 1 - } else if quality > 100 { - quality = 100 - } - } - // Convert from a quality rating to a scaling factor. - var scale int - if quality < 50 { - scale = 5000 / quality - } else { - scale = 200 - quality*2 - } - // Initialize the quantization tables. - for i := range e.quant { - for j := range e.quant[i] { - x := int(unscaledQuant[i][j]) - x = (x*scale + 50) / 100 - if x < 1 { - x = 1 - } else if x > 255 { - x = 255 - } - e.quant[i][j] = uint8(x) - } - } - // Compute number of components based on input image type. - nComponent := 3 - switch m.(type) { - // TODO(wathiede): switch on m.ColorModel() instead of type. - case *image.Gray: - nComponent = 1 - } - // Write the Start Of Image marker. - e.buf[0] = 0xff - e.buf[1] = 0xd8 - e.write(e.buf[:2]) - // Write the quantization tables. - e.writeDQT() - // Write the image dimensions. - e.writeSOF0(b.Size(), nComponent) - // Write the Huffman tables. - e.writeDHT(nComponent) - // Write the image data. - e.writeSOS(m) - // Write the End Of Image marker. - e.buf[0] = 0xff - e.buf[1] = 0xd9 - e.write(e.buf[:2]) - e.flush() - return e.err -} diff --git a/src/ImageProcessorCore/Formats/Jpg/main.go b/src/ImageProcessorCore/Formats/Jpg/main.go index a68322aa27..22f6067f57 100644 --- a/src/ImageProcessorCore/Formats/Jpg/main.go +++ b/src/ImageProcessorCore/Formats/Jpg/main.go @@ -1,12 +1,20 @@ package main -import "fmt" -import "os" -import "./jpeg2" +import ( + "fmt" + "os" + "image" + "./jpeg2" + _ "image/png" +) func main() { - f, err := os.Open("/Users/mweber/dev/fprint/print.jpg"); + f, err := os.Open("/Users/mweber/dev/ImageProcessor/src/ImageProcessorCore/a.png"); + m, _, err := image.Decode(f) + + f2, err := os.Create("/Users/mweber/dev/ImageProcessor/src/ImageProcessorCore/go.jpg"); + if err == nil { - jpeg2.Decode(f); + jpeg2.Encode(f2, m, nil); fmt.Println("done") } }