checkerboard.c 12.5 KB
Newer Older
Elliot Lee's avatar
Elliot Lee committed
1
/*
2
 * This is a plug-in for GIMP.
Elliot Lee's avatar
Elliot Lee committed
3 4 5
 *
 * Copyright (C) 1997 Brent Burton & the Edward Blevins
 *
6
 * This program is free software: you can redistribute it and/or modify
Elliot Lee's avatar
Elliot Lee committed
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation; either version 3 of the License, or
Elliot Lee's avatar
Elliot Lee committed
9 10 11 12 13 14 15 16
 * (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
17
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
Elliot Lee's avatar
Elliot Lee committed
18 19 20
 *
 */

21 22
#include "config.h"

23 24
#include <libgimp/gimp.h>
#include <libgimp/gimpui.h>
Elliot Lee's avatar
Elliot Lee committed
25

26 27
#include "libgimp/stdplugins-intl.h"

28

29 30
#define PLUG_IN_PROC      "plug-in-checkerboard"
#define PLUG_IN_BINARY    "checkerboard"
31
#define SPIN_BUTTON_WIDTH 8
32

33

Elliot Lee's avatar
Elliot Lee committed
34
/* Variables set in dialog box */
35 36
typedef struct data
{
37 38
  gboolean mode;
  gint     size;
Elliot Lee's avatar
Elliot Lee committed
39 40
} CheckVals;

41

Elliot Lee's avatar
Elliot Lee committed
42
static void      query  (void);
43
static void      run    (const gchar       *name,
44 45 46 47
                         gint               nparams,
                         const GimpParam   *param,
                         gint              *nreturn_vals,
                         GimpParam        **return_vals);
Elliot Lee's avatar
Elliot Lee committed
48

49 50
static void      do_checkerboard_pattern    (GimpDrawable *drawable,
                                             GimpPreview  *preview);
51 52
static gint      inblock                    (gint          pos,
                                             gint          size);
53

54
static gboolean  checkerboard_dialog        (gint32        image_ID,
55
                                             GimpDrawable *drawable);
56
static void      check_size_update_callback (GtkWidget    *widget);
Elliot Lee's avatar
Elliot Lee committed
57

58

59
const GimpPlugInInfo PLUG_IN_INFO =
Elliot Lee's avatar
Elliot Lee committed
60
{
61 62 63 64
  NULL,  /* init_proc  */
  NULL,  /* quit_proc  */
  query, /* query_proc */
  run,   /* run_proc   */
Elliot Lee's avatar
Elliot Lee committed
65 66 67 68
};

static CheckVals cvals =
{
69 70
  FALSE,     /* mode */
  10         /* size */
Elliot Lee's avatar
Elliot Lee committed
71 72 73 74 75
};

MAIN ()

static void
76
query (void)
Elliot Lee's avatar
Elliot Lee committed
77
{
78
  static const GimpParamDef args[] =
Elliot Lee's avatar
Elliot Lee committed
79
  {
80
    { GIMP_PDB_INT32,    "run-mode",   "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
81 82 83 84
    { GIMP_PDB_IMAGE,    "image",      "Input image (unused)" },
    { GIMP_PDB_DRAWABLE, "drawable",   "Input drawable"       },
    { GIMP_PDB_INT32,    "check-mode", "Check mode { REGULAR (0), PSYCHOBILY (1) }" },
    { GIMP_PDB_INT32,    "check-size", "Size of the checks"   }
Elliot Lee's avatar
Elliot Lee committed
85
  };
86

87
  gimp_install_procedure (PLUG_IN_PROC,
88
                          N_("Create a checkerboard pattern"),
89 90 91 92 93 94 95 96 97
                          "More here later",
                          "Brent Burton & the Edward Blevins",
                          "Brent Burton & the Edward Blevins",
                          "1997",
                          N_("_Checkerboard..."),
                          "RGB*, GRAY*",
                          GIMP_PLUGIN,
                          G_N_ELEMENTS (args), 0,
                          args, NULL);
98

99
  gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Filters/Render/Pattern");
Elliot Lee's avatar
Elliot Lee committed
100 101 102
}

static void
103 104 105 106 107
run (const gchar      *name,
     gint              nparams,
     const GimpParam  *param,
     gint             *nreturn_vals,
     GimpParam       **return_vals)
Elliot Lee's avatar
Elliot Lee committed
108
{
109 110
  static GimpParam   values[1];
  GimpDrawable      *drawable;
111
  gint32             image_ID;
112
  GimpRunMode        run_mode;
113
  GimpPDBStatusType  status = GIMP_PDB_SUCCESS;
Elliot Lee's avatar
Elliot Lee committed
114

115
  INIT_I18N ();
116

Elliot Lee's avatar
Elliot Lee committed
117
  run_mode = param[0].data.d_int32;
118
  image_ID = param[1].data.d_int32;
Elliot Lee's avatar
Elliot Lee committed
119 120

  *nreturn_vals = 1;
121
  *return_vals  = values;
Elliot Lee's avatar
Elliot Lee committed
122

123
  values[0].type          = GIMP_PDB_STATUS;
Elliot Lee's avatar
Elliot Lee committed
124 125 126 127 128 129
  values[0].data.d_status = status;

  drawable = gimp_drawable_get (param[2].data.d_drawable);

  switch (run_mode)
    {
Sven Neumann's avatar
Sven Neumann committed
130
    case GIMP_RUN_INTERACTIVE:
131
      gimp_get_data (PLUG_IN_PROC, &cvals);
132
      if (! checkerboard_dialog (image_ID, drawable))
133 134 135 136
        {
          gimp_drawable_detach (drawable);
          return;
        }
Elliot Lee's avatar
Elliot Lee committed
137 138
      break;

Sven Neumann's avatar
Sven Neumann committed
139
    case GIMP_RUN_NONINTERACTIVE:
Elliot Lee's avatar
Elliot Lee committed
140
      if (nparams != 5)
141
        status = GIMP_PDB_CALLING_ERROR;
Sven Neumann's avatar
Sven Neumann committed
142
      if (status == GIMP_PDB_SUCCESS)
143 144 145 146
        {
          cvals.mode = param[3].data.d_int32;
          cvals.size = param[4].data.d_int32;
        }
Elliot Lee's avatar
Elliot Lee committed
147 148
      break;

Sven Neumann's avatar
Sven Neumann committed
149
    case GIMP_RUN_WITH_LAST_VALS:
150
      gimp_get_data (PLUG_IN_PROC, &cvals);
Elliot Lee's avatar
Elliot Lee committed
151 152 153 154 155 156
      break;

    default:
      break;
    }

157 158
  if (gimp_drawable_is_rgb (drawable->drawable_id) ||
      gimp_drawable_is_gray (drawable->drawable_id))
Elliot Lee's avatar
Elliot Lee committed
159
    {
160
      gimp_progress_init (_("Adding checkerboard"));
Elliot Lee's avatar
Elliot Lee committed
161

162
      do_checkerboard_pattern (drawable, NULL);
Elliot Lee's avatar
Elliot Lee committed
163

Sven Neumann's avatar
Sven Neumann committed
164
      if (run_mode != GIMP_RUN_NONINTERACTIVE)
165
        gimp_displays_flush ();
Elliot Lee's avatar
Elliot Lee committed
166

Sven Neumann's avatar
Sven Neumann committed
167
      if (run_mode == GIMP_RUN_INTERACTIVE)
168
        gimp_set_data (PLUG_IN_PROC, &cvals, sizeof (CheckVals));
Elliot Lee's avatar
Elliot Lee committed
169 170 171
    }
  else
    {
Sven Neumann's avatar
Sven Neumann committed
172
      status = GIMP_PDB_EXECUTION_ERROR;
Elliot Lee's avatar
Elliot Lee committed
173 174 175 176 177 178 179
    }

  values[0].data.d_status = status;

  gimp_drawable_detach (drawable);
}

180 181
typedef struct
{
182 183 184
  guchar fg[4];
  guchar bg[4];
} CheckerboardParam_t;
Elliot Lee's avatar
Elliot Lee committed
185 186

static void
187 188 189 190 191
checkerboard_func (gint      x,
                   gint      y,
                   guchar   *dest,
                   gint      bpp,
                   gpointer  data)
Elliot Lee's avatar
Elliot Lee committed
192
{
193
  CheckerboardParam_t *param = (CheckerboardParam_t*) data;
194

195 196
  gint val, xp, yp;
  gint b;
197

198 199 200 201 202 203 204 205 206 207 208 209 210
  if (cvals.mode)
    {
      /* Psychobilly Mode */
      val = (inblock (x, cvals.size) != inblock (y, cvals.size));
    }
  else
    {
      /* Normal, regular checkerboard mode.
       * Determine base factor (even or odd) of block
       * this x/y position is in.
       */
      xp = x / cvals.size;
      yp = y / cvals.size;
211

212 213
      /* if both even or odd, color sqr */
      val = ( (xp & 1) != (yp & 1) );
Elliot Lee's avatar
Elliot Lee committed
214
    }
215

216 217 218
  for (b = 0; b < bpp; b++)
    dest[b] = val ? param->fg[b] : param->bg[b];
}
Elliot Lee's avatar
Elliot Lee committed
219

220
static void
221 222
do_checkerboard_pattern (GimpDrawable *drawable,
                         GimpPreview  *preview)
223
{
224 225 226
  CheckerboardParam_t  param;
  GimpRgnIterator     *iter;
  GimpRGB              color;
227

228
  gimp_context_get_background (&color);
229 230
  gimp_drawable_get_color_uchar (drawable->drawable_id, &color, param.bg);

231
  gimp_context_get_foreground (&color);
232
  gimp_drawable_get_color_uchar (drawable->drawable_id, &color, param.fg);
233

234 235 236 237 238 239
  if (cvals.size < 1)
    {
      /* make size 1 to prevent division by zero */
      cvals.size = 1;
    }

240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268
  if (preview)
    {
      gint    x1, y1;
      gint    width, height;
      gint    i;
      gint    bpp;
      guchar *buffer;

      gimp_preview_get_position (preview, &x1, &y1);
      gimp_preview_get_size (preview, &width, &height);
      bpp = drawable->bpp;
      buffer = g_new (guchar, width * height * bpp);

      for (i = 0; i < width * height; i++)
        {
          checkerboard_func (x1 + i % width,
                             y1 + i / width,
                             buffer + i * bpp,
                             bpp, &param);
        }
      gimp_preview_draw_buffer (preview, buffer, width * bpp);
      g_free (buffer);
    }
  else
    {
      iter = gimp_rgn_iterator_new (drawable, 0);
      gimp_rgn_iterator_dest (iter, checkerboard_func, &param);
      gimp_rgn_iterator_free (iter);
    }
Elliot Lee's avatar
Elliot Lee committed
269 270
}

271
static gint
272
inblock (gint pos,
273
         gint size)
Elliot Lee's avatar
Elliot Lee committed
274
{
275 276
  static gint *in  = NULL;       /* initialized first time */
  static gint  len = -1;
277 278 279 280 281

  /* avoid a FP exception */
  if (size == 1)
    size = 2;

282 283 284 285 286 287
  if (in && len != size * size)
    {
      g_free (in);
      in = NULL;
    }
  len = size * size;
288 289 290 291 292 293
  /*
   * Initialize the array; since we'll be called thousands of
   * times with the same size value, precompute the array.
   */
  if (in == NULL)
    {
294
      gint cell = 1;    /* cell value */
295 296
      gint i, j, k;

297
      in = g_new (gint, len);
298 299 300 301 302 303

      /*
       * i is absolute index into in[]
       * j is current number of blocks to fill in with a 1 or 0.
       * k is just counter for the j cells.
       */
304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320
      i = 0;
      for (j = 1; j <= size; j++)
        { /* first half */
          for (k = 0; k < j; k++)
            {
              in[i++] = cell;
            }
          cell = !cell;
        }
      for (j = size - 1; j >= 1; j--)
        { /* second half */
          for (k = 0; k < j; k++)
            {
              in[i++] = cell;
            }
          cell = !cell;
        }
321
    }
Elliot Lee's avatar
Elliot Lee committed
322

323
  /* place pos within 0..(len-1) grid and return the value. */
324
  return in[pos % (len - 1)];
Elliot Lee's avatar
Elliot Lee committed
325 326
}

327
static gboolean
328 329
checkerboard_dialog (gint32        image_ID,
                     GimpDrawable *drawable)
Elliot Lee's avatar
Elliot Lee committed
330
{
331
  GtkWidget *dialog;
332
  GtkWidget *vbox;
333
  GtkWidget *hbox;
334
  GtkWidget *preview;
Elliot Lee's avatar
Elliot Lee committed
335
  GtkWidget *toggle;
336
  GtkWidget *size_entry;
337
  gint       size, width, height;
338 339 340
  GimpUnit   unit;
  gdouble    xres;
  gdouble    yres;
341
  gboolean   run;
Elliot Lee's avatar
Elliot Lee committed
342

343
  gimp_ui_init (PLUG_IN_BINARY, FALSE);
Elliot Lee's avatar
Elliot Lee committed
344

345
  dialog = gimp_dialog_new (_("Checkerboard"), PLUG_IN_BINARY,
346
                            NULL, 0,
347
                            gimp_standard_help_func, PLUG_IN_PROC,
348

349 350
                            GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
                            GTK_STOCK_OK,     GTK_RESPONSE_OK,
351

352
                            NULL);
353

354 355 356 357
  gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
                                           GTK_RESPONSE_OK,
                                           GTK_RESPONSE_CANCEL,
                                           -1);
358

359
  gimp_window_set_transient (GTK_WINDOW (dialog));
360

361 362
  vbox = gtk_vbox_new (FALSE, 12);
  gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
363 364
  gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
                      vbox, TRUE, TRUE, 0);
365
  gtk_widget_show (vbox);
Elliot Lee's avatar
Elliot Lee committed
366

367
  preview = gimp_drawable_preview_new (drawable, NULL);
368
  gtk_box_pack_start (GTK_BOX (vbox), preview, TRUE, TRUE, 0);
369 370 371 372 373
  gtk_widget_show (preview);
  g_signal_connect_swapped (preview, "invalidated",
                            G_CALLBACK (do_checkerboard_pattern),
                            drawable);

374 375 376
  hbox = gtk_hbox_new (FALSE, 0);
  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
  gtk_widget_show (hbox);
377 378 379 380 381 382 383 384

  /*  Get the image resolution and unit  */
  gimp_image_get_resolution (image_ID, &xres, &yres);
  unit = gimp_image_get_unit (image_ID);

  width  = gimp_drawable_width (drawable->drawable_id);
  height = gimp_drawable_height (drawable->drawable_id);
  size   = MIN (width, height);
385

386 387 388 389 390
  size_entry = gimp_size_entry_new (1, unit, "%a",
                                    TRUE, TRUE, FALSE, SPIN_BUTTON_WIDTH,
                                    GIMP_SIZE_ENTRY_UPDATE_SIZE);
  gtk_table_set_col_spacing (GTK_TABLE (size_entry), 0, 4);
  gtk_table_set_col_spacing (GTK_TABLE (size_entry), 1, 4);
391 392
  gtk_box_pack_start (GTK_BOX (hbox), size_entry, FALSE, FALSE, 0);
  gtk_widget_show (size_entry);
393 394 395 396 397 398 399 400

  /*  set the unit back to pixels, since most times we will want pixels */
  gimp_size_entry_set_unit (GIMP_SIZE_ENTRY (size_entry), GIMP_UNIT_PIXEL);

  /*  set the resolution to the image resolution  */
  gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (size_entry), 0, xres, TRUE);

  /*  set the size (in pixels) that will be treated as 0% and 100%  */
401
  gimp_size_entry_set_size (GIMP_SIZE_ENTRY (size_entry), 0, 0.0, size);
402 403

  /*  set upper and lower limits (in pixels)  */
404 405
  gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (size_entry), 0,
                                         1.0, size);
406 407

  /*  initialize the values  */
408
  gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (size_entry), 0, cvals.size);
409 410

  /*  attach labels  */
411 412
  gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (size_entry),
                                _("_Size:"), 1, 0, 0.0);
413

414
  g_signal_connect (size_entry, "value-changed",
415
                    G_CALLBACK (check_size_update_callback),
416
                    &cvals.size);
417 418 419
  g_signal_connect_swapped (size_entry, "value-changed",
                            G_CALLBACK (gimp_preview_invalidate),
                            preview);
420

421 422 423 424 425 426 427 428 429 430 431
  toggle = gtk_check_button_new_with_mnemonic (_("_Psychobilly"));
  gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), cvals.mode);
  gtk_widget_show (toggle);

  g_signal_connect (toggle, "toggled",
                    G_CALLBACK (gimp_toggle_button_update),
                    &cvals.mode);
  g_signal_connect_swapped (toggle, "toggled",
                            G_CALLBACK (gimp_preview_invalidate),
                            preview);
432

433
  gtk_widget_show (dialog);
Elliot Lee's avatar
Elliot Lee committed
434

435
  run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
Elliot Lee's avatar
Elliot Lee committed
436

437
  gtk_widget_destroy (dialog);
Elliot Lee's avatar
Elliot Lee committed
438

439
  return run;
440 441
}

Elliot Lee's avatar
Elliot Lee committed
442
static void
443
check_size_update_callback (GtkWidget *widget)
Elliot Lee's avatar
Elliot Lee committed
444
{
445
  cvals.size = gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (widget), 0);
Elliot Lee's avatar
Elliot Lee committed
446
}