/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2003 University of Southern California * Copyright © 2009,2010,2011 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth * Chris Wilson */ #include "cairoint.h" #include "cairo-pixman.h" #include "cairo-default-context-private.h" #include "cairo-image-surface-private.h" #include "cairo-pattern-private.h" #include "cairo-clip-inline.h" #include "cairo-traps-private.h" #include "cairoint.h" #include "cairo-region-private.h" extern const cairo_surface_backend_t cairo_pixman_surface_backend; typedef struct _cairo_pixman_surface cairo_pixman_surface_t; struct _cairo_pixman_surface { cairo_surface_t base; pixman_image_t * pimage; }; /** * SECTION:cairo-pixman * @Title: Pixman Surfaces * @Short_Description: Rendering to pixman images * @See_Also: #cairo_surface_t * * Pixman surfaces provide the ability to render to pixman images * allocated by the calling code. The supported pixman formats are * those for which pixman_supported_destination() returns TRUE. */ /** * CAIRO_HAS_PIXMAN_SURFACE: * * Defined if the pixman surface backend is available. * * @Since: 1.14 */ static cairo_content_t content_from_pixman_image (pixman_image_t *image) { pixman_format_code_t format = pixman_image_get_format (image); cairo_content_t result; result = 0; if (PIXMAN_FORMAT_RGB (format)) result |= CAIRO_CONTENT_COLOR; if (PIXMAN_FORMAT_A (format)) result |= CAIRO_CONTENT_ALPHA; return result; } /** * cairo_pixman_surface_create: * @image: a pixman image * * Creates an pixman surface based on the given image * dimensions. Initially the surface contents are all * 0. (Specifically, within each pixel, each color or alpha channel * belonging to format will be 0. The contents of bits within a pixel, * but not belonging to the given format are undefined). * * Return value: a pointer to the newly created surface. The caller * owns the surface and should call cairo_surface_destroy() when done * with it. * * This function always returns a valid pointer, but it will return a * pointer to a "nil" surface if an error such as out of memory * occurs. You can use cairo_surface_status() to check for this. **/ cairo_surface_t * cairo_pixman_surface_create (pixman_image_t *image) { cairo_pixman_surface_t *surface; pixman_format_code_t format; if (!(surface = malloc (sizeof *surface))) { return _cairo_surface_create_in_error ( _cairo_error (CAIRO_STATUS_NO_MEMORY)); } format = pixman_image_get_format (image); if (!format || !pixman_format_supported_destination (format)) { /* FIXME: This error status is intended for incorrect * cairo_format_ts, not incorrect pixman formats. In * principle a new error status should be added */ return _cairo_surface_create_in_error ( _cairo_error (CAIRO_STATUS_INVALID_FORMAT)); } _cairo_surface_init (&surface->base, &cairo_pixman_surface_backend, NULL, /* device */ content_from_pixman_image (image)); surface->pimage = pixman_image_ref (image); return (cairo_surface_t *)surface; } static cairo_status_t cairo_pixman_surface_finish (void *abstract_surface) { cairo_pixman_surface_t *surface = abstract_surface; pixman_image_unref (surface->pimage); surface->pimage = NULL; return CAIRO_STATUS_SUCCESS; } static cairo_surface_t * cairo_pixman_surface_create_similar (void *abstract_other, cairo_content_t content, int width, int height) { cairo_pixman_surface_t *other = abstract_other; pixman_format_code_t format; pixman_image_t *copy; format = pixman_image_get_format (other->pimage); copy = pixman_image_create_bits (format, width, height, NULL, -1); return cairo_pixman_surface_create (copy); } static cairo_surface_t * create_image_surface (cairo_pixman_surface_t *psurface) { pixman_image_t *pimage = psurface->pimage; pixman_format_code_t pformat = pixman_image_get_format (pimage); cairo_bool_t must_copy = FALSE; cairo_surface_t *image; cairo_format_t format; switch (pformat) { case PIXMAN_a8r8g8b8: format = CAIRO_FORMAT_ARGB32; break; case PIXMAN_x8r8g8b8: format = CAIRO_FORMAT_RGB24; break; case PIXMAN_a8: format = CAIRO_FORMAT_A8; break; case PIXMAN_a1: format = CAIRO_FORMAT_A1; break; case PIXMAN_r5g6b5: format = CAIRO_FORMAT_RGB16_565; break; case PIXMAN_x2r10g10b10: format = CAIRO_FORMAT_RGB30; break; case PIXMAN_a8b8g8r8: case PIXMAN_x8b8g8r8: case PIXMAN_b8g8r8a8: case PIXMAN_b8g8r8x8: case PIXMAN_r8g8b8a8: case PIXMAN_r8g8b8x8: case PIXMAN_x14r6g6b6: case PIXMAN_a2r10g10b10: case PIXMAN_x2b10g10r10: case PIXMAN_a2b10g10r10: case PIXMAN_a8r8g8b8_sRGB: case PIXMAN_r8g8b8: case PIXMAN_b8g8r8: case PIXMAN_b5g6r5: case PIXMAN_a1r5g5b5: case PIXMAN_x1r5g5b5: case PIXMAN_a1b5g5r5: case PIXMAN_x1b5g5r5: case PIXMAN_a4r4g4b4: case PIXMAN_x4r4g4b4: case PIXMAN_a4b4g4r4: case PIXMAN_x4b4g4r4: case PIXMAN_r3g3b2: case PIXMAN_b2g3r3: case PIXMAN_a2r2g2b2: case PIXMAN_a2b2g2r2: case PIXMAN_g8: case PIXMAN_x4a4: case PIXMAN_a4: case PIXMAN_r1g2b1: case PIXMAN_c8: case PIXMAN_b1g2r1: case PIXMAN_a1r1g1b1: case PIXMAN_a1b1g1r1: case PIXMAN_c4: case PIXMAN_g4: case PIXMAN_g1: case PIXMAN_yuy2: case PIXMAN_yv12: default: format = CAIRO_FORMAT_ARGB32; must_copy = TRUE; break; } if (must_copy) { int width = pixman_image_get_width (pimage); int height = pixman_image_get_height (pimage); pixman_image_t *dest; image = cairo_image_surface_create (format, width, height); dest = ((cairo_image_surface_t *)image)->pixman_image; pixman_image_composite32 (PIXMAN_OP_SRC, pimage, NULL, dest, 0, 0, 0, 0, 0, 0, width, height); } else { image = _cairo_image_surface_create_for_pixman_image (pimage, pformat); } return image; } static cairo_image_surface_t * cairo_pixman_surface_map_to_image (void *abstract_other, const cairo_rectangle_int_t *extents) { cairo_pixman_surface_t *surface = abstract_other; /* FIXME: if we need to copy, we should only copy extents */ return (cairo_image_surface_t *)create_image_surface (surface); } static cairo_int_status_t cairo_pixman_surface_unmap_image (void *abstract_surface, cairo_image_surface_t *image) { cairo_pixman_surface_t *surface = abstract_surface; if (image->pixman_image != surface->pimage) { pixman_image_composite32 (PIXMAN_OP_SRC, image->pixman_image, NULL, surface->pimage, 0, 0, 0, 0, 0, 0, pixman_image_get_width (surface->pimage), pixman_image_get_height (surface->pimage)); } return CAIRO_STATUS_SUCCESS; } static cairo_surface_t * cairo_pixman_surface_source (void *abstract_surface, cairo_rectangle_int_t *extents) { cairo_pixman_surface_t *surface = abstract_surface; extents->x = extents->y = 0; extents->width = pixman_image_get_width (surface->pimage); extents->height = pixman_image_get_height (surface->pimage); return &surface->base; } static cairo_status_t cairo_pixman_surface_acquire_source_image (void *abstract_surface, cairo_image_surface_t **image_out, void **image_extra) { cairo_pixman_surface_t *psurface = abstract_surface; *image_out = (cairo_image_surface_t *)create_image_surface (psurface); *image_extra = NULL; /* FIXME: create_image_surface can fail (or should be able to fail) */ return CAIRO_STATUS_SUCCESS; } static void cairo_pixman_surface_release_source_image (void *abstract_surface, cairo_image_surface_t *image, void *image_extra) { cairo_surface_destroy ((cairo_surface_t *)image); } static cairo_surface_t * cairo_pixman_surface_snapshot (void *abstract_surface) { cairo_pixman_surface_t *psurface = abstract_surface; return create_image_surface (psurface); } static cairo_bool_t cairo_pixman_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *rectangle) { cairo_pixman_surface_t *psurface = abstract_surface; rectangle->x = 0; rectangle->y = 0; rectangle->width = pixman_image_get_width (psurface->pimage); rectangle->height = pixman_image_get_height (psurface->pimage); return CAIRO_STATUS_SUCCESS; } static void cairo_pixman_surface_get_font_options (void *abstract_surface, cairo_font_options_t *options) { _cairo_font_options_init_default (options); cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_ON); _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_ON); } typedef enum { NOP, NEW_BLANK, NEW_BROKEN, NEW_WHITE, NEW_IMAGE, NEW_REGION, NEW_TRAPS, NEW_GLYPHS, COMPOSITE } command_type_t; typedef union { command_type_t type; /* Create a new image 'id' with the same * format as "based on" */ struct { command_type_t type; int id; int based_on; } new_blank; struct { command_type_t type; int id; } new_white; struct { command_type_t type; } new_broken; struct { command_type_t type; int id; pixman_image_t * image; } new_image; struct { command_type_t type; int id; pixman_region32_t * region; } new_region; struct { command_type_t type; int id; cairo_antialias_t antialias; int n_traps; pixman_trapezoid_t * traps; } new_traps; struct { command_type_t type; int id; pixman_glyph_cache_t * glyph_cache; int n_glyphs; pixman_glyph_t * glyphs; } new_glyphs; struct { command_type_t type; int dest; pixman_op_t op; int src; int mask; /* Generated by optimizer */ pixman_region32_t * clip; } composite; } command_t; typedef struct command_buffer_t command_buffer_t; struct command_buffer_t { int oom; int n_commands; int n_allocated; int id; command_t commands[1]; }; static command_buffer_t * command_buffer_ensure (command_buffer_t *buffer, int n_more) { int required; if (buffer->oom) return buffer; required = buffer->n_commands + n_more; if (required > buffer->n_allocated) { command_buffer_t *new_buffer; int pot = 1; while (pot < required) pot *= 2; new_buffer = realloc ( buffer, sizeof (command_buffer_t) + pot * sizeof (command_t)); if (!new_buffer) { buffer->oom = 1; return buffer; } new_buffer->n_allocated = pot; buffer = new_buffer; } return buffer; } static void command_buffer_clear (command_buffer_t *buffer) { /* FIXME: walk the commands and free contents */ buffer->oom = 0; buffer->n_commands = 0; buffer->n_allocated = 1; buffer->id = 0; } static command_buffer_t * command_buffer_new (void) { command_buffer_t *buffer = malloc (sizeof (command_buffer_t)); if (!buffer) return NULL; command_buffer_clear (buffer); return buffer; } static void command_buffer_free (command_buffer_t *buffer) { command_buffer_clear (buffer); free (buffer); } static command_buffer_t * command_buffer_append (command_buffer_t *buffer, command_t **command, command_type_t type) { if (buffer->oom) { /* Just overwrite the first command since * the buffer is unusable anyway */ *command = &(buffer->commands[0]); return buffer; } buffer = command_buffer_ensure (buffer, 1); *command = buffer->commands + buffer->n_commands++; (*command)->type = type; return buffer; } static int command_buffer_new_white (command_buffer_t **buffer) { command_t *command; *buffer = command_buffer_append (*buffer, &command, NEW_WHITE); command->new_white.id = (*buffer)->id++; return command->new_white.id; } static int command_buffer_new_blank (command_buffer_t **buffer, int based_on) { command_t *command; *buffer = command_buffer_append (*buffer, &command, NEW_BLANK); command->new_blank.id = (*buffer)->id++; command->new_blank.based_on = based_on; return command->new_blank.id; } static int command_buffer_new_broken (command_buffer_t **buffer) { command_t *command; *buffer = command_buffer_append (*buffer, &command, NEW_BROKEN); return -1; } static int command_buffer_new_image (command_buffer_t **buffer, pixman_image_t *image) { command_t *command; *buffer = command_buffer_append (*buffer, &command, NEW_IMAGE); command->new_image.id = (*buffer)->id++; command->new_image.image = image; return command->new_image.id; } static int command_buffer_new_region (command_buffer_t **buffer, pixman_region32_t *region) { command_t *command; *buffer = command_buffer_append (*buffer, &command, NEW_REGION); command->new_region.id = (*buffer)->id++; command->new_region.region = region; return command->new_region.id; } static int command_buffer_new_traps (command_buffer_t **buffer, cairo_antialias_t antialias, int n_traps, pixman_trapezoid_t *traps) { command_t *command; *buffer = command_buffer_append (*buffer, &command, NEW_TRAPS); command->new_traps.id = (*buffer)->id++; command->new_traps.antialias = antialias; command->new_traps.n_traps = n_traps; command->new_traps.traps = traps; return command->new_traps.id; } static int command_buffer_new_glyphs (command_buffer_t **buffer, pixman_glyph_cache_t *glyph_cache, int n_glyphs, pixman_glyph_t *glyphs) { command_t *command; *buffer = command_buffer_append (*buffer, &command, NEW_GLYPHS); command->new_glyphs.id = (*buffer)->id++; command->new_glyphs.glyph_cache = glyph_cache; command->new_glyphs.n_glyphs = n_glyphs; command->new_glyphs.glyphs = glyphs; return command->new_glyphs.id; } static void command_buffer_composite (command_buffer_t **buffer, int dest, pixman_op_t op, int src, int mask) { command_t *command; *buffer = command_buffer_append (*buffer, &command, COMPOSITE); command->composite.dest = dest; command->composite.src = src; command->composite.mask = mask; command->composite.op = op; command->composite.clip = NULL; } static const pixman_color_t white = { 0xffff, 0xffff, 0xffff, 0xffff }; static void command_buffer_optimize (command_buffer_t *buffer) { typedef struct { enum { UNKNOWN, BLANK, REGION, WHITE, IMAGE, TRAPS, GLYPHS, CLIPPED_MASK, } type; int same_as; union { pixman_region32_t *region; struct { int mask; pixman_region32_t *clip; } clipped_mask; } u; } image_info_t; image_info_t *info; cairo_bool_t changed; int i; if (!(info = calloc (sizeof (image_info_t), buffer->id))) return; for (i = 0; i < buffer->id; ++i) { image_info_t *image = &(info[i]); image->type = UNKNOWN; image->same_as = -1; } for (i = 0; i < buffer->n_commands; ++i) { command_t *command = &(buffer->commands[i]); switch (command->type) { case NEW_BLANK: info[command->new_blank.id].type = BLANK; break; case NEW_WHITE: info[command->new_white.id].type = WHITE; break; case NEW_IMAGE: info[command->new_image.id].type = IMAGE; break; case NEW_REGION: info[command->new_region.id].type = REGION; info[command->new_region.id].u.region = command->new_region.region; break; case NEW_TRAPS: info[command->new_traps.id].type = TRAPS; break; case NEW_GLYPHS: info[command->new_glyphs.id].type = GLYPHS; break; case NOP: case NEW_BROKEN: case COMPOSITE: default: break; } } do { changed = FALSE; for (i = 0; i < buffer->n_commands; ++i) { command_t *command = &(buffer->commands[i]); int src, mask, dest; switch (command->type) { case NOP: break; case COMPOSITE: src = command->composite.src; mask = command->composite.mask; dest = command->composite.dest; if (info[src].same_as != -1) { src = command->composite.src = info[src].same_as; changed = TRUE; } if (info[mask].same_as != -1) { mask = command->composite.mask = info[mask].same_as; changed = TRUE; } if (info[dest].same_as != -1) { dest = command->composite.dest = info[dest].same_as; changed = TRUE; } if (command->composite.op == PIXMAN_OP_SRC) { if (info[src].type == REGION && info[mask].type == WHITE && info[dest].type == BLANK && !command->composite.clip) { info[dest].same_as = src; command->type = NOP; changed = TRUE; } else if (info[src].type == REGION && info[dest].type == BLANK && !command->composite.clip) { int j; int white = -1; for (j = 0; j < i; ++j) { image_info_t *in = &(info[j]); if (in->type == WHITE) { white = j; break; } } if (white != -1) { printf ("tick\n"); command->composite.clip = info[src].u.region; src = command->composite.src = white; } changed = TRUE; } else if (info[src].type == WHITE && info[dest].type == BLANK && command->composite.clip) { info[dest].type = CLIPPED_MASK; info[dest].u.clipped_mask.mask = mask; info[dest].u.clipped_mask.clip = command->composite.clip; changed = TRUE; } } else if (command->composite.op == PIXMAN_OP_OVER || command->composite.op == PIXMAN_OP_ADD) { if (info[mask].type == CLIPPED_MASK && !command->composite.clip) { command->composite.mask = info[mask].u.clipped_mask.mask; command->composite.clip = info[mask].u.clipped_mask.clip; changed = TRUE; } } } } } while (changed); out: free (info); } static cairo_int_status_t command_buffer_process (command_buffer_t *buffer, int width, int height) { pixman_image_t **images = NULL; pixman_image_t *white_img = NULL; cairo_int_status_t status; int i; int n_images; status = CAIRO_INT_STATUS_NO_MEMORY; if (buffer->oom) goto out; command_buffer_optimize (buffer); n_images = buffer->id; if (!(images = calloc (sizeof (pixman_image_t *), n_images))) goto out; if (!(white_img = pixman_image_create_solid_fill (&white))) goto out; for (i = 0; i < buffer->n_commands; ++i) { command_t *command = &(buffer->commands[i]); pixman_image_t *img; pixman_format_code_t format; switch (command->type) { case NOP: printf ("nop\n"); break; case NEW_BLANK: printf ("%d => blank\n", command->new_blank.id); img = images[command->new_blank.based_on]; format = pixman_image_get_format (img); if (format == 0) format = PIXMAN_a8r8g8b8; /* FIXME: set to dest format */ img = pixman_image_create_bits (format, width, height, NULL, -1); if (!img) goto out; images[command->new_blank.id] = img; break; case NEW_BROKEN: printf ("Broken!\n"); goto out; case NEW_WHITE: printf ("%d => white\n", command->new_white.id); images[command->new_white.id] = pixman_image_ref (white_img); break; case NEW_IMAGE: printf ("%d => image\n", command->new_image.id); images[command->new_image.id] = pixman_image_ref (command->new_image.image); break; case NEW_REGION: printf ("%d => region\n", command->new_region.id); if (!(img = pixman_image_create_bits ( PIXMAN_a8, width, height, NULL, -1))) { goto out; } pixman_image_set_clip_region32 (img, command->new_region.region); pixman_image_composite32 (PIXMAN_OP_SRC, white_img, NULL, img, 0, 0, 0, 0, 0, 0, width, height); images[command->new_region.id] = img; break; case NEW_TRAPS: printf ("%d => traps\n", command->new_traps.id); switch (command->new_traps.antialias) { case CAIRO_ANTIALIAS_NONE: format = PIXMAN_a1; break; case CAIRO_ANTIALIAS_FAST: format = PIXMAN_a4; break; default: case CAIRO_ANTIALIAS_GRAY: case CAIRO_ANTIALIAS_DEFAULT: case CAIRO_ANTIALIAS_SUBPIXEL: case CAIRO_ANTIALIAS_GOOD: case CAIRO_ANTIALIAS_BEST: format = PIXMAN_a8; break; } if (!(img = pixman_image_create_bits ( format, width, height, NULL, -1))) { goto out; } pixman_add_trapezoids ( img, 0, 0, command->new_traps.n_traps, command->new_traps.traps); images[command->new_traps.id] = img; break; case NEW_GLYPHS: printf ("%d => glyphs\n", command->new_traps.id); format = pixman_glyph_get_mask_format ( command->new_glyphs.glyph_cache, command->new_glyphs.n_glyphs, command->new_glyphs.glyphs); img = pixman_image_create_bits (format, width, height, NULL, -1); if (!img) goto out; pixman_composite_glyphs ( PIXMAN_OP_SRC, white_img, img, format, 0, 0, 0, 0, 0, 0, width, height, command->new_glyphs.glyph_cache, command->new_glyphs.n_glyphs, command->new_glyphs.glyphs); images[command->new_glyphs.id] = img; break; case COMPOSITE: printf ("composite %d (src: %d mask: %d dest: %d) clip: %d\n", command->composite.op, command->composite.src, command->composite.mask, command->composite.dest, command->composite.clip? pixman_region32_n_rects (command->composite.clip) : -1); if (command->composite.clip) { pixman_image_set_clip_region32 ( images[command->composite.dest], command->composite.clip); } pixman_image_composite32 ( command->composite.op, images[command->composite.src], images[command->composite.mask], images[command->composite.dest], 0, 0, 0, 0, 0, 0, width, height); if (command->composite.clip) { pixman_image_set_clip_region32 ( images[command->composite.dest], NULL); } break; } } printf ("- done -\n"); status = CAIRO_INT_STATUS_SUCCESS; out: if (images) { for (i = 0; i < n_images; ++i) { if (images[i]) pixman_image_unref (images[i]); } free (images); } if (white_img) pixman_image_unref (white_img); return status; } static void set_properties (pixman_image_t *image, cairo_pattern_t *pattern, cairo_matrix_t *matrix) { pixman_transform_t transform; pixman_filter_t filter; pixman_repeat_t repeat; if (!matrix) matrix = &(pattern->matrix); /* 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] = pixman_fixed_1; pixman_image_set_transform (image, &transform); /* Filter */ switch (pattern->filter) { case CAIRO_FILTER_FAST: filter = PIXMAN_FILTER_FAST; break; case CAIRO_FILTER_BEST: filter = PIXMAN_FILTER_BEST; break; case CAIRO_FILTER_NEAREST: filter = PIXMAN_FILTER_NEAREST; break; case CAIRO_FILTER_BILINEAR: filter = PIXMAN_FILTER_BILINEAR; break; case CAIRO_FILTER_GAUSSIAN: case CAIRO_FILTER_GOOD: default: filter = PIXMAN_FILTER_GOOD; break; } pixman_image_set_filter (image, filter, NULL, -1); /* Repeat mode */ switch (pattern->extend) { default: case CAIRO_EXTEND_NONE: repeat = PIXMAN_REPEAT_NONE; break; case CAIRO_EXTEND_REPEAT: repeat = PIXMAN_REPEAT_NORMAL; break; case CAIRO_EXTEND_REFLECT: repeat = PIXMAN_REPEAT_REFLECT; break; case CAIRO_EXTEND_PAD: repeat = PIXMAN_REPEAT_PAD; break; } pixman_image_set_repeat (image, repeat); pixman_image_set_component_alpha (image, pattern->has_component_alpha); } static int pimage_from_solid_pattern (command_buffer_t **buffer, cairo_solid_pattern_t *solid) { pixman_color_t pcolor; pixman_image_t *image; pcolor.red = _cairo_color_double_to_short (solid->color.red); pcolor.green = _cairo_color_double_to_short (solid->color.green); pcolor.blue = _cairo_color_double_to_short (solid->color.blue); pcolor.alpha = _cairo_color_double_to_short (solid->color.alpha); if (!(image = pixman_image_create_solid_fill (&pcolor))) return command_buffer_new_broken (buffer); return command_buffer_new_image (buffer, image); } typedef struct acquire_source_cleanup_t { cairo_surface_t * surface; cairo_image_surface_t * image; void * extra; } acquire_source_cleanup_t; static void clean_up_acquire (pixman_image_t *image, void *closure) { acquire_source_cleanup_t *info = closure; if (info) { if (info->image) { _cairo_surface_release_source_image ( info->surface, info->image, info->extra); } free (info); } } static int pimage_from_surface_pattern (command_buffer_t **buffer, cairo_surface_pattern_t *pattern) { pixman_image_t *simage, *image; acquire_source_cleanup_t *info = NULL; cairo_int_status_t status; /* First, get a pixman image that has the right bits */ switch (pattern->surface->type) { case CAIRO_SURFACE_TYPE_PIXMAN: simage = ((cairo_pixman_surface_t *)pattern->surface)->pimage; break; case CAIRO_SURFACE_TYPE_IMAGE: simage = ((cairo_image_surface_t *)pattern->surface)->pixman_image; break; case CAIRO_SURFACE_TYPE_PDF: case CAIRO_SURFACE_TYPE_PS: case CAIRO_SURFACE_TYPE_XLIB: case CAIRO_SURFACE_TYPE_XCB: case CAIRO_SURFACE_TYPE_GLITZ: case CAIRO_SURFACE_TYPE_QUARTZ: case CAIRO_SURFACE_TYPE_WIN32: case CAIRO_SURFACE_TYPE_BEOS: case CAIRO_SURFACE_TYPE_DIRECTFB: case CAIRO_SURFACE_TYPE_SVG: case CAIRO_SURFACE_TYPE_OS2: case CAIRO_SURFACE_TYPE_WIN32_PRINTING: case CAIRO_SURFACE_TYPE_QUARTZ_IMAGE: case CAIRO_SURFACE_TYPE_SCRIPT: case CAIRO_SURFACE_TYPE_QT: case CAIRO_SURFACE_TYPE_RECORDING: case CAIRO_SURFACE_TYPE_VG: case CAIRO_SURFACE_TYPE_GL: case CAIRO_SURFACE_TYPE_DRM: case CAIRO_SURFACE_TYPE_TEE: case CAIRO_SURFACE_TYPE_XML: case CAIRO_SURFACE_TYPE_SKIA: case CAIRO_SURFACE_TYPE_SUBSURFACE: case CAIRO_SURFACE_TYPE_COGL: default: if (!(info = malloc (sizeof (acquire_source_cleanup_t)))) return command_buffer_new_broken (buffer); info->surface = pattern->surface; info->image = NULL; info->extra = NULL; status = _cairo_surface_acquire_source_image ( pattern->surface, &info->image, &info->extra); if (status != CAIRO_INT_STATUS_SUCCESS) { clean_up_acquire (NULL, info); return command_buffer_new_broken (buffer); } simage = info->image->pixman_image; break; } /* Then create a clone of that image */ if (!(image = pixman_image_create_bits ( pixman_image_get_format (simage), pixman_image_get_width (simage), pixman_image_get_height (simage), pixman_image_get_data (simage), pixman_image_get_stride (simage)))) { clean_up_acquire (NULL, info); return command_buffer_new_broken (buffer); } if (info) pixman_image_set_destroy_function (image, clean_up_acquire, info); /* Then set the right properties on the clone */ set_properties (image, (cairo_pattern_t *)pattern, NULL); return command_buffer_new_image (buffer, image); } #define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */ static int pimage_from_gradient_pattern (command_buffer_t **buffer, const cairo_gradient_pattern_t *pattern) { pixman_gradient_stop_t *pstops; cairo_circle_double_t extremes[2]; pixman_point_fixed_t p1, p2; cairo_int_status_t status; cairo_matrix_t matrix; pixman_image_t *image; unsigned int i; if (!(pstops = malloc (pattern->n_stops * sizeof (pixman_gradient_stop_t)))) return command_buffer_new_broken (buffer); for (i = 0; i < pattern->n_stops; i++) { pstops[i].x = _cairo_fixed_16_16_from_double (pattern->stops[i].offset); pstops[i].color.red = pattern->stops[i].color.red_short; pstops[i].color.green = pattern->stops[i].color.green_short; pstops[i].color.blue = pattern->stops[i].color.blue_short; pstops[i].color.alpha = pattern->stops[i].color.alpha_short; } _cairo_gradient_pattern_fit_to_range ( pattern, PIXMAN_MAX_INT >> 1, &matrix, extremes); p1.x = _cairo_fixed_16_16_from_double (extremes[0].center.x); p1.y = _cairo_fixed_16_16_from_double (extremes[0].center.y); p2.x = _cairo_fixed_16_16_from_double (extremes[1].center.x); p2.y = _cairo_fixed_16_16_from_double (extremes[1].center.y); status = CAIRO_INT_STATUS_SUCCESS; if (pattern->base.type == CAIRO_PATTERN_TYPE_LINEAR) { image = pixman_image_create_linear_gradient ( &p1, &p2, pstops, pattern->n_stops); } else /* CAIRO_PATTERN_TYPE_RADIAL */ { pixman_fixed_t r1, r2; r1 = _cairo_fixed_16_16_from_double (extremes[0].radius); r2 = _cairo_fixed_16_16_from_double (extremes[1].radius); image = pixman_image_create_radial_gradient ( &p1, &p2, r1, r2, pstops, pattern->n_stops); } free (pstops); if (!image) return command_buffer_new_broken (buffer); set_properties (image, (cairo_pattern_t *)pattern, &matrix); return command_buffer_new_image (buffer, image); } static int pimage_from_mesh_pattern (command_buffer_t **buffer, cairo_pixman_surface_t *surface, const cairo_mesh_pattern_t *pattern) { int width, height; pixman_image_t *image; width = pixman_image_get_width (surface->pimage); height = pixman_image_get_height (surface->pimage); image = pixman_image_create_bits (PIXMAN_a8r8g8b8, width, height, NULL, 0); if (!image) return command_buffer_new_broken (buffer); _cairo_mesh_pattern_rasterize (pattern, pixman_image_get_data (image), width, height, pixman_image_get_stride (image), 0, 0); return command_buffer_new_image (buffer, image); } typedef struct { const cairo_pattern_t *pattern; cairo_surface_t *surface; cairo_image_surface_t *image; void *extra; } raster_info_t; static void raster_source_clean_up (pixman_image_t *pixman_image, void *closure) { raster_info_t *info = closure; _cairo_surface_release_source_image (info->surface, info->image, info->extra); _cairo_raster_source_pattern_release (info->pattern, info->surface); free (info); } static int pimage_from_raster_source_pattern (command_buffer_t **buffer, cairo_pixman_surface_t *psurface, const cairo_raster_source_pattern_t *pattern) { int width, height; cairo_surface_t *surface; cairo_image_surface_t *image; raster_info_t *info; cairo_int_status_t status; pixman_image_t *result; void *extra; width = pixman_image_get_width (psurface->pimage); height = pixman_image_get_height (psurface->pimage); surface = _cairo_raster_source_pattern_acquire ( &pattern->base, &psurface->base, NULL); if (surface == NULL || surface->status) return command_buffer_new_broken (buffer); status = _cairo_surface_acquire_source_image (surface, &image, &extra); if (status != CAIRO_INT_STATUS_SUCCESS) { _cairo_raster_source_pattern_release (&pattern->base, surface); return command_buffer_new_broken (buffer); } if (!(result = pixman_image_create_bits ( image->pixman_format, image->width, image->height, (uint32_t *) image->data, image->stride))) { _cairo_surface_release_source_image (surface, image, extra); _cairo_raster_source_pattern_release (&pattern->base, surface); return command_buffer_new_broken (buffer); } if (!(info = malloc (sizeof (*info)))) { pixman_image_unref (result); _cairo_surface_release_source_image (surface, image, extra); _cairo_raster_source_pattern_release (&pattern->base, surface); return command_buffer_new_broken (buffer); } info->pattern = &pattern->base; info->surface = surface; info->image = image; info->extra = extra; pixman_image_set_destroy_function ( result, raster_source_clean_up, info); set_properties (result, (cairo_pattern_t *)pattern, NULL); return command_buffer_new_image (buffer, result); } static int pimage_from_pattern (command_buffer_t **buffer, cairo_pixman_surface_t *surface, const cairo_pattern_t *pattern) { switch (pattern->type) { case CAIRO_PATTERN_TYPE_SOLID: return pimage_from_solid_pattern ( buffer, (cairo_solid_pattern_t *)pattern); break; case CAIRO_PATTERN_TYPE_SURFACE: return pimage_from_surface_pattern ( buffer, (cairo_surface_pattern_t *)pattern); break; case CAIRO_PATTERN_TYPE_LINEAR: case CAIRO_PATTERN_TYPE_RADIAL: return pimage_from_gradient_pattern ( buffer, (cairo_gradient_pattern_t *)pattern); break; case CAIRO_PATTERN_TYPE_MESH: return pimage_from_mesh_pattern ( buffer, surface, (cairo_mesh_pattern_t *)pattern); break; case CAIRO_PATTERN_TYPE_RASTER_SOURCE: return pimage_from_raster_source_pattern ( buffer, surface, (cairo_raster_source_pattern_t *)pattern); break; } ASSERT_NOT_REACHED; return CAIRO_INT_STATUS_DEGENERATE; } #define CAIRO_FIXED_16_16_MIN _cairo_fixed_from_int (-32768) #define CAIRO_FIXED_16_16_MAX _cairo_fixed_from_int (32767) static cairo_bool_t line_exceeds_16_16 (const cairo_line_t *line) { return line->p1.x <= CAIRO_FIXED_16_16_MIN || line->p1.x >= CAIRO_FIXED_16_16_MAX || line->p2.x <= CAIRO_FIXED_16_16_MIN || line->p2.x >= CAIRO_FIXED_16_16_MAX || line->p1.y <= CAIRO_FIXED_16_16_MIN || line->p1.y >= CAIRO_FIXED_16_16_MAX || line->p2.y <= CAIRO_FIXED_16_16_MIN || line->p2.y >= CAIRO_FIXED_16_16_MAX; } static void project_line_x_onto_16_16 (const cairo_line_t *line, cairo_fixed_t top, cairo_fixed_t bottom, pixman_line_fixed_t *out) { /* XXX use fixed-point arithmetic? */ cairo_point_double_t p1, p2; double m; p1.x = _cairo_fixed_to_double (line->p1.x); p1.y = _cairo_fixed_to_double (line->p1.y); p2.x = _cairo_fixed_to_double (line->p2.x); p2.y = _cairo_fixed_to_double (line->p2.y); m = (p2.x - p1.x) / (p2.y - p1.y); out->p1.x = _cairo_fixed_16_16_from_double ( p1.x + m * _cairo_fixed_to_double (top - line->p1.y)); out->p2.x = _cairo_fixed_16_16_from_double ( p1.x + m * _cairo_fixed_to_double (bottom - line->p1.y)); } static pixman_trapezoid_t * traps_to_pixman_trapezoids (const cairo_traps_t *traps) { pixman_trapezoid_t *ptraps; int i; if (!(ptraps = malloc (traps->num_traps * sizeof (pixman_trapezoid_t)))) return NULL; for (i = 0; i < traps->num_traps; ++i) { cairo_trapezoid_t *trap = &(traps->traps[i]); pixman_trapezoid_t *ptrap = &ptraps[i]; /* top/bottom will be clamped to surface bounds */ ptrap->top = _cairo_fixed_to_16_16 (trap->top); ptrap->bottom = _cairo_fixed_to_16_16 (trap->bottom); /* However, all the other coordinates will have been left * untouched so as not to introduce numerical error. Recompute * them if they * exceed the 16.16 limits. */ if (unlikely (line_exceeds_16_16 (&trap->left))) { project_line_x_onto_16_16 ( &trap->left, trap->top, trap->bottom, &ptrap->left); ptrap->left.p1.y = ptrap->top; ptrap->left.p2.y = ptrap->bottom; } else { ptrap->left.p1.x = _cairo_fixed_to_16_16 (trap->left.p1.x); ptrap->left.p1.y = _cairo_fixed_to_16_16 (trap->left.p1.y); ptrap->left.p2.x = _cairo_fixed_to_16_16 (trap->left.p2.x); ptrap->left.p2.y = _cairo_fixed_to_16_16 (trap->left.p2.y); } if (unlikely (line_exceeds_16_16 (&trap->right))) { project_line_x_onto_16_16 ( &trap->right, trap->top, trap->bottom, &ptrap->right); ptrap->right.p1.y = ptrap->top; ptrap->right.p2.y = ptrap->bottom; } else { ptrap->right.p1.x = _cairo_fixed_to_16_16 (trap->right.p1.x); ptrap->right.p1.y = _cairo_fixed_to_16_16 (trap->right.p1.y); ptrap->right.p2.x = _cairo_fixed_to_16_16 (trap->right.p2.x); ptrap->right.p2.y = _cairo_fixed_to_16_16 (trap->right.p2.y); } } return ptraps; } static int create_clip_image (command_buffer_t **buffer, const cairo_clip_t *clip, int width, int height) { cairo_int_status_t status; pixman_trapezoid_t *ptraps = NULL; cairo_clip_path_t *clip_path; int i; int boxes, clip_id, wh; cairo_region_t *region; status = CAIRO_INT_STATUS_SUCCESS; if (!clip) return command_buffer_new_white (buffer); if ((region = _cairo_clip_get_region (clip))) return command_buffer_new_region (buffer, ®ion->rgn); /* First add the boxes */ if (!(ptraps = malloc (clip->num_boxes * sizeof (pixman_trapezoid_t)))) return command_buffer_new_broken (buffer); for (i = 0; i < clip->num_boxes; ++i) { cairo_box_t *box = &(clip->boxes[i]); pixman_trapezoid_t *trap = &(ptraps[i]); trap->top = _cairo_fixed_to_16_16 (box->p1.y); trap->bottom = _cairo_fixed_to_16_16 (box->p2.y); trap->left.p1.x = _cairo_fixed_to_16_16 (box->p1.x); trap->left.p1.y = _cairo_fixed_to_16_16 (box->p1.y); trap->left.p2.x = _cairo_fixed_to_16_16 (box->p1.x); trap->left.p2.y = _cairo_fixed_to_16_16 (box->p2.y); trap->right.p1.x = _cairo_fixed_to_16_16 (box->p2.x); trap->right.p1.y = _cairo_fixed_to_16_16 (box->p1.y); trap->right.p2.x = _cairo_fixed_to_16_16 (box->p2.x); trap->right.p2.y = _cairo_fixed_to_16_16 (box->p2.y); } /* FIXME: is CAIRO_ANTIALIAS_DEFAULT correct here? It seems to be * what the image surface is using */ boxes = command_buffer_new_traps ( buffer, CAIRO_ANTIALIAS_DEFAULT, clip->num_boxes, ptraps); clip_id = command_buffer_new_blank (buffer, boxes); wh = command_buffer_new_white (buffer); command_buffer_composite (buffer, clip_id, PIXMAN_OP_SRC, wh, boxes); for (clip_path = clip->path; clip_path != NULL; clip_path = clip_path->prev) { cairo_polygon_t polygon; cairo_traps_t traps; pixman_trapezoid_t *ptraps = NULL; int tmp; _cairo_polygon_init (&polygon, NULL, 0); _cairo_traps_init (&traps); status = _cairo_path_fixed_fill_to_polygon ( &clip_path->path, clip_path->tolerance, &polygon); if (status != CAIRO_INT_STATUS_SUCCESS) goto exit_loop; status = _cairo_bentley_ottmann_tessellate_polygon ( &traps, &polygon, clip_path->fill_rule); if (status != CAIRO_INT_STATUS_SUCCESS) goto exit_loop; if (!(ptraps = traps_to_pixman_trapezoids (&traps))) { status = CAIRO_INT_STATUS_NO_MEMORY; goto exit_loop; } tmp = command_buffer_new_traps ( buffer, clip_path->antialias, traps.num_traps, ptraps); command_buffer_composite (buffer, clip_id, PIXMAN_OP_IN, wh, tmp); exit_loop: free (ptraps); _cairo_polygon_fini (&polygon); _cairo_traps_fini (&traps); if (status != CAIRO_INT_STATUS_SUCCESS) { command_buffer_new_broken (buffer); break; } } return clip_id; } static pixman_op_t _pixman_operator (cairo_operator_t op) { switch ((int) op) { case CAIRO_OPERATOR_CLEAR: return PIXMAN_OP_CLEAR; case CAIRO_OPERATOR_SOURCE: return PIXMAN_OP_SRC; case CAIRO_OPERATOR_OVER: return PIXMAN_OP_OVER; case CAIRO_OPERATOR_IN: return PIXMAN_OP_IN; case CAIRO_OPERATOR_OUT: return PIXMAN_OP_OUT; case CAIRO_OPERATOR_ATOP: return PIXMAN_OP_ATOP; case CAIRO_OPERATOR_DEST: return PIXMAN_OP_DST; case CAIRO_OPERATOR_DEST_OVER: return PIXMAN_OP_OVER_REVERSE; case CAIRO_OPERATOR_DEST_IN: return PIXMAN_OP_IN_REVERSE; case CAIRO_OPERATOR_DEST_OUT: return PIXMAN_OP_OUT_REVERSE; case CAIRO_OPERATOR_DEST_ATOP: return PIXMAN_OP_ATOP_REVERSE; case CAIRO_OPERATOR_XOR: return PIXMAN_OP_XOR; case CAIRO_OPERATOR_ADD: return PIXMAN_OP_ADD; case CAIRO_OPERATOR_SATURATE: return PIXMAN_OP_SATURATE; case CAIRO_OPERATOR_MULTIPLY: return PIXMAN_OP_MULTIPLY; case CAIRO_OPERATOR_SCREEN: return PIXMAN_OP_SCREEN; case CAIRO_OPERATOR_OVERLAY: return PIXMAN_OP_OVERLAY; case CAIRO_OPERATOR_DARKEN: return PIXMAN_OP_DARKEN; case CAIRO_OPERATOR_LIGHTEN: return PIXMAN_OP_LIGHTEN; case CAIRO_OPERATOR_COLOR_DODGE: return PIXMAN_OP_COLOR_DODGE; case CAIRO_OPERATOR_COLOR_BURN: return PIXMAN_OP_COLOR_BURN; case CAIRO_OPERATOR_HARD_LIGHT: return PIXMAN_OP_HARD_LIGHT; case CAIRO_OPERATOR_SOFT_LIGHT: return PIXMAN_OP_SOFT_LIGHT; case CAIRO_OPERATOR_DIFFERENCE: return PIXMAN_OP_DIFFERENCE; case CAIRO_OPERATOR_EXCLUSION: return PIXMAN_OP_EXCLUSION; case CAIRO_OPERATOR_HSL_HUE: return PIXMAN_OP_HSL_HUE; case CAIRO_OPERATOR_HSL_SATURATION: return PIXMAN_OP_HSL_SATURATION; case CAIRO_OPERATOR_HSL_COLOR: return PIXMAN_OP_HSL_COLOR; case CAIRO_OPERATOR_HSL_LUMINOSITY: return PIXMAN_OP_HSL_LUMINOSITY; default: ASSERT_NOT_REACHED; return PIXMAN_OP_OVER; } } static int combine_mask_and_clip (command_buffer_t **buffer, int mask_id, int clip_id) { int combined_id; combined_id = command_buffer_new_blank (buffer, mask_id); command_buffer_composite ( buffer, combined_id, PIXMAN_OP_SRC, clip_id, mask_id); return combined_id; } static void clip_and_composite (command_buffer_t **buffer, cairo_pixman_surface_t *psurface, cairo_operator_t operator, const cairo_pattern_t *source, int mask_id, const cairo_clip_t *clip) { pixman_image_t *dest_image = psurface->pimage; int width = pixman_image_get_width (dest_image); int height = pixman_image_get_height (dest_image); int src_id, clip_id, combined_id, dest_id, white_id; pixman_op_t pop; if (clip && _cairo_clip_is_all_clipped (clip)) return; dest_id = command_buffer_new_image (buffer, psurface->pimage); clip_id = create_clip_image (buffer, clip, width, height); src_id = pimage_from_pattern (buffer, psurface, source); white_id = command_buffer_new_white (buffer); pop = _pixman_operator (operator); if (pop == PIXMAN_OP_CLEAR) { /* clip in mask OUT_REVERSE dest */ command_buffer_composite ( buffer, dest_id, PIXMAN_OP_OUT_REVERSE, mask_id, clip_id); } else if (pop == PIXMAN_OP_IN || pop == PIXMAN_OP_OUT || pop == PIXMAN_OP_IN_REVERSE || pop == PIXMAN_OP_ATOP_REVERSE) { /* First composite to a temporary surface */ combined_id = command_buffer_new_blank (buffer, dest_id); command_buffer_composite ( buffer, combined_id, PIXMAN_OP_SRC, dest_id, white_id); command_buffer_composite ( buffer, combined_id, pop, src_id, mask_id); /* Then punch out the clip from the destination */ command_buffer_composite ( buffer, dest_id, PIXMAN_OP_OUT_REVERSE, clip_id, white_id); /* And add the pieces together */ command_buffer_composite ( buffer, dest_id, PIXMAN_OP_ADD, combined_id, clip_id); } else if (pop == PIXMAN_OP_SRC) { combined_id = combine_mask_and_clip (buffer, mask_id, clip_id); command_buffer_composite ( buffer, dest_id, PIXMAN_OP_OUT_REVERSE, combined_id, white_id); command_buffer_composite ( buffer, dest_id, PIXMAN_OP_ADD, src_id, combined_id); } else { combined_id = combine_mask_and_clip (buffer, mask_id, clip_id); command_buffer_composite ( buffer, dest_id, pop, src_id, combined_id); } } static cairo_int_status_t cairo_pixman_surface_paint (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_clip_t *clip) { cairo_pixman_surface_t *psurface = abstract_surface; command_buffer_t *buffer = command_buffer_new (); int width = pixman_image_get_width (psurface->pimage); int height = pixman_image_get_height (psurface->pimage); int mask_id; mask_id = command_buffer_new_white (&buffer); clip_and_composite (&buffer, psurface, op, source, mask_id, clip); return command_buffer_process (buffer, width, height); } static cairo_int_status_t cairo_pixman_surface_mask (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, const cairo_clip_t *clip) { cairo_pixman_surface_t *psurface = abstract_surface; command_buffer_t *buffer = command_buffer_new (); int width = pixman_image_get_width (psurface->pimage); int height = pixman_image_get_height (psurface->pimage); int mask_id; mask_id = pimage_from_pattern (&buffer, psurface, mask); clip_and_composite (&buffer, psurface, op, source, mask_id, clip); return command_buffer_process (buffer, width, height); } static cairo_int_status_t cairo_pixman_surface_stroke (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { cairo_pixman_surface_t *psurface = abstract_surface; cairo_int_status_t status; cairo_traps_t traps; command_buffer_t *buffer = command_buffer_new (); int width = pixman_image_get_width (psurface->pimage); int height = pixman_image_get_height (psurface->pimage); _cairo_traps_init (&traps); status = _cairo_path_fixed_stroke_to_traps ( path, style, ctm, ctm_inverse, tolerance, &traps); if (status == CAIRO_INT_STATUS_SUCCESS) { pixman_trapezoid_t *ptraps; int mask_id; ptraps = traps_to_pixman_trapezoids (&traps); mask_id = command_buffer_new_traps ( &buffer, antialias, traps.num_traps, ptraps); clip_and_composite ( &buffer, psurface, op, source, mask_id, clip); } _cairo_traps_fini (&traps); return command_buffer_process (buffer, width, height); } static cairo_int_status_t cairo_pixman_surface_fill (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { cairo_pixman_surface_t *psurface = abstract_surface; cairo_int_status_t status; cairo_traps_t traps; int width = pixman_image_get_width (psurface->pimage); int height = pixman_image_get_height (psurface->pimage); command_buffer_t *buffer = command_buffer_new (); _cairo_traps_init (&traps); status = _cairo_path_fixed_fill_to_traps ( path, fill_rule, tolerance, &traps); if (status == CAIRO_INT_STATUS_SUCCESS) { pixman_trapezoid_t *ptraps; int mask_id; ptraps = traps_to_pixman_trapezoids (&traps); mask_id = command_buffer_new_traps ( &buffer, antialias, traps.num_traps, ptraps); clip_and_composite ( &buffer, psurface, op, source, mask_id, clip); } _cairo_traps_fini (&traps); return command_buffer_process (buffer, width, height); } static pixman_glyph_cache_t *global_glyph_cache; static inline pixman_glyph_cache_t * get_glyph_cache (void) { if (!global_glyph_cache) global_glyph_cache = pixman_glyph_cache_create (); return global_glyph_cache; } /* FIXME: make sure this gets called when glyphs are finalized */ void _cairo_pixman_surface_scaled_glyph_fini (cairo_scaled_font_t *scaled_font, cairo_scaled_glyph_t *scaled_glyph) { CAIRO_MUTEX_LOCK (_cairo_glyph_cache_mutex); if (global_glyph_cache) { pixman_glyph_cache_remove ( global_glyph_cache, scaled_font, (void *)_cairo_scaled_glyph_index (scaled_glyph)); } CAIRO_MUTEX_UNLOCK (_cairo_glyph_cache_mutex); } static cairo_int_status_t cairo_pixman_surface_glyphs (void *abstract_surface, cairo_operator_t operator, const cairo_pattern_t *source, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, const cairo_clip_t *clip) { cairo_pixman_surface_t *psurface = abstract_surface; cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS; pixman_glyph_cache_t *glyph_cache; pixman_glyph_t pglyphs_stack[CAIRO_STACK_ARRAY_LENGTH (pixman_glyph_t)]; pixman_glyph_t *pglyphs = pglyphs_stack; int width, height; pixman_glyph_t *pg; command_buffer_t *buffer; int glyph_id; int i; width = pixman_image_get_width (psurface->pimage); height = pixman_image_get_height (psurface->pimage); buffer = command_buffer_new (); CAIRO_MUTEX_LOCK (_cairo_glyph_cache_mutex); glyph_cache = get_glyph_cache(); if (unlikely (glyph_cache == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto out_unlock; } pixman_glyph_cache_freeze (glyph_cache); if (num_glyphs > ARRAY_LENGTH (pglyphs_stack)) { pglyphs = _cairo_malloc_ab (num_glyphs, sizeof (pixman_glyph_t)); if (unlikely (pglyphs == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto out_thaw; } } pg = pglyphs; for (i = 0; i < num_glyphs; i++) { unsigned long index = glyphs[i].index; const void *glyph; glyph = pixman_glyph_cache_lookup (glyph_cache, scaled_font, (void *)index); if (!glyph) { cairo_scaled_glyph_t *scaled_glyph; cairo_image_surface_t *glyph_surface; /* This call can actually end up recursing, so we have to * drop the mutex around it. */ CAIRO_MUTEX_UNLOCK (_cairo_glyph_cache_mutex); status = _cairo_scaled_glyph_lookup (scaled_font, index, CAIRO_SCALED_GLYPH_INFO_SURFACE, &scaled_glyph); CAIRO_MUTEX_LOCK (_cairo_glyph_cache_mutex); if (unlikely (status)) goto out_thaw; glyph_surface = scaled_glyph->surface; glyph = pixman_glyph_cache_insert (glyph_cache, scaled_font, (void *)index, glyph_surface->base.device_transform.x0, glyph_surface->base.device_transform.y0, glyph_surface->pixman_image); if (unlikely (!glyph)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto out_thaw; } } pg->x = _cairo_lround (glyphs[i].x); pg->y = _cairo_lround (glyphs[i].y); pg->glyph = glyph; pg++; } glyph_id = command_buffer_new_glyphs (&buffer, glyph_cache, pg - pglyphs, pglyphs); clip_and_composite ( &buffer, psurface, operator, source, glyph_id, clip); status = command_buffer_process (buffer, width, height); out_thaw: pixman_glyph_cache_thaw (glyph_cache); if (pglyphs != pglyphs_stack) free (pglyphs); out_unlock: CAIRO_MUTEX_UNLOCK (_cairo_glyph_cache_mutex); return status; } const cairo_surface_backend_t cairo_pixman_surface_backend = { CAIRO_SURFACE_TYPE_IMAGE, cairo_pixman_surface_finish, _cairo_default_context_create, cairo_pixman_surface_create_similar, NULL, /* create similar image */ cairo_pixman_surface_map_to_image, cairo_pixman_surface_unmap_image, cairo_pixman_surface_source, cairo_pixman_surface_acquire_source_image, cairo_pixman_surface_release_source_image, cairo_pixman_surface_snapshot, NULL, /* copy_page */ NULL, /* show_page */ cairo_pixman_surface_get_extents, cairo_pixman_surface_get_font_options, NULL, /* flush */ NULL, cairo_pixman_surface_paint, cairo_pixman_surface_mask, cairo_pixman_surface_stroke, cairo_pixman_surface_fill, NULL, /* fill-stroke */ cairo_pixman_surface_glyphs, };