/*

			Slide Show Screen Saver for Windows

				   Colour Quantification

*/

#define JPEG_INTERNALS

#include "screensv.h"

#include <setjmp.h>
#include "jinclude.h"

static jmp_buf JPEG_bailout;

//	Copied from ../jpeg/jdmaster.c because it's local to
//	that file.  No change in operation.

LOCAL(void)
prepare_range_limit_table(j_decompress_ptr cinfo)
/* Allocate and fill in the sample_range_limit table */
{
  JSAMPLE * table;
  int i;

  table = (JSAMPLE *)
    (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
		(5 * (MAXJSAMPLE+1) + CENTERJSAMPLE) * SIZEOF(JSAMPLE));
  table += (MAXJSAMPLE+1);	/* allow negative subscripts of simple table */
  cinfo->sample_range_limit = table;
  /* First segment of "simple" table: limit[x] = 0 for x < 0 */
  MEMZERO(table - (MAXJSAMPLE+1), (MAXJSAMPLE+1) * SIZEOF(JSAMPLE));
  /* Main part of "simple" table: limit[x] = x */
  for (i = 0; i <= MAXJSAMPLE; i++)
    table[i] = (JSAMPLE) i;
  table += CENTERJSAMPLE;	/* Point to where post-IDCT table starts */
  /* End of simple table, rest of first half of post-IDCT table */
  for (i = CENTERJSAMPLE; i < 2*(MAXJSAMPLE+1); i++)
    table[i] = MAXJSAMPLE;
  /* Second half of post-IDCT table */
  MEMZERO(table + (2 * (MAXJSAMPLE+1)),
	  (2 * (MAXJSAMPLE+1) - CENTERJSAMPLE) * SIZEOF(JSAMPLE));
  MEMCOPY(table + (4 * (MAXJSAMPLE+1) - CENTERJSAMPLE),
	  cinfo->sample_range_limit, CENTERJSAMPLE * SIZEOF(JSAMPLE));
}


/*  QUANTIFYBMP  --  Reduce a full-colour bitmap into a
					 palette-mapped bitmap.  The internal
					 quantification machinery of the IJG
					 JPEG library is used to perform the
					 quantisation.  A new colour-mapped
					 bitmap is returned.  Note that it is
					 the responsibility of the caller to
					 release the full-colour bitmap, if
					 desired.  */

HGLOBAL quantifyBMP(HGLOBAL imageBitmapIn)
{
	int i, pass;
	LPBITMAPINFOHEADER bh, ibh;
	DWORD bmpsize;
	struct jpeg_decompress_struct cinfo;
	struct jpeg_error_mgr jerr;
	JSAMPARRAY colormap;
	LPBYTE pix, px, ipix, ipox;
	int linewid, ilinewid;
	int pixbytes;
	static LPBYTE sl = NULL, slo, slop;
	static HGLOBAL imageBitmap = NULL;		// In-memory bitmap

	sl = NULL;
	imageBitmap = NULL;
	if (setjmp(JPEG_bailout) != 0) {
		/*	Since we arrive here via longjmp() from
			parts unknown, we may have allocated
			the line buffer or bitmap prior to bailing
			out.  If they've been allocated, release them.  */

		if (sl != NULL) {
			GlobalFree(sl);
			sl = NULL;
		}
		if (imageBitmap != NULL) {
			GlobalFree(imageBitmap);
			imageBitmap = NULL;
		}
		return NULL;
	}
	cinfo.err = jpeg_std_error(&jerr);
	jpeg_create_decompress(&cinfo);

	/* Now we plug values directly into the cinfo structure
	   convincingly enough so that jquant2.c will be fooled
	   into believing it is the result of actually decoding
	   a JPEG image. */

	ibh = (LPBITMAPINFOHEADER) imageBitmapIn;
	cinfo.image_width = cinfo.output_width = ibh->biWidth;
	cinfo.image_height = cinfo.output_height = ibh->biHeight;
	cinfo.num_components = cinfo.out_color_components = 3;
	cinfo.output_components = 1;
	cinfo.dither_mode = JDITHER_FS;
	cinfo.desired_number_of_colors = 254;
	cinfo.quantize_colors = TRUE;
	cinfo.enable_2pass_quant = TRUE;
	prepare_range_limit_table(&cinfo);

	ilinewid = ((((cinfo.output_width * 3) + (sizeof(LONG) - 1)) / sizeof(LONG)) * sizeof(LONG));
	ipix = ((LPBYTE) imageBitmapIn) + sizeof(BITMAPINFOHEADER);

	//	Allocate storage for decoder and the new bitmap

	pixbytes = 1;
	sl = GlobalAlloc(GMEM_FIXED, cinfo.output_width * 3);
	if (sl == NULL) {
		loadError = "Cannot allocate colour quantisation row buffer";
		return FALSE;
	}

	linewid = ((((cinfo.output_width * pixbytes) + (sizeof(LONG) - 1)) / sizeof(LONG)) * sizeof(LONG));
	bmpsize = sizeof(BITMAPINFOHEADER) +
		(256 * sizeof(RGBQUAD)) +
		(linewid * cinfo.output_height);

	imageBitmap = GlobalAlloc(GMEM_FIXED, bmpsize);
	if (imageBitmap == NULL) {
		loadError = "Cannot allocate bitmap for decoded image";
		GlobalFree(sl);
		return FALSE;
	}

	//	Plug in header fields if output bitmap with information from cinfo

	bh = (LPBITMAPINFOHEADER) imageBitmap;
	pix = ((LPBYTE) imageBitmap) + sizeof(BITMAPINFOHEADER) +
			(256 * sizeof(RGBQUAD));
	bh->biSize = sizeof(BITMAPINFOHEADER);
	bh->biWidth = cinfo.output_width;
	bh->biHeight = cinfo.output_height;
	bh->biPlanes = 1;
	bh->biBitCount = 8;
	bh->biCompression = BI_RGB;
	bh->biSizeImage = 0;
	bh->biXPelsPerMeter = bh->biYPelsPerMeter = 2835;
	bh->biClrUsed = 0;
	bh->biClrImportant = 0;

	//	Now perform the two pass quantisation

	jinit_2pass_quantizer(&cinfo);
	for (pass = 0; pass <= 1; pass++) {
		unsigned char *slp[1] = { sl },
					  *slpo[1] = { slo };

		(*cinfo.cquantize->start_pass)(&cinfo, (J_boolean) (pass == 0));

		/*  Transfer the individual scan lines to the
			quantiser, transforming them as we go from
			Windows buggered-up format into argy-bee.  On
			the second pass, copy the resulting colour
			mapped pixels into the output bitmap.  */

		slpo[0] = pix;
		px = ipix;

		for (i = 0; i < (int) cinfo.output_height; i++) {
			int j;

			ipox = px;
			slop = sl;
			for (j = 0; j < (int) cinfo.output_width; j++) {
				slop[2] = ipox[0];
				slop[1] = ipox[1];
				slop[0] = ipox[2];
				ipox += 3;
				slop += 3;	
			}
#ifdef TRACE_LINES
{ char s[32]; sprintf(s, "Pass %d, line %d\r\n", pass + 1, i); OutputDebugString(s); }
if (i == 959) {
	j = i;
}
#endif
			(*cinfo.cquantize->color_quantize) (&cinfo,
				slp, slpo, 1);
			slpo[0] += linewid;
			px += ilinewid;
		}


		(*cinfo.cquantize->finish_pass)(&cinfo);
	}

	/*	Construct the palette from the colour map optimised
		for the JPEG file.  */

	colormap = cinfo.colormap;
	for (i = 0; i < cinfo.actual_number_of_colors; i++) {
		((LPRGBQUAD) &((LPBITMAPINFO) bh)->bmiColors[i])->rgbRed = GETJSAMPLE(colormap[0][i]);
		((LPRGBQUAD) &((LPBITMAPINFO) bh)->bmiColors[i])->rgbGreen = GETJSAMPLE(colormap[1][i]);
		((LPRGBQUAD) &((LPBITMAPINFO) bh)->bmiColors[i])->rgbBlue = GETJSAMPLE(colormap[2][i]);
		((LPRGBQUAD) &((LPBITMAPINFO) bh)->bmiColors[i])->rgbReserved = 0;
	}

	/*	Plug black and our text colour in the last two slots
		of the palette so we canbe sure they're available.  */

	((LPRGBQUAD) &((LPBITMAPINFO) bh)->bmiColors[254])->rgbRed = 0;
	((LPRGBQUAD) &((LPBITMAPINFO) bh)->bmiColors[254])->rgbGreen = 0;
	((LPRGBQUAD) &((LPBITMAPINFO) bh)->bmiColors[254])->rgbBlue = 0;
	((LPRGBQUAD) &((LPBITMAPINFO) bh)->bmiColors[254])->rgbReserved = 0;

	((LPRGBQUAD) &((LPBITMAPINFO) bh)->bmiColors[255])->rgbRed = GetRValue(skycolour);
	((LPRGBQUAD) &((LPBITMAPINFO) bh)->bmiColors[255])->rgbGreen = GetGValue(skycolour);
	((LPRGBQUAD) &((LPBITMAPINFO) bh)->bmiColors[255])->rgbBlue = GetBValue(skycolour);
	((LPRGBQUAD) &((LPBITMAPINFO) bh)->bmiColors[255])->rgbReserved = 0;

	GlobalFree(sl);
	sl = NULL;
	jpeg_destroy_decompress(&cinfo);

//#define SNAP_BMP
#ifdef SNAP_BMP
{
	FILE *o_file = fopen("d:/slidescr/snap.bmp", "wb");

	BITMAPFILEHEADER bf;

	memcpy(&bf.bfType , "BM", 2);
	bf.bfSize = (sizeof bf) + bmpsize;
	bf.bfReserved1 = bf.bfReserved2 = 0;
	bf.bfOffBits = (sizeof bf) + sizeof(BITMAPINFOHEADER) + (usePalette * 256 * sizeof(RGBQUAD));
	fwrite(&bf, sizeof bf, 1, o_file);
	fwrite(bh, bmpsize, 1, o_file);
	fclose(o_file);
}
#endif
	 
	return imageBitmap;
}
