/*
 * File:	ximaj2k.cpp
 * Purpose:	Platform Independent J2K Image Class Loader and Writer
 * 12/Jul/2002 Davide Pizzolato - www.xdp.it
 * CxImage version 5.99c 17/Oct/2004
 */

#include "ximaj2k.h"

#if CXIMAGE_SUPPORT_J2K

#define CEILDIV(a,b) ((a+b-1)/b)

////////////////////////////////////////////////////////////////////////////////
bool CxImageJ2K::Decode(CxFile *hFile)
{
	if (hFile == NULL) return false;

  try
  {
	BYTE* src;
	long len;
	j2k_image_t *img=NULL;
	j2k_cp_t *cp=NULL;
	long i,x,y,w,h,max;

	len=hFile->Size();
	src=(BYTE*)malloc(len);
	hFile->Read(src, len, 1);

	if (!j2k_decode(src, len, &img, &cp)) {
		free(src);
		throw "failed to decode J2K image!";
	}

	free(src);

    if (img->numcomps==3 &&
		img->comps[0].dx==img->comps[1].dx &&
		img->comps[1].dx==img->comps[2].dx &&
		img->comps[0].dy==img->comps[1].dy &&
		img->comps[1].dy==img->comps[2].dy &&
		img->comps[0].prec==img->comps[1].prec &&
		img->comps[1].prec==img->comps[2].prec)
	{
        w=CEILDIV(img->x1-img->x0, img->comps[0].dx);
        h=CEILDIV(img->y1-img->y0, img->comps[0].dy);
        max=(1<<img->comps[0].prec)-1;

		Create(w,h,24,CXIMAGE_FORMAT_J2K);

		RGBQUAD c;
        for (i=0,y=0; y<h; y++) {
			for (x=0; x<w; x++,i++){
				c.rgbRed   = img->comps[0].data[i];
				c.rgbGreen = img->comps[1].data[i];
				c.rgbBlue  = img->comps[2].data[i];
				SetPixelColor(x,h-1-y,c);
			}
		}
	} else {
		int compno;
		info.nNumFrames = img->numcomps;
		if ((info.nFrame<0)||(info.nFrame>=info.nNumFrames)){
			j2k_destroy(&img,&cp);
			throw "wrong frame!";
		}
		for (compno=0; compno<=info.nFrame; compno++) {
			w=CEILDIV(img->x1-img->x0, img->comps[compno].dx);
			h=CEILDIV(img->y1-img->y0, img->comps[compno].dy);
			max=(1<<img->comps[compno].prec)-1;
			Create(w,h,8,CXIMAGE_FORMAT_J2K);
			SetGrayPalette();
			for (i=0,y=0; y<h; y++) {
				for (x=0; x<w; x++,i++){
					SetPixelIndex(x,h-1-y,img->comps[compno].data[i]);
				}
			}
		}
	}

	j2k_destroy(&img,&cp);

  } catch (char *message) {
	strncpy(info.szLastError,message,255);
	return FALSE;
  }
	return true;
}
////////////////////////////////////////////////////////////////////////////////
bool CxImageJ2K::Encode(CxFile * hFile)
{
	if (EncodeSafeCheck(hFile)) return false;

	if (head.biClrUsed!=0 && !IsGrayScale()){
		strcpy(info.szLastError,"J2K can save only RGB or GrayScale images");
		return false;
	}

    int i,x,y;
    j2k_image_t *img;
    j2k_cp_t *cp;
    j2k_tcp_t *tcp;
    j2k_tccp_t *tccp;

	img = (j2k_image_t *)calloc(sizeof(j2k_image_t),1);
	cp = (j2k_cp_t *)calloc(sizeof(j2k_cp_t),1);

    cp->tx0=0; cp->ty0=0;
    cp->tw=1; cp->th=1;
    cp->tcps=(j2k_tcp_t*)calloc(sizeof(j2k_tcp_t),1);
    tcp=&cp->tcps[0];

	long w=head.biWidth;
	long h=head.biHeight;
 
	tcp->numlayers=1;
	for (i=0;i<tcp->numlayers;i++) tcp->rates[i]=(w*h*GetJpegQuality())/600;


    if (IsGrayScale()) {
        img->x0=0;
		img->y0=0;
		img->x1=w;
		img->y1=h;
        img->numcomps=1;
        img->comps=(j2k_comp_t*)calloc(sizeof(j2k_comp_t),1);
        img->comps[0].data=(int*)calloc(w*h*sizeof(int),1);
        img->comps[0].prec=8;
        img->comps[0].sgnd=0;
        img->comps[0].dx=1;
        img->comps[0].dy=1;
		for (i=0,y=0; y<h; y++) {
			for (x=0; x<w; x++,i++){
				img->comps[0].data[i]=GetPixelIndex(x,h-1-y);
			}
		}
    } else if (!IsIndexed()) {
        img->x0=0;
		img->y0=0;
		img->x1=w;
		img->y1=h;
        img->numcomps=3;
        img->comps=(j2k_comp_t*)calloc(img->numcomps*sizeof(j2k_comp_t),1);
        for (i=0; i<img->numcomps; i++) {
            img->comps[i].data=(int*)calloc(w*h*sizeof(int),1);
            img->comps[i].prec=8;
            img->comps[i].sgnd=0;
            img->comps[i].dx=1;
            img->comps[i].dy=1;
        }
		RGBQUAD c;
        for (i=0,y=0; y<h; y++) {
			for (x=0; x<w; x++,i++){
				c=GetPixelColor(x,h-1-y);
				img->comps[0].data[i]=c.rgbRed;
				img->comps[1].data[i]=c.rgbGreen;
				img->comps[2].data[i]=c.rgbBlue;
			}
		}
    } else {
        return 0;
    }
	
    cp->tdx=img->x1-img->x0;
	cp->tdy=img->y1-img->y0;

    tcp->csty=0;
    tcp->prg=0;
    tcp->mct=img->numcomps==3?1:0;
    tcp->tccps=(j2k_tccp_t*)calloc(img->numcomps*sizeof(j2k_tccp_t),1);

    int ir=0; /* or 1 ???*/

    for (i=0; i<img->numcomps; i++) {
        tccp=&tcp->tccps[i];
        tccp->csty=0;
        tccp->numresolutions=6;
        tccp->cblkw=6;
        tccp->cblkh=6;
        tccp->cblksty=0;
        tccp->qmfbid=ir?0:1;
        tccp->qntsty=ir?J2K_CCP_QNTSTY_SEQNT:J2K_CCP_QNTSTY_NOQNT;
        tccp->numgbits=2;
        tccp->roishift=0;
        j2k_calc_explicit_stepsizes(tccp, img->comps[i].prec);
    }

    BYTE* dest=(BYTE*)calloc(tcp->rates[tcp->numlayers-1]+2,1);
    long len = j2k_encode(img, cp, dest, tcp->rates[tcp->numlayers-1]+2);

    if (len==0) {
		strcpy(info.szLastError,"J2K failed to encode image");
    } else {
		hFile->Write(dest, len, 1);
	}
	
	free(dest);
	j2k_destroy(&img,&cp);

	return (len!=0);
}
////////////////////////////////////////////////////////////////////////////////

static const double dwt_norms_97[4][10]={
    {1.000, 1.965, 4.177, 8.403, 16.90, 33.84, 67.69, 135.3, 270.6, 540.9},
    {2.022, 3.989, 8.355, 17.04, 34.27, 68.63, 137.3, 274.6, 549.0},
    {2.022, 3.989, 8.355, 17.04, 34.27, 68.63, 137.3, 274.6, 549.0},
    {2.080, 3.865, 8.307, 17.18, 34.71, 69.59, 139.3, 278.6, 557.2}
};

////////////////////////////////////////////////////////////////////////////////
void CxImageJ2K::j2k_calc_explicit_stepsizes(j2k_tccp_t *tccp, int prec) {
    int numbands, bandno;
    numbands=3*tccp->numresolutions-2;
    for (bandno=0; bandno<numbands; bandno++) {
        double stepsize;

        int resno, level, orient, gain;
        resno=bandno==0?0:(bandno-1)/3+1;
        orient=bandno==0?0:(bandno-1)%3+1;
        level=tccp->numresolutions-1-resno;
        gain=tccp->qmfbid==0?0:(orient==0?0:(orient==1||orient==2?1:2));
        if (tccp->qntsty==J2K_CCP_QNTSTY_NOQNT) {
            stepsize=1.0;
        } else {
            double norm=dwt_norms_97[orient][level];
            stepsize=(1<<(gain+1))/norm;
        }
        j2k_encode_stepsize((int)floor(stepsize*8192.0), prec+gain, &tccp->stepsizes[bandno].expn, &tccp->stepsizes[bandno].mant);
    }
}
////////////////////////////////////////////////////////////////////////////////
void CxImageJ2K::j2k_encode_stepsize(int stepsize, int numbps, int *expn, int *mant) {
    int p, n;
    p=j2k_floorlog2(stepsize)-13;
    n=11-j2k_floorlog2(stepsize);
    *mant=(n<0?stepsize>>-n:stepsize<<n)&0x7ff;
    *expn=numbps-p;
}
////////////////////////////////////////////////////////////////////////////////
int CxImageJ2K::j2k_floorlog2(int a) {
    int l;
    for (l=0; a>1; l++) {
        a>>=1;
    }
    return l;
}
////////////////////////////////////////////////////////////////////////////////
#endif 	// CXIMAGE_SUPPORT_J2K
