1 /* LIBGIMP - The GIMP Library
2 * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
3 *
4 * gimpcolortransform.c
5 * Copyright (C) 2014 Michael Natterer <mitch@gimp.org>
6 * Elle Stone <ellestone@ninedegreesbelow.com>
7 * Øyvind Kolås <pippin@gimp.org>
8 *
9 * This library is free software: you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 3 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library. If not, see
21 * <https://www.gnu.org/licenses/>.
22 */
23
24 #include "config.h"
25
26 #include <string.h>
27
28 #include <lcms2.h>
29
30 #include <gio/gio.h>
31 #include <gegl.h>
32
33 #include "libgimpbase/gimpbase.h"
34 #include "libgimpconfig/gimpconfig.h"
35
36 #include "gimpcolortypes.h"
37
38 #include "gimpcolorprofile.h"
39 #include "gimpcolortransform.h"
40
41 #include "libgimp/libgimp-intl.h"
42
43
44 /**
45 * SECTION: gimpcolortransform
46 * @title: GimpColorTransform
47 * @short_description: Definitions and Functions relating to LCMS.
48 *
49 * Definitions and Functions relating to LCMS.
50 **/
51
52 /**
53 * GimpColorTransform:
54 *
55 * Simply a typedef to #gpointer, but actually is a cmsHTRANSFORM. It's
56 * used in public GIMP APIs in order to avoid having to include LCMS
57 * headers.
58 **/
59
60
61 enum
62 {
63 PROGRESS,
64 LAST_SIGNAL
65 };
66
67
68 struct _GimpColorTransformPrivate
69 {
70 GimpColorProfile *src_profile;
71 const Babl *src_format;
72 const Babl *src_space_format;
73
74 GimpColorProfile *dest_profile;
75 const Babl *dest_format;
76 const Babl *dest_space_format;
77
78 cmsHTRANSFORM transform;
79 const Babl *fish;
80 };
81
82
83 static void gimp_color_transform_finalize (GObject *object);
84
85
86 G_DEFINE_TYPE_WITH_PRIVATE (GimpColorTransform, gimp_color_transform,
87 G_TYPE_OBJECT)
88
89 #define parent_class gimp_color_transform_parent_class
90
91 static guint gimp_color_transform_signals[LAST_SIGNAL] = { 0 };
92
93 static gchar *lcms_last_error = NULL;
94
95
96 static void
lcms_error_clear(void)97 lcms_error_clear (void)
98 {
99 if (lcms_last_error)
100 {
101 g_free (lcms_last_error);
102 lcms_last_error = NULL;
103 }
104 }
105
106 static void
lcms_error_handler(cmsContext ContextID,cmsUInt32Number ErrorCode,const gchar * text)107 lcms_error_handler (cmsContext ContextID,
108 cmsUInt32Number ErrorCode,
109 const gchar *text)
110 {
111 lcms_error_clear ();
112
113 lcms_last_error = g_strdup_printf ("lcms2 error %d: %s", ErrorCode, text);
114 }
115
116 static void
gimp_color_transform_class_init(GimpColorTransformClass * klass)117 gimp_color_transform_class_init (GimpColorTransformClass *klass)
118 {
119 GObjectClass *object_class = G_OBJECT_CLASS (klass);
120
121 object_class->finalize = gimp_color_transform_finalize;
122
123 gimp_color_transform_signals[PROGRESS] =
124 g_signal_new ("progress",
125 G_OBJECT_CLASS_TYPE (object_class),
126 G_SIGNAL_RUN_FIRST,
127 G_STRUCT_OFFSET (GimpColorTransformClass,
128 progress),
129 NULL, NULL,
130 g_cclosure_marshal_VOID__DOUBLE,
131 G_TYPE_NONE, 1,
132 G_TYPE_DOUBLE);
133
134 cmsSetLogErrorHandler (lcms_error_handler);
135 }
136
137 static void
gimp_color_transform_init(GimpColorTransform * transform)138 gimp_color_transform_init (GimpColorTransform *transform)
139 {
140 transform->priv = gimp_color_transform_get_instance_private (transform);
141 }
142
143 static void
gimp_color_transform_finalize(GObject * object)144 gimp_color_transform_finalize (GObject *object)
145 {
146 GimpColorTransform *transform = GIMP_COLOR_TRANSFORM (object);
147
148 g_clear_object (&transform->priv->src_profile);
149 g_clear_object (&transform->priv->dest_profile);
150
151 g_clear_pointer (&transform->priv->transform, cmsDeleteTransform);
152
153 G_OBJECT_CLASS (parent_class)->finalize (object);
154 }
155
156
157 /**
158 * gimp_color_transform_new:
159 * @src_profile: the source #GimpColorProfile
160 * @src_format: the source #Babl format
161 * @dest_profile: the destination #GimpColorProfile
162 * @dest_format: the destination #Babl format
163 * @rendering_intent: the rendering intent
164 * @flags: transform flags
165 *
166 * This function creates an color transform.
167 *
168 * Return value: the #GimpColorTransform, or %NULL if no transform is needed
169 * to convert between pixels of @src_profile and @dest_profile.
170 *
171 * Since: 2.10
172 **/
173 GimpColorTransform *
gimp_color_transform_new(GimpColorProfile * src_profile,const Babl * src_format,GimpColorProfile * dest_profile,const Babl * dest_format,GimpColorRenderingIntent rendering_intent,GimpColorTransformFlags flags)174 gimp_color_transform_new (GimpColorProfile *src_profile,
175 const Babl *src_format,
176 GimpColorProfile *dest_profile,
177 const Babl *dest_format,
178 GimpColorRenderingIntent rendering_intent,
179 GimpColorTransformFlags flags)
180 {
181 GimpColorTransform *transform;
182 GimpColorTransformPrivate *priv;
183 cmsHPROFILE src_lcms;
184 cmsHPROFILE dest_lcms;
185 cmsUInt32Number lcms_src_format;
186 cmsUInt32Number lcms_dest_format;
187 GError *error = NULL;
188
189 g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (src_profile), NULL);
190 g_return_val_if_fail (src_format != NULL, NULL);
191 g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (dest_profile), NULL);
192 g_return_val_if_fail (dest_format != NULL, NULL);
193
194 if (gimp_color_transform_can_gegl_copy (src_profile, dest_profile))
195 return NULL;
196
197 transform = g_object_new (GIMP_TYPE_COLOR_TRANSFORM, NULL);
198
199 priv = transform->priv;
200
201 priv->src_space_format = gimp_color_profile_get_format (src_profile,
202 src_format,
203 BABL_ICC_INTENT_RELATIVE_COLORIMETRIC,
204 &error);
205 if (! priv->src_space_format)
206 {
207 g_printerr ("%s: error making src format: %s\n",
208 G_STRFUNC, error->message);
209 g_clear_error (&error);
210 }
211
212 priv->dest_space_format = gimp_color_profile_get_format (dest_profile,
213 dest_format,
214 rendering_intent,
215 &error);
216 if (! priv->dest_space_format)
217 {
218 g_printerr ("%s: error making dest format: %s\n",
219 G_STRFUNC, error->message);
220 g_clear_error (&error);
221 }
222
223 if (! g_getenv ("GIMP_COLOR_TRANSFORM_DISABLE_BABL") &&
224 priv->src_space_format && priv->dest_space_format)
225 {
226 priv->src_format = src_format;
227 priv->dest_format = dest_format;
228 priv->fish = babl_fish (priv->src_space_format,
229 priv->dest_space_format);
230
231 g_printerr ("%s: using babl for '%s' -> '%s'\n",
232 G_STRFUNC,
233 gimp_color_profile_get_label (src_profile),
234 gimp_color_profile_get_label (dest_profile));
235
236 return transform;
237 }
238
239 priv->src_space_format = NULL;
240 priv->dest_space_format = NULL;
241
242 priv->src_format = gimp_color_profile_get_lcms_format (src_format,
243 &lcms_src_format);
244 priv->dest_format = gimp_color_profile_get_lcms_format (dest_format,
245 &lcms_dest_format);
246
247 src_lcms = gimp_color_profile_get_lcms_profile (src_profile);
248 dest_lcms = gimp_color_profile_get_lcms_profile (dest_profile);
249
250 lcms_error_clear ();
251
252 priv->transform = cmsCreateTransform (src_lcms, lcms_src_format,
253 dest_lcms, lcms_dest_format,
254 rendering_intent,
255 flags |
256 cmsFLAGS_COPY_ALPHA);
257
258 if (lcms_last_error)
259 {
260 if (priv->transform)
261 {
262 cmsDeleteTransform (priv->transform);
263 priv->transform = NULL;
264 }
265
266 g_printerr ("%s\n", lcms_last_error);
267 }
268
269 if (! priv->transform)
270 {
271 g_object_unref (transform);
272 transform = NULL;
273 }
274
275 return transform;
276 }
277
278 /**
279 * gimp_color_transform_new_proofing:
280 * @src_profile: the source #GimpColorProfile
281 * @src_format: the source #Babl format
282 * @dest_profile: the destination #GimpColorProfile
283 * @dest_format: the destination #Babl format
284 * @proof_profile: the proof #GimpColorProfile
285 * @proof_intent: the proof intent
286 * @display_intent: the display intent
287 * @flags: transform flags
288 *
289 * This function creates a simulation / proofing color transform.
290 *
291 * Return value: the #GimpColorTransform, or %NULL.
292 *
293 * Since: 2.10
294 **/
295 GimpColorTransform *
gimp_color_transform_new_proofing(GimpColorProfile * src_profile,const Babl * src_format,GimpColorProfile * dest_profile,const Babl * dest_format,GimpColorProfile * proof_profile,GimpColorRenderingIntent proof_intent,GimpColorRenderingIntent display_intent,GimpColorTransformFlags flags)296 gimp_color_transform_new_proofing (GimpColorProfile *src_profile,
297 const Babl *src_format,
298 GimpColorProfile *dest_profile,
299 const Babl *dest_format,
300 GimpColorProfile *proof_profile,
301 GimpColorRenderingIntent proof_intent,
302 GimpColorRenderingIntent display_intent,
303 GimpColorTransformFlags flags)
304 {
305 GimpColorTransform *transform;
306 GimpColorTransformPrivate *priv;
307 cmsHPROFILE src_lcms;
308 cmsHPROFILE dest_lcms;
309 cmsHPROFILE proof_lcms;
310 cmsUInt32Number lcms_src_format;
311 cmsUInt32Number lcms_dest_format;
312
313 g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (src_profile), NULL);
314 g_return_val_if_fail (src_format != NULL, NULL);
315 g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (dest_profile), NULL);
316 g_return_val_if_fail (dest_format != NULL, NULL);
317 g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (proof_profile), NULL);
318
319 transform = g_object_new (GIMP_TYPE_COLOR_TRANSFORM, NULL);
320
321 priv = transform->priv;
322
323 src_lcms = gimp_color_profile_get_lcms_profile (src_profile);
324 dest_lcms = gimp_color_profile_get_lcms_profile (dest_profile);
325 proof_lcms = gimp_color_profile_get_lcms_profile (proof_profile);
326
327 priv->src_format = gimp_color_profile_get_lcms_format (src_format,
328 &lcms_src_format);
329 priv->dest_format = gimp_color_profile_get_lcms_format (dest_format,
330 &lcms_dest_format);
331
332 lcms_error_clear ();
333
334 priv->transform = cmsCreateProofingTransform (src_lcms, lcms_src_format,
335 dest_lcms, lcms_dest_format,
336 proof_lcms,
337 proof_intent,
338 display_intent,
339 flags |
340 cmsFLAGS_SOFTPROOFING |
341 cmsFLAGS_COPY_ALPHA);
342
343 if (lcms_last_error)
344 {
345 if (priv->transform)
346 {
347 cmsDeleteTransform (priv->transform);
348 priv->transform = NULL;
349 }
350
351 g_printerr ("%s\n", lcms_last_error);
352 }
353
354 if (! priv->transform)
355 {
356 g_object_unref (transform);
357 transform = NULL;
358 }
359
360 return transform;
361 }
362
363 /**
364 * gimp_color_transform_process_pixels:
365 * @transform: a #GimpColorTransform
366 * @src_format: #Babl format of @src_pixels
367 * @src_pixels: pointer to the source pixels
368 * @dest_format: #Babl format of @dest_pixels
369 * @dest_pixels: pointer to the destination pixels
370 * @length: number of pixels to process
371 *
372 * This function transforms a contiguous line of pixels.
373 *
374 * Since: 2.10
375 **/
376 void
gimp_color_transform_process_pixels(GimpColorTransform * transform,const Babl * src_format,gconstpointer src_pixels,const Babl * dest_format,gpointer dest_pixels,gsize length)377 gimp_color_transform_process_pixels (GimpColorTransform *transform,
378 const Babl *src_format,
379 gconstpointer src_pixels,
380 const Babl *dest_format,
381 gpointer dest_pixels,
382 gsize length)
383 {
384 GimpColorTransformPrivate *priv;
385 gpointer *src;
386 gpointer *dest;
387
388 g_return_if_fail (GIMP_IS_COLOR_TRANSFORM (transform));
389 g_return_if_fail (src_format != NULL);
390 g_return_if_fail (src_pixels != NULL);
391 g_return_if_fail (dest_format != NULL);
392 g_return_if_fail (dest_pixels != NULL);
393
394 priv = transform->priv;
395
396 if (src_format != priv->src_format)
397 {
398 src = g_malloc (length * babl_format_get_bytes_per_pixel (priv->src_format));
399
400 babl_process (babl_fish (src_format,
401 priv->src_format),
402 src_pixels, src, length);
403 }
404 else
405 {
406 src = (gpointer) src_pixels;
407 }
408
409 if (dest_format != priv->dest_format)
410 {
411 dest = g_malloc (length * babl_format_get_bytes_per_pixel (priv->dest_format));
412 }
413 else
414 {
415 dest = dest_pixels;
416 }
417
418 if (priv->transform)
419 {
420 cmsDoTransform (priv->transform, src, dest, length);
421 }
422 else
423 {
424 babl_process (priv->fish, src, dest, length);
425 }
426
427 if (src_format != priv->src_format)
428 {
429 g_free (src);
430 }
431
432 if (dest_format != priv->dest_format)
433 {
434 babl_process (babl_fish (priv->dest_format,
435 dest_format),
436 dest, dest_pixels, length);
437
438 g_free (dest);
439 }
440 }
441
442 /**
443 * gimp_color_transform_process_buffer:
444 * @transform: a #GimpColorTransform
445 * @src_buffer: source #GeglBuffer
446 * @src_rect: rectangle in @src_buffer
447 * @dest_buffer: destination #GeglBuffer
448 * @dest_rect: rectangle in @dest_buffer
449 *
450 * This function transforms buffer into another buffer.
451 *
452 * Since: 2.10
453 **/
454 void
gimp_color_transform_process_buffer(GimpColorTransform * transform,GeglBuffer * src_buffer,const GeglRectangle * src_rect,GeglBuffer * dest_buffer,const GeglRectangle * dest_rect)455 gimp_color_transform_process_buffer (GimpColorTransform *transform,
456 GeglBuffer *src_buffer,
457 const GeglRectangle *src_rect,
458 GeglBuffer *dest_buffer,
459 const GeglRectangle *dest_rect)
460 {
461 GimpColorTransformPrivate *priv;
462 GeglBufferIterator *iter;
463 gint total_pixels;
464 gint done_pixels = 0;
465
466 g_return_if_fail (GIMP_IS_COLOR_TRANSFORM (transform));
467 g_return_if_fail (GEGL_IS_BUFFER (src_buffer));
468 g_return_if_fail (GEGL_IS_BUFFER (dest_buffer));
469
470 priv = transform->priv;
471
472 if (src_rect)
473 {
474 total_pixels = src_rect->width * src_rect->height;
475 }
476 else
477 {
478 total_pixels = (gegl_buffer_get_width (src_buffer) *
479 gegl_buffer_get_height (src_buffer));
480 }
481
482 if (src_buffer != dest_buffer)
483 {
484 iter = gegl_buffer_iterator_new (src_buffer, src_rect, 0,
485 priv->src_format,
486 GEGL_ACCESS_READ,
487 GEGL_ABYSS_NONE, 2);
488
489 gegl_buffer_iterator_add (iter, dest_buffer, dest_rect, 0,
490 priv->dest_format,
491 GEGL_ACCESS_WRITE,
492 GEGL_ABYSS_NONE);
493
494 while (gegl_buffer_iterator_next (iter))
495 {
496 if (priv->transform)
497 {
498 cmsDoTransform (priv->transform,
499 iter->items[0].data, iter->items[1].data, iter->length);
500 }
501 else
502 {
503 babl_process (priv->fish,
504 iter->items[0].data, iter->items[1].data, iter->length);
505 }
506
507 done_pixels += iter->items[0].roi.width * iter->items[0].roi.height;
508
509 g_signal_emit (transform, gimp_color_transform_signals[PROGRESS], 0,
510 (gdouble) done_pixels /
511 (gdouble) total_pixels);
512 }
513 }
514 else
515 {
516 iter = gegl_buffer_iterator_new (src_buffer, src_rect, 0,
517 priv->src_format,
518 GEGL_ACCESS_READWRITE,
519 GEGL_ABYSS_NONE, 1);
520
521 while (gegl_buffer_iterator_next (iter))
522 {
523 if (priv->transform)
524 {
525 cmsDoTransform (priv->transform,
526 iter->items[0].data, iter->items[0].data, iter->length);
527 }
528 else
529 {
530 babl_process (priv->fish,
531 iter->items[0].data, iter->items[0].data, iter->length);
532 }
533
534 done_pixels += iter->items[0].roi.width * iter->items[0].roi.height;
535
536 g_signal_emit (transform, gimp_color_transform_signals[PROGRESS], 0,
537 (gdouble) done_pixels /
538 (gdouble) total_pixels);
539 }
540 }
541
542 g_signal_emit (transform, gimp_color_transform_signals[PROGRESS], 0,
543 1.0);
544 }
545
546 /**
547 * gimp_color_transform_can_gegl_copy:
548 * @src_profile: source #GimpColorProfile
549 * @dest_profile: destination #GimpColorProfile
550 *
551 * This function checks if a GimpColorTransform is needed at all.
552 *
553 * Return value: %TRUE if pixels can be correctly converted between
554 * @src_profile and @dest_profile by simply using
555 * gegl_buffer_copy(), babl_process() or similar.
556 *
557 * Since: 2.10
558 **/
559 gboolean
gimp_color_transform_can_gegl_copy(GimpColorProfile * src_profile,GimpColorProfile * dest_profile)560 gimp_color_transform_can_gegl_copy (GimpColorProfile *src_profile,
561 GimpColorProfile *dest_profile)
562 {
563 static GimpColorProfile *srgb_profile = NULL;
564 static GimpColorProfile *srgb_linear_profile = NULL;
565 static GimpColorProfile *gray_profile = NULL;
566 static GimpColorProfile *gray_linear_profile = NULL;
567
568 g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (src_profile), FALSE);
569 g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (dest_profile), FALSE);
570
571 if (gimp_color_profile_is_equal (src_profile, dest_profile))
572 return TRUE;
573
574 if (! srgb_profile)
575 {
576 srgb_profile = gimp_color_profile_new_rgb_srgb ();
577 srgb_linear_profile = gimp_color_profile_new_rgb_srgb_linear ();
578 gray_profile = gimp_color_profile_new_d65_gray_srgb_trc ();
579 gray_linear_profile = gimp_color_profile_new_d65_gray_linear ();
580 }
581
582 if ((gimp_color_profile_is_equal (src_profile, srgb_profile) ||
583 gimp_color_profile_is_equal (src_profile, srgb_linear_profile) ||
584 gimp_color_profile_is_equal (src_profile, gray_profile) ||
585 gimp_color_profile_is_equal (src_profile, gray_linear_profile))
586 &&
587 (gimp_color_profile_is_equal (dest_profile, srgb_profile) ||
588 gimp_color_profile_is_equal (dest_profile, srgb_linear_profile) ||
589 gimp_color_profile_is_equal (dest_profile, gray_profile) ||
590 gimp_color_profile_is_equal (dest_profile, gray_linear_profile)))
591 {
592 return TRUE;
593 }
594
595 return FALSE;
596 }
597