#include #include #include #include "fimage.h" /* 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. * * 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 { STATE_WHITE, STATE_BLANK, STATE_TRAPS, STATE_GLYPHS, STATE_IMAGE, STATE_COMPOSITE } state_type_t; typedef struct { int ref_count; state_type_t type; } state_common_t; typedef struct { state_common_t common; int n_traps; pixman_trapezoid_t *traps; } state_traps_t; typedef struct { state_common_t common; pixman_glyph_cache_t *cache; int n_glyphs; pixman_glyph_t * glyphs; } state_glyphs_t; typedef struct { state_common_t common; 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_composite_t; union state_t { state_common_t common; state_traps_t traps; state_glyphs_t glyphs; state_image_t image; state_composite_t composite; }; struct fragment_t { pixman_bool_t broken; pixman_region32_t region; state_t * state; }; static state_t * 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; } 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) { switch (state->common.type) { case STATE_WHITE: case STATE_BLANK: break; case STATE_TRAPS: free (state->traps.traps); break; case STATE_GLYPHS: free (state->glyphs.glyphs); break; case STATE_IMAGE: pixman_image_unref (state->image.image); break; case STATE_COMPOSITE: state_unref (state->composite.dest); state_unref (state->composite.source); break; } 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; /* FIXME: Always using COMPOSITE should be correct, but * we can do a lot better with a few simple optimizations */ new_state->common.type = STATE_COMPOSITE; new_state->common.ref_count = 1; new_state->composite.dest = state_ref (dest); new_state->composite.op = op; new_state->composite.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) { fragment_t *fragment = malloc (sizeof *fragment); if (!fragment) return (fragment_t *)&broken_fragment; 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; } fragment_t * fragment_new_blank (int width, int height) { return fragment_alloc (width, height, STATE_BLANK); } fragment_t * fragment_new_white (int width, int height) { return fragment_alloc (width, height, STATE_WHITE); } fragment_t * fragment_new_traps (int width, int height, int n_traps, 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; } fragment_t * fragment_new_region (pixman_region32_t *region) { fragment_t *fragment; fragment = fragment_alloc (1, 1, STATE_WHITE); if (fragment->broken) return fragment; if (!pixman_region32_copy (&fragment->region, region)) { fragment_free (fragment); return (fragment_t *)&broken_fragment; } return fragment; } fragment_t * fragment_new_glyphs (int width, int height, pixman_glyph_cache_t *cache, int n_glyphs, 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; } fragment_t * fragment_new_image (int width, int height, pixman_image_t *image) { fragment_t *fragment; fragment = fragment_alloc (width, height, STATE_IMAGE); if (fragment->broken) return fragment; fragment->state->image.image = image; return fragment; } fragment_t * fragment_subtract (fragment_t *dest, fragment_t *other) { if (dest->broken || other->broken || !pixman_region32_subtract ( &dest->region, &dest->region, &(other->region))) { fragment_free (dest); return (fragment_t *)&broken_fragment; } return dest; } pixman_bool_t fragment_is_empty (fragment_t *fragment) { if (fragment->broken) return FALSE; return !pixman_region32_not_empty (&fragment->region); } pixman_bool_t fragment_is_broken (fragment_t *fragment) { return fragment->broken; } static fragment_t * fragment_intersect (fragment_t *dest, fragment_t *source) { fragment_t *new_fragment; if (!(new_fragment = malloc (sizeof *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. * Both intersection and dest may end up empty or broken. */ 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) { if (fragment->broken) return; pixman_region32_fini (&fragment->region); state_unref (fragment->state); } static const pixman_color_t white = { 0xffff, 0xffff, 0xffff, 0xffff }; static void state_apply (state_t *state, pixman_image_t *image) { int width = pixman_image_get_width (image); int height = pixman_image_get_height (image); pixman_image_t *white_img, *temp; pixman_format_code_t mask_format; pixman_glyph_cache_t *glyph_cache; pixman_glyph_t *glyphs; int n_glyphs; white_img = pixman_image_create_solid_fill (&white); if (!white_img) return; /* FIXME status - FIXME: only create when necessary */ switch (state->common.type) { case STATE_BLANK: pixman_image_composite32 ( PIXMAN_OP_CLEAR, white_img, NULL, image, 0, 0, 0, 0, 0, 0, width, height); break; case STATE_TRAPS: /* FIXME: there may be a pixman bug here that we need * to fix or work around: it incorrectly clips rendering * to the extent of the trapezoids. (That is wrong for SRC * and other operators). */ pixman_composite_trapezoids ( PIXMAN_OP_SRC, white_img, image, PIXMAN_a8, 0, 0, 0, 0, state->traps.n_traps, state->traps.traps); break; case STATE_GLYPHS: glyph_cache = state->glyphs.cache; n_glyphs = state->glyphs.n_glyphs; glyphs = state->glyphs.glyphs; mask_format = pixman_glyph_get_mask_format ( glyph_cache, n_glyphs, glyphs); pixman_composite_glyphs ( PIXMAN_OP_SRC, white_img, image, mask_format, 0, 0, 0, 0, 0, 0, width, height, glyph_cache, n_glyphs, glyphs); break; case STATE_WHITE: pixman_image_composite32 ( PIXMAN_OP_SRC, white_img, NULL, image, 0, 0, 0, 0, 0, 0, width, height); break; case STATE_IMAGE: if (state->image.image == image) return; pixman_image_composite ( PIXMAN_OP_SRC, state->image.image, NULL, image, 0, 0, 0, 0, 0, 0, width, height); break; case STATE_COMPOSITE: /* FIXME: track alpha-only */ temp = pixman_image_create_bits ( PIXMAN_a8r8g8b8, width, height, NULL, -1); if (!temp) break; /* FIXME: status */ state_apply (state->composite.dest, image); /* FIXME: check if source can be expressed as * (s IN m), (s IN glyphs), (s IN traps) */ state_apply (state->composite.source, temp); pixman_image_composite32 ( state->composite.op, temp, NULL, image, 0, 0, 0, 0, 0, 0, width, height); pixman_image_unref (temp); 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, image); pixman_image_set_clip_region32 (image, NULL); return TRUE; }