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