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