1 /* GIMP - The GNU Image Manipulation Program
2 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
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 <https://www.gnu.org/licenses/>.
16 */
17
18 /* Cartoon filter for GIMP for BIPS
19 * -Spencer Kimball
20 *
21 * This filter propagates dark values in an image based on
22 * each pixel's relative darkness to a neighboring average
23 */
24
25 #include "config.h"
26
27 #include <string.h>
28
29 #include <libgimp/gimp.h>
30 #include <libgimp/gimpui.h>
31
32 #include "libgimp/stdplugins-intl.h"
33
34
35 /* Some useful macros */
36
37 #define PLUG_IN_PROC "plug-in-cartoon"
38 #define PLUG_IN_BINARY "cartoon"
39 #define PLUG_IN_ROLE "gimp-cartoon"
40 #define TILE_CACHE_SIZE 48
41
42 typedef struct
43 {
44 gdouble mask_radius;
45 gdouble threshold;
46 gdouble pct_black;
47 } CartoonVals;
48
49
50 /*
51 * Function prototypes.
52 */
53
54 static void query (void);
55 static void run (const gchar *name,
56 gint nparams,
57 const GimpParam *param,
58 gint *nreturn_vals,
59 GimpParam **return_vals);
60
61 static void cartoon (GimpDrawable *drawable,
62 GimpPreview *preview);
63 static gboolean cartoon_dialog (GimpDrawable *drawable);
64
65 static gdouble compute_ramp (guchar *dest1,
66 guchar *dest2,
67 gint length,
68 gdouble pct_black);
69
70 /*
71 * Gaussian blur helper functions
72 */
73 static void find_constants (gdouble n_p[],
74 gdouble n_m[],
75 gdouble d_p[],
76 gdouble d_m[],
77 gdouble bd_p[],
78 gdouble bd_m[],
79 gdouble std_dev);
80 static void transfer_pixels (gdouble *src1,
81 gdouble *src2,
82 guchar *dest,
83 gint jump,
84 gint bytes,
85 gint width);
86
87
88 /***** Local vars *****/
89
90 const GimpPlugInInfo PLUG_IN_INFO =
91 {
92 NULL, /* init */
93 NULL, /* quit */
94 query, /* query */
95 run, /* run */
96 };
97
98 static CartoonVals cvals =
99 {
100 7.0, /* mask_radius */
101 1.0, /* threshold */
102 0.2 /* pct_black */
103 };
104
105
106 /***** Functions *****/
107
MAIN()108 MAIN ()
109
110 static void
111 query (void)
112 {
113 static const GimpParamDef args[] =
114 {
115 { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
116 { GIMP_PDB_IMAGE, "image", "Input image (unused)" },
117 { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
118 { GIMP_PDB_FLOAT, "mask-radius", "Cartoon mask radius (radius of pixel neighborhood)" },
119 { GIMP_PDB_FLOAT, "pct-black", "Percentage of darkened pixels to set to black (0.0 - 1.0)" }
120 };
121
122 gchar *help_string =
123 "Propagates dark values in an image based on "
124 "each pixel's relative darkness to a neighboring average. The idea behind "
125 "this filter is to give the look of a black felt pen drawing subsequently "
126 "shaded with color. This is achieved by darkening areas of the image which "
127 "are measured to be darker than a neighborhood average. In this way, "
128 "sufficiently large shifts in intensity are darkened to black. The rate "
129 "at which they are darkened to black is determined by the second pct_black "
130 "parameter. The mask_radius parameter controls the size of the pixel "
131 "neighborhood over which the average intensity is computed and then "
132 "compared to each pixel in the neighborhood to decide whether or not to "
133 "darken it to black. Large values for mask_radius result in very thick "
134 "black areas bordering the shaded regions of color and much less detail "
135 "for black areas everywhere including inside regions of color. Small "
136 "values result in more subtle pen strokes and detail everywhere. Small "
137 "values for the pct_black make the blend from the color regions to the "
138 "black border lines smoother and the lines themselves thinner and less "
139 "noticeable; larger values achieve the opposite effect.";
140
141 gimp_install_procedure (PLUG_IN_PROC,
142 N_("Simulate a cartoon by enhancing edges"),
143 help_string,
144 "Spencer Kimball",
145 "Bit Specialists, Inc.",
146 "2001",
147 N_("Ca_rtoon (legacy)..."),
148 "RGB*, GRAY*",
149 GIMP_PLUGIN,
150 G_N_ELEMENTS (args), 0,
151 args, NULL);
152
153 gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Filters/Artistic");
154 }
155
156 static void
run(const gchar * name,gint nparams,const GimpParam * param,gint * nreturn_vals,GimpParam ** return_vals)157 run (const gchar *name,
158 gint nparams,
159 const GimpParam *param,
160 gint *nreturn_vals,
161 GimpParam **return_vals)
162 {
163 static GimpParam values[2];
164 GimpRunMode run_mode;
165 GimpDrawable *drawable;
166 GimpPDBStatusType status = GIMP_PDB_SUCCESS;
167
168 run_mode = param[0].data.d_int32;
169
170 /* Get the specified drawable */
171 drawable = gimp_drawable_get (param[2].data.d_drawable);
172
173 /* set the tile cache size */
174 gimp_tile_cache_ntiles (TILE_CACHE_SIZE);
175
176 *nreturn_vals = 1;
177 *return_vals = values;
178
179 values[0].type = GIMP_PDB_STATUS;
180 values[0].data.d_status = status;
181
182 INIT_I18N();
183
184 switch (run_mode)
185 {
186 case GIMP_RUN_INTERACTIVE:
187 /* Possibly retrieve data */
188 gimp_get_data (PLUG_IN_PROC, &cvals);
189
190 /* First acquire information with a dialog */
191 if (! cartoon_dialog (drawable))
192 return;
193 break;
194
195 case GIMP_RUN_NONINTERACTIVE:
196 cvals.mask_radius = param[3].data.d_float;
197 cvals.pct_black = param[4].data.d_float;
198 break;
199
200 case GIMP_RUN_WITH_LAST_VALS:
201 /* Possibly retrieve data */
202 gimp_get_data (PLUG_IN_PROC, &cvals);
203 break;
204
205 default:
206 break;
207 }
208
209 if (status == GIMP_PDB_SUCCESS)
210 {
211 /* Make sure that the drawable is RGB or GRAY color */
212 if (gimp_drawable_is_rgb (drawable->drawable_id) ||
213 gimp_drawable_is_gray (drawable->drawable_id))
214 {
215 gimp_progress_init ("Cartoon");
216
217
218 cartoon (drawable, NULL);
219
220 if (run_mode != GIMP_RUN_NONINTERACTIVE)
221 gimp_displays_flush ();
222
223 /* Store data */
224 if (run_mode == GIMP_RUN_INTERACTIVE)
225 gimp_set_data (PLUG_IN_PROC, &cvals, sizeof (CartoonVals));
226 }
227 else
228 {
229 status = GIMP_PDB_EXECUTION_ERROR;
230 *nreturn_vals = 2;
231 values[1].type = GIMP_PDB_STRING;
232 values[1].data.d_string = _("Cannot operate on indexed color images.");
233 }
234 }
235
236 values[0].data.d_status = status;
237
238 gimp_drawable_detach (drawable);
239 }
240
241 /*
242 * Cartoon algorithm
243 * -----------------
244 * Mask radius = radius of pixel neighborhood for intensity comparison
245 * Threshold = relative intensity difference which will result in darkening
246 * Ramp = amount of relative intensity difference before total black
247 * Blur radius = mask radius / 3.0
248 *
249 * Algorithm:
250 * For each pixel, calculate pixel intensity value to be: avg (blur radius)
251 * relative diff = pixel intensity / avg (mask radius)
252 * If relative diff < Threshold
253 * intensity mult = (Ramp - MIN (Ramp, (Threshold - relative diff))) / Ramp
254 * pixel intensity *= intensity mult
255 */
256 static void
cartoon(GimpDrawable * drawable,GimpPreview * preview)257 cartoon (GimpDrawable *drawable,
258 GimpPreview *preview)
259 {
260 GimpPixelRgn src_rgn, dest_rgn;
261 GimpPixelRgn *pr;
262 gint x, y, width, height;
263 gint bytes;
264 gboolean has_alpha;
265 guchar *dest1;
266 guchar *dest2;
267 guchar *src;
268 guchar *src1, *sp_p1, *sp_m1;
269 guchar *src2, *sp_p2, *sp_m2;
270 gdouble n_p1[5], n_m1[5];
271 gdouble n_p2[5], n_m2[5];
272 gdouble d_p1[5], d_m1[5];
273 gdouble d_p2[5], d_m2[5];
274 gdouble bd_p1[5], bd_m1[5];
275 gdouble bd_p2[5], bd_m2[5];
276 gdouble *val_p1, *val_m1, *vp1, *vm1;
277 gdouble *val_p2, *val_m2, *vp2, *vm2;
278 gint i, j;
279 gint row, col, b;
280 gint terms;
281 gint progress, max_progress;
282 gint initial_p1[4];
283 gint initial_p2[4];
284 gint initial_m1[4];
285 gint initial_m2[4];
286 gdouble radius;
287 gdouble std_dev1;
288 gdouble std_dev2;
289 gdouble ramp;
290 guchar *preview_buffer = NULL;
291
292 if (preview)
293 {
294 gimp_preview_get_position (preview, &x, &y);
295 gimp_preview_get_size (preview, &width, &height);
296 }
297 else if (! gimp_drawable_mask_intersect (drawable->drawable_id,
298 &x, &y, &width, &height))
299 {
300 return;
301 }
302
303 bytes = drawable->bpp;
304 has_alpha = gimp_drawable_has_alpha (drawable->drawable_id);
305
306 val_p1 = g_new (gdouble, MAX (width, height) * bytes);
307 val_p2 = g_new (gdouble, MAX (width, height) * bytes);
308 val_m1 = g_new (gdouble, MAX (width, height) * bytes);
309 val_m2 = g_new (gdouble, MAX (width, height) * bytes);
310
311 src = g_new (guchar, MAX (width, height) * bytes);
312 dest1 = g_new0 (guchar, width * height);
313 dest2 = g_new0 (guchar, width * height);
314
315 gimp_pixel_rgn_init (&src_rgn, drawable,
316 0, 0, drawable->width, drawable->height, FALSE, FALSE);
317
318 progress = 0;
319 max_progress = width * height * 2;
320
321 /* Calculate the standard deviations */
322 radius = 1.0; /* blur radius */
323 radius = fabs (radius) + 1.0;
324 std_dev1 = sqrt (-(radius * radius) / (2 * log (1.0 / 255.0)));
325
326 radius = cvals.mask_radius;
327 radius = fabs (radius) + 1.0;
328 std_dev2 = sqrt (-(radius * radius) / (2 * log (1.0 / 255.0)));
329
330 /* derive the constants for calculating the gaussian from the std dev */
331 find_constants (n_p1, n_m1, d_p1, d_m1, bd_p1, bd_m1, std_dev1);
332 find_constants (n_p2, n_m2, d_p2, d_m2, bd_p2, bd_m2, std_dev2);
333
334 /* First the vertical pass */
335 for (col = 0; col < width; col++)
336 {
337 memset (val_p1, 0, height * bytes * sizeof (gdouble));
338 memset (val_p2, 0, height * bytes * sizeof (gdouble));
339 memset (val_m1, 0, height * bytes * sizeof (gdouble));
340 memset (val_m2, 0, height * bytes * sizeof (gdouble));
341
342 gimp_pixel_rgn_get_col (&src_rgn, src, col + x, y, height);
343
344 src1 = src;
345 sp_p1 = src1;
346 sp_m1 = src1 + (height - 1) * bytes;
347 vp1 = val_p1;
348 vp2 = val_p2;
349 vm1 = val_m1 + (height - 1) * bytes;
350 vm2 = val_m2 + (height - 1) * bytes;
351
352 /* Set up the first vals */
353 for (i = 0; i < bytes; i++)
354 {
355 initial_p1[i] = sp_p1[i];
356 initial_m1[i] = sp_m1[i];
357 }
358
359 for (row = 0; row < height; row++)
360 {
361 gdouble *vpptr1, *vmptr1;
362 gdouble *vpptr2, *vmptr2;
363
364 terms = (row < 4) ? row : 4;
365
366 for (b = 0; b < bytes; b++)
367 {
368 vpptr1 = vp1 + b; vmptr1 = vm1 + b;
369 vpptr2 = vp2 + b; vmptr2 = vm2 + b;
370
371 for (i = 0; i <= terms; i++)
372 {
373 *vpptr1 += n_p1[i] * sp_p1[(-i * bytes) + b] -
374 d_p1[i] * vp1[(-i * bytes) + b];
375 *vmptr1 += n_m1[i] * sp_m1[(i * bytes) + b] -
376 d_m1[i] * vm1[(i * bytes) + b];
377
378 *vpptr2 += n_p2[i] * sp_p1[(-i * bytes) + b] -
379 d_p2[i] * vp2[(-i * bytes) + b];
380 *vmptr2 += n_m2[i] * sp_m1[(i * bytes) + b] -
381 d_m2[i] * vm2[(i * bytes) + b];
382 }
383
384 for (j = i; j <= 4; j++)
385 {
386 *vpptr1 += (n_p1[j] - bd_p1[j]) * initial_p1[b];
387 *vmptr1 += (n_m1[j] - bd_m1[j]) * initial_m1[b];
388
389 *vpptr2 += (n_p2[j] - bd_p2[j]) * initial_p1[b];
390 *vmptr2 += (n_m2[j] - bd_m2[j]) * initial_m1[b];
391 }
392 }
393
394 sp_p1 += bytes;
395 sp_m1 -= bytes;
396 vp1 += bytes;
397 vp2 += bytes;
398 vm1 -= bytes;
399 vm2 -= bytes;
400 }
401
402 transfer_pixels (val_p1, val_m1, dest1 + col, width, bytes, height);
403 transfer_pixels (val_p2, val_m2, dest2 + col, width, bytes, height);
404
405 if (!preview)
406 {
407 progress += height;
408 if ((col % 5) == 0)
409 gimp_progress_update ((gdouble) progress / (gdouble) max_progress);
410 }
411 }
412
413 for (row = 0; row < height; row++)
414 {
415 memset (val_p1, 0, width * sizeof (gdouble));
416 memset (val_p2, 0, width * sizeof (gdouble));
417 memset (val_m1, 0, width * sizeof (gdouble));
418 memset (val_m2, 0, width * sizeof (gdouble));
419
420 src1 = dest1 + row * width;
421 src2 = dest2 + row * width;
422
423 sp_p1 = src1;
424 sp_p2 = src2;
425 sp_m1 = src1 + width - 1;
426 sp_m2 = src2 + width - 1;
427 vp1 = val_p1;
428 vp2 = val_p2;
429 vm1 = val_m1 + width - 1;
430 vm2 = val_m2 + width - 1;
431
432 /* Set up the first vals */
433 initial_p1[0] = sp_p1[0];
434 initial_p2[0] = sp_p2[0];
435 initial_m1[0] = sp_m1[0];
436 initial_m2[0] = sp_m2[0];
437
438 for (col = 0; col < width; col++)
439 {
440 gdouble *vpptr1, *vmptr1;
441 gdouble *vpptr2, *vmptr2;
442
443 terms = (col < 4) ? col : 4;
444
445 vpptr1 = vp1; vmptr1 = vm1;
446 vpptr2 = vp2; vmptr2 = vm2;
447
448 for (i = 0; i <= terms; i++)
449 {
450 *vpptr1 += n_p1[i] * sp_p1[-i] - d_p1[i] * vp1[-i];
451 *vmptr1 += n_m1[i] * sp_m1[i] - d_m1[i] * vm1[i];
452
453 *vpptr2 += n_p2[i] * sp_p2[-i] - d_p2[i] * vp2[-i];
454 *vmptr2 += n_m2[i] * sp_m2[i] - d_m2[i] * vm2[i];
455 }
456
457 for (j = i; j <= 4; j++)
458 {
459 *vpptr1 += (n_p1[j] - bd_p1[j]) * initial_p1[0];
460 *vmptr1 += (n_m1[j] - bd_m1[j]) * initial_m1[0];
461
462 *vpptr2 += (n_p2[j] - bd_p2[j]) * initial_p2[0];
463 *vmptr2 += (n_m2[j] - bd_m2[j]) * initial_m2[0];
464 }
465
466 sp_p1 ++;
467 sp_p2 ++;
468 sp_m1 --;
469 sp_m2 --;
470 vp1 ++;
471 vp2 ++;
472 vm1 --;
473 vm2 --;
474 }
475
476 transfer_pixels (val_p1, val_m1, dest1 + row * width, 1, 1, width);
477 transfer_pixels (val_p2, val_m2, dest2 + row * width, 1, 1, width);
478
479 if (!preview)
480 {
481 progress += width;
482 if ((row % 5) == 0)
483 gimp_progress_update ((gdouble) progress / (gdouble) max_progress);
484 }
485 }
486
487 /* Compute the ramp value which sets 'pct_black' % of the darkened pixels black */
488 ramp = compute_ramp (dest1, dest2, width * height, cvals.pct_black);
489
490 /* Initialize the pixel regions. */
491 gimp_pixel_rgn_init (&src_rgn, drawable, x, y, width, height, FALSE, FALSE);
492
493 if (preview)
494 {
495 pr = gimp_pixel_rgns_register (1, &src_rgn);
496 preview_buffer = g_new (guchar, width * height * bytes);
497 }
498 else
499 {
500 gimp_pixel_rgn_init (&dest_rgn, drawable,
501 x, y, width, height, TRUE, TRUE);
502 pr = gimp_pixel_rgns_register (2, &src_rgn, &dest_rgn);
503 }
504
505 while (pr)
506 {
507 guchar *src_ptr = src_rgn.data;
508 guchar *dest_ptr;
509 guchar *blur_ptr = dest1 + (src_rgn.y - y) * width + (src_rgn.x - x);
510 guchar *avg_ptr = dest2 + (src_rgn.y - y) * width + (src_rgn.x - x);
511 gdouble diff;
512 gdouble mult = 0.0;
513 gdouble lightness;
514
515 if (preview)
516 dest_ptr =
517 preview_buffer +
518 ((src_rgn.y - y) * width + (src_rgn.x - x)) * bytes;
519 else
520 dest_ptr = dest_rgn.data;
521
522 for (row = 0; row < src_rgn.h; row++)
523 {
524 for (col = 0; col < src_rgn.w; col++)
525 {
526 if (avg_ptr[col] != 0)
527 {
528 diff = (gdouble) blur_ptr[col] / (gdouble) avg_ptr[col];
529 if (diff < cvals.threshold)
530 {
531 if (ramp == 0.0)
532 mult = 0.0;
533 else
534 mult = (ramp - MIN (ramp, (cvals.threshold - diff))) / ramp;
535 }
536 else
537 mult = 1.0;
538 }
539
540 lightness = CLAMP (blur_ptr[col] * mult, 0, 255);
541
542 if (bytes < 3)
543 {
544 dest_ptr[col * bytes] = (guchar) lightness;
545 if (has_alpha)
546 dest_ptr[col * bytes + 1] = src_ptr[col * src_rgn.bpp + 1];
547 }
548 else
549 {
550 /* Convert to HLS, set lightness and convert back */
551 gint r, g, b;
552
553 r = src_ptr[col * src_rgn.bpp + 0];
554 g = src_ptr[col * src_rgn.bpp + 1];
555 b = src_ptr[col * src_rgn.bpp + 2];
556
557 gimp_rgb_to_hsl_int (&r, &g, &b);
558 b = lightness;
559 gimp_hsl_to_rgb_int (&r, &g, &b);
560
561 dest_ptr[col * bytes + 0] = r;
562 dest_ptr[col * bytes + 1] = g;
563 dest_ptr[col * bytes + 2] = b;
564
565 if (has_alpha)
566 dest_ptr[col * bytes + 3] = src_ptr[col * src_rgn.bpp + 3];
567 }
568 }
569
570 src_ptr += src_rgn.rowstride;
571 if (preview)
572 dest_ptr += width * bytes;
573 else
574 dest_ptr += dest_rgn.rowstride;
575 blur_ptr += width;
576 avg_ptr += width;
577 }
578
579 if (!preview)
580 {
581 progress += src_rgn.w * src_rgn.h;
582 gimp_progress_update ((gdouble) progress / (gdouble) max_progress);
583 }
584
585 pr = gimp_pixel_rgns_process (pr);
586 }
587
588 if (preview)
589 {
590 gimp_preview_draw_buffer (preview, preview_buffer, width * bytes);
591 g_free (preview_buffer);
592 }
593 else
594 {
595 gimp_progress_update (1.0);
596 /* merge the shadow, update the drawable */
597 gimp_drawable_flush (drawable);
598 gimp_drawable_merge_shadow (drawable->drawable_id, TRUE);
599 gimp_drawable_update (drawable->drawable_id, x, y, width, height);
600 }
601
602 /* free up buffers */
603 g_free (val_p1);
604 g_free (val_p2);
605 g_free (val_m1);
606 g_free (val_m2);
607 g_free (src);
608 g_free (dest1);
609 g_free (dest2);
610 }
611
612 static gdouble
compute_ramp(guchar * dest1,guchar * dest2,gint length,gdouble pct_black)613 compute_ramp (guchar *dest1,
614 guchar *dest2,
615 gint length,
616 gdouble pct_black)
617 {
618 gint hist[100];
619 gdouble diff;
620 gint count;
621 gint i;
622 gint sum;
623
624 memset (hist, 0, sizeof (int) * 100);
625 count = 0;
626
627 for (i = 0; i < length; i++)
628 {
629 if (*dest2 != 0)
630 {
631 diff = (gdouble) *dest1 / (gdouble) *dest2;
632 if (diff < 1.0)
633 {
634 hist[(int) (diff * 100)] += 1;
635 count += 1;
636 }
637 }
638
639 dest1++;
640 dest2++;
641 }
642
643 if (pct_black == 0.0 || count == 0)
644 return 1.0;
645
646 sum = 0;
647 for (i = 0; i < 100; i++)
648 {
649 sum += hist[i];
650 if (((gdouble) sum / (gdouble) count) > pct_black)
651 return (1.0 - (gdouble) i / 100.0);
652 }
653
654 return 0.0;
655 }
656
657
658 /*
659 * Gaussian blur helper functions
660 */
661
662 static void
transfer_pixels(gdouble * src1,gdouble * src2,guchar * dest,gint jump,gint bytes,gint width)663 transfer_pixels (gdouble *src1,
664 gdouble *src2,
665 guchar *dest,
666 gint jump,
667 gint bytes,
668 gint width)
669 {
670 gint i, b;
671 gdouble sum[4];
672
673 for(i = 0; i < width; i++)
674 {
675 for (b = 0; b < bytes; b++)
676 {
677 sum[b] = src1[b] + src2[b];
678 if (sum[b] > 255) sum[b] = 255;
679 else if(sum[b] < 0) sum[b] = 0;
680 }
681
682 /* Convert to lightness if RGB */
683 if (bytes > 2)
684 *dest = (guchar) gimp_rgb_to_l_int (sum[0], sum[1], sum[2]);
685 else
686 *dest = (guchar) sum[0];
687
688 src1 += bytes;
689 src2 += bytes;
690 dest += jump;
691 }
692 }
693
694 static void
find_constants(gdouble n_p[],gdouble n_m[],gdouble d_p[],gdouble d_m[],gdouble bd_p[],gdouble bd_m[],gdouble std_dev)695 find_constants (gdouble n_p[],
696 gdouble n_m[],
697 gdouble d_p[],
698 gdouble d_m[],
699 gdouble bd_p[],
700 gdouble bd_m[],
701 gdouble std_dev)
702 {
703 gint i;
704 gdouble constants [8];
705 gdouble div;
706
707 /* The constants used in the implementation of a casual sequence
708 * using a 4th order approximation of the gaussian operator
709 */
710
711 div = sqrt (2 * G_PI) * std_dev;
712
713 constants [0] = -1.783 / std_dev;
714 constants [1] = -1.723 / std_dev;
715 constants [2] = 0.6318 / std_dev;
716 constants [3] = 1.997 / std_dev;
717 constants [4] = 1.6803 / div;
718 constants [5] = 3.735 / div;
719 constants [6] = -0.6803 / div;
720 constants [7] = -0.2598 / div;
721
722 n_p [0] = constants[4] + constants[6];
723 n_p [1] = exp (constants[1]) *
724 (constants[7] * sin (constants[3]) -
725 (constants[6] + 2 * constants[4]) * cos (constants[3])) +
726 exp (constants[0]) *
727 (constants[5] * sin (constants[2]) -
728 (2 * constants[6] + constants[4]) * cos (constants[2]));
729 n_p [2] = 2 * exp (constants[0] + constants[1]) *
730 ((constants[4] + constants[6]) * cos (constants[3]) * cos (constants[2]) -
731 constants[5] * cos (constants[3]) * sin (constants[2]) -
732 constants[7] * cos (constants[2]) * sin (constants[3])) +
733 constants[6] * exp (2 * constants[0]) +
734 constants[4] * exp (2 * constants[1]);
735 n_p [3] = exp (constants[1] + 2 * constants[0]) *
736 (constants[7] * sin (constants[3]) - constants[6] * cos (constants[3])) +
737 exp (constants[0] + 2 * constants[1]) *
738 (constants[5] * sin (constants[2]) - constants[4] * cos (constants[2]));
739 n_p [4] = 0.0;
740
741 d_p [0] = 0.0;
742 d_p [1] = -2 * exp (constants[1]) * cos (constants[3]) -
743 2 * exp (constants[0]) * cos (constants[2]);
744 d_p [2] = 4 * cos (constants[3]) * cos (constants[2]) * exp (constants[0] + constants[1]) +
745 exp (2 * constants[1]) + exp (2 * constants[0]);
746 d_p [3] = -2 * cos (constants[2]) * exp (constants[0] + 2 * constants[1]) -
747 2 * cos (constants[3]) * exp (constants[1] + 2 * constants[0]);
748 d_p [4] = exp (2 * constants[0] + 2 * constants[1]);
749
750 #ifndef ORIGINAL_READABLE_CODE
751 memcpy(d_m, d_p, 5 * sizeof(gdouble));
752 #else
753 for (i = 0; i <= 4; i++)
754 d_m [i] = d_p [i];
755 #endif
756
757 n_m[0] = 0.0;
758 for (i = 1; i <= 4; i++)
759 n_m [i] = n_p[i] - d_p[i] * n_p[0];
760
761 {
762 gdouble sum_n_p, sum_n_m, sum_d;
763 gdouble a, b;
764
765 sum_n_p = 0.0;
766 sum_n_m = 0.0;
767 sum_d = 0.0;
768
769 for (i = 0; i <= 4; i++)
770 {
771 sum_n_p += n_p[i];
772 sum_n_m += n_m[i];
773 sum_d += d_p[i];
774 }
775
776 #ifndef ORIGINAL_READABLE_CODE
777 sum_d++;
778 a = sum_n_p / sum_d;
779 b = sum_n_m / sum_d;
780 #else
781 a = sum_n_p / (1 + sum_d);
782 b = sum_n_m / (1 + sum_d);
783 #endif
784
785 for (i = 0; i <= 4; i++)
786 {
787 bd_p[i] = d_p[i] * a;
788 bd_m[i] = d_m[i] * b;
789 }
790 }
791 }
792
793 /*******************************************************/
794 /* Dialog */
795 /*******************************************************/
796
797 static gboolean
cartoon_dialog(GimpDrawable * drawable)798 cartoon_dialog (GimpDrawable *drawable)
799 {
800 GtkWidget *dialog;
801 GtkWidget *main_vbox;
802 GtkWidget *preview;
803 GtkWidget *table;
804 GtkObject *scale_data;
805 gboolean run;
806
807 gimp_ui_init (PLUG_IN_BINARY, FALSE);
808
809 dialog = gimp_dialog_new (_("Cartoon"), PLUG_IN_ROLE,
810 NULL, 0,
811 gimp_standard_help_func, PLUG_IN_PROC,
812
813 _("_Cancel"), GTK_RESPONSE_CANCEL,
814 _("_OK"), GTK_RESPONSE_OK,
815
816 NULL);
817
818 gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
819 GTK_RESPONSE_OK,
820 GTK_RESPONSE_CANCEL,
821 -1);
822
823 gimp_window_set_transient (GTK_WINDOW (dialog));
824
825 main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
826 gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
827 gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
828 main_vbox, TRUE, TRUE, 0);
829 gtk_widget_show (main_vbox);
830
831 preview = gimp_drawable_preview_new_from_drawable_id (drawable->drawable_id);
832 gtk_box_pack_start (GTK_BOX (main_vbox), preview, TRUE, TRUE, 0);
833 gtk_widget_show (preview);
834
835 g_signal_connect_swapped (preview, "invalidated",
836 G_CALLBACK (cartoon),
837 drawable);
838
839 table = gtk_table_new (3, 3, FALSE);
840 gtk_table_set_col_spacings (GTK_TABLE (table), 6);
841 gtk_table_set_row_spacings (GTK_TABLE (table), 6);
842 gtk_box_pack_start (GTK_BOX (main_vbox), table, FALSE, FALSE, 0);
843 gtk_widget_show (table);
844
845 /* Label, scale, entry for cvals.amount */
846 scale_data = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
847 _("_Mask radius:"), 100, 5,
848 cvals.mask_radius, 1.0, 50.0, 1, 5.0, 2,
849 TRUE, 0, 0,
850 NULL, NULL);
851
852 g_signal_connect (scale_data, "value-changed",
853 G_CALLBACK (gimp_double_adjustment_update),
854 &cvals.mask_radius);
855 g_signal_connect_swapped (scale_data, "value-changed",
856 G_CALLBACK (gimp_preview_invalidate),
857 preview);
858
859 /* Label, scale, entry for cvals.amount */
860 scale_data = gimp_scale_entry_new (GTK_TABLE (table), 0, 1,
861 _("_Percent black:"), 50, 5,
862 cvals.pct_black, 0.0, 1.0, 0.01, 0.1, 3,
863 TRUE, 0, 0,
864 NULL, NULL);
865
866 g_signal_connect (scale_data, "value-changed",
867 G_CALLBACK (gimp_double_adjustment_update),
868 &cvals.pct_black);
869 g_signal_connect_swapped (scale_data, "value-changed",
870 G_CALLBACK (gimp_preview_invalidate),
871 preview);
872
873 gtk_widget_show (dialog);
874
875 run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
876
877 gtk_widget_destroy (dialog);
878
879 return run;
880 }
881