Compare commits
8 Commits
852cbfc1fb
...
trunk
Author | SHA1 | Date | |
---|---|---|---|
2b5c950f1b | |||
1d7cab96bc | |||
4b829a4b3c | |||
af7dc8395d | |||
8ac3f8a548 | |||
63b2780499 | |||
d472f6348d | |||
84ea557696 |
8
README
8
README
@ -39,13 +39,7 @@ similar.
|
||||
|
||||
Partial package list on Debian/Ubuntu/etc:
|
||||
|
||||
appstream-util bison build-essential cmake desktop-file-utils flex gjs libaa1-dev libappstream-glib-dev libarchive-dev
|
||||
libatk1.0-dev libcairo2-dev libcfitsio-dev libexiv2-dev libfontconfig-dev libfreetype-dev libgdk-pixbuf-2.0-dev
|
||||
libgexiv2-dev libglib2.0-dev libgs-dev libgtk-3-dev libgudev-1.0-dev libharfbuzz-dev libheif-dev libjpeg-dev
|
||||
libjson-glib-dev liblcms2-dev libmng-dev libmypaint-dev libopenexr-dev libopenjp2-7-dev libpango1.0-dev libpng-dev
|
||||
libpoppler-dev libpoppler-glib-dev librsvg2-dev libtiff-dev libunwind-dev libwebp-dev libwmf-dev libx11-dev
|
||||
libxcursor-dev libxext-dev libxfixes-dev libxmu-dev libxpm-dev libzstd-dev luajit meson mypaint-brushes ninja-build
|
||||
pappler-data python3-gi-dev xmllint xsltproc
|
||||
appstream-util bison build-essential cmake desktop-file-utils flex gjs libaa1-dev libappstream-glib-dev libarchive-dev libatk1.0-dev libcairo2-dev libcfitsio-dev libexiv2-dev libfontconfig-dev libfreetype-dev libgdk-pixbuf-2.0-dev libgexiv2-dev libglib2.0-dev libgs-dev libgtk-3-dev libgudev-1.0-dev libharfbuzz-dev libheif-dev libjpeg-dev libjson-glib-dev liblcms2-dev libmng-dev libmypaint-dev libopenexr-dev libopenjp2-7-dev libpango1.0-dev libpng-dev libpoppler-dev libpoppler-glib-dev librsvg2-dev libtiff-dev libunwind-dev libwebp-dev libwmf-dev libx11-dev libxcursor-dev libxext-dev libxfixes-dev libxmu-dev libxpm-dev libzstd-dev luajit meson mypaint-brushes ninja-build poppler-data python3-gi libxml2-utils xsltproc gobject-introspection libgirepository1.0-dev libbz2-dev gettext
|
||||
|
||||
Seems like a lot! But many of these are for format support. This is for the maximal setup we've tested.
|
||||
|
||||
|
@ -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;
|
||||
|
@ -44,7 +44,7 @@
|
||||
#endif
|
||||
|
||||
#define DEFAULT_USER_MANUAL_ONLINE_URI \
|
||||
"https://docs.pika.org/" PIKA_APP_VERSION_STRING
|
||||
"https://heckin.technology/AlderconeStudio/PIKApp/wiki/" PIKA_APP_VERSION_STRING
|
||||
|
||||
|
||||
enum
|
||||
|
@ -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)
|
||||
|
@ -34,11 +34,12 @@
|
||||
|
||||
#include "config/pikacoreconfig.h"
|
||||
|
||||
#include "widgets/pikawidgets-utils.h"
|
||||
|
||||
#include "about.h"
|
||||
#include "git-version.h"
|
||||
|
||||
#include "about-dialog.h"
|
||||
#include "authors.h"
|
||||
#include "pika-update.h"
|
||||
#include "pika-version.h"
|
||||
|
||||
@ -47,20 +48,22 @@
|
||||
|
||||
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;
|
||||
guint timer;
|
||||
|
||||
gint index;
|
||||
gint animstep;
|
||||
gint state;
|
||||
gboolean visible;
|
||||
gint index;
|
||||
gint animstep;
|
||||
gint state;
|
||||
gboolean visible;
|
||||
} PikaAboutDialog;
|
||||
|
||||
|
||||
@ -93,7 +96,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 +110,7 @@ about_dialog_create (PikaCoreConfig *config)
|
||||
gchar *copyright;
|
||||
gchar *version;
|
||||
|
||||
dialog.pika = pika;
|
||||
dialog.config = config;
|
||||
|
||||
pixbuf = about_dialog_load_logo ();
|
||||
@ -134,9 +139,6 @@ about_dialog_create (PikaCoreConfig *config)
|
||||
"logo", pixbuf,
|
||||
"website", "https://heckin.technology/AlderconeStudio/PIKApp/",
|
||||
"website-label", _("Visit the PIKA website"),
|
||||
/* Translators: insert your names here,
|
||||
separated by newline */
|
||||
"translator-credits", _("translator-credits"),
|
||||
NULL);
|
||||
|
||||
if (pixbuf)
|
||||
@ -196,6 +198,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
|
||||
@ -264,203 +270,6 @@ static void
|
||||
about_dialog_add_update (PikaAboutDialog *dialog,
|
||||
PikaCoreConfig *config)
|
||||
{
|
||||
GtkWidget *container;
|
||||
GList *children;
|
||||
GtkWidget *vbox;
|
||||
|
||||
GtkWidget *frame;
|
||||
GtkWidget *box;
|
||||
GtkWidget *box2;
|
||||
GtkWidget *label;
|
||||
GtkWidget *button;
|
||||
GtkWidget *button_image;
|
||||
GtkWidget *button_label;
|
||||
GDateTime *datetime;
|
||||
gchar *date;
|
||||
gchar *text;
|
||||
|
||||
if (dialog->update_frame)
|
||||
{
|
||||
gtk_widget_destroy (dialog->update_frame);
|
||||
dialog->update_frame = NULL;
|
||||
}
|
||||
|
||||
/* Get the dialog vbox. */
|
||||
container = gtk_dialog_get_content_area (GTK_DIALOG (dialog->dialog));
|
||||
children = gtk_container_get_children (GTK_CONTAINER (container));
|
||||
g_return_if_fail (GTK_IS_BOX (children->data));
|
||||
vbox = children->data;
|
||||
g_list_free (children);
|
||||
|
||||
/* The update frame. */
|
||||
frame = gtk_frame_new (NULL);
|
||||
gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 2);
|
||||
|
||||
box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
|
||||
gtk_container_add (GTK_CONTAINER (frame), box);
|
||||
|
||||
/* Button in the frame. */
|
||||
button = gtk_button_new ();
|
||||
gtk_box_pack_start (GTK_BOX (box), button, FALSE, FALSE, 0);
|
||||
gtk_widget_show (button);
|
||||
|
||||
box2 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
|
||||
gtk_container_add (GTK_CONTAINER (button), box2);
|
||||
gtk_widget_show (box2);
|
||||
|
||||
button_image = gtk_image_new_from_icon_name (NULL, GTK_ICON_SIZE_DIALOG);
|
||||
gtk_box_pack_start (GTK_BOX (box2), button_image, FALSE, FALSE, 0);
|
||||
gtk_widget_show (button_image);
|
||||
|
||||
button_label = gtk_label_new (NULL);
|
||||
gtk_box_pack_start (GTK_BOX (box2), button_label, FALSE, FALSE, 0);
|
||||
gtk_container_child_set (GTK_CONTAINER (box2), button_label, "expand", TRUE, NULL);
|
||||
gtk_widget_show (button_label);
|
||||
|
||||
if (config->last_known_release != NULL)
|
||||
{
|
||||
/* There is a newer version. */
|
||||
const gchar *download_url = NULL;
|
||||
gchar *comment = NULL;
|
||||
|
||||
/* We want the frame to stand out. */
|
||||
label = gtk_label_new (NULL);
|
||||
text = g_strdup_printf ("<tt><b><big>%s</big></b></tt>",
|
||||
_("Update available!"));
|
||||
gtk_label_set_markup (GTK_LABEL (label), text);
|
||||
g_free (text);
|
||||
gtk_widget_show (label);
|
||||
gtk_frame_set_label_widget (GTK_FRAME (frame), label);
|
||||
gtk_frame_set_label_align (GTK_FRAME (frame), 0.5, 0.5);
|
||||
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_OUT);
|
||||
gtk_box_reorder_child (GTK_BOX (vbox), frame, 3);
|
||||
|
||||
/* Button is an update link. */
|
||||
gtk_image_set_from_icon_name (GTK_IMAGE (button_image),
|
||||
"software-update-available",
|
||||
GTK_ICON_SIZE_DIALOG);
|
||||
#ifdef PIKA_UNSTABLE
|
||||
download_url = "https://heckin.technology/AlderconeStudio/PIKApp/downloads/devel/";
|
||||
#else
|
||||
download_url = "https://heckin.technology/AlderconeStudio/PIKApp/downloads/";
|
||||
#endif
|
||||
g_signal_connect (button, "clicked",
|
||||
(GCallback) about_dialog_download_clicked,
|
||||
(gpointer) download_url);
|
||||
|
||||
/* The preferred localized date representation without the time. */
|
||||
datetime = g_date_time_new_from_unix_local (config->last_release_timestamp);
|
||||
date = g_date_time_format (datetime, "%x");
|
||||
g_date_time_unref (datetime);
|
||||
|
||||
if (config->last_revision > 0)
|
||||
{
|
||||
/* This is actually a new revision of current version. */
|
||||
text = g_strdup_printf (_("Download PIKA %s revision %d (released on %s)\n"),
|
||||
config->last_known_release,
|
||||
config->last_revision,
|
||||
date);
|
||||
|
||||
/* Finally an optional release comment. */
|
||||
if (config->last_release_comment)
|
||||
{
|
||||
/* Translators: <> tags are Pango markup. Please keep these
|
||||
* markups in your translation. */
|
||||
comment = g_strdup_printf (_("<u>Release comment</u>: <i>%s</i>"), config->last_release_comment);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
text = g_strdup_printf (_("Download PIKA %s (released on %s)\n"),
|
||||
config->last_known_release, date);
|
||||
}
|
||||
gtk_label_set_text (GTK_LABEL (button_label), text);
|
||||
g_free (text);
|
||||
g_free (date);
|
||||
|
||||
if (comment)
|
||||
{
|
||||
label = gtk_label_new (NULL);
|
||||
gtk_label_set_max_width_chars (GTK_LABEL (label), 80);
|
||||
gtk_label_set_markup (GTK_LABEL (label), comment);
|
||||
gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
|
||||
g_free (comment);
|
||||
|
||||
gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0);
|
||||
gtk_widget_show (label);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Button is a "Check for updates" action. */
|
||||
gtk_image_set_from_icon_name (GTK_IMAGE (button_image),
|
||||
"view-refresh",
|
||||
GTK_ICON_SIZE_MENU);
|
||||
gtk_label_set_text (GTK_LABEL (button_label), _("Check for updates"));
|
||||
g_signal_connect_swapped (button, "clicked",
|
||||
(GCallback) pika_update_check, config);
|
||||
|
||||
}
|
||||
|
||||
gtk_box_reorder_child (GTK_BOX (vbox), frame, 4);
|
||||
|
||||
/* Last check date box. */
|
||||
box2 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
|
||||
if (config->last_known_release != NULL)
|
||||
gtk_widget_set_margin_top (box2, 20);
|
||||
gtk_container_add (GTK_CONTAINER (box), box2);
|
||||
gtk_widget_show (box2);
|
||||
|
||||
/* Show a small "Check for updates" button only if the big one has
|
||||
* been replaced by a download button.
|
||||
*/
|
||||
if (config->last_known_release != NULL)
|
||||
{
|
||||
button = gtk_button_new_from_icon_name ("view-refresh", GTK_ICON_SIZE_MENU);
|
||||
gtk_widget_set_tooltip_text (button, _("Check for updates"));
|
||||
gtk_box_pack_start (GTK_BOX (box2), button, FALSE, FALSE, 0);
|
||||
g_signal_connect_swapped (button, "clicked",
|
||||
(GCallback) pika_update_check, config);
|
||||
gtk_widget_show (button);
|
||||
}
|
||||
|
||||
if (config->check_update_timestamp > 0)
|
||||
{
|
||||
gchar *subtext;
|
||||
gchar *time;
|
||||
|
||||
datetime = g_date_time_new_from_unix_local (config->check_update_timestamp);
|
||||
date = g_date_time_format (datetime, "%x");
|
||||
time = g_date_time_format (datetime, "%X");
|
||||
/* Translators: first string is the date in the locale's date
|
||||
* representation (e.g., 12/31/99), second is the time in the
|
||||
* locale's time representation (e.g., 23:13:48).
|
||||
*/
|
||||
subtext = g_strdup_printf (_("Last checked on %s at %s"), date, time);
|
||||
g_date_time_unref (datetime);
|
||||
g_free (date);
|
||||
g_free (time);
|
||||
|
||||
text = g_strdup_printf ("<i>%s</i>", subtext);
|
||||
label = gtk_label_new (NULL);
|
||||
gtk_label_set_markup (GTK_LABEL (label), text);
|
||||
gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_CENTER);
|
||||
gtk_box_pack_start (GTK_BOX (box2), label, FALSE, FALSE, 0);
|
||||
gtk_container_child_set (GTK_CONTAINER (box2), label, "expand", TRUE, NULL);
|
||||
gtk_widget_show (label);
|
||||
g_free (text);
|
||||
g_free (subtext);
|
||||
}
|
||||
|
||||
gtk_widget_show (box);
|
||||
gtk_widget_show (frame);
|
||||
|
||||
g_set_weak_pointer (&dialog->update_frame, frame);
|
||||
|
||||
/* Reconstruct the dialog when release info changes. */
|
||||
g_signal_connect (config, "notify::last-known-release",
|
||||
(GCallback) about_dialog_last_release_changed,
|
||||
dialog);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -625,82 +434,6 @@ decorate_text (PikaAboutDialog *dialog,
|
||||
static gboolean
|
||||
about_dialog_timer (gpointer data)
|
||||
{
|
||||
PikaAboutDialog *dialog = data;
|
||||
gint timeout = 0;
|
||||
|
||||
if (dialog->animstep == 0)
|
||||
{
|
||||
gchar *text = NULL;
|
||||
|
||||
dialog->visible = TRUE;
|
||||
|
||||
switch (dialog->state)
|
||||
{
|
||||
case 0:
|
||||
dialog->timer = g_timeout_add (30, about_dialog_timer, dialog);
|
||||
dialog->state += 1;
|
||||
return FALSE;
|
||||
|
||||
case 1:
|
||||
text = insert_spacers (_("PIKA is brought to you by Aldercone Studio"));
|
||||
break;
|
||||
|
||||
case 2:
|
||||
return FALSE;
|
||||
|
||||
default:
|
||||
g_return_val_if_reached (TRUE);
|
||||
break;
|
||||
}
|
||||
|
||||
g_return_val_if_fail (text != NULL, TRUE);
|
||||
|
||||
pango_layout_set_text (dialog->layout, text, -1);
|
||||
pango_layout_set_attributes (dialog->layout, NULL);
|
||||
|
||||
g_free (text);
|
||||
}
|
||||
|
||||
if (dialog->animstep < 16)
|
||||
{
|
||||
decorate_text (dialog, 2, ((gfloat) dialog->animstep) / 15.0);
|
||||
}
|
||||
else if (dialog->animstep == 16)
|
||||
{
|
||||
timeout = 800;
|
||||
}
|
||||
else if (dialog->animstep == 17)
|
||||
{
|
||||
timeout = 30;
|
||||
}
|
||||
else if (dialog->animstep < 33)
|
||||
{
|
||||
decorate_text (dialog, 1,
|
||||
1.0 - ((gfloat) (dialog->animstep - 17)) / 15.0);
|
||||
}
|
||||
else if (dialog->animstep == 33)
|
||||
{
|
||||
dialog->visible = FALSE;
|
||||
timeout = 300;
|
||||
}
|
||||
else
|
||||
{
|
||||
dialog->visible = FALSE;
|
||||
dialog->animstep = -1;
|
||||
timeout = 30;
|
||||
}
|
||||
|
||||
dialog->animstep++;
|
||||
|
||||
gtk_widget_queue_draw (dialog->anim_area);
|
||||
|
||||
if (timeout > 0)
|
||||
{
|
||||
dialog->timer = g_timeout_add (timeout, about_dialog_timer, dialog);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* else keep the current timeout */
|
||||
return 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"),
|
||||
|
@ -191,7 +191,7 @@ tips_dialog_create (Pika *pika)
|
||||
gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
|
||||
gtk_widget_show (hbox);
|
||||
|
||||
more_button = gtk_link_button_new_with_label ("https://docs.pika.org/",
|
||||
more_button = gtk_link_button_new_with_label ("https://heckin.technology/AlderconeStudio/PIKApp/wiki/",
|
||||
/* a link to the related section in the user manual */
|
||||
_("Learn more"));
|
||||
gtk_widget_show (more_button);
|
||||
@ -251,7 +251,7 @@ tips_dialog_set_tip (PikaTip *tip)
|
||||
|
||||
/* set the URI to unset the "visited" state */
|
||||
gtk_link_button_set_uri (GTK_LINK_BUTTON (more_button),
|
||||
"https://docs.pika.org/");
|
||||
"https://heckin.technology/AlderconeStudio/PIKApp/wiki/");
|
||||
|
||||
gtk_widget_set_sensitive (more_button, tip->help_id != NULL);
|
||||
}
|
||||
|
@ -191,12 +191,12 @@ welcome_dialog_create (Pika *pika)
|
||||
/* "graduation cap" emoticone in UTF-8. */
|
||||
"\xf0\x9f\x8e\x93",
|
||||
_("Tutorials"),
|
||||
"https://heckin.technology/AlderconeStudio/PIKApp/tutorials/");
|
||||
"https://heckin.technology/AlderconeStudio/PIKApp/");
|
||||
welcome_add_link (GTK_GRID (grid), 0, &row,
|
||||
/* "open book" emoticone in UTF-8. */
|
||||
"\xf0\x9f\x93\x96",
|
||||
_("Documentation"),
|
||||
"https://docs.pika.org/");
|
||||
"https://heckin.technology/AlderconeStudio/PIKApp/wiki/");
|
||||
|
||||
/* XXX: should we add API docs for plug-in developers once it's
|
||||
* properly set up? */
|
||||
@ -229,12 +229,12 @@ welcome_dialog_create (Pika *pika)
|
||||
/* "keyboard" emoticone in UTF-8. */
|
||||
"\xe2\x8c\xa8",
|
||||
_("Contributing"),
|
||||
"https://heckin.technology/AlderconeStudio/PIKApp/develop/");
|
||||
"https://heckin.technology/AlderconeStudio/PIKApp/");
|
||||
welcome_add_link (GTK_GRID (grid), 1, &row,
|
||||
/* "love letter" emoticone in UTF-8. */
|
||||
"\xf0\x9f\x92\x8c",
|
||||
_("Donating"),
|
||||
"https://heckin.technology/AlderconeStudio/PIKApp/donating/");
|
||||
"https://mastodon.art/@aldercone/");
|
||||
|
||||
/*****************/
|
||||
/* Release Notes */
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -2451,7 +2451,7 @@ pika_statusbar_queue_pos_redraw (gpointer data)
|
||||
* The goal of this is to avoid the label size jumping up and
|
||||
* down. Actually it was not a problem on Linux, but this was
|
||||
* reported on macOS.
|
||||
* See: https://gitlab.gnome.org/GNOME/pika/-/merge_requests/572#note_1389445
|
||||
* See: https://gitlab.gnome.org/GNOME/gimp/-/merge_requests/572#note_1389445
|
||||
* So we just compute what looks like a reasonable "biggest size"
|
||||
* in worst cases.
|
||||
* Of course, it could still happen for people going way
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -1066,7 +1066,7 @@ pika_dialog_factory_add_foreign (PikaDialogFactory *factory,
|
||||
* This function correctly positions a dialog on re-showing so it
|
||||
* appears where it was before it was hidden.
|
||||
*
|
||||
* See https://gitlab.gnome.org/GNOME/pika/issues/1093
|
||||
* See https://gitlab.gnome.org/GNOME/gimp/issues/1093
|
||||
**/
|
||||
void
|
||||
pika_dialog_factory_position_dialog (PikaDialogFactory *factory,
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -432,7 +432,7 @@ pika_sample_point_editor_points_changed (PikaSamplePointEditor *editor)
|
||||
|
||||
/* Keep that many color frames around so they remember their color
|
||||
* model. Let's hope nobody uses more and notices they get reset to
|
||||
* "pixel". See https://gitlab.gnome.org/GNOME/pika/issues/1805
|
||||
* "pixel". See https://gitlab.gnome.org/GNOME/gimp/issues/1805
|
||||
*/
|
||||
#define RANDOM_MAGIC 16
|
||||
|
||||
|
@ -86,13 +86,13 @@
|
||||
"sources": [
|
||||
{
|
||||
"type": "archive",
|
||||
"url": "https://github.com/Exiv2/exiv2/releases/download/v0.28.0/exiv2-0.28.0-Source.tar.gz",
|
||||
"sha256": "89af3b5ef7277753ef7a7b5374ae017c6b9e304db3b688f1948e73e103491f3d",
|
||||
"url": "https://github.com/Exiv2/exiv2/archive/refs/tags/v0.28.1.tar.gz",
|
||||
"sha256": "3078651f995cb6313b1041f07f4dd1bf0e9e4d394d6e2adc6e92ad0b621291fa",
|
||||
"x-checker-data": {
|
||||
"type": "anitya",
|
||||
"project-id": 769,
|
||||
"stable-only": true,
|
||||
"url-template": "https://github.com/Exiv2/exiv2/releases/download/v$version/exiv2-$version-Source.tar.gz"
|
||||
"url-template": "https://github.com/Exiv2/exiv2/archive/refs/tags/v$version.tar.gz"
|
||||
}
|
||||
}
|
||||
]
|
||||
@ -119,7 +119,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"name" : "openexr",
|
||||
"name": "openexr",
|
||||
"config-opts": [
|
||||
"-DBUILD_SHARED_LIBS=ON",
|
||||
"-DOPENEXR_INSTALL_EXAMPLES=OFF",
|
||||
@ -201,7 +201,8 @@
|
||||
"-DOpenJPEG_DIR=/usr/lib64/openjpeg-2.3",
|
||||
"-DENABLE_BOOST=OFF",
|
||||
"-DWITH_NSS3:BOOL=OFF",
|
||||
"-DENABLE_QT5:BOOL=OFF"
|
||||
"-DENABLE_QT5:BOOL=OFF",
|
||||
"-DENABLE_QT6:BOOL=OFF"
|
||||
],
|
||||
"cleanup": [
|
||||
"/bin",
|
||||
@ -212,8 +213,8 @@
|
||||
"sources": [
|
||||
{
|
||||
"type": "archive",
|
||||
"url": "https://poppler.freedesktop.org/poppler-23.08.0.tar.xz",
|
||||
"sha256": "4a4bf7fc903b9f1a2ab7d04b7c5d8220db9bc6261cc73fdb9a826dc272f49aa8",
|
||||
"url": "https://poppler.freedesktop.org/poppler-23.11.0.tar.xz",
|
||||
"sha256": "f99cca6799cb9cb6c92fc1e0eb78547b611cb733750ab7cb047cb0e6c246539c",
|
||||
"x-checker-data": {
|
||||
"type": "anitya",
|
||||
"project-id": 3686,
|
||||
@ -314,8 +315,8 @@
|
||||
"sources": [
|
||||
{
|
||||
"type": "archive",
|
||||
"url": "https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/download/gs10012/ghostscript-10.01.2.tar.gz",
|
||||
"sha512": "11887850d440b8bbd8b83b8b58df8c743cf94be8fdbcfbda1a5fce55ce7c6a963d94b41a4ebcf1ba48f64f8af01e76dec789e2711212b1f7a32f98c42d75e0ff",
|
||||
"url": "https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/download/gs10021/ghostscript-10.02.1.tar.gz",
|
||||
"sha512": "24eeff047a24085413283ee42539a2feea4d3de81077664861399055a9d30349a0ef0950746bb0b0ee2237b4b9ee215340204ab0e33347b53dc2acee85fcfc19",
|
||||
"x-checker-data": {
|
||||
"//": "Bypass broken url-template with anitya checker - See https://github.com/flathub/flatpak-external-data-checker/issues/360",
|
||||
"type": "json",
|
||||
@ -365,8 +366,8 @@
|
||||
{
|
||||
"type": "git",
|
||||
"url": "https://github.com/json-c/json-c.git",
|
||||
"tag": "json-c-0.16-20220414",
|
||||
"commit": "2f2ddc1f2dbca56c874e8f9c31b5b963202d80e7",
|
||||
"tag": "json-c-0.17-20230812",
|
||||
"commit": "b4c371fa0cbc4dcbaccc359ce9e957a22988fb34",
|
||||
"x-checker-data": {
|
||||
"type": "git",
|
||||
"tag-pattern": "^json-c-([\\d.]+)-[\\d]+$"
|
||||
@ -469,8 +470,8 @@
|
||||
"sources": [
|
||||
{
|
||||
"type": "archive",
|
||||
"url": "https://github.com/xianyi/OpenBLAS/archive/v0.3.23.tar.gz",
|
||||
"sha256": "5d9491d07168a5d00116cdc068a40022c3455bf9293c7cb86a65b1054d7e5114",
|
||||
"url": "https://github.com/xianyi/OpenBLAS/archive/v0.3.24.tar.gz",
|
||||
"sha256": "ceadc5065da97bd92404cac7254da66cc6eb192679cf1002098688978d4d5132",
|
||||
"x-checker-data": {
|
||||
"type": "anitya",
|
||||
"project-id": 2540,
|
||||
@ -616,8 +617,8 @@
|
||||
"sources": [
|
||||
{
|
||||
"type": "archive",
|
||||
"url": "https://github.com/strukturag/libheif/releases/download/v1.16.2/libheif-1.16.2.tar.gz",
|
||||
"sha256": "7f97e4205c0bd9f9b8560536c8bd2e841d1c9a6d610401eb3eb87ed9cdfe78ea",
|
||||
"url": "https://github.com/strukturag/libheif/releases/download/v1.17.3/libheif-1.17.3.tar.gz",
|
||||
"sha256": "8d5b6292e7931324f81f871f250ecbb9f874aa3c66b4f6f35ceb0bf3163b53ea",
|
||||
"x-checker-data": {
|
||||
"type": "anitya",
|
||||
"project-id": 64439,
|
||||
|
@ -57,7 +57,6 @@ download_lang Galician.isl
|
||||
download_lang Georgian.isl
|
||||
download_lang Greek.isl
|
||||
download_lang Indonesian.isl
|
||||
download_lang Korean.isl
|
||||
download_lang Latvian.isl
|
||||
download_lang Lithuanian.isl
|
||||
download_lang Malaysian.isl
|
||||
@ -67,9 +66,9 @@ download_lang Swedish.isl
|
||||
download_lang Vietnamese.isl
|
||||
cd -
|
||||
|
||||
# Hungarian is not in a release yet, but was moved from Unofficial
|
||||
# Any language not in a release yet, but moved from Unofficial, should be added here
|
||||
cd "${ISCCDIR}/Languages/"
|
||||
download_lang_official Hungarian.isl
|
||||
download_lang_official Korean.isl
|
||||
cd -
|
||||
|
||||
# Copy generated language files into the source directory.
|
||||
|
@ -157,6 +157,9 @@ cp -fr ${MSYS_PREFIX}/share/xml/iso-codes/iso_639.xml ${PIKA_DISTRIB}/share/xml/
|
||||
# Adwaita can be used as the base icon set.
|
||||
cp -fr ${MSYS_PREFIX}/share/icons/Adwaita ${PIKA_DISTRIB}/share/icons/
|
||||
|
||||
# Gdbus is needed to avoid warnings in CMD.
|
||||
cp -fr ${MSYS_PREFIX}/bin/gdbus.exe ${PIKA_DISTRIB}/bin
|
||||
|
||||
# XXX Why are these for exactly?
|
||||
cp -fr ${MSYS_PREFIX}/bin/gspawn*.exe ${PIKA_DISTRIB}/bin/
|
||||
|
||||
|
@ -13,6 +13,7 @@ exr
|
||||
gif
|
||||
heif
|
||||
heic
|
||||
hej2
|
||||
icns
|
||||
jp2
|
||||
j2k
|
||||
|
@ -25,7 +25,7 @@
|
||||
|
||||
<!-- Shown before the wizard starts on development versions of PIKA -->
|
||||
<config name="DevelopmentWarning">
|
||||
<value>This is a development version of PIKA where some features may not be finished, or it may be unstable.%nThis version of PIKA is not intended for day-to-day work as it may be unstable, and you could lose your work.%nIf you encounter any problems, first verify that they haven't already been fixed in GIT before you contact the developers or report it in PIKA gitlab:%n_https://gitlab.gnome.org/GNOME/pika/issues%n%nDo you wish to continue with installation anyway?</value>
|
||||
<value>This is a development version of PIKA where some features may not be finished, or it may be unstable.%nThis version of PIKA is not intended for day-to-day work as it may be unstable, and you could lose your work.%nIf you encounter any problems, first verify that they haven't already been fixed in GIT before you contact the developers or report it in PIKA gitlab:%n_https://heckin.technology/AlderconeStudio/PIKApp/issues/%n%nDo you wish to continue with installation anyway?</value>
|
||||
</config>
|
||||
<config name="DevelopmentButtonContinue">
|
||||
<value>&Continue</value>
|
||||
|
@ -216,7 +216,7 @@ Name: "it"; MessagesFile: "compiler:Languages\Italian.isl,lang\it.setup.isl"
|
||||
Name: "ja"; MessagesFile: "compiler:Languages\Japanese.isl,lang\ja.setup.isl"
|
||||
Name: "ka"; MessagesFile: "compiler:Languages\Unofficial\Georgian.isl,lang\ka.setup.isl"
|
||||
Name: "kab"; MessagesFile: "compiler:Default.isl,lang\kab.isl,lang\kab.setup.isl"
|
||||
Name: "ko"; MessagesFile: "compiler:Languages\Unofficial\Korean.isl,lang\ko.setup.isl"
|
||||
Name: "ko"; MessagesFile: "compiler:Languages\Korean.isl,lang\ko.setup.isl"
|
||||
Name: "lt"; MessagesFile: "compiler:Languages\Unofficial\Lithuanian.isl,lang\lt.setup.isl"
|
||||
Name: "lv"; MessagesFile: "compiler:Languages\Unofficial\Latvian.isl,lang\lv.setup.isl"
|
||||
Name: "mr"; MessagesFile: "compiler:Languages\Unofficial\Marathi.islu,lang\mr.setup.isl"
|
||||
|
@ -37,11 +37,11 @@
|
||||
</p>
|
||||
</description>
|
||||
<url type="bugtracker">@BUG_REPORT_URL@</url>
|
||||
<url type="donation">https://heckin.technology/AlderconeStudio/PIKApp/donating/</url>
|
||||
<url type="donation">https://mastodon.art/@aldercone/</url>
|
||||
<url type="help">https://heckin.technology/AlderconeStudio/PIKApp/docs/</url>
|
||||
<url type="homepage">https://heckin.technology/AlderconeStudio/PIKApp/</url>
|
||||
<url type="vcs-browser">https://gitlab.gnome.org/GNOME/pika/</url>
|
||||
<url type="contribute">https://heckin.technology/AlderconeStudio/PIKApp/develop/</url>
|
||||
<url type="contribute">https://heckin.technology/AlderconeStudio/PIKApp/</url>
|
||||
<screenshots>
|
||||
<screenshot type="default">
|
||||
<image>https://heckin.technology/AlderconeStudio/PIKApp/screenshots/Screenshot-pika-2.10-painting.jpg</image>
|
||||
@ -407,6 +407,32 @@
|
||||
</description>
|
||||
</release>
|
||||
|
||||
<release version="2.10.36" date="2023-11-05">
|
||||
<description>
|
||||
<p>
|
||||
This new version of PIKA comes with many fixes, including
|
||||
vulnerability corrections. It is highly recommended to update. It
|
||||
also provides a few interesting changes, such as:
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
Adobe ACB and ASE palette support
|
||||
</li>
|
||||
<li>
|
||||
New gradient: FG to Transparent (Hardedge)
|
||||
</li>
|
||||
<li>
|
||||
Some text tool algorithm enhancement when replacing formatted text
|
||||
</li>
|
||||
<li>
|
||||
Better theming of item locks (when hovering them or setting them active)
|
||||
</li>
|
||||
<li>
|
||||
Improvements in handling a few specific metadata
|
||||
</li>
|
||||
</ul>
|
||||
</description>
|
||||
</release>
|
||||
<release version="2.10.34" date="2023-02-21">
|
||||
<description>
|
||||
<p>
|
||||
|
@ -322,7 +322,7 @@ As a last resort, PIKA uses the default splash image located at
|
||||
.SH SUGGESTIONS AND BUG REPORTS
|
||||
Any bugs found should be reported to the online bug-tracking system
|
||||
available on the web at
|
||||
https://gitlab.gnome.org/GNOME/pika/issues. Before reporting bugs,
|
||||
https://heckin.technology/AlderconeStudio/PIKApp/issues/. Before reporting bugs,
|
||||
please check to see if the bug has already been reported.
|
||||
|
||||
When reporting PIKA bugs, it is important to include a reliable way to
|
||||
@ -337,7 +337,7 @@ The canonical place to find PIKA info is at https://heckin.technology/AlderconeS
|
||||
Here you can find links to just about many other PIKA sites,
|
||||
tutorials, data sets, mailing list archives, and more.
|
||||
|
||||
There is also a PIKA User Manual available at https://docs.pika.org/
|
||||
There is also a PIKA User Manual available at https://heckin.technology/AlderconeStudio/PIKApp/wiki/
|
||||
that goes into much more detail about the interactive use of PIKA.
|
||||
|
||||
The latest versions of PIKA and the GTK libs are always available at
|
||||
|
@ -1057,7 +1057,7 @@ When enabled, the online user manual will be used by the help system.
|
||||
Otherwise the locally installed copy is used. Possible values are yes and no.
|
||||
|
||||
.TP
|
||||
(user-manual-online-uri "https://docs.pika.org/2.99")
|
||||
(user-manual-online-uri "https://heckin.technology/AlderconeStudio/PIKApp/wiki/2.99")
|
||||
|
||||
The location of the online user manual. This is used if 'user-manual-online'
|
||||
is enabled. This is a string value.
|
||||
|
@ -842,7 +842,7 @@
|
||||
# The location of the online user manual. This is used if
|
||||
# 'user-manual-online' is enabled. This is a string value.
|
||||
#
|
||||
# (user-manual-online-uri "https://docs.pika.org/2.99")
|
||||
# (user-manual-online-uri "https://heckin.technology/AlderconeStudio/PIKApp/wiki/2.99")
|
||||
|
||||
# The window type hint that is set on dock windows and the toolbox window.
|
||||
# This may affect the way your window manager decorates and handles these
|
||||
|
@ -1191,14 +1191,14 @@ pika_vectors_import_from_string (PikaImage *image,
|
||||
* pika_vectors_export_to_file:
|
||||
* @image: The image.
|
||||
* @file: The SVG file to create.
|
||||
* @vectors: The vectors object to be saved, or 0 for all in the image.
|
||||
* @vectors: The vectors object to export, or %NULL for all in the image.
|
||||
*
|
||||
* save a path as an SVG file.
|
||||
*
|
||||
* This procedure creates an SVG file to save a Vectors object, that
|
||||
* is, a path. The resulting file can be edited using a vector graphics
|
||||
* application, or later reloaded into PIKA. If you pass 0 as the
|
||||
* 'vectors' argument, then all paths in the image will be exported.
|
||||
* application, or later reloaded into PIKA. Pass %NULL as the
|
||||
* 'vectors' argument to export all paths in the image.
|
||||
*
|
||||
* Returns: TRUE on success.
|
||||
*
|
||||
@ -1234,14 +1234,14 @@ pika_vectors_export_to_file (PikaImage *image,
|
||||
/**
|
||||
* pika_vectors_export_to_string:
|
||||
* @image: The image.
|
||||
* @vectors: The vectors object to save, or 0 for all in the image.
|
||||
* @vectors: The vectors object to export, or %NULL for all in the image.
|
||||
*
|
||||
* Save a path as an SVG string.
|
||||
*
|
||||
* This procedure works like pika_vectors_export_to_file() but creates
|
||||
* a string rather than a file. The contents are a NUL-terminated
|
||||
* string that holds a complete XML document. If you pass 0 as the
|
||||
* 'vectors' argument, then all paths in the image will be exported.
|
||||
* a string rather than a file. The string is NULL-terminated and holds
|
||||
* a complete XML document. Pass %NULL as the 'vectors' argument to
|
||||
* export all paths in the image.
|
||||
*
|
||||
* Returns: (transfer full):
|
||||
* A string whose contents are a complete SVG document.
|
||||
|
@ -351,6 +351,7 @@ pika_config_param_spec_duplicate (GParamSpec *pspec)
|
||||
* with type names instead.
|
||||
*/
|
||||
g_strcmp0 (type_name, "PikaImage") == 0 ||
|
||||
g_strcmp0 (type_name, "PikaDisplay") == 0 ||
|
||||
g_strcmp0 (type_name, "PikaDrawable") == 0 ||
|
||||
g_strcmp0 (type_name, "PikaLayer") == 0 ||
|
||||
g_strcmp0 (type_name, "PikaTextLayer") == 0 ||
|
||||
|
@ -753,7 +753,7 @@ pika_matrix3_invert (PikaMatrix3 *matrix)
|
||||
/* To avoid redundant access to the coefficients, inline the determinant
|
||||
* formula.
|
||||
*
|
||||
* See: https://gitlab.gnome.org/GNOME/pika/-/merge_requests/880#note_1727051
|
||||
* See: https://gitlab.gnome.org/GNOME/gimp/-/merge_requests/880#note_1727051
|
||||
*/
|
||||
det = m00 * (m11 * m22 - m12 * m21)
|
||||
- m10 * (m01 * m22 - m02 * m21)
|
||||
|
@ -96,7 +96,9 @@ static void pika_dialog_close (GtkDialog *dialog);
|
||||
static void pika_dialog_response (GtkDialog *dialog,
|
||||
gint response_id);
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
static void pika_dialog_set_title_bar_theme (GtkWidget *dialog);
|
||||
#endif
|
||||
|
||||
G_DEFINE_TYPE_WITH_PRIVATE (PikaDialog, pika_dialog, GTK_TYPE_DIALOG)
|
||||
|
||||
@ -770,10 +772,10 @@ pika_dialogs_show_help_button (gboolean show)
|
||||
show_help_button = show ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
void
|
||||
pika_dialog_set_title_bar_theme (GtkWidget *dialog)
|
||||
{
|
||||
#ifdef G_OS_WIN32
|
||||
HWND hwnd;
|
||||
gboolean use_dark_mode = FALSE;
|
||||
GdkWindow *window = NULL;
|
||||
@ -810,5 +812,5 @@ pika_dialog_set_title_bar_theme (GtkWidget *dialog)
|
||||
gdk_window_hide (window);
|
||||
gdk_window_show (window);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
@ -90,10 +90,16 @@
|
||||
<item><attribute name="action">app.edit-paste-in-place</attribute></item>
|
||||
<submenu>
|
||||
<attribute name="label" translatable="yes" context="edit-action">Paste _as</attribute>
|
||||
<item><attribute name="action">app.edit-paste-merged</attribute></item>
|
||||
<item><attribute name="action">app.edit-paste-merged-in-place</attribute></item>
|
||||
<item><attribute name="action">app.edit-paste-into</attribute></item>
|
||||
<item><attribute name="action">app.edit-paste-into-in-place</attribute></item>
|
||||
<section>
|
||||
<item><attribute name="action">app.edit-paste-merged</attribute></item>
|
||||
<item><attribute name="action">app.edit-paste-merged-in-place</attribute></item>
|
||||
</section>
|
||||
<section>
|
||||
<item><attribute name="action">app.edit-paste-float</attribute></item>
|
||||
<item><attribute name="action">app.edit-paste-float-in-place</attribute></item>
|
||||
<item><attribute name="action">app.edit-paste-into</attribute></item>
|
||||
<item><attribute name="action">app.edit-paste-into-in-place</attribute></item>
|
||||
</section>
|
||||
<item><attribute name="action">app.edit-paste-as-new-image</attribute><attribute name="label-variant">long</attribute></item>
|
||||
</submenu>
|
||||
<submenu>
|
||||
@ -143,7 +149,11 @@
|
||||
<item><attribute name="action">app.select-all</attribute></item>
|
||||
<item><attribute name="action">app.select-none</attribute></item>
|
||||
<item><attribute name="action">app.select-invert</attribute></item>
|
||||
<item><attribute name="action">app.select-float</attribute></item>
|
||||
<submenu>
|
||||
<attribute name="label" translatable="yes" context="view-action">_Float</attribute>
|
||||
<item><attribute name="action">app.select-cut-float</attribute></item>
|
||||
<item><attribute name="action">app.select-copy-float</attribute></item>
|
||||
</submenu>
|
||||
<item><attribute name="action">app.tools-by-color-select-short</attribute></item>
|
||||
<item><attribute name="action">app.vectors-selection-from-vectors</attribute></item>
|
||||
<item><attribute name="action">app.dialogs-selection-editor</attribute></item>
|
||||
|
@ -10,7 +10,10 @@
|
||||
<item><attribute name="action">app.select-none</attribute></item>
|
||||
<item><attribute name="action">app.select-invert</attribute></item>
|
||||
<item><attribute name="action">app.vectors-selection-from-vectors</attribute></item>
|
||||
<item><attribute name="action">app.select-float</attribute></item>
|
||||
<section>
|
||||
<item><attribute name="action">app.select-cut-float</attribute></item>
|
||||
<item><attribute name="action">app.select-copy-float</attribute></item>
|
||||
</section>
|
||||
<section>
|
||||
<item><attribute name="action">app.select-feather</attribute></item>
|
||||
<item><attribute name="action">app.select-sharpen</attribute></item>
|
||||
|
@ -1,6 +1,6 @@
|
||||
project('pika',
|
||||
'c', 'cpp',
|
||||
version: '2.99.17',
|
||||
version: '0.1.0.GnuImp2.99.70', #heckimp skip
|
||||
meson_version: '>=0.59.0',
|
||||
default_options: [
|
||||
'cpp_std=gnu++14',
|
||||
@ -8,7 +8,7 @@ project('pika',
|
||||
],
|
||||
)
|
||||
|
||||
project_url = 'https://gitlab.gnome.org/GNOME/pika'
|
||||
project_url = 'https://heckin.technology/AlderconeStudio/PIKApp'
|
||||
project_url_issues = project_url + '/issues/new'
|
||||
|
||||
conf = configuration_data()
|
||||
|
@ -192,7 +192,7 @@ sub vectors_stroke_get_length {
|
||||
code => <<"CODE"
|
||||
{
|
||||
PikaStroke *stroke = pika_pdb_get_vectors_stroke (vectors, stroke_id, 0, error);
|
||||
|
||||
|
||||
if (stroke)
|
||||
length = pika_stroke_get_length (stroke, precision);
|
||||
else
|
||||
@ -207,7 +207,7 @@ sub vectors_stroke_get_point_at_dist {
|
||||
|
||||
$help = <<'HELP';
|
||||
This will return the x,y position of a point at a given distance along the
|
||||
stroke. The distance will be obtained by first digitizing the
|
||||
stroke. The distance will be obtained by first digitizing the
|
||||
curve internally and then walking along the curve. For a closed stroke the
|
||||
start of the path is the first point on the path that was created. This might
|
||||
not be obvious. If the stroke is not long enough, a "valid" flag will be FALSE.
|
||||
@ -241,7 +241,7 @@ HELP
|
||||
code => <<"CODE"
|
||||
{
|
||||
PikaStroke *stroke = pika_pdb_get_vectors_stroke (vectors, stroke_id, 0, error);
|
||||
|
||||
|
||||
if (stroke)
|
||||
{
|
||||
PikaCoords coord;
|
||||
@ -279,7 +279,7 @@ HELP
|
||||
{
|
||||
PikaStroke *stroke = pika_pdb_get_vectors_stroke (vectors, stroke_id,
|
||||
PIKA_PDB_ITEM_CONTENT, error);
|
||||
|
||||
|
||||
if (stroke)
|
||||
{
|
||||
if (pika_item_is_attached (PIKA_ITEM (vectors)))
|
||||
@ -317,7 +317,7 @@ HELP
|
||||
{
|
||||
PikaStroke *stroke = pika_pdb_get_vectors_stroke (vectors, stroke_id,
|
||||
PIKA_PDB_ITEM_CONTENT, error);
|
||||
|
||||
|
||||
if (stroke)
|
||||
{
|
||||
if (pika_item_is_attached (PIKA_ITEM (vectors)))
|
||||
@ -405,7 +405,7 @@ HELP
|
||||
PIKA_PDB_ITEM_CONTENT |
|
||||
PIKA_PDB_ITEM_POSITION,
|
||||
error);
|
||||
|
||||
|
||||
if (stroke)
|
||||
{
|
||||
if (pika_item_is_attached (PIKA_ITEM (vectors)))
|
||||
@ -451,7 +451,7 @@ HELP
|
||||
PIKA_PDB_ITEM_CONTENT |
|
||||
PIKA_PDB_ITEM_POSITION,
|
||||
error);
|
||||
|
||||
|
||||
if (stroke)
|
||||
{
|
||||
if (pika_item_is_attached (PIKA_ITEM (vectors)))
|
||||
@ -498,7 +498,7 @@ HELP
|
||||
PIKA_PDB_ITEM_CONTENT |
|
||||
PIKA_PDB_ITEM_POSITION,
|
||||
error);
|
||||
|
||||
|
||||
if (stroke)
|
||||
{
|
||||
if (pika_item_is_attached (PIKA_ITEM (vectors)))
|
||||
@ -544,7 +544,7 @@ HELP
|
||||
PIKA_PDB_ITEM_CONTENT |
|
||||
PIKA_PDB_ITEM_POSITION,
|
||||
error);
|
||||
|
||||
|
||||
if (stroke)
|
||||
{
|
||||
if (pika_item_is_attached (PIKA_ITEM (vectors)))
|
||||
@ -595,7 +595,7 @@ HELP
|
||||
PIKA_PDB_ITEM_CONTENT |
|
||||
PIKA_PDB_ITEM_POSITION,
|
||||
error);
|
||||
|
||||
|
||||
if (stroke)
|
||||
{
|
||||
if (pika_item_is_attached (PIKA_ITEM (vectors)))
|
||||
@ -647,7 +647,7 @@ HELP
|
||||
code => <<"CODE"
|
||||
{
|
||||
PikaStroke *stroke = pika_pdb_get_vectors_stroke (vectors, stroke_id, 0, error);
|
||||
|
||||
|
||||
if (PIKA_IS_BEZIER_STROKE (stroke))
|
||||
{
|
||||
GArray *points_array;
|
||||
@ -713,7 +713,7 @@ HELP
|
||||
code => <<"CODE"
|
||||
{
|
||||
PikaStroke *stroke = pika_pdb_get_vectors_stroke (vectors, stroke_id, 0, error);
|
||||
|
||||
|
||||
if (stroke)
|
||||
{
|
||||
GArray *coords_array;
|
||||
@ -1162,7 +1162,7 @@ HELP
|
||||
{
|
||||
GList *list;
|
||||
gint i;
|
||||
|
||||
|
||||
vectors = g_new (PikaVectors *, num_vectors);
|
||||
|
||||
for (i = 0, list = vectors_list;
|
||||
@ -1231,9 +1231,9 @@ HELP
|
||||
{
|
||||
GList *list;
|
||||
gint i;
|
||||
|
||||
|
||||
vectors = g_new (PikaVectors *, num_vectors);
|
||||
|
||||
|
||||
for (i = 0, list = vectors_list;
|
||||
i < num_vectors;
|
||||
i++, list = g_list_next (list))
|
||||
@ -1255,8 +1255,8 @@ sub vectors_export_to_file {
|
||||
$help = <<'HELP';
|
||||
This procedure creates an SVG file to save a Vectors object, that is,
|
||||
a path. The resulting file can be edited using a vector graphics
|
||||
application, or later reloaded into PIKA. If you pass 0 as the 'vectors'
|
||||
argument, then all paths in the image will be exported.
|
||||
application, or later reloaded into PIKA. Pass %NULL as the 'vectors'
|
||||
argument to export all paths in the image.
|
||||
HELP
|
||||
|
||||
&bill_pdb_misc('2007', '2.6');
|
||||
@ -1267,14 +1267,17 @@ HELP
|
||||
{ name => 'file', type => 'file',
|
||||
desc => 'The SVG file to create.' },
|
||||
{ name => 'vectors', type => 'vectors', no_validate => 1,
|
||||
desc => 'The vectors object to be saved, or 0 for all in the image' }
|
||||
desc => 'The vectors object to export, or %NULL for all in the image' }
|
||||
);
|
||||
|
||||
%invoke = (
|
||||
headers => [ qw("vectors/pikavectors-export.h") ],
|
||||
code => <<'CODE'
|
||||
{
|
||||
GList *vectors_list = g_list_prepend (NULL, vectors);
|
||||
GList *vectors_list = NULL;
|
||||
|
||||
if (vectors != NULL)
|
||||
vectors_list = g_list_prepend (vectors_list, vectors);
|
||||
|
||||
success = pika_vectors_export_file (image, vectors_list, file, error);
|
||||
|
||||
@ -1289,9 +1292,9 @@ sub vectors_export_to_string {
|
||||
|
||||
$help = <<'HELP';
|
||||
This procedure works like pika_vectors_export_to_file() but creates a string
|
||||
rather than a file. The contents are a NUL-terminated string that holds a
|
||||
complete XML document. If you pass 0 as the 'vectors' argument, then all
|
||||
paths in the image will be exported.
|
||||
rather than a file. The string is NULL-terminated and holds a
|
||||
complete XML document. Pass %NULL as the 'vectors' argument to export
|
||||
all paths in the image.
|
||||
HELP
|
||||
|
||||
&bill_pdb_misc('2007', '2.6');
|
||||
@ -1300,7 +1303,7 @@ HELP
|
||||
{ name => 'image', type => 'image',
|
||||
desc => 'The image' },
|
||||
{ name => 'vectors', type => 'vectors', no_validate => 1,
|
||||
desc => 'The vectors object to save, or 0 for all in the image' }
|
||||
desc => 'The vectors object to export, or %NULL for all in the image' }
|
||||
);
|
||||
|
||||
@outargs = (
|
||||
@ -1312,7 +1315,10 @@ HELP
|
||||
headers => [ qw("vectors/pikavectors-export.h") ],
|
||||
code => <<'CODE'
|
||||
{
|
||||
GList *vectors_list = g_list_prepend (NULL, vectors);
|
||||
GList *vectors_list = NULL;
|
||||
|
||||
if (vectors != NULL)
|
||||
vectors_list = g_list_prepend (vectors_list, vectors);
|
||||
|
||||
string = pika_vectors_export_string (image, vectors_list);
|
||||
g_list_free (vectors_list);
|
||||
|
@ -64,7 +64,7 @@
|
||||
<download-page rdf:resource="https:///www.pika.org/downloads/" />
|
||||
|
||||
<bug-database
|
||||
rdf:resource="https://gitlab.gnome.org/GNOME/pika/issues"
|
||||
rdf:resource="https://heckin.technology/AlderconeStudio/PIKApp/issues/"
|
||||
/>
|
||||
|
||||
<programming-language>C</programming-language>
|
||||
|
@ -352,7 +352,8 @@ static struct
|
||||
gint delayTime;
|
||||
gint inputFlag;
|
||||
gint disposal;
|
||||
} Gif89 = { -1, -1, -1, 0 };
|
||||
gint num_loops;
|
||||
} Gif89 = { -1, -1, -1, 0, -1 };
|
||||
|
||||
static void read_error (const gchar *error_type,
|
||||
PikaImage *image,
|
||||
@ -399,9 +400,10 @@ load_image (GFile *file,
|
||||
gint grayScale;
|
||||
gboolean useGlobalColormap;
|
||||
gint bitPixel;
|
||||
gint imageCount = 0;
|
||||
PikaImage *image = NULL;
|
||||
gint imageCount = 0;
|
||||
PikaImage *image = NULL;
|
||||
gboolean status;
|
||||
gboolean saved_parasite = FALSE;
|
||||
|
||||
pika_progress_init_printf (_("Opening '%s'"),
|
||||
pika_file_get_utf8_name (file));
|
||||
@ -590,6 +592,25 @@ load_image (GFile *file,
|
||||
/* If we are loading a thumbnail, we stop after the first frame. */
|
||||
if (thumbnail)
|
||||
break;
|
||||
|
||||
/* If there is more than one frame, we add a parasite so that
|
||||
* we know to export as an animation on overwrite */
|
||||
if (Gif89.num_loops > -1 && ! saved_parasite)
|
||||
{
|
||||
PikaParasite *parasite;
|
||||
gchar *str;
|
||||
|
||||
str = g_strdup_printf ("%d", Gif89.num_loops);
|
||||
parasite = pika_parasite_new ("gif/animated",
|
||||
PIKA_PARASITE_PERSISTENT,
|
||||
strlen (str) + 1,
|
||||
(gpointer) str);
|
||||
g_free (str);
|
||||
|
||||
pika_image_attach_parasite (image, parasite);
|
||||
pika_parasite_free (parasite);
|
||||
saved_parasite = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
fclose (fd);
|
||||
@ -690,6 +711,15 @@ DoExtension (FILE *fd,
|
||||
#ifdef GIFDEBUG
|
||||
str = "Application Extension";
|
||||
#endif
|
||||
/* Animation block */
|
||||
if (GetDataBlock (fd, (guchar *) buf))
|
||||
{
|
||||
if (strncmp ((const gchar *) buf, "NETSCAPE2.0", 8) == 0)
|
||||
{
|
||||
if (GetDataBlock (fd, (guchar *) buf))
|
||||
Gif89.num_loops = (buf[0] << 8) | buf[1];
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0xfe: /* Comment Extension */
|
||||
#ifdef GIFDEBUG
|
||||
@ -1256,7 +1286,7 @@ ReadImage (FILE *fd,
|
||||
g_set_error (error, PIKA_PLUG_IN_ERROR, 0,
|
||||
_("Can't handle non-alpha RGB frames.\n"
|
||||
"Please file a bug report at "
|
||||
"https://gitlab.gnome.org/GNOME/pika/issues"));
|
||||
"https://heckin.technology/AlderconeStudio/PIKApp/issues/"));
|
||||
pika_image_delete (*image);
|
||||
*image = NULL;
|
||||
return FALSE;
|
||||
|
@ -268,6 +268,7 @@ gif_save (PikaProcedure *procedure,
|
||||
PikaExportReturn export = PIKA_EXPORT_CANCEL;
|
||||
PikaImage *orig_image;
|
||||
PikaImage *sanitized_image = NULL;
|
||||
PikaParasite *parasite = NULL;
|
||||
GError *error = NULL;
|
||||
|
||||
gegl_init (NULL, NULL);
|
||||
@ -288,6 +289,34 @@ gif_save (PikaProcedure *procedure,
|
||||
*/
|
||||
sanitized_image = image;
|
||||
|
||||
/* If imported as an animation, set the animation configurations
|
||||
* when overwriting the file */
|
||||
parasite = pika_image_get_parasite (image, "gif/animated");
|
||||
if (parasite)
|
||||
{
|
||||
gint num_loops;
|
||||
gchar *parasite_data;
|
||||
guint32 parasite_size;
|
||||
|
||||
parasite_data = (gchar *) pika_parasite_get_data (parasite, ¶site_size);
|
||||
parasite_data = g_strndup (parasite_data, parasite_size);
|
||||
|
||||
if (sscanf (parasite_data, "%i", &num_loops) == 1)
|
||||
{
|
||||
gboolean loop = (num_loops == 0);
|
||||
|
||||
g_object_set (config,
|
||||
"as-animation", TRUE,
|
||||
"loop", loop,
|
||||
"number-of-repeats", num_loops,
|
||||
NULL);
|
||||
}
|
||||
|
||||
pika_image_detach_parasite (image, "gif/animated");
|
||||
g_free (parasite);
|
||||
g_free (parasite_data);
|
||||
}
|
||||
|
||||
if (run_mode == PIKA_RUN_INTERACTIVE)
|
||||
{
|
||||
if (! save_dialog (image, procedure, G_OBJECT (config)))
|
||||
|
@ -31,6 +31,7 @@
|
||||
|
||||
#define LOAD_PROC "file-heif-load"
|
||||
#define LOAD_PROC_AV1 "file-heif-av1-load"
|
||||
#define LOAD_PROC_HEJ2 "file-heif-hej2-load"
|
||||
#define SAVE_PROC "file-heif-save"
|
||||
#define SAVE_PROC_AV1 "file-heif-av1-save"
|
||||
#define PLUG_IN_BINARY "file-heif"
|
||||
@ -103,6 +104,8 @@ static PikaValueArray * heif_av1_save (PikaProcedure *pro
|
||||
#endif
|
||||
|
||||
static PikaImage * load_image (GFile *file,
|
||||
PikaMetadata *metadata,
|
||||
PikaMetadataLoadFlags *flags,
|
||||
gboolean interactive,
|
||||
PikaPDBStatusType *status,
|
||||
GError **error);
|
||||
@ -172,6 +175,13 @@ heif_init_procedures (PikaPlugIn *plug_in)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if LIBHEIF_HAVE_VERSION(1,17,0)
|
||||
if (heif_have_decoder_for_format (heif_compression_JPEG2000))
|
||||
{
|
||||
list = g_list_append (list, g_strdup (LOAD_PROC_HEJ2));
|
||||
}
|
||||
#endif
|
||||
|
||||
#if LIBHEIF_HAVE_VERSION(1,13,0)
|
||||
heif_deinit ();
|
||||
#endif
|
||||
@ -214,7 +224,7 @@ heif_create_procedure (PikaPlugIn *plug_in,
|
||||
|
||||
/* HEIF is an ISOBMFF format whose "brand" (the value after "ftyp")
|
||||
* can be of various values.
|
||||
* See also: https://gitlab.gnome.org/GNOME/pika/issues/2209
|
||||
* See also: https://heckin.technology/AlderconeStudio/PIKApp/issues//2209
|
||||
*/
|
||||
pika_file_procedure_set_magics (PIKA_FILE_PROCEDURE (procedure),
|
||||
"4,string,ftypheic,4,string,ftypheix,"
|
||||
@ -412,6 +422,33 @@ heif_create_procedure (PikaPlugIn *plug_in,
|
||||
pika_export_xmp (),
|
||||
G_PARAM_READWRITE);
|
||||
}
|
||||
#endif
|
||||
#if LIBHEIF_HAVE_VERSION(1,17,0)
|
||||
else if (! strcmp (name, LOAD_PROC_HEJ2))
|
||||
{
|
||||
procedure = pika_load_procedure_new (plug_in, name, PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
heif_load, NULL, NULL);
|
||||
|
||||
pika_procedure_set_menu_label (procedure, _("JPEG 2000 encapsulated in HEIF"));
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
_("Loads HEJ2 images"),
|
||||
_("Load JPEG 2000 image encapsulated in HEIF (HEJ2)"),
|
||||
name);
|
||||
pika_procedure_set_attribution (procedure,
|
||||
"Daniel Novomesky <dnovomesky@gmail.com>",
|
||||
"Daniel Novomesky <dnovomesky@gmail.com>",
|
||||
"2023");
|
||||
|
||||
pika_file_procedure_set_handles_remote (PIKA_FILE_PROCEDURE (procedure), TRUE);
|
||||
pika_file_procedure_set_mime_types (PIKA_FILE_PROCEDURE (procedure),
|
||||
"image/hej2k");
|
||||
pika_file_procedure_set_extensions (PIKA_FILE_PROCEDURE (procedure),
|
||||
"hej2");
|
||||
|
||||
pika_file_procedure_set_magics (PIKA_FILE_PROCEDURE (procedure),
|
||||
"4,string,ftypj2ki");
|
||||
}
|
||||
#endif
|
||||
return procedure;
|
||||
}
|
||||
@ -442,7 +479,7 @@ heif_load (PikaProcedure *procedure,
|
||||
heif_init (NULL);
|
||||
#endif
|
||||
|
||||
image = load_image (file, interactive, &status, &error);
|
||||
image = load_image (file, metadata, flags, interactive, &status, &error);
|
||||
|
||||
#if LIBHEIF_HAVE_VERSION(1,13,0)
|
||||
heif_deinit ();
|
||||
@ -836,10 +873,12 @@ nclx_to_pika_profile (const struct heif_color_profile_nclx *nclx)
|
||||
#endif
|
||||
|
||||
PikaImage *
|
||||
load_image (GFile *file,
|
||||
gboolean interactive,
|
||||
PikaPDBStatusType *status,
|
||||
GError **error)
|
||||
load_image (GFile *file,
|
||||
PikaMetadata *metadata,
|
||||
PikaMetadataLoadFlags *flags,
|
||||
gboolean interactive,
|
||||
PikaPDBStatusType *status,
|
||||
GError **error)
|
||||
{
|
||||
GInputStream *input;
|
||||
goffset file_size;
|
||||
@ -1267,117 +1306,111 @@ load_image (GFile *file,
|
||||
|
||||
g_object_unref (buffer);
|
||||
|
||||
{
|
||||
size_t exif_data_size = 0;
|
||||
uint8_t *exif_data = NULL;
|
||||
size_t xmp_data_size = 0;
|
||||
uint8_t *xmp_data = NULL;
|
||||
gint n_metadata;
|
||||
heif_item_id metadata_id;
|
||||
if (metadata)
|
||||
{
|
||||
size_t exif_data_size = 0;
|
||||
uint8_t *exif_data = NULL;
|
||||
size_t xmp_data_size = 0;
|
||||
uint8_t *xmp_data = NULL;
|
||||
gint n_metadata;
|
||||
heif_item_id metadata_id;
|
||||
|
||||
n_metadata =
|
||||
heif_image_handle_get_list_of_metadata_block_IDs (handle,
|
||||
"Exif",
|
||||
&metadata_id, 1);
|
||||
if (n_metadata > 0)
|
||||
{
|
||||
exif_data_size = heif_image_handle_get_metadata_size (handle,
|
||||
metadata_id);
|
||||
exif_data = g_alloca (exif_data_size);
|
||||
n_metadata = heif_image_handle_get_list_of_metadata_block_IDs (handle, "Exif",
|
||||
&metadata_id, 1);
|
||||
if (n_metadata > 0)
|
||||
{
|
||||
exif_data_size = heif_image_handle_get_metadata_size (handle, metadata_id);
|
||||
|
||||
err = heif_image_handle_get_metadata (handle, metadata_id, exif_data);
|
||||
if (err.code != 0)
|
||||
{
|
||||
exif_data = NULL;
|
||||
exif_data_size = 0;
|
||||
}
|
||||
}
|
||||
exif_data = g_alloca (exif_data_size);
|
||||
|
||||
n_metadata =
|
||||
heif_image_handle_get_list_of_metadata_block_IDs (handle,
|
||||
"mime",
|
||||
&metadata_id, 1);
|
||||
if (n_metadata > 0)
|
||||
{
|
||||
if (g_strcmp0 (
|
||||
heif_image_handle_get_metadata_content_type (handle, metadata_id),
|
||||
"application/rdf+xml") == 0)
|
||||
{
|
||||
xmp_data_size = heif_image_handle_get_metadata_size (handle,
|
||||
metadata_id);
|
||||
xmp_data = g_alloca (xmp_data_size);
|
||||
err = heif_image_handle_get_metadata (handle, metadata_id, exif_data);
|
||||
if (err.code != 0)
|
||||
{
|
||||
exif_data = NULL;
|
||||
exif_data_size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
err = heif_image_handle_get_metadata (handle, metadata_id, xmp_data);
|
||||
if (err.code != 0)
|
||||
{
|
||||
xmp_data = NULL;
|
||||
xmp_data_size = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
n_metadata = heif_image_handle_get_list_of_metadata_block_IDs (handle, "mime",
|
||||
&metadata_id, 1);
|
||||
if (n_metadata > 0)
|
||||
{
|
||||
if (g_strcmp0 (heif_image_handle_get_metadata_content_type (handle, metadata_id), "application/rdf+xml")
|
||||
== 0)
|
||||
{
|
||||
xmp_data_size = heif_image_handle_get_metadata_size (handle, metadata_id);
|
||||
|
||||
if (exif_data || xmp_data)
|
||||
{
|
||||
PikaMetadata *metadata = pika_metadata_new ();
|
||||
PikaMetadataLoadFlags flags = PIKA_METADATA_LOAD_COMMENT | PIKA_METADATA_LOAD_RESOLUTION;
|
||||
xmp_data = g_alloca (xmp_data_size);
|
||||
|
||||
if (exif_data)
|
||||
{
|
||||
const guint8 tiffHeaderBE[4] = { 'M', 'M', 0, 42 };
|
||||
const guint8 tiffHeaderLE[4] = { 'I', 'I', 42, 0 };
|
||||
GExiv2Metadata *exif_metadata = GEXIV2_METADATA (metadata);
|
||||
const guint8 *tiffheader = exif_data;
|
||||
glong new_exif_size = exif_data_size;
|
||||
err = heif_image_handle_get_metadata (handle, metadata_id, xmp_data);
|
||||
if (err.code != 0)
|
||||
{
|
||||
xmp_data = NULL;
|
||||
xmp_data_size = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (new_exif_size >= 4) /*Searching for TIFF Header*/
|
||||
{
|
||||
if (tiffheader[0] == tiffHeaderBE[0] && tiffheader[1] == tiffHeaderBE[1] &&
|
||||
tiffheader[2] == tiffHeaderBE[2] && tiffheader[3] == tiffHeaderBE[3])
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (tiffheader[0] == tiffHeaderLE[0] && tiffheader[1] == tiffHeaderLE[1] &&
|
||||
tiffheader[2] == tiffHeaderLE[2] && tiffheader[3] == tiffHeaderLE[3])
|
||||
{
|
||||
break;
|
||||
}
|
||||
new_exif_size--;
|
||||
tiffheader++;
|
||||
}
|
||||
if (exif_data || xmp_data)
|
||||
{
|
||||
gexiv2_metadata_clear (GEXIV2_METADATA (metadata));
|
||||
|
||||
if (new_exif_size > 4) /* TIFF header + some data found*/
|
||||
{
|
||||
if (! gexiv2_metadata_open_buf (exif_metadata, tiffheader, new_exif_size, error))
|
||||
{
|
||||
g_printerr ("%s: Failed to set EXIF metadata: %s\n", G_STRFUNC, (*error)->message);
|
||||
g_clear_error (error);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
g_printerr ("%s: EXIF metadata not set\n", G_STRFUNC);
|
||||
}
|
||||
}
|
||||
if (exif_data)
|
||||
{
|
||||
const guint8 tiffHeaderBE[4] = { 'M', 'M', 0, 42 };
|
||||
const guint8 tiffHeaderLE[4] = { 'I', 'I', 42, 0 };
|
||||
GExiv2Metadata *exif_metadata = GEXIV2_METADATA (metadata);
|
||||
const guint8 *tiffheader = exif_data;
|
||||
glong new_exif_size = exif_data_size;
|
||||
|
||||
if (xmp_data)
|
||||
{
|
||||
if (!pika_metadata_set_from_xmp (metadata, xmp_data, xmp_data_size, error))
|
||||
{
|
||||
g_printerr ("%s: Failed to set XMP metadata: %s\n", G_STRFUNC, (*error)->message);
|
||||
g_clear_error (error);
|
||||
}
|
||||
}
|
||||
while (new_exif_size >= 4) /*Searching for TIFF Header*/
|
||||
{
|
||||
if (tiffheader[0] == tiffHeaderBE[0] && tiffheader[1] == tiffHeaderBE[1] &&
|
||||
tiffheader[2] == tiffHeaderBE[2] && tiffheader[3] == tiffHeaderBE[3])
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (tiffheader[0] == tiffHeaderLE[0] && tiffheader[1] == tiffHeaderLE[1] &&
|
||||
tiffheader[2] == tiffHeaderLE[2] && tiffheader[3] == tiffHeaderLE[3])
|
||||
{
|
||||
break;
|
||||
}
|
||||
new_exif_size--;
|
||||
tiffheader++;
|
||||
}
|
||||
|
||||
gexiv2_metadata_try_set_orientation (GEXIV2_METADATA (metadata),
|
||||
GEXIV2_ORIENTATION_NORMAL, NULL);
|
||||
gexiv2_metadata_try_set_metadata_pixel_width (GEXIV2_METADATA (metadata),
|
||||
width, NULL);
|
||||
gexiv2_metadata_try_set_metadata_pixel_height (GEXIV2_METADATA (metadata),
|
||||
height, NULL);
|
||||
pika_image_metadata_load_finish (image, "image/heif",
|
||||
metadata, flags);
|
||||
}
|
||||
}
|
||||
if (new_exif_size > 4) /* TIFF header + some data found*/
|
||||
{
|
||||
if (! gexiv2_metadata_open_buf (exif_metadata, tiffheader, new_exif_size, error))
|
||||
{
|
||||
g_printerr ("%s: Failed to set EXIF metadata: %s\n", G_STRFUNC, (*error)->message);
|
||||
g_clear_error (error);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
g_printerr ("%s: EXIF metadata not set\n", G_STRFUNC);
|
||||
}
|
||||
}
|
||||
|
||||
if (xmp_data)
|
||||
{
|
||||
if (! pika_metadata_set_from_xmp (metadata, xmp_data, xmp_data_size, error))
|
||||
{
|
||||
g_printerr ("%s: Failed to set XMP metadata: %s\n", G_STRFUNC, (*error)->message);
|
||||
g_clear_error (error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gexiv2_metadata_try_set_orientation (GEXIV2_METADATA (metadata),
|
||||
GEXIV2_ORIENTATION_NORMAL, NULL);
|
||||
gexiv2_metadata_try_set_metadata_pixel_width (GEXIV2_METADATA (metadata), width, NULL);
|
||||
gexiv2_metadata_try_set_metadata_pixel_height (GEXIV2_METADATA (metadata),
|
||||
height, NULL);
|
||||
|
||||
*flags = PIKA_METADATA_LOAD_COMMENT | PIKA_METADATA_LOAD_RESOLUTION;
|
||||
}
|
||||
|
||||
if (profile)
|
||||
g_object_unref (profile);
|
||||
|
@ -230,10 +230,10 @@ load_image (GFile *file,
|
||||
{
|
||||
ILBM_Image *true_image = iff_image[i];
|
||||
/* Struct representing bitmap header properties */
|
||||
ILBM_BitMapHeader *bitMapHeader = true_image->bitMapHeader;
|
||||
ILBM_BitMapHeader *bitMapHeader;
|
||||
/* Struct containing the color palette */
|
||||
ILBM_ColorMap *colorMap = true_image->colorMap;
|
||||
ILBM_Viewport *camg = true_image->viewport;
|
||||
ILBM_ColorMap *colorMap;
|
||||
ILBM_Viewport *camg;
|
||||
IFF_UByte *bitplanes;
|
||||
PikaImageType image_type;
|
||||
guchar pika_cmap[768]; /* Max index is (2^nplanes) - 1 */
|
||||
@ -244,29 +244,38 @@ load_image (GFile *file,
|
||||
gint row_length;
|
||||
gint pixel_size = 1;
|
||||
gint y_height = 0;
|
||||
gint aspect_x = 0;
|
||||
gint aspect_y = 0;
|
||||
gboolean ehb_mode = FALSE;
|
||||
gboolean ham_mode = FALSE;
|
||||
|
||||
if (! true_image || ! bitMapHeader)
|
||||
if (! true_image)
|
||||
{
|
||||
g_message (_("Invalid or missing ILBM image"));
|
||||
return image;
|
||||
}
|
||||
if (! true_image->body)
|
||||
{
|
||||
g_message (_("ILBM contains no image data - likely a palette file"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
colorMap = true_image->colorMap;
|
||||
camg = true_image->viewport;
|
||||
|
||||
/* Convert ACBM files to ILBM format */
|
||||
if (ILBM_imageIsACBM (true_image))
|
||||
ILBM_convertACBMToILBM (true_image);
|
||||
|
||||
bitMapHeader = true_image->bitMapHeader;
|
||||
if (! bitMapHeader || ! true_image->body)
|
||||
{
|
||||
g_message (_("ILBM contains no image data - likely a palette file"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
width = bitMapHeader->w;
|
||||
height = bitMapHeader->h;
|
||||
nPlanes = bitMapHeader->nPlanes;
|
||||
row_length = (width + 15) / 16;
|
||||
pixel_size = nPlanes / 8;
|
||||
aspect_x = bitMapHeader->xAspect;
|
||||
aspect_y = bitMapHeader->yAspect;
|
||||
|
||||
/* Check for ILBM variants in CMAG chunk */
|
||||
if (camg)
|
||||
@ -329,8 +338,9 @@ load_image (GFile *file,
|
||||
|
||||
ILBM_unpackByteRun (true_image);
|
||||
|
||||
image = pika_image_new (width, height,
|
||||
pixel_size == 1 ? PIKA_INDEXED : PIKA_RGB);
|
||||
if (! image)
|
||||
image = pika_image_new (width, height,
|
||||
pixel_size == 1 ? PIKA_INDEXED : PIKA_RGB);
|
||||
|
||||
layer = pika_layer_new (image, _("Background"), width, height,
|
||||
image_type, 100,
|
||||
@ -341,6 +351,24 @@ load_image (GFile *file,
|
||||
|
||||
bitplanes = true_image->body->chunkData;
|
||||
|
||||
/* Setting resolution for non-square pixel aspect ratios */
|
||||
if (aspect_x != aspect_y && aspect_x > 0 && aspect_y > 0)
|
||||
{
|
||||
gdouble image_xres;
|
||||
gdouble image_yres;
|
||||
gfloat ratio = (gfloat) aspect_x / aspect_y;
|
||||
|
||||
g_message (_("Non-square pixels. Image might look squashed if "
|
||||
"Dot for Dot mode is enabled."));
|
||||
|
||||
pika_image_get_resolution (image, &image_xres, &image_yres);
|
||||
if (ratio < 1)
|
||||
image_xres = image_yres * (1 / ratio);
|
||||
else
|
||||
image_yres = image_xres * ratio;
|
||||
pika_image_set_resolution (image, image_xres, image_yres);
|
||||
}
|
||||
|
||||
/* Loading rows */
|
||||
for (gint j = 0; j < height; j++)
|
||||
{
|
||||
|
@ -396,9 +396,11 @@ create_cmyk_layer (PikaImage *image,
|
||||
}
|
||||
|
||||
static PikaImage *
|
||||
load_image (GFile *file,
|
||||
PikaRunMode runmode,
|
||||
GError **error)
|
||||
load_image (GFile *file,
|
||||
PikaRunMode runmode,
|
||||
PikaMetadata *metadata,
|
||||
PikaMetadataLoadFlags *flags,
|
||||
GError **error)
|
||||
{
|
||||
FILE *inputFile = g_fopen (g_file_peek_path (file), "rb");
|
||||
|
||||
@ -430,6 +432,7 @@ load_image (GFile *file,
|
||||
const Babl *type;
|
||||
PikaPrecision precision_linear;
|
||||
PikaPrecision precision_non_linear;
|
||||
uint32_t i;
|
||||
|
||||
if (!inputFile)
|
||||
{
|
||||
@ -637,7 +640,7 @@ load_image (GFile *file,
|
||||
}
|
||||
|
||||
/* Check for extra channels */
|
||||
for (gint32 i = 0; i < basicinfo.num_extra_channels; i++)
|
||||
for (i = 0; i < basicinfo.num_extra_channels; i++)
|
||||
{
|
||||
JxlExtraChannelInfo extra;
|
||||
|
||||
@ -936,205 +939,208 @@ load_image (GFile *file,
|
||||
g_object_unref (profile);
|
||||
}
|
||||
|
||||
if (basicinfo.have_container)
|
||||
if (metadata)
|
||||
{
|
||||
JxlDecoderReleaseInput (decoder);
|
||||
JxlDecoderRewind (decoder);
|
||||
if (basicinfo.have_container)
|
||||
{
|
||||
JxlDecoderReleaseInput (decoder);
|
||||
JxlDecoderRewind (decoder);
|
||||
|
||||
if (JxlDecoderSetInput (decoder, memory, inputFileSize) != JXL_DEC_SUCCESS)
|
||||
{
|
||||
g_printerr ("%s: JxlDecoderSetInput failed after JxlDecoderRewind\n", G_STRFUNC);
|
||||
}
|
||||
else
|
||||
{
|
||||
JxlDecoderCloseInput (decoder);
|
||||
if (JxlDecoderSubscribeEvents (decoder, JXL_DEC_BOX) != JXL_DEC_SUCCESS)
|
||||
if (JxlDecoderSetInput (decoder, memory, inputFileSize) != JXL_DEC_SUCCESS)
|
||||
{
|
||||
g_printerr ("%s: JxlDecoderSubscribeEvents for JXL_DEC_BOX failed\n", G_STRFUNC);
|
||||
g_printerr ("%s: JxlDecoderSetInput failed after JxlDecoderRewind\n", G_STRFUNC);
|
||||
}
|
||||
else
|
||||
{
|
||||
gboolean search_exif = TRUE;
|
||||
gboolean search_xmp = TRUE;
|
||||
gboolean success_exif = FALSE;
|
||||
gboolean success_xmp = FALSE;
|
||||
JxlBoxType box_type = { 0, 0, 0, 0 };
|
||||
GByteArray *exif_box = NULL;
|
||||
GByteArray *xml_box = NULL;
|
||||
size_t exif_remains = 0;
|
||||
size_t xml_remains = 0;
|
||||
|
||||
while (search_exif || search_xmp)
|
||||
JxlDecoderCloseInput (decoder);
|
||||
if (JxlDecoderSubscribeEvents (decoder, JXL_DEC_BOX) != JXL_DEC_SUCCESS)
|
||||
{
|
||||
status = JxlDecoderProcessInput (decoder);
|
||||
switch (status)
|
||||
g_printerr ("%s: JxlDecoderSubscribeEvents for JXL_DEC_BOX failed\n", G_STRFUNC);
|
||||
}
|
||||
else
|
||||
{
|
||||
gboolean search_exif = TRUE;
|
||||
gboolean search_xmp = TRUE;
|
||||
gboolean success_exif = FALSE;
|
||||
gboolean success_xmp = FALSE;
|
||||
JxlBoxType box_type = { 0, 0, 0, 0 };
|
||||
GByteArray *exif_box = NULL;
|
||||
GByteArray *xml_box = NULL;
|
||||
size_t exif_remains = 0;
|
||||
size_t xml_remains = 0;
|
||||
|
||||
while (search_exif || search_xmp)
|
||||
{
|
||||
case JXL_DEC_SUCCESS:
|
||||
if (box_type[0] == 'E' && box_type[1] == 'x' && box_type[2] == 'i' && box_type[3] == 'f' && search_exif)
|
||||
{
|
||||
exif_remains = JxlDecoderReleaseBoxBuffer (decoder);
|
||||
g_byte_array_set_size (exif_box, exif_box->len - exif_remains);
|
||||
success_exif = TRUE;
|
||||
}
|
||||
else if (box_type[0] == 'x' && box_type[1] == 'm' && box_type[2] == 'l' && box_type[3] == ' ' && search_xmp)
|
||||
{
|
||||
xml_remains = JxlDecoderReleaseBoxBuffer (decoder);
|
||||
g_byte_array_set_size (xml_box, xml_box->len - xml_remains);
|
||||
success_xmp = TRUE;
|
||||
}
|
||||
|
||||
search_exif = FALSE;
|
||||
search_xmp = FALSE;
|
||||
break;
|
||||
case JXL_DEC_ERROR:
|
||||
search_exif = FALSE;
|
||||
search_xmp = FALSE;
|
||||
g_printerr ("%s: Metadata decoding error\n", G_STRFUNC);
|
||||
break;
|
||||
case JXL_DEC_NEED_MORE_INPUT:
|
||||
search_exif = FALSE;
|
||||
search_xmp = FALSE;
|
||||
g_printerr ("%s: JXL metadata are probably incomplete\n", G_STRFUNC);
|
||||
break;
|
||||
case JXL_DEC_BOX:
|
||||
JxlDecoderSetDecompressBoxes (decoder, JXL_TRUE);
|
||||
|
||||
if (box_type[0] == 'E' && box_type[1] == 'x' && box_type[2] == 'i' && box_type[3] == 'f' && search_exif && exif_box)
|
||||
{
|
||||
exif_remains = JxlDecoderReleaseBoxBuffer (decoder);
|
||||
g_byte_array_set_size (exif_box, exif_box->len - exif_remains);
|
||||
|
||||
search_exif = FALSE;
|
||||
success_exif = TRUE;
|
||||
}
|
||||
else if (box_type[0] == 'x' && box_type[1] == 'm' && box_type[2] == 'l' && box_type[3] == ' ' && search_xmp && xml_box)
|
||||
{
|
||||
xml_remains = JxlDecoderReleaseBoxBuffer (decoder);
|
||||
g_byte_array_set_size (xml_box, xml_box->len - xml_remains);
|
||||
|
||||
search_xmp = FALSE;
|
||||
success_xmp = TRUE;
|
||||
}
|
||||
|
||||
if (JxlDecoderGetBoxType (decoder, box_type, JXL_TRUE) == JXL_DEC_SUCCESS)
|
||||
status = JxlDecoderProcessInput (decoder);
|
||||
switch (status)
|
||||
{
|
||||
case JXL_DEC_SUCCESS:
|
||||
if (box_type[0] == 'E' && box_type[1] == 'x' && box_type[2] == 'i' && box_type[3] == 'f' && search_exif)
|
||||
{
|
||||
exif_box = g_byte_array_sized_new (4096);
|
||||
g_byte_array_set_size (exif_box, 4096);
|
||||
|
||||
JxlDecoderSetBoxBuffer (decoder, exif_box->data, exif_box->len);
|
||||
exif_remains = JxlDecoderReleaseBoxBuffer (decoder);
|
||||
g_byte_array_set_size (exif_box, exif_box->len - exif_remains);
|
||||
success_exif = TRUE;
|
||||
}
|
||||
else if (box_type[0] == 'x' && box_type[1] == 'm' && box_type[2] == 'l' && box_type[3] == ' ' && search_xmp)
|
||||
{
|
||||
xml_box = g_byte_array_sized_new (4096);
|
||||
g_byte_array_set_size (xml_box, 4096);
|
||||
|
||||
JxlDecoderSetBoxBuffer (decoder, xml_box->data, xml_box->len);
|
||||
xml_remains = JxlDecoderReleaseBoxBuffer (decoder);
|
||||
g_byte_array_set_size (xml_box, xml_box->len - xml_remains);
|
||||
success_xmp = TRUE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
search_exif = FALSE;
|
||||
search_xmp = FALSE;
|
||||
g_printerr ("%s: Error in JxlDecoderGetBoxType\n", G_STRFUNC);
|
||||
}
|
||||
break;
|
||||
case JXL_DEC_BOX_NEED_MORE_OUTPUT:
|
||||
if (box_type[0] == 'E' && box_type[1] == 'x' && box_type[2] == 'i' && box_type[3] == 'f' && search_exif)
|
||||
{
|
||||
exif_remains = JxlDecoderReleaseBoxBuffer (decoder);
|
||||
g_byte_array_set_size (exif_box, exif_box->len + 4096);
|
||||
JxlDecoderSetBoxBuffer (decoder, exif_box->data + exif_box->len - (4096 + exif_remains), 4096 + exif_remains);
|
||||
}
|
||||
else if (box_type[0] == 'x' && box_type[1] == 'm' && box_type[2] == 'l' && box_type[3] == ' ' && search_xmp)
|
||||
{
|
||||
xml_remains = JxlDecoderReleaseBoxBuffer (decoder);
|
||||
g_byte_array_set_size (xml_box, xml_box->len + 4096);
|
||||
JxlDecoderSetBoxBuffer (decoder, xml_box->data + xml_box->len - (4096 + xml_remains), 4096 + xml_remains);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
case JXL_DEC_ERROR:
|
||||
search_exif = FALSE;
|
||||
search_xmp = FALSE;
|
||||
g_printerr ("%s: Metadata decoding error\n", G_STRFUNC);
|
||||
break;
|
||||
case JXL_DEC_NEED_MORE_INPUT:
|
||||
search_exif = FALSE;
|
||||
search_xmp = FALSE;
|
||||
g_printerr ("%s: JXL metadata are probably incomplete\n", G_STRFUNC);
|
||||
break;
|
||||
case JXL_DEC_BOX:
|
||||
JxlDecoderSetDecompressBoxes (decoder, JXL_TRUE);
|
||||
|
||||
if (box_type[0] == 'E' && box_type[1] == 'x' && box_type[2] == 'i' && box_type[3] == 'f' && search_exif && exif_box)
|
||||
{
|
||||
exif_remains = JxlDecoderReleaseBoxBuffer (decoder);
|
||||
g_byte_array_set_size (exif_box, exif_box->len - exif_remains);
|
||||
|
||||
search_exif = FALSE;
|
||||
success_exif = TRUE;
|
||||
}
|
||||
else if (box_type[0] == 'x' && box_type[1] == 'm' && box_type[2] == 'l' && box_type[3] == ' ' && search_xmp && xml_box)
|
||||
{
|
||||
xml_remains = JxlDecoderReleaseBoxBuffer (decoder);
|
||||
g_byte_array_set_size (xml_box, xml_box->len - xml_remains);
|
||||
|
||||
search_xmp = FALSE;
|
||||
success_xmp = TRUE;
|
||||
}
|
||||
|
||||
if (JxlDecoderGetBoxType (decoder, box_type, JXL_TRUE) == JXL_DEC_SUCCESS)
|
||||
{
|
||||
if (box_type[0] == 'E' && box_type[1] == 'x' && box_type[2] == 'i' && box_type[3] == 'f' && search_exif)
|
||||
{
|
||||
exif_box = g_byte_array_sized_new (4096);
|
||||
g_byte_array_set_size (exif_box, 4096);
|
||||
|
||||
JxlDecoderSetBoxBuffer (decoder, exif_box->data, exif_box->len);
|
||||
}
|
||||
else if (box_type[0] == 'x' && box_type[1] == 'm' && box_type[2] == 'l' && box_type[3] == ' ' && search_xmp)
|
||||
{
|
||||
xml_box = g_byte_array_sized_new (4096);
|
||||
g_byte_array_set_size (xml_box, 4096);
|
||||
|
||||
JxlDecoderSetBoxBuffer (decoder, xml_box->data, xml_box->len);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
search_exif = FALSE;
|
||||
search_xmp = FALSE;
|
||||
g_printerr ("%s: Error in JxlDecoderGetBoxType\n", G_STRFUNC);
|
||||
}
|
||||
break;
|
||||
case JXL_DEC_BOX_NEED_MORE_OUTPUT:
|
||||
if (box_type[0] == 'E' && box_type[1] == 'x' && box_type[2] == 'i' && box_type[3] == 'f' && search_exif)
|
||||
{
|
||||
exif_remains = JxlDecoderReleaseBoxBuffer (decoder);
|
||||
g_byte_array_set_size (exif_box, exif_box->len + 4096);
|
||||
JxlDecoderSetBoxBuffer (decoder, exif_box->data + exif_box->len - (4096 + exif_remains), 4096 + exif_remains);
|
||||
}
|
||||
else if (box_type[0] == 'x' && box_type[1] == 'm' && box_type[2] == 'l' && box_type[3] == ' ' && search_xmp)
|
||||
{
|
||||
xml_remains = JxlDecoderReleaseBoxBuffer (decoder);
|
||||
g_byte_array_set_size (xml_box, xml_box->len + 4096);
|
||||
JxlDecoderSetBoxBuffer (decoder, xml_box->data + xml_box->len - (4096 + xml_remains), 4096 + xml_remains);
|
||||
}
|
||||
else
|
||||
{
|
||||
search_exif = FALSE;
|
||||
search_xmp = FALSE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (success_exif || success_xmp)
|
||||
{
|
||||
PikaMetadata *metadata = pika_metadata_new ();
|
||||
|
||||
if (success_exif && exif_box)
|
||||
if (success_exif || success_xmp)
|
||||
{
|
||||
const guint8 tiffHeaderBE[4] = { 'M', 'M', 0, 42 };
|
||||
const guint8 tiffHeaderLE[4] = { 'I', 'I', 42, 0 };
|
||||
const guint8 *tiffheader = exif_box->data;
|
||||
glong new_exif_size = exif_box->len;
|
||||
gexiv2_metadata_clear (GEXIV2_METADATA (metadata));
|
||||
|
||||
while (new_exif_size >= 4) /*Searching for TIFF Header*/
|
||||
if (success_exif && exif_box)
|
||||
{
|
||||
if (tiffheader[0] == tiffHeaderBE[0] && tiffheader[1] == tiffHeaderBE[1] &&
|
||||
tiffheader[2] == tiffHeaderBE[2] && tiffheader[3] == tiffHeaderBE[3])
|
||||
const guint8 tiffHeaderBE[4] = { 'M', 'M', 0, 42 };
|
||||
const guint8 tiffHeaderLE[4] = { 'I', 'I', 42, 0 };
|
||||
const guint8 *tiffheader = exif_box->data;
|
||||
glong new_exif_size = exif_box->len;
|
||||
|
||||
while (new_exif_size >= 4) /*Searching for TIFF Header*/
|
||||
{
|
||||
break;
|
||||
if (tiffheader[0] == tiffHeaderBE[0] && tiffheader[1] == tiffHeaderBE[1] &&
|
||||
tiffheader[2] == tiffHeaderBE[2] && tiffheader[3] == tiffHeaderBE[3])
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (tiffheader[0] == tiffHeaderLE[0] && tiffheader[1] == tiffHeaderLE[1] &&
|
||||
tiffheader[2] == tiffHeaderLE[2] && tiffheader[3] == tiffHeaderLE[3])
|
||||
{
|
||||
break;
|
||||
}
|
||||
new_exif_size--;
|
||||
tiffheader++;
|
||||
}
|
||||
if (tiffheader[0] == tiffHeaderLE[0] && tiffheader[1] == tiffHeaderLE[1] &&
|
||||
tiffheader[2] == tiffHeaderLE[2] && tiffheader[3] == tiffHeaderLE[3])
|
||||
|
||||
if (new_exif_size > 4) /* TIFF header + some data found*/
|
||||
{
|
||||
break;
|
||||
if (! gexiv2_metadata_open_buf (GEXIV2_METADATA (metadata), tiffheader, new_exif_size, error))
|
||||
{
|
||||
g_printerr ("%s: Failed to set EXIF metadata: %s\n", G_STRFUNC, (*error)->message);
|
||||
g_clear_error (error);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
g_printerr ("%s: EXIF metadata not set\n", G_STRFUNC);
|
||||
}
|
||||
new_exif_size--;
|
||||
tiffheader++;
|
||||
}
|
||||
|
||||
if (new_exif_size > 4) /* TIFF header + some data found*/
|
||||
if (success_xmp && xml_box)
|
||||
{
|
||||
if (! gexiv2_metadata_open_buf (GEXIV2_METADATA (metadata), tiffheader, new_exif_size, error))
|
||||
if (! pika_metadata_set_from_xmp (metadata, xml_box->data, xml_box->len, error))
|
||||
{
|
||||
g_printerr ("%s: Failed to set EXIF metadata: %s\n", G_STRFUNC, (*error)->message);
|
||||
g_printerr ("%s: Failed to set XMP metadata: %s\n", G_STRFUNC, (*error)->message);
|
||||
g_clear_error (error);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
g_printerr ("%s: EXIF metadata not set\n", G_STRFUNC);
|
||||
}
|
||||
}
|
||||
|
||||
if (success_xmp && xml_box)
|
||||
if (exif_box)
|
||||
{
|
||||
if (! pika_metadata_set_from_xmp (metadata, xml_box->data, xml_box->len, error))
|
||||
{
|
||||
g_printerr ("%s: Failed to set XMP metadata: %s\n", G_STRFUNC, (*error)->message);
|
||||
g_clear_error (error);
|
||||
}
|
||||
g_byte_array_free (exif_box, TRUE);
|
||||
}
|
||||
|
||||
gexiv2_metadata_try_set_orientation (GEXIV2_METADATA (metadata),
|
||||
GEXIV2_ORIENTATION_NORMAL, NULL);
|
||||
gexiv2_metadata_try_set_metadata_pixel_width (GEXIV2_METADATA (metadata),
|
||||
basicinfo.xsize, NULL);
|
||||
gexiv2_metadata_try_set_metadata_pixel_height (GEXIV2_METADATA (metadata),
|
||||
basicinfo.ysize, NULL);
|
||||
pika_image_metadata_load_finish (image, "image/jxl", metadata,
|
||||
PIKA_METADATA_LOAD_COMMENT | PIKA_METADATA_LOAD_RESOLUTION);
|
||||
}
|
||||
|
||||
if (exif_box)
|
||||
{
|
||||
g_byte_array_free (exif_box, TRUE);
|
||||
}
|
||||
|
||||
if (xml_box)
|
||||
{
|
||||
g_byte_array_free (xml_box, TRUE);
|
||||
if (xml_box)
|
||||
{
|
||||
g_byte_array_free (xml_box, TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gexiv2_metadata_try_set_orientation (GEXIV2_METADATA (metadata),
|
||||
GEXIV2_ORIENTATION_NORMAL, NULL);
|
||||
gexiv2_metadata_try_set_metadata_pixel_width (GEXIV2_METADATA (metadata),
|
||||
basicinfo.xsize, NULL);
|
||||
gexiv2_metadata_try_set_metadata_pixel_height (GEXIV2_METADATA (metadata),
|
||||
basicinfo.ysize, NULL);
|
||||
|
||||
*flags = PIKA_METADATA_LOAD_COMMENT | PIKA_METADATA_LOAD_RESOLUTION;
|
||||
}
|
||||
|
||||
JxlThreadParallelRunnerDestroy (runner);
|
||||
@ -1158,7 +1164,7 @@ jpegxl_load (PikaProcedure *procedure,
|
||||
|
||||
gegl_init (NULL, NULL);
|
||||
|
||||
image = load_image (file, run_mode, &error);
|
||||
image = load_image (file, run_mode, metadata, flags, &error);
|
||||
|
||||
if (! image)
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
|
@ -886,12 +886,13 @@ gui_single (PikaProcedure *procedure,
|
||||
PikaProcedureConfig *config,
|
||||
PikaImage *image)
|
||||
{
|
||||
GtkWidget *window;
|
||||
GtkWidget *widget;
|
||||
GList *missing_fonts;
|
||||
GList *dialog_props = NULL;
|
||||
gboolean run;
|
||||
gint32 n_layers;
|
||||
GtkWidget *window;
|
||||
GtkWidget *widget;
|
||||
PikaLayer **layers;
|
||||
GList *missing_fonts;
|
||||
GList *dialog_props = NULL;
|
||||
gboolean run;
|
||||
gint32 n_layers;
|
||||
|
||||
pika_ui_init (PLUG_IN_BINARY);
|
||||
|
||||
@ -910,7 +911,13 @@ gui_single (PikaProcedure *procedure,
|
||||
widget = pika_procedure_dialog_fill_frame (PIKA_PROCEDURE_DIALOG (window),
|
||||
"pages-frame", "layers-as-pages", FALSE,
|
||||
"pages-box");
|
||||
g_free (pika_image_get_layers (multi_page.images[0], &n_layers));
|
||||
/* Enable "layers-as-pages" if more than one layer, or there's a single
|
||||
* layer group has more than one layer */
|
||||
layers = pika_image_get_layers (multi_page.images[0], &n_layers);
|
||||
if (n_layers == 1 && pika_item_is_group (PIKA_ITEM (layers[0])))
|
||||
g_free (pika_item_get_children (PIKA_ITEM (layers[0]), &n_layers));
|
||||
g_free (layers);
|
||||
|
||||
gtk_widget_set_sensitive (widget, n_layers > 1);
|
||||
|
||||
/* Warning for missing fonts (non-embeddable with rasterization
|
||||
|
@ -65,6 +65,7 @@ typedef enum _PngExportformat
|
||||
PNG_FORMAT_GRAYA16
|
||||
} PngExportFormat;
|
||||
|
||||
static GSList *safe_to_copy_chunks;
|
||||
|
||||
typedef struct _Png Png;
|
||||
typedef struct _PngClass PngClass;
|
||||
@ -139,6 +140,9 @@ static gboolean ia_has_transparent_pixels (GeglBuffer *buffer);
|
||||
static gint find_unused_ia_color (GeglBuffer *buffer,
|
||||
gint *colors);
|
||||
|
||||
static gint read_unknown_chunk (png_structp png_ptr,
|
||||
png_unknown_chunkp chunk);
|
||||
|
||||
|
||||
G_DEFINE_TYPE (Png, png, PIKA_TYPE_PLUG_IN)
|
||||
|
||||
@ -602,6 +606,7 @@ load_image (GFile *file,
|
||||
const Babl *file_format; /* BABL format for layer */
|
||||
png_structp pp; /* PNG read pointer */
|
||||
png_infop info; /* PNG info pointers */
|
||||
png_voidp user_chunkp; /* PNG unknown chunk pointer */
|
||||
guchar **pixels; /* Pixel rows */
|
||||
guchar *pixel; /* Pixel data */
|
||||
guchar alpha[256]; /* Index -> Alpha */
|
||||
@ -609,6 +614,8 @@ load_image (GFile *file,
|
||||
gint num_texts;
|
||||
struct read_error_data error_data;
|
||||
|
||||
safe_to_copy_chunks = NULL;
|
||||
|
||||
pp = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
|
||||
if (! pp)
|
||||
{
|
||||
@ -667,6 +674,11 @@ load_image (GFile *file,
|
||||
png_init_io (pp, fp);
|
||||
png_set_compression_buffer_size (pp, 512);
|
||||
|
||||
/* Set up callback to save "safe to copy" chunks */
|
||||
png_set_keep_unknown_chunks (pp, PNG_HANDLE_CHUNK_IF_SAFE, NULL, 0);
|
||||
user_chunkp = png_get_user_chunk_ptr (pp);
|
||||
png_set_read_user_chunk_fn (pp, user_chunkp, read_unknown_chunk);
|
||||
|
||||
/*
|
||||
* Get the image info
|
||||
*/
|
||||
@ -1184,6 +1196,23 @@ load_image (GFile *file,
|
||||
g_object_unref (buffer);
|
||||
}
|
||||
|
||||
/* If any safe-to-copy chunks were saved,
|
||||
* store them in the image as parasite */
|
||||
if (safe_to_copy_chunks)
|
||||
{
|
||||
GSList *iter;
|
||||
|
||||
for (iter = safe_to_copy_chunks; iter; iter = iter->next)
|
||||
{
|
||||
PikaParasite *parasite = iter->data;
|
||||
|
||||
pika_image_attach_parasite ((PikaImage *) image, parasite);
|
||||
pika_parasite_free (parasite);
|
||||
}
|
||||
|
||||
g_slist_free (safe_to_copy_chunks);
|
||||
}
|
||||
|
||||
return (PikaImage *) image;
|
||||
}
|
||||
|
||||
@ -1292,6 +1321,7 @@ save_image (GFile *file,
|
||||
gint num; /* Number of rows to load */
|
||||
FILE *fp; /* File pointer */
|
||||
PikaColorProfile *profile = NULL; /* Color profile */
|
||||
gchar **parasites; /* Safe-to-copy chunks */
|
||||
gboolean out_linear; /* Save linear RGB */
|
||||
GeglBuffer *buffer; /* GEGL buffer for layer */
|
||||
const Babl *file_format = NULL; /* BABL format of file */
|
||||
@ -1914,6 +1944,51 @@ save_image (GFile *file,
|
||||
if (G_BYTE_ORDER == G_LITTLE_ENDIAN)
|
||||
png_set_swap (pp);
|
||||
|
||||
/* Write any safe-to-copy chunks saved from import */
|
||||
parasites = pika_image_get_parasite_list (image);
|
||||
|
||||
if (parasites)
|
||||
{
|
||||
gint count;
|
||||
|
||||
count = g_strv_length (parasites);
|
||||
for (gint i = 0; i < count; i++)
|
||||
{
|
||||
if (strncmp (parasites[i], "png", 3) == 0)
|
||||
{
|
||||
PikaParasite *parasite;
|
||||
|
||||
parasite = pika_image_get_parasite (image, parasites[i]);
|
||||
|
||||
if (parasite)
|
||||
{
|
||||
gchar buf[1024];
|
||||
gchar *chunk_name;
|
||||
|
||||
g_strlcpy (buf, parasites[i], sizeof (buf));
|
||||
chunk_name = strchr (buf, '/');
|
||||
chunk_name++;
|
||||
|
||||
if (chunk_name)
|
||||
{
|
||||
png_byte name[4];
|
||||
const guint8 *data;
|
||||
guint32 len;
|
||||
|
||||
for (gint j = 0; j < 4; j++)
|
||||
name[j] = chunk_name[j];
|
||||
|
||||
data = (const guint8 *) pika_parasite_get_data (parasite, &len);
|
||||
|
||||
png_write_chunk (pp, name, data, len);
|
||||
}
|
||||
pika_parasite_free (parasite);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
g_strfreev (parasites);
|
||||
|
||||
/*
|
||||
* Turn on interlace handling...
|
||||
*/
|
||||
@ -2263,6 +2338,28 @@ respin_cmap (png_structp pp,
|
||||
return get_bit_depth_for_palette (colors);
|
||||
}
|
||||
|
||||
static gint
|
||||
read_unknown_chunk (png_structp png_ptr,
|
||||
png_unknown_chunkp chunk)
|
||||
{
|
||||
/* Chunks with a lowercase letter in the 4th byte
|
||||
* are safe to copy */
|
||||
if (g_ascii_islower (chunk->name[3]))
|
||||
{
|
||||
PikaParasite *parasite;
|
||||
gchar pname[255];
|
||||
|
||||
g_snprintf (pname, sizeof (pname), "png/%s", chunk->name);
|
||||
|
||||
if ((parasite = pika_parasite_new (pname,
|
||||
PIKA_PARASITE_PERSISTENT,
|
||||
chunk->size, chunk->data)))
|
||||
safe_to_copy_chunks = g_slist_prepend (safe_to_copy_chunks, parasite);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
save_dialog (PikaImage *image,
|
||||
PikaProcedure *procedure,
|
||||
|
@ -944,7 +944,7 @@ process_pam_header (PNMScanner *scan,
|
||||
{
|
||||
/* skip unknown headers but recognize xv's thumbnail format */
|
||||
CHECK_FOR_ERROR (g_ascii_isdigit (*buf), pnminfo->jmpbuf,
|
||||
_("PAM: Unsupported inofficial PNM variant."));
|
||||
_("PAM: Unsupported unofficial PNM variant."));
|
||||
}
|
||||
}
|
||||
CHECK_FOR_ERROR (pnmscanner_eof (scan), pnminfo->jmpbuf,
|
||||
|
@ -195,7 +195,7 @@ foreach plugin : common_plugins_list
|
||||
copy: true,
|
||||
)
|
||||
|
||||
# See https://gitlab.gnome.org/GNOME/pika/-/issues/8537
|
||||
# See https://gitlab.gnome.org/GNOME/gimp/-/issues/8537
|
||||
if generate_version_h
|
||||
compile_resources_depfiles = []
|
||||
compile_resources_depends = [ gitversion_h ]
|
||||
|
@ -77,7 +77,7 @@ struct _LicClass
|
||||
|
||||
|
||||
#define LIC_TYPE (lic_get_type ())
|
||||
#define LIC (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), LIC_TYPE, Lic))
|
||||
#define LIC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), LIC_TYPE, Lic))
|
||||
|
||||
GType lic_get_type (void) G_GNUC_CONST;
|
||||
|
||||
|
@ -381,6 +381,7 @@ load_image (GFile *file,
|
||||
_("Unsupported compression (%u) in BMP file from '%s'"),
|
||||
bitmap_head.biCompr,
|
||||
pika_file_get_utf8_name (file));
|
||||
goto out;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
|
@ -42,10 +42,6 @@
|
||||
#define LOAD_PROC "file-dds-load"
|
||||
#define SAVE_PROC "file-dds-save"
|
||||
|
||||
#define DECODE_YCOCG_PROC "color-decode-ycocg"
|
||||
#define DECODE_YCOCG_SCALED_PROC "color-decode-ycocg-scaled"
|
||||
#define DECODE_ALPHA_EXP_PROC "color-decode-alpha-exp"
|
||||
|
||||
|
||||
typedef struct _Dds Dds;
|
||||
typedef struct _DdsClass DdsClass;
|
||||
@ -86,15 +82,6 @@ static PikaValueArray * dds_save (PikaProcedure *procedure,
|
||||
PikaMetadata *metadata,
|
||||
PikaProcedureConfig *config,
|
||||
gpointer run_data);
|
||||
#if 0
|
||||
static PikaValueArray * dds_decode (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
PikaProcedureConfig *config,
|
||||
gpointer run_data);
|
||||
#endif
|
||||
|
||||
|
||||
G_DEFINE_TYPE (Dds, dds, PIKA_TYPE_PLUG_IN)
|
||||
@ -125,11 +112,6 @@ dds_query_procedures (PikaPlugIn *plug_in)
|
||||
|
||||
list = g_list_append (list, g_strdup (LOAD_PROC));
|
||||
list = g_list_append (list, g_strdup (SAVE_PROC));
|
||||
#if 0
|
||||
list = g_list_append (list, g_strdup (DECODE_YCOCG_PROC));
|
||||
list = g_list_append (list, g_strdup (DECODE_YCOCG_SCALED_PROC));
|
||||
list = g_list_append (list, g_strdup (DECODE_ALPHA_EXP_PROC));
|
||||
#endif
|
||||
|
||||
return list;
|
||||
}
|
||||
@ -170,10 +152,10 @@ dds_create_procedure (PikaPlugIn *plug_in,
|
||||
TRUE,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_BOOLEAN (procedure, "decode-images",
|
||||
_("Automatically decode YCoCg/AE_xp images when detected"),
|
||||
_("Decode YCoCg/AExp images when detected"),
|
||||
TRUE,
|
||||
PIKA_PROC_ARG_BOOLEAN (procedure, "flip-image",
|
||||
_("Flip image _vertically"),
|
||||
_("Flip the image vertically on import"),
|
||||
FALSE,
|
||||
G_PARAM_READWRITE);
|
||||
}
|
||||
else if (! strcmp (name, SAVE_PROC))
|
||||
@ -249,13 +231,17 @@ dds_create_procedure (PikaPlugIn *plug_in,
|
||||
"default",
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "save-type",
|
||||
"Save type",
|
||||
"How to save the image (0 = selected layer, "
|
||||
"1 = cube map, 2 = volume map, 3 = texture array, "
|
||||
"4 = all visible layers)",
|
||||
0, 4, DDS_SAVE_SELECTED_LAYER,
|
||||
G_PARAM_READWRITE);
|
||||
PIKA_PROC_ARG_CHOICE (procedure, "save-type",
|
||||
_("Sav_e type"),
|
||||
_("How to save the image"),
|
||||
pika_choice_new_with_values ("layer", DDS_SAVE_SELECTED_LAYER, _("Selected layer"), NULL,
|
||||
"canvas", DDS_SAVE_VISIBLE_LAYERS, _("All visible layers"), NULL,
|
||||
"cube", DDS_SAVE_CUBEMAP, _("As cube map"), NULL,
|
||||
"volume", DDS_SAVE_VOLUMEMAP, _("As volume map"), NULL,
|
||||
"array", DDS_SAVE_ARRAY, _("As texture array"), NULL,
|
||||
NULL),
|
||||
"layer",
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_BOOLEAN (procedure, "flip-image",
|
||||
_("Flip image _vertically on export"),
|
||||
@ -276,26 +262,29 @@ dds_create_procedure (PikaPlugIn *plug_in,
|
||||
0, 255, 0,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "mipmaps",
|
||||
"Mipmaps",
|
||||
"How to handle mipmaps (0 = No mipmaps, "
|
||||
"1 = Generate mipmaps, "
|
||||
"2 = Use existing mipmaps (layers)",
|
||||
0, 2, DDS_MIPMAP_NONE,
|
||||
G_PARAM_READWRITE);
|
||||
PIKA_PROC_ARG_CHOICE (procedure, "mipmaps",
|
||||
_("_Mipmaps"),
|
||||
_("How to handle mipmaps"),
|
||||
pika_choice_new_with_values ("none", DDS_MIPMAP_NONE, _("No mipmaps"), NULL,
|
||||
"generate", DDS_MIPMAP_GENERATE, _("Generate mipmaps"), NULL,
|
||||
"existing", DDS_MIPMAP_EXISTING, _("Use existing mipmaps"), NULL,
|
||||
NULL),
|
||||
"none",
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_CHOICE (procedure, "mipmap-filter",
|
||||
_("F_ilter"),
|
||||
_("Filtering to use when generating mipmaps"),
|
||||
pika_choice_new_with_values ("default", DDS_MIPMAP_FILTER_DEFAULT, _("Default"), NULL,
|
||||
"nearest", DDS_MIPMAP_FILTER_NEAREST, _("Nearest"), NULL,
|
||||
"box", DDS_MIPMAP_FILTER_BOX, _("Box"), NULL,
|
||||
"triangle", DDS_MIPMAP_FILTER_TRIANGLE, _("Triangle"), NULL,
|
||||
"quadratic", DDS_MIPMAP_FILTER_QUADRATIC, _("Quadratic"), NULL,
|
||||
"bspline", DDS_MIPMAP_FILTER_BSPLINE, _("B-Spline"), NULL,
|
||||
"mitchell", DDS_MIPMAP_FILTER_MITCHELL, _("Mitchell"), NULL,
|
||||
"lanczos", DDS_MIPMAP_FILTER_LANCZOS, _("Lanczos"), NULL,
|
||||
"kaiser", DDS_MIPMAP_FILTER_KAISER, _("Kaiser"), NULL,
|
||||
pika_choice_new_with_values ("default", DDS_MIPMAP_FILTER_DEFAULT, _("Default"), NULL,
|
||||
"nearest", DDS_MIPMAP_FILTER_NEAREST, _("Nearest"), NULL,
|
||||
"box", DDS_MIPMAP_FILTER_BOX, _("Box"), NULL,
|
||||
"triangle", DDS_MIPMAP_FILTER_TRIANGLE, _("Triangle"), NULL,
|
||||
"quadratic", DDS_MIPMAP_FILTER_QUADRATIC, _("Quadratic"), NULL,
|
||||
"bspline", DDS_MIPMAP_FILTER_BSPLINE, _("B-Spline"), NULL,
|
||||
"mitchell", DDS_MIPMAP_FILTER_MITCHELL, _("Mitchell"), NULL,
|
||||
"catrom", DDS_MIPMAP_FILTER_CATROM, _("Catmull-Rom"), NULL,
|
||||
"lanczos", DDS_MIPMAP_FILTER_LANCZOS, _("Lanczos"), NULL,
|
||||
"kaiser", DDS_MIPMAP_FILTER_KAISER, _("Kaiser"), NULL,
|
||||
NULL),
|
||||
"default",
|
||||
G_PARAM_READWRITE);
|
||||
@ -343,78 +332,6 @@ dds_create_procedure (PikaPlugIn *plug_in,
|
||||
0.0, 1.0, 0.5,
|
||||
G_PARAM_READWRITE);
|
||||
}
|
||||
#if 0
|
||||
else if (! strcmp (name, DECODE_YCOCG_PROC))
|
||||
{
|
||||
procedure = pika_image_procedure_new (plug_in, name,
|
||||
PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
dds_decode, NULL, NULL);
|
||||
|
||||
pika_procedure_set_image_types (procedure, "RGBA");
|
||||
pika_procedure_set_sensitivity_mask (procedure,
|
||||
PIKA_PROCEDURE_SENSITIVE_DRAWABLE);
|
||||
|
||||
pika_procedure_set_menu_label (procedure, _("Decode YCoCg"));
|
||||
/* pika_procedure_add_menu_path (procedure, "<Image>/Filters/Colors"); */
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
_("Converts YCoCg encoded pixels to RGB"),
|
||||
_("Converts YCoCg encoded pixels to RGB"),
|
||||
name);
|
||||
pika_procedure_set_attribution (procedure,
|
||||
"Shawn Kirst",
|
||||
"Shawn Kirst",
|
||||
"2008");
|
||||
}
|
||||
else if (! strcmp (name, DECODE_YCOCG_SCALED_PROC))
|
||||
{
|
||||
procedure = pika_image_procedure_new (plug_in, name,
|
||||
PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
dds_decode, NULL, NULL);
|
||||
|
||||
pika_procedure_set_image_types (procedure, "RGBA");
|
||||
pika_procedure_set_sensitivity_mask (procedure,
|
||||
PIKA_PROCEDURE_SENSITIVE_DRAWABLE);
|
||||
|
||||
pika_procedure_set_menu_label (procedure, _("Decode YCoCg (scaled)"));
|
||||
/* pika_procedure_add_menu_path (procedure, "<Image>/Filters/Colors"); */
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
_("Converts YCoCg (scaled) encoded "
|
||||
"pixels to RGB"),
|
||||
_("Converts YCoCg (scaled) encoded "
|
||||
"pixels to RGB"),
|
||||
name);
|
||||
pika_procedure_set_attribution (procedure,
|
||||
"Shawn Kirst",
|
||||
"Shawn Kirst",
|
||||
"2008");
|
||||
}
|
||||
else if (! strcmp (name, DECODE_ALPHA_EXP_PROC))
|
||||
{
|
||||
procedure = pika_image_procedure_new (plug_in, name,
|
||||
PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
dds_decode, NULL, NULL);
|
||||
|
||||
pika_procedure_set_image_types (procedure, "RGBA");
|
||||
pika_procedure_set_sensitivity_mask (procedure,
|
||||
PIKA_PROCEDURE_SENSITIVE_DRAWABLE);
|
||||
|
||||
pika_procedure_set_menu_label (procedure, _("Decode Alpha exponent"));
|
||||
/* pika_procedure_add_menu_path (procedure, "<Image>/Filters/Colors"); */
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
_("Converts alpha exponent encoded "
|
||||
"pixels to RGB",
|
||||
_("Converts alpha exponent encoded "
|
||||
"pixels to RGB"),
|
||||
name);
|
||||
pika_procedure_set_attribution (procedure,
|
||||
"Shawn Kirst",
|
||||
"Shawn Kirst",
|
||||
"2008");
|
||||
}
|
||||
#endif
|
||||
|
||||
return procedure;
|
||||
}
|
||||
@ -436,7 +353,7 @@ dds_load (PikaProcedure *procedure,
|
||||
gegl_init (NULL, NULL);
|
||||
|
||||
status = read_dds (file, &image, run_mode == PIKA_RUN_INTERACTIVE,
|
||||
procedure, G_OBJECT (config), &error);
|
||||
procedure, config, &error);
|
||||
|
||||
if (status != PIKA_PDB_SUCCESS)
|
||||
return pika_procedure_new_return_values (procedure, status, error);
|
||||
@ -520,53 +437,3 @@ dds_save (PikaProcedure *procedure,
|
||||
|
||||
return pika_procedure_new_return_values (procedure, status, error);
|
||||
}
|
||||
|
||||
#if 0
|
||||
static PikaValueArray *
|
||||
dds_decode (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
PikaProcedureConfig *config,
|
||||
gpointer run_data)
|
||||
{
|
||||
const gchar *name = pika_procedure_get_name (procedure);
|
||||
PikaDrawable *drawable,
|
||||
|
||||
if (n_drawables != 1)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
g_set_error (&error, PIKA_PLUG_IN_ERROR, 0,
|
||||
_("Procedure '%s' only works with one drawable."),
|
||||
name);
|
||||
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_EXECUTION_ERROR,
|
||||
error);
|
||||
}
|
||||
else
|
||||
{
|
||||
drawable = drawables[0];
|
||||
}
|
||||
|
||||
if (! strcmp (name, DECODE_YCOCG_PROC))
|
||||
{
|
||||
decode_ycocg_image (drawable, TRUE);
|
||||
}
|
||||
else if (! strcmp (name, DECODE_YCOCG_SCALED_PROC))
|
||||
{
|
||||
decode_ycocg_scaled_image (drawable, TRUE);
|
||||
}
|
||||
else if (! strcmp (name, DECODE_ALPHA_EXP_PROC))
|
||||
{
|
||||
decode_alpha_exp_image (drawable, TRUE);
|
||||
}
|
||||
|
||||
if (run_mode != PIKA_RUN_NONINTERACTIVE)
|
||||
pika_displays_flush ();
|
||||
|
||||
return pika_procedure_new_return_values (procedure, PIKA_PDB_SUCCESS, NULL);
|
||||
}
|
||||
#endif
|
||||
|
@ -21,6 +21,7 @@
|
||||
#ifndef __DDS_H__
|
||||
#define __DDS_H__
|
||||
|
||||
|
||||
#define DDS_PLUGIN_VERSION_MAJOR 3
|
||||
#define DDS_PLUGIN_VERSION_MINOR 9
|
||||
#define DDS_PLUGIN_VERSION_REVISION 92
|
||||
@ -101,6 +102,7 @@ typedef enum
|
||||
DDS_MIPMAP_FILTER_QUADRATIC,
|
||||
DDS_MIPMAP_FILTER_BSPLINE,
|
||||
DDS_MIPMAP_FILTER_MITCHELL,
|
||||
DDS_MIPMAP_FILTER_CATROM,
|
||||
DDS_MIPMAP_FILTER_LANCZOS,
|
||||
DDS_MIPMAP_FILTER_KAISER,
|
||||
DDS_MIPMAP_FILTER_MAX
|
||||
@ -115,6 +117,7 @@ typedef enum
|
||||
DDS_MIPMAP_WRAP_MAX
|
||||
} DDS_MIPMAP_WRAP;
|
||||
|
||||
|
||||
#define DDS_HEADERSIZE 128
|
||||
#define DDS_HEADERSIZE_DX10 20
|
||||
|
||||
@ -122,18 +125,43 @@ typedef enum
|
||||
#define DDSD_HEIGHT 0x00000002
|
||||
#define DDSD_WIDTH 0x00000004
|
||||
#define DDSD_PITCH 0x00000008
|
||||
#define DDSD_BACKBUFFERCOUNT 0x00000020
|
||||
#define DDSD_ZBUFFERBITDEPTH 0x00000040
|
||||
#define DDSD_ALPHABITDEPTH 0x00000080
|
||||
#define DDSD_LPSURFACE 0x00000800
|
||||
#define DDSD_PIXELFORMAT 0x00001000
|
||||
#define DDSD_CKDESTOVERLAY 0x00002000
|
||||
#define DDSD_CKDESTBLT 0x00004000
|
||||
#define DDSD_CKSRCOVERLAY 0x00008000
|
||||
#define DDSD_CKSRCBLT 0x00010000
|
||||
#define DDSD_MIPMAPCOUNT 0x00020000
|
||||
#define DDSD_REFRESHRATE 0x00040000
|
||||
#define DDSD_LINEARSIZE 0x00080000
|
||||
#define DDSD_TEXTURESTAGE 0x00100000
|
||||
#define DDSD_FVF 0x00200000
|
||||
#define DDSD_SRCVBHANDLE 0x00400000
|
||||
#define DDSD_DEPTH 0x00800000
|
||||
|
||||
#define DDPF_ALPHAPIXELS 0x00000001
|
||||
#define DDPF_ALPHA 0x00000002
|
||||
#define DDPF_FOURCC 0x00000004
|
||||
#define DDPF_PALETTEINDEXED4 0x00000008
|
||||
#define DDPF_PALETTEINDEXEDTO8 0x00000010
|
||||
#define DDPF_PALETTEINDEXED8 0x00000020
|
||||
#define DDPF_RGB 0x00000040
|
||||
#define DDPF_COMPRESSED 0x00000080
|
||||
#define DDPF_RGBTOYUV 0x00000100
|
||||
#define DDPF_YUV 0x00000200
|
||||
#define DDPF_ZBUFFER 0x00000400
|
||||
#define DDPF_PALETTEINDEXED1 0x00000800
|
||||
#define DDPF_PALETTEINDEXED2 0x00001000
|
||||
#define DDPF_ZPIXELS 0x00002000
|
||||
#define DDPF_STENCILBUFFER 0x00004000
|
||||
#define DDPF_ALPHAPREMULT 0x00008000
|
||||
#define DDPF_LUMINANCE 0x00020000
|
||||
#define DDPF_NORMAL 0x80000000 // nvidia specific
|
||||
#define DDPF_BUMPLUMINANCE 0x00040000
|
||||
#define DDPF_BUMPDUDV 0x00080000
|
||||
#define DDPF_NORMAL 0x80000000 /* NVidia specific */
|
||||
|
||||
#define DDSCAPS_COMPLEX 0x00000008
|
||||
#define DDSCAPS_TEXTURE 0x00001000
|
||||
@ -159,6 +187,7 @@ typedef enum
|
||||
#define D3D10_RESOURCE_DIMENSION_TEXTURE2D 3
|
||||
#define D3D10_RESOURCE_DIMENSION_TEXTURE3D 4
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned int size;
|
||||
@ -192,8 +221,8 @@ typedef struct
|
||||
{
|
||||
struct
|
||||
{
|
||||
unsigned int magic1; // FOURCC "PIKA"
|
||||
unsigned int magic2; // FOURCC "-DDS"
|
||||
unsigned int magic1; /* FOURCC "PIKA" */
|
||||
unsigned int magic2; /* FOURCC "-DDS" */
|
||||
unsigned int version;
|
||||
unsigned int extra_fourcc;
|
||||
} pika_dds_special;
|
||||
@ -204,6 +233,7 @@ typedef struct
|
||||
unsigned int reserved2;
|
||||
} dds_header_t;
|
||||
|
||||
|
||||
typedef enum
|
||||
{
|
||||
DXGI_FORMAT_UNKNOWN = 0,
|
||||
@ -334,4 +364,70 @@ typedef struct
|
||||
unsigned int reserved;
|
||||
} dds_header_dx10_t;
|
||||
|
||||
/* Format values that can be found in the FOURCC field */
|
||||
typedef enum _D3DFORMAT {
|
||||
D3DFMT_R8G8B8 = 20,
|
||||
D3DFMT_A8R8G8B8 = 21,
|
||||
D3DFMT_X8R8G8B8 = 22,
|
||||
D3DFMT_R5G6B5 = 23,
|
||||
D3DFMT_X1R5G5B5 = 24,
|
||||
D3DFMT_A1R5G5B5 = 25,
|
||||
D3DFMT_A4R4G4B4 = 26,
|
||||
D3DFMT_R3G3B2 = 27,
|
||||
D3DFMT_A8 = 28,
|
||||
D3DFMT_A8R3G3B2 = 29,
|
||||
D3DFMT_X4R4G4B4 = 30,
|
||||
D3DFMT_A2B10G10R10 = 31,
|
||||
D3DFMT_A8B8G8R8 = 32,
|
||||
D3DFMT_X8B8G8R8 = 33,
|
||||
D3DFMT_G16R16 = 34,
|
||||
D3DFMT_A2R10G10B10 = 35,
|
||||
D3DFMT_A16B16G16R16 = 36,
|
||||
|
||||
D3DFMT_A8P8 = 40,
|
||||
D3DFMT_P8 = 41,
|
||||
|
||||
D3DFMT_L8 = 50,
|
||||
D3DFMT_A8L8 = 51,
|
||||
D3DFMT_A4L4 = 52,
|
||||
|
||||
D3DFMT_V8U8 = 60,
|
||||
D3DFMT_L6V5U5 = 61,
|
||||
D3DFMT_X8L8V8U8 = 62,
|
||||
D3DFMT_Q8W8V8U8 = 63,
|
||||
D3DFMT_V16U16 = 64,
|
||||
D3DFMT_A2W10V10U10 = 67,
|
||||
|
||||
D3DFMT_D16_LOCKABLE = 70,
|
||||
D3DFMT_D32 = 71,
|
||||
D3DFMT_D15S1 = 73,
|
||||
D3DFMT_D24S8 = 75,
|
||||
D3DFMT_D24X8 = 77,
|
||||
D3DFMT_D24X4S4 = 79,
|
||||
D3DFMT_D16 = 80,
|
||||
D3DFMT_L16 = 81,
|
||||
D3DFMT_D32F_LOCKABLE = 82,
|
||||
D3DFMT_D24FS8 = 83,
|
||||
D3DFMT_D32_LOCKABLE = 84,
|
||||
D3DFMT_S8_LOCKABLE = 85,
|
||||
|
||||
D3DFMT_VERTEXDATA = 100,
|
||||
D3DFMT_INDEX16 = 101,
|
||||
D3DFMT_INDEX32 = 102,
|
||||
|
||||
D3DFMT_Q16W16V16U16 = 110,
|
||||
D3DFMT_R16F = 111,
|
||||
D3DFMT_G16R16F = 112,
|
||||
D3DFMT_A16B16G16R16F = 113,
|
||||
D3DFMT_R32F = 114,
|
||||
D3DFMT_G32R32F = 115,
|
||||
D3DFMT_A32B32G32R32F = 116,
|
||||
D3DFMT_CxV8U8 = 117,
|
||||
|
||||
D3DFMT_A1 = 118,
|
||||
D3DFMT_A2B10G10R10_XR_BIAS = 119,
|
||||
D3DFMT_BINARYBUFFER = 199,
|
||||
} D3DFORMAT;
|
||||
|
||||
|
||||
#endif /* __DDS_H__ */
|
||||
|
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user