/*********************************************************/
/* This source code copyright (c) 1991-2001, Aaron Giles */
/* See the Read Me file for licensing information.       */
/* Contact email: mac@aarongiles.com                     */
/*********************************************************/

#include <stdio.h>
#include "jpeglib.h"
#include "jerror.h"

static OSErr DecompressJPEGImage(struct jpeg_decompress_struct *cinfo, PixMapHandle dstPixMap, ICMProgressProcRecordPtr prog);
static void CopyPixMapToDestination(PixMapHandle srcPixMap, short startRow);

GLOBAL void
InitIJGSource (j_decompress_ptr cinfo, Handle theHandle);

extern OSErr DrawJPEG(Handle theHandle, JVDrawParamsHandle theParams)
{
	NestedProgress theProgress = (*theParams)->progress;
	struct jpeg_decompress_struct cinfo;
	char hState = HGetState(theHandle);
	struct jpeg_error_mgr jerr;
	GWorldPtr theGWorld;
	OSErr theErr;
	
	HLock(theHandle);
	cinfo.err = jpeg_std_error(&jerr);
	jpeg_create_decompress(&cinfo);
	InitIJGSource(&cinfo, theHandle);
	jpeg_read_header(&cinfo, true);
	SysBeep(1);
	jpeg_start_decompress(&cinfo);
	SysBeep(1);
/*	{
		// note: need to set depth to 8 for grayscale images
		theGWorld = NewTempGWorld(cinfo.output_width, cinfo.output_height, (cinfo.out_color_components == 1) ? 40 : 32, nil);
		if (theGWorld) {
			theErr = DecompressJPEGImage(&cinfo, GetGWorldPixMap(theGWorld), (ICMProgressProcRecordPtr)&theProgress);
			if ((*theParams)->progress.aborted) theErr = codecAbortErr;
			DisposeGWorld(theGWorld);
		} else gIntError = errNoDrawMemory, theErr = memFullErr;
	}*/
	SysBeep(1);
	jpeg_finish_decompress(&cinfo);
	SysBeep(1);
	jpeg_destroy_decompress(&cinfo);
	HSetState(theHandle, hState);
	return noErr;
}

static OSErr DecompressJPEGImage(struct jpeg_decompress_struct *cinfo, PixMapHandle dstPixMap, ICMProgressProcRecordPtr prog)
{
	char pState = GetPixelsState(dstPixMap);
	RgnHandle dstRgn = qd.thePort->visRgn;
	ulong bandRow = 0, row, bufferRows = Height(&(*dstPixMap)->bounds);
	char mmuMode = true32b;
	OSErr theErr = noErr;
	uchar *rowStart;
	JSAMPARRAY scanLines;
	
	LockPixels(dstPixMap);
	rowStart = (uchar *)GetPixBaseAddr(dstPixMap);
	if (scanLines = (JSAMPARRAY)NewPtrClear(bufferRows * sizeof(JSAMPROW))) {
		for (row = 0; row < bufferRows; row++) scanLines[row] = rowStart, rowStart += (*dstPixMap)->rowBytes & 0x3fff;
		for (row = 0; row < cinfo->output_height; row += bufferRows) {
			SwapMMUMode(&mmuMode);
			jpeg_read_scanlines(cinfo, scanLines, bufferRows);
			SwapMMUMode(&mmuMode);
			CopyPixMapToDestination(dstPixMap, row);
		}
	/*	
		for (row = 0; row < ; row++) {
			if (ptr < dataStart || ptr > dataEnd) {
				theErr = codecBadDataErr;
				break;
			}
			SwapMMUMode(&mmuMode);
			dst = rowStart;
			UnpackBits((Ptr *)&ptr, (Ptr *)&dst, 576 / 8);
			SwapMMUMode(&mmuMode);
			rowStart += ((*dstPixMap)->rowBytes & 0x3fff);
			if (!(row & 63) && prog->progressProc)
				theErr = (OSErr)CallICMProgressProc(prog->progressProc, 
							codecProgressUpdatePercent,
							FixRatio(row, 720), prog->progressRefCon);
			if (++bandRow == Height(&(*dstPixMap)->bounds)) {
				CopyPixMapToDestination(dstPixMap, row - bandRow + 1);
				bandRow = 0;
				rowStart = (uchar *)GetPixBaseAddr(dstPixMap);
			}
			if (theErr != noErr) break;
		}
		if (theErr == noErr && bandRow) CopyPixMapToDestination(dstPixMap, row - bandRow);*/
	}
	SetPixelsState(dstPixMap, pState);
	return theErr;
}

//=====================================================================================
// void CopyPixMapToDestination(PixMapHandle srcPixMap, short startRow)
//=====================================================================================
// Copies the band contained in srcPixMap to the destination, offset from the top by
// startRow.
//=====================================================================================

static void CopyPixMapToDestination(PixMapHandle srcPixMap, short startRow)
{
	Rect srcRect = (*srcPixMap)->bounds, dstRect = (*srcPixMap)->bounds;
	PixMapHandle dstPixMap = GetGWorldPixMap((CGrafPtr)qd.thePort);
	char srcState = HGetState((Handle)srcPixMap);
	char dstState = HGetState((Handle)dstPixMap);
	
	OffsetRect(&dstRect, 0, startRow);
	HLock((Handle)srcPixMap);
	HLock((Handle)dstPixMap);
	CopyBits((BitMap *)*srcPixMap, (BitMap *)*dstPixMap, &srcRect, &dstRect,
				srcCopy + ditherCopy, qd.thePort->visRgn);
	HSetState((Handle)dstPixMap, dstState);
	HSetState((Handle)srcPixMap, srcState);
}

//============================================================================================
//============================================================================================
//===========================                               ==================================
//===========================  IJG Data Source Object Code  ==================================
//===========================                               ==================================
//============================================================================================
//============================================================================================

/* Expanded data source object for stdio input */

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

typedef my_source_mgr * my_src_ptr;

/*
 * Initialize source --- called by jpeg_read_header
 * before any data is actually read.
 */

METHODDEF void
init_source (j_decompress_ptr cinfo)
{
}

/*
 * Fill the input buffer --- called whenever buffer is emptied.
 */

METHODDEF boolean
fill_input_buffer (j_decompress_ptr cinfo)
{
  static JOCTET dummyBuffer[] = { (JOCTET)0xff, (JOCTET)JPEG_EOI };
  my_src_ptr src = (my_src_ptr) cinfo->src;

  WARNMS(cinfo, JWRN_JPEG_EOF);
  src->pub.next_input_byte = (JOCTET *)&dummyBuffer;
  src->pub.bytes_in_buffer = 2;

  return TRUE;
}


/*
 * Skip data --- used to skip over a potentially large amount of
 * uninteresting data (such as an APPn marker).
 */

METHODDEF void
skip_input_data (j_decompress_ptr cinfo, long num_bytes)
{
}

/*
 * An additional method that can be provided by data source modules is the
 * resync_to_restart method for error recovery in the presence of RST markers.
 * For the moment, this source module just uses the default resync method
 * provided by the JPEG library.  That method assumes that no backtracking
 * is possible.
 */


/*
 * Terminate source --- called by jpeg_finish_decompress
 * after all data has been read.  Often a no-op.
 */

METHODDEF void
term_source (j_decompress_ptr cinfo)
{
}

/*
 * Prepare for input from a memory handle.
 */

GLOBAL void
InitIJGSource (j_decompress_ptr cinfo, Handle theHandle)
{
  my_src_ptr src;

  /* The source object and input buffer are made permanent so that a series
   * of JPEG images can be read from the same file by calling jpeg_stdio_src
   * only before the first one.  (If we discarded the buffer at the end of
   * one image, we'd likely lose the start of the next one.)
   * This makes it unsafe to use this manager and a different source
   * manager serially with the same JPEG object.  Caveat programmer.
   */
  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(my_source_mgr));
    src = (my_src_ptr) cinfo->src;
  }

  src = (my_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->pub.bytes_in_buffer = GetHandleSize(theHandle);
  src->pub.next_input_byte = (JOCTET *)StripAddress(*theHandle);
}
