/*
 * A simple wrapper of JPEG decoder.
 *
 * Copyright (C) Hidenori TAKESHIMA <hidenori@a2.ctktv.ne.jp>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */


#include "config.h"

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

#include "ijgdec.h"

#if defined(HAVE_LIBJPEG) && defined(HAVE_JPEGLIB_H)

#include <jpeglib.h>
#include <jerror.h>
#include <setjmp.h>

typedef struct IJGSrcImpl IJGSrcImpl;
typedef struct IJGErrImpl IJGErrImpl;
struct IJGSrcImpl
{
	struct jpeg_source_mgr	pub;	/* must be first */

	const char** ppsrcs;
	const int* plenofsrcs;
	int srccount;
	int srcindex;
};

struct IJGErrImpl
{
	struct jpeg_error_mgr err;

	jmp_buf	env;
};


/* for the jpeg decompressor source manager. */
static void IJGDec_init_source(j_decompress_ptr cinfo) {}

static boolean IJGDec_fill_input_buffer(j_decompress_ptr cinfo)
{
	IJGSrcImpl* pImpl = (IJGSrcImpl*)cinfo->src;

	if ( pImpl->srcindex >= pImpl->srccount )
	{
		ERREXIT(cinfo, JERR_INPUT_EMPTY);
	}
	pImpl->pub.next_input_byte = pImpl->ppsrcs[pImpl->srcindex];
	pImpl->pub.bytes_in_buffer = pImpl->plenofsrcs[pImpl->srcindex];
	pImpl->srcindex ++;

	return TRUE;
}

static void IJGDec_skip_input_data(j_decompress_ptr cinfo,long num_bytes)
{
	IJGSrcImpl* pImpl = (IJGSrcImpl*)cinfo->src;

	if ( num_bytes <= 0 ) return;

	while ( num_bytes > pImpl->pub.bytes_in_buffer )
	{
		num_bytes -= pImpl->pub.bytes_in_buffer;
		if ( !IJGDec_fill_input_buffer(cinfo) )
		{
			ERREXIT(cinfo, JERR_INPUT_EMPTY);
		}
	}

	pImpl->pub.next_input_byte += num_bytes;
	pImpl->pub.bytes_in_buffer -= num_bytes;
}

static void IJGDec_term_source(j_decompress_ptr cinfo)
{
}

static void IJGDec_error_exit(j_common_ptr cinfo)
{
	IJGErrImpl* pImpl = (IJGErrImpl*)cinfo->err;

	longjmp(pImpl->env,1);
}

static void rgb_to_bgr(char* pdata,int width)
{
	int x;
	char c;

	for(x=0;x<width;x++)
	{
		c = pdata[0];
		pdata[0] = pdata[2];
		pdata[2] = c;
		pdata += 3;
	}
}

int IJGDEC_Decode( char* pdst, int dstpitch, int dstwidth, int dstheight, int dstbpp, const char** ppsrcs, const int* plenofsrcs, int srccount )
{
	IJGSrcImpl jsrc;
	IJGErrImpl jerr;
	struct jpeg_decompress_struct jdec;
	int ret = -1;

	jsrc.ppsrcs = ppsrcs;
	jsrc.plenofsrcs = plenofsrcs;
	jsrc.srccount = srccount;
	jsrc.srcindex = 0;
	jsrc.pub.bytes_in_buffer = 0;
	jsrc.pub.next_input_byte = NULL;
	jsrc.pub.init_source = IJGDec_init_source;
	jsrc.pub.fill_input_buffer = IJGDec_fill_input_buffer;
	jsrc.pub.skip_input_data = IJGDec_skip_input_data;
	jsrc.pub.resync_to_restart = jpeg_resync_to_restart;
	jsrc.pub.term_source = IJGDec_term_source;

	jdec.err = jpeg_std_error(&jerr.err);
	jerr.err.error_exit = IJGDec_error_exit;

	if ( setjmp(jerr.env) != 0 )
	{
		jpeg_destroy_decompress(&jdec);
		return -1;
	}

	jpeg_create_decompress(&jdec);
	jdec.src = &jsrc.pub;

	ret = jpeg_read_header(&jdec,TRUE);
	if ( ret != JPEG_HEADER_OK ) goto err;

	jpeg_start_decompress(&jdec);

	if ( jdec.output_width != dstwidth ||
		 jdec.output_height != dstheight ||
		 (jdec.output_components*8) != dstbpp ) goto err;

	while (jdec.output_scanline < jdec.output_height)
	{
		jpeg_read_scanlines(&jdec,(JSAMPLE**)&pdst,1);
		rgb_to_bgr(pdst,dstwidth);
		pdst += dstpitch;
	}

	jpeg_finish_decompress(&jdec);
	ret = 0;
err:
	jpeg_destroy_decompress(&jdec);

	return ret;
}

#else

int IJGDEC_Decode( char* pdst, int dstpitch, int dstwidth, int dstheight, int dstbpp, const char** ppsrcs, const int* plenofsrcs, int srccount )
{
	return -1;
}

#endif


