eog/libeog/eog-image.c
Paolo Borelli e22cfa75a0 don't stomp on an already set GError. Fixes bug #320342.
2005-11-09  Paolo Borelli  <pborelli@katamail.com>

	* libeog/eog-image.c (eog_image_real_load): don't stomp on an
	already	set GError. Fixes bug #320342.
2005-11-09 20:47:41 +00:00

2014 lines
47 KiB
C

#include <config.h>
#include <string.h>
#include <unistd.h>
#include <glib/gthread.h>
#include <glib/gqueue.h>
#include <gtk/gtkmain.h>
#include <libgnome/gnome-macros.h>
#include <glib/gi18n.h>
#include <libgnomeui/gnome-thumbnail.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <libgnomevfs/gnome-vfs.h>
#if HAVE_EXIF
#include <libexif/exif-data.h>
#include <libexif/exif-utils.h>
#include <libexif/exif-loader.h>
#endif
#include "libeog-marshal.h"
#include "eog-image.h"
#include "eog-image-private.h"
#include "eog-pixbuf-util.h"
#include "eog-metadata-reader.h"
#include "eog-image-save-info.h"
#include "eog-info-view-file.h"
#include "eog-util.h"
#if HAVE_JPEG
#include "eog-image-jpeg.h"
#endif
#if HAVE_LCMS
#include <lcms.h>
#ifndef EXIF_TAG_GAMMA
#define EXIF_TAG_GAMMA 0xa500
#endif
#endif
enum {
SIGNAL_LOADING_UPDATE,
SIGNAL_LOADING_SIZE_PREPARED,
SIGNAL_LOADING_FINISHED,
SIGNAL_LOADING_FAILED,
SIGNAL_LOADING_CANCELLED,
SIGNAL_PROGRESS,
SIGNAL_IMAGE_CHANGED,
SIGNAL_THUMBNAIL_FINISHED,
SIGNAL_THUMBNAIL_FAILED,
SIGNAL_THUMBNAIL_CANCELLED,
SIGNAL_LAST
};
static gint eog_image_signals [SIGNAL_LAST];
#define NO_DEBUG
#define DEBUG_ASYNC 0
#define OBJECT_WATCH 0
#if OBJECT_WATCH
static int n_active_images = 0;
#endif
#define CHECK_LOAD_TIMEOUT 5
/* Chunk size for reading image data */
#define READ_BUFFER_SIZE 65536
/*======================================
EogImage implementation
------------------------------------*/
GNOME_CLASS_BOILERPLATE (EogImage,
eog_image,
GObject,
G_TYPE_OBJECT);
static void
eog_image_dispose (GObject *object)
{
EogImagePrivate *priv;
GList *it;
priv = EOG_IMAGE (object)->priv;
eog_image_free_mem_private (EOG_IMAGE (object));
if (priv->toclean) {
g_free (priv->toclean);
if (priv->monitor_handle != NULL) {
gnome_vfs_monitor_cancel (priv->monitor_handle);
}
}
if (priv->uri) {
gnome_vfs_uri_unref (priv->uri);
priv->uri = NULL;
}
if (priv->caption) {
g_free (priv->caption);
priv->caption = NULL;
}
if (priv->caption_key) {
g_free (priv->caption_key);
priv->caption_key = NULL;
}
if (priv->file_type) {
g_free (priv->file_type);
priv->file_type = NULL;
}
if (priv->error_message) {
g_free (priv->error_message);
priv->error_message = NULL;
}
if (priv->status_mutex) {
g_mutex_free (priv->status_mutex);
priv->status_mutex = NULL;
}
if (priv->trans) {
g_object_unref (priv->trans);
priv->trans = NULL;
}
if (priv->undo_stack) {
for (it = priv->undo_stack; it != NULL; it = it->next){
g_object_unref (G_OBJECT (it->data));
}
g_list_free (priv->undo_stack);
priv->undo_stack = NULL;
}
}
static void
eog_image_finalize (GObject *object)
{
EogImagePrivate *priv;
priv = EOG_IMAGE (object)->priv;
g_free (priv);
#if OBJECT_WATCH
n_active_images--;
if (n_active_images == 0) {
g_message ("All image objects destroyed.");
}
else {
g_message ("active image objects: %i", n_active_images);
}
#endif
}
static void
eog_image_class_init (EogImageClass *klass)
{
GObjectClass *object_class = (GObjectClass*) klass;
object_class->dispose = eog_image_dispose;
object_class->finalize = eog_image_finalize;
eog_image_signals [SIGNAL_LOADING_UPDATE] =
g_signal_new ("loading_update",
G_TYPE_OBJECT,
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (EogImageClass, loading_update),
NULL, NULL,
libeog_marshal_VOID__INT_INT_INT_INT,
G_TYPE_NONE, 4,
G_TYPE_INT,
G_TYPE_INT,
G_TYPE_INT,
G_TYPE_INT);
eog_image_signals [SIGNAL_LOADING_SIZE_PREPARED] =
g_signal_new ("loading_size_prepared",
G_TYPE_OBJECT,
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (EogImageClass, loading_size_prepared),
NULL, NULL,
libeog_marshal_VOID__INT_INT,
G_TYPE_NONE, 2,
G_TYPE_INT,
G_TYPE_INT);
eog_image_signals [SIGNAL_LOADING_FINISHED] =
g_signal_new ("loading_finished",
G_TYPE_OBJECT,
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (EogImageClass, loading_finished),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
eog_image_signals [SIGNAL_LOADING_FAILED] =
g_signal_new ("loading_failed",
G_TYPE_OBJECT,
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (EogImageClass, loading_failed),
NULL, NULL,
libeog_marshal_VOID__POINTER,
G_TYPE_NONE, 1,
G_TYPE_POINTER);
eog_image_signals [SIGNAL_LOADING_CANCELLED] =
g_signal_new ("loading_cancelled",
G_TYPE_OBJECT,
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (EogImageClass, loading_cancelled),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
eog_image_signals [SIGNAL_PROGRESS] =
g_signal_new ("progress",
G_TYPE_OBJECT,
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (EogImageClass, progress),
NULL, NULL,
g_cclosure_marshal_VOID__FLOAT,
G_TYPE_NONE, 1,
G_TYPE_FLOAT);
eog_image_signals [SIGNAL_IMAGE_CHANGED] =
g_signal_new ("image_changed",
G_TYPE_OBJECT,
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (EogImageClass, image_changed),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
eog_image_signals [SIGNAL_THUMBNAIL_FINISHED] =
g_signal_new ("thumbnail_finished",
G_TYPE_OBJECT,
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (EogImageClass, thumbnail_finished),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
eog_image_signals [SIGNAL_THUMBNAIL_FAILED] =
g_signal_new ("thumbnail_failed",
G_TYPE_OBJECT,
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (EogImageClass, thumbnail_failed),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
eog_image_signals [SIGNAL_THUMBNAIL_CANCELLED] =
g_signal_new ("thumbnail_cancelled",
G_TYPE_OBJECT,
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (EogImageClass, thumbnail_cancelled),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
}
static void
eog_image_instance_init (EogImage *img)
{
EogImagePrivate *priv;
priv = g_new0 (EogImagePrivate, 1);
priv->uri = NULL;
priv->image = NULL;
priv->thumbnail = NULL;
priv->width = priv->height = -1;
priv->modified = FALSE;
priv->status_mutex = g_mutex_new ();
priv->load_finished = NULL;
priv->error_message = NULL;
#if HAVE_EXIF
priv->exif = NULL;
#endif
#if HAVE_LCMS
priv->profile = NULL;
#endif
priv->data_ref_count = 0;
img->priv = priv;
}
EogImage*
eog_image_new_uri (GnomeVFSURI *uri)
{
EogImage *img;
EogImagePrivate *priv;
img = EOG_IMAGE (g_object_new (EOG_TYPE_IMAGE, NULL));
priv = img->priv;
priv->uri = gnome_vfs_uri_ref (uri);
priv->status = EOG_IMAGE_STATUS_UNKNOWN;
priv->modified = FALSE;
priv->load_thread = NULL;
priv->undo_stack = NULL;
priv->trans = NULL;
#if OBJECT_WATCH
n_active_images++;
g_message ("active image objects: %i", n_active_images);
#endif
return img;
}
EogImage*
eog_image_new (const char *txt_uri)
{
GnomeVFSURI *uri;
EogImage *image;
uri = gnome_vfs_uri_new (txt_uri);
image = eog_image_new_uri (uri);
gnome_vfs_uri_unref (uri);
return image;
}
GQuark
eog_image_error_quark (void)
{
static GQuark q = 0;
if (q == 0)
q = g_quark_from_static_string ("eog-image-error-quark");
return q;
}
#if HAVE_EXIF
static void
update_exif_data (EogImage *image)
{
EogImagePrivate *priv;
ExifEntry *entry;
ExifByteOrder bo;
g_return_if_fail (EOG_IS_IMAGE (image));
priv = image->priv;
#ifdef DEBUG
g_message ("update exif data");
#endif
if (priv->exif == NULL) return;
/* FIXME: Must we update more properties here? */
bo = exif_data_get_byte_order (priv->exif);
entry = exif_content_get_entry (priv->exif->ifd [EXIF_IFD_EXIF], EXIF_TAG_PIXEL_X_DIMENSION);
if (entry != NULL && (priv->width >= 0)) {
exif_set_long (entry->data, bo, priv->width);
}
entry = exif_content_get_entry (priv->exif->ifd [EXIF_IFD_EXIF], EXIF_TAG_PIXEL_Y_DIMENSION);
if (entry != NULL && (priv->height >= 0)) {
exif_set_long (entry->data, bo, priv->height);
}
}
#else
static void
update_exif_data (EogImage *image)
{
/* nothing todo if we don't have exif support */
}
#endif
static void
load_size_prepared (GdkPixbufLoader *loader, gint width, gint height, gpointer data)
{
EogImage *img;
g_return_if_fail (EOG_IS_IMAGE (data));
img = EOG_IMAGE (data);
g_mutex_lock (img->priv->status_mutex);
img->priv->width = width;
img->priv->height = height;
g_mutex_unlock (img->priv->status_mutex);
}
static EogMetadataReader*
check_for_metadata_img_format (EogImage *img, guchar *buffer, int bytes_read)
{
EogMetadataReader *md_reader = NULL;
#ifdef DEBUG
g_print ("check img format for jpeg: %x%x - length: %i\n", buffer[0], buffer[1], bytes_read);
#endif
if (bytes_read >= 2) {
/* SOI (start of image) marker for JPEGs is 0xFFD8 */
if ((buffer[0] == 0xFF) && (buffer[1] == 0xD8)) {
md_reader = eog_metadata_reader_new (EOG_METADATA_JPEG);
}
}
return md_reader;
}
static gboolean
eog_image_needs_transformation (EogImage *img)
{
g_return_val_if_fail (EOG_IS_IMAGE (img), FALSE);
return (img->priv->trans != NULL);
}
static gboolean
eog_image_apply_transformations (EogImage *img, EogJob *job, GError **error)
{
GdkPixbuf *transformed = NULL;
EogImagePrivate *priv;
g_return_val_if_fail (EOG_IS_IMAGE (img), FALSE);
priv = img->priv;
if (priv->trans == NULL) {
return TRUE;
}
if (priv->image == NULL) {
g_set_error (error, EOG_IMAGE_ERROR, EOG_IMAGE_ERROR_NOT_LOADED,
_("Transformation on unloaded image."));
return FALSE;
}
g_assert (priv->image != NULL);
if (priv->trans != NULL) {
transformed = eog_transform_apply (priv->trans, priv->image, job);
}
if (transformed != NULL) {
g_object_unref (priv->image);
priv->image = transformed;
priv->width = gdk_pixbuf_get_width (priv->image);
priv->height = gdk_pixbuf_get_height (priv->image);
}
return (transformed != NULL);
}
static GnomeVFSFileSize
eog_image_determine_file_bytes (EogImage *img, GError **error)
{
GnomeVFSFileInfo *info;
GnomeVFSFileSize bytes;
GnomeVFSResult result;
/* determine file size */
info = gnome_vfs_file_info_new ();
result = gnome_vfs_get_file_info_uri (img->priv->uri,
info,
GNOME_VFS_FILE_INFO_DEFAULT |
GNOME_VFS_FILE_INFO_FOLLOW_LINKS);
if ((result != GNOME_VFS_OK) || (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE) == 0) {
bytes = 0;
g_set_error (error, EOG_IMAGE_ERROR, EOG_IMAGE_ERROR_VFS,
gnome_vfs_result_to_string (result));
}
else {
bytes = info->size;
}
gnome_vfs_file_info_unref (info);
return bytes;
}
/* this function runs in it's own thread */
static gboolean
eog_image_load_exif_data_only (EogImage *img, EogJob *job, GError **error)
{
EogImagePrivate *priv;
GnomeVFSHandle *handle;
guchar *buffer;
GnomeVFSFileSize bytes_read;
GnomeVFSFileSize bytes_read_total;
GnomeVFSResult result;
gboolean failed;
gboolean first_run = TRUE;
EogMetadataReader *md_reader = NULL;
g_assert (error == NULL || *error == NULL);
priv = img->priv;
#ifdef DEBUG
g_print ("real image exif load %s\n", gnome_vfs_uri_to_string (priv->uri, GNOME_VFS_URI_HIDE_NONE));
#endif
g_assert (priv->image == NULL);
priv->bytes = eog_image_determine_file_bytes (img, error);
if (priv->bytes == 0 && (error == NULL || *error != NULL)) {
return FALSE;
}
result = gnome_vfs_open_uri (&handle, priv->uri, GNOME_VFS_OPEN_READ);
if (result != GNOME_VFS_OK) {
g_set_error (error, EOG_IMAGE_ERROR, EOG_IMAGE_ERROR_VFS,
gnome_vfs_result_to_string (result));
return FALSE;
}
buffer = g_new0 (guchar, READ_BUFFER_SIZE);
failed = FALSE;
bytes_read_total = 0;
while (!priv->cancel_loading && (first_run || !eog_metadata_reader_finished (md_reader))) {
result = gnome_vfs_read (handle, buffer, READ_BUFFER_SIZE, &bytes_read);
if (result == GNOME_VFS_ERROR_EOF || bytes_read == 0) {
break;
}
else if (result != GNOME_VFS_OK) {
failed = TRUE;
g_set_error (error, EOG_IMAGE_ERROR, EOG_IMAGE_ERROR_VFS,
gnome_vfs_result_to_string (result));
break;
}
bytes_read_total += bytes_read;
if (job != NULL) {
float progress = (float) bytes_read_total / (float) priv->bytes;
eog_job_set_progress (job, progress);
}
/* check if we support reading metadata for that image format (only JPG atm) */
if (first_run) {
md_reader = check_for_metadata_img_format (img, buffer, bytes_read);
g_set_error (error, EOG_IMAGE_ERROR, EOG_IMAGE_ERROR_GENERIC,
_("EXIF not supported for this file format."));
first_run = FALSE;
}
if (md_reader != NULL) {
eog_metadata_reader_consume (md_reader, buffer, bytes_read);
}
}
g_free (buffer);
gnome_vfs_close (handle);
failed = (failed || bytes_read_total == 0 || md_reader == NULL || priv->cancel_loading);
if (priv->cancel_loading) {
priv->cancel_loading = FALSE;
priv->status = EOG_IMAGE_STATUS_UNKNOWN;
}
else if (!failed && (md_reader != NULL)) {
/* update meta data */
#if HAVE_EXIF
priv->exif = eog_metadata_reader_get_exif_data (md_reader);
priv->exif_chunk = NULL;
priv->exif_chunk_len = 0;
#else
eog_metadata_reader_get_exif_chunk (md_reader, &priv->exif_chunk, &priv->exif_chunk_len);
#endif
}
/* clean up */
if (md_reader != NULL) {
g_object_unref (md_reader);
}
return !failed;
}
#if HAVE_LCMS
void
eog_image_apply_display_profile (EogImage *img, cmsHPROFILE screen)
{
EogImagePrivate *priv;
cmsHTRANSFORM transform;
int row, width, rows, stride;
guchar *p;
g_return_if_fail (img != NULL);
if (screen == NULL)
return;
priv = img->priv;
if (priv->profile == NULL)
return;
transform = cmsCreateTransform(priv->profile, TYPE_RGB_8, screen, TYPE_RGB_8, INTENT_PERCEPTUAL, 0);
rows = gdk_pixbuf_get_height(priv->image);
width = gdk_pixbuf_get_width (priv->image);
stride = gdk_pixbuf_get_rowstride (priv->image);
p = gdk_pixbuf_get_pixels (priv->image);
for (row = 0; row < rows; ++row) {
cmsDoTransform(transform, p, p, width);
p += stride;
}
cmsDeleteTransform(transform);
//cmsCloseProfile(screen);
}
static void
extract_profile (EogImage *img, EogMetadataReader *md_reader)
{
EogImagePrivate *priv = img->priv;
#if HAVE_EXIF
ExifEntry *entry;
const ExifByteOrder o = exif_data_get_byte_order (priv->exif);
#endif
/* TODO: switch on format to specialised functions */
/* Embedded ICC profiles rule over anything else */
{
gpointer data = eog_metadata_reader_get_icc_chunk (md_reader);
if (data != NULL) {
cmsErrorAction (LCMS_ERROR_SHOW);
priv->profile = cmsOpenProfileFromMem(data, eog_metadata_reader_get_icc_chunk_size (md_reader));
if (priv->profile) {
g_printerr("JPEG has ICC profile\n");
} else {
g_printerr("JPEG has invalid ICC profile\n");
}
return;
}
}
#if HAVE_EXIF
/* No EXIF data, so can't do anything */
if (priv->exif == NULL)
return;
entry = exif_content_get_entry (priv->exif->ifd [EXIF_IFD_EXIF], EXIF_TAG_COLOR_SPACE);
if (entry == NULL)
return;
if (exif_get_short (entry->data, o) == 1) {
priv->profile = cmsCreate_sRGBProfile ();
g_printerr ("JPEG is sRGB\n");
} else if (exif_get_short (entry->data, o) == 2) {
/* TODO: create Adobe RGB profile */
//priv->profile = cmsCreate_Adobe1998Profile ();
g_printerr ("JPEG is Adobe RGB (NOT correcting for now!)\n");
} else if (exif_get_short (entry->data, o) == 0xFFFF) {
double gammaValue;
cmsCIExyY whitepoint;
cmsCIExyYTRIPLE primaries;
LPGAMMATABLE gamma[3];
const int offset = exif_format_get_size (EXIF_FORMAT_RATIONAL);
ExifRational r;
entry = exif_content_get_entry (priv->exif->ifd [EXIF_IFD_0], EXIF_TAG_WHITE_POINT);
if (entry && entry->components == 2) {
r = exif_get_rational (entry->data, o);
whitepoint.x = (double)r.numerator/r.denominator;
r = exif_get_rational (entry->data + offset, o);
whitepoint.y = (double)r.numerator/r.denominator;
whitepoint.Y = 1.0;
} else {
g_printerr("No whitepoint found\n");
return;
}
entry = exif_content_get_entry (priv->exif->ifd [EXIF_IFD_0], EXIF_TAG_PRIMARY_CHROMATICITIES);
if (entry && entry->components == 6) {
r = exif_get_rational (entry->data + 0 * offset, o);
primaries.Red.x = (double)r.numerator/r.denominator;
r = exif_get_rational (entry->data + 1 * offset, o);
primaries.Red.y = (double)r.numerator/r.denominator;
r = exif_get_rational (entry->data + 2 * offset, o);
primaries.Green.x = (double)r.numerator/r.denominator;
r = exif_get_rational (entry->data + 3 * offset, o);
primaries.Green.y = (double)r.numerator/r.denominator;
r = exif_get_rational (entry->data + 4 * offset, o);
primaries.Blue.x = (double)r.numerator/r.denominator;
r = exif_get_rational (entry->data + 5 * offset, o);
primaries.Blue.y = (double)r.numerator/r.denominator;
primaries.Red.Y = primaries.Green.Y = primaries.Blue.Y = 1.0;
} else {
g_printerr("No primary chromaticities found\n");
return;
}
entry = exif_content_get_entry (priv->exif->ifd [EXIF_IFD_EXIF], EXIF_TAG_GAMMA);
if (entry) {
r = exif_get_rational (entry->data, o);
gammaValue = (double)r.numerator/r.denominator;
} else {
/* Assume 2.2 */
g_printerr("No gamma found\n");
gammaValue = 2.2;
}
gamma[0] = gamma[1] = gamma[2] = cmsBuildGamma(256, gammaValue);
priv->profile = cmsCreateRGBProfile(&whitepoint, &primaries, gamma);
g_printerr ("JPEG is calibrated\n");
cmsFreeGamma(gamma[0]);
}
#endif
}
#endif
/* this function runs in it's own thread */
static gboolean
eog_image_real_load (EogImage *img, guint data2read, EogJob *job, GError **error)
{
EogImagePrivate *priv;
GdkPixbufLoader *loader;
GnomeVFSHandle *handle;
guchar *buffer;
GnomeVFSFileSize bytes_read;
GnomeVFSFileSize bytes_read_total;
GnomeVFSResult result;
gboolean failed;
gboolean first_run = TRUE;
EogMetadataReader *md_reader = NULL;
GdkPixbufFormat *format;
g_assert (error == NULL || *error == NULL);
priv = img->priv;
#ifdef DEBUG
g_print ("real image load %s\n", gnome_vfs_uri_to_string (priv->uri, GNOME_VFS_URI_HIDE_NONE));
#endif
g_assert (priv->image == NULL);
if (priv->file_type != NULL) {
g_free (priv->file_type);
priv->file_type = NULL;
}
priv->bytes = eog_image_determine_file_bytes (img, error);
if (priv->bytes == 0 && (error == NULL || *error != NULL)) {
return FALSE;
}
result = gnome_vfs_open_uri (&handle, priv->uri, GNOME_VFS_OPEN_READ);
if (result != GNOME_VFS_OK) {
g_set_error (error, EOG_IMAGE_ERROR, EOG_IMAGE_ERROR_VFS,
gnome_vfs_result_to_string (result));
return FALSE;
}
buffer = g_new0 (guchar, READ_BUFFER_SIZE);
loader = gdk_pixbuf_loader_new ();
failed = FALSE;
bytes_read_total = 0;
g_signal_connect_object (G_OBJECT (loader), "size-prepared", (GCallback) load_size_prepared, img, 0);
while (!priv->cancel_loading) {
result = gnome_vfs_read (handle, buffer, READ_BUFFER_SIZE, &bytes_read);
if (result == GNOME_VFS_ERROR_EOF || bytes_read == 0) {
break;
}
else if (result != GNOME_VFS_OK) {
failed = TRUE;
g_set_error (error, EOG_IMAGE_ERROR, EOG_IMAGE_ERROR_VFS,
gnome_vfs_result_to_string (result));
break;
}
if (!gdk_pixbuf_loader_write (loader, buffer, bytes_read, error)) {
failed = TRUE;
break;
}
bytes_read_total += bytes_read;
if (job != NULL) {
float progress = (float) bytes_read_total / (float) priv->bytes;
eog_job_set_progress (job, progress);
}
/* check if we support reading metadata for that image format (only JPG atm) */
if (first_run) {
md_reader = check_for_metadata_img_format (img, buffer, bytes_read);
first_run = FALSE;
}
if (md_reader != NULL) {
eog_metadata_reader_consume (md_reader, buffer, bytes_read);
}
}
/* if we already failed ignore errors on close */
if (failed) {
gdk_pixbuf_loader_close (loader, NULL);
}
else if (!gdk_pixbuf_loader_close (loader, error)) {
failed = TRUE;
}
g_free (buffer);
gnome_vfs_close (handle);
failed = (failed || bytes_read_total == 0 || priv->cancel_loading);
if (*error != NULL) {
failed = TRUE;
priv->status = EOG_IMAGE_STATUS_FAILED;
}
if (priv->cancel_loading) {
priv->cancel_loading = FALSE;
failed = TRUE;
priv->status = EOG_IMAGE_STATUS_UNKNOWN;
}
else if (!failed) {
/* update image data */
if (priv->image != NULL) {
g_object_unref (priv->image);
}
priv->image = gdk_pixbuf_loader_get_pixbuf (loader);
g_assert (priv->image != NULL);
g_object_ref (priv->image);
priv->width = gdk_pixbuf_get_width (priv->image);
priv->height = gdk_pixbuf_get_height (priv->image);
/* update meta data */
if (md_reader != NULL) {
#if HAVE_EXIF
priv->exif = eog_metadata_reader_get_exif_data (md_reader);
priv->exif_chunk = NULL;
priv->exif_chunk_len = 0;
update_exif_data (img);
#else
eog_metadata_reader_get_exif_chunk (md_reader, &priv->exif_chunk, &priv->exif_chunk_len);
#endif
}
/* update file format */
format = gdk_pixbuf_loader_get_format (loader);
if (format != NULL) {
priv->file_type = gdk_pixbuf_format_get_name (format);
}
#if HAVE_LCMS
if (md_reader != NULL) {
extract_profile (img, md_reader);
}
#endif
}
/* clean up */
g_object_unref (loader);
if (md_reader != NULL) {
g_object_unref (md_reader);
md_reader = NULL;
}
return !failed;
}
gboolean
eog_image_has_data (EogImage *img, guint req_data)
{
EogImagePrivate *priv;
gboolean has_data = TRUE;
g_return_val_if_fail (EOG_IS_IMAGE (img), FALSE);
priv = img->priv;
if ((req_data & EOG_IMAGE_DATA_IMAGE) > 0) {
req_data = (req_data & !EOG_IMAGE_DATA_IMAGE); // remove from req_data
has_data = has_data && (priv->image != NULL);
}
if ((req_data & EOG_IMAGE_DATA_DIMENSION) > 0 ) {
req_data = (req_data & !EOG_IMAGE_DATA_DIMENSION); // remove from req_data
has_data = has_data && (priv->width >= 0) && (priv->height >= 0);
}
if ((req_data & EOG_IMAGE_DATA_EXIF) > 0) {
req_data = (req_data & !EOG_IMAGE_DATA_EXIF); // remove from req_data
#if HAVE_EXIF
has_data = has_data && (priv->exif != NULL);
#else
has_data = has_data && (priv->exif_chunk != NULL);
#endif
}
if (req_data != 0) {
g_warning ("Asking for unknown data, remaining: %i\n", req_data);
has_data = FALSE;
}
return has_data;
}
gboolean
eog_image_load (EogImage *img, guint data2read, EogJob *job, GError **error)
{
EogImagePrivate *priv;
gboolean success = FALSE;
g_return_val_if_fail (EOG_IS_IMAGE (img), FALSE);
priv = EOG_IMAGE (img)->priv;
g_print ("eog-image_load.c\n");
if (data2read == 0) {
/* nothing to read */
return TRUE;
}
if (eog_image_has_data (img, data2read)) {
g_warning ("Image %s has requested data already loaded.\n",
eog_image_get_caption (img));
}
if (priv->status == EOG_IMAGE_STATUS_FAILED) {
g_set_error (error, EOG_IMAGE_ERROR, EOG_IMAGE_ERROR_NOT_LOADED,
priv->error_message);
return FALSE;
}
priv->status = EOG_IMAGE_STATUS_LOADING;
/* if transformations will be need, the number of job parts should be increased */
if (eog_image_needs_transformation (img) && job != NULL) {
guint n_parts;
g_object_get (G_OBJECT (job), "progress-n-parts", &n_parts, NULL);
g_object_set (G_OBJECT (job), "progress-n-parts", ++n_parts, NULL);
}
/* Read the requested data from the image */
if (data2read == EOG_IMAGE_DATA_EXIF) {
success = eog_image_load_exif_data_only (img, job, error);
}
else {
success = eog_image_real_load (img, data2read, job, error);
}
g_print ("load success: %i\n", success);
/* perform required transformation */
if (eog_image_needs_transformation (img)) {
if (job != NULL) {
eog_job_part_finished (job);
}
if (success) {
success = eog_image_apply_transformations (img, job, error);
}
}
/* update status */
if (success) {
priv->status = EOG_IMAGE_STATUS_LOADED;
}
else {
priv->status = EOG_IMAGE_STATUS_FAILED;
/* set the image error message */
if (priv->error_message != NULL) {
g_free (priv->error_message);
}
priv->error_message = g_strdup((*error)->message);
}
return success;
}
void
eog_image_set_thumbnail (EogImage *img, GdkPixbuf *thumbnail)
{
EogImagePrivate *priv;
g_return_if_fail (EOG_IS_IMAGE (img));
g_return_if_fail (thumbnail != NULL);
priv = img->priv;
if (priv->thumbnail != NULL)
{
g_object_unref (priv->thumbnail);
priv->thumbnail = NULL;
}
if (priv->trans != NULL) {
priv->thumbnail = eog_transform_apply (priv->trans, thumbnail, NULL);
}
else {
priv->thumbnail = thumbnail;
g_object_ref (priv->thumbnail);
}
}
gboolean
eog_image_is_animation (EogImage *img)
{
return FALSE;
}
GdkPixbuf*
eog_image_get_pixbuf (EogImage *img)
{
GdkPixbuf *image = NULL;
g_return_val_if_fail (EOG_IS_IMAGE (img), NULL);
g_mutex_lock (img->priv->status_mutex);
image = img->priv->image;
g_mutex_unlock (img->priv->status_mutex);
if (image != NULL) {
g_object_ref (image);
}
return image;
}
#if HAVE_LCMS
cmsHPROFILE
eog_image_get_profile (EogImage *img)
{
g_return_val_if_fail (EOG_IS_IMAGE (img), NULL);
return img->priv->profile;
}
#endif
GdkPixbuf*
eog_image_get_pixbuf_thumbnail (EogImage *img)
{
g_return_val_if_fail (EOG_IS_IMAGE (img), NULL);
if (img->priv->thumbnail != 0) {
g_object_ref (img->priv->thumbnail);
return img->priv->thumbnail;
}
return NULL;
}
void
eog_image_get_size (EogImage *img, int *width, int *height)
{
EogImagePrivate *priv;
g_return_if_fail (EOG_IS_IMAGE (img));
priv = img->priv;
*width = priv->width;
*height = priv->height;
}
static void
image_transform (EogImage *img, EogTransform *trans, gboolean is_undo, EogJob *job)
{
EogImagePrivate *priv;
GdkPixbuf *transformed;
gboolean modified = FALSE;
g_return_if_fail (EOG_IS_IMAGE (img));
g_return_if_fail (EOG_IS_TRANSFORM (trans));
priv = img->priv;
if (priv->image != NULL) {
transformed = eog_transform_apply (trans, priv->image, job);
g_object_unref (priv->image);
priv->image = transformed;
priv->width = gdk_pixbuf_get_width (transformed);
priv->height = gdk_pixbuf_get_height (transformed);
modified = TRUE;
}
if (priv->thumbnail != NULL) {
transformed = eog_transform_apply (trans, priv->thumbnail, job);
g_object_unref (priv->thumbnail);
priv->thumbnail = transformed;
modified = TRUE;
}
if (modified) {
priv->modified = TRUE;
#if HAVE_EXIF
update_exif_data (img);
#endif
}
if (priv->trans == NULL) {
g_object_ref (trans);
priv->trans = trans;
}
else {
EogTransform *composition;
composition = eog_transform_compose (priv->trans, trans);
g_object_unref (priv->trans);
priv->trans = composition;
}
if (!is_undo) {
g_object_ref (trans);
priv->undo_stack = g_list_prepend (priv->undo_stack, trans);
}
}
/* Public API for doing image transformations */
void
eog_image_transform (EogImage *img, EogTransform *trans, EogJob *job)
{
image_transform (img, trans, FALSE, job);
}
void
eog_image_undo (EogImage *img)
{
EogImagePrivate *priv;
EogTransform *trans;
EogTransform *inverse;
g_return_if_fail (EOG_IS_IMAGE (img));
priv = img->priv;
if (priv->undo_stack != NULL) {
trans = EOG_TRANSFORM (priv->undo_stack->data);
inverse = eog_transform_reverse (trans);
image_transform (img, inverse, TRUE, NULL);
priv->undo_stack = g_list_delete_link (priv->undo_stack, priv->undo_stack);
g_object_unref (trans);
g_object_unref (inverse);
if (eog_transform_is_identity (priv->trans)) {
g_object_unref (priv->trans);
priv->trans = NULL;
}
}
priv->modified = (priv->undo_stack != NULL);
}
static char*
tmp_file_get_path (void)
{
char *tmp_file;
int fd;
tmp_file = g_build_filename (g_get_tmp_dir (), "eog-save-XXXXXX", NULL);
fd = g_mkstemp (tmp_file);
if (fd == -1) {
/* error case */
g_free (tmp_file);
tmp_file = NULL;
}
else {
close (fd);
}
return tmp_file;
}
static gboolean
tmp_file_move_to_uri (const char* tmpfile, const GnomeVFSURI *uri, gboolean overwrite, GError **error)
{
GnomeVFSResult result;
GnomeVFSURI *source_uri;
GnomeVFSFileInfo *info;
GnomeVFSXferOverwriteMode overwrt_mode = GNOME_VFS_XFER_OVERWRITE_MODE_ABORT;
if (!overwrite && gnome_vfs_uri_exists ((GnomeVFSURI*) uri))
{
/* explicit check if uri exists, seems that gnome_vfs_xfer_uri, doesn't
* work as expected
*/
g_set_error (error, EOG_IMAGE_ERROR,
EOG_IMAGE_ERROR_FILE_EXISTS,
_("File exists"));
return FALSE;
}
info = gnome_vfs_file_info_new ();
result = gnome_vfs_get_file_info_uri ((GnomeVFSURI*) uri, info, GNOME_VFS_FILE_INFO_DEFAULT);
if (result != GNOME_VFS_OK) {
/* we don't propagate the error here, because if we get a
* fatal error, the xfer_uri will fail too and then
* handled.
*/
gnome_vfs_file_info_unref (info);
info = NULL;
}
if (overwrite == TRUE) {
overwrt_mode = GNOME_VFS_XFER_OVERWRITE_MODE_REPLACE;
}
source_uri = gnome_vfs_uri_new (tmpfile);
result = gnome_vfs_xfer_uri (source_uri,
uri,
GNOME_VFS_XFER_DELETE_ITEMS, /* delete source file */
GNOME_VFS_XFER_ERROR_MODE_ABORT, /* abort on all errors */
overwrt_mode,
NULL, /* no progress callback */
NULL);
gnome_vfs_uri_unref (source_uri);
if (result == GNOME_VFS_ERROR_FILE_EXISTS) {
g_set_error (error, EOG_IMAGE_ERROR,
EOG_IMAGE_ERROR_FILE_EXISTS,
gnome_vfs_result_to_string (result));
}
else if (result != GNOME_VFS_OK) {
g_set_error (error, EOG_IMAGE_ERROR,
EOG_IMAGE_ERROR_VFS,
gnome_vfs_result_to_string (result));
}
else if (info != NULL) {
/* reset file permissions/owner to the original ones */
GnomeVFSSetFileInfoMask mask =
GNOME_VFS_SET_FILE_INFO_PERMISSIONS | GNOME_VFS_SET_FILE_INFO_OWNER;
gnome_vfs_set_file_info_uri ((GnomeVFSURI*) uri, info, mask);
}
if (info != NULL) {
gnome_vfs_file_info_unref (info);
}
return (result == GNOME_VFS_OK);
}
static gboolean
tmp_file_delete (char *tmpfile)
{
if (tmpfile == NULL) return FALSE;
if (g_file_test (tmpfile, G_FILE_TEST_EXISTS)) {
int result;
result = unlink (tmpfile);
if (result == -1) {
g_warning ("Couldn't delete temporary file: %s\n", tmpfile);
return FALSE;
}
}
return TRUE;
}
static void
eog_image_reset_modifications (EogImage *image)
{
EogImagePrivate *priv;
GList *it = NULL;
g_return_if_fail (EOG_IS_IMAGE (image));
priv = image->priv;
/* free the undo stack */
for (it = priv->undo_stack; it != NULL; it = it->next) {
g_object_unref (G_OBJECT (it->data));
}
g_list_free (priv->undo_stack);
priv->undo_stack = NULL;
/* free accumulated transform object */
if (priv->trans != NULL) {
g_object_unref (priv->trans);
priv->trans = NULL;
}
priv->modified = FALSE;
}
static void
eog_image_link_with_target (EogImage *image, EogImageSaveInfo *target)
{
EogImagePrivate *priv;
g_return_if_fail (EOG_IS_IMAGE (image));
g_return_if_fail (EOG_IS_IMAGE_SAVE_INFO (target));
priv = image->priv;
/* update file location */
if (priv->uri != NULL) {
gnome_vfs_uri_unref (priv->uri);
}
priv->uri = gnome_vfs_uri_ref (target->uri);
/* Clear caption and caption key, these will be
* updated on next eog_image_get_caption call.
*/
if (priv->caption != NULL) {
g_free (priv->caption);
priv->caption = NULL;
}
if (priv->caption_key != NULL) {
g_free (priv->caption_key);
priv->caption_key = NULL;
}
/* update file format */
if (priv->file_type != NULL) {
g_free (priv->file_type);
}
priv->file_type = g_strdup (target->format);
}
gboolean
eog_image_save_by_info (EogImage *img, EogImageSaveInfo *source, EogJob *job, GError **error)
{
EogImagePrivate *priv;
gboolean success = FALSE;
char *tmpfile;
g_return_val_if_fail (EOG_IS_IMAGE (img), FALSE);
g_return_val_if_fail (EOG_IS_IMAGE_SAVE_INFO (source), FALSE);
priv = img->priv;
/* see if we need any saving at all */
if (source->exists && !source->modified) {
return TRUE;
}
/* fail if there is no image to save */
if (priv->image == NULL) {
g_set_error (error, EOG_IMAGE_ERROR,
EOG_IMAGE_ERROR_NOT_LOADED,
_("No image loaded."));
return FALSE;
}
/* generate temporary file name */
tmpfile = tmp_file_get_path ();
if (tmpfile == NULL) {
g_set_error (error, EOG_IMAGE_ERROR,
EOG_IMAGE_ERROR_TMP_FILE_FAILED,
_("Temporary file creation failed."));
return FALSE;
}
#if HAVE_JPEG
/* determine kind of saving */
if ((g_ascii_strcasecmp (source->format, EOG_FILE_FORMAT_JPEG) == 0) &&
source->exists && source->modified)
{
success = eog_image_jpeg_save_file (img, tmpfile, source, NULL, error);
}
#endif
if (!success && (*error == NULL)) {
success = gdk_pixbuf_save (priv->image, tmpfile, source->format, error, NULL);
}
if (success) {
/* try to move result file to target uri */
success = tmp_file_move_to_uri (tmpfile, priv->uri, TRUE /*overwrite*/, error);
}
if (success) {
eog_image_reset_modifications (img);
}
tmp_file_delete (tmpfile);
g_free (tmpfile);
return success;
}
static gboolean
eog_image_copy_file (EogImageSaveInfo *source, EogImageSaveInfo *target, GError **error)
{
GnomeVFSResult result;
GnomeVFSFileInfo *info;
GnomeVFSXferOverwriteMode overwrt_mode = GNOME_VFS_XFER_OVERWRITE_MODE_ABORT;
g_return_val_if_fail (EOG_IS_IMAGE_SAVE_INFO (source), FALSE);
g_return_val_if_fail (EOG_IS_IMAGE_SAVE_INFO (target), FALSE);
if (target->overwrite != TRUE &&
gnome_vfs_uri_exists (target->uri))
{
/* explicit check if uri exists, seems that gnome_vfs_xfer_uri, doesn't
* work as expected
*/
g_set_error (error, EOG_IMAGE_ERROR,
EOG_IMAGE_ERROR_FILE_EXISTS,
_("File exists"));
return FALSE;
}
else if (target->overwrite == TRUE) {
overwrt_mode = GNOME_VFS_XFER_OVERWRITE_MODE_REPLACE;
}
info = gnome_vfs_file_info_new ();
result = gnome_vfs_get_file_info_uri ((GnomeVFSURI*) target->uri, info, GNOME_VFS_FILE_INFO_DEFAULT);
if (result != GNOME_VFS_OK) {
/* we don't propagate the error here, because if we get a
* fatal error, the xfer_uri will fail too and then
* handled.
*/
gnome_vfs_file_info_unref (info);
info = NULL;
}
result = gnome_vfs_xfer_uri (source->uri,
target->uri,
GNOME_VFS_XFER_DEFAULT, /* copy the data */
GNOME_VFS_XFER_ERROR_MODE_ABORT, /* abort on all errors */
overwrt_mode,
NULL, /* no progress callback */
NULL);
if (result == GNOME_VFS_ERROR_FILE_EXISTS) {
g_set_error (error, EOG_IMAGE_ERROR,
EOG_IMAGE_ERROR_FILE_EXISTS,
gnome_vfs_result_to_string (result));
}
else if (result != GNOME_VFS_OK) {
g_set_error (error, EOG_IMAGE_ERROR,
EOG_IMAGE_ERROR_VFS,
gnome_vfs_result_to_string (result));
}
else if (info != NULL) {
/* reset file permissions/owner to the original ones */
GnomeVFSSetFileInfoMask mask =
GNOME_VFS_SET_FILE_INFO_PERMISSIONS | GNOME_VFS_SET_FILE_INFO_OWNER;
gnome_vfs_set_file_info_uri (target->uri, info, mask);
}
if (info != NULL) {
gnome_vfs_file_info_unref (info);
}
return (result == GNOME_VFS_OK);
}
gboolean
eog_image_save_as_by_info (EogImage *img, EogImageSaveInfo *source, EogImageSaveInfo *target, EogJob *job, GError **error)
{
EogImagePrivate *priv;
gboolean success = FALSE;
char *tmpfile;
gboolean direct_copy = FALSE;
g_return_val_if_fail (EOG_IS_IMAGE (img), FALSE);
g_return_val_if_fail (EOG_IS_IMAGE_SAVE_INFO (source), FALSE);
g_return_val_if_fail (EOG_IS_IMAGE_SAVE_INFO (target), FALSE);
priv = img->priv;
/* fail if there is no image to save */
if (priv->image == NULL) {
g_set_error (error, EOG_IMAGE_ERROR,
EOG_IMAGE_ERROR_NOT_LOADED,
_("No image loaded."));
return FALSE;
}
/* generate temporary file name */
tmpfile = tmp_file_get_path ();
if (tmpfile == NULL) {
g_set_error (error, EOG_IMAGE_ERROR,
EOG_IMAGE_ERROR_TMP_FILE_FAILED,
_("Temporary file creation failed."));
return FALSE;
}
/* determine kind of saving */
if (g_ascii_strcasecmp (source->format, target->format) == 0 && !source->modified) {
success = eog_image_copy_file (source, target, error);
direct_copy = success;
}
#if HAVE_JPEG
else if ((g_ascii_strcasecmp (source->format, EOG_FILE_FORMAT_JPEG) == 0 && source->exists) ||
(g_ascii_strcasecmp (target->format, EOG_FILE_FORMAT_JPEG) == 0))
{
success = eog_image_jpeg_save_file (img, tmpfile, source, target, error);
}
#endif
if (!success && (*error == NULL)) {
success = gdk_pixbuf_save (priv->image, tmpfile, target->format, error, NULL);
}
if (success && !direct_copy) { /* not required if we alredy copied the file directly */
/* try to move result file to target uri */
success = tmp_file_move_to_uri (tmpfile, target->uri, target->overwrite, error);
}
if (success) {
/* update image information to new uri */
eog_image_reset_modifications (img);
eog_image_link_with_target (img, target);
}
tmp_file_delete (tmpfile);
g_free (tmpfile);
return success;
}
/*
* This function is extracted from
* File: nautilus/libnautilus-private/nautilus-file.c
* Revision: 1.309
* Author: Darin Adler <darin@bentspoon.com>
*/
static gboolean
have_broken_filenames (void)
{
static gboolean initialized = FALSE;
static gboolean broken;
if (initialized) {
return broken;
}
broken = g_getenv ("G_BROKEN_FILENAMES") != NULL;
initialized = TRUE;
return broken;
}
/*
* This function is inspired by
* nautilus/libnautilus-private/nautilus-file.c:nautilus_file_get_display_name_nocopy
* Revision: 1.309
* Author: Darin Adler <darin@bentspoon.com>
*/
gchar*
eog_image_get_caption (EogImage *img)
{
EogImagePrivate *priv;
char *name;
char *utf8_name;
gboolean validated = FALSE;
gboolean broken_filenames;
g_return_val_if_fail (EOG_IS_IMAGE (img), NULL);
priv = img->priv;
if (priv->uri == NULL) return NULL;
if (priv->caption != NULL)
/* Use cached caption string */
return priv->caption;
/* g_print ("priv->uri: %p\n", priv->uri);
g_print ("%s\n", gnome_vfs_uri_to_string (priv->uri, GNOME_VFS_URI_HIDE_NONE)); */
name = gnome_vfs_uri_extract_short_name (priv->uri);
if (name != NULL && gnome_vfs_uri_is_local (priv->uri)) {
/* Support the G_BROKEN_FILENAMES feature of
* glib by using g_filename_to_utf8 to convert
* local filenames to UTF-8. Also do the same
* thing with any local filename that does not
* validate as good UTF-8.
*/
broken_filenames = have_broken_filenames ();
if (broken_filenames || !g_utf8_validate (name, -1, NULL)) {
utf8_name = g_locale_to_utf8 (name, -1, NULL, NULL, NULL);
if (utf8_name != NULL) {
g_free (name);
name = utf8_name;
/* Guaranteed to be correct utf8 here */
validated = TRUE;
}
}
else if (!broken_filenames) {
/* name was valid, no need to re-validate */
validated = TRUE;
}
}
if (!validated && !g_utf8_validate (name, -1, NULL)) {
if (name == NULL) {
name = g_strdup ("[Invalid Unicode]");
}
else {
utf8_name = eog_util_make_valid_utf8 (name);
g_free (name);
name = utf8_name;
}
}
priv->caption = name;
if (priv->caption == NULL) {
char *short_str;
short_str = gnome_vfs_uri_extract_short_name (priv->uri);
if (g_utf8_validate (short_str, -1, NULL)) {
priv->caption = g_strdup (short_str);
}
else {
priv->caption = g_filename_to_utf8 (short_str, -1, NULL, NULL, NULL);
}
g_free (short_str);
}
return priv->caption;
}
void
eog_image_free_mem_private (EogImage *image)
{
EogImagePrivate *priv;
priv = image->priv;
if (priv->status == EOG_IMAGE_STATUS_LOADING) {
eog_image_cancel_load (image);
}
else {
if (priv->image != NULL) {
gdk_pixbuf_unref (priv->image);
priv->image = NULL;
}
#if HAVE_EXIF
if (priv->exif != NULL) {
exif_data_unref (priv->exif);
priv->exif = NULL;
}
#endif
if (priv->exif_chunk != NULL) {
g_free (priv->exif_chunk);
priv->exif_chunk = NULL;
}
priv->exif_chunk_len = 0;
#if HAVE_LCMS
if (priv->profile != NULL) {
cmsCloseProfile (priv->profile);
}
#endif
priv->status = EOG_IMAGE_STATUS_UNKNOWN;
}
}
const gchar*
eog_image_get_collate_key (EogImage *img)
{
EogImagePrivate *priv;
g_return_val_if_fail (EOG_IS_IMAGE (img), NULL);
priv = img->priv;
if (priv->caption_key == NULL) {
char *caption;
caption = eog_image_get_caption (img);
priv->caption_key = g_utf8_collate_key (caption, -1);
}
return priv->caption_key;
}
void
eog_image_cancel_load (EogImage *img)
{
EogImagePrivate *priv;
g_return_if_fail (EOG_IS_IMAGE (img));
priv = img->priv;
g_mutex_lock (priv->status_mutex);
if (priv->status == EOG_IMAGE_STATUS_LOADING) {
priv->cancel_loading = TRUE;
}
g_mutex_unlock (priv->status_mutex);
}
gboolean
eog_image_has_metadata (EogImage *img)
{
EogImagePrivate *priv;
gboolean has_metadata;
g_return_val_if_fail (EOG_IS_IMAGE (img), FALSE);
priv = img->priv;
has_metadata = ((priv->exif_chunk != NULL) || (priv->iptc_chunk != NULL));
#if HAVE_EXIF
has_metadata |= (priv->exif != NULL);
#endif
return has_metadata;
}
gpointer
eog_image_get_exif_information (EogImage *img)
{
EogImagePrivate *priv;
gpointer data = NULL;
g_return_val_if_fail (EOG_IS_IMAGE (img), NULL);
priv = img->priv;
#if HAVE_EXIF
g_mutex_lock (priv->status_mutex);
exif_data_ref (priv->exif);
data = priv->exif;
g_mutex_unlock (priv->status_mutex);
#endif
return data;
}
gboolean
eog_image_is_loaded (EogImage *img)
{
EogImagePrivate *priv;
gboolean result;
g_return_val_if_fail (EOG_IS_IMAGE (img), FALSE);
priv = img->priv;
g_mutex_lock (priv->status_mutex);
result = (priv->status == EOG_IMAGE_STATUS_LOADED);
g_mutex_unlock (priv->status_mutex);
return result;
}
GnomeVFSURI*
eog_image_get_uri (EogImage *img)
{
g_return_val_if_fail (EOG_IS_IMAGE (img), NULL);
return gnome_vfs_uri_ref (img->priv->uri);
}
typedef struct {
EogInfoViewFile *view;
EogImage *img;
} VfsFileCbContext;
static gchar* temp = NULL;
static gboolean had_create = FALSE, had_delete = FALSE;
static void
vfs_monitor_dir_cb (GnomeVFSMonitorHandle *handle,
const gchar *monitor_uri,
const gchar *info_uri,
GnomeVFSMonitorEventType event_type,
gpointer user_data)
{
VfsFileCbContext *ctx = user_data;
gchar *monitor_ptr = (gchar *) monitor_uri;
gchar *info_ptr = (gchar *) info_uri;
monitor_ptr += 7;
info_ptr += 7;
g_return_if_fail (EOG_IS_IMAGE (ctx->img));
g_return_if_fail (EOG_IS_INFO_VIEW_FILE (ctx->view));
if (strcmp (gnome_vfs_uri_extract_dirname (ctx->img->priv->uri), monitor_ptr) == 0 &&
strcmp (gnome_vfs_uri_get_path (ctx->img->priv->uri), info_ptr) == 0 &&
event_type == GNOME_VFS_MONITOR_EVENT_DELETED) {
had_delete = TRUE;
}
if (strcmp (gnome_vfs_uri_extract_dirname (ctx->img->priv->uri), monitor_ptr) == 0 &&
event_type == GNOME_VFS_MONITOR_EVENT_CREATED) {
if (temp)
g_free (temp);
had_create = TRUE;
temp = g_strdup (info_uri);
}
}
static void
vfs_monitor_file_cb (GnomeVFSMonitorHandle *handle,
const gchar *monitor_uri,
const gchar *info_uri,
GnomeVFSMonitorEventType event_type,
gpointer user_data)
{
VfsFileCbContext *ctx = user_data;
g_return_if_fail (EOG_IS_IMAGE (ctx->img));
g_return_if_fail (EOG_IS_INFO_VIEW_FILE (ctx->view));
if (had_delete && had_create)
{
if (ctx->img->priv->caption)
g_free (ctx->img->priv->caption);
ctx->img->priv->caption = NULL;
gnome_vfs_uri_unref (ctx->img->priv->uri);
ctx->img->priv->uri = gnome_vfs_uri_new (temp);
eog_info_view_file_show_data (ctx->view, ctx->img);
/* g_print ("Rename detected to %s -- %s\n",
temp, eog_image_get_caption (ctx->img)); */
gnome_vfs_monitor_add (&ctx->img->priv->monitor_handle,
gnome_vfs_uri_get_path (ctx->img->priv->uri),
GNOME_VFS_MONITOR_FILE,
vfs_monitor_file_cb,
ctx);
}
had_create = FALSE;
had_delete = FALSE;
return;
}
void
eog_image_register_infoview (EogImage *img, gpointer view)
{
VfsFileCbContext *ctx = g_new0 (VfsFileCbContext, 1);
ctx->img = img;
ctx->view = view;
g_return_if_fail (EOG_IS_INFO_VIEW_FILE (ctx->view));
g_return_if_fail (EOG_IS_IMAGE (ctx->img));
gnome_vfs_monitor_add (&ctx->img->priv->monitor_handle,
gnome_vfs_uri_extract_dirname (ctx->img->priv->uri),
GNOME_VFS_MONITOR_DIRECTORY,
vfs_monitor_dir_cb,
ctx);
gnome_vfs_monitor_add (&ctx->img->priv->monitor_handle,
gnome_vfs_uri_get_path (ctx->img->priv->uri),
GNOME_VFS_MONITOR_FILE,
vfs_monitor_file_cb,
ctx);
img->priv->toclean = ctx;
}
gboolean
eog_image_is_modified (EogImage *img)
{
g_return_val_if_fail (EOG_IS_IMAGE (img), FALSE);
return img->priv->modified;
}
GnomeVFSFileSize
eog_image_get_bytes (EogImage *img)
{
g_return_val_if_fail (EOG_IS_IMAGE (img), 0);
return img->priv->bytes;
}
void
eog_image_modified (EogImage *img)
{
g_return_if_fail (EOG_IS_IMAGE (img));
g_signal_emit (G_OBJECT (img), eog_image_signals [SIGNAL_IMAGE_CHANGED], 0);
}
gchar*
eog_image_get_uri_for_display (EogImage *img)
{
EogImagePrivate *priv;
gchar *uri_str = NULL;
gchar *str = NULL;
g_return_val_if_fail (EOG_IS_IMAGE (img), NULL);
priv = img->priv;
if (priv->uri != NULL) {
uri_str = gnome_vfs_uri_to_string (priv->uri, GNOME_VFS_URI_HIDE_NONE);
if (uri_str != NULL) {
str = gnome_vfs_format_uri_for_display (uri_str);
g_free (uri_str);
}
}
return str;
}
EogImage*
eog_image_data_ref (EogImage *img)
{
g_return_val_if_fail (EOG_IS_IMAGE (img), NULL);
g_object_ref (G_OBJECT (img));
img->priv->data_ref_count++;
g_assert (img->priv->data_ref_count <= G_OBJECT (img)->ref_count);
return img;
}
EogImage*
eog_image_data_unref (EogImage *img)
{
g_return_val_if_fail (EOG_IS_IMAGE (img), NULL);
if (img->priv->data_ref_count > 0) {
img->priv->data_ref_count--;
}
else {
g_warning ("More image data unrefs than refs.");
}
if (img->priv->data_ref_count == 0) {
eog_image_free_mem_private (img);
}
g_object_unref (G_OBJECT (img));
g_assert (img->priv->data_ref_count <= G_OBJECT (img)->ref_count);
return img;
}
/* Print API */
void
eog_image_print (EogImage *img, GnomePrintContext *context, gdouble paper_width, gdouble paper_height)
{
EogImagePrivate *priv = NULL;
GdkPixbuf *printed_image = NULL;
gint pix_width;
gint pix_height;
gdouble width, height;
g_return_if_fail (EOG_IS_IMAGE (img));
priv = img->priv;
g_return_if_fail (GDK_IS_PIXBUF (priv->image));
if (gdk_pixbuf_get_width (priv->image) > gdk_pixbuf_get_height (priv->image)) {
printed_image = gdk_pixbuf_rotate_simple (priv->image,
GDK_PIXBUF_ROTATE_COUNTERCLOCKWISE);
} else {
gdk_pixbuf_ref (priv->image);
printed_image = priv->image;
}
pix_width = gdk_pixbuf_get_width (printed_image);
pix_height = gdk_pixbuf_get_height (printed_image);
width = (gint)paper_width;
height = (gint)paper_height;
if (((gdouble) pix_height/pix_width) >
((gdouble)width/height)) {
/* We scale to the top */
width = height * (gdouble)pix_width/pix_height;
} else {
/* We scale to the sides of the page */
height = width * (gdouble)pix_height/pix_width;
}
width -= 40;
height -= 40;
gnome_print_translate (context, (paper_width - width)/2.0, (paper_height - height)/2.0);
gnome_print_scale (context, width, height);
gnome_print_rgbimage (context, gdk_pixbuf_get_pixels (printed_image),
pix_width, pix_height, gdk_pixbuf_get_rowstride (printed_image));
g_object_unref (G_OBJECT (printed_image));
}