/* * This is a plug-in for PIKA. * * Copyright (C) 1997 Brent Burton & the Edward Blevins * * 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 3 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, see . * */ #include "config.h" #include #include #include "libpika/stdplugins-intl.h" #define PLUG_IN_PROC "plug-in-checkerboard" #define PLUG_IN_BINARY "checkerboard" #define PLUG_IN_ROLE "pika-checkerboard" typedef struct _Checkerboard Checkerboard; typedef struct _CheckerboardClass CheckerboardClass; struct _Checkerboard { PikaPlugIn parent_instance; }; struct _CheckerboardClass { PikaPlugInClass parent_class; }; #define CHECKERBOARD_TYPE (checkerboard_get_type ()) #define CHECKERBOARD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CHECKERBOARD_TYPE, Checkerboard)) GType checkerboard_get_type (void) G_GNUC_CONST; static GList * checkerboard_query_procedures (PikaPlugIn *plug_in); static PikaProcedure * checkerboard_create_procedure (PikaPlugIn *plug_in, const gchar *name); static PikaValueArray * checkerboard_run (PikaProcedure *procedure, PikaRunMode run_mode, PikaImage *image, gint n_drawables, PikaDrawable **drawables, PikaProcedureConfig *config, gpointer run_data); static void do_checkerboard_pattern (GObject *config, PikaDrawable *drawable, PikaPreview *preview); static void do_checkerboard_preview (GtkWidget *widget, GObject *config); static gint inblock (gint pos, gint size); static gboolean checkerboard_dialog (PikaProcedure *procedure, GObject *config, PikaImage *image, PikaDrawable *drawable); G_DEFINE_TYPE (Checkerboard, checkerboard, PIKA_TYPE_PLUG_IN) PIKA_MAIN (CHECKERBOARD_TYPE) DEFINE_STD_SET_I18N static void checkerboard_class_init (CheckerboardClass *klass) { PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass); plug_in_class->query_procedures = checkerboard_query_procedures; plug_in_class->create_procedure = checkerboard_create_procedure; plug_in_class->set_i18n = STD_SET_I18N; } static void checkerboard_init (Checkerboard *checkerboard) { } static GList * checkerboard_query_procedures (PikaPlugIn *plug_in) { return g_list_append (NULL, g_strdup (PLUG_IN_PROC)); } static PikaProcedure * checkerboard_create_procedure (PikaPlugIn *plug_in, const gchar *name) { PikaProcedure *procedure = NULL; if (! strcmp (name, PLUG_IN_PROC)) { procedure = pika_image_procedure_new (plug_in, name, PIKA_PDB_PROC_TYPE_PLUGIN, checkerboard_run, NULL, NULL); pika_procedure_set_image_types (procedure, "RGB*, GRAY*"); pika_procedure_set_sensitivity_mask (procedure, PIKA_PROCEDURE_SENSITIVE_DRAWABLE); pika_procedure_set_menu_label (procedure, _("_Checkerboard (legacy)...")); pika_procedure_add_menu_path (procedure, "/Filters/Render/Pattern"); pika_procedure_set_documentation (procedure, _("Create a checkerboard pattern"), "More here later", name); pika_procedure_set_attribution (procedure, "Brent Burton & the Edward Blevins", "Brent Burton & the Edward Blevins", "1997"); PIKA_PROC_ARG_BOOLEAN (procedure, "psychobily", _("_Psychobilly"), _("Render a psychobilly checkerboard"), FALSE, G_PARAM_READWRITE); PIKA_PROC_ARG_INT (procedure, "check-size", _("_Size"), _("Size of the checks"), 1, PIKA_MAX_IMAGE_SIZE, 10, G_PARAM_READWRITE); PIKA_PROC_AUX_ARG_UNIT (procedure, "check-size-unit", _("Check size unit of measure"), _("Check size unit of measure"), TRUE, TRUE, PIKA_UNIT_PIXEL, PIKA_PARAM_READWRITE); } return procedure; } static PikaValueArray * checkerboard_run (PikaProcedure *procedure, PikaRunMode run_mode, PikaImage *image, gint n_drawables, PikaDrawable **drawables, PikaProcedureConfig *config, gpointer run_data) { PikaDrawable *drawable; gegl_init (NULL, NULL); if (n_drawables != 1) { GError *error = NULL; g_set_error (&error, PIKA_PLUG_IN_ERROR, 0, _("Procedure '%s' only works with one drawable."), PLUG_IN_PROC); return pika_procedure_new_return_values (procedure, PIKA_PDB_CALLING_ERROR, error); } else { drawable = drawables[0]; } if (run_mode == PIKA_RUN_INTERACTIVE && ! checkerboard_dialog (procedure, G_OBJECT (config), image, drawable)) return pika_procedure_new_return_values (procedure, PIKA_PDB_CANCEL, NULL); if (pika_drawable_is_rgb (drawable) || pika_drawable_is_gray (drawable)) { do_checkerboard_pattern (G_OBJECT (config), drawable, NULL); if (run_mode != PIKA_RUN_NONINTERACTIVE) pika_displays_flush (); } else { return pika_procedure_new_return_values (procedure, PIKA_PDB_EXECUTION_ERROR, NULL); } return pika_procedure_new_return_values (procedure, PIKA_PDB_SUCCESS, NULL); } typedef struct { guchar fg[4]; guchar bg[4]; } CheckerboardParam_t; static void checkerboard_func (gint x, gint y, guchar *dest, gint bpp, gint size, gboolean mode, gpointer data) { CheckerboardParam_t *param = (CheckerboardParam_t*) data; gint val, xp, yp; gint b; if (mode) { /* Psychobilly Mode */ val = (inblock (x, size) != inblock (y, size)); } else { /* Normal, regular checkerboard mode. * Determine base factor (even or odd) of block * this x/y position is in. */ xp = x / size; yp = y / size; /* if both even or odd, color sqr */ val = ( (xp & 1) != (yp & 1) ); } for (b = 0; b < bpp; b++) dest[b] = val ? param->fg[b] : param->bg[b]; } static void do_checkerboard_pattern (GObject *config, PikaDrawable *drawable, PikaPreview *preview) { CheckerboardParam_t param; PikaRGB fg, bg; const Babl *format; gint bpp; gboolean mode = FALSE; gint size = 10; if (config) g_object_get (config, "check-size", &size, "psychobily", &mode, NULL); pika_context_get_background (&bg); pika_context_get_foreground (&fg); if (pika_drawable_is_gray (drawable)) { param.bg[0] = pika_rgb_luminance_uchar (&bg); pika_rgba_get_uchar (&bg, NULL, NULL, NULL, param.bg + 1); param.fg[0] = pika_rgb_luminance_uchar (&fg); pika_rgba_get_uchar (&fg, NULL, NULL, NULL, param.fg + 3); if (pika_drawable_has_alpha (drawable)) format = babl_format ("R'G'B'A u8"); else format = babl_format ("R'G'B' u8"); } else { pika_rgba_get_uchar (&bg, param.bg, param.bg + 1, param.bg + 2, param.bg + 1); pika_rgba_get_uchar (&fg, param.fg, param.fg + 1, param.fg + 2, param.fg + 3); if (pika_drawable_has_alpha (drawable)) format = babl_format ("Y'A u8"); else format = babl_format ("Y' u8"); } bpp = babl_format_get_bytes_per_pixel (format); if (size < 1) { /* make size 1 to prevent division by zero */ size = 1; } if (preview) { gint x1, y1; gint width, height; gint i; guchar *buffer; pika_preview_get_position (preview, &x1, &y1); pika_preview_get_size (preview, &width, &height); bpp = pika_drawable_get_bpp (drawable); 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, size, mode, ¶m); } pika_preview_draw_buffer (preview, buffer, width * bpp); g_free (buffer); } else { GeglBuffer *buffer; GeglBufferIterator *iter; gint x, y, w, h; gint progress_total; gint progress_done = 0; if (! pika_drawable_mask_intersect (drawable, &x, &y, &w, &h)) return; progress_total = w * h; pika_progress_init (_("Checkerboard")); buffer = pika_drawable_get_shadow_buffer (drawable); iter = gegl_buffer_iterator_new (buffer, GEGL_RECTANGLE (x, y, w, h), 0, format, GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE, 1); while (gegl_buffer_iterator_next (iter)) { GeglRectangle roi = iter->items[0].roi; guchar *dest = iter->items[0].data; guchar *d; gint y1, x1; d = dest; for (y1 = 0; y1 < roi.height; y1++) { for (x1 = 0; x1 < roi.width; x1++) { checkerboard_func (roi.x + x1, roi.y + y1, d + x1 * bpp, bpp, size, mode, ¶m); } d += roi.width * bpp; } progress_done += roi.width * roi.height; pika_progress_update ((gdouble) progress_done / (gdouble) progress_total); } g_object_unref (buffer); pika_progress_update (1.0); pika_drawable_merge_shadow (drawable, TRUE); pika_drawable_update (drawable, x, y, w, h); } } static void do_checkerboard_preview (GtkWidget *widget, GObject *config) { PikaPreview *preview = PIKA_PREVIEW (widget); PikaDrawable *drawable = g_object_get_data (config, "drawable"); do_checkerboard_pattern (config, drawable, preview); } static gint inblock (gint pos, gint size) { static gint *in = NULL; /* initialized first time */ static gint len = -1; /* avoid a FP exception */ if (size == 1) size = 2; if (in && len != size * size) { g_free (in); in = NULL; } len = size * size; /* Initialize the array; since we'll be called thousands of * times with the same size value, precompute the array. */ if (in == NULL) { gint cell = 1; /* cell value */ gint i, j, k; in = g_new (gint, len); /* 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. */ 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; } } /* place pos within 0..(len-1) grid and return the value. */ return in[pos % (len - 1)]; } static gboolean checkerboard_dialog (PikaProcedure *procedure, GObject *config, PikaImage *image, PikaDrawable *drawable) { GtkWidget *dialog; GtkWidget *preview; GtkWidget *toggle; GtkWidget *size_entry; gint size, width, height; gdouble xres; gdouble yres; gboolean run; pika_ui_init (PLUG_IN_BINARY); dialog = pika_procedure_dialog_new (procedure, PIKA_PROCEDURE_CONFIG (config), _("Checkerboard")); pika_dialog_set_alternative_button_order (GTK_DIALOG (dialog), GTK_RESPONSE_OK, GTK_RESPONSE_CANCEL, -1); pika_window_set_transient (GTK_WINDOW (dialog)); preview = pika_drawable_preview_new_from_drawable (drawable); gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), preview, TRUE, TRUE, 0); gtk_widget_show (preview); /* Get the image resolution and unit */ pika_image_get_resolution (image, &xres, &yres); width = pika_drawable_get_width (drawable); height = pika_drawable_get_height (drawable); size = MIN (width, height); size_entry = pika_procedure_dialog_get_size_entry (PIKA_PROCEDURE_DIALOG (dialog), "check-size", TRUE, "check-size-unit", "%a", PIKA_SIZE_ENTRY_UPDATE_SIZE, xres); gtk_widget_set_margin_bottom (size_entry, 12); /* set the size (in pixels) that will be treated as 0% and 100% */ pika_size_entry_set_size (PIKA_SIZE_ENTRY (size_entry), 0, 0.0, size); /* set upper and lower limits (in pixels) */ pika_size_entry_set_refval_boundaries (PIKA_SIZE_ENTRY (size_entry), 0, 1.0, size); toggle = pika_procedure_dialog_get_widget (PIKA_PROCEDURE_DIALOG (dialog), "psychobily", GTK_TYPE_CHECK_BUTTON); gtk_widget_set_margin_bottom (toggle, 12); g_object_set_data (config, "drawable", drawable); g_signal_connect (preview, "invalidated", G_CALLBACK (do_checkerboard_preview), config); g_signal_connect_swapped (config, "notify", G_CALLBACK (pika_preview_invalidate), preview); pika_procedure_dialog_fill (PIKA_PROCEDURE_DIALOG (dialog), "check-size", "psychobily", NULL); gtk_widget_show (dialog); run = pika_procedure_dialog_run (PIKA_PROCEDURE_DIALOG (dialog)); gtk_widget_destroy (dialog); return run; }