diff options
author | Søren Sandmann Pedersen <ssp@redhat.com> | 2012-10-04 16:22:47 -0400 |
---|---|---|
committer | Søren Sandmann Pedersen <ssp@redhat.com> | 2012-10-04 16:22:47 -0400 |
commit | 7707f3487b16a49b74e700a2e4c2ba8f33afa6c3 (patch) | |
tree | 6bfdd3880faf0487bbe6d098ceefb41f5d31197b | |
parent | 0ead8e3023b8e3c740bc9e93a9dde0d5a8b096cb (diff) |
compositing
-rw-r--r-- | fimage.h | 2 | ||||
-rw-r--r-- | fragment.c | 239 |
2 files changed, 221 insertions, 20 deletions
@@ -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); @@ -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; } |