[gthumb: 77/129] jpeg_utils: added function to read image width and height from the jpeg header
- From: Paolo Bacchilega <paobac src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gthumb: 77/129] jpeg_utils: added function to read image width and height from the jpeg header
- Date: Wed, 27 Apr 2011 20:56:51 +0000 (UTC)
commit 001de4a94d2c026dc7a233087b1df036d89ca42b
Author: Paolo Bacchilega <paobac src gnome org>
Date: Mon Apr 25 15:21:56 2011 +0200
jpeg_utils: added function to read image width and height from the jpeg header
extensions/jpeg_utils/Makefile.am | 4 +-
extensions/jpeg_utils/jpeg-exif-orientation.c | 189 ------------
extensions/jpeg_utils/jpeg-exif-orientation.h | 30 --
extensions/jpeg_utils/jpeg-info.c | 407 +++++++++++++++++++++++++
extensions/jpeg_utils/jpeg-info.h | 41 +++
5 files changed, 450 insertions(+), 221 deletions(-)
---
diff --git a/extensions/jpeg_utils/Makefile.am b/extensions/jpeg_utils/Makefile.am
index 5efee06..349d720 100644
--- a/extensions/jpeg_utils/Makefile.am
+++ b/extensions/jpeg_utils/Makefile.am
@@ -7,8 +7,8 @@ libjpeg_utils_la_SOURCES = \
jmemorydest.h \
jmemorysrc.c \
jmemorysrc.h \
- jpeg-exif-orientation.c \
- jpeg-exif-orientation.h \
+ jpeg-info.c \
+ jpeg-info.h \
jpegtran.c \
jpegtran.h \
transupp.h \
diff --git a/extensions/jpeg_utils/jpeg-info.c b/extensions/jpeg_utils/jpeg-info.c
new file mode 100644
index 0000000..a225382
--- /dev/null
+++ b/extensions/jpeg_utils/jpeg-info.c
@@ -0,0 +1,407 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * GThumb
+ *
+ * Copyright (C) 2011 Free Software Foundation, Inc.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include <config.h>
+#include "jpeg-info.h"
+
+
+static guchar
+_jpeg_read_segment_marker (GDataInputStream *data_stream,
+ GCancellable *cancellable,
+ GError **error)
+{
+ guchar marker_id;
+
+ while ((marker_id = g_data_input_stream_read_byte (data_stream, cancellable, error)) == 0xff)
+ /* skip padding */;
+
+ return marker_id;
+}
+
+
+static gboolean
+_jpeg_skip_segment_data (GDataInputStream *data_stream,
+ guchar marker_id,
+ GCancellable *cancellable,
+ GError **error)
+{
+ if (marker_id == 0xd9) /* EOI => end of image */
+ return FALSE;
+ if (marker_id == 0xda) /* SOS => end of header */
+ return FALSE;
+
+ if ((marker_id != 0xd0)
+ && (marker_id != 0xd1)
+ && (marker_id != 0xd2)
+ && (marker_id != 0xd3)
+ && (marker_id != 0xd4)
+ && (marker_id != 0xd5)
+ && (marker_id != 0xd6)
+ && (marker_id != 0xd7)
+ && (marker_id != 0xd8)
+ && (marker_id != 0x01))
+ {
+ guint h, l;
+ guint segment_size;
+
+ /* skip to the next segment */
+
+ h = g_data_input_stream_read_byte (data_stream, cancellable, error);
+ l = g_data_input_stream_read_byte (data_stream, cancellable, error);
+ segment_size = (h << 8) + l;
+
+ if (g_input_stream_skip (G_INPUT_STREAM (data_stream),
+ segment_size - 2,
+ cancellable,
+ error) < 0)
+ {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+
+static gboolean
+_jpeg_skip_to_segment (GDataInputStream *data_stream,
+ guchar segment_id,
+ GCancellable *cancellable,
+ GError **error)
+{
+ guchar marker_id = 0x00;
+
+ while ((marker_id = _jpeg_read_segment_marker (data_stream, cancellable, error)) != 0x00) {
+ if (marker_id == segment_id)
+ return TRUE;
+ if (! _jpeg_skip_segment_data (data_stream, marker_id, cancellable, error))
+ return FALSE;
+ }
+
+ return FALSE;
+}
+
+
+static GthTransform
+_jpeg_exif_orientation_from_app1_segment (guchar *in_buffer,
+ gsize app1_segment_size)
+{
+ int pos;
+ guint length;
+ gboolean is_motorola;
+ guchar *exif_data;
+ guint offset, number_of_tags, tagnum;
+ int orientation;
+
+ /* Length includes itself, so must be at least 2 */
+ /* Following Exif data length must be at least 6 */
+
+ length = app1_segment_size;
+ if (length < 8)
+ return 0;
+
+ pos = 0;
+
+ /* Read Exif head, check for "Exif" */
+
+ if ((in_buffer[pos++] != 0x45)
+ || (in_buffer[pos++] != 0x78)
+ || (in_buffer[pos++] != 0x69)
+ || (in_buffer[pos++] != 0x66)
+ || (in_buffer[pos++] != 0)
+ || (in_buffer[pos++] != 0))
+ {
+ return 0;
+ }
+
+ /* Length of an IFD entry */
+
+ if (length < 12)
+ return 0;
+
+ exif_data = in_buffer + pos;
+
+ /* Discover byte order */
+
+ if ((exif_data[0] == 0x49) && (exif_data[1] == 0x49))
+ is_motorola = FALSE;
+ else if ((exif_data[0] == 0x4D) && (exif_data[1] == 0x4D))
+ is_motorola = TRUE;
+ else
+ return 0;
+
+ /* Check Tag Mark */
+
+ if (is_motorola) {
+ if (exif_data[2] != 0)
+ return 0;
+ if (exif_data[3] != 0x2A)
+ return 0;
+ }
+ else {
+ if (exif_data[3] != 0)
+ return 0;
+ if (exif_data[2] != 0x2A)
+ return 0;
+ }
+
+ /* Get first IFD offset (offset to IFD0) */
+
+ if (is_motorola) {
+ if (exif_data[4] != 0)
+ return 0;
+ if (exif_data[5] != 0)
+ return 0;
+ offset = exif_data[6];
+ offset <<= 8;
+ offset += exif_data[7];
+ }
+ else {
+ if (exif_data[7] != 0)
+ return 0;
+ if (exif_data[6] != 0)
+ return 0;
+ offset = exif_data[5];
+ offset <<= 8;
+ offset += exif_data[4];
+ }
+
+ if (offset > length - 2) /* check end of data segment */
+ return 0;
+
+ /* Get the number of directory entries contained in this IFD */
+
+ if (is_motorola) {
+ number_of_tags = exif_data[offset];
+ number_of_tags <<= 8;
+ number_of_tags += exif_data[offset+1];
+ }
+ else {
+ number_of_tags = exif_data[offset+1];
+ number_of_tags <<= 8;
+ number_of_tags += exif_data[offset];
+ }
+ if (number_of_tags == 0)
+ return 0;
+
+ offset += 2;
+
+ /* Search for Orientation Tag in IFD0 */
+
+ for (;;) {
+ if (offset > length - 12) /* check end of data segment */
+ return 0;
+
+ /* Get Tag number */
+
+ if (is_motorola) {
+ tagnum = exif_data[offset];
+ tagnum <<= 8;
+ tagnum += exif_data[offset+1];
+ }
+ else {
+ tagnum = exif_data[offset+1];
+ tagnum <<= 8;
+ tagnum += exif_data[offset];
+ }
+
+ if (tagnum == 0x0112) /* found Orientation Tag */
+ break;
+
+ if (--number_of_tags == 0)
+ return 0;
+
+ offset += 12;
+ }
+
+ /* Get the Orientation value */
+
+ if (is_motorola) {
+ if (exif_data[offset + 8] != 0)
+ return 0;
+ orientation = exif_data[offset + 9];
+ }
+ else {
+ if (exif_data[offset + 9] != 0)
+ return 0;
+ orientation = exif_data[offset + 8];
+ }
+
+ if (orientation > 8)
+ orientation = 0;
+
+ return (GthTransform) orientation;
+}
+
+
+gboolean
+_jpeg_get_image_info (GInputStream *stream,
+ int *width,
+ int *height,
+ GthTransform *orientation,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean size_read;
+ gboolean orientation_read;
+ GDataInputStream *data_stream;
+ guchar marker_id;
+
+ size_read = FALSE;
+
+ if (orientation != NULL) {
+ *orientation = GTH_TRANSFORM_NONE;
+ orientation_read = FALSE;
+ }
+ else
+ /* no need to search for the orientation flag if orientation is NULL */
+ orientation_read = TRUE;
+
+ data_stream = g_data_input_stream_new (stream);
+ g_filter_input_stream_set_close_base_stream (G_FILTER_INPUT_STREAM (data_stream), FALSE);
+
+ while ((marker_id = _jpeg_read_segment_marker (data_stream, cancellable, error)) != 0x00) {
+ if (marker_id == 0xc0) { /* SOF0 */
+ guint h, l;
+ guint size;
+
+ /* size */
+
+ h = g_data_input_stream_read_byte (data_stream, cancellable, error);
+ l = g_data_input_stream_read_byte (data_stream, cancellable, error);
+ size = (h << 8) + l;
+
+ /* data precision */
+
+ (void) g_data_input_stream_read_byte (data_stream, cancellable, error);
+
+ /* height */
+
+ h = g_data_input_stream_read_byte (data_stream, cancellable, error);
+ l = g_data_input_stream_read_byte (data_stream, cancellable, error);
+ if (height != NULL)
+ *height = (h << 8) + l;
+
+ /* width */
+
+ h = g_data_input_stream_read_byte (data_stream, cancellable, error);
+ l = g_data_input_stream_read_byte (data_stream, cancellable, error);
+ if (width != NULL)
+ *width = (h << 8) + l;
+
+ size_read = TRUE;
+
+ /* skip to the end of the segment */
+
+ if (! orientation_read)
+ g_input_stream_skip (G_INPUT_STREAM (data_stream), size - 7, cancellable, error);
+ }
+
+ if (! orientation_read && (marker_id == 0xe1)) { /* APP1 */
+ guint h, l;
+ gsize size;
+ guchar *app1_segment;
+
+ /* size */
+
+ h = g_data_input_stream_read_byte (data_stream, cancellable, error);
+ l = g_data_input_stream_read_byte (data_stream, cancellable, error);
+ size = (h << 8) + l;
+
+ app1_segment = g_new (guchar, size);
+ if (g_input_stream_read (stream, app1_segment, size, cancellable, error) > 0)
+ *orientation = _jpeg_exif_orientation_from_app1_segment (app1_segment, size);
+
+ orientation_read = TRUE;
+
+ g_free (app1_segment);
+ }
+
+ if (size_read && orientation_read)
+ break;
+
+ if (! _jpeg_skip_segment_data (data_stream, marker_id, cancellable, error))
+ break;
+ }
+
+ g_object_unref (data_stream);
+
+ return size_read;
+}
+
+
+GthTransform
+_jpeg_exif_orientation (guchar *in_buffer,
+ gsize in_buffer_size)
+{
+ GInputStream *stream;
+ GthTransform orientation;
+
+ stream = g_memory_input_stream_new_from_data (in_buffer, in_buffer_size, NULL);
+ orientation = _jpeg_exif_orientation_from_stream (stream, NULL, NULL);
+
+ g_object_unref (stream);
+
+ return orientation;
+}
+
+
+GthTransform
+_jpeg_exif_orientation_from_stream (GInputStream *stream,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GthTransform orientation;
+ GDataInputStream *data_stream;
+
+ orientation = GTH_TRANSFORM_NONE;
+ data_stream = g_data_input_stream_new (stream);
+ g_filter_input_stream_set_close_base_stream (G_FILTER_INPUT_STREAM (data_stream), FALSE);
+
+ if (_jpeg_read_segment_marker (data_stream, cancellable, error) == 0xd8) {
+ if (_jpeg_skip_to_segment (data_stream, 0xe1, cancellable, error)) {
+ guint h, l;
+ guint app1_segment_size;
+ guchar *app1_segment;
+
+ h = g_data_input_stream_read_byte (data_stream, cancellable, error);
+ l = g_data_input_stream_read_byte (data_stream, cancellable, error);
+ app1_segment_size = (h << 8) + l;
+
+ app1_segment = g_new (guchar, app1_segment_size);
+ if (g_input_stream_read (stream,
+ app1_segment,
+ app1_segment_size,
+ cancellable,
+ error) > 0)
+ {
+ orientation = _jpeg_exif_orientation_from_app1_segment (app1_segment, app1_segment_size);
+ }
+
+ g_free (app1_segment);
+ }
+ }
+
+ g_object_unref (data_stream);
+
+ return orientation;
+}
diff --git a/extensions/jpeg_utils/jpeg-info.h b/extensions/jpeg_utils/jpeg-info.h
new file mode 100644
index 0000000..4807fa3
--- /dev/null
+++ b/extensions/jpeg_utils/jpeg-info.h
@@ -0,0 +1,41 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * GThumb
+ *
+ * Copyright (C) 2001-2009 The Free Software Foundation, Inc.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef JPEG_INFO_H
+#define JPEG_INFO_H
+
+#include <gthumb.h>
+
+#define JPEG_SEGMENT_MAX_SIZE (64 * 1024)
+
+gboolean _jpeg_get_image_info (GInputStream *stream,
+ int *width,
+ int *height,
+ GthTransform *orientation,
+ GCancellable *cancellable,
+ GError **error);
+GthTransform _jpeg_exif_orientation (guchar *in_buffer,
+ gsize in_buffer_size);
+GthTransform _jpeg_exif_orientation_from_stream (GInputStream *stream,
+ GCancellable *cancellable,
+ GError **error);
+
+#endif /* JPEG_INFO_H */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]