| /* -*- 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 <cworth@cworth.org> |
| * Chris Wilson <chris@chris-wilson.co.uk> |
| */ |
| |
| /* The primarily reason for keeping a traps-compositor around is |
| * for validating cairo-xlib (which currently also uses traps). |
| */ |
| |
| #include "cairoint.h" |
| |
| #include "cairo-image-surface-private.h" |
| |
| #include "cairo-compositor-private.h" |
| #include "cairo-spans-compositor-private.h" |
| |
| #include "cairo-region-private.h" |
| #include "cairo-traps-private.h" |
| #include "cairo-tristrip-private.h" |
| |
| #include "cairo-pixman-private.h" |
| |
| static pixman_image_t * |
| to_pixman_image (cairo_surface_t *s) |
| { |
| return ((cairo_image_surface_t *)s)->pixman_image; |
| } |
| |
| static cairo_int_status_t |
| acquire (void *abstract_dst) |
| { |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static cairo_int_status_t |
| release (void *abstract_dst) |
| { |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static cairo_int_status_t |
| set_clip_region (void *_surface, |
| cairo_region_t *region) |
| { |
| cairo_image_surface_t *surface = _surface; |
| pixman_region32_t *rgn = region ? ®ion->rgn : NULL; |
| |
| if (! pixman_image_set_clip_region32 (surface->pixman_image, rgn)) |
| return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static cairo_int_status_t |
| draw_image_boxes (void *_dst, |
| cairo_image_surface_t *image, |
| cairo_boxes_t *boxes, |
| int dx, int dy) |
| { |
| cairo_image_surface_t *dst = _dst; |
| struct _cairo_boxes_chunk *chunk; |
| int i; |
| |
| TRACE ((stderr, "%s x %d\n", __FUNCTION__, boxes->num_boxes)); |
| |
| for (chunk = &boxes->chunks; chunk; chunk = chunk->next) { |
| for (i = 0; i < chunk->count; i++) { |
| cairo_box_t *b = &chunk->base[i]; |
| int x = _cairo_fixed_integer_part (b->p1.x); |
| int y = _cairo_fixed_integer_part (b->p1.y); |
| int w = _cairo_fixed_integer_part (b->p2.x) - x; |
| int h = _cairo_fixed_integer_part (b->p2.y) - y; |
| if (dst->pixman_format != image->pixman_format || |
| ! pixman_blt ((uint32_t *)image->data, (uint32_t *)dst->data, |
| image->stride / sizeof (uint32_t), |
| dst->stride / sizeof (uint32_t), |
| PIXMAN_FORMAT_BPP (image->pixman_format), |
| PIXMAN_FORMAT_BPP (dst->pixman_format), |
| x + dx, y + dy, |
| x, y, |
| w, h)) |
| { |
| pixman_image_composite32 (PIXMAN_OP_SRC, |
| image->pixman_image, NULL, dst->pixman_image, |
| x + dx, y + dy, |
| 0, 0, |
| x, y, |
| w, h); |
| } |
| } |
| } |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static inline uint32_t |
| color_to_uint32 (const cairo_color_t *color) |
| { |
| return |
| (color->alpha_short >> 8 << 24) | |
| (color->red_short >> 8 << 16) | |
| (color->green_short & 0xff00) | |
| (color->blue_short >> 8); |
| } |
| |
| static inline cairo_bool_t |
| color_to_pixel (const cairo_color_t *color, |
| pixman_format_code_t format, |
| uint32_t *pixel) |
| { |
| uint32_t c; |
| |
| if (!(format == PIXMAN_a8r8g8b8 || |
| format == PIXMAN_x8r8g8b8 || |
| format == PIXMAN_a8b8g8r8 || |
| format == PIXMAN_x8b8g8r8 || |
| format == PIXMAN_b8g8r8a8 || |
| format == PIXMAN_b8g8r8x8 || |
| format == PIXMAN_r5g6b5 || |
| format == PIXMAN_b5g6r5 || |
| format == PIXMAN_a8)) |
| { |
| return FALSE; |
| } |
| |
| c = color_to_uint32 (color); |
| |
| if (PIXMAN_FORMAT_TYPE (format) == PIXMAN_TYPE_ABGR) { |
| c = ((c & 0xff000000) >> 0) | |
| ((c & 0x00ff0000) >> 16) | |
| ((c & 0x0000ff00) >> 0) | |
| ((c & 0x000000ff) << 16); |
| } |
| |
| if (PIXMAN_FORMAT_TYPE (format) == PIXMAN_TYPE_BGRA) { |
| c = ((c & 0xff000000) >> 24) | |
| ((c & 0x00ff0000) >> 8) | |
| ((c & 0x0000ff00) << 8) | |
| ((c & 0x000000ff) << 24); |
| } |
| |
| if (format == PIXMAN_a8) { |
| c = c >> 24; |
| } else if (format == PIXMAN_r5g6b5 || format == PIXMAN_b5g6r5) { |
| c = ((((c) >> 3) & 0x001f) | |
| (((c) >> 5) & 0x07e0) | |
| (((c) >> 8) & 0xf800)); |
| } |
| |
| *pixel = c; |
| return TRUE; |
| } |
| |
| 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 cairo_bool_t |
| __fill_reduces_to_source (cairo_operator_t op, |
| const cairo_color_t *color, |
| const cairo_image_surface_t *dst) |
| { |
| if (op == CAIRO_OPERATOR_SOURCE || op == CAIRO_OPERATOR_CLEAR) |
| return TRUE; |
| if (op == CAIRO_OPERATOR_OVER && CAIRO_COLOR_IS_OPAQUE (color)) |
| return TRUE; |
| if (dst->base.is_clear) |
| return op == CAIRO_OPERATOR_OVER || op == CAIRO_OPERATOR_ADD; |
| |
| return FALSE; |
| } |
| |
| static cairo_bool_t |
| fill_reduces_to_source (cairo_operator_t op, |
| const cairo_color_t *color, |
| const cairo_image_surface_t *dst, |
| uint32_t *pixel) |
| { |
| if (__fill_reduces_to_source (op, color, dst)) { |
| return color_to_pixel (color, dst->pixman_format, pixel); |
| } |
| |
| return FALSE; |
| } |
| |
| static cairo_int_status_t |
| fill_rectangles (void *_dst, |
| cairo_operator_t op, |
| const cairo_color_t *color, |
| cairo_rectangle_int_t *rects, |
| int num_rects) |
| { |
| cairo_image_surface_t *dst = _dst; |
| uint32_t pixel; |
| int i; |
| |
| TRACE ((stderr, "%s\n", __FUNCTION__)); |
| |
| if (fill_reduces_to_source (op, color, dst, &pixel)) { |
| for (i = 0; i < num_rects; i++) { |
| pixman_fill ((uint32_t *) dst->data, dst->stride / sizeof (uint32_t), |
| PIXMAN_FORMAT_BPP (dst->pixman_format), |
| rects[i].x, rects[i].y, |
| rects[i].width, rects[i].height, |
| pixel); |
| } |
| } else { |
| pixman_image_t *src = _pixman_image_for_color (color); |
| if (unlikely (src == NULL)) |
| return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
| |
| op = _pixman_operator (op); |
| for (i = 0; i < num_rects; i++) { |
| pixman_image_composite32 (op, |
| src, NULL, dst->pixman_image, |
| 0, 0, |
| 0, 0, |
| rects[i].x, rects[i].y, |
| rects[i].width, rects[i].height); |
| } |
| |
| pixman_image_unref (src); |
| } |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static cairo_int_status_t |
| fill_boxes (void *_dst, |
| cairo_operator_t op, |
| const cairo_color_t *color, |
| cairo_boxes_t *boxes) |
| { |
| cairo_image_surface_t *dst = _dst; |
| struct _cairo_boxes_chunk *chunk; |
| uint32_t pixel; |
| int i; |
| |
| TRACE ((stderr, "%s x %d\n", __FUNCTION__, boxes->num_boxes)); |
| |
| if (fill_reduces_to_source (op, color, dst, &pixel)) { |
| for (chunk = &boxes->chunks; chunk; chunk = chunk->next) { |
| for (i = 0; i < chunk->count; i++) { |
| int x = _cairo_fixed_integer_part (chunk->base[i].p1.x); |
| int y = _cairo_fixed_integer_part (chunk->base[i].p1.y); |
| int w = _cairo_fixed_integer_part (chunk->base[i].p2.x) - x; |
| int h = _cairo_fixed_integer_part (chunk->base[i].p2.y) - y; |
| pixman_fill ((uint32_t *) dst->data, |
| dst->stride / sizeof (uint32_t), |
| PIXMAN_FORMAT_BPP (dst->pixman_format), |
| x, y, w, h, pixel); |
| } |
| } |
| } |
| else |
| { |
| pixman_image_t *src = _pixman_image_for_color (color); |
| if (unlikely (src == NULL)) |
| return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
| |
| op = _pixman_operator (op); |
| for (chunk = &boxes->chunks; chunk; chunk = chunk->next) { |
| for (i = 0; i < chunk->count; i++) { |
| int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x); |
| int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y); |
| int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x); |
| int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y); |
| pixman_image_composite32 (op, |
| src, NULL, dst->pixman_image, |
| 0, 0, |
| 0, 0, |
| x1, y1, |
| x2-x1, y2-y1); |
| } |
| } |
| |
| pixman_image_unref (src); |
| } |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static cairo_int_status_t |
| composite (void *_dst, |
| cairo_operator_t op, |
| cairo_surface_t *abstract_src, |
| cairo_surface_t *abstract_mask, |
| 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_image_source_t *src = (cairo_image_source_t *)abstract_src; |
| cairo_image_source_t *mask = (cairo_image_source_t *)abstract_mask; |
| |
| TRACE ((stderr, "%s\n", __FUNCTION__)); |
| |
| if (mask) { |
| pixman_image_composite32 (_pixman_operator (op), |
| src->pixman_image, mask->pixman_image, to_pixman_image (_dst), |
| src_x, src_y, |
| mask_x, mask_y, |
| dst_x, dst_y, |
| width, height); |
| } else { |
| pixman_image_composite32 (_pixman_operator (op), |
| src->pixman_image, NULL, to_pixman_image (_dst), |
| src_x, src_y, |
| 0, 0, |
| dst_x, dst_y, |
| width, height); |
| } |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static cairo_int_status_t |
| lerp (void *_dst, |
| cairo_surface_t *abstract_src, |
| cairo_surface_t *abstract_mask, |
| 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_image_surface_t *dst = _dst; |
| cairo_image_source_t *src = (cairo_image_source_t *)abstract_src; |
| cairo_image_source_t *mask = (cairo_image_source_t *)abstract_mask; |
| |
| TRACE ((stderr, "%s\n", __FUNCTION__)); |
| |
| #if PIXMAN_HAS_OP_LERP |
| pixman_image_composite32 (PIXMAN_OP_LERP_SRC, |
| src->pixman_image, mask->pixman_image, dst->pixman_image, |
| src_x, src_y, |
| mask_x, mask_y, |
| dst_x, dst_y, |
| width, height); |
| #else |
| /* Punch the clip out of the destination */ |
| TRACE ((stderr, "%s - OUT_REVERSE (mask=%d/%p, dst=%d/%p)\n", |
| __FUNCTION__, |
| mask->base.unique_id, mask->pixman_image, |
| dst->base.unique_id, dst->pixman_image)); |
| pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE, |
| mask->pixman_image, NULL, dst->pixman_image, |
| mask_x, mask_y, |
| 0, 0, |
| dst_x, dst_y, |
| width, height); |
| |
| /* Now add the two results together */ |
| TRACE ((stderr, "%s - ADD (src=%d/%p, mask=%d/%p, dst=%d/%p)\n", |
| __FUNCTION__, |
| src->base.unique_id, src->pixman_image, |
| mask->base.unique_id, mask->pixman_image, |
| dst->base.unique_id, dst->pixman_image)); |
| pixman_image_composite32 (PIXMAN_OP_ADD, |
| src->pixman_image, mask->pixman_image, dst->pixman_image, |
| src_x, src_y, |
| mask_x, mask_y, |
| dst_x, dst_y, |
| width, height); |
| #endif |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static cairo_int_status_t |
| composite_boxes (void *_dst, |
| cairo_operator_t op, |
| cairo_surface_t *abstract_src, |
| cairo_surface_t *abstract_mask, |
| int src_x, |
| int src_y, |
| int mask_x, |
| int mask_y, |
| int dst_x, |
| int dst_y, |
| cairo_boxes_t *boxes, |
| const cairo_rectangle_int_t *extents) |
| { |
| pixman_image_t *dst = to_pixman_image (_dst); |
| pixman_image_t *src = ((cairo_image_source_t *)abstract_src)->pixman_image; |
| pixman_image_t *mask = abstract_mask ? ((cairo_image_source_t *)abstract_mask)->pixman_image : NULL; |
| pixman_image_t *free_src = NULL; |
| struct _cairo_boxes_chunk *chunk; |
| int i; |
| |
| /* XXX consider using a region? saves multiple prepare-composite */ |
| TRACE ((stderr, "%s x %d\n", __FUNCTION__, boxes->num_boxes)); |
| |
| if (((cairo_surface_t *)_dst)->is_clear && |
| (op == CAIRO_OPERATOR_SOURCE || |
| op == CAIRO_OPERATOR_OVER || |
| op == CAIRO_OPERATOR_ADD)) { |
| op = PIXMAN_OP_SRC; |
| } else if (mask) { |
| if (op == CAIRO_OPERATOR_CLEAR) { |
| #if PIXMAN_HAS_OP_LERP |
| op = PIXMAN_OP_LERP_CLEAR; |
| #else |
| free_src = src = _pixman_image_for_color (CAIRO_COLOR_WHITE); |
| if (unlikely (src == NULL)) |
| return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
| op = PIXMAN_OP_OUT_REVERSE; |
| #endif |
| } else if (op == CAIRO_OPERATOR_SOURCE) { |
| #if PIXMAN_HAS_OP_LERP |
| op = PIXMAN_OP_LERP_SRC; |
| #else |
| return CAIRO_INT_STATUS_UNSUPPORTED; |
| #endif |
| } else { |
| op = _pixman_operator (op); |
| } |
| } else { |
| op = _pixman_operator (op); |
| } |
| |
| for (chunk = &boxes->chunks; chunk; chunk = chunk->next) { |
| for (i = 0; i < chunk->count; i++) { |
| int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x); |
| int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y); |
| int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x); |
| int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y); |
| |
| pixman_image_composite32 (op, src, mask, dst, |
| x1 + src_x, y1 + src_y, |
| x1 + mask_x, y1 + mask_y, |
| x1 + dst_x, y1 + dst_y, |
| x2 - x1, y2 - y1); |
| } |
| } |
| |
| if (free_src) |
| pixman_image_unref (free_src); |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| #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)); |
| } |
| |
| void |
| _pixman_image_add_traps (pixman_image_t *image, |
| int dst_x, int dst_y, |
| cairo_traps_t *traps) |
| { |
| cairo_trapezoid_t *t = traps->traps; |
| int num_traps = traps->num_traps; |
| while (num_traps--) { |
| pixman_trapezoid_t trap; |
| |
| /* top/bottom will be clamped to surface bounds */ |
| trap.top = _cairo_fixed_to_16_16 (t->top); |
| trap.bottom = _cairo_fixed_to_16_16 (t->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 (&t->left))) { |
| project_line_x_onto_16_16 (&t->left, t->top, t->bottom, &trap.left); |
| trap.left.p1.y = trap.top; |
| trap.left.p2.y = trap.bottom; |
| } else { |
| trap.left.p1.x = _cairo_fixed_to_16_16 (t->left.p1.x); |
| trap.left.p1.y = _cairo_fixed_to_16_16 (t->left.p1.y); |
| trap.left.p2.x = _cairo_fixed_to_16_16 (t->left.p2.x); |
| trap.left.p2.y = _cairo_fixed_to_16_16 (t->left.p2.y); |
| } |
| |
| if (unlikely (line_exceeds_16_16 (&t->right))) { |
| project_line_x_onto_16_16 (&t->right, t->top, t->bottom, &trap.right); |
| trap.right.p1.y = trap.top; |
| trap.right.p2.y = trap.bottom; |
| } else { |
| trap.right.p1.x = _cairo_fixed_to_16_16 (t->right.p1.x); |
| trap.right.p1.y = _cairo_fixed_to_16_16 (t->right.p1.y); |
| trap.right.p2.x = _cairo_fixed_to_16_16 (t->right.p2.x); |
| trap.right.p2.y = _cairo_fixed_to_16_16 (t->right.p2.y); |
| } |
| |
| pixman_rasterize_trapezoid (image, &trap, -dst_x, -dst_y); |
| t++; |
| } |
| } |
| |
| static cairo_int_status_t |
| composite_traps (void *_dst, |
| cairo_operator_t op, |
| cairo_surface_t *abstract_src, |
| int src_x, |
| int src_y, |
| int dst_x, |
| int dst_y, |
| const cairo_rectangle_int_t *extents, |
| cairo_antialias_t antialias, |
| cairo_traps_t *traps) |
| { |
| cairo_image_surface_t *dst = (cairo_image_surface_t *) _dst; |
| cairo_image_source_t *src = (cairo_image_source_t *) abstract_src; |
| cairo_int_status_t status; |
| pixman_image_t *mask; |
| pixman_format_code_t format; |
| |
| TRACE ((stderr, "%s\n", __FUNCTION__)); |
| |
| /* pixman doesn't eliminate self-intersecting trapezoids/edges */ |
| status = _cairo_bentley_ottmann_tessellate_traps (traps, |
| CAIRO_FILL_RULE_WINDING); |
| if (status != CAIRO_INT_STATUS_SUCCESS) |
| return status; |
| |
| /* Special case adding trapezoids onto a mask surface; we want to avoid |
| * creating an intermediate temporary mask unnecessarily. |
| * |
| * We make the assumption here that the portion of the trapezoids |
| * contained within the surface is bounded by [dst_x,dst_y,width,height]; |
| * the Cairo core code passes bounds based on the trapezoid extents. |
| */ |
| format = antialias == CAIRO_ANTIALIAS_NONE ? PIXMAN_a1 : PIXMAN_a8; |
| if (dst->pixman_format == format && |
| (abstract_src == NULL || |
| (op == CAIRO_OPERATOR_ADD && src->is_opaque_solid))) |
| { |
| _pixman_image_add_traps (dst->pixman_image, dst_x, dst_y, traps); |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| mask = pixman_image_create_bits (format, |
| extents->width, extents->height, |
| NULL, 0); |
| if (unlikely (mask == NULL)) |
| return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
| |
| _pixman_image_add_traps (mask, extents->x, extents->y, traps); |
| pixman_image_composite32 (_pixman_operator (op), |
| src->pixman_image, mask, dst->pixman_image, |
| extents->x + src_x, extents->y + src_y, |
| 0, 0, |
| extents->x - dst_x, extents->y - dst_y, |
| extents->width, extents->height); |
| |
| pixman_image_unref (mask); |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| #if PIXMAN_VERSION >= PIXMAN_VERSION_ENCODE(0,22,0) |
| static void |
| set_point (pixman_point_fixed_t *p, cairo_point_t *c) |
| { |
| p->x = _cairo_fixed_to_16_16 (c->x); |
| p->y = _cairo_fixed_to_16_16 (c->y); |
| } |
| |
| void |
| _pixman_image_add_tristrip (pixman_image_t *image, |
| int dst_x, int dst_y, |
| cairo_tristrip_t *strip) |
| { |
| pixman_triangle_t tri; |
| pixman_point_fixed_t *p[3] = {&tri.p1, &tri.p2, &tri.p3 }; |
| int n; |
| |
| set_point (p[0], &strip->points[0]); |
| set_point (p[1], &strip->points[1]); |
| set_point (p[2], &strip->points[2]); |
| pixman_add_triangles (image, -dst_x, -dst_y, 1, &tri); |
| for (n = 3; n < strip->num_points; n++) { |
| set_point (p[n%3], &strip->points[n]); |
| pixman_add_triangles (image, -dst_x, -dst_y, 1, &tri); |
| } |
| } |
| |
| static cairo_int_status_t |
| composite_tristrip (void *_dst, |
| cairo_operator_t op, |
| cairo_surface_t *abstract_src, |
| int src_x, |
| int src_y, |
| int dst_x, |
| int dst_y, |
| const cairo_rectangle_int_t *extents, |
| cairo_antialias_t antialias, |
| cairo_tristrip_t *strip) |
| { |
| cairo_image_surface_t *dst = (cairo_image_surface_t *) _dst; |
| cairo_image_source_t *src = (cairo_image_source_t *) abstract_src; |
| pixman_image_t *mask; |
| pixman_format_code_t format; |
| |
| TRACE ((stderr, "%s\n", __FUNCTION__)); |
| |
| if (strip->num_points < 3) |
| return CAIRO_STATUS_SUCCESS; |
| |
| if (1) { /* pixman doesn't eliminate self-intersecting triangles/edges */ |
| cairo_int_status_t status; |
| cairo_traps_t traps; |
| int n; |
| |
| _cairo_traps_init (&traps); |
| for (n = 0; n < strip->num_points; n++) { |
| cairo_point_t p[4]; |
| |
| p[0] = strip->points[0]; |
| p[1] = strip->points[1]; |
| p[2] = strip->points[2]; |
| p[3] = strip->points[0]; |
| |
| _cairo_traps_tessellate_convex_quad (&traps, p); |
| } |
| status = composite_traps (_dst, op, abstract_src, |
| src_x, src_y, |
| dst_x, dst_y, |
| extents, antialias, &traps); |
| _cairo_traps_fini (&traps); |
| |
| return status; |
| } |
| |
| format = antialias == CAIRO_ANTIALIAS_NONE ? PIXMAN_a1 : PIXMAN_a8; |
| if (dst->pixman_format == format && |
| (abstract_src == NULL || |
| (op == CAIRO_OPERATOR_ADD && src->is_opaque_solid))) |
| { |
| _pixman_image_add_tristrip (dst->pixman_image, dst_x, dst_y, strip); |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| mask = pixman_image_create_bits (format, |
| extents->width, extents->height, |
| NULL, 0); |
| if (unlikely (mask == NULL)) |
| return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
| |
| _pixman_image_add_tristrip (mask, extents->x, extents->y, strip); |
| pixman_image_composite32 (_pixman_operator (op), |
| src->pixman_image, mask, dst->pixman_image, |
| extents->x + src_x, extents->y + src_y, |
| 0, 0, |
| extents->x - dst_x, extents->y - dst_y, |
| extents->width, extents->height); |
| |
| pixman_image_unref (mask); |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| #endif |
| |
| static cairo_int_status_t |
| check_composite_glyphs (const cairo_composite_rectangles_t *extents, |
| cairo_scaled_font_t *scaled_font, |
| cairo_glyph_t *glyphs, |
| int *num_glyphs) |
| { |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| #if HAS_PIXMAN_GLYPHS |
| 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; |
| } |
| |
| void |
| _cairo_image_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 |
| composite_glyphs (void *_dst, |
| cairo_operator_t op, |
| cairo_surface_t *_src, |
| int src_x, |
| int src_y, |
| int dst_x, |
| int dst_y, |
| cairo_composite_glyphs_info_t *info) |
| { |
| 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; |
| pixman_glyph_t *pg; |
| int i; |
| |
| TRACE ((stderr, "%s\n", __FUNCTION__)); |
| |
| 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 (info->num_glyphs > ARRAY_LENGTH (pglyphs_stack)) { |
| pglyphs = _cairo_malloc_ab (info->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 < info->num_glyphs; i++) { |
| unsigned long index = info->glyphs[i].index; |
| const void *glyph; |
| |
| glyph = pixman_glyph_cache_lookup (glyph_cache, info->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 (info->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, info->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 (info->glyphs[i].x); |
| pg->y = _cairo_lround (info->glyphs[i].y); |
| pg->glyph = glyph; |
| pg++; |
| } |
| |
| if (info->use_mask) { |
| pixman_format_code_t mask_format; |
| |
| mask_format = pixman_glyph_get_mask_format (glyph_cache, pg - pglyphs, pglyphs); |
| |
| pixman_composite_glyphs (_pixman_operator (op), |
| ((cairo_image_source_t *)_src)->pixman_image, |
| to_pixman_image (_dst), |
| mask_format, |
| info->extents.x + src_x, info->extents.y + src_y, |
| info->extents.x, info->extents.y, |
| info->extents.x - dst_x, info->extents.y - dst_y, |
| info->extents.width, info->extents.height, |
| glyph_cache, pg - pglyphs, pglyphs); |
| } else { |
| pixman_composite_glyphs_no_mask (_pixman_operator (op), |
| ((cairo_image_source_t *)_src)->pixman_image, |
| to_pixman_image (_dst), |
| src_x, src_y, |
| - dst_x, - dst_y, |
| glyph_cache, pg - pglyphs, pglyphs); |
| } |
| |
| 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; |
| } |
| #else |
| void |
| _cairo_image_scaled_glyph_fini (cairo_scaled_font_t *scaled_font, |
| cairo_scaled_glyph_t *scaled_glyph) |
| { |
| } |
| |
| static cairo_int_status_t |
| composite_one_glyph (void *_dst, |
| cairo_operator_t op, |
| cairo_surface_t *_src, |
| int src_x, |
| int src_y, |
| int dst_x, |
| int dst_y, |
| cairo_composite_glyphs_info_t *info) |
| { |
| cairo_image_surface_t *glyph_surface; |
| cairo_scaled_glyph_t *scaled_glyph; |
| cairo_status_t status; |
| int x, y; |
| |
| TRACE ((stderr, "%s\n", __FUNCTION__)); |
| |
| status = _cairo_scaled_glyph_lookup (info->font, |
| info->glyphs[0].index, |
| CAIRO_SCALED_GLYPH_INFO_SURFACE, |
| &scaled_glyph); |
| |
| if (unlikely (status)) |
| return status; |
| |
| glyph_surface = scaled_glyph->surface; |
| if (glyph_surface->width == 0 || glyph_surface->height == 0) |
| return CAIRO_INT_STATUS_NOTHING_TO_DO; |
| |
| /* round glyph locations to the nearest pixel */ |
| /* XXX: FRAGILE: We're ignoring device_transform scaling here. A bug? */ |
| x = _cairo_lround (info->glyphs[0].x - |
| glyph_surface->base.device_transform.x0); |
| y = _cairo_lround (info->glyphs[0].y - |
| glyph_surface->base.device_transform.y0); |
| |
| pixman_image_composite32 (_pixman_operator (op), |
| ((cairo_image_source_t *)_src)->pixman_image, |
| glyph_surface->pixman_image, |
| to_pixman_image (_dst), |
| x + src_x, y + src_y, |
| 0, 0, |
| x - dst_x, y - dst_y, |
| glyph_surface->width, |
| glyph_surface->height); |
| |
| return CAIRO_INT_STATUS_SUCCESS; |
| } |
| |
| static cairo_int_status_t |
| composite_glyphs_via_mask (void *_dst, |
| cairo_operator_t op, |
| cairo_surface_t *_src, |
| int src_x, |
| int src_y, |
| int dst_x, |
| int dst_y, |
| cairo_composite_glyphs_info_t *info) |
| { |
| cairo_scaled_glyph_t *glyph_cache[64]; |
| pixman_image_t *white = _pixman_image_for_color (CAIRO_COLOR_WHITE); |
| cairo_scaled_glyph_t *scaled_glyph; |
| uint8_t buf[2048]; |
| pixman_image_t *mask; |
| pixman_format_code_t format; |
| cairo_status_t status; |
| int i; |
| |
| TRACE ((stderr, "%s\n", __FUNCTION__)); |
| |
| if (unlikely (white == NULL)) |
| return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
| |
| /* XXX convert the glyphs to common formats a8/a8r8g8b8 to hit |
| * optimised paths through pixman. Should we increase the bit |
| * depth of the target surface, we should reconsider the appropriate |
| * mask formats. |
| */ |
| |
| status = _cairo_scaled_glyph_lookup (info->font, |
| info->glyphs[0].index, |
| CAIRO_SCALED_GLYPH_INFO_SURFACE, |
| &scaled_glyph); |
| if (unlikely (status)) { |
| pixman_image_unref (white); |
| return status; |
| } |
| |
| memset (glyph_cache, 0, sizeof (glyph_cache)); |
| glyph_cache[info->glyphs[0].index % ARRAY_LENGTH (glyph_cache)] = scaled_glyph; |
| |
| format = PIXMAN_a8; |
| i = (info->extents.width + 3) & ~3; |
| if (scaled_glyph->surface->base.content & CAIRO_CONTENT_COLOR) { |
| format = PIXMAN_a8r8g8b8; |
| i = info->extents.width * 4; |
| } |
| |
| if (i * info->extents.height > (int) sizeof (buf)) { |
| mask = pixman_image_create_bits (format, |
| info->extents.width, |
| info->extents.height, |
| NULL, 0); |
| } else { |
| memset (buf, 0, i * info->extents.height); |
| mask = pixman_image_create_bits (format, |
| info->extents.width, |
| info->extents.height, |
| (uint32_t *)buf, i); |
| } |
| if (unlikely (mask == NULL)) { |
| pixman_image_unref (white); |
| return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
| } |
| |
| status = CAIRO_STATUS_SUCCESS; |
| for (i = 0; i < info->num_glyphs; i++) { |
| unsigned long glyph_index = info->glyphs[i].index; |
| int cache_index = glyph_index % ARRAY_LENGTH (glyph_cache); |
| cairo_image_surface_t *glyph_surface; |
| int x, y; |
| |
| scaled_glyph = glyph_cache[cache_index]; |
| if (scaled_glyph == NULL || |
| _cairo_scaled_glyph_index (scaled_glyph) != glyph_index) |
| { |
| status = _cairo_scaled_glyph_lookup (info->font, glyph_index, |
| CAIRO_SCALED_GLYPH_INFO_SURFACE, |
| &scaled_glyph); |
| |
| if (unlikely (status)) { |
| pixman_image_unref (mask); |
| pixman_image_unref (white); |
| return status; |
| } |
| |
| glyph_cache[cache_index] = scaled_glyph; |
| } |
| |
| glyph_surface = scaled_glyph->surface; |
| if (glyph_surface->width && glyph_surface->height) { |
| if (glyph_surface->base.content & CAIRO_CONTENT_COLOR && |
| format == PIXMAN_a8) { |
| pixman_image_t *ca_mask; |
| |
| format = PIXMAN_a8r8g8b8; |
| ca_mask = pixman_image_create_bits (format, |
| info->extents.width, |
| info->extents.height, |
| NULL, 0); |
| if (unlikely (ca_mask == NULL)) { |
| pixman_image_unref (mask); |
| pixman_image_unref (white); |
| return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
| } |
| |
| pixman_image_composite32 (PIXMAN_OP_SRC, |
| white, mask, ca_mask, |
| 0, 0, |
| 0, 0, |
| 0, 0, |
| info->extents.width, |
| info->extents.height); |
| pixman_image_unref (mask); |
| mask = ca_mask; |
| } |
| |
| /* round glyph locations to the nearest pixel */ |
| /* XXX: FRAGILE: We're ignoring device_transform scaling here. A bug? */ |
| x = _cairo_lround (info->glyphs[i].x - |
| glyph_surface->base.device_transform.x0); |
| y = _cairo_lround (info->glyphs[i].y - |
| glyph_surface->base.device_transform.y0); |
| |
| if (glyph_surface->pixman_format == format) { |
| pixman_image_composite32 (PIXMAN_OP_ADD, |
| glyph_surface->pixman_image, NULL, mask, |
| 0, 0, |
| 0, 0, |
| x - info->extents.x, y - info->extents.y, |
| glyph_surface->width, |
| glyph_surface->height); |
| } else { |
| pixman_image_composite32 (PIXMAN_OP_ADD, |
| white, glyph_surface->pixman_image, mask, |
| 0, 0, |
| 0, 0, |
| x - info->extents.x, y - info->extents.y, |
| glyph_surface->width, |
| glyph_surface->height); |
| } |
| } |
| } |
| |
| if (format == PIXMAN_a8r8g8b8) |
| pixman_image_set_component_alpha (mask, TRUE); |
| |
| pixman_image_composite32 (_pixman_operator (op), |
| ((cairo_image_source_t *)_src)->pixman_image, |
| mask, |
| to_pixman_image (_dst), |
| info->extents.x + src_x, info->extents.y + src_y, |
| 0, 0, |
| info->extents.x - dst_x, info->extents.y - dst_y, |
| info->extents.width, info->extents.height); |
| pixman_image_unref (mask); |
| pixman_image_unref (white); |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static cairo_int_status_t |
| composite_glyphs (void *_dst, |
| cairo_operator_t op, |
| cairo_surface_t *_src, |
| int src_x, |
| int src_y, |
| int dst_x, |
| int dst_y, |
| cairo_composite_glyphs_info_t *info) |
| { |
| cairo_scaled_glyph_t *glyph_cache[64]; |
| pixman_image_t *dst, *src; |
| cairo_status_t status; |
| int i; |
| |
| TRACE ((stderr, "%s\n", __FUNCTION__)); |
| |
| if (info->num_glyphs == 1) |
| return composite_one_glyph(_dst, op, _src, src_x, src_y, dst_x, dst_y, info); |
| |
| if (info->use_mask) |
| return composite_glyphs_via_mask(_dst, op, _src, src_x, src_y, dst_x, dst_y, info); |
| |
| op = _pixman_operator (op); |
| dst = to_pixman_image (_dst); |
| src = ((cairo_image_source_t *)_src)->pixman_image; |
| |
| memset (glyph_cache, 0, sizeof (glyph_cache)); |
| status = CAIRO_STATUS_SUCCESS; |
| |
| for (i = 0; i < info->num_glyphs; i++) { |
| int x, y; |
| cairo_image_surface_t *glyph_surface; |
| cairo_scaled_glyph_t *scaled_glyph; |
| unsigned long glyph_index = info->glyphs[i].index; |
| int cache_index = glyph_index % ARRAY_LENGTH (glyph_cache); |
| |
| scaled_glyph = glyph_cache[cache_index]; |
| if (scaled_glyph == NULL || |
| _cairo_scaled_glyph_index (scaled_glyph) != glyph_index) |
| { |
| status = _cairo_scaled_glyph_lookup (info->font, glyph_index, |
| CAIRO_SCALED_GLYPH_INFO_SURFACE, |
| &scaled_glyph); |
| |
| if (unlikely (status)) |
| break; |
| |
| glyph_cache[cache_index] = scaled_glyph; |
| } |
| |
| glyph_surface = scaled_glyph->surface; |
| if (glyph_surface->width && glyph_surface->height) { |
| /* round glyph locations to the nearest pixel */ |
| /* XXX: FRAGILE: We're ignoring device_transform scaling here. A bug? */ |
| x = _cairo_lround (info->glyphs[i].x - |
| glyph_surface->base.device_transform.x0); |
| y = _cairo_lround (info->glyphs[i].y - |
| glyph_surface->base.device_transform.y0); |
| |
| pixman_image_composite32 (op, src, glyph_surface->pixman_image, dst, |
| x + src_x, y + src_y, |
| 0, 0, |
| x - dst_x, y - dst_y, |
| glyph_surface->width, |
| glyph_surface->height); |
| } |
| } |
| |
| return status; |
| } |
| #endif |
| |
| static cairo_int_status_t |
| check_composite (const cairo_composite_rectangles_t *extents) |
| { |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| const cairo_compositor_t * |
| _cairo_image_traps_compositor_get (void) |
| { |
| static cairo_traps_compositor_t compositor; |
| |
| if (compositor.base.delegate == NULL) { |
| _cairo_traps_compositor_init (&compositor, |
| &__cairo_no_compositor); |
| compositor.acquire = acquire; |
| compositor.release = release; |
| compositor.set_clip_region = set_clip_region; |
| compositor.pattern_to_surface = _cairo_image_source_create_for_pattern; |
| compositor.draw_image_boxes = draw_image_boxes; |
| //compositor.copy_boxes = copy_boxes; |
| compositor.fill_boxes = fill_boxes; |
| compositor.check_composite = check_composite; |
| compositor.composite = composite; |
| compositor.lerp = lerp; |
| //compositor.check_composite_boxes = check_composite_boxes; |
| compositor.composite_boxes = composite_boxes; |
| //compositor.check_composite_traps = check_composite_traps; |
| compositor.composite_traps = composite_traps; |
| //compositor.check_composite_tristrip = check_composite_traps; |
| #if PIXMAN_VERSION >= PIXMAN_VERSION_ENCODE(0,22,0) |
| compositor.composite_tristrip = composite_tristrip; |
| #endif |
| compositor.check_composite_glyphs = check_composite_glyphs; |
| compositor.composite_glyphs = composite_glyphs; |
| } |
| |
| return &compositor.base; |
| } |
| |
| const cairo_compositor_t * |
| _cairo_image_mask_compositor_get (void) |
| { |
| static cairo_mask_compositor_t compositor; |
| |
| if (compositor.base.delegate == NULL) { |
| _cairo_mask_compositor_init (&compositor, |
| _cairo_image_traps_compositor_get ()); |
| compositor.acquire = acquire; |
| compositor.release = release; |
| compositor.set_clip_region = set_clip_region; |
| compositor.pattern_to_surface = _cairo_image_source_create_for_pattern; |
| compositor.draw_image_boxes = draw_image_boxes; |
| compositor.fill_rectangles = fill_rectangles; |
| compositor.fill_boxes = fill_boxes; |
| compositor.check_composite = check_composite; |
| compositor.composite = composite; |
| //compositor.lerp = lerp; |
| //compositor.check_composite_boxes = check_composite_boxes; |
| compositor.composite_boxes = composite_boxes; |
| compositor.check_composite_glyphs = check_composite_glyphs; |
| compositor.composite_glyphs = composite_glyphs; |
| } |
| |
| return &compositor.base; |
| } |
| |
| #if PIXMAN_HAS_COMPOSITOR |
| typedef struct _cairo_image_span_renderer { |
| cairo_span_renderer_t base; |
| |
| pixman_image_compositor_t *compositor; |
| pixman_image_t *src, *mask; |
| float opacity; |
| cairo_rectangle_int_t extents; |
| } cairo_image_span_renderer_t; |
| COMPILE_TIME_ASSERT (sizeof (cairo_image_span_renderer_t) <= sizeof (cairo_abstract_span_renderer_t)); |
| |
| static cairo_status_t |
| _cairo_image_bounded_opaque_spans (void *abstract_renderer, |
| int y, int height, |
| const cairo_half_open_span_t *spans, |
| unsigned num_spans) |
| { |
| cairo_image_span_renderer_t *r = abstract_renderer; |
| |
| if (num_spans == 0) |
| return CAIRO_STATUS_SUCCESS; |
| |
| do { |
| if (spans[0].coverage) |
| pixman_image_compositor_blt (r->compositor, |
| spans[0].x, y, |
| spans[1].x - spans[0].x, height, |
| spans[0].coverage); |
| spans++; |
| } while (--num_spans > 1); |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static cairo_status_t |
| _cairo_image_bounded_spans (void *abstract_renderer, |
| int y, int height, |
| const cairo_half_open_span_t *spans, |
| unsigned num_spans) |
| { |
| cairo_image_span_renderer_t *r = abstract_renderer; |
| |
| if (num_spans == 0) |
| return CAIRO_STATUS_SUCCESS; |
| |
| do { |
| if (spans[0].coverage) { |
| pixman_image_compositor_blt (r->compositor, |
| spans[0].x, y, |
| spans[1].x - spans[0].x, height, |
| r->opacity * spans[0].coverage); |
| } |
| spans++; |
| } while (--num_spans > 1); |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static cairo_status_t |
| _cairo_image_unbounded_spans (void *abstract_renderer, |
| int y, int height, |
| const cairo_half_open_span_t *spans, |
| unsigned num_spans) |
| { |
| cairo_image_span_renderer_t *r = abstract_renderer; |
| |
| assert (y + height <= r->extents.height); |
| if (y > r->extents.y) { |
| pixman_image_compositor_blt (r->compositor, |
| r->extents.x, r->extents.y, |
| r->extents.width, y - r->extents.y, |
| 0); |
| } |
| |
| if (num_spans == 0) { |
| pixman_image_compositor_blt (r->compositor, |
| r->extents.x, y, |
| r->extents.width, height, |
| 0); |
| } else { |
| if (spans[0].x != r->extents.x) { |
| pixman_image_compositor_blt (r->compositor, |
| r->extents.x, y, |
| spans[0].x - r->extents.x, |
| height, |
| 0); |
| } |
| |
| do { |
| assert (spans[0].x < r->extents.x + r->extents.width); |
| pixman_image_compositor_blt (r->compositor, |
| spans[0].x, y, |
| spans[1].x - spans[0].x, height, |
| r->opacity * spans[0].coverage); |
| spans++; |
| } while (--num_spans > 1); |
| |
| if (spans[0].x != r->extents.x + r->extents.width) { |
| assert (spans[0].x < r->extents.x + r->extents.width); |
| pixman_image_compositor_blt (r->compositor, |
| spans[0].x, y, |
| r->extents.x + r->extents.width - spans[0].x, height, |
| 0); |
| } |
| } |
| |
| r->extents.y = y + height; |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static cairo_status_t |
| _cairo_image_clipped_spans (void *abstract_renderer, |
| int y, int height, |
| const cairo_half_open_span_t *spans, |
| unsigned num_spans) |
| { |
| cairo_image_span_renderer_t *r = abstract_renderer; |
| |
| assert (num_spans); |
| |
| do { |
| if (! spans[0].inverse) |
| pixman_image_compositor_blt (r->compositor, |
| spans[0].x, y, |
| spans[1].x - spans[0].x, height, |
| r->opacity * spans[0].coverage); |
| spans++; |
| } while (--num_spans > 1); |
| |
| r->extents.y = y + height; |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static cairo_status_t |
| _cairo_image_finish_unbounded_spans (void *abstract_renderer) |
| { |
| cairo_image_span_renderer_t *r = abstract_renderer; |
| |
| if (r->extents.y < r->extents.height) { |
| pixman_image_compositor_blt (r->compositor, |
| r->extents.x, r->extents.y, |
| r->extents.width, |
| r->extents.height - r->extents.y, |
| 0); |
| } |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static cairo_int_status_t |
| span_renderer_init (cairo_abstract_span_renderer_t *_r, |
| const cairo_composite_rectangles_t *composite, |
| cairo_bool_t needs_clip) |
| { |
| cairo_image_span_renderer_t *r = (cairo_image_span_renderer_t *)_r; |
| cairo_image_surface_t *dst = (cairo_image_surface_t *)composite->surface; |
| const cairo_pattern_t *source = &composite->source_pattern.base; |
| cairo_operator_t op = composite->op; |
| int src_x, src_y; |
| int mask_x, mask_y; |
| |
| TRACE ((stderr, "%s\n", __FUNCTION__)); |
| |
| if (op == CAIRO_OPERATOR_CLEAR) { |
| op = PIXMAN_OP_LERP_CLEAR; |
| } else if (dst->base.is_clear && |
| (op == CAIRO_OPERATOR_SOURCE || |
| op == CAIRO_OPERATOR_OVER || |
| op == CAIRO_OPERATOR_ADD)) { |
| op = PIXMAN_OP_SRC; |
| } else if (op == CAIRO_OPERATOR_SOURCE) { |
| op = PIXMAN_OP_LERP_SRC; |
| } else { |
| op = _pixman_operator (op); |
| } |
| |
| r->compositor = NULL; |
| r->mask = NULL; |
| r->src = _pixman_image_for_pattern (dst, source, FALSE, |
| &composite->unbounded, |
| &composite->source_sample_area, |
| &src_x, &src_y); |
| if (unlikely (r->src == NULL)) |
| return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
| |
| r->opacity = 1.0; |
| if (composite->mask_pattern.base.type == CAIRO_PATTERN_TYPE_SOLID) { |
| r->opacity = composite->mask_pattern.solid.color.alpha; |
| } else { |
| r->mask = _pixman_image_for_pattern (dst, |
| &composite->mask_pattern.base, |
| TRUE, |
| &composite->unbounded, |
| &composite->mask_sample_area, |
| &mask_x, &mask_y); |
| if (unlikely (r->mask == NULL)) |
| return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
| |
| /* XXX Component-alpha? */ |
| if ((dst->base.content & CAIRO_CONTENT_COLOR) == 0 && |
| _cairo_pattern_is_opaque (source, &composite->source_sample_area)) |
| { |
| pixman_image_unref (r->src); |
| r->src = r->mask; |
| src_x = mask_x; |
| src_y = mask_y; |
| r->mask = NULL; |
| } |
| } |
| |
| if (composite->is_bounded) { |
| if (r->opacity == 1.) |
| r->base.render_rows = _cairo_image_bounded_opaque_spans; |
| else |
| r->base.render_rows = _cairo_image_bounded_spans; |
| r->base.finish = NULL; |
| } else { |
| if (needs_clip) |
| r->base.render_rows = _cairo_image_clipped_spans; |
| else |
| r->base.render_rows = _cairo_image_unbounded_spans; |
| r->base.finish = _cairo_image_finish_unbounded_spans; |
| r->extents = composite->unbounded; |
| r->extents.height += r->extents.y; |
| } |
| |
| r->compositor = |
| pixman_image_create_compositor (op, r->src, r->mask, dst->pixman_image, |
| composite->unbounded.x + src_x, |
| composite->unbounded.y + src_y, |
| composite->unbounded.x + mask_x, |
| composite->unbounded.y + mask_y, |
| composite->unbounded.x, |
| composite->unbounded.y, |
| composite->unbounded.width, |
| composite->unbounded.height); |
| if (unlikely (r->compositor == NULL)) |
| return CAIRO_INT_STATUS_NOTHING_TO_DO; |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static void |
| span_renderer_fini (cairo_abstract_span_renderer_t *_r, |
| cairo_int_status_t status) |
| { |
| cairo_image_span_renderer_t *r = (cairo_image_span_renderer_t *) _r; |
| |
| TRACE ((stderr, "%s\n", __FUNCTION__)); |
| |
| if (status == CAIRO_INT_STATUS_SUCCESS && r->base.finish) |
| r->base.finish (r); |
| |
| if (r->compositor) |
| pixman_image_compositor_destroy (r->compositor); |
| |
| if (r->src) |
| pixman_image_unref (r->src); |
| if (r->mask) |
| pixman_image_unref (r->mask); |
| } |
| #else |
| typedef struct _cairo_image_span_renderer { |
| cairo_span_renderer_t base; |
| |
| const cairo_composite_rectangles_t *composite; |
| |
| float opacity; |
| uint8_t op; |
| int bpp; |
| |
| pixman_image_t *src, *mask; |
| union { |
| struct fill { |
| int stride; |
| uint8_t *data; |
| uint32_t pixel; |
| } fill; |
| struct blit { |
| int stride; |
| uint8_t *data; |
| int src_stride; |
| uint8_t *src_data; |
| } blit; |
| struct composite { |
| pixman_image_t *dst; |
| int src_x, src_y; |
| int mask_x, mask_y; |
| int run_length; |
| } composite; |
| struct finish { |
| cairo_rectangle_int_t extents; |
| int src_x, src_y; |
| int stride; |
| uint8_t *data; |
| } mask; |
| } u; |
| uint8_t _buf[0]; |
| #define SZ_BUF (int)(sizeof (cairo_abstract_span_renderer_t) - sizeof (cairo_image_span_renderer_t)) |
| } cairo_image_span_renderer_t; |
| COMPILE_TIME_ASSERT (sizeof (cairo_image_span_renderer_t) <= sizeof (cairo_abstract_span_renderer_t)); |
| |
| static cairo_status_t |
| _cairo_image_spans (void *abstract_renderer, |
| int y, int height, |
| const cairo_half_open_span_t *spans, |
| unsigned num_spans) |
| { |
| cairo_image_span_renderer_t *r = abstract_renderer; |
| uint8_t *mask, *row; |
| int len; |
| |
| if (num_spans == 0) |
| return CAIRO_STATUS_SUCCESS; |
| |
| mask = r->u.mask.data + (y - r->u.mask.extents.y) * r->u.mask.stride; |
| mask += spans[0].x - r->u.mask.extents.x; |
| row = mask; |
| |
| do { |
| len = spans[1].x - spans[0].x; |
| if (spans[0].coverage) { |
| *row++ = r->opacity * spans[0].coverage; |
| if (--len) |
| memset (row, row[-1], len); |
| } |
| row += len; |
| spans++; |
| } while (--num_spans > 1); |
| |
| len = row - mask; |
| row = mask; |
| while (--height) { |
| mask += r->u.mask.stride; |
| memcpy (mask, row, len); |
| } |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static cairo_status_t |
| _cairo_image_spans_and_zero (void *abstract_renderer, |
| int y, int height, |
| const cairo_half_open_span_t *spans, |
| unsigned num_spans) |
| { |
| cairo_image_span_renderer_t *r = abstract_renderer; |
| uint8_t *mask; |
| int len; |
| |
| mask = r->u.mask.data; |
| if (y > r->u.mask.extents.y) { |
| len = (y - r->u.mask.extents.y) * r->u.mask.stride; |
| memset (mask, 0, len); |
| mask += len; |
| } |
| |
| r->u.mask.extents.y = y + height; |
| r->u.mask.data = mask + height * r->u.mask.stride; |
| if (num_spans == 0) { |
| memset (mask, 0, height * r->u.mask.stride); |
| } else { |
| uint8_t *row = mask; |
| |
| if (spans[0].x != r->u.mask.extents.x) { |
| len = spans[0].x - r->u.mask.extents.x; |
| memset (row, 0, len); |
| row += len; |
| } |
| |
| do { |
| len = spans[1].x - spans[0].x; |
| *row++ = r->opacity * spans[0].coverage; |
| if (len > 1) { |
| memset (row, row[-1], --len); |
| row += len; |
| } |
| spans++; |
| } while (--num_spans > 1); |
| |
| if (spans[0].x != r->u.mask.extents.x + r->u.mask.extents.width) { |
| len = r->u.mask.extents.x + r->u.mask.extents.width - spans[0].x; |
| memset (row, 0, len); |
| } |
| |
| row = mask; |
| while (--height) { |
| mask += r->u.mask.stride; |
| memcpy (mask, row, r->u.mask.extents.width); |
| } |
| } |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static cairo_status_t |
| _cairo_image_finish_spans_and_zero (void *abstract_renderer) |
| { |
| cairo_image_span_renderer_t *r = abstract_renderer; |
| |
| if (r->u.mask.extents.y < r->u.mask.extents.height) |
| memset (r->u.mask.data, 0, (r->u.mask.extents.height - r->u.mask.extents.y) * r->u.mask.stride); |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static cairo_status_t |
| _fill8_spans (void *abstract_renderer, int y, int h, |
| const cairo_half_open_span_t *spans, unsigned num_spans) |
| { |
| cairo_image_span_renderer_t *r = abstract_renderer; |
| |
| if (num_spans == 0) |
| return CAIRO_STATUS_SUCCESS; |
| |
| if (likely(h == 1)) { |
| do { |
| if (spans[0].coverage) { |
| int len = spans[1].x - spans[0].x; |
| uint8_t *d = r->u.fill.data + r->u.fill.stride*y + spans[0].x; |
| if (len == 1) |
| *d = r->u.fill.pixel; |
| else |
| memset(d, r->u.fill.pixel, len); |
| } |
| spans++; |
| } while (--num_spans > 1); |
| } else { |
| do { |
| if (spans[0].coverage) { |
| int yy = y, hh = h; |
| do { |
| int len = spans[1].x - spans[0].x; |
| uint8_t *d = r->u.fill.data + r->u.fill.stride*yy + spans[0].x; |
| if (len == 1) |
| *d = r->u.fill.pixel; |
| else |
| memset(d, r->u.fill.pixel, len); |
| yy++; |
| } while (--hh); |
| } |
| spans++; |
| } while (--num_spans > 1); |
| } |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static cairo_status_t |
| _fill16_spans (void *abstract_renderer, int y, int h, |
| const cairo_half_open_span_t *spans, unsigned num_spans) |
| { |
| cairo_image_span_renderer_t *r = abstract_renderer; |
| |
| if (num_spans == 0) |
| return CAIRO_STATUS_SUCCESS; |
| |
| if (likely(h == 1)) { |
| do { |
| if (spans[0].coverage) { |
| int len = spans[1].x - spans[0].x; |
| uint16_t *d = (uint16_t*)(r->u.fill.data + r->u.fill.stride*y + spans[0].x*2); |
| while (len--) |
| *d++ = r->u.fill.pixel; |
| } |
| spans++; |
| } while (--num_spans > 1); |
| } else { |
| do { |
| if (spans[0].coverage) { |
| int yy = y, hh = h; |
| do { |
| int len = spans[1].x - spans[0].x; |
| uint16_t *d = (uint16_t*)(r->u.fill.data + r->u.fill.stride*yy + spans[0].x*2); |
| while (len--) |
| *d++ = r->u.fill.pixel; |
| yy++; |
| } while (--hh); |
| } |
| spans++; |
| } while (--num_spans > 1); |
| } |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static cairo_status_t |
| _fill32_spans (void *abstract_renderer, int y, int h, |
| const cairo_half_open_span_t *spans, unsigned num_spans) |
| { |
| cairo_image_span_renderer_t *r = abstract_renderer; |
| |
| if (num_spans == 0) |
| return CAIRO_STATUS_SUCCESS; |
| |
| if (likely(h == 1)) { |
| do { |
| if (spans[0].coverage) { |
| int len = spans[1].x - spans[0].x; |
| if (len > 32) { |
| pixman_fill ((uint32_t *)r->u.fill.data, r->u.fill.stride / sizeof(uint32_t), r->bpp, |
| spans[0].x, y, len, 1, r->u.fill.pixel); |
| } else { |
| uint32_t *d = (uint32_t*)(r->u.fill.data + r->u.fill.stride*y + spans[0].x*4); |
| while (len--) |
| *d++ = r->u.fill.pixel; |
| } |
| } |
| spans++; |
| } while (--num_spans > 1); |
| } else { |
| do { |
| if (spans[0].coverage) { |
| if (spans[1].x - spans[0].x > 16) { |
| pixman_fill ((uint32_t *)r->u.fill.data, r->u.fill.stride / sizeof(uint32_t), r->bpp, |
| spans[0].x, y, spans[1].x - spans[0].x, h, |
| r->u.fill.pixel); |
| } else { |
| int yy = y, hh = h; |
| do { |
| int len = spans[1].x - spans[0].x; |
| uint32_t *d = (uint32_t*)(r->u.fill.data + r->u.fill.stride*yy + spans[0].x*4); |
| while (len--) |
| *d++ = r->u.fill.pixel; |
| yy++; |
| } while (--hh); |
| } |
| } |
| spans++; |
| } while (--num_spans > 1); |
| } |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| #if 0 |
| static cairo_status_t |
| _fill_spans (void *abstract_renderer, int y, int h, |
| const cairo_half_open_span_t *spans, unsigned num_spans) |
| { |
| cairo_image_span_renderer_t *r = abstract_renderer; |
| |
| if (num_spans == 0) |
| return CAIRO_STATUS_SUCCESS; |
| |
| do { |
| if (spans[0].coverage) { |
| pixman_fill ((uint32_t *) r->data, r->stride, r->bpp, |
| spans[0].x, y, |
| spans[1].x - spans[0].x, h, |
| r->pixel); |
| } |
| spans++; |
| } while (--num_spans > 1); |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| #endif |
| |
| static cairo_status_t |
| _blit_spans (void *abstract_renderer, int y, int h, |
| const cairo_half_open_span_t *spans, unsigned num_spans) |
| { |
| cairo_image_span_renderer_t *r = abstract_renderer; |
| int cpp; |
| |
| if (num_spans == 0) |
| return CAIRO_STATUS_SUCCESS; |
| |
| cpp = r->bpp/8; |
| if (likely (h == 1)) { |
| uint8_t *src = r->u.blit.src_data + y*r->u.blit.src_stride; |
| uint8_t *dst = r->u.blit.data + y*r->u.blit.stride; |
| do { |
| if (spans[0].coverage) { |
| void *s = src + spans[0].x*cpp; |
| void *d = dst + spans[0].x*cpp; |
| int len = (spans[1].x - spans[0].x) * cpp; |
| switch (len) { |
| case 1: |
| *(uint8_t *)d = *(uint8_t *)s; |
| break; |
| case 2: |
| *(uint16_t *)d = *(uint16_t *)s; |
| break; |
| case 4: |
| *(uint32_t *)d = *(uint32_t *)s; |
| break; |
| #if HAVE_UINT64_T |
| case 8: |
| *(uint64_t *)d = *(uint64_t *)s; |
| break; |
| #endif |
| default: |
| memcpy(d, s, len); |
| break; |
| } |
| } |
| spans++; |
| } while (--num_spans > 1); |
| } else { |
| do { |
| if (spans[0].coverage) { |
| int yy = y, hh = h; |
| do { |
| void *src = r->u.blit.src_data + yy*r->u.blit.src_stride + spans[0].x*cpp; |
| void *dst = r->u.blit.data + yy*r->u.blit.stride + spans[0].x*cpp; |
| int len = (spans[1].x - spans[0].x) * cpp; |
| switch (len) { |
| case 1: |
| *(uint8_t *)dst = *(uint8_t *)src; |
| break; |
| case 2: |
| *(uint16_t *)dst = *(uint16_t *)src; |
| break; |
| case 4: |
| *(uint32_t *)dst = *(uint32_t *)src; |
| break; |
| #if HAVE_UINT64_T |
| case 8: |
| *(uint64_t *)dst = *(uint64_t *)src; |
| break; |
| #endif |
| default: |
| memcpy(dst, src, len); |
| break; |
| } |
| yy++; |
| } while (--hh); |
| } |
| spans++; |
| } while (--num_spans > 1); |
| } |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static cairo_status_t |
| _mono_spans (void *abstract_renderer, int y, int h, |
| const cairo_half_open_span_t *spans, unsigned num_spans) |
| { |
| cairo_image_span_renderer_t *r = abstract_renderer; |
| |
| if (num_spans == 0) |
| return CAIRO_STATUS_SUCCESS; |
| |
| do { |
| if (spans[0].coverage) { |
| pixman_image_composite32 (r->op, |
| r->src, NULL, r->u.composite.dst, |
| spans[0].x + r->u.composite.src_x, y + r->u.composite.src_y, |
| 0, 0, |
| spans[0].x, y, |
| spans[1].x - spans[0].x, h); |
| } |
| spans++; |
| } while (--num_spans > 1); |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static cairo_status_t |
| _mono_unbounded_spans (void *abstract_renderer, int y, int h, |
| const cairo_half_open_span_t *spans, unsigned num_spans) |
| { |
| cairo_image_span_renderer_t *r = abstract_renderer; |
| |
| if (num_spans == 0) { |
| pixman_image_composite32 (PIXMAN_OP_CLEAR, |
| r->src, NULL, r->u.composite.dst, |
| spans[0].x + r->u.composite.src_x, y + r->u.composite.src_y, |
| 0, 0, |
| r->composite->unbounded.x, y, |
| r->composite->unbounded.width, h); |
| r->u.composite.mask_y = y + h; |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| if (y != r->u.composite.mask_y) { |
| pixman_image_composite32 (PIXMAN_OP_CLEAR, |
| r->src, NULL, r->u.composite.dst, |
| spans[0].x + r->u.composite.src_x, y + r->u.composite.src_y, |
| 0, 0, |
| r->composite->unbounded.x, r->u.composite.mask_y, |
| r->composite->unbounded.width, y - r->u.composite.mask_y); |
| } |
| |
| if (spans[0].x != r->composite->unbounded.x) { |
| pixman_image_composite32 (PIXMAN_OP_CLEAR, |
| r->src, NULL, r->u.composite.dst, |
| spans[0].x + r->u.composite.src_x, y + r->u.composite.src_y, |
| 0, 0, |
| r->composite->unbounded.x, y, |
| spans[0].x - r->composite->unbounded.x, h); |
| } |
| |
| do { |
| int op = spans[0].coverage ? r->op : PIXMAN_OP_CLEAR; |
| pixman_image_composite32 (op, |
| r->src, NULL, r->u.composite.dst, |
| spans[0].x + r->u.composite.src_x, y + r->u.composite.src_y, |
| 0, 0, |
| spans[0].x, y, |
| spans[1].x - spans[0].x, h); |
| spans++; |
| } while (--num_spans > 1); |
| |
| if (spans[0].x != r->composite->unbounded.x + r->composite->unbounded.width) { |
| pixman_image_composite32 (PIXMAN_OP_CLEAR, |
| r->src, NULL, r->u.composite.dst, |
| spans[0].x + r->u.composite.src_x, y + r->u.composite.src_y, |
| 0, 0, |
| spans[0].x, y, |
| r->composite->unbounded.x + r->composite->unbounded.width - spans[0].x, h); |
| } |
| |
| r->u.composite.mask_y = y + h; |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static cairo_status_t |
| _mono_finish_unbounded_spans (void *abstract_renderer) |
| { |
| cairo_image_span_renderer_t *r = abstract_renderer; |
| |
| if (r->u.composite.mask_y < r->composite->unbounded.y + r->composite->unbounded.height) { |
| pixman_image_composite32 (PIXMAN_OP_CLEAR, |
| r->src, NULL, r->u.composite.dst, |
| r->composite->unbounded.x + r->u.composite.src_x, r->u.composite.mask_y + r->u.composite.src_y, |
| 0, 0, |
| r->composite->unbounded.x, r->u.composite.mask_y, |
| r->composite->unbounded.width, |
| r->composite->unbounded.y + r->composite->unbounded.height - r->u.composite.mask_y); |
| } |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static cairo_int_status_t |
| mono_renderer_init (cairo_image_span_renderer_t *r, |
| const cairo_composite_rectangles_t *composite, |
| cairo_antialias_t antialias, |
| cairo_bool_t needs_clip) |
| { |
| cairo_image_surface_t *dst = (cairo_image_surface_t *)composite->surface; |
| |
| if (antialias != CAIRO_ANTIALIAS_NONE) |
| return CAIRO_INT_STATUS_UNSUPPORTED; |
| |
| if (!_cairo_pattern_is_opaque_solid (&composite->mask_pattern.base)) |
| return CAIRO_INT_STATUS_UNSUPPORTED; |
| |
| r->base.render_rows = NULL; |
| if (composite->source_pattern.base.type == CAIRO_PATTERN_TYPE_SOLID) { |
| const cairo_color_t *color; |
| |
| color = &composite->source_pattern.solid.color; |
| if (composite->op == CAIRO_OPERATOR_CLEAR) |
| color = CAIRO_COLOR_TRANSPARENT; |
| |
| if (fill_reduces_to_source (composite->op, color, dst, &r->u.fill.pixel)) { |
| /* Use plain C for the fill operations as the span length is |
| * typically small, too small to payback the startup overheads of |
| * using SSE2 etc. |
| */ |
| switch (PIXMAN_FORMAT_BPP(dst->pixman_format)) { |
| case 8: r->base.render_rows = _fill8_spans; break; |
| case 16: r->base.render_rows = _fill16_spans; break; |
| case 32: r->base.render_rows = _fill32_spans; break; |
| default: break; |
| } |
| r->u.fill.data = dst->data; |
| r->u.fill.stride = dst->stride; |
| } |
| } else if ((composite->op == CAIRO_OPERATOR_SOURCE || |
| (composite->op == CAIRO_OPERATOR_OVER && |
| (dst->base.is_clear || (dst->base.content & CAIRO_CONTENT_ALPHA) == 0))) && |
| composite->source_pattern.base.type == CAIRO_PATTERN_TYPE_SURFACE && |
| composite->source_pattern.surface.surface->backend->type == CAIRO_SURFACE_TYPE_IMAGE && |
| to_image_surface(composite->source_pattern.surface.surface)->format == dst->format) |
| { |
| cairo_image_surface_t *src = |
| to_image_surface(composite->source_pattern.surface.surface); |
| int tx, ty; |
| |
| if (_cairo_matrix_is_integer_translation(&composite->source_pattern.base.matrix, |
| &tx, &ty) && |
| composite->bounded.x + tx >= 0 && |
| composite->bounded.y + ty >= 0 && |
| composite->bounded.x + composite->bounded.width + tx <= src->width && |
| composite->bounded.y + composite->bounded.height + ty <= src->height) { |
| |
| r->u.blit.stride = dst->stride; |
| r->u.blit.data = dst->data; |
| r->u.blit.src_stride = src->stride; |
| r->u.blit.src_data = src->data + src->stride * ty + tx * 4; |
| r->base.render_rows = _blit_spans; |
| } |
| } |
| |
| if (r->base.render_rows == NULL) { |
| r->src = _pixman_image_for_pattern (dst, &composite->source_pattern.base, FALSE, |
| &composite->unbounded, |
| &composite->source_sample_area, |
| &r->u.composite.src_x, &r->u.composite.src_y); |
| if (unlikely (r->src == NULL)) |
| return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
| |
| r->u.composite.dst = to_pixman_image (composite->surface); |
| r->op = _pixman_operator (composite->op); |
| if (composite->is_bounded == 0) { |
| r->base.render_rows = _mono_unbounded_spans; |
| r->base.finish = _mono_finish_unbounded_spans; |
| r->u.composite.mask_y = composite->unbounded.y; |
| } else |
| r->base.render_rows = _mono_spans; |
| } |
| r->bpp = PIXMAN_FORMAT_BPP(dst->pixman_format); |
| |
| return CAIRO_INT_STATUS_SUCCESS; |
| } |
| |
| #define ONE_HALF 0x7f |
| #define RB_MASK 0x00ff00ff |
| #define RB_ONE_HALF 0x007f007f |
| #define RB_MASK_PLUS_ONE 0x01000100 |
| #define G_SHIFT 8 |
| static inline uint32_t |
| mul8x2_8 (uint32_t a, uint8_t b) |
| { |
| uint32_t t = (a & RB_MASK) * b + RB_ONE_HALF; |
| return ((t + ((t >> G_SHIFT) & RB_MASK)) >> G_SHIFT) & RB_MASK; |
| } |
| |
| static inline uint32_t |
| add8x2_8x2 (uint32_t a, uint32_t b) |
| { |
| uint32_t t = a + b; |
| t |= RB_MASK_PLUS_ONE - ((t >> G_SHIFT) & RB_MASK); |
| return t & RB_MASK; |
| } |
| |
| static inline uint8_t |
| mul8_8 (uint8_t a, uint8_t b) |
| { |
| uint16_t t = a * (uint16_t)b + ONE_HALF; |
| return ((t >> G_SHIFT) + t) >> G_SHIFT; |
| } |
| |
| static inline uint32_t |
| lerp8x4 (uint32_t src, uint8_t a, uint32_t dst) |
| { |
| return (add8x2_8x2 (mul8x2_8 (src, a), |
| mul8x2_8 (dst, ~a)) | |
| add8x2_8x2 (mul8x2_8 (src >> G_SHIFT, a), |
| mul8x2_8 (dst >> G_SHIFT, ~a)) << G_SHIFT); |
| } |
| |
| static cairo_status_t |
| _fill_a8_lerp_opaque_spans (void *abstract_renderer, int y, int h, |
| const cairo_half_open_span_t *spans, unsigned num_spans) |
| { |
| cairo_image_span_renderer_t *r = abstract_renderer; |
| |
| if (num_spans == 0) |
| return CAIRO_STATUS_SUCCESS; |
| |
| if (likely(h == 1)) { |
| uint8_t *d = r->u.fill.data + r->u.fill.stride*y; |
| do { |
| uint8_t a = spans[0].coverage; |
| if (a) { |
| int len = spans[1].x - spans[0].x; |
| if (a == 0xff) { |
| memset(d + spans[0].x, r->u.fill.pixel, len); |
| } else { |
| uint8_t s = mul8_8(a, r->u.fill.pixel); |
| uint8_t *dst = d + spans[0].x; |
| a = ~a; |
| while (len--) { |
| uint8_t t = mul8_8(*dst, a); |
| *dst++ = t + s; |
| } |
| } |
| } |
| spans++; |
| } while (--num_spans > 1); |
| } else { |
| do { |
| uint8_t a = spans[0].coverage; |
| if (a) { |
| int yy = y, hh = h; |
| if (a == 0xff) { |
| do { |
| int len = spans[1].x - spans[0].x; |
| uint8_t *d = r->u.fill.data + r->u.fill.stride*yy + spans[0].x; |
| memset(d, r->u.fill.pixel, len); |
| yy++; |
| } while (--hh); |
| } else { |
| uint8_t s = mul8_8(a, r->u.fill.pixel); |
| a = ~a; |
| do { |
| int len = spans[1].x - spans[0].x; |
| uint8_t *d = r->u.fill.data + r->u.fill.stride*yy + spans[0].x; |
| while (len--) { |
| uint8_t t = mul8_8(*d, a); |
| *d++ = t + s; |
| } |
| yy++; |
| } while (--hh); |
| } |
| } |
| spans++; |
| } while (--num_spans > 1); |
| } |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static cairo_status_t |
| _fill_xrgb32_lerp_opaque_spans (void *abstract_renderer, int y, int h, |
| const cairo_half_open_span_t *spans, unsigned num_spans) |
| { |
| cairo_image_span_renderer_t *r = abstract_renderer; |
| |
| if (num_spans == 0) |
| return CAIRO_STATUS_SUCCESS; |
| |
| if (likely(h == 1)) { |
| do { |
| uint8_t a = spans[0].coverage; |
| if (a) { |
| int len = spans[1].x - spans[0].x; |
| uint32_t *d = (uint32_t*)(r->u.fill.data + r->u.fill.stride*y + spans[0].x*4); |
| if (a == 0xff) { |
| if (len > 31) { |
| pixman_fill ((uint32_t *)r->u.fill.data, r->u.fill.stride / sizeof(uint32_t), 32, |
| spans[0].x, y, len, 1, r->u.fill.pixel); |
| } else { |
| uint32_t *d = (uint32_t*)(r->u.fill.data + r->u.fill.stride*y + spans[0].x*4); |
| while (len-- > 0) |
| *d++ = r->u.fill.pixel; |
| } |
| } else while (len-- > 0) { |
| *d = lerp8x4 (r->u.fill.pixel, a, *d); |
| d++; |
| } |
| } |
| spans++; |
| } while (--num_spans > 1); |
| } else { |
| do { |
| uint8_t a = spans[0].coverage; |
| if (a) { |
| if (a == 0xff) { |
| if (spans[1].x - spans[0].x > 16) { |
| pixman_fill ((uint32_t *)r->u.fill.data, r->u.fill.stride / sizeof(uint32_t), 32, |
| spans[0].x, y, spans[1].x - spans[0].x, h, |
| r->u.fill.pixel); |
| } else { |
| int yy = y, hh = h; |
| do { |
| int len = spans[1].x - spans[0].x; |
| uint32_t *d = (uint32_t*)(r->u.fill.data + r->u.fill.stride*yy + spans[0].x*4); |
| while (len--) |
| *d++ = r->u.fill.pixel; |
| yy++; |
| } while (--hh); |
| } |
| } else { |
| int yy = y, hh = h; |
| do { |
| int len = spans[1].x - spans[0].x; |
| uint32_t *d = (uint32_t *)(r->u.fill.data + r->u.fill.stride*yy + spans[0].x*4); |
| while (len--) { |
| *d = lerp8x4 (r->u.fill.pixel, a, *d); |
| d++; |
| } |
| yy++; |
| } while (--hh); |
| } |
| } |
| spans++; |
| } while (--num_spans > 1); |
| } |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static cairo_status_t |
| _fill_a8_lerp_spans (void *abstract_renderer, int y, int h, |
| const cairo_half_open_span_t *spans, unsigned num_spans) |
| { |
| cairo_image_span_renderer_t *r = abstract_renderer; |
| |
| if (num_spans == 0) |
| return CAIRO_STATUS_SUCCESS; |
| |
| if (likely(h == 1)) { |
| do { |
| uint8_t a = mul8_8 (spans[0].coverage, r->bpp); |
| if (a) { |
| int len = spans[1].x - spans[0].x; |
| uint8_t *d = r->u.fill.data + r->u.fill.stride*y + spans[0].x; |
| uint16_t p = (uint16_t)a * r->u.fill.pixel + 0x7f; |
| uint16_t ia = ~a; |
| while (len--) { |
| uint16_t t = *d*ia + p; |
| *d++ = (t + (t>>8)) >> 8; |
| } |
| } |
| spans++; |
| } while (--num_spans > 1); |
| } else { |
| do { |
| uint8_t a = mul8_8 (spans[0].coverage, r->bpp); |
| if (a) { |
| int yy = y, hh = h; |
| uint16_t p = (uint16_t)a * r->u.fill.pixel + 0x7f; |
| uint16_t ia = ~a; |
| do { |
| int len = spans[1].x - spans[0].x; |
| uint8_t *d = r->u.fill.data + r->u.fill.stride*yy + spans[0].x; |
| while (len--) { |
| uint16_t t = *d*ia + p; |
| *d++ = (t + (t>>8)) >> 8; |
| } |
| yy++; |
| } while (--hh); |
| } |
| spans++; |
| } while (--num_spans > 1); |
| } |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static cairo_status_t |
| _fill_xrgb32_lerp_spans (void *abstract_renderer, int y, int h, |
| const cairo_half_open_span_t *spans, unsigned num_spans) |
| { |
| cairo_image_span_renderer_t *r = abstract_renderer; |
| |
| if (num_spans == 0) |
| return CAIRO_STATUS_SUCCESS; |
| |
| if (likely(h == 1)) { |
| do { |
| uint8_t a = mul8_8 (spans[0].coverage, r->bpp); |
| if (a) { |
| int len = spans[1].x - spans[0].x; |
| uint32_t *d = (uint32_t*)(r->u.fill.data + r->u.fill.stride*y + spans[0].x*4); |
| while (len--) { |
| *d = lerp8x4 (r->u.fill.pixel, a, *d); |
| d++; |
| } |
| } |
| spans++; |
| } while (--num_spans > 1); |
| } else { |
| do { |
| uint8_t a = mul8_8 (spans[0].coverage, r->bpp); |
| if (a) { |
| int yy = y, hh = h; |
| do { |
| int len = spans[1].x - spans[0].x; |
| uint32_t *d = (uint32_t *)(r->u.fill.data + r->u.fill.stride*yy + spans[0].x*4); |
| while (len--) { |
| *d = lerp8x4 (r->u.fill.pixel, a, *d); |
| d++; |
| } |
| yy++; |
| } while (--hh); |
| } |
| spans++; |
| } while (--num_spans > 1); |
| } |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static cairo_status_t |
| _blit_xrgb32_lerp_spans (void *abstract_renderer, int y, int h, |
| const cairo_half_open_span_t *spans, unsigned num_spans) |
| { |
| cairo_image_span_renderer_t *r = abstract_renderer; |
| |
| if (num_spans == 0) |
| return CAIRO_STATUS_SUCCESS; |
| |
| if (likely(h == 1)) { |
| uint8_t *src = r->u.blit.src_data + y*r->u.blit.src_stride; |
| uint8_t *dst = r->u.blit.data + y*r->u.blit.stride; |
| do { |
| uint8_t a = mul8_8 (spans[0].coverage, r->bpp); |
| if (a) { |
| uint32_t *s = (uint32_t*)src + spans[0].x; |
| uint32_t *d = (uint32_t*)dst + spans[0].x; |
| int len = spans[1].x - spans[0].x; |
| if (a == 0xff) { |
| if (len == 1) |
| *d = *s; |
| else |
| memcpy(d, s, len*4); |
| } else { |
| while (len--) { |
| *d = lerp8x4 (*s, a, *d); |
| s++, d++; |
| } |
| } |
| } |
| spans++; |
| } while (--num_spans > 1); |
| } else { |
| do { |
| uint8_t a = mul8_8 (spans[0].coverage, r->bpp); |
| if (a) { |
| int yy = y, hh = h; |
| do { |
| uint32_t *s = (uint32_t *)(r->u.blit.src_data + yy*r->u.blit.src_stride + spans[0].x * 4); |
| uint32_t *d = (uint32_t *)(r->u.blit.data + yy*r->u.blit.stride + spans[0].x * 4); |
| int len = spans[1].x - spans[0].x; |
| if (a == 0xff) { |
| if (len == 1) |
| *d = *s; |
| else |
| memcpy(d, s, len * 4); |
| } else { |
| while (len--) { |
| *d = lerp8x4 (*s, a, *d); |
| s++, d++; |
| } |
| } |
| yy++; |
| } while (--hh); |
| } |
| spans++; |
| } while (--num_spans > 1); |
| } |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static cairo_status_t |
| _inplace_spans (void *abstract_renderer, |
| int y, int h, |
| const cairo_half_open_span_t *spans, |
| unsigned num_spans) |
| { |
| cairo_image_span_renderer_t *r = abstract_renderer; |
| uint8_t *mask; |
| int x0, x1; |
| |
| if (num_spans == 0) |
| return CAIRO_STATUS_SUCCESS; |
| |
| if (num_spans == 2 && spans[0].coverage == 0xff) { |
| pixman_image_composite32 (r->op, r->src, NULL, r->u.composite.dst, |
| spans[0].x + r->u.composite.src_x, |
| y + r->u.composite.src_y, |
| 0, 0, |
| spans[0].x, y, |
| spans[1].x - spans[0].x, h); |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| mask = (uint8_t *)pixman_image_get_data (r->mask); |
| x1 = x0 = spans[0].x; |
| do { |
| int len = spans[1].x - spans[0].x; |
| *mask++ = spans[0].coverage; |
| if (len > 1) { |
| if (len >= r->u.composite.run_length && spans[0].coverage == 0xff) { |
| if (x1 != x0) { |
| pixman_image_composite32 (r->op, r->src, r->mask, r->u.composite.dst, |
| x0 + r->u.composite.src_x, |
| y + r->u.composite.src_y, |
| 0, 0, |
| x0, y, |
| x1 - x0, h); |
| } |
| pixman_image_composite32 (r->op, r->src, NULL, r->u.composite.dst, |
| spans[0].x + r->u.composite.src_x, |
| y + r->u.composite.src_y, |
| 0, 0, |
| spans[0].x, y, |
| len, h); |
| mask = (uint8_t *)pixman_image_get_data (r->mask); |
| x0 = spans[1].x; |
| } else if (spans[0].coverage == 0x0 && |
| x1 - x0 > r->u.composite.run_length) { |
| pixman_image_composite32 (r->op, r->src, r->mask, r->u.composite.dst, |
| x0 + r->u.composite.src_x, |
| y + r->u.composite.src_y, |
| 0, 0, |
| x0, y, |
| x1 - x0, h); |
| mask = (uint8_t *)pixman_image_get_data (r->mask); |
| x0 = spans[1].x; |
| }else { |
| memset (mask, spans[0].coverage, |