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
This commit is contained in:
committed by
Michael J. Chudobiak
parent
5938f6add5
commit
d11535037e
@ -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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
/* 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
|
||||
#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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user