mirror of https://github.com/SixLabors/ImageSharp
Browse Source
Former-commit-id: 20a1e3d68fbece9f35a426af61fe500fdb82e15b Former-commit-id: 82d67e54001ff822292c1ef939c8ef1a0cd96d4b Former-commit-id: c73e1ed636cb13bd6ff97e440bfc8bdb0af3f945af/merge-core
10 changed files with 647 additions and 2510 deletions
@ -0,0 +1,623 @@ |
|||
namespace ImageProcessorCore.Formats.Jpg |
|||
{ |
|||
using System; |
|||
using System.IO; |
|||
|
|||
public partial class Encoder |
|||
{ |
|||
private const int sof0Marker = 0xc0; // Start Of Frame (Baseline).
|
|||
private const int sof1Marker = 0xc1; // Start Of Frame (Extended Sequential).
|
|||
private const int sof2Marker = 0xc2; // Start Of Frame (Progressive).
|
|||
private const int dhtMarker = 0xc4; // Define Huffman Table.
|
|||
private const int rst0Marker = 0xd0; // ReSTart (0).
|
|||
private const int rst7Marker = 0xd7; // ReSTart (7).
|
|||
private const int soiMarker = 0xd8; // Start Of Image.
|
|||
private const int eoiMarker = 0xd9; // End Of Image.
|
|||
private const int sosMarker = 0xda; // Start Of Scan.
|
|||
private const int dqtMarker = 0xdb; // Define Quantization Table.
|
|||
private const int driMarker = 0xdd; // Define Restart Interval.
|
|||
private const int comMarker = 0xfe; // COMment.
|
|||
// "APPlication specific" markers aren't part of the JPEG spec per se,
|
|||
// but in practice, their use is described at
|
|||
// http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html
|
|||
private const int app0Marker = 0xe0; |
|||
private const int app14Marker = 0xee; |
|||
private const int app15Marker = 0xef; |
|||
|
|||
// bitCount counts the number of bits needed to hold an integer.
|
|||
private readonly byte[] bitCount = { |
|||
0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, |
|||
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, |
|||
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, |
|||
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, |
|||
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, |
|||
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, |
|||
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, |
|||
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, |
|||
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, |
|||
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, |
|||
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, |
|||
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, |
|||
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, |
|||
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, |
|||
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, |
|||
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, }; |
|||
|
|||
// unzig maps from the zig-zag ordering to the natural ordering. For example,
|
|||
// unzig[3] is the column and row of the fourth element in zig-zag order. The
|
|||
// value is 16, which means first column (16%8 == 0) and third row (16/8 == 2).
|
|||
private static readonly int[] unzig = new int[] { |
|||
0, 1, 8, 16, 9, 2, 3, 10, |
|||
17, 24, 32, 25, 18, 11, 4, 5, |
|||
12, 19, 26, 33, 40, 48, 41, 34, |
|||
27, 20, 13, 6, 7, 14, 21, 28, |
|||
35, 42, 49, 56, 57, 50, 43, 36, |
|||
29, 22, 15, 23, 30, 37, 44, 51, |
|||
58, 59, 52, 45, 38, 31, 39, 46, |
|||
53, 60, 61, 54, 47, 55, 62, 63, |
|||
}; |
|||
|
|||
private const int nQuantIndex = 2; |
|||
private const int nHuffIndex = 4; |
|||
|
|||
private enum quantIndex |
|||
{ |
|||
quantIndexLuminance = 0, |
|||
quantIndexChrominance = 1, |
|||
} |
|||
|
|||
private enum huffIndex |
|||
{ |
|||
huffIndexLuminanceDC = 0, |
|||
huffIndexLuminanceAC = 1, |
|||
huffIndexChrominanceDC = 2, |
|||
huffIndexChrominanceAC = 3, |
|||
} |
|||
|
|||
// unscaledQuant are the unscaled quantization tables in zig-zag order. Each
|
|||
// encoder copies and scales the tables according to its quality parameter.
|
|||
// The values are derived from section K.1 after converting from natural to
|
|||
// zig-zag order.
|
|||
private byte[,] unscaledQuant = new byte[,] { |
|||
// Luminance.
|
|||
{ |
|||
16, 11, 12, 14, 12, 10, 16, 14, |
|||
13, 14, 18, 17, 16, 19, 24, 40, |
|||
26, 24, 22, 22, 24, 49, 35, 37, |
|||
29, 40, 58, 51, 61, 60, 57, 51, |
|||
56, 55, 64, 72, 92, 78, 64, 68, |
|||
87, 69, 55, 56, 80, 109, 81, 87, |
|||
95, 98, 103, 104, 103, 62, 77, 113, |
|||
121, 112, 100, 120, 92, 101, 103, 99, |
|||
}, |
|||
// Chrominance.
|
|||
{ |
|||
17, 18, 18, 24, 21, 24, 47, 26, |
|||
26, 47, 99, 66, 56, 66, 99, 99, |
|||
99, 99, 99, 99, 99, 99, 99, 99, |
|||
99, 99, 99, 99, 99, 99, 99, 99, |
|||
99, 99, 99, 99, 99, 99, 99, 99, |
|||
99, 99, 99, 99, 99, 99, 99, 99, |
|||
99, 99, 99, 99, 99, 99, 99, 99, |
|||
99, 99, 99, 99, 99, 99, 99, 99, |
|||
}, |
|||
}; |
|||
|
|||
private class huffmanSpec |
|||
{ |
|||
public huffmanSpec(byte[] c, byte[] v) { count = c; values = v; } |
|||
public byte[] count; |
|||
public byte[] values; |
|||
}; |
|||
|
|||
// theHuffmanSpec is the Huffman encoding specifications.
|
|||
// This encoder uses the same Huffman encoding for all images.
|
|||
private huffmanSpec[] theHuffmanSpec = new huffmanSpec[] { |
|||
// Luminance DC.
|
|||
new huffmanSpec( |
|||
new byte[] {0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}, |
|||
new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11} |
|||
), |
|||
new huffmanSpec( |
|||
new byte[] {0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 125}, |
|||
new byte[] { |
|||
0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, |
|||
0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, |
|||
0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, |
|||
0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, |
|||
0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, |
|||
0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, |
|||
0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, |
|||
0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, |
|||
0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, |
|||
0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, |
|||
0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, |
|||
0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, |
|||
0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, |
|||
0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, |
|||
0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, |
|||
0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, |
|||
0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, |
|||
0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, |
|||
0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, |
|||
0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, |
|||
0xf9, 0xfa, |
|||
} |
|||
), |
|||
new huffmanSpec( |
|||
new byte[] {0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}, |
|||
new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11} |
|||
), |
|||
// Chrominance AC.
|
|||
new huffmanSpec( |
|||
new byte[] {0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 119}, |
|||
new byte[] { |
|||
0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, |
|||
0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, |
|||
0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, |
|||
0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, |
|||
0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, |
|||
0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, |
|||
0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, |
|||
0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, |
|||
0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, |
|||
0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, |
|||
0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, |
|||
0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, |
|||
0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, |
|||
0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, |
|||
0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, |
|||
0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, |
|||
0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, |
|||
0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, |
|||
0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, |
|||
0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, |
|||
0xf9, 0xfa, |
|||
} |
|||
), |
|||
}; |
|||
|
|||
// huffmanLUT is a compiled look-up table representation of a huffmanSpec.
|
|||
// Each value maps to a uint32 of which the 8 most significant bits hold the
|
|||
// codeword size in bits and the 24 least significant bits hold the codeword.
|
|||
// The maximum codeword size is 16 bits.
|
|||
private class huffmanLUT |
|||
{ |
|||
public uint[] values; |
|||
|
|||
public huffmanLUT(huffmanSpec s) |
|||
{ |
|||
int maxValue = 0; |
|||
|
|||
foreach(var v in s.values) |
|||
{ |
|||
if(v > maxValue) |
|||
maxValue = v; |
|||
} |
|||
|
|||
values = new uint[maxValue+1]; |
|||
|
|||
int code = 0; |
|||
int k = 0; |
|||
|
|||
for(int i = 0; i < s.count.Length; i++) |
|||
{ |
|||
int nBits = (i+1) << 24; |
|||
for(int j = 0; j < s.count[i]; j++) |
|||
{ |
|||
values[s.values[k]] = (uint)(nBits | code); |
|||
code++; |
|||
k++; |
|||
} |
|||
code <<= 1; |
|||
} |
|||
} |
|||
} |
|||
|
|||
// w is the writer to write to. err is the first error encountered during
|
|||
// writing. All attempted writes after the first error become no-ops.
|
|||
private Stream w; |
|||
// buf is a scratch buffer.
|
|||
private byte[] buf = new byte[16]; |
|||
// bits and nBits are accumulated bits to write to w.
|
|||
private uint bits, nBits; |
|||
// quant is the scaled quantization tables, in zig-zag order.
|
|||
private byte[][] quant = new byte[nQuantIndex][];//[Block.blockSize];
|
|||
// theHuffmanLUT are compiled representations of theHuffmanSpec.
|
|||
private huffmanLUT[] theHuffmanLUT = new huffmanLUT[4]; |
|||
|
|||
private void writeByte(byte b) |
|||
{ |
|||
var data = new byte[1]; |
|||
data[0] = b; |
|||
w.Write(data, 0, 1); |
|||
} |
|||
|
|||
// emit emits the least significant nBits bits of bits to the bit-stream.
|
|||
// The precondition is bits < 1<<nBits && nBits <= 16.
|
|||
private void emit(uint bits, uint nBits) |
|||
{ |
|||
nBits += this.nBits; |
|||
bits <<= (int)(32 - nBits); |
|||
bits |= this.bits; |
|||
while(nBits >= 8) |
|||
{ |
|||
byte b = (byte)(bits >> 24); |
|||
writeByte(b); |
|||
if(b == 0xff) |
|||
writeByte(0x00); |
|||
bits <<= 8; |
|||
nBits -= 8; |
|||
} |
|||
this.bits = bits; |
|||
this.nBits = nBits; |
|||
} |
|||
|
|||
// emitHuff emits the given value with the given Huffman encoder.
|
|||
private void emitHuff(huffIndex h, int v) |
|||
{ |
|||
uint x = theHuffmanLUT[(int)h].values[v]; |
|||
emit(x&((1<<24)-1), x>>24); |
|||
} |
|||
|
|||
// emitHuffRLE emits a run of runLength copies of value encoded with the given
|
|||
// Huffman encoder.
|
|||
private void emitHuffRLE(huffIndex h, int runLength, int v) |
|||
{ |
|||
int a = v; |
|||
int b = v; |
|||
if(a < 0) |
|||
{ |
|||
a = -v; |
|||
b = v-1; |
|||
} |
|||
uint nBits = 0; |
|||
if(a < 0x100) |
|||
nBits = bitCount[a]; |
|||
else |
|||
nBits = 8 + (uint)bitCount[a>>8]; |
|||
|
|||
emitHuff(h, (int)((runLength<<4)|nBits)); |
|||
if(nBits > 0) |
|||
emit((uint)b & (uint)((1 << ((int)nBits)) - 1), nBits); |
|||
} |
|||
|
|||
// writeMarkerHeader writes the header for a marker with the given length.
|
|||
private void writeMarkerHeader(byte marker, int markerlen) |
|||
{ |
|||
buf[0] = 0xff; |
|||
buf[1] = marker; |
|||
buf[2] = (byte)(markerlen >> 8); |
|||
buf[3] = (byte)(markerlen & 0xff); |
|||
w.Write(buf, 0, 4); |
|||
} |
|||
|
|||
// writeDQT writes the Define Quantization Table marker.
|
|||
private void writeDQT() |
|||
{ |
|||
int markerlen = 2 + nQuantIndex*(1+Block.blockSize); |
|||
writeMarkerHeader(dqtMarker, markerlen); |
|||
for(int i = 0; i < nQuantIndex; i++) |
|||
{ |
|||
writeByte((byte)i); |
|||
w.Write(quant[i], 0, quant[i].Length); |
|||
} |
|||
} |
|||
|
|||
// writeSOF0 writes the Start Of Frame (Baseline) marker.
|
|||
private void writeSOF0(int wid, int hei, int nComponent) |
|||
{ |
|||
byte[] chroma1 = new byte[] { 0x22, 0x11, 0x11 }; |
|||
byte[] chroma2 = new byte[] { 0x00, 0x01, 0x01 }; |
|||
|
|||
int markerlen = 8 + 3*nComponent; |
|||
writeMarkerHeader(sof0Marker, markerlen); |
|||
buf[0] = 8; // 8-bit color.
|
|||
buf[1] = (byte)(hei >> 8); |
|||
buf[2] = (byte)(hei & 0xff); |
|||
buf[3] = (byte)(wid >> 8); |
|||
buf[4] = (byte)(wid & 0xff); |
|||
buf[5] = (byte)(nComponent); |
|||
if(nComponent == 1) |
|||
{ |
|||
buf[6] = 1; |
|||
// No subsampling for grayscale image.
|
|||
buf[7] = 0x11; |
|||
buf[8] = 0x00; |
|||
} |
|||
else |
|||
{ |
|||
for(int i = 0; i < nComponent; i++) |
|||
{ |
|||
buf[3*i+6] = (byte)(i + 1); |
|||
// We use 4:2:0 chroma subsampling.
|
|||
buf[3*i+7] = chroma1[i]; |
|||
buf[3*i+8] = chroma2[i]; |
|||
} |
|||
} |
|||
w.Write(buf, 0, 3*(nComponent-1)+9); |
|||
} |
|||
|
|||
// writeDHT writes the Define Huffman Table marker.
|
|||
private void writeDHT(int nComponent) |
|||
{ |
|||
byte[] headers = new byte[] { 0x00, 0x10, 0x01, 0x11 }; |
|||
int markerlen = 2; |
|||
huffmanSpec[] specs = theHuffmanSpec; |
|||
|
|||
if(nComponent == 1) |
|||
{ |
|||
// Drop the Chrominance tables.
|
|||
specs = new huffmanSpec[] { theHuffmanSpec[0], theHuffmanSpec[1] }; |
|||
} |
|||
|
|||
foreach(var s in specs) |
|||
markerlen += 1 + 16 + s.values.Length; |
|||
|
|||
writeMarkerHeader(dhtMarker, markerlen); |
|||
for(int i = 0; i < specs.Length; i++) |
|||
{ |
|||
var s = specs[i]; |
|||
|
|||
writeByte(headers[i]); |
|||
w.Write(s.count, 0, s.count.Length); |
|||
w.Write(s.values, 0, s.values.Length); |
|||
} |
|||
} |
|||
|
|||
// writeBlock writes a block of pixel data using the given quantization table,
|
|||
// returning the post-quantized DC value of the DCT-transformed block. b is in
|
|||
// natural (not zig-zag) order.
|
|||
private int writeBlock(Block b, quantIndex q, int prevDC) |
|||
{ |
|||
FDCT(b); |
|||
|
|||
// Emit the DC delta.
|
|||
int dc = div(b[0], 8*quant[(int)q][0]); |
|||
emitHuffRLE((huffIndex)(2*(int)q+0), 0, dc-prevDC); |
|||
|
|||
// Emit the AC components.
|
|||
var h = (huffIndex)(2*(int)q+1); |
|||
int runLength = 0; |
|||
|
|||
for(int zig = 1; zig < Block.blockSize; zig++) |
|||
{ |
|||
int ac = div(b[unzig[zig]], 8*quant[(int)q][zig]); |
|||
|
|||
if(ac == 0) |
|||
{ |
|||
runLength++; |
|||
} |
|||
else |
|||
{ |
|||
while(runLength > 15) |
|||
{ |
|||
emitHuff(h, 0xf0); |
|||
runLength -= 16; |
|||
} |
|||
|
|||
emitHuffRLE(h, runLength, ac); |
|||
runLength = 0; |
|||
} |
|||
} |
|||
if(runLength > 0) |
|||
emitHuff(h, 0x00); |
|||
return dc; |
|||
} |
|||
|
|||
// toYCbCr converts the 8x8 region of m whose top-left corner is p to its
|
|||
// YCbCr values.
|
|||
private void toYCbCr(ImageBase m, int x, int y, Block yBlock, Block cbBlock, Block crBlock) |
|||
{ |
|||
int xmax = m.Width - 1; |
|||
int ymax = m.Height - 1; |
|||
for(int j = 0; j < 8; j++) |
|||
{ |
|||
for(int i = 0; i < 8; i++) |
|||
{ |
|||
byte yy, cb, cr; |
|||
|
|||
var c = m[Math.Min(x+i, xmax), Math.Min(y+j, ymax)]; |
|||
Colors.RGBToYCbCr((byte)(c.R*255), (byte)(c.G*255), (byte)(c.B*255), out yy, out cb, out cr); |
|||
yBlock[8*j+i] = yy; |
|||
cbBlock[8*j+i] = cb; |
|||
crBlock[8*j+i] = cr; |
|||
} |
|||
} |
|||
} |
|||
|
|||
// grayToY stores the 8x8 region of m whose top-left corner is p in yBlock.
|
|||
/*func grayToY(m *image.Gray, p image.Point, yBlock *block) { |
|||
b := m.Bounds() |
|||
xmax := b.Max.X - 1 |
|||
ymax := b.Max.Y - 1 |
|||
pix := m.Pix |
|||
for j := 0; j < 8; j++ { |
|||
for i := 0; i < 8; i++ { |
|||
idx := m.PixOffset(min(p.X+i, xmax), min(p.Y+j, ymax)) |
|||
yBlock[8*j+i] = int32(pix[idx]) |
|||
} |
|||
} |
|||
} |
|||
|
|||
// rgbaToYCbCr is a specialized version of toYCbCr for image.RGBA images.
|
|||
func rgbaToYCbCr(m *image.RGBA, p image.Point, yBlock, cbBlock, crBlock *block) { |
|||
b := m.Bounds() |
|||
xmax := b.Max.X - 1 |
|||
ymax := b.Max.Y - 1 |
|||
for j := 0; j < 8; j++ { |
|||
sj := p.Y + j |
|||
if sj > ymax { |
|||
sj = ymax |
|||
} |
|||
offset := (sj-b.Min.Y)*m.Stride - b.Min.X*4 |
|||
for i := 0; i < 8; i++ { |
|||
sx := p.X + i |
|||
if sx > xmax { |
|||
sx = xmax |
|||
} |
|||
pix := m.Pix[offset+sx*4:] |
|||
yy, cb, cr := color.RGBToYCbCr(pix[0], pix[1], pix[2]) |
|||
yBlock[8*j+i] = int32(yy) |
|||
cbBlock[8*j+i] = int32(cb) |
|||
crBlock[8*j+i] = int32(cr) |
|||
} |
|||
} |
|||
}*/ |
|||
|
|||
// scale scales the 16x16 region represented by the 4 src blocks to the 8x8
|
|||
// dst block.
|
|||
private void scale(Block dst, Block[] src) |
|||
{ |
|||
for(int i = 0; i < 4; i++) |
|||
{ |
|||
int dstOff = ((i&2)<<4) | ((i&1)<<2); |
|||
for(int y = 0; y < 4; y++) |
|||
{ |
|||
for(int x = 0; x < 4; x++) |
|||
{ |
|||
int j = 16*y + 2*x; |
|||
int sum = src[i][j] + src[i][j+1] + src[i][j+8] + src[i][j+9]; |
|||
dst[8*y+x+dstOff] = (sum + 2) >> 2; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
// sosHeaderY is the SOS marker "\xff\xda" followed by 8 bytes:
|
|||
// - the marker length "\x00\x08",
|
|||
// - the number of components "\x01",
|
|||
// - component 1 uses DC table 0 and AC table 0 "\x01\x00",
|
|||
// - the bytes "\x00\x3f\x00". Section B.2.3 of the spec says that for
|
|||
// sequential DCTs, those bytes (8-bit Ss, 8-bit Se, 4-bit Ah, 4-bit Al)
|
|||
// should be 0x00, 0x3f, 0x00<<4 | 0x00.
|
|||
private readonly byte[] sosHeaderY = new byte[] { |
|||
0xff, 0xda, 0x00, 0x08, 0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, |
|||
}; |
|||
|
|||
// sosHeaderYCbCr is the SOS marker "\xff\xda" followed by 12 bytes:
|
|||
// - the marker length "\x00\x0c",
|
|||
// - the number of components "\x03",
|
|||
// - component 1 uses DC table 0 and AC table 0 "\x01\x00",
|
|||
// - component 2 uses DC table 1 and AC table 1 "\x02\x11",
|
|||
// - component 3 uses DC table 1 and AC table 1 "\x03\x11",
|
|||
// - the bytes "\x00\x3f\x00". Section B.2.3 of the spec says that for
|
|||
// sequential DCTs, those bytes (8-bit Ss, 8-bit Se, 4-bit Ah, 4-bit Al)
|
|||
// should be 0x00, 0x3f, 0x00<<4 | 0x00.
|
|||
private readonly byte[] sosHeaderYCbCr = new byte[] { |
|||
0xff, 0xda, 0x00, 0x0c, 0x03, 0x01, 0x00, 0x02, |
|||
0x11, 0x03, 0x11, 0x00, 0x3f, 0x00, |
|||
}; |
|||
|
|||
// writeSOS writes the StartOfScan marker.
|
|||
private void writeSOS(ImageBase m) |
|||
{ |
|||
w.Write(sosHeaderYCbCr, 0, sosHeaderYCbCr.Length); |
|||
|
|||
Block b = new Block(); |
|||
Block[] cb = new Block[4]; |
|||
Block[] cr = new Block[4]; |
|||
int prevDCY = 0, prevDCCb = 0, prevDCCr = 0; |
|||
|
|||
for(int i = 0; i < 4; i++) cb[i] = new Block(); |
|||
for(int i = 0; i < 4; i++) cr[i] = new Block(); |
|||
|
|||
for(int y = 0; y < m.Height; y += 16) |
|||
{ |
|||
for(int x = 0; x < m.Width; x += 16) |
|||
{ |
|||
for(int i = 0; i < 4; i++) |
|||
{ |
|||
int xOff = (i & 1) * 8; |
|||
int yOff = (i & 2) * 4; |
|||
|
|||
toYCbCr(m, x+xOff, y+yOff, b, cb[i], cr[i]); |
|||
prevDCY = writeBlock(b, 0, prevDCY); |
|||
} |
|||
scale(b, cb); |
|||
prevDCCb = writeBlock(b, (quantIndex)1, prevDCCb); |
|||
scale(b, cr); |
|||
prevDCCr = writeBlock(b, (quantIndex)1, prevDCCr); |
|||
} |
|||
} |
|||
|
|||
// Pad the last byte with 1's.
|
|||
emit(0x7f, 7); |
|||
} |
|||
|
|||
// Encode writes the Image m to w in JPEG 4:2:0 baseline format with the given
|
|||
// options. Default parameters are used if a nil *Options is passed.
|
|||
public void Encode(Stream w, ImageBase m, int quality) |
|||
{ |
|||
this.w = w; |
|||
|
|||
for(int i = 0; i < theHuffmanSpec.Length; i++) |
|||
theHuffmanLUT[i] = new huffmanLUT(theHuffmanSpec[i]); |
|||
|
|||
for(int i = 0; i < nQuantIndex; i++) |
|||
quant[i] = new byte[Block.blockSize]; |
|||
|
|||
if(m.Width >= (1<<16) || m.Height >= (1<<16)) |
|||
throw new Exception("jpeg: image is too large to encode"); |
|||
|
|||
if(quality < 1) quality = 1; |
|||
if(quality > 100) quality = 100; |
|||
|
|||
// Convert from a quality rating to a scaling factor.
|
|||
int scale; |
|||
if(quality < 50) |
|||
scale = 5000 / quality; |
|||
else |
|||
scale = 200 - quality*2; |
|||
|
|||
// Initialize the quantization tables.
|
|||
|
|||
for(int i = 0; i < nQuantIndex; i++) |
|||
{ |
|||
for(int j = 0; j < Block.blockSize; j++) |
|||
{ |
|||
int x = unscaledQuant[i,j]; |
|||
x = (x*scale + 50) / 100; |
|||
if(x < 1) x = 1; |
|||
if(x > 255) x = 255; |
|||
quant[i][j] = (byte)x; |
|||
} |
|||
} |
|||
|
|||
// Compute number of components based on input image type.
|
|||
int nComponent = 3; |
|||
|
|||
// Write the Start Of Image marker.
|
|||
buf[0] = 0xff; |
|||
buf[1] = 0xd8; |
|||
w.Write(buf, 0, 2); |
|||
|
|||
// Write the quantization tables.
|
|||
writeDQT(); |
|||
|
|||
// Write the image dimensions.
|
|||
writeSOF0(m.Width, m.Height, nComponent); |
|||
|
|||
// Write the Huffman tables.
|
|||
writeDHT(nComponent); |
|||
|
|||
// Write the image data.
|
|||
writeSOS(m); |
|||
|
|||
// Write the End Of Image marker.
|
|||
buf[0] = 0xff; |
|||
buf[1] = 0xd9; |
|||
w.Write(buf, 0, 2); |
|||
w.Flush(); |
|||
} |
|||
|
|||
// div returns a/b rounded to the nearest integer, instead of rounded to zero.
|
|||
private static int div(int a, int b) |
|||
{ |
|||
if(a >= 0) |
|||
return (a + (b >> 1)) / b; |
|||
else |
|||
return -((-a + (b >> 1)) / b); |
|||
} |
|||
} |
|||
} |
|||
@ -1,190 +0,0 @@ |
|||
// Copyright 2011 The Go Authors. All rights reserved. |
|||
// Use of this source code is governed by a BSD-style |
|||
// license that can be found in the LICENSE file. |
|||
|
|||
package golang |
|||
|
|||
// This file implements a Forward Discrete Cosine Transformation. |
|||
|
|||
/* |
|||
It is based on the code in jfdctint.c from the Independent JPEG Group, |
|||
found at http://www.ijg.org/files/jpegsrc.v8c.tar.gz. |
|||
|
|||
The "LEGAL ISSUES" section of the README in that archive says: |
|||
|
|||
In plain English: |
|||
|
|||
1. We don't promise that this software works. (But if you find any bugs, |
|||
please let us know!) |
|||
2. You can use this software for whatever you want. You don't have to pay us. |
|||
3. You may not pretend that you wrote this software. If you use it in a |
|||
program, you must acknowledge somewhere in your documentation that |
|||
you've used the IJG code. |
|||
|
|||
In legalese: |
|||
|
|||
The authors make NO WARRANTY or representation, either express or implied, |
|||
with respect to this software, its quality, accuracy, merchantability, or |
|||
fitness for a particular purpose. This software is provided "AS IS", and you, |
|||
its user, assume the entire risk as to its quality and accuracy. |
|||
|
|||
This software is copyright (C) 1991-2011, Thomas G. Lane, Guido Vollbeding. |
|||
All Rights Reserved except as specified below. |
|||
|
|||
Permission is hereby granted to use, copy, modify, and distribute this |
|||
software (or portions thereof) for any purpose, without fee, subject to these |
|||
conditions: |
|||
(1) If any part of the source code for this software is distributed, then this |
|||
README file must be included, with this copyright and no-warranty notice |
|||
unaltered; and any additions, deletions, or changes to the original files |
|||
must be clearly indicated in accompanying documentation. |
|||
(2) If only executable code is distributed, then the accompanying |
|||
documentation must state that "this software is based in part on the work of |
|||
the Independent JPEG Group". |
|||
(3) Permission for use of this software is granted only if the user accepts |
|||
full responsibility for any undesirable consequences; the authors accept |
|||
NO LIABILITY for damages of any kind. |
|||
|
|||
These conditions apply to any software derived from or based on the IJG code, |
|||
not just to the unmodified library. If you use our work, you ought to |
|||
acknowledge us. |
|||
|
|||
Permission is NOT granted for the use of any IJG author's name or company name |
|||
in advertising or publicity relating to this software or products derived from |
|||
it. This software may be referred to only as "the Independent JPEG Group's |
|||
software". |
|||
|
|||
We specifically permit and encourage the use of this software as the basis of |
|||
commercial products, provided that all warranty or liability claims are |
|||
assumed by the product vendor. |
|||
*/ |
|||
|
|||
// Trigonometric constants in 13-bit fixed point format. |
|||
const ( |
|||
fix_0_298631336 = 2446 |
|||
fix_0_390180644 = 3196 |
|||
fix_0_541196100 = 4433 |
|||
fix_0_765366865 = 6270 |
|||
fix_0_899976223 = 7373 |
|||
fix_1_175875602 = 9633 |
|||
fix_1_501321110 = 12299 |
|||
fix_1_847759065 = 15137 |
|||
fix_1_961570560 = 16069 |
|||
fix_2_053119869 = 16819 |
|||
fix_2_562915447 = 20995 |
|||
fix_3_072711026 = 25172 |
|||
) |
|||
|
|||
const ( |
|||
constBits = 13 |
|||
pass1Bits = 2 |
|||
centerJSample = 128 |
|||
) |
|||
|
|||
// fdct performs a forward DCT on an 8x8 block of coefficients, including a |
|||
// level shift. |
|||
func fdct(b *block) { |
|||
// Pass 1: process rows. |
|||
for y := 0; y < 8; y++ { |
|||
x0 := b[y*8+0] |
|||
x1 := b[y*8+1] |
|||
x2 := b[y*8+2] |
|||
x3 := b[y*8+3] |
|||
x4 := b[y*8+4] |
|||
x5 := b[y*8+5] |
|||
x6 := b[y*8+6] |
|||
x7 := b[y*8+7] |
|||
|
|||
tmp0 := x0 + x7 |
|||
tmp1 := x1 + x6 |
|||
tmp2 := x2 + x5 |
|||
tmp3 := x3 + x4 |
|||
|
|||
tmp10 := tmp0 + tmp3 |
|||
tmp12 := tmp0 - tmp3 |
|||
tmp11 := tmp1 + tmp2 |
|||
tmp13 := tmp1 - tmp2 |
|||
|
|||
tmp0 = x0 - x7 |
|||
tmp1 = x1 - x6 |
|||
tmp2 = x2 - x5 |
|||
tmp3 = x3 - x4 |
|||
|
|||
b[y*8+0] = (tmp10 + tmp11 - 8*centerJSample) << pass1Bits |
|||
b[y*8+4] = (tmp10 - tmp11) << pass1Bits |
|||
z1 := (tmp12 + tmp13) * fix_0_541196100 |
|||
z1 += 1 << (constBits - pass1Bits - 1) |
|||
b[y*8+2] = (z1 + tmp12*fix_0_765366865) >> (constBits - pass1Bits) |
|||
b[y*8+6] = (z1 - tmp13*fix_1_847759065) >> (constBits - pass1Bits) |
|||
|
|||
tmp10 = tmp0 + tmp3 |
|||
tmp11 = tmp1 + tmp2 |
|||
tmp12 = tmp0 + tmp2 |
|||
tmp13 = tmp1 + tmp3 |
|||
z1 = (tmp12 + tmp13) * fix_1_175875602 |
|||
z1 += 1 << (constBits - pass1Bits - 1) |
|||
tmp0 = tmp0 * fix_1_501321110 |
|||
tmp1 = tmp1 * fix_3_072711026 |
|||
tmp2 = tmp2 * fix_2_053119869 |
|||
tmp3 = tmp3 * fix_0_298631336 |
|||
tmp10 = tmp10 * -fix_0_899976223 |
|||
tmp11 = tmp11 * -fix_2_562915447 |
|||
tmp12 = tmp12 * -fix_0_390180644 |
|||
tmp13 = tmp13 * -fix_1_961570560 |
|||
|
|||
tmp12 += z1 |
|||
tmp13 += z1 |
|||
b[y*8+1] = (tmp0 + tmp10 + tmp12) >> (constBits - pass1Bits) |
|||
b[y*8+3] = (tmp1 + tmp11 + tmp13) >> (constBits - pass1Bits) |
|||
b[y*8+5] = (tmp2 + tmp11 + tmp12) >> (constBits - pass1Bits) |
|||
b[y*8+7] = (tmp3 + tmp10 + tmp13) >> (constBits - pass1Bits) |
|||
} |
|||
// Pass 2: process columns. |
|||
// We remove pass1Bits scaling, but leave results scaled up by an overall factor of 8. |
|||
for x := 0; x < 8; x++ { |
|||
tmp0 := b[0*8+x] + b[7*8+x] |
|||
tmp1 := b[1*8+x] + b[6*8+x] |
|||
tmp2 := b[2*8+x] + b[5*8+x] |
|||
tmp3 := b[3*8+x] + b[4*8+x] |
|||
|
|||
tmp10 := tmp0 + tmp3 + 1<<(pass1Bits-1) |
|||
tmp12 := tmp0 - tmp3 |
|||
tmp11 := tmp1 + tmp2 |
|||
tmp13 := tmp1 - tmp2 |
|||
|
|||
tmp0 = b[0*8+x] - b[7*8+x] |
|||
tmp1 = b[1*8+x] - b[6*8+x] |
|||
tmp2 = b[2*8+x] - b[5*8+x] |
|||
tmp3 = b[3*8+x] - b[4*8+x] |
|||
|
|||
b[0*8+x] = (tmp10 + tmp11) >> pass1Bits |
|||
b[4*8+x] = (tmp10 - tmp11) >> pass1Bits |
|||
|
|||
z1 := (tmp12 + tmp13) * fix_0_541196100 |
|||
z1 += 1 << (constBits + pass1Bits - 1) |
|||
b[2*8+x] = (z1 + tmp12*fix_0_765366865) >> (constBits + pass1Bits) |
|||
b[6*8+x] = (z1 - tmp13*fix_1_847759065) >> (constBits + pass1Bits) |
|||
|
|||
tmp10 = tmp0 + tmp3 |
|||
tmp11 = tmp1 + tmp2 |
|||
tmp12 = tmp0 + tmp2 |
|||
tmp13 = tmp1 + tmp3 |
|||
z1 = (tmp12 + tmp13) * fix_1_175875602 |
|||
z1 += 1 << (constBits + pass1Bits - 1) |
|||
tmp0 = tmp0 * fix_1_501321110 |
|||
tmp1 = tmp1 * fix_3_072711026 |
|||
tmp2 = tmp2 * fix_2_053119869 |
|||
tmp3 = tmp3 * fix_0_298631336 |
|||
tmp10 = tmp10 * -fix_0_899976223 |
|||
tmp11 = tmp11 * -fix_2_562915447 |
|||
tmp12 = tmp12 * -fix_0_390180644 |
|||
tmp13 = tmp13 * -fix_1_961570560 |
|||
|
|||
tmp12 += z1 |
|||
tmp13 += z1 |
|||
b[1*8+x] = (tmp0 + tmp10 + tmp12) >> (constBits + pass1Bits) |
|||
b[3*8+x] = (tmp1 + tmp11 + tmp13) >> (constBits + pass1Bits) |
|||
b[5*8+x] = (tmp2 + tmp11 + tmp12) >> (constBits + pass1Bits) |
|||
b[7*8+x] = (tmp3 + tmp10 + tmp13) >> (constBits + pass1Bits) |
|||
} |
|||
} |
|||
File diff suppressed because it is too large
@ -0,0 +1 @@ |
|||
388f7cd6fbe96c7f904ad8994ae174b9878efa4d |
|||
@ -1,614 +0,0 @@ |
|||
// Copyright 2011 The Go Authors. All rights reserved. |
|||
// Use of this source code is governed by a BSD-style |
|||
// license that can be found in the LICENSE file. |
|||
|
|||
package golang |
|||
|
|||
import ( |
|||
"bufio" |
|||
"errors" |
|||
"image" |
|||
"image/color" |
|||
"io" |
|||
) |
|||
|
|||
// min returns the minimum of two integers. |
|||
func min(x, y int) int { |
|||
if x < y { |
|||
return x |
|||
} |
|||
return y |
|||
} |
|||
|
|||
// div returns a/b rounded to the nearest integer, instead of rounded to zero. |
|||
func div(a, b int32) int32 { |
|||
if a >= 0 { |
|||
return (a + (b >> 1)) / b |
|||
} |
|||
return -((-a + (b >> 1)) / b) |
|||
} |
|||
|
|||
// bitCount counts the number of bits needed to hold an integer. |
|||
var bitCount = [256]byte{ |
|||
0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, |
|||
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, |
|||
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, |
|||
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, |
|||
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, |
|||
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, |
|||
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, |
|||
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, |
|||
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, |
|||
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, |
|||
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, |
|||
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, |
|||
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, |
|||
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, |
|||
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, |
|||
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, |
|||
} |
|||
|
|||
type quantIndex int |
|||
|
|||
const ( |
|||
quantIndexLuminance quantIndex = iota |
|||
quantIndexChrominance |
|||
nQuantIndex |
|||
) |
|||
|
|||
// unscaledQuant are the unscaled quantization tables in zig-zag order. Each |
|||
// encoder copies and scales the tables according to its quality parameter. |
|||
// The values are derived from section K.1 after converting from natural to |
|||
// zig-zag order. |
|||
var unscaledQuant = [nQuantIndex][blockSize]byte{ |
|||
// Luminance. |
|||
{ |
|||
16, 11, 12, 14, 12, 10, 16, 14, |
|||
13, 14, 18, 17, 16, 19, 24, 40, |
|||
26, 24, 22, 22, 24, 49, 35, 37, |
|||
29, 40, 58, 51, 61, 60, 57, 51, |
|||
56, 55, 64, 72, 92, 78, 64, 68, |
|||
87, 69, 55, 56, 80, 109, 81, 87, |
|||
95, 98, 103, 104, 103, 62, 77, 113, |
|||
121, 112, 100, 120, 92, 101, 103, 99, |
|||
}, |
|||
// Chrominance. |
|||
{ |
|||
17, 18, 18, 24, 21, 24, 47, 26, |
|||
26, 47, 99, 66, 56, 66, 99, 99, |
|||
99, 99, 99, 99, 99, 99, 99, 99, |
|||
99, 99, 99, 99, 99, 99, 99, 99, |
|||
99, 99, 99, 99, 99, 99, 99, 99, |
|||
99, 99, 99, 99, 99, 99, 99, 99, |
|||
99, 99, 99, 99, 99, 99, 99, 99, |
|||
99, 99, 99, 99, 99, 99, 99, 99, |
|||
}, |
|||
} |
|||
|
|||
type huffIndex int |
|||
|
|||
const ( |
|||
huffIndexLuminanceDC huffIndex = iota |
|||
huffIndexLuminanceAC |
|||
huffIndexChrominanceDC |
|||
huffIndexChrominanceAC |
|||
nHuffIndex |
|||
) |
|||
|
|||
// huffmanSpec specifies a Huffman encoding. |
|||
type huffmanSpec struct { |
|||
// count[i] is the number of codes of length i bits. |
|||
count [16]byte |
|||
// value[i] is the decoded value of the i'th codeword. |
|||
value []byte |
|||
} |
|||
|
|||
// theHuffmanSpec is the Huffman encoding specifications. |
|||
// This encoder uses the same Huffman encoding for all images. |
|||
var theHuffmanSpec = [nHuffIndex]huffmanSpec{ |
|||
// Luminance DC. |
|||
{ |
|||
[16]byte{0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}, |
|||
[]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, |
|||
}, |
|||
// Luminance AC. |
|||
{ |
|||
[16]byte{0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 125}, |
|||
[]byte{ |
|||
0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, |
|||
0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, |
|||
0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, |
|||
0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, |
|||
0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, |
|||
0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, |
|||
0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, |
|||
0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, |
|||
0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, |
|||
0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, |
|||
0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, |
|||
0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, |
|||
0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, |
|||
0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, |
|||
0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, |
|||
0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, |
|||
0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, |
|||
0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, |
|||
0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, |
|||
0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, |
|||
0xf9, 0xfa, |
|||
}, |
|||
}, |
|||
// Chrominance DC. |
|||
{ |
|||
[16]byte{0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}, |
|||
[]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, |
|||
}, |
|||
// Chrominance AC. |
|||
{ |
|||
[16]byte{0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 119}, |
|||
[]byte{ |
|||
0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, |
|||
0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, |
|||
0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, |
|||
0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, |
|||
0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, |
|||
0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, |
|||
0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, |
|||
0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, |
|||
0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, |
|||
0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, |
|||
0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, |
|||
0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, |
|||
0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, |
|||
0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, |
|||
0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, |
|||
0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, |
|||
0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, |
|||
0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, |
|||
0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, |
|||
0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, |
|||
0xf9, 0xfa, |
|||
}, |
|||
}, |
|||
} |
|||
|
|||
// huffmanLUT is a compiled look-up table representation of a huffmanSpec. |
|||
// Each value maps to a uint32 of which the 8 most significant bits hold the |
|||
// codeword size in bits and the 24 least significant bits hold the codeword. |
|||
// The maximum codeword size is 16 bits. |
|||
type huffmanLUT []uint32 |
|||
|
|||
func (h *huffmanLUT) init(s huffmanSpec) { |
|||
maxValue := 0 |
|||
for _, v := range s.value { |
|||
if int(v) > maxValue { |
|||
maxValue = int(v) |
|||
} |
|||
} |
|||
*h = make([]uint32, maxValue+1) |
|||
code, k := uint32(0), 0 |
|||
for i := 0; i < len(s.count); i++ { |
|||
nBits := uint32(i+1) << 24 |
|||
for j := uint8(0); j < s.count[i]; j++ { |
|||
(*h)[s.value[k]] = nBits | code |
|||
code++ |
|||
k++ |
|||
} |
|||
code <<= 1 |
|||
} |
|||
} |
|||
|
|||
// theHuffmanLUT are compiled representations of theHuffmanSpec. |
|||
var theHuffmanLUT [4]huffmanLUT |
|||
|
|||
func init() { |
|||
for i, s := range theHuffmanSpec { |
|||
theHuffmanLUT[i].init(s) |
|||
} |
|||
} |
|||
|
|||
// writer is a buffered writer. |
|||
type writer interface { |
|||
Flush() error |
|||
io.Writer |
|||
io.ByteWriter |
|||
} |
|||
|
|||
// encoder encodes an image to the JPEG format. |
|||
type encoder struct { |
|||
// w is the writer to write to. err is the first error encountered during |
|||
// writing. All attempted writes after the first error become no-ops. |
|||
w writer |
|||
err error |
|||
// buf is a scratch buffer. |
|||
buf [16]byte |
|||
// bits and nBits are accumulated bits to write to w. |
|||
bits, nBits uint32 |
|||
// quant is the scaled quantization tables, in zig-zag order. |
|||
quant [nQuantIndex][blockSize]byte |
|||
} |
|||
|
|||
func (e *encoder) flush() { |
|||
if e.err != nil { |
|||
return |
|||
} |
|||
e.err = e.w.Flush() |
|||
} |
|||
|
|||
func (e *encoder) write(p []byte) { |
|||
if e.err != nil { |
|||
return |
|||
} |
|||
_, e.err = e.w.Write(p) |
|||
} |
|||
|
|||
func (e *encoder) writeByte(b byte) { |
|||
if e.err != nil { |
|||
return |
|||
} |
|||
e.err = e.w.WriteByte(b) |
|||
} |
|||
|
|||
// emit emits the least significant nBits bits of bits to the bit-stream. |
|||
// The precondition is bits < 1<<nBits && nBits <= 16. |
|||
func (e *encoder) emit(bits, nBits uint32) { |
|||
nBits += e.nBits |
|||
bits <<= 32 - nBits |
|||
bits |= e.bits |
|||
for nBits >= 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<<nBits-1), nBits) |
|||
} |
|||
} |
|||
|
|||
// writeMarkerHeader writes the header for a marker with the given length. |
|||
func (e *encoder) writeMarkerHeader(marker uint8, markerlen int) { |
|||
e.buf[0] = 0xff |
|||
e.buf[1] = marker |
|||
e.buf[2] = uint8(markerlen >> 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 |
|||
} |
|||
@ -1,12 +1,20 @@ |
|||
package main |
|||
import "fmt" |
|||
import "os" |
|||
import "./jpeg2" |
|||
import ( |
|||
"fmt" |
|||
"os" |
|||
"image" |
|||
"./jpeg2" |
|||
_ "image/png" |
|||
) |
|||
|
|||
func main() { |
|||
f, err := os.Open("/Users/mweber/dev/fprint/print.jpg"); |
|||
f, err := os.Open("/Users/mweber/dev/ImageProcessor/src/ImageProcessorCore/a.png"); |
|||
m, _, err := image.Decode(f) |
|||
|
|||
f2, err := os.Create("/Users/mweber/dev/ImageProcessor/src/ImageProcessorCore/go.jpg"); |
|||
|
|||
if err == nil { |
|||
jpeg2.Decode(f); |
|||
jpeg2.Encode(f2, m, nil); |
|||
fmt.Println("done") |
|||
} |
|||
} |
|||
|
|||
Loading…
Reference in new issue