Bill Skaggs <weskaggs@primate.ucdavis.edu>

* configure.in: add check for libexif version >= 0.6.0,
	necessary because of nasty incompatible api change.

	* plug-ins/jpeg/exif-handling.txt: removed from here...

	* devel-docs/exif-handling.txt: ...and added here

	* plug-ins/jpeg/jpeg-exif.c
	* plug-ins/jpeg/Makefile.am
	* plug-ins/jpeg/jpeg-load.c
	* plug-ins/jpeg/jpeg.c
	* plug-ins/jpeg/jpeg.h: extract info from exif on loading,
	and add info to exif on saving, addresses bug #56433,
	bug #61499, and bug #121810.
This commit is contained in:
William Skaggs 2005-01-04 17:48:13 +00:00
parent 02dd92bf37
commit bd895a1131
8 changed files with 492 additions and 49 deletions

View File

@ -1,3 +1,20 @@
2005-01-04 Bill Skaggs <weskaggs@primate.ucdavis.edu>
* configure.in: add check for libexif version >= 0.6.0,
necessary because of nasty incompatible api change.
* plug-ins/jpeg/exif-handling.txt: removed from here...
* devel-docs/exif-handling.txt: ...and added here
* plug-ins/jpeg/jpeg-exif.c
* plug-ins/jpeg/Makefile.am
* plug-ins/jpeg/jpeg-load.c
* plug-ins/jpeg/jpeg.c
* plug-ins/jpeg/jpeg.h: extract info from exif on loading,
and add info to exif on saving, addresses bug #56433,
bug #61499, and bug #121810.
2005-01-04 Sven Neumann <sven@gimp.org>
* app/display/gimpdisplayshell-close.c

View File

@ -1043,11 +1043,22 @@ if test x$with_libexif != xno && test -z "$LIBEXIF" && test -n "$LIBJPEG"; then
AC_MSG_WARN([libexif not found!
EXIF support will not be built into the JPEG plug-in.
libexif is available from http://www.sourceforge.net/projects/libexif]))
AC_MSG_CHECKING([if libexif is version 0.6.0 or newer])
if $PKG_CONFIG --atleast-version=0.6.0 libexif; then
have_exif_0_6=yes
else
have_exif_0_6=no
fi
AC_MSG_RESULT($have_exif_0_6)
if test x$have_exif_0_6 == xyes; then
AC_DEFINE(HAVE_EXIF_0_6, 1, "Define to 1 if libexif is at least version 0.6.0")
fi
fi
AC_SUBST(EXIF_CFLAGS)
AC_SUBST(EXIF_LIBS)
#################
# Check for libaa
#################

View File

@ -36,7 +36,8 @@ jpeg_SOURCES = \
jpeg-save.c \
jpeg.h \
jpeg-save.h \
jpeg-load.h
jpeg-load.h \
jpeg-exif.c
jpeg_LDADD = \
$(libgimpui) \

425
plug-ins/jpeg/jpeg-exif.c Normal file
View File

@ -0,0 +1,425 @@
/* 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.
*/
/*
* EXIF-handling code for the jpeg plugin. May eventually be better
* to move this stuff into libgimpbase and make it available for
* other plugins.
*/
#include "jpeg.h"
#ifdef HAVE_EXIF
#include <libexif/exif-content.h>
#include <libexif/exif-utils.h>
#define EXIF_HEADER_SIZE 8
#define jpeg_exif_content_get_value(c,t,v,m) \
(exif_content_get_entry (c,t) ? \
jpeg_exif_entry_get_value (exif_content_get_entry (c,t),v,m) : NULL)
static void jpeg_remove_exif_entry (ExifData *exif_data,
ExifIfd ifd,
ExifTag tag);
static const gchar *jpeg_exif_entry_get_value (ExifEntry *e,
gchar *val,
guint maxlen);
static gboolean jpeg_query (const gchar *msg);
void
jpeg_apply_exif_data_to_image (const gchar *filename,
const gint32 image_ID)
{
GimpParasite *parasite = NULL;
ExifData *exif_data = NULL;
guchar *exif_buf = NULL;
guint exif_buf_len = 0;
ExifEntry *entry;
gchar value[1000];
gint byte_order;
exif_data = exif_data_new_from_file (filename);
if (!exif_data)
return;
exif_data_save_data (exif_data, &exif_buf, &exif_buf_len);
if (exif_buf_len > EXIF_HEADER_SIZE)
{
parasite = gimp_parasite_new ("exif-data",
GIMP_PARASITE_PERSISTENT,
exif_buf_len, exif_buf);
gimp_image_parasite_attach (image_ID, parasite);
gimp_parasite_free (parasite);
}
free (exif_buf);
byte_order = exif_data_get_byte_order (exif_data);
/* get copyright and put it in a parasite */
if (jpeg_exif_content_get_value (exif_data->ifd[EXIF_IFD_0],
EXIF_TAG_COPYRIGHT,
value, 1000))
{
parasite = gimp_parasite_new ("gimp-copyright",
/* GIMP_PARASITE_PERSISTENT */ 0,
strlen (value), value);
gimp_image_parasite_attach (image_ID, parasite);
gimp_parasite_free (parasite);
}
/* get image artist and put it in a parasite */
if (jpeg_exif_content_get_value (exif_data->ifd[EXIF_IFD_0],
EXIF_TAG_ARTIST,
value, 1000))
{
parasite = gimp_parasite_new ("gimp-artist",
/* GIMP_PARASITE_PERSISTENT */ 0,
strlen (value), value);
gimp_image_parasite_attach (image_ID, parasite);
gimp_parasite_free (parasite);
}
/* get user comment and put it in a parasite */
/* note that the format is undefined according to the spec */
if (jpeg_exif_content_get_value (exif_data->ifd[EXIF_IFD_EXIF],
EXIF_TAG_USER_COMMENT,
value, 1000))
{
parasite = gimp_parasite_new ("jpeg-user-comment",
/* GIMP_PARASITE_PERSISTENT */ 0,
strlen (value), value);
gimp_image_parasite_attach (image_ID, parasite);
gimp_parasite_free (parasite);
}
/* get image description and put it in a parasite */
/* this must be ascii, so we put it into gimp-comment */
if (jpeg_exif_content_get_value (exif_data->ifd[EXIF_IFD_0],
EXIF_TAG_IMAGE_DESCRIPTION,
value, 1000))
{
parasite = gimp_parasite_new ("gimp-comment",
GIMP_PARASITE_PERSISTENT,
strlen (value), value);
gimp_image_parasite_attach (image_ID, parasite);
gimp_parasite_free (parasite);
}
/* get orientation and rotate image accordingly if necessary */
if ((entry = exif_content_get_entry (exif_data->ifd[EXIF_IFD_0],
EXIF_TAG_ORIENTATION)))
{
gint orient = exif_get_short (entry->data, byte_order);
if (load_interactive && orient != 1)
if (jpeg_query (_("According to the EXIF data, this image is rotated. "
"Would you like GIMP to rotate it into the standard "
"orientation?")))
{
switch (orient)
{
case 0: /* invalid, so ignore */
case 1: /* standard orientation, do nothing */
break;
case 2: /* flipped right-left */
gimp_image_flip (image_ID, GIMP_ORIENTATION_HORIZONTAL);
break;
case 3: /* rotated 180 */
break;
case 4: /* flipped top-bottom */
gimp_image_flip (image_ID, GIMP_ORIENTATION_VERTICAL);
break;
case 5: /* flipped diagonally around '\' */
gimp_image_rotate (image_ID, GIMP_ROTATE_90);
gimp_image_flip (image_ID, GIMP_ORIENTATION_HORIZONTAL);
break;
case 6: /* 90 CW */
gimp_image_rotate (image_ID, GIMP_ROTATE_90);
break;
case 7: /* flipped diagonally around '/' */
gimp_image_rotate (image_ID, GIMP_ROTATE_90);
gimp_image_flip (image_ID, GIMP_ORIENTATION_VERTICAL);
break;
case 8: /* 90 CCW */
gimp_image_rotate (image_ID, GIMP_ROTATE_270);
break;
default: /* invalid, ignore */
break;
}
}
}
/* get Colorspace and put it in a parasite */
/* this can only be 'sRGB' or 'uncalibrated' */
if ((entry = exif_content_get_entry (exif_data->ifd[EXIF_IFD_EXIF],
EXIF_TAG_COLOR_SPACE)))
{
gint s = exif_get_short (entry->data, byte_order);
if (s == 1)
sprintf (value, "sRGB");
else
sprintf (value, "uncalibrated");
parasite = gimp_parasite_new ("gimp-colorspace",
/* GIMP_PARASITE_PERSISTENT */ 0,
strlen (value), value);
gimp_image_parasite_attach (image_ID, parasite);
gimp_parasite_free (parasite);
}
exif_data_unref (exif_data);
}
void
jpeg_setup_exif_for_save (ExifData *exif_data,
const gint32 image_ID)
{
ExifRational r;
gdouble xres, yres;
ExifEntry *entry;
GimpParasite *parasite;
gint byte_order = exif_data_get_byte_order (exif_data);
/* set orientation to top - left */
if ((entry = exif_content_get_entry (exif_data->ifd[EXIF_IFD_0],
EXIF_TAG_ORIENTATION)))
{
exif_set_short (entry->data, byte_order, (ExifShort) 1);
}
/* set x and y resolution */
gimp_image_get_resolution (image_ID, &xres, &yres);
r.numerator = xres;
r.denominator = 1;
if ((entry = exif_content_get_entry (exif_data->ifd[EXIF_IFD_0],
EXIF_TAG_X_RESOLUTION)))
{
exif_set_rational (entry->data, byte_order, r);
}
r.numerator = yres;
if ((entry = exif_content_get_entry (exif_data->ifd[EXIF_IFD_0],
EXIF_TAG_Y_RESOLUTION)))
{
exif_set_rational (entry->data, byte_order, r);
}
/* set resolution unit, always inches */
if ((entry = exif_content_get_entry (exif_data->ifd[EXIF_IFD_0],
EXIF_TAG_RESOLUTION_UNIT)))
{
exif_set_short (entry->data, byte_order, (ExifShort) 2);
}
/* set software to "The GIMP" */
if ((entry = exif_content_get_entry (exif_data->ifd[EXIF_IFD_0],
EXIF_TAG_SOFTWARE)))
{
entry->data = g_strdup ("The GIMP");
entry->size = strlen ("The GIMP") + 1;
entry->components = entry->size;
}
/* set the width and height */
if ((entry = exif_content_get_entry (exif_data->ifd[EXIF_IFD_0],
EXIF_TAG_PIXEL_X_DIMENSION)))
{
exif_set_long (entry->data, byte_order,
(ExifLong) gimp_image_width (image_ID));
}
if ((entry = exif_content_get_entry (exif_data->ifd[EXIF_IFD_0],
EXIF_TAG_PIXEL_Y_DIMENSION)))
{
exif_set_long (entry->data, byte_order,
(ExifLong) gimp_image_height (image_ID));
}
/*
* set the date & time image was saved
* note, date & time of original photo is stored elsewwhere, we
* aren't losing it.
*/
if ((entry = exif_content_get_entry (exif_data->ifd[EXIF_IFD_0],
EXIF_TAG_DATE_TIME)))
{
/* small memory leak here */
entry->data = NULL;
exif_entry_initialize (entry, EXIF_TAG_DATE_TIME);
}
/* save "gimp-comment" as the image description */
parasite = gimp_image_parasite_find (image_ID, "gimp-comment");
if (parasite)
{
entry = exif_content_get_entry (exif_data->ifd[EXIF_IFD_EXIF],
EXIF_TAG_IMAGE_DESCRIPTION);
if (!entry)
{
entry = exif_entry_new ();
exif_content_add_entry (exif_data->ifd[EXIF_IFD_EXIF],
entry);
exif_entry_initialize (entry, EXIF_TAG_IMAGE_DESCRIPTION);
entry->data = g_strndup (parasite->data, parasite->size);
entry->size = parasite->size;
entry->components = parasite->size;
}
gimp_parasite_free (parasite);
}
parasite = gimp_image_parasite_find (image_ID, "gimp-copyright");
if (parasite)
{
entry = exif_content_get_entry (exif_data->ifd[EXIF_IFD_EXIF],
EXIF_TAG_COPYRIGHT);
if (!entry)
{
entry = exif_entry_new ();
exif_content_add_entry (exif_data->ifd[EXIF_IFD_EXIF],
entry);
exif_entry_initialize (entry, EXIF_TAG_COPYRIGHT);
entry->data = g_strndup (parasite->data, parasite->size);
entry->size = parasite->size;
entry->components = parasite->size;
}
gimp_parasite_free (parasite);
}
parasite = gimp_image_parasite_find (image_ID, "gimp-artist");
if (parasite)
{
entry = exif_content_get_entry (exif_data->ifd[EXIF_IFD_EXIF],
EXIF_TAG_ARTIST);
if (!entry)
{
entry = exif_entry_new ();
exif_content_add_entry (exif_data->ifd[EXIF_IFD_EXIF],
entry);
exif_entry_initialize (entry, EXIF_TAG_ARTIST);
entry->data = g_strndup (parasite->data, parasite->size);
entry->size = parasite->size;
entry->components = parasite->size;
}
gimp_parasite_free (parasite);
}
parasite = gimp_image_parasite_find (image_ID, "jpeg-user-comment");
if (parasite)
{
entry = exif_content_get_entry (exif_data->ifd[EXIF_IFD_EXIF],
EXIF_TAG_USER_COMMENT);
if (!entry)
{
entry = exif_entry_new ();
exif_content_add_entry (exif_data->ifd[EXIF_IFD_EXIF],
entry);
exif_entry_initialize (entry, EXIF_TAG_USER_COMMENT);
entry->data = g_strndup (parasite->data, parasite->size);
entry->size = parasite->size;
entry->components = parasite->size;
}
gimp_parasite_free (parasite);
}
/* should set components configuration, don't know how */
/*
*remove entries that don't apply to jpeg
*(may have come from tiff or raw)
*/
jpeg_remove_exif_entry(exif_data, EXIF_IFD_0, EXIF_TAG_COMPRESSION);
jpeg_remove_exif_entry(exif_data, EXIF_IFD_0, EXIF_TAG_IMAGE_WIDTH);
jpeg_remove_exif_entry(exif_data, EXIF_IFD_0, EXIF_TAG_IMAGE_LENGTH);
jpeg_remove_exif_entry(exif_data, EXIF_IFD_0, EXIF_TAG_BITS_PER_SAMPLE);
jpeg_remove_exif_entry(exif_data, EXIF_IFD_0, EXIF_TAG_SAMPLES_PER_PIXEL);
jpeg_remove_exif_entry(exif_data, EXIF_IFD_0, EXIF_TAG_PHOTOMETRIC_INTERPRETATION);
jpeg_remove_exif_entry(exif_data, EXIF_IFD_0, EXIF_TAG_STRIP_OFFSETS);
jpeg_remove_exif_entry(exif_data, EXIF_IFD_0, EXIF_TAG_PLANAR_CONFIGURATION);
jpeg_remove_exif_entry(exif_data, EXIF_IFD_0, EXIF_TAG_YCBCR_SUB_SAMPLING);
/* should set thumbnail attributes */
}
static void
jpeg_remove_exif_entry (ExifData *exif_data,
ExifIfd ifd,
ExifTag tag)
{
ExifEntry *entry = exif_content_get_entry (exif_data->ifd[ifd],
tag);
if (entry)
exif_content_remove_entry (exif_data->ifd[ifd], entry);
}
static const gchar *
jpeg_exif_entry_get_value (ExifEntry *e,
gchar *val,
guint maxlen)
{
#ifdef HAVE_EXIF_0_6
return exif_entry_get_value (e, val, maxlen);
#else
strncpy (val, exif_entry_get_value (e), maxlen);
return val;
#endif /* HAVE_EXIF_0_6 */
}
static gboolean
jpeg_query (const gchar *msg)
{
GtkWidget *label;
gboolean ret;
GtkWidget *dialog = gimp_dialog_new (_("Loading JPEG . . ."), "jpeg-query",
NULL, 0, gimp_standard_help_func,
"file-jpeg-load",
GTK_STOCK_NO, GTK_RESPONSE_CANCEL,
GTK_STOCK_YES, GTK_RESPONSE_OK,
NULL);
label = gtk_label_new (msg);
gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), label, TRUE, TRUE, 12);
gtk_widget_show (label);
ret = gimp_dialog_run (GIMP_DIALOG (dialog));
gtk_widget_destroy (dialog);
return ((ret == GTK_RESPONSE_OK));
}
#endif /* HAVE_EXIF */

View File

@ -95,11 +95,6 @@ load_image (const gchar *filename,
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;
@ -456,31 +451,10 @@ load_image (const gchar *filename,
}
#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;
jpeg_apply_exif_data_to_image (filename, image_ID);
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
}

View File

@ -174,6 +174,18 @@ run (const gchar *name,
if (strcmp (name, "file_jpeg_load") == 0)
{
switch (run_mode)
{
case GIMP_RUN_INTERACTIVE:
case GIMP_RUN_WITH_LAST_VALS:
gimp_ui_init ("jpeg", FALSE);
load_interactive = TRUE;
break;
default:
load_interactive = FALSE;
break;
}
image_ID = load_image (param[1].data.d_string, run_mode, FALSE);
if (image_ID != -1)
@ -186,6 +198,7 @@ run (const gchar *name,
{
status = GIMP_PDB_EXECUTION_ERROR;
}
}
#ifdef HAVE_EXIF
@ -265,11 +278,6 @@ run (const gchar *name,
break;
}
#ifdef HAVE_EXIF
exif_data_unref (exif_data);
exif_data = NULL;
#endif /* HAVE_EXIF */
g_free (image_comment);
image_comment = NULL;
@ -288,6 +296,9 @@ run (const gchar *name,
{
exif_data = exif_data_new_from_data (gimp_parasite_data (parasite),
gimp_parasite_data_size (parasite));
jpeg_setup_exif_for_save (exif_data, orig_image_ID);
gimp_parasite_free (parasite);
}

View File

@ -46,31 +46,35 @@ gint32 volatile image_ID_global;
gint32 layer_ID_global;
GimpDrawable *drawable_global;
gboolean undo_touched;
gboolean load_interactive;
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);
gint32 load_image (const gchar *filename,
GimpRunMode runmode,
gboolean preview);
void destroy_preview (void);
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);
#ifdef HAVE_EXIF
gint32 load_thumbnail_image(const gchar *filename,
gint *width,
gint *height);
ExifData *exif_data;
gint32 load_thumbnail_image (const gchar *filename,
gint *width,
gint *height);
void jpeg_apply_exif_data_to_image (const gchar *filename,
const gint32 image_ID);
void jpeg_setup_exif_for_save (ExifData *exif_data,
const gint32 image_ID);
#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);