/* 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" typedef struct _cairo_glitz_surface { cairo_surface_t base; glitz_surface_t *surface; glitz_format_t *format; cairo_bool_t has_clip; cairo_region_t clip; } 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->has_clip) { glitz_surface_set_clip_region (surface->surface, 0, 0, NULL, 0); _cairo_region_fini (&surface->clip); } 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 _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); 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_bool_t _CAIRO_MASK_FORMAT (cairo_format_masks_t *masks, cairo_format_t *format) { switch (masks->bpp) { case 32: if (masks->alpha_mask == 0xff000000 && masks->red_mask == 0x00ff0000 && masks->green_mask == 0x0000ff00 && masks->blue_mask == 0x000000ff) { *format = CAIRO_FORMAT_ARGB32; return TRUE; } if (masks->alpha_mask == 0x00000000 && masks->red_mask == 0x00ff0000 && masks->green_mask == 0x0000ff00 && masks->blue_mask == 0x000000ff) { *format = CAIRO_FORMAT_RGB24; return TRUE; } break; case 8: if (masks->alpha_mask == 0xff) { *format = CAIRO_FORMAT_A8; return TRUE; } break; case 1: if (masks->alpha_mask == 0x1) { *format = CAIRO_FORMAT_A1; return TRUE; } break; } return FALSE; } static cairo_status_t _cairo_glitz_get_boxes_from_region (cairo_region_t *region, glitz_box_t **boxes, int *nboxes) { cairo_box_int_t *cboxes; cairo_status_t status; int n, i; status = _cairo_region_get_boxes (region, &n, &cboxes); if (status) return status; if (n == 0) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto done; } *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 = cboxes[i].p1.x; (*boxes)[i].y1 = cboxes[i].p1.y; (*boxes)[i].x2 = cboxes[i].p2.x; (*boxes)[i].y2 = cboxes[i].p2.y; } *nboxes = n; done: _cairo_region_boxes_fini (region, cboxes); return status; } 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; int x1, y1, x2, y2; int width, height; unsigned char *pixels; cairo_format_masks_t masks; glitz_buffer_t *buffer; glitz_pixel_format_t pf; cairo_format_t format; x1 = 0; y1 = 0; x2 = glitz_surface_get_width (surface->surface); y2 = glitz_surface_get_height (surface->surface); if (interest) { if (interest->x > x1) x1 = interest->x; if (interest->y > y1) y1 = interest->y; if (interest->x + interest->width < x2) x2 = interest->x + interest->width; if (interest->y + interest->height < y2) y2 = interest->y + interest->height; if (x1 >= x2 || y1 >= y2) { *image_out = NULL; return CAIRO_STATUS_SUCCESS; } } width = x2 - x1; height = y2 - y1; if (rect_out) { rect_out->x = x1; rect_out->y = y1; rect_out->width = width; rect_out->height = height; } if (surface->format->color.fourcc == GLITZ_FOURCC_RGB) { if (surface->format->color.red_size > 0) { masks.bpp = 32; if (surface->format->color.alpha_size > 0) masks.alpha_mask = 0xff000000; else masks.alpha_mask = 0x0; masks.red_mask = 0xff0000; masks.green_mask = 0xff00; masks.blue_mask = 0xff; } else { masks.bpp = 8; masks.blue_mask = masks.green_mask = masks.red_mask = 0x0; masks.alpha_mask = 0xff; } } else { masks.bpp = 32; masks.alpha_mask = 0xff000000; masks.red_mask = 0xff0000; masks.green_mask = 0xff00; masks.blue_mask = 0xff; } 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 = (((width * masks.bpp) / 8) + 3) & -4; pf.scanline_order = GLITZ_PIXEL_SCANLINE_ORDER_TOP_DOWN; pixels = _cairo_malloc_ab (height, pf.bytes_per_line); if (!pixels) return _cairo_error (CAIRO_STATUS_NO_MEMORY); buffer = glitz_buffer_create_for_data (pixels); if (!buffer) { free (pixels); 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, x1, y1, width, height, &pf, buffer); glitz_buffer_destroy (buffer); /* restore the clip, if any */ if (surface->has_clip) { glitz_box_t *box; cairo_status_t status; int n; status = _cairo_glitz_get_boxes_from_region (&surface->clip, &box, &n); if (status) { free (pixels); return status; } glitz_surface_set_clip_region (surface->surface, 0, 0, box, n); free (box); } /* * Prefer to use a standard pixman format instead of the * general masks case. */ if (_CAIRO_MASK_FORMAT (&masks, &format)) { image = (cairo_image_surface_t *) cairo_image_surface_create_for_data (pixels, format, x2 - x1, y2 - y1, pf.bytes_per_line); if (image->base.status) goto FAIL; } else { /* * XXX This can't work. We must convert the data to one of the * supported pixman formats. Pixman needs another function * which takes data in an arbitrary format and converts it * to something supported by that library. */ image = (cairo_image_surface_t *) _cairo_image_surface_create_with_masks (pixels, &masks, x2 - x1, y2 - y1, pf.bytes_per_line); if (image->base.status) goto FAIL; } _cairo_image_surface_assume_ownership_of_data (image); *image_out = image; return CAIRO_STATUS_SUCCESS; FAIL: free (pixels); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } static void cairo_format_get_masks (cairo_format_t format, uint32_t *bpp, uint32_t *alpha, uint32_t *red, uint32_t *green, uint32_t *blue) { *red = 0x0; *green = 0x0; *blue = 0x0; *alpha = 0x0; switch (format) { case CAIRO_FORMAT_ARGB32: *alpha = 0xff000000; case CAIRO_FORMAT_RGB24: default: *bpp = 32; *red = 0x00ff0000; *green = 0x0000ff00; *blue = 0x000000ff; break; case CAIRO_FORMAT_A8: *bpp = 8; *alpha = 0xff; break; case CAIRO_FORMAT_A1: *bpp = 1; *alpha = 0x1; break; } } 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; uint32_t bpp, am, rm, gm, bm; char *data; cairo_format_get_masks (image->format, &bpp, &am, &rm, &gm, &bm); pf.fourcc = GLITZ_FOURCC_RGB; pf.masks.bpp = bpp; pf.masks.alpha_mask = am; pf.masks.red_mask = rm; pf.masks.green_mask = gm; pf.masks.blue_mask = bm; 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) 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 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, 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_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; cairo_content_t content; cairo_rectangle_int_t image_extent; cairo_rectangle_int_t extent; content = _cairo_content_from_format (image_src->format); clone = (cairo_glitz_surface_t *) _cairo_glitz_surface_create_similar (surface, content, image_src->width, image_src->height); if (clone->base.status) return clone->base.status; image_extent.x = 0; image_extent.y = 0; image_extent.width = image_src->width; image_extent.height = image_src->height; extent.x = src_x; extent.y = src_y; extent.width = width; extent.height = height; _cairo_rectangle_intersect(&extent, &image_extent); status = _cairo_glitz_surface_set_image (clone, image_src, extent.x, extent.y, extent.width, extent.height, extent.x, extent.y); if (status) { cairo_surface_destroy (&clone->base); return status; } *clone_out = &clone->base; 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_from_double (matrix->xx); transform.matrix[0][1] = _cairo_fixed_from_double (matrix->xy); transform.matrix[0][2] = _cairo_fixed_from_double (matrix->x0); transform.matrix[1][0] = _cairo_fixed_from_double (matrix->yx); transform.matrix[1][1] = _cairo_fixed_from_double (matrix->yy); transform.matrix[1][2] = _cairo_fixed_from_double (matrix->y0); transform.matrix[2][0] = 0; transform.matrix[2][1] = 0; transform.matrix[2][2] = _cairo_fixed_from_double (1); glitz_surface_set_transform (surface->surface, &transform); } static glitz_operator_t _glitz_operator (cairo_operator_t op) { switch (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; case CAIRO_OPERATOR_SATURATE: /* XXX: This line should never be reached. Glitz backend should bail out earlier if saturate operator is used. OpenGL can't do saturate with pre-multiplied colors. Solid colors can still be done as we can just un-pre-multiply them. However, support for that will have to be added to glitz. */ /* fall-through */ break; } 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 (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_surface_create_similar_scratch (&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] = gradient->stops[i].x; 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] = grad->p1.x; params[1] = grad->p1.y; params[2] = grad->p2.x; params[3] = grad->p2.y; attr->filter = GLITZ_FILTER_LINEAR_GRADIENT; } else { cairo_radial_pattern_t *grad = (cairo_radial_pattern_t *) pattern; params[0] = grad->c1.x; params[1] = grad->c1.y; params[2] = grad->r1; params[3] = grad->c2.x; params[4] = grad->c2.y; params[5] = 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_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 (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 (cairo_pattern_t *src, 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_pattern_union_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.solid, &combined, CAIRO_COLOR_IS_OPAQUE (&combined) ? CAIRO_CONTENT_COLOR : CAIRO_CONTENT_COLOR_ALPHA); mask = NULL; } else { status = _cairo_pattern_init_copy (&tmp.base, src); if (status) return status; } status = _cairo_glitz_pattern_acquire_surface (&tmp.base, dst, src_x, src_y, width, height, src_out, sattr); _cairo_pattern_fini (&tmp.base); if (status) return status; if (mask) { status = _cairo_pattern_init_copy (&tmp.base, mask); if (status) return status; status = _cairo_glitz_pattern_acquire_surface (&tmp.base, dst, mask_x, mask_y, width, height, mask_out, mattr); if (status) _cairo_glitz_pattern_release_surface (&tmp.base, *src_out, sattr); _cairo_pattern_fini (&tmp.base); 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_int_status_t _cairo_glitz_surface_composite (cairo_operator_t op, cairo_pattern_t *src_pattern, 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_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 (op == CAIRO_OPERATOR_SATURATE) return CAIRO_INT_STATUS_UNSUPPORTED; if (_glitz_ensure_target (dst->surface)) return CAIRO_INT_STATUS_UNSUPPORTED; 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); if (mask_attr.n_params) free (mask_attr.params); _cairo_glitz_pattern_release_surface (mask_pattern, mask, &mask_attr); } 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 (src_attr.n_params) free (src_attr.params); _cairo_glitz_pattern_release_surface (src_pattern, src, &src_attr); 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_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; switch (op) { case CAIRO_OPERATOR_SOURCE: { glitz_color_t glitz_color; glitz_color.red = color->red_short; glitz_color.green = color->green_short; glitz_color.blue = color->blue_short; glitz_color.alpha = color->alpha_short; glitz_set_rectangles (dst->surface, &glitz_color, (glitz_rectangle_t *) rects, n_rects); } break; case CAIRO_OPERATOR_CLEAR: { static const glitz_color_t glitz_color = { 0, 0, 0, 0 }; glitz_set_rectangles (dst->surface, &glitz_color, (glitz_rectangle_t *) 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)) 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, NULL); if (src->base.status) return src->base.status; glitz_surface_set_fill (src->surface, GLITZ_FILL_REPEAT); while (n_rects--) { glitz_composite (_glitz_operator (op), src->surface, NULL, dst->surface, 0, 0, 0, 0, rects->x, rects->y, rects->width, rects->height); rects++; } cairo_surface_destroy (&src->base); break; } 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, 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_pattern_union_t tmp_src_pattern; cairo_pattern_t *src_pattern; 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; if (antialias != CAIRO_ANTIALIAS_DEFAULT && antialias != CAIRO_ANTIALIAS_GRAY) return CAIRO_INT_STATUS_UNSUPPORTED; if (dst->base.status) return dst->base.status; if (op == CAIRO_OPERATOR_SATURATE) return CAIRO_INT_STATUS_UNSUPPORTED; if (_glitz_ensure_target (dst->surface)) return CAIRO_INT_STATUS_UNSUPPORTED; if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { status = _cairo_pattern_init_copy (&tmp_src_pattern.base, pattern); if (status) return status; status = _cairo_glitz_pattern_acquire_surface (&tmp_src_pattern.base, dst, src_x, src_y, width, height, &src, &attributes); src_pattern = &tmp_src_pattern.base; } else { status = _cairo_glitz_pattern_acquire_surface (pattern, dst, src_x, src_y, width, height, &src, &attributes); src_pattern = pattern; } alpha = 0xffff; if (status) return status; 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->base.status) { _cairo_glitz_pattern_release_surface (src_pattern, src, &attributes); if (src_pattern == &tmp_src_pattern.base) _cairo_pattern_fini (&tmp_src_pattern.base); return mask->base.status; } 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) { _cairo_glitz_pattern_release_surface (src_pattern, src, &attributes); if (src_pattern == &tmp_src_pattern.base) _cairo_pattern_fini (&tmp_src_pattern.base); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } data = p; if (buffer) glitz_buffer_destroy (buffer); buffer = glitz_buffer_create_for_data (data); if (!buffer) { free (data); _cairo_glitz_pattern_release_surface (src_pattern, src, &attributes); if (src_pattern == &tmp_src_pattern.base) _cairo_pattern_fini (&tmp_src_pattern.base); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } } offset += glitz_add_trapezoids (buffer, offset, size - offset, format.vertex.type, mask->surface, (glitz_trapezoid_t *) 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) { _cairo_glitz_pattern_release_surface (src_pattern, src, &attributes); if (src_pattern == &tmp_src_pattern.base) _cairo_pattern_fini (&tmp_src_pattern.base); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } /* 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); if (image->base.status) { cairo_surface_destroy (&src->base); free (data); return image->base.status; } pixman_add_trapezoids (image->pixman_image, -dst_x, -dst_y, n_traps, (pixman_trapezoid_t *) traps); mask = (cairo_glitz_surface_t *) _cairo_surface_create_similar_scratch (&dst->base, CAIRO_CONTENT_ALPHA, width, height); if (mask->base.status) { _cairo_glitz_pattern_release_surface (src_pattern, src, &attributes); free (data); cairo_surface_destroy (&image->base); return mask->base.status; } status = _cairo_glitz_surface_set_image (mask, image, 0, 0, width, height, 0, 0); cairo_surface_destroy(&image->base); if (status) return status; } _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); _cairo_glitz_pattern_release_surface (src_pattern, src, &attributes); if (src_pattern == &tmp_src_pattern.base) _cairo_pattern_fini (&tmp_src_pattern.base); if (mask) cairo_surface_destroy (&mask->base); 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_set_clip_region (void *abstract_surface, cairo_region_t *region) { cairo_glitz_surface_t *surface = abstract_surface; if (region) { glitz_box_t *box; int n; cairo_status_t status; if (!surface->has_clip) { _cairo_region_init (&surface->clip); surface->has_clip = TRUE; } status = _cairo_region_copy (&surface->clip, region); if (status) { _cairo_region_fini (&surface->clip); surface->has_clip = FALSE; return status; } status = _cairo_glitz_get_boxes_from_region (&surface->clip, &box, &n); if (status) { _cairo_region_fini (&surface->clip); surface->has_clip = FALSE; return status; } glitz_surface_set_clip_region (surface->surface, 0, 0, box, n); free (box); } else { glitz_surface_set_clip_region (surface->surface, 0, 0, NULL, 0); if (surface->has_clip) { _cairo_region_fini (&surface->clip); surface->has_clip = FALSE; } } return CAIRO_STATUS_SUCCESS; } static cairo_int_status_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 CAIRO_STATUS_SUCCESS; } #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; unsigned int bpp, am, rm, gm, bm; 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_STATUS_SUCCESS; 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); } cairo_format_get_masks (glyph_surface->format, &bpp, &am, &rm, &gm, &bm); pf.fourcc = GLITZ_FOURCC_RGB; pf.masks.bpp = bpp; pf.masks.alpha_mask = am; pf.masks.red_mask = rm; pf.masks.green_mask = gm; pf.masks.blue_mask = bm; 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, 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_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 (op == CAIRO_OPERATOR_SATURATE) return CAIRO_INT_STATUS_UNSUPPORTED; if (_glitz_ensure_target (dst->surface)) return CAIRO_INT_STATUS_UNSUPPORTED; 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; 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, (cairo_surface_t **) &clone); if (status) goto UNLOCK; 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); return drawable_a == drawable_b; } static cairo_status_t _cairo_glitz_surface_reset (void *abstract_surface) { cairo_glitz_surface_t *surface = abstract_surface; cairo_status_t status; status = _cairo_glitz_surface_set_clip_region (surface, NULL); if (status) return status; return CAIRO_STATUS_SUCCESS; } 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, /* copy_page */ NULL, /* show_page */ _cairo_glitz_surface_set_clip_region, NULL, /* intersect_clip_path */ _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 */ NULL, /* snapshot */ _cairo_glitz_surface_is_similar, _cairo_glitz_surface_reset }; 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; return (cairo_surface_t *) crsurface; } slim_hidden_def (cairo_glitz_surface_create);