summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSøren Sandmann Pedersen <ssp@redhat.com>2012-10-04 16:22:47 -0400
committerSøren Sandmann Pedersen <ssp@redhat.com>2012-10-04 16:22:47 -0400
commit7707f3487b16a49b74e700a2e4c2ba8f33afa6c3 (patch)
tree6bfdd3880faf0487bbe6d098ceefb41f5d31197b
parent0ead8e3023b8e3c740bc9e93a9dde0d5a8b096cb (diff)
compositing
-rw-r--r--fimage.h2
-rw-r--r--fragment.c239
2 files changed, 221 insertions, 20 deletions
diff --git a/fimage.h b/fimage.h
index a08dc93..a44b1b4 100644
--- a/fimage.h
+++ b/fimage.h
@@ -24,5 +24,5 @@ fragment_t *fragment_composite (fragment_t *dest,
pixman_op_t op,
fragment_t *source);
-void fragment_apply (fragment_t *fragment,
+pixman_bool_t fragment_apply (fragment_t *fragment,
pixman_image_t *image);
diff --git a/fragment.c b/fragment.c
index 1bb84ce..03dc330 100644
--- a/fragment.c
+++ b/fragment.c
@@ -1,3 +1,4 @@
+#include <assert.h>
#include <stdlib.h>
#include <pixman.h>
#include "fimage.h"
@@ -5,7 +6,14 @@
/* State should be considered a 'value' object that can be copied and written
* to. It may use copy-on-write internally, but the first iteration will
* just copy. What if the copy in copy-on-write fails? Either writes can
- * fail or states must be broken.
+ * fail or states must be broken.
+ *
+ * There may be only one operation required: composite. If so,
+ * having that copy and write wouldn't be too bad.
+ *
+ * Actually composite doesn't even need to update the state. We can
+ * just always copy because once a state is created it doesn't ever change.
+ * Which also means that command buffer is just "command".
*/
typedef union state_t state_t;
typedef enum
@@ -14,14 +22,14 @@ typedef enum
STATE_BLANK,
STATE_TRAPS,
STATE_GLYPHS,
- STATE_IMAGE
+ STATE_IMAGE,
+ STATE_COMMAND /* FIXME: should be called composite? */
} state_type_t;
typedef struct
{
int ref_count;
state_type_t type;
- pixman_region32_t region;
} state_common_t;
typedef struct
@@ -45,52 +53,128 @@ typedef struct
pixman_image_t * image;
} state_image_t;
+/* This state is interpreted as "copy @dest, then composite @source
+ * on top using @op as the operator".
+ */
+typedef struct
+{
+ state_t * dest;
+ pixman_op_t op;
+ state_t * source;
+} state_command_t;
+
union state_t
{
state_common_t common;
state_traps_t traps;
state_glyphs_t glyphs;
state_image_t image;
+ state_command_t command;
};
struct fragment_t
{
pixman_bool_t broken;
+ pixman_region32_t region;
state_t * state;
};
static state_t *
-state_new (int width, int height, state_type_t type)
+state_new (state_type_t type)
{
state_t *state = malloc (sizeof *state);
if (!state)
return NULL;
-
+
state->common.ref_count = 1;
state->common.type = type;
+
+ return state;
+}
- pixman_region32_init_rect (&state->common.region, 0, 0, width, height);
-
+static state_t *
+state_ref (state_t *state)
+{
+ state->common.ref_count++;
+
return state;
}
+static void
+state_unref (state_t *state)
+{
+ if (--state->common.ref_count == 0)
+ {
+ /* FIXME - check the states */
+ free (state);
+ }
+}
+
+static state_t *
+state_composite (state_t *dest, pixman_op_t op, state_t *source)
+{
+ state_t *new_state;
+
+ new_state = malloc (sizeof *new_state);
+ if (!new_state)
+ return NULL;
+
+ /* This function conceptually returns a new object. However,
+ * in order to optimize, we will want to to do copy-on-write
+ * whenever possible
+ */
+ /* It is always correct to make a copy of dest and source, and
+ * use them in a command buffer. That in turn implies that a
+ * command buffer holds a ref on all the images involved.
+ */
+ /* Actually a composite command should most likely be considered
+ * to have implicit destination and one source. That's what the callers
+ * are going to want:
+ *
+ * clip_image = create_clip_image()
+ * dest_image = create_dest_image()
+ * composite (dest_image, DEST_OUT, clip_image);
+ * etc.
+ */
+ /* Hence, for a new state, we should start by SRCing dest onto
+ * the new image, and then apply the composite.
+ */
+
+ /* FIXME: optimize here */
+ new_state->common.type = STATE_COMMAND;
+ new_state->common.ref_count = 1;
+
+ new_state->command.dest = state_ref (dest);
+ new_state->command.op = op;
+ new_state->command.source = state_ref (source);
+
+ return new_state;
+}
+
+static const fragment_t broken_fragment =
+{
+ TRUE, /* broken */
+ { { 0, 0, 0, 0 }, NULL }, /* region */
+ NULL /* state */
+};
+
static fragment_t *
fragment_alloc (int width, int height, state_type_t type)
{
- static const fragment_t broken_fragment = { TRUE };
fragment_t *fragment = malloc (sizeof *fragment);
-
+
if (!fragment)
return (fragment_t *)&broken_fragment;
-
- fragment->broken = FALSE;
- if (!(fragment->state = state_new (width, height, type)))
+ if (!(fragment->state = state_new (type)))
{
free (fragment);
return (fragment_t *)&broken_fragment;
}
-
+
+ fragment->broken = FALSE;
+ pixman_region32_init_rect (&fragment->region, 0, 0, width, height);
+
return fragment;
}
@@ -112,14 +196,14 @@ fragment_new_traps (int width, int height,
pixman_trapezoid_t *traps)
{
fragment_t *fragment;
-
+
fragment = fragment_alloc (width, height, STATE_TRAPS);
if (fragment->broken)
return fragment;
-
+
fragment->state->traps.n_traps = n_traps;
fragment->state->traps.traps = traps;
-
+
return fragment;
}
@@ -130,18 +214,49 @@ fragment_new_glyphs (int width, int height,
pixman_glyph_t *glyphs)
{
fragment_t *fragment;
-
+
fragment = fragment_alloc (width, height, STATE_GLYPHS);
if (fragment->broken)
return fragment;
-
+
fragment->state->glyphs.n_glyphs = n_glyphs;
fragment->state->glyphs.cache = cache;
fragment->state->glyphs.glyphs = glyphs;
-
+
return fragment;
}
+static fragment_t *
+fragment_intersect (fragment_t *dest, fragment_t *source)
+{
+ fragment_t *new_fragment;
+
+ new_fragment = malloc (sizeof *new_fragment);
+ if (!new_fragment)
+ return (fragment_t *)&broken_fragment;
+
+ new_fragment->broken = FALSE;
+
+ pixman_region32_init (&new_fragment->region);
+ if (!pixman_region32_intersect (
+ &new_fragment->region, &dest->region, &source->region))
+ {
+ free (new_fragment);
+ return (fragment_t *)&broken_fragment;
+ }
+
+ if (!pixman_region32_subtract (
+ &dest->region, &dest->region, &new_fragment->region))
+ {
+ pixman_region32_fini (&new_fragment->region);
+ free (new_fragment);
+
+ return (fragment_t *)&broken_fragment;
+ }
+
+ return new_fragment;
+}
+
/* Compute intersection of dest and source with state corresponding
* to compositing source with dest. Then subtract the region of
* intersection from dest and return the intersection fragment.
@@ -150,5 +265,91 @@ fragment_new_glyphs (int width, int height,
fragment_t *
fragment_composite (fragment_t *dest, pixman_op_t op, fragment_t *source)
{
+ fragment_t *result;
+ state_t *new_state;
+
+ if (dest->broken || source->broken)
+ return (fragment_t *)&broken_fragment;
+
+ new_state = state_composite (dest->state, op, source->state);
+ if (!new_state)
+ return (fragment_t *)&broken_fragment;
+
+ result = fragment_intersect (dest, source);
+ if (result->broken)
+ {
+ state_unref (new_state);
+ return result;
+ }
+
+ result->broken = FALSE;
+ result->state = new_state;
+
+ return result;
+}
+
+void
+fragment_free (fragment_t *fragment)
+{
+ /* FIXME */
+ pixman_region32_fini (&fragment->region);
+ state_unref (fragment->state);
+}
+
+static void
+state_apply (state_t *state, pixman_op_t op, pixman_image_t *image)
+{
+ int width = pixman_image_get_width (image);
+ int height = pixman_image_get_height (image);
+
+ switch (state->common.type)
+ {
+ case STATE_IMAGE:
+ if (state->image.image == image)
+ {
+ assert (op == PIXMAN_OP_SRC);
+ break;
+ }
+
+ pixman_image_composite (
+ op, state->image.image, NULL, image,
+ 0, 0, 0, 0, 0, 0,
+ width, height);
+ break;
+
+ case STATE_COMMAND:
+ /* FIXME: hmm this has to be broken because it doesn't ever
+ * create a temporary image.
+ */
+ /* The correct thing here may be to
+ * - SRC source onto temp image a
+ * - SRC dest onto temp image b
+ * - apply a op b
+ * - SRC b onto image
+ *
+ * which simplifies to
+ * - SRC source onto temp image
+ * - SRC dest into image
+ * - apply source to image
+ *
+ * In the case where source is just a "foo IN bar",
+ * we can simpify to "apply (foo, bar, dest)".
+ */
+ state_apply (state->command.dest, PIXMAN_OP_SRC, image);
+ state_apply (state->command.source, state->command.op, image);
+ break;
+ }
+}
+
+pixman_bool_t
+fragment_apply (fragment_t *fragment, pixman_image_t *image)
+{
+ if (fragment->broken)
+ return FALSE;
+
+ pixman_image_set_clip_region32 (image, &fragment->region);
+
+ state_apply (fragment->state, PIXMAN_OP_SRC, image);
+ return TRUE;
}