~ubuntu-branches/ubuntu/jaunty/gimp/jaunty-security

« back to all changes in this revision

Viewing changes to app/core/gimpcurve.c

  • Committer: Bazaar Package Importer
  • Author(s): Sebastien Bacher
  • Date: 2008-10-06 13:30:41 UTC
  • mto: This revision was merged to the branch mainline in revision 35.
  • Revision ID: james.westby@ubuntu.com-20081006133041-3panbkcanaymfsmp
Tags: upstream-2.6.0
ImportĀ upstreamĀ versionĀ 2.6.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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 2 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, write to the Free Software
 
16
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 
17
 */
 
18
 
 
19
#include "config.h"
 
20
 
 
21
#include <stdlib.h>
 
22
 
 
23
#include <glib-object.h>
 
24
 
 
25
#include "libgimpmath/gimpmath.h"
 
26
#include "libgimpconfig/gimpconfig.h"
 
27
 
 
28
#include "core-types.h"
 
29
 
 
30
#include "gimpcurve.h"
 
31
#include "gimpcurve-load.h"
 
32
#include "gimpcurve-save.h"
 
33
 
 
34
#include "gimp-intl.h"
 
35
 
 
36
 
 
37
enum
 
38
{
 
39
  PROP_0,
 
40
  PROP_CURVE_TYPE,
 
41
  PROP_N_POINTS,
 
42
  PROP_POINTS,
 
43
  PROP_N_SAMPLES,
 
44
  PROP_SAMPLES
 
45
};
 
46
 
 
47
 
 
48
/*  local function prototypes  */
 
49
 
 
50
static void          gimp_curve_config_iface_init (GimpConfigInterface *iface);
 
51
 
 
52
static void          gimp_curve_finalize          (GObject          *object);
 
53
static void          gimp_curve_set_property      (GObject          *object,
 
54
                                                   guint             property_id,
 
55
                                                   const GValue     *value,
 
56
                                                   GParamSpec       *pspec);
 
57
static void          gimp_curve_get_property      (GObject          *object,
 
58
                                                   guint             property_id,
 
59
                                                   GValue           *value,
 
60
                                                   GParamSpec       *pspec);
 
61
 
 
62
static gint64        gimp_curve_get_memsize       (GimpObject       *object,
 
63
                                                   gint64           *gui_size);
 
64
 
 
65
static void          gimp_curve_get_preview_size  (GimpViewable     *viewable,
 
66
                                                   gint              size,
 
67
                                                   gboolean          popup,
 
68
                                                   gboolean          dot_for_dot,
 
69
                                                   gint             *width,
 
70
                                                   gint             *height);
 
71
static gboolean      gimp_curve_get_popup_size    (GimpViewable     *viewable,
 
72
                                                   gint              width,
 
73
                                                   gint              height,
 
74
                                                   gboolean          dot_for_dot,
 
75
                                                   gint             *popup_width,
 
76
                                                   gint             *popup_height);
 
77
static TempBuf     * gimp_curve_get_new_preview   (GimpViewable     *viewable,
 
78
                                                   GimpContext      *context,
 
79
                                                   gint              width,
 
80
                                                   gint              height);
 
81
static gchar       * gimp_curve_get_description   (GimpViewable     *viewable,
 
82
                                                   gchar           **tooltip);
 
83
 
 
84
static void          gimp_curve_dirty             (GimpData         *data);
 
85
static const gchar * gimp_curve_get_extension     (GimpData         *data);
 
86
static GimpData    * gimp_curve_duplicate         (GimpData         *data);
 
87
 
 
88
static gboolean      gimp_curve_serialize         (GimpConfig       *config,
 
89
                                                   GimpConfigWriter *writer,
 
90
                                                   gpointer          data);
 
91
static gboolean      gimp_curve_deserialize       (GimpConfig       *config,
 
92
                                                   GScanner         *scanner,
 
93
                                                   gint              nest_level,
 
94
                                                   gpointer          data);
 
95
static gboolean      gimp_curve_equal             (GimpConfig       *a,
 
96
                                                   GimpConfig       *b);
 
97
static void          _gimp_curve_reset            (GimpConfig       *config);
 
98
static gboolean      gimp_curve_copy              (GimpConfig       *src,
 
99
                                                   GimpConfig       *dest,
 
100
                                                   GParamFlags       flags);
 
101
 
 
102
static void          gimp_curve_set_n_points      (GimpCurve        *curve,
 
103
                                                   gint              n_points);
 
104
static void          gimp_curve_set_n_samples     (GimpCurve        *curve,
 
105
                                                   gint              n_samples);
 
106
 
 
107
static void          gimp_curve_calculate         (GimpCurve        *curve);
 
108
static void          gimp_curve_plot              (GimpCurve        *curve,
 
109
                                                   gint              p1,
 
110
                                                   gint              p2,
 
111
                                                   gint              p3,
 
112
                                                   gint              p4);
 
113
 
 
114
 
 
115
G_DEFINE_TYPE_WITH_CODE (GimpCurve, gimp_curve, GIMP_TYPE_DATA,
 
116
                         G_IMPLEMENT_INTERFACE (GIMP_TYPE_CONFIG,
 
117
                                                gimp_curve_config_iface_init))
 
118
 
 
119
#define parent_class gimp_curve_parent_class
 
120
 
 
121
 
 
122
static void
 
123
gimp_curve_class_init (GimpCurveClass *klass)
 
124
{
 
125
  GObjectClass      *object_class      = G_OBJECT_CLASS (klass);
 
126
  GimpObjectClass   *gimp_object_class = GIMP_OBJECT_CLASS (klass);
 
127
  GimpViewableClass *viewable_class    = GIMP_VIEWABLE_CLASS (klass);
 
128
  GimpDataClass     *data_class        = GIMP_DATA_CLASS (klass);
 
129
  GParamSpec        *array_spec;
 
130
 
 
131
  object_class->finalize           = gimp_curve_finalize;
 
132
  object_class->set_property       = gimp_curve_set_property;
 
133
  object_class->get_property       = gimp_curve_get_property;
 
134
 
 
135
  gimp_object_class->get_memsize   = gimp_curve_get_memsize;
 
136
 
 
137
  viewable_class->default_stock_id = "FIXME";
 
138
  viewable_class->get_preview_size = gimp_curve_get_preview_size;
 
139
  viewable_class->get_popup_size   = gimp_curve_get_popup_size;
 
140
  viewable_class->get_new_preview  = gimp_curve_get_new_preview;
 
141
  viewable_class->get_description  = gimp_curve_get_description;
 
142
 
 
143
  data_class->dirty                = gimp_curve_dirty;
 
144
  data_class->save                 = gimp_curve_save;
 
145
  data_class->get_extension        = gimp_curve_get_extension;
 
146
  data_class->duplicate            = gimp_curve_duplicate;
 
147
 
 
148
  GIMP_CONFIG_INSTALL_PROP_ENUM (object_class, PROP_CURVE_TYPE,
 
149
                                 "curve-type",
 
150
                                 "The curve type",
 
151
                                 GIMP_TYPE_CURVE_TYPE,
 
152
                                 GIMP_CURVE_SMOOTH, 0);
 
153
 
 
154
  GIMP_CONFIG_INSTALL_PROP_INT (object_class, PROP_N_POINTS,
 
155
                                "n-points",
 
156
                                "The number of points",
 
157
                                17, 17, 17, 0);
 
158
 
 
159
  array_spec = g_param_spec_double ("point", NULL, NULL,
 
160
                                    -1.0, 1.0, 0.0, GIMP_PARAM_READWRITE);
 
161
  g_object_class_install_property (object_class, PROP_POINTS,
 
162
                                   g_param_spec_value_array ("points",
 
163
                                                             NULL, NULL,
 
164
                                                             array_spec,
 
165
                                                             GIMP_PARAM_STATIC_STRINGS |
 
166
                                                             GIMP_CONFIG_PARAM_FLAGS));
 
167
 
 
168
  GIMP_CONFIG_INSTALL_PROP_INT  (object_class, PROP_N_SAMPLES,
 
169
                                 "n-samples",
 
170
                                 "The number of samples",
 
171
                                 256, 256, 256, 0);
 
172
 
 
173
  array_spec = g_param_spec_double ("sample", NULL, NULL,
 
174
                                    0.0, 1.0, 0.0, GIMP_PARAM_READWRITE);
 
175
  g_object_class_install_property (object_class, PROP_SAMPLES,
 
176
                                   g_param_spec_value_array ("samples",
 
177
                                                             NULL, NULL,
 
178
                                                             array_spec,
 
179
                                                             GIMP_PARAM_STATIC_STRINGS |
 
180
                                                             GIMP_CONFIG_PARAM_FLAGS));
 
181
}
 
182
 
 
183
static void
 
184
gimp_curve_config_iface_init (GimpConfigInterface *iface)
 
185
{
 
186
  iface->serialize   = gimp_curve_serialize;
 
187
  iface->deserialize = gimp_curve_deserialize;
 
188
  iface->equal       = gimp_curve_equal;
 
189
  iface->reset       = _gimp_curve_reset;
 
190
  iface->copy        = gimp_curve_copy;
 
191
}
 
192
 
 
193
static void
 
194
gimp_curve_init (GimpCurve *curve)
 
195
{
 
196
  curve->n_points  = 0;
 
197
  curve->points    = NULL;
 
198
  curve->n_samples = 0;
 
199
  curve->samples   = NULL;
 
200
  curve->identity  = FALSE;
 
201
}
 
202
 
 
203
static void
 
204
gimp_curve_finalize (GObject *object)
 
205
{
 
206
  GimpCurve *curve = GIMP_CURVE (object);
 
207
 
 
208
  if (curve->points)
 
209
    {
 
210
      g_free (curve->points);
 
211
      curve->points = NULL;
 
212
    }
 
213
 
 
214
  if (curve->samples)
 
215
    {
 
216
      g_free (curve->samples);
 
217
      curve->samples = NULL;
 
218
    }
 
219
 
 
220
  G_OBJECT_CLASS (parent_class)->finalize (object);
 
221
}
 
222
 
 
223
static void
 
224
gimp_curve_set_property (GObject      *object,
 
225
                         guint         property_id,
 
226
                         const GValue *value,
 
227
                         GParamSpec   *pspec)
 
228
{
 
229
  GimpCurve *curve = GIMP_CURVE (object);
 
230
 
 
231
  switch (property_id)
 
232
    {
 
233
    case PROP_CURVE_TYPE:
 
234
      gimp_curve_set_curve_type (curve, g_value_get_enum (value));
 
235
      break;
 
236
 
 
237
    case PROP_N_POINTS:
 
238
      gimp_curve_set_n_points (curve, g_value_get_int (value));
 
239
      break;
 
240
 
 
241
    case PROP_POINTS:
 
242
      {
 
243
        GValueArray *array = g_value_get_boxed (value);
 
244
        gint         i;
 
245
 
 
246
        if (! array)
 
247
          break;
 
248
 
 
249
        for (i = 0; i < curve->n_points && i * 2 < array->n_values; i++)
 
250
          {
 
251
            GValue *x = g_value_array_get_nth (array, i * 2);
 
252
            GValue *y = g_value_array_get_nth (array, i * 2 + 1);
 
253
 
 
254
            curve->points[i].x = g_value_get_double (x);
 
255
            curve->points[i].y = g_value_get_double (y);
 
256
          }
 
257
      }
 
258
      break;
 
259
 
 
260
    case PROP_N_SAMPLES:
 
261
      gimp_curve_set_n_samples (curve, g_value_get_int (value));
 
262
      break;
 
263
 
 
264
    case PROP_SAMPLES:
 
265
      {
 
266
        GValueArray *array = g_value_get_boxed (value);
 
267
        gint         i;
 
268
 
 
269
        if (! array)
 
270
          break;
 
271
 
 
272
        for (i = 0; i < curve->n_samples && i < array->n_values; i++)
 
273
          {
 
274
            GValue *v = g_value_array_get_nth (array, i);
 
275
 
 
276
            curve->samples[i] = g_value_get_double (v);
 
277
          }
 
278
      }
 
279
      break;
 
280
 
 
281
    default:
 
282
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 
283
      break;
 
284
    }
 
285
}
 
286
 
 
287
static void
 
288
gimp_curve_get_property (GObject    *object,
 
289
                         guint       property_id,
 
290
                         GValue     *value,
 
291
                         GParamSpec *pspec)
 
292
{
 
293
  GimpCurve *curve = GIMP_CURVE (object);
 
294
 
 
295
  switch (property_id)
 
296
    {
 
297
    case PROP_CURVE_TYPE:
 
298
      g_value_set_enum (value, curve->curve_type);
 
299
      break;
 
300
 
 
301
    case PROP_N_POINTS:
 
302
      g_value_set_int (value, curve->n_points);
 
303
      break;
 
304
 
 
305
    case PROP_POINTS:
 
306
      {
 
307
        GValueArray *array = g_value_array_new (curve->n_points * 2);
 
308
        GValue       v     = { 0, };
 
309
        gint         i;
 
310
 
 
311
        g_value_init (&v, G_TYPE_DOUBLE);
 
312
 
 
313
        for (i = 0; i < curve->n_points; i++)
 
314
          {
 
315
            g_value_set_double (&v, curve->points[i].x);
 
316
            g_value_array_append (array, &v);
 
317
 
 
318
            g_value_set_double (&v, curve->points[i].y);
 
319
            g_value_array_append (array, &v);
 
320
          }
 
321
 
 
322
        g_value_unset (&v);
 
323
 
 
324
        g_value_take_boxed (value, array);
 
325
      }
 
326
      break;
 
327
 
 
328
    case PROP_N_SAMPLES:
 
329
      g_value_set_int (value, curve->n_samples);
 
330
      break;
 
331
 
 
332
    case PROP_SAMPLES:
 
333
      {
 
334
        GValueArray *array = g_value_array_new (curve->n_samples);
 
335
        GValue       v     = { 0, };
 
336
        gint         i;
 
337
 
 
338
        g_value_init (&v, G_TYPE_DOUBLE);
 
339
 
 
340
        for (i = 0; i < curve->n_samples; i++)
 
341
          {
 
342
            g_value_set_double (&v, curve->samples[i]);
 
343
            g_value_array_append (array, &v);
 
344
          }
 
345
 
 
346
        g_value_unset (&v);
 
347
 
 
348
        g_value_take_boxed (value, array);
 
349
      }
 
350
      break;
 
351
 
 
352
    default:
 
353
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 
354
      break;
 
355
    }
 
356
}
 
357
 
 
358
static gint64
 
359
gimp_curve_get_memsize (GimpObject *object,
 
360
                        gint64     *gui_size)
 
361
{
 
362
  GimpCurve *curve   = GIMP_CURVE (object);
 
363
  gint64     memsize = 0;
 
364
 
 
365
  memsize += curve->n_points  * sizeof (GimpVector2);
 
366
  memsize += curve->n_samples * sizeof (gdouble);
 
367
 
 
368
  return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object,
 
369
                                                                  gui_size);
 
370
}
 
371
 
 
372
static void
 
373
gimp_curve_get_preview_size (GimpViewable *viewable,
 
374
                             gint          size,
 
375
                             gboolean      popup,
 
376
                             gboolean      dot_for_dot,
 
377
                             gint         *width,
 
378
                             gint         *height)
 
379
{
 
380
  *width  = size;
 
381
  *height = size;
 
382
}
 
383
 
 
384
static gboolean
 
385
gimp_curve_get_popup_size (GimpViewable *viewable,
 
386
                           gint          width,
 
387
                           gint          height,
 
388
                           gboolean      dot_for_dot,
 
389
                           gint         *popup_width,
 
390
                           gint         *popup_height)
 
391
{
 
392
  *popup_width  = width  * 2;
 
393
  *popup_height = height * 2;
 
394
 
 
395
  return TRUE;
 
396
}
 
397
 
 
398
static TempBuf *
 
399
gimp_curve_get_new_preview (GimpViewable *viewable,
 
400
                            GimpContext  *context,
 
401
                            gint          width,
 
402
                            gint          height)
 
403
{
 
404
  return NULL;
 
405
}
 
406
 
 
407
static gchar *
 
408
gimp_curve_get_description (GimpViewable  *viewable,
 
409
                            gchar        **tooltip)
 
410
{
 
411
  GimpCurve *curve = GIMP_CURVE (viewable);
 
412
 
 
413
  return g_strdup_printf ("%s", GIMP_OBJECT (curve)->name);
 
414
}
 
415
 
 
416
static void
 
417
gimp_curve_dirty (GimpData *data)
 
418
{
 
419
  GimpCurve *curve = GIMP_CURVE (data);
 
420
 
 
421
  curve->identity = FALSE;
 
422
 
 
423
  gimp_curve_calculate (curve);
 
424
 
 
425
  GIMP_DATA_CLASS (parent_class)->dirty (data);
 
426
}
 
427
 
 
428
static const gchar *
 
429
gimp_curve_get_extension (GimpData *data)
 
430
{
 
431
  return GIMP_CURVE_FILE_EXTENSION;
 
432
}
 
433
 
 
434
static GimpData *
 
435
gimp_curve_duplicate (GimpData *data)
 
436
{
 
437
  GimpCurve *curve = GIMP_CURVE (data);
 
438
  GimpCurve *new;
 
439
 
 
440
  new = g_object_new (GIMP_TYPE_CURVE,
 
441
                      "curve-type", curve->curve_type,
 
442
                      NULL);
 
443
 
 
444
  return GIMP_DATA (new);
 
445
}
 
446
 
 
447
static gboolean
 
448
gimp_curve_serialize (GimpConfig       *config,
 
449
                      GimpConfigWriter *writer,
 
450
                      gpointer          data)
 
451
{
 
452
  return gimp_config_serialize_properties (config, writer);
 
453
}
 
454
 
 
455
static gboolean
 
456
gimp_curve_deserialize (GimpConfig *config,
 
457
                        GScanner   *scanner,
 
458
                        gint        nest_level,
 
459
                        gpointer    data)
 
460
{
 
461
  gboolean success;
 
462
 
 
463
  success = gimp_config_deserialize_properties (config, scanner, nest_level);
 
464
 
 
465
  GIMP_CURVE (config)->identity = FALSE;
 
466
 
 
467
  return success;
 
468
}
 
469
 
 
470
static gboolean
 
471
gimp_curve_equal (GimpConfig *a,
 
472
                  GimpConfig *b)
 
473
{
 
474
  GimpCurve *a_curve = GIMP_CURVE (a);
 
475
  GimpCurve *b_curve = GIMP_CURVE (b);
 
476
 
 
477
  if (a_curve->curve_type != b_curve->curve_type)
 
478
    return FALSE;
 
479
 
 
480
  if (memcmp (a_curve->points, b_curve->points,
 
481
              sizeof (GimpVector2) * b_curve->n_points) ||
 
482
      memcmp (a_curve->samples, b_curve->samples,
 
483
              sizeof (gdouble) * b_curve->n_samples))
 
484
    return FALSE;
 
485
 
 
486
  return TRUE;
 
487
}
 
488
 
 
489
static void
 
490
_gimp_curve_reset (GimpConfig *config)
 
491
{
 
492
  gimp_curve_reset (GIMP_CURVE (config), TRUE);
 
493
}
 
494
 
 
495
static gboolean
 
496
gimp_curve_copy (GimpConfig  *src,
 
497
                 GimpConfig  *dest,
 
498
                 GParamFlags  flags)
 
499
{
 
500
  GimpCurve *src_curve  = GIMP_CURVE (src);
 
501
  GimpCurve *dest_curve = GIMP_CURVE (dest);
 
502
 
 
503
  gimp_config_sync (G_OBJECT (src), G_OBJECT (dest), flags);
 
504
 
 
505
  dest_curve->identity = src_curve->identity;
 
506
 
 
507
  gimp_data_dirty (GIMP_DATA (dest));
 
508
 
 
509
  return TRUE;
 
510
}
 
511
 
 
512
 
 
513
/*  public functions  */
 
514
 
 
515
GimpData *
 
516
gimp_curve_new (const gchar *name)
 
517
{
 
518
  g_return_val_if_fail (name != NULL, NULL);
 
519
  g_return_val_if_fail (*name != '\0', NULL);
 
520
 
 
521
  return g_object_new (GIMP_TYPE_CURVE,
 
522
                       "name", name,
 
523
                       NULL);
 
524
}
 
525
 
 
526
GimpData *
 
527
gimp_curve_get_standard (void)
 
528
{
 
529
  static GimpData *standard_curve = NULL;
 
530
 
 
531
  if (! standard_curve)
 
532
    {
 
533
      standard_curve = gimp_curve_new ("Standard");
 
534
 
 
535
      standard_curve->dirty = FALSE;
 
536
      gimp_data_make_internal (standard_curve);
 
537
 
 
538
      g_object_ref (standard_curve);
 
539
    }
 
540
 
 
541
  return standard_curve;
 
542
}
 
543
 
 
544
void
 
545
gimp_curve_reset (GimpCurve *curve,
 
546
                  gboolean   reset_type)
 
547
{
 
548
  gint i;
 
549
 
 
550
  g_return_if_fail (GIMP_IS_CURVE (curve));
 
551
 
 
552
  g_object_freeze_notify (G_OBJECT (curve));
 
553
 
 
554
  for (i = 0; i < curve->n_samples; i++)
 
555
    curve->samples[i] = (gdouble) i / (gdouble) (curve->n_samples - 1);
 
556
 
 
557
  g_object_notify (G_OBJECT (curve), "samples");
 
558
 
 
559
  curve->points[0].x = 0.0;
 
560
  curve->points[0].y = 0.0;
 
561
 
 
562
  for (i = 1; i < curve->n_points - 1; i++)
 
563
    {
 
564
      curve->points[i].x = -1.0;
 
565
      curve->points[i].y = -1.0;
 
566
    }
 
567
 
 
568
  curve->points[curve->n_points - 1].x = 1.0;
 
569
  curve->points[curve->n_points - 1].y = 1.0;
 
570
 
 
571
  g_object_notify (G_OBJECT (curve), "points");
 
572
 
 
573
  if (reset_type)
 
574
    {
 
575
      curve->curve_type = GIMP_CURVE_SMOOTH;
 
576
      g_object_notify (G_OBJECT (curve), "curve-type");
 
577
    }
 
578
 
 
579
  curve->identity = TRUE;
 
580
 
 
581
  g_object_thaw_notify (G_OBJECT (curve));
 
582
 
 
583
  gimp_data_dirty (GIMP_DATA (curve));
 
584
}
 
585
 
 
586
void
 
587
gimp_curve_set_curve_type (GimpCurve     *curve,
 
588
                           GimpCurveType  curve_type)
 
589
{
 
590
  g_return_if_fail (GIMP_IS_CURVE (curve));
 
591
 
 
592
  if (curve->curve_type != curve_type)
 
593
    {
 
594
      g_object_freeze_notify (G_OBJECT (curve));
 
595
 
 
596
      curve->curve_type = curve_type;
 
597
 
 
598
      if (curve_type == GIMP_CURVE_SMOOTH)
 
599
        {
 
600
          gint n_points;
 
601
          gint i;
 
602
 
 
603
          for (i = 0; i < curve->n_points; i++)
 
604
            {
 
605
              curve->points[i].x = -1;
 
606
              curve->points[i].y = -1;
 
607
            }
 
608
 
 
609
          /*  pick some points from the curve and make them control
 
610
           *  points
 
611
           */
 
612
          n_points = CLAMP (9, curve->n_points / 2, curve->n_points);
 
613
 
 
614
          for (i = 0; i < n_points; i++)
 
615
            {
 
616
              gint sample = i * (curve->n_samples - 1) / (n_points - 1);
 
617
              gint point  = i * (curve->n_points  - 1) / (n_points - 1);
 
618
 
 
619
              curve->points[point].x = ((gdouble) sample /
 
620
                                        (gdouble) (curve->n_samples - 1));
 
621
              curve->points[point].y = curve->samples[sample];
 
622
            }
 
623
 
 
624
          g_object_notify (G_OBJECT (curve), "points");
 
625
        }
 
626
 
 
627
      g_object_notify (G_OBJECT (curve), "curve-type");
 
628
 
 
629
      g_object_thaw_notify (G_OBJECT (curve));
 
630
 
 
631
      gimp_data_dirty (GIMP_DATA (curve));
 
632
    }
 
633
}
 
634
 
 
635
GimpCurveType
 
636
gimp_curve_get_curve_type (GimpCurve *curve)
 
637
{
 
638
  g_return_val_if_fail (GIMP_IS_CURVE (curve), GIMP_CURVE_SMOOTH);
 
639
 
 
640
  return curve->curve_type;
 
641
}
 
642
 
 
643
static void
 
644
gimp_curve_set_n_points (GimpCurve *curve,
 
645
                         gint       n_points)
 
646
{
 
647
  g_return_if_fail (GIMP_IS_CURVE (curve));
 
648
 
 
649
  if (n_points != curve->n_points)
 
650
    {
 
651
      gint i;
 
652
 
 
653
      g_object_freeze_notify (G_OBJECT (curve));
 
654
 
 
655
      curve->n_points = n_points;
 
656
      g_object_notify (G_OBJECT (curve), "n-points");
 
657
 
 
658
      curve->points = g_renew (GimpVector2, curve->points, curve->n_points);
 
659
 
 
660
      curve->points[0].x = 0.0;
 
661
      curve->points[0].y = 0.0;
 
662
 
 
663
      for (i = 1; i < curve->n_points - 1; i++)
 
664
        {
 
665
          curve->points[i].x = -1.0;
 
666
          curve->points[i].y = -1.0;
 
667
        }
 
668
 
 
669
      curve->points[curve->n_points - 1].x = 1.0;
 
670
      curve->points[curve->n_points - 1].y = 1.0;
 
671
 
 
672
      g_object_notify (G_OBJECT (curve), "points");
 
673
 
 
674
      if (curve->curve_type == GIMP_CURVE_SMOOTH)
 
675
        curve->identity = TRUE;
 
676
 
 
677
      g_object_thaw_notify (G_OBJECT (curve));
 
678
    }
 
679
}
 
680
 
 
681
gint
 
682
gimp_curve_get_n_points (GimpCurve *curve)
 
683
{
 
684
  g_return_val_if_fail (GIMP_IS_CURVE (curve), 0);
 
685
 
 
686
  return curve->n_points;
 
687
}
 
688
 
 
689
static void
 
690
gimp_curve_set_n_samples (GimpCurve *curve,
 
691
                          gint       n_samples)
 
692
{
 
693
  g_return_if_fail (GIMP_IS_CURVE (curve));
 
694
 
 
695
  if (n_samples != curve->n_samples)
 
696
    {
 
697
      gint i;
 
698
 
 
699
      g_object_freeze_notify (G_OBJECT (curve));
 
700
 
 
701
      curve->n_samples = n_samples;
 
702
      g_object_notify (G_OBJECT (curve), "n-samples");
 
703
 
 
704
      curve->samples = g_renew (gdouble, curve->samples, curve->n_samples);
 
705
 
 
706
      for (i = 0; i < curve->n_samples; i++)
 
707
        curve->samples[i] = (gdouble) i / (gdouble) (curve->n_samples - 1);
 
708
 
 
709
      g_object_notify (G_OBJECT (curve), "samples");
 
710
 
 
711
      if (curve->curve_type == GIMP_CURVE_FREE)
 
712
        curve->identity = TRUE;
 
713
 
 
714
      g_object_thaw_notify (G_OBJECT (curve));
 
715
    }
 
716
}
 
717
 
 
718
gint
 
719
gimp_curve_get_n_samples (GimpCurve *curve)
 
720
{
 
721
  g_return_val_if_fail (GIMP_IS_CURVE (curve), 0);
 
722
 
 
723
  return curve->n_samples;
 
724
}
 
725
 
 
726
gint
 
727
gimp_curve_get_closest_point (GimpCurve *curve,
 
728
                              gdouble    x)
 
729
{
 
730
  gint    closest_point = 0;
 
731
  gdouble distance      = G_MAXDOUBLE;
 
732
  gint    i;
 
733
 
 
734
  g_return_val_if_fail (GIMP_IS_CURVE (curve), 0);
 
735
 
 
736
  for (i = 0; i < curve->n_points; i++)
 
737
    {
 
738
      if (curve->points[i].x >= 0.0 &&
 
739
          fabs (x - curve->points[i].x) < distance)
 
740
        {
 
741
          distance = fabs (x - curve->points[i].x);
 
742
          closest_point = i;
 
743
        }
 
744
    }
 
745
 
 
746
  if (distance > (1.0 / (curve->n_points * 2.0)))
 
747
    closest_point = ROUND (x * (gdouble) (curve->n_points - 1));
 
748
 
 
749
  return closest_point;
 
750
}
 
751
 
 
752
void
 
753
gimp_curve_set_point (GimpCurve *curve,
 
754
                      gint       point,
 
755
                      gdouble    x,
 
756
                      gdouble    y)
 
757
{
 
758
  g_return_if_fail (GIMP_IS_CURVE (curve));
 
759
  g_return_if_fail (point >= 0 && point < curve->n_points);
 
760
  g_return_if_fail (x == -1.0 || (x >= 0 && x <= 1.0));
 
761
  g_return_if_fail (y == -1.0 || (y >= 0 && y <= 1.0));
 
762
 
 
763
  if (curve->curve_type == GIMP_CURVE_FREE)
 
764
    return;
 
765
 
 
766
  g_object_freeze_notify (G_OBJECT (curve));
 
767
 
 
768
  curve->points[point].x = x;
 
769
  curve->points[point].y = y;
 
770
 
 
771
  g_object_notify (G_OBJECT (curve), "points");
 
772
 
 
773
  g_object_thaw_notify (G_OBJECT (curve));
 
774
 
 
775
  gimp_data_dirty (GIMP_DATA (curve));
 
776
}
 
777
 
 
778
void
 
779
gimp_curve_move_point (GimpCurve *curve,
 
780
                       gint       point,
 
781
                       gdouble    y)
 
782
{
 
783
  g_return_if_fail (GIMP_IS_CURVE (curve));
 
784
  g_return_if_fail (point >= 0 && point < curve->n_points);
 
785
  g_return_if_fail (y >= 0 && y <= 1.0);
 
786
 
 
787
  if (curve->curve_type == GIMP_CURVE_FREE)
 
788
    return;
 
789
 
 
790
  g_object_freeze_notify (G_OBJECT (curve));
 
791
 
 
792
  curve->points[point].y = y;
 
793
 
 
794
  g_object_notify (G_OBJECT (curve), "points");
 
795
 
 
796
  g_object_thaw_notify (G_OBJECT (curve));
 
797
 
 
798
  gimp_data_dirty (GIMP_DATA (curve));
 
799
}
 
800
 
 
801
void
 
802
gimp_curve_get_point (GimpCurve *curve,
 
803
                      gint       point,
 
804
                      gdouble   *x,
 
805
                      gdouble   *y)
 
806
{
 
807
  g_return_if_fail (GIMP_IS_CURVE (curve));
 
808
  g_return_if_fail (point >= 0 && point < curve->n_points);
 
809
 
 
810
  if (curve->curve_type == GIMP_CURVE_FREE)
 
811
    return;
 
812
 
 
813
  if (x) *x = curve->points[point].x;
 
814
  if (y) *y = curve->points[point].y;
 
815
}
 
816
 
 
817
void
 
818
gimp_curve_set_curve (GimpCurve *curve,
 
819
                      gdouble    x,
 
820
                      gdouble    y)
 
821
{
 
822
  g_return_if_fail (GIMP_IS_CURVE (curve));
 
823
  g_return_if_fail (x >= 0 && x <= 1.0);
 
824
  g_return_if_fail (y >= 0 && y <= 1.0);
 
825
 
 
826
  if (curve->curve_type == GIMP_CURVE_SMOOTH)
 
827
    return;
 
828
 
 
829
  g_object_freeze_notify (G_OBJECT (curve));
 
830
 
 
831
  curve->samples[ROUND (x * (gdouble) (curve->n_samples - 1))] = y;
 
832
 
 
833
  g_object_notify (G_OBJECT (curve), "samples");
 
834
 
 
835
  g_object_thaw_notify (G_OBJECT (curve));
 
836
 
 
837
  gimp_data_dirty (GIMP_DATA (curve));
 
838
}
 
839
 
 
840
/**
 
841
 * gimp_curve_is_identity:
 
842
 * @curve: a #GimpCurve object
 
843
 *
 
844
 * If this function returns %TRUE, then the curve maps each value to
 
845
 * itself. If it returns %FALSE, then this assumption can not be made.
 
846
 *
 
847
 * Return value: %TRUE if the curve is an identity mapping, %FALSE otherwise.
 
848
 **/
 
849
gboolean
 
850
gimp_curve_is_identity (GimpCurve *curve)
 
851
{
 
852
  g_return_val_if_fail (GIMP_IS_CURVE (curve), FALSE);
 
853
 
 
854
  return curve->identity;
 
855
}
 
856
 
 
857
void
 
858
gimp_curve_get_uchar (GimpCurve *curve,
 
859
                      gint       n_samples,
 
860
                      guchar    *samples)
 
861
{
 
862
  gint i;
 
863
 
 
864
  g_return_if_fail (GIMP_IS_CURVE (curve));
 
865
#ifdef __GNUC__
 
866
#warning: FIXME: support n_samples != curve->n_samples
 
867
#endif
 
868
  g_return_if_fail (n_samples == curve->n_samples);
 
869
  g_return_if_fail (samples != NULL);
 
870
 
 
871
  for (i = 0; i < curve->n_samples; i++)
 
872
    samples[i] = curve->samples[i] * 255.999;
 
873
}
 
874
 
 
875
 
 
876
/*  private functions  */
 
877
 
 
878
static void
 
879
gimp_curve_calculate (GimpCurve *curve)
 
880
{
 
881
  gint *points;
 
882
  gint  i;
 
883
  gint  num_pts;
 
884
  gint  p1, p2, p3, p4;
 
885
 
 
886
  if (GIMP_DATA (curve)->freeze_count > 0)
 
887
    return;
 
888
 
 
889
  points = g_newa (gint, curve->n_points);
 
890
 
 
891
  switch (curve->curve_type)
 
892
    {
 
893
    case GIMP_CURVE_SMOOTH:
 
894
      /*  cycle through the curves  */
 
895
      num_pts = 0;
 
896
      for (i = 0; i < curve->n_points; i++)
 
897
        if (curve->points[i].x >= 0.0)
 
898
          points[num_pts++] = i;
 
899
 
 
900
      /*  Initialize boundary curve points */
 
901
      if (num_pts != 0)
 
902
        {
 
903
          GimpVector2 point;
 
904
          gint        boundary;
 
905
 
 
906
          point    = curve->points[points[0]];
 
907
          boundary = ROUND (point.x * (gdouble) (curve->n_samples - 1));
 
908
 
 
909
          for (i = 0; i < boundary; i++)
 
910
            curve->samples[i] = point.y;
 
911
 
 
912
          point    = curve->points[points[num_pts - 1]];
 
913
          boundary = ROUND (point.x * (gdouble) (curve->n_samples - 1));
 
914
 
 
915
          for (i = boundary; i < curve->n_samples; i++)
 
916
            curve->samples[i] = point.y;
 
917
        }
 
918
 
 
919
      for (i = 0; i < num_pts - 1; i++)
 
920
        {
 
921
          p1 = points[MAX (i - 1, 0)];
 
922
          p2 = points[i];
 
923
          p3 = points[i + 1];
 
924
          p4 = points[MIN (i + 2, num_pts - 1)];
 
925
 
 
926
          gimp_curve_plot (curve, p1, p2, p3, p4);
 
927
        }
 
928
 
 
929
      /* ensure that the control points are used exactly */
 
930
      for (i = 0; i < num_pts; i++)
 
931
        {
 
932
          gdouble x = curve->points[points[i]].x;
 
933
          gdouble y = curve->points[points[i]].y;
 
934
 
 
935
          curve->samples[ROUND (x * (gdouble) (curve->n_samples - 1))] = y;
 
936
        }
 
937
 
 
938
      g_object_notify (G_OBJECT (curve), "samples");
 
939
      break;
 
940
 
 
941
    case GIMP_CURVE_FREE:
 
942
      break;
 
943
    }
 
944
}
 
945
 
 
946
/*
 
947
 * This function calculates the curve values between the control points
 
948
 * p2 and p3, taking the potentially existing neighbors p1 and p4 into
 
949
 * account.
 
950
 *
 
951
 * This function uses a cubic bezier curve for the individual segments and
 
952
 * calculates the necessary intermediate control points depending on the
 
953
 * neighbor curve control points.
 
954
 */
 
955
static void
 
956
gimp_curve_plot (GimpCurve *curve,
 
957
                 gint       p1,
 
958
                 gint       p2,
 
959
                 gint       p3,
 
960
                 gint       p4)
 
961
{
 
962
  gint    i;
 
963
  gdouble x0, x3;
 
964
  gdouble y0, y1, y2, y3;
 
965
  gdouble dx, dy;
 
966
  gdouble slope;
 
967
 
 
968
  /* the outer control points for the bezier curve. */
 
969
  x0 = curve->points[p2].x;
 
970
  y0 = curve->points[p2].y;
 
971
  x3 = curve->points[p3].x;
 
972
  y3 = curve->points[p3].y;
 
973
 
 
974
  /*
 
975
   * the x values of the inner control points are fixed at
 
976
   * x1 = 2/3*x0 + 1/3*x3   and  x2 = 1/3*x0 + 2/3*x3
 
977
   * this ensures that the x values increase linearily with the
 
978
   * parameter t and enables us to skip the calculation of the x
 
979
   * values altogehter - just calculate y(t) evenly spaced.
 
980
   */
 
981
 
 
982
  dx = x3 - x0;
 
983
  dy = y3 - y0;
 
984
 
 
985
  g_return_if_fail (dx > 0);
 
986
 
 
987
  if (p1 == p2 && p3 == p4)
 
988
    {
 
989
      /* No information about the neighbors,
 
990
       * calculate y1 and y2 to get a straight line
 
991
       */
 
992
      y1 = y0 + dy / 3.0;
 
993
      y2 = y0 + dy * 2.0 / 3.0;
 
994
    }
 
995
  else if (p1 == p2 && p3 != p4)
 
996
    {
 
997
      /* only the right neighbor is available. Make the tangent at the
 
998
       * right endpoint parallel to the line between the left endpoint
 
999
       * and the right neighbor. Then point the tangent at the left towards
 
1000
       * the control handle of the right tangent, to ensure that the curve
 
1001
       * does not have an inflection point.
 
1002
       */
 
1003
      slope = (curve->points[p4].y - y0) / (curve->points[p4].x - x0);
 
1004
 
 
1005
      y2 = y3 - slope * dx / 3.0;
 
1006
      y1 = y0 + (y2 - y0) / 2.0;
 
1007
    }
 
1008
  else if (p1 != p2 && p3 == p4)
 
1009
    {
 
1010
      /* see previous case */
 
1011
      slope = (y3 - curve->points[p1].y) / (x3 - curve->points[p1].x);
 
1012
 
 
1013
      y1 = y0 + slope * dx / 3.0;
 
1014
      y2 = y3 + (y1 - y3) / 2.0;
 
1015
    }
 
1016
  else /* (p1 != p2 && p3 != p4) */
 
1017
    {
 
1018
      /* Both neighbors are available. Make the tangents at the endpoints
 
1019
       * parallel to the line between the opposite endpoint and the adjacent
 
1020
       * neighbor.
 
1021
       */
 
1022
      slope = (y3 - curve->points[p1].y) / (x3 - curve->points[p1].x);
 
1023
 
 
1024
      y1 = y0 + slope * dx / 3.0;
 
1025
 
 
1026
      slope = (curve->points[p4].y - y0) / (curve->points[p4].x - x0);
 
1027
 
 
1028
      y2 = y3 - slope * dx / 3.0;
 
1029
    }
 
1030
 
 
1031
  /*
 
1032
   * finally calculate the y(t) values for the given bezier values. We can
 
1033
   * use homogenously distributed values for t, since x(t) increases linearily.
 
1034
   */
 
1035
  for (i = 0; i <= ROUND (dx * (gdouble) (curve->n_samples - 1)); i++)
 
1036
    {
 
1037
      gdouble y, t;
 
1038
      gint    index;
 
1039
 
 
1040
      t = i / dx / (gdouble) (curve->n_samples - 1);
 
1041
      y =     y0 * (1-t) * (1-t) * (1-t) +
 
1042
          3 * y1 * (1-t) * (1-t) * t     +
 
1043
          3 * y2 * (1-t) * t     * t     +
 
1044
              y3 * t     * t     * t;
 
1045
 
 
1046
      index = i + ROUND (x0 * (gdouble) (curve->n_samples - 1));
 
1047
 
 
1048
      if (index < curve->n_samples)
 
1049
        curve->samples[index] = CLAMP (y, 0.0, 1.0);
 
1050
    }
 
1051
}