2003-11-14 Michael Natterer <mitch@gimp.org> * libgimpwidgets/gimpquerybox.c * app/widgets/gimpwidgets-utils.c: configure the labels in the message dialog and the query boxes to do automatic word wrapping to be HIG compliant. * app/app_procs.c * app/batch.c * app/config/gimpconfig-deserialize.c * app/config/gimpconfig-path.c * app/config/gimpconfig-utils.c * app/config/gimpconfigwriter.c * app/config/gimpscanner.c * app/core/gimpbrush.c * app/core/gimpbrushgenerated.c * app/core/gimpbrushpipe.c * app/core/gimpdatafactory.c * app/core/gimpgradient.c * app/core/gimpimage-merge.c * app/core/gimpimage.c * app/core/gimpimagefile.c * app/core/gimplayer-floating-sel.c * app/core/gimppalette.c * app/core/gimppattern.c * app/core/gimpselection.c * app/display/gimpdisplayshell.c * app/file/file-utils.c * app/gui/brush-select.c * app/gui/dialogs-commands.c * app/gui/drawable-commands.c * app/gui/edit-commands.c * app/gui/file-commands.c * app/gui/file-new-dialog.c * app/gui/font-select.c * app/gui/gradient-select.c * app/gui/gui.c * app/gui/image-commands.c * app/gui/layers-commands.c * app/gui/palette-select.c * app/gui/palettes-commands.c * app/gui/pattern-select.c * app/gui/preferences-dialog.c * app/gui/select-commands.c * app/gui/stroke-dialog.c * app/gui/tool-options-menu.c * app/gui/vectors-commands.c * app/gui/view-commands.c * app/plug-in/plug-in-message.c * app/plug-in/plug-in.c * app/plug-in/plug-ins.c * app/text/gimptextlayer-xcf.c * app/text/gimptextlayer.c * app/tools/gimpcurvestool.c * app/tools/gimphuesaturationtool.c * app/tools/gimplevelstool.c * app/tools/gimptransformtool.c * app/vectors/gimpvectors-export.c * app/widgets/gimpdatafactoryview.c * app/widgets/gimphelp.c * app/widgets/gimptemplateview.c * app/widgets/gimptooloptionseditor.c * app/xcf/xcf.c * tools/pdbgen/pdb/image.pdb: removed explicit newlines from messages. Reduced number of translatable strings by making many file error messages the same. Quote single words and filenames with 'foo', not "foo". Replaced some more "drawable" by "layer". General message cleanup and consistency check. * app/pdb/image_cmds.c: regenerated.
1197 lines
31 KiB
C
1197 lines
31 KiB
C
/* The GIMP -- an image manipulation program
|
|
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
|
|
#include <glib-object.h>
|
|
|
|
#include "libgimpcolor/gimpcolor.h"
|
|
#include "libgimpmath/gimpmath.h"
|
|
#include "libgimpbase/gimpbase.h"
|
|
|
|
#include "core-types.h"
|
|
|
|
#include "base/temp-buf.h"
|
|
|
|
#include "gimpimage.h"
|
|
#include "gimpgradient.h"
|
|
|
|
#include "gimp-intl.h"
|
|
|
|
|
|
#define EPSILON 1e-10
|
|
|
|
|
|
static void gimp_gradient_class_init (GimpGradientClass *klass);
|
|
static void gimp_gradient_init (GimpGradient *gradient);
|
|
|
|
static void gimp_gradient_finalize (GObject *object);
|
|
|
|
static gsize gimp_gradient_get_memsize (GimpObject *object,
|
|
gsize *gui_size);
|
|
|
|
static void gimp_gradient_get_preview_size (GimpViewable *viewable,
|
|
gint size,
|
|
gboolean popup,
|
|
gboolean dot_for_dot,
|
|
gint *width,
|
|
gint *height);
|
|
static gboolean gimp_gradient_get_popup_size (GimpViewable *viewable,
|
|
gint width,
|
|
gint height,
|
|
gboolean dot_for_dot,
|
|
gint *popup_width,
|
|
gint *popup_height);
|
|
static TempBuf * gimp_gradient_get_new_preview (GimpViewable *viewable,
|
|
gint width,
|
|
gint height);
|
|
static void gimp_gradient_dirty (GimpData *data);
|
|
static gboolean gimp_gradient_save (GimpData *data,
|
|
GError **error);
|
|
static gchar * gimp_gradient_get_extension (GimpData *data);
|
|
static GimpData * gimp_gradient_duplicate (GimpData *data,
|
|
gboolean stingy_memory_use);
|
|
|
|
static gdouble gimp_gradient_calc_linear_factor (gdouble middle,
|
|
gdouble pos);
|
|
static gdouble gimp_gradient_calc_curved_factor (gdouble middle,
|
|
gdouble pos);
|
|
static gdouble gimp_gradient_calc_sine_factor (gdouble middle,
|
|
gdouble pos);
|
|
static gdouble gimp_gradient_calc_sphere_increasing_factor (gdouble middle,
|
|
gdouble pos);
|
|
static gdouble gimp_gradient_calc_sphere_decreasing_factor (gdouble middle,
|
|
gdouble pos);
|
|
|
|
|
|
static GimpDataClass *parent_class = NULL;
|
|
|
|
|
|
GType
|
|
gimp_gradient_get_type (void)
|
|
{
|
|
static GType gradient_type = 0;
|
|
|
|
if (! gradient_type)
|
|
{
|
|
static const GTypeInfo gradient_info =
|
|
{
|
|
sizeof (GimpGradientClass),
|
|
(GBaseInitFunc) NULL,
|
|
(GBaseFinalizeFunc) NULL,
|
|
(GClassInitFunc) gimp_gradient_class_init,
|
|
NULL, /* class_finalize */
|
|
NULL, /* class_data */
|
|
sizeof (GimpGradient),
|
|
0, /* n_preallocs */
|
|
(GInstanceInitFunc) gimp_gradient_init,
|
|
};
|
|
|
|
gradient_type = g_type_register_static (GIMP_TYPE_DATA,
|
|
"GimpGradient",
|
|
&gradient_info, 0);
|
|
}
|
|
|
|
return gradient_type;
|
|
}
|
|
|
|
static void
|
|
gimp_gradient_class_init (GimpGradientClass *klass)
|
|
{
|
|
GObjectClass *object_class;
|
|
GimpObjectClass *gimp_object_class;
|
|
GimpViewableClass *viewable_class;
|
|
GimpDataClass *data_class;
|
|
|
|
object_class = G_OBJECT_CLASS (klass);
|
|
gimp_object_class = GIMP_OBJECT_CLASS (klass);
|
|
viewable_class = GIMP_VIEWABLE_CLASS (klass);
|
|
data_class = GIMP_DATA_CLASS (klass);
|
|
|
|
parent_class = g_type_class_peek_parent (klass);
|
|
|
|
object_class->finalize = gimp_gradient_finalize;
|
|
|
|
gimp_object_class->get_memsize = gimp_gradient_get_memsize;
|
|
|
|
viewable_class->get_preview_size = gimp_gradient_get_preview_size;
|
|
viewable_class->get_popup_size = gimp_gradient_get_popup_size;
|
|
viewable_class->get_new_preview = gimp_gradient_get_new_preview;
|
|
|
|
data_class->dirty = gimp_gradient_dirty;
|
|
data_class->save = gimp_gradient_save;
|
|
data_class->get_extension = gimp_gradient_get_extension;
|
|
data_class->duplicate = gimp_gradient_duplicate;
|
|
}
|
|
|
|
static void
|
|
gimp_gradient_init (GimpGradient *gradient)
|
|
{
|
|
gradient->segments = NULL;
|
|
gradient->last_visited = NULL;
|
|
}
|
|
|
|
static void
|
|
gimp_gradient_finalize (GObject *object)
|
|
{
|
|
GimpGradient *gradient;
|
|
|
|
gradient = GIMP_GRADIENT (object);
|
|
|
|
if (gradient->segments)
|
|
{
|
|
gimp_gradient_segments_free (gradient->segments);
|
|
gradient->segments = NULL;
|
|
}
|
|
|
|
G_OBJECT_CLASS (parent_class)->finalize (object);
|
|
}
|
|
|
|
static gsize
|
|
gimp_gradient_get_memsize (GimpObject *object,
|
|
gsize *gui_size)
|
|
{
|
|
GimpGradient *gradient;
|
|
GimpGradientSegment *segment;
|
|
gsize memsize = 0;
|
|
|
|
gradient = GIMP_GRADIENT (object);
|
|
|
|
for (segment = gradient->segments; segment; segment = segment->next)
|
|
memsize += sizeof (GimpGradientSegment);
|
|
|
|
return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object,
|
|
gui_size);
|
|
}
|
|
|
|
static void
|
|
gimp_gradient_get_preview_size (GimpViewable *viewable,
|
|
gint size,
|
|
gboolean popup,
|
|
gboolean dot_for_dot,
|
|
gint *width,
|
|
gint *height)
|
|
{
|
|
*width = size;
|
|
*height = size / 2;
|
|
}
|
|
|
|
static gboolean
|
|
gimp_gradient_get_popup_size (GimpViewable *viewable,
|
|
gint width,
|
|
gint height,
|
|
gboolean dot_for_dot,
|
|
gint *popup_width,
|
|
gint *popup_height)
|
|
{
|
|
if (width < 128 || height < 32)
|
|
{
|
|
*popup_width = 128;
|
|
*popup_height = 32;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static TempBuf *
|
|
gimp_gradient_get_new_preview (GimpViewable *viewable,
|
|
gint width,
|
|
gint height)
|
|
{
|
|
GimpGradient *gradient;
|
|
TempBuf *temp_buf;
|
|
guchar *buf;
|
|
guchar *p;
|
|
guchar *row;
|
|
gint x, y;
|
|
gdouble dx, cur_x;
|
|
GimpRGB color;
|
|
|
|
gradient = GIMP_GRADIENT (viewable);
|
|
|
|
dx = 1.0 / (width - 1);
|
|
cur_x = 0.0;
|
|
p = row = g_malloc (width * 4);
|
|
|
|
/* Create lines to fill the image */
|
|
|
|
for (x = 0; x < width; x++)
|
|
{
|
|
gimp_gradient_get_color_at (gradient, cur_x, FALSE, &color);
|
|
|
|
*p++ = color.r * 255.0;
|
|
*p++ = color.g * 255.0;
|
|
*p++ = color.b * 255.0;
|
|
*p++ = color.a * 255.0;
|
|
|
|
cur_x += dx;
|
|
}
|
|
|
|
temp_buf = temp_buf_new (width, height, 4, 0, 0, NULL);
|
|
|
|
buf = temp_buf_data (temp_buf);
|
|
|
|
for (y = 0; y < height; y++)
|
|
memcpy (buf + (width * y * 4), row, width * 4);
|
|
|
|
g_free (row);
|
|
|
|
return temp_buf;
|
|
}
|
|
|
|
static GimpData *
|
|
gimp_gradient_duplicate (GimpData *data,
|
|
gboolean stingy_memory_use)
|
|
{
|
|
GimpGradient *gradient;
|
|
GimpGradientSegment *head, *prev, *cur, *orig;
|
|
|
|
gradient = g_object_new (GIMP_TYPE_GRADIENT, NULL);
|
|
|
|
gimp_data_dirty (GIMP_DATA (gradient));
|
|
|
|
prev = NULL;
|
|
orig = GIMP_GRADIENT (data)->segments;
|
|
head = NULL;
|
|
|
|
while (orig)
|
|
{
|
|
cur = gimp_gradient_segment_new ();
|
|
|
|
*cur = *orig; /* Copy everything */
|
|
|
|
cur->prev = prev;
|
|
cur->next = NULL;
|
|
|
|
if (prev)
|
|
prev->next = cur;
|
|
else
|
|
head = cur; /* Remember head */
|
|
|
|
prev = cur;
|
|
orig = orig->next;
|
|
}
|
|
|
|
gradient->segments = head;
|
|
|
|
return GIMP_DATA (gradient);
|
|
}
|
|
|
|
GimpData *
|
|
gimp_gradient_new (const gchar *name,
|
|
gboolean stingy_memory_use)
|
|
{
|
|
GimpGradient *gradient;
|
|
|
|
g_return_val_if_fail (name != NULL, NULL);
|
|
|
|
gradient = g_object_new (GIMP_TYPE_GRADIENT, NULL);
|
|
|
|
gimp_object_set_name (GIMP_OBJECT (gradient), name);
|
|
|
|
gradient->segments = gimp_gradient_segment_new ();
|
|
|
|
return GIMP_DATA (gradient);
|
|
}
|
|
|
|
GimpData *
|
|
gimp_gradient_get_standard (void)
|
|
{
|
|
static GimpGradient *standard_gradient = NULL;
|
|
|
|
if (! standard_gradient)
|
|
{
|
|
standard_gradient = GIMP_GRADIENT (gimp_gradient_new ("Standard", FALSE));
|
|
|
|
g_object_ref (standard_gradient);
|
|
}
|
|
|
|
return GIMP_DATA (standard_gradient);
|
|
}
|
|
|
|
GimpData *
|
|
gimp_gradient_load (const gchar *filename,
|
|
gboolean stingy_memory_use,
|
|
GError **error)
|
|
{
|
|
GimpGradient *gradient;
|
|
GimpGradientSegment *seg;
|
|
GimpGradientSegment *prev;
|
|
gint num_segments;
|
|
gint i;
|
|
gint type, color;
|
|
FILE *file;
|
|
gchar line[1024];
|
|
|
|
g_return_val_if_fail (filename != NULL, NULL);
|
|
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
|
|
|
|
file = fopen (filename, "rb");
|
|
if (!file)
|
|
{
|
|
g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_OPEN,
|
|
_("Could not open '%s' for reading: %s"),
|
|
filename, g_strerror (errno));
|
|
return NULL;
|
|
}
|
|
|
|
fgets (line, 1024, file);
|
|
if (strcmp (line, "GIMP Gradient\n") != 0)
|
|
{
|
|
g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ,
|
|
_("Fatal parse error in gradient file '%s': "
|
|
"Not a GIMP gradient file."),
|
|
filename);
|
|
fclose (file);
|
|
return NULL;
|
|
}
|
|
|
|
gradient = g_object_new (GIMP_TYPE_GRADIENT, NULL);
|
|
|
|
gimp_data_set_filename (GIMP_DATA (gradient), filename);
|
|
|
|
fgets (line, 1024, file);
|
|
if (! strncmp (line, "Name: ", strlen ("Name: ")))
|
|
{
|
|
gchar *utf8;
|
|
|
|
utf8 = gimp_any_to_utf8 (&line[strlen ("Name: ")], -1,
|
|
_("Invalid UTF-8 string in gradient file '%s'."),
|
|
filename);
|
|
g_strstrip (utf8);
|
|
|
|
gimp_object_set_name (GIMP_OBJECT (gradient), utf8);
|
|
g_free (utf8);
|
|
|
|
fgets (line, 1024, file);
|
|
}
|
|
else /* old gradient format */
|
|
{
|
|
gchar *basename;
|
|
gchar *utf8;
|
|
|
|
basename = g_path_get_basename (filename);
|
|
|
|
utf8 = g_filename_to_utf8 (basename, -1, NULL, NULL, NULL);
|
|
g_free (basename);
|
|
|
|
gimp_object_set_name (GIMP_OBJECT (gradient), utf8);
|
|
g_free (utf8);
|
|
}
|
|
|
|
num_segments = atoi (line);
|
|
|
|
if (num_segments < 1)
|
|
{
|
|
g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ,
|
|
_("Fatal parse error in gradient file '%s': "
|
|
"File is corrupt."),
|
|
filename);
|
|
g_object_unref (gradient);
|
|
fclose (file);
|
|
return NULL;
|
|
}
|
|
|
|
prev = NULL;
|
|
|
|
for (i = 0; i < num_segments; i++)
|
|
{
|
|
gchar *end;
|
|
|
|
seg = gimp_gradient_segment_new ();
|
|
|
|
seg->prev = prev;
|
|
|
|
if (prev)
|
|
prev->next = seg;
|
|
else
|
|
gradient->segments = seg;
|
|
|
|
fgets (line, 1024, file);
|
|
|
|
seg->left = g_ascii_strtod (line, &end);
|
|
if (end && errno != ERANGE)
|
|
seg->middle = g_ascii_strtod (end, &end);
|
|
if (end && errno != ERANGE)
|
|
seg->right = g_ascii_strtod (end, &end);
|
|
|
|
if (end && errno != ERANGE)
|
|
seg->left_color.r = g_ascii_strtod (end, &end);
|
|
if (end && errno != ERANGE)
|
|
seg->left_color.g = g_ascii_strtod (end, &end);
|
|
if (end && errno != ERANGE)
|
|
seg->left_color.b = g_ascii_strtod (end, &end);
|
|
if (end && errno != ERANGE)
|
|
seg->left_color.a = g_ascii_strtod (end, &end);
|
|
|
|
if (end && errno != ERANGE)
|
|
seg->right_color.r = g_ascii_strtod (end, &end);
|
|
if (end && errno != ERANGE)
|
|
seg->right_color.g = g_ascii_strtod (end, &end);
|
|
if (end && errno != ERANGE)
|
|
seg->right_color.b = g_ascii_strtod (end, &end);
|
|
if (end && errno != ERANGE)
|
|
seg->right_color.a = g_ascii_strtod (end, &end);
|
|
|
|
if (errno != ERANGE &&
|
|
sscanf (end, "%d %d", &type, &color) == 2)
|
|
{
|
|
seg->type = (GimpGradientSegmentType) type;
|
|
seg->color = (GimpGradientSegmentColor) color;
|
|
}
|
|
else
|
|
{
|
|
g_message (_("Corrupt segment %d in gradient file '%s'."),
|
|
i, filename);
|
|
}
|
|
|
|
prev = seg;
|
|
}
|
|
|
|
fclose (file);
|
|
|
|
GIMP_DATA (gradient)->dirty = FALSE;
|
|
|
|
return GIMP_DATA (gradient);
|
|
}
|
|
|
|
static void
|
|
gimp_gradient_dirty (GimpData *data)
|
|
{
|
|
GimpGradient *gradient;
|
|
|
|
gradient = GIMP_GRADIENT (data);
|
|
|
|
gradient->last_visited = NULL;
|
|
|
|
if (GIMP_DATA_CLASS (parent_class)->dirty)
|
|
GIMP_DATA_CLASS (parent_class)->dirty (data);
|
|
}
|
|
|
|
static gboolean
|
|
gimp_gradient_save (GimpData *data,
|
|
GError **error)
|
|
{
|
|
GimpGradient *gradient;
|
|
GimpGradientSegment *seg;
|
|
gint num_segments;
|
|
FILE *file;
|
|
|
|
gradient = GIMP_GRADIENT (data);
|
|
|
|
file = fopen (data->filename, "wb");
|
|
if (! file)
|
|
{
|
|
g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_OPEN,
|
|
_("Could not open '%s' for writing: %s"),
|
|
data->filename, g_strerror (errno));
|
|
return FALSE;
|
|
}
|
|
|
|
/* File format is:
|
|
*
|
|
* GIMP Gradient
|
|
* Name: name
|
|
* number_of_segments
|
|
* left middle right r0 g0 b0 a0 r1 g1 b1 a1 type coloring
|
|
* left middle right r0 g0 b0 a0 r1 g1 b1 a1 type coloring
|
|
* ...
|
|
*/
|
|
|
|
fprintf (file, "GIMP Gradient\n");
|
|
|
|
fprintf (file, "Name: %s\n", GIMP_OBJECT (gradient)->name);
|
|
|
|
/* Count number of segments */
|
|
num_segments = 0;
|
|
seg = gradient->segments;
|
|
|
|
while (seg)
|
|
{
|
|
num_segments++;
|
|
seg = seg->next;
|
|
}
|
|
|
|
/* Write rest of file */
|
|
fprintf (file, "%d\n", num_segments);
|
|
|
|
for (seg = gradient->segments; seg; seg = seg->next)
|
|
{
|
|
gchar buf[11][G_ASCII_DTOSTR_BUF_SIZE];
|
|
|
|
g_ascii_formatd (buf[0], G_ASCII_DTOSTR_BUF_SIZE, "%f", seg->left);
|
|
g_ascii_formatd (buf[1], G_ASCII_DTOSTR_BUF_SIZE, "%f", seg->middle);
|
|
g_ascii_formatd (buf[2], G_ASCII_DTOSTR_BUF_SIZE, "%f", seg->right);
|
|
g_ascii_formatd (buf[3], G_ASCII_DTOSTR_BUF_SIZE, "%f", seg->left_color.r);
|
|
g_ascii_formatd (buf[4], G_ASCII_DTOSTR_BUF_SIZE, "%f", seg->left_color.g);
|
|
g_ascii_formatd (buf[5], G_ASCII_DTOSTR_BUF_SIZE, "%f", seg->left_color.b);
|
|
g_ascii_formatd (buf[6], G_ASCII_DTOSTR_BUF_SIZE, "%f", seg->left_color.a);
|
|
g_ascii_formatd (buf[7], G_ASCII_DTOSTR_BUF_SIZE, "%f", seg->right_color.r);
|
|
g_ascii_formatd (buf[8], G_ASCII_DTOSTR_BUF_SIZE, "%f", seg->right_color.g);
|
|
g_ascii_formatd (buf[9], G_ASCII_DTOSTR_BUF_SIZE, "%f", seg->right_color.b);
|
|
g_ascii_formatd (buf[10], G_ASCII_DTOSTR_BUF_SIZE, "%f", seg->right_color.a);
|
|
|
|
fprintf (file, "%s %s %s %s %s %s %s %s %s %s %s %d %d\n",
|
|
buf[0], buf[1], buf[2], buf[3], buf[4],
|
|
buf[5], buf[6], buf[7], buf[8], buf[9], buf[10],
|
|
(gint) seg->type,
|
|
(gint) seg->color);
|
|
}
|
|
|
|
fclose (file);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gchar *
|
|
gimp_gradient_get_extension (GimpData *data)
|
|
{
|
|
return GIMP_GRADIENT_FILE_EXTENSION;
|
|
}
|
|
|
|
gboolean
|
|
gimp_gradient_save_as_pov (GimpGradient *gradient,
|
|
const gchar *filename,
|
|
GError **error)
|
|
{
|
|
FILE *file;
|
|
GimpGradientSegment *seg;
|
|
gchar buf[G_ASCII_DTOSTR_BUF_SIZE];
|
|
gchar color_buf[4][G_ASCII_DTOSTR_BUF_SIZE];
|
|
|
|
g_return_val_if_fail (GIMP_IS_GRADIENT (gradient), FALSE);
|
|
g_return_val_if_fail (filename != NULL, FALSE);
|
|
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
|
|
|
|
file = fopen (filename, "wb");
|
|
|
|
if (! file)
|
|
{
|
|
g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_OPEN,
|
|
_("Could not open '%s' for writing: %s"),
|
|
filename, g_strerror (errno));
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
fprintf (file, "/* color_map file created by the GIMP */\n");
|
|
fprintf (file, "/* http://www.gimp.org/ */\n");
|
|
|
|
fprintf (file, "color_map {\n");
|
|
|
|
for (seg = gradient->segments; seg; seg = seg->next)
|
|
{
|
|
/* Left */
|
|
g_ascii_formatd (buf, G_ASCII_DTOSTR_BUF_SIZE, "%f",
|
|
seg->left);
|
|
g_ascii_formatd (color_buf[0], G_ASCII_DTOSTR_BUF_SIZE, "%f",
|
|
seg->left_color.r);
|
|
g_ascii_formatd (color_buf[1], G_ASCII_DTOSTR_BUF_SIZE, "%f",
|
|
seg->left_color.g);
|
|
g_ascii_formatd (color_buf[2], G_ASCII_DTOSTR_BUF_SIZE, "%f",
|
|
seg->left_color.b);
|
|
g_ascii_formatd (color_buf[3], G_ASCII_DTOSTR_BUF_SIZE, "%f",
|
|
1.0 - seg->left_color.a);
|
|
|
|
fprintf (file, "\t[%s color rgbt <%s, %s, %s, %s>]\n",
|
|
buf,
|
|
color_buf[0], color_buf[1], color_buf[2], color_buf[3]);
|
|
|
|
/* Middle */
|
|
g_ascii_formatd (buf, G_ASCII_DTOSTR_BUF_SIZE, "%f",
|
|
seg->middle);
|
|
g_ascii_formatd (color_buf[0], G_ASCII_DTOSTR_BUF_SIZE, "%f",
|
|
(seg->left_color.r + seg->right_color.r) / 2.0);
|
|
g_ascii_formatd (color_buf[1], G_ASCII_DTOSTR_BUF_SIZE, "%f",
|
|
(seg->left_color.g + seg->right_color.g) / 2.0);
|
|
g_ascii_formatd (color_buf[2], G_ASCII_DTOSTR_BUF_SIZE, "%f",
|
|
(seg->left_color.b + seg->right_color.b) / 2.0);
|
|
g_ascii_formatd (color_buf[3], G_ASCII_DTOSTR_BUF_SIZE, "%f",
|
|
1.0 - (seg->left_color.a + seg->right_color.a) / 2.0);
|
|
|
|
fprintf (file, "\t[%s color rgbt <%s, %s, %s, %s>]\n",
|
|
buf,
|
|
color_buf[0], color_buf[1], color_buf[2], color_buf[3]);
|
|
|
|
/* Right */
|
|
g_ascii_formatd (buf, G_ASCII_DTOSTR_BUF_SIZE, "%f",
|
|
seg->right);
|
|
g_ascii_formatd (color_buf[0], G_ASCII_DTOSTR_BUF_SIZE, "%f",
|
|
seg->right_color.r);
|
|
g_ascii_formatd (color_buf[1], G_ASCII_DTOSTR_BUF_SIZE, "%f",
|
|
seg->right_color.g);
|
|
g_ascii_formatd (color_buf[2], G_ASCII_DTOSTR_BUF_SIZE, "%f",
|
|
seg->right_color.b);
|
|
g_ascii_formatd (color_buf[3], G_ASCII_DTOSTR_BUF_SIZE, "%f",
|
|
1.0 - seg->right_color.a);
|
|
|
|
fprintf (file, "\t[%s color rgbt <%s, %s, %s, %s>]\n",
|
|
buf,
|
|
color_buf[0], color_buf[1], color_buf[2], color_buf[3]);
|
|
}
|
|
|
|
fprintf (file, "} /* color_map */\n");
|
|
fclose (file);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
gimp_gradient_get_color_at (GimpGradient *gradient,
|
|
gdouble pos,
|
|
gboolean reverse,
|
|
GimpRGB *color)
|
|
{
|
|
gdouble factor = 0.0;
|
|
GimpGradientSegment *seg;
|
|
gdouble seg_len;
|
|
gdouble middle;
|
|
GimpRGB rgb;
|
|
|
|
g_return_if_fail (GIMP_IS_GRADIENT (gradient));
|
|
g_return_if_fail (color != NULL);
|
|
|
|
pos = CLAMP (pos, 0.0, 1.0);
|
|
|
|
if (reverse)
|
|
pos = 1.0 - pos;
|
|
|
|
seg = gimp_gradient_get_segment_at (gradient, pos);
|
|
|
|
seg_len = seg->right - seg->left;
|
|
|
|
if (seg_len < EPSILON)
|
|
{
|
|
middle = 0.5;
|
|
pos = 0.5;
|
|
}
|
|
else
|
|
{
|
|
middle = (seg->middle - seg->left) / seg_len;
|
|
pos = (pos - seg->left) / seg_len;
|
|
}
|
|
|
|
switch (seg->type)
|
|
{
|
|
case GIMP_GRAD_LINEAR:
|
|
factor = gimp_gradient_calc_linear_factor (middle, pos);
|
|
break;
|
|
|
|
case GIMP_GRAD_CURVED:
|
|
factor = gimp_gradient_calc_curved_factor (middle, pos);
|
|
break;
|
|
|
|
case GIMP_GRAD_SINE:
|
|
factor = gimp_gradient_calc_sine_factor (middle, pos);
|
|
break;
|
|
|
|
case GIMP_GRAD_SPHERE_INCREASING:
|
|
factor = gimp_gradient_calc_sphere_increasing_factor (middle, pos);
|
|
break;
|
|
|
|
case GIMP_GRAD_SPHERE_DECREASING:
|
|
factor = gimp_gradient_calc_sphere_decreasing_factor (middle, pos);
|
|
break;
|
|
|
|
default:
|
|
g_warning ("%s: Unknown gradient type %d.",
|
|
G_GNUC_PRETTY_FUNCTION, seg->type);
|
|
break;
|
|
}
|
|
|
|
/* Calculate color components */
|
|
|
|
if (seg->color == GIMP_GRAD_RGB)
|
|
{
|
|
rgb.r =
|
|
seg->left_color.r + (seg->right_color.r - seg->left_color.r) * factor;
|
|
|
|
rgb.g =
|
|
seg->left_color.g + (seg->right_color.g - seg->left_color.g) * factor;
|
|
|
|
rgb.b =
|
|
seg->left_color.b + (seg->right_color.b - seg->left_color.b) * factor;
|
|
}
|
|
else
|
|
{
|
|
GimpHSV left_hsv;
|
|
GimpHSV right_hsv;
|
|
|
|
gimp_rgb_to_hsv (&seg->left_color, &left_hsv);
|
|
gimp_rgb_to_hsv (&seg->right_color, &right_hsv);
|
|
|
|
left_hsv.s = left_hsv.s + (right_hsv.s - left_hsv.s) * factor;
|
|
left_hsv.v = left_hsv.v + (right_hsv.v - left_hsv.v) * factor;
|
|
|
|
switch (seg->color)
|
|
{
|
|
case GIMP_GRAD_HSV_CCW:
|
|
if (left_hsv.h < right_hsv.h)
|
|
{
|
|
left_hsv.h += (right_hsv.h - left_hsv.h) * factor;
|
|
}
|
|
else
|
|
{
|
|
left_hsv.h += (1.0 - (left_hsv.h - right_hsv.h)) * factor;
|
|
|
|
if (left_hsv.h > 1.0)
|
|
left_hsv.h -= 1.0;
|
|
}
|
|
break;
|
|
|
|
case GIMP_GRAD_HSV_CW:
|
|
if (right_hsv.h < left_hsv.h)
|
|
{
|
|
left_hsv.h -= (left_hsv.h - right_hsv.h) * factor;
|
|
}
|
|
else
|
|
{
|
|
left_hsv.h -= (1.0 - (right_hsv.h - left_hsv.h)) * factor;
|
|
|
|
if (left_hsv.h < 0.0)
|
|
left_hsv.h += 1.0;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
g_warning ("%s(): Unknown coloring mode %d",
|
|
G_GNUC_PRETTY_FUNCTION, (gint) seg->color);
|
|
break;
|
|
}
|
|
|
|
gimp_hsv_to_rgb (&left_hsv, &rgb);
|
|
}
|
|
|
|
/* Calculate alpha */
|
|
|
|
rgb.a =
|
|
seg->left_color.a + (seg->right_color.a - seg->left_color.a) * factor;
|
|
|
|
*color = rgb;
|
|
}
|
|
|
|
GimpGradientSegment *
|
|
gimp_gradient_get_segment_at (GimpGradient *gradient,
|
|
gdouble pos)
|
|
{
|
|
GimpGradientSegment *seg;
|
|
|
|
g_return_val_if_fail (GIMP_IS_GRADIENT (gradient), NULL);
|
|
|
|
/* handle FP imprecision at the edges of the gradient */
|
|
pos = CLAMP (pos, 0.0, 1.0);
|
|
|
|
if (gradient->last_visited)
|
|
seg = gradient->last_visited;
|
|
else
|
|
seg = gradient->segments;
|
|
|
|
while (seg)
|
|
{
|
|
if (pos >= seg->left)
|
|
{
|
|
if (pos <= seg->right)
|
|
{
|
|
gradient->last_visited = seg; /* for speed */
|
|
return seg;
|
|
}
|
|
else
|
|
{
|
|
seg = seg->next;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
seg = seg->prev;
|
|
}
|
|
}
|
|
|
|
/* Oops: we should have found a segment, but we didn't */
|
|
g_warning ("%s(): no matching segment for position %0.15f",
|
|
G_GNUC_PRETTY_FUNCTION, pos);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static gdouble
|
|
gimp_gradient_calc_linear_factor (gdouble middle,
|
|
gdouble pos)
|
|
{
|
|
if (pos <= middle)
|
|
{
|
|
if (middle < EPSILON)
|
|
return 0.0;
|
|
else
|
|
return 0.5 * pos / middle;
|
|
}
|
|
else
|
|
{
|
|
pos -= middle;
|
|
middle = 1.0 - middle;
|
|
|
|
if (middle < EPSILON)
|
|
return 1.0;
|
|
else
|
|
return 0.5 + 0.5 * pos / middle;
|
|
}
|
|
}
|
|
|
|
static gdouble
|
|
gimp_gradient_calc_curved_factor (gdouble middle,
|
|
gdouble pos)
|
|
{
|
|
if (middle < EPSILON)
|
|
middle = EPSILON;
|
|
|
|
return pow(pos, log (0.5) / log (middle));
|
|
}
|
|
|
|
static gdouble
|
|
gimp_gradient_calc_sine_factor (gdouble middle,
|
|
gdouble pos)
|
|
{
|
|
pos = gimp_gradient_calc_linear_factor (middle, pos);
|
|
|
|
return (sin ((-G_PI / 2.0) + G_PI * pos) + 1.0) / 2.0;
|
|
}
|
|
|
|
static gdouble
|
|
gimp_gradient_calc_sphere_increasing_factor (gdouble middle,
|
|
gdouble pos)
|
|
{
|
|
pos = gimp_gradient_calc_linear_factor (middle, pos) - 1.0;
|
|
|
|
return sqrt (1.0 - pos * pos); /* Works for convex increasing and concave decreasing */
|
|
}
|
|
|
|
static gdouble
|
|
gimp_gradient_calc_sphere_decreasing_factor (gdouble middle,
|
|
gdouble pos)
|
|
{
|
|
pos = gimp_gradient_calc_linear_factor (middle, pos);
|
|
|
|
return 1.0 - sqrt(1.0 - pos * pos); /* Works for convex decreasing and concave increasing */
|
|
}
|
|
|
|
|
|
/* gradient segment functions */
|
|
|
|
GimpGradientSegment *
|
|
gimp_gradient_segment_new (void)
|
|
{
|
|
GimpGradientSegment *seg;
|
|
|
|
seg = g_new (GimpGradientSegment, 1);
|
|
|
|
seg->left = 0.0;
|
|
seg->middle = 0.5;
|
|
seg->right = 1.0;
|
|
|
|
gimp_rgba_set (&seg->left_color, 0.0, 0.0, 0.0, 1.0);
|
|
gimp_rgba_set (&seg->right_color, 1.0, 1.0, 1.0, 1.0);
|
|
|
|
seg->type = GIMP_GRAD_LINEAR;
|
|
seg->color = GIMP_GRAD_RGB;
|
|
|
|
seg->prev = seg->next = NULL;
|
|
|
|
return seg;
|
|
}
|
|
|
|
|
|
void
|
|
gimp_gradient_segment_free (GimpGradientSegment *seg)
|
|
{
|
|
g_return_if_fail (seg != NULL);
|
|
|
|
g_free (seg);
|
|
}
|
|
|
|
void
|
|
gimp_gradient_segments_free (GimpGradientSegment *seg)
|
|
{
|
|
GimpGradientSegment *tmp;
|
|
|
|
g_return_if_fail (seg != NULL);
|
|
|
|
while (seg)
|
|
{
|
|
tmp = seg->next;
|
|
gimp_gradient_segment_free (seg);
|
|
seg = tmp;
|
|
}
|
|
}
|
|
|
|
GimpGradientSegment *
|
|
gimp_gradient_segment_get_last (GimpGradientSegment *seg)
|
|
{
|
|
if (!seg)
|
|
return NULL;
|
|
|
|
while (seg->next)
|
|
seg = seg->next;
|
|
|
|
return seg;
|
|
}
|
|
|
|
GimpGradientSegment *
|
|
gimp_gradient_segment_get_first (GimpGradientSegment *seg)
|
|
{
|
|
if (!seg)
|
|
return NULL;
|
|
|
|
while (seg->prev)
|
|
seg = seg->prev;
|
|
|
|
return seg;
|
|
}
|
|
|
|
void
|
|
gimp_gradient_segment_split_midpoint (GimpGradient *gradient,
|
|
GimpGradientSegment *lseg,
|
|
GimpGradientSegment **newl,
|
|
GimpGradientSegment **newr)
|
|
{
|
|
GimpRGB color;
|
|
GimpGradientSegment *newseg;
|
|
|
|
g_return_if_fail (GIMP_IS_GRADIENT (gradient));
|
|
g_return_if_fail (lseg != NULL);
|
|
g_return_if_fail (newl != NULL);
|
|
g_return_if_fail (newr != NULL);
|
|
|
|
/* Get color at original segment's midpoint */
|
|
gimp_gradient_get_color_at (gradient, lseg->middle, FALSE, &color);
|
|
|
|
/* Create a new segment and insert it in the list */
|
|
|
|
newseg = gimp_gradient_segment_new ();
|
|
|
|
newseg->prev = lseg;
|
|
newseg->next = lseg->next;
|
|
|
|
lseg->next = newseg;
|
|
|
|
if (newseg->next)
|
|
newseg->next->prev = newseg;
|
|
|
|
/* Set coordinates of new segment */
|
|
|
|
newseg->left = lseg->middle;
|
|
newseg->right = lseg->right;
|
|
newseg->middle = (newseg->left + newseg->right) / 2.0;
|
|
|
|
/* Set coordinates of original segment */
|
|
|
|
lseg->right = newseg->left;
|
|
lseg->middle = (lseg->left + lseg->right) / 2.0;
|
|
|
|
/* Set colors of both segments */
|
|
|
|
newseg->right_color = lseg->right_color;
|
|
|
|
lseg->right_color.r = newseg->left_color.r = color.r;
|
|
lseg->right_color.g = newseg->left_color.g = color.g;
|
|
lseg->right_color.b = newseg->left_color.b = color.b;
|
|
lseg->right_color.a = newseg->left_color.a = color.a;
|
|
|
|
/* Set parameters of new segment */
|
|
|
|
newseg->type = lseg->type;
|
|
newseg->color = lseg->color;
|
|
|
|
/* Done */
|
|
|
|
*newl = lseg;
|
|
*newr = newseg;
|
|
}
|
|
|
|
void
|
|
gimp_gradient_segment_split_uniform (GimpGradient *gradient,
|
|
GimpGradientSegment *lseg,
|
|
gint parts,
|
|
GimpGradientSegment **newl,
|
|
GimpGradientSegment **newr)
|
|
{
|
|
GimpGradientSegment *seg, *prev, *tmp;
|
|
gdouble seg_len;
|
|
gint i;
|
|
|
|
g_return_if_fail (GIMP_IS_GRADIENT (gradient));
|
|
g_return_if_fail (lseg != NULL);
|
|
g_return_if_fail (newl != NULL);
|
|
g_return_if_fail (newr != NULL);
|
|
|
|
seg_len = (lseg->right - lseg->left) / parts; /* Length of divisions */
|
|
|
|
seg = NULL;
|
|
prev = NULL;
|
|
tmp = NULL;
|
|
|
|
for (i = 0; i < parts; i++)
|
|
{
|
|
seg = gimp_gradient_segment_new ();
|
|
|
|
if (i == 0)
|
|
tmp = seg; /* Remember first segment */
|
|
|
|
seg->left = lseg->left + i * seg_len;
|
|
seg->right = lseg->left + (i + 1) * seg_len;
|
|
seg->middle = (seg->left + seg->right) / 2.0;
|
|
|
|
gimp_gradient_get_color_at (gradient, seg->left, FALSE, &seg->left_color);
|
|
gimp_gradient_get_color_at (gradient, seg->right, FALSE, &seg->right_color);
|
|
|
|
seg->type = lseg->type;
|
|
seg->color = lseg->color;
|
|
|
|
seg->prev = prev;
|
|
seg->next = NULL;
|
|
|
|
if (prev)
|
|
prev->next = seg;
|
|
|
|
prev = seg;
|
|
}
|
|
|
|
/* Fix edges */
|
|
|
|
tmp->left_color = lseg->left_color;
|
|
|
|
seg->right_color = lseg->right_color;
|
|
|
|
tmp->left = lseg->left;
|
|
seg->right = lseg->right; /* To squish accumulative error */
|
|
|
|
/* Link in list */
|
|
|
|
tmp->prev = lseg->prev;
|
|
seg->next = lseg->next;
|
|
|
|
if (lseg->prev)
|
|
lseg->prev->next = tmp;
|
|
else
|
|
gradient->segments = tmp; /* We are on leftmost segment */
|
|
|
|
if (lseg->next)
|
|
lseg->next->prev = seg;
|
|
|
|
gradient->last_visited = NULL; /* Force re-search */
|
|
|
|
/* Done */
|
|
|
|
*newl = tmp;
|
|
*newr = seg;
|
|
|
|
/* Delete old segment */
|
|
|
|
gimp_gradient_segment_free (lseg);
|
|
}
|
|
|
|
void
|
|
gimp_gradient_segments_compress_range (GimpGradientSegment *range_l,
|
|
GimpGradientSegment *range_r,
|
|
gdouble new_l,
|
|
gdouble new_r)
|
|
{
|
|
gdouble orig_l, orig_r;
|
|
gdouble scale;
|
|
GimpGradientSegment *seg, *aseg;
|
|
|
|
g_return_if_fail (range_l != NULL);
|
|
g_return_if_fail (range_r != NULL);
|
|
|
|
orig_l = range_l->left;
|
|
orig_r = range_r->right;
|
|
|
|
scale = (new_r - new_l) / (orig_r - orig_l);
|
|
|
|
seg = range_l;
|
|
|
|
do
|
|
{
|
|
seg->left = new_l + (seg->left - orig_l) * scale;
|
|
seg->middle = new_l + (seg->middle - orig_l) * scale;
|
|
seg->right = new_l + (seg->right - orig_l) * scale;
|
|
|
|
/* Next */
|
|
|
|
aseg = seg;
|
|
seg = seg->next;
|
|
}
|
|
while (aseg != range_r);
|
|
}
|
|
|
|
void
|
|
gimp_gradient_segments_blend_endpoints (GimpGradientSegment *lseg,
|
|
GimpGradientSegment *rseg,
|
|
GimpRGB *rgb1,
|
|
GimpRGB *rgb2,
|
|
gboolean blend_colors,
|
|
gboolean blend_opacity)
|
|
{
|
|
GimpRGB d;
|
|
gdouble left, len;
|
|
GimpGradientSegment *seg;
|
|
GimpGradientSegment *aseg;
|
|
|
|
g_return_if_fail (lseg != NULL);
|
|
g_return_if_fail (rseg != NULL);
|
|
|
|
d.r = rgb2->r - rgb1->r;
|
|
d.g = rgb2->g - rgb1->g;
|
|
d.b = rgb2->b - rgb1->b;
|
|
d.a = rgb2->a - rgb1->a;
|
|
|
|
left = lseg->left;
|
|
len = rseg->right - left;
|
|
|
|
seg = lseg;
|
|
|
|
do
|
|
{
|
|
if (blend_colors)
|
|
{
|
|
seg->left_color.r = rgb1->r + (seg->left - left) / len * d.r;
|
|
seg->left_color.g = rgb1->g + (seg->left - left) / len * d.g;
|
|
seg->left_color.b = rgb1->b + (seg->left - left) / len * d.b;
|
|
|
|
seg->right_color.r = rgb1->r + (seg->right - left) / len * d.r;
|
|
seg->right_color.g = rgb1->g + (seg->right - left) / len * d.g;
|
|
seg->right_color.b = rgb1->b + (seg->right - left) / len * d.b;
|
|
}
|
|
|
|
if (blend_opacity)
|
|
{
|
|
seg->left_color.a = rgb1->a + (seg->left - left) / len * d.a;
|
|
seg->right_color.a = rgb1->a + (seg->right - left) / len * d.a;
|
|
}
|
|
|
|
aseg = seg;
|
|
seg = seg->next;
|
|
}
|
|
while (aseg != rseg);
|
|
}
|