/*
 * The Python Imaging Library.
 * $Id$
 *
 * decoder for JPEG2000 image data.
 *
 * history:
 * 2014-03-12 ajh  Created
 *
 * Copyright (c) 2014 Coriolis Systems Limited
 * Copyright (c) 2014 Alastair Houghton
 *
 * See the README file for details on usage and redistribution.
 */

#include "Imaging.h"

#ifdef HAVE_OPENJPEG

#include <stdlib.h>
#include "Jpeg2K.h"

typedef struct {
    OPJ_UINT32 tile_index;
    OPJ_UINT32 data_size;
    OPJ_INT32  x0, y0, x1, y1;
    OPJ_UINT32 nb_comps;
} JPEG2KTILEINFO;

/* -------------------------------------------------------------------- */
/* Error handler                                                        */
/* -------------------------------------------------------------------- */

static void
j2k_error(const char *msg, void *client_data)
{
    JPEG2KDECODESTATE *state = (JPEG2KDECODESTATE *) client_data;
    free((void *)state->error_msg);
    state->error_msg = strdup(msg);
}

/* -------------------------------------------------------------------- */
/* Buffer input stream                                                  */
/* -------------------------------------------------------------------- */

static OPJ_SIZE_T
j2k_read(void *p_buffer, OPJ_SIZE_T p_nb_bytes, void *p_user_data)
{
    ImagingCodecState state = (ImagingCodecState)p_user_data;

    size_t len = _imaging_read_pyFd(state->fd, p_buffer, p_nb_bytes);

    return len ? len : (OPJ_SIZE_T)-1;
}

static OPJ_OFF_T
j2k_skip(OPJ_OFF_T p_nb_bytes, void *p_user_data)
{
    off_t pos;
    ImagingCodecState state = (ImagingCodecState)p_user_data;

    _imaging_seek_pyFd(state->fd, p_nb_bytes, SEEK_CUR);
    pos = _imaging_tell_pyFd(state->fd);

    return pos ? pos : (OPJ_OFF_T)-1;
}

/* -------------------------------------------------------------------- */
/* Unpackers                                                            */
/* -------------------------------------------------------------------- */

typedef void (*j2k_unpacker_t)(opj_image_t *in,
                               const JPEG2KTILEINFO *tileInfo,
                               const UINT8 *data,
                               Imaging im);

struct j2k_decode_unpacker {
    const char          *mode;
    OPJ_COLOR_SPACE     color_space;
    unsigned            components;
    j2k_unpacker_t      unpacker;
};

static inline
unsigned j2ku_shift(unsigned x, int n)
{
    if (n < 0)
        return x >> -n;
    else
        return x << n;
}

static void
j2ku_gray_l(opj_image_t *in, const JPEG2KTILEINFO *tileinfo,
            const UINT8 *tiledata, Imaging im)
{
    unsigned x0 = tileinfo->x0 - in->x0, y0 = tileinfo->y0 - in->y0;
    unsigned w = tileinfo->x1 - tileinfo->x0;
    unsigned h = tileinfo->y1 - tileinfo->y0;

    int shift = 8 - in->comps[0].prec;
    int offset = in->comps[0].sgnd ? 1 << (in->comps[0].prec - 1) : 0;
    int csiz = (in->comps[0].prec + 7) >> 3;

    unsigned x, y;

    if (csiz == 3)
        csiz = 4;

    if (shift < 0)
        offset += 1 << (-shift - 1);

    switch (csiz) {
    case 1:
        for (y = 0; y < h; ++y) {
            const UINT8 *data = &tiledata[y * w];
            UINT8 *row = (UINT8 *)im->image[y0 + y] + x0;
            for (x = 0; x < w; ++x)
                *row++ = j2ku_shift(offset + *data++, shift);
        }
        break;
    case 2:
        for (y = 0; y < h; ++y) {
            const UINT16 *data = (const UINT16 *)&tiledata[2 * y * w];
            UINT8 *row = (UINT8 *)im->image[y0 + y] + x0;
            for (x = 0; x < w; ++x)
                *row++ = j2ku_shift(offset + *data++, shift);
        }
        break;
    case 4:
        for (y = 0; y < h; ++y) {
            const UINT32 *data = (const UINT32 *)&tiledata[4 * y * w];
            UINT8 *row = (UINT8 *)im->image[y0 + y] + x0;
            for (x = 0; x < w; ++x)
                *row++ = j2ku_shift(offset + *data++, shift);
        }
        break;
    }
}


static void
j2ku_gray_i(opj_image_t *in, const JPEG2KTILEINFO *tileinfo,
            const UINT8 *tiledata, Imaging im)
{
    unsigned x0 = tileinfo->x0 - in->x0, y0 = tileinfo->y0 - in->y0;
    unsigned w = tileinfo->x1 - tileinfo->x0;
    unsigned h = tileinfo->y1 - tileinfo->y0;

    int shift = 16 - in->comps[0].prec;
    int offset = in->comps[0].sgnd ? 1 << (in->comps[0].prec - 1) : 0;
    int csiz = (in->comps[0].prec + 7) >> 3;

    unsigned x, y;

    if (csiz == 3)
        csiz = 4;

    if (shift < 0)
        offset += 1 << (-shift - 1);

    switch (csiz) {
    case 1:
        for (y = 0; y < h; ++y) {
            const UINT8 *data = &tiledata[y * w];
            UINT16 *row = (UINT16 *)im->image[y0 + y] + x0;
            for (x = 0; x < w; ++x)
                *row++ = j2ku_shift(offset + *data++, shift);
        }
        break;
    case 2:
        for (y = 0; y < h; ++y) {
            const UINT16 *data = (const UINT16 *)&tiledata[2 * y * w];
            UINT16 *row = (UINT16 *)im->image[y0 + y] + x0;
            for (x = 0; x < w; ++x)
                *row++ = j2ku_shift(offset + *data++, shift);
        }
        break;
    case 4:
        for (y = 0; y < h; ++y) {
            const UINT32 *data = (const UINT32 *)&tiledata[4 * y * w];
            UINT16 *row = (UINT16 *)im->image[y0 + y] + x0;
            for (x = 0; x < w; ++x)
                *row++ = j2ku_shift(offset + *data++, shift);
        }
        break;
    }
}


static void
j2ku_gray_rgb(opj_image_t *in, const JPEG2KTILEINFO *tileinfo,
              const UINT8 *tiledata, Imaging im)
{
    unsigned x0 = tileinfo->x0 - in->x0, y0 = tileinfo->y0 - in->y0;
    unsigned w = tileinfo->x1 - tileinfo->x0;
    unsigned h = tileinfo->y1 - tileinfo->y0;

    int shift = 8 - in->comps[0].prec;
    int offset = in->comps[0].sgnd ? 1 << (in->comps[0].prec - 1) : 0;
    int csiz = (in->comps[0].prec + 7) >> 3;

    unsigned x, y;

    if (shift < 0)
        offset += 1 << (-shift - 1);

    if (csiz == 3)
        csiz = 4;

    switch (csiz) {
    case 1:
        for (y = 0; y < h; ++y) {
            const UINT8 *data = &tiledata[y * w];
            UINT8 *row = (UINT8 *)im->image[y0 + y] + x0;
            for (x = 0; x < w; ++x) {
                UINT8 byte = j2ku_shift(offset + *data++, shift);
                row[0] = row[1] = row[2] = byte;
                row[3] = 0xff;
                row += 4;
            }
        }
        break;
    case 2:
        for (y = 0; y < h; ++y) {
            const UINT16 *data = (UINT16 *)&tiledata[2 * y * w];
            UINT8 *row = (UINT8 *)im->image[y0 + y] + x0;
            for (x = 0; x < w; ++x) {
                UINT8 byte = j2ku_shift(offset + *data++, shift);
                row[0] = row[1] = row[2] = byte;
                row[3] = 0xff;
                row += 4;
            }
        }
        break;
    case 4:
        for (y = 0; y < h; ++y) {
            const UINT32 *data = (UINT32 *)&tiledata[4 * y * w];
            UINT8 *row = (UINT8 *)im->image[y0 + y] + x0;
            for (x = 0; x < w; ++x) {
                UINT8 byte = j2ku_shift(offset + *data++, shift);
                row[0] = row[1] = row[2] = byte;
                row[3] = 0xff;
                row += 4;
            }
        }
        break;
    }
}

static void
j2ku_graya_la(opj_image_t *in, const JPEG2KTILEINFO *tileinfo,
              const UINT8 *tiledata, Imaging im)
{
    unsigned x0 = tileinfo->x0 - in->x0, y0 = tileinfo->y0 - in->y0;
    unsigned w = tileinfo->x1 - tileinfo->x0;
    unsigned h = tileinfo->y1 - tileinfo->y0;

    int shift = 8 - in->comps[0].prec;
    int offset = in->comps[0].sgnd ? 1 << (in->comps[0].prec - 1) : 0;
    int csiz = (in->comps[0].prec + 7) >> 3;
    int ashift = 8 - in->comps[1].prec;
    int aoffset = in->comps[1].sgnd ? 1 << (in->comps[1].prec - 1) : 0;
    int acsiz = (in->comps[1].prec + 7) >> 3;
    const UINT8 *atiledata;

    unsigned x, y;

    if (csiz == 3)
        csiz = 4;
    if (acsiz == 3)
        acsiz = 4;

    if (shift < 0)
        offset += 1 << (-shift - 1);
    if (ashift < 0)
        aoffset += 1 << (-ashift - 1);

    atiledata = tiledata + csiz * w * h;

    for (y = 0; y < h; ++y) {
        const UINT8 *data = &tiledata[csiz * y * w];
        const UINT8 *adata = &atiledata[acsiz * y * w];
        UINT8 *row = (UINT8 *)im->image[y0 + y] + x0 * 4;
        for (x = 0; x < w; ++x) {
            UINT32 word = 0, aword = 0, byte;

            switch (csiz) {
            case 1: word = *data++; break;
            case 2: word = *(const UINT16 *)data; data += 2; break;
            case 4: word = *(const UINT32 *)data; data += 4; break;
            }

            switch (acsiz) {
            case 1: aword = *adata++; break;
            case 2: aword = *(const UINT16 *)adata; adata += 2; break;
            case 4: aword = *(const UINT32 *)adata; adata += 4; break;
            }

            byte = j2ku_shift(offset + word, shift);
            row[0] = row[1] = row[2] = byte;
            row[3] = j2ku_shift(aoffset + aword, ashift);
            row += 4;
        }
    }
}

static void
j2ku_srgb_rgb(opj_image_t *in, const JPEG2KTILEINFO *tileinfo,
              const UINT8 *tiledata, Imaging im)
{
    unsigned x0 = tileinfo->x0 - in->x0, y0 = tileinfo->y0 - in->y0;
    unsigned w = tileinfo->x1 - tileinfo->x0;
    unsigned h = tileinfo->y1 - tileinfo->y0;

    int shifts[3], offsets[3], csiz[3];
    const UINT8 *cdata[3];
    const UINT8 *cptr = tiledata;
    unsigned n, x, y;

    for (n = 0; n < 3; ++n) {
        cdata[n] = cptr;
        shifts[n] = 8 - in->comps[n].prec;
        offsets[n] = in->comps[n].sgnd ? 1 << (in->comps[n].prec - 1) : 0;
        csiz[n] = (in->comps[n].prec + 7) >> 3;

        if (csiz[n] == 3)
            csiz[n] = 4;

        if (shifts[n] < 0)
            offsets[n] += 1 << (-shifts[n] - 1);

        cptr += csiz[n] * w * h;
    }

    for (y = 0; y < h; ++y) {
        const UINT8 *data[3];
        UINT8 *row = (UINT8 *)im->image[y0 + y] + x0 * 4;
        for (n = 0; n < 3; ++n)
            data[n] = &cdata[n][csiz[n] * y * w];

        for (x = 0; x < w; ++x) {
            for (n = 0; n < 3; ++n) {
                UINT32 word = 0;

                switch (csiz[n]) {
                case 1: word = *data[n]++; break;
                case 2: word = *(const UINT16 *)data[n]; data[n] += 2; break;
                case 4: word = *(const UINT32 *)data[n]; data[n] += 4; break;
                }

                row[n] = j2ku_shift(offsets[n] + word, shifts[n]);
            }
            row[3] = 0xff;
            row += 4;
        }
    }
}

static void
j2ku_sycc_rgb(opj_image_t *in, const JPEG2KTILEINFO *tileinfo,
              const UINT8 *tiledata, Imaging im)
{
    unsigned x0 = tileinfo->x0 - in->x0, y0 = tileinfo->y0 - in->y0;
    unsigned w = tileinfo->x1 - tileinfo->x0;
    unsigned h = tileinfo->y1 - tileinfo->y0;

    int shifts[3], offsets[3], csiz[3];
    const UINT8 *cdata[3];
    const UINT8 *cptr = tiledata;
    unsigned n, x, y;

    for (n = 0; n < 3; ++n) {
        cdata[n] = cptr;
        shifts[n] = 8 - in->comps[n].prec;
        offsets[n] = in->comps[n].sgnd ? 1 << (in->comps[n].prec - 1) : 0;
        csiz[n] = (in->comps[n].prec + 7) >> 3;

        if (csiz[n] == 3)
            csiz[n] = 4;

        if (shifts[n] < 0)
            offsets[n] += 1 << (-shifts[n] - 1);

        cptr += csiz[n] * w * h;
    }

    for (y = 0; y < h; ++y) {
        const UINT8 *data[3];
        UINT8 *row = (UINT8 *)im->image[y0 + y] + x0 * 4;
        UINT8 *row_start = row;
        for (n = 0; n < 3; ++n)
            data[n] = &cdata[n][csiz[n] * y * w];

        for (x = 0; x < w; ++x) {
            for (n = 0; n < 3; ++n) {
                UINT32 word = 0;

                switch (csiz[n]) {
                case 1: word = *data[n]++; break;
                case 2: word = *(const UINT16 *)data[n]; data[n] += 2; break;
                case 4: word = *(const UINT32 *)data[n]; data[n] += 4; break;
                }

                row[n] = j2ku_shift(offsets[n] + word, shifts[n]);
            }
            row[3] = 0xff;
            row += 4;
        }

        ImagingConvertYCbCr2RGB(row_start, row_start, w);
    }
}

static void
j2ku_srgba_rgba(opj_image_t *in, const JPEG2KTILEINFO *tileinfo,
                const UINT8 *tiledata, Imaging im)
{
    unsigned x0 = tileinfo->x0 - in->x0, y0 = tileinfo->y0 - in->y0;
    unsigned w = tileinfo->x1 - tileinfo->x0;
    unsigned h = tileinfo->y1 - tileinfo->y0;

    int shifts[4], offsets[4], csiz[4];
    const UINT8 *cdata[4];
    const UINT8 *cptr = tiledata;
    unsigned n, x, y;

    for (n = 0; n < 4; ++n) {
        cdata[n] = cptr;
        shifts[n] = 8 - in->comps[n].prec;
        offsets[n] = in->comps[n].sgnd ? 1 << (in->comps[n].prec - 1) : 0;
        csiz[n] = (in->comps[n].prec + 7) >> 3;

        if (csiz[n] == 3)
            csiz[n] = 4;

        if (shifts[n] < 0)
            offsets[n] += 1 << (-shifts[n] - 1);

        cptr += csiz[n] * w * h;
    }

    for (y = 0; y < h; ++y) {
        const UINT8 *data[4];
        UINT8 *row = (UINT8 *)im->image[y0 + y] + x0 * 4;
        for (n = 0; n < 4; ++n)
            data[n] = &cdata[n][csiz[n] * y * w];

        for (x = 0; x < w; ++x) {
            for (n = 0; n < 4; ++n) {
                UINT32 word = 0;

                switch (csiz[n]) {
                case 1: word = *data[n]++; break;
                case 2: word = *(const UINT16 *)data[n]; data[n] += 2; break;
                case 4: word = *(const UINT32 *)data[n]; data[n] += 4; break;
                }

                row[n] = j2ku_shift(offsets[n] + word, shifts[n]);
            }
            row += 4;
        }
    }
}

static void
j2ku_sycca_rgba(opj_image_t *in, const JPEG2KTILEINFO *tileinfo,
                const UINT8 *tiledata, Imaging im)
{
    unsigned x0 = tileinfo->x0 - in->x0, y0 = tileinfo->y0 - in->y0;
    unsigned w = tileinfo->x1 - tileinfo->x0;
    unsigned h = tileinfo->y1 - tileinfo->y0;

    int shifts[4], offsets[4], csiz[4];
    const UINT8 *cdata[4];
    const UINT8 *cptr = tiledata;
    unsigned n, x, y;

    for (n = 0; n < 4; ++n) {
        cdata[n] = cptr;
        shifts[n] = 8 - in->comps[n].prec;
        offsets[n] = in->comps[n].sgnd ? 1 << (in->comps[n].prec - 1) : 0;
        csiz[n] = (in->comps[n].prec + 7) >> 3;

        if (csiz[n] == 3)
            csiz[n] = 4;

        if (shifts[n] < 0)
            offsets[n] += 1 << (-shifts[n] - 1);

        cptr += csiz[n] * w * h;
    }

    for (y = 0; y < h; ++y) {
        const UINT8 *data[4];
        UINT8 *row = (UINT8 *)im->image[y0 + y] + x0 * 4;
        UINT8 *row_start = row;
        for (n = 0; n < 4; ++n)
            data[n] = &cdata[n][csiz[n] * y * w];

        for (x = 0; x < w; ++x) {
            for (n = 0; n < 4; ++n) {
                UINT32 word = 0;

                switch (csiz[n]) {
                case 1: word = *data[n]++; break;
                case 2: word = *(const UINT16 *)data[n]; data[n] += 2; break;
                case 4: word = *(const UINT32 *)data[n]; data[n] += 4; break;
                }

                row[n] = j2ku_shift(offsets[n] + word, shifts[n]);
            }
            row += 4;
        }

        ImagingConvertYCbCr2RGB(row_start, row_start, w);
    }
}

static const struct j2k_decode_unpacker j2k_unpackers[] = {
    { "L", OPJ_CLRSPC_GRAY, 1, j2ku_gray_l },
    { "I;16", OPJ_CLRSPC_GRAY, 1, j2ku_gray_i },
    { "I;16B", OPJ_CLRSPC_GRAY, 1, j2ku_gray_i },
    { "LA", OPJ_CLRSPC_GRAY, 2, j2ku_graya_la },
    { "RGB", OPJ_CLRSPC_GRAY, 1, j2ku_gray_rgb },
    { "RGB", OPJ_CLRSPC_GRAY, 2, j2ku_gray_rgb },
    { "RGB", OPJ_CLRSPC_SRGB, 3, j2ku_srgb_rgb },
    { "RGB", OPJ_CLRSPC_SYCC, 3, j2ku_sycc_rgb },
    { "RGB", OPJ_CLRSPC_SRGB, 4, j2ku_srgb_rgb },
    { "RGB", OPJ_CLRSPC_SYCC, 4, j2ku_sycc_rgb },
    { "RGBA", OPJ_CLRSPC_GRAY, 1, j2ku_gray_rgb },
    { "RGBA", OPJ_CLRSPC_GRAY, 2, j2ku_graya_la },
    { "RGBA", OPJ_CLRSPC_SRGB, 3, j2ku_srgb_rgb },
    { "RGBA", OPJ_CLRSPC_SYCC, 3, j2ku_sycc_rgb },
    { "RGBA", OPJ_CLRSPC_SRGB, 4, j2ku_srgba_rgba },
    { "RGBA", OPJ_CLRSPC_SYCC, 4, j2ku_sycca_rgba },
};

/* -------------------------------------------------------------------- */
/* Decoder                                                              */
/* -------------------------------------------------------------------- */

enum {
    J2K_STATE_START = 0,
    J2K_STATE_DECODING = 1,
    J2K_STATE_DONE = 2,
    J2K_STATE_FAILED = 3,
};

static int
j2k_decode_entry(Imaging im, ImagingCodecState state)
{
    JPEG2KDECODESTATE *context = (JPEG2KDECODESTATE *) state->context;
    opj_stream_t *stream = NULL;
    opj_image_t *image = NULL;
    opj_codec_t *codec = NULL;
    opj_dparameters_t params;
    OPJ_COLOR_SPACE color_space;
    j2k_unpacker_t unpack = NULL;
    size_t buffer_size = 0;
    unsigned n;

    stream = opj_stream_create(BUFFER_SIZE, OPJ_TRUE);

    if (!stream) {
        state->errcode = IMAGING_CODEC_BROKEN;
        state->state = J2K_STATE_FAILED;
        goto quick_exit;
    }

    opj_stream_set_read_function(stream, j2k_read);
    opj_stream_set_skip_function(stream, j2k_skip);

    /* OpenJPEG 2.0 doesn't have OPJ_VERSION_MAJOR */
#ifndef OPJ_VERSION_MAJOR
    opj_stream_set_user_data(stream, state);
#else
    opj_stream_set_user_data(stream, state, NULL);

    /* Hack: if we don't know the length, the largest file we can
       possibly support is 4GB.  We can't go larger than this, because
       OpenJPEG truncates this value for the final box in the file, and
       the box lengths in OpenJPEG are currently 32 bit. */
    if (context->length < 0)
        opj_stream_set_user_data_length(stream, 0xffffffff);
    else
        opj_stream_set_user_data_length(stream, context->length);
#endif

    /* Setup decompression context */
    context->error_msg = NULL;

    opj_set_default_decoder_parameters(&params);
    params.cp_reduce = context->reduce;
    params.cp_layer = context->layers;

    codec = opj_create_decompress(context->format);

    if (!codec) {
        state->errcode = IMAGING_CODEC_BROKEN;
        state->state = J2K_STATE_FAILED;
        goto quick_exit;
    }

    opj_set_error_handler(codec, j2k_error, context);
    opj_setup_decoder(codec, &params);

    if (!opj_read_header(stream, codec, &image)) {
        state->errcode = IMAGING_CODEC_BROKEN;
        state->state = J2K_STATE_FAILED;
        goto quick_exit;
    }

    /* Check that this image is something we can handle */
    if (image->numcomps < 1 || image->numcomps > 4
        || image->color_space == OPJ_CLRSPC_UNKNOWN) {
        state->errcode = IMAGING_CODEC_BROKEN;
        state->state = J2K_STATE_FAILED;
        goto quick_exit;
    }

    for (n = 1; n < image->numcomps; ++n) {
        if (image->comps[n].dx != 1 || image->comps[n].dy != 1) {
            state->errcode = IMAGING_CODEC_BROKEN;
            state->state = J2K_STATE_FAILED;
            goto quick_exit;
        }
    }

    /*
         Colorspace    Number of components    PIL mode
       ------------------------------------------------------
         sRGB          3                       RGB
         sRGB          4                       RGBA
         gray          1                       L or I
         gray          2                       LA
         YCC           3                       YCbCr


       If colorspace is unspecified, we assume:

           Number of components   Colorspace
         -----------------------------------------
           1                      gray
           2                      gray (+ alpha)
           3                      sRGB
           4                      sRGB (+ alpha)

    */

    /* Find the correct unpacker */
    color_space = image->color_space;

    if (color_space == OPJ_CLRSPC_UNSPECIFIED) {
        switch (image->numcomps) {
        case 1: case 2: color_space = OPJ_CLRSPC_GRAY; break;
        case 3: case 4: color_space = OPJ_CLRSPC_SRGB; break;
        }
    }

    for (n = 0; n < sizeof(j2k_unpackers) / sizeof (j2k_unpackers[0]); ++n) {
        if (color_space == j2k_unpackers[n].color_space
            && image->numcomps == j2k_unpackers[n].components
            && strcmp (im->mode, j2k_unpackers[n].mode) == 0) {
            unpack = j2k_unpackers[n].unpacker;
            break;
        }
    }

    if (!unpack) {
        state->errcode = IMAGING_CODEC_BROKEN;
        state->state = J2K_STATE_FAILED;
        goto quick_exit;
    }

    /* Decode the image tile-by-tile; this means we only need use as much
       memory as is required for one tile's worth of components. */
    for (;;) {
        JPEG2KTILEINFO tile_info;
        OPJ_BOOL should_continue;
        unsigned correction = (1 << params.cp_reduce) - 1;

        if (!opj_read_tile_header(codec,
                                  stream,
                                  &tile_info.tile_index,
                                  &tile_info.data_size,
                                  &tile_info.x0, &tile_info.y0,
                                  &tile_info.x1, &tile_info.y1,
                                  &tile_info.nb_comps,
                                  &should_continue)) {
            state->errcode = IMAGING_CODEC_BROKEN;
            state->state = J2K_STATE_FAILED;
            goto quick_exit;
        }

        if (!should_continue)
            break;

        /* Adjust the tile co-ordinates based on the reduction (OpenJPEG
           doesn't do this for us) */
        tile_info.x0 = (tile_info.x0 + correction) >> context->reduce;
        tile_info.y0 = (tile_info.y0 + correction) >> context->reduce;
        tile_info.x1 = (tile_info.x1 + correction) >> context->reduce;
        tile_info.y1 = (tile_info.y1 + correction) >> context->reduce;

        if (buffer_size < tile_info.data_size) {
            /* malloc check ok, tile_info.data_size from openjpeg */
            UINT8 *new = realloc (state->buffer, tile_info.data_size);
            if (!new) {
                state->errcode = IMAGING_CODEC_MEMORY;
                state->state = J2K_STATE_FAILED;
                goto quick_exit;
            }
            state->buffer = new;
            buffer_size = tile_info.data_size;
        }

        if (!opj_decode_tile_data(codec,
                                  tile_info.tile_index,
                                  (OPJ_BYTE *)state->buffer,
                                  tile_info.data_size,
                                  stream)) {
            state->errcode = IMAGING_CODEC_BROKEN;
            state->state = J2K_STATE_FAILED;
            goto quick_exit;
        }

        /* Check the tile bounds; if the tile is outside the image area,
           or if it has a negative width or height (i.e. the coordinates are
           swapped), bail. */
        if (tile_info.x0 >= tile_info.x1
            || tile_info.y0 >= tile_info.y1
            || tile_info.x0 < image->x0
            || tile_info.y0 < image->y0
            || tile_info.x1 - image->x0 > im->xsize
            || tile_info.y1 - image->y0 > im->ysize) {
            state->errcode = IMAGING_CODEC_BROKEN;
            state->state = J2K_STATE_FAILED;
            goto quick_exit;
        }

        unpack(image, &tile_info, state->buffer, im);
    }

    if (!opj_end_decompress(codec, stream)) {
        state->errcode = IMAGING_CODEC_BROKEN;
        state->state = J2K_STATE_FAILED;
        goto quick_exit;
    }

    state->state = J2K_STATE_DONE;
    state->errcode = IMAGING_CODEC_END;

    if (context->pfile) {
        if(fclose(context->pfile)){
            context->pfile = NULL;
        }
    }

 quick_exit:
    if (codec)
        opj_destroy_codec(codec);
    if (image)
        opj_image_destroy(image);
    if (stream)
        opj_stream_destroy(stream);

    return -1;
}

int
ImagingJpeg2KDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
{

    if (bytes){
        state->errcode = IMAGING_CODEC_BROKEN;
        state->state = J2K_STATE_FAILED;
        return -1;
    }

    if (state->state == J2K_STATE_DONE || state->state == J2K_STATE_FAILED)
        return -1;

    if (state->state == J2K_STATE_START) {
        state->state = J2K_STATE_DECODING;

        return j2k_decode_entry(im, state);
    }

    if (state->state == J2K_STATE_DECODING) {
        state->errcode = IMAGING_CODEC_BROKEN;
        state->state = J2K_STATE_FAILED;
        return -1;
    }
    return -1;
}

/* -------------------------------------------------------------------- */
/* Cleanup                                                              */
/* -------------------------------------------------------------------- */

int
ImagingJpeg2KDecodeCleanup(ImagingCodecState state) {
    JPEG2KDECODESTATE *context = (JPEG2KDECODESTATE *)state->context;

    if (context->error_msg) {
        free ((void *)context->error_msg);
    }

    context->error_msg = NULL;

    return -1;
}

const char *
ImagingJpeg2KVersion(void)
{
    return opj_version();
}

#endif /* HAVE_OPENJPEG */

/*
 * Local Variables:
 * c-basic-offset: 4
 * End:
 *
 */
