io-jpeg.c 21.8 KB
Newer Older
1 2 3 4
/* GdkPixbuf library - JPEG image loader
 *
 * Copyright (C) 1999 Michael Zucchi
 * Copyright (C) 1999 The Free Software Foundation
5 6
 * 
 * Progressive loading code Copyright (C) 1999 Red Hat, Inc.
7 8 9
 *
 * Authors: Michael Zucchi <zucchi@zedzone.mmc.com.au>
 *          Federico Mena-Quintero <federico@gimp.org>
10
 *          Michael Fulbright <drmike@redhat.com>
11 12
 *
 * This library is free software; you can redistribute it and/or
13
 * modify it under the terms of the GNU Lesser General Public
14 15 16 17 18 19
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20
 * Lesser General Public License for more details.
21
 *
22
 * You should have received a copy of the GNU Lesser General Public
23 24 25 26
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */
Michael Zucci's avatar
Michael Zucci committed
27

28 29

/*
30
  Progressive file loading notes (11/03/1999) <drmike@redhat.com>...
31 32 33 34 35 36 37 38 39 40 41 42 43

  These are issues I know of and will be dealing with shortly:

    -  Currently does not handle progressive jpegs - this
       requires a change in the way image_load_increment () calls
       libjpeg. Progressive jpegs are rarer but I will add this
       support asap.

    - error handling is not as good as it should be

 */


Michael Zucci's avatar
Michael Zucci committed
44 45
#include <config.h>
#include <stdio.h>
46 47
#include <stdlib.h>
#include <string.h>
Michael Zucci's avatar
Michael Zucci committed
48 49
#include <setjmp.h>
#include <jpeglib.h>
50
#include "gdk-pixbuf-private.h"
51
#include "gdk-pixbuf-io.h"
52

Tor Lillqvist's avatar
Tor Lillqvist committed
53 54 55 56 57
#ifndef HAVE_SIGSETJMP
#define sigjmp_buf jmp_buf
#define sigsetjmp(jb, x) setjmp(jb)
#define siglongjmp longjmp
#endif
58

Michael Zucci's avatar
Michael Zucci committed
59

60
/* we are a "source manager" as far as libjpeg is concerned */
61 62
#define JPEG_PROG_BUF_SIZE 4096

63 64 65
typedef struct {
	struct jpeg_source_mgr pub;   /* public fields */

66
	JOCTET buffer[JPEG_PROG_BUF_SIZE];              /* start of buffer */
67
	long  skip_next;              /* number of bytes to skip next read */
68

69 70 71 72
} my_source_mgr;

typedef my_source_mgr * my_src_ptr;

Michael Zucci's avatar
Michael Zucci committed
73
/* error handler data */
74
struct error_handler_data {
Michael Zucci's avatar
Michael Zucci committed
75
	struct jpeg_error_mgr pub;
76
	sigjmp_buf setjmp_buffer;
Havoc Pennington's avatar
Havoc Pennington committed
77
        GError **error;
Michael Zucci's avatar
Michael Zucci committed
78 79
};

80 81
/* progressive loader context */
typedef struct {
82 83 84 85
	ModuleUpdatedNotifyFunc  updated_func;
	ModulePreparedNotifyFunc prepared_func;
	gpointer                 user_data;
	
86 87 88 89 90 91 92 93 94 95
	GdkPixbuf                *pixbuf;
	guchar                   *dptr;   /* current position in pixbuf */

	gboolean                 did_prescan;  /* are we in image data yet? */
	gboolean                 got_header;  /* have we loaded jpeg header? */
	gboolean                 src_initialized;/* TRUE when jpeg lib initialized */
	struct jpeg_decompress_struct cinfo;
	struct error_handler_data     jerr;
} JpegProgContext;

96 97 98 99 100 101 102 103 104
static GdkPixbuf *gdk_pixbuf__jpeg_image_load (FILE *f, GError **error);
static gpointer gdk_pixbuf__jpeg_image_begin_load (ModulePreparedNotifyFunc func, 
                                                   ModuleUpdatedNotifyFunc func2,
                                                   gpointer user_data,
                                                   GError **error);
static gboolean gdk_pixbuf__jpeg_image_stop_load (gpointer context, GError **error);
static gboolean gdk_pixbuf__jpeg_image_load_increment(gpointer context,
                                                      const guchar *buf, guint size,
                                                      GError **error);
105 106


Michael Zucci's avatar
Michael Zucci committed
107
static void
108
fatal_error_handler (j_common_ptr cinfo)
Michael Zucci's avatar
Michael Zucci committed
109
{
110
	struct error_handler_data *errmgr;
Havoc Pennington's avatar
Havoc Pennington committed
111 112
        char buffer[JMSG_LENGTH_MAX];
        
113
	errmgr = (struct error_handler_data *) cinfo->err;
Havoc Pennington's avatar
Havoc Pennington committed
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
        
        /* Create the message */
        (* cinfo->err->format_message) (cinfo, buffer);

        /* broken check for *error == NULL for robustness against
         * crappy JPEG library
         */
        if (errmgr->error && *errmgr->error == NULL) {
                g_set_error (errmgr->error,
                             GDK_PIXBUF_ERROR,
                             GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
                             _("Error interpreting JPEG image file (%s)"),
                             buffer);
        }
        
129
	siglongjmp (errmgr->setjmp_buffer, 1);
130

Havoc Pennington's avatar
Havoc Pennington committed
131 132 133 134 135 136 137 138 139
        g_assert_not_reached ();
}

static void
output_message_handler (j_common_ptr cinfo)
{
  /* This method keeps libjpeg from dumping crap to stderr */

  /* do nothing */
Michael Zucci's avatar
Michael Zucci committed
140 141
}

142
/* Destroy notification function for the pixbuf */
143
static void
144
free_buffer (guchar *pixels, gpointer data)
145
{
146
	g_free (pixels);
147 148
}

149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171

/* explode gray image data from jpeg library into rgb components in pixbuf */
static void
explode_gray_into_buf (struct jpeg_decompress_struct *cinfo,
		       guchar **lines) 
{
	gint i, j;
	guint w;

	g_return_if_fail (cinfo != NULL);
	g_return_if_fail (cinfo->output_components == 1);

	/* Expand grey->colour.  Expand from the end of the
	 * memory down, so we can use the same buffer.
	 */
	w = cinfo->image_width;
	for (i = cinfo->rec_outbuf_height - 1; i >= 0; i--) {
		guchar *from, *to;
		
		from = lines[i] + w - 1;
		to = lines[i] + (w - 1) * 3;
		for (j = w - 1; j >= 0; j--) {
			to[0] = from[0];
172 173
			to[1] = from[0];
			to[2] = from[0];
174 175 176 177 178 179
			to -= 3;
			from--;
		}
	}
}

180
/* Shared library entry point */
181
static GdkPixbuf *
Havoc Pennington's avatar
Havoc Pennington committed
182
gdk_pixbuf__jpeg_image_load (FILE *f, GError **error)
Michael Zucci's avatar
Michael Zucci committed
183
{
184
	gint w, h, i;
185 186
	guchar *pixels = NULL;
	guchar *dptr;
187 188 189 190
	guchar *lines[4]; /* Used to expand rows, via rec_outbuf_height, 
                           * from the header file: 
                           * " Usually rec_outbuf_height will be 1 or 2, 
                           * at most 4."
191 192
			   */
	guchar **lptr;
Michael Zucci's avatar
Michael Zucci committed
193
	struct jpeg_decompress_struct cinfo;
194
	struct error_handler_data jerr;
Michael Zucci's avatar
Michael Zucci committed
195 196

	/* setup error handler */
197 198
	cinfo.err = jpeg_std_error (&jerr.pub);
	jerr.pub.error_exit = fatal_error_handler;
Havoc Pennington's avatar
Havoc Pennington committed
199
        jerr.pub.output_message = output_message_handler;
Michael Zucci's avatar
Michael Zucci committed
200

Havoc Pennington's avatar
Havoc Pennington committed
201 202
        jerr.error = error;
        
203
	if (sigsetjmp (jerr.setjmp_buffer, 1)) {
Michael Zucci's avatar
Michael Zucci committed
204
		/* Whoops there was a jpeg error */
205 206 207 208
		if (pixels)
			free (pixels);

		jpeg_destroy_decompress (&cinfo);
Michael Zucci's avatar
Michael Zucci committed
209 210 211 212
		return NULL;
	}

	/* load header, setup */
213 214 215 216
	jpeg_create_decompress (&cinfo);
	jpeg_stdio_src (&cinfo, f);
	jpeg_read_header (&cinfo, TRUE);
	jpeg_start_decompress (&cinfo);
Michael Zucci's avatar
Michael Zucci committed
217 218
	cinfo.do_fancy_upsampling = FALSE;
	cinfo.do_block_smoothing = FALSE;
219

Michael Zucci's avatar
Michael Zucci committed
220 221 222
	w = cinfo.output_width;
	h = cinfo.output_height;

223
	pixels = g_try_malloc (h * w * 3);
224 225
	if (!pixels) {
		jpeg_destroy_decompress (&cinfo);
Havoc Pennington's avatar
Havoc Pennington committed
226 227 228 229 230 231 232 233 234 235 236

                /* broken check for *error == NULL for robustness against
                 * crappy JPEG library
                 */
                if (error && *error == NULL) {
                        g_set_error (error,
                                     GDK_PIXBUF_ERROR,
                                     GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
                                     _("Insufficient memory to load image, try exiting some applications to free memory"));
                }
                
Michael Zucci's avatar
Michael Zucci committed
237 238
		return NULL;
	}
239

Michael Zucci's avatar
Michael Zucci committed
240 241 242 243 244 245
	dptr = pixels;

	/* decompress all the lines, a few at a time */

	while (cinfo.output_scanline < cinfo.output_height) {
		lptr = lines;
246 247 248
		for (i = 0; i < cinfo.rec_outbuf_height; i++) {
			*lptr++ = dptr;
			dptr += w * 3;
Michael Zucci's avatar
Michael Zucci committed
249
		}
250 251

		jpeg_read_scanlines (&cinfo, lines, cinfo.rec_outbuf_height);
252 253 254

		if (cinfo.output_components == 1)
			explode_gray_into_buf (&cinfo, lines);
Michael Zucci's avatar
Michael Zucci committed
255 256
	}

257 258 259
	jpeg_finish_decompress (&cinfo);
	jpeg_destroy_decompress (&cinfo);

260
	return gdk_pixbuf_new_from_data (pixels, GDK_COLORSPACE_RGB, FALSE, 8,
261 262
					 w, h, w * 3,
					 free_buffer, NULL);
Michael Zucci's avatar
Michael Zucci committed
263
}
264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319


/**** Progressive image loading handling *****/

/* these routines required because we are acting as a source manager for */
/* libjpeg. */
static void
init_source (j_decompress_ptr cinfo)
{
	my_src_ptr src = (my_src_ptr) cinfo->src;

	src->skip_next = 0;
}


static void
term_source (j_decompress_ptr cinfo)
{
	/* XXXX - probably should scream something has happened */
}


/* for progressive loading (called "I/O Suspension" by libjpeg docs) */
/* we do nothing except return "FALSE"                               */
static boolean
fill_input_buffer (j_decompress_ptr cinfo)
{
	return FALSE;
}


static void
skip_input_data (j_decompress_ptr cinfo, long num_bytes)
{
	my_src_ptr src = (my_src_ptr) cinfo->src;
	long   num_can_do;

	/* move as far as we can into current buffer */
	/* then set skip_next to catch the rest      */
	if (num_bytes > 0) {
		num_can_do = MIN (src->pub.bytes_in_buffer, num_bytes);
		src->pub.next_input_byte += (size_t) num_can_do;
		src->pub.bytes_in_buffer -= (size_t) num_can_do;

		src->skip_next = num_bytes - num_can_do;
	}
}

 
/* 
 * func - called when we have pixmap created (but no image data)
 * user_data - passed as arg 1 to func
 * return context (opaque to user)
 */

gpointer
Federico Mena Quintero's avatar
Federico Mena Quintero committed
320 321
gdk_pixbuf__jpeg_image_begin_load (ModulePreparedNotifyFunc prepared_func, 
				   ModuleUpdatedNotifyFunc  updated_func,
Havoc Pennington's avatar
Havoc Pennington committed
322 323
				   gpointer user_data,
                                   GError **error)
324 325 326 327
{
	JpegProgContext *context;
	my_source_mgr   *src;

328
	context = g_new0 (JpegProgContext, 1);
329 330 331
	context->prepared_func = prepared_func;
	context->updated_func  = updated_func;
	context->user_data = user_data;
332 333 334 335 336 337 338 339
	context->pixbuf = NULL;
	context->got_header = FALSE;
	context->did_prescan = FALSE;
	context->src_initialized = FALSE;

	/* create libjpeg structures */
	jpeg_create_decompress (&context->cinfo);

340 341 342 343 344 345 346 347 348 349
	context->cinfo.src = (struct jpeg_source_mgr *) g_try_malloc (sizeof (my_source_mgr));
	if (!context->cinfo.src) {
	  g_set_error (error,
		       GDK_PIXBUF_ERROR,
		       GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
		       _("Couldn't allocate memory for loading JPEG file"));
	  return NULL;
	}
	memset (context->cinfo.src, 0, sizeof (my_source_mgr));
       
350 351 352
	src = (my_src_ptr) context->cinfo.src;

	context->cinfo.err = jpeg_std_error (&context->jerr.pub);
353
	context->jerr.pub.error_exit = fatal_error_handler;
Havoc Pennington's avatar
Havoc Pennington committed
354 355 356
        context->jerr.pub.output_message = output_message_handler;
        context->jerr.error = error;
        
357 358 359 360 361 362 363 364 365
	src = (my_src_ptr) context->cinfo.src;
	src->pub.init_source = init_source;
	src->pub.fill_input_buffer = fill_input_buffer;
	src->pub.skip_input_data = skip_input_data;
	src->pub.resync_to_restart = jpeg_resync_to_restart;
	src->pub.term_source = term_source;
	src->pub.bytes_in_buffer = 0;
	src->pub.next_input_byte = NULL;

Havoc Pennington's avatar
Havoc Pennington committed
366 367
        context->jerr.error = NULL;
        
368 369 370 371 372 373 374 375
	return (gpointer) context;
}

/*
 * context - returned from image_begin_load
 *
 * free context, unref gdk_pixbuf
 */
376 377
static gboolean
gdk_pixbuf__jpeg_image_stop_load (gpointer data, GError **error)
378 379
{
	JpegProgContext *context = (JpegProgContext *) data;
380

381
	g_return_val_if_fail (context != NULL, TRUE);
382

383 384 385 386
        /* FIXME this thing needs to report errors if
         * we have unused image data
         */
        
387
	if (context->pixbuf)
388
		g_object_unref (context->pixbuf);
389

390 391 392 393 394 395 396
	/* if we have an error? */
	if (sigsetjmp (context->jerr.setjmp_buffer, 1)) {
		jpeg_destroy_decompress (&context->cinfo);
	} else {
		jpeg_finish_decompress(&context->cinfo);
		jpeg_destroy_decompress(&context->cinfo);
	}
397

398 399 400 401 402 403 404
	if (context->cinfo.src) {
		my_src_ptr src = (my_src_ptr) context->cinfo.src;
		
		g_free (src);
	}

	g_free (context);
405 406

        return TRUE;
407 408 409 410 411 412 413 414 415 416 417 418
}




/*
 * context - from image_begin_load
 * buf - new image data
 * size - length of new image data
 *
 * append image data onto inrecrementally built output image
 */
419 420 421
static gboolean
gdk_pixbuf__jpeg_image_load_increment (gpointer data,
                                       const guchar *buf, guint size,
Havoc Pennington's avatar
Havoc Pennington committed
422
                                       GError **error)
423 424 425 426 427
{
	JpegProgContext *context = (JpegProgContext *)data;
	struct jpeg_decompress_struct *cinfo;
	my_src_ptr  src;
	guint       num_left, num_copy;
428 429
	guint       last_bytes_left;
	guint       spinguard;
430
	gboolean    first;
431
	const guchar *bufhd;
432 433 434 435 436

	g_return_val_if_fail (context != NULL, FALSE);
	g_return_val_if_fail (buf != NULL, FALSE);

	src = (my_src_ptr) context->cinfo.src;
437

438 439
	cinfo = &context->cinfo;

Havoc Pennington's avatar
Havoc Pennington committed
440 441
        context->jerr.error = error;
        
442 443 444 445
	/* XXXXXXX (drmike) - loop(s) below need to be recoded now I
         *                    have a grasp of what the flow needs to be!
         */

446 447 448 449
	/* check for fatal error */
	if (sigsetjmp (context->jerr.setjmp_buffer, 1)) {
		return FALSE;
	}
450

451 452 453 454 455 456 457 458
	/* skip over data if requested, handle unsigned int sizes cleanly */
	/* only can happen if we've already called jpeg_get_header once   */
	if (context->src_initialized && src->skip_next) {
		if (src->skip_next > size) {
			src->skip_next -= size;
			return TRUE;
		} else {
			num_left = size - src->skip_next;
459
			bufhd = buf + src->skip_next;
460 461 462 463
			src->skip_next = 0;
		}
	} else {
		num_left = size;
464
		bufhd = buf;
465 466
	}

467 468
	if (num_left == 0)
		return TRUE;
469

470 471
	last_bytes_left = 0;
	spinguard = 0;
472 473
	first = TRUE;
	while (TRUE) {
474 475 476 477 478 479 480

		/* handle any data from caller we haven't processed yet */
		if (num_left > 0) {
			if(src->pub.bytes_in_buffer && 
			   src->pub.next_input_byte != src->buffer)
				memmove(src->buffer, src->pub.next_input_byte,
					src->pub.bytes_in_buffer);
481 482


483 484
			num_copy = MIN (JPEG_PROG_BUF_SIZE - src->pub.bytes_in_buffer,
					num_left);
Elliot Lee's avatar
revert  
Elliot Lee committed
485

486 487 488 489 490 491 492 493 494 495
/*			if (num_copy == 0) 
				g_error ("Buffer overflow!");
*/
			memcpy(src->buffer + src->pub.bytes_in_buffer, bufhd,num_copy);
			src->pub.next_input_byte = src->buffer;
			src->pub.bytes_in_buffer += num_copy;
			bufhd += num_copy;
			num_left -= num_copy;
		} else {
		/* did anything change from last pass, if not return */
496
			if (first) {
497
				last_bytes_left = src->pub.bytes_in_buffer;
498 499
				first = FALSE;
			} else if (src->pub.bytes_in_buffer == last_bytes_left)
500 501 502 503
				spinguard++;
			else
				last_bytes_left = src->pub.bytes_in_buffer;
		}
Elliot Lee's avatar
revert  
Elliot Lee committed
504

505 506 507
		/* should not go through twice and not pull bytes out of buf */
		if (spinguard > 2)
			return TRUE;
508 509 510 511 512 513 514

		/* try to load jpeg header */
		if (!context->got_header) {
			int rc;

			rc = jpeg_read_header (cinfo, TRUE);
			context->src_initialized = TRUE;
515

516 517 518 519 520
			if (rc == JPEG_SUSPENDED)
				continue;

			context->got_header = TRUE;

521
#if 0
522 523 524 525 526
			if (jpeg_has_multiple_scans (cinfo)) {
				g_print ("io-jpeg.c: Does not currently "
					 "support progressive jpeg files.\n");
				return FALSE;
			}
527
#endif
528 529
			context->pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, 
							 FALSE,
530 531 532 533 534
							 8, 
							 cinfo->image_width,
							 cinfo->image_height);

			if (context->pixbuf == NULL) {
535 536 537 538 539
                                g_set_error (error,
                                             GDK_PIXBUF_ERROR,
                                             GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
                                             _("Couldn't allocate memory for loading JPEG file"));
                                return FALSE;
540 541
			}

542
			/* Use pixbuf buffer to store decompressed data */
543
			context->dptr = context->pixbuf->pixels;
544 545

			/* Notify the client that we are ready to go */
546
			(* context->prepared_func) (context->pixbuf,
Havoc Pennington's avatar
Havoc Pennington committed
547
                                                    NULL,
548
						    context->user_data);
549 550 551 552 553 554 555 556 557 558 559 560 561 562

		} else if (!context->did_prescan) {
			int rc;

			/* start decompression */
			rc = jpeg_start_decompress (cinfo);
			cinfo->do_fancy_upsampling = FALSE;
			cinfo->do_block_smoothing = FALSE;

			if (rc == JPEG_SUSPENDED)
				continue;

			context->did_prescan = TRUE;
		} else {
563

564 565 566
			/* we're decompressing so feed jpeg lib scanlines */
			guchar *lines[4];
			guchar **lptr;
Arturo Espinosa's avatar
Arturo Espinosa committed
567
			guchar *rowptr;
568 569 570
			gint   nlines, i;
			gint   start_scanline;

571
			/* keep going until we've done all scanlines */
572 573 574 575 576 577
			while (cinfo->output_scanline < cinfo->output_height) {
				start_scanline = cinfo->output_scanline;
				lptr = lines;
				rowptr = context->dptr;
				for (i=0; i < cinfo->rec_outbuf_height; i++) {
					*lptr++ = rowptr;
578
					rowptr += context->pixbuf->rowstride;
579
				}
580

581 582 583 584 585 586 587 588 589
				nlines = jpeg_read_scanlines (cinfo, lines,
							      cinfo->rec_outbuf_height);
				if (nlines == 0)
					break;

				/* handle gray */
				if (cinfo->output_components == 1)
					explode_gray_into_buf (cinfo, lines);

590
				context->dptr += nlines * context->pixbuf->rowstride;
591

592 593
				/* send updated signal */
				(* context->updated_func) (context->pixbuf,
594 595 596 597 598
							   0, 
							   cinfo->output_scanline-1,
							   cinfo->image_width, 
							   nlines,
							   context->user_data);
599

600
#undef DEBUG_JPEG_PROGRESSIVE
601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618
#ifdef DEBUG_JPEG_PROGRESSIVE
				
				if (start_scanline != cinfo->output_scanline)
					g_print("jpeg: Input pass=%2d, next input scanline=%3d,"
						" emitted %3d - %3d\n",
						cinfo->input_scan_number, cinfo->input_iMCU_row * 16,
						start_scanline, cinfo->output_scanline - 1);
				
				
				
				g_print ("Scanline %d of %d - ", 
					 cinfo->output_scanline,
					 cinfo->output_height);
/*			g_print ("rec_height %d -", cinfo->rec_outbuf_height); */
				g_print ("Processed %d lines - bytes left = %d\n",
					 nlines, cinfo->src->bytes_in_buffer);
#endif
			}
619
			/* did entire image */
620 621 622 623
			if (cinfo->output_scanline >= cinfo->output_height)
				return TRUE;
			else
				continue;
624 625 626 627 628
		}
	}

	return TRUE;
}
629

630
static gboolean
631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662
gdk_pixbuf__jpeg_image_save (FILE          *f, 
                             GdkPixbuf     *pixbuf, 
                             gchar        **keys,
                             gchar        **values,
                             GError       **error)
{
        /* FIXME error handling is broken */
        
       struct jpeg_compress_struct cinfo;
       guchar *buf = NULL;
       guchar *ptr;
       guchar *pixels = NULL;
       JSAMPROW *jbuf;
       int y = 0;
       int quality = 75; /* default; must be between 0 and 100 */
       int i, j;
       int w, h = 0;
       int rowstride = 0;
       struct error_handler_data jerr;

       if (keys && *keys) {
               gchar **kiter = keys;
               gchar **viter = values;

               while (*kiter) {
                       if (strcmp (*kiter, "quality") == 0) {
                               char *endptr = NULL;
                               quality = strtol (*viter, &endptr, 10);

                               if (endptr == *viter) {
                                       g_set_error (error,
                                                    GDK_PIXBUF_ERROR,
663
                                                    GDK_PIXBUF_ERROR_BAD_OPTION,
664 665 666 667 668 669 670 671 672 673 674 675 676 677
                                                    _("JPEG quality must be a value between 0 and 100; value '%s' could not be parsed."),
                                                    *viter);

                                       return FALSE;
                               }
                               
                               if (quality < 0 ||
                                   quality > 100) {
                                       /* This is a user-visible error;
                                        * lets people skip the range-checking
                                        * in their app.
                                        */
                                       g_set_error (error,
                                                    GDK_PIXBUF_ERROR,
678
                                                    GDK_PIXBUF_ERROR_BAD_OPTION,
679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704
                                                    _("JPEG quality must be a value between 0 and 100; value '%d' is not allowed."),
                                                    quality);

                                       return FALSE;
                               }
                       } else {
                               g_warning ("Bad option name '%s' passed to JPEG saver",
                                          *kiter);
                               return FALSE;
                       }
               
                       ++kiter;
                       ++viter;
               }
       }
       
       rowstride = gdk_pixbuf_get_rowstride (pixbuf);

       w = gdk_pixbuf_get_width (pixbuf);
       h = gdk_pixbuf_get_height (pixbuf);

       /* no image data? abort */
       pixels = gdk_pixbuf_get_pixels (pixbuf);
       g_return_val_if_fail (pixels != NULL, FALSE);

       /* allocate a small buffer to convert image data */
705 706 707 708 709 710 711 712
       buf = g_try_malloc (w * 3 * sizeof (guchar));
       if (!buf) {
	       g_set_error (error,
			    GDK_PIXBUF_ERROR,
			    GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
			    _("Couldn't allocate memory for loading JPEG file"));
	       return FALSE;
       }
713 714 715

       /* set up error handling */
       jerr.pub.error_exit = fatal_error_handler;
Havoc Pennington's avatar
Havoc Pennington committed
716 717 718
       jerr.pub.output_message = output_message_handler;
       jerr.error = error;
       
719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759
       cinfo.err = jpeg_std_error (&(jerr.pub));
       if (sigsetjmp (jerr.setjmp_buffer, 1)) {
               jpeg_destroy_compress (&cinfo);
               free (buf);
               return FALSE;
       }

       /* setup compress params */
       jpeg_create_compress (&cinfo);
       jpeg_stdio_dest (&cinfo, f);
       cinfo.image_width      = w;
       cinfo.image_height     = h;
       cinfo.input_components = 3; 
       cinfo.in_color_space   = JCS_RGB;

       /* set up jepg compression parameters */
       jpeg_set_defaults (&cinfo);
       jpeg_set_quality (&cinfo, quality, TRUE);
       jpeg_start_compress (&cinfo, TRUE);
       /* get the start pointer */
       ptr = pixels;
       /* go one scanline at a time... and save */
       i = 0;
       while (cinfo.next_scanline < cinfo.image_height) {
               /* convert scanline from ARGB to RGB packed */
               for (j = 0; j < w; j++)
                       memcpy (&(buf[j*3]), &(ptr[i*rowstride + j*3]), 3);

               /* write scanline */
               jbuf = (JSAMPROW *)(&buf);
               jpeg_write_scanlines (&cinfo, jbuf, 1);
               i++;
               y++;

       }
       
       /* finish off */
       jpeg_finish_compress (&cinfo);   
       free (buf);
       return TRUE;
}
760 761 762 763 764 765 766 767 768 769

void
gdk_pixbuf__jpeg_fill_vtable (GdkPixbufModule *module)
{
  module->load = gdk_pixbuf__jpeg_image_load;
  module->begin_load = gdk_pixbuf__jpeg_image_begin_load;
  module->stop_load = gdk_pixbuf__jpeg_image_stop_load;
  module->load_increment = gdk_pixbuf__jpeg_image_load_increment;
  module->save = gdk_pixbuf__jpeg_image_save;
}