Commit d1153503 authored by Michael J. Chudobiak's avatar Michael J. Chudobiak Committed by Michael J. Chudobiak

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
parent 5938f6ad
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.
......
......@@ -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
========================
......
......@@ -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
========================
......
......@@ -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.
......
......@@ -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) ***))],
......
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
......
......@@ -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;
}
......
......@@ -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;
......
......@@ -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;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment