1 /**
2  * Copyright (C) 2012 hejian <hejian.he@gmail.com>
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 <http://www.gnu.org/licenses/>.
16  */
17 
18 #include <libgimp/gimp.h>
19 #include <libgimp/gimpui.h>
20 
21 #include "skin-whitening-effect.h"
22 #include "skin-whitening-images.h"
23 
24 #define PLUG_IN_PROC   "plug-in-skin-whitening"
25 #define PLUG_IN_BINARY "skin-whitening"
26 #define PLUG_IN_ROLE   "gimp-skin-whitening"
27 
28 #define PREVIEW_SIZE  480
29 #define THUMBNAIL_SIZE  80
30 
31 typedef struct
32 {
33   WhiteningEffectType effect;
34 } WhiteningValues;
35 
36 static const WhiteningEffectType effects[] =
37 {
38   WHITENING_EFFECT_LITTLE_WHITENING,
39   WHITENING_EFFECT_MODERATE_WHITENING,
40   WHITENING_EFFECT_HIGH_WHITENING,
41   WHITENING_EFFECT_LITTLE_PINK,
42   WHITENING_EFFECT_MODERATE_PINK,
43   WHITENING_EFFECT_HIGH_PINK,
44   WHITENING_EFFECT_LITTLE_FLESH,
45   WHITENING_EFFECT_MODERATE_FLESH,
46   WHITENING_EFFECT_HIGH_FLESH,
47 };
48 
49 static void     query    (void);
50 static void     run      (const gchar      *name,
51                           gint              nparams,
52                           const GimpParam  *param,
53                           gint             *nreturn_vals,
54                           GimpParam       **return_vals);
55 
56 static void     skin_whitening (GimpDrawable *drawable);
57 static void     skin_whitening_effect (GimpDrawable *drawable);
58 
59 static gboolean skin_whitening_dialog (gint32        image_ID,
60                                        GimpDrawable *drawable);
61 
62 static void     reset_pressed (GtkButton *button, gpointer user_date);
63 
64 static void     preview_update (GtkWidget *preview);
65 
66 static GtkWidget* effects_box_new ();
67 static GtkWidget* effect_icon_new (WhiteningEffectType effect);
68 static gboolean   effect_select (GtkWidget *event_box, GdkEventButton *event, WhiteningEffectType effect);
69 
70 const GimpPlugInInfo PLUG_IN_INFO =
71 {
72   NULL,  /* init_proc  */
73   NULL,  /* quit_proc  */
74   query, /* query_proc */
75   run,   /* run_proc   */
76 };
77 
78 static WhiteningValues wvals =
79 {
80   WHITENING_EFFECT_NONE,  /* effect */
81 };
82 
83 static gint32     image_ID         = 0;
84 static gint       width;
85 static gint       height;
86 
87 static GtkWidget *preview          = NULL;
88 static gint32     preview_image    = 0;
89 
90 /* compatable with gtk2 */
91 #if GTK_MAJOR_VERSION < 3
92 GtkWidget *
gtk_box_new(GtkOrientation orientation,gint spacing)93 gtk_box_new (GtkOrientation  orientation,
94              gint            spacing)
95 {
96   if (orientation == GTK_ORIENTATION_HORIZONTAL)
97     return gtk_hbox_new (FALSE, spacing);
98   else
99     return gtk_vbox_new (FALSE, spacing);
100 }
101 #endif
102 
MAIN()103 MAIN ()
104 
105 static void
106 query (void)
107 {
108   static const GimpParamDef args[] =
109   {
110     { GIMP_PDB_INT32,    "run-mode",   "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
111     { GIMP_PDB_IMAGE,    "image",      "Input image" },
112     { GIMP_PDB_DRAWABLE, "drawable",   "Input drawable" },
113     { GIMP_PDB_INT32,    "effect",     "The effect to apply { LITTLE_WHITENING (1), MODERATE_WHITENING (2), HIGH_WHITENING (3), LITTLE_PINK (4), MODERATE_PINK (5), HIGH_PINK (6), LITTLE_FLESH (7), MODERATE_FLESH (8), HIGH_FLESH (9) }" }
114   };
115 
116   gimp_install_procedure (PLUG_IN_PROC,
117                           "Quickly and easily whiten the skin.",
118                           "Quickly and easily whiten the skin. Goto https://github.com/hejiann/beautify/wiki get more help.",
119                           "Hejian <hejian.he@gmail.com>",
120                           "Hejian <hejian.he@gmail.com>",
121                           "2012",
122                           "_Skin whitening...",
123                           "RGB*, GRAY*",
124                           GIMP_PLUGIN,
125                           G_N_ELEMENTS (args), 0,
126                           args, NULL);
127 
128   gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Filters/Beautify");
129 }
130 
131 static void
run(const gchar * name,gint nparams,const GimpParam * param,gint * nreturn_vals,GimpParam ** return_vals)132 run (const gchar      *name,
133      gint              nparams,
134      const GimpParam  *param,
135      gint             *nreturn_vals,
136      GimpParam       **return_vals)
137 {
138   static GimpParam   values[2];
139   GimpDrawable      *drawable;
140   GimpRunMode        run_mode;
141   GimpPDBStatusType  status = GIMP_PDB_SUCCESS;
142 
143   run_mode = param[0].data.d_int32;
144 
145   *nreturn_vals = 1;
146   *return_vals  = values;
147 
148   values[0].type          = GIMP_PDB_STATUS;
149   values[0].data.d_status = status;
150 
151   image_ID = param[1].data.d_image;
152   drawable = gimp_drawable_get (param[2].data.d_drawable);
153 
154   width = gimp_image_width (image_ID);
155   height = gimp_image_height (image_ID);
156 
157   switch (run_mode)
158   {
159     case GIMP_RUN_INTERACTIVE:
160       if (! skin_whitening_dialog (image_ID, drawable))
161         return;
162       break;
163 
164     case GIMP_RUN_NONINTERACTIVE:
165       /*  Make sure all the arguments are there!  */
166       if (nparams != 4)
167         status = GIMP_PDB_CALLING_ERROR;
168 
169       if (status == GIMP_PDB_SUCCESS)
170       {
171         wvals.effect = param[3].data.d_int32;
172       }
173       break;
174 
175     case GIMP_RUN_WITH_LAST_VALS:
176       /*  Possibly retrieve data  */
177       gimp_get_data (PLUG_IN_PROC, &wvals);
178       break;
179 
180     default:
181       break;
182   }
183 
184   if ((status == GIMP_PDB_SUCCESS) &&
185       (gimp_drawable_is_rgb(drawable->drawable_id) ||
186        gimp_drawable_is_gray(drawable->drawable_id)))
187     {
188       /* Run! */
189       gimp_image_undo_group_start (image_ID);
190       if (run_mode == GIMP_RUN_INTERACTIVE)
191         skin_whitening (drawable);
192       else
193         skin_whitening_effect (drawable);
194       gimp_image_undo_group_end (image_ID);
195 
196       /* If run mode is interactive, flush displays */
197       if (run_mode != GIMP_RUN_NONINTERACTIVE)
198         gimp_displays_flush ();
199 
200       /* Store data */
201       if (run_mode == GIMP_RUN_INTERACTIVE)
202         gimp_set_data (PLUG_IN_PROC, &wvals, sizeof (WhiteningValues));
203 
204     }
205 
206   gimp_drawable_detach (drawable);
207 }
208 
209 static void
skin_whitening(GimpDrawable * drawable)210 skin_whitening (GimpDrawable *drawable)
211 {
212   gint32 source = gimp_image_get_active_layer (preview_image);
213   gimp_edit_copy (source);
214   gint32 floating_sel = gimp_edit_paste (drawable->drawable_id, FALSE);
215   gimp_floating_sel_anchor (floating_sel);
216 }
217 
218 static void
skin_whitening_effect(GimpDrawable * drawable)219 skin_whitening_effect (GimpDrawable *drawable)
220 {
221   run_effect (image_ID, wvals.effect);
222 }
223 
224 static gboolean
skin_whitening_dialog(gint32 image_ID,GimpDrawable * drawable)225 skin_whitening_dialog (gint32        image_ID,
226                  GimpDrawable *drawable)
227 {
228   GtkWidget *dialog;
229   GtkWidget *main_hbox;
230   GtkWidget *left_vbox;
231   GtkWidget *middle_vbox;
232   GtkWidget *right_vbox;
233 
234   gimp_ui_init (PLUG_IN_BINARY, FALSE);
235 
236   dialog = gimp_dialog_new ("Skin Whitening", PLUG_IN_ROLE,
237                             NULL, 0,
238                             gimp_standard_help_func, PLUG_IN_PROC,
239 
240                             GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
241                             GTK_STOCK_OK,     GTK_RESPONSE_OK,
242 
243                             NULL);
244 
245   gimp_window_set_transient (GTK_WINDOW (dialog));
246 
247   gtk_widget_show (dialog);
248 
249   main_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
250   gtk_container_set_border_width (GTK_CONTAINER (main_hbox), 12);
251   gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
252                       main_hbox, TRUE, TRUE, 0);
253   gtk_widget_show (main_hbox);
254 
255   left_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
256   gtk_container_set_border_width (GTK_CONTAINER (left_vbox), 12);
257   gtk_box_pack_start (GTK_BOX (main_hbox), left_vbox, TRUE, TRUE, 0);
258   gtk_widget_show (left_vbox);
259 
260   middle_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
261   gtk_container_set_border_width (GTK_CONTAINER (middle_vbox), 12);
262   gtk_box_pack_start (GTK_BOX (main_hbox), middle_vbox, TRUE, TRUE, 0);
263   gtk_widget_show (middle_vbox);
264 
265   right_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
266   gtk_container_set_border_width (GTK_CONTAINER (right_vbox), 12);
267   gtk_box_pack_start (GTK_BOX (main_hbox), right_vbox, TRUE, TRUE, 0);
268   gtk_widget_show (right_vbox);
269 
270   /* buttons */
271   GtkWidget *buttons = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
272   gtk_box_pack_start (GTK_BOX (middle_vbox), buttons, FALSE, FALSE, 0);
273   gtk_widget_show (buttons);
274 
275   GtkWidget *reset = gtk_button_new_with_label ("Reset");
276   gtk_box_pack_start (GTK_BOX (buttons), reset, FALSE, FALSE, 0);
277   gtk_widget_show (reset);
278   g_signal_connect (reset, "pressed", G_CALLBACK (reset_pressed), NULL);
279 
280   /* preview */
281   preview_image = gimp_image_duplicate (image_ID);
282 
283   preview = gtk_image_new();
284   preview_update (preview);
285 
286   gtk_box_pack_start (GTK_BOX (middle_vbox), preview, TRUE, TRUE, 0);
287   gtk_widget_show (preview);
288 
289   /* effects */
290   GtkWidget *label = gtk_label_new ("One click whitening");
291   gtk_box_pack_start (GTK_BOX (right_vbox), label, FALSE, FALSE, 0);
292   gtk_widget_show (label);
293 
294   GtkWidget *effects = effects_box_new ();
295   gtk_box_pack_start (GTK_BOX (right_vbox), effects, FALSE, FALSE, 0);
296   gtk_widget_show (effects);
297 
298   gboolean run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
299 
300   gtk_widget_destroy (dialog);
301 
302   return run;
303 }
304 
305 static void
reset_pressed(GtkButton * button,gpointer user_date)306 reset_pressed (GtkButton *button, gpointer user_date)
307 {
308   gimp_image_delete (preview_image);
309   preview_image = gimp_image_duplicate (image_ID);
310   preview_update (preview);
311 }
312 
313 static void
preview_update(GtkWidget * preview)314 preview_update (GtkWidget *preview)
315 {
316   gint preview_size = PREVIEW_SIZE;
317   gint max_size = height;
318   if (height < width)
319     max_size = width;
320   if (preview_size > max_size)
321     preview_size = max_size;
322   GdkPixbuf *pixbuf = gimp_image_get_thumbnail (preview_image, preview_size, preview_size, GIMP_PIXBUF_SMALL_CHECKS);
323   gtk_image_set_from_pixbuf (GTK_IMAGE(preview), pixbuf);
324 }
325 
326 static GtkWidget *
effects_box_new()327 effects_box_new ()
328 {
329   gint rows = 5;
330   gint cols = 3;
331   GtkWidget *table = gtk_table_new (rows, cols, FALSE);
332   gtk_table_set_col_spacings (GTK_TABLE (table), 6);
333   gtk_table_set_row_spacings (GTK_TABLE (table), 6);
334 
335   gint row = 1;
336   gint col = 1;
337 
338   gint i;
339   for (i = 0; i < G_N_ELEMENTS (effects); i++) {
340     GtkWidget *icon = effect_icon_new (effects[i]);
341     gtk_table_attach_defaults (GTK_TABLE (table), icon, col - 1, col, row - 1, row);
342     gtk_widget_show (icon);
343 
344     col++;
345     if (col > cols)
346     {
347       row++;
348       col = 1;
349     }
350   }
351 
352   return table;
353 }
354 
355 static GtkWidget *
effect_icon_new(WhiteningEffectType effect)356 effect_icon_new (WhiteningEffectType effect)
357 {
358   const guint8 *data;
359   gchar  *title;
360 
361   switch (effect) {
362     case WHITENING_EFFECT_LITTLE_WHITENING:
363       data = skin_whitening_1;
364       title = "Little\nWhitening";
365       break;
366     case WHITENING_EFFECT_MODERATE_WHITENING:
367       data = skin_whitening_2;
368       title = "Moderate\nWhitening";
369       break;
370     case WHITENING_EFFECT_HIGH_WHITENING:
371       data = skin_whitening_3;
372       title = "High\nWhitening";
373       break;
374     case WHITENING_EFFECT_LITTLE_PINK:
375       data = skin_whitening_4;
376       title = "Little Pink";
377       break;
378     case WHITENING_EFFECT_MODERATE_PINK:
379       data = skin_whitening_5;
380       title = "Moderate\nPink";
381       break;
382     case WHITENING_EFFECT_HIGH_PINK:
383       data = skin_whitening_6;
384       title = "High Pink";
385       break;
386     case WHITENING_EFFECT_LITTLE_FLESH:
387       data = skin_whitening_7;
388       title = "Little Flesh";
389       break;
390     case WHITENING_EFFECT_MODERATE_FLESH:
391       data = skin_whitening_8;
392       title = "Moderate\nFlesh";
393       break;
394     case WHITENING_EFFECT_HIGH_FLESH:
395       data = skin_whitening_9;
396       title = "High Flesh";
397       break;
398   }
399 
400   GtkWidget *box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
401 
402   /* image */
403   GdkPixbuf *pixbuf = gdk_pixbuf_new_from_inline (-1, data, FALSE, NULL);
404   GtkWidget *image = gtk_image_new_from_pixbuf (pixbuf);
405   GtkWidget *event_box = gtk_event_box_new ();
406   gtk_container_add (GTK_CONTAINER (event_box), image);
407   gtk_widget_show (image);
408   gtk_box_pack_start (GTK_BOX (box), event_box, FALSE, FALSE, 0);
409   gtk_widget_show (event_box);
410 
411   g_signal_connect (event_box, "button_press_event", G_CALLBACK (effect_select), (gpointer)effect);
412 
413   /* label */
414   GtkWidget *label = gtk_label_new (title);
415   /* gtk_label_set_line_wrap will let textalign to left instead of center :(
416   gtk_widget_set_size_request (label, 80, -1);
417   gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);*/
418   gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_CENTER);
419   gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0);
420   gtk_widget_show (label);
421 
422   return box;
423 }
424 
425 static gboolean
effect_select(GtkWidget * event_box,GdkEventButton * event,WhiteningEffectType effect)426 effect_select (GtkWidget *event_box, GdkEventButton *event, WhiteningEffectType effect)
427 {
428   run_effect (preview_image, effect);
429 
430   preview_update (preview);
431 }
432