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