summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael J. Chudobiak <mjc@cvs.gnome.org>2007-05-18 14:19:47 +0000
committerMichael J. Chudobiak <mjc@src.gnome.org>2007-05-18 14:19:47 +0000
commitd11535037e623356289068d15fa24a64c33d96b5 (patch)
treeef3993d9cb47eda0bc22c53a2b2959c56389aeb2
parent5938f6add5cbd0a3db4bfc67cabcbaace8ec9f9d (diff)
downloadgtk+-d11535037e623356289068d15fa24a64c33d96b5.tar.gz
Bump libtiff requirement to 3.6.0, by requiring presence of
2007-05-18 Michael J. Chudobiak <mjc@cvs.gnome.org> * INSTALL.in: * README.in: * configure.in: Bump libtiff requirement to 3.6.0, by requiring presence of TIFFReadRGBAImageOriented. * gdk-pixbuf-scaled-anim.c: (get_scaled_pixbuf): Preserve pixbuf options when generating a new scaled pixbuf. * io-jpeg.c: (get_orientation), (gdk_pixbuf__jpeg_image_load), (gdk_pixbuf__jpeg_image_load_increment): Read the exif orientation tag and associate it with the "orientation" pixbuf option. Renders libexif unnecessary in some applications. * io-tiff.c: (tiff_image_parse): Read the tiff orientation tag, compensate for the partial rotations performed by libtiff, and generate an "orientation" option for the pixbuf. svn path=/trunk/; revision=17863
-rw-r--r--ChangeLog19
-rw-r--r--INSTALL2
-rw-r--r--INSTALL.in2
-rw-r--r--README.in2
-rw-r--r--configure.in2
-rw-r--r--gdk-pixbuf/ChangeLog14
-rw-r--r--gdk-pixbuf/gdk-pixbuf-scaled-anim.c18
-rw-r--r--gdk-pixbuf/io-jpeg.c198
-rw-r--r--gdk-pixbuf/io-tiff.c155
9 files changed, 304 insertions, 108 deletions
diff --git a/ChangeLog b/ChangeLog
index fa4a834040..5fee42cbf8 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,22 @@
+2007-05-18 Michael J. Chudobiak <mjc@cvs.gnome.org>
+
+ * INSTALL.in:
+ * README.in:
+ * configure.in: Bump libtiff requirement to 3.6.0, by requiring
+ presence of TIFFReadRGBAImageOriented.
+
+ * gdk-pixbuf-scaled-anim.c: (get_scaled_pixbuf):
+ Preserve pixbuf options when generating a new scaled pixbuf.
+
+ * io-jpeg.c: (get_orientation), (gdk_pixbuf__jpeg_image_load),
+ (gdk_pixbuf__jpeg_image_load_increment): Read the exif
+ orientation tag and associate it with the "orientation" pixbuf
+ option. Renders libexif unnecessary in some applications.
+
+ * io-tiff.c: (tiff_image_parse): Read the tiff orientation tag,
+ compensate for the partial rotations performed by libtiff,
+ and generate an "orientation" option for the pixbuf.
+
2007-05-18 Carlos Garnacho <carlos@imendio.com>
* gtk/gtkexpander.c: remove c++ style comment.
diff --git a/INSTALL b/INSTALL
index 2be180e9a4..cf80c2fae2 100644
--- a/INSTALL
+++ b/INSTALL
@@ -15,6 +15,8 @@ GTK+ requires the following packages:
http://www.libpng.org/
http://www.ijg.org/
+ libtiff must be version 3.6.0 or higher.
+
Simple install procedure
========================
diff --git a/INSTALL.in b/INSTALL.in
index 9d149238d3..742d782803 100644
--- a/INSTALL.in
+++ b/INSTALL.in
@@ -15,6 +15,8 @@ GTK+ requires the following packages:
http://www.libpng.org/
http://www.ijg.org/
+ libtiff must be version 3.6.0 or higher.
+
Simple install procedure
========================
diff --git a/README.in b/README.in
index cf6b373bcf..302696e4a9 100644
--- a/README.in
+++ b/README.in
@@ -37,6 +37,8 @@ Release notes for 2.12
Release notes for 2.10
======================
+* The tiff loader now requires libtiff 3.6.0 or later.
+
* The hexadecimal Unicode input feature has been reworked. It no longer
blocks the use of the sixteen Ctrl-Shift-<hex digit> key sequences. Now
it only uses Ctrl-Shift-u.
diff --git a/configure.in b/configure.in
index 99371439c5..f959736630 100644
--- a/configure.in
+++ b/configure.in
@@ -747,7 +747,7 @@ AC_ARG_WITH(libtiff,
dnl Test for libtiff
if test x$with_libtiff != xno && test -z "$LIBTIFF"; then
- AC_CHECK_LIB(tiff, TIFFReadScanline,
+ AC_CHECK_LIB(tiff, TIFFReadRGBAImageOriented,
[AC_CHECK_HEADER(tiffio.h,
TIFF='tiff'; LIBTIFF='-ltiff',
AC_MSG_WARN(*** TIFF loader will not be built (TIFF header files not found) ***))],
diff --git a/gdk-pixbuf/ChangeLog b/gdk-pixbuf/ChangeLog
index df7c7461c5..029793c14f 100644
--- a/gdk-pixbuf/ChangeLog
+++ b/gdk-pixbuf/ChangeLog
@@ -1,3 +1,17 @@
+2007-05-18 Michael J. Chudobiak <mjc@cvs.gnome.org>
+
+ * gdk-pixbuf-scaled-anim.c: (get_scaled_pixbuf):
+ Preserve pixbuf options when generating a new scaled pixbuf.
+
+ * io-jpeg.c: (get_orientation), (gdk_pixbuf__jpeg_image_load),
+ (gdk_pixbuf__jpeg_image_load_increment): Read the exif
+ orientation tag and associate it with the "orientation" pixbuf
+ option. Renders libexif unnecessary in some applications.
+
+ * io-tiff.c: (tiff_image_parse): Read the tiff orientation tag,
+ compensate for the partial rotations performed by libtiff,
+ and generate an "orientation" option for the pixbuf.
+
2007-04-29 Matthias Clasen <mclasen@redhat.com>
* io-jpeg.c: Remove a pointless check from the previous
diff --git a/gdk-pixbuf/gdk-pixbuf-scaled-anim.c b/gdk-pixbuf/gdk-pixbuf-scaled-anim.c
index 1a848f3c63..26f105bd38 100644
--- a/gdk-pixbuf/gdk-pixbuf-scaled-anim.c
+++ b/gdk-pixbuf/gdk-pixbuf-scaled-anim.c
@@ -121,14 +121,32 @@ static GdkPixbuf *
get_scaled_pixbuf (GdkPixbufScaledAnim *scaled,
GdkPixbuf *pixbuf)
{
+ GQuark quark;
+ gchar **options;
+
if (scaled->current)
g_object_unref (scaled->current);
+ /* Preserve the options associated with the original pixbuf
+ (if present), mostly so that client programs can use the
+ "orientation" option (if present) to rotate the image
+ appropriately. gdk_pixbuf_scale_simple (and most other
+ gdk transform operations) does not preserve the attached
+ options when returning a new pixbuf. */
+
+ quark = g_quark_from_static_string ("gdk_pixbuf_options");
+ options = g_object_get_qdata (G_OBJECT (pixbuf), quark);
+
+ /* Get a new scaled pixbuf */
scaled->current = gdk_pixbuf_scale_simple (pixbuf,
(int) (gdk_pixbuf_get_width (pixbuf) * scaled->xscale),
(int) (gdk_pixbuf_get_height (pixbuf) * scaled->yscale),
GDK_INTERP_BILINEAR);
+ /* Copy the original pixbuf options to the scaled pixbuf */
+ if (options && scaled->current)
+ g_object_set_qdata (G_OBJECT (scaled->current), quark, g_strdupv (options));
+
return scaled->current;
}
diff --git a/gdk-pixbuf/io-jpeg.c b/gdk-pixbuf/io-jpeg.c
index 1df2b813ca..6a1fde0b1e 100644
--- a/gdk-pixbuf/io-jpeg.c
+++ b/gdk-pixbuf/io-jpeg.c
@@ -277,11 +277,161 @@ colorspace_name (const J_COLOR_SPACE jpeg_color_space)
}
}
+
+const char leth[] = {0x49, 0x49, 0x2a, 0x00}; // Little endian TIFF header
+const char beth[] = {0x4d, 0x4d, 0x00, 0x2a}; // Big endian TIFF header
+const char types[] = {0x00, 0x01, 0x01, 0x02, 0x04, 0x08, 0x00,
+ 0x08, 0x00, 0x04, 0x08}; // size in bytes for EXIF types
+
+#define DE_ENDIAN16(val) endian == G_BIG_ENDIAN ? GUINT16_FROM_BE(val) : GUINT16_FROM_LE(val)
+#define DE_ENDIAN32(val) endian == G_BIG_ENDIAN ? GUINT32_FROM_BE(val) : GUINT32_FROM_LE(val)
+
+#define ENDIAN16_IT(val) endian == G_BIG_ENDIAN ? GUINT16_TO_BE(val) : GUINT16_TO_LE(val)
+#define ENDIAN32_IT(val) endian == G_BIG_ENDIAN ? GUINT32_TO_BE(val) : GUINT32_TO_LE(val)
+
+#define EXIF_JPEG_MARKER JPEG_APP0+1
+#define EXIF_IDENT_STRING "Exif\000\000"
+
+static gint
+get_orientation (j_decompress_ptr cinfo)
+{
+ /* This function looks through the meta data in the libjpeg decompress structure to
+ determine if an EXIF Orientation tag is present and if so return its value (1-8).
+ If no EXIF Orientation tag is found 0 (zero) is returned. */
+
+ guint i; /* index into working buffer */
+ guint orient_tag_id; /* endianed version of orientation tag ID */
+ guint ret; /* Return value */
+ guint offset; /* de-endianed offset in various situations */
+ guint tags; /* number of tags in current ifd */
+ guint type; /* de-endianed type of tag used as index into types[] */
+ guint count; /* de-endianed count of elements in a tag */
+ guint tiff = 0; /* offset to active tiff header */
+ guint endian = 0; /* detected endian of data */
+
+ jpeg_saved_marker_ptr exif_marker; /* Location of the Exif APP1 marker */
+ jpeg_saved_marker_ptr cmarker; /* Location to check for Exif APP1 marker */
+
+ /* check for Exif marker (also called the APP1 marker) */
+ exif_marker = NULL;
+ cmarker = cinfo->marker_list;
+ while (cmarker) {
+ if (cmarker->marker == EXIF_JPEG_MARKER) {
+ /* The Exif APP1 marker should contain a unique
+ identification string ("Exif\0\0"). Check for it. */
+ if (!memcmp (cmarker->data, EXIF_IDENT_STRING, 6)) {
+ exif_marker = cmarker;
+ }
+ }
+ cmarker = cmarker->next;
+ }
+
+ /* Did we find the Exif APP1 marker? */
+ if (exif_marker == NULL)
+ return 0;
+
+ /* Do we have enough data? */
+ if (exif_marker->data_length < 32)
+ return 0;
+
+ /* Check for TIFF header and catch endianess */
+ i = 0;
+
+ /* Just skip data until TIFF header - it should be within 16 bytes from marker start.
+ Normal structure relative to APP1 marker -
+ 0x0000: APP1 marker entry = 2 bytes
+ 0x0002: APP1 length entry = 2 bytes
+ 0x0004: Exif Identifier entry = 6 bytes
+ 0x000A: Start of TIFF header (Byte order entry) - 4 bytes
+ - This is what we look for, to determine endianess.
+ 0x000E: 0th IFD offset pointer - 4 bytes
+
+ exif_marker->data points to the first data after the APP1 marker
+ and length entries, which is the exif identification string.
+ The TIFF header should thus normally be found at i=6, below,
+ and the pointer to IFD0 will be at 6+4 = 10.
+ */
+
+ while (i < 16) {
+
+ /* Little endian TIFF header */
+ if (memcmp (&exif_marker->data[i], leth, 4) == 0){
+ endian = G_LITTLE_ENDIAN;
+ }
+
+ /* Big endian TIFF header */
+ else if (memcmp (&exif_marker->data[i], beth, 4) == 0){
+ endian = G_BIG_ENDIAN;
+ }
+
+ /* Keep looking through buffer */
+ else {
+ i++;
+ continue;
+ }
+ /* We have found either big or little endian TIFF header */
+ tiff = i;
+ break;
+ }
+
+ /* So did we find a TIFF header or did we just hit end of buffer? */
+ if (tiff == 0)
+ return 0;
+
+ /* Endian the orientation tag ID, to locate it more easily */
+ orient_tag_id = ENDIAN16_IT(0x112);
+
+ /* Read out the offset pointer to IFD0 */
+ offset = DE_ENDIAN32(*((unsigned long*) (&exif_marker->data[i] + 4)));
+ i = i + offset;
+
+ /* Check that we still are within the buffer and can read the tag count */
+ if ((i + 2) > exif_marker->data_length)
+ return 0;
+
+ /* Find out how many tags we have in IFD0. As per the TIFF spec, the first
+ two bytes of the IFD contain a count of the number of tags. */
+ tags = DE_ENDIAN16(*((unsigned short*) (&exif_marker->data[i])));
+ i = i + 2;
+
+ /* Check that we still have enough data for all tags to check. The tags
+ are listed in consecutive 12-byte blocks. The tag ID, type, size, and
+ a pointer to the actual value, are packed into these 12 byte entries. */
+ if ((i + tags * 12) > exif_marker->data_length)
+ return 0;
+
+ /* Check through IFD0 for tags of interest */
+ while (tags--){
+ type = DE_ENDIAN16(*((unsigned short*)(&exif_marker->data[i + 2])));
+ count = DE_ENDIAN32(*((unsigned long*) (&exif_marker->data[i + 4])));
+
+ /* Is this the orientation tag? */
+ if (memcmp (&exif_marker->data[i], (char *) &orient_tag_id, 2) == 0){
+
+ /* Check that type and count fields are OK. The orientation field
+ will consist of a single (count=1) 2-byte integer (type=3). */
+ if (type != 3 || count != 1) return 0;
+
+ /* Return the orientation value. Within the 12-byte block, the
+ pointer to the actual data is at offset 8. */
+ ret = DE_ENDIAN16(*((unsigned short*) (&exif_marker->data[i + 8])));
+ return ret <= 8 ? ret : 0;
+ }
+ /* move the pointer to the next 12-byte tag field. */
+ i = i + 12;
+ }
+
+ return 0; /* No EXIF Orientation tag found */
+}
+
+
/* Shared library entry point */
static GdkPixbuf *
gdk_pixbuf__jpeg_image_load (FILE *f, GError **error)
{
- gint i;
+ gint i;
+ int is_otag;
+ char otag_str[5];
GdkPixbuf * volatile pixbuf = NULL;
guchar *dptr;
guchar *lines[4]; /* Used to expand rows, via rec_outbuf_height,
@@ -332,7 +482,12 @@ gdk_pixbuf__jpeg_image_load (FILE *f, GError **error)
src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */
src->pub.next_input_byte = NULL; /* until buffer loaded */
+ jpeg_save_markers (&cinfo, EXIF_JPEG_MARKER, 0xffff);
jpeg_read_header (&cinfo, TRUE);
+
+ /* check for orientation tag */
+ is_otag = get_orientation (&cinfo);
+
jpeg_start_decompress (&cinfo);
cinfo.do_fancy_upsampling = FALSE;
cinfo.do_block_smoothing = FALSE;
@@ -357,6 +512,13 @@ gdk_pixbuf__jpeg_image_load (FILE *f, GError **error)
return NULL;
}
+ /* if orientation tag was found set an option to remember its value */
+ if (is_otag) {
+ snprintf (otag_str, sizeof (otag_str), "%d", is_otag);
+ gdk_pixbuf_set_option (pixbuf, "orientation", otag_str);
+ }
+
+
dptr = pixbuf->pixels;
/* decompress all the lines, a few at a time */
@@ -626,14 +788,16 @@ gdk_pixbuf__jpeg_image_load_increment (gpointer data,
GError **error)
{
JpegProgContext *context = (JpegProgContext *)data;
- struct jpeg_decompress_struct *cinfo;
- my_src_ptr src;
- guint num_left, num_copy;
- guint last_bytes_left;
- guint spinguard;
- gboolean first;
- const guchar *bufhd;
- gint width, height;
+ struct jpeg_decompress_struct *cinfo;
+ my_src_ptr src;
+ guint num_left, num_copy;
+ guint last_bytes_left;
+ guint spinguard;
+ gboolean first;
+ const guchar *bufhd;
+ gint width, height;
+ int is_otag;
+ char otag_str[5];
g_return_val_if_fail (context != NULL, FALSE);
g_return_val_if_fail (buf != NULL, FALSE);
@@ -707,7 +871,8 @@ gdk_pixbuf__jpeg_image_load_increment (gpointer data,
/* try to load jpeg header */
if (!context->got_header) {
int rc;
-
+
+ jpeg_save_markers (cinfo, EXIF_JPEG_MARKER, 0xffff);
rc = jpeg_read_header (cinfo, TRUE);
context->src_initialized = TRUE;
@@ -715,7 +880,10 @@ gdk_pixbuf__jpeg_image_load_increment (gpointer data,
continue;
context->got_header = TRUE;
-
+
+ /* check for orientation tag */
+ is_otag = get_orientation (cinfo);
+
width = cinfo->image_width;
height = cinfo->image_height;
if (context->size_func) {
@@ -751,7 +919,13 @@ gdk_pixbuf__jpeg_image_load_increment (gpointer data,
_("Couldn't allocate memory for loading JPEG file"));
return FALSE;
}
-
+
+ /* if orientation tag was found set an option to remember its value */
+ if (is_otag) {
+ snprintf (otag_str, sizeof (otag_str), "%d", is_otag);
+ gdk_pixbuf_set_option (context->pixbuf, "orientation", otag_str);
+ }
+
/* Use pixbuf buffer to store decompressed data */
context->dptr = context->pixbuf->pixels;
diff --git a/gdk-pixbuf/io-tiff.c b/gdk-pixbuf/io-tiff.c
index 79e8ef2c60..a9863e6cff 100644
--- a/gdk-pixbuf/io-tiff.c
+++ b/gdk-pixbuf/io-tiff.c
@@ -141,27 +141,14 @@ static void free_buffer (guchar *pixels, gpointer data)
g_free (pixels);
}
-#if TIFFLIB_VERSION >= 20031226
-static gboolean tifflibversion (int *major, int *minor, int *revision)
-{
- if (sscanf (TIFFGetVersion(),
- "LIBTIFF, Version %d.%d.%d",
- major, minor, revision) < 3)
- return FALSE;
-
- return TRUE;
-}
-#endif
-
static GdkPixbuf *
tiff_image_parse (TIFF *tiff, TiffContext *context, GError **error)
{
guchar *pixels = NULL;
gint width, height, rowstride, bytes;
GdkPixbuf *pixbuf;
-#if TIFFLIB_VERSION >= 20031226
- gint major, minor, revision;
-#endif
+ uint16 orientation = 0;
+ uint16 transform = 0;
/* We're called with the lock held. */
@@ -238,95 +225,73 @@ tiff_image_parse (TIFF *tiff, TiffContext *context, GError **error)
return NULL;
}
+ /* Set the "orientation" key associated with this image. libtiff
+ orientation handling is odd, so further processing is required
+ by higher-level functions based on this tag. If the embedded
+ orientation tag is 1-4, libtiff flips/mirrors the image as
+ required, and no client processing is required - so we report
+ no orientation. Orientations 5-8 require rotations which would
+ swap the width and height of the image. libtiff does not do this.
+ Instead it interprets orientations 5-8 the same as 1-4.
+ See http://bugzilla.remotesensing.org/show_bug.cgi?id=1548.
+ To correct for this, the client must apply the transform normally
+ used for orientation 5 to both orientations 5 and 7, and apply
+ the transform normally used for orientation 7 for both
+ orientations 6 and 8. Then everythings works out OK! */
+
+ TIFFGetField (tiff, TIFFTAG_ORIENTATION, &orientation);
+
+ switch (orientation) {
+ case 5:
+ case 7:
+ transform = 5;
+ break;
+ case 6:
+ case 8:
+ transform = 7;
+ break;
+ default:
+ transform = 0;
+ break;
+ }
+
+ if (transform > 0 ) {
+ gchar str[5];
+ snprintf (str, sizeof (str), "%d", transform);
+ gdk_pixbuf_set_option (pixbuf, "orientation", str);
+ }
+
if (context && context->prepare_func)
(* context->prepare_func) (pixbuf, NULL, context->user_data);
-#if TIFFLIB_VERSION >= 20031226
- if (tifflibversion(&major, &minor, &revision) && major == 3 &&
- (minor > 6 || (minor == 6 && revision > 0))) {
- if (!TIFFReadRGBAImageOriented (tiff, width, height, (uint32 *)pixels, ORIENTATION_TOPLEFT, 1) || global_error)
- {
- tiff_set_error (error,
- GDK_PIXBUF_ERROR_FAILED,
- _("Failed to load RGB data from TIFF file"));
- g_object_unref (pixbuf);
- return NULL;
- }
+ if (!TIFFReadRGBAImageOriented (tiff, width, height, (uint32 *)pixels, ORIENTATION_TOPLEFT, 1) || global_error) {
+ tiff_set_error (error,
+ GDK_PIXBUF_ERROR_FAILED,
+ _("Failed to load RGB data from TIFF file"));
+ g_object_unref (pixbuf);
+ return NULL;
+ }
#if G_BYTE_ORDER == G_BIG_ENDIAN
- /* Turns out that the packing used by TIFFRGBAImage depends on
- * the host byte order...
- */
- while (pixels < pixbuf->pixels + bytes) {
- uint32 pixel = *(uint32 *)pixels;
- int r = TIFFGetR(pixel);
- int g = TIFFGetG(pixel);
- int b = TIFFGetB(pixel);
- int a = TIFFGetA(pixel);
- *pixels++ = r;
- *pixels++ = g;
- *pixels++ = b;
- *pixels++ = a;
- }
-#endif
- }
- else
+ /* Turns out that the packing used by TIFFRGBAImage depends on
+ * the host byte order...
+ */
+ while (pixels < pixbuf->pixels + bytes) {
+ uint32 pixel = *(uint32 *)pixels;
+ int r = TIFFGetR(pixel);
+ int g = TIFFGetG(pixel);
+ int b = TIFFGetB(pixel);
+ int a = TIFFGetA(pixel);
+ *pixels++ = r;
+ *pixels++ = g;
+ *pixels++ = b;
+ *pixels++ = a;
+ }
#endif
- {
- uint32 *rast, *tmp_rast;
- gint x, y;
- guchar *tmppix;
-
- /* Yes, it needs to be _TIFFMalloc... */
- rast = (uint32 *) _TIFFmalloc (width * height * sizeof (uint32));
- if (!rast) {
- g_set_error (error,
- GDK_PIXBUF_ERROR,
- GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
- _("Insufficient memory to open TIFF file"));
- g_object_unref (pixbuf);
-
- return NULL;
- }
- if (!TIFFReadRGBAImage (tiff, width, height, rast, 1) || global_error) {
- tiff_set_error (error,
- GDK_PIXBUF_ERROR_FAILED,
- _("Failed to load RGB data from TIFF file"));
- g_object_unref (pixbuf);
- _TIFFfree (rast);
-
- return NULL;
- }
-
- pixels = gdk_pixbuf_get_pixels (pixbuf);
-
- g_assert (pixels);
-
- tmppix = pixels;
-
- for (y = 0; y < height; y++) {
- /* Unexplainable...are tiffs backwards? */
- /* Also looking at the GIMP plugin, this
- * whole reading thing can be a bit more
- * robust.
- */
- tmp_rast = rast + ((height - y - 1) * width);
- for (x = 0; x < width; x++) {
- tmppix[0] = TIFFGetR (*tmp_rast);
- tmppix[1] = TIFFGetG (*tmp_rast);
- tmppix[2] = TIFFGetB (*tmp_rast);
- tmppix[3] = TIFFGetA (*tmp_rast);
- tmp_rast++;
- tmppix += 4;
- }
- }
-
- _TIFFfree (rast);
- }
if (context && context->update_func)
(* context->update_func) (pixbuf, 0, 0, width, height, context->user_data);
-
+
return pixbuf;
}