Blame src/gd_jpeg.c

Packit ed3af9
/*
Packit ed3af9
 * gd_jpeg.c: Read and write JPEG (JFIF) format image files using the
Packit ed3af9
 * gd graphics library (http://www.libgd.org).
Packit ed3af9
 *
Packit ed3af9
 * This software is based in part on the work of the Independent JPEG
Packit ed3af9
 * Group.  For more information on the IJG JPEG software (and JPEG
Packit ed3af9
 * documentation, etc.), see ftp://ftp.uu.net/graphics/jpeg/.
Packit ed3af9
 *
Packit ed3af9
 * NOTE: IJG 12-bit JSAMPLE (BITS_IN_JSAMPLE == 12) mode is not
Packit ed3af9
 * supported at all on read in gd 2.0, and is not supported on write
Packit ed3af9
 * except for palette images, which is sort of pointless (TBB). Even that
Packit ed3af9
 * has never been tested according to DB.
Packit ed3af9
 *
Packit ed3af9
 * Copyright 2000 Doug Becker, mailto:thebeckers@home.com
Packit ed3af9
 *
Packit ed3af9
 * Modification 4/18/00 TBB: JPEG_DEBUG rather than just DEBUG,
Packit ed3af9
 * so VC++ builds don't spew to standard output, causing
Packit ed3af9
 * major CGI brain damage
Packit ed3af9
 *
Packit ed3af9
 * 2.0.10: more efficient gdImageCreateFromJpegCtx, thanks to
Packit ed3af9
 * Christian Aberger
Packit ed3af9
 */
Packit ed3af9
Packit ed3af9
/**
Packit ed3af9
 * File: JPEG IO
Packit ed3af9
 *
Packit ed3af9
 * Read and write JPEG images.
Packit ed3af9
 */
Packit ed3af9
Packit ed3af9
#ifdef HAVE_CONFIG_H
Packit ed3af9
#	include "config.h"
Packit ed3af9
#endif
Packit ed3af9
Packit ed3af9
#include <stdlib.h>
Packit ed3af9
#include <setjmp.h>
Packit ed3af9
#include <limits.h>
Packit ed3af9
#include <string.h>
Packit ed3af9
Packit ed3af9
#include "gd.h"
Packit ed3af9
#include "gd_errors.h"
Packit ed3af9
/* TBB: move this up so include files are not brought in */
Packit ed3af9
/* JCE: arrange HAVE_LIBJPEG so that it can be set in gd.h */
Packit ed3af9
#ifdef HAVE_LIBJPEG
Packit ed3af9
#include "gdhelpers.h"
Packit ed3af9
Packit ed3af9
#if defined(_WIN32) && defined(__MINGW32__)
Packit ed3af9
# define HAVE_BOOLEAN
Packit ed3af9
#endif
Packit ed3af9
Packit ed3af9
/* JCE undef two symbols that we don't need anymore but which are
Packit ed3af9
   may be defined in config.h from ./configure but which are
Packit ed3af9
   redefined incompatibly in jpeglib.h */
Packit ed3af9
#undef HAVE_STDDEF_H
Packit ed3af9
#undef HAVE_STDLIB_H
Packit ed3af9
Packit ed3af9
/* 1.8.1: remove dependency on jinclude.h */
Packit ed3af9
#include "jpeglib.h"
Packit ed3af9
#include "jerror.h"
Packit ed3af9
Packit ed3af9
static const char *const GD_JPEG_VERSION = "1.0";
Packit ed3af9
Packit ed3af9
typedef struct _jmpbuf_wrapper {
Packit ed3af9
	jmp_buf jmpbuf;
Packit ed3af9
        int ignore_warning;
Packit ed3af9
}
Packit ed3af9
jmpbuf_wrapper;
Packit ed3af9
Packit ed3af9
static void jpeg_emit_message(j_common_ptr jpeg_info, int level)
Packit ed3af9
{
Packit ed3af9
	char message[JMSG_LENGTH_MAX];
Packit ed3af9
	jmpbuf_wrapper *jmpbufw;
Packit ed3af9
	int ignore_warning = 0;
Packit ed3af9
Packit ed3af9
	jmpbufw = (jmpbuf_wrapper *) jpeg_info->client_data;
Packit ed3af9
Packit ed3af9
	if (jmpbufw != 0) {
Packit ed3af9
		ignore_warning = jmpbufw->ignore_warning;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	(jpeg_info->err->format_message)(jpeg_info,message);
Packit ed3af9
Packit ed3af9
	/* It is a warning message */
Packit ed3af9
	if (level < 0) {
Packit ed3af9
		/* display only the 1st warning, as would do a default libjpeg
Packit ed3af9
		 * unless strace_level >= 3
Packit ed3af9
		 */
Packit ed3af9
		if ((jpeg_info->err->num_warnings == 0) || (jpeg_info->err->trace_level >= 3)) {
Packit ed3af9
			if (!ignore_warning) {
Packit ed3af9
				gd_error("gd-jpeg, libjpeg: recoverable error: %s\n", message);
Packit ed3af9
			}
Packit ed3af9
		}
Packit ed3af9
Packit ed3af9
		jpeg_info->err->num_warnings++;
Packit ed3af9
	} else {
Packit ed3af9
		/* strace msg, Show it if trace_level >= level. */
Packit ed3af9
		if (jpeg_info->err->trace_level >= level) {
Packit ed3af9
			if (!ignore_warning) {
Packit ed3af9
				gd_error("gd-jpeg, libjpeg: strace message: %s\n", message);
Packit ed3af9
			}
Packit ed3af9
		}
Packit ed3af9
	}
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
/* Called by the IJG JPEG library upon encountering a fatal error */
Packit ed3af9
static void fatal_jpeg_error(j_common_ptr cinfo)
Packit ed3af9
{
Packit ed3af9
	jmpbuf_wrapper *jmpbufw;
Packit ed3af9
	char buffer[JMSG_LENGTH_MAX];
Packit ed3af9
Packit ed3af9
	(*cinfo->err->format_message)(cinfo, buffer);
Packit ed3af9
	gd_error_ex(GD_WARNING, "gd-jpeg: JPEG library reports unrecoverable error: %s", buffer);
Packit ed3af9
Packit ed3af9
	jmpbufw = (jmpbuf_wrapper *)cinfo->client_data;
Packit ed3af9
	jpeg_destroy(cinfo);
Packit ed3af9
Packit ed3af9
	if(jmpbufw != 0) {
Packit ed3af9
		longjmp(jmpbufw->jmpbuf, 1);
Packit ed3af9
		gd_error_ex(GD_ERROR, "gd-jpeg: EXTREMELY fatal error: longjmp returned control; terminating\n");
Packit ed3af9
	} else {
Packit ed3af9
		gd_error_ex(GD_ERROR, "gd-jpeg: EXTREMELY fatal error: jmpbuf unrecoverable; terminating\n");
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	exit(99);
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
/*
Packit ed3af9
 * Write IM to OUTFILE as a JFIF-formatted JPEG image, using quality
Packit ed3af9
 * QUALITY.  If QUALITY is in the range 0-100, increasing values
Packit ed3af9
 * represent higher quality but also larger image size.  If QUALITY is
Packit ed3af9
 * negative, the IJG JPEG library's default quality is used (which
Packit ed3af9
 * should be near optimal for many applications).  See the IJG JPEG
Packit ed3af9
 * library documentation for more details.
Packit ed3af9
 */
Packit ed3af9
Packit ed3af9
Packit ed3af9
/*
Packit ed3af9
  Function: gdImageJpeg
Packit ed3af9
Packit ed3af9
    <gdImageJpeg> outputs the specified image to the specified file in
Packit ed3af9
    JPEG format. The file must be open for writing. Under MSDOS and
Packit ed3af9
    all versions of Windows, it is important to use "wb" as opposed to
Packit ed3af9
    simply "w" as the mode when opening the file, and under Unix there
Packit ed3af9
    is no penalty for doing so. <gdImageJpeg> does not close the file;
Packit ed3af9
    your code must do so.
Packit ed3af9
Packit ed3af9
    If _quality_ is negative, the default IJG JPEG quality value (which
Packit ed3af9
    should yield a good general quality / size tradeoff for most
Packit ed3af9
    situations) is used. Otherwise, for practical purposes, _quality_
Packit ed3af9
    should be a value in the range 0-95, higher quality values usually
Packit ed3af9
    implying both higher quality and larger image sizes.
Packit ed3af9
Packit ed3af9
    If you have set image interlacing using <gdImageInterlace>, this
Packit ed3af9
    function will interpret that to mean you wish to output a
Packit ed3af9
    progressive JPEG. Some programs (e.g., Web browsers) can display
Packit ed3af9
    progressive JPEGs incrementally; this can be useful when browsing
Packit ed3af9
    over a relatively slow communications link, for
Packit ed3af9
    example. Progressive JPEGs can also be slightly smaller than
Packit ed3af9
    sequential (non-progressive) JPEGs.
Packit ed3af9
Packit ed3af9
  Variants:
Packit ed3af9
Packit ed3af9
    <gdImageJpegCtx> stores the image using a <gdIOCtx> struct.
Packit ed3af9
Packit ed3af9
    <gdImageJpegPtr> stores the image to RAM.
Packit ed3af9
Packit ed3af9
  Parameters:
Packit ed3af9
Packit ed3af9
    im      - The image to save
Packit ed3af9
    outFile - The FILE pointer to write to.
Packit ed3af9
    quality - Compression quality (0-95, 0 means use the default).
Packit ed3af9
Packit ed3af9
  Returns:
Packit ed3af9
Packit ed3af9
    Nothing.
Packit ed3af9
Packit ed3af9
  Example:
Packit ed3af9
    (start code)
Packit ed3af9
Packit ed3af9
    gdImagePtr im;
Packit ed3af9
    int black, white;
Packit ed3af9
    FILE *out;
Packit ed3af9
    // Create the image
Packit ed3af9
    im = gdImageCreate(100, 100);
Packit ed3af9
    // Allocate background
Packit ed3af9
    white = gdImageColorAllocate(im, 255, 255, 255);
Packit ed3af9
    // Allocate drawing color
Packit ed3af9
    black = gdImageColorAllocate(im, 0, 0, 0);
Packit ed3af9
    // Draw rectangle
Packit ed3af9
    gdImageRectangle(im, 0, 0, 99, 99, black);
Packit ed3af9
    // Open output file in binary mode
Packit ed3af9
    out = fopen("rect.jpg", "wb");
Packit ed3af9
    // Write JPEG using default quality
Packit ed3af9
    gdImageJpeg(im, out, -1);
Packit ed3af9
    // Close file
Packit ed3af9
    fclose(out);
Packit ed3af9
    // Destroy image
Packit ed3af9
    gdImageDestroy(im);
Packit ed3af9
Packit ed3af9
    (end code)
Packit ed3af9
*/
Packit ed3af9
Packit ed3af9
BGD_DECLARE(void) gdImageJpeg(gdImagePtr im, FILE *outFile, int quality)
Packit ed3af9
{
Packit ed3af9
	gdIOCtx *out = gdNewFileCtx(outFile);
Packit ed3af9
	if (out == NULL) return;
Packit ed3af9
	gdImageJpegCtx(im, out, quality);
Packit ed3af9
	out->gd_free(out);
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
/*
Packit ed3af9
  Function: gdImageJpegPtr
Packit ed3af9
Packit ed3af9
    Identical to <gdImageJpeg> except that it returns a pointer to a
Packit ed3af9
    memory area with the JPEG data. This memory must be freed by the
Packit ed3af9
    caller when it is no longer needed.
Packit ed3af9
Packit ed3af9
    The caller *must* invoke <gdFree>, not free().  This is because it
Packit ed3af9
    is not guaranteed that libgd will use the same implementation of
Packit ed3af9
    malloc, free, etc. as your proggram.
Packit ed3af9
Packit ed3af9
    The 'size' parameter receives the total size of the block of
Packit ed3af9
    memory.
Packit ed3af9
Packit ed3af9
  Parameters:
Packit ed3af9
Packit ed3af9
    im      - The image to write
Packit ed3af9
    size    - Output: the size of the resulting image.
Packit ed3af9
    quality - Compression quality.
Packit ed3af9
Packit ed3af9
  Returns:
Packit ed3af9
Packit ed3af9
    A pointer to the JPEG data or NULL if an error occurred.
Packit ed3af9
Packit ed3af9
*/
Packit ed3af9
BGD_DECLARE(void *) gdImageJpegPtr(gdImagePtr im, int *size, int quality)
Packit ed3af9
{
Packit ed3af9
	void *rv;
Packit ed3af9
	gdIOCtx *out = gdNewDynamicCtx(2048, NULL);
Packit ed3af9
	if (out == NULL) return NULL;
Packit ed3af9
	gdImageJpegCtx(im, out, quality);
Packit ed3af9
	rv = gdDPExtractData(out, size);
Packit ed3af9
	out->gd_free(out);
Packit ed3af9
	return rv;
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
void jpeg_gdIOCtx_dest(j_compress_ptr cinfo, gdIOCtx *outfile);
Packit ed3af9
Packit ed3af9
/*
Packit ed3af9
  Function: gdImageJpegCtx
Packit ed3af9
Packit ed3af9
    Write the image as JPEG data via a <gdIOCtx>. See <gdImageJpeg>
Packit ed3af9
    for more details.
Packit ed3af9
Packit ed3af9
  Parameters:
Packit ed3af9
Packit ed3af9
    im      - The image to write.
Packit ed3af9
    outfile - The output sink.
Packit ed3af9
    quality - Image quality. 
Packit ed3af9
Packit ed3af9
*/
Packit ed3af9
BGD_DECLARE(void) gdImageJpegCtx(gdImagePtr im, gdIOCtx *outfile, int quality)
Packit ed3af9
{
Packit ed3af9
	struct jpeg_compress_struct cinfo;
Packit ed3af9
	struct jpeg_error_mgr jerr;
Packit ed3af9
	int i, j, jidx;
Packit ed3af9
	/* volatile so we can gdFree it on return from longjmp */
Packit ed3af9
	volatile JSAMPROW row = 0;
Packit ed3af9
	JSAMPROW rowptr[1];
Packit ed3af9
	jmpbuf_wrapper jmpbufw;
Packit ed3af9
	JDIMENSION nlines;
Packit ed3af9
	char comment[255];
Packit ed3af9
Packit ed3af9
#ifdef JPEG_DEBUG
Packit ed3af9
	gd_error_ex(GD_DEBUG, "gd-jpeg: gd JPEG version %s\n", GD_JPEG_VERSION);
Packit ed3af9
	gd_error_ex(GD_DEBUG, "gd-jpeg: JPEG library version %d, %d-bit sample values\n", JPEG_LIB_VERSION, BITS_IN_JSAMPLE);
Packit ed3af9
	if (!im->trueColor) {
Packit ed3af9
		for(i = 0; i < im->colorsTotal; i++) {
Packit ed3af9
			if(!im->open[i]) {
Packit ed3af9
				gd_error_ex(GD_DEBUG, "gd-jpeg: gd colormap index %d: (%d, %d, %d)\n", i, im->red[i], im->green[i], im->blue[i]);
Packit ed3af9
			}
Packit ed3af9
		}
Packit ed3af9
	}
Packit ed3af9
#endif /* JPEG_DEBUG */
Packit ed3af9
Packit ed3af9
	memset(&cinfo, 0, sizeof(cinfo));
Packit ed3af9
	memset(&jerr, 0, sizeof(jerr));
Packit ed3af9
Packit ed3af9
	cinfo.err = jpeg_std_error(&jerr);
Packit ed3af9
	cinfo.client_data = &jmpbufw;
Packit ed3af9
Packit ed3af9
	if(setjmp(jmpbufw.jmpbuf) != 0) {
Packit ed3af9
		/* we're here courtesy of longjmp */
Packit ed3af9
		if(row) {
Packit ed3af9
			gdFree(row);
Packit ed3af9
		}
Packit ed3af9
		return;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	cinfo.err->emit_message = jpeg_emit_message;
Packit ed3af9
	cinfo.err->error_exit = fatal_jpeg_error;
Packit ed3af9
Packit ed3af9
	jpeg_create_compress(&cinfo);
Packit ed3af9
Packit ed3af9
	cinfo.image_width = im->sx;
Packit ed3af9
	cinfo.image_height = im->sy;
Packit ed3af9
	cinfo.input_components = 3; /* # of color components per pixel */
Packit ed3af9
	cinfo.in_color_space = JCS_RGB; /* colorspace of input image */
Packit ed3af9
Packit ed3af9
	jpeg_set_defaults(&cinfo);
Packit ed3af9
Packit ed3af9
	cinfo.density_unit = 1;
Packit ed3af9
	cinfo.X_density = im->res_x;
Packit ed3af9
	cinfo.Y_density = im->res_y;
Packit ed3af9
Packit ed3af9
	if(quality >= 0) {
Packit ed3af9
		jpeg_set_quality(&cinfo, quality, TRUE);
Packit ed3af9
		if (quality >= 90) {
Packit ed3af9
			cinfo.comp_info[0].h_samp_factor = 1;
Packit ed3af9
			cinfo.comp_info[0].v_samp_factor = 1;
Packit ed3af9
		}
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	/* If user requests interlace, translate that to progressive JPEG */
Packit ed3af9
	if(gdImageGetInterlaced(im)) {
Packit ed3af9
#ifdef JPEG_DEBUG
Packit ed3af9
		gd_error_ex(GD_DEBUG, "gd-jpeg: interlace set, outputting progressive JPEG image\n");
Packit ed3af9
#endif
Packit ed3af9
		jpeg_simple_progression(&cinfo);
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	jpeg_gdIOCtx_dest(&cinfo, outfile);
Packit ed3af9
Packit ed3af9
	row = (JSAMPROW)gdCalloc(1, cinfo.image_width * cinfo.input_components * sizeof(JSAMPLE));
Packit ed3af9
	if(row == 0) {
Packit ed3af9
		gd_error("gd-jpeg: error: unable to allocate JPEG row structure: gdCalloc returns NULL\n");
Packit ed3af9
		jpeg_destroy_compress(&cinfo);
Packit ed3af9
		return;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	rowptr[0] = row;
Packit ed3af9
Packit ed3af9
	jpeg_start_compress(&cinfo, TRUE);
Packit ed3af9
Packit ed3af9
	sprintf(comment, "CREATOR: gd-jpeg v%s (using IJG JPEG v%d),", GD_JPEG_VERSION, JPEG_LIB_VERSION);
Packit ed3af9
Packit ed3af9
	if(quality >= 0) {
Packit ed3af9
		sprintf (comment + strlen(comment), " quality = %d\n", quality);
Packit ed3af9
	} else {
Packit ed3af9
		strcat(comment + strlen(comment), " default quality\n");
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	jpeg_write_marker(&cinfo, JPEG_COM, (unsigned char *) comment, (unsigned int)strlen(comment));
Packit ed3af9
Packit ed3af9
	if(im->trueColor) {
Packit ed3af9
#if BITS_IN_JSAMPLE == 12
Packit ed3af9
		gd_error(
Packit ed3af9
		        "gd-jpeg: error: jpeg library was compiled for 12-bit\n"
Packit ed3af9
		        "precision. This is mostly useless, because JPEGs on the web are\n"
Packit ed3af9
		        "8-bit and such versions of the jpeg library won't read or write\n"
Packit ed3af9
		        "them. GD doesn't support these unusual images. Edit your\n"
Packit ed3af9
		        "jmorecfg.h file to specify the correct precision and completely\n"
Packit ed3af9
		        "'make clean' and 'make install' libjpeg again. Sorry.\n"
Packit ed3af9
		       );
Packit ed3af9
		goto error;
Packit ed3af9
#endif /* BITS_IN_JSAMPLE == 12 */
Packit ed3af9
		for(i = 0; i < im->sy; i++) {
Packit ed3af9
			for(jidx = 0, j = 0; j < im->sx; j++) {
Packit ed3af9
				int val = im->tpixels[i][j];
Packit ed3af9
				row[jidx++] = gdTrueColorGetRed(val);
Packit ed3af9
				row[jidx++] = gdTrueColorGetGreen(val);
Packit ed3af9
				row[jidx++] = gdTrueColorGetBlue(val);
Packit ed3af9
			}
Packit ed3af9
Packit ed3af9
			nlines = jpeg_write_scanlines(&cinfo, rowptr, 1);
Packit ed3af9
Packit ed3af9
			if(nlines != 1) {
Packit ed3af9
				gd_error("gd_jpeg: warning: jpeg_write_scanlines returns %u -- expected 1\n", nlines);
Packit ed3af9
			}
Packit ed3af9
		}
Packit ed3af9
	} else {
Packit ed3af9
		for(i = 0; i < im->sy; i++) {
Packit ed3af9
			for(jidx = 0, j = 0; j < im->sx; j++) {
Packit ed3af9
				int idx = im->pixels[i][j];
Packit ed3af9
Packit ed3af9
				/*
Packit ed3af9
				 * NB: Although gd RGB values are ints, their max value is
Packit ed3af9
				 * 255 (see the documentation for gdImageColorAllocate())
Packit ed3af9
				 * -- perfect for 8-bit JPEG encoding (which is the norm)
Packit ed3af9
				 */
Packit ed3af9
#if BITS_IN_JSAMPLE == 8
Packit ed3af9
				row[jidx++] = im->red[idx];
Packit ed3af9
				row[jidx++] = im->green[idx];
Packit ed3af9
				row[jidx++] = im->blue[idx];
Packit ed3af9
#elif BITS_IN_JSAMPLE == 12
Packit ed3af9
				row[jidx++] = im->red[idx] << 4;
Packit ed3af9
				row[jidx++] = im->green[idx] << 4;
Packit ed3af9
				row[jidx++] = im->blue[idx] << 4;
Packit ed3af9
#else
Packit ed3af9
#error IJG JPEG library BITS_IN_JSAMPLE value must be 8 or 12
Packit ed3af9
#endif
Packit ed3af9
			}
Packit ed3af9
Packit ed3af9
			nlines = jpeg_write_scanlines(&cinfo, rowptr, 1);
Packit ed3af9
			if(nlines != 1) {
Packit ed3af9
				gd_error("gd_jpeg: warning: jpeg_write_scanlines"
Packit ed3af9
				         " returns %u -- expected 1\n", nlines);
Packit ed3af9
			}
Packit ed3af9
		}
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	jpeg_finish_compress(&cinfo);
Packit ed3af9
	jpeg_destroy_compress(&cinfo);
Packit ed3af9
	gdFree(row);
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
Packit ed3af9
Packit ed3af9
Packit ed3af9
/*
Packit ed3af9
  Function: gdImageCreateFromJpeg
Packit ed3af9
Packit ed3af9
  See <gdImageCreateFromJpegEx>.
Packit ed3af9
*/
Packit ed3af9
BGD_DECLARE(gdImagePtr) gdImageCreateFromJpeg(FILE *inFile)
Packit ed3af9
{
Packit ed3af9
	return gdImageCreateFromJpegEx(inFile, 1);
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
Packit ed3af9
/*
Packit ed3af9
  Function: gdImageCreateFromJpegEx
Packit ed3af9
Packit ed3af9
    <gdImageCreateFromJpegEx> is called to load truecolor images from
Packit ed3af9
    JPEG format files. Invoke <gdImageCreateFromJpegEx> with an
Packit ed3af9
    already opened pointer to a file containing the desired
Packit ed3af9
    image. <gdImageCreateFromJpegEx> returns a <gdImagePtr> to the new
Packit ed3af9
    truecolor image, or NULL if unable to load the image (most often
Packit ed3af9
    because the file is corrupt or does not contain a JPEG
Packit ed3af9
    image). <gdImageCreateFromJpegEx> does not close the file.
Packit ed3af9
Packit ed3af9
    You can inspect the sx and sy members of the image to determine
Packit ed3af9
    its size. The image must eventually be destroyed using
Packit ed3af9
    <gdImageDestroy>.
Packit ed3af9
Packit ed3af9
    *The returned image is always a truecolor image.*
Packit ed3af9
Packit ed3af9
  Variants:
Packit ed3af9
Packit ed3af9
    <gdImageCreateFromJpegPtrEx> creates an image from JPEG data
Packit ed3af9
    already in memory.
Packit ed3af9
Packit ed3af9
    <gdImageCreateFromJpegCtxEx> reads its data via the function
Packit ed3af9
    pointers in a <gdIOCtx> structure.
Packit ed3af9
Packit ed3af9
    <gdImageCreateFromJpeg>, <gdImageCreateFromJpegPtr> and
Packit ed3af9
    <gdImageCreateFromJpegCtx> are equivalent to calling their
Packit ed3af9
    _Ex_-named counterparts with an ignore_warning set to 1
Packit ed3af9
    (i.e. TRUE).
Packit ed3af9
Packit ed3af9
  Parameters:
Packit ed3af9
Packit ed3af9
    infile          - The input FILE pointer.
Packit ed3af9
    ignore_warning  - Flag.  If true, ignores recoverable warnings.
Packit ed3af9
Packit ed3af9
  Returns:
Packit ed3af9
Packit ed3af9
    A pointer to the new *truecolor* image.  This will need to be
Packit ed3af9
    destroyed with <gdImageDestroy> once it is no longer needed.
Packit ed3af9
Packit ed3af9
    On error, returns NULL.
Packit ed3af9
Packit ed3af9
  Example:
Packit ed3af9
    (start code)
Packit ed3af9
Packit ed3af9
    gdImagePtr im;
Packit ed3af9
    FILE *in;
Packit ed3af9
    in = fopen("myjpeg.jpg", "rb");
Packit ed3af9
    im = gdImageCreateFromJpegEx(in, GD_TRUE);
Packit ed3af9
    fclose(in);
Packit ed3af9
    // ... Use the image ...
Packit ed3af9
    gdImageDestroy(im);
Packit ed3af9
Packit ed3af9
    (end code)
Packit ed3af9
*/
Packit ed3af9
BGD_DECLARE(gdImagePtr) gdImageCreateFromJpegEx(FILE *inFile, int ignore_warning)
Packit ed3af9
{
Packit ed3af9
	gdImagePtr im;
Packit ed3af9
	gdIOCtx *in = gdNewFileCtx(inFile);
Packit ed3af9
	if (in == NULL) return NULL;
Packit ed3af9
	im = gdImageCreateFromJpegCtxEx(in, ignore_warning);
Packit ed3af9
	in->gd_free(in);
Packit ed3af9
	return im;
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
/*
Packit ed3af9
  Function: gdImageCreateFromJpegPtr
Packit ed3af9
Packit ed3af9
  Parameters:
Packit ed3af9
  
Packit ed3af9
    size    - size of JPEG data in bytes.
Packit ed3af9
    data    - pointer to JPEG data.
Packit ed3af9
Packit ed3af9
  See <gdImageCreateFromJpegEx>.
Packit ed3af9
*/
Packit ed3af9
BGD_DECLARE(gdImagePtr) gdImageCreateFromJpegPtr(int size, void *data)
Packit ed3af9
{
Packit ed3af9
	return gdImageCreateFromJpegPtrEx(size, data, 1);
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
/*
Packit ed3af9
  Function: gdImageCreateFromJpegPtrEx
Packit ed3af9
Packit ed3af9
  Parameters:
Packit ed3af9
  
Packit ed3af9
    size            - size of JPEG data in bytes.
Packit ed3af9
    data            - pointer to JPEG data.
Packit ed3af9
    ignore_warning  - if true, ignore recoverable warnings
Packit ed3af9
Packit ed3af9
  See <gdImageCreateFromJpegEx>.
Packit ed3af9
*/
Packit ed3af9
BGD_DECLARE(gdImagePtr) gdImageCreateFromJpegPtrEx(int size, void *data, int ignore_warning)
Packit ed3af9
{
Packit ed3af9
	gdImagePtr im;
Packit ed3af9
	gdIOCtx *in = gdNewDynamicCtxEx(size, data, 0);
Packit ed3af9
	if(!in) {
Packit ed3af9
		return 0;
Packit ed3af9
	}
Packit ed3af9
	im = gdImageCreateFromJpegCtxEx(in, ignore_warning);
Packit ed3af9
	in->gd_free(in);
Packit ed3af9
	return im;
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
void jpeg_gdIOCtx_src(j_decompress_ptr cinfo, gdIOCtx *infile);
Packit ed3af9
Packit ed3af9
static int CMYKToRGB(int c, int m, int y, int k, int inverted);
Packit ed3af9
Packit ed3af9
/*
Packit ed3af9
  Function: gdImageCreateFromJpegCtx
Packit ed3af9
Packit ed3af9
  See <gdImageCreateFromJpeg>.
Packit ed3af9
*/
Packit ed3af9
BGD_DECLARE(gdImagePtr) gdImageCreateFromJpegCtx(gdIOCtx *infile)
Packit ed3af9
{
Packit ed3af9
	return gdImageCreateFromJpegCtxEx(infile, 1);
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
/*
Packit ed3af9
  Function: gdImageCreateFromJpegCtxEx
Packit ed3af9
Packit ed3af9
  See <gdImageCreateFromJpeg>.
Packit ed3af9
*/
Packit ed3af9
BGD_DECLARE(gdImagePtr) gdImageCreateFromJpegCtxEx(gdIOCtx *infile, int ignore_warning)
Packit ed3af9
{
Packit ed3af9
	struct jpeg_decompress_struct cinfo;
Packit ed3af9
	struct jpeg_error_mgr jerr;
Packit ed3af9
	jmpbuf_wrapper jmpbufw;
Packit ed3af9
	/* volatile so we can gdFree them after longjmp */
Packit ed3af9
	volatile JSAMPROW row = 0;
Packit ed3af9
	volatile gdImagePtr im = 0;
Packit ed3af9
	JSAMPROW rowptr[1];
Packit ed3af9
	JDIMENSION i, j;
Packit ed3af9
	int retval;
Packit ed3af9
	JDIMENSION nrows;
Packit ed3af9
	int channels = 3;
Packit ed3af9
	int inverted = 0;
Packit ed3af9
Packit ed3af9
#ifdef JPEG_DEBUG
Packit ed3af9
	gd_error_ex(GD_DEBUG, "gd-jpeg: gd JPEG version %s\n", GD_JPEG_VERSION);
Packit ed3af9
	gd_error_ex(GD_DEBUG, "gd-jpeg: JPEG library version %d, %d-bit sample values\n", JPEG_LIB_VERSION, BITS_IN_JSAMPLE);
Packit ed3af9
	gd_error_ex(GD_DEBUG, "sizeof: %d\n", sizeof(struct jpeg_decompress_struct));
Packit ed3af9
#endif
Packit ed3af9
Packit ed3af9
	memset(&cinfo, 0, sizeof(cinfo));
Packit ed3af9
	memset(&jerr, 0, sizeof(jerr));
Packit ed3af9
Packit ed3af9
	jmpbufw.ignore_warning = ignore_warning;
Packit ed3af9
Packit ed3af9
	cinfo.err = jpeg_std_error(&jerr);
Packit ed3af9
	cinfo.client_data = &jmpbufw;
Packit ed3af9
Packit ed3af9
	cinfo.err->emit_message = jpeg_emit_message;
Packit ed3af9
Packit ed3af9
	if(setjmp(jmpbufw.jmpbuf) != 0) {
Packit ed3af9
		/* we're here courtesy of longjmp */
Packit ed3af9
		if(row) {
Packit ed3af9
			gdFree(row);
Packit ed3af9
		}
Packit ed3af9
		if(im) {
Packit ed3af9
			gdImageDestroy(im);
Packit ed3af9
		}
Packit ed3af9
		return 0;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	cinfo.err->error_exit = fatal_jpeg_error;
Packit ed3af9
Packit ed3af9
	jpeg_create_decompress(&cinfo);
Packit ed3af9
Packit ed3af9
	jpeg_gdIOCtx_src(&cinfo, infile);
Packit ed3af9
Packit ed3af9
	/* 2.0.22: save the APP14 marker to check for Adobe Photoshop CMYK
Packit ed3af9
	 * files with inverted components.
Packit ed3af9
	 */
Packit ed3af9
	jpeg_save_markers(&cinfo, JPEG_APP0 + 14, 256);
Packit ed3af9
Packit ed3af9
	retval = jpeg_read_header(&cinfo, TRUE);
Packit ed3af9
	if(retval != JPEG_HEADER_OK) {
Packit ed3af9
		gd_error("gd-jpeg: warning: jpeg_read_header returns"
Packit ed3af9
		         " %d, expected %d\n", retval, JPEG_HEADER_OK);
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	if(cinfo.image_height > INT_MAX) {
Packit ed3af9
		gd_error("gd-jpeg: warning: JPEG image height (%u) is"
Packit ed3af9
		         " greater than INT_MAX (%d) (and thus greater than"
Packit ed3af9
		         " gd can handle)", cinfo.image_height, INT_MAX);
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	if(cinfo.image_width > INT_MAX) {
Packit ed3af9
		gd_error("gd-jpeg: warning: JPEG image width (%u) is"
Packit ed3af9
		         " greater than INT_MAX (%d) (and thus greater than"
Packit ed3af9
		         " gd can handle)\n", cinfo.image_width, INT_MAX);
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	im = gdImageCreateTrueColor((int)cinfo.image_width, (int)cinfo.image_height);
Packit ed3af9
	if(im == 0) {
Packit ed3af9
		gd_error("gd-jpeg error: cannot allocate gdImage struct\n");
Packit ed3af9
		goto error;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	/* check if the resolution is specified */
Packit ed3af9
	switch (cinfo.density_unit) {
Packit ed3af9
	case 1:
Packit ed3af9
		im->res_x = cinfo.X_density;
Packit ed3af9
		im->res_y = cinfo.Y_density;
Packit ed3af9
		break;
Packit ed3af9
	case 2:
Packit ed3af9
		im->res_x = DPCM2DPI(cinfo.X_density);
Packit ed3af9
		im->res_y = DPCM2DPI(cinfo.Y_density);
Packit ed3af9
		break;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	/* 2.0.22: very basic support for reading CMYK colorspace files. Nice for
Packit ed3af9
	 * thumbnails but there's no support for fussy adjustment of the
Packit ed3af9
	 * assumed properties of inks and paper.
Packit ed3af9
	 */
Packit ed3af9
	if((cinfo.jpeg_color_space == JCS_CMYK) || (cinfo.jpeg_color_space == JCS_YCCK)) {
Packit ed3af9
		cinfo.out_color_space = JCS_CMYK;
Packit ed3af9
	} else {
Packit ed3af9
		cinfo.out_color_space = JCS_RGB;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	if(jpeg_start_decompress(&cinfo) != TRUE) {
Packit ed3af9
		gd_error("gd-jpeg: warning: jpeg_start_decompress"
Packit ed3af9
		        " reports suspended data source\n");
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
#ifdef JPEG_DEBUG
Packit ed3af9
	gd_error_ex(GD_DEBUG, "gd-jpeg: JPEG image information:");
Packit ed3af9
	if(cinfo.saw_JFIF_marker) {
Packit ed3af9
		gd_error_ex(GD_DEBUG, " JFIF version %d.%.2d", (int)cinfo.JFIF_major_version, (int)cinfo.JFIF_minor_version);
Packit ed3af9
	} else if(cinfo.saw_Adobe_marker) {
Packit ed3af9
		gd_error_ex(GD_DEBUG, " Adobe format");
Packit ed3af9
	} else {
Packit ed3af9
		gd_error_ex(GD_DEBUG, " UNKNOWN format");
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	gd_error_ex(GD_DEBUG, " %ux%u (raw) / %ux%u (scaled) %d-bit", cinfo.image_width,
Packit ed3af9
		    cinfo.image_height, cinfo.output_width,
Packit ed3af9
		    cinfo.output_height, cinfo.data_precision
Packit ed3af9
		);
Packit ed3af9
	gd_error_ex(GD_DEBUG, " %s", (cinfo.progressive_mode ? "progressive" : "baseline"));
Packit ed3af9
	gd_error_ex(GD_DEBUG, " image, %d quantized colors, ", cinfo.actual_number_of_colors);
Packit ed3af9
Packit ed3af9
	switch(cinfo.jpeg_color_space) {
Packit ed3af9
	case JCS_GRAYSCALE:
Packit ed3af9
		gd_error_ex(GD_DEBUG, "grayscale");
Packit ed3af9
		break;
Packit ed3af9
Packit ed3af9
	case JCS_RGB:
Packit ed3af9
		gd_error_ex(GD_DEBUG, "RGB");
Packit ed3af9
		break;
Packit ed3af9
Packit ed3af9
	case JCS_YCbCr:
Packit ed3af9
		gd_error_ex(GD_DEBUG, "YCbCr (a.k.a. YUV)");
Packit ed3af9
		break;
Packit ed3af9
Packit ed3af9
	case JCS_CMYK:
Packit ed3af9
		gd_error_ex(GD_DEBUG, "CMYK");
Packit ed3af9
		break;
Packit ed3af9
Packit ed3af9
	case JCS_YCCK:
Packit ed3af9
		gd_error_ex(GD_DEBUG, "YCbCrK");
Packit ed3af9
		break;
Packit ed3af9
Packit ed3af9
	default:
Packit ed3af9
		gd_error_ex(GD_DEBUG, "UNKNOWN (value: %d)", (int)cinfo.jpeg_color_space);
Packit ed3af9
		break;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	gd_error_ex(GD_DEBUG, " colorspace\n");
Packit ed3af9
	fflush(stdout);
Packit ed3af9
#endif /* JPEG_DEBUG */
Packit ed3af9
Packit ed3af9
	/* REMOVED by TBB 2/12/01. This field of the structure is
Packit ed3af9
	 * documented as private, and sure enough it's gone in the
Packit ed3af9
	 * latest libjpeg, replaced by something else. Unfortunately
Packit ed3af9
	 * there is still no right way to find out if the file was
Packit ed3af9
	 * progressive or not; just declare your intent before you
Packit ed3af9
	 * write one by calling gdImageInterlace(im, 1) yourself.
Packit ed3af9
	 * After all, we're not really supposed to rework JPEGs and
Packit ed3af9
	 * write them out again anyway. Lossy compression, remember? */
Packit ed3af9
#if 0
Packit ed3af9
	gdImageInterlace (im, cinfo.progressive_mode != 0);
Packit ed3af9
#endif
Packit ed3af9
	if(cinfo.out_color_space == JCS_RGB) {
Packit ed3af9
		if(cinfo.output_components != 3) {
Packit ed3af9
			gd_error("gd-jpeg: error: JPEG color quantization"
Packit ed3af9
			         " request resulted in output_components == %d"
Packit ed3af9
			         " (expected 3 for RGB)\n", cinfo.output_components);
Packit ed3af9
			goto error;
Packit ed3af9
		}
Packit ed3af9
		channels = 3;
Packit ed3af9
	} else if(cinfo.out_color_space == JCS_CMYK) {
Packit ed3af9
		jpeg_saved_marker_ptr marker;
Packit ed3af9
		if(cinfo.output_components != 4) {
Packit ed3af9
			gd_error("gd-jpeg: error: JPEG color quantization"
Packit ed3af9
			         " request resulted in output_components == %d"
Packit ed3af9
			         " (expected 4 for CMYK)\n", cinfo.output_components);
Packit ed3af9
			goto error;
Packit ed3af9
		}
Packit ed3af9
		channels = 4;
Packit ed3af9
Packit ed3af9
		marker = cinfo.marker_list;
Packit ed3af9
		while(marker) {
Packit ed3af9
			if(	(marker->marker == (JPEG_APP0 + 14)) &&
Packit ed3af9
			        (marker->data_length >= 12) &&
Packit ed3af9
			        (!strncmp((const char *)marker->data, "Adobe", 5))) {
Packit ed3af9
				inverted = 1;
Packit ed3af9
				break;
Packit ed3af9
			}
Packit ed3af9
			marker = marker->next;
Packit ed3af9
		}
Packit ed3af9
	} else {
Packit ed3af9
		gd_error("gd-jpeg: error: unexpected colorspace\n");
Packit ed3af9
		goto error;
Packit ed3af9
	}
Packit ed3af9
#if BITS_IN_JSAMPLE == 12
Packit ed3af9
	gd_error_ex(GD_ERROR,
Packit ed3af9
		    "gd-jpeg: error: jpeg library was compiled for 12-bit\n"
Packit ed3af9
		    "precision. This is mostly useless, because JPEGs on the web are\n"
Packit ed3af9
		    "8-bit and such versions of the jpeg library won't read or write\n"
Packit ed3af9
		    "them. GD doesn't support these unusual images. Edit your\n"
Packit ed3af9
		    "jmorecfg.h file to specify the correct precision and completely\n"
Packit ed3af9
		    "'make clean' and 'make install' libjpeg again. Sorry.\n");
Packit ed3af9
	goto error;
Packit ed3af9
#endif /* BITS_IN_JSAMPLE == 12 */
Packit ed3af9
Packit ed3af9
	row = gdCalloc(cinfo.output_width *channels, sizeof(JSAMPLE));
Packit ed3af9
	if(row == 0) {
Packit ed3af9
		gd_error("gd-jpeg: error: unable to allocate row for"
Packit ed3af9
		         " JPEG scanline: gdCalloc returns NULL\n");
Packit ed3af9
		goto error;
Packit ed3af9
	}
Packit ed3af9
	rowptr[0] = row;
Packit ed3af9
	if(cinfo.out_color_space == JCS_CMYK) {
Packit ed3af9
		for(i = 0; i < cinfo.output_height; i++) {
Packit ed3af9
			register JSAMPROW currow = row;
Packit ed3af9
			register int *tpix = im->tpixels[i];
Packit ed3af9
			nrows = jpeg_read_scanlines(&cinfo, rowptr, 1);
Packit ed3af9
			if(nrows != 1) {
Packit ed3af9
				gd_error("gd-jpeg: error: jpeg_read_scanlines"
Packit ed3af9
				         " returns %u, expected 1\n", nrows);
Packit ed3af9
				goto error;
Packit ed3af9
			}
Packit ed3af9
			for(j = 0; j < cinfo.output_width; j++, currow += 4, tpix++) {
Packit ed3af9
				*tpix = CMYKToRGB(currow[0], currow[1], currow[2], currow[3], inverted);
Packit ed3af9
			}
Packit ed3af9
		}
Packit ed3af9
	} else {
Packit ed3af9
		for(i = 0; i < cinfo.output_height; i++) {
Packit ed3af9
			register JSAMPROW currow = row;
Packit ed3af9
			register int *tpix = im->tpixels[i];
Packit ed3af9
			nrows = jpeg_read_scanlines(&cinfo, rowptr, 1);
Packit ed3af9
			if(nrows != 1) {
Packit ed3af9
				gd_error("gd-jpeg: error: jpeg_read_scanlines"
Packit ed3af9
				         " returns %u, expected 1\n", nrows);
Packit ed3af9
				goto error;
Packit ed3af9
			}
Packit ed3af9
			for(j = 0; j < cinfo.output_width; j++, currow += 3, tpix++) {
Packit ed3af9
				*tpix = gdTrueColor(currow[0], currow[1], currow[2]);
Packit ed3af9
			}
Packit ed3af9
		}
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	if(jpeg_finish_decompress (&cinfo) != TRUE) {
Packit ed3af9
		gd_error("gd-jpeg: warning: jpeg_finish_decompress"
Packit ed3af9
		         " reports suspended data source\n");
Packit ed3af9
	}
Packit ed3af9
	/* TBB 2.0.29: we should do our best to read whatever we can read, and a
Packit ed3af9
	 * warning is a warning. A fatal error on warnings doesn't make sense. */
Packit ed3af9
#if 0
Packit ed3af9
	/* This was originally added by Truxton Fulton */
Packit ed3af9
	if (cinfo.err->num_warnings > 0)
Packit ed3af9
		goto error;
Packit ed3af9
#endif
Packit ed3af9
Packit ed3af9
	jpeg_destroy_decompress(&cinfo);
Packit ed3af9
	gdFree(row);
Packit ed3af9
	return im;
Packit ed3af9
Packit ed3af9
error:
Packit ed3af9
	jpeg_destroy_decompress(&cinfo);
Packit ed3af9
Packit ed3af9
	if(row) {
Packit ed3af9
		gdFree(row);
Packit ed3af9
	}
Packit ed3af9
	if(im) {
Packit ed3af9
		gdImageDestroy(im);
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	return 0;
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
/* A very basic conversion approach, TBB */
Packit ed3af9
Packit ed3af9
static int CMYKToRGB(int c, int m, int y, int k, int inverted)
Packit ed3af9
{
Packit ed3af9
	if(inverted) {
Packit ed3af9
		c = 255 - c;
Packit ed3af9
		m = 255 - m;
Packit ed3af9
		y = 255 - y;
Packit ed3af9
		k = 255 - k;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	return gdTrueColor(
Packit ed3af9
	           (255 - c) * (255 - k) / 255,
Packit ed3af9
	           (255 - m) * (255 - k) / 255,
Packit ed3af9
	           (255 - y) * (255 - k) / 255
Packit ed3af9
	       );
Packit ed3af9
#if 0
Packit ed3af9
	if (inverted) {
Packit ed3af9
		c = 255 - c;
Packit ed3af9
		m = 255 - m;
Packit ed3af9
		y = 255 - y;
Packit ed3af9
		k = 255 - k;
Packit ed3af9
	}
Packit ed3af9
	c = c * (255 - k) / 255 + k;
Packit ed3af9
	if (c > 255) {
Packit ed3af9
		c = 255;
Packit ed3af9
	}
Packit ed3af9
	if (c < 0) {
Packit ed3af9
		c = 0;
Packit ed3af9
	}
Packit ed3af9
	m = m * (255 - k) / 255 + k;
Packit ed3af9
	if (m > 255) {
Packit ed3af9
		m = 255;
Packit ed3af9
	}
Packit ed3af9
	if (m < 0) {
Packit ed3af9
		m = 0;
Packit ed3af9
	}
Packit ed3af9
	y = y * (255 - k) / 255 + k;
Packit ed3af9
	if (y > 255) {
Packit ed3af9
		y = 255;
Packit ed3af9
	}
Packit ed3af9
	if (y < 0) {
Packit ed3af9
		y = 0;
Packit ed3af9
	}
Packit ed3af9
	c = 255 - c;
Packit ed3af9
	m = 255 - m;
Packit ed3af9
	y = 255 - y;
Packit ed3af9
	return gdTrueColor (c, m, y);
Packit ed3af9
#endif
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
/*
Packit ed3af9
 * gdIOCtx JPEG data sources and sinks, T. Boutell
Packit ed3af9
 * almost a simple global replace from T. Lane's stdio versions.
Packit ed3af9
 */
Packit ed3af9
Packit ed3af9
/* Expanded data source object for gdIOCtx input */
Packit ed3af9
typedef struct {
Packit ed3af9
	struct jpeg_source_mgr pub;	/* public fields */
Packit ed3af9
	gdIOCtx *infile;			/* source stream */
Packit ed3af9
	unsigned char *buffer;		/* start of buffer */
Packit ed3af9
	boolean start_of_file;	/* have we gotten any data yet? */
Packit ed3af9
}
Packit ed3af9
my_source_mgr;
Packit ed3af9
Packit ed3af9
typedef my_source_mgr *my_src_ptr;
Packit ed3af9
Packit ed3af9
#define INPUT_BUF_SIZE	4096 /* choose an efficiently fread'able size */
Packit ed3af9
Packit ed3af9
/*
Packit ed3af9
 * Initialize source --- called by jpeg_read_header
Packit ed3af9
 * before any data is actually read.
Packit ed3af9
 */
Packit ed3af9
Packit ed3af9
void init_source(j_decompress_ptr cinfo)
Packit ed3af9
{
Packit ed3af9
	my_src_ptr src = (my_src_ptr)cinfo->src;
Packit ed3af9
Packit ed3af9
	/* We reset the empty-input-file flag for each image,
Packit ed3af9
	 * but we don't clear the input buffer.
Packit ed3af9
	 * This is correct behavior for reading a series of images from one source.
Packit ed3af9
	 */
Packit ed3af9
	src->start_of_file = TRUE;
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
Packit ed3af9
/*
Packit ed3af9
 * Fill the input buffer --- called whenever buffer is emptied.
Packit ed3af9
 *
Packit ed3af9
 * In typical applications, this should read fresh data into the buffer
Packit ed3af9
 * (ignoring the current state of next_input_byte & bytes_in_buffer),
Packit ed3af9
 * reset the pointer & count to the start of the buffer, and return TRUE
Packit ed3af9
 * indicating that the buffer has been reloaded.  It is not necessary to
Packit ed3af9
 * fill the buffer entirely, only to obtain at least one more byte.
Packit ed3af9
 *
Packit ed3af9
 * There is no such thing as an EOF return.  If the end of the file has been
Packit ed3af9
 * reached, the routine has a choice of ERREXIT() or inserting fake data into
Packit ed3af9
 * the buffer.  In most cases, generating a warning message and inserting a
Packit ed3af9
 * fake EOI marker is the best course of action --- this will allow the
Packit ed3af9
 * decompressor to output however much of the image is there.  However,
Packit ed3af9
 * the resulting error message is misleading if the real problem is an empty
Packit ed3af9
 * input file, so we handle that case specially.
Packit ed3af9
 *
Packit ed3af9
 * In applications that need to be able to suspend compression due to input
Packit ed3af9
 * not being available yet, a FALSE return indicates that no more data can be
Packit ed3af9
 * obtained right now, but more may be forthcoming later.  In this situation,
Packit ed3af9
 * the decompressor will return to its caller (with an indication of the
Packit ed3af9
 * number of scanlines it has read, if any).  The application should resume
Packit ed3af9
 * decompression after it has loaded more data into the input buffer.  Note
Packit ed3af9
 * that there are substantial restrictions on the use of suspension --- see
Packit ed3af9
 * the documentation.
Packit ed3af9
 *
Packit ed3af9
 * When suspending, the decompressor will back up to a convenient restart point
Packit ed3af9
 * (typically the start of the current MCU). next_input_byte & bytes_in_buffer
Packit ed3af9
 * indicate where the restart point will be if the current call returns FALSE.
Packit ed3af9
 * Data beyond this point must be rescanned after resumption, so move it to
Packit ed3af9
 * the front of the buffer rather than discarding it.
Packit ed3af9
 */
Packit ed3af9
Packit ed3af9
#define END_JPEG_SEQUENCE "\r\n[*]--:END JPEG:--[*]\r\n"
Packit ed3af9
Packit ed3af9
boolean fill_input_buffer(j_decompress_ptr cinfo)
Packit ed3af9
{
Packit ed3af9
	my_src_ptr src = (my_src_ptr)cinfo->src;
Packit ed3af9
	/* 2.0.12: signed size. Thanks to Geert Jansen */
Packit ed3af9
	/* 2.0.14: some platforms (mingw-msys) don't have ssize_t. Call
Packit ed3af9
	 * an int an int.
Packit ed3af9
	 */
Packit ed3af9
	int nbytes = 0;
Packit ed3af9
	memset(src->buffer, 0, INPUT_BUF_SIZE);
Packit ed3af9
Packit ed3af9
	while(nbytes < INPUT_BUF_SIZE) {
Packit ed3af9
		int got = gdGetBuf(src->buffer + nbytes, INPUT_BUF_SIZE - nbytes, src->infile);
Packit ed3af9
Packit ed3af9
		if((got == EOF) || (got == 0)) {
Packit ed3af9
			/* EOF or error. If we got any data, don't worry about it.
Packit ed3af9
			 * If we didn't, then this is unexpected. */
Packit ed3af9
			if(!nbytes) {
Packit ed3af9
				nbytes = -1;
Packit ed3af9
			}
Packit ed3af9
			break;
Packit ed3af9
		}
Packit ed3af9
		nbytes += got;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	if(nbytes <= 0) {
Packit ed3af9
		if(src->start_of_file) {
Packit ed3af9
			/* Treat empty input file as fatal error */
Packit ed3af9
			ERREXIT(cinfo, JERR_INPUT_EMPTY);
Packit ed3af9
		}
Packit ed3af9
		WARNMS(cinfo, JWRN_JPEG_EOF);
Packit ed3af9
		/* Insert a fake EOI marker */
Packit ed3af9
		src->buffer[0] = (unsigned char)0xFF;
Packit ed3af9
		src->buffer[1] = (unsigned char)JPEG_EOI;
Packit ed3af9
		nbytes = 2;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	src->pub.next_input_byte = src->buffer;
Packit ed3af9
	src->pub.bytes_in_buffer = nbytes;
Packit ed3af9
	src->start_of_file = FALSE;
Packit ed3af9
Packit ed3af9
	return TRUE;
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
/*
Packit ed3af9
 * Skip data --- used to skip over a potentially large amount of
Packit ed3af9
 * uninteresting data (such as an APPn marker).
Packit ed3af9
 *
Packit ed3af9
 * Writers of suspendable-input applications must note that skip_input_data
Packit ed3af9
 * is not granted the right to give a suspension return.  If the skip extends
Packit ed3af9
 * beyond the data currently in the buffer, the buffer can be marked empty so
Packit ed3af9
 * that the next read will cause a fill_input_buffer call that can suspend.
Packit ed3af9
 * Arranging for additional bytes to be discarded before reloading the input
Packit ed3af9
 * buffer is the application writer's problem.
Packit ed3af9
 */
Packit ed3af9
Packit ed3af9
void skip_input_data(j_decompress_ptr cinfo, long num_bytes)
Packit ed3af9
{
Packit ed3af9
	my_src_ptr src = (my_src_ptr)cinfo->src;
Packit ed3af9
Packit ed3af9
	/* Just a dumb implementation for now. Not clear that being smart is worth
Packit ed3af9
	 * any trouble anyway --- large skips are infrequent.
Packit ed3af9
	 */
Packit ed3af9
	if(num_bytes > 0) {
Packit ed3af9
		while(num_bytes > (long)src->pub.bytes_in_buffer) {
Packit ed3af9
			num_bytes -= (long)src->pub.bytes_in_buffer;
Packit ed3af9
			(void)fill_input_buffer(cinfo);
Packit ed3af9
			/* note we assume that fill_input_buffer will never return FALSE,
Packit ed3af9
			 * so suspension need not be handled.
Packit ed3af9
			 */
Packit ed3af9
		}
Packit ed3af9
		src->pub.next_input_byte += (size_t)num_bytes;
Packit ed3af9
		src->pub.bytes_in_buffer -= (size_t)num_bytes;
Packit ed3af9
	}
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
/*
Packit ed3af9
 * An additional method that can be provided by data source modules is the
Packit ed3af9
 * resync_to_restart method for error recovery in the presence of RST markers.
Packit ed3af9
 * For the moment, this source module just uses the default resync method
Packit ed3af9
 * provided by the JPEG library.  That method assumes that no backtracking
Packit ed3af9
 * is possible.
Packit ed3af9
 */
Packit ed3af9
Packit ed3af9
/*
Packit ed3af9
 * Terminate source --- called by jpeg_finish_decompress
Packit ed3af9
 * after all data has been read.  Often a no-op.
Packit ed3af9
 *
Packit ed3af9
 * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding
Packit ed3af9
 * application must deal with any cleanup that should happen even
Packit ed3af9
 * for error exit.
Packit ed3af9
 */
Packit ed3af9
void term_source(j_decompress_ptr cinfo)
Packit ed3af9
{
Packit ed3af9
	(void)cinfo;
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
Packit ed3af9
/*
Packit ed3af9
 * Prepare for input from a gdIOCtx stream.
Packit ed3af9
 * The caller must have already opened the stream, and is responsible
Packit ed3af9
 * for closing it after finishing decompression.
Packit ed3af9
 */
Packit ed3af9
Packit ed3af9
void jpeg_gdIOCtx_src(j_decompress_ptr cinfo, gdIOCtx *infile)
Packit ed3af9
{
Packit ed3af9
	my_src_ptr src;
Packit ed3af9
Packit ed3af9
	/* The source object and input buffer are made permanent so that a series
Packit ed3af9
	 * of JPEG images can be read from the same file by calling jpeg_gdIOCtx_src
Packit ed3af9
	 * only before the first one.  (If we discarded the buffer at the end of
Packit ed3af9
	 * one image, we'd likely lose the start of the next one.)
Packit ed3af9
	 * This makes it unsafe to use this manager and a different source
Packit ed3af9
	 * manager serially with the same JPEG object.  Caveat programmer.
Packit ed3af9
	 */
Packit ed3af9
	if(cinfo->src == NULL) {
Packit ed3af9
		/* first time for this JPEG object? */
Packit ed3af9
		cinfo->src = (struct jpeg_source_mgr *)
Packit ed3af9
		             (*cinfo->mem->alloc_small)((j_common_ptr)cinfo, JPOOL_PERMANENT,
Packit ed3af9
		                                        sizeof(my_source_mgr));
Packit ed3af9
		src = (my_src_ptr)cinfo->src;
Packit ed3af9
		src->buffer = (unsigned char *)
Packit ed3af9
		              (*cinfo->mem->alloc_small)((j_common_ptr)cinfo, JPOOL_PERMANENT,
Packit ed3af9
		                      INPUT_BUF_SIZE * sizeof(unsigned char));
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	src = (my_src_ptr)cinfo->src;
Packit ed3af9
	src->pub.init_source = init_source;
Packit ed3af9
	src->pub.fill_input_buffer = fill_input_buffer;
Packit ed3af9
	src->pub.skip_input_data = skip_input_data;
Packit ed3af9
	src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */
Packit ed3af9
	src->pub.term_source = term_source;
Packit ed3af9
	src->infile = infile;
Packit ed3af9
	src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */
Packit ed3af9
	src->pub.next_input_byte = NULL; /* until buffer loaded */
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
/* Expanded data destination object for stdio output */
Packit ed3af9
typedef struct {
Packit ed3af9
	struct jpeg_destination_mgr pub; /* public fields */
Packit ed3af9
	gdIOCtx *outfile; /* target stream */
Packit ed3af9
	unsigned char *buffer; /* start of buffer */
Packit ed3af9
}
Packit ed3af9
my_destination_mgr;
Packit ed3af9
Packit ed3af9
typedef my_destination_mgr *my_dest_ptr;
Packit ed3af9
Packit ed3af9
#define OUTPUT_BUF_SIZE	4096 /* choose an efficiently fwrite'able size */
Packit ed3af9
Packit ed3af9
/*
Packit ed3af9
 * Initialize destination --- called by jpeg_start_compress
Packit ed3af9
 * before any data is actually written.
Packit ed3af9
 */
Packit ed3af9
Packit ed3af9
void init_destination(j_compress_ptr cinfo)
Packit ed3af9
{
Packit ed3af9
	my_dest_ptr dest = (my_dest_ptr)cinfo->dest;
Packit ed3af9
Packit ed3af9
	/* Allocate the output buffer --- it will be released when done with image */
Packit ed3af9
	dest->buffer = (unsigned char *)
Packit ed3af9
	               (*cinfo->mem->alloc_small)((j_common_ptr)cinfo, JPOOL_IMAGE,
Packit ed3af9
	                       OUTPUT_BUF_SIZE * sizeof(unsigned char));
Packit ed3af9
Packit ed3af9
	dest->pub.next_output_byte = dest->buffer;
Packit ed3af9
	dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
Packit ed3af9
/*
Packit ed3af9
 * Empty the output buffer --- called whenever buffer fills up.
Packit ed3af9
 *
Packit ed3af9
 * In typical applications, this should write the entire output buffer
Packit ed3af9
 * (ignoring the current state of next_output_byte & free_in_buffer),
Packit ed3af9
 * reset the pointer & count to the start of the buffer, and return TRUE
Packit ed3af9
 * indicating that the buffer has been dumped.
Packit ed3af9
 *
Packit ed3af9
 * In applications that need to be able to suspend compression due to output
Packit ed3af9
 * overrun, a FALSE return indicates that the buffer cannot be emptied now.
Packit ed3af9
 * In this situation, the compressor will return to its caller (possibly with
Packit ed3af9
 * an indication that it has not accepted all the supplied scanlines).  The
Packit ed3af9
 * application should resume compression after it has made more room in the
Packit ed3af9
 * output buffer.  Note that there are substantial restrictions on the use of
Packit ed3af9
 * suspension --- see the documentation.
Packit ed3af9
 *
Packit ed3af9
 * When suspending, the compressor will back up to a convenient restart point
Packit ed3af9
 * (typically the start of the current MCU). next_output_byte & free_in_buffer
Packit ed3af9
 * indicate where the restart point will be if the current call returns FALSE.
Packit ed3af9
 * Data beyond this point will be regenerated after resumption, so do not
Packit ed3af9
 * write it out when emptying the buffer externally.
Packit ed3af9
 */
Packit ed3af9
Packit ed3af9
boolean empty_output_buffer(j_compress_ptr cinfo)
Packit ed3af9
{
Packit ed3af9
	my_dest_ptr dest = (my_dest_ptr)cinfo->dest;
Packit ed3af9
Packit ed3af9
	if(gdPutBuf(dest->buffer, OUTPUT_BUF_SIZE, dest->outfile) != (size_t)OUTPUT_BUF_SIZE) {
Packit ed3af9
		ERREXIT(cinfo, JERR_FILE_WRITE);
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	dest->pub.next_output_byte = dest->buffer;
Packit ed3af9
	dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
Packit ed3af9
Packit ed3af9
	return TRUE;
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
Packit ed3af9
/*
Packit ed3af9
 * Terminate destination --- called by jpeg_finish_compress
Packit ed3af9
 * after all data has been written.  Usually needs to flush buffer.
Packit ed3af9
 *
Packit ed3af9
 * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding
Packit ed3af9
 * application must deal with any cleanup that should happen even
Packit ed3af9
 * for error exit.
Packit ed3af9
 */
Packit ed3af9
Packit ed3af9
void term_destination(j_compress_ptr cinfo)
Packit ed3af9
{
Packit ed3af9
	my_dest_ptr dest = (my_dest_ptr) cinfo->dest;
Packit ed3af9
	int datacount = OUTPUT_BUF_SIZE - dest->pub.free_in_buffer;
Packit ed3af9
Packit ed3af9
	/* Write any data remaining in the buffer */
Packit ed3af9
	if(datacount > 0) {
Packit ed3af9
		if(gdPutBuf(dest->buffer, datacount, dest->outfile) != datacount) {
Packit ed3af9
			ERREXIT(cinfo, JERR_FILE_WRITE);
Packit ed3af9
		}
Packit ed3af9
	}
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
/*
Packit ed3af9
 * Prepare for output to a stdio stream.
Packit ed3af9
 * The caller must have already opened the stream, and is responsible
Packit ed3af9
 * for closing it after finishing compression.
Packit ed3af9
 */
Packit ed3af9
Packit ed3af9
void jpeg_gdIOCtx_dest(j_compress_ptr cinfo, gdIOCtx *outfile)
Packit ed3af9
{
Packit ed3af9
	my_dest_ptr dest;
Packit ed3af9
Packit ed3af9
	/* The destination object is made permanent so that multiple JPEG images
Packit ed3af9
	 * can be written to the same file without re-executing jpeg_stdio_dest.
Packit ed3af9
	 * This makes it dangerous to use this manager and a different destination
Packit ed3af9
	 * manager serially with the same JPEG object, because their private object
Packit ed3af9
	 * sizes may be different.  Caveat programmer.
Packit ed3af9
	 */
Packit ed3af9
	if(cinfo->dest == NULL) {
Packit ed3af9
		/* first time for this JPEG object? */
Packit ed3af9
		cinfo->dest = (struct jpeg_destination_mgr *)
Packit ed3af9
		              (*cinfo->mem->alloc_small)((j_common_ptr)cinfo, JPOOL_PERMANENT,
Packit ed3af9
		                      sizeof(my_destination_mgr));
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	dest = (my_dest_ptr)cinfo->dest;
Packit ed3af9
	dest->pub.init_destination = init_destination;
Packit ed3af9
	dest->pub.empty_output_buffer = empty_output_buffer;
Packit ed3af9
	dest->pub.term_destination = term_destination;
Packit ed3af9
	dest->outfile = outfile;
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
#endif /* HAVE_LIBJPEG */