diff --git a/src/ImageProcessorCore/Formats/Jpg/Block.cs b/src/ImageProcessorCore/Formats/Jpg/Block.cs new file mode 100644 index 000000000..ac904f48b --- /dev/null +++ b/src/ImageProcessorCore/Formats/Jpg/Block.cs @@ -0,0 +1,18 @@ +namespace ImageProcessorCore.Formats.Jpg +{ + using System; + using System.IO; + + public class Block + { + public const int blockSize = 64; + private int[] _data; + + public Block() + { + _data = new int[blockSize]; + } + + 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 new file mode 100644 index 000000000..0c00a51bc --- /dev/null +++ b/src/ImageProcessorCore/Formats/Jpg/Colors.cs @@ -0,0 +1,45 @@ +namespace ImageProcessorCore.Formats.Jpg +{ + using System; + using System.IO; + + public static class Colors + { + public static void RGBToYCbCr(byte r, byte g, byte b, out byte yy, out byte cr, out byte cb) + { + // 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/Component.cs b/src/ImageProcessorCore/Formats/Jpg/Component.cs new file mode 100644 index 000000000..d5a0273ea --- /dev/null +++ b/src/ImageProcessorCore/Formats/Jpg/Component.cs @@ -0,0 +1,13 @@ +namespace ImageProcessorCore.Formats.Jpg +{ + using System; + using System.IO; + + public class Component + { + public int h; // Horizontal sampling factor. + public int v; // Vertical sampling factor. + public byte c; // Component identifier. + public byte tq; // Quantization table destination selector. + } +} diff --git a/src/ImageProcessorCore/Formats/Jpg/Decoder.cs b/src/ImageProcessorCore/Formats/Jpg/Decoder.cs new file mode 100644 index 000000000..d0e3c3301 --- /dev/null +++ b/src/ImageProcessorCore/Formats/Jpg/Decoder.cs @@ -0,0 +1,1646 @@ +namespace ImageProcessorCore.Formats.Jpg +{ + using System; + using System.IO; + + public partial class Decoder + { + private class errMissingFF00 : Exception { } + private class errShortHuffmanData : Exception { } + private class errEOF : Exception { } + + // maxCodeLength is the maximum (inclusive) number of bits in a Huffman code. + private const int maxCodeLength = 16; + // maxNCodes is the maximum (inclusive) number of codes in a Huffman tree. + private const int maxNCodes = 256; + // lutSize is the log-2 size of the Huffman decoder's look-up table. + private const int lutSize = 8; + private const int maxComponents = 4; + private const int maxTc = 1; + private const int maxTh = 3; + private const int maxTq = 3; + private const int dcTable = 0; + private const int acTable = 1; + + 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; + + // See http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html#Adobe + private const int adobeTransformUnknown = 0; + private const int adobeTransformYCbCr = 1; + private const int 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). + 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, + }; + + public Decoder() + { + huff = new huffman_class[maxTc + 1, maxTh + 1]; + quant = new Block[maxTq + 1]; + tmp = new byte[2 * Block.blockSize]; + comp = new Component[maxComponents]; + progCoeffs = new Block[maxComponents][]; + bits = new bits_class(); + bytes = new bytes_class(); + + for(int i = 0; i < maxTc + 1; i++) + for(int j = 0; j < maxTh + 1; j++) + huff[i, j] = new huffman_class(); + + for(int i = 0; i < quant.Length; i++) + quant[i] = new Block(); + + for(int i = 0; i < comp.Length; i++) + comp[i] = new Component(); + } + + public class img_ycbcr + { + public enum YCbCrSubsampleRatio + { + YCbCrSubsampleRatio444, + YCbCrSubsampleRatio422, + YCbCrSubsampleRatio420, + YCbCrSubsampleRatio440, + YCbCrSubsampleRatio411, + YCbCrSubsampleRatio410, + } + + private static void yCbCrSize(int w, int h, YCbCrSubsampleRatio ratio, out int cw, out int ch) + { + switch(ratio) + { + case YCbCrSubsampleRatio.YCbCrSubsampleRatio422: + cw = (w+1)/2; + ch = h; + break; + case YCbCrSubsampleRatio.YCbCrSubsampleRatio420: + cw = (w+1)/2; + ch = (h+1)/2; + break; + case YCbCrSubsampleRatio.YCbCrSubsampleRatio440: + cw = w; + ch = (h+1)/2; + break; + case YCbCrSubsampleRatio.YCbCrSubsampleRatio411: + cw = (w+3)/4; + ch = h; + break; + case YCbCrSubsampleRatio.YCbCrSubsampleRatio410: + cw = (w+3)/4; + ch = (h+1)/2; + break; + default: + // Default to 4:4:4 subsampling. + cw = w; + ch = h; + break; + } + } + + private img_ycbcr() + { + } + + public byte[] pix_y, pix_cb, pix_cr; + public int y_stride, c_stride; + public int y_offset, c_offset; + public int x, y, w, h; + public YCbCrSubsampleRatio ratio; + + public img_ycbcr(int w, int h, YCbCrSubsampleRatio ratio) + { + int cw, ch; + yCbCrSize(w, h, ratio, out cw, out ch); + this.pix_y = new byte[w*h]; + this.pix_cb = new byte[cw*ch]; + this.pix_cr = new byte[cw*ch]; + this.ratio = ratio; + this.y_stride = w; + this.c_stride = cw; + this.x = 0; + this.y = 0; + this.w = w; + this.h = h; + } + + public img_ycbcr subimage(int x, int y, int w, int h) + { + var ret = new img_ycbcr(); + ret.w = w; + ret.h = h; + ret.pix_y = pix_y; + ret.pix_cb = pix_cb; + ret.pix_cr = pix_cr; + ret.ratio = ratio; + ret.y_stride = y_stride; + ret.c_stride = c_stride; + ret.y_offset = y * y_stride + x; + ret.c_offset = y * c_stride + x; + return ret; + } + + public int get_row_y_offset(int y) + { + return y * y_stride; + } + + public int get_row_c_offset(int y) + { + switch(ratio) + { + case YCbCrSubsampleRatio.YCbCrSubsampleRatio422: + return y * c_stride; + case YCbCrSubsampleRatio.YCbCrSubsampleRatio420: + return (y/2) * c_stride; + case YCbCrSubsampleRatio.YCbCrSubsampleRatio440: + return (y/2) * c_stride; + case YCbCrSubsampleRatio.YCbCrSubsampleRatio411: + return y * c_stride; + case YCbCrSubsampleRatio.YCbCrSubsampleRatio410: + return (y/2) * c_stride; + default: + return y * c_stride; + } + } + } + + public class img_rgb + { + public byte[] pixels; + public int stride; + public int w, h; + + private img_rgb() + { + } + + public img_rgb(int w, int h) + { + this.w = w; + this.h = h; + this.pixels = new byte[w*h*3]; + this.stride = w*3; + } + + public int get_row_offset(int y) + { + return y * stride; + } + } + + public class img_gray + { + public byte[] pixels; + public int stride; + public int x, y, w, h; + public int offset; + + private img_gray() + { + } + + public img_gray(int w, int h) + { + this.w = w; + this.h = h; + this.pixels = new byte[w*h]; + this.stride = w; + this.offset = 0; + } + + public img_gray subimage(int x, int y, int w, int h) + { + var ret = new img_gray(); + ret.w = w; + ret.h = h; + ret.pixels = pixels; + ret.stride = stride; + ret.offset = y * stride + x; + return ret; + } + + public int get_row_offset(int y) + { + return offset + y * stride; + } + } + + private class huffman_class + { + public huffman_class() + { + lut = new ushort[1 << lutSize]; + vals = new byte[maxNCodes]; + minCodes = new int[maxCodeLength]; + maxCodes = new int[maxCodeLength]; + valsIndices = new int[maxCodeLength]; + nCodes = 0; + } + + // length is the number of codes in the tree. + public int nCodes; + // 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. + public ushort[] lut; + // vals are the decoded values, sorted by their encoding. + public byte[] vals; + // minCodes[i] is the minimum code of length i, or -1 if there are no + // codes of that length. + public int[] minCodes; + // maxCodes[i] is the maximum code of length i, or -1 if there are no + // codes of that length. + public int[] maxCodes; + // valsIndices[i] is the index into vals of minCodes[i]. + public int[] valsIndices; + } + + // 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. + private class bytes_class + { + public bytes_class() + { + buf = new byte[4096]; + i = 0; + j = 0; + nUnreadable = 0; + } + + // buf[i:j] are the buffered bytes read from the underlying + // io.Reader that haven't yet been passed further on. + public byte[] buf; + public int i, j; + // nUnreadable is the number of bytes to back up i after + // overshooting. It can be 0, 1 or 2. + public int nUnreadable; + } + + // 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. + private class bits_class + { + public uint a; // accumulator. + public uint m; // mask. m==1<<(n-1) when n>0, with m==0 when n==0. + public int n; // the number of unread bits in a. + } + + public int width, height; + public int nComp; + public img_gray img1; //grayscale + public img_ycbcr img3; //YCrCb + public img_rgb imgrgb; //YCrCb + + private Stream r; + private bits_class bits; + private bytes_class bytes; + private byte[] blackPix; + private int blackStride; + + private int ri; // Restart Interval. + private bool progressive; + private bool jfif; + private bool adobeTransformValid; + private byte adobeTransform; + private ushort eobRun; // End-of-Band run, specified in section G.1.2.2. + private Component[] comp; + private Block[][] progCoeffs; // Saved state between progressive-mode scans. + private huffman_class[,] huff; + private Block[] quant; // Quantization tables, in zig-zag order. + private byte[] tmp; + + // ensureNBits reads bytes from the byte buffer to ensure that 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 bits.n < n. + private void ensureNBits(int n) + { + while(true) + { + var c = readByteStuffedByte(); + bits.a = bits.a<<8 | (uint)c; + bits.n += 8; + if(bits.m == 0) + bits.m = 1 << 7; + else + bits.m <<= 8; + if(bits.n >= n) + break; + } + } + + // receiveExtend is the composition of RECEIVE and EXTEND, specified in section + // F.2.2.1. + public int receiveExtend(byte t) + { + if(bits.n < t) + ensureNBits(t); + + bits.n -= t; + bits.m >>= t; + int s = 1 << t; + int x = (int)((bits.a>>bits.n) & (s - 1)); + if(x < (s>>1)) + x += ((-1) << t) + 1; + return x; + } + + // processDHT processes a Define Huffman Table marker, and initializes a huffman + // struct from its contents. Specified in section B.2.4.2. + public void processDHT(int n) + { + while(n > 0) + { + if(n < 17) + throw new Exception("DHT has wrong length"); + + readFull(tmp, 0, 17); + + int tc = tmp[0] >> 4; + if(tc > maxTc) + throw new Exception("bad Tc value"); + + int th = tmp[0] & 0x0f; + if(th > maxTh || !progressive && th > 1) + throw new Exception("bad Th value"); + + huffman_class h = 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; + + int[] ncodes = new int[maxCodeLength]; + for(int i = 0; i < ncodes.Length; i++) + { + ncodes[i] = tmp[i+1]; + h.nCodes += ncodes[i]; + } + + if(h.nCodes == 0) + throw new Exception("Huffman table has zero length"); + if(h.nCodes > maxNCodes) + throw new Exception("Huffman table has excessive length"); + + n -= h.nCodes + 17; + if(n < 0) + throw new Exception("DHT has wrong length"); + + readFull(h.vals, 0, h.nCodes); + + // Derive the look-up table. + for(int i = 0; i < h.lut.Length; i++) + h.lut[i] = 0; + + uint x = 0, code = 0; + + for(int i = 0; i < lutSize; i++) + { + code <<= 1; + + for(int j = 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. + byte base2 = (byte)(code << (7 - i)); + ushort lutValue = (ushort)(((ushort)h.vals[x]<<8) | (2+i)); + for(int k = 0; k < 1<<(7-i); k++) + h.lut[base2|k] = lutValue; + code++; + x++; + } + } + + // Derive minCodes, maxCodes, and valsIndices. + int c = 0, index = 0; + for(int i = 0; i < ncodes.Length; i++) + { + int nc = ncodes[i]; + if(nc == 0) + { + h.minCodes[i] = -1; + h.maxCodes[i] = -1; + h.valsIndices[i] = -1; + } + else + { + h.minCodes[i] = c; + h.maxCodes[i] = c + nc - 1; + h.valsIndices[i] = index; + c += nc; + index += nc; + } + c <<= 1; + } + } + } + + // decodeHuffman returns the next Huffman-coded value from the bit-stream, + // decoded according to h. + private byte decodeHuffman(huffman_class h) + { + if(h.nCodes == 0) + throw new Exception("uninitialized Huffman table"); + + if(bits.n < 8) + { + try + { + ensureNBits(8); + } + catch(errMissingFF00) + { + if(bytes.nUnreadable != 0) + unreadByteStuffedByte(); + goto slowPath; + } + catch(errShortHuffmanData) + { + if(bytes.nUnreadable != 0) + unreadByteStuffedByte(); + goto slowPath; + } + } + + ushort v = h.lut[(bits.a>>(bits.n-lutSize))&0xff]; + if(v != 0) + { + byte n = (byte)((v & 0xff) - 1); + bits.n -= n; + bits.m >>= n; + return (byte)(v >> 8); + } + + slowPath: + int code = 0; + for(int i = 0; i < maxCodeLength; i++) + { + if(bits.n == 0) + ensureNBits(1); + if((bits.a & bits.m) != 0) + code |= 1; + bits.n--; + bits.m >>= 1; + if(code <= h.maxCodes[i]) + return h.vals[h.valsIndices[i]+code-h.minCodes[i]]; + code <<= 1; + } + + throw new Exception("bad Huffman code"); + } + + public bool decodeBit() + { + if(bits.n == 0) + ensureNBits(1); + + bool ret = (bits.a & bits.m) != 0; + bits.n--; + bits.m >>= 1; + return ret; + } + + public uint decodeBits(int n) + { + if(bits.n < n) + ensureNBits(n); + + uint ret = bits.a >> (bits.n-n); + ret = (uint)(ret & ((1 << n) - 1)); + bits.n -= n; + bits.m >>= n; + return ret; + } + + // fill fills up the bytes.buf buffer from the underlying io.Reader. It + // should only be called when there are no unread bytes in bytes. + public void fill() + { + if(bytes.i != bytes.j) + throw new Exception("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(bytes.j > 2) + { + bytes.buf[0] = bytes.buf[bytes.j-2]; + bytes.buf[1] = bytes.buf[bytes.j-1]; + bytes.i = 2; + bytes.j = 2; + } + + // Fill in the rest of the buffer. + int n = r.Read(bytes.buf, bytes.j, bytes.buf.Length - bytes.j); + if(n == 0) + throw new errEOF(); + bytes.j += n; + } + + // unreadByteStuffedByte undoes the most recent readByteStuffedByte call, + // giving a byte of data back from bits to 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. + public void unreadByteStuffedByte() + { + bytes.i -= bytes.nUnreadable; + bytes.nUnreadable = 0; + if(bits.n >= 8) + { + bits.a >>= 8; + bits.n -= 8; + bits.m >>= 8; + } + } + + // readByte returns the next byte, whether buffered or not buffere It does + // not care about byte stuffing. + public byte readByte() + { + while(bytes.i == bytes.j) + fill(); + byte x = bytes.buf[bytes.i]; + bytes.i++; + bytes.nUnreadable = 0; + return x; + } + + // readByteStuffedByte is like readByte but is for byte-stuffed Huffman data. + public byte readByteStuffedByte() + { + byte x; + + // Take the fast path if bytes.buf contains at least two bytes. + if(bytes.i+2 <= bytes.j) + { + x = bytes.buf[bytes.i]; + bytes.i++; + bytes.nUnreadable = 1; + if(x != 0xff) + return x; + if(bytes.buf[bytes.i] != 0x00) + throw new errMissingFF00(); + bytes.i++; + bytes.nUnreadable = 2; + return 0xff; + } + + bytes.nUnreadable = 0; + + x = readByte(); + bytes.nUnreadable = 1; + if(x != 0xff) + return x; + x = readByte(); + bytes.nUnreadable = 2; + if(x != 0x00) + throw new errMissingFF00(); + return 0xff; + } + + // readFull reads exactly len(p) bytes into p. It does not care about byte + // stuffing. + void readFull(byte[] data, int offset, int len) + { + // Unread the overshot bytes, if any. + if(bytes.nUnreadable != 0) + { + if(bits.n >= 8) + unreadByteStuffedByte(); + bytes.nUnreadable = 0; + } + + while(len > 0) + { + if(bytes.j - bytes.i >= len) + { + Array.Copy(bytes.buf, bytes.i, data, offset, len); + bytes.i += len; + len -= len; + } + else + { + Array.Copy(bytes.buf, bytes.i, data, offset, bytes.j - bytes.i); + bytes.i += bytes.j - bytes.i; + offset += bytes.j - bytes.i; + len -= bytes.j - bytes.i; + + fill(); + } + } + } + + // ignore ignores the next n bytes. + public void ignore(int n) + { + // Unread the overshot bytes, if any. + if(bytes.nUnreadable != 0) + { + if(bits.n >= 8) + unreadByteStuffedByte(); + bytes.nUnreadable = 0; + } + + while(true) + { + int m = bytes.j - bytes.i; + if(m > n) m = n; + bytes.i += m; + n -= m; + if(n == 0) + break; + else + fill(); + } + } + + // Specified in section B.2.2. + public void processSOF(int n) + { + if(nComp != 0) + throw new Exception("multiple SOF markers"); + + switch(n) + { + case 6 + 3*1: // Grayscale image. + nComp = 1; + break; + case 6 + 3*3: // YCbCr or RGB image. + nComp = 3; + break; + case 6 + 3*4: // YCbCrK or CMYK image. + nComp = 4; + break; + default: + throw new Exception("number of components"); + } + + readFull(tmp, 0, n); + + // We only support 8-bit precision. + if(tmp[0] != 8) + throw new Exception("precision"); + + height = (tmp[1]<<8) + tmp[2]; + width = (tmp[3]<<8) + tmp[4]; + if(tmp[5] != nComp) + throw new Exception("SOF has wrong length"); + + for(int i = 0; i < nComp; i++) + { + comp[i].c = 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(int j = 0; j < i; j++) + { + if(comp[i].c == comp[j].c) + throw new Exception("repeated component identifier"); + } + + comp[i].tq = tmp[8+3*i]; + if(comp[i].tq > maxTq) + throw new Exception("bad Tq value"); + + byte hv = tmp[7+3*i]; + int h = hv>>4; + int v = hv&0x0f; + if(h < 1 || 4 < h || v < 1 || 4 < v) + throw new Exception("luma/chroma subsampling ratio"); + if(h == 3 || v == 3) + throw new Exception("unsupported subsampling ratio"); + + switch(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 = 1; + v = 1; + break; + } + + 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) + throw new Exception("unsupported subsampling ratio"); + break; + } + case 1: // Cb. + { + if(comp[0].h%h != 0 || comp[0].v%v != 0) + throw new Exception("unsupported subsampling ratio"); + break; + } + case 2: // Cr. + { + if(comp[1].h != h || comp[1].v != v) + throw new Exception("unsupported subsampling ratio"); + break; + } + } + break; + } + + 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) + throw new Exception("unsupported subsampling ratio"); + break; + case 1: + case 2: + if(hv != 0x11) + throw new Exception("unsupported subsampling ratio"); + break; + case 3: + if(comp[0].h != h || comp[0].v != v) + throw new Exception("unsupported subsampling ratio"); + break; + } + break; + } + } + + comp[i].h = h; + comp[i].v = v; + } + } + + // Specified in section B.2.4.1. + public void processDQT(int n) + { + while(n > 0) + { + bool done = false; + + n--; + byte x = readByte(); + byte tq = (byte)(x & 0x0f); + if(tq > maxTq) + throw new Exception("bad Tq value"); + + switch(x >> 4) + { + case 0: + if(n < Block.blockSize) + { + done = true; + break; + } + n -= Block.blockSize; + readFull(tmp, 0, Block.blockSize); + + for(int i = 0; i < Block.blockSize; i++) + quant[tq][i] = tmp[i]; + break; + case 1: + if(n < 2*Block.blockSize) + { + done = true; + break; + } + n -= 2 * Block.blockSize; + readFull(tmp, 0, 2*Block.blockSize); + + for(int i = 0; i < Block.blockSize; i++) + quant[tq][i] = ((int)tmp[2*i]<<8) | (int)tmp[2*i+1]; + break; + default: + throw new Exception("bad Pq value"); + } + + if(done) + break; + } + + if(n != 0) + throw new Exception("DQT has wrong length"); + } + + // Specified in section B.2.4.4. + public void processDRI(int n) + { + if(n != 2) + throw new Exception("DRI has wrong length"); + + readFull(tmp, 0, 2); + ri = ((int)tmp[0]<<8) + (int)tmp[1]; + } + + public void processApp0Marker(int n) + { + if(n < 5) + { + ignore(n); + return; + } + + readFull(tmp, 0, 5); + n -= 5; + + jfif = tmp[0] == 'J' && tmp[1] == 'F' && tmp[2] == 'I' && tmp[3] == 'F' && tmp[4] == '\x00'; + if(n > 0) + ignore(n); + } + + public void processApp14Marker(int n) + { + if(n < 12) + { + ignore(n); + return; + } + + readFull(tmp, 0, 12); + n -= 12; + + if(tmp[0] == 'A' && tmp[1] == 'd' && tmp[2] == 'o' && tmp[3] == 'b' && tmp[4] == 'e') + { + adobeTransformValid = true; + adobeTransform = tmp[11]; + } + + if(n > 0) + ignore(n); + } + + // decode reads a JPEG image from r and returns it as an image.Image. + public object decode(Stream r, bool configOnly) + { + this.r = r; + + // Check for the Start Of Image marker. + readFull(tmp, 0, 2); + if(tmp[0] != 0xff || tmp[1] != soiMarker) + throw new Exception("missing SOI marker"); + + // Process the remaining segments until the End Of Image marker. + while(true) + { + readFull(tmp, 0, 2); + while(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 ignore + // + // 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. + tmp[0] = tmp[1]; + tmp[1] = readByte(); + } + byte marker = tmp[1]; + if(marker == 0) { + // Treat "\xff\x00" as extraneous data. + continue; + } + + while(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 = readByte(); + } + + 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. + readFull(tmp, 0, 2); + int n = ((int)tmp[0]<<8) + (int)tmp[1] - 2; + if(n < 0) + throw new Exception("short segment length"); + + switch(marker) + { + case sof0Marker: + case sof1Marker: + case sof2Marker: + progressive = (marker == sof2Marker); + processSOF(n); + if(configOnly && jfif) + return null; + break; + case dhtMarker: + if(configOnly) + ignore(n); + else + processDHT(n); + break; + case dqtMarker: + if(configOnly) + ignore(n); + else + processDQT(n); + break; + case sosMarker: + if(configOnly) + return null; + else + processSOS(n); + break; + case driMarker: + if(configOnly) + ignore(n); + else + processDRI(n); + break; + case app0Marker: + processApp0Marker(n); + break; + case app14Marker: + processApp14Marker(n); + break; + default: + if(app0Marker <= marker && marker <= app15Marker || marker == comMarker) + ignore(n); + else if(marker < 0xc0) // See Table B.1 "Marker code assignments". + throw new Exception("unknown marker"); + else + throw new Exception("unknown marker"); + break; + } + } + + if(img1 != null) + return img1; + if(img3 != null) + { + if(comp[0].c == 'R' && comp[1].c == 'G' && comp[2].c == 'B') + { + imgrgb = convert_direct_to_rgb(width, height); + return imgrgb; + } + else + { + imgrgb = convert_to_rgb(width, height); + return imgrgb; + } + } + + /*if img3 != nil { + if blackPix != nil { + return applyBlack() + } else if isRGB() { + return convertToRGB() + } + return img3, nil + }*/ + + throw new Exception("missing SOS marker"); + } + + private img_rgb convert_to_rgb(int w, int h) + { + var ret = new img_rgb(w, h); + + int cScale = comp[0].h / comp[1].h; + for(var y = 0; y < h; 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++) + { + byte yy = img3.pix_y[yo+x]; + byte cb = img3.pix_cb[co+x/cScale]; + byte cr = img3.pix_cr[co+x/cScale]; + + 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; + } + } + + return ret; + } + + private img_rgb convert_direct_to_rgb(int w, int h) + { + var ret = new img_rgb(w, h); + + int cScale = comp[0].h / comp[1].h; + for(var y = 0; y < h; 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++) + { + 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]; + } + } + + return ret; + } + + private struct scan_scruct { + public byte compIndex; + public byte td; + public byte ta; + } + + // Specified in section B.2.3. + void processSOS(int n) + { + if(nComp == 0) + throw new Exception("missing SOF marker"); + + if(n < 6 || 4+2*nComp < n || n%2 != 0) + throw new Exception("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"); + + var scan = new scan_scruct[maxComponents]; + int totalHV = 0; + for(int i = 0; i < nComp; i++) + { + int cs = tmp[1+2*i]; // Component selector. + 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"); + + 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 + // B.2.2) are unique, it suffices to check that the implicit indexes + // into comp are unique. + 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"); + + scan[i].ta = (byte)(tmp[2+2*i] & 0x0f); + if(scan[i].ta > maxTh) + throw new Exception("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"); + + // 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. + int zigStart = 0; + int zigEnd = Block.blockSize - 1; + int ah = 0; + int al = 0; + + if(progressive) + { + zigStart = (int)(tmp[1+2*nComp]); + zigEnd = (int)(tmp[2+2*nComp]); + ah = (int)(tmp[3+2*nComp] >> 4); + al = (int)(tmp[3+2*nComp] & 0x0f); + if((zigStart == 0 && zigEnd != 0) || zigStart > zigEnd || Block.blockSize <= zigEnd) + throw new Exception("bad spectral selection bounds");; + if(zigStart != 0 && nComp != 1) + throw new Exception("progressive AC coefficients for more than one component"); + if(ah != 0 && ah != al+1) + throw new Exception("bad successive approximation values"); + } + + // mxx and myy are the number of MCUs (Minimum Coded Units) in the image. + int h0 = comp[0].h; + int v0 = comp[0].v; + int mxx = (width + 8*h0 - 1) / (8 * h0); + int myy = (height + 8*v0 - 1) / (8 * v0); + + if(img1 == null && img3 == null) + makeImg(mxx, myy); + + if(progressive) + { + for(int i = 0; i < nComp; i++) + { + int compIndex = scan[i].compIndex; + if(progCoeffs[compIndex] == null) + { + progCoeffs[compIndex] = new Block[mxx*myy*comp[compIndex].h*comp[compIndex].v]; + + for(int j = 0; j < progCoeffs[compIndex].Length; j++) + progCoeffs[compIndex][j] = new Block(); + } + } + } + + bits = new bits_class(); + + byte mcu = 0; + byte expectedRST = rst0Marker; + + // 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; + + for(int my = 0; my < myy; my++) + { + for(int mx = 0; mx < mxx; mx++) + { + for(int i = 0; i < nComp; i++) + { + int compIndex = scan[i].compIndex; + int hi = comp[compIndex].h; + int vi = comp[compIndex].v; + Block qt = quant[comp[compIndex].tq]; + + for(int j = 0; j < hi*vi; 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 interleave 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 + { + int q = mxx * hi; + bx = blockCount % q; + by = blockCount / q; + blockCount++; + if(bx*8 >= width || by*8 >= height) + continue; + } + + // Load the previous partially decoded coefficients, if applicable. + if(progressive) + b = progCoeffs[compIndex][by*mxx*hi+bx]; + else + b = new Block(); + + if(ah != 0) + { + refine(b, huff[acTable, scan[i].ta], zigStart, zigEnd, 1< 16) + throw new Exception("excessive DC component"); + int dcDelta = receiveExtend(value); + dc[compIndex] += dcDelta; + b[0] = dc[compIndex] << al; + } + + if(zig <= zigEnd && eobRun > 0) + { + eobRun--; + } + else + { + // Decode the AC coefficients, as specified in section F.2.2.2. + var huffv = huff[acTable, scan[i].ta]; + for( ; zig <= zigEnd; zig++) + { + byte value = decodeHuffman(huffv); + byte val0 = (byte)(value >> 4); + byte val1 = (byte)(value & 0x0f); + if(val1 != 0) + { + zig += val0; + if(zig > zigEnd) + break; + + var ac = receiveExtend(val1); + b[unzig[zig]] = ac << al; + } + else + { + if(val0 != 0x0f) + { + eobRun = (ushort)(1 << val0); + if(val0 != 0) + { + uint bits = decodeBits(val0); + eobRun |= (ushort)(bits); + } + eobRun--; + break; + } + zig += 0x0f; + } + } + } + } + + if(progressive) + { + if(zigEnd != Block.blockSize-1 || al != 0) + { + // 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, + // 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(int zig = 0; zig < Block.blockSize; zig++) + b[unzig[zig]] *= qt[zig]; + + IDCT(b); + + byte[] dst = null; + int offset = 0; + int stride = 0; + + if(nComp == 1) + { + dst = img1.pixels; + stride = img1.stride; + offset = img1.offset + 8*(by*img1.stride+bx); + } + else + { + switch(compIndex) + { + case 0: + { + dst = img3.pix_y; + stride = img3.y_stride; + offset = img3.y_offset + 8*(by*img3.y_stride+bx); + break; + } + case 1: + { + dst = img3.pix_cb; + stride = img3.c_stride; + offset = img3.c_offset + 8*(by*img3.c_stride+bx); + break; + } + case 2: + { + dst = img3.pix_cr; + stride = img3.c_stride; + offset = img3.c_offset + 8*(by*img3.c_stride+bx); + break; + } + case 3: + { + //dst, stride = blackPix[8*(by*blackStride+bx):], blackStride + break; + } + default: + throw new Exception("too many components"); + } + } + + // Level shift by +128, clip to [0, 255], and write to dst. + for(int y = 0; y < 8; y++) + { + int y8 = y * 8; + int yStride = y * stride; + for(int x = 0; x < 8; x++) + { + int c = b[y8+x]; + if(c < -128) + c = 0; + else if(c > 127) + c = 255; + else + c += 128; + + dst[yStride+x+offset] = (byte)c; + } + } + } // for j + } // for i + + mcu++; + + if(ri > 0 && mcu%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. + readFull(tmp, 0, 2); + if(tmp[0] != 0xff || tmp[1] != expectedRST) + throw new Exception("bad RST marker"); + + expectedRST++; + if(expectedRST == rst7Marker+1) + expectedRST = rst0Marker; + + // Reset the Huffman decoder. + bits = new bits_class(); + // Reset the DC components, as per section F.2.1.3.1. + dc = new int[maxComponents]; + // Reset the progressive decoder state, as per section G.1.2.2. + eobRun = 0; + } + } // for mx + } // for my + } + + // refine decodes a successive approximation refinement block, as specified in + // section G.1.2. + private void refine(Block b, huffman_class h, int zigStart, int zigEnd, int delta) + { + // Refining a DC component is trivial. + if(zigStart == 0) + { + if(zigEnd != 0) + throw new Exception("Invalid state for zig DC component"); + + bool bit = decodeBit(); + if(bit) + b[0] |= delta; + + return; + } + + // Refining AC components is more complicated; see sections G.1.2.2 and G.1.2.3. + int zig = zigStart; + if(eobRun == 0) + { + for( ; zig <= zigEnd; zig++) + { + bool done = false; + int z = 0; + var val = decodeHuffman(h); + int val0 = val >> 4; + int val1 = val & 0x0f; + + switch(val1) + { + case 0: + if(val0 != 0x0f) + { + eobRun = (ushort)(1 << val0); + if(val0 != 0) + { + uint bits = decodeBits(val0); + eobRun |= (ushort)bits; + } + } + done = true; + break; + case 1: + z = delta; + bool bit = decodeBit(); + if(!bit) + z = -z; + break; + default: + throw new Exception("unexpected Huffman code"); + } + + zig = refineNonZeroes(b, zig, zigEnd, val0, delta); + if(zig > zigEnd) + throw new Exception("too many coefficients"); + + if(z != 0) + b[unzig[zig]] = z; + } + } + + if(eobRun > 0) + { + eobRun--; + refineNonZeroes(b, zig, zigEnd, -1, delta); + } + } + + // refineNonZeroes refines non-zero entries of b in zig-zag order. If nz >= 0, + // the first nz zero entries are skipped over. + public int refineNonZeroes(Block b, int zig, int zigEnd, int nz, int delta) + { + for( ; zig <= zigEnd; zig++) + { + int u = unzig[zig]; + if(b[u] == 0) + { + if(nz == 0) + break; + nz--; + continue; + } + + bool bit = decodeBit(); + if(!bit) + continue; + + if(b[u] >= 0) + b[u] += delta; + else + b[u] -= delta; + } + + return zig; + } + + private void makeImg(int mxx, int myy) + { + if(nComp == 1) + { + var m = new img_gray(8*mxx, 8*myy); + img1 = m.subimage(0, 0, width, height); + } + else + { + var h0 = comp[0].h; + var v0 = comp[0].v; + var hRatio = h0 / comp[1].h; + var vRatio = v0 / comp[1].v; + + var ratio = img_ycbcr.YCbCrSubsampleRatio.YCbCrSubsampleRatio444; + switch((hRatio<<4) | vRatio) + { + case 0x11: + ratio = img_ycbcr.YCbCrSubsampleRatio.YCbCrSubsampleRatio444; + break; + case 0x12: + ratio = img_ycbcr.YCbCrSubsampleRatio.YCbCrSubsampleRatio440; + break; + case 0x21: + ratio = img_ycbcr.YCbCrSubsampleRatio.YCbCrSubsampleRatio422; + break; + case 0x22: + ratio = img_ycbcr.YCbCrSubsampleRatio.YCbCrSubsampleRatio420; + break; + case 0x41: + ratio = img_ycbcr.YCbCrSubsampleRatio.YCbCrSubsampleRatio411; + break; + case 0x42: + ratio = img_ycbcr.YCbCrSubsampleRatio.YCbCrSubsampleRatio410; + break; + } + + var m = new img_ycbcr(8*h0*mxx, 8*v0*myy, ratio); + img3 = m.subimage(0, 0, width, height); + + /*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 + }*/ + } + } + } +} diff --git a/src/ImageProcessorCore/Formats/Jpg/FDCT.cs b/src/ImageProcessorCore/Formats/Jpg/FDCT.cs new file mode 100644 index 000000000..a3202d57c --- /dev/null +++ b/src/ImageProcessorCore/Formats/Jpg/FDCT.cs @@ -0,0 +1,137 @@ +namespace ImageProcessorCore.Formats.Jpg +{ + using System; + using System.IO; + + public partial class JpegGo + { + // Trigonometric constants in 13-bit fixed point format. + private const int fix_0_298631336 = 2446; + private const int fix_0_390180644 = 3196; + private const int fix_0_541196100 = 4433; + private const int fix_0_765366865 = 6270; + private const int fix_0_899976223 = 7373; + private const int fix_1_175875602 = 9633; + private const int fix_1_501321110 = 12299; + private const int fix_1_847759065 = 15137; + private const int fix_1_961570560 = 16069; + private const int fix_2_053119869 = 16819; + private const int fix_2_562915447 = 20995; + private const int fix_3_072711026 = 25172; + private const int constBits = 13; + private const int pass1Bits = 2; + private const int centerJSample = 128; + + // fdct performs a forward DCT on an 8x8 block of coefficients, including a + // level shift. + private static void FDCT(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 tmp0 = x0 + x7; + int tmp1 = x1 + x6; + int tmp2 = x2 + x5; + int tmp3 = x3 + x4; + + int tmp10 = tmp0 + tmp3; + int tmp12 = tmp0 - tmp3; + int tmp11 = tmp1 + tmp2; + int 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; + 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); + + 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(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 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]; + + b[0*8+x] = (tmp10 + tmp11) >> pass1Bits; + b[4*8+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); + + 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/IDCT.cs b/src/ImageProcessorCore/Formats/Jpg/IDCT.cs new file mode 100644 index 000000000..ade544ef5 --- /dev/null +++ b/src/ImageProcessorCore/Formats/Jpg/IDCT.cs @@ -0,0 +1,161 @@ +namespace ImageProcessorCore.Formats.Jpg +{ + using System; + using System.IO; + + public partial class Decoder + { + private const int w1 = 2841; // 2048*sqrt(2)*cos(1*pi/16) + private const int w2 = 2676; // 2048*sqrt(2)*cos(2*pi/16) + private const int w3 = 2408; // 2048*sqrt(2)*cos(3*pi/16) + private const int w5 = 1609; // 2048*sqrt(2)*cos(5*pi/16) + private const int w6 = 1108; // 2048*sqrt(2)*cos(6*pi/16) + private const int w7 = 565; // 2048*sqrt(2)*cos(7*pi/16) + + private const int w1pw7 = w1 + w7; + private const int w1mw7 = w1 - w7; + private const int w2pw6 = w2 + w6; + private const int w2mw6 = w2 - w6; + private const int w3pw5 = w3 + w5; + private const int w3mw5 = w3 - w5; + + private const int 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. + private static void IDCT(Block src) + { + // Horizontal 1-D IDCT. + for(int y = 0; y < 8; y++) + { + 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) + { + 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]; + + // Stage 1. + int 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(int 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. + 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; + + // 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; + } + } + } +} diff --git a/src/ImageProcessorCore/Formats/Jpg/JpegDecoder.cs b/src/ImageProcessorCore/Formats/Jpg/JpegDecoder.cs index c26973314..9ca19c475 100644 --- a/src/ImageProcessorCore/Formats/Jpg/JpegDecoder.cs +++ b/src/ImageProcessorCore/Formats/Jpg/JpegDecoder.cs @@ -8,8 +8,7 @@ namespace ImageProcessorCore.Formats using System; using System.IO; using System.Threading.Tasks; - - using BitMiracle.LibJpeg; + using ImageProcessorCore.Formats.Jpg; /// /// Image decoder for generating an image out of a jpg stream. @@ -95,56 +94,52 @@ namespace ImageProcessorCore.Formats { Guard.NotNull(image, "image"); Guard.NotNull(stream, "stream"); - JpegImage jpg = new JpegImage(stream); - int pixelWidth = jpg.Width; - int pixelHeight = jpg.Height; + Decoder decoder = new Decoder(); + decoder.decode(stream, false); + + int pixelWidth = decoder.width; + int pixelHeight = decoder.height; float[] pixels = new float[pixelWidth * pixelHeight * 4]; - if (jpg.Colorspace == Colorspace.RGB && jpg.BitsPerComponent == 8) - { + if(decoder.nComp == 1) + { Parallel.For( 0, pixelHeight, y => + { + var yoff = decoder.img1.get_row_offset(y); + for (int x = 0; x < pixelWidth; x++) { - SampleRow row = jpg.GetRow(y); - - for (int x = 0; x < pixelWidth; x++) - { - Sample sample = row.GetAt(x); - - int offset = ((y * pixelWidth) + x) * 4; + int offset = ((y * pixelWidth) + x) * 4; - pixels[offset + 0] = sample[0] / 255f; - pixels[offset + 1] = sample[1] / 255f; - pixels[offset + 2] = sample[2] / 255f; - pixels[offset + 3] = 1; - } - }); - } - else if (jpg.Colorspace == Colorspace.Grayscale && jpg.BitsPerComponent == 8) + 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; + } + }); + } + else if(decoder.nComp == 3) { Parallel.For( 0, pixelHeight, y => - { - SampleRow row = jpg.GetRow(y); - - for (int x = 0; x < pixelWidth; x++) { - Sample sample = row.GetAt(x); - - int offset = ((y * pixelWidth) + x) * 4; + var yoff = decoder.imgrgb.get_row_offset(y); + for (int x = 0; x < pixelWidth; x++) + { + int offset = ((y * pixelWidth) + x) * 4; - pixels[offset + 0] = sample[0] / 255f; - pixels[offset + 1] = sample[0] / 255f; - pixels[offset + 2] = sample[0] / 255f; - pixels[offset + 3] = 1; - } - }); + 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 + 3] = 1; + } + }); } else { @@ -152,8 +147,6 @@ namespace ImageProcessorCore.Formats } image.SetPixels(pixelWidth, pixelHeight, pixels); - - jpg.Dispose(); } /// diff --git a/src/ImageProcessorCore/Formats/Jpg/JpegEncoder.cs b/src/ImageProcessorCore/Formats/Jpg/JpegEncoder.cs index e73483b82..b6e5250d8 100644 --- a/src/ImageProcessorCore/Formats/Jpg/JpegEncoder.cs +++ b/src/ImageProcessorCore/Formats/Jpg/JpegEncoder.cs @@ -9,8 +9,6 @@ namespace ImageProcessorCore.Formats using System.IO; using System.Threading.Tasks; - using BitMiracle.LibJpeg; - /// /// Encoder for writing the data image to a stream in jpeg format. /// @@ -62,7 +60,7 @@ namespace ImageProcessorCore.Formats int imageWidth = image.Width; int imageHeight = image.Height; - SampleRow[] rows = new SampleRow[imageHeight]; + /*SampleRow[] rows = new SampleRow[imageHeight]; Parallel.For( 0, @@ -87,7 +85,7 @@ namespace ImageProcessorCore.Formats using (JpegImage jpg = new JpegImage(rows, Colorspace.RGB)) { jpg.WriteJpeg(stream, new CompressionParameters { Quality = this.Quality }); - } + }*/ } } } diff --git a/src/ImageProcessorCore/Formats/Jpg/JpegFormat.cs b/src/ImageProcessorCore/Formats/Jpg/JpegFormat.cs index ec9ceb6dc..521657524 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 => new JpegEncoder(); + public IImageEncoder Encoder => null; } } diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/BitStream.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/BitStream.cs deleted file mode 100644 index 45ddc1ee9..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/BitStream.cs +++ /dev/null @@ -1,383 +0,0 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// -// -// A stream for reading bits in a sequence of bytes. -// -// -------------------------------------------------------------------------------------------------------------------- - -namespace ImageProcessorCore.Formats -{ - using System; - using System.IO; - - /// - /// A stream for reading bits in a sequence of bytes. - /// - internal class BitStream : IDisposable - { - /// - /// The number of bits in byte. - /// - private const int BitsInByte = 8; - - /// - /// A value indicating whether this instance of the given entity has been disposed. - /// - /// if this instance has been disposed; otherwise, . - /// - /// If the entity is disposed, it must not be disposed a second - /// time. The isDisposed field is set the first time the entity - /// is disposed. If the isDisposed field is true, then the Dispose() - /// method will not dispose again. This help not to prolong the entity's - /// life in the Garbage Collector. - /// - private bool isDisposed; - - /// - /// The underlying stream. - /// - private Stream stream; - - /// - /// The current position. - /// - private int currentPosition; - - /// - /// The size of the underlying stream. - /// - private int size; - - /// - /// Initializes a new instance of the class. - /// - public BitStream() - { - this.stream = new MemoryStream(); - } - - /// - /// Initializes a new instance of the class based on the - /// specified byte array. - /// - /// - /// The from which to create the current stream. - /// - /// - /// Thrown if the given stream is null. - /// - public BitStream(Stream stream) - { - Guard.NotNull(stream, nameof(stream)); - - this.stream = new MemoryStream(); - stream.CopyTo(this.stream); - this.size = this.BitsAllocated(); - } - - /// - /// Initializes a new instance of the class based on the - /// specified byte array. - /// - /// - /// The from which to create the current stream. - /// - /// - /// Thrown if the given buffer is null. - /// - public BitStream(byte[] buffer) - { - Guard.NotNull(buffer, nameof(buffer)); - - this.stream = new MemoryStream(buffer); - this.size = this.BitsAllocated(); - } - - /// - /// Finalizes an instance of the class. - /// - ~BitStream() - { - // Do not re-create Dispose clean-up code here. - // Calling Dispose(false) is optimal in terms of - // readability and maintainability. - this.Dispose(true); - } - - /// - /// Gets the underlying stream. - /// - public Stream UnderlyingStream => this.stream; - - /// - /// Disposes the object and frees resources for the Garbage Collector. - /// - public void Dispose() - { - this.Dispose(true); - - // This object will be cleaned up by the Dispose method. - // Therefore, you should call GC.SuppressFinalize to - // take this object off the finalization queue - // and prevent finalization code for this object - // from executing a second time. - GC.SuppressFinalize(this); - } - - /// - /// Returns the size of the stream. - /// - /// - /// The representing the size. - /// - public int Size() - { - return this.size; - } - - /// - /// Returns a number representing the given number of bits read from the stream - /// advancing the stream by that number. - /// - /// The number of bits to read. - /// - /// The representing the total number of bits read. - /// - public virtual int Read(int bitCount) - { - Guard.MustBeLessThanOrEqualTo(this.Tell() + bitCount, this.BitsAllocated(), nameof(bitCount)); - return this.ReadBits(bitCount); - } - - /// - /// Writes a block of bits represented by an to the current stream. - /// - /// The bits to write. - /// The number of bits to write. - /// - /// The representing the number of bits written. - /// - public int Write(int bitStorage, int bitCount) - { - if (bitCount == 0) - { - return 0; - } - - const int MaxBitsInStorage = sizeof(int) * BitsInByte; - - Guard.MustBeLessThanOrEqualTo(bitCount, MaxBitsInStorage, nameof(bitCount)); - - for (int i = 0; i < bitCount; ++i) - { - byte bit = (byte)((bitStorage << (MaxBitsInStorage - (bitCount - i))) >> (MaxBitsInStorage - 1)); - if (!this.WriteBits(bit)) - { - return i; - } - } - - return bitCount; - } - - /// - /// Sets the position within the current stream to the specified value. - /// - /// - /// The new position within the stream. - /// This is relative to the parameter, and can be positive or negative. - /// - /// - /// A value of type , which acts as the seek reference point. - /// - public void Seek(int position, SeekOrigin location) - { - switch (location) - { - case SeekOrigin.Begin: - this.SeekSet(position); - break; - - case SeekOrigin.Current: - this.SeekCurrent(position); - break; - - case SeekOrigin.End: - this.SeekSet(this.Size() + position); - break; - } - } - - /// - /// TODO: Document this. - /// - /// - /// The . - /// - public int Tell() - { - return ((int)this.stream.Position * BitsInByte) + this.currentPosition; - } - - /// - /// Disposes the object and frees resources for the Garbage Collector. - /// - /// If true, the object gets disposed. - protected virtual void Dispose(bool disposing) - { - if (this.isDisposed) - { - return; - } - - if (disposing) - { - if (this.stream != null) - { - this.stream.Dispose(); - this.stream = null; - } - } - - // Note disposing is done. - this.isDisposed = true; - } - - /// - /// Returns the number of bits allocated to the stream. - /// - /// - /// The representing the number of bits. - /// - private int BitsAllocated() - { - return (int)this.stream.Length * BitsInByte; - } - - /// - /// Returns a number representing the given number of bits read from the stream. - /// - /// The number of bits to read. - /// - /// The representing the total number of bits read. - /// - private int ReadBits(int bitsCount) - { - // Codes are packed into a continuous bit stream, high-order bit first. - // This stream is then divided into 8-bit bytes, high-order bit first. - // Thus, codes can straddle byte boundaries arbitrarily. After the EOD marker (code value 257), - // any leftover bits in the final byte are set to 0. - Guard.MustBeBetweenOrEqualTo(bitsCount, 0, 32, "bitsCount"); - - if (bitsCount == 0) - { - return 0; - } - - int bitsRead = 0; - int result = 0; - byte[] bt = new byte[1]; - while (bitsRead == 0 || (bitsRead - this.currentPosition < bitsCount)) - { - this.stream.Read(bt, 0, 1); - - result = result << BitsInByte; - result += bt[0]; - - bitsRead += 8; - } - - this.currentPosition = (this.currentPosition + bitsCount) % 8; - if (this.currentPosition != 0) - { - result = result >> (BitsInByte - this.currentPosition); - - this.stream.Seek(-1, SeekOrigin.Current); - } - - if (bitsCount < 32) - { - int mask = (1 << bitsCount) - 1; - result = result & mask; - } - - return result; - } - - /// - /// Writes a block of bits represented to the current stream. - /// - /// - /// The bits to write. - /// - /// - /// True. TODO: investigate this as it always returns true. - /// - private bool WriteBits(byte bits) - { - if (this.stream.Position == this.stream.Length) - { - byte[] bytes = { (byte)(bits << (BitsInByte - 1)) }; - this.stream.Write(bytes, 0, 1); - this.stream.Seek(-1, SeekOrigin.Current); - } - else - { - byte[] bytes = { 0 }; - this.stream.Read(bytes, 0, 1); - this.stream.Seek(-1, SeekOrigin.Current); - - int shift = (BitsInByte - this.currentPosition - 1) % BitsInByte; - byte maskByte = (byte)(bits << shift); - - bytes[0] |= maskByte; - this.stream.Write(bytes, 0, 1); - this.stream.Seek(-1, SeekOrigin.Current); - } - - this.Seek(1, SeekOrigin.Current); - - int position = this.Tell(); - if (position > this.size) - { - this.size = position; - } - - return true; - } - - /// - /// Sets the position within the current stream to the specified value. - /// - /// - /// The new position within the stream. Can be positive or negative. - /// - private void SeekSet(int position) - { - Guard.MustBeGreaterThanOrEqualTo(position, 0, "position"); - - int byteDisplacement = position / BitsInByte; - this.stream.Seek(byteDisplacement, SeekOrigin.Begin); - - int shiftInByte = position - (byteDisplacement * BitsInByte); - this.currentPosition = shiftInByte; - } - - /// - /// Sets the position to current position in the current stream. - /// - /// - /// The new position within the stream. Can be positive or negative. - /// - private void SeekCurrent(int position) - { - int result = this.Tell() + position; - Guard.MustBeBetweenOrEqualTo(position, 0, this.BitsAllocated(), "position"); - - this.SeekSet(result); - } - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/BitmapDestination.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/BitmapDestination.cs deleted file mode 100644 index 4fc60d821..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/BitmapDestination.cs +++ /dev/null @@ -1,326 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Text; - -using BitMiracle.LibJpeg.Classic; - -namespace BitMiracle.LibJpeg -{ - class BitmapDestination : IDecompressDestination - { - /* Target file spec; filled in by djpeg.c after object is created. */ - private Stream m_output; - - private byte[][] m_pixels; - - private int m_rowWidth; /* physical width of one row in the BMP file */ - - private int m_currentRow; /* next row# to write to virtual array */ - private LoadedImageAttributes m_parameters; - - public BitmapDestination(Stream output) - { - m_output = output; - } - - public Stream Output - { - get - { - return m_output; - } - } - - public void SetImageAttributes(LoadedImageAttributes parameters) - { - if (parameters == null) - throw new ArgumentNullException("parameters"); - - m_parameters = parameters; - } - - /// - /// Startup: normally writes the file header. - /// In this module we may as well postpone everything until finish_output. - /// - public void BeginWrite() - { - //Determine width of rows in the BMP file (padded to 4-byte boundary). - m_rowWidth = m_parameters.Width * m_parameters.Components; - while (m_rowWidth % 4 != 0) - m_rowWidth++; - - m_pixels = new byte[m_rowWidth][]; - for (int i = 0; i < m_rowWidth; i++) - m_pixels[i] = new byte[m_parameters.Height]; - - m_currentRow = 0; - } - - /// - /// Write some pixel data. - /// - public void ProcessPixelsRow(byte[] row) - { - if (m_parameters.Colorspace == Colorspace.Grayscale || m_parameters.QuantizeColors) - { - putGrayRow(row); - } - else - { - if (m_parameters.Colorspace == Colorspace.CMYK) - putCmykRow(row); - else - putRgbRow(row); - } - - ++m_currentRow; - } - - /// - /// Finish up at the end of the file. - /// Here is where we really output the BMP file. - /// - public void EndWrite() - { - writeHeader(); - writePixels(); - - /* Make sure we wrote the output file OK */ - m_output.Flush(); - } - - - /// - /// This version is for grayscale OR quantized color output - /// - private void putGrayRow(byte[] row) - { - for (int i = 0; i < m_parameters.Width; ++i) - m_pixels[i][m_currentRow] = row[i]; - } - - /// - /// This version is for writing 24-bit pixels - /// - private void putRgbRow(byte[] row) - { - /* Transfer data. Note destination values must be in BGR order - * (even though Microsoft's own documents say the opposite). - */ - for (int i = 0; i < m_parameters.Width; ++i) - { - int firstComponent = i * 3; - byte red = row[firstComponent]; - byte green = row[firstComponent + 1]; - byte blue = row[firstComponent + 2]; - m_pixels[firstComponent][m_currentRow] = blue; - m_pixels[firstComponent + 1][m_currentRow] = green; - m_pixels[firstComponent + 2][m_currentRow] = red; - } - } - - /// - /// This version is for writing 24-bit pixels - /// - private void putCmykRow(byte[] row) - { - /* Transfer data. Note destination values must be in BGR order - * (even though Microsoft's own documents say the opposite). - */ - for (int i = 0; i < m_parameters.Width; ++i) - { - int firstComponent = i * 4; - m_pixels[firstComponent][m_currentRow] = row[firstComponent + 2]; - m_pixels[firstComponent + 1][m_currentRow] = row[firstComponent + 1]; - m_pixels[firstComponent + 2][m_currentRow] = row[firstComponent + 0]; - m_pixels[firstComponent + 3][m_currentRow] = row[firstComponent + 3]; - } - } - - /// - /// Write a Windows-style BMP file header, including colormap if needed - /// - private void writeHeader() - { - int bits_per_pixel; - int cmap_entries; - - /* Compute colormap size and total file size */ - if (m_parameters.Colorspace == Colorspace.Grayscale || m_parameters.QuantizeColors) - { - bits_per_pixel = 8; - cmap_entries = 256; - } - else - { - cmap_entries = 0; - - if (m_parameters.Colorspace == Colorspace.RGB) - bits_per_pixel = 24; - else if (m_parameters.Colorspace == Colorspace.CMYK) - bits_per_pixel = 32; - else - throw new InvalidOperationException(); - } - - byte[] infoHeader = null; - if (m_parameters.Colorspace == Colorspace.RGB) - infoHeader = createBitmapInfoHeader(bits_per_pixel, cmap_entries); - else - infoHeader = createBitmapV4InfoHeader(bits_per_pixel); - - /* File size */ - const int fileHeaderSize = 14; - int infoHeaderSize = infoHeader.Length; - int paletteSize = cmap_entries * 4; - int offsetToPixels = fileHeaderSize + infoHeaderSize + paletteSize; /* Header and colormap */ - int fileSize = offsetToPixels + m_rowWidth * m_parameters.Height; - - byte[] fileHeader = createBitmapFileHeader(offsetToPixels, fileSize); - - m_output.Write(fileHeader, 0, fileHeader.Length); - m_output.Write(infoHeader, 0, infoHeader.Length); - - if (cmap_entries > 0) - writeColormap(cmap_entries, 4); - } - - private static byte[] createBitmapFileHeader(int offsetToPixels, int fileSize) - { - byte[] bmpfileheader = new byte[14]; - bmpfileheader[0] = 0x42; /* first 2 bytes are ASCII 'B', 'M' */ - bmpfileheader[1] = 0x4D; - PUT_4B(bmpfileheader, 2, fileSize); - /* we leave bfReserved1 & bfReserved2 = 0 */ - PUT_4B(bmpfileheader, 10, offsetToPixels); /* bfOffBits */ - return bmpfileheader; - } - - private byte[] createBitmapInfoHeader(int bits_per_pixel, int cmap_entries) - { - byte[] bmpinfoheader = new byte[40]; - fillBitmapInfoHeader(bits_per_pixel, cmap_entries, bmpinfoheader); - return bmpinfoheader; - } - - private void fillBitmapInfoHeader(int bitsPerPixel, int cmap_entries, byte[] infoHeader) - { - /* Fill the info header (Microsoft calls this a BITMAPINFOHEADER) */ - PUT_2B(infoHeader, 0, infoHeader.Length); /* biSize */ - PUT_4B(infoHeader, 4, m_parameters.Width); /* biWidth */ - PUT_4B(infoHeader, 8, m_parameters.Height); /* biHeight */ - PUT_2B(infoHeader, 12, 1); /* biPlanes - must be 1 */ - PUT_2B(infoHeader, 14, bitsPerPixel); /* biBitCount */ - /* we leave biCompression = 0, for none */ - /* we leave biSizeImage = 0; this is correct for uncompressed data */ - - if (m_parameters.DensityUnit == DensityUnit.DotsCm) - { - /* if have density in dots/cm, then */ - PUT_4B(infoHeader, 24, m_parameters.DensityX * 100); /* XPels/M */ - PUT_4B(infoHeader, 28, m_parameters.DensityY * 100); /* XPels/M */ - } - PUT_2B(infoHeader, 32, cmap_entries); /* biClrUsed */ - /* we leave biClrImportant = 0 */ - } - - private byte[] createBitmapV4InfoHeader(int bitsPerPixel) - { - byte[] infoHeader = new byte[40 + 68]; - fillBitmapInfoHeader(bitsPerPixel, 0, infoHeader); - - PUT_4B(infoHeader, 56, 0x02); /* CSType == 0x02 (CMYK) */ - - return infoHeader; - } - - /// - /// Write the colormap. - /// Windows uses BGR0 map entries; OS/2 uses BGR entries. - /// - private void writeColormap(int map_colors, int map_entry_size) - { - byte[][] colormap = m_parameters.Colormap; - int num_colors = m_parameters.ActualNumberOfColors; - - int i = 0; - if (colormap != null) - { - if (m_parameters.ComponentsPerSample == 3) - { - /* Normal case with RGB colormap */ - for (i = 0; i < num_colors; i++) - { - m_output.WriteByte(colormap[2][i]); - m_output.WriteByte(colormap[1][i]); - m_output.WriteByte(colormap[0][i]); - if (map_entry_size == 4) - m_output.WriteByte(0); - } - } - else - { - /* Grayscale colormap (only happens with grayscale quantization) */ - for (i = 0; i < num_colors; i++) - { - m_output.WriteByte(colormap[0][i]); - m_output.WriteByte(colormap[0][i]); - m_output.WriteByte(colormap[0][i]); - if (map_entry_size == 4) - m_output.WriteByte(0); - } - } - } - else - { - /* If no colormap, must be grayscale data. Generate a linear "map". */ - for (i = 0; i < 256; i++) - { - m_output.WriteByte((byte)i); - m_output.WriteByte((byte)i); - m_output.WriteByte((byte)i); - if (map_entry_size == 4) - m_output.WriteByte(0); - } - } - - /* Pad colormap with zeros to ensure specified number of colormap entries */ - if (i > map_colors) - throw new InvalidOperationException("Too many colors"); - - for (; i < map_colors; i++) - { - m_output.WriteByte(0); - m_output.WriteByte(0); - m_output.WriteByte(0); - if (map_entry_size == 4) - m_output.WriteByte(0); - } - } - - private void writePixels() - { - for (int row = m_parameters.Height - 1; row >= 0; --row) - for (int col = 0; col < m_rowWidth; ++col) - m_output.WriteByte(m_pixels[col][row]); - } - - - private static void PUT_2B(byte[] array, int offset, int value) - { - array[offset] = (byte)((value) & 0xFF); - array[offset + 1] = (byte)(((value) >> 8) & 0xFF); - } - - private static void PUT_4B(byte[] array, int offset, int value) - { - array[offset] = (byte)((value) & 0xFF); - array[offset + 1] = (byte)(((value) >> 8) & 0xFF); - array[offset + 2] = (byte)(((value) >> 16) & 0xFF); - array[offset + 3] = (byte)(((value) >> 24) & 0xFF); - } - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/DensityUnit.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/DensityUnit.cs deleted file mode 100644 index 70157fc4d..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/DensityUnit.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic -{ - /// - /// The unit of density. - /// - /// - /// -#if EXPOSE_LIBJPEG - public -#endif - enum DensityUnit - { - /// - /// Unknown density - /// - Unknown = 0, - - /// - /// Dots/inch - /// - DotsInch = 1, - - /// - /// Dots/cm - /// - DotsCm = 2 - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/ComponentBuffer.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/ComponentBuffer.cs deleted file mode 100644 index 2df43597b..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/ComponentBuffer.cs +++ /dev/null @@ -1,55 +0,0 @@ -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// Encapsulates buffer of image samples for one color component - /// When provided with funny indices (see jpeg_d_main_controller for - /// explanation of what it is) uses them for non-linear row access. - /// - class ComponentBuffer - { - private byte[][] m_buffer; - - // array of funny indices - private int[] m_funnyIndices; - - // index of "first funny index" (used because some code uses negative - // indices when retrieve rows) - // see for example my_upsampler.h2v2_fancy_upsample - private int m_funnyOffset; - - public ComponentBuffer() - { - } - - public ComponentBuffer(byte[][] buf, int[] funnyIndices, int funnyOffset) - { - SetBuffer(buf, funnyIndices, funnyOffset); - } - - public void SetBuffer(byte[][] buf, int[] funnyIndices, int funnyOffset) - { - m_buffer = buf; - m_funnyIndices = funnyIndices; - m_funnyOffset = funnyOffset; - } - - public byte[] this[int i] - { - get - { - if (m_funnyIndices == null) - return m_buffer[i]; - - return m_buffer[m_funnyIndices[i + m_funnyOffset]]; - } - } - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/J_BUF_MODE.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/J_BUF_MODE.cs deleted file mode 100644 index 977d3e537..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/J_BUF_MODE.cs +++ /dev/null @@ -1,29 +0,0 @@ -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// Operating modes for buffer controllers - /// - enum J_BUF_MODE - { - JBUF_PASS_THRU, /* Plain stripwise operation */ - - /* Remaining modes require a full-image buffer to have been created */ - - JBUF_SAVE_SOURCE, /* Run source subobject only, save output */ - JBUF_CRANK_DEST, /* Run dest subobject only, using saved data */ - JBUF_SAVE_AND_PASS /* Run both subobjects, save output */ - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/JpegUtils.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/JpegUtils.cs deleted file mode 100644 index 858d10d7f..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/JpegUtils.cs +++ /dev/null @@ -1,116 +0,0 @@ -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -/* - * This file contains tables and miscellaneous utility routines needed - * for both compression and decompression. - * Note we prefix all global names with "j" to minimize conflicts with - * a surrounding application. - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - class JpegUtils - { - /* - * jpeg_natural_order[i] is the natural-order position of the i'th element - * of zigzag order. - * - * When reading corrupted data, the Huffman decoders could attempt - * to reference an entry beyond the end of this array (if the decoded - * zero run length reaches past the end of the block). To prevent - * wild stores without adding an inner-loop test, we put some extra - * "63"s after the real entries. This will cause the extra coefficient - * to be stored in location 63 of the block, not somewhere random. - * The worst case would be a run-length of 15, which means we need 16 - * fake entries. - */ - public static int[] jpeg_natural_order = - { - 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, 63, 63, 63, 63, 63, 63, 63, 63, - /* extra entries for safety in decoder */ - 63, 63, 63, 63, 63, 63, 63, 63 - }; - - /* We assume that right shift corresponds to signed division by 2 with - * rounding towards minus infinity. This is correct for typical "arithmetic - * shift" instructions that shift in copies of the sign bit. - * RIGHT_SHIFT provides a proper signed right shift of an int quantity. - * It is only applied with constant shift counts. SHIFT_TEMPS must be - * included in the variables of any routine using RIGHT_SHIFT. - */ - public static int RIGHT_SHIFT(int x, int shft) - { - return (x >> shft); - } - - /* Descale and correctly round an int value that's scaled by N bits. - * We assume RIGHT_SHIFT rounds towards minus infinity, so adding - * the fudge factor is correct for either sign of X. - */ - public static int DESCALE(int x, int n) - { - return RIGHT_SHIFT(x + (1 << (n - 1)), n); - } - - ////////////////////////////////////////////////////////////////////////// - // Arithmetic utilities - - /// - /// Compute a/b rounded up to next integer, ie, ceil(a/b) - /// Assumes a >= 0, b > 0 - /// - public static int jdiv_round_up(int a, int b) - { - return (a + b - 1) / b; - } - - /// - /// Compute a rounded up to next multiple of b, ie, ceil(a/b)*b - /// Assumes a >= 0, b > 0 - /// - public static int jround_up(int a, int b) - { - a += b - 1; - return a - (a % b); - } - - /// - /// Copy some rows of samples from one place to another. - /// num_rows rows are copied from input_array[source_row++] - /// to output_array[dest_row++]; these areas may overlap for duplication. - /// The source and destination arrays must be at least as wide as num_cols. - /// - public static void jcopy_sample_rows(ComponentBuffer input_array, int source_row, byte[][] output_array, int dest_row, int num_rows, int num_cols) - { - for (int row = 0; row < num_rows; row++) - Buffer.BlockCopy(input_array[source_row + row], 0, output_array[dest_row + row], 0, num_cols); - } - - public static void jcopy_sample_rows(ComponentBuffer input_array, int source_row, ComponentBuffer output_array, int dest_row, int num_rows, int num_cols) - { - for (int row = 0; row < num_rows; row++) - Buffer.BlockCopy(input_array[source_row + row], 0, output_array[dest_row + row], 0, num_cols); - } - - public static void jcopy_sample_rows(byte[][] input_array, int source_row, byte[][] output_array, int dest_row, int num_rows, int num_cols) - { - for (int row = 0; row < num_rows; row++) - Buffer.BlockCopy(input_array[source_row++], 0, output_array[dest_row++], 0, num_cols); - } - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/bitread_perm_state.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/bitread_perm_state.cs deleted file mode 100644 index bacb9b964..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/bitread_perm_state.cs +++ /dev/null @@ -1,24 +0,0 @@ -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// Bitreading state saved across MCUs - /// - struct bitread_perm_state - { - public int get_buffer; /* current bit-extraction buffer */ - public int bits_left; /* # of unused bits in it */ - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/bitread_working_state.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/bitread_working_state.cs deleted file mode 100644 index 2599ab425..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/bitread_working_state.cs +++ /dev/null @@ -1,27 +0,0 @@ -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// Bitreading working state within an MCU - /// - struct bitread_working_state - { - public int get_buffer; /* current bit-extraction buffer */ - public int bits_left; /* # of unused bits in it */ - - /* Pointer needed by jpeg_fill_bit_buffer. */ - public jpeg_decompress_struct cinfo; /* back link to decompress master record */ - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/d_derived_tbl.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/d_derived_tbl.cs deleted file mode 100644 index 776168f33..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/d_derived_tbl.cs +++ /dev/null @@ -1,41 +0,0 @@ -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// Derived data constructed for each Huffman table - /// - class d_derived_tbl - { - /* Basic tables: (element [0] of each array is unused) */ - public int[] maxcode = new int[18]; /* largest code of length k (-1 if none) */ - /* (maxcode[17] is a sentinel to ensure jpeg_huff_decode terminates) */ - public int[] valoffset = new int[17]; /* huffval[] offset for codes of length k */ - /* valoffset[k] = huffval[] index of 1st symbol of code length k, less - * the smallest code of length k; so given a code of length k, the - * corresponding symbol is huffval[code + valoffset[k]] - */ - - /* Link to public Huffman table (needed only in jpeg_huff_decode) */ - public JHUFF_TBL pub; - - /* Lookahead tables: indexed by the next HUFF_LOOKAHEAD bits of - * the input data stream. If the next Huffman code is no more - * than HUFF_LOOKAHEAD bits long, we can obtain its length and - * the corresponding symbol directly from these tables. - */ - public int[] look_nbits = new int[1 << JpegConstants.HUFF_LOOKAHEAD]; /* # bits, or 0 if too long */ - public byte[] look_sym = new byte[1 << JpegConstants.HUFF_LOOKAHEAD]; /* symbol, or unused */ - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/huff_entropy_decoder.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/huff_entropy_decoder.cs deleted file mode 100644 index ff4620f2b..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/huff_entropy_decoder.cs +++ /dev/null @@ -1,316 +0,0 @@ -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -/* - * This file contains Huffman entropy decoding routines. - * - * Much of the complexity here has to do with supporting input suspension. - * If the data source module demands suspension, we want to be able to back - * up to the start of the current MCU. To do this, we copy state variables - * into local working storage, and update them back to the permanent - * storage only upon successful completion of an MCU. - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// Expanded entropy decoder object for Huffman decoding. - /// - /// The savable_state subrecord contains fields that change within an MCU, - /// but must not be updated permanently until we complete the MCU. - /// - class huff_entropy_decoder : jpeg_entropy_decoder - { - private class savable_state - { - public int[] last_dc_val = new int[JpegConstants.MAX_COMPS_IN_SCAN]; /* last DC coef for each component */ - - public void Assign(savable_state ss) - { - Buffer.BlockCopy(ss.last_dc_val, 0, last_dc_val, 0, last_dc_val.Length * sizeof(int)); - } - } - - /* These fields are loaded into local variables at start of each MCU. - * In case of suspension, we exit WITHOUT updating them. - */ - private bitread_perm_state m_bitstate; /* Bit buffer at start of MCU */ - private savable_state m_saved = new savable_state(); /* Other state at start of MCU */ - - /* These fields are NOT loaded into local working state. */ - private int m_restarts_to_go; /* MCUs left in this restart interval */ - - /* Pointers to derived tables (these workspaces have image lifespan) */ - private d_derived_tbl[] m_dc_derived_tbls = new d_derived_tbl[JpegConstants.NUM_HUFF_TBLS]; - private d_derived_tbl[] m_ac_derived_tbls = new d_derived_tbl[JpegConstants.NUM_HUFF_TBLS]; - - /* Precalculated info set up by start_pass for use in decode_mcu: */ - - /* Pointers to derived tables to be used for each block within an MCU */ - private d_derived_tbl[] m_dc_cur_tbls = new d_derived_tbl[JpegConstants.D_MAX_BLOCKS_IN_MCU]; - private d_derived_tbl[] m_ac_cur_tbls = new d_derived_tbl[JpegConstants.D_MAX_BLOCKS_IN_MCU]; - - /* Whether we care about the DC and AC coefficient values for each block */ - private bool[] m_dc_needed = new bool[JpegConstants.D_MAX_BLOCKS_IN_MCU]; - private bool[] m_ac_needed = new bool[JpegConstants.D_MAX_BLOCKS_IN_MCU]; - - public huff_entropy_decoder(jpeg_decompress_struct cinfo) - { - m_cinfo = cinfo; - - /* Mark tables unallocated */ - for (int i = 0; i < JpegConstants.NUM_HUFF_TBLS; i++) - m_dc_derived_tbls[i] = m_ac_derived_tbls[i] = null; - } - - /// - /// Initialize for a Huffman-compressed scan. - /// - public override void start_pass() - { - /* Check that the scan parameters Ss, Se, Ah/Al are OK for sequential JPEG. - * This ought to be an error condition, but we make it a warning because - * there are some baseline files out there with all zeroes in these bytes. - */ - if (m_cinfo.m_Ss != 0 || m_cinfo.m_Se != JpegConstants.DCTSIZE2 - 1 || m_cinfo.m_Ah != 0 || m_cinfo.m_Al != 0) - m_cinfo.WARNMS(J_MESSAGE_CODE.JWRN_NOT_SEQUENTIAL); - - for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) - { - jpeg_component_info componentInfo = m_cinfo.Comp_info[m_cinfo.m_cur_comp_info[ci]]; - int dctbl = componentInfo.Dc_tbl_no; - int actbl = componentInfo.Ac_tbl_no; - - /* Compute derived values for Huffman tables */ - /* We may do this more than once for a table, but it's not expensive */ - jpeg_make_d_derived_tbl(true, dctbl, ref m_dc_derived_tbls[dctbl]); - jpeg_make_d_derived_tbl(false, actbl, ref m_ac_derived_tbls[actbl]); - - /* Initialize DC predictions to 0 */ - m_saved.last_dc_val[ci] = 0; - } - - /* Precalculate decoding info for each block in an MCU of this scan */ - for (int blkn = 0; blkn < m_cinfo.m_blocks_in_MCU; blkn++) - { - int ci = m_cinfo.m_MCU_membership[blkn]; - jpeg_component_info componentInfo = m_cinfo.Comp_info[m_cinfo.m_cur_comp_info[ci]]; - - /* Precalculate which table to use for each block */ - m_dc_cur_tbls[blkn] = m_dc_derived_tbls[componentInfo.Dc_tbl_no]; - m_ac_cur_tbls[blkn] = m_ac_derived_tbls[componentInfo.Ac_tbl_no]; - - /* Decide whether we really care about the coefficient values */ - if (componentInfo.component_needed) - { - m_dc_needed[blkn] = true; - /* we don't need the ACs if producing a 1/8th-size image */ - m_ac_needed[blkn] = (componentInfo.DCT_scaled_size > 1); - } - else - { - m_dc_needed[blkn] = m_ac_needed[blkn] = false; - } - } - - /* Initialize bitread state variables */ - m_bitstate.bits_left = 0; - m_bitstate.get_buffer = 0; - m_insufficient_data = false; - - /* Initialize restart counter */ - m_restarts_to_go = m_cinfo.m_restart_interval; - } - - /// - /// Decode and return one MCU's worth of Huffman-compressed coefficients. - /// The coefficients are reordered from zigzag order into natural array order, - /// but are not dequantized. - /// - /// The i'th block of the MCU is stored into the block pointed to by - /// MCU_data[i]. WE ASSUME THIS AREA HAS BEEN ZEROED BY THE CALLER. - /// (Wholesale zeroing is usually a little faster than retail...) - /// - /// Returns false if data source requested suspension. In that case no - /// changes have been made to permanent state. (Exception: some output - /// coefficients may already have been assigned. This is harmless for - /// this module, since we'll just re-assign them on the next call.) - /// - public override bool decode_mcu(JBLOCK[] MCU_data) - { - /* Process restart marker if needed; may have to suspend */ - if (m_cinfo.m_restart_interval != 0) - { - if (m_restarts_to_go == 0) - { - if (!process_restart()) - return false; - } - } - - /* If we've run out of data, just leave the MCU set to zeroes. - * This way, we return uniform gray for the remainder of the segment. - */ - if (!m_insufficient_data) - { - /* Load up working state */ - int get_buffer; - int bits_left; - bitread_working_state br_state = new bitread_working_state(); - BITREAD_LOAD_STATE(m_bitstate, out get_buffer, out bits_left, ref br_state); - savable_state state = new savable_state(); - state.Assign(m_saved); - - /* Outer loop handles each block in the MCU */ - - for (int blkn = 0; blkn < m_cinfo.m_blocks_in_MCU; blkn++) - { - /* Decode a single block's worth of coefficients */ - - /* Section F.2.2.1: decode the DC coefficient difference */ - int s; - if (!HUFF_DECODE(out s, ref br_state, m_dc_cur_tbls[blkn], ref get_buffer, ref bits_left)) - return false; - - if (s != 0) - { - if (!CHECK_BIT_BUFFER(ref br_state, s, ref get_buffer, ref bits_left)) - return false; - - int r = GET_BITS(s, get_buffer, ref bits_left); - s = HUFF_EXTEND(r, s); - } - - if (m_dc_needed[blkn]) - { - /* Convert DC difference to actual value, update last_dc_val */ - int ci = m_cinfo.m_MCU_membership[blkn]; - s += state.last_dc_val[ci]; - state.last_dc_val[ci] = s; - - /* Output the DC coefficient (assumes jpeg_natural_order[0] = 0) */ - MCU_data[blkn][0] = (short) s; - } - - if (m_ac_needed[blkn]) - { - /* Section F.2.2.2: decode the AC coefficients */ - /* Since zeroes are skipped, output area must be cleared beforehand */ - for (int k = 1; k < JpegConstants.DCTSIZE2; k++) - { - if (!HUFF_DECODE(out s, ref br_state, m_ac_cur_tbls[blkn], ref get_buffer, ref bits_left)) - return false; - - int r = s >> 4; - s &= 15; - - if (s != 0) - { - k += r; - if (!CHECK_BIT_BUFFER(ref br_state, s, ref get_buffer, ref bits_left)) - return false; - r = GET_BITS(s, get_buffer, ref bits_left); - s = HUFF_EXTEND(r, s); - - /* Output coefficient in natural (dezigzagged) order. - * Note: the extra entries in jpeg_natural_order[] will save us - * if k >= DCTSIZE2, which could happen if the data is corrupted. - */ - MCU_data[blkn][JpegUtils.jpeg_natural_order[k]] = (short) s; - } - else - { - if (r != 15) - break; - - k += 15; - } - } - } - else - { - /* Section F.2.2.2: decode the AC coefficients */ - /* In this path we just discard the values */ - for (int k = 1; k < JpegConstants.DCTSIZE2; k++) - { - if (!HUFF_DECODE(out s, ref br_state, m_ac_cur_tbls[blkn], ref get_buffer, ref bits_left)) - return false; - - int r = s >> 4; - s &= 15; - - if (s != 0) - { - k += r; - if (!CHECK_BIT_BUFFER(ref br_state, s, ref get_buffer, ref bits_left)) - return false; - - DROP_BITS(s, ref bits_left); - } - else - { - if (r != 15) - break; - - k += 15; - } - } - } - } - - /* Completed MCU, so update state */ - BITREAD_SAVE_STATE(ref m_bitstate, get_buffer, bits_left); - m_saved.Assign(state); - } - - /* Account for restart interval (no-op if not using restarts) */ - m_restarts_to_go--; - - return true; - - } - - /// - /// Check for a restart marker and resynchronize decoder. - /// Returns false if must suspend. - /// - private bool process_restart() - { - /* Throw away any unused bits remaining in bit buffer; */ - /* include any full bytes in next_marker's count of discarded bytes */ - m_cinfo.m_marker.SkipBytes(m_bitstate.bits_left / 8); - m_bitstate.bits_left = 0; - - /* Advance past the RSTn marker */ - if (!m_cinfo.m_marker.read_restart_marker()) - return false; - - /* Re-initialize DC predictions to 0 */ - for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) - m_saved.last_dc_val[ci] = 0; - - /* Reset restart counter */ - m_restarts_to_go = m_cinfo.m_restart_interval; - - /* Reset out-of-data flag, unless read_restart_marker left us smack up - * against a marker. In that case we will end up treating the next data - * segment as empty, and we can avoid producing bogus output pixels by - * leaving the flag set. - */ - if (m_cinfo.m_unread_marker == 0) - m_insufficient_data = false; - - return true; - } - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/huff_entropy_encoder.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/huff_entropy_encoder.cs deleted file mode 100644 index 73c3423fe..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/huff_entropy_encoder.cs +++ /dev/null @@ -1,542 +0,0 @@ -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -/* - * This file contains Huffman entropy encoding routines. - * - * Much of the complexity here has to do with supporting output suspension. - * If the data destination module demands suspension, we want to be able to - * back up to the start of the current MCU. To do this, we copy state - * variables into local working storage, and update them back to the - * permanent JPEG objects only upon successful completion of an MCU. - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// Expanded entropy encoder object for Huffman encoding. - /// - class huff_entropy_encoder : jpeg_entropy_encoder - { - /* The savable_state subrecord contains fields that change within an MCU, - * but must not be updated permanently until we complete the MCU. - */ - private class savable_state - { - public int put_buffer; /* current bit-accumulation buffer */ - public int put_bits; /* # of bits now in it */ - public int[] last_dc_val = new int[JpegConstants.MAX_COMPS_IN_SCAN]; /* last DC coef for each component */ - } - - private bool m_gather_statistics; - - private savable_state m_saved = new savable_state(); /* Bit buffer & DC state at start of MCU */ - - /* These fields are NOT loaded into local working state. */ - private int m_restarts_to_go; /* MCUs left in this restart interval */ - private int m_next_restart_num; /* next restart number to write (0-7) */ - - /* Pointers to derived tables (these workspaces have image lifespan) */ - private c_derived_tbl[] m_dc_derived_tbls = new c_derived_tbl[JpegConstants.NUM_HUFF_TBLS]; - private c_derived_tbl[] m_ac_derived_tbls = new c_derived_tbl[JpegConstants.NUM_HUFF_TBLS]; - - /* Statistics tables for optimization */ - private long[][] m_dc_count_ptrs = new long[JpegConstants.NUM_HUFF_TBLS][]; - private long[][] m_ac_count_ptrs = new long[JpegConstants.NUM_HUFF_TBLS][]; - - public huff_entropy_encoder(jpeg_compress_struct cinfo) - { - m_cinfo = cinfo; - - /* Mark tables unallocated */ - for (int i = 0; i < JpegConstants.NUM_HUFF_TBLS; i++) - { - m_dc_derived_tbls[i] = m_ac_derived_tbls[i] = null; - m_dc_count_ptrs[i] = m_ac_count_ptrs[i] = null; - } - } - - /// - /// Initialize for a Huffman-compressed scan. - /// If gather_statistics is true, we do not output anything during the scan, - /// just count the Huffman symbols used and generate Huffman code tables. - /// - public override void start_pass(bool gather_statistics) - { - m_gather_statistics = gather_statistics; - - for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) - { - int dctbl = m_cinfo.Component_info[m_cinfo.m_cur_comp_info[ci]].Dc_tbl_no; - int actbl = m_cinfo.Component_info[m_cinfo.m_cur_comp_info[ci]].Ac_tbl_no; - if (m_gather_statistics) - { - /* Check for invalid table indexes */ - /* (make_c_derived_tbl does this in the other path) */ - if (dctbl < 0 || dctbl >= JpegConstants.NUM_HUFF_TBLS) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NO_HUFF_TABLE, dctbl); - - if (actbl < 0 || actbl >= JpegConstants.NUM_HUFF_TBLS) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NO_HUFF_TABLE, actbl); - - /* Allocate and zero the statistics tables */ - /* Note that jpeg_gen_optimal_table expects 257 entries in each table! */ - if (m_dc_count_ptrs[dctbl] == null) - m_dc_count_ptrs[dctbl] = new long[257]; - - Array.Clear(m_dc_count_ptrs[dctbl], 0, m_dc_count_ptrs[dctbl].Length); - - if (m_ac_count_ptrs[actbl] == null) - m_ac_count_ptrs[actbl] = new long[257]; - - Array.Clear(m_ac_count_ptrs[actbl], 0, m_ac_count_ptrs[actbl].Length); - } - else - { - /* Compute derived values for Huffman tables */ - /* We may do this more than once for a table, but it's not expensive */ - jpeg_make_c_derived_tbl(true, dctbl, ref m_dc_derived_tbls[dctbl]); - jpeg_make_c_derived_tbl(false, actbl, ref m_ac_derived_tbls[actbl]); - } - - /* Initialize DC predictions to 0 */ - m_saved.last_dc_val[ci] = 0; - } - - /* Initialize bit buffer to empty */ - m_saved.put_buffer = 0; - m_saved.put_bits = 0; - - /* Initialize restart stuff */ - m_restarts_to_go = m_cinfo.m_restart_interval; - m_next_restart_num = 0; - } - - public override bool encode_mcu(JBLOCK[][] MCU_data) - { - if (m_gather_statistics) - return encode_mcu_gather(MCU_data); - - return encode_mcu_huff(MCU_data); - } - - public override void finish_pass() - { - if (m_gather_statistics) - finish_pass_gather(); - else - finish_pass_huff(); - } - - /// - /// Encode and output one MCU's worth of Huffman-compressed coefficients. - /// - private bool encode_mcu_huff(JBLOCK[][] MCU_data) - { - /* Load up working state */ - savable_state state; - state = m_saved; - - /* Emit restart marker if needed */ - if (m_cinfo.m_restart_interval != 0) - { - if (m_restarts_to_go == 0) - { - if (!emit_restart(state, m_next_restart_num)) - return false; - } - } - - /* Encode the MCU data blocks */ - for (int blkn = 0; blkn < m_cinfo.m_blocks_in_MCU; blkn++) - { - int ci = m_cinfo.m_MCU_membership[blkn]; - if (!encode_one_block(state, MCU_data[blkn][0].data, state.last_dc_val[ci], - m_dc_derived_tbls[m_cinfo.Component_info[m_cinfo.m_cur_comp_info[ci]].Dc_tbl_no], - m_ac_derived_tbls[m_cinfo.Component_info[m_cinfo.m_cur_comp_info[ci]].Ac_tbl_no])) - { - return false; - } - - /* Update last_dc_val */ - state.last_dc_val[ci] = MCU_data[blkn][0][0]; - } - - /* Completed MCU, so update state */ - m_saved = state; - - /* Update restart-interval state too */ - if (m_cinfo.m_restart_interval != 0) - { - if (m_restarts_to_go == 0) - { - m_restarts_to_go = m_cinfo.m_restart_interval; - m_next_restart_num++; - m_next_restart_num &= 7; - } - - m_restarts_to_go--; - } - - return true; - } - - /// - /// Finish up at the end of a Huffman-compressed scan. - /// - private void finish_pass_huff() - { - /* Load up working state ... flush_bits needs it */ - savable_state state; - state = m_saved; - - /* Flush out the last data */ - if (!flush_bits(state)) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CANT_SUSPEND); - - /* Update state */ - m_saved = state; - } - - /// - /// Trial-encode one MCU's worth of Huffman-compressed coefficients. - /// No data is actually output, so no suspension return is possible. - /// - private bool encode_mcu_gather(JBLOCK[][] MCU_data) - { - /* Take care of restart intervals if needed */ - if (m_cinfo.m_restart_interval != 0) - { - if (m_restarts_to_go == 0) - { - /* Re-initialize DC predictions to 0 */ - for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) - m_saved.last_dc_val[ci] = 0; - - /* Update restart state */ - m_restarts_to_go = m_cinfo.m_restart_interval; - } - - m_restarts_to_go--; - } - - for (int blkn = 0; blkn < m_cinfo.m_blocks_in_MCU; blkn++) - { - int ci = m_cinfo.m_MCU_membership[blkn]; - htest_one_block(MCU_data[blkn][0].data, m_saved.last_dc_val[ci], - m_dc_count_ptrs[m_cinfo.Component_info[m_cinfo.m_cur_comp_info[ci]].Dc_tbl_no], - m_ac_count_ptrs[m_cinfo.Component_info[m_cinfo.m_cur_comp_info[ci]].Ac_tbl_no]); - m_saved.last_dc_val[ci] = MCU_data[blkn][0][0]; - } - - return true; - } - - /// - /// Finish up a statistics-gathering pass and create the new Huffman tables. - /// - private void finish_pass_gather() - { - /* It's important not to apply jpeg_gen_optimal_table more than once - * per table, because it clobbers the input frequency counts! - */ - bool[] did_dc = new bool [JpegConstants.NUM_HUFF_TBLS]; - bool[] did_ac = new bool[JpegConstants.NUM_HUFF_TBLS]; - - for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) - { - int dctbl = m_cinfo.Component_info[m_cinfo.m_cur_comp_info[ci]].Dc_tbl_no; - if (!did_dc[dctbl]) - { - if (m_cinfo.m_dc_huff_tbl_ptrs[dctbl] == null) - m_cinfo.m_dc_huff_tbl_ptrs[dctbl] = new JHUFF_TBL(); - - jpeg_gen_optimal_table(m_cinfo.m_dc_huff_tbl_ptrs[dctbl], m_dc_count_ptrs[dctbl]); - did_dc[dctbl] = true; - } - - int actbl = m_cinfo.Component_info[m_cinfo.m_cur_comp_info[ci]].Ac_tbl_no; - if (!did_ac[actbl]) - { - if (m_cinfo.m_ac_huff_tbl_ptrs[actbl] == null) - m_cinfo.m_ac_huff_tbl_ptrs[actbl] = new JHUFF_TBL(); - - jpeg_gen_optimal_table(m_cinfo.m_ac_huff_tbl_ptrs[actbl], m_ac_count_ptrs[actbl]); - did_ac[actbl] = true; - } - } - } - - /// - /// Encode a single block's worth of coefficients - /// - private bool encode_one_block(savable_state state, short[] block, int last_dc_val, c_derived_tbl dctbl, c_derived_tbl actbl) - { - /* Encode the DC coefficient difference per section F.1.2.1 */ - int temp = block[0] - last_dc_val; - int temp2 = temp; - if (temp < 0) - { - temp = -temp; /* temp is abs value of input */ - /* For a negative input, want temp2 = bitwise complement of abs(input) */ - /* This code assumes we are on a two's complement machine */ - temp2--; - } - - /* Find the number of bits needed for the magnitude of the coefficient */ - int nbits = 0; - while (temp != 0) - { - nbits++; - temp >>= 1; - } - - /* Check for out-of-range coefficient values. - * Since we're encoding a difference, the range limit is twice as much. - */ - if (nbits > MAX_HUFFMAN_COEF_BITS + 1) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_DCT_COEF); - - /* Emit the Huffman-coded symbol for the number of bits */ - if (!emit_bits(state, dctbl.ehufco[nbits], dctbl.ehufsi[nbits])) - return false; - - /* Emit that number of bits of the value, if positive, */ - /* or the complement of its magnitude, if negative. */ - if (nbits != 0) - { - /* emit_bits rejects calls with size 0 */ - if (!emit_bits(state, temp2, nbits)) - return false; - } - - /* Encode the AC coefficients per section F.1.2.2 */ - int r = 0; /* r = run length of zeros */ - for (int k = 1; k < JpegConstants.DCTSIZE2; k++) - { - temp = block[JpegUtils.jpeg_natural_order[k]]; - if (temp == 0) - { - r++; - } - else - { - /* if run length > 15, must emit special run-length-16 codes (0xF0) */ - while (r > 15) - { - if (!emit_bits(state, actbl.ehufco[0xF0], actbl.ehufsi[0xF0])) - return false; - r -= 16; - } - - temp2 = temp; - if (temp < 0) - { - temp = -temp; /* temp is abs value of input */ - /* This code assumes we are on a two's complement machine */ - temp2--; - } - - /* Find the number of bits needed for the magnitude of the coefficient */ - nbits = 1; /* there must be at least one 1 bit */ - while ((temp >>= 1) != 0) - nbits++; - - /* Check for out-of-range coefficient values */ - if (nbits > MAX_HUFFMAN_COEF_BITS) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_DCT_COEF); - - /* Emit Huffman symbol for run length / number of bits */ - int i = (r << 4) + nbits; - if (!emit_bits(state, actbl.ehufco[i], actbl.ehufsi[i])) - return false; - - /* Emit that number of bits of the value, if positive, */ - /* or the complement of its magnitude, if negative. */ - if (!emit_bits(state, temp2, nbits)) - return false; - - r = 0; - } - } - - /* If the last coef(s) were zero, emit an end-of-block code */ - if (r > 0) - { - if (!emit_bits(state, actbl.ehufco[0], actbl.ehufsi[0])) - return false; - } - - return true; - } - - /// - /// Huffman coding optimization. - /// - /// We first scan the supplied data and count the number of uses of each symbol - /// that is to be Huffman-coded. (This process MUST agree with the code above.) - /// Then we build a Huffman coding tree for the observed counts. - /// Symbols which are not needed at all for the particular image are not - /// assigned any code, which saves space in the DHT marker as well as in - /// the compressed data. - /// - private void htest_one_block(short[] block, int last_dc_val, long[] dc_counts, long[] ac_counts) - { - /* Encode the DC coefficient difference per section F.1.2.1 */ - int temp = block[0] - last_dc_val; - if (temp < 0) - temp = -temp; - - /* Find the number of bits needed for the magnitude of the coefficient */ - int nbits = 0; - while (temp != 0) - { - nbits++; - temp >>= 1; - } - - /* Check for out-of-range coefficient values. - * Since we're encoding a difference, the range limit is twice as much. - */ - if (nbits > MAX_HUFFMAN_COEF_BITS + 1) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_DCT_COEF); - - /* Count the Huffman symbol for the number of bits */ - dc_counts[nbits]++; - - /* Encode the AC coefficients per section F.1.2.2 */ - int r = 0; /* r = run length of zeros */ - for (int k = 1; k < JpegConstants.DCTSIZE2; k++) - { - temp = block[JpegUtils.jpeg_natural_order[k]]; - if (temp == 0) - { - r++; - } - else - { - /* if run length > 15, must emit special run-length-16 codes (0xF0) */ - while (r > 15) - { - ac_counts[0xF0]++; - r -= 16; - } - - /* Find the number of bits needed for the magnitude of the coefficient */ - if (temp < 0) - temp = -temp; - - /* Find the number of bits needed for the magnitude of the coefficient */ - nbits = 1; /* there must be at least one 1 bit */ - while ((temp >>= 1) != 0) - nbits++; - - /* Check for out-of-range coefficient values */ - if (nbits > MAX_HUFFMAN_COEF_BITS) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_DCT_COEF); - - /* Count Huffman symbol for run length / number of bits */ - ac_counts[(r << 4) + nbits]++; - - r = 0; - } - } - - /* If the last coef(s) were zero, emit an end-of-block code */ - if (r > 0) - ac_counts[0]++; - } - - private bool emit_byte(int val) - { - return m_cinfo.m_dest.emit_byte(val); - } - - /// - /// Only the right 24 bits of put_buffer are used; the valid bits are - /// left-justified in this part. At most 16 bits can be passed to emit_bits - /// in one call, and we never retain more than 7 bits in put_buffer - /// between calls, so 24 bits are sufficient. - /// - private bool emit_bits(savable_state state, int code, int size) - { - // Emit some bits; return true if successful, false if must suspend - /* This routine is heavily used, so it's worth coding tightly. */ - int put_buffer = code; - int put_bits = state.put_bits; - - /* if size is 0, caller used an invalid Huffman table entry */ - if (size == 0) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_HUFF_MISSING_CODE); - - put_buffer &= (1 << size) - 1; /* mask off any extra bits in code */ - put_bits += size; /* new number of bits in buffer */ - put_buffer <<= 24 - put_bits; /* align incoming bits */ - put_buffer |= state.put_buffer; /* and merge with old buffer contents */ - - while (put_bits >= 8) - { - int c = (put_buffer >> 16) & 0xFF; - if (!emit_byte(c)) - return false; - - if (c == 0xFF) - { - /* need to stuff a zero byte? */ - if (!emit_byte(0)) - return false; - } - - put_buffer <<= 8; - put_bits -= 8; - } - - state.put_buffer = put_buffer; /* update state variables */ - state.put_bits = put_bits; - - return true; - } - - private bool flush_bits(savable_state state) - { - if (!emit_bits(state, 0x7F, 7)) /* fill any partial byte with ones */ - return false; - - state.put_buffer = 0; /* and reset bit-buffer to empty */ - state.put_bits = 0; - return true; - } - - /// - /// Emit a restart marker and resynchronize predictions. - /// - private bool emit_restart(savable_state state, int restart_num) - { - if (!flush_bits(state)) - return false; - - if (!emit_byte(0xFF)) - return false; - - if (!emit_byte((int)(JPEG_MARKER.RST0 + restart_num))) - return false; - - /* Re-initialize DC predictions to 0 */ - for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) - state.last_dc_val[ci] = 0; - - /* The restart counter is not updated until we successfully write the MCU. */ - return true; - } - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_c_coef_controller.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_c_coef_controller.cs deleted file mode 100644 index c69e79c14..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_c_coef_controller.cs +++ /dev/null @@ -1,24 +0,0 @@ -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// Coefficient buffer control - /// - interface jpeg_c_coef_controller - { - void start_pass(J_BUF_MODE pass_mode); - bool compress_data(byte[][][] input_buf); - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_c_main_controller.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_c_main_controller.cs deleted file mode 100644 index 9bb591ffd..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_c_main_controller.cs +++ /dev/null @@ -1,119 +0,0 @@ -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -/* - * This file contains the main buffer controller for compression. - * The main buffer lies between the pre-processor and the JPEG - * compressor proper; it holds downsampled data in the JPEG colorspace. - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// Main buffer control (downsampled-data buffer) - /// - class jpeg_c_main_controller - { - private jpeg_compress_struct m_cinfo; - - private int m_cur_iMCU_row; /* number of current iMCU row */ - private int m_rowgroup_ctr; /* counts row groups received in iMCU row */ - private bool m_suspended; /* remember if we suspended output */ - - /* If using just a strip buffer, this points to the entire set of buffers - * (we allocate one for each component). In the full-image case, this - * points to the currently accessible strips of the virtual arrays. - */ - private byte[][][] m_buffer = new byte[JpegConstants.MAX_COMPONENTS][][]; - - public jpeg_c_main_controller(jpeg_compress_struct cinfo) - { - m_cinfo = cinfo; - - /* Allocate a strip buffer for each component */ - for (int ci = 0; ci < cinfo.m_num_components; ci++) - { - m_buffer[ci] = jpeg_common_struct.AllocJpegSamples( - cinfo.Component_info[ci].Width_in_blocks * JpegConstants.DCTSIZE, - cinfo.Component_info[ci].V_samp_factor * JpegConstants.DCTSIZE); - } - } - - // Initialize for a processing pass. - public void start_pass(J_BUF_MODE pass_mode) - { - /* Do nothing in raw-data mode. */ - if (m_cinfo.m_raw_data_in) - return; - - m_cur_iMCU_row = 0; /* initialize counters */ - m_rowgroup_ctr = 0; - m_suspended = false; - - if (pass_mode != J_BUF_MODE.JBUF_PASS_THRU) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_BUFFER_MODE); - } - - /// - /// Process some data. - /// This routine handles the simple pass-through mode, - /// where we have only a strip buffer. - /// - public void process_data(byte[][] input_buf, ref int in_row_ctr, int in_rows_avail) - { - while (m_cur_iMCU_row < m_cinfo.m_total_iMCU_rows) - { - /* Read input data if we haven't filled the main buffer yet */ - if (m_rowgroup_ctr < JpegConstants.DCTSIZE) - m_cinfo.m_prep.pre_process_data(input_buf, ref in_row_ctr, in_rows_avail, m_buffer, ref m_rowgroup_ctr, JpegConstants.DCTSIZE); - - /* If we don't have a full iMCU row buffered, return to application for - * more data. Note that preprocessor will always pad to fill the iMCU row - * at the bottom of the image. - */ - if (m_rowgroup_ctr != JpegConstants.DCTSIZE) - return; - - /* Send the completed row to the compressor */ - if (!m_cinfo.m_coef.compress_data(m_buffer)) - { - /* If compressor did not consume the whole row, then we must need to - * suspend processing and return to the application. In this situation - * we pretend we didn't yet consume the last input row; otherwise, if - * it happened to be the last row of the image, the application would - * think we were done. - */ - if (!m_suspended) - { - in_row_ctr--; - m_suspended = true; - } - - return; - } - - /* We did finish the row. Undo our little suspension hack if a previous - * call suspended; then mark the main buffer empty. - */ - if (m_suspended) - { - in_row_ctr++; - m_suspended = false; - } - - m_rowgroup_ctr = 0; - m_cur_iMCU_row++; - } - } - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_c_prep_controller.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_c_prep_controller.cs deleted file mode 100644 index 1a97cbe75..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_c_prep_controller.cs +++ /dev/null @@ -1,287 +0,0 @@ -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -/* - * This file contains the compression preprocessing controller. - * This controller manages the color conversion, downsampling, - * and edge expansion steps. - * - * Most of the complexity here is associated with buffering input rows - * as required by the downsampler. See the comments at the head of - * my_downsampler for the downsampler's needs. - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// Compression preprocessing (downsampling input buffer control). - /// - /// For the simple (no-context-row) case, we just need to buffer one - /// row group's worth of pixels for the downsampling step. At the bottom of - /// the image, we pad to a full row group by replicating the last pixel row. - /// The downsampler's last output row is then replicated if needed to pad - /// out to a full iMCU row. - /// - /// When providing context rows, we must buffer three row groups' worth of - /// pixels. Three row groups are physically allocated, but the row pointer - /// arrays are made five row groups high, with the extra pointers above and - /// below "wrapping around" to point to the last and first real row groups. - /// This allows the downsampler to access the proper context rows. - /// At the top and bottom of the image, we create dummy context rows by - /// copying the first or last real pixel row. This copying could be avoided - /// by pointer hacking as is done in jdmainct.c, but it doesn't seem worth the - /// trouble on the compression side. - /// - class jpeg_c_prep_controller - { - private jpeg_compress_struct m_cinfo; - - /* Downsampling input buffer. This buffer holds color-converted data - * until we have enough to do a downsample step. - */ - private byte[][][] m_color_buf = new byte[JpegConstants.MAX_COMPONENTS][][]; - private int m_colorBufRowsOffset; - - private int m_rows_to_go; /* counts rows remaining in source image */ - private int m_next_buf_row; /* index of next row to store in color_buf */ - - private int m_this_row_group; /* starting row index of group to process */ - private int m_next_buf_stop; /* downsample when we reach this index */ - - public jpeg_c_prep_controller(jpeg_compress_struct cinfo) - { - m_cinfo = cinfo; - - /* Allocate the color conversion buffer. - * We make the buffer wide enough to allow the downsampler to edge-expand - * horizontally within the buffer, if it so chooses. - */ - if (cinfo.m_downsample.NeedContextRows()) - { - /* Set up to provide context rows */ - create_context_buffer(); - } - else - { - /* No context, just make it tall enough for one row group */ - for (int ci = 0; ci < cinfo.m_num_components; ci++) - { - m_colorBufRowsOffset = 0; - m_color_buf[ci] = jpeg_compress_struct.AllocJpegSamples( - (cinfo.Component_info[ci].Width_in_blocks * JpegConstants.DCTSIZE * cinfo.m_max_h_samp_factor) / cinfo.Component_info[ci].H_samp_factor, - cinfo.m_max_v_samp_factor); - } - } - } - - /// - /// Initialize for a processing pass. - /// - public void start_pass(J_BUF_MODE pass_mode) - { - if (pass_mode != J_BUF_MODE.JBUF_PASS_THRU) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_BUFFER_MODE); - - /* Initialize total-height counter for detecting bottom of image */ - m_rows_to_go = m_cinfo.m_image_height; - - /* Mark the conversion buffer empty */ - m_next_buf_row = 0; - - /* Preset additional state variables for context mode. - * These aren't used in non-context mode, so we needn't test which mode. - */ - m_this_row_group = 0; - - /* Set next_buf_stop to stop after two row groups have been read in. */ - m_next_buf_stop = 2 * m_cinfo.m_max_v_samp_factor; - } - - public void pre_process_data(byte[][] input_buf, ref int in_row_ctr, int in_rows_avail, byte[][][] output_buf, ref int out_row_group_ctr, int out_row_groups_avail) - { - if (m_cinfo.m_downsample.NeedContextRows()) - pre_process_context(input_buf, ref in_row_ctr, in_rows_avail, output_buf, ref out_row_group_ctr, out_row_groups_avail); - else - pre_process_WithoutContext(input_buf, ref in_row_ctr, in_rows_avail, output_buf, ref out_row_group_ctr, out_row_groups_avail); - } - - /// - /// Create the wrapped-around downsampling input buffer needed for context mode. - /// - private void create_context_buffer() - { - int rgroup_height = m_cinfo.m_max_v_samp_factor; - for (int ci = 0; ci < m_cinfo.m_num_components; ci++) - { - int samplesPerRow = (m_cinfo.Component_info[ci].Width_in_blocks * JpegConstants.DCTSIZE * m_cinfo.m_max_h_samp_factor) / m_cinfo.Component_info[ci].H_samp_factor; - - byte[][] fake_buffer = new byte[5 * rgroup_height][]; - for (int i = 1; i < 4 * rgroup_height; i++) - fake_buffer[i] = new byte [samplesPerRow]; - - /* Allocate the actual buffer space (3 row groups) for this component. - * We make the buffer wide enough to allow the downsampler to edge-expand - * horizontally within the buffer, if it so chooses. - */ - byte[][] true_buffer = jpeg_common_struct.AllocJpegSamples(samplesPerRow, 3 * rgroup_height); - - /* Copy true buffer row pointers into the middle of the fake row array */ - for (int i = 0; i < 3 * rgroup_height; i++) - fake_buffer[rgroup_height + i] = true_buffer[i]; - - /* Fill in the above and below wraparound pointers */ - for (int i = 0; i < rgroup_height; i++) - { - fake_buffer[i] = true_buffer[2 * rgroup_height + i]; - fake_buffer[4 * rgroup_height + i] = true_buffer[i]; - } - - m_color_buf[ci] = fake_buffer; - m_colorBufRowsOffset = rgroup_height; - } - } - - /// - /// Process some data in the simple no-context case. - /// - /// Preprocessor output data is counted in "row groups". A row group - /// is defined to be v_samp_factor sample rows of each component. - /// Downsampling will produce this much data from each max_v_samp_factor - /// input rows. - /// - private void pre_process_WithoutContext(byte[][] input_buf, ref int in_row_ctr, int in_rows_avail, byte[][][] output_buf, ref int out_row_group_ctr, int out_row_groups_avail) - { - while (in_row_ctr < in_rows_avail && out_row_group_ctr < out_row_groups_avail) - { - /* Do color conversion to fill the conversion buffer. */ - int inrows = in_rows_avail - in_row_ctr; - int numrows = m_cinfo.m_max_v_samp_factor - m_next_buf_row; - numrows = Math.Min(numrows, inrows); - m_cinfo.m_cconvert.color_convert(input_buf, in_row_ctr, m_color_buf, m_colorBufRowsOffset + m_next_buf_row, numrows); - in_row_ctr += numrows; - m_next_buf_row += numrows; - m_rows_to_go -= numrows; - - /* If at bottom of image, pad to fill the conversion buffer. */ - if (m_rows_to_go == 0 && m_next_buf_row < m_cinfo.m_max_v_samp_factor) - { - for (int ci = 0; ci < m_cinfo.m_num_components; ci++) - expand_bottom_edge(m_color_buf[ci], m_colorBufRowsOffset, m_cinfo.m_image_width, m_next_buf_row, m_cinfo.m_max_v_samp_factor); - - m_next_buf_row = m_cinfo.m_max_v_samp_factor; - } - - /* If we've filled the conversion buffer, empty it. */ - if (m_next_buf_row == m_cinfo.m_max_v_samp_factor) - { - m_cinfo.m_downsample.downsample(m_color_buf, m_colorBufRowsOffset, output_buf, out_row_group_ctr); - m_next_buf_row = 0; - out_row_group_ctr++; - } - - /* If at bottom of image, pad the output to a full iMCU height. - * Note we assume the caller is providing a one-iMCU-height output buffer! - */ - if (m_rows_to_go == 0 && out_row_group_ctr < out_row_groups_avail) - { - for (int ci = 0; ci < m_cinfo.m_num_components; ci++) - { - jpeg_component_info componentInfo = m_cinfo.Component_info[ci]; - expand_bottom_edge(output_buf[ci], 0, componentInfo.Width_in_blocks * JpegConstants.DCTSIZE, - out_row_group_ctr * componentInfo.V_samp_factor, - out_row_groups_avail * componentInfo.V_samp_factor); - } - - out_row_group_ctr = out_row_groups_avail; - break; /* can exit outer loop without test */ - } - } - } - - /// - /// Process some data in the context case. - /// - private void pre_process_context(byte[][] input_buf, ref int in_row_ctr, int in_rows_avail, byte[][][] output_buf, ref int out_row_group_ctr, int out_row_groups_avail) - { - while (out_row_group_ctr < out_row_groups_avail) - { - if (in_row_ctr < in_rows_avail) - { - /* Do color conversion to fill the conversion buffer. */ - int inrows = in_rows_avail - in_row_ctr; - int numrows = m_next_buf_stop - m_next_buf_row; - numrows = Math.Min(numrows, inrows); - m_cinfo.m_cconvert.color_convert(input_buf, in_row_ctr, m_color_buf, m_colorBufRowsOffset + m_next_buf_row, numrows); - - /* Pad at top of image, if first time through */ - if (m_rows_to_go == m_cinfo.m_image_height) - { - for (int ci = 0; ci < m_cinfo.m_num_components; ci++) - { - for (int row = 1; row <= m_cinfo.m_max_v_samp_factor; row++) - JpegUtils.jcopy_sample_rows(m_color_buf[ci], m_colorBufRowsOffset, m_color_buf[ci], m_colorBufRowsOffset - row, 1, m_cinfo.m_image_width); - } - } - - in_row_ctr += numrows; - m_next_buf_row += numrows; - m_rows_to_go -= numrows; - } - else - { - /* Return for more data, unless we are at the bottom of the image. */ - if (m_rows_to_go != 0) - break; - - /* When at bottom of image, pad to fill the conversion buffer. */ - if (m_next_buf_row < m_next_buf_stop) - { - for (int ci = 0; ci < m_cinfo.m_num_components; ci++) - expand_bottom_edge(m_color_buf[ci], m_colorBufRowsOffset, m_cinfo.m_image_width, m_next_buf_row, m_next_buf_stop); - - m_next_buf_row = m_next_buf_stop; - } - } - - /* If we've gotten enough data, downsample a row group. */ - if (m_next_buf_row == m_next_buf_stop) - { - m_cinfo.m_downsample.downsample(m_color_buf, m_colorBufRowsOffset + m_this_row_group, output_buf, out_row_group_ctr); - out_row_group_ctr++; - - /* Advance pointers with wraparound as necessary. */ - m_this_row_group += m_cinfo.m_max_v_samp_factor; - int buf_height = m_cinfo.m_max_v_samp_factor * 3; - - if (m_this_row_group >= buf_height) - m_this_row_group = 0; - - if (m_next_buf_row >= buf_height) - m_next_buf_row = 0; - - m_next_buf_stop = m_next_buf_row + m_cinfo.m_max_v_samp_factor; - } - } - } - - /// - /// Expand an image vertically from height input_rows to height output_rows, - /// by duplicating the bottom row. - /// - private static void expand_bottom_edge(byte[][] image_data, int rowsOffset, int num_cols, int input_rows, int output_rows) - { - for (int row = input_rows; row < output_rows; row++) - JpegUtils.jcopy_sample_rows(image_data, rowsOffset + input_rows - 1, image_data, row, 1, num_cols); - } - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_color_converter.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_color_converter.cs deleted file mode 100644 index 3b3aba82a..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_color_converter.cs +++ /dev/null @@ -1,419 +0,0 @@ -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -/* - * This file contains input colorspace conversion routines. - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// Colorspace conversion - /// - class jpeg_color_converter - { - private const int SCALEBITS = 16; /* speediest right-shift on some machines */ - private const int CBCR_OFFSET = JpegConstants.CENTERJSAMPLE << SCALEBITS; - private const int ONE_HALF = 1 << (SCALEBITS - 1); - - // We allocate one big table and divide it up into eight parts, instead of - // doing eight alloc_small requests. This lets us use a single table base - // address, which can be held in a register in the inner loops on many - // machines (more than can hold all eight addresses, anyway). - private const int R_Y_OFF = 0; /* offset to R => Y section */ - private const int G_Y_OFF = (1 * (JpegConstants.MAXJSAMPLE+1)); /* offset to G => Y section */ - private const int B_Y_OFF = (2 * (JpegConstants.MAXJSAMPLE+1)); /* etc. */ - private const int R_CB_OFF = (3 * (JpegConstants.MAXJSAMPLE+1)); - private const int G_CB_OFF = (4 * (JpegConstants.MAXJSAMPLE+1)); - private const int B_CB_OFF = (5 * (JpegConstants.MAXJSAMPLE+1)); - private const int R_CR_OFF = B_CB_OFF; /* B=>Cb, R=>Cr are the same */ - private const int G_CR_OFF = (6 * (JpegConstants.MAXJSAMPLE+1)); - private const int B_CR_OFF = (7 * (JpegConstants.MAXJSAMPLE+1)); - private const int TABLE_SIZE = (8 * (JpegConstants.MAXJSAMPLE + 1)); - - private jpeg_compress_struct m_cinfo; - - private bool m_useNullStart; - - private bool m_useCmykYcckConvert; - private bool m_useGrayscaleConvert; - private bool m_useNullConvert; - private bool m_useRgbGrayConvert; - private bool m_useRgbYccConvert; - - private int[] m_rgb_ycc_tab; /* => table for RGB to YCbCr conversion */ - - public jpeg_color_converter(jpeg_compress_struct cinfo) - { - m_cinfo = cinfo; - - /* set start_pass to null method until we find out differently */ - m_useNullStart = true; - - /* Make sure input_components agrees with in_color_space */ - switch (cinfo.m_in_color_space) - { - case J_COLOR_SPACE.JCS_GRAYSCALE: - if (cinfo.m_input_components != 1) - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_IN_COLORSPACE); - break; - - case J_COLOR_SPACE.JCS_RGB: - case J_COLOR_SPACE.JCS_YCbCr: - if (cinfo.m_input_components != 3) - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_IN_COLORSPACE); - break; - - case J_COLOR_SPACE.JCS_CMYK: - case J_COLOR_SPACE.JCS_YCCK: - if (cinfo.m_input_components != 4) - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_IN_COLORSPACE); - break; - - default: - /* JCS_UNKNOWN can be anything */ - if (cinfo.m_input_components < 1) - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_IN_COLORSPACE); - break; - } - - /* Check num_components, set conversion method based on requested space */ - clearConvertFlags(); - switch (cinfo.m_jpeg_color_space) - { - case J_COLOR_SPACE.JCS_GRAYSCALE: - if (cinfo.m_num_components != 1) - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); - - if (cinfo.m_in_color_space == J_COLOR_SPACE.JCS_GRAYSCALE) - m_useGrayscaleConvert = true; - else if (cinfo.m_in_color_space == J_COLOR_SPACE.JCS_RGB) - { - m_useNullStart = false; // use rgb_ycc_start - m_useRgbGrayConvert = true; - } - else if (cinfo.m_in_color_space == J_COLOR_SPACE.JCS_YCbCr) - m_useGrayscaleConvert = true; - else - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); - break; - - case J_COLOR_SPACE.JCS_RGB: - if (cinfo.m_num_components != 3) - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); - - if (cinfo.m_in_color_space == J_COLOR_SPACE.JCS_RGB) - m_useNullConvert = true; - else - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); - break; - - case J_COLOR_SPACE.JCS_YCbCr: - if (cinfo.m_num_components != 3) - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); - - if (cinfo.m_in_color_space == J_COLOR_SPACE.JCS_RGB) - { - m_useNullStart = false; // use rgb_ycc_start - m_useRgbYccConvert = true; - } - else if (cinfo.m_in_color_space == J_COLOR_SPACE.JCS_YCbCr) - m_useNullConvert = true; - else - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); - break; - - case J_COLOR_SPACE.JCS_CMYK: - if (cinfo.m_num_components != 4) - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); - - if (cinfo.m_in_color_space == J_COLOR_SPACE.JCS_CMYK) - m_useNullConvert = true; - else - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); - break; - - case J_COLOR_SPACE.JCS_YCCK: - if (cinfo.m_num_components != 4) - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); - - if (cinfo.m_in_color_space == J_COLOR_SPACE.JCS_CMYK) - { - m_useNullStart = false; // use rgb_ycc_start - m_useCmykYcckConvert = true; - } - else if (cinfo.m_in_color_space == J_COLOR_SPACE.JCS_YCCK) - m_useNullConvert = true; - else - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); - break; - - default: - /* allow null conversion of JCS_UNKNOWN */ - if (cinfo.m_jpeg_color_space != cinfo.m_in_color_space || cinfo.m_num_components != cinfo.m_input_components) - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); - - m_useNullConvert = true; - break; - } - } - - public void start_pass() - { - if (!m_useNullStart) - rgb_ycc_start(); - } - - /// - /// Convert some rows of samples to the JPEG colorspace. - /// - /// Note that we change from the application's interleaved-pixel format - /// to our internal noninterleaved, one-plane-per-component format. - /// The input buffer is therefore three times as wide as the output buffer. - /// - /// A starting row offset is provided only for the output buffer. The caller - /// can easily adjust the passed input_buf value to accommodate any row - /// offset required on that side. - /// - public void color_convert(byte[][] input_buf, int input_row, byte[][][] output_buf, int output_row, int num_rows) - { - if (m_useCmykYcckConvert) - cmyk_ycck_convert(input_buf, input_row, output_buf, output_row, num_rows); - else if (m_useGrayscaleConvert) - grayscale_convert(input_buf, input_row, output_buf, output_row, num_rows); - else if (m_useRgbGrayConvert) - rgb_gray_convert(input_buf, input_row, output_buf, output_row, num_rows); - else if (m_useRgbYccConvert) - rgb_ycc_convert(input_buf, input_row, output_buf, output_row, num_rows); - else if (m_useNullConvert) - null_convert(input_buf, input_row, output_buf, output_row, num_rows); - else - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); - } - - /// - /// Initialize for RGB->YCC colorspace conversion. - /// - private void rgb_ycc_start() - { - /* Allocate and fill in the conversion tables. */ - m_rgb_ycc_tab = new int[TABLE_SIZE]; - - for (int i = 0; i <= JpegConstants.MAXJSAMPLE; i++) - { - m_rgb_ycc_tab[i + R_Y_OFF] = FIX(0.29900) * i; - m_rgb_ycc_tab[i + G_Y_OFF] = FIX(0.58700) * i; - m_rgb_ycc_tab[i + B_Y_OFF] = FIX(0.11400) * i + ONE_HALF; - m_rgb_ycc_tab[i + R_CB_OFF] = (-FIX(0.16874)) * i; - m_rgb_ycc_tab[i + G_CB_OFF] = (-FIX(0.33126)) * i; - - /* We use a rounding fudge-factor of 0.5-epsilon for Cb and Cr. - * This ensures that the maximum output will round to MAXJSAMPLE - * not MAXJSAMPLE+1, and thus that we don't have to range-limit. - */ - m_rgb_ycc_tab[i + B_CB_OFF] = FIX(0.50000) * i + CBCR_OFFSET + ONE_HALF - 1; - - /* B=>Cb and R=>Cr tables are the same - rgb_ycc_tab[i+R_CR_OFF] = FIX(0.50000) * i + CBCR_OFFSET + ONE_HALF-1; - */ - m_rgb_ycc_tab[i + G_CR_OFF] = (-FIX(0.41869)) * i; - m_rgb_ycc_tab[i + B_CR_OFF] = (-FIX(0.08131)) * i; - } - } - - private void clearConvertFlags() - { - m_useCmykYcckConvert = false; - m_useGrayscaleConvert = false; - m_useNullConvert = false; - m_useRgbGrayConvert = false; - m_useRgbYccConvert = false; - } - - private static int FIX(double x) - { - return (int)(x * (1L << SCALEBITS) + 0.5); - } - - /// - /// RGB -> YCbCr conversion: most common case - /// YCbCr is defined per CCIR 601-1, except that Cb and Cr are - /// normalized to the range 0..MAXJSAMPLE rather than -0.5 .. 0.5. - /// The conversion equations to be implemented are therefore - /// Y = 0.29900 * R + 0.58700 * G + 0.11400 * B - /// Cb = -0.16874 * R - 0.33126 * G + 0.50000 * B + CENTERJSAMPLE - /// Cr = 0.50000 * R - 0.41869 * G - 0.08131 * B + CENTERJSAMPLE - /// (These numbers are derived from TIFF 6.0 section 21, dated 3-June-92.) - /// To avoid floating-point arithmetic, we represent the fractional constants - /// as integers scaled up by 2^16 (about 4 digits precision); we have to divide - /// the products by 2^16, with appropriate rounding, to get the correct answer. - /// For even more speed, we avoid doing any multiplications in the inner loop - /// by precalculating the constants times R,G,B for all possible values. - /// For 8-bit JSAMPLEs this is very reasonable (only 256 entries per table); - /// for 12-bit samples it is still acceptable. It's not very reasonable for - /// 16-bit samples, but if you want lossless storage you shouldn't be changing - /// colorspace anyway. - /// The CENTERJSAMPLE offsets and the rounding fudge-factor of 0.5 are included - /// in the tables to save adding them separately in the inner loop. - /// - private void rgb_ycc_convert(byte[][] input_buf, int input_row, byte[][][] output_buf, int output_row, int num_rows) - { - int num_cols = m_cinfo.m_image_width; - for (int row = 0; row < num_rows; row++) - { - int columnOffset = 0; - for (int col = 0; col < num_cols; col++) - { - int r = input_buf[input_row + row][columnOffset + JpegConstants.RGB_RED]; - int g = input_buf[input_row + row][columnOffset + JpegConstants.RGB_GREEN]; - int b = input_buf[input_row + row][columnOffset + JpegConstants.RGB_BLUE]; - columnOffset += JpegConstants.RGB_PIXELSIZE; - - /* If the inputs are 0..MAXJSAMPLE, the outputs of these equations - * must be too; we do not need an explicit range-limiting operation. - * Hence the value being shifted is never negative, and we don't - * need the general RIGHT_SHIFT macro. - */ - /* Y */ - output_buf[0][output_row][col] = (byte)((m_rgb_ycc_tab[r + R_Y_OFF] + m_rgb_ycc_tab[g + G_Y_OFF] + m_rgb_ycc_tab[b + B_Y_OFF]) >> SCALEBITS); - /* Cb */ - output_buf[1][output_row][col] = (byte)((m_rgb_ycc_tab[r + R_CB_OFF] + m_rgb_ycc_tab[g + G_CB_OFF] + m_rgb_ycc_tab[b + B_CB_OFF]) >> SCALEBITS); - /* Cr */ - output_buf[2][output_row][col] = (byte)((m_rgb_ycc_tab[r + R_CR_OFF] + m_rgb_ycc_tab[g + G_CR_OFF] + m_rgb_ycc_tab[b + B_CR_OFF]) >> SCALEBITS); - } - - output_row++; - } - } - - /// - /// Convert some rows of samples to the JPEG colorspace. - /// This version handles RGB->grayscale conversion, which is the same - /// as the RGB->Y portion of RGB->YCbCr. - /// We assume rgb_ycc_start has been called (we only use the Y tables). - /// - private void rgb_gray_convert(byte[][] input_buf, int input_row, byte[][][] output_buf, int output_row, int num_rows) - { - int num_cols = m_cinfo.m_image_width; - for (int row = 0; row < num_rows; row++) - { - int columnOffset = 0; - for (int col = 0; col < num_cols; col++) - { - int r = input_buf[input_row + row][columnOffset + JpegConstants.RGB_RED]; - int g = input_buf[input_row + row][columnOffset + JpegConstants.RGB_GREEN]; - int b = input_buf[input_row + row][columnOffset + JpegConstants.RGB_BLUE]; - columnOffset += JpegConstants.RGB_PIXELSIZE; - - /* Y */ - output_buf[0][output_row][col] = (byte)((m_rgb_ycc_tab[r + R_Y_OFF] + m_rgb_ycc_tab[g + G_Y_OFF] + m_rgb_ycc_tab[b + B_Y_OFF]) >> SCALEBITS); - } - - output_row++; - } - } - - /// - /// Convert some rows of samples to the JPEG colorspace. - /// This version handles Adobe-style CMYK->YCCK conversion, - /// where we convert R=1-C, G=1-M, and B=1-Y to YCbCr using the same - /// conversion as above, while passing K (black) unchanged. - /// We assume rgb_ycc_start has been called. - /// - private void cmyk_ycck_convert(byte[][] input_buf, int input_row, byte[][][] output_buf, int output_row, int num_rows) - { - int num_cols = m_cinfo.m_image_width; - for (int row = 0; row < num_rows; row++) - { - int columnOffset = 0; - for (int col = 0; col < num_cols; col++) - { - int r = JpegConstants.MAXJSAMPLE - input_buf[input_row + row][columnOffset]; - int g = JpegConstants.MAXJSAMPLE - input_buf[input_row + row][columnOffset + 1]; - int b = JpegConstants.MAXJSAMPLE - input_buf[input_row + row][columnOffset + 2]; - - /* K passes through as-is */ - /* don't need GETJSAMPLE here */ - output_buf[3][output_row][col] = input_buf[input_row + row][columnOffset + 3]; - columnOffset += 4; - - /* If the inputs are 0..MAXJSAMPLE, the outputs of these equations - * must be too; we do not need an explicit range-limiting operation. - * Hence the value being shifted is never negative, and we don't - * need the general RIGHT_SHIFT macro. - */ - /* Y */ - output_buf[0][output_row][col] = (byte)((m_rgb_ycc_tab[r + R_Y_OFF] + m_rgb_ycc_tab[g + G_Y_OFF] + m_rgb_ycc_tab[b + B_Y_OFF]) >> SCALEBITS); - /* Cb */ - output_buf[1][output_row][col] = (byte)((m_rgb_ycc_tab[r + R_CB_OFF] + m_rgb_ycc_tab[g + G_CB_OFF] + m_rgb_ycc_tab[b + B_CB_OFF]) >> SCALEBITS); - /* Cr */ - output_buf[2][output_row][col] = (byte)((m_rgb_ycc_tab[r + R_CR_OFF] + m_rgb_ycc_tab[g + G_CR_OFF] + m_rgb_ycc_tab[b + B_CR_OFF]) >> SCALEBITS); - } - - output_row++; - } - } - - /// - /// Convert some rows of samples to the JPEG colorspace. - /// This version handles grayscale output with no conversion. - /// The source can be either plain grayscale or YCbCr (since Y == gray). - /// - private void grayscale_convert(byte[][] input_buf, int input_row, byte[][][] output_buf, int output_row, int num_rows) - { - int num_cols = m_cinfo.m_image_width; - int instride = m_cinfo.m_input_components; - - for (int row = 0; row < num_rows; row++) - { - int columnOffset = 0; - for (int col = 0; col < num_cols; col++) - { - /* don't need GETJSAMPLE() here */ - output_buf[0][output_row][col] = input_buf[input_row + row][columnOffset]; - columnOffset += instride; - } - - output_row++; - } - } - - /// - /// Convert some rows of samples to the JPEG colorspace. - /// This version handles multi-component colorspaces without conversion. - /// We assume input_components == num_components. - /// - private void null_convert(byte[][] input_buf, int input_row, byte[][][] output_buf, int output_row, int num_rows) - { - int nc = m_cinfo.m_num_components; - int num_cols = m_cinfo.m_image_width; - - for (int row = 0; row < num_rows; row++) - { - /* It seems fastest to make a separate pass for each component. */ - for (int ci = 0; ci < nc; ci++) - { - int columnOffset = 0; - for (int col = 0; col < num_cols; col++) - { - /* don't need GETJSAMPLE() here */ - output_buf[ci][output_row][col] = input_buf[input_row + row][columnOffset + ci]; - columnOffset += nc; - } - } - - output_row++; - } - } - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_color_deconverter.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_color_deconverter.cs deleted file mode 100644 index 47c4d8dc5..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_color_deconverter.cs +++ /dev/null @@ -1,388 +0,0 @@ -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -/* - * This file contains output colorspace conversion routines. - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// Colorspace conversion - /// - class jpeg_color_deconverter - { - private const int SCALEBITS = 16; /* speediest right-shift on some machines */ - private const int ONE_HALF = 1 << (SCALEBITS - 1); - - private enum ColorConverter - { - grayscale_converter, - ycc_rgb_converter, - gray_rgb_converter, - null_converter, - ycck_cmyk_converter - } - - private ColorConverter m_converter; - private jpeg_decompress_struct m_cinfo; - - private int[] m_perComponentOffsets; - - /* Private state for YCC->RGB conversion */ - private int[] m_Cr_r_tab; /* => table for Cr to R conversion */ - private int[] m_Cb_b_tab; /* => table for Cb to B conversion */ - private int[] m_Cr_g_tab; /* => table for Cr to G conversion */ - private int[] m_Cb_g_tab; /* => table for Cb to G conversion */ - - /// - /// Module initialization routine for output colorspace conversion. - /// - public jpeg_color_deconverter(jpeg_decompress_struct cinfo) - { - m_cinfo = cinfo; - - /* Make sure num_components agrees with jpeg_color_space */ - switch (cinfo.m_jpeg_color_space) - { - case J_COLOR_SPACE.JCS_GRAYSCALE: - if (cinfo.m_num_components != 1) - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); - break; - - case J_COLOR_SPACE.JCS_RGB: - case J_COLOR_SPACE.JCS_YCbCr: - if (cinfo.m_num_components != 3) - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); - break; - - case J_COLOR_SPACE.JCS_CMYK: - case J_COLOR_SPACE.JCS_YCCK: - if (cinfo.m_num_components != 4) - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); - break; - - default: - /* JCS_UNKNOWN can be anything */ - if (cinfo.m_num_components < 1) - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); - break; - } - - /* Set out_color_components and conversion method based on requested space. - * Also clear the component_needed flags for any unused components, - * so that earlier pipeline stages can avoid useless computation. - */ - - switch (cinfo.m_out_color_space) - { - case J_COLOR_SPACE.JCS_GRAYSCALE: - cinfo.m_out_color_components = 1; - if (cinfo.m_jpeg_color_space == J_COLOR_SPACE.JCS_GRAYSCALE || cinfo.m_jpeg_color_space == J_COLOR_SPACE.JCS_YCbCr) - { - m_converter = ColorConverter.grayscale_converter; - /* For color->grayscale conversion, only the Y (0) component is needed */ - for (int ci = 1; ci < cinfo.m_num_components; ci++) - cinfo.Comp_info[ci].component_needed = false; - } - else - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); - break; - - case J_COLOR_SPACE.JCS_RGB: - cinfo.m_out_color_components = JpegConstants.RGB_PIXELSIZE; - if (cinfo.m_jpeg_color_space == J_COLOR_SPACE.JCS_YCbCr) - { - m_converter = ColorConverter.ycc_rgb_converter; - build_ycc_rgb_table(); - } - else if (cinfo.m_jpeg_color_space == J_COLOR_SPACE.JCS_GRAYSCALE) - m_converter = ColorConverter.gray_rgb_converter; - else if (cinfo.m_jpeg_color_space == J_COLOR_SPACE.JCS_RGB) - m_converter = ColorConverter.null_converter; - else - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); - break; - - case J_COLOR_SPACE.JCS_CMYK: - cinfo.m_out_color_components = 4; - if (cinfo.m_jpeg_color_space == J_COLOR_SPACE.JCS_YCCK) - { - m_converter = ColorConverter.ycck_cmyk_converter; - build_ycc_rgb_table(); - } - else if (cinfo.m_jpeg_color_space == J_COLOR_SPACE.JCS_CMYK) - m_converter = ColorConverter.null_converter; - else - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); - break; - - default: - /* Permit null conversion to same output space */ - if (cinfo.m_out_color_space == cinfo.m_jpeg_color_space) - { - cinfo.m_out_color_components = cinfo.m_num_components; - m_converter = ColorConverter.null_converter; - } - else - { - /* unsupported non-null conversion */ - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); - } - break; - } - - if (cinfo.m_quantize_colors) - cinfo.m_output_components = 1; /* single colormapped output component */ - else - cinfo.m_output_components = cinfo.m_out_color_components; - } - - /// - /// Convert some rows of samples to the output colorspace. - /// - /// Note that we change from noninterleaved, one-plane-per-component format - /// to interleaved-pixel format. The output buffer is therefore three times - /// as wide as the input buffer. - /// A starting row offset is provided only for the input buffer. The caller - /// can easily adjust the passed output_buf value to accommodate any row - /// offset required on that side. - /// - public void color_convert(ComponentBuffer[] input_buf, int[] perComponentOffsets, int input_row, byte[][] output_buf, int output_row, int num_rows) - { - m_perComponentOffsets = perComponentOffsets; - - switch (m_converter) - { - case ColorConverter.grayscale_converter: - grayscale_convert(input_buf, input_row, output_buf, output_row, num_rows); - break; - case ColorConverter.ycc_rgb_converter: - ycc_rgb_convert(input_buf, input_row, output_buf, output_row, num_rows); - break; - case ColorConverter.gray_rgb_converter: - gray_rgb_convert(input_buf, input_row, output_buf, output_row, num_rows); - break; - case ColorConverter.null_converter: - null_convert(input_buf, input_row, output_buf, output_row, num_rows); - break; - case ColorConverter.ycck_cmyk_converter: - ycck_cmyk_convert(input_buf, input_row, output_buf, output_row, num_rows); - break; - default: - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); - break; - } - } - - /**************** YCbCr -> RGB conversion: most common case **************/ - - /* - * YCbCr is defined per CCIR 601-1, except that Cb and Cr are - * normalized to the range 0..MAXJSAMPLE rather than -0.5 .. 0.5. - * The conversion equations to be implemented are therefore - * R = Y + 1.40200 * Cr - * G = Y - 0.34414 * Cb - 0.71414 * Cr - * B = Y + 1.77200 * Cb - * where Cb and Cr represent the incoming values less CENTERJSAMPLE. - * (These numbers are derived from TIFF 6.0 section 21, dated 3-June-92.) - * - * To avoid floating-point arithmetic, we represent the fractional constants - * as integers scaled up by 2^16 (about 4 digits precision); we have to divide - * the products by 2^16, with appropriate rounding, to get the correct answer. - * Notice that Y, being an integral input, does not contribute any fraction - * so it need not participate in the rounding. - * - * For even more speed, we avoid doing any multiplications in the inner loop - * by precalculating the constants times Cb and Cr for all possible values. - * For 8-bit JSAMPLEs this is very reasonable (only 256 entries per table); - * for 12-bit samples it is still acceptable. It's not very reasonable for - * 16-bit samples, but if you want lossless storage you shouldn't be changing - * colorspace anyway. - * The Cr=>R and Cb=>B values can be rounded to integers in advance; the - * values for the G calculation are left scaled up, since we must add them - * together before rounding. - */ - - /// - /// Initialize tables for YCC->RGB colorspace conversion. - /// - private void build_ycc_rgb_table() - { - m_Cr_r_tab = new int[JpegConstants.MAXJSAMPLE + 1]; - m_Cb_b_tab = new int[JpegConstants.MAXJSAMPLE + 1]; - m_Cr_g_tab = new int[JpegConstants.MAXJSAMPLE + 1]; - m_Cb_g_tab = new int[JpegConstants.MAXJSAMPLE + 1]; - - for (int i = 0, x = -JpegConstants.CENTERJSAMPLE; i <= JpegConstants.MAXJSAMPLE; i++, x++) - { - /* i is the actual input pixel value, in the range 0..MAXJSAMPLE */ - /* The Cb or Cr value we are thinking of is x = i - CENTERJSAMPLE */ - /* Cr=>R value is nearest int to 1.40200 * x */ - m_Cr_r_tab[i] = JpegUtils.RIGHT_SHIFT(FIX(1.40200) * x + ONE_HALF, SCALEBITS); - - /* Cb=>B value is nearest int to 1.77200 * x */ - m_Cb_b_tab[i] = JpegUtils.RIGHT_SHIFT(FIX(1.77200) * x + ONE_HALF, SCALEBITS); - - /* Cr=>G value is scaled-up -0.71414 * x */ - m_Cr_g_tab[i] = (-FIX(0.71414)) * x; - - /* Cb=>G value is scaled-up -0.34414 * x */ - /* We also add in ONE_HALF so that need not do it in inner loop */ - m_Cb_g_tab[i] = (-FIX(0.34414)) * x + ONE_HALF; - } - } - - private void ycc_rgb_convert(ComponentBuffer[] input_buf, int input_row, byte[][] output_buf, int output_row, int num_rows) - { - int component0RowOffset = m_perComponentOffsets[0]; - int component1RowOffset = m_perComponentOffsets[1]; - int component2RowOffset = m_perComponentOffsets[2]; - - byte[] limit = m_cinfo.m_sample_range_limit; - int limitOffset = m_cinfo.m_sampleRangeLimitOffset; - - for (int row = 0; row < num_rows; row++) - { - int columnOffset = 0; - for (int col = 0; col < m_cinfo.m_output_width; col++) - { - int y = input_buf[0][input_row + component0RowOffset][col]; - int cb = input_buf[1][input_row + component1RowOffset][col]; - int cr = input_buf[2][input_row + component2RowOffset][col]; - - /* Range-limiting is essential due to noise introduced by DCT losses. */ - output_buf[output_row + row][columnOffset + JpegConstants.RGB_RED] = limit[limitOffset + y + m_Cr_r_tab[cr]]; - output_buf[output_row + row][columnOffset + JpegConstants.RGB_GREEN] = limit[limitOffset + y + JpegUtils.RIGHT_SHIFT(m_Cb_g_tab[cb] + m_Cr_g_tab[cr], SCALEBITS)]; - output_buf[output_row + row][columnOffset + JpegConstants.RGB_BLUE] = limit[limitOffset + y + m_Cb_b_tab[cb]]; - columnOffset += JpegConstants.RGB_PIXELSIZE; - } - - input_row++; - } - } - - /**************** Cases other than YCbCr -> RGB **************/ - - /// - /// Adobe-style YCCK->CMYK conversion. - /// We convert YCbCr to R=1-C, G=1-M, and B=1-Y using the same - /// conversion as above, while passing K (black) unchanged. - /// We assume build_ycc_rgb_table has been called. - /// - private void ycck_cmyk_convert(ComponentBuffer[] input_buf, int input_row, byte[][] output_buf, int output_row, int num_rows) - { - int component0RowOffset = m_perComponentOffsets[0]; - int component1RowOffset = m_perComponentOffsets[1]; - int component2RowOffset = m_perComponentOffsets[2]; - int component3RowOffset = m_perComponentOffsets[3]; - - byte[] limit = m_cinfo.m_sample_range_limit; - int limitOffset = m_cinfo.m_sampleRangeLimitOffset; - - int num_cols = m_cinfo.m_output_width; - for (int row = 0; row < num_rows; row++) - { - int columnOffset = 0; - for (int col = 0; col < num_cols; col++) - { - int y = input_buf[0][input_row + component0RowOffset][col]; - int cb = input_buf[1][input_row + component1RowOffset][col]; - int cr = input_buf[2][input_row + component2RowOffset][col]; - - /* Range-limiting is essential due to noise introduced by DCT losses. */ - output_buf[output_row + row][columnOffset] = limit[limitOffset + JpegConstants.MAXJSAMPLE - (y + m_Cr_r_tab[cr])]; /* red */ - output_buf[output_row + row][columnOffset + 1] = limit[limitOffset + JpegConstants.MAXJSAMPLE - (y + JpegUtils.RIGHT_SHIFT(m_Cb_g_tab[cb] + m_Cr_g_tab[cr], SCALEBITS))]; /* green */ - output_buf[output_row + row][columnOffset + 2] = limit[limitOffset + JpegConstants.MAXJSAMPLE - (y + m_Cb_b_tab[cb])]; /* blue */ - - /* K passes through unchanged */ - /* don't need GETJSAMPLE here */ - output_buf[output_row + row][columnOffset + 3] = input_buf[3][input_row + component3RowOffset][col]; - columnOffset += 4; - } - - input_row++; - } - } - - /// - /// Convert grayscale to RGB: just duplicate the graylevel three times. - /// This is provided to support applications that don't want to cope - /// with grayscale as a separate case. - /// - private void gray_rgb_convert(ComponentBuffer[] input_buf, int input_row, byte[][] output_buf, int output_row, int num_rows) - { - int component0RowOffset = m_perComponentOffsets[0]; - int component1RowOffset = m_perComponentOffsets[1]; - int component2RowOffset = m_perComponentOffsets[2]; - - int num_cols = m_cinfo.m_output_width; - for (int row = 0; row < num_rows; row++) - { - int columnOffset = 0; - for (int col = 0; col < num_cols; col++) - { - /* We can dispense with GETJSAMPLE() here */ - output_buf[output_row + row][columnOffset + JpegConstants.RGB_RED] = input_buf[0][input_row + component0RowOffset][col]; - output_buf[output_row + row][columnOffset + JpegConstants.RGB_GREEN] = input_buf[0][input_row + component1RowOffset][col]; - output_buf[output_row + row][columnOffset + JpegConstants.RGB_BLUE] = input_buf[0][input_row + component2RowOffset][col]; - columnOffset += JpegConstants.RGB_PIXELSIZE; - } - - input_row++; - } - } - - /// - /// Color conversion for grayscale: just copy the data. - /// This also works for YCbCr -> grayscale conversion, in which - /// we just copy the Y (luminance) component and ignore chrominance. - /// - private void grayscale_convert(ComponentBuffer[] input_buf, int input_row, byte[][] output_buf, int output_row, int num_rows) - { - JpegUtils.jcopy_sample_rows(input_buf[0], input_row + m_perComponentOffsets[0], output_buf, output_row, num_rows, m_cinfo.m_output_width); - } - - /// - /// Color conversion for no colorspace change: just copy the data, - /// converting from separate-planes to interleaved representation. - /// - private void null_convert(ComponentBuffer[] input_buf, int input_row, byte[][] output_buf, int output_row, int num_rows) - { - for (int row = 0; row < num_rows; row++) - { - for (int ci = 0; ci < m_cinfo.m_num_components; ci++) - { - int columnIndex = 0; - int componentOffset = 0; - int perComponentOffset = m_perComponentOffsets[ci]; - - for (int count = m_cinfo.m_output_width; count > 0; count--) - { - /* needn't bother with GETJSAMPLE() here */ - output_buf[output_row + row][ci + componentOffset] = input_buf[ci][input_row + perComponentOffset][columnIndex]; - componentOffset += m_cinfo.m_num_components; - columnIndex++; - } - } - - input_row++; - } - } - - private static int FIX(double x) - { - return (int)(x * (1L << SCALEBITS) + 0.5); - } - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_color_quantizer.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_color_quantizer.cs deleted file mode 100644 index 628d09132..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_color_quantizer.cs +++ /dev/null @@ -1,28 +0,0 @@ -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// Color quantization or color precision reduction - /// - interface jpeg_color_quantizer - { - void start_pass(bool is_pre_scan); - - void color_quantize(byte[][] input_buf, int in_row, byte[][] output_buf, int out_row, int num_rows); - - void finish_pass(); - void new_color_map(); - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_comp_master.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_comp_master.cs deleted file mode 100644 index 713853ffc..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_comp_master.cs +++ /dev/null @@ -1,364 +0,0 @@ -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// Master control module - /// - class jpeg_comp_master - { - private enum c_pass_type - { - main_pass, /* input data, also do first output step */ - huff_opt_pass, /* Huffman code optimization pass */ - output_pass /* data output pass */ - } - - private jpeg_compress_struct m_cinfo; - - private bool m_call_pass_startup; /* True if pass_startup must be called */ - private bool m_is_last_pass; /* True during last pass */ - - private c_pass_type m_pass_type; /* the type of the current pass */ - - private int m_pass_number; /* # of passes completed */ - private int m_total_passes; /* total # of passes needed */ - - private int m_scan_number; /* current index in scan_info[] */ - - public jpeg_comp_master(jpeg_compress_struct cinfo, bool transcode_only) - { - m_cinfo = cinfo; - - if (transcode_only) - { - /* no main pass in transcoding */ - if (cinfo.m_optimize_coding) - m_pass_type = c_pass_type.huff_opt_pass; - else - m_pass_type = c_pass_type.output_pass; - } - else - { - /* for normal compression, first pass is always this type: */ - m_pass_type = c_pass_type.main_pass; - } - - if (cinfo.m_optimize_coding) - m_total_passes = cinfo.m_num_scans * 2; - else - m_total_passes = cinfo.m_num_scans; - } - - /// - /// Per-pass setup. - /// - /// This is called at the beginning of each pass. We determine which - /// modules will be active during this pass and give them appropriate - /// start_pass calls. - /// We also set is_last_pass to indicate whether any more passes will - /// be required. - /// - public void prepare_for_pass() - { - switch (m_pass_type) - { - case c_pass_type.main_pass: - prepare_for_main_pass(); - break; - case c_pass_type.huff_opt_pass: - if (!prepare_for_huff_opt_pass()) - break; - prepare_for_output_pass(); - break; - case c_pass_type.output_pass: - prepare_for_output_pass(); - break; - default: - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NOT_COMPILED); - break; - } - - m_is_last_pass = (m_pass_number == m_total_passes - 1); - - /* Set up progress monitor's pass info if present */ - if (m_cinfo.m_progress != null) - { - m_cinfo.m_progress.Completed_passes = m_pass_number; - m_cinfo.m_progress.Total_passes = m_total_passes; - } - } - - /// - /// Special start-of-pass hook. - /// - /// This is called by jpeg_write_scanlines if call_pass_startup is true. - /// In single-pass processing, we need this hook because we don't want to - /// write frame/scan headers during jpeg_start_compress; we want to let the - /// application write COM markers etc. between jpeg_start_compress and the - /// jpeg_write_scanlines loop. - /// In multi-pass processing, this routine is not used. - /// - public void pass_startup() - { - m_cinfo.m_master.m_call_pass_startup = false; /* reset flag so call only once */ - - m_cinfo.m_marker.write_frame_header(); - m_cinfo.m_marker.write_scan_header(); - } - - /// - /// Finish up at end of pass. - /// - public void finish_pass() - { - /* The entropy coder always needs an end-of-pass call, - * either to analyze statistics or to flush its output buffer. - */ - m_cinfo.m_entropy.finish_pass(); - - /* Update state for next pass */ - switch (m_pass_type) - { - case c_pass_type.main_pass: - /* next pass is either output of scan 0 (after optimization) - * or output of scan 1 (if no optimization). - */ - m_pass_type = c_pass_type.output_pass; - if (!m_cinfo.m_optimize_coding) - m_scan_number++; - break; - case c_pass_type.huff_opt_pass: - /* next pass is always output of current scan */ - m_pass_type = c_pass_type.output_pass; - break; - case c_pass_type.output_pass: - /* next pass is either optimization or output of next scan */ - if (m_cinfo.m_optimize_coding) - m_pass_type = c_pass_type.huff_opt_pass; - m_scan_number++; - break; - } - - m_pass_number++; - } - - public bool IsLastPass() - { - return m_is_last_pass; - } - - public bool MustCallPassStartup() - { - return m_call_pass_startup; - } - - private void prepare_for_main_pass() - { - /* Initial pass: will collect input data, and do either Huffman - * optimization or data output for the first scan. - */ - select_scan_parameters(); - per_scan_setup(); - - if (!m_cinfo.m_raw_data_in) - { - m_cinfo.m_cconvert.start_pass(); - m_cinfo.m_prep.start_pass(J_BUF_MODE.JBUF_PASS_THRU); - } - - m_cinfo.m_fdct.start_pass(); - m_cinfo.m_entropy.start_pass(m_cinfo.m_optimize_coding); - m_cinfo.m_coef.start_pass((m_total_passes > 1 ? J_BUF_MODE.JBUF_SAVE_AND_PASS : J_BUF_MODE.JBUF_PASS_THRU)); - m_cinfo.m_main.start_pass(J_BUF_MODE.JBUF_PASS_THRU); - - if (m_cinfo.m_optimize_coding) - { - /* No immediate data output; postpone writing frame/scan headers */ - m_call_pass_startup = false; - } - else - { - /* Will write frame/scan headers at first jpeg_write_scanlines call */ - m_call_pass_startup = true; - } - } - - private bool prepare_for_huff_opt_pass() - { - /* Do Huffman optimization for a scan after the first one. */ - select_scan_parameters(); - per_scan_setup(); - - if (m_cinfo.m_Ss != 0 || m_cinfo.m_Ah == 0) - { - m_cinfo.m_entropy.start_pass(true); - m_cinfo.m_coef.start_pass(J_BUF_MODE.JBUF_CRANK_DEST); - m_call_pass_startup = false; - return false; - } - - /* Special case: Huffman DC refinement scans need no Huffman table - * and therefore we can skip the optimization pass for them. - */ - m_pass_type = c_pass_type.output_pass; - m_pass_number++; - return true; - } - - private void prepare_for_output_pass() - { - /* Do a data-output pass. */ - /* We need not repeat per-scan setup if prior optimization pass did it. */ - if (!m_cinfo.m_optimize_coding) - { - select_scan_parameters(); - per_scan_setup(); - } - - m_cinfo.m_entropy.start_pass(false); - m_cinfo.m_coef.start_pass(J_BUF_MODE.JBUF_CRANK_DEST); - - /* We emit frame/scan headers now */ - if (m_scan_number == 0) - m_cinfo.m_marker.write_frame_header(); - - m_cinfo.m_marker.write_scan_header(); - m_call_pass_startup = false; - } - - // Set up the scan parameters for the current scan - private void select_scan_parameters() - { - if (m_cinfo.m_scan_info != null) - { - /* Prepare for current scan --- the script is already validated */ - jpeg_scan_info scanInfo = m_cinfo.m_scan_info[m_scan_number]; - - m_cinfo.m_comps_in_scan = scanInfo.comps_in_scan; - for (int ci = 0; ci < scanInfo.comps_in_scan; ci++) - m_cinfo.m_cur_comp_info[ci] = scanInfo.component_index[ci]; - - m_cinfo.m_Ss = scanInfo.Ss; - m_cinfo.m_Se = scanInfo.Se; - m_cinfo.m_Ah = scanInfo.Ah; - m_cinfo.m_Al = scanInfo.Al; - } - else - { - /* Prepare for single sequential-JPEG scan containing all components */ - if (m_cinfo.m_num_components > JpegConstants.MAX_COMPS_IN_SCAN) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_COMPONENT_COUNT, m_cinfo.m_num_components, JpegConstants.MAX_COMPS_IN_SCAN); - - m_cinfo.m_comps_in_scan = m_cinfo.m_num_components; - for (int ci = 0; ci < m_cinfo.m_num_components; ci++) - m_cinfo.m_cur_comp_info[ci] = ci; - - m_cinfo.m_Ss = 0; - m_cinfo.m_Se = JpegConstants.DCTSIZE2 - 1; - m_cinfo.m_Ah = 0; - m_cinfo.m_Al = 0; - } - } - - /// - /// Do computations that are needed before processing a JPEG scan - /// cinfo.comps_in_scan and cinfo.cur_comp_info[] are already set - /// - private void per_scan_setup() - { - if (m_cinfo.m_comps_in_scan == 1) - { - /* Noninterleaved (single-component) scan */ - int compIndex = m_cinfo.m_cur_comp_info[0]; - - /* Overall image size in MCUs */ - m_cinfo.m_MCUs_per_row = m_cinfo.Component_info[compIndex].Width_in_blocks; - m_cinfo.m_MCU_rows_in_scan = m_cinfo.Component_info[compIndex].height_in_blocks; - - /* For noninterleaved scan, always one block per MCU */ - m_cinfo.Component_info[compIndex].MCU_width = 1; - m_cinfo.Component_info[compIndex].MCU_height = 1; - m_cinfo.Component_info[compIndex].MCU_blocks = 1; - m_cinfo.Component_info[compIndex].MCU_sample_width = JpegConstants.DCTSIZE; - m_cinfo.Component_info[compIndex].last_col_width = 1; - - /* For noninterleaved scans, it is convenient to define last_row_height - * as the number of block rows present in the last iMCU row. - */ - int tmp = m_cinfo.Component_info[compIndex].height_in_blocks % m_cinfo.Component_info[compIndex].V_samp_factor; - if (tmp == 0) - tmp = m_cinfo.Component_info[compIndex].V_samp_factor; - m_cinfo.Component_info[compIndex].last_row_height = tmp; - - /* Prepare array describing MCU composition */ - m_cinfo.m_blocks_in_MCU = 1; - m_cinfo.m_MCU_membership[0] = 0; - } - else - { - /* Interleaved (multi-component) scan */ - if (m_cinfo.m_comps_in_scan <= 0 || m_cinfo.m_comps_in_scan > JpegConstants.MAX_COMPS_IN_SCAN) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_COMPONENT_COUNT, m_cinfo.m_comps_in_scan, JpegConstants.MAX_COMPS_IN_SCAN); - - /* Overall image size in MCUs */ - m_cinfo.m_MCUs_per_row = JpegUtils.jdiv_round_up( - m_cinfo.m_image_width, m_cinfo.m_max_h_samp_factor * JpegConstants.DCTSIZE); - - m_cinfo.m_MCU_rows_in_scan = JpegUtils.jdiv_round_up(m_cinfo.m_image_height, - m_cinfo.m_max_v_samp_factor * JpegConstants.DCTSIZE); - - m_cinfo.m_blocks_in_MCU = 0; - - for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) - { - int compIndex = m_cinfo.m_cur_comp_info[ci]; - - /* Sampling factors give # of blocks of component in each MCU */ - m_cinfo.Component_info[compIndex].MCU_width = m_cinfo.Component_info[compIndex].H_samp_factor; - m_cinfo.Component_info[compIndex].MCU_height = m_cinfo.Component_info[compIndex].V_samp_factor; - m_cinfo.Component_info[compIndex].MCU_blocks = m_cinfo.Component_info[compIndex].MCU_width * m_cinfo.Component_info[compIndex].MCU_height; - m_cinfo.Component_info[compIndex].MCU_sample_width = m_cinfo.Component_info[compIndex].MCU_width * JpegConstants.DCTSIZE; - - /* Figure number of non-dummy blocks in last MCU column & row */ - int tmp = m_cinfo.Component_info[compIndex].Width_in_blocks % m_cinfo.Component_info[compIndex].MCU_width; - if (tmp == 0) - tmp = m_cinfo.Component_info[compIndex].MCU_width; - m_cinfo.Component_info[compIndex].last_col_width = tmp; - - tmp = m_cinfo.Component_info[compIndex].height_in_blocks % m_cinfo.Component_info[compIndex].MCU_height; - if (tmp == 0) - tmp = m_cinfo.Component_info[compIndex].MCU_height; - m_cinfo.Component_info[compIndex].last_row_height = tmp; - - /* Prepare array describing MCU composition */ - int mcublks = m_cinfo.Component_info[compIndex].MCU_blocks; - if (m_cinfo.m_blocks_in_MCU + mcublks > JpegConstants.C_MAX_BLOCKS_IN_MCU) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_MCU_SIZE); - - while (mcublks-- > 0) - m_cinfo.m_MCU_membership[m_cinfo.m_blocks_in_MCU++] = ci; - } - } - - /* Convert restart specified in rows to actual MCU count. */ - /* Note that count must fit in 16 bits, so we provide limiting. */ - if (m_cinfo.m_restart_in_rows > 0) - { - int nominal = m_cinfo.m_restart_in_rows * m_cinfo.m_MCUs_per_row; - m_cinfo.m_restart_interval = Math.Min(nominal, 65535); - } - } - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_d_coef_controller.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_d_coef_controller.cs deleted file mode 100644 index d9d11cb0f..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_d_coef_controller.cs +++ /dev/null @@ -1,761 +0,0 @@ -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -/* - * This file contains the coefficient buffer controller for decompression. - * This controller is the top level of the JPEG decompressor proper. - * The coefficient buffer lies between entropy decoding and inverse-DCT steps. - * - * In buffered-image mode, this controller is the interface between - * input-oriented processing and output-oriented processing. - * Also, the input side (only) is used when reading a file for transcoding. - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// Coefficient buffer control - /// - /// This code applies interblock smoothing as described by section K.8 - /// of the JPEG standard: the first 5 AC coefficients are estimated from - /// the DC values of a DCT block and its 8 neighboring blocks. - /// We apply smoothing only for progressive JPEG decoding, and only if - /// the coefficients it can estimate are not yet known to full precision. - /// - class jpeg_d_coef_controller - { - private const int SAVED_COEFS = 6; /* we save coef_bits[0..5] */ - - /* Natural-order array positions of the first 5 zigzag-order coefficients */ - private const int Q01_POS = 1; - private const int Q10_POS = 8; - private const int Q20_POS = 16; - private const int Q11_POS = 9; - private const int Q02_POS = 2; - - private enum DecompressorType - { - Ordinary, - Smooth, - OnePass - } - - private jpeg_decompress_struct m_cinfo; - private bool m_useDummyConsumeData; - private DecompressorType m_decompressor; - - /* These variables keep track of the current location of the input side. */ - /* cinfo.input_iMCU_row is also used for this. */ - private int m_MCU_ctr; /* counts MCUs processed in current row */ - private int m_MCU_vert_offset; /* counts MCU rows within iMCU row */ - private int m_MCU_rows_per_iMCU_row; /* number of such rows needed */ - - /* The output side's location is represented by cinfo.output_iMCU_row. */ - - /* In single-pass modes, it's sufficient to buffer just one MCU. - * We allocate a workspace of D_MAX_BLOCKS_IN_MCU coefficient blocks, - * and let the entropy decoder write into that workspace each time. - * (On 80x86, the workspace is FAR even though it's not really very big; - * this is to keep the module interfaces unchanged when a large coefficient - * buffer is necessary.) - * In multi-pass modes, this array points to the current MCU's blocks - * within the virtual arrays; it is used only by the input side. - */ - private JBLOCK[] m_MCU_buffer = new JBLOCK[JpegConstants.D_MAX_BLOCKS_IN_MCU]; - - /* In multi-pass modes, we need a virtual block array for each component. */ - private jvirt_array[] m_whole_image = new jvirt_array[JpegConstants.MAX_COMPONENTS]; - private jvirt_array[] m_coef_arrays; - - /* When doing block smoothing, we latch coefficient Al values here */ - private int[] m_coef_bits_latch; - private int m_coef_bits_savedOffset; - - public jpeg_d_coef_controller(jpeg_decompress_struct cinfo, bool need_full_buffer) - { - m_cinfo = cinfo; - - /* Create the coefficient buffer. */ - if (need_full_buffer) - { - /* Allocate a full-image virtual array for each component, */ - /* padded to a multiple of samp_factor DCT blocks in each direction. */ - /* Note we ask for a pre-zeroed array. */ - for (int ci = 0; ci < cinfo.m_num_components; ci++) - { - m_whole_image[ci] = jpeg_common_struct.CreateBlocksArray( - JpegUtils.jround_up(cinfo.Comp_info[ci].Width_in_blocks, cinfo.Comp_info[ci].H_samp_factor), - JpegUtils.jround_up(cinfo.Comp_info[ci].height_in_blocks, cinfo.Comp_info[ci].V_samp_factor)); - m_whole_image[ci].ErrorProcessor = cinfo; - } - - m_useDummyConsumeData = false; - m_decompressor = DecompressorType.Ordinary; - m_coef_arrays = m_whole_image; /* link to virtual arrays */ - } - else - { - /* We only need a single-MCU buffer. */ - JBLOCK[] buffer = new JBLOCK[JpegConstants.D_MAX_BLOCKS_IN_MCU]; - for (int i = 0; i < JpegConstants.D_MAX_BLOCKS_IN_MCU; i++) - { - buffer[i] = new JBLOCK(); - for (int ii = 0; ii < buffer[i].data.Length; ii++) - buffer[i].data[ii] = -12851; - - m_MCU_buffer[i] = buffer[i]; - } - - m_useDummyConsumeData = true; - m_decompressor = DecompressorType.OnePass; - m_coef_arrays = null; /* flag for no virtual arrays */ - } - } - - /// - /// Initialize for an input processing pass. - /// - public void start_input_pass() - { - m_cinfo.m_input_iMCU_row = 0; - start_iMCU_row(); - } - - /// - /// Consume input data and store it in the full-image coefficient buffer. - /// We read as much as one fully interleaved MCU row ("iMCU" row) per call, - /// ie, v_samp_factor block rows for each component in the scan. - /// - public ReadResult consume_data() - { - if (m_useDummyConsumeData) - return ReadResult.JPEG_SUSPENDED; /* Always indicate nothing was done */ - - JBLOCK[][][] buffer = new JBLOCK[JpegConstants.MAX_COMPS_IN_SCAN][][]; - - /* Align the virtual buffers for the components used in this scan. */ - for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) - { - jpeg_component_info componentInfo = m_cinfo.Comp_info[m_cinfo.m_cur_comp_info[ci]]; - - buffer[ci] = m_whole_image[componentInfo.Component_index].Access( - m_cinfo.m_input_iMCU_row * componentInfo.V_samp_factor, componentInfo.V_samp_factor); - - /* Note: entropy decoder expects buffer to be zeroed, - * but this is handled automatically by the memory manager - * because we requested a pre-zeroed array. - */ - } - - /* Loop to process one whole iMCU row */ - for (int yoffset = m_MCU_vert_offset; yoffset < m_MCU_rows_per_iMCU_row; yoffset++) - { - for (int MCU_col_num = m_MCU_ctr; MCU_col_num < m_cinfo.m_MCUs_per_row; MCU_col_num++) - { - /* Construct list of pointers to DCT blocks belonging to this MCU */ - int blkn = 0; /* index of current DCT block within MCU */ - for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) - { - jpeg_component_info componentInfo = m_cinfo.Comp_info[m_cinfo.m_cur_comp_info[ci]]; - int start_col = MCU_col_num * componentInfo.MCU_width; - for (int yindex = 0; yindex < componentInfo.MCU_height; yindex++) - { - for (int xindex = 0; xindex < componentInfo.MCU_width; xindex++) - { - m_MCU_buffer[blkn] = buffer[ci][yindex + yoffset][start_col + xindex]; - blkn++; - } - } - } - - /* Try to fetch the MCU. */ - if (!m_cinfo.m_entropy.decode_mcu(m_MCU_buffer)) - { - /* Suspension forced; update state counters and exit */ - m_MCU_vert_offset = yoffset; - m_MCU_ctr = MCU_col_num; - return ReadResult.JPEG_SUSPENDED; - } - } - - /* Completed an MCU row, but perhaps not an iMCU row */ - m_MCU_ctr = 0; - } - - /* Completed the iMCU row, advance counters for next one */ - m_cinfo.m_input_iMCU_row++; - if (m_cinfo.m_input_iMCU_row < m_cinfo.m_total_iMCU_rows) - { - start_iMCU_row(); - return ReadResult.JPEG_ROW_COMPLETED; - } - - /* Completed the scan */ - m_cinfo.m_inputctl.finish_input_pass(); - return ReadResult.JPEG_SCAN_COMPLETED; - } - - /// - /// Initialize for an output processing pass. - /// - public void start_output_pass() - { - /* If multipass, check to see whether to use block smoothing on this pass */ - if (m_coef_arrays != null) - { - if (m_cinfo.m_do_block_smoothing && smoothing_ok()) - m_decompressor = DecompressorType.Smooth; - else - m_decompressor = DecompressorType.Ordinary; - } - - m_cinfo.m_output_iMCU_row = 0; - } - - public ReadResult decompress_data(ComponentBuffer[] output_buf) - { - switch (m_decompressor) - { - case DecompressorType.Ordinary: - return decompress_data_ordinary(output_buf); - - case DecompressorType.Smooth: - return decompress_smooth_data(output_buf); - - case DecompressorType.OnePass: - return decompress_onepass(output_buf); - } - - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NOTIMPL); - return 0; - } - - /* Pointer to array of coefficient virtual arrays, or null if none */ - public jvirt_array[] GetCoefArrays() - { - return m_coef_arrays; - } - - /// - /// Decompress and return some data in the single-pass case. - /// Always attempts to emit one fully interleaved MCU row ("iMCU" row). - /// Input and output must run in lockstep since we have only a one-MCU buffer. - /// Return value is JPEG_ROW_COMPLETED, JPEG_SCAN_COMPLETED, or JPEG_SUSPENDED. - /// - /// NB: output_buf contains a plane for each component in image, - /// which we index according to the component's SOF position. - /// - private ReadResult decompress_onepass(ComponentBuffer[] output_buf) - { - int last_MCU_col = m_cinfo.m_MCUs_per_row - 1; - int last_iMCU_row = m_cinfo.m_total_iMCU_rows - 1; - - /* Loop to process as much as one whole iMCU row */ - for (int yoffset = m_MCU_vert_offset; yoffset < m_MCU_rows_per_iMCU_row; yoffset++) - { - for (int MCU_col_num = m_MCU_ctr; MCU_col_num <= last_MCU_col; MCU_col_num++) - { - /* Try to fetch an MCU. Entropy decoder expects buffer to be zeroed. */ - for (int i = 0; i < m_cinfo.m_blocks_in_MCU; i++) - Array.Clear(m_MCU_buffer[i].data, 0, m_MCU_buffer[i].data.Length); - - if (!m_cinfo.m_entropy.decode_mcu(m_MCU_buffer)) - { - /* Suspension forced; update state counters and exit */ - m_MCU_vert_offset = yoffset; - m_MCU_ctr = MCU_col_num; - return ReadResult.JPEG_SUSPENDED; - } - - /* Determine where data should go in output_buf and do the IDCT thing. - * We skip dummy blocks at the right and bottom edges (but blkn gets - * incremented past them!). Note the inner loop relies on having - * allocated the MCU_buffer[] blocks sequentially. - */ - int blkn = 0; /* index of current DCT block within MCU */ - for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) - { - jpeg_component_info componentInfo = m_cinfo.Comp_info[m_cinfo.m_cur_comp_info[ci]]; - - /* Don't bother to IDCT an uninteresting component. */ - if (!componentInfo.component_needed) - { - blkn += componentInfo.MCU_blocks; - continue; - } - - int useful_width = (MCU_col_num < last_MCU_col) ? componentInfo.MCU_width : componentInfo.last_col_width; - int outputIndex = yoffset * componentInfo.DCT_scaled_size; - int start_col = MCU_col_num * componentInfo.MCU_sample_width; - for (int yindex = 0; yindex < componentInfo.MCU_height; yindex++) - { - if (m_cinfo.m_input_iMCU_row < last_iMCU_row || yoffset + yindex < componentInfo.last_row_height) - { - int output_col = start_col; - for (int xindex = 0; xindex < useful_width; xindex++) - { - m_cinfo.m_idct.inverse(componentInfo.Component_index, - m_MCU_buffer[blkn + xindex].data, output_buf[componentInfo.Component_index], - outputIndex, output_col); - - output_col += componentInfo.DCT_scaled_size; - } - } - - blkn += componentInfo.MCU_width; - outputIndex += componentInfo.DCT_scaled_size; - } - } - } - - /* Completed an MCU row, but perhaps not an iMCU row */ - m_MCU_ctr = 0; - } - - /* Completed the iMCU row, advance counters for next one */ - m_cinfo.m_output_iMCU_row++; - m_cinfo.m_input_iMCU_row++; - if (m_cinfo.m_input_iMCU_row < m_cinfo.m_total_iMCU_rows) - { - start_iMCU_row(); - return ReadResult.JPEG_ROW_COMPLETED; - } - - /* Completed the scan */ - m_cinfo.m_inputctl.finish_input_pass(); - return ReadResult.JPEG_SCAN_COMPLETED; - } - - /// - /// Decompress and return some data in the multi-pass case. - /// Always attempts to emit one fully interleaved MCU row ("iMCU" row). - /// Return value is JPEG_ROW_COMPLETED, JPEG_SCAN_COMPLETED, or JPEG_SUSPENDED. - /// - /// NB: output_buf contains a plane for each component in image. - /// - private ReadResult decompress_data_ordinary(ComponentBuffer[] output_buf) - { - /* Force some input to be done if we are getting ahead of the input. */ - while (m_cinfo.m_input_scan_number < m_cinfo.m_output_scan_number || - (m_cinfo.m_input_scan_number == m_cinfo.m_output_scan_number && - m_cinfo.m_input_iMCU_row <= m_cinfo.m_output_iMCU_row)) - { - if (m_cinfo.m_inputctl.consume_input() == ReadResult.JPEG_SUSPENDED) - return ReadResult.JPEG_SUSPENDED; - } - - int last_iMCU_row = m_cinfo.m_total_iMCU_rows - 1; - - /* OK, output from the virtual arrays. */ - for (int ci = 0; ci < m_cinfo.m_num_components; ci++) - { - jpeg_component_info componentInfo = m_cinfo.Comp_info[ci]; - - /* Don't bother to IDCT an uninteresting component. */ - if (!componentInfo.component_needed) - continue; - - /* Align the virtual buffer for this component. */ - JBLOCK[][] buffer = m_whole_image[ci].Access(m_cinfo.m_output_iMCU_row * componentInfo.V_samp_factor, - componentInfo.V_samp_factor); - - /* Count non-dummy DCT block rows in this iMCU row. */ - int block_rows; - if (m_cinfo.m_output_iMCU_row < last_iMCU_row) - block_rows = componentInfo.V_samp_factor; - else - { - /* NB: can't use last_row_height here; it is input-side-dependent! */ - block_rows = componentInfo.height_in_blocks % componentInfo.V_samp_factor; - if (block_rows == 0) - block_rows = componentInfo.V_samp_factor; - } - - /* Loop over all DCT blocks to be processed. */ - int rowIndex = 0; - for (int block_row = 0; block_row < block_rows; block_row++) - { - int output_col = 0; - for (int block_num = 0; block_num < componentInfo.Width_in_blocks; block_num++) - { - m_cinfo.m_idct.inverse(componentInfo.Component_index, - buffer[block_row][block_num].data, output_buf[ci], rowIndex, output_col); - - output_col += componentInfo.DCT_scaled_size; - } - - rowIndex += componentInfo.DCT_scaled_size; - } - } - - m_cinfo.m_output_iMCU_row++; - if (m_cinfo.m_output_iMCU_row < m_cinfo.m_total_iMCU_rows) - return ReadResult.JPEG_ROW_COMPLETED; - - return ReadResult.JPEG_SCAN_COMPLETED; - } - - /// - /// Variant of decompress_data for use when doing block smoothing. - /// - private ReadResult decompress_smooth_data(ComponentBuffer[] output_buf) - { - /* Force some input to be done if we are getting ahead of the input. */ - while (m_cinfo.m_input_scan_number <= m_cinfo.m_output_scan_number && !m_cinfo.m_inputctl.EOIReached()) - { - if (m_cinfo.m_input_scan_number == m_cinfo.m_output_scan_number) - { - /* If input is working on current scan, we ordinarily want it to - * have completed the current row. But if input scan is DC, - * we want it to keep one row ahead so that next block row's DC - * values are up to date. - */ - int delta = (m_cinfo.m_Ss == 0) ? 1 : 0; - if (m_cinfo.m_input_iMCU_row > m_cinfo.m_output_iMCU_row + delta) - break; - } - - if (m_cinfo.m_inputctl.consume_input() == ReadResult.JPEG_SUSPENDED) - return ReadResult.JPEG_SUSPENDED; - } - - int last_iMCU_row = m_cinfo.m_total_iMCU_rows - 1; - - /* OK, output from the virtual arrays. */ - for (int ci = 0; ci < m_cinfo.m_num_components; ci++) - { - jpeg_component_info componentInfo = m_cinfo.Comp_info[ci]; - - /* Don't bother to IDCT an uninteresting component. */ - if (!componentInfo.component_needed) - continue; - - int block_rows; - int access_rows; - bool last_row; - /* Count non-dummy DCT block rows in this iMCU row. */ - if (m_cinfo.m_output_iMCU_row < last_iMCU_row) - { - block_rows = componentInfo.V_samp_factor; - access_rows = block_rows * 2; /* this and next iMCU row */ - last_row = false; - } - else - { - /* NB: can't use last_row_height here; it is input-side-dependent! */ - block_rows = componentInfo.height_in_blocks % componentInfo.V_samp_factor; - if (block_rows == 0) - block_rows = componentInfo.V_samp_factor; - access_rows = block_rows; /* this iMCU row only */ - last_row = true; - } - - /* Align the virtual buffer for this component. */ - JBLOCK[][] buffer = null; - bool first_row; - int bufferRowOffset = 0; - if (m_cinfo.m_output_iMCU_row > 0) - { - access_rows += componentInfo.V_samp_factor; /* prior iMCU row too */ - buffer = m_whole_image[ci].Access((m_cinfo.m_output_iMCU_row - 1) * componentInfo.V_samp_factor, access_rows); - bufferRowOffset = componentInfo.V_samp_factor; /* point to current iMCU row */ - first_row = false; - } - else - { - buffer = m_whole_image[ci].Access(0, access_rows); - first_row = true; - } - - /* Fetch component-dependent info */ - int coefBitsOffset = ci * SAVED_COEFS; - int Q00 = componentInfo.quant_table.quantval[0]; - int Q01 = componentInfo.quant_table.quantval[Q01_POS]; - int Q10 = componentInfo.quant_table.quantval[Q10_POS]; - int Q20 = componentInfo.quant_table.quantval[Q20_POS]; - int Q11 = componentInfo.quant_table.quantval[Q11_POS]; - int Q02 = componentInfo.quant_table.quantval[Q02_POS]; - int outputIndex = ci; - - /* Loop over all DCT blocks to be processed. */ - for (int block_row = 0; block_row < block_rows; block_row++) - { - int bufferIndex = bufferRowOffset + block_row; - - int prev_block_row = 0; - if (first_row && block_row == 0) - prev_block_row = bufferIndex; - else - prev_block_row = bufferIndex - 1; - - int next_block_row = 0; - if (last_row && block_row == block_rows - 1) - next_block_row = bufferIndex; - else - next_block_row = bufferIndex + 1; - - /* We fetch the surrounding DC values using a sliding-register approach. - * Initialize all nine here so as to do the right thing on narrow pics. - */ - int DC1 = buffer[prev_block_row][0][0]; - int DC2 = DC1; - int DC3 = DC1; - - int DC4 = buffer[bufferIndex][0][0]; - int DC5 = DC4; - int DC6 = DC4; - - int DC7 = buffer[next_block_row][0][0]; - int DC8 = DC7; - int DC9 = DC7; - - int output_col = 0; - int last_block_column = componentInfo.Width_in_blocks - 1; - for (int block_num = 0; block_num <= last_block_column; block_num++) - { - /* Fetch current DCT block into workspace so we can modify it. */ - JBLOCK workspace = new JBLOCK(); - Buffer.BlockCopy(buffer[bufferIndex][0].data, 0, workspace.data, 0, workspace.data.Length * sizeof(short)); - - /* Update DC values */ - if (block_num < last_block_column) - { - DC3 = buffer[prev_block_row][1][0]; - DC6 = buffer[bufferIndex][1][0]; - DC9 = buffer[next_block_row][1][0]; - } - - /* Compute coefficient estimates per K.8. - * An estimate is applied only if coefficient is still zero, - * and is not known to be fully accurate. - */ - /* AC01 */ - int Al = m_coef_bits_latch[m_coef_bits_savedOffset + coefBitsOffset + 1]; - if (Al != 0 && workspace[1] == 0) - { - int pred; - int num = 36 * Q00 * (DC4 - DC6); - if (num >= 0) - { - pred = ((Q01 << 7) + num) / (Q01 << 8); - if (Al > 0 && pred >= (1 << Al)) - pred = (1 << Al) - 1; - } - else - { - pred = ((Q01 << 7) - num) / (Q01 << 8); - if (Al > 0 && pred >= (1 << Al)) - pred = (1 << Al) - 1; - pred = -pred; - } - workspace[1] = (short) pred; - } - - /* AC10 */ - Al = m_coef_bits_latch[m_coef_bits_savedOffset + coefBitsOffset + 2]; - if (Al != 0 && workspace[8] == 0) - { - int pred; - int num = 36 * Q00 * (DC2 - DC8); - if (num >= 0) - { - pred = ((Q10 << 7) + num) / (Q10 << 8); - if (Al > 0 && pred >= (1 << Al)) - pred = (1 << Al) - 1; - } - else - { - pred = ((Q10 << 7) - num) / (Q10 << 8); - if (Al > 0 && pred >= (1 << Al)) - pred = (1 << Al) - 1; - pred = -pred; - } - workspace[8] = (short) pred; - } - - /* AC20 */ - Al = m_coef_bits_latch[m_coef_bits_savedOffset + coefBitsOffset + 3]; - if (Al != 0 && workspace[16] == 0) - { - int pred; - int num = 9 * Q00 * (DC2 + DC8 - 2 * DC5); - if (num >= 0) - { - pred = ((Q20 << 7) + num) / (Q20 << 8); - if (Al > 0 && pred >= (1 << Al)) - pred = (1 << Al) - 1; - } - else - { - pred = ((Q20 << 7) - num) / (Q20 << 8); - if (Al > 0 && pred >= (1 << Al)) - pred = (1 << Al) - 1; - pred = -pred; - } - workspace[16] = (short) pred; - } - - /* AC11 */ - Al = m_coef_bits_latch[m_coef_bits_savedOffset + coefBitsOffset + 4]; - if (Al != 0 && workspace[9] == 0) - { - int pred; - int num = 5 * Q00 * (DC1 - DC3 - DC7 + DC9); - if (num >= 0) - { - pred = ((Q11 << 7) + num) / (Q11 << 8); - if (Al > 0 && pred >= (1 << Al)) - pred = (1 << Al) - 1; - } - else - { - pred = ((Q11 << 7) - num) / (Q11 << 8); - if (Al > 0 && pred >= (1 << Al)) - pred = (1 << Al) - 1; - pred = -pred; - } - workspace[9] = (short) pred; - } - - /* AC02 */ - Al = m_coef_bits_latch[m_coef_bits_savedOffset + coefBitsOffset + 5]; - if (Al != 0 && workspace[2] == 0) - { - int pred; - int num = 9 * Q00 * (DC4 + DC6 - 2 * DC5); - if (num >= 0) - { - pred = ((Q02 << 7) + num) / (Q02 << 8); - if (Al > 0 && pred >= (1 << Al)) - pred = (1 << Al) - 1; - } - else - { - pred = ((Q02 << 7) - num) / (Q02 << 8); - if (Al > 0 && pred >= (1 << Al)) - pred = (1 << Al) - 1; - pred = -pred; - } - workspace[2] = (short) pred; - } - - /* OK, do the IDCT */ - m_cinfo.m_idct.inverse(componentInfo.Component_index, workspace.data, output_buf[outputIndex], 0, output_col); - - /* Advance for next column */ - DC1 = DC2; - DC2 = DC3; - DC4 = DC5; - DC5 = DC6; - DC7 = DC8; - DC8 = DC9; - - bufferIndex++; - prev_block_row++; - next_block_row++; - - output_col += componentInfo.DCT_scaled_size; - } - - outputIndex += componentInfo.DCT_scaled_size; - } - } - - m_cinfo.m_output_iMCU_row++; - if (m_cinfo.m_output_iMCU_row < m_cinfo.m_total_iMCU_rows) - return ReadResult.JPEG_ROW_COMPLETED; - - return ReadResult.JPEG_SCAN_COMPLETED; - } - - /// - /// Determine whether block smoothing is applicable and safe. - /// We also latch the current states of the coef_bits[] entries for the - /// AC coefficients; otherwise, if the input side of the decompressor - /// advances into a new scan, we might think the coefficients are known - /// more accurately than they really are. - /// - private bool smoothing_ok() - { - if (!m_cinfo.m_progressive_mode || m_cinfo.m_coef_bits == null) - return false; - - /* Allocate latch area if not already done */ - if (m_coef_bits_latch == null) - { - m_coef_bits_latch = new int[m_cinfo.m_num_components * SAVED_COEFS]; - m_coef_bits_savedOffset = 0; - } - - bool smoothing_useful = false; - for (int ci = 0; ci < m_cinfo.m_num_components; ci++) - { - /* All components' quantization values must already be latched. */ - JQUANT_TBL qtable = m_cinfo.Comp_info[ci].quant_table; - if (qtable == null) - return false; - - /* Verify DC & first 5 AC quantizers are nonzero to avoid zero-divide. */ - if (qtable.quantval[0] == 0 || qtable.quantval[Q01_POS] == 0 || - qtable.quantval[Q10_POS] == 0 || qtable.quantval[Q20_POS] == 0 || - qtable.quantval[Q11_POS] == 0 || qtable.quantval[Q02_POS] == 0) - { - return false; - } - - /* DC values must be at least partly known for all components. */ - if (m_cinfo.m_coef_bits[ci][0] < 0) - return false; - - /* Block smoothing is helpful if some AC coefficients remain inaccurate. */ - for (int coefi = 1; coefi <= 5; coefi++) - { - m_coef_bits_latch[m_coef_bits_savedOffset + coefi] = m_cinfo.m_coef_bits[ci][coefi]; - if (m_cinfo.m_coef_bits[ci][coefi] != 0) - smoothing_useful = true; - } - - m_coef_bits_savedOffset += SAVED_COEFS; - } - - return smoothing_useful; - } - - /// - /// Reset within-iMCU-row counters for a new row (input side) - /// - private void start_iMCU_row() - { - /* In an interleaved scan, an MCU row is the same as an iMCU row. - * In a noninterleaved scan, an iMCU row has v_samp_factor MCU rows. - * But at the bottom of the image, process only what's left. - */ - if (m_cinfo.m_comps_in_scan > 1) - { - m_MCU_rows_per_iMCU_row = 1; - } - else - { - jpeg_component_info componentInfo = m_cinfo.Comp_info[m_cinfo.m_cur_comp_info[0]]; - - if (m_cinfo.m_input_iMCU_row < (m_cinfo.m_total_iMCU_rows - 1)) - m_MCU_rows_per_iMCU_row = componentInfo.V_samp_factor; - else - m_MCU_rows_per_iMCU_row = componentInfo.last_row_height; - } - - m_MCU_ctr = 0; - m_MCU_vert_offset = 0; - } - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_d_main_controller.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_d_main_controller.cs deleted file mode 100644 index 4f60b57b2..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_d_main_controller.cs +++ /dev/null @@ -1,510 +0,0 @@ -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -/* - * This file contains the main buffer controller for decompression. - * The main buffer lies between the JPEG decompressor proper and the - * post-processor; it holds downsampled data in the JPEG colorspace. - * - * Note that this code is bypassed in raw-data mode, since the application - * supplies the equivalent of the main buffer in that case. - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// Main buffer control (downsampled-data buffer) - /// - /// In the current system design, the main buffer need never be a full-image - /// buffer; any full-height buffers will be found inside the coefficient or - /// postprocessing controllers. Nonetheless, the main controller is not - /// trivial. Its responsibility is to provide context rows for upsampling/ - /// rescaling, and doing this in an efficient fashion is a bit tricky. - /// - /// Postprocessor input data is counted in "row groups". A row group - /// is defined to be (v_samp_factor * DCT_scaled_size / min_DCT_scaled_size) - /// sample rows of each component. (We require DCT_scaled_size values to be - /// chosen such that these numbers are integers. In practice DCT_scaled_size - /// values will likely be powers of two, so we actually have the stronger - /// condition that DCT_scaled_size / min_DCT_scaled_size is an integer.) - /// Upsampling will typically produce max_v_samp_factor pixel rows from each - /// row group (times any additional scale factor that the upsampler is - /// applying). - /// - /// The coefficient controller will deliver data to us one iMCU row at a time; - /// each iMCU row contains v_samp_factor * DCT_scaled_size sample rows, or - /// exactly min_DCT_scaled_size row groups. (This amount of data corresponds - /// to one row of MCUs when the image is fully interleaved.) Note that the - /// number of sample rows varies across components, but the number of row - /// groups does not. Some garbage sample rows may be included in the last iMCU - /// row at the bottom of the image. - /// - /// Depending on the vertical scaling algorithm used, the upsampler may need - /// access to the sample row(s) above and below its current input row group. - /// The upsampler is required to set need_context_rows true at global selection - /// time if so. When need_context_rows is false, this controller can simply - /// obtain one iMCU row at a time from the coefficient controller and dole it - /// out as row groups to the postprocessor. - /// - /// When need_context_rows is true, this controller guarantees that the buffer - /// passed to postprocessing contains at least one row group's worth of samples - /// above and below the row group(s) being processed. Note that the context - /// rows "above" the first passed row group appear at negative row offsets in - /// the passed buffer. At the top and bottom of the image, the required - /// context rows are manufactured by duplicating the first or last real sample - /// row; this avoids having special cases in the upsampling inner loops. - /// - /// The amount of context is fixed at one row group just because that's a - /// convenient number for this controller to work with. The existing - /// upsamplers really only need one sample row of context. An upsampler - /// supporting arbitrary output rescaling might wish for more than one row - /// group of context when shrinking the image; tough, we don't handle that. - /// (This is justified by the assumption that downsizing will be handled mostly - /// by adjusting the DCT_scaled_size values, so that the actual scale factor at - /// the upsample step needn't be much less than one.) - /// - /// To provide the desired context, we have to retain the last two row groups - /// of one iMCU row while reading in the next iMCU row. (The last row group - /// can't be processed until we have another row group for its below-context, - /// and so we have to save the next-to-last group too for its above-context.) - /// We could do this most simply by copying data around in our buffer, but - /// that'd be very slow. We can avoid copying any data by creating a rather - /// strange pointer structure. Here's how it works. We allocate a workspace - /// consisting of M+2 row groups (where M = min_DCT_scaled_size is the number - /// of row groups per iMCU row). We create two sets of redundant pointers to - /// the workspace. Labeling the physical row groups 0 to M+1, the synthesized - /// pointer lists look like this: - /// M+1 M-1 - /// master pointer --> 0 master pointer --> 0 - /// 1 1 - /// ... ... - /// M-3 M-3 - /// M-2 M - /// M-1 M+1 - /// M M-2 - /// M+1 M-1 - /// 0 0 - /// We read alternate iMCU rows using each master pointer; thus the last two - /// row groups of the previous iMCU row remain un-overwritten in the workspace. - /// The pointer lists are set up so that the required context rows appear to - /// be adjacent to the proper places when we pass the pointer lists to the - /// upsampler. - /// - /// The above pictures describe the normal state of the pointer lists. - /// At top and bottom of the image, we diddle the pointer lists to duplicate - /// the first or last sample row as necessary (this is cheaper than copying - /// sample rows around). - /// - /// This scheme breaks down if M less than 2, ie, min_DCT_scaled_size is 1. In that - /// situation each iMCU row provides only one row group so the buffering logic - /// must be different (eg, we must read two iMCU rows before we can emit the - /// first row group). For now, we simply do not support providing context - /// rows when min_DCT_scaled_size is 1. That combination seems unlikely to - /// be worth providing --- if someone wants a 1/8th-size preview, they probably - /// want it quick and dirty, so a context-free upsampler is sufficient. - /// - class jpeg_d_main_controller - { - private enum DataProcessor - { - context_main, - simple_main, - crank_post - } - - /* context_state values: */ - private const int CTX_PREPARE_FOR_IMCU = 0; /* need to prepare for MCU row */ - private const int CTX_PROCESS_IMCU = 1; /* feeding iMCU to postprocessor */ - private const int CTX_POSTPONED_ROW = 2; /* feeding postponed row group */ - - private DataProcessor m_dataProcessor; - private jpeg_decompress_struct m_cinfo; - - /* Pointer to allocated workspace (M or M+2 row groups). */ - private byte[][][] m_buffer = new byte[JpegConstants.MAX_COMPONENTS][][]; - - private bool m_buffer_full; /* Have we gotten an iMCU row from decoder? */ - private int m_rowgroup_ctr; /* counts row groups output to postprocessor */ - - /* Remaining fields are only used in the context case. */ - - private int[][][] m_funnyIndices = new int[2][][] { new int[JpegConstants.MAX_COMPONENTS][], new int[JpegConstants.MAX_COMPONENTS][]}; - private int[] m_funnyOffsets = new int[JpegConstants.MAX_COMPONENTS]; - private int m_whichFunny; /* indicates which funny indices set is now in use */ - - private int m_context_state; /* process_data state machine status */ - private int m_rowgroups_avail; /* row groups available to postprocessor */ - private int m_iMCU_row_ctr; /* counts iMCU rows to detect image top/bot */ - - public jpeg_d_main_controller(jpeg_decompress_struct cinfo) - { - m_cinfo = cinfo; - - /* Allocate the workspace. - * ngroups is the number of row groups we need. - */ - int ngroups = cinfo.m_min_DCT_scaled_size; - if (cinfo.m_upsample.NeedContextRows()) - { - if (cinfo.m_min_DCT_scaled_size < 2) /* unsupported, see comments above */ - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NOTIMPL); - - alloc_funny_pointers(); /* Alloc space for xbuffer[] lists */ - ngroups = cinfo.m_min_DCT_scaled_size + 2; - } - - for (int ci = 0; ci < cinfo.m_num_components; ci++) - { - /* height of a row group of component */ - int rgroup = (cinfo.Comp_info[ci].V_samp_factor * cinfo.Comp_info[ci].DCT_scaled_size) / cinfo.m_min_DCT_scaled_size; - - m_buffer[ci] = jpeg_common_struct.AllocJpegSamples( - cinfo.Comp_info[ci].Width_in_blocks * cinfo.Comp_info[ci].DCT_scaled_size, - rgroup * ngroups); - } - } - - /// - /// Initialize for a processing pass. - /// - public void start_pass(J_BUF_MODE pass_mode) - { - switch (pass_mode) - { - case J_BUF_MODE.JBUF_PASS_THRU: - if (m_cinfo.m_upsample.NeedContextRows()) - { - m_dataProcessor = DataProcessor.context_main; - make_funny_pointers(); /* Create the xbuffer[] lists */ - m_whichFunny = 0; /* Read first iMCU row into xbuffer[0] */ - m_context_state = CTX_PREPARE_FOR_IMCU; - m_iMCU_row_ctr = 0; - } - else - { - /* Simple case with no context needed */ - m_dataProcessor = DataProcessor.simple_main; - } - m_buffer_full = false; /* Mark buffer empty */ - m_rowgroup_ctr = 0; - break; - case J_BUF_MODE.JBUF_CRANK_DEST: - /* For last pass of 2-pass quantization, just crank the postprocessor */ - m_dataProcessor = DataProcessor.crank_post; - break; - default: - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_BUFFER_MODE); - break; - } - } - - public void process_data(byte[][] output_buf, ref int out_row_ctr, int out_rows_avail) - { - switch (m_dataProcessor) - { - case DataProcessor.simple_main: - process_data_simple_main(output_buf, ref out_row_ctr, out_rows_avail); - break; - - case DataProcessor.context_main: - process_data_context_main(output_buf, ref out_row_ctr, out_rows_avail); - break; - - case DataProcessor.crank_post: - process_data_crank_post(output_buf, ref out_row_ctr, out_rows_avail); - break; - - default: - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NOTIMPL); - break; - } - } - - /// - /// Process some data. - /// This handles the simple case where no context is required. - /// - private void process_data_simple_main(byte[][] output_buf, ref int out_row_ctr, int out_rows_avail) - { - ComponentBuffer[] cb = new ComponentBuffer[JpegConstants.MAX_COMPONENTS]; - for (int i = 0; i < JpegConstants.MAX_COMPONENTS; i++) - { - cb[i] = new ComponentBuffer(); - cb[i].SetBuffer(m_buffer[i], null, 0); - } - - /* Read input data if we haven't filled the main buffer yet */ - if (!m_buffer_full) - { - if (m_cinfo.m_coef.decompress_data(cb) == ReadResult.JPEG_SUSPENDED) - { - /* suspension forced, can do nothing more */ - return; - } - - /* OK, we have an iMCU row to work with */ - m_buffer_full = true; - } - - /* There are always min_DCT_scaled_size row groups in an iMCU row. */ - int rowgroups_avail = m_cinfo.m_min_DCT_scaled_size; - - /* Note: at the bottom of the image, we may pass extra garbage row groups - * to the postprocessor. The postprocessor has to check for bottom - * of image anyway (at row resolution), so no point in us doing it too. - */ - - /* Feed the postprocessor */ - m_cinfo.m_post.post_process_data(cb, ref m_rowgroup_ctr, rowgroups_avail, output_buf, ref out_row_ctr, out_rows_avail); - - /* Has postprocessor consumed all the data yet? If so, mark buffer empty */ - if (m_rowgroup_ctr >= rowgroups_avail) - { - m_buffer_full = false; - m_rowgroup_ctr = 0; - } - } - - /// - /// Process some data. - /// This handles the case where context rows must be provided. - /// - private void process_data_context_main(byte[][] output_buf, ref int out_row_ctr, int out_rows_avail) - { - ComponentBuffer[] cb = new ComponentBuffer[m_cinfo.m_num_components]; - for (int i = 0; i < m_cinfo.m_num_components; i++) - { - cb[i] = new ComponentBuffer(); - cb[i].SetBuffer(m_buffer[i], m_funnyIndices[m_whichFunny][i], m_funnyOffsets[i]); - } - - /* Read input data if we haven't filled the main buffer yet */ - if (!m_buffer_full) - { - if (m_cinfo.m_coef.decompress_data(cb) == ReadResult.JPEG_SUSPENDED) - { - /* suspension forced, can do nothing more */ - return; - } - - /* OK, we have an iMCU row to work with */ - m_buffer_full = true; - - /* count rows received */ - m_iMCU_row_ctr++; - } - - /* Postprocessor typically will not swallow all the input data it is handed - * in one call (due to filling the output buffer first). Must be prepared - * to exit and restart. - - - This switch lets us keep track of how far we got. - * Note that each case falls through to the next on successful completion. - */ - if (m_context_state == CTX_POSTPONED_ROW) - { - /* Call postprocessor using previously set pointers for postponed row */ - m_cinfo.m_post.post_process_data(cb, ref m_rowgroup_ctr, - m_rowgroups_avail, output_buf, ref out_row_ctr, out_rows_avail); - - if (m_rowgroup_ctr < m_rowgroups_avail) - { - /* Need to suspend */ - return; - } - - m_context_state = CTX_PREPARE_FOR_IMCU; - - if (out_row_ctr >= out_rows_avail) - { - /* Postprocessor exactly filled output buf */ - return; - } - } - - if (m_context_state == CTX_PREPARE_FOR_IMCU) - { - /* Prepare to process first M-1 row groups of this iMCU row */ - m_rowgroup_ctr = 0; - m_rowgroups_avail = m_cinfo.m_min_DCT_scaled_size - 1; - - /* Check for bottom of image: if so, tweak pointers to "duplicate" - * the last sample row, and adjust rowgroups_avail to ignore padding rows. - */ - if (m_iMCU_row_ctr == m_cinfo.m_total_iMCU_rows) - set_bottom_pointers(); - - m_context_state = CTX_PROCESS_IMCU; - } - - if (m_context_state == CTX_PROCESS_IMCU) - { - /* Call postprocessor using previously set pointers */ - m_cinfo.m_post.post_process_data(cb, ref m_rowgroup_ctr, - m_rowgroups_avail, output_buf, ref out_row_ctr, out_rows_avail); - - if (m_rowgroup_ctr < m_rowgroups_avail) - { - /* Need to suspend */ - return; - } - - /* After the first iMCU, change wraparound pointers to normal state */ - if (m_iMCU_row_ctr == 1) - set_wraparound_pointers(); - - /* Prepare to load new iMCU row using other xbuffer list */ - m_whichFunny ^= 1; /* 0=>1 or 1=>0 */ - m_buffer_full = false; - - /* Still need to process last row group of this iMCU row, */ - /* which is saved at index M+1 of the other xbuffer */ - m_rowgroup_ctr = m_cinfo.m_min_DCT_scaled_size + 1; - m_rowgroups_avail = m_cinfo.m_min_DCT_scaled_size + 2; - m_context_state = CTX_POSTPONED_ROW; - } - } - - /// - /// Process some data. - /// Final pass of two-pass quantization: just call the postprocessor. - /// Source data will be the postprocessor controller's internal buffer. - /// - private void process_data_crank_post(byte[][] output_buf, ref int out_row_ctr, int out_rows_avail) - { - int dummy = 0; - m_cinfo.m_post.post_process_data(null, ref dummy, 0, output_buf, ref out_row_ctr, out_rows_avail); - } - - /// - /// Allocate space for the funny pointer lists. - /// This is done only once, not once per pass. - /// - private void alloc_funny_pointers() - { - int M = m_cinfo.m_min_DCT_scaled_size; - for (int ci = 0; ci < m_cinfo.m_num_components; ci++) - { - /* height of a row group of component */ - int rgroup = (m_cinfo.Comp_info[ci].V_samp_factor * m_cinfo.Comp_info[ci].DCT_scaled_size) / m_cinfo.m_min_DCT_scaled_size; - - /* Get space for pointer lists --- M+4 row groups in each list. - */ - m_funnyIndices[0][ci] = new int[rgroup * (M + 4)]; - m_funnyIndices[1][ci] = new int[rgroup * (M + 4)]; - m_funnyOffsets[ci] = rgroup; - } - } - - /// - /// Create the funny pointer lists discussed in the comments above. - /// The actual workspace is already allocated (in main.buffer), - /// and the space for the pointer lists is allocated too. - /// This routine just fills in the curiously ordered lists. - /// This will be repeated at the beginning of each pass. - /// - private void make_funny_pointers() - { - int M = m_cinfo.m_min_DCT_scaled_size; - for (int ci = 0; ci < m_cinfo.m_num_components; ci++) - { - /* height of a row group of component */ - int rgroup = (m_cinfo.Comp_info[ci].V_samp_factor * m_cinfo.Comp_info[ci].DCT_scaled_size) / m_cinfo.m_min_DCT_scaled_size; - - int[] ind0 = m_funnyIndices[0][ci]; - int[] ind1 = m_funnyIndices[1][ci]; - - /* First copy the workspace pointers as-is */ - for (int i = 0; i < rgroup * (M + 2); i++) - { - ind0[i + rgroup] = i; - ind1[i + rgroup] = i; - } - - /* In the second list, put the last four row groups in swapped order */ - for (int i = 0; i < rgroup * 2; i++) - { - ind1[rgroup * (M - 1) + i] = rgroup * M + i; - ind1[rgroup * (M + 1) + i] = rgroup * (M - 2) + i; - } - - /* The wraparound pointers at top and bottom will be filled later - * (see set_wraparound_pointers, below). Initially we want the "above" - * pointers to duplicate the first actual data line. This only needs - * to happen in xbuffer[0]. - */ - for (int i = 0; i < rgroup; i++) - ind0[i] = ind0[rgroup]; - } - } - - /// - /// Set up the "wraparound" pointers at top and bottom of the pointer lists. - /// This changes the pointer list state from top-of-image to the normal state. - /// - private void set_wraparound_pointers() - { - int M = m_cinfo.m_min_DCT_scaled_size; - for (int ci = 0; ci < m_cinfo.m_num_components; ci++) - { - /* height of a row group of component */ - int rgroup = (m_cinfo.Comp_info[ci].V_samp_factor * m_cinfo.Comp_info[ci].DCT_scaled_size) / m_cinfo.m_min_DCT_scaled_size; - - int[] ind0 = m_funnyIndices[0][ci]; - int[] ind1 = m_funnyIndices[1][ci]; - - for (int i = 0; i < rgroup; i++) - { - ind0[i] = ind0[rgroup * (M + 2) + i]; - ind1[i] = ind1[rgroup * (M + 2) + i]; - - ind0[rgroup * (M + 3) + i] = ind0[i + rgroup]; - ind1[rgroup * (M + 3) + i] = ind1[i + rgroup]; - } - } - } - - /// - /// Change the pointer lists to duplicate the last sample row at the bottom - /// of the image. m_whichFunny indicates which m_funnyIndices holds the final iMCU row. - /// Also sets rowgroups_avail to indicate number of nondummy row groups in row. - /// - private void set_bottom_pointers() - { - for (int ci = 0; ci < m_cinfo.m_num_components; ci++) - { - /* Count sample rows in one iMCU row and in one row group */ - int iMCUheight = m_cinfo.Comp_info[ci].V_samp_factor * m_cinfo.Comp_info[ci].DCT_scaled_size; - int rgroup = iMCUheight / m_cinfo.m_min_DCT_scaled_size; - - /* Count nondummy sample rows remaining for this component */ - int rows_left = m_cinfo.Comp_info[ci].downsampled_height % iMCUheight; - if (rows_left == 0) - rows_left = iMCUheight; - - /* Count nondummy row groups. Should get same answer for each component, - * so we need only do it once. - */ - if (ci == 0) - m_rowgroups_avail = (rows_left - 1) / rgroup + 1; - - /* Duplicate the last real sample row rgroup*2 times; this pads out the - * last partial rowgroup and ensures at least one full rowgroup of context. - */ - for (int i = 0; i < rgroup * 2; i++) - m_funnyIndices[m_whichFunny][ci][rows_left + i + rgroup] = m_funnyIndices[m_whichFunny][ci][rows_left - 1 + rgroup]; - } - } - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_d_post_controller.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_d_post_controller.cs deleted file mode 100644 index 79c711753..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_d_post_controller.cs +++ /dev/null @@ -1,248 +0,0 @@ -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -/* - * This file contains the decompression postprocessing controller. - * This controller manages the upsampling, color conversion, and color - * quantization/reduction steps; specifically, it controls the buffering - * between upsample/color conversion and color quantization/reduction. - * - * If no color quantization/reduction is required, then this module has no - * work to do, and it just hands off to the upsample/color conversion code. - * An integrated upsample/convert/quantize process would replace this module - * entirely. - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// Decompression postprocessing (color quantization buffer control) - /// - class jpeg_d_post_controller - { - private enum ProcessorType - { - OnePass, - PrePass, - Upsample, - SecondPass - } - - private ProcessorType m_processor; - - private jpeg_decompress_struct m_cinfo; - - /* Color quantization source buffer: this holds output data from - * the upsample/color conversion step to be passed to the quantizer. - * For two-pass color quantization, we need a full-image buffer; - * for one-pass operation, a strip buffer is sufficient. - */ - private jvirt_array m_whole_image; /* virtual array, or null if one-pass */ - private byte[][] m_buffer; /* strip buffer, or current strip of virtual */ - private int m_strip_height; /* buffer size in rows */ - /* for two-pass mode only: */ - private int m_starting_row; /* row # of first row in current strip */ - private int m_next_row; /* index of next row to fill/empty in strip */ - - /// - /// Initialize postprocessing controller. - /// - public jpeg_d_post_controller(jpeg_decompress_struct cinfo, bool need_full_buffer) - { - m_cinfo = cinfo; - - /* Create the quantization buffer, if needed */ - if (cinfo.m_quantize_colors) - { - /* The buffer strip height is max_v_samp_factor, which is typically - * an efficient number of rows for upsampling to return. - * (In the presence of output rescaling, we might want to be smarter?) - */ - m_strip_height = cinfo.m_max_v_samp_factor; - - if (need_full_buffer) - { - /* Two-pass color quantization: need full-image storage. */ - /* We round up the number of rows to a multiple of the strip height. */ - m_whole_image = jpeg_common_struct.CreateSamplesArray( - cinfo.m_output_width * cinfo.m_out_color_components, - JpegUtils.jround_up(cinfo.m_output_height, m_strip_height)); - m_whole_image.ErrorProcessor = cinfo; - } - else - { - /* One-pass color quantization: just make a strip buffer. */ - m_buffer = jpeg_common_struct.AllocJpegSamples( - cinfo.m_output_width * cinfo.m_out_color_components, m_strip_height); - } - } - } - - /// - /// Initialize for a processing pass. - /// - public void start_pass(J_BUF_MODE pass_mode) - { - switch (pass_mode) - { - case J_BUF_MODE.JBUF_PASS_THRU: - if (m_cinfo.m_quantize_colors) - { - /* Single-pass processing with color quantization. */ - m_processor = ProcessorType.OnePass; - /* We could be doing buffered-image output before starting a 2-pass - * color quantization; in that case, jinit_d_post_controller did not - * allocate a strip buffer. Use the virtual-array buffer as workspace. - */ - if (m_buffer == null) - m_buffer = m_whole_image.Access(0, m_strip_height); - } - else - { - /* For single-pass processing without color quantization, - * I have no work to do; just call the upsampler directly. - */ - m_processor = ProcessorType.Upsample; - } - break; - case J_BUF_MODE.JBUF_SAVE_AND_PASS: - /* First pass of 2-pass quantization */ - if (m_whole_image == null) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_BUFFER_MODE); - - m_processor = ProcessorType.PrePass; - break; - case J_BUF_MODE.JBUF_CRANK_DEST: - /* Second pass of 2-pass quantization */ - if (m_whole_image == null) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_BUFFER_MODE); - - m_processor = ProcessorType.SecondPass; - break; - default: - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_BUFFER_MODE); - break; - } - m_starting_row = m_next_row = 0; - } - - public void post_process_data(ComponentBuffer[] input_buf, ref int in_row_group_ctr, int in_row_groups_avail, byte[][] output_buf, ref int out_row_ctr, int out_rows_avail) - { - switch (m_processor) - { - case ProcessorType.OnePass: - post_process_1pass(input_buf, ref in_row_group_ctr, in_row_groups_avail, output_buf, ref out_row_ctr, out_rows_avail); - break; - case ProcessorType.PrePass: - post_process_prepass(input_buf, ref in_row_group_ctr, in_row_groups_avail, ref out_row_ctr); - break; - case ProcessorType.Upsample: - m_cinfo.m_upsample.upsample(input_buf, ref in_row_group_ctr, in_row_groups_avail, output_buf, ref out_row_ctr, out_rows_avail); - break; - case ProcessorType.SecondPass: - post_process_2pass(output_buf, ref out_row_ctr, out_rows_avail); - break; - default: - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NOTIMPL); - break; - } - } - - /// - /// Process some data in the one-pass (strip buffer) case. - /// This is used for color precision reduction as well as one-pass quantization. - /// - private void post_process_1pass(ComponentBuffer[] input_buf, ref int in_row_group_ctr, int in_row_groups_avail, byte[][] output_buf, ref int out_row_ctr, int out_rows_avail) - { - /* Fill the buffer, but not more than what we can dump out in one go. */ - /* Note we rely on the upsampler to detect bottom of image. */ - int max_rows = out_rows_avail - out_row_ctr; - if (max_rows > m_strip_height) - max_rows = m_strip_height; - - int num_rows = 0; - m_cinfo.m_upsample.upsample(input_buf, ref in_row_group_ctr, in_row_groups_avail, m_buffer, ref num_rows, max_rows); - - /* Quantize and emit data. */ - m_cinfo.m_cquantize.color_quantize(m_buffer, 0, output_buf, out_row_ctr, num_rows); - out_row_ctr += num_rows; - } - - /// - /// Process some data in the first pass of 2-pass quantization. - /// - private void post_process_prepass(ComponentBuffer[] input_buf, ref int in_row_group_ctr, int in_row_groups_avail, ref int out_row_ctr) - { - int old_next_row, num_rows; - - /* Reposition virtual buffer if at start of strip. */ - if (m_next_row == 0) - m_buffer = m_whole_image.Access(m_starting_row, m_strip_height); - - /* Upsample some data (up to a strip height's worth). */ - old_next_row = m_next_row; - m_cinfo.m_upsample.upsample(input_buf, ref in_row_group_ctr, in_row_groups_avail, m_buffer, ref m_next_row, m_strip_height); - - /* Allow quantizer to scan new data. No data is emitted, */ - /* but we advance out_row_ctr so outer loop can tell when we're done. */ - if (m_next_row > old_next_row) - { - num_rows = m_next_row - old_next_row; - m_cinfo.m_cquantize.color_quantize(m_buffer, old_next_row, null, 0, num_rows); - out_row_ctr += num_rows; - } - - /* Advance if we filled the strip. */ - if (m_next_row >= m_strip_height) - { - m_starting_row += m_strip_height; - m_next_row = 0; - } - } - - /// - /// Process some data in the second pass of 2-pass quantization. - /// - private void post_process_2pass(byte[][] output_buf, ref int out_row_ctr, int out_rows_avail) - { - int num_rows, max_rows; - - /* Reposition virtual buffer if at start of strip. */ - if (m_next_row == 0) - m_buffer = m_whole_image.Access(m_starting_row, m_strip_height); - - /* Determine number of rows to emit. */ - num_rows = m_strip_height - m_next_row; /* available in strip */ - max_rows = out_rows_avail - out_row_ctr; /* available in output area */ - if (num_rows > max_rows) - num_rows = max_rows; - - /* We have to check bottom of image here, can't depend on upsampler. */ - max_rows = m_cinfo.m_output_height - m_starting_row; - if (num_rows > max_rows) - num_rows = max_rows; - - /* Quantize and emit data. */ - m_cinfo.m_cquantize.color_quantize(m_buffer, m_next_row, output_buf, out_row_ctr, num_rows); - out_row_ctr += num_rows; - - /* Advance if we filled the strip. */ - m_next_row += num_rows; - if (m_next_row >= m_strip_height) - { - m_starting_row += m_strip_height; - m_next_row = 0; - } - } - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_decomp_master.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_decomp_master.cs deleted file mode 100644 index 7d8e81647..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_decomp_master.cs +++ /dev/null @@ -1,344 +0,0 @@ -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -/* - * This file contains master control logic for the JPEG decompressor. - * These routines are concerned with selecting the modules to be executed - * and with determining the number of passes and the work to be done in each - * pass. - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// Master control module - /// - class jpeg_decomp_master - { - private jpeg_decompress_struct m_cinfo; - - private int m_pass_number; /* # of passes completed */ - private bool m_is_dummy_pass; /* True during 1st pass for 2-pass quant */ - - private bool m_using_merged_upsample; /* true if using merged upsample/cconvert */ - - /* Saved references to initialized quantizer modules, - * in case we need to switch modes. - */ - private jpeg_color_quantizer m_quantizer_1pass; - private jpeg_color_quantizer m_quantizer_2pass; - - public jpeg_decomp_master(jpeg_decompress_struct cinfo) - { - m_cinfo = cinfo; - master_selection(); - } - - /// - /// Per-pass setup. - /// This is called at the beginning of each output pass. We determine which - /// modules will be active during this pass and give them appropriate - /// start_pass calls. We also set is_dummy_pass to indicate whether this - /// is a "real" output pass or a dummy pass for color quantization. - /// (In the latter case, we will crank the pass to completion.) - /// - public void prepare_for_output_pass() - { - if (m_is_dummy_pass) - { - /* Final pass of 2-pass quantization */ - m_is_dummy_pass = false; - m_cinfo.m_cquantize.start_pass(false); - m_cinfo.m_post.start_pass(J_BUF_MODE.JBUF_CRANK_DEST); - m_cinfo.m_main.start_pass(J_BUF_MODE.JBUF_CRANK_DEST); - } - else - { - if (m_cinfo.m_quantize_colors && m_cinfo.m_colormap == null) - { - /* Select new quantization method */ - if (m_cinfo.m_two_pass_quantize && m_cinfo.m_enable_2pass_quant) - { - m_cinfo.m_cquantize = m_quantizer_2pass; - m_is_dummy_pass = true; - } - else if (m_cinfo.m_enable_1pass_quant) - m_cinfo.m_cquantize = m_quantizer_1pass; - else - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_MODE_CHANGE); - } - - m_cinfo.m_idct.start_pass(); - m_cinfo.m_coef.start_output_pass(); - - if (!m_cinfo.m_raw_data_out) - { - m_cinfo.m_upsample.start_pass(); - - if (m_cinfo.m_quantize_colors) - m_cinfo.m_cquantize.start_pass(m_is_dummy_pass); - - m_cinfo.m_post.start_pass((m_is_dummy_pass ? J_BUF_MODE.JBUF_SAVE_AND_PASS : J_BUF_MODE.JBUF_PASS_THRU)); - m_cinfo.m_main.start_pass(J_BUF_MODE.JBUF_PASS_THRU); - } - } - - /* Set up progress monitor's pass info if present */ - if (m_cinfo.m_progress != null) - { - m_cinfo.m_progress.Completed_passes = m_pass_number; - m_cinfo.m_progress.Total_passes = m_pass_number + (m_is_dummy_pass ? 2 : 1); - - /* In buffered-image mode, we assume one more output pass if EOI not - * yet reached, but no more passes if EOI has been reached. - */ - if (m_cinfo.m_buffered_image && !m_cinfo.m_inputctl.EOIReached()) - m_cinfo.m_progress.Total_passes += (m_cinfo.m_enable_2pass_quant ? 2 : 1); - } - } - - /// - /// Finish up at end of an output pass. - /// - public void finish_output_pass() - { - if (m_cinfo.m_quantize_colors) - m_cinfo.m_cquantize.finish_pass(); - - m_pass_number++; - } - - public bool IsDummyPass() - { - return m_is_dummy_pass; - } - - /// - /// Master selection of decompression modules. - /// This is done once at jpeg_start_decompress time. We determine - /// which modules will be used and give them appropriate initialization calls. - /// We also initialize the decompressor input side to begin consuming data. - /// - /// Since jpeg_read_header has finished, we know what is in the SOF - /// and (first) SOS markers. We also have all the application parameter - /// settings. - /// - private void master_selection() - { - /* Initialize dimensions and other stuff */ - m_cinfo.jpeg_calc_output_dimensions(); - prepare_range_limit_table(); - - /* Width of an output scanline must be representable as int. */ - long samplesperrow = m_cinfo.m_output_width * m_cinfo.m_out_color_components; - int jd_samplesperrow = (int)samplesperrow; - if ((long)jd_samplesperrow != samplesperrow) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_WIDTH_OVERFLOW); - - /* Initialize my private state */ - m_pass_number = 0; - m_using_merged_upsample = m_cinfo.use_merged_upsample(); - - /* Color quantizer selection */ - m_quantizer_1pass = null; - m_quantizer_2pass = null; - - /* No mode changes if not using buffered-image mode. */ - if (!m_cinfo.m_quantize_colors || !m_cinfo.m_buffered_image) - { - m_cinfo.m_enable_1pass_quant = false; - m_cinfo.m_enable_external_quant = false; - m_cinfo.m_enable_2pass_quant = false; - } - - if (m_cinfo.m_quantize_colors) - { - if (m_cinfo.m_raw_data_out) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NOTIMPL); - - /* 2-pass quantizer only works in 3-component color space. */ - if (m_cinfo.m_out_color_components != 3) - { - m_cinfo.m_enable_1pass_quant = true; - m_cinfo.m_enable_external_quant = false; - m_cinfo.m_enable_2pass_quant = false; - m_cinfo.m_colormap = null; - } - else if (m_cinfo.m_colormap != null) - m_cinfo.m_enable_external_quant = true; - else if (m_cinfo.m_two_pass_quantize) - m_cinfo.m_enable_2pass_quant = true; - else - m_cinfo.m_enable_1pass_quant = true; - - if (m_cinfo.m_enable_1pass_quant) - { - m_cinfo.m_cquantize = new my_1pass_cquantizer(m_cinfo); - m_quantizer_1pass = m_cinfo.m_cquantize; - } - - /* We use the 2-pass code to map to external colormaps. */ - if (m_cinfo.m_enable_2pass_quant || m_cinfo.m_enable_external_quant) - { - m_cinfo.m_cquantize = new my_2pass_cquantizer(m_cinfo); - m_quantizer_2pass = m_cinfo.m_cquantize; - } - /* If both quantizers are initialized, the 2-pass one is left active; - * this is necessary for starting with quantization to an external map. - */ - } - - /* Post-processing: in particular, color conversion first */ - if (!m_cinfo.m_raw_data_out) - { - if (m_using_merged_upsample) - { - /* does color conversion too */ - m_cinfo.m_upsample = new my_merged_upsampler(m_cinfo); - } - else - { - m_cinfo.m_cconvert = new jpeg_color_deconverter(m_cinfo); - m_cinfo.m_upsample = new my_upsampler(m_cinfo); - } - - m_cinfo.m_post = new jpeg_d_post_controller(m_cinfo, m_cinfo.m_enable_2pass_quant); - } - - /* Inverse DCT */ - m_cinfo.m_idct = new jpeg_inverse_dct(m_cinfo); - - if (m_cinfo.m_progressive_mode) - m_cinfo.m_entropy = new phuff_entropy_decoder(m_cinfo); - else - m_cinfo.m_entropy = new huff_entropy_decoder(m_cinfo); - - /* Initialize principal buffer controllers. */ - bool use_c_buffer = m_cinfo.m_inputctl.HasMultipleScans() || m_cinfo.m_buffered_image; - m_cinfo.m_coef = new jpeg_d_coef_controller(m_cinfo, use_c_buffer); - - if (!m_cinfo.m_raw_data_out) - m_cinfo.m_main = new jpeg_d_main_controller(m_cinfo); - - /* Initialize input side of decompressor to consume first scan. */ - m_cinfo.m_inputctl.start_input_pass(); - - /* If jpeg_start_decompress will read the whole file, initialize - * progress monitoring appropriately. The input step is counted - * as one pass. - */ - if (m_cinfo.m_progress != null && !m_cinfo.m_buffered_image && m_cinfo.m_inputctl.HasMultipleScans()) - { - /* Estimate number of scans to set pass_limit. */ - int nscans; - if (m_cinfo.m_progressive_mode) - { - /* Arbitrarily estimate 2 interleaved DC scans + 3 AC scans/component. */ - nscans = 2 + 3 * m_cinfo.m_num_components; - } - else - { - /* For a non progressive multiscan file, estimate 1 scan per component. */ - nscans = m_cinfo.m_num_components; - } - - m_cinfo.m_progress.Pass_counter = 0; - m_cinfo.m_progress.Pass_limit = m_cinfo.m_total_iMCU_rows * nscans; - m_cinfo.m_progress.Completed_passes = 0; - m_cinfo.m_progress.Total_passes = (m_cinfo.m_enable_2pass_quant ? 3 : 2); - - /* Count the input pass as done */ - m_pass_number++; - } - } - - /// - /// Allocate and fill in the sample_range_limit table. - /// - /// Several decompression processes need to range-limit values to the range - /// 0..MAXJSAMPLE; the input value may fall somewhat outside this range - /// due to noise introduced by quantization, roundoff error, etc. These - /// processes are inner loops and need to be as fast as possible. On most - /// machines, particularly CPUs with pipelines or instruction prefetch, - /// a (subscript-check-less) C table lookup - /// x = sample_range_limit[x]; - /// is faster than explicit tests - /// - /// if (x & 0) - /// x = 0; - /// else if (x > MAXJSAMPLE) - /// x = MAXJSAMPLE; - /// - /// These processes all use a common table prepared by the routine below. - /// - /// For most steps we can mathematically guarantee that the initial value - /// of x is within MAXJSAMPLE + 1 of the legal range, so a table running from - /// -(MAXJSAMPLE + 1) to 2 * MAXJSAMPLE + 1 is sufficient. But for the initial - /// limiting step (just after the IDCT), a wildly out-of-range value is - /// possible if the input data is corrupt. To avoid any chance of indexing - /// off the end of memory and getting a bad-pointer trap, we perform the - /// post-IDCT limiting thus: x = range_limit[x & MASK]; - /// where MASK is 2 bits wider than legal sample data, ie 10 bits for 8-bit - /// samples. Under normal circumstances this is more than enough range and - /// a correct output will be generated; with bogus input data the mask will - /// cause wraparound, and we will safely generate a bogus-but-in-range output. - /// For the post-IDCT step, we want to convert the data from signed to unsigned - /// representation by adding CENTERJSAMPLE at the same time that we limit it. - /// So the post-IDCT limiting table ends up looking like this: - ///
-        ///     CENTERJSAMPLE, CENTERJSAMPLE + 1, ..., MAXJSAMPLE,
-        ///     MAXJSAMPLE (repeat 2 * (MAXJSAMPLE + 1) - CENTERJSAMPLE times),
-        ///     0          (repeat 2 * (MAXJSAMPLE + 1) - CENTERJSAMPLE times),
-        ///     0, 1, ..., CENTERJSAMPLE - 1
-        /// 
- /// Negative inputs select values from the upper half of the table after - /// masking. - /// - /// We can save some space by overlapping the start of the post-IDCT table - /// with the simpler range limiting table. The post-IDCT table begins at - /// sample_range_limit + CENTERJSAMPLE. - /// - /// Note that the table is allocated in near data space on PCs; it's small - /// enough and used often enough to justify this. - ///
- private void prepare_range_limit_table() - { - byte[] table = new byte[5 * (JpegConstants.MAXJSAMPLE + 1) + JpegConstants.CENTERJSAMPLE]; - - /* allow negative subscripts of simple table */ - int tableOffset = JpegConstants.MAXJSAMPLE + 1; - m_cinfo.m_sample_range_limit = table; - m_cinfo.m_sampleRangeLimitOffset = tableOffset; - - /* First segment of "simple" table: limit[x] = 0 for x < 0 */ - Array.Clear(table, 0, JpegConstants.MAXJSAMPLE + 1); - - /* Main part of "simple" table: limit[x] = x */ - for (int i = 0; i <= JpegConstants.MAXJSAMPLE; i++) - table[tableOffset + i] = (byte) i; - - tableOffset += JpegConstants.CENTERJSAMPLE; /* Point to where post-IDCT table starts */ - - /* End of simple table, rest of first half of post-IDCT table */ - for (int i = JpegConstants.CENTERJSAMPLE; i < 2 * (JpegConstants.MAXJSAMPLE + 1); i++) - table[tableOffset + i] = JpegConstants.MAXJSAMPLE; - - /* Second half of post-IDCT table */ - Array.Clear(table, tableOffset + 2 * (JpegConstants.MAXJSAMPLE + 1), - 2 * (JpegConstants.MAXJSAMPLE + 1) - JpegConstants.CENTERJSAMPLE); - - Buffer.BlockCopy(m_cinfo.m_sample_range_limit, 0, table, - tableOffset + 4 * (JpegConstants.MAXJSAMPLE + 1) - JpegConstants.CENTERJSAMPLE, JpegConstants.CENTERJSAMPLE); - } - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_downsampler.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_downsampler.cs deleted file mode 100644 index 192f88100..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_downsampler.cs +++ /dev/null @@ -1,546 +0,0 @@ -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -/* - * This file contains downsampling routines. - * - * Downsampling input data is counted in "row groups". A row group - * is defined to be max_v_samp_factor pixel rows of each component, - * from which the downsampler produces v_samp_factor sample rows. - * A single row group is processed in each call to the downsampler module. - * - * The downsampler is responsible for edge-expansion of its output data - * to fill an integral number of DCT blocks horizontally. The source buffer - * may be modified if it is helpful for this purpose (the source buffer is - * allocated wide enough to correspond to the desired output width). - * The caller (the prep controller) is responsible for vertical padding. - * - * The downsampler may request "context rows" by setting need_context_rows - * during startup. In this case, the input arrays will contain at least - * one row group's worth of pixels above and below the passed-in data; - * the caller will create dummy rows at image top and bottom by replicating - * the first or last real pixel row. - * - * An excellent reference for image resampling is - * Digital Image Warping, George Wolberg, 1990. - * Pub. by IEEE Computer Society Press, Los Alamitos, CA. ISBN 0-8186-8944-7. - * - * The downsampling algorithm used here is a simple average of the source - * pixels covered by the output pixel. The hi-falutin sampling literature - * refers to this as a "box filter". In general the characteristics of a box - * filter are not very good, but for the specific cases we normally use (1:1 - * and 2:1 ratios) the box is equivalent to a "triangle filter" which is not - * nearly so bad. If you intend to use other sampling ratios, you'd be well - * advised to improve this code. - * - * A simple input-smoothing capability is provided. This is mainly intended - * for cleaning up color-dithered GIF input files (if you find it inadequate, - * we suggest using an external filtering program such as pnmconvol). When - * enabled, each input pixel P is replaced by a weighted sum of itself and its - * eight neighbors. P's weight is 1-8*SF and each neighbor's weight is SF, - * where SF = (smoothing_factor / 1024). - * Currently, smoothing is only supported for 2h2v sampling factors. - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// Downsampling - /// - class jpeg_downsampler - { - private enum downSampleMethod - { - fullsize_smooth_downsampler, - fullsize_downsampler, - h2v1_downsampler, - h2v2_smooth_downsampler, - h2v2_downsampler, - int_downsampler - }; - - /* Downsamplers, one per component */ - private downSampleMethod[] m_downSamplers = new downSampleMethod[JpegConstants.MAX_COMPONENTS]; - - private jpeg_compress_struct m_cinfo; - private bool m_need_context_rows; /* true if need rows above & below */ - - public jpeg_downsampler(jpeg_compress_struct cinfo) - { - m_cinfo = cinfo; - m_need_context_rows = false; - - if (cinfo.m_CCIR601_sampling) - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CCIR601_NOTIMPL); - - /* Verify we can handle the sampling factors, and set up method pointers */ - bool smoothok = true; - for (int ci = 0; ci < cinfo.m_num_components; ci++) - { - jpeg_component_info componentInfo = cinfo.Component_info[ci]; - - if (componentInfo.H_samp_factor == cinfo.m_max_h_samp_factor && - componentInfo.V_samp_factor == cinfo.m_max_v_samp_factor) - { - if (cinfo.m_smoothing_factor != 0) - { - m_downSamplers[ci] = downSampleMethod.fullsize_smooth_downsampler; - m_need_context_rows = true; - } - else - { - m_downSamplers[ci] = downSampleMethod.fullsize_downsampler; - } - } - else if (componentInfo.H_samp_factor * 2 == cinfo.m_max_h_samp_factor && - componentInfo.V_samp_factor == cinfo.m_max_v_samp_factor) - { - smoothok = false; - m_downSamplers[ci] = downSampleMethod.h2v1_downsampler; - } - else if (componentInfo.H_samp_factor * 2 == cinfo.m_max_h_samp_factor && - componentInfo.V_samp_factor * 2 == cinfo.m_max_v_samp_factor) - { - if (cinfo.m_smoothing_factor != 0) - { - m_downSamplers[ci] = downSampleMethod.h2v2_smooth_downsampler; - m_need_context_rows = true; - } - else - { - m_downSamplers[ci] = downSampleMethod.h2v2_downsampler; - } - } - else if ((cinfo.m_max_h_samp_factor % componentInfo.H_samp_factor) == 0 && - (cinfo.m_max_v_samp_factor % componentInfo.V_samp_factor) == 0) - { - smoothok = false; - m_downSamplers[ci] = downSampleMethod.int_downsampler; - } - else - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_FRACT_SAMPLE_NOTIMPL); - } - - if (cinfo.m_smoothing_factor != 0 && !smoothok) - cinfo.TRACEMS(0, J_MESSAGE_CODE.JTRC_SMOOTH_NOTIMPL); - } - - /// - /// Do downsampling for a whole row group (all components). - /// - /// In this version we simply downsample each component independently. - /// - public void downsample(byte[][][] input_buf, int in_row_index, byte[][][] output_buf, int out_row_group_index) - { - for (int ci = 0; ci < m_cinfo.m_num_components; ci++) - { - int outIndex = out_row_group_index * m_cinfo.Component_info[ci].V_samp_factor; - switch (m_downSamplers[ci]) - { - case downSampleMethod.fullsize_smooth_downsampler: - fullsize_smooth_downsample(ci, input_buf[ci], in_row_index, output_buf[ci], outIndex); - break; - - case downSampleMethod.fullsize_downsampler: - fullsize_downsample(ci, input_buf[ci], in_row_index, output_buf[ci], outIndex); - break; - - case downSampleMethod.h2v1_downsampler: - h2v1_downsample(ci, input_buf[ci], in_row_index, output_buf[ci], outIndex); - break; - - case downSampleMethod.h2v2_smooth_downsampler: - h2v2_smooth_downsample(ci, input_buf[ci], in_row_index, output_buf[ci], outIndex); - break; - - case downSampleMethod.h2v2_downsampler: - h2v2_downsample(ci, input_buf[ci], in_row_index, output_buf[ci], outIndex); - break; - - case downSampleMethod.int_downsampler: - int_downsample(ci, input_buf[ci], in_row_index, output_buf[ci], outIndex); - break; - }; - } - } - - public bool NeedContextRows() - { - return m_need_context_rows; - } - - /// - /// Downsample pixel values of a single component. - /// One row group is processed per call. - /// This version handles arbitrary integral sampling ratios, without smoothing. - /// Note that this version is not actually used for customary sampling ratios. - /// - private void int_downsample(int componentIndex, byte[][] input_data, int startInputRow, byte[][] output_data, int startOutRow) - { - /* Expand input data enough to let all the output samples be generated - * by the standard loop. Special-casing padded output would be more - * efficient. - */ - int output_cols = m_cinfo.Component_info[componentIndex].Width_in_blocks * JpegConstants.DCTSIZE; - int h_expand = m_cinfo.m_max_h_samp_factor / m_cinfo.Component_info[componentIndex].H_samp_factor; - expand_right_edge(input_data, startInputRow, m_cinfo.m_max_v_samp_factor, m_cinfo.m_image_width, output_cols * h_expand); - - int v_expand = m_cinfo.m_max_v_samp_factor / m_cinfo.Component_info[componentIndex].V_samp_factor; - int numpix = h_expand * v_expand; - int numpix2 = numpix / 2; - int inrow = 0; - for (int outrow = 0; outrow < m_cinfo.Component_info[componentIndex].V_samp_factor; outrow++) - { - for (int outcol = 0, outcol_h = 0; outcol < output_cols; outcol++, outcol_h += h_expand) - { - int outvalue = 0; - for (int v = 0; v < v_expand; v++) - { - for (int h = 0; h < h_expand; h++) - outvalue += input_data[startInputRow + inrow + v][outcol_h + h]; - } - - output_data[startOutRow + outrow][outcol] = (byte)((outvalue + numpix2) / numpix); - } - - inrow += v_expand; - } - } - - /// - /// Downsample pixel values of a single component. - /// This version handles the special case of a full-size component, - /// without smoothing. - /// - private void fullsize_downsample(int componentIndex, byte[][] input_data, int startInputRow, byte[][] output_data, int startOutRow) - { - /* Copy the data */ - JpegUtils.jcopy_sample_rows(input_data, startInputRow, output_data, startOutRow, m_cinfo.m_max_v_samp_factor, m_cinfo.m_image_width); - - /* Edge-expand */ - expand_right_edge(output_data, startOutRow, m_cinfo.m_max_v_samp_factor, m_cinfo.m_image_width, m_cinfo.Component_info[componentIndex].Width_in_blocks * JpegConstants.DCTSIZE); - } - - /// - /// Downsample pixel values of a single component. - /// This version handles the common case of 2:1 horizontal and 1:1 vertical, - /// without smoothing. - /// - /// A note about the "bias" calculations: when rounding fractional values to - /// integer, we do not want to always round 0.5 up to the next integer. - /// If we did that, we'd introduce a noticeable bias towards larger values. - /// Instead, this code is arranged so that 0.5 will be rounded up or down at - /// alternate pixel locations (a simple ordered dither pattern). - /// - private void h2v1_downsample(int componentIndex, byte[][] input_data, int startInputRow, byte[][] output_data, int startOutRow) - { - /* Expand input data enough to let all the output samples be generated - * by the standard loop. Special-casing padded output would be more - * efficient. - */ - int output_cols = m_cinfo.Component_info[componentIndex].Width_in_blocks * JpegConstants.DCTSIZE; - expand_right_edge(input_data, startInputRow, m_cinfo.m_max_v_samp_factor, m_cinfo.m_image_width, output_cols * 2); - - for (int outrow = 0; outrow < m_cinfo.Component_info[componentIndex].V_samp_factor; outrow++) - { - /* bias = 0,1,0,1,... for successive samples */ - int bias = 0; - int inputColumn = 0; - for (int outcol = 0; outcol < output_cols; outcol++) - { - output_data[startOutRow + outrow][outcol] = (byte)( - ((int)input_data[startInputRow + outrow][inputColumn] + - (int)input_data[startInputRow + outrow][inputColumn + 1] + bias) >> 1); - - bias ^= 1; /* 0=>1, 1=>0 */ - inputColumn += 2; - } - } - } - - /// - /// Downsample pixel values of a single component. - /// This version handles the standard case of 2:1 horizontal and 2:1 vertical, - /// without smoothing. - /// - private void h2v2_downsample(int componentIndex, byte[][] input_data, int startInputRow, byte[][] output_data, int startOutRow) - { - /* Expand input data enough to let all the output samples be generated - * by the standard loop. Special-casing padded output would be more - * efficient. - */ - int output_cols = m_cinfo.Component_info[componentIndex].Width_in_blocks * JpegConstants.DCTSIZE; - expand_right_edge(input_data, startInputRow, m_cinfo.m_max_v_samp_factor, m_cinfo.m_image_width, output_cols * 2); - - int inrow = 0; - for (int outrow = 0; outrow < m_cinfo.Component_info[componentIndex].V_samp_factor; outrow++) - { - /* bias = 1,2,1,2,... for successive samples */ - int bias = 1; - int inputColumn = 0; - for (int outcol = 0; outcol < output_cols; outcol++) - { - output_data[startOutRow + outrow][outcol] = (byte)(( - (int)input_data[startInputRow + inrow][inputColumn] + - (int)input_data[startInputRow + inrow][inputColumn + 1] + - (int)input_data[startInputRow + inrow + 1][inputColumn] + - (int)input_data[startInputRow + inrow + 1][inputColumn + 1] + bias) >> 2); - - bias ^= 3; /* 1=>2, 2=>1 */ - inputColumn += 2; - } - - inrow += 2; - } - } - - /// - /// Downsample pixel values of a single component. - /// This version handles the standard case of 2:1 horizontal and 2:1 vertical, - /// with smoothing. One row of context is required. - /// - private void h2v2_smooth_downsample(int componentIndex, byte[][] input_data, int startInputRow, byte[][] output_data, int startOutRow) - { - /* Expand input data enough to let all the output samples be generated - * by the standard loop. Special-casing padded output would be more - * efficient. - */ - int output_cols = m_cinfo.Component_info[componentIndex].Width_in_blocks * JpegConstants.DCTSIZE; - expand_right_edge(input_data, startInputRow - 1, m_cinfo.m_max_v_samp_factor + 2, m_cinfo.m_image_width, output_cols * 2); - - /* We don't bother to form the individual "smoothed" input pixel values; - * we can directly compute the output which is the average of the four - * smoothed values. Each of the four member pixels contributes a fraction - * (1-8*SF) to its own smoothed image and a fraction SF to each of the three - * other smoothed pixels, therefore a total fraction (1-5*SF)/4 to the final - * output. The four corner-adjacent neighbor pixels contribute a fraction - * SF to just one smoothed pixel, or SF/4 to the final output; while the - * eight edge-adjacent neighbors contribute SF to each of two smoothed - * pixels, or SF/2 overall. In order to use integer arithmetic, these - * factors are scaled by 2^16 = 65536. - * Also recall that SF = smoothing_factor / 1024. - */ - - int memberscale = 16384 - m_cinfo.m_smoothing_factor * 80; /* scaled (1-5*SF)/4 */ - int neighscale = m_cinfo.m_smoothing_factor * 16; /* scaled SF/4 */ - - for (int inrow = 0, outrow = 0; outrow < m_cinfo.Component_info[componentIndex].V_samp_factor; outrow++) - { - int outIndex = 0; - int inIndex0 = 0; - int inIndex1 = 0; - int aboveIndex = 0; - int belowIndex = 0; - - /* Special case for first column: pretend column -1 is same as column 0 */ - int membersum = input_data[startInputRow + inrow][inIndex0] + - input_data[startInputRow + inrow][inIndex0 + 1] + - input_data[startInputRow + inrow + 1][inIndex1] + - input_data[startInputRow + inrow + 1][inIndex1 + 1]; - - int neighsum = input_data[startInputRow + inrow - 1][aboveIndex] + - input_data[startInputRow + inrow - 1][aboveIndex + 1] + - input_data[startInputRow + inrow + 2][belowIndex] + - input_data[startInputRow + inrow + 2][belowIndex + 1] + - input_data[startInputRow + inrow][inIndex0] + - input_data[startInputRow + inrow][inIndex0 + 2] + - input_data[startInputRow + inrow + 1][inIndex1] + - input_data[startInputRow + inrow + 1][inIndex1 + 2]; - - neighsum += neighsum; - neighsum += input_data[startInputRow + inrow - 1][aboveIndex] + - input_data[startInputRow + inrow - 1][aboveIndex + 2] + - input_data[startInputRow + inrow + 2][belowIndex] + - input_data[startInputRow + inrow + 2][belowIndex + 2]; - - membersum = membersum * memberscale + neighsum * neighscale; - output_data[startOutRow + outrow][outIndex] = (byte)((membersum + 32768) >> 16); - outIndex++; - - inIndex0 += 2; - inIndex1 += 2; - aboveIndex += 2; - belowIndex += 2; - - for (int colctr = output_cols - 2; colctr > 0; colctr--) - { - /* sum of pixels directly mapped to this output element */ - membersum = input_data[startInputRow + inrow][inIndex0] + - input_data[startInputRow + inrow][inIndex0 + 1] + - input_data[startInputRow + inrow + 1][inIndex1] + - input_data[startInputRow + inrow + 1][inIndex1 + 1]; - - /* sum of edge-neighbor pixels */ - neighsum = input_data[startInputRow + inrow - 1][aboveIndex] + - input_data[startInputRow + inrow - 1][aboveIndex + 1] + - input_data[startInputRow + inrow + 2][belowIndex] + - input_data[startInputRow + inrow + 2][belowIndex + 1] + - input_data[startInputRow + inrow][inIndex0 - 1] + - input_data[startInputRow + inrow][inIndex0 + 2] + - input_data[startInputRow + inrow + 1][inIndex1 - 1] + - input_data[startInputRow + inrow + 1][inIndex1 + 2]; - - /* The edge-neighbors count twice as much as corner-neighbors */ - neighsum += neighsum; - - /* Add in the corner-neighbors */ - neighsum += input_data[startInputRow + inrow - 1][aboveIndex - 1] + - input_data[startInputRow + inrow - 1][aboveIndex + 2] + - input_data[startInputRow + inrow + 2][belowIndex - 1] + - input_data[startInputRow + inrow + 2][belowIndex + 2]; - - /* form final output scaled up by 2^16 */ - membersum = membersum * memberscale + neighsum * neighscale; - - /* round, descale and output it */ - output_data[startOutRow + outrow][outIndex] = (byte)((membersum + 32768) >> 16); - outIndex++; - - inIndex0 += 2; - inIndex1 += 2; - aboveIndex += 2; - belowIndex += 2; - } - - /* Special case for last column */ - membersum = input_data[startInputRow + inrow][inIndex0] + - input_data[startInputRow + inrow][inIndex0 + 1] + - input_data[startInputRow + inrow + 1][inIndex1] + - input_data[startInputRow + inrow + 1][inIndex1 + 1]; - - neighsum = input_data[startInputRow + inrow - 1][aboveIndex] + - input_data[startInputRow + inrow - 1][aboveIndex + 1] + - input_data[startInputRow + inrow + 2][belowIndex] + - input_data[startInputRow + inrow + 2][belowIndex + 1] + - input_data[startInputRow + inrow][inIndex0 - 1] + - input_data[startInputRow + inrow][inIndex0 + 1] + - input_data[startInputRow + inrow + 1][inIndex1 - 1] + - input_data[startInputRow + inrow + 1][inIndex1 + 1]; - - neighsum += neighsum; - neighsum += input_data[startInputRow + inrow - 1][aboveIndex - 1] + - input_data[startInputRow + inrow - 1][aboveIndex + 1] + - input_data[startInputRow + inrow + 2][belowIndex - 1] + - input_data[startInputRow + inrow + 2][belowIndex + 1]; - - membersum = membersum * memberscale + neighsum * neighscale; - output_data[startOutRow + outrow][outIndex] = (byte)((membersum + 32768) >> 16); - - inrow += 2; - } - } - - /// - /// Downsample pixel values of a single component. - /// This version handles the special case of a full-size component, - /// with smoothing. One row of context is required. - /// - private void fullsize_smooth_downsample(int componentIndex, byte[][] input_data, int startInputRow, byte[][] output_data, int startOutRow) - { - /* Expand input data enough to let all the output samples be generated - * by the standard loop. Special-casing padded output would be more - * efficient. - */ - int output_cols = m_cinfo.Component_info[componentIndex].Width_in_blocks * JpegConstants.DCTSIZE; - expand_right_edge(input_data, startInputRow - 1, m_cinfo.m_max_v_samp_factor + 2, m_cinfo.m_image_width, output_cols); - - /* Each of the eight neighbor pixels contributes a fraction SF to the - * smoothed pixel, while the main pixel contributes (1-8*SF). In order - * to use integer arithmetic, these factors are multiplied by 2^16 = 65536. - * Also recall that SF = smoothing_factor / 1024. - */ - - int memberscale = 65536 - m_cinfo.m_smoothing_factor * 512; /* scaled 1-8*SF */ - int neighscale = m_cinfo.m_smoothing_factor * 64; /* scaled SF */ - - for (int outrow = 0; outrow < m_cinfo.Component_info[componentIndex].V_samp_factor; outrow++) - { - int outIndex = 0; - int inIndex = 0; - int aboveIndex = 0; - int belowIndex = 0; - - /* Special case for first column */ - int colsum = input_data[startInputRow + outrow - 1][aboveIndex] + - input_data[startInputRow + outrow + 1][belowIndex] + - input_data[startInputRow + outrow][inIndex]; - - aboveIndex++; - belowIndex++; - - int membersum = input_data[startInputRow + outrow][inIndex]; - inIndex++; - - int nextcolsum = input_data[startInputRow + outrow - 1][aboveIndex] + - input_data[startInputRow + outrow + 1][belowIndex] + - input_data[startInputRow + outrow][inIndex]; - - int neighsum = colsum + (colsum - membersum) + nextcolsum; - - membersum = membersum * memberscale + neighsum * neighscale; - output_data[startOutRow + outrow][outIndex] = (byte)((membersum + 32768) >> 16); - outIndex++; - - int lastcolsum = colsum; - colsum = nextcolsum; - - for (int colctr = output_cols - 2; colctr > 0; colctr--) - { - membersum = input_data[startInputRow + outrow][inIndex]; - - inIndex++; - aboveIndex++; - belowIndex++; - - nextcolsum = input_data[startInputRow + outrow - 1][aboveIndex] + - input_data[startInputRow + outrow + 1][belowIndex] + - input_data[startInputRow + outrow][inIndex]; - - neighsum = lastcolsum + (colsum - membersum) + nextcolsum; - membersum = membersum * memberscale + neighsum * neighscale; - - output_data[startOutRow + outrow][outIndex] = (byte)((membersum + 32768) >> 16); - outIndex++; - - lastcolsum = colsum; - colsum = nextcolsum; - } - - /* Special case for last column */ - membersum = input_data[startInputRow + outrow][inIndex]; - neighsum = lastcolsum + (colsum - membersum) + colsum; - membersum = membersum * memberscale + neighsum * neighscale; - output_data[startOutRow + outrow][outIndex] = (byte)((membersum + 32768) >> 16); - } - } - - /// - /// Expand a component horizontally from width input_cols to width output_cols, - /// by duplicating the rightmost samples. - /// - private static void expand_right_edge(byte[][] image_data, int startInputRow, int num_rows, int input_cols, int output_cols) - { - int numcols = output_cols - input_cols; - if (numcols > 0) - { - for (int row = startInputRow; row < (startInputRow + num_rows); row++) - { - /* don't need GETJSAMPLE() here */ - byte pixval = image_data[row][input_cols - 1]; - for (int count = 0; count < numcols; count++) - image_data[row][input_cols + count] = pixval; - } - } - } - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_entropy_decoder.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_entropy_decoder.cs deleted file mode 100644 index 930951cbd..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_entropy_decoder.cs +++ /dev/null @@ -1,473 +0,0 @@ -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// Entropy decoding - /// - abstract class jpeg_entropy_decoder - { - // Figure F.12: extend sign bit. - // entry n is 2**(n-1) - private static int[] extend_test = - { - 0, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, - 0x0040, 0x0080, 0x0100, 0x0200, 0x0400, 0x0800, - 0x1000, 0x2000, 0x4000 - }; - - // entry n is (-1 << n) + 1 - private static int[] extend_offset = - { - 0, (-1 << 1) + 1, (-1 << 2) + 1, - (-1 << 3) + 1, (-1 << 4) + 1, (-1 << 5) + 1, - (-1 << 6) + 1, (-1 << 7) + 1, (-1 << 8) + 1, - (-1 << 9) + 1, (-1 << 10) + 1, - (-1 << 11) + 1, (-1 << 12) + 1, - (-1 << 13) + 1, (-1 << 14) + 1, - (-1 << 15) + 1 - }; - - /* Fetching the next N bits from the input stream is a time-critical operation - * for the Huffman decoders. We implement it with a combination of inline - * macros and out-of-line subroutines. Note that N (the number of bits - * demanded at one time) never exceeds 15 for JPEG use. - * - * We read source bytes into get_buffer and dole out bits as needed. - * If get_buffer already contains enough bits, they are fetched in-line - * by the macros CHECK_BIT_BUFFER and GET_BITS. When there aren't enough - * bits, jpeg_fill_bit_buffer is called; it will attempt to fill get_buffer - * as full as possible (not just to the number of bits needed; this - * prefetching reduces the overhead cost of calling jpeg_fill_bit_buffer). - * Note that jpeg_fill_bit_buffer may return false to indicate suspension. - * On true return, jpeg_fill_bit_buffer guarantees that get_buffer contains - * at least the requested number of bits --- dummy zeroes are inserted if - * necessary. - */ - protected const int BIT_BUF_SIZE = 32; /* size of buffer in bits */ - - /* - * Out-of-line code for bit fetching (shared with jdphuff.c). - * See jdhuff.h for info about usage. - * Note: current values of get_buffer and bits_left are passed as parameters, - * but are returned in the corresponding fields of the state struct. - * - * On most machines MIN_GET_BITS should be 25 to allow the full 32-bit width - * of get_buffer to be used. (On machines with wider words, an even larger - * buffer could be used.) However, on some machines 32-bit shifts are - * quite slow and take time proportional to the number of places shifted. - * (This is true with most PC compilers, for instance.) In this case it may - * be a win to set MIN_GET_BITS to the minimum value of 15. This reduces the - * average shift distance at the cost of more calls to jpeg_fill_bit_buffer. - */ - - protected const int MIN_GET_BITS = BIT_BUF_SIZE - 7; - - protected jpeg_decompress_struct m_cinfo; - - /* This is here to share code between baseline and progressive decoders; */ - /* other modules probably should not use it */ - protected bool m_insufficient_data; /* set true after emitting warning */ - - public abstract void start_pass(); - public abstract bool decode_mcu(JBLOCK[] MCU_data); - - protected static int HUFF_EXTEND(int x, int s) - { - return ((x) < extend_test[s] ? (x) + extend_offset[s] : (x)); - } - - protected void BITREAD_LOAD_STATE(bitread_perm_state bitstate, out int get_buffer, out int bits_left, ref bitread_working_state br_state) - { - br_state.cinfo = m_cinfo; - get_buffer = bitstate.get_buffer; - bits_left = bitstate.bits_left; - } - - protected static void BITREAD_SAVE_STATE(ref bitread_perm_state bitstate, int get_buffer, int bits_left) - { - bitstate.get_buffer = get_buffer; - bitstate.bits_left = bits_left; - } - - /// - /// Expand a Huffman table definition into the derived format - /// This routine also performs some validation checks on the table. - /// - protected void jpeg_make_d_derived_tbl(bool isDC, int tblno, ref d_derived_tbl dtbl) - { - /* Note that huffsize[] and huffcode[] are filled in code-length order, - * paralleling the order of the symbols themselves in htbl.huffval[]. - */ - - /* Find the input Huffman table */ - if (tblno < 0 || tblno >= JpegConstants.NUM_HUFF_TBLS) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NO_HUFF_TABLE, tblno); - - JHUFF_TBL htbl = isDC ? m_cinfo.m_dc_huff_tbl_ptrs[tblno] : m_cinfo.m_ac_huff_tbl_ptrs[tblno]; - if (htbl == null) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NO_HUFF_TABLE, tblno); - - /* Allocate a workspace if we haven't already done so. */ - if (dtbl == null) - dtbl = new d_derived_tbl(); - - dtbl.pub = htbl; /* fill in back link */ - - /* Figure C.1: make table of Huffman code length for each symbol */ - - int p = 0; - char[] huffsize = new char[257]; - for (int l = 1; l <= 16; l++) - { - int i = htbl.Bits[l]; - if (i < 0 || p + i> 256) /* protect against table overrun */ - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_HUFF_TABLE); - - while ((i--) != 0) - huffsize[p++] = (char) l; - } - huffsize[p] = (char)0; - int numsymbols = p; - - /* Figure C.2: generate the codes themselves */ - /* We also validate that the counts represent a legal Huffman code tree. */ - - int code = 0; - int si = huffsize[0]; - int[] huffcode = new int[257]; - p = 0; - while (huffsize[p] != 0) - { - while (((int)huffsize[p]) == si) - { - huffcode[p++] = code; - code++; - } - - /* code is now 1 more than the last code used for codelength si; but - * it must still fit in si bits, since no code is allowed to be all ones. - */ - if (code >= (1 << si)) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_HUFF_TABLE); - code <<= 1; - si++; - } - - /* Figure F.15: generate decoding tables for bit-sequential decoding */ - - p = 0; - for (int l = 1; l <= 16; l++) - { - if (htbl.Bits[l] != 0) - { - /* valoffset[l] = huffval[] index of 1st symbol of code length l, - * minus the minimum code of length l - */ - dtbl.valoffset[l] = p - huffcode[p]; - p += htbl.Bits[l]; - dtbl.maxcode[l] = huffcode[p - 1]; /* maximum code of length l */ - } - else - { - /* -1 if no codes of this length */ - dtbl.maxcode[l] = -1; - } - } - dtbl.maxcode[17] = 0xFFFFF; /* ensures jpeg_huff_decode terminates */ - - /* Compute lookahead tables to speed up decoding. - * First we set all the table entries to 0, indicating "too long"; - * then we iterate through the Huffman codes that are short enough and - * fill in all the entries that correspond to bit sequences starting - * with that code. - */ - - Array.Clear(dtbl.look_nbits, 0, dtbl.look_nbits.Length); - p = 0; - for (int l = 1; l <= JpegConstants.HUFF_LOOKAHEAD; l++) - { - for (int i = 1; i <= htbl.Bits[l]; i++, p++) - { - /* l = current code's length, p = its index in huffcode[] & huffval[]. */ - /* Generate left-justified code followed by all possible bit sequences */ - int lookbits = huffcode[p] << (JpegConstants.HUFF_LOOKAHEAD - l); - for (int ctr = 1 << (JpegConstants.HUFF_LOOKAHEAD - l); ctr > 0; ctr--) - { - dtbl.look_nbits[lookbits] = l; - dtbl.look_sym[lookbits] = htbl.Huffval[p]; - lookbits++; - } - } - } - - /* Validate symbols as being reasonable. - * For AC tables, we make no check, but accept all byte values 0..255. - * For DC tables, we require the symbols to be in range 0..15. - * (Tighter bounds could be applied depending on the data depth and mode, - * but this is sufficient to ensure safe decoding.) - */ - if (isDC) - { - for (int i = 0; i < numsymbols; i++) - { - int sym = htbl.Huffval[i]; - if (sym < 0 || sym> 15) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_HUFF_TABLE); - } - } - } - - /* - * These methods provide the in-line portion of bit fetching. - * Use CHECK_BIT_BUFFER to ensure there are N bits in get_buffer - * before using GET_BITS, PEEK_BITS, or DROP_BITS. - * The variables get_buffer and bits_left are assumed to be locals, - * but the state struct might not be (jpeg_huff_decode needs this). - * CHECK_BIT_BUFFER(state,n,action); - * Ensure there are N bits in get_buffer; if suspend, take action. - * val = GET_BITS(n); - * Fetch next N bits. - * val = PEEK_BITS(n); - * Fetch next N bits without removing them from the buffer. - * DROP_BITS(n); - * Discard next N bits. - * The value N should be a simple variable, not an expression, because it - * is evaluated multiple times. - */ - - protected static bool CHECK_BIT_BUFFER(ref bitread_working_state state, int nbits, ref int get_buffer, ref int bits_left) - { - if (bits_left < nbits) - { - if (!jpeg_fill_bit_buffer(ref state, get_buffer, bits_left, nbits)) - return false; - - get_buffer = state.get_buffer; - bits_left = state.bits_left; - } - - return true; - } - - protected static int GET_BITS(int nbits, int get_buffer, ref int bits_left) - { - return (((int)(get_buffer >> (bits_left -= nbits))) & ((1 << nbits) - 1)); - } - - protected static int PEEK_BITS(int nbits, int get_buffer, int bits_left) - { - return (((int)(get_buffer >> (bits_left - nbits))) & ((1 << nbits) - 1)); - } - - protected static void DROP_BITS(int nbits, ref int bits_left) - { - bits_left -= nbits; - } - - /* Load up the bit buffer to a depth of at least nbits */ - protected static bool jpeg_fill_bit_buffer(ref bitread_working_state state, int get_buffer, int bits_left, int nbits) - { - /* Attempt to load at least MIN_GET_BITS bits into get_buffer. */ - /* (It is assumed that no request will be for more than that many bits.) */ - /* We fail to do so only if we hit a marker or are forced to suspend. */ - - bool noMoreBytes = false; - - if (state.cinfo.m_unread_marker == 0) - { - /* cannot advance past a marker */ - while (bits_left < MIN_GET_BITS) - { - int c; - state.cinfo.m_src.GetByte(out c); - - /* If it's 0xFF, check and discard stuffed zero byte */ - if (c == 0xFF) - { - /* Loop here to discard any padding FF's on terminating marker, - * so that we can save a valid unread_marker value. NOTE: we will - * accept multiple FF's followed by a 0 as meaning a single FF data - * byte. This data pattern is not valid according to the standard. - */ - do - { - state.cinfo.m_src.GetByte(out c); - } - while (c == 0xFF); - - if (c == 0) - { - /* Found FF/00, which represents an FF data byte */ - c = 0xFF; - } - else - { - /* Oops, it's actually a marker indicating end of compressed data. - * Save the marker code for later use. - * Fine point: it might appear that we should save the marker into - * bitread working state, not straight into permanent state. But - * once we have hit a marker, we cannot need to suspend within the - * current MCU, because we will read no more bytes from the data - * source. So it is OK to update permanent state right away. - */ - state.cinfo.m_unread_marker = c; - /* See if we need to insert some fake zero bits. */ - noMoreBytes = true; - break; - } - } - - /* OK, load c into get_buffer */ - get_buffer = (get_buffer << 8) | c; - bits_left += 8; - } /* end while */ - } - else - noMoreBytes = true; - - if (noMoreBytes) - { - /* We get here if we've read the marker that terminates the compressed - * data segment. There should be enough bits in the buffer register - * to satisfy the request; if so, no problem. - */ - if (nbits > bits_left) - { - /* Uh-oh. Report corrupted data to user and stuff zeroes into - * the data stream, so that we can produce some kind of image. - * We use a nonvolatile flag to ensure that only one warning message - * appears per data segment. - */ - if (!state.cinfo.m_entropy.m_insufficient_data) - { - state.cinfo.WARNMS(J_MESSAGE_CODE.JWRN_HIT_MARKER); - state.cinfo.m_entropy.m_insufficient_data = true; - } - - /* Fill the buffer with zero bits */ - get_buffer <<= MIN_GET_BITS - bits_left; - bits_left = MIN_GET_BITS; - } - } - - /* Unload the local registers */ - state.get_buffer = get_buffer; - state.bits_left = bits_left; - - return true; - } - - /* - * Code for extracting next Huffman-coded symbol from input bit stream. - * Again, this is time-critical and we make the main paths be macros. - * - * We use a lookahead table to process codes of up to HUFF_LOOKAHEAD bits - * without looping. Usually, more than 95% of the Huffman codes will be 8 - * or fewer bits long. The few overlength codes are handled with a loop, - * which need not be inline code. - * - * Notes about the HUFF_DECODE macro: - * 1. Near the end of the data segment, we may fail to get enough bits - * for a lookahead. In that case, we do it the hard way. - * 2. If the lookahead table contains no entry, the next code must be - * more than HUFF_LOOKAHEAD bits long. - * 3. jpeg_huff_decode returns -1 if forced to suspend. - */ - protected static bool HUFF_DECODE(out int result, ref bitread_working_state state, d_derived_tbl htbl, ref int get_buffer, ref int bits_left) - { - int nb = 0; - bool doSlow = false; - - if (bits_left < JpegConstants.HUFF_LOOKAHEAD) - { - if (!jpeg_fill_bit_buffer(ref state, get_buffer, bits_left, 0)) - { - result = -1; - return false; - } - - get_buffer = state.get_buffer; - bits_left = state.bits_left; - if (bits_left < JpegConstants.HUFF_LOOKAHEAD) - { - nb = 1; - doSlow = true; - } - } - - if (!doSlow) - { - int look = PEEK_BITS(JpegConstants.HUFF_LOOKAHEAD, get_buffer, bits_left); - if ((nb = htbl.look_nbits[look]) != 0) - { - DROP_BITS(nb, ref bits_left); - result = htbl.look_sym[look]; - return true; - } - - nb = JpegConstants.HUFF_LOOKAHEAD + 1; - } - - result = jpeg_huff_decode(ref state, get_buffer, bits_left, htbl, nb); - if (result < 0) - return false; - - get_buffer = state.get_buffer; - bits_left = state.bits_left; - - return true; - } - - /* Out-of-line case for Huffman code fetching */ - protected static int jpeg_huff_decode(ref bitread_working_state state, int get_buffer, int bits_left, d_derived_tbl htbl, int min_bits) - { - /* HUFF_DECODE has determined that the code is at least min_bits */ - /* bits long, so fetch that many bits in one swoop. */ - int l = min_bits; - if (!CHECK_BIT_BUFFER(ref state, l, ref get_buffer, ref bits_left)) - return -1; - - int code = GET_BITS(l, get_buffer, ref bits_left); - - /* Collect the rest of the Huffman code one bit at a time. */ - /* This is per Figure F.16 in the JPEG spec. */ - - while (code > htbl.maxcode[l]) - { - code <<= 1; - if (!CHECK_BIT_BUFFER(ref state, 1, ref get_buffer, ref bits_left)) - return -1; - - code |= GET_BITS(1, get_buffer, ref bits_left); - l++; - } - - /* Unload the local registers */ - state.get_buffer = get_buffer; - state.bits_left = bits_left; - - /* With garbage input we may reach the sentinel value l = 17. */ - - if (l > 16) - { - state.cinfo.WARNMS(J_MESSAGE_CODE.JWRN_HUFF_BAD_CODE); - /* fake a zero as the safest result */ - return 0; - } - - return htbl.pub.Huffval[code + htbl.valoffset[l]]; - } - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_entropy_encoder.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_entropy_encoder.cs deleted file mode 100644 index 5fc2ffc67..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_entropy_encoder.cs +++ /dev/null @@ -1,303 +0,0 @@ -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// Entropy encoding - /// - abstract class jpeg_entropy_encoder - { - /* Derived data constructed for each Huffman table */ - protected class c_derived_tbl - { - public int[] ehufco = new int[256]; /* code for each symbol */ - public char[] ehufsi = new char[256]; /* length of code for each symbol */ - /* If no code has been allocated for a symbol S, ehufsi[S] contains 0 */ - } - - /* The legal range of a DCT coefficient is - * -1024 .. +1023 for 8-bit data; - * -16384 .. +16383 for 12-bit data. - * Hence the magnitude should always fit in 10 or 14 bits respectively. - */ - protected static int MAX_HUFFMAN_COEF_BITS = 10; - private static int MAX_CLEN = 32; /* assumed maximum initial code length */ - - protected jpeg_compress_struct m_cinfo; - - public abstract void start_pass(bool gather_statistics); - public abstract bool encode_mcu(JBLOCK[][] MCU_data); - public abstract void finish_pass(); - - /// - /// Expand a Huffman table definition into the derived format - /// Compute the derived values for a Huffman table. - /// This routine also performs some validation checks on the table. - /// - protected void jpeg_make_c_derived_tbl(bool isDC, int tblno, ref c_derived_tbl dtbl) - { - /* Note that huffsize[] and huffcode[] are filled in code-length order, - * paralleling the order of the symbols themselves in htbl.huffval[]. - */ - - /* Find the input Huffman table */ - if (tblno < 0 || tblno >= JpegConstants.NUM_HUFF_TBLS) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NO_HUFF_TABLE, tblno); - - JHUFF_TBL htbl = isDC ? m_cinfo.m_dc_huff_tbl_ptrs[tblno] : m_cinfo.m_ac_huff_tbl_ptrs[tblno]; - if (htbl == null) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NO_HUFF_TABLE, tblno); - - /* Allocate a workspace if we haven't already done so. */ - if (dtbl == null) - dtbl = new c_derived_tbl(); - - /* Figure C.1: make table of Huffman code length for each symbol */ - - int p = 0; - char[] huffsize = new char[257]; - for (int l = 1; l <= 16; l++) - { - int i = htbl.Bits[l]; - if (i < 0 || p + i> 256) /* protect against table overrun */ - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_HUFF_TABLE); - - while ((i--) != 0) - huffsize[p++] = (char) l; - } - huffsize[p] = (char)0; - int lastp = p; - - /* Figure C.2: generate the codes themselves */ - /* We also validate that the counts represent a legal Huffman code tree. */ - - int code = 0; - int si = huffsize[0]; - p = 0; - int[] huffcode = new int[257]; - while (huffsize[p] != 0) - { - while (((int)huffsize[p]) == si) - { - huffcode[p++] = code; - code++; - } - /* code is now 1 more than the last code used for codelength si; but - * it must still fit in si bits, since no code is allowed to be all ones. - */ - if (code >= (1 << si)) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_HUFF_TABLE); - code <<= 1; - si++; - } - - /* Figure C.3: generate encoding tables */ - /* These are code and size indexed by symbol value */ - - /* Set all codeless symbols to have code length 0; - * this lets us detect duplicate VAL entries here, and later - * allows emit_bits to detect any attempt to emit such symbols. - */ - Array.Clear(dtbl.ehufsi, 0, dtbl.ehufsi.Length); - - /* This is also a convenient place to check for out-of-range - * and duplicated VAL entries. We allow 0..255 for AC symbols - * but only 0..15 for DC. (We could constrain them further - * based on data depth and mode, but this seems enough.) - */ - int maxsymbol = isDC ? 15 : 255; - - for (p = 0; p < lastp; p++) - { - int i = htbl.Huffval[p]; - if (i < 0 || i> maxsymbol || dtbl.ehufsi[i] != 0) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_HUFF_TABLE); - - dtbl.ehufco[i] = huffcode[p]; - dtbl.ehufsi[i] = huffsize[p]; - } - } - - /// - /// Generate the best Huffman code table for the given counts, fill htbl. - /// - /// The JPEG standard requires that no symbol be assigned a codeword of all - /// one bits (so that padding bits added at the end of a compressed segment - /// can't look like a valid code). Because of the canonical ordering of - /// codewords, this just means that there must be an unused slot in the - /// longest codeword length category. Section K.2 of the JPEG spec suggests - /// reserving such a slot by pretending that symbol 256 is a valid symbol - /// with count 1. In theory that's not optimal; giving it count zero but - /// including it in the symbol set anyway should give a better Huffman code. - /// But the theoretically better code actually seems to come out worse in - /// practice, because it produces more all-ones bytes (which incur stuffed - /// zero bytes in the final file). In any case the difference is tiny. - /// - /// The JPEG standard requires Huffman codes to be no more than 16 bits long. - /// If some symbols have a very small but nonzero probability, the Huffman tree - /// must be adjusted to meet the code length restriction. We currently use - /// the adjustment method suggested in JPEG section K.2. This method is *not* - /// optimal; it may not choose the best possible limited-length code. But - /// typically only very-low-frequency symbols will be given less-than-optimal - /// lengths, so the code is almost optimal. Experimental comparisons against - /// an optimal limited-length-code algorithm indicate that the difference is - /// microscopic --- usually less than a hundredth of a percent of total size. - /// So the extra complexity of an optimal algorithm doesn't seem worthwhile. - /// - protected void jpeg_gen_optimal_table(JHUFF_TBL htbl, long[] freq) - { - byte[] bits = new byte[MAX_CLEN + 1]; /* bits[k] = # of symbols with code length k */ - int[] codesize = new int[257]; /* codesize[k] = code length of symbol k */ - int[] others = new int[257]; /* next symbol in current branch of tree */ - int c1, c2; - int p, i, j; - long v; - - /* This algorithm is explained in section K.2 of the JPEG standard */ - for (i = 0; i < 257; i++) - others[i] = -1; /* init links to empty */ - - freq[256] = 1; /* make sure 256 has a nonzero count */ - /* Including the pseudo-symbol 256 in the Huffman procedure guarantees - * that no real symbol is given code-value of all ones, because 256 - * will be placed last in the largest codeword category. - */ - - /* Huffman's basic algorithm to assign optimal code lengths to symbols */ - - for (; ;) - { - /* Find the smallest nonzero frequency, set c1 = its symbol */ - /* In case of ties, take the larger symbol number */ - c1 = -1; - v = 1000000000L; - for (i = 0; i <= 256; i++) - { - if (freq[i] != 0 && freq[i] <= v) - { - v = freq[i]; - c1 = i; - } - } - - /* Find the next smallest nonzero frequency, set c2 = its symbol */ - /* In case of ties, take the larger symbol number */ - c2 = -1; - v = 1000000000L; - for (i = 0; i <= 256; i++) - { - if (freq[i] != 0 && freq[i] <= v && i != c1) - { - v = freq[i]; - c2 = i; - } - } - - /* Done if we've merged everything into one frequency */ - if (c2 < 0) - break; - - /* Else merge the two counts/trees */ - freq[c1] += freq[c2]; - freq[c2] = 0; - - /* Increment the codesize of everything in c1's tree branch */ - codesize[c1]++; - while (others[c1] >= 0) - { - c1 = others[c1]; - codesize[c1]++; - } - - others[c1] = c2; /* chain c2 onto c1's tree branch */ - - /* Increment the codesize of everything in c2's tree branch */ - codesize[c2]++; - while (others[c2] >= 0) - { - c2 = others[c2]; - codesize[c2]++; - } - } - - /* Now count the number of symbols of each code length */ - for (i = 0; i <= 256; i++) - { - if (codesize[i] != 0) - { - /* The JPEG standard seems to think that this can't happen, */ - /* but I'm paranoid... */ - if (codesize[i] > MAX_CLEN) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_HUFF_CLEN_OVERFLOW); - - bits[codesize[i]]++; - } - } - - /* JPEG doesn't allow symbols with code lengths over 16 bits, so if the pure - * Huffman procedure assigned any such lengths, we must adjust the coding. - * Here is what the JPEG spec says about how this next bit works: - * Since symbols are paired for the longest Huffman code, the symbols are - * removed from this length category two at a time. The prefix for the pair - * (which is one bit shorter) is allocated to one of the pair; then, - * skipping the BITS entry for that prefix length, a code word from the next - * shortest nonzero BITS entry is converted into a prefix for two code words - * one bit longer. - */ - - for (i = MAX_CLEN; i > 16; i--) - { - while (bits[i] > 0) - { - j = i - 2; /* find length of new prefix to be used */ - while (bits[j] == 0) - j--; - - bits[i] -= 2; /* remove two symbols */ - bits[i - 1]++; /* one goes in this length */ - bits[j + 1] += 2; /* two new symbols in this length */ - bits[j]--; /* symbol of this length is now a prefix */ - } - } - - /* Remove the count for the pseudo-symbol 256 from the largest codelength */ - while (bits[i] == 0) /* find largest codelength still in use */ - i--; - bits[i]--; - - /* Return final symbol counts (only for lengths 0..16) */ - Buffer.BlockCopy(bits, 0, htbl.Bits, 0, htbl.Bits.Length); - - /* Return a list of the symbols sorted by code length */ - /* It's not real clear to me why we don't need to consider the codelength - * changes made above, but the JPEG spec seems to think this works. - */ - p = 0; - for (i = 1; i <= MAX_CLEN; i++) - { - for (j = 0; j <= 255; j++) - { - if (codesize[j] == i) - { - htbl.Huffval[p] = (byte) j; - p++; - } - } - } - - /* Set sent_table false so updated table will be written to JPEG file. */ - htbl.Sent_table = false; - } - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_forward_dct.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_forward_dct.cs deleted file mode 100644 index cabe25631..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_forward_dct.cs +++ /dev/null @@ -1,813 +0,0 @@ -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -/* - * This file contains the forward-DCT management logic. - * This code selects a particular DCT implementation to be used, - * and it performs related housekeeping chores including coefficient - * quantization. - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// Forward DCT (also controls coefficient quantization) - /// - /// A forward DCT routine is given a pointer to a work area of type DCTELEM[]; - /// the DCT is to be performed in-place in that buffer. Type DCTELEM is int - /// for 8-bit samples, int for 12-bit samples. (NOTE: Floating-point DCT - /// implementations use an array of type float, instead.) - /// The DCT inputs are expected to be signed (range +-CENTERJSAMPLE). - /// The DCT outputs are returned scaled up by a factor of 8; they therefore - /// have a range of +-8K for 8-bit data, +-128K for 12-bit data. This - /// convention improves accuracy in integer implementations and saves some - /// work in floating-point ones. - /// - /// Each IDCT routine has its own ideas about the best dct_table element type. - /// - class jpeg_forward_dct - { - private const int FAST_INTEGER_CONST_BITS = 8; - - /* We use the following pre-calculated constants. - * If you change FAST_INTEGER_CONST_BITS you may want to add appropriate values. - * - * Convert a positive real constant to an integer scaled by CONST_SCALE. - * static int FAST_INTEGER_FIX(double x) - *{ - * return ((int) ((x) * (((int) 1) << FAST_INTEGER_CONST_BITS) + 0.5)); - *} - */ - private const int FAST_INTEGER_FIX_0_382683433 = 98; /* FIX(0.382683433) */ - private const int FAST_INTEGER_FIX_0_541196100 = 139; /* FIX(0.541196100) */ - private const int FAST_INTEGER_FIX_0_707106781 = 181; /* FIX(0.707106781) */ - private const int FAST_INTEGER_FIX_1_306562965 = 334; /* FIX(1.306562965) */ - - private const int SLOW_INTEGER_CONST_BITS = 13; - private const int SLOW_INTEGER_PASS1_BITS = 2; - - /* We use the following pre-calculated constants. - * If you change SLOW_INTEGER_CONST_BITS you may want to add appropriate values. - * - * Convert a positive real constant to an integer scaled by CONST_SCALE. - * - * static int SLOW_INTEGER_FIX(double x) - * { - * return ((int) ((x) * (((int) 1) << SLOW_INTEGER_CONST_BITS) + 0.5)); - * } - */ - private const int SLOW_INTEGER_FIX_0_298631336 = 2446; /* FIX(0.298631336) */ - private const int SLOW_INTEGER_FIX_0_390180644 = 3196; /* FIX(0.390180644) */ - private const int SLOW_INTEGER_FIX_0_541196100 = 4433; /* FIX(0.541196100) */ - private const int SLOW_INTEGER_FIX_0_765366865 = 6270; /* FIX(0.765366865) */ - private const int SLOW_INTEGER_FIX_0_899976223 = 7373; /* FIX(0.899976223) */ - private const int SLOW_INTEGER_FIX_1_175875602 = 9633; /* FIX(1.175875602) */ - private const int SLOW_INTEGER_FIX_1_501321110 = 12299; /* FIX(1.501321110) */ - private const int SLOW_INTEGER_FIX_1_847759065 = 15137; /* FIX(1.847759065) */ - private const int SLOW_INTEGER_FIX_1_961570560 = 16069; /* FIX(1.961570560) */ - private const int SLOW_INTEGER_FIX_2_053119869 = 16819; /* FIX(2.053119869) */ - private const int SLOW_INTEGER_FIX_2_562915447 = 20995; /* FIX(2.562915447) */ - private const int SLOW_INTEGER_FIX_3_072711026 = 25172; /* FIX(3.072711026) */ - - /* For AA&N IDCT method, divisors are equal to quantization - * coefficients scaled by scalefactor[row]*scalefactor[col], where - * scalefactor[0] = 1 - * scalefactor[k] = cos(k*PI/16) * sqrt(2) for k=1..7 - * We apply a further scale factor of 8. - */ - private const int CONST_BITS = 14; - - /* precomputed values scaled up by 14 bits */ - private static short[] aanscales = { - 16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520, 22725, 31521, 29692, 26722, 22725, 17855, - 12299, 6270, 21407, 29692, 27969, 25172, 21407, 16819, 11585, - 5906, 19266, 26722, 25172, 22654, 19266, 15137, 10426, 5315, - 16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520, 12873, - 17855, 16819, 15137, 12873, 10114, 6967, 3552, 8867, 12299, - 11585, 10426, 8867, 6967, 4799, 2446, 4520, 6270, 5906, 5315, - 4520, 3552, 2446, 1247 }; - - /* For float AA&N IDCT method, divisors are equal to quantization - * coefficients scaled by scalefactor[row]*scalefactor[col], where - * scalefactor[0] = 1 - * scalefactor[k] = cos(k*PI/16) * sqrt(2) for k=1..7 - * We apply a further scale factor of 8. - * What's actually stored is 1/divisor so that the inner loop can - * use a multiplication rather than a division. - */ - private static double[] aanscalefactor = { - 1.0, 1.387039845, 1.306562965, 1.175875602, 1.0, - 0.785694958, 0.541196100, 0.275899379 }; - - private jpeg_compress_struct m_cinfo; - private bool m_useSlowMethod; - private bool m_useFloatMethod; - - /* The actual post-DCT divisors --- not identical to the quant table - * entries, because of scaling (especially for an unnormalized DCT). - * Each table is given in normal array order. - */ - private int[][] m_divisors = new int [JpegConstants.NUM_QUANT_TBLS][]; - - /* Same as above for the floating-point case. */ - private float[][] m_float_divisors = new float[JpegConstants.NUM_QUANT_TBLS][]; - - public jpeg_forward_dct(jpeg_compress_struct cinfo) - { - m_cinfo = cinfo; - - switch (cinfo.m_dct_method) - { - case J_DCT_METHOD.JDCT_ISLOW: - m_useFloatMethod = false; - m_useSlowMethod = true; - break; - case J_DCT_METHOD.JDCT_IFAST: - m_useFloatMethod = false; - m_useSlowMethod = false; - break; - case J_DCT_METHOD.JDCT_FLOAT: - m_useFloatMethod = true; - break; - default: - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NOT_COMPILED); - break; - } - - /* Mark divisor tables unallocated */ - for (int i = 0; i < JpegConstants.NUM_QUANT_TBLS; i++) - { - m_divisors[i] = null; - m_float_divisors[i] = null; - } - } - - /// - /// Initialize for a processing pass. - /// Verify that all referenced Q-tables are present, and set up - /// the divisor table for each one. - /// In the current implementation, DCT of all components is done during - /// the first pass, even if only some components will be output in the - /// first scan. Hence all components should be examined here. - /// - public virtual void start_pass() - { - for (int ci = 0; ci < m_cinfo.m_num_components; ci++) - { - int qtblno = m_cinfo.Component_info[ci].Quant_tbl_no; - - /* Make sure specified quantization table is present */ - if (qtblno < 0 || qtblno >= JpegConstants.NUM_QUANT_TBLS || m_cinfo.m_quant_tbl_ptrs[qtblno] == null) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NO_QUANT_TABLE, qtblno); - - JQUANT_TBL qtbl = m_cinfo.m_quant_tbl_ptrs[qtblno]; - - /* Compute divisors for this quant table */ - /* We may do this more than once for same table, but it's not a big deal */ - int i = 0; - switch (m_cinfo.m_dct_method) - { - case J_DCT_METHOD.JDCT_ISLOW: - /* For LL&M IDCT method, divisors are equal to raw quantization - * coefficients multiplied by 8 (to counteract scaling). - */ - if (m_divisors[qtblno] == null) - m_divisors[qtblno] = new int [JpegConstants.DCTSIZE2]; - - for (i = 0; i < JpegConstants.DCTSIZE2; i++) - m_divisors[qtblno][i] = ((int)qtbl.quantval[i]) << 3; - - break; - case J_DCT_METHOD.JDCT_IFAST: - if (m_divisors[qtblno] == null) - m_divisors[qtblno] = new int [JpegConstants.DCTSIZE2]; - - for (i = 0; i < JpegConstants.DCTSIZE2; i++) - m_divisors[qtblno][i] = JpegUtils.DESCALE((int)qtbl.quantval[i] * (int)aanscales[i], CONST_BITS - 3); - break; - case J_DCT_METHOD.JDCT_FLOAT: - if (m_float_divisors[qtblno] == null) - m_float_divisors[qtblno] = new float [JpegConstants.DCTSIZE2]; - - float[] fdtbl = m_float_divisors[qtblno]; - i = 0; - for (int row = 0; row < JpegConstants.DCTSIZE; row++) - { - for (int col = 0; col < JpegConstants.DCTSIZE; col++) - { - fdtbl[i] = (float)(1.0 / (((double) qtbl.quantval[i] * aanscalefactor[row] * aanscalefactor[col] * 8.0))); - i++; - } - } - break; - default: - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NOT_COMPILED); - break; - } - } - } - - /// - /// Perform forward DCT on one or more blocks of a component. - /// - /// The input samples are taken from the sample_data[] array starting at - /// position start_row/start_col, and moving to the right for any additional - /// blocks. The quantized coefficients are returned in coef_blocks[]. - /// - public virtual void forward_DCT(int quant_tbl_no, byte[][] sample_data, JBLOCK[] coef_blocks, int start_row, int start_col, int num_blocks) - { - if (m_useFloatMethod) - forwardDCTFloatImpl(quant_tbl_no, sample_data, coef_blocks, start_row, start_col, num_blocks); - else - forwardDCTImpl(quant_tbl_no, sample_data, coef_blocks, start_row, start_col, num_blocks); - } - - // This version is used for integer DCT implementations. - private void forwardDCTImpl(int quant_tbl_no, byte[][] sample_data, JBLOCK[] coef_blocks, int start_row, int start_col, int num_blocks) - { - /* This routine is heavily used, so it's worth coding it tightly. */ - int[] workspace = new int [JpegConstants.DCTSIZE2]; /* work area for FDCT subroutine */ - for (int bi = 0; bi < num_blocks; bi++, start_col += JpegConstants.DCTSIZE) - { - /* Load data into workspace, applying unsigned->signed conversion */ - int workspaceIndex = 0; - for (int elemr = 0; elemr < JpegConstants.DCTSIZE; elemr++) - { - for (int column = 0; column < JpegConstants.DCTSIZE; column++) - { - workspace[workspaceIndex] = (int)sample_data[start_row + elemr][start_col + column] - JpegConstants.CENTERJSAMPLE; - workspaceIndex++; - } - } - - /* Perform the DCT */ - if (m_useSlowMethod) - jpeg_fdct_islow(workspace); - else - jpeg_fdct_ifast(workspace); - - /* Quantize/descale the coefficients, and store into coef_blocks[] */ - for (int i = 0; i < JpegConstants.DCTSIZE2; i++) - { - int qval = m_divisors[quant_tbl_no][i]; - int temp = workspace[i]; - - if (temp < 0) - { - temp = -temp; - temp += qval >> 1; /* for rounding */ - - if (temp >= qval) - temp /= qval; - else - temp = 0; - - temp = -temp; - } - else - { - temp += qval >> 1; /* for rounding */ - - if (temp >= qval) - temp /= qval; - else - temp = 0; - } - - coef_blocks[bi][i] = (short) temp; - } - } - } - - // This version is used for floating-point DCT implementations. - private void forwardDCTFloatImpl(int quant_tbl_no, byte[][] sample_data, JBLOCK[] coef_blocks, int start_row, int start_col, int num_blocks) - { - /* This routine is heavily used, so it's worth coding it tightly. */ - float[] workspace = new float [JpegConstants.DCTSIZE2]; /* work area for FDCT subroutine */ - for (int bi = 0; bi < num_blocks; bi++, start_col += JpegConstants.DCTSIZE) - { - /* Load data into workspace, applying unsigned->signed conversion */ - int workspaceIndex = 0; - for (int elemr = 0; elemr < JpegConstants.DCTSIZE; elemr++) - { - for (int column = 0; column < JpegConstants.DCTSIZE; column++) - { - workspace[workspaceIndex] = (float)((int)sample_data[start_row + elemr][start_col + column] - JpegConstants.CENTERJSAMPLE); - workspaceIndex++; - } - } - - /* Perform the DCT */ - jpeg_fdct_float(workspace); - - /* Quantize/descale the coefficients, and store into coef_blocks[] */ - for (int i = 0; i < JpegConstants.DCTSIZE2; i++) - { - /* Apply the quantization and scaling factor */ - float temp = workspace[i] * m_float_divisors[quant_tbl_no][i]; - - /* Round to nearest integer. - * Since C does not specify the direction of rounding for negative - * quotients, we have to force the dividend positive for portability. - * The maximum coefficient size is +-16K (for 12-bit data), so this - * code should work for either 16-bit or 32-bit ints. - */ - coef_blocks[bi][i] = (short)((int)(temp + (float)16384.5) - 16384); - } - } - } - - /// - /// Perform the forward DCT on one block of samples. - /// NOTE: this code only copes with 8x8 DCTs. - /// - /// A floating-point implementation of the - /// forward DCT (Discrete Cosine Transform). - /// - /// This implementation should be more accurate than either of the integer - /// DCT implementations. However, it may not give the same results on all - /// machines because of differences in roundoff behavior. Speed will depend - /// on the hardware's floating point capacity. - /// - /// A 2-D DCT can be done by 1-D DCT on each row followed by 1-D DCT - /// on each column. Direct algorithms are also available, but they are - /// much more complex and seem not to be any faster when reduced to code. - /// - /// This implementation is based on Arai, Agui, and Nakajima's algorithm for - /// scaled DCT. Their original paper (Trans. IEICE E-71(11):1095) is in - /// Japanese, but the algorithm is described in the Pennebaker & Mitchell - /// JPEG textbook (see REFERENCES section in file README). The following code - /// is based directly on figure 4-8 in P&M. - /// While an 8-point DCT cannot be done in less than 11 multiplies, it is - /// possible to arrange the computation so that many of the multiplies are - /// simple scalings of the final outputs. These multiplies can then be - /// folded into the multiplications or divisions by the JPEG quantization - /// table entries. The AA&N method leaves only 5 multiplies and 29 adds - /// to be done in the DCT itself. - /// The primary disadvantage of this method is that with a fixed-point - /// implementation, accuracy is lost due to imprecise representation of the - /// scaled quantization values. However, that problem does not arise if - /// we use floating point arithmetic. - /// - private static void jpeg_fdct_float(float[] data) - { - /* Pass 1: process rows. */ - int dataIndex = 0; - for (int ctr = JpegConstants.DCTSIZE - 1; ctr >= 0; ctr--) - { - float tmp0 = data[dataIndex + 0] + data[dataIndex + 7]; - float tmp7 = data[dataIndex + 0] - data[dataIndex + 7]; - float tmp1 = data[dataIndex + 1] + data[dataIndex + 6]; - float tmp6 = data[dataIndex + 1] - data[dataIndex + 6]; - float tmp2 = data[dataIndex + 2] + data[dataIndex + 5]; - float tmp5 = data[dataIndex + 2] - data[dataIndex + 5]; - float tmp3 = data[dataIndex + 3] + data[dataIndex + 4]; - float tmp4 = data[dataIndex + 3] - data[dataIndex + 4]; - - /* Even part */ - - float tmp10 = tmp0 + tmp3; /* phase 2 */ - float tmp13 = tmp0 - tmp3; - float tmp11 = tmp1 + tmp2; - float tmp12 = tmp1 - tmp2; - - data[dataIndex + 0] = tmp10 + tmp11; /* phase 3 */ - data[dataIndex + 4] = tmp10 - tmp11; - - float z1 = (tmp12 + tmp13) * ((float)0.707106781); /* c4 */ - data[dataIndex + 2] = tmp13 + z1; /* phase 5 */ - data[dataIndex + 6] = tmp13 - z1; - - /* Odd part */ - - tmp10 = tmp4 + tmp5; /* phase 2 */ - tmp11 = tmp5 + tmp6; - tmp12 = tmp6 + tmp7; - - /* The rotator is modified from fig 4-8 to avoid extra negations. */ - float z5 = (tmp10 - tmp12) * ((float)0.382683433); /* c6 */ - float z2 = ((float)0.541196100) * tmp10 + z5; /* c2-c6 */ - float z4 = ((float)1.306562965) * tmp12 + z5; /* c2+c6 */ - float z3 = tmp11 * ((float)0.707106781); /* c4 */ - - float z11 = tmp7 + z3; /* phase 5 */ - float z13 = tmp7 - z3; - - data[dataIndex + 5] = z13 + z2; /* phase 6 */ - data[dataIndex + 3] = z13 - z2; - data[dataIndex + 1] = z11 + z4; - data[dataIndex + 7] = z11 - z4; - - dataIndex += JpegConstants.DCTSIZE; /* advance pointer to next row */ - } - - /* Pass 2: process columns. */ - - dataIndex = 0; - for (int ctr = JpegConstants.DCTSIZE - 1; ctr >= 0; ctr--) - { - float tmp0 = data[dataIndex + JpegConstants.DCTSIZE * 0] + data[dataIndex + JpegConstants.DCTSIZE * 7]; - float tmp7 = data[dataIndex + JpegConstants.DCTSIZE * 0] - data[dataIndex + JpegConstants.DCTSIZE * 7]; - float tmp1 = data[dataIndex + JpegConstants.DCTSIZE * 1] + data[dataIndex + JpegConstants.DCTSIZE * 6]; - float tmp6 = data[dataIndex + JpegConstants.DCTSIZE * 1] - data[dataIndex + JpegConstants.DCTSIZE * 6]; - float tmp2 = data[dataIndex + JpegConstants.DCTSIZE * 2] + data[dataIndex + JpegConstants.DCTSIZE * 5]; - float tmp5 = data[dataIndex + JpegConstants.DCTSIZE * 2] - data[dataIndex + JpegConstants.DCTSIZE * 5]; - float tmp3 = data[dataIndex + JpegConstants.DCTSIZE * 3] + data[dataIndex + JpegConstants.DCTSIZE * 4]; - float tmp4 = data[dataIndex + JpegConstants.DCTSIZE * 3] - data[dataIndex + JpegConstants.DCTSIZE * 4]; - - /* Even part */ - - float tmp10 = tmp0 + tmp3; /* phase 2 */ - float tmp13 = tmp0 - tmp3; - float tmp11 = tmp1 + tmp2; - float tmp12 = tmp1 - tmp2; - - data[dataIndex + JpegConstants.DCTSIZE * 0] = tmp10 + tmp11; /* phase 3 */ - data[dataIndex + JpegConstants.DCTSIZE * 4] = tmp10 - tmp11; - - float z1 = (tmp12 + tmp13) * ((float)0.707106781); /* c4 */ - data[dataIndex + JpegConstants.DCTSIZE * 2] = tmp13 + z1; /* phase 5 */ - data[dataIndex + JpegConstants.DCTSIZE * 6] = tmp13 - z1; - - /* Odd part */ - - tmp10 = tmp4 + tmp5; /* phase 2 */ - tmp11 = tmp5 + tmp6; - tmp12 = tmp6 + tmp7; - - /* The rotator is modified from fig 4-8 to avoid extra negations. */ - float z5 = (tmp10 - tmp12) * ((float)0.382683433); /* c6 */ - float z2 = ((float)0.541196100) * tmp10 + z5; /* c2-c6 */ - float z4 = ((float)1.306562965) * tmp12 + z5; /* c2+c6 */ - float z3 = tmp11 * ((float)0.707106781); /* c4 */ - - float z11 = tmp7 + z3; /* phase 5 */ - float z13 = tmp7 - z3; - - data[dataIndex + JpegConstants.DCTSIZE * 5] = z13 + z2; /* phase 6 */ - data[dataIndex + JpegConstants.DCTSIZE * 3] = z13 - z2; - data[dataIndex + JpegConstants.DCTSIZE * 1] = z11 + z4; - data[dataIndex + JpegConstants.DCTSIZE * 7] = z11 - z4; - - dataIndex++; /* advance pointer to next column */ - } - } - - /// - /// Perform the forward DCT on one block of samples. - /// NOTE: this code only copes with 8x8 DCTs. - /// This file contains a fast, not so accurate integer implementation of the - /// forward DCT (Discrete Cosine Transform). - /// - /// A 2-D DCT can be done by 1-D DCT on each row followed by 1-D DCT - /// on each column. Direct algorithms are also available, but they are - /// much more complex and seem not to be any faster when reduced to code. - /// - /// This implementation is based on Arai, Agui, and Nakajima's algorithm for - /// scaled DCT. Their original paper (Trans. IEICE E-71(11):1095) is in - /// Japanese, but the algorithm is described in the Pennebaker & Mitchell - /// JPEG textbook (see REFERENCES section in file README). The following code - /// is based directly on figure 4-8 in P&M. - /// While an 8-point DCT cannot be done in less than 11 multiplies, it is - /// possible to arrange the computation so that many of the multiplies are - /// simple scalings of the final outputs. These multiplies can then be - /// folded into the multiplications or divisions by the JPEG quantization - /// table entries. The AA&N method leaves only 5 multiplies and 29 adds - /// to be done in the DCT itself. - /// The primary disadvantage of this method is that with fixed-point math, - /// accuracy is lost due to imprecise representation of the scaled - /// quantization values. The smaller the quantization table entry, the less - /// precise the scaled value, so this implementation does worse with high- - /// quality-setting files than with low-quality ones. - /// - /// Scaling decisions are generally the same as in the LL&M algorithm; - /// see jpeg_fdct_islow for more details. However, we choose to descale - /// (right shift) multiplication products as soon as they are formed, - /// rather than carrying additional fractional bits into subsequent additions. - /// This compromises accuracy slightly, but it lets us save a few shifts. - /// More importantly, 16-bit arithmetic is then adequate (for 8-bit samples) - /// everywhere except in the multiplications proper; this saves a good deal - /// of work on 16-bit-int machines. - /// - /// Again to save a few shifts, the intermediate results between pass 1 and - /// pass 2 are not upscaled, but are represented only to integral precision. - /// - /// A final compromise is to represent the multiplicative constants to only - /// 8 fractional bits, rather than 13. This saves some shifting work on some - /// machines, and may also reduce the cost of multiplication (since there - /// are fewer one-bits in the constants). - /// - private static void jpeg_fdct_ifast(int[] data) - { - /* Pass 1: process rows. */ - int dataIndex = 0; - for (int ctr = JpegConstants.DCTSIZE - 1; ctr >= 0; ctr--) - { - int tmp0 = data[dataIndex + 0] + data[dataIndex + 7]; - int tmp7 = data[dataIndex + 0] - data[dataIndex + 7]; - int tmp1 = data[dataIndex + 1] + data[dataIndex + 6]; - int tmp6 = data[dataIndex + 1] - data[dataIndex + 6]; - int tmp2 = data[dataIndex + 2] + data[dataIndex + 5]; - int tmp5 = data[dataIndex + 2] - data[dataIndex + 5]; - int tmp3 = data[dataIndex + 3] + data[dataIndex + 4]; - int tmp4 = data[dataIndex + 3] - data[dataIndex + 4]; - - /* Even part */ - - int tmp10 = tmp0 + tmp3; /* phase 2 */ - int tmp13 = tmp0 - tmp3; - int tmp11 = tmp1 + tmp2; - int tmp12 = tmp1 - tmp2; - - data[dataIndex + 0] = tmp10 + tmp11; /* phase 3 */ - data[dataIndex + 4] = tmp10 - tmp11; - - int z1 = FAST_INTEGER_MULTIPLY(tmp12 + tmp13, FAST_INTEGER_FIX_0_707106781); /* c4 */ - data[dataIndex + 2] = tmp13 + z1; /* phase 5 */ - data[dataIndex + 6] = tmp13 - z1; - - /* Odd part */ - - tmp10 = tmp4 + tmp5; /* phase 2 */ - tmp11 = tmp5 + tmp6; - tmp12 = tmp6 + tmp7; - - /* The rotator is modified from fig 4-8 to avoid extra negations. */ - int z5 = FAST_INTEGER_MULTIPLY(tmp10 - tmp12, FAST_INTEGER_FIX_0_382683433); /* c6 */ - int z2 = FAST_INTEGER_MULTIPLY(tmp10, FAST_INTEGER_FIX_0_541196100) + z5; /* c2-c6 */ - int z4 = FAST_INTEGER_MULTIPLY(tmp12, FAST_INTEGER_FIX_1_306562965) + z5; /* c2+c6 */ - int z3 = FAST_INTEGER_MULTIPLY(tmp11, FAST_INTEGER_FIX_0_707106781); /* c4 */ - - int z11 = tmp7 + z3; /* phase 5 */ - int z13 = tmp7 - z3; - - data[dataIndex + 5] = z13 + z2; /* phase 6 */ - data[dataIndex + 3] = z13 - z2; - data[dataIndex + 1] = z11 + z4; - data[dataIndex + 7] = z11 - z4; - - dataIndex += JpegConstants.DCTSIZE; /* advance pointer to next row */ - } - - /* Pass 2: process columns. */ - - dataIndex = 0; - for (int ctr = JpegConstants.DCTSIZE - 1; ctr >= 0; ctr--) - { - int tmp0 = data[dataIndex + JpegConstants.DCTSIZE * 0] + data[dataIndex + JpegConstants.DCTSIZE * 7]; - int tmp7 = data[dataIndex + JpegConstants.DCTSIZE * 0] - data[dataIndex + JpegConstants.DCTSIZE * 7]; - int tmp1 = data[dataIndex + JpegConstants.DCTSIZE * 1] + data[dataIndex + JpegConstants.DCTSIZE * 6]; - int tmp6 = data[dataIndex + JpegConstants.DCTSIZE * 1] - data[dataIndex + JpegConstants.DCTSIZE * 6]; - int tmp2 = data[dataIndex + JpegConstants.DCTSIZE * 2] + data[dataIndex + JpegConstants.DCTSIZE * 5]; - int tmp5 = data[dataIndex + JpegConstants.DCTSIZE * 2] - data[dataIndex + JpegConstants.DCTSIZE * 5]; - int tmp3 = data[dataIndex + JpegConstants.DCTSIZE * 3] + data[dataIndex + JpegConstants.DCTSIZE * 4]; - int tmp4 = data[dataIndex + JpegConstants.DCTSIZE * 3] - data[dataIndex + JpegConstants.DCTSIZE * 4]; - - /* Even part */ - - int tmp10 = tmp0 + tmp3; /* phase 2 */ - int tmp13 = tmp0 - tmp3; - int tmp11 = tmp1 + tmp2; - int tmp12 = tmp1 - tmp2; - - data[dataIndex + JpegConstants.DCTSIZE * 0] = tmp10 + tmp11; /* phase 3 */ - data[dataIndex + JpegConstants.DCTSIZE * 4] = tmp10 - tmp11; - - int z1 = FAST_INTEGER_MULTIPLY(tmp12 + tmp13, FAST_INTEGER_FIX_0_707106781); /* c4 */ - data[dataIndex + JpegConstants.DCTSIZE * 2] = tmp13 + z1; /* phase 5 */ - data[dataIndex + JpegConstants.DCTSIZE * 6] = tmp13 - z1; - - /* Odd part */ - - tmp10 = tmp4 + tmp5; /* phase 2 */ - tmp11 = tmp5 + tmp6; - tmp12 = tmp6 + tmp7; - - /* The rotator is modified from fig 4-8 to avoid extra negations. */ - int z5 = FAST_INTEGER_MULTIPLY(tmp10 - tmp12, FAST_INTEGER_FIX_0_382683433); /* c6 */ - int z2 = FAST_INTEGER_MULTIPLY(tmp10, FAST_INTEGER_FIX_0_541196100) + z5; /* c2-c6 */ - int z4 = FAST_INTEGER_MULTIPLY(tmp12, FAST_INTEGER_FIX_1_306562965) + z5; /* c2+c6 */ - int z3 = FAST_INTEGER_MULTIPLY(tmp11, FAST_INTEGER_FIX_0_707106781); /* c4 */ - - int z11 = tmp7 + z3; /* phase 5 */ - int z13 = tmp7 - z3; - - data[dataIndex + JpegConstants.DCTSIZE * 5] = z13 + z2; /* phase 6 */ - data[dataIndex + JpegConstants.DCTSIZE * 3] = z13 - z2; - data[dataIndex + JpegConstants.DCTSIZE * 1] = z11 + z4; - data[dataIndex + JpegConstants.DCTSIZE * 7] = z11 - z4; - - dataIndex++; /* advance pointer to next column */ - } - } - - /// - /// Perform the forward DCT on one block of samples. - /// NOTE: this code only copes with 8x8 DCTs. - /// - /// A slow-but-accurate integer implementation of the - /// forward DCT (Discrete Cosine Transform). - /// - /// A 2-D DCT can be done by 1-D DCT on each row followed by 1-D DCT - /// on each column. Direct algorithms are also available, but they are - /// much more complex and seem not to be any faster when reduced to code. - /// - /// This implementation is based on an algorithm described in - /// C. Loeffler, A. Ligtenberg and G. Moschytz, "Practical Fast 1-D DCT - /// Algorithms with 11 Multiplications", Proc. Int'l. Conf. on Acoustics, - /// Speech, and Signal Processing 1989 (ICASSP '89), pp. 988-991. - /// The primary algorithm described there uses 11 multiplies and 29 adds. - /// We use their alternate method with 12 multiplies and 32 adds. - /// The advantage of this method is that no data path contains more than one - /// multiplication; this allows a very simple and accurate implementation in - /// scaled fixed-point arithmetic, with a minimal number of shifts. - /// - /// The poop on this scaling stuff is as follows: - /// - /// Each 1-D DCT step produces outputs which are a factor of sqrt(N) - /// larger than the true DCT outputs. The final outputs are therefore - /// a factor of N larger than desired; since N=8 this can be cured by - /// a simple right shift at the end of the algorithm. The advantage of - /// this arrangement is that we save two multiplications per 1-D DCT, - /// because the y0 and y4 outputs need not be divided by sqrt(N). - /// In the IJG code, this factor of 8 is removed by the quantization - /// step, NOT here. - /// - /// We have to do addition and subtraction of the integer inputs, which - /// is no problem, and multiplication by fractional constants, which is - /// a problem to do in integer arithmetic. We multiply all the constants - /// by CONST_SCALE and convert them to integer constants (thus retaining - /// SLOW_INTEGER_CONST_BITS bits of precision in the constants). After doing a - /// multiplication we have to divide the product by CONST_SCALE, with proper - /// rounding, to produce the correct output. This division can be done - /// cheaply as a right shift of SLOW_INTEGER_CONST_BITS bits. We postpone shifting - /// as long as possible so that partial sums can be added together with - /// full fractional precision. - /// - /// The outputs of the first pass are scaled up by SLOW_INTEGER_PASS1_BITS bits so that - /// they are represented to better-than-integral precision. These outputs - /// require BITS_IN_JSAMPLE + SLOW_INTEGER_PASS1_BITS + 3 bits; this fits in a 16-bit word - /// with the recommended scaling. (For 12-bit sample data, the intermediate - /// array is int anyway.) - /// - /// To avoid overflow of the 32-bit intermediate results in pass 2, we must - /// have BITS_IN_JSAMPLE + SLOW_INTEGER_CONST_BITS + SLOW_INTEGER_PASS1_BITS <= 26. Error analysis - /// shows that the values given below are the most effective. - /// - private static void jpeg_fdct_islow(int[] data) - { - /* Pass 1: process rows. */ - /* Note results are scaled up by sqrt(8) compared to a true DCT; */ - /* furthermore, we scale the results by 2**SLOW_INTEGER_PASS1_BITS. */ - int dataIndex = 0; - for (int ctr = JpegConstants.DCTSIZE - 1; ctr >= 0; ctr--) - { - int tmp0 = data[dataIndex + 0] + data[dataIndex + 7]; - int tmp7 = data[dataIndex + 0] - data[dataIndex + 7]; - int tmp1 = data[dataIndex + 1] + data[dataIndex + 6]; - int tmp6 = data[dataIndex + 1] - data[dataIndex + 6]; - int tmp2 = data[dataIndex + 2] + data[dataIndex + 5]; - int tmp5 = data[dataIndex + 2] - data[dataIndex + 5]; - int tmp3 = data[dataIndex + 3] + data[dataIndex + 4]; - int tmp4 = data[dataIndex + 3] - data[dataIndex + 4]; - - /* Even part per LL&M figure 1 --- note that published figure is faulty; - * rotator "sqrt(2)*c1" should be "sqrt(2)*c6". - */ - - int tmp10 = tmp0 + tmp3; - int tmp13 = tmp0 - tmp3; - int tmp11 = tmp1 + tmp2; - int tmp12 = tmp1 - tmp2; - - data[dataIndex + 0] = (tmp10 + tmp11) << SLOW_INTEGER_PASS1_BITS; - data[dataIndex + 4] = (tmp10 - tmp11) << SLOW_INTEGER_PASS1_BITS; - - int z1 = (tmp12 + tmp13) * SLOW_INTEGER_FIX_0_541196100; - data[dataIndex + 2] = JpegUtils.DESCALE(z1 + tmp13 * SLOW_INTEGER_FIX_0_765366865, - SLOW_INTEGER_CONST_BITS - SLOW_INTEGER_PASS1_BITS); - data[dataIndex + 6] = JpegUtils.DESCALE(z1 + tmp12 * (-SLOW_INTEGER_FIX_1_847759065), - SLOW_INTEGER_CONST_BITS - SLOW_INTEGER_PASS1_BITS); - - /* Odd part per figure 8 --- note paper omits factor of sqrt(2). - * cK represents cos(K*pi/16). - * i0..i3 in the paper are tmp4..tmp7 here. - */ - - z1 = tmp4 + tmp7; - int z2 = tmp5 + tmp6; - int z3 = tmp4 + tmp6; - int z4 = tmp5 + tmp7; - int z5 = (z3 + z4) * SLOW_INTEGER_FIX_1_175875602; /* sqrt(2) * c3 */ - - tmp4 = tmp4 * SLOW_INTEGER_FIX_0_298631336; /* sqrt(2) * (-c1+c3+c5-c7) */ - tmp5 = tmp5 * SLOW_INTEGER_FIX_2_053119869; /* sqrt(2) * ( c1+c3-c5+c7) */ - tmp6 = tmp6 * SLOW_INTEGER_FIX_3_072711026; /* sqrt(2) * ( c1+c3+c5-c7) */ - tmp7 = tmp7 * SLOW_INTEGER_FIX_1_501321110; /* sqrt(2) * ( c1+c3-c5-c7) */ - z1 = z1 * (-SLOW_INTEGER_FIX_0_899976223); /* sqrt(2) * (c7-c3) */ - z2 = z2 * (-SLOW_INTEGER_FIX_2_562915447); /* sqrt(2) * (-c1-c3) */ - z3 = z3 * (-SLOW_INTEGER_FIX_1_961570560); /* sqrt(2) * (-c3-c5) */ - z4 = z4 * (-SLOW_INTEGER_FIX_0_390180644); /* sqrt(2) * (c5-c3) */ - - z3 += z5; - z4 += z5; - - data[dataIndex + 7] = JpegUtils.DESCALE(tmp4 + z1 + z3, SLOW_INTEGER_CONST_BITS - SLOW_INTEGER_PASS1_BITS); - data[dataIndex + 5] = JpegUtils.DESCALE(tmp5 + z2 + z4, SLOW_INTEGER_CONST_BITS - SLOW_INTEGER_PASS1_BITS); - data[dataIndex + 3] = JpegUtils.DESCALE(tmp6 + z2 + z3, SLOW_INTEGER_CONST_BITS - SLOW_INTEGER_PASS1_BITS); - data[dataIndex + 1] = JpegUtils.DESCALE(tmp7 + z1 + z4, SLOW_INTEGER_CONST_BITS - SLOW_INTEGER_PASS1_BITS); - - dataIndex += JpegConstants.DCTSIZE; /* advance pointer to next row */ - } - - /* Pass 2: process columns. - * We remove the SLOW_INTEGER_PASS1_BITS scaling, but leave the results scaled up - * by an overall factor of 8. - */ - - dataIndex = 0; - for (int ctr = JpegConstants.DCTSIZE - 1; ctr >= 0; ctr--) - { - int tmp0 = data[dataIndex + JpegConstants.DCTSIZE * 0] + data[dataIndex + JpegConstants.DCTSIZE * 7]; - int tmp7 = data[dataIndex + JpegConstants.DCTSIZE * 0] - data[dataIndex + JpegConstants.DCTSIZE * 7]; - int tmp1 = data[dataIndex + JpegConstants.DCTSIZE * 1] + data[dataIndex + JpegConstants.DCTSIZE * 6]; - int tmp6 = data[dataIndex + JpegConstants.DCTSIZE * 1] - data[dataIndex + JpegConstants.DCTSIZE * 6]; - int tmp2 = data[dataIndex + JpegConstants.DCTSIZE * 2] + data[dataIndex + JpegConstants.DCTSIZE * 5]; - int tmp5 = data[dataIndex + JpegConstants.DCTSIZE * 2] - data[dataIndex + JpegConstants.DCTSIZE * 5]; - int tmp3 = data[dataIndex + JpegConstants.DCTSIZE * 3] + data[dataIndex + JpegConstants.DCTSIZE * 4]; - int tmp4 = data[dataIndex + JpegConstants.DCTSIZE * 3] - data[dataIndex + JpegConstants.DCTSIZE * 4]; - - /* Even part per LL&M figure 1 --- note that published figure is faulty; - * rotator "sqrt(2)*c1" should be "sqrt(2)*c6". - */ - - int tmp10 = tmp0 + tmp3; - int tmp13 = tmp0 - tmp3; - int tmp11 = tmp1 + tmp2; - int tmp12 = tmp1 - tmp2; - - data[dataIndex + JpegConstants.DCTSIZE * 0] = JpegUtils.DESCALE(tmp10 + tmp11, SLOW_INTEGER_PASS1_BITS); - data[dataIndex + JpegConstants.DCTSIZE * 4] = JpegUtils.DESCALE(tmp10 - tmp11, SLOW_INTEGER_PASS1_BITS); - - int z1 = (tmp12 + tmp13) * SLOW_INTEGER_FIX_0_541196100; - data[dataIndex + JpegConstants.DCTSIZE * 2] = JpegUtils.DESCALE(z1 + tmp13 * SLOW_INTEGER_FIX_0_765366865, - SLOW_INTEGER_CONST_BITS + SLOW_INTEGER_PASS1_BITS); - data[dataIndex + JpegConstants.DCTSIZE * 6] = JpegUtils.DESCALE(z1 + tmp12 * (-SLOW_INTEGER_FIX_1_847759065), - SLOW_INTEGER_CONST_BITS + SLOW_INTEGER_PASS1_BITS); - - /* Odd part per figure 8 --- note paper omits factor of sqrt(2). - * cK represents cos(K*pi/16). - * i0..i3 in the paper are tmp4..tmp7 here. - */ - - z1 = tmp4 + tmp7; - int z2 = tmp5 + tmp6; - int z3 = tmp4 + tmp6; - int z4 = tmp5 + tmp7; - int z5 = (z3 + z4) * SLOW_INTEGER_FIX_1_175875602; /* sqrt(2) * c3 */ - - tmp4 = tmp4 * SLOW_INTEGER_FIX_0_298631336; /* sqrt(2) * (-c1+c3+c5-c7) */ - tmp5 = tmp5 * SLOW_INTEGER_FIX_2_053119869; /* sqrt(2) * ( c1+c3-c5+c7) */ - tmp6 = tmp6 * SLOW_INTEGER_FIX_3_072711026; /* sqrt(2) * ( c1+c3+c5-c7) */ - tmp7 = tmp7 * SLOW_INTEGER_FIX_1_501321110; /* sqrt(2) * ( c1+c3-c5-c7) */ - z1 = z1 * (-SLOW_INTEGER_FIX_0_899976223); /* sqrt(2) * (c7-c3) */ - z2 = z2 * (-SLOW_INTEGER_FIX_2_562915447); /* sqrt(2) * (-c1-c3) */ - z3 = z3 * (-SLOW_INTEGER_FIX_1_961570560); /* sqrt(2) * (-c3-c5) */ - z4 = z4 * (-SLOW_INTEGER_FIX_0_390180644); /* sqrt(2) * (c5-c3) */ - - z3 += z5; - z4 += z5; - - data[dataIndex + JpegConstants.DCTSIZE * 7] = JpegUtils.DESCALE(tmp4 + z1 + z3, SLOW_INTEGER_CONST_BITS + SLOW_INTEGER_PASS1_BITS); - data[dataIndex + JpegConstants.DCTSIZE * 5] = JpegUtils.DESCALE(tmp5 + z2 + z4, SLOW_INTEGER_CONST_BITS + SLOW_INTEGER_PASS1_BITS); - data[dataIndex + JpegConstants.DCTSIZE * 3] = JpegUtils.DESCALE(tmp6 + z2 + z3, SLOW_INTEGER_CONST_BITS + SLOW_INTEGER_PASS1_BITS); - data[dataIndex + JpegConstants.DCTSIZE * 1] = JpegUtils.DESCALE(tmp7 + z1 + z4, SLOW_INTEGER_CONST_BITS + SLOW_INTEGER_PASS1_BITS); - - dataIndex++; /* advance pointer to next column */ - } - } - - /// - /// Multiply a DCTELEM variable by an int constant, and immediately - /// descale to yield a DCTELEM result. - /// - private static int FAST_INTEGER_MULTIPLY(int var, int c) - { -#if !USE_ACCURATE_ROUNDING - return (JpegUtils.RIGHT_SHIFT((var) * (c), FAST_INTEGER_CONST_BITS)); -#else - return (JpegUtils.DESCALE((var) * (c), FAST_INTEGER_CONST_BITS)); -#endif - } - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_input_controller.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_input_controller.cs deleted file mode 100644 index 5794bacc2..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_input_controller.cs +++ /dev/null @@ -1,394 +0,0 @@ -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -/* - * This file contains input control logic for the JPEG decompressor. - * These routines are concerned with controlling the decompressor's input - * processing (marker reading and coefficient decoding). - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// Input control module - /// - class jpeg_input_controller - { - private jpeg_decompress_struct m_cinfo; - private bool m_consumeData; - private bool m_inheaders; /* true until first SOS is reached */ - private bool m_has_multiple_scans; /* True if file has multiple scans */ - private bool m_eoi_reached; /* True when EOI has been consumed */ - - /// - /// Initialize the input controller module. - /// This is called only once, when the decompression object is created. - /// - public jpeg_input_controller(jpeg_decompress_struct cinfo) - { - m_cinfo = cinfo; - - /* Initialize state: can't use reset_input_controller since we don't - * want to try to reset other modules yet. - */ - m_inheaders = true; - } - - public ReadResult consume_input() - { - if (m_consumeData) - return m_cinfo.m_coef.consume_data(); - - return consume_markers(); - } - - /// - /// Reset state to begin a fresh datastream. - /// - public void reset_input_controller() - { - m_consumeData = false; - m_has_multiple_scans = false; /* "unknown" would be better */ - m_eoi_reached = false; - m_inheaders = true; - - /* Reset other modules */ - m_cinfo.m_err.reset_error_mgr(); - m_cinfo.m_marker.reset_marker_reader(); - - /* Reset progression state -- would be cleaner if entropy decoder did this */ - m_cinfo.m_coef_bits = null; - } - - /// - /// Initialize the input modules to read a scan of compressed data. - /// The first call to this is done after initializing - /// the entire decompressor (during jpeg_start_decompress). - /// Subsequent calls come from consume_markers, below. - /// - public void start_input_pass() - { - per_scan_setup(); - latch_quant_tables(); - m_cinfo.m_entropy.start_pass(); - m_cinfo.m_coef.start_input_pass(); - m_consumeData = true; - } - - /// - /// Finish up after inputting a compressed-data scan. - /// This is called by the coefficient controller after it's read all - /// the expected data of the scan. - /// - public void finish_input_pass() - { - m_consumeData = false; - } - - public bool HasMultipleScans() - { - return m_has_multiple_scans; - } - - public bool EOIReached() - { - return m_eoi_reached; - } - - /// - /// Read JPEG markers before, between, or after compressed-data scans. - /// Change state as necessary when a new scan is reached. - /// Return value is JPEG_SUSPENDED, JPEG_REACHED_SOS, or JPEG_REACHED_EOI. - /// - /// The consume_input method pointer points either here or to the - /// coefficient controller's consume_data routine, depending on whether - /// we are reading a compressed data segment or inter-segment markers. - /// - private ReadResult consume_markers() - { - ReadResult val; - - if (m_eoi_reached) /* After hitting EOI, read no further */ - return ReadResult.JPEG_REACHED_EOI; - - val = m_cinfo.m_marker.read_markers(); - - switch (val) - { - case ReadResult.JPEG_REACHED_SOS: - /* Found SOS */ - if (m_inheaders) - { - /* 1st SOS */ - initial_setup(); - m_inheaders = false; - /* Note: start_input_pass must be called by jpeg_decomp_master - * before any more input can be consumed. - */ - } - else - { - /* 2nd or later SOS marker */ - if (!m_has_multiple_scans) - { - /* Oops, I wasn't expecting this! */ - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_EOI_EXPECTED); - } - - m_cinfo.m_inputctl.start_input_pass(); - } - break; - case ReadResult.JPEG_REACHED_EOI: - /* Found EOI */ - m_eoi_reached = true; - if (m_inheaders) - { - /* Tables-only datastream, apparently */ - if (m_cinfo.m_marker.SawSOF()) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_SOF_NO_SOS); - } - else - { - /* Prevent infinite loop in coef ctlr's decompress_data routine - * if user set output_scan_number larger than number of scans. - */ - if (m_cinfo.m_output_scan_number > m_cinfo.m_input_scan_number) - m_cinfo.m_output_scan_number = m_cinfo.m_input_scan_number; - } - break; - case ReadResult.JPEG_SUSPENDED: - break; - } - - return val; - } - - /// - /// Routines to calculate various quantities related to the size of the image. - /// Called once, when first SOS marker is reached - /// - private void initial_setup() - { - /* Make sure image isn't bigger than I can handle */ - if (m_cinfo.m_image_height > JpegConstants.JPEG_MAX_DIMENSION || - m_cinfo.m_image_width > JpegConstants.JPEG_MAX_DIMENSION) - { - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_IMAGE_TOO_BIG, (int)JpegConstants.JPEG_MAX_DIMENSION); - - } - - /* For now, precision must match compiled-in value... */ - if (m_cinfo.m_data_precision != JpegConstants.BITS_IN_JSAMPLE) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_PRECISION, m_cinfo.m_data_precision); - - /* Check that number of components won't exceed internal array sizes */ - if (m_cinfo.m_num_components > JpegConstants.MAX_COMPONENTS) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_COMPONENT_COUNT, m_cinfo.m_num_components, JpegConstants.MAX_COMPONENTS); - - /* Compute maximum sampling factors; check factor validity */ - m_cinfo.m_max_h_samp_factor = 1; - m_cinfo.m_max_v_samp_factor = 1; - - for (int ci = 0; ci < m_cinfo.m_num_components; ci++) - { - if (m_cinfo.Comp_info[ci].H_samp_factor <= 0 || m_cinfo.Comp_info[ci].H_samp_factor > JpegConstants.MAX_SAMP_FACTOR || - m_cinfo.Comp_info[ci].V_samp_factor <= 0 || m_cinfo.Comp_info[ci].V_samp_factor > JpegConstants.MAX_SAMP_FACTOR) - { - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_SAMPLING); - } - - m_cinfo.m_max_h_samp_factor = Math.Max(m_cinfo.m_max_h_samp_factor, m_cinfo.Comp_info[ci].H_samp_factor); - m_cinfo.m_max_v_samp_factor = Math.Max(m_cinfo.m_max_v_samp_factor, m_cinfo.Comp_info[ci].V_samp_factor); - } - - /* We initialize DCT_scaled_size and min_DCT_scaled_size to DCTSIZE. - * In the full decompressor, this will be overridden jpeg_decomp_master; - * but in the transcoder, jpeg_decomp_master is not used, so we must do it here. - */ - m_cinfo.m_min_DCT_scaled_size = JpegConstants.DCTSIZE; - - /* Compute dimensions of components */ - for (int ci = 0; ci < m_cinfo.m_num_components; ci++) - { - m_cinfo.Comp_info[ci].DCT_scaled_size = JpegConstants.DCTSIZE; - - /* Size in DCT blocks */ - m_cinfo.Comp_info[ci].Width_in_blocks = JpegUtils.jdiv_round_up( - m_cinfo.m_image_width * m_cinfo.Comp_info[ci].H_samp_factor, - m_cinfo.m_max_h_samp_factor * JpegConstants.DCTSIZE); - - m_cinfo.Comp_info[ci].height_in_blocks = JpegUtils.jdiv_round_up( - m_cinfo.m_image_height * m_cinfo.Comp_info[ci].V_samp_factor, - m_cinfo.m_max_v_samp_factor * JpegConstants.DCTSIZE); - - /* downsampled_width and downsampled_height will also be overridden by - * jpeg_decomp_master if we are doing full decompression. The transcoder library - * doesn't use these values, but the calling application might. - */ - /* Size in samples */ - m_cinfo.Comp_info[ci].downsampled_width = JpegUtils.jdiv_round_up( - m_cinfo.m_image_width * m_cinfo.Comp_info[ci].H_samp_factor, - m_cinfo.m_max_h_samp_factor); - - m_cinfo.Comp_info[ci].downsampled_height = JpegUtils.jdiv_round_up( - m_cinfo.m_image_height * m_cinfo.Comp_info[ci].V_samp_factor, - m_cinfo.m_max_v_samp_factor); - - /* Mark component needed, until color conversion says otherwise */ - m_cinfo.Comp_info[ci].component_needed = true; - - /* Mark no quantization table yet saved for component */ - m_cinfo.Comp_info[ci].quant_table = null; - } - - /* Compute number of fully interleaved MCU rows. */ - m_cinfo.m_total_iMCU_rows = JpegUtils.jdiv_round_up( - m_cinfo.m_image_height, m_cinfo.m_max_v_samp_factor * JpegConstants.DCTSIZE); - - /* Decide whether file contains multiple scans */ - if (m_cinfo.m_comps_in_scan < m_cinfo.m_num_components || m_cinfo.m_progressive_mode) - m_cinfo.m_inputctl.m_has_multiple_scans = true; - else - m_cinfo.m_inputctl.m_has_multiple_scans = false; - } - - /// - /// Save away a copy of the Q-table referenced by each component present - /// in the current scan, unless already saved during a prior scan. - /// - /// In a multiple-scan JPEG file, the encoder could assign different components - /// the same Q-table slot number, but change table definitions between scans - /// so that each component uses a different Q-table. (The IJG encoder is not - /// currently capable of doing this, but other encoders might.) Since we want - /// to be able to dequantize all the components at the end of the file, this - /// means that we have to save away the table actually used for each component. - /// We do this by copying the table at the start of the first scan containing - /// the component. - /// The JPEG spec prohibits the encoder from changing the contents of a Q-table - /// slot between scans of a component using that slot. If the encoder does so - /// anyway, this decoder will simply use the Q-table values that were current - /// at the start of the first scan for the component. - /// - /// The decompressor output side looks only at the saved quant tables, - /// not at the current Q-table slots. - /// - private void latch_quant_tables() - { - for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) - { - jpeg_component_info componentInfo = m_cinfo.Comp_info[m_cinfo.m_cur_comp_info[ci]]; - - /* No work if we already saved Q-table for this component */ - if (componentInfo.quant_table != null) - continue; - - /* Make sure specified quantization table is present */ - int qtblno = componentInfo.Quant_tbl_no; - if (qtblno < 0 || qtblno >= JpegConstants.NUM_QUANT_TBLS || m_cinfo.m_quant_tbl_ptrs[qtblno] == null) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NO_QUANT_TABLE, qtblno); - - /* OK, save away the quantization table */ - JQUANT_TBL qtbl = new JQUANT_TBL(); - Buffer.BlockCopy(m_cinfo.m_quant_tbl_ptrs[qtblno].quantval, 0, - qtbl.quantval, 0, qtbl.quantval.Length * sizeof(short)); - qtbl.Sent_table = m_cinfo.m_quant_tbl_ptrs[qtblno].Sent_table; - componentInfo.quant_table = qtbl; - m_cinfo.Comp_info[m_cinfo.m_cur_comp_info[ci]] = componentInfo; - } - } - - /// - /// Do computations that are needed before processing a JPEG scan - /// cinfo.comps_in_scan and cinfo.cur_comp_info[] were set from SOS marker - /// - private void per_scan_setup() - { - if (m_cinfo.m_comps_in_scan == 1) - { - /* Noninterleaved (single-component) scan */ - jpeg_component_info componentInfo = m_cinfo.Comp_info[m_cinfo.m_cur_comp_info[0]]; - - /* Overall image size in MCUs */ - m_cinfo.m_MCUs_per_row = componentInfo.Width_in_blocks; - m_cinfo.m_MCU_rows_in_scan = componentInfo.height_in_blocks; - - /* For noninterleaved scan, always one block per MCU */ - componentInfo.MCU_width = 1; - componentInfo.MCU_height = 1; - componentInfo.MCU_blocks = 1; - componentInfo.MCU_sample_width = componentInfo.DCT_scaled_size; - componentInfo.last_col_width = 1; - - /* For noninterleaved scans, it is convenient to define last_row_height - * as the number of block rows present in the last iMCU row. - */ - int tmp = componentInfo.height_in_blocks % componentInfo.V_samp_factor; - if (tmp == 0) - tmp = componentInfo.V_samp_factor; - componentInfo.last_row_height = tmp; - m_cinfo.Comp_info[m_cinfo.m_cur_comp_info[0]] = componentInfo; - - /* Prepare array describing MCU composition */ - m_cinfo.m_blocks_in_MCU = 1; - m_cinfo.m_MCU_membership[0] = 0; - } - else - { - /* Interleaved (multi-component) scan */ - if (m_cinfo.m_comps_in_scan <= 0 || m_cinfo.m_comps_in_scan > JpegConstants.MAX_COMPS_IN_SCAN) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_COMPONENT_COUNT, m_cinfo.m_comps_in_scan, JpegConstants.MAX_COMPS_IN_SCAN); - - /* Overall image size in MCUs */ - m_cinfo.m_MCUs_per_row = JpegUtils.jdiv_round_up( - m_cinfo.m_image_width, m_cinfo.m_max_h_samp_factor * JpegConstants.DCTSIZE); - - m_cinfo.m_MCU_rows_in_scan = JpegUtils.jdiv_round_up( - m_cinfo.m_image_height, m_cinfo.m_max_v_samp_factor * JpegConstants.DCTSIZE); - - m_cinfo.m_blocks_in_MCU = 0; - - for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) - { - jpeg_component_info componentInfo = m_cinfo.Comp_info[m_cinfo.m_cur_comp_info[ci]]; - - /* Sampling factors give # of blocks of component in each MCU */ - componentInfo.MCU_width = componentInfo.H_samp_factor; - componentInfo.MCU_height = componentInfo.V_samp_factor; - componentInfo.MCU_blocks = componentInfo.MCU_width * componentInfo.MCU_height; - componentInfo.MCU_sample_width = componentInfo.MCU_width * componentInfo.DCT_scaled_size; - - /* Figure number of non-dummy blocks in last MCU column & row */ - int tmp = componentInfo.Width_in_blocks % componentInfo.MCU_width; - if (tmp == 0) - tmp = componentInfo.MCU_width; - componentInfo.last_col_width = tmp; - - tmp = componentInfo.height_in_blocks % componentInfo.MCU_height; - if (tmp == 0) - tmp = componentInfo.MCU_height; - componentInfo.last_row_height = tmp; - - /* Prepare array describing MCU composition */ - int mcublks = componentInfo.MCU_blocks; - if (m_cinfo.m_blocks_in_MCU + mcublks > JpegConstants.D_MAX_BLOCKS_IN_MCU) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_MCU_SIZE); - - m_cinfo.Comp_info[m_cinfo.m_cur_comp_info[ci]] = componentInfo; - - while (mcublks-- > 0) - m_cinfo.m_MCU_membership[m_cinfo.m_blocks_in_MCU++] = ci; - } - } - } - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_inverse_dct.cs.REMOVED.git-id b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_inverse_dct.cs.REMOVED.git-id deleted file mode 100644 index b24ac8130..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_inverse_dct.cs.REMOVED.git-id +++ /dev/null @@ -1 +0,0 @@ -db377809a2636d474a6da2cbb6fe7478f2100495 \ No newline at end of file diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_marker_reader.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_marker_reader.cs deleted file mode 100644 index e88cad4f4..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_marker_reader.cs +++ /dev/null @@ -1,1188 +0,0 @@ -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -/* - * This file contains routines to decode JPEG datastream markers. - * Most of the complexity arises from our desire to support input - * suspension: if not all of the data for a marker is available, - * we must exit back to the application. On resumption, we reprocess - * the marker. - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// Marker reading and parsing - /// - class jpeg_marker_reader - { - private const int APP0_DATA_LEN = 14; /* Length of interesting data in APP0 */ - private const int APP14_DATA_LEN = 12; /* Length of interesting data in APP14 */ - private const int APPN_DATA_LEN = 14; /* Must be the largest of the above!! */ - - private jpeg_decompress_struct m_cinfo; - - /* Application-overridable marker processing methods */ - private jpeg_decompress_struct.jpeg_marker_parser_method m_process_COM; - private jpeg_decompress_struct.jpeg_marker_parser_method[] m_process_APPn = new jpeg_decompress_struct.jpeg_marker_parser_method[16]; - - /* Limit on marker data length to save for each marker type */ - private int m_length_limit_COM; - private int[] m_length_limit_APPn = new int[16]; - - private bool m_saw_SOI; /* found SOI? */ - private bool m_saw_SOF; /* found SOF? */ - private int m_next_restart_num; /* next restart number expected (0-7) */ - private int m_discarded_bytes; /* # of bytes skipped looking for a marker */ - - /* Status of COM/APPn marker saving */ - private jpeg_marker_struct m_cur_marker; /* null if not processing a marker */ - private int m_bytes_read; /* data bytes read so far in marker */ - /* Note: cur_marker is not linked into marker_list until it's all read. */ - - /// - /// Initialize the marker reader module. - /// This is called only once, when the decompression object is created. - /// - public jpeg_marker_reader(jpeg_decompress_struct cinfo) - { - m_cinfo = cinfo; - - /* Initialize COM/APPn processing. - * By default, we examine and then discard APP0 and APP14, - * but simply discard COM and all other APPn. - */ - m_process_COM = skip_variable; - - for (int i = 0; i < 16; i++) - { - m_process_APPn[i] = skip_variable; - m_length_limit_APPn[i] = 0; - } - - m_process_APPn[0] = get_interesting_appn; - m_process_APPn[14] = get_interesting_appn; - - /* Reset marker processing state */ - reset_marker_reader(); - } - - /// - /// Reset marker processing state to begin a fresh datastream. - /// - public void reset_marker_reader() - { - m_cinfo.Comp_info = null; /* until allocated by get_sof */ - m_cinfo.m_input_scan_number = 0; /* no SOS seen yet */ - m_cinfo.m_unread_marker = 0; /* no pending marker */ - m_saw_SOI = false; /* set internal state too */ - m_saw_SOF = false; - m_discarded_bytes = 0; - m_cur_marker = null; - } - - /// - /// Read markers until SOS or EOI. - /// - /// Returns same codes as are defined for jpeg_consume_input: - /// JPEG_SUSPENDED, JPEG_REACHED_SOS, or JPEG_REACHED_EOI. - /// - public ReadResult read_markers() - { - /* Outer loop repeats once for each marker. */ - for ( ; ; ) - { - /* Collect the marker proper, unless we already did. */ - /* NB: first_marker() enforces the requirement that SOI appear first. */ - if (m_cinfo.m_unread_marker == 0) - { - if (!m_cinfo.m_marker.m_saw_SOI) - { - if (!first_marker()) - return ReadResult.JPEG_SUSPENDED; - } - else - { - if (!next_marker()) - return ReadResult.JPEG_SUSPENDED; - } - } - - /* At this point m_cinfo.unread_marker contains the marker code and the - * input point is just past the marker proper, but before any parameters. - * A suspension will cause us to return with this state still true. - */ - switch ((JPEG_MARKER)m_cinfo.m_unread_marker) - { - case JPEG_MARKER.SOI: - if (!get_soi()) - return ReadResult.JPEG_SUSPENDED; - break; - - case JPEG_MARKER.SOF0: - /* Baseline */ - case JPEG_MARKER.SOF1: - /* Extended sequential, Huffman */ - if (!get_sof(false)) - return ReadResult.JPEG_SUSPENDED; - break; - - case JPEG_MARKER.SOF2: - /* Progressive, Huffman */ - if (!get_sof(true)) - return ReadResult.JPEG_SUSPENDED; - break; - - /* Currently unsupported SOFn types */ - case JPEG_MARKER.SOF3: - /* Lossless, Huffman */ - case JPEG_MARKER.SOF5: - /* Differential sequential, Huffman */ - case JPEG_MARKER.SOF6: - /* Differential progressive, Huffman */ - case JPEG_MARKER.SOF7: - /* Differential lossless, Huffman */ - case JPEG_MARKER.SOF9: - /* Extended sequential, arithmetic */ - case JPEG_MARKER.SOF10: - /* Progressive, arithmetic */ - case JPEG_MARKER.JPG: - /* Reserved for JPEG extensions */ - case JPEG_MARKER.SOF11: - /* Lossless, arithmetic */ - case JPEG_MARKER.SOF13: - /* Differential sequential, arithmetic */ - case JPEG_MARKER.SOF14: - /* Differential progressive, arithmetic */ - case JPEG_MARKER.SOF15: - /* Differential lossless, arithmetic */ - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_SOF_UNSUPPORTED, m_cinfo.m_unread_marker); - break; - - case JPEG_MARKER.SOS: - if (!get_sos()) - return ReadResult.JPEG_SUSPENDED; - m_cinfo.m_unread_marker = 0; /* processed the marker */ - return ReadResult.JPEG_REACHED_SOS; - - case JPEG_MARKER.EOI: - m_cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_EOI); - m_cinfo.m_unread_marker = 0; /* processed the marker */ - return ReadResult.JPEG_REACHED_EOI; - - case JPEG_MARKER.DAC: - if (!skip_variable(m_cinfo)) - return ReadResult.JPEG_SUSPENDED; - break; - - case JPEG_MARKER.DHT: - if (!get_dht()) - return ReadResult.JPEG_SUSPENDED; - break; - - case JPEG_MARKER.DQT: - if (!get_dqt()) - return ReadResult.JPEG_SUSPENDED; - break; - - case JPEG_MARKER.DRI: - if (!get_dri()) - return ReadResult.JPEG_SUSPENDED; - break; - - case JPEG_MARKER.APP0: - case JPEG_MARKER.APP1: - case JPEG_MARKER.APP2: - case JPEG_MARKER.APP3: - case JPEG_MARKER.APP4: - case JPEG_MARKER.APP5: - case JPEG_MARKER.APP6: - case JPEG_MARKER.APP7: - case JPEG_MARKER.APP8: - case JPEG_MARKER.APP9: - case JPEG_MARKER.APP10: - case JPEG_MARKER.APP11: - case JPEG_MARKER.APP12: - case JPEG_MARKER.APP13: - case JPEG_MARKER.APP14: - case JPEG_MARKER.APP15: - if (!m_cinfo.m_marker.m_process_APPn[m_cinfo.m_unread_marker - (int)JPEG_MARKER.APP0](m_cinfo)) - return ReadResult.JPEG_SUSPENDED; - break; - - case JPEG_MARKER.COM: - if (!m_cinfo.m_marker.m_process_COM(m_cinfo)) - return ReadResult.JPEG_SUSPENDED; - break; - - /* these are all parameterless */ - case JPEG_MARKER.RST0: - case JPEG_MARKER.RST1: - case JPEG_MARKER.RST2: - case JPEG_MARKER.RST3: - case JPEG_MARKER.RST4: - case JPEG_MARKER.RST5: - case JPEG_MARKER.RST6: - case JPEG_MARKER.RST7: - case JPEG_MARKER.TEM: - m_cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_PARMLESS_MARKER, m_cinfo.m_unread_marker); - break; - - case JPEG_MARKER.DNL: - /* Ignore DNL ... perhaps the wrong thing */ - if (!skip_variable(m_cinfo)) - return ReadResult.JPEG_SUSPENDED; - break; - - default: - /* must be DHP, EXP, JPGn, or RESn */ - /* For now, we treat the reserved markers as fatal errors since they are - * likely to be used to signal incompatible JPEG Part 3 extensions. - * Once the JPEG 3 version-number marker is well defined, this code - * ought to change! - */ - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_UNKNOWN_MARKER, m_cinfo.m_unread_marker); - break; - } - - /* Successfully processed marker, so reset state variable */ - m_cinfo.m_unread_marker = 0; - } /* end loop */ - } - - /// - /// Read a restart marker, which is expected to appear next in the datastream; - /// if the marker is not there, take appropriate recovery action. - /// Returns false if suspension is required. - /// - /// Made public for use by entropy decoder only - /// - /// This is called by the entropy decoder after it has read an appropriate - /// number of MCUs. cinfo.unread_marker may be nonzero if the entropy decoder - /// has already read a marker from the data source. Under normal conditions - /// cinfo.unread_marker will be reset to 0 before returning; if not reset, - /// it holds a marker which the decoder will be unable to read past. - /// - public bool read_restart_marker() - { - /* Obtain a marker unless we already did. */ - /* Note that next_marker will complain if it skips any data. */ - if (m_cinfo.m_unread_marker == 0) - { - if (!next_marker()) - return false; - } - - if (m_cinfo.m_unread_marker == ((int)JPEG_MARKER.RST0 + m_cinfo.m_marker.m_next_restart_num)) - { - /* Normal case --- swallow the marker and let entropy decoder continue */ - m_cinfo.TRACEMS(3, J_MESSAGE_CODE.JTRC_RST, m_cinfo.m_marker.m_next_restart_num); - m_cinfo.m_unread_marker = 0; - } - else - { - /* Uh-oh, the restart markers have been messed up. */ - /* Let the data source manager determine how to resync. */ - if (!m_cinfo.m_src.resync_to_restart(m_cinfo, m_cinfo.m_marker.m_next_restart_num)) - return false; - } - - /* Update next-restart state */ - m_cinfo.m_marker.m_next_restart_num = (m_cinfo.m_marker.m_next_restart_num + 1) & 7; - - return true; - } - - /// - /// Find the next JPEG marker, save it in cinfo.unread_marker. - /// Returns false if had to suspend before reaching a marker; - /// in that case cinfo.unread_marker is unchanged. - /// - /// Note that the result might not be a valid marker code, - /// but it will never be 0 or FF. - /// - public bool next_marker() - { - int c; - for ( ; ; ) - { - if (!m_cinfo.m_src.GetByte(out c)) - return false; - - /* Skip any non-FF bytes. - * This may look a bit inefficient, but it will not occur in a valid file. - * We sync after each discarded byte so that a suspending data source - * can discard the byte from its buffer. - */ - while (c != 0xFF) - { - m_cinfo.m_marker.m_discarded_bytes++; - if (!m_cinfo.m_src.GetByte(out c)) - return false; - } - - /* This loop swallows any duplicate FF bytes. Extra FFs are legal as - * pad bytes, so don't count them in discarded_bytes. We assume there - * will not be so many consecutive FF bytes as to overflow a suspending - * data source's input buffer. - */ - do - { - if (!m_cinfo.m_src.GetByte(out c)) - return false; - } - while (c == 0xFF); - - if (c != 0) - { - /* found a valid marker, exit loop */ - break; - } - - /* Reach here if we found a stuffed-zero data sequence (FF/00). - * Discard it and loop back to try again. - */ - m_cinfo.m_marker.m_discarded_bytes += 2; - } - - if (m_cinfo.m_marker.m_discarded_bytes != 0) - { - m_cinfo.WARNMS(J_MESSAGE_CODE.JWRN_EXTRANEOUS_DATA, m_cinfo.m_marker.m_discarded_bytes, c); - m_cinfo.m_marker.m_discarded_bytes = 0; - } - - m_cinfo.m_unread_marker = c; - return true; - } - - /// - /// Install a special processing method for COM or APPn markers. - /// - public void jpeg_set_marker_processor(int marker_code, jpeg_decompress_struct.jpeg_marker_parser_method routine) - { - if (marker_code == (int)JPEG_MARKER.COM) - m_process_COM = routine; - else if (marker_code >= (int)JPEG_MARKER.APP0 && marker_code <= (int)JPEG_MARKER.APP15) - m_process_APPn[marker_code - (int)JPEG_MARKER.APP0] = routine; - else - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_UNKNOWN_MARKER, marker_code); - } - - public void jpeg_save_markers(int marker_code, int length_limit) - { - /* Choose processor routine to use. - * APP0/APP14 have special requirements. - */ - jpeg_decompress_struct.jpeg_marker_parser_method processor; - if (length_limit != 0) - { - processor = save_marker; - /* If saving APP0/APP14, save at least enough for our internal use. */ - if (marker_code == (int)JPEG_MARKER.APP0 && length_limit < APP0_DATA_LEN) - length_limit = APP0_DATA_LEN; - else if (marker_code == (int)JPEG_MARKER.APP14 && length_limit < APP14_DATA_LEN) - length_limit = APP14_DATA_LEN; - } - else - { - processor = skip_variable; - /* If discarding APP0/APP14, use our regular on-the-fly processor. */ - if (marker_code == (int)JPEG_MARKER.APP0 || marker_code == (int)JPEG_MARKER.APP14) - processor = get_interesting_appn; - } - - if (marker_code == (int)JPEG_MARKER.COM) - { - m_process_COM = processor; - m_length_limit_COM = length_limit; - } - else if (marker_code >= (int)JPEG_MARKER.APP0 && marker_code <= (int)JPEG_MARKER.APP15) - { - m_process_APPn[marker_code - (int)JPEG_MARKER.APP0] = processor; - m_length_limit_APPn[marker_code - (int)JPEG_MARKER.APP0] = length_limit; - } - else - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_UNKNOWN_MARKER, marker_code); - } - - /* State of marker reader, applications - * supplying COM or APPn handlers might like to know the state. - */ - public bool SawSOI() - { - return m_saw_SOI; - } - - public bool SawSOF() - { - return m_saw_SOF; - } - - public int NextRestartNumber() - { - return m_next_restart_num; - } - - public int DiscardedByteCount() - { - return m_discarded_bytes; - } - - public void SkipBytes(int count) - { - m_discarded_bytes += count; - } - - /// - /// Save an APPn or COM marker into the marker list - /// - private static bool save_marker(jpeg_decompress_struct cinfo) - { - jpeg_marker_struct cur_marker = cinfo.m_marker.m_cur_marker; - - byte[] data = null; - int length = 0; - int bytes_read; - int data_length; - int dataOffset = 0; - - if (cur_marker == null) - { - /* begin reading a marker */ - if (!cinfo.m_src.GetTwoBytes(out length)) - return false; - - length -= 2; - if (length >= 0) - { - /* watch out for bogus length word */ - /* figure out how much we want to save */ - int limit; - if (cinfo.m_unread_marker == (int)JPEG_MARKER.COM) - limit = cinfo.m_marker.m_length_limit_COM; - else - limit = cinfo.m_marker.m_length_limit_APPn[cinfo.m_unread_marker - (int)JPEG_MARKER.APP0]; - - if (length < limit) - limit = length; - - /* allocate and initialize the marker item */ - cur_marker = new jpeg_marker_struct((byte)cinfo.m_unread_marker, length, limit); - - /* data area is just beyond the jpeg_marker_struct */ - data = cur_marker.Data; - cinfo.m_marker.m_cur_marker = cur_marker; - cinfo.m_marker.m_bytes_read = 0; - bytes_read = 0; - data_length = limit; - } - else - { - /* deal with bogus length word */ - bytes_read = data_length = 0; - data = null; - } - } - else - { - /* resume reading a marker */ - bytes_read = cinfo.m_marker.m_bytes_read; - data_length = cur_marker.Data.Length; - data = cur_marker.Data; - dataOffset = bytes_read; - } - - byte[] tempData = null; - if (data_length != 0) - tempData = new byte[data.Length]; - - while (bytes_read < data_length) - { - /* move the restart point to here */ - cinfo.m_marker.m_bytes_read = bytes_read; - - /* If there's not at least one byte in buffer, suspend */ - if (!cinfo.m_src.MakeByteAvailable()) - return false; - - /* Copy bytes with reasonable rapidity */ - int read = cinfo.m_src.GetBytes(tempData, data_length - bytes_read); - Buffer.BlockCopy(tempData, 0, data, dataOffset, data_length - bytes_read); - bytes_read += read; - } - - /* Done reading what we want to read */ - if (cur_marker != null) - { - /* will be null if bogus length word */ - /* Add new marker to end of list */ - cinfo.m_marker_list.Add(cur_marker); - - /* Reset pointer & calc remaining data length */ - data = cur_marker.Data; - dataOffset = 0; - length = cur_marker.OriginalLength - data_length; - } - - /* Reset to initial state for next marker */ - cinfo.m_marker.m_cur_marker = null; - - JPEG_MARKER currentMarker = (JPEG_MARKER)cinfo.m_unread_marker; - if (data_length != 0 && (currentMarker == JPEG_MARKER.APP0 || currentMarker == JPEG_MARKER.APP14)) - { - tempData = new byte[data.Length]; - Buffer.BlockCopy(data, dataOffset, tempData, 0, data.Length - dataOffset); - } - - /* Process the marker if interesting; else just make a generic trace msg */ - switch ((JPEG_MARKER)cinfo.m_unread_marker) - { - case JPEG_MARKER.APP0: - examine_app0(cinfo, tempData, data_length, length); - break; - case JPEG_MARKER.APP14: - examine_app14(cinfo, tempData, data_length, length); - break; - default: - cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_MISC_MARKER, cinfo.m_unread_marker, data_length + length); - break; - } - - /* skip any remaining data -- could be lots */ - if (length > 0) - cinfo.m_src.skip_input_data(length); - - return true; - } - - /// - /// Skip over an unknown or uninteresting variable-length marker - /// - private static bool skip_variable(jpeg_decompress_struct cinfo) - { - int length; - if (!cinfo.m_src.GetTwoBytes(out length)) - return false; - - length -= 2; - - cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_MISC_MARKER, cinfo.m_unread_marker, length); - - if (length > 0) - cinfo.m_src.skip_input_data(length); - - return true; - } - - /// - /// Process an APP0 or APP14 marker without saving it - /// - private static bool get_interesting_appn(jpeg_decompress_struct cinfo) - { - int length; - if (!cinfo.m_src.GetTwoBytes(out length)) - return false; - - length -= 2; - - /* get the interesting part of the marker data */ - int numtoread = 0; - if (length >= APPN_DATA_LEN) - numtoread = APPN_DATA_LEN; - else if (length > 0) - numtoread = length; - - byte[] b = new byte[APPN_DATA_LEN]; - for (int i = 0; i < numtoread; i++) - { - int temp = 0; - if (!cinfo.m_src.GetByte(out temp)) - return false; - - b[i] = (byte) temp; - } - - length -= numtoread; - - /* process it */ - switch ((JPEG_MARKER)cinfo.m_unread_marker) - { - case JPEG_MARKER.APP0: - examine_app0(cinfo, b, numtoread, length); - break; - case JPEG_MARKER.APP14: - examine_app14(cinfo, b, numtoread, length); - break; - default: - /* can't get here unless jpeg_save_markers chooses wrong processor */ - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_UNKNOWN_MARKER, cinfo.m_unread_marker); - break; - } - - /* skip any remaining data -- could be lots */ - if (length > 0) - cinfo.m_src.skip_input_data(length); - - return true; - } - - /* - * Routines for processing APPn and COM markers. - * These are either saved in memory or discarded, per application request. - * APP0 and APP14 are specially checked to see if they are - * JFIF and Adobe markers, respectively. - */ - - /// - /// Examine first few bytes from an APP0. - /// Take appropriate action if it is a JFIF marker. - /// datalen is # of bytes at data[], remaining is length of rest of marker data. - /// - private static void examine_app0(jpeg_decompress_struct cinfo, byte[] data, int datalen, int remaining) - { - int totallen = datalen + remaining; - - if (datalen >= APP0_DATA_LEN && - data[0] == 0x4A && - data[1] == 0x46 && - data[2] == 0x49 && - data[3] == 0x46 && - data[4] == 0) - { - /* Found JFIF APP0 marker: save info */ - cinfo.m_saw_JFIF_marker = true; - cinfo.m_JFIF_major_version = data[5]; - cinfo.m_JFIF_minor_version = data[6]; - cinfo.m_density_unit = (DensityUnit)data[7]; - cinfo.m_X_density = (short)((data[8] << 8) + data[9]); - cinfo.m_Y_density = (short)((data[10] << 8) + data[11]); - - /* Check version. - * Major version must be 1, anything else signals an incompatible change. - * (We used to treat this as an error, but now it's a nonfatal warning, - * because some bozo at Hijaak couldn't read the spec.) - * Minor version should be 0..2, but process anyway if newer. - */ - if (cinfo.m_JFIF_major_version != 1) - cinfo.WARNMS(J_MESSAGE_CODE.JWRN_JFIF_MAJOR, cinfo.m_JFIF_major_version, cinfo.m_JFIF_minor_version); - - /* Generate trace messages */ - cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_JFIF, cinfo.m_JFIF_major_version, cinfo.m_JFIF_minor_version, cinfo.m_X_density, - cinfo.m_Y_density, cinfo.m_density_unit); - - /* Validate thumbnail dimensions and issue appropriate messages */ - if ((data[12] | data[13]) != 0) - cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_JFIF_THUMBNAIL, data[12], data[13]); - - totallen -= APP0_DATA_LEN; - if (totallen != ((int)data[12] * (int)data[13] * 3)) - cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_JFIF_BADTHUMBNAILSIZE, totallen); - } - else if (datalen >= 6 && data[0] == 0x4A && data[1] == 0x46 && data[2] == 0x58 && data[3] == 0x58 && data[4] == 0) - { - /* Found JFIF "JFXX" extension APP0 marker */ - /* The library doesn't actually do anything with these, - * but we try to produce a helpful trace message. - */ - switch (data[5]) - { - case 0x10: - cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_THUMB_JPEG, totallen); - break; - case 0x11: - cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_THUMB_PALETTE, totallen); - break; - case 0x13: - cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_THUMB_RGB, totallen); - break; - default: - cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_JFIF_EXTENSION, data[5], totallen); - break; - } - } - else - { - /* Start of APP0 does not match "JFIF" or "JFXX", or too short */ - cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_APP0, totallen); - } - } - - /// - /// Examine first few bytes from an APP14. - /// Take appropriate action if it is an Adobe marker. - /// datalen is # of bytes at data[], remaining is length of rest of marker data. - /// - private static void examine_app14(jpeg_decompress_struct cinfo, byte[] data, int datalen, int remaining) - { - if (datalen >= APP14_DATA_LEN && - data[0] == 0x41 && - data[1] == 0x64 && - data[2] == 0x6F && - data[3] == 0x62 && - data[4] == 0x65) - { - /* Found Adobe APP14 marker */ - int version = (data[5] << 8) + data[6]; - int flags0 = (data[7] << 8) + data[8]; - int flags1 = (data[9] << 8) + data[10]; - int transform = data[11]; - cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_ADOBE, version, flags0, flags1, transform); - cinfo.m_saw_Adobe_marker = true; - cinfo.m_Adobe_transform = (byte) transform; - } - else - { - /* Start of APP14 does not match "Adobe", or too short */ - cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_APP14, datalen + remaining); - } - } - - /* - * Routines to process JPEG markers. - * - * Entry condition: JPEG marker itself has been read and its code saved - * in cinfo.unread_marker; input restart point is just after the marker. - * - * Exit: if return true, have read and processed any parameters, and have - * updated the restart point to point after the parameters. - * If return false, was forced to suspend before reaching end of - * marker parameters; restart point has not been moved. Same routine - * will be called again after application supplies more input data. - * - * This approach to suspension assumes that all of a marker's parameters - * can fit into a single input bufferload. This should hold for "normal" - * markers. Some COM/APPn markers might have large parameter segments - * that might not fit. If we are simply dropping such a marker, we use - * skip_input_data to get past it, and thereby put the problem on the - * source manager's shoulders. If we are saving the marker's contents - * into memory, we use a slightly different convention: when forced to - * suspend, the marker processor updates the restart point to the end of - * what it's consumed (ie, the end of the buffer) before returning false. - * On resumption, cinfo.unread_marker still contains the marker code, - * but the data source will point to the next chunk of marker data. - * The marker processor must retain internal state to deal with this. - * - * Note that we don't bother to avoid duplicate trace messages if a - * suspension occurs within marker parameters. Other side effects - * require more care. - */ - - - /// - /// Process an SOI marker - /// - private bool get_soi() - { - m_cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_SOI); - - if (m_cinfo.m_marker.m_saw_SOI) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_SOI_DUPLICATE); - - /* Reset all parameters that are defined to be reset by SOI */ - m_cinfo.m_restart_interval = 0; - - /* Set initial assumptions for colorspace etc */ - - m_cinfo.m_jpeg_color_space = J_COLOR_SPACE.JCS_UNKNOWN; - m_cinfo.m_CCIR601_sampling = false; /* Assume non-CCIR sampling??? */ - - m_cinfo.m_saw_JFIF_marker = false; - m_cinfo.m_JFIF_major_version = 1; /* set default JFIF APP0 values */ - m_cinfo.m_JFIF_minor_version = 1; - m_cinfo.m_density_unit = DensityUnit.Unknown; - m_cinfo.m_X_density = 1; - m_cinfo.m_Y_density = 1; - m_cinfo.m_saw_Adobe_marker = false; - m_cinfo.m_Adobe_transform = 0; - - m_cinfo.m_marker.m_saw_SOI = true; - - return true; - } - - /// - /// Process a SOFn marker - /// - private bool get_sof(bool is_prog) - { - m_cinfo.m_progressive_mode = is_prog; - - int length; - if (!m_cinfo.m_src.GetTwoBytes(out length)) - return false; - - if (!m_cinfo.m_src.GetByte(out m_cinfo.m_data_precision)) - return false; - - int temp = 0; - if (!m_cinfo.m_src.GetTwoBytes(out temp)) - return false; - m_cinfo.m_image_height = temp; - - if (!m_cinfo.m_src.GetTwoBytes(out temp)) - return false; - m_cinfo.m_image_width = temp; - - if (!m_cinfo.m_src.GetByte(out m_cinfo.m_num_components)) - return false; - - length -= 8; - - m_cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_SOF, m_cinfo.m_unread_marker, m_cinfo.m_image_width, m_cinfo.m_image_height, - m_cinfo.m_num_components); - - if (m_cinfo.m_marker.m_saw_SOF) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_SOF_DUPLICATE); - - /* We don't support files in which the image height is initially specified */ - /* as 0 and is later redefined by DNL. As long as we have to check that, */ - /* might as well have a general sanity check. */ - if (m_cinfo.m_image_height <= 0 || m_cinfo.m_image_width <= 0 || m_cinfo.m_num_components <= 0) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_EMPTY_IMAGE); - - if (length != (m_cinfo.m_num_components * 3)) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_LENGTH); - - if (m_cinfo.Comp_info == null) - { - /* do only once, even if suspend */ - m_cinfo.Comp_info = jpeg_component_info.createArrayOfComponents(m_cinfo.m_num_components); - } - - for (int ci = 0; ci < m_cinfo.m_num_components; ci++) - { - m_cinfo.Comp_info[ci].Component_index = ci; - - int component_id; - if (!m_cinfo.m_src.GetByte(out component_id)) - return false; - - m_cinfo.Comp_info[ci].Component_id = component_id; - - int c; - if (!m_cinfo.m_src.GetByte(out c)) - return false; - - m_cinfo.Comp_info[ci].H_samp_factor = (c >> 4) & 15; - m_cinfo.Comp_info[ci].V_samp_factor = (c) & 15; - - int quant_tbl_no; - if (!m_cinfo.m_src.GetByte(out quant_tbl_no)) - return false; - - m_cinfo.Comp_info[ci].Quant_tbl_no = quant_tbl_no; - - m_cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_SOF_COMPONENT, m_cinfo.Comp_info[ci].Component_id, - m_cinfo.Comp_info[ci].H_samp_factor, m_cinfo.Comp_info[ci].V_samp_factor, - m_cinfo.Comp_info[ci].Quant_tbl_no); - } - - m_cinfo.m_marker.m_saw_SOF = true; - return true; - } - - /// - /// Process a SOS marker - /// - private bool get_sos() - { - if (!m_cinfo.m_marker.m_saw_SOF) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_SOS_NO_SOF); - - int length; - if (!m_cinfo.m_src.GetTwoBytes(out length)) - return false; - - /* Number of components */ - int n; - if (!m_cinfo.m_src.GetByte(out n)) - return false; - - m_cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_SOS, n); - - if (length != (n * 2 + 6) || n < 1 || n > JpegConstants.MAX_COMPS_IN_SCAN) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_LENGTH); - - m_cinfo.m_comps_in_scan = n; - - /* Collect the component-spec parameters */ - - for (int i = 0; i < n; i++) - { - int cc; - if (!m_cinfo.m_src.GetByte(out cc)) - return false; - - int c; - if (!m_cinfo.m_src.GetByte(out c)) - return false; - - bool idFound = false; - int foundIndex = -1; - for (int ci = 0; ci < m_cinfo.m_num_components; ci++) - { - if (cc == m_cinfo.Comp_info[ci].Component_id) - { - foundIndex = ci; - idFound = true; - break; - } - } - - if (!idFound) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_COMPONENT_ID, cc); - - m_cinfo.m_cur_comp_info[i] = foundIndex; - m_cinfo.Comp_info[foundIndex].Dc_tbl_no = (c >> 4) & 15; - m_cinfo.Comp_info[foundIndex].Ac_tbl_no = (c) & 15; - - m_cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_SOS_COMPONENT, cc, - m_cinfo.Comp_info[foundIndex].Dc_tbl_no, m_cinfo.Comp_info[foundIndex].Ac_tbl_no); - } - - /* Collect the additional scan parameters Ss, Se, Ah/Al. */ - int temp; - if (!m_cinfo.m_src.GetByte(out temp)) - return false; - - m_cinfo.m_Ss = temp; - if (!m_cinfo.m_src.GetByte(out temp)) - return false; - - m_cinfo.m_Se = temp; - if (!m_cinfo.m_src.GetByte(out temp)) - return false; - - m_cinfo.m_Ah = (temp >> 4) & 15; - m_cinfo.m_Al = (temp) & 15; - - m_cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_SOS_PARAMS, m_cinfo.m_Ss, m_cinfo.m_Se, m_cinfo.m_Ah, m_cinfo.m_Al); - - /* Prepare to scan data & restart markers */ - m_cinfo.m_marker.m_next_restart_num = 0; - - /* Count another SOS marker */ - m_cinfo.m_input_scan_number++; - return true; - } - - /// - /// Process a DHT marker - /// - private bool get_dht() - { - int length; - if (!m_cinfo.m_src.GetTwoBytes(out length)) - return false; - - length -= 2; - - byte[] bits = new byte[17]; - byte[] huffval = new byte[256]; - while (length > 16) - { - int index; - if (!m_cinfo.m_src.GetByte(out index)) - return false; - - m_cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_DHT, index); - - bits[0] = 0; - int count = 0; - for (int i = 1; i <= 16; i++) - { - int temp = 0; - if (!m_cinfo.m_src.GetByte(out temp)) - return false; - - bits[i] = (byte) temp; - count += bits[i]; - } - - length -= 1 + 16; - - m_cinfo.TRACEMS(2, J_MESSAGE_CODE.JTRC_HUFFBITS, bits[1], bits[2], bits[3], bits[4], bits[5], bits[6], bits[7], bits[8]); - m_cinfo.TRACEMS(2, J_MESSAGE_CODE.JTRC_HUFFBITS, bits[9], bits[10], bits[11], bits[12], bits[13], bits[14], bits[15], bits[16]); - - /* Here we just do minimal validation of the counts to avoid walking - * off the end of our table space. huff_entropy_decoder will check more carefully. - */ - if (count > 256 || count > length) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_HUFF_TABLE); - - for (int i = 0; i < count; i++) - { - int temp = 0; - if (!m_cinfo.m_src.GetByte(out temp)) - return false; - - huffval[i] = (byte) temp; - } - - length -= count; - - JHUFF_TBL htblptr = null; - if ((index & 0x10) != 0) - { - /* AC table definition */ - index -= 0x10; - if (m_cinfo.m_ac_huff_tbl_ptrs[index] == null) - m_cinfo.m_ac_huff_tbl_ptrs[index] = new JHUFF_TBL(); - - htblptr = m_cinfo.m_ac_huff_tbl_ptrs[index]; - } - else - { - /* DC table definition */ - if (m_cinfo.m_dc_huff_tbl_ptrs[index] == null) - m_cinfo.m_dc_huff_tbl_ptrs[index] = new JHUFF_TBL(); - - htblptr = m_cinfo.m_dc_huff_tbl_ptrs[index]; - } - - if (index < 0 || index >= JpegConstants.NUM_HUFF_TBLS) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_DHT_INDEX, index); - - Buffer.BlockCopy(bits, 0, htblptr.Bits, 0, htblptr.Bits.Length); - Buffer.BlockCopy(huffval, 0, htblptr.Huffval, 0, htblptr.Huffval.Length); - } - - if (length != 0) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_LENGTH); - - return true; - } - - /// - /// Process a DQT marker - /// - private bool get_dqt() - { - int length; - if (!m_cinfo.m_src.GetTwoBytes(out length)) - return false; - - length -= 2; - while (length > 0) - { - int n; - if (!m_cinfo.m_src.GetByte(out n)) - return false; - - int prec = n >> 4; - n &= 0x0F; - - m_cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_DQT, n, prec); - - if (n >= JpegConstants.NUM_QUANT_TBLS) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_DQT_INDEX, n); - - if (m_cinfo.m_quant_tbl_ptrs[n] == null) - m_cinfo.m_quant_tbl_ptrs[n] = new JQUANT_TBL(); - - JQUANT_TBL quant_ptr = m_cinfo.m_quant_tbl_ptrs[n]; - - for (int i = 0; i < JpegConstants.DCTSIZE2; i++) - { - int tmp; - if (prec != 0) - { - int temp = 0; - if (!m_cinfo.m_src.GetTwoBytes(out temp)) - return false; - - tmp = temp; - } - else - { - int temp = 0; - if (!m_cinfo.m_src.GetByte(out temp)) - return false; - - tmp = temp; - } - - /* We convert the zigzag-order table to natural array order. */ - quant_ptr.quantval[JpegUtils.jpeg_natural_order[i]] = (short) tmp; - } - - if (m_cinfo.m_err.m_trace_level >= 2) - { - for (int i = 0; i < JpegConstants.DCTSIZE2; i += 8) - { - m_cinfo.TRACEMS(2, J_MESSAGE_CODE.JTRC_QUANTVALS, quant_ptr.quantval[i], - quant_ptr.quantval[i + 1], quant_ptr.quantval[i + 2], - quant_ptr.quantval[i + 3], quant_ptr.quantval[i + 4], - quant_ptr.quantval[i + 5], quant_ptr.quantval[i + 6], quant_ptr.quantval[i + 7]); - } - } - - length -= JpegConstants.DCTSIZE2 + 1; - if (prec != 0) - length -= JpegConstants.DCTSIZE2; - } - - if (length != 0) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_LENGTH); - - return true; - } - - /// - /// Process a DRI marker - /// - private bool get_dri() - { - int length; - if (!m_cinfo.m_src.GetTwoBytes(out length)) - return false; - - if (length != 4) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_LENGTH); - - int temp = 0; - if (!m_cinfo.m_src.GetTwoBytes(out temp)) - return false; - - int tmp = temp; - m_cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_DRI, tmp); - m_cinfo.m_restart_interval = tmp; - - return true; - } - - /// - /// Like next_marker, but used to obtain the initial SOI marker. - /// For this marker, we do not allow preceding garbage or fill; otherwise, - /// we might well scan an entire input file before realizing it ain't JPEG. - /// If an application wants to process non-JFIF files, it must seek to the - /// SOI before calling the JPEG library. - /// - private bool first_marker() - { - int c; - if (!m_cinfo.m_src.GetByte(out c)) - return false; - - int c2; - if (!m_cinfo.m_src.GetByte(out c2)) - return false; - - if (c != 0xFF || c2 != (int)JPEG_MARKER.SOI) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NO_SOI, c, c2); - - m_cinfo.m_unread_marker = c2; - return true; - } - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_marker_writer.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_marker_writer.cs deleted file mode 100644 index 623e1c5dd..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_marker_writer.cs +++ /dev/null @@ -1,515 +0,0 @@ -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -/* - * This file contains routines to write JPEG datastream markers. - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// Marker writing - /// - class jpeg_marker_writer - { - private jpeg_compress_struct m_cinfo; - private int m_last_restart_interval; /* last DRI value emitted; 0 after SOI */ - - public jpeg_marker_writer(jpeg_compress_struct cinfo) - { - m_cinfo = cinfo; - } - - /// - /// Write datastream header. - /// This consists of an SOI and optional APPn markers. - /// We recommend use of the JFIF marker, but not the Adobe marker, - /// when using YCbCr or grayscale data. The JFIF marker should NOT - /// be used for any other JPEG colorspace. The Adobe marker is helpful - /// to distinguish RGB, CMYK, and YCCK colorspaces. - /// Note that an application can write additional header markers after - /// jpeg_start_compress returns. - /// - public void write_file_header() - { - emit_marker(JPEG_MARKER.SOI); /* first the SOI */ - - /* SOI is defined to reset restart interval to 0 */ - m_last_restart_interval = 0; - - if (m_cinfo.m_write_JFIF_header) /* next an optional JFIF APP0 */ - emit_jfif_app0(); - if (m_cinfo.m_write_Adobe_marker) /* next an optional Adobe APP14 */ - emit_adobe_app14(); - } - - /// - /// Write frame header. - /// This consists of DQT and SOFn markers. - /// Note that we do not emit the SOF until we have emitted the DQT(s). - /// This avoids compatibility problems with incorrect implementations that - /// try to error-check the quant table numbers as soon as they see the SOF. - /// - public void write_frame_header() - { - /* Emit DQT for each quantization table. - * Note that emit_dqt() suppresses any duplicate tables. - */ - int prec = 0; - for (int ci = 0; ci < m_cinfo.m_num_components; ci++) - prec += emit_dqt(m_cinfo.Component_info[ci].Quant_tbl_no); - - /* now prec is nonzero iff there are any 16-bit quant tables. */ - - /* Check for a non-baseline specification. - * Note we assume that Huffman table numbers won't be changed later. - */ - bool is_baseline; - if (m_cinfo.m_progressive_mode || m_cinfo.m_data_precision != 8) - { - is_baseline = false; - } - else - { - is_baseline = true; - for (int ci = 0; ci < m_cinfo.m_num_components; ci++) - { - if (m_cinfo.Component_info[ci].Dc_tbl_no > 1 || m_cinfo.Component_info[ci].Ac_tbl_no > 1) - is_baseline = false; - } - - if (prec != 0 && is_baseline) - { - is_baseline = false; - /* If it's baseline except for quantizer size, warn the user */ - m_cinfo.TRACEMS(0, J_MESSAGE_CODE.JTRC_16BIT_TABLES); - } - } - - /* Emit the proper SOF marker */ - if (m_cinfo.m_progressive_mode) - emit_sof(JPEG_MARKER.SOF2); /* SOF code for progressive Huffman */ - else if (is_baseline) - emit_sof(JPEG_MARKER.SOF0); /* SOF code for baseline implementation */ - else - emit_sof(JPEG_MARKER.SOF1); /* SOF code for non-baseline Huffman file */ - } - - /// - /// Write scan header. - /// This consists of DHT or DAC markers, optional DRI, and SOS. - /// Compressed data will be written following the SOS. - /// - public void write_scan_header() - { - /* Emit Huffman tables. - * Note that emit_dht() suppresses any duplicate tables. - */ - for (int i = 0; i < m_cinfo.m_comps_in_scan; i++) - { - int ac_tbl_no = m_cinfo.Component_info[m_cinfo.m_cur_comp_info[i]].Ac_tbl_no; - int dc_tbl_no = m_cinfo.Component_info[m_cinfo.m_cur_comp_info[i]].Dc_tbl_no; - if (m_cinfo.m_progressive_mode) - { - /* Progressive mode: only DC or only AC tables are used in one scan */ - if (m_cinfo.m_Ss == 0) - { - if (m_cinfo.m_Ah == 0) - { - /* DC needs no table for refinement scan */ - emit_dht(dc_tbl_no, false); - } - } - else - { - emit_dht(ac_tbl_no, true); - } - } - else - { - /* Sequential mode: need both DC and AC tables */ - emit_dht(dc_tbl_no, false); - emit_dht(ac_tbl_no, true); - } - } - - /* Emit DRI if required --- note that DRI value could change for each scan. - * We avoid wasting space with unnecessary DRIs, however. - */ - if (m_cinfo.m_restart_interval != m_last_restart_interval) - { - emit_dri(); - m_last_restart_interval = m_cinfo.m_restart_interval; - } - - emit_sos(); - } - - /// - /// Write datastream trailer. - /// - public void write_file_trailer() - { - emit_marker(JPEG_MARKER.EOI); - } - - /// - /// Write an abbreviated table-specification datastream. - /// This consists of SOI, DQT and DHT tables, and EOI. - /// Any table that is defined and not marked sent_table = true will be - /// emitted. Note that all tables will be marked sent_table = true at exit. - /// - public void write_tables_only() - { - emit_marker(JPEG_MARKER.SOI); - - for (int i = 0; i < JpegConstants.NUM_QUANT_TBLS; i++) - { - if (m_cinfo.m_quant_tbl_ptrs[i] != null) - emit_dqt(i); - } - - for (int i = 0; i < JpegConstants.NUM_HUFF_TBLS; i++) - { - if (m_cinfo.m_dc_huff_tbl_ptrs[i] != null) - emit_dht(i, false); - if (m_cinfo.m_ac_huff_tbl_ptrs[i] != null) - emit_dht(i, true); - } - - emit_marker(JPEG_MARKER.EOI); - } - - ////////////////////////////////////////////////////////////////////////// - // These routines allow writing an arbitrary marker with parameters. - // The only intended use is to emit COM or APPn markers after calling - // write_file_header and before calling write_frame_header. - // Other uses are not guaranteed to produce desirable results. - // Counting the parameter bytes properly is the caller's responsibility. - - /// - /// Emit an arbitrary marker header - /// - public void write_marker_header(int marker, int datalen) - { - if (datalen > 65533) /* safety check */ - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_LENGTH); - - emit_marker((JPEG_MARKER) marker); - - emit_2bytes(datalen + 2); /* total length */ - } - - /// - /// Emit one byte of marker parameters following write_marker_header - /// - public void write_marker_byte(byte val) - { - emit_byte(val); - } - - ////////////////////////////////////////////////////////////////////////// - // Routines to write specific marker types. - // - - /// - /// Emit a SOS marker - /// - private void emit_sos() - { - emit_marker(JPEG_MARKER.SOS); - - emit_2bytes(2 * m_cinfo.m_comps_in_scan + 2 + 1 + 3); /* length */ - - emit_byte(m_cinfo.m_comps_in_scan); - - for (int i = 0; i < m_cinfo.m_comps_in_scan; i++) - { - int componentIndex = m_cinfo.m_cur_comp_info[i]; - emit_byte(m_cinfo.Component_info[componentIndex].Component_id); - - int td = m_cinfo.Component_info[componentIndex].Dc_tbl_no; - int ta = m_cinfo.Component_info[componentIndex].Ac_tbl_no; - if (m_cinfo.m_progressive_mode) - { - /* Progressive mode: only DC or only AC tables are used in one scan; - * furthermore, Huffman coding of DC refinement uses no table at all. - * We emit 0 for unused field(s); this is recommended by the P&M text - * but does not seem to be specified in the standard. - */ - if (m_cinfo.m_Ss == 0) - { - /* DC scan */ - ta = 0; - if (m_cinfo.m_Ah != 0) - { - /* no DC table either */ - td = 0; - } - } - else - { - /* AC scan */ - td = 0; - } - } - - emit_byte((td << 4) + ta); - } - - emit_byte(m_cinfo.m_Ss); - emit_byte(m_cinfo.m_Se); - emit_byte((m_cinfo.m_Ah << 4) + m_cinfo.m_Al); - } - - /// - /// Emit a SOF marker - /// - private void emit_sof(JPEG_MARKER code) - { - emit_marker(code); - - emit_2bytes(3 * m_cinfo.m_num_components + 2 + 5 + 1); /* length */ - - /* Make sure image isn't bigger than SOF field can handle */ - if (m_cinfo.m_image_height > 65535 || m_cinfo.m_image_width > 65535) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_IMAGE_TOO_BIG, 65535); - - emit_byte(m_cinfo.m_data_precision); - emit_2bytes(m_cinfo.m_image_height); - emit_2bytes(m_cinfo.m_image_width); - - emit_byte(m_cinfo.m_num_components); - - for (int ci = 0; ci < m_cinfo.m_num_components; ci++) - { - jpeg_component_info componentInfo = m_cinfo.Component_info[ci]; - emit_byte(componentInfo.Component_id); - emit_byte((componentInfo.H_samp_factor << 4) + componentInfo.V_samp_factor); - emit_byte(componentInfo.Quant_tbl_no); - } - } - - /// - /// Emit an Adobe APP14 marker - /// - private void emit_adobe_app14() - { - /* - * Length of APP14 block (2 bytes) - * Block ID (5 bytes - ASCII "Adobe") - * Version Number (2 bytes - currently 100) - * Flags0 (2 bytes - currently 0) - * Flags1 (2 bytes - currently 0) - * Color transform (1 byte) - * - * Although Adobe TN 5116 mentions Version = 101, all the Adobe files - * now in circulation seem to use Version = 100, so that's what we write. - * - * We write the color transform byte as 1 if the JPEG color space is - * YCbCr, 2 if it's YCCK, 0 otherwise. Adobe's definition has to do with - * whether the encoder performed a transformation, which is pretty useless. - */ - - emit_marker(JPEG_MARKER.APP14); - - emit_2bytes(2 + 5 + 2 + 2 + 2 + 1); /* length */ - - emit_byte(0x41); /* Identifier: ASCII "Adobe" */ - emit_byte(0x64); - emit_byte(0x6F); - emit_byte(0x62); - emit_byte(0x65); - emit_2bytes(100); /* Version */ - emit_2bytes(0); /* Flags0 */ - emit_2bytes(0); /* Flags1 */ - switch (m_cinfo.m_jpeg_color_space) - { - case J_COLOR_SPACE.JCS_YCbCr: - emit_byte(1); /* Color transform = 1 */ - break; - case J_COLOR_SPACE.JCS_YCCK: - emit_byte(2); /* Color transform = 2 */ - break; - default: - emit_byte(0); /* Color transform = 0 */ - break; - } - } - - /// - /// Emit a DRI marker - /// - private void emit_dri() - { - emit_marker(JPEG_MARKER.DRI); - - emit_2bytes(4); /* fixed length */ - - emit_2bytes(m_cinfo.m_restart_interval); - } - - /// - /// Emit a DHT marker - /// - private void emit_dht(int index, bool is_ac) - { - JHUFF_TBL htbl = m_cinfo.m_dc_huff_tbl_ptrs[index]; - if (is_ac) - { - htbl = m_cinfo.m_ac_huff_tbl_ptrs[index]; - index += 0x10; /* output index has AC bit set */ - } - - if (htbl == null) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NO_HUFF_TABLE, index); - - if (!htbl.Sent_table) - { - emit_marker(JPEG_MARKER.DHT); - - int length = 0; - for (int i = 1; i <= 16; i++) - length += htbl.Bits[i]; - - emit_2bytes(length + 2 + 1 + 16); - emit_byte(index); - - for (int i = 1; i <= 16; i++) - emit_byte(htbl.Bits[i]); - - for (int i = 0; i < length; i++) - emit_byte(htbl.Huffval[i]); - - htbl.Sent_table = true; - } - } - - /// - /// Emit a DQT marker - /// - /// The index. - /// the precision used (0 = 8bits, 1 = 16bits) for baseline checking - private int emit_dqt(int index) - { - JQUANT_TBL qtbl = m_cinfo.m_quant_tbl_ptrs[index]; - if (qtbl == null) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NO_QUANT_TABLE, index); - - int prec = 0; - for (int i = 0; i < JpegConstants.DCTSIZE2; i++) - { - if (qtbl.quantval[i] > 255) - prec = 1; - } - - if (!qtbl.Sent_table) - { - emit_marker(JPEG_MARKER.DQT); - - emit_2bytes(prec != 0 ? JpegConstants.DCTSIZE2 * 2 + 1 + 2 : JpegConstants.DCTSIZE2 + 1 + 2); - - emit_byte(index + (prec << 4)); - - for (int i = 0; i < JpegConstants.DCTSIZE2; i++) - { - /* The table entries must be emitted in zigzag order. */ - int qval = qtbl.quantval[JpegUtils.jpeg_natural_order[i]]; - - if (prec != 0) - emit_byte(qval >> 8); - - emit_byte(qval & 0xFF); - } - - qtbl.Sent_table = true; - } - - return prec; - } - - /// - /// Emit a JFIF-compliant APP0 marker - /// - private void emit_jfif_app0() - { - /* - * Length of APP0 block (2 bytes) - * Block ID (4 bytes - ASCII "JFIF") - * Zero byte (1 byte to terminate the ID string) - * Version Major, Minor (2 bytes - major first) - * Units (1 byte - 0x00 = none, 0x01 = inch, 0x02 = cm) - * Xdpu (2 bytes - dots per unit horizontal) - * Ydpu (2 bytes - dots per unit vertical) - * Thumbnail X size (1 byte) - * Thumbnail Y size (1 byte) - */ - - emit_marker(JPEG_MARKER.APP0); - - emit_2bytes(2 + 4 + 1 + 2 + 1 + 2 + 2 + 1 + 1); /* length */ - - emit_byte(0x4A); /* Identifier: ASCII "JFIF" */ - emit_byte(0x46); - emit_byte(0x49); - emit_byte(0x46); - emit_byte(0); - emit_byte(m_cinfo.m_JFIF_major_version); /* Version fields */ - emit_byte(m_cinfo.m_JFIF_minor_version); - emit_byte((int)m_cinfo.m_density_unit); /* Pixel size information */ - emit_2bytes(m_cinfo.m_X_density); - emit_2bytes(m_cinfo.m_Y_density); - emit_byte(0); /* No thumbnail image */ - emit_byte(0); - } - - ////////////////////////////////////////////////////////////////////////// - // Basic output routines. - // - // Note that we do not support suspension while writing a marker. - // Therefore, an application using suspension must ensure that there is - // enough buffer space for the initial markers (typ. 600-700 bytes) before - // calling jpeg_start_compress, and enough space to write the trailing EOI - // (a few bytes) before calling jpeg_finish_compress. Multipass compression - // modes are not supported at all with suspension, so those two are the only - // points where markers will be written. - - - /// - /// Emit a marker code - /// - private void emit_marker(JPEG_MARKER mark) - { - emit_byte(0xFF); - emit_byte((int)mark); - } - - /// - /// Emit a 2-byte integer; these are always MSB first in JPEG files - /// - private void emit_2bytes(int value) - { - emit_byte((value >> 8) & 0xFF); - emit_byte(value & 0xFF); - } - - /// - /// Emit a byte - /// - private void emit_byte(int val) - { - if (!m_cinfo.m_dest.emit_byte(val)) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CANT_SUSPEND); - } - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_scan_info.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_scan_info.cs deleted file mode 100644 index e4adfcc46..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_scan_info.cs +++ /dev/null @@ -1,28 +0,0 @@ -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// The script for encoding a multiple-scan file is an array of these: - /// - class jpeg_scan_info - { - public int comps_in_scan; /* number of components encoded in this scan */ - public int[] component_index = new int[JpegConstants.MAX_COMPS_IN_SCAN]; /* their SOF/comp_info[] indexes */ - public int Ss; - public int Se; /* progressive JPEG spectral selection parms */ - public int Ah; - public int Al; /* progressive JPEG successive approx. parms */ - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_upsampler.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_upsampler.cs deleted file mode 100644 index 721e0a89c..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/jpeg_upsampler.cs +++ /dev/null @@ -1,31 +0,0 @@ -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// Upsampling (note that upsampler must also call color converter) - /// - abstract class jpeg_upsampler - { - protected bool m_need_context_rows; /* true if need rows above & below */ - - public abstract void start_pass(); - public abstract void upsample(ComponentBuffer[] input_buf, ref int in_row_group_ctr, int in_row_groups_avail, byte[][] output_buf, ref int out_row_ctr, int out_rows_avail); - - public bool NeedContextRows() - { - return m_need_context_rows; - } - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/my_1pass_cquantizer.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/my_1pass_cquantizer.cs deleted file mode 100644 index 3831a2c74..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/my_1pass_cquantizer.cs +++ /dev/null @@ -1,854 +0,0 @@ -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -/* - * This file contains 1-pass color quantization (color mapping) routines. - * These routines provide mapping to a fixed color map using equally spaced - * color values. Optional Floyd-Steinberg or ordered dithering is available. - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// The main purpose of 1-pass quantization is to provide a fast, if not very - /// high quality, colormapped output capability. A 2-pass quantizer usually - /// gives better visual quality; however, for quantized grayscale output this - /// quantizer is perfectly adequate. Dithering is highly recommended with this - /// quantizer, though you can turn it off if you really want to. - /// - /// In 1-pass quantization the colormap must be chosen in advance of seeing the - /// image. We use a map consisting of all combinations of Ncolors[i] color - /// values for the i'th component. The Ncolors[] values are chosen so that - /// their product, the total number of colors, is no more than that requested. - /// (In most cases, the product will be somewhat less.) - /// - /// Since the colormap is orthogonal, the representative value for each color - /// component can be determined without considering the other components; - /// then these indexes can be combined into a colormap index by a standard - /// N-dimensional-array-subscript calculation. Most of the arithmetic involved - /// can be precalculated and stored in the lookup table colorindex[]. - /// colorindex[i][j] maps pixel value j in component i to the nearest - /// representative value (grid plane) for that component; this index is - /// multiplied by the array stride for component i, so that the - /// index of the colormap entry closest to a given pixel value is just - /// sum( colorindex[component-number][pixel-component-value] ) - /// Aside from being fast, this scheme allows for variable spacing between - /// representative values with no additional lookup cost. - /// - /// If gamma correction has been applied in color conversion, it might be wise - /// to adjust the color grid spacing so that the representative colors are - /// equidistant in linear space. At this writing, gamma correction is not - /// implemented, so nothing is done here. - /// - /// - /// Declarations for Floyd-Steinberg dithering. - /// - /// Errors are accumulated into the array fserrors[], at a resolution of - /// 1/16th of a pixel count. The error at a given pixel is propagated - /// to its not-yet-processed neighbors using the standard F-S fractions, - /// ... (here) 7/16 - /// 3/16 5/16 1/16 - /// We work left-to-right on even rows, right-to-left on odd rows. - /// - /// We can get away with a single array (holding one row's worth of errors) - /// by using it to store the current row's errors at pixel columns not yet - /// processed, but the next row's errors at columns already processed. We - /// need only a few extra variables to hold the errors immediately around the - /// current column. (If we are lucky, those variables are in registers, but - /// even if not, they're probably cheaper to access than array elements are.) - /// - /// The fserrors[] array is indexed [component#][position]. - /// We provide (#columns + 2) entries per component; the extra entry at each - /// end saves us from special-casing the first and last pixels. - /// - /// - /// Declarations for ordered dithering. - /// - /// We use a standard 16x16 ordered dither array. The basic concept of ordered - /// dithering is described in many references, for instance Dale Schumacher's - /// chapter II.2 of Graphics Gems II (James Arvo, ed. Academic Press, 1991). - /// In place of Schumacher's comparisons against a "threshold" value, we add a - /// "dither" value to the input pixel and then round the result to the nearest - /// output value. The dither value is equivalent to (0.5 - threshold) times - /// the distance between output values. For ordered dithering, we assume that - /// the output colors are equally spaced; if not, results will probably be - /// worse, since the dither may be too much or too little at a given point. - /// - /// The normal calculation would be to form pixel value + dither, range-limit - /// this to 0..MAXJSAMPLE, and then index into the colorindex table as usual. - /// We can skip the separate range-limiting step by extending the colorindex - /// table in both directions. - /// - class my_1pass_cquantizer : jpeg_color_quantizer - { - private enum QuantizerType - { - color_quantizer3, - color_quantizer, - quantize3_ord_dither_quantizer, - quantize_ord_dither_quantizer, - quantize_fs_dither_quantizer - } - - private static int[] RGB_order = { JpegConstants.RGB_GREEN, JpegConstants.RGB_RED, JpegConstants.RGB_BLUE }; - private const int MAX_Q_COMPS = 4; /* max components I can handle */ - - private const int ODITHER_SIZE = 16; /* dimension of dither matrix */ - - /* NB: if ODITHER_SIZE is not a power of 2, ODITHER_MASK uses will break */ - private const int ODITHER_CELLS = (ODITHER_SIZE * ODITHER_SIZE); /* # cells in matrix */ - private const int ODITHER_MASK = (ODITHER_SIZE-1); /* mask for wrapping around counters */ - - /* Bayer's order-4 dither array. Generated by the code given in - * Stephen Hawley's article "Ordered Dithering" in Graphics Gems I. - * The values in this array must range from 0 to ODITHER_CELLS-1. - */ - private static byte[][] base_dither_matrix = new byte[][] - { - new byte[] { 0,192, 48,240, 12,204, 60,252, 3,195, 51,243, 15,207, 63,255 }, - new byte[] { 128, 64,176,112,140, 76,188,124,131, 67,179,115,143, 79,191,127 }, - new byte[] { 32,224, 16,208, 44,236, 28,220, 35,227, 19,211, 47,239, 31,223 }, - new byte[] { 160, 96,144, 80,172,108,156, 92,163, 99,147, 83,175,111,159, 95 }, - new byte[] { 8,200, 56,248, 4,196, 52,244, 11,203, 59,251, 7,199, 55,247 }, - new byte[] { 136, 72,184,120,132, 68,180,116,139, 75,187,123,135, 71,183,119 }, - new byte[] { 40,232, 24,216, 36,228, 20,212, 43,235, 27,219, 39,231, 23,215 }, - new byte[] { 168,104,152, 88,164,100,148, 84,171,107,155, 91,167,103,151, 87 }, - new byte[] { 2,194, 50,242, 14,206, 62,254, 1,193, 49,241, 13,205, 61,253 }, - new byte[] { 130, 66,178,114,142, 78,190,126,129, 65,177,113,141, 77,189,125 }, - new byte[] { 34,226, 18,210, 46,238, 30,222, 33,225, 17,209, 45,237, 29,221 }, - new byte[] { 162, 98,146, 82,174,110,158, 94,161, 97,145, 81,173,109,157, 93 }, - new byte[] { 10,202, 58,250, 6,198, 54,246, 9,201, 57,249, 5,197, 53,245 }, - new byte[] { 138, 74,186,122,134, 70,182,118,137, 73,185,121,133, 69,181,117 }, - new byte[] { 42,234, 26,218, 38,230, 22,214, 41,233, 25,217, 37,229, 21,213 }, - new byte[] { 170,106,154, 90,166,102,150, 86,169,105,153, 89,165,101,149, 85 } - }; - - private QuantizerType m_quantizer; - - private jpeg_decompress_struct m_cinfo; - - /* Initially allocated colormap is saved here */ - private byte[][] m_sv_colormap; /* The color map as a 2-D pixel array */ - private int m_sv_actual; /* number of entries in use */ - - private byte[][] m_colorindex; /* Precomputed mapping for speed */ - private int[] m_colorindexOffset; - - /* colorindex[i][j] = index of color closest to pixel value j in component i, - * premultiplied as described above. Since colormap indexes must fit into - * bytes, the entries of this array will too. - */ - private bool m_is_padded; /* is the colorindex padded for odither? */ - - private int[] m_Ncolors = new int[MAX_Q_COMPS]; /* # of values alloced to each component */ - - /* Variables for ordered dithering */ - private int m_row_index; /* cur row's vertical index in dither matrix */ - private int[][][] m_odither = new int[MAX_Q_COMPS][][]; /* one dither array per component */ - - /* Variables for Floyd-Steinberg dithering */ - private short[][] m_fserrors = new short[MAX_Q_COMPS][]; /* accumulated errors */ - private bool m_on_odd_row; /* flag to remember which row we are on */ - - /// - /// Module initialization routine for 1-pass color quantization. - /// - /// The cinfo. - public my_1pass_cquantizer(jpeg_decompress_struct cinfo) - { - m_cinfo = cinfo; - - m_fserrors[0] = null; /* Flag FS workspace not allocated */ - m_odither[0] = null; /* Also flag odither arrays not allocated */ - - /* Make sure my internal arrays won't overflow */ - if (cinfo.m_out_color_components > MAX_Q_COMPS) - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_QUANT_COMPONENTS, MAX_Q_COMPS); - - /* Make sure colormap indexes can be represented by JSAMPLEs */ - if (cinfo.m_desired_number_of_colors > (JpegConstants.MAXJSAMPLE + 1)) - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_QUANT_MANY_COLORS, JpegConstants.MAXJSAMPLE + 1); - - /* Create the colormap and color index table. */ - create_colormap(); - create_colorindex(); - - /* Allocate Floyd-Steinberg workspace now if requested. - * We do this now since it is FAR storage and may affect the memory - * manager's space calculations. If the user changes to FS dither - * mode in a later pass, we will allocate the space then, and will - * possibly overrun the max_memory_to_use setting. - */ - if (cinfo.m_dither_mode == J_DITHER_MODE.JDITHER_FS) - alloc_fs_workspace(); - } - - /// - /// Initialize for one-pass color quantization. - /// - public virtual void start_pass(bool is_pre_scan) - { - /* Install my colormap. */ - m_cinfo.m_colormap = m_sv_colormap; - m_cinfo.m_actual_number_of_colors = m_sv_actual; - - /* Initialize for desired dithering mode. */ - switch (m_cinfo.m_dither_mode) - { - case J_DITHER_MODE.JDITHER_NONE: - if (m_cinfo.m_out_color_components == 3) - m_quantizer = QuantizerType.color_quantizer3; - else - m_quantizer = QuantizerType.color_quantizer; - - break; - case J_DITHER_MODE.JDITHER_ORDERED: - if (m_cinfo.m_out_color_components == 3) - m_quantizer = QuantizerType.quantize3_ord_dither_quantizer; - else - m_quantizer = QuantizerType.quantize3_ord_dither_quantizer; - - /* initialize state for ordered dither */ - m_row_index = 0; - - /* If user changed to ordered dither from another mode, - * we must recreate the color index table with padding. - * This will cost extra space, but probably isn't very likely. - */ - if (!m_is_padded) - create_colorindex(); - - /* Create ordered-dither tables if we didn't already. */ - if (m_odither[0] == null) - create_odither_tables(); - - break; - case J_DITHER_MODE.JDITHER_FS: - m_quantizer = QuantizerType.quantize_fs_dither_quantizer; - - /* initialize state for F-S dither */ - m_on_odd_row = false; - - /* Allocate Floyd-Steinberg workspace if didn't already. */ - if (m_fserrors[0] == null) - alloc_fs_workspace(); - - /* Initialize the propagated errors to zero. */ - int arraysize = m_cinfo.m_output_width + 2; - for (int i = 0; i < m_cinfo.m_out_color_components; i++) - Array.Clear(m_fserrors[i], 0, arraysize); - - break; - default: - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NOT_COMPILED); - break; - } - } - - public virtual void color_quantize(byte[][] input_buf, int in_row, byte[][] output_buf, int out_row, int num_rows) - { - switch (m_quantizer) - { - case QuantizerType.color_quantizer3: - quantize3(input_buf, in_row, output_buf, out_row, num_rows); - break; - case QuantizerType.color_quantizer: - quantize(input_buf, in_row, output_buf, out_row, num_rows); - break; - case QuantizerType.quantize3_ord_dither_quantizer: - quantize3_ord_dither(input_buf, in_row, output_buf, out_row, num_rows); - break; - case QuantizerType.quantize_ord_dither_quantizer: - quantize_ord_dither(input_buf, in_row, output_buf, out_row, num_rows); - break; - case QuantizerType.quantize_fs_dither_quantizer: - quantize_fs_dither(input_buf, in_row, output_buf, out_row, num_rows); - break; - default: - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NOTIMPL); - break; - } - } - - /// - /// Finish up at the end of the pass. - /// - public virtual void finish_pass() - { - /* no work in 1-pass case */ - } - - /// - /// Switch to a new external colormap between output passes. - /// Shouldn't get to this! - /// - public virtual void new_color_map() - { - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_MODE_CHANGE); - } - - /// - /// Map some rows of pixels to the output colormapped representation. - /// General case, no dithering. - /// - private void quantize(byte[][] input_buf, int in_row, byte[][] output_buf, int out_row, int num_rows) - { - int nc = m_cinfo.m_out_color_components; - - for (int row = 0; row < num_rows; row++) - { - int inIndex = 0; - int inRow = in_row + row; - - int outIndex = 0; - int outRow = out_row + row; - - for (int col = m_cinfo.m_output_width; col > 0; col--) - { - int pixcode = 0; - for (int ci = 0; ci < nc; ci++) - { - pixcode += m_colorindex[ci][m_colorindexOffset[ci] + input_buf[inRow][inIndex]]; - inIndex++; - } - - output_buf[outRow][outIndex] = (byte)pixcode; - outIndex++; - } - } - } - - /// - /// Map some rows of pixels to the output colormapped representation. - /// Fast path for out_color_components==3, no dithering - /// - private void quantize3(byte[][] input_buf, int in_row, byte[][] output_buf, int out_row, int num_rows) - { - int width = m_cinfo.m_output_width; - - for (int row = 0; row < num_rows; row++) - { - int inIndex = 0; - int inRow = in_row + row; - - int outIndex = 0; - int outRow = out_row + row; - - for (int col = width; col > 0; col--) - { - int pixcode = m_colorindex[0][m_colorindexOffset[0] + input_buf[inRow][inIndex]]; - inIndex++; - - pixcode += m_colorindex[1][m_colorindexOffset[1] + input_buf[inRow][inIndex]]; - inIndex++; - - pixcode += m_colorindex[2][m_colorindexOffset[2] + input_buf[inRow][inIndex]]; - inIndex++; - - output_buf[outRow][outIndex] = (byte)pixcode; - outIndex++; - } - } - } - - /// - /// Map some rows of pixels to the output colormapped representation. - /// General case, with ordered dithering. - /// - private void quantize_ord_dither(byte[][] input_buf, int in_row, byte[][] output_buf, int out_row, int num_rows) - { - int nc = m_cinfo.m_out_color_components; - int width = m_cinfo.m_output_width; - - for (int row = 0; row < num_rows; row++) - { - /* Initialize output values to 0 so can process components separately */ - Array.Clear(output_buf[out_row + row], 0, width); - - int row_index = m_row_index; - for (int ci = 0; ci < nc; ci++) - { - int inputIndex = ci; - int outIndex = 0; - int outRow = out_row + row; - - int col_index = 0; - for (int col = width; col > 0; col--) - { - /* Form pixel value + dither, range-limit to 0..MAXJSAMPLE, - * select output value, accumulate into output code for this pixel. - * Range-limiting need not be done explicitly, as we have extended - * the colorindex table to produce the right answers for out-of-range - * inputs. The maximum dither is +- MAXJSAMPLE; this sets the - * required amount of padding. - */ - output_buf[outRow][outIndex] += m_colorindex[ci][m_colorindexOffset[ci] + input_buf[in_row + row][inputIndex] + m_odither[ci][row_index][col_index]]; - inputIndex += nc; - outIndex++; - col_index = (col_index + 1) & ODITHER_MASK; - } - } - - /* Advance row index for next row */ - row_index = (row_index + 1) & ODITHER_MASK; - m_row_index = row_index; - } - } - - /// - /// Map some rows of pixels to the output colormapped representation. - /// Fast path for out_color_components==3, with ordered dithering - /// - private void quantize3_ord_dither(byte[][] input_buf, int in_row, byte[][] output_buf, int out_row, int num_rows) - { - int width = m_cinfo.m_output_width; - - for (int row = 0; row < num_rows; row++) - { - int row_index = m_row_index; - int inRow = in_row + row; - int inIndex = 0; - - int outIndex = 0; - int outRow = out_row + row; - - int col_index = 0; - for (int col = width; col > 0; col--) - { - int pixcode = m_colorindex[0][m_colorindexOffset[0] + input_buf[inRow][inIndex] + m_odither[0][row_index][col_index]]; - inIndex++; - - pixcode += m_colorindex[1][m_colorindexOffset[1] + input_buf[inRow][inIndex] + m_odither[1][row_index][col_index]]; - inIndex++; - - pixcode += m_colorindex[2][m_colorindexOffset[2] + input_buf[inRow][inIndex] + m_odither[2][row_index][col_index]]; - inIndex++; - - output_buf[outRow][outIndex] = (byte)pixcode; - outIndex++; - - col_index = (col_index + 1) & ODITHER_MASK; - } - - row_index = (row_index + 1) & ODITHER_MASK; - m_row_index = row_index; - } - } - - /// - /// Map some rows of pixels to the output colormapped representation. - /// General case, with Floyd-Steinberg dithering - /// - private void quantize_fs_dither(byte[][] input_buf, int in_row, byte[][] output_buf, int out_row, int num_rows) - { - int nc = m_cinfo.m_out_color_components; - int width = m_cinfo.m_output_width; - - byte[] limit = m_cinfo.m_sample_range_limit; - int limitOffset = m_cinfo.m_sampleRangeLimitOffset; - - for (int row = 0; row < num_rows; row++) - { - /* Initialize output values to 0 so can process components separately */ - Array.Clear(output_buf[out_row + row], 0, width); - - for (int ci = 0; ci < nc; ci++) - { - int inRow = in_row + row; - int inIndex = ci; - - int outIndex = 0; - int outRow = out_row + row; - - int errorIndex = 0; - int dir; /* 1 for left-to-right, -1 for right-to-left */ - if (m_on_odd_row) - { - /* work right to left in this row */ - inIndex += (width - 1) * nc; /* so point to rightmost pixel */ - outIndex += width - 1; - dir = -1; - errorIndex = width + 1; /* => entry after last column */ - } - else - { - /* work left to right in this row */ - dir = 1; - errorIndex = 0; /* => entry before first column */ - } - int dirnc = dir * nc; - - /* Preset error values: no error propagated to first pixel from left */ - int cur = 0; - /* and no error propagated to row below yet */ - int belowerr = 0; - int bpreverr = 0; - - for (int col = width; col > 0; col--) - { - /* cur holds the error propagated from the previous pixel on the - * current line. Add the error propagated from the previous line - * to form the complete error correction term for this pixel, and - * round the error term (which is expressed * 16) to an integer. - * RIGHT_SHIFT rounds towards minus infinity, so adding 8 is correct - * for either sign of the error value. - * Note: errorIndex is for *previous* column's array entry. - */ - cur = JpegUtils.RIGHT_SHIFT(cur + m_fserrors[ci][errorIndex + dir] + 8, 4); - - /* Form pixel value + error, and range-limit to 0..MAXJSAMPLE. - * The maximum error is +- MAXJSAMPLE; this sets the required size - * of the range_limit array. - */ - cur += input_buf[inRow][inIndex]; - cur = limit[limitOffset + cur]; - - /* Select output value, accumulate into output code for this pixel */ - int pixcode = m_colorindex[ci][m_colorindexOffset[ci] + cur]; - output_buf[outRow][outIndex] += (byte)pixcode; - - /* Compute actual representation error at this pixel */ - /* Note: we can do this even though we don't have the final */ - /* pixel code, because the colormap is orthogonal. */ - cur -= m_sv_colormap[ci][pixcode]; - - /* Compute error fractions to be propagated to adjacent pixels. - * Add these into the running sums, and simultaneously shift the - * next-line error sums left by 1 column. - */ - int bnexterr = cur; - int delta = cur * 2; - cur += delta; /* form error * 3 */ - m_fserrors[ci][errorIndex + 0] = (short) (bpreverr + cur); - cur += delta; /* form error * 5 */ - bpreverr = belowerr + cur; - belowerr = bnexterr; - cur += delta; /* form error * 7 */ - - /* At this point cur contains the 7/16 error value to be propagated - * to the next pixel on the current line, and all the errors for the - * next line have been shifted over. We are therefore ready to move on. - */ - inIndex += dirnc; /* advance input to next column */ - outIndex += dir; /* advance output to next column */ - errorIndex += dir; /* advance errorIndex to current column */ - } - - /* Post-loop cleanup: we must unload the final error value into the - * final fserrors[] entry. Note we need not unload belowerr because - * it is for the dummy column before or after the actual array. - */ - m_fserrors[ci][errorIndex + 0] = (short) bpreverr; /* unload prev err into array */ - } - - m_on_odd_row = (m_on_odd_row ? false : true); - } - } - - /// - /// Create the colormap. - /// - private void create_colormap() - { - /* Select number of colors for each component */ - int total_colors = select_ncolors(m_Ncolors); - - /* Report selected color counts */ - if (m_cinfo.m_out_color_components == 3) - m_cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_QUANT_3_NCOLORS, total_colors, m_Ncolors[0], m_Ncolors[1], m_Ncolors[2]); - else - m_cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_QUANT_NCOLORS, total_colors); - - /* Allocate and fill in the colormap. */ - /* The colors are ordered in the map in standard row-major order, */ - /* i.e. rightmost (highest-indexed) color changes most rapidly. */ - byte[][] colormap = jpeg_common_struct.AllocJpegSamples(total_colors, m_cinfo.m_out_color_components); - - /* blksize is number of adjacent repeated entries for a component */ - /* blkdist is distance between groups of identical entries for a component */ - int blkdist = total_colors; - for (int i = 0; i < m_cinfo.m_out_color_components; i++) - { - /* fill in colormap entries for i'th color component */ - int nci = m_Ncolors[i]; /* # of distinct values for this color */ - int blksize = blkdist / nci; - for (int j = 0; j < nci; j++) - { - /* Compute j'th output value (out of nci) for component */ - int val = output_value(j, nci - 1); - - /* Fill in all colormap entries that have this value of this component */ - for (int ptr = j * blksize; ptr < total_colors; ptr += blkdist) - { - /* fill in blksize entries beginning at ptr */ - for (int k = 0; k < blksize; k++) - colormap[i][ptr + k] = (byte)val; - } - } - - /* blksize of this color is blkdist of next */ - blkdist = blksize; - } - - /* Save the colormap in private storage, - * where it will survive color quantization mode changes. - */ - m_sv_colormap = colormap; - m_sv_actual = total_colors; - } - - /// - /// Create the color index table. - /// - private void create_colorindex() - { - /* For ordered dither, we pad the color index tables by MAXJSAMPLE in - * each direction (input index values can be -MAXJSAMPLE .. 2*MAXJSAMPLE). - * This is not necessary in the other dithering modes. However, we - * flag whether it was done in case user changes dithering mode. - */ - int pad; - if (m_cinfo.m_dither_mode == J_DITHER_MODE.JDITHER_ORDERED) - { - pad = JpegConstants.MAXJSAMPLE * 2; - m_is_padded = true; - } - else - { - pad = 0; - m_is_padded = false; - } - - m_colorindex = jpeg_common_struct.AllocJpegSamples(JpegConstants.MAXJSAMPLE + 1 + pad, m_cinfo.m_out_color_components); - m_colorindexOffset = new int[m_cinfo.m_out_color_components]; - - /* blksize is number of adjacent repeated entries for a component */ - int blksize = m_sv_actual; - for (int i = 0; i < m_cinfo.m_out_color_components; i++) - { - /* fill in colorindex entries for i'th color component */ - int nci = m_Ncolors[i]; /* # of distinct values for this color */ - blksize = blksize / nci; - - /* adjust colorindex pointers to provide padding at negative indexes. */ - if (pad != 0) - m_colorindexOffset[i] += JpegConstants.MAXJSAMPLE; - - /* in loop, val = index of current output value, */ - /* and k = largest j that maps to current val */ - int val = 0; - int k = largest_input_value(0, nci - 1); - for (int j = 0; j <= JpegConstants.MAXJSAMPLE; j++) - { - while (j > k) - { - /* advance val if past boundary */ - k = largest_input_value(++val, nci - 1); - } - - /* premultiply so that no multiplication needed in main processing */ - m_colorindex[i][m_colorindexOffset[i] + j] = (byte)(val * blksize); - } - - /* Pad at both ends if necessary */ - if (pad != 0) - { - for (int j = 1; j <= JpegConstants.MAXJSAMPLE; j++) - { - m_colorindex[i][m_colorindexOffset[i] + -j] = m_colorindex[i][m_colorindexOffset[i]]; - m_colorindex[i][m_colorindexOffset[i] + JpegConstants.MAXJSAMPLE + j] = m_colorindex[i][m_colorindexOffset[i] + JpegConstants.MAXJSAMPLE]; - } - } - } - } - - /// - /// Create the ordered-dither tables. - /// Components having the same number of representative colors may - /// share a dither table. - /// - private void create_odither_tables() - { - for (int i = 0; i < m_cinfo.m_out_color_components; i++) - { - int nci = m_Ncolors[i]; /* # of distinct values for this color */ - - /* search for matching prior component */ - int foundPos = -1; - for (int j = 0; j < i; j++) - { - if (nci == m_Ncolors[j]) - { - foundPos = j; - break; - } - } - - if (foundPos == -1) - { - /* need a new table? */ - m_odither[i] = make_odither_array(nci); - } - else - m_odither[i] = m_odither[foundPos]; - } - } - - /// - /// Allocate workspace for Floyd-Steinberg errors. - /// - private void alloc_fs_workspace() - { - for (int i = 0; i < m_cinfo.m_out_color_components; i++) - m_fserrors[i] = new short[m_cinfo.m_output_width + 2]; - } - - /* - * Policy-making subroutines for create_colormap and create_colorindex. - * These routines determine the colormap to be used. The rest of the module - * only assumes that the colormap is orthogonal. - * - * * select_ncolors decides how to divvy up the available colors - * among the components. - * * output_value defines the set of representative values for a component. - * * largest_input_value defines the mapping from input values to - * representative values for a component. - * Note that the latter two routines may impose different policies for - * different components, though this is not currently done. - */ - - /// - /// Return largest input value that should map to j'th output value - /// Must have largest(j=0) >= 0, and largest(j=maxj) >= MAXJSAMPLE - /// - private static int largest_input_value(int j, int maxj) - { - /* Breakpoints are halfway between values returned by output_value */ - return (int)(((2 * j + 1) * JpegConstants.MAXJSAMPLE + maxj) / (2 * maxj)); - } - - /// - /// Return j'th output value, where j will range from 0 to maxj - /// The output values must fall in 0..MAXJSAMPLE in increasing order - /// - private static int output_value(int j, int maxj) - { - /* We always provide values 0 and MAXJSAMPLE for each component; - * any additional values are equally spaced between these limits. - * (Forcing the upper and lower values to the limits ensures that - * dithering can't produce a color outside the selected gamut.) - */ - return (int)((j * JpegConstants.MAXJSAMPLE + maxj / 2) / maxj); - } - - /// - /// Determine allocation of desired colors to components, - /// and fill in Ncolors[] array to indicate choice. - /// Return value is total number of colors (product of Ncolors[] values). - /// - private int select_ncolors(int[] Ncolors) - { - int nc = m_cinfo.m_out_color_components; /* number of color components */ - int max_colors = m_cinfo.m_desired_number_of_colors; - - /* We can allocate at least the nc'th root of max_colors per component. */ - /* Compute floor(nc'th root of max_colors). */ - int iroot = 1; - long temp = 0; - do - { - iroot++; - temp = iroot; /* set temp = iroot ** nc */ - for (int i = 1; i < nc; i++) - temp *= iroot; - } - while (temp <= max_colors); /* repeat till iroot exceeds root */ - - /* now iroot = floor(root) */ - iroot--; - - /* Must have at least 2 color values per component */ - if (iroot < 2) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_QUANT_FEW_COLORS, (int)temp); - - /* Initialize to iroot color values for each component */ - int total_colors = 1; - for (int i = 0; i < nc; i++) - { - Ncolors[i] = iroot; - total_colors *= iroot; - } - - /* We may be able to increment the count for one or more components without - * exceeding max_colors, though we know not all can be incremented. - * Sometimes, the first component can be incremented more than once! - * (Example: for 16 colors, we start at 2*2*2, go to 3*2*2, then 4*2*2.) - * In RGB colorspace, try to increment G first, then R, then B. - */ - bool changed = false; - do - { - changed = false; - for (int i = 0; i < nc; i++) - { - int j = (m_cinfo.m_out_color_space == J_COLOR_SPACE.JCS_RGB ? RGB_order[i] : i); - /* calculate new total_colors if Ncolors[j] is incremented */ - temp = total_colors / Ncolors[j]; - temp *= Ncolors[j] + 1; /* done in long arith to avoid oflo */ - - if (temp > max_colors) - break; /* won't fit, done with this pass */ - - Ncolors[j]++; /* OK, apply the increment */ - total_colors = (int)temp; - changed = true; - } - } - while (changed); - - return total_colors; - } - - /// - /// Create an ordered-dither array for a component having ncolors - /// distinct output values. - /// - private static int[][] make_odither_array(int ncolors) - { - int[][] odither = new int[ODITHER_SIZE][]; - for (int i = 0; i < ODITHER_SIZE; i++) - odither[i] = new int[ODITHER_SIZE]; - - /* The inter-value distance for this color is MAXJSAMPLE/(ncolors-1). - * Hence the dither value for the matrix cell with fill order f - * (f=0..N-1) should be (N-1-2*f)/(2*N) * MAXJSAMPLE/(ncolors-1). - * On 16-bit-int machine, be careful to avoid overflow. - */ - int den = 2 * ODITHER_CELLS * (ncolors - 1); - for (int j = 0; j < ODITHER_SIZE; j++) - { - for (int k = 0; k < ODITHER_SIZE; k++) - { - int num = ((int)(ODITHER_CELLS - 1 - 2 * ((int)base_dither_matrix[j][k]))) * JpegConstants.MAXJSAMPLE; - - /* Ensure round towards zero despite C's lack of consistency - * about rounding negative values in integer division... - */ - odither[j][k] = num < 0 ? -((-num) / den) : num / den; - } - } - - return odither; - } - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/my_2pass_cquantizer.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/my_2pass_cquantizer.cs deleted file mode 100644 index 88512a872..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/my_2pass_cquantizer.cs +++ /dev/null @@ -1,1390 +0,0 @@ -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -/* - * This file contains 2-pass color quantization (color mapping) routines. - * These routines provide selection of a custom color map for an image, - * followed by mapping of the image to that color map, with optional - * Floyd-Steinberg dithering. - * It is also possible to use just the second pass to map to an arbitrary - * externally-given color map. - * - * Note: ordered dithering is not supported, since there isn't any fast - * way to compute intercolor distances; it's unclear that ordered dither's - * fundamental assumptions even hold with an irregularly spaced color map. - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// This module implements the well-known Heckbert paradigm for color - /// quantization. Most of the ideas used here can be traced back to - /// Heckbert's seminal paper - /// Heckbert, Paul. "Color Image Quantization for Frame Buffer Display", - /// Proc. SIGGRAPH '82, Computer Graphics v.16 #3 (July 1982), pp 297-304. - /// - /// In the first pass over the image, we accumulate a histogram showing the - /// usage count of each possible color. To keep the histogram to a reasonable - /// size, we reduce the precision of the input; typical practice is to retain - /// 5 or 6 bits per color, so that 8 or 4 different input values are counted - /// in the same histogram cell. - /// - /// Next, the color-selection step begins with a box representing the whole - /// color space, and repeatedly splits the "largest" remaining box until we - /// have as many boxes as desired colors. Then the mean color in each - /// remaining box becomes one of the possible output colors. - /// - /// The second pass over the image maps each input pixel to the closest output - /// color (optionally after applying a Floyd-Steinberg dithering correction). - /// This mapping is logically trivial, but making it go fast enough requires - /// considerable care. - /// - /// Heckbert-style quantizers vary a good deal in their policies for choosing - /// the "largest" box and deciding where to cut it. The particular policies - /// used here have proved out well in experimental comparisons, but better ones - /// may yet be found. - /// - /// In earlier versions of the IJG code, this module quantized in YCbCr color - /// space, processing the raw upsampled data without a color conversion step. - /// This allowed the color conversion math to be done only once per colormap - /// entry, not once per pixel. However, that optimization precluded other - /// useful optimizations (such as merging color conversion with upsampling) - /// and it also interfered with desired capabilities such as quantizing to an - /// externally-supplied colormap. We have therefore abandoned that approach. - /// The present code works in the post-conversion color space, typically RGB. - /// - /// To improve the visual quality of the results, we actually work in scaled - /// RGB space, giving G distances more weight than R, and R in turn more than - /// B. To do everything in integer math, we must use integer scale factors. - /// The 2/3/1 scale factors used here correspond loosely to the relative - /// weights of the colors in the NTSC grayscale equation. - /// If you want to use this code to quantize a non-RGB color space, you'll - /// probably need to change these scale factors. - /// - /// First we have the histogram data structure and routines for creating it. - /// - /// The number of bits of precision can be adjusted by changing these symbols. - /// We recommend keeping 6 bits for G and 5 each for R and B. - /// If you have plenty of memory and cycles, 6 bits all around gives marginally - /// better results; if you are short of memory, 5 bits all around will save - /// some space but degrade the results. - /// To maintain a fully accurate histogram, we'd need to allocate a "long" - /// (preferably unsigned long) for each cell. In practice this is overkill; - /// we can get by with 16 bits per cell. Few of the cell counts will overflow, - /// and clamping those that do overflow to the maximum value will give close- - /// enough results. This reduces the recommended histogram size from 256Kb - /// to 128Kb, which is a useful savings on PC-class machines. - /// (In the second pass the histogram space is re-used for pixel mapping data; - /// in that capacity, each cell must be able to store zero to the number of - /// desired colors. 16 bits/cell is plenty for that too.) - /// Since the JPEG code is intended to run in small memory model on 80x86 - /// machines, we can't just allocate the histogram in one chunk. Instead - /// of a true 3-D array, we use a row of pointers to 2-D arrays. Each - /// pointer corresponds to a C0 value (typically 2^5 = 32 pointers) and - /// each 2-D array has 2^6*2^5 = 2048 or 2^6*2^6 = 4096 entries. Note that - /// on 80x86 machines, the pointer row is in near memory but the actual - /// arrays are in far memory (same arrangement as we use for image arrays). - /// - /// - /// Declarations for Floyd-Steinberg dithering. - /// - /// Errors are accumulated into the array fserrors[], at a resolution of - /// 1/16th of a pixel count. The error at a given pixel is propagated - /// to its not-yet-processed neighbors using the standard F-S fractions, - /// ... (here) 7/16 - /// 3/16 5/16 1/16 - /// We work left-to-right on even rows, right-to-left on odd rows. - /// - /// We can get away with a single array (holding one row's worth of errors) - /// by using it to store the current row's errors at pixel columns not yet - /// processed, but the next row's errors at columns already processed. We - /// need only a few extra variables to hold the errors immediately around the - /// current column. (If we are lucky, those variables are in registers, but - /// even if not, they're probably cheaper to access than array elements are.) - /// - /// The fserrors[] array has (#columns + 2) entries; the extra entry at - /// each end saves us from special-casing the first and last pixels. - /// Each entry is three values long, one value for each color component. - /// - class my_2pass_cquantizer : jpeg_color_quantizer - { - private struct box - { - /* The bounds of the box (inclusive); expressed as histogram indexes */ - public int c0min; - public int c0max; - public int c1min; - public int c1max; - public int c2min; - public int c2max; - /* The volume (actually 2-norm) of the box */ - public int volume; - /* The number of nonzero histogram cells within this box */ - public long colorcount; - } - - private enum QuantizerType - { - prescan_quantizer, - pass2_fs_dither_quantizer, - pass2_no_dither_quantizer - } - - private const int MAXNUMCOLORS = (JpegConstants.MAXJSAMPLE+1); /* maximum size of colormap */ - - /* These will do the right thing for either R,G,B or B,G,R color order, - * but you may not like the results for other color orders. - */ - private const int HIST_C0_BITS = 5; /* bits of precision in R/B histogram */ - private const int HIST_C1_BITS = 6; /* bits of precision in G histogram */ - private const int HIST_C2_BITS = 5; /* bits of precision in B/R histogram */ - - /* Number of elements along histogram axes. */ - private const int HIST_C0_ELEMS = (1< - /// Module initialization routine for 2-pass color quantization. - ///
- public my_2pass_cquantizer(jpeg_decompress_struct cinfo) - { - m_cinfo = cinfo; - - /* Make sure jdmaster didn't give me a case I can't handle */ - if (cinfo.m_out_color_components != 3) - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NOTIMPL); - - /* Allocate the histogram/inverse colormap storage */ - m_histogram = new ushort[HIST_C0_ELEMS][]; - for (int i = 0; i < HIST_C0_ELEMS; i++) - m_histogram[i] = new ushort[HIST_C1_ELEMS * HIST_C2_ELEMS]; - - m_needs_zeroed = true; /* histogram is garbage now */ - - /* Allocate storage for the completed colormap, if required. - * We do this now since it is FAR storage and may affect - * the memory manager's space calculations. - */ - if (cinfo.m_enable_2pass_quant) - { - /* Make sure color count is acceptable */ - int desired_local = cinfo.m_desired_number_of_colors; - - /* Lower bound on # of colors ... somewhat arbitrary as long as > 0 */ - if (desired_local < 8) - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_QUANT_FEW_COLORS, 8); - - /* Make sure colormap indexes can be represented by JSAMPLEs */ - if (desired_local > MAXNUMCOLORS) - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_QUANT_MANY_COLORS, MAXNUMCOLORS); - - m_sv_colormap = jpeg_common_struct.AllocJpegSamples(desired_local, 3); - m_desired = desired_local; - } - - /* Only F-S dithering or no dithering is supported. */ - /* If user asks for ordered dither, give him F-S. */ - if (cinfo.m_dither_mode != J_DITHER_MODE.JDITHER_NONE) - cinfo.m_dither_mode = J_DITHER_MODE.JDITHER_FS; - - /* Allocate Floyd-Steinberg workspace if necessary. - * This isn't really needed until pass 2, but again it is FAR storage. - * Although we will cope with a later change in dither_mode, - * we do not promise to honor max_memory_to_use if dither_mode changes. - */ - if (cinfo.m_dither_mode == J_DITHER_MODE.JDITHER_FS) - { - m_fserrors = new short[(cinfo.m_output_width + 2) * 3]; - - /* Might as well create the error-limiting table too. */ - init_error_limit(); - } - } - - /// - /// Initialize for each processing pass. - /// - public virtual void start_pass(bool is_pre_scan) - { - /* Only F-S dithering or no dithering is supported. */ - /* If user asks for ordered dither, give him F-S. */ - if (m_cinfo.m_dither_mode != J_DITHER_MODE.JDITHER_NONE) - m_cinfo.m_dither_mode = J_DITHER_MODE.JDITHER_FS; - - if (is_pre_scan) - { - /* Set up method pointers */ - m_quantizer = QuantizerType.prescan_quantizer; - m_useFinishPass1 = true; - m_needs_zeroed = true; /* Always zero histogram */ - } - else - { - /* Set up method pointers */ - if (m_cinfo.m_dither_mode == J_DITHER_MODE.JDITHER_FS) - m_quantizer = QuantizerType.pass2_fs_dither_quantizer; - else - m_quantizer = QuantizerType.pass2_no_dither_quantizer; - - m_useFinishPass1 = false; - - /* Make sure color count is acceptable */ - int i = m_cinfo.m_actual_number_of_colors; - if (i < 1) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_QUANT_FEW_COLORS, 1); - - if (i > MAXNUMCOLORS) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_QUANT_MANY_COLORS, MAXNUMCOLORS); - - if (m_cinfo.m_dither_mode == J_DITHER_MODE.JDITHER_FS) - { - /* Allocate Floyd-Steinberg workspace if we didn't already. */ - if (m_fserrors == null) - { - int arraysize = (m_cinfo.m_output_width + 2) * 3; - m_fserrors = new short[arraysize]; - } - else - { - /* Initialize the propagated errors to zero. */ - Array.Clear(m_fserrors, 0, m_fserrors.Length); - } - - /* Make the error-limit table if we didn't already. */ - if (m_error_limiter == null) - init_error_limit(); - - m_on_odd_row = false; - } - } - - /* Zero the histogram or inverse color map, if necessary */ - if (m_needs_zeroed) - { - for (int i = 0; i < HIST_C0_ELEMS; i++) - Array.Clear(m_histogram[i], 0, m_histogram[i].Length); - - m_needs_zeroed = false; - } - } - - public virtual void color_quantize(byte[][] input_buf, int in_row, byte[][] output_buf, int out_row, int num_rows) - { - switch (m_quantizer) - { - case QuantizerType.prescan_quantizer: - prescan_quantize(input_buf, in_row, num_rows); - break; - case QuantizerType.pass2_fs_dither_quantizer: - pass2_fs_dither(input_buf, in_row, output_buf, out_row, num_rows); - break; - case QuantizerType.pass2_no_dither_quantizer: - pass2_no_dither(input_buf, in_row, output_buf, out_row, num_rows); - break; - default: - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NOTIMPL); - break; - } - } - - public virtual void finish_pass() - { - if (m_useFinishPass1) - finish_pass1(); - } - - /// - /// Switch to a new external colormap between output passes. - /// - public virtual void new_color_map() - { - /* Reset the inverse color map */ - m_needs_zeroed = true; - } - - /// - /// Prescan some rows of pixels. - /// In this module the prescan simply updates the histogram, which has been - /// initialized to zeroes by start_pass. - /// An output_buf parameter is required by the method signature, but no data - /// is actually output (in fact the buffer controller is probably passing a - /// null pointer). - /// - private void prescan_quantize(byte[][] input_buf, int in_row, int num_rows) - { - for (int row = 0; row < num_rows; row++) - { - int inputIndex = 0; - for (int col = m_cinfo.m_output_width; col > 0; col--) - { - int rowIndex = (int)input_buf[in_row + row][inputIndex] >> C0_SHIFT; - int columnIndex = ((int)input_buf[in_row + row][inputIndex + 1] >> C1_SHIFT) * HIST_C2_ELEMS + - ((int)input_buf[in_row + row][inputIndex + 2] >> C2_SHIFT); - - /* increment pixel value, check for overflow and undo increment if so. */ - m_histogram[rowIndex][columnIndex]++; - if (m_histogram[rowIndex][columnIndex] <= 0) - m_histogram[rowIndex][columnIndex]--; - - inputIndex += 3; - } - } - } - - /// - /// Map some rows of pixels to the output colormapped representation. - /// This version performs Floyd-Steinberg dithering - /// - private void pass2_fs_dither(byte[][] input_buf, int in_row, byte[][] output_buf, int out_row, int num_rows) - { - byte[] limit = m_cinfo.m_sample_range_limit; - int limitOffset = m_cinfo.m_sampleRangeLimitOffset; - - for (int row = 0; row < num_rows; row++) - { - int inputPixelIndex = 0; - int outputPixelIndex = 0; - int errorIndex = 0; - int dir; /* +1 or -1 depending on direction */ - int dir3; /* 3*dir, for advancing inputIndex & errorIndex */ - if (m_on_odd_row) - { - /* work right to left in this row */ - inputPixelIndex += (m_cinfo.m_output_width - 1) * 3; /* so point to rightmost pixel */ - outputPixelIndex += m_cinfo.m_output_width - 1; - dir = -1; - dir3 = -3; - errorIndex = (m_cinfo.m_output_width + 1) * 3; /* => entry after last column */ - m_on_odd_row = false; /* flip for next time */ - } - else - { - /* work left to right in this row */ - dir = 1; - dir3 = 3; - errorIndex = 0; /* => entry before first real column */ - m_on_odd_row = true; /* flip for next time */ - } - - /* Preset error values: no error propagated to first pixel from left */ - /* current error or pixel value */ - int cur0 = 0; - int cur1 = 0; - int cur2 = 0; - /* and no error propagated to row below yet */ - /* error for pixel below cur */ - int belowerr0 = 0; - int belowerr1 = 0; - int belowerr2 = 0; - /* error for below/prev col */ - int bpreverr0 = 0; - int bpreverr1 = 0; - int bpreverr2 = 0; - - for (int col = m_cinfo.m_output_width; col > 0; col--) - { - /* curN holds the error propagated from the previous pixel on the - * current line. Add the error propagated from the previous line - * to form the complete error correction term for this pixel, and - * round the error term (which is expressed * 16) to an integer. - * RIGHT_SHIFT rounds towards minus infinity, so adding 8 is correct - * for either sign of the error value. - * Note: errorIndex is for *previous* column's array entry. - */ - cur0 = JpegUtils.RIGHT_SHIFT(cur0 + m_fserrors[errorIndex + dir3] + 8, 4); - cur1 = JpegUtils.RIGHT_SHIFT(cur1 + m_fserrors[errorIndex + dir3 + 1] + 8, 4); - cur2 = JpegUtils.RIGHT_SHIFT(cur2 + m_fserrors[errorIndex + dir3 + 2] + 8, 4); - - /* Limit the error using transfer function set by init_error_limit. - * See comments with init_error_limit for rationale. - */ - cur0 = m_error_limiter[JpegConstants.MAXJSAMPLE + cur0]; - cur1 = m_error_limiter[JpegConstants.MAXJSAMPLE + cur1]; - cur2 = m_error_limiter[JpegConstants.MAXJSAMPLE + cur2]; - - /* Form pixel value + error, and range-limit to 0..MAXJSAMPLE. - * The maximum error is +- MAXJSAMPLE (or less with error limiting); - * this sets the required size of the range_limit array. - */ - cur0 += input_buf[in_row + row][inputPixelIndex]; - cur1 += input_buf[in_row + row][inputPixelIndex + 1]; - cur2 += input_buf[in_row + row][inputPixelIndex + 2]; - cur0 = limit[limitOffset + cur0]; - cur1 = limit[limitOffset + cur1]; - cur2 = limit[limitOffset + cur2]; - - /* Index into the cache with adjusted pixel value */ - int hRow = cur0 >> C0_SHIFT; - int hColumn = (cur1 >> C1_SHIFT) * HIST_C2_ELEMS + (cur2 >> C2_SHIFT); - - /* If we have not seen this color before, find nearest colormap */ - /* entry and update the cache */ - if (m_histogram[hRow][hColumn] == 0) - fill_inverse_cmap(cur0 >> C0_SHIFT, cur1 >> C1_SHIFT, cur2 >> C2_SHIFT); - - /* Now emit the colormap index for this cell */ - int pixcode = m_histogram[hRow][hColumn] - 1; - output_buf[out_row + row][outputPixelIndex] = (byte) pixcode; - - /* Compute representation error for this pixel */ - cur0 -= m_cinfo.m_colormap[0][pixcode]; - cur1 -= m_cinfo.m_colormap[1][pixcode]; - cur2 -= m_cinfo.m_colormap[2][pixcode]; - - /* Compute error fractions to be propagated to adjacent pixels. - * Add these into the running sums, and simultaneously shift the - * next-line error sums left by 1 column. - */ - int bnexterr = cur0; /* Process component 0 */ - int delta = cur0 * 2; - cur0 += delta; /* form error * 3 */ - m_fserrors[errorIndex] = (short) (bpreverr0 + cur0); - cur0 += delta; /* form error * 5 */ - bpreverr0 = belowerr0 + cur0; - belowerr0 = bnexterr; - cur0 += delta; /* form error * 7 */ - bnexterr = cur1; /* Process component 1 */ - delta = cur1 * 2; - cur1 += delta; /* form error * 3 */ - m_fserrors[errorIndex + 1] = (short) (bpreverr1 + cur1); - cur1 += delta; /* form error * 5 */ - bpreverr1 = belowerr1 + cur1; - belowerr1 = bnexterr; - cur1 += delta; /* form error * 7 */ - bnexterr = cur2; /* Process component 2 */ - delta = cur2 * 2; - cur2 += delta; /* form error * 3 */ - m_fserrors[errorIndex + 2] = (short) (bpreverr2 + cur2); - cur2 += delta; /* form error * 5 */ - bpreverr2 = belowerr2 + cur2; - belowerr2 = bnexterr; - cur2 += delta; /* form error * 7 */ - - /* At this point curN contains the 7/16 error value to be propagated - * to the next pixel on the current line, and all the errors for the - * next line have been shifted over. We are therefore ready to move on. - */ - inputPixelIndex += dir3; /* Advance pixel pointers to next column */ - outputPixelIndex += dir; - errorIndex += dir3; /* advance errorIndex to current column */ - } - - /* Post-loop cleanup: we must unload the final error values into the - * final fserrors[] entry. Note we need not unload belowerrN because - * it is for the dummy column before or after the actual array. - */ - m_fserrors[errorIndex] = (short) bpreverr0; /* unload prev errs into array */ - m_fserrors[errorIndex + 1] = (short) bpreverr1; - m_fserrors[errorIndex + 2] = (short) bpreverr2; - } - } - - /// - /// Map some rows of pixels to the output colormapped representation. - /// This version performs no dithering - /// - private void pass2_no_dither(byte[][] input_buf, int in_row, byte[][] output_buf, int out_row, int num_rows) - { - for (int row = 0; row < num_rows; row++) - { - int inRow = row + in_row; - int inIndex = 0; - int outIndex = 0; - int outRow = out_row + row; - for (int col = m_cinfo.m_output_width; col > 0; col--) - { - /* get pixel value and index into the cache */ - int c0 = (int)input_buf[inRow][inIndex] >> C0_SHIFT; - inIndex++; - - int c1 = (int)input_buf[inRow][inIndex] >> C1_SHIFT; - inIndex++; - - int c2 = (int)input_buf[inRow][inIndex] >> C2_SHIFT; - inIndex++; - - int hRow = c0; - int hColumn = c1 * HIST_C2_ELEMS + c2; - - /* If we have not seen this color before, find nearest colormap entry */ - /* and update the cache */ - if (m_histogram[hRow][hColumn] == 0) - fill_inverse_cmap(c0, c1, c2); - - /* Now emit the colormap index for this cell */ - output_buf[outRow][outIndex] = (byte)(m_histogram[hRow][hColumn] - 1); - outIndex++; - } - } - } - - /// - /// Finish up at the end of each pass. - /// - private void finish_pass1() - { - /* Select the representative colors and fill in cinfo.colormap */ - m_cinfo.m_colormap = m_sv_colormap; - select_colors(m_desired); - - /* Force next pass to zero the color index table */ - m_needs_zeroed = true; - } - - /// - /// Compute representative color for a box, put it in colormap[icolor] - /// - private void compute_color(box[] boxlist, int boxIndex, int icolor) - { - /* Current algorithm: mean weighted by pixels (not colors) */ - /* Note it is important to get the rounding correct! */ - long total = 0; - long c0total = 0; - long c1total = 0; - long c2total = 0; - box curBox = boxlist[boxIndex]; - for (int c0 = curBox.c0min; c0 <= curBox.c0max; c0++) - { - for (int c1 = curBox.c1min; c1 <= curBox.c1max; c1++) - { - int histogramIndex = c1 * HIST_C2_ELEMS + curBox.c2min; - for (int c2 = curBox.c2min; c2 <= curBox.c2max; c2++) - { - long count = m_histogram[c0][histogramIndex]; - histogramIndex++; - - if (count != 0) - { - total += count; - c0total += ((c0 << C0_SHIFT) + ((1 << C0_SHIFT) >> 1)) * count; - c1total += ((c1 << C1_SHIFT) + ((1 << C1_SHIFT) >> 1)) * count; - c2total += ((c2 << C2_SHIFT) + ((1 << C2_SHIFT) >> 1)) * count; - } - } - } - } - - m_cinfo.m_colormap[0][icolor] = (byte)((c0total + (total >> 1)) / total); - m_cinfo.m_colormap[1][icolor] = (byte)((c1total + (total >> 1)) / total); - m_cinfo.m_colormap[2][icolor] = (byte)((c2total + (total >> 1)) / total); - } - - /// - /// Master routine for color selection - /// - private void select_colors(int desired_colors) - { - /* Allocate workspace for box list */ - box[] boxlist = new box[desired_colors]; - - /* Initialize one box containing whole space */ - int numboxes = 1; - boxlist[0].c0min = 0; - boxlist[0].c0max = JpegConstants.MAXJSAMPLE >> C0_SHIFT; - boxlist[0].c1min = 0; - boxlist[0].c1max = JpegConstants.MAXJSAMPLE >> C1_SHIFT; - boxlist[0].c2min = 0; - boxlist[0].c2max = JpegConstants.MAXJSAMPLE >> C2_SHIFT; - - /* Shrink it to actually-used volume and set its statistics */ - update_box(boxlist, 0); - - /* Perform median-cut to produce final box list */ - numboxes = median_cut(boxlist, numboxes, desired_colors); - - /* Compute the representative color for each box, fill colormap */ - for (int i = 0; i < numboxes; i++) - compute_color(boxlist, i, i); - - m_cinfo.m_actual_number_of_colors = numboxes; - m_cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_QUANT_SELECTED, numboxes); - } - - /// - /// Repeatedly select and split the largest box until we have enough boxes - /// - private int median_cut(box[] boxlist, int numboxes, int desired_colors) - { - while (numboxes < desired_colors) - { - /* Select box to split. - * Current algorithm: by population for first half, then by volume. - */ - int foundIndex; - if (numboxes * 2 <= desired_colors) - foundIndex = find_biggest_color_pop(boxlist, numboxes); - else - foundIndex = find_biggest_volume(boxlist, numboxes); - - if (foundIndex == -1) /* no splittable boxes left! */ - break; - - /* Copy the color bounds to the new box. */ - boxlist[numboxes].c0max = boxlist[foundIndex].c0max; - boxlist[numboxes].c1max = boxlist[foundIndex].c1max; - boxlist[numboxes].c2max = boxlist[foundIndex].c2max; - boxlist[numboxes].c0min = boxlist[foundIndex].c0min; - boxlist[numboxes].c1min = boxlist[foundIndex].c1min; - boxlist[numboxes].c2min = boxlist[foundIndex].c2min; - - /* Choose which axis to split the box on. - * Current algorithm: longest scaled axis. - * See notes in update_box about scaling distances. - */ - int c0 = ((boxlist[foundIndex].c0max - boxlist[foundIndex].c0min) << C0_SHIFT) * R_SCALE; - int c1 = ((boxlist[foundIndex].c1max - boxlist[foundIndex].c1min) << C1_SHIFT) * G_SCALE; - int c2 = ((boxlist[foundIndex].c2max - boxlist[foundIndex].c2min) << C2_SHIFT) * B_SCALE; - - /* We want to break any ties in favor of green, then red, blue last. - * This code does the right thing for R,G,B or B,G,R color orders only. - */ - int cmax = c1; - int n = 1; - - if (c0 > cmax) - { - cmax = c0; - n = 0; - } - - if (c2 > cmax) - { - n = 2; - } - - /* Choose split point along selected axis, and update box bounds. - * Current algorithm: split at halfway point. - * (Since the box has been shrunk to minimum volume, - * any split will produce two nonempty subboxes.) - * Note that lb value is max for lower box, so must be < old max. - */ - int lb; - switch (n) - { - case 0: - lb = (boxlist[foundIndex].c0max + boxlist[foundIndex].c0min) / 2; - boxlist[foundIndex].c0max = lb; - boxlist[numboxes].c0min = lb + 1; - break; - case 1: - lb = (boxlist[foundIndex].c1max + boxlist[foundIndex].c1min) / 2; - boxlist[foundIndex].c1max = lb; - boxlist[numboxes].c1min = lb + 1; - break; - case 2: - lb = (boxlist[foundIndex].c2max + boxlist[foundIndex].c2min) / 2; - boxlist[foundIndex].c2max = lb; - boxlist[numboxes].c2min = lb + 1; - break; - } - - /* Update stats for boxes */ - update_box(boxlist, foundIndex); - update_box(boxlist, numboxes); - numboxes++; - } - - return numboxes; - } - - /* - * Next we have the really interesting routines: selection of a colormap - * given the completed histogram. - * These routines work with a list of "boxes", each representing a rectangular - * subset of the input color space (to histogram precision). - */ - - /// - /// Find the splittable box with the largest color population - /// Returns null if no splittable boxes remain - /// - private static int find_biggest_color_pop(box[] boxlist, int numboxes) - { - long maxc = 0; - int which = -1; - for (int i = 0; i < numboxes; i++) - { - if (boxlist[i].colorcount > maxc && boxlist[i].volume > 0) - { - which = i; - maxc = boxlist[i].colorcount; - } - } - - return which; - } - - /// - /// Find the splittable box with the largest (scaled) volume - /// Returns null if no splittable boxes remain - /// - private static int find_biggest_volume(box[] boxlist, int numboxes) - { - int maxv = 0; - int which = -1; - for (int i = 0; i < numboxes; i++) - { - if (boxlist[i].volume > maxv) - { - which = i; - maxv = boxlist[i].volume; - } - } - - return which; - } - - /// - /// Shrink the min/max bounds of a box to enclose only nonzero elements, - /// and recompute its volume and population - /// - private void update_box(box[] boxlist, int boxIndex) - { - box curBox = boxlist[boxIndex]; - bool have_c0min = false; - - if (curBox.c0max > curBox.c0min) - { - for (int c0 = curBox.c0min; c0 <= curBox.c0max; c0++) - { - for (int c1 = curBox.c1min; c1 <= curBox.c1max; c1++) - { - int histogramIndex = c1 * HIST_C2_ELEMS + curBox.c2min; - for (int c2 = curBox.c2min; c2 <= curBox.c2max; c2++) - { - if (m_histogram[c0][histogramIndex++] != 0) - { - curBox.c0min = c0; - have_c0min = true; - break; - } - } - - if (have_c0min) - break; - } - - if (have_c0min) - break; - } - } - - bool have_c0max = false; - if (curBox.c0max > curBox.c0min) - { - for (int c0 = curBox.c0max; c0 >= curBox.c0min; c0--) - { - for (int c1 = curBox.c1min; c1 <= curBox.c1max; c1++) - { - int histogramIndex = c1 * HIST_C2_ELEMS + curBox.c2min; - for (int c2 = curBox.c2min; c2 <= curBox.c2max; c2++) - { - if (m_histogram[c0][histogramIndex++] != 0) - { - curBox.c0max = c0; - have_c0max = true; - break; - } - } - - if (have_c0max) - break; - } - - if (have_c0max) - break; - } - } - - bool have_c1min = false; - if (curBox.c1max > curBox.c1min) - { - for (int c1 = curBox.c1min; c1 <= curBox.c1max; c1++) - { - for (int c0 = curBox.c0min; c0 <= curBox.c0max; c0++) - { - int histogramIndex = c1 * HIST_C2_ELEMS + curBox.c2min; - for (int c2 = curBox.c2min; c2 <= curBox.c2max; c2++) - { - if (m_histogram[c0][histogramIndex++] != 0) - { - curBox.c1min = c1; - have_c1min = true; - break; - } - } - - if (have_c1min) - break; - } - - if (have_c1min) - break; - } - } - - bool have_c1max = false; - if (curBox.c1max > curBox.c1min) - { - for (int c1 = curBox.c1max; c1 >= curBox.c1min; c1--) - { - for (int c0 = curBox.c0min; c0 <= curBox.c0max; c0++) - { - int histogramIndex = c1 * HIST_C2_ELEMS + curBox.c2min; - for (int c2 = curBox.c2min; c2 <= curBox.c2max; c2++) - { - if (m_histogram[c0][histogramIndex++] != 0) - { - curBox.c1max = c1; - have_c1max = true; - break; - } - } - - if (have_c1max) - break; - } - - if (have_c1max) - break; - } - } - - bool have_c2min = false; - if (curBox.c2max > curBox.c2min) - { - for (int c2 = curBox.c2min; c2 <= curBox.c2max; c2++) - { - for (int c0 = curBox.c0min; c0 <= curBox.c0max; c0++) - { - int histogramIndex = curBox.c1min * HIST_C2_ELEMS + c2; - for (int c1 = curBox.c1min; c1 <= curBox.c1max; c1++, histogramIndex += HIST_C2_ELEMS) - { - if (m_histogram[c0][histogramIndex] != 0) - { - curBox.c2min = c2; - have_c2min = true; - break; - } - } - - if (have_c2min) - break; - } - - if (have_c2min) - break; - } - } - - bool have_c2max = false; - if (curBox.c2max > curBox.c2min) - { - for (int c2 = curBox.c2max; c2 >= curBox.c2min; c2--) - { - for (int c0 = curBox.c0min; c0 <= curBox.c0max; c0++) - { - int histogramIndex = curBox.c1min * HIST_C2_ELEMS + c2; - for (int c1 = curBox.c1min; c1 <= curBox.c1max; c1++, histogramIndex += HIST_C2_ELEMS) - { - if (m_histogram[c0][histogramIndex] != 0) - { - curBox.c2max = c2; - have_c2max = true; - break; - } - } - - if (have_c2max) - break; - } - - if (have_c2max) - break; - } - } - - /* Update box volume. - * We use 2-norm rather than real volume here; this biases the method - * against making long narrow boxes, and it has the side benefit that - * a box is splittable iff norm > 0. - * Since the differences are expressed in histogram-cell units, - * we have to shift back to byte units to get consistent distances; - * after which, we scale according to the selected distance scale factors. - */ - int dist0 = ((curBox.c0max - curBox.c0min) << C0_SHIFT) * R_SCALE; - int dist1 = ((curBox.c1max - curBox.c1min) << C1_SHIFT) * G_SCALE; - int dist2 = ((curBox.c2max - curBox.c2min) << C2_SHIFT) * B_SCALE; - curBox.volume = dist0 * dist0 + dist1 * dist1 + dist2 * dist2; - - /* Now scan remaining volume of box and compute population */ - long ccount = 0; - for (int c0 = curBox.c0min; c0 <= curBox.c0max; c0++) - { - for (int c1 = curBox.c1min; c1 <= curBox.c1max; c1++) - { - int histogramIndex = c1 * HIST_C2_ELEMS + curBox.c2min; - for (int c2 = curBox.c2min; c2 <= curBox.c2max; c2++, histogramIndex++) - { - if (m_histogram[c0][histogramIndex] != 0) - ccount++; - } - } - } - - curBox.colorcount = ccount; - boxlist[boxIndex] = curBox; - } - - /// - /// Initialize the error-limiting transfer function (lookup table). - /// The raw F-S error computation can potentially compute error values of up to - /// +- MAXJSAMPLE. But we want the maximum correction applied to a pixel to be - /// much less, otherwise obviously wrong pixels will be created. (Typical - /// effects include weird fringes at color-area boundaries, isolated bright - /// pixels in a dark area, etc.) The standard advice for avoiding this problem - /// is to ensure that the "corners" of the color cube are allocated as output - /// colors; then repeated errors in the same direction cannot cause cascading - /// error buildup. However, that only prevents the error from getting - /// completely out of hand; Aaron Giles reports that error limiting improves - /// the results even with corner colors allocated. - /// A simple clamping of the error values to about +- MAXJSAMPLE/8 works pretty - /// well, but the smoother transfer function used below is even better. Thanks - /// to Aaron Giles for this idea. - /// - private void init_error_limit() - { - m_error_limiter = new int [JpegConstants.MAXJSAMPLE * 2 + 1]; - int tableOffset = JpegConstants.MAXJSAMPLE; - - const int STEPSIZE = ((JpegConstants.MAXJSAMPLE + 1) / 16); - - /* Map errors 1:1 up to +- MAXJSAMPLE/16 */ - int output = 0; - int input = 0; - for (; input < STEPSIZE; input++, output++) - { - m_error_limiter[tableOffset + input] = output; - m_error_limiter[tableOffset - input] = -output; - } - - /* Map errors 1:2 up to +- 3*MAXJSAMPLE/16 */ - for (; input < STEPSIZE*3; input++) - { - m_error_limiter[tableOffset + input] = output; - m_error_limiter[tableOffset - input] = -output; - output += (input & 1) != 0 ? 1 : 0; - } - - /* Clamp the rest to final output value (which is (MAXJSAMPLE+1)/8) */ - for (; input <= JpegConstants.MAXJSAMPLE; input++) - { - m_error_limiter[tableOffset + input] = output; - m_error_limiter[tableOffset - input] = -output; - } - } - - /* - * These routines are concerned with the time-critical task of mapping input - * colors to the nearest color in the selected colormap. - * - * We re-use the histogram space as an "inverse color map", essentially a - * cache for the results of nearest-color searches. All colors within a - * histogram cell will be mapped to the same colormap entry, namely the one - * closest to the cell's center. This may not be quite the closest entry to - * the actual input color, but it's almost as good. A zero in the cache - * indicates we haven't found the nearest color for that cell yet; the array - * is cleared to zeroes before starting the mapping pass. When we find the - * nearest color for a cell, its colormap index plus one is recorded in the - * cache for future use. The pass2 scanning routines call fill_inverse_cmap - * when they need to use an unfilled entry in the cache. - * - * Our method of efficiently finding nearest colors is based on the "locally - * sorted search" idea described by Heckbert and on the incremental distance - * calculation described by Spencer W. Thomas in chapter III.1 of Graphics - * Gems II (James Arvo, ed. Academic Press, 1991). Thomas points out that - * the distances from a given colormap entry to each cell of the histogram can - * be computed quickly using an incremental method: the differences between - * distances to adjacent cells themselves differ by a constant. This allows a - * fairly fast implementation of the "brute force" approach of computing the - * distance from every colormap entry to every histogram cell. Unfortunately, - * it needs a work array to hold the best-distance-so-far for each histogram - * cell (because the inner loop has to be over cells, not colormap entries). - * The work array elements have to be ints, so the work array would need - * 256Kb at our recommended precision. This is not feasible in DOS machines. - * - * To get around these problems, we apply Thomas' method to compute the - * nearest colors for only the cells within a small subbox of the histogram. - * The work array need be only as big as the subbox, so the memory usage - * problem is solved. Furthermore, we need not fill subboxes that are never - * referenced in pass2; many images use only part of the color gamut, so a - * fair amount of work is saved. An additional advantage of this - * approach is that we can apply Heckbert's locality criterion to quickly - * eliminate colormap entries that are far away from the subbox; typically - * three-fourths of the colormap entries are rejected by Heckbert's criterion, - * and we need not compute their distances to individual cells in the subbox. - * The speed of this approach is heavily influenced by the subbox size: too - * small means too much overhead, too big loses because Heckbert's criterion - * can't eliminate as many colormap entries. Empirically the best subbox - * size seems to be about 1/512th of the histogram (1/8th in each direction). - * - * Thomas' article also describes a refined method which is asymptotically - * faster than the brute-force method, but it is also far more complex and - * cannot efficiently be applied to small subboxes. It is therefore not - * useful for programs intended to be portable to DOS machines. On machines - * with plenty of memory, filling the whole histogram in one shot with Thomas' - * refined method might be faster than the present code --- but then again, - * it might not be any faster, and it's certainly more complicated. - */ - - /* - * The next three routines implement inverse colormap filling. They could - * all be folded into one big routine, but splitting them up this way saves - * some stack space (the mindist[] and bestdist[] arrays need not coexist) - * and may allow some compilers to produce better code by registerizing more - * inner-loop variables. - */ - - /// - /// Locate the colormap entries close enough to an update box to be candidates - /// for the nearest entry to some cell(s) in the update box. The update box - /// is specified by the center coordinates of its first cell. The number of - /// candidate colormap entries is returned, and their colormap indexes are - /// placed in colorlist[]. - /// This routine uses Heckbert's "locally sorted search" criterion to select - /// the colors that need further consideration. - /// - private int find_nearby_colors(int minc0, int minc1, int minc2, byte[] colorlist) - { - /* Compute true coordinates of update box's upper corner and center. - * Actually we compute the coordinates of the center of the upper-corner - * histogram cell, which are the upper bounds of the volume we care about. - * Note that since ">>" rounds down, the "center" values may be closer to - * min than to max; hence comparisons to them must be "<=", not "<". - */ - int maxc0 = minc0 + ((1 << BOX_C0_SHIFT) - (1 << C0_SHIFT)); - int centerc0 = (minc0 + maxc0) >> 1; - - int maxc1 = minc1 + ((1 << BOX_C1_SHIFT) - (1 << C1_SHIFT)); - int centerc1 = (minc1 + maxc1) >> 1; - - int maxc2 = minc2 + ((1 << BOX_C2_SHIFT) - (1 << C2_SHIFT)); - int centerc2 = (minc2 + maxc2) >> 1; - - /* For each color in colormap, find: - * 1. its minimum squared-distance to any point in the update box - * (zero if color is within update box); - * 2. its maximum squared-distance to any point in the update box. - * Both of these can be found by considering only the corners of the box. - * We save the minimum distance for each color in mindist[]; - * only the smallest maximum distance is of interest. - */ - int minmaxdist = 0x7FFFFFFF; - int[] mindist = new int[MAXNUMCOLORS]; /* min distance to colormap entry i */ - - for (int i = 0; i < m_cinfo.m_actual_number_of_colors; i++) - { - /* We compute the squared-c0-distance term, then add in the other two. */ - int x = m_cinfo.m_colormap[0][i]; - int min_dist; - int max_dist; - - if (x < minc0) - { - int tdist = (x - minc0) * R_SCALE; - min_dist = tdist * tdist; - tdist = (x - maxc0) * R_SCALE; - max_dist = tdist * tdist; - } - else if (x > maxc0) - { - int tdist = (x - maxc0) * R_SCALE; - min_dist = tdist * tdist; - tdist = (x - minc0) * R_SCALE; - max_dist = tdist * tdist; - } - else - { - /* within cell range so no contribution to min_dist */ - min_dist = 0; - if (x <= centerc0) - { - int tdist = (x - maxc0) * R_SCALE; - max_dist = tdist * tdist; - } - else - { - int tdist = (x - minc0) * R_SCALE; - max_dist = tdist * tdist; - } - } - - x = m_cinfo.m_colormap[1][i]; - if (x < minc1) - { - int tdist = (x - minc1) * G_SCALE; - min_dist += tdist * tdist; - tdist = (x - maxc1) * G_SCALE; - max_dist += tdist * tdist; - } - else if (x > maxc1) - { - int tdist = (x - maxc1) * G_SCALE; - min_dist += tdist * tdist; - tdist = (x - minc1) * G_SCALE; - max_dist += tdist * tdist; - } - else - { - /* within cell range so no contribution to min_dist */ - if (x <= centerc1) - { - int tdist = (x - maxc1) * G_SCALE; - max_dist += tdist * tdist; - } - else - { - int tdist = (x - minc1) * G_SCALE; - max_dist += tdist * tdist; - } - } - - x = m_cinfo.m_colormap[2][i]; - if (x < minc2) - { - int tdist = (x - minc2) * B_SCALE; - min_dist += tdist * tdist; - tdist = (x - maxc2) * B_SCALE; - max_dist += tdist * tdist; - } - else if (x > maxc2) - { - int tdist = (x - maxc2) * B_SCALE; - min_dist += tdist * tdist; - tdist = (x - minc2) * B_SCALE; - max_dist += tdist * tdist; - } - else - { - /* within cell range so no contribution to min_dist */ - if (x <= centerc2) - { - int tdist = (x - maxc2) * B_SCALE; - max_dist += tdist * tdist; - } - else - { - int tdist = (x - minc2) * B_SCALE; - max_dist += tdist * tdist; - } - } - - mindist[i] = min_dist; /* save away the results */ - if (max_dist < minmaxdist) - minmaxdist = max_dist; - } - - /* Now we know that no cell in the update box is more than minmaxdist - * away from some colormap entry. Therefore, only colors that are - * within minmaxdist of some part of the box need be considered. - */ - int ncolors = 0; - for (int i = 0; i < m_cinfo.m_actual_number_of_colors; i++) - { - if (mindist[i] <= minmaxdist) - colorlist[ncolors++] = (byte) i; - } - - return ncolors; - } - - /// - /// Find the closest colormap entry for each cell in the update box, - /// given the list of candidate colors prepared by find_nearby_colors. - /// Return the indexes of the closest entries in the bestcolor[] array. - /// This routine uses Thomas' incremental distance calculation method to - /// find the distance from a colormap entry to successive cells in the box. - /// - private void find_best_colors(int minc0, int minc1, int minc2, int numcolors, byte[] colorlist, byte[] bestcolor) - { - /* Nominal steps between cell centers ("x" in Thomas article) */ - const int STEP_C0 = ((1 << C0_SHIFT) * R_SCALE); - const int STEP_C1 = ((1 << C1_SHIFT) * G_SCALE); - const int STEP_C2 = ((1 << C2_SHIFT) * B_SCALE); - - /* This array holds the distance to the nearest-so-far color for each cell */ - int[] bestdist = new int[BOX_C0_ELEMS * BOX_C1_ELEMS * BOX_C2_ELEMS]; - - /* Initialize best-distance for each cell of the update box */ - int bestIndex = 0; - for (int i = BOX_C0_ELEMS * BOX_C1_ELEMS * BOX_C2_ELEMS - 1; i >= 0; i--) - { - bestdist[bestIndex] = 0x7FFFFFFF; - bestIndex++; - } - - /* For each color selected by find_nearby_colors, - * compute its distance to the center of each cell in the box. - * If that's less than best-so-far, update best distance and color number. - */ - for (int i = 0; i < numcolors; i++) - { - int icolor = colorlist[i]; - - /* Compute (square of) distance from minc0/c1/c2 to this color */ - int inc0 = (minc0 - m_cinfo.m_colormap[0][icolor]) * R_SCALE; - int dist0 = inc0 * inc0; - - int inc1 = (minc1 - m_cinfo.m_colormap[1][icolor]) * G_SCALE; - dist0 += inc1 * inc1; - - int inc2 = (minc2 - m_cinfo.m_colormap[2][icolor]) * B_SCALE; - dist0 += inc2 * inc2; - - /* Form the initial difference increments */ - inc0 = inc0 * (2 * STEP_C0) + STEP_C0 * STEP_C0; - inc1 = inc1 * (2 * STEP_C1) + STEP_C1 * STEP_C1; - inc2 = inc2 * (2 * STEP_C2) + STEP_C2 * STEP_C2; - - /* Now loop over all cells in box, updating distance per Thomas method */ - bestIndex = 0; - int colorIndex = 0; - int xx0 = inc0; - for (int ic0 = BOX_C0_ELEMS - 1; ic0 >= 0; ic0--) - { - int dist1 = dist0; - int xx1 = inc1; - for (int ic1 = BOX_C1_ELEMS - 1; ic1 >= 0; ic1--) - { - int dist2 = dist1; - int xx2 = inc2; - for (int ic2 = BOX_C2_ELEMS - 1; ic2 >= 0; ic2--) - { - if (dist2 < bestdist[bestIndex]) - { - bestdist[bestIndex] = dist2; - bestcolor[colorIndex] = (byte) icolor; - } - - dist2 += xx2; - xx2 += 2 * STEP_C2 * STEP_C2; - bestIndex++; - colorIndex++; - } - - dist1 += xx1; - xx1 += 2 * STEP_C1 * STEP_C1; - } - - dist0 += xx0; - xx0 += 2 * STEP_C0 * STEP_C0; - } - } - } - - /// - /// Fill the inverse-colormap entries in the update box that contains - /// histogram cell c0/c1/c2. (Only that one cell MUST be filled, but - /// we can fill as many others as we wish.) - /// - private void fill_inverse_cmap(int c0, int c1, int c2) - { - /* Convert cell coordinates to update box ID */ - c0 >>= BOX_C0_LOG; - c1 >>= BOX_C1_LOG; - c2 >>= BOX_C2_LOG; - - /* Compute true coordinates of update box's origin corner. - * Actually we compute the coordinates of the center of the corner - * histogram cell, which are the lower bounds of the volume we care about. - */ - int minc0 = (c0 << BOX_C0_SHIFT) + ((1 << C0_SHIFT) >> 1); - int minc1 = (c1 << BOX_C1_SHIFT) + ((1 << C1_SHIFT) >> 1); - int minc2 = (c2 << BOX_C2_SHIFT) + ((1 << C2_SHIFT) >> 1); - - /* Determine which colormap entries are close enough to be candidates - * for the nearest entry to some cell in the update box. - */ - /* This array lists the candidate colormap indexes. */ - byte[] colorlist = new byte[MAXNUMCOLORS]; - int numcolors = find_nearby_colors(minc0, minc1, minc2, colorlist); - - /* Determine the actually nearest colors. */ - /* This array holds the actually closest colormap index for each cell. */ - byte[] bestcolor = new byte[BOX_C0_ELEMS * BOX_C1_ELEMS * BOX_C2_ELEMS]; - find_best_colors(minc0, minc1, minc2, numcolors, colorlist, bestcolor); - - /* Save the best color numbers (plus 1) in the main cache array */ - c0 <<= BOX_C0_LOG; /* convert ID back to base cell indexes */ - c1 <<= BOX_C1_LOG; - c2 <<= BOX_C2_LOG; - int bestcolorIndex = 0; - for (int ic0 = 0; ic0 < BOX_C0_ELEMS; ic0++) - { - for (int ic1 = 0; ic1 < BOX_C1_ELEMS; ic1++) - { - int histogramIndex = (c1 + ic1) * HIST_C2_ELEMS + c2; - for (int ic2 = 0; ic2 < BOX_C2_ELEMS; ic2++) - { - m_histogram[c0 + ic0][histogramIndex] = (ushort) ((int)bestcolor[bestcolorIndex] + 1); - histogramIndex++; - bestcolorIndex++; - } - } - } - } - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/my_c_coef_controller.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/my_c_coef_controller.cs deleted file mode 100644 index b1721df52..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/my_c_coef_controller.cs +++ /dev/null @@ -1,424 +0,0 @@ -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -/* - * This file contains the coefficient buffer controller for compression. - * This controller is the top level of the JPEG compressor proper. - * The coefficient buffer lies between forward-DCT and entropy encoding steps. - */ - -/* We use a full-image coefficient buffer when doing Huffman optimization, - * and also for writing multiple-scan JPEG files. In all cases, the DCT - * step is run during the first pass, and subsequent passes need only read - * the buffered coefficients. - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - class my_c_coef_controller : jpeg_c_coef_controller - { - private J_BUF_MODE m_passModeSetByLastStartPass; - private jpeg_compress_struct m_cinfo; - - private int m_iMCU_row_num; /* iMCU row # within image */ - private int m_mcu_ctr; /* counts MCUs processed in current row */ - private int m_MCU_vert_offset; /* counts MCU rows within iMCU row */ - private int m_MCU_rows_per_iMCU_row; /* number of such rows needed */ - - /* For single-pass compression, it's sufficient to buffer just one MCU - * (although this may prove a bit slow in practice). We allocate a - * workspace of C_MAX_BLOCKS_IN_MCU coefficient blocks, and reuse it for each - * MCU constructed and sent. (On 80x86, the workspace is FAR even though - * it's not really very big; this is to keep the module interfaces unchanged - * when a large coefficient buffer is necessary.) - * In multi-pass modes, this array points to the current MCU's blocks - * within the virtual arrays. - */ - private JBLOCK[][] m_MCU_buffer = new JBLOCK[JpegConstants.C_MAX_BLOCKS_IN_MCU][]; - - /* In multi-pass modes, we need a virtual block array for each component. */ - private jvirt_array[] m_whole_image = new jvirt_array[JpegConstants.MAX_COMPONENTS]; - - public my_c_coef_controller(jpeg_compress_struct cinfo, bool need_full_buffer) - { - m_cinfo = cinfo; - - /* Create the coefficient buffer. */ - if (need_full_buffer) - { - /* Allocate a full-image virtual array for each component, */ - /* padded to a multiple of samp_factor DCT blocks in each direction. */ - for (int ci = 0; ci < cinfo.m_num_components; ci++) - { - m_whole_image[ci] = jpeg_common_struct.CreateBlocksArray( - JpegUtils.jround_up(cinfo.Component_info[ci].Width_in_blocks, cinfo.Component_info[ci].H_samp_factor), - JpegUtils.jround_up(cinfo.Component_info[ci].height_in_blocks, cinfo.Component_info[ci].V_samp_factor)); - m_whole_image[ci].ErrorProcessor = cinfo; - } - } - else - { - /* We only need a single-MCU buffer. */ - JBLOCK[] buffer = new JBLOCK[JpegConstants.C_MAX_BLOCKS_IN_MCU]; - for (int i = 0; i < JpegConstants.C_MAX_BLOCKS_IN_MCU; i++) - buffer[i] = new JBLOCK(); - - for (int i = 0; i < JpegConstants.C_MAX_BLOCKS_IN_MCU; i++) - { - m_MCU_buffer[i] = new JBLOCK[JpegConstants.C_MAX_BLOCKS_IN_MCU - i]; - for (int j = i; j < JpegConstants.C_MAX_BLOCKS_IN_MCU; j++) - m_MCU_buffer[i][j - i] = buffer[j]; - } - - /* flag for no virtual arrays */ - m_whole_image[0] = null; - } - } - - // Initialize for a processing pass. - public virtual void start_pass(J_BUF_MODE pass_mode) - { - m_iMCU_row_num = 0; - start_iMCU_row(); - - switch (pass_mode) - { - case J_BUF_MODE.JBUF_PASS_THRU: - if (m_whole_image[0] != null) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_BUFFER_MODE); - break; - - case J_BUF_MODE.JBUF_SAVE_AND_PASS: - if (m_whole_image[0] == null) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_BUFFER_MODE); - break; - - case J_BUF_MODE.JBUF_CRANK_DEST: - if (m_whole_image[0] == null) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_BUFFER_MODE); - break; - - default: - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_BUFFER_MODE); - break; - } - - m_passModeSetByLastStartPass = pass_mode; - } - - public virtual bool compress_data(byte[][][] input_buf) - { - switch (m_passModeSetByLastStartPass) - { - case J_BUF_MODE.JBUF_PASS_THRU: - return compressDataImpl(input_buf); - - case J_BUF_MODE.JBUF_SAVE_AND_PASS: - return compressFirstPass(input_buf); - - case J_BUF_MODE.JBUF_CRANK_DEST: - return compressOutput(); - } - - return false; - } - - /// - /// Process some data in the single-pass case. - /// We process the equivalent of one fully interleaved MCU row ("iMCU" row) - /// per call, ie, v_samp_factor block rows for each component in the image. - /// Returns true if the iMCU row is completed, false if suspended. - /// - /// NB: input_buf contains a plane for each component in image, - /// which we index according to the component's SOF position. - /// - private bool compressDataImpl(byte[][][] input_buf) - { - int last_MCU_col = m_cinfo.m_MCUs_per_row - 1; - int last_iMCU_row = m_cinfo.m_total_iMCU_rows - 1; - - /* Loop to write as much as one whole iMCU row */ - for (int yoffset = m_MCU_vert_offset; yoffset < m_MCU_rows_per_iMCU_row; yoffset++) - { - for (int MCU_col_num = m_mcu_ctr; MCU_col_num <= last_MCU_col; MCU_col_num++) - { - /* Determine where data comes from in input_buf and do the DCT thing. - * Each call on forward_DCT processes a horizontal row of DCT blocks - * as wide as an MCU; we rely on having allocated the MCU_buffer[] blocks - * sequentially. Dummy blocks at the right or bottom edge are filled in - * specially. The data in them does not matter for image reconstruction, - * so we fill them with values that will encode to the smallest amount of - * data, viz: all zeroes in the AC entries, DC entries equal to previous - * block's DC value. (Thanks to Thomas Kinsman for this idea.) - */ - int blkn = 0; - for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) - { - jpeg_component_info componentInfo = m_cinfo.Component_info[m_cinfo.m_cur_comp_info[ci]]; - int blockcnt = (MCU_col_num < last_MCU_col) ? componentInfo.MCU_width : componentInfo.last_col_width; - int xpos = MCU_col_num * componentInfo.MCU_sample_width; - int ypos = yoffset * JpegConstants.DCTSIZE; - - for (int yindex = 0; yindex < componentInfo.MCU_height; yindex++) - { - if (m_iMCU_row_num < last_iMCU_row || yoffset + yindex < componentInfo.last_row_height) - { - m_cinfo.m_fdct.forward_DCT(componentInfo.Quant_tbl_no, input_buf[componentInfo.Component_index], - m_MCU_buffer[blkn], ypos, xpos, blockcnt); - - if (blockcnt < componentInfo.MCU_width) - { - /* Create some dummy blocks at the right edge of the image. */ - for (int i = 0; i < (componentInfo.MCU_width - blockcnt); i++) - Array.Clear(m_MCU_buffer[blkn + blockcnt][i].data, 0, m_MCU_buffer[blkn + blockcnt][i].data.Length); - - for (int bi = blockcnt; bi < componentInfo.MCU_width; bi++) - m_MCU_buffer[blkn + bi][0][0] = m_MCU_buffer[blkn + bi - 1][0][0]; - } - } - else - { - /* Create a row of dummy blocks at the bottom of the image. */ - for (int i = 0; i < componentInfo.MCU_width; i++) - Array.Clear(m_MCU_buffer[blkn][i].data, 0, m_MCU_buffer[blkn][i].data.Length); - - for (int bi = 0; bi < componentInfo.MCU_width; bi++) - m_MCU_buffer[blkn + bi][0][0] = m_MCU_buffer[blkn - 1][0][0]; - } - - blkn += componentInfo.MCU_width; - ypos += JpegConstants.DCTSIZE; - } - } - - /* Try to write the MCU. In event of a suspension failure, we will - * re-DCT the MCU on restart (a bit inefficient, could be fixed...) - */ - if (!m_cinfo.m_entropy.encode_mcu(m_MCU_buffer)) - { - /* Suspension forced; update state counters and exit */ - m_MCU_vert_offset = yoffset; - m_mcu_ctr = MCU_col_num; - return false; - } - } - - /* Completed an MCU row, but perhaps not an iMCU row */ - m_mcu_ctr = 0; - } - - /* Completed the iMCU row, advance counters for next one */ - m_iMCU_row_num++; - start_iMCU_row(); - return true; - } - - /// - /// Process some data in the first pass of a multi-pass case. - /// We process the equivalent of one fully interleaved MCU row ("iMCU" row) - /// per call, ie, v_samp_factor block rows for each component in the image. - /// This amount of data is read from the source buffer, DCT'd and quantized, - /// and saved into the virtual arrays. We also generate suitable dummy blocks - /// as needed at the right and lower edges. (The dummy blocks are constructed - /// in the virtual arrays, which have been padded appropriately.) This makes - /// it possible for subsequent passes not to worry about real vs. dummy blocks. - /// - /// We must also emit the data to the entropy encoder. This is conveniently - /// done by calling compress_output() after we've loaded the current strip - /// of the virtual arrays. - /// - /// NB: input_buf contains a plane for each component in image. All - /// components are DCT'd and loaded into the virtual arrays in this pass. - /// However, it may be that only a subset of the components are emitted to - /// the entropy encoder during this first pass; be careful about looking - /// at the scan-dependent variables (MCU dimensions, etc). - /// - private bool compressFirstPass(byte[][][] input_buf) - { - int last_iMCU_row = m_cinfo.m_total_iMCU_rows - 1; - - for (int ci = 0; ci < m_cinfo.m_num_components; ci++) - { - jpeg_component_info componentInfo = m_cinfo.Component_info[ci]; - - /* Align the virtual buffer for this component. */ - JBLOCK[][] buffer = m_whole_image[ci].Access(m_iMCU_row_num * componentInfo.V_samp_factor, - componentInfo.V_samp_factor); - - /* Count non-dummy DCT block rows in this iMCU row. */ - int block_rows; - if (m_iMCU_row_num < last_iMCU_row) - { - block_rows = componentInfo.V_samp_factor; - } - else - { - /* NB: can't use last_row_height here, since may not be set! */ - block_rows = componentInfo.height_in_blocks % componentInfo.V_samp_factor; - if (block_rows == 0) - block_rows = componentInfo.V_samp_factor; - } - - int blocks_across = componentInfo.Width_in_blocks; - int h_samp_factor = componentInfo.H_samp_factor; - - /* Count number of dummy blocks to be added at the right margin. */ - int ndummy = blocks_across % h_samp_factor; - if (ndummy > 0) - ndummy = h_samp_factor - ndummy; - - /* Perform DCT for all non-dummy blocks in this iMCU row. Each call - * on forward_DCT processes a complete horizontal row of DCT blocks. - */ - for (int block_row = 0; block_row < block_rows; block_row++) - { - m_cinfo.m_fdct.forward_DCT(componentInfo.Quant_tbl_no, input_buf[ci], - buffer[block_row], block_row * JpegConstants.DCTSIZE, 0, blocks_across); - - if (ndummy > 0) - { - /* Create dummy blocks at the right edge of the image. */ - Array.Clear(buffer[block_row][blocks_across].data, 0, buffer[block_row][blocks_across].data.Length); - - short lastDC = buffer[block_row][blocks_across - 1][0]; - for (int bi = 0; bi < ndummy; bi++) - buffer[block_row][blocks_across + bi][0] = lastDC; - } - } - - /* If at end of image, create dummy block rows as needed. - * The tricky part here is that within each MCU, we want the DC values - * of the dummy blocks to match the last real block's DC value. - * This squeezes a few more bytes out of the resulting file... - */ - if (m_iMCU_row_num == last_iMCU_row) - { - blocks_across += ndummy; /* include lower right corner */ - int MCUs_across = blocks_across / h_samp_factor; - for (int block_row = block_rows; block_row < componentInfo.V_samp_factor; block_row++) - { - for (int i = 0; i < blocks_across; i++) - Array.Clear(buffer[block_row][i].data, 0, buffer[block_row][i].data.Length); - - int thisOffset = 0; - int lastOffset = 0; - for (int MCUindex = 0; MCUindex < MCUs_across; MCUindex++) - { - short lastDC = buffer[block_row - 1][lastOffset + h_samp_factor - 1][0]; - for (int bi = 0; bi < h_samp_factor; bi++) - buffer[block_row][thisOffset + bi][0] = lastDC; - - thisOffset += h_samp_factor; /* advance to next MCU in row */ - lastOffset += h_samp_factor; - } - } - } - } - - /* NB: compress_output will increment iMCU_row_num if successful. - * A suspension return will result in redoing all the work above next time. - */ - - /* Emit data to the entropy encoder, sharing code with subsequent passes */ - return compressOutput(); - } - - /// - /// Process some data in subsequent passes of a multi-pass case. - /// We process the equivalent of one fully interleaved MCU row ("iMCU" row) - /// per call, ie, v_samp_factor block rows for each component in the scan. - /// The data is obtained from the virtual arrays and fed to the entropy coder. - /// Returns true if the iMCU row is completed, false if suspended. - /// - private bool compressOutput() - { - /* Align the virtual buffers for the components used in this scan. - */ - JBLOCK[][][] buffer = new JBLOCK[JpegConstants.MAX_COMPS_IN_SCAN][][]; - for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) - { - jpeg_component_info componentInfo = m_cinfo.Component_info[m_cinfo.m_cur_comp_info[ci]]; - buffer[ci] = m_whole_image[componentInfo.Component_index].Access( - m_iMCU_row_num * componentInfo.V_samp_factor, componentInfo.V_samp_factor); - } - - /* Loop to process one whole iMCU row */ - for (int yoffset = m_MCU_vert_offset; yoffset < m_MCU_rows_per_iMCU_row; yoffset++) - { - for (int MCU_col_num = m_mcu_ctr; MCU_col_num < m_cinfo.m_MCUs_per_row; MCU_col_num++) - { - /* Construct list of pointers to DCT blocks belonging to this MCU */ - int blkn = 0; /* index of current DCT block within MCU */ - for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) - { - jpeg_component_info componentInfo = m_cinfo.Component_info[m_cinfo.m_cur_comp_info[ci]]; - int start_col = MCU_col_num * componentInfo.MCU_width; - for (int yindex = 0; yindex < componentInfo.MCU_height; yindex++) - { - for (int xindex = 0; xindex < componentInfo.MCU_width; xindex++) - { - int bufLength = buffer[ci][yindex + yoffset].Length; - int start = start_col + xindex; - m_MCU_buffer[blkn] = new JBLOCK[bufLength - start]; - for (int j = start; j < bufLength; j++) - m_MCU_buffer[blkn][j - start] = buffer[ci][yindex + yoffset][j]; - - blkn++; - } - } - } - - /* Try to write the MCU. */ - if (!m_cinfo.m_entropy.encode_mcu(m_MCU_buffer)) - { - /* Suspension forced; update state counters and exit */ - m_MCU_vert_offset = yoffset; - m_mcu_ctr = MCU_col_num; - return false; - } - } - - /* Completed an MCU row, but perhaps not an iMCU row */ - m_mcu_ctr = 0; - } - - /* Completed the iMCU row, advance counters for next one */ - m_iMCU_row_num++; - start_iMCU_row(); - return true; - } - - // Reset within-iMCU-row counters for a new row - private void start_iMCU_row() - { - /* In an interleaved scan, an MCU row is the same as an iMCU row. - * In a noninterleaved scan, an iMCU row has v_samp_factor MCU rows. - * But at the bottom of the image, process only what's left. - */ - if (m_cinfo.m_comps_in_scan > 1) - { - m_MCU_rows_per_iMCU_row = 1; - } - else - { - if (m_iMCU_row_num < (m_cinfo.m_total_iMCU_rows - 1)) - m_MCU_rows_per_iMCU_row = m_cinfo.Component_info[m_cinfo.m_cur_comp_info[0]].V_samp_factor; - else - m_MCU_rows_per_iMCU_row = m_cinfo.Component_info[m_cinfo.m_cur_comp_info[0]].last_row_height; - } - - m_mcu_ctr = 0; - m_MCU_vert_offset = 0; - } - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/my_destination_mgr.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/my_destination_mgr.cs deleted file mode 100644 index 77946e018..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/my_destination_mgr.cs +++ /dev/null @@ -1,127 +0,0 @@ -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -/* - * This file contains compression data destination routines for the case of - * emitting JPEG data to a file (or any stdio stream). While these routines - * are sufficient for most applications, some will want to use a different - * destination manager. - * IMPORTANT: we assume that fwrite() will correctly transcribe an array of - * bytes into 8-bit-wide elements on external storage. If char is wider - * than 8 bits on your machine, you may need to do some tweaking. - */ - -using System; -using System.Collections.Generic; -using System.Text; -using System.IO; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// Expanded data destination object for output to Stream - /// - class my_destination_mgr : jpeg_destination_mgr - { - private const int OUTPUT_BUF_SIZE = 4096; /* choose an efficiently fwrite'able size */ - - private jpeg_compress_struct m_cinfo; - - private Stream m_outfile; /* target stream */ - private byte[] m_buffer; /* start of buffer */ - - public my_destination_mgr(jpeg_compress_struct cinfo, Stream alreadyOpenFile) - { - m_cinfo = cinfo; - m_outfile = alreadyOpenFile; - } - - /// - /// Initialize destination --- called by jpeg_start_compress - /// before any data is actually written. - /// - public override void init_destination() - { - /* Allocate the output buffer --- it will be released when done with image */ - m_buffer = new byte[OUTPUT_BUF_SIZE]; - initInternalBuffer(m_buffer, 0); - } - - /// - /// Empty the output buffer --- called whenever buffer fills up. - /// - /// In typical applications, this should write the entire output buffer - /// (ignoring the current state of next_output_byte and free_in_buffer), - /// reset the pointer and count to the start of the buffer, and return true - /// indicating that the buffer has been dumped. - /// - /// In applications that need to be able to suspend compression due to output - /// overrun, a false return indicates that the buffer cannot be emptied now. - /// In this situation, the compressor will return to its caller (possibly with - /// an indication that it has not accepted all the supplied scanlines). The - /// application should resume compression after it has made more room in the - /// output buffer. Note that there are substantial restrictions on the use of - /// suspension --- see the documentation. - /// - /// When suspending, the compressor will back up to a convenient restart point - /// (typically the start of the current MCU). next_output_byte and free_in_buffer - /// indicate where the restart point will be if the current call returns false. - /// Data beyond this point will be regenerated after resumption, so do not - /// write it out when emptying the buffer externally. - /// - public override bool empty_output_buffer() - { - writeBuffer(m_buffer.Length); - initInternalBuffer(m_buffer, 0); - return true; - } - - /// - /// Terminate destination --- called by jpeg_finish_compress - /// after all data has been written. Usually needs to flush buffer. - /// - /// NB: *not* called by jpeg_abort or jpeg_destroy; surrounding - /// application must deal with any cleanup that should happen even - /// for error exit. - /// - public override void term_destination() - { - int datacount = m_buffer.Length - freeInBuffer; - - /* Write any data remaining in the buffer */ - if (datacount > 0) - writeBuffer(datacount); - - m_outfile.Flush(); - } - - private void writeBuffer(int dataCount) - { - try - { - m_outfile.Write(m_buffer, 0, dataCount); - } - catch (IOException e) - { - m_cinfo.TRACEMS(0, J_MESSAGE_CODE.JERR_FILE_WRITE, e.Message); - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_FILE_WRITE); - } - catch (NotSupportedException e) - { - m_cinfo.TRACEMS(0, J_MESSAGE_CODE.JERR_FILE_WRITE, e.Message); - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_FILE_WRITE); - } - catch (ObjectDisposedException e) - { - m_cinfo.TRACEMS(0, J_MESSAGE_CODE.JERR_FILE_WRITE, e.Message); - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_FILE_WRITE); - } - } - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/my_merged_upsampler.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/my_merged_upsampler.cs deleted file mode 100644 index e7aaeb699..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/my_merged_upsampler.cs +++ /dev/null @@ -1,377 +0,0 @@ -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -/* - * This file contains code for merged upsampling/color conversion. - * - * This file combines functions from my_upsampler and jpeg_color_deconverter; - * read those files first to understand what's going on. - * - * When the chroma components are to be upsampled by simple replication - * (ie, box filtering), we can save some work in color conversion by - * calculating all the output pixels corresponding to a pair of chroma - * samples at one time. In the conversion equations - * R = Y + K1 * Cr - * G = Y + K2 * Cb + K3 * Cr - * B = Y + K4 * Cb - * only the Y term varies among the group of pixels corresponding to a pair - * of chroma samples, so the rest of the terms can be calculated just once. - * At typical sampling ratios, this eliminates half or three-quarters of the - * multiplications needed for color conversion. - * - * This file currently provides implementations for the following cases: - * YCbCr => RGB color conversion only. - * Sampling ratios of 2h1v or 2h2v. - * No scaling needed at upsample time. - * Corner-aligned (non-CCIR601) sampling alignment. - * Other special cases could be added, but in most applications these are - * the only common cases. (For uncommon cases we fall back on the more - * general code in my_upsampler and jpeg_color_deconverter) - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - class my_merged_upsampler : jpeg_upsampler - { - private const int SCALEBITS = 16; /* speediest right-shift on some machines */ - private const int ONE_HALF = 1 << (SCALEBITS - 1); - - private jpeg_decompress_struct m_cinfo; - - private bool m_use_2v_upsample; - - /* Private state for YCC->RGB conversion */ - private int[] m_Cr_r_tab; /* => table for Cr to R conversion */ - private int[] m_Cb_b_tab; /* => table for Cb to B conversion */ - private int[] m_Cr_g_tab; /* => table for Cr to G conversion */ - private int[] m_Cb_g_tab; /* => table for Cb to G conversion */ - - /* For 2:1 vertical sampling, we produce two output rows at a time. - * We need a "spare" row buffer to hold the second output row if the - * application provides just a one-row buffer; we also use the spare - * to discard the dummy last row if the image height is odd. - */ - private byte[] m_spare_row; - private bool m_spare_full; /* T if spare buffer is occupied */ - - private int m_out_row_width; /* samples per output row */ - private int m_rows_to_go; /* counts rows remaining in image */ - - public my_merged_upsampler(jpeg_decompress_struct cinfo) - { - m_cinfo = cinfo; - m_need_context_rows = false; - - m_out_row_width = cinfo.m_output_width * cinfo.m_out_color_components; - - if (cinfo.m_max_v_samp_factor == 2) - { - m_use_2v_upsample = true; - /* Allocate a spare row buffer */ - m_spare_row = new byte[m_out_row_width]; - } - else - { - m_use_2v_upsample = false; - } - - build_ycc_rgb_table(); - } - - /// - /// Initialize for an upsampling pass. - /// - public override void start_pass() - { - /* Mark the spare buffer empty */ - m_spare_full = false; - - /* Initialize total-height counter for detecting bottom of image */ - m_rows_to_go = m_cinfo.m_output_height; - } - - public override void upsample(ComponentBuffer[] input_buf, ref int in_row_group_ctr, int in_row_groups_avail, byte[][] output_buf, ref int out_row_ctr, int out_rows_avail) - { - if (m_use_2v_upsample) - merged_2v_upsample(input_buf, ref in_row_group_ctr, output_buf, ref out_row_ctr, out_rows_avail); - else - merged_1v_upsample(input_buf, ref in_row_group_ctr, output_buf, ref out_row_ctr); - } - - /// - /// Control routine to do upsampling (and color conversion). - /// The control routine just handles the row buffering considerations. - /// 1:1 vertical sampling case: much easier, never need a spare row. - /// - private void merged_1v_upsample(ComponentBuffer[] input_buf, ref int in_row_group_ctr, byte[][] output_buf, ref int out_row_ctr) - { - /* Just do the upsampling. */ - h2v1_merged_upsample(input_buf, in_row_group_ctr, output_buf, out_row_ctr); - - /* Adjust counts */ - out_row_ctr++; - in_row_group_ctr++; - } - - /// - /// Control routine to do upsampling (and color conversion). - /// The control routine just handles the row buffering considerations. - /// 2:1 vertical sampling case: may need a spare row. - /// - private void merged_2v_upsample(ComponentBuffer[] input_buf, ref int in_row_group_ctr, byte[][] output_buf, ref int out_row_ctr, int out_rows_avail) - { - int num_rows; /* number of rows returned to caller */ - if (m_spare_full) - { - /* If we have a spare row saved from a previous cycle, just return it. */ - byte[][] temp = new byte[1][]; - temp[0] = m_spare_row; - JpegUtils.jcopy_sample_rows(temp, 0, output_buf, out_row_ctr, 1, m_out_row_width); - num_rows = 1; - m_spare_full = false; - } - else - { - /* Figure number of rows to return to caller. */ - num_rows = 2; - - /* Not more than the distance to the end of the image. */ - if (num_rows > m_rows_to_go) - num_rows = m_rows_to_go; - - /* And not more than what the client can accept: */ - out_rows_avail -= out_row_ctr; - if (num_rows > out_rows_avail) - num_rows = out_rows_avail; - - /* Create output pointer array for upsampler. */ - byte[][] work_ptrs = new byte[2][]; - work_ptrs[0] = output_buf[out_row_ctr]; - if (num_rows > 1) - { - work_ptrs[1] = output_buf[out_row_ctr + 1]; - } - else - { - work_ptrs[1] = m_spare_row; - m_spare_full = true; - } - - /* Now do the upsampling. */ - h2v2_merged_upsample(input_buf, in_row_group_ctr, work_ptrs); - } - - /* Adjust counts */ - out_row_ctr += num_rows; - m_rows_to_go -= num_rows; - - /* When the buffer is emptied, declare this input row group consumed */ - if (!m_spare_full) - in_row_group_ctr++; - } - - /* - * These are the routines invoked by the control routines to do - * the actual upsampling/conversion. One row group is processed per call. - * - * Note: since we may be writing directly into application-supplied buffers, - * we have to be honest about the output width; we can't assume the buffer - * has been rounded up to an even width. - */ - - /// - /// Upsample and color convert for the case of 2:1 horizontal and 1:1 vertical. - /// - private void h2v1_merged_upsample(ComponentBuffer[] input_buf, int in_row_group_ctr, byte[][] output_buf, int outRow) - { - int inputIndex0 = 0; - int inputIndex1 = 0; - int inputIndex2 = 0; - int outputIndex = 0; - - byte[] limit = m_cinfo.m_sample_range_limit; - int limitOffset = m_cinfo.m_sampleRangeLimitOffset; - - /* Loop for each pair of output pixels */ - for (int col = m_cinfo.m_output_width >> 1; col > 0; col--) - { - /* Do the chroma part of the calculation */ - int cb = input_buf[1][in_row_group_ctr][inputIndex1]; - inputIndex1++; - - int cr = input_buf[2][in_row_group_ctr][inputIndex2]; - inputIndex2++; - - int cred = m_Cr_r_tab[cr]; - int cgreen = JpegUtils.RIGHT_SHIFT(m_Cb_g_tab[cb] + m_Cr_g_tab[cr], SCALEBITS); - int cblue = m_Cb_b_tab[cb]; - - /* Fetch 2 Y values and emit 2 pixels */ - int y = input_buf[0][in_row_group_ctr][inputIndex0]; - inputIndex0++; - - output_buf[outRow][outputIndex + JpegConstants.RGB_RED] = limit[limitOffset + y + cred]; - output_buf[outRow][outputIndex + JpegConstants.RGB_GREEN] = limit[limitOffset + y + cgreen]; - output_buf[outRow][outputIndex + JpegConstants.RGB_BLUE] = limit[limitOffset + y + cblue]; - outputIndex += JpegConstants.RGB_PIXELSIZE; - - y = input_buf[0][in_row_group_ctr][inputIndex0]; - inputIndex0++; - - output_buf[outRow][outputIndex + JpegConstants.RGB_RED] = limit[limitOffset + y + cred]; - output_buf[outRow][outputIndex + JpegConstants.RGB_GREEN] = limit[limitOffset + y + cgreen]; - output_buf[outRow][outputIndex + JpegConstants.RGB_BLUE] = limit[limitOffset + y + cblue]; - outputIndex += JpegConstants.RGB_PIXELSIZE; - } - - /* If image width is odd, do the last output column separately */ - if ((m_cinfo.m_output_width & 1) != 0) - { - int cb = input_buf[1][in_row_group_ctr][inputIndex1]; - int cr = input_buf[2][in_row_group_ctr][inputIndex2]; - int cred = m_Cr_r_tab[cr]; - int cgreen = JpegUtils.RIGHT_SHIFT(m_Cb_g_tab[cb] + m_Cr_g_tab[cr], SCALEBITS); - int cblue = m_Cb_b_tab[cb]; - - int y = input_buf[0][in_row_group_ctr][inputIndex0]; - output_buf[outRow][outputIndex + JpegConstants.RGB_RED] = limit[limitOffset + y + cred]; - output_buf[outRow][outputIndex + JpegConstants.RGB_GREEN] = limit[limitOffset + y + cgreen]; - output_buf[outRow][outputIndex + JpegConstants.RGB_BLUE] = limit[limitOffset + y + cblue]; - } - } - - /// - /// Upsample and color convert for the case of 2:1 horizontal and 2:1 vertical. - /// - private void h2v2_merged_upsample(ComponentBuffer[] input_buf, int in_row_group_ctr, byte[][] output_buf) - { - int inputRow00 = in_row_group_ctr * 2; - int inputIndex00 = 0; - - int inputRow01 = in_row_group_ctr * 2 + 1; - int inputIndex01 = 0; - - int inputIndex1 = 0; - int inputIndex2 = 0; - - int outIndex0 = 0; - int outIndex1 = 0; - - byte[] limit = m_cinfo.m_sample_range_limit; - int limitOffset = m_cinfo.m_sampleRangeLimitOffset; - - /* Loop for each group of output pixels */ - for (int col = m_cinfo.m_output_width >> 1; col > 0; col--) - { - /* Do the chroma part of the calculation */ - int cb = input_buf[1][in_row_group_ctr][inputIndex1]; - inputIndex1++; - - int cr = input_buf[2][in_row_group_ctr][inputIndex2]; - inputIndex2++; - - int cred = m_Cr_r_tab[cr]; - int cgreen = JpegUtils.RIGHT_SHIFT(m_Cb_g_tab[cb] + m_Cr_g_tab[cr], SCALEBITS); - int cblue = m_Cb_b_tab[cb]; - - /* Fetch 4 Y values and emit 4 pixels */ - int y = input_buf[0][inputRow00][inputIndex00]; - inputIndex00++; - - output_buf[0][outIndex0 + JpegConstants.RGB_RED] = limit[limitOffset + y + cred]; - output_buf[0][outIndex0 + JpegConstants.RGB_GREEN] = limit[limitOffset + y + cgreen]; - output_buf[0][outIndex0 + JpegConstants.RGB_BLUE] = limit[limitOffset + y + cblue]; - outIndex0 += JpegConstants.RGB_PIXELSIZE; - - y = input_buf[0][inputRow00][inputIndex00]; - inputIndex00++; - - output_buf[0][outIndex0 + JpegConstants.RGB_RED] = limit[limitOffset + y + cred]; - output_buf[0][outIndex0 + JpegConstants.RGB_GREEN] = limit[limitOffset + y + cgreen]; - output_buf[0][outIndex0 + JpegConstants.RGB_BLUE] = limit[limitOffset + y + cblue]; - outIndex0 += JpegConstants.RGB_PIXELSIZE; - - y = input_buf[0][inputRow01][inputIndex01]; - inputIndex01++; - - output_buf[1][outIndex1 + JpegConstants.RGB_RED] = limit[limitOffset + y + cred]; - output_buf[1][outIndex1 + JpegConstants.RGB_GREEN] = limit[limitOffset + y + cgreen]; - output_buf[1][outIndex1 + JpegConstants.RGB_BLUE] = limit[limitOffset + y + cblue]; - outIndex1 += JpegConstants.RGB_PIXELSIZE; - - y = input_buf[0][inputRow01][inputIndex01]; - inputIndex01++; - - output_buf[1][outIndex1 + JpegConstants.RGB_RED] = limit[limitOffset + y + cred]; - output_buf[1][outIndex1 + JpegConstants.RGB_GREEN] = limit[limitOffset + y + cgreen]; - output_buf[1][outIndex1 + JpegConstants.RGB_BLUE] = limit[limitOffset + y + cblue]; - outIndex1 += JpegConstants.RGB_PIXELSIZE; - } - - /* If image width is odd, do the last output column separately */ - if ((m_cinfo.m_output_width & 1) != 0) - { - int cb = input_buf[1][in_row_group_ctr][inputIndex1]; - int cr = input_buf[2][in_row_group_ctr][inputIndex2]; - int cred = m_Cr_r_tab[cr]; - int cgreen = JpegUtils.RIGHT_SHIFT(m_Cb_g_tab[cb] + m_Cr_g_tab[cr], SCALEBITS); - int cblue = m_Cb_b_tab[cb]; - - int y = input_buf[0][inputRow00][inputIndex00]; - output_buf[0][outIndex0 + JpegConstants.RGB_RED] = limit[limitOffset + y + cred]; - output_buf[0][outIndex0 + JpegConstants.RGB_GREEN] = limit[limitOffset + y + cgreen]; - output_buf[0][outIndex0 + JpegConstants.RGB_BLUE] = limit[limitOffset + y + cblue]; - - y = input_buf[0][inputRow01][inputIndex01]; - output_buf[1][outIndex1 + JpegConstants.RGB_RED] = limit[limitOffset + y + cred]; - output_buf[1][outIndex1 + JpegConstants.RGB_GREEN] = limit[limitOffset + y + cgreen]; - output_buf[1][outIndex1 + JpegConstants.RGB_BLUE] = limit[limitOffset + y + cblue]; - } - } - - /// - /// Initialize tables for YCC->RGB colorspace conversion. - /// This is taken directly from jpeg_color_deconverter; see that file for more info. - /// - private void build_ycc_rgb_table() - { - m_Cr_r_tab = new int[JpegConstants.MAXJSAMPLE + 1]; - m_Cb_b_tab = new int[JpegConstants.MAXJSAMPLE + 1]; - m_Cr_g_tab = new int[JpegConstants.MAXJSAMPLE + 1]; - m_Cb_g_tab = new int[JpegConstants.MAXJSAMPLE + 1]; - - for (int i = 0, x = -JpegConstants.CENTERJSAMPLE; i <= JpegConstants.MAXJSAMPLE; i++, x++) - { - /* i is the actual input pixel value, in the range 0..MAXJSAMPLE */ - /* The Cb or Cr value we are thinking of is x = i - CENTERJSAMPLE */ - /* Cr=>R value is nearest int to 1.40200 * x */ - m_Cr_r_tab[i] = JpegUtils.RIGHT_SHIFT(FIX(1.40200) * x + ONE_HALF, SCALEBITS); - - /* Cb=>B value is nearest int to 1.77200 * x */ - m_Cb_b_tab[i] = JpegUtils.RIGHT_SHIFT(FIX(1.77200) * x + ONE_HALF, SCALEBITS); - - /* Cr=>G value is scaled-up -0.71414 * x */ - m_Cr_g_tab[i] = (-FIX(0.71414)) * x; - - /* Cb=>G value is scaled-up -0.34414 * x */ - /* We also add in ONE_HALF so that need not do it in inner loop */ - m_Cb_g_tab[i] = (-FIX(0.34414)) * x + ONE_HALF; - } - } - - private static int FIX(double x) - { - return ((int)((x) * (1L << SCALEBITS) + 0.5)); - } - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/my_source_mgr.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/my_source_mgr.cs deleted file mode 100644 index d69e92e28..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/my_source_mgr.cs +++ /dev/null @@ -1,119 +0,0 @@ -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -/* - * This file contains decompression data source routines for the case of - * reading JPEG data from a file (or any stdio stream). While these routines - * are sufficient for most applications, some will want to use a different - * source manager. - * IMPORTANT: we assume that fread() will correctly transcribe an array of - * bytes from 8-bit-wide elements on external storage. If char is wider - * than 8 bits on your machine, you may need to do some tweaking. - */ - -using System; -using System.Collections.Generic; -using System.Text; -using System.IO; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// Expanded data source object for stdio input - /// - class my_source_mgr : jpeg_source_mgr - { - private const int INPUT_BUF_SIZE = 4096; - - private jpeg_decompress_struct m_cinfo; - - private Stream m_infile; /* source stream */ - private byte[] m_buffer; /* start of buffer */ - private bool m_start_of_file; /* have we gotten any data yet? */ - - /// - /// Initialize source - called by jpeg_read_header - /// before any data is actually read. - /// - public my_source_mgr(jpeg_decompress_struct cinfo) - { - m_cinfo = cinfo; - m_buffer = new byte[INPUT_BUF_SIZE]; - } - - public void Attach(Stream infile) - { - m_infile = infile; - m_infile.Seek(0, SeekOrigin.Begin); - initInternalBuffer(null, 0); - } - - public override void init_source() - { - /* We reset the empty-input-file flag for each image, - * but we don't clear the input buffer. - * This is correct behavior for reading a series of images from one source. - */ - m_start_of_file = true; - } - - /// - /// Fill the input buffer - called whenever buffer is emptied. - /// - /// In typical applications, this should read fresh data into the buffer - /// (ignoring the current state of next_input_byte and bytes_in_buffer), - /// reset the pointer and count to the start of the buffer, and return true - /// indicating that the buffer has been reloaded. It is not necessary to - /// fill the buffer entirely, only to obtain at least one more byte. - /// - /// There is no such thing as an EOF return. If the end of the file has been - /// reached, the routine has a choice of ERREXIT() or inserting fake data into - /// the buffer. In most cases, generating a warning message and inserting a - /// fake EOI marker is the best course of action --- this will allow the - /// decompressor to output however much of the image is there. However, - /// the resulting error message is misleading if the real problem is an empty - /// input file, so we handle that case specially. - /// - /// In applications that need to be able to suspend compression due to input - /// not being available yet, a false return indicates that no more data can be - /// obtained right now, but more may be forthcoming later. In this situation, - /// the decompressor will return to its caller (with an indication of the - /// number of scanlines it has read, if any). The application should resume - /// decompression after it has loaded more data into the input buffer. Note - /// that there are substantial restrictions on the use of suspension --- see - /// the documentation. - /// - /// When suspending, the decompressor will back up to a convenient restart point - /// (typically the start of the current MCU). next_input_byte and bytes_in_buffer - /// indicate where the restart point will be if the current call returns false. - /// Data beyond this point must be rescanned after resumption, so move it to - /// the front of the buffer rather than discarding it. - /// - public override bool fill_input_buffer() - { - int nbytes = m_infile.Read(m_buffer, 0, INPUT_BUF_SIZE); - if (nbytes <= 0) - { - if (m_start_of_file) /* Treat empty input file as fatal error */ - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_INPUT_EMPTY); - - m_cinfo.WARNMS(J_MESSAGE_CODE.JWRN_JPEG_EOF); - /* Insert a fake EOI marker */ - m_buffer[0] = (byte)0xFF; - m_buffer[1] = (byte)JPEG_MARKER.EOI; - nbytes = 2; - } - - initInternalBuffer(m_buffer, nbytes); - m_start_of_file = false; - - return true; - } - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/my_trans_c_coef_controller.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/my_trans_c_coef_controller.cs deleted file mode 100644 index 28cc3bfd9..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/my_trans_c_coef_controller.cs +++ /dev/null @@ -1,200 +0,0 @@ -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -/* - * This file contains library routines for transcoding compression, - * that is, writing raw DCT coefficient arrays to an output JPEG file. - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// This is a special implementation of the coefficient - /// buffer controller. This is similar to jccoefct.c, but it handles only - /// output from presupplied virtual arrays. Furthermore, we generate any - /// dummy padding blocks on-the-fly rather than expecting them to be present - /// in the arrays. - /// - class my_trans_c_coef_controller : jpeg_c_coef_controller - { - private jpeg_compress_struct m_cinfo; - - private int m_iMCU_row_num; /* iMCU row # within image */ - private int m_mcu_ctr; /* counts MCUs processed in current row */ - private int m_MCU_vert_offset; /* counts MCU rows within iMCU row */ - private int m_MCU_rows_per_iMCU_row; /* number of such rows needed */ - - /* Virtual block array for each component. */ - private jvirt_array[] m_whole_image; - - /* Workspace for constructing dummy blocks at right/bottom edges. */ - private JBLOCK[][] m_dummy_buffer = new JBLOCK[JpegConstants.C_MAX_BLOCKS_IN_MCU][]; - - /// - /// Initialize coefficient buffer controller. - /// - /// Each passed coefficient array must be the right size for that - /// coefficient: width_in_blocks wide and height_in_blocks high, - /// with unit height at least v_samp_factor. - /// - public my_trans_c_coef_controller(jpeg_compress_struct cinfo, jvirt_array[] coef_arrays) - { - m_cinfo = cinfo; - - /* Save pointer to virtual arrays */ - m_whole_image = coef_arrays; - - /* Allocate and pre-zero space for dummy DCT blocks. */ - JBLOCK[] buffer = new JBLOCK[JpegConstants.C_MAX_BLOCKS_IN_MCU]; - for (int i = 0; i < JpegConstants.C_MAX_BLOCKS_IN_MCU; i++) - buffer[i] = new JBLOCK(); - - for (int i = 0; i < JpegConstants.C_MAX_BLOCKS_IN_MCU; i++) - { - m_dummy_buffer[i] = new JBLOCK[JpegConstants.C_MAX_BLOCKS_IN_MCU - i]; - for (int j = i; j < JpegConstants.C_MAX_BLOCKS_IN_MCU; j++) - m_dummy_buffer[i][j - i] = buffer[j]; - } - } - - /// - /// Initialize for a processing pass. - /// - public virtual void start_pass(J_BUF_MODE pass_mode) - { - if (pass_mode != J_BUF_MODE.JBUF_CRANK_DEST) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_BUFFER_MODE); - - m_iMCU_row_num = 0; - start_iMCU_row(); - } - - /// - /// Process some data. - /// We process the equivalent of one fully interleaved MCU row ("iMCU" row) - /// per call, ie, v_samp_factor block rows for each component in the scan. - /// The data is obtained from the virtual arrays and fed to the entropy coder. - /// Returns true if the iMCU row is completed, false if suspended. - /// - /// NB: input_buf is ignored; it is likely to be a null pointer. - /// - public virtual bool compress_data(byte[][][] input_buf) - { - /* Align the virtual buffers for the components used in this scan. */ - JBLOCK[][][] buffer = new JBLOCK[JpegConstants.MAX_COMPS_IN_SCAN][][]; - for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) - { - jpeg_component_info componentInfo = m_cinfo.Component_info[m_cinfo.m_cur_comp_info[ci]]; - buffer[ci] = m_whole_image[componentInfo.Component_index].Access( - m_iMCU_row_num * componentInfo.V_samp_factor, componentInfo.V_samp_factor); - } - - /* Loop to process one whole iMCU row */ - int last_MCU_col = m_cinfo.m_MCUs_per_row - 1; - int last_iMCU_row = m_cinfo.m_total_iMCU_rows - 1; - JBLOCK[][] MCU_buffer = new JBLOCK[JpegConstants.C_MAX_BLOCKS_IN_MCU][]; - for (int yoffset = m_MCU_vert_offset; yoffset < m_MCU_rows_per_iMCU_row; yoffset++) - { - for (int MCU_col_num = m_mcu_ctr; MCU_col_num < m_cinfo.m_MCUs_per_row; MCU_col_num++) - { - /* Construct list of pointers to DCT blocks belonging to this MCU */ - int blkn = 0; /* index of current DCT block within MCU */ - for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) - { - jpeg_component_info componentInfo = m_cinfo.Component_info[m_cinfo.m_cur_comp_info[ci]]; - int start_col = MCU_col_num * componentInfo.MCU_width; - int blockcnt = (MCU_col_num < last_MCU_col) ? componentInfo.MCU_width : componentInfo.last_col_width; - for (int yindex = 0; yindex < componentInfo.MCU_height; yindex++) - { - int xindex = 0; - if (m_iMCU_row_num < last_iMCU_row || yindex + yoffset < componentInfo.last_row_height) - { - /* Fill in pointers to real blocks in this row */ - for (xindex = 0; xindex < blockcnt; xindex++) - { - int bufLength = buffer[ci][yindex + yoffset].Length; - int start = start_col + xindex; - MCU_buffer[blkn] = new JBLOCK[bufLength - start]; - for (int j = start; j < bufLength; j++) - MCU_buffer[blkn][j - start] = buffer[ci][yindex + yoffset][j]; - - blkn++; - } - } - else - { - /* At bottom of image, need a whole row of dummy blocks */ - xindex = 0; - } - - /* Fill in any dummy blocks needed in this row. - * Dummy blocks are filled in the same way as in jccoefct.c: - * all zeroes in the AC entries, DC entries equal to previous - * block's DC value. The init routine has already zeroed the - * AC entries, so we need only set the DC entries correctly. - */ - for (; xindex < componentInfo.MCU_width; xindex++) - { - MCU_buffer[blkn] = m_dummy_buffer[blkn]; - MCU_buffer[blkn][0][0] = MCU_buffer[blkn - 1][0][0]; - blkn++; - } - } - } - - /* Try to write the MCU. */ - if (!m_cinfo.m_entropy.encode_mcu(MCU_buffer)) - { - /* Suspension forced; update state counters and exit */ - m_MCU_vert_offset = yoffset; - m_mcu_ctr = MCU_col_num; - return false; - } - } - - /* Completed an MCU row, but perhaps not an iMCU row */ - m_mcu_ctr = 0; - } - - /* Completed the iMCU row, advance counters for next one */ - m_iMCU_row_num++; - start_iMCU_row(); - return true; - } - - /// - /// Reset within-iMCU-row counters for a new row - /// - private void start_iMCU_row() - { - /* In an interleaved scan, an MCU row is the same as an iMCU row. - * In a noninterleaved scan, an iMCU row has v_samp_factor MCU rows. - * But at the bottom of the image, process only what's left. - */ - if (m_cinfo.m_comps_in_scan > 1) - { - m_MCU_rows_per_iMCU_row = 1; - } - else - { - if (m_iMCU_row_num < (m_cinfo.m_total_iMCU_rows - 1)) - m_MCU_rows_per_iMCU_row = m_cinfo.Component_info[m_cinfo.m_cur_comp_info[0]].V_samp_factor; - else - m_MCU_rows_per_iMCU_row = m_cinfo.Component_info[m_cinfo.m_cur_comp_info[0]].last_row_height; - } - - m_mcu_ctr = 0; - m_MCU_vert_offset = 0; - } - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/my_upsampler.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/my_upsampler.cs deleted file mode 100644 index adae0897f..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/my_upsampler.cs +++ /dev/null @@ -1,521 +0,0 @@ -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -/* - * This file contains upsampling routines. - * - * Upsampling input data is counted in "row groups". A row group - * is defined to be (v_samp_factor * DCT_scaled_size / min_DCT_scaled_size) - * sample rows of each component. Upsampling will normally produce - * max_v_samp_factor pixel rows from each row group (but this could vary - * if the upsampler is applying a scale factor of its own). - * - * An excellent reference for image resampling is - * Digital Image Warping, George Wolberg, 1990. - * Pub. by IEEE Computer Society Press, Los Alamitos, CA. ISBN 0-8186-8944-7. - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - class my_upsampler : jpeg_upsampler - { - private enum ComponentUpsampler - { - noop_upsampler, - fullsize_upsampler, - h2v1_fancy_upsampler, - h2v1_upsampler, - h2v2_fancy_upsampler, - h2v2_upsampler, - int_upsampler - } - - private jpeg_decompress_struct m_cinfo; - - /* Color conversion buffer. When using separate upsampling and color - * conversion steps, this buffer holds one upsampled row group until it - * has been color converted and output. - * Note: we do not allocate any storage for component(s) which are full-size, - * ie do not need rescaling. The corresponding entry of color_buf[] is - * simply set to point to the input data array, thereby avoiding copying. - */ - private ComponentBuffer[] m_color_buf = new ComponentBuffer[JpegConstants.MAX_COMPONENTS]; - - // used only for fullsize_upsampler mode - private int[] m_perComponentOffsets = new int[JpegConstants.MAX_COMPONENTS]; - - /* Per-component upsampling method pointers */ - private ComponentUpsampler[] m_upsampleMethods = new ComponentUpsampler[JpegConstants.MAX_COMPONENTS]; - private int m_currentComponent; // component being upsampled - private int m_upsampleRowOffset; - - private int m_next_row_out; /* counts rows emitted from color_buf */ - private int m_rows_to_go; /* counts rows remaining in image */ - - /* Height of an input row group for each component. */ - private int[] m_rowgroup_height = new int[JpegConstants.MAX_COMPONENTS]; - - /* These arrays save pixel expansion factors so that int_expand need not - * recompute them each time. They are unused for other upsampling methods. - */ - private byte[] m_h_expand = new byte[JpegConstants.MAX_COMPONENTS]; - private byte[] m_v_expand = new byte[JpegConstants.MAX_COMPONENTS]; - - public my_upsampler(jpeg_decompress_struct cinfo) - { - m_cinfo = cinfo; - m_need_context_rows = false; /* until we find out differently */ - - if (cinfo.m_CCIR601_sampling) /* this isn't supported */ - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CCIR601_NOTIMPL); - - /* jpeg_d_main_controller doesn't support context rows when min_DCT_scaled_size = 1, - * so don't ask for it. - */ - bool do_fancy = cinfo.m_do_fancy_upsampling && cinfo.m_min_DCT_scaled_size > 1; - - /* Verify we can handle the sampling factors, select per-component methods, - * and create storage as needed. - */ - for (int ci = 0; ci < cinfo.m_num_components; ci++) - { - jpeg_component_info componentInfo = cinfo.Comp_info[ci]; - - /* Compute size of an "input group" after IDCT scaling. This many samples - * are to be converted to max_h_samp_factor * max_v_samp_factor pixels. - */ - int h_in_group = (componentInfo.H_samp_factor * componentInfo.DCT_scaled_size) / cinfo.m_min_DCT_scaled_size; - int v_in_group = (componentInfo.V_samp_factor * componentInfo.DCT_scaled_size) / cinfo.m_min_DCT_scaled_size; - int h_out_group = cinfo.m_max_h_samp_factor; - int v_out_group = cinfo.m_max_v_samp_factor; - - /* save for use later */ - m_rowgroup_height[ci] = v_in_group; - bool need_buffer = true; - if (!componentInfo.component_needed) - { - /* Don't bother to upsample an uninteresting component. */ - m_upsampleMethods[ci] = ComponentUpsampler.noop_upsampler; - need_buffer = false; - } - else if (h_in_group == h_out_group && v_in_group == v_out_group) - { - /* Fullsize components can be processed without any work. */ - m_upsampleMethods[ci] = ComponentUpsampler.fullsize_upsampler; - need_buffer = false; - } - else if (h_in_group * 2 == h_out_group && v_in_group == v_out_group) - { - /* Special cases for 2h1v upsampling */ - if (do_fancy && componentInfo.downsampled_width > 2) - m_upsampleMethods[ci] = ComponentUpsampler.h2v1_fancy_upsampler; - else - m_upsampleMethods[ci] = ComponentUpsampler.h2v1_upsampler; - } - else if (h_in_group * 2 == h_out_group && v_in_group * 2 == v_out_group) - { - /* Special cases for 2h2v upsampling */ - if (do_fancy && componentInfo.downsampled_width > 2) - { - m_upsampleMethods[ci] = ComponentUpsampler.h2v2_fancy_upsampler; - m_need_context_rows = true; - } - else - { - m_upsampleMethods[ci] = ComponentUpsampler.h2v2_upsampler; - } - } - else if ((h_out_group % h_in_group) == 0 && (v_out_group % v_in_group) == 0) - { - /* Generic integral-factors upsampling method */ - m_upsampleMethods[ci] = ComponentUpsampler.int_upsampler; - m_h_expand[ci] = (byte) (h_out_group / h_in_group); - m_v_expand[ci] = (byte) (v_out_group / v_in_group); - } - else - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_FRACT_SAMPLE_NOTIMPL); - - if (need_buffer) - { - ComponentBuffer cb = new ComponentBuffer(); - cb.SetBuffer(jpeg_common_struct.AllocJpegSamples(JpegUtils.jround_up(cinfo.m_output_width, - cinfo.m_max_h_samp_factor), cinfo.m_max_v_samp_factor), null, 0); - - m_color_buf[ci] = cb; - } - } - } - - /// - /// Initialize for an upsampling pass. - /// - public override void start_pass() - { - /* Mark the conversion buffer empty */ - m_next_row_out = m_cinfo.m_max_v_samp_factor; - - /* Initialize total-height counter for detecting bottom of image */ - m_rows_to_go = m_cinfo.m_output_height; - } - - /// - /// Control routine to do upsampling (and color conversion). - /// - /// In this version we upsample each component independently. - /// We upsample one row group into the conversion buffer, then apply - /// color conversion a row at a time. - /// - public override void upsample(ComponentBuffer[] input_buf, ref int in_row_group_ctr, int in_row_groups_avail, byte[][] output_buf, ref int out_row_ctr, int out_rows_avail) - { - /* Fill the conversion buffer, if it's empty */ - if (m_next_row_out >= m_cinfo.m_max_v_samp_factor) - { - for (int ci = 0; ci < m_cinfo.m_num_components; ci++) - { - m_perComponentOffsets[ci] = 0; - - /* Invoke per-component upsample method.*/ - m_currentComponent = ci; - m_upsampleRowOffset = in_row_group_ctr * m_rowgroup_height[ci]; - upsampleComponent(ref input_buf[ci]); - } - - m_next_row_out = 0; - } - - /* Color-convert and emit rows */ - - /* How many we have in the buffer: */ - int num_rows = m_cinfo.m_max_v_samp_factor - m_next_row_out; - - /* Not more than the distance to the end of the image. Need this test - * in case the image height is not a multiple of max_v_samp_factor: - */ - if (num_rows > m_rows_to_go) - num_rows = m_rows_to_go; - - /* And not more than what the client can accept: */ - out_rows_avail -= out_row_ctr; - if (num_rows > out_rows_avail) - num_rows = out_rows_avail; - - m_cinfo.m_cconvert.color_convert(m_color_buf, m_perComponentOffsets, m_next_row_out, output_buf, out_row_ctr, num_rows); - - /* Adjust counts */ - out_row_ctr += num_rows; - m_rows_to_go -= num_rows; - m_next_row_out += num_rows; - - /* When the buffer is emptied, declare this input row group consumed */ - if (m_next_row_out >= m_cinfo.m_max_v_samp_factor) - in_row_group_ctr++; - } - - private void upsampleComponent(ref ComponentBuffer input_data) - { - switch (m_upsampleMethods[m_currentComponent]) - { - case ComponentUpsampler.noop_upsampler: - noop_upsample(); - break; - case ComponentUpsampler.fullsize_upsampler: - fullsize_upsample(ref input_data); - break; - case ComponentUpsampler.h2v1_fancy_upsampler: - h2v1_fancy_upsample(m_cinfo.Comp_info[m_currentComponent].downsampled_width, ref input_data); - break; - case ComponentUpsampler.h2v1_upsampler: - h2v1_upsample(ref input_data); - break; - case ComponentUpsampler.h2v2_fancy_upsampler: - h2v2_fancy_upsample(m_cinfo.Comp_info[m_currentComponent].downsampled_width, ref input_data); - break; - case ComponentUpsampler.h2v2_upsampler: - h2v2_upsample(ref input_data); - break; - case ComponentUpsampler.int_upsampler: - int_upsample(ref input_data); - break; - default: - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NOTIMPL); - break; - } - } - - /* - * These are the routines invoked to upsample pixel values - * of a single component. One row group is processed per call. - */ - - /// - /// This is a no-op version used for "uninteresting" components. - /// These components will not be referenced by color conversion. - /// - private static void noop_upsample() - { - // do nothing - } - - /// - /// For full-size components, we just make color_buf[ci] point at the - /// input buffer, and thus avoid copying any data. Note that this is - /// safe only because sep_upsample doesn't declare the input row group - /// "consumed" until we are done color converting and emitting it. - /// - private void fullsize_upsample(ref ComponentBuffer input_data) - { - m_color_buf[m_currentComponent] = input_data; - m_perComponentOffsets[m_currentComponent] = m_upsampleRowOffset; - } - - /// - /// Fancy processing for the common case of 2:1 horizontal and 1:1 vertical. - /// - /// The upsampling algorithm is linear interpolation between pixel centers, - /// also known as a "triangle filter". This is a good compromise between - /// speed and visual quality. The centers of the output pixels are 1/4 and 3/4 - /// of the way between input pixel centers. - /// - /// A note about the "bias" calculations: when rounding fractional values to - /// integer, we do not want to always round 0.5 up to the next integer. - /// If we did that, we'd introduce a noticeable bias towards larger values. - /// Instead, this code is arranged so that 0.5 will be rounded up or down at - /// alternate pixel locations (a simple ordered dither pattern). - /// - private void h2v1_fancy_upsample(int downsampled_width, ref ComponentBuffer input_data) - { - ComponentBuffer output_data = m_color_buf[m_currentComponent]; - - for (int inrow = 0; inrow < m_cinfo.m_max_v_samp_factor; inrow++) - { - int row = m_upsampleRowOffset + inrow; - int inIndex = 0; - - int outIndex = 0; - - /* Special case for first column */ - int invalue = input_data[row][inIndex]; - inIndex++; - - output_data[inrow][outIndex] = (byte)invalue; - outIndex++; - output_data[inrow][outIndex] = (byte)((invalue * 3 + (int)input_data[row][inIndex] + 2) >> 2); - outIndex++; - - for (int colctr = downsampled_width - 2; colctr > 0; colctr--) - { - /* General case: 3/4 * nearer pixel + 1/4 * further pixel */ - invalue = (int)input_data[row][inIndex] * 3; - inIndex++; - - output_data[inrow][outIndex] = (byte)((invalue + (int)input_data[row][inIndex - 2] + 1) >> 2); - outIndex++; - - output_data[inrow][outIndex] = (byte)((invalue + (int)input_data[row][inIndex] + 2) >> 2); - outIndex++; - } - - /* Special case for last column */ - invalue = input_data[row][inIndex]; - output_data[inrow][outIndex] = (byte)((invalue * 3 + (int)input_data[row][inIndex - 1] + 1) >> 2); - outIndex++; - output_data[inrow][outIndex] = (byte)invalue; - outIndex++; - } - } - - /// - /// Fast processing for the common case of 2:1 horizontal and 1:1 vertical. - /// It's still a box filter. - /// - private void h2v1_upsample(ref ComponentBuffer input_data) - { - ComponentBuffer output_data = m_color_buf[m_currentComponent]; - - for (int inrow = 0; inrow < m_cinfo.m_max_v_samp_factor; inrow++) - { - int row = m_upsampleRowOffset + inrow; - int outIndex = 0; - - for (int col = 0; outIndex < m_cinfo.m_output_width; col++) - { - byte invalue = input_data[row][col]; /* don't need GETJSAMPLE() here */ - output_data[inrow][outIndex] = invalue; - outIndex++; - output_data[inrow][outIndex] = invalue; - outIndex++; - } - } - } - - /// - /// Fancy processing for the common case of 2:1 horizontal and 2:1 vertical. - /// Again a triangle filter; see comments for h2v1 case, above. - /// - /// It is OK for us to reference the adjacent input rows because we demanded - /// context from the main buffer controller (see initialization code). - /// - private void h2v2_fancy_upsample(int downsampled_width, ref ComponentBuffer input_data) - { - ComponentBuffer output_data = m_color_buf[m_currentComponent]; - - int inrow = m_upsampleRowOffset; - int outrow = 0; - while (outrow < m_cinfo.m_max_v_samp_factor) - { - for (int v = 0; v < 2; v++) - { - // nearest input row index - int inIndex0 = 0; - - //next nearest input row index - int inIndex1 = 0; - int inRow1 = -1; - if (v == 0) - { - /* next nearest is row above */ - inRow1 = inrow - 1; - } - else - { - /* next nearest is row below */ - inRow1 = inrow + 1; - } - - int row = outrow; - int outIndex = 0; - outrow++; - - /* Special case for first column */ - int thiscolsum = (int)input_data[inrow][inIndex0] * 3 + (int)input_data[inRow1][inIndex1]; - inIndex0++; - inIndex1++; - - int nextcolsum = (int)input_data[inrow][inIndex0] * 3 + (int)input_data[inRow1][inIndex1]; - inIndex0++; - inIndex1++; - - output_data[row][outIndex] = (byte)((thiscolsum * 4 + 8) >> 4); - outIndex++; - - output_data[row][outIndex] = (byte)((thiscolsum * 3 + nextcolsum + 7) >> 4); - outIndex++; - - int lastcolsum = thiscolsum; - thiscolsum = nextcolsum; - - for (int colctr = downsampled_width - 2; colctr > 0; colctr--) - { - /* General case: 3/4 * nearer pixel + 1/4 * further pixel in each */ - /* dimension, thus 9/16, 3/16, 3/16, 1/16 overall */ - nextcolsum = (int)input_data[inrow][inIndex0] * 3 + (int)input_data[inRow1][inIndex1]; - inIndex0++; - inIndex1++; - - output_data[row][outIndex] = (byte)((thiscolsum * 3 + lastcolsum + 8) >> 4); - outIndex++; - - output_data[row][outIndex] = (byte)((thiscolsum * 3 + nextcolsum + 7) >> 4); - outIndex++; - - lastcolsum = thiscolsum; - thiscolsum = nextcolsum; - } - - /* Special case for last column */ - output_data[row][outIndex] = (byte)((thiscolsum * 3 + lastcolsum + 8) >> 4); - outIndex++; - output_data[row][outIndex] = (byte)((thiscolsum * 4 + 7) >> 4); - outIndex++; - } - - inrow++; - } - } - - /// - /// Fast processing for the common case of 2:1 horizontal and 2:1 vertical. - /// It's still a box filter. - /// - private void h2v2_upsample(ref ComponentBuffer input_data) - { - ComponentBuffer output_data = m_color_buf[m_currentComponent]; - - int inrow = 0; - int outrow = 0; - while (outrow < m_cinfo.m_max_v_samp_factor) - { - int row = m_upsampleRowOffset + inrow; - int outIndex = 0; - - for (int col = 0; col < m_cinfo.m_output_width; col++) - { - byte invalue = input_data[row][col]; /* don't need GETJSAMPLE() here */ - output_data[outrow][outIndex] = invalue; - outIndex++; - output_data[outrow][outIndex] = invalue; - outIndex++; - } - - JpegUtils.jcopy_sample_rows(output_data, outrow, output_data, outrow + 1, 1, m_cinfo.m_output_width); - inrow++; - outrow += 2; - } - } - - /// - /// This version handles any integral sampling ratios. - /// This is not used for typical JPEG files, so it need not be fast. - /// Nor, for that matter, is it particularly accurate: the algorithm is - /// simple replication of the input pixel onto the corresponding output - /// pixels. The hi-falutin sampling literature refers to this as a - /// "box filter". A box filter tends to introduce visible artifacts, - /// so if you are actually going to use 3:1 or 4:1 sampling ratios - /// you would be well advised to improve this code. - /// - private void int_upsample(ref ComponentBuffer input_data) - { - ComponentBuffer output_data = m_color_buf[m_currentComponent]; - int h_expand = m_h_expand[m_currentComponent]; - int v_expand = m_v_expand[m_currentComponent]; - - int inrow = 0; - int outrow = 0; - while (outrow < m_cinfo.m_max_v_samp_factor) - { - /* Generate one output row with proper horizontal expansion */ - int row = m_upsampleRowOffset + inrow; - for (int col = 0; col < m_cinfo.m_output_width; col++) - { - byte invalue = input_data[row][col]; /* don't need GETJSAMPLE() here */ - int outIndex = 0; - for (int h = h_expand; h > 0; h--) - { - output_data[outrow][outIndex] = invalue; - outIndex++; - } - } - - /* Generate any additional output rows by duplicating the first one */ - if (v_expand > 1) - { - JpegUtils.jcopy_sample_rows(output_data, outrow, output_data, - outrow + 1, v_expand - 1, m_cinfo.m_output_width); - } - - inrow++; - outrow += v_expand; - } - } - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/phuff_entropy_decoder.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/phuff_entropy_decoder.cs deleted file mode 100644 index 7d3a7a123..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/phuff_entropy_decoder.cs +++ /dev/null @@ -1,716 +0,0 @@ -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -/* - * This file contains Huffman entropy decoding routines for progressive JPEG. - * - * Much of the complexity here has to do with supporting input suspension. - * If the data source module demands suspension, we want to be able to back - * up to the start of the current MCU. To do this, we copy state variables - * into local working storage, and update them back to the permanent - * storage only upon successful completion of an MCU. - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// Expanded entropy decoder object for progressive Huffman decoding. - /// - /// The savable_state subrecord contains fields that change within an MCU, - /// but must not be updated permanently until we complete the MCU. - /// - class phuff_entropy_decoder : jpeg_entropy_decoder - { - private class savable_state - { - //savable_state operator=(savable_state src); - public int EOBRUN; /* remaining EOBs in EOBRUN */ - public int[] last_dc_val = new int[JpegConstants.MAX_COMPS_IN_SCAN]; /* last DC coef for each component */ - - public void Assign(savable_state ss) - { - EOBRUN = ss.EOBRUN; - Buffer.BlockCopy(ss.last_dc_val, 0, last_dc_val, 0, last_dc_val.Length * sizeof(int)); - } - } - - private enum MCUDecoder - { - mcu_DC_first_decoder, - mcu_AC_first_decoder, - mcu_DC_refine_decoder, - mcu_AC_refine_decoder - } - - private MCUDecoder m_decoder; - - /* These fields are loaded into local variables at start of each MCU. - * In case of suspension, we exit WITHOUT updating them. - */ - private bitread_perm_state m_bitstate; /* Bit buffer at start of MCU */ - private savable_state m_saved = new savable_state(); /* Other state at start of MCU */ - - /* These fields are NOT loaded into local working state. */ - private int m_restarts_to_go; /* MCUs left in this restart interval */ - - /* Pointers to derived tables (these workspaces have image lifespan) */ - private d_derived_tbl[] m_derived_tbls = new d_derived_tbl[JpegConstants.NUM_HUFF_TBLS]; - - private d_derived_tbl m_ac_derived_tbl; /* active table during an AC scan */ - - public phuff_entropy_decoder(jpeg_decompress_struct cinfo) - { - m_cinfo = cinfo; - - /* Mark derived tables unallocated */ - for (int i = 0; i < JpegConstants.NUM_HUFF_TBLS; i++) - m_derived_tbls[i] = null; - - /* Create progression status table */ - cinfo.m_coef_bits = new int[cinfo.m_num_components][]; - for (int i = 0; i < cinfo.m_num_components; i++) - cinfo.m_coef_bits[i] = new int[JpegConstants.DCTSIZE2]; - - for (int ci = 0; ci < cinfo.m_num_components; ci++) - { - for (int i = 0; i < JpegConstants.DCTSIZE2; i++) - cinfo.m_coef_bits[ci][i] = -1; - } - } - - /// - /// Initialize for a Huffman-compressed scan. - /// - public override void start_pass() - { - /* Validate scan parameters */ - bool bad = false; - bool is_DC_band = (m_cinfo.m_Ss == 0); - if (is_DC_band) - { - if (m_cinfo.m_Se != 0) - bad = true; - } - else - { - /* need not check Ss/Se < 0 since they came from unsigned bytes */ - if (m_cinfo.m_Ss > m_cinfo.m_Se || m_cinfo.m_Se >= JpegConstants.DCTSIZE2) - bad = true; - - /* AC scans may have only one component */ - if (m_cinfo.m_comps_in_scan != 1) - bad = true; - } - - if (m_cinfo.m_Ah != 0) - { - /* Successive approximation refinement scan: must have Al = Ah-1. */ - if (m_cinfo.m_Al != m_cinfo.m_Ah - 1) - bad = true; - } - - if (m_cinfo.m_Al > 13) - { - /* need not check for < 0 */ - bad = true; - } - - /* Arguably the maximum Al value should be less than 13 for 8-bit precision, - * but the spec doesn't say so, and we try to be liberal about what we - * accept. Note: large Al values could result in out-of-range DC - * coefficients during early scans, leading to bizarre displays due to - * overflows in the IDCT math. But we won't crash. - */ - if (bad) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_PROGRESSION, m_cinfo.m_Ss, m_cinfo.m_Se, m_cinfo.m_Ah, m_cinfo.m_Al); - - /* Update progression status, and verify that scan order is legal. - * Note that inter-scan inconsistencies are treated as warnings - * not fatal errors ... not clear if this is right way to behave. - */ - for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) - { - int cindex = m_cinfo.Comp_info[m_cinfo.m_cur_comp_info[ci]].Component_index; - if (!is_DC_band && m_cinfo.m_coef_bits[cindex][0] < 0) /* AC without prior DC scan */ - m_cinfo.WARNMS(J_MESSAGE_CODE.JWRN_BOGUS_PROGRESSION, cindex, 0); - - for (int coefi = m_cinfo.m_Ss; coefi <= m_cinfo.m_Se; coefi++) - { - int expected = m_cinfo.m_coef_bits[cindex][coefi]; - if (expected < 0) - expected = 0; - - if (m_cinfo.m_Ah != expected) - m_cinfo.WARNMS(J_MESSAGE_CODE.JWRN_BOGUS_PROGRESSION, cindex, coefi); - - m_cinfo.m_coef_bits[cindex][coefi] = m_cinfo.m_Al; - } - } - - /* Select MCU decoding routine */ - if (m_cinfo.m_Ah == 0) - { - if (is_DC_band) - m_decoder = MCUDecoder.mcu_DC_first_decoder; - else - m_decoder = MCUDecoder.mcu_AC_first_decoder; - } - else - { - if (is_DC_band) - m_decoder = MCUDecoder.mcu_DC_refine_decoder; - else - m_decoder = MCUDecoder.mcu_AC_refine_decoder; - } - - for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) - { - jpeg_component_info componentInfo = m_cinfo.Comp_info[m_cinfo.m_cur_comp_info[ci]]; - /* Make sure requested tables are present, and compute derived tables. - * We may build same derived table more than once, but it's not expensive. - */ - if (is_DC_band) - { - if (m_cinfo.m_Ah == 0) - { - /* DC refinement needs no table */ - jpeg_make_d_derived_tbl(true, componentInfo.Dc_tbl_no, ref m_derived_tbls[componentInfo.Dc_tbl_no]); - } - } - else - { - jpeg_make_d_derived_tbl(false, componentInfo.Ac_tbl_no, ref m_derived_tbls[componentInfo.Ac_tbl_no]); - - /* remember the single active table */ - m_ac_derived_tbl = m_derived_tbls[componentInfo.Ac_tbl_no]; - } - - /* Initialize DC predictions to 0 */ - m_saved.last_dc_val[ci] = 0; - } - - /* Initialize bitread state variables */ - m_bitstate.bits_left = 0; - m_bitstate.get_buffer = 0; /* unnecessary, but keeps Purify quiet */ - m_insufficient_data = false; - - /* Initialize private state variables */ - m_saved.EOBRUN = 0; - - /* Initialize restart counter */ - m_restarts_to_go = m_cinfo.m_restart_interval; - } - - public override bool decode_mcu(JBLOCK[] MCU_data) - { - switch (m_decoder) - { - case MCUDecoder.mcu_DC_first_decoder: - return decode_mcu_DC_first(MCU_data); - case MCUDecoder.mcu_AC_first_decoder: - return decode_mcu_AC_first(MCU_data); - case MCUDecoder.mcu_DC_refine_decoder: - return decode_mcu_DC_refine(MCU_data); - case MCUDecoder.mcu_AC_refine_decoder: - return decode_mcu_AC_refine(MCU_data); - } - - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NOTIMPL); - return false; - } - - /* - * Huffman MCU decoding. - * Each of these routines decodes and returns one MCU's worth of - * Huffman-compressed coefficients. - * The coefficients are reordered from zigzag order into natural array order, - * but are not dequantized. - * - * The i'th block of the MCU is stored into the block pointed to by - * MCU_data[i]. WE ASSUME THIS AREA IS INITIALLY ZEROED BY THE CALLER. - * - * We return false if data source requested suspension. In that case no - * changes have been made to permanent state. (Exception: some output - * coefficients may already have been assigned. This is harmless for - * spectral selection, since we'll just re-assign them on the next call. - * Successive approximation AC refinement has to be more careful, however.) - */ - - /// - /// MCU decoding for DC initial scan (either spectral selection, - /// or first pass of successive approximation). - /// - private bool decode_mcu_DC_first(JBLOCK[] MCU_data) - { - /* Process restart marker if needed; may have to suspend */ - if (m_cinfo.m_restart_interval != 0) - { - if (m_restarts_to_go == 0) - { - if (!process_restart()) - return false; - } - } - - /* If we've run out of data, just leave the MCU set to zeroes. - * This way, we return uniform gray for the remainder of the segment. - */ - if (!m_insufficient_data) - { - /* Load up working state */ - int get_buffer; - int bits_left; - bitread_working_state br_state = new bitread_working_state(); - BITREAD_LOAD_STATE(m_bitstate, out get_buffer, out bits_left, ref br_state); - savable_state state = new savable_state(); - state.Assign(m_saved); - - /* Outer loop handles each block in the MCU */ - for (int blkn = 0; blkn < m_cinfo.m_blocks_in_MCU; blkn++) - { - int ci = m_cinfo.m_MCU_membership[blkn]; - - /* Decode a single block's worth of coefficients */ - - /* Section F.2.2.1: decode the DC coefficient difference */ - int s; - if (!HUFF_DECODE(out s, ref br_state, m_derived_tbls[m_cinfo.Comp_info[m_cinfo.m_cur_comp_info[ci]].Dc_tbl_no], ref get_buffer, ref bits_left)) - return false; - - if (s != 0) - { - if (!CHECK_BIT_BUFFER(ref br_state, s, ref get_buffer, ref bits_left)) - return false; - - int r = GET_BITS(s, get_buffer, ref bits_left); - s = HUFF_EXTEND(r, s); - } - - /* Convert DC difference to actual value, update last_dc_val */ - s += state.last_dc_val[ci]; - state.last_dc_val[ci] = s; - - /* Scale and output the coefficient (assumes jpeg_natural_order[0]=0) */ - MCU_data[blkn][0] = (short)(s << m_cinfo.m_Al); - } - - /* Completed MCU, so update state */ - BITREAD_SAVE_STATE(ref m_bitstate, get_buffer, bits_left); - m_saved.Assign(state); - } - - /* Account for restart interval (no-op if not using restarts) */ - m_restarts_to_go--; - - return true; - } - - /// - /// MCU decoding for AC initial scan (either spectral selection, - /// or first pass of successive approximation). - /// - private bool decode_mcu_AC_first(JBLOCK[] MCU_data) - { - /* Process restart marker if needed; may have to suspend */ - if (m_cinfo.m_restart_interval != 0) - { - if (m_restarts_to_go == 0) - { - if (!process_restart()) - return false; - } - } - - /* If we've run out of data, just leave the MCU set to zeroes. - * This way, we return uniform gray for the remainder of the segment. - */ - if (!m_insufficient_data) - { - /* Load up working state. - * We can avoid loading/saving bitread state if in an EOB run. - */ - int EOBRUN = m_saved.EOBRUN; /* only part of saved state we need */ - - /* There is always only one block per MCU */ - - if (EOBRUN > 0) - { - /* if it's a band of zeroes... */ - /* ...process it now (we do nothing) */ - EOBRUN--; - } - else - { - int get_buffer; - int bits_left; - bitread_working_state br_state = new bitread_working_state(); - BITREAD_LOAD_STATE(m_bitstate, out get_buffer, out bits_left, ref br_state); - - for (int k = m_cinfo.m_Ss; k <= m_cinfo.m_Se; k++) - { - int s; - if (!HUFF_DECODE(out s, ref br_state, m_ac_derived_tbl, ref get_buffer, ref bits_left)) - return false; - - int r = s >> 4; - s &= 15; - if (s != 0) - { - k += r; - - if (!CHECK_BIT_BUFFER(ref br_state, s, ref get_buffer, ref bits_left)) - return false; - - r = GET_BITS(s, get_buffer, ref bits_left); - s = HUFF_EXTEND(r, s); - - /* Scale and output coefficient in natural (dezigzagged) order */ - MCU_data[0][JpegUtils.jpeg_natural_order[k]] = (short)(s << m_cinfo.m_Al); - } - else - { - if (r == 15) - { - /* ZRL */ - k += 15; /* skip 15 zeroes in band */ - } - else - { - /* EOBr, run length is 2^r + appended bits */ - EOBRUN = 1 << r; - if (r != 0) - { - /* EOBr, r > 0 */ - if (!CHECK_BIT_BUFFER(ref br_state, r, ref get_buffer, ref bits_left)) - return false; - - r = GET_BITS(r, get_buffer, ref bits_left); - EOBRUN += r; - } - - EOBRUN--; /* this band is processed at this moment */ - break; /* force end-of-band */ - } - } - } - - BITREAD_SAVE_STATE(ref m_bitstate, get_buffer, bits_left); - } - - /* Completed MCU, so update state */ - m_saved.EOBRUN = EOBRUN; /* only part of saved state we need */ - } - - /* Account for restart interval (no-op if not using restarts) */ - m_restarts_to_go--; - - return true; - } - - /// - /// MCU decoding for DC successive approximation refinement scan. - /// Note: we assume such scans can be multi-component, although the spec - /// is not very clear on the point. - /// - private bool decode_mcu_DC_refine(JBLOCK[] MCU_data) - { - /* Process restart marker if needed; may have to suspend */ - if (m_cinfo.m_restart_interval != 0) - { - if (m_restarts_to_go == 0) - { - if (!process_restart()) - return false; - } - } - - /* Not worth the cycles to check insufficient_data here, - * since we will not change the data anyway if we read zeroes. - */ - - /* Load up working state */ - int get_buffer; - int bits_left; - bitread_working_state br_state = new bitread_working_state(); - BITREAD_LOAD_STATE(m_bitstate, out get_buffer, out bits_left, ref br_state); - - /* Outer loop handles each block in the MCU */ - - for (int blkn = 0; blkn < m_cinfo.m_blocks_in_MCU; blkn++) - { - /* Encoded data is simply the next bit of the two's-complement DC value */ - if (!CHECK_BIT_BUFFER(ref br_state, 1, ref get_buffer, ref bits_left)) - return false; - - if (GET_BITS(1, get_buffer, ref bits_left) != 0) - { - /* 1 in the bit position being coded */ - MCU_data[blkn][0] = (short)((ushort)MCU_data[blkn][0] | (ushort)(1 << m_cinfo.m_Al)); - } - - /* Note: since we use |=, repeating the assignment later is safe */ - } - - /* Completed MCU, so update state */ - BITREAD_SAVE_STATE(ref m_bitstate, get_buffer, bits_left); - - /* Account for restart interval (no-op if not using restarts) */ - m_restarts_to_go--; - - return true; - } - - // There is always only one block per MCU - private bool decode_mcu_AC_refine(JBLOCK[] MCU_data) - { - int p1 = 1 << m_cinfo.m_Al; /* 1 in the bit position being coded */ - int m1 = -1 << m_cinfo.m_Al; /* -1 in the bit position being coded */ - - /* Process restart marker if needed; may have to suspend */ - if (m_cinfo.m_restart_interval != 0) - { - if (m_restarts_to_go == 0) - { - if (!process_restart()) - return false; - } - } - - /* If we've run out of data, don't modify the MCU. - */ - if (!m_insufficient_data) - { - /* Load up working state */ - int get_buffer; - int bits_left; - bitread_working_state br_state = new bitread_working_state(); - BITREAD_LOAD_STATE(m_bitstate, out get_buffer, out bits_left, ref br_state); - int EOBRUN = m_saved.EOBRUN; /* only part of saved state we need */ - - /* If we are forced to suspend, we must undo the assignments to any newly - * nonzero coefficients in the block, because otherwise we'd get confused - * next time about which coefficients were already nonzero. - * But we need not undo addition of bits to already-nonzero coefficients; - * instead, we can test the current bit to see if we already did it. - */ - int num_newnz = 0; - int[] newnz_pos = new int[JpegConstants.DCTSIZE2]; - - /* initialize coefficient loop counter to start of band */ - int k = m_cinfo.m_Ss; - - if (EOBRUN == 0) - { - for (; k <= m_cinfo.m_Se; k++) - { - int s; - if (!HUFF_DECODE(out s, ref br_state, m_ac_derived_tbl, ref get_buffer, ref bits_left)) - { - undo_decode_mcu_AC_refine(MCU_data, newnz_pos, num_newnz); - return false; - } - - int r = s >> 4; - s &= 15; - if (s != 0) - { - if (s != 1) - { - /* size of new coef should always be 1 */ - m_cinfo.WARNMS(J_MESSAGE_CODE.JWRN_HUFF_BAD_CODE); - } - - if (!CHECK_BIT_BUFFER(ref br_state, 1, ref get_buffer, ref bits_left)) - { - undo_decode_mcu_AC_refine(MCU_data, newnz_pos, num_newnz); - return false; - } - - if (GET_BITS(1, get_buffer, ref bits_left) != 0) - { - /* newly nonzero coef is positive */ - s = p1; - } - else - { - /* newly nonzero coef is negative */ - s = m1; - } - } - else - { - if (r != 15) - { - EOBRUN = 1 << r; /* EOBr, run length is 2^r + appended bits */ - if (r != 0) - { - if (!CHECK_BIT_BUFFER(ref br_state, r, ref get_buffer, ref bits_left)) - { - undo_decode_mcu_AC_refine(MCU_data, newnz_pos, num_newnz); - return false; - } - - r = GET_BITS(r, get_buffer, ref bits_left); - EOBRUN += r; - } - break; /* rest of block is handled by EOB logic */ - } - /* note s = 0 for processing ZRL */ - } - /* Advance over already-nonzero coefs and r still-zero coefs, - * appending correction bits to the nonzeroes. A correction bit is 1 - * if the absolute value of the coefficient must be increased. - */ - do - { - int blockIndex = JpegUtils.jpeg_natural_order[k]; - short thiscoef = MCU_data[0][blockIndex]; - if (thiscoef != 0) - { - if (!CHECK_BIT_BUFFER(ref br_state, 1, ref get_buffer, ref bits_left)) - { - undo_decode_mcu_AC_refine(MCU_data, newnz_pos, num_newnz); - return false; - } - - if (GET_BITS(1, get_buffer, ref bits_left) != 0) - { - if ((thiscoef & p1) == 0) - { - /* do nothing if already set it */ - if (thiscoef >= 0) - MCU_data[0][blockIndex] += (short)p1; - else - MCU_data[0][blockIndex] += (short)m1; - } - } - } - else - { - if (--r < 0) - break; /* reached target zero coefficient */ - } - - k++; - } - while (k <= m_cinfo.m_Se); - - if (s != 0) - { - int pos = JpegUtils.jpeg_natural_order[k]; - - /* Output newly nonzero coefficient */ - MCU_data[0][pos] = (short)s; - - /* Remember its position in case we have to suspend */ - newnz_pos[num_newnz++] = pos; - } - } - } - - if (EOBRUN > 0) - { - /* Scan any remaining coefficient positions after the end-of-band - * (the last newly nonzero coefficient, if any). Append a correction - * bit to each already-nonzero coefficient. A correction bit is 1 - * if the absolute value of the coefficient must be increased. - */ - for (; k <= m_cinfo.m_Se; k++) - { - int blockIndex = JpegUtils.jpeg_natural_order[k]; - short thiscoef = MCU_data[0][blockIndex]; - if (thiscoef != 0) - { - if (!CHECK_BIT_BUFFER(ref br_state, 1, ref get_buffer, ref bits_left)) - { - //undo_decode_mcu_AC_refine(MCU_data[0], newnz_pos, num_newnz); - undo_decode_mcu_AC_refine(MCU_data, newnz_pos, num_newnz); - return false; - } - - if (GET_BITS(1, get_buffer, ref bits_left) != 0) - { - if ((thiscoef & p1) == 0) - { - /* do nothing if already changed it */ - if (thiscoef >= 0) - MCU_data[0][blockIndex] += (short)p1; - else - MCU_data[0][blockIndex] += (short)m1; - } - } - } - } - - /* Count one block completed in EOB run */ - EOBRUN--; - } - - /* Completed MCU, so update state */ - BITREAD_SAVE_STATE(ref m_bitstate, get_buffer, bits_left); - m_saved.EOBRUN = EOBRUN; /* only part of saved state we need */ - } - - /* Account for restart interval (no-op if not using restarts) */ - m_restarts_to_go--; - - return true; - } - - /// - /// Check for a restart marker and resynchronize decoder. - /// Returns false if must suspend. - /// - private bool process_restart() - { - /* Throw away any unused bits remaining in bit buffer; */ - /* include any full bytes in next_marker's count of discarded bytes */ - m_cinfo.m_marker.SkipBytes(m_bitstate.bits_left / 8); - m_bitstate.bits_left = 0; - - /* Advance past the RSTn marker */ - if (!m_cinfo.m_marker.read_restart_marker()) - return false; - - /* Re-initialize DC predictions to 0 */ - for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) - m_saved.last_dc_val[ci] = 0; - - /* Re-init EOB run count, too */ - m_saved.EOBRUN = 0; - - /* Reset restart counter */ - m_restarts_to_go = m_cinfo.m_restart_interval; - - /* Reset out-of-data flag, unless read_restart_marker left us smack up - * against a marker. In that case we will end up treating the next data - * segment as empty, and we can avoid producing bogus output pixels by - * leaving the flag set. - */ - if (m_cinfo.m_unread_marker == 0) - m_insufficient_data = false; - - return true; - } - - /// - /// MCU decoding for AC successive approximation refinement scan. - /// - private static void undo_decode_mcu_AC_refine(JBLOCK[] block, int[] newnz_pos, int num_newnz) - { - /* Re-zero any output coefficients that we made newly nonzero */ - while (num_newnz > 0) - block[0][newnz_pos[--num_newnz]] = 0; - } - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/phuff_entropy_encoder.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/phuff_entropy_encoder.cs deleted file mode 100644 index ddd2121a5..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/Internal/phuff_entropy_encoder.cs +++ /dev/null @@ -1,769 +0,0 @@ -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -/* - * This file contains Huffman entropy encoding routines for progressive JPEG. - * - * We do not support output suspension in this module, since the library - * currently does not allow multiple-scan files to be written with output - * suspension. - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// Expanded entropy encoder object for progressive Huffman encoding. - /// - class phuff_entropy_encoder : jpeg_entropy_encoder - { - private enum MCUEncoder - { - mcu_DC_first_encoder, - mcu_AC_first_encoder, - mcu_DC_refine_encoder, - mcu_AC_refine_encoder - } - - /* MAX_CORR_BITS is the number of bits the AC refinement correction-bit - * buffer can hold. Larger sizes may slightly improve compression, but - * 1000 is already well into the realm of overkill. - * The minimum safe size is 64 bits. - */ - private const int MAX_CORR_BITS = 1000; /* Max # of correction bits I can buffer */ - - private MCUEncoder m_MCUEncoder; - - /* Mode flag: true for optimization, false for actual data output */ - private bool m_gather_statistics; - - // Bit-level coding status. - private int m_put_buffer; /* current bit-accumulation buffer */ - private int m_put_bits; /* # of bits now in it */ - - /* Coding status for DC components */ - private int[] m_last_dc_val = new int[JpegConstants.MAX_COMPS_IN_SCAN]; /* last DC coef for each component */ - - /* Coding status for AC components */ - private int m_ac_tbl_no; /* the table number of the single component */ - private int m_EOBRUN; /* run length of EOBs */ - private int m_BE; /* # of buffered correction bits before MCU */ - private char[] m_bit_buffer; /* buffer for correction bits (1 per char) */ - /* packing correction bits tightly would save some space but cost time... */ - - private int m_restarts_to_go; /* MCUs left in this restart interval */ - private int m_next_restart_num; /* next restart number to write (0-7) */ - - /* Pointers to derived tables (these workspaces have image lifespan). - * Since any one scan codes only DC or only AC, we only need one set - * of tables, not one for DC and one for AC. - */ - private c_derived_tbl[] m_derived_tbls = new c_derived_tbl[JpegConstants.NUM_HUFF_TBLS]; - - /* Statistics tables for optimization; again, one set is enough */ - private long[][] m_count_ptrs = new long[JpegConstants.NUM_HUFF_TBLS][]; - - public phuff_entropy_encoder(jpeg_compress_struct cinfo) - { - m_cinfo = cinfo; - - /* Mark tables unallocated */ - for (int i = 0; i < JpegConstants.NUM_HUFF_TBLS; i++) - { - m_derived_tbls[i] = null; - m_count_ptrs[i] = null; - } - } - - // Initialize for a Huffman-compressed scan using progressive JPEG. - public override void start_pass(bool gather_statistics) - { - m_gather_statistics = gather_statistics; - - /* We assume the scan parameters are already validated. */ - - /* Select execution routines */ - bool is_DC_band = (m_cinfo.m_Ss == 0); - if (m_cinfo.m_Ah == 0) - { - if (is_DC_band) - m_MCUEncoder = MCUEncoder.mcu_DC_first_encoder; - else - m_MCUEncoder = MCUEncoder.mcu_AC_first_encoder; - } - else - { - if (is_DC_band) - { - m_MCUEncoder = MCUEncoder.mcu_DC_refine_encoder; - } - else - { - m_MCUEncoder = MCUEncoder.mcu_AC_refine_encoder; - - /* AC refinement needs a correction bit buffer */ - if (m_bit_buffer == null) - m_bit_buffer = new char[MAX_CORR_BITS]; - } - } - - /* Only DC coefficients may be interleaved, so m_cinfo.comps_in_scan = 1 - * for AC coefficients. - */ - for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) - { - jpeg_component_info componentInfo = m_cinfo.Component_info[m_cinfo.m_cur_comp_info[ci]]; - - /* Initialize DC predictions to 0 */ - m_last_dc_val[ci] = 0; - - /* Get table index */ - int tbl; - if (is_DC_band) - { - if (m_cinfo.m_Ah != 0) /* DC refinement needs no table */ - continue; - - tbl = componentInfo.Dc_tbl_no; - } - else - { - m_ac_tbl_no = componentInfo.Ac_tbl_no; - tbl = componentInfo.Ac_tbl_no; - } - - if (m_gather_statistics) - { - /* Check for invalid table index */ - /* (make_c_derived_tbl does this in the other path) */ - if (tbl < 0 || tbl >= JpegConstants.NUM_HUFF_TBLS) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NO_HUFF_TABLE, tbl); - - /* Allocate and zero the statistics tables */ - /* Note that jpeg_gen_optimal_table expects 257 entries in each table! */ - if (m_count_ptrs[tbl] == null) - m_count_ptrs[tbl] = new long[257]; - - Array.Clear(m_count_ptrs[tbl], 0, 257); - } - else - { - /* Compute derived values for Huffman table */ - /* We may do this more than once for a table, but it's not expensive */ - jpeg_make_c_derived_tbl(is_DC_band, tbl, ref m_derived_tbls[tbl]); - } - } - - /* Initialize AC stuff */ - m_EOBRUN = 0; - m_BE = 0; - - /* Initialize bit buffer to empty */ - m_put_buffer = 0; - m_put_bits = 0; - - /* Initialize restart stuff */ - m_restarts_to_go = m_cinfo.m_restart_interval; - m_next_restart_num = 0; - } - - public override bool encode_mcu(JBLOCK[][] MCU_data) - { - switch (m_MCUEncoder) - { - case MCUEncoder.mcu_DC_first_encoder: - return encode_mcu_DC_first(MCU_data); - case MCUEncoder.mcu_AC_first_encoder: - return encode_mcu_AC_first(MCU_data); - case MCUEncoder.mcu_DC_refine_encoder: - return encode_mcu_DC_refine(MCU_data); - case MCUEncoder.mcu_AC_refine_encoder: - return encode_mcu_AC_refine(MCU_data); - } - - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NOTIMPL); - return false; - } - - public override void finish_pass() - { - if (m_gather_statistics) - finish_pass_gather_phuff(); - else - finish_pass_phuff(); - } - - /// - /// MCU encoding for DC initial scan (either spectral selection, - /// or first pass of successive approximation). - /// - private bool encode_mcu_DC_first(JBLOCK[][] MCU_data) - { - /* Emit restart marker if needed */ - if (m_cinfo.m_restart_interval != 0) - { - if (m_restarts_to_go == 0) - emit_restart(m_next_restart_num); - } - - /* Encode the MCU data blocks */ - for (int blkn = 0; blkn < m_cinfo.m_blocks_in_MCU; blkn++) - { - /* Compute the DC value after the required point transform by Al. - * This is simply an arithmetic right shift. - */ - int temp2 = IRIGHT_SHIFT(MCU_data[blkn][0][0], m_cinfo.m_Al); - - /* DC differences are figured on the point-transformed values. */ - int ci = m_cinfo.m_MCU_membership[blkn]; - int temp = temp2 - m_last_dc_val[ci]; - m_last_dc_val[ci] = temp2; - - /* Encode the DC coefficient difference per section G.1.2.1 */ - temp2 = temp; - if (temp < 0) - { - /* temp is abs value of input */ - temp = -temp; - - /* For a negative input, want temp2 = bitwise complement of abs(input) */ - /* This code assumes we are on a two's complement machine */ - temp2--; - } - - /* Find the number of bits needed for the magnitude of the coefficient */ - int nbits = 0; - while (temp != 0) - { - nbits++; - temp >>= 1; - } - - /* Check for out-of-range coefficient values. - * Since we're encoding a difference, the range limit is twice as much. - */ - if (nbits > MAX_HUFFMAN_COEF_BITS + 1) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_DCT_COEF); - - /* Count/emit the Huffman-coded symbol for the number of bits */ - emit_symbol(m_cinfo.Component_info[m_cinfo.m_cur_comp_info[ci]].Dc_tbl_no, nbits); - - /* Emit that number of bits of the value, if positive, */ - /* or the complement of its magnitude, if negative. */ - if (nbits != 0) - { - /* emit_bits rejects calls with size 0 */ - emit_bits(temp2, nbits); - } - } - - /* Update restart-interval state too */ - if (m_cinfo.m_restart_interval != 0) - { - if (m_restarts_to_go == 0) - { - m_restarts_to_go = m_cinfo.m_restart_interval; - m_next_restart_num++; - m_next_restart_num &= 7; - } - - m_restarts_to_go--; - } - - return true; - } - - /// - /// MCU encoding for AC initial scan (either spectral selection, - /// or first pass of successive approximation). - /// - private bool encode_mcu_AC_first(JBLOCK[][] MCU_data) - { - /* Emit restart marker if needed */ - if (m_cinfo.m_restart_interval != 0) - { - if (m_restarts_to_go == 0) - emit_restart(m_next_restart_num); - } - - /* Encode the AC coefficients per section G.1.2.2, fig. G.3 */ - /* r = run length of zeros */ - int r = 0; - for (int k = m_cinfo.m_Ss; k <= m_cinfo.m_Se; k++) - { - int temp = MCU_data[0][0][JpegUtils.jpeg_natural_order[k]]; - if (temp == 0) - { - r++; - continue; - } - - /* We must apply the point transform by Al. For AC coefficients this - * is an integer division with rounding towards 0. To do this portably - * in C, we shift after obtaining the absolute value; so the code is - * interwoven with finding the abs value (temp) and output bits (temp2). - */ - int temp2; - if (temp < 0) - { - temp = -temp; /* temp is abs value of input */ - temp >>= m_cinfo.m_Al; /* apply the point transform */ - /* For a negative coef, want temp2 = bitwise complement of abs(coef) */ - temp2 = ~temp; - } - else - { - temp >>= m_cinfo.m_Al; /* apply the point transform */ - temp2 = temp; - } - - /* Watch out for case that nonzero coef is zero after point transform */ - if (temp == 0) - { - r++; - continue; - } - - /* Emit any pending EOBRUN */ - if (m_EOBRUN > 0) - emit_eobrun(); - - /* if run length > 15, must emit special run-length-16 codes (0xF0) */ - while (r > 15) - { - emit_symbol(m_ac_tbl_no, 0xF0); - r -= 16; - } - - /* Find the number of bits needed for the magnitude of the coefficient */ - int nbits = 1; /* there must be at least one 1 bit */ - while ((temp >>= 1) != 0) - nbits++; - - /* Check for out-of-range coefficient values */ - if (nbits > MAX_HUFFMAN_COEF_BITS) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_DCT_COEF); - - /* Count/emit Huffman symbol for run length / number of bits */ - emit_symbol(m_ac_tbl_no, (r << 4) + nbits); - - /* Emit that number of bits of the value, if positive, */ - /* or the complement of its magnitude, if negative. */ - emit_bits(temp2, nbits); - - r = 0; /* reset zero run length */ - } - - if (r > 0) - { - /* If there are trailing zeroes, */ - m_EOBRUN++; /* count an EOB */ - if (m_EOBRUN == 0x7FFF) - emit_eobrun(); /* force it out to avoid overflow */ - } - - /* Update restart-interval state too */ - if (m_cinfo.m_restart_interval != 0) - { - if (m_restarts_to_go == 0) - { - m_restarts_to_go = m_cinfo.m_restart_interval; - m_next_restart_num++; - m_next_restart_num &= 7; - } - m_restarts_to_go--; - } - - return true; - } - - /// - /// MCU encoding for DC successive approximation refinement scan. - /// Note: we assume such scans can be multi-component, although the spec - /// is not very clear on the point. - /// - private bool encode_mcu_DC_refine(JBLOCK[][] MCU_data) - { - /* Emit restart marker if needed */ - if (m_cinfo.m_restart_interval != 0) - { - if (m_restarts_to_go == 0) - emit_restart(m_next_restart_num); - } - - /* Encode the MCU data blocks */ - for (int blkn = 0; blkn < m_cinfo.m_blocks_in_MCU; blkn++) - { - /* We simply emit the Al'th bit of the DC coefficient value. */ - int temp = MCU_data[blkn][0][0]; - emit_bits(temp >> m_cinfo.m_Al, 1); - } - - /* Update restart-interval state too */ - if (m_cinfo.m_restart_interval != 0) - { - if (m_restarts_to_go == 0) - { - m_restarts_to_go = m_cinfo.m_restart_interval; - m_next_restart_num++; - m_next_restart_num &= 7; - } - m_restarts_to_go--; - } - - return true; - } - - /// - /// MCU encoding for AC successive approximation refinement scan. - /// - private bool encode_mcu_AC_refine(JBLOCK[][] MCU_data) - { - /* Emit restart marker if needed */ - if (m_cinfo.m_restart_interval != 0) - { - if (m_restarts_to_go == 0) - emit_restart(m_next_restart_num); - } - - /* Encode the MCU data block */ - - /* It is convenient to make a pre-pass to determine the transformed - * coefficients' absolute values and the EOB position. - */ - int EOB = 0; - int[] absvalues = new int[JpegConstants.DCTSIZE2]; - for (int k = m_cinfo.m_Ss; k <= m_cinfo.m_Se; k++) - { - int temp = MCU_data[0][0][JpegUtils.jpeg_natural_order[k]]; - - /* We must apply the point transform by Al. For AC coefficients this - * is an integer division with rounding towards 0. To do this portably - * in C, we shift after obtaining the absolute value. - */ - if (temp < 0) - temp = -temp; /* temp is abs value of input */ - - temp >>= m_cinfo.m_Al; /* apply the point transform */ - absvalues[k] = temp; /* save abs value for main pass */ - - if (temp == 1) - { - /* EOB = index of last newly-nonzero coef */ - EOB = k; - } - } - - /* Encode the AC coefficients per section G.1.2.3, fig. G.7 */ - - int r = 0; /* r = run length of zeros */ - int BR = 0; /* BR = count of buffered bits added now */ - int bitBufferOffset = m_BE; /* Append bits to buffer */ - - for (int k = m_cinfo.m_Ss; k <= m_cinfo.m_Se; k++) - { - int temp = absvalues[k]; - if (temp == 0) - { - r++; - continue; - } - - /* Emit any required ZRLs, but not if they can be folded into EOB */ - while (r > 15 && k <= EOB) - { - /* emit any pending EOBRUN and the BE correction bits */ - emit_eobrun(); - - /* Emit ZRL */ - emit_symbol(m_ac_tbl_no, 0xF0); - r -= 16; - - /* Emit buffered correction bits that must be associated with ZRL */ - emit_buffered_bits(bitBufferOffset, BR); - bitBufferOffset = 0;/* BE bits are gone now */ - BR = 0; - } - - /* If the coef was previously nonzero, it only needs a correction bit. - * NOTE: a straight translation of the spec's figure G.7 would suggest - * that we also need to test r > 15. But if r > 15, we can only get here - * if k > EOB, which implies that this coefficient is not 1. - */ - if (temp > 1) - { - /* The correction bit is the next bit of the absolute value. */ - m_bit_buffer[bitBufferOffset + BR] = (char) (temp & 1); - BR++; - continue; - } - - /* Emit any pending EOBRUN and the BE correction bits */ - emit_eobrun(); - - /* Count/emit Huffman symbol for run length / number of bits */ - emit_symbol(m_ac_tbl_no, (r << 4) + 1); - - /* Emit output bit for newly-nonzero coef */ - temp = (MCU_data[0][0][JpegUtils.jpeg_natural_order[k]] < 0) ? 0 : 1; - emit_bits(temp, 1); - - /* Emit buffered correction bits that must be associated with this code */ - emit_buffered_bits(bitBufferOffset, BR); - bitBufferOffset = 0;/* BE bits are gone now */ - BR = 0; - r = 0; /* reset zero run length */ - } - - if (r > 0 || BR > 0) - { - /* If there are trailing zeroes, */ - m_EOBRUN++; /* count an EOB */ - m_BE += BR; /* concat my correction bits to older ones */ - - /* We force out the EOB if we risk either: - * 1. overflow of the EOB counter; - * 2. overflow of the correction bit buffer during the next MCU. - */ - if (m_EOBRUN == 0x7FFF || m_BE > (MAX_CORR_BITS - JpegConstants.DCTSIZE2 + 1)) - emit_eobrun(); - } - - /* Update restart-interval state too */ - if (m_cinfo.m_restart_interval != 0) - { - if (m_restarts_to_go == 0) - { - m_restarts_to_go = m_cinfo.m_restart_interval; - m_next_restart_num++; - m_next_restart_num &= 7; - } - m_restarts_to_go--; - } - - return true; - } - - /// - /// Finish up at the end of a Huffman-compressed progressive scan. - /// - private void finish_pass_phuff() - { - /* Flush out any buffered data */ - emit_eobrun(); - flush_bits(); - } - - /// - /// Finish up a statistics-gathering pass and create the new Huffman tables. - /// - private void finish_pass_gather_phuff() - { - /* Flush out buffered data (all we care about is counting the EOB symbol) */ - emit_eobrun(); - - /* It's important not to apply jpeg_gen_optimal_table more than once - * per table, because it clobbers the input frequency counts! - */ - bool[] did = new bool [JpegConstants.NUM_HUFF_TBLS]; - - bool is_DC_band = (m_cinfo.m_Ss == 0); - for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) - { - jpeg_component_info componentInfo = m_cinfo.Component_info[m_cinfo.m_cur_comp_info[ci]]; - int tbl = componentInfo.Ac_tbl_no; - - if (is_DC_band) - { - if (m_cinfo.m_Ah != 0) /* DC refinement needs no table */ - continue; - - tbl = componentInfo.Dc_tbl_no; - } - - if (!did[tbl]) - { - JHUFF_TBL htblptr = null; - if (is_DC_band) - { - if (m_cinfo.m_dc_huff_tbl_ptrs[tbl] == null) - m_cinfo.m_dc_huff_tbl_ptrs[tbl] = new JHUFF_TBL(); - - htblptr = m_cinfo.m_dc_huff_tbl_ptrs[tbl]; - } - else - { - if (m_cinfo.m_ac_huff_tbl_ptrs[tbl] == null) - m_cinfo.m_ac_huff_tbl_ptrs[tbl] = new JHUFF_TBL(); - - htblptr = m_cinfo.m_ac_huff_tbl_ptrs[tbl]; - } - - jpeg_gen_optimal_table(htblptr, m_count_ptrs[tbl]); - did[tbl] = true; - } - } - } - - ////////////////////////////////////////////////////////////////////////// - // Outputting bytes to the file. - // NB: these must be called only when actually outputting, - // that is, entropy.gather_statistics == false. - - // Emit a byte - private void emit_byte(int val) - { - m_cinfo.m_dest.emit_byte(val); - } - - /// - /// Outputting bits to the file - /// - /// Only the right 24 bits of put_buffer are used; the valid bits are - /// left-justified in this part. At most 16 bits can be passed to emit_bits - /// in one call, and we never retain more than 7 bits in put_buffer - /// between calls, so 24 bits are sufficient. - /// - private void emit_bits(int code, int size) - { - // Emit some bits, unless we are in gather mode - /* This routine is heavily used, so it's worth coding tightly. */ - int local_put_buffer = code; - - /* if size is 0, caller used an invalid Huffman table entry */ - if (size == 0) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_HUFF_MISSING_CODE); - - if (m_gather_statistics) - { - /* do nothing if we're only getting stats */ - return; - } - - local_put_buffer &= (1 << size) - 1; /* mask off any extra bits in code */ - - m_put_bits += size; /* new number of bits in buffer */ - - local_put_buffer <<= 24 - m_put_bits; /* align incoming bits */ - - local_put_buffer |= m_put_buffer; /* and merge with old buffer contents */ - - while (m_put_bits >= 8) - { - int c = (local_put_buffer >> 16) & 0xFF; - - emit_byte(c); - if (c == 0xFF) - { - /* need to stuff a zero byte? */ - emit_byte(0); - } - local_put_buffer <<= 8; - m_put_bits -= 8; - } - - m_put_buffer = local_put_buffer; /* update variables */ - } - - private void flush_bits() - { - emit_bits(0x7F, 7); /* fill any partial byte with ones */ - m_put_buffer = 0; /* and reset bit-buffer to empty */ - m_put_bits = 0; - } - - // Emit (or just count) a Huffman symbol. - private void emit_symbol(int tbl_no, int symbol) - { - if (m_gather_statistics) - m_count_ptrs[tbl_no][symbol]++; - else - emit_bits(m_derived_tbls[tbl_no].ehufco[symbol], m_derived_tbls[tbl_no].ehufsi[symbol]); - } - - // Emit bits from a correction bit buffer. - private void emit_buffered_bits(int offset, int nbits) - { - if (m_gather_statistics) - { - /* no real work */ - return; - } - - for (int i = 0; i < nbits; i++) - emit_bits(m_bit_buffer[offset + i], 1); - } - - // Emit any pending EOBRUN symbol. - private void emit_eobrun() - { - if (m_EOBRUN > 0) - { - /* if there is any pending EOBRUN */ - int temp = m_EOBRUN; - int nbits = 0; - while ((temp >>= 1) != 0) - nbits++; - - /* safety check: shouldn't happen given limited correction-bit buffer */ - if (nbits > 14) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_HUFF_MISSING_CODE); - - emit_symbol(m_ac_tbl_no, nbits << 4); - if (nbits != 0) - emit_bits(m_EOBRUN, nbits); - - m_EOBRUN = 0; - - /* Emit any buffered correction bits */ - emit_buffered_bits(0, m_BE); - m_BE = 0; - } - } - - // Emit a restart marker & resynchronize predictions. - private void emit_restart(int restart_num) - { - emit_eobrun(); - - if (!m_gather_statistics) - { - flush_bits(); - emit_byte(0xFF); - emit_byte((int)(JPEG_MARKER.RST0 + restart_num)); - } - - if (m_cinfo.m_Ss == 0) - { - /* Re-initialize DC predictions to 0 */ - for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) - m_last_dc_val[ci] = 0; - } - else - { - /* Re-initialize all AC-related fields to 0 */ - m_EOBRUN = 0; - m_BE = 0; - } - } - - /// - /// IRIGHT_SHIFT is like RIGHT_SHIFT, but works on int rather than int. - /// We assume that int right shift is unsigned if int right shift is, - /// which should be safe. - /// - private static int IRIGHT_SHIFT(int x, int shft) - { - return (x >> shft); - } - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/JBLOCK.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/JBLOCK.cs deleted file mode 100644 index bf9a2b096..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/JBLOCK.cs +++ /dev/null @@ -1,43 +0,0 @@ -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic -{ - /// - /// One block of coefficients. - /// -#if EXPOSE_LIBJPEG - public -#endif - class JBLOCK - { - internal short[] data = new short[JpegConstants.DCTSIZE2]; - - /// - /// Gets or sets the element at the specified index. - /// - /// The index of required element. - /// The required element. - public short this[int index] - { - get - { - return data[index]; - } - set - { - data[index] = value; - } - } - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/JHUFF_TBL.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/JHUFF_TBL.cs deleted file mode 100644 index 9abd90d13..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/JHUFF_TBL.cs +++ /dev/null @@ -1,65 +0,0 @@ -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic -{ - /// - /// Huffman coding table. - /// -#if EXPOSE_LIBJPEG - public -#endif - class JHUFF_TBL - { - /* These two fields directly represent the contents of a JPEG DHT marker */ - private readonly byte[] m_bits = new byte[17]; /* bits[k] = # of symbols with codes of */ - - /* length k bits; bits[0] is unused */ - private readonly byte[] m_huffval = new byte[256]; /* The symbols, in order of incr code length */ - - private bool m_sent_table; /* true when table has been output */ - - - internal JHUFF_TBL() - { - } - - internal byte[] Bits - { - get { return m_bits; } - } - - internal byte[] Huffval - { - get { return m_huffval; } - } - - /// - /// Gets or sets a value indicating whether the table has been output to file. - /// - /// It's initialized false when the table is created, and set - /// true when it's been output to the file. You could suppress output - /// of a table by setting this to true. - /// - /// This property is used only during compression. It's initialized - /// false when the table is created, and set true when it's been - /// output to the file. You could suppress output of a table by setting this to - /// true. (See jpeg_suppress_tables for an example.) - /// - public bool Sent_table - { - get { return m_sent_table; } - set { m_sent_table = value; } - } - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/JPEG_MARKER.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/JPEG_MARKER.cs deleted file mode 100644 index c5de76cca..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/JPEG_MARKER.cs +++ /dev/null @@ -1,238 +0,0 @@ -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic -{ - /// - /// JPEG marker codes. - /// - /// Special markers -#if EXPOSE_LIBJPEG - public -#endif - enum JPEG_MARKER - { - /// - /// - /// - SOF0 = 0xc0, - /// - /// - /// - SOF1 = 0xc1, - /// - /// - /// - SOF2 = 0xc2, - /// - /// - /// - SOF3 = 0xc3, - /// - /// - /// - SOF5 = 0xc5, - /// - /// - /// - SOF6 = 0xc6, - /// - /// - /// - SOF7 = 0xc7, - /// - /// - /// - JPG = 0xc8, - /// - /// - /// - SOF9 = 0xc9, - /// - /// - /// - SOF10 = 0xca, - /// - /// - /// - SOF11 = 0xcb, - /// - /// - /// - SOF13 = 0xcd, - /// - /// - /// - SOF14 = 0xce, - /// - /// - /// - SOF15 = 0xcf, - /// - /// - /// - DHT = 0xc4, - /// - /// - /// - DAC = 0xcc, - /// - /// - /// - RST0 = 0xd0, - /// - /// - /// - RST1 = 0xd1, - /// - /// - /// - RST2 = 0xd2, - /// - /// - /// - RST3 = 0xd3, - /// - /// - /// - RST4 = 0xd4, - /// - /// - /// - RST5 = 0xd5, - /// - /// - /// - RST6 = 0xd6, - /// - /// - /// - RST7 = 0xd7, - /// - /// - /// - SOI = 0xd8, - /// - /// - /// - EOI = 0xd9, - /// - /// - /// - SOS = 0xda, - /// - /// - /// - DQT = 0xdb, - /// - /// - /// - DNL = 0xdc, - /// - /// - /// - DRI = 0xdd, - /// - /// - /// - DHP = 0xde, - /// - /// - /// - EXP = 0xdf, - /// - /// - /// - APP0 = 0xe0, - /// - /// - /// - APP1 = 0xe1, - /// - /// - /// - APP2 = 0xe2, - /// - /// - /// - APP3 = 0xe3, - /// - /// - /// - APP4 = 0xe4, - /// - /// - /// - APP5 = 0xe5, - /// - /// - /// - APP6 = 0xe6, - /// - /// - /// - APP7 = 0xe7, - /// - /// - /// - APP8 = 0xe8, - /// - /// - /// - APP9 = 0xe9, - /// - /// - /// - APP10 = 0xea, - /// - /// - /// - APP11 = 0xeb, - /// - /// - /// - APP12 = 0xec, - /// - /// - /// - APP13 = 0xed, - /// - /// - /// - APP14 = 0xee, - /// - /// - /// - APP15 = 0xef, - /// - /// - /// - JPG0 = 0xf0, - /// - /// - /// - JPG13 = 0xfd, - /// - /// - /// - COM = 0xfe, - /// - /// - /// - TEM = 0x01, - /// - /// - /// - ERROR = 0x100 - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/JQUANT_TBL.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/JQUANT_TBL.cs deleted file mode 100644 index 30b727b73..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/JQUANT_TBL.cs +++ /dev/null @@ -1,55 +0,0 @@ -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic -{ - /// - /// DCT coefficient quantization tables. - /// -#if EXPOSE_LIBJPEG - public -#endif - class JQUANT_TBL - { - /* This field is used only during compression. It's initialized false when - * the table is created, and set true when it's been output to the file. - * You could suppress output of a table by setting this to true. - * (See jpeg_suppress_tables for an example.) - */ - private bool m_sent_table; /* true when table has been output */ - - /* This array gives the coefficient quantizers in natural array order - * (not the zigzag order in which they are stored in a JPEG DQT marker). - * CAUTION: IJG versions prior to v6a kept this array in zigzag order. - */ - internal readonly short[] quantval = new short[JpegConstants.DCTSIZE2]; /* quantization step for each coefficient */ - - internal JQUANT_TBL() - { - } - - /// - /// Gets or sets a value indicating whether the table has been output to file. - /// - /// It's initialized false when the table is created, and set - /// true when it's been output to the file. You could suppress output of a table by setting this to true. - /// - /// This property is used only during compression. - /// - public bool Sent_table - { - get { return m_sent_table; } - set { m_sent_table = value; } - } - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/J_COLOR_SPACE.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/J_COLOR_SPACE.cs deleted file mode 100644 index c12ea7a35..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/J_COLOR_SPACE.cs +++ /dev/null @@ -1,55 +0,0 @@ -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic -{ - /// - /// Known color spaces. - /// - /// Special color spaces -#if EXPOSE_LIBJPEG - public -#endif - enum J_COLOR_SPACE - { - /// - /// Unspecified color space. - /// - JCS_UNKNOWN, - - /// - /// Grayscale - /// - JCS_GRAYSCALE, - - /// - /// RGB - /// - JCS_RGB, - - /// - /// YCbCr (also known as YUV) - /// - JCS_YCbCr, - - /// - /// CMYK - /// - JCS_CMYK, - - /// - /// YCbCrK - /// - JCS_YCCK - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/J_DCT_METHOD.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/J_DCT_METHOD.cs deleted file mode 100644 index 9b2e1ac54..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/J_DCT_METHOD.cs +++ /dev/null @@ -1,47 +0,0 @@ -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic -{ - /// - /// Algorithm used for the DCT step. - /// - /// The FLOAT method is very slightly more accurate than the ISLOW method, - /// but may give different results on different machines due to varying roundoff behavior. - /// The integer methods should give the same results on all machines. On machines with - /// sufficiently fast hardware, the floating-point method may also be the fastest. - /// The IFAST method is considerably less accurate than the other two; its use is not recommended - /// if high quality is a concern. - /// - /// -#if EXPOSE_LIBJPEG - public -#endif - enum J_DCT_METHOD - { - /// - /// Slow but accurate integer algorithm. - /// - JDCT_ISLOW, - - /// - /// Faster, less accurate integer method. - /// - JDCT_IFAST, - - /// - /// Floating-point method. - /// - JDCT_FLOAT - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/J_DITHER_MODE.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/J_DITHER_MODE.cs deleted file mode 100644 index bd76686db..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/J_DITHER_MODE.cs +++ /dev/null @@ -1,40 +0,0 @@ -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic -{ - /// - /// Dithering options for decompression. - /// - /// -#if EXPOSE_LIBJPEG - public -#endif - enum J_DITHER_MODE - { - /// - /// No dithering: fast, very low quality - /// - JDITHER_NONE, - - /// - /// Ordered dither: moderate speed and quality - /// - JDITHER_ORDERED, - - /// - /// Floyd-Steinberg dither: slow, high quality - /// - JDITHER_FS - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/J_MESSAGE_CODE.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/J_MESSAGE_CODE.cs deleted file mode 100644 index 7aafb0b36..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/J_MESSAGE_CODE.cs +++ /dev/null @@ -1,427 +0,0 @@ -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -/* - * This file defines the error and message codes for the JPEG library. - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic -{ - /// - /// Message codes used in code to signal errors, warning and trace messages. - /// - /// -#if EXPOSE_LIBJPEG - public -#endif - enum J_MESSAGE_CODE - { - /// - /// Must be first entry! - /// - JMSG_NOMESSAGE, - - /// - /// - /// - JERR_ARITH_NOTIMPL, - /// - /// - /// - JERR_BAD_BUFFER_MODE, - /// - /// - /// - JERR_BAD_COMPONENT_ID, - /// - /// - /// - JERR_BAD_DCT_COEF, - /// - /// - /// - JERR_BAD_DCTSIZE, - /// - /// - /// - JERR_BAD_HUFF_TABLE, - /// - /// - /// - JERR_BAD_IN_COLORSPACE, - /// - /// - /// - JERR_BAD_J_COLORSPACE, - /// - /// - /// - JERR_BAD_LENGTH, - /// - /// - /// - JERR_BAD_MCU_SIZE, - /// - /// - /// - JERR_BAD_PRECISION, - /// - /// - /// - JERR_BAD_PROGRESSION, - /// - /// - /// - JERR_BAD_PROG_SCRIPT, - /// - /// - /// - JERR_BAD_SAMPLING, - /// - /// - /// - JERR_BAD_SCAN_SCRIPT, - /// - /// - /// - JERR_BAD_STATE, - /// - /// - /// - JERR_BAD_VIRTUAL_ACCESS, - /// - /// - /// - JERR_BUFFER_SIZE, - /// - /// - /// - JERR_CANT_SUSPEND, - /// - /// - /// - JERR_CCIR601_NOTIMPL, - /// - /// - /// - JERR_COMPONENT_COUNT, - /// - /// - /// - JERR_CONVERSION_NOTIMPL, - /// - /// - /// - JERR_DHT_INDEX, - /// - /// - /// - JERR_DQT_INDEX, - /// - /// - /// - JERR_EMPTY_IMAGE, - /// - /// - /// - JERR_EOI_EXPECTED, - /// - /// - /// - JERR_FILE_WRITE, - /// - /// - /// - JERR_FRACT_SAMPLE_NOTIMPL, - /// - /// - /// - JERR_HUFF_CLEN_OVERFLOW, - /// - /// - /// - JERR_HUFF_MISSING_CODE, - /// - /// - /// - JERR_IMAGE_TOO_BIG, - /// - /// - /// - JERR_INPUT_EMPTY, - /// - /// - /// - JERR_INPUT_EOF, - /// - /// - /// - JERR_MISMATCHED_QUANT_TABLE, - /// - /// - /// - JERR_MISSING_DATA, - /// - /// - /// - JERR_MODE_CHANGE, - /// - /// - /// - JERR_NOTIMPL, - /// - /// - /// - JERR_NOT_COMPILED, - /// - /// - /// - JERR_NO_HUFF_TABLE, - /// - /// - /// - JERR_NO_IMAGE, - /// - /// - /// - JERR_NO_QUANT_TABLE, - /// - /// - /// - JERR_NO_SOI, - /// - /// - /// - JERR_OUT_OF_MEMORY, - /// - /// - /// - JERR_QUANT_COMPONENTS, - /// - /// - /// - JERR_QUANT_FEW_COLORS, - /// - /// - /// - JERR_QUANT_MANY_COLORS, - /// - /// - /// - JERR_SOF_DUPLICATE, - /// - /// - /// - JERR_SOF_NO_SOS, - /// - /// - /// - JERR_SOF_UNSUPPORTED, - /// - /// - /// - JERR_SOI_DUPLICATE, - /// - /// - /// - JERR_SOS_NO_SOF, - /// - /// - /// - JERR_TOO_LITTLE_DATA, - /// - /// - /// - JERR_UNKNOWN_MARKER, - /// - /// - /// - JERR_WIDTH_OVERFLOW, - /// - /// - /// - JTRC_16BIT_TABLES, - /// - /// - /// - JTRC_ADOBE, - /// - /// - /// - JTRC_APP0, - /// - /// - /// - JTRC_APP14, - /// - /// - /// - JTRC_DHT, - /// - /// - /// - JTRC_DQT, - /// - /// - /// - JTRC_DRI, - /// - /// - /// - JTRC_EOI, - /// - /// - /// - JTRC_HUFFBITS, - /// - /// - /// - JTRC_JFIF, - /// - /// - /// - JTRC_JFIF_BADTHUMBNAILSIZE, - /// - /// - /// - JTRC_JFIF_EXTENSION, - /// - /// - /// - JTRC_JFIF_THUMBNAIL, - /// - /// - /// - JTRC_MISC_MARKER, - /// - /// - /// - JTRC_PARMLESS_MARKER, - /// - /// - /// - JTRC_QUANTVALS, - /// - /// - /// - JTRC_QUANT_3_NCOLORS, - /// - /// - /// - JTRC_QUANT_NCOLORS, - /// - /// - /// - JTRC_QUANT_SELECTED, - /// - /// - /// - JTRC_RECOVERY_ACTION, - /// - /// - /// - JTRC_RST, - /// - /// - /// - JTRC_SMOOTH_NOTIMPL, - /// - /// - /// - JTRC_SOF, - /// - /// - /// - JTRC_SOF_COMPONENT, - /// - /// - /// - JTRC_SOI, - /// - /// - /// - JTRC_SOS, - /// - /// - /// - JTRC_SOS_COMPONENT, - /// - /// - /// - JTRC_SOS_PARAMS, - /// - /// - /// - JTRC_THUMB_JPEG, - /// - /// - /// - JTRC_THUMB_PALETTE, - /// - /// - /// - JTRC_THUMB_RGB, - /// - /// - /// - JTRC_UNKNOWN_IDS, - /// - /// - /// - JWRN_ADOBE_XFORM, - /// - /// - /// - JWRN_BOGUS_PROGRESSION, - /// - /// - /// - JWRN_EXTRANEOUS_DATA, - /// - /// - /// - JWRN_HIT_MARKER, - /// - /// - /// - JWRN_HUFF_BAD_CODE, - /// - /// - /// - JWRN_JFIF_MAJOR, - /// - /// - /// - JWRN_JPEG_EOF, - /// - /// - /// - JWRN_MUST_RESYNC, - /// - /// - /// - JWRN_NOT_SEQUENTIAL, - /// - /// - /// - JWRN_TOO_MUCH_DATA, - /// - /// - /// - JMSG_UNKNOWNMSGCODE, - /// - /// - /// - JMSG_LASTMSGCODE - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/JpegConstants.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/JpegConstants.cs deleted file mode 100644 index b0cdaccb4..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/JpegConstants.cs +++ /dev/null @@ -1,166 +0,0 @@ -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic -{ - /// - /// Defines some JPEG constants. - /// -#if EXPOSE_LIBJPEG - public -#endif - static class JpegConstants - { - ////////////////////////////////////////////////////////////////////////// - // All of these are specified by the JPEG standard, so don't change them - // if you want to be compatible. - // - - /// - /// The basic DCT block is 8x8 samples - /// - public const int DCTSIZE = 8; - - /// - /// DCTSIZE squared; the number of elements in a block. - /// - public const int DCTSIZE2 = DCTSIZE * DCTSIZE; - - /// - /// Quantization tables are numbered 0..3 - /// - public const int NUM_QUANT_TBLS = 4; - - /// - /// Huffman tables are numbered 0..3 - /// - public const int NUM_HUFF_TBLS = 4; - - /// - /// JPEG limit on the number of components in one scan. - /// - public const int MAX_COMPS_IN_SCAN = 4; - - // compressor's limit on blocks per MCU - // - // Unfortunately, some bozo at Adobe saw no reason to be bound by the standard; - // the PostScript DCT filter can emit files with many more than 10 blocks/MCU. - // If you happen to run across such a file, you can up D_MAX_BLOCKS_IN_MCU - // to handle it. We even let you do this from the jconfig.h file. However, - // we strongly discourage changing C_MAX_BLOCKS_IN_MCU; just because Adobe - // sometimes emits noncompliant files doesn't mean you should too. - - /// - /// Compressor's limit on blocks per MCU. - /// - public const int C_MAX_BLOCKS_IN_MCU = 10; - - /// - /// Decompressor's limit on blocks per MCU. - /// - public const int D_MAX_BLOCKS_IN_MCU = 10; - - /// - /// JPEG limit on sampling factors. - /// - public const int MAX_SAMP_FACTOR = 4; - - - ////////////////////////////////////////////////////////////////////////// - // implementation-specific constants - // - - // Maximum number of components (color channels) allowed in JPEG image. - // To meet the letter of the JPEG spec, set this to 255. However, darn - // few applications need more than 4 channels (maybe 5 for CMYK + alpha - // mask). We recommend 10 as a reasonable compromise; use 4 if you are - // really short on memory. (Each allowed component costs a hundred or so - // bytes of storage, whether actually used in an image or not.) - - /// - /// Maximum number of color channels allowed in JPEG image. - /// - public const int MAX_COMPONENTS = 10; - - - - /// - /// The size of sample. - /// - /// Are either: - /// 8 - for 8-bit sample values (the usual setting)
- /// 12 - for 12-bit sample values (not supported by this version)
- /// Only 8 and 12 are legal data precisions for lossy JPEG according to the JPEG standard. - /// Althought original IJG code claims it supports 12 bit images, our code does not support - /// anything except 8-bit images.
- public const int BITS_IN_JSAMPLE = 8; - - /// - /// DCT method used by default. - /// - public static J_DCT_METHOD JDCT_DEFAULT = J_DCT_METHOD.JDCT_ISLOW; - - /// - /// Fastest DCT method. - /// - public static J_DCT_METHOD JDCT_FASTEST = J_DCT_METHOD.JDCT_IFAST; - - /// - /// A tad under 64K to prevent overflows. - /// - public const int JPEG_MAX_DIMENSION = 65500; - - /// - /// The maximum sample value. - /// - public const int MAXJSAMPLE = 255; - - /// - /// The medium sample value. - /// - public const int CENTERJSAMPLE = 128; - - // Ordering of RGB data in scanlines passed to or from the application. - // RESTRICTIONS: - // 1. These macros only affect RGB<=>YCbCr color conversion, so they are not - // useful if you are using JPEG color spaces other than YCbCr or grayscale. - // 2. The color quantizer modules will not behave desirably if RGB_PIXELSIZE - // is not 3 (they don't understand about dummy color components!). So you - // can't use color quantization if you change that value. - - /// - /// Offset of Red in an RGB scanline element. - /// - public const int RGB_RED = 0; - - /// - /// Offset of Green in an RGB scanline element. - /// - public const int RGB_GREEN = 1; - - /// - /// Offset of Blue in an RGB scanline element. - /// - public const int RGB_BLUE = 2; - - /// - /// Bytes per RGB scanline element. - /// - public const int RGB_PIXELSIZE = 3; - - /// - /// The number of bits of lookahead. - /// - public const int HUFF_LOOKAHEAD = 8; - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/ReadResult.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/ReadResult.cs deleted file mode 100644 index 5769639cb..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/ReadResult.cs +++ /dev/null @@ -1,49 +0,0 @@ -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic -{ - /// - /// Describes a result of read operation. - /// - /// -#if EXPOSE_LIBJPEG - public -#endif - enum ReadResult - { - /// - /// Suspended due to lack of input data. Can occur only if a suspending data source is used. - /// - JPEG_SUSPENDED = 0, - /// - /// Found valid image datastream. - /// - JPEG_HEADER_OK = 1, - /// - /// Found valid table-specs-only datastream. - /// - JPEG_HEADER_TABLES_ONLY = 2, - /// - /// Reached a SOS marker (the start of a new scan) - /// - JPEG_REACHED_SOS = 3, - /// - /// Reached the EOI marker (end of image) - /// - JPEG_REACHED_EOI = 4, - /// - /// Completed reading one MCU row of compressed data. - /// - JPEG_ROW_COMPLETED = 5, - /// - /// Completed reading last MCU row of current scan. - /// - JPEG_SCAN_COMPLETED = 6 - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/jpeg_common_struct.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/jpeg_common_struct.cs deleted file mode 100644 index 077bddac9..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/jpeg_common_struct.cs +++ /dev/null @@ -1,364 +0,0 @@ -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -/* - * This file contains application interface routines that are used for both - * compression and decompression. - */ - -using System; -using System.Collections.Generic; -using System.Text; -using System.Reflection; -using System.Globalization; - -namespace BitMiracle.LibJpeg.Classic -{ - /// Base class for both JPEG compressor and decompresor. - /// - /// Routines that are to be used by both halves of the library are declared - /// to receive an instance of this class. There are no actual instances of - /// , only of - /// and - /// -#if EXPOSE_LIBJPEG - public -#endif - abstract class jpeg_common_struct - { - internal enum JpegState - { - DESTROYED = 0, - CSTATE_START = 100, /* after create_compress */ - CSTATE_SCANNING = 101, /* start_compress done, write_scanlines OK */ - CSTATE_RAW_OK = 102, /* start_compress done, write_raw_data OK */ - CSTATE_WRCOEFS = 103, /* jpeg_write_coefficients done */ - DSTATE_START = 200, /* after create_decompress */ - DSTATE_INHEADER = 201, /* reading header markers, no SOS yet */ - DSTATE_READY = 202, /* found SOS, ready for start_decompress */ - DSTATE_PRELOAD = 203, /* reading multiscan file in start_decompress*/ - DSTATE_PRESCAN = 204, /* performing dummy pass for 2-pass quant */ - DSTATE_SCANNING = 205, /* start_decompress done, read_scanlines OK */ - DSTATE_RAW_OK = 206, /* start_decompress done, read_raw_data OK */ - DSTATE_BUFIMAGE = 207, /* expecting jpeg_start_output */ - DSTATE_BUFPOST = 208, /* looking for SOS/EOI in jpeg_finish_output */ - DSTATE_RDCOEFS = 209, /* reading file in jpeg_read_coefficients */ - DSTATE_STOPPING = 210 /* looking for EOI in jpeg_finish_decompress */ - } - - // Error handler module - internal jpeg_error_mgr m_err; - - // Progress monitor, or null if none - internal jpeg_progress_mgr m_progress; - - internal JpegState m_global_state; /* For checking call sequence validity */ - - /// - /// Base constructor. - /// - /// - /// - public jpeg_common_struct() : this(new jpeg_error_mgr()) - { - } - - /// - /// Base constructor. - /// - /// The error manager. - /// - /// - public jpeg_common_struct(jpeg_error_mgr errorManager) - { - Err = errorManager; - } - - /// - /// Gets a value indicating whether this instance is Jpeg decompressor. - /// - /// - /// true if this is Jpeg decompressor; otherwise, false. - /// - public abstract bool IsDecompressor - { - get; - } - - /// - /// Progress monitor. - /// - /// The progress manager. - /// Default value: null. - public jpeg_progress_mgr Progress - { - get - { - return m_progress; - } - set - { - if (value == null) - throw new ArgumentNullException("value"); - - m_progress = value; - } - } - - /// - /// Error handler module. - /// - /// The error manager. - /// Error handling - public jpeg_error_mgr Err - { - get - { - return m_err; - } - set - { - if (value == null) - throw new ArgumentNullException("value"); - - m_err = value; - } - } - - /// - /// Gets the version of LibJpeg. - /// - /// The version of LibJpeg. - public static string Version - { - get - { - Version version = typeof(jpeg_common_struct).GetTypeInfo().Assembly.GetName().Version; - string versionString = version.Major.ToString(CultureInfo.InvariantCulture) + - "." + version.Minor.ToString(CultureInfo.InvariantCulture); - - versionString += "." + version.Build.ToString(CultureInfo.InvariantCulture); - versionString += "." + version.Revision.ToString(CultureInfo.InvariantCulture); - - return versionString; - } - } - - /// - /// Gets the LibJpeg's copyright. - /// - /// The copyright. - public static string Copyright - { - get - { - return "Copyright (C) 2008-2011, Bit Miracle"; - } - } - - /// - /// Creates the array of samples. - /// - /// The number of samples in row. - /// The number of rows. - /// The array of samples. - public static jvirt_array CreateSamplesArray(int samplesPerRow, int numberOfRows) - { - return new jvirt_array(samplesPerRow, numberOfRows, AllocJpegSamples); - } - - /// - /// Creates the array of blocks. - /// - /// The number of blocks in row. - /// The number of rows. - /// The array of blocks. - /// - public static jvirt_array CreateBlocksArray(int blocksPerRow, int numberOfRows) - { - return new jvirt_array(blocksPerRow, numberOfRows, allocJpegBlocks); - } - - /// - /// Creates 2-D sample array. - /// - /// The number of samples per row. - /// The number of rows. - /// The array of samples. - public static byte[][] AllocJpegSamples(int samplesPerRow, int numberOfRows) - { - byte[][] result = new byte[numberOfRows][]; - for (int i = 0; i < numberOfRows; ++i) - result[i] = new byte[samplesPerRow]; - - return result; - } - - // Creation of 2-D block arrays. - private static JBLOCK[][] allocJpegBlocks(int blocksPerRow, int numberOfRows) - { - JBLOCK[][] result = new JBLOCK[numberOfRows][]; - for (int i = 0; i < numberOfRows; ++i) - { - result[i] = new JBLOCK[blocksPerRow]; - for (int j = 0; j < blocksPerRow; ++j) - result[i][j] = new JBLOCK(); - } - return result; - } - - // Generic versions of jpeg_abort and jpeg_destroy that work on either - // flavor of JPEG object. These may be more convenient in some places. - - /// - /// Abort processing of a JPEG compression or decompression operation, - /// but don't destroy the object itself. - /// - /// Closing a data source or destination, if necessary, is the - /// application's responsibility. - /// - public void jpeg_abort() - { - /* Reset overall state for possible reuse of object */ - if (IsDecompressor) - { - m_global_state = JpegState.DSTATE_START; - - /* Try to keep application from accessing now-deleted marker list. - * A bit kludgy to do it here, but this is the most central place. - */ - jpeg_decompress_struct s = this as jpeg_decompress_struct; - if (s != null) - s.m_marker_list = null; - } - else - { - m_global_state = JpegState.CSTATE_START; - } - } - - /// - /// Destruction of a JPEG object. - /// - /// Closing a data source or destination, if necessary, is the - /// application's responsibility. - /// - public void jpeg_destroy() - { - // mark it destroyed - m_global_state = JpegState.DESTROYED; - } - - // Fatal errors (print message and exit) - - /// - /// Used for fatal errors (print message and exit). - /// - /// The message code. - public void ERREXIT(J_MESSAGE_CODE code) - { - ERREXIT((int)code); - } - - /// - /// Used for fatal errors (print message and exit). - /// - /// The message code. - /// The parameters of message. - public void ERREXIT(J_MESSAGE_CODE code, params object[] args) - { - ERREXIT((int)code, args); - } - - /// - /// Used for fatal errors (print message and exit). - /// - /// The message code. - /// The parameters of message. - public void ERREXIT(int code, params object[] args) - { - m_err.m_msg_code = code; - m_err.m_msg_parm = args; - m_err.error_exit(); - } - - // Nonfatal errors (we can keep going, but the data is probably corrupt) - - - /// - /// Used for non-fatal errors (we can keep going, but the data is probably corrupt). - /// - /// The message code. - public void WARNMS(J_MESSAGE_CODE code) - { - WARNMS((int)code); - } - - /// - /// Used for non-fatal errors (we can keep going, but the data is probably corrupt). - /// - /// The message code. - /// The parameters of message. - public void WARNMS(J_MESSAGE_CODE code, params object[] args) - { - WARNMS((int)code, args); - } - - /// - /// Used for non-fatal errors (we can keep going, but the data is probably corrupt). - /// - /// The message code. - /// The parameters of message. - public void WARNMS(int code, params object[] args) - { - m_err.m_msg_code = code; - m_err.m_msg_parm = args; - m_err.emit_message(-1); - } - - // Informational/debugging messages - - /// - /// Shows informational and debugging messages. - /// - /// See for description. - /// The message code. - /// - public void TRACEMS(int lvl, J_MESSAGE_CODE code) - { - TRACEMS(lvl, (int)code); - } - - /// - /// Shows informational and debugging messages. - /// - /// See for description. - /// The message code. - /// The parameters of message. - /// - public void TRACEMS(int lvl, J_MESSAGE_CODE code, params object[] args) - { - TRACEMS(lvl, (int)code, args); - } - - /// - /// Shows informational and debugging messages. - /// - /// See for description. - /// The message code. - /// The parameters of message. - /// - public void TRACEMS(int lvl, int code, params object[] args) - { - m_err.m_msg_code = code; - m_err.m_msg_parm = args; - m_err.emit_message(lvl); - } - } -} \ No newline at end of file diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/jpeg_component_info.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/jpeg_component_info.cs deleted file mode 100644 index 0414f3d87..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/jpeg_component_info.cs +++ /dev/null @@ -1,218 +0,0 @@ -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -using System; -using System.Collections.Generic; -using System.Text; - -using BitMiracle.LibJpeg.Classic.Internal; - -namespace BitMiracle.LibJpeg.Classic -{ - /// - /// Basic info about one component (color channel). - /// -#if EXPOSE_LIBJPEG - public -#endif - class jpeg_component_info - { - /* These values are fixed over the whole image. */ - /* For compression, they must be supplied by parameter setup; */ - /* for decompression, they are read from the SOF marker. */ - - private int component_id; - private int component_index; - private int h_samp_factor; - private int v_samp_factor; - private int quant_tbl_no; - - /* These values may vary between scans. */ - /* For compression, they must be supplied by parameter setup; */ - /* for decompression, they are read from the SOS marker. */ - /* The decompressor output side may not use these variables. */ - private int dc_tbl_no; - private int ac_tbl_no; - - /* Remaining fields should be treated as private by applications. */ - - /* These values are computed during compression or decompression startup: */ - /* Component's size in DCT blocks. - * Any dummy blocks added to complete an MCU are not counted; therefore - * these values do not depend on whether a scan is interleaved or not. - */ - private int width_in_blocks; - internal int height_in_blocks; - /* Size of a DCT block in samples. Always DCTSIZE for compression. - * For decompression this is the size of the output from one DCT block, - * reflecting any scaling we choose to apply during the IDCT step. - * Values of 1,2,4,8 are likely to be supported. Note that different - * components may receive different IDCT scalings. - */ - internal int DCT_scaled_size; - /* The downsampled dimensions are the component's actual, unpadded number - * of samples at the main buffer (preprocessing/compression interface), thus - * downsampled_width = ceil(image_width * Hi/Hmax) - * and similarly for height. For decompression, IDCT scaling is included, so - * downsampled_width = ceil(image_width * Hi/Hmax * DCT_scaled_size/DCTSIZE) - */ - internal int downsampled_width; /* actual width in samples */ - - internal int downsampled_height; /* actual height in samples */ - /* This flag is used only for decompression. In cases where some of the - * components will be ignored (eg grayscale output from YCbCr image), - * we can skip most computations for the unused components. - */ - internal bool component_needed; /* do we need the value of this component? */ - - /* These values are computed before starting a scan of the component. */ - /* The decompressor output side may not use these variables. */ - internal int MCU_width; /* number of blocks per MCU, horizontally */ - internal int MCU_height; /* number of blocks per MCU, vertically */ - internal int MCU_blocks; /* MCU_width * MCU_height */ - internal int MCU_sample_width; /* MCU width in samples, MCU_width*DCT_scaled_size */ - internal int last_col_width; /* # of non-dummy blocks across in last MCU */ - internal int last_row_height; /* # of non-dummy blocks down in last MCU */ - - /* Saved quantization table for component; null if none yet saved. - * See jpeg_input_controller comments about the need for this information. - * This field is currently used only for decompression. - */ - internal JQUANT_TBL quant_table; - - internal jpeg_component_info() - { - } - - internal void Assign(jpeg_component_info ci) - { - component_id = ci.component_id; - component_index = ci.component_index; - h_samp_factor = ci.h_samp_factor; - v_samp_factor = ci.v_samp_factor; - quant_tbl_no = ci.quant_tbl_no; - dc_tbl_no = ci.dc_tbl_no; - ac_tbl_no = ci.ac_tbl_no; - width_in_blocks = ci.width_in_blocks; - height_in_blocks = ci.height_in_blocks; - DCT_scaled_size = ci.DCT_scaled_size; - downsampled_width = ci.downsampled_width; - downsampled_height = ci.downsampled_height; - component_needed = ci.component_needed; - MCU_width = ci.MCU_width; - MCU_height = ci.MCU_height; - MCU_blocks = ci.MCU_blocks; - MCU_sample_width = ci.MCU_sample_width; - last_col_width = ci.last_col_width; - last_row_height = ci.last_row_height; - quant_table = ci.quant_table; - } - - /// - /// Identifier for this component (0..255) - /// - /// The component ID. - public int Component_id - { - get { return component_id; } - set { component_id = value; } - } - - /// - /// Its index in SOF or . - /// - /// The component index. - public int Component_index - { - get { return component_index; } - set { component_index = value; } - } - - /// - /// Horizontal sampling factor (1..4) - /// - /// The horizontal sampling factor. - public int H_samp_factor - { - get { return h_samp_factor; } - set { h_samp_factor = value; } - } - - /// - /// Vertical sampling factor (1..4) - /// - /// The vertical sampling factor. - public int V_samp_factor - { - get { return v_samp_factor; } - set { v_samp_factor = value; } - } - - /// - /// Quantization table selector (0..3) - /// - /// The quantization table selector. - public int Quant_tbl_no - { - get { return quant_tbl_no; } - set { quant_tbl_no = value; } - } - - /// - /// DC entropy table selector (0..3) - /// - /// The DC entropy table selector. - public int Dc_tbl_no - { - get { return dc_tbl_no; } - set { dc_tbl_no = value; } - } - - /// - /// AC entropy table selector (0..3) - /// - /// The AC entropy table selector. - public int Ac_tbl_no - { - get { return ac_tbl_no; } - set { ac_tbl_no = value; } - } - - /// - /// Gets or sets the width in blocks. - /// - /// The width in blocks. - public int Width_in_blocks - { - get { return width_in_blocks; } - set { width_in_blocks = value; } - } - - /// - /// Gets the downsampled width. - /// - /// The downsampled width. - public int Downsampled_width - { - get { return downsampled_width; } - } - - internal static jpeg_component_info[] createArrayOfComponents(int length) - { - if (length < 0) - throw new ArgumentOutOfRangeException("length"); - - jpeg_component_info[] result = new jpeg_component_info[length]; - for (int i = 0; i < result.Length; ++i) - result[i] = new jpeg_component_info(); - - return result; - } - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/jpeg_compress_struct.cs.REMOVED.git-id b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/jpeg_compress_struct.cs.REMOVED.git-id deleted file mode 100644 index 90b2842e5..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/jpeg_compress_struct.cs.REMOVED.git-id +++ /dev/null @@ -1 +0,0 @@ -efd39fc8e0e3681aeb3da59a67c3ac2ad7b858a2 \ No newline at end of file diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/jpeg_decompress_struct.cs.REMOVED.git-id b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/jpeg_decompress_struct.cs.REMOVED.git-id deleted file mode 100644 index f40eb5e80..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/jpeg_decompress_struct.cs.REMOVED.git-id +++ /dev/null @@ -1 +0,0 @@ -57234e24afc9b4d1637062735248d7e1c7812a48 \ No newline at end of file diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/jpeg_destination_mgr.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/jpeg_destination_mgr.cs deleted file mode 100644 index 876d3a1af..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/jpeg_destination_mgr.cs +++ /dev/null @@ -1,87 +0,0 @@ -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic -{ - /// - /// Data destination object for compression. - /// -#if EXPOSE_LIBJPEG - public -#endif - abstract class jpeg_destination_mgr - { - private byte[] m_buffer; - private int m_position; - private int m_free_in_buffer; /* # of byte spaces remaining in buffer */ - - /// - /// Initializes this instance. - /// - public abstract void init_destination(); - - /// - /// Empties output buffer. - /// - /// true if operation succeed; otherwise, false - public abstract bool empty_output_buffer(); - - /// - /// Term_destinations this instance. - /// - public abstract void term_destination(); - - /// - /// Emits a byte. - /// - /// The byte value. - /// true if operation succeed; otherwise, false - public virtual bool emit_byte(int val) - { - m_buffer[m_position] = (byte)val; - m_position++; - - if (--m_free_in_buffer == 0) - { - if (!empty_output_buffer()) - return false; - } - - return true; - } - - /// - /// Initializes the internal buffer. - /// - /// The buffer. - /// The offset. - protected void initInternalBuffer(byte[] buffer, int offset) - { - m_buffer = buffer; - m_free_in_buffer = buffer.Length - offset; - m_position = offset; - } - - /// - /// Gets the number of free bytes in buffer. - /// - /// The number of free bytes in buffer. - protected int freeInBuffer - { - get - { - return m_free_in_buffer; - } - } - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/jpeg_error_mgr.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/jpeg_error_mgr.cs deleted file mode 100644 index a5ed42d9d..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/jpeg_error_mgr.cs +++ /dev/null @@ -1,405 +0,0 @@ -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -/* - * This file contains simple error-reporting and trace-message routines. - * Many applications will want to override some or all of these routines. - * - * These routines are used by both the compression and decompression code. - */ - -using System; -using System.Collections.Generic; -using System.Text; -using System.Globalization; -using System.Diagnostics; - -namespace BitMiracle.LibJpeg.Classic -{ - /// - /// Contains simple error-reporting and trace-message routines. - /// - /// This class is used by both the compression and decompression code. - /// Error handling -#if EXPOSE_LIBJPEG - public -#endif - class jpeg_error_mgr - { - // The message ID code and any parameters are saved in fields below. - internal int m_msg_code; - internal object[] m_msg_parm; - - internal int m_trace_level; - internal int m_num_warnings; - - /// - /// Initializes a new instance of the class. - /// - public jpeg_error_mgr() - { - } - - /// - /// Gets or sets the maximum message level that will be displayed. - /// - /// Values are: - /// -1: recoverable corrupt-data warning, may want to abort.
- /// 0: important advisory messages (always display to user).
- /// 1: first level of tracing detail.
- /// 2, 3, ...: successively more detailed tracing messages. - ///
- /// - public int Trace_level - { - get { return m_trace_level; } - set { m_trace_level = value; } - } - - /// - /// Gets the number of corrupt-data warnings. - /// - /// The num_warnings. - /// For recoverable corrupt-data errors, we emit a warning message, but keep going - /// unless emit_message chooses to abort. - /// emit_message should count warnings in Num_warnings. The surrounding application - /// can check for bad data by seeing if Num_warnings is nonzero at the end of processing. - public int Num_warnings - { - get { return m_num_warnings; } - } - - /// - /// Receives control for a fatal error. - /// - /// This method calls output_message - /// and then throws an exception. - /// Error handling - public virtual void error_exit() - { - // Always display the message - output_message(); - - string buffer = format_message(); - throw new Exception(buffer); - } - - /// - /// Conditionally emit a trace or warning message. - /// - /// The message severity level.
- /// Values are:
- /// -1: recoverable corrupt-data warning, may want to abort.
- /// 0: important advisory messages (always display to user).
- /// 1: first level of tracing detail.
- /// 2, 3, ...: successively more detailed tracing messages. - /// - /// The main reason for overriding this method would be to abort on warnings. - /// This method calls output_message for message showing.
- /// - /// An application might override this method if it wanted to abort on - /// warnings or change the policy about which messages to display. - ///
- /// Error handling - public virtual void emit_message(int msg_level) - { - if (msg_level < 0) - { - /* It's a warning message. Since corrupt files may generate many warnings, - * the policy implemented here is to show only the first warning, - * unless trace_level >= 3. - */ - if (m_num_warnings == 0 || m_trace_level >= 3) - output_message(); - - /* Always count warnings in num_warnings. */ - m_num_warnings++; - } - else - { - /* It's a trace message. Show it if trace_level >= msg_level. */ - if (m_trace_level >= msg_level) - output_message(); - } - } - - /// - /// Actual output of any JPEG message. - /// - /// Override this to send messages somewhere other than Console. - /// Note that this method does not know how to generate a message, only where to send it. - /// For extending a generation of messages see format_message. - /// - /// Error handling - public virtual void output_message() - { - // Create the message - string buffer = format_message(); - - // Send it to console, adding a newline */ - Debug.WriteLine(buffer); - } - - /// - /// Constructs a readable error message string. - /// - /// This method is called by output_message. - /// Few applications should need to override this method. One possible reason for doing so is to - /// implement dynamic switching of error message language. - /// The formatted message - /// Error handling - public virtual string format_message() - { - string msgtext = GetMessageText(m_msg_code); - if (msgtext == null) - { - m_msg_parm = new object[] { m_msg_code }; - msgtext = GetMessageText(0); - } - - /* Format the message into the passed buffer */ - return string.Format(CultureInfo.CurrentCulture, msgtext, m_msg_parm); - } - - /// - /// Resets error manager to initial state. - /// - /// This is called during compression startup to reset trace/error - /// processing to default state. An application might possibly want to - /// override this method if it has additional error processing state. - /// - public virtual void reset_error_mgr() - { - m_num_warnings = 0; - - /* trace_level is not reset since it is an application-supplied parameter */ - - // may be useful as a flag for "no error" - m_msg_code = 0; - } - - /// - /// Gets the actual message texts. - /// - /// The message code. See for details. - /// The message text associated with code. - /// It may be useful for an application to add its own message texts that are handled - /// by the same mechanism. You can override GetMessageText for this purpose. If you number - /// the addon messages beginning at 1000 or so, you won't have to worry about conflicts - /// with the library's built-in messages. - /// - /// - /// Error handling - protected virtual string GetMessageText(int code) - { - switch ((J_MESSAGE_CODE)code) - { - default: - case J_MESSAGE_CODE.JMSG_NOMESSAGE: - return "Bogus message code {0}"; - - /* For maintenance convenience, list is alphabetical by message code name */ - case J_MESSAGE_CODE.JERR_ARITH_NOTIMPL: - return "Sorry, there are legal restrictions on arithmetic coding"; - case J_MESSAGE_CODE.JERR_BAD_BUFFER_MODE: - return "Bogus buffer control mode"; - case J_MESSAGE_CODE.JERR_BAD_COMPONENT_ID: - return "Invalid component ID {0} in SOS"; - case J_MESSAGE_CODE.JERR_BAD_DCT_COEF: - return "DCT coefficient out of range"; - case J_MESSAGE_CODE.JERR_BAD_DCTSIZE: - return "IDCT output block size {0} not supported"; - case J_MESSAGE_CODE.JERR_BAD_HUFF_TABLE: - return "Bogus Huffman table definition"; - case J_MESSAGE_CODE.JERR_BAD_IN_COLORSPACE: - return "Bogus input colorspace"; - case J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE: - return "Bogus JPEG colorspace"; - case J_MESSAGE_CODE.JERR_BAD_LENGTH: - return "Bogus marker length"; - case J_MESSAGE_CODE.JERR_BAD_MCU_SIZE: - return "Sampling factors too large for interleaved scan"; - case J_MESSAGE_CODE.JERR_BAD_PRECISION: - return "Unsupported JPEG data precision {0}"; - case J_MESSAGE_CODE.JERR_BAD_PROGRESSION: - return "Invalid progressive parameters Ss={0} Se={1} Ah={2} Al={3}"; - case J_MESSAGE_CODE.JERR_BAD_PROG_SCRIPT: - return "Invalid progressive parameters at scan script entry {0}"; - case J_MESSAGE_CODE.JERR_BAD_SAMPLING: - return "Bogus sampling factors"; - case J_MESSAGE_CODE.JERR_BAD_SCAN_SCRIPT: - return "Invalid scan script at entry {0}"; - case J_MESSAGE_CODE.JERR_BAD_STATE: - return "Improper call to JPEG library in state {0}"; - case J_MESSAGE_CODE.JERR_BAD_VIRTUAL_ACCESS: - return "Bogus virtual array access"; - case J_MESSAGE_CODE.JERR_BUFFER_SIZE: - return "Buffer passed to JPEG library is too small"; - case J_MESSAGE_CODE.JERR_CANT_SUSPEND: - return "Suspension not allowed here"; - case J_MESSAGE_CODE.JERR_CCIR601_NOTIMPL: - return "CCIR601 sampling not implemented yet"; - case J_MESSAGE_CODE.JERR_COMPONENT_COUNT: - return "Too many color components: {0}, max {1}"; - case J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL: - return "Unsupported color conversion request"; - case J_MESSAGE_CODE.JERR_DHT_INDEX: - return "Bogus DHT index {0}"; - case J_MESSAGE_CODE.JERR_DQT_INDEX: - return "Bogus DQT index {0}"; - case J_MESSAGE_CODE.JERR_EMPTY_IMAGE: - return "Empty JPEG image (DNL not supported)"; - case J_MESSAGE_CODE.JERR_EOI_EXPECTED: - return "Didn't expect more than one scan"; - case J_MESSAGE_CODE.JERR_FILE_WRITE: - return "Output file write error --- out of disk space?"; - case J_MESSAGE_CODE.JERR_FRACT_SAMPLE_NOTIMPL: - return "Fractional sampling not implemented yet"; - case J_MESSAGE_CODE.JERR_HUFF_CLEN_OVERFLOW: - return "Huffman code size table overflow"; - case J_MESSAGE_CODE.JERR_HUFF_MISSING_CODE: - return "Missing Huffman code table entry"; - case J_MESSAGE_CODE.JERR_IMAGE_TOO_BIG: - return "Maximum supported image dimension is {0} pixels"; - case J_MESSAGE_CODE.JERR_INPUT_EMPTY: - return "Empty input file"; - case J_MESSAGE_CODE.JERR_INPUT_EOF: - return "Premature end of input file"; - case J_MESSAGE_CODE.JERR_MISMATCHED_QUANT_TABLE: - return "Cannot transcode due to multiple use of quantization table {0}"; - case J_MESSAGE_CODE.JERR_MISSING_DATA: - return "Scan script does not transmit all data"; - case J_MESSAGE_CODE.JERR_MODE_CHANGE: - return "Invalid color quantization mode change"; - case J_MESSAGE_CODE.JERR_NOTIMPL: - return "Not implemented yet"; - case J_MESSAGE_CODE.JERR_NOT_COMPILED: - return "Requested feature was omitted at compile time"; - case J_MESSAGE_CODE.JERR_NO_HUFF_TABLE: - return "Huffman table 0x{0:X2} was not defined"; - case J_MESSAGE_CODE.JERR_NO_IMAGE: - return "JPEG datastream contains no image"; - case J_MESSAGE_CODE.JERR_NO_QUANT_TABLE: - return "Quantization table 0x{0:X2} was not defined"; - case J_MESSAGE_CODE.JERR_NO_SOI: - return "Not a JPEG file: starts with 0x{0:X2} 0x{1:X2}"; - case J_MESSAGE_CODE.JERR_OUT_OF_MEMORY: - return "Insufficient memory (case {0})"; - case J_MESSAGE_CODE.JERR_QUANT_COMPONENTS: - return "Cannot quantize more than {0} color components"; - case J_MESSAGE_CODE.JERR_QUANT_FEW_COLORS: - return "Cannot quantize to fewer than {0} colors"; - case J_MESSAGE_CODE.JERR_QUANT_MANY_COLORS: - return "Cannot quantize to more than {0} colors"; - case J_MESSAGE_CODE.JERR_SOF_DUPLICATE: - return "Invalid JPEG file structure: two SOF markers"; - case J_MESSAGE_CODE.JERR_SOF_NO_SOS: - return "Invalid JPEG file structure: missing SOS marker"; - case J_MESSAGE_CODE.JERR_SOF_UNSUPPORTED: - return "Unsupported JPEG process: SOF type 0x{0:X2}"; - case J_MESSAGE_CODE.JERR_SOI_DUPLICATE: - return "Invalid JPEG file structure: two SOI markers"; - case J_MESSAGE_CODE.JERR_SOS_NO_SOF: - return "Invalid JPEG file structure: SOS before SOF"; - case J_MESSAGE_CODE.JERR_TOO_LITTLE_DATA: - return "Application transferred too few scanlines"; - case J_MESSAGE_CODE.JERR_UNKNOWN_MARKER: - return "Unsupported marker type 0x{0:X2}"; - case J_MESSAGE_CODE.JERR_WIDTH_OVERFLOW: - return "Image too wide for this implementation"; - case J_MESSAGE_CODE.JTRC_16BIT_TABLES: - return "Caution: quantization tables are too coarse for baseline JPEG"; - case J_MESSAGE_CODE.JTRC_ADOBE: - return "Adobe APP14 marker: version {0}, flags 0x{1:X4} 0x{2:X4}, transform {3}"; - case J_MESSAGE_CODE.JTRC_APP0: - return "Unknown APP0 marker (not JFIF), length {0}"; - case J_MESSAGE_CODE.JTRC_APP14: - return "Unknown APP14 marker (not Adobe), length {0}"; - case J_MESSAGE_CODE.JTRC_DHT: - return "Define Huffman Table 0x{0:X2}"; - case J_MESSAGE_CODE.JTRC_DQT: - return "Define Quantization Table {0} precision {1}"; - case J_MESSAGE_CODE.JTRC_DRI: - return "Define Restart Interval {0}"; - case J_MESSAGE_CODE.JTRC_EOI: - return "End Of Image"; - case J_MESSAGE_CODE.JTRC_HUFFBITS: - return " {0:D3} {1:D3} {2:D3} {3:D3} {4:D3} {5:D3} {6:D3} {7:D3}"; - case J_MESSAGE_CODE.JTRC_JFIF: - return "JFIF APP0 marker: version {0}.{1:D2}, density {2}x{3} {4}"; - case J_MESSAGE_CODE.JTRC_JFIF_BADTHUMBNAILSIZE: - return "Warning: thumbnail image size does not match data length {0}"; - case J_MESSAGE_CODE.JTRC_JFIF_EXTENSION: - return "JFIF extension marker: type 0x{0:X2}, length {1}"; - case J_MESSAGE_CODE.JTRC_JFIF_THUMBNAIL: - return " with {0} x {1} thumbnail image"; - case J_MESSAGE_CODE.JTRC_MISC_MARKER: - return "Miscellaneous marker 0x{0:X2}, length {1}"; - case J_MESSAGE_CODE.JTRC_PARMLESS_MARKER: - return "Unexpected marker 0x{0:X2}"; - case J_MESSAGE_CODE.JTRC_QUANTVALS: - return " {0:D4} {1:D4} {2:D4} {3:D4} {4:D4} {5:D4} {6:D4} {7:D4}"; - case J_MESSAGE_CODE.JTRC_QUANT_3_NCOLORS: - return "Quantizing to {0} = {1}*{2}*{3} colors"; - case J_MESSAGE_CODE.JTRC_QUANT_NCOLORS: - return "Quantizing to {0} colors"; - case J_MESSAGE_CODE.JTRC_QUANT_SELECTED: - return "Selected {0} colors for quantization"; - case J_MESSAGE_CODE.JTRC_RECOVERY_ACTION: - return "At marker 0x{0:X2}, recovery action {1}"; - case J_MESSAGE_CODE.JTRC_RST: - return "RST{0}"; - case J_MESSAGE_CODE.JTRC_SMOOTH_NOTIMPL: - return "Smoothing not supported with nonstandard sampling ratios"; - case J_MESSAGE_CODE.JTRC_SOF: - return "Start Of Frame 0x{0:X2}: width={1}, height={2}, components={3}"; - case J_MESSAGE_CODE.JTRC_SOF_COMPONENT: - return " Component {0}: {1}hx{2}v q={3}"; - case J_MESSAGE_CODE.JTRC_SOI: - return "Start of Image"; - case J_MESSAGE_CODE.JTRC_SOS: - return "Start Of Scan: {0} components"; - case J_MESSAGE_CODE.JTRC_SOS_COMPONENT: - return " Component {0}: dc={1} ac={2}"; - case J_MESSAGE_CODE.JTRC_SOS_PARAMS: - return " Ss={0}, Se={1}, Ah={2}, Al={3}"; - case J_MESSAGE_CODE.JTRC_THUMB_JPEG: - return "JFIF extension marker: JPEG-compressed thumbnail image, length {0}"; - case J_MESSAGE_CODE.JTRC_THUMB_PALETTE: - return "JFIF extension marker: palette thumbnail image, length {0}"; - case J_MESSAGE_CODE.JTRC_THUMB_RGB: - return "JFIF extension marker: RGB thumbnail image, length {0}"; - case J_MESSAGE_CODE.JTRC_UNKNOWN_IDS: - return "Unrecognized component IDs {0} {1} {2}, assuming YCbCr"; - case J_MESSAGE_CODE.JWRN_ADOBE_XFORM: - return "Unknown Adobe color transform code {0}"; - case J_MESSAGE_CODE.JWRN_BOGUS_PROGRESSION: - return "Inconsistent progression sequence for component {0} coefficient {1}"; - case J_MESSAGE_CODE.JWRN_EXTRANEOUS_DATA: - return "Corrupt JPEG data: {0} extraneous bytes before marker 0x{1:X2}"; - case J_MESSAGE_CODE.JWRN_HIT_MARKER: - return "Corrupt JPEG data: premature end of data segment"; - case J_MESSAGE_CODE.JWRN_HUFF_BAD_CODE: - return "Corrupt JPEG data: bad Huffman code"; - case J_MESSAGE_CODE.JWRN_JFIF_MAJOR: - return "Warning: unknown JFIF revision number {0}.{1:D2}"; - case J_MESSAGE_CODE.JWRN_JPEG_EOF: - return "Premature end of JPEG file"; - case J_MESSAGE_CODE.JWRN_MUST_RESYNC: - return "Corrupt JPEG data: found marker 0x{0:X2} instead of RST{1}"; - case J_MESSAGE_CODE.JWRN_NOT_SEQUENTIAL: - return "Invalid SOS parameters for sequential JPEG"; - case J_MESSAGE_CODE.JWRN_TOO_MUCH_DATA: - return "Application transferred too many scanlines"; - case J_MESSAGE_CODE.JMSG_UNKNOWNMSGCODE: - return "Unknown message code (possibly it is an error from application)"; - } - } - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/jpeg_marker_struct.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/jpeg_marker_struct.cs deleted file mode 100644 index d7ccbef08..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/jpeg_marker_struct.cs +++ /dev/null @@ -1,84 +0,0 @@ -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic -{ - /// - /// Representation of special JPEG marker. - /// - /// You can't create instance of this class manually. - /// Concrete objects are instantiated by library and you can get them - /// through Marker_list property. - /// - /// - /// Special markers -#if EXPOSE_LIBJPEG - public -#endif - class jpeg_marker_struct - { - private byte m_marker; /* marker code: JPEG_COM, or JPEG_APP0+n */ - private int m_originalLength; /* # bytes of data in the file */ - private byte[] m_data; /* the data contained in the marker */ - - internal jpeg_marker_struct(byte marker, int originalDataLength, int lengthLimit) - { - m_marker = marker; - m_originalLength = originalDataLength; - m_data = new byte[lengthLimit]; - } - - /// - /// Gets the special marker. - /// - /// The marker value. - public byte Marker - { - get - { - return m_marker; - } - } - - /// - /// Gets the full length of original data associated with the marker. - /// - /// The length of original data associated with the marker. - /// This length excludes the marker length word, whereas the stored representation - /// within the JPEG file includes it. (Hence the maximum data length is really only 65533.) - /// - public int OriginalLength - { - get - { - return m_originalLength; - } - } - - /// - /// Gets the data associated with the marker. - /// - /// The data associated with the marker. - /// The length of this array doesn't exceed length_limit for the particular marker type. - /// Note that this length excludes the marker length word, whereas the stored representation - /// within the JPEG file includes it. (Hence the maximum data length is really only 65533.) - /// - public byte[] Data - { - get - { - return m_data; - } - } - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/jpeg_progress_mgr.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/jpeg_progress_mgr.cs deleted file mode 100644 index e69b8a917..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/jpeg_progress_mgr.cs +++ /dev/null @@ -1,91 +0,0 @@ -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic -{ - /// - /// The progress monitor object. - /// - /// Progress monitoring -#if EXPOSE_LIBJPEG - public -#endif - class jpeg_progress_mgr - { - private int m_passCounter; - private int m_passLimit; - private int m_completedPasses; - private int m_totalPasses; - - /// - /// Occurs when progress is changed. - /// - /// Progress monitoring - public event EventHandler OnProgress; - - /// - /// Gets or sets the number of work units completed in this pass. - /// - /// The number of work units completed in this pass. - /// Progress monitoring - public int Pass_counter - { - get { return m_passCounter; } - set { m_passCounter = value; } - } - - /// - /// Gets or sets the total number of work units in this pass. - /// - /// The total number of work units in this pass. - /// Progress monitoring - public int Pass_limit - { - get { return m_passLimit; } - set { m_passLimit = value; } - } - - /// - /// Gets or sets the number of passes completed so far. - /// - /// The number of passes completed so far. - /// Progress monitoring - public int Completed_passes - { - get { return m_completedPasses; } - set { m_completedPasses = value; } - } - - /// - /// Gets or sets the total number of passes expected. - /// - /// The total number of passes expected. - /// Progress monitoring - public int Total_passes - { - get { return m_totalPasses; } - set { m_totalPasses = value; } - } - - /// - /// Indicates that progress was changed. - /// - /// Call this method if you change some progress parameters manually. - /// This method ensures happening of the OnProgress event. - public void Updated() - { - if (OnProgress != null) - OnProgress(this, new EventArgs()); - } - } -} \ No newline at end of file diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/jpeg_source_mgr.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/jpeg_source_mgr.cs deleted file mode 100644 index b415f9d76..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/jpeg_source_mgr.cs +++ /dev/null @@ -1,296 +0,0 @@ -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic -{ - /// - /// Data source object for decompression. - /// -#if EXPOSE_LIBJPEG - public -#endif - abstract class jpeg_source_mgr - { - private byte[] m_next_input_byte; - private int m_bytes_in_buffer; /* # of bytes remaining (unread) in buffer */ - private int m_position; - - /// - /// Initializes this instance. - /// - public abstract void init_source(); - - /// - /// Fills input buffer - /// - /// true if operation succeed; otherwise, false - public abstract bool fill_input_buffer(); - - /// - /// Initializes the internal buffer. - /// - /// The buffer. - /// The size. - protected void initInternalBuffer(byte[] buffer, int size) - { - m_bytes_in_buffer = size; - m_next_input_byte = buffer; - m_position = 0; - } - - /// - /// Skip data - used to skip over a potentially large amount of - /// uninteresting data (such as an APPn marker). - /// - /// The number of bytes to skip. - /// Writers of suspendable-input applications must note that skip_input_data - /// is not granted the right to give a suspension return. If the skip extends - /// beyond the data currently in the buffer, the buffer can be marked empty so - /// that the next read will cause a fill_input_buffer call that can suspend. - /// Arranging for additional bytes to be discarded before reloading the input - /// buffer is the application writer's problem. - public virtual void skip_input_data(int num_bytes) - { - /* Just a dumb implementation for now. Could use fseek() except - * it doesn't work on pipes. Not clear that being smart is worth - * any trouble anyway --- large skips are infrequent. - */ - if (num_bytes > 0) - { - while (num_bytes > m_bytes_in_buffer) - { - num_bytes -= m_bytes_in_buffer; - fill_input_buffer(); - /* note we assume that fill_input_buffer will never return false, - * so suspension need not be handled. - */ - } - - m_position += num_bytes; - m_bytes_in_buffer -= num_bytes; - } - } - - /// - /// This is the default resync_to_restart method for data source - /// managers to use if they don't have any better approach. - /// - /// An instance of - /// The desired - /// false if suspension is required. - /// That method assumes that no backtracking is possible. - /// Some data source managers may be able to back up, or may have - /// additional knowledge about the data which permits a more - /// intelligent recovery strategy; such managers would - /// presumably supply their own resync method.

- /// - /// read_restart_marker calls resync_to_restart if it finds a marker other than - /// the restart marker it was expecting. (This code is *not* used unless - /// a nonzero restart interval has been declared.) cinfo.unread_marker is - /// the marker code actually found (might be anything, except 0 or FF). - /// The desired restart marker number (0..7) is passed as a parameter.

- /// - /// This routine is supposed to apply whatever error recovery strategy seems - /// appropriate in order to position the input stream to the next data segment. - /// Note that cinfo.unread_marker is treated as a marker appearing before - /// the current data-source input point; usually it should be reset to zero - /// before returning.

- /// - /// This implementation is substantially constrained by wanting to treat the - /// input as a data stream; this means we can't back up. Therefore, we have - /// only the following actions to work with:
- /// 1. Simply discard the marker and let the entropy decoder resume at next - /// byte of file.
- /// 2. Read forward until we find another marker, discarding intervening - /// data. (In theory we could look ahead within the current bufferload, - /// without having to discard data if we don't find the desired marker. - /// This idea is not implemented here, in part because it makes behavior - /// dependent on buffer size and chance buffer-boundary positions.)
- /// 3. Leave the marker unread (by failing to zero cinfo.unread_marker). - /// This will cause the entropy decoder to process an empty data segment, - /// inserting dummy zeroes, and then we will reprocess the marker.
- /// - /// #2 is appropriate if we think the desired marker lies ahead, while #3 is - /// appropriate if the found marker is a future restart marker (indicating - /// that we have missed the desired restart marker, probably because it got - /// corrupted).
- /// We apply #2 or #3 if the found marker is a restart marker no more than - /// two counts behind or ahead of the expected one. We also apply #2 if the - /// found marker is not a legal JPEG marker code (it's certainly bogus data). - /// If the found marker is a restart marker more than 2 counts away, we do #1 - /// (too much risk that the marker is erroneous; with luck we will be able to - /// resync at some future point).
- /// For any valid non-restart JPEG marker, we apply #3. This keeps us from - /// overrunning the end of a scan. An implementation limited to single-scan - /// files might find it better to apply #2 for markers other than EOI, since - /// any other marker would have to be bogus data in that case.
- public virtual bool resync_to_restart(jpeg_decompress_struct cinfo, int desired) - { - /* Always put up a warning. */ - cinfo.WARNMS(J_MESSAGE_CODE.JWRN_MUST_RESYNC, cinfo.m_unread_marker, desired); - - /* Outer loop handles repeated decision after scanning forward. */ - int action = 1; - for ( ; ; ) - { - if (cinfo.m_unread_marker < (int)JPEG_MARKER.SOF0) - { - /* invalid marker */ - action = 2; - } - else if (cinfo.m_unread_marker < (int)JPEG_MARKER.RST0 || - cinfo.m_unread_marker > (int)JPEG_MARKER.RST7) - { - /* valid non-restart marker */ - action = 3; - } - else - { - if (cinfo.m_unread_marker == ((int)JPEG_MARKER.RST0 + ((desired + 1) & 7)) - || cinfo.m_unread_marker == ((int)JPEG_MARKER.RST0 + ((desired + 2) & 7))) - { - /* one of the next two expected restarts */ - action = 3; - } - else if (cinfo.m_unread_marker == ((int)JPEG_MARKER.RST0 + ((desired - 1) & 7)) || - cinfo.m_unread_marker == ((int)JPEG_MARKER.RST0 + ((desired - 2) & 7))) - { - /* a prior restart, so advance */ - action = 2; - } - else - { - /* desired restart or too far away */ - action = 1; - } - } - - cinfo.TRACEMS(4, J_MESSAGE_CODE.JTRC_RECOVERY_ACTION, cinfo.m_unread_marker, action); - - switch (action) - { - case 1: - /* Discard marker and let entropy decoder resume processing. */ - cinfo.m_unread_marker = 0; - return true; - case 2: - /* Scan to the next marker, and repeat the decision loop. */ - if (!cinfo.m_marker.next_marker()) - return false; - break; - case 3: - /* Return without advancing past this marker. */ - /* Entropy decoder will be forced to process an empty segment. */ - return true; - } - } - } - - /// - /// Terminate source - called by jpeg_finish_decompress - /// after all data has been read. Often a no-op. - /// - /// NB: not called by jpeg_abort or jpeg_destroy; surrounding - /// application must deal with any cleanup that should happen even - /// for error exit. - public virtual void term_source() - { - } - - /// - /// Reads two bytes interpreted as an unsigned 16-bit integer. - /// - /// The result. - /// true if operation succeed; otherwise, false - public virtual bool GetTwoBytes(out int V) - { - if (!MakeByteAvailable()) - { - V = 0; - return false; - } - - m_bytes_in_buffer--; - V = m_next_input_byte[m_position] << 8; - m_position++; - - if (!MakeByteAvailable()) - return false; - - m_bytes_in_buffer--; - V += m_next_input_byte[m_position]; - m_position++; - return true; - } - - /// - /// Read a byte into variable V. - /// If must suspend, take the specified action (typically "return false"). - /// - /// The result. - /// true if operation succeed; otherwise, false - public virtual bool GetByte(out int V) - { - if (!MakeByteAvailable()) - { - V = 0; - return false; - } - - m_bytes_in_buffer--; - V = m_next_input_byte[m_position]; - m_position++; - return true; - } - - /// - /// Gets the bytes. - /// - /// The destination. - /// The amount. - /// The number of available bytes. - public virtual int GetBytes(byte[] dest, int amount) - { - int avail = amount; - if (avail > m_bytes_in_buffer) - avail = m_bytes_in_buffer; - - for (int i = 0; i < avail; i++) - { - dest[i] = m_next_input_byte[m_position]; - m_position++; - m_bytes_in_buffer--; - } - - return avail; - } - - /// - /// Functions for fetching data from the data source module. - /// - /// true if operation succeed; otherwise, false - /// At all times, cinfo.src.next_input_byte and .bytes_in_buffer reflect - /// the current restart point; we update them only when we have reached a - /// suitable place to restart if a suspension occurs. - public virtual bool MakeByteAvailable() - { - if (m_bytes_in_buffer == 0) - { - if (!fill_input_buffer()) - return false; - } - - return true; - } - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/jvirt_array.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/jvirt_array.cs deleted file mode 100644 index fc75723b5..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Classic/jvirt_array.cs +++ /dev/null @@ -1,105 +0,0 @@ -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -/* - * This file contains the JPEG system-independent memory management - * routines. - */ - -/* - * About virtual array management: - * - * Full-image-sized buffers are handled as "virtual" arrays. The array is still accessed a strip at a - * time, but the memory manager must save the whole array for repeated - * accesses. - * - * The Access method is responsible for making a specific strip area accessible. - */ - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic -{ - /// - /// JPEG virtual array. - /// - /// The type of array's elements. - /// You can't create virtual array manually. For creation use methods - /// and - /// . - /// -#if EXPOSE_LIBJPEG - public -#endif - class jvirt_array - { - internal delegate T[][] Allocator(int width, int height); - - private jpeg_common_struct m_cinfo; - - private T[][] m_buffer; /* => the in-memory buffer */ - - /// - /// Request a virtual 2-D array - /// - /// Width of array - /// Total virtual array height - /// The allocator. - internal jvirt_array(int width, int height, Allocator allocator) - { - m_cinfo = null; - m_buffer = allocator(width, height); - - Debug.Assert(m_buffer != null); - } - - /// - /// Gets or sets the error processor. - /// - /// The error processor.
- /// Default value: null - ///
- /// Uses only for calling - /// jpeg_common_struct.ERREXIT - /// on error. - public jpeg_common_struct ErrorProcessor - { - get { return m_cinfo; } - set { m_cinfo = value; } - } - - /// - /// Access the part of a virtual array. - /// - /// The first row in required block. - /// The number of required rows. - /// The required part of virtual array. - public T[][] Access(int startRow, int numberOfRows) - { - /* debugging check */ - if (startRow + numberOfRows > m_buffer.Length) - { - if (m_cinfo != null) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_VIRTUAL_ACCESS); - else - throw new InvalidOperationException("Bogus virtual array access"); - } - - /* Return proper part of the buffer */ - T[][] ret = new T[numberOfRows][]; - for (int i = 0; i < numberOfRows; i++) - ret[i] = m_buffer[startRow + i]; - - return ret; - } - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/CompressionParameters.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/CompressionParameters.cs deleted file mode 100644 index 0c45f3829..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/CompressionParameters.cs +++ /dev/null @@ -1,109 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -using BitMiracle.LibJpeg.Classic; - -namespace BitMiracle.LibJpeg -{ - /// - /// Parameters of compression. - /// - /// Being used in -#if EXPOSE_LIBJPEG - public -#endif - class CompressionParameters - { - private int m_quality = 75; - private int m_smoothingFactor; - private bool m_simpleProgressive; - - /// - /// Initializes a new instance of the class. - /// - public CompressionParameters() - { - } - - internal CompressionParameters(CompressionParameters parameters) - { - if (parameters == null) - throw new ArgumentNullException("parameters"); - - m_quality = parameters.m_quality; - m_smoothingFactor = parameters.m_smoothingFactor; - m_simpleProgressive = parameters.m_simpleProgressive; - } - - /// - /// Determines whether the specified is equal to this instance. - /// - /// The to compare with this instance. - /// - /// true if the specified is equal to this instance; otherwise, false. - /// - public override bool Equals(object obj) - { - CompressionParameters parameters = obj as CompressionParameters; - if (parameters == null) - return false; - - return (m_quality == parameters.m_quality && - m_smoothingFactor == parameters.m_smoothingFactor && - m_simpleProgressive == parameters.m_simpleProgressive); - } - - /// - /// Returns a hash code for this instance. - /// - /// - /// A hash code for this instance, suitable for use in hashing algorithms - /// and data structures like a hash table. - /// - public override int GetHashCode() - { - return base.GetHashCode(); - } - - /// - /// Gets or sets the quality of JPEG image. - /// - /// Default value: 75
- /// The quality value is expressed on the 0..100 scale. - ///
- /// The quality of JPEG image. - public int Quality - { - get { return m_quality; } - set { m_quality = value; } - } - - /// - /// Gets or sets the coefficient of image smoothing. - /// - /// Default value: 0
- /// If non-zero, the input image is smoothed; the value should be 1 for - /// minimal smoothing to 100 for maximum smoothing. - ///
- /// The coefficient of image smoothing. - public int SmoothingFactor - { - get { return m_smoothingFactor; } - set { m_smoothingFactor = value; } - } - - /// - /// Gets or sets a value indicating whether to write a progressive-JPEG file. - /// - /// - /// true for writing a progressive-JPEG file; false - /// for non-progressive JPEG files. - /// - public bool SimpleProgressive - { - get { return m_simpleProgressive; } - set { m_simpleProgressive = value; } - } - } -} \ No newline at end of file diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/DecompressionParameters.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/DecompressionParameters.cs deleted file mode 100644 index d12874c2c..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/DecompressionParameters.cs +++ /dev/null @@ -1,243 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -using BitMiracle.LibJpeg.Classic; - -namespace BitMiracle.LibJpeg -{ - class DecompressionParameters - { - private Colorspace m_outColorspace = Colorspace.Unknown; - private int m_scaleNumerator = 1; - private int m_scaleDenominator = 1; - private bool m_bufferedImage; - private bool m_rawDataOut; - private DCTMethod m_dctMethod = (DCTMethod)JpegConstants.JDCT_DEFAULT; - private DitherMode m_ditherMode = DitherMode.FloydSteinberg; - private bool m_doFancyUpsampling = true; - private bool m_doBlockSmoothing = true; - private bool m_quantizeColors; - private bool m_twoPassQuantize = true; - private int m_desiredNumberOfColors = 256; - private bool m_enableOnePassQuantizer; - private bool m_enableExternalQuant; - private bool m_enableTwoPassQuantizer; - private int m_traceLevel; - - public int TraceLevel - { - get - { - return m_traceLevel; - } - set - { - m_traceLevel = value; - } - } - - /* Decompression processing parameters --- these fields must be set before - * calling jpeg_start_decompress(). Note that jpeg_read_header() initializes - * them to default values. - */ - - // colorspace for output - public Colorspace OutColorspace - { - get - { - return m_outColorspace; - } - set - { - m_outColorspace = value; - } - } - - // fraction by which to scale image - public int ScaleNumerator - { - get - { - return m_scaleNumerator; - } - set - { - m_scaleNumerator = value; - } - } - - public int ScaleDenominator - { - get - { - return m_scaleDenominator; - } - set - { - m_scaleDenominator = value; - } - } - - // true=multiple output passes - public bool BufferedImage - { - get - { - return m_bufferedImage; - } - set - { - m_bufferedImage = value; - } - } - - // true=downsampled data wanted - public bool RawDataOut - { - get - { - return m_rawDataOut; - } - set - { - m_rawDataOut = value; - } - } - - // IDCT algorithm selector - public DCTMethod DCTMethod - { - get - { - return m_dctMethod; - } - set - { - m_dctMethod = value; - } - } - - // true=apply fancy upsampling - public bool DoFancyUpsampling - { - get - { - return m_doFancyUpsampling; - } - set - { - m_doFancyUpsampling = value; - } - } - - // true=apply interblock smoothing - public bool DoBlockSmoothing - { - get - { - return m_doBlockSmoothing; - } - set - { - m_doBlockSmoothing = value; - } - } - - // true=colormapped output wanted - public bool QuantizeColors - { - get - { - return m_quantizeColors; - } - set - { - m_quantizeColors = value; - } - } - - /* the following are ignored if not quantize_colors: */ - - // type of color dithering to use - public DitherMode DitherMode - { - get - { - return m_ditherMode; - } - set - { - m_ditherMode = value; - } - } - - // true=use two-pass color quantization - public bool TwoPassQuantize - { - get - { - return m_twoPassQuantize; - } - set - { - m_twoPassQuantize = value; - } - } - - // max # colors to use in created colormap - public int DesiredNumberOfColors - { - get - { - return m_desiredNumberOfColors; - } - set - { - m_desiredNumberOfColors = value; - } - } - - /* these are significant only in buffered-image mode: */ - - // enable future use of 1-pass quantizer - public bool EnableOnePassQuantizer - { - get - { - return m_enableOnePassQuantizer; - } - set - { - m_enableOnePassQuantizer = value; - } - } - - // enable future use of external colormap - public bool EnableExternalQuant - { - get - { - return m_enableExternalQuant; - } - set - { - m_enableExternalQuant = value; - } - } - - // enable future use of 2-pass quantizer - public bool EnableTwoPassQuantizer - { - get - { - return m_enableTwoPassQuantizer; - } - set - { - m_enableTwoPassQuantizer = value; - } - } - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/DecompressorToJpegImage.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/DecompressorToJpegImage.cs deleted file mode 100644 index e3fd5ce32..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/DecompressorToJpegImage.cs +++ /dev/null @@ -1,95 +0,0 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// -// -// Decompresses a jpeg image. -// -// -------------------------------------------------------------------------------------------------------------------- - -namespace BitMiracle.LibJpeg -{ - using System; - using System.IO; - - using ImageProcessorCore; - - /// - /// Decompresses a jpeg image. - /// - internal class DecompressorToJpegImage : IDecompressDestination - { - /// - /// The jpeg image. - /// - private readonly JpegImage jpegImage; - - /// - /// Initializes a new instance of the class. - /// - /// - /// The jpeg image. - /// - internal DecompressorToJpegImage(JpegImage jpegImage) - { - this.jpegImage = jpegImage; - } - - /// - /// Gets the stream with decompressed data. - /// - public Stream Output => null; - - /// - /// Sets the image attributes. - /// - /// - /// The containing attributes. - /// - /// - /// - public void SetImageAttributes(LoadedImageAttributes parameters) - { - if (parameters.Width > ImageBase.MaxWidth || parameters.Height > ImageBase.MaxHeight) - { - throw new ArgumentOutOfRangeException( - $"The input jpg '{ parameters.Width }x{ parameters.Height }' is bigger then the max allowed size '{ ImageBase.MaxWidth }x{ ImageBase.MaxHeight }'"); - } - - this.jpegImage.Width = parameters.Width; - this.jpegImage.Height = parameters.Height; - this.jpegImage.BitsPerComponent = 8; - this.jpegImage.ComponentsPerSample = (byte)parameters.ComponentsPerSample; - this.jpegImage.Colorspace = parameters.Colorspace; - } - - /// - /// Begins writing. - /// - /// Not implemented. - public void BeginWrite() - { - } - - /// - /// Processes the given row of pixels. - /// - /// - /// The representing the row. - /// - public void ProcessPixelsRow(byte[] row) - { - SampleRow samplesRow = new SampleRow(row, this.jpegImage.Width, this.jpegImage.BitsPerComponent, this.jpegImage.ComponentsPerSample); - this.jpegImage.addSampleRow(samplesRow); - } - - /// - /// Ends write. - /// - /// Not implemented. - public void EndWrite() - { - } - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Enumerations.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Enumerations.cs deleted file mode 100644 index 3c82d9316..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Enumerations.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg -{ - /// - /// Known color spaces. - /// -#if EXPOSE_LIBJPEG - public -#endif - enum Colorspace - { - /// - /// Unspecified colorspace - /// - Unknown, - - /// - /// Grayscale - /// - Grayscale, - - /// - /// RGB - /// - RGB, - - /// - /// YCbCr (also known as YUV) - /// - YCbCr, - - /// - /// CMYK - /// - CMYK, - - /// - /// YCbCrK - /// - YCCK - } - - /// - /// DCT/IDCT algorithm options. - /// - enum DCTMethod - { - IntegerSlow, /* slow but accurate integer algorithm */ - IntegerFast, /* faster, less accurate integer method */ - Float /* floating-point: accurate, fast on fast HW */ - } - - /// - /// Dithering options for decompression. - /// - enum DitherMode - { - None, /* no dithering */ - Ordered, /* simple ordered dither */ - FloydSteinberg /* Floyd-Steinberg error diffusion dither */ - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/IDecompressDestination.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/IDecompressDestination.cs deleted file mode 100644 index c57fc3f40..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/IDecompressDestination.cs +++ /dev/null @@ -1,51 +0,0 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// -// -------------------------------------------------------------------------------------------------------------------- - -namespace BitMiracle.LibJpeg -{ - using System.IO; - - /// - /// Common interface for processing of decompression. - /// - internal interface IDecompressDestination - { - /// - /// Gets the stream with decompressed data. - /// - Stream Output - { - get; - } - - /// - /// Sets the image attributes. - /// - /// - /// The containing attributes. - /// - void SetImageAttributes(LoadedImageAttributes parameters); - - /// - /// Begins writing. Called before decompression - /// - void BeginWrite(); - - /// - /// Processes the given row of pixels. - /// - /// - /// The representing the row. - /// - void ProcessPixelsRow(byte[] row); - - /// - /// Ends writing. Called after decompression - /// - void EndWrite(); - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/IRawImage.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/IRawImage.cs deleted file mode 100644 index 34477c121..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/IRawImage.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace BitMiracle.LibJpeg -{ - interface IRawImage - { - int Width - { get; } - - int Height - { get; } - - Colorspace Colorspace - { get; } - - int ComponentsPerPixel - { get; } - - void BeginRead(); - byte[] GetPixelRow(); - void EndRead(); - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Jpeg.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Jpeg.cs deleted file mode 100644 index 1cd921462..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Jpeg.cs +++ /dev/null @@ -1,247 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Text; - -using BitMiracle.LibJpeg.Classic; - -namespace BitMiracle.LibJpeg -{ - /// - /// Internal wrapper for classic jpeg compressor and decompressor - /// - class Jpeg - { - private jpeg_compress_struct m_compressor = new jpeg_compress_struct(new jpeg_error_mgr()); - private jpeg_decompress_struct m_decompressor = new jpeg_decompress_struct(new jpeg_error_mgr()); - - private CompressionParameters m_compressionParameters = new CompressionParameters(); - private DecompressionParameters m_decompressionParameters = new DecompressionParameters(); - - /// - /// Advanced users may set specific parameters of compression - /// - public CompressionParameters CompressionParameters - { - get - { - return m_compressionParameters; - } - set - { - if (value == null) - throw new ArgumentNullException("value"); - - m_compressionParameters = value; - } - } - - /// - /// Advanced users may set specific parameters of decompression - /// - public DecompressionParameters DecompressionParameters - { - get - { - return m_decompressionParameters; - } - set - { - if (value == null) - throw new ArgumentNullException("value"); - - m_decompressionParameters = value; - } - } - - /// - /// Compresses any image described as ICompressSource to JPEG - /// - /// Contains description of input image - /// Stream for output of compressed JPEG - public void Compress(IRawImage source, Stream output) - { - if (source == null) - throw new ArgumentNullException("source"); - - if (output == null) - throw new ArgumentNullException("output"); - - m_compressor.Image_width = source.Width; - m_compressor.Image_height = source.Height; - m_compressor.In_color_space = (J_COLOR_SPACE)source.Colorspace; - m_compressor.Input_components = source.ComponentsPerPixel; - //m_compressor.Data_precision = source.DataPrecision; - - m_compressor.jpeg_set_defaults(); - - //we need to set density parameters after setting of default jpeg parameters - //m_compressor.Density_unit = source.DensityUnit; - //m_compressor.X_density = (short)source.DensityX; - //m_compressor.Y_density = (short)source.DensityY; - - applyParameters(m_compressionParameters); - - // Specify data destination for compression - m_compressor.jpeg_stdio_dest(output); - - // Start compression - m_compressor.jpeg_start_compress(true); - - // Process pixels - source.BeginRead(); - while (m_compressor.Next_scanline < m_compressor.Image_height) - { - byte[] row = source.GetPixelRow(); - if (row == null) - { -#if !SILVERLIGHT - throw new InvalidDataException("Row of pixels is null"); -#else - // System.IO.InvalidDataException is not available in Silverlight - throw new IOException("Row of pixels is null"); -#endif - } - - byte[][] rowForDecompressor = new byte[1][]; - rowForDecompressor[0] = row; - m_compressor.jpeg_write_scanlines(rowForDecompressor, 1); - } - source.EndRead(); - - // Finish compression and release memory - m_compressor.jpeg_finish_compress(); - } - - /// - /// Decompresses JPEG image to any image described as ICompressDestination - /// - /// Stream with JPEG data - /// Stream for output of compressed JPEG - public void Decompress(Stream jpeg, IDecompressDestination destination) - { - if (jpeg == null) - throw new ArgumentNullException("jpeg"); - - if (destination == null) - throw new ArgumentNullException("destination"); - - beforeDecompress(jpeg); - - // Start decompression - m_decompressor.jpeg_start_decompress(); - - LoadedImageAttributes parameters = getImageParametersFromDecompressor(); - destination.SetImageAttributes(parameters); - destination.BeginWrite(); - - /* Process data */ - while (m_decompressor.Output_scanline < m_decompressor.Output_height) - { - byte[][] row = jpeg_common_struct.AllocJpegSamples(m_decompressor.Output_width * m_decompressor.Output_components, 1); - m_decompressor.jpeg_read_scanlines(row, 1); - destination.ProcessPixelsRow(row[0]); - } - - destination.EndWrite(); - - // Finish decompression and release memory. - m_decompressor.jpeg_finish_decompress(); - } - - /// - /// Tunes decompressor - /// - /// Stream with input compressed JPEG data - private void beforeDecompress(Stream jpeg) - { - m_decompressor.jpeg_stdio_src(jpeg); - /* Read file header, set default decompression parameters */ - m_decompressor.jpeg_read_header(true); - - applyParameters(m_decompressionParameters); - m_decompressor.jpeg_calc_output_dimensions(); - } - - private LoadedImageAttributes getImageParametersFromDecompressor() - { - LoadedImageAttributes result = new LoadedImageAttributes(); - result.Colorspace = (Colorspace)m_decompressor.Out_color_space; - result.QuantizeColors = m_decompressor.Quantize_colors; - result.Width = m_decompressor.Output_width; - result.Height = m_decompressor.Output_height; - result.ComponentsPerSample = m_decompressor.Out_color_components; - result.Components = m_decompressor.Output_components; - result.ActualNumberOfColors = m_decompressor.Actual_number_of_colors; - result.Colormap = m_decompressor.Colormap; - result.DensityUnit = m_decompressor.Density_unit; - result.DensityX = m_decompressor.X_density; - result.DensityY = m_decompressor.Y_density; - return result; - } - - public jpeg_compress_struct ClassicCompressor - { - get - { - return m_compressor; - } - } - - public jpeg_decompress_struct ClassicDecompressor - { - get - { - return m_decompressor; - } - } - - /// - /// Delegate for application-supplied marker processing methods. - /// Need not pass marker code since it is stored in cinfo.unread_marker. - /// - public delegate bool MarkerParser(Jpeg decompressor); - - /* Install a special processing method for COM or APPn markers. */ - public void SetMarkerProcessor(int markerCode, MarkerParser routine) - { - jpeg_decompress_struct.jpeg_marker_parser_method f = delegate { return routine(this); }; - m_decompressor.jpeg_set_marker_processor(markerCode, f); - } - - private void applyParameters(DecompressionParameters parameters) - { - Debug.Assert(parameters != null); - - if (parameters.OutColorspace != Colorspace.Unknown) - m_decompressor.Out_color_space = (J_COLOR_SPACE)parameters.OutColorspace; - - m_decompressor.Scale_num = parameters.ScaleNumerator; - m_decompressor.Scale_denom = parameters.ScaleDenominator; - m_decompressor.Buffered_image = parameters.BufferedImage; - m_decompressor.Raw_data_out = parameters.RawDataOut; - m_decompressor.Dct_method = (J_DCT_METHOD)parameters.DCTMethod; - m_decompressor.Dither_mode = (J_DITHER_MODE)parameters.DitherMode; - m_decompressor.Do_fancy_upsampling = parameters.DoFancyUpsampling; - m_decompressor.Do_block_smoothing = parameters.DoBlockSmoothing; - m_decompressor.Quantize_colors = parameters.QuantizeColors; - m_decompressor.Two_pass_quantize = parameters.TwoPassQuantize; - m_decompressor.Desired_number_of_colors = parameters.DesiredNumberOfColors; - m_decompressor.Enable_1pass_quant = parameters.EnableOnePassQuantizer; - m_decompressor.Enable_external_quant = parameters.EnableExternalQuant; - m_decompressor.Enable_2pass_quant = parameters.EnableTwoPassQuantizer; - m_decompressor.Err.Trace_level = parameters.TraceLevel; - } - - private void applyParameters(CompressionParameters parameters) - { - Debug.Assert(parameters != null); - - m_compressor.Smoothing_factor = parameters.SmoothingFactor; - m_compressor.jpeg_set_quality(parameters.Quality, true); - if (parameters.SimpleProgressive) - m_compressor.jpeg_simple_progression(); - } - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/JpegImage.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/JpegImage.cs deleted file mode 100644 index f17ea8a2d..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/JpegImage.cs +++ /dev/null @@ -1,360 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; - -using BitMiracle.LibJpeg.Classic; - -namespace BitMiracle.LibJpeg -{ - /// - /// Main class for work with JPEG images. - /// -#if EXPOSE_LIBJPEG - public -#endif - sealed class JpegImage : IDisposable - { - private bool m_alreadyDisposed; - - /// - /// Description of image pixels (samples) - /// - private List m_rows = new List(); - - private int m_width; - private int m_height; - private byte m_bitsPerComponent; - private byte m_componentsPerSample; - private Colorspace m_colorspace; - - // Fields below (m_compressedData, m_decompressedData, m_bitmap) are not initialized in constructors necessarily. - // Instead direct access to these field you should use corresponding properties (compressedData, decompressedData, bitmap) - // Such agreement allows to load required data (e.g. compress image) only by request. - - /// - /// Bytes of jpeg image. Refreshed when m_compressionParameters changed. - /// - private MemoryStream m_compressedData; - - /// - /// Current compression parameters corresponding with compressed data. - /// - private CompressionParameters m_compressionParameters; - - /// - /// Bytes of decompressed image (bitmap) - /// - private MemoryStream m_decompressedData; - - /// - /// Creates from stream with an arbitrary image data - /// - /// Stream containing bytes of image in - /// arbitrary format (BMP, Jpeg, GIF, PNG, TIFF, e.t.c) - public JpegImage(Stream imageData) - { - createFromStream(imageData); - } - - /// - /// Creates from pixels - /// - /// Description of pixels. - /// Colorspace of image. - /// - public JpegImage(SampleRow[] sampleData, Colorspace colorspace) - { - if (sampleData == null) - throw new ArgumentNullException("sampleData"); - - if (sampleData.Length == 0) - throw new ArgumentException("sampleData must be no empty"); - - if (colorspace == Colorspace.Unknown) - throw new ArgumentException("Unknown colorspace"); - - m_rows = new List(sampleData); - - SampleRow firstRow = m_rows[0]; - m_width = firstRow.Length; - m_height = m_rows.Count; - - Sample firstSample = firstRow[0]; - m_bitsPerComponent = firstSample.BitsPerComponent; - m_componentsPerSample = firstSample.ComponentCount; - m_colorspace = colorspace; - } - - /// - /// Frees and releases all resources allocated by this - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - private void Dispose(bool disposing) - { - if (!m_alreadyDisposed) - { - if (disposing) - { - // dispose managed resources - if (m_compressedData != null) - m_compressedData.Dispose(); - - if (m_decompressedData != null) - m_decompressedData.Dispose(); - } - - // free native resources - m_compressionParameters = null; - m_compressedData = null; - m_decompressedData = null; - m_rows = null; - m_alreadyDisposed = true; - } - } - - /// - /// Gets the width of image in samples. - /// - /// The width of image. - public int Width - { - get - { - return m_width; - } - internal set - { - m_width = value; - } - } - - /// - /// Gets the height of image in samples. - /// - /// The height of image. - public int Height - { - get - { - return m_height; - } - internal set - { - m_height = value; - } - } - - /// - /// Gets the number of color components per sample. - /// - /// The number of color components per sample. - public byte ComponentsPerSample - { - get - { - return m_componentsPerSample; - } - internal set - { - m_componentsPerSample = value; - } - } - - /// - /// Gets the number of bits per color component of sample. - /// - /// The number of bits per color component. - public byte BitsPerComponent - { - get - { - return m_bitsPerComponent; - } - internal set - { - m_bitsPerComponent = value; - } - } - - /// - /// Gets the colorspace of image. - /// - /// The colorspace of image. - public Colorspace Colorspace - { - get - { - return m_colorspace; - } - internal set - { - m_colorspace = value; - } - } - - - /// - /// Retrieves the required row of image. - /// - /// The number of row. - /// Image row of samples. - public SampleRow GetRow(int rowNumber) - { - return m_rows[rowNumber]; - } - - /// - /// Writes compressed JPEG image to stream. - /// - /// Output stream. - public void WriteJpeg(Stream output) - { - WriteJpeg(output, new CompressionParameters()); - } - - /// - /// Compresses image to JPEG with given parameters and writes it to stream. - /// - /// Output stream. - /// The parameters of compression. - public void WriteJpeg(Stream output, CompressionParameters parameters) - { - compress(parameters); - compressedData.WriteTo(output); - } - - /// - /// Writes decompressed image data as bitmap to stream. - /// - /// Output stream. - public void WriteBitmap(Stream output) - { - decompressedData.WriteTo(output); - } - - private MemoryStream compressedData - { - get - { - if (m_compressedData == null) - compress(new CompressionParameters()); - - Debug.Assert(m_compressedData != null); - Debug.Assert(m_compressedData.Length != 0); - - return m_compressedData; - } - } - - private MemoryStream decompressedData - { - get - { - if (m_decompressedData == null) - fillDecompressedData(); - - Debug.Assert(m_decompressedData != null); - - return m_decompressedData; - } - } - - /// - /// Needs for DecompressorToJpegImage class - /// - internal void addSampleRow(SampleRow row) - { - if (row == null) - throw new ArgumentNullException("row"); - - m_rows.Add(row); - } - - /// - /// Checks if imageData contains jpeg image - /// - private static bool isCompressed(Stream imageData) - { - if (imageData == null) - return false; - - if (imageData.Length <= 2) - return false; - - imageData.Seek(0, SeekOrigin.Begin); - int first = imageData.ReadByte(); - int second = imageData.ReadByte(); - return (first == 0xFF && second == (int)JPEG_MARKER.SOI); - } - - private void createFromStream(Stream imageData) - { - if (imageData == null) - throw new ArgumentNullException("imageData"); - - if (isCompressed(imageData)) - { - m_compressedData = Utils.CopyStream(imageData); - decompress(); - } - else - { - throw new NotImplementedException("JpegImage.createFromStream(Stream)"); - } - } - - private void compress(CompressionParameters parameters) - { - Debug.Assert(m_rows != null); - Debug.Assert(m_rows.Count != 0); - - RawImage source = new RawImage(m_rows, m_colorspace); - compress(source, parameters); - } - - private void compress(IRawImage source, CompressionParameters parameters) - { - Debug.Assert(source != null); - - if (!needCompressWith(parameters)) - return; - - m_compressedData = new MemoryStream(); - m_compressionParameters = new CompressionParameters(parameters); - - Jpeg jpeg = new Jpeg(); - jpeg.CompressionParameters = m_compressionParameters; - jpeg.Compress(source, m_compressedData); - } - - private bool needCompressWith(CompressionParameters parameters) - { - return m_compressedData == null || - m_compressionParameters == null || - !m_compressionParameters.Equals(parameters); - } - - private void decompress() - { - Jpeg jpeg = new Jpeg(); - jpeg.Decompress(compressedData, new DecompressorToJpegImage(this)); - } - - private void fillDecompressedData() - { - Debug.Assert(m_decompressedData == null); - - m_decompressedData = new MemoryStream(); - BitmapDestination dest = new BitmapDestination(m_decompressedData); - - Jpeg jpeg = new Jpeg(); - jpeg.Decompress(compressedData, dest); - } - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/LoadedImageAttributes.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/LoadedImageAttributes.cs deleted file mode 100644 index 17bf0de93..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/LoadedImageAttributes.cs +++ /dev/null @@ -1,274 +0,0 @@ -namespace BitMiracle.LibJpeg -{ - using BitMiracle.LibJpeg.Classic; - - /// - /// Holds parameters of image for decompression (IDecomressDesination) - /// - class LoadedImageAttributes - { - /// - /// The m_colorspace. - /// - private Colorspace m_colorspace; - - /// - /// The m_quantize colors. - /// - private bool m_quantizeColors; - - /// - /// The m_width. - /// - private int m_width; - - /// - /// The m_height. - /// - private int m_height; - - /// - /// The m_components per sample. - /// - private int m_componentsPerSample; - - /// - /// The m_components. - /// - private int m_components; - - /// - /// The m_actual number of colors. - /// - private int m_actualNumberOfColors; - - /// - /// The m_colormap. - /// - private byte[][] m_colormap; - - /// - /// The m_density unit. - /// - private DensityUnit m_densityUnit; - - /// - /// The m_density x. - /// - private int m_densityX; - - /// - /// The m_density y. - /// - private int m_densityY; - - /* Decompression processing parameters --- these fields must be set before - * calling jpeg_start_decompress(). Note that jpeg_read_header() initializes - * them to default values. - */ - - // colorspace for output - /// - /// Gets the colorspace. - /// - public Colorspace Colorspace - { - get - { - return this.m_colorspace; - } - - internal set - { - this.m_colorspace = value; - } - } - - // true=colormapped output wanted - /// - /// Gets a value indicating whether quantize colors. - /// - public bool QuantizeColors - { - get - { - return this.m_quantizeColors; - } - - internal set - { - this.m_quantizeColors = value; - } - } - - /* Description of actual output image that will be returned to application. - * These fields are computed by jpeg_start_decompress(). - * You can also use jpeg_calc_output_dimensions() to determine these values - * in advance of calling jpeg_start_decompress(). - */ - - // scaled image width - /// - /// Gets the width. - /// - public int Width - { - get - { - return this.m_width; - } - - internal set - { - this.m_width = value; - } - } - - // scaled image height - /// - /// Gets the height. - /// - public int Height - { - get - { - return this.m_height; - } - - internal set - { - this.m_height = value; - } - } - - // # of color components in out_color_space - /// - /// Gets the components per sample. - /// - public int ComponentsPerSample - { - get - { - return this.m_componentsPerSample; - } - - internal set - { - this.m_componentsPerSample = value; - } - } - - // # of color components returned. it is 1 (a colormap index) when - // quantizing colors; otherwise it equals out_color_components. - /// - /// Gets the components. - /// - public int Components - { - get - { - return this.m_components; - } - - internal set - { - this.m_components = value; - } - } - - /* When quantizing colors, the output colormap is described by these fields. - * The application can supply a colormap by setting colormap non-null before - * calling jpeg_start_decompress; otherwise a colormap is created during - * jpeg_start_decompress or jpeg_start_output. - * The map has out_color_components rows and actual_number_of_colors columns. - */ - - // number of entries in use - /// - /// Gets the actual number of colors. - /// - public int ActualNumberOfColors - { - get - { - return this.m_actualNumberOfColors; - } - - internal set - { - this.m_actualNumberOfColors = value; - } - } - - // The color map as a 2-D pixel array - /// - /// Gets the colormap. - /// - public byte[][] Colormap - { - get - { - return this.m_colormap; - } - - internal set - { - this.m_colormap = value; - } - } - - // These fields record data obtained from optional markers - // recognized by the JPEG library. - - // JFIF code for pixel size units - /// - /// Gets the density unit. - /// - public DensityUnit DensityUnit - { - get - { - return this.m_densityUnit; - } - - internal set - { - this.m_densityUnit = value; - } - } - - // Horizontal pixel density - /// - /// Gets the density x. - /// - public int DensityX - { - get - { - return this.m_densityX; - } - - internal set - { - this.m_densityX = value; - } - } - - // Vertical pixel density - /// - /// Gets the density y. - /// - public int DensityY - { - get - { - return this.m_densityY; - } - - internal set - { - this.m_densityY = value; - } - } - } -} \ No newline at end of file diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/RawImage.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/RawImage.cs deleted file mode 100644 index f18794b01..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/RawImage.cs +++ /dev/null @@ -1,80 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Text; - -namespace BitMiracle.LibJpeg -{ - class RawImage : IRawImage - { - private List m_samples; - private Colorspace m_colorspace; - - private int m_currentRow = -1; - - internal RawImage(List samples, Colorspace colorspace) - { - Debug.Assert(samples != null); - Debug.Assert(samples.Count > 0); - Debug.Assert(colorspace != Colorspace.Unknown); - - m_samples = samples; - m_colorspace = colorspace; - } - - public int Width - { - get - { - return m_samples[0].Length; - } - } - - public int Height - { - get - { - return m_samples.Count; - } - } - - public Colorspace Colorspace - { - get - { - return m_colorspace; - } - } - - public int ComponentsPerPixel - { - get - { - return m_samples[0][0].ComponentCount; - } - } - - public void BeginRead() - { - m_currentRow = 0; - } - - public byte[] GetPixelRow() - { - SampleRow row = m_samples[m_currentRow]; - List result = new List(); - for (int i = 0; i < row.Length; ++i) - { - Sample sample = row[i]; - for (int j = 0; j < sample.ComponentCount; ++j) - result.Add((byte)sample[j]); - } - ++m_currentRow; - return result.ToArray(); - } - - public void EndRead() - { - } - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Sample.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Sample.cs deleted file mode 100644 index 69b0fa79c..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Sample.cs +++ /dev/null @@ -1,106 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg -{ - using ImageProcessorCore.Formats; - - /// - /// Represents a "sample" (you can understand it as a "pixel") of image. - /// - /// It's impossible to create an instance of this class directly, - /// but you can use existing samples through collection. - /// Usual scenario is to get row of samples from the method. - /// -#if EXPOSE_LIBJPEG - public -#endif - class Sample - { - private short[] m_components; - private byte m_bitsPerComponent; - - internal Sample(BitStream bitStream, byte bitsPerComponent, byte componentCount) - { - if (bitStream == null) - throw new ArgumentNullException("bitStream"); - - if (bitsPerComponent <= 0 || bitsPerComponent > 16) - throw new ArgumentOutOfRangeException("bitsPerComponent"); - - if (componentCount <= 0 || componentCount > 5) - throw new ArgumentOutOfRangeException("componentCount"); - - m_bitsPerComponent = bitsPerComponent; - - m_components = new short[componentCount]; - for (short i = 0; i < componentCount; ++i) - m_components[i] = (short)bitStream.Read(bitsPerComponent); - } - - internal Sample(short[] components, byte bitsPerComponent) - { - if (components == null) - throw new ArgumentNullException("components"); - - if (components.Length == 0 || components.Length > 5) - throw new ArgumentException("components must be not empty and contain less than 5 elements"); - - if (bitsPerComponent <= 0 || bitsPerComponent > 16) - throw new ArgumentOutOfRangeException("bitsPerComponent"); - - m_bitsPerComponent = bitsPerComponent; - - m_components = new short[components.Length]; - Buffer.BlockCopy(components, 0, m_components, 0, components.Length * sizeof(short)); - } - - /// - /// Gets the number of bits per color component. - /// - /// The number of bits per color component. - public byte BitsPerComponent - { - get - { - return m_bitsPerComponent; - } - } - - /// - /// Gets the number of color components. - /// - /// The number of color components. - public byte ComponentCount - { - get - { - return (byte)m_components.Length; - } - } - - /// - /// Gets the color component at the specified index. - /// - /// The number of color component. - /// Value of color component. - public short this[int componentNumber] - { - get - { - return m_components[componentNumber]; - } - } - - /// - /// Gets the required color component. - /// - /// The number of color component. - /// Value of color component. - public short GetComponent(int componentNumber) - { - return m_components[componentNumber]; - } - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/SampleRow.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/SampleRow.cs deleted file mode 100644 index a2c56c901..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/SampleRow.cs +++ /dev/null @@ -1,150 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg -{ - using ImageProcessorCore.Formats; - - /// - /// Represents a row of image - collection of samples. - /// -#if EXPOSE_LIBJPEG - public -#endif - class SampleRow - { - private byte[] m_bytes; - private Sample[] m_samples; - - /// - /// Creates a row from raw samples data. - /// - /// Raw description of samples.
- /// You can pass collection with more than sampleCount samples - only sampleCount samples - /// will be parsed and all remaining bytes will be ignored. - /// The number of samples in row. - /// The number of bits per component. - /// The number of components per sample. - public SampleRow(byte[] row, int sampleCount, byte bitsPerComponent, byte componentsPerSample) - { - if (row == null) - throw new ArgumentNullException("row"); - - if (row.Length == 0) - throw new ArgumentException("row is empty"); - - if (sampleCount <= 0) - throw new ArgumentOutOfRangeException("sampleCount"); - - if (bitsPerComponent <= 0 || bitsPerComponent > 16) - throw new ArgumentOutOfRangeException("bitsPerComponent"); - - if (componentsPerSample <= 0 || componentsPerSample > 5) - throw new ArgumentOutOfRangeException("componentsPerSample"); - - m_bytes = row; - - using (BitStream bitStream = new BitStream(row)) - { - m_samples = new Sample[sampleCount]; - for (int i = 0; i < sampleCount; ++i) - m_samples[i] = new Sample(bitStream, bitsPerComponent, componentsPerSample); - } - } - - /// - /// Creates row from an array of components. - /// - /// Array of color components. - /// The number of bits per component. - /// The number of components per sample. - /// The difference between this constructor and - /// another one - - /// this constructor accept an array of prepared color components whereas - /// another constructor accept raw bytes and parse them. - /// - internal SampleRow(short[] sampleComponents, byte bitsPerComponent, byte componentsPerSample) - { - if (sampleComponents == null) - throw new ArgumentNullException("sampleComponents"); - - if (sampleComponents.Length == 0) - throw new ArgumentException("row is empty"); - - if (bitsPerComponent <= 0 || bitsPerComponent > 16) - throw new ArgumentOutOfRangeException("bitsPerComponent"); - - if (componentsPerSample <= 0 || componentsPerSample > 5) - throw new ArgumentOutOfRangeException("componentsPerSample"); - - int sampleCount = sampleComponents.Length / componentsPerSample; - m_samples = new Sample[sampleCount]; - for (int i = 0; i < sampleCount; ++i) - { - short[] components = new short[componentsPerSample]; - Buffer.BlockCopy(sampleComponents, i * componentsPerSample * sizeof(short), components, 0, componentsPerSample * sizeof(short)); - m_samples[i] = new Sample(components, bitsPerComponent); - } - - using (BitStream bits = new BitStream()) - { - for (int i = 0; i < sampleCount; ++i) - { - for (int j = 0; j < componentsPerSample; ++j) - bits.Write(sampleComponents[i * componentsPerSample + j], bitsPerComponent); - } - - m_bytes = new byte[bits.UnderlyingStream.Length]; - bits.UnderlyingStream.Seek(0, System.IO.SeekOrigin.Begin); - bits.UnderlyingStream.Read(m_bytes, 0, (int)bits.UnderlyingStream.Length); - } - } - - - /// - /// Gets the number of samples in this row. - /// - /// The number of samples. - public int Length - { - get - { - return m_samples.Length; - } - } - - - /// - /// Gets the sample at the specified index. - /// - /// The number of sample. - /// The required sample. - public Sample this[int sampleNumber] - { - get - { - return GetAt(sampleNumber); - } - } - - /// - /// Gets the sample at the specified index. - /// - /// The number of sample. - /// The required sample. - public Sample GetAt(int sampleNumber) - { - return m_samples[sampleNumber]; - } - - /// - /// Serializes this row to raw bytes. - /// - /// The row representation as array of bytes - public byte[] ToBytes() - { - return m_bytes; - } - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Utils.cs b/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Utils.cs deleted file mode 100644 index c6988a43a..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/LibJpeg/Utils.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.IO; - -namespace BitMiracle.LibJpeg -{ - class Utils - { - public static MemoryStream CopyStream(Stream stream) - { - if (stream == null) - throw new ArgumentNullException("stream"); - - long positionBefore = stream.Position; - stream.Seek(0, SeekOrigin.Begin); - - MemoryStream result = new MemoryStream((int)stream.Length); - - byte[] block = new byte[2048]; - for ( ; ; ) - { - int bytesRead = stream.Read(block, 0, 2048); - result.Write(block, 0, bytesRead); - if (bytesRead < 2048) - break; - } - - stream.Seek(positionBefore, SeekOrigin.Begin); - return result; - } - - public static void CMYK2RGB(byte c, byte m, byte y, byte k, out byte red, out byte green, out byte blue) - { - float C, M, Y, K; - C = c / 255.0f; - M = m / 255.0f; - Y = y / 255.0f; - K = k / 255.0f; - - float R, G, B; - R = C * (1.0f - K) + K; - G = M * (1.0f - K) + K; - B = Y * (1.0f - K) + K; - - R = (1.0f - R) * 255.0f + 0.5f; - G = (1.0f - G) * 255.0f + 0.5f; - B = (1.0f - B) * 255.0f + 0.5f; - - red = (byte)(R * 255); - green = (byte)(G * 255); - blue = (byte)(B * 255); - - //C = (double)c; - //M = (double)m; - //Y = (double)y; - //K = (double)k; - - //C = C / 255.0; - //M = M / 255.0; - //Y = Y / 255.0; - //K = K / 255.0; - - //R = C * (1.0 - K) + K; - //G = M * (1.0 - K) + K; - //B = Y * (1.0 - K) + K; - - //R = (1.0 - R) * 255.0 + 0.5; - //G = (1.0 - G) * 255.0 + 0.5; - //B = (1.0 - B) * 255.0 + 0.5; - - //r = (byte)R; - //g = (byte)G; - //b = (byte)B; - - //rgb = RGB(r, g, b); - - //return rgb; - } - } -} diff --git a/src/ImageProcessorCore/Formats/Jpg/README.md b/src/ImageProcessorCore/Formats/Jpg/README.md deleted file mode 100644 index 7416304dc..000000000 --- a/src/ImageProcessorCore/Formats/Jpg/README.md +++ /dev/null @@ -1,5 +0,0 @@ -Encoder/Decoder adapted from: - -https://github.com/BitMiracle/libjpeg.net/ -https://github.com/yufeih/Nine.Imaging/ -https://imagetools.codeplex.com/ diff --git a/src/ImageProcessorCore/Formats/Jpg/jpeg2/fdct.goa b/src/ImageProcessorCore/Formats/Jpg/jpeg2/fdct.goa new file mode 100644 index 000000000..a92204cf9 --- /dev/null +++ b/src/ImageProcessorCore/Formats/Jpg/jpeg2/fdct.goa @@ -0,0 +1,190 @@ +// 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 new file mode 100644 index 000000000..e3ebf589e --- /dev/null +++ b/src/ImageProcessorCore/Formats/Jpg/jpeg2/reader.go @@ -0,0 +1,1665 @@ +// 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/writer.goa b/src/ImageProcessorCore/Formats/Jpg/jpeg2/writer.goa new file mode 100644 index 000000000..03c7181df --- /dev/null +++ b/src/ImageProcessorCore/Formats/Jpg/jpeg2/writer.goa @@ -0,0 +1,614 @@ +// 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 new file mode 100644 index 000000000..a68322aa2 --- /dev/null +++ b/src/ImageProcessorCore/Formats/Jpg/main.go @@ -0,0 +1,12 @@ +package main +import "fmt" +import "os" +import "./jpeg2" + +func main() { + f, err := os.Open("/Users/mweber/dev/fprint/print.jpg"); + if err == nil { + jpeg2.Decode(f); + fmt.Println("done") + } +}