1 /* GIMP - The GNU Image Manipulation Program
2 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3 *
4 * Gradient Map plug-in
5 * Copyright (C) 1997 Eiichi Takamori <taka@ma1.seikyou.ne.jp>
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
21 #include "config.h"
22 #include <string.h>
23
24 #include <libgimp/gimp.h>
25
26 #include "libgimp/stdplugins-intl.h"
27
28
29 /* Some useful macros */
30 #define GRADMAP_PROC "plug-in-gradmap"
31 #define PALETTEMAP_PROC "plug-in-palettemap"
32 #define PLUG_IN_BINARY "gradient-map"
33 #define PLUG_IN_ROLE "gimp-gradient-map"
34 #define NSAMPLES 2048
35
36 typedef enum
37 {
38 GRADIENT_MODE = 1,
39 PALETTE_MODE
40 } MapMode;
41
42 static void query (void);
43 static void run (const gchar *name,
44 gint nparams,
45 const GimpParam *param,
46 gint *nreturn_vals,
47 GimpParam **return_vals);
48 static void map (GeglBuffer *buffer,
49 GeglBuffer *shadow_buffer,
50 gint32 drawable_id,
51 MapMode mode);
52 static gdouble * get_samples_gradient (gint32 drawable_id);
53 static gdouble * get_samples_palette (gint32 drawable_id);
54
55
56 const GimpPlugInInfo PLUG_IN_INFO =
57 {
58 NULL, /* init_proc */
59 NULL, /* quit_proc */
60 query, /* query_proc */
61 run, /* run_proc */
62 };
63
MAIN()64 MAIN ()
65
66 static void
67 query (void)
68 {
69 static const GimpParamDef args[]=
70 {
71 { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
72 { GIMP_PDB_IMAGE, "image", "Input image (unused)" },
73 { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" }
74 };
75
76 gimp_install_procedure (GRADMAP_PROC,
77 N_("Recolor the image using colors from the active gradient"),
78 "This plug-in maps the contents of the specified "
79 "drawable with active gradient. It calculates "
80 "luminosity of each pixel and replaces the pixel "
81 "by the sample of active gradient at the position "
82 "proportional to that luminosity. Complete black "
83 "pixel becomes the leftmost color of the gradient, "
84 "and complete white becomes the rightmost. Works on "
85 "both Grayscale and RGB image with/without alpha "
86 "channel.",
87 "Eiichi Takamori",
88 "Eiichi Takamori",
89 "1997",
90 N_("_Gradient Map"),
91 "RGB*, GRAY*",
92 GIMP_PLUGIN,
93 G_N_ELEMENTS (args), 0,
94 args, NULL);
95
96 gimp_plugin_menu_register (GRADMAP_PROC, "<Image>/Colors/Map");
97
98 gimp_install_procedure (PALETTEMAP_PROC,
99 N_("Recolor the image using colors from the active palette"),
100 "This plug-in maps the contents of the specified "
101 "drawable with the active palette. It calculates "
102 "luminosity of each pixel and replaces the pixel "
103 "by the palette sample at the corresponding "
104 "index. A complete black "
105 "pixel becomes the lowest palette entry, "
106 "and complete white becomes the highest. Works on "
107 "both Grayscale and RGB image with/without alpha "
108 "channel.",
109 "Bill Skaggs",
110 "Bill Skaggs",
111 "2004",
112 N_("_Palette Map"),
113 "RGB*, GRAY*",
114 GIMP_PLUGIN,
115 G_N_ELEMENTS (args), 0,
116 args, NULL);
117
118 gimp_plugin_menu_register (PALETTEMAP_PROC, "<Image>/Colors/Map");
119 }
120
121 static void
run(const gchar * name,gint nparams,const GimpParam * param,gint * nreturn_vals,GimpParam ** return_vals)122 run (const gchar *name,
123 gint nparams,
124 const GimpParam *param,
125 gint *nreturn_vals,
126 GimpParam **return_vals)
127 {
128 static GimpParam values[1];
129 GimpPDBStatusType status = GIMP_PDB_SUCCESS;
130 GimpRunMode run_mode;
131 gint32 drawable_id;
132 GeglBuffer *shadow_buffer;
133 GeglBuffer *buffer;
134
135 run_mode = param[0].data.d_int32;
136 drawable_id = param[2].data.d_drawable;
137
138 INIT_I18N ();
139 gegl_init (NULL, NULL);
140
141 *nreturn_vals = 1;
142 *return_vals = values;
143
144 values[0].type = GIMP_PDB_STATUS;
145 values[0].data.d_status = status;
146
147 /* Get the specified drawable */
148 shadow_buffer = gimp_drawable_get_shadow_buffer (drawable_id);
149 buffer = gimp_drawable_get_buffer (drawable_id);
150
151 /* Make sure that the drawable is gray or RGB color */
152 if (gimp_drawable_is_rgb (drawable_id) ||
153 gimp_drawable_is_gray (drawable_id))
154 {
155 MapMode mode = 0;
156
157 if ( !strcmp (name, GRADMAP_PROC))
158 {
159 mode = GRADIENT_MODE;
160 gimp_progress_init (_("Gradient Map"));
161 }
162 else if ( !strcmp (name, PALETTEMAP_PROC))
163 {
164 mode = PALETTE_MODE;
165 gimp_progress_init (_("Palette Map"));
166 }
167 else
168 {
169 status = GIMP_PDB_CALLING_ERROR;
170 }
171
172 if (status == GIMP_PDB_SUCCESS)
173 {
174 if (mode)
175 map (buffer, shadow_buffer, drawable_id, mode);
176 }
177 }
178 else
179 {
180 status = GIMP_PDB_EXECUTION_ERROR;
181 }
182
183 g_object_unref (buffer);
184 g_object_unref (shadow_buffer);
185
186 gimp_drawable_merge_shadow (drawable_id, TRUE);
187
188 gimp_drawable_update (drawable_id, 0, 0,
189 gimp_drawable_width (drawable_id),
190 gimp_drawable_height (drawable_id));
191
192 values[0].data.d_status = status;
193
194 if (run_mode != GIMP_RUN_NONINTERACTIVE)
195 gimp_displays_flush ();
196 }
197
198 static void
map(GeglBuffer * buffer,GeglBuffer * shadow_buffer,gint32 drawable_id,MapMode mode)199 map (GeglBuffer *buffer,
200 GeglBuffer *shadow_buffer,
201 gint32 drawable_id,
202 MapMode mode)
203 {
204 GeglBufferIterator *gi;
205 gint nb_color_chan;
206 gint nb_chan;
207 gint nb_chan2;
208 gint nb_chan_samp;
209 gint index_iter;
210 gboolean interpolate;
211 gdouble *samples;
212 gboolean is_rgb;
213 gboolean has_alpha;
214 const Babl *format_shadow;
215 const Babl *format_buffer;
216
217 is_rgb = gimp_drawable_is_rgb (drawable_id);
218 has_alpha = gimp_drawable_has_alpha (drawable_id);
219
220 switch (mode)
221 {
222 case GRADIENT_MODE:
223 samples = get_samples_gradient (drawable_id);
224 interpolate = TRUE;
225 break;
226 case PALETTE_MODE:
227 samples = get_samples_palette (drawable_id);
228 interpolate = FALSE;
229 break;
230 default:
231 g_error ("plug_in_gradmap: invalid mode");
232 }
233
234 if (is_rgb)
235 {
236 nb_color_chan = 3;
237 nb_chan_samp = 4;
238 if (has_alpha)
239 format_shadow = babl_format ("R'G'B'A float");
240 else
241 format_shadow = babl_format ("R'G'B' float");
242 }
243 else
244 {
245 nb_color_chan = 1;
246 nb_chan_samp = 2;
247 if (has_alpha)
248 format_shadow = babl_format ("Y'A float");
249 else
250 format_shadow = babl_format ("Y' float");
251 }
252
253
254 if (has_alpha)
255 {
256 nb_chan = nb_color_chan + 1;
257 nb_chan2 = 2;
258 format_buffer = babl_format ("Y'A float");
259 }
260 else
261 {
262 nb_chan = nb_color_chan;
263 nb_chan2 = 1;
264 format_buffer = babl_format ("Y' float");
265 }
266
267 gi = gegl_buffer_iterator_new (shadow_buffer, NULL, 0, format_shadow,
268 GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE, 2);
269
270 index_iter = gegl_buffer_iterator_add (gi, buffer, NULL,
271 0, format_buffer,
272 GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
273
274 while (gegl_buffer_iterator_next (gi))
275 {
276 guint k;
277 gfloat *data;
278 gfloat *data2;
279
280 data = (gfloat*) gi->items[0].data;
281 data2 = (gfloat*) gi->items[index_iter].data;
282
283 if (interpolate)
284 {
285 for (k = 0; k < gi->length; k++)
286 {
287 gint b, ind1, ind2;
288 gdouble *samp1, *samp2;
289 gfloat c1, c2, val;
290
291 val = data2[0] * (NSAMPLES-1);
292
293 ind1 = CLAMP (floor (val), 0, NSAMPLES-1);
294 ind2 = CLAMP (ceil (val), 0, NSAMPLES-1);
295
296 c1 = 1.0 - (val - ind1);
297 c2 = 1.0 - c1;
298
299 samp1 = &(samples[ind1 * nb_chan_samp]);
300 samp2 = &(samples[ind2 * nb_chan_samp]);
301
302 for (b = 0; b < nb_color_chan; b++)
303 data[b] = (samp1[b] * c1 + samp2[b] * c2);
304
305 if (has_alpha)
306 {
307 float alpha = (samp1[b] * c1 + samp2[b] * c2);
308 data[b] = alpha * data2[1];
309 }
310
311 data += nb_chan;
312 data2 += nb_chan2;
313 }
314 }
315 else
316 {
317 for (k = 0; k < gi->length; k++)
318 {
319 gint b, ind;
320 gdouble *samp;
321 ind = CLAMP (data2[0] * (NSAMPLES-1), 0, NSAMPLES-1);
322
323 samp = &(samples[ind * nb_chan_samp]);
324
325 for (b = 0; b < nb_color_chan; b++)
326 data[b] = samp[b];
327
328 if (has_alpha)
329 {
330 data[b] = samp[b] * data2[1];
331 }
332
333 data += nb_chan;
334 data2 += nb_chan2;
335 }
336 }
337 }
338
339 g_free (samples);
340 }
341
342 /*
343 Returns 2048 samples of the gradient.
344 Each sample is (R'G'B'A float) or (Y'A float), depending on the drawable
345 */
346 static gdouble *
get_samples_gradient(gint32 drawable_id)347 get_samples_gradient (gint32 drawable_id)
348 {
349 gchar *gradient_name;
350 gint n_d_samples;
351 gdouble *d_samples = NULL;
352
353 gradient_name = gimp_context_get_gradient ();
354
355 /* FIXME: "reverse" hardcoded to FALSE. */
356 gimp_gradient_get_uniform_samples (gradient_name, NSAMPLES, FALSE,
357 &n_d_samples, &d_samples);
358 g_free (gradient_name);
359
360 if (!gimp_drawable_is_rgb (drawable_id))
361 {
362 const Babl *format_src = babl_format ("R'G'B'A double");
363 const Babl *format_dst = babl_format ("Y'A double");
364 const Babl *fish = babl_fish (format_src, format_dst);
365 babl_process (fish, d_samples, d_samples, NSAMPLES);
366 }
367
368 return d_samples;
369 }
370
371 /*
372 Returns 2048 samples of the palette.
373 Each sample is (R'G'B'A float) or (Y'A float), depending on the drawable
374 */
375 static gdouble *
get_samples_palette(gint32 drawable_id)376 get_samples_palette (gint32 drawable_id)
377 {
378 gchar *palette_name;
379 GimpRGB color_sample;
380 gdouble *d_samples, *d_samp;
381 gboolean is_rgb;
382 gdouble factor;
383 gint pal_entry, num_colors;
384 gint nb_color_chan, nb_chan, i;
385 const Babl *format;
386
387 palette_name = gimp_context_get_palette ();
388 gimp_palette_get_info (palette_name, &num_colors);
389
390 is_rgb = gimp_drawable_is_rgb (drawable_id);
391
392 factor = ((double) num_colors) / NSAMPLES;
393 format = is_rgb ? babl_format ("R'G'B'A double") : babl_format ("Y'A double");
394 nb_color_chan = is_rgb ? 3 : 1;
395 nb_chan = nb_color_chan + 1;
396
397 d_samples = g_new (gdouble, NSAMPLES * nb_chan);
398
399 for (i = 0; i < NSAMPLES; i++)
400 {
401 d_samp = &d_samples[i * nb_chan];
402 pal_entry = CLAMP ((int)(i * factor), 0, num_colors - 1);
403
404 gimp_palette_entry_get_color (palette_name, pal_entry, &color_sample);
405 gimp_rgb_get_pixel (&color_sample,
406 format,
407 d_samp);
408 }
409
410 g_free (palette_name);
411 return d_samples;
412 }
413