Commit 001de4a9 authored by Paolo Bacchilega's avatar Paolo Bacchilega
Browse files

jpeg_utils: added function to read image width and height from the jpeg header

parent be499e8a
......@@ -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 \
......
......@@ -21,41 +21,105 @@
#include <config.h>
#include "jpeg-exif-orientation.h"
#include "jpeg-info.h"
GthTransform
_jpeg_exif_orientation (guchar *in_buffer,
gsize in_buffer_size)
static guchar
_jpeg_read_segment_marker (GDataInputStream *data_stream,
GCancellable *cancellable,
GError **error)
{
int pos = 0;
guint length;
gboolean is_motorola;
guchar *exif_data;
guint offset, number_of_tags, tagnum;
int orientation;
guchar marker_id;
while ((marker_id = g_data_input_stream_read_byte (data_stream, cancellable, error)) == 0xff)
/* skip padding */;
return marker_id;
}
/* Read File head, check for JPEG SOI + Exif APP1 */
if ((in_buffer[pos++] != 0xFF)
|| (in_buffer[pos++] != 0xD8)
|| (in_buffer[pos++] != 0xFF)
|| (in_buffer[pos++] != 0xE1))
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))
{
return 0;
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;
}
}
if (in_buffer_size < 2)
return 0;
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 = ((guint) in_buffer[pos] << 8) + ((guint) in_buffer[pos + 1]);
length = app1_segment_size;
if (length < 8)
return 0;
pos += 2;
pos = 0;
/* Read Exif head, check for "Exif" */
......@@ -187,3 +251,157 @@ _jpeg_exif_orientation (guchar *in_buffer,
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;
}
......@@ -19,12 +19,23 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef JPEG_EXIF_ORIENTATION_H
#define JPEG_EXIF_ORIENTATION_H
#ifndef JPEG_INFO_H
#define JPEG_INFO_H
#include <gthumb.h>
GthTransform _jpeg_exif_orientation (guchar *in_buffer,
gsize in_buffer_size);
#define JPEG_SEGMENT_MAX_SIZE (64 * 1024)
#endif /* JPEG_EXIF_ORIENTATION_H */
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 */
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