2023-09-26 00:35:21 +02:00
|
|
|
/* Plug-in to load and export .gih (PIKA Brush Pipe) files.
|
|
|
|
*
|
|
|
|
* Copyright (C) 1999 Tor Lillqvist
|
|
|
|
* Copyright (C) 2000 Jens Lautenbacher, Sven Neumann
|
|
|
|
*
|
|
|
|
* 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 <https://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Example of how to call file_gih_save from script-fu:
|
|
|
|
|
|
|
|
(let ((ranks (cons-array 1 'byte)))
|
|
|
|
(aset ranks 0 12)
|
|
|
|
(file-gih-save 1
|
|
|
|
img
|
|
|
|
drawable
|
|
|
|
"foo.gih"
|
|
|
|
"foo.gih"
|
|
|
|
100
|
|
|
|
"test brush"
|
|
|
|
125
|
|
|
|
125
|
|
|
|
3
|
|
|
|
4
|
|
|
|
1
|
|
|
|
ranks
|
|
|
|
1
|
|
|
|
'("random")))
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
#include <libpika/pika.h>
|
|
|
|
#include <libpika/pikaui.h>
|
|
|
|
#include <libpikabase/pikaparasiteio.h>
|
|
|
|
|
|
|
|
#include "libpika/stdplugins-intl.h"
|
|
|
|
|
|
|
|
|
|
|
|
#define SAVE_PROC "file-gih-save"
|
|
|
|
#define PLUG_IN_BINARY "file-gih"
|
|
|
|
#define PLUG_IN_ROLE "pika-file-gih"
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
{
|
2023-10-30 23:55:30 +01:00
|
|
|
GtkWidget *rank_entry[PIKA_PIXPIPE_MAXDIM];
|
|
|
|
GtkWidget *mode_entry[PIKA_PIXPIPE_MAXDIM];
|
2023-09-26 00:35:21 +02:00
|
|
|
} SizeAdjustmentData;
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct _Gih Gih;
|
|
|
|
typedef struct _GihClass GihClass;
|
|
|
|
|
|
|
|
struct _Gih
|
|
|
|
{
|
|
|
|
PikaPlugIn parent_instance;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct _GihClass
|
|
|
|
{
|
|
|
|
PikaPlugInClass parent_class;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
#define GIH_TYPE (gih_get_type ())
|
2023-10-30 23:55:30 +01:00
|
|
|
#define GIH(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIH_TYPE, Gih))
|
|
|
|
|
|
|
|
GType gih_get_type (void) G_GNUC_CONST;
|
2023-09-26 00:35:21 +02:00
|
|
|
|
2023-10-30 23:55:30 +01:00
|
|
|
static GList * gih_query_procedures (PikaPlugIn *plug_in);
|
|
|
|
static PikaProcedure * gih_create_procedure (PikaPlugIn *plug_in,
|
|
|
|
const gchar *name);
|
2023-09-26 00:35:21 +02:00
|
|
|
|
2023-10-30 23:55:30 +01:00
|
|
|
static PikaValueArray * gih_save (PikaProcedure *procedure,
|
|
|
|
PikaRunMode run_mode,
|
|
|
|
PikaImage *image,
|
|
|
|
gint n_drawables,
|
|
|
|
PikaDrawable **drawables,
|
|
|
|
GFile *file,
|
|
|
|
PikaMetadata *metadata,
|
|
|
|
PikaProcedureConfig *config,
|
|
|
|
gpointer run_data);
|
2023-09-26 00:35:21 +02:00
|
|
|
|
2023-10-30 23:55:30 +01:00
|
|
|
static void gih_remove_guides (PikaProcedureConfig *config,
|
|
|
|
PikaImage *image);
|
|
|
|
static void gih_cell_width_notify (PikaProcedureConfig *config,
|
|
|
|
const GParamSpec *pspec,
|
|
|
|
PikaProcedureDialog *dialog);
|
2023-09-26 00:35:21 +02:00
|
|
|
|
2023-10-30 23:55:30 +01:00
|
|
|
static gboolean gih_save_dialog (PikaImage *image,
|
|
|
|
PikaProcedure *procedure,
|
|
|
|
PikaProcedureConfig *config);
|
2023-09-26 00:35:21 +02:00
|
|
|
|
|
|
|
|
|
|
|
G_DEFINE_TYPE (Gih, gih, PIKA_TYPE_PLUG_IN)
|
|
|
|
|
|
|
|
PIKA_MAIN (GIH_TYPE)
|
|
|
|
DEFINE_STD_SET_I18N
|
|
|
|
|
|
|
|
|
|
|
|
static const gchar * const selection_modes[] = { "incremental",
|
|
|
|
"angular",
|
|
|
|
"random",
|
|
|
|
"velocity",
|
|
|
|
"pressure",
|
|
|
|
"xtilt",
|
|
|
|
"ytilt" };
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
gih_class_init (GihClass *klass)
|
|
|
|
{
|
|
|
|
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
|
|
|
|
|
|
|
|
plug_in_class->query_procedures = gih_query_procedures;
|
|
|
|
plug_in_class->create_procedure = gih_create_procedure;
|
|
|
|
plug_in_class->set_i18n = STD_SET_I18N;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gih_init (Gih *gih)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
static GList *
|
|
|
|
gih_query_procedures (PikaPlugIn *plug_in)
|
|
|
|
{
|
|
|
|
return g_list_append (NULL, g_strdup (SAVE_PROC));
|
|
|
|
}
|
|
|
|
|
|
|
|
static PikaProcedure *
|
|
|
|
gih_create_procedure (PikaPlugIn *plug_in,
|
|
|
|
const gchar *name)
|
|
|
|
{
|
|
|
|
PikaProcedure *procedure = NULL;
|
|
|
|
|
|
|
|
if (! strcmp (name, SAVE_PROC))
|
|
|
|
{
|
|
|
|
procedure = pika_save_procedure_new (plug_in, name,
|
|
|
|
PIKA_PDB_PROC_TYPE_PLUGIN,
|
2023-10-30 23:55:30 +01:00
|
|
|
FALSE, gih_save, NULL, NULL);
|
2023-09-26 00:35:21 +02:00
|
|
|
|
|
|
|
pika_procedure_set_image_types (procedure, "RGB*, GRAY*");
|
|
|
|
|
|
|
|
pika_procedure_set_menu_label (procedure, _("PIKA brush (animated)"));
|
|
|
|
pika_procedure_set_icon_name (procedure, PIKA_ICON_BRUSH);
|
|
|
|
|
|
|
|
pika_procedure_set_documentation (procedure,
|
|
|
|
"exports images in PIKA brush pipe "
|
|
|
|
"format",
|
|
|
|
"This plug-in exports an image in "
|
|
|
|
"the PIKA brush pipe format. For a "
|
|
|
|
"colored brush pipe, RGBA layers are "
|
|
|
|
"used, otherwise the layers should be "
|
|
|
|
"grayscale masks. The image can be "
|
|
|
|
"multi-layered, and additionally the "
|
|
|
|
"layers can be divided into a "
|
|
|
|
"rectangular array of brushes.",
|
|
|
|
SAVE_PROC);
|
|
|
|
pika_procedure_set_attribution (procedure,
|
|
|
|
"Tor Lillqvist",
|
|
|
|
"Tor Lillqvist",
|
|
|
|
"1999");
|
|
|
|
|
|
|
|
pika_file_procedure_set_mime_types (PIKA_FILE_PROCEDURE (procedure),
|
|
|
|
"image/x-pika-gih");
|
2023-10-30 23:55:30 +01:00
|
|
|
pika_file_procedure_set_format_name (PIKA_FILE_PROCEDURE (procedure),
|
|
|
|
_("Brush Pipe"));
|
2023-09-26 00:35:21 +02:00
|
|
|
pika_file_procedure_set_extensions (PIKA_FILE_PROCEDURE (procedure),
|
|
|
|
"gih");
|
|
|
|
pika_file_procedure_set_handles_remote (PIKA_FILE_PROCEDURE (procedure),
|
|
|
|
TRUE);
|
|
|
|
|
|
|
|
PIKA_PROC_ARG_INT (procedure, "spacing",
|
2023-10-30 23:55:30 +01:00
|
|
|
_("Spacing (_percent)"),
|
|
|
|
_("Spacing of the brush"),
|
|
|
|
1, 1000, 20,
|
2023-09-26 00:35:21 +02:00
|
|
|
PIKA_PARAM_READWRITE);
|
|
|
|
|
|
|
|
PIKA_PROC_ARG_STRING (procedure, "description",
|
2023-10-30 23:55:30 +01:00
|
|
|
_("_Description"),
|
|
|
|
_("Short description of the GIH brush pipe"),
|
|
|
|
"PIKA Brush Pipe",
|
2023-09-26 00:35:21 +02:00
|
|
|
PIKA_PARAM_READWRITE);
|
|
|
|
|
|
|
|
PIKA_PROC_ARG_INT (procedure, "cell-width",
|
2023-10-30 23:55:30 +01:00
|
|
|
_("Cell _width"),
|
|
|
|
_("Width of the brush cells in pixels"),
|
|
|
|
1, 1000, 1,
|
2023-09-26 00:35:21 +02:00
|
|
|
PIKA_PARAM_READWRITE);
|
|
|
|
|
|
|
|
PIKA_PROC_ARG_INT (procedure, "cell-height",
|
2023-10-30 23:55:30 +01:00
|
|
|
_("Cell _height"),
|
|
|
|
_("Height of the brush cells in pixels"),
|
2023-09-26 00:35:21 +02:00
|
|
|
1, 1000, 1,
|
|
|
|
PIKA_PARAM_READWRITE);
|
|
|
|
|
2023-10-30 23:55:30 +01:00
|
|
|
PIKA_PROC_ARG_INT (procedure, "num-cells",
|
|
|
|
_("_Number of cells"),
|
|
|
|
_("Number of cells to cut up"),
|
2023-09-26 00:35:21 +02:00
|
|
|
1, 1000, 1,
|
|
|
|
PIKA_PARAM_READWRITE);
|
|
|
|
|
2023-10-30 23:55:30 +01:00
|
|
|
PIKA_PROC_ARG_BYTES (procedure, "ranks",
|
|
|
|
_("_Rank"),
|
|
|
|
_("Ranks of the dimensions"),
|
2023-09-26 00:35:21 +02:00
|
|
|
PIKA_PARAM_READWRITE);
|
|
|
|
|
2023-10-30 23:55:30 +01:00
|
|
|
PIKA_PROC_ARG_STRV (procedure, "selection-modes",
|
|
|
|
"Selection modes",
|
2023-09-26 00:35:21 +02:00
|
|
|
"Selection modes",
|
|
|
|
PIKA_PARAM_READWRITE);
|
2023-10-30 23:55:30 +01:00
|
|
|
|
|
|
|
/* Auxiliary arguments. Only useful for the GUI, to pass info around. */
|
|
|
|
|
|
|
|
PIKA_PROC_AUX_ARG_STRING (procedure, "info-text",
|
|
|
|
_("Display as"),
|
|
|
|
_("Describe how the layers will be split"),
|
|
|
|
"", PIKA_PARAM_READWRITE);
|
|
|
|
|
|
|
|
PIKA_PROC_AUX_ARG_INT (procedure, "dimension",
|
|
|
|
_("D_imension"),
|
|
|
|
_("How many dimensions the animated brush has"),
|
|
|
|
1, 1000, 1,
|
|
|
|
PIKA_PARAM_READWRITE);
|
|
|
|
|
|
|
|
PIKA_PROC_AUX_ARG_INT32_ARRAY (procedure, "guides",
|
|
|
|
"Guides",
|
|
|
|
"Guides to show how the layers will be split in cells",
|
|
|
|
PIKA_PARAM_READWRITE);
|
2023-09-26 00:35:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return procedure;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PikaValueArray *
|
|
|
|
gih_save (PikaProcedure *procedure,
|
|
|
|
PikaRunMode run_mode,
|
|
|
|
PikaImage *image,
|
|
|
|
gint n_drawables,
|
|
|
|
PikaDrawable **drawables,
|
|
|
|
GFile *file,
|
2023-10-30 23:55:30 +01:00
|
|
|
PikaMetadata *metadata,
|
|
|
|
PikaProcedureConfig *config,
|
2023-09-26 00:35:21 +02:00
|
|
|
gpointer run_data)
|
|
|
|
{
|
|
|
|
PikaPDBStatusType status = PIKA_PDB_SUCCESS;
|
|
|
|
PikaExportReturn export = PIKA_EXPORT_CANCEL;
|
|
|
|
PikaParasite *parasite;
|
|
|
|
PikaImage *orig_image;
|
|
|
|
GError *error = NULL;
|
2023-10-30 23:55:30 +01:00
|
|
|
|
|
|
|
PikaPixPipeParams gihparams = { 0, };
|
|
|
|
gchar *description = NULL;
|
|
|
|
gint spacing;
|
2023-09-26 00:35:21 +02:00
|
|
|
GBytes *rank_bytes;
|
2023-10-30 23:55:30 +01:00
|
|
|
const guint8 *rank;
|
|
|
|
gchar **selection;
|
2023-09-26 00:35:21 +02:00
|
|
|
|
|
|
|
orig_image = image;
|
|
|
|
|
2023-10-30 23:55:30 +01:00
|
|
|
if (run_mode == PIKA_RUN_INTERACTIVE)
|
2023-09-26 00:35:21 +02:00
|
|
|
{
|
2023-10-30 23:55:30 +01:00
|
|
|
GParamSpec *spec;
|
|
|
|
gint image_width;
|
|
|
|
gint image_height;
|
|
|
|
gint cell_width;
|
|
|
|
gint cell_height;
|
2023-09-26 00:35:21 +02:00
|
|
|
|
|
|
|
export = pika_export_image (&image, &n_drawables, &drawables, "GIH",
|
|
|
|
PIKA_EXPORT_CAN_HANDLE_RGB |
|
|
|
|
PIKA_EXPORT_CAN_HANDLE_GRAY |
|
|
|
|
PIKA_EXPORT_CAN_HANDLE_ALPHA |
|
|
|
|
PIKA_EXPORT_CAN_HANDLE_LAYERS);
|
|
|
|
|
|
|
|
if (export == PIKA_EXPORT_CANCEL)
|
|
|
|
return pika_procedure_new_return_values (procedure,
|
|
|
|
PIKA_PDB_CANCEL,
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
/* Possibly retrieve data */
|
|
|
|
parasite = pika_image_get_parasite (orig_image,
|
|
|
|
"pika-brush-pipe-name");
|
|
|
|
if (parasite)
|
|
|
|
{
|
|
|
|
gchar *parasite_data;
|
|
|
|
guint32 parasite_size;
|
|
|
|
|
|
|
|
parasite_data = (gchar *) pika_parasite_get_data (parasite, ¶site_size);
|
|
|
|
|
2023-10-30 23:55:30 +01:00
|
|
|
g_object_set (config, "description", parasite_data , NULL);
|
2023-09-26 00:35:21 +02:00
|
|
|
|
|
|
|
pika_parasite_free (parasite);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
gchar *name = g_path_get_basename (pika_file_get_utf8_name (file));
|
|
|
|
|
|
|
|
if (g_str_has_suffix (name, ".gih"))
|
|
|
|
name[strlen (name) - 4] = '\0';
|
|
|
|
|
|
|
|
if (strlen (name))
|
2023-10-30 23:55:30 +01:00
|
|
|
g_object_set (config, "description", name, NULL);
|
2023-09-26 00:35:21 +02:00
|
|
|
|
|
|
|
g_free (name);
|
|
|
|
}
|
|
|
|
|
2023-10-30 23:55:30 +01:00
|
|
|
g_object_get (config,
|
|
|
|
"cell-width", &cell_width,
|
|
|
|
"cell-height", &cell_height,
|
|
|
|
NULL);
|
2023-09-26 00:35:21 +02:00
|
|
|
|
2023-10-30 23:55:30 +01:00
|
|
|
image_width = pika_image_get_width (image);
|
|
|
|
image_height = pika_image_get_height (image);
|
2023-09-26 00:35:21 +02:00
|
|
|
|
2023-10-30 23:55:30 +01:00
|
|
|
spec = g_object_class_find_property (G_OBJECT_GET_CLASS (config), "cell-width");
|
|
|
|
G_PARAM_SPEC_INT (spec)->default_value = image_width;
|
|
|
|
G_PARAM_SPEC_INT (spec)->maximum = image_width;
|
2023-09-26 00:35:21 +02:00
|
|
|
|
2023-10-30 23:55:30 +01:00
|
|
|
spec = g_object_class_find_property (G_OBJECT_GET_CLASS (config), "cell-height");
|
|
|
|
G_PARAM_SPEC_INT (spec)->default_value = image_height;
|
|
|
|
G_PARAM_SPEC_INT (spec)->maximum = image_height;
|
2023-09-26 00:35:21 +02:00
|
|
|
|
2023-10-30 23:55:30 +01:00
|
|
|
if (cell_width == 1 && cell_height == 1)
|
2023-09-26 00:35:21 +02:00
|
|
|
{
|
2023-10-30 23:55:30 +01:00
|
|
|
g_object_set (config,
|
|
|
|
"cell-width", image_width,
|
|
|
|
"cell-height", image_height,
|
|
|
|
NULL);
|
2023-09-26 00:35:21 +02:00
|
|
|
|
2023-10-30 23:55:30 +01:00
|
|
|
spec = g_object_class_find_property (G_OBJECT_GET_CLASS (config), "num-cells");
|
|
|
|
G_PARAM_SPEC_INT (spec)->maximum = 1;
|
2023-09-26 00:35:21 +02:00
|
|
|
}
|
|
|
|
|
2023-10-30 23:55:30 +01:00
|
|
|
pika_ui_init (PLUG_IN_BINARY);
|
2023-09-26 00:35:21 +02:00
|
|
|
|
2023-10-30 23:55:30 +01:00
|
|
|
if (! gih_save_dialog (image, procedure, config))
|
2023-09-26 00:35:21 +02:00
|
|
|
{
|
|
|
|
status = PIKA_PDB_CANCEL;
|
|
|
|
goto out;
|
|
|
|
}
|
2023-10-30 23:55:30 +01:00
|
|
|
}
|
2023-09-26 00:35:21 +02:00
|
|
|
|
2023-10-30 23:55:30 +01:00
|
|
|
pika_pixpipe_params_init (&gihparams);
|
2023-09-26 00:35:21 +02:00
|
|
|
|
2023-10-30 23:55:30 +01:00
|
|
|
g_object_get (config,
|
|
|
|
"spacing", &spacing,
|
|
|
|
"description", &description,
|
|
|
|
"cell-width", &gihparams.cellwidth,
|
|
|
|
"cell-height", &gihparams.cellheight,
|
|
|
|
"num-cells", &gihparams.ncells,
|
|
|
|
"ranks", &rank_bytes,
|
|
|
|
"selection-modes", &selection,
|
|
|
|
NULL);
|
2023-09-26 00:35:21 +02:00
|
|
|
|
2023-10-30 23:55:30 +01:00
|
|
|
gihparams.cols = pika_image_get_width (image) / gihparams.cellwidth;
|
|
|
|
gihparams.rows = pika_image_get_height (image) / gihparams.cellheight;
|
2023-09-26 00:35:21 +02:00
|
|
|
|
2023-10-30 23:55:30 +01:00
|
|
|
rank = g_bytes_get_data (rank_bytes, NULL);
|
|
|
|
gihparams.dim = g_bytes_get_size (rank_bytes);
|
|
|
|
|
|
|
|
for (gint i = 0; i < gihparams.dim; i++)
|
|
|
|
{
|
|
|
|
gihparams.rank[i] = rank[i];
|
|
|
|
gihparams.selection[i] = g_strdup (selection[i]);
|
2023-09-26 00:35:21 +02:00
|
|
|
}
|
|
|
|
|
2023-10-30 23:55:30 +01:00
|
|
|
g_strfreev (selection);
|
|
|
|
g_bytes_unref (rank_bytes);
|
|
|
|
|
2023-09-26 00:35:21 +02:00
|
|
|
if (status == PIKA_PDB_SUCCESS)
|
|
|
|
{
|
2023-10-30 23:55:30 +01:00
|
|
|
PikaProcedure *procedure;
|
|
|
|
PikaValueArray *save_retvals;
|
|
|
|
PikaObjectArray *drawables_array;
|
|
|
|
gchar *paramstring;
|
2023-09-26 00:35:21 +02:00
|
|
|
|
|
|
|
paramstring = pika_pixpipe_params_build (&gihparams);
|
|
|
|
|
2023-10-30 23:55:30 +01:00
|
|
|
drawables_array = pika_object_array_new (PIKA_TYPE_DRAWABLE, (GObject **) drawables,
|
|
|
|
n_drawables, FALSE);
|
|
|
|
procedure = pika_pdb_lookup_procedure (pika_get_pdb (),
|
|
|
|
"file-gih-save-internal");
|
|
|
|
save_retvals = pika_procedure_run (procedure,
|
|
|
|
"image", image,
|
|
|
|
"num-drawables", n_drawables,
|
|
|
|
"drawables", drawables_array,
|
|
|
|
"file", file,
|
|
|
|
"spacing", spacing,
|
|
|
|
"name", description,
|
|
|
|
"params", paramstring,
|
|
|
|
NULL);
|
|
|
|
pika_object_array_free (drawables_array);
|
2023-09-26 00:35:21 +02:00
|
|
|
|
|
|
|
if (PIKA_VALUES_GET_ENUM (save_retvals, 0) == PIKA_PDB_SUCCESS)
|
|
|
|
{
|
|
|
|
parasite = pika_parasite_new ("pika-brush-pipe-name",
|
|
|
|
PIKA_PARASITE_PERSISTENT,
|
2023-10-30 23:55:30 +01:00
|
|
|
strlen (description) + 1,
|
|
|
|
description);
|
2023-09-26 00:35:21 +02:00
|
|
|
pika_image_attach_parasite (orig_image, parasite);
|
|
|
|
pika_parasite_free (parasite);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
g_set_error (&error, 0, 0,
|
|
|
|
"Running procedure 'file-gih-save-internal' "
|
|
|
|
"failed: %s",
|
|
|
|
pika_pdb_get_last_error (pika_get_pdb ()));
|
|
|
|
|
|
|
|
status = PIKA_PDB_EXECUTION_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
g_free (paramstring);
|
|
|
|
}
|
|
|
|
|
|
|
|
pika_pixpipe_params_free (&gihparams);
|
2023-10-30 23:55:30 +01:00
|
|
|
g_free (description);
|
2023-09-26 00:35:21 +02:00
|
|
|
|
|
|
|
out:
|
|
|
|
if (export == PIKA_EXPORT_EXPORT)
|
|
|
|
{
|
|
|
|
pika_image_delete (image);
|
|
|
|
g_free (drawables);
|
|
|
|
}
|
|
|
|
|
|
|
|
return pika_procedure_new_return_values (procedure, status, error);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* save routines */
|
|
|
|
|
|
|
|
static void
|
2023-10-30 23:55:30 +01:00
|
|
|
gih_remove_guides (PikaProcedureConfig *config,
|
|
|
|
PikaImage *image)
|
2023-09-26 00:35:21 +02:00
|
|
|
{
|
2023-10-30 23:55:30 +01:00
|
|
|
PikaArray *array;
|
2023-09-26 00:35:21 +02:00
|
|
|
|
2023-10-30 23:55:30 +01:00
|
|
|
g_object_get (config, "guides", &array, NULL);
|
2023-09-26 00:35:21 +02:00
|
|
|
|
2023-10-30 23:55:30 +01:00
|
|
|
if (array != NULL)
|
2023-09-26 00:35:21 +02:00
|
|
|
{
|
2023-10-30 23:55:30 +01:00
|
|
|
gint32 *guides = (gint32 *) array->data;
|
|
|
|
gint n_guides = array->length / 4;
|
|
|
|
|
|
|
|
for (gint i = 0; i < n_guides; i++)
|
|
|
|
pika_image_delete_guide (image, guides[i]);
|
2023-09-26 00:35:21 +02:00
|
|
|
}
|
2023-10-30 23:55:30 +01:00
|
|
|
g_clear_pointer (&array, pika_array_free);
|
|
|
|
|
|
|
|
g_object_set (config, "guides", NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gih_cell_width_notify (PikaProcedureConfig *config,
|
|
|
|
const GParamSpec *pspec,
|
|
|
|
PikaProcedureDialog *dialog)
|
|
|
|
{
|
|
|
|
PikaImage *image;
|
|
|
|
GParamSpec *spec;
|
|
|
|
gchar *info_text;
|
|
|
|
gchar *grid_text = NULL;
|
|
|
|
const gchar *width_warning = _("Width Mismatch!");
|
|
|
|
gchar *height_warning = _("Height Mismatch!");
|
|
|
|
GtkWidget *widget;
|
|
|
|
GtkAdjustment *rank0;
|
|
|
|
gint dimension;
|
|
|
|
|
|
|
|
gint cell_width;
|
|
|
|
gint image_width;
|
|
|
|
gint n_horiz_cells;
|
|
|
|
gint n_vert_cells;
|
|
|
|
|
|
|
|
gint cell_height;
|
|
|
|
gint image_height;
|
|
|
|
gint n_vert_guides;
|
|
|
|
gint n_horiz_guides;
|
|
|
|
|
|
|
|
gint old_max_cells;
|
|
|
|
gint max_cells;
|
|
|
|
gint num_cells;
|
|
|
|
|
|
|
|
image = g_object_get_data (G_OBJECT (dialog), "image");
|
|
|
|
rank0 = g_object_get_data (G_OBJECT (dialog), "rank0");
|
|
|
|
|
|
|
|
g_object_get (config,
|
|
|
|
"cell-width", &cell_width,
|
|
|
|
"cell-height", &cell_height,
|
|
|
|
"num-cells", &num_cells,
|
|
|
|
"dimension", &dimension,
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
spec = g_object_class_find_property (G_OBJECT_GET_CLASS (config), "cell-width");
|
|
|
|
image_width = G_PARAM_SPEC_INT (spec)->maximum;
|
|
|
|
n_horiz_cells = image_width / cell_width;
|
|
|
|
n_vert_guides = n_horiz_cells - 1;
|
|
|
|
|
|
|
|
if (cell_width * n_horiz_cells == image_width)
|
|
|
|
width_warning = NULL;
|
|
|
|
|
|
|
|
spec = g_object_class_find_property (G_OBJECT_GET_CLASS (config), "cell-height");
|
|
|
|
image_height = G_PARAM_SPEC_INT (spec)->maximum;
|
|
|
|
n_vert_cells = image_height / cell_height;
|
|
|
|
n_horiz_guides = n_vert_cells - 1;
|
|
|
|
|
|
|
|
if (cell_height * n_vert_cells == image_height)
|
|
|
|
height_warning = NULL;
|
|
|
|
|
|
|
|
/* TRANSLATORS: \xc3\x97 is the UTF-8 encoding for the multiplication sign. */
|
|
|
|
if (n_horiz_guides + n_vert_guides > 0)
|
|
|
|
grid_text = g_strdup_printf (_("Displays as a %d \xc3\x97 %d grid on each layer"),
|
|
|
|
n_horiz_cells, n_vert_cells);
|
|
|
|
info_text = g_strdup_printf ("%s%s%s%s%s",
|
|
|
|
grid_text != NULL ? grid_text : "",
|
|
|
|
grid_text != NULL && width_warning != NULL ? " / " : "",
|
|
|
|
width_warning != NULL ? width_warning : "",
|
|
|
|
(grid_text != NULL || width_warning != NULL) && height_warning != NULL ? " / " : "",
|
|
|
|
height_warning != NULL ? height_warning : "");
|
|
|
|
|
|
|
|
spec = g_object_class_find_property (G_OBJECT_GET_CLASS (config), "num-cells");
|
|
|
|
old_max_cells = G_PARAM_SPEC_INT (spec)->maximum;
|
|
|
|
max_cells = n_horiz_cells * n_vert_cells;
|
|
|
|
|
|
|
|
if (num_cells == old_max_cells)
|
|
|
|
num_cells = max_cells;
|
2023-09-26 00:35:21 +02:00
|
|
|
else
|
2023-10-30 23:55:30 +01:00
|
|
|
num_cells = MIN (max_cells, num_cells);
|
|
|
|
|
|
|
|
G_PARAM_SPEC_INT (spec)->maximum = max_cells;
|
|
|
|
widget = pika_procedure_dialog_get_widget (dialog, "num-cells", G_TYPE_NONE);
|
|
|
|
g_object_set (widget, "upper", (gdouble) max_cells, NULL);
|
|
|
|
pika_label_spin_set_increments (PIKA_LABEL_SPIN (widget), 1.0, 1.0);
|
|
|
|
|
|
|
|
gih_remove_guides (config, image);
|
|
|
|
|
|
|
|
widget = pika_procedure_dialog_get_widget (dialog, "info-text", G_TYPE_NONE);
|
|
|
|
gtk_widget_set_visible (widget, (strlen (info_text) > 0));
|
|
|
|
if (n_horiz_guides + n_vert_guides > 0)
|
2023-09-26 00:35:21 +02:00
|
|
|
{
|
2023-10-30 23:55:30 +01:00
|
|
|
gint32 *guides = NULL;
|
|
|
|
GValue value = G_VALUE_INIT;
|
2023-09-26 00:35:21 +02:00
|
|
|
|
2023-10-30 23:55:30 +01:00
|
|
|
guides = g_new0 (gint32, n_horiz_guides + n_vert_guides);
|
2023-09-26 00:35:21 +02:00
|
|
|
|
2023-10-30 23:55:30 +01:00
|
|
|
for (gint i = 0; i < n_vert_guides; i++)
|
|
|
|
guides[i] = pika_image_add_vguide (image, cell_width * (i + 1));
|
2023-09-26 00:35:21 +02:00
|
|
|
|
2023-10-30 23:55:30 +01:00
|
|
|
for (gint i = 0; i < n_horiz_guides; i++)
|
|
|
|
guides[n_vert_guides + i] = pika_image_add_hguide (image, cell_height * (i + 1));
|
2023-09-26 00:35:21 +02:00
|
|
|
|
2023-10-30 23:55:30 +01:00
|
|
|
g_value_init (&value, PIKA_TYPE_INT32_ARRAY);
|
|
|
|
pika_value_set_int32_array (&value, guides, n_horiz_guides + n_vert_guides);
|
|
|
|
g_object_set_property (G_OBJECT (config), "guides", &value);
|
|
|
|
g_free (guides);
|
|
|
|
}
|
|
|
|
|
|
|
|
g_object_set (config,
|
|
|
|
"info-text", info_text,
|
|
|
|
"num-cells", num_cells,
|
|
|
|
NULL);
|
|
|
|
if (rank0 != NULL && dimension == 1)
|
|
|
|
gtk_adjustment_set_value (rank0, num_cells);
|
|
|
|
|
|
|
|
g_free (info_text);
|
|
|
|
g_free (grid_text);
|
2023-09-26 00:35:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2023-10-30 23:55:30 +01:00
|
|
|
gih_selection_mode_changed (GtkWidget *widget,
|
|
|
|
gpointer data)
|
2023-09-26 00:35:21 +02:00
|
|
|
{
|
|
|
|
gint index;
|
|
|
|
|
|
|
|
index = gtk_combo_box_get_active (GTK_COMBO_BOX (widget));
|
|
|
|
|
2023-10-30 23:55:30 +01:00
|
|
|
g_free (*((gchar **) data));
|
|
|
|
*((gchar **) data) = g_strdup (selection_modes [index]);
|
2023-09-26 00:35:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2023-10-30 23:55:30 +01:00
|
|
|
gih_dimension_notify (PikaProcedureConfig *config,
|
|
|
|
const GParamSpec *pspec,
|
|
|
|
SizeAdjustmentData *data)
|
2023-09-26 00:35:21 +02:00
|
|
|
{
|
2023-10-30 23:55:30 +01:00
|
|
|
gint dimension;
|
2023-09-26 00:35:21 +02:00
|
|
|
gint i;
|
|
|
|
|
2023-10-30 23:55:30 +01:00
|
|
|
g_object_get (config, "dimension", &dimension, NULL);
|
2023-09-26 00:35:21 +02:00
|
|
|
|
|
|
|
for (i = 0; i < PIKA_PIXPIPE_MAXDIM; i++)
|
|
|
|
{
|
2023-10-30 23:55:30 +01:00
|
|
|
gtk_widget_set_sensitive (data->rank_entry[i], i < dimension);
|
|
|
|
gtk_widget_set_sensitive (data->mode_entry[i], i < dimension);
|
2023-09-26 00:35:21 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
2023-10-30 23:55:30 +01:00
|
|
|
gih_save_dialog (PikaImage *image,
|
|
|
|
PikaProcedure *procedure,
|
|
|
|
PikaProcedureConfig *config)
|
2023-09-26 00:35:21 +02:00
|
|
|
{
|
2023-10-30 23:55:30 +01:00
|
|
|
GtkWidget *dialog;
|
|
|
|
GtkWidget *dimgrid;
|
|
|
|
GtkWidget *label;
|
|
|
|
GtkAdjustment *adjustment;
|
|
|
|
GtkWidget *spinbutton;
|
|
|
|
GtkWidget *cb;
|
|
|
|
gint i;
|
|
|
|
SizeAdjustmentData cellw_adjust;
|
|
|
|
SizeAdjustmentData cellh_adjust;
|
|
|
|
gchar **selection;
|
|
|
|
GBytes *rank_bytes = NULL;
|
|
|
|
guint8 rank[PIKA_PIXPIPE_MAXDIM];
|
|
|
|
gint num_cells;
|
|
|
|
gint dimension;
|
|
|
|
gboolean run;
|
|
|
|
|
|
|
|
dialog = pika_save_procedure_dialog_new (PIKA_SAVE_PROCEDURE (procedure),
|
|
|
|
PIKA_PROCEDURE_CONFIG (config),
|
|
|
|
image);
|
|
|
|
|
|
|
|
gtk_widget_set_halign (pika_procedure_dialog_get_label (PIKA_PROCEDURE_DIALOG (dialog),
|
|
|
|
"info-text", NULL, TRUE, FALSE),
|
|
|
|
GTK_ALIGN_START);
|
|
|
|
pika_procedure_dialog_fill (PIKA_PROCEDURE_DIALOG (dialog),
|
|
|
|
"description", "spacing",
|
|
|
|
"cell-width", "cell-height", "num-cells",
|
|
|
|
"info-text", "dimension", NULL);
|
|
|
|
|
|
|
|
g_object_set_data (G_OBJECT (dialog), "image", image);
|
|
|
|
g_signal_connect (config, "notify::cell-width",
|
|
|
|
G_CALLBACK (gih_cell_width_notify),
|
|
|
|
dialog);
|
|
|
|
g_signal_connect (config, "notify::cell-height",
|
|
|
|
G_CALLBACK (gih_cell_width_notify),
|
|
|
|
dialog);
|
|
|
|
gih_cell_width_notify (config, NULL, PIKA_PROCEDURE_DIALOG (dialog));
|
|
|
|
|
|
|
|
g_signal_connect (config, "notify::dimension",
|
|
|
|
G_CALLBACK (gih_dimension_notify),
|
|
|
|
&cellw_adjust);
|
2023-09-26 00:35:21 +02:00
|
|
|
|
|
|
|
/*
|
2023-10-30 23:55:30 +01:00
|
|
|
* Ranks / Selection: ______ ______ ______ ______ ______
|
2023-09-26 00:35:21 +02:00
|
|
|
*/
|
|
|
|
|
2023-10-30 23:55:30 +01:00
|
|
|
g_object_get (config,
|
|
|
|
"selection-modes", &selection,
|
|
|
|
"ranks", &rank_bytes,
|
|
|
|
"num-cells", &num_cells,
|
|
|
|
NULL);
|
|
|
|
if (selection == NULL || g_strv_length (selection) != PIKA_PIXPIPE_MAXDIM)
|
|
|
|
{
|
|
|
|
GStrvBuilder *builder = g_strv_builder_new ();
|
|
|
|
gint old_len;
|
2023-09-26 00:35:21 +02:00
|
|
|
|
2023-10-30 23:55:30 +01:00
|
|
|
old_len = (selection == NULL ? 0 : g_strv_length (selection));
|
2023-09-26 00:35:21 +02:00
|
|
|
|
2023-10-30 23:55:30 +01:00
|
|
|
for (i = 0; i < MIN (old_len, PIKA_PIXPIPE_MAXDIM); i++)
|
|
|
|
g_strv_builder_add (builder, selection[i]);
|
2023-09-26 00:35:21 +02:00
|
|
|
|
2023-10-30 23:55:30 +01:00
|
|
|
for (i = old_len; i < PIKA_PIXPIPE_MAXDIM; i++)
|
|
|
|
g_strv_builder_add (builder, "random");
|
2023-09-26 00:35:21 +02:00
|
|
|
|
2023-10-30 23:55:30 +01:00
|
|
|
g_strfreev (selection);
|
|
|
|
selection = g_strv_builder_end (builder);
|
|
|
|
g_strv_builder_unref (builder);
|
|
|
|
}
|
2023-09-26 00:35:21 +02:00
|
|
|
|
2023-10-30 23:55:30 +01:00
|
|
|
if (rank_bytes == NULL)
|
|
|
|
{
|
|
|
|
dimension = 1;
|
2023-09-26 00:35:21 +02:00
|
|
|
|
2023-10-30 23:55:30 +01:00
|
|
|
rank[0] = 1;
|
|
|
|
for (i = 1; i < PIKA_PIXPIPE_MAXDIM; i++)
|
|
|
|
rank[i] = 0;
|
|
|
|
}
|
2023-09-26 00:35:21 +02:00
|
|
|
else
|
2023-10-30 23:55:30 +01:00
|
|
|
{
|
|
|
|
const guint8 *stored;
|
2023-09-26 00:35:21 +02:00
|
|
|
|
2023-10-30 23:55:30 +01:00
|
|
|
stored = g_bytes_get_data (rank_bytes, NULL);
|
|
|
|
dimension = g_bytes_get_size (rank_bytes);
|
2023-09-26 00:35:21 +02:00
|
|
|
|
2023-10-30 23:55:30 +01:00
|
|
|
for (i = 0; i < dimension; i++)
|
|
|
|
rank[i] = stored[i];
|
2023-09-26 00:35:21 +02:00
|
|
|
|
2023-10-30 23:55:30 +01:00
|
|
|
for (i = dimension; i < PIKA_PIXPIPE_MAXDIM; i++)
|
|
|
|
rank[i] = 1;
|
|
|
|
}
|
|
|
|
g_bytes_unref (rank_bytes);
|
|
|
|
dimension = MAX (dimension, 1);
|
2023-09-26 00:35:21 +02:00
|
|
|
|
|
|
|
dimgrid = gtk_grid_new ();
|
|
|
|
gtk_grid_set_column_spacing (GTK_GRID (dimgrid), 4);
|
2023-10-30 23:55:30 +01:00
|
|
|
|
|
|
|
label = gtk_label_new (_("Ranks:"));
|
|
|
|
gtk_grid_attach (GTK_GRID (dimgrid), label, 0, 0, 1, 1);
|
|
|
|
gtk_widget_show (label);
|
|
|
|
|
2023-09-26 00:35:21 +02:00
|
|
|
for (i = 0; i < PIKA_PIXPIPE_MAXDIM; i++)
|
|
|
|
{
|
|
|
|
gsize j;
|
|
|
|
|
2023-10-30 23:55:30 +01:00
|
|
|
adjustment = gtk_adjustment_new (rank[i], 1, 100, 1, 1, 0);
|
|
|
|
if (i == 0)
|
|
|
|
g_object_set_data (G_OBJECT (dialog), "rank0", adjustment);
|
|
|
|
|
2023-09-26 00:35:21 +02:00
|
|
|
spinbutton = pika_spin_button_new (adjustment, 1.0, 0);
|
|
|
|
gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
|
2023-10-30 23:55:30 +01:00
|
|
|
gtk_grid_attach (GTK_GRID (dimgrid), spinbutton, 1, i, 1, 1);
|
2023-09-26 00:35:21 +02:00
|
|
|
|
|
|
|
gtk_widget_show (spinbutton);
|
|
|
|
|
2023-10-30 23:55:30 +01:00
|
|
|
if (i >= dimension)
|
2023-09-26 00:35:21 +02:00
|
|
|
gtk_widget_set_sensitive (spinbutton, FALSE);
|
|
|
|
|
|
|
|
g_signal_connect (adjustment, "value-changed",
|
|
|
|
G_CALLBACK (pika_int_adjustment_update),
|
2023-10-30 23:55:30 +01:00
|
|
|
&rank[i]);
|
2023-09-26 00:35:21 +02:00
|
|
|
|
|
|
|
cellw_adjust.rank_entry[i] = cellh_adjust.rank_entry[i] = spinbutton;
|
|
|
|
|
|
|
|
cb = gtk_combo_box_text_new ();
|
|
|
|
|
|
|
|
for (j = 0; j < G_N_ELEMENTS (selection_modes); j++)
|
|
|
|
gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (cb),
|
|
|
|
selection_modes[j]);
|
|
|
|
gtk_combo_box_set_active (GTK_COMBO_BOX (cb), 2); /* random */
|
|
|
|
|
2023-10-30 23:55:30 +01:00
|
|
|
for (j = 0; j < G_N_ELEMENTS (selection_modes); j++)
|
|
|
|
if (!strcmp (selection[i], selection_modes[j]))
|
|
|
|
{
|
|
|
|
gtk_combo_box_set_active (GTK_COMBO_BOX (cb), j);
|
|
|
|
break;
|
|
|
|
}
|
2023-09-26 00:35:21 +02:00
|
|
|
|
2023-10-30 23:55:30 +01:00
|
|
|
gtk_grid_attach (GTK_GRID (dimgrid), cb, 2, i, 1, 1);
|
2023-09-26 00:35:21 +02:00
|
|
|
|
|
|
|
gtk_widget_show (cb);
|
|
|
|
|
2023-10-30 23:55:30 +01:00
|
|
|
if (i >= dimension)
|
2023-09-26 00:35:21 +02:00
|
|
|
gtk_widget_set_sensitive (cb, FALSE);
|
|
|
|
|
|
|
|
g_signal_connect (GTK_COMBO_BOX (cb), "changed",
|
2023-10-30 23:55:30 +01:00
|
|
|
G_CALLBACK (gih_selection_mode_changed),
|
|
|
|
&selection[i]);
|
2023-09-26 00:35:21 +02:00
|
|
|
|
|
|
|
cellw_adjust.mode_entry[i] = cellh_adjust.mode_entry[i] = cb;
|
|
|
|
}
|
|
|
|
|
2023-10-30 23:55:30 +01:00
|
|
|
gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
|
|
|
|
dimgrid, TRUE, TRUE, 0);
|
|
|
|
gtk_widget_show (dimgrid);
|
2023-09-26 00:35:21 +02:00
|
|
|
|
2023-10-30 23:55:30 +01:00
|
|
|
run = pika_procedure_dialog_run (PIKA_PROCEDURE_DIALOG (dialog));
|
2023-09-26 00:35:21 +02:00
|
|
|
|
2023-10-30 23:55:30 +01:00
|
|
|
g_object_get (config, "dimension", &dimension, NULL);
|
|
|
|
rank_bytes = g_bytes_new (rank, sizeof (guint8) * dimension);
|
2023-09-26 00:35:21 +02:00
|
|
|
|
2023-10-30 23:55:30 +01:00
|
|
|
g_object_set (config,
|
|
|
|
"selection-modes", selection,
|
|
|
|
"ranks", rank_bytes,
|
|
|
|
NULL);
|
2023-09-26 00:35:21 +02:00
|
|
|
|
2023-10-30 23:55:30 +01:00
|
|
|
gih_remove_guides (config, image);
|
2023-09-26 00:35:21 +02:00
|
|
|
gtk_widget_destroy (dialog);
|
|
|
|
|
2023-10-30 23:55:30 +01:00
|
|
|
g_strfreev (selection);
|
|
|
|
g_bytes_unref (rank_bytes);
|
2023-09-26 00:35:21 +02:00
|
|
|
|
|
|
|
return run;
|
|
|
|
}
|