engrave.c 16.6 KB
Newer Older
Elliot Lee's avatar
Elliot Lee committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/* The GIMP -- an image manipulation program
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
 * Copyright (C) 1997 Eiichi Takamori
 * Copyright (C) 1996, 1997 Torsten Martinsen
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
18
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Elliot Lee's avatar
Elliot Lee committed
19 20
 */

21 22
#include "config.h"

23 24
#include <libgimp/gimp.h>
#include <libgimp/gimpui.h>
25

26
#include "libgimp/stdplugins-intl.h"
Elliot Lee's avatar
Elliot Lee committed
27

28

Elliot Lee's avatar
Elliot Lee committed
29 30
/* Some useful macros */

31 32
#define SCALE_WIDTH     125
#define TILE_CACHE_SIZE  16
Elliot Lee's avatar
Elliot Lee committed
33

34 35
typedef struct
{
36 37 38
  gint     height;
  gboolean limit;
  gboolean preview;
Elliot Lee's avatar
Elliot Lee committed
39 40
} EngraveValues;

41
static void query (void);
42
static void run   (const gchar      *name,
43 44 45 46 47
                   gint              nparams,
                   const GimpParam  *param,
                   gint             *nreturn_vals,
                   GimpParam       **return_vals);

48
static gboolean  engrave_dialog (GimpDrawable *drawable);
49

50 51
static void      engrave        (GimpDrawable *drawable,
                                 GimpPreview  *preview);
52 53

#if 0
54 55 56 57
static void      engrave_large  (GimpDrawable *drawable,
                                 gint          height,
                                 gboolean      limit,
                                 GimpPreview  *preview);
58 59
#endif

60 61 62 63 64
static void      engrave_small  (GimpDrawable *drawable,
                                 gint          height,
                                 gboolean      limit,
                                 gint          tile_width,
                                 GimpPreview  *preview);
65

66 67 68 69
static void      engrave_sub    (gint          height,
                                 gboolean      limit,
                                 gint          bpp,
                                 gint          color_n);
Elliot Lee's avatar
Elliot Lee committed
70

Sven Neumann's avatar
Sven Neumann committed
71
GimpPlugInInfo PLUG_IN_INFO =
Elliot Lee's avatar
Elliot Lee committed
72
{
73 74 75 76
  NULL,  /* init_proc  */
  NULL,  /* quit_proc  */
  query, /* query_proc */
  run,   /* run_proc   */
Elliot Lee's avatar
Elliot Lee committed
77 78 79 80
};

static EngraveValues pvals =
{
81 82 83
  10,    /* height  */
  FALSE, /* limit   */
  TRUE   /* preview */
Elliot Lee's avatar
Elliot Lee committed
84 85
};

86
MAIN ()
Elliot Lee's avatar
Elliot Lee committed
87 88

static void
89
query (void)
Elliot Lee's avatar
Elliot Lee committed
90
{
Sven Neumann's avatar
Sven Neumann committed
91
  static GimpParamDef args[] =
92
  {
Sven Neumann's avatar
Sven Neumann committed
93 94 95 96 97
    { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" },
    { GIMP_PDB_IMAGE, "image", "Input image (unused)" },
    { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
    { GIMP_PDB_INT32, "height", "Resolution in pixels" },
    { GIMP_PDB_INT32, "limit", "If true, limit line width" }
98 99 100
  };

  gimp_install_procedure ("plug_in_engrave",
101 102 103 104 105 106 107 108 109 110
                          "Engrave the contents of the specified drawable",
                          "Creates a black-and-white 'engraved' version of an image as seen in old illustrations",
                          "Spencer Kimball & Peter Mattis, Eiichi Takamori, Torsten Martinsen",
                          "Spencer Kimball & Peter Mattis, Eiichi Takamori, Torsten Martinsen",
                          "1995,1996,1997",
                          N_("En_grave..."),
                          "RGBA, GRAYA",
                          GIMP_PLUGIN,
                          G_N_ELEMENTS (args), 0,
                          args, NULL);
111

112
  gimp_plugin_menu_register ("plug_in_engrave", "<Image>/Filters/Distorts");
Elliot Lee's avatar
Elliot Lee committed
113 114 115
}

static void
116 117 118 119 120
run (const gchar      *name,
     gint              nparams,
     const GimpParam  *param,
     gint             *nreturn_vals,
     GimpParam       **return_vals)
Elliot Lee's avatar
Elliot Lee committed
121
{
122 123 124 125
  static GimpParam   values[1];
  GimpDrawable      *drawable;
  GimpRunMode        run_mode;
  GimpPDBStatusType  status = GIMP_PDB_SUCCESS;
Elliot Lee's avatar
Elliot Lee committed
126

127
  run_mode = param[0].data.d_int32;
Elliot Lee's avatar
Elliot Lee committed
128

129 130
  INIT_I18N ();

131
  *nreturn_vals = 1;
132
  *return_vals  = values;
Elliot Lee's avatar
Elliot Lee committed
133

134
  values[0].type          = GIMP_PDB_STATUS;
135
  values[0].data.d_status = status;
Elliot Lee's avatar
Elliot Lee committed
136

137
  /*  Get the specified drawable  */
138
  drawable = gimp_drawable_get (param[2].data.d_drawable);
139
  gimp_tile_cache_ntiles (TILE_CACHE_SIZE);
Elliot Lee's avatar
Elliot Lee committed
140

141 142
  switch (run_mode)
    {
Sven Neumann's avatar
Sven Neumann committed
143
    case GIMP_RUN_INTERACTIVE:
144
      /*  Possibly retrieve data  */
145
      gimp_get_data ("plug_in_engrave", &pvals);
146 147

      /*  First acquire information with a dialog  */
148 149 150 151 152
      if (!engrave_dialog (drawable))
        {
          gimp_drawable_detach (drawable);
          return;
        }
153
      break;
Elliot Lee's avatar
Elliot Lee committed
154

Sven Neumann's avatar
Sven Neumann committed
155
    case GIMP_RUN_NONINTERACTIVE:
156 157
      /*  Make sure all the arguments are there!  */
      if (nparams != 5)
158
        status = GIMP_PDB_CALLING_ERROR;
Sven Neumann's avatar
Sven Neumann committed
159
      if (status == GIMP_PDB_SUCCESS)
160 161 162 163
        {
          pvals.height = param[3].data.d_int32;
          pvals.limit  = (param[4].data.d_int32) ? TRUE : FALSE;
        }
Sven Neumann's avatar
Sven Neumann committed
164
      if ((status == GIMP_PDB_SUCCESS) &&
165 166
          pvals.height < 0)
        status = GIMP_PDB_CALLING_ERROR;
167
      break;
Elliot Lee's avatar
Elliot Lee committed
168

Sven Neumann's avatar
Sven Neumann committed
169
    case GIMP_RUN_WITH_LAST_VALS:
170 171 172
      /*  Possibly retrieve data  */
      gimp_get_data ("plug_in_engrave", &pvals);
      break;
Elliot Lee's avatar
Elliot Lee committed
173 174

    default:
175
      break;
Elliot Lee's avatar
Elliot Lee committed
176 177
    }

Sven Neumann's avatar
Sven Neumann committed
178
  if (status == GIMP_PDB_SUCCESS)
179 180
    {
      gimp_progress_init (_("Engraving..."));
Elliot Lee's avatar
Elliot Lee committed
181

182
      engrave (drawable, NULL);
Elliot Lee's avatar
Elliot Lee committed
183

Sven Neumann's avatar
Sven Neumann committed
184
      if (run_mode != GIMP_RUN_NONINTERACTIVE)
185
        gimp_displays_flush ();
Elliot Lee's avatar
Elliot Lee committed
186

187
      /*  Store data  */
Sven Neumann's avatar
Sven Neumann committed
188
      if (run_mode == GIMP_RUN_INTERACTIVE)
189
        gimp_set_data ("plug_in_engrave", &pvals, sizeof (EngraveValues));
Elliot Lee's avatar
Elliot Lee committed
190
    }
191
  values[0].data.d_status = status;
Elliot Lee's avatar
Elliot Lee committed
192

193
  gimp_drawable_detach (drawable);
Elliot Lee's avatar
Elliot Lee committed
194 195
}

Sven Neumann's avatar
Sven Neumann committed
196
static gboolean
197
engrave_dialog (GimpDrawable *drawable)
Elliot Lee's avatar
Elliot Lee committed
198
{
199 200 201
  GtkWidget *dialog;
  GtkWidget *main_vbox;
  GtkWidget *preview;
202 203
  GtkWidget *table;
  GtkWidget *toggle;
204
  GtkObject *adj;
205
  gboolean   run;
206

207
  gimp_ui_init ("engrave", FALSE);
208

209 210 211 212 213 214
  dialog = gimp_dialog_new (_("Engrave"), "engrave",
                            NULL, 0,
                            gimp_standard_help_func, "plug-in-engrave",

                            GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
                            GTK_STOCK_OK,     GTK_RESPONSE_OK,
215

216
                            NULL);
217

218 219 220 221 222
  gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
                                              GTK_RESPONSE_OK,
                                              GTK_RESPONSE_CANCEL,
                                              -1);

223 224 225 226
  main_vbox = gtk_vbox_new (FALSE, 12);
  gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
  gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), main_vbox);
  gtk_widget_show (main_vbox);
227

228 229 230 231 232 233
  preview = gimp_drawable_preview_new (drawable, &pvals.preview);
  gtk_box_pack_start_defaults (GTK_BOX (main_vbox), preview);
  gtk_widget_show (preview);
  g_signal_connect_swapped (preview, "invalidated",
                            G_CALLBACK (engrave),
                            drawable);
234

Sven Neumann's avatar
Sven Neumann committed
235 236
  table = gtk_table_new (1, 3, FALSE);
  gtk_table_set_col_spacings (GTK_TABLE (table), 6);
237
  gtk_box_pack_start (GTK_BOX (main_vbox), table, FALSE, FALSE, 0);
Sven Neumann's avatar
Sven Neumann committed
238
  gtk_widget_show (table);
239

Sven Neumann's avatar
Sven Neumann committed
240
  adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
241 242 243 244
                              _("_Height:"), SCALE_WIDTH, 0,
                              pvals.height, 2.0, 16.0, 1.0, 4.0, 0,
                              TRUE, 0, 0,
                              NULL, NULL);
245
  g_signal_connect (adj, "value_changed",
246 247
                    G_CALLBACK (gimp_int_adjustment_update),
                    &pvals.height);
248 249 250
  g_signal_connect_swapped (adj, "value_changed",
                            G_CALLBACK (gimp_preview_invalidate),
                            preview);
251

252
  toggle = gtk_check_button_new_with_mnemonic (_("_Limit line width"));
253
  gtk_box_pack_start (GTK_BOX (main_vbox), toggle, FALSE, FALSE, 0);
Sven Neumann's avatar
Sven Neumann committed
254 255 256 257 258 259
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), pvals.limit);
  gtk_widget_show (toggle);

  g_signal_connect (toggle, "toggled",
                    G_CALLBACK (gimp_toggle_button_update),
                    &pvals.limit);
260 261 262
  g_signal_connect_swapped (toggle, "toggled",
                            G_CALLBACK (gimp_preview_invalidate),
                            preview);
Sven Neumann's avatar
Sven Neumann committed
263

264
  gtk_widget_show (dialog);
265

266
  run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
267

268
  gtk_widget_destroy (dialog);
269 270

  return run;
Elliot Lee's avatar
Elliot Lee committed
271 272 273 274 275
}

/*  Engrave interface functions  */

static void
276 277
engrave (GimpDrawable *drawable,
         GimpPreview  *preview)
Elliot Lee's avatar
Elliot Lee committed
278
{
279 280 281
  gint     tile_width;
  gint     height;
  gboolean limit;
282 283

  tile_width = gimp_tile_width();
284 285
  height = pvals.height;
  limit = pvals.limit;
286 287 288
  /* [DindinX] this test is always false since
   * tile_width == 64 and height <= 16 */
#if 0
289
  if (height >= tile_width)
290
    engrave_large (drawable, height, limit, preview);
291
  else
292 293
#endif
    engrave_small (drawable, height, limit, tile_width, preview);
Elliot Lee's avatar
Elliot Lee committed
294 295
}

296
#if 0
Elliot Lee's avatar
Elliot Lee committed
297
static void
298 299 300 301
engrave_large (GimpDrawable *drawable,
               gint          height,
               gboolean      limit,
               GimpPreview  *preview)
Elliot Lee's avatar
Elliot Lee committed
302
{
303 304 305 306 307 308 309 310 311 312
  GimpPixelRgn  src_rgn, dest_rgn;
  guchar       *src_row, *dest_row;
  guchar       *src, *dest;
  gulong       *average;
  gint          row, col, b, bpp;
  gint          x, y, y_step, inten, v;
  gulong        count;
  gint          x1, y1, x2, y2;
  gint          progress, max_progress;
  gpointer      pr;
313

314
  gimp_drawable_mask_bounds (drawable->drawable_id, &x1, &y1, &x2, &y2);
315

316 317
  bpp = (gimp_drawable_is_rgb (drawable->drawable_id)) ? 3 : 1;
  average = g_new (gulong, bpp);
318 319 320 321 322 323 324 325

  /* Initialize progress */
  progress = 0;
  max_progress = 2 * (x2 - x1) * (y2 - y1);

  for (y = y1; y < y2; y += height - (y % height))
    {
      for (x = x1; x < x2; ++x)
326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365
        {
          y_step = height - (y % height);
          y_step = MIN (y_step, x2 - x);

          gimp_pixel_rgn_init (&src_rgn, drawable, x, y, 1, y_step,
                               FALSE, FALSE);
          for (b = 0; b < bpp; b++)
            average[b] = 0;
          count = 0;

          for (pr = gimp_pixel_rgns_register (1, &src_rgn);
               pr != NULL;
               pr = gimp_pixel_rgns_process(pr))
            {
              src_row = src_rgn.data;
              for (row = 0; row < src_rgn.h; row++)
                {
                  src = src_row;
                  for (col = 0; col < src_rgn.w; col++)
                    {
                      for (b = 0; b < bpp; b++)
                        average[b] += src[b];
                      src += src_rgn.bpp;
                      count += 1;
                    }
                  src_row += src_rgn.rowstride;
                }
              /* Update progress */
              progress += src_rgn.w * src_rgn.h;
              gimp_progress_update ((double) progress / (double) max_progress);
            }

          if (count > 0)
            for (b = 0; b < bpp; b++)
              average[b] = (guchar) (average[b] / count);

          if (bpp < 3)
            inten = average[0] / 254.0 * height;
          else
            inten = GIMP_RGB_INTENSITY (average[0],
366 367
                                        average[1],
                                        average[2]) / 254.0 * height;
368

369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395
          gimp_pixel_rgn_init (&dest_rgn,
                               drawable, x, y, 1, y_step, TRUE, TRUE);
          for (pr = gimp_pixel_rgns_register (1, &dest_rgn);
               pr != NULL;
               pr = gimp_pixel_rgns_process(pr))
            {
              dest_row = dest_rgn.data;
              for (row = 0; row < dest_rgn.h; row++)
                {
                  dest = dest_row;
                  v = inten > row ? 255 : 0;
                  if (limit)
                    {
                      if (row == 0)
                        v = 255;
                      else if (row == height-1)
                        v = 0;
                    }
                  for (b = 0; b < bpp; b++)
                    dest[b] = v;
                  dest_row += dest_rgn.rowstride;
                }
              /* Update progress */
              progress += dest_rgn.w * dest_rgn.h;
              gimp_progress_update((double) progress / (double) max_progress);
            }
        }
Elliot Lee's avatar
Elliot Lee committed
396 397
    }

398
  g_free (average);
Elliot Lee's avatar
Elliot Lee committed
399

400
  /*  update the engraved region  */
401 402 403
  gimp_drawable_flush( drawable);
  gimp_drawable_merge_shadow (drawable->drawable_id, TRUE);
  gimp_drawable_update (drawable->drawable_id, x1, y1, x2 - x1, y2 - y1);
Elliot Lee's avatar
Elliot Lee committed
404
}
405
#endif
Elliot Lee's avatar
Elliot Lee committed
406

407 408 409 410 411
typedef struct
{
  gint    x, y, h;
  gint    width;
  guchar *data;
Elliot Lee's avatar
Elliot Lee committed
412 413 414 415 416
} PixelArea;

PixelArea area;

static void
417 418 419 420 421
engrave_small (GimpDrawable *drawable,
               gint          line_height,
               gboolean      limit,
               gint          tile_width,
               GimpPreview  *preview)
Elliot Lee's avatar
Elliot Lee committed
422
{
Sven Neumann's avatar
Sven Neumann committed
423
  GimpPixelRgn src_rgn, dest_rgn;
424 425 426 427
  gint         bpp, color_n;
  gint         x1, y1, x2, y2;
  gint         width, height;
  gint         progress, max_progress;
428 429 430 431 432

  /*
    For speed efficiency, operates on PixelAreas, whose each width and
    height are less than tile size.

433 434
    If both ends of area cannot be divided by line_height ( as
    x1%line_height != 0 etc.), operates on the remainder pixels.
435 436
  */

437 438
  if (preview)
    {
439 440
      gimp_preview_get_position (preview, &x1, &y1);
      gimp_preview_get_size (preview, &width, &height);
441 442 443 444 445 446 447 448 449 450 451

      x2 = x1 + width;
      y2 = y1 + height;
    }
  else
    {
      gimp_drawable_mask_bounds (drawable->drawable_id, &x1, &y1, &x2, &y2);

      width  = x2 - x1;
      height = y2 - y1;
    }
452
  gimp_pixel_rgn_init (&src_rgn, drawable,
453
                       x1, y1, width, height, FALSE, FALSE);
454
  gimp_pixel_rgn_init (&dest_rgn, drawable,
455
                       x1, y1, width, height, (preview == NULL), TRUE);
456 457 458

  /* Initialize progress */
  progress = 0;
459
  max_progress = width * height;
460 461

  bpp = drawable->bpp;
462
  color_n = (gimp_drawable_is_rgb (drawable->drawable_id)) ? 3 : 1;
Elliot Lee's avatar
Elliot Lee committed
463

464
  area.width = (tile_width / line_height) * line_height;
465
  area.data = g_new(guchar, (glong) bpp * area.width * area.width);
Elliot Lee's avatar
Elliot Lee committed
466

467 468 469 470 471 472
  for (area.y = y1; area.y < y2;
       area.y += area.width - (area.y % area.width))
    {
      area.h = area.width - (area.y % area.width);
      area.h = MIN(area.h, y2 - area.y);
      for (area.x = x1; area.x < x2; ++area.x)
473 474 475 476 477 478 479 480 481 482 483 484 485 486 487
        {
          gimp_pixel_rgn_get_rect (&src_rgn, area.data, area.x, area.y, 1,
                                   area.h);

          engrave_sub (line_height, limit, bpp, color_n);

          gimp_pixel_rgn_set_rect (&dest_rgn, area.data,
                                   area.x, area.y, 1, area.h);
        }
      if (!preview)
        {
          /* Update progress */
          progress += area.h * width;
          gimp_progress_update ((double) progress / (double) max_progress);
        }
Elliot Lee's avatar
Elliot Lee committed
488 489
    }

490
  g_free(area.data);
Elliot Lee's avatar
Elliot Lee committed
491

492
  /*  update the engraved region  */
493 494
  if (preview)
    {
495 496
      gimp_drawable_preview_draw_region (GIMP_DRAWABLE_PREVIEW (preview),
                                         &dest_rgn);
497 498 499 500 501 502 503
    }
  else
    {
      gimp_drawable_flush (drawable);
      gimp_drawable_merge_shadow (drawable->drawable_id, TRUE);
      gimp_drawable_update (drawable->drawable_id, x1, y1, x2 - x1, y2 - y1);
    }
Elliot Lee's avatar
Elliot Lee committed
504 505 506
}

static void
507
engrave_sub (gint height,
508 509 510
             gint limit,
             gint bpp,
             gint color_n)
Elliot Lee's avatar
Elliot Lee committed
511
{
512
  glong average[3];             /* color_n <= 3 */
513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532
  gint y, h, inten, v;
  guchar *buf_row, *buf;
  gint row;
  gint rowstride;
  gint count;
  gint i;

  /*
    Since there's so many nested FOR's,
    put a few of them here...
  */

  rowstride = bpp;

  for (y = area.y; y < area.y + area.h; y += height - (y % height))
    {
      h = height - (y % height);
      h = MIN(h, area.y + area.h - y);

      for (i = 0; i < color_n; i++)
533
        average[i] = 0;
534 535 536 537 538 539
      count = 0;

      /* Read */
      buf_row = area.data + (y - area.y) * rowstride;

      for (row = 0; row < h; row++)
540 541 542 543 544 545 546
        {
          buf = buf_row;
          for (i = 0; i < color_n; i++)
            average[i] += buf[i];
          count++;
          buf_row += rowstride;
        }
Elliot Lee's avatar
Elliot Lee committed
547

548
      /* Average */
549
      if (count > 0)
550 551
        for (i = 0; i < color_n; i++)
          average[i] /= count;
552 553

      if (bpp < 3)
554
        inten = average[0] / 254.0 * height;
555
      else
556
        inten = GIMP_RGB_INTENSITY (average[0],
557 558
                                    average[1],
                                    average[2]) / 254.0 * height;
559 560 561 562 563

      /* Write */
      buf_row = area.data + (y - area.y) * rowstride;

      for (row = 0; row < h; row++)
564 565 566 567 568 569 570 571 572 573 574 575 576 577
        {
          buf = buf_row;
          v = inten > row ? 255 : 0;
          if (limit)
            {
              if (row == 0)
                v = 255;
              else if (row == height-1)
                v = 0;
            }
          for (i = 0; i < color_n; i++)
            buf[i] = v;
          buf_row += rowstride;
        }
Elliot Lee's avatar
Elliot Lee committed
578 579
    }
}