1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
16  */
17 
18 #include "config.h"
19 
20 #include <stdlib.h>
21 #include <string.h> /* memcmp */
22 
23 #include <gdk-pixbuf/gdk-pixbuf.h>
24 #include <gegl.h>
25 
26 #include "libgimpbase/gimpbase.h"
27 #include "libgimpmath/gimpmath.h"
28 #include "libgimpconfig/gimpconfig.h"
29 
30 #include "core-types.h"
31 
32 #include "gimpcurve.h"
33 #include "gimpcurve-load.h"
34 #include "gimpcurve-save.h"
35 #include "gimpparamspecs.h"
36 
37 #include "gimp-intl.h"
38 
39 
40 #define EPSILON 1e-6
41 
42 
43 enum
44 {
45   PROP_0,
46   PROP_CURVE_TYPE,
47   PROP_N_POINTS,
48   PROP_POINTS,
49   PROP_POINT_TYPES,
50   PROP_N_SAMPLES,
51   PROP_SAMPLES
52 };
53 
54 
55 /*  local function prototypes  */
56 
57 static void          gimp_curve_config_iface_init (GimpConfigInterface *iface);
58 
59 static void          gimp_curve_finalize          (GObject          *object);
60 static void          gimp_curve_set_property      (GObject          *object,
61                                                    guint             property_id,
62                                                    const GValue     *value,
63                                                    GParamSpec       *pspec);
64 static void          gimp_curve_get_property      (GObject          *object,
65                                                    guint             property_id,
66                                                    GValue           *value,
67                                                    GParamSpec       *pspec);
68 
69 static gint64        gimp_curve_get_memsize       (GimpObject       *object,
70                                                    gint64           *gui_size);
71 
72 static void          gimp_curve_get_preview_size  (GimpViewable     *viewable,
73                                                    gint              size,
74                                                    gboolean          popup,
75                                                    gboolean          dot_for_dot,
76                                                    gint             *width,
77                                                    gint             *height);
78 static gboolean      gimp_curve_get_popup_size    (GimpViewable     *viewable,
79                                                    gint              width,
80                                                    gint              height,
81                                                    gboolean          dot_for_dot,
82                                                    gint             *popup_width,
83                                                    gint             *popup_height);
84 static GimpTempBuf * gimp_curve_get_new_preview   (GimpViewable     *viewable,
85                                                    GimpContext      *context,
86                                                    gint              width,
87                                                    gint              height);
88 static gchar       * gimp_curve_get_description   (GimpViewable     *viewable,
89                                                    gchar           **tooltip);
90 
91 static void          gimp_curve_dirty             (GimpData         *data);
92 static const gchar * gimp_curve_get_extension     (GimpData         *data);
93 static void          gimp_curve_data_copy         (GimpData         *data,
94                                                    GimpData         *src_data);
95 
96 static gboolean      gimp_curve_serialize         (GimpConfig       *config,
97                                                    GimpConfigWriter *writer,
98                                                    gpointer          data);
99 static gboolean      gimp_curve_deserialize       (GimpConfig       *config,
100                                                    GScanner         *scanner,
101                                                    gint              nest_level,
102                                                    gpointer          data);
103 static gboolean      gimp_curve_equal             (GimpConfig       *a,
104                                                    GimpConfig       *b);
105 static void          _gimp_curve_reset            (GimpConfig       *config);
106 static gboolean      gimp_curve_config_copy       (GimpConfig       *src,
107                                                    GimpConfig       *dest,
108                                                    GParamFlags       flags);
109 
110 static void          gimp_curve_calculate         (GimpCurve        *curve);
111 static void          gimp_curve_plot              (GimpCurve        *curve,
112                                                    gint              p1,
113                                                    gint              p2,
114                                                    gint              p3,
115                                                    gint              p4);
116 
117 
G_DEFINE_TYPE_WITH_CODE(GimpCurve,gimp_curve,GIMP_TYPE_DATA,G_IMPLEMENT_INTERFACE (GIMP_TYPE_CONFIG,gimp_curve_config_iface_init))118 G_DEFINE_TYPE_WITH_CODE (GimpCurve, gimp_curve, GIMP_TYPE_DATA,
119                          G_IMPLEMENT_INTERFACE (GIMP_TYPE_CONFIG,
120                                                 gimp_curve_config_iface_init))
121 
122 #define parent_class gimp_curve_parent_class
123 
124 
125 static void
126 gimp_curve_class_init (GimpCurveClass *klass)
127 {
128   GObjectClass      *object_class      = G_OBJECT_CLASS (klass);
129   GimpObjectClass   *gimp_object_class = GIMP_OBJECT_CLASS (klass);
130   GimpViewableClass *viewable_class    = GIMP_VIEWABLE_CLASS (klass);
131   GimpDataClass     *data_class        = GIMP_DATA_CLASS (klass);
132   GParamSpec        *array_spec;
133 
134   object_class->finalize            = gimp_curve_finalize;
135   object_class->set_property        = gimp_curve_set_property;
136   object_class->get_property        = gimp_curve_get_property;
137 
138   gimp_object_class->get_memsize    = gimp_curve_get_memsize;
139 
140   viewable_class->default_icon_name = "FIXME icon name";
141   viewable_class->get_preview_size  = gimp_curve_get_preview_size;
142   viewable_class->get_popup_size    = gimp_curve_get_popup_size;
143   viewable_class->get_new_preview   = gimp_curve_get_new_preview;
144   viewable_class->get_description   = gimp_curve_get_description;
145 
146   data_class->dirty                 = gimp_curve_dirty;
147   data_class->save                  = gimp_curve_save;
148   data_class->get_extension         = gimp_curve_get_extension;
149   data_class->copy                  = gimp_curve_data_copy;
150 
151   GIMP_CONFIG_PROP_ENUM (object_class, PROP_CURVE_TYPE,
152                          "curve-type",
153                          "Curve Type",
154                          "The curve type",
155                          GIMP_TYPE_CURVE_TYPE,
156                          GIMP_CURVE_SMOOTH, 0);
157 
158   GIMP_CONFIG_PROP_INT (object_class, PROP_N_POINTS,
159                         "n-points",
160                         "Number of Points",
161                         "The number of points",
162                         0, G_MAXINT, 0,
163                         /* for backward compatibility */
164                         GIMP_CONFIG_PARAM_IGNORE);
165 
166   array_spec = g_param_spec_double ("point", NULL, NULL,
167                                     -1.0, 1.0, 0.0, GIMP_PARAM_READWRITE);
168   g_object_class_install_property (object_class, PROP_POINTS,
169                                    gimp_param_spec_value_array ("points",
170                                                                 NULL, NULL,
171                                                                 array_spec,
172                                                                 GIMP_PARAM_STATIC_STRINGS |
173                                                                 GIMP_CONFIG_PARAM_FLAGS));
174 
175   array_spec = g_param_spec_enum ("point-type", NULL, NULL,
176                                   GIMP_TYPE_CURVE_POINT_TYPE,
177                                   GIMP_CURVE_POINT_SMOOTH,
178                                   GIMP_PARAM_READWRITE);
179   g_object_class_install_property (object_class, PROP_POINT_TYPES,
180                                    gimp_param_spec_value_array ("point-types",
181                                                                 NULL, NULL,
182                                                                 array_spec,
183                                                                 GIMP_PARAM_STATIC_STRINGS |
184                                                                 GIMP_CONFIG_PARAM_FLAGS));
185 
186   GIMP_CONFIG_PROP_INT  (object_class, PROP_N_SAMPLES,
187                          "n-samples",
188                          "Number of Samples",
189                          "The number of samples",
190                          256, 256, 256, 0);
191 
192   array_spec = g_param_spec_double ("sample", NULL, NULL,
193                                     0.0, 1.0, 0.0, GIMP_PARAM_READWRITE);
194   g_object_class_install_property (object_class, PROP_SAMPLES,
195                                    gimp_param_spec_value_array ("samples",
196                                                                 NULL, NULL,
197                                                                 array_spec,
198                                                                 GIMP_PARAM_STATIC_STRINGS |
199                                                                 GIMP_CONFIG_PARAM_FLAGS));
200 }
201 
202 static void
gimp_curve_config_iface_init(GimpConfigInterface * iface)203 gimp_curve_config_iface_init (GimpConfigInterface *iface)
204 {
205   iface->serialize   = gimp_curve_serialize;
206   iface->deserialize = gimp_curve_deserialize;
207   iface->equal       = gimp_curve_equal;
208   iface->reset       = _gimp_curve_reset;
209   iface->copy        = gimp_curve_config_copy;
210 }
211 
212 static void
gimp_curve_init(GimpCurve * curve)213 gimp_curve_init (GimpCurve *curve)
214 {
215   curve->n_points  = 0;
216   curve->points    = NULL;
217   curve->n_samples = 0;
218   curve->samples   = NULL;
219   curve->identity  = FALSE;
220 }
221 
222 static void
gimp_curve_finalize(GObject * object)223 gimp_curve_finalize (GObject *object)
224 {
225   GimpCurve *curve = GIMP_CURVE (object);
226 
227   g_clear_pointer (&curve->points,  g_free);
228   curve->n_points = 0;
229 
230   g_clear_pointer (&curve->samples, g_free);
231   curve->n_samples = 0;
232 
233   G_OBJECT_CLASS (parent_class)->finalize (object);
234 }
235 
236 static void
gimp_curve_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)237 gimp_curve_set_property (GObject      *object,
238                          guint         property_id,
239                          const GValue *value,
240                          GParamSpec   *pspec)
241 {
242   GimpCurve *curve = GIMP_CURVE (object);
243 
244   switch (property_id)
245     {
246     case PROP_CURVE_TYPE:
247       gimp_curve_set_curve_type (curve, g_value_get_enum (value));
248       break;
249 
250     case PROP_N_POINTS:
251       /* ignored */
252       break;
253 
254     case PROP_POINTS:
255       {
256         GimpValueArray *array = g_value_get_boxed (value);
257         GimpCurvePoint *points;
258         gint            length;
259         gint            n_points;
260         gint            i;
261 
262         if (! array)
263           {
264             gimp_curve_clear_points (curve);
265 
266             break;
267           }
268 
269         length = gimp_value_array_length (array) / 2;
270 
271         n_points = 0;
272         points   = g_new0 (GimpCurvePoint, length);
273 
274         for (i = 0; i < length; i++)
275           {
276             GValue *x = gimp_value_array_index (array, i * 2);
277             GValue *y = gimp_value_array_index (array, i * 2 + 1);
278 
279             /* for backward compatibility */
280             if (g_value_get_double (x) < 0.0)
281               continue;
282 
283             points[n_points].x = CLAMP (g_value_get_double (x), 0.0, 1.0);
284             points[n_points].y = CLAMP (g_value_get_double (y), 0.0, 1.0);
285 
286             if (n_points > 0)
287               {
288                 points[n_points].x = MAX (points[n_points].x,
289                                           points[n_points - 1].x);
290               }
291 
292             if (n_points < curve->n_points)
293               points[n_points].type = curve->points[n_points].type;
294             else
295               points[n_points].type = GIMP_CURVE_POINT_SMOOTH;
296 
297             n_points++;
298           }
299 
300         g_free (curve->points);
301 
302         curve->n_points = n_points;
303         curve->points   = points;
304 
305         g_object_notify (object, "n-points");
306         g_object_notify (object, "point-types");
307       }
308       break;
309 
310     case PROP_POINT_TYPES:
311       {
312         GimpValueArray *array = g_value_get_boxed (value);
313         GimpCurvePoint *points;
314         gint            length;
315         gdouble         x     = 0.0;
316         gdouble         y     = 0.0;
317         gint            i;
318 
319         if (! array)
320           {
321             gimp_curve_clear_points (curve);
322 
323             break;
324           }
325 
326         length = gimp_value_array_length (array);
327 
328         points = g_new0 (GimpCurvePoint, length);
329 
330         for (i = 0; i < length; i++)
331           {
332             GValue *type = gimp_value_array_index (array, i);
333 
334             points[i].type = g_value_get_enum (type);
335 
336             if (i < curve->n_points)
337               {
338                 x = curve->points[i].x;
339                 y = curve->points[i].y;
340               }
341 
342             points[i].x = x;
343             points[i].y = y;
344           }
345 
346         g_free (curve->points);
347 
348         curve->n_points = length;
349         curve->points   = points;
350 
351         g_object_notify (object, "n-points");
352         g_object_notify (object, "points");
353       }
354       break;
355 
356     case PROP_N_SAMPLES:
357       gimp_curve_set_n_samples (curve, g_value_get_int (value));
358       break;
359 
360     case PROP_SAMPLES:
361       {
362         GimpValueArray *array = g_value_get_boxed (value);
363         gint            length;
364         gint            i;
365 
366         if (! array)
367           break;
368 
369         length = gimp_value_array_length (array);
370 
371         for (i = 0; i < curve->n_samples && i < length; i++)
372           {
373             GValue *v = gimp_value_array_index (array, i);
374 
375             curve->samples[i] = CLAMP (g_value_get_double (v), 0.0, 1.0);
376           }
377       }
378       break;
379 
380     default:
381       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
382       break;
383     }
384 }
385 
386 static void
gimp_curve_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)387 gimp_curve_get_property (GObject    *object,
388                          guint       property_id,
389                          GValue     *value,
390                          GParamSpec *pspec)
391 {
392   GimpCurve *curve = GIMP_CURVE (object);
393 
394   switch (property_id)
395     {
396     case PROP_CURVE_TYPE:
397       g_value_set_enum (value, curve->curve_type);
398       break;
399 
400     case PROP_N_POINTS:
401       g_value_set_int (value, curve->n_points);
402       break;
403 
404     case PROP_POINTS:
405       {
406         GimpValueArray *array = gimp_value_array_new (curve->n_points * 2);
407         GValue          v     = G_VALUE_INIT;
408         gint            i;
409 
410         g_value_init (&v, G_TYPE_DOUBLE);
411 
412         for (i = 0; i < curve->n_points; i++)
413           {
414             g_value_set_double (&v, curve->points[i].x);
415             gimp_value_array_append (array, &v);
416 
417             g_value_set_double (&v, curve->points[i].y);
418             gimp_value_array_append (array, &v);
419           }
420 
421         g_value_unset (&v);
422 
423         g_value_take_boxed (value, array);
424       }
425       break;
426 
427     case PROP_POINT_TYPES:
428       {
429         GimpValueArray *array = gimp_value_array_new (curve->n_points);
430         GValue          v     = G_VALUE_INIT;
431         gint            i;
432 
433         g_value_init (&v, GIMP_TYPE_CURVE_POINT_TYPE);
434 
435         for (i = 0; i < curve->n_points; i++)
436           {
437             g_value_set_enum (&v, curve->points[i].type);
438             gimp_value_array_append (array, &v);
439           }
440 
441         g_value_unset (&v);
442 
443         g_value_take_boxed (value, array);
444       }
445       break;
446 
447     case PROP_N_SAMPLES:
448       g_value_set_int (value, curve->n_samples);
449       break;
450 
451     case PROP_SAMPLES:
452       {
453         GimpValueArray *array = gimp_value_array_new (curve->n_samples);
454         GValue          v     = G_VALUE_INIT;
455         gint            i;
456 
457         g_value_init (&v, G_TYPE_DOUBLE);
458 
459         for (i = 0; i < curve->n_samples; i++)
460           {
461             g_value_set_double (&v, curve->samples[i]);
462             gimp_value_array_append (array, &v);
463           }
464 
465         g_value_unset (&v);
466 
467         g_value_take_boxed (value, array);
468       }
469       break;
470 
471     default:
472       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
473       break;
474     }
475 }
476 
477 static gint64
gimp_curve_get_memsize(GimpObject * object,gint64 * gui_size)478 gimp_curve_get_memsize (GimpObject *object,
479                         gint64     *gui_size)
480 {
481   GimpCurve *curve   = GIMP_CURVE (object);
482   gint64     memsize = 0;
483 
484   memsize += curve->n_points  * sizeof (GimpCurvePoint);
485   memsize += curve->n_samples * sizeof (gdouble);
486 
487   return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object,
488                                                                   gui_size);
489 }
490 
491 static void
gimp_curve_get_preview_size(GimpViewable * viewable,gint size,gboolean popup,gboolean dot_for_dot,gint * width,gint * height)492 gimp_curve_get_preview_size (GimpViewable *viewable,
493                              gint          size,
494                              gboolean      popup,
495                              gboolean      dot_for_dot,
496                              gint         *width,
497                              gint         *height)
498 {
499   *width  = size;
500   *height = size;
501 }
502 
503 static gboolean
gimp_curve_get_popup_size(GimpViewable * viewable,gint width,gint height,gboolean dot_for_dot,gint * popup_width,gint * popup_height)504 gimp_curve_get_popup_size (GimpViewable *viewable,
505                            gint          width,
506                            gint          height,
507                            gboolean      dot_for_dot,
508                            gint         *popup_width,
509                            gint         *popup_height)
510 {
511   *popup_width  = width  * 2;
512   *popup_height = height * 2;
513 
514   return TRUE;
515 }
516 
517 static GimpTempBuf *
gimp_curve_get_new_preview(GimpViewable * viewable,GimpContext * context,gint width,gint height)518 gimp_curve_get_new_preview (GimpViewable *viewable,
519                             GimpContext  *context,
520                             gint          width,
521                             gint          height)
522 {
523   return NULL;
524 }
525 
526 static gchar *
gimp_curve_get_description(GimpViewable * viewable,gchar ** tooltip)527 gimp_curve_get_description (GimpViewable  *viewable,
528                             gchar        **tooltip)
529 {
530   GimpCurve *curve = GIMP_CURVE (viewable);
531 
532   return g_strdup_printf ("%s", gimp_object_get_name (curve));
533 }
534 
535 static void
gimp_curve_dirty(GimpData * data)536 gimp_curve_dirty (GimpData *data)
537 {
538   GimpCurve *curve = GIMP_CURVE (data);
539 
540   curve->identity = FALSE;
541 
542   gimp_curve_calculate (curve);
543 
544   GIMP_DATA_CLASS (parent_class)->dirty (data);
545 }
546 
547 static const gchar *
gimp_curve_get_extension(GimpData * data)548 gimp_curve_get_extension (GimpData *data)
549 {
550   return GIMP_CURVE_FILE_EXTENSION;
551 }
552 
553 static void
gimp_curve_data_copy(GimpData * data,GimpData * src_data)554 gimp_curve_data_copy (GimpData *data,
555                       GimpData *src_data)
556 {
557   gimp_data_freeze (data);
558 
559   gimp_config_copy (GIMP_CONFIG (src_data),
560                     GIMP_CONFIG (data), 0);
561 
562   gimp_data_thaw (data);
563 }
564 
565 static gboolean
gimp_curve_serialize(GimpConfig * config,GimpConfigWriter * writer,gpointer data)566 gimp_curve_serialize (GimpConfig       *config,
567                       GimpConfigWriter *writer,
568                       gpointer          data)
569 {
570   return gimp_config_serialize_properties (config, writer);
571 }
572 
573 static gboolean
gimp_curve_deserialize(GimpConfig * config,GScanner * scanner,gint nest_level,gpointer data)574 gimp_curve_deserialize (GimpConfig *config,
575                         GScanner   *scanner,
576                         gint        nest_level,
577                         gpointer    data)
578 {
579   gboolean success;
580 
581   success = gimp_config_deserialize_properties (config, scanner, nest_level);
582 
583   GIMP_CURVE (config)->identity = FALSE;
584 
585   return success;
586 }
587 
588 static gboolean
gimp_curve_equal(GimpConfig * a,GimpConfig * b)589 gimp_curve_equal (GimpConfig *a,
590                   GimpConfig *b)
591 {
592   GimpCurve *a_curve = GIMP_CURVE (a);
593   GimpCurve *b_curve = GIMP_CURVE (b);
594 
595   if (a_curve->curve_type != b_curve->curve_type)
596     return FALSE;
597 
598   if (a_curve->n_points != b_curve->n_points ||
599       memcmp (a_curve->points, b_curve->points,
600               sizeof (GimpCurvePoint) * a_curve->n_points))
601     {
602       return FALSE;
603     }
604 
605   if (a_curve->n_samples != b_curve->n_samples ||
606       memcmp (a_curve->samples, b_curve->samples,
607               sizeof (gdouble) * a_curve->n_samples))
608     {
609       return FALSE;
610     }
611 
612   return TRUE;
613 }
614 
615 static void
_gimp_curve_reset(GimpConfig * config)616 _gimp_curve_reset (GimpConfig *config)
617 {
618   gimp_curve_reset (GIMP_CURVE (config), TRUE);
619 }
620 
621 static gboolean
gimp_curve_config_copy(GimpConfig * src,GimpConfig * dest,GParamFlags flags)622 gimp_curve_config_copy (GimpConfig  *src,
623                         GimpConfig  *dest,
624                         GParamFlags  flags)
625 {
626   GimpCurve *src_curve  = GIMP_CURVE (src);
627   GimpCurve *dest_curve = GIMP_CURVE (dest);
628 
629   /* make sure the curve type is copied *before* the points, so that we don't
630    * overwrite the copied points when changing the type
631    */
632   dest_curve->curve_type = src_curve->curve_type;
633 
634   gimp_config_sync (G_OBJECT (src), G_OBJECT (dest), flags);
635 
636   dest_curve->identity = src_curve->identity;
637 
638   gimp_data_dirty (GIMP_DATA (dest));
639 
640   return TRUE;
641 }
642 
643 
644 /*  public functions  */
645 
646 GimpData *
gimp_curve_new(const gchar * name)647 gimp_curve_new (const gchar *name)
648 {
649   g_return_val_if_fail (name != NULL, NULL);
650   g_return_val_if_fail (*name != '\0', NULL);
651 
652   return g_object_new (GIMP_TYPE_CURVE,
653                        "name", name,
654                        NULL);
655 }
656 
657 GimpData *
gimp_curve_get_standard(void)658 gimp_curve_get_standard (void)
659 {
660   static GimpData *standard_curve = NULL;
661 
662   if (! standard_curve)
663     {
664       standard_curve = gimp_curve_new ("Standard");
665 
666       gimp_data_clean (standard_curve);
667       gimp_data_make_internal (standard_curve,
668                                "gimp-curve-standard");
669 
670       g_object_ref (standard_curve);
671     }
672 
673   return standard_curve;
674 }
675 
676 void
gimp_curve_reset(GimpCurve * curve,gboolean reset_type)677 gimp_curve_reset (GimpCurve *curve,
678                   gboolean   reset_type)
679 {
680   gint i;
681 
682   g_return_if_fail (GIMP_IS_CURVE (curve));
683 
684   g_object_freeze_notify (G_OBJECT (curve));
685 
686   for (i = 0; i < curve->n_samples; i++)
687     curve->samples[i] = (gdouble) i / (gdouble) (curve->n_samples - 1);
688 
689   g_object_notify (G_OBJECT (curve), "samples");
690 
691   g_free (curve->points);
692 
693   curve->n_points = 2;
694   curve->points   = g_new0 (GimpCurvePoint, 2);
695 
696   curve->points[0].x    = 0.0;
697   curve->points[0].y    = 0.0;
698   curve->points[0].type = GIMP_CURVE_POINT_SMOOTH;
699 
700   curve->points[1].x    = 1.0;
701   curve->points[1].y    = 1.0;
702   curve->points[1].type = GIMP_CURVE_POINT_SMOOTH;
703 
704   g_object_notify (G_OBJECT (curve), "n-points");
705   g_object_notify (G_OBJECT (curve), "points");
706   g_object_notify (G_OBJECT (curve), "point-types");
707 
708   if (reset_type)
709     {
710       curve->curve_type = GIMP_CURVE_SMOOTH;
711       g_object_notify (G_OBJECT (curve), "curve-type");
712     }
713 
714   curve->identity = TRUE;
715 
716   g_object_thaw_notify (G_OBJECT (curve));
717 
718   gimp_data_dirty (GIMP_DATA (curve));
719 }
720 
721 void
gimp_curve_set_curve_type(GimpCurve * curve,GimpCurveType curve_type)722 gimp_curve_set_curve_type (GimpCurve     *curve,
723                            GimpCurveType  curve_type)
724 {
725   g_return_if_fail (GIMP_IS_CURVE (curve));
726 
727   if (curve->curve_type != curve_type)
728     {
729       gimp_data_freeze (GIMP_DATA (curve));
730 
731       g_object_freeze_notify (G_OBJECT (curve));
732 
733       curve->curve_type = curve_type;
734 
735       if (curve_type == GIMP_CURVE_SMOOTH)
736         {
737           gint i;
738 
739           g_free (curve->points);
740 
741           /*  pick some points from the curve and make them control
742            *  points
743            */
744           curve->n_points = 9;
745           curve->points   = g_new0 (GimpCurvePoint, 9);
746 
747           for (i = 0; i < curve->n_points; i++)
748             {
749               gint sample = i * (curve->n_samples - 1) / (curve->n_points - 1);
750 
751               curve->points[i].x    = (gdouble) sample /
752                                       (gdouble) (curve->n_samples - 1);
753               curve->points[i].y    = curve->samples[sample];
754               curve->points[i].type = GIMP_CURVE_POINT_SMOOTH;
755             }
756 
757           g_object_notify (G_OBJECT (curve), "n-points");
758           g_object_notify (G_OBJECT (curve), "points");
759           g_object_notify (G_OBJECT (curve), "point-types");
760         }
761       else
762         {
763           gimp_curve_clear_points (curve);
764         }
765 
766       g_object_notify (G_OBJECT (curve), "curve-type");
767 
768       g_object_thaw_notify (G_OBJECT (curve));
769 
770       gimp_data_thaw (GIMP_DATA (curve));
771     }
772 }
773 
774 GimpCurveType
gimp_curve_get_curve_type(GimpCurve * curve)775 gimp_curve_get_curve_type (GimpCurve *curve)
776 {
777   g_return_val_if_fail (GIMP_IS_CURVE (curve), GIMP_CURVE_SMOOTH);
778 
779   return curve->curve_type;
780 }
781 
782 gint
gimp_curve_get_n_points(GimpCurve * curve)783 gimp_curve_get_n_points (GimpCurve *curve)
784 {
785   g_return_val_if_fail (GIMP_IS_CURVE (curve), 0);
786 
787   return curve->n_points;
788 }
789 
790 void
gimp_curve_set_n_samples(GimpCurve * curve,gint n_samples)791 gimp_curve_set_n_samples (GimpCurve *curve,
792                           gint       n_samples)
793 {
794   g_return_if_fail (GIMP_IS_CURVE (curve));
795   g_return_if_fail (n_samples >= 256);
796   g_return_if_fail (n_samples <= 4096);
797 
798   if (n_samples != curve->n_samples)
799     {
800       gint i;
801 
802       g_object_freeze_notify (G_OBJECT (curve));
803 
804       curve->n_samples = n_samples;
805       g_object_notify (G_OBJECT (curve), "n-samples");
806 
807       curve->samples = g_renew (gdouble, curve->samples, curve->n_samples);
808 
809       for (i = 0; i < curve->n_samples; i++)
810         curve->samples[i] = (gdouble) i / (gdouble) (curve->n_samples - 1);
811 
812       g_object_notify (G_OBJECT (curve), "samples");
813 
814       if (curve->curve_type == GIMP_CURVE_FREE)
815         curve->identity = TRUE;
816 
817       g_object_thaw_notify (G_OBJECT (curve));
818     }
819 }
820 
821 gint
gimp_curve_get_n_samples(GimpCurve * curve)822 gimp_curve_get_n_samples (GimpCurve *curve)
823 {
824   g_return_val_if_fail (GIMP_IS_CURVE (curve), 0);
825 
826   return curve->n_samples;
827 }
828 
829 gint
gimp_curve_get_point_at(GimpCurve * curve,gdouble x)830 gimp_curve_get_point_at (GimpCurve *curve,
831                          gdouble    x)
832 {
833   gint    closest_point = -1;
834   gdouble distance      = EPSILON;
835   gint    i;
836 
837   g_return_val_if_fail (GIMP_IS_CURVE (curve), -1);
838 
839   for (i = 0; i < curve->n_points; i++)
840     {
841       gdouble point_distance;
842 
843       point_distance = fabs (x - curve->points[i].x);
844 
845       if (point_distance <= distance)
846         {
847           closest_point = i;
848           distance      = point_distance;
849         }
850     }
851 
852   return closest_point;
853 }
854 
855 gint
gimp_curve_get_closest_point(GimpCurve * curve,gdouble x,gdouble y,gdouble max_distance)856 gimp_curve_get_closest_point (GimpCurve *curve,
857                               gdouble    x,
858                               gdouble    y,
859                               gdouble    max_distance)
860 {
861   gint    closest_point = -1;
862   gdouble distance2     = G_MAXDOUBLE;
863   gint    i;
864 
865   g_return_val_if_fail (GIMP_IS_CURVE (curve), -1);
866 
867   if (max_distance >= 0.0)
868     distance2 = SQR (max_distance);
869 
870   for (i = curve->n_points - 1; i >= 0; i--)
871     {
872       gdouble point_distance2;
873 
874       point_distance2 = SQR (x - curve->points[i].x) +
875                         SQR (y - curve->points[i].y);
876 
877       if (point_distance2 <= distance2)
878         {
879           closest_point = i;
880           distance2     = point_distance2;
881         }
882     }
883 
884   return closest_point;
885 }
886 
887 gint
gimp_curve_add_point(GimpCurve * curve,gdouble x,gdouble y)888 gimp_curve_add_point (GimpCurve *curve,
889                       gdouble    x,
890                       gdouble    y)
891 {
892   GimpCurvePoint *points;
893   gint            point;
894 
895   g_return_val_if_fail (GIMP_IS_CURVE (curve), -1);
896 
897   if (curve->curve_type == GIMP_CURVE_FREE)
898     return -1;
899 
900   x = CLAMP (x, 0.0, 1.0);
901   y = CLAMP (y, 0.0, 1.0);
902 
903   for (point = 0; point < curve->n_points; point++)
904     {
905       if (curve->points[point].x > x)
906         break;
907     }
908 
909   points = g_new0 (GimpCurvePoint, curve->n_points + 1);
910 
911   memcpy (points,         curve->points,
912           point * sizeof (GimpCurvePoint));
913   memcpy (points + point + 1, curve->points + point,
914           (curve->n_points - point) * sizeof (GimpCurvePoint));
915 
916   points[point].x    = x;
917   points[point].y    = y;
918   points[point].type = GIMP_CURVE_POINT_SMOOTH;
919 
920   g_free (curve->points);
921 
922   curve->n_points++;
923   curve->points = points;
924 
925   g_object_notify (G_OBJECT (curve), "n-points");
926   g_object_notify (G_OBJECT (curve), "points");
927   g_object_notify (G_OBJECT (curve), "point-types");
928 
929   gimp_data_dirty (GIMP_DATA (curve));
930 
931   return point;
932 }
933 
934 void
gimp_curve_delete_point(GimpCurve * curve,gint point)935 gimp_curve_delete_point (GimpCurve *curve,
936                          gint       point)
937 {
938   GimpCurvePoint *points;
939 
940   g_return_if_fail (GIMP_IS_CURVE (curve));
941   g_return_if_fail (point >= 0 && point < curve->n_points);
942 
943   points = g_new0 (GimpCurvePoint, curve->n_points - 1);
944 
945   memcpy (points,         curve->points,
946           point * sizeof (GimpCurvePoint));
947   memcpy (points + point, curve->points + point + 1,
948           (curve->n_points - point - 1) * sizeof (GimpCurvePoint));
949 
950   g_free (curve->points);
951 
952   curve->n_points--;
953   curve->points = points;
954 
955   g_object_notify (G_OBJECT (curve), "n-points");
956   g_object_notify (G_OBJECT (curve), "points");
957   g_object_notify (G_OBJECT (curve), "point-types");
958 
959   gimp_data_dirty (GIMP_DATA (curve));
960 }
961 
962 void
gimp_curve_set_point(GimpCurve * curve,gint point,gdouble x,gdouble y)963 gimp_curve_set_point (GimpCurve *curve,
964                       gint       point,
965                       gdouble    x,
966                       gdouble    y)
967 {
968   g_return_if_fail (GIMP_IS_CURVE (curve));
969   g_return_if_fail (point >= 0 && point < curve->n_points);
970 
971   curve->points[point].x = CLAMP (x, 0.0, 1.0);
972   curve->points[point].y = CLAMP (y, 0.0, 1.0);
973 
974   if (point > 0)
975     curve->points[point].x = MAX (x, curve->points[point - 1].x);
976 
977   if (point < curve->n_points - 1)
978     curve->points[point].x = MIN (x, curve->points[point + 1].x);
979 
980   g_object_notify (G_OBJECT (curve), "points");
981 
982   gimp_data_dirty (GIMP_DATA (curve));
983 }
984 
985 void
gimp_curve_move_point(GimpCurve * curve,gint point,gdouble y)986 gimp_curve_move_point (GimpCurve *curve,
987                        gint       point,
988                        gdouble    y)
989 {
990   g_return_if_fail (GIMP_IS_CURVE (curve));
991   g_return_if_fail (point >= 0 && point < curve->n_points);
992 
993   curve->points[point].y = CLAMP (y, 0.0, 1.0);
994 
995   g_object_notify (G_OBJECT (curve), "points");
996 
997   gimp_data_dirty (GIMP_DATA (curve));
998 }
999 
1000 void
gimp_curve_get_point(GimpCurve * curve,gint point,gdouble * x,gdouble * y)1001 gimp_curve_get_point (GimpCurve *curve,
1002                       gint       point,
1003                       gdouble   *x,
1004                       gdouble   *y)
1005 {
1006   g_return_if_fail (GIMP_IS_CURVE (curve));
1007   g_return_if_fail (point >= 0 && point < curve->n_points);
1008 
1009   if (x) *x = curve->points[point].x;
1010   if (y) *y = curve->points[point].y;
1011 }
1012 
1013 void
gimp_curve_set_point_type(GimpCurve * curve,gint point,GimpCurvePointType type)1014 gimp_curve_set_point_type (GimpCurve          *curve,
1015                            gint                point,
1016                            GimpCurvePointType  type)
1017 {
1018   g_return_if_fail (GIMP_IS_CURVE (curve));
1019   g_return_if_fail (point >= 0 && point < curve->n_points);
1020 
1021   curve->points[point].type = type;
1022 
1023   g_object_notify (G_OBJECT (curve), "point-types");
1024 
1025   gimp_data_dirty (GIMP_DATA (curve));
1026 }
1027 
1028 GimpCurvePointType
gimp_curve_get_point_type(GimpCurve * curve,gint point)1029 gimp_curve_get_point_type (GimpCurve *curve,
1030                            gint       point)
1031 {
1032   g_return_val_if_fail (GIMP_IS_CURVE (curve), GIMP_CURVE_POINT_SMOOTH);
1033   g_return_val_if_fail (point >= 0 && point < curve->n_points, GIMP_CURVE_POINT_SMOOTH);
1034 
1035   return curve->points[point].type;
1036 }
1037 
1038 void
gimp_curve_clear_points(GimpCurve * curve)1039 gimp_curve_clear_points (GimpCurve *curve)
1040 {
1041   g_return_if_fail (GIMP_IS_CURVE (curve));
1042 
1043   if (curve->points)
1044     {
1045       g_clear_pointer (&curve->points, g_free);
1046       curve->n_points = 0;
1047 
1048       g_object_notify (G_OBJECT (curve), "n-points");
1049       g_object_notify (G_OBJECT (curve), "points");
1050       g_object_notify (G_OBJECT (curve), "point-types");
1051 
1052       gimp_data_dirty (GIMP_DATA (curve));
1053     }
1054 }
1055 
1056 void
gimp_curve_set_curve(GimpCurve * curve,gdouble x,gdouble y)1057 gimp_curve_set_curve (GimpCurve *curve,
1058                       gdouble    x,
1059                       gdouble    y)
1060 {
1061   g_return_if_fail (GIMP_IS_CURVE (curve));
1062   g_return_if_fail (x >= 0 && x <= 1.0);
1063   g_return_if_fail (y >= 0 && y <= 1.0);
1064 
1065   if (curve->curve_type == GIMP_CURVE_SMOOTH)
1066     return;
1067 
1068   curve->samples[ROUND (x * (gdouble) (curve->n_samples - 1))] = y;
1069 
1070   g_object_notify (G_OBJECT (curve), "samples");
1071 
1072   gimp_data_dirty (GIMP_DATA (curve));
1073 }
1074 
1075 /**
1076  * gimp_curve_is_identity:
1077  * @curve: a #GimpCurve object
1078  *
1079  * If this function returns %TRUE, then the curve maps each value to
1080  * itself. If it returns %FALSE, then this assumption can not be made.
1081  *
1082  * Return value: %TRUE if the curve is an identity mapping, %FALSE otherwise.
1083  **/
1084 gboolean
gimp_curve_is_identity(GimpCurve * curve)1085 gimp_curve_is_identity (GimpCurve *curve)
1086 {
1087   g_return_val_if_fail (GIMP_IS_CURVE (curve), FALSE);
1088 
1089   return curve->identity;
1090 }
1091 
1092 void
gimp_curve_get_uchar(GimpCurve * curve,gint n_samples,guchar * samples)1093 gimp_curve_get_uchar (GimpCurve *curve,
1094                       gint       n_samples,
1095                       guchar    *samples)
1096 {
1097   gint i;
1098 
1099   g_return_if_fail (GIMP_IS_CURVE (curve));
1100   /* FIXME: support n_samples != curve->n_samples */
1101   g_return_if_fail (n_samples == curve->n_samples);
1102   g_return_if_fail (samples != NULL);
1103 
1104   for (i = 0; i < curve->n_samples; i++)
1105     samples[i] = curve->samples[i] * 255.999;
1106 }
1107 
1108 
1109 /*  private functions  */
1110 
1111 static void
gimp_curve_calculate(GimpCurve * curve)1112 gimp_curve_calculate (GimpCurve *curve)
1113 {
1114   gint i;
1115   gint p1, p2, p3, p4;
1116 
1117   if (gimp_data_is_frozen (GIMP_DATA (curve)))
1118     return;
1119 
1120   switch (curve->curve_type)
1121     {
1122     case GIMP_CURVE_SMOOTH:
1123       /*  Initialize boundary curve points */
1124       if (curve->n_points > 0)
1125         {
1126           GimpCurvePoint point;
1127           gint           boundary;
1128 
1129           point    = curve->points[0];
1130           boundary = ROUND (point.x * (gdouble) (curve->n_samples - 1));
1131 
1132           for (i = 0; i < boundary; i++)
1133             curve->samples[i] = point.y;
1134 
1135           point    = curve->points[curve->n_points - 1];
1136           boundary = ROUND (point.x * (gdouble) (curve->n_samples - 1));
1137 
1138           for (i = boundary; i < curve->n_samples; i++)
1139             curve->samples[i] = point.y;
1140         }
1141 
1142       for (i = 0; i < curve->n_points - 1; i++)
1143         {
1144           p1 = MAX (i - 1, 0);
1145           p2 = i;
1146           p3 = i + 1;
1147           p4 = MIN (i + 2, curve->n_points - 1);
1148 
1149           if (curve->points[p2].type == GIMP_CURVE_POINT_CORNER)
1150             p1 = p2;
1151 
1152           if (curve->points[p3].type == GIMP_CURVE_POINT_CORNER)
1153             p4 = p3;
1154 
1155           gimp_curve_plot (curve, p1, p2, p3, p4);
1156         }
1157 
1158       /* ensure that the control points are used exactly */
1159       for (i = 0; i < curve->n_points; i++)
1160         {
1161           gdouble x = curve->points[i].x;
1162           gdouble y = curve->points[i].y;
1163 
1164           curve->samples[ROUND (x * (gdouble) (curve->n_samples - 1))] = y;
1165         }
1166 
1167       g_object_notify (G_OBJECT (curve), "samples");
1168       break;
1169 
1170     case GIMP_CURVE_FREE:
1171       break;
1172     }
1173 }
1174 
1175 /*
1176  * This function calculates the curve values between the control points
1177  * p2 and p3, taking the potentially existing neighbors p1 and p4 into
1178  * account.
1179  *
1180  * This function uses a cubic bezier curve for the individual segments and
1181  * calculates the necessary intermediate control points depending on the
1182  * neighbor curve control points.
1183  */
1184 static void
gimp_curve_plot(GimpCurve * curve,gint p1,gint p2,gint p3,gint p4)1185 gimp_curve_plot (GimpCurve *curve,
1186                  gint       p1,
1187                  gint       p2,
1188                  gint       p3,
1189                  gint       p4)
1190 {
1191   gint    i;
1192   gdouble x0, x3;
1193   gdouble y0, y1, y2, y3;
1194   gdouble dx, dy;
1195   gdouble slope;
1196 
1197   /* the outer control points for the bezier curve. */
1198   x0 = curve->points[p2].x;
1199   y0 = curve->points[p2].y;
1200   x3 = curve->points[p3].x;
1201   y3 = curve->points[p3].y;
1202 
1203   /*
1204    * the x values of the inner control points are fixed at
1205    * x1 = 2/3*x0 + 1/3*x3   and  x2 = 1/3*x0 + 2/3*x3
1206    * this ensures that the x values increase linearly with the
1207    * parameter t and enables us to skip the calculation of the x
1208    * values altogether - just calculate y(t) evenly spaced.
1209    */
1210 
1211   dx = x3 - x0;
1212   dy = y3 - y0;
1213 
1214   if (dx <= EPSILON)
1215     {
1216       gint index;
1217 
1218       index = ROUND (x0 * (gdouble) (curve->n_samples - 1));
1219 
1220       curve->samples[index] = y3;
1221 
1222       return;
1223     }
1224 
1225   if (p1 == p2 && p3 == p4)
1226     {
1227       /* No information about the neighbors,
1228        * calculate y1 and y2 to get a straight line
1229        */
1230       y1 = y0 + dy / 3.0;
1231       y2 = y0 + dy * 2.0 / 3.0;
1232     }
1233   else if (p1 == p2 && p3 != p4)
1234     {
1235       /* only the right neighbor is available. Make the tangent at the
1236        * right endpoint parallel to the line between the left endpoint
1237        * and the right neighbor. Then point the tangent at the left towards
1238        * the control handle of the right tangent, to ensure that the curve
1239        * does not have an inflection point.
1240        */
1241       slope = (curve->points[p4].y - y0) / (curve->points[p4].x - x0);
1242 
1243       y2 = y3 - slope * dx / 3.0;
1244       y1 = y0 + (y2 - y0) / 2.0;
1245     }
1246   else if (p1 != p2 && p3 == p4)
1247     {
1248       /* see previous case */
1249       slope = (y3 - curve->points[p1].y) / (x3 - curve->points[p1].x);
1250 
1251       y1 = y0 + slope * dx / 3.0;
1252       y2 = y3 + (y1 - y3) / 2.0;
1253     }
1254   else /* (p1 != p2 && p3 != p4) */
1255     {
1256       /* Both neighbors are available. Make the tangents at the endpoints
1257        * parallel to the line between the opposite endpoint and the adjacent
1258        * neighbor.
1259        */
1260       slope = (y3 - curve->points[p1].y) / (x3 - curve->points[p1].x);
1261 
1262       y1 = y0 + slope * dx / 3.0;
1263 
1264       slope = (curve->points[p4].y - y0) / (curve->points[p4].x - x0);
1265 
1266       y2 = y3 - slope * dx / 3.0;
1267     }
1268 
1269   /*
1270    * finally calculate the y(t) values for the given bezier values. We can
1271    * use homogeneously distributed values for t, since x(t) increases linearly.
1272    */
1273   for (i = 0; i <= ROUND (dx * (gdouble) (curve->n_samples - 1)); i++)
1274     {
1275       gdouble y, t;
1276       gint    index;
1277 
1278       t = i / dx / (gdouble) (curve->n_samples - 1);
1279       y =     y0 * (1-t) * (1-t) * (1-t) +
1280           3 * y1 * (1-t) * (1-t) * t     +
1281           3 * y2 * (1-t) * t     * t     +
1282               y3 * t     * t     * t;
1283 
1284       index = i + ROUND (x0 * (gdouble) (curve->n_samples - 1));
1285 
1286       if (index < curve->n_samples)
1287         curve->samples[index] = CLAMP (y, 0.0, 1.0);
1288     }
1289 }
1290