Update upstream

This commit is contained in:
Cassowary 2023-12-02 11:03:24 -08:00
parent 84ea557696
commit d472f6348d
129 changed files with 17814 additions and 14162 deletions

View File

@ -184,19 +184,31 @@ static const PikaEnumActionEntry edit_paste_actions[] =
PIKA_HELP_EDIT_PASTE_IN_PLACE },
{ "edit-paste-into", PIKA_ICON_EDIT_PASTE_INTO,
NC_("edit-action", "Paste _Into Selection"), NULL, { NULL },
NC_("edit-action", "Paste as Floating Data _Into Selection"), NULL, { NULL },
NC_("edit-action",
"Paste the content of the clipboard into the current selection"),
PIKA_PASTE_TYPE_FLOATING_INTO, FALSE,
PIKA_HELP_EDIT_PASTE_INTO },
{ "edit-paste-into-in-place", PIKA_ICON_EDIT_PASTE_INTO,
NC_("edit-action", "Paste Int_o Selection In Place"), NULL, { NULL },
NC_("edit-action", "Paste as Floating Data Int_o Selection In Place"), NULL, { NULL },
NC_("edit-action",
"Paste the content of the clipboard into the current selection "
"at its original position"),
PIKA_PASTE_TYPE_FLOATING_INTO_IN_PLACE, FALSE,
PIKA_HELP_EDIT_PASTE_INTO_IN_PLACE }
PIKA_HELP_EDIT_PASTE_INTO_IN_PLACE },
{ "edit-paste-float", PIKA_ICON_EDIT_PASTE,
NC_("edit-action", "Paste as _Floating Data"), NULL, { NULL },
NC_("edit-action", "Paste the content of the clipboard as Floating Data"),
PIKA_PASTE_TYPE_FLOATING, FALSE,
PIKA_HELP_EDIT_PASTE },
{ "edit-paste-float-in-place", PIKA_ICON_EDIT_PASTE,
NC_("edit-action", "Paste as Floa_ting Data In Place"), NULL, { NULL },
NC_("edit-action", "Paste the content of the clipboard as Floating Data at its original position"),
PIKA_PASTE_TYPE_FLOATING_IN_PLACE, FALSE,
PIKA_HELP_EDIT_PASTE }
};
static const PikaEnumActionEntry edit_fill_actions[] =

View File

@ -972,7 +972,7 @@ layers_actions_update (PikaActionGroup *group,
SET_SENSITIVE ("layers-new", image);
SET_SENSITIVE ("layers-new-last-values", image);
SET_SENSITIVE ("layers-new-from-visible", image);
SET_SENSITIVE ("layers-new-group", image && !indexed);
SET_SENSITIVE ("layers-new-group", image && !indexed && !fs);
SET_SENSITIVE ("layers-duplicate", n_selected_layers > 0 && !fs && !ac);
SET_SENSITIVE ("layers-delete", n_selected_layers > 0 && !ac);

View File

@ -62,10 +62,16 @@ static const PikaActionEntry select_actions[] =
select_invert_cmd_callback,
PIKA_HELP_SELECTION_INVERT },
{ "select-float", PIKA_ICON_LAYER_FLOATING_SELECTION,
NC_("select-action", "_Float"), NULL, { "<primary><shift>L", NULL },
NC_("select-action", "Create a floating selection"),
select_float_cmd_callback,
{ "select-cut-float", PIKA_ICON_LAYER_FLOATING_SELECTION,
NC_("select-action", "Cu_t and Float"), NULL, { "<primary><shift>L", NULL },
NC_("select-action", "Cut the selection directly into a floating selection"),
select_cut_float_cmd_callback,
PIKA_HELP_SELECTION_FLOAT },
{ "select-copy-float", PIKA_ICON_LAYER_FLOATING_SELECTION,
NC_("select-action", "_Copy and Float"), NULL, { NULL },
NC_("select-action", "Copy the selection directly into a floating selection"),
select_copy_float_cmd_callback,
PIKA_HELP_SELECTION_FLOAT },
{ "select-feather", NULL,
@ -186,9 +192,13 @@ select_actions_update (PikaActionGroup *group,
SET_SENSITIVE ("select-all", image && ! sel_all);
SET_SENSITIVE ("select-none", image && sel);
SET_SENSITIVE ("select-invert", image);
SET_SENSITIVE ("select-float", g_list_length (drawables) == 1 && sel &&
! pika_item_is_content_locked (drawables->data, NULL) &&
! pika_viewable_get_children (drawables->data));
SET_SENSITIVE ("select-cut-float", g_list_length (drawables) == 1 && sel &&
! pika_item_is_content_locked (drawables->data, NULL) &&
! pika_viewable_get_children (drawables->data));
SET_SENSITIVE ("select-copy-float", g_list_length (drawables) == 1 && sel &&
! pika_item_is_content_locked (drawables->data, NULL) &&
! pika_viewable_get_children (drawables->data));
SET_SENSITIVE ("select-feather", image && sel);
SET_SENSITIVE ("select-sharpen", image && sel);

View File

@ -71,6 +71,10 @@ static void select_shrink_callback (GtkWidget *widget,
gdouble size,
PikaUnit unit,
gpointer data);
static void select_float (PikaAction *action,
GVariant *value,
gboolean cut,
gpointer data);
/* public functions */
@ -112,33 +116,19 @@ select_invert_cmd_callback (PikaAction *action,
}
void
select_float_cmd_callback (PikaAction *action,
GVariant *value,
gpointer data)
select_cut_float_cmd_callback (PikaAction *action,
GVariant *value,
gpointer data)
{
PikaImage *image;
GtkWidget *widget;
GList *drawables;
GError *error = NULL;
return_if_no_image (image, data);
return_if_no_widget (widget, data);
select_float (action, value, TRUE, data);
}
drawables = pika_image_get_selected_drawables (image);
if (pika_selection_float (PIKA_SELECTION (pika_image_get_mask (image)),
drawables,
action_data_get_context (data),
TRUE, 0, 0, &error))
{
pika_image_flush (image);
}
else
{
pika_message_literal (image->pika,
G_OBJECT (widget), PIKA_MESSAGE_WARNING,
error->message);
g_clear_error (&error);
}
g_list_free (drawables);
void
select_copy_float_cmd_callback (PikaAction *action,
GVariant *value,
gpointer data)
{
select_float (action, value, FALSE, data);
}
void
@ -696,3 +686,34 @@ select_shrink_callback (GtkWidget *widget,
TRUE);
pika_image_flush (image);
}
static void
select_float (PikaAction *action,
GVariant *value,
gboolean cut,
gpointer data)
{
PikaImage *image;
GtkWidget *widget;
GList *drawables;
GError *error = NULL;
return_if_no_image (image, data);
return_if_no_widget (widget, data);
drawables = pika_image_get_selected_drawables (image);
if (pika_selection_float (PIKA_SELECTION (pika_image_get_mask (image)),
drawables,
action_data_get_context (data),
cut, 0, 0, &error))
{
pika_image_flush (image);
}
else
{
pika_message_literal (image->pika,
G_OBJECT (widget), PIKA_MESSAGE_WARNING,
error->message);
g_clear_error (&error);
}
g_list_free (drawables);
}

View File

@ -32,7 +32,10 @@ void select_none_cmd_callback (PikaAction *action,
void select_invert_cmd_callback (PikaAction *action,
GVariant *value,
gpointer data);
void select_float_cmd_callback (PikaAction *action,
void select_cut_float_cmd_callback (PikaAction *action,
GVariant *value,
gpointer data);
void select_copy_float_cmd_callback (PikaAction *action,
GVariant *value,
gpointer data);
void select_feather_cmd_callback (PikaAction *action,

View File

@ -98,6 +98,7 @@ enum
PROP_GLOBAL_PALETTE,
PROP_GLOBAL_GRADIENT,
PROP_GLOBAL_FONT,
PROP_GLOBAL_EXPAND,
PROP_DEFAULT_IMAGE,
PROP_DEFAULT_GRID,
PROP_UNDO_LEVELS,
@ -540,6 +541,13 @@ pika_core_config_class_init (PikaCoreConfigClass *klass)
TRUE,
PIKA_PARAM_STATIC_STRINGS);
PIKA_CONFIG_PROP_BOOLEAN (object_class, PROP_GLOBAL_EXPAND,
"global-expand",
"Global expand",
GLOBAL_EXPAND_BLURB,
TRUE,
PIKA_PARAM_STATIC_STRINGS);
PIKA_CONFIG_PROP_OBJECT (object_class, PROP_DEFAULT_IMAGE,
"default-image",
"Default image",
@ -1061,6 +1069,9 @@ pika_core_config_set_property (GObject *object,
case PROP_GLOBAL_FONT:
core_config->global_font = g_value_get_boolean (value);
break;
case PROP_GLOBAL_EXPAND:
core_config->global_expand = g_value_get_boolean (value);
break;
case PROP_DEFAULT_IMAGE:
if (g_value_get_object (value))
pika_config_sync (g_value_get_object (value) ,
@ -1337,6 +1348,9 @@ pika_core_config_get_property (GObject *object,
case PROP_GLOBAL_FONT:
g_value_set_boolean (value, core_config->global_font);
break;
case PROP_GLOBAL_EXPAND:
g_value_set_boolean (value, core_config->global_expand);
break;
case PROP_DEFAULT_IMAGE:
g_value_set_object (value, core_config->default_image);
break;

View File

@ -82,6 +82,7 @@ struct _PikaCoreConfig
gboolean global_palette;
gboolean global_gradient;
gboolean global_font;
gboolean global_expand;
PikaTemplate *default_image;
PikaGrid *default_grid;
gint levels_of_undo;

View File

@ -181,6 +181,9 @@ _("When enabled, the selected pattern will be used for all tools.")
#define GLOBAL_PALETTE_BLURB \
"When enabled, the selected palette will be used for all tools."
#define GLOBAL_EXPAND_BLURB \
"When enabled, the selected auto expand layer settings will be used for all tools."
#define GRADIENT_PATH_BLURB \
"Sets the gradient search path."

View File

@ -715,6 +715,7 @@ typedef enum /*< pdb-skip, skip >*/
PIKA_CONTEXT_PROP_BUFFER = 18,
PIKA_CONTEXT_PROP_IMAGEFILE = 19,
PIKA_CONTEXT_PROP_TEMPLATE = 20,
PIKA_CONTEXT_PROP_EXPAND = 21,
PIKA_CONTEXT_PROP_LAST = PIKA_CONTEXT_PROP_TEMPLATE
} PikaContextPropType;
@ -741,6 +742,7 @@ typedef enum /*< pdb-skip, skip >*/
PIKA_CONTEXT_PROP_MASK_BUFFER = 1 << 18,
PIKA_CONTEXT_PROP_MASK_IMAGEFILE = 1 << 19,
PIKA_CONTEXT_PROP_MASK_TEMPLATE = 1 << 20,
PIKA_CONTEXT_PROP_MASK_EXPAND = 1 << 21,
/* aliases */
PIKA_CONTEXT_PROP_MASK_PAINT = (PIKA_CONTEXT_PROP_MASK_FOREGROUND |
@ -750,7 +752,8 @@ typedef enum /*< pdb-skip, skip >*/
PIKA_CONTEXT_PROP_MASK_BRUSH |
PIKA_CONTEXT_PROP_MASK_DYNAMICS |
PIKA_CONTEXT_PROP_MASK_PATTERN |
PIKA_CONTEXT_PROP_MASK_GRADIENT),
PIKA_CONTEXT_PROP_MASK_GRADIENT |
PIKA_CONTEXT_PROP_MASK_EXPAND),
PIKA_CONTEXT_PROP_MASK_ALL = (PIKA_CONTEXT_PROP_MASK_IMAGE |
PIKA_CONTEXT_PROP_MASK_DISPLAY |

View File

@ -557,6 +557,9 @@ user_update_menurc_over20 (const GMatchInfo *matched_value,
else if (g_strcmp0 (action_match, "view-rotate-reset") == 0 &&
install->old_major == 2)
new_action_name = g_strdup ("view-reset");
/* select-float became select-cut-float in 3.0 (select-copy-float added). */
else if (g_strcmp0 (action_match, "select-float") == 0)
new_action_name = g_strdup ("select-cut-float");
if (new_action_name == NULL)
new_action_name = g_strdup (action_match);

View File

@ -286,7 +286,8 @@ pika_brush_get_new_preview (PikaViewable *viewable,
guchar *mask;
guchar *buf;
gint x, y;
gboolean scaled = FALSE;
gboolean free_mask = FALSE;
gdouble scale = 1.0;
mask_width = pika_temp_buf_get_width (mask_buf);
mask_height = pika_temp_buf_get_height (mask_buf);
@ -295,45 +296,47 @@ pika_brush_get_new_preview (PikaViewable *viewable,
{
gdouble ratio_x = (gdouble) width / (gdouble) mask_width;
gdouble ratio_y = (gdouble) height / (gdouble) mask_height;
gdouble scale = MIN (ratio_x, ratio_y);
if (scale != 1.0)
scale = MIN (ratio_x, ratio_y);
}
if (PIKA_IS_BRUSH_GENERATED (brush) || scale != 1.0)
{
pika_brush_begin_use (brush);
if (PIKA_IS_BRUSH_GENERATED (brush))
{
pika_brush_begin_use (brush);
PikaBrushGenerated *gen_brush = PIKA_BRUSH_GENERATED (brush);
if (PIKA_IS_BRUSH_GENERATED (brush))
{
PikaBrushGenerated *gen_brush = PIKA_BRUSH_GENERATED (brush);
mask_buf = pika_brush_transform_mask (brush, scale,
(pika_brush_generated_get_aspect_ratio (gen_brush) - 1.0) * 20.0 / 19.0,
pika_brush_generated_get_angle (gen_brush) / -360.0,
FALSE,
pika_brush_generated_get_hardness (gen_brush));
}
else
{
mask_buf = pika_brush_transform_mask (brush, scale, 0.0, 0.0, FALSE, 1.0);
}
mask_buf = pika_brush_transform_mask (brush, scale,
(pika_brush_generated_get_aspect_ratio (gen_brush) - 1.0) * 20.0 / 19.0,
pika_brush_generated_get_angle (gen_brush) / 360.0,
FALSE,
pika_brush_generated_get_hardness (gen_brush));
}
else
mask_buf = pika_brush_transform_mask (brush, scale,
if (! mask_buf)
{
mask_buf = pika_temp_buf_new (1, 1, babl_format ("Y u8"));
pika_temp_buf_data_clear ((PikaTempBuf *) mask_buf);
}
else
{
pika_temp_buf_ref ((PikaTempBuf *) mask_buf);
}
if (pixmap_buf)
pixmap_buf = pika_brush_transform_pixmap (brush, scale,
0.0, 0.0, FALSE, 1.0);
if (! mask_buf)
{
mask_buf = pika_temp_buf_new (1, 1, babl_format ("Y u8"));
pika_temp_buf_data_clear ((PikaTempBuf *) mask_buf);
}
else
{
pika_temp_buf_ref ((PikaTempBuf *) mask_buf);
}
mask_width = pika_temp_buf_get_width (mask_buf);
mask_height = pika_temp_buf_get_height (mask_buf);
if (pixmap_buf)
pixmap_buf = pika_brush_transform_pixmap (brush, scale,
0.0, 0.0, FALSE, 1.0);
mask_width = pika_temp_buf_get_width (mask_buf);
mask_height = pika_temp_buf_get_height (mask_buf);
scaled = TRUE;
}
free_mask = TRUE;
}
return_buf = pika_temp_buf_new (mask_width, mask_height,
@ -381,7 +384,7 @@ pika_brush_get_new_preview (PikaViewable *viewable,
pika_temp_buf_unlock (mask_buf, mask_data);
if (scaled)
if (free_mask)
{
pika_temp_buf_unref ((PikaTempBuf *) mask_buf);

View File

@ -747,7 +747,7 @@ pika_channel_resize (PikaItem *item,
gint offset_x,
gint offset_y)
{
PIKA_ITEM_CLASS (parent_class)->resize (item, context, PIKA_FILL_TRANSPARENT,
PIKA_ITEM_CLASS (parent_class)->resize (item, context, fill_type,
new_width, new_height,
offset_x, offset_y);

View File

@ -45,6 +45,8 @@ struct _PikaDrawablePrivate
GeglBuffer *paint_buffer;
cairo_region_t *paint_copy_region;
cairo_region_t *paint_update_region;
gboolean push_resize_undo;
};
#endif /* __PIKA_DRAWABLE_PRIVATE_H__ */

View File

@ -632,7 +632,10 @@ pika_drawable_resize (PikaItem *item,
copy_y - new_offset_y, 0, 0));
}
pika_drawable_set_buffer_full (drawable, pika_item_is_attached (item), NULL,
pika_drawable_set_buffer_full (drawable,
pika_item_is_attached (item) &&
drawable->private->push_resize_undo,
NULL,
new_buffer,
GEGL_RECTANGLE (new_offset_x, new_offset_y,
0, 0),
@ -910,6 +913,10 @@ pika_drawable_real_set_buffer (PikaDrawable *drawable,
}
g_set_object (&drawable->private->buffer, buffer);
if (pika_drawable_is_painting (drawable))
g_set_object (&drawable->private->paint_buffer, buffer);
g_clear_object (&drawable->private->format_profile);
if (drawable->private->buffer_source_node)
@ -1076,6 +1083,8 @@ pika_drawable_new (GType type,
pika_drawable_set_buffer (drawable, FALSE, NULL, buffer);
g_object_unref (buffer);
pika_drawable_enable_resize_undo (drawable);
return drawable;
}
@ -1676,6 +1685,22 @@ pika_drawable_push_undo (PikaDrawable *drawable,
x, y, width, height);
}
void
pika_drawable_disable_resize_undo (PikaDrawable *drawable)
{
g_return_if_fail (PIKA_IS_DRAWABLE (drawable));
drawable->private->push_resize_undo = FALSE;
}
void
pika_drawable_enable_resize_undo (PikaDrawable *drawable)
{
g_return_if_fail (PIKA_IS_DRAWABLE (drawable));
drawable->private->push_resize_undo = TRUE;
}
const Babl *
pika_drawable_get_space (PikaDrawable *drawable)
{

View File

@ -209,6 +209,9 @@ void pika_drawable_push_undo (PikaDrawable *drawable,
gint width,
gint height);
void pika_drawable_disable_resize_undo (PikaDrawable *drawable);
void pika_drawable_enable_resize_undo (PikaDrawable *drawable);
const Babl * pika_drawable_get_space (PikaDrawable *drawable);
const Babl * pika_drawable_get_format (PikaDrawable *drawable);
const Babl * pika_drawable_get_format_with_alpha(PikaDrawable *drawable);

View File

@ -1083,9 +1083,6 @@ pika_palette_load_ase (PikaContext *context,
}
g_free (palette_name);
/* Header blocks are considered a "color" so we offset the count here */
num_cols -= 1;
for (i = 0; i < num_cols; i++)
{
gchar color_space[4];
@ -1108,6 +1105,23 @@ pika_palette_load_ase (PikaContext *context,
}
skip_first = FALSE;
/* Skip group marker padding */
group = GINT16_FROM_BE (group);
if (group < 0)
{
gchar marker[4];
if (! g_input_stream_read_all (input, &marker, sizeof (marker),
&bytes_read, NULL, error))
{
g_printerr ("Invalid ASE group marker: %s.",
pika_file_get_utf8_name (file));
break;
}
num_cols--;
continue;
}
color_name = pika_palette_load_ase_block_name (input, file_size, error);
if (! color_name)
break;
@ -1148,7 +1162,7 @@ pika_palette_load_ase (PikaContext *context,
for (gint j = 0; j < components; j++)
{
gint tmp;
gint32 tmp;
if (! g_input_stream_read_all (input, &tmp, sizeof (tmp),
&bytes_read, NULL, error))
@ -1162,7 +1176,7 @@ pika_palette_load_ase (PikaContext *context,
/* Convert 4 bytes to a 32bit float value */
tmp = GINT32_FROM_BE (tmp);
pixels[j] = *(gfloat *) &tmp;
memcpy (&pixels[j], &tmp, 4);
}
if (! valid_color)

View File

@ -2,7 +2,7 @@
* 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):
* 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
@ -34,6 +34,8 @@
#include "config/pikacoreconfig.h"
#include "widgets/pikawidgets-utils.h"
#include "about.h"
#include "git-version.h"
@ -45,22 +47,34 @@
#include "pika-intl.h"
/* The first authors are the creators and maintainers, don't shuffle
* them
*/
#define START_INDEX (G_N_ELEMENTS (creators) - 1 /*NULL*/ + \
G_N_ELEMENTS (maintainers) - 1 /*NULL*/)
typedef struct
{
GtkWidget *dialog;
GtkWidget *dialog;
Pika *pika;
GtkWidget *update_frame;
PikaCoreConfig *config;
GtkWidget *anim_area;
PangoLayout *layout;
GtkWidget *anim_area;
PangoLayout *layout;
guint timer;
gint n_authors;
gint shuffle[G_N_ELEMENTS (authors) - 1]; /* NULL terminated */
gint index;
gint animstep;
gint state;
gboolean visible;
guint timer;
gint index;
gint animstep;
gint state;
gboolean visible;
} PikaAboutDialog;
@ -93,7 +107,8 @@ static void about_dialog_download_clicked
const gchar *link);
GtkWidget *
about_dialog_create (PikaCoreConfig *config)
about_dialog_create (Pika *pika,
PikaCoreConfig *config)
{
static PikaAboutDialog dialog;
@ -106,6 +121,8 @@ about_dialog_create (PikaCoreConfig *config)
gchar *copyright;
gchar *version;
dialog.pika = pika;
dialog.n_authors = G_N_ELEMENTS (authors) - 1;
dialog.config = config;
pixbuf = about_dialog_load_logo ();
@ -134,6 +151,9 @@ about_dialog_create (PikaCoreConfig *config)
"logo", pixbuf,
"website", "https://heckin.technology/AlderconeStudio/PIKApp/",
"website-label", _("Visit the PIKA website"),
"authors", authors,
"artists", artists,
"documenters", documenters,
/* Translators: insert your names here,
separated by newline */
"translator-credits", _("translator-credits"),
@ -196,6 +216,10 @@ about_dialog_map (GtkWidget *widget,
dialog->timer = g_timeout_add (800, about_dialog_timer, dialog);
}
#ifdef G_OS_WIN32
pika_window_set_title_bar_theme (dialog->pika, widget, FALSE);
#endif
}
static void
@ -466,6 +490,27 @@ about_dialog_add_update (PikaAboutDialog *dialog,
static void
about_dialog_reshuffle (PikaAboutDialog *dialog)
{
GRand *gr = g_rand_new ();
gint i;
for (i = 0; i < dialog->n_authors; i++)
dialog->shuffle[i] = i;
for (i = START_INDEX; i < dialog->n_authors; i++)
{
gint j = g_rand_int_range (gr, START_INDEX, dialog->n_authors);
if (i != j)
{
gint t;
t = dialog->shuffle[j];
dialog->shuffle[j] = dialog->shuffle[i];
dialog->shuffle[i] = t;
}
}
g_rand_free (gr);
}
static gboolean
@ -642,11 +687,17 @@ about_dialog_timer (gpointer data)
return FALSE;
case 1:
text = insert_spacers (_("PIKA is brought to you by Aldercone Studio"));
text = insert_spacers (_("PIKA is brought to you by"));
dialog->state += 1;
break;
case 2:
return FALSE;
if (! (dialog->index < dialog->n_authors))
dialog->index = 0;
text = insert_spacers (authors[dialog->shuffle[dialog->index]]);
dialog->index += 1;
break;
default:
g_return_val_if_reached (TRUE);

View File

@ -23,7 +23,8 @@
#define __ABOUT_DIALOG_H__
GtkWidget * about_dialog_create (PikaCoreConfig *config);
GtkWidget * about_dialog_create (Pika *pika,
PikaCoreConfig *config);
#endif /* __ABOUT_DIALOG_H__ */

View File

@ -221,7 +221,7 @@ dialogs_about_get (PikaDialogFactory *factory,
PikaUIManager *ui_manager,
gint view_size)
{
return about_dialog_create (context->pika);
return about_dialog_create (context->pika, context->pika->edit_config);
}
GtkWidget *

View File

@ -1856,6 +1856,9 @@ prefs_dialog_new (Pika *pika,
prefs_check_button_add_with_icon (object, "global-gradient",
_("_Gradient"), PIKA_ICON_GRADIENT,
GTK_BOX (vbox2), size_group);
prefs_check_button_add_with_icon (object, "global-expand",
_("E_xpand Layers"), PIKA_ICON_TOOL_SCALE,
GTK_BOX (vbox2), size_group);
/* Move Tool */
vbox2 = prefs_frame_new (_("Move Tool"),

View File

@ -46,6 +46,11 @@ static void pika_display_bounds_changed_handler (PikaImage *image,
gint old_x,
gint old_y,
PikaDisplay *display);
static void pika_display_flush_handler (PikaImage *image,
gboolean invalidate_preview,
PikaDisplay *display);
static gboolean
pika_display_flush_handler_idle (gpointer user_data);
/* public functions */
@ -122,3 +127,23 @@ pika_display_bounds_changed_handler (PikaImage *image,
{
pika_display_update_bounding_box (display);
}
static void
pika_display_flush_handler (PikaImage *image,
gboolean invalidate_preview,
PikaDisplay *display)
{
g_idle_add_full (G_PRIORITY_LOW,
(GSourceFunc) pika_display_flush_handler_idle,
g_object_ref (display), g_object_unref);
}
static gboolean
pika_display_flush_handler_idle (gpointer user_data)
{
PikaDisplay *display = user_data;
pika_display_flush (display);
return G_SOURCE_REMOVE;
}

View File

@ -26,13 +26,19 @@
#include <string.h>
#include <cairo.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <gegl.h>
#include <gegl-plugin.h>
#include "libpikacolor/pikacolor.h"
#include "pika-gegl-types.h"
#include "core/pikapattern.h"
#include "core/pikaprogress.h"
#include "pika-babl.h"
#include "pika-gegl-loops.h"
#include "pika-gegl-utils.h"
@ -337,6 +343,100 @@ pika_gegl_buffer_dup (GeglBuffer *buffer)
return new_buffer;
}
GeglBuffer *
pika_gegl_buffer_resize (GeglBuffer *buffer,
gint new_width,
gint new_height,
gint offset_x,
gint offset_y,
PikaRGB *color,
PikaPattern *pattern,
gint pattern_offset_x,
gint pattern_offset_y)
{
GeglBuffer *new_buffer;
gboolean intersect;
GeglRectangle copy_rect;
const GeglRectangle *extent;
const Babl *format;
g_return_val_if_fail (GEGL_IS_BUFFER (buffer), NULL);
extent = gegl_buffer_get_extent (buffer);
format = gegl_buffer_get_format (buffer);
new_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, new_width, new_height),
format);
intersect = gegl_rectangle_intersect (&copy_rect,
GEGL_RECTANGLE (0, 0, extent->width,
extent->height),
GEGL_RECTANGLE (offset_x, offset_y,
new_width, new_height));
if (! intersect ||
copy_rect.width != new_width ||
copy_rect.height != new_height)
{
/* Clear the new buffer if needed and color/pattern is given */
if (pattern)
{
GeglBuffer *src_buffer;
GeglBuffer *dest_buffer;
PikaColorProfile *src_profile;
PikaColorProfile *dest_profile;
src_buffer = pika_pattern_create_buffer (pattern);
src_profile = pika_babl_format_get_color_profile (
gegl_buffer_get_format (src_buffer));
dest_profile = pika_babl_format_get_color_profile (
gegl_buffer_get_format (new_buffer));
if (pika_color_transform_can_gegl_copy (src_profile, dest_profile))
{
dest_buffer = g_object_ref (src_buffer);
}
else
{
dest_buffer = gegl_buffer_new (gegl_buffer_get_extent (src_buffer),
gegl_buffer_get_format (new_buffer));
pika_gegl_convert_color_profile (src_buffer, NULL, src_profile,
dest_buffer, NULL, dest_profile,
PIKA_COLOR_RENDERING_INTENT_PERCEPTUAL,
TRUE, NULL);
}
g_object_unref (src_profile);
g_object_unref (dest_profile);
gegl_buffer_set_pattern (new_buffer, NULL, dest_buffer,
pattern_offset_x, pattern_offset_y);
g_object_unref (src_buffer);
g_object_unref (dest_buffer);
}
else if (color)
{
GeglColor *gegl_color;
gegl_color = pika_gegl_color_new (color, gegl_buffer_get_format (buffer));
gegl_buffer_set_color (new_buffer, NULL, gegl_color);
g_object_unref (gegl_color);
}
}
if (intersect && copy_rect.width && copy_rect.height)
{
/* Copy the pixels in the intersection */
pika_gegl_buffer_copy (buffer, &copy_rect, GEGL_ABYSS_NONE, new_buffer,
GEGL_RECTANGLE (copy_rect.x - offset_x,
copy_rect.y - offset_y, 0, 0));
}
return new_buffer;
}
gboolean
pika_gegl_buffer_set_extent (GeglBuffer *buffer,
const GeglRectangle *extent)

View File

@ -59,6 +59,15 @@ gboolean pika_gegl_param_spec_has_key (GParamSpec *pspe
const gchar *value);
GeglBuffer * pika_gegl_buffer_dup (GeglBuffer *buffer);
GeglBuffer * pika_gegl_buffer_resize (GeglBuffer *buffer,
gint new_width,
gint new_height,
gint offset_x,
gint offset_y,
PikaRGB *color,
PikaPattern *pattern,
gint pattern_offset_x,
gint pattern_offset_y);
gboolean pika_gegl_buffer_set_extent (GeglBuffer *buffer,
const GeglRectangle *extent);

View File

@ -85,5 +85,12 @@ typedef enum /*< skip, pdb-skip >*/
PIKA_PAINT_STATE_FINISH /* Cleanup and/or reset PaintFunc operation */
} PikaPaintState;
/* State of lock blinking */
typedef enum /*< skip, pdb-skip >*/
{
PIKA_PAINT_LOCK_NOT_BLINKED,
PIKA_PAINT_LOCK_BLINK_PENDING,
PIKA_PAINT_LOCK_BLINKED,
} PikaPaintLockBlinkState;
#endif /* __PAINT_ENUMS_H__ */

View File

@ -819,6 +819,8 @@ pika_brush_core_get_paint_buffer (PikaPaintCore *paint_core,
gint x1, y1, x2, y2;
gint drawable_width, drawable_height;
gint brush_width, brush_height;
gint offset_change_x, offset_change_y;
PikaCoords new_coords;
pika_brush_transform_size (core->brush,
core->scale, core->aspect_ratio,
@ -835,11 +837,31 @@ pika_brush_core_get_paint_buffer (PikaPaintCore *paint_core,
x = (gint) floor (coords->x) - (brush_width / 2);
y = (gint) floor (coords->y) - (brush_height / 2);
x1 = x - 1;
y1 = y - 1;
x2 = x + brush_width + 1;
y2 = y + brush_height + 1;
pika_paint_core_expand_drawable (paint_core, drawable, paint_options,
x1, x2, y1, y2,
&offset_change_x, &offset_change_y);
if (offset_change_x || offset_change_y)
{
x += offset_change_x;
y += offset_change_y;
new_coords = *coords;
new_coords.x = coords->x + offset_change_x;
new_coords.y = coords->y + offset_change_y;
pika_symmetry_set_origin (paint_core->sym, drawable, &new_coords);
}
drawable_width = pika_item_get_width (PIKA_ITEM (drawable));
drawable_height = pika_item_get_height (PIKA_ITEM (drawable));
x1 = CLAMP (x - 1, 0, drawable_width);
y1 = CLAMP (y - 1, 0, drawable_height);
x1 = CLAMP (x - 1, 0, drawable_width);
y1 = CLAMP (y - 1, 0, drawable_height);
x2 = CLAMP (x + brush_width + 1, 0, drawable_width);
y2 = CLAMP (y + brush_height + 1, 0, drawable_height);

View File

@ -470,6 +470,21 @@ pika_blob_duplicate (PikaBlob *b)
return g_memdup2 (b, sizeof (PikaBlob) + sizeof (PikaBlobSpan) * (b->height - 1));
}
void
pika_blob_move (PikaBlob *b,
gint x,
gint y)
{
gint i = 0;
b->y += y;
for (i = 0; i < b->height; i++)
{
b->data[i].left += x;
b->data[i].right += x;
}
}
#if 0
void
pika_blob_dump (PikaBlob *b)

View File

@ -86,6 +86,9 @@ void pika_blob_bounds (PikaBlob *b,
PikaBlob * pika_blob_convex_union (PikaBlob *b1,
PikaBlob *b2);
PikaBlob * pika_blob_duplicate (PikaBlob *b);
void pika_blob_move (PikaBlob *b,
gint x,
gint y);
#endif /* __PIKA_INK_BLOB_H__ */

View File

@ -242,17 +242,49 @@ pika_ink_get_paint_buffer (PikaPaintCore *paint_core,
gint *paint_width,
gint *paint_height)
{
PikaInk *ink = PIKA_INK (paint_core);
gint x, y;
gint width, height;
gint dwidth, dheight;
gint x1, y1, x2, y2;
PikaInk *ink = PIKA_INK (paint_core);
gint x, y;
gint width, height;
gint dwidth, dheight;
gint x1, y1, x2, y2;
gint offset_change_x, offset_change_y;
PikaCoords new_coords;
GList *iter;
pika_blob_bounds (ink->cur_blob, &x, &y, &width, &height);
x1 = x / SUBSAMPLE - 1;
y1 = y / SUBSAMPLE - 1;
x2 = (x + width) / SUBSAMPLE + 2;
y2 = (y + height) / SUBSAMPLE + 2;
pika_paint_core_expand_drawable (paint_core, drawable, paint_options,
x1, x2, y1, y2,
&offset_change_x, &offset_change_y);
dwidth = pika_item_get_width (PIKA_ITEM (drawable));
dheight = pika_item_get_height (PIKA_ITEM (drawable));
if (offset_change_x || offset_change_y)
{
x += SUBSAMPLE * offset_change_x;
y += SUBSAMPLE * offset_change_y;
new_coords = *coords;
new_coords.x = coords->x + offset_change_x;
new_coords.y = coords->y + offset_change_y;
pika_symmetry_set_origin (paint_core->sym, drawable, &new_coords);
for (iter = ink->blobs_to_render; iter; iter = g_list_next (iter))
pika_blob_move (iter->data,
SUBSAMPLE * offset_change_x,
SUBSAMPLE * offset_change_y);
for (iter = ink->last_blobs; iter; iter = g_list_next (iter))
pika_blob_move (iter->data,
SUBSAMPLE * offset_change_x,
SUBSAMPLE * offset_change_y);
}
x1 = CLAMP (x / SUBSAMPLE - 1, 0, dwidth);
y1 = CLAMP (y / SUBSAMPLE - 1, 0, dheight);
x2 = CLAMP ((x + width) / SUBSAMPLE + 2, 0, dwidth);
@ -319,7 +351,6 @@ pika_ink_motion (PikaPaintCore *paint_core,
PikaInk *ink = PIKA_INK (paint_core);
PikaInkOptions *options = PIKA_INK_OPTIONS (paint_options);
PikaContext *context = PIKA_CONTEXT (paint_options);
GList *blob_unions = NULL;
GList *blobs_to_render = NULL;
GeglBuffer *paint_buffer;
gint paint_buffer_x;
@ -338,6 +369,7 @@ pika_ink_motion (PikaPaintCore *paint_core,
coords.x -= off_x;
coords.y -= off_y;
pika_symmetry_set_origin (sym, drawable, &coords);
paint_core->sym = sym;
n_strokes = pika_symmetry_get_size (sym);
@ -377,7 +409,8 @@ pika_ink_motion (PikaPaintCore *paint_core,
last_blob);
ink->start_blobs = g_list_prepend (ink->start_blobs,
pika_blob_duplicate (last_blob));
blobs_to_render = g_list_prepend (blobs_to_render, last_blob);
blobs_to_render = g_list_prepend (blobs_to_render,
pika_blob_duplicate (last_blob));
}
ink->start_blobs = g_list_reverse (ink->start_blobs);
ink->last_blobs = g_list_reverse (ink->last_blobs);
@ -411,7 +444,6 @@ pika_ink_motion (PikaPaintCore *paint_core,
g_list_nth (ink->last_blobs, i)->data = blob;
blobs_to_render = g_list_prepend (blobs_to_render, blob_union);
blob_unions = g_list_prepend (blob_unions, blob_union);
}
blobs_to_render = g_list_reverse (blobs_to_render);
}
@ -422,6 +454,7 @@ pika_ink_motion (PikaPaintCore *paint_core,
pika_pickable_srgb_to_image_color (PIKA_PICKABLE (drawable),
&foreground, &foreground);
color = pika_gegl_color_new (&foreground, pika_drawable_get_space (drawable));
ink->blobs_to_render = blobs_to_render;
for (i = 0; i < n_strokes; i++)
{
@ -467,8 +500,7 @@ pika_ink_motion (PikaPaintCore *paint_core,
g_object_unref (color);
g_list_free_full (blob_unions, g_free);
g_list_free (blobs_to_render);
g_list_free_full (blobs_to_render, g_free);
}
static PikaBlob *

View File

@ -41,10 +41,11 @@ struct _PikaInk
{
PikaPaintCore parent_instance;
GList *start_blobs; /* starting blobs per stroke (for undo) */
GList *start_blobs; /* starting blobs per stroke (for undo) */
PikaBlob *cur_blob; /* current blob */
GList *last_blobs; /* blobs for last stroke positions */
PikaBlob *cur_blob; /* current blob */
GList *last_blobs; /* blobs for last stroke positions */
GList *blobs_to_render;
};
struct _PikaInkClass

View File

@ -259,7 +259,7 @@ pika_mybrush_core_motion (PikaPaintCore *paint_core,
{
PikaMybrushCore *mybrush = PIKA_MYBRUSH_CORE (paint_core);
MyPaintRectangle rect;
PikaCoords coords;
PikaCoords origin;
GList *iter;
gdouble dt = 0.0;
gint off_x, off_y;
@ -269,10 +269,10 @@ pika_mybrush_core_motion (PikaPaintCore *paint_core,
pika_item_get_offset (PIKA_ITEM (drawable), &off_x, &off_y);
n_strokes = pika_symmetry_get_size (sym);
coords = *(pika_symmetry_get_origin (sym));
coords.x -= off_x;
coords.y -= off_y;
pika_symmetry_set_origin (sym, drawable, &coords);
origin = *(pika_symmetry_get_origin (sym));
origin.x -= off_x;
origin.y -= off_y;
pika_symmetry_set_origin (sym, drawable, &origin);
/* The number of strokes may change during a motion, depending on
* the type of symmetry. When that happens, reset the brushes.
@ -291,9 +291,8 @@ pika_mybrush_core_motion (PikaPaintCore *paint_core,
iter;
iter = g_list_next (iter), i++)
{
MyPaintBrush *brush = iter->data;
coords = *(pika_symmetry_get_coords (sym, i));
MyPaintBrush *brush = iter->data;
PikaCoords coords = *(pika_symmetry_get_coords (sym, i));
mypaint_brush_stroke_to (brush,
(MyPaintSurface *) mybrush->private->surface,
@ -326,6 +325,42 @@ pika_mybrush_core_motion (PikaPaintCore *paint_core,
MyPaintBrush *brush = iter->data;
PikaCoords coords = *(pika_symmetry_get_coords (sym, i));
gdouble pressure = coords.pressure;
gboolean expanded;
gfloat radius = 100;
gint x1, x2, y1, y2;
gint offset_change_x, offset_change_y;
gint off_x_surf, off_y_surf;
gint off_x, off_y;
x1 = coords.x - radius;
y1 = coords.y - radius;
x2 = coords.x + radius;
y2 = coords.y + radius;
expanded = pika_paint_core_expand_drawable (paint_core, drawable, paint_options,
x1, x2, y1, y2,
&offset_change_x, &offset_change_y);
pika_item_get_offset (PIKA_ITEM (drawable), &off_x, &off_y);
if (expanded)
pika_mypaint_surface_set_buffer (mybrush->private->surface, pika_drawable_get_buffer (drawable),
off_x, off_y);
pika_mypaint_surface_get_offset (mybrush->private->surface, &off_x_surf, &off_y_surf);
coords.x -= off_x_surf;
coords.y -= off_y_surf;
if (offset_change_x || offset_change_y)
{
pika_mypaint_surface_set_offset (mybrush->private->surface,
off_x_surf + offset_change_x,
off_y_surf + offset_change_y);
origin = *(pika_symmetry_get_origin (sym));
origin.x += offset_change_x;
origin.y += offset_change_y;
pika_symmetry_set_origin (sym, drawable, &origin);
}
mypaint_brush_stroke_to (brush,
(MyPaintSurface *) mybrush->private->surface,

View File

@ -38,13 +38,15 @@
struct _PikaMybrushSurface
{
MyPaintSurface surface;
GeglBuffer *buffer;
GeglBuffer *paint_mask;
gint paint_mask_x;
gint paint_mask_y;
GeglRectangle dirty;
PikaComponentMask component_mask;
MyPaintSurface surface;
GeglBuffer *buffer;
GeglBuffer *paint_mask;
gint paint_mask_x;
gint paint_mask_y;
gint off_x;
gint off_y;
GeglRectangle dirty;
PikaComponentMask component_mask;
PikaMybrushOptions *options;
};
@ -382,6 +384,8 @@ pika_mypaint_surface_draw_dab (MyPaintSurface *base_surface,
colorize = opaque * colorize;
/* FIXME: This should use the real matrix values to trim aspect_ratio dabs */
x += surface->off_x;
y += surface->off_y;
dabRect = calculate_dab_roi (x, y, radius);
gegl_rectangle_intersect (&dabRect, &dabRect, gegl_buffer_get_extent (surface->buffer));
@ -561,5 +565,40 @@ pika_mypaint_surface_new (GeglBuffer *buffer,
surface->paint_mask_y = paint_mask_y;
surface->dirty = *GEGL_RECTANGLE (0, 0, 0, 0);
surface->off_x = 0;
surface->off_y = 0;
return surface;
}
void
pika_mypaint_surface_set_buffer (PikaMybrushSurface *surface,
GeglBuffer *buffer,
gint paint_mask_x,
gint paint_mask_y)
{
g_object_unref (surface->buffer);
surface->buffer = g_object_ref (buffer);
surface->paint_mask_x = paint_mask_x;
surface->paint_mask_y = paint_mask_y;
}
void
pika_mypaint_surface_set_offset (PikaMybrushSurface *surface,
gint off_x,
gint off_y)
{
surface->off_x = off_x;
surface->off_y = off_y;
}
void
pika_mypaint_surface_get_offset (PikaMybrushSurface *surface,
gint *off_x,
gint *off_y)
{
*off_x = surface->off_x;
*off_y = surface->off_y;
}

View File

@ -33,5 +33,18 @@ pika_mypaint_surface_new (GeglBuffer *buffer,
gint paint_mask_y,
PikaMybrushOptions *options);
void
pika_mypaint_surface_set_buffer (PikaMybrushSurface *surface,
GeglBuffer *buffer,
gint paint_mask_x,
gint paint_mask_y);
void
pika_mypaint_surface_set_offset (PikaMybrushSurface *surface,
gint off_x,
gint off_y);
void
pika_mypaint_surface_get_offset (PikaMybrushSurface *surface,
gint *off_x,
gint *off_y);
#endif /* __PIKA_MYBRUSH_SURFACE_H__ */

View File

@ -193,7 +193,8 @@ pika_paintbrush_real_get_paint_params (PikaPaintbrush *paintbrush,
*paint_mode = pika_context_get_paint_mode (context);
if (pika_paint_options_get_gradient_color (paint_options, image,
if (pika_paint_options_are_dynamics_enabled (paint_options) &&
pika_paint_options_get_gradient_color (paint_options, image,
grad_point,
paint_core->pixel_dist,
paint_color))
@ -252,6 +253,7 @@ _pika_paintbrush_motion (PikaPaintCore *paint_core,
coords.x -= off_x;
coords.y -= off_y;
pika_symmetry_set_origin (sym, drawable, &coords);
paint_core->sym = sym;
/* Some settings are based on the original stroke. */
opacity *= pika_dynamics_get_linear_value (dynamics,
@ -313,6 +315,8 @@ _pika_paintbrush_motion (PikaPaintCore *paint_core,
&paint_buffer_y,
&paint_width,
&paint_height);
coords = *(pika_symmetry_get_coords (sym, i));
if (! paint_buffer)
continue;

View File

@ -24,10 +24,12 @@
#include <string.h>
#include <cairo.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <gegl.h>
#include "libpikabase/pikabase.h"
#include "libpikacolor/pikacolor.h"
#include "libpikamath/pikamath.h"
#include "paint-types.h"
@ -47,6 +49,9 @@
#include "core/pikaimage-guides.h"
#include "core/pikaimage-symmetry.h"
#include "core/pikaimage-undo.h"
#include "core/pikaimage-undo-push.h"
#include "core/pikalayer.h"
#include "core/pikalayermask.h"
#include "core/pikapickable.h"
#include "core/pikaprojection.h"
#include "core/pikasymmetry.h"
@ -158,7 +163,10 @@ static void
pika_paint_core_init (PikaPaintCore *core)
{
core->ID = global_core_ID++;
core->undo_buffers = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref);
core->undo_buffers = g_hash_table_new_full (g_direct_hash, g_direct_equal,
NULL, g_object_unref);
core->original_bounds = g_hash_table_new_full (g_direct_hash, g_direct_equal,
NULL, g_free);
}
static void
@ -170,6 +178,7 @@ pika_paint_core_finalize (GObject *object)
g_clear_pointer (&core->undo_desc, g_free);
g_hash_table_unref (core->undo_buffers);
g_hash_table_unref (core->original_bounds);
if (core->applicators)
g_hash_table_unref (core->applicators);
@ -357,10 +366,11 @@ pika_paint_core_start (PikaPaintCore *core,
const PikaCoords *coords,
GError **error)
{
PikaImage *image;
PikaChannel *mask;
gint max_width = 0;
gint max_height = 0;
PikaImage *image;
PikaChannel *mask;
gint max_width = 0;
gint max_height = 0;
GeglRectangle *rect;
g_return_val_if_fail (PIKA_IS_PAINT_CORE (core), FALSE);
g_return_val_if_fail (g_list_length (drawables) > 0, FALSE);
@ -418,8 +428,16 @@ pika_paint_core_start (PikaPaintCore *core,
for (GList *iter = drawables; iter; iter = iter->next)
{
/* Allocate the undo structures */
rect = g_new (GeglRectangle, 1);
rect->width = pika_item_get_width (PIKA_ITEM (iter->data));
rect->height = pika_item_get_height (PIKA_ITEM (iter->data));
pika_item_get_offset (PIKA_ITEM (iter->data), &rect->x, &rect->y);
g_hash_table_insert (core->original_bounds, iter->data,
rect);
g_hash_table_insert (core->undo_buffers, iter->data,
pika_gegl_buffer_dup (pika_drawable_get_buffer (iter->data)));
max_width = MAX (max_width, pika_item_get_width (iter->data));
max_height = MAX (max_height, pika_item_get_height (iter->data));
}
@ -484,6 +502,9 @@ pika_paint_core_start (PikaPaintCore *core,
}
}
/* initialize the lock_blink_state */
core->lock_blink_state = PIKA_PAINT_LOCK_NOT_BLINKED;
/* Freeze the drawable preview so that it isn't constantly updated. */
for (GList *iter = drawables; iter; iter = iter->next)
pika_viewable_preview_freeze (PIKA_VIEWABLE (iter->data));
@ -532,7 +553,9 @@ pika_paint_core_finish (PikaPaintCore *core,
{
GeglBuffer *undo_buffer;
GeglBuffer *buffer;
GeglBuffer *drawable_buffer;
GeglRectangle rect;
GeglRectangle old_rect;
if (! g_hash_table_steal_extended (core->undo_buffers, iter->data,
NULL, (gpointer*) &undo_buffer))
@ -542,16 +565,6 @@ pika_paint_core_finish (PikaPaintCore *core,
continue;
}
pika_rectangle_intersect (core->x1, core->y1,
core->x2 - core->x1, core->y2 - core->y1,
0, 0,
pika_item_get_width (PIKA_ITEM (iter->data)),
pika_item_get_height (PIKA_ITEM (iter->data)),
&rect.x, &rect.y, &rect.width, &rect.height);
gegl_rectangle_align_to_buffer (&rect, &rect, undo_buffer,
GEGL_RECTANGLE_ALIGNMENT_SUPERSET);
if (! undo_group_started)
{
pika_image_undo_group_start (image, PIKA_UNDO_GROUP_PAINT,
@ -559,19 +572,115 @@ pika_paint_core_finish (PikaPaintCore *core,
undo_group_started = TRUE;
}
PIKA_PAINT_CORE_GET_CLASS (core)->push_undo (core, image, NULL);
/* get new and old bounds of drawable */
old_rect = *(GeglRectangle*) g_hash_table_lookup (core->original_bounds, iter->data);
buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, rect.width, rect.height),
pika_drawable_get_format (iter->data));
pika_item_get_offset (PIKA_ITEM (iter->data), &rect.x, &rect.y);
rect.width = pika_item_get_width (PIKA_ITEM (iter->data));
rect.height = pika_item_get_height (PIKA_ITEM (iter->data));
pika_gegl_buffer_copy (undo_buffer,
&rect,
GEGL_ABYSS_NONE,
buffer,
GEGL_RECTANGLE (0, 0, 0, 0));
/* Making copy of entire buffer consumes more memory, so do that only when buffer has resized */
if (rect.x == old_rect.x &&
rect.y == old_rect.y &&
rect.width == old_rect.width &&
rect.height == old_rect.height)
{
pika_rectangle_intersect (core->x1, core->y1, core->x2 - core->x1,
core->y2 - core->y1, 0, 0,
pika_item_get_width (PIKA_ITEM (iter->data)),
pika_item_get_height (PIKA_ITEM (iter->data)),
&rect.x, &rect.y, &rect.width, &rect.height);
pika_drawable_push_undo (iter->data, NULL,
buffer, rect.x, rect.y, rect.width, rect.height);
gegl_rectangle_align_to_buffer (&rect, &rect, undo_buffer,
GEGL_RECTANGLE_ALIGNMENT_SUPERSET);
PIKA_PAINT_CORE_GET_CLASS (core)->push_undo (core, image, NULL);
buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, rect.width, rect.height),
pika_drawable_get_format (iter->data));
pika_gegl_buffer_copy (undo_buffer,
&rect,
GEGL_ABYSS_NONE,
buffer,
GEGL_RECTANGLE (0, 0, 0, 0));
pika_drawable_push_undo (iter->data, NULL,
buffer, rect.x, rect.y, rect.width, rect.height);
}
else
{
/* drawable is expanded only if drawable is layer or layer mask*/
g_return_if_fail (PIKA_IS_LAYER (iter->data) || PIKA_IS_LAYER_MASK (iter->data));
/* create a copy of original buffer from undo data */
buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0,
old_rect.width,
old_rect.height),
pika_drawable_get_format (iter->data));
pika_gegl_buffer_copy (undo_buffer,
GEGL_RECTANGLE (old_rect.x - rect.x,
old_rect.y - rect.y,
old_rect.width,
old_rect.height),
GEGL_ABYSS_NONE,
buffer,
GEGL_RECTANGLE (0, 0, 0, 0));
/* make a backup copy of drawable to restore */
drawable_buffer = g_object_ref (pika_drawable_get_buffer (PIKA_DRAWABLE (iter->data)));
if (PIKA_IS_LAYER_MASK (drawables->data) || PIKA_LAYER (drawables->data)->mask)
{
GeglBuffer *other_new;
GeglBuffer *other_old;
PikaDrawable *other_drawable;
if (PIKA_IS_LAYER_MASK (drawables->data))
other_drawable = PIKA_DRAWABLE ((PIKA_LAYER_MASK (drawables->data))->layer);
else
other_drawable = PIKA_DRAWABLE (PIKA_LAYER (drawables->data)->mask);
/* create a copy of original buffer by taking the required area */
other_old = gegl_buffer_new (GEGL_RECTANGLE (0, 0,
old_rect.width,
old_rect.height),
pika_drawable_get_format (other_drawable));
pika_gegl_buffer_copy (pika_drawable_get_buffer (other_drawable),
GEGL_RECTANGLE (old_rect.x - rect.x,
old_rect.y - rect.y,
old_rect.width,
old_rect.height),
GEGL_ABYSS_NONE,
other_old,
GEGL_RECTANGLE (0, 0, 0, 0));
/* make a backup copy of drawable to restore */
other_new = g_object_ref (pika_drawable_get_buffer (other_drawable));
pika_drawable_set_buffer_full (other_drawable, FALSE, NULL,
other_old, &old_rect,
FALSE);
pika_drawable_set_buffer_full (other_drawable, TRUE, NULL,
other_new, &rect,
FALSE);
g_object_unref (other_new);
g_object_unref (other_old);
}
/* Restore drawable to state before painting started */
pika_drawable_set_buffer_full (iter->data, FALSE, NULL,
buffer, &old_rect,
FALSE);
/* Change the drawable again but push undo this time */
pika_drawable_set_buffer_full (iter->data, TRUE, NULL,
drawable_buffer, &rect,
FALSE);
g_object_unref (drawable_buffer);
}
g_object_unref (buffer);
g_object_unref (undo_buffer);
@ -614,7 +723,8 @@ pika_paint_core_cancel (PikaPaintCore *core,
&x, &y, &width, &height))
{
GeglBuffer *undo_buffer;
GeglRectangle rect;
GeglRectangle new_rect;
GeglRectangle old_rect;
if (! g_hash_table_steal_extended (core->undo_buffers, iter->data,
NULL, (gpointer*) &undo_buffer))
@ -624,21 +734,108 @@ pika_paint_core_cancel (PikaPaintCore *core,
continue;
}
gegl_rectangle_align_to_buffer (&rect,
GEGL_RECTANGLE (x, y, width, height),
pika_drawable_get_buffer (iter->data),
GEGL_RECTANGLE_ALIGNMENT_SUPERSET);
old_rect = *(GeglRectangle*) g_hash_table_lookup (core->original_bounds, iter->data);
pika_item_get_offset (PIKA_ITEM (iter->data), &new_rect.x, &new_rect.y);
new_rect.width = pika_item_get_width (PIKA_ITEM (iter->data));
new_rect.height = pika_item_get_height (PIKA_ITEM (iter->data));
if (new_rect.x == old_rect.x &&
new_rect.y == old_rect.y &&
new_rect.width == old_rect.width &&
new_rect.height == old_rect.height)
{
GeglRectangle rect;
gegl_rectangle_align_to_buffer (&rect,
GEGL_RECTANGLE (x, y, width, height),
pika_drawable_get_buffer (iter->data),
GEGL_RECTANGLE_ALIGNMENT_SUPERSET);
pika_gegl_buffer_copy (undo_buffer,
&rect,
GEGL_ABYSS_NONE,
pika_drawable_get_buffer (iter->data),
&rect);
pika_drawable_update (iter->data, x, y, width, height);
}
else
{
GeglBuffer *buffer;
GeglRectangle bbox;
/* drawable is expanded only if drawable is layer or layer mask,
* so drawable cannot be anything else */
g_return_if_fail (PIKA_IS_LAYER (iter->data) || PIKA_IS_LAYER_MASK (iter->data));
/* When canceling painting with drawable expansion, ensure that
* the drawable display outside the reverted size is not shown. We
* cannot use pika_drawable_update() because it won't work while
* painting. Directly emit the "update" signal.
* */
bbox = pika_drawable_get_bounding_box (iter->data);
g_signal_emit_by_name (iter->data, "update", bbox.x, bbox.y, bbox.width, bbox.height);
/* create a copy of original buffer from undo data */
buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0,
old_rect.width,
old_rect.height),
pika_drawable_get_format (iter->data));
pika_gegl_buffer_copy (undo_buffer,
GEGL_RECTANGLE (old_rect.x - new_rect.x,
old_rect.y - new_rect.y,
old_rect.width,
old_rect.height),
GEGL_ABYSS_NONE,
buffer,
GEGL_RECTANGLE (0, 0, 0, 0));
if (PIKA_IS_LAYER_MASK (drawables->data) || PIKA_LAYER (drawables->data)->mask)
{
GeglBuffer *other_old;
PikaDrawable *other_drawable;
if (PIKA_IS_LAYER_MASK (drawables->data))
other_drawable = PIKA_DRAWABLE ((PIKA_LAYER_MASK (drawables->data))->layer);
else
other_drawable = PIKA_DRAWABLE (PIKA_LAYER (drawables->data)->mask);
/* create a copy of original buffer by taking the required area */
other_old = gegl_buffer_new (GEGL_RECTANGLE (0, 0,
old_rect.width,
old_rect.height),
pika_drawable_get_format (other_drawable));
pika_gegl_buffer_copy (pika_drawable_get_buffer (other_drawable),
GEGL_RECTANGLE (old_rect.x - new_rect.x,
old_rect.y - new_rect.y,
old_rect.width,
old_rect.height),
GEGL_ABYSS_NONE,
other_old,
GEGL_RECTANGLE (0, 0, 0, 0));
pika_drawable_set_buffer_full (other_drawable, FALSE, NULL,
other_old, &old_rect,
FALSE);
g_object_unref (other_old);
}
/* Restore drawable to state before painting started */
pika_drawable_set_buffer_full (iter->data, FALSE, NULL,
buffer, &old_rect,
FALSE);
pika_drawable_update (iter->data, 0, 0, -1, -1);
g_object_unref (buffer);
}
pika_gegl_buffer_copy (undo_buffer,
&rect,
GEGL_ABYSS_NONE,
pika_drawable_get_buffer (iter->data),
&rect);
g_object_unref (undo_buffer);
}
pika_drawable_update (iter->data, x, y, width, height);
pika_viewable_preview_thaw (PIKA_VIEWABLE (iter->data));
}
@ -651,6 +848,7 @@ pika_paint_core_cleanup (PikaPaintCore *core)
g_return_if_fail (PIKA_IS_PAINT_CORE (core));
g_hash_table_remove_all (core->undo_buffers);
g_hash_table_remove_all (core->original_bounds);
g_clear_object (&core->saved_proj_buffer);
g_clear_object (&core->canvas_buffer);
@ -692,6 +890,227 @@ pika_paint_core_get_show_all (PikaPaintCore *core)
return core->show_all;
}
gboolean
pika_paint_core_expand_drawable (PikaPaintCore *core,
PikaDrawable *drawable,
PikaPaintOptions *options,
gint x1,
gint x2,
gint y1,
gint y2,
gint *new_off_x,
gint *new_off_y)
{
gint drawable_width, drawable_height;
gint drawable_offset_x, drawable_offset_y;
gint image_width, image_height;
gint new_width, new_height;
gint expand_amount;
gboolean show_all;
gboolean outside_image;
PikaImage *image = pika_item_get_image (PIKA_ITEM (drawable));
PikaLayer *layer;
drawable_width = pika_item_get_width (PIKA_ITEM (drawable));
drawable_height = pika_item_get_height (PIKA_ITEM (drawable));
pika_item_get_offset (PIKA_ITEM (drawable), &drawable_offset_x, &drawable_offset_y);
new_width = drawable_width;
new_height = drawable_height;
*new_off_x = 0;
*new_off_y = 0;
image_width = pika_image_get_width (image);
image_height = pika_image_get_height (image);
expand_amount = options->expand_amount;
show_all = pika_paint_core_get_show_all (core);
outside_image = x2 < -drawable_offset_x || x1 > image_width - drawable_offset_x ||
y2 < -drawable_offset_y || y1 > image_height - drawable_offset_y;
/* Don't expand if drawable is anything other
* than layer or layer mask */
if (PIKA_IS_LAYER_MASK (drawable))
layer = (PIKA_LAYER_MASK (drawable))->layer;
else if (PIKA_IS_LAYER (drawable))
layer = PIKA_LAYER (drawable);
else
return FALSE;
if (!pika_paint_core_get_show_all (core) && outside_image)
return FALSE;
if (!options->expand_use)
return FALSE;
if (x1 < 0)
{
if (show_all)
{
new_width += expand_amount - x1;
*new_off_x += expand_amount - x1;
}
else if (drawable_offset_x > 0)
{
new_width += expand_amount - x1;
*new_off_x += expand_amount - x1;
if (*new_off_x > drawable_offset_x)
{
new_width -= *new_off_x - drawable_offset_x;
*new_off_x = drawable_offset_x;
}
}
}
if (y1 < 0)
{
if (show_all)
{
new_height += expand_amount - y1;
*new_off_y += expand_amount - y1;
}
else if (drawable_offset_y > 0)
{
new_height += expand_amount - y1;
*new_off_y += expand_amount - y1;
if (*new_off_y > drawable_offset_y)
{
new_height -= *new_off_y - drawable_offset_y;
*new_off_y = drawable_offset_y;
}
}
}
if (x2 > drawable_width)
{
if (show_all)
{
new_width += x2 - drawable_width + expand_amount;
}
else if (drawable_width + drawable_offset_x < image_width)
{
new_width += x2 - drawable_width + expand_amount;
if (new_width + drawable_offset_x - *new_off_x > image_width)
new_width = image_width + *new_off_x - drawable_offset_x;
}
}
if (y2 > drawable_height)
{
if (show_all)
{
new_height += y2 - drawable_height + expand_amount;
}
else if (drawable_height + drawable_offset_y < image_height)
{
new_height += y2 - drawable_height + expand_amount;
if (new_height + drawable_offset_y - *new_off_y > image_height)
new_height = image_height + *new_off_y - drawable_offset_y;
}
}
if (new_width != drawable_width || *new_off_x ||
new_height != drawable_height || *new_off_y)
{
PikaRGB color;
PikaPattern *pattern;
PikaContext *context = PIKA_CONTEXT (options);
PikaFillType fill_type = options->expand_fill_type;
gboolean context_has_image;
PikaFillType mask_fill_type;
GeglBuffer *undo_buffer;
GeglBuffer *new_buffer;
if (pika_item_get_lock_position (PIKA_ITEM (layer)))
{
if (core->lock_blink_state == PIKA_PAINT_LOCK_NOT_BLINKED)
core->lock_blink_state = PIKA_PAINT_LOCK_BLINK_PENDING;
/* Since we are not expanding, set new offset to zero */
*new_off_x = 0;
*new_off_y = 0;
return FALSE;
}
mask_fill_type = options->expand_mask_fill_type == PIKA_ADD_MASK_BLACK ?
PIKA_FILL_TRANSPARENT :
PIKA_FILL_WHITE;
/* The image field of context is null but
* is required for Filling with Middle Gray */
context_has_image = context->image != NULL;
if (!context_has_image)
context->image = image;
/* ensure that every expansion is pushed to undo stack */
if (core->x2 == core->x1)
core->x2++;
if (core->y2 == core->y1)
core->y2++;
g_object_freeze_notify (G_OBJECT (layer));
pika_drawable_disable_resize_undo (PIKA_DRAWABLE (layer));
PIKA_LAYER_GET_CLASS (layer)->resize (layer, context, fill_type,
new_width, new_height,
*new_off_x, *new_off_y);
pika_drawable_enable_resize_undo (PIKA_DRAWABLE (layer));
if (layer->mask)
{
g_object_freeze_notify (G_OBJECT (layer->mask));
pika_drawable_disable_resize_undo (PIKA_DRAWABLE (layer->mask));
PIKA_ITEM_GET_CLASS (layer->mask)->resize (PIKA_ITEM (layer->mask), context,
mask_fill_type, new_width, new_height,
*new_off_x, *new_off_y);
pika_drawable_enable_resize_undo (PIKA_DRAWABLE (layer->mask));
g_object_thaw_notify (G_OBJECT (layer->mask));
}
g_object_thaw_notify (G_OBJECT (layer));
pika_image_flush (image);
if (PIKA_IS_LAYER_MASK (drawable))
{
fill_type = mask_fill_type;
}
else if (fill_type == PIKA_FILL_TRANSPARENT &&
! pika_drawable_has_alpha (drawable))
{
fill_type = PIKA_FILL_BACKGROUND;
}
new_buffer = pika_gegl_buffer_resize (core->canvas_buffer, new_width, new_height,
-(*new_off_x), -(*new_off_y), NULL, NULL, 0, 0);
g_object_unref (core->canvas_buffer);
core->canvas_buffer = new_buffer;
pika_get_fill_params (context, fill_type, &color, &pattern, NULL);
pika_pickable_srgb_to_image_color (PIKA_PICKABLE (drawable),
&color, &color);
if (! pika_drawable_has_alpha (drawable))
pika_rgb_set_alpha (&color, 1.0);
undo_buffer = g_hash_table_lookup (core->undo_buffers, drawable);
g_object_ref (undo_buffer);
new_buffer = pika_gegl_buffer_resize (undo_buffer, new_width, new_height,
-(*new_off_x), -(*new_off_y), &color,
pattern, 0, 0);
g_hash_table_insert (core->undo_buffers, drawable, new_buffer);
g_object_unref (undo_buffer);
/* Restore context to its original state */
if (!context_has_image)
context->image = NULL;
return TRUE;
}
return FALSE;
}
void
pika_paint_core_set_current_coords (PikaPaintCore *core,
const PikaCoords *coords)

View File

@ -69,12 +69,17 @@ struct _PikaPaintCore
GeglBuffer *paint_buffer; /* the buffer to paint pixels to */
gint paint_buffer_x;
gint paint_buffer_y;
GHashTable *original_bounds; /* the original bounds of drawables */
GeglBuffer *mask_buffer; /* the target drawable's mask */
GHashTable *applicators;
GArray *stroke_buffer;
PikaSymmetry *sym;
PikaPaintLockBlinkState
lock_blink_state;
};
struct _PikaPaintCoreClass
@ -156,6 +161,16 @@ void pika_paint_core_set_show_all (PikaPaintCore *core,
gboolean show_all);
gboolean pika_paint_core_get_show_all (PikaPaintCore *core);
gboolean pika_paint_core_expand_drawable (PikaPaintCore *paint_core,
PikaDrawable *drawable,
PikaPaintOptions *paint_options,
gint x1,
gint x2,
gint y1,
gint y2,
gint *x,
gint *y);
void pika_paint_core_set_current_coords (PikaPaintCore *core,
const PikaCoords *coords);
void pika_paint_core_get_current_coords (PikaPaintCore *core,

View File

@ -83,6 +83,11 @@
#define DEFAULT_SMOOTHING_QUALITY 20
#define DEFAULT_SMOOTHING_FACTOR 50
#define DEFAULT_EXPAND_USE FALSE
#define DEFAULT_EXPAND_AMOUNT 100.0
#define DEFAULT_EXPAND_FILL_TYPE PIKA_FILL_TRANSPARENT
#define DEFAULT_EXPAND_MASK_FILL_TYPE PIKA_ADD_MASK_WHITE
enum
{
PROP_0,
@ -134,7 +139,12 @@ enum
PROP_USE_SMOOTHING,
PROP_SMOOTHING_QUALITY,
PROP_SMOOTHING_FACTOR
PROP_SMOOTHING_FACTOR,
PROP_EXPAND_USE,
PROP_EXPAND_AMOUNT,
PROP_EXPAND_FILL_TYPE,
PROP_EXPAND_MASK_FILL_TYPE
};
@ -308,6 +318,36 @@ pika_paint_options_class_init (PikaPaintOptionsClass *klass)
DEFAULT_USE_JITTER,
PIKA_PARAM_STATIC_STRINGS);
PIKA_CONFIG_PROP_BOOLEAN (object_class, PROP_EXPAND_USE,
"expand-use",
_("Expand Layers"),
_("Expand active layer as you paint"),
DEFAULT_EXPAND_USE,
PIKA_PARAM_STATIC_STRINGS);
PIKA_CONFIG_PROP_DOUBLE (object_class, PROP_EXPAND_AMOUNT,
"expand-amount",
_("Amount"),
_("Amount of expansion"),
1.0, 1000.0, DEFAULT_EXPAND_AMOUNT,
PIKA_PARAM_STATIC_STRINGS);
PIKA_CONFIG_PROP_ENUM (object_class, PROP_EXPAND_FILL_TYPE,
"expand-fill-type",
_("Fill With"),
_("Fill layer with"),
PIKA_TYPE_FILL_TYPE,
DEFAULT_EXPAND_FILL_TYPE,
PIKA_PARAM_STATIC_STRINGS);
PIKA_CONFIG_PROP_ENUM (object_class, PROP_EXPAND_MASK_FILL_TYPE,
"expand-mask-fill-type",
_("Fill Mask With"),
_("Fill layer mask with"),
PIKA_TYPE_ADD_MASK_TYPE,
DEFAULT_EXPAND_MASK_FILL_TYPE,
PIKA_PARAM_STATIC_STRINGS);
PIKA_CONFIG_PROP_BOOLEAN (object_class, PROP_DYNAMICS_ENABLED,
"dynamics-enabled",
_("Enable dynamics"),
@ -642,6 +682,19 @@ pika_paint_options_set_property (GObject *object,
smoothing_options->smoothing_factor = g_value_get_double (value);
break;
case PROP_EXPAND_USE:
options->expand_use = g_value_get_boolean (value);
break;
case PROP_EXPAND_AMOUNT:
options->expand_amount = g_value_get_double (value);
break;
case PROP_EXPAND_FILL_TYPE:
options->expand_fill_type = g_value_get_enum (value);
break;
case PROP_EXPAND_MASK_FILL_TYPE:
options->expand_mask_fill_type = g_value_get_enum (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
@ -788,6 +841,19 @@ pika_paint_options_get_property (GObject *object,
g_value_set_double (value, smoothing_options->smoothing_factor);
break;
case PROP_EXPAND_USE:
g_value_set_boolean (value, options->expand_use);
break;
case PROP_EXPAND_AMOUNT:
g_value_set_double (value, options->expand_amount);
break;
case PROP_EXPAND_FILL_TYPE:
g_value_set_enum (value, options->expand_fill_type);
break;
case PROP_EXPAND_MASK_FILL_TYPE:
g_value_set_enum (value, options->expand_mask_fill_type);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
@ -1221,9 +1287,18 @@ static const gchar *gradient_props[] =
"gradient-repeat"
};
static const gchar *expand_props[] =
{
"expand-use",
"expand-amount",
"expand-fill-type",
"expand-mask-fill-type",
};
static const gint max_n_props = (G_N_ELEMENTS (brush_props) +
G_N_ELEMENTS (dynamics_props) +
G_N_ELEMENTS (gradient_props));
G_N_ELEMENTS (gradient_props) +
G_N_ELEMENTS (expand_props));
gboolean
pika_paint_options_is_prop (const gchar *prop_name,
@ -1254,6 +1329,13 @@ pika_paint_options_is_prop (const gchar *prop_name,
return TRUE;
}
if (prop_mask & PIKA_CONTEXT_PROP_MASK_EXPAND)
{
for (i = 0; i < G_N_ELEMENTS (expand_props); i++)
if (! strcmp (prop_name, expand_props[i]))
return TRUE;
}
return FALSE;
}
@ -1288,6 +1370,12 @@ pika_paint_options_copy_props (PikaPaintOptions *src,
names[n_props++] = gradient_props[i];
}
if (prop_mask & PIKA_CONTEXT_PROP_MASK_EXPAND)
{
for (i = 0; i < G_N_ELEMENTS (expand_props); i++)
names[n_props++] = expand_props[i];
}
if (n_props > 0)
{
g_object_getv (G_OBJECT (src), n_props, names, values);

View File

@ -109,6 +109,11 @@ struct _PikaPaintOptions
gboolean hard;
gboolean expand_use;
gdouble expand_amount;
PikaFillType expand_fill_type;
PikaAddMaskType expand_mask_fill_type;
PikaJitterOptions *jitter_options;
gboolean dynamics_enabled;

View File

@ -406,6 +406,7 @@ pika_smudge_motion (PikaPaintCore *paint_core,
coords.x -= off_x;
coords.y -= off_y;
pika_symmetry_set_origin (sym, drawable, &coords);
paint_core->sym = sym;
opacity = pika_dynamics_get_linear_value (dynamics,
PIKA_DYNAMICS_OUTPUT_OPACITY,

View File

@ -347,6 +347,7 @@ pika_source_core_motion (PikaSourceCore *source_core,
coords.x -= off_x;
coords.y -= off_y;
pika_symmetry_set_origin (sym, drawable, &coords);
paint_core->sym = sym;
/* Some settings are based on the original stroke. */
opacity = pika_dynamics_get_linear_value (dynamics,

View File

@ -5101,7 +5101,7 @@ register_image_procs (PikaPDB *pdb)
"pika-image-get-imported-file");
pika_procedure_set_static_help (procedure,
"Returns the imported file for the specified image.",
"This procedure returns the file associated with the specified image if the image was imported from a non-native Pika format. If the image was not imported, or has since been saved in the native Gimp format, this procedure returns %NULL.",
"This procedure returns the file associated with the specified image if the image was imported from a non-native Pika format. If the image was not imported, or has since been saved in the native Pika format, this procedure returns %NULL.",
NULL);
pika_procedure_set_static_attribution (procedure,
"Eric Grivel <pika@lumenssolutions.com>",

View File

@ -1297,7 +1297,10 @@ vectors_export_to_file_invoker (PikaProcedure *procedure,
if (success)
{
GList *vectors_list = g_list_prepend (NULL, vectors);
GList *vectors_list = NULL;
if (vectors != NULL)
vectors_list = g_list_prepend (vectors_list, vectors);
success = pika_vectors_export_file (image, vectors_list, file, error);
@ -1327,7 +1330,10 @@ vectors_export_to_string_invoker (PikaProcedure *procedure,
if (success)
{
GList *vectors_list = g_list_prepend (NULL, vectors);
GList *vectors_list = NULL;
if (vectors != NULL)
vectors_list = g_list_prepend (vectors_list, vectors);
string = pika_vectors_export_string (image, vectors_list);
g_list_free (vectors_list);
@ -2440,7 +2446,7 @@ register_vectors_procs (PikaPDB *pdb)
"pika-vectors-export-to-file");
pika_procedure_set_static_help (procedure,
"save a path as an SVG file.",
"This procedure creates an SVG file to save a Vectors object, that is, a path. The resulting file can be edited using a vector graphics application, or later reloaded into PIKA. If you pass 0 as the 'vectors' argument, then all paths in the image will be exported.",
"This procedure creates an SVG file to save a Vectors object, that is, a path. The resulting file can be edited using a vector graphics application, or later reloaded into PIKA. Pass %NULL as the 'vectors' argument to export all paths in the image.",
NULL);
pika_procedure_set_static_attribution (procedure,
"Bill Skaggs <weskaggs@primate.ucdavis.edu>",
@ -2461,7 +2467,7 @@ register_vectors_procs (PikaPDB *pdb)
pika_procedure_add_argument (procedure,
pika_param_spec_vectors ("vectors",
"vectors",
"The vectors object to be saved, or 0 for all in the image",
"The vectors object to export, or %NULL for all in the image",
FALSE,
PIKA_PARAM_READWRITE | PIKA_PARAM_NO_VALIDATE));
pika_pdb_register_procedure (pdb, procedure);
@ -2475,7 +2481,7 @@ register_vectors_procs (PikaPDB *pdb)
"pika-vectors-export-to-string");
pika_procedure_set_static_help (procedure,
"Save a path as an SVG string.",
"This procedure works like 'pika-vectors-export-to-file' but creates a string rather than a file. The contents are a NUL-terminated string that holds a complete XML document. If you pass 0 as the 'vectors' argument, then all paths in the image will be exported.",
"This procedure works like 'pika-vectors-export-to-file' but creates a string rather than a file. The string is NULL-terminated and holds a complete XML document. Pass %NULL as the 'vectors' argument to export all paths in the image.",
NULL);
pika_procedure_set_static_attribution (procedure,
"Bill Skaggs <weskaggs@primate.ucdavis.edu>",
@ -2490,7 +2496,7 @@ register_vectors_procs (PikaPDB *pdb)
pika_procedure_add_argument (procedure,
pika_param_spec_vectors ("vectors",
"vectors",
"The vectors object to save, or 0 for all in the image",
"The vectors object to export, or %NULL for all in the image",
FALSE,
PIKA_PARAM_READWRITE | PIKA_PARAM_NO_VALIDATE));
pika_procedure_add_return_value (procedure,

View File

@ -450,7 +450,7 @@ pika_update_about_dialog (PikaCoreConfig *config,
if (config->last_known_release != NULL)
{
#ifndef PIKA_CONSOLE_COMPILATION
gtk_widget_show (about_dialog_create (pika));
gtk_widget_show (about_dialog_create (pika, config));
#else
g_printerr (_("A new version of PIKA (%s) was released.\n"
"It is recommended to update."),

View File

@ -170,6 +170,9 @@ pika_tool_options_manager_init (Pika *pika)
g_signal_connect (pika->config, "notify::global-font",
G_CALLBACK (tool_options_manager_global_notify),
manager);
g_signal_connect (pika->config, "notify::global-expand",
G_CALLBACK (tool_options_manager_global_notify),
manager);
g_signal_connect (user_context, "tool-changed",
G_CALLBACK (tool_options_manager_tool_changed),
@ -254,6 +257,8 @@ tool_options_manager_get_global_props (PikaCoreConfig *config)
global_props |= PIKA_CONTEXT_PROP_MASK_GRADIENT;
if (config->global_font)
global_props |= PIKA_CONTEXT_PROP_MASK_FONT;
if (config->global_expand)
global_props |= PIKA_CONTEXT_PROP_MASK_EXPAND;
return global_props;
}
@ -358,6 +363,12 @@ tool_options_manager_paint_options_notify (PikaPaintOptions *src,
prop_mask |= PIKA_CONTEXT_PROP_MASK_GRADIENT;
}
if ((active || config->global_expand) &&
tool_info->context_props & PIKA_CONTEXT_PROP_MASK_EXPAND)
{
prop_mask |= PIKA_CONTEXT_PROP_MASK_EXPAND;
}
if (pika_paint_options_is_prop (pspec->name, prop_mask))
{
GValue value = G_VALUE_INIT;

View File

@ -66,6 +66,8 @@ pika_airbrush_tool_register (PikaToolRegisterCallback callback,
PIKA_TYPE_AIRBRUSH_OPTIONS,
pika_airbrush_options_gui,
PIKA_PAINT_OPTIONS_CONTEXT_MASK |
PIKA_CONTEXT_PROP_MASK_EXPAND |
PIKA_CONTEXT_PROP_MASK_PATTERN |
PIKA_CONTEXT_PROP_MASK_GRADIENT,
"pika-airbrush-tool",
_("Airbrush"),

View File

@ -61,6 +61,7 @@ pika_clone_tool_register (PikaToolRegisterCallback callback,
PIKA_TYPE_CLONE_OPTIONS,
pika_clone_options_gui,
PIKA_PAINT_OPTIONS_CONTEXT_MASK |
PIKA_CONTEXT_PROP_MASK_EXPAND |
PIKA_CONTEXT_PROP_MASK_PATTERN,
"pika-clone-tool",
_("Clone"),

View File

@ -73,7 +73,9 @@ pika_convolve_tool_register (PikaToolRegisterCallback callback,
(* callback) (PIKA_TYPE_CONVOLVE_TOOL,
PIKA_TYPE_CONVOLVE_OPTIONS,
pika_convolve_options_gui,
PIKA_PAINT_OPTIONS_CONTEXT_MASK,
PIKA_PAINT_OPTIONS_CONTEXT_MASK |
PIKA_CONTEXT_PROP_MASK_PATTERN |
PIKA_CONTEXT_PROP_MASK_EXPAND,
"pika-convolve-tool",
_("Blur / Sharpen"),
_("Blur / Sharpen Tool: Selective blurring or unblurring using a brush"),

View File

@ -52,7 +52,9 @@ pika_heal_tool_register (PikaToolRegisterCallback callback,
(* callback) (PIKA_TYPE_HEAL_TOOL,
PIKA_TYPE_SOURCE_OPTIONS,
pika_heal_options_gui,
PIKA_PAINT_OPTIONS_CONTEXT_MASK,
PIKA_PAINT_OPTIONS_CONTEXT_MASK |
PIKA_CONTEXT_PROP_MASK_PATTERN |
PIKA_CONTEXT_PROP_MASK_EXPAND,
"pika-heal-tool",
_("Healing"),
_("Healing Tool: Heal image irregularities"),

View File

@ -50,6 +50,7 @@ pika_ink_options_gui (PikaToolOptions *tool_options)
GtkWidget *frame;
GtkWidget *vbox2;
GtkWidget *scale;
GtkWidget *combo_box;
GtkWidget *blob_box;
GtkWidget *hbox;
GtkWidget *editor;
@ -133,6 +134,28 @@ pika_ink_options_gui (PikaToolOptions *tool_options)
gtk_container_add (GTK_CONTAINER (frame), editor);
gtk_widget_show (editor);
/* Expand layer options */
vbox2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
scale = pika_prop_spin_scale_new (config, "expand-amount",
1, 10, 2);
pika_spin_scale_set_constrain_drag (PIKA_SPIN_SCALE (scale), TRUE);
pika_spin_scale_set_scale_limits (PIKA_SPIN_SCALE (scale), 1.0, 1000.0);
pika_spin_scale_set_gamma (PIKA_SPIN_SCALE (scale), 1.0);
gtk_box_pack_start (GTK_BOX (vbox2), scale, FALSE, FALSE, 0);
combo_box = pika_prop_enum_combo_box_new (config, "expand-fill-type", 0, 0);
gtk_box_pack_start (GTK_BOX (vbox2), combo_box, FALSE, FALSE, 0);
frame = pika_prop_enum_radio_frame_new (config, "expand-mask-fill-type",
"Fill Layer Mask With", 0, 1);
gtk_box_pack_start (GTK_BOX (vbox2), frame, FALSE, FALSE, 0);
frame = pika_prop_expanding_frame_new (config, "expand-use", NULL,
vbox2, NULL);
gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
gtk_widget_show (frame);
pika_config_connect (config, G_OBJECT (editor), "blob-type");
pika_config_connect (config, G_OBJECT (editor), "blob-aspect");
pika_config_connect (config, G_OBJECT (editor), "blob-angle");

View File

@ -68,7 +68,9 @@ pika_ink_tool_register (PikaToolRegisterCallback callback,
PIKA_CONTEXT_PROP_MASK_FOREGROUND |
PIKA_CONTEXT_PROP_MASK_BACKGROUND |
PIKA_CONTEXT_PROP_MASK_OPACITY |
PIKA_CONTEXT_PROP_MASK_PAINT_MODE,
PIKA_CONTEXT_PROP_MASK_PAINT_MODE |
PIKA_CONTEXT_PROP_MASK_PATTERN |
PIKA_CONTEXT_PROP_MASK_EXPAND,
"pika-ink-tool",
_("Ink"),
_("Ink Tool: Calligraphy-style painting"),

View File

@ -50,8 +50,11 @@ pika_mybrush_options_gui (PikaToolOptions *tool_options)
{
GObject *config = G_OBJECT (tool_options);
GtkWidget *vbox = pika_paint_options_gui (tool_options);
GtkWidget *vbox2;
GtkWidget *button;
GtkWidget *scale;
GtkWidget *combo_box;
GtkWidget *frame;
/* the brush */
button = pika_prop_mybrush_box_new (NULL, PIKA_CONTEXT (tool_options),
@ -82,5 +85,28 @@ pika_mybrush_options_gui (PikaToolOptions *tool_options)
0.1, 1.0, 2);
gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, FALSE, 0);
/* Expand layer options */
vbox2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
scale = pika_prop_spin_scale_new (config, "expand-amount",
1, 10, 2);
pika_spin_scale_set_constrain_drag (PIKA_SPIN_SCALE (scale), TRUE);
pika_spin_scale_set_scale_limits (PIKA_SPIN_SCALE (scale), 1.0, 1000.0);
pika_spin_scale_set_gamma (PIKA_SPIN_SCALE (scale), 1.0);
gtk_box_pack_start (GTK_BOX (vbox2), scale, FALSE, FALSE, 0);
combo_box = pika_prop_enum_combo_box_new (config, "expand-fill-type", 0, 0);
gtk_box_pack_start (GTK_BOX (vbox2), combo_box, FALSE, FALSE, 0);
frame = pika_prop_enum_radio_frame_new (config, "expand-mask-fill-type",
"Fill Layer Mask With", 0, 1);
gtk_box_pack_start (GTK_BOX (vbox2), frame, FALSE, FALSE, 0);
frame = pika_prop_expanding_frame_new (config, "expand-use", NULL,
vbox2, NULL);
gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
gtk_widget_show (frame);
return vbox;
}

View File

@ -72,7 +72,9 @@ pika_mybrush_tool_register (PikaToolRegisterCallback callback,
PIKA_CONTEXT_PROP_MASK_BACKGROUND |
PIKA_CONTEXT_PROP_MASK_OPACITY |
PIKA_CONTEXT_PROP_MASK_PAINT_MODE |
PIKA_CONTEXT_PROP_MASK_MYBRUSH,
PIKA_CONTEXT_PROP_MASK_MYBRUSH |
PIKA_CONTEXT_PROP_MASK_PATTERN |
PIKA_CONTEXT_PROP_MASK_EXPAND,
"pika-mypaint-brush-tool",
_("MyPaint Brush"),
_("MyPaint Brush Tool: Use MyPaint brushes in PIKA"),

View File

@ -56,7 +56,9 @@ pika_paintbrush_tool_register (PikaToolRegisterCallback callback,
PIKA_TYPE_PAINT_OPTIONS,
pika_paint_options_gui,
PIKA_PAINT_OPTIONS_CONTEXT_MASK |
PIKA_CONTEXT_PROP_MASK_GRADIENT,
PIKA_CONTEXT_PROP_MASK_EXPAND |
PIKA_CONTEXT_PROP_MASK_GRADIENT |
PIKA_CONTEXT_PROP_MASK_PATTERN,
"pika-paintbrush-tool",
_("Paintbrush"),
_("Paintbrush Tool: Paint smooth strokes using a brush"),

View File

@ -77,6 +77,8 @@ static GtkWidget * jitter_options_gui (PikaPaintOptions *paint_options,
GType tool_type);
static GtkWidget * smoothing_options_gui (PikaPaintOptions *paint_options,
GType tool_type);
static GtkWidget * expand_options_gui (PikaPaintOptions *paint_options,
GType tool_type);
static GtkWidget * pika_paint_options_gui_scale_with_buttons
(GObject *config,
@ -274,6 +276,22 @@ pika_paint_options_gui (PikaToolOptions *tool_options)
gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
}
/* the "expand layers" options */
if (tool_type == PIKA_TYPE_PAINTBRUSH_TOOL ||
tool_type == PIKA_TYPE_PENCIL_TOOL ||
tool_type == PIKA_TYPE_AIRBRUSH_TOOL ||
tool_type == PIKA_TYPE_CLONE_TOOL ||
tool_type == PIKA_TYPE_HEAL_TOOL ||
tool_type == PIKA_TYPE_CONVOLVE_TOOL ||
tool_type == PIKA_TYPE_SMUDGE_TOOL)
{
GtkWidget *frame;
frame = expand_options_gui (options, tool_type);
gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
gtk_widget_show (frame);
}
return vbox;
}
@ -489,6 +507,39 @@ pika_paint_options_gui_reset_force (GtkWidget *button,
NULL);
}
static GtkWidget *
expand_options_gui (PikaPaintOptions *paint_options,
GType tool_type)
{
GObject *config = G_OBJECT (paint_options);
GtkWidget *frame;
GtkWidget *scale;
GtkWidget *combo_box;
GtkWidget *vbox;
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
scale = pika_prop_spin_scale_new (config, "expand-amount",
1, 10, 2);
pika_spin_scale_set_constrain_drag (PIKA_SPIN_SCALE (scale), TRUE);
pika_spin_scale_set_scale_limits (PIKA_SPIN_SCALE (scale), 1.0, 1000.0);
pika_spin_scale_set_gamma (PIKA_SPIN_SCALE (scale), 1.0);
gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, FALSE, 0);
combo_box = pika_prop_enum_combo_box_new (config, "expand-fill-type", 0, 0);
gtk_box_pack_start (GTK_BOX (vbox), combo_box, FALSE, FALSE, 0);
frame = pika_prop_enum_radio_frame_new (config, "expand-mask-fill-type",
"Fill Layer Mask With", 0, 1);
gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
frame = pika_prop_expanding_frame_new (config, "expand-use", NULL,
vbox, NULL);
return frame;
}
static GtkWidget *
pika_paint_options_gui_scale_with_buttons (GObject *config,
gchar *prop_name,

View File

@ -30,6 +30,8 @@
#include "core/pikadrawable.h"
#include "core/pikaimage.h"
#include "core/pikalayer.h"
#include "core/pikalayermask.h"
#include "core/pikaprojection.h"
#include "paint/pikapaintcore.h"
@ -41,6 +43,7 @@
#include "pikapainttool.h"
#include "pikapainttool-paint.h"
#include "pikatools-utils.h"
#define DISPLAY_UPDATE_INTERVAL 10000 /* microseconds */
@ -218,6 +221,27 @@ pika_paint_tool_paint_interpolate (PikaPaintTool *paint_tool,
pika_paint_core_interpolate (core, data->drawables, paint_options,
&data->coords, data->time);
/* Blink the lock box if required */
if (core->lock_blink_state == PIKA_PAINT_LOCK_BLINK_PENDING)
{
GList *iter;
PikaLayer *layer;
/* Blink the lock only once per stroke */
core->lock_blink_state = PIKA_PAINT_LOCK_BLINKED;
for (iter = data->drawables; iter; iter = g_list_next (iter))
{
layer = PIKA_IS_LAYER_MASK (iter->data) ?
PIKA_LAYER_MASK (iter->data)->layer :
PIKA_LAYER (iter->data);
if (pika_item_get_lock_position (PIKA_ITEM (layer)))
pika_tools_blink_lock_box (PIKA_CONTEXT (paint_options)->pika,
PIKA_ITEM (layer));
}
}
g_list_free (data->drawables);
g_slice_free (InterpolateData, data);
}

View File

@ -50,6 +50,8 @@ pika_pencil_tool_register (PikaToolRegisterCallback callback,
PIKA_TYPE_PENCIL_OPTIONS,
pika_paint_options_gui,
PIKA_PAINT_OPTIONS_CONTEXT_MASK |
PIKA_CONTEXT_PROP_MASK_EXPAND |
PIKA_CONTEXT_PROP_MASK_PATTERN |
PIKA_CONTEXT_PROP_MASK_GRADIENT,
"pika-pencil-tool",
_("Pencil"),

View File

@ -54,6 +54,8 @@ pika_smudge_tool_register (PikaToolRegisterCallback callback,
PIKA_TYPE_SMUDGE_OPTIONS,
pika_smudge_options_gui,
PIKA_PAINT_OPTIONS_CONTEXT_MASK |
PIKA_CONTEXT_PROP_MASK_EXPAND |
PIKA_CONTEXT_PROP_MASK_PATTERN |
PIKA_CONTEXT_PROP_MASK_GRADIENT,
"pika-smudge-tool",
_("Smudge"),

View File

@ -51,19 +51,26 @@ static gchar * pika_vectors_export_path_data (PikaVectors *vectors);
/**
* pika_vectors_export_file:
* @image: the #PikaImage from which to export vectors
* @vectors: a #GList of #PikaVectors objects or %NULL to export all vectors in @image
* @image: the #PikaImage from which to export
* @path_list: a #GList of #PikaVectors objects or %NULL to export all paths in @image
* @file: the file to write
* @error: return location for errors
*
* Exports one or more vectors to a SVG file.
* Exports one or more vectors aka path to an SVG file aka XML doc.
*
* When @path_list is %NULL aka empty list, exports all paths in image.
*
* When @path_list is empty and image has no paths,
* this still writes a non-empty file containing an XML doc.
*
* Will overwrite any existing file.
*
* Returns: %TRUE on success,
* %FALSE if there was an error writing the file
* %FALSE when there was an error writing the file
**/
gboolean
pika_vectors_export_file (PikaImage *image,
GList *vectors,
GList *path_list,
GFile *file,
GError **error)
{
@ -81,7 +88,7 @@ pika_vectors_export_file (PikaImage *image,
if (! output)
return FALSE;
string = pika_vectors_export (image, vectors);
string = pika_vectors_export (image, path_list);
if (! g_output_stream_write_all (output, string->str, string->len,
NULL, NULL, &my_error))
@ -111,20 +118,25 @@ pika_vectors_export_file (PikaImage *image,
/**
* pika_vectors_export_string:
* @image: the #PikaImage from which to export vectors
* @vectors: a #PikaVectors object or %NULL to export all vectors in @image
* @image: the #PikaImage from which to export
* @path_list: a #GList of #PikaVectors objects, or %NULL to export all paths in @image
*
* Exports one or more vectors to a SVG string.
* Exports one or more vectors aka path to a SVG string.
*
* Returns: a %NUL-terminated string that holds a complete XML document
* When @path_list is %NULL aka empty list, exports all paths in image.
*
* When @path_list is empty and image has no paths,
* this still returns a string for an empty XML doc.
*
* Returns: a NULL-terminated string that holds a complete XML document
**/
gchar *
pika_vectors_export_string (PikaImage *image,
GList *vectors)
GList *path_list)
{
g_return_val_if_fail (PIKA_IS_IMAGE (image), NULL);
return g_string_free (pika_vectors_export (image, vectors), FALSE);
return g_string_free (pika_vectors_export (image, path_list), FALSE);
}
static GString *

View File

@ -104,6 +104,8 @@ static void pika_action_proxy_button_activate (GtkButton *button,
static void pika_action_update_proxy_sensitive (PikaAction *action,
GtkWidget *proxy);
static void pika_action_update_proxy_visible (PikaAction *action,
GtkWidget *proxy);
static void pika_action_update_proxy_tooltip (PikaAction *action,
GtkWidget *proxy);
@ -446,9 +448,20 @@ void
pika_action_set_visible (PikaAction *action,
gboolean visible)
{
g_object_set (action,
"visible", visible,
NULL);
PikaActionPrivate *priv = GET_PRIVATE (action);
/* Only notify when the state actually changed. This is important for
* handlers such as visibility of menu items in PikaMenuModel which
* will assume that the action visibility changed. Otherwise we might
* remove items by mistake.
*/
if (priv->visible != visible)
{
priv->visible = visible;
pika_action_update_proxy_visible (action, NULL);
g_object_notify (G_OBJECT (action), "visible");
}
}
gboolean
@ -929,16 +942,8 @@ pika_action_set_property (GObject *object,
NULL);
break;
case PIKA_ACTION_PROP_VISIBLE:
if (priv->visible != g_value_get_boolean (value))
{
priv->visible = g_value_get_boolean (value);
/* Only notify when the state actually changed. This is important for
* handlers such as visibility of menu items in PikaMenuModel which
* will assume that the action visibility changed. Otherwise we might
* remove items by mistake.
*/
g_object_notify (object, "visible");
}
pika_action_set_visible (PIKA_ACTION (object),
g_value_get_boolean (value));
break;
case PIKA_ACTION_PROP_LABEL:
@ -1391,6 +1396,24 @@ pika_action_update_proxy_sensitive (PikaAction *action,
}
}
static void
pika_action_update_proxy_visible (PikaAction *action,
GtkWidget *proxy)
{
PikaActionPrivate *priv = GET_PRIVATE (action);
gboolean visible = pika_action_is_visible (action);
if (proxy)
{
gtk_widget_set_visible (proxy, visible);
}
else
{
for (GList *list = priv->proxies; list; list = list->next)
gtk_widget_set_visible (list->data, visible);
}
}
static void
pika_action_update_proxy_tooltip (PikaAction *action,
GtkWidget *proxy)

View File

@ -102,10 +102,6 @@ static void pika_container_tree_view_set_view_size (PikaContainerVi
static void pika_container_tree_view_real_edit_name (PikaContainerTreeView *tree_view);
static void pika_container_tree_view_selection_label_notify (GtkLabel *label,
GParamSpec *pspec,
PikaItemTreeView *view);
static gboolean pika_container_tree_view_edit_focus_out (GtkWidget *widget,
GdkEvent *event,
gpointer user_data);
@ -296,12 +292,6 @@ pika_container_tree_view_constructed (GObject *object)
gtk_label_set_selectable (GTK_LABEL (tree_view->priv->multi_selection_label), TRUE);
gtk_tree_view_column_set_widget (tree_view->main_column,
tree_view->priv->multi_selection_label);
g_signal_connect (tree_view->priv->multi_selection_label, "notify::label",
G_CALLBACK (pika_container_tree_view_selection_label_notify),
tree_view);
g_signal_connect (tree_view->priv->multi_selection_label, "notify::selection-bound",
G_CALLBACK (pika_container_tree_view_selection_label_notify),
tree_view);
gtk_widget_show (tree_view->priv->multi_selection_label);
gtk_tree_view_insert_column (tree_view->view, tree_view->main_column, 0);
@ -1204,20 +1194,6 @@ pika_container_tree_view_real_edit_name (PikaContainerTreeView *tree_view)
/* callbacks */
static void
pika_container_tree_view_selection_label_notify (GtkLabel *label,
GParamSpec *pspec,
PikaItemTreeView *view)
{
/* This is a weird trick to make the label follow the color scheme of
* selected items in whatever theme is selected. It seems we cannot
* link to the color of another widget whose theme we don't control.
* Faking selection is the only way I found, though it is quite ugly
* semantically.
*/
gtk_label_select_region (label, 0, -1);
}
static gboolean
pika_container_tree_view_edit_focus_out (GtkWidget *widget,
GdkEvent *event,
@ -1354,10 +1330,12 @@ pika_container_tree_view_button (GtkWidget *widget,
GdkEventButton *bevent,
PikaContainerTreeView *tree_view)
{
PikaContainerView *container_view = PIKA_CONTAINER_VIEW (tree_view);
GtkTreeViewColumn *column;
GtkTreePath *path;
gboolean handled = TRUE;
PikaContainerView *container_view = PIKA_CONTAINER_VIEW (tree_view);
GtkTreeViewColumn *column;
GtkTreePath *path;
gboolean handled = TRUE;
GtkCellRenderer *toggled_cell = NULL;
PikaCellRendererViewable *clicked_cell = NULL;
tree_view->priv->dnd_renderer = NULL;
@ -1366,8 +1344,6 @@ pika_container_tree_view_button (GtkWidget *widget,
&path, &column, NULL, NULL))
{
PikaViewRenderer *renderer;
GtkCellRenderer *toggled_cell = NULL;
PikaCellRendererViewable *clicked_cell = NULL;
GtkCellRenderer *edit_cell = NULL;
GdkRectangle column_area;
GtkTreeIter iter;
@ -1389,15 +1365,21 @@ pika_container_tree_view_button (GtkWidget *widget,
multisel_mode = FALSE;
}
/* We need to grab focus after a button click, in order to make keyboard
* navigation possible. For multi-selection though, actual selection will
* happen in pika_container_tree_view_selection_changed() but the widget
* must already be focused or the handler won't run.
* Whereas for single selection, grab must happen after we changed the
* selection (which will happen in this function) otherwise we end up
* first scrolling to the current selection. So we have a separate
* gtk_widget_grab_focus() at the end of the function.
* See also commit 3e101922 and MR !1128.
/* We need to grab focus at button click, in order to make keyboard
* navigation possible; yet the timing matters:
* 1. For multi-selection, actual selection will happen in
* pika_container_tree_view_selection_changed() but the widget
* must already be focused or the handler won't run. So we grab first.
* 2. For toggled and clicked cells, we must also grab first (see code
* below), and absolutely not in the end, because some toggle cells may
* trigger a popup (and the grab on the source widget would close the
* popup).
* 3. Finally for single selection, grab must happen after we changed
* the selection (which will happen in this function) otherwise we
* end up first scrolling to the current selection.
* This is why we have a few separate calls to gtk_widget_grab_focus()
* in this function.
* See also commit 3e101922, MR !1128 and #10281.
*/
if (multisel_mode && bevent->type == GDK_BUTTON_PRESS && ! gtk_widget_has_focus (widget))
gtk_widget_grab_focus (widget);
@ -1524,6 +1506,10 @@ pika_container_tree_view_button (GtkWidget *widget,
column, &column_area,
bevent->x, bevent->y);
if ((toggled_cell || clicked_cell) &&
bevent->type == GDK_BUTTON_PRESS && ! gtk_widget_has_focus (widget))
gtk_widget_grab_focus (widget);
if (! toggled_cell && ! clicked_cell)
{
edit_cell =
@ -1720,7 +1706,8 @@ pika_container_tree_view_button (GtkWidget *widget,
handled = TRUE;
}
if (handled && bevent->type == GDK_BUTTON_PRESS && ! gtk_widget_has_focus (widget))
if (handled && bevent->type == GDK_BUTTON_PRESS && ! gtk_widget_has_focus (widget) &&
! toggled_cell && ! clicked_cell)
gtk_widget_grab_focus (widget);
return handled;

View File

@ -46,6 +46,9 @@ static void pika_image_editor_real_set_image (PikaImageEditor *editor,
static void pika_image_editor_image_flush (PikaImage *image,
gboolean invalidate_preview,
PikaImageEditor *editor);
static gboolean
pika_image_editor_image_flush_idle
(gpointer user_data);
G_DEFINE_TYPE_WITH_CODE (PikaImageEditor, pika_image_editor, PIKA_TYPE_EDITOR,
@ -177,7 +180,19 @@ pika_image_editor_image_flush (PikaImage *image,
gboolean invalidate_preview,
PikaImageEditor *editor)
{
g_idle_add_full (G_PRIORITY_LOW,
(GSourceFunc) pika_image_editor_image_flush_idle,
g_object_ref (editor), g_object_unref);
}
static gboolean
pika_image_editor_image_flush_idle (gpointer user_data)
{
PikaImageEditor *editor = user_data;
if (pika_editor_get_ui_manager (PIKA_EDITOR (editor)))
pika_ui_manager_update (pika_editor_get_ui_manager (PIKA_EDITOR (editor)),
pika_editor_get_popup_data (PIKA_EDITOR (editor)));
return G_SOURCE_REMOVE;
}

View File

@ -1224,6 +1224,8 @@ pika_layer_tree_view_floating_selection_changed (PikaImage *image,
g_list_free (all_layers);
}
gtk_widget_set_sensitive (layer_view->priv->link_button, ! floating_sel);
pika_layer_tree_view_update_highlight (layer_view);
}

View File

@ -86,13 +86,13 @@
"sources": [
{
"type": "archive",
"url": "https://github.com/Exiv2/exiv2/releases/download/v0.28.0/exiv2-0.28.0-Source.tar.gz",
"sha256": "89af3b5ef7277753ef7a7b5374ae017c6b9e304db3b688f1948e73e103491f3d",
"url": "https://github.com/Exiv2/exiv2/archive/refs/tags/v0.28.1.tar.gz",
"sha256": "3078651f995cb6313b1041f07f4dd1bf0e9e4d394d6e2adc6e92ad0b621291fa",
"x-checker-data": {
"type": "anitya",
"project-id": 769,
"stable-only": true,
"url-template": "https://github.com/Exiv2/exiv2/releases/download/v$version/exiv2-$version-Source.tar.gz"
"url-template": "https://github.com/Exiv2/exiv2/archive/refs/tags/v$version.tar.gz"
}
}
]
@ -119,7 +119,7 @@
]
},
{
"name" : "openexr",
"name": "openexr",
"config-opts": [
"-DBUILD_SHARED_LIBS=ON",
"-DOPENEXR_INSTALL_EXAMPLES=OFF",
@ -201,7 +201,8 @@
"-DOpenJPEG_DIR=/usr/lib64/openjpeg-2.3",
"-DENABLE_BOOST=OFF",
"-DWITH_NSS3:BOOL=OFF",
"-DENABLE_QT5:BOOL=OFF"
"-DENABLE_QT5:BOOL=OFF",
"-DENABLE_QT6:BOOL=OFF"
],
"cleanup": [
"/bin",
@ -212,8 +213,8 @@
"sources": [
{
"type": "archive",
"url": "https://poppler.freedesktop.org/poppler-23.08.0.tar.xz",
"sha256": "4a4bf7fc903b9f1a2ab7d04b7c5d8220db9bc6261cc73fdb9a826dc272f49aa8",
"url": "https://poppler.freedesktop.org/poppler-23.11.0.tar.xz",
"sha256": "f99cca6799cb9cb6c92fc1e0eb78547b611cb733750ab7cb047cb0e6c246539c",
"x-checker-data": {
"type": "anitya",
"project-id": 3686,
@ -314,8 +315,8 @@
"sources": [
{
"type": "archive",
"url": "https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/download/gs10012/ghostscript-10.01.2.tar.gz",
"sha512": "11887850d440b8bbd8b83b8b58df8c743cf94be8fdbcfbda1a5fce55ce7c6a963d94b41a4ebcf1ba48f64f8af01e76dec789e2711212b1f7a32f98c42d75e0ff",
"url": "https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/download/gs10021/ghostscript-10.02.1.tar.gz",
"sha512": "24eeff047a24085413283ee42539a2feea4d3de81077664861399055a9d30349a0ef0950746bb0b0ee2237b4b9ee215340204ab0e33347b53dc2acee85fcfc19",
"x-checker-data": {
"//": "Bypass broken url-template with anitya checker - See https://github.com/flathub/flatpak-external-data-checker/issues/360",
"type": "json",
@ -365,8 +366,8 @@
{
"type": "git",
"url": "https://github.com/json-c/json-c.git",
"tag": "json-c-0.16-20220414",
"commit": "2f2ddc1f2dbca56c874e8f9c31b5b963202d80e7",
"tag": "json-c-0.17-20230812",
"commit": "b4c371fa0cbc4dcbaccc359ce9e957a22988fb34",
"x-checker-data": {
"type": "git",
"tag-pattern": "^json-c-([\\d.]+)-[\\d]+$"
@ -469,8 +470,8 @@
"sources": [
{
"type": "archive",
"url": "https://github.com/xianyi/OpenBLAS/archive/v0.3.23.tar.gz",
"sha256": "5d9491d07168a5d00116cdc068a40022c3455bf9293c7cb86a65b1054d7e5114",
"url": "https://github.com/xianyi/OpenBLAS/archive/v0.3.24.tar.gz",
"sha256": "ceadc5065da97bd92404cac7254da66cc6eb192679cf1002098688978d4d5132",
"x-checker-data": {
"type": "anitya",
"project-id": 2540,
@ -616,8 +617,8 @@
"sources": [
{
"type": "archive",
"url": "https://github.com/strukturag/libheif/releases/download/v1.16.2/libheif-1.16.2.tar.gz",
"sha256": "7f97e4205c0bd9f9b8560536c8bd2e841d1c9a6d610401eb3eb87ed9cdfe78ea",
"url": "https://github.com/strukturag/libheif/releases/download/v1.17.3/libheif-1.17.3.tar.gz",
"sha256": "8d5b6292e7931324f81f871f250ecbb9f874aa3c66b4f6f35ceb0bf3163b53ea",
"x-checker-data": {
"type": "anitya",
"project-id": 64439,

View File

@ -57,7 +57,6 @@ download_lang Galician.isl
download_lang Georgian.isl
download_lang Greek.isl
download_lang Indonesian.isl
download_lang Korean.isl
download_lang Latvian.isl
download_lang Lithuanian.isl
download_lang Malaysian.isl
@ -67,9 +66,9 @@ download_lang Swedish.isl
download_lang Vietnamese.isl
cd -
# Hungarian is not in a release yet, but was moved from Unofficial
# Any language not in a release yet, but moved from Unofficial, should be added here
cd "${ISCCDIR}/Languages/"
download_lang_official Hungarian.isl
download_lang_official Korean.isl
cd -
# Copy generated language files into the source directory.

View File

@ -157,6 +157,9 @@ cp -fr ${MSYS_PREFIX}/share/xml/iso-codes/iso_639.xml ${PIKA_DISTRIB}/share/xml/
# Adwaita can be used as the base icon set.
cp -fr ${MSYS_PREFIX}/share/icons/Adwaita ${PIKA_DISTRIB}/share/icons/
# Gdbus is needed to avoid warnings in CMD.
cp -fr ${MSYS_PREFIX}/bin/gdbus.exe ${PIKA_DISTRIB}/bin
# XXX Why are these for exactly?
cp -fr ${MSYS_PREFIX}/bin/gspawn*.exe ${PIKA_DISTRIB}/bin/

View File

@ -13,6 +13,7 @@ exr
gif
heif
heic
hej2
icns
jp2
j2k

View File

@ -216,7 +216,7 @@ Name: "it"; MessagesFile: "compiler:Languages\Italian.isl,lang\it.setup.isl"
Name: "ja"; MessagesFile: "compiler:Languages\Japanese.isl,lang\ja.setup.isl"
Name: "ka"; MessagesFile: "compiler:Languages\Unofficial\Georgian.isl,lang\ka.setup.isl"
Name: "kab"; MessagesFile: "compiler:Default.isl,lang\kab.isl,lang\kab.setup.isl"
Name: "ko"; MessagesFile: "compiler:Languages\Unofficial\Korean.isl,lang\ko.setup.isl"
Name: "ko"; MessagesFile: "compiler:Languages\Korean.isl,lang\ko.setup.isl"
Name: "lt"; MessagesFile: "compiler:Languages\Unofficial\Lithuanian.isl,lang\lt.setup.isl"
Name: "lv"; MessagesFile: "compiler:Languages\Unofficial\Latvian.isl,lang\lv.setup.isl"
Name: "mr"; MessagesFile: "compiler:Languages\Unofficial\Marathi.islu,lang\mr.setup.isl"

View File

@ -407,6 +407,32 @@
</description>
</release>
<release version="2.10.36" date="2023-11-05">
<description>
<p>
This new version of PIKA comes with many fixes, including
vulnerability corrections. It is highly recommended to update. It
also provides a few interesting changes, such as:
</p>
<ul>
<li>
Adobe ACB and ASE palette support
</li>
<li>
New gradient: FG to Transparent (Hardedge)
</li>
<li>
Some text tool algorithm enhancement when replacing formatted text
</li>
<li>
Better theming of item locks (when hovering them or setting them active)
</li>
<li>
Improvements in handling a few specific metadata
</li>
</ul>
</description>
</release>
<release version="2.10.34" date="2023-02-21">
<description>
<p>

View File

@ -47,6 +47,7 @@
#include <libpika/pikafont_pdb.h>
#include <libpika/pikafonts_pdb.h>
#include <libpika/pikafontselect_pdb.h>
#include <libpika/pikapikarc_pdb.h>
#include <libpika/pikagradient_pdb.h>
#include <libpika/pikagradients_pdb.h>
#include <libpika/pikagradientselect_pdb.h>
@ -71,7 +72,6 @@
#include <libpika/pikapattern_pdb.h>
#include <libpika/pikapatterns_pdb.h>
#include <libpika/pikapatternselect_pdb.h>
#include <libpika/pikapikarc_pdb.h>
#include <libpika/pikaprogress_pdb.h>
#include <libpika/pikaresource_pdb.h>
#include <libpika/pikaselection_pdb.h>

View File

@ -1191,14 +1191,14 @@ pika_vectors_import_from_string (PikaImage *image,
* pika_vectors_export_to_file:
* @image: The image.
* @file: The SVG file to create.
* @vectors: The vectors object to be saved, or 0 for all in the image.
* @vectors: The vectors object to export, or %NULL for all in the image.
*
* save a path as an SVG file.
*
* This procedure creates an SVG file to save a Vectors object, that
* is, a path. The resulting file can be edited using a vector graphics
* application, or later reloaded into PIKA. If you pass 0 as the
* 'vectors' argument, then all paths in the image will be exported.
* application, or later reloaded into PIKA. Pass %NULL as the
* 'vectors' argument to export all paths in the image.
*
* Returns: TRUE on success.
*
@ -1234,14 +1234,14 @@ pika_vectors_export_to_file (PikaImage *image,
/**
* pika_vectors_export_to_string:
* @image: The image.
* @vectors: The vectors object to save, or 0 for all in the image.
* @vectors: The vectors object to export, or %NULL for all in the image.
*
* Save a path as an SVG string.
*
* This procedure works like pika_vectors_export_to_file() but creates
* a string rather than a file. The contents are a NUL-terminated
* string that holds a complete XML document. If you pass 0 as the
* 'vectors' argument, then all paths in the image will be exported.
* a string rather than a file. The string is NULL-terminated and holds
* a complete XML document. Pass %NULL as the 'vectors' argument to
* export all paths in the image.
*
* Returns: (transfer full):
* A string whose contents are a complete SVG document.

View File

@ -351,6 +351,7 @@ pika_config_param_spec_duplicate (GParamSpec *pspec)
* with type names instead.
*/
g_strcmp0 (type_name, "PikaImage") == 0 ||
g_strcmp0 (type_name, "PikaDisplay") == 0 ||
g_strcmp0 (type_name, "PikaDrawable") == 0 ||
g_strcmp0 (type_name, "PikaLayer") == 0 ||
g_strcmp0 (type_name, "PikaTextLayer") == 0 ||

View File

@ -96,7 +96,9 @@ static void pika_dialog_close (GtkDialog *dialog);
static void pika_dialog_response (GtkDialog *dialog,
gint response_id);
#ifdef G_OS_WIN32
static void pika_dialog_set_title_bar_theme (GtkWidget *dialog);
#endif
G_DEFINE_TYPE_WITH_PRIVATE (PikaDialog, pika_dialog, GTK_TYPE_DIALOG)
@ -770,10 +772,10 @@ pika_dialogs_show_help_button (gboolean show)
show_help_button = show ? TRUE : FALSE;
}
#ifdef G_OS_WIN32
void
pika_dialog_set_title_bar_theme (GtkWidget *dialog)
{
#ifdef G_OS_WIN32
HWND hwnd;
gboolean use_dark_mode = FALSE;
GdkWindow *window = NULL;
@ -810,5 +812,5 @@ pika_dialog_set_title_bar_theme (GtkWidget *dialog)
gdk_window_hide (window);
gdk_window_show (window);
}
#endif
}
#endif

View File

@ -90,10 +90,16 @@
<item><attribute name="action">app.edit-paste-in-place</attribute></item>
<submenu>
<attribute name="label" translatable="yes" context="edit-action">Paste _as</attribute>
<item><attribute name="action">app.edit-paste-merged</attribute></item>
<item><attribute name="action">app.edit-paste-merged-in-place</attribute></item>
<item><attribute name="action">app.edit-paste-into</attribute></item>
<item><attribute name="action">app.edit-paste-into-in-place</attribute></item>
<section>
<item><attribute name="action">app.edit-paste-merged</attribute></item>
<item><attribute name="action">app.edit-paste-merged-in-place</attribute></item>
</section>
<section>
<item><attribute name="action">app.edit-paste-float</attribute></item>
<item><attribute name="action">app.edit-paste-float-in-place</attribute></item>
<item><attribute name="action">app.edit-paste-into</attribute></item>
<item><attribute name="action">app.edit-paste-into-in-place</attribute></item>
</section>
<item><attribute name="action">app.edit-paste-as-new-image</attribute><attribute name="label-variant">long</attribute></item>
</submenu>
<submenu>
@ -143,7 +149,11 @@
<item><attribute name="action">app.select-all</attribute></item>
<item><attribute name="action">app.select-none</attribute></item>
<item><attribute name="action">app.select-invert</attribute></item>
<item><attribute name="action">app.select-float</attribute></item>
<submenu>
<attribute name="label" translatable="yes" context="view-action">_Float</attribute>
<item><attribute name="action">app.select-cut-float</attribute></item>
<item><attribute name="action">app.select-copy-float</attribute></item>
</submenu>
<item><attribute name="action">app.tools-by-color-select-short</attribute></item>
<item><attribute name="action">app.vectors-selection-from-vectors</attribute></item>
<item><attribute name="action">app.dialogs-selection-editor</attribute></item>

View File

@ -10,7 +10,10 @@
<item><attribute name="action">app.select-none</attribute></item>
<item><attribute name="action">app.select-invert</attribute></item>
<item><attribute name="action">app.vectors-selection-from-vectors</attribute></item>
<item><attribute name="action">app.select-float</attribute></item>
<section>
<item><attribute name="action">app.select-cut-float</attribute></item>
<item><attribute name="action">app.select-copy-float</attribute></item>
</section>
<section>
<item><attribute name="action">app.select-feather</attribute></item>
<item><attribute name="action">app.select-sharpen</attribute></item>

View File

@ -192,7 +192,7 @@ sub vectors_stroke_get_length {
code => <<"CODE"
{
PikaStroke *stroke = pika_pdb_get_vectors_stroke (vectors, stroke_id, 0, error);
if (stroke)
length = pika_stroke_get_length (stroke, precision);
else
@ -207,7 +207,7 @@ sub vectors_stroke_get_point_at_dist {
$help = <<'HELP';
This will return the x,y position of a point at a given distance along the
stroke. The distance will be obtained by first digitizing the
stroke. The distance will be obtained by first digitizing the
curve internally and then walking along the curve. For a closed stroke the
start of the path is the first point on the path that was created. This might
not be obvious. If the stroke is not long enough, a "valid" flag will be FALSE.
@ -241,7 +241,7 @@ HELP
code => <<"CODE"
{
PikaStroke *stroke = pika_pdb_get_vectors_stroke (vectors, stroke_id, 0, error);
if (stroke)
{
PikaCoords coord;
@ -279,7 +279,7 @@ HELP
{
PikaStroke *stroke = pika_pdb_get_vectors_stroke (vectors, stroke_id,
PIKA_PDB_ITEM_CONTENT, error);
if (stroke)
{
if (pika_item_is_attached (PIKA_ITEM (vectors)))
@ -317,7 +317,7 @@ HELP
{
PikaStroke *stroke = pika_pdb_get_vectors_stroke (vectors, stroke_id,
PIKA_PDB_ITEM_CONTENT, error);
if (stroke)
{
if (pika_item_is_attached (PIKA_ITEM (vectors)))
@ -405,7 +405,7 @@ HELP
PIKA_PDB_ITEM_CONTENT |
PIKA_PDB_ITEM_POSITION,
error);
if (stroke)
{
if (pika_item_is_attached (PIKA_ITEM (vectors)))
@ -451,7 +451,7 @@ HELP
PIKA_PDB_ITEM_CONTENT |
PIKA_PDB_ITEM_POSITION,
error);
if (stroke)
{
if (pika_item_is_attached (PIKA_ITEM (vectors)))
@ -498,7 +498,7 @@ HELP
PIKA_PDB_ITEM_CONTENT |
PIKA_PDB_ITEM_POSITION,
error);
if (stroke)
{
if (pika_item_is_attached (PIKA_ITEM (vectors)))
@ -544,7 +544,7 @@ HELP
PIKA_PDB_ITEM_CONTENT |
PIKA_PDB_ITEM_POSITION,
error);
if (stroke)
{
if (pika_item_is_attached (PIKA_ITEM (vectors)))
@ -595,7 +595,7 @@ HELP
PIKA_PDB_ITEM_CONTENT |
PIKA_PDB_ITEM_POSITION,
error);
if (stroke)
{
if (pika_item_is_attached (PIKA_ITEM (vectors)))
@ -647,7 +647,7 @@ HELP
code => <<"CODE"
{
PikaStroke *stroke = pika_pdb_get_vectors_stroke (vectors, stroke_id, 0, error);
if (PIKA_IS_BEZIER_STROKE (stroke))
{
GArray *points_array;
@ -713,7 +713,7 @@ HELP
code => <<"CODE"
{
PikaStroke *stroke = pika_pdb_get_vectors_stroke (vectors, stroke_id, 0, error);
if (stroke)
{
GArray *coords_array;
@ -1162,7 +1162,7 @@ HELP
{
GList *list;
gint i;
vectors = g_new (PikaVectors *, num_vectors);
for (i = 0, list = vectors_list;
@ -1231,9 +1231,9 @@ HELP
{
GList *list;
gint i;
vectors = g_new (PikaVectors *, num_vectors);
for (i = 0, list = vectors_list;
i < num_vectors;
i++, list = g_list_next (list))
@ -1255,8 +1255,8 @@ sub vectors_export_to_file {
$help = <<'HELP';
This procedure creates an SVG file to save a Vectors object, that is,
a path. The resulting file can be edited using a vector graphics
application, or later reloaded into PIKA. If you pass 0 as the 'vectors'
argument, then all paths in the image will be exported.
application, or later reloaded into PIKA. Pass %NULL as the 'vectors'
argument to export all paths in the image.
HELP
&bill_pdb_misc('2007', '2.6');
@ -1267,14 +1267,17 @@ HELP
{ name => 'file', type => 'file',
desc => 'The SVG file to create.' },
{ name => 'vectors', type => 'vectors', no_validate => 1,
desc => 'The vectors object to be saved, or 0 for all in the image' }
desc => 'The vectors object to export, or %NULL for all in the image' }
);
%invoke = (
headers => [ qw("vectors/pikavectors-export.h") ],
code => <<'CODE'
{
GList *vectors_list = g_list_prepend (NULL, vectors);
GList *vectors_list = NULL;
if (vectors != NULL)
vectors_list = g_list_prepend (vectors_list, vectors);
success = pika_vectors_export_file (image, vectors_list, file, error);
@ -1289,9 +1292,9 @@ sub vectors_export_to_string {
$help = <<'HELP';
This procedure works like pika_vectors_export_to_file() but creates a string
rather than a file. The contents are a NUL-terminated string that holds a
complete XML document. If you pass 0 as the 'vectors' argument, then all
paths in the image will be exported.
rather than a file. The string is NULL-terminated and holds a
complete XML document. Pass %NULL as the 'vectors' argument to export
all paths in the image.
HELP
&bill_pdb_misc('2007', '2.6');
@ -1300,7 +1303,7 @@ HELP
{ name => 'image', type => 'image',
desc => 'The image' },
{ name => 'vectors', type => 'vectors', no_validate => 1,
desc => 'The vectors object to save, or 0 for all in the image' }
desc => 'The vectors object to export, or %NULL for all in the image' }
);
@outargs = (
@ -1312,7 +1315,10 @@ HELP
headers => [ qw("vectors/pikavectors-export.h") ],
code => <<'CODE'
{
GList *vectors_list = g_list_prepend (NULL, vectors);
GList *vectors_list = NULL;
if (vectors != NULL)
vectors_list = g_list_prepend (vectors_list, vectors);
string = pika_vectors_export_string (image, vectors_list);
g_list_free (vectors_list);

View File

@ -352,7 +352,8 @@ static struct
gint delayTime;
gint inputFlag;
gint disposal;
} Gif89 = { -1, -1, -1, 0 };
gint num_loops;
} Gif89 = { -1, -1, -1, 0, -1 };
static void read_error (const gchar *error_type,
PikaImage *image,
@ -399,9 +400,10 @@ load_image (GFile *file,
gint grayScale;
gboolean useGlobalColormap;
gint bitPixel;
gint imageCount = 0;
PikaImage *image = NULL;
gint imageCount = 0;
PikaImage *image = NULL;
gboolean status;
gboolean saved_parasite = FALSE;
pika_progress_init_printf (_("Opening '%s'"),
pika_file_get_utf8_name (file));
@ -590,6 +592,25 @@ load_image (GFile *file,
/* If we are loading a thumbnail, we stop after the first frame. */
if (thumbnail)
break;
/* If there is more than one frame, we add a parasite so that
* we know to export as an animation on overwrite */
if (Gif89.num_loops > -1 && ! saved_parasite)
{
PikaParasite *parasite;
gchar *str;
str = g_strdup_printf ("%d", Gif89.num_loops);
parasite = pika_parasite_new ("gif/animated",
PIKA_PARASITE_PERSISTENT,
strlen (str) + 1,
(gpointer) str);
g_free (str);
pika_image_attach_parasite (image, parasite);
pika_parasite_free (parasite);
saved_parasite = TRUE;
}
}
fclose (fd);
@ -690,6 +711,15 @@ DoExtension (FILE *fd,
#ifdef GIFDEBUG
str = "Application Extension";
#endif
/* Animation block */
if (GetDataBlock (fd, (guchar *) buf))
{
if (strncmp ((const gchar *) buf, "NETSCAPE2.0", 8) == 0)
{
if (GetDataBlock (fd, (guchar *) buf))
Gif89.num_loops = (buf[0] << 8) | buf[1];
}
}
break;
case 0xfe: /* Comment Extension */
#ifdef GIFDEBUG

View File

@ -268,6 +268,7 @@ gif_save (PikaProcedure *procedure,
PikaExportReturn export = PIKA_EXPORT_CANCEL;
PikaImage *orig_image;
PikaImage *sanitized_image = NULL;
PikaParasite *parasite = NULL;
GError *error = NULL;
gegl_init (NULL, NULL);
@ -288,6 +289,34 @@ gif_save (PikaProcedure *procedure,
*/
sanitized_image = image;
/* If imported as an animation, set the animation configurations
* when overwriting the file */
parasite = pika_image_get_parasite (image, "gif/animated");
if (parasite)
{
gint num_loops;
gchar *parasite_data;
guint32 parasite_size;
parasite_data = (gchar *) pika_parasite_get_data (parasite, &parasite_size);
parasite_data = g_strndup (parasite_data, parasite_size);
if (sscanf (parasite_data, "%i", &num_loops) == 1)
{
gboolean loop = (num_loops == 0);
g_object_set (config,
"as-animation", TRUE,
"loop", loop,
"number-of-repeats", num_loops,
NULL);
}
pika_image_detach_parasite (image, "gif/animated");
g_free (parasite);
g_free (parasite_data);
}
if (run_mode == PIKA_RUN_INTERACTIVE)
{
if (! save_dialog (image, procedure, G_OBJECT (config)))

View File

@ -31,6 +31,7 @@
#define LOAD_PROC "file-heif-load"
#define LOAD_PROC_AV1 "file-heif-av1-load"
#define LOAD_PROC_HEJ2 "file-heif-hej2-load"
#define SAVE_PROC "file-heif-save"
#define SAVE_PROC_AV1 "file-heif-av1-save"
#define PLUG_IN_BINARY "file-heif"
@ -103,6 +104,8 @@ static PikaValueArray * heif_av1_save (PikaProcedure *pro
#endif
static PikaImage * load_image (GFile *file,
PikaMetadata *metadata,
PikaMetadataLoadFlags *flags,
gboolean interactive,
PikaPDBStatusType *status,
GError **error);
@ -172,6 +175,13 @@ heif_init_procedures (PikaPlugIn *plug_in)
}
#endif
#if LIBHEIF_HAVE_VERSION(1,17,0)
if (heif_have_decoder_for_format (heif_compression_JPEG2000))
{
list = g_list_append (list, g_strdup (LOAD_PROC_HEJ2));
}
#endif
#if LIBHEIF_HAVE_VERSION(1,13,0)
heif_deinit ();
#endif
@ -412,6 +422,33 @@ heif_create_procedure (PikaPlugIn *plug_in,
pika_export_xmp (),
G_PARAM_READWRITE);
}
#endif
#if LIBHEIF_HAVE_VERSION(1,17,0)
else if (! strcmp (name, LOAD_PROC_HEJ2))
{
procedure = pika_load_procedure_new (plug_in, name, PIKA_PDB_PROC_TYPE_PLUGIN,
heif_load, NULL, NULL);
pika_procedure_set_menu_label (procedure, _("JPEG 2000 encapsulated in HEIF"));
pika_procedure_set_documentation (procedure,
_("Loads HEJ2 images"),
_("Load JPEG 2000 image encapsulated in HEIF (HEJ2)"),
name);
pika_procedure_set_attribution (procedure,
"Daniel Novomesky <dnovomesky@gmail.com>",
"Daniel Novomesky <dnovomesky@gmail.com>",
"2023");
pika_file_procedure_set_handles_remote (PIKA_FILE_PROCEDURE (procedure), TRUE);
pika_file_procedure_set_mime_types (PIKA_FILE_PROCEDURE (procedure),
"image/hej2k");
pika_file_procedure_set_extensions (PIKA_FILE_PROCEDURE (procedure),
"hej2");
pika_file_procedure_set_magics (PIKA_FILE_PROCEDURE (procedure),
"4,string,ftypj2ki");
}
#endif
return procedure;
}
@ -442,7 +479,7 @@ heif_load (PikaProcedure *procedure,
heif_init (NULL);
#endif
image = load_image (file, interactive, &status, &error);
image = load_image (file, metadata, flags, interactive, &status, &error);
#if LIBHEIF_HAVE_VERSION(1,13,0)
heif_deinit ();
@ -836,10 +873,12 @@ nclx_to_pika_profile (const struct heif_color_profile_nclx *nclx)
#endif
PikaImage *
load_image (GFile *file,
gboolean interactive,
PikaPDBStatusType *status,
GError **error)
load_image (GFile *file,
PikaMetadata *metadata,
PikaMetadataLoadFlags *flags,
gboolean interactive,
PikaPDBStatusType *status,
GError **error)
{
GInputStream *input;
goffset file_size;
@ -1267,117 +1306,111 @@ load_image (GFile *file,
g_object_unref (buffer);
{
size_t exif_data_size = 0;
uint8_t *exif_data = NULL;
size_t xmp_data_size = 0;
uint8_t *xmp_data = NULL;
gint n_metadata;
heif_item_id metadata_id;
if (metadata)
{
size_t exif_data_size = 0;
uint8_t *exif_data = NULL;
size_t xmp_data_size = 0;
uint8_t *xmp_data = NULL;
gint n_metadata;
heif_item_id metadata_id;
n_metadata =
heif_image_handle_get_list_of_metadata_block_IDs (handle,
"Exif",
&metadata_id, 1);
if (n_metadata > 0)
{
exif_data_size = heif_image_handle_get_metadata_size (handle,
metadata_id);
exif_data = g_alloca (exif_data_size);
n_metadata = heif_image_handle_get_list_of_metadata_block_IDs (handle, "Exif",
&metadata_id, 1);
if (n_metadata > 0)
{
exif_data_size = heif_image_handle_get_metadata_size (handle, metadata_id);
err = heif_image_handle_get_metadata (handle, metadata_id, exif_data);
if (err.code != 0)
{
exif_data = NULL;
exif_data_size = 0;
}
}
exif_data = g_alloca (exif_data_size);
n_metadata =
heif_image_handle_get_list_of_metadata_block_IDs (handle,
"mime",
&metadata_id, 1);
if (n_metadata > 0)
{
if (g_strcmp0 (
heif_image_handle_get_metadata_content_type (handle, metadata_id),
"application/rdf+xml") == 0)
{
xmp_data_size = heif_image_handle_get_metadata_size (handle,
metadata_id);
xmp_data = g_alloca (xmp_data_size);
err = heif_image_handle_get_metadata (handle, metadata_id, exif_data);
if (err.code != 0)
{
exif_data = NULL;
exif_data_size = 0;
}
}
err = heif_image_handle_get_metadata (handle, metadata_id, xmp_data);
if (err.code != 0)
{
xmp_data = NULL;
xmp_data_size = 0;
}
}
}
n_metadata = heif_image_handle_get_list_of_metadata_block_IDs (handle, "mime",
&metadata_id, 1);
if (n_metadata > 0)
{
if (g_strcmp0 (heif_image_handle_get_metadata_content_type (handle, metadata_id), "application/rdf+xml")
== 0)
{
xmp_data_size = heif_image_handle_get_metadata_size (handle, metadata_id);
if (exif_data || xmp_data)
{
PikaMetadata *metadata = pika_metadata_new ();
PikaMetadataLoadFlags flags = PIKA_METADATA_LOAD_COMMENT | PIKA_METADATA_LOAD_RESOLUTION;
xmp_data = g_alloca (xmp_data_size);
if (exif_data)
{
const guint8 tiffHeaderBE[4] = { 'M', 'M', 0, 42 };
const guint8 tiffHeaderLE[4] = { 'I', 'I', 42, 0 };
GExiv2Metadata *exif_metadata = GEXIV2_METADATA (metadata);
const guint8 *tiffheader = exif_data;
glong new_exif_size = exif_data_size;
err = heif_image_handle_get_metadata (handle, metadata_id, xmp_data);
if (err.code != 0)
{
xmp_data = NULL;
xmp_data_size = 0;
}
}
}
while (new_exif_size >= 4) /*Searching for TIFF Header*/
{
if (tiffheader[0] == tiffHeaderBE[0] && tiffheader[1] == tiffHeaderBE[1] &&
tiffheader[2] == tiffHeaderBE[2] && tiffheader[3] == tiffHeaderBE[3])
{
break;
}
if (tiffheader[0] == tiffHeaderLE[0] && tiffheader[1] == tiffHeaderLE[1] &&
tiffheader[2] == tiffHeaderLE[2] && tiffheader[3] == tiffHeaderLE[3])
{
break;
}
new_exif_size--;
tiffheader++;
}
if (exif_data || xmp_data)
{
gexiv2_metadata_clear (GEXIV2_METADATA (metadata));
if (new_exif_size > 4) /* TIFF header + some data found*/
{
if (! gexiv2_metadata_open_buf (exif_metadata, tiffheader, new_exif_size, error))
{
g_printerr ("%s: Failed to set EXIF metadata: %s\n", G_STRFUNC, (*error)->message);
g_clear_error (error);
}
}
else
{
g_printerr ("%s: EXIF metadata not set\n", G_STRFUNC);
}
}
if (exif_data)
{
const guint8 tiffHeaderBE[4] = { 'M', 'M', 0, 42 };
const guint8 tiffHeaderLE[4] = { 'I', 'I', 42, 0 };
GExiv2Metadata *exif_metadata = GEXIV2_METADATA (metadata);
const guint8 *tiffheader = exif_data;
glong new_exif_size = exif_data_size;
if (xmp_data)
{
if (!pika_metadata_set_from_xmp (metadata, xmp_data, xmp_data_size, error))
{
g_printerr ("%s: Failed to set XMP metadata: %s\n", G_STRFUNC, (*error)->message);
g_clear_error (error);
}
}
while (new_exif_size >= 4) /*Searching for TIFF Header*/
{
if (tiffheader[0] == tiffHeaderBE[0] && tiffheader[1] == tiffHeaderBE[1] &&
tiffheader[2] == tiffHeaderBE[2] && tiffheader[3] == tiffHeaderBE[3])
{
break;
}
if (tiffheader[0] == tiffHeaderLE[0] && tiffheader[1] == tiffHeaderLE[1] &&
tiffheader[2] == tiffHeaderLE[2] && tiffheader[3] == tiffHeaderLE[3])
{
break;
}
new_exif_size--;
tiffheader++;
}
gexiv2_metadata_try_set_orientation (GEXIV2_METADATA (metadata),
GEXIV2_ORIENTATION_NORMAL, NULL);
gexiv2_metadata_try_set_metadata_pixel_width (GEXIV2_METADATA (metadata),
width, NULL);
gexiv2_metadata_try_set_metadata_pixel_height (GEXIV2_METADATA (metadata),
height, NULL);
pika_image_metadata_load_finish (image, "image/heif",
metadata, flags);
}
}
if (new_exif_size > 4) /* TIFF header + some data found*/
{
if (! gexiv2_metadata_open_buf (exif_metadata, tiffheader, new_exif_size, error))
{
g_printerr ("%s: Failed to set EXIF metadata: %s\n", G_STRFUNC, (*error)->message);
g_clear_error (error);
}
}
else
{
g_printerr ("%s: EXIF metadata not set\n", G_STRFUNC);
}
}
if (xmp_data)
{
if (! pika_metadata_set_from_xmp (metadata, xmp_data, xmp_data_size, error))
{
g_printerr ("%s: Failed to set XMP metadata: %s\n", G_STRFUNC, (*error)->message);
g_clear_error (error);
}
}
}
gexiv2_metadata_try_set_orientation (GEXIV2_METADATA (metadata),
GEXIV2_ORIENTATION_NORMAL, NULL);
gexiv2_metadata_try_set_metadata_pixel_width (GEXIV2_METADATA (metadata), width, NULL);
gexiv2_metadata_try_set_metadata_pixel_height (GEXIV2_METADATA (metadata),
height, NULL);
*flags = PIKA_METADATA_LOAD_COMMENT | PIKA_METADATA_LOAD_RESOLUTION;
}
if (profile)
g_object_unref (profile);

View File

@ -230,10 +230,10 @@ load_image (GFile *file,
{
ILBM_Image *true_image = iff_image[i];
/* Struct representing bitmap header properties */
ILBM_BitMapHeader *bitMapHeader = true_image->bitMapHeader;
ILBM_BitMapHeader *bitMapHeader;
/* Struct containing the color palette */
ILBM_ColorMap *colorMap = true_image->colorMap;
ILBM_Viewport *camg = true_image->viewport;
ILBM_ColorMap *colorMap;
ILBM_Viewport *camg;
IFF_UByte *bitplanes;
PikaImageType image_type;
guchar pika_cmap[768]; /* Max index is (2^nplanes) - 1 */
@ -244,29 +244,38 @@ load_image (GFile *file,
gint row_length;
gint pixel_size = 1;
gint y_height = 0;
gint aspect_x = 0;
gint aspect_y = 0;
gboolean ehb_mode = FALSE;
gboolean ham_mode = FALSE;
if (! true_image || ! bitMapHeader)
if (! true_image)
{
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;
}
colorMap = true_image->colorMap;
camg = true_image->viewport;
/* Convert ACBM files to ILBM format */
if (ILBM_imageIsACBM (true_image))
ILBM_convertACBMToILBM (true_image);
bitMapHeader = true_image->bitMapHeader;
if (! bitMapHeader || ! true_image->body)
{
g_message (_("ILBM contains no image data - likely a palette file"));
return NULL;
}
width = bitMapHeader->w;
height = bitMapHeader->h;
nPlanes = bitMapHeader->nPlanes;
row_length = (width + 15) / 16;
pixel_size = nPlanes / 8;
aspect_x = bitMapHeader->xAspect;
aspect_y = bitMapHeader->yAspect;
/* Check for ILBM variants in CMAG chunk */
if (camg)
@ -329,8 +338,9 @@ load_image (GFile *file,
ILBM_unpackByteRun (true_image);
image = pika_image_new (width, height,
pixel_size == 1 ? PIKA_INDEXED : PIKA_RGB);
if (! 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,
@ -341,6 +351,24 @@ load_image (GFile *file,
bitplanes = true_image->body->chunkData;
/* Setting resolution for non-square pixel aspect ratios */
if (aspect_x != aspect_y && aspect_x > 0 && aspect_y > 0)
{
gdouble image_xres;
gdouble image_yres;
gfloat ratio = (gfloat) aspect_x / aspect_y;
g_message (_("Non-square pixels. Image might look squashed if "
"Dot for Dot mode is enabled."));
pika_image_get_resolution (image, &image_xres, &image_yres);
if (ratio < 1)
image_xres = image_yres * (1 / ratio);
else
image_yres = image_xres * ratio;
pika_image_set_resolution (image, image_xres, image_yres);
}
/* Loading rows */
for (gint j = 0; j < height; j++)
{

View File

@ -396,9 +396,11 @@ create_cmyk_layer (PikaImage *image,
}
static PikaImage *
load_image (GFile *file,
PikaRunMode runmode,
GError **error)
load_image (GFile *file,
PikaRunMode runmode,
PikaMetadata *metadata,
PikaMetadataLoadFlags *flags,
GError **error)
{
FILE *inputFile = g_fopen (g_file_peek_path (file), "rb");
@ -430,6 +432,7 @@ load_image (GFile *file,
const Babl *type;
PikaPrecision precision_linear;
PikaPrecision precision_non_linear;
uint32_t i;
if (!inputFile)
{
@ -637,7 +640,7 @@ load_image (GFile *file,
}
/* Check for extra channels */
for (gint32 i = 0; i < basicinfo.num_extra_channels; i++)
for (i = 0; i < basicinfo.num_extra_channels; i++)
{
JxlExtraChannelInfo extra;
@ -936,205 +939,208 @@ load_image (GFile *file,
g_object_unref (profile);
}
if (basicinfo.have_container)
if (metadata)
{
JxlDecoderReleaseInput (decoder);
JxlDecoderRewind (decoder);
if (basicinfo.have_container)
{
JxlDecoderReleaseInput (decoder);
JxlDecoderRewind (decoder);
if (JxlDecoderSetInput (decoder, memory, inputFileSize) != JXL_DEC_SUCCESS)
{
g_printerr ("%s: JxlDecoderSetInput failed after JxlDecoderRewind\n", G_STRFUNC);
}
else
{
JxlDecoderCloseInput (decoder);
if (JxlDecoderSubscribeEvents (decoder, JXL_DEC_BOX) != JXL_DEC_SUCCESS)
if (JxlDecoderSetInput (decoder, memory, inputFileSize) != JXL_DEC_SUCCESS)
{
g_printerr ("%s: JxlDecoderSubscribeEvents for JXL_DEC_BOX failed\n", G_STRFUNC);
g_printerr ("%s: JxlDecoderSetInput failed after JxlDecoderRewind\n", G_STRFUNC);
}
else
{
gboolean search_exif = TRUE;
gboolean search_xmp = TRUE;
gboolean success_exif = FALSE;
gboolean success_xmp = FALSE;
JxlBoxType box_type = { 0, 0, 0, 0 };
GByteArray *exif_box = NULL;
GByteArray *xml_box = NULL;
size_t exif_remains = 0;
size_t xml_remains = 0;
while (search_exif || search_xmp)
JxlDecoderCloseInput (decoder);
if (JxlDecoderSubscribeEvents (decoder, JXL_DEC_BOX) != JXL_DEC_SUCCESS)
{
status = JxlDecoderProcessInput (decoder);
switch (status)
g_printerr ("%s: JxlDecoderSubscribeEvents for JXL_DEC_BOX failed\n", G_STRFUNC);
}
else
{
gboolean search_exif = TRUE;
gboolean search_xmp = TRUE;
gboolean success_exif = FALSE;
gboolean success_xmp = FALSE;
JxlBoxType box_type = { 0, 0, 0, 0 };
GByteArray *exif_box = NULL;
GByteArray *xml_box = NULL;
size_t exif_remains = 0;
size_t xml_remains = 0;
while (search_exif || search_xmp)
{
case JXL_DEC_SUCCESS:
if (box_type[0] == 'E' && box_type[1] == 'x' && box_type[2] == 'i' && box_type[3] == 'f' && search_exif)
{
exif_remains = JxlDecoderReleaseBoxBuffer (decoder);
g_byte_array_set_size (exif_box, exif_box->len - exif_remains);
success_exif = TRUE;
}
else if (box_type[0] == 'x' && box_type[1] == 'm' && box_type[2] == 'l' && box_type[3] == ' ' && search_xmp)
{
xml_remains = JxlDecoderReleaseBoxBuffer (decoder);
g_byte_array_set_size (xml_box, xml_box->len - xml_remains);
success_xmp = TRUE;
}
search_exif = FALSE;
search_xmp = FALSE;
break;
case JXL_DEC_ERROR:
search_exif = FALSE;
search_xmp = FALSE;
g_printerr ("%s: Metadata decoding error\n", G_STRFUNC);
break;
case JXL_DEC_NEED_MORE_INPUT:
search_exif = FALSE;
search_xmp = FALSE;
g_printerr ("%s: JXL metadata are probably incomplete\n", G_STRFUNC);
break;
case JXL_DEC_BOX:
JxlDecoderSetDecompressBoxes (decoder, JXL_TRUE);
if (box_type[0] == 'E' && box_type[1] == 'x' && box_type[2] == 'i' && box_type[3] == 'f' && search_exif && exif_box)
{
exif_remains = JxlDecoderReleaseBoxBuffer (decoder);
g_byte_array_set_size (exif_box, exif_box->len - exif_remains);
search_exif = FALSE;
success_exif = TRUE;
}
else if (box_type[0] == 'x' && box_type[1] == 'm' && box_type[2] == 'l' && box_type[3] == ' ' && search_xmp && xml_box)
{
xml_remains = JxlDecoderReleaseBoxBuffer (decoder);
g_byte_array_set_size (xml_box, xml_box->len - xml_remains);
search_xmp = FALSE;
success_xmp = TRUE;
}
if (JxlDecoderGetBoxType (decoder, box_type, JXL_TRUE) == JXL_DEC_SUCCESS)
status = JxlDecoderProcessInput (decoder);
switch (status)
{
case JXL_DEC_SUCCESS:
if (box_type[0] == 'E' && box_type[1] == 'x' && box_type[2] == 'i' && box_type[3] == 'f' && search_exif)
{
exif_box = g_byte_array_sized_new (4096);
g_byte_array_set_size (exif_box, 4096);
JxlDecoderSetBoxBuffer (decoder, exif_box->data, exif_box->len);
exif_remains = JxlDecoderReleaseBoxBuffer (decoder);
g_byte_array_set_size (exif_box, exif_box->len - exif_remains);
success_exif = TRUE;
}
else if (box_type[0] == 'x' && box_type[1] == 'm' && box_type[2] == 'l' && box_type[3] == ' ' && search_xmp)
{
xml_box = g_byte_array_sized_new (4096);
g_byte_array_set_size (xml_box, 4096);
JxlDecoderSetBoxBuffer (decoder, xml_box->data, xml_box->len);
xml_remains = JxlDecoderReleaseBoxBuffer (decoder);
g_byte_array_set_size (xml_box, xml_box->len - xml_remains);
success_xmp = TRUE;
}
}
else
{
search_exif = FALSE;
search_xmp = FALSE;
g_printerr ("%s: Error in JxlDecoderGetBoxType\n", G_STRFUNC);
}
break;
case JXL_DEC_BOX_NEED_MORE_OUTPUT:
if (box_type[0] == 'E' && box_type[1] == 'x' && box_type[2] == 'i' && box_type[3] == 'f' && search_exif)
{
exif_remains = JxlDecoderReleaseBoxBuffer (decoder);
g_byte_array_set_size (exif_box, exif_box->len + 4096);
JxlDecoderSetBoxBuffer (decoder, exif_box->data + exif_box->len - (4096 + exif_remains), 4096 + exif_remains);
}
else if (box_type[0] == 'x' && box_type[1] == 'm' && box_type[2] == 'l' && box_type[3] == ' ' && search_xmp)
{
xml_remains = JxlDecoderReleaseBoxBuffer (decoder);
g_byte_array_set_size (xml_box, xml_box->len + 4096);
JxlDecoderSetBoxBuffer (decoder, xml_box->data + xml_box->len - (4096 + xml_remains), 4096 + xml_remains);
}
else
{
break;
case JXL_DEC_ERROR:
search_exif = FALSE;
search_xmp = FALSE;
g_printerr ("%s: Metadata decoding error\n", G_STRFUNC);
break;
case JXL_DEC_NEED_MORE_INPUT:
search_exif = FALSE;
search_xmp = FALSE;
g_printerr ("%s: JXL metadata are probably incomplete\n", G_STRFUNC);
break;
case JXL_DEC_BOX:
JxlDecoderSetDecompressBoxes (decoder, JXL_TRUE);
if (box_type[0] == 'E' && box_type[1] == 'x' && box_type[2] == 'i' && box_type[3] == 'f' && search_exif && exif_box)
{
exif_remains = JxlDecoderReleaseBoxBuffer (decoder);
g_byte_array_set_size (exif_box, exif_box->len - exif_remains);
search_exif = FALSE;
success_exif = TRUE;
}
else if (box_type[0] == 'x' && box_type[1] == 'm' && box_type[2] == 'l' && box_type[3] == ' ' && search_xmp && xml_box)
{
xml_remains = JxlDecoderReleaseBoxBuffer (decoder);
g_byte_array_set_size (xml_box, xml_box->len - xml_remains);
search_xmp = FALSE;
success_xmp = TRUE;
}
if (JxlDecoderGetBoxType (decoder, box_type, JXL_TRUE) == JXL_DEC_SUCCESS)
{
if (box_type[0] == 'E' && box_type[1] == 'x' && box_type[2] == 'i' && box_type[3] == 'f' && search_exif)
{
exif_box = g_byte_array_sized_new (4096);
g_byte_array_set_size (exif_box, 4096);
JxlDecoderSetBoxBuffer (decoder, exif_box->data, exif_box->len);
}
else if (box_type[0] == 'x' && box_type[1] == 'm' && box_type[2] == 'l' && box_type[3] == ' ' && search_xmp)
{
xml_box = g_byte_array_sized_new (4096);
g_byte_array_set_size (xml_box, 4096);
JxlDecoderSetBoxBuffer (decoder, xml_box->data, xml_box->len);
}
}
else
{
search_exif = FALSE;
search_xmp = FALSE;
g_printerr ("%s: Error in JxlDecoderGetBoxType\n", G_STRFUNC);
}
break;
case JXL_DEC_BOX_NEED_MORE_OUTPUT:
if (box_type[0] == 'E' && box_type[1] == 'x' && box_type[2] == 'i' && box_type[3] == 'f' && search_exif)
{
exif_remains = JxlDecoderReleaseBoxBuffer (decoder);
g_byte_array_set_size (exif_box, exif_box->len + 4096);
JxlDecoderSetBoxBuffer (decoder, exif_box->data + exif_box->len - (4096 + exif_remains), 4096 + exif_remains);
}
else if (box_type[0] == 'x' && box_type[1] == 'm' && box_type[2] == 'l' && box_type[3] == ' ' && search_xmp)
{
xml_remains = JxlDecoderReleaseBoxBuffer (decoder);
g_byte_array_set_size (xml_box, xml_box->len + 4096);
JxlDecoderSetBoxBuffer (decoder, xml_box->data + xml_box->len - (4096 + xml_remains), 4096 + xml_remains);
}
else
{
search_exif = FALSE;
search_xmp = FALSE;
}
break;
default:
break;
}
break;
default:
break;
}
}
if (success_exif || success_xmp)
{
PikaMetadata *metadata = pika_metadata_new ();
if (success_exif && exif_box)
if (success_exif || success_xmp)
{
const guint8 tiffHeaderBE[4] = { 'M', 'M', 0, 42 };
const guint8 tiffHeaderLE[4] = { 'I', 'I', 42, 0 };
const guint8 *tiffheader = exif_box->data;
glong new_exif_size = exif_box->len;
gexiv2_metadata_clear (GEXIV2_METADATA (metadata));
while (new_exif_size >= 4) /*Searching for TIFF Header*/
if (success_exif && exif_box)
{
if (tiffheader[0] == tiffHeaderBE[0] && tiffheader[1] == tiffHeaderBE[1] &&
tiffheader[2] == tiffHeaderBE[2] && tiffheader[3] == tiffHeaderBE[3])
const guint8 tiffHeaderBE[4] = { 'M', 'M', 0, 42 };
const guint8 tiffHeaderLE[4] = { 'I', 'I', 42, 0 };
const guint8 *tiffheader = exif_box->data;
glong new_exif_size = exif_box->len;
while (new_exif_size >= 4) /*Searching for TIFF Header*/
{
break;
if (tiffheader[0] == tiffHeaderBE[0] && tiffheader[1] == tiffHeaderBE[1] &&
tiffheader[2] == tiffHeaderBE[2] && tiffheader[3] == tiffHeaderBE[3])
{
break;
}
if (tiffheader[0] == tiffHeaderLE[0] && tiffheader[1] == tiffHeaderLE[1] &&
tiffheader[2] == tiffHeaderLE[2] && tiffheader[3] == tiffHeaderLE[3])
{
break;
}
new_exif_size--;
tiffheader++;
}
if (tiffheader[0] == tiffHeaderLE[0] && tiffheader[1] == tiffHeaderLE[1] &&
tiffheader[2] == tiffHeaderLE[2] && tiffheader[3] == tiffHeaderLE[3])
if (new_exif_size > 4) /* TIFF header + some data found*/
{
break;
if (! gexiv2_metadata_open_buf (GEXIV2_METADATA (metadata), tiffheader, new_exif_size, error))
{
g_printerr ("%s: Failed to set EXIF metadata: %s\n", G_STRFUNC, (*error)->message);
g_clear_error (error);
}
}
else
{
g_printerr ("%s: EXIF metadata not set\n", G_STRFUNC);
}
new_exif_size--;
tiffheader++;
}
if (new_exif_size > 4) /* TIFF header + some data found*/
if (success_xmp && xml_box)
{
if (! gexiv2_metadata_open_buf (GEXIV2_METADATA (metadata), tiffheader, new_exif_size, error))
if (! pika_metadata_set_from_xmp (metadata, xml_box->data, xml_box->len, error))
{
g_printerr ("%s: Failed to set EXIF metadata: %s\n", G_STRFUNC, (*error)->message);
g_printerr ("%s: Failed to set XMP metadata: %s\n", G_STRFUNC, (*error)->message);
g_clear_error (error);
}
}
else
{
g_printerr ("%s: EXIF metadata not set\n", G_STRFUNC);
}
}
if (success_xmp && xml_box)
if (exif_box)
{
if (! pika_metadata_set_from_xmp (metadata, xml_box->data, xml_box->len, error))
{
g_printerr ("%s: Failed to set XMP metadata: %s\n", G_STRFUNC, (*error)->message);
g_clear_error (error);
}
g_byte_array_free (exif_box, TRUE);
}
gexiv2_metadata_try_set_orientation (GEXIV2_METADATA (metadata),
GEXIV2_ORIENTATION_NORMAL, NULL);
gexiv2_metadata_try_set_metadata_pixel_width (GEXIV2_METADATA (metadata),
basicinfo.xsize, NULL);
gexiv2_metadata_try_set_metadata_pixel_height (GEXIV2_METADATA (metadata),
basicinfo.ysize, NULL);
pika_image_metadata_load_finish (image, "image/jxl", metadata,
PIKA_METADATA_LOAD_COMMENT | PIKA_METADATA_LOAD_RESOLUTION);
}
if (exif_box)
{
g_byte_array_free (exif_box, TRUE);
}
if (xml_box)
{
g_byte_array_free (xml_box, TRUE);
if (xml_box)
{
g_byte_array_free (xml_box, TRUE);
}
}
}
}
gexiv2_metadata_try_set_orientation (GEXIV2_METADATA (metadata),
GEXIV2_ORIENTATION_NORMAL, NULL);
gexiv2_metadata_try_set_metadata_pixel_width (GEXIV2_METADATA (metadata),
basicinfo.xsize, NULL);
gexiv2_metadata_try_set_metadata_pixel_height (GEXIV2_METADATA (metadata),
basicinfo.ysize, NULL);
*flags = PIKA_METADATA_LOAD_COMMENT | PIKA_METADATA_LOAD_RESOLUTION;
}
JxlThreadParallelRunnerDestroy (runner);
@ -1158,7 +1164,7 @@ jpegxl_load (PikaProcedure *procedure,
gegl_init (NULL, NULL);
image = load_image (file, run_mode, &error);
image = load_image (file, run_mode, metadata, flags, &error);
if (! image)
return pika_procedure_new_return_values (procedure,

View File

@ -886,12 +886,13 @@ gui_single (PikaProcedure *procedure,
PikaProcedureConfig *config,
PikaImage *image)
{
GtkWidget *window;
GtkWidget *widget;
GList *missing_fonts;
GList *dialog_props = NULL;
gboolean run;
gint32 n_layers;
GtkWidget *window;
GtkWidget *widget;
PikaLayer **layers;
GList *missing_fonts;
GList *dialog_props = NULL;
gboolean run;
gint32 n_layers;
pika_ui_init (PLUG_IN_BINARY);
@ -910,7 +911,13 @@ gui_single (PikaProcedure *procedure,
widget = pika_procedure_dialog_fill_frame (PIKA_PROCEDURE_DIALOG (window),
"pages-frame", "layers-as-pages", FALSE,
"pages-box");
g_free (pika_image_get_layers (multi_page.images[0], &n_layers));
/* Enable "layers-as-pages" if more than one layer, or there's a single
* layer group has more than one layer */
layers = pika_image_get_layers (multi_page.images[0], &n_layers);
if (n_layers == 1 && pika_item_is_group (PIKA_ITEM (layers[0])))
g_free (pika_item_get_children (PIKA_ITEM (layers[0]), &n_layers));
g_free (layers);
gtk_widget_set_sensitive (widget, n_layers > 1);
/* Warning for missing fonts (non-embeddable with rasterization

View File

@ -65,6 +65,7 @@ typedef enum _PngExportformat
PNG_FORMAT_GRAYA16
} PngExportFormat;
static GSList *safe_to_copy_chunks;
typedef struct _Png Png;
typedef struct _PngClass PngClass;
@ -139,6 +140,9 @@ static gboolean ia_has_transparent_pixels (GeglBuffer *buffer);
static gint find_unused_ia_color (GeglBuffer *buffer,
gint *colors);
static gint read_unknown_chunk (png_structp png_ptr,
png_unknown_chunkp chunk);
G_DEFINE_TYPE (Png, png, PIKA_TYPE_PLUG_IN)
@ -602,6 +606,7 @@ load_image (GFile *file,
const Babl *file_format; /* BABL format for layer */
png_structp pp; /* PNG read pointer */
png_infop info; /* PNG info pointers */
png_voidp user_chunkp; /* PNG unknown chunk pointer */
guchar **pixels; /* Pixel rows */
guchar *pixel; /* Pixel data */
guchar alpha[256]; /* Index -> Alpha */
@ -609,6 +614,8 @@ load_image (GFile *file,
gint num_texts;
struct read_error_data error_data;
safe_to_copy_chunks = NULL;
pp = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (! pp)
{
@ -667,6 +674,11 @@ load_image (GFile *file,
png_init_io (pp, fp);
png_set_compression_buffer_size (pp, 512);
/* Set up callback to save "safe to copy" chunks */
png_set_keep_unknown_chunks (pp, PNG_HANDLE_CHUNK_IF_SAFE, NULL, 0);
user_chunkp = png_get_user_chunk_ptr (pp);
png_set_read_user_chunk_fn (pp, user_chunkp, read_unknown_chunk);
/*
* Get the image info
*/
@ -1184,6 +1196,23 @@ load_image (GFile *file,
g_object_unref (buffer);
}
/* If any safe-to-copy chunks were saved,
* store them in the image as parasite */
if (safe_to_copy_chunks)
{
GSList *iter;
for (iter = safe_to_copy_chunks; iter; iter = iter->next)
{
PikaParasite *parasite = iter->data;
pika_image_attach_parasite ((PikaImage *) image, parasite);
pika_parasite_free (parasite);
}
g_slist_free (safe_to_copy_chunks);
}
return (PikaImage *) image;
}
@ -1292,6 +1321,7 @@ save_image (GFile *file,
gint num; /* Number of rows to load */
FILE *fp; /* File pointer */
PikaColorProfile *profile = NULL; /* Color profile */
gchar **parasites; /* Safe-to-copy chunks */
gboolean out_linear; /* Save linear RGB */
GeglBuffer *buffer; /* GEGL buffer for layer */
const Babl *file_format = NULL; /* BABL format of file */
@ -1914,6 +1944,51 @@ save_image (GFile *file,
if (G_BYTE_ORDER == G_LITTLE_ENDIAN)
png_set_swap (pp);
/* Write any safe-to-copy chunks saved from import */
parasites = pika_image_get_parasite_list (image);
if (parasites)
{
gint count;
count = g_strv_length (parasites);
for (gint i = 0; i < count; i++)
{
if (strncmp (parasites[i], "png", 3) == 0)
{
PikaParasite *parasite;
parasite = pika_image_get_parasite (image, parasites[i]);
if (parasite)
{
gchar buf[1024];
gchar *chunk_name;
g_strlcpy (buf, parasites[i], sizeof (buf));
chunk_name = strchr (buf, '/');
chunk_name++;
if (chunk_name)
{
png_byte name[4];
const guint8 *data;
guint32 len;
for (gint j = 0; j < 4; j++)
name[j] = chunk_name[j];
data = (const guint8 *) pika_parasite_get_data (parasite, &len);
png_write_chunk (pp, name, data, len);
}
pika_parasite_free (parasite);
}
}
}
}
g_strfreev (parasites);
/*
* Turn on interlace handling...
*/
@ -2263,6 +2338,28 @@ respin_cmap (png_structp pp,
return get_bit_depth_for_palette (colors);
}
static gint
read_unknown_chunk (png_structp png_ptr,
png_unknown_chunkp chunk)
{
/* Chunks with a lowercase letter in the 4th byte
* are safe to copy */
if (g_ascii_islower (chunk->name[3]))
{
PikaParasite *parasite;
gchar pname[255];
g_snprintf (pname, sizeof (pname), "png/%s", chunk->name);
if ((parasite = pika_parasite_new (pname,
PIKA_PARASITE_PERSISTENT,
chunk->size, chunk->data)))
safe_to_copy_chunks = g_slist_prepend (safe_to_copy_chunks, parasite);
}
return 0;
}
static gboolean
save_dialog (PikaImage *image,
PikaProcedure *procedure,

View File

@ -944,7 +944,7 @@ process_pam_header (PNMScanner *scan,
{
/* skip unknown headers but recognize xv's thumbnail format */
CHECK_FOR_ERROR (g_ascii_isdigit (*buf), pnminfo->jmpbuf,
_("PAM: Unsupported inofficial PNM variant."));
_("PAM: Unsupported unofficial PNM variant."));
}
}
CHECK_FOR_ERROR (pnmscanner_eof (scan), pnminfo->jmpbuf,

View File

@ -77,7 +77,7 @@ struct _LicClass
#define LIC_TYPE (lic_get_type ())
#define LIC (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), LIC_TYPE, Lic))
#define LIC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), LIC_TYPE, Lic))
GType lic_get_type (void) G_GNUC_CONST;

View File

@ -381,6 +381,7 @@ load_image (GFile *file,
_("Unsupported compression (%u) in BMP file from '%s'"),
bitmap_head.biCompr,
pika_file_get_utf8_name (file));
goto out;
}
#ifdef DEBUG

View File

@ -42,10 +42,6 @@
#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;
@ -86,15 +82,6 @@ static PikaValueArray * dds_save (PikaProcedure *procedure,
PikaMetadata *metadata,
PikaProcedureConfig *config,
gpointer run_data);
#if 0
static PikaValueArray * dds_decode (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
PikaProcedureConfig *config,
gpointer run_data);
#endif
G_DEFINE_TYPE (Dds, dds, PIKA_TYPE_PLUG_IN)
@ -125,11 +112,6 @@ dds_query_procedures (PikaPlugIn *plug_in)
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;
}
@ -170,10 +152,10 @@ dds_create_procedure (PikaPlugIn *plug_in,
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,
PIKA_PROC_ARG_BOOLEAN (procedure, "flip-image",
_("Flip image _vertically"),
_("Flip the image vertically on import"),
FALSE,
G_PARAM_READWRITE);
}
else if (! strcmp (name, SAVE_PROC))
@ -249,13 +231,17 @@ dds_create_procedure (PikaPlugIn *plug_in,
"default",
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_CHOICE (procedure, "save-type",
_("Sav_e type"),
_("How to save the image"),
pika_choice_new_with_values ("layer", DDS_SAVE_SELECTED_LAYER, _("Selected layer"), NULL,
"canvas", DDS_SAVE_VISIBLE_LAYERS, _("All visible layers"), NULL,
"cube", DDS_SAVE_CUBEMAP, _("As cube map"), NULL,
"volume", DDS_SAVE_VOLUMEMAP, _("As volume map"), NULL,
"array", DDS_SAVE_ARRAY, _("As texture array"), NULL,
NULL),
"layer",
G_PARAM_READWRITE);
PIKA_PROC_ARG_BOOLEAN (procedure, "flip-image",
_("Flip image _vertically on export"),
@ -276,26 +262,29 @@ dds_create_procedure (PikaPlugIn *plug_in,
0, 255, 0,
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_CHOICE (procedure, "mipmaps",
_("_Mipmaps"),
_("How to handle mipmaps"),
pika_choice_new_with_values ("none", DDS_MIPMAP_NONE, _("No mipmaps"), NULL,
"generate", DDS_MIPMAP_GENERATE, _("Generate mipmaps"), NULL,
"existing", DDS_MIPMAP_EXISTING, _("Use existing mipmaps"), NULL,
NULL),
"none",
G_PARAM_READWRITE);
PIKA_PROC_ARG_CHOICE (procedure, "mipmap-filter",
_("F_ilter"),
_("Filtering to use when generating mipmaps"),
pika_choice_new_with_values ("default", DDS_MIPMAP_FILTER_DEFAULT, _("Default"), NULL,
"nearest", DDS_MIPMAP_FILTER_NEAREST, _("Nearest"), NULL,
"box", DDS_MIPMAP_FILTER_BOX, _("Box"), NULL,
"triangle", DDS_MIPMAP_FILTER_TRIANGLE, _("Triangle"), NULL,
"quadratic", DDS_MIPMAP_FILTER_QUADRATIC, _("Quadratic"), NULL,
"bspline", DDS_MIPMAP_FILTER_BSPLINE, _("B-Spline"), NULL,
"mitchell", DDS_MIPMAP_FILTER_MITCHELL, _("Mitchell"), NULL,
"lanczos", DDS_MIPMAP_FILTER_LANCZOS, _("Lanczos"), NULL,
"kaiser", DDS_MIPMAP_FILTER_KAISER, _("Kaiser"), NULL,
pika_choice_new_with_values ("default", DDS_MIPMAP_FILTER_DEFAULT, _("Default"), NULL,
"nearest", DDS_MIPMAP_FILTER_NEAREST, _("Nearest"), NULL,
"box", DDS_MIPMAP_FILTER_BOX, _("Box"), NULL,
"triangle", DDS_MIPMAP_FILTER_TRIANGLE, _("Triangle"), NULL,
"quadratic", DDS_MIPMAP_FILTER_QUADRATIC, _("Quadratic"), NULL,
"bspline", DDS_MIPMAP_FILTER_BSPLINE, _("B-Spline"), NULL,
"mitchell", DDS_MIPMAP_FILTER_MITCHELL, _("Mitchell"), NULL,
"catrom", DDS_MIPMAP_FILTER_CATROM, _("Catmull-Rom"), NULL,
"lanczos", DDS_MIPMAP_FILTER_LANCZOS, _("Lanczos"), NULL,
"kaiser", DDS_MIPMAP_FILTER_KAISER, _("Kaiser"), NULL,
NULL),
"default",
G_PARAM_READWRITE);
@ -343,78 +332,6 @@ dds_create_procedure (PikaPlugIn *plug_in,
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;
}
@ -436,7 +353,7 @@ dds_load (PikaProcedure *procedure,
gegl_init (NULL, NULL);
status = read_dds (file, &image, run_mode == PIKA_RUN_INTERACTIVE,
procedure, G_OBJECT (config), &error);
procedure, config, &error);
if (status != PIKA_PDB_SUCCESS)
return pika_procedure_new_return_values (procedure, status, error);
@ -520,53 +437,3 @@ dds_save (PikaProcedure *procedure,
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,
PikaProcedureConfig *config,
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

View File

@ -21,6 +21,7 @@
#ifndef __DDS_H__
#define __DDS_H__
#define DDS_PLUGIN_VERSION_MAJOR 3
#define DDS_PLUGIN_VERSION_MINOR 9
#define DDS_PLUGIN_VERSION_REVISION 92
@ -101,6 +102,7 @@ typedef enum
DDS_MIPMAP_FILTER_QUADRATIC,
DDS_MIPMAP_FILTER_BSPLINE,
DDS_MIPMAP_FILTER_MITCHELL,
DDS_MIPMAP_FILTER_CATROM,
DDS_MIPMAP_FILTER_LANCZOS,
DDS_MIPMAP_FILTER_KAISER,
DDS_MIPMAP_FILTER_MAX
@ -115,6 +117,7 @@ typedef enum
DDS_MIPMAP_WRAP_MAX
} DDS_MIPMAP_WRAP;
#define DDS_HEADERSIZE 128
#define DDS_HEADERSIZE_DX10 20
@ -122,18 +125,43 @@ typedef enum
#define DDSD_HEIGHT 0x00000002
#define DDSD_WIDTH 0x00000004
#define DDSD_PITCH 0x00000008
#define DDSD_BACKBUFFERCOUNT 0x00000020
#define DDSD_ZBUFFERBITDEPTH 0x00000040
#define DDSD_ALPHABITDEPTH 0x00000080
#define DDSD_LPSURFACE 0x00000800
#define DDSD_PIXELFORMAT 0x00001000
#define DDSD_CKDESTOVERLAY 0x00002000
#define DDSD_CKDESTBLT 0x00004000
#define DDSD_CKSRCOVERLAY 0x00008000
#define DDSD_CKSRCBLT 0x00010000
#define DDSD_MIPMAPCOUNT 0x00020000
#define DDSD_REFRESHRATE 0x00040000
#define DDSD_LINEARSIZE 0x00080000
#define DDSD_TEXTURESTAGE 0x00100000
#define DDSD_FVF 0x00200000
#define DDSD_SRCVBHANDLE 0x00400000
#define DDSD_DEPTH 0x00800000
#define DDPF_ALPHAPIXELS 0x00000001
#define DDPF_ALPHA 0x00000002
#define DDPF_FOURCC 0x00000004
#define DDPF_PALETTEINDEXED4 0x00000008
#define DDPF_PALETTEINDEXEDTO8 0x00000010
#define DDPF_PALETTEINDEXED8 0x00000020
#define DDPF_RGB 0x00000040
#define DDPF_COMPRESSED 0x00000080
#define DDPF_RGBTOYUV 0x00000100
#define DDPF_YUV 0x00000200
#define DDPF_ZBUFFER 0x00000400
#define DDPF_PALETTEINDEXED1 0x00000800
#define DDPF_PALETTEINDEXED2 0x00001000
#define DDPF_ZPIXELS 0x00002000
#define DDPF_STENCILBUFFER 0x00004000
#define DDPF_ALPHAPREMULT 0x00008000
#define DDPF_LUMINANCE 0x00020000
#define DDPF_NORMAL 0x80000000 // nvidia specific
#define DDPF_BUMPLUMINANCE 0x00040000
#define DDPF_BUMPDUDV 0x00080000
#define DDPF_NORMAL 0x80000000 /* NVidia specific */
#define DDSCAPS_COMPLEX 0x00000008
#define DDSCAPS_TEXTURE 0x00001000
@ -159,6 +187,7 @@ typedef enum
#define D3D10_RESOURCE_DIMENSION_TEXTURE2D 3
#define D3D10_RESOURCE_DIMENSION_TEXTURE3D 4
typedef struct
{
unsigned int size;
@ -192,8 +221,8 @@ typedef struct
{
struct
{
unsigned int magic1; // FOURCC "PIKA"
unsigned int magic2; // FOURCC "-DDS"
unsigned int magic1; /* FOURCC "PIKA" */
unsigned int magic2; /* FOURCC "-DDS" */
unsigned int version;
unsigned int extra_fourcc;
} pika_dds_special;
@ -204,6 +233,7 @@ typedef struct
unsigned int reserved2;
} dds_header_t;
typedef enum
{
DXGI_FORMAT_UNKNOWN = 0,
@ -334,4 +364,70 @@ typedef struct
unsigned int reserved;
} dds_header_dx10_t;
/* Format values that can be found in the FOURCC field */
typedef enum _D3DFORMAT {
D3DFMT_R8G8B8 = 20,
D3DFMT_A8R8G8B8 = 21,
D3DFMT_X8R8G8B8 = 22,
D3DFMT_R5G6B5 = 23,
D3DFMT_X1R5G5B5 = 24,
D3DFMT_A1R5G5B5 = 25,
D3DFMT_A4R4G4B4 = 26,
D3DFMT_R3G3B2 = 27,
D3DFMT_A8 = 28,
D3DFMT_A8R3G3B2 = 29,
D3DFMT_X4R4G4B4 = 30,
D3DFMT_A2B10G10R10 = 31,
D3DFMT_A8B8G8R8 = 32,
D3DFMT_X8B8G8R8 = 33,
D3DFMT_G16R16 = 34,
D3DFMT_A2R10G10B10 = 35,
D3DFMT_A16B16G16R16 = 36,
D3DFMT_A8P8 = 40,
D3DFMT_P8 = 41,
D3DFMT_L8 = 50,
D3DFMT_A8L8 = 51,
D3DFMT_A4L4 = 52,
D3DFMT_V8U8 = 60,
D3DFMT_L6V5U5 = 61,
D3DFMT_X8L8V8U8 = 62,
D3DFMT_Q8W8V8U8 = 63,
D3DFMT_V16U16 = 64,
D3DFMT_A2W10V10U10 = 67,
D3DFMT_D16_LOCKABLE = 70,
D3DFMT_D32 = 71,
D3DFMT_D15S1 = 73,
D3DFMT_D24S8 = 75,
D3DFMT_D24X8 = 77,
D3DFMT_D24X4S4 = 79,
D3DFMT_D16 = 80,
D3DFMT_L16 = 81,
D3DFMT_D32F_LOCKABLE = 82,
D3DFMT_D24FS8 = 83,
D3DFMT_D32_LOCKABLE = 84,
D3DFMT_S8_LOCKABLE = 85,
D3DFMT_VERTEXDATA = 100,
D3DFMT_INDEX16 = 101,
D3DFMT_INDEX32 = 102,
D3DFMT_Q16W16V16U16 = 110,
D3DFMT_R16F = 111,
D3DFMT_G16R16F = 112,
D3DFMT_A16B16G16R16F = 113,
D3DFMT_R32F = 114,
D3DFMT_G32R32F = 115,
D3DFMT_A32B32G32R32F = 116,
D3DFMT_CxV8U8 = 117,
D3DFMT_A1 = 118,
D3DFMT_A2B10G10R10_XR_BIAS = 119,
D3DFMT_BINARYBUFFER = 199,
} D3DFORMAT;
#endif /* __DDS_H__ */

File diff suppressed because it is too large Load Diff

View File

@ -22,12 +22,12 @@
#define __DDSREAD_H__
extern PikaPDBStatusType read_dds (GFile *file,
PikaImage **image,
gboolean interactive,
PikaProcedure *procedure,
GObject *config,
GError **error);
extern PikaPDBStatusType read_dds (GFile *file,
PikaImage **image,
gboolean interactive,
PikaProcedure *procedure,
PikaProcedureConfig *config,
GError **error);
#endif /* __DDSREAD_H__ */

File diff suppressed because it is too large Load Diff

View File

@ -34,11 +34,14 @@
#include <math.h>
#include <glib.h>
#include <libpika/pika.h>
#include "dds.h"
#include "dxt.h"
#include "endian_rw.h"
#include "mipmap.h"
#include "imath.h"
#include "mipmap.h"
#include "misc.h"
#include "vec.h"
#include "dxt_tables.h"
@ -757,181 +760,6 @@ encode_color_block (unsigned char *dst,
PUTL32(dst + 4, indices);
}
static void
get_min_max_YCoCg (const unsigned char *block,
unsigned char *mincolor,
unsigned char *maxcolor)
{
int i;
mincolor[2] = mincolor[1] = 255;
maxcolor[2] = maxcolor[1] = 0;
for (i = 0; i < 16; ++i)
{
if (block[4 * i + 2] < mincolor[2]) mincolor[2] = block[4 * i + 2];
if (block[4 * i + 1] < mincolor[1]) mincolor[1] = block[4 * i + 1];
if (block[4 * i + 2] > maxcolor[2]) maxcolor[2] = block[4 * i + 2];
if (block[4 * i + 1] > maxcolor[1]) maxcolor[1] = block[4 * i + 1];
}
}
static void
scale_YCoCg (unsigned char *block,
unsigned char *mincolor,
unsigned char *maxcolor)
{
const int s0 = 128 / 2 - 1;
const int s1 = 128 / 4 - 1;
int m0, m1, m2, m3;
int mask0, mask1, scale;
int i;
m0 = abs(mincolor[2] - 128);
m1 = abs(mincolor[1] - 128);
m2 = abs(maxcolor[2] - 128);
m3 = abs(maxcolor[1] - 128);
if (m1 > m0) m0 = m1;
if (m3 > m2) m2 = m3;
if (m2 > m0) m0 = m2;
mask0 = -(m0 <= s0);
mask1 = -(m0 <= s1);
scale = 1 + (1 & mask0) + (2 & mask1);
mincolor[2] = (mincolor[2] - 128) * scale + 128;
mincolor[1] = (mincolor[1] - 128) * scale + 128;
mincolor[0] = (scale - 1) << 3;
maxcolor[2] = (maxcolor[2] - 128) * scale + 128;
maxcolor[1] = (maxcolor[1] - 128) * scale + 128;
maxcolor[0] = (scale - 1) << 3;
for (i = 0; i < 16; ++i)
{
block[i * 4 + 2] = (block[i * 4 + 2] - 128) * scale + 128;
block[i * 4 + 1] = (block[i * 4 + 1] - 128) * scale + 128;
}
}
#define INSET_SHIFT 4
static void
inset_bbox_YCoCg (unsigned char *mincolor,
unsigned char *maxcolor)
{
int inset[4], mini[4], maxi[4];
inset[2] = (maxcolor[2] - mincolor[2]) - ((1 << (INSET_SHIFT - 1)) - 1);
inset[1] = (maxcolor[1] - mincolor[1]) - ((1 << (INSET_SHIFT - 1)) - 1);
mini[2] = ((mincolor[2] << INSET_SHIFT) + inset[2]) >> INSET_SHIFT;
mini[1] = ((mincolor[1] << INSET_SHIFT) + inset[1]) >> INSET_SHIFT;
maxi[2] = ((maxcolor[2] << INSET_SHIFT) - inset[2]) >> INSET_SHIFT;
maxi[1] = ((maxcolor[1] << INSET_SHIFT) - inset[1]) >> INSET_SHIFT;
mini[2] = (mini[2] >= 0) ? mini[2] : 0;
mini[1] = (mini[1] >= 0) ? mini[1] : 0;
maxi[2] = (maxi[2] <= 255) ? maxi[2] : 255;
maxi[1] = (maxi[1] <= 255) ? maxi[1] : 255;
mincolor[2] = (mini[2] & 0xf8) | (mini[2] >> 5);
mincolor[1] = (mini[1] & 0xfc) | (mini[1] >> 6);
maxcolor[2] = (maxi[2] & 0xf8) | (maxi[2] >> 5);
maxcolor[1] = (maxi[1] & 0xfc) | (maxi[1] >> 6);
}
static void
select_diagonal_YCoCg (const unsigned char *block,
unsigned char *mincolor,
unsigned char *maxcolor)
{
unsigned char mid0, mid1, side, mask, b0, b1, c0, c1;
int i;
mid0 = ((int)mincolor[2] + maxcolor[2] + 1) >> 1;
mid1 = ((int)mincolor[1] + maxcolor[1] + 1) >> 1;
side = 0;
for (i = 0; i < 16; ++i)
{
b0 = block[i * 4 + 2] >= mid0;
b1 = block[i * 4 + 1] >= mid1;
side += (b0 ^ b1);
}
mask = -(side > 8);
mask &= -(mincolor[2] != maxcolor[2]);
c0 = mincolor[1];
c1 = maxcolor[1];
c0 ^= c1;
c1 ^= c0 & mask;
c0 ^= c1;
mincolor[1] = c0;
maxcolor[1] = c1;
}
static void
encode_YCoCg_block (unsigned char *dst,
unsigned char *block)
{
unsigned char colors[4][3], *maxcolor, *mincolor;
unsigned int mask;
int c0, c1, d0, d1, d2, d3;
int b0, b1, b2, b3, b4;
int x0, x1, x2;
int i, idx;
maxcolor = &colors[0][0];
mincolor = &colors[1][0];
get_min_max_YCoCg(block, mincolor, maxcolor);
scale_YCoCg(block, mincolor, maxcolor);
inset_bbox_YCoCg(mincolor, maxcolor);
select_diagonal_YCoCg(block, mincolor, maxcolor);
lerp_rgb13(&colors[2][0], maxcolor, mincolor);
lerp_rgb13(&colors[3][0], mincolor, maxcolor);
mask = 0;
for (i = 0; i < 16; ++i)
{
c0 = block[4 * i + 2];
c1 = block[4 * i + 1];
d0 = abs(colors[0][2] - c0) + abs(colors[0][1] - c1);
d1 = abs(colors[1][2] - c0) + abs(colors[1][1] - c1);
d2 = abs(colors[2][2] - c0) + abs(colors[2][1] - c1);
d3 = abs(colors[3][2] - c0) + abs(colors[3][1] - c1);
b0 = d0 > d3;
b1 = d1 > d2;
b2 = d0 > d2;
b3 = d1 > d3;
b4 = d2 > d3;
x0 = b1 & b2;
x1 = b0 & b3;
x2 = b0 & b4;
idx = (x2 | ((x0 | x1) << 1));
mask |= idx << (2 * i);
}
PUTL16(dst + 0, pack_rgb565(maxcolor));
PUTL16(dst + 2, pack_rgb565(mincolor));
PUTL32(dst + 4, mask);
}
/* write DXT3 alpha block */
static void
encode_alpha_block_BC2 (unsigned char *dst,
@ -1288,33 +1116,35 @@ dxt_compress (unsigned char *dst,
}
static void
decode_color_block (unsigned char *block,
unsigned char *src,
int format)
decode_color_block (guchar *block,
guchar *src,
gint format)
{
int i, x, y;
unsigned char *d = block;
unsigned int indices, idx;
unsigned char colors[4][3];
unsigned short c0, c1;
guchar *d = block;
guint indices, idx;
guchar colors[4][3];
gushort c0, c1;
gint i, x, y;
c0 = GETL16(&src[0]);
c1 = GETL16(&src[2]);
c0 = GETL16 (&src[0]);
c1 = GETL16 (&src[2]);
unpack_rgb565(colors[0], c0);
unpack_rgb565(colors[1], c1);
unpack_rgb565 (colors[0], c0);
unpack_rgb565 (colors[1], c1);
if ((c0 > c1) || (format == DDS_COMPRESS_BC3))
{
lerp_rgb13(colors[2], colors[0], colors[1]);
lerp_rgb13(colors[3], colors[1], colors[0]);
/* Four-color mode */
lerp_rgb13 (colors[2], colors[0], colors[1]);
lerp_rgb13 (colors[3], colors[1], colors[0]);
}
else
{
/* Three-color mode */
for (i = 0; i < 3; ++i)
{
colors[2][i] = (colors[0][i] + colors[1][i] + 1) >> 1;
colors[3][i] = 255;
colors[3][i] = 0; /* Three-color mode index 11 is always black */
}
}

View File

@ -2,7 +2,6 @@ plugin_name = 'file-dds'
plugin_sources = [
'dds.c',
'color.c',
'ddsread.c',
'ddswrite.c',
'dxt.c',

File diff suppressed because it is too large Load Diff

View File

@ -21,55 +21,56 @@
#ifndef __MIPMAP_H__
#define __MIPMAP_H__
int get_num_mipmaps (int width,
int height);
unsigned int get_mipmapped_size (int width,
int height,
int bpp,
int level,
int num,
int format);
unsigned int get_volume_mipmapped_size (int width,
int height,
int depth,
int bpp,
int level,
int num,
int format);
int get_next_mipmap_dimensions (int *next_w,
int *next_h,
int curr_w,
int curr_h);
float cubic_interpolate (float a,
float b,
float c,
float d,
float x);
int generate_mipmaps (unsigned char *dst,
unsigned char *src,
unsigned int width,
unsigned int height,
int bpp,
int indexed,
int mipmaps,
int filter,
int wrap,
int gamma_correct,
float gamma,
int preserve_alpha_test_coverage,
float alpha_test_threshold);
int generate_volume_mipmaps (unsigned char *dst,
unsigned char *src,
unsigned int width,
unsigned int height,
unsigned int depth,
int bpp,
int indexed,
int mipmaps,
int filter,
int wrap,
int gamma_correct,
float gamma);
gint get_num_mipmaps (gint width,
gint height);
guint get_mipmapped_size (gint width,
gint height,
gint bpp,
gint level,
gint num,
gint format);
guint get_volume_mipmapped_size (gint width,
gint height,
gint depth,
gint bpp,
gint level,
gint num,
gint format);
gint get_next_mipmap_dimensions (gint *next_w,
gint *next_h,
gint curr_w,
gint curr_h);
gint generate_mipmaps (guchar *dst,
guchar *src,
guint width,
guint height,
gint bpp,
gint indexed,
gint mipmaps,
gint filter,
gint wrap,
gint gamma_correct,
gfloat gamma,
gint preserve_alpha_test_coverage,
gfloat alpha_test_threshold);
gint generate_volume_mipmaps (guchar *dst,
guchar *src,
guint width,
guint height,
guint depth,
gint bpp,
gint indexed,
gint mipmaps,
gint filter,
gint wrap,
gint gamma_correct,
gfloat gamma);
#endif /* __MIPMAP_H__ */

View File

@ -4,56 +4,53 @@
* 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 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.
* 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.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <libpika/pika.h>
#include "endian_rw.h"
#include "imath.h"
#include "misc.h"
static inline float
saturate (float a)
{
if(a < 0) a = 0;
if(a > 1) a = 1;
/*
* Decoding Functions
*/
static inline gfloat
saturate (gfloat a)
{
if (a < 0) a = 0;
if (a > 1) a = 1;
return a;
}
void
decode_ycocg_image (PikaDrawable *drawable,
gboolean shadow)
decode_ycocg (PikaDrawable *drawable)
{
GeglBuffer *buffer, *sbuffer;
const Babl *format;
unsigned char *data;
unsigned int i, w, h, num_pixels;
const float offset = 0.5f * 256.0f / 255.0f;
float Y, Co, Cg, R, G, B;
GeglBuffer *buffer;
const Babl *format;
guchar *data;
guint num_pixels;
guint i, w, h;
const gfloat offset = 0.5f * 256.0f / 255.0f;
gfloat Y, Co, Cg;
gfloat R, G, B;
buffer = pika_drawable_get_buffer (drawable);
if (shadow)
{
sbuffer = pika_drawable_get_shadow_buffer (drawable);
gegl_buffer_copy (buffer, NULL, GEGL_ABYSS_NONE, sbuffer, NULL);
g_object_unref (buffer);
buffer = sbuffer;
}
format = babl_format ("R'G'B'A u8");
w = gegl_buffer_get_width (buffer);
@ -62,46 +59,43 @@ decode_ycocg_image (PikaDrawable *drawable,
data = g_malloc (num_pixels * 4);
gegl_buffer_get (buffer, GEGL_RECTANGLE(0, 0, w, h), 1.0, format, data,
gegl_buffer_get (buffer, GEGL_RECTANGLE (0, 0, w, h), 1.0, format, data,
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
pika_progress_init ("Decoding YCoCg pixels...");
for (i = 0; i < num_pixels; ++i)
{
Y = (float)data[4 * i + 3] / 255.0f;
Co = (float)data[4 * i + 0] / 255.0f;
Cg = (float)data[4 * i + 1] / 255.0f;
Y = (gfloat) data[4 * i + 3] / 255.0f;
Co = (gfloat) data[4 * i + 0] / 255.0f;
Cg = (gfloat) data[4 * i + 1] / 255.0f;
/* convert YCoCg to RGB */
Co -= offset;
Cg -= offset;
R = saturate(Y + Co - Cg);
G = saturate(Y + Cg);
B = saturate(Y - Co - Cg);
R = saturate (Y + Co - Cg);
G = saturate (Y + Cg);
B = saturate (Y - Co - Cg);
/* copy new alpha from blue */
data[4 * i + 3] = data[4 * i + 2];
data[4 * i + 0] = (unsigned char)(R * 255.0f);
data[4 * i + 1] = (unsigned char)(G * 255.0f);
data[4 * i + 2] = (unsigned char)(B * 255.0f);
data[4 * i + 0] = (guchar) (R * 255.0f);
data[4 * i + 1] = (guchar) (G * 255.0f);
data[4 * i + 2] = (guchar) (B * 255.0f);
if ((i & 0x7fff) == 0)
pika_progress_update ((float)i / (float)num_pixels);
pika_progress_update ((gdouble) i / (gdouble) num_pixels);
}
gegl_buffer_set (buffer, GEGL_RECTANGLE(0, 0, w, h), 0, format, data,
gegl_buffer_set (buffer, GEGL_RECTANGLE (0, 0, w, h), 0, format, data,
GEGL_AUTO_ROWSTRIDE);
pika_progress_update (1.0);
gegl_buffer_flush (buffer);
if (shadow)
pika_drawable_merge_shadow (drawable, TRUE);
pika_drawable_update (drawable, 0, 0, w, h);
g_free (data);
@ -110,27 +104,19 @@ decode_ycocg_image (PikaDrawable *drawable,
}
void
decode_ycocg_scaled_image (PikaDrawable *drawable,
gboolean shadow)
decode_ycocg_scaled (PikaDrawable *drawable)
{
GeglBuffer *buffer, *sbuffer;
const Babl *format;
unsigned char *data;
unsigned int i, w, h, num_pixels;
const float offset = 0.5f * 256.0f / 255.0f;
float Y, Co, Cg, R, G, B, s;
GeglBuffer *buffer;
const Babl *format;
guchar *data;
guint num_pixels;
guint i, w, h;
const gfloat offset = 0.5f * 256.0f / 255.0f;
gfloat Y, Co, Cg;
gfloat R, G, B, s;
buffer = pika_drawable_get_buffer (drawable);
if (shadow)
{
sbuffer = pika_drawable_get_shadow_buffer (drawable);
gegl_buffer_copy (buffer, NULL, GEGL_ABYSS_NONE, sbuffer, NULL);
g_object_unref (buffer);
buffer = sbuffer;
}
format = babl_format ("R'G'B'A u8");
w = gegl_buffer_get_width (buffer);
@ -139,17 +125,17 @@ decode_ycocg_scaled_image (PikaDrawable *drawable,
data = g_malloc (num_pixels * 4);
gegl_buffer_get (buffer, GEGL_RECTANGLE(0, 0, w, h), 1.0, format, data,
gegl_buffer_get (buffer, GEGL_RECTANGLE (0, 0, w, h), 1.0, format, data,
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
pika_progress_init ("Decoding YCoCg (scaled) pixels...");
for (i = 0; i < num_pixels; ++i)
{
Y = (float)data[4 * i + 3] / 255.0f;
Co = (float)data[4 * i + 0] / 255.0f;
Cg = (float)data[4 * i + 1] / 255.0f;
s = (float)data[4 * i + 2] / 255.0f;
Y = (gfloat) data[4 * i + 3] / 255.0f;
Co = (gfloat) data[4 * i + 0] / 255.0f;
Cg = (gfloat) data[4 * i + 1] / 255.0f;
s = (gfloat) data[4 * i + 2] / 255.0f;
/* convert YCoCg to RGB */
s = 1.0f / ((255.0f / 8.0f) * s + 1.0f);
@ -157,31 +143,28 @@ decode_ycocg_scaled_image (PikaDrawable *drawable,
Co = (Co - offset) * s;
Cg = (Cg - offset) * s;
R = saturate(Y + Co - Cg);
G = saturate(Y + Cg);
B = saturate(Y - Co - Cg);
R = saturate (Y + Co - Cg);
G = saturate (Y + Cg);
B = saturate (Y - Co - Cg);
data[4 * i + 0] = (unsigned char)(R * 255.0f);
data[4 * i + 1] = (unsigned char)(G * 255.0f);
data[4 * i + 2] = (unsigned char)(B * 255.0f);
data[4 * i + 0] = (guchar) (R * 255.0f);
data[4 * i + 1] = (guchar) (G * 255.0f);
data[4 * i + 2] = (guchar) (B * 255.0f);
/* set alpha to 1 */
data[4 * i + 3] = 255;
if ((i & 0x7fff) == 0)
pika_progress_update ((float)i / (float)num_pixels);
pika_progress_update ((gdouble) i / (gdouble) num_pixels);
}
gegl_buffer_set (buffer, GEGL_RECTANGLE(0, 0, w, h), 0, format, data,
gegl_buffer_set (buffer, GEGL_RECTANGLE (0, 0, w, h), 0, format, data,
GEGL_AUTO_ROWSTRIDE);
pika_progress_update (1.0);
gegl_buffer_flush (buffer);
if (shadow)
pika_drawable_merge_shadow (drawable, TRUE);
pika_drawable_update (drawable, 0, 0, w, h);
g_free (data);
@ -190,25 +173,17 @@ decode_ycocg_scaled_image (PikaDrawable *drawable,
}
void
decode_alpha_exp_image (PikaDrawable *drawable,
gboolean shadow)
decode_alpha_exponent (PikaDrawable *drawable)
{
GeglBuffer *buffer, *sbuffer;
GeglBuffer *buffer;
const Babl *format;
unsigned char *data;
unsigned int i, w, h, num_pixels;
int R, G, B, A;
guchar *data;
guint num_pixels;
guint i, w, h;
gint R, G, B, A;
buffer = pika_drawable_get_buffer (drawable);
if (shadow)
{
sbuffer = pika_drawable_get_shadow_buffer (drawable);
gegl_buffer_copy (buffer, NULL, GEGL_ABYSS_NONE, sbuffer, NULL);
g_object_unref (buffer);
buffer = sbuffer;
}
format = babl_format ("R'G'B'A u8");
w = gegl_buffer_get_width (buffer);
@ -217,7 +192,7 @@ decode_alpha_exp_image (PikaDrawable *drawable,
data = g_malloc (num_pixels * 4);
gegl_buffer_get (buffer, GEGL_RECTANGLE(0, 0, w, h), 1.0, format, data,
gegl_buffer_get (buffer, GEGL_RECTANGLE (0, 0, w, h), 1.0, format, data,
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
pika_progress_init ("Decoding Alpha-exponent pixels...");
@ -240,22 +215,268 @@ decode_alpha_exp_image (PikaDrawable *drawable,
data[4 * i + 3] = A;
if ((i & 0x7fff) == 0)
pika_progress_update ((float)i / (float)num_pixels);
pika_progress_update ((gdouble) i / (gdouble) num_pixels);
}
gegl_buffer_set (buffer, GEGL_RECTANGLE(0, 0, w, h), 0, format, data,
gegl_buffer_set (buffer, GEGL_RECTANGLE (0, 0, w, h), 0, format, data,
GEGL_AUTO_ROWSTRIDE);
pika_progress_update (1.0);
gegl_buffer_flush (buffer);
if (shadow)
pika_drawable_merge_shadow (drawable, TRUE);
pika_drawable_update (drawable, 0, 0, w, h);
g_free (data);
g_object_unref (buffer);
}
/*
* Encoding Functions
*/
void
encode_ycocg (guchar *dst,
gint r,
gint g,
gint b)
{
gint y = ((r + (g << 1) + b) + 2) >> 2;
gint co = ((((r << 1) - (b << 1)) + 2) >> 2) + 128;
gint 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));
}
void
encode_alpha_exponent (guchar *dst,
gint r,
gint g,
gint b,
gint a)
{
gfloat ar, ag, ab, aa;
ar = (gfloat) r / 255.0f;
ag = (gfloat) g / 255.0f;
ab = (gfloat) b / 255.0f;
aa = MAX (ar, MAX (ag, ab));
if (aa < 1e-04f)
{
dst[0] = b;
dst[1] = g;
dst[2] = r;
dst[3] = 255;
return;
}
ar /= aa;
ag /= aa;
ab /= aa;
r = (gint) floorf (255.0f * ar + 0.5f);
g = (gint) floorf (255.0f * ag + 0.5f);
b = (gint) floorf (255.0f * ab + 0.5f);
a = (gint) floorf (255.0f * aa + 0.5f);
dst[0] = MAX (0, MIN (255, b));
dst[1] = MAX (0, MIN (255, g));
dst[2] = MAX (0, MIN (255, r));
dst[3] = MAX (0, MIN (255, a));
}
/*
* Compression Functions
*/
static void
get_min_max_YCoCg (const guchar *block,
guchar *mincolor,
guchar *maxcolor)
{
gint i;
mincolor[2] = mincolor[1] = 255;
maxcolor[2] = maxcolor[1] = 0;
for (i = 0; i < 16; ++i)
{
if (block[4 * i + 2] < mincolor[2]) mincolor[2] = block[4 * i + 2];
if (block[4 * i + 1] < mincolor[1]) mincolor[1] = block[4 * i + 1];
if (block[4 * i + 2] > maxcolor[2]) maxcolor[2] = block[4 * i + 2];
if (block[4 * i + 1] > maxcolor[1]) maxcolor[1] = block[4 * i + 1];
}
}
static void
scale_YCoCg (guchar *block,
guchar *mincolor,
guchar *maxcolor)
{
const gint s0 = 128 / 2 - 1;
const gint s1 = 128 / 4 - 1;
gint m0, m1, m2, m3;
gint mask0, mask1, scale;
gint i;
m0 = abs (mincolor[2] - 128);
m1 = abs (mincolor[1] - 128);
m2 = abs (maxcolor[2] - 128);
m3 = abs (maxcolor[1] - 128);
if (m1 > m0) m0 = m1;
if (m3 > m2) m2 = m3;
if (m2 > m0) m0 = m2;
mask0 = -(m0 <= s0);
mask1 = -(m0 <= s1);
scale = 1 + (1 & mask0) + (2 & mask1);
mincolor[2] = (mincolor[2] - 128) * scale + 128;
mincolor[1] = (mincolor[1] - 128) * scale + 128;
mincolor[0] = (scale - 1) << 3;
maxcolor[2] = (maxcolor[2] - 128) * scale + 128;
maxcolor[1] = (maxcolor[1] - 128) * scale + 128;
maxcolor[0] = (scale - 1) << 3;
for (i = 0; i < 16; ++i)
{
block[i * 4 + 2] = (block[i * 4 + 2] - 128) * scale + 128;
block[i * 4 + 1] = (block[i * 4 + 1] - 128) * scale + 128;
}
}
#define INSET_SHIFT 4
static void
inset_bbox_YCoCg (guchar *mincolor,
guchar *maxcolor)
{
gint inset[4], mini[4], maxi[4];
inset[2] = (maxcolor[2] - mincolor[2]) - ((1 << (INSET_SHIFT - 1)) - 1);
inset[1] = (maxcolor[1] - mincolor[1]) - ((1 << (INSET_SHIFT - 1)) - 1);
mini[2] = ((mincolor[2] << INSET_SHIFT) + inset[2]) >> INSET_SHIFT;
mini[1] = ((mincolor[1] << INSET_SHIFT) + inset[1]) >> INSET_SHIFT;
maxi[2] = ((maxcolor[2] << INSET_SHIFT) - inset[2]) >> INSET_SHIFT;
maxi[1] = ((maxcolor[1] << INSET_SHIFT) - inset[1]) >> INSET_SHIFT;
mini[2] = (mini[2] >= 0) ? mini[2] : 0;
mini[1] = (mini[1] >= 0) ? mini[1] : 0;
maxi[2] = (maxi[2] <= 255) ? maxi[2] : 255;
maxi[1] = (maxi[1] <= 255) ? maxi[1] : 255;
mincolor[2] = (mini[2] & 0xf8) | (mini[2] >> 5);
mincolor[1] = (mini[1] & 0xfc) | (mini[1] >> 6);
maxcolor[2] = (maxi[2] & 0xf8) | (maxi[2] >> 5);
maxcolor[1] = (maxi[1] & 0xfc) | (maxi[1] >> 6);
}
static void
select_diagonal_YCoCg (const guchar *block,
guchar *mincolor,
guchar *maxcolor)
{
guchar mid0, mid1, side, mask, b0, b1, c0, c1;
gint i;
mid0 = ((gint) mincolor[2] + maxcolor[2] + 1) >> 1;
mid1 = ((gint) mincolor[1] + maxcolor[1] + 1) >> 1;
side = 0;
for (i = 0; i < 16; ++i)
{
b0 = block[i * 4 + 2] >= mid0;
b1 = block[i * 4 + 1] >= mid1;
side += (b0 ^ b1);
}
mask = -(side > 8);
mask &= -(mincolor[2] != maxcolor[2]);
c0 = mincolor[1];
c1 = maxcolor[1];
c0 ^= c1;
c1 ^= c0 & mask;
c0 ^= c1;
mincolor[1] = c0;
maxcolor[1] = c1;
}
void
encode_YCoCg_block (guchar *dst,
guchar *block)
{
guchar colors[4][3], *maxcolor, *mincolor;
guint mask;
gint c0, c1, d0, d1, d2, d3;
gint b0, b1, b2, b3, b4;
gint x0, x1, x2;
gint i, idx;
maxcolor = &colors[0][0];
mincolor = &colors[1][0];
get_min_max_YCoCg (block, mincolor, maxcolor);
scale_YCoCg (block, mincolor, maxcolor);
inset_bbox_YCoCg (mincolor, maxcolor);
select_diagonal_YCoCg (block, mincolor, maxcolor);
colors[2][0] = (2 * maxcolor[0] + mincolor[0]) / 3;
colors[2][1] = (2 * maxcolor[1] + mincolor[1]) / 3;
colors[2][2] = (2 * maxcolor[2] + mincolor[2]) / 3;
colors[3][0] = (2 * mincolor[0] + maxcolor[0]) / 3;
colors[3][1] = (2 * mincolor[1] + maxcolor[1]) / 3;
colors[3][2] = (2 * mincolor[2] + maxcolor[2]) / 3;
mask = 0;
for (i = 0; i < 16; ++i)
{
c0 = block[4 * i + 2];
c1 = block[4 * i + 1];
d0 = abs (colors[0][2] - c0) + abs (colors[0][1] - c1);
d1 = abs (colors[1][2] - c0) + abs (colors[1][1] - c1);
d2 = abs (colors[2][2] - c0) + abs (colors[2][1] - c1);
d3 = abs (colors[3][2] - c0) + abs (colors[3][1] - c1);
b0 = d0 > d3;
b1 = d1 > d2;
b2 = d0 > d2;
b3 = d1 > d3;
b4 = d2 > d3;
x0 = b1 & b2;
x1 = b0 & b3;
x2 = b0 & b4;
idx = (x2 | ((x0 | x1) << 1));
mask |= idx << (2 * i);
}
PUTL16 (dst + 0, (mul8bit (maxcolor[2], 31) << 11) |
(mul8bit (maxcolor[1], 63) << 5) |
(mul8bit (maxcolor[0], 31) ));
PUTL16 (dst + 2, (mul8bit (mincolor[2], 31) << 11) |
(mul8bit (mincolor[1], 63) << 5) |
(mul8bit (mincolor[0], 31) ));
PUTL32 (dst + 4, mask);
}

View File

@ -21,11 +21,26 @@
#ifndef __MISC_H__
#define __MISC_H__
void decode_ycocg_image (PikaDrawable *drawable,
gboolean shadow);
void decode_ycocg_scaled_image (PikaDrawable *drawable,
gboolean shadow);
void decode_alpha_exp_image (PikaDrawable *drawable,
gboolean shadow);
void decode_ycocg (PikaDrawable *drawable);
void decode_ycocg_scaled (PikaDrawable *drawable);
void decode_alpha_exponent (PikaDrawable *drawable);
void encode_ycocg (guchar *dst,
gint r,
gint g,
gint b);
void encode_alpha_exponent (guchar *dst,
gint r,
gint g,
gint b,
gint a);
void encode_YCoCg_block (guchar *dst,
guchar *block);
#endif /* __MISC_H__ */

View File

@ -40,6 +40,23 @@
static gboolean callback_lock;
static void select_web_cb (GtkWidget *widget,
AreaInfoDialog_t *param);
static void select_ftp_cb (GtkWidget *widget,
AreaInfoDialog_t *param);
static void select_gopher_cb (GtkWidget *widget,
AreaInfoDialog_t *param);
static void select_other_cb (GtkWidget *widget,
AreaInfoDialog_t *param);
static void select_file_cb (GtkWidget *widget,
AreaInfoDialog_t *param);
static void select_wais_cb (GtkWidget *widget,
AreaInfoDialog_t *param);
static void select_telnet_cb (GtkWidget *widget,
AreaInfoDialog_t *param);
static void select_email_cb (GtkWidget *widget,
AreaInfoDialog_t *param);
static gchar*
relative_filter(const char *name, gpointer data)
@ -77,7 +94,23 @@ url_changed (GtkWidget *widget,
button = param->other;
callback_lock = TRUE;
g_signal_handlers_block_by_func (param->web_site, G_CALLBACK (select_web_cb), data);
g_signal_handlers_block_by_func (param->ftp_site, G_CALLBACK (select_ftp_cb), data);
g_signal_handlers_block_by_func (param->gopher, G_CALLBACK (select_gopher_cb), data);
g_signal_handlers_block_by_func (param->other, G_CALLBACK (select_other_cb), data);
g_signal_handlers_block_by_func (param->file, G_CALLBACK (select_file_cb), data);
g_signal_handlers_block_by_func (param->wais, G_CALLBACK (select_wais_cb), data);
g_signal_handlers_block_by_func (param->telnet, G_CALLBACK (select_telnet_cb), data);
g_signal_handlers_block_by_func (param->email, G_CALLBACK (select_email_cb), data);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
g_signal_handlers_unblock_by_func (param->web_site, G_CALLBACK (select_web_cb), data);
g_signal_handlers_unblock_by_func (param->ftp_site, G_CALLBACK (select_ftp_cb), data);
g_signal_handlers_unblock_by_func (param->gopher, G_CALLBACK (select_gopher_cb), data);
g_signal_handlers_unblock_by_func (param->other, G_CALLBACK (select_other_cb), data);
g_signal_handlers_unblock_by_func (param->file, G_CALLBACK (select_file_cb), data);
g_signal_handlers_unblock_by_func (param->wais, G_CALLBACK (select_wais_cb), data);
g_signal_handlers_unblock_by_func (param->telnet, G_CALLBACK (select_telnet_cb), data);
g_signal_handlers_unblock_by_func (param->email, G_CALLBACK (select_email_cb), data);
}
static void

View File

@ -76,6 +76,10 @@ static PikaProcedure * metadata_create_procedure (PikaPlugIn *plug_in
const gchar *name);
static PikaValueArray * metadata_run (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
PikaProcedureConfig *config,
gpointer run_data);
@ -747,9 +751,9 @@ metadata_create_procedure (PikaPlugIn *plug_in,
if (! strcmp (name, PLUG_IN_PROC))
{
procedure = pika_procedure_new (plug_in, name,
PIKA_PDB_PROC_TYPE_PLUGIN,
metadata_run, NULL, NULL);
procedure = pika_image_procedure_new (plug_in, name,
PIKA_PDB_PROC_TYPE_PLUGIN,
metadata_run, NULL, NULL);
pika_procedure_set_image_types (procedure, "*");
@ -768,19 +772,6 @@ metadata_create_procedure (PikaPlugIn *plug_in,
"Ben Touchette",
"Ben Touchette",
"2017");
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_IMAGE (procedure, "image",
"Image",
"The input image",
FALSE,
G_PARAM_READWRITE);
}
return procedure;
@ -788,17 +779,18 @@ metadata_create_procedure (PikaPlugIn *plug_in,
static PikaValueArray *
metadata_run (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
PikaProcedureConfig *config,
gpointer run_data)
{
PikaImage *image;
PikaMetadata *metadata;
GError *error = NULL;
pika_ui_init (PLUG_IN_BINARY);
g_object_get (config, "image", &image, NULL);
metadata = pika_image_get_metadata (image);
/* Always show metadata dialog so we can add appropriate iptc data

View File

@ -97,6 +97,10 @@ static PikaProcedure * metadata_create_procedure (PikaPlugIn *plug_in
const gchar *name);
static PikaValueArray * metadata_run (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
PikaProcedureConfig *config,
gpointer run_data);
@ -172,9 +176,9 @@ metadata_create_procedure (PikaPlugIn *plug_in,
if (! strcmp (name, PLUG_IN_PROC))
{
procedure = pika_procedure_new (plug_in, name,
PIKA_PDB_PROC_TYPE_PLUGIN,
metadata_run, NULL, NULL);
procedure = pika_image_procedure_new (plug_in, name,
PIKA_PDB_PROC_TYPE_PLUGIN,
metadata_run, NULL, NULL);
pika_procedure_set_image_types (procedure, "*");
@ -194,19 +198,6 @@ metadata_create_procedure (PikaPlugIn *plug_in,
"Hartmut Kuhse, Michael Natterer, "
"Ben Touchette",
"2013, 2017");
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_IMAGE (procedure, "image",
"Image",
"The input image",
FALSE,
G_PARAM_READWRITE);
}
return procedure;
@ -214,17 +205,18 @@ metadata_create_procedure (PikaPlugIn *plug_in,
static PikaValueArray *
metadata_run (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
PikaProcedureConfig *config,
gpointer run_data)
{
PikaImage *image;
PikaMetadata *metadata;
GError *error = NULL;
pika_ui_init (PLUG_IN_BINARY);
g_object_get (config, "image", &image, NULL);
metadata = pika_image_get_metadata (image);
/* Always show metadata dialog so we can add appropriate iptc data

View File

@ -77,6 +77,7 @@ marshal_vector_to_drawable_array (scheme *sc,
PikaDrawable **drawable_array;
gint id;
pointer error;
GType actual_type = PIKA_TYPE_DRAWABLE;
guint num_elements = sc->vptr->vector_length (vector);
g_debug ("vector has %d elements", num_elements);
@ -104,10 +105,15 @@ marshal_vector_to_drawable_array (scheme *sc,
g_free (drawable_array);
return error;
}
/* Parameters are validated based on the actual type inside the object
array. So we set that type here instead of a generic drawable. */
if (j == 0)
actual_type = G_OBJECT_TYPE (drawable_array[j]);
}
/* Shallow copy. */
pika_value_set_object_array (value, PIKA_TYPE_DRAWABLE, (GObject**)drawable_array, num_elements);
pika_value_set_object_array (value, actual_type, (GObject**)drawable_array, num_elements);
g_free (drawable_array);

View File

@ -615,7 +615,7 @@ script_fu_resource_widget (const gchar *title,
{
GtkWidget *result_widget = NULL;
g_debug ("%s type: %ld", G_STRFUNC, resource_type);
g_debug ("%s type: %" G_GSIZE_FORMAT, G_STRFUNC, resource_type);
/* Passing NULL resource sets initial choice to resource from context.
* Passing empty string for outer widget label, since we provide our own.
@ -824,6 +824,10 @@ script_fu_update_models (SFScript *script)
FALSE);
}
break;
default:
/* Silence warnings about unhandled enumeration values */
break;
}
}
}

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