1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3  *
4  * Colorify. Changes the pixel's luminosity to a specified color
5  * Copyright (C) 1997 Francisco Bustamante
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 
23 #include <libgimp/gimp.h>
24 #include <libgimp/gimpui.h>
25 
26 #include "libgimp/stdplugins-intl.h"
27 
28 
29 #define PLUG_IN_PROC    "plug-in-colorify"
30 #define PLUG_IN_BINARY  "colorify"
31 #define PLUG_IN_ROLE    "gimp-colorify"
32 #define PLUG_IN_VERSION "1.1"
33 
34 #define COLOR_SIZE 30
35 
36 static void      query (void);
37 static void      run   (const gchar      *name,
38                         gint              nparams,
39                         const GimpParam  *param,
40                         gint             *nreturn_vals,
41                         GimpParam       **return_vals);
42 
43 static void      colorify                  (GimpDrawable *drawable,
44                                             GimpPreview  *preview);
45 static gboolean  colorify_dialog           (GimpDrawable *drawable);
46 static void      predefined_color_callback (GtkWidget    *widget,
47                                             gpointer      data);
48 
49 typedef struct
50 {
51   GimpRGB  color;
52 } ColorifyVals;
53 
54 static ColorifyVals cvals =
55 {
56   { 1.0, 1.0, 1.0, 1.0 }
57 };
58 
59 static GimpRGB button_color[] =
60 {
61   { 1.0, 0.0, 0.0, 1.0 },
62   { 1.0, 1.0, 0.0, 1.0 },
63   { 0.0, 1.0, 0.0, 1.0 },
64   { 0.0, 1.0, 1.0, 1.0 },
65   { 0.0, 0.0, 1.0, 1.0 },
66   { 1.0, 0.0, 1.0, 1.0 },
67   { 1.0, 1.0, 1.0, 1.0 },
68 };
69 
70 const GimpPlugInInfo PLUG_IN_INFO =
71 {
72   NULL,
73   NULL,
74   query,
75   run,
76 };
77 
78 static GtkWidget *custom_color_button = NULL;
79 
80 static gint lum_red_lookup[256];
81 static gint lum_green_lookup[256];
82 static gint lum_blue_lookup[256];
83 static gint final_red_lookup[256];
84 static gint final_green_lookup[256];
85 static gint final_blue_lookup[256];
86 
87 
MAIN()88 MAIN ()
89 
90 static void
91 query (void)
92 {
93   static const GimpParamDef args[] =
94   {
95     { GIMP_PDB_INT32,    "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
96     { GIMP_PDB_IMAGE,    "image",    "Input image"    },
97     { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
98     { GIMP_PDB_COLOR,    "color",    "Color to apply" }
99   };
100 
101   gimp_install_procedure (PLUG_IN_PROC,
102                           N_("Replace all colors with shades of a specified color"),
103                           "Makes an average of the RGB channels and uses it "
104                           "to set the color",
105                           "Francisco Bustamante",
106                           "Francisco Bustamante",
107                           PLUG_IN_VERSION,
108                           N_("Colorif_y..."),
109                           "RGB*",
110                           GIMP_PLUGIN,
111                           G_N_ELEMENTS (args), 0,
112                           args, NULL);
113 }
114 
115 static void
run(const gchar * name,gint nparams,const GimpParam * param,gint * nreturn_vals,GimpParam ** return_vals)116 run (const gchar      *name,
117      gint              nparams,
118      const GimpParam  *param,
119      gint             *nreturn_vals,
120      GimpParam       **return_vals)
121 {
122   GimpPDBStatusType  status;
123   static GimpParam   values[1];
124   GimpDrawable      *drawable;
125   GimpRunMode        run_mode;
126 
127   INIT_I18N ();
128 
129   status = GIMP_PDB_SUCCESS;
130   run_mode = param[0].data.d_int32;
131 
132   values[0].type = GIMP_PDB_STATUS;
133   values[0].data.d_status = status;
134 
135   *nreturn_vals = 1;
136   *return_vals = values;
137 
138   drawable = gimp_drawable_get (param[2].data.d_drawable);
139 
140   switch (run_mode)
141     {
142     case GIMP_RUN_INTERACTIVE:
143       gimp_get_data (PLUG_IN_PROC, &cvals);
144       if (!colorify_dialog (drawable))
145         return;
146       break;
147 
148     case GIMP_RUN_NONINTERACTIVE:
149       if (nparams != 4)
150         status = GIMP_PDB_CALLING_ERROR;
151 
152       if (status == GIMP_PDB_SUCCESS)
153         cvals.color = param[3].data.d_color;
154       break;
155 
156     case GIMP_RUN_WITH_LAST_VALS:
157       /*  Possibly retrieve data  */
158       gimp_get_data (PLUG_IN_PROC, &cvals);
159       break;
160 
161     default:
162       break;
163     }
164 
165   if (status == GIMP_PDB_SUCCESS)
166     {
167       gimp_progress_init (_("Colorifying"));
168 
169       colorify (drawable, NULL);
170 
171       if (run_mode == GIMP_RUN_INTERACTIVE)
172         gimp_set_data (PLUG_IN_PROC, &cvals, sizeof (ColorifyVals));
173 
174       if (run_mode != GIMP_RUN_NONINTERACTIVE)
175         gimp_displays_flush ();
176     }
177 
178   gimp_drawable_detach (drawable);
179 
180   values[0].data.d_status = status;
181 }
182 
183 static void
colorify_func(const guchar * src,guchar * dest,gint bpp,gpointer data)184 colorify_func (const guchar *src,
185                guchar       *dest,
186                gint          bpp,
187                gpointer      data)
188 {
189   gint lum;
190 
191   lum = lum_red_lookup[src[0]]   +
192         lum_green_lookup[src[1]] +
193         lum_blue_lookup[src[2]];
194 
195   dest[0] = final_red_lookup[lum];
196   dest[1] = final_green_lookup[lum];
197   dest[2] = final_blue_lookup[lum];
198 
199   if (bpp == 4)
200     dest[3] = src[3];
201 }
202 
203 static void
colorify(GimpDrawable * drawable,GimpPreview * preview)204 colorify (GimpDrawable *drawable,
205           GimpPreview  *preview)
206 {
207   gint  i;
208 
209   for (i = 0; i < 256; i ++)
210     {
211       lum_red_lookup[i]     = i * GIMP_RGB_LUMINANCE_RED;
212       lum_green_lookup[i]   = i * GIMP_RGB_LUMINANCE_GREEN;
213       lum_blue_lookup[i]    = i * GIMP_RGB_LUMINANCE_BLUE;
214       final_red_lookup[i]   = i * cvals.color.r;
215       final_green_lookup[i] = i * cvals.color.g;
216       final_blue_lookup[i]  = i * cvals.color.b;
217     }
218 
219   if (preview)
220     {
221       gint    width, height, bytes;
222       guchar *src;
223 
224       src = gimp_zoom_preview_get_source (GIMP_ZOOM_PREVIEW (preview),
225                                           &width, &height, &bytes);
226       for (i = 0; i < width * height; i++)
227         colorify_func (src + i * bytes, src + i * bytes, bytes, NULL);
228 
229       gimp_preview_draw_buffer (preview, src, width * bytes);
230       g_free (src);
231     }
232   else
233     {
234       GimpPixelRgn  srcPR, destPR;
235       gint          x1, y1, x2, y2;
236       gpointer      pr;
237       gint          total_area;
238       gint          area_so_far;
239       gint          count;
240 
241       gimp_drawable_mask_bounds (drawable->drawable_id, &x1, &y1, &x2, &y2);
242 
243       total_area  = (x2 - x1) * (y2 - y1);
244       area_so_far = 0;
245 
246       if (total_area <= 0)
247         return;
248 
249       /* Initialize the pixel regions. */
250       gimp_pixel_rgn_init (&srcPR, drawable, x1, y1, (x2 - x1), (y2 - y1),
251                            FALSE, FALSE);
252       gimp_pixel_rgn_init (&destPR, drawable, x1, y1, (x2 - x1), (y2 - y1),
253                            TRUE, TRUE);
254 
255       for (pr = gimp_pixel_rgns_register (2, &srcPR, &destPR), count = 0;
256            pr != NULL;
257            pr = gimp_pixel_rgns_process (pr), count++)
258         {
259           const guchar *src  = srcPR.data;
260           guchar       *dest = destPR.data;
261           gint          row;
262 
263           for (row = 0; row < srcPR.h; row++)
264             {
265               const guchar *s      = src;
266               guchar       *d      = dest;
267               gint          pixels = srcPR.w;
268 
269               while (pixels--)
270                 {
271                   colorify_func (s, d, srcPR.bpp, NULL);
272 
273                   s += srcPR.bpp;
274                   d += destPR.bpp;
275                 }
276 
277               src  += srcPR.rowstride;
278               dest += destPR.rowstride;
279             }
280 
281           area_so_far += srcPR.w * srcPR.h;
282 
283           if ((count % 16) == 0)
284             gimp_progress_update ((gdouble) area_so_far / (gdouble) total_area);
285         }
286 
287       /*  update the processed region  */
288       gimp_drawable_flush (drawable);
289       gimp_drawable_merge_shadow (drawable->drawable_id, TRUE);
290       gimp_drawable_update (drawable->drawable_id, x1, y1, (x2 - x1), (y2 - y1));
291     }
292 }
293 
294 static gboolean
colorify_dialog(GimpDrawable * drawable)295 colorify_dialog (GimpDrawable *drawable)
296 {
297   GtkWidget *dialog;
298   GtkWidget *main_vbox;
299   GtkWidget *preview;
300   GtkWidget *label;
301   GtkWidget *button;
302   GtkWidget *table;
303   GtkWidget *color_area;
304   gint       i;
305   gboolean   run;
306 
307   gimp_ui_init (PLUG_IN_BINARY, TRUE);
308 
309   dialog = gimp_dialog_new (_("Colorify"), PLUG_IN_ROLE,
310                             NULL, 0,
311                             gimp_standard_help_func, PLUG_IN_PROC,
312 
313                             _("_Cancel"), GTK_RESPONSE_CANCEL,
314                             _("_OK"),     GTK_RESPONSE_OK,
315 
316                             NULL);
317 
318   gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
319                                            GTK_RESPONSE_OK,
320                                            GTK_RESPONSE_CANCEL,
321                                            -1);
322 
323   gimp_window_set_transient (GTK_WINDOW (dialog));
324 
325   main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
326   gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
327   gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
328                       main_vbox, TRUE, TRUE, 0);
329   gtk_widget_show (main_vbox);
330 
331   preview = gimp_zoom_preview_new_from_drawable_id (drawable->drawable_id);
332   gtk_box_pack_start (GTK_BOX (main_vbox), preview, TRUE, TRUE, 0);
333   gtk_widget_show (preview);
334   g_signal_connect_swapped (preview, "invalidated",
335                             G_CALLBACK (colorify),
336                             drawable);
337 
338   table = gtk_table_new (2, 7, TRUE);
339   gtk_table_set_row_spacings (GTK_TABLE (table), 6);
340   gtk_table_set_col_spacings (GTK_TABLE (table), 6);
341   gtk_box_pack_start (GTK_BOX (main_vbox), table, FALSE, FALSE, 0);
342   gtk_widget_show (table);
343 
344   label = gtk_label_new (_("Custom color:"));
345   gtk_table_attach (GTK_TABLE (table), label, 4, 6, 0, 1,
346                     GTK_FILL, GTK_FILL, 0, 0);
347   gtk_widget_show (label);
348 
349   custom_color_button = gimp_color_button_new (_("Colorify Custom Color"),
350                                                COLOR_SIZE, COLOR_SIZE,
351                                                &cvals.color,
352                                                GIMP_COLOR_AREA_FLAT);
353   g_signal_connect (custom_color_button, "color-changed",
354                     G_CALLBACK (gimp_color_button_get_color),
355                     &cvals.color);
356   g_signal_connect_swapped (custom_color_button, "color-changed",
357                             G_CALLBACK (gimp_preview_invalidate),
358                             preview);
359 
360   gtk_table_attach (GTK_TABLE (table), custom_color_button, 6, 7, 0, 1,
361                     GTK_FILL, GTK_FILL, 0, 0);
362   gtk_widget_show (custom_color_button);
363 
364   for (i = 0; i < 7; i++)
365     {
366       button = gtk_button_new ();
367       color_area = gimp_color_area_new (&button_color[i],
368                                         GIMP_COLOR_AREA_FLAT,
369                                         GDK_BUTTON2_MASK);
370       gtk_widget_set_size_request (GTK_WIDGET (color_area),
371                                    COLOR_SIZE, COLOR_SIZE);
372       gtk_container_add (GTK_CONTAINER (button), color_area);
373       g_signal_connect (button, "clicked",
374                         G_CALLBACK (predefined_color_callback),
375                         &button_color[i]);
376       gtk_widget_show (color_area);
377 
378       gtk_table_attach (GTK_TABLE (table), button, i, i + 1, 1, 2,
379                         GTK_FILL, GTK_FILL, 0, 0);
380       gtk_widget_show (button);
381     }
382 
383   gtk_widget_show (dialog);
384 
385   run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
386 
387   gtk_widget_destroy (dialog);
388 
389   return run;
390 }
391 
392 static void
predefined_color_callback(GtkWidget * widget,gpointer data)393 predefined_color_callback (GtkWidget *widget,
394                            gpointer   data)
395 {
396   gimp_color_button_set_color (GIMP_COLOR_BUTTON (custom_color_button),
397                                (GimpRGB *) data);
398 }
399