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