[gimp/wip/animation: 63/197] plug-ins: cache panels directly as GEGL buffers.
- From: Jehan Pagès <jehanp src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gimp/wip/animation: 63/197] plug-ins: cache panels directly as GEGL buffers.
- Date: Sat, 7 Oct 2017 03:03:42 +0000 (UTC)
commit 3bd674ee392d650ef307bb8d4c046282a8dad918
Author: Jehan <jehan girinstud io>
Date: Mon Jul 25 01:08:11 2016 +0200
plug-ins: cache panels directly as GEGL buffers.
Do not use an internal GIMP image anymore and do all the dirty work
directly in GEGL graphs.
Also pre-compute updated frames as soon as possible (instead of the last
second when it is needed at playback) as a cache system.
plug-ins/animation-play/core/animationanimatic.c | 348 ++++++++++++----------
1 files changed, 187 insertions(+), 161 deletions(-)
---
diff --git a/plug-ins/animation-play/core/animationanimatic.c
b/plug-ins/animation-play/core/animationanimatic.c
index 34cd338..120b9a9 100644
--- a/plug-ins/animation-play/core/animationanimatic.c
+++ b/plug-ins/animation-play/core/animationanimatic.c
@@ -60,12 +60,10 @@ typedef struct _AnimationAnimaticPrivate AnimationAnimaticPrivate;
struct _AnimationAnimaticPrivate
{
- gint preview_width;
- gint preview_height;
+ gdouble proxy_ratio;
- /* Panel images are associated to an unused image (used as backend
- * for GEGL buffers). */
- gint32 panels;
+ /* Panels are cached as GEGL buffers. */
+ GeglBuffer **cache;
/* The number of panels. */
gint n_panels;
/* Layers associated to each panel. For serialization. */
@@ -126,6 +124,9 @@ static void animation_animatic_text (GMarkupParseContext *context
/* Utils */
+static void animation_animatic_cache (AnimationAnimatic *animation,
+ gint panel,
+ gboolean recursion);
static gint animation_animatic_get_layer (AnimationAnimatic *animation,
gint pos);
@@ -195,8 +196,8 @@ static void
animation_animatic_finalize (GObject *object)
{
AnimationAnimaticPrivate *priv = GET_PRIVATE (object);
+ gint i;
- gimp_image_delete (priv->panels);
if (priv->tattoos)
g_free (priv->tattoos);
if (priv->durations)
@@ -205,14 +206,21 @@ animation_animatic_finalize (GObject *object)
g_free (priv->combine);
if (priv->comments)
{
- gint i;
-
for (i = 0; i < priv->n_panels; i++)
{
g_free (priv->comments[i]);
}
g_free (priv->comments);
}
+ if (priv->cache)
+ {
+ for (i = 0; i < priv->n_panels; i++)
+ {
+ if (priv->cache[i])
+ g_object_unref (priv->cache[i]);
+ }
+ g_free (priv->cache);
+ }
G_OBJECT_CLASS (parent_class)->finalize (object);
}
@@ -320,26 +328,14 @@ animation_animatic_set_combine (AnimationAnimatic *animatic,
gboolean combine)
{
AnimationAnimaticPrivate *priv = GET_PRIVATE (animatic);
- Animation *animation = ANIMATION (animatic);
- gint position;
-
- position = animation_get_position (animation);
g_return_if_fail (panel_num > 0 &&
panel_num <= priv->n_panels);
- priv->combine[panel_num - 1] = combine;
- if (animation_animatic_get_panel (animatic, position) == panel_num)
+ if (priv->combine[panel_num - 1] != combine)
{
- GeglBuffer *buffer;
-
- buffer = animation_get_frame (animation, position);
- g_signal_emit_by_name (animation, "render",
- position, buffer, TRUE);
- if (buffer)
- {
- g_object_unref (buffer);
- }
+ priv->combine[panel_num - 1] = combine;
+ animation_animatic_cache (animatic, panel_num, TRUE);
}
}
@@ -363,8 +359,6 @@ animation_animatic_get_panel (AnimationAnimatic *animation,
gint count = 0;
gint i = -1;
- g_return_val_if_fail (priv->panels, -1);
-
if (pos >= 1 &&
pos <= animation_animatic_get_length (ANIMATION (animation)))
{
@@ -390,7 +384,7 @@ void animation_animatic_jump_panel (AnimationAnimatic *animation,
gint pos = 1;
gint i;
- g_return_if_fail (priv->panels && panel <= priv->n_panels);
+ g_return_if_fail (panel <= priv->n_panels);
for (i = 0; i < panel - 1; i++)
{
@@ -423,9 +417,22 @@ animation_animatic_get_size (Animation *animation,
gint *height)
{
AnimationAnimaticPrivate *priv = GET_PRIVATE (animation);
+ gint32 image_id;
+ gint image_width;
+ gint image_height;
- *width = priv->preview_width;
- *height = priv->preview_height;
+ image_id = animation_get_image_id (animation);
+
+ image_width = gimp_image_width (image_id);
+ image_height = gimp_image_height (image_id);
+
+ /* Full preview size. */
+ *width = image_width;
+ *height = image_height;
+
+ /* Apply proxy ratio. */
+ *width *= priv->proxy_ratio;
+ *height *= priv->proxy_ratio;
}
static void
@@ -435,18 +442,13 @@ animation_animatic_load (Animation *animation,
AnimationAnimaticPrivate *priv = GET_PRIVATE (animation);
gint32 *layers;
gint32 image_id;
- gint32 new_frame;
- gint32 previous_frame = 0;
- gint image_width;
- gint image_height;
gint i;
g_return_if_fail (proxy_ratio > 0.0 && proxy_ratio <= 1.0);
/* Cleaning. */
- if (gimp_image_is_valid (priv->panels))
+ if (priv->cache)
{
- gimp_image_delete (priv->panels);
g_free (priv->tattoos);
g_free (priv->durations);
g_free (priv->combine);
@@ -454,8 +456,11 @@ animation_animatic_load (Animation *animation,
for (i = 0; i < priv->n_panels; i++)
{
g_free (priv->comments[i]);
+ if (priv->cache[i])
+ g_object_unref (priv->cache[i]);
}
g_free (priv->comments);
+ g_free (priv->cache);
}
image_id = animation_get_image_id (animation);
@@ -465,38 +470,22 @@ animation_animatic_load (Animation *animation,
priv->durations = g_try_malloc0_n (priv->n_panels, sizeof (gint));
priv->combine = g_try_malloc0_n (priv->n_panels, sizeof (gboolean));
priv->comments = g_try_malloc0_n (priv->n_panels, sizeof (gchar*));
+ priv->cache = g_try_malloc0_n (priv->n_panels, sizeof (GeglBuffer*));
if (! priv->tattoos || ! priv->durations ||
- ! priv->combine || ! priv->comments)
+ ! priv->combine || ! priv->comments ||
+ ! priv->cache)
{
gimp_message (_("Memory could not be allocated to the animatic."));
gimp_quit ();
return;
}
-
- /* We default at full preview size. */
- image_width = gimp_image_width (image_id);
- image_height = gimp_image_height (image_id);
-
- priv->preview_width = image_width;
- priv->preview_height = image_height;
-
- /* Apply proxy ratio. */
- priv->preview_width *= proxy_ratio;
- priv->preview_height *= proxy_ratio;
-
- priv->panels = gimp_image_new (priv->preview_width,
- priv->preview_width,
- GIMP_RGB);
-
- gimp_image_undo_disable (priv->panels);
+ priv->proxy_ratio = proxy_ratio;
for (i = 0; i < priv->n_panels; i++)
{
- gchar *layer_name;
- gint duration;
- gboolean combine;
- gint layer_offx;
- gint layer_offy;
+ gchar *layer_name;
+ gint duration;
+ gboolean combine;
g_signal_emit_by_name (animation, "loading",
(gdouble) i / ((gdouble) priv->n_panels - 0.999));
@@ -513,31 +502,8 @@ animation_animatic_load (Animation *animation,
/* Layer names are used as default comments. */
priv->comments[i] = layer_name;
- /* Frame disposal. */
- if (i > 0 && combine)
- {
- previous_frame = gimp_layer_copy (previous_frame);
- gimp_image_insert_layer (priv->panels, previous_frame, 0, 0);
- gimp_item_set_visible (previous_frame, TRUE);
- }
-
- new_frame = gimp_layer_new_from_drawable (layers[priv->n_panels - (i + 1)],
- priv->panels);
- gimp_image_insert_layer (priv->panels, new_frame, 0, 0);
- gimp_layer_scale (new_frame,
- (gimp_drawable_width (layers[priv->n_panels - (i + 1)]) * (gint)
priv->preview_width) / image_width,
- (gimp_drawable_height (layers[priv->n_panels - (i + 1)]) * (gint)
priv->preview_height) / image_height,
- FALSE);
- gimp_drawable_offsets (layers[priv->n_panels - (i + 1)], &layer_offx, &layer_offy);
- gimp_layer_set_offsets (new_frame,
- (layer_offx * (gint) priv->preview_width) / image_width,
- (layer_offy * (gint) priv->preview_height) / image_height);
- gimp_layer_resize_to_image_size (new_frame);
- gimp_item_set_visible (new_frame, TRUE);
- new_frame = gimp_image_merge_visible_layers (priv->panels, GIMP_CLIP_TO_IMAGE);
- gimp_item_set_visible (new_frame, FALSE);
-
- previous_frame = new_frame;
+ /* Panel image. */
+ animation_animatic_cache (ANIMATION_ANIMATIC (animation), i + 1, FALSE);
}
g_free (layers);
}
@@ -583,6 +549,7 @@ animation_animatic_load_xml (Animation *animation,
g_warning ("Error parsing XML: %s", error->message);
}
g_markup_parse_context_free (context);
+
/* If XML parsing failed, just reset the animation. */
if (error)
animation_animatic_load (animation, proxy_ratio);
@@ -592,67 +559,13 @@ static GeglBuffer *
animation_animatic_get_frame (Animation *animation,
gint pos)
{
- GeglBuffer *buffer = NULL;
- gint panel;
+ AnimationAnimaticPrivate *priv;
+ gint panel;
+ priv = GET_PRIVATE (animation);
panel = animation_animatic_get_panel (ANIMATION_ANIMATIC (animation),
pos);
- if (panel > 0)
- {
- AnimationAnimaticPrivate *priv;
- GeglBuffer *panel_buffer;
- gint32 *layers;
- gint32 num_layers;
-
- priv = GET_PRIVATE (animation);
- layers = gimp_image_get_layers (priv->panels, &num_layers);
- panel_buffer = gimp_drawable_get_buffer (layers[num_layers - panel]);
-
- if (panel > 1 && priv->combine[panel - 1] &&
- gimp_drawable_has_alpha (layers[num_layers - panel]))
- {
- GeglNode *graph, *backdrop, *source, *blend, *target;
- GeglBuffer *prev_panel_buffer;
-
- prev_panel_buffer = gimp_drawable_get_buffer (layers[num_layers - panel + 1]);
-
- graph = gegl_node_new ();
- backdrop = gegl_node_new_child (graph,
- "operation", "gegl:buffer-source",
- "buffer", prev_panel_buffer,
- NULL);
- source = gegl_node_new_child (graph,
- "operation", "gegl:buffer-source",
- "buffer", panel_buffer,
- NULL);
- blend = gegl_node_new_child (graph,
- "operation", "gegl:over",
- NULL);
-
- target = gegl_node_new_child (graph,
- "operation", "gegl:buffer-sink",
- "buffer", &buffer,
- "format", gegl_buffer_get_format (panel_buffer),
- NULL);
-
- gegl_node_link_many (backdrop, blend, target, NULL);
- gegl_node_connect_to (source, "output",
- blend, "aux");
- gegl_node_process (target);
-
- g_object_unref (graph);
- g_object_unref (panel_buffer);
- g_object_unref (prev_panel_buffer);
- }
- else
- {
- buffer = panel_buffer;
- }
-
- g_free (layers);
- }
-
- return buffer;
+ return g_object_ref (priv->cache[panel - 1]);
}
static gchar *
@@ -757,6 +670,7 @@ animation_animatic_start_element (GMarkupParseContext *context,
const gchar **values = attribute_values;
ParseStatus *status = (ParseStatus *) user_data;
AnimationAnimaticPrivate *priv = GET_PRIVATE (status->animation);
+ gboolean combine;
status->xml_level++;
switch (status->state)
@@ -821,6 +735,7 @@ animation_animatic_start_element (GMarkupParseContext *context,
return;
}
status->panel++;
+ combine = FALSE;
while (*names && *values)
{
if (strcmp (*names, "duration") == 0 && **values)
@@ -834,12 +749,18 @@ animation_animatic_start_element (GMarkupParseContext *context,
strcmp (*values, "normal") == 0)
{
/* Only the "normal" blend mode is supported currently. */
- priv->combine[status->panel - 1] = TRUE;
+ combine = TRUE;
}
names++;
values++;
}
+ if (priv->combine[status->panel - 1] != combine)
+ {
+ priv->combine[status->panel - 1] = combine;
+ animation_animatic_cache (ANIMATION_ANIMATIC (status->animation),
+ status->panel, FALSE);
+ }
status->state = PANEL_STATE;
break;
case PANEL_STATE:
@@ -970,34 +891,139 @@ animation_animatic_text (GMarkupParseContext *context,
/**** Utils ****/
-static gint
-animation_animatic_get_layer (AnimationAnimatic *animation,
- gint pos)
+static void
+animation_animatic_cache (AnimationAnimatic *animatic,
+ gint panel,
+ gboolean recursion)
{
- AnimationAnimaticPrivate *priv = GET_PRIVATE (animation);
- gint i = -1;
+ AnimationAnimaticPrivate *priv = GET_PRIVATE (animatic);
+ Animation *animation = ANIMATION (animatic);
+ GeglBuffer *buffer;
+ GeglNode *graph, *source, *scale, *translate, *target;
+ GeglNode *backdrop, *blend;
+ gint layer_offx;
+ gint layer_offy;
+ gdouble panel_offx;
+ gdouble panel_offy;
+ gint position;
+ gint preview_width;
+ gint preview_height;
+ gint32 image_id;
+ gint32 layer;
- if (priv->panels)
+ image_id = animation_get_image_id (animation);
+ layer = gimp_image_get_layer_by_tattoo (image_id,
+ priv->tattoos[panel - 1]);
+ if (! layer)
{
- gint32 *layers;
- gint32 num_layers;
- gint count = 0;
+ g_warning ("Caching failed: a layer must have been deleted.");
+ return;
+ }
+
+ /* Destroy existing cache. */
+ if (priv->cache[panel - 1])
+ {
+ g_object_unref (priv->cache[panel - 1]);
+ }
+
+ /* Panel image. */
+ buffer = gimp_drawable_get_buffer (layer);
+ animation_get_size (animation, &preview_width, &preview_height);
+ priv->cache[panel - 1] = gegl_buffer_new (GEGL_RECTANGLE (0, 0,
+ preview_width,
+ preview_height),
+ gegl_buffer_get_format (buffer));
+ graph = gegl_node_new ();
+ source = gegl_node_new_child (graph,
+ "operation", "gegl:buffer-source",
+ "buffer", buffer,
+ NULL);
+ scale = gegl_node_new_child (graph,
+ "operation", "gegl:scale-ratio",
+ "sampler", GEGL_SAMPLER_NEAREST,
+ "x", priv->proxy_ratio,
+ "y", priv->proxy_ratio,
+ NULL);
+
+ gimp_drawable_offsets (layer,
+ &layer_offx, &layer_offy);
+ panel_offx = layer_offx * priv->proxy_ratio;
+ panel_offy = layer_offy * priv->proxy_ratio;
+ translate = gegl_node_new_child (graph,
+ "operation", "gegl:translate",
+ "x", panel_offx,
+ "y", panel_offy,
+ NULL);
+
+ target = gegl_node_new_child (graph,
+ "operation", "gegl:write-buffer",
+ "buffer", priv->cache[panel - 1],
+ NULL);
+
+ if (panel > 1 && priv->combine[panel - 1])
+ {
+ backdrop = gegl_node_new_child (graph,
+ "operation", "gegl:buffer-source",
+ "buffer", priv->cache[panel - 2],
+ NULL);
+ blend = gegl_node_new_child (graph,
+ "operation", "gegl:over",
+ NULL);
+ gegl_node_link_many (source, scale, translate, NULL);
+ gegl_node_link_many (backdrop, blend, target, NULL);
+ gegl_node_connect_to (translate, "output",
+ blend, "aux");
+ }
+ else
+ {
+ gegl_node_link_many (source, scale, translate, target, NULL);
+ }
+ gegl_node_process (target);
+
+ g_object_unref (graph);
+ g_object_unref (buffer);
- layers = gimp_image_get_layers (priv->panels, &num_layers);
+ /* If next panel is in "combine" mode, it must also be re-cached.
+ * And so on, recursively. */
+ if (recursion &&
+ panel < priv->n_panels &&
+ priv->combine[panel])
+ {
+ animation_animatic_cache (animatic, panel + 1, TRUE);
+ }
- if (num_layers > 0 &&
- pos >= 1 &&
- pos <= animation_animatic_get_length (ANIMATION (animation)))
+ /* Finally re-render if we are currently showing this panel. */
+ position = animation_get_position (animation);
+ if (animation_animatic_get_panel (animatic, position) == panel)
+ {
+ buffer = animation_get_frame (animation, position);
+ g_signal_emit_by_name (animation, "render",
+ position, buffer, TRUE);
+ if (buffer)
{
- for (i = num_layers - 1; i >= 0; i--)
- {
- count += priv->durations[i];
- if (count >= pos)
- break;
- }
+ g_object_unref (buffer);
}
+ }
+}
- g_free (layers);
+static gint
+animation_animatic_get_layer (AnimationAnimatic *animation,
+ gint pos)
+{
+ AnimationAnimaticPrivate *priv = GET_PRIVATE (animation);
+ gint count = 0;
+ gint i = -1;
+
+ if (priv->n_panels > 0 &&
+ pos >= 1 &&
+ pos <= animation_animatic_get_length (ANIMATION (animation)))
+ {
+ for (i = priv->n_panels - 1; i >= 0; i--)
+ {
+ count += priv->durations[i];
+ if (count >= pos)
+ break;
+ }
}
return i;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]