diff options
author | Benjamin Otte <otte@redhat.com> | 2010-01-22 10:48:58 +0100 |
---|---|---|
committer | Benjamin Otte <otte@redhat.com> | 2010-04-23 23:30:08 +0200 |
commit | 30d1696faf839cbd769c36524c827ffa019a7915 (patch) | |
tree | c9ef40f67cbc41219db04a34673930b3d18e7003 | |
parent | a3825d23c129b1945ea42653d3b7e39f82a2d6e4 (diff) |
Add cairo_image_surface_create_planar()
Includes addition of cairo_color_space_t enum with values for ARGB, RGB
and unpremultiplied ARGB.
We also keep track of the color space in image surfaces. This is needed
for creating copies of the pixman_image_t and when drawing to other
surface types.
-rw-r--r-- | src/cairo-image-surface.c | 192 | ||||
-rw-r--r-- | src/cairo-pattern.c | 3 | ||||
-rw-r--r-- | src/cairo-xlib-surface.c | 3 | ||||
-rw-r--r-- | src/cairo.h | 27 | ||||
-rw-r--r-- | src/cairoint.h | 8 |
5 files changed, 213 insertions, 20 deletions
diff --git a/src/cairo-image-surface.c b/src/cairo-image-surface.c index cd26ec77..817b04d3 100644 --- a/src/cairo-image-surface.c +++ b/src/cairo-image-surface.c @@ -128,7 +128,8 @@ _cairo_content_from_pixman_format (pixman_format_code_t pixman_format) cairo_surface_t * _cairo_image_surface_create_for_pixman_image (pixman_image_t *pixman_image, - pixman_format_code_t pixman_format) + pixman_format_code_t pixman_format, + pixman_color_space_t pixman_color_space) { cairo_image_surface_t *surface; int width = pixman_image_get_width (pixman_image); @@ -146,6 +147,7 @@ _cairo_image_surface_create_for_pixman_image (pixman_image_t *pixman_image, surface->pixman_image = pixman_image; surface->pixman_format = pixman_format; + surface->pixman_color_space = pixman_color_space; surface->format = _cairo_format_from_pixman_format (pixman_format); surface->owns_data = FALSE; surface->transparency = CAIRO_IMAGE_UNKNOWN; @@ -288,6 +290,23 @@ _cairo_format_to_pixman_format_code (cairo_format_t format) return ret; } +static pixman_color_space_t +_cairo_color_space_to_pixman_color_space (cairo_color_space_t color_space) +{ + pixman_color_space_t ret; + + switch (color_space) { + case CAIRO_COLOR_SPACE_ARGB_UNMULTIPLIED: + ret = PIXMAN_COLOR_SPACE_ARGB_UNMULTIPLIED; + break; + case CAIRO_COLOR_SPACE_ARGB: + default: + ret = PIXMAN_COLOR_SPACE_ARGB; + break; + } + return ret; +} + cairo_surface_t * _cairo_image_surface_create_with_pixman_format (unsigned char *data, pixman_format_code_t pixman_format, @@ -310,7 +329,8 @@ _cairo_image_surface_create_with_pixman_format (unsigned char *data, return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); surface = _cairo_image_surface_create_for_pixman_image (pixman_image, - pixman_format); + pixman_format, + PIXMAN_COLOR_SPACE_ARGB); if (unlikely (surface->status)) { pixman_image_unref (pixman_image); return surface; @@ -495,6 +515,113 @@ cairo_image_surface_create_for_data (unsigned char *data, slim_hidden_def (cairo_image_surface_create_for_data); /** + * cairo_image_surface_create_planar: + * @format: the layout format of the pixel data + * @color_space: the color space of the pixel data + * @width: the width of the image + * @height: the height of the image + * @n_planes: number of planes of data. This number must correspond + * with the number of planes expected by the given @format. + * @data: Array of @n_planes pointers to image data. + * @strides: An array containing for each @data buffer the number + * of bytes between the start of the rows in the buffer. + * + * Creates an image surface for the provided pixel data. The output + * buffer must be kept around until the #cairo_surface_t is destroyed + * or cairo_surface_finish() is called on the surface. The initial + * contents of @data will be used as the initial image contents; you + * must explicitly clear the buffer, using, for example, + * cairo_rectangle() and cairo_fill() if you want it cleared. + * + * This function is intended to be used only when one need to use + * data in a specific format or color space, as operations on the + * resulting surface may be slow. However, it is always faster to use + * Cairo than to do pixel conversions manually. Please refer to the + * pixman documentation for performance details. Or, if you are able + * to choose a format, use cairo_image_surface_create() to create + * the image surface instead. + * + * Note that the stride may be larger than width*bytes_per_pixel to + * provide proper alignment for each pixel and row. This alignment + * is required to allow high-performance rendering within cairo. + * Legal stride values must be multiples of 4. Note that + * cairo_format_stride_for_width() will only work for formats with a + * single plane. + * + * Return value: a pointer to the newly created surface. The caller + * owns the surface and should call cairo_surface_destroy() when done + * with it. + * + * This function always returns a valid pointer, but it will return a + * pointer to a "nil" surface in the case of an error such as out of + * memory or an invalid stride value. In case of invalid stride value + * the error status of the returned surface will be + * %CAIRO_STATUS_INVALID_STRIDE. You can use + * cairo_surface_status() to check for this. + * + * See cairo_surface_set_user_data() for a means of attaching a + * destroy-notification fallback to the surface if necessary. + **/ +cairo_public cairo_surface_t * +cairo_image_surface_create_planar (cairo_format_t format, + cairo_color_space_t color_space, + int width, + int height, + unsigned int n_planes, + char ** data, + int * strides) +{ + pixman_format_code_t pixman_format; + pixman_color_space_t pixman_color_space; + cairo_surface_t *surface; + pixman_image_t *pixman_image; + unsigned int i; + + if (! CAIRO_FORMAT_VALID (format)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); + if (! CAIRO_COLOR_SPACE_VALID (color_space)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); + + if (width == 0 || height == 0 || + ! _cairo_image_surface_is_size_valid (width, height)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); + + pixman_format = _cairo_format_to_pixman_format_code (format); + pixman_color_space = _cairo_color_space_to_pixman_color_space (color_space); + + if (n_planes != pixman_format_num_planes (pixman_format)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); + for (i = 0; i < n_planes; i++) { + if (data[i] == NULL) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NULL_POINTER)); + if ((strides[i] & (CAIRO_STRIDE_ALIGNMENT-1)) != 0) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_STRIDE)); + } + + pixman_image = pixman_image_create_planar (pixman_format, + pixman_color_space, + width, height, + n_planes, + (uint32_t **) data, + strides); + + if (unlikely (pixman_image == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + surface = _cairo_image_surface_create_for_pixman_image (pixman_image, + pixman_format, + pixman_color_space); + if (unlikely (surface->status)) { + pixman_image_unref (pixman_image); + return surface; + } + + return surface; +} +slim_hidden_def (cairo_image_surface_create_planar); + + +/** * cairo_image_surface_get_data: * @surface: a #cairo_image_surface_t * @@ -1200,6 +1327,35 @@ _acquire_source_cleanup (pixman_image_t *pixman_image, } static pixman_image_t * +_pixman_image_create_clone (pixman_image_t * image, + pixman_format_code_t pixman_format, + pixman_color_space_t pixman_color_space) +{ + uint32_t *data[CAIRO_FORMAT_MAX_PLANES]; + int strides[CAIRO_FORMAT_MAX_PLANES]; + int i, n_planes; + pixman_image_t *clone; + + n_planes = pixman_format_num_planes (pixman_format); + assert (n_planes <= CAIRO_FORMAT_MAX_PLANES); + + for (i = 0; i < n_planes; i++) { + data[i] = pixman_image_get_data_for_plane (image, i); + strides[i] = pixman_image_get_stride_for_plane (image, i); + } + + clone = pixman_image_create_planar (pixman_format, + pixman_color_space, + pixman_image_get_width (image), + pixman_image_get_height (image), + n_planes, + data, + strides); + + return clone; +} + +static pixman_image_t * _pixman_image_for_surface (const cairo_surface_pattern_t *pattern, const cairo_rectangle_int_t *extents, int *ix, int *iy) @@ -1244,11 +1400,9 @@ _pixman_image_for_surface (const cairo_surface_pattern_t *pattern, return pixman_image_ref (source->pixman_image); } - pixman_image = pixman_image_create_bits (source->pixman_format, - source->width, - source->height, - pixman_image_get_data (source->pixman_image), - pixman_image_get_stride (source->pixman_image)); + pixman_image = _pixman_image_create_clone (source->pixman_image, + source->pixman_format, + source->pixman_color_space); if (unlikely (pixman_image == NULL)) return NULL; } else if (type == CAIRO_INTERNAL_SURFACE_TYPE_SUBSURFACE) { @@ -1278,7 +1432,8 @@ _pixman_image_for_surface (const cairo_surface_pattern_t *pattern, } /* Avoid sub-byte offsets, force a copy in that case. */ - if (PIXMAN_FORMAT_BPP (source->pixman_format) >= 8) { + if (pixman_format_num_planes (source->pixman_format) == 1 && + PIXMAN_FORMAT_BPP (source->pixman_format) >= 8) { uint8_t *data = cairo_image_surface_get_data_for_row (source, sub->extents.y); data += sub->extents.x * PIXMAN_FORMAT_BPP(source->pixman_format)/8; pixman_image = pixman_image_create_bits (source->pixman_format, @@ -1320,11 +1475,9 @@ _pixman_image_for_surface (const cairo_surface_pattern_t *pattern, extend = CAIRO_EXTEND_NONE; } - pixman_image = pixman_image_create_bits (source->pixman_format, - source->width, - source->height, - pixman_image_get_data (source->pixman_image), - pixman_image_get_stride (source->pixman_image)); + pixman_image = _pixman_image_create_clone (source->pixman_image, + source->pixman_format, + source->pixman_color_space); if (unlikely (pixman_image == NULL)) { _cairo_surface_release_source_image (pattern->surface, cleanup->image, @@ -1761,7 +1914,7 @@ _create_composite_mask_pattern (cairo_clip_t *clip, if (need_clip_surface) { cairo_surface_t *tmp; - tmp = _cairo_image_surface_create_for_pixman_image (mask, PIXMAN_a8); + tmp = _cairo_image_surface_create_for_pixman_image (mask, PIXMAN_a8, PIXMAN_COLOR_SPACE_ARGB); if (unlikely (tmp->status)) { pixman_image_unref (mask); return NULL; @@ -2341,8 +2494,12 @@ static inline cairo_bool_t pattern_to_pixel (const cairo_solid_pattern_t *solid, cairo_operator_t op, pixman_format_code_t format, + pixman_color_space_t color_space, uint32_t *pixel) { + if (color_space != PIXMAN_COLOR_SPACE_ARGB) + return FALSE; + if (op == CAIRO_OPERATOR_CLEAR) { *pixel = 0; return TRUE; @@ -2708,7 +2865,7 @@ _composite_boxes (cairo_image_surface_t *dst, return CAIRO_INT_STATUS_UNSUPPORTED; if (pattern_to_pixel ((cairo_solid_pattern_t *) pattern, op, - dst->pixman_format, &pixel)) + dst->pixman_format, dst->pixman_color_space, &pixel)) { return _fill_unaligned_boxes (dst, pattern, pixel, boxes, extents); } @@ -2722,7 +2879,7 @@ _composite_boxes (cairo_image_surface_t *dst, status = CAIRO_STATUS_SUCCESS; if (! need_clip_mask && pattern_to_pixel ((cairo_solid_pattern_t *) pattern, op, dst->pixman_format, - &pixel)) + dst->pixman_color_space, &pixel)) { for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { cairo_box_t *box = chunk->base; @@ -4375,7 +4532,8 @@ _cairo_image_surface_coerce_to_format (cairo_image_surface_t *surface, if (unlikely (status)) return (cairo_image_surface_t *)_cairo_surface_create_in_error (status); - if (surface->format == format) + if (surface->pixman_color_space == PIXMAN_COLOR_SPACE_ARGB && + surface->format == format) return (cairo_image_surface_t *)cairo_surface_reference(&surface->base); clone = (cairo_image_surface_t *) diff --git a/src/cairo-pattern.c b/src/cairo-pattern.c index 7a9bf67a..e218ba08 100644 --- a/src/cairo-pattern.c +++ b/src/cairo-pattern.c @@ -1451,7 +1451,8 @@ _cairo_pattern_acquire_surface_for_gradient (const cairo_gradient_pattern_t *pat { image = (cairo_image_surface_t *) _cairo_image_surface_create_for_pixman_image (pixman_image, - PIXMAN_a8r8g8b8); + PIXMAN_a8r8g8b8, + PIXMAN_COLOR_SPACE_ARGB); if (image->base.status) { pixman_image_unref (pixman_image); diff --git a/src/cairo-xlib-surface.c b/src/cairo-xlib-surface.c index eea7baf0..cf7497a6 100644 --- a/src/cairo-xlib-surface.c +++ b/src/cairo-xlib-surface.c @@ -1056,7 +1056,8 @@ _draw_image_surface (cairo_xlib_surface_t *surface, if (unlikely (status)) return status; - if (!_pixman_format_to_masks (image->pixman_format, &image_masks)) + if (image->pixman_color_space != PIXMAN_COLOR_SPACE_ARGB || + !_pixman_format_to_masks (image->pixman_format, &image_masks)) { pixman_format_code_t intermediate_format; int ret; diff --git a/src/cairo.h b/src/cairo.h index fc6a68a7..350ccc47 100644 --- a/src/cairo.h +++ b/src/cairo.h @@ -2281,6 +2281,24 @@ typedef enum _cairo_format { CAIRO_FORMAT_RGB16_565 = 4 } cairo_format_t; +/** + * cairo_color_space_t: + * @CAIRO_COLOR_SPACE_ARGB: RGB data. The RGB values are premultiplied + * with the alpha value. The color corresponding to color values + * greater than the alpha value is undefined. + * @CAIRO_COLOR_SPACE_ARGB_UNMULTIPLIED: RGB data. The RGB values + * are not premultiplied with the alpha value. + * + * #cairo_color_space_t is used to interpret the color values + * of image data. + * + * New entries may be added in future versions. + **/ +typedef enum _cairo_color_space { + CAIRO_COLOR_SPACE_ARGB, + CAIRO_COLOR_SPACE_ARGB_UNMULTIPLIED +} cairo_color_space_t; + cairo_public cairo_surface_t * cairo_image_surface_create (cairo_format_t format, int width, @@ -2297,6 +2315,15 @@ cairo_image_surface_create_for_data (unsigned char *data, int height, int stride); +cairo_public cairo_surface_t * +cairo_image_surface_create_planar (cairo_format_t format, + cairo_color_space_t color_space, + int width, + int height, + unsigned int n_planes, + char ** data, + int * strides); + cairo_public unsigned char * cairo_image_surface_get_data (cairo_surface_t *surface); diff --git a/src/cairoint.h b/src/cairoint.h index 9c886ec3..b8408743 100644 --- a/src/cairoint.h +++ b/src/cairoint.h @@ -823,6 +823,7 @@ struct _cairo_image_surface { cairo_surface_t base; pixman_format_code_t pixman_format; + pixman_color_space_t pixman_color_space; cairo_format_t format; int width; @@ -2147,6 +2148,9 @@ _cairo_surface_has_device_transform (cairo_surface_t *surface) cairo_pure; */ #define CAIRO_FORMAT_VALID(format) ((format) >= CAIRO_FORMAT_ARGB32 && \ (format) <= CAIRO_FORMAT_RGB16_565) +#define CAIRO_FORMAT_MAX_PLANES 4 + +#define CAIRO_COLOR_SPACE_VALID(colorspace) ((colorspace) <= CAIRO_COLOR_SPACE_ARGB_UNMULTIPLIED) /* pixman-required stride alignment in bytes. */ #define CAIRO_STRIDE_ALIGNMENT (sizeof (uint32_t)) @@ -2176,7 +2180,8 @@ _cairo_content_from_pixman_format (pixman_format_code_t pixman_format); cairo_private cairo_surface_t * _cairo_image_surface_create_for_pixman_image (pixman_image_t *pixman_image, - pixman_format_code_t pixman_format); + pixman_format_code_t pixman_format, + pixman_color_space_t pixman_color_space); cairo_private cairo_bool_t _pixman_format_from_masks (cairo_format_masks_t *masks, @@ -2655,6 +2660,7 @@ slim_hidden_proto (cairo_glyph_allocate); slim_hidden_proto (cairo_glyph_free); slim_hidden_proto (cairo_image_surface_create); slim_hidden_proto (cairo_image_surface_create_for_data); +slim_hidden_proto (cairo_image_surface_create_planar); slim_hidden_proto (cairo_image_surface_get_data); slim_hidden_proto (cairo_image_surface_get_format); slim_hidden_proto (cairo_image_surface_get_height); |