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);
     }
 }