mirror of
https://gitlab.gnome.org/GNOME/eog
synced 2024-10-19 14:34:42 +00:00
343d369459
2006-02-19 Lucas Rocha <lucasr@gnome.org> * libeog/eog-image-list.c, libeog/eog-image.c, libeog/eog-info-view.c, libeog/eog-job-manager.c, libeog/eog-job.c, shell/eog-window.c: disable all debug messages by default (Fixes bug #331362).
2015 lines
47 KiB
C
2015 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>
|
|
#ifdef 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"
|
|
#ifdef HAVE_JPEG
|
|
#include "eog-image-jpeg.h"
|
|
#endif
|
|
|
|
#ifdef 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 65535
|
|
|
|
|
|
/*======================================
|
|
|
|
EogImage implementation
|
|
|
|
------------------------------------*/
|
|
|
|
G_DEFINE_TYPE (EogImage, eog_image, 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_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;
|
|
#ifdef HAVE_EXIF
|
|
priv->exif = NULL;
|
|
#endif
|
|
#ifdef 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;
|
|
}
|
|
|
|
#ifdef 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, guint 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 */
|
|
#ifdef 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;
|
|
}
|
|
|
|
#ifdef 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;
|
|
#ifdef 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;
|
|
}
|
|
}
|
|
#ifdef 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) {
|
|
#ifdef 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);
|
|
}
|
|
#ifdef 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
|
|
#ifdef 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;
|
|
|
|
#ifdef DEBUG
|
|
g_print ("eog-image_load.c\n");
|
|
#endif
|
|
|
|
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);
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
g_print ("load success: %i\n", success);
|
|
#endif
|
|
|
|
/* 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;
|
|
}
|
|
|
|
#ifdef 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;
|
|
#ifdef 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;
|
|
}
|
|
|
|
#ifdef 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;
|
|
}
|
|
|
|
#ifdef 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 && g_ascii_strcasecmp (priv->uri->method_string, "file") == 0) {
|
|
/* 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) {
|
|
g_object_unref (priv->image);
|
|
priv->image = NULL;
|
|
}
|
|
|
|
#ifdef 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;
|
|
|
|
#ifdef 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));
|
|
|
|
#ifdef 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;
|
|
|
|
#ifdef 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 {
|
|
g_object_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));
|
|
}
|
|
|