1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 
3 /*
4  *  GThumb
5  *
6  *  Copyright (C) 2001-2008 The Free Software Foundation, Inc.
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2 of the License, or
11  *  (at your option) any later version.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
20  */
21 
22 #include <config.h>
23 #include <glib/gi18n.h>
24 #include <gdk-pixbuf/gdk-pixbuf.h>
25 #include <gtk/gtk.h>
26 #include "glib-utils.h"
27 #include "gth-file-data.h"
28 #include "gth-image-loader.h"
29 #include "gth-main.h"
30 
31 
32 struct _GthImageLoaderPrivate {
33 	gboolean           as_animation;  /* Whether to load the image in a
34 				           * GdkPixbufAnimation structure. */
35 	GthImageFormat     preferred_format;
36 	GthICCProfile     *out_profile;
37 	GthImageLoaderFunc loader_func;
38 	gpointer           loader_data;
39 };
40 
41 
G_DEFINE_TYPE_WITH_CODE(GthImageLoader,gth_image_loader,G_TYPE_OBJECT,G_ADD_PRIVATE (GthImageLoader))42 G_DEFINE_TYPE_WITH_CODE (GthImageLoader,
43 			 gth_image_loader,
44 			 G_TYPE_OBJECT,
45 			 G_ADD_PRIVATE (GthImageLoader))
46 
47 
48 static void
49 gth_image_loader_finalize (GObject *object)
50 {
51 	GthImageLoader *self = GTH_IMAGE_LOADER (object);
52 
53 	_g_object_unref (self->priv->out_profile);
54 
55 	G_OBJECT_CLASS (gth_image_loader_parent_class)->finalize (object);
56 }
57 
58 
59 static void
gth_image_loader_class_init(GthImageLoaderClass * class)60 gth_image_loader_class_init (GthImageLoaderClass *class)
61 {
62 	GObjectClass *object_class;
63 
64 	object_class = G_OBJECT_CLASS (class);
65 	object_class->finalize = gth_image_loader_finalize;
66 
67 }
68 
69 
70 static void
gth_image_loader_init(GthImageLoader * self)71 gth_image_loader_init (GthImageLoader *self)
72 {
73 	self->priv = gth_image_loader_get_instance_private (self);
74 	self->priv->as_animation = FALSE;
75 	self->priv->loader_func = NULL;
76 	self->priv->loader_data = NULL;
77 	self->priv->preferred_format = GTH_IMAGE_FORMAT_CAIRO_SURFACE;
78 	self->priv->out_profile = NULL;
79 }
80 
81 
82 GthImageLoader *
gth_image_loader_new(GthImageLoaderFunc loader_func,gpointer loader_data)83 gth_image_loader_new (GthImageLoaderFunc loader_func,
84 		      gpointer           loader_data)
85 {
86 	GthImageLoader *self;
87 
88 	self = g_object_new (GTH_TYPE_IMAGE_LOADER, NULL);
89 	gth_image_loader_set_loader_func (self, loader_func, loader_data);
90 
91 	return self;
92 }
93 
94 
95 void
gth_image_loader_set_loader_func(GthImageLoader * self,GthImageLoaderFunc loader_func,gpointer loader_data)96 gth_image_loader_set_loader_func (GthImageLoader     *self,
97 				  GthImageLoaderFunc  loader_func,
98 				  gpointer            loader_data)
99 {
100 	g_return_if_fail (self != NULL);
101 
102 	self->priv->loader_func = loader_func;
103 	self->priv->loader_data = loader_data;
104 }
105 
106 
107 void
gth_image_loader_set_preferred_format(GthImageLoader * self,GthImageFormat preferred_format)108 gth_image_loader_set_preferred_format (GthImageLoader *self,
109 				       GthImageFormat  preferred_format)
110 {
111 	g_return_if_fail (self != NULL);
112 	self->priv->preferred_format = preferred_format;
113 }
114 
115 
116 void
gth_image_loader_set_out_profile(GthImageLoader * self,GthICCProfile * out_profile)117 gth_image_loader_set_out_profile (GthImageLoader *self,
118 				  GthICCProfile  *out_profile)
119 {
120 	g_return_if_fail (self != NULL);
121 
122 	_g_object_ref (out_profile);
123 	_g_object_unref (self->priv->out_profile);
124 	self->priv->out_profile = out_profile;
125 }
126 
127 
128 /* -- gth_image_loader_load -- */
129 
130 
131 typedef struct {
132 	GthFileData  *file_data;
133 	int           requested_size;
134 	GthImage     *image;
135 	int           original_width;
136 	int           original_height;
137 } LoaderOptions;
138 
139 
140 static LoaderOptions *
loader_options_new(GthFileData * file_data,int requested_size)141 loader_options_new (GthFileData  *file_data,
142 	            int           requested_size)
143 {
144 	LoaderOptions *load_data;
145 
146 	load_data = g_new0 (LoaderOptions, 1);
147 	load_data->file_data = _g_object_ref (file_data);
148 	load_data->requested_size = requested_size;
149 
150 	return load_data;
151 }
152 
153 
154 static void
loader_options_free(LoaderOptions * options)155 loader_options_free (LoaderOptions *options)
156 {
157 	_g_object_unref (options->file_data);
158 	g_free (options);
159 }
160 
161 
162 typedef struct {
163 	GthImage *image;
164 	int       original_width;
165 	int       original_height;
166 	gboolean  loaded_original;
167 } LoaderResult;
168 
169 
170 static LoaderResult *
loader_result_new(void)171 loader_result_new (void)
172 {
173 	LoaderResult *result;
174 
175 	result = g_new0 (LoaderResult, 1);
176 	result->image = NULL;
177 	result->original_width = -1;
178 	result->original_height = -1;
179 	result->loaded_original = FALSE;
180 
181 	return result;
182 }
183 
184 
185 static void
loader_result_free(LoaderResult * result)186 loader_result_free (LoaderResult *result)
187 {
188 	_g_object_unref (result->image);
189 	g_free (result);
190 }
191 
192 
193 static void
load_image_thread(GTask * task,gpointer source_object,gpointer task_data,GCancellable * cancellable)194 load_image_thread (GTask        *task,
195                    gpointer      source_object,
196 		   gpointer      task_data,
197 		   GCancellable *cancellable)
198 {
199 	GthImageLoader *self = GTH_IMAGE_LOADER (source_object);
200 	LoaderOptions  *options;
201 	int             original_width;
202 	int             original_height;
203 	gboolean        loaded_original;
204 	GInputStream   *istream;
205 	GthImage       *image = NULL;
206 	GError         *error = NULL;
207 	LoaderResult   *result;
208 
209 	options = g_task_get_task_data (task);
210 	original_width = -1;
211 	original_height = -1;
212 
213 	istream = (GInputStream *) g_file_read (options->file_data->file, cancellable, &error);
214 	if (istream == NULL) {
215 		g_task_return_error (task, error);
216 		return;
217 	}
218 
219 	loaded_original = TRUE;
220 
221 	if (self->priv->loader_func != NULL) {
222 		image = (*self->priv->loader_func) (istream,
223 						    options->file_data,
224 						    options->requested_size,
225 						    &original_width,
226 						    &original_height,
227 						    &loaded_original,
228 						    self->priv->loader_data,
229 						    cancellable,
230 						    &error);
231 	}
232 	else  {
233 		GthImageLoaderFunc  loader_func = NULL;
234 		const char         *mime_type;
235 
236 		mime_type = _g_content_type_get_from_stream (istream, options->file_data->file, cancellable, NULL);
237 		if (mime_type != NULL)
238 			loader_func = gth_main_get_image_loader_func (mime_type, self->priv->preferred_format);
239 
240 		if (loader_func != NULL)
241 			image = loader_func (istream,
242 					     options->file_data,
243 					     options->requested_size,
244 					     &original_width,
245 					     &original_height,
246 					     &loaded_original,
247 					     NULL,
248 					     cancellable,
249 					     &error);
250 		else
251 			error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, _("No suitable loader available for this file type"));
252 	}
253 
254 	_g_object_unref (istream);
255 
256 	if ((image != NULL)
257 	    && ! g_cancellable_is_cancelled (cancellable)
258 	    && (self->priv->out_profile != NULL)
259 	    && gth_image_get_icc_profile (image) != NULL)
260 	{
261 		gth_image_apply_icc_profile (image, self->priv->out_profile, cancellable);
262 	}
263 
264 	if (g_cancellable_is_cancelled (cancellable)) {
265 		_g_clear_object (&image);
266 		g_clear_error (&error);
267 		g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_CANCELLED, "");
268 	}
269 
270 	if (error != NULL) {
271 		if ((image == NULL) || gth_image_get_is_null (image)) {
272 			_g_object_unref (image);
273 			g_task_return_error (task, error);
274 			return;
275 		}
276 
277 		/* ignore the error if the image is not null. */
278 		g_clear_error (&error);
279 	}
280 
281 	result = loader_result_new ();
282 	result->image = image;
283 	result->original_width = original_width;
284 	result->original_height = original_height;
285 	result->loaded_original = loaded_original;
286 	g_task_return_pointer (task, result, (GDestroyNotify) loader_result_free);
287 }
288 
289 
290 void
gth_image_loader_load(GthImageLoader * loader,GthFileData * file_data,int requested_size,int io_priority,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)291 gth_image_loader_load (GthImageLoader      *loader,
292 		       GthFileData         *file_data,
293 		       int                  requested_size,
294 		       int                  io_priority,
295 		       GCancellable        *cancellable,
296 		       GAsyncReadyCallback  callback,
297 		       gpointer             user_data)
298 {
299 	GTask *task;
300 
301 	task = g_task_new (G_OBJECT (loader), cancellable, callback, user_data);
302 	g_task_set_task_data (task,
303 			      loader_options_new (file_data, requested_size),
304 			      (GDestroyNotify) loader_options_free);
305 	g_task_run_in_thread (task, load_image_thread);
306 
307 	g_object_unref (task);
308 }
309 
310 
311 gboolean
gth_image_loader_load_finish(GthImageLoader * loader,GAsyncResult * task,GthImage ** image,int * original_width,int * original_height,gboolean * loaded_original,GError ** error)312 gth_image_loader_load_finish (GthImageLoader   *loader,
313 			      GAsyncResult     *task,
314 			      GthImage        **image,
315 			      int              *original_width,
316 			      int              *original_height,
317 			      gboolean         *loaded_original,
318 			      GError         **error)
319 {
320 	  LoaderResult *result;
321 
322 	  g_return_val_if_fail (g_task_is_valid (G_TASK (task), G_OBJECT (loader)), FALSE);
323 
324 	  result = g_task_propagate_pointer (G_TASK (task), error);
325 	  if (result == NULL)
326 		  return FALSE;
327 
328 	  if (image != NULL)
329 		  *image = _g_object_ref (result->image);
330 	  if (original_width != NULL)
331 	  	  *original_width = result->original_width;
332 	  if (original_height != NULL)
333 	  	  *original_height = result->original_height;
334 	  if (loaded_original != NULL)
335 	  	  *loaded_original = result->loaded_original;
336 
337 	  loader_result_free (result);
338 
339 	  return TRUE;
340 }
341 
342 
343 GthImage *
gth_image_new_from_stream(GInputStream * istream,int requested_size,int * p_original_width,int * p_original_height,GCancellable * cancellable,GError ** p_error)344 gth_image_new_from_stream (GInputStream  *istream,
345 			   int            requested_size,
346 			   int           *p_original_width,
347 			   int           *p_original_height,
348 			   GCancellable  *cancellable,
349 			   GError       **p_error)
350 {
351 	const char         *mime_type;
352 	GthImageLoaderFunc  loader_func;
353 	GthImage           *image;
354 	int                 original_width;
355 	int                 original_height;
356 	GError             *error = NULL;
357 
358 	image = NULL;
359 	mime_type = _g_content_type_get_from_stream (istream, NULL, cancellable, &error);
360 	if (mime_type != NULL) {
361 		loader_func = gth_main_get_image_loader_func (mime_type, GTH_IMAGE_FORMAT_CAIRO_SURFACE);
362 		if (loader_func != NULL)
363 			image = loader_func (istream,
364 					     NULL,
365 					     requested_size,
366 					     &original_width,
367 					     &original_height,
368 					     NULL,
369 					     NULL,
370 					     cancellable,
371 					     &error);
372 	}
373 
374 	if ((image == NULL) && (error == NULL))
375 		error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, _("No suitable loader available for this file type"));
376 
377 	if (p_error != NULL)
378 		*p_error = error;
379 	else
380 		_g_error_free (error);
381 
382 	if (p_original_width != NULL)
383 		*p_original_width = original_width;
384 	if (p_original_height != NULL)
385 		*p_original_height = original_height;
386 
387 	return image;
388 }
389