Blame gdk-pixbuf/io-jpeg.c

Packit a4058c
/* -*- mode: C; c-file-style: "linux" -*- */
Packit a4058c
/* GdkPixbuf library - JPEG image loader
Packit a4058c
 *
Packit a4058c
 * Copyright (C) 1999 Michael Zucchi
Packit a4058c
 * Copyright (C) 1999 The Free Software Foundation
Packit a4058c
 * 
Packit a4058c
 * Progressive loading code Copyright (C) 1999 Red Hat, Inc.
Packit a4058c
 *
Packit a4058c
 * Authors: Michael Zucchi <zucchi@zedzone.mmc.com.au>
Packit a4058c
 *          Federico Mena-Quintero <federico@gimp.org>
Packit a4058c
 *          Michael Fulbright <drmike@redhat.com>
Packit a4058c
 *
Packit a4058c
 * This library is free software; you can redistribute it and/or
Packit a4058c
 * modify it under the terms of the GNU Lesser General Public
Packit a4058c
 * License as published by the Free Software Foundation; either
Packit a4058c
 * version 2 of the License, or (at your option) any later version.
Packit a4058c
 *
Packit a4058c
 * This library is distributed in the hope that it will be useful,
Packit a4058c
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit a4058c
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit a4058c
 * Lesser General Public License for more details.
Packit a4058c
 *
Packit a4058c
 * You should have received a copy of the GNU Lesser General Public
Packit a4058c
 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
Packit a4058c
 */
Packit a4058c
Packit a4058c
Packit a4058c
#include "config.h"
Packit a4058c
#include <stdio.h>
Packit a4058c
#include <stdlib.h>
Packit a4058c
#include <string.h>
Packit a4058c
#include <setjmp.h>
Packit a4058c
#include <jpeglib.h>
Packit a4058c
#include <jerror.h>
Packit a4058c
#include <math.h>
Packit a4058c
Packit a4058c
#include "gdk-pixbuf-private.h"
Packit a4058c
#include "fallback-c89.c"
Packit a4058c
Packit a4058c
#ifndef HAVE_SIGSETJMP
Packit a4058c
#define sigjmp_buf jmp_buf
Packit a4058c
#define sigsetjmp(jb, x) setjmp(jb)
Packit a4058c
#define siglongjmp longjmp
Packit a4058c
#endif
Packit a4058c

Packit a4058c
Packit a4058c
/* we are a "source manager" as far as libjpeg is concerned */
Packit a4058c
#define JPEG_PROG_BUF_SIZE 65536
Packit a4058c
Packit a4058c
typedef struct {
Packit a4058c
	struct jpeg_source_mgr pub;   /* public fields */
Packit a4058c
Packit a4058c
	JOCTET buffer[JPEG_PROG_BUF_SIZE];              /* start of buffer */
Packit a4058c
	long  skip_next;              /* number of bytes to skip next read */
Packit a4058c
	
Packit a4058c
} my_source_mgr;
Packit a4058c
Packit a4058c
typedef my_source_mgr * my_src_ptr;
Packit a4058c
Packit a4058c
/* error handler data */
Packit a4058c
struct error_handler_data {
Packit a4058c
	struct jpeg_error_mgr pub;
Packit a4058c
	sigjmp_buf setjmp_buffer;
Packit a4058c
        GError **error;
Packit a4058c
};
Packit a4058c
Packit a4058c
/* progressive loader context */
Packit a4058c
typedef struct {
Packit a4058c
        GdkPixbufModuleSizeFunc     size_func;
Packit a4058c
	GdkPixbufModuleUpdatedFunc  updated_func;
Packit a4058c
	GdkPixbufModulePreparedFunc prepared_func;
Packit a4058c
	gpointer                    user_data;
Packit a4058c
	
Packit a4058c
	GdkPixbuf                *pixbuf;
Packit a4058c
	guchar                   *dptr;   /* current position in pixbuf */
Packit a4058c
Packit a4058c
	gboolean                 did_prescan;  /* are we in image data yet? */
Packit a4058c
	gboolean                 got_header;  /* have we loaded jpeg header? */
Packit a4058c
	gboolean                 src_initialized;/* TRUE when jpeg lib initialized */
Packit a4058c
	gboolean                 in_output;   /* did we get suspended in an output pass? */
Packit a4058c
	struct jpeg_decompress_struct cinfo;
Packit a4058c
	struct error_handler_data     jerr;
Packit a4058c
} JpegProgContext;
Packit a4058c
Packit a4058c
/* EXIF context */
Packit a4058c
typedef struct {
Packit a4058c
	gint			 orientation;
Packit a4058c
	gchar			*icc_profile;
Packit a4058c
	gsize			 icc_profile_size;
Packit a4058c
	gsize			 icc_profile_size_allocated;
Packit a4058c
} JpegExifContext;
Packit a4058c
Packit a4058c
static GdkPixbuf *gdk_pixbuf__jpeg_image_load (FILE *f, GError **error);
Packit a4058c
static gpointer gdk_pixbuf__jpeg_image_begin_load (GdkPixbufModuleSizeFunc           func0,
Packit a4058c
                                                   GdkPixbufModulePreparedFunc func1, 
Packit a4058c
                                                   GdkPixbufModuleUpdatedFunc func2,
Packit a4058c
                                                   gpointer user_data,
Packit a4058c
                                                   GError **error);
Packit a4058c
static gboolean gdk_pixbuf__jpeg_image_stop_load (gpointer context, GError **error);
Packit a4058c
static gboolean gdk_pixbuf__jpeg_image_load_increment(gpointer context,
Packit a4058c
                                                      const guchar *buf, guint size,
Packit a4058c
                                                      GError **error);
Packit a4058c
Packit a4058c
Packit a4058c
static void
Packit a4058c
fatal_error_handler (j_common_ptr cinfo)
Packit a4058c
{
Packit a4058c
	struct error_handler_data *errmgr;
Packit a4058c
        char buffer[JMSG_LENGTH_MAX];
Packit a4058c
        
Packit a4058c
	errmgr = (struct error_handler_data *) cinfo->err;
Packit a4058c
        
Packit a4058c
        /* Create the message */
Packit a4058c
        (* cinfo->err->format_message) (cinfo, buffer);
Packit a4058c
Packit a4058c
        /* broken check for *error == NULL for robustness against
Packit a4058c
         * crappy JPEG library
Packit a4058c
         */
Packit a4058c
        if (errmgr->error && *errmgr->error == NULL) {
Packit a4058c
                g_set_error (errmgr->error,
Packit a4058c
                             GDK_PIXBUF_ERROR,
Packit a4058c
                             cinfo->err->msg_code == JERR_OUT_OF_MEMORY 
Packit a4058c
			     ? GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY 
Packit a4058c
			     : GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
Packit a4058c
                             _("Error interpreting JPEG image file (%s)"),
Packit a4058c
                             buffer);
Packit a4058c
        }
Packit a4058c
        
Packit a4058c
	siglongjmp (errmgr->setjmp_buffer, 1);
Packit a4058c
Packit a4058c
        g_assert_not_reached ();
Packit a4058c
}
Packit a4058c
Packit a4058c
static void
Packit a4058c
output_message_handler (j_common_ptr cinfo)
Packit a4058c
{
Packit a4058c
  /* This method keeps libjpeg from dumping crap to stderr */
Packit a4058c
Packit a4058c
  /* do nothing */
Packit a4058c
}
Packit a4058c
Packit a4058c
/* explode gray image data from jpeg library into rgb components in pixbuf */
Packit a4058c
static void
Packit a4058c
explode_gray_into_buf (struct jpeg_decompress_struct *cinfo,
Packit a4058c
		       guchar **lines) 
Packit a4058c
{
Packit a4058c
	gint i, j;
Packit a4058c
	guint w;
Packit a4058c
Packit a4058c
	g_return_if_fail (cinfo != NULL);
Packit a4058c
	g_return_if_fail (cinfo->output_components == 1);
Packit a4058c
	g_return_if_fail (cinfo->out_color_space == JCS_GRAYSCALE);
Packit a4058c
Packit a4058c
	/* Expand grey->colour.  Expand from the end of the
Packit a4058c
	 * memory down, so we can use the same buffer.
Packit a4058c
	 */
Packit a4058c
	w = cinfo->output_width;
Packit a4058c
	for (i = cinfo->rec_outbuf_height - 1; i >= 0; i--) {
Packit a4058c
		guchar *from, *to;
Packit a4058c
		
Packit a4058c
		from = lines[i] + w - 1;
Packit a4058c
		to = lines[i] + (w - 1) * 3;
Packit a4058c
		for (j = w - 1; j >= 0; j--) {
Packit a4058c
			to[0] = from[0];
Packit a4058c
			to[1] = from[0];
Packit a4058c
			to[2] = from[0];
Packit a4058c
			to -= 3;
Packit a4058c
			from--;
Packit a4058c
		}
Packit a4058c
	}
Packit a4058c
}
Packit a4058c
Packit a4058c
Packit a4058c
static void
Packit a4058c
convert_cmyk_to_rgb (struct jpeg_decompress_struct *cinfo,
Packit a4058c
		     guchar **lines) 
Packit a4058c
{
Packit a4058c
	gint i, j;
Packit a4058c
Packit a4058c
	g_return_if_fail (cinfo != NULL);
Packit a4058c
	g_return_if_fail (cinfo->output_components == 4);
Packit a4058c
	g_return_if_fail (cinfo->out_color_space == JCS_CMYK);
Packit a4058c
Packit a4058c
	for (i = cinfo->rec_outbuf_height - 1; i >= 0; i--) {
Packit a4058c
		guchar *p;
Packit a4058c
		
Packit a4058c
		p = lines[i];
Packit a4058c
		for (j = 0; j < cinfo->output_width; j++) {
Packit a4058c
			int c, m, y, k;
Packit a4058c
			c = p[0];
Packit a4058c
			m = p[1];
Packit a4058c
			y = p[2];
Packit a4058c
			k = p[3];
Packit a4058c
Packit a4058c
			/* We now assume that all CMYK JPEG files
Packit a4058c
			 * use inverted CMYK, as Photoshop does
Packit a4058c
			 * See https://bugzilla.gnome.org/show_bug.cgi?id=618096 */
Packit a4058c
			p[0] = k*c / 255;
Packit a4058c
			p[1] = k*m / 255;
Packit a4058c
			p[2] = k*y / 255;
Packit a4058c
			p[3] = 255;
Packit a4058c
			p += 4;
Packit a4058c
		}
Packit a4058c
	}
Packit a4058c
}
Packit a4058c
Packit a4058c
typedef struct {
Packit a4058c
  struct jpeg_source_mgr pub;	/* public fields */
Packit a4058c
Packit a4058c
  FILE * infile;		/* source stream */
Packit a4058c
  JOCTET * buffer;		/* start of buffer */
Packit a4058c
  boolean start_of_file;	/* have we gotten any data yet? */
Packit a4058c
} stdio_source_mgr;
Packit a4058c
Packit a4058c
typedef stdio_source_mgr * stdio_src_ptr;
Packit a4058c
Packit a4058c
static void
Packit a4058c
stdio_init_source (j_decompress_ptr cinfo)
Packit a4058c
{
Packit a4058c
  stdio_src_ptr src = (stdio_src_ptr)cinfo->src;
Packit a4058c
  src->start_of_file = FALSE;
Packit a4058c
}
Packit a4058c
Packit a4058c
static boolean
Packit a4058c
stdio_fill_input_buffer (j_decompress_ptr cinfo)
Packit a4058c
{
Packit a4058c
  stdio_src_ptr src = (stdio_src_ptr) cinfo->src;
Packit a4058c
  size_t nbytes;
Packit a4058c
Packit a4058c
  nbytes = fread (src->buffer, 1, JPEG_PROG_BUF_SIZE, src->infile);
Packit a4058c
Packit a4058c
  if (nbytes <= 0) {
Packit a4058c
#if 0
Packit a4058c
    if (src->start_of_file)	/* Treat empty input file as fatal error */
Packit a4058c
      ERREXIT(cinfo, JERR_INPUT_EMPTY);
Packit a4058c
    WARNMS(cinfo, JWRN_JPEG_EOF);
Packit a4058c
#endif
Packit a4058c
    /* Insert a fake EOI marker */
Packit a4058c
    src->buffer[0] = (JOCTET) 0xFF;
Packit a4058c
    src->buffer[1] = (JOCTET) JPEG_EOI;
Packit a4058c
    nbytes = 2;
Packit a4058c
  }
Packit a4058c
Packit a4058c
  src->pub.next_input_byte = src->buffer;
Packit a4058c
  src->pub.bytes_in_buffer = nbytes;
Packit a4058c
  src->start_of_file = FALSE;
Packit a4058c
Packit a4058c
  return TRUE;
Packit a4058c
}
Packit a4058c
Packit a4058c
static void
Packit a4058c
stdio_skip_input_data (j_decompress_ptr cinfo, long num_bytes)
Packit a4058c
{
Packit a4058c
  stdio_src_ptr src = (stdio_src_ptr) cinfo->src;
Packit a4058c
Packit a4058c
  if (num_bytes > 0) {
Packit a4058c
    while (num_bytes > (long) src->pub.bytes_in_buffer) {
Packit a4058c
      num_bytes -= (long) src->pub.bytes_in_buffer;
Packit a4058c
      (void)stdio_fill_input_buffer(cinfo);
Packit a4058c
    }
Packit a4058c
    src->pub.next_input_byte += (size_t) num_bytes;
Packit a4058c
    src->pub.bytes_in_buffer -= (size_t) num_bytes;
Packit a4058c
  }
Packit a4058c
}
Packit a4058c
Packit a4058c
static void
Packit a4058c
stdio_term_source (j_decompress_ptr cinfo)
Packit a4058c
{
Packit a4058c
}
Packit a4058c
Packit a4058c
static gchar *
Packit a4058c
colorspace_name (const J_COLOR_SPACE jpeg_color_space) 
Packit a4058c
{
Packit a4058c
	switch (jpeg_color_space) {
Packit a4058c
	    case JCS_UNKNOWN: return "UNKNOWN"; 
Packit a4058c
	    case JCS_GRAYSCALE: return "GRAYSCALE"; 
Packit a4058c
	    case JCS_RGB: return "RGB"; 
Packit a4058c
	    case JCS_YCbCr: return "YCbCr"; 
Packit a4058c
	    case JCS_CMYK: return "CMYK"; 
Packit a4058c
	    case JCS_YCCK: return "YCCK";
Packit a4058c
	    default: return "invalid";
Packit a4058c
	}
Packit a4058c
}
Packit a4058c
Packit a4058c
#define DE_ENDIAN16(val) endian == G_BIG_ENDIAN ? GUINT16_FROM_BE(val) : GUINT16_FROM_LE(val)
Packit a4058c
#define DE_ENDIAN32(val) endian == G_BIG_ENDIAN ? GUINT32_FROM_BE(val) : GUINT32_FROM_LE(val)
Packit a4058c
Packit a4058c
#define ENDIAN16_IT(val) endian == G_BIG_ENDIAN ? GUINT16_TO_BE(val) : GUINT16_TO_LE(val)
Packit a4058c
#define ENDIAN32_IT(val) endian == G_BIG_ENDIAN ? GUINT32_TO_BE(val) : GUINT32_TO_LE(val)
Packit a4058c
Packit a4058c
static unsigned short de_get16(void *ptr, guint endian)
Packit a4058c
{
Packit a4058c
       unsigned short val;
Packit a4058c
Packit a4058c
       memcpy(&val, ptr, sizeof(val));
Packit a4058c
       val = DE_ENDIAN16(val);
Packit a4058c
Packit a4058c
       return val;
Packit a4058c
}
Packit a4058c
Packit a4058c
static unsigned int de_get32(void *ptr, guint endian)
Packit a4058c
{
Packit a4058c
       unsigned int val;
Packit a4058c
Packit a4058c
       memcpy(&val, ptr, sizeof(val));
Packit a4058c
       val = DE_ENDIAN32(val);
Packit a4058c
Packit a4058c
       return val;
Packit a4058c
}
Packit a4058c
Packit a4058c
/* application specific data segment */
Packit a4058c
static gboolean
Packit a4058c
jpeg_parse_exif_app2_segment (JpegExifContext *context, jpeg_saved_marker_ptr marker)
Packit a4058c
{
Packit a4058c
	guint ret = FALSE;
Packit a4058c
	guint sequence_number;
Packit a4058c
	guint number_of_chunks;
Packit a4058c
	guint chunk_size;
Packit a4058c
	guint offset;
Packit a4058c
Packit a4058c
	/* do we have enough data? */
Packit a4058c
	if (marker->data_length < 16)
Packit a4058c
		goto out;
Packit a4058c
Packit a4058c
	/* unique identification string */
Packit a4058c
	if (memcmp (marker->data, "ICC_PROFILE\0", 12) != 0)
Packit a4058c
		goto out;
Packit a4058c
Packit a4058c
	/* get data about this segment */
Packit a4058c
	sequence_number = marker->data[12];
Packit a4058c
	number_of_chunks = marker->data[13];
Packit a4058c
Packit a4058c
	/* this is invalid, the base offset is 1 */
Packit a4058c
	if (sequence_number == 0)
Packit a4058c
		goto out;
Packit a4058c
Packit a4058c
	/* this is invalid, the base offset is 1 */
Packit a4058c
	if (sequence_number > number_of_chunks)
Packit a4058c
		goto out;
Packit a4058c
Packit a4058c
	/* size includes the id (12 bytes), length field (1 byte), and sequence field (1 byte) */
Packit a4058c
	chunk_size = marker->data_length - 14;
Packit a4058c
	offset = (sequence_number - 1) * 0xffef;
Packit a4058c
Packit a4058c
	/* Deal with the trivial profile (99% of images) to avoid allocating
Packit a4058c
	 * 64kb when we might only use a few kb. */
Packit a4058c
	if (number_of_chunks == 1) {
Packit a4058c
		if (context->icc_profile_size_allocated > 0)
Packit a4058c
			goto out;
Packit a4058c
		context->icc_profile_size = chunk_size;
Packit a4058c
		context->icc_profile_size_allocated = chunk_size;
Packit a4058c
		context->icc_profile = g_new (gchar, chunk_size);
Packit a4058c
		/* copy the segment data to the profile space */
Packit a4058c
		memcpy (context->icc_profile, marker->data + 14, chunk_size);
Packit a4058c
		goto out;
Packit a4058c
	}
Packit a4058c
Packit a4058c
	/* There is no promise the APP2 segments are going to be in order, so we
Packit a4058c
	 * have to allocate a huge swathe of memory and fill in the gaps when
Packit a4058c
	 * (if) we get the segment.
Packit a4058c
	 * Theoretically this could be as much as 16Mb, but display profiles are
Packit a4058c
	 * vary rarely above 100kb, and printer profiles are usually less than
Packit a4058c
	 * 2Mb */
Packit a4058c
	if (context->icc_profile_size_allocated == 0) {
Packit a4058c
		context->icc_profile_size_allocated = number_of_chunks * 0xffff;
Packit a4058c
		context->icc_profile = g_new0 (gchar, number_of_chunks * 0xffff);
Packit a4058c
	}
Packit a4058c
Packit a4058c
	/* check the data will fit in our previously allocated buffer */
Packit a4058c
	if (offset + chunk_size > context->icc_profile_size_allocated)
Packit a4058c
		goto out;
Packit a4058c
Packit a4058c
	/* copy the segment data to the profile space */
Packit a4058c
	memcpy (context->icc_profile + offset, marker->data + 14, chunk_size);
Packit a4058c
Packit a4058c
	/* it's now this big plus the new data we've just copied */
Packit a4058c
	context->icc_profile_size += chunk_size;
Packit a4058c
Packit a4058c
	/* success */
Packit a4058c
	ret = TRUE;
Packit a4058c
out:
Packit a4058c
	return ret;
Packit a4058c
}
Packit a4058c
Packit a4058c
static gboolean
Packit a4058c
jpeg_parse_exif_app1 (JpegExifContext *context, jpeg_saved_marker_ptr marker)
Packit a4058c
{
Packit a4058c
	guint i;
Packit a4058c
	guint ret = FALSE;
Packit a4058c
	guint offset;
Packit a4058c
	guint tags;	   /* number of tags in current ifd */
Packit a4058c
	guint endian = 0;	/* detected endian of data */
Packit a4058c
	const char leth[]  = {0x49, 0x49, 0x2a, 0x00};	// Little endian TIFF header
Packit a4058c
	const char beth[]  = {0x4d, 0x4d, 0x00, 0x2a};	// Big endian TIFF header
Packit a4058c
Packit a4058c
	/* do we have enough data? */
Packit a4058c
	if (marker->data_length < 4)
Packit a4058c
		goto out;
Packit a4058c
Packit a4058c
	/* unique identification string */
Packit a4058c
	if (memcmp (marker->data, "Exif", 4) != 0)
Packit a4058c
		goto out;
Packit a4058c
Packit a4058c
	/* do we have enough data? */
Packit a4058c
	if (marker->data_length < 32)
Packit a4058c
		goto out;
Packit a4058c
Packit a4058c
	/* Just skip data until TIFF header - it should be within 16 bytes from marker start.
Packit a4058c
	   Normal structure relative to APP1 marker -
Packit a4058c
		0x0000: APP1 marker entry = 2 bytes
Packit a4058c
		0x0002: APP1 length entry = 2 bytes
Packit a4058c
		0x0004: Exif Identifier entry = 6 bytes
Packit a4058c
		0x000A: Start of TIFF header (Byte order entry) - 4 bytes
Packit a4058c
			- This is what we look for, to determine endianess.
Packit a4058c
		0x000E: 0th IFD offset pointer - 4 bytes
Packit a4058c
Packit a4058c
		marker->data points to the first data after the APP1 marker
Packit a4058c
		and length entries, which is the exif identification string.
Packit a4058c
		The TIFF header should thus normally be found at i=6, below,
Packit a4058c
		and the pointer to IFD0 will be at 6+4 = 10.
Packit a4058c
	*/
Packit a4058c
Packit a4058c
	for (i=0; i<16; i++) {
Packit a4058c
		/* little endian TIFF header */
Packit a4058c
		if (memcmp (&marker->data[i], leth, 4) == 0) {
Packit a4058c
			endian = G_LITTLE_ENDIAN;
Packit a4058c
			ret = TRUE;
Packit a4058c
			break;
Packit a4058c
		}
Packit a4058c
Packit a4058c
		/* big endian TIFF header */
Packit a4058c
		if (memcmp (&marker->data[i], beth, 4) == 0) {
Packit a4058c
			endian = G_BIG_ENDIAN;
Packit a4058c
			ret = TRUE;
Packit a4058c
			break;
Packit a4058c
		}
Packit a4058c
	}
Packit a4058c
Packit a4058c
	/* could not find header */
Packit a4058c
	if (!ret)
Packit a4058c
		goto out;
Packit a4058c
Packit a4058c
	/* read out the offset pointer to IFD0 */
Packit a4058c
	offset  = de_get32(&marker->data[i] + 4, endian);
Packit a4058c
	i = i + offset;
Packit a4058c
Packit a4058c
	/* check that we still are within the buffer and can read the tag count */
Packit a4058c
	{
Packit a4058c
	    const size_t new_i = i + 2;
Packit a4058c
	    if (new_i < i || new_i > marker->data_length) {
Packit a4058c
		    ret = FALSE;
Packit a4058c
		    goto out;
Packit a4058c
	    }
Packit a4058c
Packit a4058c
	    /* find out how many tags we have in IFD0. As per the TIFF spec, the first
Packit a4058c
	       two bytes of the IFD contain a count of the number of tags. */
Packit a4058c
	    tags = de_get16(&marker->data[i], endian);
Packit a4058c
	    i = new_i;
Packit a4058c
	}
Packit a4058c
Packit a4058c
	/* check that we still have enough data for all tags to check. The tags
Packit a4058c
	   are listed in consecutive 12-byte blocks. The tag ID, type, size, and
Packit a4058c
	   a pointer to the actual value, are packed into these 12 byte entries. */
Packit a4058c
	{
Packit a4058c
	    const size_t new_i = i + tags * 12;
Packit a4058c
	    if (new_i < i || new_i > marker->data_length) {
Packit a4058c
		ret = FALSE;
Packit a4058c
		goto out;
Packit a4058c
	    }
Packit a4058c
	}
Packit a4058c
Packit a4058c
	/* check through IFD0 for tags */
Packit a4058c
	while (tags--) {
Packit a4058c
		size_t new_i;
Packit a4058c
Packit a4058c
		/* We check for integer overflow before the loop and
Packit a4058c
		 * at the end of each iteration */
Packit a4058c
		guint tag   = de_get16(&marker->data[i + 0], endian);
Packit a4058c
		guint type  = de_get16(&marker->data[i + 2], endian);
Packit a4058c
		guint count = de_get32(&marker->data[i + 4], endian);
Packit a4058c
Packit a4058c
		/* orientation tag? */
Packit a4058c
		if (tag == 0x112){
Packit a4058c
Packit a4058c
			/* The orientation field should consist of a single 2-byte integer,
Packit a4058c
			 * but might be a signed long.
Packit a4058c
			 * Values of types smaller than 4 bytes are stored directly in the
Packit a4058c
			 * Value Offset field */
Packit a4058c
			if (type == 0x3 && count == 1) {
Packit a4058c
				guint short_value = de_get16(&marker->data[i + 8], endian);
Packit a4058c
Packit a4058c
				context->orientation = short_value <= 8 ? short_value : 0;
Packit a4058c
			} else if (type == 0x9 && count == 1) {
Packit a4058c
				guint long_value = de_get32(&marker->data[i + 8], endian);
Packit a4058c
Packit a4058c
				context->orientation = long_value <= 8 ? long_value : 0;
Packit a4058c
			}
Packit a4058c
		}
Packit a4058c
		/* move the pointer to the next 12-byte tag field. */
Packit a4058c
		new_i = i + 12;
Packit a4058c
		if (new_i < i || new_i > marker->data_length) {
Packit a4058c
			ret = FALSE;
Packit a4058c
			goto out;
Packit a4058c
		}
Packit a4058c
		i = new_i;
Packit a4058c
	}
Packit a4058c
Packit a4058c
out:
Packit a4058c
	return ret;
Packit a4058c
}
Packit a4058c
Packit a4058c
static void
Packit a4058c
jpeg_parse_exif (JpegExifContext *context, j_decompress_ptr cinfo)
Packit a4058c
{
Packit a4058c
	jpeg_saved_marker_ptr cmarker;
Packit a4058c
Packit a4058c
	/* check for interesting Exif markers */
Packit a4058c
	cmarker = cinfo->marker_list;
Packit a4058c
	while (cmarker != NULL) {
Packit a4058c
		if (cmarker->marker == JPEG_APP0+1)
Packit a4058c
			jpeg_parse_exif_app1 (context, cmarker);
Packit a4058c
		else if (cmarker->marker == JPEG_APP0+2)
Packit a4058c
			jpeg_parse_exif_app2_segment (context, cmarker);
Packit a4058c
		cmarker = cmarker->next;
Packit a4058c
	}
Packit a4058c
}
Packit a4058c
Packit a4058c
static gchar *
Packit a4058c
jpeg_get_comment (j_decompress_ptr cinfo)
Packit a4058c
{
Packit a4058c
	jpeg_saved_marker_ptr cmarker;
Packit a4058c
Packit a4058c
	cmarker = cinfo->marker_list;
Packit a4058c
	while (cmarker != NULL) {
Packit a4058c
		if (cmarker->marker == JPEG_COM)
Packit a4058c
			return g_strndup ((const gchar *) cmarker->data, cmarker->data_length);
Packit a4058c
		cmarker = cmarker->next;
Packit a4058c
	}
Packit a4058c
Packit a4058c
	return NULL;
Packit a4058c
}
Packit a4058c
Packit a4058c
static void
Packit a4058c
jpeg_destroy_exif_context (JpegExifContext *context)
Packit a4058c
{
Packit a4058c
	g_free (context->icc_profile);
Packit a4058c
}
Packit a4058c
Packit a4058c
/* Shared library entry point */
Packit a4058c
static GdkPixbuf *
Packit a4058c
gdk_pixbuf__jpeg_image_load (FILE *f, GError **error)
Packit a4058c
{
Packit a4058c
	gint   i;
Packit a4058c
	char   otag_str[5];
Packit a4058c
	char  *density_str;
Packit a4058c
	GdkPixbuf * volatile pixbuf = NULL;
Packit a4058c
	guchar *dptr;
Packit a4058c
	guchar *lines[4]; /* Used to expand rows, via rec_outbuf_height, 
Packit a4058c
                           * from the header file: 
Packit a4058c
                           * " Usually rec_outbuf_height will be 1 or 2, 
Packit a4058c
                           * at most 4."
Packit a4058c
			   */
Packit a4058c
	guchar **lptr;
Packit a4058c
	struct jpeg_decompress_struct cinfo;
Packit a4058c
	struct error_handler_data jerr;
Packit a4058c
	stdio_src_ptr src;
Packit a4058c
	gchar *icc_profile_base64;
Packit a4058c
	gchar *comment;
Packit a4058c
	JpegExifContext exif_context = { 0, };
Packit a4058c
Packit a4058c
	/* setup error handler */
Packit a4058c
	cinfo.err = jpeg_std_error (&jerr.pub);
Packit a4058c
	jerr.pub.error_exit = fatal_error_handler;
Packit a4058c
        jerr.pub.output_message = output_message_handler;
Packit a4058c
        jerr.error = error;
Packit a4058c
        
Packit a4058c
	if (sigsetjmp (jerr.setjmp_buffer, 1)) {
Packit a4058c
		/* Whoops there was a jpeg error */
Packit a4058c
		if (pixbuf)
Packit a4058c
			g_object_unref (pixbuf);
Packit a4058c
Packit a4058c
		jpeg_destroy_decompress (&cinfo);
Packit a4058c
		jpeg_destroy_exif_context (&exif_context);
Packit a4058c
Packit a4058c
		/* error should have been set by fatal_error_handler () */
Packit a4058c
		return NULL;
Packit a4058c
	}
Packit a4058c
Packit a4058c
	/* load header, setup */
Packit a4058c
	jpeg_create_decompress (&cinfo);
Packit a4058c
Packit a4058c
	cinfo.src = (struct jpeg_source_mgr *)
Packit a4058c
	  (*cinfo.mem->alloc_small) ((j_common_ptr) &cinfo, JPOOL_PERMANENT,
Packit a4058c
				  sizeof (stdio_source_mgr));
Packit a4058c
	src = (stdio_src_ptr) cinfo.src;
Packit a4058c
	src->buffer = (JOCTET *)
Packit a4058c
	  (*cinfo.mem->alloc_small) ((j_common_ptr) &cinfo, JPOOL_PERMANENT,
Packit a4058c
				      JPEG_PROG_BUF_SIZE * sizeof (JOCTET));
Packit a4058c
Packit a4058c
	src->pub.init_source = stdio_init_source;
Packit a4058c
	src->pub.fill_input_buffer = stdio_fill_input_buffer;
Packit a4058c
	src->pub.skip_input_data = stdio_skip_input_data;
Packit a4058c
	src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */
Packit a4058c
	src->pub.term_source = stdio_term_source;
Packit a4058c
	src->infile = f;
Packit a4058c
	src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */
Packit a4058c
	src->pub.next_input_byte = NULL; /* until buffer loaded */
Packit a4058c
Packit a4058c
	jpeg_save_markers (&cinfo, JPEG_APP0+1, 0xffff);
Packit a4058c
	jpeg_save_markers (&cinfo, JPEG_APP0+2, 0xffff);
Packit a4058c
	jpeg_save_markers (&cinfo, JPEG_COM, 0xffff);
Packit a4058c
	jpeg_read_header (&cinfo, TRUE);
Packit a4058c
Packit a4058c
	/* parse exif data */
Packit a4058c
	jpeg_parse_exif (&exif_context, &cinfo);
Packit a4058c
	
Packit a4058c
	jpeg_start_decompress (&cinfo);
Packit a4058c
	cinfo.do_fancy_upsampling = FALSE;
Packit a4058c
	cinfo.do_block_smoothing = FALSE;
Packit a4058c
Packit a4058c
	pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, 
Packit a4058c
				 cinfo.out_color_components == 4 ? TRUE : FALSE, 
Packit a4058c
				 8, cinfo.output_width, cinfo.output_height);
Packit a4058c
	      
Packit a4058c
	if (!pixbuf) {
Packit a4058c
                /* broken check for *error == NULL for robustness against
Packit a4058c
                 * crappy JPEG library
Packit a4058c
                 */
Packit a4058c
                if (error && *error == NULL) {
Packit a4058c
                        g_set_error_literal (error,
Packit a4058c
                                             GDK_PIXBUF_ERROR,
Packit a4058c
                                             GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
Packit a4058c
                                             _("Insufficient memory to load image, try exiting some applications to free memory"));
Packit a4058c
                }
Packit a4058c
               
Packit a4058c
		goto out; 
Packit a4058c
	}
Packit a4058c
Packit a4058c
	comment = jpeg_get_comment (&cinfo);
Packit a4058c
	if (comment != NULL) {
Packit a4058c
		gdk_pixbuf_set_option (pixbuf, "comment", comment);
Packit a4058c
		g_free (comment);
Packit a4058c
	}
Packit a4058c
Packit a4058c
	switch (cinfo.density_unit) {
Packit a4058c
	case 1:
Packit a4058c
		/* Dots per inch (no conversion required) */
Packit a4058c
		density_str = g_strdup_printf ("%d", cinfo.X_density);
Packit a4058c
		gdk_pixbuf_set_option (pixbuf, "x-dpi", density_str);
Packit a4058c
		g_free (density_str);
Packit a4058c
		density_str = g_strdup_printf ("%d", cinfo.Y_density);
Packit a4058c
		gdk_pixbuf_set_option (pixbuf, "y-dpi", density_str);
Packit a4058c
		g_free (density_str);
Packit a4058c
		break;
Packit a4058c
	case 2:
Packit a4058c
		/* Dots per cm - convert into dpi */
Packit a4058c
		density_str = g_strdup_printf ("%d", DPCM_TO_DPI (cinfo.X_density));
Packit a4058c
		gdk_pixbuf_set_option (pixbuf, "x-dpi", density_str);
Packit a4058c
		g_free (density_str);
Packit a4058c
		density_str = g_strdup_printf ("%d", DPCM_TO_DPI (cinfo.Y_density));
Packit a4058c
		gdk_pixbuf_set_option (pixbuf, "y-dpi", density_str);
Packit a4058c
		g_free (density_str);
Packit a4058c
		break;
Packit a4058c
	}
Packit a4058c
Packit a4058c
	/* if orientation tag was found */
Packit a4058c
	if (exif_context.orientation != 0) {
Packit a4058c
		g_snprintf (otag_str, sizeof (otag_str), "%d", exif_context.orientation);
Packit a4058c
		gdk_pixbuf_set_option (pixbuf, "orientation", otag_str);
Packit a4058c
	}
Packit a4058c
Packit a4058c
	/* if icc profile was found */
Packit a4058c
	if (exif_context.icc_profile != NULL) {
Packit a4058c
		icc_profile_base64 = g_base64_encode ((const guchar *) exif_context.icc_profile, exif_context.icc_profile_size);
Packit a4058c
		gdk_pixbuf_set_option (pixbuf, "icc-profile", icc_profile_base64);
Packit a4058c
		g_free (icc_profile_base64);
Packit a4058c
	}
Packit a4058c
Packit a4058c
	dptr = pixbuf->pixels;
Packit a4058c
Packit a4058c
	/* decompress all the lines, a few at a time */
Packit a4058c
	while (cinfo.output_scanline < cinfo.output_height) {
Packit a4058c
		lptr = lines;
Packit a4058c
		for (i = 0; i < cinfo.rec_outbuf_height; i++) {
Packit a4058c
			*lptr++ = dptr;
Packit a4058c
			dptr += pixbuf->rowstride;
Packit a4058c
		}
Packit a4058c
Packit a4058c
		jpeg_read_scanlines (&cinfo, lines, cinfo.rec_outbuf_height);
Packit a4058c
Packit a4058c
		switch (cinfo.out_color_space) {
Packit a4058c
		    case JCS_GRAYSCALE:
Packit a4058c
		      explode_gray_into_buf (&cinfo, lines);
Packit a4058c
		      break;
Packit a4058c
		    case JCS_RGB:
Packit a4058c
		      /* do nothing */
Packit a4058c
		      break;
Packit a4058c
		    case JCS_CMYK:
Packit a4058c
		      convert_cmyk_to_rgb (&cinfo, lines);
Packit a4058c
		      break;
Packit a4058c
		    default:
Packit a4058c
		      g_clear_object (&pixbuf);
Packit a4058c
                      g_set_error (error,
Packit a4058c
                                   GDK_PIXBUF_ERROR,
Packit a4058c
				   GDK_PIXBUF_ERROR_UNKNOWN_TYPE,
Packit a4058c
				   _("Unsupported JPEG color space (%s)"),
Packit a4058c
				   colorspace_name (cinfo.out_color_space));
Packit a4058c
		      goto out;
Packit a4058c
		}
Packit a4058c
	}
Packit a4058c
Packit a4058c
out:
Packit a4058c
	jpeg_finish_decompress (&cinfo);
Packit a4058c
	jpeg_destroy_decompress (&cinfo);
Packit a4058c
	jpeg_destroy_exif_context (&exif_context);
Packit a4058c
Packit a4058c
	return pixbuf;
Packit a4058c
}
Packit a4058c
Packit a4058c
Packit a4058c
/**** Progressive image loading handling *****/
Packit a4058c
Packit a4058c
/* these routines required because we are acting as a source manager for */
Packit a4058c
/* libjpeg. */
Packit a4058c
static void
Packit a4058c
init_source (j_decompress_ptr cinfo)
Packit a4058c
{
Packit a4058c
	my_src_ptr src = (my_src_ptr) cinfo->src;
Packit a4058c
Packit a4058c
	src->skip_next = 0;
Packit a4058c
}
Packit a4058c
Packit a4058c
Packit a4058c
static void
Packit a4058c
term_source (j_decompress_ptr cinfo)
Packit a4058c
{
Packit a4058c
	/* XXXX - probably should scream something has happened */
Packit a4058c
}
Packit a4058c
Packit a4058c
Packit a4058c
/* for progressive loading (called "I/O Suspension" by libjpeg docs) */
Packit a4058c
/* we do nothing except return "FALSE"                               */
Packit a4058c
static boolean
Packit a4058c
fill_input_buffer (j_decompress_ptr cinfo)
Packit a4058c
{
Packit a4058c
	return FALSE;
Packit a4058c
}
Packit a4058c
Packit a4058c
Packit a4058c
static void
Packit a4058c
skip_input_data (j_decompress_ptr cinfo, long num_bytes)
Packit a4058c
{
Packit a4058c
	my_src_ptr src = (my_src_ptr) cinfo->src;
Packit a4058c
	long   num_can_do;
Packit a4058c
Packit a4058c
	/* move as far as we can into current buffer */
Packit a4058c
	/* then set skip_next to catch the rest      */
Packit a4058c
	if (num_bytes > 0) {
Packit a4058c
		num_can_do = MIN (src->pub.bytes_in_buffer, num_bytes);
Packit a4058c
		src->pub.next_input_byte += (size_t) num_can_do;
Packit a4058c
		src->pub.bytes_in_buffer -= (size_t) num_can_do;
Packit a4058c
Packit a4058c
		src->skip_next = num_bytes - num_can_do;
Packit a4058c
	}
Packit a4058c
}
Packit a4058c
Packit a4058c
 
Packit a4058c
/* 
Packit a4058c
 * func - called when we have pixmap created (but no image data)
Packit a4058c
 * user_data - passed as arg 1 to func
Packit a4058c
 * return context (opaque to user)
Packit a4058c
 */
Packit a4058c
Packit a4058c
static gpointer
Packit a4058c
gdk_pixbuf__jpeg_image_begin_load (GdkPixbufModuleSizeFunc size_func,
Packit a4058c
				   GdkPixbufModulePreparedFunc prepared_func, 
Packit a4058c
				   GdkPixbufModuleUpdatedFunc updated_func,
Packit a4058c
				   gpointer user_data,
Packit a4058c
                                   GError **error)
Packit a4058c
{
Packit a4058c
	JpegProgContext *context;
Packit a4058c
	my_source_mgr   *src;
Packit a4058c
Packit a4058c
	context = g_new0 (JpegProgContext, 1);
Packit a4058c
	context->size_func = size_func;
Packit a4058c
	context->prepared_func = prepared_func;
Packit a4058c
	context->updated_func  = updated_func;
Packit a4058c
	context->user_data = user_data;
Packit a4058c
	context->pixbuf = NULL;
Packit a4058c
	context->got_header = FALSE;
Packit a4058c
	context->did_prescan = FALSE;
Packit a4058c
	context->src_initialized = FALSE;
Packit a4058c
	context->in_output = FALSE;
Packit a4058c
Packit a4058c
        /* From jpeglib.h: "NB: you must set up the error-manager
Packit a4058c
         * BEFORE calling jpeg_create_xxx". */
Packit a4058c
	context->cinfo.err = jpeg_std_error (&context->jerr.pub);
Packit a4058c
	context->jerr.pub.error_exit = fatal_error_handler;
Packit a4058c
        context->jerr.pub.output_message = output_message_handler;
Packit a4058c
        context->jerr.error = error;
Packit a4058c
Packit a4058c
        if (sigsetjmp (context->jerr.setjmp_buffer, 1)) {
Packit a4058c
                jpeg_destroy_decompress (&context->cinfo);
Packit a4058c
                g_free(context);
Packit a4058c
                /* error should have been set by fatal_error_handler () */
Packit a4058c
                return NULL;
Packit a4058c
        }
Packit a4058c
Packit a4058c
	/* create libjpeg structures */
Packit a4058c
	jpeg_create_decompress (&context->cinfo);
Packit a4058c
Packit a4058c
	context->cinfo.src = (struct jpeg_source_mgr *) g_try_malloc (sizeof (my_source_mgr));
Packit a4058c
	if (!context->cinfo.src) {
Packit a4058c
		g_set_error_literal (error,
Packit a4058c
                                     GDK_PIXBUF_ERROR,
Packit a4058c
                                     GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
Packit a4058c
                                     _("Couldn't allocate memory for loading JPEG file"));
Packit a4058c
		return NULL;
Packit a4058c
	}
Packit a4058c
	memset (context->cinfo.src, 0, sizeof (my_source_mgr));
Packit a4058c
Packit a4058c
	src = (my_src_ptr) context->cinfo.src;
Packit a4058c
	src->pub.init_source = init_source;
Packit a4058c
	src->pub.fill_input_buffer = fill_input_buffer;
Packit a4058c
	src->pub.skip_input_data = skip_input_data;
Packit a4058c
	src->pub.resync_to_restart = jpeg_resync_to_restart;
Packit a4058c
	src->pub.term_source = term_source;
Packit a4058c
	src->pub.bytes_in_buffer = 0;
Packit a4058c
	src->pub.next_input_byte = NULL;
Packit a4058c
Packit a4058c
        context->jerr.error = NULL;
Packit a4058c
        
Packit a4058c
	return (gpointer) context;
Packit a4058c
}
Packit a4058c
Packit a4058c
/*
Packit a4058c
 * context - returned from image_begin_load
Packit a4058c
 *
Packit a4058c
 * free context, unref gdk_pixbuf
Packit a4058c
 */
Packit a4058c
static gboolean
Packit a4058c
gdk_pixbuf__jpeg_image_stop_load (gpointer data, GError **error)
Packit a4058c
{
Packit a4058c
	JpegProgContext *context = (JpegProgContext *) data;
Packit a4058c
        gboolean retval;
Packit a4058c
Packit a4058c
	g_return_val_if_fail (context != NULL, TRUE);
Packit a4058c
	
Packit a4058c
        /* FIXME this thing needs to report errors if
Packit a4058c
         * we have unused image data
Packit a4058c
         */
Packit a4058c
        
Packit a4058c
	if (context->pixbuf)
Packit a4058c
		g_object_unref (context->pixbuf);
Packit a4058c
	
Packit a4058c
	/* if we have an error? */
Packit a4058c
	context->jerr.error = error;
Packit a4058c
	if (sigsetjmp (context->jerr.setjmp_buffer, 1)) {
Packit a4058c
                retval = FALSE;
Packit a4058c
	} else {
Packit a4058c
		jpeg_finish_decompress (&context->cinfo);
Packit a4058c
                retval = TRUE;
Packit a4058c
	}
Packit a4058c
Packit a4058c
        jpeg_destroy_decompress (&context->cinfo);
Packit a4058c
Packit a4058c
	if (context->cinfo.src) {
Packit a4058c
		my_src_ptr src = (my_src_ptr) context->cinfo.src;
Packit a4058c
		
Packit a4058c
		g_free (src);
Packit a4058c
	}
Packit a4058c
Packit a4058c
	g_free (context);
Packit a4058c
Packit a4058c
        return retval;
Packit a4058c
}
Packit a4058c
Packit a4058c
Packit a4058c
static gboolean
Packit a4058c
gdk_pixbuf__jpeg_image_load_lines (JpegProgContext  *context,
Packit a4058c
                                   GError          **error)
Packit a4058c
{
Packit a4058c
        struct jpeg_decompress_struct *cinfo = &context->cinfo;
Packit a4058c
        guchar *lines[4];
Packit a4058c
        guchar **lptr;
Packit a4058c
        guchar *rowptr;
Packit a4058c
        gint   nlines, i;
Packit a4058c
Packit a4058c
        /* keep going until we've done all scanlines */
Packit a4058c
        while (cinfo->output_scanline < cinfo->output_height) {
Packit a4058c
                lptr = lines;
Packit a4058c
                rowptr = context->dptr;
Packit a4058c
                for (i=0; i < cinfo->rec_outbuf_height; i++) {
Packit a4058c
                        *lptr++ = rowptr;
Packit a4058c
                        rowptr += context->pixbuf->rowstride;
Packit a4058c
                }
Packit a4058c
Packit a4058c
                nlines = jpeg_read_scanlines (cinfo, lines,
Packit a4058c
                                              cinfo->rec_outbuf_height);
Packit a4058c
                if (nlines == 0)
Packit a4058c
                        break;
Packit a4058c
Packit a4058c
                switch (cinfo->out_color_space) {
Packit a4058c
                case JCS_GRAYSCALE:
Packit a4058c
                        explode_gray_into_buf (cinfo, lines);
Packit a4058c
                        break;
Packit a4058c
                case JCS_RGB:
Packit a4058c
                        /* do nothing */
Packit a4058c
                        break;
Packit a4058c
                case JCS_CMYK:
Packit a4058c
                        convert_cmyk_to_rgb (cinfo, lines);
Packit a4058c
                        break;
Packit a4058c
                default:
Packit a4058c
                        g_set_error (error,
Packit a4058c
                                     GDK_PIXBUF_ERROR,
Packit a4058c
                                     GDK_PIXBUF_ERROR_UNKNOWN_TYPE,
Packit a4058c
                                     _("Unsupported JPEG color space (%s)"),
Packit a4058c
                                     colorspace_name (cinfo->out_color_space));
Packit a4058c
Packit a4058c
                        return FALSE;
Packit a4058c
                }
Packit a4058c
Packit a4058c
                context->dptr += (gsize)nlines * context->pixbuf->rowstride;
Packit a4058c
Packit a4058c
                /* send updated signal */
Packit a4058c
		if (context->updated_func)
Packit a4058c
			(* context->updated_func) (context->pixbuf,
Packit a4058c
						   0,
Packit a4058c
						   cinfo->output_scanline - 1,
Packit a4058c
						   cinfo->image_width,
Packit a4058c
						   nlines,
Packit a4058c
						   context->user_data);
Packit a4058c
        }
Packit a4058c
Packit a4058c
        return TRUE;
Packit a4058c
}
Packit a4058c
Packit a4058c
Packit a4058c
/*
Packit a4058c
 * context - from image_begin_load
Packit a4058c
 * buf - new image data
Packit a4058c
 * size - length of new image data
Packit a4058c
 *
Packit a4058c
 * append image data onto inrecrementally built output image
Packit a4058c
 */
Packit a4058c
static gboolean
Packit a4058c
gdk_pixbuf__jpeg_image_load_increment (gpointer data,
Packit a4058c
                                       const guchar *buf, guint size,
Packit a4058c
                                       GError **error)
Packit a4058c
{
Packit a4058c
	JpegProgContext *context = (JpegProgContext *)data;
Packit a4058c
	struct           jpeg_decompress_struct *cinfo;
Packit a4058c
	my_src_ptr       src;
Packit a4058c
	guint            num_left, num_copy;
Packit a4058c
	guint            last_num_left, last_bytes_left;
Packit a4058c
	guint            spinguard;
Packit a4058c
	gboolean         first;
Packit a4058c
	const guchar    *bufhd;
Packit a4058c
	gint             width, height;
Packit a4058c
	char             otag_str[5];
Packit a4058c
	gchar 		*icc_profile_base64;
Packit a4058c
	char            *density_str;
Packit a4058c
	JpegExifContext  exif_context = { 0, };
Packit a4058c
	gboolean	 retval;
Packit a4058c
Packit a4058c
	g_return_val_if_fail (context != NULL, FALSE);
Packit a4058c
	g_return_val_if_fail (buf != NULL, FALSE);
Packit a4058c
Packit a4058c
	src = (my_src_ptr) context->cinfo.src;
Packit a4058c
Packit a4058c
	cinfo = &context->cinfo;
Packit a4058c
Packit a4058c
        context->jerr.error = error;
Packit a4058c
        
Packit a4058c
	/* check for fatal error */
Packit a4058c
	if (sigsetjmp (context->jerr.setjmp_buffer, 1)) {
Packit a4058c
		retval = FALSE;
Packit a4058c
		goto out;
Packit a4058c
	}
Packit a4058c
Packit a4058c
	/* skip over data if requested, handle unsigned int sizes cleanly */
Packit a4058c
	/* only can happen if we've already called jpeg_get_header once   */
Packit a4058c
	if (context->src_initialized && src->skip_next) {
Packit a4058c
		if (src->skip_next > size) {
Packit a4058c
			src->skip_next -= size;
Packit a4058c
			retval = TRUE;
Packit a4058c
			goto out;
Packit a4058c
		} else {
Packit a4058c
			num_left = size - src->skip_next;
Packit a4058c
			bufhd = buf + src->skip_next;
Packit a4058c
			src->skip_next = 0;
Packit a4058c
		}
Packit a4058c
	} else {
Packit a4058c
		num_left = size;
Packit a4058c
		bufhd = buf;
Packit a4058c
	}
Packit a4058c
Packit a4058c
	if (num_left == 0) {
Packit a4058c
		retval = TRUE;
Packit a4058c
		goto out;
Packit a4058c
	}
Packit a4058c
Packit a4058c
	last_num_left = num_left;
Packit a4058c
	last_bytes_left = 0;
Packit a4058c
	spinguard = 0;
Packit a4058c
	first = TRUE;
Packit a4058c
	while (TRUE) {
Packit a4058c
Packit a4058c
		/* handle any data from caller we haven't processed yet */
Packit a4058c
		if (num_left > 0) {
Packit a4058c
			if(src->pub.bytes_in_buffer && 
Packit a4058c
			   src->pub.next_input_byte != src->buffer)
Packit a4058c
				memmove(src->buffer, src->pub.next_input_byte,
Packit a4058c
					src->pub.bytes_in_buffer);
Packit a4058c
Packit a4058c
Packit a4058c
			num_copy = MIN (JPEG_PROG_BUF_SIZE - src->pub.bytes_in_buffer,
Packit a4058c
					num_left);
Packit a4058c
Packit a4058c
			memcpy(src->buffer + src->pub.bytes_in_buffer, bufhd,num_copy);
Packit a4058c
			src->pub.next_input_byte = src->buffer;
Packit a4058c
			src->pub.bytes_in_buffer += num_copy;
Packit a4058c
			bufhd += num_copy;
Packit a4058c
			num_left -= num_copy;
Packit a4058c
		}
Packit a4058c
Packit a4058c
                /* did anything change from last pass, if not return */
Packit a4058c
                if (first) {
Packit a4058c
                        last_bytes_left = src->pub.bytes_in_buffer;
Packit a4058c
                        first = FALSE;
Packit a4058c
                } else if (src->pub.bytes_in_buffer == last_bytes_left
Packit a4058c
			   && num_left == last_num_left) {
Packit a4058c
                        spinguard++;
Packit a4058c
		} else {
Packit a4058c
                        last_bytes_left = src->pub.bytes_in_buffer;
Packit a4058c
			last_num_left = num_left;
Packit a4058c
		}
Packit a4058c
Packit a4058c
		/* should not go through twice and not pull bytes out of buf */
Packit a4058c
		if (spinguard > 2) {
Packit a4058c
			retval = TRUE;
Packit a4058c
			goto out;
Packit a4058c
		}
Packit a4058c
Packit a4058c
		/* try to load jpeg header */
Packit a4058c
		if (!context->got_header) {
Packit a4058c
			int rc;
Packit a4058c
			gchar* comment;
Packit a4058c
			gboolean has_alpha;
Packit a4058c
		
Packit a4058c
			jpeg_save_markers (cinfo, JPEG_APP0+1, 0xffff);
Packit a4058c
			jpeg_save_markers (cinfo, JPEG_APP0+2, 0xffff);
Packit a4058c
			jpeg_save_markers (cinfo, JPEG_COM, 0xffff);
Packit a4058c
			rc = jpeg_read_header (cinfo, TRUE);
Packit a4058c
			context->src_initialized = TRUE;
Packit a4058c
			
Packit a4058c
			if (rc == JPEG_SUSPENDED)
Packit a4058c
				continue;
Packit a4058c
			
Packit a4058c
			context->got_header = TRUE;
Packit a4058c
Packit a4058c
			/* parse exif data */
Packit a4058c
			jpeg_parse_exif (&exif_context, cinfo);
Packit a4058c
		
Packit a4058c
			width = cinfo->image_width;
Packit a4058c
			height = cinfo->image_height;
Packit a4058c
			if (context->size_func) {
Packit a4058c
				(* context->size_func) (&width, &height, context->user_data);
Packit a4058c
				if (width == 0 || height == 0) {
Packit a4058c
					g_set_error_literal (error,
Packit a4058c
                                                             GDK_PIXBUF_ERROR,
Packit a4058c
                                                             GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
Packit a4058c
                                                             _("Transformed JPEG has zero width or height."));
Packit a4058c
					retval = FALSE;
Packit a4058c
					goto out;
Packit a4058c
				}
Packit a4058c
			}
Packit a4058c
			
Packit a4058c
			cinfo->scale_num = 1;
Packit a4058c
			for (cinfo->scale_denom = 2; cinfo->scale_denom <= 8; cinfo->scale_denom *= 2) {
Packit a4058c
				jpeg_calc_output_dimensions (cinfo);
Packit a4058c
				if (cinfo->output_width < width || cinfo->output_height < height) {
Packit a4058c
					cinfo->scale_denom /= 2;
Packit a4058c
					break;
Packit a4058c
				}
Packit a4058c
			}
Packit a4058c
			jpeg_calc_output_dimensions (cinfo);
Packit a4058c
Packit a4058c
			if (cinfo->output_components == 3) {
Packit a4058c
				has_alpha = FALSE;
Packit a4058c
			} else if (cinfo->output_components == 4) {
Packit a4058c
				has_alpha = TRUE;
Packit a4058c
			} else if (cinfo->output_components == 1 &&
Packit a4058c
				   cinfo->out_color_space == JCS_GRAYSCALE) {
Packit a4058c
				has_alpha = FALSE;
Packit a4058c
			} else {
Packit a4058c
				g_set_error (error,
Packit a4058c
					     GDK_PIXBUF_ERROR,
Packit a4058c
					     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
Packit a4058c
					     _("Unsupported number of color components (%d)"),
Packit a4058c
					     cinfo->output_components);
Packit a4058c
				retval = FALSE;
Packit a4058c
				goto out;
Packit a4058c
			}
Packit a4058c
Packit a4058c
			context->pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
Packit a4058c
							  has_alpha,
Packit a4058c
							  8,
Packit a4058c
							  cinfo->output_width,
Packit a4058c
							  cinfo->output_height);
Packit a4058c
Packit a4058c
			if (context->pixbuf == NULL) {
Packit a4058c
                                g_set_error_literal (error,
Packit a4058c
                                                     GDK_PIXBUF_ERROR,
Packit a4058c
                                                     GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
Packit a4058c
                                                     _("Couldn't allocate memory for loading JPEG file"));
Packit a4058c
                                retval = FALSE;
Packit a4058c
				goto out;
Packit a4058c
			}
Packit a4058c
Packit a4058c
			comment = jpeg_get_comment (cinfo);
Packit a4058c
			if (comment != NULL) {
Packit a4058c
				gdk_pixbuf_set_option (context->pixbuf, "comment", comment);
Packit a4058c
				g_free (comment);
Packit a4058c
			}
Packit a4058c
Packit a4058c
			switch (cinfo->density_unit) {
Packit a4058c
			case 1:
Packit a4058c
				/* Dots per inch (no conversion required) */
Packit a4058c
				density_str = g_strdup_printf ("%d", cinfo->X_density);
Packit a4058c
				gdk_pixbuf_set_option (context->pixbuf, "x-dpi", density_str);
Packit a4058c
				g_free (density_str);
Packit a4058c
				density_str = g_strdup_printf ("%d", cinfo->Y_density);
Packit a4058c
				gdk_pixbuf_set_option (context->pixbuf, "y-dpi", density_str);
Packit a4058c
				g_free (density_str);
Packit a4058c
				break;
Packit a4058c
			case 2:
Packit a4058c
				/* Dots per cm - convert into dpi */
Packit a4058c
				density_str = g_strdup_printf ("%d", DPCM_TO_DPI (cinfo->X_density));
Packit a4058c
				gdk_pixbuf_set_option (context->pixbuf, "x-dpi", density_str);
Packit a4058c
				g_free (density_str);
Packit a4058c
				density_str = g_strdup_printf ("%d", DPCM_TO_DPI (cinfo->Y_density));
Packit a4058c
				gdk_pixbuf_set_option (context->pixbuf, "y-dpi", density_str);
Packit a4058c
				g_free (density_str);
Packit a4058c
				break;
Packit a4058c
			}
Packit a4058c
		
Packit a4058c
		        /* if orientation tag was found set an option to remember its value */
Packit a4058c
			if (exif_context.orientation != 0) {
Packit a4058c
				g_snprintf (otag_str, sizeof (otag_str), "%d", exif_context.orientation);
Packit a4058c
		                gdk_pixbuf_set_option (context->pixbuf, "orientation", otag_str);
Packit a4058c
		        }
Packit a4058c
			/* if icc profile was found */
Packit a4058c
			if (exif_context.icc_profile != NULL) {
Packit a4058c
				icc_profile_base64 = g_base64_encode ((const guchar *) exif_context.icc_profile, exif_context.icc_profile_size);
Packit a4058c
				gdk_pixbuf_set_option (context->pixbuf, "icc-profile", icc_profile_base64);
Packit a4058c
				g_free (icc_profile_base64);
Packit a4058c
			}
Packit a4058c
Packit a4058c
Packit a4058c
			/* Use pixbuf buffer to store decompressed data */
Packit a4058c
			context->dptr = context->pixbuf->pixels;
Packit a4058c
			
Packit a4058c
			/* Notify the client that we are ready to go */
Packit a4058c
			if (context->prepared_func)
Packit a4058c
				(* context->prepared_func) (context->pixbuf,
Packit a4058c
							    NULL,
Packit a4058c
							    context->user_data);
Packit a4058c
			
Packit a4058c
		} else if (!context->did_prescan) {
Packit a4058c
			int rc;			
Packit a4058c
			
Packit a4058c
			/* start decompression */
Packit a4058c
			cinfo->buffered_image = cinfo->progressive_mode;
Packit a4058c
			rc = jpeg_start_decompress (cinfo);
Packit a4058c
			cinfo->do_fancy_upsampling = FALSE;
Packit a4058c
			cinfo->do_block_smoothing = FALSE;
Packit a4058c
Packit a4058c
			if (rc == JPEG_SUSPENDED)
Packit a4058c
				continue;
Packit a4058c
Packit a4058c
			context->did_prescan = TRUE;
Packit a4058c
		} else if (!cinfo->buffered_image) {
Packit a4058c
                        /* we're decompressing unbuffered so
Packit a4058c
                         * simply get scanline by scanline from jpeg lib
Packit a4058c
                         */
Packit a4058c
                        if (! gdk_pixbuf__jpeg_image_load_lines (context,
Packit a4058c
                                                                 error)) {
Packit a4058c
                                retval = FALSE;
Packit a4058c
				goto out;
Packit a4058c
			}
Packit a4058c
Packit a4058c
			if (cinfo->output_scanline >= cinfo->output_height) {
Packit a4058c
				retval = TRUE;
Packit a4058c
				goto out;
Packit a4058c
			}
Packit a4058c
		} else {
Packit a4058c
                        /* we're decompressing buffered (progressive)
Packit a4058c
                         * so feed jpeg lib scanlines
Packit a4058c
                         */
Packit a4058c
Packit a4058c
			/* keep going until we've done all passes */
Packit a4058c
			while (!jpeg_input_complete (cinfo)) {
Packit a4058c
				if (!context->in_output) {
Packit a4058c
					if (jpeg_start_output (cinfo, cinfo->input_scan_number)) {
Packit a4058c
						context->in_output = TRUE;
Packit a4058c
						context->dptr = context->pixbuf->pixels;
Packit a4058c
					}
Packit a4058c
					else
Packit a4058c
						break;
Packit a4058c
				}
Packit a4058c
Packit a4058c
                                /* get scanlines from jpeg lib */
Packit a4058c
                                if (! gdk_pixbuf__jpeg_image_load_lines (context,
Packit a4058c
                                                                         error)) {
Packit a4058c
                                        retval = FALSE;
Packit a4058c
					goto out;
Packit a4058c
				}
Packit a4058c
Packit a4058c
				if (cinfo->output_scanline >= cinfo->output_height &&
Packit a4058c
				    jpeg_finish_output (cinfo))
Packit a4058c
					context->in_output = FALSE;
Packit a4058c
				else
Packit a4058c
					break;
Packit a4058c
			}
Packit a4058c
			if (jpeg_input_complete (cinfo)) {
Packit a4058c
				/* did entire image */
Packit a4058c
				retval = TRUE;
Packit a4058c
				goto out;
Packit a4058c
			}
Packit a4058c
			else
Packit a4058c
				continue;
Packit a4058c
		}
Packit a4058c
	}
Packit a4058c
out:
Packit a4058c
	jpeg_destroy_exif_context (&exif_context);
Packit a4058c
	return retval;
Packit a4058c
}
Packit a4058c
Packit a4058c
/* Save */
Packit a4058c
Packit a4058c
#define TO_FUNCTION_BUF_SIZE 4096
Packit a4058c
Packit a4058c
typedef struct {
Packit a4058c
	struct jpeg_destination_mgr pub;
Packit a4058c
	JOCTET             *buffer;
Packit a4058c
	GdkPixbufSaveFunc   save_func;
Packit a4058c
	gpointer            user_data;
Packit a4058c
	GError            **error;
Packit a4058c
} ToFunctionDestinationManager;
Packit a4058c
Packit a4058c
void
Packit a4058c
to_callback_init (j_compress_ptr cinfo)
Packit a4058c
{
Packit a4058c
	ToFunctionDestinationManager *destmgr;
Packit a4058c
Packit a4058c
	destmgr	= (ToFunctionDestinationManager*) cinfo->dest;
Packit a4058c
	destmgr->pub.next_output_byte = destmgr->buffer;
Packit a4058c
	destmgr->pub.free_in_buffer = TO_FUNCTION_BUF_SIZE;
Packit a4058c
}
Packit a4058c
Packit a4058c
static void
Packit a4058c
to_callback_do_write (j_compress_ptr cinfo, gsize length)
Packit a4058c
{
Packit a4058c
	ToFunctionDestinationManager *destmgr;
Packit a4058c
Packit a4058c
	destmgr	= (ToFunctionDestinationManager*) cinfo->dest;
Packit a4058c
        if (!destmgr->save_func ((gchar *)destmgr->buffer,
Packit a4058c
				 length,
Packit a4058c
				 destmgr->error,
Packit a4058c
				 destmgr->user_data)) {
Packit a4058c
		struct error_handler_data *errmgr;
Packit a4058c
        
Packit a4058c
		errmgr = (struct error_handler_data *) cinfo->err;
Packit a4058c
		/* Use a default error message if the callback didn't set one,
Packit a4058c
		 * which it should have.
Packit a4058c
		 */
Packit a4058c
		if (errmgr->error && *errmgr->error == NULL) {
Packit a4058c
			g_set_error_literal (errmgr->error,
Packit a4058c
                                             GDK_PIXBUF_ERROR,
Packit a4058c
                                             GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
Packit a4058c
                                             "write function failed");
Packit a4058c
		}
Packit a4058c
		siglongjmp (errmgr->setjmp_buffer, 1);
Packit a4058c
		g_assert_not_reached ();
Packit a4058c
        }
Packit a4058c
}
Packit a4058c
Packit a4058c
static boolean
Packit a4058c
to_callback_empty_output_buffer (j_compress_ptr cinfo)
Packit a4058c
{
Packit a4058c
	ToFunctionDestinationManager *destmgr;
Packit a4058c
Packit a4058c
	destmgr	= (ToFunctionDestinationManager*) cinfo->dest;
Packit a4058c
	to_callback_do_write (cinfo, TO_FUNCTION_BUF_SIZE);
Packit a4058c
	destmgr->pub.next_output_byte = destmgr->buffer;
Packit a4058c
	destmgr->pub.free_in_buffer = TO_FUNCTION_BUF_SIZE;
Packit a4058c
	return TRUE;
Packit a4058c
}
Packit a4058c
Packit a4058c
void
Packit a4058c
to_callback_terminate (j_compress_ptr cinfo)
Packit a4058c
{
Packit a4058c
	ToFunctionDestinationManager *destmgr;
Packit a4058c
Packit a4058c
	destmgr	= (ToFunctionDestinationManager*) cinfo->dest;
Packit a4058c
	to_callback_do_write (cinfo, TO_FUNCTION_BUF_SIZE - destmgr->pub.free_in_buffer);
Packit a4058c
}
Packit a4058c
Packit a4058c
static gboolean
Packit a4058c
real_save_jpeg (GdkPixbuf          *pixbuf,
Packit a4058c
		gchar             **keys,
Packit a4058c
		gchar             **values,
Packit a4058c
		GError            **error,
Packit a4058c
		gboolean            to_callback,
Packit a4058c
		FILE               *f,
Packit a4058c
		GdkPixbufSaveFunc   save_func,
Packit a4058c
		gpointer            user_data)
Packit a4058c
{
Packit a4058c
        /* FIXME error handling is broken */
Packit a4058c
        
Packit a4058c
       struct jpeg_compress_struct cinfo;
Packit a4058c
       guchar *buf = NULL;
Packit a4058c
       guchar *ptr;
Packit a4058c
       guchar *pixels = NULL;
Packit a4058c
       JSAMPROW *jbuf;
Packit a4058c
       int y = 0;
Packit a4058c
       volatile int quality = 75; /* default; must be between 0 and 100 */
Packit a4058c
       int i, j;
Packit a4058c
       int w, h = 0;
Packit a4058c
       int rowstride = 0;
Packit a4058c
       int n_channels;
Packit a4058c
       struct error_handler_data jerr;
Packit a4058c
       ToFunctionDestinationManager to_callback_destmgr;
Packit a4058c
       int x_density = 0;
Packit a4058c
       int y_density = 0;
Packit a4058c
       gchar *icc_profile = NULL;
Packit a4058c
       gchar *data;
Packit a4058c
       gint retval = TRUE;
Packit a4058c
       gsize icc_profile_size = 0;
Packit a4058c
Packit a4058c
       to_callback_destmgr.buffer = NULL;
Packit a4058c
Packit a4058c
       if (keys && *keys) {
Packit a4058c
               gchar **kiter = keys;
Packit a4058c
               gchar **viter = values;
Packit a4058c
Packit a4058c
               while (*kiter) {
Packit a4058c
                       if (strcmp (*kiter, "quality") == 0) {
Packit a4058c
                               char *endptr = NULL;
Packit a4058c
                               quality = strtol (*viter, &endptr, 10);
Packit a4058c
Packit a4058c
                               if (endptr == *viter) {
Packit a4058c
                                       g_set_error (error,
Packit a4058c
                                                    GDK_PIXBUF_ERROR,
Packit a4058c
                                                    GDK_PIXBUF_ERROR_BAD_OPTION,
Packit a4058c
                                                    _("JPEG quality must be a value between 0 and 100; value '%s' could not be parsed."),
Packit a4058c
                                                    *viter);
Packit a4058c
Packit a4058c
                                       retval = FALSE;
Packit a4058c
                                       goto cleanup;
Packit a4058c
                               }
Packit a4058c
                               
Packit a4058c
                               if (quality < 0 ||
Packit a4058c
                                   quality > 100) {
Packit a4058c
                                       /* This is a user-visible error;
Packit a4058c
                                        * lets people skip the range-checking
Packit a4058c
                                        * in their app.
Packit a4058c
                                        */
Packit a4058c
                                       g_set_error (error,
Packit a4058c
                                                    GDK_PIXBUF_ERROR,
Packit a4058c
                                                    GDK_PIXBUF_ERROR_BAD_OPTION,
Packit a4058c
                                                    _("JPEG quality must be a value between 0 and 100; value '%d' is not allowed."),
Packit a4058c
                                                    quality);
Packit a4058c
Packit a4058c
                                       retval = FALSE;
Packit a4058c
                                       goto cleanup;
Packit a4058c
                               }
Packit a4058c
                       } else if (strcmp (*kiter, "x-dpi") == 0) {
Packit a4058c
                               char *endptr = NULL;
Packit a4058c
                               x_density = strtol (*viter, &endptr, 10);
Packit a4058c
                               if (endptr == *viter)
Packit a4058c
                                       x_density = -1;
Packit a4058c
Packit a4058c
                               if (x_density <= 0 ||
Packit a4058c
                                   x_density > 65535) {
Packit a4058c
                                       /* This is a user-visible error;
Packit a4058c
                                        * lets people skip the range-checking
Packit a4058c
                                        * in their app.
Packit a4058c
                                        */
Packit a4058c
                                       g_set_error (error,
Packit a4058c
                                                    GDK_PIXBUF_ERROR,
Packit a4058c
                                                    GDK_PIXBUF_ERROR_BAD_OPTION,
Packit a4058c
                                                    _("JPEG x-dpi must be a value between 1 and 65535; value '%s' is not allowed."),
Packit a4058c
                                                    *viter);
Packit a4058c
Packit a4058c
                                       retval = FALSE;
Packit a4058c
                                       goto cleanup;
Packit a4058c
                               }
Packit a4058c
                       } else if (strcmp (*kiter, "y-dpi") == 0) {
Packit a4058c
                               char *endptr = NULL;
Packit a4058c
                               y_density = strtol (*viter, &endptr, 10);
Packit a4058c
                               if (endptr == *viter)
Packit a4058c
                                       y_density = -1;
Packit a4058c
Packit a4058c
                               if (y_density <= 0 ||
Packit a4058c
                                   y_density > 65535) {
Packit a4058c
                                       /* This is a user-visible error;
Packit a4058c
                                        * lets people skip the range-checking
Packit a4058c
                                        * in their app.
Packit a4058c
                                        */
Packit a4058c
                                       g_set_error (error,
Packit a4058c
                                                    GDK_PIXBUF_ERROR,
Packit a4058c
                                                    GDK_PIXBUF_ERROR_BAD_OPTION,
Packit a4058c
                                                    _("JPEG y-dpi must be a value between 1 and 65535; value '%s' is not allowed."),
Packit a4058c
                                                    *viter);
Packit a4058c
Packit a4058c
                                       retval = FALSE;
Packit a4058c
                                       goto cleanup;
Packit a4058c
                               }
Packit a4058c
                       } else if (strcmp (*kiter, "icc-profile") == 0) {
Packit a4058c
                               /* decode from base64 */
Packit a4058c
                               icc_profile = (gchar*) g_base64_decode (*viter, &icc_profile_size);
Packit a4058c
                               if (icc_profile_size < 127) {
Packit a4058c
                                       /* This is a user-visible error */
Packit a4058c
                                       g_set_error (error,
Packit a4058c
                                                    GDK_PIXBUF_ERROR,
Packit a4058c
                                                    GDK_PIXBUF_ERROR_BAD_OPTION,
Packit a4058c
                                                    _("Color profile has invalid length '%u'."),
Packit a4058c
                                                    (guint) icc_profile_size);
Packit a4058c
                                       retval = FALSE;
Packit a4058c
                                       goto cleanup;
Packit a4058c
                               }
Packit a4058c
                       } else {
Packit a4058c
                               g_warning ("Unrecognized parameter (%s) passed to JPEG saver.", *kiter);
Packit a4058c
                       }
Packit a4058c
               
Packit a4058c
                       ++kiter;
Packit a4058c
                       ++viter;
Packit a4058c
               }
Packit a4058c
       }
Packit a4058c
       
Packit a4058c
       rowstride = gdk_pixbuf_get_rowstride (pixbuf);
Packit a4058c
       n_channels = gdk_pixbuf_get_n_channels (pixbuf);
Packit a4058c
Packit a4058c
       w = gdk_pixbuf_get_width (pixbuf);
Packit a4058c
       h = gdk_pixbuf_get_height (pixbuf);
Packit a4058c
       pixels = gdk_pixbuf_get_pixels (pixbuf);
Packit a4058c
Packit a4058c
       /* Guaranteed by the caller. */
Packit a4058c
       g_assert (w >= 0);
Packit a4058c
       g_assert (h >= 0);
Packit a4058c
       g_assert (rowstride >= 0);
Packit a4058c
       g_assert (n_channels >= 0);
Packit a4058c
Packit a4058c
       /* Allocate a small buffer to convert image data,
Packit a4058c
	* and a larger buffer if doing to_callback save.
Packit a4058c
	*/
Packit a4058c
       buf = g_try_malloc (w * 3 * sizeof (guchar));
Packit a4058c
       if (!buf) {
Packit a4058c
	       g_set_error_literal (error,
Packit a4058c
                                    GDK_PIXBUF_ERROR,
Packit a4058c
                                    GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
Packit a4058c
                                    _("Couldn't allocate memory for loading JPEG file"));
Packit a4058c
	       retval = FALSE;
Packit a4058c
	       goto cleanup;
Packit a4058c
       }
Packit a4058c
       if (to_callback) {
Packit a4058c
	       to_callback_destmgr.buffer = g_try_malloc (TO_FUNCTION_BUF_SIZE);
Packit a4058c
	       if (!to_callback_destmgr.buffer) {
Packit a4058c
		       g_set_error_literal (error,
Packit a4058c
                                            GDK_PIXBUF_ERROR,
Packit a4058c
                                            GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
Packit a4058c
                                            _("Couldn't allocate memory for loading JPEG file"));
Packit a4058c
		       retval = FALSE;
Packit a4058c
		       goto cleanup;
Packit a4058c
	       }
Packit a4058c
       }
Packit a4058c
Packit a4058c
       /* set up error handling */
Packit a4058c
       cinfo.err = jpeg_std_error (&(jerr.pub));
Packit a4058c
       jerr.pub.error_exit = fatal_error_handler;
Packit a4058c
       jerr.pub.output_message = output_message_handler;
Packit a4058c
       jerr.error = error;
Packit a4058c
       
Packit a4058c
       if (sigsetjmp (jerr.setjmp_buffer, 1)) {
Packit a4058c
               jpeg_destroy_compress (&cinfo);
Packit a4058c
	       retval = FALSE;
Packit a4058c
	       goto cleanup;
Packit a4058c
       }
Packit a4058c
Packit a4058c
       /* setup compress params */
Packit a4058c
       jpeg_create_compress (&cinfo);
Packit a4058c
       if (to_callback) {
Packit a4058c
	       to_callback_destmgr.pub.init_destination    = to_callback_init;
Packit a4058c
	       to_callback_destmgr.pub.empty_output_buffer = to_callback_empty_output_buffer;
Packit a4058c
	       to_callback_destmgr.pub.term_destination    = to_callback_terminate;
Packit a4058c
	       to_callback_destmgr.error = error;
Packit a4058c
	       to_callback_destmgr.save_func = save_func;
Packit a4058c
	       to_callback_destmgr.user_data = user_data;
Packit a4058c
	       cinfo.dest = (struct jpeg_destination_mgr*) &to_callback_destmgr;
Packit a4058c
       } else {
Packit a4058c
	       jpeg_stdio_dest (&cinfo, f);
Packit a4058c
       }
Packit a4058c
       cinfo.image_width      = w;
Packit a4058c
       cinfo.image_height     = h;
Packit a4058c
       cinfo.input_components = 3; 
Packit a4058c
       cinfo.in_color_space   = JCS_RGB;
Packit a4058c
Packit a4058c
       /* set up jepg compression parameters */
Packit a4058c
       jpeg_set_defaults (&cinfo);
Packit a4058c
       jpeg_set_quality (&cinfo, quality, TRUE);
Packit a4058c
Packit a4058c
       /* set density information */
Packit a4058c
       if (x_density > 0 && y_density > 0) {
Packit a4058c
           cinfo.density_unit = 1; /* Dots per inch */
Packit a4058c
           cinfo.X_density = x_density;
Packit a4058c
           cinfo.Y_density = y_density;
Packit a4058c
       }
Packit a4058c
Packit a4058c
       jpeg_start_compress (&cinfo, TRUE);
Packit a4058c
Packit a4058c
	/* write ICC profile data */
Packit a4058c
	if (icc_profile != NULL) {
Packit a4058c
		/* optimise for the common case where only one APP2 segment is required */
Packit a4058c
		if (icc_profile_size < 0xffef) {
Packit a4058c
			data = g_new (gchar, icc_profile_size + 14);
Packit a4058c
			memcpy (data, "ICC_PROFILE\000\001\001", 14);
Packit a4058c
			memcpy (data + 14, icc_profile, icc_profile_size);
Packit a4058c
			jpeg_write_marker (&cinfo, JPEG_APP0+2, (const JOCTET *) data, icc_profile_size + 14);
Packit a4058c
			g_free (data);
Packit a4058c
		} else {
Packit a4058c
			guint segments;
Packit a4058c
			guint size = 0xffef;
Packit a4058c
			guint offset;
Packit a4058c
Packit a4058c
			segments = (guint) ceilf ((gfloat) icc_profile_size / (gfloat) 0xffef);
Packit a4058c
			data = g_new (gchar, 0xffff);
Packit a4058c
			memcpy (data, "ICC_PROFILE\000", 12);
Packit a4058c
			data[13] = segments;
Packit a4058c
			for (i=0; i<=segments; i++) {
Packit a4058c
				data[12] = i;
Packit a4058c
				offset = 0xffef * i;
Packit a4058c
Packit a4058c
				/* last segment */
Packit a4058c
				if (i == segments)
Packit a4058c
					size = icc_profile_size % 0xffef;
Packit a4058c
Packit a4058c
				memcpy (data + 14, icc_profile + offset, size);
Packit a4058c
				jpeg_write_marker (&cinfo, JPEG_APP0+2, (const JOCTET *) data, size + 14);
Packit a4058c
			}
Packit a4058c
			g_free (data);
Packit a4058c
		}
Packit a4058c
	}
Packit a4058c
Packit a4058c
       /* get the start pointer */
Packit a4058c
       ptr = pixels;
Packit a4058c
       /* go one scanline at a time... and save */
Packit a4058c
       i = 0;
Packit a4058c
       while (cinfo.next_scanline < cinfo.image_height) {
Packit a4058c
               /* convert scanline from ARGB to RGB packed */
Packit a4058c
               for (j = 0; j < w; j++)
Packit a4058c
                       memcpy (&(buf[j*3]), &(ptr[(gsize)i*rowstride + j*n_channels]), 3);
Packit a4058c
Packit a4058c
               /* write scanline */
Packit a4058c
               jbuf = (JSAMPROW *)(&buf;;
Packit a4058c
               if (jpeg_write_scanlines (&cinfo, jbuf, 1) == 0) {
Packit a4058c
                      jpeg_destroy_compress (&cinfo);
Packit a4058c
                      retval = FALSE;
Packit a4058c
                      goto cleanup;
Packit a4058c
               }
Packit a4058c
Packit a4058c
               i++;
Packit a4058c
               y++;
Packit a4058c
Packit a4058c
       }
Packit a4058c
Packit a4058c
       /* finish off */
Packit a4058c
       jpeg_finish_compress (&cinfo);
Packit a4058c
       jpeg_destroy_compress(&cinfo);
Packit a4058c
cleanup:
Packit a4058c
	g_free (buf);
Packit a4058c
	g_free (to_callback_destmgr.buffer);
Packit a4058c
	g_free (icc_profile);
Packit a4058c
	return retval;
Packit a4058c
}
Packit a4058c
Packit a4058c
static gboolean
Packit a4058c
gdk_pixbuf__jpeg_image_save (FILE          *f, 
Packit a4058c
                             GdkPixbuf     *pixbuf, 
Packit a4058c
                             gchar        **keys,
Packit a4058c
                             gchar        **values,
Packit a4058c
                             GError       **error)
Packit a4058c
{
Packit a4058c
	return real_save_jpeg (pixbuf, keys, values, error,
Packit a4058c
			       FALSE, f, NULL, NULL);
Packit a4058c
}
Packit a4058c
Packit a4058c
static gboolean
Packit a4058c
gdk_pixbuf__jpeg_image_save_to_callback (GdkPixbufSaveFunc   save_func,
Packit a4058c
					 gpointer            user_data,
Packit a4058c
					 GdkPixbuf          *pixbuf, 
Packit a4058c
					 gchar             **keys,
Packit a4058c
					 gchar             **values,
Packit a4058c
					 GError            **error)
Packit a4058c
{
Packit a4058c
	return real_save_jpeg (pixbuf, keys, values, error,
Packit a4058c
			       TRUE, NULL, save_func, user_data);
Packit a4058c
}
Packit a4058c
Packit a4058c
static gboolean
Packit a4058c
gdk_pixbuf__jpeg_is_save_option_supported (const gchar *option_key)
Packit a4058c
{
Packit a4058c
        if (g_strcmp0 (option_key, "quality") == 0 ||
Packit a4058c
            g_strcmp0 (option_key, "icc-profile") == 0)
Packit a4058c
                return TRUE;
Packit a4058c
Packit a4058c
        return FALSE;
Packit a4058c
}
Packit a4058c
Packit a4058c
#ifndef INCLUDE_jpeg
Packit a4058c
#define MODULE_ENTRY(function) G_MODULE_EXPORT void function
Packit a4058c
#else
Packit a4058c
#define MODULE_ENTRY(function) void _gdk_pixbuf__jpeg_ ## function
Packit a4058c
#endif
Packit a4058c
Packit a4058c
MODULE_ENTRY (fill_vtable) (GdkPixbufModule *module)
Packit a4058c
{
Packit a4058c
	module->load = gdk_pixbuf__jpeg_image_load;
Packit a4058c
	module->begin_load = gdk_pixbuf__jpeg_image_begin_load;
Packit a4058c
	module->stop_load = gdk_pixbuf__jpeg_image_stop_load;
Packit a4058c
	module->load_increment = gdk_pixbuf__jpeg_image_load_increment;
Packit a4058c
	module->save = gdk_pixbuf__jpeg_image_save;
Packit a4058c
	module->save_to_callback = gdk_pixbuf__jpeg_image_save_to_callback;
Packit a4058c
        module->is_save_option_supported = gdk_pixbuf__jpeg_is_save_option_supported;
Packit a4058c
}
Packit a4058c
Packit a4058c
MODULE_ENTRY (fill_info) (GdkPixbufFormat *info)
Packit a4058c
{
Packit a4058c
	static const GdkPixbufModulePattern signature[] = {
Packit a4058c
		{ "\xff\xd8", NULL, 100 },
Packit a4058c
		{ NULL, NULL, 0 }
Packit a4058c
	};
Packit a4058c
	static const gchar *mime_types[] = {
Packit a4058c
		"image/jpeg",
Packit a4058c
		NULL
Packit a4058c
	};
Packit a4058c
	static const gchar *extensions[] = {
Packit a4058c
		"jpeg",
Packit a4058c
		"jpe",
Packit a4058c
		"jpg",
Packit a4058c
		NULL
Packit a4058c
	};
Packit a4058c
Packit a4058c
	info->name = "jpeg";
Packit a4058c
	info->signature = (GdkPixbufModulePattern *) signature;
Packit a4058c
	info->description = NC_("image format", "JPEG");
Packit a4058c
	info->mime_types = (gchar **) mime_types;
Packit a4058c
	info->extensions = (gchar **) extensions;
Packit a4058c
	info->flags = GDK_PIXBUF_FORMAT_WRITABLE | GDK_PIXBUF_FORMAT_THREADSAFE;
Packit a4058c
	info->license = "LGPL";
Packit a4058c
}