Update upstream
This commit is contained in:
@ -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[] =
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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."
|
||||
|
||||
|
@ -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 |
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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__ */
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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__ */
|
||||
|
@ -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 *
|
||||
|
@ -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"),
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 (©_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, ©_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)
|
||||
|
@ -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);
|
||||
|
@ -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__ */
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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__ */
|
||||
|
@ -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 *
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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__ */
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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>",
|
||||
|
@ -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,
|
||||
|
@ -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."),
|
||||
|
@ -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;
|
||||
|
@ -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"),
|
||||
|
@ -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"),
|
||||
|
@ -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"),
|
||||
|
@ -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"),
|
||||
|
@ -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");
|
||||
|
@ -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"),
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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"),
|
||||
|
@ -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"),
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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"),
|
||||
|
@ -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"),
|
||||
|
@ -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 *
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user