[gthumb] jpeg info: make the api more generic, allow to load the icc profile
- From: Paolo Bacchilega <paobac src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gthumb] jpeg info: make the api more generic, allow to load the icc profile
- Date: Fri, 6 Feb 2015 09:58:22 +0000 (UTC)
commit 3e04a97de7f49173fe4c9ed8eb10238421ec9154
Author: Paolo Bacchilega <paobac src gnome org>
Date: Sun Jan 25 22:35:01 2015 +0100
jpeg info: make the api more generic, allow to load the icc profile
extensions/cairo_io/cairo-image-surface-jpeg.c | 10 +-
.../image_viewer/gth-metadata-provider-image.c | 40 ++-
extensions/jpeg_utils/jpeg-info.c | 308 +++++++++++++-------
extensions/jpeg_utils/jpeg-info.h | 40 ++-
4 files changed, 266 insertions(+), 132 deletions(-)
---
diff --git a/extensions/cairo_io/cairo-image-surface-jpeg.c b/extensions/cairo_io/cairo-image-surface-jpeg.c
index a9ecb61..0dd4201 100644
--- a/extensions/cairo_io/cairo-image-surface-jpeg.c
+++ b/extensions/cairo_io/cairo-image-surface-jpeg.c
@@ -164,6 +164,7 @@ _cairo_image_surface_create_from_jpeg (GInputStream *istream,
int pixel_step;
void *in_buffer;
gsize in_buffer_size;
+ JpegInfoData jpeg_info;
struct error_handler_data jsrcerr;
struct jpeg_decompress_struct srcinfo;
cairo_surface_t *surface;
@@ -191,6 +192,14 @@ _cairo_image_surface_create_from_jpeg (GInputStream *istream,
return image;
}
+ _jpeg_info_data_init (&jpeg_info);
+ _jpeg_info_get_from_buffer (in_buffer, in_buffer_size, _JPEG_INFO_EXIF_ORIENTATION, &jpeg_info);
+ if (jpeg_info.valid & _JPEG_INFO_EXIF_ORIENTATION)
+ orientation = jpeg_info.orientation;
+ else
+ orientation = GTH_TRANSFORM_NONE;
+ _jpeg_info_data_dispose (&jpeg_info);
+
srcinfo.err = jpeg_std_error (&(jsrcerr.pub));
jsrcerr.pub.error_exit = fatal_error_handler;
jsrcerr.pub.output_message = output_message_handler;
@@ -233,7 +242,6 @@ _cairo_image_surface_create_from_jpeg (GInputStream *istream,
jpeg_start_decompress (&srcinfo);
- orientation = _jpeg_exif_orientation (in_buffer, in_buffer_size);
_cairo_image_surface_transform_get_steps (CAIRO_FORMAT_ARGB32,
MIN (srcinfo.output_width, CAIRO_MAX_IMAGE_SIZE),
MIN (srcinfo.output_height, CAIRO_MAX_IMAGE_SIZE),
diff --git a/extensions/image_viewer/gth-metadata-provider-image.c
b/extensions/image_viewer/gth-metadata-provider-image.c
index 1c077bc..13b45ea 100644
--- a/extensions/image_viewer/gth-metadata-provider-image.c
+++ b/extensions/image_viewer/gth-metadata-provider-image.c
@@ -120,7 +120,9 @@ gth_metadata_provider_image_read (GthMetadataProvider *self,
{
/* JPEG */
- GthTransform orientation;
+ JpegInfoData jpeg_info;
+
+ _jpeg_info_data_init (&jpeg_info);
if (g_seekable_can_seek (G_SEEKABLE (stream))) {
g_seekable_seek (G_SEEKABLE (stream), 0, G_SEEK_SET, cancellable,
NULL);
@@ -130,27 +132,33 @@ gth_metadata_provider_image_read (GthMetadataProvider *self,
stream = g_file_read (file_data->file, cancellable, NULL);
}
- if (_jpeg_get_image_info (G_INPUT_STREAM (stream),
- &width,
- &height,
- &orientation,
- cancellable,
- NULL))
- {
+ _jpeg_info_get_from_stream (G_INPUT_STREAM (stream),
+ _JPEG_INFO_IMAGE_SIZE |
_JPEG_INFO_EXIF_ORIENTATION,
+ &jpeg_info,
+ cancellable,
+ NULL);
+
+ if (jpeg_info.valid & _JPEG_INFO_IMAGE_SIZE) {
+ width = jpeg_info.width;
+ height = jpeg_info.height;
description = _("JPEG");
mime_type = "image/jpeg";
format_recognized = TRUE;
- if ((orientation == GTH_TRANSFORM_ROTATE_90)
- || (orientation == GTH_TRANSFORM_ROTATE_270)
- || (orientation == GTH_TRANSFORM_TRANSPOSE)
- || (orientation == GTH_TRANSFORM_TRANSVERSE))
- {
- int tmp = width;
- width = height;
- height = tmp;
+ if (jpeg_info.valid & _JPEG_INFO_EXIF_ORIENTATION) {
+ if ((jpeg_info.orientation == GTH_TRANSFORM_ROTATE_90)
+ || (jpeg_info.orientation == GTH_TRANSFORM_ROTATE_270)
+ || (jpeg_info.orientation == GTH_TRANSFORM_TRANSPOSE)
+ || (jpeg_info.orientation == GTH_TRANSFORM_TRANSVERSE))
+ {
+ int tmp = width;
+ width = height;
+ height = tmp;
+ }
}
}
+
+ _jpeg_info_data_dispose (&jpeg_info);
}
#endif /* HAVE_LIBJPEG */
diff --git a/extensions/jpeg_utils/jpeg-info.c b/extensions/jpeg_utils/jpeg-info.c
index 915320e..8626cb1 100644
--- a/extensions/jpeg_utils/jpeg-info.c
+++ b/extensions/jpeg_utils/jpeg-info.c
@@ -3,7 +3,7 @@
/*
* GThumb
*
- * Copyright (C) 2011 Free Software Foundation, Inc.
+ * Copyright (C) 2011-2015 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
@@ -23,6 +23,28 @@
#include <config.h>
#include "jpeg-info.h"
+
+void
+_jpeg_info_data_init (JpegInfoData *data)
+{
+ data->valid = _JPEG_INFO_NONE;
+ data->width = 0;
+ data->height = 0;
+ data->orientation = GTH_TRANSFORM_NONE;
+ data->icc_data = NULL;
+ data->icc_data_size = 0;
+}
+
+
+void
+_jpeg_info_data_dispose (JpegInfoData *data)
+{
+ if (data->valid & _JPEG_INFO_ICC_PROFILE) {
+ g_free (data->icc_data);
+ }
+}
+
+
static guchar
_g_input_stream_read_byte (GInputStream *stream,
GCancellable *cancellable,
@@ -89,25 +111,6 @@ _jpeg_skip_segment_data (GInputStream *stream,
}
-static gboolean
-_jpeg_skip_to_segment (GInputStream *stream,
- guchar segment_id,
- GCancellable *cancellable,
- GError **error)
-{
- guchar marker_id = 0x00;
-
- while ((marker_id = _jpeg_read_segment_marker (stream, cancellable, error)) != 0x00) {
- if (marker_id == segment_id)
- return TRUE;
- if (! _jpeg_skip_segment_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)
@@ -130,10 +133,10 @@ _jpeg_exif_orientation_from_app1_segment (guchar *in_buffer,
/* Read Exif head, check for "Exif" */
- if ((in_buffer[pos++] != 0x45)
- || (in_buffer[pos++] != 0x78)
- || (in_buffer[pos++] != 0x69)
- || (in_buffer[pos++] != 0x66)
+ if ((in_buffer[pos++] != 'E')
+ || (in_buffer[pos++] != 'x')
+ || (in_buffer[pos++] != 'i')
+ || (in_buffer[pos++] != 'f')
|| (in_buffer[pos++] != 0)
|| (in_buffer[pos++] != 0))
{
@@ -260,34 +263,112 @@ _jpeg_exif_orientation_from_app1_segment (guchar *in_buffer,
}
-gboolean
-_jpeg_get_image_info (GInputStream *stream,
- int *width,
- int *height,
- GthTransform *orientation,
- GCancellable *cancellable,
- GError **error)
+/* -- _jpeg_get_icc_profile_chunk_from_app2_segment -- */
+
+
+typedef struct {
+ int seq_n;
+ int tot;
+ guchar *in_buffer;
+ guchar *data;
+ gsize size;
+} ICCProfileChunk;
+
+
+static void
+icc_profile_chunk_free (ICCProfileChunk *chunk)
{
- gboolean size_read;
- guchar marker_id;
+ g_free (chunk->in_buffer);
+ g_free (chunk);
+}
- size_read = FALSE;
- if (orientation != NULL)
- *orientation = GTH_TRANSFORM_NONE;
+static int
+icc_chunk_compare (gconstpointer a,
+ gconstpointer b)
+{
+ const ICCProfileChunk *chunk_a = a;
+ const ICCProfileChunk *chunk_b = b;
+
+ if (chunk_a->seq_n < chunk_b->seq_n)
+ return -1;
+ if (chunk_a->seq_n > chunk_b->seq_n)
+ return 1;
+ return 0;
+}
+
+
+static ICCProfileChunk *
+_jpeg_get_icc_profile_chunk_from_app2_segment (guchar *in_buffer,
+ gsize app2_segment_size)
+{
+ int pos;
+ guint length;
+ ICCProfileChunk *chunk;
+
+ length = app2_segment_size;
+ if (length <= 14)
+ return NULL;
+
+ pos = 0;
+
+ /* check for "ICC_PROFILE" */
+
+ if ((in_buffer[pos++] != 'I')
+ || (in_buffer[pos++] != 'C')
+ || (in_buffer[pos++] != 'C')
+ || (in_buffer[pos++] != '_')
+ || (in_buffer[pos++] != 'P')
+ || (in_buffer[pos++] != 'R')
+ || (in_buffer[pos++] != 'O')
+ || (in_buffer[pos++] != 'F')
+ || (in_buffer[pos++] != 'I')
+ || (in_buffer[pos++] != 'L')
+ || (in_buffer[pos++] != 'E')
+ || (in_buffer[pos++] != 0))
+ {
+ return NULL;
+ }
+
+ chunk = g_new (ICCProfileChunk, 1);
+ chunk->in_buffer = in_buffer;
+ chunk->seq_n = in_buffer[pos++];
+ chunk->tot = in_buffer[pos++];
+ chunk->data = in_buffer + 14;
+ chunk->size = app2_segment_size - 14;
+
+ return chunk;
+}
+
+gboolean
+_jpeg_info_get_from_stream (GInputStream *stream,
+ JpegInfoFlags flags,
+ JpegInfoData *data,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GList *icc_chunks;
+ guchar marker_id;
+
+ g_return_val_if_fail (data->valid == _JPEG_INFO_NONE, FALSE);
+
+ icc_chunks = NULL;
while ((marker_id = _jpeg_read_segment_marker (stream, cancellable, error)) != 0x00) {
gboolean segment_data_consumed = FALSE;
- if ((marker_id == 0xc0) || (marker_id == 0xc2)) { /* SOF0 or SOF1 */
+ if ((flags & _JPEG_INFO_IMAGE_SIZE)
+ && ! (data->valid & _JPEG_INFO_IMAGE_SIZE)
+ && ((marker_id == 0xc0) || (marker_id == 0xc2))) /* SOF0 or SOF1 */
+ {
guint h, l;
- /*guint size;*/
+ guint size;
/* size */
h = _g_input_stream_read_byte (stream, cancellable, error);
l = _g_input_stream_read_byte (stream, cancellable, error);
- /*size = (h << 8) + l;*/
+ size = (h << 8) + l;
/* data precision */
@@ -297,107 +378,128 @@ _jpeg_get_image_info (GInputStream *stream,
h = _g_input_stream_read_byte (stream, cancellable, error);
l = _g_input_stream_read_byte (stream, cancellable, error);
- if (height != NULL)
- *height = (h << 8) + l;
+ data->height = (h << 8) + l;
/* width */
h = _g_input_stream_read_byte (stream, cancellable, error);
l = _g_input_stream_read_byte (stream, cancellable, error);
- if (width != NULL)
- *width = (h << 8) + l;
-
- size_read = TRUE;
+ data->width = (h << 8) + l;
- /* Skip to the end of the segment. In general this is
- * needed but in this case we exit after reading the
- * size. */
- /* g_input_stream_skip (stream, size - 7, cancellable, error); */
+ g_input_stream_skip (stream, size - 7, cancellable, error);
segment_data_consumed = TRUE;
}
- else if (marker_id == 0xe1) { /* APP1 */
+
+ if ((flags & _JPEG_INFO_EXIF_ORIENTATION)
+ && ! (data->valid & _JPEG_INFO_EXIF_ORIENTATION)
+ && (marker_id == 0xe1)) { /* APP1 */
guint h, l;
- gsize size;
+ guint app1_segment_size;
guchar *app1_segment;
- /* size */
-
h = _g_input_stream_read_byte (stream, cancellable, error);
l = _g_input_stream_read_byte (stream, cancellable, error);
- size = (h << 8) + l - 2;
+ app1_segment_size = (h << 8) + l - 2;
- 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);
+ app1_segment = g_new (guchar, app1_segment_size);
+ if (g_input_stream_read (stream,
+ app1_segment,
+ app1_segment_size,
+ cancellable,
+ error) > 0)
+ {
+ data->valid |= _JPEG_INFO_EXIF_ORIENTATION;
+ data->orientation = _jpeg_exif_orientation_from_app1_segment (app1_segment,
app1_segment_size);
+ }
segment_data_consumed = TRUE;
g_free (app1_segment);
}
- /* If we have read the size we are done because the APP1
- * segment, if present, is always the first. */
+ if ((flags & _JPEG_INFO_ICC_PROFILE)
+ && ! (data->valid & _JPEG_INFO_ICC_PROFILE)
+ && (marker_id == 0xe2)) /* APP2 */
+ {
+ guint h, l;
+ gsize app2_segment_size;
+ guchar *app2_segment;
- if (size_read)
- break;
+ /* size */
+
+ h = _g_input_stream_read_byte (stream, cancellable, error);
+ l = _g_input_stream_read_byte (stream, cancellable, error);
+ app2_segment_size = (h << 8) + l - 2;
+
+ app2_segment = g_new (guchar, app2_segment_size);
+ if (g_input_stream_read (stream, app2_segment, app2_segment_size, cancellable, error)
0) {
+ ICCProfileChunk *chunk;
+
+ chunk = _jpeg_get_icc_profile_chunk_from_app2_segment (app2_segment,
app2_segment_size);
+ if (chunk != NULL)
+ icc_chunks = g_list_prepend (icc_chunks, chunk);
+ }
+
+ segment_data_consumed = TRUE;
+
+ g_free (app2_segment);
+ }
if (! segment_data_consumed && ! _jpeg_skip_segment_data (stream, marker_id, cancellable,
error))
break;
}
- return size_read;
-}
+ if (flags & _JPEG_INFO_ICC_PROFILE) {
+ gboolean valid_icc = (icc_chunks != NULL);
+ GOutputStream *ostream;
+ GList *scan;
+ int seq_n;
+
+ ostream = g_memory_output_stream_new_resizable ();
+ icc_chunks = g_list_sort (icc_chunks, icc_chunk_compare);
+ seq_n = 1;
+ for (scan = icc_chunks; scan; scan = scan->next) {
+ ICCProfileChunk *chunk = scan->data;
+
+ if (chunk->seq_n != seq_n) {
+ valid_icc = FALSE;
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Invalid ICC
data");
+ break;
+ }
+ g_output_stream_write_all (ostream, chunk->data, chunk->size, NULL, cancellable,
error);
+ seq_n++;
+ }
-GthTransform
-_jpeg_exif_orientation (guchar *in_buffer,
- gsize in_buffer_size)
-{
- GInputStream *stream;
- GthTransform orientation;
+ if (valid_icc) {
+ data->valid |= _JPEG_INFO_ICC_PROFILE;
+ data->icc_data = g_memory_output_stream_steal_data (G_MEMORY_OUTPUT_STREAM (ostream));
+ data->icc_data_size = g_memory_output_stream_get_data_size (G_MEMORY_OUTPUT_STREAM
(ostream));
+ }
- 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 (ostream);
+ }
- g_object_unref (stream);
+ g_list_free_full (icc_chunks, (GDestroyNotify) icc_profile_chunk_free);
- return orientation;
+ return (flags == data->valid);
}
-GthTransform
-_jpeg_exif_orientation_from_stream (GInputStream *stream,
- GCancellable *cancellable,
- GError **error)
+gboolean
+_jpeg_info_get_from_buffer (guchar *in_buffer,
+ gsize in_buffer_size,
+ JpegInfoFlags flags,
+ JpegInfoData *data)
{
- GthTransform orientation;
-
- orientation = GTH_TRANSFORM_NONE;
-
- if (_jpeg_read_segment_marker (stream, cancellable, error) == 0xd8) {
- if (_jpeg_skip_to_segment (stream, 0xe1, cancellable, error)) {
- guint h, l;
- guint app1_segment_size;
- guchar *app1_segment;
-
- h = _g_input_stream_read_byte (stream, cancellable, error);
- l = _g_input_stream_read_byte (stream, cancellable, error);
- app1_segment_size = (h << 8) + l - 2;
+ GInputStream *stream;
+ gboolean result;
- 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);
- }
+ stream = g_memory_input_stream_new_from_data (in_buffer, in_buffer_size, NULL);
+ result = _jpeg_info_get_from_stream (stream, flags, data, NULL, NULL);
- g_free (app1_segment);
- }
- }
+ g_object_unref (stream);
- return orientation;
+ return result;
}
diff --git a/extensions/jpeg_utils/jpeg-info.h b/extensions/jpeg_utils/jpeg-info.h
index 4807fa3..09c718b 100644
--- a/extensions/jpeg_utils/jpeg-info.h
+++ b/extensions/jpeg_utils/jpeg-info.h
@@ -3,7 +3,7 @@
/*
* GThumb
*
- * Copyright (C) 2001-2009 The Free Software Foundation, Inc.
+ * Copyright (C) 2001-2015 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
@@ -26,16 +26,32 @@
#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);
+typedef enum /*< skip >*/ {
+ _JPEG_INFO_NONE = 0,
+ _JPEG_INFO_IMAGE_SIZE = 1 << 0,
+ _JPEG_INFO_EXIF_ORIENTATION = 1 << 1,
+ _JPEG_INFO_ICC_PROFILE = 1 << 2
+} JpegInfoFlags;
+
+typedef struct {
+ JpegInfoFlags valid;
+ int width;
+ int height;
+ GthTransform orientation;
+ gpointer icc_data;
+ gsize icc_data_size;
+} JpegInfoData;
+
+void _jpeg_info_data_init (JpegInfoData *data);
+void _jpeg_info_data_dispose (JpegInfoData *data);
+gboolean _jpeg_info_get_from_stream (GInputStream *stream,
+ JpegInfoFlags flags,
+ JpegInfoData *data,
+ GCancellable *cancellable,
+ GError **error);
+gboolean _jpeg_info_get_from_buffer (guchar *in_buffer,
+ gsize in_buffer_size,
+ JpegInfoFlags flags,
+ JpegInfoData *data);
#endif /* JPEG_INFO_H */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]