/* code adapted from mtpaint - GPLv3 */

#ifdef HAVE_OPENJPEG

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
#include <openjpeg.h>

#include "image.h"
#include "rwTable.h"


/* *** PREFACE ***
 * OpenJPEG 1.x is wasteful in the extreme, with memory overhead of about
 * 7 times the unpacked image size. So it can fail to handle even such
 * resolutions that fit into available memory with lots of room to spare.
 * Still, JasPer is an even worse memory hog, if a somewhat faster one.
 * Another thing - Linux builds of OpenJPEG cannot properly encode an opacity
 * channel (fixed in SVN on 06.11.09, revision 541)
 * And JP2 images with 4 channels, produced by OpenJPEG, cause JasPer
 * to die horribly - WJ */

extern void * xmalloc(size_t n);
extern void set_xlate(unsigned char *xlat, int bpp);

static void stupid_callback(const char *msg, void *client_data)
{
}

int
TestJP2K(char *file)
{
    unsigned char buf[8];
    FILE *fd = fopen(file, "r");
    int ret = 0;

    if (fd == NULL)
	return 0;

    if (6 == fread(buf, sizeof(char), 6, fd)) {
        if (buf[0] == 0xFF && buf[1] == 0x4F)
	    ret = 1;
	if (buf[0] == 0x00 && buf[1] == 0x00 &&
            buf[2] == 0x00 && buf[3] == 0x0c &&
            buf[4] == 0x6A && buf[5] == 0x50)
	    ret = 1;
    }
    fclose(fd);

    return ret;
}

Image *
ReadJP2K(char *file_name)
{
	opj_dparameters_t par;
	opj_dinfo_t *dinfo;
	opj_cio_t *cio = NULL;
	opj_image_t *image = NULL;
	opj_image_comp_t *comp;
	opj_event_mgr_t useless_events; // !!! Silently made mandatory in v1.2
	unsigned char xtb[256], *dest, *buf = NULL;
	FILE *fp;
        Image *outImage;
	int i, j, k, l, w, h, w0, nc, step, delta, shift, bpp;
	int *src, codec = CODEC_JP2;

	if ((fp = fopen(file_name, "rb")) == NULL) return (void *)(-1);

	/* Read in the entire file */
	fseek(fp, 0, SEEK_END);
	l = ftell(fp);
	fseek(fp, 0, SEEK_SET);
	buf = malloc(l);
	if (!buf) goto ffail;
	i = fread(buf, 1, l, fp);
	if (i < l) goto ffail;
	fclose(fp);
	if ((buf[0] == 0xFF) && (buf[1] == 0x4F)) codec = CODEC_J2K;

	/* Decompress it */
	dinfo = opj_create_decompress(codec);
	if (!dinfo) goto lfail;
	memset(&useless_events, 0, sizeof(useless_events));
	useless_events.error_handler = useless_events.warning_handler =
		useless_events.info_handler = stupid_callback;
	opj_set_event_mgr((opj_common_ptr)dinfo, &useless_events, stderr);
	opj_set_default_decoder_parameters(&par);
	opj_setup_decoder(dinfo, &par);
	cio = opj_cio_open((opj_common_ptr)dinfo, buf, l);
	if (!cio) goto lfail;
	image = opj_decode(dinfo, cio);
	opj_cio_close(cio);
	opj_destroy_decompress(dinfo);
	free(buf);
	if (!image) goto ifail;
	
	/* Analyze what we got */
        nc = image->numcomps;
	comp = image->comps;
        if (nc < 3)
	    bpp = 1;
        else
	    bpp = 3;
        /* printf("bpp = %d comps=%d\n", image->numcomps, comp); */
	w = (comp->w + (1 << comp->factor) - 1) >> comp->factor;
	h = (comp->h + (1 << comp->factor) - 1) >> comp->factor;

	for (i = 1; i < nc; i++) /* Check if all components are the same size */
	{
		comp++;
		if ((w != (comp->w + (1 << comp->factor) - 1) >> comp->factor) ||
		    (h != (comp->h + (1 << comp->factor) - 1) >> comp->factor))
			goto ifail;
	}

        if (bpp == 1)
            outImage = ImageNewGrey(w, h);
        else
            outImage = ImageNew(w, h);
        if (!outImage) goto ifail;

	/* Unpack data */
	for (i = 0, comp = image->comps; i < nc; i++ , comp++)
	{
		if (i < bpp) /* Image */
		{
			dest = outImage->data + i;
			step = bpp;
		}
		else /* Alpha */
		{
		        if (!outImage->alpha) 
			    outImage->alpha = xmalloc(w*h);
		        dest = outImage->alpha;
			if (!dest) break; /* No alpha allocated */
			step = 1;
		}
		w0 = comp->w;
		delta = comp->sgnd ? 1 << (comp->prec - 1) : 0;
		shift = comp->prec > 8 ? comp->prec - 8 : 0;
		set_xlate(xtb, comp->prec - shift);
		for (j = 0; j < h; j++)
		{
			src = comp->data + j * w0;
			for (k = 0; k < w; k++)
			{
				*dest = xtb[(src[k] + delta) >> shift];
				dest += step;
			}
		}
	}
        opj_image_destroy(image);
        return outImage;

ifail:	opj_image_destroy(image);
	return NULL;
lfail:	opj_destroy_decompress(dinfo);
	free(buf);
	return NULL;
ffail:	free(buf);
	fclose(fp);
	return NULL;
}

int 
WriteJP2K(char *file_name, Image * inImage)
{
	opj_cparameters_t par;
	opj_cinfo_t *cinfo;
	opj_image_cmptparm_t channels[4];
	opj_cio_t *cio = NULL;
	opj_image_t *image;
	opj_event_mgr_t useless_events; // !!! Silently made mandatory in v1.2
	unsigned char *src;
	FILE *fp;
	int i, j, k, nc, step;
	int *dest, w, h, res = 1;
        int jp2k_mode, jp2k_rate;

	if ((fp = fopen(file_name, "wb")) == NULL) return 1;

	/* Create intermediate structure */
	nc = inImage->alpha ? 4 : 3;
        w = inImage->width;
        h = inImage->height;
        jp2k_mode = 1;
        jp2k_rate = 8;

	memset(channels, 0, sizeof(channels));
	for (i = 0; i < nc; i++)
	{
		channels[i].prec = channels[i].bpp = 8;
		channels[i].dx = channels[i].dy = 1;
		channels[i].w = w;
		channels[i].h = h;
	}
	image = opj_image_create(nc, channels, CLRSPC_SRGB);
	if (!image) goto ffail;
	image->x0 = image->y0 = 0;
	image->x1 = w; image->y1 = h;
        

	/* Fill it */
	k = w * h;
	for (i = 0; i < nc; i++)
	{
		if (i < 3)
		{
			src = inImage->data + i;
			step = 3;
		}
		else
		{
			src = inImage->alpha;
			step = 1;
		}
		dest = image->comps[i].data;
		for (j = 0; j < k; j++ , src += step) dest[j] = *src;
	}

	/* Compress it */
	cinfo = opj_create_compress((jp2k_mode)? CODEC_JP2 : CODEC_J2K);
	if (!cinfo) goto fail;
	memset(&useless_events, 0, sizeof(useless_events));
	useless_events.error_handler = useless_events.warning_handler =
		useless_events.info_handler = stupid_callback;
	opj_set_event_mgr((opj_common_ptr)cinfo, &useless_events, stderr);
	opj_set_default_encoder_parameters(&par);
	par.tcp_numlayers = 1;
	par.tcp_rates[0] = jp2k_rate;
	par.cp_disto_alloc = 1;
	opj_setup_encoder(cinfo, &par, image);
	cio = opj_cio_open((opj_common_ptr)cinfo, NULL, 0);
	if (!cio) goto fail;
	if (!opj_encode(cinfo, cio, image, NULL)) goto fail;

	/* Write it */
	k = cio_tell(cio);
	if (fwrite(cio->buffer, 1, k, fp) == k) res = 0;

fail:	if (cio) opj_cio_close(cio);
	opj_destroy_compress(cinfo);
	opj_image_destroy(image);
ffail:	fclose(fp);
	return (res);
}
#endif

#ifdef HAVE_JASPER

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/stat.h>
#include <jasper.h>

#include "image.h"
#include "rwTable.h"

/* *** PREFACE ***
 * JasPer is QUITE a memory waster, with peak memory usage nearly TEN times the
 * unpacked image size. But what is worse, its API is 99% undocumented.
 * And to add insult to injury, it reacts to some invalid JP2 files (4-channel
 * ones written by OpenJPEG) by abort()ing, instead of returning error - WJ */

static int jasper_init;

/* Build bitdepth translation table */
void set_xlate(unsigned char *xlat, int bpp)
{
	int i, j, m, n = (1 << bpp) - 1;

	for (i = 0 , j = n , m = n + n; i <= n; i++ , j += 255 * 2)
		xlat[i] = j / m;
}

int
TestJP2K(char *file)
{
    unsigned char buf[8];
    FILE *fd = fopen(file, "r");
    int ret = 0;

    if (fd == NULL)
	return 0;

    if (6 == fread(buf, sizeof(char), 6, fd)) {
        if (buf[0] == 0xFF && buf[1] == 0x4F)
	    ret = 1;
	if (buf[0] == 0x00 && buf[1] == 0x00 &&
            buf[2] == 0x00 && buf[3] == 0x0c &&
            buf[4] == 0x6A && buf[5] == 0x50)
	    ret = 1;
    }
    fclose(fd);

    return ret;
}

Image *
ReadJP2K(char *file_name)
{
	jas_image_t *img;
	jas_stream_t *inp;
	jas_matrix_t *mx;
	jas_seqent_t *src;
        Image * inImage;
	char *fmt;
	unsigned char xtb[256], *dest;
	int nc, cspace, mode, slots[4];
	int bits, shift, delta, chan, step;
	int i, j, k, n, w, h, bpp;
        int jp2k_mode, jp2k_rate;

	/* Init the dumb library */
	if (!jasper_init) jas_init();
	jasper_init = 1;
        jp2k_mode = 1;
        jp2k_rate = 8;

	/* Open the file */
	inp = jas_stream_fopen(file_name, "rb");
	if (!inp) return NULL;
	/* Validate format */
	fmt = jas_image_fmttostr(jas_image_getfmt(inp));
	if (!fmt || strcmp(fmt, (jp2k_mode)? "jp2" : "jpc"))
		goto ffail;

	/* Decode the file into a halfbaked pile of bytes */
	img = jas_image_decode(inp, -1, NULL);
	jas_stream_close(inp);
	if (!img) goto dfail;
	/* Analyze the pile's contents */
	nc = jas_image_numcmpts(img);
	mode = jas_clrspc_fam(cspace = jas_image_clrspc(img));
	if (mode == JAS_CLRSPC_FAM_GRAY) bpp = 1;
	else if (mode == JAS_CLRSPC_FAM_RGB) bpp = 3;
	else goto ifail;
	if (bpp == 3)
	{
		slots[0] = jas_image_getcmptbytype(img,
			JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_RGB_R));
		slots[1] = jas_image_getcmptbytype(img,
			JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_RGB_G));
		slots[2] = jas_image_getcmptbytype(img,
			JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_RGB_B));
		if ((slots[1] < 0) | (slots[2] < 0)) goto ifail;
	}
	else
	{
		slots[0] = jas_image_getcmptbytype(img,
			JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_GRAY_Y));
	}
	if (slots[0] < 0) goto ifail;
	if (nc > bpp)
	{
		slots[bpp] = jas_image_getcmptbytype(img, JAS_IMAGE_CT_OPACITY);
/* !!! JasPer has a bug - it doesn't write out component definitions if color
 * channels are in natural order, thus losing the types of any extra components.
 * (See where variable "needcdef" in src/libjasper/jp2/jp2_enc.c gets unset.)
 * Then on reading, type will be replaced by component's ordinal number - WJ */
		if (slots[bpp] < 0) slots[bpp] = jas_image_getcmptbytype(img, bpp);
		/* Use an unlabeled extra component for alpha if no labeled one */
		if (slots[bpp] < 0)
			slots[bpp] = jas_image_getcmptbytype(img, JAS_IMAGE_CT_UNKNOWN);
		nc = bpp + (slots[bpp] >= 0); // Ignore extra channels if no alpha
	}
	w = jas_image_cmptwidth(img, slots[0]);
	h = jas_image_cmptheight(img, slots[0]);
	for (i = 1; i < nc; i++) /* Check if all components are the same size */
	{
		if ((jas_image_cmptwidth(img, slots[i]) != w) ||
			(jas_image_cmptheight(img, slots[i]) != h)) goto ifail;
	}

	/* Allocate "matrix" */
	mx = jas_matrix_create(1, w);
	if (!mx) goto ifail;
	/* Allocate image */
        if (bpp == 1)
            inImage = ImageNewGrey(w, h);
        else
            inImage = ImageNew(w, h);        
        if (!inImage) goto mfail;

	if (nc > bpp) inImage->alpha = xmalloc(w*h);
#if U_LCMS
	/* JasPer implements CMS internally, but without lcms, it makes no sense
	 * to provide all the interface stuff for this one rare format - WJ */
	while ((bpp == 3) && (cspace != JAS_CLRSPC_SRGB))
	{
		jas_cmprof_t *prof;
		jas_image_t *timg;

		prof = jas_cmprof_createfromclrspc(JAS_CLRSPC_SRGB);
		if (!prof) break;
		timg = jas_image_chclrspc(img, prof, JAS_CMXFORM_INTENT_PER);
		jas_cmprof_destroy(prof);
		if (!timg) break;
		jas_image_destroy(img);
		img = timg;
		break;
	}
#endif

	/* Unravel the ugly thing into proper format */
	for (i = n = 0; i < nc; i++)
	{
		if (i < bpp) /* Image */
		{
			dest = inImage->data + i;
			step = bpp;
		}
		else /* Alpha */
		{
			dest = inImage->alpha;
			step = 1;
		}
		chan = slots[i];
		bits = jas_image_cmptprec(img, chan);
		delta = jas_image_cmptsgnd(img, chan) ? 1 << (bits - 1) : 0;
		shift = bits > 8 ? bits - 8 : 0;
		set_xlate(xtb, bits - shift);
		for (j = 0; j < h; j++ , n++)
		{
			jas_image_readcmpt(img, chan, 0, j, w, 1, mx);
			src = jas_matrix_getref(mx, 0, 0);
			for (k = 0; k < w; k++)
			{
				*dest = xtb[(unsigned)(src[k] + delta) >> shift];
				dest += step;
			}
		}
	}
        jas_image_destroy(img);
        return inImage;

mfail:	jas_matrix_destroy(mx);
ifail:	jas_image_destroy(img);
dfail:	return NULL;
ffail:	jas_stream_close(inp);
	return NULL;
}

int 
WriteJP2K(char *file_name, Image * inImage)
{
	static const jas_image_cmpttype_t chans[4] = {
		JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_RGB_R),
		JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_RGB_G),
		JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_RGB_B),
		JAS_IMAGE_CT_OPACITY };
	jas_image_cmptparm_t cp[4];
	jas_image_t *img;
	jas_stream_t *outp;
	jas_matrix_t *mx;
	jas_seqent_t *dest;
	char buf[256], *opts = NULL;
	unsigned char *src;
	int w , h, res = 1;
	int i, j, k, n, nc, step, bpp = 3;
        int jp2k_mode, jp2k_rate;

	/* Init the dumb library */
	if (!jasper_init) jas_init();
	jasper_init = 1;
        w = inImage->width;
        h = inImage->height;
        jp2k_mode = 1;
        jp2k_rate = 8;

	/* Open the file */
	outp = jas_stream_fopen(file_name, "wb");
	if (!outp) return 1;
	/* Setup component parameters */
	memset(cp, 0, sizeof(cp)); // Zero out all that needs zeroing
	cp[0].hstep = cp[0].vstep = 1;
	cp[0].width = w; cp[0].height = h;
	cp[0].prec = 8;
	cp[3] = cp[2] = cp[1] = cp[0];
	/* Create image structure */
	nc = (inImage->alpha)? 4 : 3;
	img = jas_image_create(nc, cp, JAS_CLRSPC_SRGB);
	if (!img) goto fail;
	/* Allocate "matrix" */
	mx = jas_matrix_create(1, w);
	if (!mx) goto fail2;

	/* Fill image structure */
	for (i = n = 0; i < nc; i++)
	{
	/* !!! The only workaround for JasPer losing extra components' types on
	 * write is to reorder the RGB components - but then, dumb readers, such
	 * as ones in Mozilla and GTK+, would read them in wrong order - WJ */
		jas_image_setcmpttype(img, i, chans[i]);
		if (i < 3) /* Image */
		{
			src = inImage->data + i;
			step = bpp;
		}
		else /* Alpha */
		{
			src = inImage->alpha;
			step = 1;
		}
		for (j = 0; j < h; j++ , n++)
		{
			dest = jas_matrix_getref(mx, 0, 0);
			for (k = 0; k < w; k++)
			{
				dest[k] = *src;
				src += step;
			}
			jas_image_writecmpt(img, i, 0, j, w, 1, mx);
		}
	}

	/* Compress it */
	if (jp2k_rate) // Lossless if NO "rate" option passed
		sprintf(opts = buf, "rate=%g", 1.0 / jp2k_rate);
	if (!jas_image_encode(img, outp, jas_image_strtofmt(
		jp2k_mode ? "jp2" : "jpc"), opts)) res = 0;
	jas_stream_flush(outp);

fail3:	jas_matrix_destroy(mx);
fail2:	jas_image_destroy(img);
fail:	jas_stream_close(outp);
	return (res);
}
#endif

#ifdef HAVE_OPENJP2
/*
 * The copyright in this software is being made available under the 2-clauses
 * BSD License, included below. This software may be subject to other third
 * party and contributor rights, including patent rights, and no such rights
 * are granted under this license.
 *
 * Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium
 * Copyright (c) 2002-2014, Professor Benoit Macq
 * Copyright (c) 2001-2003, David Janssens
 * Copyright (c) 2002-2003, Yannick Verschueren
 * Copyright (c) 2003-2007, Francois-Olivier Devaux
 * Copyright (c) 2003-2014, Antonin Descampe
 * Copyright (c) 2005, Herve Drolon, FreeImage Team
 * Copyright (c) 2006-2007, Parvatha Elangovan
 * Copyright (c) 2008, 2011-2012, Centre National d'Etudes Spatiales (CNES), FR
 * Copyright (c) 2012, CS Systemes d'Information, France
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>

#include "openjpeg.h"

#include "image.h"
#include "rwTable.h"

#define J2K_CFMT 0
#define JP2_CFMT 1
#define JPT_CFMT 2

#define JP2_RFC3745_MAGIC "\x00\x00\x00\x0c\x6a\x50\x20\x20\x0d\x0a\x87\x0a"
#define JP2_MAGIC "\x0d\x0a\x87\x0a"
/* position 45: "\xff\x52" */
#define J2K_CODESTREAM_MAGIC "\xff\x4f\xff\x51"

#ifndef opj_image_data_alloc
#define opj_image_data_alloc malloc
#endif
#ifndef opj_image_data_free
#define opj_image_data_free free
#endif

static int infile_format(const char *fname)
{
    FILE *reader;
    const char *magic_s;
    unsigned char buf[12];
    OPJ_SIZE_T l_nb_read;
    int decod_format; 

    reader = fopen(fname, "rb");

    if (reader == NULL) {
        return -2;
    }

    memset(buf, 0, 12);
    l_nb_read = fread(buf, 1, 12, reader);
    fclose(reader);
    if (l_nb_read != 12) {
	return -1;
    }

    if (memcmp(buf, JP2_RFC3745_MAGIC, 12) == 0 ||
	memcmp(buf, JP2_MAGIC, 4) == 0) {
        decod_format = JP2_CFMT;
        magic_s = ".jp2";
    } else if (memcmp(buf, J2K_CODESTREAM_MAGIC, 4) == 0) {
        decod_format = J2K_CFMT;
        magic_s = ".j2k or .jpc or .j2c";
    } else {
        decod_format = -1;
    }
    return decod_format;
}

int
TestJP2K(char *file)
{
    return (infile_format(file)>=0);
}

/* -------------------------------------------------------------------------- */

/**
sample error callback expecting a FILE* client object
*/
static void error_callback(const char *msg, void *client_data)
{
/*
    (void)client_data;
    fprintf(stdout, "[ERROR] %s", msg); */
}
/**
sample warning callback expecting a FILE* client object
*/
static void warning_callback(const char *msg, void *client_data)
{
/*
    (void)client_data;
    fprintf(stdout, "[WARNING] %s", msg); */
}
/**
sample debug callback expecting no client object
*/
static void info_callback(const char *msg, void *client_data)
{
/*
    (void)client_data;  
    fprintf(stdout, "[INFO] %s", msg); */
}

static void sycc_to_rgb(int offset, int upb, int y, int cb, int cr,
                        int *out_r, int *out_g, int *out_b)
{
    int r, g, b;

    cb -= offset;
    cr -= offset;
    r = y + (int)(1.402 * (float)cr);
    if (r < 0) {
        r = 0;
    } else if (r > upb) {
        r = upb;
    }
    *out_r = r;

    g = y - (int)(0.344 * (float)cb + 0.714 * (float)cr);
    if (g < 0) {
        g = 0;
    } else if (g > upb) {
        g = upb;
    }
    *out_g = g;

    b = y + (int)(1.772 * (float)cb);
    if (b < 0) {
        b = 0;
    } else if (b > upb) {
        b = upb;
    }
    *out_b = b;
}

static void sycc444_to_rgb(opj_image_t *img)
{
    int *d0, *d1, *d2, *r, *g, *b;
    const int *y, *cb, *cr;
    size_t maxw, maxh, max, i;
    int offset, upb;

    upb = (int)img->comps[0].prec;
    offset = 1 << (upb - 1);
    upb = (1 << upb) - 1;

    maxw = (size_t)img->comps[0].w;
    maxh = (size_t)img->comps[0].h;
    max = maxw * maxh;

    y = img->comps[0].data;
    cb = img->comps[1].data;
    cr = img->comps[2].data;

    d0 = r = (int*)opj_image_data_alloc(sizeof(int) * max);
    d1 = g = (int*)opj_image_data_alloc(sizeof(int) * max);
    d2 = b = (int*)opj_image_data_alloc(sizeof(int) * max);

    if (r == NULL || g == NULL || b == NULL) {
        goto fails;
    }

    for (i = 0U; i < max; ++i) {
        sycc_to_rgb(offset, upb, *y, *cb, *cr, r, g, b);
        ++y;
        ++cb;
        ++cr;
        ++r;
        ++g;
        ++b;
    }
    opj_image_data_free(img->comps[0].data);
    img->comps[0].data = d0;
    opj_image_data_free(img->comps[1].data);
    img->comps[1].data = d1;
    opj_image_data_free(img->comps[2].data);
    img->comps[2].data = d2;
    img->color_space = OPJ_CLRSPC_SRGB;
    return;

fails:
    opj_image_data_free(r);
    opj_image_data_free(g);
    opj_image_data_free(b);
}/* sycc444_to_rgb() */

static void sycc422_to_rgb(opj_image_t *img)
{
    int *d0, *d1, *d2, *r, *g, *b;
    const int *y, *cb, *cr;
    size_t maxw, maxh, max, offx, loopmaxw;
    int offset, upb;
    size_t i;

    upb = (int)img->comps[0].prec;
    offset = 1 << (upb - 1);
    upb = (1 << upb) - 1;

    maxw = (size_t)img->comps[0].w;
    maxh = (size_t)img->comps[0].h;
    max = maxw * maxh;

    y = img->comps[0].data;
    cb = img->comps[1].data;
    cr = img->comps[2].data;

    d0 = r = (int*)opj_image_data_alloc(sizeof(int) * max);
    d1 = g = (int*)opj_image_data_alloc(sizeof(int) * max);
    d2 = b = (int*)opj_image_data_alloc(sizeof(int) * max);

    if (r == NULL || g == NULL || b == NULL) {
        goto fails;
    }

    /* if img->x0 is odd, then first column shall use Cb/Cr = 0 */
    offx = img->x0 & 1U;
    loopmaxw = maxw - offx;

    for (i = 0U; i < maxh; ++i) {
        size_t j;

        if (offx > 0U) {
            sycc_to_rgb(offset, upb, *y, 0, 0, r, g, b);
            ++y;
            ++r;
            ++g;
            ++b;
        }

        for (j = 0U; j < (loopmaxw & ~(size_t)1U); j += 2U) {
            sycc_to_rgb(offset, upb, *y, *cb, *cr, r, g, b);
            ++y;
            ++r;
            ++g;
            ++b;
            sycc_to_rgb(offset, upb, *y, *cb, *cr, r, g, b);
            ++y;
            ++r;
            ++g;
            ++b;
            ++cb;
            ++cr;
        }
        if (j < loopmaxw) {
            sycc_to_rgb(offset, upb, *y, *cb, *cr, r, g, b);
            ++y;
            ++r;
            ++g;
            ++b;
            ++cb;
            ++cr;
        }
    }

    opj_image_data_free(img->comps[0].data);
    img->comps[0].data = d0;
    opj_image_data_free(img->comps[1].data);
    img->comps[1].data = d1;
    opj_image_data_free(img->comps[2].data);
    img->comps[2].data = d2;

    img->comps[1].w = img->comps[2].w = img->comps[0].w;
    img->comps[1].h = img->comps[2].h = img->comps[0].h;
    img->comps[1].dx = img->comps[2].dx = img->comps[0].dx;
    img->comps[1].dy = img->comps[2].dy = img->comps[0].dy;
    img->color_space = OPJ_CLRSPC_SRGB;
    return;

fails:
    opj_image_data_free(r);
    opj_image_data_free(g);
    opj_image_data_free(b);
}/* sycc422_to_rgb() */

static void sycc420_to_rgb(opj_image_t *img)
{
    int *d0, *d1, *d2, *r, *g, *b, *nr, *ng, *nb;
    const int *y, *cb, *cr, *ny;
    size_t maxw, maxh, max, offx, loopmaxw, offy, loopmaxh;
    int offset, upb;
    size_t i;

    upb = (int)img->comps[0].prec;
    offset = 1 << (upb - 1);
    upb = (1 << upb) - 1;

    maxw = (size_t)img->comps[0].w;
    maxh = (size_t)img->comps[0].h;
    max = maxw * maxh;

    y = img->comps[0].data;
    cb = img->comps[1].data;
    cr = img->comps[2].data;

    d0 = r = (int*)opj_image_data_alloc(sizeof(int) * max);
    d1 = g = (int*)opj_image_data_alloc(sizeof(int) * max);
    d2 = b = (int*)opj_image_data_alloc(sizeof(int) * max);

    if (r == NULL || g == NULL || b == NULL) {
        goto fails;
    }

    /* if img->x0 is odd, then first column shall use Cb/Cr = 0 */
    offx = img->x0 & 1U;
    loopmaxw = maxw - offx;
    /* if img->y0 is odd, then first line shall use Cb/Cr = 0 */
    offy = img->y0 & 1U;
    loopmaxh = maxh - offy;

    if (offy > 0U) {
        size_t j;

        for (j = 0; j < maxw; ++j) {
            sycc_to_rgb(offset, upb, *y, 0, 0, r, g, b);
            ++y;
            ++r;
            ++g;
            ++b;
        }
    }

    for (i = 0U; i < (loopmaxh & ~(size_t)1U); i += 2U) {
        size_t j;

        ny = y + maxw;
        nr = r + maxw;
        ng = g + maxw;
        nb = b + maxw;

        if (offx > 0U) {
            sycc_to_rgb(offset, upb, *y, 0, 0, r, g, b);
            ++y;
            ++r;
            ++g;
            ++b;
            sycc_to_rgb(offset, upb, *ny, *cb, *cr, nr, ng, nb);
            ++ny;
            ++nr;
            ++ng;
            ++nb;
        }

        for (j = 0; j < (loopmaxw & ~(size_t)1U); j += 2U) {
            sycc_to_rgb(offset, upb, *y, *cb, *cr, r, g, b);
            ++y;
            ++r;
            ++g;
            ++b;
            sycc_to_rgb(offset, upb, *y, *cb, *cr, r, g, b);
            ++y;
            ++r;
            ++g;
            ++b;

            sycc_to_rgb(offset, upb, *ny, *cb, *cr, nr, ng, nb);
            ++ny;
            ++nr;
            ++ng;
            ++nb;
            sycc_to_rgb(offset, upb, *ny, *cb, *cr, nr, ng, nb);
            ++ny;
            ++nr;
            ++ng;
            ++nb;
            ++cb;
            ++cr;
        }
        if (j < loopmaxw) {
            sycc_to_rgb(offset, upb, *y, *cb, *cr, r, g, b);
            ++y;
            ++r;
            ++g;
            ++b;

            sycc_to_rgb(offset, upb, *ny, *cb, *cr, nr, ng, nb);
            ++ny;
            ++nr;
            ++ng;
            ++nb;
            ++cb;
            ++cr;
        }
        y += maxw;
        r += maxw;
        g += maxw;
        b += maxw;
    }
    if (i < loopmaxh) {
        size_t j;

        for (j = 0U; j < (maxw & ~(size_t)1U); j += 2U) {
            sycc_to_rgb(offset, upb, *y, *cb, *cr, r, g, b);

            ++y;
            ++r;
            ++g;
            ++b;

            sycc_to_rgb(offset, upb, *y, *cb, *cr, r, g, b);

            ++y;
            ++r;
            ++g;
            ++b;
            ++cb;
            ++cr;
        }
        if (j < maxw) {
            sycc_to_rgb(offset, upb, *y, *cb, *cr, r, g, b);
        }
    }

    opj_image_data_free(img->comps[0].data);
    img->comps[0].data = d0;
    opj_image_data_free(img->comps[1].data);
    img->comps[1].data = d1;
    opj_image_data_free(img->comps[2].data);
    img->comps[2].data = d2;

    img->comps[1].w = img->comps[2].w = img->comps[0].w;
    img->comps[1].h = img->comps[2].h = img->comps[0].h;
    img->comps[1].dx = img->comps[2].dx = img->comps[0].dx;
    img->comps[1].dy = img->comps[2].dy = img->comps[0].dy;
    img->color_space = OPJ_CLRSPC_SRGB;
    return;

fails:
    opj_image_data_free(r);
    opj_image_data_free(g);
    opj_image_data_free(b);
}/* sycc420_to_rgb() */

void color_sycc_to_rgb(opj_image_t *img)
{
    if (img->numcomps < 3) {
        img->color_space = OPJ_CLRSPC_GRAY;
        return;
    }

    if ((img->comps[0].dx == 1)
            && (img->comps[1].dx == 2)
            && (img->comps[2].dx == 2)
            && (img->comps[0].dy == 1)
            && (img->comps[1].dy == 2)
            && (img->comps[2].dy == 2)) { /* horizontal and vertical sub-sample */
        sycc420_to_rgb(img);
    } else if ((img->comps[0].dx == 1)
               && (img->comps[1].dx == 2)
               && (img->comps[2].dx == 2)
               && (img->comps[0].dy == 1)
               && (img->comps[1].dy == 1)
               && (img->comps[2].dy == 1)) { /* horizontal sub-sample only */
        sycc422_to_rgb(img);
    } else if ((img->comps[0].dx == 1)
               && (img->comps[1].dx == 1)
               && (img->comps[2].dx == 1)
               && (img->comps[0].dy == 1)
               && (img->comps[1].dy == 1)
               && (img->comps[2].dy == 1)) { /* no sub-sample */
        sycc444_to_rgb(img);
    } else {
        fprintf(stderr, "color_sycc_to_rgb failed\n");
        return;
    }
}/* color_sycc_to_rgb() */

static int are_comps_same_dimensions(opj_image_t * image)
{
    unsigned int i;
    for (i = 1; i < image->numcomps; i++) {
        if (image->comps[0].dx != image->comps[i].dx ||
                image->comps[0].dy != image->comps[i].dy) {
            return OPJ_FALSE;
        }
    }
    return OPJ_TRUE;
}

void color_cmyk_to_rgb(opj_image_t *image)
{
    float C, M, Y, K;
    float sC, sM, sY, sK;
    unsigned int w, h, max, i;

    w = image->comps[0].w;
    h = image->comps[0].h;

    if (
        (image->numcomps < 4)
        || (image->comps[0].dx != image->comps[1].dx) ||
        (image->comps[0].dx != image->comps[2].dx) ||
        (image->comps[0].dx != image->comps[3].dx)
        || (image->comps[0].dy != image->comps[1].dy) ||
        (image->comps[0].dy != image->comps[2].dy) ||
        (image->comps[0].dy != image->comps[3].dy)
    ) {
        fprintf(stderr, "color_cmyk_to_rgb failed\n");
        return;
    }

    max = w * h;

    sC = 1.0F / (float)((1 << image->comps[0].prec) - 1);
    sM = 1.0F / (float)((1 << image->comps[1].prec) - 1);
    sY = 1.0F / (float)((1 << image->comps[2].prec) - 1);
    sK = 1.0F / (float)((1 << image->comps[3].prec) - 1);

    for (i = 0; i < max; ++i) {
        /* CMYK values from 0 to 1 */
        C = (float)(image->comps[0].data[i]) * sC;
        M = (float)(image->comps[1].data[i]) * sM;
        Y = (float)(image->comps[2].data[i]) * sY;
        K = (float)(image->comps[3].data[i]) * sK;

        /* Invert all CMYK values */
        C = 1.0F - C;
        M = 1.0F - M;
        Y = 1.0F - Y;
        K = 1.0F - K;

        /* CMYK -> RGB : RGB results from 0 to 255 */
        image->comps[0].data[i] = (int)(255.0F * C * K); /* R */
        image->comps[1].data[i] = (int)(255.0F * M * K); /* G */
        image->comps[2].data[i] = (int)(255.0F * Y * K); /* B */
    }

    opj_image_data_free(image->comps[3].data);
    image->comps[3].data = NULL;
    image->comps[0].prec = 8;
    image->comps[1].prec = 8;
    image->comps[2].prec = 8;
    image->numcomps -= 1;
    image->color_space = OPJ_CLRSPC_SRGB;

    for (i = 3; i < image->numcomps; ++i) {
        memcpy(&(image->comps[i]), &(image->comps[i + 1]), sizeof(image->comps[i]));
    }

}/* color_cmyk_to_rgb() */

/*
 * This code has been adopted from sjpx_openjpeg.c of ghostscript
 */
void color_esycc_to_rgb(opj_image_t *image)
{
    int y, cb, cr, sign1, sign2, val;
    unsigned int w, h, max, i;
    int flip_value = (1 << (image->comps[0].prec - 1));
    int max_value = (1 << image->comps[0].prec) - 1;

    if (
        (image->numcomps < 3)
        || (image->comps[0].dx != image->comps[1].dx) ||
        (image->comps[0].dx != image->comps[2].dx)
        || (image->comps[0].dy != image->comps[1].dy) ||
        (image->comps[0].dy != image->comps[2].dy)
    ) {
        fprintf(stderr, "color_esycc_to_rgb failed\n");
        return;
    }

    w = image->comps[0].w;
    h = image->comps[0].h;

    sign1 = (int)image->comps[1].sgnd;
    sign2 = (int)image->comps[2].sgnd;

    max = w * h;

    for (i = 0; i < max; ++i) {

        y = image->comps[0].data[i];
        cb = image->comps[1].data[i];
        cr = image->comps[2].data[i];

        if (!sign1) {
            cb -= flip_value;
        }
        if (!sign2) {
            cr -= flip_value;
        }

        val = (int)
              ((float)y - (float)0.0000368 * (float)cb
               + (float)1.40199 * (float)cr + (float)0.5);

        if (val > max_value) {
            val = max_value;
        } else if (val < 0) {
            val = 0;
        }
        image->comps[0].data[i] = val;

        val = (int)
              ((float)1.0003 * (float)y - (float)0.344125 * (float)cb
               - (float)0.7141128 * (float)cr + (float)0.5);

        if (val > max_value) {
            val = max_value;
        } else if (val < 0) {
            val = 0;
        }
        image->comps[1].data[i] = val;

        val = (int)
              ((float)0.999823 * (float)y + (float)1.77204 * (float)cb
               - (float)0.000008 * (float)cr + (float)0.5);

        if (val > max_value) {
            val = max_value;
        } else if (val < 0) {
            val = 0;
        }
        image->comps[2].data[i] = val;
    }
    image->color_space = OPJ_CLRSPC_SRGB;

}/* color_esycc_to_rgb() */

int imagetopixels(opj_image_t * image, Image **outImage)
{
    int *red, *green, *blue, *alpha;
    int wr, hr, max;
    int i;
    unsigned int compno, ncomp;
    int adjustR, adjustG, adjustB, adjustA;
    int fails, two, want_gray, has_alpha, triple;
    int prec, v;
    unsigned char *data, *alphadata;

    alpha = NULL;

    for (i=0; i<image->numcomps; i++)    
    if ((prec = (int)image->comps[i].prec) > 16) {
         fprintf(stderr, "image precision %d is larger than 16 !!\n", prec);
         return 1;
    }
    for (i = 1; i < image->numcomps; i++) {
        if (image->comps[0].dx != image->comps[i].dx ||
                image->comps[0].dy != image->comps[i].dy ||
                (i <= 2 &&
                 (image->comps[0].prec != image->comps[i].prec ||
                  image->comps[0].sgnd != image->comps[i].sgnd))) {
            fprintf(stderr, "image has several components that are dissimilar !!\n");	  
            return 1;
        }
    }
    
    two = has_alpha = 0;
    fails = 1;
    ncomp = image->numcomps;

    if (ncomp >= 3) {
        two = (prec > 8);
        triple = (ncomp > 2);
        wr = (int)image->comps[0].w;
        hr = (int)image->comps[0].h;
	*outImage = ImageNew(wr, hr);
	data = (*outImage)->data;
        max = (1 << prec) - 1;
        has_alpha = (ncomp == 4);
#if DEBUG    	
	printf("prec=%d, ncomp=%d\n", prec, ncomp);	  
#endif	
        red = image->comps[0].data;

        if (triple) {
            green = image->comps[1].data;
            blue = image->comps[2].data;
        } else {
            green = blue = NULL;
        }

        if (has_alpha) {
	    alphadata = (unsigned char *)malloc(wr * hr);
	    (*outImage)->alpha = alphadata;
            alpha = image->comps[ncomp - 1].data;
            adjustA = (image->comps[ncomp - 1].sgnd ?
                       1 << (image->comps[ncomp - 1].prec - 1) : 0);
        } else {
            adjustA = 0;
        }
        adjustR = (image->comps[0].sgnd ? 1 << (image->comps[0].prec - 1) : 0);

        if (triple) {
            adjustG = (image->comps[1].sgnd ? 1 << (image->comps[1].prec - 1) : 0);
            adjustB = (image->comps[2].sgnd ? 1 << (image->comps[2].prec - 1) : 0);
        } else {
            adjustG = adjustB = 0;
        }

        for (i = 0; i < wr * hr; ++i) {
            if (two) {
                v = *red + adjustR;
                ++red;
                if (v > 65535) {
                    v = 65535;
                } else if (v < 0) {
                    v = 0;
                }

                *data++ = (unsigned char)(v >> 8);
                // *data++ = (unsigned char)v;
                if (triple) {
                    v = *green + adjustG;
                    ++green;
                    if (v > 65535) {
                        v = 65535;
                    } else if (v < 0) {
                        v = 0;
                    }

                    *data++ = (unsigned char)(v >> 8);
                    // *data++ = (unsigned char)v;

                    v =  *blue + adjustB;
                    ++blue;
                    if (v > 65535) {
                        v = 65535;
                    } else if (v < 0) {
                        v = 0;
                    }

                    *data++ = (unsigned char)(v >> 8);
                    // *data++ = (unsigned char)v;

                }/* if(triple) */

                if (has_alpha) {
                    v = *alpha + adjustA;
                    ++alpha;
                    if (v > 65535) {
                        v = 65535;
                    } else if (v < 0) {
                        v = 0;
                    }

                    *alphadata++ = (unsigned char)(v >> 8);
                    // *data++ = (unsigned char)v; 
                }
                continue;

            }   /* if(two) */

            /* prec <= 8: */
            v = *red++;
            if (v > 255) {
                v = 255;
            } else if (v < 0) {
                v = 0;
            }
            *data++ = (unsigned char)v;
	    
            if (triple) {
                v = *green++;
                if (v > 255) {
                    v = 255;
                } else if (v < 0) {
                    v = 0;
                }
                *data++ = (unsigned char)v;
		
                v = *blue++;
                if (v > 255) {
                    v = 255;
                } else if (v < 0) {
                    v = 0;
                }
                *data++ = (unsigned char)v;
            }
            if (has_alpha) {
                v = *alpha++;
                if (v > 255) {
                    v = 255;
                } else if (v < 0) {
                    v = 0;
                }
                *alphadata++ = (unsigned char)v;		
            }
        }   /* ncomp >= 3 */
	return 0;
    }

    /* ncomp <= 2, grey image */
    wr = (int)image->comps[0].w;
    hr = (int)image->comps[0].h;
    prec = (int)image->comps[0].prec;
    max = (1 << prec) - 1;
    has_alpha = (ncomp == 2);
    
    red = image->comps[0].data;
    if (!red) return 1;
    if (has_alpha) {
        alpha = image->comps[1].data;
        if (!alpha) has_alpha = 0;
    }
#if DEBUG
    printf("grey prec=%d alpha=%d\n", prec, has_alpha);
#endif
    
    *outImage = ImageNewGrey(wr, hr);
    data = (*outImage)->data;
    if (has_alpha) {
        alphadata = malloc(wr * hr);
	(*outImage)->alpha = alphadata;
    }
    
    adjustR =
        (image->comps[0].sgnd ? 1 << (image->comps[0].prec - 1) : 0);

    if (prec > 8) {
        for (i = 0; i < wr * hr; i++) {
            v = *red + adjustR;
            ++red;
            if (v > 65535) {
                v = 65535;
            } else if (v < 0) {
                v = 0;
            }
	    *data++ = (unsigned char)(v >> 8);

            if (has_alpha) {
                v = *alpha++;
                if (v > 65535) {
                    v = 65535;
                } else if (v < 0) {
                    v = 0;
                }
	        *alphadata++ = (unsigned char)(v >> 8);		
            }
        }/* prec > 8 */
    } else { /* prec <= 8 */
        for (i = 0; i < wr * hr; ++i) {
            v = *red + adjustR;
            ++red;
            if (v > 255) {
                v = 255;
            } else if (v < 0) {
                v = 0;
            }
	    *data++ = (unsigned char)v;
	    
            if (has_alpha)
	        *alphadata++ = (unsigned char)*alpha++;
        }
    }
        
    return 0;
}/* imagetopixels() */

opj_image_t* pixelstoimage(Image *inImage, opj_cparameters_t *parameters)
{
    int maxval=255, prec = 8;
    int i, ind, compno, numcomps, numcolors, w, h;
    OPJ_COLOR_SPACE color_space;
    opj_image_cmptparm_t cmptparm[4]; /* RGBA: max. 4 components */
    opj_image_t * image = NULL;
    unsigned char *data, *alpha, *cmap;

    if (!inImage || inImage->width == 0 || inImage->height == 0 ||
	!inImage->data) {
        return NULL;
    }

    numcolors = (inImage->isGrey)? 1 : 3;
    data = inImage->data;
    alpha = inImage->alpha;
    cmap = inImage->cmapData;
    numcomps = numcolors + (alpha!=NULL);

#if DEBUG    
    printf("numcolors=%d, numcomps=%d alpha=%lu cmap=%lu\n",
	   numcolors, numcomps, alpha, cmap);
#endif
	   
    if (numcomps < 3) {
        color_space = OPJ_CLRSPC_GRAY;    /* GRAY, GRAYA */
    } else {
        color_space = OPJ_CLRSPC_SRGB;    /* RGB, RGBA */
    }

    w = inImage->width;
    h = inImage->height;

    memset(&cmptparm[0], 0, (size_t)numcomps * sizeof(opj_image_cmptparm_t));

    for (i = 0; i < numcomps; i++) {
        cmptparm[i].prec = (OPJ_UINT32)prec;
        cmptparm[i].bpp = (OPJ_UINT32)prec;
        cmptparm[i].sgnd = 0;
        cmptparm[i].dx = 1;
        cmptparm[i].dy = 1;
        cmptparm[i].w = (OPJ_UINT32)w;
        cmptparm[i].h = (OPJ_UINT32)h;
    }
    
    image = opj_image_create((OPJ_UINT32)numcomps, &cmptparm[0], color_space);
    if (!image) {
        return NULL;
    }

    /* set image offset and reference grid (no tiles here ...) */
    image->x0 = 0;
    image->y0 = 0;
    image->x1 = w;
    image->y1 = h;

    if (alpha) {
        for (i = 0; i < w * h; i++)
	    image->comps[numcolors].data[i] = *alpha++;
    }

    if (cmap) {
        if (inImage->scale == 1)
	    for (i = 0; i < w * h; i++) {
	        ind = *data++; 	      
                for (compno = 0; compno < numcolors; compno++) {
		    image->comps[compno].data[i] = cmap[3*ind + compno];
		}
	    }
        if (inImage->scale == 2)
	    for (i = 0; i < w * h; i++) {
	        ind = *data++;
		ind = (ind << 8) | *data++;
                for (compno = 0; compno < numcolors; compno++) {
		    image->comps[compno].data[i] = cmap[3*ind + compno];
		}
	    }
    } else {
        for (i = 0; i < w * h; i++) {
            for (compno = 0; compno < numcolors; compno++) {
                image->comps[compno].data[i] = *data++;
            }
        }
    }

    return image;
} /* pixelstoimage() */

/* -------------------------------------------------------------------------- */
/**
 * OPJ_DECOMPRESS MAIN
 */
/* -------------------------------------------------------------------------- */

Image *
ReadJP2K(char *file_name)
{
    Image *outImage = NULL;  
    opj_dparameters_t core;  
    opj_image_t* image = NULL;
    opj_stream_t *l_stream = NULL;              /* Stream */
    opj_codec_t* l_codec = NULL;                /* Handle to a decompressor */
    opj_codestream_index_t* cstr_index = NULL;
    int decod_format;

    /* set decoding parameters to default values */
    opj_set_default_decoder_parameters(&core);
	
    /* read the input file and put it in memory */
    /* ---------------------------------------- */

    if (!file_name) return NULL;

    decod_format = infile_format(file_name);
    if (decod_format < 0) return NULL;
	
    l_stream = opj_stream_create_default_file_stream(file_name, 1);
    if (!l_stream) {
        fprintf(stderr, "ERROR -> failed to create the stream from the file %s\n",
                file_name);
        goto readend;
    }

    /* decode the JPEG2000 stream */
    /* ---------------------- */

    switch (decod_format) {
    case J2K_CFMT: { /* JPEG-2000 codestream */
        /* Get a decoder handle */
        l_codec = opj_create_decompress(OPJ_CODEC_J2K);
        break;
    }
    case JP2_CFMT: { /* JPEG 2000 compressed image data */
        /* Get a decoder handle */
        l_codec = opj_create_decompress(OPJ_CODEC_JP2);
        break;
    }
    case JPT_CFMT: { /* JPEG 2000, JPIP */
        /* Get a decoder handle */
        l_codec = opj_create_decompress(OPJ_CODEC_JPT);
        break;
    }
    default:
        fprintf(stderr, "format unknown: skipping file...\n");
        opj_stream_destroy(l_stream);
        break;
    }

    /* catch events using our callbacks and give a local context */
    opj_set_info_handler(l_codec, info_callback, NULL);
    opj_set_warning_handler(l_codec, warning_callback, NULL);
    opj_set_error_handler(l_codec, error_callback, NULL);

    /* Setup the decoder decoding parameters using user parameters */
    if (!opj_setup_decoder(l_codec, &core)) {
        fprintf(stderr, "ERROR -> opj_decompress: failed to setup the decoder\n");
        opj_stream_destroy(l_stream);
        opj_destroy_codec(l_codec);
        goto readend;
    }

    /* Read the main header of the codestream and if necessary the JP2 boxes*/
    if (! opj_read_header(l_stream, l_codec, &image)) {
      // fprintf(stderr, "ERROR -> opj_decompress: failed to read the header\n");
        opj_stream_destroy(l_stream);
        opj_destroy_codec(l_codec);
        opj_image_destroy(image);
        goto readend;
    }

    /* Get the decoded image */
    if (!(opj_decode(l_codec, l_stream, image) &&
          opj_end_decompress(l_codec,   l_stream))) {
        fprintf(stderr, "ERROR -> opj_decompress: failed to decode image!\n");
        opj_destroy_codec(l_codec);
        opj_stream_destroy(l_stream);
        opj_image_destroy(image);
        goto readend;
    }

    /* FIXME? Shouldn't that situation be considered as an error of */
    /* opj_decode() / opj_get_decoded_tile() ? */
    if (image->comps[0].data == NULL) {
        fprintf(stderr, "ERROR -> opj_decompress: no image data!\n");
        opj_destroy_codec(l_codec);
        opj_stream_destroy(l_stream);
        opj_image_destroy(image);
        goto readend;
    }

    /* Close the byte stream */
    opj_stream_destroy(l_stream);

    if (image->color_space != OPJ_CLRSPC_SYCC
        && image->numcomps == 3 && image->comps[0].dx == image->comps[0].dy
        && image->comps[1].dx != 1) {
        image->color_space = OPJ_CLRSPC_SYCC;
    } else if (image->numcomps <= 2) {
        image->color_space = OPJ_CLRSPC_GRAY;
    }

    if (image->color_space == OPJ_CLRSPC_SYCC) {
        color_sycc_to_rgb(image);
    } else if (image->color_space == OPJ_CLRSPC_CMYK) {
        color_cmyk_to_rgb(image);
    } else if (image->color_space == OPJ_CLRSPC_EYCC) {
        color_esycc_to_rgb(image);
    }
    
    /* ignore icc_profile !! */
    if (image->icc_profile_buf) {
        free(image->icc_profile_buf);
        image->icc_profile_buf = NULL;
        image->icc_profile_len = 0;
    }

    /* create output image */
    /* ------------------- */
    if (imagetopixels(image, &outImage)) {
        fprintf(stderr, "Decoding error for format JP2 format type",
		decod_format);
    }

    /* free remaining structures */
    if (l_codec) {
        opj_destroy_codec(l_codec);
    }


    /* free image data structure */
    opj_image_destroy(image);

    /* destroy the codestream index */
    opj_destroy_cstr_index(&cstr_index);

readend:
    return outImage;
}

int 
WriteJP2K(char *file_name, Image * inImage)
{
    opj_cparameters_t parameters;   /* compression parameters */    
    opj_stream_t *l_stream = NULL;
    opj_codec_t* l_codec = NULL;
    opj_image_t *image = NULL;
    OPJ_BOOL bSuccess;
    unsigned int i;
    int ret = 0;

    /* set encoding parameters to default values */
    opj_set_default_encoder_parameters(&parameters);
    parameters.cod_format = JP2_CFMT;

    image = pixelstoimage(inImage, &parameters);
    if (!image) {
        fprintf(stderr, "Unable to convert image\n");
        ret = 1;
        goto writeend;
    }	

    parameters.tcp_mct = (image->numcomps >= 3) ? 1 : 0;
    l_codec = opj_create_compress(OPJ_CODEC_JP2);

    /* catch events using our callbacks and give a local context */
    opj_set_info_handler(l_codec, info_callback, NULL);
    opj_set_warning_handler(l_codec, warning_callback, NULL);
    opj_set_error_handler(l_codec, error_callback, NULL);

    if (! opj_setup_encoder(l_codec, &parameters, image)) {
        fprintf(stderr, "failed to encode image: opj_setup_encoder\n");
        opj_destroy_codec(l_codec);
        opj_image_destroy(image);
        ret = 1;
        goto writeend;
    }

    /* open a byte stream for writing and allocate memory for all tiles */
    l_stream = opj_stream_create_default_file_stream(file_name, OPJ_FALSE);
    if (!l_stream) {
        ret = 1;
        goto writeend;
    }
    
    /* encode the image */
    bSuccess = opj_start_compress(l_codec, image, l_stream);
    if (!bSuccess)  {
        fprintf(stderr, "failed to encode image: opj_start_compress\n");
    }
    
    bSuccess = bSuccess && opj_encode(l_codec, l_stream);
    if (!bSuccess)  {
        fprintf(stderr, "failed to encode image: opj_encode\n");
    }
    
    bSuccess = bSuccess && opj_end_compress(l_codec, l_stream);
    if (!bSuccess)  {
        fprintf(stderr, "failed to encode image: opj_end_compress\n");
    }

    if (!bSuccess)  {
        opj_stream_destroy(l_stream);
        opj_destroy_codec(l_codec);
        opj_image_destroy(image);
        fprintf(stderr, "failed to encode image\n");
        remove(file_name);
        ret = 1;
        goto writeend;
    }

    opj_stream_destroy(l_stream);

    /* free remaining compression structures */
    opj_destroy_codec(l_codec);

    /* free image data */
    opj_image_destroy(image);

writeend:
    return ret;
}

#endif
