summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenjamin Otte <otte@redhat.com>2010-01-22 10:48:58 +0100
committerBenjamin Otte <otte@redhat.com>2010-04-23 23:30:08 +0200
commit30d1696faf839cbd769c36524c827ffa019a7915 (patch)
treec9ef40f67cbc41219db04a34673930b3d18e7003
parenta3825d23c129b1945ea42653d3b7e39f82a2d6e4 (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.c192
-rw-r--r--src/cairo-pattern.c3
-rw-r--r--src/cairo-xlib-surface.c3
-rw-r--r--src/cairo.h27
-rw-r--r--src/cairoint.h8
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);