/* cairo - a vector graphics library with display and print output * * Copyright © 2009 Eric Anholt * Copyright © 2009 Chris Wilson * Copyright © 2005,2010 Red Hat, Inc * * 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 Red Hat, Inc. * * Contributor(s): * Benjamin Otte * Carl Worth * Chris Wilson * Eric Anholt */ #include "cairoint.h" #include "cairo-composite-rectangles-private.h" #include "cairo-default-context-private.h" #include "cairo-error-private.h" #include "cairo-gl-private.h" #include "cairo-image-surface-inline.h" cairo_status_t _cairo_gl_surface_acquire_dest_image (void *abstract_surface, cairo_rectangle_int_t *interest_rect, cairo_image_surface_t **image_out, cairo_rectangle_int_t *image_rect_out, void **image_extra) { cairo_gl_surface_t *surface = abstract_surface; cairo_int_status_t status; status = _cairo_gl_surface_deferred_clear (surface); if (unlikely (status)) return status; *image_extra = NULL; return _cairo_gl_surface_get_image (surface, interest_rect, image_out, image_rect_out); } void _cairo_gl_surface_release_dest_image (void *abstract_surface, cairo_rectangle_int_t *interest_rect, cairo_image_surface_t *image, cairo_rectangle_int_t *image_rect, void *image_extra) { cairo_status_t status; status = _cairo_gl_surface_draw_image (abstract_surface, image, 0, 0, image->width, image->height, image_rect->x, image_rect->y); /* as we created the image, its format should be directly applicable */ assert (status == CAIRO_STATUS_SUCCESS); cairo_surface_destroy (&image->base); } cairo_status_t _cairo_gl_surface_clone_similar (void *abstract_surface, cairo_surface_t *src, int src_x, int src_y, int width, int height, int *clone_offset_x, int *clone_offset_y, cairo_surface_t **clone_out) { cairo_gl_surface_t *surface = abstract_surface; cairo_int_status_t status; /* XXX: Use GLCopyTexImage2D to clone non-texture-surfaces */ if (src->device == surface->base.device && _cairo_gl_surface_is_texture ((cairo_gl_surface_t *) src)) { status = _cairo_gl_surface_deferred_clear ((cairo_gl_surface_t *)src); if (unlikely (status)) return status; *clone_offset_x = 0; *clone_offset_y = 0; *clone_out = cairo_surface_reference (src); return CAIRO_STATUS_SUCCESS; } else if (_cairo_surface_is_image (src)) { cairo_image_surface_t *image_src = (cairo_image_surface_t *)src; cairo_gl_surface_t *clone; clone = (cairo_gl_surface_t *) _cairo_gl_surface_create_similar (&surface->base, src->content, width, height); if (clone == NULL) return UNSUPPORTED ("create_similar failed"); if (clone->base.status) return clone->base.status; status = _cairo_gl_surface_draw_image (clone, image_src, src_x, src_y, width, height, 0, 0); if (status) { cairo_surface_destroy (&clone->base); return status; } *clone_out = &clone->base; *clone_offset_x = src_x; *clone_offset_y = src_y; return CAIRO_STATUS_SUCCESS; } return UNSUPPORTED ("unknown src surface type in clone_similar"); } /* Creates a cairo-gl pattern surface for the given trapezoids */ static cairo_status_t _cairo_gl_get_traps_pattern (cairo_gl_surface_t *dst, int dst_x, int dst_y, int width, int height, cairo_trapezoid_t *traps, int num_traps, cairo_antialias_t antialias, cairo_surface_pattern_t *pattern) { pixman_format_code_t pixman_format; pixman_image_t *image; cairo_surface_t *surface; int i; pixman_format = antialias != CAIRO_ANTIALIAS_NONE ? PIXMAN_a8 : PIXMAN_a1, image = pixman_image_create_bits (pixman_format, width, height, NULL, 0); if (unlikely (image == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); for (i = 0; i < num_traps; i++) { pixman_trapezoid_t trap; trap.top = _cairo_fixed_to_16_16 (traps[i].top); trap.bottom = _cairo_fixed_to_16_16 (traps[i].bottom); trap.left.p1.x = _cairo_fixed_to_16_16 (traps[i].left.p1.x); trap.left.p1.y = _cairo_fixed_to_16_16 (traps[i].left.p1.y); trap.left.p2.x = _cairo_fixed_to_16_16 (traps[i].left.p2.x); trap.left.p2.y = _cairo_fixed_to_16_16 (traps[i].left.p2.y); trap.right.p1.x = _cairo_fixed_to_16_16 (traps[i].right.p1.x); trap.right.p1.y = _cairo_fixed_to_16_16 (traps[i].right.p1.y); trap.right.p2.x = _cairo_fixed_to_16_16 (traps[i].right.p2.x); trap.right.p2.y = _cairo_fixed_to_16_16 (traps[i].right.p2.y); pixman_rasterize_trapezoid (image, &trap, -dst_x, -dst_y); } surface = _cairo_image_surface_create_for_pixman_image (image, pixman_format); if (unlikely (surface->status)) { pixman_image_unref (image); return surface->status; } _cairo_pattern_init_for_surface (pattern, surface); cairo_surface_destroy (surface); return CAIRO_STATUS_SUCCESS; } cairo_int_status_t _cairo_gl_surface_composite (cairo_operator_t op, const cairo_pattern_t *src, const cairo_pattern_t *mask, void *abstract_dst, int src_x, int src_y, int mask_x, int mask_y, int dst_x, int dst_y, unsigned int width, unsigned int height, cairo_region_t *clip_region) { cairo_gl_surface_t *dst = abstract_dst; cairo_gl_context_t *ctx; cairo_status_t status; cairo_gl_composite_t setup; cairo_rectangle_int_t rect = { dst_x, dst_y, width, height }; int dx, dy; status = _cairo_gl_surface_deferred_clear (dst); if (unlikely (status)) return status; if (op == CAIRO_OPERATOR_SOURCE && mask == NULL && src->type == CAIRO_PATTERN_TYPE_SURFACE && _cairo_surface_is_image (((cairo_surface_pattern_t *) src)->surface) && _cairo_matrix_is_integer_translation (&src->matrix, &dx, &dy)) { cairo_image_surface_t *image = (cairo_image_surface_t *) ((cairo_surface_pattern_t *) src)->surface; dx += src_x; dy += src_y; if (dx >= 0 && dy >= 0 && dx + width <= (unsigned int) image->width && dy + height <= (unsigned int) image->height) { status = _cairo_gl_surface_draw_image (dst, image, dx, dy, width, height, dst_x, dst_y); if (status != CAIRO_INT_STATUS_UNSUPPORTED) return status; } } status = _cairo_gl_composite_init (&setup, op, dst, mask && mask->has_component_alpha, &rect); if (unlikely (status)) goto CLEANUP; status = _cairo_gl_composite_set_source (&setup, src, src_x, src_y, dst_x, dst_y, width, height); if (unlikely (status)) goto CLEANUP; status = _cairo_gl_composite_set_mask (&setup, mask, mask_x, mask_y, dst_x, dst_y, width, height); if (unlikely (status)) goto CLEANUP; status = _cairo_gl_composite_begin (&setup, &ctx); if (unlikely (status)) goto CLEANUP; if (clip_region != NULL) { int i, num_rectangles; num_rectangles = cairo_region_num_rectangles (clip_region); for (i = 0; i < num_rectangles; i++) { cairo_rectangle_int_t rect; cairo_region_get_rectangle (clip_region, i, &rect); _cairo_gl_composite_emit_rect (ctx, rect.x, rect.y, rect.x + rect.width, rect.y + rect.height, 0); } } else { _cairo_gl_composite_emit_rect (ctx, dst_x, dst_y, dst_x + width, dst_y + height, 0); } status = _cairo_gl_context_release (ctx, status); CLEANUP: _cairo_gl_composite_fini (&setup); return status; } cairo_int_status_t _cairo_gl_surface_composite_trapezoids (cairo_operator_t op, const cairo_pattern_t *pattern, void *abstract_dst, cairo_antialias_t antialias, int src_x, int src_y, int dst_x, int dst_y, unsigned int width, unsigned int height, cairo_trapezoid_t *traps, int num_traps, cairo_region_t *clip_region) { cairo_gl_surface_t *dst = abstract_dst; cairo_surface_pattern_t traps_pattern; cairo_int_status_t status; if (! _cairo_gl_operator_is_supported (op)) return UNSUPPORTED ("unsupported operator"); status = _cairo_gl_surface_deferred_clear (dst); if (unlikely (status)) return status; status = _cairo_gl_get_traps_pattern (dst, dst_x, dst_y, width, height, traps, num_traps, antialias, &traps_pattern); if (unlikely (status)) return status; status = _cairo_gl_surface_composite (op, pattern, &traps_pattern.base, dst, src_x, src_y, 0, 0, dst_x, dst_y, width, height, clip_region); _cairo_pattern_fini (&traps_pattern.base); assert (status != CAIRO_INT_STATUS_UNSUPPORTED); return status; } cairo_int_status_t _cairo_gl_surface_fill_rectangles (void *abstract_dst, cairo_operator_t op, const cairo_color_t *color, cairo_rectangle_int_t *rects, int num_rects) { cairo_gl_surface_t *dst = abstract_dst; cairo_solid_pattern_t solid; cairo_gl_context_t *ctx; cairo_status_t status; cairo_gl_composite_t setup; int i; status = _cairo_gl_surface_deferred_clear (dst); if (unlikely (status)) return status; status = _cairo_gl_composite_init (&setup, op, dst, FALSE, /* XXX */ NULL); if (unlikely (status)) goto CLEANUP; _cairo_pattern_init_solid (&solid, color); status = _cairo_gl_composite_set_source (&setup, &solid.base, 0, 0, 0, 0, 0, 0); if (unlikely (status)) goto CLEANUP; status = _cairo_gl_composite_set_mask (&setup, NULL, 0, 0, 0, 0, 0, 0); if (unlikely (status)) goto CLEANUP; status = _cairo_gl_composite_begin (&setup, &ctx); if (unlikely (status)) goto CLEANUP; for (i = 0; i < num_rects; i++) { _cairo_gl_composite_emit_rect (ctx, rects[i].x, rects[i].y, rects[i].x + rects[i].width, rects[i].y + rects[i].height, 0); } status = _cairo_gl_context_release (ctx, status); CLEANUP: _cairo_gl_composite_fini (&setup); return status; } typedef struct _cairo_gl_surface_span_renderer { cairo_span_renderer_t base; cairo_gl_composite_t setup; int xmin, xmax; int ymin, ymax; cairo_gl_context_t *ctx; } cairo_gl_surface_span_renderer_t; static cairo_status_t _cairo_gl_render_bounded_spans (void *abstract_renderer, int y, int height, const cairo_half_open_span_t *spans, unsigned num_spans) { cairo_gl_surface_span_renderer_t *renderer = abstract_renderer; if (num_spans == 0) return CAIRO_STATUS_SUCCESS; do { if (spans[0].coverage) { _cairo_gl_composite_emit_rect (renderer->ctx, spans[0].x, y, spans[1].x, y + height, spans[0].coverage); } spans++; } while (--num_spans > 1); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_gl_render_unbounded_spans (void *abstract_renderer, int y, int height, const cairo_half_open_span_t *spans, unsigned num_spans) { cairo_gl_surface_span_renderer_t *renderer = abstract_renderer; if (y > renderer->ymin) { _cairo_gl_composite_emit_rect (renderer->ctx, renderer->xmin, renderer->ymin, renderer->xmax, y, 0); } if (num_spans == 0) { _cairo_gl_composite_emit_rect (renderer->ctx, renderer->xmin, y, renderer->xmax, y + height, 0); } else { if (spans[0].x != renderer->xmin) { _cairo_gl_composite_emit_rect (renderer->ctx, renderer->xmin, y, spans[0].x, y + height, 0); } do { _cairo_gl_composite_emit_rect (renderer->ctx, spans[0].x, y, spans[1].x, y + height, spans[0].coverage); spans++; } while (--num_spans > 1); if (spans[0].x != renderer->xmax) { _cairo_gl_composite_emit_rect (renderer->ctx, spans[0].x, y, renderer->xmax, y + height, 0); } } renderer->ymin = y + height; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_gl_finish_unbounded_spans (void *abstract_renderer) { cairo_gl_surface_span_renderer_t *renderer = abstract_renderer; if (renderer->ymax > renderer->ymin) { _cairo_gl_composite_emit_rect (renderer->ctx, renderer->xmin, renderer->ymin, renderer->xmax, renderer->ymax, 0); } return _cairo_gl_context_release (renderer->ctx, CAIRO_STATUS_SUCCESS); } static cairo_status_t _cairo_gl_finish_bounded_spans (void *abstract_renderer) { cairo_gl_surface_span_renderer_t *renderer = abstract_renderer; return _cairo_gl_context_release (renderer->ctx, CAIRO_STATUS_SUCCESS); } static void _cairo_gl_surface_span_renderer_destroy (void *abstract_renderer) { cairo_gl_surface_span_renderer_t *renderer = abstract_renderer; if (!renderer) return; _cairo_gl_composite_fini (&renderer->setup); free (renderer); } cairo_bool_t _cairo_gl_surface_check_span_renderer (cairo_operator_t op, const cairo_pattern_t *pattern, void *abstract_dst, cairo_antialias_t antialias) { if (! _cairo_gl_operator_is_supported (op)) return FALSE; return TRUE; (void) pattern; (void) abstract_dst; (void) antialias; } cairo_span_renderer_t * _cairo_gl_surface_create_span_renderer (cairo_operator_t op, const cairo_pattern_t *src, void *abstract_dst, cairo_antialias_t antialias, const cairo_composite_rectangles_t *rects) { cairo_gl_surface_t *dst = abstract_dst; cairo_gl_surface_span_renderer_t *renderer; cairo_status_t status; const cairo_rectangle_int_t *extents; status = _cairo_gl_surface_deferred_clear (dst); if (unlikely (status)) return _cairo_span_renderer_create_in_error (status); renderer = calloc (1, sizeof (*renderer)); if (unlikely (renderer == NULL)) return _cairo_span_renderer_create_in_error (CAIRO_STATUS_NO_MEMORY); renderer->base.destroy = _cairo_gl_surface_span_renderer_destroy; if (rects->is_bounded) { renderer->base.render_rows = _cairo_gl_render_bounded_spans; renderer->base.finish = _cairo_gl_finish_bounded_spans; extents = &rects->bounded; } else { renderer->base.render_rows = _cairo_gl_render_unbounded_spans; renderer->base.finish = _cairo_gl_finish_unbounded_spans; extents = &rects->unbounded; } renderer->xmin = extents->x; renderer->xmax = extents->x + extents->width; renderer->ymin = extents->y; renderer->ymax = extents->y + extents->height; status = _cairo_gl_composite_init (&renderer->setup, op, dst, FALSE, extents); if (unlikely (status)) goto FAIL; status = _cairo_gl_composite_set_source (&renderer->setup, src, extents->x, extents->y, extents->x, extents->y, extents->width, extents->height); if (unlikely (status)) goto FAIL; _cairo_gl_composite_set_spans (&renderer->setup); _cairo_gl_composite_set_clip_region (&renderer->setup, _cairo_clip_get_region (rects->clip)); status = _cairo_gl_composite_begin (&renderer->setup, &renderer->ctx); if (unlikely (status)) goto FAIL; return &renderer->base; FAIL: _cairo_gl_composite_fini (&renderer->setup); free (renderer); return _cairo_span_renderer_create_in_error (status); }