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