Initial checkin of Pika from heckimp
This commit is contained in:
797
plug-ins/common/align-layers.c
Normal file
797
plug-ins/common/align-layers.c
Normal file
@ -0,0 +1,797 @@
|
||||
/* align_layers.c
|
||||
* Author: Shuji Narazaki <narazaki@InetQ.or.jp>
|
||||
* Version: 0.26
|
||||
*
|
||||
* Copyright (C) 1997-1998 Shuji Narazaki <narazaki@InetQ.or.jp>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <libpika/pika.h>
|
||||
#include <libpika/pikaui.h>
|
||||
|
||||
#include "libpika/stdplugins-intl.h"
|
||||
|
||||
#define PLUG_IN_PROC "plug-in-align-layers"
|
||||
#define PLUG_IN_BINARY "align-layers"
|
||||
#define PLUG_IN_ROLE "pika-align-layers"
|
||||
|
||||
enum
|
||||
{
|
||||
H_NONE,
|
||||
H_COLLECT,
|
||||
LEFT2RIGHT,
|
||||
RIGHT2LEFT,
|
||||
SNAP2HGRID
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
H_BASE_LEFT,
|
||||
H_BASE_CENTER,
|
||||
H_BASE_RIGHT
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
V_NONE,
|
||||
V_COLLECT,
|
||||
TOP2BOTTOM,
|
||||
BOTTOM2TOP,
|
||||
SNAP2VGRID
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
V_BASE_TOP,
|
||||
V_BASE_CENTER,
|
||||
V_BASE_BOTTOM
|
||||
};
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
gint step_x;
|
||||
gint step_y;
|
||||
gint base_x;
|
||||
gint base_y;
|
||||
} AlignData;
|
||||
|
||||
typedef struct _AlignLayers AlignLayers;
|
||||
typedef struct _AlignLayersClass AlignLayersClass;
|
||||
|
||||
struct _AlignLayers
|
||||
{
|
||||
PikaPlugIn parent_instance;
|
||||
};
|
||||
|
||||
struct _AlignLayersClass
|
||||
{
|
||||
PikaPlugInClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
#define ALIGN_LAYERS_TYPE (align_layers_get_type ())
|
||||
#define ALIGN_LAYERS (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ALIGN_LAYERS_TYPE, AlignLayers))
|
||||
|
||||
GType align_layers_get_type (void) G_GNUC_CONST;
|
||||
|
||||
static GList * align_layers_query_procedures (PikaPlugIn *plug_in);
|
||||
static PikaProcedure * align_layers_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name);
|
||||
|
||||
static PikaValueArray * align_layers_run (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data);
|
||||
|
||||
|
||||
/* Main function */
|
||||
static PikaPDBStatusType align_layers (PikaImage *image,
|
||||
GObject *config);
|
||||
|
||||
/* Helpers and internal functions */
|
||||
static gint align_layers_count_visibles_layers (GList *layers);
|
||||
static PikaLayer * align_layers_find_last_layer (GList *layers,
|
||||
gboolean *found);
|
||||
static gint align_layers_spread_visibles_layers (GList *layers,
|
||||
PikaLayer **layers_array);
|
||||
static PikaLayer ** align_layers_spread_image (PikaImage *image,
|
||||
gint *layer_num);
|
||||
static PikaLayer * align_layers_find_background (PikaImage *image);
|
||||
static AlignData align_layers_gather_data (PikaLayer **layers,
|
||||
gint layer_num,
|
||||
PikaLayer *background,
|
||||
GObject *config);
|
||||
static void align_layers_perform_alignment (PikaLayer **layers,
|
||||
gint layer_num,
|
||||
AlignData data,
|
||||
GObject *config);
|
||||
static void align_layers_get_align_offsets (PikaDrawable *drawable,
|
||||
gint *x,
|
||||
gint *y,
|
||||
GObject *config);
|
||||
static gint align_layers_dialog (PikaProcedure *procedure,
|
||||
GObject *config);
|
||||
|
||||
|
||||
G_DEFINE_TYPE (AlignLayers, align_layers, PIKA_TYPE_PLUG_IN)
|
||||
|
||||
PIKA_MAIN (ALIGN_LAYERS_TYPE)
|
||||
DEFINE_STD_SET_I18N
|
||||
|
||||
|
||||
static void
|
||||
align_layers_class_init (AlignLayersClass *klass)
|
||||
{
|
||||
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
|
||||
|
||||
plug_in_class->query_procedures = align_layers_query_procedures;
|
||||
plug_in_class->create_procedure = align_layers_create_procedure;
|
||||
plug_in_class->set_i18n = STD_SET_I18N;
|
||||
}
|
||||
|
||||
static void
|
||||
align_layers_init (AlignLayers *film)
|
||||
{
|
||||
}
|
||||
|
||||
static GList *
|
||||
align_layers_query_procedures (PikaPlugIn *plug_in)
|
||||
{
|
||||
return g_list_append (NULL, g_strdup (PLUG_IN_PROC));
|
||||
}
|
||||
|
||||
static PikaProcedure *
|
||||
align_layers_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name)
|
||||
{
|
||||
PikaProcedure *procedure = NULL;
|
||||
|
||||
if (! strcmp (name, PLUG_IN_PROC))
|
||||
{
|
||||
procedure = pika_image_procedure_new (plug_in, name,
|
||||
PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
align_layers_run, NULL, NULL);
|
||||
|
||||
pika_procedure_set_image_types (procedure, "*");
|
||||
pika_procedure_set_sensitivity_mask (procedure,
|
||||
PIKA_PROCEDURE_SENSITIVE_DRAWABLE |
|
||||
PIKA_PROCEDURE_SENSITIVE_DRAWABLES |
|
||||
PIKA_PROCEDURE_SENSITIVE_NO_DRAWABLES);
|
||||
|
||||
pika_procedure_set_menu_label (procedure, _("Align Visi_ble Layers..."));
|
||||
pika_procedure_add_menu_path (procedure, "<Image>/Image/[Arrange]");
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
_("Align all visible layers of the image"),
|
||||
"Align visible layers",
|
||||
name);
|
||||
pika_procedure_set_attribution (procedure,
|
||||
"Shuji Narazaki <narazaki@InetQ.or.jp>",
|
||||
"Shuji Narazaki",
|
||||
"1997");
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "horizontal-style",
|
||||
_("_Horizontal style"),
|
||||
_("(None = 0, Collect = 1, "
|
||||
"Fill left to right = 2, Fill right to left = 3, "
|
||||
"Snap to grid = 4)"),
|
||||
H_NONE, SNAP2HGRID, H_NONE,
|
||||
PIKA_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "horizontal-base",
|
||||
_("Hori_zontal base"),
|
||||
_("(Left edge = 0, Center = 1, "
|
||||
"Right edge = 2)"),
|
||||
H_BASE_LEFT, H_BASE_RIGHT, H_BASE_LEFT,
|
||||
PIKA_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "vertical-style",
|
||||
_("_Vertical style"),
|
||||
_("(None = 0, Collect = 1, "
|
||||
"Fill left to right = 2, Fill right to left = 3, "
|
||||
"Snap to grid = 4)"),
|
||||
V_NONE, SNAP2VGRID, V_NONE,
|
||||
PIKA_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "vertical-base",
|
||||
_("Ver_tical base"),
|
||||
_("(Left edge = 0, Center = 1, "
|
||||
"Right edge = 2)"),
|
||||
V_BASE_TOP, V_BASE_BOTTOM, V_BASE_TOP,
|
||||
PIKA_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "grid-size",
|
||||
_("_Grid"),
|
||||
_("Grid"),
|
||||
1, 200, 10,
|
||||
PIKA_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_BOOLEAN (procedure,
|
||||
"ignore-bottom-layer",
|
||||
_("Ignore the _bottom layer even if visible"),
|
||||
_("Ignore the bottom layer even if visible"),
|
||||
TRUE,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_BOOLEAN (procedure,
|
||||
"use-bottom-layer",
|
||||
_("_Use the (invisible) bottom layer as the base"),
|
||||
_("Use the (invisible) bottom layer as the base"),
|
||||
FALSE,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
/* TODO: these 2 arguments existed in the original procedure but
|
||||
* were actually unused. If we want to keep them, let's at least
|
||||
* implement the documented behavior, or else let's just drop
|
||||
* them.
|
||||
*/
|
||||
/*
|
||||
PIKA_PROC_ARG_BOOLEAN (procedure,
|
||||
"link-after-alignment",
|
||||
"Link the visible layers after alignment",
|
||||
FALSE,
|
||||
G_PARAM_READWRITE);
|
||||
PIKA_PROC_ARG_BOOLEAN (procedure,
|
||||
"use-bottom",
|
||||
"use the bottom layer as the base of alignment",
|
||||
TRUE,
|
||||
G_PARAM_READWRITE);
|
||||
*/
|
||||
}
|
||||
|
||||
return procedure;
|
||||
}
|
||||
|
||||
static PikaValueArray *
|
||||
align_layers_run (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data)
|
||||
{
|
||||
PikaProcedureConfig *config;
|
||||
PikaValueArray *return_vals = NULL;
|
||||
PikaPDBStatusType status = PIKA_PDB_EXECUTION_ERROR;
|
||||
GError *error = NULL;
|
||||
GList *layers;
|
||||
gint layer_num;
|
||||
|
||||
config = pika_procedure_create_config (procedure);
|
||||
pika_procedure_config_begin_run (config, NULL, run_mode, args);
|
||||
|
||||
switch ( run_mode )
|
||||
{
|
||||
case PIKA_RUN_INTERACTIVE:
|
||||
layers = pika_image_list_layers (image);
|
||||
layer_num = align_layers_count_visibles_layers (layers);
|
||||
g_list_free (layers);
|
||||
if (layer_num < 2)
|
||||
{
|
||||
g_set_error (&error, PIKA_PLUG_IN_ERROR, 0,
|
||||
_("There are not enough layers to align."));
|
||||
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_CALLING_ERROR,
|
||||
error);
|
||||
}
|
||||
|
||||
if (! align_layers_dialog (procedure, G_OBJECT (config)))
|
||||
status = PIKA_PDB_CANCEL;
|
||||
break;
|
||||
|
||||
case PIKA_RUN_NONINTERACTIVE:
|
||||
case PIKA_RUN_WITH_LAST_VALS:
|
||||
break;
|
||||
}
|
||||
|
||||
if (status != PIKA_PDB_CANCEL)
|
||||
{
|
||||
status = align_layers (image, G_OBJECT (config));
|
||||
|
||||
if (run_mode != PIKA_RUN_NONINTERACTIVE)
|
||||
pika_displays_flush ();
|
||||
}
|
||||
|
||||
pika_procedure_config_end_run (config, status);
|
||||
g_object_unref (config);
|
||||
|
||||
return_vals = pika_procedure_new_return_values (procedure, status,
|
||||
error);
|
||||
|
||||
return return_vals;
|
||||
}
|
||||
|
||||
/*
|
||||
* Main function
|
||||
*/
|
||||
static PikaPDBStatusType
|
||||
align_layers (PikaImage *image,
|
||||
GObject *config)
|
||||
{
|
||||
gint layer_num = 0;
|
||||
PikaLayer **layers = NULL;
|
||||
PikaLayer *background = 0;
|
||||
AlignData data;
|
||||
gboolean ignore_bottom_layer;
|
||||
|
||||
g_object_get (config,
|
||||
"ignore-bottom-layer", &ignore_bottom_layer,
|
||||
NULL);
|
||||
|
||||
layers = align_layers_spread_image (image, &layer_num);
|
||||
if (layer_num < 2)
|
||||
{
|
||||
g_free (layers);
|
||||
return PIKA_PDB_EXECUTION_ERROR;
|
||||
}
|
||||
|
||||
background = align_layers_find_background (image);
|
||||
|
||||
/* If we want to ignore the bottom layer and if it's visible */
|
||||
if (ignore_bottom_layer && background == layers[layer_num - 1])
|
||||
{
|
||||
layer_num--;
|
||||
}
|
||||
|
||||
data = align_layers_gather_data (layers,
|
||||
layer_num,
|
||||
background,
|
||||
config);
|
||||
|
||||
pika_image_undo_group_start (image);
|
||||
|
||||
align_layers_perform_alignment (layers,
|
||||
layer_num,
|
||||
data,
|
||||
config);
|
||||
|
||||
pika_image_undo_group_end (image);
|
||||
|
||||
g_free (layers);
|
||||
|
||||
return PIKA_PDB_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the bottommost layer, visible or not
|
||||
* The image must contain at least one layer.
|
||||
*/
|
||||
static PikaLayer *
|
||||
align_layers_find_last_layer (GList *layers,
|
||||
gboolean *found)
|
||||
{
|
||||
GList *last = g_list_last (layers);
|
||||
|
||||
for (; last; last = last->prev)
|
||||
{
|
||||
PikaItem *item = last->data;
|
||||
|
||||
if (pika_item_is_group (item))
|
||||
{
|
||||
GList *children;
|
||||
PikaLayer *last_layer;
|
||||
|
||||
children = pika_item_list_children (item);
|
||||
last_layer = align_layers_find_last_layer (children,
|
||||
found);
|
||||
g_list_free (children);
|
||||
if (*found)
|
||||
return last_layer;
|
||||
}
|
||||
else if (pika_item_is_layer (item))
|
||||
{
|
||||
*found = TRUE;
|
||||
return PIKA_LAYER (item);
|
||||
}
|
||||
}
|
||||
|
||||
/* should never happen */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the bottom layer.
|
||||
*/
|
||||
static PikaLayer *
|
||||
align_layers_find_background (PikaImage *image)
|
||||
{
|
||||
GList *layers;
|
||||
PikaLayer *background;
|
||||
gboolean found = FALSE;
|
||||
|
||||
layers = pika_image_list_layers (image);
|
||||
background = align_layers_find_last_layer (layers, &found);
|
||||
g_list_free (layers);
|
||||
|
||||
return background;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fill layers_array with all visible layers.
|
||||
* layers_array needs to be allocated before the call
|
||||
*/
|
||||
static gint
|
||||
align_layers_spread_visibles_layers (GList *layers,
|
||||
PikaLayer **layers_array)
|
||||
{
|
||||
GList *iter;
|
||||
gint index = 0;
|
||||
|
||||
for (iter = layers; iter; iter = iter->next)
|
||||
{
|
||||
PikaItem *item = iter->data;
|
||||
|
||||
if (pika_item_get_visible (item))
|
||||
{
|
||||
if (pika_item_is_group (item))
|
||||
{
|
||||
GList *children;
|
||||
|
||||
children = pika_item_list_children (item);
|
||||
index += align_layers_spread_visibles_layers (children,
|
||||
&(layers_array[index]));
|
||||
g_list_free (children);
|
||||
}
|
||||
else if (pika_item_is_layer (item))
|
||||
{
|
||||
layers_array[index] = PIKA_LAYER (item);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return a contiguous array of all visible layers
|
||||
*/
|
||||
static PikaLayer **
|
||||
align_layers_spread_image (PikaImage *image,
|
||||
gint *layer_num)
|
||||
{
|
||||
GList *layers;
|
||||
PikaLayer **layers_array;
|
||||
|
||||
layers = pika_image_list_layers (image);
|
||||
*layer_num = align_layers_count_visibles_layers (layers);
|
||||
|
||||
layers_array = g_malloc (sizeof (PikaLayer *) * *layer_num);
|
||||
|
||||
align_layers_spread_visibles_layers (layers,
|
||||
layers_array);
|
||||
g_list_free (layers);
|
||||
|
||||
return layers_array;
|
||||
}
|
||||
|
||||
static gint
|
||||
align_layers_count_visibles_layers (GList *layers)
|
||||
{
|
||||
GList *iter;
|
||||
gint count = 0;
|
||||
|
||||
for (iter = layers; iter; iter = iter->next)
|
||||
{
|
||||
PikaItem *item = iter->data;
|
||||
|
||||
if (pika_item_get_visible (item))
|
||||
{
|
||||
if (pika_item_is_group (item))
|
||||
{
|
||||
GList *children;
|
||||
|
||||
children = pika_item_list_children (item);
|
||||
count += align_layers_count_visibles_layers (children);
|
||||
g_list_free (children);
|
||||
}
|
||||
else if (pika_item_is_layer (item))
|
||||
{
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static AlignData
|
||||
align_layers_gather_data (PikaLayer **layers,
|
||||
gint layer_num,
|
||||
PikaLayer *background,
|
||||
GObject *config)
|
||||
{
|
||||
AlignData data;
|
||||
gint min_x = G_MAXINT;
|
||||
gint min_y = G_MAXINT;
|
||||
gint max_x = G_MININT;
|
||||
gint max_y = G_MININT;
|
||||
gint index;
|
||||
gint orig_x = 0;
|
||||
gint orig_y = 0;
|
||||
gint offset_x = 0;
|
||||
gint offset_y = 0;
|
||||
gint horizontal_style;
|
||||
gint vertical_style;
|
||||
gboolean use_bottom_layer;
|
||||
|
||||
g_object_get (config,
|
||||
"horizontal-style", &horizontal_style,
|
||||
"vertical-style", &vertical_style,
|
||||
"use-bottom-layer", &use_bottom_layer,
|
||||
NULL);
|
||||
|
||||
data.step_x = 0;
|
||||
data.step_y = 0;
|
||||
data.base_x = 0;
|
||||
data.base_y = 0;
|
||||
|
||||
/* 0 is the top layer */
|
||||
for (index = 0; index < layer_num; index++)
|
||||
{
|
||||
pika_drawable_get_offsets (PIKA_DRAWABLE (layers[index]), &orig_x, &orig_y);
|
||||
|
||||
align_layers_get_align_offsets (PIKA_DRAWABLE (layers[index]),
|
||||
&offset_x,
|
||||
&offset_y,
|
||||
config);
|
||||
orig_x += offset_x;
|
||||
orig_y += offset_y;
|
||||
|
||||
min_x = MIN (min_x, orig_x);
|
||||
max_x = MAX (max_x, orig_x);
|
||||
min_y = MIN (min_y, orig_y);
|
||||
max_y = MAX (max_y, orig_y);
|
||||
}
|
||||
|
||||
if (use_bottom_layer)
|
||||
{
|
||||
pika_drawable_get_offsets (PIKA_DRAWABLE (background), &orig_x, &orig_y);
|
||||
|
||||
align_layers_get_align_offsets (PIKA_DRAWABLE (background),
|
||||
&offset_x,
|
||||
&offset_y,
|
||||
config);
|
||||
orig_x += offset_x;
|
||||
orig_y += offset_y;
|
||||
data.base_x = min_x = orig_x;
|
||||
data.base_y = min_y = orig_y;
|
||||
}
|
||||
|
||||
if (layer_num > 1)
|
||||
{
|
||||
data.step_x = (max_x - min_x) / (layer_num - 1);
|
||||
data.step_y = (max_y - min_y) / (layer_num - 1);
|
||||
}
|
||||
|
||||
if ((horizontal_style == LEFT2RIGHT) || (horizontal_style == RIGHT2LEFT))
|
||||
data.base_x = min_x;
|
||||
|
||||
if ((vertical_style == TOP2BOTTOM) || (vertical_style == BOTTOM2TOP))
|
||||
data.base_y = min_y;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/*
|
||||
* Modifies position of each visible layers
|
||||
* according to data.
|
||||
*/
|
||||
static void
|
||||
align_layers_perform_alignment (PikaLayer **layers,
|
||||
gint layer_num,
|
||||
AlignData data,
|
||||
GObject *config)
|
||||
{
|
||||
gint index;
|
||||
gint horizontal_style;
|
||||
gint vertical_style;
|
||||
gint grid_size;
|
||||
|
||||
g_object_get (config,
|
||||
"horizontal-style", &horizontal_style,
|
||||
"vertical-style", &vertical_style,
|
||||
"grid-size", &grid_size,
|
||||
NULL);
|
||||
|
||||
for (index = 0; index < layer_num; index++)
|
||||
{
|
||||
gint x = 0;
|
||||
gint y = 0;
|
||||
gint orig_x;
|
||||
gint orig_y;
|
||||
gint offset_x;
|
||||
gint offset_y;
|
||||
|
||||
pika_drawable_get_offsets (PIKA_DRAWABLE (layers[index]), &orig_x, &orig_y);
|
||||
|
||||
align_layers_get_align_offsets (PIKA_DRAWABLE (layers[index]),
|
||||
&offset_x,
|
||||
&offset_y,
|
||||
config);
|
||||
switch (horizontal_style)
|
||||
{
|
||||
case H_NONE:
|
||||
x = orig_x;
|
||||
break;
|
||||
case H_COLLECT:
|
||||
x = data.base_x - offset_x;
|
||||
break;
|
||||
case LEFT2RIGHT:
|
||||
x = (data.base_x + index * data.step_x) - offset_x;
|
||||
break;
|
||||
case RIGHT2LEFT:
|
||||
x = (data.base_x + (layer_num - index - 1) * data.step_x) - offset_x;
|
||||
break;
|
||||
case SNAP2HGRID:
|
||||
x = grid_size
|
||||
* (int) ((orig_x + offset_x + grid_size /2) / grid_size)
|
||||
- offset_x;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (vertical_style)
|
||||
{
|
||||
case V_NONE:
|
||||
y = orig_y;
|
||||
break;
|
||||
case V_COLLECT:
|
||||
y = data.base_y - offset_y;
|
||||
break;
|
||||
case TOP2BOTTOM:
|
||||
y = (data.base_y + index * data.step_y) - offset_y;
|
||||
break;
|
||||
case BOTTOM2TOP:
|
||||
y = (data.base_y + (layer_num - index - 1) * data.step_y) - offset_y;
|
||||
break;
|
||||
case SNAP2VGRID:
|
||||
y = grid_size
|
||||
* (int) ((orig_y + offset_y + grid_size / 2) / grid_size)
|
||||
- offset_y;
|
||||
break;
|
||||
}
|
||||
|
||||
pika_layer_set_offsets (layers[index], x, y);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
align_layers_get_align_offsets (PikaDrawable *drawable,
|
||||
gint *x,
|
||||
gint *y,
|
||||
GObject *config)
|
||||
{
|
||||
gint width = pika_drawable_get_width (drawable);
|
||||
gint height = pika_drawable_get_height (drawable);
|
||||
gint horizontal_base;
|
||||
gint vertical_base;
|
||||
|
||||
g_object_get (config,
|
||||
"horizontal-base", &horizontal_base,
|
||||
"vertical-base", &vertical_base,
|
||||
NULL);
|
||||
|
||||
switch (horizontal_base)
|
||||
{
|
||||
case H_BASE_LEFT:
|
||||
*x = 0;
|
||||
break;
|
||||
case H_BASE_CENTER:
|
||||
*x = width / 2;
|
||||
break;
|
||||
case H_BASE_RIGHT:
|
||||
*x = width;
|
||||
break;
|
||||
default:
|
||||
*x = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (vertical_base)
|
||||
{
|
||||
case V_BASE_TOP:
|
||||
*y = 0;
|
||||
break;
|
||||
case V_BASE_CENTER:
|
||||
*y = height / 2;
|
||||
break;
|
||||
case V_BASE_BOTTOM:
|
||||
*y = height;
|
||||
break;
|
||||
default:
|
||||
*y = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
align_layers_dialog (PikaProcedure *procedure,
|
||||
GObject *config)
|
||||
{
|
||||
GtkWidget *dialog;
|
||||
GtkListStore *store;
|
||||
gboolean run;
|
||||
|
||||
pika_ui_init (PLUG_IN_BINARY);
|
||||
|
||||
dialog = pika_procedure_dialog_new (procedure,
|
||||
PIKA_PROCEDURE_CONFIG (config),
|
||||
_("Align Visible Layers"));
|
||||
|
||||
pika_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
|
||||
GTK_RESPONSE_OK,
|
||||
GTK_RESPONSE_CANCEL,
|
||||
-1);
|
||||
|
||||
pika_window_set_transient (GTK_WINDOW (dialog));
|
||||
|
||||
store = pika_int_store_new (_("None"), H_NONE,
|
||||
_("Collect"), H_COLLECT,
|
||||
_("Fill (left to right)"), LEFT2RIGHT,
|
||||
_("Fill (right to left)"), RIGHT2LEFT,
|
||||
_("Snap to grid"), SNAP2HGRID,
|
||||
NULL);
|
||||
pika_procedure_dialog_get_int_combo (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"horizontal-style",
|
||||
PIKA_INT_STORE (store));
|
||||
|
||||
store = pika_int_store_new (_("Left edge"), H_BASE_LEFT,
|
||||
_("Center"), H_BASE_CENTER,
|
||||
_("Right edge"), H_BASE_RIGHT,
|
||||
NULL);
|
||||
pika_procedure_dialog_get_int_combo (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"horizontal-base",
|
||||
PIKA_INT_STORE (store));
|
||||
|
||||
store = pika_int_store_new (_("None"), V_NONE,
|
||||
_("Collect"), V_COLLECT,
|
||||
_("Fill (top to bottom)"), TOP2BOTTOM,
|
||||
_("Fill (bottom to top)"), BOTTOM2TOP,
|
||||
_("Snap to grid"), SNAP2VGRID,
|
||||
NULL);
|
||||
pika_procedure_dialog_get_int_combo (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"vertical-style",
|
||||
PIKA_INT_STORE (store));
|
||||
|
||||
store = pika_int_store_new (_("Top edge"), V_BASE_TOP,
|
||||
_("Center"), V_BASE_CENTER,
|
||||
_("Bottom edge"), V_BASE_BOTTOM,
|
||||
NULL);
|
||||
pika_procedure_dialog_get_int_combo (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"vertical-base",
|
||||
PIKA_INT_STORE (store));
|
||||
|
||||
pika_procedure_dialog_get_scale_entry (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"grid-size", 1.0);
|
||||
|
||||
pika_procedure_dialog_fill (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
NULL);
|
||||
|
||||
gtk_widget_show (dialog);
|
||||
|
||||
run = pika_procedure_dialog_run (PIKA_PROCEDURE_DIALOG (dialog));
|
||||
|
||||
gtk_widget_destroy (dialog);
|
||||
|
||||
return run;
|
||||
}
|
||||
1423
plug-ins/common/animation-optimize.c
Normal file
1423
plug-ins/common/animation-optimize.c
Normal file
File diff suppressed because it is too large
Load Diff
1778
plug-ins/common/animation-play.c
Normal file
1778
plug-ins/common/animation-play.c
Normal file
File diff suppressed because it is too large
Load Diff
778
plug-ins/common/blinds.c
Normal file
778
plug-ins/common/blinds.c
Normal file
@ -0,0 +1,778 @@
|
||||
/*
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* This is a plug-in for PIKA.
|
||||
*
|
||||
* Blinds plug-in. Distort an image as though it was stuck to
|
||||
* window blinds and the blinds where opened/closed.
|
||||
*
|
||||
* Copyright (C) 1997 Andy Thomas alt@picnic.demon.co.uk
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* A fair proportion of this code was taken from the Whirl plug-in
|
||||
* which was copyrighted by Federico Mena Quintero (as below).
|
||||
*
|
||||
* Whirl plug-in --- distort an image into a whirlpool
|
||||
* Copyright (C) 1997 Federico Mena Quintero
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <libpika/pika.h>
|
||||
#include <libpika/pikaui.h>
|
||||
|
||||
#include "libpika/stdplugins-intl.h"
|
||||
|
||||
/***** Magic numbers *****/
|
||||
|
||||
#define PLUG_IN_PROC "plug-in-blinds"
|
||||
#define PLUG_IN_BINARY "blinds"
|
||||
#define PLUG_IN_ROLE "pika-blinds"
|
||||
|
||||
#define MAX_FANS 100
|
||||
|
||||
|
||||
typedef struct _Blinds Blinds;
|
||||
typedef struct _BlindsClass BlindsClass;
|
||||
|
||||
struct _Blinds
|
||||
{
|
||||
PikaPlugIn parent_instance;
|
||||
};
|
||||
|
||||
struct _BlindsClass
|
||||
{
|
||||
PikaPlugInClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
#define BLINDS_TYPE (blinds_get_type ())
|
||||
#define BLINDS (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), BLINDS_TYPE, Blinds))
|
||||
|
||||
GType blinds_get_type (void) G_GNUC_CONST;
|
||||
|
||||
static GList * blinds_query_procedures (PikaPlugIn *plug_in);
|
||||
static PikaProcedure * blinds_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name);
|
||||
|
||||
static PikaValueArray * blinds_run (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data);
|
||||
|
||||
static gboolean blinds_dialog (PikaProcedure *procedure,
|
||||
GObject *config,
|
||||
PikaDrawable *drawable);
|
||||
|
||||
static void dialog_update_preview (GtkWidget *widget,
|
||||
GObject *config);
|
||||
static void apply_blinds (GObject *config,
|
||||
PikaDrawable *drawable);
|
||||
|
||||
|
||||
G_DEFINE_TYPE (Blinds, blinds, PIKA_TYPE_PLUG_IN)
|
||||
|
||||
PIKA_MAIN (BLINDS_TYPE)
|
||||
DEFINE_STD_SET_I18N
|
||||
|
||||
|
||||
/* Array to hold each size of fans. And no there are not each the
|
||||
* same size (rounding errors...)
|
||||
*/
|
||||
|
||||
static gint fanwidths[MAX_FANS];
|
||||
|
||||
static void
|
||||
blinds_class_init (BlindsClass *klass)
|
||||
{
|
||||
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
|
||||
|
||||
plug_in_class->query_procedures = blinds_query_procedures;
|
||||
plug_in_class->create_procedure = blinds_create_procedure;
|
||||
plug_in_class->set_i18n = STD_SET_I18N;
|
||||
}
|
||||
|
||||
static void
|
||||
blinds_init (Blinds *blinds)
|
||||
{
|
||||
}
|
||||
|
||||
static GList *
|
||||
blinds_query_procedures (PikaPlugIn *plug_in)
|
||||
{
|
||||
return g_list_append (NULL, g_strdup (PLUG_IN_PROC));
|
||||
}
|
||||
|
||||
static PikaProcedure *
|
||||
blinds_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name)
|
||||
{
|
||||
PikaProcedure *procedure = NULL;
|
||||
|
||||
if (! strcmp (name, PLUG_IN_PROC))
|
||||
{
|
||||
procedure = pika_image_procedure_new (plug_in, name,
|
||||
PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
blinds_run, NULL, NULL);
|
||||
|
||||
pika_procedure_set_image_types (procedure, "RGB*, GRAY*");
|
||||
pika_procedure_set_sensitivity_mask (procedure,
|
||||
PIKA_PROCEDURE_SENSITIVE_DRAWABLE);
|
||||
|
||||
pika_procedure_set_menu_label (procedure, _("_Blinds..."));
|
||||
pika_procedure_add_menu_path (procedure, "<Image>/Filters/Distorts");
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
_("Simulate an image painted on "
|
||||
"window blinds"),
|
||||
"More here later",
|
||||
name);
|
||||
pika_procedure_set_attribution (procedure,
|
||||
"Andy Thomas",
|
||||
"Andy Thomas",
|
||||
"1997");
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "angle-displacement",
|
||||
_("_Displacement"),
|
||||
_("Angle of Displacement"),
|
||||
0, 360, 30,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "num-segments",
|
||||
_("_Number of segments"),
|
||||
_("Number of segments in blinds"),
|
||||
1, 1024, 3,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "orientation",
|
||||
_("Orientation"),
|
||||
_("The orientation"),
|
||||
PIKA_ORIENTATION_HORIZONTAL,
|
||||
PIKA_ORIENTATION_VERTICAL,
|
||||
PIKA_ORIENTATION_HORIZONTAL,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_BOOLEAN (procedure, "bg-transparent",
|
||||
_("_Transparent"),
|
||||
_("Background transparent"),
|
||||
FALSE,
|
||||
G_PARAM_READWRITE);
|
||||
}
|
||||
|
||||
return procedure;
|
||||
}
|
||||
|
||||
static PikaValueArray *
|
||||
blinds_run (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data)
|
||||
{
|
||||
PikaProcedureConfig *config;
|
||||
PikaDrawable *drawable;
|
||||
|
||||
gegl_init (NULL, NULL);
|
||||
|
||||
if (n_drawables != 1)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
g_set_error (&error, PIKA_PLUG_IN_ERROR, 0,
|
||||
_("Procedure '%s' only works with one drawable."),
|
||||
PLUG_IN_PROC);
|
||||
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_CALLING_ERROR,
|
||||
error);
|
||||
}
|
||||
else
|
||||
{
|
||||
drawable = drawables[0];
|
||||
}
|
||||
|
||||
config = pika_procedure_create_config (procedure);
|
||||
pika_procedure_config_begin_run (config, NULL, run_mode, args);
|
||||
|
||||
switch (run_mode)
|
||||
{
|
||||
case PIKA_RUN_INTERACTIVE:
|
||||
if (! blinds_dialog (procedure, G_OBJECT (config), drawable))
|
||||
{
|
||||
pika_procedure_config_end_run (config, PIKA_PDB_CANCEL);
|
||||
g_object_unref (config);
|
||||
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_CANCEL,
|
||||
NULL);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (pika_drawable_is_rgb (drawable) ||
|
||||
pika_drawable_is_gray (drawable))
|
||||
{
|
||||
pika_progress_init (_("Adding blinds"));
|
||||
|
||||
apply_blinds (G_OBJECT (config), drawable);
|
||||
|
||||
if (run_mode != PIKA_RUN_NONINTERACTIVE)
|
||||
pika_displays_flush ();
|
||||
}
|
||||
else
|
||||
{
|
||||
pika_procedure_config_end_run (config, PIKA_PDB_EXECUTION_ERROR);
|
||||
g_object_unref (config);
|
||||
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_EXECUTION_ERROR,
|
||||
NULL);
|
||||
}
|
||||
|
||||
pika_procedure_config_end_run (config, PIKA_PDB_SUCCESS);
|
||||
g_object_unref (config);
|
||||
|
||||
return pika_procedure_new_return_values (procedure, PIKA_PDB_SUCCESS, NULL);
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
blinds_dialog (PikaProcedure *procedure,
|
||||
GObject *config,
|
||||
PikaDrawable *drawable)
|
||||
{
|
||||
GtkWidget *dialog;
|
||||
GtkWidget *preview;
|
||||
GtkWidget *vbox;
|
||||
GtkWidget *hbox;
|
||||
GtkWidget *scale;
|
||||
GtkListStore *store;
|
||||
gboolean run;
|
||||
|
||||
pika_ui_init (PLUG_IN_BINARY);
|
||||
|
||||
dialog = pika_procedure_dialog_new (procedure,
|
||||
PIKA_PROCEDURE_CONFIG (config),
|
||||
_("Blinds"));
|
||||
|
||||
pika_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
|
||||
GTK_RESPONSE_OK,
|
||||
GTK_RESPONSE_CANCEL,
|
||||
-1);
|
||||
|
||||
pika_window_set_transient (GTK_WINDOW (dialog));
|
||||
|
||||
preview = pika_drawable_preview_new_from_drawable (drawable);
|
||||
gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
|
||||
preview, TRUE, TRUE, 0);
|
||||
gtk_widget_set_margin_bottom (preview, 12);
|
||||
gtk_widget_show (preview);
|
||||
|
||||
store = pika_int_store_new (_("Horizontal"), PIKA_ORIENTATION_HORIZONTAL,
|
||||
_("Vertical"), PIKA_ORIENTATION_VERTICAL,
|
||||
NULL);
|
||||
pika_procedure_dialog_get_int_radio (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"orientation", PIKA_INT_STORE (store));
|
||||
|
||||
pika_procedure_dialog_get_label (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"bg_label", _("Background"));
|
||||
|
||||
pika_procedure_dialog_fill_frame (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"bg-frame", "bg_label", FALSE,
|
||||
"bg-transparent");
|
||||
pika_procedure_dialog_set_sensitive (PIKA_PROCEDURE_DIALOG (dialog), "bg-frame",
|
||||
pika_drawable_has_alpha (drawable),
|
||||
NULL, NULL, FALSE);
|
||||
|
||||
hbox = pika_procedure_dialog_fill_box (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"blinds-hbox", "orientation",
|
||||
"bg-frame", NULL);
|
||||
gtk_orientable_set_orientation (GTK_ORIENTABLE (hbox),
|
||||
GTK_ORIENTATION_HORIZONTAL);
|
||||
gtk_box_set_spacing (GTK_BOX (hbox), 12);
|
||||
gtk_box_set_homogeneous (GTK_BOX (hbox), TRUE);
|
||||
gtk_widget_set_margin_bottom (hbox, 12);
|
||||
|
||||
scale = pika_procedure_dialog_get_scale_entry (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"angle-displacement", 1.0);
|
||||
gtk_widget_set_margin_bottom (scale, 12);
|
||||
|
||||
pika_procedure_dialog_get_scale_entry (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"num-segments", 1.0);
|
||||
|
||||
vbox = pika_procedure_dialog_fill_box (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"blinds-vbox", "blinds-hbox",
|
||||
"angle-displacement", "num-segments",
|
||||
NULL);
|
||||
gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
|
||||
gtk_widget_set_size_request (vbox, 320, -1);
|
||||
|
||||
g_object_set_data (config, "drawable", drawable);
|
||||
|
||||
g_signal_connect (preview, "invalidated",
|
||||
G_CALLBACK (dialog_update_preview),
|
||||
config);
|
||||
|
||||
g_signal_connect_swapped (config, "notify",
|
||||
G_CALLBACK (pika_preview_invalidate),
|
||||
preview);
|
||||
|
||||
pika_procedure_dialog_fill (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"blinds-vbox",
|
||||
NULL);
|
||||
|
||||
gtk_widget_show (dialog);
|
||||
|
||||
run = pika_procedure_dialog_run (PIKA_PROCEDURE_DIALOG (dialog));
|
||||
|
||||
gtk_widget_destroy (dialog);
|
||||
|
||||
return run;
|
||||
}
|
||||
|
||||
static void
|
||||
blindsapply (GObject *config,
|
||||
const guchar *srow,
|
||||
guchar *drow,
|
||||
gint width,
|
||||
gint bpp,
|
||||
guchar *bg)
|
||||
{
|
||||
const guchar *src;
|
||||
guchar *dst;
|
||||
gint i,j,k;
|
||||
gdouble ang;
|
||||
gint available;
|
||||
gint angledsp;
|
||||
gint numsegs;
|
||||
|
||||
g_object_get (config,
|
||||
"angle-displacement", &angledsp,
|
||||
"num-segments", &numsegs,
|
||||
NULL);
|
||||
|
||||
/* Make the row 'shrink' around points along its length */
|
||||
/* The numsegs determines how many segments to slip it in to */
|
||||
/* The angle is the conceptual 'rotation' of each of these segments */
|
||||
|
||||
/* Note the row is considered to be made up of a two dim array actual
|
||||
* pixel locations and the RGB color at these locations.
|
||||
*/
|
||||
|
||||
/* In the process copy the src row to the destination row */
|
||||
|
||||
/* Fill in with background color ? */
|
||||
for (i = 0 ; i < width ; i++)
|
||||
{
|
||||
dst = &drow[i*bpp];
|
||||
|
||||
for (j = 0 ; j < bpp; j++)
|
||||
{
|
||||
dst[j] = bg[j];
|
||||
}
|
||||
}
|
||||
|
||||
/* Apply it */
|
||||
|
||||
available = width;
|
||||
for (i = 0; i < numsegs; i++)
|
||||
{
|
||||
/* Width of segs are variable */
|
||||
fanwidths[i] = available / (numsegs - i);
|
||||
available -= fanwidths[i];
|
||||
}
|
||||
|
||||
/* do center points first - just for fun...*/
|
||||
available = 0;
|
||||
for (k = 1; k <= numsegs; k++)
|
||||
{
|
||||
int point;
|
||||
|
||||
point = available + fanwidths[k - 1] / 2;
|
||||
|
||||
available += fanwidths[k - 1];
|
||||
|
||||
src = &srow[point * bpp];
|
||||
dst = &drow[point * bpp];
|
||||
|
||||
/* Copy pixels across */
|
||||
for (j = 0 ; j < bpp; j++)
|
||||
{
|
||||
dst[j] = src[j];
|
||||
}
|
||||
}
|
||||
|
||||
/* Disp for each point */
|
||||
ang = (angledsp * 2 * G_PI) / 360; /* Angle in rads */
|
||||
ang = (1 - fabs (cos (ang)));
|
||||
|
||||
available = 0;
|
||||
for (k = 0 ; k < numsegs; k++)
|
||||
{
|
||||
int dx; /* Amount to move by */
|
||||
int fw;
|
||||
|
||||
for (i = 0 ; i < (fanwidths[k]/2) ; i++)
|
||||
{
|
||||
/* Copy pixels across of left half of fan */
|
||||
fw = fanwidths[k] / 2;
|
||||
dx = (int) (ang * ((double) (fw - (double)(i % fw))));
|
||||
|
||||
src = &srow[(available + i) * bpp];
|
||||
dst = &drow[(available + i + dx) * bpp];
|
||||
|
||||
for (j = 0; j < bpp; j++)
|
||||
{
|
||||
dst[j] = src[j];
|
||||
}
|
||||
|
||||
/* Right side */
|
||||
j = i + 1;
|
||||
src = &srow[(available + fanwidths[k] - j
|
||||
- (fanwidths[k] % 2)) * bpp];
|
||||
dst = &drow[(available + fanwidths[k] - j
|
||||
- (fanwidths[k] % 2) - dx) * bpp];
|
||||
|
||||
for (j = 0; j < bpp; j++)
|
||||
{
|
||||
dst[j] = src[j];
|
||||
}
|
||||
}
|
||||
|
||||
available += fanwidths[k];
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
dialog_update_preview (GtkWidget *widget,
|
||||
GObject *config)
|
||||
{
|
||||
PikaPreview *preview = PIKA_PREVIEW (widget);
|
||||
PikaDrawable *drawable = g_object_get_data (config, "drawable");
|
||||
gint y;
|
||||
const guchar *p;
|
||||
guchar *buffer;
|
||||
GBytes *cache;
|
||||
const guchar *cache_start;
|
||||
PikaRGB background;
|
||||
guchar bg[4];
|
||||
gint width;
|
||||
gint height;
|
||||
gint bpp;
|
||||
PikaOrientationType orientation;
|
||||
gint bg_trans;
|
||||
|
||||
g_object_get (config,
|
||||
"bg-transparent", &bg_trans,
|
||||
"orientation", &orientation,
|
||||
NULL);
|
||||
|
||||
pika_preview_get_size (preview, &width, &height);
|
||||
cache = pika_drawable_get_thumbnail_data (drawable,
|
||||
width, height,
|
||||
&width, &height, &bpp);
|
||||
p = cache_start = g_bytes_get_data (cache, NULL);
|
||||
|
||||
pika_context_get_background (&background);
|
||||
|
||||
if (bg_trans)
|
||||
pika_rgb_set_alpha (&background, 0.0);
|
||||
|
||||
if (pika_drawable_is_gray (drawable))
|
||||
{
|
||||
bg[0] = pika_rgb_luminance_uchar (&background);
|
||||
pika_rgba_get_uchar (&background, NULL, NULL, NULL, bg + 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
pika_rgba_get_uchar (&background, bg, bg + 1, bg + 2, bg + 3);
|
||||
}
|
||||
|
||||
buffer = g_new (guchar, width * height * bpp);
|
||||
|
||||
if (orientation == PIKA_ORIENTATION_VERTICAL)
|
||||
{
|
||||
for (y = 0; y < height; y++)
|
||||
{
|
||||
blindsapply (config, p,
|
||||
buffer + y * width * bpp,
|
||||
width,
|
||||
bpp, bg);
|
||||
p += width * bpp;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Horizontal blinds */
|
||||
/* Apply the blinds algo to a single column -
|
||||
* this act as a transformation matrix for the
|
||||
* rows. Make row 0 invalid so we can find it again!
|
||||
*/
|
||||
gint i;
|
||||
guchar *sr = g_new (guchar, height * 4);
|
||||
guchar *dr = g_new0 (guchar, height * 4);
|
||||
guchar dummybg[4] = {0, 0, 0, 0};
|
||||
|
||||
/* Fill in with background color ? */
|
||||
for (i = 0 ; i < width ; i++)
|
||||
{
|
||||
gint j;
|
||||
gint bd = bpp;
|
||||
guchar *dst;
|
||||
dst = &buffer[i * bd];
|
||||
|
||||
for (j = 0 ; j < bd; j++)
|
||||
{
|
||||
dst[j] = bg[j];
|
||||
}
|
||||
}
|
||||
|
||||
for ( y = 0 ; y < height; y++)
|
||||
{
|
||||
sr[y] = y+1;
|
||||
}
|
||||
|
||||
/* Bit of a fiddle since blindsapply really works on an image
|
||||
* row not a set of bytes. - preview can't be > 255
|
||||
* or must make dr sr int rows.
|
||||
*/
|
||||
blindsapply (config, sr, dr, height, 1, dummybg);
|
||||
|
||||
for (y = 0; y < height; y++)
|
||||
{
|
||||
if (dr[y] == 0)
|
||||
{
|
||||
/* Draw background line */
|
||||
p = buffer;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Draw line from src */
|
||||
p = cache_start +
|
||||
(width * bpp * (dr[y] - 1));
|
||||
}
|
||||
memcpy (buffer + y * width * bpp,
|
||||
p,
|
||||
width * bpp);
|
||||
}
|
||||
g_free (sr);
|
||||
g_free (dr);
|
||||
}
|
||||
|
||||
pika_preview_draw_buffer (preview, buffer, width * bpp);
|
||||
|
||||
g_free (buffer);
|
||||
g_bytes_unref (cache);
|
||||
}
|
||||
|
||||
/* STEP tells us how many rows/columns to gulp down in one go... */
|
||||
/* Note all the "4" literals around here are to do with the depths
|
||||
* of the images. Makes it easier to deal with for my small brain.
|
||||
*/
|
||||
|
||||
#define STEP 40
|
||||
|
||||
static void
|
||||
apply_blinds (GObject *config,
|
||||
PikaDrawable *drawable)
|
||||
{
|
||||
GeglBuffer *src_buffer;
|
||||
GeglBuffer *dest_buffer;
|
||||
const Babl *format;
|
||||
guchar *src_rows, *des_rows;
|
||||
gint bytes;
|
||||
gint x, y;
|
||||
PikaRGB background;
|
||||
guchar bg[4];
|
||||
gint sel_x1, sel_y1;
|
||||
gint sel_width, sel_height;
|
||||
PikaOrientationType orientation;
|
||||
gint bg_trans;
|
||||
|
||||
g_object_get (config,
|
||||
"bg-transparent", &bg_trans,
|
||||
"orientation", &orientation,
|
||||
NULL);
|
||||
|
||||
pika_context_get_background (&background);
|
||||
|
||||
if (bg_trans)
|
||||
pika_rgb_set_alpha (&background, 0.0);
|
||||
|
||||
pika_rgba_get_uchar (&background, bg, bg + 1, bg + 2, bg + 3);
|
||||
|
||||
if (! pika_drawable_mask_intersect (drawable,
|
||||
&sel_x1, &sel_y1,
|
||||
&sel_width, &sel_height))
|
||||
return;
|
||||
|
||||
if (pika_drawable_has_alpha (drawable))
|
||||
format = babl_format ("R'G'B'A u8");
|
||||
else
|
||||
format = babl_format ("R'G'B' u8");
|
||||
|
||||
bytes = babl_format_get_bytes_per_pixel (format);
|
||||
|
||||
src_buffer = pika_drawable_get_buffer (drawable);
|
||||
dest_buffer = pika_drawable_get_shadow_buffer (drawable);
|
||||
|
||||
src_rows = g_new (guchar, MAX (sel_width, sel_height) * bytes * STEP);
|
||||
des_rows = g_new (guchar, MAX (sel_width, sel_height) * bytes * STEP);
|
||||
|
||||
if (orientation == PIKA_ORIENTATION_VERTICAL)
|
||||
{
|
||||
for (y = 0; y < sel_height; y += STEP)
|
||||
{
|
||||
gint rr;
|
||||
gint step;
|
||||
|
||||
if ((y + STEP) > sel_height)
|
||||
step = sel_height - y;
|
||||
else
|
||||
step = STEP;
|
||||
|
||||
gegl_buffer_get (src_buffer,
|
||||
GEGL_RECTANGLE (sel_x1, sel_y1 + y,
|
||||
sel_width, step), 1.0,
|
||||
format, src_rows,
|
||||
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
|
||||
|
||||
/* OK I could make this better */
|
||||
for (rr = 0; rr < STEP; rr++)
|
||||
blindsapply (config,
|
||||
src_rows + (sel_width * rr * bytes),
|
||||
des_rows + (sel_width * rr * bytes),
|
||||
sel_width, bytes, bg);
|
||||
|
||||
gegl_buffer_set (dest_buffer,
|
||||
GEGL_RECTANGLE (sel_x1, sel_y1 + y,
|
||||
sel_width, step), 0,
|
||||
format, des_rows,
|
||||
GEGL_AUTO_ROWSTRIDE);
|
||||
|
||||
pika_progress_update ((double) y / (double) sel_height);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Horizontal blinds */
|
||||
/* Apply the blinds algo to a single column -
|
||||
* this act as a transformation matrix for the
|
||||
* rows. Make row 0 invalid so we can find it again!
|
||||
*/
|
||||
gint i;
|
||||
gint *sr = g_new (gint, sel_height * bytes);
|
||||
gint *dr = g_new (gint, sel_height * bytes);
|
||||
guchar *dst = g_new (guchar, STEP * bytes);
|
||||
guchar dummybg[4];
|
||||
|
||||
memset (dummybg, 0, 4);
|
||||
memset (dr, 0, sel_height * bytes); /* all dr rows are background rows */
|
||||
for (y = 0; y < sel_height; y++)
|
||||
{
|
||||
sr[y] = y+1;
|
||||
}
|
||||
|
||||
/* Hmmm. does this work portably? */
|
||||
/* This "swaps" the integers around that are held in in the
|
||||
* sr & dr arrays.
|
||||
*/
|
||||
blindsapply (config, (guchar *) sr, (guchar *) dr,
|
||||
sel_height, sizeof (gint), dummybg);
|
||||
|
||||
/* Fill in with background color ? */
|
||||
for (i = 0 ; i < STEP ; i++)
|
||||
{
|
||||
int j;
|
||||
guchar *bgdst;
|
||||
bgdst = &dst[i * bytes];
|
||||
|
||||
for (j = 0 ; j < bytes; j++)
|
||||
{
|
||||
bgdst[j] = bg[j];
|
||||
}
|
||||
}
|
||||
|
||||
for (x = 0; x < sel_width; x += STEP)
|
||||
{
|
||||
int rr;
|
||||
int step;
|
||||
guchar *p;
|
||||
|
||||
if((x + STEP) > sel_width)
|
||||
step = sel_width - x;
|
||||
else
|
||||
step = STEP;
|
||||
|
||||
gegl_buffer_get (src_buffer,
|
||||
GEGL_RECTANGLE (sel_x1 + x, sel_y1,
|
||||
step, sel_height), 1.0,
|
||||
format, src_rows,
|
||||
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
|
||||
|
||||
/* OK I could make this better */
|
||||
for (rr = 0; rr < sel_height; rr++)
|
||||
{
|
||||
if(dr[rr] == 0)
|
||||
{
|
||||
/* Draw background line */
|
||||
p = dst;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Draw line from src */
|
||||
p = src_rows + (step * bytes * (dr[rr] - 1));
|
||||
}
|
||||
memcpy (des_rows + (rr * step * bytes), p,
|
||||
step * bytes);
|
||||
}
|
||||
|
||||
gegl_buffer_set (dest_buffer,
|
||||
GEGL_RECTANGLE (sel_x1 + x, sel_y1,
|
||||
step, sel_height), 0,
|
||||
format, des_rows,
|
||||
GEGL_AUTO_ROWSTRIDE);
|
||||
|
||||
pika_progress_update ((double) x / (double) sel_width);
|
||||
}
|
||||
|
||||
g_free (dst);
|
||||
g_free (sr);
|
||||
g_free (dr);
|
||||
}
|
||||
|
||||
g_free (src_rows);
|
||||
g_free (des_rows);
|
||||
|
||||
g_object_unref (src_buffer);
|
||||
g_object_unref (dest_buffer);
|
||||
|
||||
pika_progress_update (1.0);
|
||||
|
||||
pika_drawable_merge_shadow (drawable, TRUE);
|
||||
pika_drawable_update (drawable,
|
||||
sel_x1, sel_y1, sel_width, sel_height);
|
||||
}
|
||||
468
plug-ins/common/border-average.c
Normal file
468
plug-ins/common/border-average.c
Normal file
@ -0,0 +1,468 @@
|
||||
/* borderaverage 0.01 - image processing plug-in for PIKA.
|
||||
*
|
||||
* Copyright (C) 1998 Philipp Klaus (webmaster@access.ch)
|
||||
*
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <libpika/pika.h>
|
||||
#include <libpika/pikaui.h>
|
||||
|
||||
#include "libpika/stdplugins-intl.h"
|
||||
|
||||
|
||||
#define PLUG_IN_PROC "plug-in-borderaverage"
|
||||
#define PLUG_IN_BINARY "border-average"
|
||||
#define PLUG_IN_ROLE "pika-border-average"
|
||||
|
||||
|
||||
typedef struct _BorderAverage BorderAverage;
|
||||
typedef struct _BorderAverageClass BorderAverageClass;
|
||||
|
||||
struct _BorderAverage
|
||||
{
|
||||
PikaPlugIn parent_instance;
|
||||
};
|
||||
|
||||
struct _BorderAverageClass
|
||||
{
|
||||
PikaPlugInClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
#define BORDER_AVERAGE_TYPE (border_average_get_type ())
|
||||
#define BORDER_AVERAGE (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), BORDER_AVERAGE_TYPE, BorderAverage))
|
||||
|
||||
|
||||
GType border_average_get_type (void) G_GNUC_CONST;
|
||||
|
||||
static GList * border_average_query_procedures (PikaPlugIn *plug_in);
|
||||
static PikaProcedure * border_average_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name);
|
||||
|
||||
static PikaValueArray * border_average_run (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data);
|
||||
|
||||
|
||||
static void borderaverage (GObject *config,
|
||||
GeglBuffer *buffer,
|
||||
PikaDrawable *drawable,
|
||||
PikaRGB *result);
|
||||
|
||||
static gboolean borderaverage_dialog (PikaProcedure *procedure,
|
||||
GObject *config,
|
||||
PikaImage *image,
|
||||
PikaDrawable *drawable);
|
||||
|
||||
static void add_new_color (const guchar *buffer,
|
||||
gint *cube,
|
||||
gint bucket_expo);
|
||||
|
||||
|
||||
G_DEFINE_TYPE (BorderAverage, border_average, PIKA_TYPE_PLUG_IN)
|
||||
|
||||
PIKA_MAIN (BORDER_AVERAGE_TYPE)
|
||||
DEFINE_STD_SET_I18N
|
||||
|
||||
|
||||
static void
|
||||
border_average_class_init (BorderAverageClass *klass)
|
||||
{
|
||||
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
|
||||
|
||||
plug_in_class->query_procedures = border_average_query_procedures;
|
||||
plug_in_class->create_procedure = border_average_create_procedure;
|
||||
plug_in_class->set_i18n = STD_SET_I18N;
|
||||
}
|
||||
|
||||
static void
|
||||
border_average_init (BorderAverage *film)
|
||||
{
|
||||
}
|
||||
|
||||
static GList *
|
||||
border_average_query_procedures (PikaPlugIn *plug_in)
|
||||
{
|
||||
return g_list_append (NULL, g_strdup (PLUG_IN_PROC));
|
||||
}
|
||||
|
||||
static PikaProcedure *
|
||||
border_average_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name)
|
||||
{
|
||||
PikaProcedure *procedure = NULL;
|
||||
|
||||
if (! strcmp (name, PLUG_IN_PROC))
|
||||
{
|
||||
procedure = pika_image_procedure_new (plug_in, name,
|
||||
PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
border_average_run, NULL, NULL);
|
||||
|
||||
pika_procedure_set_image_types (procedure, "RGB*");
|
||||
pika_procedure_set_sensitivity_mask (procedure,
|
||||
PIKA_PROCEDURE_SENSITIVE_DRAWABLE);
|
||||
|
||||
pika_procedure_set_menu_label (procedure, _("_Border Average..."));
|
||||
pika_procedure_add_menu_path (procedure, "<Image>/Colors/Info");
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
_("Set foreground to the average color of the image border"),
|
||||
"",
|
||||
name);
|
||||
pika_procedure_set_attribution (procedure,
|
||||
"Philipp Klaus",
|
||||
"Internet Access AG",
|
||||
"1998");
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "thickness",
|
||||
_("_Thickness"),
|
||||
_("Border size to take in count"),
|
||||
0, G_MAXINT, 3,
|
||||
G_PARAM_READWRITE);
|
||||
PIKA_PROC_AUX_ARG_UNIT (procedure, "thickness-unit",
|
||||
_("Thickness unit of measure"),
|
||||
_("Border size unit of measure"),
|
||||
TRUE, TRUE, PIKA_UNIT_PIXEL,
|
||||
PIKA_PARAM_READWRITE);
|
||||
PIKA_PROC_ARG_INT (procedure, "bucket-exponent",
|
||||
_("Bucket Si_ze"),
|
||||
_("Bits for bucket size (default=4: 16 Levels)"),
|
||||
0, G_MAXINT, 4,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_VAL_RGB (procedure, "borderaverage",
|
||||
_("The average color of the specified border."),
|
||||
_("The average color of the specified border."),
|
||||
TRUE, NULL,
|
||||
G_PARAM_READWRITE);
|
||||
}
|
||||
|
||||
return procedure;
|
||||
}
|
||||
|
||||
|
||||
static PikaValueArray *
|
||||
border_average_run (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data)
|
||||
{
|
||||
PikaProcedureConfig *config;
|
||||
PikaDrawable *drawable;
|
||||
PikaValueArray *return_vals = NULL;
|
||||
PikaPDBStatusType status = PIKA_PDB_SUCCESS;
|
||||
PikaRGB result_color = { 0.0, };
|
||||
GeglBuffer *buffer;
|
||||
|
||||
gegl_init (NULL, NULL);
|
||||
|
||||
if (n_drawables != 1)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
g_set_error (&error, PIKA_PLUG_IN_ERROR, 0,
|
||||
_("Procedure '%s' only works with one drawable."),
|
||||
PLUG_IN_PROC);
|
||||
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_CALLING_ERROR,
|
||||
error);
|
||||
}
|
||||
else
|
||||
{
|
||||
drawable = drawables[0];
|
||||
}
|
||||
|
||||
config = pika_procedure_create_config (procedure);
|
||||
pika_procedure_config_begin_run (config, NULL, run_mode, args);
|
||||
|
||||
buffer = pika_drawable_get_buffer (drawable);
|
||||
|
||||
switch (run_mode)
|
||||
{
|
||||
case PIKA_RUN_INTERACTIVE:
|
||||
if (! borderaverage_dialog (procedure, G_OBJECT (config),
|
||||
image, drawable))
|
||||
status = PIKA_PDB_EXECUTION_ERROR;
|
||||
break;
|
||||
|
||||
case PIKA_RUN_NONINTERACTIVE:
|
||||
if (pika_value_array_length (args) != 2)
|
||||
status = PIKA_PDB_CALLING_ERROR;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (status == PIKA_PDB_SUCCESS)
|
||||
{
|
||||
/* Make sure that the drawable is RGB color */
|
||||
if (pika_drawable_is_rgb (drawable))
|
||||
{
|
||||
pika_progress_init ( _("Border Average"));
|
||||
borderaverage (G_OBJECT (config), buffer, drawable, &result_color);
|
||||
|
||||
if (run_mode != PIKA_RUN_NONINTERACTIVE)
|
||||
pika_context_set_foreground (&result_color);
|
||||
}
|
||||
else
|
||||
{
|
||||
status = PIKA_PDB_EXECUTION_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
g_object_unref (buffer);
|
||||
|
||||
return_vals = pika_procedure_new_return_values (procedure, status, NULL);
|
||||
|
||||
pika_procedure_config_end_run (config, PIKA_PDB_SUCCESS);
|
||||
g_object_unref (config);
|
||||
|
||||
if (status == PIKA_PDB_SUCCESS)
|
||||
PIKA_VALUES_SET_RGB (return_vals, 1, &result_color);
|
||||
|
||||
return return_vals;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
borderaverage (GObject *config,
|
||||
GeglBuffer *buffer,
|
||||
PikaDrawable *drawable,
|
||||
PikaRGB *result)
|
||||
{
|
||||
gint x, y, width, height;
|
||||
gint max;
|
||||
guchar r, g, b;
|
||||
gint bucket_num, bucket_expo, bucket_rexpo;
|
||||
gint *cube;
|
||||
gint i, j, k;
|
||||
GeglRectangle border[4];
|
||||
gint borderaverage_thickness;
|
||||
gint borderaverage_bucket_exponent;
|
||||
|
||||
g_object_get (config,
|
||||
"thickness", &borderaverage_thickness,
|
||||
"bucket-exponent", &borderaverage_bucket_exponent,
|
||||
NULL);
|
||||
|
||||
if (! pika_drawable_mask_intersect (drawable, &x, &y, &width, &height))
|
||||
{
|
||||
pika_rgba_set_uchar (result, 0, 0, 0, 255);
|
||||
return;
|
||||
}
|
||||
|
||||
/* allocate and clear the cube before */
|
||||
bucket_expo = borderaverage_bucket_exponent;
|
||||
bucket_rexpo = 8 - bucket_expo;
|
||||
cube = g_new (gint, 1 << (bucket_rexpo * 3));
|
||||
bucket_num = 1 << bucket_rexpo;
|
||||
|
||||
for (i = 0; i < bucket_num; i++)
|
||||
{
|
||||
for (j = 0; j < bucket_num; j++)
|
||||
{
|
||||
for (k = 0; k < bucket_num; k++)
|
||||
{
|
||||
cube[(i << (bucket_rexpo << 1)) + (j << bucket_rexpo) + k] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Top */
|
||||
border[0].x = x;
|
||||
border[0].y = y;
|
||||
border[0].width = width;
|
||||
border[0].height = borderaverage_thickness;
|
||||
|
||||
/* Bottom */
|
||||
border[1].x = x;
|
||||
border[1].y = y + height - borderaverage_thickness;
|
||||
border[1].width = width;
|
||||
border[1].height = borderaverage_thickness;
|
||||
|
||||
/* Left */
|
||||
border[2].x = x;
|
||||
border[2].y = y + borderaverage_thickness;
|
||||
border[2].width = borderaverage_thickness;
|
||||
border[2].height = height - 2 * borderaverage_thickness;
|
||||
|
||||
/* Right */
|
||||
border[3].x = x + width - borderaverage_thickness;
|
||||
border[3].y = y + borderaverage_thickness;
|
||||
border[3].width = borderaverage_thickness;
|
||||
border[3].height = height - 2 * borderaverage_thickness;
|
||||
|
||||
/* Fill the cube */
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
if (border[i].width > 0 && border[i].height > 0)
|
||||
{
|
||||
GeglBufferIterator *gi;
|
||||
|
||||
gi = gegl_buffer_iterator_new (buffer, &border[i], 0, babl_format ("R'G'B' u8"),
|
||||
GEGL_ACCESS_READWRITE, GEGL_ABYSS_NONE, 1);
|
||||
|
||||
while (gegl_buffer_iterator_next (gi))
|
||||
{
|
||||
guint k;
|
||||
guchar *data;
|
||||
|
||||
data = (guchar*) gi->items[0].data;
|
||||
|
||||
for (k = 0; k < gi->length; k++)
|
||||
{
|
||||
add_new_color (data + k * 3,
|
||||
cube,
|
||||
bucket_expo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
max = 0; r = 0; g = 0; b = 0;
|
||||
|
||||
/* get max of cube */
|
||||
for (i = 0; i < bucket_num; i++)
|
||||
{
|
||||
for (j = 0; j < bucket_num; j++)
|
||||
{
|
||||
for (k = 0; k < bucket_num; k++)
|
||||
{
|
||||
if (cube[(i << (bucket_rexpo << 1)) +
|
||||
(j << bucket_rexpo) + k] > max)
|
||||
{
|
||||
max = cube[(i << (bucket_rexpo << 1)) +
|
||||
(j << bucket_rexpo) + k];
|
||||
r = (i<<bucket_expo) + (1<<(bucket_expo - 1));
|
||||
g = (j<<bucket_expo) + (1<<(bucket_expo - 1));
|
||||
b = (k<<bucket_expo) + (1<<(bucket_expo - 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* return the color */
|
||||
pika_rgba_set_uchar (result, r, g, b, 255);
|
||||
|
||||
g_free (cube);
|
||||
}
|
||||
|
||||
static void
|
||||
add_new_color (const guchar *buffer,
|
||||
gint *cube,
|
||||
gint bucket_expo)
|
||||
{
|
||||
guchar r, g, b;
|
||||
gint bucket_rexpo;
|
||||
|
||||
bucket_rexpo = 8 - bucket_expo;
|
||||
r = buffer[0] >> bucket_expo;
|
||||
g = buffer[1] >> bucket_expo;
|
||||
b = buffer[2] >> bucket_expo;
|
||||
cube[(r << (bucket_rexpo << 1)) + (g << bucket_rexpo) + b]++;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
borderaverage_dialog (PikaProcedure *procedure,
|
||||
GObject *config,
|
||||
PikaImage *image,
|
||||
PikaDrawable *drawable)
|
||||
{
|
||||
GtkWidget *dialog;
|
||||
GtkListStore *store;
|
||||
GtkWidget *size_entry;
|
||||
gboolean run;
|
||||
gdouble xres, yres;
|
||||
GeglBuffer *buffer = NULL;
|
||||
|
||||
pika_ui_init (PLUG_IN_BINARY);
|
||||
|
||||
dialog = pika_procedure_dialog_new (procedure,
|
||||
PIKA_PROCEDURE_CONFIG (config),
|
||||
_("Border Average"));
|
||||
|
||||
pika_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
|
||||
GTK_RESPONSE_OK,
|
||||
GTK_RESPONSE_CANCEL,
|
||||
-1);
|
||||
|
||||
pika_window_set_transient (GTK_WINDOW (dialog));
|
||||
|
||||
pika_procedure_dialog_get_label (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"border-size-label",
|
||||
_("Border Size"));
|
||||
|
||||
/* Get the image resolution */
|
||||
pika_image_get_resolution (image, &xres, &yres);
|
||||
size_entry = pika_procedure_dialog_get_size_entry (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"thickness", TRUE,
|
||||
"thickness-unit", "%a",
|
||||
PIKA_SIZE_ENTRY_UPDATE_SIZE,
|
||||
xres);
|
||||
|
||||
/* set the size (in pixels) that will be treated as 0% and 100% */
|
||||
buffer = pika_drawable_get_buffer (drawable);
|
||||
if (buffer)
|
||||
pika_size_entry_set_size (PIKA_SIZE_ENTRY (size_entry), 0, 0.0,
|
||||
MIN (gegl_buffer_get_width (buffer),
|
||||
gegl_buffer_get_height (buffer)));
|
||||
pika_size_entry_set_refval_boundaries (PIKA_SIZE_ENTRY (size_entry), 0, 1.0,
|
||||
MIN (gegl_buffer_get_width (buffer),
|
||||
gegl_buffer_get_height (buffer)) / 2);
|
||||
|
||||
pika_procedure_dialog_fill_frame (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"border-size-frame", "border-size-label",
|
||||
FALSE, "thickness");
|
||||
|
||||
pika_procedure_dialog_get_label (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"bucket-size-label",
|
||||
_("Number of Colors"));
|
||||
|
||||
store = pika_int_store_new ("1", 0, "2", 1, "4", 2, "8", 3,
|
||||
"16", 4, "32", 5, "64", 6, "128", 7,
|
||||
"256", 8, NULL);
|
||||
pika_procedure_dialog_get_int_combo (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"bucket-exponent",
|
||||
PIKA_INT_STORE (store));
|
||||
|
||||
pika_procedure_dialog_fill_frame (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"bucket-size-frame", "bucket-size-label",
|
||||
FALSE, "bucket-exponent");
|
||||
|
||||
pika_procedure_dialog_fill (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"border-size-frame",
|
||||
"bucket-size-frame",
|
||||
NULL);
|
||||
|
||||
gtk_widget_show (dialog);
|
||||
|
||||
run = pika_procedure_dialog_run (PIKA_PROCEDURE_DIALOG (dialog));
|
||||
|
||||
gtk_widget_destroy (dialog);
|
||||
|
||||
return run;
|
||||
}
|
||||
373
plug-ins/common/busy-dialog.c
Normal file
373
plug-ins/common/busy-dialog.c
Normal file
@ -0,0 +1,373 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* busy-dialog.c
|
||||
* Copyright (C) 2018 Ell
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <libpika/pika.h>
|
||||
#include <libpika/pikaui.h>
|
||||
|
||||
#include "libpika/stdplugins-intl.h"
|
||||
|
||||
|
||||
#define PLUG_IN_PROC "plug-in-busy-dialog"
|
||||
#define PLUG_IN_BINARY "busy-dialog"
|
||||
#define PLUG_IN_ROLE "pika-busy-dialog"
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GIOChannel *read_channel;
|
||||
GIOChannel *write_channel;
|
||||
} Context;
|
||||
|
||||
|
||||
typedef struct _BusyDialog BusyDialog;
|
||||
typedef struct _BusyDialogClass BusyDialogClass;
|
||||
|
||||
struct _BusyDialog
|
||||
{
|
||||
PikaPlugIn parent_instance;
|
||||
};
|
||||
|
||||
struct _BusyDialogClass
|
||||
{
|
||||
PikaPlugInClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
#define BUSY_DIALOG_TYPE (busy_dialog_get_type ())
|
||||
#define BUSY_DIALOG (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), BUSY_DIALOG_TYPE, BusyDialog))
|
||||
|
||||
GType busy_dialog_get_type (void) G_GNUC_CONST;
|
||||
|
||||
|
||||
static GList * busy_dialog_query_procedures (PikaPlugIn *plug_in);
|
||||
static PikaProcedure * busy_dialog_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name);
|
||||
static PikaValueArray * busy_dialog_run (PikaProcedure *procedure,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data);
|
||||
|
||||
static PikaPDBStatusType busy_dialog (gint read_fd,
|
||||
gint write_fd,
|
||||
const gchar *message,
|
||||
gboolean cancelable);
|
||||
|
||||
static gboolean busy_dialog_read_channel_notify (GIOChannel *source,
|
||||
GIOCondition condition,
|
||||
Context *context);
|
||||
|
||||
static gboolean busy_dialog_delete_event (GtkDialog *dialog,
|
||||
GdkEvent *event,
|
||||
Context *context);
|
||||
static void busy_dialog_response (GtkDialog *dialog,
|
||||
gint response_id,
|
||||
Context *context);
|
||||
|
||||
|
||||
G_DEFINE_TYPE (BusyDialog, busy_dialog, PIKA_TYPE_PLUG_IN)
|
||||
|
||||
PIKA_MAIN (BUSY_DIALOG_TYPE)
|
||||
DEFINE_STD_SET_I18N
|
||||
|
||||
static void
|
||||
busy_dialog_class_init (BusyDialogClass *klass)
|
||||
{
|
||||
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
|
||||
|
||||
plug_in_class->query_procedures = busy_dialog_query_procedures;
|
||||
plug_in_class->create_procedure = busy_dialog_create_procedure;
|
||||
plug_in_class->set_i18n = STD_SET_I18N;
|
||||
}
|
||||
|
||||
static void
|
||||
busy_dialog_init (BusyDialog *busy_dialog)
|
||||
{
|
||||
}
|
||||
|
||||
static GList *
|
||||
busy_dialog_query_procedures (PikaPlugIn *plug_in)
|
||||
{
|
||||
return g_list_append (NULL, g_strdup (PLUG_IN_PROC));
|
||||
}
|
||||
|
||||
static PikaProcedure *
|
||||
busy_dialog_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name)
|
||||
{
|
||||
PikaProcedure *procedure = NULL;
|
||||
|
||||
if (! strcmp (name, PLUG_IN_PROC))
|
||||
{
|
||||
procedure = pika_procedure_new (plug_in, name,
|
||||
PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
busy_dialog_run, NULL, NULL);
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
"Show a dialog while waiting for an "
|
||||
"operation to finish",
|
||||
"Used by PIKA to display a dialog, "
|
||||
"containing a spinner and a custom "
|
||||
"message, while waiting for an "
|
||||
"ongoing operation to finish. "
|
||||
"Optionally, the dialog may provide "
|
||||
"a \"Cancel\" button, which can be used "
|
||||
"to cancel the operation.",
|
||||
name);
|
||||
pika_procedure_set_attribution (procedure,
|
||||
"Ell",
|
||||
"Ell",
|
||||
"2018");
|
||||
|
||||
PIKA_PROC_ARG_ENUM (procedure, "run-mode",
|
||||
"Run mode",
|
||||
"The run mode",
|
||||
PIKA_TYPE_RUN_MODE,
|
||||
PIKA_RUN_INTERACTIVE,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "read-fd",
|
||||
"The read file descriptor",
|
||||
"The read file descriptor",
|
||||
G_MININT, G_MAXINT, 0,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "write-fd",
|
||||
"The write file descriptor",
|
||||
"The write file descriptor",
|
||||
G_MININT, G_MAXINT, 0,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_STRING (procedure, "message",
|
||||
"The message",
|
||||
"The message",
|
||||
NULL,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_BOOLEAN (procedure, "cancelable",
|
||||
"Whether the dialog is cancelable",
|
||||
"Whether the dialog is cancelable",
|
||||
FALSE,
|
||||
G_PARAM_READWRITE);
|
||||
}
|
||||
|
||||
return procedure;
|
||||
}
|
||||
|
||||
static PikaValueArray *
|
||||
busy_dialog_run (PikaProcedure *procedure,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data)
|
||||
{
|
||||
PikaValueArray *return_vals = NULL;
|
||||
PikaPDBStatusType status = PIKA_PDB_SUCCESS;
|
||||
PikaRunMode run_mode;
|
||||
|
||||
run_mode = PIKA_VALUES_GET_ENUM (args, 0);
|
||||
switch (run_mode)
|
||||
{
|
||||
case PIKA_RUN_INTERACTIVE:
|
||||
case PIKA_RUN_NONINTERACTIVE:
|
||||
case PIKA_RUN_WITH_LAST_VALS:
|
||||
if (pika_value_array_length (args) != 5)
|
||||
{
|
||||
status = PIKA_PDB_CALLING_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
status = busy_dialog (PIKA_VALUES_GET_INT (args, 1),
|
||||
PIKA_VALUES_GET_INT (args, 2),
|
||||
PIKA_VALUES_GET_STRING (args, 3),
|
||||
PIKA_VALUES_GET_BOOLEAN (args, 4));
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
status = PIKA_PDB_CALLING_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
return_vals = pika_procedure_new_return_values (procedure, status, NULL);
|
||||
|
||||
return return_vals;
|
||||
}
|
||||
|
||||
static PikaPDBStatusType
|
||||
busy_dialog (gint read_fd,
|
||||
gint write_fd,
|
||||
const gchar *message,
|
||||
gboolean cancelable)
|
||||
{
|
||||
Context context;
|
||||
GtkWidget *window;
|
||||
GtkWidget *content_area;
|
||||
GtkWidget *vbox;
|
||||
GtkWidget *label;
|
||||
GtkWidget *box;
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
context.read_channel = g_io_channel_win32_new_fd (read_fd);
|
||||
context.write_channel = g_io_channel_win32_new_fd (write_fd);
|
||||
#else
|
||||
context.read_channel = g_io_channel_unix_new (read_fd);
|
||||
context.write_channel = g_io_channel_unix_new (write_fd);
|
||||
#endif
|
||||
|
||||
g_io_channel_set_close_on_unref (context.read_channel, TRUE);
|
||||
g_io_channel_set_close_on_unref (context.write_channel, TRUE);
|
||||
|
||||
/* triggered when the operation is finished in the main app, and we should
|
||||
* quit.
|
||||
*/
|
||||
g_io_add_watch (context.read_channel, G_IO_IN | G_IO_ERR | G_IO_HUP,
|
||||
(GIOFunc) busy_dialog_read_channel_notify,
|
||||
&context);
|
||||
|
||||
/* call gtk_init() before pika_ui_init(), to avoid DESKTOP_STARTUP_ID from
|
||||
* taking effect -- we want the dialog to be prominently displayed above
|
||||
* other plug-in windows.
|
||||
*/
|
||||
gtk_init (NULL, NULL);
|
||||
|
||||
pika_ui_init (PLUG_IN_BINARY);
|
||||
|
||||
/* the main window */
|
||||
if (! cancelable)
|
||||
{
|
||||
window = g_object_new (GTK_TYPE_WINDOW,
|
||||
"title", _("Please Wait"),
|
||||
"skip-taskbar-hint", TRUE,
|
||||
"deletable", FALSE,
|
||||
"resizable", FALSE,
|
||||
"role", "pika-busy-dialog",
|
||||
"type-hint", GDK_WINDOW_TYPE_HINT_DIALOG,
|
||||
"window-position", GTK_WIN_POS_CENTER,
|
||||
NULL);
|
||||
|
||||
g_signal_connect (window, "delete-event", G_CALLBACK (gtk_true), NULL);
|
||||
|
||||
content_area = window;
|
||||
}
|
||||
else
|
||||
{
|
||||
window = g_object_new (GTK_TYPE_DIALOG,
|
||||
"title", _("Please Wait"),
|
||||
"skip-taskbar-hint", TRUE,
|
||||
"resizable", FALSE,
|
||||
"role", "pika-busy-dialog",
|
||||
"window-position", GTK_WIN_POS_CENTER,
|
||||
NULL);
|
||||
|
||||
gtk_dialog_add_button (GTK_DIALOG (window),
|
||||
_("_Cancel"), GTK_RESPONSE_CANCEL);
|
||||
|
||||
g_signal_connect (window, "delete-event",
|
||||
G_CALLBACK (busy_dialog_delete_event),
|
||||
&context);
|
||||
|
||||
g_signal_connect (window, "response",
|
||||
G_CALLBACK (busy_dialog_response),
|
||||
&context);
|
||||
|
||||
content_area = gtk_dialog_get_content_area (GTK_DIALOG (window));
|
||||
}
|
||||
|
||||
/* the main vbox */
|
||||
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 8);
|
||||
gtk_container_set_border_width (GTK_CONTAINER (vbox), 16);
|
||||
gtk_container_add (GTK_CONTAINER (content_area), vbox);
|
||||
gtk_widget_show (vbox);
|
||||
|
||||
/* the title label */
|
||||
label = gtk_label_new (_("Please wait for the operation to complete"));
|
||||
pika_label_set_attributes (GTK_LABEL (label),
|
||||
PANGO_ATTR_WEIGHT, PANGO_WEIGHT_BOLD,
|
||||
-1);
|
||||
gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
|
||||
gtk_widget_show (label);
|
||||
|
||||
/* the busy box */
|
||||
box = pika_busy_box_new (message);
|
||||
gtk_container_set_border_width (GTK_CONTAINER (box), 8);
|
||||
gtk_box_pack_start (GTK_BOX (vbox), box, TRUE, TRUE, 0);
|
||||
gtk_widget_show (box);
|
||||
|
||||
gtk_window_present (GTK_WINDOW (window));
|
||||
|
||||
gtk_main ();
|
||||
|
||||
gtk_widget_destroy (window);
|
||||
|
||||
g_clear_pointer (&context.read_channel, g_io_channel_unref);
|
||||
g_clear_pointer (&context.write_channel, g_io_channel_unref);
|
||||
|
||||
return PIKA_PDB_SUCCESS;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
busy_dialog_read_channel_notify (GIOChannel *source,
|
||||
GIOCondition condition,
|
||||
Context *context)
|
||||
{
|
||||
gtk_main_quit ();
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
busy_dialog_delete_event (GtkDialog *dialog,
|
||||
GdkEvent *event,
|
||||
Context *context)
|
||||
{
|
||||
gtk_dialog_response (dialog, GTK_RESPONSE_CANCEL);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
busy_dialog_response (GtkDialog *dialog,
|
||||
gint response_id,
|
||||
Context *context)
|
||||
{
|
||||
switch (response_id)
|
||||
{
|
||||
case GTK_RESPONSE_CANCEL:
|
||||
{
|
||||
GtkWidget *button;
|
||||
|
||||
gtk_dialog_set_response_sensitive (dialog, GTK_RESPONSE_CANCEL, FALSE);
|
||||
|
||||
button = gtk_dialog_get_widget_for_response (dialog,
|
||||
GTK_RESPONSE_CANCEL);
|
||||
gtk_button_set_label (GTK_BUTTON (button), _("Canceling..."));
|
||||
|
||||
/* signal the cancellation request to the main app */
|
||||
g_clear_pointer (&context->write_channel, g_io_channel_unref);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
562
plug-ins/common/checkerboard.c
Normal file
562
plug-ins/common/checkerboard.c
Normal file
@ -0,0 +1,562 @@
|
||||
/*
|
||||
* This is a plug-in for PIKA.
|
||||
*
|
||||
* Copyright (C) 1997 Brent Burton & the Edward Blevins
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <libpika/pika.h>
|
||||
#include <libpika/pikaui.h>
|
||||
|
||||
#include "libpika/stdplugins-intl.h"
|
||||
|
||||
|
||||
#define PLUG_IN_PROC "plug-in-checkerboard"
|
||||
#define PLUG_IN_BINARY "checkerboard"
|
||||
#define PLUG_IN_ROLE "pika-checkerboard"
|
||||
|
||||
|
||||
typedef struct _Checkerboard Checkerboard;
|
||||
typedef struct _CheckerboardClass CheckerboardClass;
|
||||
|
||||
struct _Checkerboard
|
||||
{
|
||||
PikaPlugIn parent_instance;
|
||||
};
|
||||
|
||||
struct _CheckerboardClass
|
||||
{
|
||||
PikaPlugInClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
#define CHECKERBOARD_TYPE (checkerboard_get_type ())
|
||||
#define CHECKERBOARD (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CHECKERBOARD_TYPE, Checkerboard))
|
||||
|
||||
GType checkerboard_get_type (void) G_GNUC_CONST;
|
||||
|
||||
static GList * checkerboard_query_procedures (PikaPlugIn *plug_in);
|
||||
static PikaProcedure * checkerboard_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name);
|
||||
|
||||
static PikaValueArray * checkerboard_run (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data);
|
||||
|
||||
static void do_checkerboard_pattern (GObject *config,
|
||||
PikaDrawable *drawable,
|
||||
PikaPreview *preview);
|
||||
static void do_checkerboard_preview (GtkWidget *widget,
|
||||
GObject *config);
|
||||
static gint inblock (gint pos,
|
||||
gint size);
|
||||
|
||||
static gboolean checkerboard_dialog (PikaProcedure *procedure,
|
||||
GObject *config,
|
||||
PikaImage *image,
|
||||
PikaDrawable *drawable);
|
||||
|
||||
|
||||
G_DEFINE_TYPE (Checkerboard, checkerboard, PIKA_TYPE_PLUG_IN)
|
||||
|
||||
PIKA_MAIN (CHECKERBOARD_TYPE)
|
||||
DEFINE_STD_SET_I18N
|
||||
|
||||
|
||||
static void
|
||||
checkerboard_class_init (CheckerboardClass *klass)
|
||||
{
|
||||
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
|
||||
|
||||
plug_in_class->query_procedures = checkerboard_query_procedures;
|
||||
plug_in_class->create_procedure = checkerboard_create_procedure;
|
||||
plug_in_class->set_i18n = STD_SET_I18N;
|
||||
}
|
||||
|
||||
static void
|
||||
checkerboard_init (Checkerboard *checkerboard)
|
||||
{
|
||||
}
|
||||
|
||||
static GList *
|
||||
checkerboard_query_procedures (PikaPlugIn *plug_in)
|
||||
{
|
||||
return g_list_append (NULL, g_strdup (PLUG_IN_PROC));
|
||||
}
|
||||
|
||||
static PikaProcedure *
|
||||
checkerboard_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name)
|
||||
{
|
||||
PikaProcedure *procedure = NULL;
|
||||
|
||||
if (! strcmp (name, PLUG_IN_PROC))
|
||||
{
|
||||
procedure = pika_image_procedure_new (plug_in, name,
|
||||
PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
checkerboard_run, NULL, NULL);
|
||||
|
||||
pika_procedure_set_image_types (procedure, "RGB*, GRAY*");
|
||||
pika_procedure_set_sensitivity_mask (procedure,
|
||||
PIKA_PROCEDURE_SENSITIVE_DRAWABLE);
|
||||
|
||||
pika_procedure_set_menu_label (procedure,
|
||||
_("_Checkerboard (legacy)..."));
|
||||
pika_procedure_add_menu_path (procedure,
|
||||
"<Image>/Filters/Render/Pattern");
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
_("Create a checkerboard pattern"),
|
||||
"More here later",
|
||||
name);
|
||||
pika_procedure_set_attribution (procedure,
|
||||
"Brent Burton & the Edward Blevins",
|
||||
"Brent Burton & the Edward Blevins",
|
||||
"1997");
|
||||
|
||||
PIKA_PROC_ARG_BOOLEAN (procedure, "psychobily",
|
||||
_("_Psychobilly"),
|
||||
_("Render a psychobilly checkerboard"),
|
||||
FALSE,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "check-size",
|
||||
_("_Size"),
|
||||
_("Size of the checks"),
|
||||
1, PIKA_MAX_IMAGE_SIZE, 10,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_AUX_ARG_UNIT (procedure, "check-size-unit",
|
||||
_("Check size unit of measure"),
|
||||
_("Check size unit of measure"),
|
||||
TRUE, TRUE, PIKA_UNIT_PIXEL,
|
||||
PIKA_PARAM_READWRITE);
|
||||
}
|
||||
|
||||
return procedure;
|
||||
}
|
||||
|
||||
static PikaValueArray *
|
||||
checkerboard_run (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data)
|
||||
{
|
||||
PikaProcedureConfig *config;
|
||||
PikaDrawable *drawable;
|
||||
|
||||
gegl_init (NULL, NULL);
|
||||
|
||||
if (n_drawables != 1)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
g_set_error (&error, PIKA_PLUG_IN_ERROR, 0,
|
||||
_("Procedure '%s' only works with one drawable."),
|
||||
PLUG_IN_PROC);
|
||||
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_CALLING_ERROR,
|
||||
error);
|
||||
}
|
||||
else
|
||||
{
|
||||
drawable = drawables[0];
|
||||
}
|
||||
|
||||
config = pika_procedure_create_config (procedure);
|
||||
pika_procedure_config_begin_run (config, NULL, run_mode, args);
|
||||
|
||||
switch (run_mode)
|
||||
{
|
||||
case PIKA_RUN_INTERACTIVE:
|
||||
if (! checkerboard_dialog (procedure, G_OBJECT (config), image,
|
||||
drawable))
|
||||
{
|
||||
pika_procedure_config_end_run (config, PIKA_PDB_CANCEL);
|
||||
g_object_unref (config);
|
||||
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_CANCEL,
|
||||
NULL);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (pika_drawable_is_rgb (drawable) ||
|
||||
pika_drawable_is_gray (drawable))
|
||||
{
|
||||
do_checkerboard_pattern (G_OBJECT (config), drawable, NULL);
|
||||
|
||||
if (run_mode != PIKA_RUN_NONINTERACTIVE)
|
||||
pika_displays_flush ();
|
||||
}
|
||||
else
|
||||
{
|
||||
pika_procedure_config_end_run (config, PIKA_PDB_EXECUTION_ERROR);
|
||||
g_object_unref (config);
|
||||
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_EXECUTION_ERROR,
|
||||
NULL);
|
||||
}
|
||||
|
||||
pika_procedure_config_end_run (config, PIKA_PDB_SUCCESS);
|
||||
g_object_unref (config);
|
||||
|
||||
return pika_procedure_new_return_values (procedure, PIKA_PDB_SUCCESS, NULL);
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
guchar fg[4];
|
||||
guchar bg[4];
|
||||
} CheckerboardParam_t;
|
||||
|
||||
static void
|
||||
checkerboard_func (gint x,
|
||||
gint y,
|
||||
guchar *dest,
|
||||
gint bpp,
|
||||
gint size,
|
||||
gboolean mode,
|
||||
gpointer data)
|
||||
{
|
||||
CheckerboardParam_t *param = (CheckerboardParam_t*) data;
|
||||
|
||||
gint val, xp, yp;
|
||||
gint b;
|
||||
|
||||
if (mode)
|
||||
{
|
||||
/* Psychobilly Mode */
|
||||
val = (inblock (x, size) != inblock (y, size));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Normal, regular checkerboard mode.
|
||||
* Determine base factor (even or odd) of block
|
||||
* this x/y position is in.
|
||||
*/
|
||||
xp = x / size;
|
||||
yp = y / size;
|
||||
|
||||
/* if both even or odd, color sqr */
|
||||
val = ( (xp & 1) != (yp & 1) );
|
||||
}
|
||||
|
||||
for (b = 0; b < bpp; b++)
|
||||
dest[b] = val ? param->fg[b] : param->bg[b];
|
||||
}
|
||||
|
||||
static void
|
||||
do_checkerboard_pattern (GObject *config,
|
||||
PikaDrawable *drawable,
|
||||
PikaPreview *preview)
|
||||
{
|
||||
CheckerboardParam_t param;
|
||||
PikaRGB fg, bg;
|
||||
const Babl *format;
|
||||
gint bpp;
|
||||
gboolean mode = FALSE;
|
||||
gint size = 10;
|
||||
|
||||
if (config)
|
||||
g_object_get (config,
|
||||
"check-size", &size,
|
||||
"psychobily", &mode,
|
||||
NULL);
|
||||
|
||||
pika_context_get_background (&bg);
|
||||
pika_context_get_foreground (&fg);
|
||||
|
||||
if (pika_drawable_is_gray (drawable))
|
||||
{
|
||||
param.bg[0] = pika_rgb_luminance_uchar (&bg);
|
||||
pika_rgba_get_uchar (&bg, NULL, NULL, NULL, param.bg + 1);
|
||||
|
||||
param.fg[0] = pika_rgb_luminance_uchar (&fg);
|
||||
pika_rgba_get_uchar (&fg, NULL, NULL, NULL, param.fg + 3);
|
||||
|
||||
if (pika_drawable_has_alpha (drawable))
|
||||
format = babl_format ("R'G'B'A u8");
|
||||
else
|
||||
format = babl_format ("R'G'B' u8");
|
||||
}
|
||||
else
|
||||
{
|
||||
pika_rgba_get_uchar (&bg,
|
||||
param.bg, param.bg + 1, param.bg + 2, param.bg + 1);
|
||||
|
||||
pika_rgba_get_uchar (&fg,
|
||||
param.fg, param.fg + 1, param.fg + 2, param.fg + 3);
|
||||
|
||||
if (pika_drawable_has_alpha (drawable))
|
||||
format = babl_format ("Y'A u8");
|
||||
else
|
||||
format = babl_format ("Y' u8");
|
||||
}
|
||||
|
||||
bpp = babl_format_get_bytes_per_pixel (format);
|
||||
|
||||
if (size < 1)
|
||||
{
|
||||
/* make size 1 to prevent division by zero */
|
||||
size = 1;
|
||||
}
|
||||
|
||||
if (preview)
|
||||
{
|
||||
gint x1, y1;
|
||||
gint width, height;
|
||||
gint i;
|
||||
guchar *buffer;
|
||||
|
||||
pika_preview_get_position (preview, &x1, &y1);
|
||||
pika_preview_get_size (preview, &width, &height);
|
||||
bpp = pika_drawable_get_bpp (drawable);
|
||||
buffer = g_new (guchar, width * height * bpp);
|
||||
|
||||
for (i = 0; i < width * height; i++)
|
||||
{
|
||||
checkerboard_func (x1 + i % width,
|
||||
y1 + i / width,
|
||||
buffer + i * bpp,
|
||||
bpp, size, mode,
|
||||
¶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;
|
||||
}
|
||||
2661
plug-ins/common/cml-explorer.c
Normal file
2661
plug-ins/common/cml-explorer.c
Normal file
File diff suppressed because it is too large
Load Diff
845
plug-ins/common/colormap-remap.c
Normal file
845
plug-ins/common/colormap-remap.c
Normal file
@ -0,0 +1,845 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Colormap remapping plug-in
|
||||
* Copyright (C) 2006 Mukund Sivaraman <muks@mukund.org>
|
||||
*
|
||||
* This plug-in takes the colormap and lets you move colors from one index
|
||||
* to another while keeping the original image visually unmodified.
|
||||
*
|
||||
* Such functionality is useful for creating graphics files for applications
|
||||
* which expect certain indices to contain some specific colors.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <libpika/pika.h>
|
||||
#include <libpika/pikaui.h>
|
||||
|
||||
#include "libpika/stdplugins-intl.h"
|
||||
|
||||
|
||||
#define PLUG_IN_PROC_REMAP "plug-in-colormap-remap"
|
||||
#define PLUG_IN_PROC_SWAP "plug-in-colormap-swap"
|
||||
#define PLUG_IN_BINARY "colormap-remap"
|
||||
#define PLUG_IN_ROLE "pika-colormap-remap"
|
||||
|
||||
struct _PikaRemap
|
||||
{
|
||||
PikaPlugIn parent_instance;
|
||||
|
||||
GtkApplication *app;
|
||||
GtkWindow *window;
|
||||
PikaImage *image;
|
||||
guchar map[256];
|
||||
gint n_cols;
|
||||
|
||||
GMenu *menu;
|
||||
};
|
||||
|
||||
|
||||
#define PIKA_TYPE_REMAP (pika_remap_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (PikaRemap, pika_remap, PIKA, REMAP, PikaPlugIn)
|
||||
|
||||
|
||||
GType remap_get_type (void) G_GNUC_CONST;
|
||||
|
||||
static void pika_remap_finalize (GObject *object);
|
||||
|
||||
static GList * remap_query_procedures (PikaPlugIn *plug_in);
|
||||
static PikaProcedure * remap_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name);
|
||||
|
||||
static PikaValueArray * remap_run (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data);
|
||||
|
||||
static gboolean real_remap (PikaImage *image,
|
||||
gint num_colors,
|
||||
guchar *map);
|
||||
|
||||
static gboolean remap_dialog (PikaImage *image,
|
||||
guchar *map,
|
||||
PikaRemap *remap);
|
||||
|
||||
static void remap_sort_hue_action (GSimpleAction *action,
|
||||
GVariant *parameter,
|
||||
gpointer user_data);
|
||||
static void remap_sort_sat_action (GSimpleAction *action,
|
||||
GVariant *parameter,
|
||||
gpointer user_data);
|
||||
static void remap_sort_val_action (GSimpleAction *action,
|
||||
GVariant *parameter,
|
||||
gpointer user_data);
|
||||
static void remap_reverse_action (GSimpleAction *action,
|
||||
GVariant *parameter,
|
||||
gpointer user_data);
|
||||
static void remap_reset_action (GSimpleAction *action,
|
||||
GVariant *parameter,
|
||||
gpointer user_data);
|
||||
|
||||
static void on_app_activate (GApplication *gapp,
|
||||
gpointer user_data);
|
||||
|
||||
|
||||
|
||||
G_DEFINE_TYPE (PikaRemap, pika_remap, PIKA_TYPE_PLUG_IN)
|
||||
|
||||
PIKA_MAIN (PIKA_TYPE_REMAP)
|
||||
DEFINE_STD_SET_I18N
|
||||
|
||||
|
||||
static GtkWindow *window = NULL;
|
||||
static GtkListStore *store = NULL;
|
||||
static gboolean remap_ok = FALSE;
|
||||
|
||||
static const GActionEntry ACTIONS[] =
|
||||
{
|
||||
{ "sort-hue", remap_sort_hue_action },
|
||||
{ "sort-sat", remap_sort_sat_action },
|
||||
{ "sort-val", remap_sort_val_action },
|
||||
|
||||
{ "reverse", remap_reverse_action },
|
||||
|
||||
{ "reset", remap_reset_action },
|
||||
};
|
||||
|
||||
static void
|
||||
pika_remap_class_init (PikaRemapClass *klass)
|
||||
{
|
||||
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->finalize = pika_remap_finalize;
|
||||
|
||||
plug_in_class->query_procedures = remap_query_procedures;
|
||||
plug_in_class->create_procedure = remap_create_procedure;
|
||||
plug_in_class->set_i18n = STD_SET_I18N;
|
||||
}
|
||||
|
||||
static void
|
||||
pika_remap_finalize (GObject *object)
|
||||
{
|
||||
PikaRemap *remap = PIKA_REMAP (object);
|
||||
|
||||
G_OBJECT_CLASS (pika_remap_parent_class)->finalize (object);
|
||||
|
||||
g_clear_object (&remap->menu);
|
||||
}
|
||||
|
||||
static void
|
||||
pika_remap_init (PikaRemap *remap)
|
||||
{
|
||||
}
|
||||
|
||||
static GList *
|
||||
remap_query_procedures (PikaPlugIn *plug_in)
|
||||
{
|
||||
GList *list = NULL;
|
||||
|
||||
list = g_list_prepend (list, g_strdup (PLUG_IN_PROC_REMAP));
|
||||
list = g_list_prepend (list, g_strdup (PLUG_IN_PROC_SWAP));
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
static PikaProcedure *
|
||||
remap_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name)
|
||||
{
|
||||
PikaProcedure *procedure = NULL;
|
||||
|
||||
if (! strcmp (name, PLUG_IN_PROC_REMAP))
|
||||
{
|
||||
procedure = pika_image_procedure_new (plug_in, name,
|
||||
PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
remap_run, NULL, NULL);
|
||||
|
||||
pika_procedure_set_image_types (procedure, "INDEXED*");
|
||||
pika_procedure_set_sensitivity_mask (procedure,
|
||||
PIKA_PROCEDURE_SENSITIVE_DRAWABLE |
|
||||
PIKA_PROCEDURE_SENSITIVE_DRAWABLES |
|
||||
PIKA_PROCEDURE_SENSITIVE_NO_DRAWABLES);
|
||||
|
||||
pika_procedure_set_menu_label (procedure, _("R_earrange Colormap..."));
|
||||
pika_procedure_set_icon_name (procedure, PIKA_ICON_COLORMAP);
|
||||
pika_procedure_add_menu_path (procedure, "<Image>/Colors/Map/[Colormap]");
|
||||
pika_procedure_add_menu_path (procedure, "<Colormap>");
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
_("Rearrange the colormap"),
|
||||
"This procedure takes an indexed "
|
||||
"image and lets you alter the "
|
||||
"positions of colors in the colormap "
|
||||
"without visually changing the image.",
|
||||
name);
|
||||
pika_procedure_set_attribution (procedure,
|
||||
"Mukund Sivaraman <muks@mukund.org>",
|
||||
"Mukund Sivaraman <muks@mukund.org>",
|
||||
"June 2006");
|
||||
|
||||
PIKA_PROC_ARG_BYTES (procedure, "map",
|
||||
"Map",
|
||||
"Remap array for the colormap",
|
||||
G_PARAM_READWRITE);
|
||||
}
|
||||
else if (! strcmp (name, PLUG_IN_PROC_SWAP))
|
||||
{
|
||||
procedure = pika_image_procedure_new (plug_in, name,
|
||||
PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
remap_run, NULL, NULL);
|
||||
|
||||
pika_procedure_set_image_types (procedure, "INDEXED*");
|
||||
pika_procedure_set_sensitivity_mask (procedure,
|
||||
PIKA_PROCEDURE_SENSITIVE_DRAWABLE |
|
||||
PIKA_PROCEDURE_SENSITIVE_DRAWABLES |
|
||||
PIKA_PROCEDURE_SENSITIVE_NO_DRAWABLES);
|
||||
|
||||
pika_procedure_set_menu_label (procedure, _("_Swap Colors"));
|
||||
pika_procedure_set_icon_name (procedure, PIKA_ICON_COLORMAP);
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
_("Swap two colors in the colormap"),
|
||||
"This procedure takes an indexed "
|
||||
"image and lets you swap the "
|
||||
"positions of two colors in the "
|
||||
"colormap without visually changing "
|
||||
"the image.",
|
||||
name);
|
||||
pika_procedure_set_attribution (procedure,
|
||||
"Mukund Sivaraman <muks@mukund.org>",
|
||||
"Mukund Sivaraman <muks@mukund.org>",
|
||||
"June 2006");
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "index1",
|
||||
"Index 1",
|
||||
"First index in the colormap",
|
||||
0, 255, 0,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "index2",
|
||||
"Index 2",
|
||||
"Second (other) index in the colormap",
|
||||
0, 255, 0,
|
||||
G_PARAM_READWRITE);
|
||||
}
|
||||
|
||||
return procedure;
|
||||
}
|
||||
|
||||
static PikaValueArray *
|
||||
remap_run (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data)
|
||||
{
|
||||
GMenu *section;
|
||||
gint i;
|
||||
|
||||
PikaRemap *remap = PIKA_REMAP (run_data);
|
||||
|
||||
remap = PIKA_REMAP (pika_procedure_get_plug_in (procedure));
|
||||
#if GLIB_CHECK_VERSION(2,74,0)
|
||||
remap->app = gtk_application_new (NULL, G_APPLICATION_DEFAULT_FLAGS);
|
||||
#else
|
||||
remap->app = gtk_application_new (NULL, G_APPLICATION_FLAGS_NONE);
|
||||
#endif
|
||||
remap->image = image;
|
||||
|
||||
remap->menu = g_menu_new ();
|
||||
|
||||
section = g_menu_new ();
|
||||
g_menu_append (section, _("Sort on Hue"), "win.sort-hue");
|
||||
g_menu_append (section, _("Sort on Saturation"), "win.sort-sat");
|
||||
g_menu_append (section, _("Sort on Value"), "win.sort-val");
|
||||
g_menu_append_section (remap->menu, NULL, G_MENU_MODEL (section));
|
||||
g_clear_object (§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 });
|
||||
}
|
||||
1513
plug-ins/common/compose.c
Normal file
1513
plug-ins/common/compose.c
Normal file
File diff suppressed because it is too large
Load Diff
882
plug-ins/common/contrast-retinex.c
Normal file
882
plug-ins/common/contrast-retinex.c
Normal file
@ -0,0 +1,882 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "libpika/pika.h"
|
||||
#include "libpika/pikaui.h"
|
||||
|
||||
#include "libpika/stdplugins-intl.h"
|
||||
|
||||
|
||||
#define PLUG_IN_PROC "plug-in-retinex"
|
||||
#define PLUG_IN_BINARY "contrast-retinex"
|
||||
#define PLUG_IN_ROLE "pika-contrast-retinex"
|
||||
#define MAX_RETINEX_SCALES 8
|
||||
#define MIN_GAUSSIAN_SCALE 16
|
||||
#define MAX_GAUSSIAN_SCALE 250
|
||||
|
||||
|
||||
typedef enum
|
||||
{
|
||||
filter_uniform,
|
||||
filter_low,
|
||||
filter_high
|
||||
} FilterMode;
|
||||
|
||||
/*
|
||||
Definit comment sont repartis les
|
||||
differents filtres en fonction de
|
||||
l'echelle (~= ecart type de la gaussienne)
|
||||
*/
|
||||
#define RETINEX_UNIFORM 0
|
||||
#define RETINEX_LOW 1
|
||||
#define RETINEX_HIGH 2
|
||||
|
||||
static gfloat RetinexScales[MAX_RETINEX_SCALES];
|
||||
|
||||
typedef struct
|
||||
{
|
||||
gint N;
|
||||
gfloat sigma;
|
||||
gdouble B;
|
||||
gdouble b[4];
|
||||
} gauss3_coefs;
|
||||
|
||||
|
||||
typedef struct _Retinex Retinex;
|
||||
typedef struct _RetinexClass RetinexClass;
|
||||
|
||||
struct _Retinex
|
||||
{
|
||||
PikaPlugIn parent_instance;
|
||||
};
|
||||
|
||||
struct _RetinexClass
|
||||
{
|
||||
PikaPlugInClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
#define RETINEX_TYPE (retinex_get_type ())
|
||||
#define RETINEX (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), RETINEX_TYPE, Retinex))
|
||||
|
||||
GType retinex_get_type (void) G_GNUC_CONST;
|
||||
|
||||
static GList * retinex_query_procedures (PikaPlugIn *plug_in);
|
||||
static PikaProcedure * retinex_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name);
|
||||
|
||||
static PikaValueArray * retinex_run (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data);
|
||||
|
||||
static gboolean retinex_dialog (PikaProcedure *procedure,
|
||||
GObject *config,
|
||||
PikaDrawable *drawable);
|
||||
static void retinex (GObject *config,
|
||||
PikaDrawable *drawable,
|
||||
PikaPreview *preview);
|
||||
static void retinex_preview (GtkWidget *widget,
|
||||
GObject *config);
|
||||
|
||||
static void retinex_scales_distribution (gfloat *scales,
|
||||
gint nscales,
|
||||
gint mode,
|
||||
gint s);
|
||||
|
||||
static void compute_mean_var (gfloat *src,
|
||||
gfloat *mean,
|
||||
gfloat *var,
|
||||
gint size,
|
||||
gint bytes);
|
||||
|
||||
static void compute_coefs3 (gauss3_coefs *c,
|
||||
gfloat sigma);
|
||||
|
||||
static void gausssmooth (gfloat *in,
|
||||
gfloat *out,
|
||||
gint size,
|
||||
gint rowtride,
|
||||
gauss3_coefs *c);
|
||||
|
||||
|
||||
/*
|
||||
* MSRCR = MultiScale Retinex with Color Restoration
|
||||
*/
|
||||
static void MSRCR (GObject *config,
|
||||
guchar *src,
|
||||
gint width,
|
||||
gint height,
|
||||
gint bytes,
|
||||
gboolean preview_mode);
|
||||
|
||||
|
||||
G_DEFINE_TYPE (Retinex, retinex, PIKA_TYPE_PLUG_IN)
|
||||
|
||||
PIKA_MAIN (RETINEX_TYPE)
|
||||
DEFINE_STD_SET_I18N
|
||||
|
||||
|
||||
static void
|
||||
retinex_class_init (RetinexClass *klass)
|
||||
{
|
||||
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
|
||||
|
||||
plug_in_class->query_procedures = retinex_query_procedures;
|
||||
plug_in_class->create_procedure = retinex_create_procedure;
|
||||
plug_in_class->set_i18n = STD_SET_I18N;
|
||||
}
|
||||
|
||||
static void
|
||||
retinex_init (Retinex *retinex)
|
||||
{
|
||||
}
|
||||
|
||||
static GList *
|
||||
retinex_query_procedures (PikaPlugIn *plug_in)
|
||||
{
|
||||
return g_list_append (NULL, g_strdup (PLUG_IN_PROC));
|
||||
}
|
||||
|
||||
static PikaProcedure *
|
||||
retinex_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name)
|
||||
{
|
||||
PikaProcedure *procedure = NULL;
|
||||
|
||||
if (! strcmp (name, PLUG_IN_PROC))
|
||||
{
|
||||
procedure = pika_image_procedure_new (plug_in, name,
|
||||
PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
retinex_run, NULL, NULL);
|
||||
|
||||
pika_procedure_set_image_types (procedure, "RGB*");
|
||||
pika_procedure_set_sensitivity_mask (procedure,
|
||||
PIKA_PROCEDURE_SENSITIVE_DRAWABLE);
|
||||
|
||||
pika_procedure_set_menu_label (procedure, _("Retine_x..."));
|
||||
pika_procedure_add_menu_path (procedure, "<Image>/Colors/Tone Mapping");
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
_("Enhance contrast using the "
|
||||
"Retinex method"),
|
||||
_("The Retinex Image Enhancement "
|
||||
"Algorithm is an automatic image "
|
||||
"enhancement method that enhances "
|
||||
"a digital image in terms of dynamic "
|
||||
"range compression, color independence "
|
||||
"from the spectral distribution of the "
|
||||
"scene illuminant, and color/lightness "
|
||||
"rendition."),
|
||||
name);
|
||||
pika_procedure_set_attribution (procedure,
|
||||
"Fabien Pelisson",
|
||||
"Fabien Pelisson",
|
||||
"2003");
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "scale",
|
||||
_("Scal_e"),
|
||||
_("Biggest scale value"),
|
||||
MIN_GAUSSIAN_SCALE, MAX_GAUSSIAN_SCALE, 240,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "nscales",
|
||||
_("Scale _division"),
|
||||
_("Number of scales"),
|
||||
0, MAX_RETINEX_SCALES, 3,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "scales-mode",
|
||||
_("Le_vel"),
|
||||
_("Retinex distribution through scales "
|
||||
"{ Uniform (0), Low (1), High (2) }"),
|
||||
RETINEX_UNIFORM, RETINEX_HIGH, RETINEX_UNIFORM,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_DOUBLE (procedure, "cvar",
|
||||
_("Dy_namic"),
|
||||
_("Variance value"),
|
||||
0, 4, 1.2,
|
||||
G_PARAM_READWRITE);
|
||||
}
|
||||
|
||||
return procedure;
|
||||
}
|
||||
|
||||
static PikaValueArray *
|
||||
retinex_run (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data)
|
||||
{
|
||||
PikaProcedureConfig *config;
|
||||
PikaDrawable *drawable;
|
||||
gint x, y, width, height;
|
||||
|
||||
gegl_init (NULL, NULL);
|
||||
|
||||
config = pika_procedure_create_config (procedure);
|
||||
pika_procedure_config_begin_run (config, NULL, run_mode, args);
|
||||
|
||||
if (n_drawables != 1)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
pika_procedure_config_end_run (config, PIKA_PDB_EXECUTION_ERROR);
|
||||
g_object_unref (config);
|
||||
|
||||
g_set_error (&error, PIKA_PLUG_IN_ERROR, 0,
|
||||
_("Procedure '%s' only works with one drawable."),
|
||||
PLUG_IN_PROC);
|
||||
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_CALLING_ERROR,
|
||||
error);
|
||||
}
|
||||
else
|
||||
{
|
||||
drawable = drawables[0];
|
||||
}
|
||||
|
||||
if (! pika_drawable_mask_intersect (drawable, &x, &y, &width, &height) ||
|
||||
width < MIN_GAUSSIAN_SCALE ||
|
||||
height < MIN_GAUSSIAN_SCALE)
|
||||
{
|
||||
pika_procedure_config_end_run (config, PIKA_PDB_EXECUTION_ERROR);
|
||||
g_object_unref (config);
|
||||
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_EXECUTION_ERROR,
|
||||
NULL);
|
||||
}
|
||||
|
||||
switch (run_mode)
|
||||
{
|
||||
case PIKA_RUN_INTERACTIVE:
|
||||
if (! retinex_dialog (procedure, G_OBJECT (config), drawable))
|
||||
{
|
||||
pika_procedure_config_end_run (config, PIKA_PDB_CANCEL);
|
||||
g_object_unref (config);
|
||||
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_CANCEL,
|
||||
NULL);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (pika_drawable_is_rgb (drawable))
|
||||
{
|
||||
pika_progress_init (_("Retinex"));
|
||||
|
||||
retinex (G_OBJECT (config), drawable, NULL);
|
||||
|
||||
if (run_mode != PIKA_RUN_NONINTERACTIVE)
|
||||
pika_displays_flush ();
|
||||
}
|
||||
else
|
||||
{
|
||||
pika_procedure_config_end_run (config, PIKA_PDB_EXECUTION_ERROR);
|
||||
g_object_unref (config);
|
||||
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_EXECUTION_ERROR,
|
||||
NULL);
|
||||
}
|
||||
|
||||
pika_procedure_config_end_run (config, PIKA_PDB_SUCCESS);
|
||||
g_object_unref (config);
|
||||
|
||||
return pika_procedure_new_return_values (procedure, PIKA_PDB_SUCCESS, NULL);
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
retinex_dialog (PikaProcedure *procedure,
|
||||
GObject *config,
|
||||
PikaDrawable *drawable)
|
||||
{
|
||||
GtkWidget *dialog;
|
||||
GtkWidget *preview;
|
||||
GtkWidget *combo;
|
||||
GtkWidget *scale;
|
||||
GtkListStore *store;
|
||||
gboolean run;
|
||||
|
||||
pika_ui_init (PLUG_IN_BINARY);
|
||||
|
||||
dialog = pika_procedure_dialog_new (procedure,
|
||||
PIKA_PROCEDURE_CONFIG (config),
|
||||
_("Retinex Image Enhancement"));
|
||||
|
||||
pika_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
|
||||
GTK_RESPONSE_OK,
|
||||
GTK_RESPONSE_CANCEL,
|
||||
-1);
|
||||
|
||||
pika_window_set_transient (GTK_WINDOW (dialog));
|
||||
|
||||
preview = pika_zoom_preview_new_from_drawable (drawable);
|
||||
gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
|
||||
preview, TRUE, TRUE, 0);
|
||||
gtk_widget_set_margin_bottom (preview, 12);
|
||||
gtk_widget_show (preview);
|
||||
|
||||
store = pika_int_store_new (_("Uniform"), filter_uniform,
|
||||
_("Low"), filter_low,
|
||||
_("High"), filter_high,
|
||||
NULL);
|
||||
combo = pika_procedure_dialog_get_int_combo (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"scales-mode",
|
||||
PIKA_INT_STORE (store));
|
||||
gtk_widget_set_margin_bottom (combo, 12);
|
||||
|
||||
scale = pika_procedure_dialog_get_scale_entry (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"scale", 1.0);
|
||||
gtk_widget_set_margin_bottom (scale, 12);
|
||||
|
||||
scale = pika_procedure_dialog_get_scale_entry (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"nscales", 1.0);
|
||||
gtk_widget_set_margin_bottom (scale, 12);
|
||||
|
||||
scale = pika_procedure_dialog_get_scale_entry (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"cvar", 1.0);
|
||||
gtk_widget_set_margin_bottom (scale, 12);
|
||||
|
||||
pika_procedure_dialog_fill_box (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"retinex-vbox", "scales-mode",
|
||||
"scale", "nscales", "cvar", NULL);
|
||||
|
||||
g_object_set_data (config, "drawable", drawable);
|
||||
|
||||
g_signal_connect (preview, "invalidated",
|
||||
G_CALLBACK (retinex_preview),
|
||||
config);
|
||||
|
||||
g_signal_connect_swapped (config, "notify",
|
||||
G_CALLBACK (pika_preview_invalidate),
|
||||
preview);
|
||||
|
||||
pika_procedure_dialog_fill (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"retinex-vbox",
|
||||
NULL);
|
||||
|
||||
gtk_widget_show (dialog);
|
||||
|
||||
run = pika_procedure_dialog_run (PIKA_PROCEDURE_DIALOG (dialog));
|
||||
|
||||
gtk_widget_destroy (dialog);
|
||||
|
||||
return run;
|
||||
}
|
||||
|
||||
/*
|
||||
* Applies the algorithm
|
||||
*/
|
||||
static void
|
||||
retinex (GObject *config,
|
||||
PikaDrawable *drawable,
|
||||
PikaPreview *preview)
|
||||
{
|
||||
GeglBuffer *src_buffer;
|
||||
GeglBuffer *dest_buffer;
|
||||
const Babl *format;
|
||||
guchar *src = NULL;
|
||||
guchar *psrc = NULL;
|
||||
gint x, y, width, height;
|
||||
gint size, bytes;
|
||||
|
||||
/*
|
||||
* Get the size of the current image or its selection.
|
||||
*/
|
||||
if (preview)
|
||||
{
|
||||
src = pika_zoom_preview_get_source (PIKA_ZOOM_PREVIEW (preview),
|
||||
&width, &height, &bytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (! pika_drawable_mask_intersect (drawable,
|
||||
&x, &y, &width, &height))
|
||||
return;
|
||||
|
||||
if (pika_drawable_has_alpha (drawable))
|
||||
format = babl_format ("R'G'B'A u8");
|
||||
else
|
||||
format = babl_format ("R'G'B' u8");
|
||||
|
||||
bytes = babl_format_get_bytes_per_pixel (format);
|
||||
|
||||
/* Allocate memory */
|
||||
size = width * height * bytes;
|
||||
src = g_try_malloc (sizeof (guchar) * size);
|
||||
|
||||
if (src == NULL)
|
||||
{
|
||||
g_warning ("Failed to allocate memory");
|
||||
return;
|
||||
}
|
||||
|
||||
memset (src, 0, sizeof (guchar) * size);
|
||||
|
||||
/* Fill allocated memory with pixel data */
|
||||
src_buffer = pika_drawable_get_buffer (drawable);
|
||||
|
||||
gegl_buffer_get (src_buffer, GEGL_RECTANGLE (x, y, width, height), 1.0,
|
||||
format, src,
|
||||
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
|
||||
}
|
||||
|
||||
/*
|
||||
Algorithm for Multi-scale Retinex with color Restoration (MSRCR).
|
||||
*/
|
||||
psrc = src;
|
||||
MSRCR (config, psrc, width, height, bytes, preview != NULL);
|
||||
|
||||
if (preview)
|
||||
{
|
||||
pika_preview_draw_buffer (preview, psrc, width * bytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
dest_buffer = pika_drawable_get_shadow_buffer (drawable);
|
||||
|
||||
gegl_buffer_set (dest_buffer, GEGL_RECTANGLE (x, y, width, height), 0,
|
||||
format, psrc,
|
||||
GEGL_AUTO_ROWSTRIDE);
|
||||
|
||||
g_object_unref (src_buffer);
|
||||
g_object_unref (dest_buffer);
|
||||
|
||||
pika_progress_update (1.0);
|
||||
|
||||
pika_drawable_merge_shadow (drawable, TRUE);
|
||||
pika_drawable_update (drawable, x, y, width, height);
|
||||
}
|
||||
|
||||
g_free (src);
|
||||
}
|
||||
|
||||
static void
|
||||
retinex_preview (GtkWidget *widget,
|
||||
GObject *config)
|
||||
{
|
||||
PikaPreview *preview = PIKA_PREVIEW (widget);
|
||||
PikaDrawable *drawable = g_object_get_data (config, "drawable");
|
||||
|
||||
retinex (config, drawable, preview);
|
||||
}
|
||||
|
||||
/*
|
||||
* calculate scale values for desired distribution.
|
||||
*/
|
||||
static void
|
||||
retinex_scales_distribution (gfloat *scales,
|
||||
gint nscales,
|
||||
gint mode,
|
||||
gint s)
|
||||
{
|
||||
if (nscales == 1)
|
||||
{ /* For one filter we choose the median scale */
|
||||
scales[0] = (gint) s / 2;
|
||||
}
|
||||
else if (nscales == 2)
|
||||
{ /* For two filters we choose the median and maximum scale */
|
||||
scales[0] = (gint) s / 2;
|
||||
scales[1] = (gint) s;
|
||||
}
|
||||
else
|
||||
{
|
||||
gfloat size_step = (gfloat) s / (gfloat) nscales;
|
||||
gint i;
|
||||
|
||||
switch(mode)
|
||||
{
|
||||
case RETINEX_UNIFORM:
|
||||
for(i = 0; i < nscales; ++i)
|
||||
scales[i] = 2. + (gfloat) i * size_step;
|
||||
break;
|
||||
|
||||
case RETINEX_LOW:
|
||||
size_step = (gfloat) log(s - 2.0) / (gfloat) nscales;
|
||||
for (i = 0; i < nscales; ++i)
|
||||
scales[i] = 2. + pow (10, (i * size_step) / log (10));
|
||||
break;
|
||||
|
||||
case RETINEX_HIGH:
|
||||
size_step = (gfloat) log(s - 2.0) / (gfloat) nscales;
|
||||
for (i = 0; i < nscales; ++i)
|
||||
scales[i] = s - pow (10, (i * size_step) / log (10));
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate the coefficients for the recursive filter algorithm
|
||||
* Fast Computation of gaussian blurring.
|
||||
*/
|
||||
static void
|
||||
compute_coefs3 (gauss3_coefs *c, gfloat sigma)
|
||||
{
|
||||
/*
|
||||
* Papers: "Recursive Implementation of the gaussian filter.",
|
||||
* Ian T. Young , Lucas J. Van Vliet, Signal Processing 44, Elsevier 1995.
|
||||
* formula: 11b computation of q
|
||||
* 8c computation of b0..b1
|
||||
* 10 alpha is normalization constant B
|
||||
*/
|
||||
gfloat q, q2, q3;
|
||||
|
||||
if (sigma >= 2.5)
|
||||
{
|
||||
q = 0.98711 * sigma - 0.96330;
|
||||
}
|
||||
else if ((sigma >= 0.5) && (sigma < 2.5))
|
||||
{
|
||||
q = 3.97156 - 4.14554 * (gfloat) sqrt ((double) 1 - 0.26891 * sigma);
|
||||
}
|
||||
else
|
||||
{
|
||||
q = 0.1147705018520355224609375;
|
||||
}
|
||||
|
||||
q2 = q * q;
|
||||
q3 = q * q2;
|
||||
c->b[0] = (1.57825+(2.44413*q)+(1.4281 *q2)+(0.422205*q3));
|
||||
c->b[1] = ( (2.44413*q)+(2.85619*q2)+(1.26661 *q3));
|
||||
c->b[2] = ( -((1.4281*q2)+(1.26661 *q3)));
|
||||
c->b[3] = ( (0.422205*q3));
|
||||
c->B = 1.0-((c->b[1]+c->b[2]+c->b[3])/c->b[0]);
|
||||
c->sigma = sigma;
|
||||
c->N = 3;
|
||||
|
||||
/*
|
||||
g_printerr ("q %f\n", q);
|
||||
g_printerr ("q2 %f\n", q2);
|
||||
g_printerr ("q3 %f\n", q3);
|
||||
g_printerr ("c->b[0] %f\n", c->b[0]);
|
||||
g_printerr ("c->b[1] %f\n", c->b[1]);
|
||||
g_printerr ("c->b[2] %f\n", c->b[2]);
|
||||
g_printerr ("c->b[3] %f\n", c->b[3]);
|
||||
g_printerr ("c->B %f\n", c->B);
|
||||
g_printerr ("c->sigma %f\n", c->sigma);
|
||||
g_printerr ("c->N %d\n", c->N);
|
||||
*/
|
||||
}
|
||||
|
||||
static void
|
||||
gausssmooth (gfloat *in, gfloat *out, gint size, gint rowstride, gauss3_coefs *c)
|
||||
{
|
||||
/*
|
||||
* Papers: "Recursive Implementation of the gaussian filter.",
|
||||
* Ian T. Young , Lucas J. Van Vliet, Signal Processing 44, Elsevier 1995.
|
||||
* formula: 9a forward filter
|
||||
* 9b backward filter
|
||||
* fig7 algorithm
|
||||
*/
|
||||
gint i,n, bufsize;
|
||||
gfloat *w1,*w2;
|
||||
|
||||
/* forward pass */
|
||||
bufsize = size+3;
|
||||
size -= 1;
|
||||
w1 = (gfloat *) g_try_malloc (bufsize * sizeof (gfloat));
|
||||
w2 = (gfloat *) g_try_malloc (bufsize * sizeof (gfloat));
|
||||
w1[0] = in[0];
|
||||
w1[1] = in[0];
|
||||
w1[2] = in[0];
|
||||
for ( i = 0 , n=3; i <= size ; i++, n++)
|
||||
{
|
||||
w1[n] = (gfloat)(c->B*in[i*rowstride] +
|
||||
((c->b[1]*w1[n-1] +
|
||||
c->b[2]*w1[n-2] +
|
||||
c->b[3]*w1[n-3] ) / c->b[0]));
|
||||
}
|
||||
|
||||
/* backward pass */
|
||||
w2[size+1]= w1[size+3];
|
||||
w2[size+2]= w1[size+3];
|
||||
w2[size+3]= w1[size+3];
|
||||
for (i = size, n = i; i >= 0; i--, n--)
|
||||
{
|
||||
w2[n]= out[i * rowstride] = (gfloat)(c->B*w1[n+3] +
|
||||
((c->b[1]*w2[n+1] +
|
||||
c->b[2]*w2[n+2] +
|
||||
c->b[3]*w2[n+3] ) / c->b[0]));
|
||||
}
|
||||
|
||||
g_free (w1);
|
||||
g_free (w2);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is the heart of the algo.
|
||||
* (a) Filterings at several scales and sumarize the results.
|
||||
* (b) Calculation of the final values.
|
||||
*/
|
||||
static void
|
||||
MSRCR (GObject *config,
|
||||
guchar *src,
|
||||
gint width,
|
||||
gint height,
|
||||
gint bytes,
|
||||
gboolean preview_mode)
|
||||
{
|
||||
|
||||
gint scale,row,col;
|
||||
gint i,j;
|
||||
gint size;
|
||||
gint channel;
|
||||
guchar *psrc = NULL; /* backup pointer for src buffer */
|
||||
gfloat *dst = NULL; /* float buffer for algorithm */
|
||||
gfloat *pdst = NULL; /* backup pointer for float buffer */
|
||||
gfloat *in, *out;
|
||||
gint channelsize; /* Float memory cache for one channel */
|
||||
gfloat weight;
|
||||
gauss3_coefs coef;
|
||||
gfloat mean, var;
|
||||
gfloat mini, range, maxi;
|
||||
gfloat alpha;
|
||||
gfloat gain;
|
||||
gfloat offset;
|
||||
gdouble max_preview = 0.0;
|
||||
gint scales_mode;
|
||||
gint config_scale;
|
||||
gint nscales;
|
||||
gdouble cvar;
|
||||
|
||||
g_object_get (config,
|
||||
"scales-mode", &scales_mode,
|
||||
"scale", &config_scale,
|
||||
"nscales", &nscales,
|
||||
"cvar", &cvar,
|
||||
NULL);
|
||||
|
||||
if (!preview_mode)
|
||||
{
|
||||
pika_progress_init (_("Retinex: filtering"));
|
||||
max_preview = 3 * nscales;
|
||||
}
|
||||
|
||||
/* Allocate all the memory needed for algorithm*/
|
||||
size = width * height * bytes;
|
||||
dst = g_try_malloc (size * sizeof (gfloat));
|
||||
if (dst == NULL)
|
||||
{
|
||||
g_warning ("Failed to allocate memory");
|
||||
return;
|
||||
}
|
||||
memset (dst, 0, size * sizeof (gfloat));
|
||||
|
||||
channelsize = (width * height);
|
||||
in = (gfloat *) g_try_malloc (channelsize * sizeof (gfloat));
|
||||
if (in == NULL)
|
||||
{
|
||||
g_free (dst);
|
||||
g_warning ("Failed to allocate memory");
|
||||
return; /* do some clever stuff */
|
||||
}
|
||||
|
||||
out = (gfloat *) g_try_malloc (channelsize * sizeof (gfloat));
|
||||
if (out == NULL)
|
||||
{
|
||||
g_free (in);
|
||||
g_free (dst);
|
||||
g_warning ("Failed to allocate memory");
|
||||
return; /* do some clever stuff */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Calculate the scales of filtering according to the
|
||||
number of filter and their distribution.
|
||||
*/
|
||||
|
||||
retinex_scales_distribution (RetinexScales,
|
||||
nscales, scales_mode, config_scale);
|
||||
|
||||
/*
|
||||
Filtering according to the various scales.
|
||||
Summerize the results of the various filters according to a
|
||||
specific weight(here equivalent for all).
|
||||
*/
|
||||
weight = 1./ (gfloat) nscales;
|
||||
|
||||
/*
|
||||
The recursive filtering algorithm needs different coefficients according
|
||||
to the selected scale (~ = standard deviation of Gaussian).
|
||||
*/
|
||||
for (channel = 0; channel < 3; channel++)
|
||||
{
|
||||
gint pos;
|
||||
|
||||
for (i = 0, pos = channel; i < channelsize ; i++, pos += bytes)
|
||||
{
|
||||
/* 0-255 => 1-256 */
|
||||
in[i] = (gfloat)(src[pos] + 1.0);
|
||||
}
|
||||
for (scale = 0; scale < nscales; scale++)
|
||||
{
|
||||
compute_coefs3 (&coef, RetinexScales[scale]);
|
||||
/*
|
||||
* Filtering (smoothing) Gaussian recursive.
|
||||
*
|
||||
* Filter rows first
|
||||
*/
|
||||
for (row=0 ;row < height; row++)
|
||||
{
|
||||
pos = row * width;
|
||||
gausssmooth (in + pos, out + pos, width, 1, &coef);
|
||||
}
|
||||
|
||||
memcpy(in, out, channelsize * sizeof(gfloat));
|
||||
memset(out, 0 , channelsize * sizeof(gfloat));
|
||||
|
||||
/*
|
||||
* Filtering (smoothing) Gaussian recursive.
|
||||
*
|
||||
* Second columns
|
||||
*/
|
||||
for (col=0; col < width; col++)
|
||||
{
|
||||
pos = col;
|
||||
gausssmooth(in + pos, out + pos, height, width, &coef);
|
||||
}
|
||||
|
||||
/*
|
||||
Summarize the filtered values.
|
||||
In fact one calculates a ratio between the original values and the filtered values.
|
||||
*/
|
||||
for (i = 0, pos = channel; i < channelsize; i++, pos += bytes)
|
||||
{
|
||||
dst[pos] += weight * (log (src[pos] + 1.) - log (out[i]));
|
||||
}
|
||||
|
||||
if (!preview_mode)
|
||||
pika_progress_update ((channel * nscales + scale) /
|
||||
max_preview);
|
||||
}
|
||||
}
|
||||
g_free(in);
|
||||
g_free(out);
|
||||
|
||||
/*
|
||||
Final calculation with original value and cumulated filter values.
|
||||
The parameters gain, alpha and offset are constants.
|
||||
*/
|
||||
/* Ci(x,y)=log[a Ii(x,y)]-log[ Ei=1-s Ii(x,y)] */
|
||||
|
||||
alpha = 128.;
|
||||
gain = 1.;
|
||||
offset = 0.;
|
||||
|
||||
for (i = 0; i < size; i += bytes)
|
||||
{
|
||||
gfloat logl;
|
||||
|
||||
psrc = src+i;
|
||||
pdst = dst+i;
|
||||
|
||||
logl = log((gfloat)psrc[0] + (gfloat)psrc[1] + (gfloat)psrc[2] + 3.);
|
||||
|
||||
pdst[0] = gain * ((log(alpha * (psrc[0]+1.)) - logl) * pdst[0]) + offset;
|
||||
pdst[1] = gain * ((log(alpha * (psrc[1]+1.)) - logl) * pdst[1]) + offset;
|
||||
pdst[2] = gain * ((log(alpha * (psrc[2]+1.)) - logl) * pdst[2]) + offset;
|
||||
}
|
||||
|
||||
/* if (!preview_mode)
|
||||
pika_progress_update ((2.0 + (nscales * 3)) /
|
||||
((nscales * 3) + 3));*/
|
||||
|
||||
/*
|
||||
Adapt the dynamics of the colors according to the statistics of the first and second order.
|
||||
The use of the variance makes it possible to control the degree of saturation of the colors.
|
||||
*/
|
||||
pdst = dst;
|
||||
|
||||
compute_mean_var (pdst, &mean, &var, size, bytes);
|
||||
mini = mean - cvar * var;
|
||||
maxi = mean + cvar * var;
|
||||
range = maxi - mini;
|
||||
|
||||
if (!range)
|
||||
range = 1.0;
|
||||
|
||||
for (i = 0; i < size; i+= bytes)
|
||||
{
|
||||
psrc = src + i;
|
||||
pdst = dst + i;
|
||||
|
||||
for (j = 0 ; j < 3 ; j++)
|
||||
{
|
||||
gfloat c = 255 * ( pdst[j] - mini ) / range;
|
||||
|
||||
psrc[j] = (guchar) CLAMP (c, 0, 255);
|
||||
}
|
||||
}
|
||||
|
||||
g_free (dst);
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate the average and variance in one go.
|
||||
*/
|
||||
static void
|
||||
compute_mean_var (gfloat *src, gfloat *mean, gfloat *var, gint size, gint bytes)
|
||||
{
|
||||
gfloat vsquared;
|
||||
gint i,j;
|
||||
gfloat *psrc;
|
||||
|
||||
vsquared = 0;
|
||||
*mean = 0;
|
||||
for (i = 0; i < size; i+= bytes)
|
||||
{
|
||||
psrc = src+i;
|
||||
for (j = 0 ; j < 3 ; j++)
|
||||
{
|
||||
*mean += psrc[j];
|
||||
vsquared += psrc[j] * psrc[j];
|
||||
}
|
||||
}
|
||||
|
||||
*mean /= (gfloat) size; /* mean */
|
||||
vsquared /= (gfloat) size; /* mean (x^2) */
|
||||
*var = ( vsquared - (*mean * *mean) );
|
||||
*var = sqrt(*var); /* var */
|
||||
}
|
||||
356
plug-ins/common/crop-zealous.c
Normal file
356
plug-ins/common/crop-zealous.c
Normal file
@ -0,0 +1,356 @@
|
||||
/*
|
||||
* ZealousCrop plug-in version 1.00
|
||||
* by Adam D. Moss <adam@foxbox.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <libpika/pika.h>
|
||||
|
||||
#include "libpika/stdplugins-intl.h"
|
||||
|
||||
|
||||
#define PLUG_IN_PROC "plug-in-zealouscrop"
|
||||
|
||||
#define EPSILON (1e-5)
|
||||
#define FLOAT_IS_ZERO(value) (value > -EPSILON && value < EPSILON)
|
||||
#define FLOAT_EQUAL(v1, v2) ((v1 - v2) > -EPSILON && (v1 - v2) < EPSILON)
|
||||
|
||||
|
||||
typedef struct _Crop Crop;
|
||||
typedef struct _CropClass CropClass;
|
||||
|
||||
struct _Crop
|
||||
{
|
||||
PikaPlugIn parent_instance;
|
||||
};
|
||||
|
||||
struct _CropClass
|
||||
{
|
||||
PikaPlugInClass parent_class;
|
||||
};
|
||||
|
||||
#define CROP_TYPE (crop_get_type ())
|
||||
#define CROP (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CROP_TYPE, Crop))
|
||||
|
||||
GType crop_get_type (void) G_GNUC_CONST;
|
||||
|
||||
static GList * crop_query_procedures (PikaPlugIn *plug_in);
|
||||
static PikaProcedure * crop_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name);
|
||||
|
||||
static PikaValueArray * crop_run (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data);
|
||||
|
||||
static inline gboolean colors_equal (const gfloat *col1,
|
||||
const gfloat *col2,
|
||||
gint components,
|
||||
gboolean has_alpha);
|
||||
static void do_zcrop (PikaDrawable *drawable,
|
||||
PikaImage *image);
|
||||
|
||||
|
||||
G_DEFINE_TYPE (Crop, crop, PIKA_TYPE_PLUG_IN)
|
||||
|
||||
PIKA_MAIN (CROP_TYPE)
|
||||
DEFINE_STD_SET_I18N
|
||||
|
||||
|
||||
static void
|
||||
crop_class_init (CropClass *klass)
|
||||
{
|
||||
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
|
||||
|
||||
plug_in_class->query_procedures = crop_query_procedures;
|
||||
plug_in_class->create_procedure = crop_create_procedure;
|
||||
plug_in_class->set_i18n = STD_SET_I18N;
|
||||
}
|
||||
|
||||
static void
|
||||
crop_init (Crop *crop)
|
||||
{
|
||||
}
|
||||
|
||||
static GList *
|
||||
crop_query_procedures (PikaPlugIn *plug_in)
|
||||
{
|
||||
return g_list_append (NULL, g_strdup (PLUG_IN_PROC));
|
||||
}
|
||||
|
||||
static PikaProcedure *
|
||||
crop_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name)
|
||||
{
|
||||
PikaProcedure *procedure = NULL;
|
||||
|
||||
if (! strcmp (name, PLUG_IN_PROC))
|
||||
{
|
||||
procedure = pika_image_procedure_new (plug_in, name,
|
||||
PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
crop_run, NULL, NULL);
|
||||
|
||||
pika_procedure_set_image_types (procedure, "*");
|
||||
pika_procedure_set_sensitivity_mask (procedure,
|
||||
PIKA_PROCEDURE_SENSITIVE_DRAWABLE);
|
||||
|
||||
pika_procedure_set_menu_label (procedure, _("_Zealous Crop"));
|
||||
pika_procedure_add_menu_path (procedure, "<Image>/Image/[Crop]");
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
_("Autocrop unused space from "
|
||||
"edges and middle"),
|
||||
NULL,
|
||||
name);
|
||||
pika_procedure_set_attribution (procedure,
|
||||
"Adam D. Moss",
|
||||
"Adam D. Moss",
|
||||
"1997");
|
||||
}
|
||||
|
||||
return procedure;
|
||||
}
|
||||
|
||||
static PikaValueArray *
|
||||
crop_run (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data)
|
||||
{
|
||||
PikaDrawable *drawable;
|
||||
|
||||
gegl_init (NULL, NULL);
|
||||
|
||||
pika_progress_init (_("Zealous cropping"));
|
||||
|
||||
if (n_drawables != 1)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
g_set_error (&error, PIKA_PLUG_IN_ERROR, 0,
|
||||
_("Procedure '%s' only works with one drawable."),
|
||||
PLUG_IN_PROC);
|
||||
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_CALLING_ERROR,
|
||||
error);
|
||||
}
|
||||
else
|
||||
{
|
||||
drawable = drawables[0];
|
||||
}
|
||||
|
||||
do_zcrop (drawable, image);
|
||||
|
||||
if (run_mode != PIKA_RUN_NONINTERACTIVE)
|
||||
pika_displays_flush ();
|
||||
|
||||
return pika_procedure_new_return_values (procedure, PIKA_PDB_SUCCESS, NULL);
|
||||
}
|
||||
|
||||
static inline gboolean
|
||||
colors_equal (const gfloat *col1,
|
||||
const gfloat *col2,
|
||||
gint components,
|
||||
gboolean has_alpha)
|
||||
{
|
||||
if (has_alpha &&
|
||||
FLOAT_IS_ZERO (col1[components - 1]) &&
|
||||
FLOAT_IS_ZERO (col2[components - 1]))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
gint b;
|
||||
|
||||
for (b = 0; b < components; b++)
|
||||
{
|
||||
if (! FLOAT_EQUAL (col1[b], col2[b]))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
do_zcrop (PikaDrawable *drawable,
|
||||
PikaImage *image)
|
||||
{
|
||||
GeglBuffer *drawable_buffer;
|
||||
GeglBuffer *shadow_buffer;
|
||||
gfloat *linear_buf;
|
||||
const Babl *format;
|
||||
|
||||
gint x, y, width, height;
|
||||
gint components;
|
||||
gint8 *killrows;
|
||||
gint8 *killcols;
|
||||
gint32 livingrows, livingcols, destrow, destcol;
|
||||
PikaChannel *selection_copy;
|
||||
gboolean has_alpha;
|
||||
|
||||
drawable_buffer = pika_drawable_get_buffer (drawable);
|
||||
shadow_buffer = pika_drawable_get_shadow_buffer (drawable);
|
||||
|
||||
width = gegl_buffer_get_width (drawable_buffer);
|
||||
height = gegl_buffer_get_height (drawable_buffer);
|
||||
has_alpha = pika_drawable_has_alpha (drawable);
|
||||
|
||||
if (has_alpha)
|
||||
format = babl_format ("R'G'B'A float");
|
||||
else
|
||||
format = babl_format ("R'G'B' float");
|
||||
|
||||
components = babl_format_get_n_components (format);
|
||||
|
||||
killrows = g_new (gint8, height);
|
||||
killcols = g_new (gint8, width);
|
||||
|
||||
linear_buf = g_new (gfloat, (width > height ? width : height) * components);
|
||||
|
||||
/* search which rows to remove */
|
||||
|
||||
livingrows = 0;
|
||||
for (y = 0; y < height; y++)
|
||||
{
|
||||
gegl_buffer_get (drawable_buffer, GEGL_RECTANGLE (0, y, width, 1),
|
||||
1.0, format, linear_buf,
|
||||
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
|
||||
|
||||
killrows[y] = TRUE;
|
||||
|
||||
for (x = components; x < width * components; x += components)
|
||||
{
|
||||
if (! colors_equal (linear_buf, &linear_buf[x], components, has_alpha))
|
||||
{
|
||||
livingrows++;
|
||||
killrows[y] = FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pika_progress_update (0.25);
|
||||
|
||||
/* search which columns to remove */
|
||||
|
||||
livingcols = 0;
|
||||
for (x = 0; x < width; x++)
|
||||
{
|
||||
gegl_buffer_get (drawable_buffer, GEGL_RECTANGLE (x, 0, 1, height),
|
||||
1.0, format, linear_buf,
|
||||
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
|
||||
|
||||
killcols[x] = TRUE;
|
||||
|
||||
for (y = components; y < height * components; y += components)
|
||||
{
|
||||
if (! colors_equal (linear_buf, &linear_buf[y], components, has_alpha))
|
||||
{
|
||||
livingcols++;
|
||||
killcols[x] = FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pika_progress_update (0.5);
|
||||
|
||||
if ((livingcols == 0 || livingrows == 0) ||
|
||||
(livingcols == width && livingrows == height))
|
||||
{
|
||||
g_message (_("Nothing to crop."));
|
||||
|
||||
g_object_unref (shadow_buffer);
|
||||
g_object_unref (drawable_buffer);
|
||||
|
||||
g_free (linear_buf);
|
||||
g_free (killrows);
|
||||
g_free (killcols);
|
||||
return;
|
||||
}
|
||||
|
||||
/* restitute living rows */
|
||||
|
||||
destrow = 0;
|
||||
for (y = 0; y < height; y++)
|
||||
{
|
||||
if (!killrows[y])
|
||||
{
|
||||
gegl_buffer_copy (drawable_buffer,
|
||||
GEGL_RECTANGLE (0, y, width, 1),
|
||||
GEGL_ABYSS_NONE,
|
||||
shadow_buffer,
|
||||
GEGL_RECTANGLE (0, destrow, width, 1));
|
||||
|
||||
destrow++;
|
||||
}
|
||||
}
|
||||
|
||||
pika_progress_update (0.75);
|
||||
|
||||
/* restitute living columns */
|
||||
|
||||
destcol = 0;
|
||||
for (x = 0; x < width; x++)
|
||||
{
|
||||
if (!killcols[x])
|
||||
{
|
||||
gegl_buffer_copy (shadow_buffer,
|
||||
GEGL_RECTANGLE (x, 0, 1, height),
|
||||
GEGL_ABYSS_NONE,
|
||||
shadow_buffer,
|
||||
GEGL_RECTANGLE (destcol, 0, 1, height));
|
||||
|
||||
destcol++;
|
||||
}
|
||||
}
|
||||
|
||||
pika_progress_update (1.00);
|
||||
|
||||
pika_image_undo_group_start (image);
|
||||
|
||||
selection_copy = PIKA_CHANNEL (pika_selection_save (image));
|
||||
pika_selection_none (image);
|
||||
|
||||
gegl_buffer_flush (shadow_buffer);
|
||||
pika_drawable_merge_shadow (drawable, TRUE);
|
||||
gegl_buffer_flush (drawable_buffer);
|
||||
|
||||
pika_image_select_item (image, PIKA_CHANNEL_OP_REPLACE,
|
||||
PIKA_ITEM (selection_copy));
|
||||
pika_image_remove_channel (image, selection_copy);
|
||||
|
||||
pika_image_crop (image, livingcols, livingrows, 0, 0);
|
||||
|
||||
pika_image_undo_group_end (image);
|
||||
|
||||
g_object_unref (shadow_buffer);
|
||||
g_object_unref (drawable_buffer);
|
||||
|
||||
g_free (linear_buf);
|
||||
g_free (killrows);
|
||||
g_free (killcols);
|
||||
}
|
||||
3319
plug-ins/common/curve-bend.c
Normal file
3319
plug-ins/common/curve-bend.c
Normal file
File diff suppressed because it is too large
Load Diff
1019
plug-ins/common/decompose.c
Normal file
1019
plug-ins/common/decompose.c
Normal file
File diff suppressed because it is too large
Load Diff
1157
plug-ins/common/depth-merge.c
Normal file
1157
plug-ins/common/depth-merge.c
Normal file
File diff suppressed because it is too large
Load Diff
868
plug-ins/common/despeckle.c
Normal file
868
plug-ins/common/despeckle.c
Normal file
@ -0,0 +1,868 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* Despeckle (adaptive median) filter
|
||||
*
|
||||
* Copyright 1997-1998 Michael Sweet (mike@easysw.com)
|
||||
* optimized in 2010 by Przemyslaw Zych (kermidt.zed@gmail.com)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <libpika/pika.h>
|
||||
#include <libpika/pikaui.h>
|
||||
|
||||
#include "libpika/stdplugins-intl.h"
|
||||
|
||||
|
||||
/*
|
||||
* Constants...
|
||||
*/
|
||||
|
||||
#define PLUG_IN_PROC "plug-in-despeckle"
|
||||
#define PLUG_IN_BINARY "despeckle"
|
||||
#define PLUG_IN_VERSION "May 2010"
|
||||
#define SCALE_WIDTH 100
|
||||
#define ENTRY_WIDTH 3
|
||||
#define MAX_RADIUS 30
|
||||
|
||||
#define FILTER_ADAPTIVE 0x01
|
||||
#define FILTER_RECURSIVE 0x02
|
||||
|
||||
/* List that stores pixels falling in to the same luma bucket */
|
||||
#define MAX_LIST_ELEMS SQR(2 * MAX_RADIUS + 1)
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const guchar *elems[MAX_LIST_ELEMS];
|
||||
gint start;
|
||||
gint count;
|
||||
} PixelsList;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
gint elems[256]; /* Number of pixels that fall into each luma bucket */
|
||||
PixelsList origs[256]; /* Original pixels */
|
||||
gint xmin;
|
||||
gint ymin;
|
||||
gint xmax;
|
||||
gint ymax; /* Source rect */
|
||||
} DespeckleHistogram;
|
||||
|
||||
|
||||
typedef struct _Despeckle Despeckle;
|
||||
typedef struct _DespeckleClass DespeckleClass;
|
||||
|
||||
struct _Despeckle
|
||||
{
|
||||
PikaPlugIn parent_instance;
|
||||
};
|
||||
|
||||
struct _DespeckleClass
|
||||
{
|
||||
PikaPlugInClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
#define DESPECKLE_TYPE (despeckle_get_type ())
|
||||
#define DESPECKLE (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), DESPECKLE_TYPE, Despeckle))
|
||||
|
||||
GType despeckle_get_type (void) G_GNUC_CONST;
|
||||
|
||||
static GList * despeckle_query_procedures (PikaPlugIn *plug_in);
|
||||
static PikaProcedure * despeckle_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name);
|
||||
|
||||
static PikaValueArray * despeckle_run (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data);
|
||||
|
||||
static void despeckle (PikaDrawable *drawable,
|
||||
GObject *config);
|
||||
static void despeckle_median (GObject *config,
|
||||
guchar *src,
|
||||
guchar *dst,
|
||||
gint width,
|
||||
gint height,
|
||||
gint bpp,
|
||||
gboolean preview);
|
||||
|
||||
static gboolean despeckle_dialog (PikaProcedure *procedure,
|
||||
GObject *config,
|
||||
PikaDrawable *drawable);
|
||||
|
||||
static void preview_update (GtkWidget *preview,
|
||||
GObject *config);
|
||||
|
||||
|
||||
G_DEFINE_TYPE (Despeckle, despeckle, PIKA_TYPE_PLUG_IN)
|
||||
|
||||
PIKA_MAIN (DESPECKLE_TYPE)
|
||||
DEFINE_STD_SET_I18N
|
||||
|
||||
|
||||
/* Number of pixels in actual histogram falling into each category */
|
||||
static gint hist0; /* Less than min threshold */
|
||||
static gint hist255; /* More than max threshold */
|
||||
static gint histrest; /* From min to max */
|
||||
|
||||
static DespeckleHistogram histogram;
|
||||
|
||||
|
||||
static void
|
||||
despeckle_class_init (DespeckleClass *klass)
|
||||
{
|
||||
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
|
||||
|
||||
plug_in_class->query_procedures = despeckle_query_procedures;
|
||||
plug_in_class->create_procedure = despeckle_create_procedure;
|
||||
plug_in_class->set_i18n = STD_SET_I18N;
|
||||
}
|
||||
|
||||
static void
|
||||
despeckle_init (Despeckle *despeckle)
|
||||
{
|
||||
}
|
||||
|
||||
static GList *
|
||||
despeckle_query_procedures (PikaPlugIn *plug_in)
|
||||
{
|
||||
return g_list_append (NULL, g_strdup (PLUG_IN_PROC));
|
||||
}
|
||||
|
||||
static PikaProcedure *
|
||||
despeckle_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name)
|
||||
{
|
||||
PikaProcedure *procedure = NULL;
|
||||
|
||||
if (! strcmp (name, PLUG_IN_PROC))
|
||||
{
|
||||
procedure = pika_image_procedure_new (plug_in, name,
|
||||
PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
despeckle_run, NULL, NULL);
|
||||
|
||||
pika_procedure_set_image_types (procedure, "RGB*, GRAY*");
|
||||
pika_procedure_set_sensitivity_mask (procedure,
|
||||
PIKA_PROCEDURE_SENSITIVE_DRAWABLE);
|
||||
|
||||
pika_procedure_set_menu_label (procedure, _("Des_peckle..."));
|
||||
pika_procedure_add_menu_path (procedure, "<Image>/Filters/Enhance");
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
_("Remove speckle noise from the "
|
||||
"image"),
|
||||
_("This plug-in selectively performs "
|
||||
"a median or adaptive box filter on "
|
||||
"an image."),
|
||||
name);
|
||||
pika_procedure_set_attribution (procedure,
|
||||
"Michael Sweet <mike@easysw.com>",
|
||||
"Copyright 1997-1998 by Michael Sweet",
|
||||
PLUG_IN_VERSION);
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "radius",
|
||||
_("R_adius"),
|
||||
_("Filter box radius"),
|
||||
1, MAX_RADIUS, 3,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "type",
|
||||
_("_Filter Type"),
|
||||
_("Filter type { MEDIAN (0), ADAPTIVE (1), "
|
||||
"RECURSIVE-MEDIAN (2), RECURSIVE-ADAPTIVE (3) }"),
|
||||
0, 3, FILTER_ADAPTIVE,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "black",
|
||||
_("_Black level"),
|
||||
_("Black level"),
|
||||
-1, 255, 7,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "white",
|
||||
_("_White level"),
|
||||
_("White level"),
|
||||
0, 256, 248,
|
||||
G_PARAM_READWRITE);
|
||||
}
|
||||
|
||||
return procedure;
|
||||
}
|
||||
|
||||
static PikaValueArray *
|
||||
despeckle_run (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data)
|
||||
{
|
||||
PikaProcedureConfig *config;
|
||||
PikaDrawable *drawable;
|
||||
|
||||
gegl_init (NULL, NULL);
|
||||
|
||||
if (n_drawables != 1)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
g_set_error (&error, PIKA_PLUG_IN_ERROR, 0,
|
||||
_("Procedure '%s' only works with one drawable."),
|
||||
PLUG_IN_PROC);
|
||||
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_CALLING_ERROR,
|
||||
error);
|
||||
}
|
||||
else
|
||||
{
|
||||
drawable = drawables[0];
|
||||
}
|
||||
|
||||
if (! pika_drawable_is_rgb (drawable) &&
|
||||
! pika_drawable_is_gray (drawable))
|
||||
{
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_EXECUTION_ERROR,
|
||||
NULL);
|
||||
}
|
||||
|
||||
config = pika_procedure_create_config (procedure);
|
||||
pika_procedure_config_begin_run (config, NULL, run_mode, args);
|
||||
|
||||
if (run_mode == PIKA_RUN_INTERACTIVE)
|
||||
{
|
||||
if (! despeckle_dialog (procedure, G_OBJECT (config), drawable))
|
||||
{
|
||||
pika_procedure_config_end_run (config, PIKA_PDB_CANCEL);
|
||||
g_object_unref (config);
|
||||
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_CANCEL,
|
||||
NULL);
|
||||
}
|
||||
}
|
||||
|
||||
despeckle (drawable, G_OBJECT (config));
|
||||
|
||||
pika_procedure_config_end_run (config, PIKA_PDB_SUCCESS);
|
||||
g_object_unref (config);
|
||||
|
||||
return pika_procedure_new_return_values (procedure, PIKA_PDB_SUCCESS, NULL);
|
||||
}
|
||||
|
||||
static inline guchar
|
||||
pixel_luminance (const guchar *p,
|
||||
gint bpp)
|
||||
{
|
||||
switch (bpp)
|
||||
{
|
||||
case 1:
|
||||
case 2:
|
||||
return p[0];
|
||||
|
||||
case 3:
|
||||
case 4:
|
||||
return PIKA_RGB_LUMINANCE (p[0], p[1], p[2]);
|
||||
|
||||
default:
|
||||
return 0; /* should not be reached */
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
pixel_copy (guchar *dest,
|
||||
const guchar *src,
|
||||
gint bpp)
|
||||
{
|
||||
switch (bpp)
|
||||
{
|
||||
case 4:
|
||||
*dest++ = *src++;
|
||||
case 3:
|
||||
*dest++ = *src++;
|
||||
case 2:
|
||||
*dest++ = *src++;
|
||||
case 1:
|
||||
*dest++ = *src++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 'despeckle()' - Despeckle an image using a median filter.
|
||||
*
|
||||
* A median filter basically collects pixel values in a region around the
|
||||
* target pixel, sorts them, and uses the median value. This code uses a
|
||||
* circular row buffer to improve performance.
|
||||
*
|
||||
* The adaptive filter is based on the median filter but analyzes the histogram
|
||||
* of the region around the target pixel and adjusts the despeckle diameter
|
||||
* accordingly.
|
||||
*/
|
||||
|
||||
static const Babl *
|
||||
get_u8_format (PikaDrawable *drawable)
|
||||
{
|
||||
if (pika_drawable_is_rgb (drawable))
|
||||
{
|
||||
if (pika_drawable_has_alpha (drawable))
|
||||
return babl_format ("R'G'B'A u8");
|
||||
else
|
||||
return babl_format ("R'G'B' u8");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pika_drawable_has_alpha (drawable))
|
||||
return babl_format ("Y'A u8");
|
||||
else
|
||||
return babl_format ("Y' u8");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
despeckle (PikaDrawable *drawable,
|
||||
GObject *config)
|
||||
{
|
||||
GeglBuffer *src_buffer;
|
||||
GeglBuffer *dest_buffer;
|
||||
const Babl *format;
|
||||
guchar *src;
|
||||
guchar *dst;
|
||||
gint img_bpp;
|
||||
gint x, y;
|
||||
gint width, height;
|
||||
|
||||
if (! pika_drawable_mask_intersect (drawable,
|
||||
&x, &y, &width, &height))
|
||||
return;
|
||||
|
||||
format = get_u8_format (drawable);
|
||||
img_bpp = babl_format_get_bytes_per_pixel (format);
|
||||
|
||||
src_buffer = pika_drawable_get_buffer (drawable);
|
||||
dest_buffer = pika_drawable_get_shadow_buffer (drawable);
|
||||
|
||||
src = g_new (guchar, width * height * img_bpp);
|
||||
dst = g_new (guchar, width * height * img_bpp);
|
||||
|
||||
gegl_buffer_get (src_buffer, GEGL_RECTANGLE (x, y, width, height), 1.0,
|
||||
format, src,
|
||||
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
|
||||
|
||||
despeckle_median (config,
|
||||
src, dst, width, height, img_bpp, FALSE);
|
||||
|
||||
gegl_buffer_set (dest_buffer, GEGL_RECTANGLE (x, y, width, height), 0,
|
||||
format, dst,
|
||||
GEGL_AUTO_ROWSTRIDE);
|
||||
|
||||
g_object_unref (src_buffer);
|
||||
g_object_unref (dest_buffer);
|
||||
|
||||
pika_drawable_merge_shadow (drawable, TRUE);
|
||||
pika_drawable_update (drawable, x, y, width, height);
|
||||
|
||||
g_free (dst);
|
||||
g_free (src);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
despeckle_dialog (PikaProcedure *procedure,
|
||||
GObject *config,
|
||||
PikaDrawable *drawable)
|
||||
{
|
||||
GtkWidget *dialog;
|
||||
GtkWidget *preview;
|
||||
GtkWidget *vbox;
|
||||
GtkWidget *scale;
|
||||
GtkWidget *combo;
|
||||
GtkListStore *store;
|
||||
gboolean run;
|
||||
|
||||
pika_ui_init (PLUG_IN_BINARY);
|
||||
|
||||
dialog = pika_procedure_dialog_new (procedure,
|
||||
PIKA_PROCEDURE_CONFIG (config),
|
||||
_("Despeckle"));
|
||||
|
||||
pika_window_set_transient (GTK_WINDOW (dialog));
|
||||
|
||||
preview = pika_drawable_preview_new_from_drawable (drawable);
|
||||
gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
|
||||
preview, TRUE, TRUE, 0);
|
||||
gtk_widget_set_margin_bottom (preview, 12);
|
||||
gtk_widget_show (preview);
|
||||
|
||||
store = pika_int_store_new (_("Median"), 0,
|
||||
_("Adaptive"), FILTER_ADAPTIVE,
|
||||
_("Recursive-Median"), FILTER_RECURSIVE,
|
||||
_("Recursive-Adaptive"), 3,
|
||||
NULL);
|
||||
combo = pika_procedure_dialog_get_int_combo (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"type",
|
||||
PIKA_INT_STORE (store));
|
||||
gtk_widget_set_margin_bottom (combo, 12);
|
||||
|
||||
/*
|
||||
* Box size (diameter) control...
|
||||
*/
|
||||
scale = pika_procedure_dialog_get_scale_entry (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"radius", 1.0);
|
||||
gtk_widget_set_margin_bottom (scale, 12);
|
||||
/*
|
||||
* Black level control...
|
||||
*/
|
||||
scale = pika_procedure_dialog_get_scale_entry (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"black", 1.0);
|
||||
gtk_widget_set_margin_bottom (scale, 12);
|
||||
/*
|
||||
* White level control...
|
||||
*/
|
||||
pika_procedure_dialog_get_scale_entry (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"white", 1.0);
|
||||
|
||||
vbox = pika_procedure_dialog_fill_box (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"despeckle-vbox",
|
||||
"type", "radius", "black", "white",
|
||||
NULL);
|
||||
gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
|
||||
|
||||
g_object_set_data (config, "drawable", drawable);
|
||||
|
||||
g_signal_connect (preview, "invalidated",
|
||||
G_CALLBACK (preview_update),
|
||||
config);
|
||||
|
||||
g_signal_connect_swapped (config, "notify",
|
||||
G_CALLBACK (pika_preview_invalidate),
|
||||
preview);
|
||||
|
||||
pika_procedure_dialog_fill (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"despeckle-vbox",
|
||||
NULL);
|
||||
gtk_widget_show (dialog);
|
||||
|
||||
run = pika_procedure_dialog_run (PIKA_PROCEDURE_DIALOG (dialog));
|
||||
|
||||
gtk_widget_destroy (dialog);
|
||||
|
||||
return run;
|
||||
}
|
||||
|
||||
static void
|
||||
preview_update (GtkWidget *widget,
|
||||
GObject *config)
|
||||
{
|
||||
PikaPreview *preview = PIKA_PREVIEW (widget);
|
||||
PikaDrawable *drawable = g_object_get_data (config, "drawable");
|
||||
GeglBuffer *src_buffer;
|
||||
const Babl *format;
|
||||
guchar *dst;
|
||||
guchar *src;
|
||||
gint img_bpp;
|
||||
gint x1,y1;
|
||||
gint width, height;
|
||||
|
||||
format = get_u8_format (drawable);
|
||||
img_bpp = babl_format_get_bytes_per_pixel (format);
|
||||
|
||||
pika_preview_get_size (preview, &width, &height);
|
||||
pika_preview_get_position (preview, &x1, &y1);
|
||||
|
||||
src_buffer = pika_drawable_get_buffer (drawable);
|
||||
|
||||
dst = g_new (guchar, width * height * img_bpp);
|
||||
src = g_new (guchar, width * height * img_bpp);
|
||||
|
||||
gegl_buffer_get (src_buffer, GEGL_RECTANGLE (x1, y1, width, height), 1.0,
|
||||
format, src,
|
||||
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
|
||||
|
||||
despeckle_median (config,
|
||||
src, dst, width, height, img_bpp, TRUE);
|
||||
|
||||
pika_preview_draw_buffer (preview, dst, width * img_bpp);
|
||||
|
||||
g_object_unref (src_buffer);
|
||||
|
||||
g_free (src);
|
||||
g_free (dst);
|
||||
}
|
||||
|
||||
static inline void
|
||||
list_add_elem (PixelsList *list,
|
||||
const guchar *elem)
|
||||
{
|
||||
const gint pos = list->start + list->count++;
|
||||
|
||||
list->elems[pos >= MAX_LIST_ELEMS ? pos - MAX_LIST_ELEMS : pos] = elem;
|
||||
}
|
||||
|
||||
static inline void
|
||||
list_del_elem (PixelsList* list)
|
||||
{
|
||||
list->count--;
|
||||
list->start++;
|
||||
|
||||
if (list->start >= MAX_LIST_ELEMS)
|
||||
list->start = 0;
|
||||
}
|
||||
|
||||
static inline const guchar *
|
||||
list_get_random_elem (PixelsList *list)
|
||||
{
|
||||
const gint pos = list->start + rand () % list->count;
|
||||
|
||||
if (pos >= MAX_LIST_ELEMS)
|
||||
return list->elems[pos - MAX_LIST_ELEMS];
|
||||
|
||||
return list->elems[pos];
|
||||
}
|
||||
|
||||
static inline void
|
||||
histogram_add (DespeckleHistogram *hist,
|
||||
guchar val,
|
||||
const guchar *orig)
|
||||
{
|
||||
hist->elems[val]++;
|
||||
list_add_elem (&hist->origs[val], orig);
|
||||
}
|
||||
|
||||
static inline void
|
||||
histogram_remove (DespeckleHistogram *hist,
|
||||
guchar val)
|
||||
{
|
||||
hist->elems[val]--;
|
||||
list_del_elem (&hist->origs[val]);
|
||||
}
|
||||
|
||||
static inline void
|
||||
histogram_clean (DespeckleHistogram *hist)
|
||||
{
|
||||
gint i;
|
||||
|
||||
for (i = 0; i < 256; i++)
|
||||
{
|
||||
hist->elems[i] = 0;
|
||||
hist->origs[i].count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline const guchar *
|
||||
histogram_get_median (DespeckleHistogram *hist,
|
||||
const guchar *_default)
|
||||
{
|
||||
gint count = histrest;
|
||||
gint i;
|
||||
gint sum = 0;
|
||||
|
||||
if (! count)
|
||||
return _default;
|
||||
|
||||
count = (count + 1) / 2;
|
||||
|
||||
i = 0;
|
||||
while ((sum += hist->elems[i]) < count)
|
||||
i++;
|
||||
|
||||
return list_get_random_elem (&hist->origs[i]);
|
||||
}
|
||||
|
||||
static inline void
|
||||
add_val (DespeckleHistogram *hist,
|
||||
gint black_level,
|
||||
gint white_level,
|
||||
const guchar *src,
|
||||
gint width,
|
||||
gint bpp,
|
||||
gint x,
|
||||
gint y)
|
||||
{
|
||||
const gint pos = (x + (y * width)) * bpp;
|
||||
const gint value = pixel_luminance (src + pos, bpp);
|
||||
|
||||
if (value > black_level && value < white_level)
|
||||
{
|
||||
histogram_add (hist, value, src + pos);
|
||||
histrest++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (value <= black_level)
|
||||
hist0++;
|
||||
|
||||
if (value >= white_level)
|
||||
hist255++;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
del_val (DespeckleHistogram *hist,
|
||||
gint black_level,
|
||||
gint white_level,
|
||||
const guchar *src,
|
||||
gint width,
|
||||
gint bpp,
|
||||
gint x,
|
||||
gint y)
|
||||
{
|
||||
const gint pos = (x + (y * width)) * bpp;
|
||||
const gint value = pixel_luminance (src + pos, bpp);
|
||||
|
||||
if (value > black_level && value < white_level)
|
||||
{
|
||||
histogram_remove (hist, value);
|
||||
histrest--;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (value <= black_level)
|
||||
hist0--;
|
||||
|
||||
if (value >= white_level)
|
||||
hist255--;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
add_vals (DespeckleHistogram *hist,
|
||||
gint black_level,
|
||||
gint white_level,
|
||||
const guchar *src,
|
||||
gint width,
|
||||
gint bpp,
|
||||
gint xmin,
|
||||
gint ymin,
|
||||
gint xmax,
|
||||
gint ymax)
|
||||
{
|
||||
gint x;
|
||||
gint y;
|
||||
|
||||
if (xmin > xmax)
|
||||
return;
|
||||
|
||||
for (y = ymin; y <= ymax; y++)
|
||||
{
|
||||
for (x = xmin; x <= xmax; x++)
|
||||
{
|
||||
add_val (hist,
|
||||
black_level, white_level,
|
||||
src, width, bpp, x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
del_vals (DespeckleHistogram *hist,
|
||||
gint black_level,
|
||||
gint white_level,
|
||||
const guchar *src,
|
||||
gint width,
|
||||
gint bpp,
|
||||
gint xmin,
|
||||
gint ymin,
|
||||
gint xmax,
|
||||
gint ymax)
|
||||
{
|
||||
gint x;
|
||||
gint y;
|
||||
|
||||
if (xmin > xmax)
|
||||
return;
|
||||
|
||||
for (y = ymin; y <= ymax; y++)
|
||||
{
|
||||
for (x = xmin; x <= xmax; x++)
|
||||
{
|
||||
del_val (hist,
|
||||
black_level, white_level,
|
||||
src, width, bpp, x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
update_histogram (DespeckleHistogram *hist,
|
||||
gint black_level,
|
||||
gint white_level,
|
||||
const guchar *src,
|
||||
gint width,
|
||||
gint bpp,
|
||||
gint xmin,
|
||||
gint ymin,
|
||||
gint xmax,
|
||||
gint ymax)
|
||||
{
|
||||
/* assuming that radious of the box can change no more than one
|
||||
pixel in each call */
|
||||
/* assuming that box is moving either right or down */
|
||||
|
||||
del_vals (hist,
|
||||
black_level, white_level,
|
||||
src, width, bpp, hist->xmin, hist->ymin, xmin - 1, hist->ymax);
|
||||
del_vals (hist,
|
||||
black_level, white_level,
|
||||
src, width, bpp, xmin, hist->ymin, xmax, ymin - 1);
|
||||
del_vals (hist,
|
||||
black_level, white_level,
|
||||
src, width, bpp, xmin, ymax + 1, xmax, hist->ymax);
|
||||
|
||||
add_vals (hist,
|
||||
black_level, white_level,
|
||||
src, width, bpp, hist->xmax + 1, ymin, xmax, ymax);
|
||||
add_vals (hist,
|
||||
black_level, white_level,
|
||||
src, width, bpp, xmin, ymin, hist->xmax, hist->ymin - 1);
|
||||
add_vals (hist,
|
||||
black_level, white_level,
|
||||
src, width, bpp, hist->xmin, hist->ymax + 1, hist->xmax, ymax);
|
||||
|
||||
hist->xmin = xmin;
|
||||
hist->ymin = ymin;
|
||||
hist->xmax = xmax;
|
||||
hist->ymax = ymax;
|
||||
}
|
||||
|
||||
static void
|
||||
despeckle_median (GObject *config,
|
||||
guchar *src,
|
||||
guchar *dst,
|
||||
gint width,
|
||||
gint height,
|
||||
gint bpp,
|
||||
gboolean preview)
|
||||
{
|
||||
gint radius;
|
||||
gint filter_type;
|
||||
gint black_level;
|
||||
gint white_level;
|
||||
guint progress;
|
||||
guint max_progress;
|
||||
gint x, y;
|
||||
gint adapt_radius;
|
||||
gint pos;
|
||||
gint ymin;
|
||||
gint ymax;
|
||||
gint xmin;
|
||||
gint xmax;
|
||||
|
||||
g_object_get (config,
|
||||
"radius", &radius,
|
||||
"type", &filter_type,
|
||||
"black", &black_level,
|
||||
"white", &white_level,
|
||||
NULL);
|
||||
|
||||
memset (&histogram, 0, sizeof(histogram));
|
||||
progress = 0;
|
||||
max_progress = width * height;
|
||||
|
||||
if (! preview)
|
||||
pika_progress_init (_("Despeckle"));
|
||||
|
||||
adapt_radius = radius;
|
||||
for (y = 0; y < height; y++)
|
||||
{
|
||||
x = 0;
|
||||
ymin = MAX (0, y - adapt_radius);
|
||||
ymax = MIN (height - 1, y + adapt_radius);
|
||||
xmin = MAX (0, x - adapt_radius);
|
||||
xmax = MIN (width - 1, x + adapt_radius);
|
||||
hist0 = 0;
|
||||
histrest = 0;
|
||||
hist255 = 0;
|
||||
histogram_clean (&histogram);
|
||||
histogram.xmin = xmin;
|
||||
histogram.ymin = ymin;
|
||||
histogram.xmax = xmax;
|
||||
histogram.ymax = ymax;
|
||||
add_vals (&histogram,
|
||||
black_level, white_level,
|
||||
src, width, bpp,
|
||||
histogram.xmin, histogram.ymin,
|
||||
histogram.xmax, histogram.ymax);
|
||||
|
||||
for (x = 0; x < width; x++)
|
||||
{
|
||||
const guchar *pixel;
|
||||
|
||||
ymin = MAX (0, y - adapt_radius); /* update ymin, ymax when adapt_radius changed (FILTER_ADAPTIVE) */
|
||||
ymax = MIN (height - 1, y + adapt_radius);
|
||||
xmin = MAX (0, x - adapt_radius);
|
||||
xmax = MIN (width - 1, x + adapt_radius);
|
||||
|
||||
update_histogram (&histogram,
|
||||
black_level, white_level,
|
||||
src, width, bpp, xmin, ymin, xmax, ymax);
|
||||
|
||||
pos = (x + (y * width)) * bpp;
|
||||
pixel = histogram_get_median (&histogram, src + pos);
|
||||
|
||||
if (filter_type & FILTER_RECURSIVE)
|
||||
{
|
||||
del_val (&histogram,
|
||||
black_level, white_level,
|
||||
src, width, bpp, x, y);
|
||||
|
||||
pixel_copy (src + pos, pixel, bpp);
|
||||
|
||||
add_val (&histogram,
|
||||
black_level, white_level,
|
||||
src, width, bpp, x, y);
|
||||
}
|
||||
|
||||
pixel_copy (dst + pos, pixel, bpp);
|
||||
|
||||
/*
|
||||
* Check the histogram and adjust the diameter accordingly...
|
||||
*/
|
||||
if (filter_type & FILTER_ADAPTIVE)
|
||||
{
|
||||
if (hist0 >= adapt_radius || hist255 >= adapt_radius)
|
||||
{
|
||||
if (adapt_radius < radius)
|
||||
adapt_radius++;
|
||||
}
|
||||
else if (adapt_radius > 1)
|
||||
{
|
||||
adapt_radius--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
progress += width;
|
||||
|
||||
if (! preview && y % 32 == 0)
|
||||
pika_progress_update ((gdouble) progress / (gdouble) max_progress);
|
||||
}
|
||||
|
||||
if (! preview)
|
||||
pika_progress_update (1.0);
|
||||
}
|
||||
540
plug-ins/common/destripe.c
Normal file
540
plug-ins/common/destripe.c
Normal file
@ -0,0 +1,540 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* Destripe filter
|
||||
*
|
||||
* Copyright 1997 Marc Lehmann, heavily modified from a filter by
|
||||
* Michael Sweet.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <libpika/pika.h>
|
||||
#include <libpika/pikaui.h>
|
||||
|
||||
#include "libpika/stdplugins-intl.h"
|
||||
|
||||
|
||||
#define PLUG_IN_PROC "plug-in-destripe"
|
||||
#define PLUG_IN_BINARY "destripe"
|
||||
#define PLUG_IN_ROLE "pika-destripe"
|
||||
#define PLUG_IN_VERSION "0.2"
|
||||
#define MAX_AVG 100
|
||||
|
||||
|
||||
typedef struct _Destripe Destripe;
|
||||
typedef struct _DestripeClass DestripeClass;
|
||||
|
||||
struct _Destripe
|
||||
{
|
||||
PikaPlugIn parent_instance;
|
||||
};
|
||||
|
||||
struct _DestripeClass
|
||||
{
|
||||
PikaPlugInClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
#define DESTRIPE_TYPE (destripe_get_type ())
|
||||
#define DESTRIPE (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), DESTRIPE_TYPE, Destripe))
|
||||
|
||||
GType destripe_get_type (void) G_GNUC_CONST;
|
||||
|
||||
static GList * destripe_query_procedures (PikaPlugIn *plug_in);
|
||||
static PikaProcedure * destripe_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name);
|
||||
|
||||
static PikaValueArray * destripe_run (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data);
|
||||
|
||||
static void destripe (GObject *config,
|
||||
PikaDrawable *drawable,
|
||||
PikaPreview *preview);
|
||||
static void destripe_preview (GtkWidget *widget,
|
||||
GObject *config);
|
||||
|
||||
static gboolean destripe_dialog (PikaProcedure *procedure,
|
||||
GObject *config,
|
||||
PikaDrawable *drawable);
|
||||
|
||||
|
||||
G_DEFINE_TYPE (Destripe, destripe, PIKA_TYPE_PLUG_IN)
|
||||
|
||||
PIKA_MAIN (DESTRIPE_TYPE)
|
||||
DEFINE_STD_SET_I18N
|
||||
|
||||
|
||||
static void
|
||||
destripe_class_init (DestripeClass *klass)
|
||||
{
|
||||
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
|
||||
|
||||
plug_in_class->query_procedures = destripe_query_procedures;
|
||||
plug_in_class->create_procedure = destripe_create_procedure;
|
||||
plug_in_class->set_i18n = STD_SET_I18N;
|
||||
}
|
||||
|
||||
static void
|
||||
destripe_init (Destripe *destripe)
|
||||
{
|
||||
}
|
||||
|
||||
static GList *
|
||||
destripe_query_procedures (PikaPlugIn *plug_in)
|
||||
{
|
||||
return g_list_append (NULL, g_strdup (PLUG_IN_PROC));
|
||||
}
|
||||
|
||||
static PikaProcedure *
|
||||
destripe_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name)
|
||||
{
|
||||
PikaProcedure *procedure = NULL;
|
||||
|
||||
if (! strcmp (name, PLUG_IN_PROC))
|
||||
{
|
||||
procedure = pika_image_procedure_new (plug_in, name,
|
||||
PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
destripe_run, NULL, NULL);
|
||||
|
||||
pika_procedure_set_image_types (procedure, "RGB*, GRAY*");
|
||||
pika_procedure_set_sensitivity_mask (procedure,
|
||||
PIKA_PROCEDURE_SENSITIVE_DRAWABLE);
|
||||
|
||||
pika_procedure_set_menu_label (procedure, _("Des_tripe..."));
|
||||
pika_procedure_add_menu_path (procedure, "<Image>/Colors/Tone Mapping");
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
_("Remove vertical stripe artifacts "
|
||||
"from the image"),
|
||||
_("This plug-in tries to remove vertical "
|
||||
"stripes from an image."),
|
||||
name);
|
||||
pika_procedure_set_attribution (procedure,
|
||||
"Marc Lehmann <pcg@goof.com>",
|
||||
"Marc Lehmann <pcg@goof.com>",
|
||||
PLUG_IN_VERSION);
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "avg-width",
|
||||
_("_Width"),
|
||||
_("Averaging filter width"),
|
||||
2, MAX_AVG, 36,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_BOOLEAN (procedure,
|
||||
"create-histogram",
|
||||
_("Create _histogram"),
|
||||
_("Output a histogram"),
|
||||
FALSE,
|
||||
G_PARAM_READWRITE);
|
||||
}
|
||||
|
||||
return procedure;
|
||||
}
|
||||
|
||||
static PikaValueArray *
|
||||
destripe_run (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data)
|
||||
{
|
||||
PikaProcedureConfig *config;
|
||||
PikaDrawable *drawable;
|
||||
|
||||
gegl_init (NULL, NULL);
|
||||
|
||||
config = pika_procedure_create_config (procedure);
|
||||
pika_procedure_config_begin_run (config, NULL, run_mode, args);
|
||||
|
||||
if (n_drawables != 1)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
pika_procedure_config_end_run (config, PIKA_PDB_EXECUTION_ERROR);
|
||||
g_object_unref (config);
|
||||
|
||||
g_set_error (&error, PIKA_PLUG_IN_ERROR, 0,
|
||||
_("Procedure '%s' only works with one drawable."),
|
||||
PLUG_IN_PROC);
|
||||
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_CALLING_ERROR,
|
||||
error);
|
||||
}
|
||||
else
|
||||
{
|
||||
drawable = drawables[0];
|
||||
}
|
||||
|
||||
switch (run_mode)
|
||||
{
|
||||
case PIKA_RUN_INTERACTIVE:
|
||||
if (! destripe_dialog (procedure, G_OBJECT (config), drawable))
|
||||
{
|
||||
pika_procedure_config_end_run (config, PIKA_PDB_CANCEL);
|
||||
g_object_unref (config);
|
||||
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_CANCEL,
|
||||
NULL);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
};
|
||||
|
||||
if (pika_drawable_is_rgb (drawable) ||
|
||||
pika_drawable_is_gray (drawable))
|
||||
{
|
||||
destripe (G_OBJECT (config), drawable, NULL);
|
||||
|
||||
if (run_mode != PIKA_RUN_NONINTERACTIVE)
|
||||
pika_displays_flush ();
|
||||
}
|
||||
else
|
||||
{
|
||||
pika_procedure_config_end_run (config, PIKA_PDB_EXECUTION_ERROR);
|
||||
g_object_unref (config);
|
||||
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_EXECUTION_ERROR,
|
||||
NULL);
|
||||
}
|
||||
|
||||
pika_procedure_config_end_run (config, PIKA_PDB_SUCCESS);
|
||||
g_object_unref (config);
|
||||
|
||||
return pika_procedure_new_return_values (procedure, PIKA_PDB_SUCCESS, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
destripe (GObject *config,
|
||||
PikaDrawable *drawable,
|
||||
PikaPreview *preview)
|
||||
{
|
||||
GeglBuffer *src_buffer;
|
||||
GeglBuffer *dest_buffer;
|
||||
const Babl *format;
|
||||
guchar *src_rows; /* image data */
|
||||
gdouble progress, progress_inc;
|
||||
gint x1, x2, y1;
|
||||
gint width, height;
|
||||
gint bpp;
|
||||
glong *hist, *corr; /* "histogram" data */
|
||||
gint tile_width = pika_tile_width ();
|
||||
gint i, x, y, ox, cols;
|
||||
gint avg_width;
|
||||
gboolean histogram;
|
||||
|
||||
g_object_get (config,
|
||||
"avg-width", &avg_width,
|
||||
"create-histogram", &histogram,
|
||||
NULL);
|
||||
|
||||
progress = 0.0;
|
||||
progress_inc = 0.0;
|
||||
|
||||
if (preview)
|
||||
{
|
||||
pika_preview_get_position (preview, &x1, &y1);
|
||||
pika_preview_get_size (preview, &width, &height);
|
||||
}
|
||||
else
|
||||
{
|
||||
pika_progress_init (_("Destriping"));
|
||||
|
||||
if (! pika_drawable_mask_intersect (drawable,
|
||||
&x1, &y1, &width, &height))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
progress = 0;
|
||||
progress_inc = 0.5 * tile_width / width;
|
||||
}
|
||||
|
||||
x2 = x1 + width;
|
||||
|
||||
if (pika_drawable_is_rgb (drawable))
|
||||
{
|
||||
if (pika_drawable_has_alpha (drawable))
|
||||
format = babl_format ("R'G'B'A u8");
|
||||
else
|
||||
format = babl_format ("R'G'B' u8");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pika_drawable_has_alpha (drawable))
|
||||
format = babl_format ("Y'A u8");
|
||||
else
|
||||
format = babl_format ("Y' u8");
|
||||
}
|
||||
|
||||
bpp = babl_format_get_bytes_per_pixel (format);
|
||||
|
||||
/*
|
||||
* Setup for filter...
|
||||
*/
|
||||
|
||||
src_buffer = pika_drawable_get_buffer (drawable);
|
||||
dest_buffer = pika_drawable_get_shadow_buffer (drawable);
|
||||
|
||||
hist = g_new (long, width * bpp);
|
||||
corr = g_new (long, width * bpp);
|
||||
src_rows = g_new (guchar, tile_width * height * bpp);
|
||||
|
||||
memset (hist, 0, width * bpp * sizeof (long));
|
||||
|
||||
/*
|
||||
* collect "histogram" data.
|
||||
*/
|
||||
|
||||
for (ox = x1; ox < x2; ox += tile_width)
|
||||
{
|
||||
guchar *rows = src_rows;
|
||||
|
||||
cols = x2 - ox;
|
||||
if (cols > tile_width)
|
||||
cols = tile_width;
|
||||
|
||||
gegl_buffer_get (src_buffer, GEGL_RECTANGLE (ox, y1, cols, height), 1.0,
|
||||
format, rows,
|
||||
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
|
||||
|
||||
for (y = 0; y < height; y++)
|
||||
{
|
||||
long *h = hist + (ox - x1) * bpp;
|
||||
guchar *row_end = rows + cols * bpp;
|
||||
|
||||
while (rows < row_end)
|
||||
*h++ += *rows++;
|
||||
}
|
||||
|
||||
if (! preview)
|
||||
pika_progress_update (progress += progress_inc);
|
||||
}
|
||||
|
||||
/*
|
||||
* average out histogram
|
||||
*/
|
||||
|
||||
{
|
||||
gint extend = (avg_width / 2) * bpp;
|
||||
|
||||
for (i = 0; i < MIN (3, bpp); i++)
|
||||
{
|
||||
long *h = hist - extend + i;
|
||||
long *c = corr - extend + i;
|
||||
long sum = 0;
|
||||
gint cnt = 0;
|
||||
|
||||
for (x = -extend; x < width * bpp; x += bpp)
|
||||
{
|
||||
if (x + extend < width * bpp)
|
||||
{
|
||||
sum += h[ extend]; cnt++;
|
||||
}
|
||||
|
||||
if (x - extend >= 0)
|
||||
{
|
||||
sum -= h[-extend]; cnt--;
|
||||
}
|
||||
|
||||
if (x >= 0)
|
||||
{
|
||||
if (*h)
|
||||
*c = ((sum / cnt - *h) << 10) / *h;
|
||||
else
|
||||
*c = G_MAXINT;
|
||||
}
|
||||
|
||||
h += bpp;
|
||||
c += bpp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* remove stripes.
|
||||
*/
|
||||
|
||||
for (ox = x1; ox < x2; ox += tile_width)
|
||||
{
|
||||
guchar *rows = src_rows;
|
||||
|
||||
cols = x2 - ox;
|
||||
if (cols > tile_width)
|
||||
cols = tile_width;
|
||||
|
||||
gegl_buffer_get (src_buffer, GEGL_RECTANGLE (ox, y1, cols, height), 1.0,
|
||||
format, rows,
|
||||
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
|
||||
|
||||
if (! preview)
|
||||
pika_progress_update (progress += progress_inc);
|
||||
|
||||
for (y = 0; y < height; y++)
|
||||
{
|
||||
long *c = corr + (ox - x1) * bpp;
|
||||
guchar *row_end = rows + cols * bpp;
|
||||
|
||||
if (histogram)
|
||||
{
|
||||
while (rows < row_end)
|
||||
{
|
||||
*rows = MIN (255, MAX (0, 128 + (*rows * *c >> 10)));
|
||||
c++; rows++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while (rows < row_end)
|
||||
{
|
||||
*rows = MIN (255, MAX (0, *rows + (*rows * *c >> 10) ));
|
||||
c++; rows++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gegl_buffer_set (dest_buffer, GEGL_RECTANGLE (ox, y1, cols, height), 0,
|
||||
format, src_rows,
|
||||
GEGL_AUTO_ROWSTRIDE);
|
||||
|
||||
if (! preview)
|
||||
pika_progress_update (progress += progress_inc);
|
||||
}
|
||||
|
||||
g_free (src_rows);
|
||||
|
||||
g_object_unref (src_buffer);
|
||||
|
||||
if (preview)
|
||||
{
|
||||
guchar *buffer = g_new (guchar, width * height * bpp);
|
||||
|
||||
gegl_buffer_get (dest_buffer, GEGL_RECTANGLE (x1, y1, width, height), 1.0,
|
||||
format, buffer,
|
||||
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
|
||||
|
||||
pika_preview_draw_buffer (PIKA_PREVIEW (preview),
|
||||
buffer, width * bpp);
|
||||
|
||||
g_free (buffer);
|
||||
g_object_unref (dest_buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_object_unref (dest_buffer);
|
||||
|
||||
pika_progress_update (1.0);
|
||||
|
||||
pika_drawable_merge_shadow (drawable, TRUE);
|
||||
pika_drawable_update (drawable,
|
||||
x1, y1, width, height);
|
||||
}
|
||||
|
||||
g_free (hist);
|
||||
g_free (corr);
|
||||
}
|
||||
|
||||
static void
|
||||
destripe_preview (GtkWidget *widget,
|
||||
GObject *config)
|
||||
{
|
||||
PikaPreview *preview = PIKA_PREVIEW (widget);
|
||||
PikaDrawable *drawable = g_object_get_data (config, "drawable");
|
||||
|
||||
destripe (config, drawable, preview);
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
destripe_dialog (PikaProcedure *procedure,
|
||||
GObject *config,
|
||||
PikaDrawable *drawable)
|
||||
{
|
||||
GtkWidget *dialog;
|
||||
GtkWidget *preview;
|
||||
GtkWidget *scale;
|
||||
GtkWidget *button;
|
||||
gboolean run;
|
||||
|
||||
pika_ui_init (PLUG_IN_BINARY);
|
||||
|
||||
dialog = pika_procedure_dialog_new (procedure,
|
||||
PIKA_PROCEDURE_CONFIG (config),
|
||||
_("Destripe"));
|
||||
|
||||
pika_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
|
||||
GTK_RESPONSE_OK,
|
||||
GTK_RESPONSE_CANCEL,
|
||||
-1);
|
||||
|
||||
pika_window_set_transient (GTK_WINDOW (dialog));
|
||||
|
||||
preview = pika_drawable_preview_new_from_drawable (drawable);
|
||||
gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
|
||||
preview, TRUE, TRUE, 0);
|
||||
gtk_widget_show (preview);
|
||||
|
||||
scale = pika_procedure_dialog_get_scale_entry (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"avg-width", 1.0);
|
||||
gtk_widget_set_margin_bottom (scale, 12);
|
||||
|
||||
button = pika_procedure_dialog_get_widget (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"create-histogram",
|
||||
GTK_TYPE_CHECK_BUTTON);
|
||||
gtk_widget_set_margin_bottom (button, 12);
|
||||
|
||||
g_object_set_data (config, "drawable", drawable);
|
||||
|
||||
g_signal_connect (preview, "invalidated",
|
||||
G_CALLBACK (destripe_preview),
|
||||
config);
|
||||
|
||||
g_signal_connect_swapped (config, "notify",
|
||||
G_CALLBACK (pika_preview_invalidate),
|
||||
preview);
|
||||
|
||||
pika_procedure_dialog_fill (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"avg-width", "create-histogram",
|
||||
NULL);
|
||||
|
||||
gtk_widget_show (dialog);
|
||||
|
||||
run = pika_procedure_dialog_run (PIKA_PROCEDURE_DIALOG (dialog));
|
||||
|
||||
gtk_widget_destroy (dialog);
|
||||
|
||||
return run;
|
||||
}
|
||||
414
plug-ins/common/file-aa.c
Normal file
414
plug-ins/common/file-aa.c
Normal file
@ -0,0 +1,414 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* aa.c version 1.0
|
||||
* A plugin that uses libaa (ftp://ftp.ta.jcu.cz/pub/aa) to save images as
|
||||
* ASCII.
|
||||
* NOTE: This plugin *requires* aalib 1.2 or later. Earlier versions will
|
||||
* not work.
|
||||
* Code copied from all over the PIKA source.
|
||||
* Tim Newsome <nuisance@cmu.edu>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <aalib.h>
|
||||
|
||||
#include <libpika/pika.h>
|
||||
#include <libpika/pikaui.h>
|
||||
|
||||
#include "libpika/stdplugins-intl.h"
|
||||
|
||||
|
||||
#define SAVE_PROC "file-aa-save"
|
||||
#define PLUG_IN_BINARY "file-aa"
|
||||
|
||||
|
||||
typedef struct _Ascii Ascii;
|
||||
typedef struct _AsciiClass AsciiClass;
|
||||
|
||||
struct _Ascii
|
||||
{
|
||||
PikaPlugIn parent_instance;
|
||||
};
|
||||
|
||||
struct _AsciiClass
|
||||
{
|
||||
PikaPlugInClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
#define ASCII_TYPE (ascii_get_type ())
|
||||
#define ASCII (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ASCII_TYPE, Ascii))
|
||||
|
||||
GType ascii_get_type (void) G_GNUC_CONST;
|
||||
|
||||
static GList * ascii_query_procedures (PikaPlugIn *plug_in);
|
||||
static PikaProcedure * ascii_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name);
|
||||
|
||||
static PikaValueArray * ascii_save (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
GFile *file,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data);
|
||||
|
||||
static gboolean save_aa (GFile *file,
|
||||
PikaDrawable *drawable,
|
||||
GObject *config,
|
||||
GError **error);
|
||||
static void pika2aa (PikaDrawable *drawable,
|
||||
aa_context *context);
|
||||
|
||||
static gboolean save_dialog (PikaProcedure *procedure,
|
||||
GObject *config,
|
||||
PikaImage *image);
|
||||
|
||||
|
||||
G_DEFINE_TYPE (Ascii, ascii, PIKA_TYPE_PLUG_IN)
|
||||
|
||||
PIKA_MAIN (ASCII_TYPE)
|
||||
DEFINE_STD_SET_I18N
|
||||
|
||||
|
||||
static void
|
||||
ascii_class_init (AsciiClass *klass)
|
||||
{
|
||||
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
|
||||
|
||||
plug_in_class->query_procedures = ascii_query_procedures;
|
||||
plug_in_class->create_procedure = ascii_create_procedure;
|
||||
plug_in_class->set_i18n = STD_SET_I18N;
|
||||
}
|
||||
|
||||
static void
|
||||
ascii_init (Ascii *ascii)
|
||||
{
|
||||
}
|
||||
|
||||
static GList *
|
||||
ascii_query_procedures (PikaPlugIn *plug_in)
|
||||
{
|
||||
return g_list_append (NULL, g_strdup (SAVE_PROC));
|
||||
}
|
||||
|
||||
static PikaProcedure *
|
||||
ascii_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name)
|
||||
{
|
||||
PikaProcedure *procedure = NULL;
|
||||
|
||||
if (! strcmp (name, SAVE_PROC))
|
||||
{
|
||||
gint i;
|
||||
|
||||
procedure = pika_save_procedure_new (plug_in, name,
|
||||
PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
ascii_save, NULL, NULL);
|
||||
|
||||
pika_procedure_set_image_types (procedure, "*");
|
||||
|
||||
pika_procedure_set_menu_label (procedure, _("ASCII art"));
|
||||
pika_file_procedure_set_format_name (PIKA_FILE_PROCEDURE (procedure),
|
||||
_("ASCII art"));
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
_("Saves grayscale image in various "
|
||||
"text formats"),
|
||||
_("This plug-in uses aalib to save "
|
||||
"grayscale image as ascii art into "
|
||||
"a variety of text formats"),
|
||||
name);
|
||||
pika_procedure_set_attribution (procedure,
|
||||
"Tim Newsome <nuisance@cmu.edu>",
|
||||
"Tim Newsome <nuisance@cmu.edu>",
|
||||
"1997");
|
||||
|
||||
pika_file_procedure_set_mime_types (PIKA_FILE_PROCEDURE (procedure),
|
||||
"text/plain");
|
||||
pika_file_procedure_set_extensions (PIKA_FILE_PROCEDURE (procedure),
|
||||
"txt,ansi,text");
|
||||
|
||||
for (i = 0; aa_formats[i]; i++);
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "file-type",
|
||||
_("_Format"),
|
||||
_("File type to use"),
|
||||
0, i, 0,
|
||||
G_PARAM_READWRITE);
|
||||
}
|
||||
|
||||
return procedure;
|
||||
}
|
||||
|
||||
static PikaValueArray *
|
||||
ascii_save (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
GFile *file,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data)
|
||||
{
|
||||
PikaProcedureConfig *config;
|
||||
PikaPDBStatusType status = PIKA_PDB_SUCCESS;
|
||||
PikaExportReturn export = PIKA_EXPORT_CANCEL;
|
||||
GError *error = NULL;
|
||||
|
||||
gegl_init (NULL, NULL);
|
||||
|
||||
config = pika_procedure_create_config (procedure);
|
||||
pika_procedure_config_begin_run (config, image, run_mode, args);
|
||||
|
||||
switch (run_mode)
|
||||
{
|
||||
case PIKA_RUN_INTERACTIVE:
|
||||
case PIKA_RUN_WITH_LAST_VALS:
|
||||
pika_ui_init (PLUG_IN_BINARY);
|
||||
|
||||
export = pika_export_image (&image, &n_drawables, &drawables, "AA",
|
||||
PIKA_EXPORT_CAN_HANDLE_RGB |
|
||||
PIKA_EXPORT_CAN_HANDLE_GRAY |
|
||||
PIKA_EXPORT_CAN_HANDLE_INDEXED |
|
||||
PIKA_EXPORT_CAN_HANDLE_ALPHA);
|
||||
|
||||
if (export == PIKA_EXPORT_CANCEL)
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_CANCEL,
|
||||
NULL);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (n_drawables != 1)
|
||||
{
|
||||
g_set_error (&error, G_FILE_ERROR, 0,
|
||||
_("ASCII art does not support multiple layers."));
|
||||
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_CALLING_ERROR,
|
||||
error);
|
||||
}
|
||||
|
||||
if (run_mode == PIKA_RUN_INTERACTIVE)
|
||||
{
|
||||
if (! save_dialog (procedure, G_OBJECT (config), image))
|
||||
status = PIKA_PDB_CANCEL;
|
||||
}
|
||||
|
||||
if (status == PIKA_PDB_SUCCESS)
|
||||
{
|
||||
if (! save_aa (file, drawables[0], G_OBJECT (config), &error))
|
||||
{
|
||||
status = PIKA_PDB_EXECUTION_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
pika_procedure_config_end_run (config, status);
|
||||
g_object_unref (config);
|
||||
|
||||
if (export == PIKA_EXPORT_EXPORT)
|
||||
{
|
||||
pika_image_delete (image);
|
||||
g_free (drawables);
|
||||
}
|
||||
|
||||
return pika_procedure_new_return_values (procedure, status, error);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
save_aa (GFile *file,
|
||||
PikaDrawable *drawable,
|
||||
GObject *config,
|
||||
GError **error)
|
||||
{
|
||||
aa_savedata savedata;
|
||||
aa_context *context;
|
||||
aa_format format;
|
||||
gint output_type;
|
||||
|
||||
g_object_get (config,
|
||||
"file-type", &output_type,
|
||||
NULL);
|
||||
|
||||
memcpy (&format, aa_formats[output_type], sizeof (aa_format));
|
||||
|
||||
format.width = pika_drawable_get_width (drawable) / 2;
|
||||
format.height = pika_drawable_get_height (drawable) / 2;
|
||||
|
||||
/* Get a libaa context which will save its output to filename. */
|
||||
savedata.name = g_file_get_path (file);
|
||||
savedata.format = &format;
|
||||
|
||||
context = aa_init (&save_d, &aa_defparams, &savedata);
|
||||
if (! context)
|
||||
return FALSE;
|
||||
|
||||
pika2aa (drawable, context);
|
||||
|
||||
aa_flush (context);
|
||||
aa_close (context);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
pika2aa (PikaDrawable *drawable,
|
||||
aa_context *context)
|
||||
{
|
||||
GeglBuffer *buffer;
|
||||
const Babl *format;
|
||||
aa_renderparams *renderparams;
|
||||
gint width;
|
||||
gint height;
|
||||
gint x, y;
|
||||
gint bpp;
|
||||
guchar *buf;
|
||||
guchar *p;
|
||||
|
||||
buffer = pika_drawable_get_buffer (drawable);
|
||||
|
||||
width = aa_imgwidth (context);
|
||||
height = aa_imgheight (context);
|
||||
|
||||
switch (pika_drawable_type (drawable))
|
||||
{
|
||||
case PIKA_GRAY_IMAGE:
|
||||
format = babl_format ("Y' u8");
|
||||
break;
|
||||
|
||||
case PIKA_GRAYA_IMAGE:
|
||||
format = babl_format ("Y'A u8");
|
||||
break;
|
||||
|
||||
case PIKA_RGB_IMAGE:
|
||||
case PIKA_INDEXED_IMAGE:
|
||||
format = babl_format ("R'G'B' u8");
|
||||
break;
|
||||
|
||||
case PIKA_RGBA_IMAGE:
|
||||
case PIKA_INDEXEDA_IMAGE:
|
||||
format = babl_format ("R'G'B'A u8");
|
||||
break;
|
||||
|
||||
default:
|
||||
g_return_if_reached ();
|
||||
break;
|
||||
}
|
||||
|
||||
bpp = babl_format_get_bytes_per_pixel (format);
|
||||
|
||||
buf = g_new (guchar, width * bpp);
|
||||
|
||||
for (y = 0; y < height; y++)
|
||||
{
|
||||
gegl_buffer_get (buffer, GEGL_RECTANGLE (0, y, width, 1), 1.0,
|
||||
format, buf,
|
||||
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
|
||||
|
||||
switch (bpp)
|
||||
{
|
||||
case 1: /* GRAY */
|
||||
for (x = 0, p = buf; x < width; x++, p++)
|
||||
aa_putpixel (context, x, y, *p);
|
||||
break;
|
||||
|
||||
case 2: /* GRAYA, blend over black */
|
||||
for (x = 0, p = buf; x < width; x++, p += 2)
|
||||
aa_putpixel (context, x, y, (p[0] * (p[1] + 1)) >> 8);
|
||||
break;
|
||||
|
||||
case 3: /* RGB */
|
||||
for (x = 0, p = buf; x < width; x++, p += 3)
|
||||
aa_putpixel (context, x, y,
|
||||
PIKA_RGB_LUMINANCE (p[0], p[1], p[2]) + 0.5);
|
||||
break;
|
||||
|
||||
case 4: /* RGBA, blend over black */
|
||||
for (x = 0, p = buf; x < width; x++, p += 4)
|
||||
aa_putpixel (context, x, y,
|
||||
((guchar) (PIKA_RGB_LUMINANCE (p[0], p[1], p[2]) + 0.5)
|
||||
* (p[3] + 1)) >> 8);
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
g_free (buf);
|
||||
|
||||
g_object_unref (buffer);
|
||||
|
||||
renderparams = aa_getrenderparams ();
|
||||
renderparams->dither = AA_FLOYD_S;
|
||||
|
||||
aa_render (context, renderparams, 0, 0,
|
||||
aa_scrwidth (context), aa_scrheight (context));
|
||||
}
|
||||
|
||||
static gboolean
|
||||
save_dialog (PikaProcedure *procedure,
|
||||
GObject *config,
|
||||
PikaImage *image)
|
||||
{
|
||||
GtkWidget *dialog;
|
||||
GtkListStore *store;
|
||||
GtkWidget *combo;
|
||||
gint i;
|
||||
gboolean run;
|
||||
|
||||
dialog = pika_save_procedure_dialog_new (PIKA_SAVE_PROCEDURE (procedure),
|
||||
PIKA_PROCEDURE_CONFIG (config),
|
||||
image);
|
||||
|
||||
store = g_object_new (PIKA_TYPE_INT_STORE, NULL);
|
||||
|
||||
for (i = 0; aa_formats[i]; i++)
|
||||
gtk_list_store_insert_with_values (store, NULL, -1,
|
||||
PIKA_INT_STORE_VALUE, i,
|
||||
PIKA_INT_STORE_LABEL, aa_formats[i]->formatname,
|
||||
-1);
|
||||
|
||||
combo = pika_procedure_dialog_get_int_combo (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"file-type",
|
||||
PIKA_INT_STORE (store));
|
||||
g_object_set (combo, "margin", 12, NULL);
|
||||
|
||||
pika_procedure_dialog_fill (PIKA_PROCEDURE_DIALOG (dialog), NULL);
|
||||
gtk_widget_show (dialog);
|
||||
|
||||
run = pika_procedure_dialog_run (PIKA_PROCEDURE_DIALOG (dialog));
|
||||
|
||||
gtk_widget_destroy (dialog);
|
||||
|
||||
return run;
|
||||
}
|
||||
1029
plug-ins/common/file-cel.c
Normal file
1029
plug-ins/common/file-cel.c
Normal file
File diff suppressed because it is too large
Load Diff
1040
plug-ins/common/file-compressor.c
Normal file
1040
plug-ins/common/file-compressor.c
Normal file
File diff suppressed because it is too large
Load Diff
997
plug-ins/common/file-csource.c
Normal file
997
plug-ins/common/file-csource.c
Normal file
@ -0,0 +1,997 @@
|
||||
/* CSource - PIKA Plugin to dump image data in RGB(A) format for C source
|
||||
* Copyright (C) 1999 Tim Janik
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 3
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This plugin is heavily based on the header plugin by Spencer Kimball and
|
||||
* Peter Mattis.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <glib/gstdio.h>
|
||||
|
||||
#include <libpika/pika.h>
|
||||
#include <libpika/pikaui.h>
|
||||
|
||||
#include "libpika/stdplugins-intl.h"
|
||||
|
||||
|
||||
#define SAVE_PROC "file-csource-save"
|
||||
#define PLUG_IN_BINARY "file-csource"
|
||||
#define PLUG_IN_ROLE "pika-file-csource"
|
||||
|
||||
|
||||
typedef struct _Csource Csource;
|
||||
typedef struct _CsourceClass CsourceClass;
|
||||
|
||||
struct _Csource
|
||||
{
|
||||
PikaPlugIn parent_instance;
|
||||
};
|
||||
|
||||
struct _CsourceClass
|
||||
{
|
||||
PikaPlugInClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
#define CSOURCE_TYPE (csource_get_type ())
|
||||
#define CSOURCE (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CSOURCE_TYPE, Csource))
|
||||
|
||||
GType csource_get_type (void) G_GNUC_CONST;
|
||||
|
||||
static GList * csource_query_procedures (PikaPlugIn *plug_in);
|
||||
static PikaProcedure * csource_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name);
|
||||
|
||||
static PikaValueArray * csource_save (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
GFile *file,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data);
|
||||
|
||||
static gboolean save_image (GFile *file,
|
||||
PikaImage *image,
|
||||
PikaDrawable *drawable,
|
||||
GObject *config,
|
||||
GError **error);
|
||||
static gboolean save_dialog (PikaImage *image,
|
||||
PikaProcedure *procedure,
|
||||
GObject *config);
|
||||
|
||||
|
||||
G_DEFINE_TYPE (Csource, csource, PIKA_TYPE_PLUG_IN)
|
||||
|
||||
PIKA_MAIN (CSOURCE_TYPE)
|
||||
DEFINE_STD_SET_I18N
|
||||
|
||||
|
||||
static void
|
||||
csource_class_init (CsourceClass *klass)
|
||||
{
|
||||
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
|
||||
|
||||
plug_in_class->query_procedures = csource_query_procedures;
|
||||
plug_in_class->create_procedure = csource_create_procedure;
|
||||
plug_in_class->set_i18n = STD_SET_I18N;
|
||||
}
|
||||
|
||||
static void
|
||||
csource_init (Csource *csource)
|
||||
{
|
||||
}
|
||||
|
||||
static GList *
|
||||
csource_query_procedures (PikaPlugIn *plug_in)
|
||||
{
|
||||
return g_list_append (NULL, g_strdup (SAVE_PROC));
|
||||
}
|
||||
|
||||
static PikaProcedure *
|
||||
csource_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name)
|
||||
{
|
||||
PikaProcedure *procedure = NULL;
|
||||
|
||||
if (! strcmp (name, SAVE_PROC))
|
||||
{
|
||||
procedure = pika_save_procedure_new (plug_in, name,
|
||||
PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
csource_save, NULL, NULL);
|
||||
|
||||
pika_procedure_set_image_types (procedure, "*");
|
||||
|
||||
pika_procedure_set_menu_label (procedure, _("C source code"));
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
_("Dump image data in RGB(A) format "
|
||||
"for C source"),
|
||||
_("CSource cannot be run non-interactively."),
|
||||
name);
|
||||
pika_procedure_set_attribution (procedure,
|
||||
"Tim Janik",
|
||||
"Tim Janik",
|
||||
"1999");
|
||||
|
||||
pika_file_procedure_set_format_name (PIKA_FILE_PROCEDURE (procedure),
|
||||
_("C-Source"));
|
||||
pika_file_procedure_set_handles_remote (PIKA_FILE_PROCEDURE (procedure),
|
||||
TRUE);
|
||||
pika_file_procedure_set_mime_types (PIKA_FILE_PROCEDURE (procedure),
|
||||
"image/x-csrc");
|
||||
pika_file_procedure_set_extensions (PIKA_FILE_PROCEDURE (procedure),
|
||||
"c");
|
||||
|
||||
PIKA_PROC_AUX_ARG_STRING (procedure, "prefixed-name",
|
||||
_("_Prefixed name"),
|
||||
_("Prefixed name"),
|
||||
"pika_image",
|
||||
PIKA_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_AUX_ARG_STRING (procedure, "pika-comment",
|
||||
_("Comme_nt"),
|
||||
_("Comment"),
|
||||
pika_get_default_comment (),
|
||||
PIKA_PARAM_READWRITE);
|
||||
|
||||
pika_procedure_set_argument_sync (procedure, "pika-comment",
|
||||
PIKA_ARGUMENT_SYNC_PARASITE);
|
||||
|
||||
PIKA_PROC_AUX_ARG_BOOLEAN (procedure, "save-comment",
|
||||
_("Save comment to _file"),
|
||||
_("Save comment"),
|
||||
pika_export_comment (),
|
||||
PIKA_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_AUX_ARG_BOOLEAN (procedure, "glib-types",
|
||||
_("Use GLib types (guint_8*)"),
|
||||
_("Use GLib types"),
|
||||
TRUE,
|
||||
PIKA_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_AUX_ARG_BOOLEAN (procedure, "save-alpha",
|
||||
_("Save alpha channel (RG_BA/RGB)"),
|
||||
_("Save the alpha channel"),
|
||||
FALSE,
|
||||
PIKA_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_AUX_ARG_BOOLEAN (procedure, "rgb565",
|
||||
_("Save as RGB565 (1_6-bit)"),
|
||||
_("Use RGB565 encoding"),
|
||||
FALSE,
|
||||
PIKA_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_AUX_ARG_BOOLEAN (procedure, "use-macros",
|
||||
_("_Use macros instead of struct"),
|
||||
_("Use C macros"),
|
||||
FALSE,
|
||||
PIKA_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_AUX_ARG_BOOLEAN (procedure, "use-rle",
|
||||
_("Use _1 bit Run-Length-Encoding"),
|
||||
_("Use run-length-encoding"),
|
||||
FALSE,
|
||||
PIKA_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_AUX_ARG_DOUBLE (procedure, "opacity",
|
||||
_("Opaci_ty"),
|
||||
_("Opacity"),
|
||||
0.0, 100.0, 100.0,
|
||||
PIKA_PARAM_READWRITE);
|
||||
}
|
||||
|
||||
return procedure;
|
||||
}
|
||||
|
||||
static PikaValueArray *
|
||||
csource_save (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
GFile *file,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data)
|
||||
{
|
||||
PikaProcedureConfig *config;
|
||||
PikaPDBStatusType status = PIKA_PDB_SUCCESS;
|
||||
PikaExportReturn export = PIKA_EXPORT_CANCEL;
|
||||
gchar *prefixed_name;
|
||||
gchar *comment;
|
||||
GError *error = NULL;
|
||||
|
||||
gegl_init (NULL, NULL);
|
||||
|
||||
if (run_mode != PIKA_RUN_INTERACTIVE)
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_CALLING_ERROR,
|
||||
NULL);
|
||||
|
||||
config = pika_procedure_create_config (procedure);
|
||||
pika_procedure_config_begin_export (config, image, run_mode, args, NULL);
|
||||
|
||||
pika_ui_init (PLUG_IN_BINARY);
|
||||
|
||||
export = pika_export_image (&image, &n_drawables, &drawables, "C Source",
|
||||
PIKA_EXPORT_CAN_HANDLE_RGB |
|
||||
PIKA_EXPORT_CAN_HANDLE_ALPHA);
|
||||
|
||||
if (n_drawables != 1)
|
||||
{
|
||||
g_set_error (&error, G_FILE_ERROR, 0,
|
||||
_("C source does not support multiple layers."));
|
||||
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_CALLING_ERROR,
|
||||
error);
|
||||
}
|
||||
|
||||
g_object_set (config,
|
||||
"save-alpha", pika_drawable_has_alpha (drawables[0]),
|
||||
NULL);
|
||||
|
||||
if (export == PIKA_EXPORT_CANCEL)
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_CANCEL,
|
||||
NULL);
|
||||
|
||||
if (! save_dialog (image, procedure, G_OBJECT (config)))
|
||||
status = PIKA_PDB_CANCEL;
|
||||
|
||||
g_object_get (config,
|
||||
"prefixed-name", &prefixed_name,
|
||||
"pika-comment", &comment,
|
||||
NULL);
|
||||
|
||||
if (! prefixed_name || ! prefixed_name[0])
|
||||
g_object_set (config,
|
||||
"prefixed-name", "tmp",
|
||||
NULL);
|
||||
|
||||
if (comment && ! comment[0])
|
||||
g_object_set (config,
|
||||
"pika-comment", NULL,
|
||||
NULL);
|
||||
|
||||
g_free (prefixed_name);
|
||||
g_free (comment);
|
||||
|
||||
if (status == PIKA_PDB_SUCCESS)
|
||||
{
|
||||
if (! save_image (file, image, drawables[0], G_OBJECT (config),
|
||||
&error))
|
||||
{
|
||||
status = PIKA_PDB_EXECUTION_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
pika_procedure_config_end_export (config, image, file, status);
|
||||
g_object_unref (config);
|
||||
|
||||
if (export == PIKA_EXPORT_EXPORT)
|
||||
{
|
||||
pika_image_delete (image);
|
||||
g_free (drawables);
|
||||
}
|
||||
|
||||
return pika_procedure_new_return_values (procedure, status, error);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
diff2_rgb565 (guint8 *ip)
|
||||
{
|
||||
return ip[0] != ip[2] || ip[1] != ip[3];
|
||||
}
|
||||
|
||||
static gboolean
|
||||
diff2_rgb (guint8 *ip)
|
||||
{
|
||||
return ip[0] != ip[3] || ip[1] != ip[4] || ip[2] != ip[5];
|
||||
}
|
||||
|
||||
static gboolean
|
||||
diff2_rgba (guint8 *ip)
|
||||
{
|
||||
return ip[0] != ip[4] || ip[1] != ip[5] || ip[2] != ip[6] || ip[3] != ip[7];
|
||||
}
|
||||
|
||||
static guint8 *
|
||||
rl_encode_rgbx (guint8 *bp,
|
||||
guint8 *ip,
|
||||
guint8 *limit,
|
||||
guint bpp)
|
||||
{
|
||||
gboolean (*diff2_pix) (guint8 *);
|
||||
guint8 *ilimit = limit - bpp;
|
||||
|
||||
switch (bpp)
|
||||
{
|
||||
case 2: diff2_pix = diff2_rgb565; break;
|
||||
case 3: diff2_pix = diff2_rgb; break;
|
||||
case 4: diff2_pix = diff2_rgba; break;
|
||||
default: g_assert_not_reached ();
|
||||
}
|
||||
|
||||
while (ip < limit)
|
||||
{
|
||||
g_assert (ip < ilimit); /* paranoid */
|
||||
|
||||
if (diff2_pix (ip))
|
||||
{
|
||||
guint8 *s_ip = ip;
|
||||
guint l = 1;
|
||||
|
||||
ip += bpp;
|
||||
while (l < 127 && ip < ilimit && diff2_pix (ip))
|
||||
{ ip += bpp; l += 1; }
|
||||
if (ip == ilimit && l < 127)
|
||||
{ ip += bpp; l += 1; }
|
||||
*(bp++) = l;
|
||||
memcpy (bp, s_ip, l * bpp);
|
||||
bp += l * bpp;
|
||||
}
|
||||
else
|
||||
{
|
||||
guint l = 2;
|
||||
|
||||
ip += bpp;
|
||||
while (l < 127 && ip < ilimit && !diff2_pix (ip))
|
||||
{ ip += bpp; l += 1; }
|
||||
*(bp++) = l | 128;
|
||||
memcpy (bp, ip, bpp);
|
||||
ip += bpp;
|
||||
bp += bpp;
|
||||
}
|
||||
if (ip == ilimit)
|
||||
{
|
||||
*(bp++) = 1;
|
||||
memcpy (bp, ip, bpp);
|
||||
ip += bpp;
|
||||
bp += bpp;
|
||||
}
|
||||
}
|
||||
|
||||
return bp;
|
||||
}
|
||||
|
||||
static gboolean print (GOutputStream *stream,
|
||||
GError **error,
|
||||
const gchar *format,
|
||||
...) G_GNUC_PRINTF (3, 4);
|
||||
|
||||
static gboolean
|
||||
print (GOutputStream *stream,
|
||||
GError **error,
|
||||
const gchar *format,
|
||||
...)
|
||||
{
|
||||
va_list args;
|
||||
gboolean success;
|
||||
|
||||
va_start (args, format);
|
||||
success = g_output_stream_vprintf (stream, NULL, NULL,
|
||||
error, format, args);
|
||||
va_end (args);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
static inline gboolean
|
||||
save_rle_decoder (GOutputStream *output,
|
||||
const gchar *macro_name,
|
||||
const gchar *s_uint,
|
||||
const gchar *s_uint_8,
|
||||
guint bpp,
|
||||
GError **error)
|
||||
{
|
||||
return
|
||||
print (output, error,
|
||||
"#define %s_RUN_LENGTH_DECODE(image_buf, rle_data, size, bpp) do \\\n",
|
||||
macro_name) &&
|
||||
print (output, error,
|
||||
"{ %s __bpp; %s *__ip; const %s *__il, *__rd; \\\n",
|
||||
s_uint, s_uint_8, s_uint_8) &&
|
||||
print (output, error,
|
||||
" __bpp = (bpp); __ip = (image_buf); __il = __ip + (size) * __bpp; \\\n"
|
||||
" __rd = (rle_data); if (__bpp > 3) { /* RGBA */ \\\n"
|
||||
" while (__ip < __il) { %s __l = *(__rd++); \\\n",
|
||||
s_uint) &&
|
||||
print (output, error,
|
||||
" if (__l & 128) { __l = __l - 128; \\\n"
|
||||
" do { memcpy (__ip, __rd, 4); __ip += 4; } while (--__l); __rd += 4; \\\n"
|
||||
" } else { __l *= 4; memcpy (__ip, __rd, __l); \\\n"
|
||||
" __ip += __l; __rd += __l; } } \\\n"
|
||||
" } else if (__bpp == 3) { /* RGB */ \\\n"
|
||||
" while (__ip < __il) { %s __l = *(__rd++); \\\n",
|
||||
s_uint) &&
|
||||
print (output, error,
|
||||
" if (__l & 128) { __l = __l - 128; \\\n"
|
||||
" do { memcpy (__ip, __rd, 3); __ip += 3; } while (--__l); __rd += 3; \\\n"
|
||||
" } else { __l *= 3; memcpy (__ip, __rd, __l); \\\n"
|
||||
" __ip += __l; __rd += __l; } } \\\n"
|
||||
" } else { /* RGB16 */ \\\n"
|
||||
" while (__ip < __il) { %s __l = *(__rd++); \\\n",
|
||||
s_uint) &&
|
||||
print (output, error,
|
||||
" if (__l & 128) { __l = __l - 128; \\\n"
|
||||
" do { memcpy (__ip, __rd, 2); __ip += 2; } while (--__l); __rd += 2; \\\n"
|
||||
" } else { __l *= 2; memcpy (__ip, __rd, __l); \\\n"
|
||||
" __ip += __l; __rd += __l; } } \\\n"
|
||||
" } } while (0)\n");
|
||||
}
|
||||
|
||||
static inline gboolean
|
||||
save_uchar (GOutputStream *output,
|
||||
guint *c,
|
||||
guint8 d,
|
||||
gboolean use_macros,
|
||||
GError **error)
|
||||
{
|
||||
static guint8 pad = 0;
|
||||
|
||||
if (*c > 74)
|
||||
{
|
||||
if (! use_macros)
|
||||
{
|
||||
if (! print (output, error, "\"\n \""))
|
||||
return FALSE;
|
||||
|
||||
*c = 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (! print (output, error, "\"\n \""))
|
||||
return FALSE;
|
||||
|
||||
*c = 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (d < 33 || (d >= 48 && d <= 57) || d > 126)
|
||||
{
|
||||
if (! print (output, error, "\\%03o", d))
|
||||
return FALSE;
|
||||
|
||||
*c += 1 + 1 + (d > 7) + (d > 63);
|
||||
pad = d < 64;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (d == '\\')
|
||||
{
|
||||
if (! print (output, error, "\\\\"))
|
||||
return FALSE;
|
||||
|
||||
*c += 2;
|
||||
}
|
||||
else if (d == '"')
|
||||
{
|
||||
if (! print (output, error, "\\\""))
|
||||
return FALSE;
|
||||
|
||||
*c += 2;
|
||||
}
|
||||
else if (pad && d >= '0' && d <= '9')
|
||||
{
|
||||
if (! print (output, error, "\"\"%c", d))
|
||||
return FALSE;
|
||||
|
||||
*c += 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (! print (output, error, "%c", d))
|
||||
return FALSE;
|
||||
|
||||
*c += 1;
|
||||
}
|
||||
|
||||
pad = 0;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
save_image (GFile *file,
|
||||
PikaImage *image,
|
||||
PikaDrawable *drawable,
|
||||
GObject *config,
|
||||
GError **error)
|
||||
{
|
||||
GOutputStream *output;
|
||||
GeglBuffer *buffer;
|
||||
GCancellable *cancellable;
|
||||
PikaImageType drawable_type = pika_drawable_type (drawable);
|
||||
gchar *s_uint_8, *s_uint, *s_char, *s_null;
|
||||
guint c;
|
||||
gchar *macro_name;
|
||||
guint8 *img_buffer, *img_buffer_end;
|
||||
gchar *basename;
|
||||
guint8 *data, *p;
|
||||
gint width;
|
||||
gint height;
|
||||
gint x, y, pad, n_bytes, bpp;
|
||||
const Babl *drawable_format;
|
||||
gint drawable_bpp;
|
||||
gchar *config_prefixed_name;
|
||||
gchar *config_comment;
|
||||
gboolean config_save_comment;
|
||||
gboolean config_glib_types;
|
||||
gboolean config_save_alpha;
|
||||
gboolean config_rgb565;
|
||||
gboolean config_use_macros;
|
||||
gboolean config_use_rle;
|
||||
gdouble config_opacity;
|
||||
|
||||
g_object_get (config,
|
||||
"prefixed-name", &config_prefixed_name,
|
||||
"pika-comment", &config_comment,
|
||||
"save-comment", &config_save_comment,
|
||||
"glib-types", &config_glib_types,
|
||||
"save-alpha", &config_save_alpha,
|
||||
"rgb565", &config_rgb565,
|
||||
"use-macros", &config_use_macros,
|
||||
"use-rle", &config_use_rle,
|
||||
"opacity", &config_opacity,
|
||||
NULL);
|
||||
|
||||
output = G_OUTPUT_STREAM (g_file_replace (file,
|
||||
NULL, FALSE, G_FILE_CREATE_NONE,
|
||||
NULL, error));
|
||||
if (output)
|
||||
{
|
||||
GOutputStream *buffered;
|
||||
|
||||
buffered = g_buffered_output_stream_new (output);
|
||||
g_object_unref (output);
|
||||
|
||||
output = buffered;
|
||||
}
|
||||
else
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
buffer = pika_drawable_get_buffer (drawable);
|
||||
|
||||
width = gegl_buffer_get_width (buffer);
|
||||
height = gegl_buffer_get_height (buffer);
|
||||
|
||||
if (pika_drawable_has_alpha (drawable))
|
||||
drawable_format = babl_format ("R'G'B'A u8");
|
||||
else
|
||||
drawable_format = babl_format ("R'G'B' u8");
|
||||
|
||||
drawable_bpp = babl_format_get_bytes_per_pixel (drawable_format);
|
||||
|
||||
bpp = config_rgb565 ? 2 : (config_save_alpha ? 4 : 3);
|
||||
n_bytes = width * height * bpp;
|
||||
pad = width * drawable_bpp;
|
||||
if (config_use_rle)
|
||||
pad = MAX (pad, 130 + n_bytes / 127);
|
||||
|
||||
data = g_new (guint8, pad + n_bytes);
|
||||
p = data + pad;
|
||||
|
||||
for (y = 0; y < height; y++)
|
||||
{
|
||||
gegl_buffer_get (buffer, GEGL_RECTANGLE (0, y, width, 1), 1.0,
|
||||
drawable_format, data,
|
||||
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
|
||||
|
||||
if (bpp == 2)
|
||||
{
|
||||
for (x = 0; x < width; x++)
|
||||
{
|
||||
guint8 *d = data + x * drawable_bpp;
|
||||
guint8 r, g, b;
|
||||
gushort rgb16;
|
||||
gdouble alpha = drawable_type == PIKA_RGBA_IMAGE ? d[3] : 0xff;
|
||||
|
||||
alpha *= config_opacity / 25500.0;
|
||||
r = (0.5 + alpha * (gdouble) d[0]);
|
||||
g = (0.5 + alpha * (gdouble) d[1]);
|
||||
b = (0.5 + alpha * (gdouble) d[2]);
|
||||
r >>= 3;
|
||||
g >>= 2;
|
||||
b >>= 3;
|
||||
rgb16 = (r << 11) + (g << 5) + b;
|
||||
*(p++) = (guchar) rgb16;
|
||||
*(p++) = (guchar) (rgb16 >> 8);
|
||||
}
|
||||
}
|
||||
else if (config_save_alpha)
|
||||
{
|
||||
for (x = 0; x < width; x++)
|
||||
{
|
||||
guint8 *d = data + x * drawable_bpp;
|
||||
gdouble alpha = drawable_type == PIKA_RGBA_IMAGE ? d[3] : 0xff;
|
||||
|
||||
alpha *= config_opacity / 100.0;
|
||||
*(p++) = d[0];
|
||||
*(p++) = d[1];
|
||||
*(p++) = d[2];
|
||||
*(p++) = alpha + 0.5;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (x = 0; x < width; x++)
|
||||
{
|
||||
guint8 *d = data + x * drawable_bpp;
|
||||
gdouble alpha = drawable_type == PIKA_RGBA_IMAGE ? d[3] : 0xff;
|
||||
|
||||
alpha *= config_opacity / 25500.0;
|
||||
*(p++) = 0.5 + alpha * (gdouble) d[0];
|
||||
*(p++) = 0.5 + alpha * (gdouble) d[1];
|
||||
*(p++) = 0.5 + alpha * (gdouble) d[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
img_buffer = data + pad;
|
||||
if (config_use_rle)
|
||||
{
|
||||
img_buffer_end = rl_encode_rgbx (data, img_buffer,
|
||||
img_buffer + n_bytes, bpp);
|
||||
img_buffer = data;
|
||||
}
|
||||
else
|
||||
{
|
||||
img_buffer_end = img_buffer + n_bytes;
|
||||
}
|
||||
|
||||
if (! config_use_macros && config_glib_types)
|
||||
{
|
||||
s_uint_8 = "guint8 ";
|
||||
s_uint = "guint ";
|
||||
s_char = "gchar ";
|
||||
s_null = "NULL";
|
||||
}
|
||||
else if (! config_use_macros)
|
||||
{
|
||||
s_uint_8 = "unsigned char";
|
||||
s_uint = "unsigned int ";
|
||||
s_char = "char ";
|
||||
s_null = "(char*) 0";
|
||||
}
|
||||
else if (config_use_macros && config_glib_types)
|
||||
{
|
||||
s_uint_8 = "guint8";
|
||||
s_uint = "guint";
|
||||
s_char = "gchar";
|
||||
s_null = "NULL";
|
||||
}
|
||||
else /* config_use_macros && ! config_glib_types */
|
||||
{
|
||||
s_uint_8 = "unsigned char";
|
||||
s_uint = "unsigned int";
|
||||
s_char = "char";
|
||||
s_null = "(char*) 0";
|
||||
}
|
||||
|
||||
macro_name = g_ascii_strup (config_prefixed_name, -1);
|
||||
|
||||
basename = g_file_get_basename (file);
|
||||
|
||||
if (! print (output, error,
|
||||
"/* PIKA %s C-Source image dump %s(%s) */\n\n",
|
||||
config_save_alpha ? "RGBA" : "RGB",
|
||||
config_use_rle ? "1-byte-run-length-encoded " : "",
|
||||
basename))
|
||||
goto fail;
|
||||
|
||||
g_free (basename);
|
||||
|
||||
if (config_use_rle && !config_use_macros)
|
||||
{
|
||||
if (! save_rle_decoder (output,
|
||||
macro_name,
|
||||
config_glib_types ? "guint" : "unsigned int",
|
||||
config_glib_types ? "guint8" : "unsigned char",
|
||||
bpp,
|
||||
error))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!config_use_macros)
|
||||
{
|
||||
if (! print (output, error,
|
||||
"static const struct {\n"
|
||||
" %s\t width;\n"
|
||||
" %s\t height;\n"
|
||||
" %s\t bytes_per_pixel; /* 2:RGB16, 3:RGB, 4:RGBA */ \n",
|
||||
s_uint, s_uint, s_uint))
|
||||
goto fail;
|
||||
|
||||
if (config_save_comment)
|
||||
{
|
||||
if (! print (output, error, " %s\t*comment;\n", s_char))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (! print (output, error,
|
||||
" %s\t %spixel_data[",
|
||||
s_uint_8,
|
||||
config_use_rle ? "rle_" : ""))
|
||||
goto fail;
|
||||
|
||||
if (config_use_rle)
|
||||
{
|
||||
if (! print (output, error,
|
||||
"%u + 1];\n",
|
||||
(guint) (img_buffer_end - img_buffer)))
|
||||
goto fail;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (! print (output, error,
|
||||
"%u * %u * %u + 1];\n",
|
||||
width,
|
||||
height,
|
||||
bpp))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (! print (output, error, "} %s = {\n", config_prefixed_name))
|
||||
goto fail;
|
||||
|
||||
if (! print (output, error,
|
||||
" %u, %u, %u,\n",
|
||||
width,
|
||||
height,
|
||||
bpp))
|
||||
goto fail;
|
||||
}
|
||||
else /* use macros */
|
||||
{
|
||||
if (! print (output, error,
|
||||
"#define %s_WIDTH (%u)\n"
|
||||
"#define %s_HEIGHT (%u)\n"
|
||||
"#define %s_BYTES_PER_PIXEL (%u) /* 2:RGB16, 3:RGB, 4:RGBA */\n",
|
||||
macro_name, width,
|
||||
macro_name, height,
|
||||
macro_name, bpp))
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (config_save_comment && ! config_comment)
|
||||
{
|
||||
if (! config_use_macros)
|
||||
{
|
||||
if (! print (output, error, " %s,\n", s_null))
|
||||
goto fail;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (! print (output, error,
|
||||
"#define %s_COMMENT (%s)\n",
|
||||
macro_name, s_null))
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
else if (config_save_comment)
|
||||
{
|
||||
gchar *p = config_comment - 1;
|
||||
|
||||
if (config_use_macros)
|
||||
{
|
||||
if (! print (output, error, "#define %s_COMMENT \\\n", macro_name))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (! print (output, error, " \""))
|
||||
goto fail;
|
||||
|
||||
while (*(++p))
|
||||
{
|
||||
gboolean success = FALSE;
|
||||
|
||||
if (*p == '\\')
|
||||
success = print (output, error, "\\\\");
|
||||
else if (*p == '"')
|
||||
success = print (output, error, "\\\"");
|
||||
else if (*p == '\n' && p[1])
|
||||
success = print (output, error,
|
||||
"\\n\"%s\n \"",
|
||||
config_use_macros ? " \\" : "");
|
||||
else if (*p == '\n')
|
||||
success = print (output, error, "\\n");
|
||||
else if (*p == '\r')
|
||||
success = print (output, error, "\\r");
|
||||
else if (*p == '\b')
|
||||
success = print (output, error, "\\b");
|
||||
else if (*p == '\f')
|
||||
success = print (output, error, "\\f");
|
||||
else if (( *p >= 32 && *p <= 47 ) || (*p >= 58 && *p <= 126))
|
||||
success = print (output, error, "%c", *p);
|
||||
else
|
||||
success = print (output, error, "\\%03o", *p);
|
||||
|
||||
if (! success)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (! config_use_macros)
|
||||
{
|
||||
if (! print (output, error, "\",\n"))
|
||||
goto fail;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (! print (output, error, "\"\n"))
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (config_use_macros)
|
||||
{
|
||||
if (! print (output, error,
|
||||
"#define %s_%sPIXEL_DATA ((%s*) %s_%spixel_data)\n",
|
||||
macro_name,
|
||||
config_use_rle ? "RLE_" : "",
|
||||
s_uint_8,
|
||||
macro_name,
|
||||
config_use_rle ? "rle_" : ""))
|
||||
goto fail;
|
||||
|
||||
if (config_use_rle)
|
||||
{
|
||||
if (! save_rle_decoder (output,
|
||||
macro_name,
|
||||
s_uint,
|
||||
s_uint_8,
|
||||
bpp,
|
||||
error))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (! print (output, error,
|
||||
"static const %s %s_%spixel_data[",
|
||||
s_uint_8,
|
||||
macro_name,
|
||||
config_use_rle ? "rle_" : ""))
|
||||
goto fail;
|
||||
|
||||
if (config_use_rle)
|
||||
{
|
||||
if (! print (output, error,
|
||||
"%u + 1] =\n",
|
||||
(guint) (img_buffer_end - img_buffer)))
|
||||
goto fail;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (! print (output, error,
|
||||
"%u * %u * %u + 1] =\n",
|
||||
width,
|
||||
height,
|
||||
bpp))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (! print (output, error, "(\""))
|
||||
goto fail;
|
||||
|
||||
c = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (! print (output, error, " \""))
|
||||
goto fail;
|
||||
|
||||
c = 3;
|
||||
}
|
||||
|
||||
switch (drawable_type)
|
||||
{
|
||||
case PIKA_RGB_IMAGE:
|
||||
case PIKA_RGBA_IMAGE:
|
||||
do
|
||||
{
|
||||
if (! save_uchar (output, &c, *(img_buffer++), config_use_macros,
|
||||
error))
|
||||
goto fail;
|
||||
}
|
||||
while (img_buffer < img_buffer_end);
|
||||
break;
|
||||
|
||||
default:
|
||||
g_warning ("unhandled drawable type (%d)", drawable_type);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (! config_use_macros)
|
||||
{
|
||||
if (! print (output, error, "\",\n};\n\n"))
|
||||
goto fail;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (! print (output, error, "\");\n\n"))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (! g_output_stream_close (output, NULL, error))
|
||||
goto fail;
|
||||
|
||||
g_object_unref (output);
|
||||
g_object_unref (buffer);
|
||||
|
||||
return TRUE;
|
||||
|
||||
fail:
|
||||
|
||||
cancellable = g_cancellable_new ();
|
||||
g_cancellable_cancel (cancellable);
|
||||
g_output_stream_close (output, cancellable, NULL);
|
||||
g_object_unref (cancellable);
|
||||
|
||||
g_object_unref (output);
|
||||
g_object_unref (buffer);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
save_dialog (PikaImage *image,
|
||||
PikaProcedure *procedure,
|
||||
GObject *config)
|
||||
{
|
||||
GtkWidget *dialog;
|
||||
GtkWidget *vbox;
|
||||
gboolean run;
|
||||
|
||||
dialog = pika_save_procedure_dialog_new (PIKA_SAVE_PROCEDURE (procedure),
|
||||
PIKA_PROCEDURE_CONFIG (config),
|
||||
image);
|
||||
|
||||
pika_procedure_dialog_get_scale_entry (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"opacity", 1.0);
|
||||
|
||||
pika_procedure_dialog_set_sensitive (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"save-alpha", TRUE, config, "rgb565",
|
||||
TRUE);
|
||||
|
||||
vbox = pika_procedure_dialog_fill_box (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"csource-box",
|
||||
"prefixed-name", "pika-comment",
|
||||
"save-comment", "glib-types",
|
||||
"use-macros", "use-rle", "save-alpha",
|
||||
"rgb565", "opacity",
|
||||
NULL);
|
||||
gtk_box_set_spacing (GTK_BOX (vbox), 12);
|
||||
gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
|
||||
|
||||
pika_procedure_dialog_fill (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"csource-box",
|
||||
NULL);
|
||||
gtk_widget_show (dialog);
|
||||
|
||||
run = pika_procedure_dialog_run (PIKA_PROCEDURE_DIALOG (dialog));
|
||||
|
||||
gtk_widget_destroy (dialog);
|
||||
|
||||
return run;
|
||||
}
|
||||
213
plug-ins/common/file-desktop-link.c
Normal file
213
plug-ins/common/file-desktop-link.c
Normal file
@ -0,0 +1,213 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Desktop Entry Specification
|
||||
* http://standards.freedesktop.org/desktop-entry-spec/latest/
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <glib/gstdio.h>
|
||||
|
||||
#include <libpika/pika.h>
|
||||
|
||||
#include "libpika/stdplugins-intl.h"
|
||||
|
||||
|
||||
#define LOAD_PROC "file-desktop-link-load"
|
||||
#define PLUG_IN_BINARY "file-desktop-link"
|
||||
#define PLUG_IN_ROLE "pika-file-desktop-link"
|
||||
|
||||
|
||||
typedef struct _Desktop Desktop;
|
||||
typedef struct _DesktopClass DesktopClass;
|
||||
|
||||
struct _Desktop
|
||||
{
|
||||
PikaPlugIn parent_instance;
|
||||
};
|
||||
|
||||
struct _DesktopClass
|
||||
{
|
||||
PikaPlugInClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
#define DESKTOP_TYPE (desktop_get_type ())
|
||||
#define DESKTOP (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), DESKTOP_TYPE, Desktop))
|
||||
|
||||
GType desktop_get_type (void) G_GNUC_CONST;
|
||||
|
||||
static GList * desktop_query_procedures (PikaPlugIn *plug_in);
|
||||
static PikaProcedure * desktop_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name);
|
||||
|
||||
static PikaValueArray * desktop_load (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
GFile *file,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data);
|
||||
|
||||
static PikaImage * load_image (GFile *file,
|
||||
PikaRunMode run_mode,
|
||||
GError **error);
|
||||
|
||||
|
||||
G_DEFINE_TYPE (Desktop, desktop, PIKA_TYPE_PLUG_IN)
|
||||
|
||||
PIKA_MAIN (DESKTOP_TYPE)
|
||||
DEFINE_STD_SET_I18N
|
||||
|
||||
|
||||
static void
|
||||
desktop_class_init (DesktopClass *klass)
|
||||
{
|
||||
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
|
||||
|
||||
plug_in_class->query_procedures = desktop_query_procedures;
|
||||
plug_in_class->create_procedure = desktop_create_procedure;
|
||||
plug_in_class->set_i18n = STD_SET_I18N;
|
||||
}
|
||||
|
||||
static void
|
||||
desktop_init (Desktop *desktop)
|
||||
{
|
||||
}
|
||||
|
||||
static GList *
|
||||
desktop_query_procedures (PikaPlugIn *plug_in)
|
||||
{
|
||||
return g_list_append (NULL, g_strdup (LOAD_PROC));
|
||||
}
|
||||
|
||||
static PikaProcedure *
|
||||
desktop_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name)
|
||||
{
|
||||
PikaProcedure *procedure = NULL;
|
||||
|
||||
if (! strcmp (name, LOAD_PROC))
|
||||
{
|
||||
procedure = pika_load_procedure_new (plug_in, name,
|
||||
PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
desktop_load, NULL, NULL);
|
||||
|
||||
pika_procedure_set_menu_label (procedure, _("Desktop Link"));
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
"Follows a link to an image in a "
|
||||
".desktop file",
|
||||
"Opens a .desktop file and if it is "
|
||||
"a link, it asks PIKA to open the "
|
||||
"file the link points to.",
|
||||
LOAD_PROC);
|
||||
|
||||
pika_procedure_set_attribution (procedure,
|
||||
"Sven Neumann",
|
||||
"Sven Neumann",
|
||||
"2006");
|
||||
|
||||
pika_file_procedure_set_extensions (PIKA_FILE_PROCEDURE (procedure),
|
||||
"desktop");
|
||||
}
|
||||
|
||||
return procedure;
|
||||
}
|
||||
|
||||
static PikaValueArray *
|
||||
desktop_load (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
GFile *file,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data)
|
||||
{
|
||||
PikaValueArray *return_values;
|
||||
PikaImage *image;
|
||||
GError *error = NULL;
|
||||
|
||||
image = load_image (file, run_mode, &error);
|
||||
|
||||
if (! image)
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_EXECUTION_ERROR,
|
||||
error);
|
||||
|
||||
return_values = pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_SUCCESS,
|
||||
NULL);
|
||||
|
||||
PIKA_VALUES_SET_IMAGE (return_values, 1, image);
|
||||
|
||||
return return_values;
|
||||
}
|
||||
|
||||
static PikaImage *
|
||||
load_image (GFile *file,
|
||||
PikaRunMode run_mode,
|
||||
GError **load_error)
|
||||
{
|
||||
GKeyFile *key_file = g_key_file_new ();
|
||||
PikaImage *image = NULL;
|
||||
gchar *filename = NULL;
|
||||
gchar *group = NULL;
|
||||
gchar *value = NULL;
|
||||
GError *error = NULL;
|
||||
|
||||
filename = g_file_get_path (file);
|
||||
|
||||
if (! g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, &error))
|
||||
goto out;
|
||||
|
||||
group = g_key_file_get_start_group (key_file);
|
||||
if (! group || strcmp (group, G_KEY_FILE_DESKTOP_GROUP) != 0)
|
||||
goto out;
|
||||
|
||||
value = g_key_file_get_value (key_file,
|
||||
group, G_KEY_FILE_DESKTOP_KEY_TYPE, &error);
|
||||
if (! value || strcmp (value, G_KEY_FILE_DESKTOP_TYPE_LINK) != 0)
|
||||
goto out;
|
||||
|
||||
g_free (value);
|
||||
|
||||
value = g_key_file_get_value (key_file,
|
||||
group, G_KEY_FILE_DESKTOP_KEY_URL, &error);
|
||||
if (value)
|
||||
image = pika_file_load (run_mode, g_file_new_for_uri (value));
|
||||
|
||||
out:
|
||||
if (error)
|
||||
{
|
||||
g_set_error (load_error, error->domain, error->code,
|
||||
_("Error loading desktop file '%s': %s"),
|
||||
pika_filename_to_utf8 (filename), error->message);
|
||||
g_error_free (error);
|
||||
}
|
||||
|
||||
g_free (value);
|
||||
g_free (group);
|
||||
g_free (filename);
|
||||
g_key_file_free (key_file);
|
||||
|
||||
return image;
|
||||
}
|
||||
1834
plug-ins/common/file-dicom.c
Normal file
1834
plug-ins/common/file-dicom.c
Normal file
File diff suppressed because it is too large
Load Diff
428
plug-ins/common/file-farbfeld.c
Normal file
428
plug-ins/common/file-farbfeld.c
Normal file
@ -0,0 +1,428 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* Farbfeld Image Format plug-in
|
||||
*
|
||||
* Copyright (C) 2023 Alex S.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <glib/gstdio.h>
|
||||
|
||||
#include <libpika/pika.h>
|
||||
#include <libpika/pikaui.h>
|
||||
|
||||
#include "libpika/stdplugins-intl.h"
|
||||
|
||||
|
||||
#define LOAD_PROC "file-farbfeld-load"
|
||||
#define SAVE_PROC "file-farbfeld-save"
|
||||
#define PLUG_IN_BINARY "file-farbfeld"
|
||||
#define PLUG_IN_ROLE "pika-file-farbfeld"
|
||||
|
||||
|
||||
typedef struct _Farbfeld Farbfeld;
|
||||
typedef struct _FarbfeldClass FarbfeldClass;
|
||||
|
||||
struct _Farbfeld
|
||||
{
|
||||
PikaPlugIn parent_instance;
|
||||
};
|
||||
|
||||
struct _FarbfeldClass
|
||||
{
|
||||
PikaPlugInClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
#define FARBFELD_TYPE (farbfeld_get_type ())
|
||||
#define FARBFELD (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FARBFELD_TYPE, Farbfeld))
|
||||
|
||||
GType farbfeld_get_type (void) G_GNUC_CONST;
|
||||
|
||||
|
||||
static GList * farbfeld_query_procedures (PikaPlugIn *plug_in);
|
||||
static PikaProcedure * farbfeld_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name);
|
||||
|
||||
static PikaValueArray * farbfeld_load (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
GFile *file,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data);
|
||||
static PikaValueArray * farbfeld_save (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
GFile *file,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data);
|
||||
|
||||
static PikaImage * load_image (GFile *file,
|
||||
GObject *config,
|
||||
PikaRunMode run_mode,
|
||||
GError **error);
|
||||
static gboolean save_image (GFile *file,
|
||||
PikaImage *image,
|
||||
PikaDrawable *drawable,
|
||||
GError **error);
|
||||
|
||||
|
||||
G_DEFINE_TYPE (Farbfeld, farbfeld, PIKA_TYPE_PLUG_IN)
|
||||
|
||||
PIKA_MAIN (FARBFELD_TYPE)
|
||||
DEFINE_STD_SET_I18N
|
||||
|
||||
|
||||
static void
|
||||
farbfeld_class_init (FarbfeldClass *klass)
|
||||
{
|
||||
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
|
||||
|
||||
plug_in_class->query_procedures = farbfeld_query_procedures;
|
||||
plug_in_class->create_procedure = farbfeld_create_procedure;
|
||||
plug_in_class->set_i18n = STD_SET_I18N;
|
||||
}
|
||||
|
||||
static void
|
||||
farbfeld_init (Farbfeld *farbfeld)
|
||||
{
|
||||
}
|
||||
|
||||
static GList *
|
||||
farbfeld_query_procedures (PikaPlugIn *plug_in)
|
||||
{
|
||||
GList *list = NULL;
|
||||
|
||||
list = g_list_append (list, g_strdup (LOAD_PROC));
|
||||
list = g_list_append (list, g_strdup (SAVE_PROC));
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
static PikaProcedure *
|
||||
farbfeld_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name)
|
||||
{
|
||||
PikaProcedure *procedure = NULL;
|
||||
|
||||
if (! strcmp (name, LOAD_PROC))
|
||||
{
|
||||
procedure = pika_load_procedure_new (plug_in, name,
|
||||
PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
farbfeld_load, NULL, NULL);
|
||||
|
||||
pika_procedure_set_menu_label (procedure,
|
||||
N_("Farbfeld"));
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
_("Load file in the Farbfeld file "
|
||||
"format"),
|
||||
_("Load file in the Farbfeld file "
|
||||
"format"),
|
||||
name);
|
||||
pika_procedure_set_attribution (procedure,
|
||||
"Alex S.",
|
||||
"Alex S.",
|
||||
"2023");
|
||||
|
||||
pika_file_procedure_set_extensions (PIKA_FILE_PROCEDURE (procedure),
|
||||
"ff");
|
||||
pika_file_procedure_set_magics (PIKA_FILE_PROCEDURE (procedure),
|
||||
"0,string,farbfeld");
|
||||
}
|
||||
else if (! strcmp (name, SAVE_PROC))
|
||||
{
|
||||
procedure = pika_save_procedure_new (plug_in, name,
|
||||
PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
farbfeld_save, NULL, NULL);
|
||||
|
||||
pika_procedure_set_image_types (procedure, "*");
|
||||
|
||||
pika_procedure_set_menu_label (procedure, _("Farbfeld"));
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
_("Export image in the Farbfeld file "
|
||||
"format"),
|
||||
_("Export image in the Farbfeld file "
|
||||
"format"),
|
||||
name);
|
||||
pika_procedure_set_attribution (procedure,
|
||||
"Alex S.",
|
||||
"Alex S.",
|
||||
"2023");
|
||||
|
||||
pika_file_procedure_set_extensions (PIKA_FILE_PROCEDURE (procedure),
|
||||
"ff");
|
||||
}
|
||||
|
||||
return procedure;
|
||||
}
|
||||
|
||||
static PikaValueArray *
|
||||
farbfeld_load (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
GFile *file,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data)
|
||||
{
|
||||
PikaProcedureConfig *config;
|
||||
PikaValueArray *return_vals;
|
||||
PikaImage *image;
|
||||
GError *error = NULL;
|
||||
|
||||
gegl_init (NULL, NULL);
|
||||
|
||||
config = pika_procedure_create_config (procedure);
|
||||
pika_procedure_config_begin_run (config, NULL, run_mode, args);
|
||||
|
||||
image = load_image (file, G_OBJECT (config), run_mode, &error);
|
||||
|
||||
if (! image)
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_EXECUTION_ERROR,
|
||||
error);
|
||||
|
||||
pika_procedure_config_end_run (config, PIKA_PDB_SUCCESS);
|
||||
g_object_unref (config);
|
||||
|
||||
return_vals = pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_SUCCESS,
|
||||
NULL);
|
||||
|
||||
PIKA_VALUES_SET_IMAGE (return_vals, 1, image);
|
||||
|
||||
return return_vals;
|
||||
}
|
||||
|
||||
static PikaValueArray *
|
||||
farbfeld_save (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
GFile *file,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data)
|
||||
{
|
||||
PikaPDBStatusType status = PIKA_PDB_SUCCESS;
|
||||
PikaExportReturn export = PIKA_EXPORT_CANCEL;
|
||||
GError *error = NULL;
|
||||
|
||||
gegl_init (NULL, NULL);
|
||||
|
||||
switch (run_mode)
|
||||
{
|
||||
case PIKA_RUN_INTERACTIVE:
|
||||
case PIKA_RUN_WITH_LAST_VALS:
|
||||
pika_ui_init (PLUG_IN_BINARY);
|
||||
|
||||
export = pika_export_image (&image, &n_drawables, &drawables, "farbfeld",
|
||||
PIKA_EXPORT_CAN_HANDLE_RGB |
|
||||
PIKA_EXPORT_CAN_HANDLE_GRAY |
|
||||
PIKA_EXPORT_CAN_HANDLE_INDEXED |
|
||||
PIKA_EXPORT_CAN_HANDLE_ALPHA);
|
||||
|
||||
if (export == PIKA_EXPORT_CANCEL)
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_CANCEL,
|
||||
NULL);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (n_drawables != 1)
|
||||
{
|
||||
g_set_error (&error, G_FILE_ERROR, 0,
|
||||
_("Farbfeld format does not support multiple layers."));
|
||||
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_CALLING_ERROR,
|
||||
error);
|
||||
}
|
||||
|
||||
if (! save_image (file, image, drawables[0], &error))
|
||||
status = PIKA_PDB_EXECUTION_ERROR;
|
||||
|
||||
if (export == PIKA_EXPORT_EXPORT)
|
||||
{
|
||||
pika_image_delete (image);
|
||||
g_free (drawables);
|
||||
}
|
||||
|
||||
return pika_procedure_new_return_values (procedure, status, error);
|
||||
}
|
||||
|
||||
static PikaImage *
|
||||
load_image (GFile *file,
|
||||
GObject *config,
|
||||
PikaRunMode run_mode,
|
||||
GError **error)
|
||||
{
|
||||
PikaImage *image = NULL;
|
||||
PikaLayer *layer;
|
||||
GeglBuffer *buffer;
|
||||
guint16 *pixels;
|
||||
guchar magic_number[8];
|
||||
guint32 width;
|
||||
guint32 height;
|
||||
guint32 row_size;
|
||||
const Babl *format = babl_format ("R'G'B'A u16");
|
||||
FILE *fp;
|
||||
|
||||
fp = g_fopen (g_file_peek_path (file), "rb");
|
||||
|
||||
if (! fp)
|
||||
{
|
||||
g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
|
||||
_("Could not open '%s' for reading: %s"),
|
||||
pika_file_get_utf8_name (file), g_strerror (errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Load the header */
|
||||
if (! fread (magic_number, 8, 1, fp) ||
|
||||
! fread (&width, sizeof (guint32), 1, fp) ||
|
||||
! fread (&height, sizeof (guint32), 1, fp))
|
||||
{
|
||||
g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
|
||||
_("Failed to read Farbfeld header"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Header information is stored in Big-Endian format */
|
||||
width = GUINT32_FROM_BE (width);
|
||||
height = GUINT32_FROM_BE (height);
|
||||
row_size = width * sizeof (guint16) * 4;
|
||||
|
||||
image = pika_image_new_with_precision (width, height, PIKA_RGB,
|
||||
PIKA_PRECISION_U16_NON_LINEAR);
|
||||
|
||||
layer = pika_layer_new (image, _("Background"), width, height,
|
||||
PIKA_RGBA_IMAGE, 100,
|
||||
pika_image_get_default_new_layer_mode (image));
|
||||
pika_image_insert_layer (image, layer, NULL, 0);
|
||||
|
||||
buffer = pika_drawable_get_buffer (PIKA_DRAWABLE (layer));
|
||||
|
||||
for (gint i = 0; i < height; i++)
|
||||
{
|
||||
pixels = g_malloc (row_size);
|
||||
|
||||
if (! fread (pixels, row_size, 1, fp))
|
||||
{
|
||||
g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
|
||||
_("Premature end of Farbfeld pixel data"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Pixels are also stored in Big-Endian format */
|
||||
for (gint j = 0; j < (width * 4); j++)
|
||||
pixels[j] = GUINT16_FROM_BE (pixels[j]);
|
||||
|
||||
gegl_buffer_set (buffer,
|
||||
GEGL_RECTANGLE (0, i, width, 1), 0,
|
||||
format, pixels, GEGL_AUTO_ROWSTRIDE);
|
||||
|
||||
g_free (pixels);
|
||||
}
|
||||
|
||||
fclose (fp);
|
||||
g_object_unref (buffer);
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
save_image (GFile *file,
|
||||
PikaImage *image,
|
||||
PikaDrawable *drawable,
|
||||
GError **error)
|
||||
{
|
||||
FILE *fp;
|
||||
GeglBuffer *buffer;
|
||||
guint16 *pixels;
|
||||
const Babl *format = babl_format ("R'G'B'A u16");
|
||||
gchar *magic_number;
|
||||
guint32 image_width;
|
||||
guint32 image_height;
|
||||
guint32 export_width;
|
||||
guint32 export_height;
|
||||
|
||||
pika_progress_init_printf (_("Exporting '%s'"),
|
||||
pika_file_get_utf8_name (file));
|
||||
|
||||
fp = g_fopen (g_file_peek_path (file), "wb");
|
||||
|
||||
if (! fp)
|
||||
{
|
||||
g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
|
||||
_("Could not open '%s' for writing: %s"),
|
||||
pika_file_get_utf8_name (file), g_strerror (errno));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
buffer = pika_drawable_get_buffer (drawable);
|
||||
|
||||
image_width = gegl_buffer_get_width (buffer);
|
||||
image_height = gegl_buffer_get_height (buffer);
|
||||
/* Farbfeld values are Big-Endian */
|
||||
export_width = GUINT32_TO_BE (image_width);
|
||||
export_height = GUINT32_TO_BE (image_height);
|
||||
|
||||
/* Write header */
|
||||
magic_number = "farbfeld";
|
||||
for (gint i = 0; i < 8; i++)
|
||||
fputc (magic_number[i], fp);
|
||||
fwrite ((gchar *) &export_width, 1, 4, fp);
|
||||
fwrite ((gchar *) &export_height, 1, 4, fp);
|
||||
|
||||
/* Write pixel data */
|
||||
for (gint i = 0; i < image_height; i++)
|
||||
{
|
||||
pixels = g_malloc (image_width * sizeof (guint16) * 4);
|
||||
|
||||
gegl_buffer_get (buffer, GEGL_RECTANGLE (0, i, image_width, 1),
|
||||
1.0, format, pixels,
|
||||
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
|
||||
|
||||
for (gint j = 0; j < (image_width * 4); j++)
|
||||
{
|
||||
pixels[j] = GUINT16_TO_BE (pixels[j]);
|
||||
fwrite ((gchar *) &pixels[j], 1, 2, fp);
|
||||
}
|
||||
|
||||
g_free (pixels);
|
||||
pika_progress_update (i / (gdouble) image_height);
|
||||
}
|
||||
|
||||
pika_progress_update (1.0);
|
||||
|
||||
fclose (fp);
|
||||
|
||||
if (buffer)
|
||||
g_object_unref (buffer);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
335
plug-ins/common/file-gbr.c
Normal file
335
plug-ins/common/file-gbr.c
Normal file
@ -0,0 +1,335 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* gbr plug-in version 1.00
|
||||
* Loads/exports version 2 PIKA .gbr files, by Tim Newsome <drz@frody.bloke.com>
|
||||
* Some bits stolen from the .99.7 source tree.
|
||||
*
|
||||
* Added in GBR version 1 support after learning that there wasn't a
|
||||
* tool to read them.
|
||||
* July 6, 1998 by Seth Burgess <sjburges@gimp.org>
|
||||
*
|
||||
* Dec 17, 2000
|
||||
* Load and save PIKA brushes in GRAY or RGBA. jtl + neo
|
||||
*
|
||||
*
|
||||
* TODO: Give some better error reporting on not opening files/bad headers
|
||||
* etc.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <libpika/pika.h>
|
||||
#include <libpika/pikaui.h>
|
||||
|
||||
#include "libpika/stdplugins-intl.h"
|
||||
|
||||
|
||||
#define SAVE_PROC "file-gbr-save"
|
||||
#define PLUG_IN_BINARY "file-gbr"
|
||||
|
||||
|
||||
typedef struct _Gbr Gbr;
|
||||
typedef struct _GbrClass GbrClass;
|
||||
|
||||
struct _Gbr
|
||||
{
|
||||
PikaPlugIn parent_instance;
|
||||
};
|
||||
|
||||
struct _GbrClass
|
||||
{
|
||||
PikaPlugInClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
#define GBR_TYPE (gbr_get_type ())
|
||||
#define GBR (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GBR_TYPE, Gbr))
|
||||
|
||||
GType gbr_get_type (void) G_GNUC_CONST;
|
||||
|
||||
static GList * gbr_query_procedures (PikaPlugIn *plug_in);
|
||||
static PikaProcedure * gbr_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name);
|
||||
|
||||
static PikaValueArray * gbr_save (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
GFile *file,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data);
|
||||
|
||||
static gboolean save_dialog (PikaProcedure *procedure,
|
||||
GObject *config,
|
||||
PikaImage *image);
|
||||
|
||||
|
||||
G_DEFINE_TYPE (Gbr, gbr, PIKA_TYPE_PLUG_IN)
|
||||
|
||||
PIKA_MAIN (GBR_TYPE)
|
||||
DEFINE_STD_SET_I18N
|
||||
|
||||
|
||||
static void
|
||||
gbr_class_init (GbrClass *klass)
|
||||
{
|
||||
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
|
||||
|
||||
plug_in_class->query_procedures = gbr_query_procedures;
|
||||
plug_in_class->create_procedure = gbr_create_procedure;
|
||||
plug_in_class->set_i18n = STD_SET_I18N;
|
||||
}
|
||||
|
||||
static void
|
||||
gbr_init (Gbr *gbr)
|
||||
{
|
||||
}
|
||||
|
||||
static GList *
|
||||
gbr_query_procedures (PikaPlugIn *plug_in)
|
||||
{
|
||||
return g_list_append (NULL, g_strdup (SAVE_PROC));
|
||||
}
|
||||
|
||||
static PikaProcedure *
|
||||
gbr_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name)
|
||||
{
|
||||
PikaProcedure *procedure = NULL;
|
||||
|
||||
if (! strcmp (name, SAVE_PROC))
|
||||
{
|
||||
procedure = pika_save_procedure_new (plug_in, name,
|
||||
PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
gbr_save, NULL, NULL);
|
||||
|
||||
pika_procedure_set_image_types (procedure, "*");
|
||||
|
||||
pika_procedure_set_menu_label (procedure, _("PIKA brush"));
|
||||
pika_file_procedure_set_format_name (PIKA_FILE_PROCEDURE (procedure),
|
||||
_("Brush"));
|
||||
pika_procedure_set_icon_name (procedure, PIKA_ICON_BRUSH);
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
_("Exports files in the PIKA brush "
|
||||
"file format"),
|
||||
_("Exports files in the PIKA brush "
|
||||
"file format"),
|
||||
SAVE_PROC);
|
||||
pika_procedure_set_attribution (procedure,
|
||||
"Tim Newsome, Jens Lautenbacher, "
|
||||
"Sven Neumann",
|
||||
"Tim Newsome, Jens Lautenbacher, "
|
||||
"Sven Neumann",
|
||||
"1997-2000");
|
||||
|
||||
pika_file_procedure_set_mime_types (PIKA_FILE_PROCEDURE (procedure),
|
||||
"image/x-pika-gbr");
|
||||
pika_file_procedure_set_extensions (PIKA_FILE_PROCEDURE (procedure),
|
||||
"gbr");
|
||||
pika_file_procedure_set_handles_remote (PIKA_FILE_PROCEDURE (procedure),
|
||||
TRUE);
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "spacing",
|
||||
_("Sp_acing"),
|
||||
_("Spacing of the brush"),
|
||||
1, 1000, 10,
|
||||
PIKA_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_STRING (procedure, "description",
|
||||
_("_Description"),
|
||||
_("Short description of the brush"),
|
||||
_("PIKA Brush"),
|
||||
PIKA_PARAM_READWRITE);
|
||||
}
|
||||
|
||||
return procedure;
|
||||
}
|
||||
|
||||
static PikaValueArray *
|
||||
gbr_save (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
GFile *file,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data)
|
||||
{
|
||||
PikaProcedureConfig *config;
|
||||
PikaPDBStatusType status = PIKA_PDB_SUCCESS;
|
||||
PikaExportReturn export = PIKA_EXPORT_CANCEL;
|
||||
gchar *description;
|
||||
GError *error = NULL;
|
||||
|
||||
config = pika_procedure_create_config (procedure);
|
||||
pika_procedure_config_begin_run (config, image, run_mode, args);
|
||||
|
||||
g_object_get (config,
|
||||
"description", &description,
|
||||
NULL);
|
||||
|
||||
if (! description || ! strlen (description))
|
||||
{
|
||||
gchar *name = g_path_get_basename (pika_file_get_utf8_name (file));
|
||||
|
||||
if (g_str_has_suffix (name, ".gbr"))
|
||||
name[strlen (name) - 4] = '\0';
|
||||
|
||||
if (strlen (name))
|
||||
g_object_set (config,
|
||||
"description", name,
|
||||
NULL);
|
||||
|
||||
g_free (name);
|
||||
}
|
||||
|
||||
g_free (description);
|
||||
|
||||
switch (run_mode)
|
||||
{
|
||||
case PIKA_RUN_INTERACTIVE:
|
||||
case PIKA_RUN_WITH_LAST_VALS:
|
||||
pika_ui_init (PLUG_IN_BINARY);
|
||||
|
||||
export = pika_export_image (&image, &n_drawables, &drawables, "GBR",
|
||||
PIKA_EXPORT_CAN_HANDLE_GRAY |
|
||||
PIKA_EXPORT_CAN_HANDLE_RGB |
|
||||
PIKA_EXPORT_CAN_HANDLE_INDEXED |
|
||||
PIKA_EXPORT_CAN_HANDLE_ALPHA);
|
||||
|
||||
if (export == PIKA_EXPORT_CANCEL)
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_CANCEL,
|
||||
NULL);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (n_drawables != 1)
|
||||
{
|
||||
g_set_error (&error, G_FILE_ERROR, 0,
|
||||
_("GBR format does not support multiple layers."));
|
||||
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_CALLING_ERROR,
|
||||
error);
|
||||
}
|
||||
|
||||
if (run_mode == PIKA_RUN_INTERACTIVE)
|
||||
{
|
||||
if (! save_dialog (procedure, G_OBJECT (config), image))
|
||||
status = PIKA_PDB_CANCEL;
|
||||
}
|
||||
|
||||
if (status == PIKA_PDB_SUCCESS)
|
||||
{
|
||||
PikaValueArray *save_retvals;
|
||||
gint spacing;
|
||||
|
||||
g_object_get (config,
|
||||
"description", &description,
|
||||
"spacing", &spacing,
|
||||
NULL);
|
||||
|
||||
save_retvals =
|
||||
pika_pdb_run_procedure (pika_get_pdb (),
|
||||
"file-gbr-save-internal",
|
||||
PIKA_TYPE_RUN_MODE, PIKA_RUN_NONINTERACTIVE,
|
||||
PIKA_TYPE_IMAGE, image,
|
||||
PIKA_TYPE_DRAWABLE, drawables[0],
|
||||
G_TYPE_FILE, file,
|
||||
G_TYPE_INT, spacing,
|
||||
G_TYPE_STRING, description,
|
||||
G_TYPE_NONE);
|
||||
|
||||
g_free (description);
|
||||
|
||||
if (PIKA_VALUES_GET_ENUM (save_retvals, 0) != PIKA_PDB_SUCCESS)
|
||||
{
|
||||
g_set_error (&error, 0, 0,
|
||||
"Running procedure 'file-gbr-save-internal' "
|
||||
"failed: %s",
|
||||
pika_pdb_get_last_error (pika_get_pdb ()));
|
||||
|
||||
status = PIKA_PDB_EXECUTION_ERROR;
|
||||
}
|
||||
|
||||
pika_value_array_unref (save_retvals);
|
||||
}
|
||||
|
||||
pika_procedure_config_end_run (config, status);
|
||||
g_object_unref (config);
|
||||
|
||||
if (export == PIKA_EXPORT_EXPORT)
|
||||
{
|
||||
pika_image_delete (image);
|
||||
g_free (drawables);
|
||||
}
|
||||
|
||||
return pika_procedure_new_return_values (procedure, status, error);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
save_dialog (PikaProcedure *procedure,
|
||||
GObject *config,
|
||||
PikaImage *image)
|
||||
{
|
||||
GtkWidget *dialog;
|
||||
GtkWidget *vbox;
|
||||
GtkWidget *entry;
|
||||
GtkWidget *real_entry;
|
||||
gboolean run;
|
||||
|
||||
dialog = pika_save_procedure_dialog_new (PIKA_SAVE_PROCEDURE (procedure),
|
||||
PIKA_PROCEDURE_CONFIG (config),
|
||||
image);
|
||||
|
||||
entry = pika_procedure_dialog_get_widget (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"description", PIKA_TYPE_LABEL_ENTRY);
|
||||
real_entry = pika_label_entry_get_entry (PIKA_LABEL_ENTRY (entry));
|
||||
gtk_entry_set_max_length (GTK_ENTRY (real_entry), 256);
|
||||
gtk_entry_set_width_chars (GTK_ENTRY (real_entry), 20);
|
||||
gtk_entry_set_activates_default (GTK_ENTRY (real_entry), TRUE);
|
||||
|
||||
pika_procedure_dialog_get_scale_entry (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"spacing", 1.0);
|
||||
|
||||
vbox = pika_procedure_dialog_fill_box (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"gbr-vbox", "description", "spacing",
|
||||
NULL);
|
||||
gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
|
||||
|
||||
pika_procedure_dialog_fill (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"gbr-vbox", NULL);
|
||||
gtk_widget_show (dialog);
|
||||
|
||||
run = pika_procedure_dialog_run (PIKA_PROCEDURE_DIALOG (dialog));
|
||||
|
||||
gtk_widget_destroy (dialog);
|
||||
|
||||
return run;
|
||||
}
|
||||
523
plug-ins/common/file-gegl.c
Normal file
523
plug-ins/common/file-gegl.c
Normal file
@ -0,0 +1,523 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* file-gegl.c -- GEGL based file format plug-in
|
||||
* Copyright (C) 2012 Simon Budig <simon@gimp.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <glib/gstdio.h>
|
||||
|
||||
#include <libpika/pika.h>
|
||||
#include <libpika/pikaui.h>
|
||||
|
||||
#include "libpika/stdplugins-intl.h"
|
||||
|
||||
|
||||
#define PLUG_IN_BINARY "file-gegl"
|
||||
|
||||
|
||||
typedef struct _FileFormat FileFormat;
|
||||
|
||||
struct _FileFormat
|
||||
{
|
||||
const gchar *file_type;
|
||||
const gchar *mime_type;
|
||||
const gchar *extensions;
|
||||
const gchar *magic;
|
||||
|
||||
const gchar *load_proc;
|
||||
const gchar *load_blurb;
|
||||
const gchar *load_help;
|
||||
const gchar *load_op;
|
||||
|
||||
const gchar *save_proc;
|
||||
const gchar *save_blurb;
|
||||
const gchar *save_help;
|
||||
const gchar *save_op;
|
||||
};
|
||||
|
||||
|
||||
typedef struct _Goat Goat;
|
||||
typedef struct _GoatClass GoatClass;
|
||||
|
||||
struct _Goat
|
||||
{
|
||||
PikaPlugIn parent_instance;
|
||||
};
|
||||
|
||||
struct _GoatClass
|
||||
{
|
||||
PikaPlugInClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
#define GOAT_TYPE (goat_get_type ())
|
||||
#define GOAT (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GOAT_TYPE, Goat))
|
||||
|
||||
GType goat_get_type (void) G_GNUC_CONST;
|
||||
|
||||
static GList * goat_query_procedures (PikaPlugIn *plug_in);
|
||||
static PikaProcedure * goat_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name);
|
||||
|
||||
static PikaValueArray * goat_load (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
GFile *file,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data);
|
||||
static PikaValueArray * goat_save (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
GFile *file,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data);
|
||||
|
||||
static PikaImage * load_image (GFile *file,
|
||||
const gchar *gegl_op,
|
||||
GError **error);
|
||||
static gboolean save_image (GFile *file,
|
||||
const gchar *gegl_op,
|
||||
PikaImage *image,
|
||||
PikaDrawable *drawable,
|
||||
GError **error);
|
||||
|
||||
|
||||
G_DEFINE_TYPE (Goat, goat, PIKA_TYPE_PLUG_IN)
|
||||
|
||||
PIKA_MAIN (GOAT_TYPE)
|
||||
DEFINE_STD_SET_I18N
|
||||
|
||||
|
||||
static const FileFormat file_formats[] =
|
||||
{
|
||||
{
|
||||
N_("Radiance RGBE"),
|
||||
"image/vnd.radiance",
|
||||
"hdr",
|
||||
"0,string,#?",
|
||||
|
||||
"file-load-rgbe",
|
||||
"Load files in the RGBE file format",
|
||||
"This procedure loads images in the RGBE format, using gegl:rgbe-load",
|
||||
"gegl:rgbe-load",
|
||||
|
||||
"file-save-rgbe",
|
||||
"Saves files in the RGBE file format",
|
||||
"This procedure exports images in the RGBE format, using gegl:rgbe-save",
|
||||
"gegl:rgbe-save",
|
||||
},
|
||||
{
|
||||
N_("OpenEXR image"),
|
||||
"image/x-exr",
|
||||
"exr",
|
||||
"0,lelong,20000630",
|
||||
|
||||
/* no EXR loading (implemented in native PIKA plug-in) */
|
||||
NULL, NULL, NULL, NULL,
|
||||
|
||||
"file-exr-save",
|
||||
"Saves files in the OpenEXR file format",
|
||||
"This procedure saves images in the OpenEXR format, using gegl:exr-save",
|
||||
"gegl:exr-save"
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static void
|
||||
goat_class_init (GoatClass *klass)
|
||||
{
|
||||
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
|
||||
|
||||
plug_in_class->query_procedures = goat_query_procedures;
|
||||
plug_in_class->create_procedure = goat_create_procedure;
|
||||
plug_in_class->set_i18n = STD_SET_I18N;
|
||||
}
|
||||
|
||||
static void
|
||||
goat_init (Goat *goat)
|
||||
{
|
||||
}
|
||||
|
||||
static GList *
|
||||
goat_query_procedures (PikaPlugIn *plug_in)
|
||||
{
|
||||
GList *list = NULL;
|
||||
gint i;
|
||||
|
||||
for (i = 0; i < G_N_ELEMENTS (file_formats); i++)
|
||||
{
|
||||
const FileFormat *format = &file_formats[i];
|
||||
|
||||
if (format->load_proc)
|
||||
list = g_list_append (list, g_strdup (format->load_proc));
|
||||
|
||||
if (format->save_proc)
|
||||
list = g_list_append (list, g_strdup (format->save_proc));
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
static PikaProcedure *
|
||||
goat_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name)
|
||||
{
|
||||
PikaProcedure *procedure = NULL;
|
||||
gint i;
|
||||
|
||||
for (i = 0; i < G_N_ELEMENTS (file_formats); i++)
|
||||
{
|
||||
const FileFormat *format = &file_formats[i];
|
||||
|
||||
if (! g_strcmp0 (name, format->load_proc))
|
||||
{
|
||||
procedure = pika_load_procedure_new (plug_in, name,
|
||||
PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
goat_load,
|
||||
(gpointer) format, NULL);
|
||||
|
||||
pika_procedure_set_menu_label (procedure, format->file_type);
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
format->load_blurb,
|
||||
format->load_help,
|
||||
name);
|
||||
|
||||
pika_file_procedure_set_mime_types (PIKA_FILE_PROCEDURE (procedure),
|
||||
format->mime_type);
|
||||
pika_file_procedure_set_extensions (PIKA_FILE_PROCEDURE (procedure),
|
||||
format->extensions);
|
||||
pika_file_procedure_set_magics (PIKA_FILE_PROCEDURE (procedure),
|
||||
format->magic);
|
||||
}
|
||||
else if (! g_strcmp0 (name, format->save_proc))
|
||||
{
|
||||
procedure = pika_save_procedure_new (plug_in, name,
|
||||
PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
goat_save,
|
||||
(gpointer) format, NULL);
|
||||
|
||||
pika_procedure_set_image_types (procedure, "*");
|
||||
|
||||
pika_procedure_set_menu_label (procedure, format->file_type);
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
format->save_blurb,
|
||||
format->save_help,
|
||||
name);
|
||||
|
||||
pika_file_procedure_set_mime_types (PIKA_FILE_PROCEDURE (procedure),
|
||||
format->mime_type);
|
||||
pika_file_procedure_set_extensions (PIKA_FILE_PROCEDURE (procedure),
|
||||
format->extensions);
|
||||
}
|
||||
}
|
||||
|
||||
return procedure;
|
||||
}
|
||||
|
||||
static PikaValueArray *
|
||||
goat_load (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
GFile *file,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data)
|
||||
{
|
||||
const FileFormat *format = run_data;
|
||||
PikaValueArray *return_vals;
|
||||
PikaImage *image;
|
||||
GError *error = NULL;
|
||||
|
||||
gegl_init (NULL, NULL);
|
||||
|
||||
image = load_image (file, format->load_op, &error);
|
||||
|
||||
if (! image)
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_EXECUTION_ERROR,
|
||||
error);
|
||||
|
||||
return_vals = pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_SUCCESS,
|
||||
NULL);
|
||||
|
||||
PIKA_VALUES_SET_IMAGE (return_vals, 1, image);
|
||||
|
||||
return return_vals;
|
||||
}
|
||||
|
||||
static PikaValueArray *
|
||||
goat_save (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
GFile *file,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data)
|
||||
{
|
||||
const FileFormat *format = run_data;
|
||||
PikaPDBStatusType status = PIKA_PDB_SUCCESS;
|
||||
PikaExportReturn export = PIKA_EXPORT_CANCEL;
|
||||
GError *error = NULL;
|
||||
|
||||
gegl_init (NULL, NULL);
|
||||
|
||||
switch (run_mode)
|
||||
{
|
||||
case PIKA_RUN_INTERACTIVE:
|
||||
case PIKA_RUN_WITH_LAST_VALS:
|
||||
pika_ui_init (PLUG_IN_BINARY);
|
||||
|
||||
export = pika_export_image (&image, &n_drawables, &drawables, "GEGL",
|
||||
PIKA_EXPORT_CAN_HANDLE_RGB |
|
||||
PIKA_EXPORT_CAN_HANDLE_GRAY |
|
||||
PIKA_EXPORT_CAN_HANDLE_INDEXED |
|
||||
PIKA_EXPORT_CAN_HANDLE_ALPHA);
|
||||
|
||||
if (export == PIKA_EXPORT_CANCEL)
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_CANCEL,
|
||||
NULL);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (n_drawables != 1)
|
||||
{
|
||||
g_set_error (&error, G_FILE_ERROR, 0,
|
||||
_("GEGL export plug-in does not support multiple layers."));
|
||||
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_CALLING_ERROR,
|
||||
error);
|
||||
}
|
||||
|
||||
if (! save_image (file, format->save_op, image, drawables[0],
|
||||
&error))
|
||||
{
|
||||
status = PIKA_PDB_EXECUTION_ERROR;
|
||||
}
|
||||
|
||||
if (export == PIKA_EXPORT_EXPORT)
|
||||
{
|
||||
pika_image_delete (image);
|
||||
g_free (drawables);
|
||||
}
|
||||
|
||||
return pika_procedure_new_return_values (procedure, status, error);
|
||||
}
|
||||
|
||||
static PikaImage *
|
||||
load_image (GFile *file,
|
||||
const gchar *gegl_op,
|
||||
GError **error)
|
||||
{
|
||||
PikaImage *image;
|
||||
PikaLayer *layer;
|
||||
PikaImageType image_type;
|
||||
PikaImageBaseType base_type;
|
||||
PikaPrecision precision;
|
||||
gint width;
|
||||
gint height;
|
||||
GeglNode *graph;
|
||||
GeglNode *sink;
|
||||
GeglNode *source;
|
||||
GeglBuffer *src_buf = NULL;
|
||||
GeglBuffer *dest_buf = NULL;
|
||||
const Babl *format;
|
||||
|
||||
pika_progress_init_printf (_("Opening '%s'"),
|
||||
pika_file_get_utf8_name (file));
|
||||
|
||||
graph = gegl_node_new ();
|
||||
|
||||
source = gegl_node_new_child (graph,
|
||||
"operation", gegl_op,
|
||||
"path", g_file_peek_path (file),
|
||||
NULL);
|
||||
sink = gegl_node_new_child (graph,
|
||||
"operation", "gegl:buffer-sink",
|
||||
"buffer", &src_buf,
|
||||
NULL);
|
||||
|
||||
gegl_node_link (source, sink);
|
||||
|
||||
gegl_node_process (sink);
|
||||
|
||||
g_object_unref (graph);
|
||||
|
||||
if (! src_buf)
|
||||
{
|
||||
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
||||
_("Could not open '%s'"),
|
||||
pika_file_get_utf8_name (file));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pika_progress_update (0.33);
|
||||
|
||||
width = gegl_buffer_get_width (src_buf);
|
||||
height = gegl_buffer_get_height (src_buf);
|
||||
format = gegl_buffer_get_format (src_buf);
|
||||
|
||||
if (babl_format_is_palette (format))
|
||||
{
|
||||
base_type = PIKA_INDEXED;
|
||||
|
||||
if (babl_format_has_alpha (format))
|
||||
image_type = PIKA_INDEXEDA_IMAGE;
|
||||
else
|
||||
image_type = PIKA_INDEXED_IMAGE;
|
||||
|
||||
precision = PIKA_PRECISION_U8_NON_LINEAR;
|
||||
}
|
||||
else
|
||||
{
|
||||
const Babl *model = babl_format_get_model (format);
|
||||
const Babl *type = babl_format_get_type (format, 0);
|
||||
gboolean linear = TRUE;
|
||||
|
||||
if (model == babl_model ("Y") ||
|
||||
model == babl_model ("Y'") ||
|
||||
model == babl_model ("YA") ||
|
||||
model == babl_model ("Y'A"))
|
||||
{
|
||||
base_type = PIKA_GRAY;
|
||||
|
||||
if (babl_format_has_alpha (format))
|
||||
image_type = PIKA_GRAYA_IMAGE;
|
||||
else
|
||||
image_type = PIKA_GRAY_IMAGE;
|
||||
|
||||
if (model == babl_model ("Y'") ||
|
||||
model == babl_model ("Y'A"))
|
||||
linear = FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
base_type = PIKA_RGB;
|
||||
|
||||
if (babl_format_has_alpha (format))
|
||||
image_type = PIKA_RGBA_IMAGE;
|
||||
else
|
||||
image_type = PIKA_RGB_IMAGE;
|
||||
|
||||
if (model == babl_model ("R'G'B'") ||
|
||||
model == babl_model ("R'G'B'A"))
|
||||
linear = FALSE;
|
||||
}
|
||||
|
||||
if (linear)
|
||||
{
|
||||
if (type == babl_type ("u8"))
|
||||
precision = PIKA_PRECISION_U8_LINEAR;
|
||||
else if (type == babl_type ("u16"))
|
||||
precision = PIKA_PRECISION_U16_LINEAR;
|
||||
else if (type == babl_type ("u32"))
|
||||
precision = PIKA_PRECISION_U32_LINEAR;
|
||||
else if (type == babl_type ("half"))
|
||||
precision = PIKA_PRECISION_HALF_LINEAR;
|
||||
else
|
||||
precision = PIKA_PRECISION_FLOAT_LINEAR;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (type == babl_type ("u8"))
|
||||
precision = PIKA_PRECISION_U8_NON_LINEAR;
|
||||
else if (type == babl_type ("u16"))
|
||||
precision = PIKA_PRECISION_U16_NON_LINEAR;
|
||||
else if (type == babl_type ("u32"))
|
||||
precision = PIKA_PRECISION_U32_NON_LINEAR;
|
||||
else if (type == babl_type ("half"))
|
||||
precision = PIKA_PRECISION_HALF_NON_LINEAR;
|
||||
else
|
||||
precision = PIKA_PRECISION_FLOAT_NON_LINEAR;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
image = pika_image_new_with_precision (width, height,
|
||||
base_type, precision);
|
||||
layer = pika_layer_new (image,
|
||||
_("Background"),
|
||||
width, height,
|
||||
image_type,
|
||||
100,
|
||||
pika_image_get_default_new_layer_mode (image));
|
||||
pika_image_insert_layer (image, layer, NULL, 0);
|
||||
|
||||
dest_buf = pika_drawable_get_buffer (PIKA_DRAWABLE (layer));
|
||||
|
||||
pika_progress_update (0.66);
|
||||
|
||||
gegl_buffer_copy (src_buf, NULL, GEGL_ABYSS_NONE, dest_buf, NULL);
|
||||
|
||||
g_object_unref (src_buf);
|
||||
g_object_unref (dest_buf);
|
||||
|
||||
pika_progress_update (1.0);
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
save_image (GFile *file,
|
||||
const gchar *gegl_op,
|
||||
PikaImage *image,
|
||||
PikaDrawable *drawable,
|
||||
GError **error)
|
||||
{
|
||||
GeglNode *graph;
|
||||
GeglNode *source;
|
||||
GeglNode *sink;
|
||||
GeglBuffer *src_buf;
|
||||
|
||||
src_buf = pika_drawable_get_buffer (drawable);
|
||||
|
||||
graph = gegl_node_new ();
|
||||
|
||||
source = gegl_node_new_child (graph,
|
||||
"operation", "gegl:buffer-source",
|
||||
"buffer", src_buf,
|
||||
NULL);
|
||||
sink = gegl_node_new_child (graph,
|
||||
"operation", gegl_op,
|
||||
"path", g_file_peek_path (file),
|
||||
NULL);
|
||||
|
||||
gegl_node_link (source, sink);
|
||||
|
||||
gegl_node_process (sink);
|
||||
|
||||
g_object_unref (graph);
|
||||
g_object_unref (src_buf);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
1367
plug-ins/common/file-gif-load.c
Normal file
1367
plug-ins/common/file-gif-load.c
Normal file
File diff suppressed because it is too large
Load Diff
2511
plug-ins/common/file-gif-save.c
Normal file
2511
plug-ins/common/file-gif-save.c
Normal file
File diff suppressed because it is too large
Load Diff
896
plug-ins/common/file-gih.c
Normal file
896
plug-ins/common/file-gih.c
Normal file
@ -0,0 +1,896 @@
|
||||
/* Plug-in to load and export .gih (PIKA Brush Pipe) files.
|
||||
*
|
||||
* Copyright (C) 1999 Tor Lillqvist
|
||||
* Copyright (C) 2000 Jens Lautenbacher, Sven Neumann
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* Example of how to call file_gih_save from script-fu:
|
||||
|
||||
(let ((ranks (cons-array 1 'byte)))
|
||||
(aset ranks 0 12)
|
||||
(file-gih-save 1
|
||||
img
|
||||
drawable
|
||||
"foo.gih"
|
||||
"foo.gih"
|
||||
100
|
||||
"test brush"
|
||||
125
|
||||
125
|
||||
3
|
||||
4
|
||||
1
|
||||
ranks
|
||||
1
|
||||
'("random")))
|
||||
*/
|
||||
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <libpika/pika.h>
|
||||
#include <libpika/pikaui.h>
|
||||
#include <libpikabase/pikaparasiteio.h>
|
||||
|
||||
#include "libpika/stdplugins-intl.h"
|
||||
|
||||
|
||||
#define SAVE_PROC "file-gih-save"
|
||||
#define PLUG_IN_BINARY "file-gih"
|
||||
#define PLUG_IN_ROLE "pika-file-gih"
|
||||
|
||||
|
||||
/* Parameters applicable each time we export a gih, exported in the
|
||||
* main pika application between invocations of this plug-in.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
gchar description[256];
|
||||
gint spacing;
|
||||
} BrushInfo;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
PikaOrientationType orientation;
|
||||
PikaImage *image;
|
||||
PikaLayer *toplayer;
|
||||
gint nguides;
|
||||
gint32 *guides;
|
||||
gint *value;
|
||||
GtkWidget *count_label; /* Corresponding count adjustment, */
|
||||
gint *count; /* cols or rows */
|
||||
gint *other_count; /* and the other one */
|
||||
GtkAdjustment *ncells;
|
||||
GtkAdjustment *rank0;
|
||||
GtkWidget *warning_label;
|
||||
GtkWidget *rank_entry[PIKA_PIXPIPE_MAXDIM];
|
||||
GtkWidget *mode_entry[PIKA_PIXPIPE_MAXDIM];
|
||||
} SizeAdjustmentData;
|
||||
|
||||
|
||||
typedef struct _Gih Gih;
|
||||
typedef struct _GihClass GihClass;
|
||||
|
||||
struct _Gih
|
||||
{
|
||||
PikaPlugIn parent_instance;
|
||||
};
|
||||
|
||||
struct _GihClass
|
||||
{
|
||||
PikaPlugInClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
#define GIH_TYPE (gih_get_type ())
|
||||
#define GIH (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIH_TYPE, Gih))
|
||||
|
||||
GType gih_get_type (void) G_GNUC_CONST;
|
||||
|
||||
static GList * gih_query_procedures (PikaPlugIn *plug_in);
|
||||
static PikaProcedure * gih_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name);
|
||||
|
||||
static PikaValueArray * gih_save (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
GFile *file,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data);
|
||||
|
||||
static gboolean gih_save_dialog (PikaImage *image);
|
||||
|
||||
|
||||
G_DEFINE_TYPE (Gih, gih, PIKA_TYPE_PLUG_IN)
|
||||
|
||||
PIKA_MAIN (GIH_TYPE)
|
||||
DEFINE_STD_SET_I18N
|
||||
|
||||
|
||||
static BrushInfo info =
|
||||
{
|
||||
"PIKA Brush Pipe",
|
||||
20
|
||||
};
|
||||
|
||||
static gint num_layers = 0;
|
||||
static PikaPixPipeParams gihparams = { 0, };
|
||||
|
||||
static const gchar * const selection_modes[] = { "incremental",
|
||||
"angular",
|
||||
"random",
|
||||
"velocity",
|
||||
"pressure",
|
||||
"xtilt",
|
||||
"ytilt" };
|
||||
|
||||
|
||||
static void
|
||||
gih_class_init (GihClass *klass)
|
||||
{
|
||||
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
|
||||
|
||||
plug_in_class->query_procedures = gih_query_procedures;
|
||||
plug_in_class->create_procedure = gih_create_procedure;
|
||||
plug_in_class->set_i18n = STD_SET_I18N;
|
||||
}
|
||||
|
||||
static void
|
||||
gih_init (Gih *gih)
|
||||
{
|
||||
}
|
||||
|
||||
static GList *
|
||||
gih_query_procedures (PikaPlugIn *plug_in)
|
||||
{
|
||||
return g_list_append (NULL, g_strdup (SAVE_PROC));
|
||||
}
|
||||
|
||||
static PikaProcedure *
|
||||
gih_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name)
|
||||
{
|
||||
PikaProcedure *procedure = NULL;
|
||||
|
||||
if (! strcmp (name, SAVE_PROC))
|
||||
{
|
||||
procedure = pika_save_procedure_new (plug_in, name,
|
||||
PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
gih_save, NULL, NULL);
|
||||
|
||||
pika_procedure_set_image_types (procedure, "RGB*, GRAY*");
|
||||
|
||||
pika_procedure_set_menu_label (procedure, _("PIKA brush (animated)"));
|
||||
pika_procedure_set_icon_name (procedure, PIKA_ICON_BRUSH);
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
"exports images in PIKA brush pipe "
|
||||
"format",
|
||||
"This plug-in exports an image in "
|
||||
"the PIKA brush pipe format. For a "
|
||||
"colored brush pipe, RGBA layers are "
|
||||
"used, otherwise the layers should be "
|
||||
"grayscale masks. The image can be "
|
||||
"multi-layered, and additionally the "
|
||||
"layers can be divided into a "
|
||||
"rectangular array of brushes.",
|
||||
SAVE_PROC);
|
||||
pika_procedure_set_attribution (procedure,
|
||||
"Tor Lillqvist",
|
||||
"Tor Lillqvist",
|
||||
"1999");
|
||||
|
||||
pika_file_procedure_set_mime_types (PIKA_FILE_PROCEDURE (procedure),
|
||||
"image/x-pika-gih");
|
||||
pika_file_procedure_set_extensions (PIKA_FILE_PROCEDURE (procedure),
|
||||
"gih");
|
||||
pika_file_procedure_set_handles_remote (PIKA_FILE_PROCEDURE (procedure),
|
||||
TRUE);
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "spacing",
|
||||
"Spacing",
|
||||
"Spacing of the brush",
|
||||
1, 1000, 10,
|
||||
PIKA_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_STRING (procedure, "description",
|
||||
"Description",
|
||||
"Short description of the gihtern",
|
||||
"PIKA Gihtern",
|
||||
PIKA_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "cell-width",
|
||||
"Cell width",
|
||||
"Width of the brush cells",
|
||||
1, 1000, 10,
|
||||
PIKA_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "cell-height",
|
||||
"Cell height",
|
||||
"Height of the brush cells",
|
||||
1, 1000, 10,
|
||||
PIKA_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "display-cols",
|
||||
"Display columns",
|
||||
"Display column number",
|
||||
1, 1000, 1,
|
||||
PIKA_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "display-rows",
|
||||
"Display rows",
|
||||
"Display row number",
|
||||
1, 1000, 1,
|
||||
PIKA_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_BYTES (procedure, "rank",
|
||||
"Rank",
|
||||
"Ranks of the dimensions",
|
||||
PIKA_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "dimension-2",
|
||||
"Dimension 2",
|
||||
"Dimension of the brush pipe (same as dimension)",
|
||||
1, 4, 1,
|
||||
PIKA_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_STRV (procedure, "sel",
|
||||
"Sel",
|
||||
"Selection modes",
|
||||
PIKA_PARAM_READWRITE);
|
||||
}
|
||||
|
||||
return procedure;
|
||||
}
|
||||
|
||||
static PikaValueArray *
|
||||
gih_save (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
GFile *file,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data)
|
||||
{
|
||||
PikaPDBStatusType status = PIKA_PDB_SUCCESS;
|
||||
PikaExportReturn export = PIKA_EXPORT_CANCEL;
|
||||
PikaParasite *parasite;
|
||||
PikaImage *orig_image;
|
||||
GError *error = NULL;
|
||||
gint i;
|
||||
GBytes *rank_bytes;
|
||||
|
||||
orig_image = image;
|
||||
|
||||
switch (run_mode)
|
||||
{
|
||||
case PIKA_RUN_INTERACTIVE:
|
||||
case PIKA_RUN_WITH_LAST_VALS:
|
||||
pika_ui_init (PLUG_IN_BINARY);
|
||||
|
||||
export = pika_export_image (&image, &n_drawables, &drawables, "GIH",
|
||||
PIKA_EXPORT_CAN_HANDLE_RGB |
|
||||
PIKA_EXPORT_CAN_HANDLE_GRAY |
|
||||
PIKA_EXPORT_CAN_HANDLE_ALPHA |
|
||||
PIKA_EXPORT_CAN_HANDLE_LAYERS);
|
||||
|
||||
if (export == PIKA_EXPORT_CANCEL)
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_CANCEL,
|
||||
NULL);
|
||||
|
||||
/* Possibly retrieve data */
|
||||
pika_get_data (SAVE_PROC, &info);
|
||||
|
||||
parasite = pika_image_get_parasite (orig_image,
|
||||
"pika-brush-pipe-name");
|
||||
if (parasite)
|
||||
{
|
||||
gchar *parasite_data;
|
||||
guint32 parasite_size;
|
||||
|
||||
parasite_data = (gchar *) pika_parasite_get_data (parasite, ¶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;
|
||||
}
|
||||
488
plug-ins/common/file-glob.c
Normal file
488
plug-ins/common/file-glob.c
Normal file
@ -0,0 +1,488 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* The idea is taken from a plug-in written by George Hartz; the code isn't.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "libpika/pika.h"
|
||||
|
||||
|
||||
#define PLUG_IN_PROC "file-glob"
|
||||
|
||||
|
||||
typedef struct _Glob Glob;
|
||||
typedef struct _GlobClass GlobClass;
|
||||
|
||||
struct _Glob
|
||||
{
|
||||
PikaPlugIn parent_instance;
|
||||
};
|
||||
|
||||
struct _GlobClass
|
||||
{
|
||||
PikaPlugInClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
#define GLOB_TYPE (glob_get_type ())
|
||||
#define GLOB (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GLOB_TYPE, Glob))
|
||||
|
||||
GType glob_get_type (void) G_GNUC_CONST;
|
||||
|
||||
static GList * glob_query_procedures (PikaPlugIn *plug_in);
|
||||
static PikaProcedure * glob_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name);
|
||||
|
||||
static PikaValueArray * glob_run (PikaProcedure *procedure,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data);
|
||||
|
||||
static gboolean glob_match (const gchar *pattern,
|
||||
gboolean filename_encoding,
|
||||
gchar ***matches);
|
||||
static gboolean glob_fnmatch (const gchar *pattern,
|
||||
const gchar *string);
|
||||
|
||||
|
||||
G_DEFINE_TYPE (Glob, glob, PIKA_TYPE_PLUG_IN)
|
||||
|
||||
PIKA_MAIN (GLOB_TYPE)
|
||||
|
||||
|
||||
static void
|
||||
glob_class_init (GlobClass *klass)
|
||||
{
|
||||
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
|
||||
|
||||
plug_in_class->query_procedures = glob_query_procedures;
|
||||
plug_in_class->create_procedure = glob_create_procedure;
|
||||
plug_in_class->set_i18n = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
glob_init (Glob *glob)
|
||||
{
|
||||
}
|
||||
|
||||
static GList *
|
||||
glob_query_procedures (PikaPlugIn *plug_in)
|
||||
{
|
||||
return g_list_append (NULL, g_strdup (PLUG_IN_PROC));
|
||||
}
|
||||
|
||||
static PikaProcedure *
|
||||
glob_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name)
|
||||
{
|
||||
PikaProcedure *procedure = NULL;
|
||||
|
||||
if (! strcmp (name, PLUG_IN_PROC))
|
||||
{
|
||||
procedure = pika_procedure_new (plug_in, name,
|
||||
PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
glob_run, NULL, NULL);
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
"Returns a list of matching filenames",
|
||||
"This can be useful in scripts and "
|
||||
"other plug-ins (e.g., "
|
||||
"batch-conversion). See the glob(7) "
|
||||
"manpage for more info. Note however "
|
||||
"that this isn't a full-featured glob "
|
||||
"implementation. It only handles "
|
||||
"simple patterns like "
|
||||
"\"/home/foo/bar/*.jpg\".",
|
||||
name);
|
||||
pika_procedure_set_attribution (procedure,
|
||||
"Sven Neumann",
|
||||
"Sven Neumann",
|
||||
"2004");
|
||||
|
||||
PIKA_PROC_ARG_STRING (procedure, "pattern",
|
||||
"Pattern",
|
||||
"The glob pattern (in UTF-8 encoding)",
|
||||
NULL,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_BOOLEAN (procedure, "filename-encoding",
|
||||
"Filename encoding",
|
||||
"FALSE to return UTF-8 strings, TRUE to return "
|
||||
"strings in filename encoding",
|
||||
FALSE,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_VAL_STRV (procedure, "files",
|
||||
"Files",
|
||||
"The list of matching filenames",
|
||||
G_PARAM_READWRITE |
|
||||
PIKA_PARAM_NO_VALIDATE);
|
||||
}
|
||||
|
||||
return procedure;
|
||||
}
|
||||
|
||||
static PikaValueArray *
|
||||
glob_run (PikaProcedure *procedure,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data)
|
||||
{
|
||||
PikaValueArray *return_vals;
|
||||
const gchar *pattern;
|
||||
gboolean filename_encoding;
|
||||
gchar **matches;
|
||||
|
||||
pattern = PIKA_VALUES_GET_STRING (args, 0);
|
||||
filename_encoding = PIKA_VALUES_GET_BOOLEAN (args, 1);
|
||||
|
||||
if (! glob_match (pattern, filename_encoding, &matches))
|
||||
{
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_EXECUTION_ERROR,
|
||||
NULL);
|
||||
}
|
||||
|
||||
return_vals = pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_SUCCESS,
|
||||
NULL);
|
||||
|
||||
PIKA_VALUES_TAKE_STRV (return_vals, 1, matches);
|
||||
|
||||
return return_vals;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
glob_match (const gchar *pattern,
|
||||
gboolean filename_encoding,
|
||||
gchar ***matches)
|
||||
{
|
||||
GDir *dir;
|
||||
GPtrArray *array;
|
||||
const gchar *filename;
|
||||
gchar *dirname;
|
||||
gchar *tmp;
|
||||
|
||||
g_return_val_if_fail (pattern != NULL, FALSE);
|
||||
g_return_val_if_fail (matches != NULL, FALSE);
|
||||
|
||||
*matches = NULL;
|
||||
|
||||
/* This is not a complete glob() implementation but rather a very
|
||||
* simplistic approach. However it works for the most common use
|
||||
* case and is better than nothing.
|
||||
*/
|
||||
|
||||
tmp = g_filename_from_utf8 (pattern, -1, NULL, NULL, NULL);
|
||||
if (! tmp)
|
||||
return FALSE;
|
||||
|
||||
dirname = g_path_get_dirname (tmp);
|
||||
|
||||
dir = g_dir_open (dirname, 0, NULL);
|
||||
g_free (tmp);
|
||||
|
||||
if (! dir)
|
||||
{
|
||||
g_free (dirname);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* check if the pattern has a directory part at all */
|
||||
tmp = g_path_get_basename (pattern);
|
||||
if (strcmp (pattern, tmp) == 0)
|
||||
{
|
||||
g_free (dirname);
|
||||
dirname = NULL;
|
||||
}
|
||||
g_free (tmp);
|
||||
|
||||
array = g_ptr_array_new ();
|
||||
|
||||
for (filename = g_dir_read_name (dir);
|
||||
filename;
|
||||
filename = g_dir_read_name (dir))
|
||||
{
|
||||
gchar *path;
|
||||
gchar *name;
|
||||
|
||||
if (dirname)
|
||||
path = g_build_filename (dirname, filename, NULL);
|
||||
else
|
||||
path = g_strdup (filename);
|
||||
|
||||
name = g_filename_to_utf8 (path, -1, NULL, NULL, NULL);
|
||||
|
||||
if (name && glob_fnmatch (pattern, name))
|
||||
{
|
||||
if (filename_encoding)
|
||||
{
|
||||
g_ptr_array_add (array, path);
|
||||
path = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_ptr_array_add (array, name);
|
||||
name = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
g_free (path);
|
||||
g_free (name);
|
||||
}
|
||||
|
||||
g_dir_close (dir);
|
||||
g_free (dirname);
|
||||
|
||||
/* NULL-terminator */
|
||||
g_ptr_array_add (array, NULL);
|
||||
|
||||
*matches = (gchar **) g_ptr_array_free (array, FALSE);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* The following code is borrowed from GTK+.
|
||||
*
|
||||
* GTK+ used to use a old version of GNU fnmatch() that was buggy
|
||||
* in various ways and didn't handle UTF-8. The following is
|
||||
* converted to UTF-8. To simplify the process of making it
|
||||
* correct, this is special-cased to the combinations of flags
|
||||
* that gtkfilesel.c uses.
|
||||
*
|
||||
* FNM_FILE_NAME - always set
|
||||
* FNM_LEADING_DIR - never set
|
||||
* FNM_NOESCAPE - set only on windows
|
||||
* FNM_CASEFOLD - set only on windows
|
||||
*/
|
||||
|
||||
/* We need to make sure that all constants are defined
|
||||
* to properly compile this file
|
||||
*/
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
static gunichar
|
||||
get_char (const char **str)
|
||||
{
|
||||
gunichar c = g_utf8_get_char (*str);
|
||||
*str = g_utf8_next_char (*str);
|
||||
|
||||
#ifdef G_PLATFORM_WIN32
|
||||
c = g_unichar_tolower (c);
|
||||
#endif
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
#if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN)
|
||||
#define DO_ESCAPE 0
|
||||
#else
|
||||
#define DO_ESCAPE 1
|
||||
#endif
|
||||
|
||||
static gunichar
|
||||
get_unescaped_char (const char **str,
|
||||
gboolean *was_escaped)
|
||||
{
|
||||
gunichar c = get_char (str);
|
||||
|
||||
*was_escaped = DO_ESCAPE && c == '\\';
|
||||
if (*was_escaped)
|
||||
c = get_char (str);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
/* Match STRING against the filename pattern PATTERN,
|
||||
* returning TRUE if it matches, FALSE otherwise.
|
||||
*/
|
||||
static gboolean
|
||||
fnmatch_intern (const gchar *pattern,
|
||||
const gchar *string,
|
||||
gboolean component_start,
|
||||
gboolean no_leading_period)
|
||||
{
|
||||
const char *p = pattern, *n = string;
|
||||
|
||||
while (*p)
|
||||
{
|
||||
const char *last_n = n;
|
||||
|
||||
gunichar c = get_char (&p);
|
||||
gunichar nc = get_char (&n);
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case '?':
|
||||
if (nc == '\0')
|
||||
return FALSE;
|
||||
else if (nc == G_DIR_SEPARATOR)
|
||||
return FALSE;
|
||||
else if (nc == '.' && component_start && no_leading_period)
|
||||
return FALSE;
|
||||
break;
|
||||
case '\\':
|
||||
if (DO_ESCAPE)
|
||||
c = get_char (&p);
|
||||
if (nc != c)
|
||||
return FALSE;
|
||||
break;
|
||||
case '*':
|
||||
if (nc == '.' && component_start && no_leading_period)
|
||||
return FALSE;
|
||||
|
||||
{
|
||||
const char *last_p = p;
|
||||
|
||||
for (last_p = p, c = get_char (&p);
|
||||
c == '?' || c == '*';
|
||||
last_p = p, c = get_char (&p))
|
||||
{
|
||||
if (c == '?')
|
||||
{
|
||||
if (nc == '\0')
|
||||
return FALSE;
|
||||
else if (nc == G_DIR_SEPARATOR)
|
||||
return FALSE;
|
||||
else
|
||||
{
|
||||
last_n = n; nc = get_char (&n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If the pattern ends with wildcards, we have a
|
||||
* guaranteed match unless there is a dir separator
|
||||
* in the remainder of the string.
|
||||
*/
|
||||
if (c == '\0')
|
||||
{
|
||||
if (strchr (last_n, G_DIR_SEPARATOR) != NULL)
|
||||
return FALSE;
|
||||
else
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (DO_ESCAPE && c == '\\')
|
||||
c = get_char (&p);
|
||||
|
||||
for (p = last_p; nc != '\0';)
|
||||
{
|
||||
if ((c == '[' || nc == c) &&
|
||||
fnmatch_intern (p, last_n,
|
||||
component_start, no_leading_period))
|
||||
return TRUE;
|
||||
|
||||
component_start = (nc == G_DIR_SEPARATOR);
|
||||
last_n = n;
|
||||
nc = get_char (&n);
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
case '[':
|
||||
{
|
||||
/* Nonzero if the sense of the character class is inverted. */
|
||||
gboolean not;
|
||||
gboolean was_escaped;
|
||||
|
||||
if (nc == '\0' || nc == G_DIR_SEPARATOR)
|
||||
return FALSE;
|
||||
|
||||
if (nc == '.' && component_start && no_leading_period)
|
||||
return FALSE;
|
||||
|
||||
not = (*p == '!' || *p == '^');
|
||||
if (not)
|
||||
++p;
|
||||
|
||||
c = get_unescaped_char (&p, &was_escaped);
|
||||
for (;;)
|
||||
{
|
||||
register gunichar cstart = c, cend = c;
|
||||
if (c == '\0')
|
||||
/* [ (unterminated) loses. */
|
||||
return FALSE;
|
||||
|
||||
c = get_unescaped_char (&p, &was_escaped);
|
||||
|
||||
if (!was_escaped && c == '-' && *p != ']')
|
||||
{
|
||||
cend = get_unescaped_char (&p, &was_escaped);
|
||||
if (cend == '\0')
|
||||
return FALSE;
|
||||
|
||||
c = get_char (&p);
|
||||
}
|
||||
|
||||
if (nc >= cstart && nc <= cend)
|
||||
goto matched;
|
||||
|
||||
if (!was_escaped && c == ']')
|
||||
break;
|
||||
}
|
||||
if (!not)
|
||||
return FALSE;
|
||||
break;
|
||||
|
||||
matched:;
|
||||
/* Skip the rest of the [...] that already matched. */
|
||||
/* XXX 1003.2d11 is unclear if was_escaped is right. */
|
||||
while (was_escaped || c != ']')
|
||||
{
|
||||
if (c == '\0')
|
||||
/* [... (unterminated) loses. */
|
||||
return FALSE;
|
||||
|
||||
c = get_unescaped_char (&p, &was_escaped);
|
||||
}
|
||||
if (not)
|
||||
return FALSE;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (c != nc)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
component_start = (nc == G_DIR_SEPARATOR);
|
||||
}
|
||||
|
||||
if (*n == '\0')
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
glob_fnmatch (const gchar *pattern,
|
||||
const gchar *string)
|
||||
{
|
||||
return fnmatch_intern (pattern, string, TRUE, TRUE);
|
||||
}
|
||||
474
plug-ins/common/file-header.c
Normal file
474
plug-ins/common/file-header.c
Normal file
@ -0,0 +1,474 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <libpika/pika.h>
|
||||
#include <libpika/pikaui.h>
|
||||
|
||||
#include "libpika/stdplugins-intl.h"
|
||||
|
||||
|
||||
#define SAVE_PROC "file-header-save"
|
||||
#define PLUG_IN_BINARY "file-header"
|
||||
#define PLUG_IN_ROLE "pika-file-header"
|
||||
|
||||
|
||||
typedef struct _Header Header;
|
||||
typedef struct _HeaderClass HeaderClass;
|
||||
|
||||
struct _Header
|
||||
{
|
||||
PikaPlugIn parent_instance;
|
||||
};
|
||||
|
||||
struct _HeaderClass
|
||||
{
|
||||
PikaPlugInClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
#define HEADER_TYPE (header_get_type ())
|
||||
#define HEADER (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), HEADER_TYPE, Header))
|
||||
|
||||
GType header_get_type (void) G_GNUC_CONST;
|
||||
|
||||
static GList * header_query_procedures (PikaPlugIn *plug_in);
|
||||
static PikaProcedure * header_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name);
|
||||
|
||||
static PikaValueArray * header_save (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
GFile *file,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data);
|
||||
|
||||
static gboolean save_image (GFile *file,
|
||||
PikaImage *image,
|
||||
PikaDrawable *drawable,
|
||||
GError **error);
|
||||
|
||||
static gboolean print (GOutputStream *output,
|
||||
GError **error,
|
||||
const gchar *format,
|
||||
...) G_GNUC_PRINTF (3, 4);
|
||||
|
||||
|
||||
G_DEFINE_TYPE (Header, header, PIKA_TYPE_PLUG_IN)
|
||||
|
||||
PIKA_MAIN (HEADER_TYPE)
|
||||
DEFINE_STD_SET_I18N
|
||||
|
||||
|
||||
static void
|
||||
header_class_init (HeaderClass *klass)
|
||||
{
|
||||
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
|
||||
|
||||
plug_in_class->query_procedures = header_query_procedures;
|
||||
plug_in_class->create_procedure = header_create_procedure;
|
||||
plug_in_class->set_i18n = STD_SET_I18N;
|
||||
}
|
||||
|
||||
static void
|
||||
header_init (Header *header)
|
||||
{
|
||||
}
|
||||
|
||||
static GList *
|
||||
header_query_procedures (PikaPlugIn *plug_in)
|
||||
{
|
||||
return g_list_append (NULL, g_strdup (SAVE_PROC));
|
||||
}
|
||||
|
||||
static PikaProcedure *
|
||||
header_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name)
|
||||
{
|
||||
PikaProcedure *procedure = NULL;
|
||||
|
||||
if (! strcmp (name, SAVE_PROC))
|
||||
{
|
||||
procedure = pika_save_procedure_new (plug_in, name,
|
||||
PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
header_save, NULL, NULL);
|
||||
|
||||
pika_procedure_set_image_types (procedure, "INDEXED, RGB");
|
||||
|
||||
pika_procedure_set_menu_label (procedure, _("C source code header"));
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
"saves files as C unsigned character "
|
||||
"array",
|
||||
"FIXME: write help",
|
||||
name);
|
||||
pika_procedure_set_attribution (procedure,
|
||||
"Spencer Kimball & Peter Mattis",
|
||||
"Spencer Kimball & Peter Mattis",
|
||||
"1997");
|
||||
|
||||
pika_file_procedure_set_handles_remote (PIKA_FILE_PROCEDURE (procedure),
|
||||
TRUE);
|
||||
pika_file_procedure_set_mime_types (PIKA_FILE_PROCEDURE (procedure),
|
||||
"image/x-chdr");
|
||||
pika_file_procedure_set_extensions (PIKA_FILE_PROCEDURE (procedure),
|
||||
"h");
|
||||
}
|
||||
|
||||
return procedure;
|
||||
}
|
||||
|
||||
static PikaValueArray *
|
||||
header_save (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
GFile *file,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data)
|
||||
{
|
||||
PikaPDBStatusType status = PIKA_PDB_SUCCESS;
|
||||
PikaExportReturn export = PIKA_EXPORT_CANCEL;
|
||||
GError *error = NULL;
|
||||
|
||||
gegl_init (NULL, NULL);
|
||||
|
||||
switch (run_mode)
|
||||
{
|
||||
case PIKA_RUN_INTERACTIVE:
|
||||
case PIKA_RUN_WITH_LAST_VALS:
|
||||
pika_ui_init (PLUG_IN_BINARY);
|
||||
|
||||
export = pika_export_image (&image, &n_drawables, &drawables, "Header",
|
||||
PIKA_EXPORT_CAN_HANDLE_RGB |
|
||||
PIKA_EXPORT_CAN_HANDLE_INDEXED);
|
||||
|
||||
if (export == PIKA_EXPORT_CANCEL)
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_CANCEL,
|
||||
NULL);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (n_drawables != 1)
|
||||
{
|
||||
g_set_error (&error, G_FILE_ERROR, 0,
|
||||
_("Header plug-in does not support multiple layers."));
|
||||
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_CALLING_ERROR,
|
||||
error);
|
||||
}
|
||||
|
||||
if (! save_image (file, image, drawables[0],
|
||||
&error))
|
||||
{
|
||||
status = PIKA_PDB_EXECUTION_ERROR;
|
||||
}
|
||||
|
||||
if (export == PIKA_EXPORT_EXPORT)
|
||||
{
|
||||
pika_image_delete (image);
|
||||
g_free (drawables);
|
||||
}
|
||||
|
||||
return pika_procedure_new_return_values (procedure, status, error);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
save_image (GFile *file,
|
||||
PikaImage *image,
|
||||
PikaDrawable *drawable,
|
||||
GError **error)
|
||||
{
|
||||
GeglBuffer *buffer;
|
||||
const Babl *format;
|
||||
PikaImageType drawable_type;
|
||||
GOutputStream *output;
|
||||
gint x, y, b, c;
|
||||
const gchar *backslash = "\\\\";
|
||||
const gchar *quote = "\\\"";
|
||||
const gchar *newline = "\"\n\t\"";
|
||||
gchar buf[4];
|
||||
guchar *d = NULL;
|
||||
guchar *data = NULL;
|
||||
guchar *cmap;
|
||||
GCancellable *cancellable;
|
||||
gint colors;
|
||||
gint width;
|
||||
gint height;
|
||||
|
||||
output = G_OUTPUT_STREAM (g_file_replace (file,
|
||||
NULL, FALSE, G_FILE_CREATE_NONE,
|
||||
NULL, error));
|
||||
if (output)
|
||||
{
|
||||
GOutputStream *buffered;
|
||||
|
||||
buffered = g_buffered_output_stream_new (output);
|
||||
g_object_unref (output);
|
||||
|
||||
output = buffered;
|
||||
}
|
||||
else
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
buffer = pika_drawable_get_buffer (drawable);
|
||||
|
||||
width = gegl_buffer_get_width (buffer);
|
||||
height = gegl_buffer_get_height (buffer);
|
||||
|
||||
drawable_type = pika_drawable_type (drawable);
|
||||
|
||||
if (! print (output, error,
|
||||
"/* PIKA header image file format (%s): %s */\n\n",
|
||||
PIKA_RGB_IMAGE == drawable_type ? "RGB" : "INDEXED",
|
||||
pika_file_get_utf8_name (file)) ||
|
||||
! print (output, error,
|
||||
"static unsigned int width = %d;\n", width) ||
|
||||
! print (output, error,
|
||||
"static unsigned int height = %d;\n\n", height) ||
|
||||
! print (output, error,
|
||||
"/* Call this macro repeatedly. After each use, the pixel data can be extracted */\n\n"))
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
|
||||
switch (drawable_type)
|
||||
{
|
||||
case PIKA_RGB_IMAGE:
|
||||
if (! print (output, error,
|
||||
"#define HEADER_PIXEL(data,pixel) {\\\n"
|
||||
"pixel[0] = (((data[0] - 33) << 2) | ((data[1] - 33) >> 4)); \\\n"
|
||||
"pixel[1] = ((((data[1] - 33) & 0xF) << 4) | ((data[2] - 33) >> 2)); \\\n"
|
||||
"pixel[2] = ((((data[2] - 33) & 0x3) << 6) | ((data[3] - 33))); \\\n"
|
||||
"data += 4; \\\n}\n") ||
|
||||
! print (output, error,
|
||||
"static char *header_data =\n\t\""))
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
|
||||
format = babl_format ("R'G'B' u8");
|
||||
|
||||
data = g_new (guchar, width * babl_format_get_bytes_per_pixel (format));
|
||||
|
||||
c = 0;
|
||||
for (y = 0; y < height; y++)
|
||||
{
|
||||
gegl_buffer_get (buffer, GEGL_RECTANGLE (0, y, width, 1), 1.0,
|
||||
format, data,
|
||||
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
|
||||
|
||||
for (x = 0; x < width; x++)
|
||||
{
|
||||
d = data + x * babl_format_get_bytes_per_pixel (format);
|
||||
|
||||
buf[0] = ((d[0] >> 2) & 0x3F) + 33;
|
||||
buf[1] = ((((d[0] & 0x3) << 4) | (d[1] >> 4)) & 0x3F) + 33;
|
||||
buf[2] = ((((d[1] & 0xF) << 2) | (d[2] >> 6)) & 0x3F) + 33;
|
||||
buf[3] = (d[2] & 0x3F) + 33;
|
||||
|
||||
for (b = 0; b < 4; b++)
|
||||
{
|
||||
if (buf[b] == '"')
|
||||
{
|
||||
if (! print (output, error, "%s", quote))
|
||||
goto fail;
|
||||
}
|
||||
else if (buf[b] == '\\')
|
||||
{
|
||||
if (! print (output, error, "%s", backslash))
|
||||
goto fail;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (! print (output, error, "%c", buf[b]))
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
c++;
|
||||
if (c >= 16)
|
||||
{
|
||||
if (! print (output, error, "%s", newline))
|
||||
goto fail;
|
||||
|
||||
c = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (! print (output, error, "\";\n"))
|
||||
goto fail;
|
||||
break;
|
||||
|
||||
case PIKA_INDEXED_IMAGE:
|
||||
if (! print (output, error,
|
||||
"#define HEADER_PIXEL(data,pixel) {\\\n"
|
||||
"pixel[0] = header_data_cmap[(unsigned char)data[0]][0]; \\\n"
|
||||
"pixel[1] = header_data_cmap[(unsigned char)data[0]][1]; \\\n"
|
||||
"pixel[2] = header_data_cmap[(unsigned char)data[0]][2]; \\\n"
|
||||
"data ++; }\n\n"))
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* save colormap */
|
||||
cmap = pika_image_get_colormap (image, NULL, &colors);
|
||||
|
||||
if (! print (output, error,
|
||||
"static unsigned char header_data_cmap[256][3] = {") ||
|
||||
! print (output, error,
|
||||
"\n\t{%3d,%3d,%3d}",
|
||||
(gint) cmap[0], (gint) cmap[1], (gint) cmap[2]))
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
|
||||
for (c = 1; c < colors; c++)
|
||||
{
|
||||
if (! print (output, error,
|
||||
",\n\t{%3d,%3d,%3d}",
|
||||
(gint) cmap[3 * c],
|
||||
(gint) cmap[3 * c + 1],
|
||||
(gint) cmap[3 * c + 2]))
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
/* fill the rest */
|
||||
for ( ; c < 256; c++)
|
||||
{
|
||||
if (! print (output, error, ",\n\t{255,255,255}"))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* close bracket */
|
||||
if (! print (output, error, "\n\t};\n"))
|
||||
goto fail;
|
||||
|
||||
g_free (cmap);
|
||||
|
||||
/* save image */
|
||||
if (! print (output, error, "static unsigned char header_data[] = {\n\t"))
|
||||
goto fail;
|
||||
|
||||
data = g_new (guchar, width * 1);
|
||||
|
||||
c = 0;
|
||||
for (y = 0; y < height; y++)
|
||||
{
|
||||
gegl_buffer_get (buffer, GEGL_RECTANGLE (0, y, width, 1), 1.0,
|
||||
NULL, data,
|
||||
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
|
||||
|
||||
for (x = 0; x < width -1; x++)
|
||||
{
|
||||
d = data + x * 1;
|
||||
|
||||
if (! print (output, error, "%d,", (gint) d[0]))
|
||||
goto fail;
|
||||
|
||||
c++;
|
||||
if (c >= 16)
|
||||
{
|
||||
if (! print (output, error, "\n\t"))
|
||||
goto fail;
|
||||
|
||||
c = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (y != height - 1)
|
||||
{
|
||||
if (! print (output, error, "%d,\n\t", (gint) d[1]))
|
||||
goto fail;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (! print (output, error, "%d\n\t", (gint) d[1]))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
c = 0; /* reset line counter */
|
||||
}
|
||||
|
||||
if (! print (output, error, "};\n"))
|
||||
goto fail;
|
||||
break;
|
||||
|
||||
default:
|
||||
g_warning ("unhandled drawable type (%d)", drawable_type);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (! g_output_stream_close (output, NULL, error))
|
||||
goto fail;
|
||||
|
||||
g_free (data);
|
||||
g_object_unref (output);
|
||||
g_object_unref (buffer);
|
||||
|
||||
return TRUE;
|
||||
|
||||
fail:
|
||||
|
||||
cancellable = g_cancellable_new ();
|
||||
g_cancellable_cancel (cancellable);
|
||||
g_output_stream_close (output, cancellable, NULL);
|
||||
|
||||
g_free (data);
|
||||
g_object_unref (output);
|
||||
g_object_unref (buffer);
|
||||
g_object_unref (cancellable);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
print (GOutputStream *output,
|
||||
GError **error,
|
||||
const gchar *format,
|
||||
...)
|
||||
{
|
||||
va_list args;
|
||||
gboolean success;
|
||||
|
||||
va_start (args, format);
|
||||
success = g_output_stream_vprintf (output, NULL, NULL,
|
||||
error, format, args);
|
||||
va_end (args);
|
||||
|
||||
return success;
|
||||
}
|
||||
2562
plug-ins/common/file-heif.c
Normal file
2562
plug-ins/common/file-heif.c
Normal file
File diff suppressed because it is too large
Load Diff
726
plug-ins/common/file-html-table.c
Normal file
726
plug-ins/common/file-html-table.c
Normal file
@ -0,0 +1,726 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* GTM plug-in --- PIKA Table Magic
|
||||
* Allows images to be exported as HTML tables with different colored cells.
|
||||
* It doesn't have very much practical use other than being able to
|
||||
* easily design a table by "painting" it in PIKA, or to make small HTML
|
||||
* table images/icons.
|
||||
*
|
||||
* Copyright (C) 1997 Daniel Dunbar
|
||||
* Email: ddunbar@diads.com
|
||||
* WWW: http://millennium.diads.com/pika/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* Version 1.0:
|
||||
* Once I first found out that it was possible to have pixel level control
|
||||
* of HTML tables I instantly realized that it would be possible, however
|
||||
* pointless, to save an image as a, albeit huge, HTML table.
|
||||
*
|
||||
* One night when I was feeling in an adventourously stupid programming mood
|
||||
* I decided to write a program to do it.
|
||||
*
|
||||
* At first I just wrote a really ugly hack to do it, which I then planned
|
||||
* on using once just to see how it worked, and then posting a URL and
|
||||
* laughing about it on #pika. As it turns out, tigert thought it actually
|
||||
* had potential to be a useful plugin, so I started adding features and
|
||||
* and making a nice UI.
|
||||
*
|
||||
* It's still not very useful, but I did manage to significantly improve my
|
||||
* C programming skills in the process, so it was worth it.
|
||||
*
|
||||
* If you happen to find it useful I would appreciate any email about it.
|
||||
* - Daniel Dunbar
|
||||
* ddunbar@diads.com
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <libpika/pika.h>
|
||||
#include <libpika/pikaui.h>
|
||||
|
||||
#include "libpika/stdplugins-intl.h"
|
||||
|
||||
|
||||
#define SAVE_PROC "file-html-table-save"
|
||||
#define PLUG_IN_BINARY "file-html-table"
|
||||
|
||||
|
||||
typedef struct _Html Html;
|
||||
typedef struct _HtmlClass HtmlClass;
|
||||
|
||||
struct _Html
|
||||
{
|
||||
PikaPlugIn parent_instance;
|
||||
};
|
||||
|
||||
struct _HtmlClass
|
||||
{
|
||||
PikaPlugInClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
#define HTML_TYPE (html_get_type ())
|
||||
#define HTML (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), HTML_TYPE, Html))
|
||||
|
||||
GType html_get_type (void) G_GNUC_CONST;
|
||||
|
||||
static GList * html_query_procedures (PikaPlugIn *plug_in);
|
||||
static PikaProcedure * html_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name);
|
||||
|
||||
static PikaValueArray * html_save (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
GFile *file,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data);
|
||||
|
||||
static gboolean save_image (GFile *file,
|
||||
GeglBuffer *buffer,
|
||||
GObject *config,
|
||||
GError **error);
|
||||
static gboolean save_dialog (PikaImage *image,
|
||||
PikaProcedure *procedure,
|
||||
GObject *config);
|
||||
|
||||
static gboolean print (GOutputStream *output,
|
||||
GError **error,
|
||||
const gchar *format,
|
||||
...) G_GNUC_PRINTF (3, 0);
|
||||
static gboolean color_comp (guchar *buffer,
|
||||
guchar *buf2);
|
||||
|
||||
|
||||
G_DEFINE_TYPE (Html, html, PIKA_TYPE_PLUG_IN)
|
||||
|
||||
PIKA_MAIN (HTML_TYPE)
|
||||
DEFINE_STD_SET_I18N
|
||||
|
||||
|
||||
static void
|
||||
html_class_init (HtmlClass *klass)
|
||||
{
|
||||
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
|
||||
|
||||
plug_in_class->query_procedures = html_query_procedures;
|
||||
plug_in_class->create_procedure = html_create_procedure;
|
||||
plug_in_class->set_i18n = STD_SET_I18N;
|
||||
}
|
||||
|
||||
static void
|
||||
html_init (Html *html)
|
||||
{
|
||||
}
|
||||
|
||||
static GList *
|
||||
html_query_procedures (PikaPlugIn *plug_in)
|
||||
{
|
||||
return g_list_append (NULL, g_strdup (SAVE_PROC));
|
||||
}
|
||||
|
||||
static PikaProcedure *
|
||||
html_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name)
|
||||
{
|
||||
PikaProcedure *procedure = NULL;
|
||||
|
||||
if (! strcmp (name, SAVE_PROC))
|
||||
{
|
||||
procedure = pika_save_procedure_new (plug_in, name,
|
||||
PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
html_save, NULL, NULL);
|
||||
|
||||
pika_procedure_set_image_types (procedure, "*");
|
||||
|
||||
pika_procedure_set_menu_label (procedure, _("HTML table"));
|
||||
pika_file_procedure_set_format_name (PIKA_FILE_PROCEDURE (procedure),
|
||||
_("HTML Table"));
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
_("PIKA Table Magic"),
|
||||
_("Allows you to draw an HTML table "
|
||||
"in PIKA. See help for more info."),
|
||||
name);
|
||||
pika_procedure_set_attribution (procedure,
|
||||
"Daniel Dunbar",
|
||||
"Daniel Dunbar",
|
||||
"1998");
|
||||
|
||||
pika_file_procedure_set_handles_remote (PIKA_FILE_PROCEDURE (procedure),
|
||||
TRUE);
|
||||
pika_file_procedure_set_mime_types (PIKA_FILE_PROCEDURE (procedure),
|
||||
"text/html");
|
||||
pika_file_procedure_set_extensions (PIKA_FILE_PROCEDURE (procedure),
|
||||
"html,htm");
|
||||
|
||||
PIKA_PROC_AUX_ARG_BOOLEAN (procedure, "use-caption",
|
||||
_("Use c_aption"),
|
||||
_("Enable if you would like to have the table "
|
||||
"captioned."),
|
||||
FALSE,
|
||||
PIKA_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_AUX_ARG_STRING (procedure, "caption-text",
|
||||
_("Capt_ion"),
|
||||
_("The text for the table caption."),
|
||||
"Made with PIKA Table Magic",
|
||||
PIKA_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_AUX_ARG_STRING (procedure, "cell-content",
|
||||
_("Cell con_tent"),
|
||||
_("The text to go into each cell."),
|
||||
" ",
|
||||
PIKA_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_AUX_ARG_STRING (procedure, "cell-width",
|
||||
_("_Width"),
|
||||
_("The width for each table cell. "
|
||||
"Can be a number or a percent."),
|
||||
"",
|
||||
PIKA_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_AUX_ARG_STRING (procedure, "cell-height",
|
||||
_("_Height"),
|
||||
_("The height for each table cell. "
|
||||
"Can be a number or a percent."),
|
||||
"",
|
||||
PIKA_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_AUX_ARG_BOOLEAN (procedure, "full-document",
|
||||
_("_Generate full HTML document"),
|
||||
_("If enabled GTM will output a full HTML "
|
||||
"document with <HTML>, <BODY>, etc. tags "
|
||||
"instead of just the table html."),
|
||||
TRUE,
|
||||
PIKA_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_AUX_ARG_INT (procedure, "border",
|
||||
_("_Border"),
|
||||
_("The number of pixels in the table border."),
|
||||
0, 1000, 2,
|
||||
PIKA_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_AUX_ARG_BOOLEAN (procedure, "span-tags",
|
||||
_("_Use cellspan"),
|
||||
_("If enabled GTM will replace any "
|
||||
"rectangular sections of identically "
|
||||
"colored blocks with one large cell with "
|
||||
"ROWSPAN and COLSPAN values."),
|
||||
FALSE,
|
||||
PIKA_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_AUX_ARG_BOOLEAN (procedure, "compress-td-tags",
|
||||
_("Co_mpress TD tags"),
|
||||
_("Enabling this will cause GTM to "
|
||||
"leave no whitespace between the TD "
|
||||
"tags and the cell content. This is only "
|
||||
"necessary for pixel level positioning "
|
||||
"control."),
|
||||
FALSE,
|
||||
PIKA_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_AUX_ARG_INT (procedure, "cell-padding",
|
||||
_("Cell-pa_dding"),
|
||||
_("The amount of cell padding."),
|
||||
0, 1000, 4,
|
||||
PIKA_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_AUX_ARG_INT (procedure, "cell-spacing",
|
||||
_("Cell spaci_ng"),
|
||||
_("The amount of cell spacing."),
|
||||
0, 1000, 0,
|
||||
PIKA_PARAM_READWRITE);
|
||||
}
|
||||
|
||||
return procedure;
|
||||
}
|
||||
|
||||
static PikaValueArray *
|
||||
html_save (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
GFile *file,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data)
|
||||
{
|
||||
PikaProcedureConfig *config;
|
||||
PikaPDBStatusType status = PIKA_PDB_SUCCESS;
|
||||
GeglBuffer *buffer;
|
||||
GError *error = NULL;
|
||||
|
||||
gegl_init (NULL, NULL);
|
||||
|
||||
if (run_mode != PIKA_RUN_INTERACTIVE)
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_CALLING_ERROR,
|
||||
NULL);
|
||||
|
||||
config = pika_procedure_create_config (procedure);
|
||||
pika_procedure_config_begin_run (config, image, run_mode, args);
|
||||
|
||||
if (! save_dialog (image, procedure, G_OBJECT (config)))
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_CANCEL,
|
||||
NULL);
|
||||
|
||||
if (n_drawables != 1)
|
||||
{
|
||||
g_set_error (&error, G_FILE_ERROR, 0,
|
||||
_("HTML table plug-in does not support multiple layers."));
|
||||
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_CALLING_ERROR,
|
||||
error);
|
||||
}
|
||||
|
||||
buffer = pika_drawable_get_buffer (drawables[0]);
|
||||
|
||||
if (! save_image (file, buffer, G_OBJECT (config),
|
||||
&error))
|
||||
{
|
||||
status = PIKA_PDB_EXECUTION_ERROR;
|
||||
}
|
||||
|
||||
g_object_unref (buffer);
|
||||
|
||||
pika_procedure_config_end_run (config, status);
|
||||
g_object_unref (config);
|
||||
|
||||
return pika_procedure_new_return_values (procedure, status, error);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
save_image (GFile *file,
|
||||
GeglBuffer *buffer,
|
||||
GObject *config,
|
||||
GError **error)
|
||||
{
|
||||
const Babl *format = babl_format ("R'G'B'A u8");
|
||||
GeglSampler *sampler;
|
||||
GCancellable *cancellable;
|
||||
GOutputStream *output;
|
||||
gint row, col;
|
||||
gint cols, rows;
|
||||
gint x, y;
|
||||
gint colcount, colspan, rowspan;
|
||||
gint *palloc;
|
||||
guchar *buf, *buf2;
|
||||
gchar *width = NULL;
|
||||
gchar *height = NULL;
|
||||
gboolean config_use_caption;
|
||||
gchar *config_caption_text;
|
||||
gchar *config_cell_content;
|
||||
gchar *config_cell_width;
|
||||
gchar *config_cell_height;
|
||||
gboolean config_full_document;
|
||||
gint config_border;
|
||||
gboolean config_span_tags;
|
||||
gboolean config_compress_td_tags;
|
||||
gint config_cell_padding;
|
||||
gint config_cell_spacing;
|
||||
|
||||
g_object_get (config,
|
||||
"use-caption", &config_use_caption,
|
||||
"caption-text", &config_caption_text,
|
||||
"cell-content", &config_cell_content,
|
||||
"cell-width", &config_cell_width,
|
||||
"cell-height", &config_cell_height,
|
||||
"full-document", &config_full_document,
|
||||
"border", &config_border,
|
||||
"span-tags", &config_span_tags,
|
||||
"compress-td-tags", &config_compress_td_tags,
|
||||
"cell-padding", &config_cell_padding,
|
||||
"cell-spacing", &config_cell_spacing,
|
||||
NULL);
|
||||
|
||||
if (! config_caption_text) config_caption_text = g_strdup ("");
|
||||
if (! config_cell_content) config_cell_content = g_strdup ("");
|
||||
if (! config_cell_width) config_cell_width = g_strdup ("");
|
||||
if (! config_cell_height) config_cell_height = g_strdup ("");
|
||||
|
||||
cols = gegl_buffer_get_width (buffer);
|
||||
rows = gegl_buffer_get_height (buffer);
|
||||
|
||||
pika_progress_init_printf (_("Exporting '%s'"),
|
||||
pika_file_get_utf8_name (file));
|
||||
|
||||
output = G_OUTPUT_STREAM (g_file_replace (file,
|
||||
NULL, FALSE, G_FILE_CREATE_NONE,
|
||||
NULL, error));
|
||||
if (output)
|
||||
{
|
||||
GOutputStream *buffered;
|
||||
|
||||
buffered = g_buffered_output_stream_new (output);
|
||||
g_object_unref (output);
|
||||
|
||||
output = buffered;
|
||||
}
|
||||
else
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
sampler = gegl_buffer_sampler_new (buffer, format, GEGL_SAMPLER_NEAREST);
|
||||
|
||||
palloc = g_new (int, rows * cols);
|
||||
|
||||
if (config_full_document)
|
||||
{
|
||||
if (! print (output, error,
|
||||
"<HTML>\n<HEAD><TITLE>%s</TITLE></HEAD>\n<BODY>\n",
|
||||
pika_file_get_utf8_name (file)) ||
|
||||
! print (output, error, "<H1>%s</H1>\n",
|
||||
pika_file_get_utf8_name (file)))
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (! print (output, error,
|
||||
"<TABLE BORDER=%d CELLPADDING=%d CELLSPACING=%d>\n",
|
||||
config_border, config_cell_padding, config_cell_spacing))
|
||||
goto fail;
|
||||
|
||||
if (config_use_caption)
|
||||
{
|
||||
if (! print (output, error, "<CAPTION>%s</CAPTION>\n",
|
||||
config_caption_text))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
buf = g_newa (guchar, babl_format_get_bytes_per_pixel (format));
|
||||
buf2 = g_newa (guchar, babl_format_get_bytes_per_pixel (format));
|
||||
|
||||
if (strcmp (config_cell_width, "") != 0)
|
||||
{
|
||||
width = g_strdup_printf (" WIDTH=\"%s\"", config_cell_width);
|
||||
}
|
||||
|
||||
if (strcmp (config_cell_height, "") != 0)
|
||||
{
|
||||
height = g_strdup_printf (" HEIGHT=\"%s\" ", config_cell_height);
|
||||
}
|
||||
|
||||
if (! width)
|
||||
width = g_strdup (" ");
|
||||
|
||||
if (! height)
|
||||
height = g_strdup (" ");
|
||||
|
||||
/* Initialize array to hold ROWSPAN and COLSPAN cell allocation table */
|
||||
|
||||
for (row = 0; row < rows; row++)
|
||||
for (col = 0; col < cols; col++)
|
||||
palloc[cols * row + col] = 1;
|
||||
|
||||
colspan = 0;
|
||||
rowspan = 0;
|
||||
|
||||
for (y = 0; y < rows; y++)
|
||||
{
|
||||
if (! print (output, error, " <TR>\n"))
|
||||
goto fail;
|
||||
|
||||
for (x = 0; x < cols; x++)
|
||||
{
|
||||
gegl_sampler_get (sampler, x, y, NULL, buf, GEGL_ABYSS_NONE);
|
||||
|
||||
/* Determine ROWSPAN and COLSPAN */
|
||||
|
||||
if (config_span_tags)
|
||||
{
|
||||
col = x;
|
||||
row = y;
|
||||
colcount = 0;
|
||||
colspan = 0;
|
||||
rowspan = 0;
|
||||
|
||||
gegl_sampler_get (sampler, col, row, NULL, buf2, GEGL_ABYSS_NONE);
|
||||
|
||||
while (color_comp (buf, buf2) &&
|
||||
palloc[cols * row + col] == 1 &&
|
||||
row < rows)
|
||||
{
|
||||
while (color_comp (buf, buf2) &&
|
||||
palloc[cols * row + col] == 1 &&
|
||||
col < cols)
|
||||
{
|
||||
colcount++;
|
||||
col++;
|
||||
|
||||
gegl_sampler_get (sampler,
|
||||
col, row, NULL, buf2, GEGL_ABYSS_NONE);
|
||||
}
|
||||
|
||||
if (colcount != 0)
|
||||
{
|
||||
row++;
|
||||
rowspan++;
|
||||
}
|
||||
|
||||
if (colcount < colspan || colspan == 0)
|
||||
colspan = colcount;
|
||||
|
||||
col = x;
|
||||
colcount = 0;
|
||||
|
||||
gegl_sampler_get (sampler,
|
||||
col, row, NULL, buf2, GEGL_ABYSS_NONE);
|
||||
}
|
||||
|
||||
if (colspan > 1 || rowspan > 1)
|
||||
{
|
||||
for (row = 0; row < rowspan; row++)
|
||||
for (col = 0; col < colspan; col++)
|
||||
palloc[cols * (row + y) + (col + x)] = 0;
|
||||
|
||||
palloc[cols * y + x] = 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (palloc[cols * y + x] == 1)
|
||||
{
|
||||
if (! print (output, error,
|
||||
" <TD%s%sBGCOLOR=#%02x%02x%02x>",
|
||||
width, height, buf[0], buf[1], buf[2]))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (palloc[cols * y + x] == 2)
|
||||
{
|
||||
if (! print (output, error,
|
||||
" <TD ROWSPAN=\"%d\" COLSPAN=\"%d\"%s%sBGCOLOR=#%02x%02x%02x>",
|
||||
rowspan, colspan, width, height,
|
||||
buf[0], buf[1], buf[2]))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (palloc[cols * y + x] != 0)
|
||||
{
|
||||
if (config_compress_td_tags)
|
||||
{
|
||||
if (! print (output, error,
|
||||
"%s</TD>\n", config_cell_content))
|
||||
goto fail;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (! print (output, error,
|
||||
"\n %s\n </TD>\n", config_cell_content))
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (! print (output, error, " </TR>\n"))
|
||||
goto fail;
|
||||
|
||||
pika_progress_update ((double) y / (double) rows);
|
||||
}
|
||||
|
||||
if (config_full_document)
|
||||
{
|
||||
if (! print (output, error, "</TABLE></BODY></HTML>\n"))
|
||||
goto fail;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (! print (output, error, "</TABLE>\n"))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (! g_output_stream_close (output, NULL, error))
|
||||
goto fail;
|
||||
|
||||
g_object_unref (output);
|
||||
g_object_unref (sampler);
|
||||
g_free (width);
|
||||
g_free (height);
|
||||
g_free (palloc);
|
||||
|
||||
g_free (config_caption_text);
|
||||
g_free (config_cell_content);
|
||||
g_free (config_cell_width);
|
||||
g_free (config_cell_height);
|
||||
|
||||
pika_progress_update (1.0);
|
||||
|
||||
return TRUE;
|
||||
|
||||
fail:
|
||||
|
||||
cancellable = g_cancellable_new ();
|
||||
g_cancellable_cancel (cancellable);
|
||||
g_output_stream_close (output, cancellable, NULL);
|
||||
g_object_unref (cancellable);
|
||||
|
||||
g_object_unref (output);
|
||||
g_object_unref (sampler);
|
||||
g_free (width);
|
||||
g_free (height);
|
||||
g_free (palloc);
|
||||
|
||||
g_free (config_caption_text);
|
||||
g_free (config_cell_content);
|
||||
g_free (config_cell_width);
|
||||
g_free (config_cell_height);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gint
|
||||
save_dialog (PikaImage *image,
|
||||
PikaProcedure *procedure,
|
||||
GObject *config)
|
||||
{
|
||||
GtkWidget *dialog;
|
||||
GtkWidget *frame;
|
||||
gboolean run;
|
||||
|
||||
pika_ui_init (PLUG_IN_BINARY);
|
||||
|
||||
dialog = pika_save_procedure_dialog_new (PIKA_SAVE_PROCEDURE (procedure),
|
||||
PIKA_PROCEDURE_CONFIG (config),
|
||||
image);
|
||||
|
||||
if (pika_image_get_width (image) * pika_image_get_height (image) > 4096)
|
||||
{
|
||||
GtkWidget *eek;
|
||||
GtkWidget *hbox;
|
||||
|
||||
pika_procedure_dialog_get_label (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"warning-label",
|
||||
_("You are about to create a huge\n"
|
||||
"HTML file which will most likely\n"
|
||||
"crash your browser."));
|
||||
hbox = pika_procedure_dialog_fill_box (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"warning-hbox", "warning-label",
|
||||
NULL);
|
||||
gtk_orientable_set_orientation (GTK_ORIENTABLE (hbox),
|
||||
GTK_ORIENTATION_HORIZONTAL);
|
||||
|
||||
eek = gtk_image_new_from_icon_name (PIKA_ICON_MASCOT_EEK,
|
||||
GTK_ICON_SIZE_DIALOG);
|
||||
gtk_box_pack_start (GTK_BOX (hbox), eek, FALSE, FALSE, 0);
|
||||
gtk_widget_show (eek);
|
||||
gtk_box_reorder_child (GTK_BOX (hbox), eek, 0);
|
||||
gtk_widget_set_margin_end (eek, 24);
|
||||
|
||||
pika_procedure_dialog_get_label (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"warning-frame-label", _("Warning"));
|
||||
frame = pika_procedure_dialog_fill_frame (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"warning-frame",
|
||||
"warning-frame-label",
|
||||
FALSE, "warning-hbox");
|
||||
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
|
||||
|
||||
pika_procedure_dialog_fill (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"warning-frame", NULL);
|
||||
}
|
||||
|
||||
/* HTML Page Options */
|
||||
pika_procedure_dialog_get_label (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"page-label", _("HTML Page Options"));
|
||||
frame = pika_procedure_dialog_fill_frame (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"page-frame",
|
||||
"page-label",
|
||||
FALSE, "full-document");
|
||||
gtk_widget_set_margin_bottom (frame, 8);
|
||||
|
||||
/* HTML Table Creation Options */
|
||||
pika_procedure_dialog_get_label (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"creation-label",
|
||||
_("Table Creation Options"));
|
||||
|
||||
pika_procedure_dialog_fill_box (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"creation-vbox", "span-tags",
|
||||
"compress-td-tags", "use-caption",
|
||||
"caption-text", "cell-content", NULL);
|
||||
|
||||
pika_procedure_dialog_set_sensitive (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"caption-text", TRUE,
|
||||
config, "use-caption",
|
||||
FALSE);
|
||||
|
||||
frame = pika_procedure_dialog_fill_frame (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"creation-frame",
|
||||
"creation-label",
|
||||
FALSE, "creation-vbox");
|
||||
gtk_widget_set_margin_bottom (frame, 8);
|
||||
/* HTML Table Options */
|
||||
pika_procedure_dialog_get_label (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"table-label", _("Table Options"));
|
||||
|
||||
pika_procedure_dialog_fill_box (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"table-vbox", "border", "cell-width",
|
||||
"cell-height", "cell-padding",
|
||||
"cell-spacing", NULL);
|
||||
|
||||
frame = pika_procedure_dialog_fill_frame (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"table-frame", "table-label",
|
||||
FALSE, "table-vbox");
|
||||
gtk_widget_set_margin_bottom (frame, 8);
|
||||
|
||||
pika_procedure_dialog_fill (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"page-frame", "creation-frame",
|
||||
"table-frame", NULL);
|
||||
|
||||
gtk_widget_show (dialog);
|
||||
|
||||
run = pika_procedure_dialog_run (PIKA_PROCEDURE_DIALOG (dialog));
|
||||
|
||||
gtk_widget_destroy (dialog);
|
||||
|
||||
return run;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
print (GOutputStream *output,
|
||||
GError **error,
|
||||
const gchar *format,
|
||||
...)
|
||||
{
|
||||
va_list args;
|
||||
gboolean success;
|
||||
|
||||
va_start (args, format);
|
||||
success = g_output_stream_vprintf (output, NULL, NULL,
|
||||
error, format, args);
|
||||
va_end (args);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
color_comp (guchar *buf,
|
||||
guchar *buf2)
|
||||
{
|
||||
return (buf[0] == buf2[0] &&
|
||||
buf[1] == buf2[1] &&
|
||||
buf[2] == buf2[2]);
|
||||
}
|
||||
542
plug-ins/common/file-iff.c
Normal file
542
plug-ins/common/file-iff.c
Normal file
@ -0,0 +1,542 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* Amiga IFF plug-in
|
||||
*
|
||||
* Copyright (C) 2023 Alex S.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <glib/gstdio.h>
|
||||
|
||||
#include <libpika/pika.h>
|
||||
#include <libpika/pikaui.h>
|
||||
|
||||
#include <libilbm/ilbm.h>
|
||||
#include <libilbm/ilbmimage.h>
|
||||
#include <libilbm/interleave.h>
|
||||
#include <libilbm/byterun.h>
|
||||
|
||||
#include "libpika/stdplugins-intl.h"
|
||||
|
||||
|
||||
#define LOAD_PROC "file-iff-load"
|
||||
#define PLUG_IN_BINARY "file-iff"
|
||||
#define PLUG_IN_ROLE "pika-file-iff"
|
||||
|
||||
typedef struct _Iff Iff;
|
||||
typedef struct _IffClass IffClass;
|
||||
|
||||
struct _Iff
|
||||
{
|
||||
PikaPlugIn parent_instance;
|
||||
};
|
||||
|
||||
struct _IffClass
|
||||
{
|
||||
PikaPlugInClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
#define IFF_TYPE (iff_get_type ())
|
||||
#define IFF (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), IFF_TYPE, Iff))
|
||||
|
||||
GType iff_get_type (void) G_GNUC_CONST;
|
||||
|
||||
|
||||
static GList * iff_query_procedures (PikaPlugIn *plug_in);
|
||||
static PikaProcedure * iff_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name);
|
||||
|
||||
static PikaValueArray * iff_load (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
GFile *file,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data);
|
||||
|
||||
static PikaImage * load_image (GFile *file,
|
||||
GObject *config,
|
||||
PikaRunMode run_mode,
|
||||
GError **error);
|
||||
|
||||
static void deleave_indexed_row (IFF_UByte *bitplanes,
|
||||
guchar *pixel_row,
|
||||
gint width,
|
||||
gint nPlanes);
|
||||
|
||||
static void deleave_rgb_row (IFF_UByte *bitplanes,
|
||||
guchar *pixel_row,
|
||||
gint width,
|
||||
gint nPlanes,
|
||||
gint pixel_size);
|
||||
|
||||
static void deleave_ham_row (const guchar *pika_cmap,
|
||||
IFF_UByte *bitplanes,
|
||||
guchar *pixel_row,
|
||||
gint width,
|
||||
gint nPlanes);
|
||||
|
||||
static void pbm_row (IFF_UByte *bitplanes,
|
||||
guchar *pixel_row,
|
||||
gint width);
|
||||
|
||||
|
||||
G_DEFINE_TYPE (Iff, iff, PIKA_TYPE_PLUG_IN)
|
||||
|
||||
PIKA_MAIN (IFF_TYPE)
|
||||
DEFINE_STD_SET_I18N
|
||||
|
||||
|
||||
static void
|
||||
iff_class_init (IffClass *klass)
|
||||
{
|
||||
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
|
||||
|
||||
plug_in_class->query_procedures = iff_query_procedures;
|
||||
plug_in_class->create_procedure = iff_create_procedure;
|
||||
plug_in_class->set_i18n = STD_SET_I18N;
|
||||
}
|
||||
|
||||
static void
|
||||
iff_init (Iff *iff)
|
||||
{
|
||||
}
|
||||
|
||||
static GList *
|
||||
iff_query_procedures (PikaPlugIn *plug_in)
|
||||
{
|
||||
GList *list = NULL;
|
||||
|
||||
list = g_list_append (list, g_strdup (LOAD_PROC));
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
static PikaProcedure *
|
||||
iff_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name)
|
||||
{
|
||||
PikaProcedure *procedure = NULL;
|
||||
|
||||
if (! strcmp (name, LOAD_PROC))
|
||||
{
|
||||
procedure = pika_load_procedure_new (plug_in, name,
|
||||
PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
iff_load, NULL, NULL);
|
||||
|
||||
pika_procedure_set_menu_label (procedure, _("Amiga IFF"));
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
_("Load file in the IFF file format"),
|
||||
_("Load file in the IFF file format"),
|
||||
name);
|
||||
pika_procedure_set_attribution (procedure,
|
||||
"Alex S.",
|
||||
"Alex S.",
|
||||
"2023");
|
||||
|
||||
pika_file_procedure_set_mime_types (PIKA_FILE_PROCEDURE (procedure),
|
||||
"image/x-ilbm");
|
||||
pika_file_procedure_set_extensions (PIKA_FILE_PROCEDURE (procedure),
|
||||
"iff,ilbm,lbm,acbm,ham,ham6,ham8");
|
||||
pika_file_procedure_set_magics (PIKA_FILE_PROCEDURE (procedure),
|
||||
"0,string,FORM");
|
||||
}
|
||||
|
||||
return procedure;
|
||||
}
|
||||
|
||||
static PikaValueArray *
|
||||
iff_load (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
GFile *file,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data)
|
||||
{
|
||||
PikaProcedureConfig *config;
|
||||
PikaValueArray *return_vals;
|
||||
PikaImage *image;
|
||||
GError *error = NULL;
|
||||
|
||||
gegl_init (NULL, NULL);
|
||||
|
||||
config = pika_procedure_create_config (procedure);
|
||||
pika_procedure_config_begin_run (config, NULL, run_mode, args);
|
||||
|
||||
image = load_image (file, G_OBJECT (config), run_mode, &error);
|
||||
|
||||
if (! image)
|
||||
{
|
||||
pika_procedure_config_end_run (config, PIKA_PDB_EXECUTION_ERROR);
|
||||
g_object_unref (config);
|
||||
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_EXECUTION_ERROR,
|
||||
error);
|
||||
}
|
||||
|
||||
pika_procedure_config_end_run (config, PIKA_PDB_SUCCESS);
|
||||
g_object_unref (config);
|
||||
|
||||
return_vals = pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_SUCCESS,
|
||||
NULL);
|
||||
|
||||
PIKA_VALUES_SET_IMAGE (return_vals, 1, image);
|
||||
|
||||
return return_vals;
|
||||
}
|
||||
|
||||
static PikaImage *
|
||||
load_image (GFile *file,
|
||||
GObject *config,
|
||||
PikaRunMode run_mode,
|
||||
GError **error)
|
||||
{
|
||||
PikaImage *image = NULL;
|
||||
PikaLayer *layer;
|
||||
GeglBuffer *buffer;
|
||||
FILE *fp;
|
||||
guint imagesLength;
|
||||
IFF_Chunk *chunk;
|
||||
ILBM_Image **iff_image;
|
||||
|
||||
fp = g_fopen (g_file_peek_path (file), "rb");
|
||||
|
||||
if (! fp)
|
||||
{
|
||||
g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
|
||||
_("Could not open '%s' for reading: %s"),
|
||||
pika_file_get_utf8_name (file), g_strerror (errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fclose (fp);
|
||||
|
||||
chunk = ILBM_read (g_file_peek_path (file));
|
||||
iff_image = ILBM_extractImages (chunk, &imagesLength);
|
||||
|
||||
for (gint i = 0; i < imagesLength; i++)
|
||||
{
|
||||
ILBM_Image *true_image = iff_image[i];
|
||||
/* Struct representing bitmap header properties */
|
||||
ILBM_BitMapHeader *bitMapHeader = true_image->bitMapHeader;
|
||||
/* Struct containing the color palette */
|
||||
ILBM_ColorMap *colorMap = true_image->colorMap;
|
||||
ILBM_Viewport *camg = true_image->viewport;
|
||||
IFF_UByte *bitplanes;
|
||||
PikaImageType image_type;
|
||||
guchar pika_cmap[768]; /* Max index is (2^nplanes) - 1 */
|
||||
gint width;
|
||||
gint height;
|
||||
gint nPlanes;
|
||||
gint palette_size = 0;
|
||||
gint row_length;
|
||||
gint pixel_size = 1;
|
||||
gint y_height = 0;
|
||||
gboolean ehb_mode = FALSE;
|
||||
gboolean ham_mode = FALSE;
|
||||
|
||||
if (! true_image || ! bitMapHeader)
|
||||
{
|
||||
g_message (_("Invalid or missing ILBM image"));
|
||||
return image;
|
||||
}
|
||||
if (! true_image->body)
|
||||
{
|
||||
g_message (_("ILBM contains no image data - likely a palette file"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Convert ACBM files to ILBM format */
|
||||
if (ILBM_imageIsACBM (true_image))
|
||||
ILBM_convertACBMToILBM (true_image);
|
||||
|
||||
width = bitMapHeader->w;
|
||||
height = bitMapHeader->h;
|
||||
nPlanes = bitMapHeader->nPlanes;
|
||||
row_length = (width + 15) / 16;
|
||||
pixel_size = nPlanes / 8;
|
||||
|
||||
/* Check for ILBM variants in CMAG chunk */
|
||||
if (camg)
|
||||
{
|
||||
if (camg->viewportMode & (1 << 7))
|
||||
ehb_mode = TRUE;
|
||||
if (camg->viewportMode & (1 << 11) &&
|
||||
(nPlanes >= 5 && nPlanes <= 8))
|
||||
ham_mode = TRUE;
|
||||
}
|
||||
|
||||
/* Load palette if it exists */
|
||||
if (colorMap)
|
||||
{
|
||||
palette_size = colorMap->colorRegisterLength;
|
||||
|
||||
for (gint j = 0; j < palette_size; j++)
|
||||
{
|
||||
pika_cmap[j * 3] = colorMap->colorRegister[j].red;
|
||||
pika_cmap[j * 3 + 1] = colorMap->colorRegister[j].green;
|
||||
pika_cmap[j * 3 + 2] = colorMap->colorRegister[j].blue;
|
||||
}
|
||||
|
||||
if (ehb_mode)
|
||||
{
|
||||
/* EHB mode adds 32 more colors. Each are half the RGB values
|
||||
* of the first 32 colors */
|
||||
for (gint j = 0; j < palette_size * 2; j++)
|
||||
{
|
||||
gint offset_index = j + 32;
|
||||
|
||||
pika_cmap[offset_index * 3] =
|
||||
colorMap->colorRegister[j].red / 2;
|
||||
pika_cmap[offset_index * 3 + 1] =
|
||||
colorMap->colorRegister[j].green / 2;
|
||||
pika_cmap[offset_index * 3 + 2] =
|
||||
colorMap->colorRegister[j].blue / 2;
|
||||
}
|
||||
/* EHB mode always has 64 colors */
|
||||
palette_size = 64;
|
||||
}
|
||||
}
|
||||
|
||||
if (ham_mode)
|
||||
pixel_size = 3;
|
||||
|
||||
if (pixel_size == 4)
|
||||
{
|
||||
image_type = PIKA_RGBA_IMAGE;
|
||||
}
|
||||
else if (pixel_size == 3)
|
||||
{
|
||||
image_type = PIKA_RGB_IMAGE;
|
||||
}
|
||||
else
|
||||
{
|
||||
pixel_size = 1;
|
||||
image_type = PIKA_INDEXED_IMAGE;
|
||||
}
|
||||
|
||||
ILBM_unpackByteRun (true_image);
|
||||
|
||||
image = pika_image_new (width, height,
|
||||
pixel_size == 1 ? PIKA_INDEXED : PIKA_RGB);
|
||||
|
||||
layer = pika_layer_new (image, _("Background"), width, height,
|
||||
image_type, 100,
|
||||
pika_image_get_default_new_layer_mode (image));
|
||||
pika_image_insert_layer (image, layer, NULL, 0);
|
||||
|
||||
buffer = pika_drawable_get_buffer (PIKA_DRAWABLE (layer));
|
||||
|
||||
bitplanes = true_image->body->chunkData;
|
||||
|
||||
/* Loading rows */
|
||||
for (gint j = 0; j < height; j++)
|
||||
{
|
||||
guchar *pixel_row;
|
||||
|
||||
pixel_row = g_malloc (width * pixel_size * sizeof (guchar));
|
||||
|
||||
/* PBM uses one byte per pixel index */
|
||||
if (ILBM_imageIsPBM (true_image))
|
||||
pbm_row (bitplanes, pixel_row, width);
|
||||
else if (pixel_size == 1)
|
||||
deleave_indexed_row (bitplanes, pixel_row, width, nPlanes);
|
||||
else if (ham_mode)
|
||||
deleave_ham_row (pika_cmap, bitplanes, pixel_row, width, nPlanes);
|
||||
else
|
||||
deleave_rgb_row (bitplanes, pixel_row, width, nPlanes, pixel_size);
|
||||
|
||||
bitplanes += (row_length * 2 * nPlanes);
|
||||
|
||||
gegl_buffer_set (buffer, GEGL_RECTANGLE (0, y_height, width, 1), 0,
|
||||
NULL, pixel_row, GEGL_AUTO_ROWSTRIDE);
|
||||
|
||||
y_height++;
|
||||
g_free (pixel_row);
|
||||
}
|
||||
|
||||
if (pixel_size == 1)
|
||||
pika_image_set_colormap (image, pika_cmap, palette_size);
|
||||
|
||||
g_object_unref (buffer);
|
||||
}
|
||||
ILBM_freeImages (iff_image, imagesLength);
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
static void
|
||||
deleave_indexed_row (IFF_UByte *bitplanes,
|
||||
guchar *pixel_row,
|
||||
gint width,
|
||||
gint nPlanes)
|
||||
{
|
||||
guchar index[width];
|
||||
gint row_length = ((width + 15) / 16) * 2;
|
||||
|
||||
/* Initialize index array */
|
||||
for (gint i = 0; i < width; i++)
|
||||
index[i] = 0;
|
||||
|
||||
/* Deleave rows */
|
||||
for (gint i = 0; i < row_length; i++)
|
||||
{
|
||||
for (gint j = 0; j < 8; j++)
|
||||
{
|
||||
guint8 bitmask = (1 << (8 - j)) - (1 << (7 - j));
|
||||
|
||||
for (gint k = 0; k < nPlanes; k++)
|
||||
{
|
||||
guint8 update = (1 << (k + 1)) - (1 << (k));
|
||||
|
||||
if (bitplanes[i + (row_length * k)] & bitmask)
|
||||
index[j + (i * 8)] += update;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Associate palette with pixels */
|
||||
for (gint i = 0; i < width; i++)
|
||||
pixel_row[i] = index[i];
|
||||
}
|
||||
|
||||
static void
|
||||
deleave_ham_row (const guchar *pika_cmap,
|
||||
IFF_UByte *bitplanes,
|
||||
guchar *pixel_row,
|
||||
gint width,
|
||||
gint nPlanes)
|
||||
{
|
||||
const gint control_index[3] = {2, 0, 1};
|
||||
const gint row_length = ((width + 15) / 16) * 2;
|
||||
gint prior_rgb[3] = {0, 0, 0};
|
||||
gint current_index = 0;
|
||||
|
||||
/* Deleave rows */
|
||||
for (gint i = 0; i < row_length; i++)
|
||||
{
|
||||
for (gint j = 0; j < 8; j++)
|
||||
{
|
||||
guint8 bitmask = (1 << (8 - j)) - (1 << (7 - j));
|
||||
guint8 control = 0;
|
||||
guint8 color = 0;
|
||||
guint8 index = 0;
|
||||
|
||||
for (gint k = 0; k < nPlanes; k++)
|
||||
{
|
||||
if (bitplanes[i + (row_length * k)] & bitmask)
|
||||
{
|
||||
gint limit = nPlanes < 7 ? 4 : 6;
|
||||
|
||||
/* The last two planes are control values.
|
||||
* Everything else is either an index or a color.
|
||||
* For HAM 5/6 colors, we use the 4 bits as both
|
||||
* upper and lower bit modifiers. For HAM 7/8,
|
||||
* we replace the 6 MSB with the color value. */
|
||||
if (k < limit)
|
||||
{
|
||||
gint update = 1 << k;
|
||||
|
||||
index += update;
|
||||
if (limit == 4)
|
||||
color += update + (update << 4);
|
||||
else
|
||||
color += update;
|
||||
}
|
||||
else
|
||||
{
|
||||
control += 1 << (k - limit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (control == 0)
|
||||
{
|
||||
prior_rgb[0] = pika_cmap[index * 3];
|
||||
prior_rgb[1] = pika_cmap[index * 3 + 1];
|
||||
prior_rgb[2] = pika_cmap[index * 3 + 2];
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Determines which RGB component to modify */
|
||||
gint modify = control_index[control - 1];
|
||||
|
||||
if (nPlanes < 7)
|
||||
prior_rgb[modify] = color;
|
||||
else
|
||||
prior_rgb[modify] = (color << 2) + (prior_rgb[modify] & 3);
|
||||
}
|
||||
|
||||
pixel_row[current_index * 3] = prior_rgb[0];
|
||||
pixel_row[current_index * 3 + 1] = prior_rgb[1];
|
||||
pixel_row[current_index * 3 + 2] = prior_rgb[2];
|
||||
|
||||
current_index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
deleave_rgb_row (IFF_UByte *bitplanes,
|
||||
guchar *pixel_row,
|
||||
gint width,
|
||||
gint nPlanes,
|
||||
gint pixel_size)
|
||||
{
|
||||
gint row_length = ((width + 15) / 16) * 2;
|
||||
gint current_pixel = 0;
|
||||
|
||||
/* Initialize index array */
|
||||
for (gint i = 0; i < (width * pixel_size); i++)
|
||||
pixel_row[i] = 0;
|
||||
|
||||
/* Deleave rows */
|
||||
for (gint i = 0; i < row_length; i++)
|
||||
{
|
||||
for (gint j = 0; j < 8; j++)
|
||||
{
|
||||
guint8 bitmask = (1 << (8 - j)) - (1 << (7 - j));
|
||||
|
||||
for (gint k = 0; k < pixel_size; k++)
|
||||
{
|
||||
for (gint l = 0; l < 8; l++)
|
||||
{
|
||||
guint8 update = (1 << (l + 1)) - (1 << (l));
|
||||
|
||||
if (bitplanes[i + (row_length * (l + k * 8))] & bitmask)
|
||||
pixel_row[current_pixel] += update;
|
||||
}
|
||||
current_pixel++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
pbm_row (IFF_UByte *bitplanes,
|
||||
guchar *pixel_row,
|
||||
gint width)
|
||||
{
|
||||
for (gint i = 0; i < width; i++)
|
||||
pixel_row[i] = bitplanes[i];
|
||||
}
|
||||
1375
plug-ins/common/file-jp2-load.c
Normal file
1375
plug-ins/common/file-jp2-load.c
Normal file
File diff suppressed because it is too large
Load Diff
2199
plug-ins/common/file-jpegxl.c
Normal file
2199
plug-ins/common/file-jpegxl.c
Normal file
File diff suppressed because it is too large
Load Diff
1689
plug-ins/common/file-mng.c
Normal file
1689
plug-ins/common/file-mng.c
Normal file
File diff suppressed because it is too large
Load Diff
292
plug-ins/common/file-pat.c
Normal file
292
plug-ins/common/file-pat.c
Normal file
@ -0,0 +1,292 @@
|
||||
/*
|
||||
* pat plug-in version 1.01
|
||||
* Loads/exports version 1 PIKA .pat files, by Tim Newsome <drz@frody.bloke.com>
|
||||
*
|
||||
* PIKA - Photo and Image Kooker Application
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <libpika/pika.h>
|
||||
#include <libpika/pikaui.h>
|
||||
|
||||
#include "libpika/stdplugins-intl.h"
|
||||
|
||||
|
||||
#define SAVE_PROC "file-pat-save"
|
||||
#define PLUG_IN_BINARY "file-pat"
|
||||
|
||||
|
||||
typedef struct _Pat Pat;
|
||||
typedef struct _PatClass PatClass;
|
||||
|
||||
struct _Pat
|
||||
{
|
||||
PikaPlugIn parent_instance;
|
||||
};
|
||||
|
||||
struct _PatClass
|
||||
{
|
||||
PikaPlugInClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
#define PAT_TYPE (pat_get_type ())
|
||||
#define PAT (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PAT_TYPE, Pat))
|
||||
|
||||
GType pat_get_type (void) G_GNUC_CONST;
|
||||
|
||||
static GList * pat_query_procedures (PikaPlugIn *plug_in);
|
||||
static PikaProcedure * pat_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name);
|
||||
|
||||
static PikaValueArray * pat_save (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
GFile *file,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data);
|
||||
|
||||
static gboolean save_dialog (PikaImage *image,
|
||||
PikaProcedure *procedure,
|
||||
GObject *config);
|
||||
|
||||
|
||||
G_DEFINE_TYPE (Pat, pat, PIKA_TYPE_PLUG_IN)
|
||||
|
||||
PIKA_MAIN (PAT_TYPE)
|
||||
DEFINE_STD_SET_I18N
|
||||
|
||||
|
||||
static void
|
||||
pat_class_init (PatClass *klass)
|
||||
{
|
||||
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
|
||||
|
||||
plug_in_class->query_procedures = pat_query_procedures;
|
||||
plug_in_class->create_procedure = pat_create_procedure;
|
||||
plug_in_class->set_i18n = STD_SET_I18N;
|
||||
}
|
||||
|
||||
static void
|
||||
pat_init (Pat *pat)
|
||||
{
|
||||
}
|
||||
|
||||
static GList *
|
||||
pat_query_procedures (PikaPlugIn *plug_in)
|
||||
{
|
||||
return g_list_append (NULL, g_strdup (SAVE_PROC));
|
||||
}
|
||||
|
||||
static PikaProcedure *
|
||||
pat_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name)
|
||||
{
|
||||
PikaProcedure *procedure = NULL;
|
||||
|
||||
if (! strcmp (name, SAVE_PROC))
|
||||
{
|
||||
procedure = pika_save_procedure_new (plug_in, name,
|
||||
PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
pat_save, NULL, NULL);
|
||||
|
||||
pika_procedure_set_image_types (procedure, "*");
|
||||
|
||||
pika_procedure_set_menu_label (procedure, _("PIKA pattern"));
|
||||
pika_procedure_set_icon_name (procedure, PIKA_ICON_PATTERN);
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
_("Exports PIKA pattern file (.PAT)"),
|
||||
_("New PIKA patterns can be created "
|
||||
"by exporting them in the "
|
||||
"appropriate place with this plug-in."),
|
||||
SAVE_PROC);
|
||||
pika_procedure_set_attribution (procedure,
|
||||
"Tim Newsome",
|
||||
"Tim Newsome",
|
||||
"1997");
|
||||
|
||||
pika_file_procedure_set_format_name (PIKA_FILE_PROCEDURE (procedure),
|
||||
_("Pattern"));
|
||||
pika_file_procedure_set_mime_types (PIKA_FILE_PROCEDURE (procedure),
|
||||
"image/x-pika-pat");
|
||||
pika_file_procedure_set_extensions (PIKA_FILE_PROCEDURE (procedure),
|
||||
"pat");
|
||||
pika_file_procedure_set_handles_remote (PIKA_FILE_PROCEDURE (procedure),
|
||||
TRUE);
|
||||
|
||||
PIKA_PROC_ARG_STRING (procedure, "description",
|
||||
_("_Description"),
|
||||
_("Short description of the pattern"),
|
||||
_("PIKA Pattern"),
|
||||
PIKA_PARAM_READWRITE);
|
||||
}
|
||||
|
||||
return procedure;
|
||||
}
|
||||
|
||||
static PikaValueArray *
|
||||
pat_save (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
GFile *file,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data)
|
||||
{
|
||||
PikaProcedureConfig *config;
|
||||
PikaPDBStatusType status = PIKA_PDB_SUCCESS;
|
||||
PikaExportReturn export = PIKA_EXPORT_CANCEL;
|
||||
gchar *description;
|
||||
GError *error = NULL;
|
||||
|
||||
config = pika_procedure_create_config (procedure);
|
||||
pika_procedure_config_begin_run (config, image, run_mode, args);
|
||||
|
||||
g_object_get (config,
|
||||
"description", &description,
|
||||
NULL);
|
||||
|
||||
if (! description || ! strlen (description))
|
||||
{
|
||||
gchar *name = g_path_get_basename (pika_file_get_utf8_name (file));
|
||||
|
||||
if (g_str_has_suffix (name, ".pat"))
|
||||
name[strlen (name) - 4] = '\0';
|
||||
|
||||
if (strlen (name))
|
||||
g_object_set (config,
|
||||
"description", name,
|
||||
NULL);
|
||||
|
||||
g_free (name);
|
||||
}
|
||||
|
||||
g_free (description);
|
||||
|
||||
switch (run_mode)
|
||||
{
|
||||
case PIKA_RUN_INTERACTIVE:
|
||||
case PIKA_RUN_WITH_LAST_VALS:
|
||||
pika_ui_init (PLUG_IN_BINARY);
|
||||
|
||||
export = pika_export_image (&image, &n_drawables, &drawables, "PAT",
|
||||
PIKA_EXPORT_CAN_HANDLE_GRAY |
|
||||
PIKA_EXPORT_CAN_HANDLE_RGB |
|
||||
PIKA_EXPORT_CAN_HANDLE_INDEXED |
|
||||
PIKA_EXPORT_CAN_HANDLE_ALPHA);
|
||||
|
||||
if (export == PIKA_EXPORT_CANCEL)
|
||||
return pika_procedure_new_return_values (procedure, PIKA_PDB_CANCEL,
|
||||
NULL);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (run_mode == PIKA_RUN_INTERACTIVE)
|
||||
{
|
||||
if (! save_dialog (image, procedure, G_OBJECT (config)))
|
||||
status = PIKA_PDB_CANCEL;
|
||||
}
|
||||
|
||||
if (status == PIKA_PDB_SUCCESS)
|
||||
{
|
||||
PikaValueArray *save_retvals;
|
||||
PikaValueArray *args;
|
||||
|
||||
g_object_get (config,
|
||||
"description", &description,
|
||||
NULL);
|
||||
|
||||
args = pika_value_array_new_from_types (NULL,
|
||||
PIKA_TYPE_RUN_MODE, PIKA_RUN_NONINTERACTIVE,
|
||||
PIKA_TYPE_IMAGE, image,
|
||||
G_TYPE_INT, n_drawables,
|
||||
PIKA_TYPE_OBJECT_ARRAY, NULL,
|
||||
G_TYPE_FILE, file,
|
||||
G_TYPE_STRING, description,
|
||||
G_TYPE_NONE);
|
||||
pika_value_set_object_array (pika_value_array_index (args, 3),
|
||||
PIKA_TYPE_ITEM, (GObject **) drawables, n_drawables);
|
||||
|
||||
save_retvals = pika_pdb_run_procedure_array (pika_get_pdb (),
|
||||
"file-pat-save-internal",
|
||||
args);
|
||||
pika_value_array_unref (args);
|
||||
|
||||
if (PIKA_VALUES_GET_ENUM (save_retvals, 0) != PIKA_PDB_SUCCESS)
|
||||
{
|
||||
g_set_error (&error, 0, 0,
|
||||
"Running procedure 'file-pat-save-internal' "
|
||||
"failed: %s",
|
||||
pika_pdb_get_last_error (pika_get_pdb ()));
|
||||
|
||||
status = PIKA_PDB_EXECUTION_ERROR;
|
||||
}
|
||||
|
||||
pika_value_array_unref (save_retvals);
|
||||
}
|
||||
|
||||
pika_procedure_config_end_run (config, status);
|
||||
g_object_unref (config);
|
||||
|
||||
if (export == PIKA_EXPORT_EXPORT)
|
||||
{
|
||||
pika_image_delete (image);
|
||||
g_free (drawables);
|
||||
}
|
||||
|
||||
return pika_procedure_new_return_values (procedure, status, error);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
save_dialog (PikaImage *image,
|
||||
PikaProcedure *procedure,
|
||||
GObject *config)
|
||||
{
|
||||
GtkWidget *dialog;
|
||||
GtkWidget *entry;
|
||||
GtkWidget *real_entry;
|
||||
gboolean run;
|
||||
|
||||
dialog = pika_save_procedure_dialog_new (PIKA_SAVE_PROCEDURE (procedure),
|
||||
PIKA_PROCEDURE_CONFIG (config),
|
||||
image);
|
||||
|
||||
entry = pika_procedure_dialog_get_widget (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"description", PIKA_TYPE_LABEL_ENTRY);
|
||||
real_entry = pika_label_entry_get_entry (PIKA_LABEL_ENTRY (entry));
|
||||
gtk_entry_set_max_length (GTK_ENTRY (real_entry), 256);
|
||||
gtk_entry_set_width_chars (GTK_ENTRY (real_entry), 20);
|
||||
gtk_entry_set_activates_default (GTK_ENTRY (real_entry), TRUE);
|
||||
|
||||
pika_procedure_dialog_fill (PIKA_PROCEDURE_DIALOG (dialog), NULL);
|
||||
|
||||
gtk_widget_show (dialog);
|
||||
|
||||
run = pika_procedure_dialog_run (PIKA_PROCEDURE_DIALOG (dialog));
|
||||
|
||||
gtk_widget_destroy (dialog);
|
||||
|
||||
return run;
|
||||
}
|
||||
1426
plug-ins/common/file-pcx.c
Normal file
1426
plug-ins/common/file-pcx.c
Normal file
File diff suppressed because it is too large
Load Diff
1847
plug-ins/common/file-pdf-load.c
Normal file
1847
plug-ins/common/file-pdf-load.c
Normal file
File diff suppressed because it is too large
Load Diff
2015
plug-ins/common/file-pdf-save.c
Normal file
2015
plug-ins/common/file-pdf-save.c
Normal file
File diff suppressed because it is too large
Load Diff
762
plug-ins/common/file-pix.c
Normal file
762
plug-ins/common/file-pix.c
Normal file
@ -0,0 +1,762 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
* Alias|Wavefront pix/matte image reading and writing code
|
||||
* Copyright (C) 1997 Mike Taylor
|
||||
* (email: mtaylor@aw.sgi.com, WWW: http://reality.sgi.com/mtaylor)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/* This plug-in was written using the online documentation from
|
||||
* Alias|Wavefront Inc's PowerAnimator product.
|
||||
*
|
||||
* Bug reports or suggestions should be e-mailed to mtaylor@aw.sgi.com
|
||||
*/
|
||||
|
||||
/* Event history:
|
||||
* V 1.0, MT, 02-Jul-97: initial version of plug-in
|
||||
* V 1.1, MT, 04-Dec-97: added .als file extension
|
||||
*/
|
||||
|
||||
/* Features
|
||||
* - loads and exports
|
||||
* - 24-bit (.pix)
|
||||
* - 8-bit (.matte, .alpha, or .mask) images
|
||||
*
|
||||
* NOTE: pix and matte files do not support alpha channels or indexed
|
||||
* color, so neither does this plug-in
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <glib/gstdio.h>
|
||||
|
||||
#include <libpika/pika.h>
|
||||
#include <libpika/pikaui.h>
|
||||
|
||||
#include "libpika/stdplugins-intl.h"
|
||||
|
||||
|
||||
#define LOAD_PROC "file-pix-load"
|
||||
#define SAVE_PROC "file-pix-save"
|
||||
#define PLUG_IN_BINARY "file-pix"
|
||||
#define PLUG_IN_ROLE "pika-file-pix"
|
||||
|
||||
|
||||
/* #define PIX_DEBUG */
|
||||
|
||||
#ifdef PIX_DEBUG
|
||||
# define PIX_DEBUG_PRINT(a,b) g_printerr (a,b)
|
||||
#else
|
||||
# define PIX_DEBUG_PRINT(a,b)
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct _Pix Pix;
|
||||
typedef struct _PixClass PixClass;
|
||||
|
||||
struct _Pix
|
||||
{
|
||||
PikaPlugIn parent_instance;
|
||||
};
|
||||
|
||||
struct _PixClass
|
||||
{
|
||||
PikaPlugInClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
#define PIX_TYPE (pix_get_type ())
|
||||
#define PIX (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIX_TYPE, Pix))
|
||||
|
||||
GType pix_get_type (void) G_GNUC_CONST;
|
||||
|
||||
static GList * pix_query_procedures (PikaPlugIn *plug_in);
|
||||
static PikaProcedure * pix_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name);
|
||||
|
||||
static PikaValueArray * pix_load (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
GFile *file,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data);
|
||||
static PikaValueArray * pix_save (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
GFile *file,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data);
|
||||
|
||||
static PikaImage * load_image (GFile *file,
|
||||
GError **error);
|
||||
static gboolean save_image (GFile *file,
|
||||
PikaImage *image,
|
||||
PikaDrawable *drawable,
|
||||
GError **error);
|
||||
|
||||
static gboolean get_short (GInputStream *input,
|
||||
guint16 *value,
|
||||
GError **error);
|
||||
static gboolean put_short (GOutputStream *output,
|
||||
guint16 value,
|
||||
GError **error);
|
||||
|
||||
|
||||
G_DEFINE_TYPE (Pix, pix, PIKA_TYPE_PLUG_IN)
|
||||
|
||||
PIKA_MAIN (PIX_TYPE)
|
||||
DEFINE_STD_SET_I18N
|
||||
|
||||
|
||||
static void
|
||||
pix_class_init (PixClass *klass)
|
||||
{
|
||||
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
|
||||
|
||||
plug_in_class->query_procedures = pix_query_procedures;
|
||||
plug_in_class->create_procedure = pix_create_procedure;
|
||||
plug_in_class->set_i18n = STD_SET_I18N;
|
||||
}
|
||||
|
||||
static void
|
||||
pix_init (Pix *pix)
|
||||
{
|
||||
}
|
||||
|
||||
static GList *
|
||||
pix_query_procedures (PikaPlugIn *plug_in)
|
||||
{
|
||||
GList *list = NULL;
|
||||
|
||||
list = g_list_append (list, g_strdup (LOAD_PROC));
|
||||
list = g_list_append (list, g_strdup (SAVE_PROC));
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
static PikaProcedure *
|
||||
pix_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name)
|
||||
{
|
||||
PikaProcedure *procedure = NULL;
|
||||
|
||||
if (! strcmp (name, LOAD_PROC))
|
||||
{
|
||||
procedure = pika_load_procedure_new (plug_in, name,
|
||||
PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
pix_load, NULL, NULL);
|
||||
|
||||
pika_file_procedure_set_handles_remote (PIKA_FILE_PROCEDURE (procedure),
|
||||
TRUE);
|
||||
|
||||
pika_procedure_set_menu_label (procedure, _("Alias Pix image"));
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
"Loads files of the Alias|Wavefront "
|
||||
"Pix file format",
|
||||
"Loads files of the Alias|Wavefront "
|
||||
"Pix file format",
|
||||
name);
|
||||
pika_procedure_set_attribution (procedure,
|
||||
"Michael Taylor",
|
||||
"Michael Taylor",
|
||||
"1997");
|
||||
|
||||
pika_file_procedure_set_extensions (PIKA_FILE_PROCEDURE (procedure),
|
||||
"pix,matte,mask,alpha,als");
|
||||
}
|
||||
else if (! strcmp (name, SAVE_PROC))
|
||||
{
|
||||
procedure = pika_save_procedure_new (plug_in, name,
|
||||
PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
pix_save, NULL, NULL);
|
||||
|
||||
pika_procedure_set_image_types (procedure, "*");
|
||||
|
||||
pika_file_procedure_set_handles_remote (PIKA_FILE_PROCEDURE (procedure),
|
||||
TRUE);
|
||||
|
||||
pika_procedure_set_menu_label (procedure, _("Alias Pix image"));
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
"Export file in the Alias|Wavefront "
|
||||
"pix/matte file format",
|
||||
"Export file in the Alias|Wavefront "
|
||||
"pix/matte file format",
|
||||
name);
|
||||
pika_procedure_set_attribution (procedure,
|
||||
"Michael Taylor",
|
||||
"Michael Taylor",
|
||||
"1997");
|
||||
|
||||
pika_file_procedure_set_extensions (PIKA_FILE_PROCEDURE (procedure),
|
||||
"pix,matte,mask,alpha,als");
|
||||
}
|
||||
|
||||
return procedure;
|
||||
}
|
||||
|
||||
static PikaValueArray *
|
||||
pix_load (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
GFile *file,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data)
|
||||
{
|
||||
PikaValueArray *return_vals;
|
||||
PikaImage *image;
|
||||
GError *error = NULL;
|
||||
|
||||
gegl_init (NULL, NULL);
|
||||
|
||||
image = load_image (file, &error);
|
||||
|
||||
if (! image)
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_EXECUTION_ERROR,
|
||||
error);
|
||||
|
||||
return_vals = pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_SUCCESS,
|
||||
NULL);
|
||||
|
||||
PIKA_VALUES_SET_IMAGE (return_vals, 1, image);
|
||||
|
||||
return return_vals;
|
||||
}
|
||||
|
||||
static PikaValueArray *
|
||||
pix_save (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
GFile *file,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data)
|
||||
{
|
||||
PikaPDBStatusType status = PIKA_PDB_SUCCESS;
|
||||
PikaExportReturn export = PIKA_EXPORT_CANCEL;
|
||||
GError *error = NULL;
|
||||
|
||||
gegl_init (NULL, NULL);
|
||||
|
||||
switch (run_mode)
|
||||
{
|
||||
case PIKA_RUN_INTERACTIVE:
|
||||
case PIKA_RUN_WITH_LAST_VALS:
|
||||
pika_ui_init (PLUG_IN_BINARY);
|
||||
|
||||
export = pika_export_image (&image, &n_drawables, &drawables, "PIX",
|
||||
PIKA_EXPORT_CAN_HANDLE_RGB |
|
||||
PIKA_EXPORT_CAN_HANDLE_GRAY |
|
||||
PIKA_EXPORT_CAN_HANDLE_INDEXED);
|
||||
|
||||
if (export == PIKA_EXPORT_CANCEL)
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_CANCEL,
|
||||
NULL);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (n_drawables != 1)
|
||||
{
|
||||
g_set_error (&error, G_FILE_ERROR, 0,
|
||||
_("PIX format does not support multiple layers."));
|
||||
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_CALLING_ERROR,
|
||||
error);
|
||||
}
|
||||
|
||||
if (! save_image (file, image, drawables[0], &error))
|
||||
{
|
||||
status = PIKA_PDB_EXECUTION_ERROR;
|
||||
}
|
||||
|
||||
if (export == PIKA_EXPORT_EXPORT)
|
||||
{
|
||||
pika_image_delete (image);
|
||||
g_free (drawables);
|
||||
}
|
||||
|
||||
return pika_procedure_new_return_values (procedure, status, error);
|
||||
}
|
||||
|
||||
/*
|
||||
* Description:
|
||||
* Reads a 16-bit integer from a file in such a way that the machine's
|
||||
* byte order should not matter.
|
||||
*/
|
||||
|
||||
static gboolean
|
||||
get_short (GInputStream *input,
|
||||
guint16 *value,
|
||||
GError **error)
|
||||
{
|
||||
guchar buf[2];
|
||||
gsize bytes_read;
|
||||
|
||||
if (! g_input_stream_read_all (input, buf, 2,
|
||||
&bytes_read, NULL, error) ||
|
||||
bytes_read != 2)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (value)
|
||||
*value = (buf[0] << 8) + buf[1];
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Description:
|
||||
* Writes a 16-bit integer to a file in such a way that the machine's
|
||||
* byte order should not matter.
|
||||
*/
|
||||
|
||||
static gboolean
|
||||
put_short (GOutputStream *output,
|
||||
guint16 value,
|
||||
GError **error)
|
||||
{
|
||||
guchar buf[2];
|
||||
|
||||
buf[0] = (value >> 8) & 0xFF;
|
||||
buf[1] = value & 0xFF;
|
||||
|
||||
return g_output_stream_write_all (output, buf, 2, NULL, NULL, error);
|
||||
}
|
||||
|
||||
/*
|
||||
* Description:
|
||||
* load the given image into pika
|
||||
*
|
||||
* Arguments:
|
||||
* filename - name on the file to read
|
||||
*
|
||||
* Return Value:
|
||||
* Image id for the loaded image
|
||||
*
|
||||
*/
|
||||
|
||||
static PikaImage *
|
||||
load_image (GFile *file,
|
||||
GError **error)
|
||||
{
|
||||
GInputStream *input;
|
||||
GeglBuffer *buffer;
|
||||
PikaImageBaseType imgtype;
|
||||
PikaImageType gdtype;
|
||||
guchar *dest;
|
||||
guchar *dest_base;
|
||||
PikaImage *image;
|
||||
PikaLayer *layer;
|
||||
gushort width, height, depth;
|
||||
gint i, j, tile_height, row;
|
||||
|
||||
PIX_DEBUG_PRINT ("Opening file: %s\n", pika_file_get_utf8_name (file));
|
||||
|
||||
pika_progress_init_printf (_("Opening '%s'"),
|
||||
g_file_get_parse_name (file));
|
||||
|
||||
input = G_INPUT_STREAM (g_file_read (file, NULL, error));
|
||||
if (! input)
|
||||
return NULL;
|
||||
|
||||
/* Read header information */
|
||||
if (! get_short (input, &width, error) ||
|
||||
! get_short (input, &height, error) ||
|
||||
! get_short (input, NULL, error) || /* Discard obsolete field */
|
||||
! get_short (input, NULL, error) || /* Discard obsolete field */
|
||||
! get_short (input, &depth, error))
|
||||
{
|
||||
g_object_unref (input);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PIX_DEBUG_PRINT ("Width %hu\n", width);
|
||||
PIX_DEBUG_PRINT ("Height %hu\n", height);
|
||||
|
||||
if (depth == 8)
|
||||
{
|
||||
/* Loading a matte file */
|
||||
imgtype = PIKA_GRAY;
|
||||
gdtype = PIKA_GRAY_IMAGE;
|
||||
}
|
||||
else if (depth == 24)
|
||||
{
|
||||
/* Loading an RGB file */
|
||||
imgtype = PIKA_RGB;
|
||||
gdtype = PIKA_RGB_IMAGE;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Header is invalid */
|
||||
g_object_unref (input);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
image = pika_image_new (width, height, imgtype);
|
||||
layer = pika_layer_new (image, _("Background"),
|
||||
width, height,
|
||||
gdtype,
|
||||
100,
|
||||
pika_image_get_default_new_layer_mode (image));
|
||||
pika_image_insert_layer (image, layer, NULL, 0);
|
||||
|
||||
buffer = pika_drawable_get_buffer (PIKA_DRAWABLE (layer));
|
||||
|
||||
tile_height = pika_tile_height ();
|
||||
|
||||
if (depth == 24)
|
||||
{
|
||||
/* Read a 24-bit Pix image */
|
||||
|
||||
dest_base = dest = g_new (guchar, 3 * width * tile_height);
|
||||
|
||||
for (i = 0; i < height;)
|
||||
{
|
||||
for (dest = dest_base, row = 0;
|
||||
row < tile_height && i < height;
|
||||
i++, row++)
|
||||
{
|
||||
guchar record[4];
|
||||
gsize bytes_read;
|
||||
guchar count;
|
||||
|
||||
/* Read a row of the image */
|
||||
j = 0;
|
||||
while (j < width)
|
||||
{
|
||||
if (! g_input_stream_read_all (input, record, 4,
|
||||
&bytes_read, NULL, error) ||
|
||||
bytes_read != 4)
|
||||
break;
|
||||
|
||||
for (count = 0; count < record[0]; ++count)
|
||||
{
|
||||
dest[0] = record[3];
|
||||
dest[1] = record[2];
|
||||
dest[2] = record[1];
|
||||
dest += 3;
|
||||
j++;
|
||||
if (j >= width)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gegl_buffer_set (buffer, GEGL_RECTANGLE (0, i - row, width, row), 0,
|
||||
NULL, dest_base, GEGL_AUTO_ROWSTRIDE);
|
||||
|
||||
pika_progress_update ((double) i / (double) height);
|
||||
}
|
||||
|
||||
g_free (dest_base);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Read an 8-bit Matte image */
|
||||
|
||||
dest_base = dest = g_new (guchar, width * tile_height);
|
||||
|
||||
for (i = 0; i < height;)
|
||||
{
|
||||
for (dest = dest_base, row = 0;
|
||||
row < tile_height && i < height;
|
||||
i++, row++)
|
||||
{
|
||||
guchar record[2];
|
||||
gsize bytes_read;
|
||||
guchar count;
|
||||
|
||||
/* Read a row of the image */
|
||||
j = 0;
|
||||
while (j < width)
|
||||
{
|
||||
if (! g_input_stream_read_all (input, record, 2,
|
||||
&bytes_read, NULL, error) ||
|
||||
bytes_read != 2)
|
||||
break;
|
||||
|
||||
for (count = 0; count < record[0]; ++count)
|
||||
{
|
||||
dest[j] = record[1];
|
||||
j++;
|
||||
if (j >= width)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
dest += width;
|
||||
}
|
||||
|
||||
gegl_buffer_set (buffer, GEGL_RECTANGLE (0, i - row, width, row), 0,
|
||||
NULL, dest_base, GEGL_AUTO_ROWSTRIDE);
|
||||
|
||||
pika_progress_update ((double) i / (double) height);
|
||||
}
|
||||
|
||||
g_free (dest_base);
|
||||
}
|
||||
|
||||
g_object_unref (buffer);
|
||||
g_object_unref (input);
|
||||
|
||||
pika_progress_update (1.0);
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
/*
|
||||
* Description:
|
||||
* save the given file out as an alias pix or matte file
|
||||
*
|
||||
* Arguments:
|
||||
* filename - name of file to save to
|
||||
* image - image to save
|
||||
* drawable - current drawable
|
||||
*/
|
||||
|
||||
static gboolean
|
||||
save_image (GFile *file,
|
||||
PikaImage *image,
|
||||
PikaDrawable *drawable,
|
||||
GError **error)
|
||||
{
|
||||
GOutputStream *output;
|
||||
GeglBuffer *buffer;
|
||||
const Babl *format;
|
||||
GCancellable *cancellable;
|
||||
gint width;
|
||||
gint height;
|
||||
gint depth, i, j, row, tile_height, rectHeight;
|
||||
gboolean savingColor = TRUE;
|
||||
guchar *src;
|
||||
guchar *src_base;
|
||||
|
||||
pika_progress_init_printf (_("Exporting '%s'"),
|
||||
g_file_get_parse_name (file));
|
||||
|
||||
output = G_OUTPUT_STREAM (g_file_replace (file,
|
||||
NULL, FALSE, G_FILE_CREATE_NONE,
|
||||
NULL, error));
|
||||
if (! output)
|
||||
return FALSE;
|
||||
|
||||
/* Get info about image */
|
||||
buffer = pika_drawable_get_buffer (drawable);
|
||||
|
||||
width = gegl_buffer_get_width (buffer);
|
||||
height = gegl_buffer_get_height (buffer);
|
||||
|
||||
savingColor = ! pika_drawable_is_gray (drawable);
|
||||
|
||||
if (savingColor)
|
||||
format = babl_format ("R'G'B' u8");
|
||||
else
|
||||
format = babl_format ("Y' u8");
|
||||
|
||||
depth = babl_format_get_bytes_per_pixel (format);
|
||||
|
||||
/* Write the image header */
|
||||
PIX_DEBUG_PRINT ("Width %hu\n", width);
|
||||
PIX_DEBUG_PRINT ("Height %hu\n", height);
|
||||
|
||||
if (! put_short (output, width, error) ||
|
||||
! put_short (output, height, error) ||
|
||||
! put_short (output, 0, error) ||
|
||||
! put_short (output, 0, error))
|
||||
{
|
||||
cancellable = g_cancellable_new ();
|
||||
g_cancellable_cancel (cancellable);
|
||||
g_output_stream_close (output, cancellable, NULL);
|
||||
g_object_unref (cancellable);
|
||||
|
||||
g_object_unref (output);
|
||||
g_object_unref (buffer);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
tile_height = pika_tile_height ();
|
||||
src_base = g_new (guchar, tile_height * width * depth);
|
||||
|
||||
if (savingColor)
|
||||
{
|
||||
/* Writing a 24-bit Pix image */
|
||||
|
||||
if (! put_short (output, 24, error))
|
||||
goto fail;
|
||||
|
||||
for (i = 0; i < height;)
|
||||
{
|
||||
rectHeight = (tile_height < (height - i)) ?
|
||||
tile_height : (height - i);
|
||||
|
||||
gegl_buffer_get (buffer, GEGL_RECTANGLE (0, i, width, rectHeight), 1.0,
|
||||
format, src_base,
|
||||
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
|
||||
|
||||
for (src = src_base, row = 0;
|
||||
row < tile_height && i < height;
|
||||
i += 1, row += 1)
|
||||
{
|
||||
/* Write a row of the image */
|
||||
|
||||
guchar record[4];
|
||||
|
||||
record[0] = 1;
|
||||
record[3] = src[0];
|
||||
record[2] = src[1];
|
||||
record[1] = src[2];
|
||||
src += depth;
|
||||
for (j = 1; j < width; ++j)
|
||||
{
|
||||
if ((record[3] != src[0]) ||
|
||||
(record[2] != src[1]) ||
|
||||
(record[1] != src[2]) ||
|
||||
(record[0] == 255))
|
||||
{
|
||||
/* Write current RLE record and start a new one */
|
||||
|
||||
if (! g_output_stream_write_all (output, record, 4,
|
||||
NULL, NULL, error))
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
|
||||
record[0] = 1;
|
||||
record[3] = src[0];
|
||||
record[2] = src[1];
|
||||
record[1] = src[2];
|
||||
}
|
||||
else
|
||||
{
|
||||
/* increment run length in current record */
|
||||
record[0]++;
|
||||
}
|
||||
src += depth;
|
||||
}
|
||||
|
||||
/* Write last record in row */
|
||||
|
||||
if (! g_output_stream_write_all (output, record, 4,
|
||||
NULL, NULL, error))
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
pika_progress_update ((double) i / (double) height);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Writing a 8-bit Matte (Mask) image */
|
||||
|
||||
if (! put_short (output, 8, error))
|
||||
goto fail;
|
||||
|
||||
for (i = 0; i < height;)
|
||||
{
|
||||
rectHeight = (tile_height < (height - i)) ?
|
||||
tile_height : (height - i);
|
||||
|
||||
gegl_buffer_get (buffer, GEGL_RECTANGLE (0, i, width, rectHeight), 1.0,
|
||||
format, src_base,
|
||||
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
|
||||
|
||||
for (src = src_base, row = 0;
|
||||
row < tile_height && i < height;
|
||||
i += 1, row += 1)
|
||||
{
|
||||
/* Write a row of the image */
|
||||
|
||||
guchar record[2];
|
||||
|
||||
record[0] = 1;
|
||||
record[1] = src[0];
|
||||
src += depth;
|
||||
for (j = 1; j < width; ++j)
|
||||
{
|
||||
if ((record[1] != src[0]) || (record[0] == 255))
|
||||
{
|
||||
/* Write current RLE record and start a new one */
|
||||
|
||||
if (! g_output_stream_write_all (output, record, 2,
|
||||
NULL, NULL, error))
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
|
||||
record[0] = 1;
|
||||
record[1] = src[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
/* increment run length in current record */
|
||||
record[0] ++;
|
||||
}
|
||||
src += depth;
|
||||
}
|
||||
|
||||
/* Write last record in row */
|
||||
|
||||
if (! g_output_stream_write_all (output, record, 2,
|
||||
NULL, NULL, error))
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
pika_progress_update ((double) i / (double) height);
|
||||
}
|
||||
}
|
||||
|
||||
g_free (src_base);
|
||||
g_object_unref (output);
|
||||
g_object_unref (buffer);
|
||||
|
||||
pika_progress_update (1.0);
|
||||
|
||||
return TRUE;
|
||||
|
||||
fail:
|
||||
|
||||
cancellable = g_cancellable_new ();
|
||||
g_cancellable_cancel (cancellable);
|
||||
g_output_stream_close (output, cancellable, NULL);
|
||||
g_object_unref (cancellable);
|
||||
|
||||
g_free (src_base);
|
||||
g_object_unref (output);
|
||||
g_object_unref (buffer);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
2336
plug-ins/common/file-png.c
Normal file
2336
plug-ins/common/file-png.c
Normal file
File diff suppressed because it is too large
Load Diff
2260
plug-ins/common/file-pnm.c
Normal file
2260
plug-ins/common/file-pnm.c
Normal file
File diff suppressed because it is too large
Load Diff
4037
plug-ins/common/file-ps.c
Normal file
4037
plug-ins/common/file-ps.c
Normal file
File diff suppressed because it is too large
Load Diff
2647
plug-ins/common/file-psp.c
Normal file
2647
plug-ins/common/file-psp.c
Normal file
File diff suppressed because it is too large
Load Diff
400
plug-ins/common/file-qoi.c
Normal file
400
plug-ins/common/file-qoi.c
Normal file
@ -0,0 +1,400 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* Quite OK Image (QOI) plug-in
|
||||
*
|
||||
* Copyright (C) 2023 Alex S.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <glib/gstdio.h>
|
||||
|
||||
#include <libpika/pika.h>
|
||||
#include <libpika/pikaui.h>
|
||||
|
||||
#define QOI_IMPLEMENTATION
|
||||
#include "qoi.h"
|
||||
|
||||
#include "libpika/stdplugins-intl.h"
|
||||
|
||||
|
||||
#define LOAD_PROC "file-qoi-load"
|
||||
#define SAVE_PROC "file-qoi-save"
|
||||
#define PLUG_IN_BINARY "file-qoi"
|
||||
#define PLUG_IN_ROLE "pika-file-qoi"
|
||||
|
||||
typedef struct _Qoi Qoi;
|
||||
typedef struct _QoiClass QoiClass;
|
||||
|
||||
struct _Qoi
|
||||
{
|
||||
PikaPlugIn parent_instance;
|
||||
};
|
||||
|
||||
struct _QoiClass
|
||||
{
|
||||
PikaPlugInClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
#define QOI_TYPE (qoi_get_type ())
|
||||
#define QOI (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), QOI_TYPE, Qoi))
|
||||
|
||||
GType qoi_get_type (void) G_GNUC_CONST;
|
||||
|
||||
|
||||
static GList * qoi_query_procedures (PikaPlugIn *plug_in);
|
||||
static PikaProcedure * qoi_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name);
|
||||
|
||||
static PikaValueArray * qoi_load (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
GFile *file,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data);
|
||||
static PikaValueArray * qoi_save (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
GFile *file,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data);
|
||||
|
||||
static PikaImage * load_image (GFile *file,
|
||||
GObject *config,
|
||||
PikaRunMode run_mode,
|
||||
GError **error);
|
||||
static gboolean save_image (GFile *file,
|
||||
PikaImage *image,
|
||||
PikaDrawable *drawable,
|
||||
GError **error);
|
||||
|
||||
|
||||
G_DEFINE_TYPE (Qoi, qoi, PIKA_TYPE_PLUG_IN)
|
||||
|
||||
PIKA_MAIN (QOI_TYPE)
|
||||
DEFINE_STD_SET_I18N
|
||||
|
||||
|
||||
static void
|
||||
qoi_class_init (QoiClass *klass)
|
||||
{
|
||||
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
|
||||
|
||||
plug_in_class->query_procedures = qoi_query_procedures;
|
||||
plug_in_class->create_procedure = qoi_create_procedure;
|
||||
plug_in_class->set_i18n = STD_SET_I18N;
|
||||
}
|
||||
|
||||
static void
|
||||
qoi_init (Qoi *qoi)
|
||||
{
|
||||
}
|
||||
|
||||
static GList *
|
||||
qoi_query_procedures (PikaPlugIn *plug_in)
|
||||
{
|
||||
GList *list = NULL;
|
||||
|
||||
list = g_list_append (list, g_strdup (LOAD_PROC));
|
||||
list = g_list_append (list, g_strdup (SAVE_PROC));
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
static PikaProcedure *
|
||||
qoi_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name)
|
||||
{
|
||||
PikaProcedure *procedure = NULL;
|
||||
|
||||
if (! strcmp (name, LOAD_PROC))
|
||||
{
|
||||
procedure = pika_load_procedure_new (plug_in, name,
|
||||
PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
qoi_load, NULL, NULL);
|
||||
|
||||
pika_procedure_set_menu_label (procedure,
|
||||
N_("Quite OK Image"));
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
_("Load file in the QOI file format"),
|
||||
_("Load file in the QOI file format "
|
||||
"(Quite OK Image)"),
|
||||
name);
|
||||
pika_procedure_set_attribution (procedure,
|
||||
"Alex S.",
|
||||
"Alex S.",
|
||||
"2023");
|
||||
|
||||
pika_file_procedure_set_mime_types (PIKA_FILE_PROCEDURE (procedure),
|
||||
"image/qoi");
|
||||
pika_file_procedure_set_extensions (PIKA_FILE_PROCEDURE (procedure),
|
||||
"qoi");
|
||||
pika_file_procedure_set_magics (PIKA_FILE_PROCEDURE (procedure),
|
||||
"0,string,qoif");
|
||||
}
|
||||
else if (! strcmp (name, SAVE_PROC))
|
||||
{
|
||||
procedure = pika_save_procedure_new (plug_in, name,
|
||||
PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
qoi_save, NULL, NULL);
|
||||
|
||||
pika_procedure_set_image_types (procedure, "*");
|
||||
|
||||
pika_procedure_set_menu_label (procedure, _("Quite OK Image"));
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
_("Export image in the QOI file format"),
|
||||
_("Export image in the QOI file format "
|
||||
"(Quite OK Image)"),
|
||||
name);
|
||||
pika_procedure_set_attribution (procedure,
|
||||
"Alex S.",
|
||||
"Alex S.",
|
||||
"2023");
|
||||
|
||||
pika_file_procedure_set_mime_types (PIKA_FILE_PROCEDURE (procedure),
|
||||
"image/qoi");
|
||||
pika_file_procedure_set_extensions (PIKA_FILE_PROCEDURE (procedure),
|
||||
"qoi");
|
||||
}
|
||||
|
||||
return procedure;
|
||||
}
|
||||
|
||||
static PikaValueArray *
|
||||
qoi_load (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
GFile *file,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data)
|
||||
{
|
||||
PikaProcedureConfig *config;
|
||||
PikaValueArray *return_vals;
|
||||
PikaImage *image;
|
||||
GError *error = NULL;
|
||||
|
||||
gegl_init (NULL, NULL);
|
||||
|
||||
config = pika_procedure_create_config (procedure);
|
||||
pika_procedure_config_begin_run (config, NULL, run_mode, args);
|
||||
|
||||
image = load_image (file, G_OBJECT (config), run_mode, &error);
|
||||
|
||||
if (! image)
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_EXECUTION_ERROR,
|
||||
error);
|
||||
|
||||
pika_procedure_config_end_run (config, PIKA_PDB_SUCCESS);
|
||||
g_object_unref (config);
|
||||
|
||||
return_vals = pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_SUCCESS,
|
||||
NULL);
|
||||
|
||||
PIKA_VALUES_SET_IMAGE (return_vals, 1, image);
|
||||
|
||||
return return_vals;
|
||||
}
|
||||
|
||||
static PikaValueArray *
|
||||
qoi_save (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
GFile *file,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data)
|
||||
{
|
||||
PikaPDBStatusType status = PIKA_PDB_SUCCESS;
|
||||
PikaExportReturn export = PIKA_EXPORT_CANCEL;
|
||||
GError *error = NULL;
|
||||
|
||||
gegl_init (NULL, NULL);
|
||||
|
||||
switch (run_mode)
|
||||
{
|
||||
case PIKA_RUN_INTERACTIVE:
|
||||
case PIKA_RUN_WITH_LAST_VALS:
|
||||
pika_ui_init (PLUG_IN_BINARY);
|
||||
|
||||
export = pika_export_image (&image, &n_drawables, &drawables, "qoi",
|
||||
PIKA_EXPORT_CAN_HANDLE_RGB |
|
||||
PIKA_EXPORT_CAN_HANDLE_GRAY |
|
||||
PIKA_EXPORT_CAN_HANDLE_INDEXED |
|
||||
PIKA_EXPORT_CAN_HANDLE_ALPHA);
|
||||
|
||||
if (export == PIKA_EXPORT_CANCEL)
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_CANCEL,
|
||||
NULL);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (n_drawables != 1)
|
||||
{
|
||||
g_set_error (&error, G_FILE_ERROR, 0,
|
||||
_("QOI format does not support multiple layers."));
|
||||
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_CALLING_ERROR,
|
||||
error);
|
||||
}
|
||||
|
||||
if (! save_image (file, image, drawables[0], &error))
|
||||
status = PIKA_PDB_EXECUTION_ERROR;
|
||||
|
||||
if (export == PIKA_EXPORT_EXPORT)
|
||||
{
|
||||
pika_image_delete (image);
|
||||
g_free (drawables);
|
||||
}
|
||||
|
||||
return pika_procedure_new_return_values (procedure, status, error);
|
||||
}
|
||||
|
||||
static PikaImage *
|
||||
load_image (GFile *file,
|
||||
GObject *config,
|
||||
PikaRunMode run_mode,
|
||||
GError **error)
|
||||
{
|
||||
PikaImage *image = NULL;
|
||||
PikaLayer *layer;
|
||||
GeglBuffer *buffer;
|
||||
qoi_desc desc;
|
||||
void *pixels = NULL;
|
||||
const Babl *format = NULL;
|
||||
FILE *fp;
|
||||
|
||||
fp = g_fopen (g_file_peek_path (file), "rb");
|
||||
|
||||
if (! fp)
|
||||
{
|
||||
g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
|
||||
_("Could not open '%s' for reading: %s"),
|
||||
pika_file_get_utf8_name (file), g_strerror (errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fclose (fp);
|
||||
|
||||
pixels = qoi_read (g_file_peek_path (file), &desc, 0);
|
||||
|
||||
if (! pixels)
|
||||
{
|
||||
g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
|
||||
_("Failed to read QOI file"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
image = pika_image_new_with_precision (desc.width, desc.height, PIKA_RGB,
|
||||
desc.colorspace ?
|
||||
PIKA_PRECISION_U8_LINEAR :
|
||||
PIKA_PRECISION_U8_NON_LINEAR);
|
||||
|
||||
layer = pika_layer_new (image, _("Background"), desc.width, desc.height,
|
||||
(desc.channels == 4) ? PIKA_RGBA_IMAGE : PIKA_RGB_IMAGE,
|
||||
100, pika_image_get_default_new_layer_mode (image));
|
||||
pika_image_insert_layer (image, layer, NULL, 0);
|
||||
|
||||
format = babl_format ((desc.channels == 4) ? "R'G'B'A u8" : "R'G'B' u8");
|
||||
|
||||
buffer = pika_drawable_get_buffer (PIKA_DRAWABLE (layer));
|
||||
|
||||
gegl_buffer_set (buffer, GEGL_RECTANGLE (0, 0, desc.width, desc.height), 0,
|
||||
format, pixels, GEGL_AUTO_ROWSTRIDE);
|
||||
|
||||
g_object_unref (buffer);
|
||||
g_free (pixels);
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
save_image (GFile *file,
|
||||
PikaImage *image,
|
||||
PikaDrawable *drawable,
|
||||
GError **error)
|
||||
{
|
||||
GeglBuffer *buffer;
|
||||
const Babl *format;
|
||||
qoi_desc desc;
|
||||
void *pixels = NULL;
|
||||
gboolean has_alpha;
|
||||
gint success;
|
||||
|
||||
has_alpha = pika_drawable_has_alpha (drawable);
|
||||
|
||||
buffer = pika_drawable_get_buffer (drawable);
|
||||
|
||||
desc.width = gegl_buffer_get_width (buffer);
|
||||
desc.height = gegl_buffer_get_height (buffer);
|
||||
desc.channels = has_alpha ? 4 : 3;
|
||||
switch (pika_image_get_precision (image))
|
||||
{
|
||||
case PIKA_PRECISION_U8_LINEAR:
|
||||
case PIKA_PRECISION_U16_LINEAR:
|
||||
case PIKA_PRECISION_U32_LINEAR:
|
||||
case PIKA_PRECISION_HALF_LINEAR:
|
||||
case PIKA_PRECISION_FLOAT_LINEAR:
|
||||
case PIKA_PRECISION_DOUBLE_LINEAR:
|
||||
desc.colorspace = QOI_LINEAR;
|
||||
break;
|
||||
|
||||
default:
|
||||
desc.colorspace = QOI_SRGB;
|
||||
break;
|
||||
}
|
||||
|
||||
pika_progress_init_printf (_("Exporting '%s'"),
|
||||
pika_file_get_utf8_name (file));
|
||||
|
||||
format = babl_format (has_alpha ? "R'G'B'A u8" : "R'G'B' u8");
|
||||
|
||||
pixels = (guchar *) g_malloc (desc.width * desc.height * desc.channels);
|
||||
|
||||
gegl_buffer_get (buffer, GEGL_RECTANGLE (0, 0, desc.width, desc.height),
|
||||
1.0, format, pixels,
|
||||
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
|
||||
|
||||
success = qoi_write (g_file_peek_path (file), pixels, &desc);
|
||||
|
||||
g_object_unref (buffer);
|
||||
g_free (pixels);
|
||||
|
||||
if (! success)
|
||||
{
|
||||
g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
|
||||
_("Writing to file '%s' failed: %s"),
|
||||
pika_file_get_utf8_name (file), g_strerror (errno));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
2959
plug-ins/common/file-raw-data.c
Normal file
2959
plug-ins/common/file-raw-data.c
Normal file
File diff suppressed because it is too large
Load Diff
1824
plug-ins/common/file-sunras.c
Normal file
1824
plug-ins/common/file-sunras.c
Normal file
File diff suppressed because it is too large
Load Diff
1083
plug-ins/common/file-svg.c
Normal file
1083
plug-ins/common/file-svg.c
Normal file
File diff suppressed because it is too large
Load Diff
1481
plug-ins/common/file-tga.c
Normal file
1481
plug-ins/common/file-tga.c
Normal file
File diff suppressed because it is too large
Load Diff
321
plug-ins/common/file-wbmp.c
Normal file
321
plug-ins/common/file-wbmp.c
Normal file
@ -0,0 +1,321 @@
|
||||
/*
|
||||
* PIKA - Photo and Image Kooker Application
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <glib/gstdio.h>
|
||||
|
||||
#include <libpika/pika.h>
|
||||
#include <libpika/pikaui.h>
|
||||
|
||||
#include "libpika/stdplugins-intl.h"
|
||||
|
||||
#define LOAD_PROC "file-wbmp-load"
|
||||
#define PLUG_IN_BINARY "file-wbmp"
|
||||
#define PLUG_IN_ROLE "pika-file-wbmp"
|
||||
|
||||
#define ReadOK(file,buffer,len) (fread(buffer, len, 1, file) != 0)
|
||||
|
||||
typedef struct _Wbmp Wbmp;
|
||||
typedef struct _WbmpClass WbmpClass;
|
||||
|
||||
struct _Wbmp
|
||||
{
|
||||
PikaPlugIn parent_instance;
|
||||
};
|
||||
|
||||
struct _WbmpClass
|
||||
{
|
||||
PikaPlugInClass parent_class;
|
||||
};
|
||||
|
||||
#define WBMP_TYPE (wbmp_get_type ())
|
||||
#define WBMP (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), WBMP_TYPE, Wbmp))
|
||||
|
||||
GType wbmp_get_type (void) G_GNUC_CONST;
|
||||
|
||||
static GList * wbmp_query_procedures (PikaPlugIn *plug_in);
|
||||
static PikaProcedure * wbmp_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name);
|
||||
|
||||
static PikaValueArray * wbmp_load (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
GFile *file,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data);
|
||||
|
||||
PikaImage * load_image (GFile *file,
|
||||
GError **error);
|
||||
|
||||
static PikaImage * read_image (FILE *fd,
|
||||
GFile *file,
|
||||
gint width,
|
||||
gint height,
|
||||
GError **error);
|
||||
|
||||
G_DEFINE_TYPE (Wbmp, wbmp, PIKA_TYPE_PLUG_IN)
|
||||
|
||||
PIKA_MAIN (WBMP_TYPE)
|
||||
DEFINE_STD_SET_I18N
|
||||
|
||||
static void
|
||||
wbmp_class_init (WbmpClass *klass)
|
||||
{
|
||||
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
|
||||
|
||||
plug_in_class->query_procedures = wbmp_query_procedures;
|
||||
plug_in_class->create_procedure = wbmp_create_procedure;
|
||||
plug_in_class->set_i18n = STD_SET_I18N;
|
||||
}
|
||||
|
||||
static void
|
||||
wbmp_init (Wbmp *wmp)
|
||||
{
|
||||
}
|
||||
|
||||
static GList *
|
||||
wbmp_query_procedures (PikaPlugIn *plug_in)
|
||||
{
|
||||
GList *list = NULL;
|
||||
|
||||
list = g_list_append (list, g_strdup (LOAD_PROC));
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
static PikaProcedure *
|
||||
wbmp_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name)
|
||||
{
|
||||
PikaProcedure *procedure = NULL;
|
||||
|
||||
if (! strcmp (name, LOAD_PROC))
|
||||
{
|
||||
procedure = pika_load_procedure_new (plug_in, name,
|
||||
PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
wbmp_load, NULL, NULL);
|
||||
|
||||
pika_procedure_set_menu_label (procedure, _("Wireless BMP image"));
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
_("Loads files of Wireless BMP file format"),
|
||||
_("Loads files of Wireless BMP file format"),
|
||||
name);
|
||||
pika_procedure_set_attribution (procedure,
|
||||
"Kevin Toyle",
|
||||
"Kevin Toyle",
|
||||
"2022");
|
||||
|
||||
pika_file_procedure_set_mime_types (PIKA_FILE_PROCEDURE (procedure),
|
||||
"image/vnd.wap.wbmp");
|
||||
pika_file_procedure_set_extensions (PIKA_FILE_PROCEDURE (procedure),
|
||||
"wbmp");
|
||||
}
|
||||
|
||||
return procedure;
|
||||
}
|
||||
|
||||
static PikaValueArray *
|
||||
wbmp_load (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
GFile *file,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data)
|
||||
{
|
||||
PikaValueArray *return_vals;
|
||||
PikaImage *image;
|
||||
GError *error = NULL;
|
||||
|
||||
gegl_init (NULL, NULL);
|
||||
|
||||
image = load_image (file, &error);
|
||||
|
||||
if (! image)
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_EXECUTION_ERROR,
|
||||
error);
|
||||
|
||||
return_vals = pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_SUCCESS,
|
||||
NULL);
|
||||
|
||||
PIKA_VALUES_SET_IMAGE (return_vals, 1, image);
|
||||
|
||||
return return_vals;
|
||||
}
|
||||
|
||||
PikaImage *
|
||||
load_image (GFile *file,
|
||||
GError **error)
|
||||
{
|
||||
FILE *fd;
|
||||
PikaImage *image = NULL;
|
||||
gint width = 0;
|
||||
gint height = 0;
|
||||
gint8 magic;
|
||||
guchar value;
|
||||
|
||||
pika_progress_init_printf (_("Opening '%s'"),
|
||||
pika_file_get_utf8_name (file));
|
||||
|
||||
fd = g_fopen (g_file_peek_path (file), "rb");
|
||||
|
||||
if (! fd)
|
||||
{
|
||||
g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
|
||||
_("Could not open '%s' for reading: %s"),
|
||||
pika_file_get_utf8_name (file), g_strerror (errno));
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Checking the type field to make sure it is 0. */
|
||||
if (! ReadOK (fd, &magic, 1) || magic != 0)
|
||||
{
|
||||
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
||||
_("'%s': Invalid WBMP type value"),
|
||||
pika_file_get_utf8_name (file));
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Checking the fixed header field to make sure it is 0 */
|
||||
if (! ReadOK (fd, &magic, 1) || magic != 0)
|
||||
{
|
||||
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
||||
_("'%s': Unsupported WBMP fixed header value"),
|
||||
pika_file_get_utf8_name (file));
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* The width and height are stored as uintvar values */
|
||||
while (ReadOK (fd, &value, 1))
|
||||
{
|
||||
width = (width << 7) | (value & 0x7F);
|
||||
if (value >> 7 != 1)
|
||||
break;
|
||||
}
|
||||
while (ReadOK (fd, &value, 1))
|
||||
{
|
||||
height = (height << 7) | (value & 0x7F);
|
||||
if (value >> 7 != 1)
|
||||
break;
|
||||
}
|
||||
|
||||
if (width <= 0 || height <= 0)
|
||||
{
|
||||
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
||||
_("'%s' is not a valid WBMP file"),
|
||||
pika_file_get_utf8_name (file));
|
||||
goto out;
|
||||
}
|
||||
|
||||
image = read_image (fd, file, width, height, error);
|
||||
|
||||
out:
|
||||
if (fd)
|
||||
fclose (fd);
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
/* Code referenced from /plug-ins/file-bmp/bmp-load.c */
|
||||
static PikaImage *
|
||||
read_image (FILE *fd,
|
||||
GFile *file,
|
||||
gint width,
|
||||
gint height,
|
||||
GError **error)
|
||||
{
|
||||
const guchar mono[6] = { 0, 0, 0, 255, 255, 255 };
|
||||
guchar v;
|
||||
gint xpos = 0;
|
||||
gint ypos = 0;
|
||||
PikaImage *image;
|
||||
PikaLayer *layer;
|
||||
GeglBuffer *buffer;
|
||||
guchar *dest, *temp;
|
||||
gint i, cur_progress, max_progress;
|
||||
|
||||
/* Make a new image in PIKA */
|
||||
if ((width < 0) || (width > PIKA_MAX_IMAGE_SIZE))
|
||||
{
|
||||
g_message (_("Unsupported or invalid image width: %d"), width);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((height < 0) || (height > PIKA_MAX_IMAGE_SIZE))
|
||||
{
|
||||
g_message (_("Unsupported or invalid image height: %d"), height);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
image = pika_image_new (width, height, PIKA_INDEXED);
|
||||
layer = pika_layer_new (image, _("Background"), width, height,
|
||||
PIKA_INDEXED_IMAGE, 100,
|
||||
pika_image_get_default_new_layer_mode (image));
|
||||
|
||||
pika_image_set_colormap (image, mono, 2);
|
||||
|
||||
pika_image_insert_layer (image, layer, NULL, 0);
|
||||
|
||||
dest = g_malloc0 (width * height);
|
||||
|
||||
ypos = 0;
|
||||
|
||||
cur_progress = 0;
|
||||
max_progress = height;
|
||||
|
||||
while (ReadOK (fd, &v, 1))
|
||||
{
|
||||
for (i = 1; (i <= 8) && (xpos < width); i++, xpos++)
|
||||
{
|
||||
temp = dest + (ypos * width) + xpos;
|
||||
*temp = (v & (((1 << 1) - 1) << (8 - i))) >> (8 - i);
|
||||
}
|
||||
|
||||
if (xpos == width)
|
||||
{
|
||||
if (ypos == height - 1)
|
||||
break;
|
||||
|
||||
ypos++;
|
||||
xpos = 0;
|
||||
|
||||
cur_progress++;
|
||||
if ((cur_progress % 5) == 0)
|
||||
pika_progress_update ((gdouble) cur_progress / (gdouble) max_progress);
|
||||
}
|
||||
|
||||
if (ypos > height - 1)
|
||||
break;
|
||||
}
|
||||
|
||||
buffer = pika_drawable_get_buffer (PIKA_DRAWABLE (layer));
|
||||
|
||||
gegl_buffer_set (buffer, GEGL_RECTANGLE (0, 0, width, height), 0, NULL, dest,
|
||||
GEGL_AUTO_ROWSTRIDE);
|
||||
|
||||
g_object_unref (buffer);
|
||||
|
||||
g_free (dest);
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
1064
plug-ins/common/file-wmf.c
Normal file
1064
plug-ins/common/file-wmf.c
Normal file
File diff suppressed because it is too large
Load Diff
1324
plug-ins/common/file-xbm.c
Normal file
1324
plug-ins/common/file-xbm.c
Normal file
File diff suppressed because it is too large
Load Diff
2318
plug-ins/common/file-xmc.c
Normal file
2318
plug-ins/common/file-xmc.c
Normal file
File diff suppressed because it is too large
Load Diff
905
plug-ins/common/file-xpm.c
Normal file
905
plug-ins/common/file-xpm.c
Normal file
@ -0,0 +1,905 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* XPM plugin version 1.2.7 */
|
||||
|
||||
/*
|
||||
1.2.7 fixes saving unused transparency (bug #4560)
|
||||
|
||||
1.2.6 fixes crash when saving indexed images (bug #109567)
|
||||
|
||||
1.2.5 only creates a "None" color entry if the image has alpha (bug #108034)
|
||||
|
||||
1.2.4 displays an error message if saving fails (bug #87588)
|
||||
|
||||
1.2.3 fixes bug when running in noninteractive mode
|
||||
changes alpha_threshold range from [0, 1] to [0,255] for consistency with
|
||||
the threshold_alpha plugin
|
||||
|
||||
1.2.2 fixes bug that generated bad digits on images with more than 20000
|
||||
colors. (thanks, yanele)
|
||||
parses gtkrc (thanks, yosh)
|
||||
doesn't load parameter screen on images that don't have alpha
|
||||
|
||||
1.2.1 fixes some minor bugs -- spaces in #XXXXXX strings, small typos in code.
|
||||
|
||||
1.2 compute color indexes so that we don't have to use XpmSaveXImage*
|
||||
|
||||
Previous...Inherited code from Ray Lehtiniemi, who inherited it from S & P.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <glib/gstdio.h>
|
||||
|
||||
#include <gdk/gdk.h> /* For GDK_WINDOWING_WIN32 */
|
||||
|
||||
#ifndef GDK_WINDOWING_X11
|
||||
#ifndef XPM_NO_X
|
||||
#define XPM_NO_X
|
||||
#endif
|
||||
#else
|
||||
#include <X11/Xlib.h>
|
||||
#endif
|
||||
|
||||
#include <X11/xpm.h>
|
||||
|
||||
#include <libpika/pika.h>
|
||||
#include <libpika/pikaui.h>
|
||||
|
||||
#include "libpika/stdplugins-intl.h"
|
||||
|
||||
|
||||
#define LOAD_PROC "file-xpm-load"
|
||||
#define SAVE_PROC "file-xpm-save"
|
||||
#define PLUG_IN_BINARY "file-xpm"
|
||||
#define PLUG_IN_ROLE "pika-file-xpm"
|
||||
#define SCALE_WIDTH 125
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
guchar r;
|
||||
guchar g;
|
||||
guchar b;
|
||||
} rgbkey;
|
||||
|
||||
|
||||
typedef struct _Xpm Xpm;
|
||||
typedef struct _XpmClass XpmClass;
|
||||
|
||||
struct _Xpm
|
||||
{
|
||||
PikaPlugIn parent_instance;
|
||||
};
|
||||
|
||||
struct _XpmClass
|
||||
{
|
||||
PikaPlugInClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
#define XPM_TYPE (xpm_get_type ())
|
||||
#define XPM (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), XPM_TYPE, Xpm))
|
||||
|
||||
GType xpm_get_type (void) G_GNUC_CONST;
|
||||
|
||||
static GList * xpm_query_procedures (PikaPlugIn *plug_in);
|
||||
static PikaProcedure * xpm_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name);
|
||||
|
||||
static PikaValueArray * xpm_load (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
GFile *file,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data);
|
||||
static PikaValueArray * xpm_save (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
GFile *file,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data);
|
||||
|
||||
static PikaImage * load_image (GFile *file,
|
||||
GError **error);
|
||||
static guchar * parse_colors (XpmImage *xpm_image);
|
||||
static void parse_image (PikaImage *image,
|
||||
XpmImage *xpm_image,
|
||||
guchar *cmap);
|
||||
static gboolean save_image (GFile *file,
|
||||
PikaImage *image,
|
||||
PikaDrawable *drawable,
|
||||
GObject *config,
|
||||
GError **error);
|
||||
static gboolean save_dialog (PikaImage *image,
|
||||
PikaProcedure *procedure,
|
||||
GObject *config);
|
||||
|
||||
|
||||
G_DEFINE_TYPE (Xpm, xpm, PIKA_TYPE_PLUG_IN)
|
||||
|
||||
PIKA_MAIN (XPM_TYPE)
|
||||
DEFINE_STD_SET_I18N
|
||||
|
||||
|
||||
static const gchar linenoise [] =
|
||||
" .+@#$%&*=-;>,')!~{]^/(_:<[}|1234567890abcdefghijklmnopqrstuvwxyz\
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ`";
|
||||
|
||||
/* whether the image is color or not. global so I only have to pass
|
||||
* one user value to the GHFunc
|
||||
*/
|
||||
static gboolean color;
|
||||
|
||||
/* bytes per pixel. global so I only have to pass one user value
|
||||
* to the GHFunc
|
||||
*/
|
||||
static gint cpp;
|
||||
|
||||
|
||||
static void
|
||||
xpm_class_init (XpmClass *klass)
|
||||
{
|
||||
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
|
||||
|
||||
plug_in_class->query_procedures = xpm_query_procedures;
|
||||
plug_in_class->create_procedure = xpm_create_procedure;
|
||||
plug_in_class->set_i18n = STD_SET_I18N;
|
||||
}
|
||||
|
||||
static void
|
||||
xpm_init (Xpm *xpm)
|
||||
{
|
||||
}
|
||||
|
||||
static GList *
|
||||
xpm_query_procedures (PikaPlugIn *plug_in)
|
||||
{
|
||||
GList *list = NULL;
|
||||
|
||||
list = g_list_append (list, g_strdup (LOAD_PROC));
|
||||
list = g_list_append (list, g_strdup (SAVE_PROC));
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
static PikaProcedure *
|
||||
xpm_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name)
|
||||
{
|
||||
PikaProcedure *procedure = NULL;
|
||||
|
||||
if (! strcmp (name, LOAD_PROC))
|
||||
{
|
||||
procedure = pika_load_procedure_new (plug_in, name,
|
||||
PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
xpm_load, NULL, NULL);
|
||||
|
||||
pika_procedure_set_menu_label (procedure, _("X PixMap image"));
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
"Load files in XPM (X11 Pixmap) format.",
|
||||
"Load files in XPM (X11 Pixmap) format. "
|
||||
"XPM is a portable image format "
|
||||
"designed to be included in C source "
|
||||
"code. XLib provides utility functions "
|
||||
"to read this format. Newer code should "
|
||||
"however be using gdk-pixbuf-csource "
|
||||
"instead. XPM supports colored images, "
|
||||
"unlike the XBM format which XPM was "
|
||||
"designed to replace.",
|
||||
name);
|
||||
pika_procedure_set_attribution (procedure,
|
||||
"Spencer Kimball & Peter Mattis & "
|
||||
"Ray Lehtiniemi",
|
||||
"Spencer Kimball & Peter Mattis",
|
||||
"1997");
|
||||
|
||||
pika_file_procedure_set_mime_types (PIKA_FILE_PROCEDURE (procedure),
|
||||
"image/x-pixmap");
|
||||
pika_file_procedure_set_extensions (PIKA_FILE_PROCEDURE (procedure),
|
||||
"xpm");
|
||||
pika_file_procedure_set_magics (PIKA_FILE_PROCEDURE (procedure),
|
||||
"0, string,/*\\040XPM\\040*/");
|
||||
}
|
||||
else if (! strcmp (name, SAVE_PROC))
|
||||
{
|
||||
procedure = pika_save_procedure_new (plug_in, name,
|
||||
PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
xpm_save, NULL, NULL);
|
||||
|
||||
pika_procedure_set_image_types (procedure, "*");
|
||||
|
||||
pika_procedure_set_menu_label (procedure, _("X PixMap image"));
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
"Export files in XPM (X11 Pixmap) format.",
|
||||
"Export files in XPM (X11 Pixmap) format. "
|
||||
"XPM is a portable image format "
|
||||
"designed to be included in C source "
|
||||
"code. XLib provides utility functions "
|
||||
"to read this format. Newer code should "
|
||||
"however be using gdk-pixbuf-csource "
|
||||
"instead. XPM supports colored images, "
|
||||
"unlike the XBM format which XPM was "
|
||||
"designed to replace.",
|
||||
name);
|
||||
pika_procedure_set_attribution (procedure,
|
||||
"Spencer Kimball & Peter Mattis & "
|
||||
"Ray Lehtiniemi & Nathan Summers",
|
||||
"Spencer Kimball & Peter Mattis",
|
||||
"1997");
|
||||
|
||||
pika_file_procedure_set_format_name (PIKA_FILE_PROCEDURE (procedure),
|
||||
_("XPM"));
|
||||
pika_file_procedure_set_mime_types (PIKA_FILE_PROCEDURE (procedure),
|
||||
"image/x-pixmap");
|
||||
pika_file_procedure_set_extensions (PIKA_FILE_PROCEDURE (procedure),
|
||||
"xpm");
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "threshold",
|
||||
_("_Threshold"),
|
||||
_("Alpha threshold"),
|
||||
0, 255, 127,
|
||||
G_PARAM_READWRITE);
|
||||
}
|
||||
|
||||
return procedure;
|
||||
}
|
||||
|
||||
static PikaValueArray *
|
||||
xpm_load (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
GFile *file,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data)
|
||||
{
|
||||
PikaValueArray *return_vals;
|
||||
PikaImage *image;
|
||||
GError *error = NULL;
|
||||
|
||||
gegl_init (NULL, NULL);
|
||||
|
||||
image = load_image (file, &error);
|
||||
|
||||
if (! image)
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_EXECUTION_ERROR,
|
||||
error);
|
||||
|
||||
return_vals = pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_SUCCESS,
|
||||
NULL);
|
||||
|
||||
PIKA_VALUES_SET_IMAGE (return_vals, 1, image);
|
||||
|
||||
return return_vals;
|
||||
}
|
||||
|
||||
static PikaValueArray *
|
||||
xpm_save (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
GFile *file,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data)
|
||||
{
|
||||
PikaProcedureConfig *config;
|
||||
PikaPDBStatusType status = PIKA_PDB_SUCCESS;
|
||||
PikaExportReturn export = PIKA_EXPORT_CANCEL;
|
||||
GError *error = NULL;
|
||||
|
||||
gegl_init (NULL, NULL);
|
||||
|
||||
config = pika_procedure_create_config (procedure);
|
||||
pika_procedure_config_begin_run (config, image, run_mode, args);
|
||||
|
||||
switch (run_mode)
|
||||
{
|
||||
case PIKA_RUN_INTERACTIVE:
|
||||
case PIKA_RUN_WITH_LAST_VALS:
|
||||
pika_ui_init (PLUG_IN_BINARY);
|
||||
|
||||
export = pika_export_image (&image, &n_drawables, &drawables, "XPM",
|
||||
PIKA_EXPORT_CAN_HANDLE_RGB |
|
||||
PIKA_EXPORT_CAN_HANDLE_GRAY |
|
||||
PIKA_EXPORT_CAN_HANDLE_INDEXED |
|
||||
PIKA_EXPORT_CAN_HANDLE_ALPHA);
|
||||
|
||||
if (export == PIKA_EXPORT_CANCEL)
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_CANCEL,
|
||||
NULL);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (n_drawables != 1)
|
||||
{
|
||||
g_set_error (&error, G_FILE_ERROR, 0,
|
||||
_("XPM format does not support multiple layers."));
|
||||
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_CALLING_ERROR,
|
||||
error);
|
||||
}
|
||||
|
||||
if (run_mode == PIKA_RUN_INTERACTIVE)
|
||||
{
|
||||
if (pika_drawable_has_alpha (drawables[0]))
|
||||
if (! save_dialog (image, procedure, G_OBJECT (config)))
|
||||
status = PIKA_PDB_CANCEL;
|
||||
}
|
||||
|
||||
if (status == PIKA_PDB_SUCCESS)
|
||||
{
|
||||
if (! save_image (file, image, drawables[0], G_OBJECT (config),
|
||||
&error))
|
||||
{
|
||||
status = PIKA_PDB_EXECUTION_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
pika_procedure_config_end_run (config, status);
|
||||
g_object_unref (config);
|
||||
|
||||
if (export == PIKA_EXPORT_EXPORT)
|
||||
{
|
||||
pika_image_delete (image);
|
||||
g_free (drawables);
|
||||
}
|
||||
|
||||
return pika_procedure_new_return_values (procedure, status, error);
|
||||
}
|
||||
|
||||
static PikaImage *
|
||||
load_image (GFile *file,
|
||||
GError **error)
|
||||
{
|
||||
XpmImage xpm_image;
|
||||
guchar *cmap;
|
||||
PikaImage *image;
|
||||
|
||||
pika_progress_init_printf (_("Opening '%s'"),
|
||||
pika_file_get_utf8_name (file));
|
||||
|
||||
/* read the raw file */
|
||||
switch (XpmReadFileToXpmImage (g_file_peek_path (file), &xpm_image, NULL))
|
||||
{
|
||||
case XpmSuccess:
|
||||
break;
|
||||
|
||||
case XpmOpenFailed:
|
||||
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
||||
_("Error opening file '%s'"),
|
||||
pika_file_get_utf8_name (file));
|
||||
return NULL;
|
||||
|
||||
case XpmFileInvalid:
|
||||
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
||||
"%s", _("XPM file invalid"));
|
||||
return NULL;
|
||||
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cmap = parse_colors (&xpm_image);
|
||||
|
||||
image = pika_image_new (xpm_image.width,
|
||||
xpm_image.height,
|
||||
PIKA_RGB);
|
||||
|
||||
/* fill it */
|
||||
parse_image (image, &xpm_image, cmap);
|
||||
|
||||
g_free (cmap);
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
static guchar *
|
||||
parse_colors (XpmImage *xpm_image)
|
||||
{
|
||||
#ifndef XPM_NO_X
|
||||
Display *display;
|
||||
Colormap colormap;
|
||||
#endif
|
||||
gint i, j;
|
||||
guchar *cmap;
|
||||
|
||||
#ifndef XPM_NO_X
|
||||
/* open the display and get the default color map */
|
||||
display = XOpenDisplay (NULL);
|
||||
if (display == NULL)
|
||||
g_printerr ("Could not open display\n");
|
||||
|
||||
colormap = DefaultColormap (display, DefaultScreen (display));
|
||||
#endif
|
||||
|
||||
/* alloc a buffer to hold the parsed colors */
|
||||
cmap = g_new0 (guchar, 4 * xpm_image->ncolors);
|
||||
|
||||
/* parse each color in the file */
|
||||
for (i = 0, j = 0; i < xpm_image->ncolors; i++)
|
||||
{
|
||||
gchar *colorspec = "None";
|
||||
XpmColor *xpm_color;
|
||||
#ifndef XPM_NO_X
|
||||
XColor xcolor;
|
||||
#else
|
||||
GdkRGBA xcolor;
|
||||
#endif
|
||||
|
||||
xpm_color = &(xpm_image->colorTable[i]);
|
||||
|
||||
/* pick the best spec available */
|
||||
if (xpm_color->c_color)
|
||||
colorspec = xpm_color->c_color;
|
||||
else if (xpm_color->g_color)
|
||||
colorspec = xpm_color->g_color;
|
||||
else if (xpm_color->g4_color)
|
||||
colorspec = xpm_color->g4_color;
|
||||
else if (xpm_color->m_color)
|
||||
colorspec = xpm_color->m_color;
|
||||
|
||||
/* parse if it's not transparent */
|
||||
if (strcmp (colorspec, "None") != 0)
|
||||
{
|
||||
#ifndef XPM_NO_X
|
||||
XParseColor (display, colormap, colorspec, &xcolor);
|
||||
cmap[j++] = xcolor.red >> 8;
|
||||
cmap[j++] = xcolor.green >> 8;
|
||||
cmap[j++] = xcolor.blue >> 8;
|
||||
#else
|
||||
gdk_rgba_parse (&xcolor, colorspec);
|
||||
cmap[j++] = CLAMP (xcolor.red * G_MAXUINT8, 0, G_MAXUINT8);
|
||||
cmap[j++] = CLAMP (xcolor.green * G_MAXUINT8, 0, G_MAXUINT8);
|
||||
cmap[j++] = CLAMP (xcolor.blue * G_MAXUINT8, 0, G_MAXUINT8);
|
||||
#endif
|
||||
cmap[j++] = ~0;
|
||||
}
|
||||
else
|
||||
{
|
||||
j += 4;
|
||||
}
|
||||
}
|
||||
#ifndef XPM_NO_X
|
||||
XCloseDisplay (display);
|
||||
#endif
|
||||
return cmap;
|
||||
}
|
||||
|
||||
static void
|
||||
parse_image (PikaImage *image,
|
||||
XpmImage *xpm_image,
|
||||
guchar *cmap)
|
||||
{
|
||||
GeglBuffer *buffer;
|
||||
gint tile_height;
|
||||
gint scanlines;
|
||||
gint val;
|
||||
guchar *buf;
|
||||
guchar *dest;
|
||||
guint *src;
|
||||
PikaLayer *layer;
|
||||
gint i;
|
||||
|
||||
layer = pika_layer_new (image,
|
||||
_("Color"),
|
||||
xpm_image->width,
|
||||
xpm_image->height,
|
||||
PIKA_RGBA_IMAGE,
|
||||
100,
|
||||
pika_image_get_default_new_layer_mode (image));
|
||||
|
||||
pika_image_insert_layer (image, layer, NULL, 0);
|
||||
|
||||
buffer = pika_drawable_get_buffer (PIKA_DRAWABLE (layer));
|
||||
|
||||
tile_height = pika_tile_height ();
|
||||
|
||||
buf = g_new (guchar, tile_height * xpm_image->width * 4);
|
||||
|
||||
src = xpm_image->data;
|
||||
for (i = 0; i < xpm_image->height; i += tile_height)
|
||||
{
|
||||
gint j;
|
||||
|
||||
dest = buf;
|
||||
scanlines = MIN (tile_height, xpm_image->height - i);
|
||||
j = scanlines * xpm_image->width;
|
||||
while (j--)
|
||||
{
|
||||
{
|
||||
val = *(src++) * 4;
|
||||
*(dest) = cmap[val];
|
||||
*(dest+1) = cmap[val+1];
|
||||
*(dest+2) = cmap[val+2];
|
||||
*(dest+3) = cmap[val+3];
|
||||
dest += 4;
|
||||
}
|
||||
|
||||
if ((j % 100) == 0)
|
||||
pika_progress_update ((double) i / (double) xpm_image->height);
|
||||
}
|
||||
|
||||
gegl_buffer_set (buffer,
|
||||
GEGL_RECTANGLE (0, i, xpm_image->width, scanlines), 0,
|
||||
NULL, buf, GEGL_AUTO_ROWSTRIDE);
|
||||
}
|
||||
|
||||
g_free (buf);
|
||||
g_object_unref (buffer);
|
||||
|
||||
pika_progress_update (1.0);
|
||||
}
|
||||
|
||||
static guint
|
||||
rgbhash (rgbkey *c)
|
||||
{
|
||||
return ((guint)c->r) ^ ((guint)c->g) ^ ((guint)c->b);
|
||||
}
|
||||
|
||||
static guint
|
||||
compare (rgbkey *c1,
|
||||
rgbkey *c2)
|
||||
{
|
||||
return (c1->r == c2->r) && (c1->g == c2->g) && (c1->b == c2->b);
|
||||
}
|
||||
|
||||
static void
|
||||
set_XpmImage (XpmColor *array,
|
||||
guint index,
|
||||
gchar *colorstring)
|
||||
{
|
||||
gchar *p;
|
||||
gint i, charnum, indtemp;
|
||||
|
||||
indtemp=index;
|
||||
array[index].string = p = g_new (gchar, cpp+1);
|
||||
|
||||
/*convert the index number to base sizeof(linenoise)-1 */
|
||||
for (i = 0; i < cpp; ++i)
|
||||
{
|
||||
charnum = indtemp % (sizeof (linenoise) - 1);
|
||||
indtemp = indtemp / (sizeof (linenoise) - 1);
|
||||
*p++ = linenoise[charnum];
|
||||
}
|
||||
/* *p++=linenoise[indtemp]; */
|
||||
|
||||
*p = '\0'; /* C and its stupid null-terminated strings... */
|
||||
|
||||
array[index].symbolic = NULL;
|
||||
array[index].m_color = NULL;
|
||||
array[index].g4_color = NULL;
|
||||
|
||||
if (color)
|
||||
{
|
||||
array[index].g_color = NULL;
|
||||
array[index].c_color = colorstring;
|
||||
}
|
||||
else
|
||||
{
|
||||
array[index].c_color = NULL;
|
||||
array[index].g_color = colorstring;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
create_colormap_from_hash (gpointer gkey,
|
||||
gpointer value,
|
||||
gpointer user_data)
|
||||
{
|
||||
rgbkey *key = gkey;
|
||||
gchar *string = g_new (gchar, 8);
|
||||
|
||||
sprintf (string, "#%02X%02X%02X", (int)key->r, (int)key->g, (int)key->b);
|
||||
set_XpmImage (user_data, *((int *) value), string);
|
||||
}
|
||||
|
||||
static void
|
||||
decrement_hash_values (gpointer gkey,
|
||||
gpointer value,
|
||||
gpointer user_data)
|
||||
{
|
||||
--(*((guint*) value));
|
||||
}
|
||||
|
||||
static gboolean
|
||||
save_image (GFile *file,
|
||||
PikaImage *image,
|
||||
PikaDrawable *drawable,
|
||||
GObject *config,
|
||||
GError **error)
|
||||
{
|
||||
GeglBuffer *buffer;
|
||||
const Babl *format;
|
||||
gint width;
|
||||
gint height;
|
||||
gint ncolors = 1;
|
||||
gint *indexno;
|
||||
gboolean indexed;
|
||||
gboolean alpha;
|
||||
gboolean alpha_used = FALSE;
|
||||
XpmColor *colormap;
|
||||
XpmImage *xpm_image;
|
||||
guint *ibuff = NULL;
|
||||
guchar *buf;
|
||||
guchar *data;
|
||||
GHashTable *hash = NULL;
|
||||
gint i, j, k;
|
||||
gint threshold;
|
||||
gboolean success = FALSE;
|
||||
|
||||
g_object_get (config,
|
||||
"threshold", &threshold,
|
||||
NULL);
|
||||
|
||||
buffer = pika_drawable_get_buffer (drawable);
|
||||
|
||||
width = gegl_buffer_get_width (buffer);
|
||||
height = gegl_buffer_get_height (buffer);
|
||||
|
||||
alpha = pika_drawable_has_alpha (drawable);
|
||||
color = ! pika_drawable_is_gray (drawable);
|
||||
indexed = pika_drawable_is_indexed (drawable);
|
||||
|
||||
switch (pika_drawable_type (drawable))
|
||||
{
|
||||
case PIKA_RGB_IMAGE:
|
||||
format = babl_format ("R'G'B' u8");
|
||||
break;
|
||||
|
||||
case PIKA_RGBA_IMAGE:
|
||||
format = babl_format ("R'G'B'A u8");
|
||||
break;
|
||||
|
||||
case PIKA_GRAY_IMAGE:
|
||||
format = babl_format ("Y' u8");
|
||||
break;
|
||||
|
||||
case PIKA_GRAYA_IMAGE:
|
||||
format = babl_format ("Y'A u8");
|
||||
break;
|
||||
|
||||
case PIKA_INDEXED_IMAGE:
|
||||
case PIKA_INDEXEDA_IMAGE:
|
||||
format = gegl_buffer_get_format (buffer);
|
||||
break;
|
||||
|
||||
default:
|
||||
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
||||
_("Unsupported drawable type"));
|
||||
g_object_unref (buffer);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* allocate buffer making the assumption that ibuff is 32 bit aligned... */
|
||||
ibuff = g_new (guint, width * height);
|
||||
|
||||
hash = g_hash_table_new ((GHashFunc) rgbhash, (GCompareFunc) compare);
|
||||
|
||||
pika_progress_init_printf (_("Exporting '%s'"),
|
||||
pika_file_get_utf8_name (file));
|
||||
|
||||
ncolors = alpha ? 1 : 0;
|
||||
|
||||
/* allocate a pixel region to work with */
|
||||
buf = g_new (guchar,
|
||||
pika_tile_height () * width *
|
||||
babl_format_get_bytes_per_pixel (format));
|
||||
|
||||
/* process each row of tiles */
|
||||
for (i = 0; i < height; i += pika_tile_height ())
|
||||
{
|
||||
gint scanlines;
|
||||
|
||||
/* read the next row of tiles */
|
||||
scanlines = MIN (pika_tile_height(), height - i);
|
||||
|
||||
gegl_buffer_get (buffer, GEGL_RECTANGLE (0, i, width, scanlines), 1.0,
|
||||
format, buf,
|
||||
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
|
||||
|
||||
data = buf;
|
||||
|
||||
/* process each pixel row */
|
||||
for (j = 0; j < scanlines; j++)
|
||||
{
|
||||
/* go to the start of this row in each image */
|
||||
guint *idata = ibuff + (i+j) * width;
|
||||
|
||||
/* do each pixel in the row */
|
||||
for (k = 0; k < width; k++)
|
||||
{
|
||||
rgbkey *key = g_new (rgbkey, 1);
|
||||
guchar a;
|
||||
|
||||
/* get pixel data */
|
||||
key->r = *(data++);
|
||||
key->g = color && !indexed ? *(data++) : key->r;
|
||||
key->b = color && !indexed ? *(data++) : key->r;
|
||||
a = alpha ? *(data++) : 255;
|
||||
|
||||
if (a < threshold)
|
||||
{
|
||||
*(idata++) = 0;
|
||||
alpha_used = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (indexed)
|
||||
{
|
||||
*(idata++) = (key->r) + (alpha ? 1 : 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
indexno = g_hash_table_lookup (hash, key);
|
||||
if (!indexno)
|
||||
{
|
||||
indexno = g_new (gint, 1);
|
||||
*indexno = ncolors++;
|
||||
g_hash_table_insert (hash, key, indexno);
|
||||
key = g_new (rgbkey, 1);
|
||||
}
|
||||
*(idata++) = *indexno;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* kick the progress bar */
|
||||
pika_progress_update ((gdouble) (i+j) / (gdouble) height);
|
||||
}
|
||||
}
|
||||
|
||||
g_free (buf);
|
||||
|
||||
/* remove alpha if not actually used */
|
||||
if (alpha && !alpha_used)
|
||||
{
|
||||
gint i;
|
||||
--ncolors;
|
||||
for (i = 0; i < width * height; ++i)
|
||||
--ibuff[i];
|
||||
|
||||
g_hash_table_foreach (hash, decrement_hash_values, NULL);
|
||||
}
|
||||
|
||||
if (indexed)
|
||||
{
|
||||
guchar *cmap = pika_image_get_colormap (image, NULL, &ncolors);
|
||||
guchar *c;
|
||||
|
||||
c = cmap;
|
||||
|
||||
if (alpha_used)
|
||||
ncolors++;
|
||||
|
||||
colormap = g_new (XpmColor, ncolors);
|
||||
cpp =
|
||||
1 + (gdouble) log (ncolors) / (gdouble) log (sizeof (linenoise) - 1.0);
|
||||
|
||||
if (alpha_used)
|
||||
set_XpmImage (colormap, 0, "None");
|
||||
|
||||
for (i = alpha_used ? 1 : 0; i < ncolors; i++)
|
||||
{
|
||||
gchar *string;
|
||||
guchar r, g, b;
|
||||
|
||||
r = *c++;
|
||||
g = *c++;
|
||||
b = *c++;
|
||||
|
||||
string = g_new (gchar, 8);
|
||||
sprintf (string, "#%02X%02X%02X", (int)r, (int)g, (int)b);
|
||||
set_XpmImage (colormap, i, string);
|
||||
}
|
||||
|
||||
g_free (cmap);
|
||||
}
|
||||
else
|
||||
{
|
||||
colormap = g_new (XpmColor, ncolors);
|
||||
cpp =
|
||||
1 + (gdouble) log (ncolors) / (gdouble) log (sizeof (linenoise) - 1.0);
|
||||
|
||||
if (alpha_used)
|
||||
set_XpmImage (colormap, 0, "None");
|
||||
|
||||
g_hash_table_foreach (hash, create_colormap_from_hash, colormap);
|
||||
}
|
||||
|
||||
xpm_image = g_new (XpmImage, 1);
|
||||
|
||||
xpm_image->width = width;
|
||||
xpm_image->height = height;
|
||||
xpm_image->ncolors = ncolors;
|
||||
xpm_image->cpp = cpp;
|
||||
xpm_image->colorTable = colormap;
|
||||
xpm_image->data = ibuff;
|
||||
|
||||
/* do the save */
|
||||
switch (XpmWriteFileFromXpmImage (g_file_peek_path (file), xpm_image, NULL))
|
||||
{
|
||||
case XpmSuccess:
|
||||
success = TRUE;
|
||||
break;
|
||||
|
||||
case XpmOpenFailed:
|
||||
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
||||
_("Error opening file '%s'"),
|
||||
pika_file_get_utf8_name (file));
|
||||
break;
|
||||
|
||||
case XpmFileInvalid:
|
||||
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
||||
"%s", _("XPM file invalid"));
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
g_object_unref (buffer);
|
||||
g_free (ibuff);
|
||||
|
||||
if (hash)
|
||||
g_hash_table_destroy (hash);
|
||||
|
||||
pika_progress_update (1.0);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
save_dialog (PikaImage *image,
|
||||
PikaProcedure *procedure,
|
||||
GObject *config)
|
||||
{
|
||||
GtkWidget *dialog;
|
||||
gboolean run;
|
||||
|
||||
dialog = pika_save_procedure_dialog_new (PIKA_SAVE_PROCEDURE (procedure),
|
||||
PIKA_PROCEDURE_CONFIG (config),
|
||||
image);
|
||||
|
||||
pika_procedure_dialog_get_scale_entry (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"threshold", 1.0);
|
||||
|
||||
pika_procedure_dialog_fill (PIKA_PROCEDURE_DIALOG (dialog), NULL);
|
||||
gtk_widget_show (dialog);
|
||||
|
||||
run = pika_procedure_dialog_run (PIKA_PROCEDURE_DIALOG (dialog));
|
||||
|
||||
gtk_widget_destroy (dialog);
|
||||
|
||||
return run;
|
||||
}
|
||||
2767
plug-ins/common/file-xwd.c
Normal file
2767
plug-ins/common/file-xwd.c
Normal file
File diff suppressed because it is too large
Load Diff
1446
plug-ins/common/film.c
Normal file
1446
plug-ins/common/film.c
Normal file
File diff suppressed because it is too large
Load Diff
487
plug-ins/common/gradient-map.c
Normal file
487
plug-ins/common/gradient-map.c
Normal file
@ -0,0 +1,487 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* Gradient Map plug-in
|
||||
* Copyright (C) 1997 Eiichi Takamori <taka@ma1.seikyou.ne.jp>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <string.h>
|
||||
|
||||
#include <libpika/pika.h>
|
||||
|
||||
#include "libpika/stdplugins-intl.h"
|
||||
|
||||
|
||||
/* Some useful macros */
|
||||
#define GRADMAP_PROC "plug-in-gradmap"
|
||||
#define PALETTEMAP_PROC "plug-in-palettemap"
|
||||
#define PLUG_IN_BINARY "gradient-map"
|
||||
#define PLUG_IN_ROLE "pika-gradient-map"
|
||||
#define NSAMPLES 2048
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GRADIENT_MODE = 1,
|
||||
PALETTE_MODE
|
||||
} MapMode;
|
||||
|
||||
|
||||
typedef struct _Map Map;
|
||||
typedef struct _MapClass MapClass;
|
||||
|
||||
struct _Map
|
||||
{
|
||||
PikaPlugIn parent_instance;
|
||||
};
|
||||
|
||||
struct _MapClass
|
||||
{
|
||||
PikaPlugInClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
#define MAP_TYPE (map_get_type ())
|
||||
#define MAP (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MAP_TYPE, Map))
|
||||
|
||||
GType map_get_type (void) G_GNUC_CONST;
|
||||
|
||||
static GList * map_query_procedures (PikaPlugIn *plug_in);
|
||||
static PikaProcedure * map_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name);
|
||||
|
||||
static PikaValueArray * map_run (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data);
|
||||
|
||||
static void map (GeglBuffer *buffer,
|
||||
GeglBuffer *shadow_buffer,
|
||||
PikaDrawable *drawable,
|
||||
MapMode mode);
|
||||
static gdouble * get_samples_gradient (PikaDrawable *drawable);
|
||||
static gdouble * get_samples_palette (PikaDrawable *drawable);
|
||||
|
||||
|
||||
G_DEFINE_TYPE (Map, map, PIKA_TYPE_PLUG_IN)
|
||||
|
||||
PIKA_MAIN (MAP_TYPE)
|
||||
DEFINE_STD_SET_I18N
|
||||
|
||||
|
||||
static void
|
||||
map_class_init (MapClass *klass)
|
||||
{
|
||||
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
|
||||
|
||||
plug_in_class->query_procedures = map_query_procedures;
|
||||
plug_in_class->create_procedure = map_create_procedure;
|
||||
plug_in_class->set_i18n = STD_SET_I18N;
|
||||
}
|
||||
|
||||
static void
|
||||
map_init (Map *map)
|
||||
{
|
||||
}
|
||||
|
||||
static GList *
|
||||
map_query_procedures (PikaPlugIn *plug_in)
|
||||
{
|
||||
GList *list = NULL;
|
||||
|
||||
list = g_list_append (list, g_strdup (GRADMAP_PROC));
|
||||
list = g_list_append (list, g_strdup (PALETTEMAP_PROC));
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
static PikaProcedure *
|
||||
map_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name)
|
||||
{
|
||||
PikaProcedure *procedure = NULL;
|
||||
|
||||
if (! strcmp (name, GRADMAP_PROC))
|
||||
{
|
||||
procedure = pika_image_procedure_new (plug_in, name,
|
||||
PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
map_run,
|
||||
GINT_TO_POINTER (GRADIENT_MODE),
|
||||
NULL);
|
||||
|
||||
pika_procedure_set_image_types (procedure, "RGB*, GRAY*");
|
||||
pika_procedure_set_sensitivity_mask (procedure,
|
||||
PIKA_PROCEDURE_SENSITIVE_DRAWABLE);
|
||||
|
||||
pika_procedure_set_menu_label (procedure, _("_Gradient Map"));
|
||||
pika_procedure_add_menu_path (procedure, "<Image>/Colors/Map");
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
_("Recolor the image using colors "
|
||||
"from the active gradient"),
|
||||
"This plug-in maps the contents of "
|
||||
"the specified drawable with active "
|
||||
"gradient. It calculates luminosity "
|
||||
"of each pixel and replaces the pixel "
|
||||
"by the sample of active gradient at "
|
||||
"the position proportional to that "
|
||||
"luminosity. Complete black pixel "
|
||||
"becomes the leftmost color of the "
|
||||
"gradient, and complete white becomes "
|
||||
"the rightmost. Works on both "
|
||||
"Grayscale and RGB image "
|
||||
"with/without alpha channel.",
|
||||
name);
|
||||
pika_procedure_set_attribution (procedure,
|
||||
"Eiichi Takamori",
|
||||
"Eiichi Takamori",
|
||||
"1997");
|
||||
}
|
||||
else if (! strcmp (name, PALETTEMAP_PROC))
|
||||
{
|
||||
procedure = pika_image_procedure_new (plug_in, name,
|
||||
PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
map_run,
|
||||
GINT_TO_POINTER (PALETTE_MODE),
|
||||
NULL);
|
||||
|
||||
pika_procedure_set_image_types (procedure, "RGB*, GRAY*");
|
||||
pika_procedure_set_sensitivity_mask (procedure,
|
||||
PIKA_PROCEDURE_SENSITIVE_DRAWABLE);
|
||||
|
||||
pika_procedure_set_menu_label (procedure, _("_Palette Map"));
|
||||
pika_procedure_add_menu_path (procedure, "<Image>/Colors/Map");
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
_("Recolor the image using colors "
|
||||
"from the active palette"),
|
||||
"This plug-in maps the contents of "
|
||||
"the specified drawable with the "
|
||||
"active palette. It calculates "
|
||||
"luminosity of each pixel and "
|
||||
"replaces the pixel by the palette "
|
||||
"sample at the corresponding index. "
|
||||
"A complete black pixel becomes the "
|
||||
"lowest palette entry, and complete "
|
||||
"white becomes the highest. Works on "
|
||||
"both Grayscale and RGB image "
|
||||
"with/without alpha channel.",
|
||||
name);
|
||||
pika_procedure_set_attribution (procedure,
|
||||
"Bill Skaggs",
|
||||
"Bill Skaggs",
|
||||
"2004");
|
||||
}
|
||||
|
||||
return procedure;
|
||||
}
|
||||
|
||||
static PikaValueArray *
|
||||
map_run (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data)
|
||||
{
|
||||
MapMode mode = GPOINTER_TO_INT (run_data);
|
||||
GeglBuffer *shadow_buffer;
|
||||
GeglBuffer *buffer;
|
||||
PikaDrawable *drawable;
|
||||
|
||||
gegl_init (NULL, NULL);
|
||||
|
||||
if (n_drawables != 1)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
g_set_error (&error, PIKA_PLUG_IN_ERROR, 0,
|
||||
_("Procedure '%s' only works with one drawable."),
|
||||
pika_procedure_get_name (procedure));
|
||||
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_CALLING_ERROR,
|
||||
error);
|
||||
}
|
||||
else
|
||||
{
|
||||
drawable = drawables[0];
|
||||
}
|
||||
|
||||
shadow_buffer = pika_drawable_get_shadow_buffer (drawable);
|
||||
buffer = pika_drawable_get_buffer (drawable);
|
||||
|
||||
/* Make sure that the drawable is gray or RGB color */
|
||||
if (pika_drawable_is_rgb (drawable) ||
|
||||
pika_drawable_is_gray (drawable))
|
||||
{
|
||||
if (mode == GRADIENT_MODE)
|
||||
{
|
||||
pika_progress_init (_("Gradient Map"));
|
||||
}
|
||||
else
|
||||
{
|
||||
pika_progress_init (_("Palette Map"));
|
||||
}
|
||||
|
||||
map (buffer, shadow_buffer, drawable, mode);
|
||||
}
|
||||
else
|
||||
{
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_EXECUTION_ERROR,
|
||||
NULL);
|
||||
}
|
||||
|
||||
g_object_unref (buffer);
|
||||
g_object_unref (shadow_buffer);
|
||||
|
||||
pika_drawable_merge_shadow (drawable, TRUE);
|
||||
|
||||
pika_drawable_update (drawable, 0, 0,
|
||||
pika_drawable_get_width (drawable),
|
||||
pika_drawable_get_height (drawable));
|
||||
|
||||
if (run_mode != PIKA_RUN_NONINTERACTIVE)
|
||||
pika_displays_flush ();
|
||||
|
||||
return pika_procedure_new_return_values (procedure, PIKA_PDB_SUCCESS, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
map (GeglBuffer *buffer,
|
||||
GeglBuffer *shadow_buffer,
|
||||
PikaDrawable *drawable,
|
||||
MapMode mode)
|
||||
{
|
||||
GeglBufferIterator *gi;
|
||||
gint nb_color_chan;
|
||||
gint nb_chan;
|
||||
gint nb_chan2;
|
||||
gint nb_chan_samp;
|
||||
gint index_iter;
|
||||
gboolean interpolate;
|
||||
gdouble *samples;
|
||||
gboolean is_rgb;
|
||||
gboolean has_alpha;
|
||||
const Babl *format_shadow;
|
||||
const Babl *format_buffer;
|
||||
|
||||
is_rgb = pika_drawable_is_rgb (drawable);
|
||||
has_alpha = pika_drawable_has_alpha (drawable);
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case GRADIENT_MODE:
|
||||
samples = get_samples_gradient (drawable);
|
||||
interpolate = TRUE;
|
||||
break;
|
||||
case PALETTE_MODE:
|
||||
samples = get_samples_palette (drawable);
|
||||
interpolate = FALSE;
|
||||
break;
|
||||
default:
|
||||
g_error ("plug_in_gradmap: invalid mode");
|
||||
}
|
||||
|
||||
if (is_rgb)
|
||||
{
|
||||
nb_color_chan = 3;
|
||||
nb_chan_samp = 4;
|
||||
if (has_alpha)
|
||||
format_shadow = babl_format ("R'G'B'A float");
|
||||
else
|
||||
format_shadow = babl_format ("R'G'B' float");
|
||||
}
|
||||
else
|
||||
{
|
||||
nb_color_chan = 1;
|
||||
nb_chan_samp = 2;
|
||||
if (has_alpha)
|
||||
format_shadow = babl_format ("Y'A float");
|
||||
else
|
||||
format_shadow = babl_format ("Y' float");
|
||||
}
|
||||
|
||||
|
||||
if (has_alpha)
|
||||
{
|
||||
nb_chan = nb_color_chan + 1;
|
||||
nb_chan2 = 2;
|
||||
format_buffer = babl_format ("Y'A float");
|
||||
}
|
||||
else
|
||||
{
|
||||
nb_chan = nb_color_chan;
|
||||
nb_chan2 = 1;
|
||||
format_buffer = babl_format ("Y' float");
|
||||
}
|
||||
|
||||
gi = gegl_buffer_iterator_new (shadow_buffer, NULL, 0, format_shadow,
|
||||
GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE, 2);
|
||||
|
||||
index_iter = gegl_buffer_iterator_add (gi, buffer, NULL,
|
||||
0, format_buffer,
|
||||
GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
|
||||
|
||||
while (gegl_buffer_iterator_next (gi))
|
||||
{
|
||||
guint k;
|
||||
gfloat *data;
|
||||
gfloat *data2;
|
||||
|
||||
data = (gfloat*) gi->items[0].data;
|
||||
data2 = (gfloat*) gi->items[index_iter].data;
|
||||
|
||||
if (interpolate)
|
||||
{
|
||||
for (k = 0; k < gi->length; k++)
|
||||
{
|
||||
gint b, ind1, ind2;
|
||||
gdouble *samp1, *samp2;
|
||||
gfloat c1, c2, val;
|
||||
|
||||
val = data2[0] * (NSAMPLES-1);
|
||||
|
||||
ind1 = CLAMP (floor (val), 0, NSAMPLES-1);
|
||||
ind2 = CLAMP (ceil (val), 0, NSAMPLES-1);
|
||||
|
||||
c1 = 1.0 - (val - ind1);
|
||||
c2 = 1.0 - c1;
|
||||
|
||||
samp1 = &(samples[ind1 * nb_chan_samp]);
|
||||
samp2 = &(samples[ind2 * nb_chan_samp]);
|
||||
|
||||
for (b = 0; b < nb_color_chan; b++)
|
||||
data[b] = (samp1[b] * c1 + samp2[b] * c2);
|
||||
|
||||
if (has_alpha)
|
||||
{
|
||||
float alpha = (samp1[b] * c1 + samp2[b] * c2);
|
||||
data[b] = alpha * data2[1];
|
||||
}
|
||||
|
||||
data += nb_chan;
|
||||
data2 += nb_chan2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (k = 0; k < gi->length; k++)
|
||||
{
|
||||
gint b, ind;
|
||||
gdouble *samp;
|
||||
ind = CLAMP (data2[0] * (NSAMPLES-1), 0, NSAMPLES-1);
|
||||
|
||||
samp = &(samples[ind * nb_chan_samp]);
|
||||
|
||||
for (b = 0; b < nb_color_chan; b++)
|
||||
data[b] = samp[b];
|
||||
|
||||
if (has_alpha)
|
||||
{
|
||||
data[b] = samp[b] * data2[1];
|
||||
}
|
||||
|
||||
data += nb_chan;
|
||||
data2 += nb_chan2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
g_free (samples);
|
||||
}
|
||||
|
||||
/*
|
||||
Returns 2048 samples of the gradient.
|
||||
Each sample is (R'G'B'A float) or (Y'A float), depending on the drawable
|
||||
*/
|
||||
static gdouble *
|
||||
get_samples_gradient (PikaDrawable *drawable)
|
||||
{
|
||||
PikaGradient *gradient;
|
||||
|
||||
gint n_d_samples;
|
||||
gdouble *d_samples = NULL;
|
||||
|
||||
gradient = pika_context_get_gradient ();
|
||||
|
||||
/* FIXME: "reverse" hardcoded to FALSE. */
|
||||
pika_gradient_get_uniform_samples (gradient, NSAMPLES, FALSE,
|
||||
&n_d_samples, &d_samples);
|
||||
|
||||
if (! pika_drawable_is_rgb (drawable))
|
||||
{
|
||||
const Babl *format_src = babl_format ("R'G'B'A double");
|
||||
const Babl *format_dst = babl_format ("Y'A double");
|
||||
const Babl *fish = babl_fish (format_src, format_dst);
|
||||
|
||||
babl_process (fish, d_samples, d_samples, NSAMPLES);
|
||||
}
|
||||
|
||||
return d_samples;
|
||||
}
|
||||
|
||||
/*
|
||||
Returns 2048 samples of the palette.
|
||||
Each sample is (R'G'B'A float) or (Y'A float), depending on the drawable
|
||||
*/
|
||||
static gdouble *
|
||||
get_samples_palette (PikaDrawable *drawable)
|
||||
{
|
||||
PikaPalette *palette;
|
||||
|
||||
PikaRGB color_sample;
|
||||
gdouble *d_samples, *d_samp;
|
||||
gboolean is_rgb;
|
||||
gdouble factor;
|
||||
gint pal_entry, num_colors;
|
||||
gint nb_color_chan, nb_chan, i;
|
||||
const Babl *format;
|
||||
|
||||
palette = pika_context_get_palette ();
|
||||
num_colors = pika_palette_get_color_count (palette);
|
||||
|
||||
is_rgb = pika_drawable_is_rgb (drawable);
|
||||
|
||||
factor = ((double) num_colors) / NSAMPLES;
|
||||
format = is_rgb ? babl_format ("R'G'B'A double") : babl_format ("Y'A double");
|
||||
nb_color_chan = is_rgb ? 3 : 1;
|
||||
nb_chan = nb_color_chan + 1;
|
||||
|
||||
d_samples = g_new (gdouble, NSAMPLES * nb_chan);
|
||||
|
||||
for (i = 0; i < NSAMPLES; i++)
|
||||
{
|
||||
d_samp = &d_samples[i * nb_chan];
|
||||
pal_entry = CLAMP ((int)(i * factor), 0, num_colors - 1);
|
||||
|
||||
pika_palette_entry_get_color (palette, pal_entry, &color_sample);
|
||||
pika_rgb_get_pixel (&color_sample,
|
||||
format,
|
||||
d_samp);
|
||||
}
|
||||
|
||||
return d_samples;
|
||||
}
|
||||
1087
plug-ins/common/grid.c
Normal file
1087
plug-ins/common/grid.c
Normal file
File diff suppressed because it is too large
Load Diff
365
plug-ins/common/guillotine.c
Normal file
365
plug-ins/common/guillotine.c
Normal file
@ -0,0 +1,365 @@
|
||||
/*
|
||||
* Guillotine plug-in v0.9 by Adam D. Moss, adam@foxbox.org. 1998/09/01
|
||||
*/
|
||||
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <libpika/pika.h>
|
||||
|
||||
#include "libpika/stdplugins-intl.h"
|
||||
|
||||
|
||||
#define PLUG_IN_PROC "plug-in-guillotine"
|
||||
|
||||
|
||||
typedef struct _Guillotine Guillotine;
|
||||
typedef struct _GuillotineClass GuillotineClass;
|
||||
|
||||
struct _Guillotine
|
||||
{
|
||||
PikaPlugIn parent_instance;
|
||||
};
|
||||
|
||||
struct _GuillotineClass
|
||||
{
|
||||
PikaPlugInClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
#define GUILLOTINE_TYPE (guillotine_get_type ())
|
||||
#define GUILLOTINE (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GUILLOTINE_TYPE, Guillotine))
|
||||
|
||||
GType guillotine_get_type (void) G_GNUC_CONST;
|
||||
|
||||
static GList * guillotine_query_procedures (PikaPlugIn *plug_in);
|
||||
static PikaProcedure * guillotine_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name);
|
||||
|
||||
static PikaValueArray * guillotine_run (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data);
|
||||
static GList * guillotine (PikaImage *image,
|
||||
gboolean interactive);
|
||||
|
||||
|
||||
G_DEFINE_TYPE (Guillotine, guillotine, PIKA_TYPE_PLUG_IN)
|
||||
|
||||
PIKA_MAIN (GUILLOTINE_TYPE)
|
||||
DEFINE_STD_SET_I18N
|
||||
|
||||
static void
|
||||
guillotine_class_init (GuillotineClass *klass)
|
||||
{
|
||||
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
|
||||
|
||||
plug_in_class->query_procedures = guillotine_query_procedures;
|
||||
plug_in_class->create_procedure = guillotine_create_procedure;
|
||||
plug_in_class->set_i18n = STD_SET_I18N;
|
||||
}
|
||||
|
||||
static void
|
||||
guillotine_init (Guillotine *film)
|
||||
{
|
||||
}
|
||||
|
||||
static GList *
|
||||
guillotine_query_procedures (PikaPlugIn *plug_in)
|
||||
{
|
||||
return g_list_append (NULL, g_strdup (PLUG_IN_PROC));
|
||||
}
|
||||
|
||||
static PikaProcedure *
|
||||
guillotine_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name)
|
||||
{
|
||||
PikaProcedure *procedure = NULL;
|
||||
|
||||
if (! strcmp (name, PLUG_IN_PROC))
|
||||
{
|
||||
procedure = pika_image_procedure_new (plug_in, name,
|
||||
PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
guillotine_run, NULL, NULL);
|
||||
|
||||
pika_procedure_set_image_types (procedure, "*");
|
||||
pika_procedure_set_sensitivity_mask (procedure,
|
||||
PIKA_PROCEDURE_SENSITIVE_DRAWABLE |
|
||||
PIKA_PROCEDURE_SENSITIVE_DRAWABLES |
|
||||
PIKA_PROCEDURE_SENSITIVE_NO_DRAWABLES);
|
||||
|
||||
pika_procedure_set_menu_label (procedure, _("Slice Using G_uides"));
|
||||
pika_procedure_add_menu_path (procedure, "<Image>/Image/[Crop]");
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
_("Slice the image into subimages "
|
||||
"using guides"),
|
||||
"This function takes an image and "
|
||||
"slices it along its guides, creating "
|
||||
"new images. The original image is "
|
||||
"not modified.",
|
||||
name);
|
||||
pika_procedure_set_attribution (procedure,
|
||||
"Adam D. Moss (adam@foxbox.org)",
|
||||
"Adam D. Moss (adam@foxbox.org)",
|
||||
"1998");
|
||||
|
||||
PIKA_PROC_VAL_INT (procedure, "image-count",
|
||||
"Number of images created",
|
||||
"Number of images created",
|
||||
0, G_MAXINT, 0,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_VAL_OBJECT_ARRAY (procedure, "images",
|
||||
"Output images",
|
||||
"Output images",
|
||||
PIKA_TYPE_IMAGE,
|
||||
G_PARAM_READWRITE);
|
||||
}
|
||||
|
||||
return procedure;
|
||||
}
|
||||
|
||||
static PikaValueArray *
|
||||
guillotine_run (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data)
|
||||
{
|
||||
PikaValueArray *return_vals = NULL;
|
||||
PikaPDBStatusType status = PIKA_PDB_SUCCESS;
|
||||
|
||||
return_vals = pika_procedure_new_return_values (procedure, status,
|
||||
NULL);
|
||||
if (status == PIKA_PDB_SUCCESS)
|
||||
{
|
||||
GList *image_list;
|
||||
GList *list;
|
||||
PikaImage **images;
|
||||
gint num_images;
|
||||
gint i;
|
||||
|
||||
pika_progress_init (_("Guillotine"));
|
||||
|
||||
image_list = guillotine (image, run_mode == PIKA_RUN_INTERACTIVE);
|
||||
|
||||
num_images = g_list_length (image_list);
|
||||
images = g_new (PikaImage *, num_images);
|
||||
|
||||
for (list = image_list, i = 0;
|
||||
list;
|
||||
list = g_list_next (list), i++)
|
||||
{
|
||||
images[i] = g_object_ref (list->data);
|
||||
}
|
||||
|
||||
g_list_free (image_list);
|
||||
|
||||
PIKA_VALUES_SET_INT (return_vals, 1, num_images);
|
||||
PIKA_VALUES_TAKE_OBJECT_ARRAY (return_vals, 2, PIKA_TYPE_IMAGE, images, num_images);
|
||||
|
||||
if (run_mode == PIKA_RUN_INTERACTIVE)
|
||||
pika_displays_flush ();
|
||||
}
|
||||
|
||||
return return_vals;
|
||||
}
|
||||
|
||||
|
||||
static gint
|
||||
guide_sort_func (gconstpointer a,
|
||||
gconstpointer b)
|
||||
{
|
||||
return GPOINTER_TO_INT (a) - GPOINTER_TO_INT (b);
|
||||
}
|
||||
|
||||
static GList *
|
||||
guillotine (PikaImage *image,
|
||||
gboolean interactive)
|
||||
{
|
||||
GList *images = NULL;
|
||||
gint guide;
|
||||
gint image_width;
|
||||
gint image_height;
|
||||
gboolean guides_found = FALSE;
|
||||
GList *hguides, *hg;
|
||||
GList *vguides, *vg;
|
||||
|
||||
image_width = pika_image_get_width (image);
|
||||
image_height = pika_image_get_height (image);
|
||||
|
||||
hguides = g_list_append (NULL, GINT_TO_POINTER (0));
|
||||
hguides = g_list_append (hguides, GINT_TO_POINTER (image_height));
|
||||
|
||||
vguides = g_list_append (NULL, GINT_TO_POINTER (0));
|
||||
vguides = g_list_append (vguides, GINT_TO_POINTER (image_width));
|
||||
|
||||
for (guide = pika_image_find_next_guide (image, 0);
|
||||
guide > 0;
|
||||
guide = pika_image_find_next_guide (image, guide))
|
||||
{
|
||||
gint position = pika_image_get_guide_position (image, guide);
|
||||
|
||||
switch (pika_image_get_guide_orientation (image, guide))
|
||||
{
|
||||
case PIKA_ORIENTATION_HORIZONTAL:
|
||||
if (! g_list_find (hguides, GINT_TO_POINTER (position)))
|
||||
{
|
||||
hguides = g_list_insert_sorted (hguides,
|
||||
GINT_TO_POINTER (position),
|
||||
guide_sort_func);
|
||||
guides_found = TRUE;
|
||||
}
|
||||
break;
|
||||
|
||||
case PIKA_ORIENTATION_VERTICAL:
|
||||
if (! g_list_find (vguides, GINT_TO_POINTER (position)))
|
||||
{
|
||||
vguides = g_list_insert_sorted (vguides,
|
||||
GINT_TO_POINTER (position),
|
||||
guide_sort_func);
|
||||
guides_found = TRUE;
|
||||
}
|
||||
break;
|
||||
|
||||
case PIKA_ORIENTATION_UNKNOWN:
|
||||
g_assert_not_reached ();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (guides_found)
|
||||
{
|
||||
GFile *file;
|
||||
gint h, v, hpad, vpad;
|
||||
gint x, y;
|
||||
gchar *hformat;
|
||||
gchar *format;
|
||||
|
||||
file = pika_image_get_file (image);
|
||||
|
||||
if (! file)
|
||||
file = g_file_new_for_uri (_("Untitled"));
|
||||
|
||||
/* get the number horizontal and vertical slices */
|
||||
h = g_list_length (hguides);
|
||||
v = g_list_length (vguides);
|
||||
|
||||
/* need the number of digits of h and v for the padding */
|
||||
hpad = log10(h) + 1;
|
||||
vpad = log10(v) + 1;
|
||||
|
||||
/* format for the x-y coordinates in the filename */
|
||||
hformat = g_strdup_printf ("%%0%i", MAX (hpad, vpad));
|
||||
format = g_strdup_printf ("-%si-%si", hformat, hformat);
|
||||
|
||||
/* Do the actual dup'ing and cropping... this isn't a too naive a
|
||||
* way to do this since we got copy-on-write tiles, either.
|
||||
*/
|
||||
for (y = 0, hg = hguides; hg && hg->next; y++, hg = hg->next)
|
||||
{
|
||||
for (x = 0, vg = vguides; vg && vg->next; x++, vg = vg->next)
|
||||
{
|
||||
PikaImage *new_image = pika_image_duplicate (image);
|
||||
GString *new_uri;
|
||||
GFile *new_file;
|
||||
gchar *fileextension;
|
||||
gchar *fileindex;
|
||||
|
||||
if (! new_image)
|
||||
{
|
||||
g_warning ("Couldn't create new image.");
|
||||
g_free (hformat);
|
||||
g_free (format);
|
||||
return images;
|
||||
}
|
||||
|
||||
pika_image_undo_disable (new_image);
|
||||
|
||||
pika_image_crop (new_image,
|
||||
GPOINTER_TO_INT (vg->next->data) -
|
||||
GPOINTER_TO_INT (vg->data),
|
||||
GPOINTER_TO_INT (hg->next->data) -
|
||||
GPOINTER_TO_INT (hg->data),
|
||||
GPOINTER_TO_INT (vg->data),
|
||||
GPOINTER_TO_INT (hg->data));
|
||||
|
||||
|
||||
new_uri = g_string_new (g_file_get_uri (file));
|
||||
|
||||
/* show the rough coordinates of the image in the title */
|
||||
fileindex = g_strdup_printf (format, x, y);
|
||||
|
||||
/* get the position of the file extension - last . in filename */
|
||||
fileextension = strrchr (new_uri->str, '.');
|
||||
if (fileextension)
|
||||
{
|
||||
gint pos;
|
||||
|
||||
/* Truncate the extension. */
|
||||
pos = fileextension - new_uri->str;
|
||||
g_string_truncate (new_uri, pos);
|
||||
}
|
||||
|
||||
/* insert the coordinates before the extension */
|
||||
g_string_append (new_uri, fileindex);
|
||||
g_free (fileindex);
|
||||
|
||||
g_string_append (new_uri, ".xcf");
|
||||
|
||||
new_file = g_file_new_for_uri (new_uri->str);
|
||||
g_string_free (new_uri, TRUE);
|
||||
|
||||
pika_image_set_file (new_image, new_file);
|
||||
g_object_unref (new_file);
|
||||
|
||||
while ((guide = pika_image_find_next_guide (new_image, 0)))
|
||||
pika_image_delete_guide (new_image, guide);
|
||||
|
||||
pika_image_undo_enable (new_image);
|
||||
|
||||
if (interactive)
|
||||
pika_display_new (new_image);
|
||||
|
||||
images = g_list_prepend (images, GINT_TO_POINTER (new_image));
|
||||
}
|
||||
}
|
||||
|
||||
g_object_unref (file);
|
||||
g_free (hformat);
|
||||
g_free (format);
|
||||
}
|
||||
|
||||
g_list_free (hguides);
|
||||
g_list_free (vguides);
|
||||
|
||||
return g_list_reverse (images);
|
||||
}
|
||||
837
plug-ins/common/hot.c
Normal file
837
plug-ins/common/hot.c
Normal file
@ -0,0 +1,837 @@
|
||||
/*
|
||||
* PIKA - Photo and Image Kooker Application
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* hot.c - Scan an image for pixels with RGB values that will give
|
||||
* "unsafe" values of chrominance signal or composite signal
|
||||
* amplitude when encoded into an NTSC or PAL color signal.
|
||||
* (This happens for certain high-intensity high-saturation colors
|
||||
* that are rare in real scenes, but can easily be present
|
||||
* in synthetic images.)
|
||||
*
|
||||
* Such pixels can be flagged so the user may then choose other
|
||||
* colors. Or, the offending pixels can be made "safe"
|
||||
* in a manner that preserves hue.
|
||||
*
|
||||
* There are two reasonable ways to make a pixel "safe":
|
||||
* We can reduce its intensity (luminance) while leaving
|
||||
* hue and saturation the same. Or, we can reduce saturation
|
||||
* while leaving hue and luminance the same. A #define selects
|
||||
* which strategy to use.
|
||||
*
|
||||
* Note to the user: You must add your own read_pixel() and write_pixel()
|
||||
* routines. You may have to modify pix_decode() and pix_encode().
|
||||
* MAXPIX, WID, and HGT are likely to need modification.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Originally written as "ikNTSC.c" by Alan Wm Paeth,
|
||||
* University of Waterloo, August, 1985
|
||||
* Updated by Dave Martindale, Imax Systems Corp., December 1990
|
||||
*/
|
||||
|
||||
/*
|
||||
* Compile time options:
|
||||
*
|
||||
*
|
||||
* CHROMA_LIM is the limit (in IRE units) of the overall
|
||||
* chrominance amplitude; it should be 50 or perhaps
|
||||
* very slightly higher.
|
||||
*
|
||||
* COMPOS_LIM is the maximum amplitude (in IRE units) allowed for
|
||||
* the composite signal. A value of 100 is the maximum
|
||||
* monochrome white, and is always safe. 120 is the absolute
|
||||
* limit for NTSC broadcasting, since the transmitter's carrier
|
||||
* goes to zero with 120 IRE input signal. Generally, 110
|
||||
* is a good compromise - it allows somewhat brighter colors
|
||||
* than 100, while staying safely away from the hard limit.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <libpika/pika.h>
|
||||
#include <libpika/pikaui.h>
|
||||
|
||||
#include "libpika/stdplugins-intl.h"
|
||||
|
||||
|
||||
#define PLUG_IN_PROC "plug-in-hot"
|
||||
#define PLUG_IN_BINARY "hot"
|
||||
#define PLUG_IN_ROLE "pika-hot"
|
||||
|
||||
|
||||
typedef enum
|
||||
{
|
||||
ACT_LREDUX,
|
||||
ACT_SREDUX,
|
||||
ACT_FLAG
|
||||
} hotAction;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
MODE_NTSC,
|
||||
MODE_PAL
|
||||
} hotModes;
|
||||
|
||||
#define CHROMA_LIM 50.0 /* chroma amplitude limit */
|
||||
#define COMPOS_LIM 110.0 /* max IRE amplitude */
|
||||
|
||||
/*
|
||||
* RGB to YIQ encoding matrix.
|
||||
*/
|
||||
|
||||
struct
|
||||
{
|
||||
gdouble pedestal;
|
||||
gdouble gamma;
|
||||
gdouble code[3][3];
|
||||
}
|
||||
static mode_vals[2] =
|
||||
{
|
||||
{
|
||||
7.5,
|
||||
2.2,
|
||||
{
|
||||
{ 0.2989, 0.5866, 0.1144 },
|
||||
{ 0.5959, -0.2741, -0.3218 },
|
||||
{ 0.2113, -0.5227, 0.3113 }
|
||||
}
|
||||
},
|
||||
{
|
||||
0.0,
|
||||
2.8,
|
||||
{
|
||||
{ 0.2989, 0.5866, 0.1144 },
|
||||
{ -0.1473, -0.2891, 0.4364 },
|
||||
{ 0.6149, -0.5145, -0.1004 }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#define SCALE 8192 /* scale factor: do floats with int math */
|
||||
#define MAXPIX 255 /* white value */
|
||||
|
||||
|
||||
typedef struct _Hot Hot;
|
||||
typedef struct _HotClass HotClass;
|
||||
|
||||
struct _Hot
|
||||
{
|
||||
PikaPlugIn parent_instance;
|
||||
};
|
||||
|
||||
struct _HotClass
|
||||
{
|
||||
PikaPlugInClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
#define HOT_TYPE (hot_get_type ())
|
||||
#define HOT (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), HOT_TYPE, Hot))
|
||||
|
||||
GType hot_get_type (void) G_GNUC_CONST;
|
||||
|
||||
static GList * hot_query_procedures (PikaPlugIn *plug_in);
|
||||
static PikaProcedure * hot_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name);
|
||||
|
||||
static PikaValueArray * hot_run (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data);
|
||||
|
||||
static gboolean pluginCore (PikaImage *image,
|
||||
PikaDrawable *drawable,
|
||||
GObject *config);
|
||||
static gboolean plugin_dialog (PikaProcedure *procedure,
|
||||
GObject *config);
|
||||
static gboolean hotp (guint8 r,
|
||||
guint8 g,
|
||||
guint8 b);
|
||||
static void build_tab (gint m);
|
||||
|
||||
/*
|
||||
* gc: apply the gamma correction specified for this video standard.
|
||||
* inv_gc: inverse function of gc.
|
||||
*
|
||||
* These are generally just a call to pow(), but be careful!
|
||||
* Future standards may use more complex functions.
|
||||
* (e.g. SMPTE 240M's "electro-optic transfer characteristic").
|
||||
*/
|
||||
#define gc(x,m) pow(x, 1.0 / mode_vals[m].gamma)
|
||||
#define inv_gc(x,m) pow(x, mode_vals[m].gamma)
|
||||
|
||||
/*
|
||||
* pix_decode: decode an integer pixel value into a floating-point
|
||||
* intensity in the range [0, 1].
|
||||
*
|
||||
* pix_encode: encode a floating-point intensity into an integer
|
||||
* pixel value.
|
||||
*
|
||||
* The code given here assumes simple linear encoding; you must change
|
||||
* these routines if you use a different pixel encoding technique.
|
||||
*/
|
||||
#define pix_decode(v) ((double)v / (double)MAXPIX)
|
||||
#define pix_encode(v) ((int)(v * (double)MAXPIX + 0.5))
|
||||
|
||||
|
||||
G_DEFINE_TYPE (Hot, hot, PIKA_TYPE_PLUG_IN)
|
||||
|
||||
PIKA_MAIN (HOT_TYPE)
|
||||
DEFINE_STD_SET_I18N
|
||||
|
||||
|
||||
static gint tab[3][3][MAXPIX+1]; /* multiply lookup table */
|
||||
static gdouble chroma_lim; /* chroma limit */
|
||||
static gdouble compos_lim; /* composite amplitude limit */
|
||||
static glong ichroma_lim2; /* chroma limit squared (scaled integer) */
|
||||
static gint icompos_lim; /* composite amplitude limit (scaled integer) */
|
||||
|
||||
|
||||
static void
|
||||
hot_class_init (HotClass *klass)
|
||||
{
|
||||
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
|
||||
|
||||
plug_in_class->query_procedures = hot_query_procedures;
|
||||
plug_in_class->create_procedure = hot_create_procedure;
|
||||
plug_in_class->set_i18n = STD_SET_I18N;
|
||||
}
|
||||
|
||||
static void
|
||||
hot_init (Hot *hot)
|
||||
{
|
||||
}
|
||||
|
||||
static GList *
|
||||
hot_query_procedures (PikaPlugIn *plug_in)
|
||||
{
|
||||
return g_list_append (NULL, g_strdup (PLUG_IN_PROC));
|
||||
}
|
||||
|
||||
static PikaProcedure *
|
||||
hot_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name)
|
||||
{
|
||||
PikaProcedure *procedure = NULL;
|
||||
|
||||
if (! strcmp (name, PLUG_IN_PROC))
|
||||
{
|
||||
procedure = pika_image_procedure_new (plug_in, name,
|
||||
PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
hot_run, NULL, NULL);
|
||||
|
||||
pika_procedure_set_image_types (procedure, "RGB");
|
||||
pika_procedure_set_sensitivity_mask (procedure,
|
||||
PIKA_PROCEDURE_SENSITIVE_DRAWABLE);
|
||||
|
||||
pika_procedure_set_menu_label (procedure, _("_Hot..."));
|
||||
pika_procedure_add_menu_path (procedure, "<Image>/Colors/[Modify]");
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
_("Find and fix pixels that may "
|
||||
"be unsafely bright"),
|
||||
"hot scans an image for pixels that "
|
||||
"will give unsave values of "
|
||||
"chrominance or composite signale "
|
||||
"amplitude when encoded into an NTSC "
|
||||
"or PAL signal. Three actions can be "
|
||||
"performed on these 'hot' pixels. "
|
||||
"(0) reduce luminance, "
|
||||
"(1) reduce saturation, or (2) Blacken.",
|
||||
name);
|
||||
pika_procedure_set_attribution (procedure,
|
||||
"Eric L. Hernes, Alan Wm Paeth",
|
||||
"Eric L. Hernes",
|
||||
"1997");
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "mode",
|
||||
_("Mode"),
|
||||
"Mode { NTSC (0), PAL (1) }",
|
||||
0, 1, MODE_NTSC,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "action",
|
||||
_("Action"),
|
||||
"Action { (0) reduce luminance, "
|
||||
"(1) reduce saturation, or (2) Blacken }",
|
||||
0, 2, ACT_LREDUX,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_BOOLEAN (procedure, "new-layer",
|
||||
_("Create _new layer"),
|
||||
"Create a new layer",
|
||||
TRUE,
|
||||
G_PARAM_READWRITE);
|
||||
}
|
||||
|
||||
return procedure;
|
||||
}
|
||||
|
||||
static PikaValueArray *
|
||||
hot_run (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data)
|
||||
{
|
||||
PikaProcedureConfig *config;
|
||||
PikaDrawable *drawable;
|
||||
|
||||
gegl_init (NULL, NULL);
|
||||
|
||||
if (n_drawables != 1)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
g_set_error (&error, PIKA_PLUG_IN_ERROR, 0,
|
||||
_("Procedure '%s' only works with one drawable."),
|
||||
pika_procedure_get_name (procedure));
|
||||
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_CALLING_ERROR,
|
||||
error);
|
||||
}
|
||||
else
|
||||
{
|
||||
drawable = drawables[0];
|
||||
}
|
||||
|
||||
config = pika_procedure_create_config (procedure);
|
||||
pika_procedure_config_begin_run (config, NULL, run_mode, args);
|
||||
|
||||
switch (run_mode)
|
||||
{
|
||||
case PIKA_RUN_INTERACTIVE:
|
||||
|
||||
if (! plugin_dialog (procedure, G_OBJECT (config)))
|
||||
{
|
||||
pika_procedure_config_end_run (config, PIKA_PDB_CANCEL);
|
||||
g_object_unref (config);
|
||||
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_CANCEL,
|
||||
NULL);
|
||||
}
|
||||
break;
|
||||
|
||||
case PIKA_RUN_NONINTERACTIVE:
|
||||
case PIKA_RUN_WITH_LAST_VALS:
|
||||
break;
|
||||
}
|
||||
|
||||
if (! pluginCore (image, drawable, G_OBJECT (config)))
|
||||
{
|
||||
pika_procedure_config_end_run (config, PIKA_PDB_CANCEL);
|
||||
g_object_unref (config);
|
||||
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_EXECUTION_ERROR,
|
||||
NULL);
|
||||
}
|
||||
|
||||
pika_procedure_config_end_run (config, PIKA_PDB_SUCCESS);
|
||||
g_object_unref (config);
|
||||
|
||||
if (run_mode != PIKA_RUN_NONINTERACTIVE)
|
||||
pika_displays_flush ();
|
||||
|
||||
return pika_procedure_new_return_values (procedure, PIKA_PDB_SUCCESS, NULL);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
pluginCore (PikaImage *image,
|
||||
PikaDrawable *drawable,
|
||||
GObject *config)
|
||||
{
|
||||
gint mode;
|
||||
gint action;
|
||||
gboolean new_layer;
|
||||
GeglBuffer *src_buffer;
|
||||
GeglBuffer *dest_buffer;
|
||||
const Babl *src_format;
|
||||
const Babl *dest_format;
|
||||
gint src_bpp;
|
||||
gint dest_bpp;
|
||||
gboolean success = TRUE;
|
||||
PikaLayer *nl = NULL;
|
||||
gint y, i;
|
||||
gint Y, I, Q;
|
||||
gint width, height;
|
||||
gint sel_x1, sel_x2, sel_y1, sel_y2;
|
||||
gint prog_interval;
|
||||
guchar *src, *s, *dst, *d;
|
||||
guchar r, prev_r=0, new_r=0;
|
||||
guchar g, prev_g=0, new_g=0;
|
||||
guchar b, prev_b=0, new_b=0;
|
||||
gdouble fy, fc, t, scale;
|
||||
gdouble pr, pg, pb;
|
||||
gdouble py;
|
||||
|
||||
g_object_get (config,
|
||||
"mode", &mode,
|
||||
"action", &action,
|
||||
"new-layer", &new_layer,
|
||||
NULL);
|
||||
|
||||
width = pika_drawable_get_width (drawable);
|
||||
height = pika_drawable_get_height (drawable);
|
||||
|
||||
if (pika_drawable_has_alpha (drawable))
|
||||
src_format = babl_format ("R'G'B'A u8");
|
||||
else
|
||||
src_format = babl_format ("R'G'B' u8");
|
||||
|
||||
dest_format = src_format;
|
||||
|
||||
if (new_layer)
|
||||
{
|
||||
gchar name[40];
|
||||
const gchar *mode_names[] =
|
||||
{
|
||||
"ntsc",
|
||||
"pal",
|
||||
};
|
||||
const gchar *action_names[] =
|
||||
{
|
||||
"lum redux",
|
||||
"sat redux",
|
||||
"flag",
|
||||
};
|
||||
|
||||
g_snprintf (name, sizeof (name), "hot mask (%s, %s)",
|
||||
mode_names[mode],
|
||||
action_names[action]);
|
||||
|
||||
nl = pika_layer_new (image, name, width, height,
|
||||
PIKA_RGBA_IMAGE,
|
||||
100,
|
||||
pika_image_get_default_new_layer_mode (image));
|
||||
|
||||
pika_drawable_fill (PIKA_DRAWABLE (nl), PIKA_FILL_TRANSPARENT);
|
||||
pika_image_insert_layer (image, nl, NULL, 0);
|
||||
|
||||
dest_format = babl_format ("R'G'B'A u8");
|
||||
}
|
||||
|
||||
if (! pika_drawable_mask_intersect (drawable,
|
||||
&sel_x1, &sel_y1, &width, &height))
|
||||
return success;
|
||||
|
||||
src_bpp = babl_format_get_bytes_per_pixel (src_format);
|
||||
dest_bpp = babl_format_get_bytes_per_pixel (dest_format);
|
||||
|
||||
sel_x2 = sel_x1 + width;
|
||||
sel_y2 = sel_y1 + height;
|
||||
|
||||
src = g_new (guchar, width * height * src_bpp);
|
||||
dst = g_new (guchar, width * height * dest_bpp);
|
||||
|
||||
src_buffer = pika_drawable_get_buffer (drawable);
|
||||
|
||||
if (new_layer)
|
||||
{
|
||||
dest_buffer = pika_drawable_get_buffer (PIKA_DRAWABLE (nl));
|
||||
}
|
||||
else
|
||||
{
|
||||
dest_buffer = pika_drawable_get_shadow_buffer (drawable);
|
||||
}
|
||||
|
||||
gegl_buffer_get (src_buffer,
|
||||
GEGL_RECTANGLE (sel_x1, sel_y1, width, height), 1.0,
|
||||
src_format, src,
|
||||
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
|
||||
|
||||
s = src;
|
||||
d = dst;
|
||||
|
||||
build_tab (mode);
|
||||
|
||||
pika_progress_init (_("Hot"));
|
||||
prog_interval = height / 10;
|
||||
|
||||
for (y = sel_y1; y < sel_y2; y++)
|
||||
{
|
||||
gint x;
|
||||
|
||||
if (y % prog_interval == 0)
|
||||
pika_progress_update ((double) y / (double) (sel_y2 - sel_y1));
|
||||
|
||||
for (x = sel_x1; x < sel_x2; x++)
|
||||
{
|
||||
if (hotp (r = *(s + 0), g = *(s + 1), b = *(s + 2)))
|
||||
{
|
||||
if (action == ACT_FLAG)
|
||||
{
|
||||
for (i = 0; i < 3; i++)
|
||||
*d++ = 0;
|
||||
s += 3;
|
||||
if (src_bpp == 4)
|
||||
*d++ = *s++;
|
||||
else if (new_layer)
|
||||
*d++ = 255;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Optimization: cache the last-computed hot pixel.
|
||||
*/
|
||||
if (r == prev_r && g == prev_g && b == prev_b)
|
||||
{
|
||||
*d++ = new_r;
|
||||
*d++ = new_g;
|
||||
*d++ = new_b;
|
||||
s += 3;
|
||||
if (src_bpp == 4)
|
||||
*d++ = *s++;
|
||||
else if (new_layer)
|
||||
*d++ = 255;
|
||||
}
|
||||
else
|
||||
{
|
||||
Y = tab[0][0][r] + tab[0][1][g] + tab[0][2][b];
|
||||
I = tab[1][0][r] + tab[1][1][g] + tab[1][2][b];
|
||||
Q = tab[2][0][r] + tab[2][1][g] + tab[2][2][b];
|
||||
|
||||
prev_r = r;
|
||||
prev_g = g;
|
||||
prev_b = b;
|
||||
/*
|
||||
* Get Y and chroma amplitudes in floating point.
|
||||
*
|
||||
* If your C library doesn't have hypot(), just use
|
||||
* hypot(a,b) = sqrt(a*a, b*b);
|
||||
*
|
||||
* Then extract linear (un-gamma-corrected)
|
||||
* floating-point pixel RGB values.
|
||||
*/
|
||||
fy = (double)Y / (double)SCALE;
|
||||
fc = hypot ((double) I / (double) SCALE,
|
||||
(double) Q / (double) SCALE);
|
||||
|
||||
pr = (double) pix_decode (r);
|
||||
pg = (double) pix_decode (g);
|
||||
pb = (double) pix_decode (b);
|
||||
|
||||
/*
|
||||
* Reducing overall pixel intensity by scaling R,
|
||||
* G, and B reduces Y, I, and Q by the same factor.
|
||||
* This changes luminance but not saturation, since
|
||||
* saturation is determined by the chroma/luminance
|
||||
* ratio.
|
||||
*
|
||||
* On the other hand, by linearly interpolating
|
||||
* between the original pixel value and a grey
|
||||
* pixel with the same luminance (R=G=B=Y), we
|
||||
* change saturation without affecting luminance.
|
||||
*/
|
||||
if (action == ACT_LREDUX)
|
||||
{
|
||||
/*
|
||||
* Calculate a scale factor that will bring the pixel
|
||||
* within both chroma and composite limits, if we scale
|
||||
* luminance and chroma simultaneously.
|
||||
*
|
||||
* The calculated chrominance reduction applies
|
||||
* to the gamma-corrected RGB values that are
|
||||
* the input to the RGB-to-YIQ operation.
|
||||
* Multiplying the original un-gamma-corrected
|
||||
* pixel values by the scaling factor raised to
|
||||
* the "gamma" power is equivalent, and avoids
|
||||
* calling gc() and inv_gc() three times each. */
|
||||
scale = chroma_lim / fc;
|
||||
t = compos_lim / (fy + fc);
|
||||
if (t < scale)
|
||||
scale = t;
|
||||
scale = pow (scale, mode_vals[mode].gamma);
|
||||
|
||||
r = (guint8) pix_encode (scale * pr);
|
||||
g = (guint8) pix_encode (scale * pg);
|
||||
b = (guint8) pix_encode (scale * pb);
|
||||
}
|
||||
else
|
||||
{ /* ACT_SREDUX hopefully */
|
||||
/*
|
||||
* Calculate a scale factor that will bring the
|
||||
* pixel within both chroma and composite
|
||||
* limits, if we scale chroma while leaving
|
||||
* luminance unchanged.
|
||||
*
|
||||
* We have to interpolate gamma-corrected RGB
|
||||
* values, so we must convert from linear to
|
||||
* gamma-corrected before interpolation and then
|
||||
* back to linear afterwards.
|
||||
*/
|
||||
scale = chroma_lim / fc;
|
||||
t = (compos_lim - fy) / fc;
|
||||
if (t < scale)
|
||||
scale = t;
|
||||
|
||||
pr = gc (pr, mode);
|
||||
pg = gc (pg, mode);
|
||||
pb = gc (pb, mode);
|
||||
|
||||
py = pr * mode_vals[mode].code[0][0] +
|
||||
pg * mode_vals[mode].code[0][1] +
|
||||
pb * mode_vals[mode].code[0][2];
|
||||
|
||||
r = pix_encode (inv_gc (py + scale * (pr - py),
|
||||
mode));
|
||||
g = pix_encode (inv_gc (py + scale * (pg - py),
|
||||
mode));
|
||||
b = pix_encode (inv_gc (py + scale * (pb - py),
|
||||
mode));
|
||||
}
|
||||
|
||||
*d++ = new_r = r;
|
||||
*d++ = new_g = g;
|
||||
*d++ = new_b = b;
|
||||
|
||||
s += 3;
|
||||
|
||||
if (src_bpp == 4)
|
||||
*d++ = *s++;
|
||||
else if (new_layer)
|
||||
*d++ = 255;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (! new_layer)
|
||||
{
|
||||
for (i = 0; i < src_bpp; i++)
|
||||
*d++ = *s++;
|
||||
}
|
||||
else
|
||||
{
|
||||
s += src_bpp;
|
||||
d += dest_bpp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gegl_buffer_set (dest_buffer,
|
||||
GEGL_RECTANGLE (sel_x1, sel_y1, width, height), 0,
|
||||
dest_format, dst,
|
||||
GEGL_AUTO_ROWSTRIDE);
|
||||
|
||||
pika_progress_update (1.0);
|
||||
|
||||
g_free (src);
|
||||
g_free (dst);
|
||||
|
||||
g_object_unref (src_buffer);
|
||||
g_object_unref (dest_buffer);
|
||||
|
||||
if (new_layer)
|
||||
{
|
||||
pika_drawable_update (PIKA_DRAWABLE (nl), sel_x1, sel_y1, width, height);
|
||||
}
|
||||
else
|
||||
{
|
||||
pika_drawable_merge_shadow (drawable, TRUE);
|
||||
pika_drawable_update (drawable, sel_x1, sel_y1, width, height);
|
||||
}
|
||||
|
||||
pika_displays_flush ();
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
plugin_dialog (PikaProcedure *procedure,
|
||||
GObject *config)
|
||||
{
|
||||
GtkWidget *dlg;
|
||||
GtkWidget *vbox;
|
||||
GtkWidget *hbox;
|
||||
GtkListStore *store;
|
||||
gboolean run;
|
||||
|
||||
pika_ui_init (PLUG_IN_BINARY);
|
||||
|
||||
dlg = pika_procedure_dialog_new (procedure,
|
||||
PIKA_PROCEDURE_CONFIG (config),
|
||||
_("Hot"));
|
||||
|
||||
pika_dialog_set_alternative_button_order (GTK_DIALOG (dlg),
|
||||
GTK_RESPONSE_OK,
|
||||
GTK_RESPONSE_CANCEL,
|
||||
-1);
|
||||
|
||||
pika_window_set_transient (GTK_WINDOW (dlg));
|
||||
|
||||
store = pika_int_store_new (_("N_TSC"), MODE_NTSC,
|
||||
_("_PAL"), MODE_PAL,
|
||||
NULL);
|
||||
pika_procedure_dialog_get_int_radio (PIKA_PROCEDURE_DIALOG (dlg),
|
||||
"mode", PIKA_INT_STORE (store));
|
||||
|
||||
store = pika_int_store_new (_("Reduce _Luminance"), ACT_LREDUX,
|
||||
_("Reduce _Saturation"), ACT_SREDUX,
|
||||
_("_Blacken"), ACT_FLAG,
|
||||
NULL);
|
||||
pika_procedure_dialog_get_int_radio (PIKA_PROCEDURE_DIALOG (dlg),
|
||||
"action", PIKA_INT_STORE (store));
|
||||
|
||||
vbox = pika_procedure_dialog_fill_box (PIKA_PROCEDURE_DIALOG (dlg),
|
||||
"hot-left-side",
|
||||
"mode",
|
||||
"new-layer",
|
||||
NULL);
|
||||
gtk_box_set_spacing (GTK_BOX (vbox), 12);
|
||||
|
||||
hbox = pika_procedure_dialog_fill_box (PIKA_PROCEDURE_DIALOG (dlg),
|
||||
"hot-hbox",
|
||||
"hot-left-side",
|
||||
"action",
|
||||
NULL);
|
||||
gtk_box_set_spacing (GTK_BOX (hbox), 12);
|
||||
gtk_box_set_homogeneous (GTK_BOX (hbox), TRUE);
|
||||
gtk_widget_set_margin_bottom (hbox, 12);
|
||||
gtk_orientable_set_orientation (GTK_ORIENTABLE (hbox),
|
||||
GTK_ORIENTATION_HORIZONTAL);
|
||||
|
||||
pika_procedure_dialog_fill (PIKA_PROCEDURE_DIALOG (dlg),
|
||||
"hot-hbox",
|
||||
NULL);
|
||||
|
||||
gtk_widget_show (dlg);
|
||||
|
||||
run = pika_procedure_dialog_run (PIKA_PROCEDURE_DIALOG (dlg));
|
||||
|
||||
gtk_widget_destroy (dlg);
|
||||
|
||||
return run;
|
||||
}
|
||||
|
||||
/*
|
||||
* build_tab: Build multiply lookup table.
|
||||
*
|
||||
* For each possible pixel value, decode value into floating-point
|
||||
* intensity. Then do gamma correction required by the video
|
||||
* standard. Scale the result by our fixed-point scale factor.
|
||||
* Then calculate 9 lookup table entries for this pixel value.
|
||||
*
|
||||
* We also calculate floating-point and scaled integer versions
|
||||
* of our limits here. This prevents evaluating expressions every pixel
|
||||
* when the compiler is too stupid to evaluate constant-valued
|
||||
* floating-point expressions at compile time.
|
||||
*
|
||||
* For convenience, the limits are #defined using IRE units.
|
||||
* We must convert them here into the units in which YIQ
|
||||
* are measured. The conversion from IRE to internal units
|
||||
* depends on the pedestal level in use, since as Y goes from
|
||||
* 0 to 1, the signal goes from the pedestal level to 100 IRE.
|
||||
* Chroma is always scaled to remain consistent with Y.
|
||||
*/
|
||||
static void
|
||||
build_tab (int m)
|
||||
{
|
||||
double f;
|
||||
int pv;
|
||||
|
||||
for (pv = 0; pv <= MAXPIX; pv++)
|
||||
{
|
||||
f = (double) SCALE * (double) gc ((double) pix_decode (pv), m);
|
||||
tab[0][0][pv] = (int) (f * mode_vals[m].code[0][0] + 0.5);
|
||||
tab[0][1][pv] = (int) (f * mode_vals[m].code[0][1] + 0.5);
|
||||
tab[0][2][pv] = (int) (f * mode_vals[m].code[0][2] + 0.5);
|
||||
tab[1][0][pv] = (int) (f * mode_vals[m].code[1][0] + 0.5);
|
||||
tab[1][1][pv] = (int) (f * mode_vals[m].code[1][1] + 0.5);
|
||||
tab[1][2][pv] = (int) (f * mode_vals[m].code[1][2] + 0.5);
|
||||
tab[2][0][pv] = (int) (f * mode_vals[m].code[2][0] + 0.5);
|
||||
tab[2][1][pv] = (int) (f * mode_vals[m].code[2][1] + 0.5);
|
||||
tab[2][2][pv] = (int) (f * mode_vals[m].code[2][2] + 0.5);
|
||||
}
|
||||
|
||||
chroma_lim = (double) CHROMA_LIM / (100.0 - mode_vals[m].pedestal);
|
||||
compos_lim = ((double )COMPOS_LIM - mode_vals[m].pedestal) /
|
||||
(100.0 - mode_vals[m].pedestal);
|
||||
|
||||
ichroma_lim2 = (int)(chroma_lim * SCALE + 0.5);
|
||||
ichroma_lim2 *= ichroma_lim2;
|
||||
icompos_lim = (int)(compos_lim * SCALE + 0.5);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
hotp (guint8 r,
|
||||
guint8 g,
|
||||
guint8 b)
|
||||
{
|
||||
int y, i, q;
|
||||
long y2, c2;
|
||||
|
||||
/*
|
||||
* Pixel decoding, gamma correction, and matrix multiplication
|
||||
* all done by lookup table.
|
||||
*
|
||||
* "i" and "q" are the two chrominance components;
|
||||
* they are I and Q for NTSC.
|
||||
* For PAL, "i" is U (scaled B-Y) and "q" is V (scaled R-Y).
|
||||
* Since we only care about the length of the chroma vector,
|
||||
* not its angle, we don't care which is which.
|
||||
*/
|
||||
y = tab[0][0][r] + tab[0][1][g] + tab[0][2][b];
|
||||
i = tab[1][0][r] + tab[1][1][g] + tab[1][2][b];
|
||||
q = tab[2][0][r] + tab[2][1][g] + tab[2][2][b];
|
||||
|
||||
/*
|
||||
* Check to see if the chrominance vector is too long or the
|
||||
* composite waveform amplitude is too large.
|
||||
*
|
||||
* Chrominance is too large if
|
||||
*
|
||||
* sqrt(i^2, q^2) > chroma_lim.
|
||||
*
|
||||
* The composite signal amplitude is too large if
|
||||
*
|
||||
* y + sqrt(i^2, q^2) > compos_lim.
|
||||
*
|
||||
* We avoid doing the sqrt by checking
|
||||
*
|
||||
* i^2 + q^2 > chroma_lim^2
|
||||
* and
|
||||
* y + sqrt(i^2 + q^2) > compos_lim
|
||||
* sqrt(i^2 + q^2) > compos_lim - y
|
||||
* i^2 + q^2 > (compos_lim - y)^2
|
||||
*
|
||||
*/
|
||||
|
||||
c2 = (long)i * i + (long)q * q;
|
||||
y2 = (long)icompos_lim - y;
|
||||
y2 *= y2;
|
||||
|
||||
if (c2 <= ichroma_lim2 && c2 <= y2)
|
||||
{ /* no problems */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
2575
plug-ins/common/jigsaw.c
Normal file
2575
plug-ins/common/jigsaw.c
Normal file
File diff suppressed because it is too large
Load Diff
901
plug-ins/common/mail.c
Normal file
901
plug-ins/common/mail.c
Normal file
@ -0,0 +1,901 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
* Copyright (C) 1997 Daniel Risacher
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* GUMP - Pika Useless Mail Plugin
|
||||
* (or Gump Useless Mail Plugin if you prefer)
|
||||
*
|
||||
* by Adrian Likins <adrian@gimp.org>
|
||||
* MIME encapsulation by Reagan Blundell <reagan@emails.net>
|
||||
*
|
||||
* As always: The utility of this plugin is left as an exercise for
|
||||
* the reader
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#ifdef SENDMAIL
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#endif
|
||||
|
||||
#include <glib/gstdio.h>
|
||||
|
||||
#include <libpika/pika.h>
|
||||
#include <libpika/pikaui.h>
|
||||
|
||||
#include "libpika/stdplugins-intl.h"
|
||||
|
||||
|
||||
#define BUFFER_SIZE 256
|
||||
|
||||
#define PLUG_IN_PROC "plug-in-mail-image"
|
||||
#define PLUG_IN_BINARY "mail"
|
||||
#define PLUG_IN_ROLE "pika-mail"
|
||||
|
||||
|
||||
typedef struct _Mail Mail;
|
||||
typedef struct _MailClass MailClass;
|
||||
|
||||
struct _Mail
|
||||
{
|
||||
PikaPlugIn parent_instance;
|
||||
};
|
||||
|
||||
struct _MailClass
|
||||
{
|
||||
PikaPlugInClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
#define MAIL_TYPE (mail_get_type ())
|
||||
#define MAIL (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MAIL_TYPE, Mail))
|
||||
|
||||
GType mail_get_type (void) G_GNUC_CONST;
|
||||
|
||||
static GList * mail_init_procedures (PikaPlugIn *plug_in);
|
||||
static PikaProcedure * mail_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name);
|
||||
|
||||
static PikaValueArray * mail_run (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data);
|
||||
|
||||
static PikaPDBStatusType send_image (GObject *config,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
gint32 run_mode);
|
||||
|
||||
static gboolean send_dialog (PikaProcedure *procedure,
|
||||
GObject *config);
|
||||
static gboolean valid_file (GFile *file);
|
||||
static gchar * find_extension (const gchar *filename);
|
||||
|
||||
#ifdef SENDMAIL
|
||||
static gchar * sendmail_content_type (const gchar *filename);
|
||||
static void sendmail_create_headers (FILE *mailpipe,
|
||||
GObject *config);
|
||||
static gboolean sendmail_to64 (const gchar *filename,
|
||||
FILE *outfile,
|
||||
GError **error);
|
||||
static FILE * sendmail_pipe (gchar **cmd,
|
||||
GPid *pid);
|
||||
#endif
|
||||
|
||||
|
||||
G_DEFINE_TYPE (Mail, mail, PIKA_TYPE_PLUG_IN)
|
||||
|
||||
PIKA_MAIN (MAIL_TYPE)
|
||||
DEFINE_STD_SET_I18N
|
||||
|
||||
|
||||
#ifdef SENDMAIL
|
||||
static gchar *mesg_body = NULL;
|
||||
#endif
|
||||
|
||||
|
||||
static void
|
||||
mail_class_init (MailClass *klass)
|
||||
{
|
||||
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
|
||||
|
||||
plug_in_class->init_procedures = mail_init_procedures;
|
||||
plug_in_class->create_procedure = mail_create_procedure;
|
||||
plug_in_class->set_i18n = STD_SET_I18N;
|
||||
}
|
||||
|
||||
static void
|
||||
mail_init (Mail *mail)
|
||||
{
|
||||
}
|
||||
|
||||
static GList *
|
||||
mail_init_procedures (PikaPlugIn *plug_in)
|
||||
{
|
||||
GList *list = NULL;
|
||||
gchar *email_bin;
|
||||
|
||||
/* Check if xdg-email or sendmail is installed.
|
||||
* TODO: allow setting the location of the executable in preferences.
|
||||
*/
|
||||
#ifdef SENDMAIL
|
||||
if (strlen (SENDMAIL) == 0)
|
||||
{
|
||||
email_bin = g_find_program_in_path ("sendmail");
|
||||
}
|
||||
else
|
||||
{
|
||||
/* If a directory has been set at build time, we assume that sendmail
|
||||
* can only be in this directory. */
|
||||
email_bin = g_build_filename (SENDMAIL, "sendmail", NULL);
|
||||
if (! g_file_test (email_bin, G_FILE_TEST_IS_EXECUTABLE))
|
||||
{
|
||||
g_free (email_bin);
|
||||
email_bin = NULL;
|
||||
}
|
||||
}
|
||||
#else
|
||||
email_bin = g_find_program_in_path ("xdg-email");
|
||||
#endif
|
||||
|
||||
if (email_bin)
|
||||
list = g_list_append (list, g_strdup (PLUG_IN_PROC));
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
static PikaProcedure *
|
||||
mail_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name)
|
||||
{
|
||||
PikaProcedure *procedure = NULL;
|
||||
|
||||
if (! strcmp (name, PLUG_IN_PROC))
|
||||
{
|
||||
procedure = pika_image_procedure_new (plug_in, name,
|
||||
PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
mail_run, NULL, NULL);
|
||||
|
||||
pika_procedure_set_image_types (procedure, "*");
|
||||
pika_procedure_set_sensitivity_mask (procedure,
|
||||
PIKA_PROCEDURE_SENSITIVE_DRAWABLE |
|
||||
PIKA_PROCEDURE_SENSITIVE_DRAWABLES |
|
||||
PIKA_PROCEDURE_SENSITIVE_NO_DRAWABLES);
|
||||
|
||||
pika_procedure_set_menu_label (procedure, _("Send by E_mail..."));
|
||||
pika_procedure_set_icon_name (procedure, PIKA_ICON_EDIT);
|
||||
pika_procedure_add_menu_path (procedure, "<Image>/File/[Send]");
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
_("Send the image by email"),
|
||||
#ifdef SENDMAIL
|
||||
_("Sendmail is used to send emails "
|
||||
"and must be properly configured."),
|
||||
#else /* xdg-email */
|
||||
_("The preferred email composer is "
|
||||
"used to send emails and must be "
|
||||
"properly configured."),
|
||||
#endif
|
||||
name);
|
||||
pika_procedure_set_attribution (procedure,
|
||||
"Adrian Likins, Reagan Blundell",
|
||||
"Adrian Likins, Reagan Blundell, "
|
||||
"Daniel Risacher, "
|
||||
"Spencer Kimball and Peter Mattis",
|
||||
"1995-1997");
|
||||
|
||||
PIKA_PROC_ARG_STRING (procedure, "filename",
|
||||
_("File_name"),
|
||||
_("The name of the file to save the image in"),
|
||||
NULL,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_STRING (procedure, "to-address",
|
||||
_("_To"),
|
||||
_("The email address to send to"),
|
||||
"",
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_STRING (procedure, "from-address",
|
||||
_("_From"),
|
||||
_("The email address for the From: field"),
|
||||
"",
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_STRING (procedure, "subject",
|
||||
_("Su_bject"),
|
||||
_("The subject"),
|
||||
"",
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_STRING (procedure, "comment",
|
||||
_("Co_mment"),
|
||||
_("The comment"),
|
||||
NULL,
|
||||
G_PARAM_READWRITE);
|
||||
}
|
||||
|
||||
return procedure;
|
||||
}
|
||||
|
||||
static PikaValueArray *
|
||||
mail_run (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data)
|
||||
{
|
||||
PikaProcedureConfig *config;
|
||||
PikaPDBStatusType status = PIKA_PDB_SUCCESS;
|
||||
|
||||
config = pika_procedure_create_config (procedure);
|
||||
pika_procedure_config_begin_run (config, NULL, run_mode, args);
|
||||
|
||||
switch (run_mode)
|
||||
{
|
||||
case PIKA_RUN_INTERACTIVE:
|
||||
{
|
||||
gchar *filename = g_file_get_path (pika_image_get_file (image));
|
||||
|
||||
if (filename)
|
||||
{
|
||||
gchar *basename = g_filename_display_basename (filename);
|
||||
gchar buffername[BUFFER_SIZE];
|
||||
|
||||
g_strlcpy (buffername, basename, BUFFER_SIZE);
|
||||
|
||||
g_object_set (config,
|
||||
"filename", buffername,
|
||||
NULL);
|
||||
g_free (basename);
|
||||
g_free (filename);
|
||||
}
|
||||
}
|
||||
|
||||
if (! send_dialog (procedure, G_OBJECT (config)))
|
||||
{
|
||||
pika_procedure_config_end_run (config, PIKA_PDB_CANCEL);
|
||||
g_object_unref (config);
|
||||
|
||||
return pika_procedure_new_return_values (procedure, PIKA_PDB_CANCEL,
|
||||
NULL);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
status = send_image (G_OBJECT (config),
|
||||
image,
|
||||
n_drawables, drawables,
|
||||
run_mode);
|
||||
|
||||
pika_procedure_config_end_run (config, status);
|
||||
g_object_unref (config);
|
||||
|
||||
return pika_procedure_new_return_values (procedure, status, NULL);
|
||||
}
|
||||
|
||||
static PikaPDBStatusType
|
||||
send_image (GObject *config,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
gint32 run_mode)
|
||||
{
|
||||
PikaPDBStatusType status = PIKA_PDB_SUCCESS;
|
||||
gchar *ext;
|
||||
GFile *tmpfile;
|
||||
gchar *tmpname;
|
||||
#ifndef SENDMAIL /* xdg-email */
|
||||
gchar *mailcmd[9];
|
||||
gchar *filepath = NULL;
|
||||
GFile *tmp_dir = NULL;
|
||||
GFileEnumerator *enumerator;
|
||||
gint i;
|
||||
#else /* SENDMAIL */
|
||||
gchar *mailcmd[3];
|
||||
GPid mailpid;
|
||||
FILE *mailpipe = NULL;
|
||||
#endif
|
||||
GError *error = NULL;
|
||||
gchar *filename = NULL;
|
||||
gchar *receipt = NULL;
|
||||
gchar *from = NULL;
|
||||
gchar *subject = NULL;
|
||||
gchar *comment = NULL;
|
||||
|
||||
g_object_get (config,
|
||||
"filename", &filename,
|
||||
"to-address", &receipt,
|
||||
"from-address", &from,
|
||||
"subject", &subject,
|
||||
"comment", &comment,
|
||||
NULL);
|
||||
|
||||
ext = find_extension (filename);
|
||||
|
||||
if (ext == NULL)
|
||||
return PIKA_PDB_CALLING_ERROR;
|
||||
|
||||
/* get a temp name with the right extension and save into it. */
|
||||
tmpfile = pika_temp_file (ext + 1);
|
||||
tmpname = g_file_get_path (tmpfile);
|
||||
|
||||
if (! (pika_file_save (run_mode, image, n_drawables,
|
||||
(const PikaItem **) drawables,
|
||||
tmpfile) &&
|
||||
valid_file (tmpfile)))
|
||||
{
|
||||
goto error;
|
||||
}
|
||||
|
||||
#ifndef SENDMAIL /* xdg-email */
|
||||
/* From xdg-email doc:
|
||||
* "Some e-mail applications require the file to remain present
|
||||
* after xdg-email returns."
|
||||
* As a consequence, the file cannot be removed at the end of the
|
||||
* function. We actually have no way to ever know *when* the file can
|
||||
* be removed since the caller could leave the email window opened for
|
||||
* hours. Yet we still want to clean sometimes and not have temporary
|
||||
* images piling up.
|
||||
* So I use a known directory that we control under $PIKA_DIRECTORY/tmp/,
|
||||
* and clean it out each time the plugin runs. This means that *if* you
|
||||
* are in the above case (your email client requires the file to stay
|
||||
* alive), you cannot run twice the plugin at the same time.
|
||||
*/
|
||||
tmp_dir = pika_directory_file ("tmp", PLUG_IN_PROC, NULL);
|
||||
|
||||
if (g_mkdir_with_parents (pika_file_get_utf8_name (tmp_dir),
|
||||
S_IRUSR | S_IWUSR | S_IXUSR) == -1)
|
||||
{
|
||||
g_message ("Temporary directory %s could not be created.",
|
||||
pika_file_get_utf8_name (tmp_dir));
|
||||
g_error_free (error);
|
||||
goto error;
|
||||
}
|
||||
|
||||
enumerator = g_file_enumerate_children (tmp_dir,
|
||||
G_FILE_ATTRIBUTE_STANDARD_TYPE,
|
||||
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
|
||||
NULL, NULL);
|
||||
if (enumerator)
|
||||
{
|
||||
GFileInfo *info;
|
||||
|
||||
while ((info = g_file_enumerator_next_file (enumerator,
|
||||
NULL, NULL)))
|
||||
{
|
||||
if (g_file_info_get_file_type (info) == G_FILE_TYPE_REGULAR)
|
||||
{
|
||||
GFile *file = g_file_enumerator_get_child (enumerator, info);
|
||||
g_file_delete (file, NULL, NULL);
|
||||
g_object_unref (file);
|
||||
}
|
||||
|
||||
g_object_unref (info);
|
||||
}
|
||||
|
||||
g_object_unref (enumerator);
|
||||
}
|
||||
|
||||
filepath = g_build_filename (pika_file_get_utf8_name (tmp_dir), filename,
|
||||
NULL);
|
||||
if (g_rename (tmpname, filepath) == -1)
|
||||
{
|
||||
/* But on some system, I got an 'Invalid cross-device link' errno
|
||||
* with g_rename().
|
||||
* On the other hand, g_file_move() seems to be more robust.
|
||||
*/
|
||||
GFile *source = tmpfile;
|
||||
GFile *target = g_file_new_for_path (filepath);
|
||||
|
||||
if (! g_file_move (source, target, G_FILE_COPY_NONE, NULL, NULL, NULL, &error))
|
||||
{
|
||||
g_message ("%s", error->message);
|
||||
g_clear_error (&error);
|
||||
g_object_unref (target);
|
||||
goto error;
|
||||
}
|
||||
|
||||
g_object_unref (target);
|
||||
}
|
||||
|
||||
mailcmd[0] = g_strdup ("xdg-email");
|
||||
mailcmd[1] = "--attach";
|
||||
mailcmd[2] = filepath;
|
||||
i = 3;
|
||||
if (subject != NULL && strlen (subject) > 0)
|
||||
{
|
||||
mailcmd[i++] = "--subject";
|
||||
mailcmd[i++] = subject;
|
||||
}
|
||||
if (comment != NULL && strlen (comment) > 0)
|
||||
{
|
||||
mailcmd[i++] = "--body";
|
||||
mailcmd[i++] = comment;
|
||||
}
|
||||
if (receipt != NULL && strlen (receipt) > 0)
|
||||
{
|
||||
mailcmd[i++] = receipt;
|
||||
}
|
||||
mailcmd[i] = NULL;
|
||||
|
||||
if (! g_spawn_async (NULL, mailcmd, NULL,
|
||||
G_SPAWN_SEARCH_PATH,
|
||||
NULL, NULL, NULL, &error))
|
||||
{
|
||||
g_message ("%s", error->message);
|
||||
g_error_free (error);
|
||||
goto error;
|
||||
}
|
||||
|
||||
#else /* SENDMAIL */
|
||||
/* construct the "sendmail user@location" line */
|
||||
if (strlen (SENDMAIL) == 0)
|
||||
mailcmd[0] = g_strdup ("sendmail");
|
||||
else
|
||||
mailcmd[0] = g_build_filename (SENDMAIL, "sendmail", NULL);
|
||||
|
||||
mailcmd[1] = receipt;
|
||||
mailcmd[2] = NULL;
|
||||
|
||||
/* create a pipe to sendmail */
|
||||
mailpipe = sendmail_pipe (mailcmd, &mailpid);
|
||||
|
||||
if (mailpipe == NULL)
|
||||
return PIKA_PDB_EXECUTION_ERROR;
|
||||
|
||||
sendmail_create_headers (mailpipe, config);
|
||||
|
||||
fflush (mailpipe);
|
||||
|
||||
if (! sendmail_to64 (tmpname, mailpipe, &error))
|
||||
{
|
||||
g_message ("%s", error->message);
|
||||
g_error_free (error);
|
||||
goto error;
|
||||
}
|
||||
|
||||
fprintf (mailpipe, "\n--GUMP-MIME-boundary--\n");
|
||||
#endif
|
||||
|
||||
goto cleanup;
|
||||
|
||||
error:
|
||||
/* stop sendmail from doing anything */
|
||||
#ifdef SENDMAIL
|
||||
kill (mailpid, SIGINT);
|
||||
#endif
|
||||
status = PIKA_PDB_EXECUTION_ERROR;
|
||||
|
||||
cleanup:
|
||||
/* close out the sendmail process */
|
||||
#ifdef SENDMAIL
|
||||
if (mailpipe)
|
||||
{
|
||||
fclose (mailpipe);
|
||||
waitpid (mailpid, NULL, 0);
|
||||
g_spawn_close_pid (mailpid);
|
||||
}
|
||||
|
||||
/* delete the tmpfile that was generated */
|
||||
g_unlink (tmpname);
|
||||
#else
|
||||
if (tmp_dir)
|
||||
g_object_unref (tmp_dir);
|
||||
if (filepath)
|
||||
g_free (filepath);
|
||||
#endif
|
||||
|
||||
g_free (filename);
|
||||
g_free (receipt);
|
||||
g_free (from);
|
||||
g_free (subject);
|
||||
g_free (comment);
|
||||
|
||||
g_free (mailcmd[0]);
|
||||
g_free (tmpname);
|
||||
g_object_unref (tmpfile);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
send_dialog (PikaProcedure *procedure,
|
||||
GObject *config)
|
||||
{
|
||||
GtkWidget *dlg;
|
||||
GtkWidget *main_vbox;
|
||||
GtkWidget *entry;
|
||||
GtkWidget *real_entry;
|
||||
GtkWidget *grid;
|
||||
GtkWidget *button;
|
||||
#ifdef SENDMAIL
|
||||
GtkWidget *scrolled_window;
|
||||
GtkWidget *text_view;
|
||||
#endif
|
||||
gchar *gump_from;
|
||||
gboolean run;
|
||||
|
||||
pika_ui_init (PLUG_IN_BINARY);
|
||||
|
||||
/* check pikarc for a preferred "From:" address */
|
||||
gump_from = pika_pikarc_query ("gump-from");
|
||||
|
||||
if (gump_from)
|
||||
{
|
||||
g_object_set (config,
|
||||
"from-address", gump_from,
|
||||
NULL);
|
||||
g_free (gump_from);
|
||||
}
|
||||
|
||||
dlg = pika_procedure_dialog_new (procedure,
|
||||
PIKA_PROCEDURE_CONFIG (config),
|
||||
_("Send by Email"));
|
||||
/* Change "Ok" button to "Send" */
|
||||
button = gtk_dialog_get_widget_for_response (GTK_DIALOG (dlg),
|
||||
GTK_RESPONSE_OK);
|
||||
gtk_button_set_label (GTK_BUTTON (button), _("Send"));
|
||||
|
||||
|
||||
pika_dialog_set_alternative_button_order (GTK_DIALOG (dlg),
|
||||
GTK_RESPONSE_OK,
|
||||
GTK_RESPONSE_CANCEL,
|
||||
-1);
|
||||
|
||||
pika_window_set_transient (GTK_WINDOW (dlg));
|
||||
|
||||
main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
|
||||
gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
|
||||
gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))),
|
||||
main_vbox, TRUE, TRUE, 0);
|
||||
gtk_widget_show (main_vbox);
|
||||
|
||||
/* grid */
|
||||
grid = gtk_grid_new ();
|
||||
gtk_box_pack_start (GTK_BOX (main_vbox), grid, FALSE, FALSE, 0);
|
||||
gtk_widget_show (grid);
|
||||
|
||||
gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
|
||||
gtk_grid_set_column_spacing (GTK_GRID (grid), 6);
|
||||
|
||||
/* Filename entry */
|
||||
entry = pika_procedure_dialog_get_widget (PIKA_PROCEDURE_DIALOG (dlg),
|
||||
"filename", PIKA_TYPE_LABEL_ENTRY);
|
||||
real_entry = pika_label_entry_get_entry (PIKA_LABEL_ENTRY (entry));
|
||||
gtk_widget_set_size_request (real_entry, 200, -1);
|
||||
gtk_entry_set_activates_default (GTK_ENTRY (real_entry), TRUE);
|
||||
|
||||
pika_procedure_dialog_fill (PIKA_PROCEDURE_DIALOG (dlg),
|
||||
"filename",
|
||||
NULL);
|
||||
gtk_entry_set_max_length (GTK_ENTRY (real_entry),
|
||||
BUFFER_SIZE - 1);
|
||||
|
||||
#ifdef SENDMAIL
|
||||
/* To entry */
|
||||
entry = pika_procedure_dialog_get_widget (PIKA_PROCEDURE_DIALOG (dlg),
|
||||
"to-address",
|
||||
PIKA_TYPE_LABEL_ENTRY);
|
||||
real_entry = pika_label_entry_get_entry (PIKA_LABEL_ENTRY (entry));
|
||||
gtk_widget_set_size_request (real_entry, 200, -1);
|
||||
gtk_entry_set_max_length (GTK_ENTRY (real_entry), BUFFER_SIZE - 1);
|
||||
pika_procedure_dialog_fill (PIKA_PROCEDURE_DIALOG (dlg),
|
||||
"to-address",
|
||||
NULL);
|
||||
gtk_entry_set_max_length (GTK_ENTRY (real_entry),
|
||||
BUFFER_SIZE - 1);
|
||||
|
||||
gtk_widget_grab_focus (real_entry);
|
||||
|
||||
/* From entry */
|
||||
entry = pika_procedure_dialog_get_widget (PIKA_PROCEDURE_DIALOG (dlg),
|
||||
"from-address",
|
||||
PIKA_TYPE_LABEL_ENTRY);
|
||||
real_entry = pika_label_entry_get_entry (PIKA_LABEL_ENTRY (entry));
|
||||
gtk_widget_set_size_request (real_entry, 200, -1);
|
||||
gtk_entry_set_max_length (GTK_ENTRY (real_entry), BUFFER_SIZE - 1);
|
||||
pika_procedure_dialog_fill (PIKA_PROCEDURE_DIALOG (dlg),
|
||||
"from-address",
|
||||
NULL);
|
||||
gtk_entry_set_max_length (GTK_ENTRY (real_entry),
|
||||
BUFFER_SIZE - 1);
|
||||
|
||||
/* Subject entry */
|
||||
entry = pika_procedure_dialog_get_widget (PIKA_PROCEDURE_DIALOG (dlg),
|
||||
"subject",
|
||||
PIKA_TYPE_LABEL_ENTRY);
|
||||
real_entry = pika_label_entry_get_entry (PIKA_LABEL_ENTRY (entry));
|
||||
gtk_widget_set_size_request (real_entry, 200, -1);
|
||||
gtk_entry_set_max_length (GTK_ENTRY (real_entry), BUFFER_SIZE - 1);
|
||||
pika_procedure_dialog_fill (PIKA_PROCEDURE_DIALOG (dlg),
|
||||
"subject",
|
||||
NULL);
|
||||
gtk_entry_set_max_length (GTK_ENTRY (real_entry),
|
||||
BUFFER_SIZE - 1);
|
||||
|
||||
/* Body */
|
||||
text_view = pika_procedure_dialog_get_widget (PIKA_PROCEDURE_DIALOG (dlg),
|
||||
"comment",
|
||||
GTK_TYPE_TEXT_VIEW);
|
||||
gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (text_view), GTK_WRAP_WORD);
|
||||
|
||||
scrolled_window =
|
||||
pika_procedure_dialog_fill_scrolled_window (PIKA_PROCEDURE_DIALOG (dlg),
|
||||
"comment-scrolled",
|
||||
"comment");
|
||||
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window),
|
||||
GTK_SHADOW_IN);
|
||||
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
|
||||
GTK_POLICY_AUTOMATIC,
|
||||
GTK_POLICY_AUTOMATIC);
|
||||
|
||||
pika_procedure_dialog_fill (PIKA_PROCEDURE_DIALOG (dlg),
|
||||
"comment-scrolled",
|
||||
NULL);
|
||||
#endif
|
||||
|
||||
gtk_widget_show (dlg);
|
||||
|
||||
run = pika_procedure_dialog_run (PIKA_PROCEDURE_DIALOG (dlg));
|
||||
|
||||
gtk_widget_destroy (dlg);
|
||||
|
||||
return run;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
valid_file (GFile *file)
|
||||
{
|
||||
GStatBuf buf;
|
||||
gboolean valid;
|
||||
|
||||
valid = g_stat (g_file_peek_path (file), &buf) == 0 && buf.st_size > 0;
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
static gchar *
|
||||
find_extension (const gchar *filename)
|
||||
{
|
||||
gchar *filename_copy;
|
||||
gchar *ext;
|
||||
|
||||
/* we never free this copy - aren't we evil! */
|
||||
filename_copy = g_strdup (filename);
|
||||
|
||||
/* find the extension, boy! */
|
||||
ext = strrchr (filename_copy, '.');
|
||||
|
||||
while (TRUE)
|
||||
{
|
||||
if (!ext || ext[1] == '\0' || strchr (ext, G_DIR_SEPARATOR))
|
||||
{
|
||||
g_message (_("some sort of error with the file extension "
|
||||
"or lack thereof"));
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (0 != g_ascii_strcasecmp (ext, ".gz") &&
|
||||
0 != g_ascii_strcasecmp (ext, ".bz2"))
|
||||
{
|
||||
return ext;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* we found something, loop back, and look again */
|
||||
*ext = 0;
|
||||
ext = strrchr (filename_copy, '.');
|
||||
}
|
||||
}
|
||||
|
||||
g_free (filename_copy);
|
||||
|
||||
return ext;
|
||||
}
|
||||
|
||||
#ifdef SENDMAIL
|
||||
static gchar *
|
||||
sendmail_content_type (const gchar *filename)
|
||||
{
|
||||
/* This function returns a MIME Content-type: value based on the
|
||||
filename it is given. */
|
||||
const gchar *type_mappings[20] =
|
||||
{
|
||||
"gif" , "image/gif",
|
||||
"jpg" , "image/jpeg",
|
||||
"jpeg", "image/jpeg",
|
||||
"tif" , "image/tiff",
|
||||
"tiff", "image/tiff",
|
||||
"png" , "image/png",
|
||||
"g3" , "image/g3fax",
|
||||
"ps" , "application/postscript",
|
||||
"eps" , "application/postscript",
|
||||
NULL, NULL
|
||||
};
|
||||
|
||||
gchar *ext;
|
||||
gint i;
|
||||
|
||||
ext = find_extension (filename);
|
||||
|
||||
if (!ext)
|
||||
{
|
||||
return g_strdup ("application/octet-stream");
|
||||
}
|
||||
|
||||
i = 0;
|
||||
ext += 1;
|
||||
|
||||
while (type_mappings[i])
|
||||
{
|
||||
if (g_ascii_strcasecmp (ext, type_mappings[i]) == 0)
|
||||
{
|
||||
return g_strdup (type_mappings[i + 1]);
|
||||
}
|
||||
|
||||
i += 2;
|
||||
}
|
||||
|
||||
return g_strdup_printf ("image/x-%s", ext);
|
||||
}
|
||||
|
||||
static void
|
||||
sendmail_create_headers (FILE *mailpipe,
|
||||
GObject *config)
|
||||
{
|
||||
gchar *filename = NULL;
|
||||
gchar *receipt = NULL;
|
||||
gchar *from = NULL;
|
||||
gchar *subject = NULL;
|
||||
gchar *comment = NULL;
|
||||
|
||||
g_object_get (config,
|
||||
"filename", &filename,
|
||||
"to-address", &receipt,
|
||||
"from-address", &from,
|
||||
"subject", &subject,
|
||||
"comment", &comment,
|
||||
NULL);
|
||||
|
||||
/* create all the mail header stuff. Feel free to add your own */
|
||||
/* It is advisable to leave the X-Mailer header though, as */
|
||||
/* there is a possibility of a Pika mail scanner/reader in the */
|
||||
/* future. It will probably need that header. */
|
||||
|
||||
fprintf (mailpipe, "To: %s \n", receipt);
|
||||
fprintf (mailpipe, "Subject: %s \n", subject);
|
||||
if (from != NULL && strlen (from) > 0)
|
||||
fprintf (mailpipe, "From: %s \n", from);
|
||||
|
||||
fprintf (mailpipe, "X-Mailer: PIKA Useless Mail plug-in %s\n", PIKA_VERSION);
|
||||
|
||||
fprintf (mailpipe, "MIME-Version: 1.0\n");
|
||||
fprintf (mailpipe, "Content-type: multipart/mixed; "
|
||||
"boundary=GUMP-MIME-boundary\n");
|
||||
|
||||
fprintf (mailpipe, "\n\n");
|
||||
|
||||
fprintf (mailpipe, "--GUMP-MIME-boundary\n");
|
||||
fprintf (mailpipe, "Content-type: text/plain; charset=UTF-8\n\n");
|
||||
|
||||
if (mesg_body)
|
||||
fprintf (mailpipe, "%s", mesg_body);
|
||||
|
||||
fprintf (mailpipe, "\n\n");
|
||||
|
||||
{
|
||||
gchar *content = sendmail_content_type (filename);
|
||||
|
||||
fprintf (mailpipe, "--GUMP-MIME-boundary\n");
|
||||
fprintf (mailpipe, "Content-type: %s\n", content);
|
||||
fprintf (mailpipe, "Content-transfer-encoding: base64\n");
|
||||
fprintf (mailpipe, "Content-disposition: attachment; filename=\"%s\"\n",
|
||||
filename);
|
||||
fprintf (mailpipe, "Content-description: %s\n\n", filename);
|
||||
|
||||
g_free (content);
|
||||
}
|
||||
|
||||
g_free (filename);
|
||||
g_free (receipt);
|
||||
g_free (from);
|
||||
g_free (subject);
|
||||
g_free (comment);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
sendmail_to64 (const gchar *filename,
|
||||
FILE *outfile,
|
||||
GError **error)
|
||||
{
|
||||
GMappedFile *infile;
|
||||
const guchar *in;
|
||||
gchar out[2048];
|
||||
gint state = 0;
|
||||
gint save = 0;
|
||||
gsize len;
|
||||
gsize bytes;
|
||||
gsize c;
|
||||
|
||||
infile = g_mapped_file_new (filename, FALSE, error);
|
||||
if (! infile)
|
||||
return FALSE;
|
||||
|
||||
in = (const guchar *) g_mapped_file_get_contents (infile);
|
||||
len = g_mapped_file_get_length (infile);
|
||||
|
||||
for (c = 0; c < len;)
|
||||
{
|
||||
gsize step = MIN (1024, len - c);
|
||||
|
||||
bytes = g_base64_encode_step (in + c, step, TRUE, out, &state, &save);
|
||||
fwrite (out, 1, bytes, outfile);
|
||||
|
||||
c += step;
|
||||
}
|
||||
|
||||
bytes = g_base64_encode_close (TRUE, out, &state, &save);
|
||||
fwrite (out, 1, bytes, outfile);
|
||||
|
||||
g_mapped_file_unref (infile);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static FILE *
|
||||
sendmail_pipe (gchar **cmd,
|
||||
GPid *pid)
|
||||
{
|
||||
gint fd;
|
||||
GError *err = NULL;
|
||||
|
||||
if (! g_spawn_async_with_pipes (NULL, cmd, NULL,
|
||||
G_SPAWN_DO_NOT_REAP_CHILD |
|
||||
G_SPAWN_SEARCH_PATH,
|
||||
NULL, NULL, pid, &fd, NULL, NULL, &err))
|
||||
{
|
||||
g_message (_("Could not start sendmail (%s)"), err->message);
|
||||
g_error_free (err);
|
||||
|
||||
*pid = -1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return fdopen (fd, "wb");
|
||||
}
|
||||
#endif
|
||||
250
plug-ins/common/meson.build
Normal file
250
plug-ins/common/meson.build
Normal file
@ -0,0 +1,250 @@
|
||||
|
||||
common_plugins_list = [
|
||||
{ 'name': 'align-layers', },
|
||||
{ 'name': 'animation-optimize', },
|
||||
{ 'name': 'animation-play', },
|
||||
{ 'name': 'blinds', },
|
||||
{ 'name': 'border-average', },
|
||||
{ 'name': 'busy-dialog', },
|
||||
{ 'name': 'checkerboard', },
|
||||
{ 'name': 'cml-explorer', },
|
||||
{ 'name': 'colormap-remap', },
|
||||
{ 'name': 'compose', },
|
||||
{ 'name': 'contrast-retinex', },
|
||||
{ 'name': 'crop-zealous',
|
||||
'deps': [ gtk3, gegl, gdk_pixbuf, cairo, ],
|
||||
},
|
||||
{ 'name': 'curve-bend', },
|
||||
{ 'name': 'decompose', },
|
||||
{ 'name': 'depth-merge', },
|
||||
{ 'name': 'despeckle', },
|
||||
{ 'name': 'destripe', },
|
||||
{ 'name': 'file-cel', },
|
||||
{ 'name': 'file-compressor',
|
||||
'deps': [ gegl, gdk_pixbuf, cairo, gio, liblzma, bz2, zlib, ],
|
||||
},
|
||||
{ 'name': 'file-csource', },
|
||||
{ 'name': 'file-desktop-link',
|
||||
'deps': [ gtk3, gegl, gdk_pixbuf, cairo, ],
|
||||
},
|
||||
{ 'name': 'file-dicom', },
|
||||
{ 'name': 'file-farbfeld', },
|
||||
{ 'name': 'file-gbr', },
|
||||
{ 'name': 'file-gegl', },
|
||||
{ 'name': 'file-gif-load',
|
||||
'deps': [ gtk3, gegl, gdk_pixbuf, cairo, ],
|
||||
},
|
||||
{ 'name': 'file-gif-save', },
|
||||
{ 'name': 'file-gih', },
|
||||
{ 'name': 'file-glob',
|
||||
'deps': [ gtk3, gegl, gdk_pixbuf, cairo, ],
|
||||
},
|
||||
{ 'name': 'file-header', },
|
||||
{ 'name': 'file-html-table', },
|
||||
{ 'name': 'file-pat', },
|
||||
{ 'name': 'file-pcx', },
|
||||
{ 'name': 'file-pdf-load',
|
||||
'deps': [ gtk3, gegl, gdk_pixbuf, poppler ],
|
||||
},
|
||||
{ 'name': 'file-pix', },
|
||||
{ 'name': 'file-png',
|
||||
'deps': [ gtk3, gegl, libpng, lcms ],
|
||||
},
|
||||
{ 'name': 'file-pnm', },
|
||||
{ 'name': 'file-psp',
|
||||
'deps': [ gtk3, gegl, zlib, ],
|
||||
},
|
||||
{ 'name': 'file-raw-data', },
|
||||
{ 'name': 'file-sunras', },
|
||||
{ 'name': 'file-svg',
|
||||
'deps': [ gtk3, gegl, rsvg, ],
|
||||
},
|
||||
{ 'name': 'file-tga', },
|
||||
{ 'name': 'file-wbmp', },
|
||||
{ 'name': 'file-xbm', },
|
||||
{ 'name': 'file-xwd', },
|
||||
{ 'name': 'film', },
|
||||
{ 'name': 'gradient-map', },
|
||||
{ 'name': 'grid', },
|
||||
{ 'name': 'guillotine', },
|
||||
{ 'name': 'hot', },
|
||||
{ 'name': 'jigsaw', },
|
||||
{ 'name': 'nl-filter', },
|
||||
{ 'name': 'plugin-browser', },
|
||||
{ 'name': 'procedure-browser', },
|
||||
{ 'name': 'qbist', },
|
||||
{ 'name': 'sample-colorize', },
|
||||
{ 'name': 'smooth-palette', },
|
||||
{ 'name': 'sparkle', },
|
||||
{ 'name': 'sphere-designer', },
|
||||
{ 'name': 'tile-small', },
|
||||
{ 'name': 'tile', },
|
||||
{ 'name': 'unit-editor', },
|
||||
{ 'name': 'van-gogh-lic', },
|
||||
{ 'name': 'warp', },
|
||||
{ 'name': 'wavelet-decompose', },
|
||||
{ 'name': 'web-browser', },
|
||||
]
|
||||
|
||||
if libaa.found()
|
||||
common_plugins_list += {
|
||||
'name': 'file-aa',
|
||||
'deps': [ gtk3, gegl, gdk_pixbuf, libaa, ],
|
||||
}
|
||||
endif
|
||||
|
||||
if have_heif
|
||||
common_plugins_list += { 'name': 'file-heif',
|
||||
'deps': [ gtk3, gegl, libheif, gexiv2, lcms, ],
|
||||
}
|
||||
endif
|
||||
|
||||
if openjpeg.found()
|
||||
common_plugins_list += { 'name': 'file-jp2-load',
|
||||
'deps': [ gtk3, gegl, gdk_pixbuf, openjpeg, ],
|
||||
}
|
||||
endif
|
||||
|
||||
if libjxl.found() and libjxl_threads.found()
|
||||
common_plugins_list += {
|
||||
'name': 'file-jpegxl',
|
||||
'deps': [ gtk3, gegl, gexiv2, libjxl, libjxl_threads, ],
|
||||
}
|
||||
endif
|
||||
|
||||
if libmng.found()
|
||||
mng_cflags = []
|
||||
if platform_windows
|
||||
mng_cflags = [ '-DMNG_USE_DLL' ]
|
||||
endif
|
||||
common_plugins_list += { 'name': 'file-mng',
|
||||
'deps': [ gtk3, gegl, libmng, libpng, ],
|
||||
'cflags': mng_cflags,
|
||||
}
|
||||
endif
|
||||
|
||||
if cairopdf.found()
|
||||
common_plugins_list += { 'name': 'file-pdf-save',
|
||||
'deps': [ gtk3, gegl, gdk_pixbuf, poppler, cairo, cairopdf ],
|
||||
}
|
||||
endif
|
||||
|
||||
if ghostscript.found()
|
||||
common_plugins_list += { 'name': 'file-ps',
|
||||
'deps': [ gtk3, gegl, gdk_pixbuf, ghostscript, ],
|
||||
}
|
||||
endif
|
||||
|
||||
if wmf.found()
|
||||
common_plugins_list += { 'name': 'file-wmf',
|
||||
'deps': [ gtk3, gegl, wmf, ],
|
||||
}
|
||||
endif
|
||||
|
||||
if xmc.found()
|
||||
common_plugins_list += { 'name': 'file-xmc',
|
||||
'deps': [ gtk3, gegl, xmc, ],
|
||||
}
|
||||
endif
|
||||
|
||||
if libxpm.found()
|
||||
common_plugins_list += { 'name': 'file-xpm',
|
||||
'deps': [ gtk3, gegl, libxpm, ],
|
||||
}
|
||||
endif
|
||||
|
||||
if have_qoi
|
||||
common_plugins_list += {
|
||||
'name': 'file-qoi',
|
||||
'deps': [ gtk3, gegl, ],
|
||||
}
|
||||
endif
|
||||
|
||||
if libiff.found() and libilbm.found()
|
||||
common_plugins_list += {
|
||||
'name': 'file-iff',
|
||||
'deps': [ gtk3, gegl, libiff, libilbm, ],
|
||||
}
|
||||
elif have_ilbm
|
||||
common_plugins_list += {
|
||||
'name': 'file-iff',
|
||||
'deps': [ gtk3, gegl, ],
|
||||
}
|
||||
endif
|
||||
|
||||
if not platform_windows
|
||||
common_plugins_list += { 'name': 'mail', }
|
||||
endif
|
||||
|
||||
if get_option('webkit-unmaintained')
|
||||
common_plugins_list += { 'name': 'web-page',
|
||||
'deps': [ gtk3, gegl, gdk_pixbuf, webkit, ],
|
||||
}
|
||||
endif
|
||||
|
||||
foreach plugin : common_plugins_list
|
||||
plugin_name = plugin.get('name')
|
||||
plugin_sources = [ plugin.get('sources', plugin_name + '.c') ]
|
||||
plugin_deps = plugin.get('deps', [ gtk3, gegl, gdk_pixbuf, ])
|
||||
plugin_cflags = plugin.get('cflags', [])
|
||||
|
||||
if platform_windows
|
||||
plugin_rc = configure_file(
|
||||
input : pika_plugins_rc.full_path(),
|
||||
output: plugin_name + '.rc',
|
||||
copy: true,
|
||||
)
|
||||
|
||||
# See https://gitlab.gnome.org/GNOME/pika/-/issues/8537
|
||||
if generate_version_h
|
||||
compile_resources_depfiles = []
|
||||
compile_resources_depends = [ gitversion_h ]
|
||||
else
|
||||
compile_resources_depfiles = [ gitversion_h ]
|
||||
compile_resources_depends = []
|
||||
endif
|
||||
|
||||
plugin_sources += windows.compile_resources(
|
||||
plugin_rc,
|
||||
args: [
|
||||
'--define', 'ORIGINALFILENAME_STR="@0@"'.format(plugin_name+'.exe'),
|
||||
'--define', 'INTERNALNAME_STR="@0@"' .format(plugin_name),
|
||||
'--define', 'TOP_SRCDIR="@0@"' .format(meson.project_source_root()),
|
||||
],
|
||||
include_directories: [
|
||||
rootInclude, appInclude,
|
||||
],
|
||||
depend_files: compile_resources_depfiles,
|
||||
depends: compile_resources_depends
|
||||
)
|
||||
endif
|
||||
|
||||
plugin_exe = executable(plugin_name,
|
||||
plugin_sources,
|
||||
include_directories: [ rootInclude, ],
|
||||
link_with : [
|
||||
libpika,
|
||||
libpikabase,
|
||||
libpikacolor,
|
||||
libpikaconfig,
|
||||
libpikamath,
|
||||
libpikaui,
|
||||
libpikawidgets,
|
||||
],
|
||||
dependencies: [ plugin_deps, math ],
|
||||
c_args: plugin_cflags,
|
||||
install: true,
|
||||
install_dir: pikaplugindir / 'plug-ins' / plugin_name,
|
||||
)
|
||||
|
||||
# Ugly trick to copy executables into subfolders so that we can run PIKA from
|
||||
# the build directory without installing it.
|
||||
custom_target('test-' + plugin_name,
|
||||
input: [ plugin_exe ],
|
||||
output: [ plugin_name + '.dummy' ],
|
||||
command: [ python, meson.project_source_root() / '.gitlab/cp-plug-in-subfolder.py',
|
||||
plugin_exe, meson.current_build_dir() / 'test-plug-ins' / plugin_name,
|
||||
'@OUTPUT@' ],
|
||||
build_by_default: true,
|
||||
)
|
||||
endforeach
|
||||
234
plug-ins/common/mkgen.pl
Normal file
234
plug-ins/common/mkgen.pl
Normal file
@ -0,0 +1,234 @@
|
||||
#!/usr/bin/perl -w
|
||||
|
||||
use lib '../../pdb';
|
||||
|
||||
require 'util.pl';
|
||||
|
||||
*write_file = \&Pika::CodeGen::util::write_file;
|
||||
*FILE_EXT = \$Pika::CodeGen::util::FILE_EXT;
|
||||
|
||||
$destdir = ".";
|
||||
$builddir = ".";
|
||||
|
||||
$ignorefile = ".gitignore";
|
||||
$rcfile = "pikarc.common";
|
||||
|
||||
$outmk = "$builddir/Makefile.am$FILE_EXT";
|
||||
$outignore = "$builddir/$ignorefile$FILE_EXT";
|
||||
$outrc = "$builddir/$rcfile$FILE_EXT";
|
||||
|
||||
open MK, "> $outmk";
|
||||
open IGNORE, "> $outignore";
|
||||
open RC, "> $outrc";
|
||||
|
||||
require './plugin-defs.pl';
|
||||
|
||||
$bins = ""; $opts = ""; $dirs = "";
|
||||
|
||||
foreach (sort keys %plugins) {
|
||||
my $makename = $_;
|
||||
$makename =~ s/-/_/g;
|
||||
|
||||
if (exists $plugins{$_}->{optional}) {
|
||||
$bins .= "${makename}_libexec_PROGRAMS = \$(\U$makename\E)\n";
|
||||
$opts .= "\t$_ \\\n";
|
||||
}
|
||||
else {
|
||||
$bins .= "${makename}_libexec_PROGRAMS = $_\n";
|
||||
}
|
||||
|
||||
$dirs .= "${makename}_libexecdir = \$(pikaplugindir)/plug-ins/$_\n";
|
||||
}
|
||||
|
||||
$extra = "";
|
||||
foreach (@extra) { $extra .= "\t$_\t\\\n" }
|
||||
if ($extra) {
|
||||
$extra =~ s/\t\\\n$//s;
|
||||
$extra = "\t\\\n$extra";
|
||||
}
|
||||
|
||||
foreach ($bins, $opts) { s/ \\\n$//s }
|
||||
|
||||
print MK <<EOT;
|
||||
|
||||
|
||||
## ---------------------------------------------------------
|
||||
## This file is autogenerated by mkgen.pl and plugin-defs.pl
|
||||
## ---------------------------------------------------------
|
||||
|
||||
## Modify those two files instead of this one; for most
|
||||
## plug-ins you should only need to modify plugin-defs.pl.
|
||||
|
||||
if OS_WIN32
|
||||
mwindows = -mwindows
|
||||
else
|
||||
libm = -lm
|
||||
endif
|
||||
|
||||
if PLATFORM_OSX
|
||||
xobjective_c = "-xobjective-c"
|
||||
framework_cocoa = -framework Cocoa
|
||||
endif
|
||||
|
||||
if HAVE_WINDRES
|
||||
include \$(top_srcdir)/build/windows/pikarc-plug-ins.rule
|
||||
include $rcfile
|
||||
endif
|
||||
|
||||
libpika = \$(top_builddir)/libpika/libpika-\$(PIKA_API_VERSION).la
|
||||
libpikabase = \$(top_builddir)/libpikabase/libpikabase-\$(PIKA_API_VERSION).la
|
||||
libpikacolor = \$(top_builddir)/libpikacolor/libpikacolor-\$(PIKA_API_VERSION).la
|
||||
libpikaconfig = \$(top_builddir)/libpikaconfig/libpikaconfig-\$(PIKA_API_VERSION).la
|
||||
libpikamath = \$(top_builddir)/libpikamath/libpikamath-\$(PIKA_API_VERSION).la \$(libm)
|
||||
libpikamodule = \$(top_builddir)/libpikamodule/libpikamodule-\$(PIKA_API_VERSION).la
|
||||
libpikaui = \$(top_builddir)/libpika/libpikaui-\$(PIKA_API_VERSION).la
|
||||
libpikawidgets = \$(top_builddir)/libpikawidgets/libpikawidgets-\$(PIKA_API_VERSION).la
|
||||
|
||||
|
||||
AM_LDFLAGS = \$(mwindows)
|
||||
|
||||
EXTRA_DIST = \\
|
||||
mkgen.pl \\
|
||||
plugin-defs.pl$extra \\
|
||||
$rcfile
|
||||
|
||||
AM_CPPFLAGS = \\
|
||||
-I\$(top_srcdir) \\
|
||||
\$(GTK_CFLAGS) \\
|
||||
\$(GEGL_CFLAGS) \\
|
||||
-I\$(includedir)
|
||||
|
||||
$dirs
|
||||
|
||||
$bins
|
||||
|
||||
EXTRA_PROGRAMS = \\
|
||||
$opts
|
||||
|
||||
install-\%: \%
|
||||
\@\$(NORMAL_INSTALL)
|
||||
\$(mkinstalldirs) \$(DESTDIR)\$(pikaplugindir)/plug-ins/\$<
|
||||
\@p=\$<; p1=`echo \$\$p|sed 's/\$(EXEEXT)\$\$//'`; \\
|
||||
if test -f \$\$p \\
|
||||
|| test -f \$\$p1 \\
|
||||
; then \\
|
||||
f=`echo "\$\$p1" | sed 's,^.*/,,;\$(transform);s/\$\$/\$(EXEEXT)/'`; \\
|
||||
echo " \$(INSTALL_PROGRAM_ENV) \$(LIBTOOL) --mode=install \$(INSTALL_PROGRAM) \$\$p \$(DESTDIR)\$(pikaplugindir)/plug-ins/\$\$p/\$\$f"; \\
|
||||
\$(INSTALL_PROGRAM_ENV) \$(LIBTOOL) --mode=install \$(INSTALL_PROGRAM) \$\$p \$(DESTDIR)\$(pikaplugindir)/plug-ins/\$\$p/\$\$f || exit 1; \\
|
||||
else :; fi
|
||||
EOT
|
||||
|
||||
print IGNORE <<EOT;
|
||||
/.deps
|
||||
/.libs
|
||||
/Makefile
|
||||
/Makefile.in
|
||||
EOT
|
||||
|
||||
foreach (sort keys %plugins) {
|
||||
my $makename = $_;
|
||||
$makename =~ s/-/_/g;
|
||||
|
||||
my $libpika = "";
|
||||
|
||||
if (exists $plugins{$_}->{ui}) {
|
||||
$libpika .= "\$(libpikaui)";
|
||||
$libpika .= "\t\t\\\n\t\$(libpikawidgets)";
|
||||
$libpika .= "\t\\\n\t\$(libpikamodule)";
|
||||
$libpika .= "\t\\\n\t";
|
||||
}
|
||||
|
||||
$libpika .= "\$(libpika)";
|
||||
$libpika .= "\t\t\\\n\t\$(libpikamath)";
|
||||
$libpika .= "\t\t\\\n\t\$(libpikaconfig)";
|
||||
$libpika .= "\t\\\n\t\$(libpikacolor)";
|
||||
$libpika .= "\t\t\\\n\t\$(libpikabase)";
|
||||
|
||||
my $glib;
|
||||
if (exists $plugins{$_}->{ui}) {
|
||||
$glib = "\$(GTK_LIBS)\t\t\\";
|
||||
} else {
|
||||
$glib = "\$(CAIRO_LIBS)\t\t\\\n\t\$(GDK_PIXBUF_LIBS)\t\\";
|
||||
|
||||
if (exists $plugins{$_}->{gio} &&
|
||||
! exists $plugins{$_}->{gegl}) {
|
||||
$glib .= "\n\t\$(GIO_LIBS)\t\t\\";
|
||||
}
|
||||
}
|
||||
|
||||
if (exists $plugins{$_}->{gegl}) {
|
||||
$glib .= "\n\t\$(GEGL_LIBS)\t\t\\";
|
||||
}
|
||||
|
||||
my $optlib = "";
|
||||
|
||||
if (exists $plugins{$_}->{libs}) {
|
||||
$optlib = "\n\t\$(" . $plugins{$_}->{libs} . ")\t\t\\";
|
||||
}
|
||||
|
||||
if (exists $plugins{$_}->{ldflags}) {
|
||||
my $ldflags = $plugins{$_}->{ldflags};
|
||||
|
||||
print MK <<EOT;
|
||||
|
||||
${makename}_LDFLAGS = $ldflags
|
||||
EOT
|
||||
}
|
||||
|
||||
if (exists $plugins{$_}->{cflags}) {
|
||||
my $cflags = $plugins{$_}->{cflags};
|
||||
my $cflagsvalue = $cflags =~ /FLAGS/ ? "\$($cflags)" : $cflags;
|
||||
|
||||
print MK <<EOT;
|
||||
|
||||
${makename}_CFLAGS = $cflagsvalue
|
||||
EOT
|
||||
}
|
||||
|
||||
if (exists $plugins{$_}->{cppflags}) {
|
||||
my $cppflags = $plugins{$_}->{cppflags};
|
||||
|
||||
print MK <<EOT;
|
||||
|
||||
${makename}_CPPFLAGS = $cppflags
|
||||
EOT
|
||||
}
|
||||
|
||||
my $deplib = "\$(RT_LIBS)\t\t\\\n\t\$(INTLLIBS)";
|
||||
if (exists $plugins{$_}->{libdep}) {
|
||||
my @lib = split(/:/, $plugins{$_}->{libdep});
|
||||
foreach $lib (@lib) {
|
||||
$deplib = "\$(\U$lib\E_LIBS)\t\t\\\n\t$deplib";
|
||||
}
|
||||
}
|
||||
|
||||
my $rclib = "\$(${makename}_RC)";
|
||||
|
||||
print MK <<EOT;
|
||||
|
||||
${makename}_SOURCES = \\
|
||||
$_.c
|
||||
|
||||
${makename}_LDADD = \\
|
||||
$libpika \\
|
||||
$glib$optlib
|
||||
$deplib \\
|
||||
$rclib
|
||||
EOT
|
||||
|
||||
print RC <<EOT;
|
||||
${makename}_RC = $_.rc.o
|
||||
EOT
|
||||
|
||||
print IGNORE "/$_\n";
|
||||
print IGNORE "/$_.exe\n";
|
||||
}
|
||||
|
||||
close RC;
|
||||
close MK;
|
||||
close IGNORE;
|
||||
|
||||
&write_file($outmk, $destdir);
|
||||
&write_file($outignore, $destdir);
|
||||
&write_file($outrc, $destdir);
|
||||
|
||||
1184
plug-ins/common/nl-filter.c
Normal file
1184
plug-ins/common/nl-filter.c
Normal file
File diff suppressed because it is too large
Load Diff
77
plug-ins/common/pikarc.common
Normal file
77
plug-ins/common/pikarc.common
Normal file
@ -0,0 +1,77 @@
|
||||
align_layers_RC = align-layers.rc.o
|
||||
animation_optimize_RC = animation-optimize.rc.o
|
||||
animation_play_RC = animation-play.rc.o
|
||||
blinds_RC = blinds.rc.o
|
||||
border_average_RC = border-average.rc.o
|
||||
busy_dialog_RC = busy-dialog.rc.o
|
||||
checkerboard_RC = checkerboard.rc.o
|
||||
cml_explorer_RC = cml-explorer.rc.o
|
||||
colormap_remap_RC = colormap-remap.rc.o
|
||||
compose_RC = compose.rc.o
|
||||
contrast_retinex_RC = contrast-retinex.rc.o
|
||||
crop_zealous_RC = crop-zealous.rc.o
|
||||
curve_bend_RC = curve-bend.rc.o
|
||||
decompose_RC = decompose.rc.o
|
||||
depth_merge_RC = depth-merge.rc.o
|
||||
despeckle_RC = despeckle.rc.o
|
||||
destripe_RC = destripe.rc.o
|
||||
file_aa_RC = file-aa.rc.o
|
||||
file_cel_RC = file-cel.rc.o
|
||||
file_compressor_RC = file-compressor.rc.o
|
||||
file_csource_RC = file-csource.rc.o
|
||||
file_desktop_link_RC = file-desktop-link.rc.o
|
||||
file_dicom_RC = file-dicom.rc.o
|
||||
file_gbr_RC = file-gbr.rc.o
|
||||
file_gegl_RC = file-gegl.rc.o
|
||||
file_gif_load_RC = file-gif-load.rc.o
|
||||
file_gif_save_RC = file-gif-save.rc.o
|
||||
file_gih_RC = file-gih.rc.o
|
||||
file_glob_RC = file-glob.rc.o
|
||||
file_header_RC = file-header.rc.o
|
||||
file_heif_RC = file-heif.rc.o
|
||||
file_html_table_RC = file-html-table.rc.o
|
||||
file_jp2_load_RC = file-jp2-load.rc.o
|
||||
file_jpegxl_RC = file-jpegxl.rc.o
|
||||
file_mng_RC = file-mng.rc.o
|
||||
file_pat_RC = file-pat.rc.o
|
||||
file_pcx_RC = file-pcx.rc.o
|
||||
file_pdf_load_RC = file-pdf-load.rc.o
|
||||
file_pdf_save_RC = file-pdf-save.rc.o
|
||||
file_pix_RC = file-pix.rc.o
|
||||
file_png_RC = file-png.rc.o
|
||||
file_pnm_RC = file-pnm.rc.o
|
||||
file_ps_RC = file-ps.rc.o
|
||||
file_psp_RC = file-psp.rc.o
|
||||
file_raw_data_RC = file-raw-data.rc.o
|
||||
file_sunras_RC = file-sunras.rc.o
|
||||
file_svg_RC = file-svg.rc.o
|
||||
file_tga_RC = file-tga.rc.o
|
||||
file_wbmp_RC = file-wbmp.rc.o
|
||||
file_wmf_RC = file-wmf.rc.o
|
||||
file_xbm_RC = file-xbm.rc.o
|
||||
file_xmc_RC = file-xmc.rc.o
|
||||
file_xpm_RC = file-xpm.rc.o
|
||||
file_xwd_RC = file-xwd.rc.o
|
||||
film_RC = film.rc.o
|
||||
gradient_map_RC = gradient-map.rc.o
|
||||
grid_RC = grid.rc.o
|
||||
guillotine_RC = guillotine.rc.o
|
||||
hot_RC = hot.rc.o
|
||||
jigsaw_RC = jigsaw.rc.o
|
||||
mail_RC = mail.rc.o
|
||||
nl_filter_RC = nl-filter.rc.o
|
||||
plugin_browser_RC = plugin-browser.rc.o
|
||||
procedure_browser_RC = procedure-browser.rc.o
|
||||
qbist_RC = qbist.rc.o
|
||||
sample_colorize_RC = sample-colorize.rc.o
|
||||
smooth_palette_RC = smooth-palette.rc.o
|
||||
sparkle_RC = sparkle.rc.o
|
||||
sphere_designer_RC = sphere-designer.rc.o
|
||||
tile_RC = tile.rc.o
|
||||
tile_small_RC = tile-small.rc.o
|
||||
unit_editor_RC = unit-editor.rc.o
|
||||
van_gogh_lic_RC = van-gogh-lic.rc.o
|
||||
warp_RC = warp.rc.o
|
||||
wavelet_decompose_RC = wavelet-decompose.rc.o
|
||||
web_browser_RC = web-browser.rc.o
|
||||
web_page_RC = web-page.rc.o
|
||||
878
plug-ins/common/plugin-browser.c
Normal file
878
plug-ins/common/plugin-browser.c
Normal file
@ -0,0 +1,878 @@
|
||||
/*
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* This is a plug-in for PIKA.
|
||||
*
|
||||
* Copyright (C) 1999 Andy Thomas alt@picnic.demon.co.uk
|
||||
*
|
||||
* Note some portions of the UI comes from the dbbrowser plugin.
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include <libpika/pika.h>
|
||||
#include <libpika/pikaui.h>
|
||||
|
||||
#include "libpika/stdplugins-intl.h"
|
||||
|
||||
|
||||
#define PLUG_IN_PROC "plug-in-plug-in-details"
|
||||
#define PLUG_IN_BINARY "plugin-browser"
|
||||
#define PLUG_IN_ROLE "pika-plugin-browser"
|
||||
#define DBL_LIST_WIDTH 250
|
||||
#define DBL_WIDTH (DBL_LIST_WIDTH + 400)
|
||||
#define DBL_HEIGHT 250
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
LIST_COLUMN_NAME,
|
||||
LIST_COLUMN_DATE,
|
||||
LIST_COLUMN_DATE_STRING,
|
||||
LIST_COLUMN_PATH,
|
||||
LIST_COLUMN_IMAGE_TYPES,
|
||||
LIST_COLUMN_PINFO,
|
||||
N_LIST_COLUMNS
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
TREE_COLUMN_PATH_NAME,
|
||||
TREE_COLUMN_DATE,
|
||||
TREE_COLUMN_DATE_STRING,
|
||||
TREE_COLUMN_IMAGE_TYPES,
|
||||
TREE_COLUMN_MPATH,
|
||||
TREE_COLUMN_PINFO,
|
||||
N_TREE_OLUMNS
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GtkWidget *dialog;
|
||||
|
||||
GtkWidget *browser;
|
||||
|
||||
GtkTreeView *list_view;
|
||||
GtkTreeView *tree_view;
|
||||
} PluginBrowser;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
gchar *menu;
|
||||
gchar *accel;
|
||||
gchar *prog;
|
||||
gchar *procedure;
|
||||
gint instime;
|
||||
} PInfo;
|
||||
|
||||
|
||||
typedef struct _Browser Browser;
|
||||
typedef struct _BrowserClass BrowserClass;
|
||||
|
||||
struct _Browser
|
||||
{
|
||||
PikaPlugIn parent_instance;
|
||||
};
|
||||
|
||||
struct _BrowserClass
|
||||
{
|
||||
PikaPlugInClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
/* Declare local functions.
|
||||
*/
|
||||
|
||||
#define BROWSER_TYPE (browser_get_type ())
|
||||
#define BROWSER (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), BROWSER_TYPE, Browser))
|
||||
|
||||
GType browser_get_type (void) G_GNUC_CONST;
|
||||
|
||||
static GList * browser_query_procedures (PikaPlugIn *plug_in);
|
||||
static PikaProcedure * browser_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name);
|
||||
|
||||
static PikaValueArray * browser_run (PikaProcedure *procedure,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data);
|
||||
|
||||
static GtkWidget * browser_dialog_new (void);
|
||||
static void browser_dialog_response (GtkWidget *widget,
|
||||
gint response_id,
|
||||
PluginBrowser *browser);
|
||||
static void browser_list_selection_changed (GtkTreeSelection *selection,
|
||||
PluginBrowser *browser);
|
||||
static void browser_tree_selection_changed (GtkTreeSelection *selection,
|
||||
PluginBrowser *browser);
|
||||
|
||||
static gboolean find_existing_mpath (GtkTreeModel *model,
|
||||
const gchar *mpath,
|
||||
GtkTreeIter *return_iter);
|
||||
|
||||
|
||||
G_DEFINE_TYPE (Browser, browser, PIKA_TYPE_PLUG_IN)
|
||||
|
||||
PIKA_MAIN (BROWSER_TYPE)
|
||||
DEFINE_STD_SET_I18N
|
||||
|
||||
|
||||
static void
|
||||
browser_class_init (BrowserClass *klass)
|
||||
{
|
||||
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
|
||||
|
||||
plug_in_class->query_procedures = browser_query_procedures;
|
||||
plug_in_class->create_procedure = browser_create_procedure;
|
||||
plug_in_class->set_i18n = STD_SET_I18N;
|
||||
}
|
||||
|
||||
static void
|
||||
browser_init (Browser *browser)
|
||||
{
|
||||
}
|
||||
|
||||
static GList *
|
||||
browser_query_procedures (PikaPlugIn *plug_in)
|
||||
{
|
||||
return g_list_append (NULL, g_strdup (PLUG_IN_PROC));
|
||||
}
|
||||
|
||||
static PikaProcedure *
|
||||
browser_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *procedure_name)
|
||||
{
|
||||
PikaProcedure *procedure = NULL;
|
||||
|
||||
if (! strcmp (procedure_name, PLUG_IN_PROC))
|
||||
{
|
||||
procedure = pika_procedure_new (plug_in, procedure_name,
|
||||
PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
browser_run, NULL, NULL);
|
||||
|
||||
pika_procedure_set_menu_label (procedure, _("_Plug-In Browser"));
|
||||
pika_procedure_set_icon_name (procedure, PIKA_ICON_PLUGIN);
|
||||
pika_procedure_add_menu_path (procedure, "<Image>/Help/[Programming]");
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
_("Display information about plug-ins"),
|
||||
_("Allows one to browse the plug-in "
|
||||
"menus system. You can search for "
|
||||
"plug-in names, sort by name or menu "
|
||||
"location and you can view a tree "
|
||||
"representation of the plug-in menus. "
|
||||
"Can also be of help to find where "
|
||||
"new plug-ins have installed "
|
||||
"themselves in the menus."),
|
||||
PLUG_IN_PROC);
|
||||
pika_procedure_set_attribution (procedure,
|
||||
"Andy Thomas",
|
||||
"Andy Thomas",
|
||||
"1999");
|
||||
|
||||
PIKA_PROC_ARG_ENUM (procedure, "run-mode",
|
||||
"Run mode",
|
||||
"The run mode",
|
||||
PIKA_TYPE_RUN_MODE,
|
||||
PIKA_RUN_INTERACTIVE,
|
||||
G_PARAM_READWRITE);
|
||||
}
|
||||
|
||||
return procedure;
|
||||
}
|
||||
|
||||
static PikaValueArray *
|
||||
browser_run (PikaProcedure *procedure,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data)
|
||||
{
|
||||
browser_dialog_new ();
|
||||
gtk_main ();
|
||||
|
||||
return pika_procedure_new_return_values (procedure, PIKA_PDB_SUCCESS, NULL);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
find_existing_mpath_helper (GtkTreeModel *model,
|
||||
GtkTreeIter *iter,
|
||||
GtkTreePath *path,
|
||||
const gchar *mpath,
|
||||
GtkTreeIter *return_iter)
|
||||
{
|
||||
do
|
||||
{
|
||||
GtkTreeIter child;
|
||||
gchar *picked_mpath;
|
||||
|
||||
gtk_tree_model_get (model, iter,
|
||||
TREE_COLUMN_MPATH, &picked_mpath,
|
||||
-1);
|
||||
|
||||
if (! strcmp (mpath, picked_mpath))
|
||||
{
|
||||
*return_iter = *iter;
|
||||
g_free (picked_mpath);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (gtk_tree_model_iter_children (model, &child, iter))
|
||||
{
|
||||
gtk_tree_path_down (path);
|
||||
|
||||
if (find_existing_mpath_helper (model, &child, path,
|
||||
mpath, return_iter))
|
||||
{
|
||||
g_free (picked_mpath);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gtk_tree_path_up (path);
|
||||
}
|
||||
|
||||
gtk_tree_path_next (path);
|
||||
g_free (picked_mpath);
|
||||
}
|
||||
while (gtk_tree_model_iter_next (model, iter));
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
find_existing_mpath (GtkTreeModel *model,
|
||||
const gchar *mpath,
|
||||
GtkTreeIter *return_iter)
|
||||
{
|
||||
GtkTreePath *path = gtk_tree_path_new_first ();
|
||||
GtkTreeIter parent;
|
||||
gboolean found;
|
||||
|
||||
if (! gtk_tree_model_get_iter (model, &parent, path))
|
||||
{
|
||||
gtk_tree_path_free (path);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
found = find_existing_mpath_helper (model, &parent, path, mpath, return_iter);
|
||||
|
||||
gtk_tree_path_free (path);
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
static void
|
||||
get_parent (PluginBrowser *browser,
|
||||
const gchar *mpath,
|
||||
GtkTreeIter *parent)
|
||||
{
|
||||
GtkTreeIter last_parent;
|
||||
gchar *tmp_ptr;
|
||||
gchar *str_ptr;
|
||||
GtkTreeStore *tree_store;
|
||||
|
||||
if (! mpath)
|
||||
return;
|
||||
|
||||
tree_store = GTK_TREE_STORE (gtk_tree_view_get_model (browser->tree_view));
|
||||
|
||||
/* Lookup for existing mpath */
|
||||
if (find_existing_mpath (GTK_TREE_MODEL (tree_store), mpath, parent))
|
||||
return;
|
||||
|
||||
tmp_ptr = g_strdup (mpath);
|
||||
|
||||
/* Strip off trailing ellipsis */
|
||||
str_ptr = strstr (mpath, "...");
|
||||
if (str_ptr && str_ptr == (mpath + strlen (mpath) - 3))
|
||||
*str_ptr = '\0';
|
||||
|
||||
str_ptr = strrchr (tmp_ptr, '/');
|
||||
|
||||
if (str_ptr == NULL)
|
||||
{
|
||||
gtk_tree_store_append (tree_store, parent, NULL);
|
||||
gtk_tree_store_set (tree_store, parent,
|
||||
TREE_COLUMN_MPATH, mpath,
|
||||
TREE_COLUMN_PATH_NAME, mpath,
|
||||
-1);
|
||||
}
|
||||
else
|
||||
{
|
||||
gchar *leaf_ptr;
|
||||
|
||||
leaf_ptr = g_strdup (str_ptr + 1);
|
||||
*str_ptr = '\0';
|
||||
|
||||
get_parent (browser, tmp_ptr, &last_parent);
|
||||
gtk_tree_store_append (tree_store, parent, &last_parent);
|
||||
gtk_tree_store_set (tree_store, parent,
|
||||
TREE_COLUMN_MPATH, mpath,
|
||||
TREE_COLUMN_PATH_NAME, leaf_ptr,
|
||||
-1);
|
||||
|
||||
g_free (leaf_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
insert_into_tree_view (PluginBrowser *browser,
|
||||
const gchar *name,
|
||||
gint64 xtime,
|
||||
const gchar *xtimestr,
|
||||
const gchar *menu_path,
|
||||
const gchar *types_str,
|
||||
PInfo *pinfo)
|
||||
{
|
||||
GtkTreeStore *tree_store;
|
||||
GtkTreeIter parent, iter;
|
||||
|
||||
get_parent (browser, menu_path, &parent);
|
||||
|
||||
tree_store = GTK_TREE_STORE (gtk_tree_view_get_model (browser->tree_view));
|
||||
gtk_tree_store_append (tree_store, &iter, &parent);
|
||||
gtk_tree_store_set (tree_store, &iter,
|
||||
TREE_COLUMN_MPATH, menu_path,
|
||||
TREE_COLUMN_PATH_NAME, name,
|
||||
TREE_COLUMN_IMAGE_TYPES, types_str,
|
||||
TREE_COLUMN_DATE, xtime,
|
||||
TREE_COLUMN_DATE_STRING, xtimestr,
|
||||
TREE_COLUMN_PINFO, pinfo,
|
||||
-1);
|
||||
}
|
||||
|
||||
static void
|
||||
browser_search (PikaBrowser *pika_browser,
|
||||
const gchar *search_text,
|
||||
gint search_type,
|
||||
PluginBrowser *browser)
|
||||
{
|
||||
PikaValueArray *return_vals;
|
||||
const gchar **procedure_strs;
|
||||
gint num_plugins = 0;
|
||||
gchar *str;
|
||||
GtkListStore *list_store;
|
||||
GtkTreeStore *tree_store;
|
||||
|
||||
pika_browser_show_message (PIKA_BROWSER (browser->browser),
|
||||
_("Searching by name"));
|
||||
|
||||
return_vals = pika_pdb_run_procedure (pika_get_pdb (),
|
||||
"pika-plug-ins-query",
|
||||
G_TYPE_STRING, search_text,
|
||||
G_TYPE_NONE);
|
||||
|
||||
if (PIKA_VALUES_GET_ENUM (return_vals, 0) == PIKA_PDB_SUCCESS)
|
||||
{
|
||||
procedure_strs = PIKA_VALUES_GET_STRV (return_vals, 1);
|
||||
num_plugins = g_strv_length ((gchar **) procedure_strs);
|
||||
}
|
||||
|
||||
if (! search_text || strlen (search_text) == 0)
|
||||
{
|
||||
str = g_strdup_printf (ngettext ("%d plug-in", "%d plug-ins",
|
||||
num_plugins),
|
||||
num_plugins);
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (num_plugins)
|
||||
{
|
||||
case 0:
|
||||
str = g_strdup (_("No matches for your query"));
|
||||
break;
|
||||
default:
|
||||
str = g_strdup_printf (ngettext ("%d plug-in matches your query",
|
||||
"%d plug-ins match your query",
|
||||
num_plugins), num_plugins);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pika_browser_set_search_summary (pika_browser, str);
|
||||
g_free (str);
|
||||
|
||||
list_store = GTK_LIST_STORE (gtk_tree_view_get_model (browser->list_view));
|
||||
gtk_list_store_clear (list_store);
|
||||
|
||||
tree_store = GTK_TREE_STORE (gtk_tree_view_get_model (browser->tree_view));
|
||||
gtk_tree_store_clear (tree_store);
|
||||
|
||||
if (num_plugins > 0)
|
||||
{
|
||||
GtkTreeSelection *sel;
|
||||
GtkTreeIter iter;
|
||||
const gchar **accel_strs;
|
||||
const gchar **prog_strs;
|
||||
const gint *time_ints;
|
||||
gint i;
|
||||
|
||||
accel_strs = PIKA_VALUES_GET_STRV (return_vals, 2);
|
||||
prog_strs = PIKA_VALUES_GET_STRV (return_vals, 3);
|
||||
time_ints = PIKA_VALUES_GET_INT32_ARRAY (return_vals, 5);
|
||||
|
||||
for (i = 0; i < num_plugins; i++)
|
||||
{
|
||||
PikaProcedure *procedure;
|
||||
const gchar *types;
|
||||
PInfo *pinfo;
|
||||
gchar *menu_label;
|
||||
gchar *tmp;
|
||||
GList *menu_paths;
|
||||
const gchar *menu_path;
|
||||
gchar xtimestr[50];
|
||||
struct tm *x;
|
||||
time_t tx;
|
||||
gint ret;
|
||||
|
||||
procedure = pika_pdb_lookup_procedure (pika_get_pdb (),
|
||||
procedure_strs[i]);
|
||||
|
||||
types = pika_procedure_get_image_types (procedure);
|
||||
menu_label = g_strdup (pika_procedure_get_menu_label (procedure));
|
||||
menu_paths = pika_procedure_get_menu_paths (procedure);
|
||||
|
||||
menu_path = menu_paths->data;
|
||||
|
||||
/* Strip off trailing ellipsis */
|
||||
tmp = strstr (menu_label, "...");
|
||||
if (tmp && tmp == (menu_label + strlen (menu_label) - 3))
|
||||
*tmp = '\0';
|
||||
|
||||
tmp = pika_strip_uline (menu_label);
|
||||
g_free (menu_label);
|
||||
menu_label = tmp;
|
||||
|
||||
tx = time_ints[i];
|
||||
if (tx)
|
||||
{
|
||||
const gchar *format = "%c"; /* gcc workaround to avoid warning */
|
||||
gchar *utf8;
|
||||
|
||||
x = localtime (&tx);
|
||||
ret = strftime (xtimestr, sizeof (xtimestr), format, x);
|
||||
xtimestr[ret] = 0;
|
||||
|
||||
if ((utf8 = g_locale_to_utf8 (xtimestr, -1, NULL, NULL, NULL)))
|
||||
{
|
||||
g_strlcpy (xtimestr, utf8, sizeof (xtimestr));
|
||||
g_free (utf8);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
strcpy (xtimestr, "");
|
||||
}
|
||||
|
||||
pinfo = g_new0 (PInfo, 1);
|
||||
|
||||
pinfo->menu = g_strdup (menu_path);
|
||||
pinfo->accel = g_strdup (accel_strs[i]);
|
||||
pinfo->prog = g_strdup (prog_strs[i]);
|
||||
pinfo->instime = time_ints[i];
|
||||
pinfo->procedure = g_strdup (procedure_strs[i]);
|
||||
|
||||
gtk_list_store_append (list_store, &iter);
|
||||
gtk_list_store_set (list_store, &iter,
|
||||
LIST_COLUMN_NAME, menu_label,
|
||||
LIST_COLUMN_DATE, (gint64) tx,
|
||||
LIST_COLUMN_DATE_STRING, xtimestr,
|
||||
LIST_COLUMN_PATH, menu_path,
|
||||
LIST_COLUMN_IMAGE_TYPES, types,
|
||||
LIST_COLUMN_PINFO, pinfo,
|
||||
-1);
|
||||
|
||||
/* Now do the tree view.... */
|
||||
insert_into_tree_view (browser,
|
||||
menu_label,
|
||||
(gint64) tx,
|
||||
xtimestr,
|
||||
menu_path,
|
||||
types,
|
||||
pinfo);
|
||||
|
||||
g_free (menu_label);
|
||||
}
|
||||
|
||||
gtk_tree_view_columns_autosize (GTK_TREE_VIEW (browser->list_view));
|
||||
gtk_tree_view_columns_autosize (GTK_TREE_VIEW (browser->tree_view));
|
||||
|
||||
gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (list_store),
|
||||
LIST_COLUMN_NAME,
|
||||
GTK_SORT_ASCENDING);
|
||||
gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (tree_store),
|
||||
TREE_COLUMN_PATH_NAME,
|
||||
GTK_SORT_ASCENDING);
|
||||
|
||||
sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (browser->list_view));
|
||||
|
||||
gtk_tree_model_get_iter_first (GTK_TREE_MODEL (list_store),
|
||||
&iter);
|
||||
gtk_tree_selection_select_iter (sel, &iter);
|
||||
}
|
||||
else
|
||||
{
|
||||
pika_browser_show_message (PIKA_BROWSER (browser->browser),
|
||||
_("No matches"));
|
||||
}
|
||||
|
||||
pika_value_array_unref (return_vals);
|
||||
}
|
||||
|
||||
static GtkWidget *
|
||||
browser_dialog_new (void)
|
||||
{
|
||||
PluginBrowser *browser;
|
||||
GtkWidget *label, *notebook;
|
||||
GtkWidget *scrolled_window;
|
||||
GtkListStore *list_store;
|
||||
GtkTreeStore *tree_store;
|
||||
GtkWidget *list_view;
|
||||
GtkWidget *tree_view;
|
||||
GtkWidget *parent;
|
||||
GtkTreeViewColumn *column;
|
||||
GtkCellRenderer *renderer;
|
||||
GtkTreeSelection *selection;
|
||||
GtkTreeIter iter;
|
||||
|
||||
pika_ui_init (PLUG_IN_BINARY);
|
||||
|
||||
browser = g_new0 (PluginBrowser, 1);
|
||||
|
||||
browser->dialog = pika_dialog_new (_("Plug-in Browser"), PLUG_IN_ROLE,
|
||||
NULL, 0,
|
||||
pika_standard_help_func, PLUG_IN_PROC,
|
||||
|
||||
_("_Close"), GTK_RESPONSE_CLOSE,
|
||||
|
||||
NULL);
|
||||
gtk_window_set_default_size (GTK_WINDOW (browser->dialog), DBL_WIDTH,
|
||||
DBL_WIDTH - DBL_LIST_WIDTH);
|
||||
|
||||
g_signal_connect (browser->dialog, "response",
|
||||
G_CALLBACK (browser_dialog_response),
|
||||
browser);
|
||||
|
||||
browser->browser = pika_browser_new ();
|
||||
gtk_container_set_border_width (GTK_CONTAINER (browser->browser), 12);
|
||||
gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (browser->dialog))),
|
||||
browser->browser, TRUE, TRUE, 0);
|
||||
gtk_widget_show (browser->browser);
|
||||
|
||||
g_signal_connect (browser->browser, "search",
|
||||
G_CALLBACK (browser_search),
|
||||
browser);
|
||||
|
||||
/* left = notebook */
|
||||
|
||||
notebook = gtk_notebook_new ();
|
||||
gtk_box_pack_start (GTK_BOX (pika_browser_get_left_vbox (PIKA_BROWSER (browser->browser))),
|
||||
notebook, TRUE, TRUE, 0);
|
||||
|
||||
/* list : list in a scrolled_win */
|
||||
list_store = gtk_list_store_new (N_LIST_COLUMNS,
|
||||
G_TYPE_STRING,
|
||||
G_TYPE_INT64,
|
||||
G_TYPE_STRING,
|
||||
G_TYPE_STRING,
|
||||
G_TYPE_STRING,
|
||||
G_TYPE_POINTER);
|
||||
|
||||
list_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (list_store));
|
||||
g_object_unref (list_store);
|
||||
|
||||
browser->list_view = GTK_TREE_VIEW (list_view);
|
||||
|
||||
renderer = gtk_cell_renderer_text_new ();
|
||||
column = gtk_tree_view_column_new_with_attributes (_("Name"),
|
||||
renderer,
|
||||
"text", LIST_COLUMN_NAME,
|
||||
NULL);
|
||||
gtk_tree_view_column_set_sort_column_id (column, LIST_COLUMN_NAME);
|
||||
gtk_tree_view_append_column (GTK_TREE_VIEW (list_view), column);
|
||||
|
||||
renderer = gtk_cell_renderer_text_new ();
|
||||
column = gtk_tree_view_column_new_with_attributes (_("Menu Path"),
|
||||
renderer,
|
||||
"text", LIST_COLUMN_PATH,
|
||||
NULL);
|
||||
gtk_tree_view_column_set_sort_column_id (column, LIST_COLUMN_PATH);
|
||||
gtk_tree_view_append_column (GTK_TREE_VIEW (list_view), column);
|
||||
|
||||
renderer = gtk_cell_renderer_text_new ();
|
||||
column = gtk_tree_view_column_new_with_attributes (_("Image Types"),
|
||||
renderer,
|
||||
"text",
|
||||
LIST_COLUMN_IMAGE_TYPES,
|
||||
NULL);
|
||||
gtk_tree_view_column_set_sort_column_id (column, LIST_COLUMN_IMAGE_TYPES);
|
||||
gtk_tree_view_append_column (GTK_TREE_VIEW (list_view), column);
|
||||
|
||||
renderer = gtk_cell_renderer_text_new ();
|
||||
|
||||
column = gtk_tree_view_column_new_with_attributes (_("Installation Date"),
|
||||
renderer,
|
||||
"text",
|
||||
LIST_COLUMN_DATE_STRING,
|
||||
NULL);
|
||||
gtk_tree_view_column_set_sort_column_id (column, LIST_COLUMN_DATE);
|
||||
gtk_tree_view_append_column (GTK_TREE_VIEW (list_view), column);
|
||||
|
||||
scrolled_window = gtk_scrolled_window_new (NULL, NULL);
|
||||
gtk_container_set_border_width (GTK_CONTAINER (scrolled_window), 2);
|
||||
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window),
|
||||
GTK_SHADOW_IN);
|
||||
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
|
||||
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
|
||||
|
||||
gtk_widget_set_size_request (list_view, DBL_LIST_WIDTH, DBL_HEIGHT);
|
||||
|
||||
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list_view));
|
||||
gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
|
||||
|
||||
g_signal_connect (selection, "changed",
|
||||
G_CALLBACK (browser_list_selection_changed),
|
||||
browser);
|
||||
|
||||
label = gtk_label_new (_("List View"));
|
||||
gtk_notebook_append_page (GTK_NOTEBOOK (notebook), scrolled_window, label);
|
||||
gtk_container_add (GTK_CONTAINER (scrolled_window), list_view);
|
||||
gtk_widget_show (list_view);
|
||||
gtk_widget_show (scrolled_window);
|
||||
|
||||
/* notebook->ctree */
|
||||
tree_store = gtk_tree_store_new (N_LIST_COLUMNS,
|
||||
G_TYPE_STRING,
|
||||
G_TYPE_INT64,
|
||||
G_TYPE_STRING,
|
||||
G_TYPE_STRING,
|
||||
G_TYPE_STRING,
|
||||
G_TYPE_POINTER);
|
||||
|
||||
tree_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (tree_store));
|
||||
g_object_unref (tree_store);
|
||||
|
||||
browser->tree_view = GTK_TREE_VIEW (tree_view);
|
||||
|
||||
renderer = gtk_cell_renderer_text_new ();
|
||||
column = gtk_tree_view_column_new_with_attributes (_("Menu Path"),
|
||||
renderer,
|
||||
"text",
|
||||
TREE_COLUMN_PATH_NAME,
|
||||
NULL);
|
||||
gtk_tree_view_column_set_sort_column_id (column, TREE_COLUMN_PATH_NAME);
|
||||
gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
|
||||
|
||||
renderer = gtk_cell_renderer_text_new ();
|
||||
column = gtk_tree_view_column_new_with_attributes (_("Image Types"),
|
||||
renderer,
|
||||
"text",
|
||||
TREE_COLUMN_IMAGE_TYPES,
|
||||
NULL);
|
||||
gtk_tree_view_column_set_sort_column_id (column, TREE_COLUMN_IMAGE_TYPES);
|
||||
gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
|
||||
|
||||
renderer = gtk_cell_renderer_text_new ();
|
||||
column = gtk_tree_view_column_new_with_attributes (_("Installation Date"),
|
||||
renderer,
|
||||
"text",
|
||||
TREE_COLUMN_DATE_STRING,
|
||||
NULL);
|
||||
gtk_tree_view_column_set_sort_column_id (column, TREE_COLUMN_DATE);
|
||||
gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
|
||||
|
||||
scrolled_window = gtk_scrolled_window_new (NULL, NULL);
|
||||
gtk_container_set_border_width (GTK_CONTAINER (scrolled_window), 2);
|
||||
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window),
|
||||
GTK_SHADOW_IN);
|
||||
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
|
||||
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
|
||||
gtk_widget_set_size_request (tree_view, DBL_LIST_WIDTH, DBL_HEIGHT);
|
||||
|
||||
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view));
|
||||
gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
|
||||
|
||||
g_signal_connect (selection, "changed",
|
||||
G_CALLBACK (browser_tree_selection_changed),
|
||||
browser);
|
||||
|
||||
label = gtk_label_new (_("Tree View"));
|
||||
gtk_notebook_append_page (GTK_NOTEBOOK (notebook), scrolled_window, label);
|
||||
gtk_container_add (GTK_CONTAINER (scrolled_window), tree_view);
|
||||
|
||||
gtk_widget_show (tree_view);
|
||||
gtk_widget_show (scrolled_window);
|
||||
gtk_widget_show (notebook);
|
||||
|
||||
parent = gtk_widget_get_parent (pika_browser_get_right_vbox (PIKA_BROWSER (browser->browser)));
|
||||
parent = gtk_widget_get_parent (parent);
|
||||
|
||||
gtk_widget_set_size_request (parent, DBL_WIDTH - DBL_LIST_WIDTH, -1);
|
||||
|
||||
/* now build the list */
|
||||
browser_search (PIKA_BROWSER (browser->browser), "", 0, browser);
|
||||
|
||||
gtk_widget_show (browser->dialog);
|
||||
|
||||
if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (list_store), &iter))
|
||||
gtk_tree_selection_select_iter (gtk_tree_view_get_selection (GTK_TREE_VIEW (list_view)),
|
||||
&iter);
|
||||
|
||||
return browser->dialog;
|
||||
}
|
||||
|
||||
static void
|
||||
browser_dialog_response (GtkWidget *widget,
|
||||
gint response_id,
|
||||
PluginBrowser *browser)
|
||||
{
|
||||
gtk_widget_destroy (browser->dialog);
|
||||
gtk_main_quit ();
|
||||
}
|
||||
|
||||
static void
|
||||
browser_list_selection_changed (GtkTreeSelection *selection,
|
||||
PluginBrowser *browser)
|
||||
{
|
||||
PInfo *pinfo = NULL;
|
||||
GtkTreeIter iter;
|
||||
GtkTreeModel *model;
|
||||
gchar *mpath = NULL;
|
||||
|
||||
g_return_if_fail (browser != NULL);
|
||||
|
||||
if (gtk_tree_selection_get_selected (selection, &model, &iter))
|
||||
{
|
||||
gtk_tree_model_get (model, &iter,
|
||||
LIST_COLUMN_PINFO, &pinfo,
|
||||
LIST_COLUMN_PATH, &mpath,
|
||||
-1);
|
||||
}
|
||||
|
||||
if (!pinfo || !mpath)
|
||||
return;
|
||||
|
||||
model = gtk_tree_view_get_model (browser->tree_view);
|
||||
|
||||
if (find_existing_mpath (model, mpath, &iter))
|
||||
{
|
||||
GtkTreeSelection *tree_selection;
|
||||
GtkTreePath *tree_path;
|
||||
|
||||
tree_path = gtk_tree_model_get_path (model, &iter);
|
||||
gtk_tree_view_expand_to_path (browser->tree_view, tree_path);
|
||||
tree_selection = gtk_tree_view_get_selection (browser->tree_view);
|
||||
|
||||
g_signal_handlers_block_by_func (tree_selection,
|
||||
browser_tree_selection_changed,
|
||||
browser);
|
||||
gtk_tree_selection_select_iter (tree_selection, &iter);
|
||||
g_signal_handlers_unblock_by_func (tree_selection,
|
||||
browser_tree_selection_changed,
|
||||
browser);
|
||||
|
||||
gtk_tree_view_scroll_to_cell (browser->tree_view,
|
||||
tree_path, NULL,
|
||||
TRUE, 0.5, 0.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_warning ("Failed to find node in tree");
|
||||
}
|
||||
|
||||
g_free (mpath);
|
||||
|
||||
pika_browser_set_widget (PIKA_BROWSER (browser->browser),
|
||||
pika_proc_view_new (pinfo->procedure));
|
||||
}
|
||||
|
||||
static void
|
||||
browser_tree_selection_changed (GtkTreeSelection *selection,
|
||||
PluginBrowser *browser)
|
||||
{
|
||||
PInfo *pinfo = NULL;
|
||||
GtkTreeIter iter;
|
||||
GtkTreeModel *model;
|
||||
gchar *mpath = NULL;
|
||||
gboolean valid, found;
|
||||
|
||||
g_return_if_fail (browser != NULL);
|
||||
|
||||
if (gtk_tree_selection_get_selected (selection, &model, &iter))
|
||||
{
|
||||
gtk_tree_model_get (model, &iter,
|
||||
TREE_COLUMN_PINFO, &pinfo,
|
||||
TREE_COLUMN_MPATH, &mpath,
|
||||
-1);
|
||||
}
|
||||
|
||||
if (!pinfo || !mpath)
|
||||
return;
|
||||
|
||||
/* Get the first iter in the list */
|
||||
model = gtk_tree_view_get_model (browser->list_view);
|
||||
valid = gtk_tree_model_get_iter_first (model, &iter);
|
||||
found = FALSE;
|
||||
|
||||
while (valid)
|
||||
{
|
||||
/* Walk through the list, reading each row */
|
||||
gchar *picked_mpath;
|
||||
|
||||
gtk_tree_model_get (model, &iter,
|
||||
LIST_COLUMN_PATH, &picked_mpath,
|
||||
-1);
|
||||
if (picked_mpath && !strcmp (mpath, picked_mpath))
|
||||
{
|
||||
found = TRUE;
|
||||
break;
|
||||
}
|
||||
|
||||
g_free (picked_mpath);
|
||||
valid = gtk_tree_model_iter_next (model, &iter);
|
||||
}
|
||||
|
||||
g_free (mpath);
|
||||
|
||||
if (found)
|
||||
{
|
||||
GtkTreeSelection *list_selection;
|
||||
GtkTreePath *tree_path;
|
||||
|
||||
tree_path = gtk_tree_model_get_path (model, &iter);
|
||||
list_selection = gtk_tree_view_get_selection (browser->list_view);
|
||||
|
||||
g_signal_handlers_block_by_func (list_selection,
|
||||
browser_list_selection_changed,
|
||||
browser);
|
||||
gtk_tree_selection_select_iter (list_selection, &iter);
|
||||
g_signal_handlers_unblock_by_func (list_selection,
|
||||
browser_list_selection_changed,
|
||||
browser);
|
||||
|
||||
gtk_tree_view_scroll_to_cell (browser->list_view,
|
||||
tree_path, NULL,
|
||||
TRUE, 0.5, 0.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_warning ("Failed to find node in list");
|
||||
}
|
||||
|
||||
pika_browser_set_widget (PIKA_BROWSER (browser->browser),
|
||||
pika_proc_view_new (pinfo->procedure));
|
||||
}
|
||||
79
plug-ins/common/plugin-defs.pl
Normal file
79
plug-ins/common/plugin-defs.pl
Normal file
@ -0,0 +1,79 @@
|
||||
%plugins = (
|
||||
'align-layers' => { ui => 1 },
|
||||
'animation-optimize' => { gegl => 1 },
|
||||
'animation-play' => { ui => 1, gegl => 1 },
|
||||
'blinds' => { ui => 1, gegl => 1 },
|
||||
'border-average' => { ui => 1, gegl => 1 },
|
||||
'busy-dialog' => { ui => 1, gegl => 1 },
|
||||
'checkerboard' => { ui => 1, gegl => 1 },
|
||||
'cml-explorer' => { ui => 1, gegl => 1 },
|
||||
'colormap-remap' => { ui => 1, gegl => 1 },
|
||||
'compose' => { ui => 1, gegl => 1 },
|
||||
'contrast-retinex' => { ui => 1, gegl => 1 },
|
||||
'crop-zealous' => { gegl => 1 },
|
||||
'curve-bend' => { ui => 1, gegl => 1 },
|
||||
'decompose' => { ui => 1, gegl => 1 },
|
||||
'depth-merge' => { ui => 1, gegl => 1 },
|
||||
'despeckle' => { ui => 1, gegl => 1 },
|
||||
'destripe' => { ui => 1, gegl => 1 },
|
||||
'file-aa' => { ui => 1, gegl => 1, optional => 1, libs => 'AA_LIBS' },
|
||||
'file-cel' => { ui => 1, gegl => 1 },
|
||||
'file-csource' => { ui => 1, gegl => 1 },
|
||||
'file-compressor' => { gio => 1, libdep => 'Z:BZIP2:LZMA', cflags => 'LZMA_CFLAGS' },
|
||||
'file-desktop-link' => { gio => 1 },
|
||||
'file-dicom' => { ui => 1, gegl => 1, cflags => '-fno-strict-aliasing' },
|
||||
'file-gbr' => { ui => 1, gegl => 1 },
|
||||
'file-gegl' => { ui => 1, gegl => 1 },
|
||||
'file-gif-load' => { gegl => 1 },
|
||||
'file-gif-save' => { ui => 1, gegl => 1 },
|
||||
'file-gih' => { ui => 1, gegl => 1 },
|
||||
'file-glob' => {},
|
||||
'file-header' => { ui => 1, gegl => 1 },
|
||||
'file-heif' => { ui => 1, optional => 1, gegl => 1, libdep => 'GEXIV2:LCMS', libs => 'LIBHEIF_LIBS', cflags => 'LIBHEIF_CFLAGS' },
|
||||
'file-html-table' => { ui => 1, gegl => 1 },
|
||||
'file-jp2-load' => { ui => 1, optional => 1, gegl => 1, libs => 'OPENJPEG_LIBS', cflags => 'OPENJPEG_CFLAGS' },
|
||||
'file-jpegxl' => { ui => 1, optional => 1, gegl => 1, libdep => 'GEXIV2:JXL:JXL_THREADS', cflags => 'JXL_CFLAGS' },
|
||||
'file-mng' => { ui => 1, gegl => 1, optional => 1, libs => 'MNG_LIBS', cflags => 'MNG_CFLAGS' },
|
||||
'file-pat' => { ui => 1, gegl => 1 },
|
||||
'file-pcx' => { ui => 1, gegl => 1 },
|
||||
'file-pix' => { ui => 1, gegl => 1 },
|
||||
'file-png' => { ui => 1, gegl => 0, libdep => 'LCMS', libs => 'PNG_LIBS', cflags => 'PNG_CFLAGS' },
|
||||
'file-pnm' => { ui => 1, gegl => 1 },
|
||||
'file-pdf-load' => { ui => 1, gegl => 1, libs => 'POPPLER_LIBS', cflags => 'POPPLER_CFLAGS' },
|
||||
'file-pdf-save' => { ui => 1, gegl => 1, optional => 1, libs => 'CAIRO_PDF_LIBS', cflags => 'CAIRO_PDF_CFLAGS' },
|
||||
'file-ps' => { ui => 1, gegl => 1, optional => 1, libs => 'GS_LIBS' },
|
||||
'file-psp' => { ui => 1, gegl => 1, libs => 'Z_LIBS' },
|
||||
'file-raw-data' => { ui => 1, gegl => 1 },
|
||||
'file-sunras' => { ui => 1, gegl => 1 },
|
||||
'file-svg' => { ui => 1, gegl => 1, libs => 'SVG_LIBS', cflags => 'SVG_CFLAGS' },
|
||||
'file-tga' => { ui => 1, gegl => 1 },
|
||||
'file-wbmp' => { ui => 1, gegl => 1 },
|
||||
'file-wmf' => { ui => 1, gegl => 1, optional => 1, libs => 'WMF_LIBS', cflags => 'WMF_CFLAGS' },
|
||||
'file-xbm' => { ui => 1, gegl => 1 },
|
||||
'file-xmc' => { ui => 1, gegl => 1, optional => 1, libs => 'XMC_LIBS' },
|
||||
'file-xpm' => { ui => 1, gegl => 1, optional => 1, libs => 'XPM_LIBS' },
|
||||
'file-xwd' => { ui => 1, gegl => 1 },
|
||||
'film' => { ui => 1, gegl => 1 },
|
||||
'gradient-map' => { gegl => 1 },
|
||||
'grid' => { ui => 1, gegl => 1 },
|
||||
'guillotine' => { gio => 1 },
|
||||
'hot' => { ui => 1, gegl => 1 },
|
||||
'jigsaw' => { ui => 1, gegl => 1 },
|
||||
'mail' => { ui => 1, optional => 1 },
|
||||
'nl-filter' => { ui => 1, gegl => 1 },
|
||||
'plugin-browser' => { ui => 1 },
|
||||
'procedure-browser' => { ui => 1 },
|
||||
'qbist' => { ui => 1, gegl => 1 },
|
||||
'sample-colorize' => { ui => 1, gegl => 1 },
|
||||
'smooth-palette' => { ui => 1, gegl => 1 },
|
||||
'sparkle' => { ui => 1, gegl => 1 },
|
||||
'sphere-designer' => { ui => 1, gegl => 1 },
|
||||
'tile' => { ui => 1, gegl => 1 },
|
||||
'tile-small' => { ui => 1, gegl => 1 },
|
||||
'unit-editor' => { ui => 1 },
|
||||
'van-gogh-lic' => { ui => 1, gegl => 1 },
|
||||
'warp' => { ui => 1, gegl => 1 },
|
||||
'wavelet-decompose' => { ui => 1, gegl => 1 },
|
||||
'web-browser' => { ui => 1, ldflags => '$(framework_cocoa)', cppflags => '$(AM_CPPFLAGS) $(xobjective_c)' },
|
||||
'web-page' => { ui => 1, optional => 1, libs => 'WEBKIT_LIBS', cflags => 'WEBKIT_CFLAGS' }
|
||||
);
|
||||
195
plug-ins/common/procedure-browser.c
Normal file
195
plug-ins/common/procedure-browser.c
Normal file
@ -0,0 +1,195 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* dbbrowser
|
||||
* 0.08 26th sept 97 by Thomas NOEL <thomas@minet.net>
|
||||
*/
|
||||
|
||||
/*
|
||||
* This plugin gives you the list of available procedure, with the
|
||||
* name, description and parameters for each procedure.
|
||||
* You can do regexp search (by name and by description)
|
||||
* Useful for scripts development.
|
||||
*
|
||||
* NOTE :
|
||||
* this is only a exercice for me (my first "plug-in" (extension))
|
||||
* so it's very (very) dirty.
|
||||
* Btw, hope it gives you some ideas about pika possibilities.
|
||||
*
|
||||
* The core of the plugin is not here. See dbbrowser_utils (shared
|
||||
* with script-fu-console).
|
||||
*
|
||||
* TODO
|
||||
* - bug fixes... (my method : rewrite from scratch :)
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include <libpika/pika.h>
|
||||
#include <libpika/pikaui.h>
|
||||
|
||||
#include "libpika/stdplugins-intl.h"
|
||||
|
||||
|
||||
#define PLUG_IN_PROC "plug-in-dbbrowser"
|
||||
#define PLUG_IN_BINARY "procedure-browser"
|
||||
#define PLUG_IN_ROLE "pika-procedure-browser"
|
||||
|
||||
|
||||
typedef struct _Browser Browser;
|
||||
typedef struct _BrowserClass BrowserClass;
|
||||
|
||||
struct _Browser
|
||||
{
|
||||
PikaPlugIn parent_instance;
|
||||
};
|
||||
|
||||
struct _BrowserClass
|
||||
{
|
||||
PikaPlugInClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
/* Declare local functions.
|
||||
*/
|
||||
|
||||
#define BROWSER_TYPE (browser_get_type ())
|
||||
#define BROWSER (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), BROWSER_TYPE, Browser))
|
||||
|
||||
GType browser_get_type (void) G_GNUC_CONST;
|
||||
|
||||
static GList * browser_query_procedures (PikaPlugIn *plug_in);
|
||||
static PikaProcedure * browser_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name);
|
||||
|
||||
static PikaValueArray * browser_run (PikaProcedure *procedure,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data);
|
||||
|
||||
|
||||
G_DEFINE_TYPE (Browser, browser, PIKA_TYPE_PLUG_IN)
|
||||
|
||||
PIKA_MAIN (BROWSER_TYPE)
|
||||
DEFINE_STD_SET_I18N
|
||||
|
||||
|
||||
static void
|
||||
browser_class_init (BrowserClass *klass)
|
||||
{
|
||||
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
|
||||
|
||||
plug_in_class->query_procedures = browser_query_procedures;
|
||||
plug_in_class->create_procedure = browser_create_procedure;
|
||||
plug_in_class->set_i18n = STD_SET_I18N;
|
||||
}
|
||||
|
||||
static void
|
||||
browser_init (Browser *browser)
|
||||
{
|
||||
}
|
||||
|
||||
static GList *
|
||||
browser_query_procedures (PikaPlugIn *plug_in)
|
||||
{
|
||||
return g_list_append (NULL, g_strdup (PLUG_IN_PROC));
|
||||
}
|
||||
|
||||
static PikaProcedure *
|
||||
browser_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name)
|
||||
{
|
||||
PikaProcedure *procedure = NULL;
|
||||
|
||||
if (! strcmp (name, PLUG_IN_PROC))
|
||||
{
|
||||
procedure = pika_procedure_new (plug_in, name,
|
||||
PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
browser_run, NULL, NULL);
|
||||
|
||||
pika_procedure_set_menu_label (procedure, _("Procedure _Browser"));
|
||||
pika_procedure_add_menu_path (procedure, "<Image>/Help/[Programming]");
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
_("List available procedures in the PDB"),
|
||||
NULL,
|
||||
PLUG_IN_PROC);
|
||||
pika_procedure_set_attribution (procedure,
|
||||
"Thomas Noel",
|
||||
"Thomas Noel",
|
||||
"23th june 1997");
|
||||
|
||||
PIKA_PROC_ARG_ENUM (procedure, "run-mode",
|
||||
"Run mode",
|
||||
"The run mode",
|
||||
PIKA_TYPE_RUN_MODE,
|
||||
PIKA_RUN_INTERACTIVE,
|
||||
G_PARAM_READWRITE);
|
||||
}
|
||||
|
||||
return procedure;
|
||||
}
|
||||
|
||||
static PikaValueArray *
|
||||
browser_run (PikaProcedure *procedure,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data)
|
||||
{
|
||||
PikaRunMode run_mode = PIKA_VALUES_GET_ENUM (args, 0);
|
||||
|
||||
switch (run_mode)
|
||||
{
|
||||
case PIKA_RUN_INTERACTIVE:
|
||||
{
|
||||
GtkWidget *dialog;
|
||||
|
||||
pika_ui_init (PLUG_IN_BINARY);
|
||||
|
||||
dialog =
|
||||
pika_proc_browser_dialog_new (_("Procedure Browser"), PLUG_IN_BINARY,
|
||||
pika_standard_help_func, PLUG_IN_PROC,
|
||||
|
||||
_("_Close"), GTK_RESPONSE_CLOSE,
|
||||
|
||||
NULL);
|
||||
|
||||
gtk_dialog_run (GTK_DIALOG (dialog));
|
||||
gtk_widget_destroy (dialog);
|
||||
}
|
||||
break;
|
||||
|
||||
case PIKA_RUN_WITH_LAST_VALS:
|
||||
case PIKA_RUN_NONINTERACTIVE:
|
||||
g_printerr (PLUG_IN_PROC " allows only interactive invocation");
|
||||
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_CALLING_ERROR,
|
||||
NULL);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return pika_procedure_new_return_values (procedure, PIKA_PDB_SUCCESS, NULL);
|
||||
}
|
||||
1059
plug-ins/common/qbist.c
Normal file
1059
plug-ins/common/qbist.c
Normal file
File diff suppressed because it is too large
Load Diff
3174
plug-ins/common/sample-colorize.c
Normal file
3174
plug-ins/common/sample-colorize.c
Normal file
File diff suppressed because it is too large
Load Diff
571
plug-ins/common/smooth-palette.c
Normal file
571
plug-ins/common/smooth-palette.c
Normal file
@ -0,0 +1,571 @@
|
||||
/*
|
||||
* smooth palette - derive smooth palette from image
|
||||
* Copyright (C) 1997 Scott Draves <spot@cs.cmu.edu>
|
||||
*
|
||||
* PIKA - Photo and Image Kooker Application
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <libpika/pika.h>
|
||||
#include <libpika/pikaui.h>
|
||||
|
||||
#include "libpika/stdplugins-intl.h"
|
||||
|
||||
|
||||
#define PLUG_IN_PROC "plug-in-smooth-palette"
|
||||
#define PLUG_IN_BINARY "smooth-palette"
|
||||
#define PLUG_IN_ROLE "pika-smooth-palette"
|
||||
|
||||
|
||||
typedef struct _Palette Palette;
|
||||
typedef struct _PaletteClass PaletteClass;
|
||||
|
||||
struct _Palette
|
||||
{
|
||||
PikaPlugIn parent_instance;
|
||||
};
|
||||
|
||||
struct _PaletteClass
|
||||
{
|
||||
PikaPlugInClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
#define PALETTE_TYPE (palette_get_type ())
|
||||
#define PALETTE (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PALETTE_TYPE, Palette))
|
||||
|
||||
GType palette_get_type (void) G_GNUC_CONST;
|
||||
|
||||
static GList * palette_query_procedures (PikaPlugIn *plug_in);
|
||||
static PikaProcedure * palette_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name);
|
||||
|
||||
static PikaValueArray * palette_run (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data);
|
||||
|
||||
static gboolean dialog (PikaDrawable *drawable);
|
||||
|
||||
static PikaImage * smooth_palette (PikaDrawable *drawable,
|
||||
PikaLayer **layer);
|
||||
|
||||
|
||||
G_DEFINE_TYPE (Palette, palette, PIKA_TYPE_PLUG_IN)
|
||||
|
||||
PIKA_MAIN (PALETTE_TYPE)
|
||||
DEFINE_STD_SET_I18N
|
||||
|
||||
|
||||
static struct
|
||||
{
|
||||
gint width;
|
||||
gint height;
|
||||
gint ntries;
|
||||
gint try_size;
|
||||
gboolean show_image;
|
||||
} config =
|
||||
{
|
||||
256,
|
||||
64,
|
||||
50,
|
||||
10000,
|
||||
TRUE
|
||||
};
|
||||
|
||||
|
||||
static void
|
||||
palette_class_init (PaletteClass *klass)
|
||||
{
|
||||
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
|
||||
|
||||
plug_in_class->query_procedures = palette_query_procedures;
|
||||
plug_in_class->create_procedure = palette_create_procedure;
|
||||
plug_in_class->set_i18n = STD_SET_I18N;
|
||||
}
|
||||
|
||||
static void
|
||||
palette_init (Palette *palette)
|
||||
{
|
||||
}
|
||||
|
||||
static GList *
|
||||
palette_query_procedures (PikaPlugIn *plug_in)
|
||||
{
|
||||
return g_list_append (NULL, g_strdup (PLUG_IN_PROC));
|
||||
}
|
||||
|
||||
static PikaProcedure *
|
||||
palette_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name)
|
||||
{
|
||||
PikaProcedure *procedure = NULL;
|
||||
|
||||
if (! strcmp (name, PLUG_IN_PROC))
|
||||
{
|
||||
procedure = pika_image_procedure_new (plug_in, name,
|
||||
PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
palette_run, NULL, NULL);
|
||||
|
||||
pika_procedure_set_image_types (procedure, "RGB*");
|
||||
pika_procedure_set_sensitivity_mask (procedure,
|
||||
PIKA_PROCEDURE_SENSITIVE_DRAWABLE);
|
||||
|
||||
pika_procedure_set_menu_label (procedure, _("Smoo_th Palette..."));
|
||||
pika_procedure_add_menu_path (procedure, "<Image>/Colors/Info");
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
_("Derive a smooth color palette "
|
||||
"from the image"),
|
||||
"help!",
|
||||
name);
|
||||
pika_procedure_set_attribution (procedure,
|
||||
"Scott Draves",
|
||||
"Scott Draves",
|
||||
"1997");
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "width",
|
||||
"Widtg",
|
||||
"Widtg",
|
||||
2, PIKA_MAX_IMAGE_SIZE, 256,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "height",
|
||||
"Height",
|
||||
"Height",
|
||||
2, PIKA_MAX_IMAGE_SIZE, 64,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "n-tries",
|
||||
"N tries",
|
||||
"Search septh",
|
||||
1, 1024, 50,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_BOOLEAN (procedure, "show-image",
|
||||
"Show image",
|
||||
"Show image",
|
||||
TRUE,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_VAL_IMAGE (procedure, "new-image",
|
||||
"New image",
|
||||
"Output image",
|
||||
FALSE,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_VAL_LAYER (procedure, "new-layer",
|
||||
"New layer",
|
||||
"Output layer",
|
||||
FALSE,
|
||||
G_PARAM_READWRITE);
|
||||
}
|
||||
|
||||
return procedure;
|
||||
}
|
||||
|
||||
static PikaValueArray *
|
||||
palette_run (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data)
|
||||
{
|
||||
PikaValueArray *return_vals;
|
||||
PikaImage *new_image;
|
||||
PikaLayer *new_layer;
|
||||
PikaDrawable *drawable;
|
||||
|
||||
gegl_init (NULL, NULL);
|
||||
|
||||
if (n_drawables != 1)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
g_set_error (&error, PIKA_PLUG_IN_ERROR, 0,
|
||||
_("Procedure '%s' only works with one drawable."),
|
||||
PLUG_IN_PROC);
|
||||
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_CALLING_ERROR,
|
||||
error);
|
||||
}
|
||||
else
|
||||
{
|
||||
drawable = drawables[0];
|
||||
}
|
||||
|
||||
switch (run_mode)
|
||||
{
|
||||
case PIKA_RUN_INTERACTIVE:
|
||||
pika_get_data (PLUG_IN_PROC, &config);
|
||||
|
||||
if (! dialog (drawable))
|
||||
{
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_CANCEL,
|
||||
NULL);
|
||||
}
|
||||
break;
|
||||
|
||||
case PIKA_RUN_NONINTERACTIVE:
|
||||
config.width = PIKA_VALUES_GET_INT (args, 0);
|
||||
config.height = PIKA_VALUES_GET_INT (args, 1);
|
||||
config.ntries = PIKA_VALUES_GET_INT (args, 2);
|
||||
config.show_image = PIKA_VALUES_GET_BOOLEAN (args, 3);
|
||||
break;
|
||||
|
||||
case PIKA_RUN_WITH_LAST_VALS:
|
||||
pika_get_data (PLUG_IN_PROC, &config);
|
||||
break;
|
||||
}
|
||||
|
||||
if (pika_drawable_is_rgb (drawable))
|
||||
{
|
||||
pika_progress_init (_("Deriving smooth palette"));
|
||||
|
||||
new_image = smooth_palette (drawable, &new_layer);
|
||||
|
||||
if (run_mode == PIKA_RUN_INTERACTIVE)
|
||||
pika_set_data (PLUG_IN_PROC, &config, sizeof (config));
|
||||
|
||||
if (config.show_image)
|
||||
pika_display_new (new_image);
|
||||
}
|
||||
else
|
||||
{
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_EXECUTION_ERROR,
|
||||
NULL);
|
||||
}
|
||||
|
||||
return_vals = pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_SUCCESS,
|
||||
NULL);
|
||||
|
||||
PIKA_VALUES_SET_IMAGE (return_vals, 1, new_image);
|
||||
PIKA_VALUES_SET_LAYER (return_vals, 2, new_layer);
|
||||
|
||||
return return_vals;
|
||||
}
|
||||
|
||||
static gfloat
|
||||
pix_diff (gfloat *pal,
|
||||
guint bpp,
|
||||
gint i,
|
||||
gint j)
|
||||
{
|
||||
gfloat r = 0.f;
|
||||
guint k;
|
||||
|
||||
for (k = 0; k < bpp; k++)
|
||||
{
|
||||
gfloat p1 = pal[j * bpp + k];
|
||||
gfloat p2 = pal[i * bpp + k];
|
||||
r += (p1 - p2) * (p1 - p2);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void
|
||||
pix_swap (gfloat *pal,
|
||||
guint bpp,
|
||||
gint i,
|
||||
gint j)
|
||||
{
|
||||
guint k;
|
||||
|
||||
for (k = 0; k < bpp; k++)
|
||||
{
|
||||
gfloat t = pal[j * bpp + k];
|
||||
pal[j * bpp + k] = pal[i * bpp + k];
|
||||
pal[i * bpp + k] = t;
|
||||
}
|
||||
}
|
||||
|
||||
static PikaImage *
|
||||
smooth_palette (PikaDrawable *drawable,
|
||||
PikaLayer **layer)
|
||||
{
|
||||
PikaImage *new_image;
|
||||
gint psize, i, j;
|
||||
guint bpp;
|
||||
gint sel_x1, sel_y1;
|
||||
gint width, height;
|
||||
GeglBuffer *buffer;
|
||||
GeglSampler *sampler;
|
||||
gfloat *pal;
|
||||
GRand *gr;
|
||||
|
||||
const Babl *format = babl_format ("RGB float");
|
||||
|
||||
new_image = pika_image_new_with_precision (config.width,
|
||||
config.height,
|
||||
PIKA_RGB,
|
||||
PIKA_PRECISION_FLOAT_LINEAR);
|
||||
|
||||
pika_image_undo_disable (new_image);
|
||||
|
||||
*layer = pika_layer_new (new_image, _("Background"),
|
||||
config.width, config.height,
|
||||
pika_drawable_type (drawable),
|
||||
100,
|
||||
pika_image_get_default_new_layer_mode (new_image));
|
||||
|
||||
pika_image_insert_layer (new_image, *layer, NULL, 0);
|
||||
|
||||
if (! pika_drawable_mask_intersect (drawable,
|
||||
&sel_x1, &sel_y1, &width, &height))
|
||||
return new_image;
|
||||
|
||||
gr = g_rand_new ();
|
||||
|
||||
psize = config.width;
|
||||
|
||||
buffer = pika_drawable_get_buffer (drawable);
|
||||
|
||||
sampler = gegl_buffer_sampler_new (buffer, format, GEGL_SAMPLER_NEAREST);
|
||||
|
||||
bpp = babl_format_get_n_components (gegl_buffer_get_format (buffer));
|
||||
|
||||
pal = g_new (gfloat, psize * bpp);
|
||||
|
||||
/* get initial palette */
|
||||
|
||||
for (i = 0; i < psize; i++)
|
||||
{
|
||||
gint x = sel_x1 + g_rand_int_range (gr, 0, width);
|
||||
gint y = sel_y1 + g_rand_int_range (gr, 0, height);
|
||||
|
||||
gegl_sampler_get (sampler,
|
||||
(gdouble) x, (gdouble) y, NULL, pal + i * bpp,
|
||||
GEGL_ABYSS_NONE);
|
||||
}
|
||||
|
||||
g_object_unref (sampler);
|
||||
g_object_unref (buffer);
|
||||
|
||||
/* reorder */
|
||||
if (1)
|
||||
{
|
||||
gfloat *pal_best;
|
||||
gfloat *original;
|
||||
gdouble len_best = 0;
|
||||
gint try;
|
||||
|
||||
pal_best = g_memdup2 (pal, bpp * psize);
|
||||
original = g_memdup2 (pal, bpp * psize);
|
||||
|
||||
for (try = 0; try < config.ntries; try++)
|
||||
{
|
||||
gdouble len;
|
||||
|
||||
if (!(try%5))
|
||||
pika_progress_update (try / (double) config.ntries);
|
||||
memcpy (pal, original, bpp * psize);
|
||||
|
||||
/* scramble */
|
||||
for (i = 1; i < psize; i++)
|
||||
pix_swap (pal, bpp, i, g_rand_int_range (gr, 0, psize));
|
||||
|
||||
/* measure */
|
||||
len = 0.0;
|
||||
for (i = 1; i < psize; i++)
|
||||
len += pix_diff (pal, bpp, i, i-1);
|
||||
|
||||
/* improve */
|
||||
for (i = 0; i < config.try_size; i++)
|
||||
{
|
||||
gint i0 = 1 + g_rand_int_range (gr, 0, psize-2);
|
||||
gint i1 = 1 + g_rand_int_range (gr, 0, psize-2);
|
||||
gfloat as_is, swapd;
|
||||
|
||||
if (1 == (i0 - i1))
|
||||
{
|
||||
as_is = (pix_diff (pal, bpp, i1 - 1, i1) +
|
||||
pix_diff (pal, bpp, i0, i0 + 1));
|
||||
swapd = (pix_diff (pal, bpp, i1 - 1, i0) +
|
||||
pix_diff (pal, bpp, i1, i0 + 1));
|
||||
}
|
||||
else if (1 == (i1 - i0))
|
||||
{
|
||||
as_is = (pix_diff (pal, bpp, i0 - 1, i0) +
|
||||
pix_diff (pal, bpp, i1, i1 + 1));
|
||||
swapd = (pix_diff (pal, bpp, i0 - 1, i1) +
|
||||
pix_diff (pal, bpp, i0, i1 + 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
as_is = (pix_diff (pal, bpp, i0, i0 + 1) +
|
||||
pix_diff (pal, bpp, i0, i0 - 1) +
|
||||
pix_diff (pal, bpp, i1, i1 + 1) +
|
||||
pix_diff (pal, bpp, i1, i1 - 1));
|
||||
swapd = (pix_diff (pal, bpp, i1, i0 + 1) +
|
||||
pix_diff (pal, bpp, i1, i0 - 1) +
|
||||
pix_diff (pal, bpp, i0, i1 + 1) +
|
||||
pix_diff (pal, bpp, i0, i1 - 1));
|
||||
}
|
||||
if (swapd < as_is)
|
||||
{
|
||||
pix_swap (pal, bpp, i0, i1);
|
||||
len += swapd - as_is;
|
||||
}
|
||||
}
|
||||
/* best? */
|
||||
if (0 == try || len < len_best)
|
||||
{
|
||||
memcpy (pal_best, pal, bpp * psize);
|
||||
len_best = len;
|
||||
}
|
||||
}
|
||||
|
||||
pika_progress_update (1.0);
|
||||
memcpy (pal, pal_best, bpp * psize);
|
||||
g_free (pal_best);
|
||||
g_free (original);
|
||||
|
||||
/* clean */
|
||||
for (i = 1; i < 4 * psize; i++)
|
||||
{
|
||||
gfloat as_is, swapd;
|
||||
gint i0 = 1 + g_rand_int_range (gr, 0, psize - 2);
|
||||
gint i1 = i0 + 1;
|
||||
|
||||
as_is = (pix_diff (pal, bpp, i0 - 1, i0) +
|
||||
pix_diff (pal, bpp, i1, i1 + 1));
|
||||
swapd = (pix_diff (pal, bpp, i0 - 1, i1) +
|
||||
pix_diff (pal, bpp, i0, i1 + 1));
|
||||
|
||||
if (swapd < as_is)
|
||||
{
|
||||
pix_swap (pal, bpp, i0, i1);
|
||||
len_best += swapd - as_is;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* store smooth palette */
|
||||
|
||||
buffer = pika_drawable_get_buffer (PIKA_DRAWABLE (*layer));
|
||||
|
||||
for (j = 0; j < config.height; j++)
|
||||
{
|
||||
GeglRectangle row = {0, j, config.width, 1};
|
||||
gegl_buffer_set (buffer, &row, 0, format, pal, GEGL_AUTO_ROWSTRIDE);
|
||||
}
|
||||
|
||||
gegl_buffer_flush (buffer);
|
||||
|
||||
pika_drawable_update (PIKA_DRAWABLE (*layer), 0, 0,
|
||||
config.width, config.height);
|
||||
pika_image_undo_enable (new_image);
|
||||
|
||||
g_object_unref (buffer);
|
||||
g_free (pal);
|
||||
g_rand_free (gr);
|
||||
|
||||
return new_image;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
dialog (PikaDrawable *drawable)
|
||||
{
|
||||
GtkWidget *dlg;
|
||||
GtkWidget *spinbutton;
|
||||
GtkAdjustment *adj;
|
||||
GtkWidget *sizeentry;
|
||||
PikaImage *image;
|
||||
PikaUnit unit;
|
||||
gdouble xres, yres;
|
||||
gboolean run;
|
||||
|
||||
pika_ui_init (PLUG_IN_BINARY);
|
||||
|
||||
dlg = pika_dialog_new (_("Smooth Palette"), PLUG_IN_ROLE,
|
||||
NULL, 0,
|
||||
pika_standard_help_func, PLUG_IN_PROC,
|
||||
|
||||
_("_Cancel"), GTK_RESPONSE_CANCEL,
|
||||
_("_OK"), GTK_RESPONSE_OK,
|
||||
|
||||
NULL);
|
||||
|
||||
pika_dialog_set_alternative_button_order (GTK_DIALOG (dlg),
|
||||
GTK_RESPONSE_OK,
|
||||
GTK_RESPONSE_CANCEL,
|
||||
-1);
|
||||
|
||||
pika_window_set_transient (GTK_WINDOW (dlg));
|
||||
|
||||
image = pika_item_get_image (PIKA_ITEM (drawable));
|
||||
unit = pika_image_get_unit (image);
|
||||
pika_image_get_resolution (image, &xres, &yres);
|
||||
|
||||
sizeentry = pika_coordinates_new (unit, "%a", TRUE, FALSE, 6,
|
||||
PIKA_SIZE_ENTRY_UPDATE_SIZE,
|
||||
FALSE, FALSE,
|
||||
|
||||
_("_Width:"),
|
||||
config.width, xres,
|
||||
2, PIKA_MAX_IMAGE_SIZE,
|
||||
2, PIKA_MAX_IMAGE_SIZE,
|
||||
|
||||
_("_Height:"),
|
||||
config.height, yres,
|
||||
1, PIKA_MAX_IMAGE_SIZE,
|
||||
1, PIKA_MAX_IMAGE_SIZE);
|
||||
gtk_container_set_border_width (GTK_CONTAINER (sizeentry), 12);
|
||||
gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))),
|
||||
sizeentry, FALSE, FALSE, 0);
|
||||
gtk_widget_show (sizeentry);
|
||||
|
||||
adj = gtk_adjustment_new (config.ntries, 1, 1024, 1, 10, 0);
|
||||
spinbutton = pika_spin_button_new (adj, 1, 0);
|
||||
gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
|
||||
|
||||
pika_grid_attach_aligned (GTK_GRID (sizeentry), 0, 2,
|
||||
_("_Search depth:"), 0.0, 0.5,
|
||||
spinbutton, 1);
|
||||
g_signal_connect (adj, "value-changed",
|
||||
G_CALLBACK (pika_int_adjustment_update),
|
||||
&config.ntries);
|
||||
|
||||
gtk_widget_show (dlg);
|
||||
|
||||
run = (pika_dialog_run (PIKA_DIALOG (dlg)) == GTK_RESPONSE_OK);
|
||||
|
||||
if (run)
|
||||
{
|
||||
config.width = pika_size_entry_get_refval (PIKA_SIZE_ENTRY (sizeentry),
|
||||
0);
|
||||
config.height = pika_size_entry_get_refval (PIKA_SIZE_ENTRY (sizeentry),
|
||||
1);
|
||||
}
|
||||
|
||||
gtk_widget_destroy (dlg);
|
||||
|
||||
return run;
|
||||
}
|
||||
1161
plug-ins/common/sparkle.c
Normal file
1161
plug-ins/common/sparkle.c
Normal file
File diff suppressed because it is too large
Load Diff
3236
plug-ins/common/sphere-designer.c
Normal file
3236
plug-ins/common/sphere-designer.c
Normal file
File diff suppressed because it is too large
Load Diff
1198
plug-ins/common/tile-small.c
Normal file
1198
plug-ins/common/tile-small.c
Normal file
File diff suppressed because it is too large
Load Diff
562
plug-ins/common/tile.c
Normal file
562
plug-ins/common/tile.c
Normal file
@ -0,0 +1,562 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This filter tiles an image to arbitrary width and height
|
||||
*/
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <libpika/pika.h>
|
||||
#include <libpika/pikaui.h>
|
||||
|
||||
#include "libpika/stdplugins-intl.h"
|
||||
|
||||
|
||||
#define PLUG_IN_PROC "plug-in-tile"
|
||||
#define PLUG_IN_BINARY "tile"
|
||||
#define PLUG_IN_ROLE "pika-tile"
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
gint new_width;
|
||||
gint new_height;
|
||||
gint constrain;
|
||||
gint new_image;
|
||||
} TileVals;
|
||||
|
||||
|
||||
typedef struct _Tile Tile;
|
||||
typedef struct _TileClass TileClass;
|
||||
|
||||
struct _Tile
|
||||
{
|
||||
PikaPlugIn parent_instance;
|
||||
};
|
||||
|
||||
struct _TileClass
|
||||
{
|
||||
PikaPlugInClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
#define TILE_TYPE (tile_get_type ())
|
||||
#define TILE (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TILE_TYPE, Tile))
|
||||
|
||||
GType tile_get_type (void) G_GNUC_CONST;
|
||||
|
||||
static GList * tile_query_procedures (PikaPlugIn *plug_in);
|
||||
static PikaProcedure * tile_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name);
|
||||
|
||||
static PikaValueArray * tile_run (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data);
|
||||
|
||||
static void tile (PikaImage *image,
|
||||
PikaDrawable *drawable,
|
||||
PikaImage **new_image,
|
||||
PikaLayer **new_layer);
|
||||
|
||||
static void tile_gegl (GeglBuffer *src,
|
||||
gint src_width,
|
||||
gint src_height,
|
||||
GeglBuffer *dst,
|
||||
gint dst_width,
|
||||
gint dst_height);
|
||||
|
||||
static gboolean tile_dialog (PikaImage *image,
|
||||
PikaDrawable *drawable);
|
||||
|
||||
|
||||
G_DEFINE_TYPE (Tile, tile, PIKA_TYPE_PLUG_IN)
|
||||
|
||||
PIKA_MAIN (TILE_TYPE)
|
||||
DEFINE_STD_SET_I18N
|
||||
|
||||
|
||||
static TileVals tvals =
|
||||
{
|
||||
1, /* new_width */
|
||||
1, /* new_height */
|
||||
TRUE, /* constrain */
|
||||
TRUE /* new_image */
|
||||
};
|
||||
|
||||
|
||||
static void
|
||||
tile_class_init (TileClass *klass)
|
||||
{
|
||||
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
|
||||
|
||||
plug_in_class->query_procedures = tile_query_procedures;
|
||||
plug_in_class->create_procedure = tile_create_procedure;
|
||||
plug_in_class->set_i18n = STD_SET_I18N;
|
||||
}
|
||||
|
||||
static void
|
||||
tile_init (Tile *tile)
|
||||
{
|
||||
}
|
||||
|
||||
static GList *
|
||||
tile_query_procedures (PikaPlugIn *plug_in)
|
||||
{
|
||||
return g_list_append (NULL, g_strdup (PLUG_IN_PROC));
|
||||
}
|
||||
|
||||
static PikaProcedure *
|
||||
tile_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name)
|
||||
{
|
||||
PikaProcedure *procedure = NULL;
|
||||
|
||||
if (! strcmp (name, PLUG_IN_PROC))
|
||||
{
|
||||
procedure = pika_image_procedure_new (plug_in, name,
|
||||
PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
tile_run, NULL, NULL);
|
||||
|
||||
pika_procedure_set_image_types (procedure, "*");
|
||||
pika_procedure_set_sensitivity_mask (procedure,
|
||||
PIKA_PROCEDURE_SENSITIVE_DRAWABLE);
|
||||
|
||||
pika_procedure_set_menu_label (procedure, _("_Tile..."));
|
||||
pika_procedure_add_menu_path (procedure, "<Image>/Filters/Map");
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
_("Create an array of copies "
|
||||
"of the image"),
|
||||
"This function creates a new image "
|
||||
"with a single layer sized to the "
|
||||
"specified 'new_width' and "
|
||||
"'new_height' parameters. The "
|
||||
"specified drawable is tiled into "
|
||||
"this layer. The new layer will have "
|
||||
"the same type as the specified "
|
||||
"drawable and the new image will "
|
||||
"have a corresponding base type.",
|
||||
name);
|
||||
pika_procedure_set_attribution (procedure,
|
||||
"Spencer Kimball & Peter Mattis",
|
||||
"Spencer Kimball & Peter Mattis",
|
||||
"1996-1997");
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "new-width",
|
||||
"New width",
|
||||
"New (tiled) image width",
|
||||
1, PIKA_MAX_IMAGE_SIZE, 1,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "new-height",
|
||||
"New height",
|
||||
"New (tiled) image height",
|
||||
1, PIKA_MAX_IMAGE_SIZE, 1,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_BOOLEAN (procedure, "new-image",
|
||||
"New image",
|
||||
"Create a new image",
|
||||
TRUE,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_VAL_IMAGE (procedure, "new-image",
|
||||
"New image",
|
||||
"Output image (NULL if new-image == FALSE)",
|
||||
TRUE,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_VAL_LAYER (procedure, "new-layer",
|
||||
"New layer",
|
||||
"Output layer (NULL if new-image == FALSE)",
|
||||
TRUE,
|
||||
G_PARAM_READWRITE);
|
||||
}
|
||||
|
||||
return procedure;
|
||||
}
|
||||
|
||||
static PikaValueArray *
|
||||
tile_run (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data)
|
||||
{
|
||||
PikaValueArray *return_vals;
|
||||
PikaLayer *new_layer;
|
||||
PikaImage *new_image;
|
||||
PikaDrawable *drawable;
|
||||
|
||||
gegl_init (NULL, NULL);
|
||||
|
||||
if (n_drawables != 1)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
g_set_error (&error, PIKA_PLUG_IN_ERROR, 0,
|
||||
_("Procedure '%s' only works with one drawable."),
|
||||
pika_procedure_get_name (procedure));
|
||||
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_CALLING_ERROR,
|
||||
error);
|
||||
}
|
||||
else
|
||||
{
|
||||
drawable = drawables[0];
|
||||
}
|
||||
|
||||
switch (run_mode)
|
||||
{
|
||||
case PIKA_RUN_INTERACTIVE:
|
||||
pika_get_data (PLUG_IN_PROC, &tvals);
|
||||
|
||||
if (! tile_dialog (image, drawable))
|
||||
{
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_CANCEL,
|
||||
NULL);
|
||||
}
|
||||
break;
|
||||
|
||||
case PIKA_RUN_NONINTERACTIVE:
|
||||
tvals.new_width = PIKA_VALUES_GET_INT (args, 0);
|
||||
tvals.new_height = PIKA_VALUES_GET_INT (args, 1);
|
||||
tvals.new_image = PIKA_VALUES_GET_BOOLEAN (args, 2);
|
||||
break;
|
||||
|
||||
case PIKA_RUN_WITH_LAST_VALS:
|
||||
pika_get_data (PLUG_IN_PROC, &tvals);
|
||||
break;
|
||||
}
|
||||
|
||||
pika_progress_init (_("Tiling"));
|
||||
|
||||
tile (image,
|
||||
drawable,
|
||||
&new_image,
|
||||
&new_layer);
|
||||
|
||||
if (run_mode == PIKA_RUN_INTERACTIVE)
|
||||
pika_set_data (PLUG_IN_PROC, &tvals, sizeof (TileVals));
|
||||
|
||||
if (run_mode != PIKA_RUN_NONINTERACTIVE)
|
||||
{
|
||||
if (tvals.new_image)
|
||||
pika_display_new (new_image);
|
||||
else
|
||||
pika_displays_flush ();
|
||||
}
|
||||
|
||||
return_vals = pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_SUCCESS,
|
||||
NULL);
|
||||
|
||||
PIKA_VALUES_SET_IMAGE (return_vals, 1, new_image);
|
||||
PIKA_VALUES_SET_LAYER (return_vals, 2, new_layer);
|
||||
|
||||
return return_vals;
|
||||
}
|
||||
|
||||
static void
|
||||
tile_gegl (GeglBuffer *src,
|
||||
gint src_width,
|
||||
gint src_height,
|
||||
GeglBuffer *dst,
|
||||
gint dst_width,
|
||||
gint dst_height)
|
||||
{
|
||||
GeglNode *node;
|
||||
GeglNode *buffer_src_node;
|
||||
GeglNode *tile_node;
|
||||
GeglNode *crop_src_node;
|
||||
GeglNode *crop_dst_node;
|
||||
GeglNode *buffer_dst_node;
|
||||
|
||||
GeglProcessor *processor;
|
||||
gdouble progress;
|
||||
|
||||
node = gegl_node_new ();
|
||||
|
||||
buffer_src_node = gegl_node_new_child (node,
|
||||
"operation", "gegl:buffer-source",
|
||||
"buffer", src,
|
||||
NULL);
|
||||
|
||||
crop_src_node = gegl_node_new_child (node,
|
||||
"operation", "gegl:crop",
|
||||
"width", (gdouble) src_width,
|
||||
"height", (gdouble) src_height,
|
||||
NULL);
|
||||
|
||||
tile_node = gegl_node_new_child (node,
|
||||
"operation", "gegl:tile",
|
||||
NULL);
|
||||
|
||||
crop_dst_node = gegl_node_new_child (node,
|
||||
"operation", "gegl:crop",
|
||||
"width", (gdouble) dst_width,
|
||||
"height", (gdouble) dst_height,
|
||||
NULL);
|
||||
|
||||
buffer_dst_node = gegl_node_new_child (node,
|
||||
"operation", "gegl:write-buffer",
|
||||
"buffer", dst,
|
||||
NULL);
|
||||
|
||||
gegl_node_link_many (buffer_src_node,
|
||||
crop_src_node,
|
||||
tile_node,
|
||||
crop_dst_node,
|
||||
buffer_dst_node,
|
||||
NULL);
|
||||
|
||||
processor = gegl_node_new_processor (buffer_dst_node, NULL);
|
||||
|
||||
while (gegl_processor_work (processor, &progress))
|
||||
if (!((gint) (progress * 100.0) % 10))
|
||||
pika_progress_update (progress);
|
||||
|
||||
pika_progress_update (1.0);
|
||||
|
||||
g_object_unref (processor);
|
||||
g_object_unref (node);
|
||||
}
|
||||
|
||||
static void
|
||||
tile (PikaImage *image,
|
||||
PikaDrawable *drawable,
|
||||
PikaImage **new_image,
|
||||
PikaLayer **new_layer)
|
||||
{
|
||||
PikaDrawable *dst_drawable;
|
||||
GeglBuffer *dst_buffer;
|
||||
GeglBuffer *src_buffer;
|
||||
gint dst_width = tvals.new_width;
|
||||
gint dst_height = tvals.new_height;
|
||||
gint src_width = pika_drawable_get_width (drawable);
|
||||
gint src_height = pika_drawable_get_height (drawable);
|
||||
|
||||
PikaImageBaseType image_type = PIKA_RGB;
|
||||
|
||||
if (tvals.new_image)
|
||||
{
|
||||
/* create a new image */
|
||||
gint32 precision = pika_image_get_precision (image);
|
||||
|
||||
switch (pika_drawable_type (drawable))
|
||||
{
|
||||
case PIKA_RGB_IMAGE:
|
||||
case PIKA_RGBA_IMAGE:
|
||||
image_type = PIKA_RGB;
|
||||
break;
|
||||
|
||||
case PIKA_GRAY_IMAGE:
|
||||
case PIKA_GRAYA_IMAGE:
|
||||
image_type = PIKA_GRAY;
|
||||
break;
|
||||
|
||||
case PIKA_INDEXED_IMAGE:
|
||||
case PIKA_INDEXEDA_IMAGE:
|
||||
image_type = PIKA_INDEXED;
|
||||
break;
|
||||
}
|
||||
|
||||
*new_image = pika_image_new_with_precision (dst_width,
|
||||
dst_height,
|
||||
image_type,
|
||||
precision);
|
||||
pika_image_undo_disable (*new_image);
|
||||
|
||||
/* copy the colormap, if necessary */
|
||||
if (image_type == PIKA_INDEXED)
|
||||
{
|
||||
guchar *cmap;
|
||||
gint ncols;
|
||||
|
||||
cmap = pika_image_get_colormap (image, NULL, &ncols);
|
||||
pika_image_set_colormap (*new_image, cmap, ncols);
|
||||
g_free (cmap);
|
||||
}
|
||||
|
||||
*new_layer = pika_layer_new (*new_image, _("Background"),
|
||||
dst_width, dst_height,
|
||||
pika_drawable_type (drawable),
|
||||
100,
|
||||
pika_image_get_default_new_layer_mode (*new_image));
|
||||
|
||||
if (*new_layer == NULL)
|
||||
return;
|
||||
|
||||
pika_image_insert_layer (*new_image, *new_layer, NULL, 0);
|
||||
dst_drawable = PIKA_DRAWABLE (*new_layer);
|
||||
}
|
||||
else
|
||||
{
|
||||
*new_image = NULL;
|
||||
*new_layer = NULL;
|
||||
|
||||
pika_image_undo_group_start (image);
|
||||
pika_image_resize (image, dst_width, dst_height, 0, 0);
|
||||
|
||||
if (pika_item_is_layer (PIKA_ITEM (drawable)))
|
||||
{
|
||||
pika_layer_resize (PIKA_LAYER (drawable), dst_width, dst_height, 0, 0);
|
||||
}
|
||||
else if (pika_item_is_layer_mask (PIKA_ITEM (drawable)))
|
||||
{
|
||||
PikaLayer *layer = pika_layer_from_mask (PIKA_LAYER_MASK (drawable));
|
||||
|
||||
pika_layer_resize (layer, dst_width, dst_height, 0, 0);
|
||||
}
|
||||
|
||||
dst_drawable = drawable;
|
||||
}
|
||||
|
||||
src_buffer = pika_drawable_get_buffer (drawable);
|
||||
dst_buffer = pika_drawable_get_buffer (dst_drawable);
|
||||
|
||||
tile_gegl (src_buffer, src_width, src_height,
|
||||
dst_buffer, dst_width, dst_height);
|
||||
|
||||
gegl_buffer_flush (dst_buffer);
|
||||
pika_drawable_update (dst_drawable, 0, 0, dst_width, dst_height);
|
||||
|
||||
if (tvals.new_image)
|
||||
{
|
||||
pika_image_undo_enable (*new_image);
|
||||
}
|
||||
else
|
||||
{
|
||||
pika_image_undo_group_end (image);
|
||||
}
|
||||
|
||||
g_object_unref (src_buffer);
|
||||
g_object_unref (dst_buffer);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
tile_dialog (PikaImage *image,
|
||||
PikaDrawable *drawable)
|
||||
{
|
||||
GtkWidget *dlg;
|
||||
GtkWidget *vbox;
|
||||
GtkWidget *frame;
|
||||
GtkWidget *sizeentry;
|
||||
GtkWidget *chainbutton;
|
||||
GtkWidget *toggle;
|
||||
gint width;
|
||||
gint height;
|
||||
gdouble xres;
|
||||
gdouble yres;
|
||||
PikaUnit unit;
|
||||
gboolean run;
|
||||
|
||||
pika_ui_init (PLUG_IN_BINARY);
|
||||
|
||||
width = pika_drawable_get_width (drawable);
|
||||
height = pika_drawable_get_height (drawable);
|
||||
unit = pika_image_get_unit (image);
|
||||
pika_image_get_resolution (image, &xres, &yres);
|
||||
|
||||
tvals.new_width = width;
|
||||
tvals.new_height = height;
|
||||
|
||||
dlg = pika_dialog_new (_("Tile"), PLUG_IN_ROLE,
|
||||
NULL, 0,
|
||||
pika_standard_help_func, PLUG_IN_PROC,
|
||||
|
||||
_("_Cancel"), GTK_RESPONSE_CANCEL,
|
||||
_("_OK"), GTK_RESPONSE_OK,
|
||||
|
||||
NULL);
|
||||
|
||||
pika_dialog_set_alternative_button_order (GTK_DIALOG (dlg),
|
||||
GTK_RESPONSE_OK,
|
||||
GTK_RESPONSE_CANCEL,
|
||||
-1);
|
||||
|
||||
pika_window_set_transient (GTK_WINDOW (dlg));
|
||||
|
||||
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
|
||||
gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
|
||||
gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))),
|
||||
vbox, TRUE, TRUE, 0);
|
||||
gtk_widget_show (vbox);
|
||||
|
||||
frame = pika_frame_new (_("Tile to New Size"));
|
||||
gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
|
||||
gtk_widget_show (frame);
|
||||
|
||||
sizeentry = pika_coordinates_new (unit, "%a", TRUE, TRUE, 8,
|
||||
PIKA_SIZE_ENTRY_UPDATE_SIZE,
|
||||
|
||||
tvals.constrain, TRUE,
|
||||
|
||||
_("_Width:"), width, xres,
|
||||
1, PIKA_MAX_IMAGE_SIZE,
|
||||
0, width,
|
||||
|
||||
_("_Height:"), height, yres,
|
||||
1, PIKA_MAX_IMAGE_SIZE,
|
||||
0, height);
|
||||
gtk_container_add (GTK_CONTAINER (frame), sizeentry);
|
||||
gtk_widget_show (sizeentry);
|
||||
|
||||
chainbutton = GTK_WIDGET (PIKA_COORDINATES_CHAINBUTTON (sizeentry));
|
||||
|
||||
toggle = gtk_check_button_new_with_mnemonic (_("C_reate new image"));
|
||||
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), tvals.new_image);
|
||||
gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
|
||||
gtk_widget_show (toggle);
|
||||
|
||||
g_signal_connect (toggle, "toggled",
|
||||
G_CALLBACK (pika_toggle_button_update),
|
||||
&tvals.new_image);
|
||||
|
||||
gtk_widget_show (dlg);
|
||||
|
||||
run = (pika_dialog_run (PIKA_DIALOG (dlg)) == GTK_RESPONSE_OK);
|
||||
|
||||
if (run)
|
||||
{
|
||||
tvals.new_width =
|
||||
RINT (pika_size_entry_get_refval (PIKA_SIZE_ENTRY (sizeentry), 0));
|
||||
tvals.new_height =
|
||||
RINT (pika_size_entry_get_refval (PIKA_SIZE_ENTRY (sizeentry), 1));
|
||||
|
||||
tvals.constrain =
|
||||
pika_chain_button_get_active (PIKA_CHAIN_BUTTON (chainbutton));
|
||||
}
|
||||
|
||||
gtk_widget_destroy (dlg);
|
||||
|
||||
return run;
|
||||
}
|
||||
776
plug-ins/common/unit-editor.c
Normal file
776
plug-ins/common/unit-editor.c
Normal file
@ -0,0 +1,776 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* This is a plug-in for PIKA.
|
||||
*
|
||||
* Copyright (C) 2000 Michael Natterer <mitch@gimp.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <libpika/pika.h>
|
||||
#include <libpika/pikaui.h>
|
||||
|
||||
#include "libpika/stdplugins-intl.h"
|
||||
|
||||
|
||||
#define PLUG_IN_PROC "plug-in-unit-editor"
|
||||
#define PLUG_IN_BINARY "unit-editor"
|
||||
#define PLUG_IN_ROLE "pika-unit-editor"
|
||||
|
||||
enum
|
||||
{
|
||||
SAVE,
|
||||
IDENTIFIER,
|
||||
FACTOR,
|
||||
DIGITS,
|
||||
SYMBOL,
|
||||
ABBREVIATION,
|
||||
SINGULAR,
|
||||
PLURAL,
|
||||
UNIT,
|
||||
USER_UNIT,
|
||||
BG_COLOR,
|
||||
NUM_COLUMNS
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const gchar *title;
|
||||
const gchar *help;
|
||||
|
||||
} UnitColumn;
|
||||
|
||||
|
||||
#define PIKA_UNIT_EDITOR_TYPE (pika_unit_editor_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (PikaUnitEditor, pika_unit_editor, PIKA, UNIT_EDITOR, PikaPlugIn)
|
||||
|
||||
struct _PikaUnitEditor
|
||||
{
|
||||
PikaPlugIn parent_instance;
|
||||
|
||||
GtkApplication *app;
|
||||
|
||||
GtkWindow *window;
|
||||
GtkWidget *tv;
|
||||
};
|
||||
|
||||
|
||||
static GList * editor_query_procedures (PikaPlugIn *plug_in);
|
||||
static PikaProcedure * editor_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name);
|
||||
|
||||
static PikaValueArray * editor_run (PikaProcedure *procedure,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data);
|
||||
|
||||
static PikaUnit new_unit_dialog (GtkWindow *main_window,
|
||||
PikaUnit template);
|
||||
static void on_app_activate (GApplication *gapp,
|
||||
gpointer user_data);
|
||||
|
||||
static gboolean unit_editor_key_press_event (GtkWidget *window,
|
||||
GdkEventKey *event,
|
||||
gpointer user_data);
|
||||
static void unit_editor_help_clicked (GtkWidget *window);
|
||||
|
||||
static void new_unit_action (GSimpleAction *action,
|
||||
GVariant *param,
|
||||
gpointer user_data);
|
||||
static void duplicate_unit_action (GSimpleAction *action,
|
||||
GVariant *param,
|
||||
gpointer user_data);
|
||||
static void refresh_action (GSimpleAction *action,
|
||||
GVariant *param,
|
||||
gpointer user_data);
|
||||
static void saved_toggled_callback (GtkCellRendererToggle *celltoggle,
|
||||
gchar *path_string,
|
||||
GtkListStore *list_store);
|
||||
static void unit_list_init (GtkTreeView *tv);
|
||||
|
||||
|
||||
G_DEFINE_TYPE (PikaUnitEditor, pika_unit_editor, PIKA_TYPE_PLUG_IN)
|
||||
|
||||
PIKA_MAIN (PIKA_UNIT_EDITOR_TYPE)
|
||||
DEFINE_STD_SET_I18N
|
||||
|
||||
|
||||
static const UnitColumn columns[] =
|
||||
{
|
||||
{ N_("Saved"), N_("A unit definition will only be saved before "
|
||||
"PIKA exits if this column is checked.") },
|
||||
{ N_("ID"), N_("This string will be used to identify a "
|
||||
"unit in PIKA's configuration files.") },
|
||||
{ N_("Factor"), N_("How many units make up an inch.") },
|
||||
{ N_("Digits"), N_("This field is a hint for numerical input "
|
||||
"fields. It specifies how many decimal digits "
|
||||
"the input field should provide to get "
|
||||
"approximately the same accuracy as an "
|
||||
"\"inch\" input field with two decimal digits.") },
|
||||
{ N_("Symbol"), N_("The unit's symbol if it has one (e.g. \" "
|
||||
"for inches). The unit's abbreviation is used "
|
||||
"if doesn't have a symbol.") },
|
||||
{ N_("Abbreviation"), N_("The unit's abbreviation (e.g. \"cm\" for "
|
||||
"centimeters).") },
|
||||
{ N_("Singular"), N_("The unit's singular form.") },
|
||||
{ N_("Plural"), N_("The unit's plural form.") }
|
||||
};
|
||||
|
||||
static GActionEntry ACTIONS[] =
|
||||
{
|
||||
{ "new-unit", new_unit_action },
|
||||
{ "duplicate-unit", duplicate_unit_action },
|
||||
{ "refresh", refresh_action },
|
||||
};
|
||||
|
||||
|
||||
static void
|
||||
pika_unit_editor_class_init (PikaUnitEditorClass *klass)
|
||||
{
|
||||
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
|
||||
|
||||
plug_in_class->query_procedures = editor_query_procedures;
|
||||
plug_in_class->create_procedure = editor_create_procedure;
|
||||
plug_in_class->set_i18n = STD_SET_I18N;
|
||||
}
|
||||
|
||||
static void
|
||||
pika_unit_editor_init (PikaUnitEditor *self)
|
||||
{
|
||||
}
|
||||
|
||||
static GList *
|
||||
editor_query_procedures (PikaPlugIn *plug_in)
|
||||
{
|
||||
return g_list_append (NULL, g_strdup (PLUG_IN_PROC));
|
||||
}
|
||||
|
||||
static PikaProcedure *
|
||||
editor_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name)
|
||||
{
|
||||
PikaProcedure *procedure = NULL;
|
||||
|
||||
if (! strcmp (name, PLUG_IN_PROC))
|
||||
{
|
||||
procedure = pika_procedure_new (plug_in, name,
|
||||
PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
editor_run, plug_in, NULL);
|
||||
|
||||
pika_procedure_set_menu_label (procedure, _("U_nits"));
|
||||
pika_procedure_set_icon_name (procedure, PIKA_ICON_TOOL_MEASURE);
|
||||
pika_procedure_add_menu_path (procedure, "<Image>/Edit/[Preferences]");
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
_("Create or alter units used in PIKA"),
|
||||
"The PIKA unit editor",
|
||||
name);
|
||||
pika_procedure_set_attribution (procedure,
|
||||
"Michael Natterer <mitch@gimp.org>",
|
||||
"Michael Natterer <mitch@gimp.org>",
|
||||
"2000");
|
||||
|
||||
PIKA_PROC_ARG_ENUM (procedure, "run-mode",
|
||||
"Run mode",
|
||||
"The run mode",
|
||||
PIKA_TYPE_RUN_MODE,
|
||||
PIKA_RUN_INTERACTIVE,
|
||||
G_PARAM_READWRITE);
|
||||
}
|
||||
|
||||
return procedure;
|
||||
}
|
||||
|
||||
static void
|
||||
on_app_activate (GApplication *gapp, gpointer user_data)
|
||||
{
|
||||
PikaUnitEditor *self = PIKA_UNIT_EDITOR (user_data);
|
||||
GtkWidget *headerbar;
|
||||
GtkWidget *vbox;
|
||||
GtkWidget *button_box;
|
||||
GtkWidget *scrolled_win;
|
||||
GtkListStore *list_store;
|
||||
GtkTreeViewColumn *col;
|
||||
GtkWidget *col_widget;
|
||||
GtkWidget *button;
|
||||
GtkCellRenderer *rend;
|
||||
|
||||
list_store = gtk_list_store_new (NUM_COLUMNS,
|
||||
G_TYPE_BOOLEAN, /* SAVE */
|
||||
G_TYPE_STRING, /* IDENTIFIER */
|
||||
G_TYPE_DOUBLE, /* FACTOR */
|
||||
G_TYPE_INT, /* DIGITS */
|
||||
G_TYPE_STRING, /* SYMBOL */
|
||||
G_TYPE_STRING, /* ABBREVIATION */
|
||||
G_TYPE_STRING, /* SINGULAR */
|
||||
G_TYPE_STRING, /* PLURAL */
|
||||
PIKA_TYPE_UNIT, /* UNIT */
|
||||
G_TYPE_BOOLEAN, /* USER_UNIT */
|
||||
GDK_TYPE_RGBA); /* BG_COLOR */
|
||||
|
||||
self->tv = gtk_tree_view_new_with_model (GTK_TREE_MODEL (list_store));
|
||||
g_object_unref (list_store);
|
||||
|
||||
self->window = GTK_WINDOW (gtk_application_window_new (self->app));
|
||||
gtk_window_set_title (self->window, _("Unit Editor"));
|
||||
gtk_window_set_role (self->window, PLUG_IN_ROLE);
|
||||
pika_help_connect (GTK_WIDGET (self->window),
|
||||
pika_standard_help_func, PLUG_IN_PROC,
|
||||
self->window, NULL);
|
||||
|
||||
/* Actions */
|
||||
g_action_map_add_action_entries (G_ACTION_MAP (self->window),
|
||||
ACTIONS, G_N_ELEMENTS (ACTIONS),
|
||||
self);
|
||||
gtk_application_set_accels_for_action (self->app, "win.new-unit",
|
||||
(const char*[]) { "<Ctrl>N", NULL });
|
||||
gtk_application_set_accels_for_action (self->app, "win.duplicate-unit",
|
||||
(const char*[]) { "<ctrl>D", NULL });
|
||||
gtk_application_set_accels_for_action (self->app, "win.refresh",
|
||||
(const char*[]) { "<ctrl>R", NULL });
|
||||
|
||||
/* Titlebar */
|
||||
headerbar = gtk_header_bar_new ();
|
||||
gtk_header_bar_set_title (GTK_HEADER_BAR (headerbar), _("Unit Editor"));
|
||||
gtk_header_bar_set_has_subtitle (GTK_HEADER_BAR (headerbar), FALSE);
|
||||
gtk_header_bar_set_show_close_button (GTK_HEADER_BAR (headerbar), TRUE);
|
||||
|
||||
button = gtk_button_new_with_mnemonic (_("_Refresh"));
|
||||
gtk_actionable_set_action_name (GTK_ACTIONABLE (button), "win.refresh");
|
||||
gtk_widget_show (button);
|
||||
gtk_header_bar_pack_start (GTK_HEADER_BAR (headerbar), button);
|
||||
|
||||
if (pika_show_help_button ())
|
||||
{
|
||||
button = gtk_button_new_with_mnemonic (_("_Help"));
|
||||
g_signal_connect_swapped (button, "clicked",
|
||||
G_CALLBACK (unit_editor_help_clicked),
|
||||
self->window);
|
||||
gtk_widget_show (button);
|
||||
gtk_header_bar_pack_start (GTK_HEADER_BAR (headerbar), button);
|
||||
}
|
||||
|
||||
button = gtk_button_new_with_mnemonic (_("_OK"));
|
||||
g_signal_connect_swapped (button, "clicked",
|
||||
G_CALLBACK (gtk_widget_destroy),
|
||||
self->window);
|
||||
gtk_widget_show (button);
|
||||
gtk_header_bar_pack_end (GTK_HEADER_BAR (headerbar), button);
|
||||
|
||||
gtk_window_set_titlebar (self->window, headerbar);
|
||||
gtk_widget_show (headerbar);
|
||||
|
||||
/* Content */
|
||||
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
|
||||
gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
|
||||
gtk_widget_show (vbox);
|
||||
gtk_container_add (GTK_CONTAINER (self->window), vbox);
|
||||
|
||||
button_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
|
||||
gtk_widget_show (button_box);
|
||||
gtk_container_add (GTK_CONTAINER (vbox), button_box);
|
||||
|
||||
button = gtk_button_new_from_icon_name (PIKA_ICON_DOCUMENT_NEW,
|
||||
GTK_ICON_SIZE_BUTTON);
|
||||
gtk_actionable_set_action_name (GTK_ACTIONABLE (button), "win.new-unit");
|
||||
gtk_widget_set_tooltip_text (button, _("Create a new unit from scratch"));
|
||||
gtk_widget_show (button);
|
||||
gtk_container_add (GTK_CONTAINER (button_box), button);
|
||||
|
||||
button = gtk_button_new_from_icon_name (PIKA_ICON_OBJECT_DUPLICATE,
|
||||
GTK_ICON_SIZE_BUTTON);
|
||||
gtk_actionable_set_action_name (GTK_ACTIONABLE (button), "win.duplicate-unit");
|
||||
gtk_widget_set_tooltip_text (button, _("Create a new unit using the currently selected unit as template"));
|
||||
gtk_widget_show (button);
|
||||
gtk_container_add (GTK_CONTAINER (button_box), button);
|
||||
|
||||
scrolled_win = gtk_scrolled_window_new (NULL, NULL);
|
||||
gtk_widget_set_size_request (scrolled_win, -1, 200);
|
||||
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_win),
|
||||
GTK_SHADOW_IN);
|
||||
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
|
||||
GTK_POLICY_NEVER,
|
||||
GTK_POLICY_ALWAYS);
|
||||
gtk_container_add (GTK_CONTAINER (vbox), scrolled_win);
|
||||
gtk_widget_show (scrolled_win);
|
||||
|
||||
gtk_widget_set_size_request (self->tv, -1, 220);
|
||||
gtk_container_add (GTK_CONTAINER (scrolled_win), self->tv);
|
||||
gtk_widget_set_vexpand (self->tv, TRUE);
|
||||
gtk_widget_show (self->tv);
|
||||
|
||||
rend = gtk_cell_renderer_toggle_new ();
|
||||
col =
|
||||
gtk_tree_view_column_new_with_attributes (gettext (columns[SAVE].title),
|
||||
rend,
|
||||
"active", SAVE,
|
||||
"activatable", USER_UNIT,
|
||||
"cell-background-rgba", BG_COLOR,
|
||||
NULL);
|
||||
|
||||
gtk_tree_view_append_column (GTK_TREE_VIEW (self->tv), col);
|
||||
|
||||
col_widget = gtk_tree_view_column_get_widget (col);
|
||||
if (col_widget)
|
||||
{
|
||||
button = gtk_widget_get_ancestor (col_widget, GTK_TYPE_BUTTON);
|
||||
|
||||
if (button)
|
||||
pika_help_set_help_data (button,
|
||||
gettext (columns[SAVE].help), NULL);
|
||||
}
|
||||
|
||||
g_signal_connect (rend, "toggled",
|
||||
G_CALLBACK (saved_toggled_callback),
|
||||
list_store);
|
||||
|
||||
for (int i = 0; i < G_N_ELEMENTS (columns); i++)
|
||||
{
|
||||
if (i == SAVE)
|
||||
continue;
|
||||
|
||||
col =
|
||||
gtk_tree_view_column_new_with_attributes (gettext (columns[i].title),
|
||||
gtk_cell_renderer_text_new (),
|
||||
"text", i,
|
||||
"cell-background-rgba", BG_COLOR,
|
||||
NULL);
|
||||
|
||||
gtk_tree_view_append_column (GTK_TREE_VIEW (self->tv), col);
|
||||
|
||||
col_widget = gtk_tree_view_column_get_widget (col);
|
||||
if (col_widget)
|
||||
{
|
||||
button = gtk_widget_get_ancestor (col_widget, GTK_TYPE_BUTTON);
|
||||
|
||||
if (button)
|
||||
pika_help_set_help_data (button, gettext (columns[i].help), NULL);
|
||||
}
|
||||
}
|
||||
|
||||
unit_list_init (GTK_TREE_VIEW (self->tv));
|
||||
|
||||
g_signal_connect (self->window, "key-press-event",
|
||||
G_CALLBACK (unit_editor_key_press_event),
|
||||
NULL);
|
||||
|
||||
gtk_widget_show (GTK_WIDGET (self->window));
|
||||
}
|
||||
|
||||
static gboolean
|
||||
unit_editor_key_press_event (GtkWidget *window,
|
||||
GdkEventKey *event,
|
||||
gpointer user_data)
|
||||
{
|
||||
if (event->state == 0 &&
|
||||
event->keyval == GDK_KEY_Escape)
|
||||
gtk_widget_destroy (GTK_WIDGET (window));
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
unit_editor_help_clicked (GtkWidget *window)
|
||||
{
|
||||
pika_standard_help_func (PLUG_IN_PROC, window);
|
||||
}
|
||||
|
||||
static PikaValueArray *
|
||||
editor_run (PikaProcedure *procedure,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data)
|
||||
{
|
||||
PikaUnitEditor *editor = PIKA_UNIT_EDITOR (run_data);
|
||||
|
||||
pika_ui_init (PLUG_IN_BINARY);
|
||||
|
||||
#if GLIB_CHECK_VERSION(2,74,0)
|
||||
editor->app = gtk_application_new (NULL, G_APPLICATION_DEFAULT_FLAGS);
|
||||
#else
|
||||
editor->app = gtk_application_new (NULL, G_APPLICATION_FLAGS_NONE);
|
||||
#endif
|
||||
g_signal_connect (editor->app, "activate", G_CALLBACK (on_app_activate), editor);
|
||||
|
||||
g_application_run (G_APPLICATION (editor->app), 0, NULL);
|
||||
|
||||
g_clear_object (&editor->app);
|
||||
|
||||
return pika_procedure_new_return_values (procedure, PIKA_PDB_SUCCESS, NULL);
|
||||
}
|
||||
|
||||
static PikaUnit
|
||||
new_unit_dialog (GtkWindow *main_window,
|
||||
PikaUnit template)
|
||||
{
|
||||
GtkWidget *dialog;
|
||||
GtkWidget *grid;
|
||||
GtkWidget *entry;
|
||||
GtkWidget *spinbutton;
|
||||
|
||||
GtkWidget *identifier_entry;
|
||||
GtkAdjustment *factor_adj;
|
||||
GtkAdjustment *digits_adj;
|
||||
GtkWidget *symbol_entry;
|
||||
GtkWidget *abbreviation_entry;
|
||||
GtkWidget *singular_entry;
|
||||
GtkWidget *plural_entry;
|
||||
|
||||
PikaUnit unit = PIKA_UNIT_PIXEL;
|
||||
|
||||
dialog = pika_dialog_new (_("Add a New Unit"), PLUG_IN_ROLE,
|
||||
GTK_WIDGET (main_window), GTK_DIALOG_MODAL,
|
||||
pika_standard_help_func, PLUG_IN_PROC,
|
||||
|
||||
_("_Cancel"), GTK_RESPONSE_CANCEL,
|
||||
_("_Add"), GTK_RESPONSE_OK,
|
||||
|
||||
NULL);
|
||||
|
||||
pika_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
|
||||
GTK_RESPONSE_OK,
|
||||
GTK_RESPONSE_CANCEL,
|
||||
-1);
|
||||
|
||||
grid = gtk_grid_new ();
|
||||
gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
|
||||
gtk_grid_set_column_spacing (GTK_GRID (grid), 6);
|
||||
gtk_container_set_border_width (GTK_CONTAINER (grid), 12);
|
||||
gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
|
||||
grid, FALSE, FALSE, 0);
|
||||
gtk_widget_show (grid);
|
||||
|
||||
entry = identifier_entry = gtk_entry_new ();
|
||||
if (template != PIKA_UNIT_PIXEL)
|
||||
{
|
||||
gtk_entry_set_text (GTK_ENTRY (entry),
|
||||
pika_unit_get_identifier (template));
|
||||
}
|
||||
pika_grid_attach_aligned (GTK_GRID (grid), 0, 0,
|
||||
_("_ID:"), 0.0, 0.5,
|
||||
entry, 1);
|
||||
|
||||
pika_help_set_help_data (entry, gettext (columns[IDENTIFIER].help), NULL);
|
||||
|
||||
factor_adj = gtk_adjustment_new ((template != PIKA_UNIT_PIXEL) ?
|
||||
pika_unit_get_factor (template) : 1.0,
|
||||
PIKA_MIN_RESOLUTION, PIKA_MAX_RESOLUTION,
|
||||
0.01, 0.1, 0.0);
|
||||
spinbutton = pika_spin_button_new (factor_adj, 0.01, 5);
|
||||
gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
|
||||
pika_grid_attach_aligned (GTK_GRID (grid), 0, 1,
|
||||
_("_Factor:"), 0.0, 0.5,
|
||||
spinbutton, 1);
|
||||
|
||||
pika_help_set_help_data (spinbutton, gettext (columns[FACTOR].help), NULL);
|
||||
|
||||
digits_adj = gtk_adjustment_new ((template != PIKA_UNIT_PIXEL) ?
|
||||
pika_unit_get_digits (template) : 2.0,
|
||||
0, 5, 1, 1, 0);
|
||||
spinbutton = pika_spin_button_new (digits_adj, 0, 0);
|
||||
gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
|
||||
pika_grid_attach_aligned (GTK_GRID (grid), 0, 2,
|
||||
_("_Digits:"), 0.0, 0.5,
|
||||
spinbutton, 1);
|
||||
|
||||
pika_help_set_help_data (spinbutton, gettext (columns[DIGITS].help), NULL);
|
||||
|
||||
entry = symbol_entry = gtk_entry_new ();
|
||||
if (template != PIKA_UNIT_PIXEL)
|
||||
{
|
||||
gtk_entry_set_text (GTK_ENTRY (entry),
|
||||
pika_unit_get_symbol (template));
|
||||
}
|
||||
pika_grid_attach_aligned (GTK_GRID (grid), 0, 3,
|
||||
_("_Symbol:"), 0.0, 0.5,
|
||||
entry, 1);
|
||||
|
||||
pika_help_set_help_data (entry, gettext (columns[SYMBOL].help), NULL);
|
||||
|
||||
entry = abbreviation_entry = gtk_entry_new ();
|
||||
if (template != PIKA_UNIT_PIXEL)
|
||||
{
|
||||
gtk_entry_set_text (GTK_ENTRY (entry),
|
||||
pika_unit_get_abbreviation (template));
|
||||
}
|
||||
pika_grid_attach_aligned (GTK_GRID (grid), 0, 4,
|
||||
_("_Abbreviation:"), 0.0, 0.5,
|
||||
entry, 1);
|
||||
|
||||
pika_help_set_help_data (entry, gettext (columns[ABBREVIATION].help), NULL);
|
||||
|
||||
entry = singular_entry = gtk_entry_new ();
|
||||
if (template != PIKA_UNIT_PIXEL)
|
||||
{
|
||||
gtk_entry_set_text (GTK_ENTRY (entry),
|
||||
pika_unit_get_singular (template));
|
||||
}
|
||||
pika_grid_attach_aligned (GTK_GRID (grid), 0, 5,
|
||||
_("Si_ngular:"), 0.0, 0.5,
|
||||
entry, 1);
|
||||
|
||||
pika_help_set_help_data (entry, gettext (columns[SINGULAR].help), NULL);
|
||||
|
||||
entry = plural_entry = gtk_entry_new ();
|
||||
if (template != PIKA_UNIT_PIXEL)
|
||||
{
|
||||
gtk_entry_set_text (GTK_ENTRY (entry),
|
||||
pika_unit_get_plural (template));
|
||||
}
|
||||
pika_grid_attach_aligned (GTK_GRID (grid), 0, 6,
|
||||
_("_Plural:"), 0.0, 0.5,
|
||||
entry, 1);
|
||||
|
||||
pika_help_set_help_data (entry, gettext (columns[PLURAL].help), NULL);
|
||||
|
||||
gtk_widget_show (dialog);
|
||||
|
||||
while (TRUE)
|
||||
{
|
||||
gchar *identifier;
|
||||
gdouble factor;
|
||||
gint digits;
|
||||
gchar *symbol;
|
||||
gchar *abbreviation;
|
||||
gchar *singular;
|
||||
gchar *plural;
|
||||
|
||||
if (pika_dialog_run (PIKA_DIALOG (dialog)) != GTK_RESPONSE_OK)
|
||||
break;
|
||||
|
||||
identifier = g_strdup (gtk_entry_get_text (GTK_ENTRY (identifier_entry)));
|
||||
factor = gtk_adjustment_get_value (factor_adj);
|
||||
digits = gtk_adjustment_get_value (digits_adj);
|
||||
symbol = g_strdup (gtk_entry_get_text (GTK_ENTRY (symbol_entry)));
|
||||
abbreviation = g_strdup (gtk_entry_get_text (GTK_ENTRY (abbreviation_entry)));
|
||||
singular = g_strdup (gtk_entry_get_text (GTK_ENTRY (singular_entry)));
|
||||
plural = g_strdup (gtk_entry_get_text (GTK_ENTRY (plural_entry)));
|
||||
|
||||
identifier = g_strstrip (identifier);
|
||||
symbol = g_strstrip (symbol);
|
||||
abbreviation = g_strstrip (abbreviation);
|
||||
singular = g_strstrip (singular);
|
||||
plural = g_strstrip (plural);
|
||||
|
||||
if (! strlen (identifier) ||
|
||||
! strlen (symbol) ||
|
||||
! strlen (abbreviation) ||
|
||||
! strlen (singular) ||
|
||||
! strlen (plural))
|
||||
{
|
||||
GtkWidget *msg = gtk_message_dialog_new (GTK_WINDOW (dialog), 0,
|
||||
GTK_MESSAGE_ERROR,
|
||||
GTK_BUTTONS_OK,
|
||||
_("Incomplete input"));
|
||||
|
||||
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (msg),
|
||||
_("Please fill in all text fields."));
|
||||
gtk_dialog_run (GTK_DIALOG (msg));
|
||||
gtk_widget_destroy (msg);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
unit = pika_unit_new (identifier,
|
||||
factor, digits,
|
||||
symbol, abbreviation, singular, plural);
|
||||
|
||||
g_free (identifier);
|
||||
g_free (symbol);
|
||||
g_free (abbreviation);
|
||||
g_free (singular);
|
||||
g_free (plural);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
gtk_widget_destroy (dialog);
|
||||
|
||||
return unit;
|
||||
}
|
||||
|
||||
static void
|
||||
new_unit_action (GSimpleAction *action,
|
||||
GVariant *param,
|
||||
gpointer user_data)
|
||||
{
|
||||
PikaUnitEditor *self = PIKA_UNIT_EDITOR (user_data);
|
||||
PikaUnit unit;
|
||||
|
||||
unit = new_unit_dialog (self->window, PIKA_UNIT_PIXEL);
|
||||
|
||||
if (unit != PIKA_UNIT_PIXEL)
|
||||
{
|
||||
GtkTreeModel *model;
|
||||
GtkTreeIter iter;
|
||||
|
||||
unit_list_init (GTK_TREE_VIEW (self->tv));
|
||||
|
||||
model = gtk_tree_view_get_model (GTK_TREE_VIEW (self->tv));
|
||||
|
||||
if (gtk_tree_model_get_iter_first (model, &iter) &&
|
||||
gtk_tree_model_iter_nth_child (model, &iter,
|
||||
NULL, unit - PIKA_UNIT_INCH))
|
||||
{
|
||||
GtkTreeSelection *selection;
|
||||
GtkAdjustment *adj;
|
||||
|
||||
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->tv));
|
||||
gtk_tree_selection_select_iter (selection, &iter);
|
||||
|
||||
adj = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (self->tv));
|
||||
gtk_adjustment_set_value (adj, gtk_adjustment_get_upper (adj));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
duplicate_unit_action (GSimpleAction *action,
|
||||
GVariant *param,
|
||||
gpointer user_data)
|
||||
{
|
||||
PikaUnitEditor *self = PIKA_UNIT_EDITOR (user_data);
|
||||
GtkTreeModel *model;
|
||||
GtkTreeSelection *sel;
|
||||
GtkTreeIter iter;
|
||||
|
||||
model = gtk_tree_view_get_model (GTK_TREE_VIEW (self->tv));
|
||||
sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->tv));
|
||||
|
||||
if (gtk_tree_selection_get_selected (sel, NULL, &iter))
|
||||
{
|
||||
PikaUnit unit;
|
||||
|
||||
gtk_tree_model_get (model, &iter,
|
||||
UNIT, &unit,
|
||||
-1);
|
||||
|
||||
unit = new_unit_dialog (self->window, unit);
|
||||
|
||||
if (unit != PIKA_UNIT_PIXEL)
|
||||
{
|
||||
GtkTreeIter iter;
|
||||
|
||||
unit_list_init (GTK_TREE_VIEW (self->tv));
|
||||
|
||||
if (gtk_tree_model_get_iter_first (model, &iter) &&
|
||||
gtk_tree_model_iter_nth_child (model, &iter,
|
||||
NULL, unit - PIKA_UNIT_INCH))
|
||||
{
|
||||
GtkAdjustment *adj;
|
||||
|
||||
gtk_tree_selection_select_iter (sel, &iter);
|
||||
|
||||
adj = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (self->tv));
|
||||
gtk_adjustment_set_value (adj, gtk_adjustment_get_upper (adj));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
refresh_action (GSimpleAction *action,
|
||||
GVariant *param,
|
||||
gpointer user_data)
|
||||
{
|
||||
PikaUnitEditor *self = PIKA_UNIT_EDITOR (user_data);
|
||||
|
||||
unit_list_init (GTK_TREE_VIEW (self->tv));
|
||||
}
|
||||
|
||||
static void
|
||||
saved_toggled_callback (GtkCellRendererToggle *celltoggle,
|
||||
gchar *path_string,
|
||||
GtkListStore *list_store)
|
||||
{
|
||||
GtkTreePath *path;
|
||||
GtkTreeIter iter;
|
||||
gboolean saved;
|
||||
PikaUnit unit;
|
||||
|
||||
path = gtk_tree_path_new_from_string (path_string);
|
||||
|
||||
if (! gtk_tree_model_get_iter (GTK_TREE_MODEL (list_store), &iter, path))
|
||||
{
|
||||
g_warning ("%s: bad tree path?", G_STRLOC);
|
||||
return;
|
||||
}
|
||||
gtk_tree_path_free (path);
|
||||
|
||||
gtk_tree_model_get (GTK_TREE_MODEL (list_store), &iter,
|
||||
SAVE, &saved,
|
||||
UNIT, &unit,
|
||||
-1);
|
||||
|
||||
if (unit >= pika_unit_get_number_of_built_in_units ())
|
||||
{
|
||||
pika_unit_set_deletion_flag (unit, saved);
|
||||
gtk_list_store_set (GTK_LIST_STORE (list_store), &iter,
|
||||
SAVE, ! saved,
|
||||
-1);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
unit_list_init (GtkTreeView *tv)
|
||||
{
|
||||
GtkListStore *list_store;
|
||||
GtkTreeIter iter;
|
||||
gint num_units;
|
||||
PikaUnit unit;
|
||||
GdkRGBA color;
|
||||
|
||||
list_store = GTK_LIST_STORE (gtk_tree_view_get_model (tv));
|
||||
|
||||
gtk_list_store_clear (list_store);
|
||||
|
||||
num_units = pika_unit_get_number_of_units ();
|
||||
|
||||
color.red = 0.87;
|
||||
color.green = 0.87;
|
||||
color.blue = 1;
|
||||
color.alpha = 1;
|
||||
|
||||
for (unit = PIKA_UNIT_INCH; unit < num_units; unit++)
|
||||
{
|
||||
gboolean user_unit = (unit >= pika_unit_get_number_of_built_in_units ());
|
||||
|
||||
gtk_list_store_append (list_store, &iter);
|
||||
gtk_list_store_set (list_store, &iter,
|
||||
SAVE, ! pika_unit_get_deletion_flag (unit),
|
||||
IDENTIFIER, pika_unit_get_identifier (unit),
|
||||
FACTOR, pika_unit_get_factor (unit),
|
||||
DIGITS, pika_unit_get_digits (unit),
|
||||
SYMBOL, pika_unit_get_symbol (unit),
|
||||
ABBREVIATION, pika_unit_get_abbreviation (unit),
|
||||
SINGULAR, pika_unit_get_singular (unit),
|
||||
PLURAL, pika_unit_get_plural (unit),
|
||||
UNIT, unit,
|
||||
USER_UNIT, user_unit,
|
||||
|
||||
user_unit ? -1 : BG_COLOR, &color,
|
||||
|
||||
-1);
|
||||
}
|
||||
|
||||
if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (list_store), &iter))
|
||||
gtk_tree_selection_select_iter (gtk_tree_view_get_selection (tv), &iter);
|
||||
}
|
||||
981
plug-ins/common/van-gogh-lic.c
Normal file
981
plug-ins/common/van-gogh-lic.c
Normal file
@ -0,0 +1,981 @@
|
||||
/* LIC 0.14 -- image filter plug-in for PIKA
|
||||
* Copyright (C) 1996 Tom Bech
|
||||
*
|
||||
* E-mail: tomb@gimp.org
|
||||
* You can contact the original PIKA authors at pika@xcf.berkeley.edu
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* In other words, you can't sue me for whatever happens while using this ;)
|
||||
*
|
||||
* Changes (post 0.10):
|
||||
* -> 0.11: Fixed a bug in the convolution kernels (Tom).
|
||||
* -> 0.12: Added Quartic's bilinear interpolation stuff (Tom).
|
||||
* -> 0.13 Changed some UI stuff causing trouble with the 0.60 release, added
|
||||
* the (PIKA) tags and changed random() calls to rand() (Tom)
|
||||
* -> 0.14 Ported to 0.99.11 (Tom)
|
||||
*
|
||||
* This plug-in implements the Line Integral Convolution (LIC) as described in
|
||||
* Cabral et al. "Imaging vector fields using line integral convolution" in the
|
||||
* Proceedings of ACM SIGGRAPH 93. Publ. by ACM, New York, NY, USA. p. 263-270.
|
||||
* (See http://www8.cs.umu.se/kurser/TDBD13/VT00/extra/p263-cabral.pdf)
|
||||
*
|
||||
* Some of the code is based on code by Steinar Haugen (thanks!), the Perlin
|
||||
* noise function is practically ripped as is :)
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <libpika/pika.h>
|
||||
#include <libpika/pikaui.h>
|
||||
|
||||
#include "libpika/stdplugins-intl.h"
|
||||
|
||||
|
||||
/************/
|
||||
/* Typedefs */
|
||||
/************/
|
||||
|
||||
#define numx 40 /* Pseudo-random vector grid size */
|
||||
#define numy 40
|
||||
|
||||
#define PLUG_IN_PROC "plug-in-lic"
|
||||
#define PLUG_IN_BINARY "van-gogh-lic"
|
||||
#define PLUG_IN_ROLE "pika-van-gogh-lic"
|
||||
|
||||
typedef enum
|
||||
{
|
||||
LIC_HUE,
|
||||
LIC_SATURATION,
|
||||
LIC_BRIGHTNESS
|
||||
} LICEffectChannel;
|
||||
|
||||
|
||||
typedef struct _Lic Lic;
|
||||
typedef struct _LicClass LicClass;
|
||||
|
||||
struct _Lic
|
||||
{
|
||||
PikaPlugIn parent_instance;
|
||||
};
|
||||
|
||||
struct _LicClass
|
||||
{
|
||||
PikaPlugInClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
#define LIC_TYPE (lic_get_type ())
|
||||
#define LIC (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), LIC_TYPE, Lic))
|
||||
|
||||
GType lic_get_type (void) G_GNUC_CONST;
|
||||
|
||||
static GList * lic_query_procedures (PikaPlugIn *plug_in);
|
||||
static PikaProcedure * lic_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name);
|
||||
|
||||
static PikaValueArray * lic_run (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data);
|
||||
static void lic_scale_entry_update (PikaLabelSpin *entry,
|
||||
gdouble *value);
|
||||
|
||||
|
||||
G_DEFINE_TYPE (Lic, lic, PIKA_TYPE_PLUG_IN)
|
||||
|
||||
PIKA_MAIN (LIC_TYPE)
|
||||
DEFINE_STD_SET_I18N
|
||||
|
||||
|
||||
static void
|
||||
lic_class_init (LicClass *klass)
|
||||
{
|
||||
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
|
||||
|
||||
plug_in_class->query_procedures = lic_query_procedures;
|
||||
plug_in_class->create_procedure = lic_create_procedure;
|
||||
plug_in_class->set_i18n = STD_SET_I18N;
|
||||
}
|
||||
|
||||
static void
|
||||
lic_init (Lic *lic)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/*****************************/
|
||||
/* Global variables and such */
|
||||
/*****************************/
|
||||
|
||||
static gdouble G[numx][numy][2];
|
||||
|
||||
typedef struct
|
||||
{
|
||||
gdouble filtlen;
|
||||
gdouble noisemag;
|
||||
gdouble intsteps;
|
||||
gdouble minv;
|
||||
gdouble maxv;
|
||||
gint effect_channel;
|
||||
gint effect_operator;
|
||||
gint effect_convolve;
|
||||
gint32 effect_image_id;
|
||||
} LicValues;
|
||||
|
||||
static LicValues licvals;
|
||||
|
||||
static gdouble l = 10.0;
|
||||
static gdouble dx = 2.0;
|
||||
static gdouble dy = 2.0;
|
||||
static gdouble minv = -2.5;
|
||||
static gdouble maxv = 2.5;
|
||||
static gdouble isteps = 20.0;
|
||||
|
||||
static gboolean source_drw_has_alpha = FALSE;
|
||||
|
||||
static gint effect_width, effect_height;
|
||||
static gint border_x, border_y, border_w, border_h;
|
||||
|
||||
static GtkWidget *dialog;
|
||||
|
||||
/************************/
|
||||
/* Convenience routines */
|
||||
/************************/
|
||||
|
||||
static void
|
||||
peek (GeglBuffer *buffer,
|
||||
gint x,
|
||||
gint y,
|
||||
PikaRGB *color)
|
||||
{
|
||||
gegl_buffer_sample (buffer, x, y, NULL,
|
||||
color, babl_format ("R'G'B'A double"),
|
||||
GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE);
|
||||
}
|
||||
|
||||
static void
|
||||
poke (GeglBuffer *buffer,
|
||||
gint x,
|
||||
gint y,
|
||||
PikaRGB *color)
|
||||
{
|
||||
gegl_buffer_set (buffer, GEGL_RECTANGLE (x, y, 1, 1), 0,
|
||||
babl_format ("R'G'B'A double"), color,
|
||||
GEGL_AUTO_ROWSTRIDE);
|
||||
}
|
||||
|
||||
static gint
|
||||
peekmap (const guchar *image,
|
||||
gint x,
|
||||
gint y)
|
||||
{
|
||||
while (x < 0)
|
||||
x += effect_width;
|
||||
x %= effect_width;
|
||||
|
||||
while (y < 0)
|
||||
y += effect_height;
|
||||
y %= effect_height;
|
||||
|
||||
return (gint) image[x + effect_width * y];
|
||||
}
|
||||
|
||||
/*************/
|
||||
/* Main part */
|
||||
/*************/
|
||||
|
||||
/***************************************************/
|
||||
/* Compute the derivative in the x and y direction */
|
||||
/* We use these convolution kernels: */
|
||||
/* |1 0 -1| | 1 2 1| */
|
||||
/* DX: |2 0 -2| DY: | 0 0 0| */
|
||||
/* |1 0 -1| | -1 -2 -1| */
|
||||
/* (It's a variation of the Sobel kernels, really) */
|
||||
/***************************************************/
|
||||
|
||||
static gint
|
||||
gradx (const guchar *image,
|
||||
gint x,
|
||||
gint y)
|
||||
{
|
||||
gint val = 0;
|
||||
|
||||
val = val + peekmap (image, x-1, y-1);
|
||||
val = val - peekmap (image, x+1, y-1);
|
||||
|
||||
val = val + 2 * peekmap (image, x-1, y);
|
||||
val = val - 2 * peekmap (image, x+1, y);
|
||||
|
||||
val = val + peekmap (image, x-1, y+1);
|
||||
val = val - peekmap (image, x+1, y+1);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static gint
|
||||
grady (const guchar *image,
|
||||
gint x,
|
||||
gint y)
|
||||
{
|
||||
gint val = 0;
|
||||
|
||||
val = val + peekmap (image, x-1, y-1);
|
||||
val = val + 2 * peekmap (image, x, y-1);
|
||||
val = val + peekmap (image, x+1, y-1);
|
||||
|
||||
val = val - peekmap (image, x-1, y+1);
|
||||
val = val - 2 * peekmap (image, x, y+1);
|
||||
val = val - peekmap (image, x+1, y+1);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
/************************************/
|
||||
/* A nice 2nd order cubic spline :) */
|
||||
/************************************/
|
||||
|
||||
static gdouble
|
||||
cubic (gdouble t)
|
||||
{
|
||||
gdouble at = fabs (t);
|
||||
|
||||
return (at < 1.0) ? at * at * (2.0 * at - 3.0) + 1.0 : 0.0;
|
||||
}
|
||||
|
||||
static gdouble
|
||||
omega (gdouble u,
|
||||
gdouble v,
|
||||
gint i,
|
||||
gint j)
|
||||
{
|
||||
while (i < 0)
|
||||
i += numx;
|
||||
|
||||
while (j < 0)
|
||||
j += numy;
|
||||
|
||||
i %= numx;
|
||||
j %= numy;
|
||||
|
||||
return cubic (u) * cubic (v) * (G[i][j][0]*u + G[i][j][1]*v);
|
||||
}
|
||||
|
||||
/*************************************************************/
|
||||
/* The noise function (2D variant of Perlins noise function) */
|
||||
/*************************************************************/
|
||||
|
||||
static gdouble
|
||||
noise (gdouble x,
|
||||
gdouble y)
|
||||
{
|
||||
gint i, sti = (gint) floor (x / dx);
|
||||
gint j, stj = (gint) floor (y / dy);
|
||||
|
||||
gdouble sum = 0.0;
|
||||
|
||||
/* Calculate the gdouble sum */
|
||||
/* ======================== */
|
||||
|
||||
for (i = sti; i <= sti + 1; i++)
|
||||
for (j = stj; j <= stj + 1; j++)
|
||||
sum += omega ((x - (gdouble) i * dx) / dx,
|
||||
(y - (gdouble) j * dy) / dy,
|
||||
i, j);
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
/*************************************************/
|
||||
/* Generates pseudo-random vectors with length 1 */
|
||||
/*************************************************/
|
||||
|
||||
static void
|
||||
generatevectors (void)
|
||||
{
|
||||
gdouble alpha;
|
||||
gint i, j;
|
||||
GRand *gr;
|
||||
|
||||
gr = g_rand_new();
|
||||
|
||||
for (i = 0; i < numx; i++)
|
||||
{
|
||||
for (j = 0; j < numy; j++)
|
||||
{
|
||||
alpha = g_rand_double_range (gr, 0, 2) * G_PI;
|
||||
G[i][j][0] = cos (alpha);
|
||||
G[i][j][1] = sin (alpha);
|
||||
}
|
||||
}
|
||||
|
||||
g_rand_free (gr);
|
||||
}
|
||||
|
||||
/* A simple triangle filter */
|
||||
/* ======================== */
|
||||
|
||||
static gdouble
|
||||
filter (gdouble u)
|
||||
{
|
||||
gdouble f = 1.0 - fabs (u) / l;
|
||||
|
||||
return (f < 0.0) ? 0.0 : f;
|
||||
}
|
||||
|
||||
/******************************************************/
|
||||
/* Compute the Line Integral Convolution (LIC) at x,y */
|
||||
/******************************************************/
|
||||
|
||||
static gdouble
|
||||
lic_noise (gint x,
|
||||
gint y,
|
||||
gdouble vx,
|
||||
gdouble vy)
|
||||
{
|
||||
gdouble i = 0.0;
|
||||
gdouble f1 = 0.0, f2 = 0.0;
|
||||
gdouble u, step = 2.0 * l / isteps;
|
||||
gdouble xx = (gdouble) x, yy = (gdouble) y;
|
||||
gdouble c, s;
|
||||
|
||||
/* Get vector at x,y */
|
||||
/* ================= */
|
||||
|
||||
c = vx;
|
||||
s = vy;
|
||||
|
||||
/* Calculate integral numerically */
|
||||
/* ============================== */
|
||||
|
||||
f1 = filter (-l) * noise (xx + l * c , yy + l * s);
|
||||
|
||||
for (u = -l + step; u <= l; u += step)
|
||||
{
|
||||
f2 = filter (u) * noise ( xx - u * c , yy - u * s);
|
||||
i += (f1 + f2) * 0.5 * step;
|
||||
f1 = f2;
|
||||
}
|
||||
|
||||
i = (i - minv) / (maxv - minv);
|
||||
|
||||
i = CLAMP (i, 0.0, 1.0);
|
||||
|
||||
i = (i / 2.0) + 0.5;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static void
|
||||
getpixel (GeglBuffer *buffer,
|
||||
PikaRGB *p,
|
||||
gdouble u,
|
||||
gdouble v)
|
||||
{
|
||||
register gint x1, y1, x2, y2;
|
||||
gint width, height;
|
||||
static PikaRGB pp[4];
|
||||
|
||||
width = border_w;
|
||||
height = border_h;
|
||||
|
||||
x1 = (gint)u;
|
||||
y1 = (gint)v;
|
||||
|
||||
if (x1 < 0)
|
||||
x1 = width - (-x1 % width);
|
||||
else
|
||||
x1 = x1 % width;
|
||||
|
||||
if (y1 < 0)
|
||||
y1 = height - (-y1 % height);
|
||||
else
|
||||
y1 = y1 % height;
|
||||
|
||||
x2 = (x1 + 1) % width;
|
||||
y2 = (y1 + 1) % height;
|
||||
|
||||
peek (buffer, x1, y1, &pp[0]);
|
||||
peek (buffer, x2, y1, &pp[1]);
|
||||
peek (buffer, x1, y2, &pp[2]);
|
||||
peek (buffer, x2, y2, &pp[3]);
|
||||
|
||||
if (source_drw_has_alpha)
|
||||
*p = pika_bilinear_rgba (u, v, pp);
|
||||
else
|
||||
*p = pika_bilinear_rgb (u, v, pp);
|
||||
}
|
||||
|
||||
static void
|
||||
lic_image (GeglBuffer *buffer,
|
||||
gint x,
|
||||
gint y,
|
||||
gdouble vx,
|
||||
gdouble vy,
|
||||
PikaRGB *color)
|
||||
{
|
||||
gdouble u, step = 2.0 * l / isteps;
|
||||
gdouble xx = (gdouble) x, yy = (gdouble) y;
|
||||
gdouble c, s;
|
||||
PikaRGB col = { 0, 0, 0, 0 };
|
||||
PikaRGB col1, col2, col3;
|
||||
|
||||
/* Get vector at x,y */
|
||||
/* ================= */
|
||||
|
||||
c = vx;
|
||||
s = vy;
|
||||
|
||||
/* Calculate integral numerically */
|
||||
/* ============================== */
|
||||
|
||||
getpixel (buffer, &col1, xx + l * c, yy + l * s);
|
||||
|
||||
if (source_drw_has_alpha)
|
||||
pika_rgba_multiply (&col1, filter (-l));
|
||||
else
|
||||
pika_rgb_multiply (&col1, filter (-l));
|
||||
|
||||
for (u = -l + step; u <= l; u += step)
|
||||
{
|
||||
getpixel (buffer, &col2, xx - u * c, yy - u * s);
|
||||
|
||||
if (source_drw_has_alpha)
|
||||
{
|
||||
pika_rgba_multiply (&col2, filter (u));
|
||||
|
||||
col3 = col1;
|
||||
pika_rgba_add (&col3, &col2);
|
||||
pika_rgba_multiply (&col3, 0.5 * step);
|
||||
pika_rgba_add (&col, &col3);
|
||||
}
|
||||
else
|
||||
{
|
||||
pika_rgb_multiply (&col2, filter (u));
|
||||
|
||||
col3 = col1;
|
||||
pika_rgb_add (&col3, &col2);
|
||||
pika_rgb_multiply (&col3, 0.5 * step);
|
||||
pika_rgb_add (&col, &col3);
|
||||
}
|
||||
col1 = col2;
|
||||
}
|
||||
if (source_drw_has_alpha)
|
||||
pika_rgba_multiply (&col, 1.0 / l);
|
||||
else
|
||||
pika_rgb_multiply (&col, 1.0 / l);
|
||||
pika_rgb_clamp (&col);
|
||||
|
||||
*color = col;
|
||||
}
|
||||
|
||||
static guchar *
|
||||
rgb_to_hsl (PikaDrawable *drawable,
|
||||
LICEffectChannel effect_channel)
|
||||
{
|
||||
GeglBuffer *buffer;
|
||||
guchar *themap, data[4];
|
||||
gint x, y;
|
||||
PikaRGB color;
|
||||
PikaHSL color_hsl;
|
||||
gdouble val = 0.0;
|
||||
glong maxc, index = 0;
|
||||
GRand *gr;
|
||||
|
||||
gr = g_rand_new ();
|
||||
|
||||
maxc = pika_drawable_get_width (drawable) * pika_drawable_get_height (drawable);
|
||||
|
||||
buffer = pika_drawable_get_buffer (drawable);
|
||||
|
||||
themap = g_new (guchar, maxc);
|
||||
|
||||
for (y = 0; y < border_h; y++)
|
||||
{
|
||||
for (x = 0; x < border_w; x++)
|
||||
{
|
||||
data[3] = 255;
|
||||
|
||||
gegl_buffer_sample (buffer, x, y, NULL,
|
||||
data, babl_format ("R'G'B'A u8"),
|
||||
GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE);
|
||||
|
||||
pika_rgba_set_uchar (&color, data[0], data[1], data[2], data[3]);
|
||||
pika_rgb_to_hsl (&color, &color_hsl);
|
||||
|
||||
switch (effect_channel)
|
||||
{
|
||||
case LIC_HUE:
|
||||
val = color_hsl.h * 255;
|
||||
break;
|
||||
case LIC_SATURATION:
|
||||
val = color_hsl.s * 255;
|
||||
break;
|
||||
case LIC_BRIGHTNESS:
|
||||
val = color_hsl.l * 255;
|
||||
break;
|
||||
}
|
||||
|
||||
/* add some random to avoid unstructured areas. */
|
||||
val += g_rand_double_range (gr, -1.0, 1.0);
|
||||
|
||||
themap[index++] = (guchar) CLAMP0255 (RINT (val));
|
||||
}
|
||||
}
|
||||
|
||||
g_object_unref (buffer);
|
||||
|
||||
g_rand_free (gr);
|
||||
|
||||
return themap;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
compute_lic (PikaDrawable *drawable,
|
||||
const guchar *scalarfield,
|
||||
gboolean rotate)
|
||||
{
|
||||
GeglBuffer *src_buffer;
|
||||
GeglBuffer *dest_buffer;
|
||||
gint xcount, ycount;
|
||||
PikaRGB color;
|
||||
gdouble vx, vy, tmp;
|
||||
|
||||
src_buffer = pika_drawable_get_buffer (drawable);
|
||||
dest_buffer = pika_drawable_get_shadow_buffer (drawable);
|
||||
|
||||
for (ycount = 0; ycount < border_h; ycount++)
|
||||
{
|
||||
for (xcount = 0; xcount < border_w; xcount++)
|
||||
{
|
||||
/* Get derivative at (x,y) and normalize it */
|
||||
/* ============================================================== */
|
||||
|
||||
vx = gradx (scalarfield, xcount, ycount);
|
||||
vy = grady (scalarfield, xcount, ycount);
|
||||
|
||||
/* Rotate if needed */
|
||||
if (rotate)
|
||||
{
|
||||
tmp = vy;
|
||||
vy = -vx;
|
||||
vx = tmp;
|
||||
}
|
||||
|
||||
tmp = sqrt (vx * vx + vy * vy);
|
||||
if (tmp >= 0.000001)
|
||||
{
|
||||
tmp = 1.0 / tmp;
|
||||
vx *= tmp;
|
||||
vy *= tmp;
|
||||
}
|
||||
|
||||
/* Convolve with the LIC at (x,y) */
|
||||
/* ============================== */
|
||||
|
||||
if (licvals.effect_convolve == 0)
|
||||
{
|
||||
peek (src_buffer, xcount, ycount, &color);
|
||||
|
||||
tmp = lic_noise (xcount, ycount, vx, vy);
|
||||
|
||||
if (source_drw_has_alpha)
|
||||
pika_rgba_multiply (&color, tmp);
|
||||
else
|
||||
pika_rgb_multiply (&color, tmp);
|
||||
}
|
||||
else
|
||||
{
|
||||
lic_image (src_buffer, xcount, ycount, vx, vy, &color);
|
||||
}
|
||||
|
||||
poke (dest_buffer, xcount, ycount, &color);
|
||||
}
|
||||
|
||||
pika_progress_update ((gfloat) ycount / (gfloat) border_h);
|
||||
}
|
||||
|
||||
g_object_unref (src_buffer);
|
||||
g_object_unref (dest_buffer);
|
||||
|
||||
pika_progress_update (1.0);
|
||||
}
|
||||
|
||||
static void
|
||||
compute_image (PikaDrawable *drawable)
|
||||
{
|
||||
PikaDrawable *effect_image;
|
||||
guchar *scalarfield = NULL;
|
||||
|
||||
/* Get some useful info on the input drawable */
|
||||
/* ========================================== */
|
||||
if (! pika_drawable_mask_intersect (drawable,
|
||||
&border_x, &border_y,
|
||||
&border_w, &border_h))
|
||||
return;
|
||||
|
||||
pika_progress_init (_("Van Gogh (LIC)"));
|
||||
|
||||
if (licvals.effect_convolve == 0)
|
||||
generatevectors ();
|
||||
|
||||
if (licvals.filtlen < 0.1)
|
||||
licvals.filtlen = 0.1;
|
||||
|
||||
l = licvals.filtlen;
|
||||
dx = dy = licvals.noisemag;
|
||||
minv = licvals.minv / 10.0;
|
||||
maxv = licvals.maxv / 10.0;
|
||||
isteps = licvals.intsteps;
|
||||
|
||||
source_drw_has_alpha = pika_drawable_has_alpha (drawable);
|
||||
|
||||
effect_image = pika_drawable_get_by_id (licvals.effect_image_id);
|
||||
|
||||
effect_width = pika_drawable_get_width (effect_image);
|
||||
effect_height = pika_drawable_get_height (effect_image);
|
||||
|
||||
switch (licvals.effect_channel)
|
||||
{
|
||||
case 0:
|
||||
scalarfield = rgb_to_hsl (effect_image, LIC_HUE);
|
||||
break;
|
||||
case 1:
|
||||
scalarfield = rgb_to_hsl (effect_image, LIC_SATURATION);
|
||||
break;
|
||||
case 2:
|
||||
scalarfield = rgb_to_hsl (effect_image, LIC_BRIGHTNESS);
|
||||
break;
|
||||
}
|
||||
|
||||
compute_lic (drawable, scalarfield, licvals.effect_operator);
|
||||
|
||||
g_free (scalarfield);
|
||||
|
||||
/* Update image */
|
||||
/* ============ */
|
||||
|
||||
pika_drawable_merge_shadow (drawable, TRUE);
|
||||
pika_drawable_update (drawable, border_x, border_y, border_w, border_h);
|
||||
|
||||
pika_displays_flush ();
|
||||
}
|
||||
|
||||
/**************************/
|
||||
/* Below is only UI stuff */
|
||||
/**************************/
|
||||
|
||||
static gboolean
|
||||
effect_image_constrain (PikaImage *image,
|
||||
PikaItem *item,
|
||||
gpointer data)
|
||||
{
|
||||
return pika_drawable_is_rgb (PIKA_DRAWABLE (item));
|
||||
}
|
||||
|
||||
static gboolean
|
||||
create_main_dialog (void)
|
||||
{
|
||||
GtkWidget *vbox;
|
||||
GtkWidget *hbox;
|
||||
GtkWidget *frame;
|
||||
GtkWidget *grid;
|
||||
GtkWidget *combo;
|
||||
GtkWidget *scale;
|
||||
gint row;
|
||||
gboolean run;
|
||||
|
||||
pika_ui_init (PLUG_IN_BINARY);
|
||||
|
||||
dialog = pika_dialog_new (_("Van Gogh (LIC)"), PLUG_IN_ROLE,
|
||||
NULL, 0,
|
||||
pika_standard_help_func, PLUG_IN_PROC,
|
||||
|
||||
_("_Cancel"), GTK_RESPONSE_CANCEL,
|
||||
_("_OK"), GTK_RESPONSE_OK,
|
||||
|
||||
NULL);
|
||||
|
||||
pika_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
|
||||
GTK_RESPONSE_OK,
|
||||
GTK_RESPONSE_CANCEL,
|
||||
-1);
|
||||
|
||||
pika_window_set_transient (GTK_WINDOW (dialog));
|
||||
|
||||
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
|
||||
gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
|
||||
gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
|
||||
vbox, TRUE, TRUE, 0);
|
||||
gtk_widget_show (vbox);
|
||||
|
||||
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
|
||||
gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
|
||||
gtk_widget_show (hbox);
|
||||
|
||||
frame = pika_int_radio_group_new (TRUE, _("Effect Channel"),
|
||||
G_CALLBACK (pika_radio_button_update),
|
||||
&licvals.effect_channel, NULL,
|
||||
licvals.effect_channel,
|
||||
|
||||
_("_Hue"), 0, NULL,
|
||||
_("_Saturation"), 1, NULL,
|
||||
_("_Brightness"), 2, NULL,
|
||||
|
||||
NULL);
|
||||
gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, FALSE, 0);
|
||||
gtk_widget_show (frame);
|
||||
|
||||
frame = pika_int_radio_group_new (TRUE, _("Effect Operator"),
|
||||
G_CALLBACK (pika_radio_button_update),
|
||||
&licvals.effect_operator, NULL,
|
||||
licvals.effect_operator,
|
||||
|
||||
_("_Derivative"), 0, NULL,
|
||||
_("_Gradient"), 1, NULL,
|
||||
|
||||
NULL);
|
||||
gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, FALSE, 0);
|
||||
gtk_widget_show (frame);
|
||||
|
||||
frame = pika_int_radio_group_new (TRUE, _("Convolve"),
|
||||
G_CALLBACK (pika_radio_button_update),
|
||||
&licvals.effect_convolve, NULL,
|
||||
licvals.effect_convolve,
|
||||
|
||||
_("_With white noise"), 0, NULL,
|
||||
_("W_ith source image"), 1, NULL,
|
||||
|
||||
NULL);
|
||||
gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, FALSE, 0);
|
||||
gtk_widget_show (frame);
|
||||
|
||||
/* Effect image menu */
|
||||
grid = gtk_grid_new ();
|
||||
gtk_grid_set_column_spacing (GTK_GRID (grid), 6);
|
||||
gtk_box_pack_start (GTK_BOX (vbox), grid, FALSE, FALSE, 0);
|
||||
gtk_widget_show (grid);
|
||||
|
||||
combo = pika_drawable_combo_box_new (effect_image_constrain, NULL, NULL);
|
||||
pika_int_combo_box_connect (PIKA_INT_COMBO_BOX (combo),
|
||||
licvals.effect_image_id,
|
||||
G_CALLBACK (pika_int_combo_box_get_active),
|
||||
&licvals.effect_image_id, NULL);
|
||||
|
||||
pika_grid_attach_aligned (GTK_GRID (grid), 0, 0,
|
||||
_("_Effect image:"), 0.0, 0.5, combo, 2);
|
||||
|
||||
grid = gtk_grid_new ();
|
||||
gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
|
||||
gtk_grid_set_column_spacing (GTK_GRID (grid), 6);
|
||||
gtk_box_pack_start (GTK_BOX (vbox), grid, FALSE, FALSE, 0);
|
||||
gtk_widget_show (grid);
|
||||
|
||||
row = 0;
|
||||
|
||||
scale = pika_scale_entry_new (_("_Filter length:"), licvals.filtlen, 0.1, 64, 1);
|
||||
pika_label_spin_set_increments (PIKA_LABEL_SPIN (scale), 1.0, 8.0);
|
||||
g_signal_connect (scale, "value-changed",
|
||||
G_CALLBACK (lic_scale_entry_update),
|
||||
&licvals.filtlen);
|
||||
gtk_grid_attach (GTK_GRID (grid), scale, 0, row++, 3, 1);
|
||||
gtk_widget_show (scale);
|
||||
|
||||
scale = pika_scale_entry_new (_("_Noise magnitude:"), licvals.noisemag, 1, 5, 1);
|
||||
pika_label_spin_set_increments (PIKA_LABEL_SPIN (scale), 0.1, 1.0);
|
||||
g_signal_connect (scale, "value-changed",
|
||||
G_CALLBACK (lic_scale_entry_update),
|
||||
&licvals.noisemag);
|
||||
gtk_grid_attach (GTK_GRID (grid), scale, 0, row++, 3, 1);
|
||||
gtk_widget_show (scale);
|
||||
|
||||
scale = pika_scale_entry_new (_("In_tegration steps:"), licvals.intsteps, 1, 40, 1);
|
||||
g_signal_connect (scale, "value-changed",
|
||||
G_CALLBACK (lic_scale_entry_update),
|
||||
&licvals.intsteps);
|
||||
gtk_grid_attach (GTK_GRID (grid), scale, 0, row++, 3, 1);
|
||||
gtk_widget_show (scale);
|
||||
|
||||
scale = pika_scale_entry_new (_("_Minimum value:"), licvals.minv, -100, 0, 1);
|
||||
g_signal_connect (scale, "value-changed",
|
||||
G_CALLBACK (lic_scale_entry_update),
|
||||
&licvals.minv);
|
||||
gtk_grid_attach (GTK_GRID (grid), scale, 0, row++, 3, 1);
|
||||
gtk_widget_show (scale);
|
||||
|
||||
scale = pika_scale_entry_new (_("M_aximum value:"),
|
||||
licvals.maxv, 0, 100, 1);
|
||||
g_signal_connect (scale, "value-changed",
|
||||
G_CALLBACK (lic_scale_entry_update),
|
||||
&licvals.maxv);
|
||||
gtk_grid_attach (GTK_GRID (grid), scale, 0, row++, 3, 1);
|
||||
gtk_widget_show (scale);
|
||||
|
||||
gtk_widget_show (dialog);
|
||||
|
||||
run = (pika_dialog_run (PIKA_DIALOG (dialog)) == GTK_RESPONSE_OK);
|
||||
|
||||
gtk_widget_destroy (dialog);
|
||||
|
||||
return run;
|
||||
}
|
||||
|
||||
/*************************************/
|
||||
/* Set parameters to standard values */
|
||||
/*************************************/
|
||||
|
||||
static void
|
||||
set_default_settings (void)
|
||||
{
|
||||
licvals.filtlen = 5;
|
||||
licvals.noisemag = 2;
|
||||
licvals.intsteps = 25;
|
||||
licvals.minv = -25;
|
||||
licvals.maxv = 25;
|
||||
licvals.effect_channel = 2;
|
||||
licvals.effect_operator = 1;
|
||||
licvals.effect_convolve = 1;
|
||||
licvals.effect_image_id = -1;
|
||||
}
|
||||
|
||||
static GList *
|
||||
lic_query_procedures (PikaPlugIn *plug_in)
|
||||
{
|
||||
return g_list_append (NULL, g_strdup (PLUG_IN_PROC));
|
||||
}
|
||||
|
||||
static PikaProcedure *
|
||||
lic_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name)
|
||||
{
|
||||
PikaProcedure *procedure = NULL;
|
||||
|
||||
if (! strcmp (name, PLUG_IN_PROC))
|
||||
{
|
||||
procedure = pika_image_procedure_new (plug_in, name,
|
||||
PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
lic_run, NULL, NULL);
|
||||
|
||||
pika_procedure_set_image_types (procedure, "RGB*");
|
||||
pika_procedure_set_sensitivity_mask (procedure,
|
||||
PIKA_PROCEDURE_SENSITIVE_DRAWABLE);
|
||||
|
||||
pika_procedure_set_menu_label (procedure, _("_Van Gogh (LIC)..."));
|
||||
pika_procedure_add_menu_path (procedure, "<Image>/Filters/Artistic");
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
_("Special effects that nobody "
|
||||
"understands"),
|
||||
"No help yet",
|
||||
name);
|
||||
pika_procedure_set_attribution (procedure,
|
||||
"Tom Bech & Federico Mena Quintero",
|
||||
"Tom Bech & Federico Mena Quintero",
|
||||
"Version 0.14, September 24 1997");
|
||||
}
|
||||
|
||||
return procedure;
|
||||
}
|
||||
|
||||
static PikaValueArray *
|
||||
lic_run (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data)
|
||||
{
|
||||
PikaDrawable *drawable;
|
||||
|
||||
gegl_init (NULL, NULL);
|
||||
|
||||
if (n_drawables != 1)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
g_set_error (&error, PIKA_PLUG_IN_ERROR, 0,
|
||||
_("Procedure '%s' only works with one drawable."),
|
||||
PLUG_IN_PROC);
|
||||
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_CALLING_ERROR,
|
||||
error);
|
||||
}
|
||||
else
|
||||
{
|
||||
drawable = drawables[0];
|
||||
}
|
||||
|
||||
/* Set default values */
|
||||
/* ================== */
|
||||
|
||||
set_default_settings ();
|
||||
|
||||
/* Possibly retrieve data */
|
||||
/* ====================== */
|
||||
|
||||
pika_get_data (PLUG_IN_PROC, &licvals);
|
||||
|
||||
if (! pika_item_id_is_valid (licvals.effect_image_id))
|
||||
licvals.effect_image_id = -1;
|
||||
|
||||
/* Make sure that the drawable is RGBA or RGB color */
|
||||
/* ================================================ */
|
||||
|
||||
if (pika_drawable_is_rgb (drawable))
|
||||
{
|
||||
switch (run_mode)
|
||||
{
|
||||
case PIKA_RUN_INTERACTIVE:
|
||||
if (! create_main_dialog ())
|
||||
{
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_CANCEL,
|
||||
NULL);
|
||||
}
|
||||
|
||||
compute_image (drawable);
|
||||
|
||||
pika_set_data (PLUG_IN_PROC, &licvals, sizeof (LicValues));
|
||||
break;
|
||||
|
||||
case PIKA_RUN_WITH_LAST_VALS:
|
||||
compute_image (drawable);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_EXECUTION_ERROR,
|
||||
NULL);
|
||||
}
|
||||
|
||||
return pika_procedure_new_return_values (procedure, PIKA_PDB_SUCCESS, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
lic_scale_entry_update (PikaLabelSpin *entry,
|
||||
gdouble *value)
|
||||
{
|
||||
*value = pika_label_spin_get_value (entry);
|
||||
}
|
||||
1928
plug-ins/common/warp.c
Normal file
1928
plug-ins/common/warp.c
Normal file
File diff suppressed because it is too large
Load Diff
425
plug-ins/common/wavelet-decompose.c
Normal file
425
plug-ins/common/wavelet-decompose.c
Normal file
@ -0,0 +1,425 @@
|
||||
/*
|
||||
* Wavelet decompose plug-in by Miroslav Talasek, miroslav.talasek@seznam.cz
|
||||
*/
|
||||
|
||||
/*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <libpika/pika.h>
|
||||
#include <libpika/pikaui.h>
|
||||
|
||||
#include "libpika/stdplugins-intl.h"
|
||||
|
||||
|
||||
#define PLUG_IN_PROC "plug-in-wavelet-decompose"
|
||||
#define PLUG_IN_ROLE "pika-wavelet-decompose"
|
||||
#define PLUG_IN_BINARY "wavelet-decompose"
|
||||
|
||||
|
||||
|
||||
|
||||
typedef struct _Wavelet Wavelet;
|
||||
typedef struct _WaveletClass WaveletClass;
|
||||
|
||||
struct _Wavelet
|
||||
{
|
||||
PikaPlugIn parent_instance;
|
||||
};
|
||||
|
||||
struct _WaveletClass
|
||||
{
|
||||
PikaPlugInClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
#define WAVELET_TYPE (wavelet_get_type ())
|
||||
#define WAVELET (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), WAVELET_TYPE, Wavelet))
|
||||
|
||||
GType wavelet_get_type (void) G_GNUC_CONST;
|
||||
|
||||
static GList * wavelet_query_procedures (PikaPlugIn *plug_in);
|
||||
static PikaProcedure * wavelet_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name);
|
||||
|
||||
static PikaValueArray * wavelet_run (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data);
|
||||
|
||||
static void wavelet_blur (PikaDrawable *drawable,
|
||||
gint radius);
|
||||
|
||||
static gboolean wavelet_decompose_dialog (PikaProcedure *procedure,
|
||||
GObject *config);
|
||||
|
||||
|
||||
G_DEFINE_TYPE (Wavelet, wavelet, PIKA_TYPE_PLUG_IN)
|
||||
|
||||
PIKA_MAIN (WAVELET_TYPE)
|
||||
DEFINE_STD_SET_I18N
|
||||
|
||||
|
||||
|
||||
|
||||
static void
|
||||
wavelet_class_init (WaveletClass *klass)
|
||||
{
|
||||
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
|
||||
|
||||
plug_in_class->query_procedures = wavelet_query_procedures;
|
||||
plug_in_class->create_procedure = wavelet_create_procedure;
|
||||
plug_in_class->set_i18n = STD_SET_I18N;
|
||||
}
|
||||
|
||||
static void
|
||||
wavelet_init (Wavelet *wavelet)
|
||||
{
|
||||
}
|
||||
|
||||
static GList *
|
||||
wavelet_query_procedures (PikaPlugIn *plug_in)
|
||||
{
|
||||
return g_list_append (NULL, g_strdup (PLUG_IN_PROC));
|
||||
}
|
||||
|
||||
static PikaProcedure *
|
||||
wavelet_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name)
|
||||
{
|
||||
PikaProcedure *procedure = NULL;
|
||||
|
||||
if (! strcmp (name, PLUG_IN_PROC))
|
||||
{
|
||||
procedure = pika_image_procedure_new (plug_in, name,
|
||||
PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
wavelet_run, NULL, NULL);
|
||||
|
||||
pika_procedure_set_image_types (procedure, "RGB*, GRAY*");
|
||||
pika_procedure_set_sensitivity_mask (procedure,
|
||||
PIKA_PROCEDURE_SENSITIVE_DRAWABLE);
|
||||
|
||||
pika_procedure_set_menu_label (procedure, _("_Wavelet-decompose..."));
|
||||
pika_procedure_add_menu_path (procedure, "<Image>/Filters/Enhance");
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
_("Wavelet decompose"),
|
||||
"Compute and render wavelet scales",
|
||||
name);
|
||||
pika_procedure_set_attribution (procedure,
|
||||
"Miroslav Talasek <miroslav.talasek@seznam.cz>",
|
||||
"Miroslav Talasek <miroslav.talasek@seznam.cz>",
|
||||
"19 January 2017");
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "scales",
|
||||
_("Scal_es"),
|
||||
_("Number of scales"),
|
||||
1, 7, 5,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_BOOLEAN (procedure, "create-group",
|
||||
_("Create a layer group to store the "
|
||||
"_decomposition"),
|
||||
"Create a layer group to store the "
|
||||
"decomposition",
|
||||
TRUE,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_BOOLEAN (procedure, "create-masks",
|
||||
_("_Add a layer mask to each scales layer"),
|
||||
"Add a layer mask to each scales layer",
|
||||
FALSE,
|
||||
G_PARAM_READWRITE);
|
||||
}
|
||||
|
||||
return procedure;
|
||||
}
|
||||
|
||||
static PikaValueArray *
|
||||
wavelet_run (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data)
|
||||
{
|
||||
PikaProcedureConfig *config;
|
||||
PikaLayer **scale_layers;
|
||||
PikaLayer *new_scale;
|
||||
PikaLayer *parent = NULL;
|
||||
PikaDrawable *drawable;
|
||||
PikaLayerMode grain_extract_mode = PIKA_LAYER_MODE_GRAIN_EXTRACT;
|
||||
PikaLayerMode grain_merge_mode = PIKA_LAYER_MODE_GRAIN_MERGE;
|
||||
gint id;
|
||||
gint scales;
|
||||
gboolean create_group;
|
||||
gboolean create_masks;
|
||||
|
||||
gegl_init (NULL, NULL);
|
||||
|
||||
if (n_drawables != 1)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
g_set_error (&error, PIKA_PLUG_IN_ERROR, 0,
|
||||
_("Procedure '%s' only works with one drawable."),
|
||||
pika_procedure_get_name (procedure));
|
||||
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_CALLING_ERROR,
|
||||
error);
|
||||
}
|
||||
else
|
||||
{
|
||||
drawable = drawables[0];
|
||||
}
|
||||
|
||||
config = pika_procedure_create_config (procedure);
|
||||
pika_procedure_config_begin_run (config, NULL, run_mode, args);
|
||||
|
||||
switch (run_mode)
|
||||
{
|
||||
case PIKA_RUN_INTERACTIVE:
|
||||
if (! wavelet_decompose_dialog (procedure, G_OBJECT (config)))
|
||||
{
|
||||
pika_procedure_config_end_run (config, PIKA_PDB_CANCEL);
|
||||
g_object_unref (config);
|
||||
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_CANCEL,
|
||||
NULL);
|
||||
}
|
||||
break;
|
||||
|
||||
case PIKA_RUN_NONINTERACTIVE:
|
||||
case PIKA_RUN_WITH_LAST_VALS:
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
g_object_get (config,
|
||||
"scales", &scales,
|
||||
"create-group", &create_group,
|
||||
"create-masks", &create_masks,
|
||||
NULL);
|
||||
|
||||
pika_progress_init (_("Wavelet-Decompose"));
|
||||
|
||||
pika_image_undo_group_start (image);
|
||||
|
||||
pika_image_freeze_layers (image);
|
||||
|
||||
if (create_group)
|
||||
{
|
||||
parent = pika_layer_group_new (image);
|
||||
|
||||
pika_item_set_name (PIKA_ITEM (parent), _("Decomposition"));
|
||||
pika_item_set_visible (PIKA_ITEM (parent), FALSE);
|
||||
pika_image_insert_layer (image, parent,
|
||||
PIKA_LAYER (pika_item_get_parent (PIKA_ITEM (drawable))),
|
||||
pika_image_get_item_position (image,
|
||||
PIKA_ITEM (drawable)));
|
||||
}
|
||||
|
||||
scale_layers = g_new (PikaLayer *, scales);
|
||||
new_scale = pika_layer_copy (PIKA_LAYER (drawable));
|
||||
pika_image_insert_layer (image, new_scale, parent,
|
||||
pika_image_get_item_position (image,
|
||||
PIKA_ITEM (drawable)));
|
||||
|
||||
/* the exact result of the grain-extract and grain-merge modes
|
||||
* depends on the choice of (gamma-corrected) midpoint intensity
|
||||
* value. for the non-legacy modes, the midpoint value is 0.5,
|
||||
* which isn't representable exactly using integer precision. for
|
||||
* the legacy modes, the midpoint value is 128/255 (i.e., 0x80),
|
||||
* which is representable exactly using (gamma-corrected) integer
|
||||
* precision. we therefore use the legacy modes when the input
|
||||
* image precision is integer, and only use the (preferable)
|
||||
* non-legacy modes when the input image precision is floating
|
||||
* point.
|
||||
*
|
||||
* this avoids imperfect reconstruction of the image when using
|
||||
* integer precision. see bug #786844.
|
||||
*/
|
||||
switch (pika_image_get_precision (image))
|
||||
{
|
||||
case PIKA_PRECISION_U8_LINEAR:
|
||||
case PIKA_PRECISION_U8_NON_LINEAR:
|
||||
case PIKA_PRECISION_U8_PERCEPTUAL:
|
||||
case PIKA_PRECISION_U16_LINEAR:
|
||||
case PIKA_PRECISION_U16_NON_LINEAR:
|
||||
case PIKA_PRECISION_U16_PERCEPTUAL:
|
||||
case PIKA_PRECISION_U32_LINEAR:
|
||||
case PIKA_PRECISION_U32_NON_LINEAR:
|
||||
case PIKA_PRECISION_U32_PERCEPTUAL:
|
||||
grain_extract_mode = PIKA_LAYER_MODE_GRAIN_EXTRACT_LEGACY;
|
||||
grain_merge_mode = PIKA_LAYER_MODE_GRAIN_MERGE_LEGACY;
|
||||
break;
|
||||
|
||||
case PIKA_PRECISION_HALF_LINEAR:
|
||||
case PIKA_PRECISION_HALF_NON_LINEAR:
|
||||
case PIKA_PRECISION_HALF_PERCEPTUAL:
|
||||
case PIKA_PRECISION_FLOAT_LINEAR:
|
||||
case PIKA_PRECISION_FLOAT_NON_LINEAR:
|
||||
case PIKA_PRECISION_FLOAT_PERCEPTUAL:
|
||||
case PIKA_PRECISION_DOUBLE_LINEAR:
|
||||
case PIKA_PRECISION_DOUBLE_NON_LINEAR:
|
||||
case PIKA_PRECISION_DOUBLE_PERCEPTUAL:
|
||||
grain_extract_mode = PIKA_LAYER_MODE_GRAIN_EXTRACT;
|
||||
grain_merge_mode = PIKA_LAYER_MODE_GRAIN_MERGE;
|
||||
break;
|
||||
}
|
||||
|
||||
for (id = 0 ; id < scales; id++)
|
||||
{
|
||||
PikaLayer *blur;
|
||||
PikaLayer *tmp;
|
||||
gchar scale_name[20];
|
||||
|
||||
pika_progress_update ((gdouble) id / (gdouble) scales);
|
||||
|
||||
scale_layers[id] = new_scale;
|
||||
|
||||
g_snprintf (scale_name, sizeof (scale_name), _("Scale %d"), id + 1);
|
||||
pika_item_set_name (PIKA_ITEM (new_scale), scale_name);
|
||||
|
||||
tmp = pika_layer_copy (new_scale);
|
||||
pika_image_insert_layer (image, tmp, parent,
|
||||
pika_image_get_item_position (image,
|
||||
PIKA_ITEM (new_scale)));
|
||||
wavelet_blur (PIKA_DRAWABLE (tmp), pow (2.0, id));
|
||||
|
||||
blur = pika_layer_copy (tmp);
|
||||
pika_image_insert_layer (image, blur, parent,
|
||||
pika_image_get_item_position (image,
|
||||
PIKA_ITEM (tmp)));
|
||||
|
||||
pika_layer_set_mode (tmp, grain_extract_mode);
|
||||
new_scale = pika_image_merge_down (image, tmp,
|
||||
PIKA_EXPAND_AS_NECESSARY);
|
||||
scale_layers[id] = new_scale;
|
||||
|
||||
pika_item_set_visible (PIKA_ITEM (new_scale), FALSE);
|
||||
|
||||
new_scale = blur;
|
||||
}
|
||||
|
||||
pika_item_set_name (PIKA_ITEM (new_scale), _("Residual"));
|
||||
|
||||
for (id = 0; id < scales; id++)
|
||||
{
|
||||
pika_image_reorder_item (image, PIKA_ITEM (scale_layers[id]),
|
||||
PIKA_ITEM (parent),
|
||||
pika_image_get_item_position (image,
|
||||
PIKA_ITEM (new_scale)));
|
||||
pika_layer_set_mode (scale_layers[id], grain_merge_mode);
|
||||
|
||||
if (create_masks)
|
||||
{
|
||||
PikaLayerMask *mask = pika_layer_create_mask (scale_layers[id],
|
||||
PIKA_ADD_MASK_WHITE);
|
||||
pika_layer_add_mask (scale_layers[id], mask);
|
||||
}
|
||||
|
||||
pika_item_set_visible (PIKA_ITEM (scale_layers[id]), TRUE);
|
||||
}
|
||||
|
||||
if (create_group)
|
||||
pika_item_set_visible (PIKA_ITEM (parent), TRUE);
|
||||
|
||||
g_free (scale_layers);
|
||||
|
||||
pika_image_thaw_layers (image);
|
||||
|
||||
pika_image_undo_group_end (image);
|
||||
|
||||
pika_progress_update (1.0);
|
||||
|
||||
pika_displays_flush ();
|
||||
|
||||
pika_procedure_config_end_run (config, PIKA_PDB_SUCCESS);
|
||||
g_object_unref (config);
|
||||
|
||||
gegl_exit ();
|
||||
|
||||
return pika_procedure_new_return_values (procedure, PIKA_PDB_SUCCESS, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
wavelet_blur (PikaDrawable *drawable,
|
||||
gint radius)
|
||||
{
|
||||
gint x, y, width, height;
|
||||
|
||||
if (pika_drawable_mask_intersect (drawable, &x, &y, &width, &height))
|
||||
{
|
||||
GeglBuffer *buffer = pika_drawable_get_buffer (drawable);
|
||||
GeglBuffer *shadow = pika_drawable_get_shadow_buffer (drawable);
|
||||
|
||||
gegl_render_op (buffer, shadow,
|
||||
"gegl:wavelet-blur",
|
||||
"radius", (gdouble) radius,
|
||||
NULL);
|
||||
|
||||
gegl_buffer_flush (shadow);
|
||||
pika_drawable_merge_shadow (drawable, FALSE);
|
||||
pika_drawable_update (drawable, x, y, width, height);
|
||||
g_object_unref (buffer);
|
||||
g_object_unref (shadow);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
wavelet_decompose_dialog (PikaProcedure *procedure,
|
||||
GObject *config)
|
||||
{
|
||||
GtkWidget *dialog;
|
||||
gboolean run;
|
||||
|
||||
pika_ui_init (PLUG_IN_BINARY);
|
||||
|
||||
dialog = pika_procedure_dialog_new (procedure,
|
||||
PIKA_PROCEDURE_CONFIG (config),
|
||||
_("Wavelet decompose"));
|
||||
|
||||
pika_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
|
||||
GTK_RESPONSE_OK,
|
||||
GTK_RESPONSE_CANCEL,
|
||||
-1);
|
||||
|
||||
pika_window_set_transient (GTK_WINDOW (dialog));
|
||||
|
||||
/* scales */
|
||||
pika_procedure_dialog_get_scale_entry (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
"scales", 1.0);
|
||||
|
||||
pika_procedure_dialog_fill (PIKA_PROCEDURE_DIALOG (dialog),
|
||||
NULL);
|
||||
|
||||
gtk_widget_show (dialog);
|
||||
|
||||
run = pika_procedure_dialog_run (PIKA_PROCEDURE_DIALOG (dialog));
|
||||
|
||||
gtk_widget_destroy (dialog);
|
||||
|
||||
return run;
|
||||
}
|
||||
251
plug-ins/common/web-browser.c
Normal file
251
plug-ins/common/web-browser.c
Normal file
@ -0,0 +1,251 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* Web Browser Plug-in
|
||||
* Copyright (C) 2003 Henrik Brix Andersen <brix@gimp.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h> /* strlen, strstr */
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#ifdef PLATFORM_OSX
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#endif
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include <libpika/pika.h>
|
||||
#include <libpika/pikaui.h>
|
||||
|
||||
#include "libpika/stdplugins-intl.h"
|
||||
|
||||
|
||||
#define PLUG_IN_PROC "plug-in-web-browser"
|
||||
#define PLUG_IN_BINARY "web-browser"
|
||||
#define PLUG_IN_ROLE "pika-web-browser"
|
||||
|
||||
|
||||
typedef struct _Browser Browser;
|
||||
typedef struct _BrowserClass BrowserClass;
|
||||
|
||||
struct _Browser
|
||||
{
|
||||
PikaPlugIn parent_instance;
|
||||
};
|
||||
|
||||
struct _BrowserClass
|
||||
{
|
||||
PikaPlugInClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
#define BROWSER_TYPE (browser_get_type ())
|
||||
#define BROWSER (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), BROWSER_TYPE, Browser))
|
||||
|
||||
GType browser_get_type (void) G_GNUC_CONST;
|
||||
|
||||
static GList * browser_query_procedures (PikaPlugIn *plug_in);
|
||||
static PikaProcedure * browser_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name);
|
||||
|
||||
static PikaValueArray * browser_run (PikaProcedure *procedure,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data);
|
||||
|
||||
static gboolean browser_open_url (GtkWindow *window,
|
||||
const gchar *url,
|
||||
GError **error);
|
||||
|
||||
|
||||
G_DEFINE_TYPE (Browser, browser, PIKA_TYPE_PLUG_IN)
|
||||
|
||||
PIKA_MAIN (BROWSER_TYPE)
|
||||
DEFINE_STD_SET_I18N
|
||||
|
||||
|
||||
static void
|
||||
browser_class_init (BrowserClass *klass)
|
||||
{
|
||||
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
|
||||
|
||||
plug_in_class->query_procedures = browser_query_procedures;
|
||||
plug_in_class->create_procedure = browser_create_procedure;
|
||||
plug_in_class->set_i18n = STD_SET_I18N;
|
||||
}
|
||||
|
||||
static void
|
||||
browser_init (Browser *browser)
|
||||
{
|
||||
}
|
||||
|
||||
static GList *
|
||||
browser_query_procedures (PikaPlugIn *plug_in)
|
||||
{
|
||||
return g_list_append (NULL, g_strdup (PLUG_IN_PROC));
|
||||
}
|
||||
|
||||
static PikaProcedure *
|
||||
browser_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name)
|
||||
{
|
||||
PikaProcedure *procedure = NULL;
|
||||
|
||||
if (! strcmp (name, PLUG_IN_PROC))
|
||||
{
|
||||
procedure = pika_procedure_new (plug_in, name,
|
||||
PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
browser_run, NULL, NULL);
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
"Open an URL in the user specified "
|
||||
"web browser",
|
||||
"Opens the given URL in the user "
|
||||
"specified web browser.",
|
||||
name);
|
||||
pika_procedure_set_attribution (procedure,
|
||||
"Henrik Brix Andersen <brix@gimp.org>",
|
||||
"2003",
|
||||
"2003/09/16");
|
||||
|
||||
PIKA_PROC_ARG_STRING (procedure, "url",
|
||||
"URL",
|
||||
"URL to open",
|
||||
"https://heckin.technology/AlderconeStudio/PIKApp/",
|
||||
G_PARAM_READWRITE);
|
||||
}
|
||||
|
||||
return procedure;
|
||||
}
|
||||
|
||||
static PikaValueArray *
|
||||
browser_run (PikaProcedure *procedure,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
if (! browser_open_url (NULL, PIKA_VALUES_GET_STRING (args, 0),
|
||||
&error))
|
||||
{
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_EXECUTION_ERROR,
|
||||
error);
|
||||
}
|
||||
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_SUCCESS,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
browser_open_url (GtkWindow *window,
|
||||
const gchar *url,
|
||||
GError **error)
|
||||
{
|
||||
#ifdef G_OS_WIN32
|
||||
|
||||
HINSTANCE hinst = ShellExecute (GetDesktopWindow(),
|
||||
"open", url, NULL, NULL, SW_SHOW);
|
||||
|
||||
if ((intptr_t) hinst <= 32)
|
||||
{
|
||||
const gchar *err;
|
||||
|
||||
switch ((intptr_t) hinst)
|
||||
{
|
||||
case 0 :
|
||||
err = _("The operating system is out of memory or resources.");
|
||||
break;
|
||||
case ERROR_FILE_NOT_FOUND :
|
||||
err = _("The specified file was not found.");
|
||||
break;
|
||||
case ERROR_PATH_NOT_FOUND :
|
||||
err = _("The specified path was not found.");
|
||||
break;
|
||||
case ERROR_BAD_FORMAT :
|
||||
err = _("The .exe file is invalid (non-Microsoft Win32 .exe or error in .exe image).");
|
||||
break;
|
||||
case SE_ERR_ACCESSDENIED :
|
||||
err = _("The operating system denied access to the specified file.");
|
||||
break;
|
||||
case SE_ERR_ASSOCINCOMPLETE :
|
||||
err = _("The file name association is incomplete or invalid.");
|
||||
break;
|
||||
case SE_ERR_DDEBUSY :
|
||||
err = _("DDE transaction busy");
|
||||
break;
|
||||
case SE_ERR_DDEFAIL :
|
||||
err = _("The DDE transaction failed.");
|
||||
break;
|
||||
case SE_ERR_DDETIMEOUT :
|
||||
err = _("The DDE transaction timed out.");
|
||||
break;
|
||||
case SE_ERR_DLLNOTFOUND :
|
||||
err = _("The specified DLL was not found.");
|
||||
break;
|
||||
case SE_ERR_NOASSOC :
|
||||
err = _("There is no application associated with the given file name extension.");
|
||||
break;
|
||||
case SE_ERR_OOM :
|
||||
err = _("There was not enough memory to complete the operation.");
|
||||
break;
|
||||
case SE_ERR_SHARE:
|
||||
err = _("A sharing violation occurred.");
|
||||
break;
|
||||
default :
|
||||
err = _("Unknown Microsoft Windows error.");
|
||||
}
|
||||
|
||||
g_set_error (error, 0, 0, _("Failed to open '%s': %s"), url, err);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
|
||||
#elif defined(PLATFORM_OSX)
|
||||
|
||||
NSURL *ns_url;
|
||||
gboolean retval;
|
||||
|
||||
@autoreleasepool
|
||||
{
|
||||
ns_url = [NSURL URLWithString: [NSString stringWithUTF8String: url]];
|
||||
retval = [[NSWorkspace sharedWorkspace] openURL: ns_url];
|
||||
}
|
||||
|
||||
return retval;
|
||||
|
||||
#else
|
||||
|
||||
pika_ui_init (PLUG_IN_BINARY);
|
||||
|
||||
return gtk_show_uri_on_window (window,
|
||||
url,
|
||||
GDK_CURRENT_TIME,
|
||||
error);
|
||||
|
||||
#endif
|
||||
}
|
||||
604
plug-ins/common/web-page.c
Normal file
604
plug-ins/common/web-page.c
Normal file
@ -0,0 +1,604 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* Webpage plug-in.
|
||||
* Copyright (C) 2011 Mukund Sivaraman <muks@banu.com>.
|
||||
* Portions are copyright of the author of the
|
||||
* file-open-location-dialog.c code.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <libpika/pika.h>
|
||||
#include <libpika/pikaui.h>
|
||||
|
||||
#include <webkit2/webkit2.h>
|
||||
|
||||
#include "libpika/stdplugins-intl.h"
|
||||
|
||||
|
||||
#define PLUG_IN_PROC "plug-in-web-page"
|
||||
#define PLUG_IN_BINARY "web-page"
|
||||
#define PLUG_IN_ROLE "pika-web-page"
|
||||
#define MAX_URL_LEN 2048
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char *url;
|
||||
gint32 width;
|
||||
gint font_size;
|
||||
PikaImage *image;
|
||||
GError *error;
|
||||
} WebpageVals;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char url[MAX_URL_LEN];
|
||||
gint32 width;
|
||||
gint font_size;
|
||||
} WebpageSaveVals;
|
||||
|
||||
|
||||
typedef struct _Webpage Webpage;
|
||||
typedef struct _WebpageClass WebpageClass;
|
||||
|
||||
struct _Webpage
|
||||
{
|
||||
PikaPlugIn parent_instance;
|
||||
};
|
||||
|
||||
struct _WebpageClass
|
||||
{
|
||||
PikaPlugInClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
#define WEBPAGE_TYPE (webpage_get_type ())
|
||||
#define WEBPAGE (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), WEBPAGE_TYPE, Webpage))
|
||||
|
||||
GType webpage_get_type (void) G_GNUC_CONST;
|
||||
|
||||
static GList * webpage_query_procedures (PikaPlugIn *plug_in);
|
||||
static PikaProcedure * webpage_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name);
|
||||
|
||||
static PikaValueArray * webpage_run (PikaProcedure *procedure,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data);
|
||||
|
||||
static gboolean webpage_dialog (void);
|
||||
static PikaImage * webpage_capture (void);
|
||||
|
||||
|
||||
G_DEFINE_TYPE (Webpage, webpage, PIKA_TYPE_PLUG_IN)
|
||||
|
||||
PIKA_MAIN (WEBPAGE_TYPE)
|
||||
DEFINE_STD_SET_I18N
|
||||
|
||||
|
||||
static WebpageVals webpagevals;
|
||||
|
||||
|
||||
static void
|
||||
webpage_class_init (WebpageClass *klass)
|
||||
{
|
||||
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
|
||||
|
||||
plug_in_class->query_procedures = webpage_query_procedures;
|
||||
plug_in_class->create_procedure = webpage_create_procedure;
|
||||
plug_in_class->set_i18n = STD_SET_I18N;
|
||||
}
|
||||
|
||||
static void
|
||||
webpage_init (Webpage *webpage)
|
||||
{
|
||||
}
|
||||
|
||||
static GList *
|
||||
webpage_query_procedures (PikaPlugIn *plug_in)
|
||||
{
|
||||
return g_list_append (NULL, g_strdup (PLUG_IN_PROC));
|
||||
}
|
||||
|
||||
static PikaProcedure *
|
||||
webpage_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name)
|
||||
{
|
||||
PikaProcedure *procedure = NULL;
|
||||
|
||||
if (! strcmp (name, PLUG_IN_PROC))
|
||||
{
|
||||
procedure = pika_procedure_new (plug_in, name,
|
||||
PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
webpage_run, NULL, NULL);
|
||||
|
||||
pika_procedure_set_menu_label (procedure, _("From _Webpage..."));
|
||||
pika_procedure_add_menu_path (procedure, "<Image>/File/Create");
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
_("Create an image of a webpage"),
|
||||
"The plug-in allows you to take a "
|
||||
"screenshot of a webpage.",
|
||||
name);
|
||||
pika_procedure_set_attribution (procedure,
|
||||
"Mukund Sivaraman <muks@banu.com>",
|
||||
"2011",
|
||||
"2011");
|
||||
|
||||
PIKA_PROC_ARG_ENUM (procedure, "run-mode",
|
||||
"Run mode",
|
||||
"The run mode",
|
||||
PIKA_TYPE_RUN_MODE,
|
||||
PIKA_RUN_INTERACTIVE,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_STRING (procedure, "url",
|
||||
"URL",
|
||||
"URL of the webpage to screenshot",
|
||||
"https://heckin.technology/AlderconeStudio/PIKApp/",
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "width",
|
||||
"Width",
|
||||
"The width of the screenshot (in pixels)",
|
||||
100, PIKA_MAX_IMAGE_SIZE, 1024,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "font-size",
|
||||
"Font size",
|
||||
"The font size to use in the page (in pt)",
|
||||
1, 1000, 12,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_VAL_IMAGE (procedure, "image",
|
||||
"Image",
|
||||
"The output image",
|
||||
FALSE,
|
||||
G_PARAM_READWRITE);
|
||||
}
|
||||
|
||||
return procedure;
|
||||
}
|
||||
|
||||
static PikaValueArray *
|
||||
webpage_run (PikaProcedure *procedure,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data)
|
||||
{
|
||||
PikaValueArray *return_vals;
|
||||
PikaRunMode run_mode;
|
||||
PikaImage *image;
|
||||
WebpageSaveVals save = { "https://heckin.technology/AlderconeStudio/PIKApp/", 1024, 12 };
|
||||
|
||||
pika_get_data (PLUG_IN_PROC, &save);
|
||||
|
||||
run_mode = PIKA_VALUES_GET_ENUM (args, 0);
|
||||
|
||||
webpagevals.url = g_strdup (save.url);
|
||||
webpagevals.width = save.width;
|
||||
webpagevals.font_size = save.font_size;
|
||||
|
||||
/* how are we running today? */
|
||||
switch (run_mode)
|
||||
{
|
||||
case PIKA_RUN_INTERACTIVE:
|
||||
if (! webpage_dialog ())
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_CANCEL,
|
||||
NULL);
|
||||
break;
|
||||
|
||||
case PIKA_RUN_WITH_LAST_VALS:
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_CALLING_ERROR,
|
||||
NULL);
|
||||
break;
|
||||
|
||||
case PIKA_RUN_NONINTERACTIVE:
|
||||
webpagevals.url = (gchar *) PIKA_VALUES_GET_STRING (args, 1);
|
||||
webpagevals.width = PIKA_VALUES_GET_INT (args, 2);
|
||||
webpagevals.font_size = PIKA_VALUES_GET_INT (args, 3);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
image = webpage_capture ();
|
||||
|
||||
if (! image)
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_EXECUTION_ERROR,
|
||||
webpagevals.error);
|
||||
|
||||
save.width = webpagevals.width;
|
||||
save.font_size = webpagevals.font_size;
|
||||
|
||||
if (strlen (webpagevals.url) < MAX_URL_LEN)
|
||||
{
|
||||
g_strlcpy (save.url, webpagevals.url, MAX_URL_LEN);
|
||||
}
|
||||
else
|
||||
{
|
||||
memset (save.url, 0, MAX_URL_LEN);
|
||||
}
|
||||
|
||||
pika_set_data (PLUG_IN_PROC, &save, sizeof save);
|
||||
|
||||
if (run_mode == PIKA_RUN_INTERACTIVE)
|
||||
pika_display_new (image);
|
||||
|
||||
return_vals = pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_SUCCESS,
|
||||
NULL);
|
||||
|
||||
PIKA_VALUES_SET_IMAGE (return_vals, 1, image);
|
||||
|
||||
return return_vals;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
webpage_dialog (void)
|
||||
{
|
||||
GtkWidget *dialog;
|
||||
GtkWidget *hbox;
|
||||
GtkWidget *vbox;
|
||||
GtkWidget *image;
|
||||
GtkWidget *label;
|
||||
GtkWidget *entry;
|
||||
GtkSizeGroup *sizegroup;
|
||||
GtkAdjustment *adjustment;
|
||||
GtkWidget *spinbutton;
|
||||
GtkWidget *combo;
|
||||
gint active;
|
||||
gint status;
|
||||
gboolean ret = FALSE;
|
||||
|
||||
pika_ui_init (PLUG_IN_BINARY);
|
||||
|
||||
dialog = pika_dialog_new (_("Create from webpage"), PLUG_IN_ROLE,
|
||||
NULL, 0,
|
||||
pika_standard_help_func, PLUG_IN_PROC,
|
||||
|
||||
_("_Cancel"), GTK_RESPONSE_CANCEL,
|
||||
_("Cre_ate"), GTK_RESPONSE_OK,
|
||||
|
||||
NULL);
|
||||
|
||||
pika_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
|
||||
GTK_RESPONSE_OK,
|
||||
GTK_RESPONSE_CANCEL,
|
||||
-1);
|
||||
pika_window_set_transient (GTK_WINDOW (dialog));
|
||||
|
||||
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
|
||||
gtk_container_set_border_width (GTK_CONTAINER (hbox), 12);
|
||||
gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
|
||||
hbox, FALSE, FALSE, 0);
|
||||
gtk_widget_show (hbox);
|
||||
|
||||
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
|
||||
gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
|
||||
gtk_widget_show (vbox);
|
||||
|
||||
image = gtk_image_new_from_icon_name (PIKA_ICON_WEB,
|
||||
GTK_ICON_SIZE_BUTTON);
|
||||
gtk_box_pack_start (GTK_BOX (vbox), image, FALSE, FALSE, 0);
|
||||
gtk_widget_show (image);
|
||||
|
||||
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
|
||||
gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
|
||||
gtk_widget_show (vbox);
|
||||
|
||||
label = gtk_label_new (_("Enter location (URI):"));
|
||||
gtk_label_set_xalign (GTK_LABEL (label), 0.0);
|
||||
gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
|
||||
gtk_widget_show (label);
|
||||
|
||||
entry = gtk_entry_new ();
|
||||
gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
|
||||
gtk_widget_set_size_request (entry, 400, -1);
|
||||
gtk_box_pack_start (GTK_BOX (vbox), entry, FALSE, FALSE, 0);
|
||||
|
||||
if (webpagevals.url)
|
||||
gtk_entry_set_text (GTK_ENTRY (entry),
|
||||
webpagevals.url);
|
||||
gtk_widget_show (entry);
|
||||
|
||||
sizegroup = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
|
||||
|
||||
/* Width */
|
||||
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
|
||||
gtk_box_pack_start (GTK_BOX (vbox),
|
||||
hbox, FALSE, FALSE, 0);
|
||||
gtk_widget_show (hbox);
|
||||
|
||||
label = gtk_label_new (_("Width (pixels):"));
|
||||
gtk_size_group_add_widget (sizegroup, label);
|
||||
|
||||
gtk_label_set_xalign (GTK_LABEL (label), 0.0);
|
||||
gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
|
||||
gtk_widget_show (label);
|
||||
|
||||
adjustment = gtk_adjustment_new (webpagevals.width,
|
||||
1, 8192, 1, 10, 0);
|
||||
spinbutton = pika_spin_button_new (adjustment, 1.0, 0);
|
||||
gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
|
||||
gtk_box_pack_start (GTK_BOX (hbox), spinbutton, FALSE, FALSE, 0);
|
||||
gtk_widget_show (spinbutton);
|
||||
|
||||
/* Font size */
|
||||
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
|
||||
gtk_box_pack_start (GTK_BOX (vbox),
|
||||
hbox, FALSE, FALSE, 0);
|
||||
gtk_widget_show (hbox);
|
||||
|
||||
label = gtk_label_new (_("Font size:"));
|
||||
gtk_size_group_add_widget (sizegroup, label);
|
||||
|
||||
gtk_label_set_xalign (GTK_LABEL (label), 0.0);
|
||||
gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
|
||||
gtk_widget_show (label);
|
||||
|
||||
combo = pika_int_combo_box_new (_("Huge"), 16,
|
||||
_("Large"), 14,
|
||||
C_("web-page", "Default"), 12,
|
||||
_("Small"), 10,
|
||||
_("Tiny"), 8,
|
||||
NULL);
|
||||
|
||||
switch (webpagevals.font_size)
|
||||
{
|
||||
case 16:
|
||||
case 14:
|
||||
case 12:
|
||||
case 10:
|
||||
case 8:
|
||||
active = webpagevals.font_size;
|
||||
break;
|
||||
default:
|
||||
active = 12;
|
||||
}
|
||||
|
||||
pika_int_combo_box_set_active (PIKA_INT_COMBO_BOX (combo), active);
|
||||
|
||||
gtk_box_pack_start (GTK_BOX (hbox), combo, FALSE, FALSE, 0);
|
||||
gtk_widget_show (combo);
|
||||
|
||||
g_object_unref (sizegroup);
|
||||
|
||||
status = pika_dialog_run (PIKA_DIALOG (dialog));
|
||||
if (status == GTK_RESPONSE_OK)
|
||||
{
|
||||
g_free (webpagevals.url);
|
||||
webpagevals.url = g_strdup (gtk_entry_get_text (GTK_ENTRY (entry)));
|
||||
|
||||
webpagevals.width = (gint) gtk_adjustment_get_value (adjustment);
|
||||
|
||||
pika_int_combo_box_get_active (PIKA_INT_COMBO_BOX (combo),
|
||||
&webpagevals.font_size);
|
||||
|
||||
ret = TRUE;
|
||||
}
|
||||
|
||||
gtk_widget_destroy (dialog);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
notify_progress_cb (WebKitWebView *view,
|
||||
GParamSpec *pspec,
|
||||
gpointer user_data)
|
||||
{
|
||||
static gdouble old_progress = 0.0;
|
||||
gdouble progress = webkit_web_view_get_estimated_load_progress (view);
|
||||
|
||||
if ((progress - old_progress) > 0.01)
|
||||
{
|
||||
pika_progress_update (progress);
|
||||
old_progress = progress;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
load_failed_cb (WebKitWebView *view,
|
||||
WebKitLoadEvent event,
|
||||
gchar *uri,
|
||||
gpointer web_error,
|
||||
gpointer user_data)
|
||||
{
|
||||
webpagevals.error = g_error_copy ((GError *) web_error);
|
||||
|
||||
gtk_main_quit ();
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
snapshot_ready (GObject *source_object,
|
||||
GAsyncResult *result,
|
||||
gpointer user_data)
|
||||
{
|
||||
WebKitWebView *view = WEBKIT_WEB_VIEW (source_object);
|
||||
cairo_surface_t *surface;
|
||||
|
||||
surface = webkit_web_view_get_snapshot_finish (view, result,
|
||||
&webpagevals.error);
|
||||
|
||||
if (surface)
|
||||
{
|
||||
gint width;
|
||||
gint height;
|
||||
PikaLayer *layer;
|
||||
|
||||
width = cairo_image_surface_get_width (surface);
|
||||
height = cairo_image_surface_get_height (surface);
|
||||
|
||||
webpagevals.image = pika_image_new (width, height, PIKA_RGB);
|
||||
|
||||
pika_image_undo_disable (webpagevals.image);
|
||||
layer = pika_layer_new_from_surface (webpagevals.image, _("Webpage"),
|
||||
surface,
|
||||
0.25, 1.0);
|
||||
pika_image_insert_layer (webpagevals.image, layer, NULL, 0);
|
||||
pika_image_undo_enable (webpagevals.image);
|
||||
|
||||
cairo_surface_destroy (surface);
|
||||
}
|
||||
|
||||
pika_progress_update (1.0);
|
||||
|
||||
gtk_main_quit ();
|
||||
}
|
||||
|
||||
static gboolean
|
||||
load_finished_idle (gpointer data)
|
||||
{
|
||||
static gint count = 0;
|
||||
|
||||
pika_progress_update ((gdouble) count * 0.025);
|
||||
|
||||
count++;
|
||||
|
||||
if (count < 10)
|
||||
return G_SOURCE_CONTINUE;
|
||||
|
||||
webkit_web_view_get_snapshot (WEBKIT_WEB_VIEW (data),
|
||||
WEBKIT_SNAPSHOT_REGION_FULL_DOCUMENT,
|
||||
WEBKIT_SNAPSHOT_OPTIONS_NONE,
|
||||
NULL,
|
||||
snapshot_ready,
|
||||
NULL);
|
||||
|
||||
count = 0;
|
||||
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static void
|
||||
load_changed_cb (WebKitWebView *view,
|
||||
WebKitLoadEvent event,
|
||||
gpointer user_data)
|
||||
{
|
||||
if (event == WEBKIT_LOAD_FINISHED)
|
||||
{
|
||||
if (! webpagevals.error)
|
||||
{
|
||||
pika_progress_init_printf (_("Transferring webpage image for '%s'"),
|
||||
webpagevals.url);
|
||||
|
||||
g_timeout_add (100, load_finished_idle, view);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
gtk_main_quit ();
|
||||
}
|
||||
}
|
||||
|
||||
static PikaImage *
|
||||
webpage_capture (void)
|
||||
{
|
||||
gchar *scheme;
|
||||
GtkWidget *window;
|
||||
GtkWidget *view;
|
||||
WebKitSettings *settings;
|
||||
char *ua;
|
||||
|
||||
if (! webpagevals.url || strlen (webpagevals.url) == 0)
|
||||
{
|
||||
g_set_error (&webpagevals.error, 0, 0, _("No URL was specified"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
scheme = g_uri_parse_scheme (webpagevals.url);
|
||||
if (! scheme)
|
||||
{
|
||||
char *url;
|
||||
|
||||
/* If we were not given a well-formed URL, make one. */
|
||||
|
||||
url = g_strconcat ("http://", webpagevals.url, NULL);
|
||||
g_free (webpagevals.url);
|
||||
webpagevals.url = url;
|
||||
|
||||
g_free (scheme);
|
||||
}
|
||||
|
||||
if (webpagevals.width < 32)
|
||||
{
|
||||
g_warning ("Width '%d' is too small. Clamped to 32.",
|
||||
webpagevals.width);
|
||||
webpagevals.width = 32;
|
||||
}
|
||||
else if (webpagevals.width > 8192)
|
||||
{
|
||||
g_warning ("Width '%d' is too large. Clamped to 8192.",
|
||||
webpagevals.width);
|
||||
webpagevals.width = 8192;
|
||||
}
|
||||
|
||||
window = gtk_offscreen_window_new ();
|
||||
gtk_widget_show (window);
|
||||
|
||||
view = webkit_web_view_new ();
|
||||
gtk_widget_show (view);
|
||||
|
||||
gtk_widget_set_vexpand (view, TRUE);
|
||||
gtk_widget_set_size_request (view, webpagevals.width, -1);
|
||||
gtk_container_add (GTK_CONTAINER (window), view);
|
||||
|
||||
/* Append "PIKA/<PIKA_VERSION>" to the user agent string */
|
||||
settings = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (view));
|
||||
ua = g_strdup_printf ("%s PIKA/%s",
|
||||
webkit_settings_get_user_agent (settings),
|
||||
PIKA_VERSION);
|
||||
webkit_settings_set_user_agent (settings, ua);
|
||||
g_free (ua);
|
||||
|
||||
/* Set font size */
|
||||
webkit_settings_set_default_font_size (settings, webpagevals.font_size);
|
||||
|
||||
g_signal_connect (view, "notify::estimated-load-progress",
|
||||
G_CALLBACK (notify_progress_cb),
|
||||
window);
|
||||
g_signal_connect (view, "load-failed",
|
||||
G_CALLBACK (load_failed_cb),
|
||||
window);
|
||||
g_signal_connect (view, "load-changed",
|
||||
G_CALLBACK (load_changed_cb),
|
||||
window);
|
||||
|
||||
pika_progress_init_printf (_("Downloading webpage '%s'"), webpagevals.url);
|
||||
|
||||
webkit_web_view_load_uri (WEBKIT_WEB_VIEW (view),
|
||||
webpagevals.url);
|
||||
|
||||
gtk_main ();
|
||||
|
||||
gtk_widget_destroy (window);
|
||||
|
||||
pika_progress_update (1.0);
|
||||
|
||||
return webpagevals.image;
|
||||
}
|
||||
1023
plug-ins/file-bmp/bmp-load.c
Normal file
1023
plug-ins/file-bmp/bmp-load.c
Normal file
File diff suppressed because it is too large
Load Diff
27
plug-ins/file-bmp/bmp-load.h
Normal file
27
plug-ins/file-bmp/bmp-load.h
Normal file
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* PIKA - Photo and Image Kooker Application
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __BMP_LOAD_H__
|
||||
#define __BMP_LOAD_H__
|
||||
|
||||
|
||||
PikaImage * load_image (GFile *file,
|
||||
GError **error);
|
||||
|
||||
|
||||
#endif /* __BMP_LOAD_H__ */
|
||||
1078
plug-ins/file-bmp/bmp-save.c
Normal file
1078
plug-ins/file-bmp/bmp-save.c
Normal file
File diff suppressed because it is too large
Load Diff
32
plug-ins/file-bmp/bmp-save.h
Normal file
32
plug-ins/file-bmp/bmp-save.h
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* PIKA - Photo and Image Kooker Application
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __BMP_SAVE_H__
|
||||
#define __BMP_SAVE_H__
|
||||
|
||||
|
||||
PikaPDBStatusType save_image (GFile *file,
|
||||
PikaImage *image,
|
||||
PikaDrawable *drawable,
|
||||
PikaRunMode run_mode,
|
||||
PikaProcedure *procedure,
|
||||
GObject *config,
|
||||
GError **error);
|
||||
|
||||
|
||||
#endif /* __BMP_SAVE_H__ */
|
||||
317
plug-ins/file-bmp/bmp.c
Normal file
317
plug-ins/file-bmp/bmp.c
Normal file
@ -0,0 +1,317 @@
|
||||
/* bmp.c */
|
||||
/* Version 0.52 */
|
||||
/* This is a File input and output filter for the */
|
||||
/* Pika. It loads and saves images in windows(TM) */
|
||||
/* bitmap format. */
|
||||
/* Some Parts that deal with the interaction with */
|
||||
/* PIKA are taken from the GIF plugin by */
|
||||
/* Peter Mattis & Spencer Kimball and from the */
|
||||
/* PCX plugin by Francisco Bustamante. */
|
||||
/* */
|
||||
/* Alexander.Schulz@stud.uni-karlsruhe.de */
|
||||
|
||||
/* Changes: 28.11.1997 Noninteractive operation */
|
||||
/* 16.03.1998 Endian-independent!! */
|
||||
/* 21.03.1998 Little Bug-fix */
|
||||
/* 06.04.1998 Bugfix in Padding */
|
||||
/* 11.04.1998 Arch. cleanup (-Wall) */
|
||||
/* Parses gtkrc */
|
||||
/* 14.04.1998 Another Bug in Padding */
|
||||
/* 28.04.1998 RLE-Encoding rewritten */
|
||||
/* 29.10.1998 Changes by Tor Lillqvist */
|
||||
/* <tml@iki.fi> to support */
|
||||
/* 16 and 32 bit images */
|
||||
/* 28.11.1998 Bug in RLE-read-padding */
|
||||
/* fixed. */
|
||||
/* 19.12.1999 Resolution support added */
|
||||
/* 06.05.2000 Overhaul for 16&24-bit */
|
||||
/* plus better OS/2 code */
|
||||
/* by njl195@zepler.org.uk */
|
||||
/* 29.06.2006 Full support for 16/32 */
|
||||
/* bits bitmaps and support */
|
||||
/* for alpha channel */
|
||||
/* by p.filiciak@zax.pl */
|
||||
|
||||
/*
|
||||
* PIKA - Photo and Image Kooker Application
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
* ----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <libpika/pika.h>
|
||||
#include <libpika/pikaui.h>
|
||||
|
||||
#include "bmp.h"
|
||||
#include "bmp-load.h"
|
||||
#include "bmp-save.h"
|
||||
|
||||
#include "libpika/stdplugins-intl.h"
|
||||
|
||||
|
||||
typedef struct _Bmp Bmp;
|
||||
typedef struct _BmpClass BmpClass;
|
||||
|
||||
struct _Bmp
|
||||
{
|
||||
PikaPlugIn parent_instance;
|
||||
};
|
||||
|
||||
struct _BmpClass
|
||||
{
|
||||
PikaPlugInClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
#define BMP_TYPE (bmp_get_type ())
|
||||
#define BMP (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), BMP_TYPE, Bmp))
|
||||
|
||||
GType bmp_get_type (void) G_GNUC_CONST;
|
||||
|
||||
static GList * bmp_query_procedures (PikaPlugIn *plug_in);
|
||||
static PikaProcedure * bmp_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name);
|
||||
|
||||
static PikaValueArray * bmp_load (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
GFile *file,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data);
|
||||
static PikaValueArray * bmp_save (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
GFile *file,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data);
|
||||
|
||||
|
||||
|
||||
G_DEFINE_TYPE (Bmp, bmp, PIKA_TYPE_PLUG_IN)
|
||||
|
||||
PIKA_MAIN (BMP_TYPE)
|
||||
DEFINE_STD_SET_I18N
|
||||
|
||||
|
||||
static void
|
||||
bmp_class_init (BmpClass *klass)
|
||||
{
|
||||
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
|
||||
|
||||
plug_in_class->query_procedures = bmp_query_procedures;
|
||||
plug_in_class->create_procedure = bmp_create_procedure;
|
||||
plug_in_class->set_i18n = STD_SET_I18N;
|
||||
}
|
||||
|
||||
static void
|
||||
bmp_init (Bmp *bmp)
|
||||
{
|
||||
}
|
||||
|
||||
static GList *
|
||||
bmp_query_procedures (PikaPlugIn *plug_in)
|
||||
{
|
||||
GList *list = NULL;
|
||||
|
||||
list = g_list_append (list, g_strdup (LOAD_PROC));
|
||||
list = g_list_append (list, g_strdup (SAVE_PROC));
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
static PikaProcedure *
|
||||
bmp_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name)
|
||||
{
|
||||
PikaProcedure *procedure = NULL;
|
||||
|
||||
if (! strcmp (name, LOAD_PROC))
|
||||
{
|
||||
procedure = pika_load_procedure_new (plug_in, name,
|
||||
PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
bmp_load, NULL, NULL);
|
||||
|
||||
pika_procedure_set_menu_label (procedure, _("Windows BMP image"));
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
_("Loads files of Windows BMP file format"),
|
||||
_("Loads files of Windows BMP file format"),
|
||||
name);
|
||||
pika_procedure_set_attribution (procedure,
|
||||
"Alexander Schulz",
|
||||
"Alexander Schulz",
|
||||
"1997");
|
||||
|
||||
pika_file_procedure_set_mime_types (PIKA_FILE_PROCEDURE (procedure),
|
||||
"image/bmp");
|
||||
pika_file_procedure_set_extensions (PIKA_FILE_PROCEDURE (procedure),
|
||||
"bmp");
|
||||
pika_file_procedure_set_magics (PIKA_FILE_PROCEDURE (procedure),
|
||||
"0,string,BM");
|
||||
}
|
||||
else if (! strcmp (name, SAVE_PROC))
|
||||
{
|
||||
procedure = pika_save_procedure_new (plug_in, name,
|
||||
PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
bmp_save, NULL, NULL);
|
||||
|
||||
pika_procedure_set_image_types (procedure, "INDEXED, GRAY, RGB*");
|
||||
|
||||
pika_procedure_set_menu_label (procedure, _("Windows BMP image"));
|
||||
pika_file_procedure_set_format_name (PIKA_FILE_PROCEDURE (procedure),
|
||||
_("BMP"));
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
_("Saves files in Windows BMP file format"),
|
||||
_("Saves files in Windows BMP file format"),
|
||||
name);
|
||||
pika_procedure_set_attribution (procedure,
|
||||
"Alexander Schulz",
|
||||
"Alexander Schulz",
|
||||
"1997");
|
||||
|
||||
pika_file_procedure_set_mime_types (PIKA_FILE_PROCEDURE (procedure),
|
||||
"image/bmp");
|
||||
pika_file_procedure_set_extensions (PIKA_FILE_PROCEDURE (procedure),
|
||||
"bmp");
|
||||
|
||||
PIKA_PROC_ARG_BOOLEAN (procedure, "use-rle",
|
||||
_("Ru_n-Length Encoded"),
|
||||
_("Use run-length-encoding compression "
|
||||
"(only valid for 4 and 8-bit indexed images)"),
|
||||
FALSE,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_BOOLEAN (procedure, "write-color-space",
|
||||
_("_Write color space information"),
|
||||
_("Whether or not to write BITMAPV5HEADER "
|
||||
"color space data"),
|
||||
TRUE,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "rgb-format",
|
||||
_("R_GB format"),
|
||||
_("Export format for RGB images "
|
||||
"(0=RGB_565, 1=RGBA_5551, 2=RGB_555, 3=RGB_888, "
|
||||
"4=RGBA_8888, 5=RGBX_8888)"),
|
||||
0, 5, 3,
|
||||
G_PARAM_READWRITE);
|
||||
}
|
||||
|
||||
return procedure;
|
||||
}
|
||||
|
||||
static PikaValueArray *
|
||||
bmp_load (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
GFile *file,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data)
|
||||
{
|
||||
PikaValueArray *return_vals;
|
||||
PikaImage *image;
|
||||
GError *error = NULL;
|
||||
|
||||
gegl_init (NULL, NULL);
|
||||
|
||||
image = load_image (file, &error);
|
||||
|
||||
if (! image)
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_EXECUTION_ERROR,
|
||||
error);
|
||||
|
||||
return_vals = pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_SUCCESS,
|
||||
NULL);
|
||||
|
||||
PIKA_VALUES_SET_IMAGE (return_vals, 1, image);
|
||||
|
||||
return return_vals;
|
||||
}
|
||||
|
||||
static PikaValueArray *
|
||||
bmp_save (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
GFile *file,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data)
|
||||
{
|
||||
PikaProcedureConfig *config;
|
||||
PikaPDBStatusType status = PIKA_PDB_SUCCESS;
|
||||
PikaExportReturn export = PIKA_EXPORT_CANCEL;
|
||||
GError *error = NULL;
|
||||
|
||||
gegl_init (NULL, NULL);
|
||||
|
||||
config = pika_procedure_create_config (procedure);
|
||||
pika_procedure_config_begin_run (config, image, run_mode, args);
|
||||
|
||||
switch (run_mode)
|
||||
{
|
||||
case PIKA_RUN_INTERACTIVE:
|
||||
case PIKA_RUN_WITH_LAST_VALS:
|
||||
pika_ui_init (PLUG_IN_BINARY);
|
||||
|
||||
export = pika_export_image (&image, &n_drawables, &drawables, "BMP",
|
||||
PIKA_EXPORT_CAN_HANDLE_RGB |
|
||||
PIKA_EXPORT_CAN_HANDLE_GRAY |
|
||||
PIKA_EXPORT_CAN_HANDLE_ALPHA |
|
||||
PIKA_EXPORT_CAN_HANDLE_INDEXED);
|
||||
|
||||
if (export == PIKA_EXPORT_CANCEL)
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_CANCEL,
|
||||
NULL);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (n_drawables != 1)
|
||||
{
|
||||
g_set_error (&error, G_FILE_ERROR, 0,
|
||||
_("BMP format does not support multiple layers."));
|
||||
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_CALLING_ERROR,
|
||||
error);
|
||||
}
|
||||
|
||||
status = save_image (file, image, drawables[0], run_mode,
|
||||
procedure, G_OBJECT (config),
|
||||
&error);
|
||||
|
||||
pika_procedure_config_end_run (config, status);
|
||||
g_object_unref (config);
|
||||
|
||||
if (export == PIKA_EXPORT_EXPORT)
|
||||
{
|
||||
pika_image_delete (image);
|
||||
g_free (drawables);
|
||||
}
|
||||
|
||||
return pika_procedure_new_return_values (procedure, status, error);
|
||||
}
|
||||
70
plug-ins/file-bmp/bmp.h
Normal file
70
plug-ins/file-bmp/bmp.h
Normal file
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* PIKA - Photo and Image Kooker Application
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __BMP_H__
|
||||
#define __BMP_H__
|
||||
|
||||
|
||||
#define LOAD_PROC "file-bmp-load"
|
||||
#define SAVE_PROC "file-bmp-save"
|
||||
#define PLUG_IN_BINARY "file-bmp"
|
||||
#define PLUG_IN_ROLE "pika-file-bmp"
|
||||
|
||||
#define MAXCOLORS 256
|
||||
|
||||
#define BitSet(byte, bit) (((byte) & (bit)) == (bit))
|
||||
|
||||
#define ReadOK(file,buffer,len) (fread(buffer, len, 1, file) != 0)
|
||||
#define Write(file,buffer,len) fwrite(buffer, len, 1, file)
|
||||
#define WriteOK(file,buffer,len) (Write(buffer, len, file) != 0)
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
gchar zzMagic[2]; /* 00 "BM" */
|
||||
guint32 bfSize; /* 02 */
|
||||
guint16 zzHotX; /* 06 */
|
||||
guint16 zzHotY; /* 08 */
|
||||
guint32 bfOffs; /* 0A */
|
||||
guint32 biSize; /* 0E */
|
||||
} BitmapFileHead;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
gint32 biWidth; /* 12 */
|
||||
gint32 biHeight; /* 16 */
|
||||
guint16 biPlanes; /* 1A */
|
||||
guint16 biBitCnt; /* 1C */
|
||||
guint32 biCompr; /* 1E */
|
||||
guint32 biSizeIm; /* 22 */
|
||||
guint32 biXPels; /* 26 */
|
||||
guint32 biYPels; /* 2A */
|
||||
guint32 biClrUsed; /* 2E */
|
||||
guint32 biClrImp; /* 32 */
|
||||
guint32 masks[4]; /* 36 */
|
||||
} BitmapHead;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
guint32 mask;
|
||||
guint32 shiftin;
|
||||
gfloat max_value;
|
||||
} BitmapChannel;
|
||||
|
||||
|
||||
#endif /* __BMP_H__ */
|
||||
28
plug-ins/file-bmp/meson.build
Normal file
28
plug-ins/file-bmp/meson.build
Normal file
@ -0,0 +1,28 @@
|
||||
plugin_name = 'file-bmp'
|
||||
|
||||
plugin_sources = [
|
||||
'bmp-load.c',
|
||||
'bmp-save.c',
|
||||
'bmp.c',
|
||||
]
|
||||
|
||||
if platform_windows
|
||||
plugin_sources += windows.compile_resources(
|
||||
pika_plugins_rc,
|
||||
args: [
|
||||
'--define', 'ORIGINALFILENAME_STR="@0@"'.format(plugin_name+'.exe'),
|
||||
'--define', 'INTERNALNAME_STR="@0@"' .format(plugin_name),
|
||||
'--define', 'TOP_SRCDIR="@0@"' .format(meson.project_source_root()),
|
||||
],
|
||||
include_directories: [
|
||||
rootInclude, appInclude,
|
||||
],
|
||||
)
|
||||
endif
|
||||
|
||||
executable(plugin_name,
|
||||
plugin_sources,
|
||||
dependencies: libpikaui_dep,
|
||||
install: true,
|
||||
install_dir: pikaplugindir / 'plug-ins' / plugin_name,
|
||||
)
|
||||
339
plug-ins/file-dds/COPYING
Normal file
339
plug-ins/file-dds/COPYING
Normal file
@ -0,0 +1,339 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Library General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
Appendix: How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) 19yy <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) 19yy name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Library General
|
||||
Public License instead of this License.
|
||||
21
plug-ins/file-dds/LICENSE
Normal file
21
plug-ins/file-dds/LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
/*
|
||||
DDS PIKA plugin
|
||||
|
||||
Copyright (C) 2012 Shawn Kirst <skirst@gmail.com>,
|
||||
with parts (C) 2003 Arne Reuter <homepage@arnereuter.de> where specified.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; see the file COPYING. If not, write to
|
||||
the Free Software Foundation, 51 Franklin Street, Fifth Floor,
|
||||
Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
23
plug-ins/file-dds/LICENSE.nvtt
Normal file
23
plug-ins/file-dds/LICENSE.nvtt
Normal file
@ -0,0 +1,23 @@
|
||||
// Copyright (c) 2009-2011 Ignacio Castano <castano@gmail.com>
|
||||
// Copyright (c) 2007-2009 NVIDIA Corporation -- Ignacio Castano <icastano@nvidia.com>
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use,
|
||||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following
|
||||
// conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
// OTHER DEALINGS IN THE SOFTWARE.
|
||||
29
plug-ins/file-dds/README
Normal file
29
plug-ins/file-dds/README
Normal file
@ -0,0 +1,29 @@
|
||||
DDS plugin for The PIKA
|
||||
(C) 2004-2012 Shawn Kirst <skirst@gmail.com>,
|
||||
with parts (C) 2003 Arne Reuter <homepage@arnereuter.de> where specified.
|
||||
==========================================
|
||||
|
||||
This is a plugin for PIKA version 2.4.x. It allows you to load and save
|
||||
images in Direct Draw Surface (DDS) format.
|
||||
|
||||
Features
|
||||
==========================================
|
||||
* Load/Save DDS files using DXT texture compression
|
||||
* Automatic mipmap generation on save
|
||||
* Load mipmaps into separate layers
|
||||
* Load cube map faces and volume map slices into separate layers
|
||||
* Cube and volume map saving
|
||||
* Pixel conversion selection for custom formats (RGBA4, R5G6B5, RGB10A2, etc.)
|
||||
* Load/save DDS files, optionally using DirectX texture compression (DXT)
|
||||
* Optional automatic mipmap generation when saving
|
||||
* Load mipmaps into separate layers
|
||||
* Load cube map faces and volume map slices into separate layers
|
||||
* Save cube maps and volume maps with automatic mipmap generation support
|
||||
* Save image with a custom pixel format
|
||||
* Non-power-of-two image loading and saving support with automatic mipmap generation support
|
||||
* Compliant with DirectX 10 compressed formats
|
||||
|
||||
|
||||
Installation
|
||||
==========================================
|
||||
See the file INSTALL for installation instructions
|
||||
175
plug-ins/file-dds/README.dxt
Normal file
175
plug-ins/file-dds/README.dxt
Normal file
@ -0,0 +1,175 @@
|
||||
The following notes are from an email I received from Fabian 'ryg' Giesen. They
|
||||
should help answer some questions regarding the code found in dxt.c
|
||||
|
||||
---
|
||||
|
||||
mul8bit: This formula is equivalent to (a*b)/255 for a fairly large set of
|
||||
values (didn't bother finding out the exact bounds). It's a fairly well-known
|
||||
trick also used in other parts of the PIKA codebase (among others) and was
|
||||
first documented by Jim Blinn, I think. A good reference is his book "Dirty
|
||||
Pixels".
|
||||
|
||||
---
|
||||
|
||||
lerp_rgb: The expression computed is exactly equivalent to mul8bit(a[i],255-f)
|
||||
+ mul8bit(b[i],f) - I just verified that by brute force for -255 <= b[i]-a[i]
|
||||
<= 255 because I couldn't be bothered to find a derivation for this :) . You
|
||||
customarily use a factor between 0 and 256 include for LERPing if you can, but
|
||||
normal DXT blocks have colors placed at 1/3 and 2/3 between the two
|
||||
interpolated colors. 255 is divisible by 3, so lerp_rgb can later be used in
|
||||
eval_colors to determine the result of
|
||||
|
||||
a*(1/3) + b*(2/3) and a*(2/3) + b*(1/3)
|
||||
|
||||
exactly, which is nice :)
|
||||
|
||||
---
|
||||
|
||||
dither_block: This is just Floyd-Steinberg dithering. Distributing the error
|
||||
terms to the adjacent pixels for each source pixel is the customary variant to
|
||||
write this, but since blocks are so small, it's nearly all boundary
|
||||
cases; "gathering" the error terms per source pixel turned out to be simpler.
|
||||
|
||||
---
|
||||
|
||||
match_colors_block: This includes a few tricks. We want to map each source
|
||||
color to its nearest representable color (index), using the euclidean distance
|
||||
as a metric.
|
||||
|
||||
The obvious, brute-force way is to just compare squared distances to the 4
|
||||
representable colors for each source pixel (using, for example,
|
||||
color_distance); this requires a lot of arithmetic operations.
|
||||
|
||||
Instead, the code uses the fact that the 4 colors lie on a line in RGB space
|
||||
(only approximately in truth, since we have discrete steps). It's a well-known
|
||||
fact in geometry that if P is the closest point to the line L in 3D space, and
|
||||
Q is the point closest to P on L, then (P-Q) is orthogonal to the direction of
|
||||
L. So (in R3 at least), we can simply determine Q by projecting P onto the
|
||||
direction vector of L, which is done by the 16 dot products in the first for
|
||||
loop. Since the RGB values have discrete steps in reality, this is just an
|
||||
approximation, but a quite good one.
|
||||
|
||||
The loop after that determines where the 4 actually representable colors lie
|
||||
along the line. After that, you simply need to determine which of those 4
|
||||
values your current pixel's dot product is closest to. Instead of doing a
|
||||
bunch of comparisons per pixel, the code computes the points along the line
|
||||
at which the decision would change (that's c0pt, halfpt and c3pt). This would
|
||||
still require 3 comparisons; by testing the middle point - which is halfpt -
|
||||
first, one point is always excluded from consideration, which reduces the
|
||||
number of compares to two in all cases. No big deal, but hey, why not :)
|
||||
|
||||
Similarly, instead of dithering full RGB values, I just dither the dot product
|
||||
values. Again, by my experiments this works just as well and reduces the
|
||||
amount of work significantly.
|
||||
|
||||
---
|
||||
|
||||
optimize_colors_block: This first determines min/max/mean for r,g,b and the
|
||||
covariance matrix for the color distribution. The latter is used to determine
|
||||
the principal component (=eigenvector with largest eigenvalue) of that color
|
||||
distribution, or the direction along which the colors in the block vary most
|
||||
(in layman's terms) - the eigenvector is determined using simple power
|
||||
iteration (a standard technique). That iteration needs a seed vector; I just
|
||||
use (max_r-min_r,max_g-min_g,max_b-min_b), which works well in practice. If
|
||||
the iteration converges to a vector with very small magnitude (or zero), which
|
||||
can happen sometimes, it just defaults to an approximation of the YCbCr Y
|
||||
vector (scaled appropriately to make sure no precision is lost with the dot
|
||||
products).
|
||||
|
||||
This is then used as an initial approximation for the direction of the line
|
||||
through RGB color space that is used to select colors for that block. It
|
||||
simply uses the two most extreme points along that axis as the two colors
|
||||
stored in the block.
|
||||
|
||||
---
|
||||
|
||||
refine_block: This takes a block and a chosen set of color indices, and tries
|
||||
to determine the optimal endpoints for these indices (i.e. the full process
|
||||
is: take block, use color distribution to get rough estimate of optimal
|
||||
direction, assign color indices accordingly, use these to get better
|
||||
endpoints, assign indices again). The computation just solves a least-squares
|
||||
system to minimize the square error between the actual pixels and the
|
||||
interpolated colors (solving for the two extremal colors). The least-squares
|
||||
computation turns out to boil down to solving a 2x2 system of linear equations
|
||||
for each of the RGB color channels; the actual solution is computed using
|
||||
Cramer's rule.
|
||||
|
||||
The code is somewhat weird (especially the "prods"/"akku" thing), but that's
|
||||
just to reduce the amount of computation done (this piece of code is a hot
|
||||
spot, so it's worth it).
|
||||
|
||||
The (!yy || !xx || xx * yy == xy*xy) part checks whether the system of linear
|
||||
equations is degenerate. After pondering about this some months ago, I found
|
||||
out that the only case in which this can ever happen is when all pixels of the
|
||||
source block get mapped to the same color value. But that case can be handled
|
||||
better in any case, by just using the single-color lookup tables. I've
|
||||
attached the new version of refine_block using that observation - it both
|
||||
increases performance (a bit) and image quality, so it's pretty neat.
|
||||
|
||||
---
|
||||
|
||||
encode_alpha_block_DXT5: The only thing that shouldn't be obvious is the index
|
||||
computation. This just uses some two's complement arithmetic and bit shuffling
|
||||
to avoid computing
|
||||
|
||||
7 * (in_alpha - min_alpha) / (max_alpha - min_alpha)
|
||||
|
||||
which would be more expensive. (The extra calc with idx is just because of the
|
||||
weird DXT color numbering).
|
||||
|
||||
---
|
||||
|
||||
Some more notes on the general flow:
|
||||
|
||||
The computation without dithering is just as I explained in the part about
|
||||
refine_block:
|
||||
1. Calc initial endpoints directly from block colors
|
||||
2. Determine color indices for these endpoints
|
||||
3. Optimize endpoints given color indices
|
||||
4. Determine new color indices given optimized endpoints
|
||||
|
||||
With dithering, there's a twist: The first two steps are done using a version
|
||||
of the block dithered to colors that are representable using 16-bit 565 RGB
|
||||
values. I've found that this significantly improves visual quality with
|
||||
dithering; if you don't do this, the colors inside a block typically vary too
|
||||
little for dithering to be useful. This process decreases objective quality
|
||||
but typically looks notably better.
|
||||
|
||||
The single-color match (omatch5/omatch6) trick: If the block only contains a
|
||||
single color (or, for the improved version of refine_block, if the color
|
||||
values are sufficiently close to all map to the same index), an optimal
|
||||
solution can be used instead.
|
||||
|
||||
You normally want solid-color blocks to map to solid-color block (because
|
||||
dithering patterns are very obvious otherwise). This means that all color
|
||||
indices for the block are going to be identical, i.e. all 0, 1, 2 or 3. All-0
|
||||
is symmetrical to all-1 (with the endpoints flipped), and all-2 is symmetrical
|
||||
to all-3 (again with the endpoints flipped). So you only need to consider
|
||||
all-0 or all-2 indices for the block. Furthermore, all-0 means that the first
|
||||
endpoint specified in the block gets used for all pixels; you can get the same
|
||||
result by setting both endpoints to the same value and using index 2 for
|
||||
everything.
|
||||
|
||||
In short, you can always set all indices to 2 without sacrificing any quality
|
||||
whatsoever. For any of the color components R,G,B, you then want to determine
|
||||
5-bit or 6-bit values a and b such that
|
||||
|
||||
expand[a]*(2/3) + expand[b]*(1/3) is as close as possible to R/G/B
|
||||
|
||||
and that's exactly what's in omatch5 (for 5-bit values) and omatch6 (for 6-bit
|
||||
values).
|
||||
|
||||
If you use the 3-color+transparency mode of DXT1, you need separate versions
|
||||
for omatch5/omatch6 for this case, since the interpolated value is exactly
|
||||
halfway between the two endpoints instead of 1/3 of the way along. But I
|
||||
recommend against that mode, because even if your top-level mipmap has 1-bit
|
||||
transparency, mipmaps will have more than 2 distinct values, and the DXT mode
|
||||
is selected per texture. That's why my original code doesn't support the
|
||||
3-color mode of DXT1 at all: I don't think it's useful in practice.
|
||||
|
||||
Not sure if all of this is useful to you or not, but I guess it might make
|
||||
sense to at least put this in a separate text file or whatever, because
|
||||
otherwise it's really quite hard to see what's going on in some places.
|
||||
|
||||
Cheers,
|
||||
-Fabian "ryg" Giesen
|
||||
7
plug-ins/file-dds/TODO
Normal file
7
plug-ins/file-dds/TODO
Normal file
@ -0,0 +1,7 @@
|
||||
TODO list for future releases of pika-dds:
|
||||
|
||||
* Add support for DX10 DDS extensions
|
||||
* BC6H and BC7 compression support
|
||||
* Volume map compression support (VTC)
|
||||
* Add support for PIKA 2.6.x GEGL for reading and writing higher precision
|
||||
pixel formats
|
||||
58
plug-ins/file-dds/color.c
Normal file
58
plug-ins/file-dds/color.c
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* DDS PIKA plugin
|
||||
*
|
||||
* Copyright (C) 2004-2012 Shawn Kirst <skirst@gmail.com>,
|
||||
* with parts (C) 2003 Arne Reuter <homepage@arnereuter.de> where specified.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include "color.h"
|
||||
|
||||
int
|
||||
linear_to_sRGB(int c)
|
||||
{
|
||||
float v = (float)c / 255.0f;
|
||||
|
||||
if(v < 0)
|
||||
v = 0;
|
||||
else if(v > 1)
|
||||
v = 1;
|
||||
else if(v <= 0.0031308f)
|
||||
v = 12.92f * v;
|
||||
else
|
||||
v = 1.055f * powf(v, 0.41666f) - 0.055f;
|
||||
|
||||
return (int)floorf(255.0f * v + 0.5f);
|
||||
}
|
||||
|
||||
int
|
||||
sRGB_to_linear(int c)
|
||||
{
|
||||
float v = (float)c / 255.0f;
|
||||
|
||||
if(v < 0)
|
||||
v = 0;
|
||||
else if(v > 1)
|
||||
v = 1;
|
||||
else if(v <= 0.04045f)
|
||||
v /= 12.92f;
|
||||
else
|
||||
v = powf((v + 0.055f) / 1.055f, 2.4f);
|
||||
|
||||
return (int)floorf(255.0f * v + 0.5f);
|
||||
}
|
||||
96
plug-ins/file-dds/color.h
Normal file
96
plug-ins/file-dds/color.h
Normal file
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* DDS PIKA plugin
|
||||
*
|
||||
* Copyright (C) 2004-2012 Shawn Kirst <skirst@gmail.com>,
|
||||
* with parts (C) 2003 Arne Reuter <homepage@arnereuter.de> where specified.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __COLOR_H__
|
||||
#define __COLOR_H__
|
||||
|
||||
#include "imath.h"
|
||||
|
||||
/* sRGB encoding/decoding */
|
||||
int linear_to_sRGB(int c);
|
||||
int sRGB_to_linear(int c);
|
||||
|
||||
/* YCoCg encoding */
|
||||
static inline void
|
||||
RGB_to_YCoCg (unsigned char *dst, int r, int g, int b)
|
||||
{
|
||||
int y = ((r + (g << 1) + b) + 2) >> 2;
|
||||
int co = ((((r << 1) - (b << 1)) + 2) >> 2) + 128;
|
||||
int cg = (((-r + (g << 1) - b) + 2) >> 2) + 128;
|
||||
|
||||
dst[0] = 255;
|
||||
dst[1] = (cg > 255 ? 255 : (cg < 0 ? 0 : cg));
|
||||
dst[2] = (co > 255 ? 255 : (co < 0 ? 0 : co));
|
||||
dst[3] = (y > 255 ? 255 : (y < 0 ? 0 : y));
|
||||
}
|
||||
|
||||
/* other color conversions */
|
||||
|
||||
static inline int
|
||||
rgb_to_luminance (int r, int g, int b)
|
||||
{
|
||||
/* ITU-R BT.709 luma coefficients, scaled by 256 */
|
||||
return ((r * 54 + g * 182 + b * 20) + 128) >> 8;
|
||||
}
|
||||
|
||||
static inline unsigned short
|
||||
pack_r5g6b5 (int r, int g, int b)
|
||||
{
|
||||
return (mul8bit(r, 31) << 11) |
|
||||
(mul8bit(g, 63) << 5) |
|
||||
(mul8bit(b, 31) );
|
||||
}
|
||||
|
||||
static inline unsigned short
|
||||
pack_rgba4 (int r, int g, int b, int a)
|
||||
{
|
||||
return (mul8bit(a, 15) << 12) |
|
||||
(mul8bit(r, 15) << 8) |
|
||||
(mul8bit(g, 15) << 4) |
|
||||
(mul8bit(b, 15) );
|
||||
}
|
||||
|
||||
static inline unsigned short
|
||||
pack_rgb5a1 (int r, int g, int b, int a)
|
||||
{
|
||||
return (((a >> 7) & 0x01) << 15) |
|
||||
(mul8bit(r, 31) << 10) |
|
||||
(mul8bit(g, 31) << 5) |
|
||||
(mul8bit(b, 31) );
|
||||
}
|
||||
|
||||
static inline unsigned char
|
||||
pack_r3g3b2(int r, int g, int b)
|
||||
{
|
||||
return (mul8bit(r, 7) << 5) |
|
||||
(mul8bit(g, 7) << 2) |
|
||||
(mul8bit(b, 3) );
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
pack_rgb10a2 (int r, int g, int b, int a)
|
||||
{
|
||||
return ((unsigned int)((a >> 6) & 0x003) << 30) |
|
||||
((unsigned int)((b << 2) & 0x3ff) << 20) |
|
||||
((unsigned int)((g << 2) & 0x3ff) << 10) |
|
||||
((unsigned int)((r << 2) & 0x3ff) );
|
||||
}
|
||||
|
||||
#endif /* __COLOR_H__ */
|
||||
549
plug-ins/file-dds/dds.c
Normal file
549
plug-ins/file-dds/dds.c
Normal file
@ -0,0 +1,549 @@
|
||||
/*
|
||||
* DDS PIKA plugin
|
||||
*
|
||||
* Copyright (C) 2004-2012 Shawn Kirst <skirst@gmail.com>,
|
||||
* with parts (C) 2003 Arne Reuter <homepage@arnereuter.de> where specified.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include <libpika/pika.h>
|
||||
#include <libpika/pikaui.h>
|
||||
|
||||
#include <libpika/stdplugins-intl.h>
|
||||
|
||||
#include "dds.h"
|
||||
#include "ddsread.h"
|
||||
#include "ddswrite.h"
|
||||
#include "misc.h"
|
||||
|
||||
|
||||
#define LOAD_PROC "file-dds-load"
|
||||
#define SAVE_PROC "file-dds-save"
|
||||
|
||||
#define DECODE_YCOCG_PROC "color-decode-ycocg"
|
||||
#define DECODE_YCOCG_SCALED_PROC "color-decode-ycocg-scaled"
|
||||
#define DECODE_ALPHA_EXP_PROC "color-decode-alpha-exp"
|
||||
|
||||
|
||||
typedef struct _Dds Dds;
|
||||
typedef struct _DdsClass DdsClass;
|
||||
|
||||
struct _Dds
|
||||
{
|
||||
PikaPlugIn parent_instance;
|
||||
};
|
||||
|
||||
struct _DdsClass
|
||||
{
|
||||
PikaPlugInClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
#define DDS_TYPE (dds_get_type ())
|
||||
#define DDS (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), DDS_TYPE, Dds))
|
||||
|
||||
GType dds_get_type (void) G_GNUC_CONST;
|
||||
|
||||
static GList * dds_query_procedures (PikaPlugIn *plug_in);
|
||||
static PikaProcedure * dds_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name);
|
||||
|
||||
static PikaValueArray * dds_load (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
GFile *file,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data);
|
||||
static PikaValueArray * dds_save (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
GFile *file,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data);
|
||||
#if 0
|
||||
static PikaValueArray * dds_decode (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data);
|
||||
#endif
|
||||
|
||||
|
||||
G_DEFINE_TYPE (Dds, dds, PIKA_TYPE_PLUG_IN)
|
||||
|
||||
PIKA_MAIN (DDS_TYPE)
|
||||
DEFINE_STD_SET_I18N
|
||||
|
||||
|
||||
static void
|
||||
dds_class_init (DdsClass *klass)
|
||||
{
|
||||
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
|
||||
|
||||
plug_in_class->query_procedures = dds_query_procedures;
|
||||
plug_in_class->create_procedure = dds_create_procedure;
|
||||
plug_in_class->set_i18n = STD_SET_I18N;
|
||||
}
|
||||
|
||||
static void
|
||||
dds_init (Dds *dds)
|
||||
{
|
||||
}
|
||||
|
||||
static GList *
|
||||
dds_query_procedures (PikaPlugIn *plug_in)
|
||||
{
|
||||
GList *list = NULL;
|
||||
|
||||
list = g_list_append (list, g_strdup (LOAD_PROC));
|
||||
list = g_list_append (list, g_strdup (SAVE_PROC));
|
||||
#if 0
|
||||
list = g_list_append (list, g_strdup (DECODE_YCOCG_PROC));
|
||||
list = g_list_append (list, g_strdup (DECODE_YCOCG_SCALED_PROC));
|
||||
list = g_list_append (list, g_strdup (DECODE_ALPHA_EXP_PROC));
|
||||
#endif
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
static PikaProcedure *
|
||||
dds_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name)
|
||||
{
|
||||
PikaProcedure *procedure = NULL;
|
||||
|
||||
if (! strcmp (name, LOAD_PROC))
|
||||
{
|
||||
procedure = pika_load_procedure_new (plug_in, name,
|
||||
PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
dds_load, NULL, NULL);
|
||||
|
||||
pika_procedure_set_menu_label (procedure, _("DDS image"));
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
_("Loads files in DDS image format"),
|
||||
_("Loads files in DDS image format"),
|
||||
name);
|
||||
pika_procedure_set_attribution (procedure,
|
||||
"Shawn Kirst",
|
||||
"Shawn Kirst",
|
||||
"2008");
|
||||
|
||||
pika_file_procedure_set_mime_types (PIKA_FILE_PROCEDURE (procedure),
|
||||
"image/dds");
|
||||
pika_file_procedure_set_extensions (PIKA_FILE_PROCEDURE (procedure),
|
||||
"dds");
|
||||
pika_file_procedure_set_magics (PIKA_FILE_PROCEDURE (procedure),
|
||||
"0,string,DDS");
|
||||
|
||||
PIKA_PROC_ARG_BOOLEAN (procedure, "load-mipmaps",
|
||||
_("Load _mipmaps"),
|
||||
_("Load mipmaps if present"),
|
||||
TRUE,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_BOOLEAN (procedure, "decode-images",
|
||||
_("Automatically decode YCoCg/AE_xp images when detected"),
|
||||
_("Decode YCoCg/AExp images when detected"),
|
||||
TRUE,
|
||||
G_PARAM_READWRITE);
|
||||
}
|
||||
else if (! strcmp (name, SAVE_PROC))
|
||||
{
|
||||
procedure = pika_save_procedure_new (plug_in, name,
|
||||
PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
dds_save, NULL, NULL);
|
||||
|
||||
pika_procedure_set_image_types (procedure, "INDEXED, GRAY, RGB");
|
||||
|
||||
pika_procedure_set_menu_label (procedure, _("DDS image"));
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
"Saves files in DDS image format",
|
||||
"Saves files in DDS image format",
|
||||
name);
|
||||
pika_procedure_set_attribution (procedure,
|
||||
"Shawn Kirst",
|
||||
"Shawn Kirst",
|
||||
"2008");
|
||||
|
||||
pika_file_procedure_set_mime_types (PIKA_FILE_PROCEDURE (procedure),
|
||||
"image/dds");
|
||||
pika_file_procedure_set_extensions (PIKA_FILE_PROCEDURE (procedure),
|
||||
"dds");
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "compression-format",
|
||||
"Compression format",
|
||||
"Compression format (0 = None, 1 = BC1/DXT1, "
|
||||
"2 = BC2/DXT3, 3 = BC3/DXT5, 4 = BC3n/DXT5nm, "
|
||||
"5 = BC4/ATI1N, 6 = BC5/ATI2N, 7 = RXGB (DXT5), "
|
||||
"8 = Alpha Exponent (DXT5), 9 = YCoCg (DXT5), "
|
||||
"10 = YCoCg scaled (DXT5))",
|
||||
0, 10, DDS_COMPRESS_NONE,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "mipmaps",
|
||||
"Mipmaps",
|
||||
"How to handle mipmaps (0 = No mipmaps, "
|
||||
"1 = Generate mipmaps, "
|
||||
"2 = Use existing mipmaps (layers)",
|
||||
0, 2, DDS_MIPMAP_NONE,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "save-type",
|
||||
"Save type",
|
||||
"How to save the image (0 = selected layer, "
|
||||
"1 = cube map, 2 = volume map, 3 = texture array, "
|
||||
"4 = all visible layers)",
|
||||
0, 4, DDS_SAVE_SELECTED_LAYER,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "format",
|
||||
"Format",
|
||||
"Pixel format (0 = default, 1 = DDS_FORMAT_RGB8, "
|
||||
"2 = DDS_FORMAT_RGBA8, 3 = DDS_FORMAT_BGR8, "
|
||||
"4 = DDS_FORMAT_ABGR8, 5 = DDS_FORMAT_R5G6B5, "
|
||||
"6 = DDS_FORMAT_RGBA4, 7 = DDS_FORMAT_RGB5A1, "
|
||||
"8 = DDS_FORMAT_RGB10A2, 9 = DDS_FORMAT_R3G3B2, "
|
||||
"10 = DDS_FORMAT_A8, 11 = DDS_FORMAT_L8, "
|
||||
"12 = DDS_FORMAT_L8A8, 13 = DDS_FORMAT_AEXP, "
|
||||
"14 = DDS_FORMAT_YCOCG)",
|
||||
0, 14, DDS_FORMAT_DEFAULT,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_BOOLEAN (procedure, "flip-image",
|
||||
"Flip image vertically",
|
||||
"Flip the image vertically on export",
|
||||
FALSE,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_BOOLEAN (procedure, "transparent-color",
|
||||
"Transparent color",
|
||||
"Make an indexed color transparent",
|
||||
FALSE,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "transparent-index",
|
||||
"Transparent index",
|
||||
"Index of transparent color or -1 to disable "
|
||||
"(for indexed images only).",
|
||||
0, 255, 0,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "mipmap-filter",
|
||||
"Mipmap filter",
|
||||
"Filtering to use when generating mipmaps "
|
||||
"(0 = default, 1 = nearest, 2 = box, 3 = triangle, "
|
||||
"4 = quadratic, 5 = bspline, 6 = mitchell, "
|
||||
"7 = lanczos, 8 = kaiser)",
|
||||
0, 8, DDS_MIPMAP_FILTER_DEFAULT,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "mipmap-wrap",
|
||||
"Mipmap wrap",
|
||||
"Wrap mode to use when generating mipmaps "
|
||||
"(0 = default, 1 = mirror, 2 = repeat, 3 = clamp)",
|
||||
0, 3, DDS_MIPMAP_WRAP_DEFAULT,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_BOOLEAN (procedure, "gamma-correct",
|
||||
"Gamma correct",
|
||||
"Use gamma correct mipmap filtering",
|
||||
FALSE,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_BOOLEAN (procedure, "srgb",
|
||||
"sRGB",
|
||||
"Use sRGB colorspace for gamma correction",
|
||||
FALSE,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_DOUBLE (procedure, "gamma",
|
||||
"Gamma",
|
||||
"Gamma value to use for gamma correction (i.e. 2.2)",
|
||||
0.0, 10.0, 0.0,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_BOOLEAN (procedure, "perceptual-metric",
|
||||
"Perceptual metric",
|
||||
"Use a perceptual error metric during compression",
|
||||
FALSE,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_BOOLEAN (procedure, "preserve-alpha-coverage",
|
||||
"Preserve alpha coverage",
|
||||
"Preserve alpha test converage for alpha "
|
||||
"channel maps",
|
||||
FALSE,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_DOUBLE (procedure, "alpha-test-threshold",
|
||||
"Alpha test threshold",
|
||||
"Alpha test threshold value for which alpha test "
|
||||
"converage should be preserved",
|
||||
0.0, 1.0, 0.5,
|
||||
G_PARAM_READWRITE);
|
||||
}
|
||||
#if 0
|
||||
else if (! strcmp (name, DECODE_YCOCG_PROC))
|
||||
{
|
||||
procedure = pika_image_procedure_new (plug_in, name,
|
||||
PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
dds_decode, NULL, NULL);
|
||||
|
||||
pika_procedure_set_image_types (procedure, "RGBA");
|
||||
pika_procedure_set_sensitivity_mask (procedure,
|
||||
PIKA_PROCEDURE_SENSITIVE_DRAWABLE);
|
||||
|
||||
pika_procedure_set_menu_label (procedure, _("Decode YCoCg"));
|
||||
/* pika_procedure_add_menu_path (procedure, "<Image>/Filters/Colors"); */
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
_("Converts YCoCg encoded pixels to RGB"),
|
||||
_("Converts YCoCg encoded pixels to RGB"),
|
||||
name);
|
||||
pika_procedure_set_attribution (procedure,
|
||||
"Shawn Kirst",
|
||||
"Shawn Kirst",
|
||||
"2008");
|
||||
}
|
||||
else if (! strcmp (name, DECODE_YCOCG_SCALED_PROC))
|
||||
{
|
||||
procedure = pika_image_procedure_new (plug_in, name,
|
||||
PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
dds_decode, NULL, NULL);
|
||||
|
||||
pika_procedure_set_image_types (procedure, "RGBA");
|
||||
pika_procedure_set_sensitivity_mask (procedure,
|
||||
PIKA_PROCEDURE_SENSITIVE_DRAWABLE);
|
||||
|
||||
pika_procedure_set_menu_label (procedure, _("Decode YCoCg (scaled)"));
|
||||
/* pika_procedure_add_menu_path (procedure, "<Image>/Filters/Colors"); */
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
_("Converts YCoCg (scaled) encoded "
|
||||
"pixels to RGB"),
|
||||
_("Converts YCoCg (scaled) encoded "
|
||||
"pixels to RGB"),
|
||||
name);
|
||||
pika_procedure_set_attribution (procedure,
|
||||
"Shawn Kirst",
|
||||
"Shawn Kirst",
|
||||
"2008");
|
||||
}
|
||||
else if (! strcmp (name, DECODE_ALPHA_EXP_PROC))
|
||||
{
|
||||
procedure = pika_image_procedure_new (plug_in, name,
|
||||
PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
dds_decode, NULL, NULL);
|
||||
|
||||
pika_procedure_set_image_types (procedure, "RGBA");
|
||||
pika_procedure_set_sensitivity_mask (procedure,
|
||||
PIKA_PROCEDURE_SENSITIVE_DRAWABLE);
|
||||
|
||||
pika_procedure_set_menu_label (procedure, _("Decode Alpha exponent"));
|
||||
/* pika_procedure_add_menu_path (procedure, "<Image>/Filters/Colors"); */
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
_("Converts alpha exponent encoded "
|
||||
"pixels to RGB",
|
||||
_("Converts alpha exponent encoded "
|
||||
"pixels to RGB"),
|
||||
name);
|
||||
pika_procedure_set_attribution (procedure,
|
||||
"Shawn Kirst",
|
||||
"Shawn Kirst",
|
||||
"2008");
|
||||
}
|
||||
#endif
|
||||
|
||||
return procedure;
|
||||
}
|
||||
|
||||
static PikaValueArray *
|
||||
dds_load (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
GFile *file,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data)
|
||||
{
|
||||
PikaProcedureConfig *config;
|
||||
PikaValueArray *return_vals;
|
||||
PikaPDBStatusType status;
|
||||
PikaImage *image;
|
||||
GError *error = NULL;
|
||||
|
||||
gegl_init (NULL, NULL);
|
||||
|
||||
config = pika_procedure_create_config (procedure);
|
||||
pika_procedure_config_begin_run (config, NULL, run_mode, args);
|
||||
|
||||
status = read_dds (file, &image, run_mode == PIKA_RUN_INTERACTIVE,
|
||||
procedure, G_OBJECT (config), &error);
|
||||
|
||||
pika_procedure_config_end_run (config, status);
|
||||
g_object_unref (config);
|
||||
|
||||
if (status != PIKA_PDB_SUCCESS)
|
||||
return pika_procedure_new_return_values (procedure, status, error);
|
||||
|
||||
return_vals = pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_SUCCESS,
|
||||
NULL);
|
||||
|
||||
PIKA_VALUES_SET_IMAGE (return_vals, 1, image);
|
||||
|
||||
return return_vals;
|
||||
}
|
||||
|
||||
static PikaValueArray *
|
||||
dds_save (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
GFile *file,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data)
|
||||
{
|
||||
PikaProcedureConfig *config;
|
||||
PikaPDBStatusType status = PIKA_PDB_SUCCESS;
|
||||
PikaExportReturn export = PIKA_EXPORT_CANCEL;
|
||||
GError *error = NULL;
|
||||
gdouble gamma;
|
||||
|
||||
gegl_init (NULL, NULL);
|
||||
|
||||
config = pika_procedure_create_config (procedure);
|
||||
pika_procedure_config_begin_run (config, NULL, run_mode, args);
|
||||
|
||||
switch (run_mode)
|
||||
{
|
||||
case PIKA_RUN_INTERACTIVE:
|
||||
case PIKA_RUN_WITH_LAST_VALS:
|
||||
pika_ui_init ("dds");
|
||||
|
||||
export = pika_export_image (&image, &n_drawables, &drawables, "DDS",
|
||||
PIKA_EXPORT_CAN_HANDLE_RGB |
|
||||
PIKA_EXPORT_CAN_HANDLE_GRAY |
|
||||
PIKA_EXPORT_CAN_HANDLE_INDEXED |
|
||||
PIKA_EXPORT_CAN_HANDLE_ALPHA |
|
||||
PIKA_EXPORT_CAN_HANDLE_LAYERS);
|
||||
|
||||
if (export == PIKA_EXPORT_CANCEL)
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_CANCEL,
|
||||
NULL);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
g_object_get (config,
|
||||
"gamma", &gamma,
|
||||
NULL);
|
||||
|
||||
/* pika_gamma () got removed and was always returning 2.2 anyway.
|
||||
* XXX Review this piece of code if we expect gamma value could be
|
||||
* parameterized.
|
||||
*/
|
||||
if (gamma < 1e-04f)
|
||||
g_object_set (config,
|
||||
"gamma", 2.2,
|
||||
NULL);
|
||||
|
||||
/* TODO: support multiple-layers selection, especially as DDS has
|
||||
* DDS_SAVE_SELECTED_LAYER option support.
|
||||
*/
|
||||
status = write_dds (file, image, drawables[0],
|
||||
run_mode == PIKA_RUN_INTERACTIVE,
|
||||
procedure, G_OBJECT (config),
|
||||
export == PIKA_EXPORT_EXPORT);
|
||||
|
||||
if (export == PIKA_EXPORT_EXPORT)
|
||||
{
|
||||
pika_image_delete (image);
|
||||
g_free (drawables);
|
||||
}
|
||||
|
||||
pika_procedure_config_end_run (config, status);
|
||||
g_object_unref (config);
|
||||
|
||||
return pika_procedure_new_return_values (procedure, status, error);
|
||||
}
|
||||
|
||||
#if 0
|
||||
static PikaValueArray *
|
||||
dds_decode (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data)
|
||||
{
|
||||
const gchar *name = pika_procedure_get_name (procedure);
|
||||
PikaDrawable *drawable,
|
||||
|
||||
if (n_drawables != 1)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
g_set_error (&error, PIKA_PLUG_IN_ERROR, 0,
|
||||
_("Procedure '%s' only works with one drawable."),
|
||||
name);
|
||||
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_EXECUTION_ERROR,
|
||||
error);
|
||||
}
|
||||
else
|
||||
{
|
||||
drawable = drawables[0];
|
||||
}
|
||||
|
||||
if (! strcmp (name, DECODE_YCOCG_PROC))
|
||||
{
|
||||
decode_ycocg_image (drawable, TRUE);
|
||||
}
|
||||
else if (! strcmp (name, DECODE_YCOCG_SCALED_PROC))
|
||||
{
|
||||
decode_ycocg_scaled_image (drawable, TRUE);
|
||||
}
|
||||
else if (! strcmp (name, DECODE_ALPHA_EXP_PROC))
|
||||
{
|
||||
decode_alpha_exp_image (drawable, TRUE);
|
||||
}
|
||||
|
||||
if (run_mode != PIKA_RUN_NONINTERACTIVE)
|
||||
pika_displays_flush ();
|
||||
|
||||
return pika_procedure_new_return_values (procedure, PIKA_PDB_SUCCESS, NULL);
|
||||
}
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user