Initial checkin of Pika from heckimp
This commit is contained in:
@@ -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
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
¶m);
|
||||
}
|
||||
|
||||
pika_preview_draw_buffer (preview, buffer, width * bpp);
|
||||
g_free (buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
GeglBuffer *buffer;
|
||||
GeglBufferIterator *iter;
|
||||
gint x, y, w, h;
|
||||
gint progress_total;
|
||||
gint progress_done = 0;
|
||||
|
||||
if (! pika_drawable_mask_intersect (drawable, &x, &y, &w, &h))
|
||||
return;
|
||||
|
||||
progress_total = w * h;
|
||||
|
||||
pika_progress_init (_("Checkerboard"));
|
||||
|
||||
buffer = pika_drawable_get_shadow_buffer (drawable);
|
||||
|
||||
iter = gegl_buffer_iterator_new (buffer,
|
||||
GEGL_RECTANGLE (x, y, w, h), 0,
|
||||
format,
|
||||
GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE, 1);
|
||||
|
||||
while (gegl_buffer_iterator_next (iter))
|
||||
{
|
||||
GeglRectangle roi = iter->items[0].roi;
|
||||
guchar *dest = iter->items[0].data;
|
||||
guchar *d;
|
||||
gint y1, x1;
|
||||
|
||||
d = dest;
|
||||
|
||||
for (y1 = 0; y1 < roi.height; y1++)
|
||||
{
|
||||
for (x1 = 0; x1 < roi.width; x1++)
|
||||
{
|
||||
checkerboard_func (roi.x + x1,
|
||||
roi.y + y1,
|
||||
d + x1 * bpp,
|
||||
bpp, size, mode,
|
||||
¶m);
|
||||
}
|
||||
|
||||
d += roi.width * bpp;
|
||||
}
|
||||
|
||||
progress_done += roi.width * roi.height;
|
||||
pika_progress_update ((gdouble) progress_done /
|
||||
(gdouble) progress_total);
|
||||
}
|
||||
|
||||
g_object_unref (buffer);
|
||||
|
||||
pika_progress_update (1.0);
|
||||
|
||||
pika_drawable_merge_shadow (drawable, TRUE);
|
||||
pika_drawable_update (drawable, x, y, w, h);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
do_checkerboard_preview (GtkWidget *widget,
|
||||
GObject *config)
|
||||
{
|
||||
PikaPreview *preview = PIKA_PREVIEW (widget);
|
||||
PikaDrawable *drawable = g_object_get_data (config, "drawable");
|
||||
|
||||
do_checkerboard_pattern (config, drawable, preview);
|
||||
}
|
||||
|
||||
static gint
|
||||
inblock (gint pos,
|
||||
gint size)
|
||||
{
|
||||
static gint *in = NULL; /* initialized first time */
|
||||
static gint len = -1;
|
||||
|
||||
/* avoid a FP exception */
|
||||
if (size == 1)
|
||||
size = 2;
|
||||
|
||||
if (in && len != size * size)
|
||||
{
|
||||
g_free (in);
|
||||
in = NULL;
|
||||
}
|
||||
len = size * size;
|
||||
|
||||
/* Initialize the array; since we'll be called thousands of
|
||||
* times with the same size value, precompute the array.
|
||||
*/
|
||||
if (in == NULL)
|
||||
{
|
||||
gint cell = 1; /* cell value */
|
||||
gint i, j, k;
|
||||
|
||||
in = g_new (gint, len);
|
||||
|
||||
/* i is absolute index into in[]
|
||||
* j is current number of blocks to fill in with a 1 or 0.
|
||||
* k is just counter for the j cells.
|
||||
*/
|
||||
i = 0;
|
||||
for (j = 1; j <= size; j++)
|
||||
{ /* first half */
|
||||
for (k = 0; k < j; k++)
|
||||
{
|
||||
in[i++] = cell;
|
||||
}
|
||||
cell = !cell;
|
||||
}
|
||||
for (j = size - 1; j >= 1; j--)
|
||||
{ /* second half */
|
||||
for (k = 0; k < j; k++)
|
||||
{
|
||||
in[i++] = cell;
|
||||
}
|
||||
cell = !cell;
|
||||
}
|
||||
}
|
||||
|
||||
/* place pos within 0..(len-1) grid and return the value. */
|
||||
return in[pos % (len - 1)];
|
||||
}
|
||||
|
||||
static gboolean
|
||||
checkerboard_dialog (PikaProcedure *procedure,
|
||||
GObject *config,
|
||||
PikaImage *image,
|
||||
PikaDrawable *drawable)
|
||||
{
|
||||
GtkWidget *dialog;
|
||||
GtkWidget *preview;
|
||||
GtkWidget *toggle;
|
||||
GtkWidget *size_entry;
|
||||
gint size, width, height;
|
||||
gdouble xres;
|
||||
gdouble yres;
|
||||
gboolean run;
|
||||
|
||||
pika_ui_init (PLUG_IN_BINARY);
|
||||
|
||||
dialog = pika_procedure_dialog_new (procedure,
|
||||
PIKA_PROCEDURE_CONFIG (config),
|
||||
_("Checkerboard"));
|
||||
|
||||
pika_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
|
||||
GTK_RESPONSE_OK,
|
||||
GTK_RESPONSE_CANCEL,
|
||||
-1);
|
||||
|
||||
pika_window_set_transient (GTK_WINDOW (dialog));
|
||||
|
||||
preview = pika_drawable_preview_new_from_drawable (drawable);
|
||||
gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
|
||||
preview, TRUE, TRUE, 0);
|
||||
gtk_widget_show (preview);
|
||||
|
||||
/* Get the image resolution and unit */
|
||||
pika_image_get_resolution (image, &xres, &yres);
|
||||
|
||||
width = pika_drawable_get_width (drawable);
|
||||
height = pika_drawable_get_height (drawable);
|
||||
size = MIN (width, height);
|
||||
|
||||
size_entry =
|
||||
pika_procedure_dialog_get_size_entry (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"check-size", TRUE,
|
||||
"check-size-unit", "%a",
|
||||
PIKA_SIZE_ENTRY_UPDATE_SIZE,
|
||||
xres);
|
||||
gtk_widget_set_margin_bottom (size_entry, 12);
|
||||
|
||||
/* set the size (in pixels) that will be treated as 0% and 100% */
|
||||
pika_size_entry_set_size (PIKA_SIZE_ENTRY (size_entry), 0, 0.0, size);
|
||||
|
||||
/* set upper and lower limits (in pixels) */
|
||||
pika_size_entry_set_refval_boundaries (PIKA_SIZE_ENTRY (size_entry), 0,
|
||||
1.0, size);
|
||||
|
||||
toggle = pika_procedure_dialog_get_widget (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"psychobily",
|
||||
GTK_TYPE_CHECK_BUTTON);
|
||||
gtk_widget_set_margin_bottom (toggle, 12);
|
||||
|
||||
g_object_set_data (config, "drawable", drawable);
|
||||
|
||||
g_signal_connect (preview, "invalidated",
|
||||
G_CALLBACK (do_checkerboard_preview),
|
||||
config);
|
||||
|
||||
g_signal_connect_swapped (config, "notify",
|
||||
G_CALLBACK (pika_preview_invalidate),
|
||||
preview);
|
||||
|
||||
pika_procedure_dialog_fill (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"check-size", "psychobily",
|
||||
NULL);
|
||||
|
||||
gtk_widget_show (dialog);
|
||||
|
||||
run = pika_procedure_dialog_run (PIKA_PROCEDURE_DIALOG (dialog));
|
||||
|
||||
gtk_widget_destroy (dialog);
|
||||
|
||||
return run;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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 (§ion);
|
||||
|
||||
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 (§ion);
|
||||
|
||||
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 });
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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 */
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
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
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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, ¶site_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, ¶site_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, ¶site_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, ¶site_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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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."),
|
||||
" ",
|
||||
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]);
|
||||
}
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
}
|
||||
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
@@ -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;
|
||||
}
|
||||
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
File diff suppressed because it is too large
Load Diff
@@ -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
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
}
|
||||
|
||||
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
@@ -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;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
@@ -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
|
||||
@@ -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);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
@@ -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));
|
||||
}
|
||||
@@ -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' }
|
||||
);
|
||||
@@ -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);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
}
|
||||
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
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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__ */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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__ */
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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__ */
|
||||
@@ -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,
|
||||
)
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
*/
|
||||
@@ -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.
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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__ */
|
||||
@@ -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
Reference in New Issue
Block a user