Initial checkin of Pika from heckimp

This commit is contained in:
2023-09-25 15:35:21 -07:00
commit 891e999216
6761 changed files with 5240685 additions and 0 deletions

View File

@ -0,0 +1,797 @@
/* align_layers.c
* Author: Shuji Narazaki <narazaki@InetQ.or.jp>
* Version: 0.26
*
* Copyright (C) 1997-1998 Shuji Narazaki <narazaki@InetQ.or.jp>
*
* 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/>.
*/
#include "config.h"
#include <libpika/pika.h>
#include <libpika/pikaui.h>
#include "libpika/stdplugins-intl.h"
#define PLUG_IN_PROC "plug-in-align-layers"
#define PLUG_IN_BINARY "align-layers"
#define PLUG_IN_ROLE "pika-align-layers"
enum
{
H_NONE,
H_COLLECT,
LEFT2RIGHT,
RIGHT2LEFT,
SNAP2HGRID
};
enum
{
H_BASE_LEFT,
H_BASE_CENTER,
H_BASE_RIGHT
};
enum
{
V_NONE,
V_COLLECT,
TOP2BOTTOM,
BOTTOM2TOP,
SNAP2VGRID
};
enum
{
V_BASE_TOP,
V_BASE_CENTER,
V_BASE_BOTTOM
};
typedef struct
{
gint step_x;
gint step_y;
gint base_x;
gint base_y;
} AlignData;
typedef struct _AlignLayers AlignLayers;
typedef struct _AlignLayersClass AlignLayersClass;
struct _AlignLayers
{
PikaPlugIn parent_instance;
};
struct _AlignLayersClass
{
PikaPlugInClass parent_class;
};
#define ALIGN_LAYERS_TYPE (align_layers_get_type ())
#define ALIGN_LAYERS (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ALIGN_LAYERS_TYPE, AlignLayers))
GType align_layers_get_type (void) G_GNUC_CONST;
static GList * align_layers_query_procedures (PikaPlugIn *plug_in);
static PikaProcedure * align_layers_create_procedure (PikaPlugIn *plug_in,
const gchar *name);
static PikaValueArray * align_layers_run (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
const PikaValueArray *args,
gpointer run_data);
/* Main function */
static PikaPDBStatusType align_layers (PikaImage *image,
GObject *config);
/* Helpers and internal functions */
static gint align_layers_count_visibles_layers (GList *layers);
static PikaLayer * align_layers_find_last_layer (GList *layers,
gboolean *found);
static gint align_layers_spread_visibles_layers (GList *layers,
PikaLayer **layers_array);
static PikaLayer ** align_layers_spread_image (PikaImage *image,
gint *layer_num);
static PikaLayer * align_layers_find_background (PikaImage *image);
static AlignData align_layers_gather_data (PikaLayer **layers,
gint layer_num,
PikaLayer *background,
GObject *config);
static void align_layers_perform_alignment (PikaLayer **layers,
gint layer_num,
AlignData data,
GObject *config);
static void align_layers_get_align_offsets (PikaDrawable *drawable,
gint *x,
gint *y,
GObject *config);
static gint align_layers_dialog (PikaProcedure *procedure,
GObject *config);
G_DEFINE_TYPE (AlignLayers, align_layers, PIKA_TYPE_PLUG_IN)
PIKA_MAIN (ALIGN_LAYERS_TYPE)
DEFINE_STD_SET_I18N
static void
align_layers_class_init (AlignLayersClass *klass)
{
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
plug_in_class->query_procedures = align_layers_query_procedures;
plug_in_class->create_procedure = align_layers_create_procedure;
plug_in_class->set_i18n = STD_SET_I18N;
}
static void
align_layers_init (AlignLayers *film)
{
}
static GList *
align_layers_query_procedures (PikaPlugIn *plug_in)
{
return g_list_append (NULL, g_strdup (PLUG_IN_PROC));
}
static PikaProcedure *
align_layers_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,
align_layers_run, NULL, NULL);
pika_procedure_set_image_types (procedure, "*");
pika_procedure_set_sensitivity_mask (procedure,
PIKA_PROCEDURE_SENSITIVE_DRAWABLE |
PIKA_PROCEDURE_SENSITIVE_DRAWABLES |
PIKA_PROCEDURE_SENSITIVE_NO_DRAWABLES);
pika_procedure_set_menu_label (procedure, _("Align Visi_ble Layers..."));
pika_procedure_add_menu_path (procedure, "<Image>/Image/[Arrange]");
pika_procedure_set_documentation (procedure,
_("Align all visible layers of the image"),
"Align visible layers",
name);
pika_procedure_set_attribution (procedure,
"Shuji Narazaki <narazaki@InetQ.or.jp>",
"Shuji Narazaki",
"1997");
PIKA_PROC_ARG_INT (procedure, "horizontal-style",
_("_Horizontal style"),
_("(None = 0, Collect = 1, "
"Fill left to right = 2, Fill right to left = 3, "
"Snap to grid = 4)"),
H_NONE, SNAP2HGRID, H_NONE,
PIKA_PARAM_READWRITE);
PIKA_PROC_ARG_INT (procedure, "horizontal-base",
_("Hori_zontal base"),
_("(Left edge = 0, Center = 1, "
"Right edge = 2)"),
H_BASE_LEFT, H_BASE_RIGHT, H_BASE_LEFT,
PIKA_PARAM_READWRITE);
PIKA_PROC_ARG_INT (procedure, "vertical-style",
_("_Vertical style"),
_("(None = 0, Collect = 1, "
"Fill left to right = 2, Fill right to left = 3, "
"Snap to grid = 4)"),
V_NONE, SNAP2VGRID, V_NONE,
PIKA_PARAM_READWRITE);
PIKA_PROC_ARG_INT (procedure, "vertical-base",
_("Ver_tical base"),
_("(Left edge = 0, Center = 1, "
"Right edge = 2)"),
V_BASE_TOP, V_BASE_BOTTOM, V_BASE_TOP,
PIKA_PARAM_READWRITE);
PIKA_PROC_ARG_INT (procedure, "grid-size",
_("_Grid"),
_("Grid"),
1, 200, 10,
PIKA_PARAM_READWRITE);
PIKA_PROC_ARG_BOOLEAN (procedure,
"ignore-bottom-layer",
_("Ignore the _bottom layer even if visible"),
_("Ignore the bottom layer even if visible"),
TRUE,
G_PARAM_READWRITE);
PIKA_PROC_ARG_BOOLEAN (procedure,
"use-bottom-layer",
_("_Use the (invisible) bottom layer as the base"),
_("Use the (invisible) bottom layer as the base"),
FALSE,
G_PARAM_READWRITE);
/* TODO: these 2 arguments existed in the original procedure but
* were actually unused. If we want to keep them, let's at least
* implement the documented behavior, or else let's just drop
* them.
*/
/*
PIKA_PROC_ARG_BOOLEAN (procedure,
"link-after-alignment",
"Link the visible layers after alignment",
FALSE,
G_PARAM_READWRITE);
PIKA_PROC_ARG_BOOLEAN (procedure,
"use-bottom",
"use the bottom layer as the base of alignment",
TRUE,
G_PARAM_READWRITE);
*/
}
return procedure;
}
static PikaValueArray *
align_layers_run (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
const PikaValueArray *args,
gpointer run_data)
{
PikaProcedureConfig *config;
PikaValueArray *return_vals = NULL;
PikaPDBStatusType status = PIKA_PDB_EXECUTION_ERROR;
GError *error = NULL;
GList *layers;
gint layer_num;
config = pika_procedure_create_config (procedure);
pika_procedure_config_begin_run (config, NULL, run_mode, args);
switch ( run_mode )
{
case PIKA_RUN_INTERACTIVE:
layers = pika_image_list_layers (image);
layer_num = align_layers_count_visibles_layers (layers);
g_list_free (layers);
if (layer_num < 2)
{
g_set_error (&error, PIKA_PLUG_IN_ERROR, 0,
_("There are not enough layers to align."));
return pika_procedure_new_return_values (procedure,
PIKA_PDB_CALLING_ERROR,
error);
}
if (! align_layers_dialog (procedure, G_OBJECT (config)))
status = PIKA_PDB_CANCEL;
break;
case PIKA_RUN_NONINTERACTIVE:
case PIKA_RUN_WITH_LAST_VALS:
break;
}
if (status != PIKA_PDB_CANCEL)
{
status = align_layers (image, G_OBJECT (config));
if (run_mode != PIKA_RUN_NONINTERACTIVE)
pika_displays_flush ();
}
pika_procedure_config_end_run (config, status);
g_object_unref (config);
return_vals = pika_procedure_new_return_values (procedure, status,
error);
return return_vals;
}
/*
* Main function
*/
static PikaPDBStatusType
align_layers (PikaImage *image,
GObject *config)
{
gint layer_num = 0;
PikaLayer **layers = NULL;
PikaLayer *background = 0;
AlignData data;
gboolean ignore_bottom_layer;
g_object_get (config,
"ignore-bottom-layer", &ignore_bottom_layer,
NULL);
layers = align_layers_spread_image (image, &layer_num);
if (layer_num < 2)
{
g_free (layers);
return PIKA_PDB_EXECUTION_ERROR;
}
background = align_layers_find_background (image);
/* If we want to ignore the bottom layer and if it's visible */
if (ignore_bottom_layer && background == layers[layer_num - 1])
{
layer_num--;
}
data = align_layers_gather_data (layers,
layer_num,
background,
config);
pika_image_undo_group_start (image);
align_layers_perform_alignment (layers,
layer_num,
data,
config);
pika_image_undo_group_end (image);
g_free (layers);
return PIKA_PDB_SUCCESS;
}
/*
* Find the bottommost layer, visible or not
* The image must contain at least one layer.
*/
static PikaLayer *
align_layers_find_last_layer (GList *layers,
gboolean *found)
{
GList *last = g_list_last (layers);
for (; last; last = last->prev)
{
PikaItem *item = last->data;
if (pika_item_is_group (item))
{
GList *children;
PikaLayer *last_layer;
children = pika_item_list_children (item);
last_layer = align_layers_find_last_layer (children,
found);
g_list_free (children);
if (*found)
return last_layer;
}
else if (pika_item_is_layer (item))
{
*found = TRUE;
return PIKA_LAYER (item);
}
}
/* should never happen */
return NULL;
}
/*
* Return the bottom layer.
*/
static PikaLayer *
align_layers_find_background (PikaImage *image)
{
GList *layers;
PikaLayer *background;
gboolean found = FALSE;
layers = pika_image_list_layers (image);
background = align_layers_find_last_layer (layers, &found);
g_list_free (layers);
return background;
}
/*
* Fill layers_array with all visible layers.
* layers_array needs to be allocated before the call
*/
static gint
align_layers_spread_visibles_layers (GList *layers,
PikaLayer **layers_array)
{
GList *iter;
gint index = 0;
for (iter = layers; iter; iter = iter->next)
{
PikaItem *item = iter->data;
if (pika_item_get_visible (item))
{
if (pika_item_is_group (item))
{
GList *children;
children = pika_item_list_children (item);
index += align_layers_spread_visibles_layers (children,
&(layers_array[index]));
g_list_free (children);
}
else if (pika_item_is_layer (item))
{
layers_array[index] = PIKA_LAYER (item);
index++;
}
}
}
return index;
}
/*
* Return a contiguous array of all visible layers
*/
static PikaLayer **
align_layers_spread_image (PikaImage *image,
gint *layer_num)
{
GList *layers;
PikaLayer **layers_array;
layers = pika_image_list_layers (image);
*layer_num = align_layers_count_visibles_layers (layers);
layers_array = g_malloc (sizeof (PikaLayer *) * *layer_num);
align_layers_spread_visibles_layers (layers,
layers_array);
g_list_free (layers);
return layers_array;
}
static gint
align_layers_count_visibles_layers (GList *layers)
{
GList *iter;
gint count = 0;
for (iter = layers; iter; iter = iter->next)
{
PikaItem *item = iter->data;
if (pika_item_get_visible (item))
{
if (pika_item_is_group (item))
{
GList *children;
children = pika_item_list_children (item);
count += align_layers_count_visibles_layers (children);
g_list_free (children);
}
else if (pika_item_is_layer (item))
{
count += 1;
}
}
}
return count;
}
static AlignData
align_layers_gather_data (PikaLayer **layers,
gint layer_num,
PikaLayer *background,
GObject *config)
{
AlignData data;
gint min_x = G_MAXINT;
gint min_y = G_MAXINT;
gint max_x = G_MININT;
gint max_y = G_MININT;
gint index;
gint orig_x = 0;
gint orig_y = 0;
gint offset_x = 0;
gint offset_y = 0;
gint horizontal_style;
gint vertical_style;
gboolean use_bottom_layer;
g_object_get (config,
"horizontal-style", &horizontal_style,
"vertical-style", &vertical_style,
"use-bottom-layer", &use_bottom_layer,
NULL);
data.step_x = 0;
data.step_y = 0;
data.base_x = 0;
data.base_y = 0;
/* 0 is the top layer */
for (index = 0; index < layer_num; index++)
{
pika_drawable_get_offsets (PIKA_DRAWABLE (layers[index]), &orig_x, &orig_y);
align_layers_get_align_offsets (PIKA_DRAWABLE (layers[index]),
&offset_x,
&offset_y,
config);
orig_x += offset_x;
orig_y += offset_y;
min_x = MIN (min_x, orig_x);
max_x = MAX (max_x, orig_x);
min_y = MIN (min_y, orig_y);
max_y = MAX (max_y, orig_y);
}
if (use_bottom_layer)
{
pika_drawable_get_offsets (PIKA_DRAWABLE (background), &orig_x, &orig_y);
align_layers_get_align_offsets (PIKA_DRAWABLE (background),
&offset_x,
&offset_y,
config);
orig_x += offset_x;
orig_y += offset_y;
data.base_x = min_x = orig_x;
data.base_y = min_y = orig_y;
}
if (layer_num > 1)
{
data.step_x = (max_x - min_x) / (layer_num - 1);
data.step_y = (max_y - min_y) / (layer_num - 1);
}
if ((horizontal_style == LEFT2RIGHT) || (horizontal_style == RIGHT2LEFT))
data.base_x = min_x;
if ((vertical_style == TOP2BOTTOM) || (vertical_style == BOTTOM2TOP))
data.base_y = min_y;
return data;
}
/*
* Modifies position of each visible layers
* according to data.
*/
static void
align_layers_perform_alignment (PikaLayer **layers,
gint layer_num,
AlignData data,
GObject *config)
{
gint index;
gint horizontal_style;
gint vertical_style;
gint grid_size;
g_object_get (config,
"horizontal-style", &horizontal_style,
"vertical-style", &vertical_style,
"grid-size", &grid_size,
NULL);
for (index = 0; index < layer_num; index++)
{
gint x = 0;
gint y = 0;
gint orig_x;
gint orig_y;
gint offset_x;
gint offset_y;
pika_drawable_get_offsets (PIKA_DRAWABLE (layers[index]), &orig_x, &orig_y);
align_layers_get_align_offsets (PIKA_DRAWABLE (layers[index]),
&offset_x,
&offset_y,
config);
switch (horizontal_style)
{
case H_NONE:
x = orig_x;
break;
case H_COLLECT:
x = data.base_x - offset_x;
break;
case LEFT2RIGHT:
x = (data.base_x + index * data.step_x) - offset_x;
break;
case RIGHT2LEFT:
x = (data.base_x + (layer_num - index - 1) * data.step_x) - offset_x;
break;
case SNAP2HGRID:
x = grid_size
* (int) ((orig_x + offset_x + grid_size /2) / grid_size)
- offset_x;
break;
}
switch (vertical_style)
{
case V_NONE:
y = orig_y;
break;
case V_COLLECT:
y = data.base_y - offset_y;
break;
case TOP2BOTTOM:
y = (data.base_y + index * data.step_y) - offset_y;
break;
case BOTTOM2TOP:
y = (data.base_y + (layer_num - index - 1) * data.step_y) - offset_y;
break;
case SNAP2VGRID:
y = grid_size
* (int) ((orig_y + offset_y + grid_size / 2) / grid_size)
- offset_y;
break;
}
pika_layer_set_offsets (layers[index], x, y);
}
}
static void
align_layers_get_align_offsets (PikaDrawable *drawable,
gint *x,
gint *y,
GObject *config)
{
gint width = pika_drawable_get_width (drawable);
gint height = pika_drawable_get_height (drawable);
gint horizontal_base;
gint vertical_base;
g_object_get (config,
"horizontal-base", &horizontal_base,
"vertical-base", &vertical_base,
NULL);
switch (horizontal_base)
{
case H_BASE_LEFT:
*x = 0;
break;
case H_BASE_CENTER:
*x = width / 2;
break;
case H_BASE_RIGHT:
*x = width;
break;
default:
*x = 0;
break;
}
switch (vertical_base)
{
case V_BASE_TOP:
*y = 0;
break;
case V_BASE_CENTER:
*y = height / 2;
break;
case V_BASE_BOTTOM:
*y = height;
break;
default:
*y = 0;
break;
}
}
static int
align_layers_dialog (PikaProcedure *procedure,
GObject *config)
{
GtkWidget *dialog;
GtkListStore *store;
gboolean run;
pika_ui_init (PLUG_IN_BINARY);
dialog = pika_procedure_dialog_new (procedure,
PIKA_PROCEDURE_CONFIG (config),
_("Align Visible Layers"));
pika_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
GTK_RESPONSE_OK,
GTK_RESPONSE_CANCEL,
-1);
pika_window_set_transient (GTK_WINDOW (dialog));
store = pika_int_store_new (_("None"), H_NONE,
_("Collect"), H_COLLECT,
_("Fill (left to right)"), LEFT2RIGHT,
_("Fill (right to left)"), RIGHT2LEFT,
_("Snap to grid"), SNAP2HGRID,
NULL);
pika_procedure_dialog_get_int_combo (PIKA_PROCEDURE_DIALOG (dialog),
"horizontal-style",
PIKA_INT_STORE (store));
store = pika_int_store_new (_("Left edge"), H_BASE_LEFT,
_("Center"), H_BASE_CENTER,
_("Right edge"), H_BASE_RIGHT,
NULL);
pika_procedure_dialog_get_int_combo (PIKA_PROCEDURE_DIALOG (dialog),
"horizontal-base",
PIKA_INT_STORE (store));
store = pika_int_store_new (_("None"), V_NONE,
_("Collect"), V_COLLECT,
_("Fill (top to bottom)"), TOP2BOTTOM,
_("Fill (bottom to top)"), BOTTOM2TOP,
_("Snap to grid"), SNAP2VGRID,
NULL);
pika_procedure_dialog_get_int_combo (PIKA_PROCEDURE_DIALOG (dialog),
"vertical-style",
PIKA_INT_STORE (store));
store = pika_int_store_new (_("Top edge"), V_BASE_TOP,
_("Center"), V_BASE_CENTER,
_("Bottom edge"), V_BASE_BOTTOM,
NULL);
pika_procedure_dialog_get_int_combo (PIKA_PROCEDURE_DIALOG (dialog),
"vertical-base",
PIKA_INT_STORE (store));
pika_procedure_dialog_get_scale_entry (PIKA_PROCEDURE_DIALOG (dialog),
"grid-size", 1.0);
pika_procedure_dialog_fill (PIKA_PROCEDURE_DIALOG (dialog),
NULL);
gtk_widget_show (dialog);
run = pika_procedure_dialog_run (PIKA_PROCEDURE_DIALOG (dialog));
gtk_widget_destroy (dialog);
return run;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

778
plug-ins/common/blinds.c Normal file
View File

@ -0,0 +1,778 @@
/*
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* This is a plug-in for PIKA.
*
* Blinds plug-in. Distort an image as though it was stuck to
* window blinds and the blinds where opened/closed.
*
* Copyright (C) 1997 Andy Thomas alt@picnic.demon.co.uk
*
* 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/>.
*
* A fair proportion of this code was taken from the Whirl plug-in
* which was copyrighted by Federico Mena Quintero (as below).
*
* Whirl plug-in --- distort an image into a whirlpool
* Copyright (C) 1997 Federico Mena Quintero
*
*/
#include "config.h"
#include <string.h>
#include <libpika/pika.h>
#include <libpika/pikaui.h>
#include "libpika/stdplugins-intl.h"
/***** Magic numbers *****/
#define PLUG_IN_PROC "plug-in-blinds"
#define PLUG_IN_BINARY "blinds"
#define PLUG_IN_ROLE "pika-blinds"
#define MAX_FANS 100
typedef struct _Blinds Blinds;
typedef struct _BlindsClass BlindsClass;
struct _Blinds
{
PikaPlugIn parent_instance;
};
struct _BlindsClass
{
PikaPlugInClass parent_class;
};
#define BLINDS_TYPE (blinds_get_type ())
#define BLINDS (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), BLINDS_TYPE, Blinds))
GType blinds_get_type (void) G_GNUC_CONST;
static GList * blinds_query_procedures (PikaPlugIn *plug_in);
static PikaProcedure * blinds_create_procedure (PikaPlugIn *plug_in,
const gchar *name);
static PikaValueArray * blinds_run (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
const PikaValueArray *args,
gpointer run_data);
static gboolean blinds_dialog (PikaProcedure *procedure,
GObject *config,
PikaDrawable *drawable);
static void dialog_update_preview (GtkWidget *widget,
GObject *config);
static void apply_blinds (GObject *config,
PikaDrawable *drawable);
G_DEFINE_TYPE (Blinds, blinds, PIKA_TYPE_PLUG_IN)
PIKA_MAIN (BLINDS_TYPE)
DEFINE_STD_SET_I18N
/* Array to hold each size of fans. And no there are not each the
* same size (rounding errors...)
*/
static gint fanwidths[MAX_FANS];
static void
blinds_class_init (BlindsClass *klass)
{
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
plug_in_class->query_procedures = blinds_query_procedures;
plug_in_class->create_procedure = blinds_create_procedure;
plug_in_class->set_i18n = STD_SET_I18N;
}
static void
blinds_init (Blinds *blinds)
{
}
static GList *
blinds_query_procedures (PikaPlugIn *plug_in)
{
return g_list_append (NULL, g_strdup (PLUG_IN_PROC));
}
static PikaProcedure *
blinds_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,
blinds_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, _("_Blinds..."));
pika_procedure_add_menu_path (procedure, "<Image>/Filters/Distorts");
pika_procedure_set_documentation (procedure,
_("Simulate an image painted on "
"window blinds"),
"More here later",
name);
pika_procedure_set_attribution (procedure,
"Andy Thomas",
"Andy Thomas",
"1997");
PIKA_PROC_ARG_INT (procedure, "angle-displacement",
_("_Displacement"),
_("Angle of Displacement"),
0, 360, 30,
G_PARAM_READWRITE);
PIKA_PROC_ARG_INT (procedure, "num-segments",
_("_Number of segments"),
_("Number of segments in blinds"),
1, 1024, 3,
G_PARAM_READWRITE);
PIKA_PROC_ARG_INT (procedure, "orientation",
_("Orientation"),
_("The orientation"),
PIKA_ORIENTATION_HORIZONTAL,
PIKA_ORIENTATION_VERTICAL,
PIKA_ORIENTATION_HORIZONTAL,
G_PARAM_READWRITE);
PIKA_PROC_ARG_BOOLEAN (procedure, "bg-transparent",
_("_Transparent"),
_("Background transparent"),
FALSE,
G_PARAM_READWRITE);
}
return procedure;
}
static PikaValueArray *
blinds_run (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
const PikaValueArray *args,
gpointer run_data)
{
PikaProcedureConfig *config;
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];
}
config = pika_procedure_create_config (procedure);
pika_procedure_config_begin_run (config, NULL, run_mode, args);
switch (run_mode)
{
case PIKA_RUN_INTERACTIVE:
if (! blinds_dialog (procedure, G_OBJECT (config), drawable))
{
pika_procedure_config_end_run (config, PIKA_PDB_CANCEL);
g_object_unref (config);
return pika_procedure_new_return_values (procedure,
PIKA_PDB_CANCEL,
NULL);
}
break;
default:
break;
}
if (pika_drawable_is_rgb (drawable) ||
pika_drawable_is_gray (drawable))
{
pika_progress_init (_("Adding blinds"));
apply_blinds (G_OBJECT (config), drawable);
if (run_mode != PIKA_RUN_NONINTERACTIVE)
pika_displays_flush ();
}
else
{
pika_procedure_config_end_run (config, PIKA_PDB_EXECUTION_ERROR);
g_object_unref (config);
return pika_procedure_new_return_values (procedure,
PIKA_PDB_EXECUTION_ERROR,
NULL);
}
pika_procedure_config_end_run (config, PIKA_PDB_SUCCESS);
g_object_unref (config);
return pika_procedure_new_return_values (procedure, PIKA_PDB_SUCCESS, NULL);
}
static gboolean
blinds_dialog (PikaProcedure *procedure,
GObject *config,
PikaDrawable *drawable)
{
GtkWidget *dialog;
GtkWidget *preview;
GtkWidget *vbox;
GtkWidget *hbox;
GtkWidget *scale;
GtkListStore *store;
gboolean run;
pika_ui_init (PLUG_IN_BINARY);
dialog = pika_procedure_dialog_new (procedure,
PIKA_PROCEDURE_CONFIG (config),
_("Blinds"));
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_set_margin_bottom (preview, 12);
gtk_widget_show (preview);
store = pika_int_store_new (_("Horizontal"), PIKA_ORIENTATION_HORIZONTAL,
_("Vertical"), PIKA_ORIENTATION_VERTICAL,
NULL);
pika_procedure_dialog_get_int_radio (PIKA_PROCEDURE_DIALOG (dialog),
"orientation", PIKA_INT_STORE (store));
pika_procedure_dialog_get_label (PIKA_PROCEDURE_DIALOG (dialog),
"bg_label", _("Background"));
pika_procedure_dialog_fill_frame (PIKA_PROCEDURE_DIALOG (dialog),
"bg-frame", "bg_label", FALSE,
"bg-transparent");
pika_procedure_dialog_set_sensitive (PIKA_PROCEDURE_DIALOG (dialog), "bg-frame",
pika_drawable_has_alpha (drawable),
NULL, NULL, FALSE);
hbox = pika_procedure_dialog_fill_box (PIKA_PROCEDURE_DIALOG (dialog),
"blinds-hbox", "orientation",
"bg-frame", NULL);
gtk_orientable_set_orientation (GTK_ORIENTABLE (hbox),
GTK_ORIENTATION_HORIZONTAL);
gtk_box_set_spacing (GTK_BOX (hbox), 12);
gtk_box_set_homogeneous (GTK_BOX (hbox), TRUE);
gtk_widget_set_margin_bottom (hbox, 12);
scale = pika_procedure_dialog_get_scale_entry (PIKA_PROCEDURE_DIALOG (dialog),
"angle-displacement", 1.0);
gtk_widget_set_margin_bottom (scale, 12);
pika_procedure_dialog_get_scale_entry (PIKA_PROCEDURE_DIALOG (dialog),
"num-segments", 1.0);
vbox = pika_procedure_dialog_fill_box (PIKA_PROCEDURE_DIALOG (dialog),
"blinds-vbox", "blinds-hbox",
"angle-displacement", "num-segments",
NULL);
gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
gtk_widget_set_size_request (vbox, 320, -1);
g_object_set_data (config, "drawable", drawable);
g_signal_connect (preview, "invalidated",
G_CALLBACK (dialog_update_preview),
config);
g_signal_connect_swapped (config, "notify",
G_CALLBACK (pika_preview_invalidate),
preview);
pika_procedure_dialog_fill (PIKA_PROCEDURE_DIALOG (dialog),
"blinds-vbox",
NULL);
gtk_widget_show (dialog);
run = pika_procedure_dialog_run (PIKA_PROCEDURE_DIALOG (dialog));
gtk_widget_destroy (dialog);
return run;
}
static void
blindsapply (GObject *config,
const guchar *srow,
guchar *drow,
gint width,
gint bpp,
guchar *bg)
{
const guchar *src;
guchar *dst;
gint i,j,k;
gdouble ang;
gint available;
gint angledsp;
gint numsegs;
g_object_get (config,
"angle-displacement", &angledsp,
"num-segments", &numsegs,
NULL);
/* Make the row 'shrink' around points along its length */
/* The numsegs determines how many segments to slip it in to */
/* The angle is the conceptual 'rotation' of each of these segments */
/* Note the row is considered to be made up of a two dim array actual
* pixel locations and the RGB color at these locations.
*/
/* In the process copy the src row to the destination row */
/* Fill in with background color ? */
for (i = 0 ; i < width ; i++)
{
dst = &drow[i*bpp];
for (j = 0 ; j < bpp; j++)
{
dst[j] = bg[j];
}
}
/* Apply it */
available = width;
for (i = 0; i < numsegs; i++)
{
/* Width of segs are variable */
fanwidths[i] = available / (numsegs - i);
available -= fanwidths[i];
}
/* do center points first - just for fun...*/
available = 0;
for (k = 1; k <= numsegs; k++)
{
int point;
point = available + fanwidths[k - 1] / 2;
available += fanwidths[k - 1];
src = &srow[point * bpp];
dst = &drow[point * bpp];
/* Copy pixels across */
for (j = 0 ; j < bpp; j++)
{
dst[j] = src[j];
}
}
/* Disp for each point */
ang = (angledsp * 2 * G_PI) / 360; /* Angle in rads */
ang = (1 - fabs (cos (ang)));
available = 0;
for (k = 0 ; k < numsegs; k++)
{
int dx; /* Amount to move by */
int fw;
for (i = 0 ; i < (fanwidths[k]/2) ; i++)
{
/* Copy pixels across of left half of fan */
fw = fanwidths[k] / 2;
dx = (int) (ang * ((double) (fw - (double)(i % fw))));
src = &srow[(available + i) * bpp];
dst = &drow[(available + i + dx) * bpp];
for (j = 0; j < bpp; j++)
{
dst[j] = src[j];
}
/* Right side */
j = i + 1;
src = &srow[(available + fanwidths[k] - j
- (fanwidths[k] % 2)) * bpp];
dst = &drow[(available + fanwidths[k] - j
- (fanwidths[k] % 2) - dx) * bpp];
for (j = 0; j < bpp; j++)
{
dst[j] = src[j];
}
}
available += fanwidths[k];
}
}
static void
dialog_update_preview (GtkWidget *widget,
GObject *config)
{
PikaPreview *preview = PIKA_PREVIEW (widget);
PikaDrawable *drawable = g_object_get_data (config, "drawable");
gint y;
const guchar *p;
guchar *buffer;
GBytes *cache;
const guchar *cache_start;
PikaRGB background;
guchar bg[4];
gint width;
gint height;
gint bpp;
PikaOrientationType orientation;
gint bg_trans;
g_object_get (config,
"bg-transparent", &bg_trans,
"orientation", &orientation,
NULL);
pika_preview_get_size (preview, &width, &height);
cache = pika_drawable_get_thumbnail_data (drawable,
width, height,
&width, &height, &bpp);
p = cache_start = g_bytes_get_data (cache, NULL);
pika_context_get_background (&background);
if (bg_trans)
pika_rgb_set_alpha (&background, 0.0);
if (pika_drawable_is_gray (drawable))
{
bg[0] = pika_rgb_luminance_uchar (&background);
pika_rgba_get_uchar (&background, NULL, NULL, NULL, bg + 3);
}
else
{
pika_rgba_get_uchar (&background, bg, bg + 1, bg + 2, bg + 3);
}
buffer = g_new (guchar, width * height * bpp);
if (orientation == PIKA_ORIENTATION_VERTICAL)
{
for (y = 0; y < height; y++)
{
blindsapply (config, p,
buffer + y * width * bpp,
width,
bpp, bg);
p += width * bpp;
}
}
else
{
/* Horizontal blinds */
/* Apply the blinds algo to a single column -
* this act as a transformation matrix for the
* rows. Make row 0 invalid so we can find it again!
*/
gint i;
guchar *sr = g_new (guchar, height * 4);
guchar *dr = g_new0 (guchar, height * 4);
guchar dummybg[4] = {0, 0, 0, 0};
/* Fill in with background color ? */
for (i = 0 ; i < width ; i++)
{
gint j;
gint bd = bpp;
guchar *dst;
dst = &buffer[i * bd];
for (j = 0 ; j < bd; j++)
{
dst[j] = bg[j];
}
}
for ( y = 0 ; y < height; y++)
{
sr[y] = y+1;
}
/* Bit of a fiddle since blindsapply really works on an image
* row not a set of bytes. - preview can't be > 255
* or must make dr sr int rows.
*/
blindsapply (config, sr, dr, height, 1, dummybg);
for (y = 0; y < height; y++)
{
if (dr[y] == 0)
{
/* Draw background line */
p = buffer;
}
else
{
/* Draw line from src */
p = cache_start +
(width * bpp * (dr[y] - 1));
}
memcpy (buffer + y * width * bpp,
p,
width * bpp);
}
g_free (sr);
g_free (dr);
}
pika_preview_draw_buffer (preview, buffer, width * bpp);
g_free (buffer);
g_bytes_unref (cache);
}
/* STEP tells us how many rows/columns to gulp down in one go... */
/* Note all the "4" literals around here are to do with the depths
* of the images. Makes it easier to deal with for my small brain.
*/
#define STEP 40
static void
apply_blinds (GObject *config,
PikaDrawable *drawable)
{
GeglBuffer *src_buffer;
GeglBuffer *dest_buffer;
const Babl *format;
guchar *src_rows, *des_rows;
gint bytes;
gint x, y;
PikaRGB background;
guchar bg[4];
gint sel_x1, sel_y1;
gint sel_width, sel_height;
PikaOrientationType orientation;
gint bg_trans;
g_object_get (config,
"bg-transparent", &bg_trans,
"orientation", &orientation,
NULL);
pika_context_get_background (&background);
if (bg_trans)
pika_rgb_set_alpha (&background, 0.0);
pika_rgba_get_uchar (&background, bg, bg + 1, bg + 2, bg + 3);
if (! pika_drawable_mask_intersect (drawable,
&sel_x1, &sel_y1,
&sel_width, &sel_height))
return;
if (pika_drawable_has_alpha (drawable))
format = babl_format ("R'G'B'A u8");
else
format = babl_format ("R'G'B' u8");
bytes = babl_format_get_bytes_per_pixel (format);
src_buffer = pika_drawable_get_buffer (drawable);
dest_buffer = pika_drawable_get_shadow_buffer (drawable);
src_rows = g_new (guchar, MAX (sel_width, sel_height) * bytes * STEP);
des_rows = g_new (guchar, MAX (sel_width, sel_height) * bytes * STEP);
if (orientation == PIKA_ORIENTATION_VERTICAL)
{
for (y = 0; y < sel_height; y += STEP)
{
gint rr;
gint step;
if ((y + STEP) > sel_height)
step = sel_height - y;
else
step = STEP;
gegl_buffer_get (src_buffer,
GEGL_RECTANGLE (sel_x1, sel_y1 + y,
sel_width, step), 1.0,
format, src_rows,
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
/* OK I could make this better */
for (rr = 0; rr < STEP; rr++)
blindsapply (config,
src_rows + (sel_width * rr * bytes),
des_rows + (sel_width * rr * bytes),
sel_width, bytes, bg);
gegl_buffer_set (dest_buffer,
GEGL_RECTANGLE (sel_x1, sel_y1 + y,
sel_width, step), 0,
format, des_rows,
GEGL_AUTO_ROWSTRIDE);
pika_progress_update ((double) y / (double) sel_height);
}
}
else
{
/* Horizontal blinds */
/* Apply the blinds algo to a single column -
* this act as a transformation matrix for the
* rows. Make row 0 invalid so we can find it again!
*/
gint i;
gint *sr = g_new (gint, sel_height * bytes);
gint *dr = g_new (gint, sel_height * bytes);
guchar *dst = g_new (guchar, STEP * bytes);
guchar dummybg[4];
memset (dummybg, 0, 4);
memset (dr, 0, sel_height * bytes); /* all dr rows are background rows */
for (y = 0; y < sel_height; y++)
{
sr[y] = y+1;
}
/* Hmmm. does this work portably? */
/* This "swaps" the integers around that are held in in the
* sr & dr arrays.
*/
blindsapply (config, (guchar *) sr, (guchar *) dr,
sel_height, sizeof (gint), dummybg);
/* Fill in with background color ? */
for (i = 0 ; i < STEP ; i++)
{
int j;
guchar *bgdst;
bgdst = &dst[i * bytes];
for (j = 0 ; j < bytes; j++)
{
bgdst[j] = bg[j];
}
}
for (x = 0; x < sel_width; x += STEP)
{
int rr;
int step;
guchar *p;
if((x + STEP) > sel_width)
step = sel_width - x;
else
step = STEP;
gegl_buffer_get (src_buffer,
GEGL_RECTANGLE (sel_x1 + x, sel_y1,
step, sel_height), 1.0,
format, src_rows,
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
/* OK I could make this better */
for (rr = 0; rr < sel_height; rr++)
{
if(dr[rr] == 0)
{
/* Draw background line */
p = dst;
}
else
{
/* Draw line from src */
p = src_rows + (step * bytes * (dr[rr] - 1));
}
memcpy (des_rows + (rr * step * bytes), p,
step * bytes);
}
gegl_buffer_set (dest_buffer,
GEGL_RECTANGLE (sel_x1 + x, sel_y1,
step, sel_height), 0,
format, des_rows,
GEGL_AUTO_ROWSTRIDE);
pika_progress_update ((double) x / (double) sel_width);
}
g_free (dst);
g_free (sr);
g_free (dr);
}
g_free (src_rows);
g_free (des_rows);
g_object_unref (src_buffer);
g_object_unref (dest_buffer);
pika_progress_update (1.0);
pika_drawable_merge_shadow (drawable, TRUE);
pika_drawable_update (drawable,
sel_x1, sel_y1, sel_width, sel_height);
}

View File

@ -0,0 +1,468 @@
/* borderaverage 0.01 - image processing plug-in for PIKA.
*
* Copyright (C) 1998 Philipp Klaus (webmaster@access.ch)
*
*
* 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/>.
*/
#include "config.h"
#include <libpika/pika.h>
#include <libpika/pikaui.h>
#include "libpika/stdplugins-intl.h"
#define PLUG_IN_PROC "plug-in-borderaverage"
#define PLUG_IN_BINARY "border-average"
#define PLUG_IN_ROLE "pika-border-average"
typedef struct _BorderAverage BorderAverage;
typedef struct _BorderAverageClass BorderAverageClass;
struct _BorderAverage
{
PikaPlugIn parent_instance;
};
struct _BorderAverageClass
{
PikaPlugInClass parent_class;
};
#define BORDER_AVERAGE_TYPE (border_average_get_type ())
#define BORDER_AVERAGE (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), BORDER_AVERAGE_TYPE, BorderAverage))
GType border_average_get_type (void) G_GNUC_CONST;
static GList * border_average_query_procedures (PikaPlugIn *plug_in);
static PikaProcedure * border_average_create_procedure (PikaPlugIn *plug_in,
const gchar *name);
static PikaValueArray * border_average_run (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
const PikaValueArray *args,
gpointer run_data);
static void borderaverage (GObject *config,
GeglBuffer *buffer,
PikaDrawable *drawable,
PikaRGB *result);
static gboolean borderaverage_dialog (PikaProcedure *procedure,
GObject *config,
PikaImage *image,
PikaDrawable *drawable);
static void add_new_color (const guchar *buffer,
gint *cube,
gint bucket_expo);
G_DEFINE_TYPE (BorderAverage, border_average, PIKA_TYPE_PLUG_IN)
PIKA_MAIN (BORDER_AVERAGE_TYPE)
DEFINE_STD_SET_I18N
static void
border_average_class_init (BorderAverageClass *klass)
{
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
plug_in_class->query_procedures = border_average_query_procedures;
plug_in_class->create_procedure = border_average_create_procedure;
plug_in_class->set_i18n = STD_SET_I18N;
}
static void
border_average_init (BorderAverage *film)
{
}
static GList *
border_average_query_procedures (PikaPlugIn *plug_in)
{
return g_list_append (NULL, g_strdup (PLUG_IN_PROC));
}
static PikaProcedure *
border_average_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,
border_average_run, NULL, NULL);
pika_procedure_set_image_types (procedure, "RGB*");
pika_procedure_set_sensitivity_mask (procedure,
PIKA_PROCEDURE_SENSITIVE_DRAWABLE);
pika_procedure_set_menu_label (procedure, _("_Border Average..."));
pika_procedure_add_menu_path (procedure, "<Image>/Colors/Info");
pika_procedure_set_documentation (procedure,
_("Set foreground to the average color of the image border"),
"",
name);
pika_procedure_set_attribution (procedure,
"Philipp Klaus",
"Internet Access AG",
"1998");
PIKA_PROC_ARG_INT (procedure, "thickness",
_("_Thickness"),
_("Border size to take in count"),
0, G_MAXINT, 3,
G_PARAM_READWRITE);
PIKA_PROC_AUX_ARG_UNIT (procedure, "thickness-unit",
_("Thickness unit of measure"),
_("Border size unit of measure"),
TRUE, TRUE, PIKA_UNIT_PIXEL,
PIKA_PARAM_READWRITE);
PIKA_PROC_ARG_INT (procedure, "bucket-exponent",
_("Bucket Si_ze"),
_("Bits for bucket size (default=4: 16 Levels)"),
0, G_MAXINT, 4,
G_PARAM_READWRITE);
PIKA_PROC_VAL_RGB (procedure, "borderaverage",
_("The average color of the specified border."),
_("The average color of the specified border."),
TRUE, NULL,
G_PARAM_READWRITE);
}
return procedure;
}
static PikaValueArray *
border_average_run (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
const PikaValueArray *args,
gpointer run_data)
{
PikaProcedureConfig *config;
PikaDrawable *drawable;
PikaValueArray *return_vals = NULL;
PikaPDBStatusType status = PIKA_PDB_SUCCESS;
PikaRGB result_color = { 0.0, };
GeglBuffer *buffer;
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];
}
config = pika_procedure_create_config (procedure);
pika_procedure_config_begin_run (config, NULL, run_mode, args);
buffer = pika_drawable_get_buffer (drawable);
switch (run_mode)
{
case PIKA_RUN_INTERACTIVE:
if (! borderaverage_dialog (procedure, G_OBJECT (config),
image, drawable))
status = PIKA_PDB_EXECUTION_ERROR;
break;
case PIKA_RUN_NONINTERACTIVE:
if (pika_value_array_length (args) != 2)
status = PIKA_PDB_CALLING_ERROR;
break;
default:
break;
}
if (status == PIKA_PDB_SUCCESS)
{
/* Make sure that the drawable is RGB color */
if (pika_drawable_is_rgb (drawable))
{
pika_progress_init ( _("Border Average"));
borderaverage (G_OBJECT (config), buffer, drawable, &result_color);
if (run_mode != PIKA_RUN_NONINTERACTIVE)
pika_context_set_foreground (&result_color);
}
else
{
status = PIKA_PDB_EXECUTION_ERROR;
}
}
g_object_unref (buffer);
return_vals = pika_procedure_new_return_values (procedure, status, NULL);
pika_procedure_config_end_run (config, PIKA_PDB_SUCCESS);
g_object_unref (config);
if (status == PIKA_PDB_SUCCESS)
PIKA_VALUES_SET_RGB (return_vals, 1, &result_color);
return return_vals;
}
static void
borderaverage (GObject *config,
GeglBuffer *buffer,
PikaDrawable *drawable,
PikaRGB *result)
{
gint x, y, width, height;
gint max;
guchar r, g, b;
gint bucket_num, bucket_expo, bucket_rexpo;
gint *cube;
gint i, j, k;
GeglRectangle border[4];
gint borderaverage_thickness;
gint borderaverage_bucket_exponent;
g_object_get (config,
"thickness", &borderaverage_thickness,
"bucket-exponent", &borderaverage_bucket_exponent,
NULL);
if (! pika_drawable_mask_intersect (drawable, &x, &y, &width, &height))
{
pika_rgba_set_uchar (result, 0, 0, 0, 255);
return;
}
/* allocate and clear the cube before */
bucket_expo = borderaverage_bucket_exponent;
bucket_rexpo = 8 - bucket_expo;
cube = g_new (gint, 1 << (bucket_rexpo * 3));
bucket_num = 1 << bucket_rexpo;
for (i = 0; i < bucket_num; i++)
{
for (j = 0; j < bucket_num; j++)
{
for (k = 0; k < bucket_num; k++)
{
cube[(i << (bucket_rexpo << 1)) + (j << bucket_rexpo) + k] = 0;
}
}
}
/* Top */
border[0].x = x;
border[0].y = y;
border[0].width = width;
border[0].height = borderaverage_thickness;
/* Bottom */
border[1].x = x;
border[1].y = y + height - borderaverage_thickness;
border[1].width = width;
border[1].height = borderaverage_thickness;
/* Left */
border[2].x = x;
border[2].y = y + borderaverage_thickness;
border[2].width = borderaverage_thickness;
border[2].height = height - 2 * borderaverage_thickness;
/* Right */
border[3].x = x + width - borderaverage_thickness;
border[3].y = y + borderaverage_thickness;
border[3].width = borderaverage_thickness;
border[3].height = height - 2 * borderaverage_thickness;
/* Fill the cube */
for (i = 0; i < 4; i++)
{
if (border[i].width > 0 && border[i].height > 0)
{
GeglBufferIterator *gi;
gi = gegl_buffer_iterator_new (buffer, &border[i], 0, babl_format ("R'G'B' u8"),
GEGL_ACCESS_READWRITE, GEGL_ABYSS_NONE, 1);
while (gegl_buffer_iterator_next (gi))
{
guint k;
guchar *data;
data = (guchar*) gi->items[0].data;
for (k = 0; k < gi->length; k++)
{
add_new_color (data + k * 3,
cube,
bucket_expo);
}
}
}
}
max = 0; r = 0; g = 0; b = 0;
/* get max of cube */
for (i = 0; i < bucket_num; i++)
{
for (j = 0; j < bucket_num; j++)
{
for (k = 0; k < bucket_num; k++)
{
if (cube[(i << (bucket_rexpo << 1)) +
(j << bucket_rexpo) + k] > max)
{
max = cube[(i << (bucket_rexpo << 1)) +
(j << bucket_rexpo) + k];
r = (i<<bucket_expo) + (1<<(bucket_expo - 1));
g = (j<<bucket_expo) + (1<<(bucket_expo - 1));
b = (k<<bucket_expo) + (1<<(bucket_expo - 1));
}
}
}
}
/* return the color */
pika_rgba_set_uchar (result, r, g, b, 255);
g_free (cube);
}
static void
add_new_color (const guchar *buffer,
gint *cube,
gint bucket_expo)
{
guchar r, g, b;
gint bucket_rexpo;
bucket_rexpo = 8 - bucket_expo;
r = buffer[0] >> bucket_expo;
g = buffer[1] >> bucket_expo;
b = buffer[2] >> bucket_expo;
cube[(r << (bucket_rexpo << 1)) + (g << bucket_rexpo) + b]++;
}
static gboolean
borderaverage_dialog (PikaProcedure *procedure,
GObject *config,
PikaImage *image,
PikaDrawable *drawable)
{
GtkWidget *dialog;
GtkListStore *store;
GtkWidget *size_entry;
gboolean run;
gdouble xres, yres;
GeglBuffer *buffer = NULL;
pika_ui_init (PLUG_IN_BINARY);
dialog = pika_procedure_dialog_new (procedure,
PIKA_PROCEDURE_CONFIG (config),
_("Border Average"));
pika_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
GTK_RESPONSE_OK,
GTK_RESPONSE_CANCEL,
-1);
pika_window_set_transient (GTK_WINDOW (dialog));
pika_procedure_dialog_get_label (PIKA_PROCEDURE_DIALOG (dialog),
"border-size-label",
_("Border Size"));
/* Get the image resolution */
pika_image_get_resolution (image, &xres, &yres);
size_entry = pika_procedure_dialog_get_size_entry (PIKA_PROCEDURE_DIALOG (dialog),
"thickness", TRUE,
"thickness-unit", "%a",
PIKA_SIZE_ENTRY_UPDATE_SIZE,
xres);
/* set the size (in pixels) that will be treated as 0% and 100% */
buffer = pika_drawable_get_buffer (drawable);
if (buffer)
pika_size_entry_set_size (PIKA_SIZE_ENTRY (size_entry), 0, 0.0,
MIN (gegl_buffer_get_width (buffer),
gegl_buffer_get_height (buffer)));
pika_size_entry_set_refval_boundaries (PIKA_SIZE_ENTRY (size_entry), 0, 1.0,
MIN (gegl_buffer_get_width (buffer),
gegl_buffer_get_height (buffer)) / 2);
pika_procedure_dialog_fill_frame (PIKA_PROCEDURE_DIALOG (dialog),
"border-size-frame", "border-size-label",
FALSE, "thickness");
pika_procedure_dialog_get_label (PIKA_PROCEDURE_DIALOG (dialog),
"bucket-size-label",
_("Number of Colors"));
store = pika_int_store_new ("1", 0, "2", 1, "4", 2, "8", 3,
"16", 4, "32", 5, "64", 6, "128", 7,
"256", 8, NULL);
pika_procedure_dialog_get_int_combo (PIKA_PROCEDURE_DIALOG (dialog),
"bucket-exponent",
PIKA_INT_STORE (store));
pika_procedure_dialog_fill_frame (PIKA_PROCEDURE_DIALOG (dialog),
"bucket-size-frame", "bucket-size-label",
FALSE, "bucket-exponent");
pika_procedure_dialog_fill (PIKA_PROCEDURE_DIALOG (dialog),
"border-size-frame",
"bucket-size-frame",
NULL);
gtk_widget_show (dialog);
run = pika_procedure_dialog_run (PIKA_PROCEDURE_DIALOG (dialog));
gtk_widget_destroy (dialog);
return run;
}

View File

@ -0,0 +1,373 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* busy-dialog.c
* Copyright (C) 2018 Ell
*
* 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/>.
*/
#include "config.h"
#include <libpika/pika.h>
#include <libpika/pikaui.h>
#include "libpika/stdplugins-intl.h"
#define PLUG_IN_PROC "plug-in-busy-dialog"
#define PLUG_IN_BINARY "busy-dialog"
#define PLUG_IN_ROLE "pika-busy-dialog"
typedef struct
{
GIOChannel *read_channel;
GIOChannel *write_channel;
} Context;
typedef struct _BusyDialog BusyDialog;
typedef struct _BusyDialogClass BusyDialogClass;
struct _BusyDialog
{
PikaPlugIn parent_instance;
};
struct _BusyDialogClass
{
PikaPlugInClass parent_class;
};
#define BUSY_DIALOG_TYPE (busy_dialog_get_type ())
#define BUSY_DIALOG (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), BUSY_DIALOG_TYPE, BusyDialog))
GType busy_dialog_get_type (void) G_GNUC_CONST;
static GList * busy_dialog_query_procedures (PikaPlugIn *plug_in);
static PikaProcedure * busy_dialog_create_procedure (PikaPlugIn *plug_in,
const gchar *name);
static PikaValueArray * busy_dialog_run (PikaProcedure *procedure,
const PikaValueArray *args,
gpointer run_data);
static PikaPDBStatusType busy_dialog (gint read_fd,
gint write_fd,
const gchar *message,
gboolean cancelable);
static gboolean busy_dialog_read_channel_notify (GIOChannel *source,
GIOCondition condition,
Context *context);
static gboolean busy_dialog_delete_event (GtkDialog *dialog,
GdkEvent *event,
Context *context);
static void busy_dialog_response (GtkDialog *dialog,
gint response_id,
Context *context);
G_DEFINE_TYPE (BusyDialog, busy_dialog, PIKA_TYPE_PLUG_IN)
PIKA_MAIN (BUSY_DIALOG_TYPE)
DEFINE_STD_SET_I18N
static void
busy_dialog_class_init (BusyDialogClass *klass)
{
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
plug_in_class->query_procedures = busy_dialog_query_procedures;
plug_in_class->create_procedure = busy_dialog_create_procedure;
plug_in_class->set_i18n = STD_SET_I18N;
}
static void
busy_dialog_init (BusyDialog *busy_dialog)
{
}
static GList *
busy_dialog_query_procedures (PikaPlugIn *plug_in)
{
return g_list_append (NULL, g_strdup (PLUG_IN_PROC));
}
static PikaProcedure *
busy_dialog_create_procedure (PikaPlugIn *plug_in,
const gchar *name)
{
PikaProcedure *procedure = NULL;
if (! strcmp (name, PLUG_IN_PROC))
{
procedure = pika_procedure_new (plug_in, name,
PIKA_PDB_PROC_TYPE_PLUGIN,
busy_dialog_run, NULL, NULL);
pika_procedure_set_documentation (procedure,
"Show a dialog while waiting for an "
"operation to finish",
"Used by PIKA to display a dialog, "
"containing a spinner and a custom "
"message, while waiting for an "
"ongoing operation to finish. "
"Optionally, the dialog may provide "
"a \"Cancel\" button, which can be used "
"to cancel the operation.",
name);
pika_procedure_set_attribution (procedure,
"Ell",
"Ell",
"2018");
PIKA_PROC_ARG_ENUM (procedure, "run-mode",
"Run mode",
"The run mode",
PIKA_TYPE_RUN_MODE,
PIKA_RUN_INTERACTIVE,
G_PARAM_READWRITE);
PIKA_PROC_ARG_INT (procedure, "read-fd",
"The read file descriptor",
"The read file descriptor",
G_MININT, G_MAXINT, 0,
G_PARAM_READWRITE);
PIKA_PROC_ARG_INT (procedure, "write-fd",
"The write file descriptor",
"The write file descriptor",
G_MININT, G_MAXINT, 0,
G_PARAM_READWRITE);
PIKA_PROC_ARG_STRING (procedure, "message",
"The message",
"The message",
NULL,
G_PARAM_READWRITE);
PIKA_PROC_ARG_BOOLEAN (procedure, "cancelable",
"Whether the dialog is cancelable",
"Whether the dialog is cancelable",
FALSE,
G_PARAM_READWRITE);
}
return procedure;
}
static PikaValueArray *
busy_dialog_run (PikaProcedure *procedure,
const PikaValueArray *args,
gpointer run_data)
{
PikaValueArray *return_vals = NULL;
PikaPDBStatusType status = PIKA_PDB_SUCCESS;
PikaRunMode run_mode;
run_mode = PIKA_VALUES_GET_ENUM (args, 0);
switch (run_mode)
{
case PIKA_RUN_INTERACTIVE:
case PIKA_RUN_NONINTERACTIVE:
case PIKA_RUN_WITH_LAST_VALS:
if (pika_value_array_length (args) != 5)
{
status = PIKA_PDB_CALLING_ERROR;
}
else
{
status = busy_dialog (PIKA_VALUES_GET_INT (args, 1),
PIKA_VALUES_GET_INT (args, 2),
PIKA_VALUES_GET_STRING (args, 3),
PIKA_VALUES_GET_BOOLEAN (args, 4));
}
break;
default:
status = PIKA_PDB_CALLING_ERROR;
break;
}
return_vals = pika_procedure_new_return_values (procedure, status, NULL);
return return_vals;
}
static PikaPDBStatusType
busy_dialog (gint read_fd,
gint write_fd,
const gchar *message,
gboolean cancelable)
{
Context context;
GtkWidget *window;
GtkWidget *content_area;
GtkWidget *vbox;
GtkWidget *label;
GtkWidget *box;
#ifdef G_OS_WIN32
context.read_channel = g_io_channel_win32_new_fd (read_fd);
context.write_channel = g_io_channel_win32_new_fd (write_fd);
#else
context.read_channel = g_io_channel_unix_new (read_fd);
context.write_channel = g_io_channel_unix_new (write_fd);
#endif
g_io_channel_set_close_on_unref (context.read_channel, TRUE);
g_io_channel_set_close_on_unref (context.write_channel, TRUE);
/* triggered when the operation is finished in the main app, and we should
* quit.
*/
g_io_add_watch (context.read_channel, G_IO_IN | G_IO_ERR | G_IO_HUP,
(GIOFunc) busy_dialog_read_channel_notify,
&context);
/* call gtk_init() before pika_ui_init(), to avoid DESKTOP_STARTUP_ID from
* taking effect -- we want the dialog to be prominently displayed above
* other plug-in windows.
*/
gtk_init (NULL, NULL);
pika_ui_init (PLUG_IN_BINARY);
/* the main window */
if (! cancelable)
{
window = g_object_new (GTK_TYPE_WINDOW,
"title", _("Please Wait"),
"skip-taskbar-hint", TRUE,
"deletable", FALSE,
"resizable", FALSE,
"role", "pika-busy-dialog",
"type-hint", GDK_WINDOW_TYPE_HINT_DIALOG,
"window-position", GTK_WIN_POS_CENTER,
NULL);
g_signal_connect (window, "delete-event", G_CALLBACK (gtk_true), NULL);
content_area = window;
}
else
{
window = g_object_new (GTK_TYPE_DIALOG,
"title", _("Please Wait"),
"skip-taskbar-hint", TRUE,
"resizable", FALSE,
"role", "pika-busy-dialog",
"window-position", GTK_WIN_POS_CENTER,
NULL);
gtk_dialog_add_button (GTK_DIALOG (window),
_("_Cancel"), GTK_RESPONSE_CANCEL);
g_signal_connect (window, "delete-event",
G_CALLBACK (busy_dialog_delete_event),
&context);
g_signal_connect (window, "response",
G_CALLBACK (busy_dialog_response),
&context);
content_area = gtk_dialog_get_content_area (GTK_DIALOG (window));
}
/* the main vbox */
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 8);
gtk_container_set_border_width (GTK_CONTAINER (vbox), 16);
gtk_container_add (GTK_CONTAINER (content_area), vbox);
gtk_widget_show (vbox);
/* the title label */
label = gtk_label_new (_("Please wait for the operation to complete"));
pika_label_set_attributes (GTK_LABEL (label),
PANGO_ATTR_WEIGHT, PANGO_WEIGHT_BOLD,
-1);
gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
gtk_widget_show (label);
/* the busy box */
box = pika_busy_box_new (message);
gtk_container_set_border_width (GTK_CONTAINER (box), 8);
gtk_box_pack_start (GTK_BOX (vbox), box, TRUE, TRUE, 0);
gtk_widget_show (box);
gtk_window_present (GTK_WINDOW (window));
gtk_main ();
gtk_widget_destroy (window);
g_clear_pointer (&context.read_channel, g_io_channel_unref);
g_clear_pointer (&context.write_channel, g_io_channel_unref);
return PIKA_PDB_SUCCESS;
}
static gboolean
busy_dialog_read_channel_notify (GIOChannel *source,
GIOCondition condition,
Context *context)
{
gtk_main_quit ();
return FALSE;
}
static gboolean
busy_dialog_delete_event (GtkDialog *dialog,
GdkEvent *event,
Context *context)
{
gtk_dialog_response (dialog, GTK_RESPONSE_CANCEL);
return TRUE;
}
static void
busy_dialog_response (GtkDialog *dialog,
gint response_id,
Context *context)
{
switch (response_id)
{
case GTK_RESPONSE_CANCEL:
{
GtkWidget *button;
gtk_dialog_set_response_sensitive (dialog, GTK_RESPONSE_CANCEL, FALSE);
button = gtk_dialog_get_widget_for_response (dialog,
GTK_RESPONSE_CANCEL);
gtk_button_set_label (GTK_BUTTON (button), _("Canceling..."));
/* signal the cancellation request to the main app */
g_clear_pointer (&context->write_channel, g_io_channel_unref);
}
break;
default:
break;
}
}

View File

@ -0,0 +1,562 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*
*/
#include "config.h"
#include <libpika/pika.h>
#include <libpika/pikaui.h>
#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,
const PikaValueArray *args,
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,
"<Image>/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,
const PikaValueArray *args,
gpointer run_data)
{
PikaProcedureConfig *config;
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];
}
config = pika_procedure_create_config (procedure);
pika_procedure_config_begin_run (config, NULL, run_mode, args);
switch (run_mode)
{
case PIKA_RUN_INTERACTIVE:
if (! checkerboard_dialog (procedure, G_OBJECT (config), image,
drawable))
{
pika_procedure_config_end_run (config, PIKA_PDB_CANCEL);
g_object_unref (config);
return pika_procedure_new_return_values (procedure,
PIKA_PDB_CANCEL,
NULL);
}
break;
default:
break;
}
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
{
pika_procedure_config_end_run (config, PIKA_PDB_EXECUTION_ERROR);
g_object_unref (config);
return pika_procedure_new_return_values (procedure,
PIKA_PDB_EXECUTION_ERROR,
NULL);
}
pika_procedure_config_end_run (config, PIKA_PDB_SUCCESS);
g_object_unref (config);
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,
&param);
}
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,
&param);
}
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;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,845 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* 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/>.
*/
/*
* Colormap remapping plug-in
* Copyright (C) 2006 Mukund Sivaraman <muks@mukund.org>
*
* This plug-in takes the colormap and lets you move colors from one index
* to another while keeping the original image visually unmodified.
*
* Such functionality is useful for creating graphics files for applications
* which expect certain indices to contain some specific colors.
*
*/
#include "config.h"
#include <string.h>
#include <libpika/pika.h>
#include <libpika/pikaui.h>
#include "libpika/stdplugins-intl.h"
#define PLUG_IN_PROC_REMAP "plug-in-colormap-remap"
#define PLUG_IN_PROC_SWAP "plug-in-colormap-swap"
#define PLUG_IN_BINARY "colormap-remap"
#define PLUG_IN_ROLE "pika-colormap-remap"
struct _PikaRemap
{
PikaPlugIn parent_instance;
GtkApplication *app;
GtkWindow *window;
PikaImage *image;
guchar map[256];
gint n_cols;
GMenu *menu;
};
#define PIKA_TYPE_REMAP (pika_remap_get_type ())
G_DECLARE_FINAL_TYPE (PikaRemap, pika_remap, PIKA, REMAP, PikaPlugIn)
GType remap_get_type (void) G_GNUC_CONST;
static void pika_remap_finalize (GObject *object);
static GList * remap_query_procedures (PikaPlugIn *plug_in);
static PikaProcedure * remap_create_procedure (PikaPlugIn *plug_in,
const gchar *name);
static PikaValueArray * remap_run (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
const PikaValueArray *args,
gpointer run_data);
static gboolean real_remap (PikaImage *image,
gint num_colors,
guchar *map);
static gboolean remap_dialog (PikaImage *image,
guchar *map,
PikaRemap *remap);
static void remap_sort_hue_action (GSimpleAction *action,
GVariant *parameter,
gpointer user_data);
static void remap_sort_sat_action (GSimpleAction *action,
GVariant *parameter,
gpointer user_data);
static void remap_sort_val_action (GSimpleAction *action,
GVariant *parameter,
gpointer user_data);
static void remap_reverse_action (GSimpleAction *action,
GVariant *parameter,
gpointer user_data);
static void remap_reset_action (GSimpleAction *action,
GVariant *parameter,
gpointer user_data);
static void on_app_activate (GApplication *gapp,
gpointer user_data);
G_DEFINE_TYPE (PikaRemap, pika_remap, PIKA_TYPE_PLUG_IN)
PIKA_MAIN (PIKA_TYPE_REMAP)
DEFINE_STD_SET_I18N
static GtkWindow *window = NULL;
static GtkListStore *store = NULL;
static gboolean remap_ok = FALSE;
static const GActionEntry ACTIONS[] =
{
{ "sort-hue", remap_sort_hue_action },
{ "sort-sat", remap_sort_sat_action },
{ "sort-val", remap_sort_val_action },
{ "reverse", remap_reverse_action },
{ "reset", remap_reset_action },
};
static void
pika_remap_class_init (PikaRemapClass *klass)
{
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = pika_remap_finalize;
plug_in_class->query_procedures = remap_query_procedures;
plug_in_class->create_procedure = remap_create_procedure;
plug_in_class->set_i18n = STD_SET_I18N;
}
static void
pika_remap_finalize (GObject *object)
{
PikaRemap *remap = PIKA_REMAP (object);
G_OBJECT_CLASS (pika_remap_parent_class)->finalize (object);
g_clear_object (&remap->menu);
}
static void
pika_remap_init (PikaRemap *remap)
{
}
static GList *
remap_query_procedures (PikaPlugIn *plug_in)
{
GList *list = NULL;
list = g_list_prepend (list, g_strdup (PLUG_IN_PROC_REMAP));
list = g_list_prepend (list, g_strdup (PLUG_IN_PROC_SWAP));
return list;
}
static PikaProcedure *
remap_create_procedure (PikaPlugIn *plug_in,
const gchar *name)
{
PikaProcedure *procedure = NULL;
if (! strcmp (name, PLUG_IN_PROC_REMAP))
{
procedure = pika_image_procedure_new (plug_in, name,
PIKA_PDB_PROC_TYPE_PLUGIN,
remap_run, NULL, NULL);
pika_procedure_set_image_types (procedure, "INDEXED*");
pika_procedure_set_sensitivity_mask (procedure,
PIKA_PROCEDURE_SENSITIVE_DRAWABLE |
PIKA_PROCEDURE_SENSITIVE_DRAWABLES |
PIKA_PROCEDURE_SENSITIVE_NO_DRAWABLES);
pika_procedure_set_menu_label (procedure, _("R_earrange Colormap..."));
pika_procedure_set_icon_name (procedure, PIKA_ICON_COLORMAP);
pika_procedure_add_menu_path (procedure, "<Image>/Colors/Map/[Colormap]");
pika_procedure_add_menu_path (procedure, "<Colormap>");
pika_procedure_set_documentation (procedure,
_("Rearrange the colormap"),
"This procedure takes an indexed "
"image and lets you alter the "
"positions of colors in the colormap "
"without visually changing the image.",
name);
pika_procedure_set_attribution (procedure,
"Mukund Sivaraman <muks@mukund.org>",
"Mukund Sivaraman <muks@mukund.org>",
"June 2006");
PIKA_PROC_ARG_BYTES (procedure, "map",
"Map",
"Remap array for the colormap",
G_PARAM_READWRITE);
}
else if (! strcmp (name, PLUG_IN_PROC_SWAP))
{
procedure = pika_image_procedure_new (plug_in, name,
PIKA_PDB_PROC_TYPE_PLUGIN,
remap_run, NULL, NULL);
pika_procedure_set_image_types (procedure, "INDEXED*");
pika_procedure_set_sensitivity_mask (procedure,
PIKA_PROCEDURE_SENSITIVE_DRAWABLE |
PIKA_PROCEDURE_SENSITIVE_DRAWABLES |
PIKA_PROCEDURE_SENSITIVE_NO_DRAWABLES);
pika_procedure_set_menu_label (procedure, _("_Swap Colors"));
pika_procedure_set_icon_name (procedure, PIKA_ICON_COLORMAP);
pika_procedure_set_documentation (procedure,
_("Swap two colors in the colormap"),
"This procedure takes an indexed "
"image and lets you swap the "
"positions of two colors in the "
"colormap without visually changing "
"the image.",
name);
pika_procedure_set_attribution (procedure,
"Mukund Sivaraman <muks@mukund.org>",
"Mukund Sivaraman <muks@mukund.org>",
"June 2006");
PIKA_PROC_ARG_INT (procedure, "index1",
"Index 1",
"First index in the colormap",
0, 255, 0,
G_PARAM_READWRITE);
PIKA_PROC_ARG_INT (procedure, "index2",
"Index 2",
"Second (other) index in the colormap",
0, 255, 0,
G_PARAM_READWRITE);
}
return procedure;
}
static PikaValueArray *
remap_run (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
const PikaValueArray *args,
gpointer run_data)
{
GMenu *section;
gint i;
PikaRemap *remap = PIKA_REMAP (run_data);
remap = PIKA_REMAP (pika_procedure_get_plug_in (procedure));
#if GLIB_CHECK_VERSION(2,74,0)
remap->app = gtk_application_new (NULL, G_APPLICATION_DEFAULT_FLAGS);
#else
remap->app = gtk_application_new (NULL, G_APPLICATION_FLAGS_NONE);
#endif
remap->image = image;
remap->menu = g_menu_new ();
section = g_menu_new ();
g_menu_append (section, _("Sort on Hue"), "win.sort-hue");
g_menu_append (section, _("Sort on Saturation"), "win.sort-sat");
g_menu_append (section, _("Sort on Value"), "win.sort-val");
g_menu_append_section (remap->menu, NULL, G_MENU_MODEL (section));
g_clear_object (&section);
section = g_menu_new ();
g_menu_append (section, _("Reverse Order"), "win.reverse");
g_menu_append (section, _("Reset Order"), "win.reset");
g_menu_append_section (remap->menu, NULL, G_MENU_MODEL (section));
g_clear_object (&section);
gegl_init (NULL, NULL);
/* Make sure that the image is indexed */
if (pika_image_get_base_type (image) != PIKA_INDEXED)
return pika_procedure_new_return_values (procedure,
PIKA_PDB_EXECUTION_ERROR,
NULL);
for (i = 0; i < 256; i++)
remap->map[i] = i;
if (strcmp (pika_procedure_get_name (procedure),
PLUG_IN_PROC_REMAP) == 0)
{
GBytes *col_args_bytes;
const guchar *col_args;
g_free (pika_image_get_colormap (image, NULL, &remap->n_cols));
col_args_bytes = PIKA_VALUES_GET_BYTES (args, 0);
col_args = g_bytes_get_data (col_args_bytes, NULL);
switch (run_mode)
{
case PIKA_RUN_INTERACTIVE:
g_signal_connect (remap->app, "activate", G_CALLBACK (on_app_activate), remap);
g_application_run (G_APPLICATION (remap->app), 0, NULL);
g_clear_object (&remap->app);
if (! remap_ok)
return pika_procedure_new_return_values (procedure,
PIKA_PDB_CANCEL,
NULL);
break;
case PIKA_RUN_NONINTERACTIVE:
if (remap->n_cols != g_bytes_get_size (col_args_bytes))
return pika_procedure_new_return_values (procedure,
PIKA_PDB_CALLING_ERROR,
NULL);
for (i = 0; i < remap->n_cols; i++)
remap->map[i] = col_args[i];
break;
case PIKA_RUN_WITH_LAST_VALS:
pika_get_data (PLUG_IN_PROC_REMAP, remap->map);
break;
}
if (! real_remap (image, remap->n_cols, remap->map))
return pika_procedure_new_return_values (procedure,
PIKA_PDB_EXECUTION_ERROR,
NULL);
if (run_mode == PIKA_RUN_INTERACTIVE)
pika_set_data (PLUG_IN_PROC_REMAP, remap->map, sizeof (remap->map));
if (run_mode != PIKA_RUN_NONINTERACTIVE)
pika_displays_flush ();
}
else if (strcmp (pika_procedure_get_name (procedure),
PLUG_IN_PROC_SWAP) == 0)
{
gint index1 = PIKA_VALUES_GET_INT (args, 0);
gint index2 = PIKA_VALUES_GET_INT (args, 1);
guchar tmp;
gint n_cols;
if (run_mode != PIKA_RUN_NONINTERACTIVE)
return pika_procedure_new_return_values (procedure,
PIKA_PDB_CALLING_ERROR,
NULL);
g_free (pika_image_get_colormap (image, NULL, &n_cols));
if (index1 >= n_cols || index2 >= n_cols)
return pika_procedure_new_return_values (procedure,
PIKA_PDB_CALLING_ERROR,
NULL);
tmp = remap->map[index1];
remap->map[index1] = remap->map[index2];
remap->map[index2] = tmp;
if (! real_remap (image, n_cols, remap->map))
return pika_procedure_new_return_values (procedure,
PIKA_PDB_EXECUTION_ERROR,
NULL);
}
return pika_procedure_new_return_values (procedure, PIKA_PDB_SUCCESS, NULL);
}
static gboolean
real_remap (PikaImage *image,
gint num_colors,
guchar *map)
{
guchar *cmap;
guchar *new_cmap;
guchar *new_cmap_i;
gint ncols;
GList *layers;
GList *list;
glong pixels = 0;
glong processed = 0;
guchar pixel_map[256];
gboolean valid[256];
gint i;
cmap = pika_image_get_colormap (image, NULL, &ncols);
g_return_val_if_fail (cmap != NULL, FALSE);
g_return_val_if_fail (ncols > 0, FALSE);
if (num_colors != ncols)
{
g_message (_("Invalid remap array was passed to remap function"));
return FALSE;
}
for (i = 0; i < ncols; i++)
valid[i] = FALSE;
for (i = 0; i < ncols; i++)
{
if (map[i] >= ncols)
{
g_message (_("Invalid remap array was passed to remap function"));
return FALSE;
}
valid[map[i]] = TRUE;
pixel_map[map[i]] = i;
}
for (i = 0; i < ncols; i++)
if (valid[i] == FALSE)
{
g_message (_("Invalid remap array was passed to remap function"));
return FALSE;
}
new_cmap = g_new (guchar, ncols * 3);
new_cmap_i = new_cmap;
for (i = 0; i < ncols; i++)
{
gint j = map[i] * 3;
*new_cmap_i++ = cmap[j];
*new_cmap_i++ = cmap[j + 1];
*new_cmap_i++ = cmap[j + 2];
}
pika_image_undo_group_start (image);
pika_image_set_colormap (image, new_cmap, ncols);
g_free (cmap);
g_free (new_cmap);
pika_progress_init (_("Rearranging the colormap"));
/* There is no needs to process the layers recursively, because
* indexed images cannot have layer groups.
*/
layers = pika_image_list_layers (image);
for (list = layers; list; list = list->next)
pixels +=
pika_drawable_get_width (list->data) * pika_drawable_get_height (list->data);
for (list = layers; list; list = list->next)
{
GeglBuffer *buffer;
GeglBuffer *shadow;
const Babl *format;
GeglBufferIterator *iter;
GeglRectangle *src_roi;
GeglRectangle *dest_roi;
gint width, height, bpp;
gint update = 0;
buffer = pika_drawable_get_buffer (list->data);
shadow = pika_drawable_get_shadow_buffer (list->data);
width = gegl_buffer_get_width (buffer);
height = gegl_buffer_get_height (buffer);
format = gegl_buffer_get_format (buffer);
bpp = babl_format_get_bytes_per_pixel (format);
iter = gegl_buffer_iterator_new (buffer,
GEGL_RECTANGLE (0, 0, width, height), 0,
format,
GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 2);
src_roi = &iter->items[0].roi;
gegl_buffer_iterator_add (iter, shadow,
GEGL_RECTANGLE (0, 0, width, height), 0,
format,
GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE);
dest_roi = &iter->items[1].roi;
while (gegl_buffer_iterator_next (iter))
{
const guchar *src_row = iter->items[0].data;
guchar *dest_row = iter->items[1].data;
gint y;
for (y = 0; y < src_roi->height; y++)
{
const guchar *src = src_row;
guchar *dest = dest_row;
gint x;
if (bpp == 1)
{
for (x = 0; x < src_roi->width; x++)
*dest++ = pixel_map[*src++];
}
else
{
for (x = 0; x < src_roi->width; x++)
{
*dest++ = pixel_map[*src++];
*dest++ = *src++;
}
}
src_row += src_roi->width * bpp;
dest_row += dest_roi->width * bpp;
}
processed += src_roi->width * src_roi->height;
update %= 16;
if (update == 0)
pika_progress_update ((gdouble) processed / pixels);
update++;
}
g_object_unref (buffer);
g_object_unref (shadow);
pika_drawable_merge_shadow (list->data, TRUE);
pika_drawable_update (list->data, 0, 0, width, height);
}
g_list_free (layers);
pika_progress_update (1.0);
pika_image_undo_group_end (image);
return TRUE;
}
/* dialog */
#define RESPONSE_RESET 1
enum
{
COLOR_INDEX,
COLOR_INDEX_TEXT,
COLOR_RGB,
COLOR_H,
COLOR_S,
COLOR_V,
NUM_COLS
};
static gint reverse_order[256];
static void
remap_sort (GtkTreeSortable *store,
gint column,
GtkSortType order)
{
gtk_tree_sortable_set_sort_column_id (store, column, order);
gtk_tree_sortable_set_sort_column_id (store,
GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID, 0);
}
static void remap_sort_hue_action (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
remap_sort (GTK_TREE_SORTABLE (store), COLOR_H, GTK_SORT_ASCENDING);
}
static void remap_sort_sat_action (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
remap_sort (GTK_TREE_SORTABLE (store), COLOR_S, GTK_SORT_ASCENDING);
}
static void remap_sort_val_action (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
remap_sort (GTK_TREE_SORTABLE (store), COLOR_V, GTK_SORT_ASCENDING);
}
static void
remap_reset_action (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
remap_sort (GTK_TREE_SORTABLE (store), COLOR_INDEX,
GTK_SORT_ASCENDING);
}
static void
remap_reverse_action (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
gtk_list_store_reorder (store, reverse_order);
}
static gboolean
remap_popup_menu (GtkWidget *widget,
GdkEventButton *event,
PikaRemap *remap)
{
GtkWidget *menu;
menu = gtk_menu_new_from_model (G_MENU_MODEL (remap->menu));
gtk_menu_attach_to_widget (GTK_MENU (menu), GTK_WIDGET (window), NULL);
gtk_menu_popup_at_pointer (GTK_MENU (menu), (GdkEvent *) event);
return TRUE;
}
static gboolean
remap_button_press (GtkWidget *widget,
GdkEventButton *event,
PikaRemap *remap)
{
if (gdk_event_triggers_context_menu ((GdkEvent *) event))
return remap_popup_menu (widget, event, remap);
return FALSE;
}
static void
remap_response (GtkWidget *dialog,
gint response_id,
PikaRemap *remap)
{
switch (response_id)
{
case RESPONSE_RESET:
remap_reset_action (NULL, NULL, NULL);
break;
case GTK_RESPONSE_OK:
remap_ok = TRUE;
/* fallthrough */
default:
gtk_widget_destroy (GTK_WIDGET (window));
break;
}
}
static gboolean
remap_dialog (PikaImage *image,
guchar *map,
PikaRemap *remap)
{
GtkWidget *dialog;
GtkWidget *vbox;
GtkWidget *box;
GtkWidget *iconview;
GtkCellRenderer *renderer;
GtkTreeIter iter;
guchar *cmap;
gint ncols, i;
gboolean valid;
pika_ui_init (PLUG_IN_BINARY);
dialog = pika_dialog_new (_("Rearrange Colormap"), PLUG_IN_ROLE,
GTK_WIDGET (window), 0,
pika_standard_help_func, PLUG_IN_PROC_REMAP,
_("_Reset"), RESPONSE_RESET,
_("_Cancel"), GTK_RESPONSE_CANCEL,
_("_OK"), GTK_RESPONSE_OK,
NULL);
pika_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
RESPONSE_RESET,
GTK_RESPONSE_OK,
GTK_RESPONSE_CANCEL,
-1);
pika_window_set_transient (GTK_WINDOW (dialog));
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
vbox, TRUE, TRUE, 0);
cmap = pika_image_get_colormap (image, NULL, &ncols);
g_return_val_if_fail ((ncols > 0) && (ncols <= 256), FALSE);
store = gtk_list_store_new (NUM_COLS,
G_TYPE_INT, G_TYPE_STRING, PIKA_TYPE_RGB,
G_TYPE_DOUBLE, G_TYPE_DOUBLE, G_TYPE_DOUBLE);
for (i = 0; i < ncols; i++)
{
PikaRGB rgb;
PikaHSV hsv;
gint index = map[i];
gchar *text = g_strdup_printf ("%d", index);
pika_rgb_set_uchar (&rgb,
cmap[index * 3],
cmap[index * 3 + 1],
cmap[index * 3 + 2]);
pika_rgb_to_hsv (&rgb, &hsv);
reverse_order[i] = ncols - i - 1;
gtk_list_store_append (store, &iter);
gtk_list_store_set (store, &iter,
COLOR_INDEX, index,
COLOR_INDEX_TEXT, text,
COLOR_RGB, &rgb,
COLOR_H, hsv.h,
COLOR_S, hsv.s,
COLOR_V, hsv.v,
-1);
g_free (text);
}
g_free (cmap);
iconview = gtk_icon_view_new_with_model (GTK_TREE_MODEL (store));
g_object_unref (store);
gtk_box_pack_start (GTK_BOX (vbox), iconview, TRUE, TRUE, 0);
gtk_icon_view_set_selection_mode (GTK_ICON_VIEW (iconview),
GTK_SELECTION_SINGLE);
gtk_icon_view_set_item_orientation (GTK_ICON_VIEW (iconview),
GTK_ORIENTATION_VERTICAL);
gtk_icon_view_set_columns (GTK_ICON_VIEW (iconview), 16);
gtk_icon_view_set_row_spacing (GTK_ICON_VIEW (iconview), 0);
gtk_icon_view_set_column_spacing (GTK_ICON_VIEW (iconview), 0);
gtk_icon_view_set_reorderable (GTK_ICON_VIEW (iconview), TRUE);
renderer = pika_cell_renderer_color_new ();
gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (iconview), renderer, TRUE);
gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (iconview), renderer,
"color", COLOR_RGB,
NULL);
g_object_set (renderer,
"width", 24,
NULL);
renderer = gtk_cell_renderer_text_new ();
gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (iconview), renderer, TRUE);
gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (iconview), renderer,
"text", COLOR_INDEX_TEXT,
NULL);
g_object_set (renderer,
"size-points", 6.0,
"xalign", 0.5,
"ypad", 0,
NULL);
g_signal_connect (iconview, "popup-menu",
G_CALLBACK (remap_popup_menu),
remap);
g_signal_connect (iconview, "button-press-event",
G_CALLBACK (remap_button_press),
remap);
g_action_map_add_action_entries (G_ACTION_MAP (window),
ACTIONS, G_N_ELEMENTS (ACTIONS),
remap);
box = pika_hint_box_new (_("Drag and drop colors to rearrange the colormap. "
"The numbers shown are the original indices. "
"Right-click for a menu with sort options."));
gtk_box_pack_start (GTK_BOX (vbox), box, FALSE, FALSE, 0);
gtk_widget_show (box);
g_signal_connect (dialog, "response",
G_CALLBACK (remap_response),
remap);
gtk_widget_show_all (dialog);
i = 0;
for (valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter);
valid;
valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter))
{
gint index;
gtk_tree_model_get (GTK_TREE_MODEL (store), &iter,
COLOR_INDEX, &index,
-1);
map[i++] = index;
}
return remap_ok;
}
static void
on_app_activate (GApplication *gapp,
gpointer user_data)
{
PikaRemap *remap = PIKA_REMAP (user_data);
GtkApplication *app = GTK_APPLICATION (gapp);
window = GTK_WINDOW (gtk_application_window_new (app));
gtk_window_set_title (window, _("Rearrange Colors"));
gtk_window_set_role (window, PLUG_IN_ROLE);
pika_help_connect (GTK_WIDGET (window),
pika_standard_help_func, PLUG_IN_PROC_REMAP,
window, NULL);
remap_dialog (remap->image, remap->map, remap);
gtk_application_set_accels_for_action (app, "win.sort-hue", (const char*[]) { NULL });
gtk_application_set_accels_for_action (app, "win.sort-sat", (const char*[]) { NULL });
gtk_application_set_accels_for_action (app, "win.sort-val", (const char*[]) { NULL });
gtk_application_set_accels_for_action (app, "win.reverse", (const char*[]) { NULL });
gtk_application_set_accels_for_action (app, "win.reset", (const char*[]) { NULL });
}

1513
plug-ins/common/compose.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,882 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* 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/>.
*/
#include "config.h"
#include <string.h>
#include "libpika/pika.h"
#include "libpika/pikaui.h"
#include "libpika/stdplugins-intl.h"
#define PLUG_IN_PROC "plug-in-retinex"
#define PLUG_IN_BINARY "contrast-retinex"
#define PLUG_IN_ROLE "pika-contrast-retinex"
#define MAX_RETINEX_SCALES 8
#define MIN_GAUSSIAN_SCALE 16
#define MAX_GAUSSIAN_SCALE 250
typedef enum
{
filter_uniform,
filter_low,
filter_high
} FilterMode;
/*
Definit comment sont repartis les
differents filtres en fonction de
l'echelle (~= ecart type de la gaussienne)
*/
#define RETINEX_UNIFORM 0
#define RETINEX_LOW 1
#define RETINEX_HIGH 2
static gfloat RetinexScales[MAX_RETINEX_SCALES];
typedef struct
{
gint N;
gfloat sigma;
gdouble B;
gdouble b[4];
} gauss3_coefs;
typedef struct _Retinex Retinex;
typedef struct _RetinexClass RetinexClass;
struct _Retinex
{
PikaPlugIn parent_instance;
};
struct _RetinexClass
{
PikaPlugInClass parent_class;
};
#define RETINEX_TYPE (retinex_get_type ())
#define RETINEX (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), RETINEX_TYPE, Retinex))
GType retinex_get_type (void) G_GNUC_CONST;
static GList * retinex_query_procedures (PikaPlugIn *plug_in);
static PikaProcedure * retinex_create_procedure (PikaPlugIn *plug_in,
const gchar *name);
static PikaValueArray * retinex_run (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
const PikaValueArray *args,
gpointer run_data);
static gboolean retinex_dialog (PikaProcedure *procedure,
GObject *config,
PikaDrawable *drawable);
static void retinex (GObject *config,
PikaDrawable *drawable,
PikaPreview *preview);
static void retinex_preview (GtkWidget *widget,
GObject *config);
static void retinex_scales_distribution (gfloat *scales,
gint nscales,
gint mode,
gint s);
static void compute_mean_var (gfloat *src,
gfloat *mean,
gfloat *var,
gint size,
gint bytes);
static void compute_coefs3 (gauss3_coefs *c,
gfloat sigma);
static void gausssmooth (gfloat *in,
gfloat *out,
gint size,
gint rowtride,
gauss3_coefs *c);
/*
* MSRCR = MultiScale Retinex with Color Restoration
*/
static void MSRCR (GObject *config,
guchar *src,
gint width,
gint height,
gint bytes,
gboolean preview_mode);
G_DEFINE_TYPE (Retinex, retinex, PIKA_TYPE_PLUG_IN)
PIKA_MAIN (RETINEX_TYPE)
DEFINE_STD_SET_I18N
static void
retinex_class_init (RetinexClass *klass)
{
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
plug_in_class->query_procedures = retinex_query_procedures;
plug_in_class->create_procedure = retinex_create_procedure;
plug_in_class->set_i18n = STD_SET_I18N;
}
static void
retinex_init (Retinex *retinex)
{
}
static GList *
retinex_query_procedures (PikaPlugIn *plug_in)
{
return g_list_append (NULL, g_strdup (PLUG_IN_PROC));
}
static PikaProcedure *
retinex_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,
retinex_run, NULL, NULL);
pika_procedure_set_image_types (procedure, "RGB*");
pika_procedure_set_sensitivity_mask (procedure,
PIKA_PROCEDURE_SENSITIVE_DRAWABLE);
pika_procedure_set_menu_label (procedure, _("Retine_x..."));
pika_procedure_add_menu_path (procedure, "<Image>/Colors/Tone Mapping");
pika_procedure_set_documentation (procedure,
_("Enhance contrast using the "
"Retinex method"),
_("The Retinex Image Enhancement "
"Algorithm is an automatic image "
"enhancement method that enhances "
"a digital image in terms of dynamic "
"range compression, color independence "
"from the spectral distribution of the "
"scene illuminant, and color/lightness "
"rendition."),
name);
pika_procedure_set_attribution (procedure,
"Fabien Pelisson",
"Fabien Pelisson",
"2003");
PIKA_PROC_ARG_INT (procedure, "scale",
_("Scal_e"),
_("Biggest scale value"),
MIN_GAUSSIAN_SCALE, MAX_GAUSSIAN_SCALE, 240,
G_PARAM_READWRITE);
PIKA_PROC_ARG_INT (procedure, "nscales",
_("Scale _division"),
_("Number of scales"),
0, MAX_RETINEX_SCALES, 3,
G_PARAM_READWRITE);
PIKA_PROC_ARG_INT (procedure, "scales-mode",
_("Le_vel"),
_("Retinex distribution through scales "
"{ Uniform (0), Low (1), High (2) }"),
RETINEX_UNIFORM, RETINEX_HIGH, RETINEX_UNIFORM,
G_PARAM_READWRITE);
PIKA_PROC_ARG_DOUBLE (procedure, "cvar",
_("Dy_namic"),
_("Variance value"),
0, 4, 1.2,
G_PARAM_READWRITE);
}
return procedure;
}
static PikaValueArray *
retinex_run (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
const PikaValueArray *args,
gpointer run_data)
{
PikaProcedureConfig *config;
PikaDrawable *drawable;
gint x, y, width, height;
gegl_init (NULL, NULL);
config = pika_procedure_create_config (procedure);
pika_procedure_config_begin_run (config, NULL, run_mode, args);
if (n_drawables != 1)
{
GError *error = NULL;
pika_procedure_config_end_run (config, PIKA_PDB_EXECUTION_ERROR);
g_object_unref (config);
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 (! pika_drawable_mask_intersect (drawable, &x, &y, &width, &height) ||
width < MIN_GAUSSIAN_SCALE ||
height < MIN_GAUSSIAN_SCALE)
{
pika_procedure_config_end_run (config, PIKA_PDB_EXECUTION_ERROR);
g_object_unref (config);
return pika_procedure_new_return_values (procedure,
PIKA_PDB_EXECUTION_ERROR,
NULL);
}
switch (run_mode)
{
case PIKA_RUN_INTERACTIVE:
if (! retinex_dialog (procedure, G_OBJECT (config), drawable))
{
pika_procedure_config_end_run (config, PIKA_PDB_CANCEL);
g_object_unref (config);
return pika_procedure_new_return_values (procedure,
PIKA_PDB_CANCEL,
NULL);
}
break;
default:
break;
}
if (pika_drawable_is_rgb (drawable))
{
pika_progress_init (_("Retinex"));
retinex (G_OBJECT (config), drawable, NULL);
if (run_mode != PIKA_RUN_NONINTERACTIVE)
pika_displays_flush ();
}
else
{
pika_procedure_config_end_run (config, PIKA_PDB_EXECUTION_ERROR);
g_object_unref (config);
return pika_procedure_new_return_values (procedure,
PIKA_PDB_EXECUTION_ERROR,
NULL);
}
pika_procedure_config_end_run (config, PIKA_PDB_SUCCESS);
g_object_unref (config);
return pika_procedure_new_return_values (procedure, PIKA_PDB_SUCCESS, NULL);
}
static gboolean
retinex_dialog (PikaProcedure *procedure,
GObject *config,
PikaDrawable *drawable)
{
GtkWidget *dialog;
GtkWidget *preview;
GtkWidget *combo;
GtkWidget *scale;
GtkListStore *store;
gboolean run;
pika_ui_init (PLUG_IN_BINARY);
dialog = pika_procedure_dialog_new (procedure,
PIKA_PROCEDURE_CONFIG (config),
_("Retinex Image Enhancement"));
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_zoom_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_set_margin_bottom (preview, 12);
gtk_widget_show (preview);
store = pika_int_store_new (_("Uniform"), filter_uniform,
_("Low"), filter_low,
_("High"), filter_high,
NULL);
combo = pika_procedure_dialog_get_int_combo (PIKA_PROCEDURE_DIALOG (dialog),
"scales-mode",
PIKA_INT_STORE (store));
gtk_widget_set_margin_bottom (combo, 12);
scale = pika_procedure_dialog_get_scale_entry (PIKA_PROCEDURE_DIALOG (dialog),
"scale", 1.0);
gtk_widget_set_margin_bottom (scale, 12);
scale = pika_procedure_dialog_get_scale_entry (PIKA_PROCEDURE_DIALOG (dialog),
"nscales", 1.0);
gtk_widget_set_margin_bottom (scale, 12);
scale = pika_procedure_dialog_get_scale_entry (PIKA_PROCEDURE_DIALOG (dialog),
"cvar", 1.0);
gtk_widget_set_margin_bottom (scale, 12);
pika_procedure_dialog_fill_box (PIKA_PROCEDURE_DIALOG (dialog),
"retinex-vbox", "scales-mode",
"scale", "nscales", "cvar", NULL);
g_object_set_data (config, "drawable", drawable);
g_signal_connect (preview, "invalidated",
G_CALLBACK (retinex_preview),
config);
g_signal_connect_swapped (config, "notify",
G_CALLBACK (pika_preview_invalidate),
preview);
pika_procedure_dialog_fill (PIKA_PROCEDURE_DIALOG (dialog),
"retinex-vbox",
NULL);
gtk_widget_show (dialog);
run = pika_procedure_dialog_run (PIKA_PROCEDURE_DIALOG (dialog));
gtk_widget_destroy (dialog);
return run;
}
/*
* Applies the algorithm
*/
static void
retinex (GObject *config,
PikaDrawable *drawable,
PikaPreview *preview)
{
GeglBuffer *src_buffer;
GeglBuffer *dest_buffer;
const Babl *format;
guchar *src = NULL;
guchar *psrc = NULL;
gint x, y, width, height;
gint size, bytes;
/*
* Get the size of the current image or its selection.
*/
if (preview)
{
src = pika_zoom_preview_get_source (PIKA_ZOOM_PREVIEW (preview),
&width, &height, &bytes);
}
else
{
if (! pika_drawable_mask_intersect (drawable,
&x, &y, &width, &height))
return;
if (pika_drawable_has_alpha (drawable))
format = babl_format ("R'G'B'A u8");
else
format = babl_format ("R'G'B' u8");
bytes = babl_format_get_bytes_per_pixel (format);
/* Allocate memory */
size = width * height * bytes;
src = g_try_malloc (sizeof (guchar) * size);
if (src == NULL)
{
g_warning ("Failed to allocate memory");
return;
}
memset (src, 0, sizeof (guchar) * size);
/* Fill allocated memory with pixel data */
src_buffer = pika_drawable_get_buffer (drawable);
gegl_buffer_get (src_buffer, GEGL_RECTANGLE (x, y, width, height), 1.0,
format, src,
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
}
/*
Algorithm for Multi-scale Retinex with color Restoration (MSRCR).
*/
psrc = src;
MSRCR (config, psrc, width, height, bytes, preview != NULL);
if (preview)
{
pika_preview_draw_buffer (preview, psrc, width * bytes);
}
else
{
dest_buffer = pika_drawable_get_shadow_buffer (drawable);
gegl_buffer_set (dest_buffer, GEGL_RECTANGLE (x, y, width, height), 0,
format, psrc,
GEGL_AUTO_ROWSTRIDE);
g_object_unref (src_buffer);
g_object_unref (dest_buffer);
pika_progress_update (1.0);
pika_drawable_merge_shadow (drawable, TRUE);
pika_drawable_update (drawable, x, y, width, height);
}
g_free (src);
}
static void
retinex_preview (GtkWidget *widget,
GObject *config)
{
PikaPreview *preview = PIKA_PREVIEW (widget);
PikaDrawable *drawable = g_object_get_data (config, "drawable");
retinex (config, drawable, preview);
}
/*
* calculate scale values for desired distribution.
*/
static void
retinex_scales_distribution (gfloat *scales,
gint nscales,
gint mode,
gint s)
{
if (nscales == 1)
{ /* For one filter we choose the median scale */
scales[0] = (gint) s / 2;
}
else if (nscales == 2)
{ /* For two filters we choose the median and maximum scale */
scales[0] = (gint) s / 2;
scales[1] = (gint) s;
}
else
{
gfloat size_step = (gfloat) s / (gfloat) nscales;
gint i;
switch(mode)
{
case RETINEX_UNIFORM:
for(i = 0; i < nscales; ++i)
scales[i] = 2. + (gfloat) i * size_step;
break;
case RETINEX_LOW:
size_step = (gfloat) log(s - 2.0) / (gfloat) nscales;
for (i = 0; i < nscales; ++i)
scales[i] = 2. + pow (10, (i * size_step) / log (10));
break;
case RETINEX_HIGH:
size_step = (gfloat) log(s - 2.0) / (gfloat) nscales;
for (i = 0; i < nscales; ++i)
scales[i] = s - pow (10, (i * size_step) / log (10));
break;
default:
break;
}
}
}
/*
* Calculate the coefficients for the recursive filter algorithm
* Fast Computation of gaussian blurring.
*/
static void
compute_coefs3 (gauss3_coefs *c, gfloat sigma)
{
/*
* Papers: "Recursive Implementation of the gaussian filter.",
* Ian T. Young , Lucas J. Van Vliet, Signal Processing 44, Elsevier 1995.
* formula: 11b computation of q
* 8c computation of b0..b1
* 10 alpha is normalization constant B
*/
gfloat q, q2, q3;
if (sigma >= 2.5)
{
q = 0.98711 * sigma - 0.96330;
}
else if ((sigma >= 0.5) && (sigma < 2.5))
{
q = 3.97156 - 4.14554 * (gfloat) sqrt ((double) 1 - 0.26891 * sigma);
}
else
{
q = 0.1147705018520355224609375;
}
q2 = q * q;
q3 = q * q2;
c->b[0] = (1.57825+(2.44413*q)+(1.4281 *q2)+(0.422205*q3));
c->b[1] = ( (2.44413*q)+(2.85619*q2)+(1.26661 *q3));
c->b[2] = ( -((1.4281*q2)+(1.26661 *q3)));
c->b[3] = ( (0.422205*q3));
c->B = 1.0-((c->b[1]+c->b[2]+c->b[3])/c->b[0]);
c->sigma = sigma;
c->N = 3;
/*
g_printerr ("q %f\n", q);
g_printerr ("q2 %f\n", q2);
g_printerr ("q3 %f\n", q3);
g_printerr ("c->b[0] %f\n", c->b[0]);
g_printerr ("c->b[1] %f\n", c->b[1]);
g_printerr ("c->b[2] %f\n", c->b[2]);
g_printerr ("c->b[3] %f\n", c->b[3]);
g_printerr ("c->B %f\n", c->B);
g_printerr ("c->sigma %f\n", c->sigma);
g_printerr ("c->N %d\n", c->N);
*/
}
static void
gausssmooth (gfloat *in, gfloat *out, gint size, gint rowstride, gauss3_coefs *c)
{
/*
* Papers: "Recursive Implementation of the gaussian filter.",
* Ian T. Young , Lucas J. Van Vliet, Signal Processing 44, Elsevier 1995.
* formula: 9a forward filter
* 9b backward filter
* fig7 algorithm
*/
gint i,n, bufsize;
gfloat *w1,*w2;
/* forward pass */
bufsize = size+3;
size -= 1;
w1 = (gfloat *) g_try_malloc (bufsize * sizeof (gfloat));
w2 = (gfloat *) g_try_malloc (bufsize * sizeof (gfloat));
w1[0] = in[0];
w1[1] = in[0];
w1[2] = in[0];
for ( i = 0 , n=3; i <= size ; i++, n++)
{
w1[n] = (gfloat)(c->B*in[i*rowstride] +
((c->b[1]*w1[n-1] +
c->b[2]*w1[n-2] +
c->b[3]*w1[n-3] ) / c->b[0]));
}
/* backward pass */
w2[size+1]= w1[size+3];
w2[size+2]= w1[size+3];
w2[size+3]= w1[size+3];
for (i = size, n = i; i >= 0; i--, n--)
{
w2[n]= out[i * rowstride] = (gfloat)(c->B*w1[n+3] +
((c->b[1]*w2[n+1] +
c->b[2]*w2[n+2] +
c->b[3]*w2[n+3] ) / c->b[0]));
}
g_free (w1);
g_free (w2);
}
/*
* This function is the heart of the algo.
* (a) Filterings at several scales and sumarize the results.
* (b) Calculation of the final values.
*/
static void
MSRCR (GObject *config,
guchar *src,
gint width,
gint height,
gint bytes,
gboolean preview_mode)
{
gint scale,row,col;
gint i,j;
gint size;
gint channel;
guchar *psrc = NULL; /* backup pointer for src buffer */
gfloat *dst = NULL; /* float buffer for algorithm */
gfloat *pdst = NULL; /* backup pointer for float buffer */
gfloat *in, *out;
gint channelsize; /* Float memory cache for one channel */
gfloat weight;
gauss3_coefs coef;
gfloat mean, var;
gfloat mini, range, maxi;
gfloat alpha;
gfloat gain;
gfloat offset;
gdouble max_preview = 0.0;
gint scales_mode;
gint config_scale;
gint nscales;
gdouble cvar;
g_object_get (config,
"scales-mode", &scales_mode,
"scale", &config_scale,
"nscales", &nscales,
"cvar", &cvar,
NULL);
if (!preview_mode)
{
pika_progress_init (_("Retinex: filtering"));
max_preview = 3 * nscales;
}
/* Allocate all the memory needed for algorithm*/
size = width * height * bytes;
dst = g_try_malloc (size * sizeof (gfloat));
if (dst == NULL)
{
g_warning ("Failed to allocate memory");
return;
}
memset (dst, 0, size * sizeof (gfloat));
channelsize = (width * height);
in = (gfloat *) g_try_malloc (channelsize * sizeof (gfloat));
if (in == NULL)
{
g_free (dst);
g_warning ("Failed to allocate memory");
return; /* do some clever stuff */
}
out = (gfloat *) g_try_malloc (channelsize * sizeof (gfloat));
if (out == NULL)
{
g_free (in);
g_free (dst);
g_warning ("Failed to allocate memory");
return; /* do some clever stuff */
}
/*
Calculate the scales of filtering according to the
number of filter and their distribution.
*/
retinex_scales_distribution (RetinexScales,
nscales, scales_mode, config_scale);
/*
Filtering according to the various scales.
Summerize the results of the various filters according to a
specific weight(here equivalent for all).
*/
weight = 1./ (gfloat) nscales;
/*
The recursive filtering algorithm needs different coefficients according
to the selected scale (~ = standard deviation of Gaussian).
*/
for (channel = 0; channel < 3; channel++)
{
gint pos;
for (i = 0, pos = channel; i < channelsize ; i++, pos += bytes)
{
/* 0-255 => 1-256 */
in[i] = (gfloat)(src[pos] + 1.0);
}
for (scale = 0; scale < nscales; scale++)
{
compute_coefs3 (&coef, RetinexScales[scale]);
/*
* Filtering (smoothing) Gaussian recursive.
*
* Filter rows first
*/
for (row=0 ;row < height; row++)
{
pos = row * width;
gausssmooth (in + pos, out + pos, width, 1, &coef);
}
memcpy(in, out, channelsize * sizeof(gfloat));
memset(out, 0 , channelsize * sizeof(gfloat));
/*
* Filtering (smoothing) Gaussian recursive.
*
* Second columns
*/
for (col=0; col < width; col++)
{
pos = col;
gausssmooth(in + pos, out + pos, height, width, &coef);
}
/*
Summarize the filtered values.
In fact one calculates a ratio between the original values and the filtered values.
*/
for (i = 0, pos = channel; i < channelsize; i++, pos += bytes)
{
dst[pos] += weight * (log (src[pos] + 1.) - log (out[i]));
}
if (!preview_mode)
pika_progress_update ((channel * nscales + scale) /
max_preview);
}
}
g_free(in);
g_free(out);
/*
Final calculation with original value and cumulated filter values.
The parameters gain, alpha and offset are constants.
*/
/* Ci(x,y)=log[a Ii(x,y)]-log[ Ei=1-s Ii(x,y)] */
alpha = 128.;
gain = 1.;
offset = 0.;
for (i = 0; i < size; i += bytes)
{
gfloat logl;
psrc = src+i;
pdst = dst+i;
logl = log((gfloat)psrc[0] + (gfloat)psrc[1] + (gfloat)psrc[2] + 3.);
pdst[0] = gain * ((log(alpha * (psrc[0]+1.)) - logl) * pdst[0]) + offset;
pdst[1] = gain * ((log(alpha * (psrc[1]+1.)) - logl) * pdst[1]) + offset;
pdst[2] = gain * ((log(alpha * (psrc[2]+1.)) - logl) * pdst[2]) + offset;
}
/* if (!preview_mode)
pika_progress_update ((2.0 + (nscales * 3)) /
((nscales * 3) + 3));*/
/*
Adapt the dynamics of the colors according to the statistics of the first and second order.
The use of the variance makes it possible to control the degree of saturation of the colors.
*/
pdst = dst;
compute_mean_var (pdst, &mean, &var, size, bytes);
mini = mean - cvar * var;
maxi = mean + cvar * var;
range = maxi - mini;
if (!range)
range = 1.0;
for (i = 0; i < size; i+= bytes)
{
psrc = src + i;
pdst = dst + i;
for (j = 0 ; j < 3 ; j++)
{
gfloat c = 255 * ( pdst[j] - mini ) / range;
psrc[j] = (guchar) CLAMP (c, 0, 255);
}
}
g_free (dst);
}
/*
* Calculate the average and variance in one go.
*/
static void
compute_mean_var (gfloat *src, gfloat *mean, gfloat *var, gint size, gint bytes)
{
gfloat vsquared;
gint i,j;
gfloat *psrc;
vsquared = 0;
*mean = 0;
for (i = 0; i < size; i+= bytes)
{
psrc = src+i;
for (j = 0 ; j < 3 ; j++)
{
*mean += psrc[j];
vsquared += psrc[j] * psrc[j];
}
}
*mean /= (gfloat) size; /* mean */
vsquared /= (gfloat) size; /* mean (x^2) */
*var = ( vsquared - (*mean * *mean) );
*var = sqrt(*var); /* var */
}

View File

@ -0,0 +1,356 @@
/*
* ZealousCrop plug-in version 1.00
* by Adam D. Moss <adam@foxbox.org>
*
* 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/>.
*/
#include "config.h"
#include <libpika/pika.h>
#include "libpika/stdplugins-intl.h"
#define PLUG_IN_PROC "plug-in-zealouscrop"
#define EPSILON (1e-5)
#define FLOAT_IS_ZERO(value) (value > -EPSILON && value < EPSILON)
#define FLOAT_EQUAL(v1, v2) ((v1 - v2) > -EPSILON && (v1 - v2) < EPSILON)
typedef struct _Crop Crop;
typedef struct _CropClass CropClass;
struct _Crop
{
PikaPlugIn parent_instance;
};
struct _CropClass
{
PikaPlugInClass parent_class;
};
#define CROP_TYPE (crop_get_type ())
#define CROP (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CROP_TYPE, Crop))
GType crop_get_type (void) G_GNUC_CONST;
static GList * crop_query_procedures (PikaPlugIn *plug_in);
static PikaProcedure * crop_create_procedure (PikaPlugIn *plug_in,
const gchar *name);
static PikaValueArray * crop_run (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
const PikaValueArray *args,
gpointer run_data);
static inline gboolean colors_equal (const gfloat *col1,
const gfloat *col2,
gint components,
gboolean has_alpha);
static void do_zcrop (PikaDrawable *drawable,
PikaImage *image);
G_DEFINE_TYPE (Crop, crop, PIKA_TYPE_PLUG_IN)
PIKA_MAIN (CROP_TYPE)
DEFINE_STD_SET_I18N
static void
crop_class_init (CropClass *klass)
{
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
plug_in_class->query_procedures = crop_query_procedures;
plug_in_class->create_procedure = crop_create_procedure;
plug_in_class->set_i18n = STD_SET_I18N;
}
static void
crop_init (Crop *crop)
{
}
static GList *
crop_query_procedures (PikaPlugIn *plug_in)
{
return g_list_append (NULL, g_strdup (PLUG_IN_PROC));
}
static PikaProcedure *
crop_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,
crop_run, NULL, NULL);
pika_procedure_set_image_types (procedure, "*");
pika_procedure_set_sensitivity_mask (procedure,
PIKA_PROCEDURE_SENSITIVE_DRAWABLE);
pika_procedure_set_menu_label (procedure, _("_Zealous Crop"));
pika_procedure_add_menu_path (procedure, "<Image>/Image/[Crop]");
pika_procedure_set_documentation (procedure,
_("Autocrop unused space from "
"edges and middle"),
NULL,
name);
pika_procedure_set_attribution (procedure,
"Adam D. Moss",
"Adam D. Moss",
"1997");
}
return procedure;
}
static PikaValueArray *
crop_run (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
const PikaValueArray *args,
gpointer run_data)
{
PikaDrawable *drawable;
gegl_init (NULL, NULL);
pika_progress_init (_("Zealous cropping"));
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];
}
do_zcrop (drawable, image);
if (run_mode != PIKA_RUN_NONINTERACTIVE)
pika_displays_flush ();
return pika_procedure_new_return_values (procedure, PIKA_PDB_SUCCESS, NULL);
}
static inline gboolean
colors_equal (const gfloat *col1,
const gfloat *col2,
gint components,
gboolean has_alpha)
{
if (has_alpha &&
FLOAT_IS_ZERO (col1[components - 1]) &&
FLOAT_IS_ZERO (col2[components - 1]))
{
return TRUE;
}
else
{
gint b;
for (b = 0; b < components; b++)
{
if (! FLOAT_EQUAL (col1[b], col2[b]))
return FALSE;
}
return TRUE;
}
}
static void
do_zcrop (PikaDrawable *drawable,
PikaImage *image)
{
GeglBuffer *drawable_buffer;
GeglBuffer *shadow_buffer;
gfloat *linear_buf;
const Babl *format;
gint x, y, width, height;
gint components;
gint8 *killrows;
gint8 *killcols;
gint32 livingrows, livingcols, destrow, destcol;
PikaChannel *selection_copy;
gboolean has_alpha;
drawable_buffer = pika_drawable_get_buffer (drawable);
shadow_buffer = pika_drawable_get_shadow_buffer (drawable);
width = gegl_buffer_get_width (drawable_buffer);
height = gegl_buffer_get_height (drawable_buffer);
has_alpha = pika_drawable_has_alpha (drawable);
if (has_alpha)
format = babl_format ("R'G'B'A float");
else
format = babl_format ("R'G'B' float");
components = babl_format_get_n_components (format);
killrows = g_new (gint8, height);
killcols = g_new (gint8, width);
linear_buf = g_new (gfloat, (width > height ? width : height) * components);
/* search which rows to remove */
livingrows = 0;
for (y = 0; y < height; y++)
{
gegl_buffer_get (drawable_buffer, GEGL_RECTANGLE (0, y, width, 1),
1.0, format, linear_buf,
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
killrows[y] = TRUE;
for (x = components; x < width * components; x += components)
{
if (! colors_equal (linear_buf, &linear_buf[x], components, has_alpha))
{
livingrows++;
killrows[y] = FALSE;
break;
}
}
}
pika_progress_update (0.25);
/* search which columns to remove */
livingcols = 0;
for (x = 0; x < width; x++)
{
gegl_buffer_get (drawable_buffer, GEGL_RECTANGLE (x, 0, 1, height),
1.0, format, linear_buf,
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
killcols[x] = TRUE;
for (y = components; y < height * components; y += components)
{
if (! colors_equal (linear_buf, &linear_buf[y], components, has_alpha))
{
livingcols++;
killcols[x] = FALSE;
break;
}
}
}
pika_progress_update (0.5);
if ((livingcols == 0 || livingrows == 0) ||
(livingcols == width && livingrows == height))
{
g_message (_("Nothing to crop."));
g_object_unref (shadow_buffer);
g_object_unref (drawable_buffer);
g_free (linear_buf);
g_free (killrows);
g_free (killcols);
return;
}
/* restitute living rows */
destrow = 0;
for (y = 0; y < height; y++)
{
if (!killrows[y])
{
gegl_buffer_copy (drawable_buffer,
GEGL_RECTANGLE (0, y, width, 1),
GEGL_ABYSS_NONE,
shadow_buffer,
GEGL_RECTANGLE (0, destrow, width, 1));
destrow++;
}
}
pika_progress_update (0.75);
/* restitute living columns */
destcol = 0;
for (x = 0; x < width; x++)
{
if (!killcols[x])
{
gegl_buffer_copy (shadow_buffer,
GEGL_RECTANGLE (x, 0, 1, height),
GEGL_ABYSS_NONE,
shadow_buffer,
GEGL_RECTANGLE (destcol, 0, 1, height));
destcol++;
}
}
pika_progress_update (1.00);
pika_image_undo_group_start (image);
selection_copy = PIKA_CHANNEL (pika_selection_save (image));
pika_selection_none (image);
gegl_buffer_flush (shadow_buffer);
pika_drawable_merge_shadow (drawable, TRUE);
gegl_buffer_flush (drawable_buffer);
pika_image_select_item (image, PIKA_CHANNEL_OP_REPLACE,
PIKA_ITEM (selection_copy));
pika_image_remove_channel (image, selection_copy);
pika_image_crop (image, livingcols, livingrows, 0, 0);
pika_image_undo_group_end (image);
g_object_unref (shadow_buffer);
g_object_unref (drawable_buffer);
g_free (linear_buf);
g_free (killrows);
g_free (killcols);
}

3319
plug-ins/common/curve-bend.c Normal file

File diff suppressed because it is too large Load Diff

1019
plug-ins/common/decompose.c Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

868
plug-ins/common/despeckle.c Normal file
View File

@ -0,0 +1,868 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* Despeckle (adaptive median) filter
*
* Copyright 1997-1998 Michael Sweet (mike@easysw.com)
* optimized in 2010 by Przemyslaw Zych (kermidt.zed@gmail.com)
*
* 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/>.
*/
#include "config.h"
#include <stdlib.h>
#include <libpika/pika.h>
#include <libpika/pikaui.h>
#include "libpika/stdplugins-intl.h"
/*
* Constants...
*/
#define PLUG_IN_PROC "plug-in-despeckle"
#define PLUG_IN_BINARY "despeckle"
#define PLUG_IN_VERSION "May 2010"
#define SCALE_WIDTH 100
#define ENTRY_WIDTH 3
#define MAX_RADIUS 30
#define FILTER_ADAPTIVE 0x01
#define FILTER_RECURSIVE 0x02
/* List that stores pixels falling in to the same luma bucket */
#define MAX_LIST_ELEMS SQR(2 * MAX_RADIUS + 1)
typedef struct
{
const guchar *elems[MAX_LIST_ELEMS];
gint start;
gint count;
} PixelsList;
typedef struct
{
gint elems[256]; /* Number of pixels that fall into each luma bucket */
PixelsList origs[256]; /* Original pixels */
gint xmin;
gint ymin;
gint xmax;
gint ymax; /* Source rect */
} DespeckleHistogram;
typedef struct _Despeckle Despeckle;
typedef struct _DespeckleClass DespeckleClass;
struct _Despeckle
{
PikaPlugIn parent_instance;
};
struct _DespeckleClass
{
PikaPlugInClass parent_class;
};
#define DESPECKLE_TYPE (despeckle_get_type ())
#define DESPECKLE (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), DESPECKLE_TYPE, Despeckle))
GType despeckle_get_type (void) G_GNUC_CONST;
static GList * despeckle_query_procedures (PikaPlugIn *plug_in);
static PikaProcedure * despeckle_create_procedure (PikaPlugIn *plug_in,
const gchar *name);
static PikaValueArray * despeckle_run (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
const PikaValueArray *args,
gpointer run_data);
static void despeckle (PikaDrawable *drawable,
GObject *config);
static void despeckle_median (GObject *config,
guchar *src,
guchar *dst,
gint width,
gint height,
gint bpp,
gboolean preview);
static gboolean despeckle_dialog (PikaProcedure *procedure,
GObject *config,
PikaDrawable *drawable);
static void preview_update (GtkWidget *preview,
GObject *config);
G_DEFINE_TYPE (Despeckle, despeckle, PIKA_TYPE_PLUG_IN)
PIKA_MAIN (DESPECKLE_TYPE)
DEFINE_STD_SET_I18N
/* Number of pixels in actual histogram falling into each category */
static gint hist0; /* Less than min threshold */
static gint hist255; /* More than max threshold */
static gint histrest; /* From min to max */
static DespeckleHistogram histogram;
static void
despeckle_class_init (DespeckleClass *klass)
{
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
plug_in_class->query_procedures = despeckle_query_procedures;
plug_in_class->create_procedure = despeckle_create_procedure;
plug_in_class->set_i18n = STD_SET_I18N;
}
static void
despeckle_init (Despeckle *despeckle)
{
}
static GList *
despeckle_query_procedures (PikaPlugIn *plug_in)
{
return g_list_append (NULL, g_strdup (PLUG_IN_PROC));
}
static PikaProcedure *
despeckle_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,
despeckle_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, _("Des_peckle..."));
pika_procedure_add_menu_path (procedure, "<Image>/Filters/Enhance");
pika_procedure_set_documentation (procedure,
_("Remove speckle noise from the "
"image"),
_("This plug-in selectively performs "
"a median or adaptive box filter on "
"an image."),
name);
pika_procedure_set_attribution (procedure,
"Michael Sweet <mike@easysw.com>",
"Copyright 1997-1998 by Michael Sweet",
PLUG_IN_VERSION);
PIKA_PROC_ARG_INT (procedure, "radius",
_("R_adius"),
_("Filter box radius"),
1, MAX_RADIUS, 3,
G_PARAM_READWRITE);
PIKA_PROC_ARG_INT (procedure, "type",
_("_Filter Type"),
_("Filter type { MEDIAN (0), ADAPTIVE (1), "
"RECURSIVE-MEDIAN (2), RECURSIVE-ADAPTIVE (3) }"),
0, 3, FILTER_ADAPTIVE,
G_PARAM_READWRITE);
PIKA_PROC_ARG_INT (procedure, "black",
_("_Black level"),
_("Black level"),
-1, 255, 7,
G_PARAM_READWRITE);
PIKA_PROC_ARG_INT (procedure, "white",
_("_White level"),
_("White level"),
0, 256, 248,
G_PARAM_READWRITE);
}
return procedure;
}
static PikaValueArray *
despeckle_run (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
const PikaValueArray *args,
gpointer run_data)
{
PikaProcedureConfig *config;
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 (! pika_drawable_is_rgb (drawable) &&
! pika_drawable_is_gray (drawable))
{
return pika_procedure_new_return_values (procedure,
PIKA_PDB_EXECUTION_ERROR,
NULL);
}
config = pika_procedure_create_config (procedure);
pika_procedure_config_begin_run (config, NULL, run_mode, args);
if (run_mode == PIKA_RUN_INTERACTIVE)
{
if (! despeckle_dialog (procedure, G_OBJECT (config), drawable))
{
pika_procedure_config_end_run (config, PIKA_PDB_CANCEL);
g_object_unref (config);
return pika_procedure_new_return_values (procedure,
PIKA_PDB_CANCEL,
NULL);
}
}
despeckle (drawable, G_OBJECT (config));
pika_procedure_config_end_run (config, PIKA_PDB_SUCCESS);
g_object_unref (config);
return pika_procedure_new_return_values (procedure, PIKA_PDB_SUCCESS, NULL);
}
static inline guchar
pixel_luminance (const guchar *p,
gint bpp)
{
switch (bpp)
{
case 1:
case 2:
return p[0];
case 3:
case 4:
return PIKA_RGB_LUMINANCE (p[0], p[1], p[2]);
default:
return 0; /* should not be reached */
}
}
static inline void
pixel_copy (guchar *dest,
const guchar *src,
gint bpp)
{
switch (bpp)
{
case 4:
*dest++ = *src++;
case 3:
*dest++ = *src++;
case 2:
*dest++ = *src++;
case 1:
*dest++ = *src++;
}
}
/*
* 'despeckle()' - Despeckle an image using a median filter.
*
* A median filter basically collects pixel values in a region around the
* target pixel, sorts them, and uses the median value. This code uses a
* circular row buffer to improve performance.
*
* The adaptive filter is based on the median filter but analyzes the histogram
* of the region around the target pixel and adjusts the despeckle diameter
* accordingly.
*/
static const Babl *
get_u8_format (PikaDrawable *drawable)
{
if (pika_drawable_is_rgb (drawable))
{
if (pika_drawable_has_alpha (drawable))
return babl_format ("R'G'B'A u8");
else
return babl_format ("R'G'B' u8");
}
else
{
if (pika_drawable_has_alpha (drawable))
return babl_format ("Y'A u8");
else
return babl_format ("Y' u8");
}
}
static void
despeckle (PikaDrawable *drawable,
GObject *config)
{
GeglBuffer *src_buffer;
GeglBuffer *dest_buffer;
const Babl *format;
guchar *src;
guchar *dst;
gint img_bpp;
gint x, y;
gint width, height;
if (! pika_drawable_mask_intersect (drawable,
&x, &y, &width, &height))
return;
format = get_u8_format (drawable);
img_bpp = babl_format_get_bytes_per_pixel (format);
src_buffer = pika_drawable_get_buffer (drawable);
dest_buffer = pika_drawable_get_shadow_buffer (drawable);
src = g_new (guchar, width * height * img_bpp);
dst = g_new (guchar, width * height * img_bpp);
gegl_buffer_get (src_buffer, GEGL_RECTANGLE (x, y, width, height), 1.0,
format, src,
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
despeckle_median (config,
src, dst, width, height, img_bpp, FALSE);
gegl_buffer_set (dest_buffer, GEGL_RECTANGLE (x, y, width, height), 0,
format, dst,
GEGL_AUTO_ROWSTRIDE);
g_object_unref (src_buffer);
g_object_unref (dest_buffer);
pika_drawable_merge_shadow (drawable, TRUE);
pika_drawable_update (drawable, x, y, width, height);
g_free (dst);
g_free (src);
}
static gboolean
despeckle_dialog (PikaProcedure *procedure,
GObject *config,
PikaDrawable *drawable)
{
GtkWidget *dialog;
GtkWidget *preview;
GtkWidget *vbox;
GtkWidget *scale;
GtkWidget *combo;
GtkListStore *store;
gboolean run;
pika_ui_init (PLUG_IN_BINARY);
dialog = pika_procedure_dialog_new (procedure,
PIKA_PROCEDURE_CONFIG (config),
_("Despeckle"));
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_set_margin_bottom (preview, 12);
gtk_widget_show (preview);
store = pika_int_store_new (_("Median"), 0,
_("Adaptive"), FILTER_ADAPTIVE,
_("Recursive-Median"), FILTER_RECURSIVE,
_("Recursive-Adaptive"), 3,
NULL);
combo = pika_procedure_dialog_get_int_combo (PIKA_PROCEDURE_DIALOG (dialog),
"type",
PIKA_INT_STORE (store));
gtk_widget_set_margin_bottom (combo, 12);
/*
* Box size (diameter) control...
*/
scale = pika_procedure_dialog_get_scale_entry (PIKA_PROCEDURE_DIALOG (dialog),
"radius", 1.0);
gtk_widget_set_margin_bottom (scale, 12);
/*
* Black level control...
*/
scale = pika_procedure_dialog_get_scale_entry (PIKA_PROCEDURE_DIALOG (dialog),
"black", 1.0);
gtk_widget_set_margin_bottom (scale, 12);
/*
* White level control...
*/
pika_procedure_dialog_get_scale_entry (PIKA_PROCEDURE_DIALOG (dialog),
"white", 1.0);
vbox = pika_procedure_dialog_fill_box (PIKA_PROCEDURE_DIALOG (dialog),
"despeckle-vbox",
"type", "radius", "black", "white",
NULL);
gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
g_object_set_data (config, "drawable", drawable);
g_signal_connect (preview, "invalidated",
G_CALLBACK (preview_update),
config);
g_signal_connect_swapped (config, "notify",
G_CALLBACK (pika_preview_invalidate),
preview);
pika_procedure_dialog_fill (PIKA_PROCEDURE_DIALOG (dialog),
"despeckle-vbox",
NULL);
gtk_widget_show (dialog);
run = pika_procedure_dialog_run (PIKA_PROCEDURE_DIALOG (dialog));
gtk_widget_destroy (dialog);
return run;
}
static void
preview_update (GtkWidget *widget,
GObject *config)
{
PikaPreview *preview = PIKA_PREVIEW (widget);
PikaDrawable *drawable = g_object_get_data (config, "drawable");
GeglBuffer *src_buffer;
const Babl *format;
guchar *dst;
guchar *src;
gint img_bpp;
gint x1,y1;
gint width, height;
format = get_u8_format (drawable);
img_bpp = babl_format_get_bytes_per_pixel (format);
pika_preview_get_size (preview, &width, &height);
pika_preview_get_position (preview, &x1, &y1);
src_buffer = pika_drawable_get_buffer (drawable);
dst = g_new (guchar, width * height * img_bpp);
src = g_new (guchar, width * height * img_bpp);
gegl_buffer_get (src_buffer, GEGL_RECTANGLE (x1, y1, width, height), 1.0,
format, src,
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
despeckle_median (config,
src, dst, width, height, img_bpp, TRUE);
pika_preview_draw_buffer (preview, dst, width * img_bpp);
g_object_unref (src_buffer);
g_free (src);
g_free (dst);
}
static inline void
list_add_elem (PixelsList *list,
const guchar *elem)
{
const gint pos = list->start + list->count++;
list->elems[pos >= MAX_LIST_ELEMS ? pos - MAX_LIST_ELEMS : pos] = elem;
}
static inline void
list_del_elem (PixelsList* list)
{
list->count--;
list->start++;
if (list->start >= MAX_LIST_ELEMS)
list->start = 0;
}
static inline const guchar *
list_get_random_elem (PixelsList *list)
{
const gint pos = list->start + rand () % list->count;
if (pos >= MAX_LIST_ELEMS)
return list->elems[pos - MAX_LIST_ELEMS];
return list->elems[pos];
}
static inline void
histogram_add (DespeckleHistogram *hist,
guchar val,
const guchar *orig)
{
hist->elems[val]++;
list_add_elem (&hist->origs[val], orig);
}
static inline void
histogram_remove (DespeckleHistogram *hist,
guchar val)
{
hist->elems[val]--;
list_del_elem (&hist->origs[val]);
}
static inline void
histogram_clean (DespeckleHistogram *hist)
{
gint i;
for (i = 0; i < 256; i++)
{
hist->elems[i] = 0;
hist->origs[i].count = 0;
}
}
static inline const guchar *
histogram_get_median (DespeckleHistogram *hist,
const guchar *_default)
{
gint count = histrest;
gint i;
gint sum = 0;
if (! count)
return _default;
count = (count + 1) / 2;
i = 0;
while ((sum += hist->elems[i]) < count)
i++;
return list_get_random_elem (&hist->origs[i]);
}
static inline void
add_val (DespeckleHistogram *hist,
gint black_level,
gint white_level,
const guchar *src,
gint width,
gint bpp,
gint x,
gint y)
{
const gint pos = (x + (y * width)) * bpp;
const gint value = pixel_luminance (src + pos, bpp);
if (value > black_level && value < white_level)
{
histogram_add (hist, value, src + pos);
histrest++;
}
else
{
if (value <= black_level)
hist0++;
if (value >= white_level)
hist255++;
}
}
static inline void
del_val (DespeckleHistogram *hist,
gint black_level,
gint white_level,
const guchar *src,
gint width,
gint bpp,
gint x,
gint y)
{
const gint pos = (x + (y * width)) * bpp;
const gint value = pixel_luminance (src + pos, bpp);
if (value > black_level && value < white_level)
{
histogram_remove (hist, value);
histrest--;
}
else
{
if (value <= black_level)
hist0--;
if (value >= white_level)
hist255--;
}
}
static inline void
add_vals (DespeckleHistogram *hist,
gint black_level,
gint white_level,
const guchar *src,
gint width,
gint bpp,
gint xmin,
gint ymin,
gint xmax,
gint ymax)
{
gint x;
gint y;
if (xmin > xmax)
return;
for (y = ymin; y <= ymax; y++)
{
for (x = xmin; x <= xmax; x++)
{
add_val (hist,
black_level, white_level,
src, width, bpp, x, y);
}
}
}
static inline void
del_vals (DespeckleHistogram *hist,
gint black_level,
gint white_level,
const guchar *src,
gint width,
gint bpp,
gint xmin,
gint ymin,
gint xmax,
gint ymax)
{
gint x;
gint y;
if (xmin > xmax)
return;
for (y = ymin; y <= ymax; y++)
{
for (x = xmin; x <= xmax; x++)
{
del_val (hist,
black_level, white_level,
src, width, bpp, x, y);
}
}
}
static inline void
update_histogram (DespeckleHistogram *hist,
gint black_level,
gint white_level,
const guchar *src,
gint width,
gint bpp,
gint xmin,
gint ymin,
gint xmax,
gint ymax)
{
/* assuming that radious of the box can change no more than one
pixel in each call */
/* assuming that box is moving either right or down */
del_vals (hist,
black_level, white_level,
src, width, bpp, hist->xmin, hist->ymin, xmin - 1, hist->ymax);
del_vals (hist,
black_level, white_level,
src, width, bpp, xmin, hist->ymin, xmax, ymin - 1);
del_vals (hist,
black_level, white_level,
src, width, bpp, xmin, ymax + 1, xmax, hist->ymax);
add_vals (hist,
black_level, white_level,
src, width, bpp, hist->xmax + 1, ymin, xmax, ymax);
add_vals (hist,
black_level, white_level,
src, width, bpp, xmin, ymin, hist->xmax, hist->ymin - 1);
add_vals (hist,
black_level, white_level,
src, width, bpp, hist->xmin, hist->ymax + 1, hist->xmax, ymax);
hist->xmin = xmin;
hist->ymin = ymin;
hist->xmax = xmax;
hist->ymax = ymax;
}
static void
despeckle_median (GObject *config,
guchar *src,
guchar *dst,
gint width,
gint height,
gint bpp,
gboolean preview)
{
gint radius;
gint filter_type;
gint black_level;
gint white_level;
guint progress;
guint max_progress;
gint x, y;
gint adapt_radius;
gint pos;
gint ymin;
gint ymax;
gint xmin;
gint xmax;
g_object_get (config,
"radius", &radius,
"type", &filter_type,
"black", &black_level,
"white", &white_level,
NULL);
memset (&histogram, 0, sizeof(histogram));
progress = 0;
max_progress = width * height;
if (! preview)
pika_progress_init (_("Despeckle"));
adapt_radius = radius;
for (y = 0; y < height; y++)
{
x = 0;
ymin = MAX (0, y - adapt_radius);
ymax = MIN (height - 1, y + adapt_radius);
xmin = MAX (0, x - adapt_radius);
xmax = MIN (width - 1, x + adapt_radius);
hist0 = 0;
histrest = 0;
hist255 = 0;
histogram_clean (&histogram);
histogram.xmin = xmin;
histogram.ymin = ymin;
histogram.xmax = xmax;
histogram.ymax = ymax;
add_vals (&histogram,
black_level, white_level,
src, width, bpp,
histogram.xmin, histogram.ymin,
histogram.xmax, histogram.ymax);
for (x = 0; x < width; x++)
{
const guchar *pixel;
ymin = MAX (0, y - adapt_radius); /* update ymin, ymax when adapt_radius changed (FILTER_ADAPTIVE) */
ymax = MIN (height - 1, y + adapt_radius);
xmin = MAX (0, x - adapt_radius);
xmax = MIN (width - 1, x + adapt_radius);
update_histogram (&histogram,
black_level, white_level,
src, width, bpp, xmin, ymin, xmax, ymax);
pos = (x + (y * width)) * bpp;
pixel = histogram_get_median (&histogram, src + pos);
if (filter_type & FILTER_RECURSIVE)
{
del_val (&histogram,
black_level, white_level,
src, width, bpp, x, y);
pixel_copy (src + pos, pixel, bpp);
add_val (&histogram,
black_level, white_level,
src, width, bpp, x, y);
}
pixel_copy (dst + pos, pixel, bpp);
/*
* Check the histogram and adjust the diameter accordingly...
*/
if (filter_type & FILTER_ADAPTIVE)
{
if (hist0 >= adapt_radius || hist255 >= adapt_radius)
{
if (adapt_radius < radius)
adapt_radius++;
}
else if (adapt_radius > 1)
{
adapt_radius--;
}
}
}
progress += width;
if (! preview && y % 32 == 0)
pika_progress_update ((gdouble) progress / (gdouble) max_progress);
}
if (! preview)
pika_progress_update (1.0);
}

540
plug-ins/common/destripe.c Normal file
View File

@ -0,0 +1,540 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* Destripe filter
*
* Copyright 1997 Marc Lehmann, heavily modified from a filter by
* Michael Sweet.
*
* 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/>.
*
*/
#include "config.h"
#include <string.h>
#include <libpika/pika.h>
#include <libpika/pikaui.h>
#include "libpika/stdplugins-intl.h"
#define PLUG_IN_PROC "plug-in-destripe"
#define PLUG_IN_BINARY "destripe"
#define PLUG_IN_ROLE "pika-destripe"
#define PLUG_IN_VERSION "0.2"
#define MAX_AVG 100
typedef struct _Destripe Destripe;
typedef struct _DestripeClass DestripeClass;
struct _Destripe
{
PikaPlugIn parent_instance;
};
struct _DestripeClass
{
PikaPlugInClass parent_class;
};
#define DESTRIPE_TYPE (destripe_get_type ())
#define DESTRIPE (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), DESTRIPE_TYPE, Destripe))
GType destripe_get_type (void) G_GNUC_CONST;
static GList * destripe_query_procedures (PikaPlugIn *plug_in);
static PikaProcedure * destripe_create_procedure (PikaPlugIn *plug_in,
const gchar *name);
static PikaValueArray * destripe_run (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
const PikaValueArray *args,
gpointer run_data);
static void destripe (GObject *config,
PikaDrawable *drawable,
PikaPreview *preview);
static void destripe_preview (GtkWidget *widget,
GObject *config);
static gboolean destripe_dialog (PikaProcedure *procedure,
GObject *config,
PikaDrawable *drawable);
G_DEFINE_TYPE (Destripe, destripe, PIKA_TYPE_PLUG_IN)
PIKA_MAIN (DESTRIPE_TYPE)
DEFINE_STD_SET_I18N
static void
destripe_class_init (DestripeClass *klass)
{
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
plug_in_class->query_procedures = destripe_query_procedures;
plug_in_class->create_procedure = destripe_create_procedure;
plug_in_class->set_i18n = STD_SET_I18N;
}
static void
destripe_init (Destripe *destripe)
{
}
static GList *
destripe_query_procedures (PikaPlugIn *plug_in)
{
return g_list_append (NULL, g_strdup (PLUG_IN_PROC));
}
static PikaProcedure *
destripe_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,
destripe_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, _("Des_tripe..."));
pika_procedure_add_menu_path (procedure, "<Image>/Colors/Tone Mapping");
pika_procedure_set_documentation (procedure,
_("Remove vertical stripe artifacts "
"from the image"),
_("This plug-in tries to remove vertical "
"stripes from an image."),
name);
pika_procedure_set_attribution (procedure,
"Marc Lehmann <pcg@goof.com>",
"Marc Lehmann <pcg@goof.com>",
PLUG_IN_VERSION);
PIKA_PROC_ARG_INT (procedure, "avg-width",
_("_Width"),
_("Averaging filter width"),
2, MAX_AVG, 36,
G_PARAM_READWRITE);
PIKA_PROC_ARG_BOOLEAN (procedure,
"create-histogram",
_("Create _histogram"),
_("Output a histogram"),
FALSE,
G_PARAM_READWRITE);
}
return procedure;
}
static PikaValueArray *
destripe_run (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
const PikaValueArray *args,
gpointer run_data)
{
PikaProcedureConfig *config;
PikaDrawable *drawable;
gegl_init (NULL, NULL);
config = pika_procedure_create_config (procedure);
pika_procedure_config_begin_run (config, NULL, run_mode, args);
if (n_drawables != 1)
{
GError *error = NULL;
pika_procedure_config_end_run (config, PIKA_PDB_EXECUTION_ERROR);
g_object_unref (config);
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];
}
switch (run_mode)
{
case PIKA_RUN_INTERACTIVE:
if (! destripe_dialog (procedure, G_OBJECT (config), drawable))
{
pika_procedure_config_end_run (config, PIKA_PDB_CANCEL);
g_object_unref (config);
return pika_procedure_new_return_values (procedure,
PIKA_PDB_CANCEL,
NULL);
}
break;
default:
break;
};
if (pika_drawable_is_rgb (drawable) ||
pika_drawable_is_gray (drawable))
{
destripe (G_OBJECT (config), drawable, NULL);
if (run_mode != PIKA_RUN_NONINTERACTIVE)
pika_displays_flush ();
}
else
{
pika_procedure_config_end_run (config, PIKA_PDB_EXECUTION_ERROR);
g_object_unref (config);
return pika_procedure_new_return_values (procedure,
PIKA_PDB_EXECUTION_ERROR,
NULL);
}
pika_procedure_config_end_run (config, PIKA_PDB_SUCCESS);
g_object_unref (config);
return pika_procedure_new_return_values (procedure, PIKA_PDB_SUCCESS, NULL);
}
static void
destripe (GObject *config,
PikaDrawable *drawable,
PikaPreview *preview)
{
GeglBuffer *src_buffer;
GeglBuffer *dest_buffer;
const Babl *format;
guchar *src_rows; /* image data */
gdouble progress, progress_inc;
gint x1, x2, y1;
gint width, height;
gint bpp;
glong *hist, *corr; /* "histogram" data */
gint tile_width = pika_tile_width ();
gint i, x, y, ox, cols;
gint avg_width;
gboolean histogram;
g_object_get (config,
"avg-width", &avg_width,
"create-histogram", &histogram,
NULL);
progress = 0.0;
progress_inc = 0.0;
if (preview)
{
pika_preview_get_position (preview, &x1, &y1);
pika_preview_get_size (preview, &width, &height);
}
else
{
pika_progress_init (_("Destriping"));
if (! pika_drawable_mask_intersect (drawable,
&x1, &y1, &width, &height))
{
return;
}
progress = 0;
progress_inc = 0.5 * tile_width / width;
}
x2 = x1 + width;
if (pika_drawable_is_rgb (drawable))
{
if (pika_drawable_has_alpha (drawable))
format = babl_format ("R'G'B'A u8");
else
format = babl_format ("R'G'B' u8");
}
else
{
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);
/*
* Setup for filter...
*/
src_buffer = pika_drawable_get_buffer (drawable);
dest_buffer = pika_drawable_get_shadow_buffer (drawable);
hist = g_new (long, width * bpp);
corr = g_new (long, width * bpp);
src_rows = g_new (guchar, tile_width * height * bpp);
memset (hist, 0, width * bpp * sizeof (long));
/*
* collect "histogram" data.
*/
for (ox = x1; ox < x2; ox += tile_width)
{
guchar *rows = src_rows;
cols = x2 - ox;
if (cols > tile_width)
cols = tile_width;
gegl_buffer_get (src_buffer, GEGL_RECTANGLE (ox, y1, cols, height), 1.0,
format, rows,
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
for (y = 0; y < height; y++)
{
long *h = hist + (ox - x1) * bpp;
guchar *row_end = rows + cols * bpp;
while (rows < row_end)
*h++ += *rows++;
}
if (! preview)
pika_progress_update (progress += progress_inc);
}
/*
* average out histogram
*/
{
gint extend = (avg_width / 2) * bpp;
for (i = 0; i < MIN (3, bpp); i++)
{
long *h = hist - extend + i;
long *c = corr - extend + i;
long sum = 0;
gint cnt = 0;
for (x = -extend; x < width * bpp; x += bpp)
{
if (x + extend < width * bpp)
{
sum += h[ extend]; cnt++;
}
if (x - extend >= 0)
{
sum -= h[-extend]; cnt--;
}
if (x >= 0)
{
if (*h)
*c = ((sum / cnt - *h) << 10) / *h;
else
*c = G_MAXINT;
}
h += bpp;
c += bpp;
}
}
}
/*
* remove stripes.
*/
for (ox = x1; ox < x2; ox += tile_width)
{
guchar *rows = src_rows;
cols = x2 - ox;
if (cols > tile_width)
cols = tile_width;
gegl_buffer_get (src_buffer, GEGL_RECTANGLE (ox, y1, cols, height), 1.0,
format, rows,
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
if (! preview)
pika_progress_update (progress += progress_inc);
for (y = 0; y < height; y++)
{
long *c = corr + (ox - x1) * bpp;
guchar *row_end = rows + cols * bpp;
if (histogram)
{
while (rows < row_end)
{
*rows = MIN (255, MAX (0, 128 + (*rows * *c >> 10)));
c++; rows++;
}
}
else
{
while (rows < row_end)
{
*rows = MIN (255, MAX (0, *rows + (*rows * *c >> 10) ));
c++; rows++;
}
}
}
gegl_buffer_set (dest_buffer, GEGL_RECTANGLE (ox, y1, cols, height), 0,
format, src_rows,
GEGL_AUTO_ROWSTRIDE);
if (! preview)
pika_progress_update (progress += progress_inc);
}
g_free (src_rows);
g_object_unref (src_buffer);
if (preview)
{
guchar *buffer = g_new (guchar, width * height * bpp);
gegl_buffer_get (dest_buffer, GEGL_RECTANGLE (x1, y1, width, height), 1.0,
format, buffer,
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
pika_preview_draw_buffer (PIKA_PREVIEW (preview),
buffer, width * bpp);
g_free (buffer);
g_object_unref (dest_buffer);
}
else
{
g_object_unref (dest_buffer);
pika_progress_update (1.0);
pika_drawable_merge_shadow (drawable, TRUE);
pika_drawable_update (drawable,
x1, y1, width, height);
}
g_free (hist);
g_free (corr);
}
static void
destripe_preview (GtkWidget *widget,
GObject *config)
{
PikaPreview *preview = PIKA_PREVIEW (widget);
PikaDrawable *drawable = g_object_get_data (config, "drawable");
destripe (config, drawable, preview);
}
static gboolean
destripe_dialog (PikaProcedure *procedure,
GObject *config,
PikaDrawable *drawable)
{
GtkWidget *dialog;
GtkWidget *preview;
GtkWidget *scale;
GtkWidget *button;
gboolean run;
pika_ui_init (PLUG_IN_BINARY);
dialog = pika_procedure_dialog_new (procedure,
PIKA_PROCEDURE_CONFIG (config),
_("Destripe"));
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);
scale = pika_procedure_dialog_get_scale_entry (PIKA_PROCEDURE_DIALOG (dialog),
"avg-width", 1.0);
gtk_widget_set_margin_bottom (scale, 12);
button = pika_procedure_dialog_get_widget (PIKA_PROCEDURE_DIALOG (dialog),
"create-histogram",
GTK_TYPE_CHECK_BUTTON);
gtk_widget_set_margin_bottom (button, 12);
g_object_set_data (config, "drawable", drawable);
g_signal_connect (preview, "invalidated",
G_CALLBACK (destripe_preview),
config);
g_signal_connect_swapped (config, "notify",
G_CALLBACK (pika_preview_invalidate),
preview);
pika_procedure_dialog_fill (PIKA_PROCEDURE_DIALOG (dialog),
"avg-width", "create-histogram",
NULL);
gtk_widget_show (dialog);
run = pika_procedure_dialog_run (PIKA_PROCEDURE_DIALOG (dialog));
gtk_widget_destroy (dialog);
return run;
}

414
plug-ins/common/file-aa.c Normal file
View File

@ -0,0 +1,414 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* 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/>.
*/
/**
* aa.c version 1.0
* A plugin that uses libaa (ftp://ftp.ta.jcu.cz/pub/aa) to save images as
* ASCII.
* NOTE: This plugin *requires* aalib 1.2 or later. Earlier versions will
* not work.
* Code copied from all over the PIKA source.
* Tim Newsome <nuisance@cmu.edu>
*/
#include "config.h"
#include <string.h>
#include <aalib.h>
#include <libpika/pika.h>
#include <libpika/pikaui.h>
#include "libpika/stdplugins-intl.h"
#define SAVE_PROC "file-aa-save"
#define PLUG_IN_BINARY "file-aa"
typedef struct _Ascii Ascii;
typedef struct _AsciiClass AsciiClass;
struct _Ascii
{
PikaPlugIn parent_instance;
};
struct _AsciiClass
{
PikaPlugInClass parent_class;
};
#define ASCII_TYPE (ascii_get_type ())
#define ASCII (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ASCII_TYPE, Ascii))
GType ascii_get_type (void) G_GNUC_CONST;
static GList * ascii_query_procedures (PikaPlugIn *plug_in);
static PikaProcedure * ascii_create_procedure (PikaPlugIn *plug_in,
const gchar *name);
static PikaValueArray * ascii_save (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
GFile *file,
const PikaValueArray *args,
gpointer run_data);
static gboolean save_aa (GFile *file,
PikaDrawable *drawable,
GObject *config,
GError **error);
static void pika2aa (PikaDrawable *drawable,
aa_context *context);
static gboolean save_dialog (PikaProcedure *procedure,
GObject *config,
PikaImage *image);
G_DEFINE_TYPE (Ascii, ascii, PIKA_TYPE_PLUG_IN)
PIKA_MAIN (ASCII_TYPE)
DEFINE_STD_SET_I18N
static void
ascii_class_init (AsciiClass *klass)
{
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
plug_in_class->query_procedures = ascii_query_procedures;
plug_in_class->create_procedure = ascii_create_procedure;
plug_in_class->set_i18n = STD_SET_I18N;
}
static void
ascii_init (Ascii *ascii)
{
}
static GList *
ascii_query_procedures (PikaPlugIn *plug_in)
{
return g_list_append (NULL, g_strdup (SAVE_PROC));
}
static PikaProcedure *
ascii_create_procedure (PikaPlugIn *plug_in,
const gchar *name)
{
PikaProcedure *procedure = NULL;
if (! strcmp (name, SAVE_PROC))
{
gint i;
procedure = pika_save_procedure_new (plug_in, name,
PIKA_PDB_PROC_TYPE_PLUGIN,
ascii_save, NULL, NULL);
pika_procedure_set_image_types (procedure, "*");
pika_procedure_set_menu_label (procedure, _("ASCII art"));
pika_file_procedure_set_format_name (PIKA_FILE_PROCEDURE (procedure),
_("ASCII art"));
pika_procedure_set_documentation (procedure,
_("Saves grayscale image in various "
"text formats"),
_("This plug-in uses aalib to save "
"grayscale image as ascii art into "
"a variety of text formats"),
name);
pika_procedure_set_attribution (procedure,
"Tim Newsome <nuisance@cmu.edu>",
"Tim Newsome <nuisance@cmu.edu>",
"1997");
pika_file_procedure_set_mime_types (PIKA_FILE_PROCEDURE (procedure),
"text/plain");
pika_file_procedure_set_extensions (PIKA_FILE_PROCEDURE (procedure),
"txt,ansi,text");
for (i = 0; aa_formats[i]; i++);
PIKA_PROC_ARG_INT (procedure, "file-type",
_("_Format"),
_("File type to use"),
0, i, 0,
G_PARAM_READWRITE);
}
return procedure;
}
static PikaValueArray *
ascii_save (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
GFile *file,
const PikaValueArray *args,
gpointer run_data)
{
PikaProcedureConfig *config;
PikaPDBStatusType status = PIKA_PDB_SUCCESS;
PikaExportReturn export = PIKA_EXPORT_CANCEL;
GError *error = NULL;
gegl_init (NULL, NULL);
config = pika_procedure_create_config (procedure);
pika_procedure_config_begin_run (config, image, run_mode, args);
switch (run_mode)
{
case PIKA_RUN_INTERACTIVE:
case PIKA_RUN_WITH_LAST_VALS:
pika_ui_init (PLUG_IN_BINARY);
export = pika_export_image (&image, &n_drawables, &drawables, "AA",
PIKA_EXPORT_CAN_HANDLE_RGB |
PIKA_EXPORT_CAN_HANDLE_GRAY |
PIKA_EXPORT_CAN_HANDLE_INDEXED |
PIKA_EXPORT_CAN_HANDLE_ALPHA);
if (export == PIKA_EXPORT_CANCEL)
return pika_procedure_new_return_values (procedure,
PIKA_PDB_CANCEL,
NULL);
break;
default:
break;
}
if (n_drawables != 1)
{
g_set_error (&error, G_FILE_ERROR, 0,
_("ASCII art does not support multiple layers."));
return pika_procedure_new_return_values (procedure,
PIKA_PDB_CALLING_ERROR,
error);
}
if (run_mode == PIKA_RUN_INTERACTIVE)
{
if (! save_dialog (procedure, G_OBJECT (config), image))
status = PIKA_PDB_CANCEL;
}
if (status == PIKA_PDB_SUCCESS)
{
if (! save_aa (file, drawables[0], G_OBJECT (config), &error))
{
status = PIKA_PDB_EXECUTION_ERROR;
}
}
pika_procedure_config_end_run (config, status);
g_object_unref (config);
if (export == PIKA_EXPORT_EXPORT)
{
pika_image_delete (image);
g_free (drawables);
}
return pika_procedure_new_return_values (procedure, status, error);
}
static gboolean
save_aa (GFile *file,
PikaDrawable *drawable,
GObject *config,
GError **error)
{
aa_savedata savedata;
aa_context *context;
aa_format format;
gint output_type;
g_object_get (config,
"file-type", &output_type,
NULL);
memcpy (&format, aa_formats[output_type], sizeof (aa_format));
format.width = pika_drawable_get_width (drawable) / 2;
format.height = pika_drawable_get_height (drawable) / 2;
/* Get a libaa context which will save its output to filename. */
savedata.name = g_file_get_path (file);
savedata.format = &format;
context = aa_init (&save_d, &aa_defparams, &savedata);
if (! context)
return FALSE;
pika2aa (drawable, context);
aa_flush (context);
aa_close (context);
return TRUE;
}
static void
pika2aa (PikaDrawable *drawable,
aa_context *context)
{
GeglBuffer *buffer;
const Babl *format;
aa_renderparams *renderparams;
gint width;
gint height;
gint x, y;
gint bpp;
guchar *buf;
guchar *p;
buffer = pika_drawable_get_buffer (drawable);
width = aa_imgwidth (context);
height = aa_imgheight (context);
switch (pika_drawable_type (drawable))
{
case PIKA_GRAY_IMAGE:
format = babl_format ("Y' u8");
break;
case PIKA_GRAYA_IMAGE:
format = babl_format ("Y'A u8");
break;
case PIKA_RGB_IMAGE:
case PIKA_INDEXED_IMAGE:
format = babl_format ("R'G'B' u8");
break;
case PIKA_RGBA_IMAGE:
case PIKA_INDEXEDA_IMAGE:
format = babl_format ("R'G'B'A u8");
break;
default:
g_return_if_reached ();
break;
}
bpp = babl_format_get_bytes_per_pixel (format);
buf = g_new (guchar, width * bpp);
for (y = 0; y < height; y++)
{
gegl_buffer_get (buffer, GEGL_RECTANGLE (0, y, width, 1), 1.0,
format, buf,
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
switch (bpp)
{
case 1: /* GRAY */
for (x = 0, p = buf; x < width; x++, p++)
aa_putpixel (context, x, y, *p);
break;
case 2: /* GRAYA, blend over black */
for (x = 0, p = buf; x < width; x++, p += 2)
aa_putpixel (context, x, y, (p[0] * (p[1] + 1)) >> 8);
break;
case 3: /* RGB */
for (x = 0, p = buf; x < width; x++, p += 3)
aa_putpixel (context, x, y,
PIKA_RGB_LUMINANCE (p[0], p[1], p[2]) + 0.5);
break;
case 4: /* RGBA, blend over black */
for (x = 0, p = buf; x < width; x++, p += 4)
aa_putpixel (context, x, y,
((guchar) (PIKA_RGB_LUMINANCE (p[0], p[1], p[2]) + 0.5)
* (p[3] + 1)) >> 8);
break;
default:
g_assert_not_reached ();
break;
}
}
g_free (buf);
g_object_unref (buffer);
renderparams = aa_getrenderparams ();
renderparams->dither = AA_FLOYD_S;
aa_render (context, renderparams, 0, 0,
aa_scrwidth (context), aa_scrheight (context));
}
static gboolean
save_dialog (PikaProcedure *procedure,
GObject *config,
PikaImage *image)
{
GtkWidget *dialog;
GtkListStore *store;
GtkWidget *combo;
gint i;
gboolean run;
dialog = pika_save_procedure_dialog_new (PIKA_SAVE_PROCEDURE (procedure),
PIKA_PROCEDURE_CONFIG (config),
image);
store = g_object_new (PIKA_TYPE_INT_STORE, NULL);
for (i = 0; aa_formats[i]; i++)
gtk_list_store_insert_with_values (store, NULL, -1,
PIKA_INT_STORE_VALUE, i,
PIKA_INT_STORE_LABEL, aa_formats[i]->formatname,
-1);
combo = pika_procedure_dialog_get_int_combo (PIKA_PROCEDURE_DIALOG (dialog),
"file-type",
PIKA_INT_STORE (store));
g_object_set (combo, "margin", 12, NULL);
pika_procedure_dialog_fill (PIKA_PROCEDURE_DIALOG (dialog), NULL);
gtk_widget_show (dialog);
run = pika_procedure_dialog_run (PIKA_PROCEDURE_DIALOG (dialog));
gtk_widget_destroy (dialog);
return run;
}

1029
plug-ins/common/file-cel.c Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,997 @@
/* CSource - PIKA Plugin to dump image data in RGB(A) format for C source
* Copyright (C) 1999 Tim Janik
*
* 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/>.
*
* This plugin is heavily based on the header plugin by Spencer Kimball and
* Peter Mattis.
*/
#include "config.h"
#include <string.h>
#include <errno.h>
#include <glib/gstdio.h>
#include <libpika/pika.h>
#include <libpika/pikaui.h>
#include "libpika/stdplugins-intl.h"
#define SAVE_PROC "file-csource-save"
#define PLUG_IN_BINARY "file-csource"
#define PLUG_IN_ROLE "pika-file-csource"
typedef struct _Csource Csource;
typedef struct _CsourceClass CsourceClass;
struct _Csource
{
PikaPlugIn parent_instance;
};
struct _CsourceClass
{
PikaPlugInClass parent_class;
};
#define CSOURCE_TYPE (csource_get_type ())
#define CSOURCE (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CSOURCE_TYPE, Csource))
GType csource_get_type (void) G_GNUC_CONST;
static GList * csource_query_procedures (PikaPlugIn *plug_in);
static PikaProcedure * csource_create_procedure (PikaPlugIn *plug_in,
const gchar *name);
static PikaValueArray * csource_save (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
GFile *file,
const PikaValueArray *args,
gpointer run_data);
static gboolean save_image (GFile *file,
PikaImage *image,
PikaDrawable *drawable,
GObject *config,
GError **error);
static gboolean save_dialog (PikaImage *image,
PikaProcedure *procedure,
GObject *config);
G_DEFINE_TYPE (Csource, csource, PIKA_TYPE_PLUG_IN)
PIKA_MAIN (CSOURCE_TYPE)
DEFINE_STD_SET_I18N
static void
csource_class_init (CsourceClass *klass)
{
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
plug_in_class->query_procedures = csource_query_procedures;
plug_in_class->create_procedure = csource_create_procedure;
plug_in_class->set_i18n = STD_SET_I18N;
}
static void
csource_init (Csource *csource)
{
}
static GList *
csource_query_procedures (PikaPlugIn *plug_in)
{
return g_list_append (NULL, g_strdup (SAVE_PROC));
}
static PikaProcedure *
csource_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,
csource_save, NULL, NULL);
pika_procedure_set_image_types (procedure, "*");
pika_procedure_set_menu_label (procedure, _("C source code"));
pika_procedure_set_documentation (procedure,
_("Dump image data in RGB(A) format "
"for C source"),
_("CSource cannot be run non-interactively."),
name);
pika_procedure_set_attribution (procedure,
"Tim Janik",
"Tim Janik",
"1999");
pika_file_procedure_set_format_name (PIKA_FILE_PROCEDURE (procedure),
_("C-Source"));
pika_file_procedure_set_handles_remote (PIKA_FILE_PROCEDURE (procedure),
TRUE);
pika_file_procedure_set_mime_types (PIKA_FILE_PROCEDURE (procedure),
"image/x-csrc");
pika_file_procedure_set_extensions (PIKA_FILE_PROCEDURE (procedure),
"c");
PIKA_PROC_AUX_ARG_STRING (procedure, "prefixed-name",
_("_Prefixed name"),
_("Prefixed name"),
"pika_image",
PIKA_PARAM_READWRITE);
PIKA_PROC_AUX_ARG_STRING (procedure, "pika-comment",
_("Comme_nt"),
_("Comment"),
pika_get_default_comment (),
PIKA_PARAM_READWRITE);
pika_procedure_set_argument_sync (procedure, "pika-comment",
PIKA_ARGUMENT_SYNC_PARASITE);
PIKA_PROC_AUX_ARG_BOOLEAN (procedure, "save-comment",
_("Save comment to _file"),
_("Save comment"),
pika_export_comment (),
PIKA_PARAM_READWRITE);
PIKA_PROC_AUX_ARG_BOOLEAN (procedure, "glib-types",
_("Use GLib types (guint_8*)"),
_("Use GLib types"),
TRUE,
PIKA_PARAM_READWRITE);
PIKA_PROC_AUX_ARG_BOOLEAN (procedure, "save-alpha",
_("Save alpha channel (RG_BA/RGB)"),
_("Save the alpha channel"),
FALSE,
PIKA_PARAM_READWRITE);
PIKA_PROC_AUX_ARG_BOOLEAN (procedure, "rgb565",
_("Save as RGB565 (1_6-bit)"),
_("Use RGB565 encoding"),
FALSE,
PIKA_PARAM_READWRITE);
PIKA_PROC_AUX_ARG_BOOLEAN (procedure, "use-macros",
_("_Use macros instead of struct"),
_("Use C macros"),
FALSE,
PIKA_PARAM_READWRITE);
PIKA_PROC_AUX_ARG_BOOLEAN (procedure, "use-rle",
_("Use _1 bit Run-Length-Encoding"),
_("Use run-length-encoding"),
FALSE,
PIKA_PARAM_READWRITE);
PIKA_PROC_AUX_ARG_DOUBLE (procedure, "opacity",
_("Opaci_ty"),
_("Opacity"),
0.0, 100.0, 100.0,
PIKA_PARAM_READWRITE);
}
return procedure;
}
static PikaValueArray *
csource_save (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
GFile *file,
const PikaValueArray *args,
gpointer run_data)
{
PikaProcedureConfig *config;
PikaPDBStatusType status = PIKA_PDB_SUCCESS;
PikaExportReturn export = PIKA_EXPORT_CANCEL;
gchar *prefixed_name;
gchar *comment;
GError *error = NULL;
gegl_init (NULL, NULL);
if (run_mode != PIKA_RUN_INTERACTIVE)
return pika_procedure_new_return_values (procedure,
PIKA_PDB_CALLING_ERROR,
NULL);
config = pika_procedure_create_config (procedure);
pika_procedure_config_begin_export (config, image, run_mode, args, NULL);
pika_ui_init (PLUG_IN_BINARY);
export = pika_export_image (&image, &n_drawables, &drawables, "C Source",
PIKA_EXPORT_CAN_HANDLE_RGB |
PIKA_EXPORT_CAN_HANDLE_ALPHA);
if (n_drawables != 1)
{
g_set_error (&error, G_FILE_ERROR, 0,
_("C source does not support multiple layers."));
return pika_procedure_new_return_values (procedure,
PIKA_PDB_CALLING_ERROR,
error);
}
g_object_set (config,
"save-alpha", pika_drawable_has_alpha (drawables[0]),
NULL);
if (export == PIKA_EXPORT_CANCEL)
return pika_procedure_new_return_values (procedure,
PIKA_PDB_CANCEL,
NULL);
if (! save_dialog (image, procedure, G_OBJECT (config)))
status = PIKA_PDB_CANCEL;
g_object_get (config,
"prefixed-name", &prefixed_name,
"pika-comment", &comment,
NULL);
if (! prefixed_name || ! prefixed_name[0])
g_object_set (config,
"prefixed-name", "tmp",
NULL);
if (comment && ! comment[0])
g_object_set (config,
"pika-comment", NULL,
NULL);
g_free (prefixed_name);
g_free (comment);
if (status == PIKA_PDB_SUCCESS)
{
if (! save_image (file, image, drawables[0], G_OBJECT (config),
&error))
{
status = PIKA_PDB_EXECUTION_ERROR;
}
}
pika_procedure_config_end_export (config, image, file, status);
g_object_unref (config);
if (export == PIKA_EXPORT_EXPORT)
{
pika_image_delete (image);
g_free (drawables);
}
return pika_procedure_new_return_values (procedure, status, error);
}
static gboolean
diff2_rgb565 (guint8 *ip)
{
return ip[0] != ip[2] || ip[1] != ip[3];
}
static gboolean
diff2_rgb (guint8 *ip)
{
return ip[0] != ip[3] || ip[1] != ip[4] || ip[2] != ip[5];
}
static gboolean
diff2_rgba (guint8 *ip)
{
return ip[0] != ip[4] || ip[1] != ip[5] || ip[2] != ip[6] || ip[3] != ip[7];
}
static guint8 *
rl_encode_rgbx (guint8 *bp,
guint8 *ip,
guint8 *limit,
guint bpp)
{
gboolean (*diff2_pix) (guint8 *);
guint8 *ilimit = limit - bpp;
switch (bpp)
{
case 2: diff2_pix = diff2_rgb565; break;
case 3: diff2_pix = diff2_rgb; break;
case 4: diff2_pix = diff2_rgba; break;
default: g_assert_not_reached ();
}
while (ip < limit)
{
g_assert (ip < ilimit); /* paranoid */
if (diff2_pix (ip))
{
guint8 *s_ip = ip;
guint l = 1;
ip += bpp;
while (l < 127 && ip < ilimit && diff2_pix (ip))
{ ip += bpp; l += 1; }
if (ip == ilimit && l < 127)
{ ip += bpp; l += 1; }
*(bp++) = l;
memcpy (bp, s_ip, l * bpp);
bp += l * bpp;
}
else
{
guint l = 2;
ip += bpp;
while (l < 127 && ip < ilimit && !diff2_pix (ip))
{ ip += bpp; l += 1; }
*(bp++) = l | 128;
memcpy (bp, ip, bpp);
ip += bpp;
bp += bpp;
}
if (ip == ilimit)
{
*(bp++) = 1;
memcpy (bp, ip, bpp);
ip += bpp;
bp += bpp;
}
}
return bp;
}
static gboolean print (GOutputStream *stream,
GError **error,
const gchar *format,
...) G_GNUC_PRINTF (3, 4);
static gboolean
print (GOutputStream *stream,
GError **error,
const gchar *format,
...)
{
va_list args;
gboolean success;
va_start (args, format);
success = g_output_stream_vprintf (stream, NULL, NULL,
error, format, args);
va_end (args);
return success;
}
static inline gboolean
save_rle_decoder (GOutputStream *output,
const gchar *macro_name,
const gchar *s_uint,
const gchar *s_uint_8,
guint bpp,
GError **error)
{
return
print (output, error,
"#define %s_RUN_LENGTH_DECODE(image_buf, rle_data, size, bpp) do \\\n",
macro_name) &&
print (output, error,
"{ %s __bpp; %s *__ip; const %s *__il, *__rd; \\\n",
s_uint, s_uint_8, s_uint_8) &&
print (output, error,
" __bpp = (bpp); __ip = (image_buf); __il = __ip + (size) * __bpp; \\\n"
" __rd = (rle_data); if (__bpp > 3) { /* RGBA */ \\\n"
" while (__ip < __il) { %s __l = *(__rd++); \\\n",
s_uint) &&
print (output, error,
" if (__l & 128) { __l = __l - 128; \\\n"
" do { memcpy (__ip, __rd, 4); __ip += 4; } while (--__l); __rd += 4; \\\n"
" } else { __l *= 4; memcpy (__ip, __rd, __l); \\\n"
" __ip += __l; __rd += __l; } } \\\n"
" } else if (__bpp == 3) { /* RGB */ \\\n"
" while (__ip < __il) { %s __l = *(__rd++); \\\n",
s_uint) &&
print (output, error,
" if (__l & 128) { __l = __l - 128; \\\n"
" do { memcpy (__ip, __rd, 3); __ip += 3; } while (--__l); __rd += 3; \\\n"
" } else { __l *= 3; memcpy (__ip, __rd, __l); \\\n"
" __ip += __l; __rd += __l; } } \\\n"
" } else { /* RGB16 */ \\\n"
" while (__ip < __il) { %s __l = *(__rd++); \\\n",
s_uint) &&
print (output, error,
" if (__l & 128) { __l = __l - 128; \\\n"
" do { memcpy (__ip, __rd, 2); __ip += 2; } while (--__l); __rd += 2; \\\n"
" } else { __l *= 2; memcpy (__ip, __rd, __l); \\\n"
" __ip += __l; __rd += __l; } } \\\n"
" } } while (0)\n");
}
static inline gboolean
save_uchar (GOutputStream *output,
guint *c,
guint8 d,
gboolean use_macros,
GError **error)
{
static guint8 pad = 0;
if (*c > 74)
{
if (! use_macros)
{
if (! print (output, error, "\"\n \""))
return FALSE;
*c = 3;
}
else
{
if (! print (output, error, "\"\n \""))
return FALSE;
*c = 2;
}
}
if (d < 33 || (d >= 48 && d <= 57) || d > 126)
{
if (! print (output, error, "\\%03o", d))
return FALSE;
*c += 1 + 1 + (d > 7) + (d > 63);
pad = d < 64;
return TRUE;
}
if (d == '\\')
{
if (! print (output, error, "\\\\"))
return FALSE;
*c += 2;
}
else if (d == '"')
{
if (! print (output, error, "\\\""))
return FALSE;
*c += 2;
}
else if (pad && d >= '0' && d <= '9')
{
if (! print (output, error, "\"\"%c", d))
return FALSE;
*c += 3;
}
else
{
if (! print (output, error, "%c", d))
return FALSE;
*c += 1;
}
pad = 0;
return TRUE;
}
static gboolean
save_image (GFile *file,
PikaImage *image,
PikaDrawable *drawable,
GObject *config,
GError **error)
{
GOutputStream *output;
GeglBuffer *buffer;
GCancellable *cancellable;
PikaImageType drawable_type = pika_drawable_type (drawable);
gchar *s_uint_8, *s_uint, *s_char, *s_null;
guint c;
gchar *macro_name;
guint8 *img_buffer, *img_buffer_end;
gchar *basename;
guint8 *data, *p;
gint width;
gint height;
gint x, y, pad, n_bytes, bpp;
const Babl *drawable_format;
gint drawable_bpp;
gchar *config_prefixed_name;
gchar *config_comment;
gboolean config_save_comment;
gboolean config_glib_types;
gboolean config_save_alpha;
gboolean config_rgb565;
gboolean config_use_macros;
gboolean config_use_rle;
gdouble config_opacity;
g_object_get (config,
"prefixed-name", &config_prefixed_name,
"pika-comment", &config_comment,
"save-comment", &config_save_comment,
"glib-types", &config_glib_types,
"save-alpha", &config_save_alpha,
"rgb565", &config_rgb565,
"use-macros", &config_use_macros,
"use-rle", &config_use_rle,
"opacity", &config_opacity,
NULL);
output = G_OUTPUT_STREAM (g_file_replace (file,
NULL, FALSE, G_FILE_CREATE_NONE,
NULL, error));
if (output)
{
GOutputStream *buffered;
buffered = g_buffered_output_stream_new (output);
g_object_unref (output);
output = buffered;
}
else
{
return FALSE;
}
buffer = pika_drawable_get_buffer (drawable);
width = gegl_buffer_get_width (buffer);
height = gegl_buffer_get_height (buffer);
if (pika_drawable_has_alpha (drawable))
drawable_format = babl_format ("R'G'B'A u8");
else
drawable_format = babl_format ("R'G'B' u8");
drawable_bpp = babl_format_get_bytes_per_pixel (drawable_format);
bpp = config_rgb565 ? 2 : (config_save_alpha ? 4 : 3);
n_bytes = width * height * bpp;
pad = width * drawable_bpp;
if (config_use_rle)
pad = MAX (pad, 130 + n_bytes / 127);
data = g_new (guint8, pad + n_bytes);
p = data + pad;
for (y = 0; y < height; y++)
{
gegl_buffer_get (buffer, GEGL_RECTANGLE (0, y, width, 1), 1.0,
drawable_format, data,
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
if (bpp == 2)
{
for (x = 0; x < width; x++)
{
guint8 *d = data + x * drawable_bpp;
guint8 r, g, b;
gushort rgb16;
gdouble alpha = drawable_type == PIKA_RGBA_IMAGE ? d[3] : 0xff;
alpha *= config_opacity / 25500.0;
r = (0.5 + alpha * (gdouble) d[0]);
g = (0.5 + alpha * (gdouble) d[1]);
b = (0.5 + alpha * (gdouble) d[2]);
r >>= 3;
g >>= 2;
b >>= 3;
rgb16 = (r << 11) + (g << 5) + b;
*(p++) = (guchar) rgb16;
*(p++) = (guchar) (rgb16 >> 8);
}
}
else if (config_save_alpha)
{
for (x = 0; x < width; x++)
{
guint8 *d = data + x * drawable_bpp;
gdouble alpha = drawable_type == PIKA_RGBA_IMAGE ? d[3] : 0xff;
alpha *= config_opacity / 100.0;
*(p++) = d[0];
*(p++) = d[1];
*(p++) = d[2];
*(p++) = alpha + 0.5;
}
}
else
{
for (x = 0; x < width; x++)
{
guint8 *d = data + x * drawable_bpp;
gdouble alpha = drawable_type == PIKA_RGBA_IMAGE ? d[3] : 0xff;
alpha *= config_opacity / 25500.0;
*(p++) = 0.5 + alpha * (gdouble) d[0];
*(p++) = 0.5 + alpha * (gdouble) d[1];
*(p++) = 0.5 + alpha * (gdouble) d[2];
}
}
}
img_buffer = data + pad;
if (config_use_rle)
{
img_buffer_end = rl_encode_rgbx (data, img_buffer,
img_buffer + n_bytes, bpp);
img_buffer = data;
}
else
{
img_buffer_end = img_buffer + n_bytes;
}
if (! config_use_macros && config_glib_types)
{
s_uint_8 = "guint8 ";
s_uint = "guint ";
s_char = "gchar ";
s_null = "NULL";
}
else if (! config_use_macros)
{
s_uint_8 = "unsigned char";
s_uint = "unsigned int ";
s_char = "char ";
s_null = "(char*) 0";
}
else if (config_use_macros && config_glib_types)
{
s_uint_8 = "guint8";
s_uint = "guint";
s_char = "gchar";
s_null = "NULL";
}
else /* config_use_macros && ! config_glib_types */
{
s_uint_8 = "unsigned char";
s_uint = "unsigned int";
s_char = "char";
s_null = "(char*) 0";
}
macro_name = g_ascii_strup (config_prefixed_name, -1);
basename = g_file_get_basename (file);
if (! print (output, error,
"/* PIKA %s C-Source image dump %s(%s) */\n\n",
config_save_alpha ? "RGBA" : "RGB",
config_use_rle ? "1-byte-run-length-encoded " : "",
basename))
goto fail;
g_free (basename);
if (config_use_rle && !config_use_macros)
{
if (! save_rle_decoder (output,
macro_name,
config_glib_types ? "guint" : "unsigned int",
config_glib_types ? "guint8" : "unsigned char",
bpp,
error))
goto fail;
}
if (!config_use_macros)
{
if (! print (output, error,
"static const struct {\n"
" %s\t width;\n"
" %s\t height;\n"
" %s\t bytes_per_pixel; /* 2:RGB16, 3:RGB, 4:RGBA */ \n",
s_uint, s_uint, s_uint))
goto fail;
if (config_save_comment)
{
if (! print (output, error, " %s\t*comment;\n", s_char))
goto fail;
}
if (! print (output, error,
" %s\t %spixel_data[",
s_uint_8,
config_use_rle ? "rle_" : ""))
goto fail;
if (config_use_rle)
{
if (! print (output, error,
"%u + 1];\n",
(guint) (img_buffer_end - img_buffer)))
goto fail;
}
else
{
if (! print (output, error,
"%u * %u * %u + 1];\n",
width,
height,
bpp))
goto fail;
}
if (! print (output, error, "} %s = {\n", config_prefixed_name))
goto fail;
if (! print (output, error,
" %u, %u, %u,\n",
width,
height,
bpp))
goto fail;
}
else /* use macros */
{
if (! print (output, error,
"#define %s_WIDTH (%u)\n"
"#define %s_HEIGHT (%u)\n"
"#define %s_BYTES_PER_PIXEL (%u) /* 2:RGB16, 3:RGB, 4:RGBA */\n",
macro_name, width,
macro_name, height,
macro_name, bpp))
{
goto fail;
}
}
if (config_save_comment && ! config_comment)
{
if (! config_use_macros)
{
if (! print (output, error, " %s,\n", s_null))
goto fail;
}
else
{
if (! print (output, error,
"#define %s_COMMENT (%s)\n",
macro_name, s_null))
goto fail;
}
}
else if (config_save_comment)
{
gchar *p = config_comment - 1;
if (config_use_macros)
{
if (! print (output, error, "#define %s_COMMENT \\\n", macro_name))
goto fail;
}
if (! print (output, error, " \""))
goto fail;
while (*(++p))
{
gboolean success = FALSE;
if (*p == '\\')
success = print (output, error, "\\\\");
else if (*p == '"')
success = print (output, error, "\\\"");
else if (*p == '\n' && p[1])
success = print (output, error,
"\\n\"%s\n \"",
config_use_macros ? " \\" : "");
else if (*p == '\n')
success = print (output, error, "\\n");
else if (*p == '\r')
success = print (output, error, "\\r");
else if (*p == '\b')
success = print (output, error, "\\b");
else if (*p == '\f')
success = print (output, error, "\\f");
else if (( *p >= 32 && *p <= 47 ) || (*p >= 58 && *p <= 126))
success = print (output, error, "%c", *p);
else
success = print (output, error, "\\%03o", *p);
if (! success)
goto fail;
}
if (! config_use_macros)
{
if (! print (output, error, "\",\n"))
goto fail;
}
else
{
if (! print (output, error, "\"\n"))
goto fail;
}
}
if (config_use_macros)
{
if (! print (output, error,
"#define %s_%sPIXEL_DATA ((%s*) %s_%spixel_data)\n",
macro_name,
config_use_rle ? "RLE_" : "",
s_uint_8,
macro_name,
config_use_rle ? "rle_" : ""))
goto fail;
if (config_use_rle)
{
if (! save_rle_decoder (output,
macro_name,
s_uint,
s_uint_8,
bpp,
error))
goto fail;
}
if (! print (output, error,
"static const %s %s_%spixel_data[",
s_uint_8,
macro_name,
config_use_rle ? "rle_" : ""))
goto fail;
if (config_use_rle)
{
if (! print (output, error,
"%u + 1] =\n",
(guint) (img_buffer_end - img_buffer)))
goto fail;
}
else
{
if (! print (output, error,
"%u * %u * %u + 1] =\n",
width,
height,
bpp))
goto fail;
}
if (! print (output, error, "(\""))
goto fail;
c = 2;
}
else
{
if (! print (output, error, " \""))
goto fail;
c = 3;
}
switch (drawable_type)
{
case PIKA_RGB_IMAGE:
case PIKA_RGBA_IMAGE:
do
{
if (! save_uchar (output, &c, *(img_buffer++), config_use_macros,
error))
goto fail;
}
while (img_buffer < img_buffer_end);
break;
default:
g_warning ("unhandled drawable type (%d)", drawable_type);
goto fail;
}
if (! config_use_macros)
{
if (! print (output, error, "\",\n};\n\n"))
goto fail;
}
else
{
if (! print (output, error, "\");\n\n"))
goto fail;
}
if (! g_output_stream_close (output, NULL, error))
goto fail;
g_object_unref (output);
g_object_unref (buffer);
return TRUE;
fail:
cancellable = g_cancellable_new ();
g_cancellable_cancel (cancellable);
g_output_stream_close (output, cancellable, NULL);
g_object_unref (cancellable);
g_object_unref (output);
g_object_unref (buffer);
return FALSE;
}
static gboolean
save_dialog (PikaImage *image,
PikaProcedure *procedure,
GObject *config)
{
GtkWidget *dialog;
GtkWidget *vbox;
gboolean run;
dialog = pika_save_procedure_dialog_new (PIKA_SAVE_PROCEDURE (procedure),
PIKA_PROCEDURE_CONFIG (config),
image);
pika_procedure_dialog_get_scale_entry (PIKA_PROCEDURE_DIALOG (dialog),
"opacity", 1.0);
pika_procedure_dialog_set_sensitive (PIKA_PROCEDURE_DIALOG (dialog),
"save-alpha", TRUE, config, "rgb565",
TRUE);
vbox = pika_procedure_dialog_fill_box (PIKA_PROCEDURE_DIALOG (dialog),
"csource-box",
"prefixed-name", "pika-comment",
"save-comment", "glib-types",
"use-macros", "use-rle", "save-alpha",
"rgb565", "opacity",
NULL);
gtk_box_set_spacing (GTK_BOX (vbox), 12);
gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
pika_procedure_dialog_fill (PIKA_PROCEDURE_DIALOG (dialog),
"csource-box",
NULL);
gtk_widget_show (dialog);
run = pika_procedure_dialog_run (PIKA_PROCEDURE_DIALOG (dialog));
gtk_widget_destroy (dialog);
return run;
}

View File

@ -0,0 +1,213 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* 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/>.
*/
/*
* Desktop Entry Specification
* http://standards.freedesktop.org/desktop-entry-spec/latest/
*/
#include "config.h"
#include <string.h>
#include <glib/gstdio.h>
#include <libpika/pika.h>
#include "libpika/stdplugins-intl.h"
#define LOAD_PROC "file-desktop-link-load"
#define PLUG_IN_BINARY "file-desktop-link"
#define PLUG_IN_ROLE "pika-file-desktop-link"
typedef struct _Desktop Desktop;
typedef struct _DesktopClass DesktopClass;
struct _Desktop
{
PikaPlugIn parent_instance;
};
struct _DesktopClass
{
PikaPlugInClass parent_class;
};
#define DESKTOP_TYPE (desktop_get_type ())
#define DESKTOP (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), DESKTOP_TYPE, Desktop))
GType desktop_get_type (void) G_GNUC_CONST;
static GList * desktop_query_procedures (PikaPlugIn *plug_in);
static PikaProcedure * desktop_create_procedure (PikaPlugIn *plug_in,
const gchar *name);
static PikaValueArray * desktop_load (PikaProcedure *procedure,
PikaRunMode run_mode,
GFile *file,
const PikaValueArray *args,
gpointer run_data);
static PikaImage * load_image (GFile *file,
PikaRunMode run_mode,
GError **error);
G_DEFINE_TYPE (Desktop, desktop, PIKA_TYPE_PLUG_IN)
PIKA_MAIN (DESKTOP_TYPE)
DEFINE_STD_SET_I18N
static void
desktop_class_init (DesktopClass *klass)
{
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
plug_in_class->query_procedures = desktop_query_procedures;
plug_in_class->create_procedure = desktop_create_procedure;
plug_in_class->set_i18n = STD_SET_I18N;
}
static void
desktop_init (Desktop *desktop)
{
}
static GList *
desktop_query_procedures (PikaPlugIn *plug_in)
{
return g_list_append (NULL, g_strdup (LOAD_PROC));
}
static PikaProcedure *
desktop_create_procedure (PikaPlugIn *plug_in,
const gchar *name)
{
PikaProcedure *procedure = NULL;
if (! strcmp (name, LOAD_PROC))
{
procedure = pika_load_procedure_new (plug_in, name,
PIKA_PDB_PROC_TYPE_PLUGIN,
desktop_load, NULL, NULL);
pika_procedure_set_menu_label (procedure, _("Desktop Link"));
pika_procedure_set_documentation (procedure,
"Follows a link to an image in a "
".desktop file",
"Opens a .desktop file and if it is "
"a link, it asks PIKA to open the "
"file the link points to.",
LOAD_PROC);
pika_procedure_set_attribution (procedure,
"Sven Neumann",
"Sven Neumann",
"2006");
pika_file_procedure_set_extensions (PIKA_FILE_PROCEDURE (procedure),
"desktop");
}
return procedure;
}
static PikaValueArray *
desktop_load (PikaProcedure *procedure,
PikaRunMode run_mode,
GFile *file,
const PikaValueArray *args,
gpointer run_data)
{
PikaValueArray *return_values;
PikaImage *image;
GError *error = NULL;
image = load_image (file, run_mode, &error);
if (! image)
return pika_procedure_new_return_values (procedure,
PIKA_PDB_EXECUTION_ERROR,
error);
return_values = pika_procedure_new_return_values (procedure,
PIKA_PDB_SUCCESS,
NULL);
PIKA_VALUES_SET_IMAGE (return_values, 1, image);
return return_values;
}
static PikaImage *
load_image (GFile *file,
PikaRunMode run_mode,
GError **load_error)
{
GKeyFile *key_file = g_key_file_new ();
PikaImage *image = NULL;
gchar *filename = NULL;
gchar *group = NULL;
gchar *value = NULL;
GError *error = NULL;
filename = g_file_get_path (file);
if (! g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, &error))
goto out;
group = g_key_file_get_start_group (key_file);
if (! group || strcmp (group, G_KEY_FILE_DESKTOP_GROUP) != 0)
goto out;
value = g_key_file_get_value (key_file,
group, G_KEY_FILE_DESKTOP_KEY_TYPE, &error);
if (! value || strcmp (value, G_KEY_FILE_DESKTOP_TYPE_LINK) != 0)
goto out;
g_free (value);
value = g_key_file_get_value (key_file,
group, G_KEY_FILE_DESKTOP_KEY_URL, &error);
if (value)
image = pika_file_load (run_mode, g_file_new_for_uri (value));
out:
if (error)
{
g_set_error (load_error, error->domain, error->code,
_("Error loading desktop file '%s': %s"),
pika_filename_to_utf8 (filename), error->message);
g_error_free (error);
}
g_free (value);
g_free (group);
g_free (filename);
g_key_file_free (key_file);
return image;
}

1834
plug-ins/common/file-dicom.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,428 @@
/* PIKA - Photo and Image Kooker Application
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* Farbfeld Image Format plug-in
*
* Copyright (C) 2023 Alex S.
*
* 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/>.
*/
#include "config.h"
#include <string.h>
#include <errno.h>
#include <glib/gstdio.h>
#include <libpika/pika.h>
#include <libpika/pikaui.h>
#include "libpika/stdplugins-intl.h"
#define LOAD_PROC "file-farbfeld-load"
#define SAVE_PROC "file-farbfeld-save"
#define PLUG_IN_BINARY "file-farbfeld"
#define PLUG_IN_ROLE "pika-file-farbfeld"
typedef struct _Farbfeld Farbfeld;
typedef struct _FarbfeldClass FarbfeldClass;
struct _Farbfeld
{
PikaPlugIn parent_instance;
};
struct _FarbfeldClass
{
PikaPlugInClass parent_class;
};
#define FARBFELD_TYPE (farbfeld_get_type ())
#define FARBFELD (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FARBFELD_TYPE, Farbfeld))
GType farbfeld_get_type (void) G_GNUC_CONST;
static GList * farbfeld_query_procedures (PikaPlugIn *plug_in);
static PikaProcedure * farbfeld_create_procedure (PikaPlugIn *plug_in,
const gchar *name);
static PikaValueArray * farbfeld_load (PikaProcedure *procedure,
PikaRunMode run_mode,
GFile *file,
const PikaValueArray *args,
gpointer run_data);
static PikaValueArray * farbfeld_save (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
GFile *file,
const PikaValueArray *args,
gpointer run_data);
static PikaImage * load_image (GFile *file,
GObject *config,
PikaRunMode run_mode,
GError **error);
static gboolean save_image (GFile *file,
PikaImage *image,
PikaDrawable *drawable,
GError **error);
G_DEFINE_TYPE (Farbfeld, farbfeld, PIKA_TYPE_PLUG_IN)
PIKA_MAIN (FARBFELD_TYPE)
DEFINE_STD_SET_I18N
static void
farbfeld_class_init (FarbfeldClass *klass)
{
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
plug_in_class->query_procedures = farbfeld_query_procedures;
plug_in_class->create_procedure = farbfeld_create_procedure;
plug_in_class->set_i18n = STD_SET_I18N;
}
static void
farbfeld_init (Farbfeld *farbfeld)
{
}
static GList *
farbfeld_query_procedures (PikaPlugIn *plug_in)
{
GList *list = NULL;
list = g_list_append (list, g_strdup (LOAD_PROC));
list = g_list_append (list, g_strdup (SAVE_PROC));
return list;
}
static PikaProcedure *
farbfeld_create_procedure (PikaPlugIn *plug_in,
const gchar *name)
{
PikaProcedure *procedure = NULL;
if (! strcmp (name, LOAD_PROC))
{
procedure = pika_load_procedure_new (plug_in, name,
PIKA_PDB_PROC_TYPE_PLUGIN,
farbfeld_load, NULL, NULL);
pika_procedure_set_menu_label (procedure,
N_("Farbfeld"));
pika_procedure_set_documentation (procedure,
_("Load file in the Farbfeld file "
"format"),
_("Load file in the Farbfeld file "
"format"),
name);
pika_procedure_set_attribution (procedure,
"Alex S.",
"Alex S.",
"2023");
pika_file_procedure_set_extensions (PIKA_FILE_PROCEDURE (procedure),
"ff");
pika_file_procedure_set_magics (PIKA_FILE_PROCEDURE (procedure),
"0,string,farbfeld");
}
else if (! strcmp (name, SAVE_PROC))
{
procedure = pika_save_procedure_new (plug_in, name,
PIKA_PDB_PROC_TYPE_PLUGIN,
farbfeld_save, NULL, NULL);
pika_procedure_set_image_types (procedure, "*");
pika_procedure_set_menu_label (procedure, _("Farbfeld"));
pika_procedure_set_documentation (procedure,
_("Export image in the Farbfeld file "
"format"),
_("Export image in the Farbfeld file "
"format"),
name);
pika_procedure_set_attribution (procedure,
"Alex S.",
"Alex S.",
"2023");
pika_file_procedure_set_extensions (PIKA_FILE_PROCEDURE (procedure),
"ff");
}
return procedure;
}
static PikaValueArray *
farbfeld_load (PikaProcedure *procedure,
PikaRunMode run_mode,
GFile *file,
const PikaValueArray *args,
gpointer run_data)
{
PikaProcedureConfig *config;
PikaValueArray *return_vals;
PikaImage *image;
GError *error = NULL;
gegl_init (NULL, NULL);
config = pika_procedure_create_config (procedure);
pika_procedure_config_begin_run (config, NULL, run_mode, args);
image = load_image (file, G_OBJECT (config), run_mode, &error);
if (! image)
return pika_procedure_new_return_values (procedure,
PIKA_PDB_EXECUTION_ERROR,
error);
pika_procedure_config_end_run (config, PIKA_PDB_SUCCESS);
g_object_unref (config);
return_vals = pika_procedure_new_return_values (procedure,
PIKA_PDB_SUCCESS,
NULL);
PIKA_VALUES_SET_IMAGE (return_vals, 1, image);
return return_vals;
}
static PikaValueArray *
farbfeld_save (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
GFile *file,
const PikaValueArray *args,
gpointer run_data)
{
PikaPDBStatusType status = PIKA_PDB_SUCCESS;
PikaExportReturn export = PIKA_EXPORT_CANCEL;
GError *error = NULL;
gegl_init (NULL, NULL);
switch (run_mode)
{
case PIKA_RUN_INTERACTIVE:
case PIKA_RUN_WITH_LAST_VALS:
pika_ui_init (PLUG_IN_BINARY);
export = pika_export_image (&image, &n_drawables, &drawables, "farbfeld",
PIKA_EXPORT_CAN_HANDLE_RGB |
PIKA_EXPORT_CAN_HANDLE_GRAY |
PIKA_EXPORT_CAN_HANDLE_INDEXED |
PIKA_EXPORT_CAN_HANDLE_ALPHA);
if (export == PIKA_EXPORT_CANCEL)
return pika_procedure_new_return_values (procedure,
PIKA_PDB_CANCEL,
NULL);
break;
default:
break;
}
if (n_drawables != 1)
{
g_set_error (&error, G_FILE_ERROR, 0,
_("Farbfeld format does not support multiple layers."));
return pika_procedure_new_return_values (procedure,
PIKA_PDB_CALLING_ERROR,
error);
}
if (! save_image (file, image, drawables[0], &error))
status = PIKA_PDB_EXECUTION_ERROR;
if (export == PIKA_EXPORT_EXPORT)
{
pika_image_delete (image);
g_free (drawables);
}
return pika_procedure_new_return_values (procedure, status, error);
}
static PikaImage *
load_image (GFile *file,
GObject *config,
PikaRunMode run_mode,
GError **error)
{
PikaImage *image = NULL;
PikaLayer *layer;
GeglBuffer *buffer;
guint16 *pixels;
guchar magic_number[8];
guint32 width;
guint32 height;
guint32 row_size;
const Babl *format = babl_format ("R'G'B'A u16");
FILE *fp;
fp = g_fopen (g_file_peek_path (file), "rb");
if (! fp)
{
g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
_("Could not open '%s' for reading: %s"),
pika_file_get_utf8_name (file), g_strerror (errno));
return NULL;
}
/* Load the header */
if (! fread (magic_number, 8, 1, fp) ||
! fread (&width, sizeof (guint32), 1, fp) ||
! fread (&height, sizeof (guint32), 1, fp))
{
g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
_("Failed to read Farbfeld header"));
return NULL;
}
/* Header information is stored in Big-Endian format */
width = GUINT32_FROM_BE (width);
height = GUINT32_FROM_BE (height);
row_size = width * sizeof (guint16) * 4;
image = pika_image_new_with_precision (width, height, PIKA_RGB,
PIKA_PRECISION_U16_NON_LINEAR);
layer = pika_layer_new (image, _("Background"), width, height,
PIKA_RGBA_IMAGE, 100,
pika_image_get_default_new_layer_mode (image));
pika_image_insert_layer (image, layer, NULL, 0);
buffer = pika_drawable_get_buffer (PIKA_DRAWABLE (layer));
for (gint i = 0; i < height; i++)
{
pixels = g_malloc (row_size);
if (! fread (pixels, row_size, 1, fp))
{
g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
_("Premature end of Farbfeld pixel data"));
return NULL;
}
/* Pixels are also stored in Big-Endian format */
for (gint j = 0; j < (width * 4); j++)
pixels[j] = GUINT16_FROM_BE (pixels[j]);
gegl_buffer_set (buffer,
GEGL_RECTANGLE (0, i, width, 1), 0,
format, pixels, GEGL_AUTO_ROWSTRIDE);
g_free (pixels);
}
fclose (fp);
g_object_unref (buffer);
return image;
}
static gboolean
save_image (GFile *file,
PikaImage *image,
PikaDrawable *drawable,
GError **error)
{
FILE *fp;
GeglBuffer *buffer;
guint16 *pixels;
const Babl *format = babl_format ("R'G'B'A u16");
gchar *magic_number;
guint32 image_width;
guint32 image_height;
guint32 export_width;
guint32 export_height;
pika_progress_init_printf (_("Exporting '%s'"),
pika_file_get_utf8_name (file));
fp = g_fopen (g_file_peek_path (file), "wb");
if (! fp)
{
g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
_("Could not open '%s' for writing: %s"),
pika_file_get_utf8_name (file), g_strerror (errno));
return FALSE;
}
buffer = pika_drawable_get_buffer (drawable);
image_width = gegl_buffer_get_width (buffer);
image_height = gegl_buffer_get_height (buffer);
/* Farbfeld values are Big-Endian */
export_width = GUINT32_TO_BE (image_width);
export_height = GUINT32_TO_BE (image_height);
/* Write header */
magic_number = "farbfeld";
for (gint i = 0; i < 8; i++)
fputc (magic_number[i], fp);
fwrite ((gchar *) &export_width, 1, 4, fp);
fwrite ((gchar *) &export_height, 1, 4, fp);
/* Write pixel data */
for (gint i = 0; i < image_height; i++)
{
pixels = g_malloc (image_width * sizeof (guint16) * 4);
gegl_buffer_get (buffer, GEGL_RECTANGLE (0, i, image_width, 1),
1.0, format, pixels,
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
for (gint j = 0; j < (image_width * 4); j++)
{
pixels[j] = GUINT16_TO_BE (pixels[j]);
fwrite ((gchar *) &pixels[j], 1, 2, fp);
}
g_free (pixels);
pika_progress_update (i / (gdouble) image_height);
}
pika_progress_update (1.0);
fclose (fp);
if (buffer)
g_object_unref (buffer);
return TRUE;
}

335
plug-ins/common/file-gbr.c Normal file
View File

@ -0,0 +1,335 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* 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/>.
*/
/*
* gbr plug-in version 1.00
* Loads/exports version 2 PIKA .gbr files, by Tim Newsome <drz@frody.bloke.com>
* Some bits stolen from the .99.7 source tree.
*
* Added in GBR version 1 support after learning that there wasn't a
* tool to read them.
* July 6, 1998 by Seth Burgess <sjburges@gimp.org>
*
* Dec 17, 2000
* Load and save PIKA brushes in GRAY or RGBA. jtl + neo
*
*
* TODO: Give some better error reporting on not opening files/bad headers
* etc.
*/
#include "config.h"
#include <libpika/pika.h>
#include <libpika/pikaui.h>
#include "libpika/stdplugins-intl.h"
#define SAVE_PROC "file-gbr-save"
#define PLUG_IN_BINARY "file-gbr"
typedef struct _Gbr Gbr;
typedef struct _GbrClass GbrClass;
struct _Gbr
{
PikaPlugIn parent_instance;
};
struct _GbrClass
{
PikaPlugInClass parent_class;
};
#define GBR_TYPE (gbr_get_type ())
#define GBR (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GBR_TYPE, Gbr))
GType gbr_get_type (void) G_GNUC_CONST;
static GList * gbr_query_procedures (PikaPlugIn *plug_in);
static PikaProcedure * gbr_create_procedure (PikaPlugIn *plug_in,
const gchar *name);
static PikaValueArray * gbr_save (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
GFile *file,
const PikaValueArray *args,
gpointer run_data);
static gboolean save_dialog (PikaProcedure *procedure,
GObject *config,
PikaImage *image);
G_DEFINE_TYPE (Gbr, gbr, PIKA_TYPE_PLUG_IN)
PIKA_MAIN (GBR_TYPE)
DEFINE_STD_SET_I18N
static void
gbr_class_init (GbrClass *klass)
{
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
plug_in_class->query_procedures = gbr_query_procedures;
plug_in_class->create_procedure = gbr_create_procedure;
plug_in_class->set_i18n = STD_SET_I18N;
}
static void
gbr_init (Gbr *gbr)
{
}
static GList *
gbr_query_procedures (PikaPlugIn *plug_in)
{
return g_list_append (NULL, g_strdup (SAVE_PROC));
}
static PikaProcedure *
gbr_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,
gbr_save, NULL, NULL);
pika_procedure_set_image_types (procedure, "*");
pika_procedure_set_menu_label (procedure, _("PIKA brush"));
pika_file_procedure_set_format_name (PIKA_FILE_PROCEDURE (procedure),
_("Brush"));
pika_procedure_set_icon_name (procedure, PIKA_ICON_BRUSH);
pika_procedure_set_documentation (procedure,
_("Exports files in the PIKA brush "
"file format"),
_("Exports files in the PIKA brush "
"file format"),
SAVE_PROC);
pika_procedure_set_attribution (procedure,
"Tim Newsome, Jens Lautenbacher, "
"Sven Neumann",
"Tim Newsome, Jens Lautenbacher, "
"Sven Neumann",
"1997-2000");
pika_file_procedure_set_mime_types (PIKA_FILE_PROCEDURE (procedure),
"image/x-pika-gbr");
pika_file_procedure_set_extensions (PIKA_FILE_PROCEDURE (procedure),
"gbr");
pika_file_procedure_set_handles_remote (PIKA_FILE_PROCEDURE (procedure),
TRUE);
PIKA_PROC_ARG_INT (procedure, "spacing",
_("Sp_acing"),
_("Spacing of the brush"),
1, 1000, 10,
PIKA_PARAM_READWRITE);
PIKA_PROC_ARG_STRING (procedure, "description",
_("_Description"),
_("Short description of the brush"),
_("PIKA Brush"),
PIKA_PARAM_READWRITE);
}
return procedure;
}
static PikaValueArray *
gbr_save (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
GFile *file,
const PikaValueArray *args,
gpointer run_data)
{
PikaProcedureConfig *config;
PikaPDBStatusType status = PIKA_PDB_SUCCESS;
PikaExportReturn export = PIKA_EXPORT_CANCEL;
gchar *description;
GError *error = NULL;
config = pika_procedure_create_config (procedure);
pika_procedure_config_begin_run (config, image, run_mode, args);
g_object_get (config,
"description", &description,
NULL);
if (! description || ! strlen (description))
{
gchar *name = g_path_get_basename (pika_file_get_utf8_name (file));
if (g_str_has_suffix (name, ".gbr"))
name[strlen (name) - 4] = '\0';
if (strlen (name))
g_object_set (config,
"description", name,
NULL);
g_free (name);
}
g_free (description);
switch (run_mode)
{
case PIKA_RUN_INTERACTIVE:
case PIKA_RUN_WITH_LAST_VALS:
pika_ui_init (PLUG_IN_BINARY);
export = pika_export_image (&image, &n_drawables, &drawables, "GBR",
PIKA_EXPORT_CAN_HANDLE_GRAY |
PIKA_EXPORT_CAN_HANDLE_RGB |
PIKA_EXPORT_CAN_HANDLE_INDEXED |
PIKA_EXPORT_CAN_HANDLE_ALPHA);
if (export == PIKA_EXPORT_CANCEL)
return pika_procedure_new_return_values (procedure,
PIKA_PDB_CANCEL,
NULL);
break;
default:
break;
}
if (n_drawables != 1)
{
g_set_error (&error, G_FILE_ERROR, 0,
_("GBR format does not support multiple layers."));
return pika_procedure_new_return_values (procedure,
PIKA_PDB_CALLING_ERROR,
error);
}
if (run_mode == PIKA_RUN_INTERACTIVE)
{
if (! save_dialog (procedure, G_OBJECT (config), image))
status = PIKA_PDB_CANCEL;
}
if (status == PIKA_PDB_SUCCESS)
{
PikaValueArray *save_retvals;
gint spacing;
g_object_get (config,
"description", &description,
"spacing", &spacing,
NULL);
save_retvals =
pika_pdb_run_procedure (pika_get_pdb (),
"file-gbr-save-internal",
PIKA_TYPE_RUN_MODE, PIKA_RUN_NONINTERACTIVE,
PIKA_TYPE_IMAGE, image,
PIKA_TYPE_DRAWABLE, drawables[0],
G_TYPE_FILE, file,
G_TYPE_INT, spacing,
G_TYPE_STRING, description,
G_TYPE_NONE);
g_free (description);
if (PIKA_VALUES_GET_ENUM (save_retvals, 0) != PIKA_PDB_SUCCESS)
{
g_set_error (&error, 0, 0,
"Running procedure 'file-gbr-save-internal' "
"failed: %s",
pika_pdb_get_last_error (pika_get_pdb ()));
status = PIKA_PDB_EXECUTION_ERROR;
}
pika_value_array_unref (save_retvals);
}
pika_procedure_config_end_run (config, status);
g_object_unref (config);
if (export == PIKA_EXPORT_EXPORT)
{
pika_image_delete (image);
g_free (drawables);
}
return pika_procedure_new_return_values (procedure, status, error);
}
static gboolean
save_dialog (PikaProcedure *procedure,
GObject *config,
PikaImage *image)
{
GtkWidget *dialog;
GtkWidget *vbox;
GtkWidget *entry;
GtkWidget *real_entry;
gboolean run;
dialog = pika_save_procedure_dialog_new (PIKA_SAVE_PROCEDURE (procedure),
PIKA_PROCEDURE_CONFIG (config),
image);
entry = pika_procedure_dialog_get_widget (PIKA_PROCEDURE_DIALOG (dialog),
"description", PIKA_TYPE_LABEL_ENTRY);
real_entry = pika_label_entry_get_entry (PIKA_LABEL_ENTRY (entry));
gtk_entry_set_max_length (GTK_ENTRY (real_entry), 256);
gtk_entry_set_width_chars (GTK_ENTRY (real_entry), 20);
gtk_entry_set_activates_default (GTK_ENTRY (real_entry), TRUE);
pika_procedure_dialog_get_scale_entry (PIKA_PROCEDURE_DIALOG (dialog),
"spacing", 1.0);
vbox = pika_procedure_dialog_fill_box (PIKA_PROCEDURE_DIALOG (dialog),
"gbr-vbox", "description", "spacing",
NULL);
gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
pika_procedure_dialog_fill (PIKA_PROCEDURE_DIALOG (dialog),
"gbr-vbox", NULL);
gtk_widget_show (dialog);
run = pika_procedure_dialog_run (PIKA_PROCEDURE_DIALOG (dialog));
gtk_widget_destroy (dialog);
return run;
}

523
plug-ins/common/file-gegl.c Normal file
View File

@ -0,0 +1,523 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* file-gegl.c -- GEGL based file format plug-in
* Copyright (C) 2012 Simon Budig <simon@gimp.org>
*
* 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/>.
*/
#include "config.h"
#include <stdlib.h>
#include <errno.h>
#include <glib/gstdio.h>
#include <libpika/pika.h>
#include <libpika/pikaui.h>
#include "libpika/stdplugins-intl.h"
#define PLUG_IN_BINARY "file-gegl"
typedef struct _FileFormat FileFormat;
struct _FileFormat
{
const gchar *file_type;
const gchar *mime_type;
const gchar *extensions;
const gchar *magic;
const gchar *load_proc;
const gchar *load_blurb;
const gchar *load_help;
const gchar *load_op;
const gchar *save_proc;
const gchar *save_blurb;
const gchar *save_help;
const gchar *save_op;
};
typedef struct _Goat Goat;
typedef struct _GoatClass GoatClass;
struct _Goat
{
PikaPlugIn parent_instance;
};
struct _GoatClass
{
PikaPlugInClass parent_class;
};
#define GOAT_TYPE (goat_get_type ())
#define GOAT (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GOAT_TYPE, Goat))
GType goat_get_type (void) G_GNUC_CONST;
static GList * goat_query_procedures (PikaPlugIn *plug_in);
static PikaProcedure * goat_create_procedure (PikaPlugIn *plug_in,
const gchar *name);
static PikaValueArray * goat_load (PikaProcedure *procedure,
PikaRunMode run_mode,
GFile *file,
const PikaValueArray *args,
gpointer run_data);
static PikaValueArray * goat_save (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
GFile *file,
const PikaValueArray *args,
gpointer run_data);
static PikaImage * load_image (GFile *file,
const gchar *gegl_op,
GError **error);
static gboolean save_image (GFile *file,
const gchar *gegl_op,
PikaImage *image,
PikaDrawable *drawable,
GError **error);
G_DEFINE_TYPE (Goat, goat, PIKA_TYPE_PLUG_IN)
PIKA_MAIN (GOAT_TYPE)
DEFINE_STD_SET_I18N
static const FileFormat file_formats[] =
{
{
N_("Radiance RGBE"),
"image/vnd.radiance",
"hdr",
"0,string,#?",
"file-load-rgbe",
"Load files in the RGBE file format",
"This procedure loads images in the RGBE format, using gegl:rgbe-load",
"gegl:rgbe-load",
"file-save-rgbe",
"Saves files in the RGBE file format",
"This procedure exports images in the RGBE format, using gegl:rgbe-save",
"gegl:rgbe-save",
},
{
N_("OpenEXR image"),
"image/x-exr",
"exr",
"0,lelong,20000630",
/* no EXR loading (implemented in native PIKA plug-in) */
NULL, NULL, NULL, NULL,
"file-exr-save",
"Saves files in the OpenEXR file format",
"This procedure saves images in the OpenEXR format, using gegl:exr-save",
"gegl:exr-save"
}
};
static void
goat_class_init (GoatClass *klass)
{
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
plug_in_class->query_procedures = goat_query_procedures;
plug_in_class->create_procedure = goat_create_procedure;
plug_in_class->set_i18n = STD_SET_I18N;
}
static void
goat_init (Goat *goat)
{
}
static GList *
goat_query_procedures (PikaPlugIn *plug_in)
{
GList *list = NULL;
gint i;
for (i = 0; i < G_N_ELEMENTS (file_formats); i++)
{
const FileFormat *format = &file_formats[i];
if (format->load_proc)
list = g_list_append (list, g_strdup (format->load_proc));
if (format->save_proc)
list = g_list_append (list, g_strdup (format->save_proc));
}
return list;
}
static PikaProcedure *
goat_create_procedure (PikaPlugIn *plug_in,
const gchar *name)
{
PikaProcedure *procedure = NULL;
gint i;
for (i = 0; i < G_N_ELEMENTS (file_formats); i++)
{
const FileFormat *format = &file_formats[i];
if (! g_strcmp0 (name, format->load_proc))
{
procedure = pika_load_procedure_new (plug_in, name,
PIKA_PDB_PROC_TYPE_PLUGIN,
goat_load,
(gpointer) format, NULL);
pika_procedure_set_menu_label (procedure, format->file_type);
pika_procedure_set_documentation (procedure,
format->load_blurb,
format->load_help,
name);
pika_file_procedure_set_mime_types (PIKA_FILE_PROCEDURE (procedure),
format->mime_type);
pika_file_procedure_set_extensions (PIKA_FILE_PROCEDURE (procedure),
format->extensions);
pika_file_procedure_set_magics (PIKA_FILE_PROCEDURE (procedure),
format->magic);
}
else if (! g_strcmp0 (name, format->save_proc))
{
procedure = pika_save_procedure_new (plug_in, name,
PIKA_PDB_PROC_TYPE_PLUGIN,
goat_save,
(gpointer) format, NULL);
pika_procedure_set_image_types (procedure, "*");
pika_procedure_set_menu_label (procedure, format->file_type);
pika_procedure_set_documentation (procedure,
format->save_blurb,
format->save_help,
name);
pika_file_procedure_set_mime_types (PIKA_FILE_PROCEDURE (procedure),
format->mime_type);
pika_file_procedure_set_extensions (PIKA_FILE_PROCEDURE (procedure),
format->extensions);
}
}
return procedure;
}
static PikaValueArray *
goat_load (PikaProcedure *procedure,
PikaRunMode run_mode,
GFile *file,
const PikaValueArray *args,
gpointer run_data)
{
const FileFormat *format = run_data;
PikaValueArray *return_vals;
PikaImage *image;
GError *error = NULL;
gegl_init (NULL, NULL);
image = load_image (file, format->load_op, &error);
if (! image)
return pika_procedure_new_return_values (procedure,
PIKA_PDB_EXECUTION_ERROR,
error);
return_vals = pika_procedure_new_return_values (procedure,
PIKA_PDB_SUCCESS,
NULL);
PIKA_VALUES_SET_IMAGE (return_vals, 1, image);
return return_vals;
}
static PikaValueArray *
goat_save (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
GFile *file,
const PikaValueArray *args,
gpointer run_data)
{
const FileFormat *format = run_data;
PikaPDBStatusType status = PIKA_PDB_SUCCESS;
PikaExportReturn export = PIKA_EXPORT_CANCEL;
GError *error = NULL;
gegl_init (NULL, NULL);
switch (run_mode)
{
case PIKA_RUN_INTERACTIVE:
case PIKA_RUN_WITH_LAST_VALS:
pika_ui_init (PLUG_IN_BINARY);
export = pika_export_image (&image, &n_drawables, &drawables, "GEGL",
PIKA_EXPORT_CAN_HANDLE_RGB |
PIKA_EXPORT_CAN_HANDLE_GRAY |
PIKA_EXPORT_CAN_HANDLE_INDEXED |
PIKA_EXPORT_CAN_HANDLE_ALPHA);
if (export == PIKA_EXPORT_CANCEL)
return pika_procedure_new_return_values (procedure,
PIKA_PDB_CANCEL,
NULL);
break;
default:
break;
}
if (n_drawables != 1)
{
g_set_error (&error, G_FILE_ERROR, 0,
_("GEGL export plug-in does not support multiple layers."));
return pika_procedure_new_return_values (procedure,
PIKA_PDB_CALLING_ERROR,
error);
}
if (! save_image (file, format->save_op, image, drawables[0],
&error))
{
status = PIKA_PDB_EXECUTION_ERROR;
}
if (export == PIKA_EXPORT_EXPORT)
{
pika_image_delete (image);
g_free (drawables);
}
return pika_procedure_new_return_values (procedure, status, error);
}
static PikaImage *
load_image (GFile *file,
const gchar *gegl_op,
GError **error)
{
PikaImage *image;
PikaLayer *layer;
PikaImageType image_type;
PikaImageBaseType base_type;
PikaPrecision precision;
gint width;
gint height;
GeglNode *graph;
GeglNode *sink;
GeglNode *source;
GeglBuffer *src_buf = NULL;
GeglBuffer *dest_buf = NULL;
const Babl *format;
pika_progress_init_printf (_("Opening '%s'"),
pika_file_get_utf8_name (file));
graph = gegl_node_new ();
source = gegl_node_new_child (graph,
"operation", gegl_op,
"path", g_file_peek_path (file),
NULL);
sink = gegl_node_new_child (graph,
"operation", "gegl:buffer-sink",
"buffer", &src_buf,
NULL);
gegl_node_link (source, sink);
gegl_node_process (sink);
g_object_unref (graph);
if (! src_buf)
{
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
_("Could not open '%s'"),
pika_file_get_utf8_name (file));
return NULL;
}
pika_progress_update (0.33);
width = gegl_buffer_get_width (src_buf);
height = gegl_buffer_get_height (src_buf);
format = gegl_buffer_get_format (src_buf);
if (babl_format_is_palette (format))
{
base_type = PIKA_INDEXED;
if (babl_format_has_alpha (format))
image_type = PIKA_INDEXEDA_IMAGE;
else
image_type = PIKA_INDEXED_IMAGE;
precision = PIKA_PRECISION_U8_NON_LINEAR;
}
else
{
const Babl *model = babl_format_get_model (format);
const Babl *type = babl_format_get_type (format, 0);
gboolean linear = TRUE;
if (model == babl_model ("Y") ||
model == babl_model ("Y'") ||
model == babl_model ("YA") ||
model == babl_model ("Y'A"))
{
base_type = PIKA_GRAY;
if (babl_format_has_alpha (format))
image_type = PIKA_GRAYA_IMAGE;
else
image_type = PIKA_GRAY_IMAGE;
if (model == babl_model ("Y'") ||
model == babl_model ("Y'A"))
linear = FALSE;
}
else
{
base_type = PIKA_RGB;
if (babl_format_has_alpha (format))
image_type = PIKA_RGBA_IMAGE;
else
image_type = PIKA_RGB_IMAGE;
if (model == babl_model ("R'G'B'") ||
model == babl_model ("R'G'B'A"))
linear = FALSE;
}
if (linear)
{
if (type == babl_type ("u8"))
precision = PIKA_PRECISION_U8_LINEAR;
else if (type == babl_type ("u16"))
precision = PIKA_PRECISION_U16_LINEAR;
else if (type == babl_type ("u32"))
precision = PIKA_PRECISION_U32_LINEAR;
else if (type == babl_type ("half"))
precision = PIKA_PRECISION_HALF_LINEAR;
else
precision = PIKA_PRECISION_FLOAT_LINEAR;
}
else
{
if (type == babl_type ("u8"))
precision = PIKA_PRECISION_U8_NON_LINEAR;
else if (type == babl_type ("u16"))
precision = PIKA_PRECISION_U16_NON_LINEAR;
else if (type == babl_type ("u32"))
precision = PIKA_PRECISION_U32_NON_LINEAR;
else if (type == babl_type ("half"))
precision = PIKA_PRECISION_HALF_NON_LINEAR;
else
precision = PIKA_PRECISION_FLOAT_NON_LINEAR;
}
}
image = pika_image_new_with_precision (width, height,
base_type, precision);
layer = pika_layer_new (image,
_("Background"),
width, height,
image_type,
100,
pika_image_get_default_new_layer_mode (image));
pika_image_insert_layer (image, layer, NULL, 0);
dest_buf = pika_drawable_get_buffer (PIKA_DRAWABLE (layer));
pika_progress_update (0.66);
gegl_buffer_copy (src_buf, NULL, GEGL_ABYSS_NONE, dest_buf, NULL);
g_object_unref (src_buf);
g_object_unref (dest_buf);
pika_progress_update (1.0);
return image;
}
static gboolean
save_image (GFile *file,
const gchar *gegl_op,
PikaImage *image,
PikaDrawable *drawable,
GError **error)
{
GeglNode *graph;
GeglNode *source;
GeglNode *sink;
GeglBuffer *src_buf;
src_buf = pika_drawable_get_buffer (drawable);
graph = gegl_node_new ();
source = gegl_node_new_child (graph,
"operation", "gegl:buffer-source",
"buffer", src_buf,
NULL);
sink = gegl_node_new_child (graph,
"operation", gegl_op,
"path", g_file_peek_path (file),
NULL);
gegl_node_link (source, sink);
gegl_node_process (sink);
g_object_unref (graph);
g_object_unref (src_buf);
return TRUE;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

896
plug-ins/common/file-gih.c Normal file
View File

@ -0,0 +1,896 @@
/* 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"
/* Parameters applicable each time we export a gih, exported in the
* main pika application between invocations of this plug-in.
*/
typedef struct
{
gchar description[256];
gint spacing;
} BrushInfo;
typedef struct
{
PikaOrientationType orientation;
PikaImage *image;
PikaLayer *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 */
GtkAdjustment *ncells;
GtkAdjustment *rank0;
GtkWidget *warning_label;
GtkWidget *rank_entry[PIKA_PIXPIPE_MAXDIM];
GtkWidget *mode_entry[PIKA_PIXPIPE_MAXDIM];
} 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 ())
#define GIH (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIH_TYPE, Gih))
GType gih_get_type (void) G_GNUC_CONST;
static GList * gih_query_procedures (PikaPlugIn *plug_in);
static PikaProcedure * gih_create_procedure (PikaPlugIn *plug_in,
const gchar *name);
static PikaValueArray * gih_save (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
GFile *file,
const PikaValueArray *args,
gpointer run_data);
static gboolean gih_save_dialog (PikaImage *image);
G_DEFINE_TYPE (Gih, gih, PIKA_TYPE_PLUG_IN)
PIKA_MAIN (GIH_TYPE)
DEFINE_STD_SET_I18N
static BrushInfo info =
{
"PIKA Brush Pipe",
20
};
static gint num_layers = 0;
static PikaPixPipeParams gihparams = { 0, };
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,
gih_save, NULL, NULL);
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");
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",
"Spacing",
"Spacing of the brush",
1, 1000, 10,
PIKA_PARAM_READWRITE);
PIKA_PROC_ARG_STRING (procedure, "description",
"Description",
"Short description of the gihtern",
"PIKA Gihtern",
PIKA_PARAM_READWRITE);
PIKA_PROC_ARG_INT (procedure, "cell-width",
"Cell width",
"Width of the brush cells",
1, 1000, 10,
PIKA_PARAM_READWRITE);
PIKA_PROC_ARG_INT (procedure, "cell-height",
"Cell height",
"Height of the brush cells",
1, 1000, 10,
PIKA_PARAM_READWRITE);
PIKA_PROC_ARG_INT (procedure, "display-cols",
"Display columns",
"Display column number",
1, 1000, 1,
PIKA_PARAM_READWRITE);
PIKA_PROC_ARG_INT (procedure, "display-rows",
"Display rows",
"Display row number",
1, 1000, 1,
PIKA_PARAM_READWRITE);
PIKA_PROC_ARG_BYTES (procedure, "rank",
"Rank",
"Ranks of the dimensions",
PIKA_PARAM_READWRITE);
PIKA_PROC_ARG_INT (procedure, "dimension-2",
"Dimension 2",
"Dimension of the brush pipe (same as dimension)",
1, 4, 1,
PIKA_PARAM_READWRITE);
PIKA_PROC_ARG_STRV (procedure, "sel",
"Sel",
"Selection modes",
PIKA_PARAM_READWRITE);
}
return procedure;
}
static PikaValueArray *
gih_save (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
GFile *file,
const PikaValueArray *args,
gpointer run_data)
{
PikaPDBStatusType status = PIKA_PDB_SUCCESS;
PikaExportReturn export = PIKA_EXPORT_CANCEL;
PikaParasite *parasite;
PikaImage *orig_image;
GError *error = NULL;
gint i;
GBytes *rank_bytes;
orig_image = image;
switch (run_mode)
{
case PIKA_RUN_INTERACTIVE:
case PIKA_RUN_WITH_LAST_VALS:
pika_ui_init (PLUG_IN_BINARY);
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 */
pika_get_data (SAVE_PROC, &info);
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, &parasite_size);
g_strlcpy (info.description, parasite_data,
MIN (sizeof (info.description), parasite_size));
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))
g_strlcpy (info.description, name, sizeof (info.description));
g_free (name);
}
parasite = pika_image_get_parasite (orig_image,
"pika-brush-pipe-spacing");
if (parasite)
{
gchar *parasite_data;
guint32 parasite_size;
parasite_data = (gchar *) pika_parasite_get_data (parasite, &parasite_size);
parasite_data = g_strndup (parasite_data, parasite_size);
info.spacing = atoi (parasite_data);
pika_parasite_free (parasite);
g_free (parasite_data);
}
break;
default:
break;
}
g_free (pika_image_get_layers (image, &num_layers));
pika_pixpipe_params_init (&gihparams);
switch (run_mode)
{
case PIKA_RUN_INTERACTIVE:
gihparams.ncells = (num_layers * gihparams.rows * gihparams.cols);
gihparams.cellwidth = pika_image_get_width (image) / gihparams.cols;
gihparams.cellheight = pika_image_get_height (image) / gihparams.rows;
parasite = pika_image_get_parasite (orig_image,
"pika-brush-pipe-parameters");
if (parasite)
{
gchar *parasite_data;
guint32 parasite_size;
parasite_data = (gchar *) pika_parasite_get_data (parasite, &parasite_size);
parasite_data = g_strndup (parasite_data, parasite_size);
pika_pixpipe_params_parse (parasite_data, &gihparams);
pika_parasite_free (parasite);
g_free (parasite_data);
}
/* Force default rank to same as number of cells if there is
* just one dim
*/
if (gihparams.dim == 1)
gihparams.rank[0] = gihparams.ncells;
if (! gih_save_dialog (image))
{
status = PIKA_PDB_CANCEL;
goto out;
}
break;
case PIKA_RUN_NONINTERACTIVE:
info.spacing = PIKA_VALUES_GET_INT (args, 0);
g_strlcpy (info.description,
PIKA_VALUES_GET_STRING (args, 1),
sizeof (info.description));
gihparams.cellwidth = PIKA_VALUES_GET_INT (args, 2);
gihparams.cellheight = PIKA_VALUES_GET_INT (args, 3);
gihparams.cols = PIKA_VALUES_GET_INT (args, 4);
gihparams.rows = PIKA_VALUES_GET_INT (args, 5);
rank_bytes = PIKA_VALUES_GET_BYTES (args, 6);
gihparams.dim = g_bytes_get_size (rank_bytes);
gihparams.ncells = 1;
if (PIKA_VALUES_GET_INT (args, 7) != gihparams.dim)
{
status = PIKA_PDB_CALLING_ERROR;
}
else
{
const guint8 *rank = g_bytes_get_data (rank_bytes, NULL);
const gchar **sel = PIKA_VALUES_GET_STRV (args, 8);
for (i = 0; i < gihparams.dim; i++)
{
gihparams.rank[i] = rank[i];
gihparams.selection[i] = g_strdup (sel[i]);
gihparams.ncells *= gihparams.rank[i];
}
}
break;
case PIKA_RUN_WITH_LAST_VALS:
parasite = pika_image_get_parasite (orig_image,
"pika-brush-pipe-parameters");
if (parasite)
{
gchar *parasite_data;
guint32 parasite_size;
parasite_data = (gchar *) pika_parasite_get_data (parasite, &parasite_size);
parasite_data = g_strndup (parasite_data, parasite_size);
pika_pixpipe_params_parse (parasite_data, &gihparams);
pika_parasite_free (parasite);
g_free (parasite_data);
}
break;
}
if (status == PIKA_PDB_SUCCESS)
{
PikaValueArray *save_retvals;
gchar spacing[8];
gchar *paramstring;
PikaValueArray *args;
paramstring = pika_pixpipe_params_build (&gihparams);
args = pika_value_array_new_from_types (NULL,
PIKA_TYPE_RUN_MODE, PIKA_RUN_NONINTERACTIVE,
PIKA_TYPE_IMAGE, image,
G_TYPE_INT, n_drawables,
PIKA_TYPE_OBJECT_ARRAY, NULL,
G_TYPE_FILE, file,
G_TYPE_INT, info.spacing,
G_TYPE_STRING, info.description,
G_TYPE_STRING, paramstring,
G_TYPE_NONE);
pika_value_set_object_array (pika_value_array_index (args, 3),
PIKA_TYPE_ITEM, (GObject **) drawables, n_drawables);
save_retvals = pika_pdb_run_procedure_array (pika_get_pdb (),
"file-gih-save-internal",
args);
pika_value_array_unref (args);
if (PIKA_VALUES_GET_ENUM (save_retvals, 0) == PIKA_PDB_SUCCESS)
{
pika_set_data (SAVE_PROC, &info, sizeof (info));
parasite = pika_parasite_new ("pika-brush-pipe-name",
PIKA_PARASITE_PERSISTENT,
strlen (info.description) + 1,
info.description);
pika_image_attach_parasite (orig_image, parasite);
pika_parasite_free (parasite);
g_snprintf (spacing, sizeof (spacing), "%d",
info.spacing);
parasite = pika_parasite_new ("pika-brush-pipe-spacing",
PIKA_PARASITE_PERSISTENT,
strlen (spacing) + 1, spacing);
pika_image_attach_parasite (orig_image, parasite);
pika_parasite_free (parasite);
parasite = pika_parasite_new ("pika-brush-pipe-parameters",
PIKA_PARASITE_PERSISTENT,
strlen (paramstring) + 1,
paramstring);
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);
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
size_adjustment_callback (GtkAdjustment *adjustment,
SizeAdjustmentData *adj)
{
gint i;
gint size;
gint newn;
gchar buf[10];
for (i = 0; i < adj->nguides; i++)
pika_image_delete_guide (adj->image, adj->guides[i]);
g_free (adj->guides);
adj->guides = NULL;
pika_displays_flush ();
*(adj->value) = gtk_adjustment_get_value (adjustment);
if (adj->orientation == PIKA_ORIENTATION_VERTICAL)
{
size = pika_image_get_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] = pika_image_add_vguide (adj->image,
*(adj->value) * (i+1));
}
else
{
size = pika_image_get_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] = pika_image_add_hguide (adj->image,
*(adj->value) * (i+1));
}
pika_displays_flush ();
g_snprintf (buf, sizeof (buf), "%2d", newn);
gtk_label_set_text (GTK_LABEL (adj->count_label), buf);
*(adj->count) = newn;
gtk_widget_set_visible (GTK_WIDGET (adj->warning_label),
newn * *(adj->value) != size);
if (adj->ncells != NULL)
gtk_adjustment_set_value (adj->ncells,
*(adj->other_count) * *(adj->count));
if (adj->rank0 != NULL)
gtk_adjustment_set_value (adj->rank0,
*(adj->other_count) * *(adj->count));
}
static void
entry_callback (GtkWidget *widget,
gpointer data)
{
if (data == info.description)
g_strlcpy (info.description, gtk_entry_get_text (GTK_ENTRY (widget)),
sizeof (info.description));
}
static void
cb_callback (GtkWidget *widget,
gpointer data)
{
gint index;
index = gtk_combo_box_get_active (GTK_COMBO_BOX (widget));
*((const gchar **) data) = selection_modes [index];
}
static void
dim_callback (GtkAdjustment *adjustment,
SizeAdjustmentData *data)
{
gint i;
gihparams.dim = RINT (gtk_adjustment_get_value (adjustment));
for (i = 0; i < PIKA_PIXPIPE_MAXDIM; i++)
{
gtk_widget_set_sensitive (data->rank_entry[i], i < gihparams.dim);
gtk_widget_set_sensitive (data->mode_entry[i], i < gihparams.dim);
}
}
static gboolean
gih_save_dialog (PikaImage *image)
{
GtkWidget *dialog;
GtkWidget *grid;
GtkWidget *dimgrid;
GtkWidget *label;
GtkAdjustment *adjustment;
GtkWidget *spinbutton;
GtkWidget *entry;
GtkWidget *box;
GtkWidget *cb;
gint i;
gchar buffer[100];
SizeAdjustmentData cellw_adjust;
SizeAdjustmentData cellh_adjust;
GList *layers;
gboolean run;
dialog = pika_export_dialog_new (_("Brush Pipe"), PLUG_IN_BINARY, SAVE_PROC);
/* The main grid */
grid = gtk_grid_new ();
gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
gtk_grid_set_column_spacing (GTK_GRID (grid), 6);
gtk_container_set_border_width (GTK_CONTAINER (grid), 12);
gtk_box_pack_start (GTK_BOX (pika_export_dialog_get_content_area (dialog)),
grid, TRUE, TRUE, 0);
gtk_widget_show (grid);
/*
* Description: ___________
*/
entry = gtk_entry_new ();
gtk_widget_set_size_request (entry, 200, -1);
gtk_entry_set_text (GTK_ENTRY (entry), info.description);
pika_grid_attach_aligned (GTK_GRID (grid), 0, 0,
_("_Description:"), 0.0, 0.5,
entry, 1);
g_signal_connect (entry, "changed",
G_CALLBACK (entry_callback),
info.description);
/*
* Spacing: __
*/
adjustment = gtk_adjustment_new (info.spacing, 1, 1000, 1, 10, 0);
spinbutton = pika_spin_button_new (adjustment, 1.0, 0);
gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
pika_grid_attach_aligned (GTK_GRID (grid), 0, 1,
_("_Spacing (percent):"), 0.0, 0.5,
spinbutton, 1);
g_signal_connect (adjustment, "value-changed",
G_CALLBACK (pika_int_adjustment_update),
&info.spacing);
/*
* Cell size: __ x __ pixels
*/
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
adjustment = gtk_adjustment_new (gihparams.cellwidth,
2, pika_image_get_width (image), 1, 10, 0);
spinbutton = pika_spin_button_new (adjustment, 1.0, 0);
gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
gtk_box_pack_start (GTK_BOX (box), spinbutton, FALSE, FALSE, 0);
gtk_widget_show (spinbutton);
layers = pika_image_list_layers (image);
cellw_adjust.orientation = PIKA_ORIENTATION_VERTICAL;
cellw_adjust.image = image;
cellw_adjust.toplayer = g_list_last (layers)->data;
cellw_adjust.nguides = 0;
cellw_adjust.guides = NULL;
cellw_adjust.value = &gihparams.cellwidth;
g_signal_connect (adjustment, "value-changed",
G_CALLBACK (size_adjustment_callback),
&cellw_adjust);
label = gtk_label_new ("x");
gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0);
gtk_widget_show (label);
adjustment = gtk_adjustment_new (gihparams.cellheight,
2, pika_image_get_height (image), 1, 10, 0);
spinbutton = pika_spin_button_new (adjustment, 1.0, 0);
gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
gtk_box_pack_start (GTK_BOX (box), spinbutton, FALSE, FALSE, 0);
gtk_widget_show (spinbutton);
cellh_adjust.orientation = PIKA_ORIENTATION_HORIZONTAL;
cellh_adjust.image = image;
cellh_adjust.toplayer = g_list_last (layers)->data;
cellh_adjust.nguides = 0;
cellh_adjust.guides = NULL;
cellh_adjust.value = &gihparams.cellheight;
g_signal_connect (adjustment, "value-changed",
G_CALLBACK (size_adjustment_callback),
&cellh_adjust);
label = gtk_label_new ( _("Pixels"));
gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0);
gtk_widget_show (label);
pika_grid_attach_aligned (GTK_GRID (grid), 0, 2,
_("Ce_ll size:"), 0.0, 0.5,
box, 1);
g_list_free (layers);
/*
* Number of cells: ___
*/
adjustment = gtk_adjustment_new (gihparams.ncells, 1, 1000, 1, 10, 0);
spinbutton = pika_spin_button_new (adjustment, 1.0, 0);
gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
pika_grid_attach_aligned (GTK_GRID (grid), 0, 3,
_("_Number of cells:"), 0.0, 0.5,
spinbutton, 1);
g_signal_connect (adjustment, "value-changed",
G_CALLBACK (pika_int_adjustment_update),
&gihparams.ncells);
if (gihparams.dim == 1)
cellw_adjust.ncells = cellh_adjust.ncells = adjustment;
else
cellw_adjust.ncells = cellh_adjust.ncells = NULL;
/*
* Display as: __ rows x __ cols
*/
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
g_snprintf (buffer, sizeof (buffer), "%2d", gihparams.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 = &gihparams.rows;
cellh_adjust.other_count = &gihparams.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);
g_snprintf (buffer, sizeof (buffer), "%2d", gihparams.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 = &gihparams.cols;
cellw_adjust.other_count = &gihparams.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;
pika_grid_attach_aligned (GTK_GRID (grid), 0, 4,
_("Display as:"), 0.0, 0.5,
box, 1);
/*
* Dimension: ___
*/
adjustment = gtk_adjustment_new (gihparams.dim,
1, PIKA_PIXPIPE_MAXDIM, 1, 1, 0);
spinbutton = pika_spin_button_new (adjustment, 1.0, 0);
gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
pika_grid_attach_aligned (GTK_GRID (grid), 0, 5,
_("Di_mension:"), 0.0, 0.5,
spinbutton, 1);
g_signal_connect (adjustment, "value-changed",
G_CALLBACK (dim_callback),
&cellw_adjust);
/*
* Ranks / Selection: ______ ______ ______ ______ ______
*/
dimgrid = gtk_grid_new ();
gtk_grid_set_column_spacing (GTK_GRID (dimgrid), 4);
for (i = 0; i < PIKA_PIXPIPE_MAXDIM; i++)
{
gsize j;
adjustment = gtk_adjustment_new (gihparams.rank[i], 1, 100, 1, 1, 0);
spinbutton = pika_spin_button_new (adjustment, 1.0, 0);
gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
gtk_grid_attach (GTK_GRID (dimgrid), spinbutton, 0, i, 1, 1);
gtk_widget_show (spinbutton);
if (i >= gihparams.dim)
gtk_widget_set_sensitive (spinbutton, FALSE);
g_signal_connect (adjustment, "value-changed",
G_CALLBACK (pika_int_adjustment_update),
&gihparams.rank[i]);
cellw_adjust.rank_entry[i] = cellh_adjust.rank_entry[i] = spinbutton;
if (i == 0)
{
if (gihparams.dim == 1)
cellw_adjust.rank0 = cellh_adjust.rank0 = adjustment;
else
cellw_adjust.rank0 = cellh_adjust.rank0 = NULL;
}
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 */
if (gihparams.selection[i])
for (j = 0; j < G_N_ELEMENTS (selection_modes); j++)
if (!strcmp (gihparams.selection[i], selection_modes[j]))
{
gtk_combo_box_set_active (GTK_COMBO_BOX (cb), j);
break;
}
gtk_grid_attach (GTK_GRID (dimgrid), cb, 1, i, 1, 1);
gtk_widget_show (cb);
if (i >= gihparams.dim)
gtk_widget_set_sensitive (cb, FALSE);
g_signal_connect (GTK_COMBO_BOX (cb), "changed",
G_CALLBACK (cb_callback),
&gihparams.selection[i]);
cellw_adjust.mode_entry[i] = cellh_adjust.mode_entry[i] = cb;
}
pika_grid_attach_aligned (GTK_GRID (grid), 0, 6,
_("Ranks:"), 0.0, 0.0,
dimgrid, 1);
gtk_widget_show (dialog);
run = (pika_dialog_run (PIKA_DIALOG (dialog)) == GTK_RESPONSE_OK);
if (run)
{
gint i;
for (i = 0; i < PIKA_PIXPIPE_MAXDIM; i++)
gihparams.selection[i] = g_strdup (gihparams.selection[i]);
/* Fix up bogus values */
gihparams.ncells = MIN (gihparams.ncells,
num_layers * gihparams.rows * gihparams.cols);
}
gtk_widget_destroy (dialog);
for (i = 0; i < cellw_adjust.nguides; i++)
pika_image_delete_guide (image, cellw_adjust.guides[i]);
for (i = 0; i < cellh_adjust.nguides; i++)
pika_image_delete_guide (image, cellh_adjust.guides[i]);
return run;
}

488
plug-ins/common/file-glob.c Normal file
View File

@ -0,0 +1,488 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* 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/>.
*/
/* The idea is taken from a plug-in written by George Hartz; the code isn't.
*/
#include "config.h"
#include <string.h>
#include "libpika/pika.h"
#define PLUG_IN_PROC "file-glob"
typedef struct _Glob Glob;
typedef struct _GlobClass GlobClass;
struct _Glob
{
PikaPlugIn parent_instance;
};
struct _GlobClass
{
PikaPlugInClass parent_class;
};
#define GLOB_TYPE (glob_get_type ())
#define GLOB (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GLOB_TYPE, Glob))
GType glob_get_type (void) G_GNUC_CONST;
static GList * glob_query_procedures (PikaPlugIn *plug_in);
static PikaProcedure * glob_create_procedure (PikaPlugIn *plug_in,
const gchar *name);
static PikaValueArray * glob_run (PikaProcedure *procedure,
const PikaValueArray *args,
gpointer run_data);
static gboolean glob_match (const gchar *pattern,
gboolean filename_encoding,
gchar ***matches);
static gboolean glob_fnmatch (const gchar *pattern,
const gchar *string);
G_DEFINE_TYPE (Glob, glob, PIKA_TYPE_PLUG_IN)
PIKA_MAIN (GLOB_TYPE)
static void
glob_class_init (GlobClass *klass)
{
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
plug_in_class->query_procedures = glob_query_procedures;
plug_in_class->create_procedure = glob_create_procedure;
plug_in_class->set_i18n = NULL;
}
static void
glob_init (Glob *glob)
{
}
static GList *
glob_query_procedures (PikaPlugIn *plug_in)
{
return g_list_append (NULL, g_strdup (PLUG_IN_PROC));
}
static PikaProcedure *
glob_create_procedure (PikaPlugIn *plug_in,
const gchar *name)
{
PikaProcedure *procedure = NULL;
if (! strcmp (name, PLUG_IN_PROC))
{
procedure = pika_procedure_new (plug_in, name,
PIKA_PDB_PROC_TYPE_PLUGIN,
glob_run, NULL, NULL);
pika_procedure_set_documentation (procedure,
"Returns a list of matching filenames",
"This can be useful in scripts and "
"other plug-ins (e.g., "
"batch-conversion). See the glob(7) "
"manpage for more info. Note however "
"that this isn't a full-featured glob "
"implementation. It only handles "
"simple patterns like "
"\"/home/foo/bar/*.jpg\".",
name);
pika_procedure_set_attribution (procedure,
"Sven Neumann",
"Sven Neumann",
"2004");
PIKA_PROC_ARG_STRING (procedure, "pattern",
"Pattern",
"The glob pattern (in UTF-8 encoding)",
NULL,
G_PARAM_READWRITE);
PIKA_PROC_ARG_BOOLEAN (procedure, "filename-encoding",
"Filename encoding",
"FALSE to return UTF-8 strings, TRUE to return "
"strings in filename encoding",
FALSE,
G_PARAM_READWRITE);
PIKA_PROC_VAL_STRV (procedure, "files",
"Files",
"The list of matching filenames",
G_PARAM_READWRITE |
PIKA_PARAM_NO_VALIDATE);
}
return procedure;
}
static PikaValueArray *
glob_run (PikaProcedure *procedure,
const PikaValueArray *args,
gpointer run_data)
{
PikaValueArray *return_vals;
const gchar *pattern;
gboolean filename_encoding;
gchar **matches;
pattern = PIKA_VALUES_GET_STRING (args, 0);
filename_encoding = PIKA_VALUES_GET_BOOLEAN (args, 1);
if (! glob_match (pattern, filename_encoding, &matches))
{
return pika_procedure_new_return_values (procedure,
PIKA_PDB_EXECUTION_ERROR,
NULL);
}
return_vals = pika_procedure_new_return_values (procedure,
PIKA_PDB_SUCCESS,
NULL);
PIKA_VALUES_TAKE_STRV (return_vals, 1, matches);
return return_vals;
}
static gboolean
glob_match (const gchar *pattern,
gboolean filename_encoding,
gchar ***matches)
{
GDir *dir;
GPtrArray *array;
const gchar *filename;
gchar *dirname;
gchar *tmp;
g_return_val_if_fail (pattern != NULL, FALSE);
g_return_val_if_fail (matches != NULL, FALSE);
*matches = NULL;
/* This is not a complete glob() implementation but rather a very
* simplistic approach. However it works for the most common use
* case and is better than nothing.
*/
tmp = g_filename_from_utf8 (pattern, -1, NULL, NULL, NULL);
if (! tmp)
return FALSE;
dirname = g_path_get_dirname (tmp);
dir = g_dir_open (dirname, 0, NULL);
g_free (tmp);
if (! dir)
{
g_free (dirname);
return TRUE;
}
/* check if the pattern has a directory part at all */
tmp = g_path_get_basename (pattern);
if (strcmp (pattern, tmp) == 0)
{
g_free (dirname);
dirname = NULL;
}
g_free (tmp);
array = g_ptr_array_new ();
for (filename = g_dir_read_name (dir);
filename;
filename = g_dir_read_name (dir))
{
gchar *path;
gchar *name;
if (dirname)
path = g_build_filename (dirname, filename, NULL);
else
path = g_strdup (filename);
name = g_filename_to_utf8 (path, -1, NULL, NULL, NULL);
if (name && glob_fnmatch (pattern, name))
{
if (filename_encoding)
{
g_ptr_array_add (array, path);
path = NULL;
}
else
{
g_ptr_array_add (array, name);
name = NULL;
}
}
g_free (path);
g_free (name);
}
g_dir_close (dir);
g_free (dirname);
/* NULL-terminator */
g_ptr_array_add (array, NULL);
*matches = (gchar **) g_ptr_array_free (array, FALSE);
return TRUE;
}
/*
* The following code is borrowed from GTK+.
*
* GTK+ used to use a old version of GNU fnmatch() that was buggy
* in various ways and didn't handle UTF-8. The following is
* converted to UTF-8. To simplify the process of making it
* correct, this is special-cased to the combinations of flags
* that gtkfilesel.c uses.
*
* FNM_FILE_NAME - always set
* FNM_LEADING_DIR - never set
* FNM_NOESCAPE - set only on windows
* FNM_CASEFOLD - set only on windows
*/
/* We need to make sure that all constants are defined
* to properly compile this file
*/
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
static gunichar
get_char (const char **str)
{
gunichar c = g_utf8_get_char (*str);
*str = g_utf8_next_char (*str);
#ifdef G_PLATFORM_WIN32
c = g_unichar_tolower (c);
#endif
return c;
}
#if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN)
#define DO_ESCAPE 0
#else
#define DO_ESCAPE 1
#endif
static gunichar
get_unescaped_char (const char **str,
gboolean *was_escaped)
{
gunichar c = get_char (str);
*was_escaped = DO_ESCAPE && c == '\\';
if (*was_escaped)
c = get_char (str);
return c;
}
/* Match STRING against the filename pattern PATTERN,
* returning TRUE if it matches, FALSE otherwise.
*/
static gboolean
fnmatch_intern (const gchar *pattern,
const gchar *string,
gboolean component_start,
gboolean no_leading_period)
{
const char *p = pattern, *n = string;
while (*p)
{
const char *last_n = n;
gunichar c = get_char (&p);
gunichar nc = get_char (&n);
switch (c)
{
case '?':
if (nc == '\0')
return FALSE;
else if (nc == G_DIR_SEPARATOR)
return FALSE;
else if (nc == '.' && component_start && no_leading_period)
return FALSE;
break;
case '\\':
if (DO_ESCAPE)
c = get_char (&p);
if (nc != c)
return FALSE;
break;
case '*':
if (nc == '.' && component_start && no_leading_period)
return FALSE;
{
const char *last_p = p;
for (last_p = p, c = get_char (&p);
c == '?' || c == '*';
last_p = p, c = get_char (&p))
{
if (c == '?')
{
if (nc == '\0')
return FALSE;
else if (nc == G_DIR_SEPARATOR)
return FALSE;
else
{
last_n = n; nc = get_char (&n);
}
}
}
/* If the pattern ends with wildcards, we have a
* guaranteed match unless there is a dir separator
* in the remainder of the string.
*/
if (c == '\0')
{
if (strchr (last_n, G_DIR_SEPARATOR) != NULL)
return FALSE;
else
return TRUE;
}
if (DO_ESCAPE && c == '\\')
c = get_char (&p);
for (p = last_p; nc != '\0';)
{
if ((c == '[' || nc == c) &&
fnmatch_intern (p, last_n,
component_start, no_leading_period))
return TRUE;
component_start = (nc == G_DIR_SEPARATOR);
last_n = n;
nc = get_char (&n);
}
return FALSE;
}
case '[':
{
/* Nonzero if the sense of the character class is inverted. */
gboolean not;
gboolean was_escaped;
if (nc == '\0' || nc == G_DIR_SEPARATOR)
return FALSE;
if (nc == '.' && component_start && no_leading_period)
return FALSE;
not = (*p == '!' || *p == '^');
if (not)
++p;
c = get_unescaped_char (&p, &was_escaped);
for (;;)
{
register gunichar cstart = c, cend = c;
if (c == '\0')
/* [ (unterminated) loses. */
return FALSE;
c = get_unescaped_char (&p, &was_escaped);
if (!was_escaped && c == '-' && *p != ']')
{
cend = get_unescaped_char (&p, &was_escaped);
if (cend == '\0')
return FALSE;
c = get_char (&p);
}
if (nc >= cstart && nc <= cend)
goto matched;
if (!was_escaped && c == ']')
break;
}
if (!not)
return FALSE;
break;
matched:;
/* Skip the rest of the [...] that already matched. */
/* XXX 1003.2d11 is unclear if was_escaped is right. */
while (was_escaped || c != ']')
{
if (c == '\0')
/* [... (unterminated) loses. */
return FALSE;
c = get_unescaped_char (&p, &was_escaped);
}
if (not)
return FALSE;
}
break;
default:
if (c != nc)
return FALSE;
}
component_start = (nc == G_DIR_SEPARATOR);
}
if (*n == '\0')
return TRUE;
return FALSE;
}
static gboolean
glob_fnmatch (const gchar *pattern,
const gchar *string)
{
return fnmatch_intern (pattern, string, TRUE, TRUE);
}

View File

@ -0,0 +1,474 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* 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/>.
*/
#include "config.h"
#include <string.h>
#include <libpika/pika.h>
#include <libpika/pikaui.h>
#include "libpika/stdplugins-intl.h"
#define SAVE_PROC "file-header-save"
#define PLUG_IN_BINARY "file-header"
#define PLUG_IN_ROLE "pika-file-header"
typedef struct _Header Header;
typedef struct _HeaderClass HeaderClass;
struct _Header
{
PikaPlugIn parent_instance;
};
struct _HeaderClass
{
PikaPlugInClass parent_class;
};
#define HEADER_TYPE (header_get_type ())
#define HEADER (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), HEADER_TYPE, Header))
GType header_get_type (void) G_GNUC_CONST;
static GList * header_query_procedures (PikaPlugIn *plug_in);
static PikaProcedure * header_create_procedure (PikaPlugIn *plug_in,
const gchar *name);
static PikaValueArray * header_save (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
GFile *file,
const PikaValueArray *args,
gpointer run_data);
static gboolean save_image (GFile *file,
PikaImage *image,
PikaDrawable *drawable,
GError **error);
static gboolean print (GOutputStream *output,
GError **error,
const gchar *format,
...) G_GNUC_PRINTF (3, 4);
G_DEFINE_TYPE (Header, header, PIKA_TYPE_PLUG_IN)
PIKA_MAIN (HEADER_TYPE)
DEFINE_STD_SET_I18N
static void
header_class_init (HeaderClass *klass)
{
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
plug_in_class->query_procedures = header_query_procedures;
plug_in_class->create_procedure = header_create_procedure;
plug_in_class->set_i18n = STD_SET_I18N;
}
static void
header_init (Header *header)
{
}
static GList *
header_query_procedures (PikaPlugIn *plug_in)
{
return g_list_append (NULL, g_strdup (SAVE_PROC));
}
static PikaProcedure *
header_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,
header_save, NULL, NULL);
pika_procedure_set_image_types (procedure, "INDEXED, RGB");
pika_procedure_set_menu_label (procedure, _("C source code header"));
pika_procedure_set_documentation (procedure,
"saves files as C unsigned character "
"array",
"FIXME: write help",
name);
pika_procedure_set_attribution (procedure,
"Spencer Kimball & Peter Mattis",
"Spencer Kimball & Peter Mattis",
"1997");
pika_file_procedure_set_handles_remote (PIKA_FILE_PROCEDURE (procedure),
TRUE);
pika_file_procedure_set_mime_types (PIKA_FILE_PROCEDURE (procedure),
"image/x-chdr");
pika_file_procedure_set_extensions (PIKA_FILE_PROCEDURE (procedure),
"h");
}
return procedure;
}
static PikaValueArray *
header_save (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
GFile *file,
const PikaValueArray *args,
gpointer run_data)
{
PikaPDBStatusType status = PIKA_PDB_SUCCESS;
PikaExportReturn export = PIKA_EXPORT_CANCEL;
GError *error = NULL;
gegl_init (NULL, NULL);
switch (run_mode)
{
case PIKA_RUN_INTERACTIVE:
case PIKA_RUN_WITH_LAST_VALS:
pika_ui_init (PLUG_IN_BINARY);
export = pika_export_image (&image, &n_drawables, &drawables, "Header",
PIKA_EXPORT_CAN_HANDLE_RGB |
PIKA_EXPORT_CAN_HANDLE_INDEXED);
if (export == PIKA_EXPORT_CANCEL)
return pika_procedure_new_return_values (procedure,
PIKA_PDB_CANCEL,
NULL);
break;
default:
break;
}
if (n_drawables != 1)
{
g_set_error (&error, G_FILE_ERROR, 0,
_("Header plug-in does not support multiple layers."));
return pika_procedure_new_return_values (procedure,
PIKA_PDB_CALLING_ERROR,
error);
}
if (! save_image (file, image, drawables[0],
&error))
{
status = PIKA_PDB_EXECUTION_ERROR;
}
if (export == PIKA_EXPORT_EXPORT)
{
pika_image_delete (image);
g_free (drawables);
}
return pika_procedure_new_return_values (procedure, status, error);
}
static gboolean
save_image (GFile *file,
PikaImage *image,
PikaDrawable *drawable,
GError **error)
{
GeglBuffer *buffer;
const Babl *format;
PikaImageType drawable_type;
GOutputStream *output;
gint x, y, b, c;
const gchar *backslash = "\\\\";
const gchar *quote = "\\\"";
const gchar *newline = "\"\n\t\"";
gchar buf[4];
guchar *d = NULL;
guchar *data = NULL;
guchar *cmap;
GCancellable *cancellable;
gint colors;
gint width;
gint height;
output = G_OUTPUT_STREAM (g_file_replace (file,
NULL, FALSE, G_FILE_CREATE_NONE,
NULL, error));
if (output)
{
GOutputStream *buffered;
buffered = g_buffered_output_stream_new (output);
g_object_unref (output);
output = buffered;
}
else
{
return FALSE;
}
buffer = pika_drawable_get_buffer (drawable);
width = gegl_buffer_get_width (buffer);
height = gegl_buffer_get_height (buffer);
drawable_type = pika_drawable_type (drawable);
if (! print (output, error,
"/* PIKA header image file format (%s): %s */\n\n",
PIKA_RGB_IMAGE == drawable_type ? "RGB" : "INDEXED",
pika_file_get_utf8_name (file)) ||
! print (output, error,
"static unsigned int width = %d;\n", width) ||
! print (output, error,
"static unsigned int height = %d;\n\n", height) ||
! print (output, error,
"/* Call this macro repeatedly. After each use, the pixel data can be extracted */\n\n"))
{
goto fail;
}
switch (drawable_type)
{
case PIKA_RGB_IMAGE:
if (! print (output, error,
"#define HEADER_PIXEL(data,pixel) {\\\n"
"pixel[0] = (((data[0] - 33) << 2) | ((data[1] - 33) >> 4)); \\\n"
"pixel[1] = ((((data[1] - 33) & 0xF) << 4) | ((data[2] - 33) >> 2)); \\\n"
"pixel[2] = ((((data[2] - 33) & 0x3) << 6) | ((data[3] - 33))); \\\n"
"data += 4; \\\n}\n") ||
! print (output, error,
"static char *header_data =\n\t\""))
{
goto fail;
}
format = babl_format ("R'G'B' u8");
data = g_new (guchar, width * babl_format_get_bytes_per_pixel (format));
c = 0;
for (y = 0; y < height; y++)
{
gegl_buffer_get (buffer, GEGL_RECTANGLE (0, y, width, 1), 1.0,
format, data,
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
for (x = 0; x < width; x++)
{
d = data + x * babl_format_get_bytes_per_pixel (format);
buf[0] = ((d[0] >> 2) & 0x3F) + 33;
buf[1] = ((((d[0] & 0x3) << 4) | (d[1] >> 4)) & 0x3F) + 33;
buf[2] = ((((d[1] & 0xF) << 2) | (d[2] >> 6)) & 0x3F) + 33;
buf[3] = (d[2] & 0x3F) + 33;
for (b = 0; b < 4; b++)
{
if (buf[b] == '"')
{
if (! print (output, error, "%s", quote))
goto fail;
}
else if (buf[b] == '\\')
{
if (! print (output, error, "%s", backslash))
goto fail;
}
else
{
if (! print (output, error, "%c", buf[b]))
goto fail;
}
}
c++;
if (c >= 16)
{
if (! print (output, error, "%s", newline))
goto fail;
c = 0;
}
}
}
if (! print (output, error, "\";\n"))
goto fail;
break;
case PIKA_INDEXED_IMAGE:
if (! print (output, error,
"#define HEADER_PIXEL(data,pixel) {\\\n"
"pixel[0] = header_data_cmap[(unsigned char)data[0]][0]; \\\n"
"pixel[1] = header_data_cmap[(unsigned char)data[0]][1]; \\\n"
"pixel[2] = header_data_cmap[(unsigned char)data[0]][2]; \\\n"
"data ++; }\n\n"))
{
goto fail;
}
/* save colormap */
cmap = pika_image_get_colormap (image, NULL, &colors);
if (! print (output, error,
"static unsigned char header_data_cmap[256][3] = {") ||
! print (output, error,
"\n\t{%3d,%3d,%3d}",
(gint) cmap[0], (gint) cmap[1], (gint) cmap[2]))
{
goto fail;
}
for (c = 1; c < colors; c++)
{
if (! print (output, error,
",\n\t{%3d,%3d,%3d}",
(gint) cmap[3 * c],
(gint) cmap[3 * c + 1],
(gint) cmap[3 * c + 2]))
{
goto fail;
}
}
/* fill the rest */
for ( ; c < 256; c++)
{
if (! print (output, error, ",\n\t{255,255,255}"))
goto fail;
}
/* close bracket */
if (! print (output, error, "\n\t};\n"))
goto fail;
g_free (cmap);
/* save image */
if (! print (output, error, "static unsigned char header_data[] = {\n\t"))
goto fail;
data = g_new (guchar, width * 1);
c = 0;
for (y = 0; y < height; y++)
{
gegl_buffer_get (buffer, GEGL_RECTANGLE (0, y, width, 1), 1.0,
NULL, data,
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
for (x = 0; x < width -1; x++)
{
d = data + x * 1;
if (! print (output, error, "%d,", (gint) d[0]))
goto fail;
c++;
if (c >= 16)
{
if (! print (output, error, "\n\t"))
goto fail;
c = 0;
}
}
if (y != height - 1)
{
if (! print (output, error, "%d,\n\t", (gint) d[1]))
goto fail;
}
else
{
if (! print (output, error, "%d\n\t", (gint) d[1]))
goto fail;
}
c = 0; /* reset line counter */
}
if (! print (output, error, "};\n"))
goto fail;
break;
default:
g_warning ("unhandled drawable type (%d)", drawable_type);
goto fail;
}
if (! g_output_stream_close (output, NULL, error))
goto fail;
g_free (data);
g_object_unref (output);
g_object_unref (buffer);
return TRUE;
fail:
cancellable = g_cancellable_new ();
g_cancellable_cancel (cancellable);
g_output_stream_close (output, cancellable, NULL);
g_free (data);
g_object_unref (output);
g_object_unref (buffer);
g_object_unref (cancellable);
return FALSE;
}
static gboolean
print (GOutputStream *output,
GError **error,
const gchar *format,
...)
{
va_list args;
gboolean success;
va_start (args, format);
success = g_output_stream_vprintf (output, NULL, NULL,
error, format, args);
va_end (args);
return success;
}

2562
plug-ins/common/file-heif.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,726 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* GTM plug-in --- PIKA Table Magic
* Allows images to be exported as HTML tables with different colored cells.
* It doesn't have very much practical use other than being able to
* easily design a table by "painting" it in PIKA, or to make small HTML
* table images/icons.
*
* Copyright (C) 1997 Daniel Dunbar
* Email: ddunbar@diads.com
* WWW: http://millennium.diads.com/pika/
*
* 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/>.
*/
/* Version 1.0:
* Once I first found out that it was possible to have pixel level control
* of HTML tables I instantly realized that it would be possible, however
* pointless, to save an image as a, albeit huge, HTML table.
*
* One night when I was feeling in an adventourously stupid programming mood
* I decided to write a program to do it.
*
* At first I just wrote a really ugly hack to do it, which I then planned
* on using once just to see how it worked, and then posting a URL and
* laughing about it on #pika. As it turns out, tigert thought it actually
* had potential to be a useful plugin, so I started adding features and
* and making a nice UI.
*
* It's still not very useful, but I did manage to significantly improve my
* C programming skills in the process, so it was worth it.
*
* If you happen to find it useful I would appreciate any email about it.
* - Daniel Dunbar
* ddunbar@diads.com
*/
#include "config.h"
#include <string.h>
#include <libpika/pika.h>
#include <libpika/pikaui.h>
#include "libpika/stdplugins-intl.h"
#define SAVE_PROC "file-html-table-save"
#define PLUG_IN_BINARY "file-html-table"
typedef struct _Html Html;
typedef struct _HtmlClass HtmlClass;
struct _Html
{
PikaPlugIn parent_instance;
};
struct _HtmlClass
{
PikaPlugInClass parent_class;
};
#define HTML_TYPE (html_get_type ())
#define HTML (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), HTML_TYPE, Html))
GType html_get_type (void) G_GNUC_CONST;
static GList * html_query_procedures (PikaPlugIn *plug_in);
static PikaProcedure * html_create_procedure (PikaPlugIn *plug_in,
const gchar *name);
static PikaValueArray * html_save (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
GFile *file,
const PikaValueArray *args,
gpointer run_data);
static gboolean save_image (GFile *file,
GeglBuffer *buffer,
GObject *config,
GError **error);
static gboolean save_dialog (PikaImage *image,
PikaProcedure *procedure,
GObject *config);
static gboolean print (GOutputStream *output,
GError **error,
const gchar *format,
...) G_GNUC_PRINTF (3, 0);
static gboolean color_comp (guchar *buffer,
guchar *buf2);
G_DEFINE_TYPE (Html, html, PIKA_TYPE_PLUG_IN)
PIKA_MAIN (HTML_TYPE)
DEFINE_STD_SET_I18N
static void
html_class_init (HtmlClass *klass)
{
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
plug_in_class->query_procedures = html_query_procedures;
plug_in_class->create_procedure = html_create_procedure;
plug_in_class->set_i18n = STD_SET_I18N;
}
static void
html_init (Html *html)
{
}
static GList *
html_query_procedures (PikaPlugIn *plug_in)
{
return g_list_append (NULL, g_strdup (SAVE_PROC));
}
static PikaProcedure *
html_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,
html_save, NULL, NULL);
pika_procedure_set_image_types (procedure, "*");
pika_procedure_set_menu_label (procedure, _("HTML table"));
pika_file_procedure_set_format_name (PIKA_FILE_PROCEDURE (procedure),
_("HTML Table"));
pika_procedure_set_documentation (procedure,
_("PIKA Table Magic"),
_("Allows you to draw an HTML table "
"in PIKA. See help for more info."),
name);
pika_procedure_set_attribution (procedure,
"Daniel Dunbar",
"Daniel Dunbar",
"1998");
pika_file_procedure_set_handles_remote (PIKA_FILE_PROCEDURE (procedure),
TRUE);
pika_file_procedure_set_mime_types (PIKA_FILE_PROCEDURE (procedure),
"text/html");
pika_file_procedure_set_extensions (PIKA_FILE_PROCEDURE (procedure),
"html,htm");
PIKA_PROC_AUX_ARG_BOOLEAN (procedure, "use-caption",
_("Use c_aption"),
_("Enable if you would like to have the table "
"captioned."),
FALSE,
PIKA_PARAM_READWRITE);
PIKA_PROC_AUX_ARG_STRING (procedure, "caption-text",
_("Capt_ion"),
_("The text for the table caption."),
"Made with PIKA Table Magic",
PIKA_PARAM_READWRITE);
PIKA_PROC_AUX_ARG_STRING (procedure, "cell-content",
_("Cell con_tent"),
_("The text to go into each cell."),
"&nbsp;",
PIKA_PARAM_READWRITE);
PIKA_PROC_AUX_ARG_STRING (procedure, "cell-width",
_("_Width"),
_("The width for each table cell. "
"Can be a number or a percent."),
"",
PIKA_PARAM_READWRITE);
PIKA_PROC_AUX_ARG_STRING (procedure, "cell-height",
_("_Height"),
_("The height for each table cell. "
"Can be a number or a percent."),
"",
PIKA_PARAM_READWRITE);
PIKA_PROC_AUX_ARG_BOOLEAN (procedure, "full-document",
_("_Generate full HTML document"),
_("If enabled GTM will output a full HTML "
"document with <HTML>, <BODY>, etc. tags "
"instead of just the table html."),
TRUE,
PIKA_PARAM_READWRITE);
PIKA_PROC_AUX_ARG_INT (procedure, "border",
_("_Border"),
_("The number of pixels in the table border."),
0, 1000, 2,
PIKA_PARAM_READWRITE);
PIKA_PROC_AUX_ARG_BOOLEAN (procedure, "span-tags",
_("_Use cellspan"),
_("If enabled GTM will replace any "
"rectangular sections of identically "
"colored blocks with one large cell with "
"ROWSPAN and COLSPAN values."),
FALSE,
PIKA_PARAM_READWRITE);
PIKA_PROC_AUX_ARG_BOOLEAN (procedure, "compress-td-tags",
_("Co_mpress TD tags"),
_("Enabling this will cause GTM to "
"leave no whitespace between the TD "
"tags and the cell content. This is only "
"necessary for pixel level positioning "
"control."),
FALSE,
PIKA_PARAM_READWRITE);
PIKA_PROC_AUX_ARG_INT (procedure, "cell-padding",
_("Cell-pa_dding"),
_("The amount of cell padding."),
0, 1000, 4,
PIKA_PARAM_READWRITE);
PIKA_PROC_AUX_ARG_INT (procedure, "cell-spacing",
_("Cell spaci_ng"),
_("The amount of cell spacing."),
0, 1000, 0,
PIKA_PARAM_READWRITE);
}
return procedure;
}
static PikaValueArray *
html_save (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
GFile *file,
const PikaValueArray *args,
gpointer run_data)
{
PikaProcedureConfig *config;
PikaPDBStatusType status = PIKA_PDB_SUCCESS;
GeglBuffer *buffer;
GError *error = NULL;
gegl_init (NULL, NULL);
if (run_mode != PIKA_RUN_INTERACTIVE)
return pika_procedure_new_return_values (procedure,
PIKA_PDB_CALLING_ERROR,
NULL);
config = pika_procedure_create_config (procedure);
pika_procedure_config_begin_run (config, image, run_mode, args);
if (! save_dialog (image, procedure, G_OBJECT (config)))
return pika_procedure_new_return_values (procedure,
PIKA_PDB_CANCEL,
NULL);
if (n_drawables != 1)
{
g_set_error (&error, G_FILE_ERROR, 0,
_("HTML table plug-in does not support multiple layers."));
return pika_procedure_new_return_values (procedure,
PIKA_PDB_CALLING_ERROR,
error);
}
buffer = pika_drawable_get_buffer (drawables[0]);
if (! save_image (file, buffer, G_OBJECT (config),
&error))
{
status = PIKA_PDB_EXECUTION_ERROR;
}
g_object_unref (buffer);
pika_procedure_config_end_run (config, status);
g_object_unref (config);
return pika_procedure_new_return_values (procedure, status, error);
}
static gboolean
save_image (GFile *file,
GeglBuffer *buffer,
GObject *config,
GError **error)
{
const Babl *format = babl_format ("R'G'B'A u8");
GeglSampler *sampler;
GCancellable *cancellable;
GOutputStream *output;
gint row, col;
gint cols, rows;
gint x, y;
gint colcount, colspan, rowspan;
gint *palloc;
guchar *buf, *buf2;
gchar *width = NULL;
gchar *height = NULL;
gboolean config_use_caption;
gchar *config_caption_text;
gchar *config_cell_content;
gchar *config_cell_width;
gchar *config_cell_height;
gboolean config_full_document;
gint config_border;
gboolean config_span_tags;
gboolean config_compress_td_tags;
gint config_cell_padding;
gint config_cell_spacing;
g_object_get (config,
"use-caption", &config_use_caption,
"caption-text", &config_caption_text,
"cell-content", &config_cell_content,
"cell-width", &config_cell_width,
"cell-height", &config_cell_height,
"full-document", &config_full_document,
"border", &config_border,
"span-tags", &config_span_tags,
"compress-td-tags", &config_compress_td_tags,
"cell-padding", &config_cell_padding,
"cell-spacing", &config_cell_spacing,
NULL);
if (! config_caption_text) config_caption_text = g_strdup ("");
if (! config_cell_content) config_cell_content = g_strdup ("");
if (! config_cell_width) config_cell_width = g_strdup ("");
if (! config_cell_height) config_cell_height = g_strdup ("");
cols = gegl_buffer_get_width (buffer);
rows = gegl_buffer_get_height (buffer);
pika_progress_init_printf (_("Exporting '%s'"),
pika_file_get_utf8_name (file));
output = G_OUTPUT_STREAM (g_file_replace (file,
NULL, FALSE, G_FILE_CREATE_NONE,
NULL, error));
if (output)
{
GOutputStream *buffered;
buffered = g_buffered_output_stream_new (output);
g_object_unref (output);
output = buffered;
}
else
{
return FALSE;
}
sampler = gegl_buffer_sampler_new (buffer, format, GEGL_SAMPLER_NEAREST);
palloc = g_new (int, rows * cols);
if (config_full_document)
{
if (! print (output, error,
"<HTML>\n<HEAD><TITLE>%s</TITLE></HEAD>\n<BODY>\n",
pika_file_get_utf8_name (file)) ||
! print (output, error, "<H1>%s</H1>\n",
pika_file_get_utf8_name (file)))
{
goto fail;
}
}
if (! print (output, error,
"<TABLE BORDER=%d CELLPADDING=%d CELLSPACING=%d>\n",
config_border, config_cell_padding, config_cell_spacing))
goto fail;
if (config_use_caption)
{
if (! print (output, error, "<CAPTION>%s</CAPTION>\n",
config_caption_text))
goto fail;
}
buf = g_newa (guchar, babl_format_get_bytes_per_pixel (format));
buf2 = g_newa (guchar, babl_format_get_bytes_per_pixel (format));
if (strcmp (config_cell_width, "") != 0)
{
width = g_strdup_printf (" WIDTH=\"%s\"", config_cell_width);
}
if (strcmp (config_cell_height, "") != 0)
{
height = g_strdup_printf (" HEIGHT=\"%s\" ", config_cell_height);
}
if (! width)
width = g_strdup (" ");
if (! height)
height = g_strdup (" ");
/* Initialize array to hold ROWSPAN and COLSPAN cell allocation table */
for (row = 0; row < rows; row++)
for (col = 0; col < cols; col++)
palloc[cols * row + col] = 1;
colspan = 0;
rowspan = 0;
for (y = 0; y < rows; y++)
{
if (! print (output, error, " <TR>\n"))
goto fail;
for (x = 0; x < cols; x++)
{
gegl_sampler_get (sampler, x, y, NULL, buf, GEGL_ABYSS_NONE);
/* Determine ROWSPAN and COLSPAN */
if (config_span_tags)
{
col = x;
row = y;
colcount = 0;
colspan = 0;
rowspan = 0;
gegl_sampler_get (sampler, col, row, NULL, buf2, GEGL_ABYSS_NONE);
while (color_comp (buf, buf2) &&
palloc[cols * row + col] == 1 &&
row < rows)
{
while (color_comp (buf, buf2) &&
palloc[cols * row + col] == 1 &&
col < cols)
{
colcount++;
col++;
gegl_sampler_get (sampler,
col, row, NULL, buf2, GEGL_ABYSS_NONE);
}
if (colcount != 0)
{
row++;
rowspan++;
}
if (colcount < colspan || colspan == 0)
colspan = colcount;
col = x;
colcount = 0;
gegl_sampler_get (sampler,
col, row, NULL, buf2, GEGL_ABYSS_NONE);
}
if (colspan > 1 || rowspan > 1)
{
for (row = 0; row < rowspan; row++)
for (col = 0; col < colspan; col++)
palloc[cols * (row + y) + (col + x)] = 0;
palloc[cols * y + x] = 2;
}
}
if (palloc[cols * y + x] == 1)
{
if (! print (output, error,
" <TD%s%sBGCOLOR=#%02x%02x%02x>",
width, height, buf[0], buf[1], buf[2]))
goto fail;
}
if (palloc[cols * y + x] == 2)
{
if (! print (output, error,
" <TD ROWSPAN=\"%d\" COLSPAN=\"%d\"%s%sBGCOLOR=#%02x%02x%02x>",
rowspan, colspan, width, height,
buf[0], buf[1], buf[2]))
goto fail;
}
if (palloc[cols * y + x] != 0)
{
if (config_compress_td_tags)
{
if (! print (output, error,
"%s</TD>\n", config_cell_content))
goto fail;
}
else
{
if (! print (output, error,
"\n %s\n </TD>\n", config_cell_content))
goto fail;
}
}
}
if (! print (output, error, " </TR>\n"))
goto fail;
pika_progress_update ((double) y / (double) rows);
}
if (config_full_document)
{
if (! print (output, error, "</TABLE></BODY></HTML>\n"))
goto fail;
}
else
{
if (! print (output, error, "</TABLE>\n"))
goto fail;
}
if (! g_output_stream_close (output, NULL, error))
goto fail;
g_object_unref (output);
g_object_unref (sampler);
g_free (width);
g_free (height);
g_free (palloc);
g_free (config_caption_text);
g_free (config_cell_content);
g_free (config_cell_width);
g_free (config_cell_height);
pika_progress_update (1.0);
return TRUE;
fail:
cancellable = g_cancellable_new ();
g_cancellable_cancel (cancellable);
g_output_stream_close (output, cancellable, NULL);
g_object_unref (cancellable);
g_object_unref (output);
g_object_unref (sampler);
g_free (width);
g_free (height);
g_free (palloc);
g_free (config_caption_text);
g_free (config_cell_content);
g_free (config_cell_width);
g_free (config_cell_height);
return FALSE;
}
static gint
save_dialog (PikaImage *image,
PikaProcedure *procedure,
GObject *config)
{
GtkWidget *dialog;
GtkWidget *frame;
gboolean run;
pika_ui_init (PLUG_IN_BINARY);
dialog = pika_save_procedure_dialog_new (PIKA_SAVE_PROCEDURE (procedure),
PIKA_PROCEDURE_CONFIG (config),
image);
if (pika_image_get_width (image) * pika_image_get_height (image) > 4096)
{
GtkWidget *eek;
GtkWidget *hbox;
pika_procedure_dialog_get_label (PIKA_PROCEDURE_DIALOG (dialog),
"warning-label",
_("You are about to create a huge\n"
"HTML file which will most likely\n"
"crash your browser."));
hbox = pika_procedure_dialog_fill_box (PIKA_PROCEDURE_DIALOG (dialog),
"warning-hbox", "warning-label",
NULL);
gtk_orientable_set_orientation (GTK_ORIENTABLE (hbox),
GTK_ORIENTATION_HORIZONTAL);
eek = gtk_image_new_from_icon_name (PIKA_ICON_MASCOT_EEK,
GTK_ICON_SIZE_DIALOG);
gtk_box_pack_start (GTK_BOX (hbox), eek, FALSE, FALSE, 0);
gtk_widget_show (eek);
gtk_box_reorder_child (GTK_BOX (hbox), eek, 0);
gtk_widget_set_margin_end (eek, 24);
pika_procedure_dialog_get_label (PIKA_PROCEDURE_DIALOG (dialog),
"warning-frame-label", _("Warning"));
frame = pika_procedure_dialog_fill_frame (PIKA_PROCEDURE_DIALOG (dialog),
"warning-frame",
"warning-frame-label",
FALSE, "warning-hbox");
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
pika_procedure_dialog_fill (PIKA_PROCEDURE_DIALOG (dialog),
"warning-frame", NULL);
}
/* HTML Page Options */
pika_procedure_dialog_get_label (PIKA_PROCEDURE_DIALOG (dialog),
"page-label", _("HTML Page Options"));
frame = pika_procedure_dialog_fill_frame (PIKA_PROCEDURE_DIALOG (dialog),
"page-frame",
"page-label",
FALSE, "full-document");
gtk_widget_set_margin_bottom (frame, 8);
/* HTML Table Creation Options */
pika_procedure_dialog_get_label (PIKA_PROCEDURE_DIALOG (dialog),
"creation-label",
_("Table Creation Options"));
pika_procedure_dialog_fill_box (PIKA_PROCEDURE_DIALOG (dialog),
"creation-vbox", "span-tags",
"compress-td-tags", "use-caption",
"caption-text", "cell-content", NULL);
pika_procedure_dialog_set_sensitive (PIKA_PROCEDURE_DIALOG (dialog),
"caption-text", TRUE,
config, "use-caption",
FALSE);
frame = pika_procedure_dialog_fill_frame (PIKA_PROCEDURE_DIALOG (dialog),
"creation-frame",
"creation-label",
FALSE, "creation-vbox");
gtk_widget_set_margin_bottom (frame, 8);
/* HTML Table Options */
pika_procedure_dialog_get_label (PIKA_PROCEDURE_DIALOG (dialog),
"table-label", _("Table Options"));
pika_procedure_dialog_fill_box (PIKA_PROCEDURE_DIALOG (dialog),
"table-vbox", "border", "cell-width",
"cell-height", "cell-padding",
"cell-spacing", NULL);
frame = pika_procedure_dialog_fill_frame (PIKA_PROCEDURE_DIALOG (dialog),
"table-frame", "table-label",
FALSE, "table-vbox");
gtk_widget_set_margin_bottom (frame, 8);
pika_procedure_dialog_fill (PIKA_PROCEDURE_DIALOG (dialog),
"page-frame", "creation-frame",
"table-frame", NULL);
gtk_widget_show (dialog);
run = pika_procedure_dialog_run (PIKA_PROCEDURE_DIALOG (dialog));
gtk_widget_destroy (dialog);
return run;
}
static gboolean
print (GOutputStream *output,
GError **error,
const gchar *format,
...)
{
va_list args;
gboolean success;
va_start (args, format);
success = g_output_stream_vprintf (output, NULL, NULL,
error, format, args);
va_end (args);
return success;
}
static gboolean
color_comp (guchar *buf,
guchar *buf2)
{
return (buf[0] == buf2[0] &&
buf[1] == buf2[1] &&
buf[2] == buf2[2]);
}

542
plug-ins/common/file-iff.c Normal file
View File

@ -0,0 +1,542 @@
/* PIKA - Photo and Image Kooker Application
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* Amiga IFF plug-in
*
* Copyright (C) 2023 Alex S.
*
* 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/>.
*/
#include "config.h"
#include <string.h>
#include <errno.h>
#include <glib/gstdio.h>
#include <libpika/pika.h>
#include <libpika/pikaui.h>
#include <libilbm/ilbm.h>
#include <libilbm/ilbmimage.h>
#include <libilbm/interleave.h>
#include <libilbm/byterun.h>
#include "libpika/stdplugins-intl.h"
#define LOAD_PROC "file-iff-load"
#define PLUG_IN_BINARY "file-iff"
#define PLUG_IN_ROLE "pika-file-iff"
typedef struct _Iff Iff;
typedef struct _IffClass IffClass;
struct _Iff
{
PikaPlugIn parent_instance;
};
struct _IffClass
{
PikaPlugInClass parent_class;
};
#define IFF_TYPE (iff_get_type ())
#define IFF (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), IFF_TYPE, Iff))
GType iff_get_type (void) G_GNUC_CONST;
static GList * iff_query_procedures (PikaPlugIn *plug_in);
static PikaProcedure * iff_create_procedure (PikaPlugIn *plug_in,
const gchar *name);
static PikaValueArray * iff_load (PikaProcedure *procedure,
PikaRunMode run_mode,
GFile *file,
const PikaValueArray *args,
gpointer run_data);
static PikaImage * load_image (GFile *file,
GObject *config,
PikaRunMode run_mode,
GError **error);
static void deleave_indexed_row (IFF_UByte *bitplanes,
guchar *pixel_row,
gint width,
gint nPlanes);
static void deleave_rgb_row (IFF_UByte *bitplanes,
guchar *pixel_row,
gint width,
gint nPlanes,
gint pixel_size);
static void deleave_ham_row (const guchar *pika_cmap,
IFF_UByte *bitplanes,
guchar *pixel_row,
gint width,
gint nPlanes);
static void pbm_row (IFF_UByte *bitplanes,
guchar *pixel_row,
gint width);
G_DEFINE_TYPE (Iff, iff, PIKA_TYPE_PLUG_IN)
PIKA_MAIN (IFF_TYPE)
DEFINE_STD_SET_I18N
static void
iff_class_init (IffClass *klass)
{
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
plug_in_class->query_procedures = iff_query_procedures;
plug_in_class->create_procedure = iff_create_procedure;
plug_in_class->set_i18n = STD_SET_I18N;
}
static void
iff_init (Iff *iff)
{
}
static GList *
iff_query_procedures (PikaPlugIn *plug_in)
{
GList *list = NULL;
list = g_list_append (list, g_strdup (LOAD_PROC));
return list;
}
static PikaProcedure *
iff_create_procedure (PikaPlugIn *plug_in,
const gchar *name)
{
PikaProcedure *procedure = NULL;
if (! strcmp (name, LOAD_PROC))
{
procedure = pika_load_procedure_new (plug_in, name,
PIKA_PDB_PROC_TYPE_PLUGIN,
iff_load, NULL, NULL);
pika_procedure_set_menu_label (procedure, _("Amiga IFF"));
pika_procedure_set_documentation (procedure,
_("Load file in the IFF file format"),
_("Load file in the IFF file format"),
name);
pika_procedure_set_attribution (procedure,
"Alex S.",
"Alex S.",
"2023");
pika_file_procedure_set_mime_types (PIKA_FILE_PROCEDURE (procedure),
"image/x-ilbm");
pika_file_procedure_set_extensions (PIKA_FILE_PROCEDURE (procedure),
"iff,ilbm,lbm,acbm,ham,ham6,ham8");
pika_file_procedure_set_magics (PIKA_FILE_PROCEDURE (procedure),
"0,string,FORM");
}
return procedure;
}
static PikaValueArray *
iff_load (PikaProcedure *procedure,
PikaRunMode run_mode,
GFile *file,
const PikaValueArray *args,
gpointer run_data)
{
PikaProcedureConfig *config;
PikaValueArray *return_vals;
PikaImage *image;
GError *error = NULL;
gegl_init (NULL, NULL);
config = pika_procedure_create_config (procedure);
pika_procedure_config_begin_run (config, NULL, run_mode, args);
image = load_image (file, G_OBJECT (config), run_mode, &error);
if (! image)
{
pika_procedure_config_end_run (config, PIKA_PDB_EXECUTION_ERROR);
g_object_unref (config);
return pika_procedure_new_return_values (procedure,
PIKA_PDB_EXECUTION_ERROR,
error);
}
pika_procedure_config_end_run (config, PIKA_PDB_SUCCESS);
g_object_unref (config);
return_vals = pika_procedure_new_return_values (procedure,
PIKA_PDB_SUCCESS,
NULL);
PIKA_VALUES_SET_IMAGE (return_vals, 1, image);
return return_vals;
}
static PikaImage *
load_image (GFile *file,
GObject *config,
PikaRunMode run_mode,
GError **error)
{
PikaImage *image = NULL;
PikaLayer *layer;
GeglBuffer *buffer;
FILE *fp;
guint imagesLength;
IFF_Chunk *chunk;
ILBM_Image **iff_image;
fp = g_fopen (g_file_peek_path (file), "rb");
if (! fp)
{
g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
_("Could not open '%s' for reading: %s"),
pika_file_get_utf8_name (file), g_strerror (errno));
return NULL;
}
fclose (fp);
chunk = ILBM_read (g_file_peek_path (file));
iff_image = ILBM_extractImages (chunk, &imagesLength);
for (gint i = 0; i < imagesLength; i++)
{
ILBM_Image *true_image = iff_image[i];
/* Struct representing bitmap header properties */
ILBM_BitMapHeader *bitMapHeader = true_image->bitMapHeader;
/* Struct containing the color palette */
ILBM_ColorMap *colorMap = true_image->colorMap;
ILBM_Viewport *camg = true_image->viewport;
IFF_UByte *bitplanes;
PikaImageType image_type;
guchar pika_cmap[768]; /* Max index is (2^nplanes) - 1 */
gint width;
gint height;
gint nPlanes;
gint palette_size = 0;
gint row_length;
gint pixel_size = 1;
gint y_height = 0;
gboolean ehb_mode = FALSE;
gboolean ham_mode = FALSE;
if (! true_image || ! bitMapHeader)
{
g_message (_("Invalid or missing ILBM image"));
return image;
}
if (! true_image->body)
{
g_message (_("ILBM contains no image data - likely a palette file"));
return NULL;
}
/* Convert ACBM files to ILBM format */
if (ILBM_imageIsACBM (true_image))
ILBM_convertACBMToILBM (true_image);
width = bitMapHeader->w;
height = bitMapHeader->h;
nPlanes = bitMapHeader->nPlanes;
row_length = (width + 15) / 16;
pixel_size = nPlanes / 8;
/* Check for ILBM variants in CMAG chunk */
if (camg)
{
if (camg->viewportMode & (1 << 7))
ehb_mode = TRUE;
if (camg->viewportMode & (1 << 11) &&
(nPlanes >= 5 && nPlanes <= 8))
ham_mode = TRUE;
}
/* Load palette if it exists */
if (colorMap)
{
palette_size = colorMap->colorRegisterLength;
for (gint j = 0; j < palette_size; j++)
{
pika_cmap[j * 3] = colorMap->colorRegister[j].red;
pika_cmap[j * 3 + 1] = colorMap->colorRegister[j].green;
pika_cmap[j * 3 + 2] = colorMap->colorRegister[j].blue;
}
if (ehb_mode)
{
/* EHB mode adds 32 more colors. Each are half the RGB values
* of the first 32 colors */
for (gint j = 0; j < palette_size * 2; j++)
{
gint offset_index = j + 32;
pika_cmap[offset_index * 3] =
colorMap->colorRegister[j].red / 2;
pika_cmap[offset_index * 3 + 1] =
colorMap->colorRegister[j].green / 2;
pika_cmap[offset_index * 3 + 2] =
colorMap->colorRegister[j].blue / 2;
}
/* EHB mode always has 64 colors */
palette_size = 64;
}
}
if (ham_mode)
pixel_size = 3;
if (pixel_size == 4)
{
image_type = PIKA_RGBA_IMAGE;
}
else if (pixel_size == 3)
{
image_type = PIKA_RGB_IMAGE;
}
else
{
pixel_size = 1;
image_type = PIKA_INDEXED_IMAGE;
}
ILBM_unpackByteRun (true_image);
image = pika_image_new (width, height,
pixel_size == 1 ? PIKA_INDEXED : PIKA_RGB);
layer = pika_layer_new (image, _("Background"), width, height,
image_type, 100,
pika_image_get_default_new_layer_mode (image));
pika_image_insert_layer (image, layer, NULL, 0);
buffer = pika_drawable_get_buffer (PIKA_DRAWABLE (layer));
bitplanes = true_image->body->chunkData;
/* Loading rows */
for (gint j = 0; j < height; j++)
{
guchar *pixel_row;
pixel_row = g_malloc (width * pixel_size * sizeof (guchar));
/* PBM uses one byte per pixel index */
if (ILBM_imageIsPBM (true_image))
pbm_row (bitplanes, pixel_row, width);
else if (pixel_size == 1)
deleave_indexed_row (bitplanes, pixel_row, width, nPlanes);
else if (ham_mode)
deleave_ham_row (pika_cmap, bitplanes, pixel_row, width, nPlanes);
else
deleave_rgb_row (bitplanes, pixel_row, width, nPlanes, pixel_size);
bitplanes += (row_length * 2 * nPlanes);
gegl_buffer_set (buffer, GEGL_RECTANGLE (0, y_height, width, 1), 0,
NULL, pixel_row, GEGL_AUTO_ROWSTRIDE);
y_height++;
g_free (pixel_row);
}
if (pixel_size == 1)
pika_image_set_colormap (image, pika_cmap, palette_size);
g_object_unref (buffer);
}
ILBM_freeImages (iff_image, imagesLength);
return image;
}
static void
deleave_indexed_row (IFF_UByte *bitplanes,
guchar *pixel_row,
gint width,
gint nPlanes)
{
guchar index[width];
gint row_length = ((width + 15) / 16) * 2;
/* Initialize index array */
for (gint i = 0; i < width; i++)
index[i] = 0;
/* Deleave rows */
for (gint i = 0; i < row_length; i++)
{
for (gint j = 0; j < 8; j++)
{
guint8 bitmask = (1 << (8 - j)) - (1 << (7 - j));
for (gint k = 0; k < nPlanes; k++)
{
guint8 update = (1 << (k + 1)) - (1 << (k));
if (bitplanes[i + (row_length * k)] & bitmask)
index[j + (i * 8)] += update;
}
}
}
/* Associate palette with pixels */
for (gint i = 0; i < width; i++)
pixel_row[i] = index[i];
}
static void
deleave_ham_row (const guchar *pika_cmap,
IFF_UByte *bitplanes,
guchar *pixel_row,
gint width,
gint nPlanes)
{
const gint control_index[3] = {2, 0, 1};
const gint row_length = ((width + 15) / 16) * 2;
gint prior_rgb[3] = {0, 0, 0};
gint current_index = 0;
/* Deleave rows */
for (gint i = 0; i < row_length; i++)
{
for (gint j = 0; j < 8; j++)
{
guint8 bitmask = (1 << (8 - j)) - (1 << (7 - j));
guint8 control = 0;
guint8 color = 0;
guint8 index = 0;
for (gint k = 0; k < nPlanes; k++)
{
if (bitplanes[i + (row_length * k)] & bitmask)
{
gint limit = nPlanes < 7 ? 4 : 6;
/* The last two planes are control values.
* Everything else is either an index or a color.
* For HAM 5/6 colors, we use the 4 bits as both
* upper and lower bit modifiers. For HAM 7/8,
* we replace the 6 MSB with the color value. */
if (k < limit)
{
gint update = 1 << k;
index += update;
if (limit == 4)
color += update + (update << 4);
else
color += update;
}
else
{
control += 1 << (k - limit);
}
}
}
if (control == 0)
{
prior_rgb[0] = pika_cmap[index * 3];
prior_rgb[1] = pika_cmap[index * 3 + 1];
prior_rgb[2] = pika_cmap[index * 3 + 2];
}
else
{
/* Determines which RGB component to modify */
gint modify = control_index[control - 1];
if (nPlanes < 7)
prior_rgb[modify] = color;
else
prior_rgb[modify] = (color << 2) + (prior_rgb[modify] & 3);
}
pixel_row[current_index * 3] = prior_rgb[0];
pixel_row[current_index * 3 + 1] = prior_rgb[1];
pixel_row[current_index * 3 + 2] = prior_rgb[2];
current_index++;
}
}
}
static void
deleave_rgb_row (IFF_UByte *bitplanes,
guchar *pixel_row,
gint width,
gint nPlanes,
gint pixel_size)
{
gint row_length = ((width + 15) / 16) * 2;
gint current_pixel = 0;
/* Initialize index array */
for (gint i = 0; i < (width * pixel_size); i++)
pixel_row[i] = 0;
/* Deleave rows */
for (gint i = 0; i < row_length; i++)
{
for (gint j = 0; j < 8; j++)
{
guint8 bitmask = (1 << (8 - j)) - (1 << (7 - j));
for (gint k = 0; k < pixel_size; k++)
{
for (gint l = 0; l < 8; l++)
{
guint8 update = (1 << (l + 1)) - (1 << (l));
if (bitplanes[i + (row_length * (l + k * 8))] & bitmask)
pixel_row[current_pixel] += update;
}
current_pixel++;
}
}
}
}
static void
pbm_row (IFF_UByte *bitplanes,
guchar *pixel_row,
gint width)
{
for (gint i = 0; i < width; i++)
pixel_row[i] = bitplanes[i];
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

1689
plug-ins/common/file-mng.c Normal file

File diff suppressed because it is too large Load Diff

292
plug-ins/common/file-pat.c Normal file
View File

@ -0,0 +1,292 @@
/*
* pat plug-in version 1.01
* Loads/exports version 1 PIKA .pat files, by Tim Newsome <drz@frody.bloke.com>
*
* PIKA - Photo and Image Kooker Application
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* 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/>.
*/
#include "config.h"
#include <libpika/pika.h>
#include <libpika/pikaui.h>
#include "libpika/stdplugins-intl.h"
#define SAVE_PROC "file-pat-save"
#define PLUG_IN_BINARY "file-pat"
typedef struct _Pat Pat;
typedef struct _PatClass PatClass;
struct _Pat
{
PikaPlugIn parent_instance;
};
struct _PatClass
{
PikaPlugInClass parent_class;
};
#define PAT_TYPE (pat_get_type ())
#define PAT (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PAT_TYPE, Pat))
GType pat_get_type (void) G_GNUC_CONST;
static GList * pat_query_procedures (PikaPlugIn *plug_in);
static PikaProcedure * pat_create_procedure (PikaPlugIn *plug_in,
const gchar *name);
static PikaValueArray * pat_save (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
GFile *file,
const PikaValueArray *args,
gpointer run_data);
static gboolean save_dialog (PikaImage *image,
PikaProcedure *procedure,
GObject *config);
G_DEFINE_TYPE (Pat, pat, PIKA_TYPE_PLUG_IN)
PIKA_MAIN (PAT_TYPE)
DEFINE_STD_SET_I18N
static void
pat_class_init (PatClass *klass)
{
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
plug_in_class->query_procedures = pat_query_procedures;
plug_in_class->create_procedure = pat_create_procedure;
plug_in_class->set_i18n = STD_SET_I18N;
}
static void
pat_init (Pat *pat)
{
}
static GList *
pat_query_procedures (PikaPlugIn *plug_in)
{
return g_list_append (NULL, g_strdup (SAVE_PROC));
}
static PikaProcedure *
pat_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,
pat_save, NULL, NULL);
pika_procedure_set_image_types (procedure, "*");
pika_procedure_set_menu_label (procedure, _("PIKA pattern"));
pika_procedure_set_icon_name (procedure, PIKA_ICON_PATTERN);
pika_procedure_set_documentation (procedure,
_("Exports PIKA pattern file (.PAT)"),
_("New PIKA patterns can be created "
"by exporting them in the "
"appropriate place with this plug-in."),
SAVE_PROC);
pika_procedure_set_attribution (procedure,
"Tim Newsome",
"Tim Newsome",
"1997");
pika_file_procedure_set_format_name (PIKA_FILE_PROCEDURE (procedure),
_("Pattern"));
pika_file_procedure_set_mime_types (PIKA_FILE_PROCEDURE (procedure),
"image/x-pika-pat");
pika_file_procedure_set_extensions (PIKA_FILE_PROCEDURE (procedure),
"pat");
pika_file_procedure_set_handles_remote (PIKA_FILE_PROCEDURE (procedure),
TRUE);
PIKA_PROC_ARG_STRING (procedure, "description",
_("_Description"),
_("Short description of the pattern"),
_("PIKA Pattern"),
PIKA_PARAM_READWRITE);
}
return procedure;
}
static PikaValueArray *
pat_save (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
GFile *file,
const PikaValueArray *args,
gpointer run_data)
{
PikaProcedureConfig *config;
PikaPDBStatusType status = PIKA_PDB_SUCCESS;
PikaExportReturn export = PIKA_EXPORT_CANCEL;
gchar *description;
GError *error = NULL;
config = pika_procedure_create_config (procedure);
pika_procedure_config_begin_run (config, image, run_mode, args);
g_object_get (config,
"description", &description,
NULL);
if (! description || ! strlen (description))
{
gchar *name = g_path_get_basename (pika_file_get_utf8_name (file));
if (g_str_has_suffix (name, ".pat"))
name[strlen (name) - 4] = '\0';
if (strlen (name))
g_object_set (config,
"description", name,
NULL);
g_free (name);
}
g_free (description);
switch (run_mode)
{
case PIKA_RUN_INTERACTIVE:
case PIKA_RUN_WITH_LAST_VALS:
pika_ui_init (PLUG_IN_BINARY);
export = pika_export_image (&image, &n_drawables, &drawables, "PAT",
PIKA_EXPORT_CAN_HANDLE_GRAY |
PIKA_EXPORT_CAN_HANDLE_RGB |
PIKA_EXPORT_CAN_HANDLE_INDEXED |
PIKA_EXPORT_CAN_HANDLE_ALPHA);
if (export == PIKA_EXPORT_CANCEL)
return pika_procedure_new_return_values (procedure, PIKA_PDB_CANCEL,
NULL);
break;
default:
break;
}
if (run_mode == PIKA_RUN_INTERACTIVE)
{
if (! save_dialog (image, procedure, G_OBJECT (config)))
status = PIKA_PDB_CANCEL;
}
if (status == PIKA_PDB_SUCCESS)
{
PikaValueArray *save_retvals;
PikaValueArray *args;
g_object_get (config,
"description", &description,
NULL);
args = pika_value_array_new_from_types (NULL,
PIKA_TYPE_RUN_MODE, PIKA_RUN_NONINTERACTIVE,
PIKA_TYPE_IMAGE, image,
G_TYPE_INT, n_drawables,
PIKA_TYPE_OBJECT_ARRAY, NULL,
G_TYPE_FILE, file,
G_TYPE_STRING, description,
G_TYPE_NONE);
pika_value_set_object_array (pika_value_array_index (args, 3),
PIKA_TYPE_ITEM, (GObject **) drawables, n_drawables);
save_retvals = pika_pdb_run_procedure_array (pika_get_pdb (),
"file-pat-save-internal",
args);
pika_value_array_unref (args);
if (PIKA_VALUES_GET_ENUM (save_retvals, 0) != PIKA_PDB_SUCCESS)
{
g_set_error (&error, 0, 0,
"Running procedure 'file-pat-save-internal' "
"failed: %s",
pika_pdb_get_last_error (pika_get_pdb ()));
status = PIKA_PDB_EXECUTION_ERROR;
}
pika_value_array_unref (save_retvals);
}
pika_procedure_config_end_run (config, status);
g_object_unref (config);
if (export == PIKA_EXPORT_EXPORT)
{
pika_image_delete (image);
g_free (drawables);
}
return pika_procedure_new_return_values (procedure, status, error);
}
static gboolean
save_dialog (PikaImage *image,
PikaProcedure *procedure,
GObject *config)
{
GtkWidget *dialog;
GtkWidget *entry;
GtkWidget *real_entry;
gboolean run;
dialog = pika_save_procedure_dialog_new (PIKA_SAVE_PROCEDURE (procedure),
PIKA_PROCEDURE_CONFIG (config),
image);
entry = pika_procedure_dialog_get_widget (PIKA_PROCEDURE_DIALOG (dialog),
"description", PIKA_TYPE_LABEL_ENTRY);
real_entry = pika_label_entry_get_entry (PIKA_LABEL_ENTRY (entry));
gtk_entry_set_max_length (GTK_ENTRY (real_entry), 256);
gtk_entry_set_width_chars (GTK_ENTRY (real_entry), 20);
gtk_entry_set_activates_default (GTK_ENTRY (real_entry), TRUE);
pika_procedure_dialog_fill (PIKA_PROCEDURE_DIALOG (dialog), NULL);
gtk_widget_show (dialog);
run = pika_procedure_dialog_run (PIKA_PROCEDURE_DIALOG (dialog));
gtk_widget_destroy (dialog);
return run;
}

1426
plug-ins/common/file-pcx.c Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

762
plug-ins/common/file-pix.c Normal file
View File

@ -0,0 +1,762 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
* Alias|Wavefront pix/matte image reading and writing code
* Copyright (C) 1997 Mike Taylor
* (email: mtaylor@aw.sgi.com, WWW: http://reality.sgi.com/mtaylor)
*
* 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/>.
*
*/
/* This plug-in was written using the online documentation from
* Alias|Wavefront Inc's PowerAnimator product.
*
* Bug reports or suggestions should be e-mailed to mtaylor@aw.sgi.com
*/
/* Event history:
* V 1.0, MT, 02-Jul-97: initial version of plug-in
* V 1.1, MT, 04-Dec-97: added .als file extension
*/
/* Features
* - loads and exports
* - 24-bit (.pix)
* - 8-bit (.matte, .alpha, or .mask) images
*
* NOTE: pix and matte files do not support alpha channels or indexed
* color, so neither does this plug-in
*/
#include "config.h"
#include <errno.h>
#include <string.h>
#include <glib/gstdio.h>
#include <libpika/pika.h>
#include <libpika/pikaui.h>
#include "libpika/stdplugins-intl.h"
#define LOAD_PROC "file-pix-load"
#define SAVE_PROC "file-pix-save"
#define PLUG_IN_BINARY "file-pix"
#define PLUG_IN_ROLE "pika-file-pix"
/* #define PIX_DEBUG */
#ifdef PIX_DEBUG
# define PIX_DEBUG_PRINT(a,b) g_printerr (a,b)
#else
# define PIX_DEBUG_PRINT(a,b)
#endif
typedef struct _Pix Pix;
typedef struct _PixClass PixClass;
struct _Pix
{
PikaPlugIn parent_instance;
};
struct _PixClass
{
PikaPlugInClass parent_class;
};
#define PIX_TYPE (pix_get_type ())
#define PIX (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIX_TYPE, Pix))
GType pix_get_type (void) G_GNUC_CONST;
static GList * pix_query_procedures (PikaPlugIn *plug_in);
static PikaProcedure * pix_create_procedure (PikaPlugIn *plug_in,
const gchar *name);
static PikaValueArray * pix_load (PikaProcedure *procedure,
PikaRunMode run_mode,
GFile *file,
const PikaValueArray *args,
gpointer run_data);
static PikaValueArray * pix_save (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
GFile *file,
const PikaValueArray *args,
gpointer run_data);
static PikaImage * load_image (GFile *file,
GError **error);
static gboolean save_image (GFile *file,
PikaImage *image,
PikaDrawable *drawable,
GError **error);
static gboolean get_short (GInputStream *input,
guint16 *value,
GError **error);
static gboolean put_short (GOutputStream *output,
guint16 value,
GError **error);
G_DEFINE_TYPE (Pix, pix, PIKA_TYPE_PLUG_IN)
PIKA_MAIN (PIX_TYPE)
DEFINE_STD_SET_I18N
static void
pix_class_init (PixClass *klass)
{
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
plug_in_class->query_procedures = pix_query_procedures;
plug_in_class->create_procedure = pix_create_procedure;
plug_in_class->set_i18n = STD_SET_I18N;
}
static void
pix_init (Pix *pix)
{
}
static GList *
pix_query_procedures (PikaPlugIn *plug_in)
{
GList *list = NULL;
list = g_list_append (list, g_strdup (LOAD_PROC));
list = g_list_append (list, g_strdup (SAVE_PROC));
return list;
}
static PikaProcedure *
pix_create_procedure (PikaPlugIn *plug_in,
const gchar *name)
{
PikaProcedure *procedure = NULL;
if (! strcmp (name, LOAD_PROC))
{
procedure = pika_load_procedure_new (plug_in, name,
PIKA_PDB_PROC_TYPE_PLUGIN,
pix_load, NULL, NULL);
pika_file_procedure_set_handles_remote (PIKA_FILE_PROCEDURE (procedure),
TRUE);
pika_procedure_set_menu_label (procedure, _("Alias Pix image"));
pika_procedure_set_documentation (procedure,
"Loads files of the Alias|Wavefront "
"Pix file format",
"Loads files of the Alias|Wavefront "
"Pix file format",
name);
pika_procedure_set_attribution (procedure,
"Michael Taylor",
"Michael Taylor",
"1997");
pika_file_procedure_set_extensions (PIKA_FILE_PROCEDURE (procedure),
"pix,matte,mask,alpha,als");
}
else if (! strcmp (name, SAVE_PROC))
{
procedure = pika_save_procedure_new (plug_in, name,
PIKA_PDB_PROC_TYPE_PLUGIN,
pix_save, NULL, NULL);
pika_procedure_set_image_types (procedure, "*");
pika_file_procedure_set_handles_remote (PIKA_FILE_PROCEDURE (procedure),
TRUE);
pika_procedure_set_menu_label (procedure, _("Alias Pix image"));
pika_procedure_set_documentation (procedure,
"Export file in the Alias|Wavefront "
"pix/matte file format",
"Export file in the Alias|Wavefront "
"pix/matte file format",
name);
pika_procedure_set_attribution (procedure,
"Michael Taylor",
"Michael Taylor",
"1997");
pika_file_procedure_set_extensions (PIKA_FILE_PROCEDURE (procedure),
"pix,matte,mask,alpha,als");
}
return procedure;
}
static PikaValueArray *
pix_load (PikaProcedure *procedure,
PikaRunMode run_mode,
GFile *file,
const PikaValueArray *args,
gpointer run_data)
{
PikaValueArray *return_vals;
PikaImage *image;
GError *error = NULL;
gegl_init (NULL, NULL);
image = load_image (file, &error);
if (! image)
return pika_procedure_new_return_values (procedure,
PIKA_PDB_EXECUTION_ERROR,
error);
return_vals = pika_procedure_new_return_values (procedure,
PIKA_PDB_SUCCESS,
NULL);
PIKA_VALUES_SET_IMAGE (return_vals, 1, image);
return return_vals;
}
static PikaValueArray *
pix_save (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
GFile *file,
const PikaValueArray *args,
gpointer run_data)
{
PikaPDBStatusType status = PIKA_PDB_SUCCESS;
PikaExportReturn export = PIKA_EXPORT_CANCEL;
GError *error = NULL;
gegl_init (NULL, NULL);
switch (run_mode)
{
case PIKA_RUN_INTERACTIVE:
case PIKA_RUN_WITH_LAST_VALS:
pika_ui_init (PLUG_IN_BINARY);
export = pika_export_image (&image, &n_drawables, &drawables, "PIX",
PIKA_EXPORT_CAN_HANDLE_RGB |
PIKA_EXPORT_CAN_HANDLE_GRAY |
PIKA_EXPORT_CAN_HANDLE_INDEXED);
if (export == PIKA_EXPORT_CANCEL)
return pika_procedure_new_return_values (procedure,
PIKA_PDB_CANCEL,
NULL);
break;
default:
break;
}
if (n_drawables != 1)
{
g_set_error (&error, G_FILE_ERROR, 0,
_("PIX format does not support multiple layers."));
return pika_procedure_new_return_values (procedure,
PIKA_PDB_CALLING_ERROR,
error);
}
if (! save_image (file, image, drawables[0], &error))
{
status = PIKA_PDB_EXECUTION_ERROR;
}
if (export == PIKA_EXPORT_EXPORT)
{
pika_image_delete (image);
g_free (drawables);
}
return pika_procedure_new_return_values (procedure, status, error);
}
/*
* Description:
* Reads a 16-bit integer from a file in such a way that the machine's
* byte order should not matter.
*/
static gboolean
get_short (GInputStream *input,
guint16 *value,
GError **error)
{
guchar buf[2];
gsize bytes_read;
if (! g_input_stream_read_all (input, buf, 2,
&bytes_read, NULL, error) ||
bytes_read != 2)
{
return FALSE;
}
if (value)
*value = (buf[0] << 8) + buf[1];
return TRUE;
}
/*
* Description:
* Writes a 16-bit integer to a file in such a way that the machine's
* byte order should not matter.
*/
static gboolean
put_short (GOutputStream *output,
guint16 value,
GError **error)
{
guchar buf[2];
buf[0] = (value >> 8) & 0xFF;
buf[1] = value & 0xFF;
return g_output_stream_write_all (output, buf, 2, NULL, NULL, error);
}
/*
* Description:
* load the given image into pika
*
* Arguments:
* filename - name on the file to read
*
* Return Value:
* Image id for the loaded image
*
*/
static PikaImage *
load_image (GFile *file,
GError **error)
{
GInputStream *input;
GeglBuffer *buffer;
PikaImageBaseType imgtype;
PikaImageType gdtype;
guchar *dest;
guchar *dest_base;
PikaImage *image;
PikaLayer *layer;
gushort width, height, depth;
gint i, j, tile_height, row;
PIX_DEBUG_PRINT ("Opening file: %s\n", pika_file_get_utf8_name (file));
pika_progress_init_printf (_("Opening '%s'"),
g_file_get_parse_name (file));
input = G_INPUT_STREAM (g_file_read (file, NULL, error));
if (! input)
return NULL;
/* Read header information */
if (! get_short (input, &width, error) ||
! get_short (input, &height, error) ||
! get_short (input, NULL, error) || /* Discard obsolete field */
! get_short (input, NULL, error) || /* Discard obsolete field */
! get_short (input, &depth, error))
{
g_object_unref (input);
return NULL;
}
PIX_DEBUG_PRINT ("Width %hu\n", width);
PIX_DEBUG_PRINT ("Height %hu\n", height);
if (depth == 8)
{
/* Loading a matte file */
imgtype = PIKA_GRAY;
gdtype = PIKA_GRAY_IMAGE;
}
else if (depth == 24)
{
/* Loading an RGB file */
imgtype = PIKA_RGB;
gdtype = PIKA_RGB_IMAGE;
}
else
{
/* Header is invalid */
g_object_unref (input);
return NULL;
}
image = pika_image_new (width, height, imgtype);
layer = pika_layer_new (image, _("Background"),
width, height,
gdtype,
100,
pika_image_get_default_new_layer_mode (image));
pika_image_insert_layer (image, layer, NULL, 0);
buffer = pika_drawable_get_buffer (PIKA_DRAWABLE (layer));
tile_height = pika_tile_height ();
if (depth == 24)
{
/* Read a 24-bit Pix image */
dest_base = dest = g_new (guchar, 3 * width * tile_height);
for (i = 0; i < height;)
{
for (dest = dest_base, row = 0;
row < tile_height && i < height;
i++, row++)
{
guchar record[4];
gsize bytes_read;
guchar count;
/* Read a row of the image */
j = 0;
while (j < width)
{
if (! g_input_stream_read_all (input, record, 4,
&bytes_read, NULL, error) ||
bytes_read != 4)
break;
for (count = 0; count < record[0]; ++count)
{
dest[0] = record[3];
dest[1] = record[2];
dest[2] = record[1];
dest += 3;
j++;
if (j >= width)
break;
}
}
}
gegl_buffer_set (buffer, GEGL_RECTANGLE (0, i - row, width, row), 0,
NULL, dest_base, GEGL_AUTO_ROWSTRIDE);
pika_progress_update ((double) i / (double) height);
}
g_free (dest_base);
}
else
{
/* Read an 8-bit Matte image */
dest_base = dest = g_new (guchar, width * tile_height);
for (i = 0; i < height;)
{
for (dest = dest_base, row = 0;
row < tile_height && i < height;
i++, row++)
{
guchar record[2];
gsize bytes_read;
guchar count;
/* Read a row of the image */
j = 0;
while (j < width)
{
if (! g_input_stream_read_all (input, record, 2,
&bytes_read, NULL, error) ||
bytes_read != 2)
break;
for (count = 0; count < record[0]; ++count)
{
dest[j] = record[1];
j++;
if (j >= width)
break;
}
}
dest += width;
}
gegl_buffer_set (buffer, GEGL_RECTANGLE (0, i - row, width, row), 0,
NULL, dest_base, GEGL_AUTO_ROWSTRIDE);
pika_progress_update ((double) i / (double) height);
}
g_free (dest_base);
}
g_object_unref (buffer);
g_object_unref (input);
pika_progress_update (1.0);
return image;
}
/*
* Description:
* save the given file out as an alias pix or matte file
*
* Arguments:
* filename - name of file to save to
* image - image to save
* drawable - current drawable
*/
static gboolean
save_image (GFile *file,
PikaImage *image,
PikaDrawable *drawable,
GError **error)
{
GOutputStream *output;
GeglBuffer *buffer;
const Babl *format;
GCancellable *cancellable;
gint width;
gint height;
gint depth, i, j, row, tile_height, rectHeight;
gboolean savingColor = TRUE;
guchar *src;
guchar *src_base;
pika_progress_init_printf (_("Exporting '%s'"),
g_file_get_parse_name (file));
output = G_OUTPUT_STREAM (g_file_replace (file,
NULL, FALSE, G_FILE_CREATE_NONE,
NULL, error));
if (! output)
return FALSE;
/* Get info about image */
buffer = pika_drawable_get_buffer (drawable);
width = gegl_buffer_get_width (buffer);
height = gegl_buffer_get_height (buffer);
savingColor = ! pika_drawable_is_gray (drawable);
if (savingColor)
format = babl_format ("R'G'B' u8");
else
format = babl_format ("Y' u8");
depth = babl_format_get_bytes_per_pixel (format);
/* Write the image header */
PIX_DEBUG_PRINT ("Width %hu\n", width);
PIX_DEBUG_PRINT ("Height %hu\n", height);
if (! put_short (output, width, error) ||
! put_short (output, height, error) ||
! put_short (output, 0, error) ||
! put_short (output, 0, error))
{
cancellable = g_cancellable_new ();
g_cancellable_cancel (cancellable);
g_output_stream_close (output, cancellable, NULL);
g_object_unref (cancellable);
g_object_unref (output);
g_object_unref (buffer);
return FALSE;
}
tile_height = pika_tile_height ();
src_base = g_new (guchar, tile_height * width * depth);
if (savingColor)
{
/* Writing a 24-bit Pix image */
if (! put_short (output, 24, error))
goto fail;
for (i = 0; i < height;)
{
rectHeight = (tile_height < (height - i)) ?
tile_height : (height - i);
gegl_buffer_get (buffer, GEGL_RECTANGLE (0, i, width, rectHeight), 1.0,
format, src_base,
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
for (src = src_base, row = 0;
row < tile_height && i < height;
i += 1, row += 1)
{
/* Write a row of the image */
guchar record[4];
record[0] = 1;
record[3] = src[0];
record[2] = src[1];
record[1] = src[2];
src += depth;
for (j = 1; j < width; ++j)
{
if ((record[3] != src[0]) ||
(record[2] != src[1]) ||
(record[1] != src[2]) ||
(record[0] == 255))
{
/* Write current RLE record and start a new one */
if (! g_output_stream_write_all (output, record, 4,
NULL, NULL, error))
{
goto fail;
}
record[0] = 1;
record[3] = src[0];
record[2] = src[1];
record[1] = src[2];
}
else
{
/* increment run length in current record */
record[0]++;
}
src += depth;
}
/* Write last record in row */
if (! g_output_stream_write_all (output, record, 4,
NULL, NULL, error))
{
goto fail;
}
}
pika_progress_update ((double) i / (double) height);
}
}
else
{
/* Writing a 8-bit Matte (Mask) image */
if (! put_short (output, 8, error))
goto fail;
for (i = 0; i < height;)
{
rectHeight = (tile_height < (height - i)) ?
tile_height : (height - i);
gegl_buffer_get (buffer, GEGL_RECTANGLE (0, i, width, rectHeight), 1.0,
format, src_base,
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
for (src = src_base, row = 0;
row < tile_height && i < height;
i += 1, row += 1)
{
/* Write a row of the image */
guchar record[2];
record[0] = 1;
record[1] = src[0];
src += depth;
for (j = 1; j < width; ++j)
{
if ((record[1] != src[0]) || (record[0] == 255))
{
/* Write current RLE record and start a new one */
if (! g_output_stream_write_all (output, record, 2,
NULL, NULL, error))
{
goto fail;
}
record[0] = 1;
record[1] = src[0];
}
else
{
/* increment run length in current record */
record[0] ++;
}
src += depth;
}
/* Write last record in row */
if (! g_output_stream_write_all (output, record, 2,
NULL, NULL, error))
{
goto fail;
}
}
pika_progress_update ((double) i / (double) height);
}
}
g_free (src_base);
g_object_unref (output);
g_object_unref (buffer);
pika_progress_update (1.0);
return TRUE;
fail:
cancellable = g_cancellable_new ();
g_cancellable_cancel (cancellable);
g_output_stream_close (output, cancellable, NULL);
g_object_unref (cancellable);
g_free (src_base);
g_object_unref (output);
g_object_unref (buffer);
return FALSE;
}

2336
plug-ins/common/file-png.c Normal file

File diff suppressed because it is too large Load Diff

2260
plug-ins/common/file-pnm.c Normal file

File diff suppressed because it is too large Load Diff

4037
plug-ins/common/file-ps.c Normal file

File diff suppressed because it is too large Load Diff

2647
plug-ins/common/file-psp.c Normal file

File diff suppressed because it is too large Load Diff

400
plug-ins/common/file-qoi.c Normal file
View File

@ -0,0 +1,400 @@
/* PIKA - Photo and Image Kooker Application
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* Quite OK Image (QOI) plug-in
*
* Copyright (C) 2023 Alex S.
*
* 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/>.
*/
#include "config.h"
#include <string.h>
#include <errno.h>
#include <glib/gstdio.h>
#include <libpika/pika.h>
#include <libpika/pikaui.h>
#define QOI_IMPLEMENTATION
#include "qoi.h"
#include "libpika/stdplugins-intl.h"
#define LOAD_PROC "file-qoi-load"
#define SAVE_PROC "file-qoi-save"
#define PLUG_IN_BINARY "file-qoi"
#define PLUG_IN_ROLE "pika-file-qoi"
typedef struct _Qoi Qoi;
typedef struct _QoiClass QoiClass;
struct _Qoi
{
PikaPlugIn parent_instance;
};
struct _QoiClass
{
PikaPlugInClass parent_class;
};
#define QOI_TYPE (qoi_get_type ())
#define QOI (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), QOI_TYPE, Qoi))
GType qoi_get_type (void) G_GNUC_CONST;
static GList * qoi_query_procedures (PikaPlugIn *plug_in);
static PikaProcedure * qoi_create_procedure (PikaPlugIn *plug_in,
const gchar *name);
static PikaValueArray * qoi_load (PikaProcedure *procedure,
PikaRunMode run_mode,
GFile *file,
const PikaValueArray *args,
gpointer run_data);
static PikaValueArray * qoi_save (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
GFile *file,
const PikaValueArray *args,
gpointer run_data);
static PikaImage * load_image (GFile *file,
GObject *config,
PikaRunMode run_mode,
GError **error);
static gboolean save_image (GFile *file,
PikaImage *image,
PikaDrawable *drawable,
GError **error);
G_DEFINE_TYPE (Qoi, qoi, PIKA_TYPE_PLUG_IN)
PIKA_MAIN (QOI_TYPE)
DEFINE_STD_SET_I18N
static void
qoi_class_init (QoiClass *klass)
{
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
plug_in_class->query_procedures = qoi_query_procedures;
plug_in_class->create_procedure = qoi_create_procedure;
plug_in_class->set_i18n = STD_SET_I18N;
}
static void
qoi_init (Qoi *qoi)
{
}
static GList *
qoi_query_procedures (PikaPlugIn *plug_in)
{
GList *list = NULL;
list = g_list_append (list, g_strdup (LOAD_PROC));
list = g_list_append (list, g_strdup (SAVE_PROC));
return list;
}
static PikaProcedure *
qoi_create_procedure (PikaPlugIn *plug_in,
const gchar *name)
{
PikaProcedure *procedure = NULL;
if (! strcmp (name, LOAD_PROC))
{
procedure = pika_load_procedure_new (plug_in, name,
PIKA_PDB_PROC_TYPE_PLUGIN,
qoi_load, NULL, NULL);
pika_procedure_set_menu_label (procedure,
N_("Quite OK Image"));
pika_procedure_set_documentation (procedure,
_("Load file in the QOI file format"),
_("Load file in the QOI file format "
"(Quite OK Image)"),
name);
pika_procedure_set_attribution (procedure,
"Alex S.",
"Alex S.",
"2023");
pika_file_procedure_set_mime_types (PIKA_FILE_PROCEDURE (procedure),
"image/qoi");
pika_file_procedure_set_extensions (PIKA_FILE_PROCEDURE (procedure),
"qoi");
pika_file_procedure_set_magics (PIKA_FILE_PROCEDURE (procedure),
"0,string,qoif");
}
else if (! strcmp (name, SAVE_PROC))
{
procedure = pika_save_procedure_new (plug_in, name,
PIKA_PDB_PROC_TYPE_PLUGIN,
qoi_save, NULL, NULL);
pika_procedure_set_image_types (procedure, "*");
pika_procedure_set_menu_label (procedure, _("Quite OK Image"));
pika_procedure_set_documentation (procedure,
_("Export image in the QOI file format"),
_("Export image in the QOI file format "
"(Quite OK Image)"),
name);
pika_procedure_set_attribution (procedure,
"Alex S.",
"Alex S.",
"2023");
pika_file_procedure_set_mime_types (PIKA_FILE_PROCEDURE (procedure),
"image/qoi");
pika_file_procedure_set_extensions (PIKA_FILE_PROCEDURE (procedure),
"qoi");
}
return procedure;
}
static PikaValueArray *
qoi_load (PikaProcedure *procedure,
PikaRunMode run_mode,
GFile *file,
const PikaValueArray *args,
gpointer run_data)
{
PikaProcedureConfig *config;
PikaValueArray *return_vals;
PikaImage *image;
GError *error = NULL;
gegl_init (NULL, NULL);
config = pika_procedure_create_config (procedure);
pika_procedure_config_begin_run (config, NULL, run_mode, args);
image = load_image (file, G_OBJECT (config), run_mode, &error);
if (! image)
return pika_procedure_new_return_values (procedure,
PIKA_PDB_EXECUTION_ERROR,
error);
pika_procedure_config_end_run (config, PIKA_PDB_SUCCESS);
g_object_unref (config);
return_vals = pika_procedure_new_return_values (procedure,
PIKA_PDB_SUCCESS,
NULL);
PIKA_VALUES_SET_IMAGE (return_vals, 1, image);
return return_vals;
}
static PikaValueArray *
qoi_save (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
GFile *file,
const PikaValueArray *args,
gpointer run_data)
{
PikaPDBStatusType status = PIKA_PDB_SUCCESS;
PikaExportReturn export = PIKA_EXPORT_CANCEL;
GError *error = NULL;
gegl_init (NULL, NULL);
switch (run_mode)
{
case PIKA_RUN_INTERACTIVE:
case PIKA_RUN_WITH_LAST_VALS:
pika_ui_init (PLUG_IN_BINARY);
export = pika_export_image (&image, &n_drawables, &drawables, "qoi",
PIKA_EXPORT_CAN_HANDLE_RGB |
PIKA_EXPORT_CAN_HANDLE_GRAY |
PIKA_EXPORT_CAN_HANDLE_INDEXED |
PIKA_EXPORT_CAN_HANDLE_ALPHA);
if (export == PIKA_EXPORT_CANCEL)
return pika_procedure_new_return_values (procedure,
PIKA_PDB_CANCEL,
NULL);
break;
default:
break;
}
if (n_drawables != 1)
{
g_set_error (&error, G_FILE_ERROR, 0,
_("QOI format does not support multiple layers."));
return pika_procedure_new_return_values (procedure,
PIKA_PDB_CALLING_ERROR,
error);
}
if (! save_image (file, image, drawables[0], &error))
status = PIKA_PDB_EXECUTION_ERROR;
if (export == PIKA_EXPORT_EXPORT)
{
pika_image_delete (image);
g_free (drawables);
}
return pika_procedure_new_return_values (procedure, status, error);
}
static PikaImage *
load_image (GFile *file,
GObject *config,
PikaRunMode run_mode,
GError **error)
{
PikaImage *image = NULL;
PikaLayer *layer;
GeglBuffer *buffer;
qoi_desc desc;
void *pixels = NULL;
const Babl *format = NULL;
FILE *fp;
fp = g_fopen (g_file_peek_path (file), "rb");
if (! fp)
{
g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
_("Could not open '%s' for reading: %s"),
pika_file_get_utf8_name (file), g_strerror (errno));
return NULL;
}
fclose (fp);
pixels = qoi_read (g_file_peek_path (file), &desc, 0);
if (! pixels)
{
g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
_("Failed to read QOI file"));
return NULL;
}
image = pika_image_new_with_precision (desc.width, desc.height, PIKA_RGB,
desc.colorspace ?
PIKA_PRECISION_U8_LINEAR :
PIKA_PRECISION_U8_NON_LINEAR);
layer = pika_layer_new (image, _("Background"), desc.width, desc.height,
(desc.channels == 4) ? PIKA_RGBA_IMAGE : PIKA_RGB_IMAGE,
100, pika_image_get_default_new_layer_mode (image));
pika_image_insert_layer (image, layer, NULL, 0);
format = babl_format ((desc.channels == 4) ? "R'G'B'A u8" : "R'G'B' u8");
buffer = pika_drawable_get_buffer (PIKA_DRAWABLE (layer));
gegl_buffer_set (buffer, GEGL_RECTANGLE (0, 0, desc.width, desc.height), 0,
format, pixels, GEGL_AUTO_ROWSTRIDE);
g_object_unref (buffer);
g_free (pixels);
return image;
}
static gboolean
save_image (GFile *file,
PikaImage *image,
PikaDrawable *drawable,
GError **error)
{
GeglBuffer *buffer;
const Babl *format;
qoi_desc desc;
void *pixels = NULL;
gboolean has_alpha;
gint success;
has_alpha = pika_drawable_has_alpha (drawable);
buffer = pika_drawable_get_buffer (drawable);
desc.width = gegl_buffer_get_width (buffer);
desc.height = gegl_buffer_get_height (buffer);
desc.channels = has_alpha ? 4 : 3;
switch (pika_image_get_precision (image))
{
case PIKA_PRECISION_U8_LINEAR:
case PIKA_PRECISION_U16_LINEAR:
case PIKA_PRECISION_U32_LINEAR:
case PIKA_PRECISION_HALF_LINEAR:
case PIKA_PRECISION_FLOAT_LINEAR:
case PIKA_PRECISION_DOUBLE_LINEAR:
desc.colorspace = QOI_LINEAR;
break;
default:
desc.colorspace = QOI_SRGB;
break;
}
pika_progress_init_printf (_("Exporting '%s'"),
pika_file_get_utf8_name (file));
format = babl_format (has_alpha ? "R'G'B'A u8" : "R'G'B' u8");
pixels = (guchar *) g_malloc (desc.width * desc.height * desc.channels);
gegl_buffer_get (buffer, GEGL_RECTANGLE (0, 0, desc.width, desc.height),
1.0, format, pixels,
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
success = qoi_write (g_file_peek_path (file), pixels, &desc);
g_object_unref (buffer);
g_free (pixels);
if (! success)
{
g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
_("Writing to file '%s' failed: %s"),
pika_file_get_utf8_name (file), g_strerror (errno));
return FALSE;
}
return TRUE;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

1083
plug-ins/common/file-svg.c Normal file

File diff suppressed because it is too large Load Diff

1481
plug-ins/common/file-tga.c Normal file

File diff suppressed because it is too large Load Diff

321
plug-ins/common/file-wbmp.c Normal file
View File

@ -0,0 +1,321 @@
/*
* PIKA - Photo and Image Kooker Application
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* 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/>.
*/
#include "config.h"
#include <errno.h>
#include <string.h>
#include <glib/gstdio.h>
#include <libpika/pika.h>
#include <libpika/pikaui.h>
#include "libpika/stdplugins-intl.h"
#define LOAD_PROC "file-wbmp-load"
#define PLUG_IN_BINARY "file-wbmp"
#define PLUG_IN_ROLE "pika-file-wbmp"
#define ReadOK(file,buffer,len) (fread(buffer, len, 1, file) != 0)
typedef struct _Wbmp Wbmp;
typedef struct _WbmpClass WbmpClass;
struct _Wbmp
{
PikaPlugIn parent_instance;
};
struct _WbmpClass
{
PikaPlugInClass parent_class;
};
#define WBMP_TYPE (wbmp_get_type ())
#define WBMP (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), WBMP_TYPE, Wbmp))
GType wbmp_get_type (void) G_GNUC_CONST;
static GList * wbmp_query_procedures (PikaPlugIn *plug_in);
static PikaProcedure * wbmp_create_procedure (PikaPlugIn *plug_in,
const gchar *name);
static PikaValueArray * wbmp_load (PikaProcedure *procedure,
PikaRunMode run_mode,
GFile *file,
const PikaValueArray *args,
gpointer run_data);
PikaImage * load_image (GFile *file,
GError **error);
static PikaImage * read_image (FILE *fd,
GFile *file,
gint width,
gint height,
GError **error);
G_DEFINE_TYPE (Wbmp, wbmp, PIKA_TYPE_PLUG_IN)
PIKA_MAIN (WBMP_TYPE)
DEFINE_STD_SET_I18N
static void
wbmp_class_init (WbmpClass *klass)
{
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
plug_in_class->query_procedures = wbmp_query_procedures;
plug_in_class->create_procedure = wbmp_create_procedure;
plug_in_class->set_i18n = STD_SET_I18N;
}
static void
wbmp_init (Wbmp *wmp)
{
}
static GList *
wbmp_query_procedures (PikaPlugIn *plug_in)
{
GList *list = NULL;
list = g_list_append (list, g_strdup (LOAD_PROC));
return list;
}
static PikaProcedure *
wbmp_create_procedure (PikaPlugIn *plug_in,
const gchar *name)
{
PikaProcedure *procedure = NULL;
if (! strcmp (name, LOAD_PROC))
{
procedure = pika_load_procedure_new (plug_in, name,
PIKA_PDB_PROC_TYPE_PLUGIN,
wbmp_load, NULL, NULL);
pika_procedure_set_menu_label (procedure, _("Wireless BMP image"));
pika_procedure_set_documentation (procedure,
_("Loads files of Wireless BMP file format"),
_("Loads files of Wireless BMP file format"),
name);
pika_procedure_set_attribution (procedure,
"Kevin Toyle",
"Kevin Toyle",
"2022");
pika_file_procedure_set_mime_types (PIKA_FILE_PROCEDURE (procedure),
"image/vnd.wap.wbmp");
pika_file_procedure_set_extensions (PIKA_FILE_PROCEDURE (procedure),
"wbmp");
}
return procedure;
}
static PikaValueArray *
wbmp_load (PikaProcedure *procedure,
PikaRunMode run_mode,
GFile *file,
const PikaValueArray *args,
gpointer run_data)
{
PikaValueArray *return_vals;
PikaImage *image;
GError *error = NULL;
gegl_init (NULL, NULL);
image = load_image (file, &error);
if (! image)
return pika_procedure_new_return_values (procedure,
PIKA_PDB_EXECUTION_ERROR,
error);
return_vals = pika_procedure_new_return_values (procedure,
PIKA_PDB_SUCCESS,
NULL);
PIKA_VALUES_SET_IMAGE (return_vals, 1, image);
return return_vals;
}
PikaImage *
load_image (GFile *file,
GError **error)
{
FILE *fd;
PikaImage *image = NULL;
gint width = 0;
gint height = 0;
gint8 magic;
guchar value;
pika_progress_init_printf (_("Opening '%s'"),
pika_file_get_utf8_name (file));
fd = g_fopen (g_file_peek_path (file), "rb");
if (! fd)
{
g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
_("Could not open '%s' for reading: %s"),
pika_file_get_utf8_name (file), g_strerror (errno));
goto out;
}
/* Checking the type field to make sure it is 0. */
if (! ReadOK (fd, &magic, 1) || magic != 0)
{
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
_("'%s': Invalid WBMP type value"),
pika_file_get_utf8_name (file));
goto out;
}
/* Checking the fixed header field to make sure it is 0 */
if (! ReadOK (fd, &magic, 1) || magic != 0)
{
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
_("'%s': Unsupported WBMP fixed header value"),
pika_file_get_utf8_name (file));
goto out;
}
/* The width and height are stored as uintvar values */
while (ReadOK (fd, &value, 1))
{
width = (width << 7) | (value & 0x7F);
if (value >> 7 != 1)
break;
}
while (ReadOK (fd, &value, 1))
{
height = (height << 7) | (value & 0x7F);
if (value >> 7 != 1)
break;
}
if (width <= 0 || height <= 0)
{
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
_("'%s' is not a valid WBMP file"),
pika_file_get_utf8_name (file));
goto out;
}
image = read_image (fd, file, width, height, error);
out:
if (fd)
fclose (fd);
return image;
}
/* Code referenced from /plug-ins/file-bmp/bmp-load.c */
static PikaImage *
read_image (FILE *fd,
GFile *file,
gint width,
gint height,
GError **error)
{
const guchar mono[6] = { 0, 0, 0, 255, 255, 255 };
guchar v;
gint xpos = 0;
gint ypos = 0;
PikaImage *image;
PikaLayer *layer;
GeglBuffer *buffer;
guchar *dest, *temp;
gint i, cur_progress, max_progress;
/* Make a new image in PIKA */
if ((width < 0) || (width > PIKA_MAX_IMAGE_SIZE))
{
g_message (_("Unsupported or invalid image width: %d"), width);
return NULL;
}
if ((height < 0) || (height > PIKA_MAX_IMAGE_SIZE))
{
g_message (_("Unsupported or invalid image height: %d"), height);
return NULL;
}
image = pika_image_new (width, height, PIKA_INDEXED);
layer = pika_layer_new (image, _("Background"), width, height,
PIKA_INDEXED_IMAGE, 100,
pika_image_get_default_new_layer_mode (image));
pika_image_set_colormap (image, mono, 2);
pika_image_insert_layer (image, layer, NULL, 0);
dest = g_malloc0 (width * height);
ypos = 0;
cur_progress = 0;
max_progress = height;
while (ReadOK (fd, &v, 1))
{
for (i = 1; (i <= 8) && (xpos < width); i++, xpos++)
{
temp = dest + (ypos * width) + xpos;
*temp = (v & (((1 << 1) - 1) << (8 - i))) >> (8 - i);
}
if (xpos == width)
{
if (ypos == height - 1)
break;
ypos++;
xpos = 0;
cur_progress++;
if ((cur_progress % 5) == 0)
pika_progress_update ((gdouble) cur_progress / (gdouble) max_progress);
}
if (ypos > height - 1)
break;
}
buffer = pika_drawable_get_buffer (PIKA_DRAWABLE (layer));
gegl_buffer_set (buffer, GEGL_RECTANGLE (0, 0, width, height), 0, NULL, dest,
GEGL_AUTO_ROWSTRIDE);
g_object_unref (buffer);
g_free (dest);
return image;
}

1064
plug-ins/common/file-wmf.c Normal file

File diff suppressed because it is too large Load Diff

1324
plug-ins/common/file-xbm.c Normal file

File diff suppressed because it is too large Load Diff

2318
plug-ins/common/file-xmc.c Normal file

File diff suppressed because it is too large Load Diff

905
plug-ins/common/file-xpm.c Normal file
View File

@ -0,0 +1,905 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* 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/>.
*/
/* XPM plugin version 1.2.7 */
/*
1.2.7 fixes saving unused transparency (bug #4560)
1.2.6 fixes crash when saving indexed images (bug #109567)
1.2.5 only creates a "None" color entry if the image has alpha (bug #108034)
1.2.4 displays an error message if saving fails (bug #87588)
1.2.3 fixes bug when running in noninteractive mode
changes alpha_threshold range from [0, 1] to [0,255] for consistency with
the threshold_alpha plugin
1.2.2 fixes bug that generated bad digits on images with more than 20000
colors. (thanks, yanele)
parses gtkrc (thanks, yosh)
doesn't load parameter screen on images that don't have alpha
1.2.1 fixes some minor bugs -- spaces in #XXXXXX strings, small typos in code.
1.2 compute color indexes so that we don't have to use XpmSaveXImage*
Previous...Inherited code from Ray Lehtiniemi, who inherited it from S & P.
*/
#include "config.h"
#include <string.h>
#include <glib/gstdio.h>
#include <gdk/gdk.h> /* For GDK_WINDOWING_WIN32 */
#ifndef GDK_WINDOWING_X11
#ifndef XPM_NO_X
#define XPM_NO_X
#endif
#else
#include <X11/Xlib.h>
#endif
#include <X11/xpm.h>
#include <libpika/pika.h>
#include <libpika/pikaui.h>
#include "libpika/stdplugins-intl.h"
#define LOAD_PROC "file-xpm-load"
#define SAVE_PROC "file-xpm-save"
#define PLUG_IN_BINARY "file-xpm"
#define PLUG_IN_ROLE "pika-file-xpm"
#define SCALE_WIDTH 125
typedef struct
{
guchar r;
guchar g;
guchar b;
} rgbkey;
typedef struct _Xpm Xpm;
typedef struct _XpmClass XpmClass;
struct _Xpm
{
PikaPlugIn parent_instance;
};
struct _XpmClass
{
PikaPlugInClass parent_class;
};
#define XPM_TYPE (xpm_get_type ())
#define XPM (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), XPM_TYPE, Xpm))
GType xpm_get_type (void) G_GNUC_CONST;
static GList * xpm_query_procedures (PikaPlugIn *plug_in);
static PikaProcedure * xpm_create_procedure (PikaPlugIn *plug_in,
const gchar *name);
static PikaValueArray * xpm_load (PikaProcedure *procedure,
PikaRunMode run_mode,
GFile *file,
const PikaValueArray *args,
gpointer run_data);
static PikaValueArray * xpm_save (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
GFile *file,
const PikaValueArray *args,
gpointer run_data);
static PikaImage * load_image (GFile *file,
GError **error);
static guchar * parse_colors (XpmImage *xpm_image);
static void parse_image (PikaImage *image,
XpmImage *xpm_image,
guchar *cmap);
static gboolean save_image (GFile *file,
PikaImage *image,
PikaDrawable *drawable,
GObject *config,
GError **error);
static gboolean save_dialog (PikaImage *image,
PikaProcedure *procedure,
GObject *config);
G_DEFINE_TYPE (Xpm, xpm, PIKA_TYPE_PLUG_IN)
PIKA_MAIN (XPM_TYPE)
DEFINE_STD_SET_I18N
static const gchar linenoise [] =
" .+@#$%&*=-;>,')!~{]^/(_:<[}|1234567890abcdefghijklmnopqrstuvwxyz\
ABCDEFGHIJKLMNOPQRSTUVWXYZ`";
/* whether the image is color or not. global so I only have to pass
* one user value to the GHFunc
*/
static gboolean color;
/* bytes per pixel. global so I only have to pass one user value
* to the GHFunc
*/
static gint cpp;
static void
xpm_class_init (XpmClass *klass)
{
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
plug_in_class->query_procedures = xpm_query_procedures;
plug_in_class->create_procedure = xpm_create_procedure;
plug_in_class->set_i18n = STD_SET_I18N;
}
static void
xpm_init (Xpm *xpm)
{
}
static GList *
xpm_query_procedures (PikaPlugIn *plug_in)
{
GList *list = NULL;
list = g_list_append (list, g_strdup (LOAD_PROC));
list = g_list_append (list, g_strdup (SAVE_PROC));
return list;
}
static PikaProcedure *
xpm_create_procedure (PikaPlugIn *plug_in,
const gchar *name)
{
PikaProcedure *procedure = NULL;
if (! strcmp (name, LOAD_PROC))
{
procedure = pika_load_procedure_new (plug_in, name,
PIKA_PDB_PROC_TYPE_PLUGIN,
xpm_load, NULL, NULL);
pika_procedure_set_menu_label (procedure, _("X PixMap image"));
pika_procedure_set_documentation (procedure,
"Load files in XPM (X11 Pixmap) format.",
"Load files in XPM (X11 Pixmap) format. "
"XPM is a portable image format "
"designed to be included in C source "
"code. XLib provides utility functions "
"to read this format. Newer code should "
"however be using gdk-pixbuf-csource "
"instead. XPM supports colored images, "
"unlike the XBM format which XPM was "
"designed to replace.",
name);
pika_procedure_set_attribution (procedure,
"Spencer Kimball & Peter Mattis & "
"Ray Lehtiniemi",
"Spencer Kimball & Peter Mattis",
"1997");
pika_file_procedure_set_mime_types (PIKA_FILE_PROCEDURE (procedure),
"image/x-pixmap");
pika_file_procedure_set_extensions (PIKA_FILE_PROCEDURE (procedure),
"xpm");
pika_file_procedure_set_magics (PIKA_FILE_PROCEDURE (procedure),
"0, string,/*\\040XPM\\040*/");
}
else if (! strcmp (name, SAVE_PROC))
{
procedure = pika_save_procedure_new (plug_in, name,
PIKA_PDB_PROC_TYPE_PLUGIN,
xpm_save, NULL, NULL);
pika_procedure_set_image_types (procedure, "*");
pika_procedure_set_menu_label (procedure, _("X PixMap image"));
pika_procedure_set_documentation (procedure,
"Export files in XPM (X11 Pixmap) format.",
"Export files in XPM (X11 Pixmap) format. "
"XPM is a portable image format "
"designed to be included in C source "
"code. XLib provides utility functions "
"to read this format. Newer code should "
"however be using gdk-pixbuf-csource "
"instead. XPM supports colored images, "
"unlike the XBM format which XPM was "
"designed to replace.",
name);
pika_procedure_set_attribution (procedure,
"Spencer Kimball & Peter Mattis & "
"Ray Lehtiniemi & Nathan Summers",
"Spencer Kimball & Peter Mattis",
"1997");
pika_file_procedure_set_format_name (PIKA_FILE_PROCEDURE (procedure),
_("XPM"));
pika_file_procedure_set_mime_types (PIKA_FILE_PROCEDURE (procedure),
"image/x-pixmap");
pika_file_procedure_set_extensions (PIKA_FILE_PROCEDURE (procedure),
"xpm");
PIKA_PROC_ARG_INT (procedure, "threshold",
_("_Threshold"),
_("Alpha threshold"),
0, 255, 127,
G_PARAM_READWRITE);
}
return procedure;
}
static PikaValueArray *
xpm_load (PikaProcedure *procedure,
PikaRunMode run_mode,
GFile *file,
const PikaValueArray *args,
gpointer run_data)
{
PikaValueArray *return_vals;
PikaImage *image;
GError *error = NULL;
gegl_init (NULL, NULL);
image = load_image (file, &error);
if (! image)
return pika_procedure_new_return_values (procedure,
PIKA_PDB_EXECUTION_ERROR,
error);
return_vals = pika_procedure_new_return_values (procedure,
PIKA_PDB_SUCCESS,
NULL);
PIKA_VALUES_SET_IMAGE (return_vals, 1, image);
return return_vals;
}
static PikaValueArray *
xpm_save (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
GFile *file,
const PikaValueArray *args,
gpointer run_data)
{
PikaProcedureConfig *config;
PikaPDBStatusType status = PIKA_PDB_SUCCESS;
PikaExportReturn export = PIKA_EXPORT_CANCEL;
GError *error = NULL;
gegl_init (NULL, NULL);
config = pika_procedure_create_config (procedure);
pika_procedure_config_begin_run (config, image, run_mode, args);
switch (run_mode)
{
case PIKA_RUN_INTERACTIVE:
case PIKA_RUN_WITH_LAST_VALS:
pika_ui_init (PLUG_IN_BINARY);
export = pika_export_image (&image, &n_drawables, &drawables, "XPM",
PIKA_EXPORT_CAN_HANDLE_RGB |
PIKA_EXPORT_CAN_HANDLE_GRAY |
PIKA_EXPORT_CAN_HANDLE_INDEXED |
PIKA_EXPORT_CAN_HANDLE_ALPHA);
if (export == PIKA_EXPORT_CANCEL)
return pika_procedure_new_return_values (procedure,
PIKA_PDB_CANCEL,
NULL);
break;
default:
break;
}
if (n_drawables != 1)
{
g_set_error (&error, G_FILE_ERROR, 0,
_("XPM format does not support multiple layers."));
return pika_procedure_new_return_values (procedure,
PIKA_PDB_CALLING_ERROR,
error);
}
if (run_mode == PIKA_RUN_INTERACTIVE)
{
if (pika_drawable_has_alpha (drawables[0]))
if (! save_dialog (image, procedure, G_OBJECT (config)))
status = PIKA_PDB_CANCEL;
}
if (status == PIKA_PDB_SUCCESS)
{
if (! save_image (file, image, drawables[0], G_OBJECT (config),
&error))
{
status = PIKA_PDB_EXECUTION_ERROR;
}
}
pika_procedure_config_end_run (config, status);
g_object_unref (config);
if (export == PIKA_EXPORT_EXPORT)
{
pika_image_delete (image);
g_free (drawables);
}
return pika_procedure_new_return_values (procedure, status, error);
}
static PikaImage *
load_image (GFile *file,
GError **error)
{
XpmImage xpm_image;
guchar *cmap;
PikaImage *image;
pika_progress_init_printf (_("Opening '%s'"),
pika_file_get_utf8_name (file));
/* read the raw file */
switch (XpmReadFileToXpmImage (g_file_peek_path (file), &xpm_image, NULL))
{
case XpmSuccess:
break;
case XpmOpenFailed:
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
_("Error opening file '%s'"),
pika_file_get_utf8_name (file));
return NULL;
case XpmFileInvalid:
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
"%s", _("XPM file invalid"));
return NULL;
default:
return NULL;
}
cmap = parse_colors (&xpm_image);
image = pika_image_new (xpm_image.width,
xpm_image.height,
PIKA_RGB);
/* fill it */
parse_image (image, &xpm_image, cmap);
g_free (cmap);
return image;
}
static guchar *
parse_colors (XpmImage *xpm_image)
{
#ifndef XPM_NO_X
Display *display;
Colormap colormap;
#endif
gint i, j;
guchar *cmap;
#ifndef XPM_NO_X
/* open the display and get the default color map */
display = XOpenDisplay (NULL);
if (display == NULL)
g_printerr ("Could not open display\n");
colormap = DefaultColormap (display, DefaultScreen (display));
#endif
/* alloc a buffer to hold the parsed colors */
cmap = g_new0 (guchar, 4 * xpm_image->ncolors);
/* parse each color in the file */
for (i = 0, j = 0; i < xpm_image->ncolors; i++)
{
gchar *colorspec = "None";
XpmColor *xpm_color;
#ifndef XPM_NO_X
XColor xcolor;
#else
GdkRGBA xcolor;
#endif
xpm_color = &(xpm_image->colorTable[i]);
/* pick the best spec available */
if (xpm_color->c_color)
colorspec = xpm_color->c_color;
else if (xpm_color->g_color)
colorspec = xpm_color->g_color;
else if (xpm_color->g4_color)
colorspec = xpm_color->g4_color;
else if (xpm_color->m_color)
colorspec = xpm_color->m_color;
/* parse if it's not transparent */
if (strcmp (colorspec, "None") != 0)
{
#ifndef XPM_NO_X
XParseColor (display, colormap, colorspec, &xcolor);
cmap[j++] = xcolor.red >> 8;
cmap[j++] = xcolor.green >> 8;
cmap[j++] = xcolor.blue >> 8;
#else
gdk_rgba_parse (&xcolor, colorspec);
cmap[j++] = CLAMP (xcolor.red * G_MAXUINT8, 0, G_MAXUINT8);
cmap[j++] = CLAMP (xcolor.green * G_MAXUINT8, 0, G_MAXUINT8);
cmap[j++] = CLAMP (xcolor.blue * G_MAXUINT8, 0, G_MAXUINT8);
#endif
cmap[j++] = ~0;
}
else
{
j += 4;
}
}
#ifndef XPM_NO_X
XCloseDisplay (display);
#endif
return cmap;
}
static void
parse_image (PikaImage *image,
XpmImage *xpm_image,
guchar *cmap)
{
GeglBuffer *buffer;
gint tile_height;
gint scanlines;
gint val;
guchar *buf;
guchar *dest;
guint *src;
PikaLayer *layer;
gint i;
layer = pika_layer_new (image,
_("Color"),
xpm_image->width,
xpm_image->height,
PIKA_RGBA_IMAGE,
100,
pika_image_get_default_new_layer_mode (image));
pika_image_insert_layer (image, layer, NULL, 0);
buffer = pika_drawable_get_buffer (PIKA_DRAWABLE (layer));
tile_height = pika_tile_height ();
buf = g_new (guchar, tile_height * xpm_image->width * 4);
src = xpm_image->data;
for (i = 0; i < xpm_image->height; i += tile_height)
{
gint j;
dest = buf;
scanlines = MIN (tile_height, xpm_image->height - i);
j = scanlines * xpm_image->width;
while (j--)
{
{
val = *(src++) * 4;
*(dest) = cmap[val];
*(dest+1) = cmap[val+1];
*(dest+2) = cmap[val+2];
*(dest+3) = cmap[val+3];
dest += 4;
}
if ((j % 100) == 0)
pika_progress_update ((double) i / (double) xpm_image->height);
}
gegl_buffer_set (buffer,
GEGL_RECTANGLE (0, i, xpm_image->width, scanlines), 0,
NULL, buf, GEGL_AUTO_ROWSTRIDE);
}
g_free (buf);
g_object_unref (buffer);
pika_progress_update (1.0);
}
static guint
rgbhash (rgbkey *c)
{
return ((guint)c->r) ^ ((guint)c->g) ^ ((guint)c->b);
}
static guint
compare (rgbkey *c1,
rgbkey *c2)
{
return (c1->r == c2->r) && (c1->g == c2->g) && (c1->b == c2->b);
}
static void
set_XpmImage (XpmColor *array,
guint index,
gchar *colorstring)
{
gchar *p;
gint i, charnum, indtemp;
indtemp=index;
array[index].string = p = g_new (gchar, cpp+1);
/*convert the index number to base sizeof(linenoise)-1 */
for (i = 0; i < cpp; ++i)
{
charnum = indtemp % (sizeof (linenoise) - 1);
indtemp = indtemp / (sizeof (linenoise) - 1);
*p++ = linenoise[charnum];
}
/* *p++=linenoise[indtemp]; */
*p = '\0'; /* C and its stupid null-terminated strings... */
array[index].symbolic = NULL;
array[index].m_color = NULL;
array[index].g4_color = NULL;
if (color)
{
array[index].g_color = NULL;
array[index].c_color = colorstring;
}
else
{
array[index].c_color = NULL;
array[index].g_color = colorstring;
}
}
static void
create_colormap_from_hash (gpointer gkey,
gpointer value,
gpointer user_data)
{
rgbkey *key = gkey;
gchar *string = g_new (gchar, 8);
sprintf (string, "#%02X%02X%02X", (int)key->r, (int)key->g, (int)key->b);
set_XpmImage (user_data, *((int *) value), string);
}
static void
decrement_hash_values (gpointer gkey,
gpointer value,
gpointer user_data)
{
--(*((guint*) value));
}
static gboolean
save_image (GFile *file,
PikaImage *image,
PikaDrawable *drawable,
GObject *config,
GError **error)
{
GeglBuffer *buffer;
const Babl *format;
gint width;
gint height;
gint ncolors = 1;
gint *indexno;
gboolean indexed;
gboolean alpha;
gboolean alpha_used = FALSE;
XpmColor *colormap;
XpmImage *xpm_image;
guint *ibuff = NULL;
guchar *buf;
guchar *data;
GHashTable *hash = NULL;
gint i, j, k;
gint threshold;
gboolean success = FALSE;
g_object_get (config,
"threshold", &threshold,
NULL);
buffer = pika_drawable_get_buffer (drawable);
width = gegl_buffer_get_width (buffer);
height = gegl_buffer_get_height (buffer);
alpha = pika_drawable_has_alpha (drawable);
color = ! pika_drawable_is_gray (drawable);
indexed = pika_drawable_is_indexed (drawable);
switch (pika_drawable_type (drawable))
{
case PIKA_RGB_IMAGE:
format = babl_format ("R'G'B' u8");
break;
case PIKA_RGBA_IMAGE:
format = babl_format ("R'G'B'A u8");
break;
case PIKA_GRAY_IMAGE:
format = babl_format ("Y' u8");
break;
case PIKA_GRAYA_IMAGE:
format = babl_format ("Y'A u8");
break;
case PIKA_INDEXED_IMAGE:
case PIKA_INDEXEDA_IMAGE:
format = gegl_buffer_get_format (buffer);
break;
default:
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
_("Unsupported drawable type"));
g_object_unref (buffer);
return FALSE;
}
/* allocate buffer making the assumption that ibuff is 32 bit aligned... */
ibuff = g_new (guint, width * height);
hash = g_hash_table_new ((GHashFunc) rgbhash, (GCompareFunc) compare);
pika_progress_init_printf (_("Exporting '%s'"),
pika_file_get_utf8_name (file));
ncolors = alpha ? 1 : 0;
/* allocate a pixel region to work with */
buf = g_new (guchar,
pika_tile_height () * width *
babl_format_get_bytes_per_pixel (format));
/* process each row of tiles */
for (i = 0; i < height; i += pika_tile_height ())
{
gint scanlines;
/* read the next row of tiles */
scanlines = MIN (pika_tile_height(), height - i);
gegl_buffer_get (buffer, GEGL_RECTANGLE (0, i, width, scanlines), 1.0,
format, buf,
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
data = buf;
/* process each pixel row */
for (j = 0; j < scanlines; j++)
{
/* go to the start of this row in each image */
guint *idata = ibuff + (i+j) * width;
/* do each pixel in the row */
for (k = 0; k < width; k++)
{
rgbkey *key = g_new (rgbkey, 1);
guchar a;
/* get pixel data */
key->r = *(data++);
key->g = color && !indexed ? *(data++) : key->r;
key->b = color && !indexed ? *(data++) : key->r;
a = alpha ? *(data++) : 255;
if (a < threshold)
{
*(idata++) = 0;
alpha_used = TRUE;
}
else
{
if (indexed)
{
*(idata++) = (key->r) + (alpha ? 1 : 0);
}
else
{
indexno = g_hash_table_lookup (hash, key);
if (!indexno)
{
indexno = g_new (gint, 1);
*indexno = ncolors++;
g_hash_table_insert (hash, key, indexno);
key = g_new (rgbkey, 1);
}
*(idata++) = *indexno;
}
}
}
/* kick the progress bar */
pika_progress_update ((gdouble) (i+j) / (gdouble) height);
}
}
g_free (buf);
/* remove alpha if not actually used */
if (alpha && !alpha_used)
{
gint i;
--ncolors;
for (i = 0; i < width * height; ++i)
--ibuff[i];
g_hash_table_foreach (hash, decrement_hash_values, NULL);
}
if (indexed)
{
guchar *cmap = pika_image_get_colormap (image, NULL, &ncolors);
guchar *c;
c = cmap;
if (alpha_used)
ncolors++;
colormap = g_new (XpmColor, ncolors);
cpp =
1 + (gdouble) log (ncolors) / (gdouble) log (sizeof (linenoise) - 1.0);
if (alpha_used)
set_XpmImage (colormap, 0, "None");
for (i = alpha_used ? 1 : 0; i < ncolors; i++)
{
gchar *string;
guchar r, g, b;
r = *c++;
g = *c++;
b = *c++;
string = g_new (gchar, 8);
sprintf (string, "#%02X%02X%02X", (int)r, (int)g, (int)b);
set_XpmImage (colormap, i, string);
}
g_free (cmap);
}
else
{
colormap = g_new (XpmColor, ncolors);
cpp =
1 + (gdouble) log (ncolors) / (gdouble) log (sizeof (linenoise) - 1.0);
if (alpha_used)
set_XpmImage (colormap, 0, "None");
g_hash_table_foreach (hash, create_colormap_from_hash, colormap);
}
xpm_image = g_new (XpmImage, 1);
xpm_image->width = width;
xpm_image->height = height;
xpm_image->ncolors = ncolors;
xpm_image->cpp = cpp;
xpm_image->colorTable = colormap;
xpm_image->data = ibuff;
/* do the save */
switch (XpmWriteFileFromXpmImage (g_file_peek_path (file), xpm_image, NULL))
{
case XpmSuccess:
success = TRUE;
break;
case XpmOpenFailed:
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
_("Error opening file '%s'"),
pika_file_get_utf8_name (file));
break;
case XpmFileInvalid:
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
"%s", _("XPM file invalid"));
break;
default:
break;
}
g_object_unref (buffer);
g_free (ibuff);
if (hash)
g_hash_table_destroy (hash);
pika_progress_update (1.0);
return success;
}
static gboolean
save_dialog (PikaImage *image,
PikaProcedure *procedure,
GObject *config)
{
GtkWidget *dialog;
gboolean run;
dialog = pika_save_procedure_dialog_new (PIKA_SAVE_PROCEDURE (procedure),
PIKA_PROCEDURE_CONFIG (config),
image);
pika_procedure_dialog_get_scale_entry (PIKA_PROCEDURE_DIALOG (dialog),
"threshold", 1.0);
pika_procedure_dialog_fill (PIKA_PROCEDURE_DIALOG (dialog), NULL);
gtk_widget_show (dialog);
run = pika_procedure_dialog_run (PIKA_PROCEDURE_DIALOG (dialog));
gtk_widget_destroy (dialog);
return run;
}

2767
plug-ins/common/file-xwd.c Normal file

File diff suppressed because it is too large Load Diff

1446
plug-ins/common/film.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,487 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* Gradient Map plug-in
* Copyright (C) 1997 Eiichi Takamori <taka@ma1.seikyou.ne.jp>
*
* 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/>.
*/
#include "config.h"
#include <string.h>
#include <libpika/pika.h>
#include "libpika/stdplugins-intl.h"
/* Some useful macros */
#define GRADMAP_PROC "plug-in-gradmap"
#define PALETTEMAP_PROC "plug-in-palettemap"
#define PLUG_IN_BINARY "gradient-map"
#define PLUG_IN_ROLE "pika-gradient-map"
#define NSAMPLES 2048
typedef enum
{
GRADIENT_MODE = 1,
PALETTE_MODE
} MapMode;
typedef struct _Map Map;
typedef struct _MapClass MapClass;
struct _Map
{
PikaPlugIn parent_instance;
};
struct _MapClass
{
PikaPlugInClass parent_class;
};
#define MAP_TYPE (map_get_type ())
#define MAP (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MAP_TYPE, Map))
GType map_get_type (void) G_GNUC_CONST;
static GList * map_query_procedures (PikaPlugIn *plug_in);
static PikaProcedure * map_create_procedure (PikaPlugIn *plug_in,
const gchar *name);
static PikaValueArray * map_run (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
const PikaValueArray *args,
gpointer run_data);
static void map (GeglBuffer *buffer,
GeglBuffer *shadow_buffer,
PikaDrawable *drawable,
MapMode mode);
static gdouble * get_samples_gradient (PikaDrawable *drawable);
static gdouble * get_samples_palette (PikaDrawable *drawable);
G_DEFINE_TYPE (Map, map, PIKA_TYPE_PLUG_IN)
PIKA_MAIN (MAP_TYPE)
DEFINE_STD_SET_I18N
static void
map_class_init (MapClass *klass)
{
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
plug_in_class->query_procedures = map_query_procedures;
plug_in_class->create_procedure = map_create_procedure;
plug_in_class->set_i18n = STD_SET_I18N;
}
static void
map_init (Map *map)
{
}
static GList *
map_query_procedures (PikaPlugIn *plug_in)
{
GList *list = NULL;
list = g_list_append (list, g_strdup (GRADMAP_PROC));
list = g_list_append (list, g_strdup (PALETTEMAP_PROC));
return list;
}
static PikaProcedure *
map_create_procedure (PikaPlugIn *plug_in,
const gchar *name)
{
PikaProcedure *procedure = NULL;
if (! strcmp (name, GRADMAP_PROC))
{
procedure = pika_image_procedure_new (plug_in, name,
PIKA_PDB_PROC_TYPE_PLUGIN,
map_run,
GINT_TO_POINTER (GRADIENT_MODE),
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, _("_Gradient Map"));
pika_procedure_add_menu_path (procedure, "<Image>/Colors/Map");
pika_procedure_set_documentation (procedure,
_("Recolor the image using colors "
"from the active gradient"),
"This plug-in maps the contents of "
"the specified drawable with active "
"gradient. It calculates luminosity "
"of each pixel and replaces the pixel "
"by the sample of active gradient at "
"the position proportional to that "
"luminosity. Complete black pixel "
"becomes the leftmost color of the "
"gradient, and complete white becomes "
"the rightmost. Works on both "
"Grayscale and RGB image "
"with/without alpha channel.",
name);
pika_procedure_set_attribution (procedure,
"Eiichi Takamori",
"Eiichi Takamori",
"1997");
}
else if (! strcmp (name, PALETTEMAP_PROC))
{
procedure = pika_image_procedure_new (plug_in, name,
PIKA_PDB_PROC_TYPE_PLUGIN,
map_run,
GINT_TO_POINTER (PALETTE_MODE),
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, _("_Palette Map"));
pika_procedure_add_menu_path (procedure, "<Image>/Colors/Map");
pika_procedure_set_documentation (procedure,
_("Recolor the image using colors "
"from the active palette"),
"This plug-in maps the contents of "
"the specified drawable with the "
"active palette. It calculates "
"luminosity of each pixel and "
"replaces the pixel by the palette "
"sample at the corresponding index. "
"A complete black pixel becomes the "
"lowest palette entry, and complete "
"white becomes the highest. Works on "
"both Grayscale and RGB image "
"with/without alpha channel.",
name);
pika_procedure_set_attribution (procedure,
"Bill Skaggs",
"Bill Skaggs",
"2004");
}
return procedure;
}
static PikaValueArray *
map_run (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
const PikaValueArray *args,
gpointer run_data)
{
MapMode mode = GPOINTER_TO_INT (run_data);
GeglBuffer *shadow_buffer;
GeglBuffer *buffer;
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."),
pika_procedure_get_name (procedure));
return pika_procedure_new_return_values (procedure,
PIKA_PDB_CALLING_ERROR,
error);
}
else
{
drawable = drawables[0];
}
shadow_buffer = pika_drawable_get_shadow_buffer (drawable);
buffer = pika_drawable_get_buffer (drawable);
/* Make sure that the drawable is gray or RGB color */
if (pika_drawable_is_rgb (drawable) ||
pika_drawable_is_gray (drawable))
{
if (mode == GRADIENT_MODE)
{
pika_progress_init (_("Gradient Map"));
}
else
{
pika_progress_init (_("Palette Map"));
}
map (buffer, shadow_buffer, drawable, mode);
}
else
{
return pika_procedure_new_return_values (procedure,
PIKA_PDB_EXECUTION_ERROR,
NULL);
}
g_object_unref (buffer);
g_object_unref (shadow_buffer);
pika_drawable_merge_shadow (drawable, TRUE);
pika_drawable_update (drawable, 0, 0,
pika_drawable_get_width (drawable),
pika_drawable_get_height (drawable));
if (run_mode != PIKA_RUN_NONINTERACTIVE)
pika_displays_flush ();
return pika_procedure_new_return_values (procedure, PIKA_PDB_SUCCESS, NULL);
}
static void
map (GeglBuffer *buffer,
GeglBuffer *shadow_buffer,
PikaDrawable *drawable,
MapMode mode)
{
GeglBufferIterator *gi;
gint nb_color_chan;
gint nb_chan;
gint nb_chan2;
gint nb_chan_samp;
gint index_iter;
gboolean interpolate;
gdouble *samples;
gboolean is_rgb;
gboolean has_alpha;
const Babl *format_shadow;
const Babl *format_buffer;
is_rgb = pika_drawable_is_rgb (drawable);
has_alpha = pika_drawable_has_alpha (drawable);
switch (mode)
{
case GRADIENT_MODE:
samples = get_samples_gradient (drawable);
interpolate = TRUE;
break;
case PALETTE_MODE:
samples = get_samples_palette (drawable);
interpolate = FALSE;
break;
default:
g_error ("plug_in_gradmap: invalid mode");
}
if (is_rgb)
{
nb_color_chan = 3;
nb_chan_samp = 4;
if (has_alpha)
format_shadow = babl_format ("R'G'B'A float");
else
format_shadow = babl_format ("R'G'B' float");
}
else
{
nb_color_chan = 1;
nb_chan_samp = 2;
if (has_alpha)
format_shadow = babl_format ("Y'A float");
else
format_shadow = babl_format ("Y' float");
}
if (has_alpha)
{
nb_chan = nb_color_chan + 1;
nb_chan2 = 2;
format_buffer = babl_format ("Y'A float");
}
else
{
nb_chan = nb_color_chan;
nb_chan2 = 1;
format_buffer = babl_format ("Y' float");
}
gi = gegl_buffer_iterator_new (shadow_buffer, NULL, 0, format_shadow,
GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE, 2);
index_iter = gegl_buffer_iterator_add (gi, buffer, NULL,
0, format_buffer,
GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
while (gegl_buffer_iterator_next (gi))
{
guint k;
gfloat *data;
gfloat *data2;
data = (gfloat*) gi->items[0].data;
data2 = (gfloat*) gi->items[index_iter].data;
if (interpolate)
{
for (k = 0; k < gi->length; k++)
{
gint b, ind1, ind2;
gdouble *samp1, *samp2;
gfloat c1, c2, val;
val = data2[0] * (NSAMPLES-1);
ind1 = CLAMP (floor (val), 0, NSAMPLES-1);
ind2 = CLAMP (ceil (val), 0, NSAMPLES-1);
c1 = 1.0 - (val - ind1);
c2 = 1.0 - c1;
samp1 = &(samples[ind1 * nb_chan_samp]);
samp2 = &(samples[ind2 * nb_chan_samp]);
for (b = 0; b < nb_color_chan; b++)
data[b] = (samp1[b] * c1 + samp2[b] * c2);
if (has_alpha)
{
float alpha = (samp1[b] * c1 + samp2[b] * c2);
data[b] = alpha * data2[1];
}
data += nb_chan;
data2 += nb_chan2;
}
}
else
{
for (k = 0; k < gi->length; k++)
{
gint b, ind;
gdouble *samp;
ind = CLAMP (data2[0] * (NSAMPLES-1), 0, NSAMPLES-1);
samp = &(samples[ind * nb_chan_samp]);
for (b = 0; b < nb_color_chan; b++)
data[b] = samp[b];
if (has_alpha)
{
data[b] = samp[b] * data2[1];
}
data += nb_chan;
data2 += nb_chan2;
}
}
}
g_free (samples);
}
/*
Returns 2048 samples of the gradient.
Each sample is (R'G'B'A float) or (Y'A float), depending on the drawable
*/
static gdouble *
get_samples_gradient (PikaDrawable *drawable)
{
PikaGradient *gradient;
gint n_d_samples;
gdouble *d_samples = NULL;
gradient = pika_context_get_gradient ();
/* FIXME: "reverse" hardcoded to FALSE. */
pika_gradient_get_uniform_samples (gradient, NSAMPLES, FALSE,
&n_d_samples, &d_samples);
if (! pika_drawable_is_rgb (drawable))
{
const Babl *format_src = babl_format ("R'G'B'A double");
const Babl *format_dst = babl_format ("Y'A double");
const Babl *fish = babl_fish (format_src, format_dst);
babl_process (fish, d_samples, d_samples, NSAMPLES);
}
return d_samples;
}
/*
Returns 2048 samples of the palette.
Each sample is (R'G'B'A float) or (Y'A float), depending on the drawable
*/
static gdouble *
get_samples_palette (PikaDrawable *drawable)
{
PikaPalette *palette;
PikaRGB color_sample;
gdouble *d_samples, *d_samp;
gboolean is_rgb;
gdouble factor;
gint pal_entry, num_colors;
gint nb_color_chan, nb_chan, i;
const Babl *format;
palette = pika_context_get_palette ();
num_colors = pika_palette_get_color_count (palette);
is_rgb = pika_drawable_is_rgb (drawable);
factor = ((double) num_colors) / NSAMPLES;
format = is_rgb ? babl_format ("R'G'B'A double") : babl_format ("Y'A double");
nb_color_chan = is_rgb ? 3 : 1;
nb_chan = nb_color_chan + 1;
d_samples = g_new (gdouble, NSAMPLES * nb_chan);
for (i = 0; i < NSAMPLES; i++)
{
d_samp = &d_samples[i * nb_chan];
pal_entry = CLAMP ((int)(i * factor), 0, num_colors - 1);
pika_palette_entry_get_color (palette, pal_entry, &color_sample);
pika_rgb_get_pixel (&color_sample,
format,
d_samp);
}
return d_samples;
}

1087
plug-ins/common/grid.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,365 @@
/*
* Guillotine plug-in v0.9 by Adam D. Moss, adam@foxbox.org. 1998/09/01
*/
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* 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/>.
*/
#include "config.h"
#include <string.h>
#include <libpika/pika.h>
#include "libpika/stdplugins-intl.h"
#define PLUG_IN_PROC "plug-in-guillotine"
typedef struct _Guillotine Guillotine;
typedef struct _GuillotineClass GuillotineClass;
struct _Guillotine
{
PikaPlugIn parent_instance;
};
struct _GuillotineClass
{
PikaPlugInClass parent_class;
};
#define GUILLOTINE_TYPE (guillotine_get_type ())
#define GUILLOTINE (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GUILLOTINE_TYPE, Guillotine))
GType guillotine_get_type (void) G_GNUC_CONST;
static GList * guillotine_query_procedures (PikaPlugIn *plug_in);
static PikaProcedure * guillotine_create_procedure (PikaPlugIn *plug_in,
const gchar *name);
static PikaValueArray * guillotine_run (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
const PikaValueArray *args,
gpointer run_data);
static GList * guillotine (PikaImage *image,
gboolean interactive);
G_DEFINE_TYPE (Guillotine, guillotine, PIKA_TYPE_PLUG_IN)
PIKA_MAIN (GUILLOTINE_TYPE)
DEFINE_STD_SET_I18N
static void
guillotine_class_init (GuillotineClass *klass)
{
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
plug_in_class->query_procedures = guillotine_query_procedures;
plug_in_class->create_procedure = guillotine_create_procedure;
plug_in_class->set_i18n = STD_SET_I18N;
}
static void
guillotine_init (Guillotine *film)
{
}
static GList *
guillotine_query_procedures (PikaPlugIn *plug_in)
{
return g_list_append (NULL, g_strdup (PLUG_IN_PROC));
}
static PikaProcedure *
guillotine_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,
guillotine_run, NULL, NULL);
pika_procedure_set_image_types (procedure, "*");
pika_procedure_set_sensitivity_mask (procedure,
PIKA_PROCEDURE_SENSITIVE_DRAWABLE |
PIKA_PROCEDURE_SENSITIVE_DRAWABLES |
PIKA_PROCEDURE_SENSITIVE_NO_DRAWABLES);
pika_procedure_set_menu_label (procedure, _("Slice Using G_uides"));
pika_procedure_add_menu_path (procedure, "<Image>/Image/[Crop]");
pika_procedure_set_documentation (procedure,
_("Slice the image into subimages "
"using guides"),
"This function takes an image and "
"slices it along its guides, creating "
"new images. The original image is "
"not modified.",
name);
pika_procedure_set_attribution (procedure,
"Adam D. Moss (adam@foxbox.org)",
"Adam D. Moss (adam@foxbox.org)",
"1998");
PIKA_PROC_VAL_INT (procedure, "image-count",
"Number of images created",
"Number of images created",
0, G_MAXINT, 0,
G_PARAM_READWRITE);
PIKA_PROC_VAL_OBJECT_ARRAY (procedure, "images",
"Output images",
"Output images",
PIKA_TYPE_IMAGE,
G_PARAM_READWRITE);
}
return procedure;
}
static PikaValueArray *
guillotine_run (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
const PikaValueArray *args,
gpointer run_data)
{
PikaValueArray *return_vals = NULL;
PikaPDBStatusType status = PIKA_PDB_SUCCESS;
return_vals = pika_procedure_new_return_values (procedure, status,
NULL);
if (status == PIKA_PDB_SUCCESS)
{
GList *image_list;
GList *list;
PikaImage **images;
gint num_images;
gint i;
pika_progress_init (_("Guillotine"));
image_list = guillotine (image, run_mode == PIKA_RUN_INTERACTIVE);
num_images = g_list_length (image_list);
images = g_new (PikaImage *, num_images);
for (list = image_list, i = 0;
list;
list = g_list_next (list), i++)
{
images[i] = g_object_ref (list->data);
}
g_list_free (image_list);
PIKA_VALUES_SET_INT (return_vals, 1, num_images);
PIKA_VALUES_TAKE_OBJECT_ARRAY (return_vals, 2, PIKA_TYPE_IMAGE, images, num_images);
if (run_mode == PIKA_RUN_INTERACTIVE)
pika_displays_flush ();
}
return return_vals;
}
static gint
guide_sort_func (gconstpointer a,
gconstpointer b)
{
return GPOINTER_TO_INT (a) - GPOINTER_TO_INT (b);
}
static GList *
guillotine (PikaImage *image,
gboolean interactive)
{
GList *images = NULL;
gint guide;
gint image_width;
gint image_height;
gboolean guides_found = FALSE;
GList *hguides, *hg;
GList *vguides, *vg;
image_width = pika_image_get_width (image);
image_height = pika_image_get_height (image);
hguides = g_list_append (NULL, GINT_TO_POINTER (0));
hguides = g_list_append (hguides, GINT_TO_POINTER (image_height));
vguides = g_list_append (NULL, GINT_TO_POINTER (0));
vguides = g_list_append (vguides, GINT_TO_POINTER (image_width));
for (guide = pika_image_find_next_guide (image, 0);
guide > 0;
guide = pika_image_find_next_guide (image, guide))
{
gint position = pika_image_get_guide_position (image, guide);
switch (pika_image_get_guide_orientation (image, guide))
{
case PIKA_ORIENTATION_HORIZONTAL:
if (! g_list_find (hguides, GINT_TO_POINTER (position)))
{
hguides = g_list_insert_sorted (hguides,
GINT_TO_POINTER (position),
guide_sort_func);
guides_found = TRUE;
}
break;
case PIKA_ORIENTATION_VERTICAL:
if (! g_list_find (vguides, GINT_TO_POINTER (position)))
{
vguides = g_list_insert_sorted (vguides,
GINT_TO_POINTER (position),
guide_sort_func);
guides_found = TRUE;
}
break;
case PIKA_ORIENTATION_UNKNOWN:
g_assert_not_reached ();
break;
}
}
if (guides_found)
{
GFile *file;
gint h, v, hpad, vpad;
gint x, y;
gchar *hformat;
gchar *format;
file = pika_image_get_file (image);
if (! file)
file = g_file_new_for_uri (_("Untitled"));
/* get the number horizontal and vertical slices */
h = g_list_length (hguides);
v = g_list_length (vguides);
/* need the number of digits of h and v for the padding */
hpad = log10(h) + 1;
vpad = log10(v) + 1;
/* format for the x-y coordinates in the filename */
hformat = g_strdup_printf ("%%0%i", MAX (hpad, vpad));
format = g_strdup_printf ("-%si-%si", hformat, hformat);
/* Do the actual dup'ing and cropping... this isn't a too naive a
* way to do this since we got copy-on-write tiles, either.
*/
for (y = 0, hg = hguides; hg && hg->next; y++, hg = hg->next)
{
for (x = 0, vg = vguides; vg && vg->next; x++, vg = vg->next)
{
PikaImage *new_image = pika_image_duplicate (image);
GString *new_uri;
GFile *new_file;
gchar *fileextension;
gchar *fileindex;
if (! new_image)
{
g_warning ("Couldn't create new image.");
g_free (hformat);
g_free (format);
return images;
}
pika_image_undo_disable (new_image);
pika_image_crop (new_image,
GPOINTER_TO_INT (vg->next->data) -
GPOINTER_TO_INT (vg->data),
GPOINTER_TO_INT (hg->next->data) -
GPOINTER_TO_INT (hg->data),
GPOINTER_TO_INT (vg->data),
GPOINTER_TO_INT (hg->data));
new_uri = g_string_new (g_file_get_uri (file));
/* show the rough coordinates of the image in the title */
fileindex = g_strdup_printf (format, x, y);
/* get the position of the file extension - last . in filename */
fileextension = strrchr (new_uri->str, '.');
if (fileextension)
{
gint pos;
/* Truncate the extension. */
pos = fileextension - new_uri->str;
g_string_truncate (new_uri, pos);
}
/* insert the coordinates before the extension */
g_string_append (new_uri, fileindex);
g_free (fileindex);
g_string_append (new_uri, ".xcf");
new_file = g_file_new_for_uri (new_uri->str);
g_string_free (new_uri, TRUE);
pika_image_set_file (new_image, new_file);
g_object_unref (new_file);
while ((guide = pika_image_find_next_guide (new_image, 0)))
pika_image_delete_guide (new_image, guide);
pika_image_undo_enable (new_image);
if (interactive)
pika_display_new (new_image);
images = g_list_prepend (images, GINT_TO_POINTER (new_image));
}
}
g_object_unref (file);
g_free (hformat);
g_free (format);
}
g_list_free (hguides);
g_list_free (vguides);
return g_list_reverse (images);
}

837
plug-ins/common/hot.c Normal file
View File

@ -0,0 +1,837 @@
/*
* PIKA - Photo and Image Kooker Application
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* 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/>.
*/
/*
* hot.c - Scan an image for pixels with RGB values that will give
* "unsafe" values of chrominance signal or composite signal
* amplitude when encoded into an NTSC or PAL color signal.
* (This happens for certain high-intensity high-saturation colors
* that are rare in real scenes, but can easily be present
* in synthetic images.)
*
* Such pixels can be flagged so the user may then choose other
* colors. Or, the offending pixels can be made "safe"
* in a manner that preserves hue.
*
* There are two reasonable ways to make a pixel "safe":
* We can reduce its intensity (luminance) while leaving
* hue and saturation the same. Or, we can reduce saturation
* while leaving hue and luminance the same. A #define selects
* which strategy to use.
*
* Note to the user: You must add your own read_pixel() and write_pixel()
* routines. You may have to modify pix_decode() and pix_encode().
* MAXPIX, WID, and HGT are likely to need modification.
*/
/*
* Originally written as "ikNTSC.c" by Alan Wm Paeth,
* University of Waterloo, August, 1985
* Updated by Dave Martindale, Imax Systems Corp., December 1990
*/
/*
* Compile time options:
*
*
* CHROMA_LIM is the limit (in IRE units) of the overall
* chrominance amplitude; it should be 50 or perhaps
* very slightly higher.
*
* COMPOS_LIM is the maximum amplitude (in IRE units) allowed for
* the composite signal. A value of 100 is the maximum
* monochrome white, and is always safe. 120 is the absolute
* limit for NTSC broadcasting, since the transmitter's carrier
* goes to zero with 120 IRE input signal. Generally, 110
* is a good compromise - it allows somewhat brighter colors
* than 100, while staying safely away from the hard limit.
*/
#include "config.h"
#include <string.h>
#include <libpika/pika.h>
#include <libpika/pikaui.h>
#include "libpika/stdplugins-intl.h"
#define PLUG_IN_PROC "plug-in-hot"
#define PLUG_IN_BINARY "hot"
#define PLUG_IN_ROLE "pika-hot"
typedef enum
{
ACT_LREDUX,
ACT_SREDUX,
ACT_FLAG
} hotAction;
typedef enum
{
MODE_NTSC,
MODE_PAL
} hotModes;
#define CHROMA_LIM 50.0 /* chroma amplitude limit */
#define COMPOS_LIM 110.0 /* max IRE amplitude */
/*
* RGB to YIQ encoding matrix.
*/
struct
{
gdouble pedestal;
gdouble gamma;
gdouble code[3][3];
}
static mode_vals[2] =
{
{
7.5,
2.2,
{
{ 0.2989, 0.5866, 0.1144 },
{ 0.5959, -0.2741, -0.3218 },
{ 0.2113, -0.5227, 0.3113 }
}
},
{
0.0,
2.8,
{
{ 0.2989, 0.5866, 0.1144 },
{ -0.1473, -0.2891, 0.4364 },
{ 0.6149, -0.5145, -0.1004 }
}
}
};
#define SCALE 8192 /* scale factor: do floats with int math */
#define MAXPIX 255 /* white value */
typedef struct _Hot Hot;
typedef struct _HotClass HotClass;
struct _Hot
{
PikaPlugIn parent_instance;
};
struct _HotClass
{
PikaPlugInClass parent_class;
};
#define HOT_TYPE (hot_get_type ())
#define HOT (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), HOT_TYPE, Hot))
GType hot_get_type (void) G_GNUC_CONST;
static GList * hot_query_procedures (PikaPlugIn *plug_in);
static PikaProcedure * hot_create_procedure (PikaPlugIn *plug_in,
const gchar *name);
static PikaValueArray * hot_run (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
const PikaValueArray *args,
gpointer run_data);
static gboolean pluginCore (PikaImage *image,
PikaDrawable *drawable,
GObject *config);
static gboolean plugin_dialog (PikaProcedure *procedure,
GObject *config);
static gboolean hotp (guint8 r,
guint8 g,
guint8 b);
static void build_tab (gint m);
/*
* gc: apply the gamma correction specified for this video standard.
* inv_gc: inverse function of gc.
*
* These are generally just a call to pow(), but be careful!
* Future standards may use more complex functions.
* (e.g. SMPTE 240M's "electro-optic transfer characteristic").
*/
#define gc(x,m) pow(x, 1.0 / mode_vals[m].gamma)
#define inv_gc(x,m) pow(x, mode_vals[m].gamma)
/*
* pix_decode: decode an integer pixel value into a floating-point
* intensity in the range [0, 1].
*
* pix_encode: encode a floating-point intensity into an integer
* pixel value.
*
* The code given here assumes simple linear encoding; you must change
* these routines if you use a different pixel encoding technique.
*/
#define pix_decode(v) ((double)v / (double)MAXPIX)
#define pix_encode(v) ((int)(v * (double)MAXPIX + 0.5))
G_DEFINE_TYPE (Hot, hot, PIKA_TYPE_PLUG_IN)
PIKA_MAIN (HOT_TYPE)
DEFINE_STD_SET_I18N
static gint tab[3][3][MAXPIX+1]; /* multiply lookup table */
static gdouble chroma_lim; /* chroma limit */
static gdouble compos_lim; /* composite amplitude limit */
static glong ichroma_lim2; /* chroma limit squared (scaled integer) */
static gint icompos_lim; /* composite amplitude limit (scaled integer) */
static void
hot_class_init (HotClass *klass)
{
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
plug_in_class->query_procedures = hot_query_procedures;
plug_in_class->create_procedure = hot_create_procedure;
plug_in_class->set_i18n = STD_SET_I18N;
}
static void
hot_init (Hot *hot)
{
}
static GList *
hot_query_procedures (PikaPlugIn *plug_in)
{
return g_list_append (NULL, g_strdup (PLUG_IN_PROC));
}
static PikaProcedure *
hot_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,
hot_run, NULL, NULL);
pika_procedure_set_image_types (procedure, "RGB");
pika_procedure_set_sensitivity_mask (procedure,
PIKA_PROCEDURE_SENSITIVE_DRAWABLE);
pika_procedure_set_menu_label (procedure, _("_Hot..."));
pika_procedure_add_menu_path (procedure, "<Image>/Colors/[Modify]");
pika_procedure_set_documentation (procedure,
_("Find and fix pixels that may "
"be unsafely bright"),
"hot scans an image for pixels that "
"will give unsave values of "
"chrominance or composite signale "
"amplitude when encoded into an NTSC "
"or PAL signal. Three actions can be "
"performed on these 'hot' pixels. "
"(0) reduce luminance, "
"(1) reduce saturation, or (2) Blacken.",
name);
pika_procedure_set_attribution (procedure,
"Eric L. Hernes, Alan Wm Paeth",
"Eric L. Hernes",
"1997");
PIKA_PROC_ARG_INT (procedure, "mode",
_("Mode"),
"Mode { NTSC (0), PAL (1) }",
0, 1, MODE_NTSC,
G_PARAM_READWRITE);
PIKA_PROC_ARG_INT (procedure, "action",
_("Action"),
"Action { (0) reduce luminance, "
"(1) reduce saturation, or (2) Blacken }",
0, 2, ACT_LREDUX,
G_PARAM_READWRITE);
PIKA_PROC_ARG_BOOLEAN (procedure, "new-layer",
_("Create _new layer"),
"Create a new layer",
TRUE,
G_PARAM_READWRITE);
}
return procedure;
}
static PikaValueArray *
hot_run (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
const PikaValueArray *args,
gpointer run_data)
{
PikaProcedureConfig *config;
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."),
pika_procedure_get_name (procedure));
return pika_procedure_new_return_values (procedure,
PIKA_PDB_CALLING_ERROR,
error);
}
else
{
drawable = drawables[0];
}
config = pika_procedure_create_config (procedure);
pika_procedure_config_begin_run (config, NULL, run_mode, args);
switch (run_mode)
{
case PIKA_RUN_INTERACTIVE:
if (! plugin_dialog (procedure, G_OBJECT (config)))
{
pika_procedure_config_end_run (config, PIKA_PDB_CANCEL);
g_object_unref (config);
return pika_procedure_new_return_values (procedure,
PIKA_PDB_CANCEL,
NULL);
}
break;
case PIKA_RUN_NONINTERACTIVE:
case PIKA_RUN_WITH_LAST_VALS:
break;
}
if (! pluginCore (image, drawable, G_OBJECT (config)))
{
pika_procedure_config_end_run (config, PIKA_PDB_CANCEL);
g_object_unref (config);
return pika_procedure_new_return_values (procedure,
PIKA_PDB_EXECUTION_ERROR,
NULL);
}
pika_procedure_config_end_run (config, PIKA_PDB_SUCCESS);
g_object_unref (config);
if (run_mode != PIKA_RUN_NONINTERACTIVE)
pika_displays_flush ();
return pika_procedure_new_return_values (procedure, PIKA_PDB_SUCCESS, NULL);
}
static gboolean
pluginCore (PikaImage *image,
PikaDrawable *drawable,
GObject *config)
{
gint mode;
gint action;
gboolean new_layer;
GeglBuffer *src_buffer;
GeglBuffer *dest_buffer;
const Babl *src_format;
const Babl *dest_format;
gint src_bpp;
gint dest_bpp;
gboolean success = TRUE;
PikaLayer *nl = NULL;
gint y, i;
gint Y, I, Q;
gint width, height;
gint sel_x1, sel_x2, sel_y1, sel_y2;
gint prog_interval;
guchar *src, *s, *dst, *d;
guchar r, prev_r=0, new_r=0;
guchar g, prev_g=0, new_g=0;
guchar b, prev_b=0, new_b=0;
gdouble fy, fc, t, scale;
gdouble pr, pg, pb;
gdouble py;
g_object_get (config,
"mode", &mode,
"action", &action,
"new-layer", &new_layer,
NULL);
width = pika_drawable_get_width (drawable);
height = pika_drawable_get_height (drawable);
if (pika_drawable_has_alpha (drawable))
src_format = babl_format ("R'G'B'A u8");
else
src_format = babl_format ("R'G'B' u8");
dest_format = src_format;
if (new_layer)
{
gchar name[40];
const gchar *mode_names[] =
{
"ntsc",
"pal",
};
const gchar *action_names[] =
{
"lum redux",
"sat redux",
"flag",
};
g_snprintf (name, sizeof (name), "hot mask (%s, %s)",
mode_names[mode],
action_names[action]);
nl = pika_layer_new (image, name, width, height,
PIKA_RGBA_IMAGE,
100,
pika_image_get_default_new_layer_mode (image));
pika_drawable_fill (PIKA_DRAWABLE (nl), PIKA_FILL_TRANSPARENT);
pika_image_insert_layer (image, nl, NULL, 0);
dest_format = babl_format ("R'G'B'A u8");
}
if (! pika_drawable_mask_intersect (drawable,
&sel_x1, &sel_y1, &width, &height))
return success;
src_bpp = babl_format_get_bytes_per_pixel (src_format);
dest_bpp = babl_format_get_bytes_per_pixel (dest_format);
sel_x2 = sel_x1 + width;
sel_y2 = sel_y1 + height;
src = g_new (guchar, width * height * src_bpp);
dst = g_new (guchar, width * height * dest_bpp);
src_buffer = pika_drawable_get_buffer (drawable);
if (new_layer)
{
dest_buffer = pika_drawable_get_buffer (PIKA_DRAWABLE (nl));
}
else
{
dest_buffer = pika_drawable_get_shadow_buffer (drawable);
}
gegl_buffer_get (src_buffer,
GEGL_RECTANGLE (sel_x1, sel_y1, width, height), 1.0,
src_format, src,
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
s = src;
d = dst;
build_tab (mode);
pika_progress_init (_("Hot"));
prog_interval = height / 10;
for (y = sel_y1; y < sel_y2; y++)
{
gint x;
if (y % prog_interval == 0)
pika_progress_update ((double) y / (double) (sel_y2 - sel_y1));
for (x = sel_x1; x < sel_x2; x++)
{
if (hotp (r = *(s + 0), g = *(s + 1), b = *(s + 2)))
{
if (action == ACT_FLAG)
{
for (i = 0; i < 3; i++)
*d++ = 0;
s += 3;
if (src_bpp == 4)
*d++ = *s++;
else if (new_layer)
*d++ = 255;
}
else
{
/*
* Optimization: cache the last-computed hot pixel.
*/
if (r == prev_r && g == prev_g && b == prev_b)
{
*d++ = new_r;
*d++ = new_g;
*d++ = new_b;
s += 3;
if (src_bpp == 4)
*d++ = *s++;
else if (new_layer)
*d++ = 255;
}
else
{
Y = tab[0][0][r] + tab[0][1][g] + tab[0][2][b];
I = tab[1][0][r] + tab[1][1][g] + tab[1][2][b];
Q = tab[2][0][r] + tab[2][1][g] + tab[2][2][b];
prev_r = r;
prev_g = g;
prev_b = b;
/*
* Get Y and chroma amplitudes in floating point.
*
* If your C library doesn't have hypot(), just use
* hypot(a,b) = sqrt(a*a, b*b);
*
* Then extract linear (un-gamma-corrected)
* floating-point pixel RGB values.
*/
fy = (double)Y / (double)SCALE;
fc = hypot ((double) I / (double) SCALE,
(double) Q / (double) SCALE);
pr = (double) pix_decode (r);
pg = (double) pix_decode (g);
pb = (double) pix_decode (b);
/*
* Reducing overall pixel intensity by scaling R,
* G, and B reduces Y, I, and Q by the same factor.
* This changes luminance but not saturation, since
* saturation is determined by the chroma/luminance
* ratio.
*
* On the other hand, by linearly interpolating
* between the original pixel value and a grey
* pixel with the same luminance (R=G=B=Y), we
* change saturation without affecting luminance.
*/
if (action == ACT_LREDUX)
{
/*
* Calculate a scale factor that will bring the pixel
* within both chroma and composite limits, if we scale
* luminance and chroma simultaneously.
*
* The calculated chrominance reduction applies
* to the gamma-corrected RGB values that are
* the input to the RGB-to-YIQ operation.
* Multiplying the original un-gamma-corrected
* pixel values by the scaling factor raised to
* the "gamma" power is equivalent, and avoids
* calling gc() and inv_gc() three times each. */
scale = chroma_lim / fc;
t = compos_lim / (fy + fc);
if (t < scale)
scale = t;
scale = pow (scale, mode_vals[mode].gamma);
r = (guint8) pix_encode (scale * pr);
g = (guint8) pix_encode (scale * pg);
b = (guint8) pix_encode (scale * pb);
}
else
{ /* ACT_SREDUX hopefully */
/*
* Calculate a scale factor that will bring the
* pixel within both chroma and composite
* limits, if we scale chroma while leaving
* luminance unchanged.
*
* We have to interpolate gamma-corrected RGB
* values, so we must convert from linear to
* gamma-corrected before interpolation and then
* back to linear afterwards.
*/
scale = chroma_lim / fc;
t = (compos_lim - fy) / fc;
if (t < scale)
scale = t;
pr = gc (pr, mode);
pg = gc (pg, mode);
pb = gc (pb, mode);
py = pr * mode_vals[mode].code[0][0] +
pg * mode_vals[mode].code[0][1] +
pb * mode_vals[mode].code[0][2];
r = pix_encode (inv_gc (py + scale * (pr - py),
mode));
g = pix_encode (inv_gc (py + scale * (pg - py),
mode));
b = pix_encode (inv_gc (py + scale * (pb - py),
mode));
}
*d++ = new_r = r;
*d++ = new_g = g;
*d++ = new_b = b;
s += 3;
if (src_bpp == 4)
*d++ = *s++;
else if (new_layer)
*d++ = 255;
}
}
}
else
{
if (! new_layer)
{
for (i = 0; i < src_bpp; i++)
*d++ = *s++;
}
else
{
s += src_bpp;
d += dest_bpp;
}
}
}
}
gegl_buffer_set (dest_buffer,
GEGL_RECTANGLE (sel_x1, sel_y1, width, height), 0,
dest_format, dst,
GEGL_AUTO_ROWSTRIDE);
pika_progress_update (1.0);
g_free (src);
g_free (dst);
g_object_unref (src_buffer);
g_object_unref (dest_buffer);
if (new_layer)
{
pika_drawable_update (PIKA_DRAWABLE (nl), sel_x1, sel_y1, width, height);
}
else
{
pika_drawable_merge_shadow (drawable, TRUE);
pika_drawable_update (drawable, sel_x1, sel_y1, width, height);
}
pika_displays_flush ();
return success;
}
static gboolean
plugin_dialog (PikaProcedure *procedure,
GObject *config)
{
GtkWidget *dlg;
GtkWidget *vbox;
GtkWidget *hbox;
GtkListStore *store;
gboolean run;
pika_ui_init (PLUG_IN_BINARY);
dlg = pika_procedure_dialog_new (procedure,
PIKA_PROCEDURE_CONFIG (config),
_("Hot"));
pika_dialog_set_alternative_button_order (GTK_DIALOG (dlg),
GTK_RESPONSE_OK,
GTK_RESPONSE_CANCEL,
-1);
pika_window_set_transient (GTK_WINDOW (dlg));
store = pika_int_store_new (_("N_TSC"), MODE_NTSC,
_("_PAL"), MODE_PAL,
NULL);
pika_procedure_dialog_get_int_radio (PIKA_PROCEDURE_DIALOG (dlg),
"mode", PIKA_INT_STORE (store));
store = pika_int_store_new (_("Reduce _Luminance"), ACT_LREDUX,
_("Reduce _Saturation"), ACT_SREDUX,
_("_Blacken"), ACT_FLAG,
NULL);
pika_procedure_dialog_get_int_radio (PIKA_PROCEDURE_DIALOG (dlg),
"action", PIKA_INT_STORE (store));
vbox = pika_procedure_dialog_fill_box (PIKA_PROCEDURE_DIALOG (dlg),
"hot-left-side",
"mode",
"new-layer",
NULL);
gtk_box_set_spacing (GTK_BOX (vbox), 12);
hbox = pika_procedure_dialog_fill_box (PIKA_PROCEDURE_DIALOG (dlg),
"hot-hbox",
"hot-left-side",
"action",
NULL);
gtk_box_set_spacing (GTK_BOX (hbox), 12);
gtk_box_set_homogeneous (GTK_BOX (hbox), TRUE);
gtk_widget_set_margin_bottom (hbox, 12);
gtk_orientable_set_orientation (GTK_ORIENTABLE (hbox),
GTK_ORIENTATION_HORIZONTAL);
pika_procedure_dialog_fill (PIKA_PROCEDURE_DIALOG (dlg),
"hot-hbox",
NULL);
gtk_widget_show (dlg);
run = pika_procedure_dialog_run (PIKA_PROCEDURE_DIALOG (dlg));
gtk_widget_destroy (dlg);
return run;
}
/*
* build_tab: Build multiply lookup table.
*
* For each possible pixel value, decode value into floating-point
* intensity. Then do gamma correction required by the video
* standard. Scale the result by our fixed-point scale factor.
* Then calculate 9 lookup table entries for this pixel value.
*
* We also calculate floating-point and scaled integer versions
* of our limits here. This prevents evaluating expressions every pixel
* when the compiler is too stupid to evaluate constant-valued
* floating-point expressions at compile time.
*
* For convenience, the limits are #defined using IRE units.
* We must convert them here into the units in which YIQ
* are measured. The conversion from IRE to internal units
* depends on the pedestal level in use, since as Y goes from
* 0 to 1, the signal goes from the pedestal level to 100 IRE.
* Chroma is always scaled to remain consistent with Y.
*/
static void
build_tab (int m)
{
double f;
int pv;
for (pv = 0; pv <= MAXPIX; pv++)
{
f = (double) SCALE * (double) gc ((double) pix_decode (pv), m);
tab[0][0][pv] = (int) (f * mode_vals[m].code[0][0] + 0.5);
tab[0][1][pv] = (int) (f * mode_vals[m].code[0][1] + 0.5);
tab[0][2][pv] = (int) (f * mode_vals[m].code[0][2] + 0.5);
tab[1][0][pv] = (int) (f * mode_vals[m].code[1][0] + 0.5);
tab[1][1][pv] = (int) (f * mode_vals[m].code[1][1] + 0.5);
tab[1][2][pv] = (int) (f * mode_vals[m].code[1][2] + 0.5);
tab[2][0][pv] = (int) (f * mode_vals[m].code[2][0] + 0.5);
tab[2][1][pv] = (int) (f * mode_vals[m].code[2][1] + 0.5);
tab[2][2][pv] = (int) (f * mode_vals[m].code[2][2] + 0.5);
}
chroma_lim = (double) CHROMA_LIM / (100.0 - mode_vals[m].pedestal);
compos_lim = ((double )COMPOS_LIM - mode_vals[m].pedestal) /
(100.0 - mode_vals[m].pedestal);
ichroma_lim2 = (int)(chroma_lim * SCALE + 0.5);
ichroma_lim2 *= ichroma_lim2;
icompos_lim = (int)(compos_lim * SCALE + 0.5);
}
static gboolean
hotp (guint8 r,
guint8 g,
guint8 b)
{
int y, i, q;
long y2, c2;
/*
* Pixel decoding, gamma correction, and matrix multiplication
* all done by lookup table.
*
* "i" and "q" are the two chrominance components;
* they are I and Q for NTSC.
* For PAL, "i" is U (scaled B-Y) and "q" is V (scaled R-Y).
* Since we only care about the length of the chroma vector,
* not its angle, we don't care which is which.
*/
y = tab[0][0][r] + tab[0][1][g] + tab[0][2][b];
i = tab[1][0][r] + tab[1][1][g] + tab[1][2][b];
q = tab[2][0][r] + tab[2][1][g] + tab[2][2][b];
/*
* Check to see if the chrominance vector is too long or the
* composite waveform amplitude is too large.
*
* Chrominance is too large if
*
* sqrt(i^2, q^2) > chroma_lim.
*
* The composite signal amplitude is too large if
*
* y + sqrt(i^2, q^2) > compos_lim.
*
* We avoid doing the sqrt by checking
*
* i^2 + q^2 > chroma_lim^2
* and
* y + sqrt(i^2 + q^2) > compos_lim
* sqrt(i^2 + q^2) > compos_lim - y
* i^2 + q^2 > (compos_lim - y)^2
*
*/
c2 = (long)i * i + (long)q * q;
y2 = (long)icompos_lim - y;
y2 *= y2;
if (c2 <= ichroma_lim2 && c2 <= y2)
{ /* no problems */
return FALSE;
}
return TRUE;
}

2575
plug-ins/common/jigsaw.c Normal file

File diff suppressed because it is too large Load Diff

901
plug-ins/common/mail.c Normal file
View File

@ -0,0 +1,901 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
* Copyright (C) 1997 Daniel Risacher
*
* 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/>.
*/
/*
* GUMP - Pika Useless Mail Plugin
* (or Gump Useless Mail Plugin if you prefer)
*
* by Adrian Likins <adrian@gimp.org>
* MIME encapsulation by Reagan Blundell <reagan@emails.net>
*
* As always: The utility of this plugin is left as an exercise for
* the reader
*
*/
#include "config.h"
#include <string.h>
#ifdef SENDMAIL
#include <sys/types.h>
#include <sys/wait.h>
#endif
#include <glib/gstdio.h>
#include <libpika/pika.h>
#include <libpika/pikaui.h>
#include "libpika/stdplugins-intl.h"
#define BUFFER_SIZE 256
#define PLUG_IN_PROC "plug-in-mail-image"
#define PLUG_IN_BINARY "mail"
#define PLUG_IN_ROLE "pika-mail"
typedef struct _Mail Mail;
typedef struct _MailClass MailClass;
struct _Mail
{
PikaPlugIn parent_instance;
};
struct _MailClass
{
PikaPlugInClass parent_class;
};
#define MAIL_TYPE (mail_get_type ())
#define MAIL (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MAIL_TYPE, Mail))
GType mail_get_type (void) G_GNUC_CONST;
static GList * mail_init_procedures (PikaPlugIn *plug_in);
static PikaProcedure * mail_create_procedure (PikaPlugIn *plug_in,
const gchar *name);
static PikaValueArray * mail_run (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
const PikaValueArray *args,
gpointer run_data);
static PikaPDBStatusType send_image (GObject *config,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
gint32 run_mode);
static gboolean send_dialog (PikaProcedure *procedure,
GObject *config);
static gboolean valid_file (GFile *file);
static gchar * find_extension (const gchar *filename);
#ifdef SENDMAIL
static gchar * sendmail_content_type (const gchar *filename);
static void sendmail_create_headers (FILE *mailpipe,
GObject *config);
static gboolean sendmail_to64 (const gchar *filename,
FILE *outfile,
GError **error);
static FILE * sendmail_pipe (gchar **cmd,
GPid *pid);
#endif
G_DEFINE_TYPE (Mail, mail, PIKA_TYPE_PLUG_IN)
PIKA_MAIN (MAIL_TYPE)
DEFINE_STD_SET_I18N
#ifdef SENDMAIL
static gchar *mesg_body = NULL;
#endif
static void
mail_class_init (MailClass *klass)
{
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
plug_in_class->init_procedures = mail_init_procedures;
plug_in_class->create_procedure = mail_create_procedure;
plug_in_class->set_i18n = STD_SET_I18N;
}
static void
mail_init (Mail *mail)
{
}
static GList *
mail_init_procedures (PikaPlugIn *plug_in)
{
GList *list = NULL;
gchar *email_bin;
/* Check if xdg-email or sendmail is installed.
* TODO: allow setting the location of the executable in preferences.
*/
#ifdef SENDMAIL
if (strlen (SENDMAIL) == 0)
{
email_bin = g_find_program_in_path ("sendmail");
}
else
{
/* If a directory has been set at build time, we assume that sendmail
* can only be in this directory. */
email_bin = g_build_filename (SENDMAIL, "sendmail", NULL);
if (! g_file_test (email_bin, G_FILE_TEST_IS_EXECUTABLE))
{
g_free (email_bin);
email_bin = NULL;
}
}
#else
email_bin = g_find_program_in_path ("xdg-email");
#endif
if (email_bin)
list = g_list_append (list, g_strdup (PLUG_IN_PROC));
return list;
}
static PikaProcedure *
mail_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,
mail_run, NULL, NULL);
pika_procedure_set_image_types (procedure, "*");
pika_procedure_set_sensitivity_mask (procedure,
PIKA_PROCEDURE_SENSITIVE_DRAWABLE |
PIKA_PROCEDURE_SENSITIVE_DRAWABLES |
PIKA_PROCEDURE_SENSITIVE_NO_DRAWABLES);
pika_procedure_set_menu_label (procedure, _("Send by E_mail..."));
pika_procedure_set_icon_name (procedure, PIKA_ICON_EDIT);
pika_procedure_add_menu_path (procedure, "<Image>/File/[Send]");
pika_procedure_set_documentation (procedure,
_("Send the image by email"),
#ifdef SENDMAIL
_("Sendmail is used to send emails "
"and must be properly configured."),
#else /* xdg-email */
_("The preferred email composer is "
"used to send emails and must be "
"properly configured."),
#endif
name);
pika_procedure_set_attribution (procedure,
"Adrian Likins, Reagan Blundell",
"Adrian Likins, Reagan Blundell, "
"Daniel Risacher, "
"Spencer Kimball and Peter Mattis",
"1995-1997");
PIKA_PROC_ARG_STRING (procedure, "filename",
_("File_name"),
_("The name of the file to save the image in"),
NULL,
G_PARAM_READWRITE);
PIKA_PROC_ARG_STRING (procedure, "to-address",
_("_To"),
_("The email address to send to"),
"",
G_PARAM_READWRITE);
PIKA_PROC_ARG_STRING (procedure, "from-address",
_("_From"),
_("The email address for the From: field"),
"",
G_PARAM_READWRITE);
PIKA_PROC_ARG_STRING (procedure, "subject",
_("Su_bject"),
_("The subject"),
"",
G_PARAM_READWRITE);
PIKA_PROC_ARG_STRING (procedure, "comment",
_("Co_mment"),
_("The comment"),
NULL,
G_PARAM_READWRITE);
}
return procedure;
}
static PikaValueArray *
mail_run (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
const PikaValueArray *args,
gpointer run_data)
{
PikaProcedureConfig *config;
PikaPDBStatusType status = PIKA_PDB_SUCCESS;
config = pika_procedure_create_config (procedure);
pika_procedure_config_begin_run (config, NULL, run_mode, args);
switch (run_mode)
{
case PIKA_RUN_INTERACTIVE:
{
gchar *filename = g_file_get_path (pika_image_get_file (image));
if (filename)
{
gchar *basename = g_filename_display_basename (filename);
gchar buffername[BUFFER_SIZE];
g_strlcpy (buffername, basename, BUFFER_SIZE);
g_object_set (config,
"filename", buffername,
NULL);
g_free (basename);
g_free (filename);
}
}
if (! send_dialog (procedure, G_OBJECT (config)))
{
pika_procedure_config_end_run (config, PIKA_PDB_CANCEL);
g_object_unref (config);
return pika_procedure_new_return_values (procedure, PIKA_PDB_CANCEL,
NULL);
}
break;
default:
break;
}
status = send_image (G_OBJECT (config),
image,
n_drawables, drawables,
run_mode);
pika_procedure_config_end_run (config, status);
g_object_unref (config);
return pika_procedure_new_return_values (procedure, status, NULL);
}
static PikaPDBStatusType
send_image (GObject *config,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
gint32 run_mode)
{
PikaPDBStatusType status = PIKA_PDB_SUCCESS;
gchar *ext;
GFile *tmpfile;
gchar *tmpname;
#ifndef SENDMAIL /* xdg-email */
gchar *mailcmd[9];
gchar *filepath = NULL;
GFile *tmp_dir = NULL;
GFileEnumerator *enumerator;
gint i;
#else /* SENDMAIL */
gchar *mailcmd[3];
GPid mailpid;
FILE *mailpipe = NULL;
#endif
GError *error = NULL;
gchar *filename = NULL;
gchar *receipt = NULL;
gchar *from = NULL;
gchar *subject = NULL;
gchar *comment = NULL;
g_object_get (config,
"filename", &filename,
"to-address", &receipt,
"from-address", &from,
"subject", &subject,
"comment", &comment,
NULL);
ext = find_extension (filename);
if (ext == NULL)
return PIKA_PDB_CALLING_ERROR;
/* get a temp name with the right extension and save into it. */
tmpfile = pika_temp_file (ext + 1);
tmpname = g_file_get_path (tmpfile);
if (! (pika_file_save (run_mode, image, n_drawables,
(const PikaItem **) drawables,
tmpfile) &&
valid_file (tmpfile)))
{
goto error;
}
#ifndef SENDMAIL /* xdg-email */
/* From xdg-email doc:
* "Some e-mail applications require the file to remain present
* after xdg-email returns."
* As a consequence, the file cannot be removed at the end of the
* function. We actually have no way to ever know *when* the file can
* be removed since the caller could leave the email window opened for
* hours. Yet we still want to clean sometimes and not have temporary
* images piling up.
* So I use a known directory that we control under $PIKA_DIRECTORY/tmp/,
* and clean it out each time the plugin runs. This means that *if* you
* are in the above case (your email client requires the file to stay
* alive), you cannot run twice the plugin at the same time.
*/
tmp_dir = pika_directory_file ("tmp", PLUG_IN_PROC, NULL);
if (g_mkdir_with_parents (pika_file_get_utf8_name (tmp_dir),
S_IRUSR | S_IWUSR | S_IXUSR) == -1)
{
g_message ("Temporary directory %s could not be created.",
pika_file_get_utf8_name (tmp_dir));
g_error_free (error);
goto error;
}
enumerator = g_file_enumerate_children (tmp_dir,
G_FILE_ATTRIBUTE_STANDARD_TYPE,
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
NULL, NULL);
if (enumerator)
{
GFileInfo *info;
while ((info = g_file_enumerator_next_file (enumerator,
NULL, NULL)))
{
if (g_file_info_get_file_type (info) == G_FILE_TYPE_REGULAR)
{
GFile *file = g_file_enumerator_get_child (enumerator, info);
g_file_delete (file, NULL, NULL);
g_object_unref (file);
}
g_object_unref (info);
}
g_object_unref (enumerator);
}
filepath = g_build_filename (pika_file_get_utf8_name (tmp_dir), filename,
NULL);
if (g_rename (tmpname, filepath) == -1)
{
/* But on some system, I got an 'Invalid cross-device link' errno
* with g_rename().
* On the other hand, g_file_move() seems to be more robust.
*/
GFile *source = tmpfile;
GFile *target = g_file_new_for_path (filepath);
if (! g_file_move (source, target, G_FILE_COPY_NONE, NULL, NULL, NULL, &error))
{
g_message ("%s", error->message);
g_clear_error (&error);
g_object_unref (target);
goto error;
}
g_object_unref (target);
}
mailcmd[0] = g_strdup ("xdg-email");
mailcmd[1] = "--attach";
mailcmd[2] = filepath;
i = 3;
if (subject != NULL && strlen (subject) > 0)
{
mailcmd[i++] = "--subject";
mailcmd[i++] = subject;
}
if (comment != NULL && strlen (comment) > 0)
{
mailcmd[i++] = "--body";
mailcmd[i++] = comment;
}
if (receipt != NULL && strlen (receipt) > 0)
{
mailcmd[i++] = receipt;
}
mailcmd[i] = NULL;
if (! g_spawn_async (NULL, mailcmd, NULL,
G_SPAWN_SEARCH_PATH,
NULL, NULL, NULL, &error))
{
g_message ("%s", error->message);
g_error_free (error);
goto error;
}
#else /* SENDMAIL */
/* construct the "sendmail user@location" line */
if (strlen (SENDMAIL) == 0)
mailcmd[0] = g_strdup ("sendmail");
else
mailcmd[0] = g_build_filename (SENDMAIL, "sendmail", NULL);
mailcmd[1] = receipt;
mailcmd[2] = NULL;
/* create a pipe to sendmail */
mailpipe = sendmail_pipe (mailcmd, &mailpid);
if (mailpipe == NULL)
return PIKA_PDB_EXECUTION_ERROR;
sendmail_create_headers (mailpipe, config);
fflush (mailpipe);
if (! sendmail_to64 (tmpname, mailpipe, &error))
{
g_message ("%s", error->message);
g_error_free (error);
goto error;
}
fprintf (mailpipe, "\n--GUMP-MIME-boundary--\n");
#endif
goto cleanup;
error:
/* stop sendmail from doing anything */
#ifdef SENDMAIL
kill (mailpid, SIGINT);
#endif
status = PIKA_PDB_EXECUTION_ERROR;
cleanup:
/* close out the sendmail process */
#ifdef SENDMAIL
if (mailpipe)
{
fclose (mailpipe);
waitpid (mailpid, NULL, 0);
g_spawn_close_pid (mailpid);
}
/* delete the tmpfile that was generated */
g_unlink (tmpname);
#else
if (tmp_dir)
g_object_unref (tmp_dir);
if (filepath)
g_free (filepath);
#endif
g_free (filename);
g_free (receipt);
g_free (from);
g_free (subject);
g_free (comment);
g_free (mailcmd[0]);
g_free (tmpname);
g_object_unref (tmpfile);
return status;
}
static gboolean
send_dialog (PikaProcedure *procedure,
GObject *config)
{
GtkWidget *dlg;
GtkWidget *main_vbox;
GtkWidget *entry;
GtkWidget *real_entry;
GtkWidget *grid;
GtkWidget *button;
#ifdef SENDMAIL
GtkWidget *scrolled_window;
GtkWidget *text_view;
#endif
gchar *gump_from;
gboolean run;
pika_ui_init (PLUG_IN_BINARY);
/* check pikarc for a preferred "From:" address */
gump_from = pika_pikarc_query ("gump-from");
if (gump_from)
{
g_object_set (config,
"from-address", gump_from,
NULL);
g_free (gump_from);
}
dlg = pika_procedure_dialog_new (procedure,
PIKA_PROCEDURE_CONFIG (config),
_("Send by Email"));
/* Change "Ok" button to "Send" */
button = gtk_dialog_get_widget_for_response (GTK_DIALOG (dlg),
GTK_RESPONSE_OK);
gtk_button_set_label (GTK_BUTTON (button), _("Send"));
pika_dialog_set_alternative_button_order (GTK_DIALOG (dlg),
GTK_RESPONSE_OK,
GTK_RESPONSE_CANCEL,
-1);
pika_window_set_transient (GTK_WINDOW (dlg));
main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))),
main_vbox, TRUE, TRUE, 0);
gtk_widget_show (main_vbox);
/* grid */
grid = gtk_grid_new ();
gtk_box_pack_start (GTK_BOX (main_vbox), grid, FALSE, FALSE, 0);
gtk_widget_show (grid);
gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
gtk_grid_set_column_spacing (GTK_GRID (grid), 6);
/* Filename entry */
entry = pika_procedure_dialog_get_widget (PIKA_PROCEDURE_DIALOG (dlg),
"filename", PIKA_TYPE_LABEL_ENTRY);
real_entry = pika_label_entry_get_entry (PIKA_LABEL_ENTRY (entry));
gtk_widget_set_size_request (real_entry, 200, -1);
gtk_entry_set_activates_default (GTK_ENTRY (real_entry), TRUE);
pika_procedure_dialog_fill (PIKA_PROCEDURE_DIALOG (dlg),
"filename",
NULL);
gtk_entry_set_max_length (GTK_ENTRY (real_entry),
BUFFER_SIZE - 1);
#ifdef SENDMAIL
/* To entry */
entry = pika_procedure_dialog_get_widget (PIKA_PROCEDURE_DIALOG (dlg),
"to-address",
PIKA_TYPE_LABEL_ENTRY);
real_entry = pika_label_entry_get_entry (PIKA_LABEL_ENTRY (entry));
gtk_widget_set_size_request (real_entry, 200, -1);
gtk_entry_set_max_length (GTK_ENTRY (real_entry), BUFFER_SIZE - 1);
pika_procedure_dialog_fill (PIKA_PROCEDURE_DIALOG (dlg),
"to-address",
NULL);
gtk_entry_set_max_length (GTK_ENTRY (real_entry),
BUFFER_SIZE - 1);
gtk_widget_grab_focus (real_entry);
/* From entry */
entry = pika_procedure_dialog_get_widget (PIKA_PROCEDURE_DIALOG (dlg),
"from-address",
PIKA_TYPE_LABEL_ENTRY);
real_entry = pika_label_entry_get_entry (PIKA_LABEL_ENTRY (entry));
gtk_widget_set_size_request (real_entry, 200, -1);
gtk_entry_set_max_length (GTK_ENTRY (real_entry), BUFFER_SIZE - 1);
pika_procedure_dialog_fill (PIKA_PROCEDURE_DIALOG (dlg),
"from-address",
NULL);
gtk_entry_set_max_length (GTK_ENTRY (real_entry),
BUFFER_SIZE - 1);
/* Subject entry */
entry = pika_procedure_dialog_get_widget (PIKA_PROCEDURE_DIALOG (dlg),
"subject",
PIKA_TYPE_LABEL_ENTRY);
real_entry = pika_label_entry_get_entry (PIKA_LABEL_ENTRY (entry));
gtk_widget_set_size_request (real_entry, 200, -1);
gtk_entry_set_max_length (GTK_ENTRY (real_entry), BUFFER_SIZE - 1);
pika_procedure_dialog_fill (PIKA_PROCEDURE_DIALOG (dlg),
"subject",
NULL);
gtk_entry_set_max_length (GTK_ENTRY (real_entry),
BUFFER_SIZE - 1);
/* Body */
text_view = pika_procedure_dialog_get_widget (PIKA_PROCEDURE_DIALOG (dlg),
"comment",
GTK_TYPE_TEXT_VIEW);
gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (text_view), GTK_WRAP_WORD);
scrolled_window =
pika_procedure_dialog_fill_scrolled_window (PIKA_PROCEDURE_DIALOG (dlg),
"comment-scrolled",
"comment");
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window),
GTK_SHADOW_IN);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
GTK_POLICY_AUTOMATIC,
GTK_POLICY_AUTOMATIC);
pika_procedure_dialog_fill (PIKA_PROCEDURE_DIALOG (dlg),
"comment-scrolled",
NULL);
#endif
gtk_widget_show (dlg);
run = pika_procedure_dialog_run (PIKA_PROCEDURE_DIALOG (dlg));
gtk_widget_destroy (dlg);
return run;
}
static gboolean
valid_file (GFile *file)
{
GStatBuf buf;
gboolean valid;
valid = g_stat (g_file_peek_path (file), &buf) == 0 && buf.st_size > 0;
return valid;
}
static gchar *
find_extension (const gchar *filename)
{
gchar *filename_copy;
gchar *ext;
/* we never free this copy - aren't we evil! */
filename_copy = g_strdup (filename);
/* find the extension, boy! */
ext = strrchr (filename_copy, '.');
while (TRUE)
{
if (!ext || ext[1] == '\0' || strchr (ext, G_DIR_SEPARATOR))
{
g_message (_("some sort of error with the file extension "
"or lack thereof"));
return NULL;
}
if (0 != g_ascii_strcasecmp (ext, ".gz") &&
0 != g_ascii_strcasecmp (ext, ".bz2"))
{
return ext;
}
else
{
/* we found something, loop back, and look again */
*ext = 0;
ext = strrchr (filename_copy, '.');
}
}
g_free (filename_copy);
return ext;
}
#ifdef SENDMAIL
static gchar *
sendmail_content_type (const gchar *filename)
{
/* This function returns a MIME Content-type: value based on the
filename it is given. */
const gchar *type_mappings[20] =
{
"gif" , "image/gif",
"jpg" , "image/jpeg",
"jpeg", "image/jpeg",
"tif" , "image/tiff",
"tiff", "image/tiff",
"png" , "image/png",
"g3" , "image/g3fax",
"ps" , "application/postscript",
"eps" , "application/postscript",
NULL, NULL
};
gchar *ext;
gint i;
ext = find_extension (filename);
if (!ext)
{
return g_strdup ("application/octet-stream");
}
i = 0;
ext += 1;
while (type_mappings[i])
{
if (g_ascii_strcasecmp (ext, type_mappings[i]) == 0)
{
return g_strdup (type_mappings[i + 1]);
}
i += 2;
}
return g_strdup_printf ("image/x-%s", ext);
}
static void
sendmail_create_headers (FILE *mailpipe,
GObject *config)
{
gchar *filename = NULL;
gchar *receipt = NULL;
gchar *from = NULL;
gchar *subject = NULL;
gchar *comment = NULL;
g_object_get (config,
"filename", &filename,
"to-address", &receipt,
"from-address", &from,
"subject", &subject,
"comment", &comment,
NULL);
/* create all the mail header stuff. Feel free to add your own */
/* It is advisable to leave the X-Mailer header though, as */
/* there is a possibility of a Pika mail scanner/reader in the */
/* future. It will probably need that header. */
fprintf (mailpipe, "To: %s \n", receipt);
fprintf (mailpipe, "Subject: %s \n", subject);
if (from != NULL && strlen (from) > 0)
fprintf (mailpipe, "From: %s \n", from);
fprintf (mailpipe, "X-Mailer: PIKA Useless Mail plug-in %s\n", PIKA_VERSION);
fprintf (mailpipe, "MIME-Version: 1.0\n");
fprintf (mailpipe, "Content-type: multipart/mixed; "
"boundary=GUMP-MIME-boundary\n");
fprintf (mailpipe, "\n\n");
fprintf (mailpipe, "--GUMP-MIME-boundary\n");
fprintf (mailpipe, "Content-type: text/plain; charset=UTF-8\n\n");
if (mesg_body)
fprintf (mailpipe, "%s", mesg_body);
fprintf (mailpipe, "\n\n");
{
gchar *content = sendmail_content_type (filename);
fprintf (mailpipe, "--GUMP-MIME-boundary\n");
fprintf (mailpipe, "Content-type: %s\n", content);
fprintf (mailpipe, "Content-transfer-encoding: base64\n");
fprintf (mailpipe, "Content-disposition: attachment; filename=\"%s\"\n",
filename);
fprintf (mailpipe, "Content-description: %s\n\n", filename);
g_free (content);
}
g_free (filename);
g_free (receipt);
g_free (from);
g_free (subject);
g_free (comment);
}
static gboolean
sendmail_to64 (const gchar *filename,
FILE *outfile,
GError **error)
{
GMappedFile *infile;
const guchar *in;
gchar out[2048];
gint state = 0;
gint save = 0;
gsize len;
gsize bytes;
gsize c;
infile = g_mapped_file_new (filename, FALSE, error);
if (! infile)
return FALSE;
in = (const guchar *) g_mapped_file_get_contents (infile);
len = g_mapped_file_get_length (infile);
for (c = 0; c < len;)
{
gsize step = MIN (1024, len - c);
bytes = g_base64_encode_step (in + c, step, TRUE, out, &state, &save);
fwrite (out, 1, bytes, outfile);
c += step;
}
bytes = g_base64_encode_close (TRUE, out, &state, &save);
fwrite (out, 1, bytes, outfile);
g_mapped_file_unref (infile);
return TRUE;
}
static FILE *
sendmail_pipe (gchar **cmd,
GPid *pid)
{
gint fd;
GError *err = NULL;
if (! g_spawn_async_with_pipes (NULL, cmd, NULL,
G_SPAWN_DO_NOT_REAP_CHILD |
G_SPAWN_SEARCH_PATH,
NULL, NULL, pid, &fd, NULL, NULL, &err))
{
g_message (_("Could not start sendmail (%s)"), err->message);
g_error_free (err);
*pid = -1;
return NULL;
}
return fdopen (fd, "wb");
}
#endif

250
plug-ins/common/meson.build Normal file
View File

@ -0,0 +1,250 @@
common_plugins_list = [
{ 'name': 'align-layers', },
{ 'name': 'animation-optimize', },
{ 'name': 'animation-play', },
{ 'name': 'blinds', },
{ 'name': 'border-average', },
{ 'name': 'busy-dialog', },
{ 'name': 'checkerboard', },
{ 'name': 'cml-explorer', },
{ 'name': 'colormap-remap', },
{ 'name': 'compose', },
{ 'name': 'contrast-retinex', },
{ 'name': 'crop-zealous',
'deps': [ gtk3, gegl, gdk_pixbuf, cairo, ],
},
{ 'name': 'curve-bend', },
{ 'name': 'decompose', },
{ 'name': 'depth-merge', },
{ 'name': 'despeckle', },
{ 'name': 'destripe', },
{ 'name': 'file-cel', },
{ 'name': 'file-compressor',
'deps': [ gegl, gdk_pixbuf, cairo, gio, liblzma, bz2, zlib, ],
},
{ 'name': 'file-csource', },
{ 'name': 'file-desktop-link',
'deps': [ gtk3, gegl, gdk_pixbuf, cairo, ],
},
{ 'name': 'file-dicom', },
{ 'name': 'file-farbfeld', },
{ 'name': 'file-gbr', },
{ 'name': 'file-gegl', },
{ 'name': 'file-gif-load',
'deps': [ gtk3, gegl, gdk_pixbuf, cairo, ],
},
{ 'name': 'file-gif-save', },
{ 'name': 'file-gih', },
{ 'name': 'file-glob',
'deps': [ gtk3, gegl, gdk_pixbuf, cairo, ],
},
{ 'name': 'file-header', },
{ 'name': 'file-html-table', },
{ 'name': 'file-pat', },
{ 'name': 'file-pcx', },
{ 'name': 'file-pdf-load',
'deps': [ gtk3, gegl, gdk_pixbuf, poppler ],
},
{ 'name': 'file-pix', },
{ 'name': 'file-png',
'deps': [ gtk3, gegl, libpng, lcms ],
},
{ 'name': 'file-pnm', },
{ 'name': 'file-psp',
'deps': [ gtk3, gegl, zlib, ],
},
{ 'name': 'file-raw-data', },
{ 'name': 'file-sunras', },
{ 'name': 'file-svg',
'deps': [ gtk3, gegl, rsvg, ],
},
{ 'name': 'file-tga', },
{ 'name': 'file-wbmp', },
{ 'name': 'file-xbm', },
{ 'name': 'file-xwd', },
{ 'name': 'film', },
{ 'name': 'gradient-map', },
{ 'name': 'grid', },
{ 'name': 'guillotine', },
{ 'name': 'hot', },
{ 'name': 'jigsaw', },
{ 'name': 'nl-filter', },
{ 'name': 'plugin-browser', },
{ 'name': 'procedure-browser', },
{ 'name': 'qbist', },
{ 'name': 'sample-colorize', },
{ 'name': 'smooth-palette', },
{ 'name': 'sparkle', },
{ 'name': 'sphere-designer', },
{ 'name': 'tile-small', },
{ 'name': 'tile', },
{ 'name': 'unit-editor', },
{ 'name': 'van-gogh-lic', },
{ 'name': 'warp', },
{ 'name': 'wavelet-decompose', },
{ 'name': 'web-browser', },
]
if libaa.found()
common_plugins_list += {
'name': 'file-aa',
'deps': [ gtk3, gegl, gdk_pixbuf, libaa, ],
}
endif
if have_heif
common_plugins_list += { 'name': 'file-heif',
'deps': [ gtk3, gegl, libheif, gexiv2, lcms, ],
}
endif
if openjpeg.found()
common_plugins_list += { 'name': 'file-jp2-load',
'deps': [ gtk3, gegl, gdk_pixbuf, openjpeg, ],
}
endif
if libjxl.found() and libjxl_threads.found()
common_plugins_list += {
'name': 'file-jpegxl',
'deps': [ gtk3, gegl, gexiv2, libjxl, libjxl_threads, ],
}
endif
if libmng.found()
mng_cflags = []
if platform_windows
mng_cflags = [ '-DMNG_USE_DLL' ]
endif
common_plugins_list += { 'name': 'file-mng',
'deps': [ gtk3, gegl, libmng, libpng, ],
'cflags': mng_cflags,
}
endif
if cairopdf.found()
common_plugins_list += { 'name': 'file-pdf-save',
'deps': [ gtk3, gegl, gdk_pixbuf, poppler, cairo, cairopdf ],
}
endif
if ghostscript.found()
common_plugins_list += { 'name': 'file-ps',
'deps': [ gtk3, gegl, gdk_pixbuf, ghostscript, ],
}
endif
if wmf.found()
common_plugins_list += { 'name': 'file-wmf',
'deps': [ gtk3, gegl, wmf, ],
}
endif
if xmc.found()
common_plugins_list += { 'name': 'file-xmc',
'deps': [ gtk3, gegl, xmc, ],
}
endif
if libxpm.found()
common_plugins_list += { 'name': 'file-xpm',
'deps': [ gtk3, gegl, libxpm, ],
}
endif
if have_qoi
common_plugins_list += {
'name': 'file-qoi',
'deps': [ gtk3, gegl, ],
}
endif
if libiff.found() and libilbm.found()
common_plugins_list += {
'name': 'file-iff',
'deps': [ gtk3, gegl, libiff, libilbm, ],
}
elif have_ilbm
common_plugins_list += {
'name': 'file-iff',
'deps': [ gtk3, gegl, ],
}
endif
if not platform_windows
common_plugins_list += { 'name': 'mail', }
endif
if get_option('webkit-unmaintained')
common_plugins_list += { 'name': 'web-page',
'deps': [ gtk3, gegl, gdk_pixbuf, webkit, ],
}
endif
foreach plugin : common_plugins_list
plugin_name = plugin.get('name')
plugin_sources = [ plugin.get('sources', plugin_name + '.c') ]
plugin_deps = plugin.get('deps', [ gtk3, gegl, gdk_pixbuf, ])
plugin_cflags = plugin.get('cflags', [])
if platform_windows
plugin_rc = configure_file(
input : pika_plugins_rc.full_path(),
output: plugin_name + '.rc',
copy: true,
)
# See https://gitlab.gnome.org/GNOME/pika/-/issues/8537
if generate_version_h
compile_resources_depfiles = []
compile_resources_depends = [ gitversion_h ]
else
compile_resources_depfiles = [ gitversion_h ]
compile_resources_depends = []
endif
plugin_sources += windows.compile_resources(
plugin_rc,
args: [
'--define', 'ORIGINALFILENAME_STR="@0@"'.format(plugin_name+'.exe'),
'--define', 'INTERNALNAME_STR="@0@"' .format(plugin_name),
'--define', 'TOP_SRCDIR="@0@"' .format(meson.project_source_root()),
],
include_directories: [
rootInclude, appInclude,
],
depend_files: compile_resources_depfiles,
depends: compile_resources_depends
)
endif
plugin_exe = executable(plugin_name,
plugin_sources,
include_directories: [ rootInclude, ],
link_with : [
libpika,
libpikabase,
libpikacolor,
libpikaconfig,
libpikamath,
libpikaui,
libpikawidgets,
],
dependencies: [ plugin_deps, math ],
c_args: plugin_cflags,
install: true,
install_dir: pikaplugindir / 'plug-ins' / plugin_name,
)
# Ugly trick to copy executables into subfolders so that we can run PIKA from
# the build directory without installing it.
custom_target('test-' + plugin_name,
input: [ plugin_exe ],
output: [ plugin_name + '.dummy' ],
command: [ python, meson.project_source_root() / '.gitlab/cp-plug-in-subfolder.py',
plugin_exe, meson.current_build_dir() / 'test-plug-ins' / plugin_name,
'@OUTPUT@' ],
build_by_default: true,
)
endforeach

234
plug-ins/common/mkgen.pl Normal file
View File

@ -0,0 +1,234 @@
#!/usr/bin/perl -w
use lib '../../pdb';
require 'util.pl';
*write_file = \&Pika::CodeGen::util::write_file;
*FILE_EXT = \$Pika::CodeGen::util::FILE_EXT;
$destdir = ".";
$builddir = ".";
$ignorefile = ".gitignore";
$rcfile = "pikarc.common";
$outmk = "$builddir/Makefile.am$FILE_EXT";
$outignore = "$builddir/$ignorefile$FILE_EXT";
$outrc = "$builddir/$rcfile$FILE_EXT";
open MK, "> $outmk";
open IGNORE, "> $outignore";
open RC, "> $outrc";
require './plugin-defs.pl';
$bins = ""; $opts = ""; $dirs = "";
foreach (sort keys %plugins) {
my $makename = $_;
$makename =~ s/-/_/g;
if (exists $plugins{$_}->{optional}) {
$bins .= "${makename}_libexec_PROGRAMS = \$(\U$makename\E)\n";
$opts .= "\t$_ \\\n";
}
else {
$bins .= "${makename}_libexec_PROGRAMS = $_\n";
}
$dirs .= "${makename}_libexecdir = \$(pikaplugindir)/plug-ins/$_\n";
}
$extra = "";
foreach (@extra) { $extra .= "\t$_\t\\\n" }
if ($extra) {
$extra =~ s/\t\\\n$//s;
$extra = "\t\\\n$extra";
}
foreach ($bins, $opts) { s/ \\\n$//s }
print MK <<EOT;
## ---------------------------------------------------------
## This file is autogenerated by mkgen.pl and plugin-defs.pl
## ---------------------------------------------------------
## Modify those two files instead of this one; for most
## plug-ins you should only need to modify plugin-defs.pl.
if OS_WIN32
mwindows = -mwindows
else
libm = -lm
endif
if PLATFORM_OSX
xobjective_c = "-xobjective-c"
framework_cocoa = -framework Cocoa
endif
if HAVE_WINDRES
include \$(top_srcdir)/build/windows/pikarc-plug-ins.rule
include $rcfile
endif
libpika = \$(top_builddir)/libpika/libpika-\$(PIKA_API_VERSION).la
libpikabase = \$(top_builddir)/libpikabase/libpikabase-\$(PIKA_API_VERSION).la
libpikacolor = \$(top_builddir)/libpikacolor/libpikacolor-\$(PIKA_API_VERSION).la
libpikaconfig = \$(top_builddir)/libpikaconfig/libpikaconfig-\$(PIKA_API_VERSION).la
libpikamath = \$(top_builddir)/libpikamath/libpikamath-\$(PIKA_API_VERSION).la \$(libm)
libpikamodule = \$(top_builddir)/libpikamodule/libpikamodule-\$(PIKA_API_VERSION).la
libpikaui = \$(top_builddir)/libpika/libpikaui-\$(PIKA_API_VERSION).la
libpikawidgets = \$(top_builddir)/libpikawidgets/libpikawidgets-\$(PIKA_API_VERSION).la
AM_LDFLAGS = \$(mwindows)
EXTRA_DIST = \\
mkgen.pl \\
plugin-defs.pl$extra \\
$rcfile
AM_CPPFLAGS = \\
-I\$(top_srcdir) \\
\$(GTK_CFLAGS) \\
\$(GEGL_CFLAGS) \\
-I\$(includedir)
$dirs
$bins
EXTRA_PROGRAMS = \\
$opts
install-\%: \%
\@\$(NORMAL_INSTALL)
\$(mkinstalldirs) \$(DESTDIR)\$(pikaplugindir)/plug-ins/\$<
\@p=\$<; p1=`echo \$\$p|sed 's/\$(EXEEXT)\$\$//'`; \\
if test -f \$\$p \\
|| test -f \$\$p1 \\
; then \\
f=`echo "\$\$p1" | sed 's,^.*/,,;\$(transform);s/\$\$/\$(EXEEXT)/'`; \\
echo " \$(INSTALL_PROGRAM_ENV) \$(LIBTOOL) --mode=install \$(INSTALL_PROGRAM) \$\$p \$(DESTDIR)\$(pikaplugindir)/plug-ins/\$\$p/\$\$f"; \\
\$(INSTALL_PROGRAM_ENV) \$(LIBTOOL) --mode=install \$(INSTALL_PROGRAM) \$\$p \$(DESTDIR)\$(pikaplugindir)/plug-ins/\$\$p/\$\$f || exit 1; \\
else :; fi
EOT
print IGNORE <<EOT;
/.deps
/.libs
/Makefile
/Makefile.in
EOT
foreach (sort keys %plugins) {
my $makename = $_;
$makename =~ s/-/_/g;
my $libpika = "";
if (exists $plugins{$_}->{ui}) {
$libpika .= "\$(libpikaui)";
$libpika .= "\t\t\\\n\t\$(libpikawidgets)";
$libpika .= "\t\\\n\t\$(libpikamodule)";
$libpika .= "\t\\\n\t";
}
$libpika .= "\$(libpika)";
$libpika .= "\t\t\\\n\t\$(libpikamath)";
$libpika .= "\t\t\\\n\t\$(libpikaconfig)";
$libpika .= "\t\\\n\t\$(libpikacolor)";
$libpika .= "\t\t\\\n\t\$(libpikabase)";
my $glib;
if (exists $plugins{$_}->{ui}) {
$glib = "\$(GTK_LIBS)\t\t\\";
} else {
$glib = "\$(CAIRO_LIBS)\t\t\\\n\t\$(GDK_PIXBUF_LIBS)\t\\";
if (exists $plugins{$_}->{gio} &&
! exists $plugins{$_}->{gegl}) {
$glib .= "\n\t\$(GIO_LIBS)\t\t\\";
}
}
if (exists $plugins{$_}->{gegl}) {
$glib .= "\n\t\$(GEGL_LIBS)\t\t\\";
}
my $optlib = "";
if (exists $plugins{$_}->{libs}) {
$optlib = "\n\t\$(" . $plugins{$_}->{libs} . ")\t\t\\";
}
if (exists $plugins{$_}->{ldflags}) {
my $ldflags = $plugins{$_}->{ldflags};
print MK <<EOT;
${makename}_LDFLAGS = $ldflags
EOT
}
if (exists $plugins{$_}->{cflags}) {
my $cflags = $plugins{$_}->{cflags};
my $cflagsvalue = $cflags =~ /FLAGS/ ? "\$($cflags)" : $cflags;
print MK <<EOT;
${makename}_CFLAGS = $cflagsvalue
EOT
}
if (exists $plugins{$_}->{cppflags}) {
my $cppflags = $plugins{$_}->{cppflags};
print MK <<EOT;
${makename}_CPPFLAGS = $cppflags
EOT
}
my $deplib = "\$(RT_LIBS)\t\t\\\n\t\$(INTLLIBS)";
if (exists $plugins{$_}->{libdep}) {
my @lib = split(/:/, $plugins{$_}->{libdep});
foreach $lib (@lib) {
$deplib = "\$(\U$lib\E_LIBS)\t\t\\\n\t$deplib";
}
}
my $rclib = "\$(${makename}_RC)";
print MK <<EOT;
${makename}_SOURCES = \\
$_.c
${makename}_LDADD = \\
$libpika \\
$glib$optlib
$deplib \\
$rclib
EOT
print RC <<EOT;
${makename}_RC = $_.rc.o
EOT
print IGNORE "/$_\n";
print IGNORE "/$_.exe\n";
}
close RC;
close MK;
close IGNORE;
&write_file($outmk, $destdir);
&write_file($outignore, $destdir);
&write_file($outrc, $destdir);

1184
plug-ins/common/nl-filter.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,77 @@
align_layers_RC = align-layers.rc.o
animation_optimize_RC = animation-optimize.rc.o
animation_play_RC = animation-play.rc.o
blinds_RC = blinds.rc.o
border_average_RC = border-average.rc.o
busy_dialog_RC = busy-dialog.rc.o
checkerboard_RC = checkerboard.rc.o
cml_explorer_RC = cml-explorer.rc.o
colormap_remap_RC = colormap-remap.rc.o
compose_RC = compose.rc.o
contrast_retinex_RC = contrast-retinex.rc.o
crop_zealous_RC = crop-zealous.rc.o
curve_bend_RC = curve-bend.rc.o
decompose_RC = decompose.rc.o
depth_merge_RC = depth-merge.rc.o
despeckle_RC = despeckle.rc.o
destripe_RC = destripe.rc.o
file_aa_RC = file-aa.rc.o
file_cel_RC = file-cel.rc.o
file_compressor_RC = file-compressor.rc.o
file_csource_RC = file-csource.rc.o
file_desktop_link_RC = file-desktop-link.rc.o
file_dicom_RC = file-dicom.rc.o
file_gbr_RC = file-gbr.rc.o
file_gegl_RC = file-gegl.rc.o
file_gif_load_RC = file-gif-load.rc.o
file_gif_save_RC = file-gif-save.rc.o
file_gih_RC = file-gih.rc.o
file_glob_RC = file-glob.rc.o
file_header_RC = file-header.rc.o
file_heif_RC = file-heif.rc.o
file_html_table_RC = file-html-table.rc.o
file_jp2_load_RC = file-jp2-load.rc.o
file_jpegxl_RC = file-jpegxl.rc.o
file_mng_RC = file-mng.rc.o
file_pat_RC = file-pat.rc.o
file_pcx_RC = file-pcx.rc.o
file_pdf_load_RC = file-pdf-load.rc.o
file_pdf_save_RC = file-pdf-save.rc.o
file_pix_RC = file-pix.rc.o
file_png_RC = file-png.rc.o
file_pnm_RC = file-pnm.rc.o
file_ps_RC = file-ps.rc.o
file_psp_RC = file-psp.rc.o
file_raw_data_RC = file-raw-data.rc.o
file_sunras_RC = file-sunras.rc.o
file_svg_RC = file-svg.rc.o
file_tga_RC = file-tga.rc.o
file_wbmp_RC = file-wbmp.rc.o
file_wmf_RC = file-wmf.rc.o
file_xbm_RC = file-xbm.rc.o
file_xmc_RC = file-xmc.rc.o
file_xpm_RC = file-xpm.rc.o
file_xwd_RC = file-xwd.rc.o
film_RC = film.rc.o
gradient_map_RC = gradient-map.rc.o
grid_RC = grid.rc.o
guillotine_RC = guillotine.rc.o
hot_RC = hot.rc.o
jigsaw_RC = jigsaw.rc.o
mail_RC = mail.rc.o
nl_filter_RC = nl-filter.rc.o
plugin_browser_RC = plugin-browser.rc.o
procedure_browser_RC = procedure-browser.rc.o
qbist_RC = qbist.rc.o
sample_colorize_RC = sample-colorize.rc.o
smooth_palette_RC = smooth-palette.rc.o
sparkle_RC = sparkle.rc.o
sphere_designer_RC = sphere-designer.rc.o
tile_RC = tile.rc.o
tile_small_RC = tile-small.rc.o
unit_editor_RC = unit-editor.rc.o
van_gogh_lic_RC = van-gogh-lic.rc.o
warp_RC = warp.rc.o
wavelet_decompose_RC = wavelet-decompose.rc.o
web_browser_RC = web-browser.rc.o
web_page_RC = web-page.rc.o

View File

@ -0,0 +1,878 @@
/*
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* This is a plug-in for PIKA.
*
* Copyright (C) 1999 Andy Thomas alt@picnic.demon.co.uk
*
* Note some portions of the UI comes from the dbbrowser plugin.
* 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/>.
*/
#include "config.h"
#include <string.h>
#include <time.h>
#include <gtk/gtk.h>
#include <libpika/pika.h>
#include <libpika/pikaui.h>
#include "libpika/stdplugins-intl.h"
#define PLUG_IN_PROC "plug-in-plug-in-details"
#define PLUG_IN_BINARY "plugin-browser"
#define PLUG_IN_ROLE "pika-plugin-browser"
#define DBL_LIST_WIDTH 250
#define DBL_WIDTH (DBL_LIST_WIDTH + 400)
#define DBL_HEIGHT 250
enum
{
LIST_COLUMN_NAME,
LIST_COLUMN_DATE,
LIST_COLUMN_DATE_STRING,
LIST_COLUMN_PATH,
LIST_COLUMN_IMAGE_TYPES,
LIST_COLUMN_PINFO,
N_LIST_COLUMNS
};
enum
{
TREE_COLUMN_PATH_NAME,
TREE_COLUMN_DATE,
TREE_COLUMN_DATE_STRING,
TREE_COLUMN_IMAGE_TYPES,
TREE_COLUMN_MPATH,
TREE_COLUMN_PINFO,
N_TREE_OLUMNS
};
typedef struct
{
GtkWidget *dialog;
GtkWidget *browser;
GtkTreeView *list_view;
GtkTreeView *tree_view;
} PluginBrowser;
typedef struct
{
gchar *menu;
gchar *accel;
gchar *prog;
gchar *procedure;
gint instime;
} PInfo;
typedef struct _Browser Browser;
typedef struct _BrowserClass BrowserClass;
struct _Browser
{
PikaPlugIn parent_instance;
};
struct _BrowserClass
{
PikaPlugInClass parent_class;
};
/* Declare local functions.
*/
#define BROWSER_TYPE (browser_get_type ())
#define BROWSER (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), BROWSER_TYPE, Browser))
GType browser_get_type (void) G_GNUC_CONST;
static GList * browser_query_procedures (PikaPlugIn *plug_in);
static PikaProcedure * browser_create_procedure (PikaPlugIn *plug_in,
const gchar *name);
static PikaValueArray * browser_run (PikaProcedure *procedure,
const PikaValueArray *args,
gpointer run_data);
static GtkWidget * browser_dialog_new (void);
static void browser_dialog_response (GtkWidget *widget,
gint response_id,
PluginBrowser *browser);
static void browser_list_selection_changed (GtkTreeSelection *selection,
PluginBrowser *browser);
static void browser_tree_selection_changed (GtkTreeSelection *selection,
PluginBrowser *browser);
static gboolean find_existing_mpath (GtkTreeModel *model,
const gchar *mpath,
GtkTreeIter *return_iter);
G_DEFINE_TYPE (Browser, browser, PIKA_TYPE_PLUG_IN)
PIKA_MAIN (BROWSER_TYPE)
DEFINE_STD_SET_I18N
static void
browser_class_init (BrowserClass *klass)
{
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
plug_in_class->query_procedures = browser_query_procedures;
plug_in_class->create_procedure = browser_create_procedure;
plug_in_class->set_i18n = STD_SET_I18N;
}
static void
browser_init (Browser *browser)
{
}
static GList *
browser_query_procedures (PikaPlugIn *plug_in)
{
return g_list_append (NULL, g_strdup (PLUG_IN_PROC));
}
static PikaProcedure *
browser_create_procedure (PikaPlugIn *plug_in,
const gchar *procedure_name)
{
PikaProcedure *procedure = NULL;
if (! strcmp (procedure_name, PLUG_IN_PROC))
{
procedure = pika_procedure_new (plug_in, procedure_name,
PIKA_PDB_PROC_TYPE_PLUGIN,
browser_run, NULL, NULL);
pika_procedure_set_menu_label (procedure, _("_Plug-In Browser"));
pika_procedure_set_icon_name (procedure, PIKA_ICON_PLUGIN);
pika_procedure_add_menu_path (procedure, "<Image>/Help/[Programming]");
pika_procedure_set_documentation (procedure,
_("Display information about plug-ins"),
_("Allows one to browse the plug-in "
"menus system. You can search for "
"plug-in names, sort by name or menu "
"location and you can view a tree "
"representation of the plug-in menus. "
"Can also be of help to find where "
"new plug-ins have installed "
"themselves in the menus."),
PLUG_IN_PROC);
pika_procedure_set_attribution (procedure,
"Andy Thomas",
"Andy Thomas",
"1999");
PIKA_PROC_ARG_ENUM (procedure, "run-mode",
"Run mode",
"The run mode",
PIKA_TYPE_RUN_MODE,
PIKA_RUN_INTERACTIVE,
G_PARAM_READWRITE);
}
return procedure;
}
static PikaValueArray *
browser_run (PikaProcedure *procedure,
const PikaValueArray *args,
gpointer run_data)
{
browser_dialog_new ();
gtk_main ();
return pika_procedure_new_return_values (procedure, PIKA_PDB_SUCCESS, NULL);
}
static gboolean
find_existing_mpath_helper (GtkTreeModel *model,
GtkTreeIter *iter,
GtkTreePath *path,
const gchar *mpath,
GtkTreeIter *return_iter)
{
do
{
GtkTreeIter child;
gchar *picked_mpath;
gtk_tree_model_get (model, iter,
TREE_COLUMN_MPATH, &picked_mpath,
-1);
if (! strcmp (mpath, picked_mpath))
{
*return_iter = *iter;
g_free (picked_mpath);
return TRUE;
}
if (gtk_tree_model_iter_children (model, &child, iter))
{
gtk_tree_path_down (path);
if (find_existing_mpath_helper (model, &child, path,
mpath, return_iter))
{
g_free (picked_mpath);
return TRUE;
}
gtk_tree_path_up (path);
}
gtk_tree_path_next (path);
g_free (picked_mpath);
}
while (gtk_tree_model_iter_next (model, iter));
return FALSE;
}
static gboolean
find_existing_mpath (GtkTreeModel *model,
const gchar *mpath,
GtkTreeIter *return_iter)
{
GtkTreePath *path = gtk_tree_path_new_first ();
GtkTreeIter parent;
gboolean found;
if (! gtk_tree_model_get_iter (model, &parent, path))
{
gtk_tree_path_free (path);
return FALSE;
}
found = find_existing_mpath_helper (model, &parent, path, mpath, return_iter);
gtk_tree_path_free (path);
return found;
}
static void
get_parent (PluginBrowser *browser,
const gchar *mpath,
GtkTreeIter *parent)
{
GtkTreeIter last_parent;
gchar *tmp_ptr;
gchar *str_ptr;
GtkTreeStore *tree_store;
if (! mpath)
return;
tree_store = GTK_TREE_STORE (gtk_tree_view_get_model (browser->tree_view));
/* Lookup for existing mpath */
if (find_existing_mpath (GTK_TREE_MODEL (tree_store), mpath, parent))
return;
tmp_ptr = g_strdup (mpath);
/* Strip off trailing ellipsis */
str_ptr = strstr (mpath, "...");
if (str_ptr && str_ptr == (mpath + strlen (mpath) - 3))
*str_ptr = '\0';
str_ptr = strrchr (tmp_ptr, '/');
if (str_ptr == NULL)
{
gtk_tree_store_append (tree_store, parent, NULL);
gtk_tree_store_set (tree_store, parent,
TREE_COLUMN_MPATH, mpath,
TREE_COLUMN_PATH_NAME, mpath,
-1);
}
else
{
gchar *leaf_ptr;
leaf_ptr = g_strdup (str_ptr + 1);
*str_ptr = '\0';
get_parent (browser, tmp_ptr, &last_parent);
gtk_tree_store_append (tree_store, parent, &last_parent);
gtk_tree_store_set (tree_store, parent,
TREE_COLUMN_MPATH, mpath,
TREE_COLUMN_PATH_NAME, leaf_ptr,
-1);
g_free (leaf_ptr);
}
}
static void
insert_into_tree_view (PluginBrowser *browser,
const gchar *name,
gint64 xtime,
const gchar *xtimestr,
const gchar *menu_path,
const gchar *types_str,
PInfo *pinfo)
{
GtkTreeStore *tree_store;
GtkTreeIter parent, iter;
get_parent (browser, menu_path, &parent);
tree_store = GTK_TREE_STORE (gtk_tree_view_get_model (browser->tree_view));
gtk_tree_store_append (tree_store, &iter, &parent);
gtk_tree_store_set (tree_store, &iter,
TREE_COLUMN_MPATH, menu_path,
TREE_COLUMN_PATH_NAME, name,
TREE_COLUMN_IMAGE_TYPES, types_str,
TREE_COLUMN_DATE, xtime,
TREE_COLUMN_DATE_STRING, xtimestr,
TREE_COLUMN_PINFO, pinfo,
-1);
}
static void
browser_search (PikaBrowser *pika_browser,
const gchar *search_text,
gint search_type,
PluginBrowser *browser)
{
PikaValueArray *return_vals;
const gchar **procedure_strs;
gint num_plugins = 0;
gchar *str;
GtkListStore *list_store;
GtkTreeStore *tree_store;
pika_browser_show_message (PIKA_BROWSER (browser->browser),
_("Searching by name"));
return_vals = pika_pdb_run_procedure (pika_get_pdb (),
"pika-plug-ins-query",
G_TYPE_STRING, search_text,
G_TYPE_NONE);
if (PIKA_VALUES_GET_ENUM (return_vals, 0) == PIKA_PDB_SUCCESS)
{
procedure_strs = PIKA_VALUES_GET_STRV (return_vals, 1);
num_plugins = g_strv_length ((gchar **) procedure_strs);
}
if (! search_text || strlen (search_text) == 0)
{
str = g_strdup_printf (ngettext ("%d plug-in", "%d plug-ins",
num_plugins),
num_plugins);
}
else
{
switch (num_plugins)
{
case 0:
str = g_strdup (_("No matches for your query"));
break;
default:
str = g_strdup_printf (ngettext ("%d plug-in matches your query",
"%d plug-ins match your query",
num_plugins), num_plugins);
break;
}
}
pika_browser_set_search_summary (pika_browser, str);
g_free (str);
list_store = GTK_LIST_STORE (gtk_tree_view_get_model (browser->list_view));
gtk_list_store_clear (list_store);
tree_store = GTK_TREE_STORE (gtk_tree_view_get_model (browser->tree_view));
gtk_tree_store_clear (tree_store);
if (num_plugins > 0)
{
GtkTreeSelection *sel;
GtkTreeIter iter;
const gchar **accel_strs;
const gchar **prog_strs;
const gint *time_ints;
gint i;
accel_strs = PIKA_VALUES_GET_STRV (return_vals, 2);
prog_strs = PIKA_VALUES_GET_STRV (return_vals, 3);
time_ints = PIKA_VALUES_GET_INT32_ARRAY (return_vals, 5);
for (i = 0; i < num_plugins; i++)
{
PikaProcedure *procedure;
const gchar *types;
PInfo *pinfo;
gchar *menu_label;
gchar *tmp;
GList *menu_paths;
const gchar *menu_path;
gchar xtimestr[50];
struct tm *x;
time_t tx;
gint ret;
procedure = pika_pdb_lookup_procedure (pika_get_pdb (),
procedure_strs[i]);
types = pika_procedure_get_image_types (procedure);
menu_label = g_strdup (pika_procedure_get_menu_label (procedure));
menu_paths = pika_procedure_get_menu_paths (procedure);
menu_path = menu_paths->data;
/* Strip off trailing ellipsis */
tmp = strstr (menu_label, "...");
if (tmp && tmp == (menu_label + strlen (menu_label) - 3))
*tmp = '\0';
tmp = pika_strip_uline (menu_label);
g_free (menu_label);
menu_label = tmp;
tx = time_ints[i];
if (tx)
{
const gchar *format = "%c"; /* gcc workaround to avoid warning */
gchar *utf8;
x = localtime (&tx);
ret = strftime (xtimestr, sizeof (xtimestr), format, x);
xtimestr[ret] = 0;
if ((utf8 = g_locale_to_utf8 (xtimestr, -1, NULL, NULL, NULL)))
{
g_strlcpy (xtimestr, utf8, sizeof (xtimestr));
g_free (utf8);
}
}
else
{
strcpy (xtimestr, "");
}
pinfo = g_new0 (PInfo, 1);
pinfo->menu = g_strdup (menu_path);
pinfo->accel = g_strdup (accel_strs[i]);
pinfo->prog = g_strdup (prog_strs[i]);
pinfo->instime = time_ints[i];
pinfo->procedure = g_strdup (procedure_strs[i]);
gtk_list_store_append (list_store, &iter);
gtk_list_store_set (list_store, &iter,
LIST_COLUMN_NAME, menu_label,
LIST_COLUMN_DATE, (gint64) tx,
LIST_COLUMN_DATE_STRING, xtimestr,
LIST_COLUMN_PATH, menu_path,
LIST_COLUMN_IMAGE_TYPES, types,
LIST_COLUMN_PINFO, pinfo,
-1);
/* Now do the tree view.... */
insert_into_tree_view (browser,
menu_label,
(gint64) tx,
xtimestr,
menu_path,
types,
pinfo);
g_free (menu_label);
}
gtk_tree_view_columns_autosize (GTK_TREE_VIEW (browser->list_view));
gtk_tree_view_columns_autosize (GTK_TREE_VIEW (browser->tree_view));
gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (list_store),
LIST_COLUMN_NAME,
GTK_SORT_ASCENDING);
gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (tree_store),
TREE_COLUMN_PATH_NAME,
GTK_SORT_ASCENDING);
sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (browser->list_view));
gtk_tree_model_get_iter_first (GTK_TREE_MODEL (list_store),
&iter);
gtk_tree_selection_select_iter (sel, &iter);
}
else
{
pika_browser_show_message (PIKA_BROWSER (browser->browser),
_("No matches"));
}
pika_value_array_unref (return_vals);
}
static GtkWidget *
browser_dialog_new (void)
{
PluginBrowser *browser;
GtkWidget *label, *notebook;
GtkWidget *scrolled_window;
GtkListStore *list_store;
GtkTreeStore *tree_store;
GtkWidget *list_view;
GtkWidget *tree_view;
GtkWidget *parent;
GtkTreeViewColumn *column;
GtkCellRenderer *renderer;
GtkTreeSelection *selection;
GtkTreeIter iter;
pika_ui_init (PLUG_IN_BINARY);
browser = g_new0 (PluginBrowser, 1);
browser->dialog = pika_dialog_new (_("Plug-in Browser"), PLUG_IN_ROLE,
NULL, 0,
pika_standard_help_func, PLUG_IN_PROC,
_("_Close"), GTK_RESPONSE_CLOSE,
NULL);
gtk_window_set_default_size (GTK_WINDOW (browser->dialog), DBL_WIDTH,
DBL_WIDTH - DBL_LIST_WIDTH);
g_signal_connect (browser->dialog, "response",
G_CALLBACK (browser_dialog_response),
browser);
browser->browser = pika_browser_new ();
gtk_container_set_border_width (GTK_CONTAINER (browser->browser), 12);
gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (browser->dialog))),
browser->browser, TRUE, TRUE, 0);
gtk_widget_show (browser->browser);
g_signal_connect (browser->browser, "search",
G_CALLBACK (browser_search),
browser);
/* left = notebook */
notebook = gtk_notebook_new ();
gtk_box_pack_start (GTK_BOX (pika_browser_get_left_vbox (PIKA_BROWSER (browser->browser))),
notebook, TRUE, TRUE, 0);
/* list : list in a scrolled_win */
list_store = gtk_list_store_new (N_LIST_COLUMNS,
G_TYPE_STRING,
G_TYPE_INT64,
G_TYPE_STRING,
G_TYPE_STRING,
G_TYPE_STRING,
G_TYPE_POINTER);
list_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (list_store));
g_object_unref (list_store);
browser->list_view = GTK_TREE_VIEW (list_view);
renderer = gtk_cell_renderer_text_new ();
column = gtk_tree_view_column_new_with_attributes (_("Name"),
renderer,
"text", LIST_COLUMN_NAME,
NULL);
gtk_tree_view_column_set_sort_column_id (column, LIST_COLUMN_NAME);
gtk_tree_view_append_column (GTK_TREE_VIEW (list_view), column);
renderer = gtk_cell_renderer_text_new ();
column = gtk_tree_view_column_new_with_attributes (_("Menu Path"),
renderer,
"text", LIST_COLUMN_PATH,
NULL);
gtk_tree_view_column_set_sort_column_id (column, LIST_COLUMN_PATH);
gtk_tree_view_append_column (GTK_TREE_VIEW (list_view), column);
renderer = gtk_cell_renderer_text_new ();
column = gtk_tree_view_column_new_with_attributes (_("Image Types"),
renderer,
"text",
LIST_COLUMN_IMAGE_TYPES,
NULL);
gtk_tree_view_column_set_sort_column_id (column, LIST_COLUMN_IMAGE_TYPES);
gtk_tree_view_append_column (GTK_TREE_VIEW (list_view), column);
renderer = gtk_cell_renderer_text_new ();
column = gtk_tree_view_column_new_with_attributes (_("Installation Date"),
renderer,
"text",
LIST_COLUMN_DATE_STRING,
NULL);
gtk_tree_view_column_set_sort_column_id (column, LIST_COLUMN_DATE);
gtk_tree_view_append_column (GTK_TREE_VIEW (list_view), column);
scrolled_window = gtk_scrolled_window_new (NULL, NULL);
gtk_container_set_border_width (GTK_CONTAINER (scrolled_window), 2);
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window),
GTK_SHADOW_IN);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_widget_set_size_request (list_view, DBL_LIST_WIDTH, DBL_HEIGHT);
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list_view));
gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
g_signal_connect (selection, "changed",
G_CALLBACK (browser_list_selection_changed),
browser);
label = gtk_label_new (_("List View"));
gtk_notebook_append_page (GTK_NOTEBOOK (notebook), scrolled_window, label);
gtk_container_add (GTK_CONTAINER (scrolled_window), list_view);
gtk_widget_show (list_view);
gtk_widget_show (scrolled_window);
/* notebook->ctree */
tree_store = gtk_tree_store_new (N_LIST_COLUMNS,
G_TYPE_STRING,
G_TYPE_INT64,
G_TYPE_STRING,
G_TYPE_STRING,
G_TYPE_STRING,
G_TYPE_POINTER);
tree_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (tree_store));
g_object_unref (tree_store);
browser->tree_view = GTK_TREE_VIEW (tree_view);
renderer = gtk_cell_renderer_text_new ();
column = gtk_tree_view_column_new_with_attributes (_("Menu Path"),
renderer,
"text",
TREE_COLUMN_PATH_NAME,
NULL);
gtk_tree_view_column_set_sort_column_id (column, TREE_COLUMN_PATH_NAME);
gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
renderer = gtk_cell_renderer_text_new ();
column = gtk_tree_view_column_new_with_attributes (_("Image Types"),
renderer,
"text",
TREE_COLUMN_IMAGE_TYPES,
NULL);
gtk_tree_view_column_set_sort_column_id (column, TREE_COLUMN_IMAGE_TYPES);
gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
renderer = gtk_cell_renderer_text_new ();
column = gtk_tree_view_column_new_with_attributes (_("Installation Date"),
renderer,
"text",
TREE_COLUMN_DATE_STRING,
NULL);
gtk_tree_view_column_set_sort_column_id (column, TREE_COLUMN_DATE);
gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
scrolled_window = gtk_scrolled_window_new (NULL, NULL);
gtk_container_set_border_width (GTK_CONTAINER (scrolled_window), 2);
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window),
GTK_SHADOW_IN);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_widget_set_size_request (tree_view, DBL_LIST_WIDTH, DBL_HEIGHT);
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view));
gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
g_signal_connect (selection, "changed",
G_CALLBACK (browser_tree_selection_changed),
browser);
label = gtk_label_new (_("Tree View"));
gtk_notebook_append_page (GTK_NOTEBOOK (notebook), scrolled_window, label);
gtk_container_add (GTK_CONTAINER (scrolled_window), tree_view);
gtk_widget_show (tree_view);
gtk_widget_show (scrolled_window);
gtk_widget_show (notebook);
parent = gtk_widget_get_parent (pika_browser_get_right_vbox (PIKA_BROWSER (browser->browser)));
parent = gtk_widget_get_parent (parent);
gtk_widget_set_size_request (parent, DBL_WIDTH - DBL_LIST_WIDTH, -1);
/* now build the list */
browser_search (PIKA_BROWSER (browser->browser), "", 0, browser);
gtk_widget_show (browser->dialog);
if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (list_store), &iter))
gtk_tree_selection_select_iter (gtk_tree_view_get_selection (GTK_TREE_VIEW (list_view)),
&iter);
return browser->dialog;
}
static void
browser_dialog_response (GtkWidget *widget,
gint response_id,
PluginBrowser *browser)
{
gtk_widget_destroy (browser->dialog);
gtk_main_quit ();
}
static void
browser_list_selection_changed (GtkTreeSelection *selection,
PluginBrowser *browser)
{
PInfo *pinfo = NULL;
GtkTreeIter iter;
GtkTreeModel *model;
gchar *mpath = NULL;
g_return_if_fail (browser != NULL);
if (gtk_tree_selection_get_selected (selection, &model, &iter))
{
gtk_tree_model_get (model, &iter,
LIST_COLUMN_PINFO, &pinfo,
LIST_COLUMN_PATH, &mpath,
-1);
}
if (!pinfo || !mpath)
return;
model = gtk_tree_view_get_model (browser->tree_view);
if (find_existing_mpath (model, mpath, &iter))
{
GtkTreeSelection *tree_selection;
GtkTreePath *tree_path;
tree_path = gtk_tree_model_get_path (model, &iter);
gtk_tree_view_expand_to_path (browser->tree_view, tree_path);
tree_selection = gtk_tree_view_get_selection (browser->tree_view);
g_signal_handlers_block_by_func (tree_selection,
browser_tree_selection_changed,
browser);
gtk_tree_selection_select_iter (tree_selection, &iter);
g_signal_handlers_unblock_by_func (tree_selection,
browser_tree_selection_changed,
browser);
gtk_tree_view_scroll_to_cell (browser->tree_view,
tree_path, NULL,
TRUE, 0.5, 0.0);
}
else
{
g_warning ("Failed to find node in tree");
}
g_free (mpath);
pika_browser_set_widget (PIKA_BROWSER (browser->browser),
pika_proc_view_new (pinfo->procedure));
}
static void
browser_tree_selection_changed (GtkTreeSelection *selection,
PluginBrowser *browser)
{
PInfo *pinfo = NULL;
GtkTreeIter iter;
GtkTreeModel *model;
gchar *mpath = NULL;
gboolean valid, found;
g_return_if_fail (browser != NULL);
if (gtk_tree_selection_get_selected (selection, &model, &iter))
{
gtk_tree_model_get (model, &iter,
TREE_COLUMN_PINFO, &pinfo,
TREE_COLUMN_MPATH, &mpath,
-1);
}
if (!pinfo || !mpath)
return;
/* Get the first iter in the list */
model = gtk_tree_view_get_model (browser->list_view);
valid = gtk_tree_model_get_iter_first (model, &iter);
found = FALSE;
while (valid)
{
/* Walk through the list, reading each row */
gchar *picked_mpath;
gtk_tree_model_get (model, &iter,
LIST_COLUMN_PATH, &picked_mpath,
-1);
if (picked_mpath && !strcmp (mpath, picked_mpath))
{
found = TRUE;
break;
}
g_free (picked_mpath);
valid = gtk_tree_model_iter_next (model, &iter);
}
g_free (mpath);
if (found)
{
GtkTreeSelection *list_selection;
GtkTreePath *tree_path;
tree_path = gtk_tree_model_get_path (model, &iter);
list_selection = gtk_tree_view_get_selection (browser->list_view);
g_signal_handlers_block_by_func (list_selection,
browser_list_selection_changed,
browser);
gtk_tree_selection_select_iter (list_selection, &iter);
g_signal_handlers_unblock_by_func (list_selection,
browser_list_selection_changed,
browser);
gtk_tree_view_scroll_to_cell (browser->list_view,
tree_path, NULL,
TRUE, 0.5, 0.0);
}
else
{
g_warning ("Failed to find node in list");
}
pika_browser_set_widget (PIKA_BROWSER (browser->browser),
pika_proc_view_new (pinfo->procedure));
}

View File

@ -0,0 +1,79 @@
%plugins = (
'align-layers' => { ui => 1 },
'animation-optimize' => { gegl => 1 },
'animation-play' => { ui => 1, gegl => 1 },
'blinds' => { ui => 1, gegl => 1 },
'border-average' => { ui => 1, gegl => 1 },
'busy-dialog' => { ui => 1, gegl => 1 },
'checkerboard' => { ui => 1, gegl => 1 },
'cml-explorer' => { ui => 1, gegl => 1 },
'colormap-remap' => { ui => 1, gegl => 1 },
'compose' => { ui => 1, gegl => 1 },
'contrast-retinex' => { ui => 1, gegl => 1 },
'crop-zealous' => { gegl => 1 },
'curve-bend' => { ui => 1, gegl => 1 },
'decompose' => { ui => 1, gegl => 1 },
'depth-merge' => { ui => 1, gegl => 1 },
'despeckle' => { ui => 1, gegl => 1 },
'destripe' => { ui => 1, gegl => 1 },
'file-aa' => { ui => 1, gegl => 1, optional => 1, libs => 'AA_LIBS' },
'file-cel' => { ui => 1, gegl => 1 },
'file-csource' => { ui => 1, gegl => 1 },
'file-compressor' => { gio => 1, libdep => 'Z:BZIP2:LZMA', cflags => 'LZMA_CFLAGS' },
'file-desktop-link' => { gio => 1 },
'file-dicom' => { ui => 1, gegl => 1, cflags => '-fno-strict-aliasing' },
'file-gbr' => { ui => 1, gegl => 1 },
'file-gegl' => { ui => 1, gegl => 1 },
'file-gif-load' => { gegl => 1 },
'file-gif-save' => { ui => 1, gegl => 1 },
'file-gih' => { ui => 1, gegl => 1 },
'file-glob' => {},
'file-header' => { ui => 1, gegl => 1 },
'file-heif' => { ui => 1, optional => 1, gegl => 1, libdep => 'GEXIV2:LCMS', libs => 'LIBHEIF_LIBS', cflags => 'LIBHEIF_CFLAGS' },
'file-html-table' => { ui => 1, gegl => 1 },
'file-jp2-load' => { ui => 1, optional => 1, gegl => 1, libs => 'OPENJPEG_LIBS', cflags => 'OPENJPEG_CFLAGS' },
'file-jpegxl' => { ui => 1, optional => 1, gegl => 1, libdep => 'GEXIV2:JXL:JXL_THREADS', cflags => 'JXL_CFLAGS' },
'file-mng' => { ui => 1, gegl => 1, optional => 1, libs => 'MNG_LIBS', cflags => 'MNG_CFLAGS' },
'file-pat' => { ui => 1, gegl => 1 },
'file-pcx' => { ui => 1, gegl => 1 },
'file-pix' => { ui => 1, gegl => 1 },
'file-png' => { ui => 1, gegl => 0, libdep => 'LCMS', libs => 'PNG_LIBS', cflags => 'PNG_CFLAGS' },
'file-pnm' => { ui => 1, gegl => 1 },
'file-pdf-load' => { ui => 1, gegl => 1, libs => 'POPPLER_LIBS', cflags => 'POPPLER_CFLAGS' },
'file-pdf-save' => { ui => 1, gegl => 1, optional => 1, libs => 'CAIRO_PDF_LIBS', cflags => 'CAIRO_PDF_CFLAGS' },
'file-ps' => { ui => 1, gegl => 1, optional => 1, libs => 'GS_LIBS' },
'file-psp' => { ui => 1, gegl => 1, libs => 'Z_LIBS' },
'file-raw-data' => { ui => 1, gegl => 1 },
'file-sunras' => { ui => 1, gegl => 1 },
'file-svg' => { ui => 1, gegl => 1, libs => 'SVG_LIBS', cflags => 'SVG_CFLAGS' },
'file-tga' => { ui => 1, gegl => 1 },
'file-wbmp' => { ui => 1, gegl => 1 },
'file-wmf' => { ui => 1, gegl => 1, optional => 1, libs => 'WMF_LIBS', cflags => 'WMF_CFLAGS' },
'file-xbm' => { ui => 1, gegl => 1 },
'file-xmc' => { ui => 1, gegl => 1, optional => 1, libs => 'XMC_LIBS' },
'file-xpm' => { ui => 1, gegl => 1, optional => 1, libs => 'XPM_LIBS' },
'file-xwd' => { ui => 1, gegl => 1 },
'film' => { ui => 1, gegl => 1 },
'gradient-map' => { gegl => 1 },
'grid' => { ui => 1, gegl => 1 },
'guillotine' => { gio => 1 },
'hot' => { ui => 1, gegl => 1 },
'jigsaw' => { ui => 1, gegl => 1 },
'mail' => { ui => 1, optional => 1 },
'nl-filter' => { ui => 1, gegl => 1 },
'plugin-browser' => { ui => 1 },
'procedure-browser' => { ui => 1 },
'qbist' => { ui => 1, gegl => 1 },
'sample-colorize' => { ui => 1, gegl => 1 },
'smooth-palette' => { ui => 1, gegl => 1 },
'sparkle' => { ui => 1, gegl => 1 },
'sphere-designer' => { ui => 1, gegl => 1 },
'tile' => { ui => 1, gegl => 1 },
'tile-small' => { ui => 1, gegl => 1 },
'unit-editor' => { ui => 1 },
'van-gogh-lic' => { ui => 1, gegl => 1 },
'warp' => { ui => 1, gegl => 1 },
'wavelet-decompose' => { ui => 1, gegl => 1 },
'web-browser' => { ui => 1, ldflags => '$(framework_cocoa)', cppflags => '$(AM_CPPFLAGS) $(xobjective_c)' },
'web-page' => { ui => 1, optional => 1, libs => 'WEBKIT_LIBS', cflags => 'WEBKIT_CFLAGS' }
);

View File

@ -0,0 +1,195 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* 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/>.
*/
/*
* dbbrowser
* 0.08 26th sept 97 by Thomas NOEL <thomas@minet.net>
*/
/*
* This plugin gives you the list of available procedure, with the
* name, description and parameters for each procedure.
* You can do regexp search (by name and by description)
* Useful for scripts development.
*
* NOTE :
* this is only a exercice for me (my first "plug-in" (extension))
* so it's very (very) dirty.
* Btw, hope it gives you some ideas about pika possibilities.
*
* The core of the plugin is not here. See dbbrowser_utils (shared
* with script-fu-console).
*
* TODO
* - bug fixes... (my method : rewrite from scratch :)
*/
#include "config.h"
#include <gtk/gtk.h>
#include <libpika/pika.h>
#include <libpika/pikaui.h>
#include "libpika/stdplugins-intl.h"
#define PLUG_IN_PROC "plug-in-dbbrowser"
#define PLUG_IN_BINARY "procedure-browser"
#define PLUG_IN_ROLE "pika-procedure-browser"
typedef struct _Browser Browser;
typedef struct _BrowserClass BrowserClass;
struct _Browser
{
PikaPlugIn parent_instance;
};
struct _BrowserClass
{
PikaPlugInClass parent_class;
};
/* Declare local functions.
*/
#define BROWSER_TYPE (browser_get_type ())
#define BROWSER (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), BROWSER_TYPE, Browser))
GType browser_get_type (void) G_GNUC_CONST;
static GList * browser_query_procedures (PikaPlugIn *plug_in);
static PikaProcedure * browser_create_procedure (PikaPlugIn *plug_in,
const gchar *name);
static PikaValueArray * browser_run (PikaProcedure *procedure,
const PikaValueArray *args,
gpointer run_data);
G_DEFINE_TYPE (Browser, browser, PIKA_TYPE_PLUG_IN)
PIKA_MAIN (BROWSER_TYPE)
DEFINE_STD_SET_I18N
static void
browser_class_init (BrowserClass *klass)
{
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
plug_in_class->query_procedures = browser_query_procedures;
plug_in_class->create_procedure = browser_create_procedure;
plug_in_class->set_i18n = STD_SET_I18N;
}
static void
browser_init (Browser *browser)
{
}
static GList *
browser_query_procedures (PikaPlugIn *plug_in)
{
return g_list_append (NULL, g_strdup (PLUG_IN_PROC));
}
static PikaProcedure *
browser_create_procedure (PikaPlugIn *plug_in,
const gchar *name)
{
PikaProcedure *procedure = NULL;
if (! strcmp (name, PLUG_IN_PROC))
{
procedure = pika_procedure_new (plug_in, name,
PIKA_PDB_PROC_TYPE_PLUGIN,
browser_run, NULL, NULL);
pika_procedure_set_menu_label (procedure, _("Procedure _Browser"));
pika_procedure_add_menu_path (procedure, "<Image>/Help/[Programming]");
pika_procedure_set_documentation (procedure,
_("List available procedures in the PDB"),
NULL,
PLUG_IN_PROC);
pika_procedure_set_attribution (procedure,
"Thomas Noel",
"Thomas Noel",
"23th june 1997");
PIKA_PROC_ARG_ENUM (procedure, "run-mode",
"Run mode",
"The run mode",
PIKA_TYPE_RUN_MODE,
PIKA_RUN_INTERACTIVE,
G_PARAM_READWRITE);
}
return procedure;
}
static PikaValueArray *
browser_run (PikaProcedure *procedure,
const PikaValueArray *args,
gpointer run_data)
{
PikaRunMode run_mode = PIKA_VALUES_GET_ENUM (args, 0);
switch (run_mode)
{
case PIKA_RUN_INTERACTIVE:
{
GtkWidget *dialog;
pika_ui_init (PLUG_IN_BINARY);
dialog =
pika_proc_browser_dialog_new (_("Procedure Browser"), PLUG_IN_BINARY,
pika_standard_help_func, PLUG_IN_PROC,
_("_Close"), GTK_RESPONSE_CLOSE,
NULL);
gtk_dialog_run (GTK_DIALOG (dialog));
gtk_widget_destroy (dialog);
}
break;
case PIKA_RUN_WITH_LAST_VALS:
case PIKA_RUN_NONINTERACTIVE:
g_printerr (PLUG_IN_PROC " allows only interactive invocation");
return pika_procedure_new_return_values (procedure,
PIKA_PDB_CALLING_ERROR,
NULL);
break;
default:
break;
}
return pika_procedure_new_return_values (procedure, PIKA_PDB_SUCCESS, NULL);
}

1059
plug-ins/common/qbist.c Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,571 @@
/*
* smooth palette - derive smooth palette from image
* Copyright (C) 1997 Scott Draves <spot@cs.cmu.edu>
*
* PIKA - Photo and Image Kooker Application
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* 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/>.
*/
#include "config.h"
#include <string.h>
#include <libpika/pika.h>
#include <libpika/pikaui.h>
#include "libpika/stdplugins-intl.h"
#define PLUG_IN_PROC "plug-in-smooth-palette"
#define PLUG_IN_BINARY "smooth-palette"
#define PLUG_IN_ROLE "pika-smooth-palette"
typedef struct _Palette Palette;
typedef struct _PaletteClass PaletteClass;
struct _Palette
{
PikaPlugIn parent_instance;
};
struct _PaletteClass
{
PikaPlugInClass parent_class;
};
#define PALETTE_TYPE (palette_get_type ())
#define PALETTE (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PALETTE_TYPE, Palette))
GType palette_get_type (void) G_GNUC_CONST;
static GList * palette_query_procedures (PikaPlugIn *plug_in);
static PikaProcedure * palette_create_procedure (PikaPlugIn *plug_in,
const gchar *name);
static PikaValueArray * palette_run (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
const PikaValueArray *args,
gpointer run_data);
static gboolean dialog (PikaDrawable *drawable);
static PikaImage * smooth_palette (PikaDrawable *drawable,
PikaLayer **layer);
G_DEFINE_TYPE (Palette, palette, PIKA_TYPE_PLUG_IN)
PIKA_MAIN (PALETTE_TYPE)
DEFINE_STD_SET_I18N
static struct
{
gint width;
gint height;
gint ntries;
gint try_size;
gboolean show_image;
} config =
{
256,
64,
50,
10000,
TRUE
};
static void
palette_class_init (PaletteClass *klass)
{
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
plug_in_class->query_procedures = palette_query_procedures;
plug_in_class->create_procedure = palette_create_procedure;
plug_in_class->set_i18n = STD_SET_I18N;
}
static void
palette_init (Palette *palette)
{
}
static GList *
palette_query_procedures (PikaPlugIn *plug_in)
{
return g_list_append (NULL, g_strdup (PLUG_IN_PROC));
}
static PikaProcedure *
palette_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,
palette_run, NULL, NULL);
pika_procedure_set_image_types (procedure, "RGB*");
pika_procedure_set_sensitivity_mask (procedure,
PIKA_PROCEDURE_SENSITIVE_DRAWABLE);
pika_procedure_set_menu_label (procedure, _("Smoo_th Palette..."));
pika_procedure_add_menu_path (procedure, "<Image>/Colors/Info");
pika_procedure_set_documentation (procedure,
_("Derive a smooth color palette "
"from the image"),
"help!",
name);
pika_procedure_set_attribution (procedure,
"Scott Draves",
"Scott Draves",
"1997");
PIKA_PROC_ARG_INT (procedure, "width",
"Widtg",
"Widtg",
2, PIKA_MAX_IMAGE_SIZE, 256,
G_PARAM_READWRITE);
PIKA_PROC_ARG_INT (procedure, "height",
"Height",
"Height",
2, PIKA_MAX_IMAGE_SIZE, 64,
G_PARAM_READWRITE);
PIKA_PROC_ARG_INT (procedure, "n-tries",
"N tries",
"Search septh",
1, 1024, 50,
G_PARAM_READWRITE);
PIKA_PROC_ARG_BOOLEAN (procedure, "show-image",
"Show image",
"Show image",
TRUE,
G_PARAM_READWRITE);
PIKA_PROC_VAL_IMAGE (procedure, "new-image",
"New image",
"Output image",
FALSE,
G_PARAM_READWRITE);
PIKA_PROC_VAL_LAYER (procedure, "new-layer",
"New layer",
"Output layer",
FALSE,
G_PARAM_READWRITE);
}
return procedure;
}
static PikaValueArray *
palette_run (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
const PikaValueArray *args,
gpointer run_data)
{
PikaValueArray *return_vals;
PikaImage *new_image;
PikaLayer *new_layer;
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];
}
switch (run_mode)
{
case PIKA_RUN_INTERACTIVE:
pika_get_data (PLUG_IN_PROC, &config);
if (! dialog (drawable))
{
return pika_procedure_new_return_values (procedure,
PIKA_PDB_CANCEL,
NULL);
}
break;
case PIKA_RUN_NONINTERACTIVE:
config.width = PIKA_VALUES_GET_INT (args, 0);
config.height = PIKA_VALUES_GET_INT (args, 1);
config.ntries = PIKA_VALUES_GET_INT (args, 2);
config.show_image = PIKA_VALUES_GET_BOOLEAN (args, 3);
break;
case PIKA_RUN_WITH_LAST_VALS:
pika_get_data (PLUG_IN_PROC, &config);
break;
}
if (pika_drawable_is_rgb (drawable))
{
pika_progress_init (_("Deriving smooth palette"));
new_image = smooth_palette (drawable, &new_layer);
if (run_mode == PIKA_RUN_INTERACTIVE)
pika_set_data (PLUG_IN_PROC, &config, sizeof (config));
if (config.show_image)
pika_display_new (new_image);
}
else
{
return pika_procedure_new_return_values (procedure,
PIKA_PDB_EXECUTION_ERROR,
NULL);
}
return_vals = pika_procedure_new_return_values (procedure,
PIKA_PDB_SUCCESS,
NULL);
PIKA_VALUES_SET_IMAGE (return_vals, 1, new_image);
PIKA_VALUES_SET_LAYER (return_vals, 2, new_layer);
return return_vals;
}
static gfloat
pix_diff (gfloat *pal,
guint bpp,
gint i,
gint j)
{
gfloat r = 0.f;
guint k;
for (k = 0; k < bpp; k++)
{
gfloat p1 = pal[j * bpp + k];
gfloat p2 = pal[i * bpp + k];
r += (p1 - p2) * (p1 - p2);
}
return r;
}
static void
pix_swap (gfloat *pal,
guint bpp,
gint i,
gint j)
{
guint k;
for (k = 0; k < bpp; k++)
{
gfloat t = pal[j * bpp + k];
pal[j * bpp + k] = pal[i * bpp + k];
pal[i * bpp + k] = t;
}
}
static PikaImage *
smooth_palette (PikaDrawable *drawable,
PikaLayer **layer)
{
PikaImage *new_image;
gint psize, i, j;
guint bpp;
gint sel_x1, sel_y1;
gint width, height;
GeglBuffer *buffer;
GeglSampler *sampler;
gfloat *pal;
GRand *gr;
const Babl *format = babl_format ("RGB float");
new_image = pika_image_new_with_precision (config.width,
config.height,
PIKA_RGB,
PIKA_PRECISION_FLOAT_LINEAR);
pika_image_undo_disable (new_image);
*layer = pika_layer_new (new_image, _("Background"),
config.width, config.height,
pika_drawable_type (drawable),
100,
pika_image_get_default_new_layer_mode (new_image));
pika_image_insert_layer (new_image, *layer, NULL, 0);
if (! pika_drawable_mask_intersect (drawable,
&sel_x1, &sel_y1, &width, &height))
return new_image;
gr = g_rand_new ();
psize = config.width;
buffer = pika_drawable_get_buffer (drawable);
sampler = gegl_buffer_sampler_new (buffer, format, GEGL_SAMPLER_NEAREST);
bpp = babl_format_get_n_components (gegl_buffer_get_format (buffer));
pal = g_new (gfloat, psize * bpp);
/* get initial palette */
for (i = 0; i < psize; i++)
{
gint x = sel_x1 + g_rand_int_range (gr, 0, width);
gint y = sel_y1 + g_rand_int_range (gr, 0, height);
gegl_sampler_get (sampler,
(gdouble) x, (gdouble) y, NULL, pal + i * bpp,
GEGL_ABYSS_NONE);
}
g_object_unref (sampler);
g_object_unref (buffer);
/* reorder */
if (1)
{
gfloat *pal_best;
gfloat *original;
gdouble len_best = 0;
gint try;
pal_best = g_memdup2 (pal, bpp * psize);
original = g_memdup2 (pal, bpp * psize);
for (try = 0; try < config.ntries; try++)
{
gdouble len;
if (!(try%5))
pika_progress_update (try / (double) config.ntries);
memcpy (pal, original, bpp * psize);
/* scramble */
for (i = 1; i < psize; i++)
pix_swap (pal, bpp, i, g_rand_int_range (gr, 0, psize));
/* measure */
len = 0.0;
for (i = 1; i < psize; i++)
len += pix_diff (pal, bpp, i, i-1);
/* improve */
for (i = 0; i < config.try_size; i++)
{
gint i0 = 1 + g_rand_int_range (gr, 0, psize-2);
gint i1 = 1 + g_rand_int_range (gr, 0, psize-2);
gfloat as_is, swapd;
if (1 == (i0 - i1))
{
as_is = (pix_diff (pal, bpp, i1 - 1, i1) +
pix_diff (pal, bpp, i0, i0 + 1));
swapd = (pix_diff (pal, bpp, i1 - 1, i0) +
pix_diff (pal, bpp, i1, i0 + 1));
}
else if (1 == (i1 - i0))
{
as_is = (pix_diff (pal, bpp, i0 - 1, i0) +
pix_diff (pal, bpp, i1, i1 + 1));
swapd = (pix_diff (pal, bpp, i0 - 1, i1) +
pix_diff (pal, bpp, i0, i1 + 1));
}
else
{
as_is = (pix_diff (pal, bpp, i0, i0 + 1) +
pix_diff (pal, bpp, i0, i0 - 1) +
pix_diff (pal, bpp, i1, i1 + 1) +
pix_diff (pal, bpp, i1, i1 - 1));
swapd = (pix_diff (pal, bpp, i1, i0 + 1) +
pix_diff (pal, bpp, i1, i0 - 1) +
pix_diff (pal, bpp, i0, i1 + 1) +
pix_diff (pal, bpp, i0, i1 - 1));
}
if (swapd < as_is)
{
pix_swap (pal, bpp, i0, i1);
len += swapd - as_is;
}
}
/* best? */
if (0 == try || len < len_best)
{
memcpy (pal_best, pal, bpp * psize);
len_best = len;
}
}
pika_progress_update (1.0);
memcpy (pal, pal_best, bpp * psize);
g_free (pal_best);
g_free (original);
/* clean */
for (i = 1; i < 4 * psize; i++)
{
gfloat as_is, swapd;
gint i0 = 1 + g_rand_int_range (gr, 0, psize - 2);
gint i1 = i0 + 1;
as_is = (pix_diff (pal, bpp, i0 - 1, i0) +
pix_diff (pal, bpp, i1, i1 + 1));
swapd = (pix_diff (pal, bpp, i0 - 1, i1) +
pix_diff (pal, bpp, i0, i1 + 1));
if (swapd < as_is)
{
pix_swap (pal, bpp, i0, i1);
len_best += swapd - as_is;
}
}
}
/* store smooth palette */
buffer = pika_drawable_get_buffer (PIKA_DRAWABLE (*layer));
for (j = 0; j < config.height; j++)
{
GeglRectangle row = {0, j, config.width, 1};
gegl_buffer_set (buffer, &row, 0, format, pal, GEGL_AUTO_ROWSTRIDE);
}
gegl_buffer_flush (buffer);
pika_drawable_update (PIKA_DRAWABLE (*layer), 0, 0,
config.width, config.height);
pika_image_undo_enable (new_image);
g_object_unref (buffer);
g_free (pal);
g_rand_free (gr);
return new_image;
}
static gboolean
dialog (PikaDrawable *drawable)
{
GtkWidget *dlg;
GtkWidget *spinbutton;
GtkAdjustment *adj;
GtkWidget *sizeentry;
PikaImage *image;
PikaUnit unit;
gdouble xres, yres;
gboolean run;
pika_ui_init (PLUG_IN_BINARY);
dlg = pika_dialog_new (_("Smooth Palette"), PLUG_IN_ROLE,
NULL, 0,
pika_standard_help_func, PLUG_IN_PROC,
_("_Cancel"), GTK_RESPONSE_CANCEL,
_("_OK"), GTK_RESPONSE_OK,
NULL);
pika_dialog_set_alternative_button_order (GTK_DIALOG (dlg),
GTK_RESPONSE_OK,
GTK_RESPONSE_CANCEL,
-1);
pika_window_set_transient (GTK_WINDOW (dlg));
image = pika_item_get_image (PIKA_ITEM (drawable));
unit = pika_image_get_unit (image);
pika_image_get_resolution (image, &xres, &yres);
sizeentry = pika_coordinates_new (unit, "%a", TRUE, FALSE, 6,
PIKA_SIZE_ENTRY_UPDATE_SIZE,
FALSE, FALSE,
_("_Width:"),
config.width, xres,
2, PIKA_MAX_IMAGE_SIZE,
2, PIKA_MAX_IMAGE_SIZE,
_("_Height:"),
config.height, yres,
1, PIKA_MAX_IMAGE_SIZE,
1, PIKA_MAX_IMAGE_SIZE);
gtk_container_set_border_width (GTK_CONTAINER (sizeentry), 12);
gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))),
sizeentry, FALSE, FALSE, 0);
gtk_widget_show (sizeentry);
adj = gtk_adjustment_new (config.ntries, 1, 1024, 1, 10, 0);
spinbutton = pika_spin_button_new (adj, 1, 0);
gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
pika_grid_attach_aligned (GTK_GRID (sizeentry), 0, 2,
_("_Search depth:"), 0.0, 0.5,
spinbutton, 1);
g_signal_connect (adj, "value-changed",
G_CALLBACK (pika_int_adjustment_update),
&config.ntries);
gtk_widget_show (dlg);
run = (pika_dialog_run (PIKA_DIALOG (dlg)) == GTK_RESPONSE_OK);
if (run)
{
config.width = pika_size_entry_get_refval (PIKA_SIZE_ENTRY (sizeentry),
0);
config.height = pika_size_entry_get_refval (PIKA_SIZE_ENTRY (sizeentry),
1);
}
gtk_widget_destroy (dlg);
return run;
}

1161
plug-ins/common/sparkle.c Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

1198
plug-ins/common/tile-small.c Normal file

File diff suppressed because it is too large Load Diff

562
plug-ins/common/tile.c Normal file
View File

@ -0,0 +1,562 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* 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/>.
*/
/*
* This filter tiles an image to arbitrary width and height
*/
#include "config.h"
#include <string.h>
#include <libpika/pika.h>
#include <libpika/pikaui.h>
#include "libpika/stdplugins-intl.h"
#define PLUG_IN_PROC "plug-in-tile"
#define PLUG_IN_BINARY "tile"
#define PLUG_IN_ROLE "pika-tile"
typedef struct
{
gint new_width;
gint new_height;
gint constrain;
gint new_image;
} TileVals;
typedef struct _Tile Tile;
typedef struct _TileClass TileClass;
struct _Tile
{
PikaPlugIn parent_instance;
};
struct _TileClass
{
PikaPlugInClass parent_class;
};
#define TILE_TYPE (tile_get_type ())
#define TILE (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TILE_TYPE, Tile))
GType tile_get_type (void) G_GNUC_CONST;
static GList * tile_query_procedures (PikaPlugIn *plug_in);
static PikaProcedure * tile_create_procedure (PikaPlugIn *plug_in,
const gchar *name);
static PikaValueArray * tile_run (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
const PikaValueArray *args,
gpointer run_data);
static void tile (PikaImage *image,
PikaDrawable *drawable,
PikaImage **new_image,
PikaLayer **new_layer);
static void tile_gegl (GeglBuffer *src,
gint src_width,
gint src_height,
GeglBuffer *dst,
gint dst_width,
gint dst_height);
static gboolean tile_dialog (PikaImage *image,
PikaDrawable *drawable);
G_DEFINE_TYPE (Tile, tile, PIKA_TYPE_PLUG_IN)
PIKA_MAIN (TILE_TYPE)
DEFINE_STD_SET_I18N
static TileVals tvals =
{
1, /* new_width */
1, /* new_height */
TRUE, /* constrain */
TRUE /* new_image */
};
static void
tile_class_init (TileClass *klass)
{
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
plug_in_class->query_procedures = tile_query_procedures;
plug_in_class->create_procedure = tile_create_procedure;
plug_in_class->set_i18n = STD_SET_I18N;
}
static void
tile_init (Tile *tile)
{
}
static GList *
tile_query_procedures (PikaPlugIn *plug_in)
{
return g_list_append (NULL, g_strdup (PLUG_IN_PROC));
}
static PikaProcedure *
tile_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,
tile_run, NULL, NULL);
pika_procedure_set_image_types (procedure, "*");
pika_procedure_set_sensitivity_mask (procedure,
PIKA_PROCEDURE_SENSITIVE_DRAWABLE);
pika_procedure_set_menu_label (procedure, _("_Tile..."));
pika_procedure_add_menu_path (procedure, "<Image>/Filters/Map");
pika_procedure_set_documentation (procedure,
_("Create an array of copies "
"of the image"),
"This function creates a new image "
"with a single layer sized to the "
"specified 'new_width' and "
"'new_height' parameters. The "
"specified drawable is tiled into "
"this layer. The new layer will have "
"the same type as the specified "
"drawable and the new image will "
"have a corresponding base type.",
name);
pika_procedure_set_attribution (procedure,
"Spencer Kimball & Peter Mattis",
"Spencer Kimball & Peter Mattis",
"1996-1997");
PIKA_PROC_ARG_INT (procedure, "new-width",
"New width",
"New (tiled) image width",
1, PIKA_MAX_IMAGE_SIZE, 1,
G_PARAM_READWRITE);
PIKA_PROC_ARG_INT (procedure, "new-height",
"New height",
"New (tiled) image height",
1, PIKA_MAX_IMAGE_SIZE, 1,
G_PARAM_READWRITE);
PIKA_PROC_ARG_BOOLEAN (procedure, "new-image",
"New image",
"Create a new image",
TRUE,
G_PARAM_READWRITE);
PIKA_PROC_VAL_IMAGE (procedure, "new-image",
"New image",
"Output image (NULL if new-image == FALSE)",
TRUE,
G_PARAM_READWRITE);
PIKA_PROC_VAL_LAYER (procedure, "new-layer",
"New layer",
"Output layer (NULL if new-image == FALSE)",
TRUE,
G_PARAM_READWRITE);
}
return procedure;
}
static PikaValueArray *
tile_run (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
const PikaValueArray *args,
gpointer run_data)
{
PikaValueArray *return_vals;
PikaLayer *new_layer;
PikaImage *new_image;
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."),
pika_procedure_get_name (procedure));
return pika_procedure_new_return_values (procedure,
PIKA_PDB_CALLING_ERROR,
error);
}
else
{
drawable = drawables[0];
}
switch (run_mode)
{
case PIKA_RUN_INTERACTIVE:
pika_get_data (PLUG_IN_PROC, &tvals);
if (! tile_dialog (image, drawable))
{
return pika_procedure_new_return_values (procedure,
PIKA_PDB_CANCEL,
NULL);
}
break;
case PIKA_RUN_NONINTERACTIVE:
tvals.new_width = PIKA_VALUES_GET_INT (args, 0);
tvals.new_height = PIKA_VALUES_GET_INT (args, 1);
tvals.new_image = PIKA_VALUES_GET_BOOLEAN (args, 2);
break;
case PIKA_RUN_WITH_LAST_VALS:
pika_get_data (PLUG_IN_PROC, &tvals);
break;
}
pika_progress_init (_("Tiling"));
tile (image,
drawable,
&new_image,
&new_layer);
if (run_mode == PIKA_RUN_INTERACTIVE)
pika_set_data (PLUG_IN_PROC, &tvals, sizeof (TileVals));
if (run_mode != PIKA_RUN_NONINTERACTIVE)
{
if (tvals.new_image)
pika_display_new (new_image);
else
pika_displays_flush ();
}
return_vals = pika_procedure_new_return_values (procedure,
PIKA_PDB_SUCCESS,
NULL);
PIKA_VALUES_SET_IMAGE (return_vals, 1, new_image);
PIKA_VALUES_SET_LAYER (return_vals, 2, new_layer);
return return_vals;
}
static void
tile_gegl (GeglBuffer *src,
gint src_width,
gint src_height,
GeglBuffer *dst,
gint dst_width,
gint dst_height)
{
GeglNode *node;
GeglNode *buffer_src_node;
GeglNode *tile_node;
GeglNode *crop_src_node;
GeglNode *crop_dst_node;
GeglNode *buffer_dst_node;
GeglProcessor *processor;
gdouble progress;
node = gegl_node_new ();
buffer_src_node = gegl_node_new_child (node,
"operation", "gegl:buffer-source",
"buffer", src,
NULL);
crop_src_node = gegl_node_new_child (node,
"operation", "gegl:crop",
"width", (gdouble) src_width,
"height", (gdouble) src_height,
NULL);
tile_node = gegl_node_new_child (node,
"operation", "gegl:tile",
NULL);
crop_dst_node = gegl_node_new_child (node,
"operation", "gegl:crop",
"width", (gdouble) dst_width,
"height", (gdouble) dst_height,
NULL);
buffer_dst_node = gegl_node_new_child (node,
"operation", "gegl:write-buffer",
"buffer", dst,
NULL);
gegl_node_link_many (buffer_src_node,
crop_src_node,
tile_node,
crop_dst_node,
buffer_dst_node,
NULL);
processor = gegl_node_new_processor (buffer_dst_node, NULL);
while (gegl_processor_work (processor, &progress))
if (!((gint) (progress * 100.0) % 10))
pika_progress_update (progress);
pika_progress_update (1.0);
g_object_unref (processor);
g_object_unref (node);
}
static void
tile (PikaImage *image,
PikaDrawable *drawable,
PikaImage **new_image,
PikaLayer **new_layer)
{
PikaDrawable *dst_drawable;
GeglBuffer *dst_buffer;
GeglBuffer *src_buffer;
gint dst_width = tvals.new_width;
gint dst_height = tvals.new_height;
gint src_width = pika_drawable_get_width (drawable);
gint src_height = pika_drawable_get_height (drawable);
PikaImageBaseType image_type = PIKA_RGB;
if (tvals.new_image)
{
/* create a new image */
gint32 precision = pika_image_get_precision (image);
switch (pika_drawable_type (drawable))
{
case PIKA_RGB_IMAGE:
case PIKA_RGBA_IMAGE:
image_type = PIKA_RGB;
break;
case PIKA_GRAY_IMAGE:
case PIKA_GRAYA_IMAGE:
image_type = PIKA_GRAY;
break;
case PIKA_INDEXED_IMAGE:
case PIKA_INDEXEDA_IMAGE:
image_type = PIKA_INDEXED;
break;
}
*new_image = pika_image_new_with_precision (dst_width,
dst_height,
image_type,
precision);
pika_image_undo_disable (*new_image);
/* copy the colormap, if necessary */
if (image_type == PIKA_INDEXED)
{
guchar *cmap;
gint ncols;
cmap = pika_image_get_colormap (image, NULL, &ncols);
pika_image_set_colormap (*new_image, cmap, ncols);
g_free (cmap);
}
*new_layer = pika_layer_new (*new_image, _("Background"),
dst_width, dst_height,
pika_drawable_type (drawable),
100,
pika_image_get_default_new_layer_mode (*new_image));
if (*new_layer == NULL)
return;
pika_image_insert_layer (*new_image, *new_layer, NULL, 0);
dst_drawable = PIKA_DRAWABLE (*new_layer);
}
else
{
*new_image = NULL;
*new_layer = NULL;
pika_image_undo_group_start (image);
pika_image_resize (image, dst_width, dst_height, 0, 0);
if (pika_item_is_layer (PIKA_ITEM (drawable)))
{
pika_layer_resize (PIKA_LAYER (drawable), dst_width, dst_height, 0, 0);
}
else if (pika_item_is_layer_mask (PIKA_ITEM (drawable)))
{
PikaLayer *layer = pika_layer_from_mask (PIKA_LAYER_MASK (drawable));
pika_layer_resize (layer, dst_width, dst_height, 0, 0);
}
dst_drawable = drawable;
}
src_buffer = pika_drawable_get_buffer (drawable);
dst_buffer = pika_drawable_get_buffer (dst_drawable);
tile_gegl (src_buffer, src_width, src_height,
dst_buffer, dst_width, dst_height);
gegl_buffer_flush (dst_buffer);
pika_drawable_update (dst_drawable, 0, 0, dst_width, dst_height);
if (tvals.new_image)
{
pika_image_undo_enable (*new_image);
}
else
{
pika_image_undo_group_end (image);
}
g_object_unref (src_buffer);
g_object_unref (dst_buffer);
}
static gboolean
tile_dialog (PikaImage *image,
PikaDrawable *drawable)
{
GtkWidget *dlg;
GtkWidget *vbox;
GtkWidget *frame;
GtkWidget *sizeentry;
GtkWidget *chainbutton;
GtkWidget *toggle;
gint width;
gint height;
gdouble xres;
gdouble yres;
PikaUnit unit;
gboolean run;
pika_ui_init (PLUG_IN_BINARY);
width = pika_drawable_get_width (drawable);
height = pika_drawable_get_height (drawable);
unit = pika_image_get_unit (image);
pika_image_get_resolution (image, &xres, &yres);
tvals.new_width = width;
tvals.new_height = height;
dlg = pika_dialog_new (_("Tile"), PLUG_IN_ROLE,
NULL, 0,
pika_standard_help_func, PLUG_IN_PROC,
_("_Cancel"), GTK_RESPONSE_CANCEL,
_("_OK"), GTK_RESPONSE_OK,
NULL);
pika_dialog_set_alternative_button_order (GTK_DIALOG (dlg),
GTK_RESPONSE_OK,
GTK_RESPONSE_CANCEL,
-1);
pika_window_set_transient (GTK_WINDOW (dlg));
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))),
vbox, TRUE, TRUE, 0);
gtk_widget_show (vbox);
frame = pika_frame_new (_("Tile to New Size"));
gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
gtk_widget_show (frame);
sizeentry = pika_coordinates_new (unit, "%a", TRUE, TRUE, 8,
PIKA_SIZE_ENTRY_UPDATE_SIZE,
tvals.constrain, TRUE,
_("_Width:"), width, xres,
1, PIKA_MAX_IMAGE_SIZE,
0, width,
_("_Height:"), height, yres,
1, PIKA_MAX_IMAGE_SIZE,
0, height);
gtk_container_add (GTK_CONTAINER (frame), sizeentry);
gtk_widget_show (sizeentry);
chainbutton = GTK_WIDGET (PIKA_COORDINATES_CHAINBUTTON (sizeentry));
toggle = gtk_check_button_new_with_mnemonic (_("C_reate new image"));
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), tvals.new_image);
gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
gtk_widget_show (toggle);
g_signal_connect (toggle, "toggled",
G_CALLBACK (pika_toggle_button_update),
&tvals.new_image);
gtk_widget_show (dlg);
run = (pika_dialog_run (PIKA_DIALOG (dlg)) == GTK_RESPONSE_OK);
if (run)
{
tvals.new_width =
RINT (pika_size_entry_get_refval (PIKA_SIZE_ENTRY (sizeentry), 0));
tvals.new_height =
RINT (pika_size_entry_get_refval (PIKA_SIZE_ENTRY (sizeentry), 1));
tvals.constrain =
pika_chain_button_get_active (PIKA_CHAIN_BUTTON (chainbutton));
}
gtk_widget_destroy (dlg);
return run;
}

View File

@ -0,0 +1,776 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* This is a plug-in for PIKA.
*
* Copyright (C) 2000 Michael Natterer <mitch@gimp.org>
*
* 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/>.
*/
#include "config.h"
#include <string.h>
#include <libpika/pika.h>
#include <libpika/pikaui.h>
#include "libpika/stdplugins-intl.h"
#define PLUG_IN_PROC "plug-in-unit-editor"
#define PLUG_IN_BINARY "unit-editor"
#define PLUG_IN_ROLE "pika-unit-editor"
enum
{
SAVE,
IDENTIFIER,
FACTOR,
DIGITS,
SYMBOL,
ABBREVIATION,
SINGULAR,
PLURAL,
UNIT,
USER_UNIT,
BG_COLOR,
NUM_COLUMNS
};
typedef struct
{
const gchar *title;
const gchar *help;
} UnitColumn;
#define PIKA_UNIT_EDITOR_TYPE (pika_unit_editor_get_type ())
G_DECLARE_FINAL_TYPE (PikaUnitEditor, pika_unit_editor, PIKA, UNIT_EDITOR, PikaPlugIn)
struct _PikaUnitEditor
{
PikaPlugIn parent_instance;
GtkApplication *app;
GtkWindow *window;
GtkWidget *tv;
};
static GList * editor_query_procedures (PikaPlugIn *plug_in);
static PikaProcedure * editor_create_procedure (PikaPlugIn *plug_in,
const gchar *name);
static PikaValueArray * editor_run (PikaProcedure *procedure,
const PikaValueArray *args,
gpointer run_data);
static PikaUnit new_unit_dialog (GtkWindow *main_window,
PikaUnit template);
static void on_app_activate (GApplication *gapp,
gpointer user_data);
static gboolean unit_editor_key_press_event (GtkWidget *window,
GdkEventKey *event,
gpointer user_data);
static void unit_editor_help_clicked (GtkWidget *window);
static void new_unit_action (GSimpleAction *action,
GVariant *param,
gpointer user_data);
static void duplicate_unit_action (GSimpleAction *action,
GVariant *param,
gpointer user_data);
static void refresh_action (GSimpleAction *action,
GVariant *param,
gpointer user_data);
static void saved_toggled_callback (GtkCellRendererToggle *celltoggle,
gchar *path_string,
GtkListStore *list_store);
static void unit_list_init (GtkTreeView *tv);
G_DEFINE_TYPE (PikaUnitEditor, pika_unit_editor, PIKA_TYPE_PLUG_IN)
PIKA_MAIN (PIKA_UNIT_EDITOR_TYPE)
DEFINE_STD_SET_I18N
static const UnitColumn columns[] =
{
{ N_("Saved"), N_("A unit definition will only be saved before "
"PIKA exits if this column is checked.") },
{ N_("ID"), N_("This string will be used to identify a "
"unit in PIKA's configuration files.") },
{ N_("Factor"), N_("How many units make up an inch.") },
{ N_("Digits"), N_("This field is a hint for numerical input "
"fields. It specifies how many decimal digits "
"the input field should provide to get "
"approximately the same accuracy as an "
"\"inch\" input field with two decimal digits.") },
{ N_("Symbol"), N_("The unit's symbol if it has one (e.g. \" "
"for inches). The unit's abbreviation is used "
"if doesn't have a symbol.") },
{ N_("Abbreviation"), N_("The unit's abbreviation (e.g. \"cm\" for "
"centimeters).") },
{ N_("Singular"), N_("The unit's singular form.") },
{ N_("Plural"), N_("The unit's plural form.") }
};
static GActionEntry ACTIONS[] =
{
{ "new-unit", new_unit_action },
{ "duplicate-unit", duplicate_unit_action },
{ "refresh", refresh_action },
};
static void
pika_unit_editor_class_init (PikaUnitEditorClass *klass)
{
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
plug_in_class->query_procedures = editor_query_procedures;
plug_in_class->create_procedure = editor_create_procedure;
plug_in_class->set_i18n = STD_SET_I18N;
}
static void
pika_unit_editor_init (PikaUnitEditor *self)
{
}
static GList *
editor_query_procedures (PikaPlugIn *plug_in)
{
return g_list_append (NULL, g_strdup (PLUG_IN_PROC));
}
static PikaProcedure *
editor_create_procedure (PikaPlugIn *plug_in,
const gchar *name)
{
PikaProcedure *procedure = NULL;
if (! strcmp (name, PLUG_IN_PROC))
{
procedure = pika_procedure_new (plug_in, name,
PIKA_PDB_PROC_TYPE_PLUGIN,
editor_run, plug_in, NULL);
pika_procedure_set_menu_label (procedure, _("U_nits"));
pika_procedure_set_icon_name (procedure, PIKA_ICON_TOOL_MEASURE);
pika_procedure_add_menu_path (procedure, "<Image>/Edit/[Preferences]");
pika_procedure_set_documentation (procedure,
_("Create or alter units used in PIKA"),
"The PIKA unit editor",
name);
pika_procedure_set_attribution (procedure,
"Michael Natterer <mitch@gimp.org>",
"Michael Natterer <mitch@gimp.org>",
"2000");
PIKA_PROC_ARG_ENUM (procedure, "run-mode",
"Run mode",
"The run mode",
PIKA_TYPE_RUN_MODE,
PIKA_RUN_INTERACTIVE,
G_PARAM_READWRITE);
}
return procedure;
}
static void
on_app_activate (GApplication *gapp, gpointer user_data)
{
PikaUnitEditor *self = PIKA_UNIT_EDITOR (user_data);
GtkWidget *headerbar;
GtkWidget *vbox;
GtkWidget *button_box;
GtkWidget *scrolled_win;
GtkListStore *list_store;
GtkTreeViewColumn *col;
GtkWidget *col_widget;
GtkWidget *button;
GtkCellRenderer *rend;
list_store = gtk_list_store_new (NUM_COLUMNS,
G_TYPE_BOOLEAN, /* SAVE */
G_TYPE_STRING, /* IDENTIFIER */
G_TYPE_DOUBLE, /* FACTOR */
G_TYPE_INT, /* DIGITS */
G_TYPE_STRING, /* SYMBOL */
G_TYPE_STRING, /* ABBREVIATION */
G_TYPE_STRING, /* SINGULAR */
G_TYPE_STRING, /* PLURAL */
PIKA_TYPE_UNIT, /* UNIT */
G_TYPE_BOOLEAN, /* USER_UNIT */
GDK_TYPE_RGBA); /* BG_COLOR */
self->tv = gtk_tree_view_new_with_model (GTK_TREE_MODEL (list_store));
g_object_unref (list_store);
self->window = GTK_WINDOW (gtk_application_window_new (self->app));
gtk_window_set_title (self->window, _("Unit Editor"));
gtk_window_set_role (self->window, PLUG_IN_ROLE);
pika_help_connect (GTK_WIDGET (self->window),
pika_standard_help_func, PLUG_IN_PROC,
self->window, NULL);
/* Actions */
g_action_map_add_action_entries (G_ACTION_MAP (self->window),
ACTIONS, G_N_ELEMENTS (ACTIONS),
self);
gtk_application_set_accels_for_action (self->app, "win.new-unit",
(const char*[]) { "<Ctrl>N", NULL });
gtk_application_set_accels_for_action (self->app, "win.duplicate-unit",
(const char*[]) { "<ctrl>D", NULL });
gtk_application_set_accels_for_action (self->app, "win.refresh",
(const char*[]) { "<ctrl>R", NULL });
/* Titlebar */
headerbar = gtk_header_bar_new ();
gtk_header_bar_set_title (GTK_HEADER_BAR (headerbar), _("Unit Editor"));
gtk_header_bar_set_has_subtitle (GTK_HEADER_BAR (headerbar), FALSE);
gtk_header_bar_set_show_close_button (GTK_HEADER_BAR (headerbar), TRUE);
button = gtk_button_new_with_mnemonic (_("_Refresh"));
gtk_actionable_set_action_name (GTK_ACTIONABLE (button), "win.refresh");
gtk_widget_show (button);
gtk_header_bar_pack_start (GTK_HEADER_BAR (headerbar), button);
if (pika_show_help_button ())
{
button = gtk_button_new_with_mnemonic (_("_Help"));
g_signal_connect_swapped (button, "clicked",
G_CALLBACK (unit_editor_help_clicked),
self->window);
gtk_widget_show (button);
gtk_header_bar_pack_start (GTK_HEADER_BAR (headerbar), button);
}
button = gtk_button_new_with_mnemonic (_("_OK"));
g_signal_connect_swapped (button, "clicked",
G_CALLBACK (gtk_widget_destroy),
self->window);
gtk_widget_show (button);
gtk_header_bar_pack_end (GTK_HEADER_BAR (headerbar), button);
gtk_window_set_titlebar (self->window, headerbar);
gtk_widget_show (headerbar);
/* Content */
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
gtk_widget_show (vbox);
gtk_container_add (GTK_CONTAINER (self->window), vbox);
button_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
gtk_widget_show (button_box);
gtk_container_add (GTK_CONTAINER (vbox), button_box);
button = gtk_button_new_from_icon_name (PIKA_ICON_DOCUMENT_NEW,
GTK_ICON_SIZE_BUTTON);
gtk_actionable_set_action_name (GTK_ACTIONABLE (button), "win.new-unit");
gtk_widget_set_tooltip_text (button, _("Create a new unit from scratch"));
gtk_widget_show (button);
gtk_container_add (GTK_CONTAINER (button_box), button);
button = gtk_button_new_from_icon_name (PIKA_ICON_OBJECT_DUPLICATE,
GTK_ICON_SIZE_BUTTON);
gtk_actionable_set_action_name (GTK_ACTIONABLE (button), "win.duplicate-unit");
gtk_widget_set_tooltip_text (button, _("Create a new unit using the currently selected unit as template"));
gtk_widget_show (button);
gtk_container_add (GTK_CONTAINER (button_box), button);
scrolled_win = gtk_scrolled_window_new (NULL, NULL);
gtk_widget_set_size_request (scrolled_win, -1, 200);
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_win),
GTK_SHADOW_IN);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
GTK_POLICY_NEVER,
GTK_POLICY_ALWAYS);
gtk_container_add (GTK_CONTAINER (vbox), scrolled_win);
gtk_widget_show (scrolled_win);
gtk_widget_set_size_request (self->tv, -1, 220);
gtk_container_add (GTK_CONTAINER (scrolled_win), self->tv);
gtk_widget_set_vexpand (self->tv, TRUE);
gtk_widget_show (self->tv);
rend = gtk_cell_renderer_toggle_new ();
col =
gtk_tree_view_column_new_with_attributes (gettext (columns[SAVE].title),
rend,
"active", SAVE,
"activatable", USER_UNIT,
"cell-background-rgba", BG_COLOR,
NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW (self->tv), col);
col_widget = gtk_tree_view_column_get_widget (col);
if (col_widget)
{
button = gtk_widget_get_ancestor (col_widget, GTK_TYPE_BUTTON);
if (button)
pika_help_set_help_data (button,
gettext (columns[SAVE].help), NULL);
}
g_signal_connect (rend, "toggled",
G_CALLBACK (saved_toggled_callback),
list_store);
for (int i = 0; i < G_N_ELEMENTS (columns); i++)
{
if (i == SAVE)
continue;
col =
gtk_tree_view_column_new_with_attributes (gettext (columns[i].title),
gtk_cell_renderer_text_new (),
"text", i,
"cell-background-rgba", BG_COLOR,
NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW (self->tv), col);
col_widget = gtk_tree_view_column_get_widget (col);
if (col_widget)
{
button = gtk_widget_get_ancestor (col_widget, GTK_TYPE_BUTTON);
if (button)
pika_help_set_help_data (button, gettext (columns[i].help), NULL);
}
}
unit_list_init (GTK_TREE_VIEW (self->tv));
g_signal_connect (self->window, "key-press-event",
G_CALLBACK (unit_editor_key_press_event),
NULL);
gtk_widget_show (GTK_WIDGET (self->window));
}
static gboolean
unit_editor_key_press_event (GtkWidget *window,
GdkEventKey *event,
gpointer user_data)
{
if (event->state == 0 &&
event->keyval == GDK_KEY_Escape)
gtk_widget_destroy (GTK_WIDGET (window));
return FALSE;
}
static void
unit_editor_help_clicked (GtkWidget *window)
{
pika_standard_help_func (PLUG_IN_PROC, window);
}
static PikaValueArray *
editor_run (PikaProcedure *procedure,
const PikaValueArray *args,
gpointer run_data)
{
PikaUnitEditor *editor = PIKA_UNIT_EDITOR (run_data);
pika_ui_init (PLUG_IN_BINARY);
#if GLIB_CHECK_VERSION(2,74,0)
editor->app = gtk_application_new (NULL, G_APPLICATION_DEFAULT_FLAGS);
#else
editor->app = gtk_application_new (NULL, G_APPLICATION_FLAGS_NONE);
#endif
g_signal_connect (editor->app, "activate", G_CALLBACK (on_app_activate), editor);
g_application_run (G_APPLICATION (editor->app), 0, NULL);
g_clear_object (&editor->app);
return pika_procedure_new_return_values (procedure, PIKA_PDB_SUCCESS, NULL);
}
static PikaUnit
new_unit_dialog (GtkWindow *main_window,
PikaUnit template)
{
GtkWidget *dialog;
GtkWidget *grid;
GtkWidget *entry;
GtkWidget *spinbutton;
GtkWidget *identifier_entry;
GtkAdjustment *factor_adj;
GtkAdjustment *digits_adj;
GtkWidget *symbol_entry;
GtkWidget *abbreviation_entry;
GtkWidget *singular_entry;
GtkWidget *plural_entry;
PikaUnit unit = PIKA_UNIT_PIXEL;
dialog = pika_dialog_new (_("Add a New Unit"), PLUG_IN_ROLE,
GTK_WIDGET (main_window), GTK_DIALOG_MODAL,
pika_standard_help_func, PLUG_IN_PROC,
_("_Cancel"), GTK_RESPONSE_CANCEL,
_("_Add"), GTK_RESPONSE_OK,
NULL);
pika_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
GTK_RESPONSE_OK,
GTK_RESPONSE_CANCEL,
-1);
grid = gtk_grid_new ();
gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
gtk_grid_set_column_spacing (GTK_GRID (grid), 6);
gtk_container_set_border_width (GTK_CONTAINER (grid), 12);
gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
grid, FALSE, FALSE, 0);
gtk_widget_show (grid);
entry = identifier_entry = gtk_entry_new ();
if (template != PIKA_UNIT_PIXEL)
{
gtk_entry_set_text (GTK_ENTRY (entry),
pika_unit_get_identifier (template));
}
pika_grid_attach_aligned (GTK_GRID (grid), 0, 0,
_("_ID:"), 0.0, 0.5,
entry, 1);
pika_help_set_help_data (entry, gettext (columns[IDENTIFIER].help), NULL);
factor_adj = gtk_adjustment_new ((template != PIKA_UNIT_PIXEL) ?
pika_unit_get_factor (template) : 1.0,
PIKA_MIN_RESOLUTION, PIKA_MAX_RESOLUTION,
0.01, 0.1, 0.0);
spinbutton = pika_spin_button_new (factor_adj, 0.01, 5);
gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
pika_grid_attach_aligned (GTK_GRID (grid), 0, 1,
_("_Factor:"), 0.0, 0.5,
spinbutton, 1);
pika_help_set_help_data (spinbutton, gettext (columns[FACTOR].help), NULL);
digits_adj = gtk_adjustment_new ((template != PIKA_UNIT_PIXEL) ?
pika_unit_get_digits (template) : 2.0,
0, 5, 1, 1, 0);
spinbutton = pika_spin_button_new (digits_adj, 0, 0);
gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
pika_grid_attach_aligned (GTK_GRID (grid), 0, 2,
_("_Digits:"), 0.0, 0.5,
spinbutton, 1);
pika_help_set_help_data (spinbutton, gettext (columns[DIGITS].help), NULL);
entry = symbol_entry = gtk_entry_new ();
if (template != PIKA_UNIT_PIXEL)
{
gtk_entry_set_text (GTK_ENTRY (entry),
pika_unit_get_symbol (template));
}
pika_grid_attach_aligned (GTK_GRID (grid), 0, 3,
_("_Symbol:"), 0.0, 0.5,
entry, 1);
pika_help_set_help_data (entry, gettext (columns[SYMBOL].help), NULL);
entry = abbreviation_entry = gtk_entry_new ();
if (template != PIKA_UNIT_PIXEL)
{
gtk_entry_set_text (GTK_ENTRY (entry),
pika_unit_get_abbreviation (template));
}
pika_grid_attach_aligned (GTK_GRID (grid), 0, 4,
_("_Abbreviation:"), 0.0, 0.5,
entry, 1);
pika_help_set_help_data (entry, gettext (columns[ABBREVIATION].help), NULL);
entry = singular_entry = gtk_entry_new ();
if (template != PIKA_UNIT_PIXEL)
{
gtk_entry_set_text (GTK_ENTRY (entry),
pika_unit_get_singular (template));
}
pika_grid_attach_aligned (GTK_GRID (grid), 0, 5,
_("Si_ngular:"), 0.0, 0.5,
entry, 1);
pika_help_set_help_data (entry, gettext (columns[SINGULAR].help), NULL);
entry = plural_entry = gtk_entry_new ();
if (template != PIKA_UNIT_PIXEL)
{
gtk_entry_set_text (GTK_ENTRY (entry),
pika_unit_get_plural (template));
}
pika_grid_attach_aligned (GTK_GRID (grid), 0, 6,
_("_Plural:"), 0.0, 0.5,
entry, 1);
pika_help_set_help_data (entry, gettext (columns[PLURAL].help), NULL);
gtk_widget_show (dialog);
while (TRUE)
{
gchar *identifier;
gdouble factor;
gint digits;
gchar *symbol;
gchar *abbreviation;
gchar *singular;
gchar *plural;
if (pika_dialog_run (PIKA_DIALOG (dialog)) != GTK_RESPONSE_OK)
break;
identifier = g_strdup (gtk_entry_get_text (GTK_ENTRY (identifier_entry)));
factor = gtk_adjustment_get_value (factor_adj);
digits = gtk_adjustment_get_value (digits_adj);
symbol = g_strdup (gtk_entry_get_text (GTK_ENTRY (symbol_entry)));
abbreviation = g_strdup (gtk_entry_get_text (GTK_ENTRY (abbreviation_entry)));
singular = g_strdup (gtk_entry_get_text (GTK_ENTRY (singular_entry)));
plural = g_strdup (gtk_entry_get_text (GTK_ENTRY (plural_entry)));
identifier = g_strstrip (identifier);
symbol = g_strstrip (symbol);
abbreviation = g_strstrip (abbreviation);
singular = g_strstrip (singular);
plural = g_strstrip (plural);
if (! strlen (identifier) ||
! strlen (symbol) ||
! strlen (abbreviation) ||
! strlen (singular) ||
! strlen (plural))
{
GtkWidget *msg = gtk_message_dialog_new (GTK_WINDOW (dialog), 0,
GTK_MESSAGE_ERROR,
GTK_BUTTONS_OK,
_("Incomplete input"));
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (msg),
_("Please fill in all text fields."));
gtk_dialog_run (GTK_DIALOG (msg));
gtk_widget_destroy (msg);
continue;
}
unit = pika_unit_new (identifier,
factor, digits,
symbol, abbreviation, singular, plural);
g_free (identifier);
g_free (symbol);
g_free (abbreviation);
g_free (singular);
g_free (plural);
break;
}
gtk_widget_destroy (dialog);
return unit;
}
static void
new_unit_action (GSimpleAction *action,
GVariant *param,
gpointer user_data)
{
PikaUnitEditor *self = PIKA_UNIT_EDITOR (user_data);
PikaUnit unit;
unit = new_unit_dialog (self->window, PIKA_UNIT_PIXEL);
if (unit != PIKA_UNIT_PIXEL)
{
GtkTreeModel *model;
GtkTreeIter iter;
unit_list_init (GTK_TREE_VIEW (self->tv));
model = gtk_tree_view_get_model (GTK_TREE_VIEW (self->tv));
if (gtk_tree_model_get_iter_first (model, &iter) &&
gtk_tree_model_iter_nth_child (model, &iter,
NULL, unit - PIKA_UNIT_INCH))
{
GtkTreeSelection *selection;
GtkAdjustment *adj;
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->tv));
gtk_tree_selection_select_iter (selection, &iter);
adj = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (self->tv));
gtk_adjustment_set_value (adj, gtk_adjustment_get_upper (adj));
}
}
}
static void
duplicate_unit_action (GSimpleAction *action,
GVariant *param,
gpointer user_data)
{
PikaUnitEditor *self = PIKA_UNIT_EDITOR (user_data);
GtkTreeModel *model;
GtkTreeSelection *sel;
GtkTreeIter iter;
model = gtk_tree_view_get_model (GTK_TREE_VIEW (self->tv));
sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->tv));
if (gtk_tree_selection_get_selected (sel, NULL, &iter))
{
PikaUnit unit;
gtk_tree_model_get (model, &iter,
UNIT, &unit,
-1);
unit = new_unit_dialog (self->window, unit);
if (unit != PIKA_UNIT_PIXEL)
{
GtkTreeIter iter;
unit_list_init (GTK_TREE_VIEW (self->tv));
if (gtk_tree_model_get_iter_first (model, &iter) &&
gtk_tree_model_iter_nth_child (model, &iter,
NULL, unit - PIKA_UNIT_INCH))
{
GtkAdjustment *adj;
gtk_tree_selection_select_iter (sel, &iter);
adj = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (self->tv));
gtk_adjustment_set_value (adj, gtk_adjustment_get_upper (adj));
}
}
}
}
static void
refresh_action (GSimpleAction *action,
GVariant *param,
gpointer user_data)
{
PikaUnitEditor *self = PIKA_UNIT_EDITOR (user_data);
unit_list_init (GTK_TREE_VIEW (self->tv));
}
static void
saved_toggled_callback (GtkCellRendererToggle *celltoggle,
gchar *path_string,
GtkListStore *list_store)
{
GtkTreePath *path;
GtkTreeIter iter;
gboolean saved;
PikaUnit unit;
path = gtk_tree_path_new_from_string (path_string);
if (! gtk_tree_model_get_iter (GTK_TREE_MODEL (list_store), &iter, path))
{
g_warning ("%s: bad tree path?", G_STRLOC);
return;
}
gtk_tree_path_free (path);
gtk_tree_model_get (GTK_TREE_MODEL (list_store), &iter,
SAVE, &saved,
UNIT, &unit,
-1);
if (unit >= pika_unit_get_number_of_built_in_units ())
{
pika_unit_set_deletion_flag (unit, saved);
gtk_list_store_set (GTK_LIST_STORE (list_store), &iter,
SAVE, ! saved,
-1);
}
}
static void
unit_list_init (GtkTreeView *tv)
{
GtkListStore *list_store;
GtkTreeIter iter;
gint num_units;
PikaUnit unit;
GdkRGBA color;
list_store = GTK_LIST_STORE (gtk_tree_view_get_model (tv));
gtk_list_store_clear (list_store);
num_units = pika_unit_get_number_of_units ();
color.red = 0.87;
color.green = 0.87;
color.blue = 1;
color.alpha = 1;
for (unit = PIKA_UNIT_INCH; unit < num_units; unit++)
{
gboolean user_unit = (unit >= pika_unit_get_number_of_built_in_units ());
gtk_list_store_append (list_store, &iter);
gtk_list_store_set (list_store, &iter,
SAVE, ! pika_unit_get_deletion_flag (unit),
IDENTIFIER, pika_unit_get_identifier (unit),
FACTOR, pika_unit_get_factor (unit),
DIGITS, pika_unit_get_digits (unit),
SYMBOL, pika_unit_get_symbol (unit),
ABBREVIATION, pika_unit_get_abbreviation (unit),
SINGULAR, pika_unit_get_singular (unit),
PLURAL, pika_unit_get_plural (unit),
UNIT, unit,
USER_UNIT, user_unit,
user_unit ? -1 : BG_COLOR, &color,
-1);
}
if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (list_store), &iter))
gtk_tree_selection_select_iter (gtk_tree_view_get_selection (tv), &iter);
}

View File

@ -0,0 +1,981 @@
/* LIC 0.14 -- image filter plug-in for PIKA
* Copyright (C) 1996 Tom Bech
*
* E-mail: tomb@gimp.org
* You can contact the original PIKA authors at pika@xcf.berkeley.edu
*
* 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/>.
*
* In other words, you can't sue me for whatever happens while using this ;)
*
* Changes (post 0.10):
* -> 0.11: Fixed a bug in the convolution kernels (Tom).
* -> 0.12: Added Quartic's bilinear interpolation stuff (Tom).
* -> 0.13 Changed some UI stuff causing trouble with the 0.60 release, added
* the (PIKA) tags and changed random() calls to rand() (Tom)
* -> 0.14 Ported to 0.99.11 (Tom)
*
* This plug-in implements the Line Integral Convolution (LIC) as described in
* Cabral et al. "Imaging vector fields using line integral convolution" in the
* Proceedings of ACM SIGGRAPH 93. Publ. by ACM, New York, NY, USA. p. 263-270.
* (See http://www8.cs.umu.se/kurser/TDBD13/VT00/extra/p263-cabral.pdf)
*
* Some of the code is based on code by Steinar Haugen (thanks!), the Perlin
* noise function is practically ripped as is :)
*/
#include "config.h"
#include <libpika/pika.h>
#include <libpika/pikaui.h>
#include "libpika/stdplugins-intl.h"
/************/
/* Typedefs */
/************/
#define numx 40 /* Pseudo-random vector grid size */
#define numy 40
#define PLUG_IN_PROC "plug-in-lic"
#define PLUG_IN_BINARY "van-gogh-lic"
#define PLUG_IN_ROLE "pika-van-gogh-lic"
typedef enum
{
LIC_HUE,
LIC_SATURATION,
LIC_BRIGHTNESS
} LICEffectChannel;
typedef struct _Lic Lic;
typedef struct _LicClass LicClass;
struct _Lic
{
PikaPlugIn parent_instance;
};
struct _LicClass
{
PikaPlugInClass parent_class;
};
#define LIC_TYPE (lic_get_type ())
#define LIC (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), LIC_TYPE, Lic))
GType lic_get_type (void) G_GNUC_CONST;
static GList * lic_query_procedures (PikaPlugIn *plug_in);
static PikaProcedure * lic_create_procedure (PikaPlugIn *plug_in,
const gchar *name);
static PikaValueArray * lic_run (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
const PikaValueArray *args,
gpointer run_data);
static void lic_scale_entry_update (PikaLabelSpin *entry,
gdouble *value);
G_DEFINE_TYPE (Lic, lic, PIKA_TYPE_PLUG_IN)
PIKA_MAIN (LIC_TYPE)
DEFINE_STD_SET_I18N
static void
lic_class_init (LicClass *klass)
{
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
plug_in_class->query_procedures = lic_query_procedures;
plug_in_class->create_procedure = lic_create_procedure;
plug_in_class->set_i18n = STD_SET_I18N;
}
static void
lic_init (Lic *lic)
{
}
/*****************************/
/* Global variables and such */
/*****************************/
static gdouble G[numx][numy][2];
typedef struct
{
gdouble filtlen;
gdouble noisemag;
gdouble intsteps;
gdouble minv;
gdouble maxv;
gint effect_channel;
gint effect_operator;
gint effect_convolve;
gint32 effect_image_id;
} LicValues;
static LicValues licvals;
static gdouble l = 10.0;
static gdouble dx = 2.0;
static gdouble dy = 2.0;
static gdouble minv = -2.5;
static gdouble maxv = 2.5;
static gdouble isteps = 20.0;
static gboolean source_drw_has_alpha = FALSE;
static gint effect_width, effect_height;
static gint border_x, border_y, border_w, border_h;
static GtkWidget *dialog;
/************************/
/* Convenience routines */
/************************/
static void
peek (GeglBuffer *buffer,
gint x,
gint y,
PikaRGB *color)
{
gegl_buffer_sample (buffer, x, y, NULL,
color, babl_format ("R'G'B'A double"),
GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE);
}
static void
poke (GeglBuffer *buffer,
gint x,
gint y,
PikaRGB *color)
{
gegl_buffer_set (buffer, GEGL_RECTANGLE (x, y, 1, 1), 0,
babl_format ("R'G'B'A double"), color,
GEGL_AUTO_ROWSTRIDE);
}
static gint
peekmap (const guchar *image,
gint x,
gint y)
{
while (x < 0)
x += effect_width;
x %= effect_width;
while (y < 0)
y += effect_height;
y %= effect_height;
return (gint) image[x + effect_width * y];
}
/*************/
/* Main part */
/*************/
/***************************************************/
/* Compute the derivative in the x and y direction */
/* We use these convolution kernels: */
/* |1 0 -1| | 1 2 1| */
/* DX: |2 0 -2| DY: | 0 0 0| */
/* |1 0 -1| | -1 -2 -1| */
/* (It's a variation of the Sobel kernels, really) */
/***************************************************/
static gint
gradx (const guchar *image,
gint x,
gint y)
{
gint val = 0;
val = val + peekmap (image, x-1, y-1);
val = val - peekmap (image, x+1, y-1);
val = val + 2 * peekmap (image, x-1, y);
val = val - 2 * peekmap (image, x+1, y);
val = val + peekmap (image, x-1, y+1);
val = val - peekmap (image, x+1, y+1);
return val;
}
static gint
grady (const guchar *image,
gint x,
gint y)
{
gint val = 0;
val = val + peekmap (image, x-1, y-1);
val = val + 2 * peekmap (image, x, y-1);
val = val + peekmap (image, x+1, y-1);
val = val - peekmap (image, x-1, y+1);
val = val - 2 * peekmap (image, x, y+1);
val = val - peekmap (image, x+1, y+1);
return val;
}
/************************************/
/* A nice 2nd order cubic spline :) */
/************************************/
static gdouble
cubic (gdouble t)
{
gdouble at = fabs (t);
return (at < 1.0) ? at * at * (2.0 * at - 3.0) + 1.0 : 0.0;
}
static gdouble
omega (gdouble u,
gdouble v,
gint i,
gint j)
{
while (i < 0)
i += numx;
while (j < 0)
j += numy;
i %= numx;
j %= numy;
return cubic (u) * cubic (v) * (G[i][j][0]*u + G[i][j][1]*v);
}
/*************************************************************/
/* The noise function (2D variant of Perlins noise function) */
/*************************************************************/
static gdouble
noise (gdouble x,
gdouble y)
{
gint i, sti = (gint) floor (x / dx);
gint j, stj = (gint) floor (y / dy);
gdouble sum = 0.0;
/* Calculate the gdouble sum */
/* ======================== */
for (i = sti; i <= sti + 1; i++)
for (j = stj; j <= stj + 1; j++)
sum += omega ((x - (gdouble) i * dx) / dx,
(y - (gdouble) j * dy) / dy,
i, j);
return sum;
}
/*************************************************/
/* Generates pseudo-random vectors with length 1 */
/*************************************************/
static void
generatevectors (void)
{
gdouble alpha;
gint i, j;
GRand *gr;
gr = g_rand_new();
for (i = 0; i < numx; i++)
{
for (j = 0; j < numy; j++)
{
alpha = g_rand_double_range (gr, 0, 2) * G_PI;
G[i][j][0] = cos (alpha);
G[i][j][1] = sin (alpha);
}
}
g_rand_free (gr);
}
/* A simple triangle filter */
/* ======================== */
static gdouble
filter (gdouble u)
{
gdouble f = 1.0 - fabs (u) / l;
return (f < 0.0) ? 0.0 : f;
}
/******************************************************/
/* Compute the Line Integral Convolution (LIC) at x,y */
/******************************************************/
static gdouble
lic_noise (gint x,
gint y,
gdouble vx,
gdouble vy)
{
gdouble i = 0.0;
gdouble f1 = 0.0, f2 = 0.0;
gdouble u, step = 2.0 * l / isteps;
gdouble xx = (gdouble) x, yy = (gdouble) y;
gdouble c, s;
/* Get vector at x,y */
/* ================= */
c = vx;
s = vy;
/* Calculate integral numerically */
/* ============================== */
f1 = filter (-l) * noise (xx + l * c , yy + l * s);
for (u = -l + step; u <= l; u += step)
{
f2 = filter (u) * noise ( xx - u * c , yy - u * s);
i += (f1 + f2) * 0.5 * step;
f1 = f2;
}
i = (i - minv) / (maxv - minv);
i = CLAMP (i, 0.0, 1.0);
i = (i / 2.0) + 0.5;
return i;
}
static void
getpixel (GeglBuffer *buffer,
PikaRGB *p,
gdouble u,
gdouble v)
{
register gint x1, y1, x2, y2;
gint width, height;
static PikaRGB pp[4];
width = border_w;
height = border_h;
x1 = (gint)u;
y1 = (gint)v;
if (x1 < 0)
x1 = width - (-x1 % width);
else
x1 = x1 % width;
if (y1 < 0)
y1 = height - (-y1 % height);
else
y1 = y1 % height;
x2 = (x1 + 1) % width;
y2 = (y1 + 1) % height;
peek (buffer, x1, y1, &pp[0]);
peek (buffer, x2, y1, &pp[1]);
peek (buffer, x1, y2, &pp[2]);
peek (buffer, x2, y2, &pp[3]);
if (source_drw_has_alpha)
*p = pika_bilinear_rgba (u, v, pp);
else
*p = pika_bilinear_rgb (u, v, pp);
}
static void
lic_image (GeglBuffer *buffer,
gint x,
gint y,
gdouble vx,
gdouble vy,
PikaRGB *color)
{
gdouble u, step = 2.0 * l / isteps;
gdouble xx = (gdouble) x, yy = (gdouble) y;
gdouble c, s;
PikaRGB col = { 0, 0, 0, 0 };
PikaRGB col1, col2, col3;
/* Get vector at x,y */
/* ================= */
c = vx;
s = vy;
/* Calculate integral numerically */
/* ============================== */
getpixel (buffer, &col1, xx + l * c, yy + l * s);
if (source_drw_has_alpha)
pika_rgba_multiply (&col1, filter (-l));
else
pika_rgb_multiply (&col1, filter (-l));
for (u = -l + step; u <= l; u += step)
{
getpixel (buffer, &col2, xx - u * c, yy - u * s);
if (source_drw_has_alpha)
{
pika_rgba_multiply (&col2, filter (u));
col3 = col1;
pika_rgba_add (&col3, &col2);
pika_rgba_multiply (&col3, 0.5 * step);
pika_rgba_add (&col, &col3);
}
else
{
pika_rgb_multiply (&col2, filter (u));
col3 = col1;
pika_rgb_add (&col3, &col2);
pika_rgb_multiply (&col3, 0.5 * step);
pika_rgb_add (&col, &col3);
}
col1 = col2;
}
if (source_drw_has_alpha)
pika_rgba_multiply (&col, 1.0 / l);
else
pika_rgb_multiply (&col, 1.0 / l);
pika_rgb_clamp (&col);
*color = col;
}
static guchar *
rgb_to_hsl (PikaDrawable *drawable,
LICEffectChannel effect_channel)
{
GeglBuffer *buffer;
guchar *themap, data[4];
gint x, y;
PikaRGB color;
PikaHSL color_hsl;
gdouble val = 0.0;
glong maxc, index = 0;
GRand *gr;
gr = g_rand_new ();
maxc = pika_drawable_get_width (drawable) * pika_drawable_get_height (drawable);
buffer = pika_drawable_get_buffer (drawable);
themap = g_new (guchar, maxc);
for (y = 0; y < border_h; y++)
{
for (x = 0; x < border_w; x++)
{
data[3] = 255;
gegl_buffer_sample (buffer, x, y, NULL,
data, babl_format ("R'G'B'A u8"),
GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE);
pika_rgba_set_uchar (&color, data[0], data[1], data[2], data[3]);
pika_rgb_to_hsl (&color, &color_hsl);
switch (effect_channel)
{
case LIC_HUE:
val = color_hsl.h * 255;
break;
case LIC_SATURATION:
val = color_hsl.s * 255;
break;
case LIC_BRIGHTNESS:
val = color_hsl.l * 255;
break;
}
/* add some random to avoid unstructured areas. */
val += g_rand_double_range (gr, -1.0, 1.0);
themap[index++] = (guchar) CLAMP0255 (RINT (val));
}
}
g_object_unref (buffer);
g_rand_free (gr);
return themap;
}
static void
compute_lic (PikaDrawable *drawable,
const guchar *scalarfield,
gboolean rotate)
{
GeglBuffer *src_buffer;
GeglBuffer *dest_buffer;
gint xcount, ycount;
PikaRGB color;
gdouble vx, vy, tmp;
src_buffer = pika_drawable_get_buffer (drawable);
dest_buffer = pika_drawable_get_shadow_buffer (drawable);
for (ycount = 0; ycount < border_h; ycount++)
{
for (xcount = 0; xcount < border_w; xcount++)
{
/* Get derivative at (x,y) and normalize it */
/* ============================================================== */
vx = gradx (scalarfield, xcount, ycount);
vy = grady (scalarfield, xcount, ycount);
/* Rotate if needed */
if (rotate)
{
tmp = vy;
vy = -vx;
vx = tmp;
}
tmp = sqrt (vx * vx + vy * vy);
if (tmp >= 0.000001)
{
tmp = 1.0 / tmp;
vx *= tmp;
vy *= tmp;
}
/* Convolve with the LIC at (x,y) */
/* ============================== */
if (licvals.effect_convolve == 0)
{
peek (src_buffer, xcount, ycount, &color);
tmp = lic_noise (xcount, ycount, vx, vy);
if (source_drw_has_alpha)
pika_rgba_multiply (&color, tmp);
else
pika_rgb_multiply (&color, tmp);
}
else
{
lic_image (src_buffer, xcount, ycount, vx, vy, &color);
}
poke (dest_buffer, xcount, ycount, &color);
}
pika_progress_update ((gfloat) ycount / (gfloat) border_h);
}
g_object_unref (src_buffer);
g_object_unref (dest_buffer);
pika_progress_update (1.0);
}
static void
compute_image (PikaDrawable *drawable)
{
PikaDrawable *effect_image;
guchar *scalarfield = NULL;
/* Get some useful info on the input drawable */
/* ========================================== */
if (! pika_drawable_mask_intersect (drawable,
&border_x, &border_y,
&border_w, &border_h))
return;
pika_progress_init (_("Van Gogh (LIC)"));
if (licvals.effect_convolve == 0)
generatevectors ();
if (licvals.filtlen < 0.1)
licvals.filtlen = 0.1;
l = licvals.filtlen;
dx = dy = licvals.noisemag;
minv = licvals.minv / 10.0;
maxv = licvals.maxv / 10.0;
isteps = licvals.intsteps;
source_drw_has_alpha = pika_drawable_has_alpha (drawable);
effect_image = pika_drawable_get_by_id (licvals.effect_image_id);
effect_width = pika_drawable_get_width (effect_image);
effect_height = pika_drawable_get_height (effect_image);
switch (licvals.effect_channel)
{
case 0:
scalarfield = rgb_to_hsl (effect_image, LIC_HUE);
break;
case 1:
scalarfield = rgb_to_hsl (effect_image, LIC_SATURATION);
break;
case 2:
scalarfield = rgb_to_hsl (effect_image, LIC_BRIGHTNESS);
break;
}
compute_lic (drawable, scalarfield, licvals.effect_operator);
g_free (scalarfield);
/* Update image */
/* ============ */
pika_drawable_merge_shadow (drawable, TRUE);
pika_drawable_update (drawable, border_x, border_y, border_w, border_h);
pika_displays_flush ();
}
/**************************/
/* Below is only UI stuff */
/**************************/
static gboolean
effect_image_constrain (PikaImage *image,
PikaItem *item,
gpointer data)
{
return pika_drawable_is_rgb (PIKA_DRAWABLE (item));
}
static gboolean
create_main_dialog (void)
{
GtkWidget *vbox;
GtkWidget *hbox;
GtkWidget *frame;
GtkWidget *grid;
GtkWidget *combo;
GtkWidget *scale;
gint row;
gboolean run;
pika_ui_init (PLUG_IN_BINARY);
dialog = pika_dialog_new (_("Van Gogh (LIC)"), PLUG_IN_ROLE,
NULL, 0,
pika_standard_help_func, PLUG_IN_PROC,
_("_Cancel"), GTK_RESPONSE_CANCEL,
_("_OK"), GTK_RESPONSE_OK,
NULL);
pika_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
GTK_RESPONSE_OK,
GTK_RESPONSE_CANCEL,
-1);
pika_window_set_transient (GTK_WINDOW (dialog));
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
vbox, TRUE, TRUE, 0);
gtk_widget_show (vbox);
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
gtk_widget_show (hbox);
frame = pika_int_radio_group_new (TRUE, _("Effect Channel"),
G_CALLBACK (pika_radio_button_update),
&licvals.effect_channel, NULL,
licvals.effect_channel,
_("_Hue"), 0, NULL,
_("_Saturation"), 1, NULL,
_("_Brightness"), 2, NULL,
NULL);
gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, FALSE, 0);
gtk_widget_show (frame);
frame = pika_int_radio_group_new (TRUE, _("Effect Operator"),
G_CALLBACK (pika_radio_button_update),
&licvals.effect_operator, NULL,
licvals.effect_operator,
_("_Derivative"), 0, NULL,
_("_Gradient"), 1, NULL,
NULL);
gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, FALSE, 0);
gtk_widget_show (frame);
frame = pika_int_radio_group_new (TRUE, _("Convolve"),
G_CALLBACK (pika_radio_button_update),
&licvals.effect_convolve, NULL,
licvals.effect_convolve,
_("_With white noise"), 0, NULL,
_("W_ith source image"), 1, NULL,
NULL);
gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, FALSE, 0);
gtk_widget_show (frame);
/* Effect image menu */
grid = gtk_grid_new ();
gtk_grid_set_column_spacing (GTK_GRID (grid), 6);
gtk_box_pack_start (GTK_BOX (vbox), grid, FALSE, FALSE, 0);
gtk_widget_show (grid);
combo = pika_drawable_combo_box_new (effect_image_constrain, NULL, NULL);
pika_int_combo_box_connect (PIKA_INT_COMBO_BOX (combo),
licvals.effect_image_id,
G_CALLBACK (pika_int_combo_box_get_active),
&licvals.effect_image_id, NULL);
pika_grid_attach_aligned (GTK_GRID (grid), 0, 0,
_("_Effect image:"), 0.0, 0.5, combo, 2);
grid = gtk_grid_new ();
gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
gtk_grid_set_column_spacing (GTK_GRID (grid), 6);
gtk_box_pack_start (GTK_BOX (vbox), grid, FALSE, FALSE, 0);
gtk_widget_show (grid);
row = 0;
scale = pika_scale_entry_new (_("_Filter length:"), licvals.filtlen, 0.1, 64, 1);
pika_label_spin_set_increments (PIKA_LABEL_SPIN (scale), 1.0, 8.0);
g_signal_connect (scale, "value-changed",
G_CALLBACK (lic_scale_entry_update),
&licvals.filtlen);
gtk_grid_attach (GTK_GRID (grid), scale, 0, row++, 3, 1);
gtk_widget_show (scale);
scale = pika_scale_entry_new (_("_Noise magnitude:"), licvals.noisemag, 1, 5, 1);
pika_label_spin_set_increments (PIKA_LABEL_SPIN (scale), 0.1, 1.0);
g_signal_connect (scale, "value-changed",
G_CALLBACK (lic_scale_entry_update),
&licvals.noisemag);
gtk_grid_attach (GTK_GRID (grid), scale, 0, row++, 3, 1);
gtk_widget_show (scale);
scale = pika_scale_entry_new (_("In_tegration steps:"), licvals.intsteps, 1, 40, 1);
g_signal_connect (scale, "value-changed",
G_CALLBACK (lic_scale_entry_update),
&licvals.intsteps);
gtk_grid_attach (GTK_GRID (grid), scale, 0, row++, 3, 1);
gtk_widget_show (scale);
scale = pika_scale_entry_new (_("_Minimum value:"), licvals.minv, -100, 0, 1);
g_signal_connect (scale, "value-changed",
G_CALLBACK (lic_scale_entry_update),
&licvals.minv);
gtk_grid_attach (GTK_GRID (grid), scale, 0, row++, 3, 1);
gtk_widget_show (scale);
scale = pika_scale_entry_new (_("M_aximum value:"),
licvals.maxv, 0, 100, 1);
g_signal_connect (scale, "value-changed",
G_CALLBACK (lic_scale_entry_update),
&licvals.maxv);
gtk_grid_attach (GTK_GRID (grid), scale, 0, row++, 3, 1);
gtk_widget_show (scale);
gtk_widget_show (dialog);
run = (pika_dialog_run (PIKA_DIALOG (dialog)) == GTK_RESPONSE_OK);
gtk_widget_destroy (dialog);
return run;
}
/*************************************/
/* Set parameters to standard values */
/*************************************/
static void
set_default_settings (void)
{
licvals.filtlen = 5;
licvals.noisemag = 2;
licvals.intsteps = 25;
licvals.minv = -25;
licvals.maxv = 25;
licvals.effect_channel = 2;
licvals.effect_operator = 1;
licvals.effect_convolve = 1;
licvals.effect_image_id = -1;
}
static GList *
lic_query_procedures (PikaPlugIn *plug_in)
{
return g_list_append (NULL, g_strdup (PLUG_IN_PROC));
}
static PikaProcedure *
lic_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,
lic_run, NULL, NULL);
pika_procedure_set_image_types (procedure, "RGB*");
pika_procedure_set_sensitivity_mask (procedure,
PIKA_PROCEDURE_SENSITIVE_DRAWABLE);
pika_procedure_set_menu_label (procedure, _("_Van Gogh (LIC)..."));
pika_procedure_add_menu_path (procedure, "<Image>/Filters/Artistic");
pika_procedure_set_documentation (procedure,
_("Special effects that nobody "
"understands"),
"No help yet",
name);
pika_procedure_set_attribution (procedure,
"Tom Bech & Federico Mena Quintero",
"Tom Bech & Federico Mena Quintero",
"Version 0.14, September 24 1997");
}
return procedure;
}
static PikaValueArray *
lic_run (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
const PikaValueArray *args,
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];
}
/* Set default values */
/* ================== */
set_default_settings ();
/* Possibly retrieve data */
/* ====================== */
pika_get_data (PLUG_IN_PROC, &licvals);
if (! pika_item_id_is_valid (licvals.effect_image_id))
licvals.effect_image_id = -1;
/* Make sure that the drawable is RGBA or RGB color */
/* ================================================ */
if (pika_drawable_is_rgb (drawable))
{
switch (run_mode)
{
case PIKA_RUN_INTERACTIVE:
if (! create_main_dialog ())
{
return pika_procedure_new_return_values (procedure,
PIKA_PDB_CANCEL,
NULL);
}
compute_image (drawable);
pika_set_data (PLUG_IN_PROC, &licvals, sizeof (LicValues));
break;
case PIKA_RUN_WITH_LAST_VALS:
compute_image (drawable);
break;
default:
break;
}
}
else
{
return pika_procedure_new_return_values (procedure,
PIKA_PDB_EXECUTION_ERROR,
NULL);
}
return pika_procedure_new_return_values (procedure, PIKA_PDB_SUCCESS, NULL);
}
static void
lic_scale_entry_update (PikaLabelSpin *entry,
gdouble *value)
{
*value = pika_label_spin_get_value (entry);
}

1928
plug-ins/common/warp.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,425 @@
/*
* Wavelet decompose plug-in by Miroslav Talasek, miroslav.talasek@seznam.cz
*/
/*
* 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/>.
*/
#include "config.h"
#include <libpika/pika.h>
#include <libpika/pikaui.h>
#include "libpika/stdplugins-intl.h"
#define PLUG_IN_PROC "plug-in-wavelet-decompose"
#define PLUG_IN_ROLE "pika-wavelet-decompose"
#define PLUG_IN_BINARY "wavelet-decompose"
typedef struct _Wavelet Wavelet;
typedef struct _WaveletClass WaveletClass;
struct _Wavelet
{
PikaPlugIn parent_instance;
};
struct _WaveletClass
{
PikaPlugInClass parent_class;
};
#define WAVELET_TYPE (wavelet_get_type ())
#define WAVELET (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), WAVELET_TYPE, Wavelet))
GType wavelet_get_type (void) G_GNUC_CONST;
static GList * wavelet_query_procedures (PikaPlugIn *plug_in);
static PikaProcedure * wavelet_create_procedure (PikaPlugIn *plug_in,
const gchar *name);
static PikaValueArray * wavelet_run (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
const PikaValueArray *args,
gpointer run_data);
static void wavelet_blur (PikaDrawable *drawable,
gint radius);
static gboolean wavelet_decompose_dialog (PikaProcedure *procedure,
GObject *config);
G_DEFINE_TYPE (Wavelet, wavelet, PIKA_TYPE_PLUG_IN)
PIKA_MAIN (WAVELET_TYPE)
DEFINE_STD_SET_I18N
static void
wavelet_class_init (WaveletClass *klass)
{
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
plug_in_class->query_procedures = wavelet_query_procedures;
plug_in_class->create_procedure = wavelet_create_procedure;
plug_in_class->set_i18n = STD_SET_I18N;
}
static void
wavelet_init (Wavelet *wavelet)
{
}
static GList *
wavelet_query_procedures (PikaPlugIn *plug_in)
{
return g_list_append (NULL, g_strdup (PLUG_IN_PROC));
}
static PikaProcedure *
wavelet_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,
wavelet_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, _("_Wavelet-decompose..."));
pika_procedure_add_menu_path (procedure, "<Image>/Filters/Enhance");
pika_procedure_set_documentation (procedure,
_("Wavelet decompose"),
"Compute and render wavelet scales",
name);
pika_procedure_set_attribution (procedure,
"Miroslav Talasek <miroslav.talasek@seznam.cz>",
"Miroslav Talasek <miroslav.talasek@seznam.cz>",
"19 January 2017");
PIKA_PROC_ARG_INT (procedure, "scales",
_("Scal_es"),
_("Number of scales"),
1, 7, 5,
G_PARAM_READWRITE);
PIKA_PROC_ARG_BOOLEAN (procedure, "create-group",
_("Create a layer group to store the "
"_decomposition"),
"Create a layer group to store the "
"decomposition",
TRUE,
G_PARAM_READWRITE);
PIKA_PROC_ARG_BOOLEAN (procedure, "create-masks",
_("_Add a layer mask to each scales layer"),
"Add a layer mask to each scales layer",
FALSE,
G_PARAM_READWRITE);
}
return procedure;
}
static PikaValueArray *
wavelet_run (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
const PikaValueArray *args,
gpointer run_data)
{
PikaProcedureConfig *config;
PikaLayer **scale_layers;
PikaLayer *new_scale;
PikaLayer *parent = NULL;
PikaDrawable *drawable;
PikaLayerMode grain_extract_mode = PIKA_LAYER_MODE_GRAIN_EXTRACT;
PikaLayerMode grain_merge_mode = PIKA_LAYER_MODE_GRAIN_MERGE;
gint id;
gint scales;
gboolean create_group;
gboolean create_masks;
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."),
pika_procedure_get_name (procedure));
return pika_procedure_new_return_values (procedure,
PIKA_PDB_CALLING_ERROR,
error);
}
else
{
drawable = drawables[0];
}
config = pika_procedure_create_config (procedure);
pika_procedure_config_begin_run (config, NULL, run_mode, args);
switch (run_mode)
{
case PIKA_RUN_INTERACTIVE:
if (! wavelet_decompose_dialog (procedure, G_OBJECT (config)))
{
pika_procedure_config_end_run (config, PIKA_PDB_CANCEL);
g_object_unref (config);
return pika_procedure_new_return_values (procedure,
PIKA_PDB_CANCEL,
NULL);
}
break;
case PIKA_RUN_NONINTERACTIVE:
case PIKA_RUN_WITH_LAST_VALS:
break;
default:
break;
}
g_object_get (config,
"scales", &scales,
"create-group", &create_group,
"create-masks", &create_masks,
NULL);
pika_progress_init (_("Wavelet-Decompose"));
pika_image_undo_group_start (image);
pika_image_freeze_layers (image);
if (create_group)
{
parent = pika_layer_group_new (image);
pika_item_set_name (PIKA_ITEM (parent), _("Decomposition"));
pika_item_set_visible (PIKA_ITEM (parent), FALSE);
pika_image_insert_layer (image, parent,
PIKA_LAYER (pika_item_get_parent (PIKA_ITEM (drawable))),
pika_image_get_item_position (image,
PIKA_ITEM (drawable)));
}
scale_layers = g_new (PikaLayer *, scales);
new_scale = pika_layer_copy (PIKA_LAYER (drawable));
pika_image_insert_layer (image, new_scale, parent,
pika_image_get_item_position (image,
PIKA_ITEM (drawable)));
/* the exact result of the grain-extract and grain-merge modes
* depends on the choice of (gamma-corrected) midpoint intensity
* value. for the non-legacy modes, the midpoint value is 0.5,
* which isn't representable exactly using integer precision. for
* the legacy modes, the midpoint value is 128/255 (i.e., 0x80),
* which is representable exactly using (gamma-corrected) integer
* precision. we therefore use the legacy modes when the input
* image precision is integer, and only use the (preferable)
* non-legacy modes when the input image precision is floating
* point.
*
* this avoids imperfect reconstruction of the image when using
* integer precision. see bug #786844.
*/
switch (pika_image_get_precision (image))
{
case PIKA_PRECISION_U8_LINEAR:
case PIKA_PRECISION_U8_NON_LINEAR:
case PIKA_PRECISION_U8_PERCEPTUAL:
case PIKA_PRECISION_U16_LINEAR:
case PIKA_PRECISION_U16_NON_LINEAR:
case PIKA_PRECISION_U16_PERCEPTUAL:
case PIKA_PRECISION_U32_LINEAR:
case PIKA_PRECISION_U32_NON_LINEAR:
case PIKA_PRECISION_U32_PERCEPTUAL:
grain_extract_mode = PIKA_LAYER_MODE_GRAIN_EXTRACT_LEGACY;
grain_merge_mode = PIKA_LAYER_MODE_GRAIN_MERGE_LEGACY;
break;
case PIKA_PRECISION_HALF_LINEAR:
case PIKA_PRECISION_HALF_NON_LINEAR:
case PIKA_PRECISION_HALF_PERCEPTUAL:
case PIKA_PRECISION_FLOAT_LINEAR:
case PIKA_PRECISION_FLOAT_NON_LINEAR:
case PIKA_PRECISION_FLOAT_PERCEPTUAL:
case PIKA_PRECISION_DOUBLE_LINEAR:
case PIKA_PRECISION_DOUBLE_NON_LINEAR:
case PIKA_PRECISION_DOUBLE_PERCEPTUAL:
grain_extract_mode = PIKA_LAYER_MODE_GRAIN_EXTRACT;
grain_merge_mode = PIKA_LAYER_MODE_GRAIN_MERGE;
break;
}
for (id = 0 ; id < scales; id++)
{
PikaLayer *blur;
PikaLayer *tmp;
gchar scale_name[20];
pika_progress_update ((gdouble) id / (gdouble) scales);
scale_layers[id] = new_scale;
g_snprintf (scale_name, sizeof (scale_name), _("Scale %d"), id + 1);
pika_item_set_name (PIKA_ITEM (new_scale), scale_name);
tmp = pika_layer_copy (new_scale);
pika_image_insert_layer (image, tmp, parent,
pika_image_get_item_position (image,
PIKA_ITEM (new_scale)));
wavelet_blur (PIKA_DRAWABLE (tmp), pow (2.0, id));
blur = pika_layer_copy (tmp);
pika_image_insert_layer (image, blur, parent,
pika_image_get_item_position (image,
PIKA_ITEM (tmp)));
pika_layer_set_mode (tmp, grain_extract_mode);
new_scale = pika_image_merge_down (image, tmp,
PIKA_EXPAND_AS_NECESSARY);
scale_layers[id] = new_scale;
pika_item_set_visible (PIKA_ITEM (new_scale), FALSE);
new_scale = blur;
}
pika_item_set_name (PIKA_ITEM (new_scale), _("Residual"));
for (id = 0; id < scales; id++)
{
pika_image_reorder_item (image, PIKA_ITEM (scale_layers[id]),
PIKA_ITEM (parent),
pika_image_get_item_position (image,
PIKA_ITEM (new_scale)));
pika_layer_set_mode (scale_layers[id], grain_merge_mode);
if (create_masks)
{
PikaLayerMask *mask = pika_layer_create_mask (scale_layers[id],
PIKA_ADD_MASK_WHITE);
pika_layer_add_mask (scale_layers[id], mask);
}
pika_item_set_visible (PIKA_ITEM (scale_layers[id]), TRUE);
}
if (create_group)
pika_item_set_visible (PIKA_ITEM (parent), TRUE);
g_free (scale_layers);
pika_image_thaw_layers (image);
pika_image_undo_group_end (image);
pika_progress_update (1.0);
pika_displays_flush ();
pika_procedure_config_end_run (config, PIKA_PDB_SUCCESS);
g_object_unref (config);
gegl_exit ();
return pika_procedure_new_return_values (procedure, PIKA_PDB_SUCCESS, NULL);
}
static void
wavelet_blur (PikaDrawable *drawable,
gint radius)
{
gint x, y, width, height;
if (pika_drawable_mask_intersect (drawable, &x, &y, &width, &height))
{
GeglBuffer *buffer = pika_drawable_get_buffer (drawable);
GeglBuffer *shadow = pika_drawable_get_shadow_buffer (drawable);
gegl_render_op (buffer, shadow,
"gegl:wavelet-blur",
"radius", (gdouble) radius,
NULL);
gegl_buffer_flush (shadow);
pika_drawable_merge_shadow (drawable, FALSE);
pika_drawable_update (drawable, x, y, width, height);
g_object_unref (buffer);
g_object_unref (shadow);
}
}
static gboolean
wavelet_decompose_dialog (PikaProcedure *procedure,
GObject *config)
{
GtkWidget *dialog;
gboolean run;
pika_ui_init (PLUG_IN_BINARY);
dialog = pika_procedure_dialog_new (procedure,
PIKA_PROCEDURE_CONFIG (config),
_("Wavelet decompose"));
pika_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
GTK_RESPONSE_OK,
GTK_RESPONSE_CANCEL,
-1);
pika_window_set_transient (GTK_WINDOW (dialog));
/* scales */
pika_procedure_dialog_get_scale_entry (PIKA_PROCEDURE_DIALOG (dialog),
"scales", 1.0);
pika_procedure_dialog_fill (PIKA_PROCEDURE_DIALOG (dialog),
NULL);
gtk_widget_show (dialog);
run = pika_procedure_dialog_run (PIKA_PROCEDURE_DIALOG (dialog));
gtk_widget_destroy (dialog);
return run;
}

View File

@ -0,0 +1,251 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* Web Browser Plug-in
* Copyright (C) 2003 Henrik Brix Andersen <brix@gimp.org>
*
* 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/>.
*/
#include "config.h"
#include <string.h> /* strlen, strstr */
#include <gtk/gtk.h>
#ifdef PLATFORM_OSX
#import <Cocoa/Cocoa.h>
#endif
#ifdef G_OS_WIN32
#include <windows.h>
#endif
#include <libpika/pika.h>
#include <libpika/pikaui.h>
#include "libpika/stdplugins-intl.h"
#define PLUG_IN_PROC "plug-in-web-browser"
#define PLUG_IN_BINARY "web-browser"
#define PLUG_IN_ROLE "pika-web-browser"
typedef struct _Browser Browser;
typedef struct _BrowserClass BrowserClass;
struct _Browser
{
PikaPlugIn parent_instance;
};
struct _BrowserClass
{
PikaPlugInClass parent_class;
};
#define BROWSER_TYPE (browser_get_type ())
#define BROWSER (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), BROWSER_TYPE, Browser))
GType browser_get_type (void) G_GNUC_CONST;
static GList * browser_query_procedures (PikaPlugIn *plug_in);
static PikaProcedure * browser_create_procedure (PikaPlugIn *plug_in,
const gchar *name);
static PikaValueArray * browser_run (PikaProcedure *procedure,
const PikaValueArray *args,
gpointer run_data);
static gboolean browser_open_url (GtkWindow *window,
const gchar *url,
GError **error);
G_DEFINE_TYPE (Browser, browser, PIKA_TYPE_PLUG_IN)
PIKA_MAIN (BROWSER_TYPE)
DEFINE_STD_SET_I18N
static void
browser_class_init (BrowserClass *klass)
{
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
plug_in_class->query_procedures = browser_query_procedures;
plug_in_class->create_procedure = browser_create_procedure;
plug_in_class->set_i18n = STD_SET_I18N;
}
static void
browser_init (Browser *browser)
{
}
static GList *
browser_query_procedures (PikaPlugIn *plug_in)
{
return g_list_append (NULL, g_strdup (PLUG_IN_PROC));
}
static PikaProcedure *
browser_create_procedure (PikaPlugIn *plug_in,
const gchar *name)
{
PikaProcedure *procedure = NULL;
if (! strcmp (name, PLUG_IN_PROC))
{
procedure = pika_procedure_new (plug_in, name,
PIKA_PDB_PROC_TYPE_PLUGIN,
browser_run, NULL, NULL);
pika_procedure_set_documentation (procedure,
"Open an URL in the user specified "
"web browser",
"Opens the given URL in the user "
"specified web browser.",
name);
pika_procedure_set_attribution (procedure,
"Henrik Brix Andersen <brix@gimp.org>",
"2003",
"2003/09/16");
PIKA_PROC_ARG_STRING (procedure, "url",
"URL",
"URL to open",
"https://heckin.technology/AlderconeStudio/PIKApp/",
G_PARAM_READWRITE);
}
return procedure;
}
static PikaValueArray *
browser_run (PikaProcedure *procedure,
const PikaValueArray *args,
gpointer run_data)
{
GError *error = NULL;
if (! browser_open_url (NULL, PIKA_VALUES_GET_STRING (args, 0),
&error))
{
return pika_procedure_new_return_values (procedure,
PIKA_PDB_EXECUTION_ERROR,
error);
}
return pika_procedure_new_return_values (procedure,
PIKA_PDB_SUCCESS,
NULL);
}
static gboolean
browser_open_url (GtkWindow *window,
const gchar *url,
GError **error)
{
#ifdef G_OS_WIN32
HINSTANCE hinst = ShellExecute (GetDesktopWindow(),
"open", url, NULL, NULL, SW_SHOW);
if ((intptr_t) hinst <= 32)
{
const gchar *err;
switch ((intptr_t) hinst)
{
case 0 :
err = _("The operating system is out of memory or resources.");
break;
case ERROR_FILE_NOT_FOUND :
err = _("The specified file was not found.");
break;
case ERROR_PATH_NOT_FOUND :
err = _("The specified path was not found.");
break;
case ERROR_BAD_FORMAT :
err = _("The .exe file is invalid (non-Microsoft Win32 .exe or error in .exe image).");
break;
case SE_ERR_ACCESSDENIED :
err = _("The operating system denied access to the specified file.");
break;
case SE_ERR_ASSOCINCOMPLETE :
err = _("The file name association is incomplete or invalid.");
break;
case SE_ERR_DDEBUSY :
err = _("DDE transaction busy");
break;
case SE_ERR_DDEFAIL :
err = _("The DDE transaction failed.");
break;
case SE_ERR_DDETIMEOUT :
err = _("The DDE transaction timed out.");
break;
case SE_ERR_DLLNOTFOUND :
err = _("The specified DLL was not found.");
break;
case SE_ERR_NOASSOC :
err = _("There is no application associated with the given file name extension.");
break;
case SE_ERR_OOM :
err = _("There was not enough memory to complete the operation.");
break;
case SE_ERR_SHARE:
err = _("A sharing violation occurred.");
break;
default :
err = _("Unknown Microsoft Windows error.");
}
g_set_error (error, 0, 0, _("Failed to open '%s': %s"), url, err);
return FALSE;
}
return TRUE;
#elif defined(PLATFORM_OSX)
NSURL *ns_url;
gboolean retval;
@autoreleasepool
{
ns_url = [NSURL URLWithString: [NSString stringWithUTF8String: url]];
retval = [[NSWorkspace sharedWorkspace] openURL: ns_url];
}
return retval;
#else
pika_ui_init (PLUG_IN_BINARY);
return gtk_show_uri_on_window (window,
url,
GDK_CURRENT_TIME,
error);
#endif
}

604
plug-ins/common/web-page.c Normal file
View File

@ -0,0 +1,604 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* 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/>.
*/
/* Webpage plug-in.
* Copyright (C) 2011 Mukund Sivaraman <muks@banu.com>.
* Portions are copyright of the author of the
* file-open-location-dialog.c code.
*/
#include "config.h"
#include <libpika/pika.h>
#include <libpika/pikaui.h>
#include <webkit2/webkit2.h>
#include "libpika/stdplugins-intl.h"
#define PLUG_IN_PROC "plug-in-web-page"
#define PLUG_IN_BINARY "web-page"
#define PLUG_IN_ROLE "pika-web-page"
#define MAX_URL_LEN 2048
typedef struct
{
char *url;
gint32 width;
gint font_size;
PikaImage *image;
GError *error;
} WebpageVals;
typedef struct
{
char url[MAX_URL_LEN];
gint32 width;
gint font_size;
} WebpageSaveVals;
typedef struct _Webpage Webpage;
typedef struct _WebpageClass WebpageClass;
struct _Webpage
{
PikaPlugIn parent_instance;
};
struct _WebpageClass
{
PikaPlugInClass parent_class;
};
#define WEBPAGE_TYPE (webpage_get_type ())
#define WEBPAGE (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), WEBPAGE_TYPE, Webpage))
GType webpage_get_type (void) G_GNUC_CONST;
static GList * webpage_query_procedures (PikaPlugIn *plug_in);
static PikaProcedure * webpage_create_procedure (PikaPlugIn *plug_in,
const gchar *name);
static PikaValueArray * webpage_run (PikaProcedure *procedure,
const PikaValueArray *args,
gpointer run_data);
static gboolean webpage_dialog (void);
static PikaImage * webpage_capture (void);
G_DEFINE_TYPE (Webpage, webpage, PIKA_TYPE_PLUG_IN)
PIKA_MAIN (WEBPAGE_TYPE)
DEFINE_STD_SET_I18N
static WebpageVals webpagevals;
static void
webpage_class_init (WebpageClass *klass)
{
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
plug_in_class->query_procedures = webpage_query_procedures;
plug_in_class->create_procedure = webpage_create_procedure;
plug_in_class->set_i18n = STD_SET_I18N;
}
static void
webpage_init (Webpage *webpage)
{
}
static GList *
webpage_query_procedures (PikaPlugIn *plug_in)
{
return g_list_append (NULL, g_strdup (PLUG_IN_PROC));
}
static PikaProcedure *
webpage_create_procedure (PikaPlugIn *plug_in,
const gchar *name)
{
PikaProcedure *procedure = NULL;
if (! strcmp (name, PLUG_IN_PROC))
{
procedure = pika_procedure_new (plug_in, name,
PIKA_PDB_PROC_TYPE_PLUGIN,
webpage_run, NULL, NULL);
pika_procedure_set_menu_label (procedure, _("From _Webpage..."));
pika_procedure_add_menu_path (procedure, "<Image>/File/Create");
pika_procedure_set_documentation (procedure,
_("Create an image of a webpage"),
"The plug-in allows you to take a "
"screenshot of a webpage.",
name);
pika_procedure_set_attribution (procedure,
"Mukund Sivaraman <muks@banu.com>",
"2011",
"2011");
PIKA_PROC_ARG_ENUM (procedure, "run-mode",
"Run mode",
"The run mode",
PIKA_TYPE_RUN_MODE,
PIKA_RUN_INTERACTIVE,
G_PARAM_READWRITE);
PIKA_PROC_ARG_STRING (procedure, "url",
"URL",
"URL of the webpage to screenshot",
"https://heckin.technology/AlderconeStudio/PIKApp/",
G_PARAM_READWRITE);
PIKA_PROC_ARG_INT (procedure, "width",
"Width",
"The width of the screenshot (in pixels)",
100, PIKA_MAX_IMAGE_SIZE, 1024,
G_PARAM_READWRITE);
PIKA_PROC_ARG_INT (procedure, "font-size",
"Font size",
"The font size to use in the page (in pt)",
1, 1000, 12,
G_PARAM_READWRITE);
PIKA_PROC_VAL_IMAGE (procedure, "image",
"Image",
"The output image",
FALSE,
G_PARAM_READWRITE);
}
return procedure;
}
static PikaValueArray *
webpage_run (PikaProcedure *procedure,
const PikaValueArray *args,
gpointer run_data)
{
PikaValueArray *return_vals;
PikaRunMode run_mode;
PikaImage *image;
WebpageSaveVals save = { "https://heckin.technology/AlderconeStudio/PIKApp/", 1024, 12 };
pika_get_data (PLUG_IN_PROC, &save);
run_mode = PIKA_VALUES_GET_ENUM (args, 0);
webpagevals.url = g_strdup (save.url);
webpagevals.width = save.width;
webpagevals.font_size = save.font_size;
/* how are we running today? */
switch (run_mode)
{
case PIKA_RUN_INTERACTIVE:
if (! webpage_dialog ())
return pika_procedure_new_return_values (procedure,
PIKA_PDB_CANCEL,
NULL);
break;
case PIKA_RUN_WITH_LAST_VALS:
return pika_procedure_new_return_values (procedure,
PIKA_PDB_CALLING_ERROR,
NULL);
break;
case PIKA_RUN_NONINTERACTIVE:
webpagevals.url = (gchar *) PIKA_VALUES_GET_STRING (args, 1);
webpagevals.width = PIKA_VALUES_GET_INT (args, 2);
webpagevals.font_size = PIKA_VALUES_GET_INT (args, 3);
break;
default:
break;
}
image = webpage_capture ();
if (! image)
return pika_procedure_new_return_values (procedure,
PIKA_PDB_EXECUTION_ERROR,
webpagevals.error);
save.width = webpagevals.width;
save.font_size = webpagevals.font_size;
if (strlen (webpagevals.url) < MAX_URL_LEN)
{
g_strlcpy (save.url, webpagevals.url, MAX_URL_LEN);
}
else
{
memset (save.url, 0, MAX_URL_LEN);
}
pika_set_data (PLUG_IN_PROC, &save, sizeof save);
if (run_mode == PIKA_RUN_INTERACTIVE)
pika_display_new (image);
return_vals = pika_procedure_new_return_values (procedure,
PIKA_PDB_SUCCESS,
NULL);
PIKA_VALUES_SET_IMAGE (return_vals, 1, image);
return return_vals;
}
static gboolean
webpage_dialog (void)
{
GtkWidget *dialog;
GtkWidget *hbox;
GtkWidget *vbox;
GtkWidget *image;
GtkWidget *label;
GtkWidget *entry;
GtkSizeGroup *sizegroup;
GtkAdjustment *adjustment;
GtkWidget *spinbutton;
GtkWidget *combo;
gint active;
gint status;
gboolean ret = FALSE;
pika_ui_init (PLUG_IN_BINARY);
dialog = pika_dialog_new (_("Create from webpage"), PLUG_IN_ROLE,
NULL, 0,
pika_standard_help_func, PLUG_IN_PROC,
_("_Cancel"), GTK_RESPONSE_CANCEL,
_("Cre_ate"), GTK_RESPONSE_OK,
NULL);
pika_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
GTK_RESPONSE_OK,
GTK_RESPONSE_CANCEL,
-1);
pika_window_set_transient (GTK_WINDOW (dialog));
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
gtk_container_set_border_width (GTK_CONTAINER (hbox), 12);
gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
hbox, FALSE, FALSE, 0);
gtk_widget_show (hbox);
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
gtk_widget_show (vbox);
image = gtk_image_new_from_icon_name (PIKA_ICON_WEB,
GTK_ICON_SIZE_BUTTON);
gtk_box_pack_start (GTK_BOX (vbox), image, FALSE, FALSE, 0);
gtk_widget_show (image);
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
gtk_widget_show (vbox);
label = gtk_label_new (_("Enter location (URI):"));
gtk_label_set_xalign (GTK_LABEL (label), 0.0);
gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
gtk_widget_show (label);
entry = gtk_entry_new ();
gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
gtk_widget_set_size_request (entry, 400, -1);
gtk_box_pack_start (GTK_BOX (vbox), entry, FALSE, FALSE, 0);
if (webpagevals.url)
gtk_entry_set_text (GTK_ENTRY (entry),
webpagevals.url);
gtk_widget_show (entry);
sizegroup = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
/* Width */
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
gtk_box_pack_start (GTK_BOX (vbox),
hbox, FALSE, FALSE, 0);
gtk_widget_show (hbox);
label = gtk_label_new (_("Width (pixels):"));
gtk_size_group_add_widget (sizegroup, label);
gtk_label_set_xalign (GTK_LABEL (label), 0.0);
gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
gtk_widget_show (label);
adjustment = gtk_adjustment_new (webpagevals.width,
1, 8192, 1, 10, 0);
spinbutton = pika_spin_button_new (adjustment, 1.0, 0);
gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
gtk_box_pack_start (GTK_BOX (hbox), spinbutton, FALSE, FALSE, 0);
gtk_widget_show (spinbutton);
/* Font size */
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
gtk_box_pack_start (GTK_BOX (vbox),
hbox, FALSE, FALSE, 0);
gtk_widget_show (hbox);
label = gtk_label_new (_("Font size:"));
gtk_size_group_add_widget (sizegroup, label);
gtk_label_set_xalign (GTK_LABEL (label), 0.0);
gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
gtk_widget_show (label);
combo = pika_int_combo_box_new (_("Huge"), 16,
_("Large"), 14,
C_("web-page", "Default"), 12,
_("Small"), 10,
_("Tiny"), 8,
NULL);
switch (webpagevals.font_size)
{
case 16:
case 14:
case 12:
case 10:
case 8:
active = webpagevals.font_size;
break;
default:
active = 12;
}
pika_int_combo_box_set_active (PIKA_INT_COMBO_BOX (combo), active);
gtk_box_pack_start (GTK_BOX (hbox), combo, FALSE, FALSE, 0);
gtk_widget_show (combo);
g_object_unref (sizegroup);
status = pika_dialog_run (PIKA_DIALOG (dialog));
if (status == GTK_RESPONSE_OK)
{
g_free (webpagevals.url);
webpagevals.url = g_strdup (gtk_entry_get_text (GTK_ENTRY (entry)));
webpagevals.width = (gint) gtk_adjustment_get_value (adjustment);
pika_int_combo_box_get_active (PIKA_INT_COMBO_BOX (combo),
&webpagevals.font_size);
ret = TRUE;
}
gtk_widget_destroy (dialog);
return ret;
}
static void
notify_progress_cb (WebKitWebView *view,
GParamSpec *pspec,
gpointer user_data)
{
static gdouble old_progress = 0.0;
gdouble progress = webkit_web_view_get_estimated_load_progress (view);
if ((progress - old_progress) > 0.01)
{
pika_progress_update (progress);
old_progress = progress;
}
}
static gboolean
load_failed_cb (WebKitWebView *view,
WebKitLoadEvent event,
gchar *uri,
gpointer web_error,
gpointer user_data)
{
webpagevals.error = g_error_copy ((GError *) web_error);
gtk_main_quit ();
return TRUE;
}
static void
snapshot_ready (GObject *source_object,
GAsyncResult *result,
gpointer user_data)
{
WebKitWebView *view = WEBKIT_WEB_VIEW (source_object);
cairo_surface_t *surface;
surface = webkit_web_view_get_snapshot_finish (view, result,
&webpagevals.error);
if (surface)
{
gint width;
gint height;
PikaLayer *layer;
width = cairo_image_surface_get_width (surface);
height = cairo_image_surface_get_height (surface);
webpagevals.image = pika_image_new (width, height, PIKA_RGB);
pika_image_undo_disable (webpagevals.image);
layer = pika_layer_new_from_surface (webpagevals.image, _("Webpage"),
surface,
0.25, 1.0);
pika_image_insert_layer (webpagevals.image, layer, NULL, 0);
pika_image_undo_enable (webpagevals.image);
cairo_surface_destroy (surface);
}
pika_progress_update (1.0);
gtk_main_quit ();
}
static gboolean
load_finished_idle (gpointer data)
{
static gint count = 0;
pika_progress_update ((gdouble) count * 0.025);
count++;
if (count < 10)
return G_SOURCE_CONTINUE;
webkit_web_view_get_snapshot (WEBKIT_WEB_VIEW (data),
WEBKIT_SNAPSHOT_REGION_FULL_DOCUMENT,
WEBKIT_SNAPSHOT_OPTIONS_NONE,
NULL,
snapshot_ready,
NULL);
count = 0;
return G_SOURCE_REMOVE;
}
static void
load_changed_cb (WebKitWebView *view,
WebKitLoadEvent event,
gpointer user_data)
{
if (event == WEBKIT_LOAD_FINISHED)
{
if (! webpagevals.error)
{
pika_progress_init_printf (_("Transferring webpage image for '%s'"),
webpagevals.url);
g_timeout_add (100, load_finished_idle, view);
return;
}
gtk_main_quit ();
}
}
static PikaImage *
webpage_capture (void)
{
gchar *scheme;
GtkWidget *window;
GtkWidget *view;
WebKitSettings *settings;
char *ua;
if (! webpagevals.url || strlen (webpagevals.url) == 0)
{
g_set_error (&webpagevals.error, 0, 0, _("No URL was specified"));
return NULL;
}
scheme = g_uri_parse_scheme (webpagevals.url);
if (! scheme)
{
char *url;
/* If we were not given a well-formed URL, make one. */
url = g_strconcat ("http://", webpagevals.url, NULL);
g_free (webpagevals.url);
webpagevals.url = url;
g_free (scheme);
}
if (webpagevals.width < 32)
{
g_warning ("Width '%d' is too small. Clamped to 32.",
webpagevals.width);
webpagevals.width = 32;
}
else if (webpagevals.width > 8192)
{
g_warning ("Width '%d' is too large. Clamped to 8192.",
webpagevals.width);
webpagevals.width = 8192;
}
window = gtk_offscreen_window_new ();
gtk_widget_show (window);
view = webkit_web_view_new ();
gtk_widget_show (view);
gtk_widget_set_vexpand (view, TRUE);
gtk_widget_set_size_request (view, webpagevals.width, -1);
gtk_container_add (GTK_CONTAINER (window), view);
/* Append "PIKA/<PIKA_VERSION>" to the user agent string */
settings = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (view));
ua = g_strdup_printf ("%s PIKA/%s",
webkit_settings_get_user_agent (settings),
PIKA_VERSION);
webkit_settings_set_user_agent (settings, ua);
g_free (ua);
/* Set font size */
webkit_settings_set_default_font_size (settings, webpagevals.font_size);
g_signal_connect (view, "notify::estimated-load-progress",
G_CALLBACK (notify_progress_cb),
window);
g_signal_connect (view, "load-failed",
G_CALLBACK (load_failed_cb),
window);
g_signal_connect (view, "load-changed",
G_CALLBACK (load_changed_cb),
window);
pika_progress_init_printf (_("Downloading webpage '%s'"), webpagevals.url);
webkit_web_view_load_uri (WEBKIT_WEB_VIEW (view),
webpagevals.url);
gtk_main ();
gtk_widget_destroy (window);
pika_progress_update (1.0);
return webpagevals.image;
}

1023
plug-ins/file-bmp/bmp-load.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,27 @@
/*
* PIKA - Photo and Image Kooker Application
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* 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/>.
*/
#ifndef __BMP_LOAD_H__
#define __BMP_LOAD_H__
PikaImage * load_image (GFile *file,
GError **error);
#endif /* __BMP_LOAD_H__ */

1078
plug-ins/file-bmp/bmp-save.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,32 @@
/*
* PIKA - Photo and Image Kooker Application
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* 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/>.
*/
#ifndef __BMP_SAVE_H__
#define __BMP_SAVE_H__
PikaPDBStatusType save_image (GFile *file,
PikaImage *image,
PikaDrawable *drawable,
PikaRunMode run_mode,
PikaProcedure *procedure,
GObject *config,
GError **error);
#endif /* __BMP_SAVE_H__ */

317
plug-ins/file-bmp/bmp.c Normal file
View File

@ -0,0 +1,317 @@
/* bmp.c */
/* Version 0.52 */
/* This is a File input and output filter for the */
/* Pika. It loads and saves images in windows(TM) */
/* bitmap format. */
/* Some Parts that deal with the interaction with */
/* PIKA are taken from the GIF plugin by */
/* Peter Mattis & Spencer Kimball and from the */
/* PCX plugin by Francisco Bustamante. */
/* */
/* Alexander.Schulz@stud.uni-karlsruhe.de */
/* Changes: 28.11.1997 Noninteractive operation */
/* 16.03.1998 Endian-independent!! */
/* 21.03.1998 Little Bug-fix */
/* 06.04.1998 Bugfix in Padding */
/* 11.04.1998 Arch. cleanup (-Wall) */
/* Parses gtkrc */
/* 14.04.1998 Another Bug in Padding */
/* 28.04.1998 RLE-Encoding rewritten */
/* 29.10.1998 Changes by Tor Lillqvist */
/* <tml@iki.fi> to support */
/* 16 and 32 bit images */
/* 28.11.1998 Bug in RLE-read-padding */
/* fixed. */
/* 19.12.1999 Resolution support added */
/* 06.05.2000 Overhaul for 16&24-bit */
/* plus better OS/2 code */
/* by njl195@zepler.org.uk */
/* 29.06.2006 Full support for 16/32 */
/* bits bitmaps and support */
/* for alpha channel */
/* by p.filiciak@zax.pl */
/*
* PIKA - Photo and Image Kooker Application
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* 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/>.
* ----------------------------------------------------------------------------
*/
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include <libpika/pika.h>
#include <libpika/pikaui.h>
#include "bmp.h"
#include "bmp-load.h"
#include "bmp-save.h"
#include "libpika/stdplugins-intl.h"
typedef struct _Bmp Bmp;
typedef struct _BmpClass BmpClass;
struct _Bmp
{
PikaPlugIn parent_instance;
};
struct _BmpClass
{
PikaPlugInClass parent_class;
};
#define BMP_TYPE (bmp_get_type ())
#define BMP (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), BMP_TYPE, Bmp))
GType bmp_get_type (void) G_GNUC_CONST;
static GList * bmp_query_procedures (PikaPlugIn *plug_in);
static PikaProcedure * bmp_create_procedure (PikaPlugIn *plug_in,
const gchar *name);
static PikaValueArray * bmp_load (PikaProcedure *procedure,
PikaRunMode run_mode,
GFile *file,
const PikaValueArray *args,
gpointer run_data);
static PikaValueArray * bmp_save (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
GFile *file,
const PikaValueArray *args,
gpointer run_data);
G_DEFINE_TYPE (Bmp, bmp, PIKA_TYPE_PLUG_IN)
PIKA_MAIN (BMP_TYPE)
DEFINE_STD_SET_I18N
static void
bmp_class_init (BmpClass *klass)
{
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
plug_in_class->query_procedures = bmp_query_procedures;
plug_in_class->create_procedure = bmp_create_procedure;
plug_in_class->set_i18n = STD_SET_I18N;
}
static void
bmp_init (Bmp *bmp)
{
}
static GList *
bmp_query_procedures (PikaPlugIn *plug_in)
{
GList *list = NULL;
list = g_list_append (list, g_strdup (LOAD_PROC));
list = g_list_append (list, g_strdup (SAVE_PROC));
return list;
}
static PikaProcedure *
bmp_create_procedure (PikaPlugIn *plug_in,
const gchar *name)
{
PikaProcedure *procedure = NULL;
if (! strcmp (name, LOAD_PROC))
{
procedure = pika_load_procedure_new (plug_in, name,
PIKA_PDB_PROC_TYPE_PLUGIN,
bmp_load, NULL, NULL);
pika_procedure_set_menu_label (procedure, _("Windows BMP image"));
pika_procedure_set_documentation (procedure,
_("Loads files of Windows BMP file format"),
_("Loads files of Windows BMP file format"),
name);
pika_procedure_set_attribution (procedure,
"Alexander Schulz",
"Alexander Schulz",
"1997");
pika_file_procedure_set_mime_types (PIKA_FILE_PROCEDURE (procedure),
"image/bmp");
pika_file_procedure_set_extensions (PIKA_FILE_PROCEDURE (procedure),
"bmp");
pika_file_procedure_set_magics (PIKA_FILE_PROCEDURE (procedure),
"0,string,BM");
}
else if (! strcmp (name, SAVE_PROC))
{
procedure = pika_save_procedure_new (plug_in, name,
PIKA_PDB_PROC_TYPE_PLUGIN,
bmp_save, NULL, NULL);
pika_procedure_set_image_types (procedure, "INDEXED, GRAY, RGB*");
pika_procedure_set_menu_label (procedure, _("Windows BMP image"));
pika_file_procedure_set_format_name (PIKA_FILE_PROCEDURE (procedure),
_("BMP"));
pika_procedure_set_documentation (procedure,
_("Saves files in Windows BMP file format"),
_("Saves files in Windows BMP file format"),
name);
pika_procedure_set_attribution (procedure,
"Alexander Schulz",
"Alexander Schulz",
"1997");
pika_file_procedure_set_mime_types (PIKA_FILE_PROCEDURE (procedure),
"image/bmp");
pika_file_procedure_set_extensions (PIKA_FILE_PROCEDURE (procedure),
"bmp");
PIKA_PROC_ARG_BOOLEAN (procedure, "use-rle",
_("Ru_n-Length Encoded"),
_("Use run-length-encoding compression "
"(only valid for 4 and 8-bit indexed images)"),
FALSE,
G_PARAM_READWRITE);
PIKA_PROC_ARG_BOOLEAN (procedure, "write-color-space",
_("_Write color space information"),
_("Whether or not to write BITMAPV5HEADER "
"color space data"),
TRUE,
G_PARAM_READWRITE);
PIKA_PROC_ARG_INT (procedure, "rgb-format",
_("R_GB format"),
_("Export format for RGB images "
"(0=RGB_565, 1=RGBA_5551, 2=RGB_555, 3=RGB_888, "
"4=RGBA_8888, 5=RGBX_8888)"),
0, 5, 3,
G_PARAM_READWRITE);
}
return procedure;
}
static PikaValueArray *
bmp_load (PikaProcedure *procedure,
PikaRunMode run_mode,
GFile *file,
const PikaValueArray *args,
gpointer run_data)
{
PikaValueArray *return_vals;
PikaImage *image;
GError *error = NULL;
gegl_init (NULL, NULL);
image = load_image (file, &error);
if (! image)
return pika_procedure_new_return_values (procedure,
PIKA_PDB_EXECUTION_ERROR,
error);
return_vals = pika_procedure_new_return_values (procedure,
PIKA_PDB_SUCCESS,
NULL);
PIKA_VALUES_SET_IMAGE (return_vals, 1, image);
return return_vals;
}
static PikaValueArray *
bmp_save (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
GFile *file,
const PikaValueArray *args,
gpointer run_data)
{
PikaProcedureConfig *config;
PikaPDBStatusType status = PIKA_PDB_SUCCESS;
PikaExportReturn export = PIKA_EXPORT_CANCEL;
GError *error = NULL;
gegl_init (NULL, NULL);
config = pika_procedure_create_config (procedure);
pika_procedure_config_begin_run (config, image, run_mode, args);
switch (run_mode)
{
case PIKA_RUN_INTERACTIVE:
case PIKA_RUN_WITH_LAST_VALS:
pika_ui_init (PLUG_IN_BINARY);
export = pika_export_image (&image, &n_drawables, &drawables, "BMP",
PIKA_EXPORT_CAN_HANDLE_RGB |
PIKA_EXPORT_CAN_HANDLE_GRAY |
PIKA_EXPORT_CAN_HANDLE_ALPHA |
PIKA_EXPORT_CAN_HANDLE_INDEXED);
if (export == PIKA_EXPORT_CANCEL)
return pika_procedure_new_return_values (procedure,
PIKA_PDB_CANCEL,
NULL);
break;
default:
break;
}
if (n_drawables != 1)
{
g_set_error (&error, G_FILE_ERROR, 0,
_("BMP format does not support multiple layers."));
return pika_procedure_new_return_values (procedure,
PIKA_PDB_CALLING_ERROR,
error);
}
status = save_image (file, image, drawables[0], run_mode,
procedure, G_OBJECT (config),
&error);
pika_procedure_config_end_run (config, status);
g_object_unref (config);
if (export == PIKA_EXPORT_EXPORT)
{
pika_image_delete (image);
g_free (drawables);
}
return pika_procedure_new_return_values (procedure, status, error);
}

70
plug-ins/file-bmp/bmp.h Normal file
View File

@ -0,0 +1,70 @@
/*
* PIKA - Photo and Image Kooker Application
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* 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/>.
*/
#ifndef __BMP_H__
#define __BMP_H__
#define LOAD_PROC "file-bmp-load"
#define SAVE_PROC "file-bmp-save"
#define PLUG_IN_BINARY "file-bmp"
#define PLUG_IN_ROLE "pika-file-bmp"
#define MAXCOLORS 256
#define BitSet(byte, bit) (((byte) & (bit)) == (bit))
#define ReadOK(file,buffer,len) (fread(buffer, len, 1, file) != 0)
#define Write(file,buffer,len) fwrite(buffer, len, 1, file)
#define WriteOK(file,buffer,len) (Write(buffer, len, file) != 0)
typedef struct
{
gchar zzMagic[2]; /* 00 "BM" */
guint32 bfSize; /* 02 */
guint16 zzHotX; /* 06 */
guint16 zzHotY; /* 08 */
guint32 bfOffs; /* 0A */
guint32 biSize; /* 0E */
} BitmapFileHead;
typedef struct
{
gint32 biWidth; /* 12 */
gint32 biHeight; /* 16 */
guint16 biPlanes; /* 1A */
guint16 biBitCnt; /* 1C */
guint32 biCompr; /* 1E */
guint32 biSizeIm; /* 22 */
guint32 biXPels; /* 26 */
guint32 biYPels; /* 2A */
guint32 biClrUsed; /* 2E */
guint32 biClrImp; /* 32 */
guint32 masks[4]; /* 36 */
} BitmapHead;
typedef struct
{
guint32 mask;
guint32 shiftin;
gfloat max_value;
} BitmapChannel;
#endif /* __BMP_H__ */

View File

@ -0,0 +1,28 @@
plugin_name = 'file-bmp'
plugin_sources = [
'bmp-load.c',
'bmp-save.c',
'bmp.c',
]
if platform_windows
plugin_sources += windows.compile_resources(
pika_plugins_rc,
args: [
'--define', 'ORIGINALFILENAME_STR="@0@"'.format(plugin_name+'.exe'),
'--define', 'INTERNALNAME_STR="@0@"' .format(plugin_name),
'--define', 'TOP_SRCDIR="@0@"' .format(meson.project_source_root()),
],
include_directories: [
rootInclude, appInclude,
],
)
endif
executable(plugin_name,
plugin_sources,
dependencies: libpikaui_dep,
install: true,
install_dir: pikaplugindir / 'plug-ins' / plugin_name,
)

339
plug-ins/file-dds/COPYING Normal file
View File

@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
Appendix: How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) 19yy <name of author>
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 USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) 19yy name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.

21
plug-ins/file-dds/LICENSE Normal file
View File

@ -0,0 +1,21 @@
/*
DDS PIKA plugin
Copyright (C) 2012 Shawn Kirst <skirst@gmail.com>,
with parts (C) 2003 Arne Reuter <homepage@arnereuter.de> where specified.
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; see the file COPYING. If not, write to
the Free Software Foundation, 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301 USA.
*/

View File

@ -0,0 +1,23 @@
// Copyright (c) 2009-2011 Ignacio Castano <castano@gmail.com>
// Copyright (c) 2007-2009 NVIDIA Corporation -- Ignacio Castano <icastano@nvidia.com>
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.

29
plug-ins/file-dds/README Normal file
View File

@ -0,0 +1,29 @@
DDS plugin for The PIKA
(C) 2004-2012 Shawn Kirst <skirst@gmail.com>,
with parts (C) 2003 Arne Reuter <homepage@arnereuter.de> where specified.
==========================================
This is a plugin for PIKA version 2.4.x. It allows you to load and save
images in Direct Draw Surface (DDS) format.
Features
==========================================
* Load/Save DDS files using DXT texture compression
* Automatic mipmap generation on save
* Load mipmaps into separate layers
* Load cube map faces and volume map slices into separate layers
* Cube and volume map saving
* Pixel conversion selection for custom formats (RGBA4, R5G6B5, RGB10A2, etc.)
* Load/save DDS files, optionally using DirectX texture compression (DXT)
* Optional automatic mipmap generation when saving
* Load mipmaps into separate layers
* Load cube map faces and volume map slices into separate layers
* Save cube maps and volume maps with automatic mipmap generation support
* Save image with a custom pixel format
* Non-power-of-two image loading and saving support with automatic mipmap generation support
* Compliant with DirectX 10 compressed formats
Installation
==========================================
See the file INSTALL for installation instructions

View File

@ -0,0 +1,175 @@
The following notes are from an email I received from Fabian 'ryg' Giesen. They
should help answer some questions regarding the code found in dxt.c
---
mul8bit: This formula is equivalent to (a*b)/255 for a fairly large set of
values (didn't bother finding out the exact bounds). It's a fairly well-known
trick also used in other parts of the PIKA codebase (among others) and was
first documented by Jim Blinn, I think. A good reference is his book "Dirty
Pixels".
---
lerp_rgb: The expression computed is exactly equivalent to mul8bit(a[i],255-f)
+ mul8bit(b[i],f) - I just verified that by brute force for -255 <= b[i]-a[i]
<= 255 because I couldn't be bothered to find a derivation for this :) . You
customarily use a factor between 0 and 256 include for LERPing if you can, but
normal DXT blocks have colors placed at 1/3 and 2/3 between the two
interpolated colors. 255 is divisible by 3, so lerp_rgb can later be used in
eval_colors to determine the result of
a*(1/3) + b*(2/3) and a*(2/3) + b*(1/3)
exactly, which is nice :)
---
dither_block: This is just Floyd-Steinberg dithering. Distributing the error
terms to the adjacent pixels for each source pixel is the customary variant to
write this, but since blocks are so small, it's nearly all boundary
cases; "gathering" the error terms per source pixel turned out to be simpler.
---
match_colors_block: This includes a few tricks. We want to map each source
color to its nearest representable color (index), using the euclidean distance
as a metric.
The obvious, brute-force way is to just compare squared distances to the 4
representable colors for each source pixel (using, for example,
color_distance); this requires a lot of arithmetic operations.
Instead, the code uses the fact that the 4 colors lie on a line in RGB space
(only approximately in truth, since we have discrete steps). It's a well-known
fact in geometry that if P is the closest point to the line L in 3D space, and
Q is the point closest to P on L, then (P-Q) is orthogonal to the direction of
L. So (in R3 at least), we can simply determine Q by projecting P onto the
direction vector of L, which is done by the 16 dot products in the first for
loop. Since the RGB values have discrete steps in reality, this is just an
approximation, but a quite good one.
The loop after that determines where the 4 actually representable colors lie
along the line. After that, you simply need to determine which of those 4
values your current pixel's dot product is closest to. Instead of doing a
bunch of comparisons per pixel, the code computes the points along the line
at which the decision would change (that's c0pt, halfpt and c3pt). This would
still require 3 comparisons; by testing the middle point - which is halfpt -
first, one point is always excluded from consideration, which reduces the
number of compares to two in all cases. No big deal, but hey, why not :)
Similarly, instead of dithering full RGB values, I just dither the dot product
values. Again, by my experiments this works just as well and reduces the
amount of work significantly.
---
optimize_colors_block: This first determines min/max/mean for r,g,b and the
covariance matrix for the color distribution. The latter is used to determine
the principal component (=eigenvector with largest eigenvalue) of that color
distribution, or the direction along which the colors in the block vary most
(in layman's terms) - the eigenvector is determined using simple power
iteration (a standard technique). That iteration needs a seed vector; I just
use (max_r-min_r,max_g-min_g,max_b-min_b), which works well in practice. If
the iteration converges to a vector with very small magnitude (or zero), which
can happen sometimes, it just defaults to an approximation of the YCbCr Y
vector (scaled appropriately to make sure no precision is lost with the dot
products).
This is then used as an initial approximation for the direction of the line
through RGB color space that is used to select colors for that block. It
simply uses the two most extreme points along that axis as the two colors
stored in the block.
---
refine_block: This takes a block and a chosen set of color indices, and tries
to determine the optimal endpoints for these indices (i.e. the full process
is: take block, use color distribution to get rough estimate of optimal
direction, assign color indices accordingly, use these to get better
endpoints, assign indices again). The computation just solves a least-squares
system to minimize the square error between the actual pixels and the
interpolated colors (solving for the two extremal colors). The least-squares
computation turns out to boil down to solving a 2x2 system of linear equations
for each of the RGB color channels; the actual solution is computed using
Cramer's rule.
The code is somewhat weird (especially the "prods"/"akku" thing), but that's
just to reduce the amount of computation done (this piece of code is a hot
spot, so it's worth it).
The (!yy || !xx || xx * yy == xy*xy) part checks whether the system of linear
equations is degenerate. After pondering about this some months ago, I found
out that the only case in which this can ever happen is when all pixels of the
source block get mapped to the same color value. But that case can be handled
better in any case, by just using the single-color lookup tables. I've
attached the new version of refine_block using that observation - it both
increases performance (a bit) and image quality, so it's pretty neat.
---
encode_alpha_block_DXT5: The only thing that shouldn't be obvious is the index
computation. This just uses some two's complement arithmetic and bit shuffling
to avoid computing
7 * (in_alpha - min_alpha) / (max_alpha - min_alpha)
which would be more expensive. (The extra calc with idx is just because of the
weird DXT color numbering).
---
Some more notes on the general flow:
The computation without dithering is just as I explained in the part about
refine_block:
1. Calc initial endpoints directly from block colors
2. Determine color indices for these endpoints
3. Optimize endpoints given color indices
4. Determine new color indices given optimized endpoints
With dithering, there's a twist: The first two steps are done using a version
of the block dithered to colors that are representable using 16-bit 565 RGB
values. I've found that this significantly improves visual quality with
dithering; if you don't do this, the colors inside a block typically vary too
little for dithering to be useful. This process decreases objective quality
but typically looks notably better.
The single-color match (omatch5/omatch6) trick: If the block only contains a
single color (or, for the improved version of refine_block, if the color
values are sufficiently close to all map to the same index), an optimal
solution can be used instead.
You normally want solid-color blocks to map to solid-color block (because
dithering patterns are very obvious otherwise). This means that all color
indices for the block are going to be identical, i.e. all 0, 1, 2 or 3. All-0
is symmetrical to all-1 (with the endpoints flipped), and all-2 is symmetrical
to all-3 (again with the endpoints flipped). So you only need to consider
all-0 or all-2 indices for the block. Furthermore, all-0 means that the first
endpoint specified in the block gets used for all pixels; you can get the same
result by setting both endpoints to the same value and using index 2 for
everything.
In short, you can always set all indices to 2 without sacrificing any quality
whatsoever. For any of the color components R,G,B, you then want to determine
5-bit or 6-bit values a and b such that
expand[a]*(2/3) + expand[b]*(1/3) is as close as possible to R/G/B
and that's exactly what's in omatch5 (for 5-bit values) and omatch6 (for 6-bit
values).
If you use the 3-color+transparency mode of DXT1, you need separate versions
for omatch5/omatch6 for this case, since the interpolated value is exactly
halfway between the two endpoints instead of 1/3 of the way along. But I
recommend against that mode, because even if your top-level mipmap has 1-bit
transparency, mipmaps will have more than 2 distinct values, and the DXT mode
is selected per texture. That's why my original code doesn't support the
3-color mode of DXT1 at all: I don't think it's useful in practice.
Not sure if all of this is useful to you or not, but I guess it might make
sense to at least put this in a separate text file or whatever, because
otherwise it's really quite hard to see what's going on in some places.
Cheers,
-Fabian "ryg" Giesen

7
plug-ins/file-dds/TODO Normal file
View File

@ -0,0 +1,7 @@
TODO list for future releases of pika-dds:
* Add support for DX10 DDS extensions
* BC6H and BC7 compression support
* Volume map compression support (VTC)
* Add support for PIKA 2.6.x GEGL for reading and writing higher precision
pixel formats

58
plug-ins/file-dds/color.c Normal file
View File

@ -0,0 +1,58 @@
/*
* DDS PIKA plugin
*
* Copyright (C) 2004-2012 Shawn Kirst <skirst@gmail.com>,
* with parts (C) 2003 Arne Reuter <homepage@arnereuter.de> where specified.
*
* 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; see the file COPYING. If not, write to
* the Free Software Foundation, 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301, USA.
*/
#include <math.h>
#include "color.h"
int
linear_to_sRGB(int c)
{
float v = (float)c / 255.0f;
if(v < 0)
v = 0;
else if(v > 1)
v = 1;
else if(v <= 0.0031308f)
v = 12.92f * v;
else
v = 1.055f * powf(v, 0.41666f) - 0.055f;
return (int)floorf(255.0f * v + 0.5f);
}
int
sRGB_to_linear(int c)
{
float v = (float)c / 255.0f;
if(v < 0)
v = 0;
else if(v > 1)
v = 1;
else if(v <= 0.04045f)
v /= 12.92f;
else
v = powf((v + 0.055f) / 1.055f, 2.4f);
return (int)floorf(255.0f * v + 0.5f);
}

96
plug-ins/file-dds/color.h Normal file
View File

@ -0,0 +1,96 @@
/*
* DDS PIKA plugin
*
* Copyright (C) 2004-2012 Shawn Kirst <skirst@gmail.com>,
* with parts (C) 2003 Arne Reuter <homepage@arnereuter.de> where specified.
*
* 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/>.
*/
#ifndef __COLOR_H__
#define __COLOR_H__
#include "imath.h"
/* sRGB encoding/decoding */
int linear_to_sRGB(int c);
int sRGB_to_linear(int c);
/* YCoCg encoding */
static inline void
RGB_to_YCoCg (unsigned char *dst, int r, int g, int b)
{
int y = ((r + (g << 1) + b) + 2) >> 2;
int co = ((((r << 1) - (b << 1)) + 2) >> 2) + 128;
int cg = (((-r + (g << 1) - b) + 2) >> 2) + 128;
dst[0] = 255;
dst[1] = (cg > 255 ? 255 : (cg < 0 ? 0 : cg));
dst[2] = (co > 255 ? 255 : (co < 0 ? 0 : co));
dst[3] = (y > 255 ? 255 : (y < 0 ? 0 : y));
}
/* other color conversions */
static inline int
rgb_to_luminance (int r, int g, int b)
{
/* ITU-R BT.709 luma coefficients, scaled by 256 */
return ((r * 54 + g * 182 + b * 20) + 128) >> 8;
}
static inline unsigned short
pack_r5g6b5 (int r, int g, int b)
{
return (mul8bit(r, 31) << 11) |
(mul8bit(g, 63) << 5) |
(mul8bit(b, 31) );
}
static inline unsigned short
pack_rgba4 (int r, int g, int b, int a)
{
return (mul8bit(a, 15) << 12) |
(mul8bit(r, 15) << 8) |
(mul8bit(g, 15) << 4) |
(mul8bit(b, 15) );
}
static inline unsigned short
pack_rgb5a1 (int r, int g, int b, int a)
{
return (((a >> 7) & 0x01) << 15) |
(mul8bit(r, 31) << 10) |
(mul8bit(g, 31) << 5) |
(mul8bit(b, 31) );
}
static inline unsigned char
pack_r3g3b2(int r, int g, int b)
{
return (mul8bit(r, 7) << 5) |
(mul8bit(g, 7) << 2) |
(mul8bit(b, 3) );
}
static inline unsigned int
pack_rgb10a2 (int r, int g, int b, int a)
{
return ((unsigned int)((a >> 6) & 0x003) << 30) |
((unsigned int)((b << 2) & 0x3ff) << 20) |
((unsigned int)((g << 2) & 0x3ff) << 10) |
((unsigned int)((r << 2) & 0x3ff) );
}
#endif /* __COLOR_H__ */

549
plug-ins/file-dds/dds.c Normal file
View File

@ -0,0 +1,549 @@
/*
* DDS PIKA plugin
*
* Copyright (C) 2004-2012 Shawn Kirst <skirst@gmail.com>,
* with parts (C) 2003 Arne Reuter <homepage@arnereuter.de> where specified.
*
* 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; see the file COPYING. If not, write to
* the Free Software Foundation, 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301, USA.
*/
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <gtk/gtk.h>
#include <libpika/pika.h>
#include <libpika/pikaui.h>
#include <libpika/stdplugins-intl.h>
#include "dds.h"
#include "ddsread.h"
#include "ddswrite.h"
#include "misc.h"
#define LOAD_PROC "file-dds-load"
#define SAVE_PROC "file-dds-save"
#define DECODE_YCOCG_PROC "color-decode-ycocg"
#define DECODE_YCOCG_SCALED_PROC "color-decode-ycocg-scaled"
#define DECODE_ALPHA_EXP_PROC "color-decode-alpha-exp"
typedef struct _Dds Dds;
typedef struct _DdsClass DdsClass;
struct _Dds
{
PikaPlugIn parent_instance;
};
struct _DdsClass
{
PikaPlugInClass parent_class;
};
#define DDS_TYPE (dds_get_type ())
#define DDS (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), DDS_TYPE, Dds))
GType dds_get_type (void) G_GNUC_CONST;
static GList * dds_query_procedures (PikaPlugIn *plug_in);
static PikaProcedure * dds_create_procedure (PikaPlugIn *plug_in,
const gchar *name);
static PikaValueArray * dds_load (PikaProcedure *procedure,
PikaRunMode run_mode,
GFile *file,
const PikaValueArray *args,
gpointer run_data);
static PikaValueArray * dds_save (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
GFile *file,
const PikaValueArray *args,
gpointer run_data);
#if 0
static PikaValueArray * dds_decode (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
const PikaValueArray *args,
gpointer run_data);
#endif
G_DEFINE_TYPE (Dds, dds, PIKA_TYPE_PLUG_IN)
PIKA_MAIN (DDS_TYPE)
DEFINE_STD_SET_I18N
static void
dds_class_init (DdsClass *klass)
{
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
plug_in_class->query_procedures = dds_query_procedures;
plug_in_class->create_procedure = dds_create_procedure;
plug_in_class->set_i18n = STD_SET_I18N;
}
static void
dds_init (Dds *dds)
{
}
static GList *
dds_query_procedures (PikaPlugIn *plug_in)
{
GList *list = NULL;
list = g_list_append (list, g_strdup (LOAD_PROC));
list = g_list_append (list, g_strdup (SAVE_PROC));
#if 0
list = g_list_append (list, g_strdup (DECODE_YCOCG_PROC));
list = g_list_append (list, g_strdup (DECODE_YCOCG_SCALED_PROC));
list = g_list_append (list, g_strdup (DECODE_ALPHA_EXP_PROC));
#endif
return list;
}
static PikaProcedure *
dds_create_procedure (PikaPlugIn *plug_in,
const gchar *name)
{
PikaProcedure *procedure = NULL;
if (! strcmp (name, LOAD_PROC))
{
procedure = pika_load_procedure_new (plug_in, name,
PIKA_PDB_PROC_TYPE_PLUGIN,
dds_load, NULL, NULL);
pika_procedure_set_menu_label (procedure, _("DDS image"));
pika_procedure_set_documentation (procedure,
_("Loads files in DDS image format"),
_("Loads files in DDS image format"),
name);
pika_procedure_set_attribution (procedure,
"Shawn Kirst",
"Shawn Kirst",
"2008");
pika_file_procedure_set_mime_types (PIKA_FILE_PROCEDURE (procedure),
"image/dds");
pika_file_procedure_set_extensions (PIKA_FILE_PROCEDURE (procedure),
"dds");
pika_file_procedure_set_magics (PIKA_FILE_PROCEDURE (procedure),
"0,string,DDS");
PIKA_PROC_ARG_BOOLEAN (procedure, "load-mipmaps",
_("Load _mipmaps"),
_("Load mipmaps if present"),
TRUE,
G_PARAM_READWRITE);
PIKA_PROC_ARG_BOOLEAN (procedure, "decode-images",
_("Automatically decode YCoCg/AE_xp images when detected"),
_("Decode YCoCg/AExp images when detected"),
TRUE,
G_PARAM_READWRITE);
}
else if (! strcmp (name, SAVE_PROC))
{
procedure = pika_save_procedure_new (plug_in, name,
PIKA_PDB_PROC_TYPE_PLUGIN,
dds_save, NULL, NULL);
pika_procedure_set_image_types (procedure, "INDEXED, GRAY, RGB");
pika_procedure_set_menu_label (procedure, _("DDS image"));
pika_procedure_set_documentation (procedure,
"Saves files in DDS image format",
"Saves files in DDS image format",
name);
pika_procedure_set_attribution (procedure,
"Shawn Kirst",
"Shawn Kirst",
"2008");
pika_file_procedure_set_mime_types (PIKA_FILE_PROCEDURE (procedure),
"image/dds");
pika_file_procedure_set_extensions (PIKA_FILE_PROCEDURE (procedure),
"dds");
PIKA_PROC_ARG_INT (procedure, "compression-format",
"Compression format",
"Compression format (0 = None, 1 = BC1/DXT1, "
"2 = BC2/DXT3, 3 = BC3/DXT5, 4 = BC3n/DXT5nm, "
"5 = BC4/ATI1N, 6 = BC5/ATI2N, 7 = RXGB (DXT5), "
"8 = Alpha Exponent (DXT5), 9 = YCoCg (DXT5), "
"10 = YCoCg scaled (DXT5))",
0, 10, DDS_COMPRESS_NONE,
G_PARAM_READWRITE);
PIKA_PROC_ARG_INT (procedure, "mipmaps",
"Mipmaps",
"How to handle mipmaps (0 = No mipmaps, "
"1 = Generate mipmaps, "
"2 = Use existing mipmaps (layers)",
0, 2, DDS_MIPMAP_NONE,
G_PARAM_READWRITE);
PIKA_PROC_ARG_INT (procedure, "save-type",
"Save type",
"How to save the image (0 = selected layer, "
"1 = cube map, 2 = volume map, 3 = texture array, "
"4 = all visible layers)",
0, 4, DDS_SAVE_SELECTED_LAYER,
G_PARAM_READWRITE);
PIKA_PROC_ARG_INT (procedure, "format",
"Format",
"Pixel format (0 = default, 1 = DDS_FORMAT_RGB8, "
"2 = DDS_FORMAT_RGBA8, 3 = DDS_FORMAT_BGR8, "
"4 = DDS_FORMAT_ABGR8, 5 = DDS_FORMAT_R5G6B5, "
"6 = DDS_FORMAT_RGBA4, 7 = DDS_FORMAT_RGB5A1, "
"8 = DDS_FORMAT_RGB10A2, 9 = DDS_FORMAT_R3G3B2, "
"10 = DDS_FORMAT_A8, 11 = DDS_FORMAT_L8, "
"12 = DDS_FORMAT_L8A8, 13 = DDS_FORMAT_AEXP, "
"14 = DDS_FORMAT_YCOCG)",
0, 14, DDS_FORMAT_DEFAULT,
G_PARAM_READWRITE);
PIKA_PROC_ARG_BOOLEAN (procedure, "flip-image",
"Flip image vertically",
"Flip the image vertically on export",
FALSE,
G_PARAM_READWRITE);
PIKA_PROC_ARG_BOOLEAN (procedure, "transparent-color",
"Transparent color",
"Make an indexed color transparent",
FALSE,
G_PARAM_READWRITE);
PIKA_PROC_ARG_INT (procedure, "transparent-index",
"Transparent index",
"Index of transparent color or -1 to disable "
"(for indexed images only).",
0, 255, 0,
G_PARAM_READWRITE);
PIKA_PROC_ARG_INT (procedure, "mipmap-filter",
"Mipmap filter",
"Filtering to use when generating mipmaps "
"(0 = default, 1 = nearest, 2 = box, 3 = triangle, "
"4 = quadratic, 5 = bspline, 6 = mitchell, "
"7 = lanczos, 8 = kaiser)",
0, 8, DDS_MIPMAP_FILTER_DEFAULT,
G_PARAM_READWRITE);
PIKA_PROC_ARG_INT (procedure, "mipmap-wrap",
"Mipmap wrap",
"Wrap mode to use when generating mipmaps "
"(0 = default, 1 = mirror, 2 = repeat, 3 = clamp)",
0, 3, DDS_MIPMAP_WRAP_DEFAULT,
G_PARAM_READWRITE);
PIKA_PROC_ARG_BOOLEAN (procedure, "gamma-correct",
"Gamma correct",
"Use gamma correct mipmap filtering",
FALSE,
G_PARAM_READWRITE);
PIKA_PROC_ARG_BOOLEAN (procedure, "srgb",
"sRGB",
"Use sRGB colorspace for gamma correction",
FALSE,
G_PARAM_READWRITE);
PIKA_PROC_ARG_DOUBLE (procedure, "gamma",
"Gamma",
"Gamma value to use for gamma correction (i.e. 2.2)",
0.0, 10.0, 0.0,
G_PARAM_READWRITE);
PIKA_PROC_ARG_BOOLEAN (procedure, "perceptual-metric",
"Perceptual metric",
"Use a perceptual error metric during compression",
FALSE,
G_PARAM_READWRITE);
PIKA_PROC_ARG_BOOLEAN (procedure, "preserve-alpha-coverage",
"Preserve alpha coverage",
"Preserve alpha test converage for alpha "
"channel maps",
FALSE,
G_PARAM_READWRITE);
PIKA_PROC_ARG_DOUBLE (procedure, "alpha-test-threshold",
"Alpha test threshold",
"Alpha test threshold value for which alpha test "
"converage should be preserved",
0.0, 1.0, 0.5,
G_PARAM_READWRITE);
}
#if 0
else if (! strcmp (name, DECODE_YCOCG_PROC))
{
procedure = pika_image_procedure_new (plug_in, name,
PIKA_PDB_PROC_TYPE_PLUGIN,
dds_decode, NULL, NULL);
pika_procedure_set_image_types (procedure, "RGBA");
pika_procedure_set_sensitivity_mask (procedure,
PIKA_PROCEDURE_SENSITIVE_DRAWABLE);
pika_procedure_set_menu_label (procedure, _("Decode YCoCg"));
/* pika_procedure_add_menu_path (procedure, "<Image>/Filters/Colors"); */
pika_procedure_set_documentation (procedure,
_("Converts YCoCg encoded pixels to RGB"),
_("Converts YCoCg encoded pixels to RGB"),
name);
pika_procedure_set_attribution (procedure,
"Shawn Kirst",
"Shawn Kirst",
"2008");
}
else if (! strcmp (name, DECODE_YCOCG_SCALED_PROC))
{
procedure = pika_image_procedure_new (plug_in, name,
PIKA_PDB_PROC_TYPE_PLUGIN,
dds_decode, NULL, NULL);
pika_procedure_set_image_types (procedure, "RGBA");
pika_procedure_set_sensitivity_mask (procedure,
PIKA_PROCEDURE_SENSITIVE_DRAWABLE);
pika_procedure_set_menu_label (procedure, _("Decode YCoCg (scaled)"));
/* pika_procedure_add_menu_path (procedure, "<Image>/Filters/Colors"); */
pika_procedure_set_documentation (procedure,
_("Converts YCoCg (scaled) encoded "
"pixels to RGB"),
_("Converts YCoCg (scaled) encoded "
"pixels to RGB"),
name);
pika_procedure_set_attribution (procedure,
"Shawn Kirst",
"Shawn Kirst",
"2008");
}
else if (! strcmp (name, DECODE_ALPHA_EXP_PROC))
{
procedure = pika_image_procedure_new (plug_in, name,
PIKA_PDB_PROC_TYPE_PLUGIN,
dds_decode, NULL, NULL);
pika_procedure_set_image_types (procedure, "RGBA");
pika_procedure_set_sensitivity_mask (procedure,
PIKA_PROCEDURE_SENSITIVE_DRAWABLE);
pika_procedure_set_menu_label (procedure, _("Decode Alpha exponent"));
/* pika_procedure_add_menu_path (procedure, "<Image>/Filters/Colors"); */
pika_procedure_set_documentation (procedure,
_("Converts alpha exponent encoded "
"pixels to RGB",
_("Converts alpha exponent encoded "
"pixels to RGB"),
name);
pika_procedure_set_attribution (procedure,
"Shawn Kirst",
"Shawn Kirst",
"2008");
}
#endif
return procedure;
}
static PikaValueArray *
dds_load (PikaProcedure *procedure,
PikaRunMode run_mode,
GFile *file,
const PikaValueArray *args,
gpointer run_data)
{
PikaProcedureConfig *config;
PikaValueArray *return_vals;
PikaPDBStatusType status;
PikaImage *image;
GError *error = NULL;
gegl_init (NULL, NULL);
config = pika_procedure_create_config (procedure);
pika_procedure_config_begin_run (config, NULL, run_mode, args);
status = read_dds (file, &image, run_mode == PIKA_RUN_INTERACTIVE,
procedure, G_OBJECT (config), &error);
pika_procedure_config_end_run (config, status);
g_object_unref (config);
if (status != PIKA_PDB_SUCCESS)
return pika_procedure_new_return_values (procedure, status, error);
return_vals = pika_procedure_new_return_values (procedure,
PIKA_PDB_SUCCESS,
NULL);
PIKA_VALUES_SET_IMAGE (return_vals, 1, image);
return return_vals;
}
static PikaValueArray *
dds_save (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
GFile *file,
const PikaValueArray *args,
gpointer run_data)
{
PikaProcedureConfig *config;
PikaPDBStatusType status = PIKA_PDB_SUCCESS;
PikaExportReturn export = PIKA_EXPORT_CANCEL;
GError *error = NULL;
gdouble gamma;
gegl_init (NULL, NULL);
config = pika_procedure_create_config (procedure);
pika_procedure_config_begin_run (config, NULL, run_mode, args);
switch (run_mode)
{
case PIKA_RUN_INTERACTIVE:
case PIKA_RUN_WITH_LAST_VALS:
pika_ui_init ("dds");
export = pika_export_image (&image, &n_drawables, &drawables, "DDS",
PIKA_EXPORT_CAN_HANDLE_RGB |
PIKA_EXPORT_CAN_HANDLE_GRAY |
PIKA_EXPORT_CAN_HANDLE_INDEXED |
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);
break;
default:
break;
}
g_object_get (config,
"gamma", &gamma,
NULL);
/* pika_gamma () got removed and was always returning 2.2 anyway.
* XXX Review this piece of code if we expect gamma value could be
* parameterized.
*/
if (gamma < 1e-04f)
g_object_set (config,
"gamma", 2.2,
NULL);
/* TODO: support multiple-layers selection, especially as DDS has
* DDS_SAVE_SELECTED_LAYER option support.
*/
status = write_dds (file, image, drawables[0],
run_mode == PIKA_RUN_INTERACTIVE,
procedure, G_OBJECT (config),
export == PIKA_EXPORT_EXPORT);
if (export == PIKA_EXPORT_EXPORT)
{
pika_image_delete (image);
g_free (drawables);
}
pika_procedure_config_end_run (config, status);
g_object_unref (config);
return pika_procedure_new_return_values (procedure, status, error);
}
#if 0
static PikaValueArray *
dds_decode (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
const PikaValueArray *args,
gpointer run_data)
{
const gchar *name = pika_procedure_get_name (procedure);
PikaDrawable *drawable,
if (n_drawables != 1)
{
GError *error = NULL;
g_set_error (&error, PIKA_PLUG_IN_ERROR, 0,
_("Procedure '%s' only works with one drawable."),
name);
return pika_procedure_new_return_values (procedure,
PIKA_PDB_EXECUTION_ERROR,
error);
}
else
{
drawable = drawables[0];
}
if (! strcmp (name, DECODE_YCOCG_PROC))
{
decode_ycocg_image (drawable, TRUE);
}
else if (! strcmp (name, DECODE_YCOCG_SCALED_PROC))
{
decode_ycocg_scaled_image (drawable, TRUE);
}
else if (! strcmp (name, DECODE_ALPHA_EXP_PROC))
{
decode_alpha_exp_image (drawable, TRUE);
}
if (run_mode != PIKA_RUN_NONINTERACTIVE)
pika_displays_flush ();
return pika_procedure_new_return_values (procedure, PIKA_PDB_SUCCESS, NULL);
}
#endif

Some files were not shown because too many files have changed in this diff Show More