diff options
author | Søren Sandmann Pedersen <ssp@redhat.com> | 2012-10-05 00:39:37 -0400 |
---|---|---|
committer | Søren Sandmann Pedersen <ssp@redhat.com> | 2012-10-05 00:39:37 -0400 |
commit | 1a505f4fdbc040da940e9c55c2cdefc5e1178a8d (patch) | |
tree | 8fba8ebf11f300e3fe4d444ca6ef40f351a718f0 | |
parent | 4f5a86a90eba179a74d5ba92887814e5d4dcb504 (diff) |
fimage
-rw-r--r-- | fimage.c | 476 | ||||
-rw-r--r-- | fimage.h | 22 |
2 files changed, 498 insertions, 0 deletions
diff --git a/fimage.c b/fimage.c new file mode 100644 index 0000000..42247d3 --- /dev/null +++ b/fimage.c @@ -0,0 +1,476 @@ +#include <stdlib.h> +#include <pixman.h> +#include "fimage.h" + +struct fimage_t +{ + pixman_bool_t broken; + int n_allocated_fragments; + int n_fragments; + fragment_t * fragments[1]; +}; + +static const fimage_t broken_fimage = +{ + TRUE, /* broken */ + 0, + NULL +}; + +static fimage_t * +fimage_new (fragment_t *fragment) +{ + fimage_t *fimage; + + if (fragment_is_broken (fragment)) + return (fimage_t *)&broken_fimage; + + fimage = malloc (sizeof *fimage); + if (!fimage) + { + fragment_free (fragment); + return (fimage_t *)&broken_fimage; + } + + fimage->broken = FALSE; + fimage->n_allocated_fragments = 1; + fimage->n_fragments = 1; + fimage->fragments[0] = fragment; + + return fimage; +} + +static fimage_t * +fimage_ensure_space (fimage_t *fimage, int n_more) +{ + size_t required_fragments = fimage->n_fragments + n_more; + + if (required_fragments > fimage->n_allocated_fragments) + { + size_t pot = 1; + size_t space; + fimage_t *new_image; + + while (pot < required_fragments) + pot *= 2; + + space = sizeof (fimage_t) + (pot - 1) * sizeof (fragment_t *); + + new_image = realloc (fimage, space); + + if (!new_image) + { + fimage_free (fimage); + return (fimage_t *)&broken_fimage; + } + + new_image->n_allocated_fragments = pot; + + fimage = new_image; + } + + return fimage; +} + +static fimage_t * +fimage_append_fragment (fimage_t *fimage, fragment_t *fragment) +{ + fimage = ensure_space (fimage, 1); + if (fimage->broken) + return fimage; + + fimage->fragments[fimage->n_fragments++] = fragment; + return fimage; +} + +fimage_t * +fimage_new_blank (int width, int height) +{ + return fimage_new (fragment_new_blank (width, height)); +} + +fimage_t * +fimage_new_white (int width, int height) +{ + return fimage_new (fragment_new_white (width, height)); +} + +fimage_t * +fimage_new_traps (int width, int height, + int n_traps, pixman_trapezoid_t *traps) +{ + return fimage_new (fragment_new_traps (width, height, n_traps, traps)); +} + +fimage_t * +fimage_new_glyphs (int width, int height, + pixman_glyph_cache_t *cache, + int n_glyphs, + pixman_glyph_t *glyphs) +{ + return fimage_new ( + fragment_new_glyphs (width, height, cache, n_glyphs, glyphs)); +} + +/* Takes ownership of the image */ +fimage_t * +fimage_new_image (int width, int height, pixman_image_t *image) +{ + return fimage_new (fragment_new_image (width, height, image)); +} + + +/* Makes a copy of the region; does not take ownership */ +fimage_t * +fimage_new_region (int width, int height, + pixman_region32_t *region) +{ + fragment1 = fragment_new_blank (width, height); + + fragment2 = fragment_new_region (region); + + fragment1 = fragment_subtract (fragment1, fragment_2); + + image = fimage_new (fragment1); + image = fimage_append_fragment (image, fragment2); + + return image; +} + + + + + +/* + + Memory management: + + For traps we don't want to copy them, so the image should take ownership. + Maybe the cairo->pixman conversion should just take place inside the image? + Maybe it doesn't matter that much. + Maybe polygon images will make the question irrelevant. + However, we definitely don't want to copy the traps structure to + all the fragments. + + For pimages, have the image take a ref would be more convenient. + + For initializing with a region, it would be convenient, but not essential, + if we can avoid copying the region parameter. + + Structures internal to the images and fragments are of course managed and + owned by the structures in question. + + - images take ownership + - fragments take ownership + - fragments wrap objects in refcountable structure + - fragments don't ref pixman images + - fragments ref the given objects + - pixman images are reffed by the fragments, and unreffed + when the image is destroyed. + + - ref countable state object? + +*/ +typedef struct fragment_t fragment_t; +typedef struct image_t image_t; +typedef struct command_buffer_t command_buffer_t; + +#define TRUE 1 +#define FALSE 0 + +typedef enum +{ + BLANK, + WHITE, + SOLID, + TRAPS, + PIXMAN, + COMMANDS +} state_t; + +struct fragment_t +{ + pixman_bool_t broken; + pixman_region32_t region; + fragment_t * next; + + /* State - should this be a separate structure? */ + state_t state; + pixman_bool_t alpha_only; + union + { + pixman_color_t solid; + struct + { + int n_traps; + pixman_trapezoid_t *traps; + } traps; + pixman_image_t *image; + command_buffer_t *buffer; + } u; +}; + +struct image_t +{ + pixman_bool_t broken; + int width, height; + fragment_t * fragments; +}; + +static const image_t broken = +{ + TRUE /* broken */ +}; + +static const fragment_t broken_fragment = +{ + TRUE +}; + +static fragment_t * +fragment_new (int width, int height) +{ + fragment_t *result = malloc (sizeof *result); + + if (!result) + return (fragment_t *)&broken_fragment; + + pixman_region32_init_rect (&result->region, 0, 0, width, height); + + return result; +} + +static void +fragment_free (fragment_t *fragment) +{ + if (fragment->broken) + return; + + + /* FIXME */ +} + +static void +fragment_set_blank (fragment_t *fragment) +{ + if (fragment->broken) + return; + + fragment->state = BLANK; +} + +static void +fragment_set_pixman (fragment_t *fragment, pixman_image_t *image) +{ + if (fragment->broken) + return; + + fragment->state = PIXMAN; + fragment->u.image = image; +} + +/* Split @fragment into two parts: one that intersects @source + * and one that doesn't. Both may be NULL. @dest is freed or reused. + */ +static void +fragment_intersect (fragment_t *dest, fragment_t *source, + fragment_t **intersection, + fragment_t **remains) +{ + *intersection = NULL; + + if (dest->broken) + { + *remains = NULL; + return; + } + + *remains = dest; + + if (source->broken) + { + *intersection = NULL; + return; + } + + *intersection = fragment_new (0, 0); + + if ((*intersection)->broken) + { + *intersection = NULL; + return; + } + + if (!pixman_region32_intersect (&((*intersection)->region), + &(dest->region), + &(source->region))) + { + fragment_free (*intersection); + *intersection = NULL; + return; + } + + if (!pixman_region32_subtract (&(dest->region), &(dest->region), + &(*intersection)->region)) + { + fragment_free (dest); + fragment_free (*intersection); + *remains = NULL; + *intersection = NULL; + return; + } + + if (!pixman_region32_not_empty (&(*intersection)->region)) + { + fragment_free (*intersection); + *intersection = NULL; + } + + if (!pixman_region32_not_empty (&dest->region)) + { + fragment_free (dest); + *remains = NULL; + } +} + +static void +fragment_composite (fragment_t *result, pixman_op_t op, + fragment_t *dest, fragment_t *src) +{ + if ((op == PIXMAN_OP_ADD && dest->state == BLANK) || + (op == PIXMAN_OP_IN && is_opaque (dest))) + { + op = PIXMAN_OP_SRC; + } + + switch (op) + { + case PIXMAN_OP_SRC: + copy_state (result, src); + break; + + case PIXMAN_OP_IN: + if (dest->state == BLANK || src->state == BLANK) + result->state == BLANK; + break; + + case PIXMAN_OP_OUT_REVERSE: + if (is_opaque (src)) + result->state == BLANK; + else if (src->state == BLANK) + copy_state (result, dest->state); + break; + + default: + /* FIXME: add command to composite the images */ + break; + } +} + +static image_t * +allocate_image (int width, int height) +{ + image_t *image = malloc (sizeof *image); + + if (!image) + return (image_t *)&broken; + + image->width = width; + image->height = height; + image->fragments = fragment_new (width, height); + + return image; +} + +static image_t * +image_new_blank (int width, int height) +{ + image_t *image; + + image = allocate_image (width, height); + if (image->broken) + return image; + + fragment_set_blank (image->fragments); + return image; +} + +void +image_free (image_t *image) +{ + if (image->broken) + return; + + /* FIXME */ +} + +static image_t * +image_new_pixman (int width, int height, pixman_image_t *pimage) +{ + image_t *image; + + image = allocate_image (width, height); + if (image->broken) + return image; + + fragment_set_pixman (image->fragments, pimage); + return image; +} + +static void +image_composite (image_t *dest, pixman_op_t op, image_t *src, image_t *mask) +{ + fragment_t *sfrag; + + if (dest->broken || src->broken || (mask && mask->broken)) + return; + + if (mask) + { + image_t *tmp = allocate_image (dest->width, dest->height); + + image_composite (tmp, PIXMAN_OP_SRC, mask, NULL); + image_composite (tmp, PIXMAN_OP_IN, src, NULL); + image_composite (dest, op, tmp, NULL); + + image_free (tmp); + + return; + } + + sfrag = src->fragments; + while (sfrag) + { + fragment_t *new_fragments = NULL; + fragment_t *dfrag; + + dfrag = dest->fragments; + while (dfrag != NULL) + { + fragment_t *intersection, *remains; + fragment_t *next = dfrag->next; + + fragment_intersect (dfrag, sfrag, &intersection, &remains); + + if (intersection) + { + fragment_composite (intersect, op, dfrag, sfrag); + + intersection->next = new_fragments; + new_fragments = intersection; + } + + if (remains) + { + remains->next = new_fragments; + new_fragments = remains; + } + + dfrag = next; + } + + sfrag = sfrag->next; + } + + dest->fragments = new_fragments; +} @@ -26,6 +26,8 @@ fragment_t *fragment_new_region (pixman_region32_t *region); * (dest OP source). * * The intersection is subtracted from dest. The source is not changed + * + * Should probably be called "fragment_new_composite (op, dest, source)" FIXME */ fragment_t * fragment_composite (fragment_t *dest, pixman_op_t op, @@ -39,3 +41,23 @@ pixman_bool_t fragment_apply (fragment_t *fragment, pixman_image_t *image); void fragment_free (fragment_t *fragment); + + + +/* Images */ +typedef struct fimage_t fimage_t; + +fimage_t *fimage_new_blank (int width, int height); +fimage_t *fimage_new_white (int width, int height); +/* Takes ownership of @glyphs, but not glyph_cache */ +fimage_t *fimage_new_glyphs (int width, int height, + pixman_glyph_cache_t *glyph_cache, + int n_glyphs, + pixman_glyphs_t *glyphs); +/* Takes ownership of the trapezoids */ +fimage_t *fimage_new_traps (int width, int height, int n_traps, pixman_trapezoids_t *traps); +/* Takes ownership of the image */ +fimage_t *fimage_new_image (pixman_image_t *image); +/* Makes a copy of the region; does not take ownership */ +fimage_t *fimage_new_region (int width, int height, + pixman_region32_t *region); |