/* cairo - a vector graphics library with display and print output * * Copyright © 2004 David Reveman * * Permission to use, copy, modify, distribute, and sell this software * and its documentation for any purpose is hereby granted without * fee, provided that the above copyright notice appear in all copies * and that both that copyright notice and this permission notice * appear in supporting documentation, and that the name of David * Reveman not be used in advertising or publicity pertaining to * distribution of the software without specific, written prior * permission. David Reveman makes no representations about the * suitability of this software for any purpose. It is provided "as * is" without express or implied warranty. * * DAVID REVEMAN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS, IN NO EVENT SHALL DAVID REVEMAN BE LIABLE FOR ANY SPECIAL, * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Author: David Reveman */ #include "cairoint.h" #include "cairo-glitz.h" #include "cairo-glitz-private.h" #include "cairo-region-private.h" typedef struct _cairo_glitz_surface { cairo_surface_t base; glitz_surface_t *surface; glitz_format_t *format; cairo_region_t *clip_region; cairo_bool_t has_clip; glitz_box_t *clip_boxes; int num_clip_boxes; } cairo_glitz_surface_t; static const cairo_surface_backend_t * _cairo_glitz_surface_get_backend (void); static cairo_status_t _cairo_glitz_surface_finish (void *abstract_surface) { cairo_glitz_surface_t *surface = abstract_surface; if (surface->clip_boxes) free (surface->clip_boxes); cairo_region_destroy (surface->clip_region); glitz_surface_destroy (surface->surface); return CAIRO_STATUS_SUCCESS; } static glitz_format_name_t _glitz_format_from_content (cairo_content_t content) { switch (content) { case CAIRO_CONTENT_COLOR: return GLITZ_STANDARD_RGB24; case CAIRO_CONTENT_ALPHA: return GLITZ_STANDARD_A8; case CAIRO_CONTENT_COLOR_ALPHA: return GLITZ_STANDARD_ARGB32; } ASSERT_NOT_REACHED; return GLITZ_STANDARD_ARGB32; } static cairo_surface_t * _cairo_glitz_surface_create_similar (void *abstract_src, cairo_content_t content, int width, int height) { cairo_glitz_surface_t *src = abstract_src; cairo_surface_t *crsurface; glitz_drawable_t *drawable; glitz_surface_t *surface; glitz_format_t *gformat; drawable = glitz_surface_get_drawable (src->surface); gformat = glitz_find_standard_format (drawable, _glitz_format_from_content (content)); if (!gformat) return NULL; surface = glitz_surface_create (drawable, gformat, width <= 0 ? 1 : width, height <= 0 ? 1 : height, 0, NULL); if (surface == NULL) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); crsurface = cairo_glitz_surface_create (surface); glitz_surface_destroy (surface); return crsurface; } static cairo_status_t _cairo_glitz_surface_get_image (cairo_glitz_surface_t *surface, cairo_rectangle_int_t *interest, cairo_image_surface_t **image_out, cairo_rectangle_int_t *rect_out) { cairo_image_surface_t *image; cairo_rectangle_int_t extents; cairo_format_t format; cairo_format_masks_t masks; glitz_buffer_t *buffer; glitz_pixel_format_t pf; extents.x = 0; extents.y = 0; extents.width = glitz_surface_get_width (surface->surface); extents.height = glitz_surface_get_height (surface->surface); if (interest != NULL) { if (! _cairo_rectangle_intersect (&extents, interest)) { *image_out = NULL; return CAIRO_STATUS_SUCCESS; } } if (rect_out != NULL) *rect_out = extents; if (surface->format->color.fourcc == GLITZ_FOURCC_RGB) { if (surface->format->color.red_size > 0) { if (surface->format->color.alpha_size > 0) format = CAIRO_FORMAT_ARGB32; else format = CAIRO_FORMAT_RGB24; } else { format = CAIRO_FORMAT_A8; } } else format = CAIRO_FORMAT_ARGB32; image = (cairo_image_surface_t*) cairo_image_surface_create (format, extents.width, extents.height); if (image->base.status) return image->base.status; _pixman_format_to_masks (image->pixman_format, &masks); pf.fourcc = GLITZ_FOURCC_RGB; pf.masks.bpp = masks.bpp; pf.masks.alpha_mask = masks.alpha_mask; pf.masks.red_mask = masks.red_mask; pf.masks.green_mask = masks.green_mask; pf.masks.blue_mask = masks.blue_mask; pf.xoffset = 0; pf.skip_lines = 0; /* XXX: we should eventually return images with negative stride, need to verify that libpixman have no problem with this first. */ pf.bytes_per_line = image->stride; pf.scanline_order = GLITZ_PIXEL_SCANLINE_ORDER_TOP_DOWN; buffer = glitz_buffer_create_for_data (image->data); if (buffer == NULL) { cairo_surface_destroy (&image->base); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } /* clear out the glitz clip; the clip affects glitz_get_pixels */ if (surface->has_clip) glitz_surface_set_clip_region (surface->surface, 0, 0, NULL, 0); glitz_get_pixels (surface->surface, extents.x, extents.y, extents.width, extents.height, &pf, buffer); glitz_buffer_destroy (buffer); /* restore the clip, if any */ if (surface->has_clip) { glitz_surface_set_clip_region (surface->surface, 0, 0, surface->clip_boxes, surface->num_clip_boxes); } *image_out = image; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_glitz_surface_set_image (void *abstract_surface, cairo_image_surface_t *image, int src_x, int src_y, int width, int height, int x_dst, int y_dst) { cairo_glitz_surface_t *surface = abstract_surface; glitz_buffer_t *buffer; glitz_pixel_format_t pf; cairo_format_masks_t masks; char *data; _pixman_format_to_masks (image->pixman_format, &masks); pf.fourcc = GLITZ_FOURCC_RGB; pf.masks.bpp = masks.bpp; pf.masks.alpha_mask = masks.alpha_mask; pf.masks.red_mask = masks.red_mask; pf.masks.green_mask = masks.green_mask; pf.masks.blue_mask = masks.blue_mask; pf.xoffset = src_x; pf.skip_lines = src_y; /* check for negative stride */ if (image->stride < 0) { pf.bytes_per_line = -image->stride; pf.scanline_order = GLITZ_PIXEL_SCANLINE_ORDER_BOTTOM_UP; data = (char *) image->data + image->stride * (image->height - 1); } else { pf.bytes_per_line = image->stride; pf.scanline_order = GLITZ_PIXEL_SCANLINE_ORDER_TOP_DOWN; data = (char *) image->data; } buffer = glitz_buffer_create_for_data (data); if (buffer == NULL) return _cairo_error (CAIRO_STATUS_NO_MEMORY); glitz_set_pixels (surface->surface, x_dst, y_dst, width, height, &pf, buffer); glitz_buffer_destroy (buffer); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_glitz_surface_acquire_source_image (void *abstract_surface, cairo_image_surface_t **image_out, void **image_extra) { cairo_glitz_surface_t *surface = abstract_surface; *image_extra = NULL; return _cairo_glitz_surface_get_image (surface, NULL, image_out, NULL); } static cairo_surface_t * _cairo_glitz_surface_snapshot (void *abstract_surface) { cairo_glitz_surface_t *surface = abstract_surface; cairo_status_t status; cairo_image_surface_t *image; status = _cairo_glitz_surface_get_image (surface, NULL, &image, NULL); if (unlikely (status)) return _cairo_surface_create_in_error (status); return &image->base; } static void _cairo_glitz_surface_release_source_image (void *abstract_surface, cairo_image_surface_t *image, void *image_extra) { cairo_surface_destroy (&image->base); } static cairo_status_t _cairo_glitz_surface_acquire_dest_image (void *abstract_surface, cairo_rectangle_int_t *interest_rect, cairo_image_surface_t **image_out, cairo_rectangle_int_t *image_rect_out, void **image_extra) { cairo_glitz_surface_t *surface = abstract_surface; cairo_image_surface_t *image; cairo_status_t status; status = _cairo_glitz_surface_get_image (surface, interest_rect, &image, image_rect_out); if (status) return status; *image_out = image; *image_extra = NULL; return status; } static void _cairo_glitz_surface_release_dest_image (void *abstract_surface, cairo_rectangle_int_t *interest_rect, cairo_image_surface_t *image, cairo_rectangle_int_t *image_rect, void *image_extra) { cairo_glitz_surface_t *surface = abstract_surface; cairo_status_t status; status = _cairo_glitz_surface_set_image (surface, image, 0, 0, image->width, image->height, image_rect->x, image_rect->y); if (status) status = _cairo_surface_set_error (&surface->base, status); cairo_surface_destroy (&image->base); } static cairo_status_t _cairo_glitz_surface_clone_similar (void *abstract_surface, cairo_surface_t *src, int src_x, int src_y, int width, int height, int *clone_offset_x, int *clone_offset_y, cairo_surface_t **clone_out) { cairo_glitz_surface_t *surface = abstract_surface; cairo_glitz_surface_t *clone; cairo_status_t status; if (surface->base.status) return surface->base.status; if (src->backend == surface->base.backend) { *clone_offset_x = 0; *clone_offset_y = 0; *clone_out = cairo_surface_reference (src); return CAIRO_STATUS_SUCCESS; } else if (_cairo_surface_is_image (src)) { cairo_image_surface_t *image_src = (cairo_image_surface_t *) src; clone = (cairo_glitz_surface_t *) _cairo_glitz_surface_create_similar (surface, src->content, width, height); if (clone == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; if (clone->base.status) return clone->base.status; status = _cairo_glitz_surface_set_image (clone, image_src, src_x, src_y, width, height, 0, 0); if (status) { cairo_surface_destroy (&clone->base); return status; } *clone_out = &clone->base; *clone_offset_x = src_x; *clone_offset_y = src_y; return CAIRO_STATUS_SUCCESS; } return CAIRO_INT_STATUS_UNSUPPORTED; } static void _cairo_glitz_surface_set_matrix (cairo_glitz_surface_t *surface, cairo_matrix_t *matrix) { glitz_transform_t transform; transform.matrix[0][0] = _cairo_fixed_16_16_from_double (matrix->xx); transform.matrix[0][1] = _cairo_fixed_16_16_from_double (matrix->xy); transform.matrix[0][2] = _cairo_fixed_16_16_from_double (matrix->x0); transform.matrix[1][0] = _cairo_fixed_16_16_from_double (matrix->yx); transform.matrix[1][1] = _cairo_fixed_16_16_from_double (matrix->yy); transform.matrix[1][2] = _cairo_fixed_16_16_from_double (matrix->y0); transform.matrix[2][0] = 0; transform.matrix[2][1] = 0; transform.matrix[2][2] = _cairo_fixed_16_16_from_double (1); glitz_surface_set_transform (surface->surface, &transform); } static cairo_bool_t _is_supported_operator (cairo_operator_t op) { /* This is really just a if (op < SATURATE), but we use a switch * so the compiler will warn if we ever add more operators. */ switch (op) { case CAIRO_OPERATOR_CLEAR: case CAIRO_OPERATOR_SOURCE: case CAIRO_OPERATOR_OVER: case CAIRO_OPERATOR_IN: case CAIRO_OPERATOR_OUT: case CAIRO_OPERATOR_ATOP: case CAIRO_OPERATOR_DEST: case CAIRO_OPERATOR_DEST_OVER: case CAIRO_OPERATOR_DEST_IN: case CAIRO_OPERATOR_DEST_OUT: case CAIRO_OPERATOR_DEST_ATOP: case CAIRO_OPERATOR_XOR: case CAIRO_OPERATOR_ADD: return TRUE; default: ASSERT_NOT_REACHED; case CAIRO_OPERATOR_SATURATE: /* nobody likes saturate, expect that it's required to do * seamless polygons! */ case CAIRO_OPERATOR_MULTIPLY: case CAIRO_OPERATOR_SCREEN: case CAIRO_OPERATOR_OVERLAY: case CAIRO_OPERATOR_DARKEN: case CAIRO_OPERATOR_LIGHTEN: case CAIRO_OPERATOR_COLOR_DODGE: case CAIRO_OPERATOR_COLOR_BURN: case CAIRO_OPERATOR_HARD_LIGHT: case CAIRO_OPERATOR_SOFT_LIGHT: case CAIRO_OPERATOR_DIFFERENCE: case CAIRO_OPERATOR_EXCLUSION: case CAIRO_OPERATOR_HSL_HUE: case CAIRO_OPERATOR_HSL_SATURATION: case CAIRO_OPERATOR_HSL_COLOR: case CAIRO_OPERATOR_HSL_LUMINOSITY: return FALSE; } } static glitz_operator_t _glitz_operator (cairo_operator_t op) { switch ((int) op) { case CAIRO_OPERATOR_CLEAR: return GLITZ_OPERATOR_CLEAR; case CAIRO_OPERATOR_SOURCE: return GLITZ_OPERATOR_SRC; case CAIRO_OPERATOR_OVER: return GLITZ_OPERATOR_OVER; case CAIRO_OPERATOR_IN: return GLITZ_OPERATOR_IN; case CAIRO_OPERATOR_OUT: return GLITZ_OPERATOR_OUT; case CAIRO_OPERATOR_ATOP: return GLITZ_OPERATOR_ATOP; case CAIRO_OPERATOR_DEST: return GLITZ_OPERATOR_DST; case CAIRO_OPERATOR_DEST_OVER: return GLITZ_OPERATOR_OVER_REVERSE; case CAIRO_OPERATOR_DEST_IN: return GLITZ_OPERATOR_IN_REVERSE; case CAIRO_OPERATOR_DEST_OUT: return GLITZ_OPERATOR_OUT_REVERSE; case CAIRO_OPERATOR_DEST_ATOP: return GLITZ_OPERATOR_ATOP_REVERSE; case CAIRO_OPERATOR_XOR: return GLITZ_OPERATOR_XOR; case CAIRO_OPERATOR_ADD: return GLITZ_OPERATOR_ADD; default: ASSERT_NOT_REACHED; /* Something's very broken if this line of code can be reached, so * we want to return something that would give a noticeably * incorrect result. The XOR operator seems so rearely desired * that it should fit the bill here. */ return CAIRO_OPERATOR_XOR; } } #define CAIRO_GLITZ_FEATURE_OK(surface, name) \ (glitz_drawable_get_features (glitz_surface_get_drawable (surface)) & \ (GLITZ_FEATURE_ ## name ## _MASK)) static glitz_status_t _glitz_ensure_target (glitz_surface_t *surface) { if (!glitz_surface_get_attached_drawable (surface)) { glitz_drawable_format_t *target_format, templ; glitz_format_t *format; glitz_drawable_t *drawable, *target; unsigned int width, height; unsigned long mask; drawable = glitz_surface_get_drawable (surface); format = glitz_surface_get_format (surface); width = glitz_surface_get_width (surface); height = glitz_surface_get_height (surface); if (format->color.fourcc != GLITZ_FOURCC_RGB) return CAIRO_INT_STATUS_UNSUPPORTED; templ.color = format->color; templ.depth_size = 0; templ.stencil_size = 0; templ.doublebuffer = 0; templ.samples = 1; mask = GLITZ_FORMAT_RED_SIZE_MASK | GLITZ_FORMAT_GREEN_SIZE_MASK | GLITZ_FORMAT_BLUE_SIZE_MASK | GLITZ_FORMAT_ALPHA_SIZE_MASK | GLITZ_FORMAT_DEPTH_SIZE_MASK | GLITZ_FORMAT_STENCIL_SIZE_MASK | GLITZ_FORMAT_DOUBLEBUFFER_MASK | GLITZ_FORMAT_SAMPLES_MASK; target_format = glitz_find_drawable_format (drawable, mask, &templ, 0); if (!target_format) return CAIRO_INT_STATUS_UNSUPPORTED; target = glitz_create_drawable (drawable, target_format, width, height); if (!target) return CAIRO_INT_STATUS_UNSUPPORTED; glitz_surface_attach (surface, target, GLITZ_DRAWABLE_BUFFER_FRONT_COLOR); glitz_drawable_destroy (target); } return CAIRO_STATUS_SUCCESS; } typedef struct _cairo_glitz_surface_attributes { cairo_surface_attributes_t base; glitz_fill_t fill; glitz_filter_t filter; glitz_fixed16_16_t *params; int n_params; cairo_bool_t acquired; } cairo_glitz_surface_attributes_t; static cairo_int_status_t _cairo_glitz_pattern_acquire_surface (const cairo_pattern_t *pattern, cairo_glitz_surface_t *dst, int x, int y, unsigned int width, unsigned int height, cairo_glitz_surface_t **surface_out, cairo_glitz_surface_attributes_t *attr) { cairo_glitz_surface_t *src = NULL; attr->acquired = FALSE; switch (pattern->type) { case CAIRO_PATTERN_TYPE_LINEAR: case CAIRO_PATTERN_TYPE_RADIAL: { cairo_gradient_pattern_t *gradient = (cairo_gradient_pattern_t *) pattern; char *data; glitz_fixed16_16_t *params; unsigned int n_params; unsigned int *pixels; unsigned int i, n_base_params; glitz_buffer_t *buffer; static const glitz_pixel_format_t format = { GLITZ_FOURCC_RGB, { 32, 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff }, 0, 0, 0, GLITZ_PIXEL_SCANLINE_ORDER_BOTTOM_UP }; /* XXX: the current color gradient acceleration provided by glitz is * experimental, it's been proven inappropriate in a number of ways, * most importantly, it's currently implemented as filters and * gradients are not filters. eventually, it will be replaced with * something more appropriate. */ if (gradient->n_stops < 2) break; if (!CAIRO_GLITZ_FEATURE_OK (dst->surface, FRAGMENT_PROGRAM)) break; if (pattern->type == CAIRO_PATTERN_TYPE_RADIAL) n_base_params = 6; else n_base_params = 4; n_params = gradient->n_stops * 3 + n_base_params; /* check for int overflow */ { int size1, size2; if (n_params >= INT32_MAX / sizeof (glitz_fixed16_16_t) || gradient->n_stops >= INT32_MAX / sizeof (unsigned int)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); size1 = n_params * sizeof (glitz_fixed16_16_t); size2 = gradient->n_stops * sizeof (unsigned int); if (size1 >= INT32_MAX - size2) return _cairo_error (CAIRO_STATUS_NO_MEMORY); data = malloc (size1 + size2); } if (!data) return _cairo_error (CAIRO_STATUS_NO_MEMORY); params = (glitz_fixed16_16_t *) data; pixels = (unsigned int *) (data + sizeof (glitz_fixed16_16_t) * n_params); buffer = glitz_buffer_create_for_data (pixels); if (!buffer) { free (data); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } src = (cairo_glitz_surface_t *) _cairo_glitz_surface_create_similar (&dst->base, CAIRO_CONTENT_COLOR_ALPHA, gradient->n_stops, 1); if (src->base.status) { glitz_buffer_destroy (buffer); free (data); return src->base.status; } for (i = 0; i < gradient->n_stops; i++) { pixels[i] = (((int) (gradient->stops[i].color.alpha_short >> 8)) << 24) | (((int) (gradient->stops[i].color.red_short >> 8)) << 16) | (((int) (gradient->stops[i].color.green_short >> 8)) << 8) | (((int) (gradient->stops[i].color.blue_short >> 8))); params[n_base_params + 3 * i + 0] = _cairo_fixed_16_16_from_double (gradient->stops[i].offset); params[n_base_params + 3 * i + 1] = i << 16; params[n_base_params + 3 * i + 2] = 0; } glitz_set_pixels (src->surface, 0, 0, gradient->n_stops, 1, (glitz_pixel_format_t *)&format, buffer); glitz_buffer_destroy (buffer); if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR) { cairo_linear_pattern_t *grad = (cairo_linear_pattern_t *) pattern; params[0] = _cairo_fixed_to_16_16 (grad->p1.x); params[1] = _cairo_fixed_to_16_16 (grad->p1.y); params[2] = _cairo_fixed_to_16_16 (grad->p2.x); params[3] = _cairo_fixed_to_16_16 (grad->p2.y); attr->filter = GLITZ_FILTER_LINEAR_GRADIENT; } else { cairo_radial_pattern_t *grad = (cairo_radial_pattern_t *) pattern; params[0] = _cairo_fixed_to_16_16 (grad->c1.x); params[1] = _cairo_fixed_to_16_16 (grad->c1.y); params[2] = _cairo_fixed_to_16_16 (grad->r1); params[3] = _cairo_fixed_to_16_16 (grad->c2.x); params[4] = _cairo_fixed_to_16_16 (grad->c2.y); params[5] = _cairo_fixed_to_16_16 (grad->r2); attr->filter = GLITZ_FILTER_RADIAL_GRADIENT; } switch (pattern->extend) { case CAIRO_EXTEND_NONE: attr->fill = GLITZ_FILL_TRANSPARENT; break; case CAIRO_EXTEND_REPEAT: attr->fill = GLITZ_FILL_REPEAT; break; case CAIRO_EXTEND_REFLECT: attr->fill = GLITZ_FILL_REFLECT; break; case CAIRO_EXTEND_PAD: attr->fill = GLITZ_FILL_NEAREST; break; } attr->params = params; attr->n_params = n_params; attr->base.matrix = pattern->matrix; attr->base.x_offset = 0; attr->base.y_offset = 0; } break; case CAIRO_PATTERN_TYPE_SOLID: case CAIRO_PATTERN_TYPE_SURFACE: default: break; } if (!src) { cairo_int_status_t status; status = _cairo_pattern_acquire_surface (pattern, &dst->base, x, y, width, height, CAIRO_PATTERN_ACQUIRE_NONE, (cairo_surface_t **) &src, &attr->base); if (status) return status; if (src) { switch (attr->base.extend) { case CAIRO_EXTEND_NONE: attr->fill = GLITZ_FILL_TRANSPARENT; break; case CAIRO_EXTEND_REPEAT: attr->fill = GLITZ_FILL_REPEAT; break; case CAIRO_EXTEND_REFLECT: attr->fill = GLITZ_FILL_REFLECT; break; case CAIRO_EXTEND_PAD: default: attr->fill = GLITZ_FILL_NEAREST; break; } switch (attr->base.filter) { case CAIRO_FILTER_FAST: case CAIRO_FILTER_NEAREST: attr->filter = GLITZ_FILTER_NEAREST; break; case CAIRO_FILTER_GOOD: case CAIRO_FILTER_BEST: case CAIRO_FILTER_BILINEAR: case CAIRO_FILTER_GAUSSIAN: default: attr->filter = GLITZ_FILTER_BILINEAR; break; } attr->params = NULL; attr->n_params = 0; attr->acquired = TRUE; } } *surface_out = src; return CAIRO_STATUS_SUCCESS; } static void _cairo_glitz_pattern_release_surface (const cairo_pattern_t *pattern, cairo_glitz_surface_t *surface, cairo_glitz_surface_attributes_t *attr) { if (attr->acquired) _cairo_pattern_release_surface (pattern, &surface->base, &attr->base); else cairo_surface_destroy (&surface->base); } static cairo_int_status_t _cairo_glitz_pattern_acquire_surfaces (const cairo_pattern_t *src, const cairo_pattern_t *mask, cairo_glitz_surface_t *dst, int src_x, int src_y, int mask_x, int mask_y, unsigned int width, unsigned int height, cairo_glitz_surface_t **src_out, cairo_glitz_surface_t **mask_out, cairo_glitz_surface_attributes_t *sattr, cairo_glitz_surface_attributes_t *mattr) { cairo_int_status_t status; cairo_solid_pattern_t tmp; /* If src and mask are both solid, then the mask alpha can be * combined into src and mask can be ignored. */ /* XXX: This optimization assumes that there is no color * information in mask, so this will need to change when we * support RENDER-style 4-channel masks. */ if (src->type == CAIRO_PATTERN_TYPE_SOLID && mask->type == CAIRO_PATTERN_TYPE_SOLID) { cairo_color_t combined; cairo_solid_pattern_t *src_solid = (cairo_solid_pattern_t *) src; cairo_solid_pattern_t *mask_solid = (cairo_solid_pattern_t *) mask; combined = src_solid->color; _cairo_color_multiply_alpha (&combined, mask_solid->color.alpha); _cairo_pattern_init_solid (&tmp, &combined, CAIRO_CONTENT_COLOR_ALPHA); mask = NULL; src = &tmp.base; } status = _cairo_glitz_pattern_acquire_surface (src, dst, src_x, src_y, width, height, src_out, sattr); if (src == &tmp.base) _cairo_pattern_fini (&tmp.base); if (status) return status; if (mask) { status = _cairo_glitz_pattern_acquire_surface (mask, dst, mask_x, mask_y, width, height, mask_out, mattr); if (status) { /* XXX src == &tmp.base -> invalid (currently inconsequential) */ _cairo_glitz_pattern_release_surface (src, *src_out, sattr); } return status; } else { *mask_out = NULL; } return CAIRO_STATUS_SUCCESS; } static void _cairo_glitz_surface_set_attributes (cairo_glitz_surface_t *surface, cairo_glitz_surface_attributes_t *a) { _cairo_glitz_surface_set_matrix (surface, &a->base.matrix); glitz_surface_set_fill (surface->surface, a->fill); glitz_surface_set_filter (surface->surface, a->filter, a->params, a->n_params); } static cairo_status_t _cairo_glitz_get_boxes_from_region (cairo_region_t *region, glitz_box_t **boxes, int *nboxes) { pixman_box32_t *pboxes; cairo_status_t status = CAIRO_STATUS_SUCCESS; int n, i; n = 0; pboxes = pixman_region32_rectangles (®ion->rgn, &n); if (n == 0) { *nboxes = 0; return CAIRO_STATUS_SUCCESS; } if (n > *nboxes) { *boxes = _cairo_malloc_ab (n, sizeof (glitz_box_t)); if (*boxes == NULL) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto done; } } for (i = 0; i < n; i++) { (*boxes)[i].x1 = pboxes[i].x1; (*boxes)[i].y1 = pboxes[i].y1; (*boxes)[i].x2 = pboxes[i].x2; (*boxes)[i].y2 = pboxes[i].y2; } *nboxes = n; done: return status; } static cairo_status_t _cairo_glitz_surface_set_clip_region (void *abstract_surface, cairo_region_t *region) { cairo_glitz_surface_t *surface = abstract_surface; if (region == surface->clip_region) return CAIRO_STATUS_SUCCESS; cairo_region_destroy (surface->clip_region); surface->clip_region = cairo_region_reference (region); if (region != NULL) { cairo_status_t status; status = _cairo_glitz_get_boxes_from_region (region, &surface->clip_boxes, &surface->num_clip_boxes); if (status) return status; glitz_surface_set_clip_region (surface->surface, 0, 0, surface->clip_boxes, surface->num_clip_boxes); surface->has_clip = TRUE; } else { glitz_surface_set_clip_region (surface->surface, 0, 0, NULL, 0); surface->has_clip = FALSE; } return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t _cairo_glitz_surface_composite (cairo_operator_t op, const cairo_pattern_t *src_pattern, const cairo_pattern_t *mask_pattern, void *abstract_dst, int src_x, int src_y, int mask_x, int mask_y, int dst_x, int dst_y, unsigned int width, unsigned int height, cairo_region_t *clip_region) { cairo_glitz_surface_attributes_t src_attr, mask_attr; cairo_glitz_surface_t *dst = abstract_dst; cairo_glitz_surface_t *src; cairo_glitz_surface_t *mask; cairo_int_status_t status; if (! _is_supported_operator (op)) return CAIRO_INT_STATUS_UNSUPPORTED; if (_glitz_ensure_target (dst->surface)) return CAIRO_INT_STATUS_UNSUPPORTED; status = _cairo_glitz_surface_set_clip_region (dst, clip_region); if (status) return status; status = _cairo_glitz_pattern_acquire_surfaces (src_pattern, mask_pattern, dst, src_x, src_y, mask_x, mask_y, width, height, &src, &mask, &src_attr, &mask_attr); if (status) return status; _cairo_glitz_surface_set_attributes (src, &src_attr); if (mask) { _cairo_glitz_surface_set_attributes (mask, &mask_attr); glitz_composite (_glitz_operator (op), src->surface, mask->surface, dst->surface, src_x + src_attr.base.x_offset, src_y + src_attr.base.y_offset, mask_x + mask_attr.base.x_offset, mask_y + mask_attr.base.y_offset, dst_x, dst_y, width, height); } else { glitz_composite (_glitz_operator (op), src->surface, NULL, dst->surface, src_x + src_attr.base.x_offset, src_y + src_attr.base.y_offset, 0, 0, dst_x, dst_y, width, height); } if (glitz_surface_get_status (dst->surface) == GLITZ_STATUS_NOT_SUPPORTED) status = CAIRO_INT_STATUS_UNSUPPORTED; if (status == CAIRO_STATUS_SUCCESS && ! _cairo_operator_bounded_by_source (op)) { int src_width, src_height; int mask_width, mask_height; src_width = glitz_surface_get_width (src->surface); src_height = glitz_surface_get_height (src->surface); if (mask) { mask_width = glitz_surface_get_width (mask->surface); mask_height = glitz_surface_get_height (mask->surface); } else { mask_width = 0; mask_height = 0; } status = _cairo_surface_composite_fixup_unbounded (&dst->base, &src_attr.base, src_width, src_height, mask ? &mask_attr.base : NULL, mask_width, mask_height, src_x, src_y, mask_x, mask_y, dst_x, dst_y, width, height, clip_region); } if (mask) { if (mask_attr.n_params) free (mask_attr.params); _cairo_glitz_pattern_release_surface (mask_pattern, mask, &mask_attr); } if (src_attr.n_params) free (src_attr.params); _cairo_glitz_pattern_release_surface (src_pattern, src, &src_attr); return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t _cairo_glitz_surface_fill_rectangles (void *abstract_dst, cairo_operator_t op, const cairo_color_t *color, cairo_rectangle_int_t *rects, int n_rects) { cairo_glitz_surface_t *dst = abstract_dst; cairo_glitz_surface_t *src; glitz_rectangle_t stack_rects[CAIRO_STACK_ARRAY_LENGTH (glitz_rectangle_t)]; glitz_rectangle_t *glitz_rects = stack_rects; glitz_rectangle_t *current_rect; cairo_status_t status; int i; if (! _is_supported_operator (op)) return CAIRO_INT_STATUS_UNSUPPORTED; status = _cairo_glitz_surface_set_clip_region (dst, NULL); assert (status == CAIRO_STATUS_SUCCESS); if (n_rects > ARRAY_LENGTH (stack_rects)) { glitz_rects = _cairo_malloc_ab (n_rects, sizeof (glitz_rectangle_t)); if (glitz_rects == NULL) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } for (i = 0; i < n_rects; i++) { glitz_rects[i].x = rects[i].x; glitz_rects[i].y = rects[i].y; glitz_rects[i].width = rects[i].width; glitz_rects[i].height = rects[i].height; } switch (op) { case CAIRO_OPERATOR_CLEAR: case CAIRO_OPERATOR_SOURCE: { glitz_color_t glitz_color; glitz_format_t *format; glitz_color.red = color->red_short; glitz_color.green = color->green_short; glitz_color.blue = color->blue_short; glitz_color.alpha = color->alpha_short; /* * XXX even if the dst surface don't have an alpha channel, the * above alpha still effect the dst surface because the * underlying glitz drawable may have an alpha channel. So * replacing the color with an opaque one is needed. */ format = glitz_surface_get_format (dst->surface); if (format->color.alpha_size == 0) glitz_color.alpha = 0xffff; glitz_set_rectangles (dst->surface, &glitz_color, glitz_rects, n_rects); } break; case CAIRO_OPERATOR_SATURATE: return CAIRO_INT_STATUS_UNSUPPORTED; case CAIRO_OPERATOR_OVER: case CAIRO_OPERATOR_IN: case CAIRO_OPERATOR_OUT: case CAIRO_OPERATOR_ATOP: case CAIRO_OPERATOR_DEST: case CAIRO_OPERATOR_DEST_OVER: case CAIRO_OPERATOR_DEST_IN: case CAIRO_OPERATOR_DEST_OUT: case CAIRO_OPERATOR_DEST_ATOP: case CAIRO_OPERATOR_XOR: case CAIRO_OPERATOR_ADD: default: if (_glitz_ensure_target (dst->surface)) { if (glitz_rects != stack_rects) free (glitz_rects); return CAIRO_INT_STATUS_UNSUPPORTED; } src = (cairo_glitz_surface_t *) _cairo_surface_create_similar_solid (&dst->base, CAIRO_CONTENT_COLOR_ALPHA, 1, 1, (cairo_color_t *) color, FALSE); if (src == NULL || src->base.status) { if (glitz_rects != stack_rects) free (glitz_rects); return src ? src->base.status : CAIRO_INT_STATUS_UNSUPPORTED; } glitz_surface_set_fill (src->surface, GLITZ_FILL_REPEAT); current_rect = glitz_rects; while (n_rects--) { glitz_composite (_glitz_operator (op), src->surface, NULL, dst->surface, 0, 0, 0, 0, current_rect->x, current_rect->y, current_rect->width, current_rect->height); current_rect++; } cairo_surface_destroy (&src->base); break; } if (glitz_rects != stack_rects) free (glitz_rects); if (glitz_surface_get_status (dst->surface) == GLITZ_STATUS_NOT_SUPPORTED) return CAIRO_INT_STATUS_UNSUPPORTED; return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t _cairo_glitz_surface_composite_trapezoids (cairo_operator_t op, const cairo_pattern_t *pattern, void *abstract_dst, cairo_antialias_t antialias, int src_x, int src_y, int dst_x, int dst_y, unsigned int width, unsigned int height, cairo_trapezoid_t *traps, int n_traps, cairo_region_t *clip_region) { cairo_glitz_surface_attributes_t attributes; cairo_glitz_surface_t *dst = abstract_dst; cairo_glitz_surface_t *src; cairo_glitz_surface_t *mask = NULL; glitz_buffer_t *buffer = NULL; void *data = NULL; cairo_int_status_t status; unsigned short alpha; pixman_trapezoid_t stack_traps[CAIRO_STACK_ARRAY_LENGTH (pixman_trapezoid_t)]; pixman_trapezoid_t *pixman_traps = stack_traps; int i; if (antialias != CAIRO_ANTIALIAS_DEFAULT && antialias != CAIRO_ANTIALIAS_GRAY) { return CAIRO_INT_STATUS_UNSUPPORTED; } if (! _is_supported_operator (op)) return CAIRO_INT_STATUS_UNSUPPORTED; if (_glitz_ensure_target (dst->surface)) return CAIRO_INT_STATUS_UNSUPPORTED; status = _cairo_glitz_surface_set_clip_region (dst, clip_region); if (unlikely (status)) return status; /* Convert traps to pixman traps */ if (n_traps > ARRAY_LENGTH (stack_traps)) { pixman_traps = _cairo_malloc_ab (n_traps, sizeof (pixman_trapezoid_t)); if (pixman_traps == NULL) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } for (i = 0; i < n_traps; i++) { pixman_traps[i].top = _cairo_fixed_to_16_16 (traps[i].top); pixman_traps[i].bottom = _cairo_fixed_to_16_16 (traps[i].bottom); pixman_traps[i].left.p1.x = _cairo_fixed_to_16_16 (traps[i].left.p1.x); pixman_traps[i].left.p1.y = _cairo_fixed_to_16_16 (traps[i].left.p1.y); pixman_traps[i].left.p2.x = _cairo_fixed_to_16_16 (traps[i].left.p2.x); pixman_traps[i].left.p2.y = _cairo_fixed_to_16_16 (traps[i].left.p2.y); pixman_traps[i].right.p1.x = _cairo_fixed_to_16_16 (traps[i].right.p1.x); pixman_traps[i].right.p1.y = _cairo_fixed_to_16_16 (traps[i].right.p1.y); pixman_traps[i].right.p2.x = _cairo_fixed_to_16_16 (traps[i].right.p2.x); pixman_traps[i].right.p2.y = _cairo_fixed_to_16_16 (traps[i].right.p2.y); } status = _cairo_glitz_pattern_acquire_surface (pattern, dst, src_x, src_y, width, height, &src, &attributes); if (status) goto FAIL; alpha = 0xffff; if (op == CAIRO_OPERATOR_ADD || n_traps <= 1) { static const glitz_color_t clear_black = { 0, 0, 0, 0 }; glitz_color_t color; glitz_geometry_format_t format; int n_trap_added; int offset = 0; int data_size = 0; int size = 30 * n_traps; /* just a guess */ format.vertex.primitive = GLITZ_PRIMITIVE_QUADS; format.vertex.type = GLITZ_DATA_TYPE_FLOAT; format.vertex.bytes_per_vertex = 3 * sizeof (glitz_float_t); format.vertex.attributes = GLITZ_VERTEX_ATTRIBUTE_MASK_COORD_MASK; format.vertex.mask.type = GLITZ_DATA_TYPE_FLOAT; format.vertex.mask.size = GLITZ_COORDINATE_SIZE_X; format.vertex.mask.offset = 2 * sizeof (glitz_float_t); mask = (cairo_glitz_surface_t *) _cairo_glitz_surface_create_similar (&dst->base, CAIRO_CONTENT_ALPHA, 2, 1); if (mask == NULL) { status = CAIRO_INT_STATUS_UNSUPPORTED; goto FAIL; } if (mask->base.status) { status = mask->base.status; goto FAIL; } color.red = color.green = color.blue = color.alpha = 0xffff; glitz_set_rectangle (mask->surface, &clear_black, 0, 0, 1, 1); glitz_set_rectangle (mask->surface, &color, 1, 0, 1, 1); glitz_surface_set_fill (mask->surface, GLITZ_FILL_NEAREST); glitz_surface_set_filter (mask->surface, GLITZ_FILTER_BILINEAR, NULL, 0); size *= format.vertex.bytes_per_vertex; while (n_traps) { if (data_size < size) { void *p; data_size = size; p = realloc (data, data_size); if (p == NULL) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto FAIL; } data = p; if (buffer) glitz_buffer_destroy (buffer); buffer = glitz_buffer_create_for_data (data); if (buffer == NULL) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); free (data); goto FAIL; } } offset += glitz_add_trapezoids (buffer, offset, size - offset, format.vertex.type, mask->surface, (glitz_trapezoid_t *) pixman_traps, n_traps, &n_trap_added); n_traps -= n_trap_added; traps += n_trap_added; size *= 2; } glitz_set_geometry (dst->surface, GLITZ_GEOMETRY_TYPE_VERTEX, &format, buffer); glitz_set_array (dst->surface, 0, 3, offset / format.vertex.bytes_per_vertex, 0, 0); } else { cairo_image_surface_t *image; unsigned char *ptr; int stride; stride = (width + 3) & -4; data = calloc (stride, height); if (data == NULL) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto FAIL; } /* using negative stride */ ptr = (unsigned char *) data + stride * (height - 1); image = (cairo_image_surface_t *) cairo_image_surface_create_for_data (ptr, CAIRO_FORMAT_A8, width, height, -stride); status = image->base.status; if (status) { free (data); goto FAIL; } pixman_add_trapezoids (image->pixman_image, -dst_x, -dst_y, n_traps, (pixman_trapezoid_t *) pixman_traps); mask = (cairo_glitz_surface_t *) _cairo_glitz_surface_create_similar (&dst->base, CAIRO_CONTENT_ALPHA, width, height); status = mask->base.status; if (status) { free (data); cairo_surface_destroy (&image->base); goto FAIL; } status = _cairo_glitz_surface_set_image (mask, image, 0, 0, width, height, 0, 0); cairo_surface_destroy (&image->base); if (status) goto FAIL; } _cairo_glitz_surface_set_attributes (src, &attributes); glitz_composite (_glitz_operator (op), src->surface, mask->surface, dst->surface, src_x + attributes.base.x_offset, src_y + attributes.base.y_offset, 0, 0, dst_x, dst_y, width, height); if (attributes.n_params) free (attributes.params); glitz_set_geometry (dst->surface, GLITZ_GEOMETRY_TYPE_NONE, NULL, NULL); if (buffer) glitz_buffer_destroy (buffer); free (data); if (glitz_surface_get_status (dst->surface) == GLITZ_STATUS_NOT_SUPPORTED) { status = CAIRO_INT_STATUS_UNSUPPORTED; goto FAIL; } if (! _cairo_operator_bounded_by_mask (op)) { status = _cairo_surface_composite_shape_fixup_unbounded (&dst->base, &attributes.base, glitz_surface_get_width (src->surface), glitz_surface_get_height (src->surface), width, height, src_x, src_y, 0, 0, dst_x, dst_y, width, height, clip_region); } FAIL: _cairo_glitz_pattern_release_surface (pattern, src, &attributes); if (mask != NULL) cairo_surface_destroy (&mask->base); if (pixman_traps != stack_traps) free (pixman_traps); return status; } static cairo_bool_t _cairo_glitz_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *rectangle) { cairo_glitz_surface_t *surface = abstract_surface; rectangle->x = 0; rectangle->y = 0; rectangle->width = glitz_surface_get_width (surface->surface); rectangle->height = glitz_surface_get_height (surface->surface); return TRUE; } #define CAIRO_GLITZ_AREA_AVAILABLE 0 #define CAIRO_GLITZ_AREA_DIVIDED 1 #define CAIRO_GLITZ_AREA_OCCUPIED 2 typedef struct _cairo_glitz_root_area cairo_glitz_root_area_t; typedef struct _cairo_glitz_area { int state; int level; int x, y; int width, height; struct _cairo_glitz_area *area[4]; cairo_glitz_root_area_t *root; void *closure; } cairo_glitz_area_t; static cairo_glitz_area_t _empty_area = { 0, 0, 0, 0, 0, 0, { NULL, NULL, NULL, NULL }, NULL, NULL }; typedef struct _cairo_glitz_area_funcs { cairo_status_t (*move_in) (cairo_glitz_area_t *area, void *closure); void (*move_out) (cairo_glitz_area_t *area, void *closure); int (*compare_score) (cairo_glitz_area_t *area, void *closure1, void *closure2); } cairo_glitz_area_funcs_t; struct _cairo_glitz_root_area { int max_level; int width, height; cairo_glitz_area_t *area; const cairo_glitz_area_funcs_t *funcs; }; static cairo_status_t _cairo_glitz_area_move_in (cairo_glitz_area_t *area, void *closure) { area->closure = closure; area->state = CAIRO_GLITZ_AREA_OCCUPIED; return (*area->root->funcs->move_in) (area, area->closure); } static void _cairo_glitz_area_move_out (cairo_glitz_area_t *area) { if (area->root) { (*area->root->funcs->move_out) (area, area->closure); area->closure = NULL; area->state = CAIRO_GLITZ_AREA_AVAILABLE; } } static cairo_glitz_area_t * _cairo_glitz_area_create (cairo_glitz_root_area_t *root, int level, int x, int y, int width, int height) { cairo_glitz_area_t *area; int n = 4; area = malloc (sizeof (cairo_glitz_area_t)); if (!area) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return NULL; } area->level = level; area->x = x; area->y = y; area->width = width; area->height = height; area->root = root; area->closure = NULL; area->state = CAIRO_GLITZ_AREA_AVAILABLE; while (n--) area->area[n] = NULL; return area; } static void _cairo_glitz_area_destroy (cairo_glitz_area_t *area) { if (area == NULL) return; if (area->state == CAIRO_GLITZ_AREA_OCCUPIED) { _cairo_glitz_area_move_out (area); } else { int n = 4; while (n--) _cairo_glitz_area_destroy (area->area[n]); } free (area); } static cairo_glitz_area_t * _cairo_glitz_area_get_top_scored_sub_area (cairo_glitz_area_t *area) { if (!area) return NULL; switch (area->state) { case CAIRO_GLITZ_AREA_OCCUPIED: return area; case CAIRO_GLITZ_AREA_AVAILABLE: break; case CAIRO_GLITZ_AREA_DIVIDED: { cairo_glitz_area_t *tmp, *top = NULL; int i; for (i = 0; i < 4; i++) { tmp = _cairo_glitz_area_get_top_scored_sub_area (area->area[i]); if (tmp && top) { if ((*area->root->funcs->compare_score) (tmp, tmp->closure, top->closure) > 0) top = tmp; } else if (tmp) { top = tmp; } } return top; } } return NULL; } static cairo_int_status_t _cairo_glitz_area_find (cairo_glitz_area_t *area, int width, int height, cairo_bool_t kick_out, void *closure) { cairo_status_t status; if (area->width < width || area->height < height) return CAIRO_INT_STATUS_UNSUPPORTED; switch (area->state) { case CAIRO_GLITZ_AREA_OCCUPIED: if (kick_out) { if ((*area->root->funcs->compare_score) (area, area->closure, closure) >= 0) return CAIRO_INT_STATUS_UNSUPPORTED; _cairo_glitz_area_move_out (area); } else { return CAIRO_INT_STATUS_UNSUPPORTED; } /* fall-through */ case CAIRO_GLITZ_AREA_AVAILABLE: { if (area->level == area->root->max_level || (area->width == width && area->height == height)) { return _cairo_glitz_area_move_in (area, closure); } else { int dx[4], dy[4], w[4], h[4], i; dx[0] = dx[2] = dy[0] = dy[1] = 0; w[0] = w[2] = dx[1] = dx[3] = width; h[0] = h[1] = dy[2] = dy[3] = height; w[1] = w[3] = area->width - width; h[2] = h[3] = area->height - height; for (i = 0; i < 2; i++) { if (w[i]) area->area[i] = _cairo_glitz_area_create (area->root, area->level + 1, area->x + dx[i], area->y + dy[i], w[i], h[i]); } for (; i < 4; i++) { if (w[i] && h[i]) area->area[i] = _cairo_glitz_area_create (area->root, area->level + 1, area->x + dx[i], area->y + dy[i], w[i], h[i]); } area->state = CAIRO_GLITZ_AREA_DIVIDED; status = _cairo_glitz_area_find (area->area[0], width, height, kick_out, closure); if (status == CAIRO_STATUS_SUCCESS) return CAIRO_STATUS_SUCCESS; } } break; case CAIRO_GLITZ_AREA_DIVIDED: { cairo_glitz_area_t *to_area; int i, rejected = FALSE; for (i = 0; i < 4; i++) { if (area->area[i]) { if (area->area[i]->width >= width && area->area[i]->height >= height) { status = _cairo_glitz_area_find (area->area[i], width, height, kick_out, closure); if (status == CAIRO_STATUS_SUCCESS) return CAIRO_STATUS_SUCCESS; rejected = TRUE; } } } if (rejected) return CAIRO_INT_STATUS_UNSUPPORTED; to_area = _cairo_glitz_area_get_top_scored_sub_area (area); if (to_area) { if (kick_out) { if ((*area->root->funcs->compare_score) (to_area, to_area->closure, closure) >= 0) return CAIRO_INT_STATUS_UNSUPPORTED; } else { return CAIRO_INT_STATUS_UNSUPPORTED; } } for (i = 0; i < 4; i++) { _cairo_glitz_area_destroy (area->area[i]); area->area[i] = NULL; } area->closure = NULL; area->state = CAIRO_GLITZ_AREA_AVAILABLE; status = _cairo_glitz_area_find (area, width, height, TRUE, closure); if (status == CAIRO_STATUS_SUCCESS) return CAIRO_STATUS_SUCCESS; } break; } return CAIRO_INT_STATUS_UNSUPPORTED; } static cairo_status_t _cairo_glitz_root_area_init (cairo_glitz_root_area_t *root, int max_level, int width, int height, const cairo_glitz_area_funcs_t *funcs) { root->max_level = max_level; root->funcs = funcs; root->area = _cairo_glitz_area_create (root, 0, 0, 0, width, height); if (!root->area) return _cairo_error (CAIRO_STATUS_NO_MEMORY); return CAIRO_STATUS_SUCCESS; } static void _cairo_glitz_root_area_fini (cairo_glitz_root_area_t *root) { _cairo_glitz_area_destroy (root->area); } typedef struct _cairo_glitz_surface_font_private { cairo_glitz_root_area_t root; glitz_surface_t *surface; } cairo_glitz_surface_font_private_t; typedef struct _cairo_glitz_surface_glyph_private { cairo_glitz_area_t *area; cairo_bool_t locked; cairo_point_double_t p1, p2; } cairo_glitz_surface_glyph_private_t; static cairo_status_t _cairo_glitz_glyph_move_in (cairo_glitz_area_t *area, void *closure) { cairo_glitz_surface_glyph_private_t *glyph_private = closure; glyph_private->area = area; return CAIRO_STATUS_SUCCESS; } static void _cairo_glitz_glyph_move_out (cairo_glitz_area_t *area, void *closure) { cairo_glitz_surface_glyph_private_t *glyph_private = closure; glyph_private->area = NULL; } static int _cairo_glitz_glyph_compare (cairo_glitz_area_t *area, void *closure1, void *closure2) { cairo_glitz_surface_glyph_private_t *glyph_private = closure1; if (glyph_private->locked) return 1; return -1; } static const cairo_glitz_area_funcs_t _cairo_glitz_area_funcs = { _cairo_glitz_glyph_move_in, _cairo_glitz_glyph_move_out, _cairo_glitz_glyph_compare }; #define GLYPH_CACHE_TEXTURE_SIZE 512 #define GLYPH_CACHE_MAX_LEVEL 64 #define GLYPH_CACHE_MAX_HEIGHT 96 #define GLYPH_CACHE_MAX_WIDTH 96 #define WRITE_VEC2(ptr, _x, _y) \ *(ptr)++ = (_x); \ *(ptr)++ = (_y) #define WRITE_BOX(ptr, _vx1, _vy1, _vx2, _vy2, p1, p2) \ WRITE_VEC2 (ptr, _vx1, _vy1); \ WRITE_VEC2 (ptr, (p1)->x, (p2)->y); \ WRITE_VEC2 (ptr, _vx2, _vy1); \ WRITE_VEC2 (ptr, (p2)->x, (p2)->y); \ WRITE_VEC2 (ptr, _vx2, _vy2); \ WRITE_VEC2 (ptr, (p2)->x, (p1)->y); \ WRITE_VEC2 (ptr, _vx1, _vy2); \ WRITE_VEC2 (ptr, (p1)->x, (p1)->y) static cairo_status_t _cairo_glitz_surface_font_init (cairo_glitz_surface_t *surface, cairo_scaled_font_t *scaled_font, cairo_format_t format) { cairo_glitz_surface_font_private_t *font_private; glitz_drawable_t *drawable; glitz_format_t *surface_format = NULL; cairo_int_status_t status; drawable = glitz_surface_get_drawable (surface->surface); switch (format) { case CAIRO_FORMAT_A1: case CAIRO_FORMAT_A8: surface_format = glitz_find_standard_format (drawable, GLITZ_STANDARD_A8); break; case CAIRO_FORMAT_RGB24: ASSERT_NOT_REACHED; break; case CAIRO_FORMAT_ARGB32: surface_format = glitz_find_standard_format (drawable, GLITZ_STANDARD_ARGB32); default: break; } if (!surface_format) return CAIRO_INT_STATUS_UNSUPPORTED; font_private = malloc (sizeof (cairo_glitz_surface_font_private_t)); if (!font_private) return _cairo_error (CAIRO_STATUS_NO_MEMORY); font_private->surface = glitz_surface_create (drawable, surface_format, GLYPH_CACHE_TEXTURE_SIZE, GLYPH_CACHE_TEXTURE_SIZE, 0, NULL); if (font_private->surface == NULL) { free (font_private); return CAIRO_INT_STATUS_UNSUPPORTED; } if (format == CAIRO_FORMAT_ARGB32) glitz_surface_set_component_alpha (font_private->surface, 1); status = _cairo_glitz_root_area_init (&font_private->root, GLYPH_CACHE_MAX_LEVEL, GLYPH_CACHE_TEXTURE_SIZE, GLYPH_CACHE_TEXTURE_SIZE, &_cairo_glitz_area_funcs); if (status != CAIRO_STATUS_SUCCESS) { glitz_surface_destroy (font_private->surface); free (font_private); return status; } scaled_font->surface_private = font_private; scaled_font->surface_backend = _cairo_glitz_surface_get_backend (); return CAIRO_STATUS_SUCCESS; } static void _cairo_glitz_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font) { cairo_glitz_surface_font_private_t *font_private; font_private = scaled_font->surface_private; if (font_private) { _cairo_glitz_root_area_fini (&font_private->root); glitz_surface_destroy (font_private->surface); free (font_private); } } static void _cairo_glitz_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph, cairo_scaled_font_t *scaled_font) { cairo_glitz_surface_glyph_private_t *glyph_private; glyph_private = scaled_glyph->surface_private; if (glyph_private) { if (glyph_private->area) _cairo_glitz_area_move_out (glyph_private->area); free (glyph_private); } } #define FIXED_TO_FLOAT(f) (((glitz_float_t) (f)) / 65536) static cairo_status_t _cairo_glitz_surface_add_glyph (cairo_glitz_surface_t *surface, cairo_scaled_font_t *scaled_font, cairo_scaled_glyph_t *scaled_glyph) { cairo_image_surface_t *glyph_surface = scaled_glyph->surface; cairo_glitz_surface_font_private_t *font_private; cairo_glitz_surface_glyph_private_t *glyph_private; glitz_point_fixed_t p1, p2; glitz_pixel_format_t pf; glitz_buffer_t *buffer; cairo_format_masks_t masks; cairo_int_status_t status; glyph_private = scaled_glyph->surface_private; if (glyph_private == NULL) { glyph_private = malloc (sizeof (cairo_glitz_surface_glyph_private_t)); if (!glyph_private) return _cairo_error (CAIRO_STATUS_NO_MEMORY); glyph_private->area = NULL; glyph_private->locked = FALSE; scaled_glyph->surface_private = (void *) glyph_private; } if (glyph_surface->width > GLYPH_CACHE_MAX_WIDTH || glyph_surface->height > GLYPH_CACHE_MAX_HEIGHT) return CAIRO_INT_STATUS_UNSUPPORTED; if (scaled_font->surface_private == NULL) { status = _cairo_glitz_surface_font_init (surface, scaled_font, glyph_surface->format); if (status) return status; } font_private = scaled_font->surface_private; if (glyph_surface->width == 0 || glyph_surface->height == 0) { glyph_private->area = &_empty_area; return CAIRO_STATUS_SUCCESS; } if (_cairo_glitz_area_find (font_private->root.area, glyph_surface->width, glyph_surface->height, FALSE, glyph_private)) { if (_cairo_glitz_area_find (font_private->root.area, glyph_surface->width, glyph_surface->height, TRUE, glyph_private)) return CAIRO_STATUS_SUCCESS; } buffer = glitz_buffer_create_for_data (glyph_surface->data); if (!buffer) { _cairo_glitz_area_move_out (glyph_private->area); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } _pixman_format_to_masks (glyph_surface->pixman_format, &masks); pf.fourcc = GLITZ_FOURCC_RGB; pf.masks.bpp = masks.bpp; pf.masks.alpha_mask = masks.alpha_mask; pf.masks.red_mask = masks.red_mask; pf.masks.green_mask = masks.green_mask; pf.masks.blue_mask = masks.blue_mask; pf.bytes_per_line = glyph_surface->stride; pf.scanline_order = GLITZ_PIXEL_SCANLINE_ORDER_BOTTOM_UP; pf.xoffset = 0; pf.skip_lines = 0; glitz_set_pixels (font_private->surface, glyph_private->area->x, glyph_private->area->y, glyph_surface->width, glyph_surface->height, &pf, buffer); glitz_buffer_destroy (buffer); p1.x = glyph_private->area->x << 16; p1.y = glyph_private->area->y << 16; p2.x = (glyph_private->area->x + glyph_surface->width) << 16; p2.y = (glyph_private->area->y + glyph_surface->height) << 16; glitz_surface_translate_point (font_private->surface, &p1, &p1); glitz_surface_translate_point (font_private->surface, &p2, &p2); glyph_private->p1.x = FIXED_TO_FLOAT (p1.x); glyph_private->p1.y = FIXED_TO_FLOAT (p1.y); glyph_private->p2.x = FIXED_TO_FLOAT (p2.x); glyph_private->p2.y = FIXED_TO_FLOAT (p2.y); return CAIRO_STATUS_SUCCESS; } #define N_STACK_BUF 256 static cairo_int_status_t _cairo_glitz_surface_old_show_glyphs (cairo_scaled_font_t *scaled_font, cairo_operator_t op, const cairo_pattern_t *pattern, void *abstract_surface, int src_x, int src_y, int dst_x, int dst_y, unsigned int width, unsigned int height, cairo_glyph_t *glyphs, int num_glyphs, cairo_region_t *clip_region) { cairo_glitz_surface_attributes_t attributes; cairo_glitz_surface_glyph_private_t *glyph_private; cairo_glitz_surface_t *dst = abstract_surface; cairo_glitz_surface_t *src; cairo_scaled_glyph_t *stack_scaled_glyphs[N_STACK_BUF]; cairo_scaled_glyph_t **scaled_glyphs; glitz_float_t stack_vertices[N_STACK_BUF * 16]; glitz_float_t *vertices; glitz_buffer_t *buffer; cairo_int_status_t status; int x_offset, y_offset; int i, cached_glyphs = 0; int remaining_glyps = num_glyphs; glitz_float_t x1, y1, x2, y2; static const glitz_vertex_format_t format = { GLITZ_PRIMITIVE_QUADS, GLITZ_DATA_TYPE_FLOAT, sizeof (glitz_float_t) * 4, GLITZ_VERTEX_ATTRIBUTE_MASK_COORD_MASK, { 0 }, { GLITZ_DATA_TYPE_FLOAT, GLITZ_COORDINATE_SIZE_XY, sizeof (glitz_float_t) * 2, } }; if (scaled_font->surface_backend != NULL && scaled_font->surface_backend != _cairo_glitz_surface_get_backend ()) return CAIRO_INT_STATUS_UNSUPPORTED; if (! _is_supported_operator (op)) return CAIRO_INT_STATUS_UNSUPPORTED; /* XXX Unbounded operators are not handled correctly */ if (! _cairo_operator_bounded_by_mask (op)) return CAIRO_INT_STATUS_UNSUPPORTED; if (_glitz_ensure_target (dst->surface)) return CAIRO_INT_STATUS_UNSUPPORTED; status = _cairo_glitz_surface_set_clip_region (dst, NULL); if (unlikely (status)) return status; status = _cairo_glitz_pattern_acquire_surface (pattern, dst, src_x, src_y, width, height, &src, &attributes); if (status) return status; _cairo_glitz_surface_set_attributes (src, &attributes); if (num_glyphs > N_STACK_BUF) { char *data; size_t size1, size2; if ((size_t)num_glyphs >= INT32_MAX / sizeof(void*) || (size_t)num_glyphs >= INT32_MAX / sizeof(glitz_float_t) || ((size_t)num_glyphs * sizeof(glitz_float_t)) >= INT32_MAX / 16) goto FAIL1; size1 = num_glyphs * sizeof(void *); size2 = num_glyphs * sizeof(glitz_float_t) * 16; if (size1 >= INT32_MAX - size2) goto FAIL1; data = malloc (size1 + size2); if (!data) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); goto FAIL1; } scaled_glyphs = (cairo_scaled_glyph_t **) data; vertices = (glitz_float_t *) (data + num_glyphs * sizeof (void *)); } else { scaled_glyphs = stack_scaled_glyphs; vertices = stack_vertices; } buffer = glitz_buffer_create_for_data (vertices); if (!buffer) goto FAIL2; _cairo_scaled_font_freeze_cache (scaled_font); for (i = 0; i < num_glyphs; i++) { status = _cairo_scaled_glyph_lookup (scaled_font, glyphs[i].index, CAIRO_SCALED_GLYPH_INFO_SURFACE, &scaled_glyphs[i]); if (status != CAIRO_STATUS_SUCCESS) { num_glyphs = i; goto UNLOCK; } glyph_private = scaled_glyphs[i]->surface_private; if (!glyph_private || !glyph_private->area) { status = _cairo_glitz_surface_add_glyph (dst, scaled_font, scaled_glyphs[i]); if (status != CAIRO_STATUS_SUCCESS) { num_glyphs = i; goto UNLOCK; } } glyph_private = scaled_glyphs[i]->surface_private; if (glyph_private && glyph_private->area) { remaining_glyps--; if (glyph_private->area->width) { x_offset = scaled_glyphs[i]->surface->base.device_transform.x0; y_offset = scaled_glyphs[i]->surface->base.device_transform.y0; x1 = _cairo_lround (glyphs[i].x - x_offset); y1 = _cairo_lround (glyphs[i].y - y_offset); x2 = x1 + glyph_private->area->width; y2 = y1 + glyph_private->area->height; WRITE_BOX (vertices, x1, y1, x2, y2, &glyph_private->p1, &glyph_private->p2); glyph_private->locked = TRUE; cached_glyphs++; } } } if (remaining_glyps) { cairo_surface_t *image; cairo_glitz_surface_t *clone; for (i = 0; i < num_glyphs; i++) { glyph_private = scaled_glyphs[i]->surface_private; if (!glyph_private || !glyph_private->area) { int glyph_width, glyph_height; int clone_offset_x, clone_offset_y; image = &scaled_glyphs[i]->surface->base; glyph_width = scaled_glyphs[i]->surface->width; glyph_height = scaled_glyphs[i]->surface->height; status = _cairo_glitz_surface_clone_similar (abstract_surface, image, 0, 0, glyph_width, glyph_height, &clone_offset_x, &clone_offset_y, (cairo_surface_t **) &clone); if (status) goto UNLOCK; assert (clone_offset_x == 0); assert (clone_offset_y == 0); x_offset = scaled_glyphs[i]->surface->base.device_transform.x0; y_offset = scaled_glyphs[i]->surface->base.device_transform.y0; x1 = _cairo_lround (glyphs[i].x - x_offset); y1 = _cairo_lround (glyphs[i].y - y_offset); glitz_composite (_glitz_operator (op), src->surface, clone->surface, dst->surface, src_x + attributes.base.x_offset + x1, src_y + attributes.base.y_offset + y1, 0, 0, x1, y1, glyph_width, glyph_height); cairo_surface_destroy (&clone->base); if (glitz_surface_get_status (dst->surface) == GLITZ_STATUS_NOT_SUPPORTED) { status = CAIRO_INT_STATUS_UNSUPPORTED; goto UNLOCK; } } } } if (cached_glyphs) { cairo_glitz_surface_font_private_t *font_private; glitz_set_geometry (dst->surface, GLITZ_GEOMETRY_TYPE_VERTEX, (glitz_geometry_format_t *) &format, buffer); glitz_set_array (dst->surface, 0, 4, cached_glyphs * 4, 0, 0); font_private = scaled_font->surface_private; glitz_composite (_glitz_operator (op), src->surface, font_private->surface, dst->surface, src_x + attributes.base.x_offset, src_y + attributes.base.y_offset, 0, 0, dst_x, dst_y, width, height); glitz_set_geometry (dst->surface, GLITZ_GEOMETRY_TYPE_NONE, NULL, NULL); } UNLOCK: if (cached_glyphs) { for (i = 0; i < num_glyphs; i++) { glyph_private = scaled_glyphs[i]->surface_private; if (glyph_private) glyph_private->locked = FALSE; } } _cairo_scaled_font_thaw_cache (scaled_font); glitz_buffer_destroy (buffer); FAIL2: if (num_glyphs > N_STACK_BUF) free (scaled_glyphs); FAIL1: if (attributes.n_params) free (attributes.params); _cairo_glitz_pattern_release_surface (pattern, src, &attributes); if (status) return status; if (glitz_surface_get_status (dst->surface) == GLITZ_STATUS_NOT_SUPPORTED) return CAIRO_INT_STATUS_UNSUPPORTED; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_glitz_surface_flush (void *abstract_surface) { cairo_glitz_surface_t *surface = abstract_surface; glitz_surface_flush (surface->surface); return CAIRO_STATUS_SUCCESS; } static cairo_bool_t _cairo_glitz_surface_is_similar (void *surface_a, void *surface_b, cairo_content_t content) { cairo_glitz_surface_t *a = (cairo_glitz_surface_t *) surface_a; cairo_glitz_surface_t *b = (cairo_glitz_surface_t *) surface_b; glitz_drawable_t *drawable_a = glitz_surface_get_drawable (a->surface); glitz_drawable_t *drawable_b = glitz_surface_get_drawable (b->surface); /* XXX Disable caching of glitz surfaces by the solid pattern cache. * Until glitz has a mechanism for releasing resources on connection * closure, we will attempt to access invalid pointers when evicting * old surfaces from the solid pattern cache. */ return FALSE; return drawable_a == drawable_b; } static const cairo_surface_backend_t cairo_glitz_surface_backend = { CAIRO_SURFACE_TYPE_GLITZ, _cairo_glitz_surface_create_similar, _cairo_glitz_surface_finish, _cairo_glitz_surface_acquire_source_image, _cairo_glitz_surface_release_source_image, _cairo_glitz_surface_acquire_dest_image, _cairo_glitz_surface_release_dest_image, _cairo_glitz_surface_clone_similar, _cairo_glitz_surface_composite, _cairo_glitz_surface_fill_rectangles, _cairo_glitz_surface_composite_trapezoids, NULL, /* create_span_renderer */ NULL, /* check_span_renderer */ NULL, /* copy_page */ NULL, /* show_page */ _cairo_glitz_surface_get_extents, _cairo_glitz_surface_old_show_glyphs, NULL, /* get_font_options */ _cairo_glitz_surface_flush, NULL, /* mark_dirty_rectangle */ _cairo_glitz_surface_scaled_font_fini, _cairo_glitz_surface_scaled_glyph_fini, NULL, /* paint */ NULL, /* mask */ NULL, /* stroke */ NULL, /* fill */ NULL, /* show_glyphs */ _cairo_glitz_surface_snapshot, _cairo_glitz_surface_is_similar, }; static const cairo_surface_backend_t * _cairo_glitz_surface_get_backend (void) { return &cairo_glitz_surface_backend; } static cairo_content_t _glitz_format_to_content (glitz_format_t * format) { assert (format->color.fourcc == GLITZ_FOURCC_RGB); if (format->color.alpha_size != 0) { if (format->color.red_size != 0 && format->color.green_size != 0 && format->color.blue_size != 0) return CAIRO_CONTENT_COLOR_ALPHA; else return CAIRO_CONTENT_ALPHA; } return CAIRO_CONTENT_COLOR; } cairo_surface_t * cairo_glitz_surface_create (glitz_surface_t *surface) { cairo_glitz_surface_t *crsurface; glitz_format_t *format; if (surface == NULL) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NULL_POINTER)); crsurface = malloc (sizeof (cairo_glitz_surface_t)); if (crsurface == NULL) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); format = glitz_surface_get_format (surface); _cairo_surface_init (&crsurface->base, &cairo_glitz_surface_backend, _glitz_format_to_content (format)); glitz_surface_reference (surface); crsurface->surface = surface; crsurface->format = format; crsurface->has_clip = FALSE; crsurface->clip_boxes = NULL; crsurface->num_clip_boxes = 0; crsurface->clip_region = NULL; return &crsurface->base; } slim_hidden_def (cairo_glitz_surface_create);