~ubuntu-branches/ubuntu/jaunty/gimp/jaunty-security

« back to all changes in this revision

Viewing changes to plug-ins/common/oilify.c

  • Committer: Bazaar Package Importer
  • Author(s): Daniel Holbach
  • Date: 2007-05-02 16:33:03 UTC
  • mfrom: (1.1.4 upstream)
  • Revision ID: james.westby@ubuntu.com-20070502163303-bvzhjzbpw8qglc4y
Tags: 2.3.16-1ubuntu1
* Resynchronized with Debian, remaining Ubuntu changes:
  - debian/rules: i18n magic.
* debian/control.in:
  - Maintainer: Ubuntu Core Developers <ubuntu-devel@lists.ubuntu.com>
* debian/patches/02_help-message.patch,
  debian/patches/03_gimp.desktop.in.in.patch,
  debian/patches/10_dont_show_wizard.patch: updated.
* debian/patches/04_composite-signedness.patch,
  debian/patches/05_add-letter-spacing.patch: dropped, used upstream.

Show diffs side-by-side

added added

removed removed

Lines of Context:
17
17
 * You should have received a copy of the GNU General Public License
18
18
 * along with this program; if not, write to the Free Software
19
19
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20
 
 *
21
 
 * $Id: oilify.c,v 1.53 2004/12/23 23:58:35 weskaggs Exp $
22
20
 */
23
21
 
24
22
#include "config.h"
25
23
 
26
 
#include <stdio.h>
27
 
#include <stdlib.h>
28
24
#include <string.h>
29
25
 
30
 
#include <gtk/gtk.h>
31
 
 
32
26
#include <libgimp/gimp.h>
33
27
#include <libgimp/gimpui.h>
34
28
 
35
29
#include "libgimp/stdplugins-intl.h"
36
30
 
37
 
#define SCALE_WIDTH  125
38
 
#define HISTSIZE     256
39
 
 
40
 
#define MODE_RGB       0
41
 
#define MODE_INTEN     1
 
31
 
 
32
#define PLUG_IN_PROC   "plug-in-oilify"
 
33
#define PLUG_IN_BINARY "oilify"
 
34
 
 
35
#define SCALE_WIDTH    125
 
36
#define HISTSIZE       256
 
37
 
 
38
#define MODE_RGB         0
 
39
#define MODE_INTEN       1
42
40
 
43
41
#define INTENSITY(p)   ((guint) (p[0]*77+p[1]*150+p[2]*29) >> 8)
44
42
 
61
59
 
62
60
static void      oilify             (GimpDrawable *drawable,
63
61
                                     GimpPreview  *preview);
64
 
static void      oilify_rgb         (GimpDrawable *drawable,
65
 
                                     GimpPreview  *preview);
66
 
static void      oilify_intensity   (GimpDrawable *drawable,
67
 
                                     GimpPreview  *preview);
68
62
 
69
63
static gboolean  oilify_dialog      (GimpDrawable *drawable);
70
64
 
71
65
 
72
 
GimpPlugInInfo PLUG_IN_INFO =
 
66
const GimpPlugInInfo PLUG_IN_INFO =
73
67
{
74
68
  NULL,  /* init_proc  */
75
69
  NULL,  /* quit_proc  */
79
73
 
80
74
static OilifyVals ovals =
81
75
{
82
 
  7.0,     /* mask size */
83
 
  0,       /* mode      */
84
 
  TRUE     /* preview   */
 
76
  7.0,        /* mask size */
 
77
  MODE_INTEN, /* mode      */
 
78
  TRUE        /* preview   */
85
79
};
86
80
 
87
81
 
90
84
static void
91
85
query (void)
92
86
{
93
 
  static GimpParamDef args[] =
 
87
  static const GimpParamDef args[] =
94
88
  {
95
 
    { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" },
96
 
    { GIMP_PDB_IMAGE, "image", "Input image (unused)" },
97
 
    { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
98
 
    { GIMP_PDB_INT32, "mask_size", "Oil paint mask size" },
99
 
    { GIMP_PDB_INT32, "mode", "Algorithm {RGB (0), INTENSITY (1)}" }
 
89
    { GIMP_PDB_INT32,    "run-mode",  "Interactive, non-interactive"       },
 
90
    { GIMP_PDB_IMAGE,    "image",     "Input image (unused)"               },
 
91
    { GIMP_PDB_DRAWABLE, "drawable",  "Input drawable"                     },
 
92
    { GIMP_PDB_INT32,    "mask-size", "Oil paint mask size"                },
 
93
    { GIMP_PDB_INT32,    "mode",      "Algorithm {RGB (0), INTENSITY (1)}" }
100
94
  };
101
95
 
102
 
  gimp_install_procedure ("plug_in_oilify",
103
 
                          "Modify the specified drawable to resemble an oil "
104
 
                          "painting",
 
96
  gimp_install_procedure (PLUG_IN_PROC,
 
97
                          N_("Smear colors to simulate an oil painting"),
105
98
                          "This function performs the well-known oil-paint "
106
99
                          "effect on the specified drawable.  The size of the "
107
100
                          "input mask is specified by 'mask_size'.",
114
107
                          G_N_ELEMENTS (args), 0,
115
108
                          args, NULL);
116
109
 
117
 
  gimp_plugin_menu_register ("plug_in_oilify", "<Image>/Filters/Artistic");
 
110
  gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Filters/Artistic");
118
111
}
119
112
 
120
113
static void
137
130
  drawable = gimp_drawable_get (param[2].data.d_drawable);
138
131
  gimp_tile_cache_ntiles (2 * drawable->ntile_cols);
139
132
 
140
 
  *nreturn_vals = 2;
 
133
  *nreturn_vals = 1;
141
134
  *return_vals  = values;
142
135
 
143
136
  values[0].type          = GIMP_PDB_STATUS;
147
140
    {
148
141
    case GIMP_RUN_INTERACTIVE:
149
142
      /*  Possibly retrieve data  */
150
 
      gimp_get_data ("plug_in_oilify", &ovals);
 
143
      gimp_get_data (PLUG_IN_PROC, &ovals);
151
144
 
152
145
      /*  First acquire information with a dialog  */
153
146
      if (! oilify_dialog (drawable))
174
167
 
175
168
    case GIMP_RUN_WITH_LAST_VALS:
176
169
      /*  Possibly retrieve data  */
177
 
      gimp_get_data ("plug_in_oilify", &ovals);
 
170
      gimp_get_data (PLUG_IN_PROC, &ovals);
178
171
      break;
179
172
 
180
173
    default:
186
179
      (gimp_drawable_is_rgb (drawable->drawable_id) ||
187
180
       gimp_drawable_is_gray (drawable->drawable_id)))
188
181
    {
189
 
      gimp_progress_init (_("Oil Painting..."));
 
182
      gimp_progress_init (_("Oil painting"));
190
183
 
191
184
      oilify (drawable, NULL);
192
185
 
195
188
 
196
189
      /*  Store data  */
197
190
      if (run_mode == GIMP_RUN_INTERACTIVE)
198
 
        gimp_set_data ("plug_in_oilify", &ovals, sizeof (OilifyVals));
 
191
        gimp_set_data (PLUG_IN_PROC, &ovals, sizeof (OilifyVals));
199
192
    }
200
193
  else
201
194
    {
209
202
}
210
203
 
211
204
/*
212
 
 * For each RGB channel, replace the pixel at (x,y) with the
213
 
 * value that occurs most often in the n x n chunk centered
214
 
 * at (x,y).
 
205
 * For each i in [0, HISTSIZE), hist[i] is the number of occurrences of the
 
206
 * value i. Return a value in [0, HISTSIZE) weighted heavily toward the
 
207
 * most frequent values in the histogram.
 
208
 *
 
209
 * Assuming that hist_max is the maximum number of occurrences for any
 
210
 * value in the histogram, the weight given to each value i is
 
211
 *
 
212
 *         weight = (hist[i] / hist_max)^8
 
213
 *
 
214
 * The 8 is subjective. Lower powers give fuzzier edges.
 
215
 */
 
216
static inline guchar
 
217
weighted_average_value (gint hist[HISTSIZE])
 
218
{
 
219
  gint i;
 
220
  gint max = 1;
 
221
  gfloat weight;
 
222
  gfloat sum = 0.0;
 
223
  gfloat div = 1.0e-6;
 
224
  gint value;
 
225
 
 
226
  for (i = 0; i < HISTSIZE; i++)
 
227
    max = MAX (max, hist[i]);
 
228
 
 
229
  for (i = 0; i < HISTSIZE; i++)
 
230
  {
 
231
    weight = (gfloat) hist[i] / (gfloat) max;
 
232
    weight *= weight;
 
233
    weight *= weight;
 
234
    weight *= weight;  /* w' = w^8 */
 
235
 
 
236
    sum += weight * (gfloat) i;
 
237
    div += weight;
 
238
  }
 
239
 
 
240
  value = (gint) (sum / div);
 
241
 
 
242
  return (guchar) CLAMP (value, 0, HISTSIZE - 1);
 
243
}
 
244
 
 
245
/*
 
246
 * For each i in [0, HISTSIZE), hist[i] is the number of occurrences of
 
247
 * pixels with intensity i. hist_rgb[][i] is the average color of those
 
248
 * pixels with intensity i, each channel multiplied by hist[i]. Write to
 
249
 * dest a pixel whose color is a weighted average of all the colors in
 
250
 * hist_rgb[][], biased heavily toward those with the most frequently-
 
251
 * occurring intensities (as noted in hist[]).
 
252
 *
 
253
 * The weight formula is the same as in weighted_average_value().
 
254
 */
 
255
static inline void
 
256
weighted_average_color (gint    hist[HISTSIZE],
 
257
                        gint    hist_rgb[4][HISTSIZE],
 
258
                        guchar *dest,
 
259
                        gint    bytes)
 
260
{
 
261
  gint   i, b, c;
 
262
  gint   max = 1;
 
263
  gfloat weight;
 
264
  gfloat div = 1.0e-6;
 
265
  gfloat color[4] = { 0.0, 0.0, 0.0, 0.0 };
 
266
 
 
267
  for (i = 0; i < HISTSIZE; i++)
 
268
    max = MAX (max, hist[i]);
 
269
 
 
270
  for (i = 0; i < HISTSIZE; i++)
 
271
  {
 
272
    weight = (gfloat) hist[i] / (gfloat) max;
 
273
    weight *= weight;
 
274
    weight *= weight;
 
275
    weight *= weight;  /* w' = w^8 */
 
276
 
 
277
    if (hist[i] > 0)
 
278
      for (b = 0; b < bytes; b++)
 
279
        color[b] += weight * (gfloat) hist_rgb[b][i] / (gfloat) hist[i];
 
280
 
 
281
    div += weight;
 
282
  }
 
283
 
 
284
  for (b = 0; b < bytes; b++)
 
285
    {
 
286
      c = (gint) (color[b] / div);
 
287
      dest[b] = (guchar) CLAMP (c, 0, HISTSIZE - 1);
 
288
    }
 
289
}
 
290
 
 
291
/*
 
292
 * For all x and y as desired, replace the pixel at (x,y)
 
293
 * with a weighted average of the most frequently occurring
 
294
 * values in a circle of radius n centered at (x,y).
215
295
 */
216
296
static void
217
 
oilify_rgb (GimpDrawable *drawable,
218
 
            GimpPreview  *preview)
 
297
oilify (GimpDrawable *drawable,
 
298
        GimpPreview  *preview)
219
299
{
 
300
  gboolean      use_inten_alg;
220
301
  GimpPixelRgn  src_rgn, dest_rgn;
221
302
  gint          bytes;
222
303
  gint          width, height;
223
304
  guchar       *src_row, *src;
224
305
  guchar       *dest_row, *dest;
225
 
  gint          x, y, c, b, xx, yy, n;
226
 
  gint          x1, y1, x2, y2;
227
 
  gint          x3, y3, x4, y4;
228
 
  gint          Cnt[4];
229
 
  gint          Hist[4][HISTSIZE];
230
 
  gpointer      pr1;
231
 
  gint          progress, max_progress;
232
 
  guchar       *src_buf;
233
 
 
234
 
  /*  get the selection bounds  */
235
 
  if (preview)
236
 
    {
237
 
      gimp_preview_get_position (preview, &x1, &y1);
238
 
      gimp_preview_get_size (preview, &width, &height);
239
 
 
240
 
      x2 = x1 + width;
241
 
      y2 = y1 + height;
242
 
    }
243
 
  else
244
 
    {
245
 
      gimp_drawable_mask_bounds (drawable->drawable_id, &x1, &y1, &x2, &y2);
246
 
      width  = x2 - x1;
247
 
      height = y2 - y1;
248
 
    }
249
 
 
250
 
  progress = 0;
251
 
  max_progress = width * height;
252
 
 
253
 
  bytes = drawable->bpp;
254
 
 
255
 
  n = (int) ovals.mask_size / 2;
256
 
 
257
 
  gimp_pixel_rgn_init (&dest_rgn, drawable,
258
 
                       x1, y1, width, height, (preview == NULL), TRUE);
259
 
  gimp_pixel_rgn_init (&src_rgn, drawable,
260
 
                       x1, y1, width, height, FALSE, FALSE);
261
 
  src_buf = g_new (guchar, width * height * bytes);
262
 
  gimp_pixel_rgn_get_rect (&src_rgn, src_buf, x1, y1, width, height);
263
 
 
264
 
  for (pr1 = gimp_pixel_rgns_register (1, &dest_rgn);
265
 
       pr1 != NULL;
266
 
       pr1 = gimp_pixel_rgns_process (pr1))
267
 
    {
268
 
      dest_row = dest_rgn.data;
269
 
 
270
 
      for (y = dest_rgn.y; y < (dest_rgn.y + dest_rgn.h); y++)
271
 
        {
272
 
          dest = dest_row;
273
 
 
274
 
          for (x = dest_rgn.x; x < (dest_rgn.x + dest_rgn.w); x++)
275
 
            {
276
 
              x3 = CLAMP ((x - n), x1, x2);
277
 
              y3 = CLAMP ((y - n), y1, y2);
278
 
              x4 = CLAMP ((x + n + 1), x1, x2);
279
 
              y4 = CLAMP ((y + n + 1), y1, y2);
280
 
 
281
 
              memset(Cnt, 0, sizeof(Cnt));
282
 
              memset(Hist, 0, sizeof(Hist));
283
 
 
284
 
              src_row = src_buf + ((y3 - y1) * width + (x3 - x1)) * bytes;
285
 
              for (yy = y3 ; yy < y4 ; yy++)
286
 
                {
287
 
                  src = src_row;
288
 
                  for (xx = x3 ; xx < x4 ; xx++)
289
 
                    {
290
 
                      for (b = 0; b < bytes; b++)
291
 
                        {
292
 
                          if ((c = ++Hist[b][src[b]]) > Cnt[b])
293
 
                            {
294
 
                              dest[b] = src[b];
295
 
                              Cnt[b] = c;
296
 
                            }
297
 
                        }
298
 
                      src += bytes;
299
 
                    }
300
 
                  src_row += width * bytes;
301
 
                }
302
 
 
303
 
                dest += bytes;
304
 
            }
305
 
 
306
 
          dest_row += dest_rgn.rowstride;
307
 
        }
308
 
 
309
 
      if (preview)
310
 
        {
311
 
          gimp_drawable_preview_draw_region (GIMP_DRAWABLE_PREVIEW (preview),
312
 
                                             &dest_rgn);
313
 
        }
314
 
      else
315
 
        {
316
 
          progress += dest_rgn.w * dest_rgn.h;
317
 
          if ((progress % 5) == 0)
318
 
            gimp_progress_update ((double) progress / (double) max_progress);
319
 
        }
320
 
    }
321
 
 
322
 
  g_free (src_buf);
323
 
 
324
 
  if (!preview)
325
 
    {
326
 
      /*  update the oil-painted region  */
327
 
      gimp_drawable_flush (drawable);
328
 
      gimp_drawable_merge_shadow (drawable->drawable_id, TRUE);
329
 
      gimp_drawable_update (drawable->drawable_id, x1, y1, width, height);
330
 
    }
331
 
}
332
 
 
333
 
/*
334
 
 * For each RGB channel, replace the pixel at (x,y) with the
335
 
 * value that occurs most often in the n x n chunk centered
336
 
 * at (x,y). Histogram is based on intensity.
337
 
 */
338
 
static void
339
 
oilify_intensity (GimpDrawable *drawable,
340
 
                  GimpPreview  *preview)
341
 
{
342
 
  GimpPixelRgn  src_rgn, dest_rgn;
343
 
  gint          bytes;
344
 
  gint          width, height;
345
 
  guchar       *src_row, *src, *selected_src = NULL;
346
 
  guchar       *dest_row, *dest;
347
 
  gint          x, y, c, xx, yy, n;
348
 
  gint          x1, y1, x2, y2;
349
 
  gint          x3, y3, x4, y4;
350
 
  gint          Cnt;
 
306
  gint          x, y, b, c, xx, yy, n;
 
307
  gint          x1, y1, x2, y2;
 
308
  gint          x3, y3, x4, y4;
351
309
  gint          Hist[HISTSIZE];
 
310
  gint          Hist_rgb[4][HISTSIZE];
352
311
  gpointer      pr1;
353
312
  gint          progress, max_progress;
354
313
  guchar       *src_buf;
 
314
  gchar        *mask_shape;
 
315
  gint          y_off;
 
316
 
 
317
  use_inten_alg = gimp_drawable_is_rgb (drawable->drawable_id) &&
 
318
                  ovals.mode == MODE_INTEN;
355
319
 
356
320
  /*  get the selection bounds  */
357
321
  if (preview)
368
332
      width  = x2 - x1;
369
333
      height = y2 - y1;
370
334
    }
371
 
  bytes = drawable->bpp;
372
335
 
373
336
  progress = 0;
374
337
  max_progress = width * height;
375
338
 
 
339
  bytes = drawable->bpp;
 
340
 
376
341
  n = (int) ovals.mask_size / 2;
377
342
 
 
343
  /*
 
344
   * mask_shape represents a (n+1)x(n+1) bitmap of one-quarter of a
 
345
   * circular disk, with radius n and center at (0,0). This is used in the
 
346
   * inner loop below so that we sample pixels only inside a circular area.
 
347
   * (Think of it as a lookup-table implementation of a distance function,
 
348
   * returning zero if the distance > n and nonzero otherwise.)
 
349
   */
 
350
  mask_shape = g_new (gchar, (n + 1) * (n + 1));
 
351
 
 
352
  for (x = 0; x <= n; x++)
 
353
    for (y = 0; y <= n; y++)
 
354
      mask_shape[y * (n + 1) + x] = x * x + y * y <= n * n;
 
355
 
378
356
  gimp_pixel_rgn_init (&dest_rgn, drawable,
379
357
                       x1, y1, width, height, (preview == NULL), TRUE);
380
358
  gimp_pixel_rgn_init (&src_rgn, drawable,
394
372
 
395
373
          for (x = dest_rgn.x; x < (dest_rgn.x + dest_rgn.w); x++)
396
374
            {
397
 
              Cnt = 0;
398
 
              memset(Hist, 0, sizeof(Hist));
 
375
              if (use_inten_alg)
 
376
                memset (Hist, 0, sizeof (Hist));
 
377
 
 
378
              memset (Hist_rgb, 0, sizeof (Hist_rgb));
399
379
 
400
380
              x3 = CLAMP ((x - n), x1, x2);
401
381
              y3 = CLAMP ((y - n), y1, y2);
403
383
              y4 = CLAMP ((y + n + 1), y1, y2);
404
384
 
405
385
              src_row = src_buf + ((y3 - y1) * width + (x3 - x1)) * bytes;
 
386
 
406
387
              for (yy = y3 ; yy < y4 ; yy++)
407
388
                {
 
389
                  y_off = ABS (yy - y) * (n + 1);
408
390
                  src = src_row;
 
391
 
409
392
                  for (xx = x3 ; xx < x4 ; xx++)
410
393
                    {
411
 
                      if ((c = ++Hist[INTENSITY(src)]) > Cnt)
 
394
                      if (mask_shape[y_off + ABS(xx - x)])
412
395
                        {
413
 
                          Cnt = c;
414
 
                          selected_src = src;
 
396
                          if (use_inten_alg)
 
397
                            {
 
398
                              c = INTENSITY(src);
 
399
                              ++Hist[c];
 
400
                              for (b = 0; b < bytes; b++)
 
401
                                Hist_rgb[b][c] += src[b];
 
402
                            }
 
403
                          else
 
404
                            {
 
405
                              for (b = 0; b < bytes; b++)
 
406
                                ++Hist_rgb[b][src[b]];
 
407
                            }
415
408
                        }
416
409
 
417
410
                      src += bytes;
419
412
 
420
413
                  src_row += width * bytes;
421
414
                }
422
 
              memcpy (dest, selected_src, bytes);
 
415
 
 
416
              if (use_inten_alg)
 
417
                {
 
418
                  weighted_average_color (Hist, Hist_rgb, dest, bytes);
 
419
                }
 
420
              else
 
421
                {
 
422
                  for (b = 0; b < bytes; b++)
 
423
                    dest[b] = weighted_average_value (Hist_rgb[b]);
 
424
                }
 
425
 
423
426
              dest += bytes;
424
427
            }
425
428
 
440
443
    }
441
444
 
442
445
  g_free (src_buf);
 
446
  g_free (mask_shape);
443
447
 
444
448
  if (!preview)
445
449
    {
450
454
    }
451
455
}
452
456
 
453
 
static void
454
 
oilify (GimpDrawable *drawable,
455
 
        GimpPreview  *preview)
456
 
{
457
 
  if (gimp_drawable_is_rgb (drawable->drawable_id) &&
458
 
      (ovals.mode == MODE_INTEN))
459
 
    oilify_intensity (drawable, preview);
460
 
  else
461
 
    oilify_rgb (drawable, preview);
462
 
}
463
 
 
464
457
static gint
465
458
oilify_dialog (GimpDrawable *drawable)
466
459
{
472
465
  GtkObject *adj;
473
466
  gboolean   run;
474
467
 
475
 
  gimp_ui_init ("oilify", FALSE);
 
468
  gimp_ui_init (PLUG_IN_BINARY, FALSE);
476
469
 
477
 
  dialog = gimp_dialog_new (_("Oilify"), "oilify",
 
470
  dialog = gimp_dialog_new (_("Oilify"), PLUG_IN_BINARY,
478
471
                            NULL, 0,
479
 
                            gimp_standard_help_func, "plug-in-oilify",
 
472
                            gimp_standard_help_func, PLUG_IN_PROC,
480
473
 
481
474
                            GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
482
475
                            GTK_STOCK_OK,     GTK_RESPONSE_OK,
483
476
 
484
477
                            NULL);
485
478
 
 
479
  gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
 
480
                                           GTK_RESPONSE_OK,
 
481
                                           GTK_RESPONSE_CANCEL,
 
482
                                           -1);
 
483
 
 
484
  gimp_window_set_transient (GTK_WINDOW (dialog));
 
485
 
486
486
  main_vbox = gtk_vbox_new (FALSE, 12);
487
487
  gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
488
488
  gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), main_vbox);
505
505
                              ovals.mask_size, 3.0, 50.0, 1.0, 5.0, 0,
506
506
                              TRUE, 0, 0,
507
507
                              NULL, NULL);
508
 
  g_signal_connect (adj, "value_changed",
 
508
  g_signal_connect (adj, "value-changed",
509
509
                    G_CALLBACK (gimp_double_adjustment_update),
510
510
                    &ovals.mask_size);
511
 
  g_signal_connect_swapped (adj, "value_changed",
 
511
  g_signal_connect_swapped (adj, "value-changed",
512
512
                            G_CALLBACK (gimp_preview_invalidate),
513
513
                            preview);
514
514
 
515
 
  toggle = gtk_check_button_new_with_mnemonic (_("_Use intensity algorithm"));
 
515
  toggle = gtk_check_button_new_with_mnemonic (_("_Use intensity"));
516
516
  gtk_table_attach (GTK_TABLE (table), toggle, 0, 3, 1, 2, GTK_FILL, 0, 0, 0);
517
517
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), ovals.mode);
518
518
  gtk_widget_show (toggle);