gimp/plug-ins/common/gpb.c

1143 lines
34 KiB
C

/* Plug-in to save .gpb (GIMP Pixmap Brush) files.
* The format for pixmap brushes might well change, and this will have to
* be updated.
*
* Copyright (C) 1999 Tor Lillqvist
*
* 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
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/* set to the level of debugging output you want, 0 for none */
#define GPB_DEBUG 0
#define IFDBG(level) if (GPB_DEBUG >= level)
#include "config.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <gtk/gtk.h>
#include <libgimp/gimp.h>
#include <libgimp/parasiteio.h>
#include "app/brush_header.h"
#include "app/pattern_header.h"
#define DUMMY_PATTERN_NAME "x"
#define MAXDESCLEN 256
/* Parameters applicable each time we save a gpb or gih, saved
* in the main gimp application between invocations of this plug-in.
*/
static struct {
/* Use by both gpb and gih: */
guint spacing;
guchar description[MAXDESCLEN+1];
} info =
/* Initialize to this, change if non-interactive later */
{
10,
"GIMP Pixmap Brush"
};
static gint run_flag = 0;
static gint num_layers_with_alpha;
static PixPipeParams gihparms;
typedef struct
{
GOrientation orientation;
gint32 image;
gint32 toplayer;
gint nguides;
gint32 *guides;
gint *value;
GtkWidget *count_label; /* Corresponding count adjustment, */
gint *count; /* cols or rows */
gint *other_count; /* And the other one */
GtkObject *ncells;
GtkObject *rank0;
GtkWidget *warning_label;
} SizeAdjustmentData;
static gint32 *vguides, *hguides;
static gint nvguides = 0, nhguides = 0;
/* Declare some local functions.
*/
static void query (void);
static void run (char *name,
int nparams,
GParam *param,
int *nreturn_vals,
GParam **return_vals);
GPlugInInfo PLUG_IN_INFO =
{
NULL, /* init_proc */
NULL, /* quit_proc */
query, /* query_proc */
run, /* run_proc */
};
MAIN ()
static void
query ()
{
static GParamDef gpb_save_args[] =
{
{ PARAM_INT32, "run_mode", "Interactive, non-interactive" },
{ PARAM_IMAGE, "image", "Input image" },
{ PARAM_DRAWABLE, "drawable", "Drawable to save" },
{ PARAM_STRING, "filename", "The name of the file to save the brush in" },
{ PARAM_STRING, "raw_filename", "The name of the file to save the brush in" },
{ PARAM_INT32, "spacing", "Spacing of the brush" },
{ PARAM_STRING, "description", "Short description of the brush" },
};
static int ngpb_save_args = sizeof (gpb_save_args)
/ sizeof (gpb_save_args[0]);
static GParamDef gih_save_args[] =
{
{ PARAM_INT32, "run_mode", "Interactive, non-interactive" },
{ PARAM_IMAGE, "image", "Input image" },
{ PARAM_DRAWABLE, "drawable", "Drawable to save" },
{ PARAM_STRING, "filename", "The name of the file to save the brush pipe in" },
{ PARAM_STRING, "raw_filename", "The name of the file to save the brush pipe in" },
{ PARAM_INT32, "spacing", "Spacing of the brush" },
{ PARAM_STRING, "description", "Short description of the brush pipe" },
};
static int ngih_save_args = sizeof (gih_save_args)
/ sizeof (gih_save_args[0]);
gimp_install_procedure ("file_gpb_save",
"saves images in GIMP pixmap brush format",
"This plug-in saves a layer of an image in "
"the GIMP pixmap brush format. "
"The image must have an alpha channel.",
"Tor Lillqvist",
"Tor Lillqvist",
"1999",
"<Save>/GPB",
"RGBA",
PROC_PLUG_IN,
ngpb_save_args, 0,
gpb_save_args, NULL);
gimp_register_save_handler ("file_gpb_save", "gpb", "");
gimp_install_procedure ("file_gih_save",
"saves images in GIMP pixmap brush pipe format",
"This plug-in saves an image in "
"the GIMP pixmap brush pipe format. "
"The image must have an alpha channel."
"The image can be multi-layered, "
"and additionally the layers can be divided into "
"a rectangular array of brushes.",
"Tor Lillqvist",
"Tor Lillqvist",
"1999",
"<Save>/GIH",
"RGBA",
PROC_PLUG_IN,
ngih_save_args, 0,
gih_save_args, NULL);
gimp_register_save_handler ("file_gih_save", "gih", "");
}
static void
adjustment_callback (GtkWidget *widget,
gpointer data)
{
*((gint *) data) = GTK_ADJUSTMENT (widget)->value;
}
static void
size_adjustment_callback (GtkWidget *widget,
gpointer data)
{
/* Unfortunately this doesn't work, sigh. The guides don't show up unless
* you manually force a redraw of the image.
*/
int i;
int size;
int newn;
SizeAdjustmentData *adj = (SizeAdjustmentData *) data;
char buf[10];
for (i = 0; i < adj->nguides; i++)
gimp_image_delete_guide (adj->image, adj->guides[i]);
g_free (adj->guides);
adj->guides = NULL;
gimp_displays_flush ();
*(adj->value) = GTK_ADJUSTMENT (widget)->value;
if (adj->orientation == ORIENTATION_VERTICAL)
{
size = gimp_image_width (adj->image);
newn = size / *(adj->value);
adj->nguides = newn - 1;
adj->guides = g_new (gint32, adj->nguides);
for (i = 0; i < adj->nguides; i++)
adj->guides[i] = gimp_image_add_vguide (adj->image,
*(adj->value) * (i+1));
}
else
{
size = gimp_image_height (adj->image);
newn = size / *(adj->value);
adj->nguides = newn - 1;
adj->guides = g_new (gint32, adj->nguides);
for (i = 0; i < adj->nguides; i++)
adj->guides[i] = gimp_image_add_hguide (adj->image,
*(adj->value) * (i+1));
}
gimp_displays_flush ();
sprintf (buf, "%2d", newn);
gtk_label_set_text (GTK_LABEL (adj->count_label), buf);
*(adj->count) = newn;
if (newn * *(adj->value) != size)
gtk_widget_show (GTK_WIDGET (adj->warning_label));
else
gtk_widget_hide (GTK_WIDGET (adj->warning_label));
if (adj->ncells != NULL)
gtk_adjustment_set_value (GTK_ADJUSTMENT (adj->ncells),
*(adj->other_count) * *(adj->count));
if (adj->rank0 != NULL)
gtk_adjustment_set_value (GTK_ADJUSTMENT (adj->rank0),
*(adj->other_count) * *(adj->count));
}
static void
entry_callback (GtkWidget *widget,
gpointer data)
{
if (data == info.description)
{
strncpy (info.description, gtk_entry_get_text (GTK_ENTRY (widget)), MAXDESCLEN);
info.description[MAXDESCLEN] = 0;
}
}
static void
cb_callback (GtkWidget *widget,
gpointer data)
{
*((char **) data) = gtk_entry_get_text (GTK_ENTRY (widget));
}
static void
close_callback (GtkWidget *widget,
gpointer data)
{
gtk_main_quit ();
}
static void
ok_callback (GtkWidget *widget,
gpointer data)
{
run_flag = 1;
gtk_widget_destroy (GTK_WIDGET (data));
}
static void
common_save_dialog (GtkWidget *dlg,
GtkWidget *table)
{
GtkWidget *button;
GtkWidget *label;
GtkObject *adjustment;
GtkWidget *box;
GtkWidget *spinbutton;
GtkWidget *entry;
gchar buffer[12];
/* Action area */
button = gtk_button_new_with_label ("OK");
GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
gtk_signal_connect (GTK_OBJECT (button), "clicked",
(GtkSignalFunc) ok_callback, dlg);
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->action_area), button, TRUE, TRUE, 0);
gtk_widget_grab_default (button);
gtk_widget_show (button);
button = gtk_button_new_with_label ("Cancel");
GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
(GtkSignalFunc) gtk_widget_destroy,
GTK_OBJECT (dlg));
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->action_area), button, TRUE, TRUE, 0);
gtk_widget_show (button);
gtk_table_set_row_spacings (GTK_TABLE (table), 10);
gtk_table_set_col_spacings (GTK_TABLE (table), 10);
/*
* Spacing: __
*/
gtk_table_resize (GTK_TABLE (table),
GTK_TABLE (table)->nrows + 1, GTK_TABLE (table)->ncols);
label = gtk_label_new ("Spacing (percent):");
gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
gtk_table_attach (GTK_TABLE (table), label, 0, 1,
GTK_TABLE (table)->nrows - 1, GTK_TABLE (table)->nrows,
GTK_FILL, GTK_FILL, 0, 0);
gtk_widget_show (label);
adjustment = gtk_adjustment_new (info.spacing, 1, 1000, 1, 10, 10);
spinbutton = gtk_spin_button_new (GTK_ADJUSTMENT (adjustment), 1, 0);
gtk_spin_button_set_shadow_type (GTK_SPIN_BUTTON (spinbutton),
GTK_SHADOW_NONE);
gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
gtk_widget_set_usize (spinbutton, 75, 0);
box = gtk_hbox_new (FALSE, 0);
gtk_box_pack_start (GTK_BOX (box), spinbutton, FALSE, TRUE, 0);
gtk_table_attach (GTK_TABLE (table), box, 1, 2,
GTK_TABLE (table)->nrows - 1, GTK_TABLE (table)->nrows,
GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
gtk_signal_connect (GTK_OBJECT (adjustment), "value_changed",
(GtkSignalFunc) adjustment_callback, &info.spacing);
gtk_widget_show (spinbutton);
gtk_widget_show (box);
/*
* Description: ___________
*/
gtk_table_resize (GTK_TABLE (table),
GTK_TABLE (table)->nrows + 1, GTK_TABLE (table)->ncols);
label = gtk_label_new ("Description:");
gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
gtk_table_attach (GTK_TABLE (table), label, 0, 1,
GTK_TABLE (table)->nrows - 1, GTK_TABLE (table)->nrows,
GTK_FILL, GTK_FILL, 0, 0);
gtk_widget_show (label);
entry = gtk_entry_new ();
gtk_table_attach (GTK_TABLE (table), entry, 1, 2,
GTK_TABLE (table)->nrows - 1, GTK_TABLE (table)->nrows,
GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
gtk_widget_set_usize (entry, 200, 0);
gtk_entry_set_text (GTK_ENTRY (entry), info.description);
gtk_signal_connect (GTK_OBJECT (entry), "changed",
(GtkSignalFunc) entry_callback, info.description);
gtk_widget_show (entry);
}
static gint
gpb_save_dialog ()
{
GtkWidget *dlg;
GtkWidget *table;
GtkWidget *entry;
gchar **argv;
gint argc;
gchar buffer[100];
argc = 1;
argv = g_new (gchar *, 1);
argv[0] = g_strdup ("gpb_save");
gtk_init (&argc, &argv);
gtk_rc_parse (gimp_gtkrc ());
dlg = gtk_dialog_new ();
gtk_window_set_title (GTK_WINDOW (dlg), "Save As Pixmap Brush");
gtk_window_position (GTK_WINDOW (dlg), GTK_WIN_POS_MOUSE);
gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
(GtkSignalFunc) close_callback, NULL);
/* The main table */
table = gtk_table_new (2, 0, FALSE);
gtk_container_border_width (GTK_CONTAINER (table), 10);
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), table, TRUE, TRUE, 0);
gtk_widget_show (table);
common_save_dialog (dlg, table);
gtk_widget_show (dlg);
gtk_main ();
gdk_flush ();
return run_flag;
}
static gint
gih_save_dialog (gint32 image_ID)
{
GtkWidget *dlg;
GtkWidget *table, *dimtable;
GtkWidget *label;
GtkObject *adjustment;
GtkWidget *spinbutton;
GtkWidget *entry;
GtkWidget *box;
GtkWidget *cb;
GList *cbitems = NULL;
gint i;
gchar **argv;
gint argc;
gchar buffer[100];
SizeAdjustmentData cellw_adjust, cellh_adjust;
gint32 *layer_ID;
gint32 nlayers;
/* Setup default values */
if (gihparms.rows >= 1 && gihparms.cols >= 1)
gihparms.ncells = num_layers_with_alpha * gihparms.rows * gihparms.cols;
else
gihparms.ncells = num_layers_with_alpha;
if (gihparms.cellwidth == 1 && gihparms.cellheight == 1)
{
gihparms.cellwidth = gimp_image_width (image_ID) / gihparms.cols;
gihparms.cellheight = gimp_image_height (image_ID) / gihparms.rows;
}
argc = 1;
argv = g_new (gchar *, 1);
argv[0] = g_strdup ("gpb_save");
gtk_init (&argc, &argv);
gtk_rc_parse (gimp_gtkrc ());
dlg = gtk_dialog_new ();
gtk_window_set_title (GTK_WINDOW (dlg), "Save As Pixmap Brush Pipe");
gtk_window_position (GTK_WINDOW (dlg), GTK_WIN_POS_MOUSE);
gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
(GtkSignalFunc) close_callback, NULL);
/* The main table */
table = gtk_table_new (2, 0, FALSE);
gtk_container_border_width (GTK_CONTAINER (table), 10);
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), table, TRUE, TRUE, 0);
gtk_widget_show (table);
common_save_dialog (dlg, table);
/*
* Cell size: __ x __ pixels
*/
gtk_table_resize (GTK_TABLE (table),
GTK_TABLE (table)->nrows + 1, GTK_TABLE (table)->ncols);
label = gtk_label_new ("Cell size:");
gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
gtk_table_attach (GTK_TABLE (table), label, 0, 1,
GTK_TABLE (table)->nrows - 1, GTK_TABLE (table)->nrows,
GTK_FILL, GTK_FILL, 0, 0);
gtk_widget_show (label);
box = gtk_hbox_new (FALSE, 0);
adjustment = gtk_adjustment_new (gihparms.cellwidth,
2, gimp_image_width (image_ID), 1, 1, 1);
spinbutton = gtk_spin_button_new (GTK_ADJUSTMENT (adjustment), 1, 0);
gtk_spin_button_set_shadow_type (GTK_SPIN_BUTTON (spinbutton),
GTK_SHADOW_NONE);
gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
gtk_widget_set_usize (spinbutton, 75, 0);
gtk_box_pack_start (GTK_BOX (box), spinbutton, FALSE, FALSE, 0);
layer_ID = gimp_image_get_layers (image_ID, &nlayers);
cellw_adjust.orientation = ORIENTATION_VERTICAL;
cellw_adjust.image = image_ID;
cellw_adjust.toplayer = layer_ID[nlayers-1];
cellw_adjust.nguides = 0;
cellw_adjust.guides = NULL;
cellw_adjust.value = &gihparms.cellwidth;
gtk_signal_connect (GTK_OBJECT (adjustment), "value_changed",
(GtkSignalFunc) size_adjustment_callback,
&cellw_adjust);
gtk_widget_show (spinbutton);
label = gtk_label_new (" x ");
gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0);
gtk_widget_show (label);
adjustment = gtk_adjustment_new (gihparms.cellheight,
2, gimp_image_height (image_ID), 1, 1, 1);
spinbutton = gtk_spin_button_new (GTK_ADJUSTMENT (adjustment), 1, 0);
gtk_spin_button_set_shadow_type (GTK_SPIN_BUTTON (spinbutton),
GTK_SHADOW_NONE);
gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
gtk_widget_set_usize (spinbutton, 75, 0);
gtk_box_pack_start (GTK_BOX (box), spinbutton, FALSE, FALSE, 0);
cellh_adjust.orientation = ORIENTATION_HORIZONTAL;
cellh_adjust.image = image_ID;
cellh_adjust.toplayer = layer_ID[nlayers-1];
cellh_adjust.nguides = 0;
cellh_adjust.guides = NULL;
cellh_adjust.value = &gihparms.cellheight;
gtk_signal_connect (GTK_OBJECT (adjustment), "value_changed",
(GtkSignalFunc) size_adjustment_callback,
&cellh_adjust);
gtk_widget_show (spinbutton);
label = gtk_label_new (" pixels");
gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0);
gtk_widget_show (label);
gtk_table_attach (GTK_TABLE (table), box, 1, 2,
GTK_TABLE (table)->nrows - 1, GTK_TABLE (table)->nrows,
GTK_FILL, GTK_FILL, 0, 0);
gtk_widget_show (box);
g_free (layer_ID);
/*
* Number of cells: ___
*/
gtk_table_resize (GTK_TABLE (table),
GTK_TABLE (table)->nrows + 1, GTK_TABLE (table)->ncols);
label = gtk_label_new ("Number of cells:");
gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
gtk_table_attach (GTK_TABLE (table), label, 0, 1,
GTK_TABLE (table)->nrows - 1, GTK_TABLE (table)->nrows,
GTK_FILL, GTK_FILL, 0, 0);
gtk_widget_show (label);
adjustment = gtk_adjustment_new (gihparms.ncells, 1, 1000, 1, 10, 10);
spinbutton = gtk_spin_button_new (GTK_ADJUSTMENT (adjustment), 1, 0);
gtk_spin_button_set_shadow_type (GTK_SPIN_BUTTON (spinbutton),
GTK_SHADOW_NONE);
gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
gtk_widget_set_usize (spinbutton, 75, 0);
box = gtk_hbox_new (FALSE, 0);
gtk_box_pack_start (GTK_BOX (box), spinbutton, FALSE, TRUE, 0);
gtk_table_attach (GTK_TABLE (table), box, 1, 2,
GTK_TABLE (table)->nrows - 1, GTK_TABLE (table)->nrows,
GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
gtk_signal_connect (GTK_OBJECT (adjustment), "value_changed",
(GtkSignalFunc) adjustment_callback, &gihparms.ncells);
gtk_widget_show (spinbutton);
gtk_widget_show (box);
if (gihparms.dim == 1)
cellw_adjust.ncells = cellh_adjust.ncells = adjustment;
else
cellw_adjust.ncells = cellh_adjust.ncells = NULL;
/*
* Display as: __ rows x __ cols
*/
gtk_table_resize (GTK_TABLE (table),
GTK_TABLE (table)->nrows + 1, GTK_TABLE (table)->ncols);
label = gtk_label_new ("Display as:");
gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
gtk_table_attach (GTK_TABLE (table), label, 0, 1,
GTK_TABLE (table)->nrows - 1, GTK_TABLE (table)->nrows,
GTK_FILL, GTK_FILL, 0, 0);
gtk_widget_show (label);
box = gtk_hbox_new (FALSE, 0);
sprintf (buffer, "%2d", gihparms.rows);
label = gtk_label_new (buffer);
gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0);
cellh_adjust.count_label = label;
cellh_adjust.count = &gihparms.rows;
cellh_adjust.other_count = &gihparms.cols;
gtk_widget_show (label);
label = gtk_label_new (" rows of ");
gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0);
gtk_widget_show (label);
sprintf (buffer, "%2d", gihparms.cols);
label = gtk_label_new (buffer);
gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0);
cellw_adjust.count_label = label;
cellw_adjust.count = &gihparms.cols;
cellw_adjust.other_count = &gihparms.rows;
gtk_widget_show (label);
label = gtk_label_new (" columns on each layer");
gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0);
gtk_widget_show (label);
label = gtk_label_new (" (width mismatch!) ");
gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0);
cellw_adjust.warning_label = label;
label = gtk_label_new (" (height mismatch!) ");
gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0);
cellh_adjust.warning_label = label;
gtk_table_attach (GTK_TABLE (table), box, 1, 2,
GTK_TABLE (table)->nrows - 1, GTK_TABLE (table)->nrows,
GTK_FILL, GTK_FILL, 0, 0);
gtk_widget_show (box);
/*
* Dimension: ___
*/
gtk_table_resize (GTK_TABLE (table),
GTK_TABLE (table)->nrows + 1, GTK_TABLE (table)->ncols);
label = gtk_label_new ("Dimension:");
gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
gtk_table_attach (GTK_TABLE (table), label, 0, 1,
GTK_TABLE (table)->nrows - 1, GTK_TABLE (table)->nrows,
GTK_FILL, GTK_FILL, 0, 0);
gtk_widget_show (label);
adjustment = gtk_adjustment_new (gihparms.dim, 1, 5, 1, 1, 1);
spinbutton = gtk_spin_button_new (GTK_ADJUSTMENT (adjustment), 1, 0);
gtk_spin_button_set_shadow_type (GTK_SPIN_BUTTON (spinbutton),
GTK_SHADOW_NONE);
gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
gtk_widget_set_usize (spinbutton, 75, 0);
box = gtk_hbox_new (FALSE, 0);
gtk_box_pack_start (GTK_BOX (box), spinbutton, FALSE, FALSE, 0);
gtk_table_attach (GTK_TABLE (table), box, 1, 2,
GTK_TABLE (table)->nrows - 1, GTK_TABLE (table)->nrows,
GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
gtk_signal_connect (GTK_OBJECT (adjustment), "value_changed",
(GtkSignalFunc) adjustment_callback, &gihparms.dim);
gtk_widget_show (spinbutton);
gtk_widget_show (box);
/*
* Ranks: __ __ __ __ __
*/
gtk_table_resize (GTK_TABLE (table),
GTK_TABLE (table)->nrows + 1, GTK_TABLE (table)->ncols);
label = gtk_label_new ("Ranks:");
gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
gtk_table_attach (GTK_TABLE (table), label, 0, 1,
GTK_TABLE (table)->nrows - 1, GTK_TABLE (table)->nrows,
GTK_FILL, GTK_FILL, 0, 0);
gtk_widget_show (label);
dimtable = gtk_table_new (PIXPIPE_MAXDIM, 1, FALSE);
for (i = 0; i < PIXPIPE_MAXDIM; i++)
{
adjustment = gtk_adjustment_new (gihparms.rank[i], 0, 100, 1, 1, 1);
spinbutton = gtk_spin_button_new (GTK_ADJUSTMENT (adjustment), 1, 0);
gtk_spin_button_set_shadow_type (GTK_SPIN_BUTTON (spinbutton),
GTK_SHADOW_NONE);
gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
gtk_widget_set_usize (spinbutton, 75, 0);
box = gtk_hbox_new (FALSE, 0);
gtk_box_pack_start (GTK_BOX (box), spinbutton, FALSE, TRUE, 0);
gtk_table_attach (GTK_TABLE (dimtable), box, i, i + 1, 0, 1,
GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 3, 0);
gtk_signal_connect (GTK_OBJECT (adjustment), "value_changed",
(GtkSignalFunc) adjustment_callback, &gihparms.rank[i]);
gtk_widget_show (spinbutton);
gtk_widget_show (box);
if (i == 0)
if (gihparms.dim == 1)
cellw_adjust.rank0 = cellh_adjust.rank0 = adjustment;
else
cellw_adjust.rank0 = cellh_adjust.rank0 = NULL;
}
gtk_table_attach (GTK_TABLE (table), dimtable, 1, 2,
GTK_TABLE (table)->nrows - 1, GTK_TABLE (table)->nrows,
GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
gtk_widget_show (dimtable);
/*
* Selection: ______ ______ ______ ______ ______
*/
gtk_table_resize (GTK_TABLE (table),
GTK_TABLE (table)->nrows + 1, GTK_TABLE (table)->ncols);
label = gtk_label_new ("Selection:");
gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
gtk_table_attach (GTK_TABLE (table), label, 0, 1,
GTK_TABLE (table)->nrows - 1, GTK_TABLE (table)->nrows,
GTK_FILL, GTK_FILL, 0, 0);
gtk_widget_show (label);
cbitems = g_list_append (cbitems, "incremental");
cbitems = g_list_append (cbitems, "angular");
cbitems = g_list_append (cbitems, "random");
cbitems = g_list_append (cbitems, "velocity");
cbitems = g_list_append (cbitems, "pressure");
cbitems = g_list_append (cbitems, "xtilt");
cbitems = g_list_append (cbitems, "ytilt");
box = gtk_hbox_new (FALSE, 0);
for (i = 0; i < PIXPIPE_MAXDIM; i++)
{
cb = gtk_combo_new ();
gtk_combo_set_popdown_strings (GTK_COMBO (cb), cbitems);
if (gihparms.selection[i])
gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (cb)->entry), gihparms.selection[i]);
else
gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (cb)->entry), "random");
gtk_entry_set_editable (GTK_ENTRY (GTK_COMBO (cb)->entry), FALSE);
gtk_box_pack_start (GTK_BOX (box), cb, FALSE, TRUE, 2);
gtk_signal_connect (GTK_OBJECT (GTK_COMBO (cb)->entry), "changed",
(GtkSignalFunc) cb_callback, &gihparms.selection[i]);
gtk_widget_show (cb);
}
gtk_table_attach (GTK_TABLE (table), box, 1, 2,
GTK_TABLE (table)->nrows - 1, GTK_TABLE (table)->nrows,
GTK_SHRINK, GTK_EXPAND | GTK_FILL, 0, 0);
gtk_widget_show (box);
gtk_widget_show (dlg);
gtk_main ();
gdk_flush ();
for (i = 0; i < cellw_adjust.nguides; i++)
gimp_image_delete_guide (image_ID, cellw_adjust.guides[i]);
for (i = 0; i < cellh_adjust.nguides; i++)
gimp_image_delete_guide (image_ID, cellh_adjust.guides[i]);
if (run_flag)
{
/* Fix up bogus values */
gihparms.ncells =
MIN (gihparms.ncells,
num_layers_with_alpha * gihparms.rows * gihparms.cols);
}
return run_flag;
}
static gboolean
try_fwrite (gpointer buffer,
guint size,
guint nitems,
FILE *file)
{
if (fwrite (buffer, size, nitems, file) < nitems)
{
g_message ("GPB: write error");
fclose (file);
return FALSE;
}
return TRUE;
}
static gboolean
save_one_gpb (FILE *file,
GPixelRgn *pixel_rgn,
int index,
int total)
{
BrushHeader brushheader;
PatternHeader patternheader;
guint y, x;
guchar *buffer;
guint width, height;
width = pixel_rgn->w;
height = pixel_rgn->h;
brushheader.header_size = g_htonl (sizeof (brushheader) + strlen (info.description) + 1);
brushheader.version = g_htonl (2);
brushheader.width = g_htonl (width);
brushheader.height = g_htonl (height);
brushheader.bytes = g_htonl (1);
brushheader.magic_number = g_htonl (GBRUSH_MAGIC);
brushheader.spacing = g_htonl (info.spacing);
if (!try_fwrite (&brushheader, sizeof (brushheader), 1, file))
return FALSE;
if (!try_fwrite (info.description, strlen (info.description) + 1, 1, file))
return FALSE;
IFDBG(3) g_message ("saving gpb %d of %d: %dx%d, offset %d,%d stride %d drawable %d ",
index+1, total,
width, height, pixel_rgn->x, pixel_rgn->y,
pixel_rgn->rowstride, pixel_rgn->drawable->id);
buffer = g_malloc (width * pixel_rgn->bpp);
for (y = 0; y < height; y++)
{
#if 0
/* No; the brushes don't have to be the same size, of course. */
if (y >= pixel_rgn->h)
{
if (y == pixel_rgn->h)
memset (buffer, 0, width);
if (!try_fwrite (buffer, width, 1, file))
{
g_free (buffer);
return FALSE;
}
}
else
#endif
{
gimp_pixel_rgn_get_row (pixel_rgn, buffer,
0 + pixel_rgn->x, y + pixel_rgn->y,
pixel_rgn->w);
for (x = 0; x < pixel_rgn->w; x++)
if (!try_fwrite (buffer + x*pixel_rgn->bpp + 3, 1, 1, file))
{
g_free (buffer);
return FALSE;
}
#if 0
if (width > pixel_rgn->w)
{
memset (buffer, 0, width - pixel_rgn->w);
if (!try_fwrite (buffer, width - pixel_rgn->w, 1, file))
{
g_free (buffer);
return FALSE;
}
}
#endif
}
if (y % 10 == 0)
gimp_progress_update
(((double) index + 0.5 * y / pixel_rgn->h) / total);
}
gimp_progress_update (((double) index + 0.5) / total);
patternheader.header_size = g_htonl (sizeof (patternheader) + strlen (DUMMY_PATTERN_NAME) + 1 );
patternheader.version = g_htonl (1);
patternheader.width = g_htonl (width);
patternheader.height = g_htonl (height);
patternheader.bytes = g_htonl (3);
patternheader.magic_number = g_htonl (GPATTERN_MAGIC);
if (!try_fwrite (&patternheader, sizeof (patternheader), 1, file))
return FALSE;
/* Pattern name is irrelevant */
if (!try_fwrite (DUMMY_PATTERN_NAME, strlen (DUMMY_PATTERN_NAME) + 1, 1, file))
return FALSE;
for (y = 0; y < height; y++)
{
#if 0
if (y >= pixel_rgn->h)
{
if (y == pixel_rgn->h)
memset (buffer, 0, width * 3);
if (!try_fwrite (buffer, width*3, 1, file))
{
g_free (buffer);
return FALSE;
}
}
else
#endif
{
gimp_pixel_rgn_get_row (pixel_rgn, buffer,
0 + pixel_rgn->x, y + pixel_rgn->y, pixel_rgn->w);
for (x = 0; x < pixel_rgn->w; x++)
if (!try_fwrite (buffer + x*pixel_rgn->bpp, 3, 1, file))
return FALSE;
#if 0
if (width > pixel_rgn->w)
{
memset (buffer, 0, (width - pixel_rgn->w) * 3);
if (!try_fwrite (buffer, (width - pixel_rgn->w) * 3, 1, file))
{
g_free (buffer);
return FALSE;
}
}
#endif
}
if (y % 10 == 0)
gimp_progress_update
(((double) index + 0.5 + 0.5 * y / pixel_rgn->h) / total);
}
g_free (buffer);
gimp_progress_update (((double) index + 1.0) / total);
return TRUE;
}
static gboolean
gpb_save_image (char *filename,
gint32 image_ID,
gint32 drawable_ID)
{
GDrawable *drawable;
GPixelRgn pixel_rgn;
FILE *file;
gchar *temp;
g_assert (gimp_drawable_has_alpha (drawable_ID));
drawable = gimp_drawable_get (drawable_ID);
gimp_tile_cache_size (gimp_tile_height () * drawable->width * 4);
gimp_pixel_rgn_init (&pixel_rgn, drawable, 0, 0, drawable->width, drawable->height, FALSE, FALSE);
temp = g_strdup_printf ("Saving %s:", filename);
gimp_progress_init (temp);
g_free (temp);
file = fopen (filename, "wb");
if (file == NULL)
{
g_message ("GPB: can't open \"%s\"", filename);
return FALSE;
}
if (!save_one_gpb (file, &pixel_rgn, 0, 1))
return FALSE;
fclose (file);
return TRUE;
}
static gboolean
gih_save_image (char *filename,
gint32 image_ID,
gint32 drawable_ID)
{
GDrawable *drawable;
GPixelRgn pixel_rgn;
FILE *file;
Parasite *pipe_parasite;
gchar *msg, *parstring, *ncells;
gint32 *layer_ID;
gint nlayers, layer, row, col;
gint imagew, imageh, offsetx, offsety;
gint k, x, y, thisx, thisy, xnext, ynext, thisw, thish;
imagew = gimp_image_width (image_ID);
imageh = gimp_image_height (image_ID);
gimp_tile_cache_size (gimp_tile_height () * imagew * 4);
msg = g_strdup_printf ("Saving %s:", filename);
gimp_progress_init (msg);
g_free (msg);
file = fopen (filename, "wb");
if (file == NULL)
{
g_message ("GPB: can't open \"%s\"", filename);
return FALSE;
}
parstring = pixpipeparams_build (&gihparms);
IFDBG(2) g_message ("parameter string: %s", parstring);
ncells = g_strdup_printf ("%d ", gihparms.ncells);
if (!(try_fwrite (info.description, strlen (info.description), 1, file)
&& try_fwrite ("\n", 1, 1, file)
&& try_fwrite (ncells, strlen (ncells), 1, file)
&& try_fwrite (parstring, strlen (parstring), 1, file)
&& try_fwrite ("\n", 1, 1, file)))
{
g_free (parstring);
g_free (ncells);
return FALSE;
}
pipe_parasite = parasite_new ("gimp-brush-pipe-parameters",
PARASITE_PERSISTENT,
strlen (parstring) + 1, parstring);
gimp_image_attach_parasite (image_ID, pipe_parasite);
parasite_free (pipe_parasite);
g_free (parstring);
g_free (ncells);
layer_ID = gimp_image_get_layers (image_ID, &nlayers);
k = 0;
for (layer = 0; layer < nlayers; layer++)
{
if (!gimp_drawable_has_alpha (layer_ID[layer]))
continue;
drawable = gimp_drawable_get (layer_ID[layer]);
gimp_drawable_offsets(layer_ID[layer], &offsetx, &offsety);
for (row = 0; row < gihparms.rows; row++)
{
y = (row * imageh) / gihparms.rows ;
ynext = ((row + 1) * imageh / gihparms.rows);
/* Assume layer is offset to positive direction in x and y.
* That's reasonable, as otherwise all of the layer
* won't be visible.
* thisy and thisx are in the drawable's coordinate space.
*/
thisy = MAX (0, y - offsety);
thish = (ynext - offsety) - thisy;
thish = MIN (thish, drawable->height - thisy);
for (col = 0; col < gihparms.cols; col++)
{
x = (col * imagew / gihparms.cols);
xnext = ((col + 1) * imagew / gihparms.cols);
thisx = MAX (0, x - offsetx);
thisw = (xnext - offsetx) - thisx;
thisw = MIN (thisw, drawable->width - thisx);
IFDBG(3) g_message ("gimp_pixel_rgn_init %d %d %d %d",
thisx, thisy, thisw, thish);
gimp_pixel_rgn_init (&pixel_rgn, drawable, thisx, thisy,
thisw, thish, FALSE, FALSE);
if (!save_one_gpb (file, &pixel_rgn, k, gihparms.ncells))
return FALSE;
k++;
}
}
}
gimp_progress_update (1.0);
fclose (file);
return TRUE;
}
static void
run (char *name,
int nparams,
GParam *param,
int *nreturn_vals,
GParam **return_vals)
{
static GParam values[1];
GRunModeType run_mode;
GStatusType status = STATUS_SUCCESS;
Parasite *pipe_parasite;
gint32 image_ID, *layer_ID;
gint nlayers, layer;
gchar *layer_name;
run_mode = param[0].data.d_int32;
*return_vals = values;
*nreturn_vals = 1;
values[0].type = PARAM_STATUS;
values[0].data.d_status = STATUS_CALLING_ERROR;
if (strcmp (name, "file_gpb_save") == 0)
{
switch (run_mode) {
case RUN_INTERACTIVE:
/* Possibly retrieve data */
gimp_get_data ("file_gpb_save", &info);
if (!gpb_save_dialog ())
return;
break;
case RUN_NONINTERACTIVE: /* FIXME - need a real RUN_NONINTERACTIVE */
if (nparams != 7)
status = STATUS_CALLING_ERROR;
if (status == STATUS_SUCCESS)
{
info.spacing = param[5].data.d_int32;
strncpy (info.description, param[6].data.d_string, MAXDESCLEN);
info.description[MAXDESCLEN] = 0;
}
break;
case RUN_WITH_LAST_VALS:
gimp_get_data ("file_gpb_save", &info);
break;
}
if (gpb_save_image (param[3].data.d_string, param[1].data.d_int32, param[2].data.d_int32))
{
gimp_set_data ("file_gpb_save", &info, sizeof (info));
status = STATUS_SUCCESS;
}
else
status = STATUS_EXECUTION_ERROR;
}
else if (strcmp (name, "file_gih_save") == 0)
{
image_ID = param[1].data.d_int32;
layer_ID = gimp_image_get_layers (image_ID, &nlayers);
num_layers_with_alpha = 0;
for (layer = 0; layer < nlayers; layer++)
{
if (!gimp_drawable_has_alpha (layer_ID[layer]))
{
layer_name = gimp_layer_get_name (layer_ID[layer]);
g_message ("Layer %s doesn't have an alpha channel, skipped",
layer_name);
g_free (layer_name);
}
num_layers_with_alpha++;
}
IFDBG(2) g_message ("nlayers:%d num_layers_with_alpha:%d",
nlayers, num_layers_with_alpha);
switch (run_mode)
{
case RUN_INTERACTIVE:
/* Possibly retrieve data */
gimp_get_data ("file_gih_save", &info);
pipe_parasite = gimp_image_find_parasite (image_ID, "gimp-brush-pipe-parameters");
pixpipeparams_init (&gihparms);
if (pipe_parasite)
pixpipeparams_parse (pipe_parasite->data, &gihparms);
if (!gih_save_dialog (image_ID))
return;
break;
case RUN_NONINTERACTIVE: /* FIXME - need a real RUN_NONINTERACTIVE */
if (nparams != 7)
status = STATUS_CALLING_ERROR;
if (status == STATUS_SUCCESS)
{
info.spacing = param[5].data.d_int32;
strncpy (info.description, param[6].data.d_string, MAXDESCLEN);
info.description[MAXDESCLEN] = 0;
}
break;
case RUN_WITH_LAST_VALS:
gimp_get_data ("file_gih_save", &info);
break;
}
if (gih_save_image (param[3].data.d_string, param[1].data.d_int32, param[2].data.d_int32))
{
gimp_set_data ("file_gih_save", &info, sizeof (info));
status = STATUS_SUCCESS;
}
else
status = STATUS_EXECUTION_ERROR;
}
values[0].data.d_status = status;
}