#include #include /* 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. */ 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; }