jarvisteach/appJar

View on GitHub
appJar/lib/nanojpeg.py

Summary

Maintainability
F
2 wks
Test Coverage
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