text: import array, sys
  # NanoJPEG -- KeyJ's Tiny Baseline JPEG Decoder
  # version 1.1 (2010-03-05)
  # by Martin J. Fiedler <martin.fiedler@gmx.net>
  # http://keyj.emphy.de/nanojpeg/
  #
  # This software is published under the terms of KeyJ's Research License,
  # version 0.2. Usage of this software is subject to the following conditions:
  # 0. There's no warranty whatsoever. The author(s) of this software can not
  #    be held liable for any damages that occur when using this software.
  # 1. This software may be used freely for both non-commercial and commercial
  #    purposes.
  # 2. This software may be redistributed freely as long as no fees are charged
  #    for the distribution and this license information is included.
  # 3. This software may be modified freely except for this license information,
  #    which must not be changed in any way.
  # 4. If anything other than configuration, indentation or comments have been
  #    altered in the code, the original author(s) must receive a copy of the
  #    modified code.
  #
  # Ported to python by Andras Suller <suller.andras@gmail.com>
  
  
  ###############################################################################
  ## DOCUMENTATION SECTION                                                     ##
  ## read this if you want to know what this is all about                      ##
  ###############################################################################
  
  # INTRODUCTION
  # ============
  #
  # This is a minimal decoder for baseline JPEG images. It accepts memory dumps
  # of JPEG files as input and generates either 8-bit grayscale or packed 24-bit
  # RGB images as output. It does not parse JFIF or Exif headers; all JPEG files
  # are assumed to be either grayscale or YCbCr. CMYK or other color spaces are
  # not supported. All YCbCr subsampling schemes with power-of-two ratios are
  # supported, as are restart intervals. Progressive or lossless JPEG is not
  # supported.
  # Summed up, NanoJPEG should be able to decode all images from digital cameras
  # and most common forms of other non-progressive JPEG images.
  # The decoder is not optimized for speed, it's optimized for simplicity and
  # small code. Image quality should be at a reasonable level. A bicubic chroma
  # upsampling filter ensures that subsampled YCbCr images are rendered in
  # decent quality. The decoder is not meant to deal with broken JPEG files in
  # a graceful manner; if anything is wrong with the bitstream, decoding will
  # simply fail.
  # The code should work with every modern C compiler without problems and
  # should not emit any warnings. It uses only (at least) 32-bit integer
  # arithmetic and is supposed to be endianness independent and 64-bit clean.
  # However, it is not thread-safe.
  
  
  # COMPILE-TIME CONFIGURATION
  # ==========================
  #
  # The following aspects of NanoJPEG can be controlled with preprocessor
  # defines:
  #
  # _NJ_EXAMPLE_PROGRAM     = Compile a main() function with an example
  #                           program.
  # _NJ_INCLUDE_HEADER_ONLY = Don't compile anything, just act as a header
  #                           file for NanoJPEG. Example:
  #                               #define _NJ_INCLUDE_HEADER_ONLY
  #                               #include "nanojpeg.c"
  #                               int main(void) {
  #                                   njInit();
  #                                   // your code here
  #                                   njDone();
  #                               }
  # NJ_USE_LIBC=1           = Use the malloc(), free(), memset() and memcpy()
  #                           functions from the standard C library (default).
  # NJ_USE_LIBC=0           = Don't use the standard C library. In this mode,
  #                           external functions njAlloc(), njFreeMem(),
  #                           njFillMem() and njCopyMem() need to be defined
  #                           and implemented somewhere.
  # NJ_USE_WIN32=0          = Normal mode (default).
  # NJ_USE_WIN32=1          = If compiling with MSVC for Win32 and
  #                           NJ_USE_LIBC=0, NanoJPEG will use its own
  #                           implementations of the required C library
  #                           functions (default if compiling with MSVC and
  #                           NJ_USE_LIBC=0).
  # NJ_CHROMA_FILTER=1      = Use the bicubic chroma upsampling filter
  #                           (default).
  # NJ_CHROMA_FILTER=0      = Use simple pixel repetition for chroma upsampling
  #                           (bad quality, but faster and less code).
  
  
  # API
  # ===
  #
  # For API documentation, read the "header section" below.
  
  
  # EXAMPLE
  # =======
  #
  # A few pages below, you can find an example program that uses NanoJPEG to
  # convert JPEG files into PGM or PPM. To compile it, use something like
  #     gcc -O3 -D_NJ_EXAMPLE_PROGRAM -o nanojpeg nanojpeg.c
  # You may also add -std=c99 -Wall -Wextra -pedantic -Werror, if you want :)
  
  
  ###############################################################################
  ## HEADER SECTION                                                            ##
  ## copy and pase this into nanojpeg.h if you want                            ##
  ###############################################################################
  
  #ifndef _NANOJPEG_H
  #define _NANOJPEG_H
  
  # nj_result_t: Result codes for njDecode().
  NJ_OK = 0           # no error, decoding successful
  NJ_NO_JPEG = 1      # not a JPEG file
  NJ_UNSUPPORTED = 2  # unsupported format
  NJ_OUT_OF_MEM = 3   # out of memory
  NJ_INTERNAL_ERR = 4 # internal error
  NJ_SYNTAX_ERROR = 5 # syntax error
  __NJ_FINISHED = 6   # used internally, will never be reported
  
  # njInit: Initialize NanoJPEG.
  # For safety reasons, this should be called at least one time before using
  # using any of the other NanoJPEG functions.
  #void njInit(void);
  
  # njDecode: Decode a JPEG image.
  # Decodes a memory dump of a JPEG file into internal buffers.
  # Parameters:
  #   jpeg = The pointer to the memory dump.
  #   size = The size of the JPEG file.
  # Return value: The error code in case of failure, or NJ_OK (zero) on success.
  #nj_result_t njDecode(const void* jpeg, const int size);
  
  # njGetWidth: Return the width (in pixels) of the most recently decoded
  # image. If njDecode() failed, the result of njGetWidth() is undefined.
  #int njGetWidth(void);
  
  # njGetHeight: Return the height (in pixels) of the most recently decoded
  # image. If njDecode() failed, the result of njGetHeight() is undefined.
  #int njGetHeight(void);
  
  # njIsColor: Return 1 if the most recently decoded image is a color image
  # (RGB) or 0 if it is a grayscale image. If njDecode() failed, the result
  # of njGetWidth() is undefined.
  #int njIsColor(void);
  
  # njGetImage: Returns the decoded image data.
  # Returns a pointer to the most recently image. The memory layout it byte-
  # oriented, top-down, without any padding between lines. Pixels of color
  # images will be stored as three consecutive bytes for the red, green and
  # blue channels. This data format is thus compatible with the PGM or PPM
  # file formats and the OpenGL texture formats GL_LUMINANCE8 or GL_RGB8.
  # If njDecode() failed, the result of njGetImage() is undefined.
  #unsigned char* njGetImage(void);
  
  # njGetImageSize: Returns the size (in bytes) of the image data returned
  # by njGetImage(). If njDecode() failed, the result of njGetImageSize() is
  # undefined.
  #int njGetImageSize(void);
  
  # njDone: Uninitialize NanoJPEG.
  # Resets NanoJPEG's internal state and frees all memory that has been
  # allocated at run-time by NanoJPEG. It is still possible to decode another
  # image after a njDone() call.
  #void njDone(void);
  
  #endif//_NANOJPEG_H
  
  
  ###############################################################################
  ## CONFIGURATION SECTION                                                     ##
  ## adjust the default settings for the NJ_ defines here                      ##
  ###############################################################################
  
  NJ_USE_LIBC = 1
  
  NJ_USE_WIN32 = 0
  
  NJ_CHROMA_FILTER = 1
  
  
  ###############################################################################
  ## EXAMPLE PROGRAM                                                           ##
  ## just define _NJ_EXAMPLE_PROGRAM to compile this (requires NJ_USE_LIBC)    ##
  ###############################################################################
  
  # #ifdef  _NJ_EXAMPLE_PROGRAM
  
  # #include <stdio.h>
  # #include <stdlib.h>
  # #include <string.h>
  
  # int main(int argc, char* argv[]) {
  #     int size;
  #     char *buf;
  #     FILE *f;
  
  #     if (argc < 2) {
  #         printf("Usage: %s <input.jpg> [<output.ppm>]\n", argv[0]);
  #         return 2;
  #     }
  #     f = fopen(argv[1], "rb");
  #     if (!f) {
  #         printf("Error opening the input file.\n");
  #         return 1;
  #     }
  #     fseek(f, 0, SEEK_END);
  #     size = (int) ftell(f);
  #     buf = malloc(size);
  #     fseek(f, 0, SEEK_SET);
  #     size = (int) fread(buf, 1, size, f);
  #     fclose(f);
  
  #     njInit();
  #     if (njDecode(buf, size)) {
  #         printf("Error decoding the input file.\n");
  #         return 1;
  #     }
  
  #     f = fopen((argc > 2) ? argv[2] : (njIsColor() ? "nanojpeg_out.ppm" : "nanojpeg_out.pgm"), "wb");
  #     if (!f) {
  #         printf("Error opening the output file.\n");
  #         return 1;
  #     }
  #     fprintf(f, "P%d\n%d %d\n255\n", njIsColor() ? 6 : 5, njGetWidth(), njGetHeight());
  #     fwrite(njGetImage(), 1, njGetImageSize(), f);
  #     fclose(f);
  #     njDone();
  #     return 0;
  # }
  
  #endif
  
  
  ###############################################################################
  ## IMPLEMENTATION SECTION                                                    ##
  ## you may stop reading here                                                 ##
  ###############################################################################
  
  #ifndef _NJ_INCLUDE_HEADER_ONLY
  
  # typedef struct _nj_code {
  #     unsigned char bits, code;
  # } nj_vlc_code_t;
  
  class nj_vlc_code_t(object):
      def __init__(self):
          self.bits = 0
          self.code = 0
  
  # typedef struct _nj_cmp {
  #     int cid;
  #     int ssx, ssy;
  #     int width, height;
  #     int stride;
  #     int qtsel;
  #     int actabsel, dctabsel;
  #     int dcpred;
  #     unsigned char *pixels;
  # } nj_component_t;
  
  class nj_component_t(object):
      def __init__(self):
          self.cid = 0
          self.ssx = 0
          self.ssy= 0
          self.width = 0
          self.height = 0
          self.stride = 0
          self.qtsel = 0
          self.actabsel = 0
          self.dctabsel = 0
          self.dcpred = 0
          self.pixels = None
  
  # typedef struct _nj_ctx {
  #     nj_result_t error;
  #     const unsigned char *pos;
  #     int size;
  #     int length;
  #     int width, height;
  #     int mbwidth, mbheight;
  #     int mbsizex, mbsizey;
  #     int ncomp;
  #     nj_component_t comp[3];
  #     int qtused, qtavail;
  #     unsigned char qtab[4][64];
  #     nj_vlc_code_t vlctab[4][65536];
  #     int buf, bufbits;
  #     int block[64];
  #     int rstinterval;
  #     unsigned char *rgb;
  # } nj_context_t;
  
  class nj_context_t(object):
      def init(self):
          self.error = 0
          self.spos = None # new param. it stores the string what is indexed by pos
          self.pos = 0
          self.size = 0
          self.length = 0
          self.width = 0
          self.height = 0
          self.mbwidth = 0
          self.mbheight = 0
          self.mbsizex = 0
          self.mbsizey = 0
          self.ncomp = 0
          self.comp = [nj_component_t(), nj_component_t(), nj_component_t()]
          self.qtused = 0
          self.qtavail = 0
          self.qtab = [[0] * 64, [0] * 64, [0] * 64, [0] * 64]
          # nj_vlc_code_t vlctab[4][65536] = None
          self.vlctab = []
          for n in range(4):
              self.vlctab.append([nj_vlc_code_t() for i in range(65536)])
          self.buf = 0
          self.bufbits = 0
          self.block = [0] * 64
          self.rstinterval = 0
          self.rgb = None
  
  # static nj_context_t nj;
  nj = nj_context_t()
  
  njZZ = [ 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 ]
  
  def njClip(x):
      if x < 0: return 0
      if x > 0xFF: return 0xFF
      return x
  
  W1 = 2841
  W2 = 2676
  W3 = 2408
  W5 = 1609
  W6 = 1108
  W7 = 565
  
  def njRowIDCT(blk, p):
      x1 = blk[p + 4] << 11
      x2 = blk[p + 6]
      x3 = blk[p + 2]
      x4 = blk[p + 1]
      x5 = blk[p + 7]
      x6 = blk[p + 5]
      x7 = blk[p + 3]
      if (not (x1 | x2 | x3 | x4 | x5 | x6 | x7)):
          v = blk[p + 0] << 3
          blk[p + 0] = v
          blk[p + 1] = v
          blk[p + 2] = v
          blk[p + 3] = v
          blk[p + 4] = v
          blk[p + 5] = v
          blk[p + 6] = v
          blk[p + 7] = v
          return
      x0 = (blk[p + 0] << 11) + 128
      x8 = W7 * (x4 + x5)
      x4 = x8 + (W1 - W7) * x4
      x5 = x8 - (W1 + W7) * x5
      x8 = W3 * (x6 + x7)
      x6 = x8 - (W3 - W5) * x6
      x7 = x8 - (W3 + W5) * x7
      x8 = x0 + x1
      x0 -= x1
      x1 = W6 * (x3 + x2)
      x2 = x1 - (W2 + W6) * x2
      x3 = x1 + (W2 - W6) * x3
      x1 = x4 + x6
      x4 -= x6
      x6 = x5 + x7
      x5 -= x7
      x7 = x8 + x3
      x8 -= x3
      x3 = x0 + x2
      x0 -= x2
      x2 = (181 * (x4 + x5) + 128) >> 8
      x4 = (181 * (x4 - x5) + 128) >> 8
      blk[p + 0] = (x7 + x1) >> 8
      blk[p + 1] = (x3 + x2) >> 8
      blk[p + 2] = (x0 + x4) >> 8
      blk[p + 3] = (x8 + x6) >> 8
      blk[p + 4] = (x8 - x6) >> 8
      blk[p + 5] = (x0 - x4) >> 8
      blk[p + 6] = (x3 - x2) >> 8
      blk[p + 7] = (x7 - x1) >> 8
  
  
  #blk was a char *, but we need to use an array and an index instead.
  #sout is an extra parameter: it is the string what we need to modify,
  #and out is the position inside sout (index)
  def njColIDCT(blk, p, sout, out, stride):
      x1 = blk[p + 8*4] << 8
      x2 = blk[p + 8*6]
      x3 = blk[p + 8*2]
      x4 = blk[p + 8*1]
      x5 = blk[p + 8*7]
      x6 = blk[p + 8*5]
      x7 = blk[p + 8*3]
      if (not (x1 | x2 | x3 | x4 | x5 | x6 | x7)):
          x1 = njClip(((blk[p + 0] + 32) >> 6) + 128)
          x0 = 8
          while x0:
              sout[out] = x1
              out += stride
              x0 -= 1
          return
  
      x0 = (blk[p + 0] << 8) + 8192
      x8 = W7 * (x4 + x5) + 4
      x4 = (x8 + (W1 - W7) * x4) >> 3
      x5 = (x8 - (W1 + W7) * x5) >> 3
      x8 = W3 * (x6 + x7) + 4
      x6 = (x8 - (W3 - W5) * x6) >> 3
      x7 = (x8 - (W3 + W5) * x7) >> 3
      x8 = x0 + x1
      x0 -= x1
      x1 = W6 * (x3 + x2) + 4
      x2 = (x1 - (W2 + W6) * x2) >> 3
      x3 = (x1 + (W2 - W6) * x3) >> 3
      x1 = x4 + x6
      x4 -= x6
      x6 = x5 + x7
      x5 -= x7
      x7 = x8 + x3
      x8 -= x3
      x3 = x0 + x2
      x0 -= x2
      x2 = (181 * (x4 + x5) + 128) >> 8
      x4 = (181 * (x4 - x5) + 128) >> 8
      sout[out] = njClip(((x7 + x1) >> 14) + 128)
      out += stride
      sout[out] = njClip(((x3 + x2) >> 14) + 128)
      out += stride
      sout[out] = njClip(((x0 + x4) >> 14) + 128)
      out += stride
      sout[out] = njClip(((x8 + x6) >> 14) + 128)
      out += stride
      sout[out] = njClip(((x8 - x6) >> 14) + 128)
      out += stride
      sout[out] = njClip(((x0 - x4) >> 14) + 128)
      out += stride
      sout[out] = njClip(((x3 - x2) >> 14) + 128)
      out += stride
      sout[out] = njClip(((x7 - x1) >> 14) + 128)
  
  
  def njShowBits(bits):
      if (not bits): return 0
      while (nj.bufbits < bits):
          if (nj.size <= 0):
              nj.buf = (nj.buf << 8) | 0xFF
              nj.bufbits += 8
              continue
  
          newbyte = nj.spos[nj.pos]
          nj.pos += 1
          nj.size -= 1
          nj.bufbits += 8
          nj.buf = (nj.buf << 8) | newbyte
          if (newbyte == 0xFF):
              if (nj.size):
                  marker = nj.spos[nj.pos]
                  nj.pos += 1
                  nj.size -= 1
                  if marker == 0xD9:
                      nj.size = 0
                  elif marker != 0:
                      if ((marker & 0xF8) != 0xD0):
                          raise Exception(NJ_SYNTAX_ERROR)
                      else:
                          nj.buf = (nj.buf << 8) | marker
                          nj.bufbits += 8
              else:
                  raise Exception(NJ_SYNTAX_ERROR)
      nj.buf = nj.buf & ((1 << nj.bufbits) - 1)
      return (nj.buf >> (nj.bufbits - bits)) & ((1 << bits) - 1)
  
  def njSkipBits(bits):
      if (nj.bufbits < bits):
          njShowBits(bits)
      nj.bufbits -= bits
  
  def njGetBits(bits):
      res = njShowBits(bits)
      njSkipBits(bits)
      return res
  
  def njByteAlign():
      nj.bufbits &= 0xF8
  
  def njSkip(count):
      nj.pos += count
      nj.size -= count
      nj.length -= count
      if (nj.size < 0): raise Exception(NJ_SYNTAX_ERROR)
  
  def njDecode16(pos):
      return (nj.spos[pos] << 8) | nj.spos[pos + 1]
  
  def njDecodeLength():
      if (nj.size < 2):
          raise Exception(NJ_SYNTAX_ERROR)
      nj.length = njDecode16(nj.pos)
      if (nj.length > nj.size):
          raise Exception(NJ_SYNTAX_ERROR)
      njSkip(2)
  
  def njSkipMarker():
      njDecodeLength()
      njSkip(nj.length)
  
  def njDecodeSOF():
      ssxmax = 0
      ssymax = 0
      njDecodeLength()
      if (nj.length < 9):
          raise Exception(NJ_SYNTAX_ERROR)
      if (nj.spos[nj.pos] != 8):
          raise Exception(NJ_UNSUPPORTED)
      nj.height = njDecode16(nj.pos + 1)
      nj.width = njDecode16(nj.pos + 3)
      nj.ncomp = nj.spos[nj.pos + 5]
      njSkip(6)
      if nj.ncomp != 1 and nj.ncomp != 3:
          raise Exception(NJ_UNSUPPORTED)
      if (nj.length < (nj.ncomp * 3)):
          raise Exception(NJ_SYNTAX_ERROR)
      i = 0
      while i < nj.ncomp:
          c = nj.comp[i]
          c.cid = nj.spos[nj.pos]
          c.ssx = nj.spos[nj.pos + 1] >> 4
          if not c.ssx:
              raise Exception(NJ_SYNTAX_ERROR)
          if (c.ssx & (c.ssx - 1)):
              raise Exception(NJ_UNSUPPORTED)  # non-power of two
          c.ssy = nj.spos[nj.pos + 1] & 15
          if not c.ssy:
              raise Exception(NJ_SYNTAX_ERROR)
          if (c.ssy & (c.ssy - 1)):
              raise Exception(NJ_UNSUPPORTED)  # non-power of two
          c.qtsel = nj.spos[nj.pos + 2]
          if c.qtsel & 0xFC:
              raise Exception(NJ_SYNTAX_ERROR)
          njSkip(3)
          nj.qtused |= 1 << c.qtsel
          if (c.ssx > ssxmax): ssxmax = c.ssx
          if (c.ssy > ssymax): ssymax = c.ssy
          i += 1
  
      nj.mbsizex = ssxmax << 3
      nj.mbsizey = ssymax << 3
      nj.mbwidth = (nj.width + nj.mbsizex - 1) // nj.mbsizex
      nj.mbheight = (nj.height + nj.mbsizey - 1) // nj.mbsizey
      i = 0
      while i < nj.ncomp:
          c = nj.comp[i]
          c.width = (nj.width * c.ssx + ssxmax - 1) // ssxmax
          c.stride = (c.width + 7)
          c.stride = c.stride & 0x7FFFFFF8
          c.height = (nj.height * c.ssy + ssymax - 1) // ssymax
          c.stride = nj.mbwidth * nj.mbsizex * c.ssx // ssxmax
          if (((c.width < 3) and (c.ssx != ssxmax)) or ((c.height < 3) and (c.ssy != ssymax))):
                  raise Exception(NJ_UNSUPPORTED)
          c.pixels = [0] * (c.stride * (nj.mbheight * nj.mbsizey * c.ssy // ssymax))
          i += 1
      if (nj.ncomp == 3):
          nj.rgb = [0] * (nj.width * nj.height * nj.ncomp)
      njSkip(nj.length)
  
  def njDecodeDHT():
      counts = [0] * 16
      njDecodeLength()
      while (nj.length >= 17):
          i = nj.spos[nj.pos]
          if (i & 0xEC):
              raise Exception(NJ_SYNTAX_ERROR)
          if (i & 0x02):
              raise Exception(NJ_UNSUPPORTED)
          i = (i | (i >> 3)) & 3  # combined DC/AC + tableid value
          for codelen in range(1, 17): # 1 to 16
              counts[codelen - 1] = nj.spos[nj.pos + codelen]
          njSkip(17)
          vlc = 0
          remain = 65536
          spread = 65536
          for codelen in range(1, 17): # 1 to 16
              spread >>= 1
              currcnt = counts[codelen - 1]
              if not currcnt: continue
              if (nj.length < currcnt):
                  raise Exception(NJ_SYNTAX_ERROR)
              remain -= currcnt << (16 - codelen)
              if (remain < 0):
                  raise Exception(NJ_SYNTAX_ERROR)
              for ii in range(currcnt):
                  code = nj.spos[nj.pos + ii]
                  j = spread
                  while j:
                      nj.vlctab[i][vlc].bits = codelen
                      nj.vlctab[i][vlc].code = code
                      vlc += 1
                      j -= 1
              njSkip(currcnt)
          while remain:
              remain -= 1
              nj.vlctab[i][vlc].bits = 0
              vlc += 1
      if (nj.length):
          raise Exception(NJ_SYNTAX_ERROR)
  
  def njDecodeDQT():
      njDecodeLength()
      while (nj.length >= 65):
          i = nj.spos[nj.pos]
          if (i & 0xFC):
              raise Exception(NJ_SYNTAX_ERROR)
          nj.qtavail |= 1 << i
          for j in range(64):
              nj.qtab[i][j] = nj.spos[nj.pos + j + 1]
          njSkip(65)
      if (nj.length):
          raise Exception(NJ_SYNTAX_ERROR)
  
  def njDecodeDRI():
      njDecodeLength()
      if (nj.length < 2):
          raise Exception(NJ_SYNTAX_ERROR)
      nj.rstinterval = njDecode16(nj.pos)
      njSkip(nj.length)
  
  #code is an array with one element, since we need to return the code to the caller
  def njGetVLC(vlc, code):
      value = njShowBits(16)
      bits = vlc[value].bits
      if not bits:
          raise Exception(NJ_SYNTAX_ERROR)
      njSkipBits(bits)
      value = vlc[value].code
      if code: code[0] = value
      bits = value & 15
      if not bits: return 0
      value = njGetBits(bits)
      if (value < (1 << (bits - 1))):
          value += ((-1) << bits) + 1
      return value
  
  #sout is a new parameter, because we need to modify the passed in array, so
  #out is now just the index in out
  def njDecodeBlock(c, sout, out):
      code = [0]
      value = 0
      coef = 0
      for i in range(len(nj.block)):
          nj.block[i] = 0
      c.dcpred += njGetVLC(nj.vlctab[c.dctabsel], None)
      nj.block[0] = c.dcpred * nj.qtab[c.qtsel][0]
      while True: # do {
          value = njGetVLC(nj.vlctab[c.actabsel], code);
          if not code[0]: break  # EOB
          if (not (code[0] & 0x0F) and (code[0] != 0xF0)):
              raise Exception(NJ_SYNTAX_ERROR)
          coef += (code[0] >> 4) + 1
          if coef > 63:
              raise Exception(NJ_SYNTAX_ERROR)
          nj.block[njZZ[coef]] = value * nj.qtab[c.qtsel][coef]
          # } while (coef < 63);
          if coef >= 63: break
      coef = 0
      while coef < 64:
          njRowIDCT(nj.block, coef)
          coef += 8
      for coef in range(8):
          njColIDCT(nj.block, coef, sout, out + coef, c.stride)
  
  def njDecodeScan():
      rstcount = nj.rstinterval
      nextrst = 0
      # nj_component_t* c;
      njDecodeLength()
      if (nj.length < (4 + 2 * nj.ncomp)):
          raise Exception(NJ_SYNTAX_ERROR)
      if (nj.spos[nj.pos] != nj.ncomp):
          raise Exception(NJ_UNSUPPORTED)
      njSkip(1)
      i = 0
      while (i < nj.ncomp):
          c = nj.comp[i]
          if (nj.spos[nj.pos] != c.cid):
              raise Exception(NJ_SYNTAX_ERROR)
          if (nj.spos[nj.pos + 1] & 0xEE):
              raise Exception(NJ_SYNTAX_ERROR)
          c.dctabsel = nj.spos[nj.pos + 1] >> 4
          c.actabsel = (nj.spos[nj.pos + 1] & 1) | 2
          njSkip(2)
          i += 1
      if (nj.spos[nj.pos] or (nj.spos[nj.pos + 1] != 63) or nj.spos[nj.pos + 2]):
          raise Exception(NJ_UNSUPPORTED)
      njSkip(nj.length)
      mbx = 0
      mby = 0
      while True:
          i = 0
          while (i < nj.ncomp):
              c = nj.comp[i]
              sby = 0
              while sby < c.ssy:
                  sbx = 0
                  while sbx < c.ssx:
                      njDecodeBlock(c, c.pixels, ((mby * c.ssy + sby) * c.stride + mbx * c.ssx + sbx) << 3)
                      if nj.error:
                          return
                      sbx += 1
                  sby += 1
              i += 1
          mbx += 1
          if mbx >= nj.mbwidth:
              mbx = 0
              mby += 1
              if mby >= nj.mbheight: break
          rstcount -= 1
          if (nj.rstinterval and not rstcount):
              njByteAlign()
              i = njGetBits(16)
              if (((i & 0xFFF8) != 0xFFD0) or ((i & 7) != nextrst)):
                  raise Exception(NJ_SYNTAX_ERROR)
              nextrst = (nextrst + 1) & 7
              rstcount = nj.rstinterval
              for i in range(3):
                  nj.comp[i].dcpred = 0
      nj.error = __NJ_FINISHED
  
  #if NJ_CHROMA_FILTER
  
  CF4A = -9
  CF4B = 111
  CF4C = 29
  CF4D = -3
  CF3A = 28
  CF3B = 109
  CF3C = -9
  CF3X = 104
  CF3Y = 27
  CF3Z = -3
  CF2A = 139
  CF2B = -11
  def CF(x):
      return njClip(((x) + 64) >> 7)
  
  def njUpsampleH(c):
      xmax = c.width - 3
      out = [0] * ((c.width * c._height) << 1)
      lin = 0
      lout = 0
      y = c._height
      while y:
          out[lout + 0] = CF(CF2A * c.pixels[lin + 0] + CF2B * c.pixels[lin + 1])
          out[lout + 1] = CF(CF3X * c.pixels[lin + 0] + CF3Y * c.pixels[lin + 1] + CF3Z * c.pixels[lin + 2])
          out[lout + 2] = CF(CF3A * c.pixels[lin + 0] + CF3B * c.pixels[lin + 1] + CF3C * c.pixels[lin + 2])
          for x in range(xmax):
              out[lout + (x << 1) + 3] = CF(CF4A * c.pixels[lin + x] + CF4B * c.pixels[lin + x + 1] + CF4C * c.pixels[lin + x + 2] + CF4D * c.pixels[lin + x + 3])
              out[lout + (x << 1) + 4] = CF(CF4D * c.pixels[lin + x] + CF4C * c.pixels[lin + x + 1] + CF4B * c.pixels[lin + x + 2] + CF4A * c.pixels[lin + x + 3])
          lin += c.stride
          lout += c.width << 1
          out[lout - 3] = CF(CF3A * c.pixels[lin - 1] + CF3B * c.pixels[lin - 2] + CF3C * c.pixels[lin - 3])
          out[lout - 2] = CF(CF3X * c.pixels[lin - 1] + CF3Y * c.pixels[lin - 2] + CF3Z * c.pixels[lin - 3])
          out[lout - 1] = CF(CF2A * c.pixels[lin - 1] + CF2B * c.pixels[lin - 2])
          y -= 1
      c.width <<= 1
      c.stride = c.width
      c.pixels = out
  
  def njUpsampleV(c):
      w = c.width
      s1 = c.stride
      s2 = s1 + s1
      out = [0] * ((c.width * c._height) << 1)
      for x in range(w):
          cin = x
          cout = x
          out[cout] = CF(CF2A * c.pixels[cin] + CF2B * c.pixels[cin + s1])
          cout += w
          out[cout] = CF(CF3X * c.pixels[cin] + CF3Y * c.pixels[cin + s1] + CF3Z * c.pixels[cin + s2])
          cout += w
          out[cout] = CF(CF3A * c.pixels[cin] + CF3B * c.pixels[cin + s1] + CF3C * c.pixels[cin + s2])
          cout += w
          cin += s1
          y = c._height - 3
          while y:
              out[cout] = CF(CF4A * c.pixels[cin - s1] + CF4B * c.pixels[cin] + CF4C * c.pixels[cin + s1] + CF4D * c.pixels[cin + s2])
              cout += w
              out[cout] = CF(CF4D * c.pixels[cin - s1] + CF4C * c.pixels[cin] + CF4B * c.pixels[cin + s1] + CF4A * c.pixels[cin + s2])
              cout += w
              cin += s1
              y -= 1
          cin += s1
          out[cout] = CF(CF3A * c.pixels[cin] + CF3B * c.pixels[cin - s1] + CF3C * c.pixels[cin - s2])
          cout += w
          out[cout] = CF(CF3X * c.pixels[cin] + CF3Y * c.pixels[cin - s1] + CF3Z * c.pixels[cin - s2])
          cout += w
          out[cout] = CF(CF2A * c.pixels[cin] + CF2B * c.pixels[cin - s1])
      c._height <<= 1
      c.stride = c.width
      c.pixels = out
  
  #else
  
  # NJ_INLINE void njUpsample(nj_component_t* c) {
  #     int x, y, xshift = 0, yshift = 0;
  #     unsigned char *out, *lin, *lout;
  #     while (c->width < nj.width) { c->width <<= 1; ++xshift; }
  #     while (c->height < nj.height) { c->height <<= 1; ++yshift; }
  #     out = njAllocMem(c->width * c->height);
  #     if (!out) njThrow(NJ_OUT_OF_MEM);
  #     lin = c->pixels;
  #     lout = out;
  #     for (y = 0;  y < c->height;  ++y) {
  #         lin = &c->pixels[(y >> yshift) * c->stride];
  #         for (x = 0;  x < c->width;  ++x)
  #             lout[x] = lin[x >> xshift];
  #         lout += c->width;
  #     }
  #     c->stride = c->width;
  #     njFreeMem(c->pixels);
  #     c->pixels = out;
  # }
  
  #endif
  
  def njConvert():
      for i in range(nj.ncomp):
          c = nj.comp[i]
          if NJ_CHROMA_FILTER:
              while ((c.width < nj.width) or (c.height < nj.height)):
                  if c.width < nj.width: njUpsampleH(c)
                  if nj.error: return
                  if c.height < nj.height: njUpsampleV(c)
                  if nj.error: return
          else:
              if ((c.width < nj.width) or (c.height < nj.height)):
                  njUpsample(c)
          if ((c.width < nj.width) or (c.height < nj.height)):
              raise Exception(NJ_INTERNAL_ERR)
              return
      if nj.ncomp == 3:
          # convert to RGB
          prgb = 0
          py  = 0
          pcb = 0
          pcr = 0
          yy = nj.height
          #print( 'nj.width: %s, nj.height: %s, strides: %s, %s, %s, lengths: %s, %s, %s, first few bytes: %s, %s, %s' % \
          #    (nj.width, nj.height, nj.comp[0].stride, nj.comp[1].stride, nj.comp[2].stride,
          #    len(nj.comp[0].pixels), len(nj.comp[1].pixels), len(nj.comp[2].pixels),
          #    nj.comp[0].pixels[0], nj.comp[0].pixels[1], nj.comp[0].pixels[2]))
          while yy:
              for x in range(nj.width):
                  y = nj.comp[0].pixels[py + x] << 8
                  # print 'len(nj.comp): %s, len(nj.comp[1].pixels): %s, pcb + x: %s, yy: %s, x: %s' % \
                  #     (len(nj.comp), len(nj.comp[1].pixels), pcb + x, yy, x)
                  cb = nj.comp[1].pixels[pcb + x] - 128
                  cr = nj.comp[2].pixels[pcr + x] - 128
                  nj.rgb[prgb] = njClip((y            + 359 * cr + 128) >> 8)
                  prgb += 1
                  nj.rgb[prgb] = njClip((y -  88 * cb - 183 * cr + 128) >> 8)
                  prgb += 1
                  nj.rgb[prgb] = njClip((y + 454 * cb            + 128) >> 8)
                  prgb += 1
              py += nj.comp[0].stride
              pcb += nj.comp[1].stride
              pcr += nj.comp[2].stride
              yy -= 1
      elif (nj.comp[0].width != nj.comp[0].stride):
          # grayscale -> only remove stride
          # unsigned char *pin = &nj.comp[0].pixels[nj.comp[0].stride];
          # unsigned char *pout = &nj.comp[0].pixels[nj.comp[0].width];
          # int y;
          # for (y = nj.comp[0].height - 1;  y;  --y) {
          #     njCopyMem(pout, pin, nj.comp[0].width);
          #     pin += nj.comp[0].stride;
          #     pout += nj.comp[0].width;
          # }
          # nj.comp[0].stride = nj.comp[0].width;
          pass
  
  def njInit():
      # njFillMem(&nj, 0, sizeof(nj_context_t));
      nj.init()
  
  def njDone():
      pass
  
  def njDecode(jpeg, size):
      njDone()
      nj.spos = jpeg
      nj.pos = 0
      nj.size = size & 0x7FFFFFFF
      if (nj.size < 2): return NJ_NO_JPEG
      if ((nj.spos[nj.pos] ^ 0xFF) | (nj.spos[nj.pos + 1] ^ 0xD8)): return NJ_NO_JPEG
      njSkip(2)
      while not nj.error:
          if ((nj.size < 2) or (nj.spos[nj.pos] != 0xFF)):
              return NJ_SYNTAX_ERROR
          njSkip(2)
          m = nj.spos[nj.pos - 1]
          if m == 0xC0: njDecodeSOF()
          elif m == 0xC4: njDecodeDHT()
          elif m == 0xDB: njDecodeDQT()
          elif m == 0xDD: njDecodeDRI()
          elif m == 0xDA: njDecodeScan()
          elif m == 0xFE: njSkipMarker()
          elif (m & 0xF0) == 0xE0:
              njSkipMarker()
          else:
              return NJ_UNSUPPORTED
      if (nj.error != __NJ_FINISHED): return nj.error
      nj.error = NJ_OK
      njConvert()
      return nj.error
  
  
  def njGetWidth():
      return nj.width
  def njGetHeight():
      return nj.height
  def njIsColor():
      return (nj.ncomp != 1)
  def njGetImage():
      return nj.comp[0].pixels if nj.ncomp == 1 else nj.rgb
  def njGetImageSize():
      return nj.width * nj.height * nj.ncomp
  
  #endif // _NJ_INCLUDE_HEADER_ONLY
  