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 <string.h>
21
22 #include <cairo.h>
23 #include <gegl.h>
24 #include <gdk-pixbuf/gdk-pixbuf.h>
25
26 #include "libgimpcolor/gimpcolor.h"
27 #include "libgimpmath/gimpmath.h"
28
29 #include "core-types.h"
30
31 #include "gimpcontext.h"
32 #include "gimpgradient.h"
33 #include "gimpgradient-load.h"
34 #include "gimpgradient-save.h"
35 #include "gimptagged.h"
36 #include "gimptempbuf.h"
37
38
39 #define EPSILON 1e-10
40
41
42 static void gimp_gradient_tagged_iface_init (GimpTaggedInterface *iface);
43 static void gimp_gradient_finalize (GObject *object);
44
45 static gint64 gimp_gradient_get_memsize (GimpObject *object,
46 gint64 *gui_size);
47
48 static void gimp_gradient_get_preview_size (GimpViewable *viewable,
49 gint size,
50 gboolean popup,
51 gboolean dot_for_dot,
52 gint *width,
53 gint *height);
54 static gboolean gimp_gradient_get_popup_size (GimpViewable *viewable,
55 gint width,
56 gint height,
57 gboolean dot_for_dot,
58 gint *popup_width,
59 gint *popup_height);
60 static GimpTempBuf * gimp_gradient_get_new_preview (GimpViewable *viewable,
61 GimpContext *context,
62 gint width,
63 gint height);
64
65 static const gchar * gimp_gradient_get_extension (GimpData *data);
66 static void gimp_gradient_copy (GimpData *data,
67 GimpData *src_data);
68 static gint gimp_gradient_compare (GimpData *data1,
69 GimpData *data2);
70
71 static gchar * gimp_gradient_get_checksum (GimpTagged *tagged);
72
73 static inline GimpGradientSegment *
74 gimp_gradient_get_segment_at_internal (GimpGradient *gradient,
75 GimpGradientSegment *seg,
76 gdouble pos);
77 static void gimp_gradient_get_flat_color (GimpContext *context,
78 const GimpRGB *color,
79 GimpGradientColor color_type,
80 GimpRGB *flat_color);
81
82
83 static inline gdouble gimp_gradient_calc_linear_factor (gdouble middle,
84 gdouble pos);
85 static inline gdouble gimp_gradient_calc_curved_factor (gdouble middle,
86 gdouble pos);
87 static inline gdouble gimp_gradient_calc_sine_factor (gdouble middle,
88 gdouble pos);
89 static inline gdouble gimp_gradient_calc_sphere_increasing_factor (gdouble middle,
90 gdouble pos);
91 static inline gdouble gimp_gradient_calc_sphere_decreasing_factor (gdouble middle,
92 gdouble pos);
93 static inline gdouble gimp_gradient_calc_step_factor (gdouble middle,
94 gdouble pos);
95
96
97 G_DEFINE_TYPE_WITH_CODE (GimpGradient, gimp_gradient, GIMP_TYPE_DATA,
98 G_IMPLEMENT_INTERFACE (GIMP_TYPE_TAGGED,
99 gimp_gradient_tagged_iface_init))
100
101 #define parent_class gimp_gradient_parent_class
102
103 static const Babl *fish_srgb_to_linear_rgb = NULL;
104 static const Babl *fish_linear_rgb_to_srgb = NULL;
105 static const Babl *fish_srgb_to_cie_lab = NULL;
106 static const Babl *fish_cie_lab_to_srgb = NULL;
107
108
109 static void
gimp_gradient_class_init(GimpGradientClass * klass)110 gimp_gradient_class_init (GimpGradientClass *klass)
111 {
112 GObjectClass *object_class = G_OBJECT_CLASS (klass);
113 GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass);
114 GimpViewableClass *viewable_class = GIMP_VIEWABLE_CLASS (klass);
115 GimpDataClass *data_class = GIMP_DATA_CLASS (klass);
116
117 object_class->finalize = gimp_gradient_finalize;
118
119 gimp_object_class->get_memsize = gimp_gradient_get_memsize;
120
121 viewable_class->default_icon_name = "gimp-tool-gradient";
122 viewable_class->get_preview_size = gimp_gradient_get_preview_size;
123 viewable_class->get_popup_size = gimp_gradient_get_popup_size;
124 viewable_class->get_new_preview = gimp_gradient_get_new_preview;
125
126 data_class->save = gimp_gradient_save;
127 data_class->get_extension = gimp_gradient_get_extension;
128 data_class->copy = gimp_gradient_copy;
129 data_class->compare = gimp_gradient_compare;
130
131 fish_srgb_to_linear_rgb = babl_fish (babl_format ("R'G'B' double"),
132 babl_format ("RGB double"));
133 fish_linear_rgb_to_srgb = babl_fish (babl_format ("RGB double"),
134 babl_format ("R'G'B' double"));
135 fish_srgb_to_cie_lab = babl_fish (babl_format ("R'G'B' double"),
136 babl_format ("CIE Lab double"));
137 fish_cie_lab_to_srgb = babl_fish (babl_format ("CIE Lab double"),
138 babl_format ("R'G'B' double"));
139 }
140
141 static void
gimp_gradient_tagged_iface_init(GimpTaggedInterface * iface)142 gimp_gradient_tagged_iface_init (GimpTaggedInterface *iface)
143 {
144 iface->get_checksum = gimp_gradient_get_checksum;
145 }
146
147 static void
gimp_gradient_init(GimpGradient * gradient)148 gimp_gradient_init (GimpGradient *gradient)
149 {
150 gradient->segments = NULL;
151 }
152
153 static void
gimp_gradient_finalize(GObject * object)154 gimp_gradient_finalize (GObject *object)
155 {
156 GimpGradient *gradient = GIMP_GRADIENT (object);
157
158 if (gradient->segments)
159 {
160 gimp_gradient_segments_free (gradient->segments);
161 gradient->segments = NULL;
162 }
163
164 G_OBJECT_CLASS (parent_class)->finalize (object);
165 }
166
167 static gint64
gimp_gradient_get_memsize(GimpObject * object,gint64 * gui_size)168 gimp_gradient_get_memsize (GimpObject *object,
169 gint64 *gui_size)
170 {
171 GimpGradient *gradient = GIMP_GRADIENT (object);
172 GimpGradientSegment *segment;
173 gint64 memsize = 0;
174
175 for (segment = gradient->segments; segment; segment = segment->next)
176 memsize += sizeof (GimpGradientSegment);
177
178 return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object,
179 gui_size);
180 }
181
182 static void
gimp_gradient_get_preview_size(GimpViewable * viewable,gint size,gboolean popup,gboolean dot_for_dot,gint * width,gint * height)183 gimp_gradient_get_preview_size (GimpViewable *viewable,
184 gint size,
185 gboolean popup,
186 gboolean dot_for_dot,
187 gint *width,
188 gint *height)
189 {
190 *width = size;
191 *height = 1 + size / 2;
192 }
193
194 static gboolean
gimp_gradient_get_popup_size(GimpViewable * viewable,gint width,gint height,gboolean dot_for_dot,gint * popup_width,gint * popup_height)195 gimp_gradient_get_popup_size (GimpViewable *viewable,
196 gint width,
197 gint height,
198 gboolean dot_for_dot,
199 gint *popup_width,
200 gint *popup_height)
201 {
202 if (width < 128 || height < 32)
203 {
204 *popup_width = 128;
205 *popup_height = 32;
206
207 return TRUE;
208 }
209
210 return FALSE;
211 }
212
213 static GimpTempBuf *
gimp_gradient_get_new_preview(GimpViewable * viewable,GimpContext * context,gint width,gint height)214 gimp_gradient_get_new_preview (GimpViewable *viewable,
215 GimpContext *context,
216 gint width,
217 gint height)
218 {
219 GimpGradient *gradient = GIMP_GRADIENT (viewable);
220 GimpGradientSegment *seg = NULL;
221 GimpTempBuf *temp_buf;
222 guchar *buf;
223 guchar *p;
224 guchar *row;
225 gint x, y;
226 gdouble dx, cur_x;
227 GimpRGB color;
228
229 dx = 1.0 / (width - 1);
230 cur_x = 0.0;
231 p = row = g_malloc (width * 4);
232
233 /* Create lines to fill the image */
234
235 for (x = 0; x < width; x++)
236 {
237 seg = gimp_gradient_get_color_at (gradient, context, seg, cur_x,
238 FALSE,
239 GIMP_GRADIENT_BLEND_RGB_PERCEPTUAL,
240 &color);
241
242 *p++ = ROUND (color.r * 255.0);
243 *p++ = ROUND (color.g * 255.0);
244 *p++ = ROUND (color.b * 255.0);
245 *p++ = ROUND (color.a * 255.0);
246
247 cur_x += dx;
248 }
249
250 temp_buf = gimp_temp_buf_new (width, height, babl_format ("R'G'B'A u8"));
251
252 buf = gimp_temp_buf_get_data (temp_buf);
253
254 for (y = 0; y < height; y++)
255 memcpy (buf + (width * y * 4), row, width * 4);
256
257 g_free (row);
258
259 return temp_buf;
260 }
261
262 static void
gimp_gradient_copy(GimpData * data,GimpData * src_data)263 gimp_gradient_copy (GimpData *data,
264 GimpData *src_data)
265 {
266 GimpGradient *gradient = GIMP_GRADIENT (data);
267 GimpGradient *src_gradient = GIMP_GRADIENT (src_data);
268 GimpGradientSegment *head, *prev, *cur, *orig;
269
270 if (gradient->segments)
271 {
272 gimp_gradient_segments_free (gradient->segments);
273 gradient->segments = NULL;
274 }
275
276 prev = NULL;
277 orig = src_gradient->segments;
278 head = NULL;
279
280 while (orig)
281 {
282 cur = gimp_gradient_segment_new ();
283
284 *cur = *orig; /* Copy everything */
285
286 cur->prev = prev;
287 cur->next = NULL;
288
289 if (prev)
290 prev->next = cur;
291 else
292 head = cur; /* Remember head */
293
294 prev = cur;
295 orig = orig->next;
296 }
297
298 gradient->segments = head;
299
300 gimp_data_dirty (GIMP_DATA (gradient));
301 }
302
303 static gint
gimp_gradient_compare(GimpData * data1,GimpData * data2)304 gimp_gradient_compare (GimpData *data1,
305 GimpData *data2)
306 {
307 gboolean is_custom1;
308 gboolean is_custom2;
309
310 /* check whether data1 and data2 are the custom gradient, which is the only
311 * writable internal gradient.
312 */
313 is_custom1 = gimp_data_is_internal (data1) && gimp_data_is_writable (data1);
314 is_custom2 = gimp_data_is_internal (data2) && gimp_data_is_writable (data2);
315
316 /* order the custom gradient before all the other gradients; use the default
317 * ordering for the rest.
318 */
319 if (is_custom1)
320 {
321 if (is_custom2)
322 return 0;
323 else
324 return -1;
325 }
326 else if (is_custom2)
327 {
328 return +1;
329 }
330 else
331 return GIMP_DATA_CLASS (parent_class)->compare (data1, data2);
332 }
333
334 static gchar *
gimp_gradient_get_checksum(GimpTagged * tagged)335 gimp_gradient_get_checksum (GimpTagged *tagged)
336 {
337 GimpGradient *gradient = GIMP_GRADIENT (tagged);
338 gchar *checksum_string = NULL;
339
340 if (gradient->segments)
341 {
342 GChecksum *checksum = g_checksum_new (G_CHECKSUM_MD5);
343 GimpGradientSegment *segment = gradient->segments;
344
345 while (segment)
346 {
347 g_checksum_update (checksum,
348 (const guchar *) &segment->left,
349 sizeof (segment->left));
350 g_checksum_update (checksum,
351 (const guchar *) &segment->middle,
352 sizeof (segment->middle));
353 g_checksum_update (checksum,
354 (const guchar *) &segment->right,
355 sizeof (segment->right));
356 g_checksum_update (checksum,
357 (const guchar *) &segment->left_color_type,
358 sizeof (segment->left_color_type));
359 g_checksum_update (checksum,
360 (const guchar *) &segment->left_color,
361 sizeof (segment->left_color));
362 g_checksum_update (checksum,
363 (const guchar *) &segment->right_color_type,
364 sizeof (segment->right_color_type));
365 g_checksum_update (checksum,
366 (const guchar *) &segment->right_color,
367 sizeof (segment->right_color));
368 g_checksum_update (checksum,
369 (const guchar *) &segment->type,
370 sizeof (segment->type));
371 g_checksum_update (checksum,
372 (const guchar *) &segment->color,
373 sizeof (segment->color));
374
375 segment = segment->next;
376 }
377
378 checksum_string = g_strdup (g_checksum_get_string (checksum));
379
380 g_checksum_free (checksum);
381 }
382
383 return checksum_string;
384 }
385
386
387 /* public functions */
388
389 GimpData *
gimp_gradient_new(GimpContext * context,const gchar * name)390 gimp_gradient_new (GimpContext *context,
391 const gchar *name)
392 {
393 GimpGradient *gradient;
394
395 g_return_val_if_fail (name != NULL, NULL);
396 g_return_val_if_fail (*name != '\0', NULL);
397
398 gradient = g_object_new (GIMP_TYPE_GRADIENT,
399 "name", name,
400 NULL);
401
402 gradient->segments = gimp_gradient_segment_new ();
403
404 return GIMP_DATA (gradient);
405 }
406
407 GimpData *
gimp_gradient_get_standard(GimpContext * context)408 gimp_gradient_get_standard (GimpContext *context)
409 {
410 static GimpData *standard_gradient = NULL;
411
412 if (! standard_gradient)
413 {
414 standard_gradient = gimp_gradient_new (context, "Standard");
415
416 gimp_data_clean (standard_gradient);
417 gimp_data_make_internal (standard_gradient, "gimp-gradient-standard");
418
419 g_object_add_weak_pointer (G_OBJECT (standard_gradient),
420 (gpointer *) &standard_gradient);
421 }
422
423 return standard_gradient;
424 }
425
426 static const gchar *
gimp_gradient_get_extension(GimpData * data)427 gimp_gradient_get_extension (GimpData *data)
428 {
429 return GIMP_GRADIENT_FILE_EXTENSION;
430 }
431
432 /**
433 * gimp_gradient_get_color_at:
434 * @gradient: a gradient
435 * @context: a context
436 * @seg: a segment to seed the search with (or %NULL)
437 * @pos: position in the gradient (between 0.0 and 1.0)
438 * @reverse: when %TRUE, use the reversed gradient
439 * @blend_color_space: color space to use for blending RGB segments
440 * @color: returns the color
441 *
442 * If you are iterating over an gradient, you should pass the the
443 * return value from the last call for @seg.
444 *
445 * Return value: the gradient segment the color is from
446 **/
447 GimpGradientSegment *
gimp_gradient_get_color_at(GimpGradient * gradient,GimpContext * context,GimpGradientSegment * seg,gdouble pos,gboolean reverse,GimpGradientBlendColorSpace blend_color_space,GimpRGB * color)448 gimp_gradient_get_color_at (GimpGradient *gradient,
449 GimpContext *context,
450 GimpGradientSegment *seg,
451 gdouble pos,
452 gboolean reverse,
453 GimpGradientBlendColorSpace blend_color_space,
454 GimpRGB *color)
455 {
456 gdouble factor = 0.0;
457 gdouble seg_len;
458 gdouble middle;
459 GimpRGB left_color;
460 GimpRGB right_color;
461 GimpRGB rgb;
462
463 /* type-check disabled to improve speed */
464 /* g_return_val_if_fail (GIMP_IS_GRADIENT (gradient), NULL); */
465 g_return_val_if_fail (color != NULL, NULL);
466
467 pos = CLAMP (pos, 0.0, 1.0);
468
469 if (reverse)
470 pos = 1.0 - pos;
471
472 seg = gimp_gradient_get_segment_at_internal (gradient, seg, pos);
473
474 seg_len = seg->right - seg->left;
475
476 if (seg_len < EPSILON)
477 {
478 middle = 0.5;
479 pos = 0.5;
480 }
481 else
482 {
483 middle = (seg->middle - seg->left) / seg_len;
484 pos = (pos - seg->left) / seg_len;
485 }
486
487 switch (seg->type)
488 {
489 case GIMP_GRADIENT_SEGMENT_LINEAR:
490 factor = gimp_gradient_calc_linear_factor (middle, pos);
491 break;
492
493 case GIMP_GRADIENT_SEGMENT_CURVED:
494 factor = gimp_gradient_calc_curved_factor (middle, pos);
495 break;
496
497 case GIMP_GRADIENT_SEGMENT_SINE:
498 factor = gimp_gradient_calc_sine_factor (middle, pos);
499 break;
500
501 case GIMP_GRADIENT_SEGMENT_SPHERE_INCREASING:
502 factor = gimp_gradient_calc_sphere_increasing_factor (middle, pos);
503 break;
504
505 case GIMP_GRADIENT_SEGMENT_SPHERE_DECREASING:
506 factor = gimp_gradient_calc_sphere_decreasing_factor (middle, pos);
507 break;
508
509 case GIMP_GRADIENT_SEGMENT_STEP:
510 factor = gimp_gradient_calc_step_factor (middle, pos);
511 break;
512
513 default:
514 g_warning ("%s: Unknown gradient type %d.", G_STRFUNC, seg->type);
515 break;
516 }
517
518 /* Get left/right colors */
519
520 if (context)
521 {
522 gimp_gradient_segment_get_left_flat_color (gradient,
523 context, seg, &left_color);
524
525 gimp_gradient_segment_get_right_flat_color (gradient,
526 context, seg, &right_color);
527 }
528 else
529 {
530 left_color = seg->left_color;
531 right_color = seg->right_color;
532 }
533
534 /* Calculate color components */
535
536 if (seg->color == GIMP_GRADIENT_SEGMENT_RGB)
537 {
538 switch (blend_color_space)
539 {
540 case GIMP_GRADIENT_BLEND_CIE_LAB:
541 babl_process (fish_srgb_to_cie_lab,
542 &left_color, &left_color, 1);
543 babl_process (fish_srgb_to_cie_lab,
544 &right_color, &right_color, 1);
545 break;
546
547 case GIMP_GRADIENT_BLEND_RGB_LINEAR:
548 babl_process (fish_srgb_to_linear_rgb,
549 &left_color, &left_color, 1);
550 babl_process (fish_srgb_to_linear_rgb,
551 &right_color, &right_color, 1);
552
553 case GIMP_GRADIENT_BLEND_RGB_PERCEPTUAL:
554 break;
555 }
556
557 rgb.r = left_color.r + (right_color.r - left_color.r) * factor;
558 rgb.g = left_color.g + (right_color.g - left_color.g) * factor;
559 rgb.b = left_color.b + (right_color.b - left_color.b) * factor;
560
561 switch (blend_color_space)
562 {
563 case GIMP_GRADIENT_BLEND_CIE_LAB:
564 babl_process (fish_cie_lab_to_srgb,
565 &rgb, &rgb, 1);
566 break;
567
568 case GIMP_GRADIENT_BLEND_RGB_LINEAR:
569 babl_process (fish_linear_rgb_to_srgb,
570 &rgb, &rgb, 1);
571
572 case GIMP_GRADIENT_BLEND_RGB_PERCEPTUAL:
573 break;
574 }
575 }
576 else
577 {
578 GimpHSV left_hsv;
579 GimpHSV right_hsv;
580
581 gimp_rgb_to_hsv (&left_color, &left_hsv);
582 gimp_rgb_to_hsv (&right_color, &right_hsv);
583
584 left_hsv.s = left_hsv.s + (right_hsv.s - left_hsv.s) * factor;
585 left_hsv.v = left_hsv.v + (right_hsv.v - left_hsv.v) * factor;
586
587 switch (seg->color)
588 {
589 case GIMP_GRADIENT_SEGMENT_HSV_CCW:
590 if (left_hsv.h < right_hsv.h)
591 {
592 left_hsv.h += (right_hsv.h - left_hsv.h) * factor;
593 }
594 else
595 {
596 left_hsv.h += (1.0 - (left_hsv.h - right_hsv.h)) * factor;
597
598 if (left_hsv.h > 1.0)
599 left_hsv.h -= 1.0;
600 }
601 break;
602
603 case GIMP_GRADIENT_SEGMENT_HSV_CW:
604 if (right_hsv.h < left_hsv.h)
605 {
606 left_hsv.h -= (left_hsv.h - right_hsv.h) * factor;
607 }
608 else
609 {
610 left_hsv.h -= (1.0 - (right_hsv.h - left_hsv.h)) * factor;
611
612 if (left_hsv.h < 0.0)
613 left_hsv.h += 1.0;
614 }
615 break;
616
617 default:
618 g_warning ("%s: Unknown coloring mode %d",
619 G_STRFUNC, (gint) seg->color);
620 break;
621 }
622
623 gimp_hsv_to_rgb (&left_hsv, &rgb);
624 }
625
626 /* Calculate alpha */
627
628 rgb.a = left_color.a + (right_color.a - left_color.a) * factor;
629
630 *color = rgb;
631
632 return seg;
633 }
634
635 GimpGradientSegment *
gimp_gradient_get_segment_at(GimpGradient * gradient,gdouble pos)636 gimp_gradient_get_segment_at (GimpGradient *gradient,
637 gdouble pos)
638 {
639 g_return_val_if_fail (GIMP_IS_GRADIENT (gradient), NULL);
640
641 return gimp_gradient_get_segment_at_internal (gradient, NULL, pos);
642 }
643
644 gboolean
gimp_gradient_has_fg_bg_segments(GimpGradient * gradient)645 gimp_gradient_has_fg_bg_segments (GimpGradient *gradient)
646 {
647 GimpGradientSegment *segment;
648
649 g_return_val_if_fail (GIMP_IS_GRADIENT (gradient), FALSE);
650
651 for (segment = gradient->segments; segment; segment = segment->next)
652 if (segment->left_color_type != GIMP_GRADIENT_COLOR_FIXED ||
653 segment->right_color_type != GIMP_GRADIENT_COLOR_FIXED)
654 return TRUE;
655
656 return FALSE;
657 }
658
659 void
gimp_gradient_split_at(GimpGradient * gradient,GimpContext * context,GimpGradientSegment * seg,gdouble pos,GimpGradientBlendColorSpace blend_color_space,GimpGradientSegment ** newl,GimpGradientSegment ** newr)660 gimp_gradient_split_at (GimpGradient *gradient,
661 GimpContext *context,
662 GimpGradientSegment *seg,
663 gdouble pos,
664 GimpGradientBlendColorSpace blend_color_space,
665 GimpGradientSegment **newl,
666 GimpGradientSegment **newr)
667 {
668 GimpRGB color;
669 GimpGradientSegment *newseg;
670
671 g_return_if_fail (GIMP_IS_GRADIENT (gradient));
672 g_return_if_fail (GIMP_IS_CONTEXT (context));
673
674 gimp_data_freeze (GIMP_DATA (gradient));
675
676 pos = CLAMP (pos, 0.0, 1.0);
677 seg = gimp_gradient_get_segment_at_internal (gradient, seg, pos);
678
679 /* Get color at pos */
680 gimp_gradient_get_color_at (gradient, context, seg, pos,
681 FALSE, blend_color_space, &color);
682
683 /* Create a new segment and insert it in the list */
684
685 newseg = gimp_gradient_segment_new ();
686
687 newseg->prev = seg;
688 newseg->next = seg->next;
689
690 seg->next = newseg;
691
692 if (newseg->next)
693 newseg->next->prev = newseg;
694
695 /* Set coordinates of new segment */
696
697 newseg->left = pos;
698 newseg->right = seg->right;
699 newseg->middle = (newseg->left + newseg->right) / 2.0;
700
701 /* Set coordinates of original segment */
702
703 seg->right = newseg->left;
704 seg->middle = (seg->left + seg->right) / 2.0;
705
706 /* Set colors of both segments */
707
708 newseg->right_color_type = seg->right_color_type;
709 newseg->right_color = seg->right_color;
710
711 seg->right_color_type = newseg->left_color_type = GIMP_GRADIENT_COLOR_FIXED;
712 seg->right_color = newseg->left_color = color;
713
714 /* Set parameters of new segment */
715
716 newseg->type = seg->type;
717 newseg->color = seg->color;
718
719 /* Done */
720
721 if (newl) *newl = seg;
722 if (newr) *newr = newseg;
723
724 gimp_data_thaw (GIMP_DATA (gradient));
725 }
726
727 GimpGradient *
gimp_gradient_flatten(GimpGradient * gradient,GimpContext * context)728 gimp_gradient_flatten (GimpGradient *gradient,
729 GimpContext *context)
730 {
731 GimpGradient *flat;
732 GimpGradientSegment *seg;
733
734 g_return_val_if_fail (GIMP_IS_GRADIENT (gradient), NULL);
735 g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
736
737 flat = GIMP_GRADIENT (gimp_data_duplicate (GIMP_DATA (gradient)));
738
739 for (seg = flat->segments; seg; seg = seg->next)
740 {
741 gimp_gradient_segment_get_left_flat_color (gradient,
742 context, seg,
743 &seg->left_color);
744
745 seg->left_color_type = GIMP_GRADIENT_COLOR_FIXED;
746
747 gimp_gradient_segment_get_right_flat_color (gradient,
748 context, seg,
749 &seg->right_color);
750
751 seg->right_color_type = GIMP_GRADIENT_COLOR_FIXED;
752 }
753
754 return flat;
755 }
756
757
758 /* gradient segment functions */
759
760 GimpGradientSegment *
gimp_gradient_segment_new(void)761 gimp_gradient_segment_new (void)
762 {
763 GimpGradientSegment *seg;
764
765 seg = g_slice_new0 (GimpGradientSegment);
766
767 seg->left = 0.0;
768 seg->middle = 0.5;
769 seg->right = 1.0;
770
771 seg->left_color_type = GIMP_GRADIENT_COLOR_FIXED;
772 gimp_rgba_set (&seg->left_color, 0.0, 0.0, 0.0, 1.0);
773
774 seg->right_color_type = GIMP_GRADIENT_COLOR_FIXED;
775 gimp_rgba_set (&seg->right_color, 1.0, 1.0, 1.0, 1.0);
776
777 seg->type = GIMP_GRADIENT_SEGMENT_LINEAR;
778 seg->color = GIMP_GRADIENT_SEGMENT_RGB;
779
780 seg->prev = seg->next = NULL;
781
782 return seg;
783 }
784
785
786 void
gimp_gradient_segment_free(GimpGradientSegment * seg)787 gimp_gradient_segment_free (GimpGradientSegment *seg)
788 {
789 g_return_if_fail (seg != NULL);
790
791 g_slice_free (GimpGradientSegment, seg);
792 }
793
794 void
gimp_gradient_segments_free(GimpGradientSegment * seg)795 gimp_gradient_segments_free (GimpGradientSegment *seg)
796 {
797 g_return_if_fail (seg != NULL);
798
799 g_slice_free_chain (GimpGradientSegment, seg, next);
800 }
801
802 GimpGradientSegment *
gimp_gradient_segment_get_last(GimpGradientSegment * seg)803 gimp_gradient_segment_get_last (GimpGradientSegment *seg)
804 {
805 if (! seg)
806 return NULL;
807
808 while (seg->next)
809 seg = seg->next;
810
811 return seg;
812 }
813
814 GimpGradientSegment *
gimp_gradient_segment_get_first(GimpGradientSegment * seg)815 gimp_gradient_segment_get_first (GimpGradientSegment *seg)
816 {
817 if (! seg)
818 return NULL;
819
820 while (seg->prev)
821 seg = seg->prev;
822
823 return seg;
824 }
825
826 GimpGradientSegment *
gimp_gradient_segment_get_nth(GimpGradientSegment * seg,gint index)827 gimp_gradient_segment_get_nth (GimpGradientSegment *seg,
828 gint index)
829 {
830 gint i = 0;
831
832 g_return_val_if_fail (index >= 0, NULL);
833
834 if (! seg)
835 return NULL;
836
837 while (seg && (i < index))
838 {
839 seg = seg->next;
840 i++;
841 }
842
843 if (i == index)
844 return seg;
845
846 return NULL;
847 }
848
849 void
gimp_gradient_segment_split_midpoint(GimpGradient * gradient,GimpContext * context,GimpGradientSegment * lseg,GimpGradientBlendColorSpace blend_color_space,GimpGradientSegment ** newl,GimpGradientSegment ** newr)850 gimp_gradient_segment_split_midpoint (GimpGradient *gradient,
851 GimpContext *context,
852 GimpGradientSegment *lseg,
853 GimpGradientBlendColorSpace blend_color_space,
854 GimpGradientSegment **newl,
855 GimpGradientSegment **newr)
856 {
857 g_return_if_fail (GIMP_IS_GRADIENT (gradient));
858 g_return_if_fail (GIMP_IS_CONTEXT (context));
859 g_return_if_fail (lseg != NULL);
860 g_return_if_fail (newl != NULL);
861 g_return_if_fail (newr != NULL);
862
863 gimp_gradient_split_at (gradient, context, lseg, lseg->middle,
864 blend_color_space, newl, newr);
865 }
866
867 void
gimp_gradient_segment_split_uniform(GimpGradient * gradient,GimpContext * context,GimpGradientSegment * lseg,gint parts,GimpGradientBlendColorSpace blend_color_space,GimpGradientSegment ** newl,GimpGradientSegment ** newr)868 gimp_gradient_segment_split_uniform (GimpGradient *gradient,
869 GimpContext *context,
870 GimpGradientSegment *lseg,
871 gint parts,
872 GimpGradientBlendColorSpace blend_color_space,
873 GimpGradientSegment **newl,
874 GimpGradientSegment **newr)
875 {
876 GimpGradientSegment *seg, *prev, *tmp;
877 gdouble seg_len;
878 gint i;
879
880 g_return_if_fail (GIMP_IS_GRADIENT (gradient));
881 g_return_if_fail (GIMP_IS_CONTEXT (context));
882 g_return_if_fail (lseg != NULL);
883 g_return_if_fail (newl != NULL);
884 g_return_if_fail (newr != NULL);
885
886 gimp_data_freeze (GIMP_DATA (gradient));
887
888 seg_len = (lseg->right - lseg->left) / parts; /* Length of divisions */
889
890 seg = NULL;
891 prev = NULL;
892 tmp = NULL;
893
894 for (i = 0; i < parts; i++)
895 {
896 seg = gimp_gradient_segment_new ();
897
898 if (i == 0)
899 tmp = seg; /* Remember first segment */
900
901 seg->left = lseg->left + i * seg_len;
902 seg->right = lseg->left + (i + 1) * seg_len;
903 seg->middle = (seg->left + seg->right) / 2.0;
904
905 seg->left_color_type = GIMP_GRADIENT_COLOR_FIXED;
906 seg->right_color_type = GIMP_GRADIENT_COLOR_FIXED;
907
908 gimp_gradient_get_color_at (gradient, context, lseg,
909 seg->left, FALSE, blend_color_space,
910 &seg->left_color);
911 gimp_gradient_get_color_at (gradient, context, lseg,
912 seg->right, FALSE, blend_color_space,
913 &seg->right_color);
914
915 seg->type = lseg->type;
916 seg->color = lseg->color;
917
918 seg->prev = prev;
919 seg->next = NULL;
920
921 if (prev)
922 prev->next = seg;
923
924 prev = seg;
925 }
926
927 /* Fix edges */
928
929 tmp->left_color_type = lseg->left_color_type;
930 tmp->left_color = lseg->left_color;
931
932 seg->right_color_type = lseg->right_color_type;
933 seg->right_color = lseg->right_color;
934
935 tmp->left = lseg->left;
936 seg->right = lseg->right; /* To squish accumulative error */
937
938 /* Link in list */
939
940 tmp->prev = lseg->prev;
941 seg->next = lseg->next;
942
943 if (lseg->prev)
944 lseg->prev->next = tmp;
945 else
946 gradient->segments = tmp; /* We are on leftmost segment */
947
948 if (lseg->next)
949 lseg->next->prev = seg;
950
951 /* Done */
952 *newl = tmp;
953 *newr = seg;
954
955 /* Delete old segment */
956 gimp_gradient_segment_free (lseg);
957
958 gimp_data_thaw (GIMP_DATA (gradient));
959 }
960
961 void
gimp_gradient_segment_get_left_color(GimpGradient * gradient,GimpGradientSegment * seg,GimpRGB * color)962 gimp_gradient_segment_get_left_color (GimpGradient *gradient,
963 GimpGradientSegment *seg,
964 GimpRGB *color)
965 {
966 g_return_if_fail (GIMP_IS_GRADIENT (gradient));
967 g_return_if_fail (seg != NULL);
968 g_return_if_fail (color != NULL);
969
970 *color = seg->left_color;
971 }
972
973 void
gimp_gradient_segment_set_left_color(GimpGradient * gradient,GimpGradientSegment * seg,const GimpRGB * color)974 gimp_gradient_segment_set_left_color (GimpGradient *gradient,
975 GimpGradientSegment *seg,
976 const GimpRGB *color)
977 {
978 g_return_if_fail (GIMP_IS_GRADIENT (gradient));
979 g_return_if_fail (seg != NULL);
980 g_return_if_fail (color != NULL);
981
982 gimp_data_freeze (GIMP_DATA (gradient));
983
984 gimp_gradient_segment_range_blend (gradient, seg, seg,
985 color, &seg->right_color,
986 TRUE, TRUE);
987
988 gimp_data_thaw (GIMP_DATA (gradient));
989 }
990
991 void
gimp_gradient_segment_get_right_color(GimpGradient * gradient,GimpGradientSegment * seg,GimpRGB * color)992 gimp_gradient_segment_get_right_color (GimpGradient *gradient,
993 GimpGradientSegment *seg,
994 GimpRGB *color)
995 {
996 g_return_if_fail (GIMP_IS_GRADIENT (gradient));
997 g_return_if_fail (seg != NULL);
998 g_return_if_fail (color != NULL);
999
1000 *color = seg->right_color;
1001 }
1002
1003 void
gimp_gradient_segment_set_right_color(GimpGradient * gradient,GimpGradientSegment * seg,const GimpRGB * color)1004 gimp_gradient_segment_set_right_color (GimpGradient *gradient,
1005 GimpGradientSegment *seg,
1006 const GimpRGB *color)
1007 {
1008 g_return_if_fail (GIMP_IS_GRADIENT (gradient));
1009 g_return_if_fail (seg != NULL);
1010 g_return_if_fail (color != NULL);
1011
1012 gimp_data_freeze (GIMP_DATA (gradient));
1013
1014 gimp_gradient_segment_range_blend (gradient, seg, seg,
1015 &seg->left_color, color,
1016 TRUE, TRUE);
1017
1018 gimp_data_thaw (GIMP_DATA (gradient));
1019 }
1020
1021 GimpGradientColor
gimp_gradient_segment_get_left_color_type(GimpGradient * gradient,GimpGradientSegment * seg)1022 gimp_gradient_segment_get_left_color_type (GimpGradient *gradient,
1023 GimpGradientSegment *seg)
1024 {
1025 g_return_val_if_fail (GIMP_IS_GRADIENT (gradient), 0);
1026 g_return_val_if_fail (seg != NULL, 0);
1027
1028 return seg->left_color_type;
1029 }
1030
1031 void
gimp_gradient_segment_set_left_color_type(GimpGradient * gradient,GimpGradientSegment * seg,GimpGradientColor color_type)1032 gimp_gradient_segment_set_left_color_type (GimpGradient *gradient,
1033 GimpGradientSegment *seg,
1034 GimpGradientColor color_type)
1035 {
1036 g_return_if_fail (GIMP_IS_GRADIENT (gradient));
1037 g_return_if_fail (seg != NULL);
1038
1039 gimp_data_freeze (GIMP_DATA (gradient));
1040
1041 seg->left_color_type = color_type;
1042
1043 gimp_data_thaw (GIMP_DATA (gradient));
1044 }
1045
1046 GimpGradientColor
gimp_gradient_segment_get_right_color_type(GimpGradient * gradient,GimpGradientSegment * seg)1047 gimp_gradient_segment_get_right_color_type (GimpGradient *gradient,
1048 GimpGradientSegment *seg)
1049 {
1050 g_return_val_if_fail (GIMP_IS_GRADIENT (gradient), 0);
1051 g_return_val_if_fail (seg != NULL, 0);
1052
1053 return seg->right_color_type;
1054 }
1055
1056 void
gimp_gradient_segment_set_right_color_type(GimpGradient * gradient,GimpGradientSegment * seg,GimpGradientColor color_type)1057 gimp_gradient_segment_set_right_color_type (GimpGradient *gradient,
1058 GimpGradientSegment *seg,
1059 GimpGradientColor color_type)
1060 {
1061 g_return_if_fail (GIMP_IS_GRADIENT (gradient));
1062 g_return_if_fail (seg != NULL);
1063
1064 gimp_data_freeze (GIMP_DATA (gradient));
1065
1066 seg->right_color_type = color_type;
1067
1068 gimp_data_thaw (GIMP_DATA (gradient));
1069 }
1070
1071 void
gimp_gradient_segment_get_left_flat_color(GimpGradient * gradient,GimpContext * context,GimpGradientSegment * seg,GimpRGB * color)1072 gimp_gradient_segment_get_left_flat_color (GimpGradient *gradient,
1073 GimpContext *context,
1074 GimpGradientSegment *seg,
1075 GimpRGB *color)
1076 {
1077 g_return_if_fail (GIMP_IS_GRADIENT (gradient));
1078 g_return_if_fail (seg != NULL);
1079 g_return_if_fail (color != NULL);
1080
1081 gimp_gradient_get_flat_color (context,
1082 &seg->left_color, seg->left_color_type,
1083 color);
1084 }
1085
1086 void
gimp_gradient_segment_get_right_flat_color(GimpGradient * gradient,GimpContext * context,GimpGradientSegment * seg,GimpRGB * color)1087 gimp_gradient_segment_get_right_flat_color (GimpGradient *gradient,
1088 GimpContext *context,
1089 GimpGradientSegment *seg,
1090 GimpRGB *color)
1091 {
1092 g_return_if_fail (GIMP_IS_GRADIENT (gradient));
1093 g_return_if_fail (seg != NULL);
1094 g_return_if_fail (color != NULL);
1095
1096 gimp_gradient_get_flat_color (context,
1097 &seg->right_color, seg->right_color_type,
1098 color);
1099 }
1100
1101 gdouble
gimp_gradient_segment_get_left_pos(GimpGradient * gradient,GimpGradientSegment * seg)1102 gimp_gradient_segment_get_left_pos (GimpGradient *gradient,
1103 GimpGradientSegment *seg)
1104 {
1105 g_return_val_if_fail (GIMP_IS_GRADIENT (gradient), 0.0);
1106 g_return_val_if_fail (seg != NULL, 0.0);
1107
1108 return seg->left;
1109 }
1110
1111 gdouble
gimp_gradient_segment_set_left_pos(GimpGradient * gradient,GimpGradientSegment * seg,gdouble pos)1112 gimp_gradient_segment_set_left_pos (GimpGradient *gradient,
1113 GimpGradientSegment *seg,
1114 gdouble pos)
1115 {
1116 gdouble final_pos;
1117
1118 g_return_val_if_fail (GIMP_IS_GRADIENT (gradient), 0.0);
1119 g_return_val_if_fail (seg != NULL, 0.0);
1120
1121 if (seg->prev == NULL)
1122 {
1123 final_pos = 0;
1124 }
1125 else
1126 {
1127 gimp_data_freeze (GIMP_DATA (gradient));
1128
1129 final_pos = seg->prev->right = seg->left =
1130 CLAMP (pos,
1131 seg->prev->middle + EPSILON,
1132 seg->middle - EPSILON);
1133
1134 gimp_data_thaw (GIMP_DATA (gradient));
1135 }
1136
1137 return final_pos;
1138 }
1139
1140 gdouble
gimp_gradient_segment_get_right_pos(GimpGradient * gradient,GimpGradientSegment * seg)1141 gimp_gradient_segment_get_right_pos (GimpGradient *gradient,
1142 GimpGradientSegment *seg)
1143 {
1144 g_return_val_if_fail (GIMP_IS_GRADIENT (gradient), 0.0);
1145 g_return_val_if_fail (seg != NULL, 0.0);
1146
1147 return seg->right;
1148 }
1149
1150 gdouble
gimp_gradient_segment_set_right_pos(GimpGradient * gradient,GimpGradientSegment * seg,gdouble pos)1151 gimp_gradient_segment_set_right_pos (GimpGradient *gradient,
1152 GimpGradientSegment *seg,
1153 gdouble pos)
1154 {
1155 gdouble final_pos;
1156
1157 g_return_val_if_fail (GIMP_IS_GRADIENT (gradient), 0.0);
1158 g_return_val_if_fail (seg != NULL, 0.0);
1159
1160 if (seg->next == NULL)
1161 {
1162 final_pos = 1.0;
1163 }
1164 else
1165 {
1166 gimp_data_freeze (GIMP_DATA (gradient));
1167
1168 final_pos = seg->next->left = seg->right =
1169 CLAMP (pos,
1170 seg->middle + EPSILON,
1171 seg->next->middle - EPSILON);
1172
1173 gimp_data_thaw (GIMP_DATA (gradient));
1174 }
1175
1176 return final_pos;
1177 }
1178
1179 gdouble
gimp_gradient_segment_get_middle_pos(GimpGradient * gradient,GimpGradientSegment * seg)1180 gimp_gradient_segment_get_middle_pos (GimpGradient *gradient,
1181 GimpGradientSegment *seg)
1182 {
1183 g_return_val_if_fail (GIMP_IS_GRADIENT (gradient), 0.0);
1184 g_return_val_if_fail (seg != NULL, 0.0);
1185
1186 return seg->middle;
1187 }
1188
1189 gdouble
gimp_gradient_segment_set_middle_pos(GimpGradient * gradient,GimpGradientSegment * seg,gdouble pos)1190 gimp_gradient_segment_set_middle_pos (GimpGradient *gradient,
1191 GimpGradientSegment *seg,
1192 gdouble pos)
1193 {
1194 gdouble final_pos;
1195
1196 g_return_val_if_fail (GIMP_IS_GRADIENT (gradient), 0.0);
1197 g_return_val_if_fail (seg != NULL, 0.0);
1198
1199 gimp_data_freeze (GIMP_DATA (gradient));
1200
1201 final_pos = seg->middle =
1202 CLAMP (pos,
1203 seg->left + EPSILON,
1204 seg->right - EPSILON);
1205
1206 gimp_data_thaw (GIMP_DATA (gradient));
1207
1208 return final_pos;
1209 }
1210
1211 GimpGradientSegmentType
gimp_gradient_segment_get_blending_function(GimpGradient * gradient,GimpGradientSegment * seg)1212 gimp_gradient_segment_get_blending_function (GimpGradient *gradient,
1213 GimpGradientSegment *seg)
1214 {
1215 g_return_val_if_fail (GIMP_IS_GRADIENT (gradient), 0);
1216
1217 return seg->type;
1218 }
1219
1220 GimpGradientSegmentColor
gimp_gradient_segment_get_coloring_type(GimpGradient * gradient,GimpGradientSegment * seg)1221 gimp_gradient_segment_get_coloring_type (GimpGradient *gradient,
1222 GimpGradientSegment *seg)
1223 {
1224 g_return_val_if_fail (GIMP_IS_GRADIENT (gradient), 0);
1225
1226 return seg->color;
1227 }
1228
1229 gint
gimp_gradient_segment_range_get_n_segments(GimpGradient * gradient,GimpGradientSegment * range_l,GimpGradientSegment * range_r)1230 gimp_gradient_segment_range_get_n_segments (GimpGradient *gradient,
1231 GimpGradientSegment *range_l,
1232 GimpGradientSegment *range_r)
1233 {
1234 gint n_segments = 0;
1235
1236 g_return_val_if_fail (GIMP_IS_GRADIENT (gradient), 0);
1237 g_return_val_if_fail (range_l != NULL, 0);
1238
1239 for (; range_l != range_r; range_l = range_l->next)
1240 n_segments++;
1241
1242 if (range_r != NULL)
1243 n_segments++;
1244
1245 return n_segments;
1246 }
1247
1248 void
gimp_gradient_segment_range_compress(GimpGradient * gradient,GimpGradientSegment * range_l,GimpGradientSegment * range_r,gdouble new_l,gdouble new_r)1249 gimp_gradient_segment_range_compress (GimpGradient *gradient,
1250 GimpGradientSegment *range_l,
1251 GimpGradientSegment *range_r,
1252 gdouble new_l,
1253 gdouble new_r)
1254 {
1255 gdouble orig_l, orig_r;
1256 GimpGradientSegment *seg, *aseg;
1257
1258 g_return_if_fail (GIMP_IS_GRADIENT (gradient));
1259 g_return_if_fail (range_l != NULL);
1260
1261 gimp_data_freeze (GIMP_DATA (gradient));
1262
1263 if (! range_r)
1264 range_r = gimp_gradient_segment_get_last (range_l);
1265
1266 orig_l = range_l->left;
1267 orig_r = range_r->right;
1268
1269 if (orig_r - orig_l > EPSILON)
1270 {
1271 gdouble scale;
1272
1273 scale = (new_r - new_l) / (orig_r - orig_l);
1274
1275 seg = range_l;
1276
1277 do
1278 {
1279 if (seg->prev)
1280 seg->left = new_l + (seg->left - orig_l) * scale;
1281 seg->middle = new_l + (seg->middle - orig_l) * scale;
1282 if (seg->next)
1283 seg->right = new_l + (seg->right - orig_l) * scale;
1284
1285 /* Next */
1286
1287 aseg = seg;
1288 seg = seg->next;
1289 }
1290 while (aseg != range_r);
1291 }
1292 else
1293 {
1294 gint n;
1295 gint i;
1296
1297 n = gimp_gradient_segment_range_get_n_segments (gradient,
1298 range_l, range_r);
1299
1300 for (i = 0, seg = range_l; i < n; i++, seg = seg->next)
1301 {
1302 if (seg->prev)
1303 seg->left = new_l + (new_r - new_l) * (i + 0.0) / n;
1304 seg->middle = new_l + (new_r - new_l) * (i + 0.5) / n;;
1305 if (seg->next)
1306 seg->right = new_l + (new_r - new_l) * (i + 1.0) / n;
1307 }
1308 }
1309
1310 /* Make sure that the left and right endpoints of the range are *exactly*
1311 * equal to new_l and new_r; the above computations can introduce
1312 * numerical inaccuracies.
1313 */
1314
1315 range_l->left = new_l;
1316 range_l->right = new_r;
1317
1318 gimp_data_thaw (GIMP_DATA (gradient));
1319 }
1320
1321 void
gimp_gradient_segment_range_blend(GimpGradient * gradient,GimpGradientSegment * lseg,GimpGradientSegment * rseg,const GimpRGB * rgb1,const GimpRGB * rgb2,gboolean blend_colors,gboolean blend_opacity)1322 gimp_gradient_segment_range_blend (GimpGradient *gradient,
1323 GimpGradientSegment *lseg,
1324 GimpGradientSegment *rseg,
1325 const GimpRGB *rgb1,
1326 const GimpRGB *rgb2,
1327 gboolean blend_colors,
1328 gboolean blend_opacity)
1329 {
1330 GimpRGB d;
1331 gdouble left, len;
1332 GimpGradientSegment *seg;
1333 GimpGradientSegment *aseg;
1334
1335 g_return_if_fail (GIMP_IS_GRADIENT (gradient));
1336 g_return_if_fail (lseg != NULL);
1337
1338 gimp_data_freeze (GIMP_DATA (gradient));
1339
1340 if (! rseg)
1341 rseg = gimp_gradient_segment_get_last (lseg);
1342
1343 d.r = rgb2->r - rgb1->r;
1344 d.g = rgb2->g - rgb1->g;
1345 d.b = rgb2->b - rgb1->b;
1346 d.a = rgb2->a - rgb1->a;
1347
1348 left = lseg->left;
1349 len = rseg->right - left;
1350
1351 seg = lseg;
1352
1353 do
1354 {
1355 if (blend_colors)
1356 {
1357 seg->left_color.r = rgb1->r + (seg->left - left) / len * d.r;
1358 seg->left_color.g = rgb1->g + (seg->left - left) / len * d.g;
1359 seg->left_color.b = rgb1->b + (seg->left - left) / len * d.b;
1360
1361 seg->right_color.r = rgb1->r + (seg->right - left) / len * d.r;
1362 seg->right_color.g = rgb1->g + (seg->right - left) / len * d.g;
1363 seg->right_color.b = rgb1->b + (seg->right - left) / len * d.b;
1364 }
1365
1366 if (blend_opacity)
1367 {
1368 seg->left_color.a = rgb1->a + (seg->left - left) / len * d.a;
1369 seg->right_color.a = rgb1->a + (seg->right - left) / len * d.a;
1370 }
1371
1372 aseg = seg;
1373 seg = seg->next;
1374 }
1375 while (aseg != rseg);
1376 gimp_data_thaw (GIMP_DATA (gradient));
1377
1378 }
1379
1380 void
gimp_gradient_segment_range_set_blending_function(GimpGradient * gradient,GimpGradientSegment * start_seg,GimpGradientSegment * end_seg,GimpGradientSegmentType new_type)1381 gimp_gradient_segment_range_set_blending_function (GimpGradient *gradient,
1382 GimpGradientSegment *start_seg,
1383 GimpGradientSegment *end_seg,
1384 GimpGradientSegmentType new_type)
1385 {
1386 GimpGradientSegment *seg;
1387 gboolean reached_last_segment = FALSE;
1388
1389 g_return_if_fail (GIMP_IS_GRADIENT (gradient));
1390
1391 gimp_data_freeze (GIMP_DATA (gradient));
1392
1393 seg = start_seg;
1394 while (seg && ! reached_last_segment)
1395 {
1396 if (seg == end_seg)
1397 reached_last_segment = TRUE;
1398
1399 seg->type = new_type;
1400 seg = seg->next;
1401 }
1402
1403 gimp_data_thaw (GIMP_DATA (gradient));
1404 }
1405
1406 void
gimp_gradient_segment_range_set_coloring_type(GimpGradient * gradient,GimpGradientSegment * start_seg,GimpGradientSegment * end_seg,GimpGradientSegmentColor new_color)1407 gimp_gradient_segment_range_set_coloring_type (GimpGradient *gradient,
1408 GimpGradientSegment *start_seg,
1409 GimpGradientSegment *end_seg,
1410 GimpGradientSegmentColor new_color)
1411 {
1412 GimpGradientSegment *seg;
1413 gboolean reached_last_segment = FALSE;
1414
1415 g_return_if_fail (GIMP_IS_GRADIENT (gradient));
1416
1417 gimp_data_freeze (GIMP_DATA (gradient));
1418
1419 seg = start_seg;
1420 while (seg && ! reached_last_segment)
1421 {
1422 if (seg == end_seg)
1423 reached_last_segment = TRUE;
1424
1425 seg->color = new_color;
1426 seg = seg->next;
1427 }
1428
1429 gimp_data_thaw (GIMP_DATA (gradient));
1430 }
1431
1432 void
gimp_gradient_segment_range_flip(GimpGradient * gradient,GimpGradientSegment * start_seg,GimpGradientSegment * end_seg,GimpGradientSegment ** final_start_seg,GimpGradientSegment ** final_end_seg)1433 gimp_gradient_segment_range_flip (GimpGradient *gradient,
1434 GimpGradientSegment *start_seg,
1435 GimpGradientSegment *end_seg,
1436 GimpGradientSegment **final_start_seg,
1437 GimpGradientSegment **final_end_seg)
1438 {
1439 GimpGradientSegment *oseg, *oaseg;
1440 GimpGradientSegment *seg, *prev, *tmp;
1441 GimpGradientSegment *lseg, *rseg;
1442 gdouble left, right;
1443
1444 g_return_if_fail (GIMP_IS_GRADIENT (gradient));
1445
1446 gimp_data_freeze (GIMP_DATA (gradient));
1447
1448 if (! end_seg)
1449 end_seg = gimp_gradient_segment_get_last (start_seg);
1450
1451 left = start_seg->left;
1452 right = end_seg->right;
1453
1454 /* Build flipped segments */
1455
1456 prev = NULL;
1457 oseg = end_seg;
1458 tmp = NULL;
1459
1460 do
1461 {
1462 seg = gimp_gradient_segment_new ();
1463
1464 if (prev == NULL)
1465 {
1466 seg->left = left;
1467 tmp = seg; /* Remember first segment */
1468 }
1469 else
1470 seg->left = left + right - oseg->right;
1471
1472 seg->middle = left + right - oseg->middle;
1473 seg->right = left + right - oseg->left;
1474
1475 seg->left_color_type = oseg->right_color_type;
1476 seg->left_color = oseg->right_color;
1477
1478 seg->right_color_type = oseg->left_color_type;
1479 seg->right_color = oseg->left_color;
1480
1481 switch (oseg->type)
1482 {
1483 case GIMP_GRADIENT_SEGMENT_SPHERE_INCREASING:
1484 seg->type = GIMP_GRADIENT_SEGMENT_SPHERE_DECREASING;
1485 break;
1486
1487 case GIMP_GRADIENT_SEGMENT_SPHERE_DECREASING:
1488 seg->type = GIMP_GRADIENT_SEGMENT_SPHERE_INCREASING;
1489 break;
1490
1491 default:
1492 seg->type = oseg->type;
1493 }
1494
1495 switch (oseg->color)
1496 {
1497 case GIMP_GRADIENT_SEGMENT_HSV_CCW:
1498 seg->color = GIMP_GRADIENT_SEGMENT_HSV_CW;
1499 break;
1500
1501 case GIMP_GRADIENT_SEGMENT_HSV_CW:
1502 seg->color = GIMP_GRADIENT_SEGMENT_HSV_CCW;
1503 break;
1504
1505 default:
1506 seg->color = oseg->color;
1507 }
1508
1509 seg->prev = prev;
1510 seg->next = NULL;
1511
1512 if (prev)
1513 prev->next = seg;
1514
1515 prev = seg;
1516
1517 oaseg = oseg;
1518 oseg = oseg->prev; /* Move backwards! */
1519 }
1520 while (oaseg != start_seg);
1521
1522 seg->right = right; /* Squish accumulative error */
1523
1524 /* Free old segments */
1525
1526 lseg = start_seg->prev;
1527 rseg = end_seg->next;
1528
1529 oseg = start_seg;
1530
1531 do
1532 {
1533 oaseg = oseg->next;
1534 gimp_gradient_segment_free (oseg);
1535 oseg = oaseg;
1536 }
1537 while (oaseg != rseg);
1538
1539 /* Link in new segments */
1540
1541 if (lseg)
1542 lseg->next = tmp;
1543 else
1544 gradient->segments = tmp;
1545
1546 tmp->prev = lseg;
1547
1548 seg->next = rseg;
1549
1550 if (rseg)
1551 rseg->prev = seg;
1552
1553 /* Reset selection */
1554
1555 if (final_start_seg)
1556 *final_start_seg = tmp;
1557
1558 if (final_end_seg)
1559 *final_end_seg = seg;
1560
1561 /* Done */
1562
1563 gimp_data_thaw (GIMP_DATA (gradient));
1564 }
1565
1566 void
gimp_gradient_segment_range_replicate(GimpGradient * gradient,GimpGradientSegment * start_seg,GimpGradientSegment * end_seg,gint replicate_times,GimpGradientSegment ** final_start_seg,GimpGradientSegment ** final_end_seg)1567 gimp_gradient_segment_range_replicate (GimpGradient *gradient,
1568 GimpGradientSegment *start_seg,
1569 GimpGradientSegment *end_seg,
1570 gint replicate_times,
1571 GimpGradientSegment **final_start_seg,
1572 GimpGradientSegment **final_end_seg)
1573 {
1574 gdouble sel_left, sel_right, sel_len;
1575 gdouble new_left;
1576 gdouble factor;
1577 GimpGradientSegment *prev, *seg, *tmp;
1578 GimpGradientSegment *oseg, *oaseg;
1579 GimpGradientSegment *lseg, *rseg;
1580 gint i;
1581
1582 g_return_if_fail (GIMP_IS_GRADIENT (gradient));
1583
1584 if (! end_seg)
1585 end_seg = gimp_gradient_segment_get_last (start_seg);
1586
1587 if (replicate_times < 2)
1588 {
1589 *final_start_seg = start_seg;
1590 *final_end_seg = end_seg;
1591 return;
1592 }
1593
1594 gimp_data_freeze (GIMP_DATA (gradient));
1595
1596 /* Remember original parameters */
1597 sel_left = start_seg->left;
1598 sel_right = end_seg->right;
1599 sel_len = sel_right - sel_left;
1600
1601 factor = 1.0 / replicate_times;
1602
1603 /* Build replicated segments */
1604
1605 prev = NULL;
1606 seg = NULL;
1607 tmp = NULL;
1608
1609 for (i = 0; i < replicate_times; i++)
1610 {
1611 /* Build one cycle */
1612
1613 new_left = sel_left + i * factor * sel_len;
1614
1615 oseg = start_seg;
1616
1617 do
1618 {
1619 seg = gimp_gradient_segment_new ();
1620
1621 if (prev == NULL)
1622 {
1623 seg->left = sel_left;
1624 tmp = seg; /* Remember first segment */
1625 }
1626 else
1627 {
1628 seg->left = new_left + factor * (oseg->left - sel_left);
1629 }
1630
1631 seg->middle = new_left + factor * (oseg->middle - sel_left);
1632 seg->right = new_left + factor * (oseg->right - sel_left);
1633
1634 seg->left_color_type = oseg->left_color_type;
1635 seg->left_color = oseg->left_color;
1636
1637 seg->right_color_type = oseg->right_color_type;
1638 seg->right_color = oseg->right_color;
1639
1640 seg->type = oseg->type;
1641 seg->color = oseg->color;
1642
1643 seg->prev = prev;
1644 seg->next = NULL;
1645
1646 if (prev)
1647 prev->next = seg;
1648
1649 prev = seg;
1650
1651 oaseg = oseg;
1652 oseg = oseg->next;
1653 }
1654 while (oaseg != end_seg);
1655 }
1656
1657 seg->right = sel_right; /* Squish accumulative error */
1658
1659 /* Free old segments */
1660
1661 lseg = start_seg->prev;
1662 rseg = end_seg->next;
1663
1664 oseg = start_seg;
1665
1666 do
1667 {
1668 oaseg = oseg->next;
1669 gimp_gradient_segment_free (oseg);
1670 oseg = oaseg;
1671 }
1672 while (oaseg != rseg);
1673
1674 /* Link in new segments */
1675
1676 if (lseg)
1677 lseg->next = tmp;
1678 else
1679 gradient->segments = tmp;
1680
1681 tmp->prev = lseg;
1682
1683 seg->next = rseg;
1684
1685 if (rseg)
1686 rseg->prev = seg;
1687
1688 /* Reset selection */
1689
1690 if (final_start_seg)
1691 *final_start_seg = tmp;
1692
1693 if (final_end_seg)
1694 *final_end_seg = seg;
1695
1696 /* Done */
1697
1698 gimp_data_thaw (GIMP_DATA (gradient));
1699 }
1700
1701 void
gimp_gradient_segment_range_split_midpoint(GimpGradient * gradient,GimpContext * context,GimpGradientSegment * start_seg,GimpGradientSegment * end_seg,GimpGradientBlendColorSpace blend_color_space,GimpGradientSegment ** final_start_seg,GimpGradientSegment ** final_end_seg)1702 gimp_gradient_segment_range_split_midpoint (GimpGradient *gradient,
1703 GimpContext *context,
1704 GimpGradientSegment *start_seg,
1705 GimpGradientSegment *end_seg,
1706 GimpGradientBlendColorSpace blend_color_space,
1707 GimpGradientSegment **final_start_seg,
1708 GimpGradientSegment **final_end_seg)
1709 {
1710 GimpGradientSegment *seg, *lseg, *rseg;
1711
1712 g_return_if_fail (GIMP_IS_GRADIENT (gradient));
1713 g_return_if_fail (GIMP_IS_CONTEXT (context));
1714
1715 gimp_data_freeze (GIMP_DATA (gradient));
1716
1717 if (! end_seg)
1718 end_seg = gimp_gradient_segment_get_last (start_seg);
1719
1720 seg = start_seg;
1721
1722 do
1723 {
1724 gimp_gradient_segment_split_midpoint (gradient, context,
1725 seg, blend_color_space,
1726 &lseg, &rseg);
1727 seg = rseg->next;
1728 }
1729 while (lseg != end_seg);
1730
1731 if (final_start_seg)
1732 *final_start_seg = start_seg;
1733
1734 if (final_end_seg)
1735 *final_end_seg = rseg;
1736
1737 gimp_data_thaw (GIMP_DATA (gradient));
1738 }
1739
1740 void
gimp_gradient_segment_range_split_uniform(GimpGradient * gradient,GimpContext * context,GimpGradientSegment * start_seg,GimpGradientSegment * end_seg,gint parts,GimpGradientBlendColorSpace blend_color_space,GimpGradientSegment ** final_start_seg,GimpGradientSegment ** final_end_seg)1741 gimp_gradient_segment_range_split_uniform (GimpGradient *gradient,
1742 GimpContext *context,
1743 GimpGradientSegment *start_seg,
1744 GimpGradientSegment *end_seg,
1745 gint parts,
1746 GimpGradientBlendColorSpace blend_color_space,
1747 GimpGradientSegment **final_start_seg,
1748 GimpGradientSegment **final_end_seg)
1749 {
1750 GimpGradientSegment *seg, *aseg, *lseg, *rseg, *lsel;
1751
1752 g_return_if_fail (GIMP_IS_GRADIENT (gradient));
1753 g_return_if_fail (GIMP_IS_CONTEXT (context));
1754
1755 if (! end_seg)
1756 end_seg = gimp_gradient_segment_get_last (start_seg);
1757
1758 if (parts < 2)
1759 {
1760 *final_start_seg = start_seg;
1761 *final_end_seg = end_seg;
1762 return;
1763 }
1764
1765 gimp_data_freeze (GIMP_DATA (gradient));
1766
1767 seg = start_seg;
1768 lsel = NULL;
1769
1770 do
1771 {
1772 aseg = seg;
1773
1774 gimp_gradient_segment_split_uniform (gradient, context, seg,
1775 parts, blend_color_space,
1776 &lseg, &rseg);
1777
1778 if (seg == start_seg)
1779 lsel = lseg;
1780
1781 seg = rseg->next;
1782 }
1783 while (aseg != end_seg);
1784
1785 if (final_start_seg)
1786 *final_start_seg = lsel;
1787
1788 if (final_end_seg)
1789 *final_end_seg = rseg;
1790
1791 gimp_data_thaw (GIMP_DATA (gradient));
1792 }
1793
1794 void
gimp_gradient_segment_range_delete(GimpGradient * gradient,GimpGradientSegment * start_seg,GimpGradientSegment * end_seg,GimpGradientSegment ** final_start_seg,GimpGradientSegment ** final_end_seg)1795 gimp_gradient_segment_range_delete (GimpGradient *gradient,
1796 GimpGradientSegment *start_seg,
1797 GimpGradientSegment *end_seg,
1798 GimpGradientSegment **final_start_seg,
1799 GimpGradientSegment **final_end_seg)
1800 {
1801 GimpGradientSegment *lseg, *rseg, *seg, *aseg, *next;
1802 gdouble join;
1803
1804 g_return_if_fail (GIMP_IS_GRADIENT (gradient));
1805
1806 if (! end_seg)
1807 end_seg = gimp_gradient_segment_get_last (start_seg);
1808
1809 /* Remember segments to the left and to the right of the selection */
1810
1811 lseg = start_seg->prev;
1812 rseg = end_seg->next;
1813
1814 /* Cannot delete all the segments in the gradient */
1815
1816 if ((lseg == NULL) && (rseg == NULL))
1817 goto premature_return;
1818
1819 gimp_data_freeze (GIMP_DATA (gradient));
1820
1821 /* Calculate join point */
1822
1823 join = (start_seg->left +
1824 end_seg->right) / 2.0;
1825
1826 if (lseg == NULL)
1827 join = 0.0;
1828 else if (rseg == NULL)
1829 join = 1.0;
1830
1831 /* Move segments */
1832
1833 if (lseg != NULL)
1834 gimp_gradient_segment_range_compress (gradient, lseg, lseg,
1835 lseg->left, join);
1836
1837 if (rseg != NULL)
1838 gimp_gradient_segment_range_compress (gradient, rseg, rseg,
1839 join, rseg->right);
1840
1841 /* Link */
1842
1843 if (lseg)
1844 lseg->next = rseg;
1845
1846 if (rseg)
1847 rseg->prev = lseg;
1848
1849 /* Delete old segments */
1850
1851 seg = start_seg;
1852
1853 do
1854 {
1855 next = seg->next;
1856 aseg = seg;
1857
1858 gimp_gradient_segment_free (seg);
1859
1860 seg = next;
1861 }
1862 while (aseg != end_seg);
1863
1864 /* Change selection */
1865
1866 if (rseg)
1867 {
1868 if (final_start_seg)
1869 *final_start_seg = rseg;
1870
1871 if (final_end_seg)
1872 *final_end_seg = rseg;
1873 }
1874 else
1875 {
1876 if (final_start_seg)
1877 *final_start_seg = lseg;
1878
1879 if (final_end_seg)
1880 *final_end_seg = lseg;
1881 }
1882
1883 if (lseg == NULL)
1884 gradient->segments = rseg;
1885
1886 gimp_data_thaw (GIMP_DATA (gradient));
1887
1888 return;
1889
1890 premature_return:
1891 if (final_start_seg)
1892 *final_start_seg = start_seg;
1893 if (final_end_seg)
1894 *final_end_seg = end_seg;
1895 }
1896
1897 void
gimp_gradient_segment_range_merge(GimpGradient * gradient,GimpGradientSegment * start_seg,GimpGradientSegment * end_seg,GimpGradientSegment ** final_start_seg,GimpGradientSegment ** final_end_seg)1898 gimp_gradient_segment_range_merge (GimpGradient *gradient,
1899 GimpGradientSegment *start_seg,
1900 GimpGradientSegment *end_seg,
1901 GimpGradientSegment **final_start_seg,
1902 GimpGradientSegment **final_end_seg)
1903 {
1904 GimpGradientSegment *seg;
1905
1906 g_return_if_fail (GIMP_IS_GRADIENT (gradient));
1907
1908 if (! end_seg)
1909 end_seg = gimp_gradient_segment_get_last (start_seg);
1910
1911 gimp_data_freeze (GIMP_DATA (gradient));
1912
1913 /* Copy the end segment's right position and color to the start segment */
1914
1915 start_seg->right = end_seg->right;
1916 start_seg->right_color_type = end_seg->right_color_type;
1917 start_seg->right_color = end_seg->right_color;
1918
1919 /* Center the start segment's midpoint */
1920
1921 start_seg->middle = (start_seg->left + start_seg->right) / 2.0;
1922
1923 /* Remove range segments past the start segment from the segment list */
1924
1925 start_seg->next = end_seg->next;
1926
1927 if (start_seg->next)
1928 start_seg->next->prev = start_seg;
1929
1930 /* Merge the range's blend function and coloring type, and free the rest of
1931 * the segments.
1932 */
1933
1934 seg = end_seg;
1935
1936 while (seg != start_seg)
1937 {
1938 GimpGradientSegment *prev = seg->prev;
1939
1940 /* If the blend function and/or coloring type aren't uniform, reset them. */
1941
1942 if (seg->type != start_seg->type)
1943 start_seg->type = GIMP_GRADIENT_SEGMENT_LINEAR;
1944
1945 if (seg->color != start_seg->color)
1946 start_seg->color = GIMP_GRADIENT_SEGMENT_RGB;
1947
1948 gimp_gradient_segment_free (seg);
1949
1950 seg = prev;
1951 }
1952
1953 if (final_start_seg)
1954 *final_start_seg = start_seg;
1955 if (final_end_seg)
1956 *final_end_seg = start_seg;
1957
1958 gimp_data_thaw (GIMP_DATA (gradient));
1959 }
1960
1961 void
gimp_gradient_segment_range_recenter_handles(GimpGradient * gradient,GimpGradientSegment * start_seg,GimpGradientSegment * end_seg)1962 gimp_gradient_segment_range_recenter_handles (GimpGradient *gradient,
1963 GimpGradientSegment *start_seg,
1964 GimpGradientSegment *end_seg)
1965 {
1966 GimpGradientSegment *seg, *aseg;
1967
1968 g_return_if_fail (GIMP_IS_GRADIENT (gradient));
1969
1970 gimp_data_freeze (GIMP_DATA (gradient));
1971
1972 if (! end_seg)
1973 end_seg = gimp_gradient_segment_get_last (start_seg);
1974
1975 seg = start_seg;
1976
1977 do
1978 {
1979 seg->middle = (seg->left + seg->right) / 2.0;
1980
1981 aseg = seg;
1982 seg = seg->next;
1983 }
1984 while (aseg != end_seg);
1985
1986 gimp_data_thaw (GIMP_DATA (gradient));
1987 }
1988
1989 void
gimp_gradient_segment_range_redistribute_handles(GimpGradient * gradient,GimpGradientSegment * start_seg,GimpGradientSegment * end_seg)1990 gimp_gradient_segment_range_redistribute_handles (GimpGradient *gradient,
1991 GimpGradientSegment *start_seg,
1992 GimpGradientSegment *end_seg)
1993 {
1994 GimpGradientSegment *seg, *aseg;
1995 gdouble left, right, seg_len;
1996 gint num_segs;
1997 gint i;
1998
1999 g_return_if_fail (GIMP_IS_GRADIENT (gradient));
2000
2001 gimp_data_freeze (GIMP_DATA (gradient));
2002
2003 if (! end_seg)
2004 end_seg = gimp_gradient_segment_get_last (start_seg);
2005
2006 /* Count number of segments in selection */
2007
2008 num_segs = 0;
2009 seg = start_seg;
2010
2011 do
2012 {
2013 num_segs++;
2014 aseg = seg;
2015 seg = seg->next;
2016 }
2017 while (aseg != end_seg);
2018
2019 /* Calculate new segment length */
2020
2021 left = start_seg->left;
2022 right = end_seg->right;
2023 seg_len = (right - left) / num_segs;
2024
2025 /* Redistribute */
2026
2027 seg = start_seg;
2028
2029 for (i = 0; i < num_segs; i++)
2030 {
2031 seg->left = left + i * seg_len;
2032 seg->right = left + (i + 1) * seg_len;
2033 seg->middle = (seg->left + seg->right) / 2.0;
2034
2035 seg = seg->next;
2036 }
2037
2038 /* Fix endpoints to squish accumulative error */
2039
2040 start_seg->left = left;
2041 end_seg->right = right;
2042
2043 gimp_data_thaw (GIMP_DATA (gradient));
2044 }
2045
2046 gdouble
gimp_gradient_segment_range_move(GimpGradient * gradient,GimpGradientSegment * range_l,GimpGradientSegment * range_r,gdouble delta,gboolean control_compress)2047 gimp_gradient_segment_range_move (GimpGradient *gradient,
2048 GimpGradientSegment *range_l,
2049 GimpGradientSegment *range_r,
2050 gdouble delta,
2051 gboolean control_compress)
2052 {
2053 gdouble lbound, rbound;
2054 gint is_first, is_last;
2055 GimpGradientSegment *seg, *aseg;
2056
2057 g_return_val_if_fail (GIMP_IS_GRADIENT (gradient), 0.0);
2058
2059 gimp_data_freeze (GIMP_DATA (gradient));
2060
2061 if (! range_r)
2062 range_r = gimp_gradient_segment_get_last (range_l);
2063
2064 /* First or last segments in gradient? */
2065
2066 is_first = (range_l->prev == NULL);
2067 is_last = (range_r->next == NULL);
2068
2069 /* Calculate drag bounds */
2070
2071 if (! control_compress)
2072 {
2073 if (!is_first)
2074 lbound = range_l->prev->middle + EPSILON;
2075 else
2076 lbound = range_l->left + EPSILON;
2077
2078 if (!is_last)
2079 rbound = range_r->next->middle - EPSILON;
2080 else
2081 rbound = range_r->right - EPSILON;
2082 }
2083 else
2084 {
2085 if (!is_first)
2086 lbound = range_l->prev->left + 2.0 * EPSILON;
2087 else
2088 lbound = range_l->left + EPSILON;
2089
2090 if (!is_last)
2091 rbound = range_r->next->right - 2.0 * EPSILON;
2092 else
2093 rbound = range_r->right - EPSILON;
2094 }
2095
2096 /* Fix the delta if necessary */
2097
2098 if (delta < 0.0)
2099 {
2100 if (!is_first)
2101 {
2102 if (range_l->left + delta < lbound)
2103 delta = lbound - range_l->left;
2104 }
2105 else
2106 if (range_l->middle + delta < lbound)
2107 delta = lbound - range_l->middle;
2108 }
2109 else
2110 {
2111 if (!is_last)
2112 {
2113 if (range_r->right + delta > rbound)
2114 delta = rbound - range_r->right;
2115 }
2116 else
2117 if (range_r->middle + delta > rbound)
2118 delta = rbound - range_r->middle;
2119 }
2120
2121 /* Move all the segments inside the range */
2122
2123 seg = range_l;
2124
2125 do
2126 {
2127 if (!((seg == range_l) && is_first))
2128 seg->left += delta;
2129
2130 seg->middle += delta;
2131
2132 if (!((seg == range_r) && is_last))
2133 seg->right += delta;
2134
2135 /* Next */
2136
2137 aseg = seg;
2138 seg = seg->next;
2139 }
2140 while (aseg != range_r);
2141
2142 /* Fix the segments that surround the range */
2143
2144 if (!is_first)
2145 {
2146 if (! control_compress)
2147 range_l->prev->right = range_l->left;
2148 else
2149 gimp_gradient_segment_range_compress (gradient,
2150 range_l->prev, range_l->prev,
2151 range_l->prev->left, range_l->left);
2152 }
2153
2154 if (!is_last)
2155 {
2156 if (! control_compress)
2157 range_r->next->left = range_r->right;
2158 else
2159 gimp_gradient_segment_range_compress (gradient,
2160 range_r->next, range_r->next,
2161 range_r->right, range_r->next->right);
2162 }
2163
2164 gimp_data_thaw (GIMP_DATA (gradient));
2165
2166 return delta;
2167 }
2168
2169
2170 /* private functions */
2171
2172 static inline GimpGradientSegment *
gimp_gradient_get_segment_at_internal(GimpGradient * gradient,GimpGradientSegment * seg,gdouble pos)2173 gimp_gradient_get_segment_at_internal (GimpGradient *gradient,
2174 GimpGradientSegment *seg,
2175 gdouble pos)
2176 {
2177 /* handle FP imprecision at the edges of the gradient */
2178 pos = CLAMP (pos, 0.0, 1.0);
2179
2180 if (! seg)
2181 seg = gradient->segments;
2182
2183 if (pos >= seg->left)
2184 {
2185 while (seg->next && pos >= seg->right)
2186 seg = seg->next;
2187 }
2188 else
2189 {
2190 do
2191 seg = seg->prev;
2192 while (pos < seg->left);
2193 }
2194
2195 return seg;
2196 }
2197
2198 static void
gimp_gradient_get_flat_color(GimpContext * context,const GimpRGB * color,GimpGradientColor color_type,GimpRGB * flat_color)2199 gimp_gradient_get_flat_color (GimpContext *context,
2200 const GimpRGB *color,
2201 GimpGradientColor color_type,
2202 GimpRGB *flat_color)
2203 {
2204 switch (color_type)
2205 {
2206 case GIMP_GRADIENT_COLOR_FIXED:
2207 *flat_color = *color;
2208 break;
2209
2210 case GIMP_GRADIENT_COLOR_FOREGROUND:
2211 case GIMP_GRADIENT_COLOR_FOREGROUND_TRANSPARENT:
2212 gimp_context_get_foreground (context, flat_color);
2213
2214 if (color_type == GIMP_GRADIENT_COLOR_FOREGROUND_TRANSPARENT)
2215 gimp_rgb_set_alpha (flat_color, 0.0);
2216 break;
2217
2218 case GIMP_GRADIENT_COLOR_BACKGROUND:
2219 case GIMP_GRADIENT_COLOR_BACKGROUND_TRANSPARENT:
2220 gimp_context_get_background (context, flat_color);
2221
2222 if (color_type == GIMP_GRADIENT_COLOR_BACKGROUND_TRANSPARENT)
2223 gimp_rgb_set_alpha (flat_color, 0.0);
2224 break;
2225 }
2226 }
2227
2228 static inline gdouble
gimp_gradient_calc_linear_factor(gdouble middle,gdouble pos)2229 gimp_gradient_calc_linear_factor (gdouble middle,
2230 gdouble pos)
2231 {
2232 if (pos <= middle)
2233 {
2234 if (middle < EPSILON)
2235 return 0.0;
2236 else
2237 return 0.5 * pos / middle;
2238 }
2239 else
2240 {
2241 pos -= middle;
2242 middle = 1.0 - middle;
2243
2244 if (middle < EPSILON)
2245 return 1.0;
2246 else
2247 return 0.5 + 0.5 * pos / middle;
2248 }
2249 }
2250
2251 static inline gdouble
gimp_gradient_calc_curved_factor(gdouble middle,gdouble pos)2252 gimp_gradient_calc_curved_factor (gdouble middle,
2253 gdouble pos)
2254 {
2255 if (middle < EPSILON)
2256 return 1.0;
2257 else if (1.0 - middle < EPSILON)
2258 return 0.0;
2259
2260 return exp (-G_LN2 * log (pos) / log (middle));
2261 }
2262
2263 static inline gdouble
gimp_gradient_calc_sine_factor(gdouble middle,gdouble pos)2264 gimp_gradient_calc_sine_factor (gdouble middle,
2265 gdouble pos)
2266 {
2267 pos = gimp_gradient_calc_linear_factor (middle, pos);
2268
2269 return (sin ((-G_PI / 2.0) + G_PI * pos) + 1.0) / 2.0;
2270 }
2271
2272 static inline gdouble
gimp_gradient_calc_sphere_increasing_factor(gdouble middle,gdouble pos)2273 gimp_gradient_calc_sphere_increasing_factor (gdouble middle,
2274 gdouble pos)
2275 {
2276 pos = gimp_gradient_calc_linear_factor (middle, pos) - 1.0;
2277
2278 /* Works for convex increasing and concave decreasing */
2279 return sqrt (1.0 - pos * pos);
2280 }
2281
2282 static inline gdouble
gimp_gradient_calc_sphere_decreasing_factor(gdouble middle,gdouble pos)2283 gimp_gradient_calc_sphere_decreasing_factor (gdouble middle,
2284 gdouble pos)
2285 {
2286 pos = gimp_gradient_calc_linear_factor (middle, pos);
2287
2288 /* Works for convex decreasing and concave increasing */
2289 return 1.0 - sqrt(1.0 - pos * pos);
2290 }
2291
2292 static inline gdouble
gimp_gradient_calc_step_factor(gdouble middle,gdouble pos)2293 gimp_gradient_calc_step_factor (gdouble middle,
2294 gdouble pos)
2295 {
2296 return pos >= middle;
2297 }
2298