Recommitting, perserving history

This commit is contained in:
Manish Singh 2005-01-03 21:36:43 +00:00
parent 4d1396596d
commit e5943a6393
6 changed files with 2812 additions and 0 deletions

912
plug-ins/jpeg/jpeg-load.c Normal file
View File

@ -0,0 +1,912 @@
/* The GIMP -- an image manipulation program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "jpeg.h"
#include "jpeg-load.h"
/* Read next byte */
static guint
jpeg_getc (j_decompress_ptr cinfo)
{
struct jpeg_source_mgr *datasrc = cinfo->src;
if (datasrc->bytes_in_buffer == 0)
{
if (! (*datasrc->fill_input_buffer) (cinfo))
ERREXIT (cinfo, JERR_CANT_SUSPEND);
}
datasrc->bytes_in_buffer--;
return *datasrc->next_input_byte++;
}
/* We need our own marker parser, since early versions of libjpeg
* don't keep a conventient list of the marker's that have been
* seen. */
/*
* Marker processor for COM markers.
* This replaces the library's built-in processor, which just skips the marker.
* Note this code relies on a non-suspending data source.
*/
static GString *local_image_comments = NULL;
static boolean
COM_handler (j_decompress_ptr cinfo)
{
gint length;
guint ch;
length = jpeg_getc (cinfo) << 8;
length += jpeg_getc (cinfo);
if (length < 2)
return FALSE;
length -= 2; /* discount the length word itself */
if (!local_image_comments)
local_image_comments = g_string_new (NULL);
else
g_string_append_c (local_image_comments, '\n');
while (length-- > 0)
{
ch = jpeg_getc (cinfo);
g_string_append_c (local_image_comments, ch);
}
return TRUE;
}
gint32
load_image (const gchar *filename,
GimpRunMode runmode,
gboolean preview)
{
GimpPixelRgn pixel_rgn;
GimpDrawable *drawable;
gint32 volatile image_ID;
gint32 layer_ID;
struct jpeg_decompress_struct cinfo;
struct my_error_mgr jerr;
FILE *infile;
guchar *buf;
guchar * volatile padded_buf = NULL;
guchar **rowbuf;
gint image_type;
gint layer_type;
gint tile_height;
gint scanlines;
gint i, start, end;
GimpParasite * volatile comment_parasite = NULL;
#ifdef HAVE_EXIF
GimpParasite *exif_parasite = NULL;
ExifData *exif_data = NULL;
#endif
/* We set up the normal JPEG error routines. */
cinfo.err = jpeg_std_error (&jerr.pub);
jerr.pub.error_exit = my_error_exit;
/* flag warnings, so we try to ignore corrupt EXIF data */
if (!preview)
{
cinfo.client_data = GINT_TO_POINTER (FALSE);
jerr.pub.emit_message = my_emit_message;
jerr.pub.output_message = my_output_message;
}
if ((infile = fopen (filename, "rb")) == NULL)
{
g_message (_("Could not open '%s' for reading: %s"),
gimp_filename_to_utf8 (filename), g_strerror (errno));
gimp_quit ();
}
if (!preview)
{
gchar *name = g_strdup_printf (_("Opening '%s'..."),
gimp_filename_to_utf8 (filename));
gimp_progress_init (name);
g_free (name);
}
image_ID = -1;
/* Establish the setjmp return context for my_error_exit to use. */
if (setjmp (jerr.setjmp_buffer))
{
/* If we get here, the JPEG code has signaled an error.
* We need to clean up the JPEG object, close the input file, and return.
*/
jpeg_destroy_decompress (&cinfo);
if (infile)
fclose (infile);
if (image_ID != -1 && !preview)
gimp_image_delete (image_ID);
if (preview)
destroy_preview();
gimp_quit ();
}
/* Now we can initialize the JPEG decompression object. */
jpeg_create_decompress (&cinfo);
/* Step 2: specify data source (eg, a file) */
jpeg_stdio_src (&cinfo, infile);
/* pw - step 2.1 let the lib know we want the comments. */
jpeg_set_marker_processor (&cinfo, JPEG_COM, COM_handler);
/* Step 3: read file parameters with jpeg_read_header() */
(void) jpeg_read_header (&cinfo, TRUE);
/* We can ignore the return value from jpeg_read_header since
* (a) suspension is not possible with the stdio data source, and
* (b) we passed TRUE to reject a tables-only JPEG file as an error.
* See libjpeg.doc for more info.
*/
if (!preview)
{
/* if we had any comments then make a parasite for them */
if (local_image_comments && local_image_comments->len)
{
gchar *comment = local_image_comments->str;
g_string_free (local_image_comments, FALSE);
local_image_comments = NULL;
if (g_utf8_validate (comment, -1, NULL))
comment_parasite = gimp_parasite_new ("gimp-comment",
GIMP_PARASITE_PERSISTENT,
strlen (comment) + 1,
comment);
g_free (comment);
}
/* Do not attach the "jpeg-save-options" parasite to the image
* because this conflics with the global defaults. See bug #75398:
* http://bugzilla.gnome.org/show_bug.cgi?id=75398 */
}
/* Step 4: set parameters for decompression */
/* In this example, we don't need to change any of the defaults set by
* jpeg_read_header(), so we do nothing here.
*/
/* Step 5: Start decompressor */
jpeg_start_decompress (&cinfo);
/* We may need to do some setup of our own at this point before reading
* the data. After jpeg_start_decompress() we have the correct scaled
* output image dimensions available, as well as the output colormap
* if we asked for color quantization.
* In this example, we need to make an output work buffer of the right size.
*/
/* temporary buffer */
tile_height = gimp_tile_height ();
buf = g_new (guchar,
tile_height * cinfo.output_width * cinfo.output_components);
rowbuf = g_new (guchar*, tile_height);
for (i = 0; i < tile_height; i++)
rowbuf[i] = buf + cinfo.output_width * cinfo.output_components * i;
/* Create a new image of the proper size and associate the filename with it.
Preview layers, not being on the bottom of a layer stack, MUST HAVE
AN ALPHA CHANNEL!
*/
switch (cinfo.output_components)
{
case 1:
image_type = GIMP_GRAY;
layer_type = preview ? GIMP_GRAYA_IMAGE : GIMP_GRAY_IMAGE;
break;
case 3:
image_type = GIMP_RGB;
layer_type = preview ? GIMP_RGBA_IMAGE : GIMP_RGB_IMAGE;
break;
case 4:
if (cinfo.out_color_space == JCS_CMYK)
{
image_type = GIMP_RGB;
layer_type = GIMP_RGB_IMAGE;
break;
}
/*fallthrough*/
default:
g_message ("Don't know how to load JPEGs\n"
"with %d color channels\n"
"using colorspace %d (%d)",
cinfo.output_components, cinfo.out_color_space,
cinfo.jpeg_color_space);
gimp_quit ();
break;
}
if (preview)
padded_buf = g_new (guchar, tile_height * cinfo.output_width *
(cinfo.output_components + 1));
else if (cinfo.out_color_space == JCS_CMYK)
padded_buf = g_new (guchar, tile_height * cinfo.output_width * 3);
else
padded_buf = NULL;
if (preview)
{
image_ID = image_ID_global;
}
else
{
image_ID = gimp_image_new (cinfo.output_width, cinfo.output_height,
image_type);
gimp_image_set_filename (image_ID, filename);
}
if (preview)
{
layer_ID_global = layer_ID =
gimp_layer_new (image_ID, _("JPEG preview"),
cinfo.output_width,
cinfo.output_height,
layer_type, 100, GIMP_NORMAL_MODE);
}
else
{
layer_ID = gimp_layer_new (image_ID, _("Background"),
cinfo.output_width,
cinfo.output_height,
layer_type, 100, GIMP_NORMAL_MODE);
}
drawable_global = drawable = gimp_drawable_get (layer_ID);
gimp_pixel_rgn_init (&pixel_rgn, drawable, 0, 0,
drawable->width, drawable->height, TRUE, FALSE);
/* Step 5.1: if the file had resolution information, set it on the image */
if (!preview && cinfo.saw_JFIF_marker)
{
gdouble xresolution;
gdouble yresolution;
gdouble asymmetry;
xresolution = cinfo.X_density;
yresolution = cinfo.Y_density;
switch (cinfo.density_unit)
{
case 0: /* unknown -> set the aspect ratio but use the default
* image resolution
*/
if (cinfo.Y_density != 0)
asymmetry = xresolution / yresolution;
else
asymmetry = 1.0;
gimp_image_get_resolution (image_ID, &xresolution, &yresolution);
xresolution *= asymmetry;
break;
case 1: /* dots per inch */
break;
case 2: /* dots per cm */
xresolution *= 2.54;
yresolution *= 2.54;
gimp_image_set_unit (image_ID, GIMP_UNIT_MM);
break;
default:
g_message ("Unknown density unit %d\nassuming dots per inch",
cinfo.density_unit);
break;
}
gimp_image_set_resolution (image_ID, xresolution, yresolution);
}
/* Step 6: while (scan lines remain to be read) */
/* jpeg_read_scanlines(...); */
/* Here we use the library's state variable cinfo.output_scanline as the
* loop counter, so that we don't have to keep track ourselves.
*/
while (cinfo.output_scanline < cinfo.output_height)
{
start = cinfo.output_scanline;
end = cinfo.output_scanline + tile_height;
end = MIN (end, cinfo.output_height);
scanlines = end - start;
for (i = 0; i < scanlines; i++)
jpeg_read_scanlines (&cinfo, (JSAMPARRAY) &rowbuf[i], 1);
if (preview) /* Add a dummy alpha channel -- convert buf to padded_buf */
{
guchar *dest = padded_buf;
guchar *src = buf;
gint num = drawable->width * scanlines;
switch (cinfo.output_components)
{
case 1:
for (i = 0; i < num; i++)
{
*(dest++) = *(src++);
*(dest++) = 255;
}
break;
case 3:
for (i = 0; i < num; i++)
{
*(dest++) = *(src++);
*(dest++) = *(src++);
*(dest++) = *(src++);
*(dest++) = 255;
}
break;
default:
g_warning ("JPEG - shouldn't have gotten here.\nReport to http://bugzilla.gnome.org/");
break;
}
}
else if (cinfo.out_color_space == JCS_CMYK) /* buf-> RGB in padded_buf */
{
guchar *dest = padded_buf;
guchar *src = buf;
gint num = drawable->width * scanlines;
for (i = 0; i < num; i++)
{
guint r_c, g_m, b_y, a_k;
r_c = *(src++);
g_m = *(src++);
b_y = *(src++);
a_k = *(src++);
*(dest++) = (r_c * a_k) / 255;
*(dest++) = (g_m * a_k) / 255;
*(dest++) = (b_y * a_k) / 255;
}
}
gimp_pixel_rgn_set_rect (&pixel_rgn, padded_buf ? padded_buf : buf,
0, start, drawable->width, scanlines);
if (! preview)
gimp_progress_update ((gdouble) cinfo.output_scanline /
(gdouble) cinfo.output_height);
}
/* Step 7: Finish decompression */
jpeg_finish_decompress (&cinfo);
/* We can ignore the return value since suspension is not possible
* with the stdio data source.
*/
/* Step 8: Release JPEG decompression object */
/* This is an important step since it will release a good deal of memory. */
jpeg_destroy_decompress (&cinfo);
/* free up the temporary buffers */
g_free (rowbuf);
g_free (buf);
g_free (padded_buf);
/* After finish_decompress, we can close the input file.
* Here we postpone it until after no more JPEG errors are possible,
* so as to simplify the setjmp error logic above. (Actually, I don't
* think that jpeg_destroy can do an error exit, but why assume anything...)
*/
fclose (infile);
/* At this point you may want to check to see whether any corrupt-data
* warnings occurred (test whether jerr.num_warnings is nonzero).
*/
/* Tell the GIMP to display the image.
*/
gimp_image_add_layer (image_ID, layer_ID, 0);
gimp_drawable_flush (drawable);
/* pw - Last of all, attach the parasites (couldn't do it earlier -
there was no image. */
if (!preview)
{
if (comment_parasite)
{
gimp_image_parasite_attach (image_ID, comment_parasite);
gimp_parasite_free (comment_parasite);
comment_parasite = NULL;
}
#ifdef HAVE_EXIF
#define EXIF_HEADER_SIZE 8
if (! GPOINTER_TO_INT (cinfo.client_data))
{
exif_data = exif_data_new_from_file (filename);
if (exif_data)
{
guchar *exif_buf;
guint exif_buf_len;
exif_data_save_data (exif_data, &exif_buf, &exif_buf_len);
exif_data_unref (exif_data);
if (exif_buf_len > EXIF_HEADER_SIZE)
{
exif_parasite = gimp_parasite_new ("exif-data",
GIMP_PARASITE_PERSISTENT,
exif_buf_len, exif_buf);
gimp_image_parasite_attach (image_ID, exif_parasite);
gimp_parasite_free (exif_parasite);
}
free (exif_buf);
}
}
#endif
}
return image_ID;
}
#ifdef HAVE_EXIF
typedef struct
{
struct jpeg_source_mgr pub; /* public fields */
gchar *buffer;
gint size;
JOCTET terminal[2];
} my_source_mgr;
typedef my_source_mgr * my_src_ptr;
static void
init_source (j_decompress_ptr cinfo)
{
}
static boolean
fill_input_buffer (j_decompress_ptr cinfo)
{
my_src_ptr src = (my_src_ptr) cinfo->src;
/* Since we have given all we have got already
* we simply fake an end of file
*/
src->pub.next_input_byte = src->terminal;
src->pub.bytes_in_buffer = 2;
src->terminal[0] = (JOCTET) 0xFF;
src->terminal[1] = (JOCTET) JPEG_EOI;
return TRUE;
}
static void
skip_input_data (j_decompress_ptr cinfo,
long num_bytes)
{
my_src_ptr src = (my_src_ptr) cinfo->src;
src->pub.next_input_byte = src->pub.next_input_byte + num_bytes;
}
static void
term_source (j_decompress_ptr cinfo)
{
}
gint32
load_thumbnail_image (const gchar *filename,
gint *width,
gint *height)
{
gint32 volatile image_ID;
ExifData *exif_data;
GimpPixelRgn pixel_rgn;
GimpDrawable *drawable;
gint32 layer_ID;
struct jpeg_decompress_struct cinfo;
struct my_error_mgr jerr;
guchar *buf;
guchar * volatile padded_buf = NULL;
guchar **rowbuf;
gint image_type;
gint layer_type;
gint tile_height;
gint scanlines;
gint i, start, end;
my_src_ptr src;
FILE *infile;
image_ID = -1;
exif_data = exif_data_new_from_file (filename);
if (! ((exif_data) && (exif_data->data) && (exif_data->size > 0)))
return -1;
cinfo.err = jpeg_std_error (&jerr.pub);
jerr.pub.error_exit = my_error_exit;
cinfo.client_data = GINT_TO_POINTER (FALSE);
jerr.pub.emit_message = my_emit_message;
jerr.pub.output_message = my_output_message;
{
gchar *name = g_strdup_printf (_("Opening thumbnail for '%s'..."),
gimp_filename_to_utf8 (filename));
gimp_progress_init (name);
g_free (name);
}
/* Establish the setjmp return context for my_error_exit to use. */
if (setjmp (jerr.setjmp_buffer))
{
/* If we get here, the JPEG code has signaled an error. We
* need to clean up the JPEG object, close the input file,
* and return.
*/
jpeg_destroy_decompress (&cinfo);
if (image_ID != -1)
gimp_image_delete (image_ID);
if (exif_data)
{
exif_data_unref (exif_data);
exif_data = NULL;
}
return -1;
}
/* Now we can initialize the JPEG decompression object. */
jpeg_create_decompress (&cinfo);
/* Step 2: specify data source (eg, a file) */
if (cinfo.src == NULL)
cinfo.src = (struct jpeg_source_mgr *)(*cinfo.mem->alloc_small)
((j_common_ptr) &cinfo, JPOOL_PERMANENT,
sizeof (my_source_mgr));
src = (my_src_ptr) cinfo.src;
src->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 = exif_data->size;
src->pub.next_input_byte = exif_data->data;
src->buffer = exif_data->data;
src->size = exif_data->size;
/* Step 3: read file parameters with jpeg_read_header() */
jpeg_read_header (&cinfo, TRUE);
/* Step 4: set parameters for decompression */
/* In this example, we don't need to change any of the defaults set by
* jpeg_read_header(), so we do nothing here.
*/
/* Step 5: Start decompressor */
jpeg_start_decompress (&cinfo);
/* We may need to do some setup of our own at this point before
* reading the data. After jpeg_start_decompress() we have the
* correct scaled output image dimensions available, as well as
* the output colormap if we asked for color quantization. In
* this example, we need to make an output work buffer of the
* right size.
*/
/* temporary buffer */
tile_height = gimp_tile_height ();
buf = g_new (guchar,
tile_height * cinfo.output_width * cinfo.output_components);
rowbuf = g_new (guchar *, tile_height);
for (i = 0; i < tile_height; i++)
rowbuf[i] = buf + cinfo.output_width * cinfo.output_components * i;
/* Create a new image of the proper size and associate the
* filename with it.
*
* Preview layers, not being on the bottom of a layer stack,
* MUST HAVE AN ALPHA CHANNEL!
*/
switch (cinfo.output_components)
{
case 1:
image_type = GIMP_GRAY;
layer_type = GIMP_GRAY_IMAGE;
break;
case 3:
image_type = GIMP_RGB;
layer_type = GIMP_RGB_IMAGE;
break;
case 4:
if (cinfo.out_color_space == JCS_CMYK)
{
image_type = GIMP_RGB;
layer_type = GIMP_RGB_IMAGE;
break;
}
/*fallthrough*/
default:
g_message ("Don't know how to load JPEGs\n"
"with %d color channels\n"
"using colorspace %d (%d)",
cinfo.output_components, cinfo.out_color_space,
cinfo.jpeg_color_space);
if (exif_data)
{
exif_data_unref (exif_data);
exif_data = NULL;
}
return -1;
break;
}
if (cinfo.out_color_space == JCS_CMYK)
padded_buf = g_new (guchar, tile_height * cinfo.output_width * 3);
else
padded_buf = NULL;
image_ID = gimp_image_new (cinfo.output_width, cinfo.output_height,
image_type);
gimp_image_set_filename (image_ID, filename);
layer_ID = gimp_layer_new (image_ID, _("Background"),
cinfo.output_width,
cinfo.output_height,
layer_type, 100, GIMP_NORMAL_MODE);
drawable_global = drawable = gimp_drawable_get (layer_ID);
gimp_pixel_rgn_init (&pixel_rgn, drawable, 0, 0,
drawable->width, drawable->height, TRUE, FALSE);
/* Step 5.1: if the file had resolution information, set it on the image */
if (cinfo.saw_JFIF_marker)
{
gdouble xresolution;
gdouble yresolution;
gdouble asymmetry;
xresolution = cinfo.X_density;
yresolution = cinfo.Y_density;
switch (cinfo.density_unit)
{
case 0: /* unknown -> set the aspect ratio but use the default
* image resolution
*/
if (cinfo.Y_density != 0)
asymmetry = xresolution / yresolution;
else
asymmetry = 1.0;
gimp_image_get_resolution (image_ID, &xresolution, &yresolution);
xresolution *= asymmetry;
break;
case 1: /* dots per inch */
break;
case 2: /* dots per cm */
xresolution *= 2.54;
yresolution *= 2.54;
gimp_image_set_unit (image_ID, GIMP_UNIT_MM);
break;
default:
g_message ("Unknown density unit %d\nassuming dots per inch",
cinfo.density_unit);
break;
}
gimp_image_set_resolution (image_ID, xresolution, yresolution);
}
/* Step 6: while (scan lines remain to be read) */
/* jpeg_read_scanlines(...); */
/* Here we use the library's state variable cinfo.output_scanline as the
* loop counter, so that we don't have to keep track ourselves.
*/
while (cinfo.output_scanline < cinfo.output_height)
{
start = cinfo.output_scanline;
end = cinfo.output_scanline + tile_height;
end = MIN (end, cinfo.output_height);
scanlines = end - start;
for (i = 0; i < scanlines; i++)
jpeg_read_scanlines (&cinfo, (JSAMPARRAY) &rowbuf[i], 1);
if (cinfo.out_color_space == JCS_CMYK) /* buf-> RGB in padded_buf */
{
guchar *dest = padded_buf;
guchar *src = buf;
gint num = drawable->width * scanlines;
for (i = 0; i < num; i++)
{
guint r_c, g_m, b_y, a_k;
r_c = *(src++);
g_m = *(src++);
b_y = *(src++);
a_k = *(src++);
*(dest++) = (r_c * a_k) / 255;
*(dest++) = (g_m * a_k) / 255;
*(dest++) = (b_y * a_k) / 255;
}
}
gimp_pixel_rgn_set_rect (&pixel_rgn, padded_buf ? padded_buf : buf,
0, start, drawable->width, scanlines);
gimp_progress_update ((gdouble) cinfo.output_scanline /
(gdouble) cinfo.output_height);
}
/* Step 7: Finish decompression */
jpeg_finish_decompress (&cinfo);
/* We can ignore the return value since suspension is not possible
* with the stdio data source.
*/
/* Step 8: Release JPEG decompression object */
/* This is an important step since it will release a good deal
* of memory.
*/
jpeg_destroy_decompress (&cinfo);
/* free up the temporary buffers */
g_free (rowbuf);
g_free (buf);
g_free (padded_buf);
/* At this point you may want to check to see whether any
* corrupt-data warnings occurred (test whether
* jerr.num_warnings is nonzero).
*/
gimp_image_add_layer (image_ID, layer_ID, 0);
/* NOW to get the dimensions of the actual image to return the
* calling app
*/
cinfo.err = jpeg_std_error (&jerr.pub);
jerr.pub.error_exit = my_error_exit;
cinfo.client_data = GINT_TO_POINTER (FALSE);
jerr.pub.emit_message = my_emit_message;
jerr.pub.output_message = my_output_message;
if ((infile = fopen (filename, "rb")) == NULL)
{
g_message (_("Could not open '%s' for reading: %s"),
gimp_filename_to_utf8 (filename), g_strerror (errno));
if (exif_data)
{
exif_data_unref (exif_data);
exif_data = NULL;
}
return -1;
}
/* Establish the setjmp return context for my_error_exit to use. */
if (setjmp (jerr.setjmp_buffer))
{
/* If we get here, the JPEG code has signaled an error. We
* need to clean up the JPEG object, close the input file,
* and return.
*/
jpeg_destroy_decompress (&cinfo);
if (image_ID != -1)
gimp_image_delete (image_ID);
if (exif_data)
{
exif_data_unref (exif_data);
exif_data = NULL;
}
return -1;
}
/* Now we can initialize the JPEG decompression object. */
jpeg_create_decompress (&cinfo);
/* Step 2: specify data source (eg, a file) */
jpeg_stdio_src (&cinfo, infile);
/* Step 3: read file parameters with jpeg_read_header() */
jpeg_read_header (&cinfo, TRUE);
jpeg_start_decompress (&cinfo);
*width = cinfo.output_width;
*height = cinfo.output_height;
/* Step 4: Release JPEG decompression object */
/* This is an important step since it will release a good deal
* of memory.
*/
jpeg_destroy_decompress (&cinfo);
if (exif_data)
{
exif_data_unref (exif_data);
exif_data = NULL;
}
return image_ID;
}
#endif /* HAVE_EXIF */

30
plug-ins/jpeg/jpeg-load.h Normal file
View File

@ -0,0 +1,30 @@
/* The GIMP -- an image manipulation program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
gint32 load_image (const gchar *filename,
GimpRunMode runmode,
gboolean preview);
#ifdef HAVE_EXIF
gint32 load_thumbnail_image (const gchar *filename,
gint *width,
gint *height);
#endif /* HAVE_EXIF */

1167
plug-ins/jpeg/jpeg-save.c Normal file

File diff suppressed because it is too large Load Diff

74
plug-ins/jpeg/jpeg-save.h Normal file
View File

@ -0,0 +1,74 @@
/* The GIMP -- an image manipulation program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#define SCALE_WIDTH 125
/* if you are not compiling this from inside the gimp tree, you have to */
/* take care yourself if your JPEG library supports progressive mode */
/* #undef HAVE_PROGRESSIVE_JPEG if your library doesn't support it */
/* #define HAVE_PROGRESSIVE_JPEG if your library knows how to handle it */
/* See bugs #63610 and #61088 for a discussion about the quality settings */
#define DEFAULT_QUALITY 85
#define DEFAULT_SMOOTHING 0.0
#define DEFAULT_OPTIMIZE TRUE
#define DEFAULT_PROGRESSIVE FALSE
#define DEFAULT_BASELINE TRUE
#define DEFAULT_SUBSMP 0
#define DEFAULT_RESTART 0
#define DEFAULT_DCT 0
#define DEFAULT_PREVIEW FALSE
#define DEFAULT_EXIF TRUE
#define DEFAULT_THUMBNAIL FALSE
typedef struct
{
gdouble quality;
gdouble smoothing;
gboolean optimize;
gboolean progressive;
gboolean baseline;
gint subsmp;
gint restart;
gint dct;
gboolean preview;
gboolean save_exif;
gboolean save_thumbnail;
} JpegSaveVals;
JpegSaveVals jsvals;
gint32 orig_image_ID_global;
gint32 drawable_ID_global;
gboolean save_image (const gchar *filename,
gint32 image_ID,
gint32 drawable_ID,
gint32 orig_image_ID,
gboolean preview);
gboolean save_dialog (void);
#ifdef HAVE_EXIF
gint create_thumbnail (gint32 image_ID,
gint32 drawable_ID,
gchar **thumbnail_buffer);
#endif /* HAVE_EXIF */

553
plug-ins/jpeg/jpeg.c Normal file
View File

@ -0,0 +1,553 @@
/* The GIMP -- an image manipulation program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "jpeg.h"
#include "jpeg-save.h"
#include "jpeg-load.h"
/* Declare local functions.
*/
static void query (void);
static void run (const gchar *name,
gint nparams,
const GimpParam *param,
gint *nreturn_vals,
GimpParam **return_vals);
GimpPlugInInfo PLUG_IN_INFO =
{
NULL, /* init_proc */
NULL, /* quit_proc */
query, /* query_proc */
run, /* run_proc */
};
MAIN ()
static void
query (void)
{
static GimpParamDef load_args[] =
{
{ GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" },
{ GIMP_PDB_STRING, "filename", "The name of the file to load" },
{ GIMP_PDB_STRING, "raw_filename", "The name of the file to load" }
};
static GimpParamDef load_return_vals[] =
{
{ GIMP_PDB_IMAGE, "image", "Output image" }
};
#ifdef HAVE_EXIF
static GimpParamDef thumb_args[] =
{
{ GIMP_PDB_STRING, "filename", "The name of the file to load" },
{ GIMP_PDB_INT32, "thumb_size", "Preferred thumbnail size" }
};
static GimpParamDef thumb_return_vals[] =
{
{ GIMP_PDB_IMAGE, "image", "Thumbnail image" },
{ GIMP_PDB_INT32, "image_width", "Width of full-sized image" },
{ GIMP_PDB_INT32, "image_height", "Height of full-sized image" }
};
#endif /* HAVE_EXIF */
static GimpParamDef save_args[] =
{
{ GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" },
{ GIMP_PDB_IMAGE, "image", "Input image" },
{ GIMP_PDB_DRAWABLE, "drawable", "Drawable to save" },
{ GIMP_PDB_STRING, "filename", "The name of the file to save the image in" },
{ GIMP_PDB_STRING, "raw_filename", "The name of the file to save the image in" },
{ GIMP_PDB_FLOAT, "quality", "Quality of saved image (0 <= quality <= 1)" },
{ GIMP_PDB_FLOAT, "smoothing", "Smoothing factor for saved image (0 <= smoothing <= 1)" },
{ GIMP_PDB_INT32, "optimize", "Optimization of entropy encoding parameters (0/1)" },
{ GIMP_PDB_INT32, "progressive", "Enable progressive jpeg image loading - ignored if not compiled with HAVE_PROGRESSIVE_JPEG (0/1)" },
{ GIMP_PDB_STRING, "comment", "Image comment" },
{ GIMP_PDB_INT32, "subsmp", "The subsampling option number" },
{ GIMP_PDB_INT32, "baseline", "Force creation of a baseline JPEG (non-baseline JPEGs can't be read by all decoders) (0/1)" },
{ GIMP_PDB_INT32, "restart", "Frequency of restart markers (in rows, 0 = no restart markers)" },
{ GIMP_PDB_INT32, "dct", "DCT algorithm to use (speed/quality tradeoff)" }
};
gimp_install_procedure ("file_jpeg_load",
"loads files in the JPEG file format",
"loads files in the JPEG file format",
"Spencer Kimball, Peter Mattis & others",
"Spencer Kimball & Peter Mattis",
"1995-1999",
N_("JPEG image"),
NULL,
GIMP_PLUGIN,
G_N_ELEMENTS (load_args),
G_N_ELEMENTS (load_return_vals),
load_args, load_return_vals);
gimp_register_file_handler_mime ("file_jpeg_load", "image/jpeg");
gimp_register_magic_load_handler ("file_jpeg_load",
"jpg,jpeg,jpe",
"",
"6,string,JFIF,6,string,Exif");
#ifdef HAVE_EXIF
gimp_install_procedure ("file_jpeg_load_thumb",
"Loads a thumbnail from a JPEG image",
"Loads a thumbnail from a JPEG image (only if it exists)",
"S. Mukund <muks@mukund.org>, Sven Neumann <sven@gimp.org>",
"S. Mukund <muks@mukund.org>, Sven Neumann <sven@gimp.org>",
"November 15, 2004",
NULL,
NULL,
GIMP_PLUGIN,
G_N_ELEMENTS (thumb_args),
G_N_ELEMENTS (thumb_return_vals),
thumb_args, thumb_return_vals);
gimp_register_thumbnail_loader ("file_jpeg_load", "file_jpeg_load_thumb");
#endif /* HAVE_EXIF */
gimp_install_procedure ("file_jpeg_save",
"saves files in the JPEG file format",
"saves files in the lossy, widely supported JPEG format",
"Spencer Kimball, Peter Mattis & others",
"Spencer Kimball & Peter Mattis",
"1995-1999",
N_("JPEG image"),
"RGB*, GRAY*",
GIMP_PLUGIN,
G_N_ELEMENTS (save_args), 0,
save_args, NULL);
gimp_register_file_handler_mime ("file_jpeg_save", "image/jpeg");
gimp_register_save_handler ("file_jpeg_save", "jpg,jpeg,jpe", "");
}
static void
run (const gchar *name,
gint nparams,
const GimpParam *param,
gint *nreturn_vals,
GimpParam **return_vals)
{
static GimpParam values[4];
GimpRunMode run_mode;
GimpPDBStatusType status = GIMP_PDB_SUCCESS;
gint32 image_ID;
gint32 drawable_ID;
gint32 orig_image_ID;
GimpParasite *parasite;
gboolean err;
GimpExportReturn export = GIMP_EXPORT_CANCEL;
run_mode = param[0].data.d_int32;
INIT_I18N ();
*nreturn_vals = 1;
*return_vals = values;
values[0].type = GIMP_PDB_STATUS;
values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
if (strcmp (name, "file_jpeg_load") == 0)
{
image_ID = load_image (param[1].data.d_string, run_mode, FALSE);
if (image_ID != -1)
{
*nreturn_vals = 2;
values[1].type = GIMP_PDB_IMAGE;
values[1].data.d_image = image_ID;
}
else
{
status = GIMP_PDB_EXECUTION_ERROR;
}
}
#ifdef HAVE_EXIF
else if (strcmp (name, "file_jpeg_load_thumb") == 0)
{
if (nparams < 2)
{
status = GIMP_PDB_CALLING_ERROR;
}
else
{
const gchar *filename = param[0].data.d_string;
gint width = 0;
gint height = 0;
gint32 image_ID;
image_ID = load_thumbnail_image (filename, &width, &height);
if (image_ID != -1)
{
*nreturn_vals = 4;
values[1].type = GIMP_PDB_IMAGE;
values[1].data.d_image = image_ID;
values[2].type = GIMP_PDB_INT32;
values[2].data.d_int32 = width;
values[3].type = GIMP_PDB_INT32;
values[3].data.d_int32 = height;
}
else
{
status = GIMP_PDB_EXECUTION_ERROR;
}
}
}
#endif /* HAVE_EXIF */
else if (strcmp (name, "file_jpeg_save") == 0)
{
image_ID = orig_image_ID = param[1].data.d_int32;
drawable_ID = param[2].data.d_int32;
/* eventually export the image */
switch (run_mode)
{
case GIMP_RUN_INTERACTIVE:
case GIMP_RUN_WITH_LAST_VALS:
gimp_ui_init ("jpeg", FALSE);
export = gimp_export_image (&image_ID, &drawable_ID, "JPEG",
(GIMP_EXPORT_CAN_HANDLE_RGB |
GIMP_EXPORT_CAN_HANDLE_GRAY));
switch (export)
{
case GIMP_EXPORT_EXPORT:
{
gchar *tmp = g_filename_from_utf8 (_("Export Preview"), -1,
NULL, NULL, NULL);
if (tmp)
{
gimp_image_set_filename (image_ID, tmp);
g_free (tmp);
}
display_ID = -1;
}
break;
case GIMP_EXPORT_IGNORE:
break;
case GIMP_EXPORT_CANCEL:
values[0].data.d_status = GIMP_PDB_CANCEL;
return;
break;
}
break;
default:
break;
}
#ifdef HAVE_EXIF
exif_data_unref (exif_data);
exif_data = NULL;
#endif /* HAVE_EXIF */
g_free (image_comment);
image_comment = NULL;
parasite = gimp_image_parasite_find (orig_image_ID, "gimp-comment");
if (parasite)
{
image_comment = g_strndup (gimp_parasite_data (parasite),
gimp_parasite_data_size (parasite));
gimp_parasite_free (parasite);
}
#ifdef HAVE_EXIF
parasite = gimp_image_parasite_find (orig_image_ID, "exif-data");
if (parasite)
{
exif_data = exif_data_new_from_data (gimp_parasite_data (parasite),
gimp_parasite_data_size (parasite));
gimp_parasite_free (parasite);
}
#endif /* HAVE_EXIF */
jsvals.quality = DEFAULT_QUALITY;
jsvals.smoothing = DEFAULT_SMOOTHING;
jsvals.optimize = DEFAULT_OPTIMIZE;
jsvals.progressive = DEFAULT_PROGRESSIVE;
jsvals.baseline = DEFAULT_BASELINE;
jsvals.subsmp = DEFAULT_SUBSMP;
jsvals.restart = DEFAULT_RESTART;
jsvals.dct = DEFAULT_DCT;
jsvals.preview = DEFAULT_PREVIEW;
jsvals.save_exif = DEFAULT_EXIF;
jsvals.save_thumbnail = DEFAULT_THUMBNAIL;
#ifdef HAVE_EXIF
if (exif_data && (exif_data->data))
jsvals.save_thumbnail = TRUE;
#endif /* HAVE_EXIF */
switch (run_mode)
{
case GIMP_RUN_INTERACTIVE:
/* Possibly retrieve data */
gimp_get_data ("file_jpeg_save", &jsvals);
/* load up the previously used values */
parasite = gimp_image_parasite_find (orig_image_ID,
"jpeg-save-options");
if (parasite)
{
const JpegSaveVals *save_vals = gimp_parasite_data (parasite);
jsvals.quality = save_vals->quality;
jsvals.smoothing = save_vals->smoothing;
jsvals.optimize = save_vals->optimize;
jsvals.progressive = save_vals->progressive;
jsvals.baseline = save_vals->baseline;
jsvals.subsmp = save_vals->subsmp;
jsvals.restart = save_vals->restart;
jsvals.dct = save_vals->dct;
jsvals.preview = save_vals->preview;
jsvals.save_exif = save_vals->save_exif;
jsvals.save_thumbnail = save_vals->save_thumbnail;
gimp_parasite_free (parasite);
}
if (jsvals.preview)
{
/* we freeze undo saving so that we can avoid sucking up
* tile cache with our unneeded preview steps. */
gimp_image_undo_freeze (image_ID);
undo_touched = TRUE;
}
/* prepare for the preview */
image_ID_global = image_ID;
orig_image_ID_global = orig_image_ID;
drawable_ID_global = drawable_ID;
/* First acquire information with a dialog */
err = save_dialog ();
if (undo_touched)
{
/* thaw undo saving and flush the displays to have them
* reflect the current shortcuts */
gimp_image_undo_thaw (image_ID);
gimp_displays_flush ();
}
if (!err)
status = GIMP_PDB_CANCEL;
break;
case GIMP_RUN_NONINTERACTIVE:
/* Make sure all the arguments are there! */
/* pw - added two more progressive and comment */
/* sg - added subsampling, preview, baseline, restarts and DCT */
if (nparams != 14)
{
status = GIMP_PDB_CALLING_ERROR;
}
else
{
/* Once the PDB gets default parameters, remove this hack */
if (param[5].data.d_float > 0.05)
{
jsvals.quality = 100.0 * param[5].data.d_float;
jsvals.smoothing = param[6].data.d_float;
jsvals.optimize = param[7].data.d_int32;
#ifdef HAVE_PROGRESSIVE_JPEG
jsvals.progressive = param[8].data.d_int32;
#endif /* HAVE_PROGRESSIVE_JPEG */
jsvals.baseline = param[11].data.d_int32;
jsvals.subsmp = param[10].data.d_int32;
jsvals.restart = param[12].data.d_int32;
jsvals.dct = param[13].data.d_int32;
/* free up the default -- wasted some effort earlier */
g_free (image_comment);
image_comment = g_strdup (param[9].data.d_string);
}
jsvals.preview = FALSE;
if (jsvals.quality < 0.0 || jsvals.quality > 100.0)
status = GIMP_PDB_CALLING_ERROR;
else if (jsvals.smoothing < 0.0 || jsvals.smoothing > 1.0)
status = GIMP_PDB_CALLING_ERROR;
else if (jsvals.subsmp < 0 || jsvals.subsmp > 2)
status = GIMP_PDB_CALLING_ERROR;
else if (jsvals.dct < 0 || jsvals.dct > 2)
status = GIMP_PDB_CALLING_ERROR;
}
break;
case GIMP_RUN_WITH_LAST_VALS:
/* Possibly retrieve data */
gimp_get_data ("file_jpeg_save", &jsvals);
parasite = gimp_image_parasite_find (orig_image_ID,
"jpeg-save-options");
if (parasite)
{
const JpegSaveVals *save_vals = gimp_parasite_data (parasite);
jsvals.quality = save_vals->quality;
jsvals.smoothing = save_vals->smoothing;
jsvals.optimize = save_vals->optimize;
jsvals.progressive = save_vals->progressive;
jsvals.baseline = save_vals->baseline;
jsvals.subsmp = save_vals->subsmp;
jsvals.restart = save_vals->restart;
jsvals.dct = save_vals->dct;
jsvals.preview = FALSE;
gimp_parasite_free (parasite);
}
break;
default:
break;
}
if (status == GIMP_PDB_SUCCESS)
{
if (save_image (param[3].data.d_string,
image_ID,
drawable_ID,
orig_image_ID,
FALSE))
{
/* Store mvals data */
gimp_set_data ("file_jpeg_save", &jsvals, sizeof (JpegSaveVals));
}
else
{
status = GIMP_PDB_EXECUTION_ERROR;
}
}
if (export == GIMP_EXPORT_EXPORT)
{
/* If the image was exported, delete the new display. */
/* This also deletes the image. */
if (display_ID != -1)
gimp_display_delete (display_ID);
else
gimp_image_delete (image_ID);
}
/* pw - now we need to change the defaults to be whatever
* was used to save this image. Dump the old parasites
* and add new ones. */
gimp_image_parasite_detach (orig_image_ID, "gimp-comment");
if (image_comment && strlen (image_comment))
{
parasite = gimp_parasite_new ("gimp-comment",
GIMP_PARASITE_PERSISTENT,
strlen (image_comment) + 1,
image_comment);
gimp_image_parasite_attach (orig_image_ID, parasite);
gimp_parasite_free (parasite);
}
gimp_image_parasite_detach (orig_image_ID, "jpeg-save-options");
parasite = gimp_parasite_new ("jpeg-save-options",
0, sizeof (jsvals), &jsvals);
gimp_image_parasite_attach (orig_image_ID, parasite);
gimp_parasite_free (parasite);
}
else
{
status = GIMP_PDB_CALLING_ERROR;
}
values[0].data.d_status = status;
}
/*
* Here's the routine that will replace the standard error_exit method:
*/
void
my_error_exit (j_common_ptr cinfo)
{
/* cinfo->err really points to a my_error_mgr struct, so coerce pointer */
my_error_ptr myerr = (my_error_ptr) cinfo->err;
/* Always display the message. */
/* We could postpone this until after returning, if we chose. */
(*cinfo->err->output_message) (cinfo);
/* Return control to the setjmp point */
longjmp (myerr->setjmp_buffer, 1);
}
void
my_emit_message (j_common_ptr cinfo,
int msg_level)
{
if (msg_level == -1)
{
/* disable loading of EXIF data */
cinfo->client_data = GINT_TO_POINTER (TRUE);
(*cinfo->err->output_message) (cinfo);
}
}
void
my_output_message (j_common_ptr cinfo)
{
gchar buffer[JMSG_LENGTH_MAX + 1];
(*cinfo->err->format_message)(cinfo, buffer);
if (GPOINTER_TO_INT (cinfo->client_data))
{
gchar *msg = g_strconcat (buffer,
"\n\n",
_("EXIF data will be ignored."),
NULL);
g_message (msg);
g_free (msg);
}
else
{
g_message (buffer);
}
}

76
plug-ins/jpeg/jpeg.h Normal file
View File

@ -0,0 +1,76 @@
#include "config.h" /* configure cares about HAVE_PROGRESSIVE_JPEG */
#include <glib.h> /* We want glib.h first because of some
* pretty obscure Win32 compilation issues.
*/
#include <errno.h>
#include <setjmp.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <jpeglib.h>
#include <jerror.h>
#ifdef HAVE_EXIF
#include <libexif/exif-data.h>
#endif /* HAVE_EXIF */
#include <libgimp/gimp.h>
#include <libgimp/gimpui.h>
#include "libgimp/stdplugins-intl.h"
typedef struct my_error_mgr
{
struct jpeg_error_mgr pub; /* "public" fields */
#ifdef __ia64__
/* Ugh, the jmp_buf field needs to be 16-byte aligned on ia64 and some
* glibc/icc combinations don't guarantee this. So we pad. See bug #138357
* for details.
*/
long double dummy;
#endif
jmp_buf setjmp_buffer; /* for return to caller */
} *my_error_ptr;
gint32 volatile image_ID_global;
gint32 layer_ID_global;
GimpDrawable *drawable_global;
gboolean undo_touched;
gint32 display_ID;
gchar *image_comment;
#ifdef HAVE_EXIF
ExifData *exif_data;
#endif /* HAVE_EXIF */
gint32 load_image (const gchar *filename,
GimpRunMode runmode,
gboolean preview);
void destroy_preview (void);
#ifdef HAVE_EXIF
gint32 load_thumbnail_image(const gchar *filename,
gint *width,
gint *height);
#endif /* HAVE_EXIF */
void my_error_exit (j_common_ptr cinfo);
void my_emit_message (j_common_ptr cinfo,
int msg_level);
void my_output_message (j_common_ptr cinfo);