// jpeg.cpp : コンソール アプリケーション用のエントリ ポイントの定義
//

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

#define XMD_H
#undef FAR

#include "jpeglib.h"
#include "jmemorydatadst.h"

#include "jpeg.h"
#include "gif_rgb.h"

#ifndef RGB_PIXELSIZE
#define RGB_PIXELSIZE 4
#endif
/**********************************************************
				jpeg_compress
**********************************************************/
unsigned char * jpeg_compress(
	int * ret_jpeg_length,
	unsigned char * rgb_32bit_data,
	int image_width,
	int image_height,
	int quality)
{
	unsigned char *pCompressedData;
  /* This struct contains the JPEG compression parameters and pointers to
   * working space (which is allocated as needed by the JPEG library).
   * It is possible to have several such structures, representing multiple
   * compression/decompression processes, in existence at once.  We refer
   * to any one struct (and its associated working data) as a "JPEG object".
   */
  struct jpeg_compress_struct cinfo;
  /* This struct represents a JPEG error handler.  It is declared separately
   * because applications often want to supply a specialized error handler
   * (see the second half of this file for an example).  But here we just
   * take the easy way out and use the standard error handler, which will
   * print a message on stderr and call exit() if compression fails.
   * Note that this struct must live as long as the main JPEG parameter
   * struct, to avoid dangling-pointer problems.
   */
  struct jpeg_error_mgr jerr;
  /* More stuff */
  /* FILE * outfile; */		/* target file */
  JSAMPROW row_pointer[1];	/* pointer to JSAMPLE row[s] */
  int row_stride;		/* physical row width in image buffer */

  /* Step 1: allocate and initialize JPEG compression object */

  /* We have to set up the error handler first, in case the initialization
   * step fails.  (Unlikely, but it could happen if you are out of memory.)
   * This routine fills in the contents of struct jerr, and returns jerr's
   * address which we place into the link field in cinfo.
   */
  cinfo.err = jpeg_std_error(&jerr);
  /* Now we can initialize the JPEG compression object. */
  jpeg_create_compress(&cinfo);

  /* Step 2: specify data destination (eg, a file) */
  /* Note: steps 2 and 3 can be done in either order. */

  /* Here we use the library-supplied code to send compressed data to a
   * stdio stream.  You can also write your own code to do something else.
   * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that
   * requires it in order to write binary files.
   */
/*  if ((outfile = fopen(filename, "wb")) == NULL) {
    fprintf(stderr, "can't open %s\n", filename);
    exit(1);
  }
  */
  //jpeg_stdio_dest(&cinfo, outfile);
	jpeg_memory_dest(&cinfo, &pCompressedData);

  /* Step 3: set parameters for compression */

  /* First we supply a description of the input image.
   * Four fields of the cinfo struct must be filled in:
   */
  cinfo.image_width = image_width; 	/* image width and height, in pixels */
  cinfo.image_height = image_height;
  cinfo.input_components = RGB_PIXELSIZE;		/* # of color components per pixel */
  cinfo.in_color_space = JCS_RGB; 	/* colorspace of input image */
  /* Now use the library's routine to set default compression parameters.
   * (You must set at least cinfo.in_color_space before calling this,
   * since the defaults depend on the source color space.)
   */
  jpeg_set_defaults(&cinfo);
  /* Now you can set any non-default parameters you wish to.
   * Here we just illustrate the use of quality (quantization table) scaling:
   */
  //quality
  jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);

  /* Step 4: Start compressor */

  /* TRUE ensures that we will write a complete interchange-JPEG file.
   * Pass TRUE unless you are very sure of what you're doing.
   */
  jpeg_start_compress(&cinfo, TRUE);

  /* Step 5: while (scan lines remain to be written) */
  /*           jpeg_write_scanlines(...); */

  /* Here we use the library's state variable cinfo.next_scanline as the
   * loop counter, so that we don't have to keep track ourselves.
   * To keep things simple, we pass one scanline per call; you can pass
   * more if you wish, though.
   */
  row_stride = image_width * RGB_PIXELSIZE;	/* JSAMPLEs per row in image_buffer */

  while (cinfo.next_scanline < cinfo.image_height) {
    /* jpeg_write_scanlines expects an array of pointers to scanlines.
     * Here the array is only one element long, but you could pass
     * more than one scanline at a time if that's more convenient.
     */
    row_pointer[0] = & rgb_32bit_data[cinfo.next_scanline * row_stride];
    (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
  }
  
  *ret_jpeg_length = get_jpeg_compressed_size (&cinfo);

  /* Step 6: Finish compression */

  jpeg_finish_compress(&cinfo);
  /* After finish_compress, we can close the output file. */
/*  fclose(outfile); */

  /* Step 7: release JPEG compression object */

  /* This is an important step since it will release a good deal of memory. */
  jpeg_destroy_compress(&cinfo);

  /* And we're done! */
	return pCompressedData;
}


/**********************************************************
				jpeg_uncompress
**********************************************************/
typedef struct {
	int width;
	int height;
	int bpp;
	int num_palette;
	RGB4 *palette;
} LoadJPGInfo, *lpLoadJPGInfo;

struct my_error_mgr {
	struct jpeg_error_mgr pub;	/* "public" fields */
};

typedef struct my_error_mgr *my_error_ptr;

METHODDEF(void)
my_error_exit (j_common_ptr cinfo)
{
	/* cinfo->err really points to a my_error_mgr struct, so coerce pointer */
	my_error_ptr myerr = (my_error_ptr) cinfo->err;
/*	throw cinfo->err->msg_code;*/
}

/* Expanded data source object for stdio input */

typedef struct {
	struct jpeg_source_mgr pub;	/* public fields */

	void *stream;
	int size;
	int count;
	boolean start_of_file;	/* have we gotten any data yet? */
} mlibjpg_source_mgr;

typedef mlibjpg_source_mgr * mlibjpg_src_ptr;

METHODDEF(void)init_source (j_decompress_ptr cinfo)
{
	mlibjpg_src_ptr src = (mlibjpg_src_ptr) cinfo->src;
	src->start_of_file = TRUE;
}

METHODDEF(boolean)fill_input_buffer (j_decompress_ptr cinfo)
{
	mlibjpg_src_ptr src = (mlibjpg_src_ptr) cinfo->src;
	src->pub.next_input_byte = (unsigned char *)src->stream;
	src->pub.bytes_in_buffer = src->size;
	src->start_of_file = FALSE;
	return TRUE;
}


METHODDEF(void)skip_input_data (j_decompress_ptr cinfo, long num_bytes)
{
	mlibjpg_src_ptr src = (mlibjpg_src_ptr) cinfo->src;
	if (num_bytes > 0) {
		while (num_bytes > (long) src->pub.bytes_in_buffer)
		{
			num_bytes -= (long) src->pub.bytes_in_buffer;
			(void) fill_input_buffer(cinfo);
		}
		src->pub.next_input_byte += (size_t) num_bytes;
		src->pub.bytes_in_buffer -= (size_t) num_bytes;
	}
}

METHODDEF(void)term_source (j_decompress_ptr cinfo)
{
}

GLOBAL(void)jpeg_memory_src(j_decompress_ptr cinfo, void *stream, int stream_size)
{
	mlibjpg_src_ptr src;

	if (cinfo->src == NULL) {	/* first time for this JPEG object? */
		cinfo->src = (struct jpeg_source_mgr *)
			(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
				sizeof(mlibjpg_source_mgr));
	}

	src = (mlibjpg_src_ptr) cinfo->src;
	src->pub.init_source = init_source;
	src->pub.fill_input_buffer = fill_input_buffer;
	src->pub.skip_input_data = skip_input_data;
	src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */
	src->pub.term_source = term_source;
	src->stream = stream;
	src->size = stream_size;
	src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */
	src->pub.next_input_byte = NULL; /* until buffer loaded */
}

int LoadJPGFromMemory(void *memory, int memory_size,
		int (*header_callback)(LoadJPGInfo *, void *data), 
		void *(*read_callback)(int line_number, void *data),
		void *data)
{
	struct jpeg_decompress_struct cinfo;
	struct my_error_mgr jerr;
	JSAMPARRAY buffer;
	int row_stride;
	unsigned char *rp,*wp;
	LoadJPGInfo info;
	unsigned x;
	int result = 1;

	if(header_callback==NULL || read_callback==NULL)
		return 0;
	
	cinfo.err = jpeg_std_error(&jerr.pub);
	jerr.pub.error_exit = my_error_exit;

	jpeg_create_decompress(&cinfo);
	jpeg_memory_src(&cinfo, memory, memory_size);
	
	jpeg_read_header(&cinfo, 1);

	info.width = cinfo.image_width;
	info.height = cinfo.image_height;
	info.bpp = 0;
	info.num_palette = 0;
	info.palette = NULL;
	
	switch(cinfo.out_color_space){
	case JCS_GRAYSCALE:
		if(cinfo.num_components == 1){
			info.bpp = 8;
			info.num_palette = 256;
			info.palette = (RGB4 *)malloc(sizeof(RGB4) * 256);
			for(x=0; x<256; x++){
				info.palette[x].b = info.palette[x].g = 
					info.palette[x].r = x;
				info.palette[x].x = 0;
			}
		}
		break;
	case JCS_RGB:
		if(cinfo.num_components == 3)
			info.bpp = 24;
		break;
	case JCS_CMYK:
		if(cinfo.num_components == 4)
			info.bpp = 24;
		break;
	}

	if(info.bpp != 0){
		if(!header_callback(&info, data))
			info.bpp = 0;
	}

	if(info.palette != NULL)
		free(info.palette);

	if(info.bpp == 0)
	{
		jpeg_destroy_decompress(&cinfo);
		return 0;
	}
	
	jpeg_start_decompress(&cinfo);

	row_stride = cinfo.output_width * cinfo.output_components;
	buffer = (*cinfo.mem->alloc_sarray)
		((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);

	while(cinfo.output_scanline < cinfo.output_height)
	{
		wp = (unsigned char *)read_callback(cinfo.output_scanline, data);
		if(wp == NULL)
			break;
		
		jpeg_read_scanlines(&cinfo, buffer, 1);

		switch(cinfo.out_color_space){
		case JCS_GRAYSCALE:
			memcpy(wp, buffer[0], cinfo.output_width);
			break;
		case JCS_RGB:
			for(x=0,rp=buffer[0]; x<cinfo.output_width; x++,rp+=RGB_PIXELSIZE){
				wp[0] = rp[0];
				wp[1] = rp[1];
				wp[2] = rp[2];
				wp += RGB_PIXELSIZE;
			}
			break;
		case JCS_CMYK:
			for(x=0,rp=buffer[0]; x<cinfo.output_width; x++,rp+=4){
				wp[0] = (rp[0] * rp[3]) / 255;
				wp[1] = (rp[1] * rp[3]) / 255;
				wp[2] = (rp[2] * rp[3]) / 255;
				wp += RGB_PIXELSIZE;
			}
		}
	}

	jpeg_finish_decompress(&cinfo);
	jpeg_destroy_decompress(&cinfo);

	return result;
}


typedef struct {
	unsigned char *dib;
	int width,height;
	int (*callback)(float,void*);
	void *callback_data;
} CallbackInfo;

static int HeaderCallback(LoadJPGInfo *info, void *data)
{
	CallbackInfo *call = (CallbackInfo *)data;
	if (info->bpp!=24) return 0; /* 24BitJpegOnly */
	call->dib = (unsigned char *)calloc(1,info->width*info->height*RGB_PIXELSIZE);
	call->height = info->height;
	call->width  = info->width;
	if (call->dib) return 1;
	return 0;
}

static void *ReadCallback(int line_number, void *data)
{
	CallbackInfo *call = (CallbackInfo *)data;
	return (char *)(call->dib)+line_number*call->width*RGB_PIXELSIZE;
}

unsigned char * jpeg_uncompress(
	int * ret_width,
	int * ret_height,
	unsigned char * jpeg_data,
	int jpeg_length)
{
	CallbackInfo call;
	int load_is_ok = 0;

	call.callback = NULL;
	call.callback_data = NULL;
	call.dib = NULL;
	
	load_is_ok = LoadJPGFromMemory(jpeg_data, jpeg_length, HeaderCallback, ReadCallback, &call);
	
	*ret_width = call.width;
	*ret_height = call.height;
	
	if (load_is_ok==0 && (call.dib != NULL) ){
		free(call.dib);
		return NULL;
	}
	return call.dib;
}
