1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
3 /*
4 * GThumb
5 *
6 * Copyright (C) 2011 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 #define GDK_PIXBUF_ENABLE_BACKEND 1
23
24 #include <config.h>
25 #include <glib.h>
26 #include <gdk-pixbuf/gdk-pixbuf.h>
27 #ifdef HAVE_LCMS2
28 #include <lcms2.h>
29 #endif /* HAVE_LCMS2 */
30 #include "cairo-utils.h"
31 #include "glib-utils.h"
32 #include "gth-image.h"
33 #include "gth-main.h"
34 #include "pixbuf-utils.h"
35
36
37 /* -- GthImage -- */
38
39
40 struct _GthImagePrivate {
41 GthImageFormat format;
42 union {
43 cairo_surface_t *surface;
44 GdkPixbuf *pixbuf;
45 GdkPixbufAnimation *pixbuf_animation;
46 } data;
47 GthICCProfile *icc_data;
48 };
49
50
G_DEFINE_TYPE_WITH_CODE(GthImage,gth_image,G_TYPE_OBJECT,G_ADD_PRIVATE (GthImage))51 G_DEFINE_TYPE_WITH_CODE (GthImage,
52 gth_image,
53 G_TYPE_OBJECT,
54 G_ADD_PRIVATE (GthImage))
55
56
57 static void
58 _gth_image_free_data (GthImage *self)
59 {
60 switch (self->priv->format) {
61 case GTH_IMAGE_FORMAT_CAIRO_SURFACE:
62 cairo_surface_destroy (self->priv->data.surface);
63 self->priv->data.surface = NULL;
64 break;
65
66 case GTH_IMAGE_FORMAT_GDK_PIXBUF:
67 _g_object_unref (self->priv->data.pixbuf);
68 self->priv->data.pixbuf = NULL;
69 break;
70
71 case GTH_IMAGE_FORMAT_GDK_PIXBUF_ANIMATION:
72 _g_object_unref (self->priv->data.pixbuf_animation);
73 self->priv->data.pixbuf_animation = NULL;
74 break;
75
76 default:
77 break;
78 }
79 }
80
81
82 static void
_gth_image_free_icc_profile(GthImage * self)83 _gth_image_free_icc_profile (GthImage *self)
84 {
85 _g_object_unref (self->priv->icc_data);
86 self->priv->icc_data = NULL;
87 }
88
89
90 static void
gth_image_finalize(GObject * object)91 gth_image_finalize (GObject *object)
92 {
93 g_return_if_fail (object != NULL);
94 g_return_if_fail (GTH_IS_IMAGE (object));
95
96 _gth_image_free_data (GTH_IMAGE (object));
97 _gth_image_free_icc_profile (GTH_IMAGE (object));
98
99 /* Chain up */
100 G_OBJECT_CLASS (gth_image_parent_class)->finalize (object);
101 }
102
103
104 static gboolean
base_get_is_zoomable(GthImage * image)105 base_get_is_zoomable (GthImage *image)
106 {
107 return FALSE;
108 }
109
110
111 static gboolean
base_set_zoom(GthImage * image,double zoom,int * original_width,int * original_height)112 base_set_zoom (GthImage *image,
113 double zoom,
114 int *original_width,
115 int *original_height)
116 {
117 return FALSE;
118 }
119
120
121 static void
gth_image_class_init(GthImageClass * klass)122 gth_image_class_init (GthImageClass *klass)
123 {
124 GObjectClass *gobject_class;
125
126 gobject_class = (GObjectClass*) klass;
127 gobject_class->finalize = gth_image_finalize;
128
129 klass->get_is_zoomable = base_get_is_zoomable;
130 klass->set_zoom = base_set_zoom;
131 }
132
133
134 static void
gth_image_init(GthImage * self)135 gth_image_init (GthImage *self)
136 {
137 self->priv = gth_image_get_instance_private (self);
138 self->priv->format = GTH_IMAGE_FORMAT_CAIRO_SURFACE;
139 self->priv->data.surface = NULL;
140 self->priv->icc_data = NULL;
141 }
142
143
144 GthImage *
gth_image_new(void)145 gth_image_new (void)
146 {
147 return (GthImage *) g_object_new (GTH_TYPE_IMAGE, NULL);
148 }
149
150
151 GthImage *
gth_image_new_for_surface(cairo_surface_t * surface)152 gth_image_new_for_surface (cairo_surface_t *surface)
153 {
154 GthImage *image;
155
156 image = gth_image_new ();
157 gth_image_set_cairo_surface (image, surface);
158
159 return image;
160 }
161
162
163 GthImage *
gth_image_new_for_pixbuf(GdkPixbuf * value)164 gth_image_new_for_pixbuf (GdkPixbuf *value)
165 {
166 GthImage *image;
167
168 image = gth_image_new ();
169 gth_image_set_pixbuf (image, value);
170
171 return image;
172 }
173
174
175 GthImage *
gth_image_copy(GthImage * image)176 gth_image_copy (GthImage *image)
177 {
178 GthImage *new_image;
179
180 new_image = gth_image_new ();
181 gth_image_set_icc_profile (new_image, gth_image_get_icc_profile (image));
182
183 switch (image->priv->format) {
184 case GTH_IMAGE_FORMAT_CAIRO_SURFACE:
185 new_image->priv->format = GTH_IMAGE_FORMAT_CAIRO_SURFACE;
186 new_image->priv->data.surface = _cairo_image_surface_copy (image->priv->data.surface);
187 break;
188
189 case GTH_IMAGE_FORMAT_GDK_PIXBUF:
190 new_image->priv->format = GTH_IMAGE_FORMAT_GDK_PIXBUF;
191 new_image->priv->data.pixbuf = gdk_pixbuf_copy (image->priv->data.pixbuf);
192 break;
193
194 case GTH_IMAGE_FORMAT_GDK_PIXBUF_ANIMATION:
195 new_image->priv->format = GTH_IMAGE_FORMAT_GDK_PIXBUF;
196 new_image->priv->data.pixbuf = gdk_pixbuf_copy (gdk_pixbuf_animation_get_static_image (image->priv->data.pixbuf_animation));
197 break;
198
199 default:
200 break;
201 }
202
203 return new_image;
204 }
205
206
207 void
gth_image_set_cairo_surface(GthImage * image,cairo_surface_t * value)208 gth_image_set_cairo_surface (GthImage *image,
209 cairo_surface_t *value)
210 {
211 _gth_image_free_data (image);
212 image->priv->format = GTH_IMAGE_FORMAT_CAIRO_SURFACE;
213 image->priv->data.surface = cairo_surface_reference (value);
214 }
215
216
217 cairo_surface_t *
gth_image_get_cairo_surface(GthImage * image)218 gth_image_get_cairo_surface (GthImage *image)
219 {
220 cairo_surface_t *result = NULL;
221
222 switch (image->priv->format) {
223 case GTH_IMAGE_FORMAT_CAIRO_SURFACE:
224 result = cairo_surface_reference (image->priv->data.surface);
225 break;
226
227 case GTH_IMAGE_FORMAT_GDK_PIXBUF:
228 result = _cairo_image_surface_create_from_pixbuf (image->priv->data.pixbuf);
229 break;
230
231 case GTH_IMAGE_FORMAT_GDK_PIXBUF_ANIMATION:
232 if (image->priv->data.pixbuf_animation != NULL) {
233 GdkPixbuf *static_image;
234
235 static_image = gdk_pixbuf_animation_get_static_image (image->priv->data.pixbuf_animation);
236 result = _cairo_image_surface_create_from_pixbuf (static_image);
237 }
238 break;
239
240 default:
241 break;
242 }
243
244 return result;
245 }
246
247
248 gboolean
gth_image_get_original_size(GthImage * image,int * width,int * height)249 gth_image_get_original_size (GthImage *image,
250 int *width,
251 int *height)
252 {
253 cairo_surface_t *surface;
254 int local_width;
255 int local_height;
256 gboolean value_set = FALSE;
257
258 switch (image->priv->format) {
259 case GTH_IMAGE_FORMAT_CAIRO_SURFACE:
260 surface = gth_image_get_cairo_surface (image);
261 if (surface != NULL) {
262 if (! _cairo_image_surface_get_original_size (surface, &local_width, &local_height)) {
263 local_width = cairo_image_surface_get_width (surface);
264 local_height = cairo_image_surface_get_height (surface);
265 }
266 value_set = TRUE;
267 }
268 break;
269
270 case GTH_IMAGE_FORMAT_GDK_PIXBUF:
271 if (image->priv->data.pixbuf != NULL) {
272 local_width = gdk_pixbuf_get_width (image->priv->data.pixbuf);
273 local_height = gdk_pixbuf_get_height (image->priv->data.pixbuf);
274 value_set = TRUE;
275 }
276 break;
277
278 case GTH_IMAGE_FORMAT_GDK_PIXBUF_ANIMATION:
279 if (image->priv->data.pixbuf_animation != NULL) {
280 local_width = gdk_pixbuf_animation_get_width (image->priv->data.pixbuf_animation);
281 local_height = gdk_pixbuf_animation_get_width (image->priv->data.pixbuf_animation);
282 value_set = TRUE;
283 }
284 break;
285
286 default:
287 break;
288 }
289
290 if (value_set) {
291 if (width != NULL) *width = local_width;
292 if (height != NULL) *height = local_height;
293 }
294
295 return value_set;
296 }
297
298
299 gboolean
gth_image_get_is_zoomable(GthImage * self)300 gth_image_get_is_zoomable (GthImage *self)
301 {
302 if (self == NULL)
303 return FALSE;
304 else
305 return GTH_IMAGE_GET_CLASS (self)->get_is_zoomable (self);
306 }
307
308
309 gboolean
gth_image_get_is_null(GthImage * self)310 gth_image_get_is_null (GthImage *self)
311 {
312 gboolean is_null = TRUE;
313
314 switch (self->priv->format) {
315 case GTH_IMAGE_FORMAT_CAIRO_SURFACE:
316 is_null = self->priv->data.surface == NULL;
317 break;
318
319 case GTH_IMAGE_FORMAT_GDK_PIXBUF:
320 is_null = self->priv->data.pixbuf == NULL;
321 break;
322
323 case GTH_IMAGE_FORMAT_GDK_PIXBUF_ANIMATION:
324 is_null = self->priv->data.pixbuf_animation == NULL;
325 break;
326
327 default:
328 break;
329 }
330
331 return is_null;
332 }
333
334
335 gboolean
gth_image_set_zoom(GthImage * self,double zoom,int * original_width,int * original_height)336 gth_image_set_zoom (GthImage *self,
337 double zoom,
338 int *original_width,
339 int *original_height)
340 {
341 return GTH_IMAGE_GET_CLASS (self)->set_zoom (self, zoom, original_width, original_height);
342 }
343
344
345 void
gth_image_set_pixbuf(GthImage * image,GdkPixbuf * value)346 gth_image_set_pixbuf (GthImage *image,
347 GdkPixbuf *value)
348 {
349 _gth_image_free_data (image);
350 image->priv->format = GTH_IMAGE_FORMAT_GDK_PIXBUF;
351 image->priv->data.pixbuf = _g_object_ref (value);
352 }
353
354
355 GdkPixbuf *
gth_image_get_pixbuf(GthImage * image)356 gth_image_get_pixbuf (GthImage *image)
357 {
358 GdkPixbuf *result = NULL;
359
360 if (image == NULL)
361 return NULL;
362
363 switch (image->priv->format) {
364 case GTH_IMAGE_FORMAT_CAIRO_SURFACE:
365 result = _gdk_pixbuf_new_from_cairo_surface (image->priv->data.surface);
366 break;
367
368 case GTH_IMAGE_FORMAT_GDK_PIXBUF:
369 result = _g_object_ref (image->priv->data.pixbuf);
370 break;
371
372 case GTH_IMAGE_FORMAT_GDK_PIXBUF_ANIMATION:
373 if (image->priv->data.pixbuf_animation != NULL) {
374 GdkPixbuf *static_image;
375
376 static_image = gdk_pixbuf_animation_get_static_image (image->priv->data.pixbuf_animation);
377 if (static_image != NULL)
378 result = gdk_pixbuf_copy (static_image);
379 }
380 break;
381
382 default:
383 break;
384 }
385
386 return result;
387 }
388
389
390 void
gth_image_set_pixbuf_animation(GthImage * image,GdkPixbufAnimation * value)391 gth_image_set_pixbuf_animation (GthImage *image,
392 GdkPixbufAnimation *value)
393 {
394 _gth_image_free_data (image);
395 image->priv->format = GTH_IMAGE_FORMAT_GDK_PIXBUF_ANIMATION;
396 image->priv->data.pixbuf_animation = _g_object_ref (value);
397 }
398
399
400 GdkPixbufAnimation *
gth_image_get_pixbuf_animation(GthImage * image)401 gth_image_get_pixbuf_animation (GthImage *image)
402 {
403 GdkPixbufAnimation *result = NULL;
404
405 switch (image->priv->format) {
406 case GTH_IMAGE_FORMAT_CAIRO_SURFACE:
407 if (image->priv->data.surface != NULL) {
408 GdkPixbuf *pixbuf;
409
410 pixbuf = _gdk_pixbuf_new_from_cairo_surface (image->priv->data.surface);
411 result = gdk_pixbuf_non_anim_new (pixbuf);
412
413 g_object_unref (pixbuf);
414 }
415 break;
416
417 case GTH_IMAGE_FORMAT_GDK_PIXBUF:
418 if (image->priv->data.pixbuf != NULL)
419 result = gdk_pixbuf_non_anim_new (image->priv->data.pixbuf);
420 break;
421
422 case GTH_IMAGE_FORMAT_GDK_PIXBUF_ANIMATION:
423 result = _g_object_ref (image->priv->data.pixbuf);
424 break;
425
426 default:
427 break;
428 }
429
430 return result;
431 }
432
433
434 gboolean
gth_image_get_is_animation(GthImage * image)435 gth_image_get_is_animation (GthImage *image)
436 {
437 if (image == NULL)
438 return FALSE;
439
440 return ((image->priv->format == GTH_IMAGE_FORMAT_GDK_PIXBUF_ANIMATION)
441 && (! gdk_pixbuf_animation_is_static_image (image->priv->data.pixbuf_animation)));
442 }
443
444
445 void
gth_image_set_icc_profile(GthImage * image,GthICCProfile * profile)446 gth_image_set_icc_profile (GthImage *image,
447 GthICCProfile *profile)
448 {
449 g_return_if_fail (image != NULL);
450
451 _g_object_ref (profile);
452 _gth_image_free_icc_profile (image);
453 image->priv->icc_data = profile;
454 }
455
456
457 GthICCProfile *
gth_image_get_icc_profile(GthImage * image)458 gth_image_get_icc_profile (GthImage *image)
459 {
460 g_return_val_if_fail (image != NULL, NULL);
461 return image->priv->icc_data;
462 }
463
464
465 /* -- gth_image_apply_icc_profile -- */
466
467
468 void
gth_image_apply_icc_profile(GthImage * image,GthICCProfile * out_profile,GCancellable * cancellable)469 gth_image_apply_icc_profile (GthImage *image,
470 GthICCProfile *out_profile,
471 GCancellable *cancellable)
472 {
473 #if HAVE_LCMS2
474
475 cairo_surface_t *surface;
476 GthICCTransform *transform;
477
478 g_return_if_fail (image != NULL);
479
480 if (out_profile == NULL)
481 return;
482
483 if (image->priv->icc_data == NULL)
484 return;
485
486 if (image->priv->format != GTH_IMAGE_FORMAT_CAIRO_SURFACE)
487 return;
488
489 surface = gth_image_get_cairo_surface (image);
490 if (surface == NULL)
491 return;
492
493 transform = gth_color_manager_get_transform (gth_main_get_default_color_manager (),
494 image->priv->icc_data,
495 out_profile);
496
497 if (transform != NULL) {
498 cmsHTRANSFORM hTransform;
499 unsigned char *surface_row;
500 int width;
501 int height;
502 int row_stride;
503 int row;
504
505 hTransform = (cmsHTRANSFORM) gth_icc_transform_get_transform (transform);
506 surface_row = _cairo_image_surface_flush_and_get_data (surface);
507 width = cairo_image_surface_get_width (surface);
508 height = cairo_image_surface_get_height (surface);
509 row_stride = cairo_image_surface_get_stride (surface);
510
511 for (row = 0; row < height; row++) {
512 if (g_cancellable_is_cancelled (cancellable))
513 break;
514 cmsDoTransform (hTransform, surface_row, surface_row, width);
515 surface_row += row_stride;
516 }
517 cairo_surface_mark_dirty (surface);
518
519 cairo_surface_destroy (surface);
520 }
521
522 _g_object_unref (transform);
523
524 #endif
525 }
526
527
528 /* -- gth_image_apply_icc_profile_async -- */
529
530
531 typedef struct {
532 GthImage *image;
533 GthICCProfile *out_profile;
534 } ApplyProfileData;
535
536
537 static void
apply_profile_data_free(gpointer user_data)538 apply_profile_data_free (gpointer user_data)
539 {
540 ApplyProfileData *apd = user_data;
541
542 g_object_unref (apd->image);
543 _g_object_unref (apd->out_profile);
544 g_free (apd);
545 }
546
547
548 static void
_gth_image_apply_icc_profile_thread(GTask * task,gpointer source_object,gpointer task_data,GCancellable * cancellable)549 _gth_image_apply_icc_profile_thread (GTask *task,
550 gpointer source_object,
551 gpointer task_data,
552 GCancellable *cancellable)
553 {
554 ApplyProfileData *apd;
555
556 apd = g_task_get_task_data (task);
557 gth_image_apply_icc_profile (apd->image, apd->out_profile, cancellable);
558
559 if ((cancellable != NULL) && g_cancellable_is_cancelled (cancellable)) {
560 g_task_return_error (task, g_error_new_literal (G_IO_ERROR, G_IO_ERROR_CANCELLED, ""));
561 return;
562 }
563
564 g_task_return_boolean (task, TRUE);
565 }
566
567
568 void
gth_image_apply_icc_profile_async(GthImage * image,GthICCProfile * out_profile,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)569 gth_image_apply_icc_profile_async (GthImage *image,
570 GthICCProfile *out_profile,
571 GCancellable *cancellable,
572 GAsyncReadyCallback callback,
573 gpointer user_data)
574 {
575 GTask *task;
576 ApplyProfileData *apd;
577
578 g_return_if_fail (image != NULL);
579
580 task = g_task_new (NULL, cancellable, callback, user_data);
581
582 apd = g_new (ApplyProfileData, 1);
583 apd->image = g_object_ref (image);
584 apd->out_profile = _g_object_ref (out_profile);
585 g_task_set_task_data (task, apd, apply_profile_data_free);
586 g_task_run_in_thread (task, _gth_image_apply_icc_profile_thread);
587
588 g_object_unref (task);
589 }
590
591
592 gboolean
gth_image_apply_icc_profile_finish(GAsyncResult * result,GError ** error)593 gth_image_apply_icc_profile_finish (GAsyncResult *result,
594 GError **error)
595 {
596 return g_task_propagate_boolean (G_TASK (result), error);
597 }
598