private YCbCrImage ycbcrImage; // YCrCb #endregion Fields #region Constructors public JpegDecoderCore() { huff = new Huffman[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 BitsClass(); bytes = new BytesClass(); for (int i = 0; i < maxTc + 1; i++){ for (int j = 0; j < maxTh + 1; j++){ huff[i, j] = new Huffman(); } } for (int i = 0; i < quant.Length; i++){ quant[i] = new Block(); } for (int i = 0; i < comp.Length; i++){ comp[i] = new Component(); } }
private int WriteBlock(Block block, QuantIndex index, int prevDc) { FDCT.Transform(block); int dc = Round(block[0], 8*quant[(int) index][0]); EmitHuffRle((HuffIndex) (2*(int) index + 0), 0, dc - prevDc); var h = (HuffIndex) (2*(int) index + 1); int runLength = 0; for (int zig = 1; zig < Block.blockSize; zig++){ int ac = Round(block[unzig[zig]], 8*quant[(int) index][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; }
private void Encode444(IPixelAccessor pixels) { Block b = new Block(); Block cb = new Block(); Block cr = new Block(); int prevDcy = 0, prevDcCb = 0, prevDcCr = 0; for (int y = 0; y < pixels.Height; y += 8){ for (int x = 0; x < pixels.Width; x += 8){ ToYCbCr(pixels, x, y, b, cb, cr); prevDcy = WriteBlock(b, QuantIndex.Luminance, prevDcy); prevDcCb = WriteBlock(cb, QuantIndex.Chrominance, prevDcCb); prevDcCr = WriteBlock(cr, QuantIndex.Chrominance, prevDcCr); } } }
private void Encode420(IPixelAccessor pixels) { 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 < pixels.Height; y += 16){ for (int x = 0; x < pixels.Width; x += 16){ for (int i = 0; i < 4; i++){ int xOff = (i & 1)*8; int yOff = (i & 2)*4; ToYCbCr(pixels, x + xOff, y + yOff, b, cb[i], cr[i]); prevDcy = WriteBlock(b, QuantIndex.Luminance, prevDcy); } Scale16X16_8X8(b, cb); prevDcCb = WriteBlock(b, QuantIndex.Chrominance, prevDcCb); Scale16X16_8X8(b, cr); prevDcCr = WriteBlock(b, QuantIndex.Chrominance, prevDcCr); } } }
private static void ToYCbCr(IPixelAccessor pixels, int x, int y, Block yBlock, Block cbBlock, Block crBlock) { int xmax = pixels.Width - 1; int ymax = pixels.Height - 1; for (int j = 0; j < 8; j++){ for (int i = 0; i < 8; i++){ byte[] pixel = pixels[Math.Min(x + i, xmax), Math.Min(y + j, ymax)].ToBytes(); YCbCr2 color = Color2.FromArgb(pixel[3], pixel[0], pixel[1], pixel[2]); int index = 8*j + i; yBlock[index] = (int) color.Y; cbBlock[index] = (int) color.Cb; crBlock[index] = (int) color.Cr; } } }
private static void Scale16X16_8X8(Block destination, Block[] source) { 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 = source[i][j] + source[i][j + 1] + source[i][j + 8] + source[i][j + 9]; destination[8*y + x + dstOff] = (sum + 2)/4; } } } }
private 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); byte lnComp = tmp[0]; if (n != 4 + (2*lnComp)){ throw new Exception("SOS length inconsistent with number of components"); } Scan[] scan = new Scan[maxComponents]; int totalHv = 0; for (int i = 0; i < lnComp; i++){ int cs = tmp[1 + (2*i)]; int compIndex = -1; for (int j = 0; j < nComp; j++){ Component compv = comp[j]; if (cs == compv.c){ compIndex = j; } } if (compIndex < 0){ throw new Exception("Unknown component selector"); } scan[i].compIndex = (byte) compIndex; 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"); } } if (nComp > 1 && totalHv > 10){ throw new Exception("Total sampling factors too large."); } int zigStart = 0; int zigEnd = Block.blockSize - 1; int ah = 0; int al = 0; if (progressive){ zigStart = tmp[1 + (2*lnComp)]; zigEnd = tmp[2 + (2*lnComp)]; ah = tmp[3 + (2*lnComp)] >> 4; al = tmp[3 + (2*lnComp)] & 0x0f; if ((zigStart == 0 && zigEnd != 0) || zigStart > zigEnd || Block.blockSize <= zigEnd){ throw new Exception("Bad spectral selection bounds"); } if (zigStart != 0 && lnComp != 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"); } } 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 (grayImage == null && ycbcrImage == null){ MakeImg(mxx, myy); } if (progressive){ for (int i = 0; i < lnComp; 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 BitsClass(); int mcu = 0; byte expectedRst = JpegConstants.Markers.RST0; Block b = new Block(); int[] dc = new int[maxComponents]; int blockCount = 0; for (int my = 0; my < myy; my++){ for (int mx = 0; mx < mxx; mx++){ for (int i = 0; i < lnComp; 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++){ int bx; int by; if (lnComp != 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; } } b = progressive ? progCoeffs[compIndex][@by*mxx*hi + bx] : new Block(); if (ah != 0){ Refine(b, huff[acTable, scan[i].ta], zigStart, zigEnd, 1 << al); } else{ int zig = zigStart; if (zig == 0){ zig++; byte value = DecodeHuffman(huff[dcTable, scan[i].td]); if (value > 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{ 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; } int ac = ReceiveExtend(val1); b[unzig[zig]] = ac << al; } else{ if (val0 != 0x0f){ eobRun = (ushort) (1 << val0); if (val0 != 0){ eobRun |= (ushort) DecodeBits(val0); } eobRun--; break; } zig += 0x0f; } } } } if (progressive){ if (zigEnd != Block.blockSize - 1 || al != 0){ progCoeffs[compIndex][by*mxx*hi + bx] = b; continue; } } for (int zig = 0; zig < Block.blockSize; zig++){ b[unzig[zig]] *= qt[zig]; } IDCT.Transform(b); byte[] dst; int offset; int stride; if (nComp == 1){ dst = grayImage.pixels; stride = grayImage.stride; offset = grayImage.offset + 8*(by*grayImage.stride + bx); } else{ switch (compIndex){ case 0: dst = ycbcrImage.pix_y; stride = ycbcrImage.y_stride; offset = ycbcrImage.y_offset + 8*(by*ycbcrImage.y_stride + bx); break; case 1: dst = ycbcrImage.pix_cb; stride = ycbcrImage.c_stride; offset = ycbcrImage.c_offset + 8*(by*ycbcrImage.c_stride + bx); break; case 2: dst = ycbcrImage.pix_cr; stride = ycbcrImage.c_stride; offset = ycbcrImage.c_offset + 8*(by*ycbcrImage.c_stride + bx); break; case 3: throw new Exception("Too many components"); default: throw new Exception("Too many components"); } } 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){ ReadFull(tmp, 0, 2); if (tmp[0] != 0xff || tmp[1] != expectedRst){ throw new Exception("Bad RST marker"); } expectedRst++; if (expectedRst == JpegConstants.Markers.RST7 + 1){ expectedRst = JpegConstants.Markers.RST0; } bits = new BitsClass(); dc = new int[maxComponents]; eobRun = 0; } } // for mx } // for my }
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 Refine(Block b, Huffman h, int zigStart, int zigEnd, int delta) { if (zigStart == 0){ if (zigEnd != 0){ throw new Exception("Invalid state for zig DC component"); } bool bit = DecodeBit(); if (bit){ b[0] |= delta; } return; } 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"); } if (done){ break; } zig = RefineNonZeroes(b, zig, zigEnd, val0, delta); if (zig > zigEnd){ throw new Exception($"too many coefficients {zig} > {zigEnd}"); } if (z != 0){ b[unzig[zig]] = z; } } } if (eobRun > 0){ eobRun--; RefineNonZeroes(b, zig, zigEnd, -1, delta); } }