cairo-gl-surface.c [plain text]
#include "cairoint.h"
#include "cairo-composite-rectangles-private.h"
#include "cairo-error-private.h"
#include "cairo-gl-private.h"
static 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);
static 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);
static cairo_status_t
_cairo_gl_surface_flush (void *abstract_surface);
static cairo_bool_t _cairo_surface_is_gl (cairo_surface_t *surface)
{
return surface->backend == &_cairo_gl_surface_backend;
}
cairo_bool_t
_cairo_gl_get_image_format_and_type (pixman_format_code_t pixman_format,
GLenum *internal_format, GLenum *format,
GLenum *type, cairo_bool_t *has_alpha)
{
*has_alpha = TRUE;
switch (pixman_format) {
case PIXMAN_a8r8g8b8:
*internal_format = GL_RGBA;
*format = GL_BGRA;
*type = GL_UNSIGNED_INT_8_8_8_8_REV;
return TRUE;
case PIXMAN_x8r8g8b8:
*internal_format = GL_RGB;
*format = GL_BGRA;
*type = GL_UNSIGNED_INT_8_8_8_8_REV;
*has_alpha = FALSE;
return TRUE;
case PIXMAN_a8b8g8r8:
*internal_format = GL_RGBA;
*format = GL_RGBA;
*type = GL_UNSIGNED_INT_8_8_8_8_REV;
return TRUE;
case PIXMAN_x8b8g8r8:
*internal_format = GL_RGB;
*format = GL_RGBA;
*type = GL_UNSIGNED_INT_8_8_8_8_REV;
*has_alpha = FALSE;
return TRUE;
case PIXMAN_b8g8r8a8:
*internal_format = GL_RGBA;
*format = GL_BGRA;
*type = GL_UNSIGNED_INT_8_8_8_8;
return TRUE;
case PIXMAN_b8g8r8x8:
*internal_format = GL_RGB;
*format = GL_BGRA;
*type = GL_UNSIGNED_INT_8_8_8_8;
*has_alpha = FALSE;
return TRUE;
case PIXMAN_r8g8b8:
*internal_format = GL_RGB;
*format = GL_RGB;
*type = GL_UNSIGNED_BYTE;
return TRUE;
case PIXMAN_b8g8r8:
*internal_format = GL_RGB;
*format = GL_BGR;
*type = GL_UNSIGNED_BYTE;
return TRUE;
case PIXMAN_r5g6b5:
*internal_format = GL_RGB;
*format = GL_RGB;
*type = GL_UNSIGNED_SHORT_5_6_5;
return TRUE;
case PIXMAN_b5g6r5:
*internal_format = GL_RGB;
*format = GL_RGB;
*type = GL_UNSIGNED_SHORT_5_6_5_REV;
return TRUE;
case PIXMAN_a1r5g5b5:
*internal_format = GL_RGBA;
*format = GL_BGRA;
*type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
return TRUE;
case PIXMAN_x1r5g5b5:
*internal_format = GL_RGB;
*format = GL_BGRA;
*type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
*has_alpha = FALSE;
return TRUE;
case PIXMAN_a1b5g5r5:
*internal_format = GL_RGBA;
*format = GL_RGBA;
*type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
return TRUE;
case PIXMAN_x1b5g5r5:
*internal_format = GL_RGB;
*format = GL_RGBA;
*type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
*has_alpha = FALSE;
return TRUE;
case PIXMAN_a8:
*internal_format = GL_ALPHA;
*format = GL_ALPHA;
*type = GL_UNSIGNED_BYTE;
return TRUE;
case PIXMAN_a2b10g10r10:
case PIXMAN_x2b10g10r10:
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_c8:
case PIXMAN_x4a4:
case PIXMAN_x4g4:
case PIXMAN_a4:
case PIXMAN_r1g2b1:
case PIXMAN_b1g2r1:
case PIXMAN_a1r1g1b1:
case PIXMAN_a1b1g1r1:
case PIXMAN_c4:
case PIXMAN_g4:
case PIXMAN_a1:
case PIXMAN_g1:
case PIXMAN_yuy2:
case PIXMAN_yv12:
case PIXMAN_x2r10g10b10:
case PIXMAN_a2r10g10b10:
default:
return FALSE;
}
}
cairo_bool_t
_cairo_gl_operator_is_supported (cairo_operator_t op)
{
return op < CAIRO_OPERATOR_SATURATE;
}
void
_cairo_gl_surface_init (cairo_device_t *device,
cairo_gl_surface_t *surface,
cairo_content_t content,
int width, int height)
{
_cairo_surface_init (&surface->base,
&_cairo_gl_surface_backend,
device,
content);
surface->width = width;
surface->height = height;
}
static cairo_surface_t *
_cairo_gl_surface_create_scratch_for_texture (cairo_gl_context_t *ctx,
cairo_content_t content,
GLuint tex,
int width,
int height)
{
cairo_gl_surface_t *surface;
assert (width <= ctx->max_framebuffer_size && height <= ctx->max_framebuffer_size);
surface = calloc (1, sizeof (cairo_gl_surface_t));
if (unlikely (surface == NULL))
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
_cairo_gl_surface_init (&ctx->base, surface, content, width, height);
surface->tex = tex;
_cairo_gl_context_activate (ctx, CAIRO_GL_TEX_TEMP);
glBindTexture (ctx->tex_target, surface->tex);
glTexParameteri (ctx->tex_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri (ctx->tex_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
return &surface->base;
}
static cairo_surface_t *
_cairo_gl_surface_create_scratch (cairo_gl_context_t *ctx,
cairo_content_t content,
int width,
int height)
{
cairo_gl_surface_t *surface;
GLenum format;
GLuint tex;
glGenTextures (1, &tex);
surface = (cairo_gl_surface_t *)
_cairo_gl_surface_create_scratch_for_texture (ctx, content,
tex, width, height);
if (unlikely (surface->base.status))
return &surface->base;
surface->owns_tex = TRUE;
if (width < 1)
width = 1;
if (height < 1)
height = 1;
switch (content) {
default:
ASSERT_NOT_REACHED;
case CAIRO_CONTENT_COLOR_ALPHA:
format = GL_RGBA;
break;
case CAIRO_CONTENT_ALPHA:
format = GL_RGBA;
break;
case CAIRO_CONTENT_COLOR:
format = GL_RGBA;
break;
}
glTexImage2D (ctx->tex_target, 0, format, width, height, 0,
format, GL_UNSIGNED_BYTE, NULL);
return &surface->base;
}
static cairo_status_t
_cairo_gl_surface_clear (cairo_gl_surface_t *surface,
const cairo_color_t *color)
{
cairo_gl_context_t *ctx;
cairo_status_t status;
double r, g, b, a;
status = _cairo_gl_context_acquire (surface->base.device, &ctx);
if (unlikely (status))
return status;
_cairo_gl_context_set_destination (ctx, surface);
if (surface->base.content & CAIRO_CONTENT_COLOR) {
r = color->red * color->alpha;
g = color->green * color->alpha;
b = color->blue * color->alpha;
} else {
r = g = b = 0;
}
if (surface->base.content & CAIRO_CONTENT_ALPHA) {
a = color->alpha;
} else {
a = 1.0;
}
glDisable (GL_SCISSOR_TEST);
glClearColor (r, g, b, a);
glClear (GL_COLOR_BUFFER_BIT);
return _cairo_gl_context_release (ctx, status);
}
cairo_surface_t *
cairo_gl_surface_create (cairo_device_t *abstract_device,
cairo_content_t content,
int width,
int height)
{
cairo_gl_context_t *ctx;
cairo_gl_surface_t *surface;
cairo_status_t status;
if (! CAIRO_CONTENT_VALID (content))
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_CONTENT));
if (abstract_device == NULL) {
return cairo_image_surface_create (_cairo_format_from_content (content),
width, height);
}
if (abstract_device->status)
return _cairo_surface_create_in_error (abstract_device->status);
if (abstract_device->backend->type != CAIRO_DEVICE_TYPE_GL)
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
status = _cairo_gl_context_acquire (abstract_device, &ctx);
if (unlikely (status))
return _cairo_surface_create_in_error (status);
surface = (cairo_gl_surface_t *)
_cairo_gl_surface_create_scratch (ctx, content, width, height);
if (unlikely (surface->base.status)) {
status = _cairo_gl_context_release (ctx, surface->base.status);
cairo_surface_destroy (&surface->base);
return _cairo_surface_create_in_error (status);
}
status = _cairo_gl_surface_clear (surface, CAIRO_COLOR_TRANSPARENT);
status = _cairo_gl_context_release (ctx, status);
if (unlikely (status)) {
cairo_surface_destroy (&surface->base);
return _cairo_surface_create_in_error (status);
}
return &surface->base;
}
slim_hidden_def (cairo_gl_surface_create);
cairo_surface_t *
cairo_gl_surface_create_for_texture (cairo_device_t *abstract_device,
cairo_content_t content,
unsigned int tex,
int width,
int height)
{
cairo_gl_context_t *ctx;
cairo_gl_surface_t *surface;
cairo_status_t status;
if (! CAIRO_CONTENT_VALID (content))
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_CONTENT));
if (abstract_device == NULL)
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NULL_POINTER));
if (abstract_device->status)
return _cairo_surface_create_in_error (abstract_device->status);
if (abstract_device->backend->type != CAIRO_DEVICE_TYPE_GL)
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
status = _cairo_gl_context_acquire (abstract_device, &ctx);
if (unlikely (status))
return _cairo_surface_create_in_error (status);
surface = (cairo_gl_surface_t *)
_cairo_gl_surface_create_scratch_for_texture (ctx, content,
tex, width, height);
status = _cairo_gl_context_release (ctx, status);
return &surface->base;
}
slim_hidden_def (cairo_gl_surface_create_for_texture);
void
cairo_gl_surface_set_size (cairo_surface_t *abstract_surface,
int width,
int height)
{
cairo_gl_surface_t *surface = (cairo_gl_surface_t *) abstract_surface;
cairo_status_t status;
if (unlikely (abstract_surface->status))
return;
if (unlikely (abstract_surface->finished)) {
status = _cairo_surface_set_error (abstract_surface,
_cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
return;
}
if (! _cairo_surface_is_gl (abstract_surface) ||
! _cairo_gl_surface_is_texture (surface)) {
status = _cairo_surface_set_error (abstract_surface,
_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
return;
}
surface->width = width;
surface->height = height;
}
int
cairo_gl_surface_get_width (cairo_surface_t *abstract_surface)
{
cairo_gl_surface_t *surface = (cairo_gl_surface_t *) abstract_surface;
if (! _cairo_surface_is_gl (abstract_surface))
return 0;
return surface->width;
}
int
cairo_gl_surface_get_height (cairo_surface_t *abstract_surface)
{
cairo_gl_surface_t *surface = (cairo_gl_surface_t *) abstract_surface;
if (! _cairo_surface_is_gl (abstract_surface))
return 0;
return surface->height;
}
void
cairo_gl_surface_swapbuffers (cairo_surface_t *abstract_surface)
{
cairo_gl_surface_t *surface = (cairo_gl_surface_t *) abstract_surface;
cairo_status_t status;
if (unlikely (abstract_surface->status))
return;
if (unlikely (abstract_surface->finished)) {
status = _cairo_surface_set_error (abstract_surface,
_cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
return;
}
if (! _cairo_surface_is_gl (abstract_surface)) {
status = _cairo_surface_set_error (abstract_surface,
CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
return;
}
if (! _cairo_gl_surface_is_texture (surface)) {
cairo_gl_context_t *ctx;
cairo_status_t status;
status = _cairo_gl_context_acquire (surface->base.device, &ctx);
if (unlikely (status))
return;
cairo_surface_flush (abstract_surface);
ctx->swap_buffers (ctx, surface);
status = _cairo_gl_context_release (ctx, status);
if (status)
status = _cairo_surface_set_error (abstract_surface, status);
}
}
static cairo_surface_t *
_cairo_gl_surface_create_similar (void *abstract_surface,
cairo_content_t content,
int width,
int height)
{
cairo_surface_t *surface = abstract_surface;
cairo_gl_context_t *ctx;
cairo_status_t status;
if (width < 1 || height < 1)
return cairo_image_surface_create (_cairo_format_from_content (content),
width, height);
status = _cairo_gl_context_acquire (surface->device, &ctx);
if (unlikely (status))
return _cairo_surface_create_in_error (status);
if (width > ctx->max_framebuffer_size ||
height > ctx->max_framebuffer_size)
{
surface = NULL;
goto RELEASE;
}
surface = _cairo_gl_surface_create_scratch (ctx, content, width, height);
RELEASE:
status = _cairo_gl_context_release (ctx, status);
if (unlikely (status)) {
cairo_surface_destroy (surface);
return _cairo_surface_create_in_error (status);
}
return surface;
}
cairo_status_t
_cairo_gl_surface_draw_image (cairo_gl_surface_t *dst,
cairo_image_surface_t *src,
int src_x, int src_y,
int width, int height,
int dst_x, int dst_y)
{
GLenum internal_format, format, type;
cairo_bool_t has_alpha;
cairo_image_surface_t *clone = NULL;
cairo_gl_context_t *ctx;
int cpp;
cairo_status_t status = CAIRO_STATUS_SUCCESS;
if (! _cairo_gl_get_image_format_and_type (src->pixman_format,
&internal_format,
&format,
&type,
&has_alpha))
{
cairo_bool_t is_supported;
clone = _cairo_image_surface_coerce (src);
if (unlikely (clone->base.status))
return clone->base.status;
is_supported =
_cairo_gl_get_image_format_and_type (clone->pixman_format,
&internal_format,
&format,
&type,
&has_alpha);
assert (is_supported);
src = clone;
}
cpp = PIXMAN_FORMAT_BPP (src->pixman_format) / 8;
status = _cairo_gl_context_acquire (dst->base.device, &ctx);
if (unlikely (status))
return status;
status = _cairo_gl_surface_flush (&dst->base);
if (unlikely (status))
goto FAIL;
glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
glPixelStorei (GL_UNPACK_ROW_LENGTH, src->stride / cpp);
if (_cairo_gl_surface_is_texture (dst)) {
_cairo_gl_context_activate (ctx, CAIRO_GL_TEX_TEMP);
glBindTexture (ctx->tex_target, dst->tex);
glTexParameteri (ctx->tex_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri (ctx->tex_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexSubImage2D (ctx->tex_target, 0,
dst_x, dst_y, width, height,
format, type,
src->data + src_y * src->stride + src_x * cpp);
if (!has_alpha) {
cairo_rectangle_int_t rect;
rect.x = dst_x;
rect.y = dst_y;
rect.width = width;
rect.height = height;
_cairo_gl_composite_flush (ctx);
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
_cairo_gl_surface_fill_rectangles (dst,
CAIRO_OPERATOR_SOURCE,
CAIRO_COLOR_BLACK,
&rect, 1);
_cairo_gl_composite_flush (ctx);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
}
} else {
cairo_surface_t *tmp;
tmp = _cairo_gl_surface_create_scratch (ctx,
dst->base.content,
width, height);
if (unlikely (tmp->status)) {
cairo_surface_destroy (tmp);
goto FAIL;
}
status = _cairo_gl_surface_draw_image ((cairo_gl_surface_t *) tmp,
src,
src_x, src_y,
width, height,
0, 0);
if (status == CAIRO_STATUS_SUCCESS) {
cairo_surface_pattern_t tmp_pattern;
_cairo_pattern_init_for_surface (&tmp_pattern, tmp);
_cairo_gl_surface_composite (CAIRO_OPERATOR_SOURCE,
&tmp_pattern.base,
NULL,
dst,
0, 0,
0, 0,
dst_x, dst_y,
width, height,
NULL);
_cairo_pattern_fini (&tmp_pattern.base);
}
cairo_surface_destroy (tmp);
}
FAIL:
glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
status = _cairo_gl_context_release (ctx, status);
if (clone)
cairo_surface_destroy (&clone->base);
return status;
}
static cairo_status_t
_cairo_gl_surface_get_image (cairo_gl_surface_t *surface,
cairo_rectangle_int_t *interest,
cairo_image_surface_t **image_out,
cairo_rectangle_int_t *rect_out)
{
cairo_image_surface_t *image;
cairo_gl_context_t *ctx;
GLenum format, type;
cairo_format_t cairo_format;
unsigned int cpp;
cairo_status_t status;
if (surface->base.content == CAIRO_CONTENT_COLOR_ALPHA) {
format = GL_BGRA;
cairo_format = CAIRO_FORMAT_ARGB32;
type = GL_UNSIGNED_INT_8_8_8_8_REV;
cpp = 4;
} else if (surface->base.content == CAIRO_CONTENT_COLOR) {
format = GL_BGRA;
cairo_format = CAIRO_FORMAT_RGB24;
type = GL_UNSIGNED_INT_8_8_8_8_REV;
cpp = 4;
} else if (surface->base.content == CAIRO_CONTENT_ALPHA) {
format = GL_ALPHA;
cairo_format = CAIRO_FORMAT_A8;
type = GL_UNSIGNED_BYTE;
cpp = 1;
} else {
ASSERT_NOT_REACHED;
return CAIRO_INT_STATUS_UNSUPPORTED;
}
image = (cairo_image_surface_t*)
cairo_image_surface_create (cairo_format,
interest->width, interest->height);
if (unlikely (image->base.status))
return image->base.status;
status = _cairo_gl_context_acquire (surface->base.device, &ctx);
if (unlikely (status))
return status;
_cairo_gl_composite_flush (ctx);
_cairo_gl_context_set_destination (ctx, surface);
glPixelStorei (GL_PACK_ALIGNMENT, 1);
glPixelStorei (GL_PACK_ROW_LENGTH, image->stride / cpp);
if (! _cairo_gl_surface_is_texture (surface) && GLEW_MESA_pack_invert)
glPixelStorei (GL_PACK_INVERT_MESA, 1);
glReadPixels (interest->x, interest->y,
interest->width, interest->height,
format, type, image->data);
if (! _cairo_gl_surface_is_texture (surface) && GLEW_MESA_pack_invert)
glPixelStorei (GL_PACK_INVERT_MESA, 0);
status = _cairo_gl_context_release (ctx, status);
if (unlikely (status)) {
cairo_surface_destroy (&image->base);
return status;
}
*image_out = image;
if (rect_out != NULL)
*rect_out = *interest;
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_cairo_gl_surface_finish (void *abstract_surface)
{
cairo_gl_surface_t *surface = abstract_surface;
cairo_status_t status;
cairo_gl_context_t *ctx;
status = _cairo_gl_context_acquire (surface->base.device, &ctx);
if (unlikely (status))
return status;
if (ctx->operands[CAIRO_GL_TEX_SOURCE].type == CAIRO_GL_OPERAND_TEXTURE &&
ctx->operands[CAIRO_GL_TEX_SOURCE].texture.surface == surface)
_cairo_gl_context_destroy_operand (ctx, CAIRO_GL_TEX_SOURCE);
if (ctx->operands[CAIRO_GL_TEX_MASK].type == CAIRO_GL_OPERAND_TEXTURE &&
ctx->operands[CAIRO_GL_TEX_MASK].texture.surface == surface)
_cairo_gl_context_destroy_operand (ctx, CAIRO_GL_TEX_MASK);
if (ctx->current_target == surface)
ctx->current_target = NULL;
if (surface->depth)
glDeleteFramebuffersEXT (1, &surface->depth);
if (surface->fb)
glDeleteFramebuffersEXT (1, &surface->fb);
if (surface->owns_tex)
glDeleteTextures (1, &surface->tex);
return _cairo_gl_context_release (ctx, status);
}
static cairo_status_t
_cairo_gl_surface_acquire_source_image (void *abstract_surface,
cairo_image_surface_t **image_out,
void **image_extra)
{
cairo_gl_surface_t *surface = abstract_surface;
cairo_rectangle_int_t extents;
*image_extra = NULL;
extents.x = extents.y = 0;
extents.width = surface->width;
extents.height = surface->height;
return _cairo_gl_surface_get_image (surface, &extents, image_out, NULL);
}
static void
_cairo_gl_surface_release_source_image (void *abstract_surface,
cairo_image_surface_t *image,
void *image_extra)
{
cairo_surface_destroy (&image->base);
}
static cairo_status_t
_cairo_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;
*image_extra = NULL;
return _cairo_gl_surface_get_image (surface, interest_rect, image_out,
image_rect_out);
}
static 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);
assert (status == CAIRO_STATUS_SUCCESS);
cairo_surface_destroy (&image->base);
}
static 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;
if (src->device == surface->base.device &&
_cairo_gl_surface_is_texture ((cairo_gl_surface_t *) src)) {
*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;
cairo_status_t status;
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");
}
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;
}
static 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;
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;
}
static 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_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;
}
static 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_composite_init (&setup, op, dst,
FALSE,
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);
}
static 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;
}
static 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_region_t *clip_region)
{
cairo_gl_surface_t *dst = abstract_dst;
cairo_gl_surface_span_renderer_t *renderer;
cairo_status_t status;
const cairo_rectangle_int_t *extents;
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_mask_spans (&renderer->setup);
_cairo_gl_composite_set_clip_region (&renderer->setup, clip_region);
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);
}
static cairo_bool_t
_cairo_gl_surface_get_extents (void *abstract_surface,
cairo_rectangle_int_t *rectangle)
{
cairo_gl_surface_t *surface = abstract_surface;
rectangle->x = 0;
rectangle->y = 0;
rectangle->width = surface->width;
rectangle->height = surface->height;
return TRUE;
}
static void
_cairo_gl_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);
}
static cairo_status_t
_cairo_gl_surface_flush (void *abstract_surface)
{
cairo_gl_surface_t *surface = abstract_surface;
cairo_status_t status;
cairo_gl_context_t *ctx;
status = _cairo_gl_context_acquire (surface->base.device, &ctx);
if (unlikely (status))
return status;
if ((ctx->operands[CAIRO_GL_TEX_SOURCE].type == CAIRO_GL_OPERAND_TEXTURE &&
ctx->operands[CAIRO_GL_TEX_SOURCE].texture.surface == surface) ||
(ctx->operands[CAIRO_GL_TEX_MASK].type == CAIRO_GL_OPERAND_TEXTURE &&
ctx->operands[CAIRO_GL_TEX_MASK].texture.surface == surface) ||
(ctx->current_target == surface))
_cairo_gl_composite_flush (ctx);
return _cairo_gl_context_release (ctx, status);
}
static cairo_int_status_t
_cairo_gl_surface_paint (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_clip_t *clip)
{
if (clip == NULL) {
if (op == CAIRO_OPERATOR_CLEAR)
return _cairo_gl_surface_clear (abstract_surface, CAIRO_COLOR_TRANSPARENT);
else if (source->type == CAIRO_PATTERN_TYPE_SOLID &&
(op == CAIRO_OPERATOR_SOURCE ||
(op == CAIRO_OPERATOR_OVER && _cairo_pattern_is_opaque_solid (source)))) {
return _cairo_gl_surface_clear (abstract_surface,
&((cairo_solid_pattern_t *) source)->color);
}
}
return CAIRO_INT_STATUS_UNSUPPORTED;
}
static cairo_int_status_t
_cairo_gl_surface_polygon (cairo_gl_surface_t *dst,
cairo_operator_t op,
const cairo_pattern_t *src,
cairo_polygon_t *polygon,
cairo_fill_rule_t fill_rule,
cairo_antialias_t antialias,
const cairo_composite_rectangles_t *extents,
cairo_clip_t *clip)
{
cairo_status_t status;
cairo_region_t *clip_region = NULL;
if (clip != NULL) {
status = _cairo_clip_get_region (clip, &clip_region);
if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO))
return CAIRO_STATUS_SUCCESS;
if (unlikely (_cairo_status_is_error (status)))
return status;
if (status == CAIRO_INT_STATUS_UNSUPPORTED)
return UNSUPPORTED ("a clip surface would be required");
}
if (! _cairo_surface_check_span_renderer (op, src, &dst->base, antialias))
return UNSUPPORTED ("no span renderer");
if (op == CAIRO_OPERATOR_SOURCE)
return UNSUPPORTED ("SOURCE compositing doesn't work in GL");
if (op == CAIRO_OPERATOR_CLEAR) {
op = CAIRO_OPERATOR_DEST_OUT;
src = &_cairo_pattern_white.base;
}
status = _cairo_surface_composite_polygon (&dst->base,
op,
src,
fill_rule,
antialias,
extents,
polygon,
clip_region);
return status;
}
static cairo_int_status_t
_cairo_gl_surface_stroke (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
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,
cairo_clip_t *clip)
{
cairo_gl_surface_t *surface = abstract_surface;
cairo_composite_rectangles_t extents;
cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack;
int num_boxes = ARRAY_LENGTH (boxes_stack);
cairo_clip_t local_clip;
cairo_bool_t have_clip = FALSE;
cairo_polygon_t polygon;
cairo_status_t status;
status = _cairo_composite_rectangles_init_for_stroke (&extents,
surface->width,
surface->height,
op, source,
path, style, ctm,
clip);
if (unlikely (status))
return status;
if (_cairo_clip_contains_extents (clip, &extents))
clip = NULL;
if (clip != NULL) {
clip = _cairo_clip_init_copy (&local_clip, clip);
have_clip = TRUE;
}
status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes);
if (unlikely (status)) {
if (have_clip)
_cairo_clip_fini (&local_clip);
return status;
}
_cairo_polygon_init (&polygon);
_cairo_polygon_limit (&polygon, clip_boxes, num_boxes);
status = _cairo_path_fixed_stroke_to_polygon (path,
style,
ctm, ctm_inverse,
tolerance,
&polygon);
if (likely (status == CAIRO_STATUS_SUCCESS)) {
status = _cairo_gl_surface_polygon (surface, op, source, &polygon,
CAIRO_FILL_RULE_WINDING, antialias,
&extents, clip);
}
_cairo_polygon_fini (&polygon);
if (have_clip)
_cairo_clip_fini (&local_clip);
return status;
}
static cairo_int_status_t
_cairo_gl_surface_fill (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias,
cairo_clip_t *clip)
{
cairo_gl_surface_t *surface = abstract_surface;
cairo_composite_rectangles_t extents;
cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack;
cairo_clip_t local_clip;
cairo_bool_t have_clip = FALSE;
int num_boxes = ARRAY_LENGTH (boxes_stack);
cairo_polygon_t polygon;
cairo_status_t status;
status = _cairo_composite_rectangles_init_for_fill (&extents,
surface->width,
surface->height,
op, source, path,
clip);
if (unlikely (status))
return status;
if (_cairo_clip_contains_extents (clip, &extents))
clip = NULL;
#if 0
if (extents.is_bounded && clip != NULL) {
cairo_clip_path_t *clip_path;
if (((clip_path = _clip_get_single_path (clip)) != NULL) &&
_cairo_path_fixed_equal (&clip_path->path, path))
{
clip = NULL;
}
}
#endif
if (clip != NULL) {
clip = _cairo_clip_init_copy (&local_clip, clip);
have_clip = TRUE;
}
status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes);
if (unlikely (status)) {
if (have_clip)
_cairo_clip_fini (&local_clip);
return status;
}
_cairo_polygon_init (&polygon);
_cairo_polygon_limit (&polygon, clip_boxes, num_boxes);
status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon);
if (likely (status == CAIRO_STATUS_SUCCESS)) {
status = _cairo_gl_surface_polygon (surface, op, source, &polygon,
fill_rule, antialias,
&extents, clip);
}
_cairo_polygon_fini (&polygon);
if (clip_boxes != boxes_stack)
free (clip_boxes);
if (have_clip)
_cairo_clip_fini (&local_clip);
return status;
}
const cairo_surface_backend_t _cairo_gl_surface_backend = {
CAIRO_SURFACE_TYPE_GL,
_cairo_gl_surface_create_similar,
_cairo_gl_surface_finish,
_cairo_gl_surface_acquire_source_image,
_cairo_gl_surface_release_source_image,
_cairo_gl_surface_acquire_dest_image,
_cairo_gl_surface_release_dest_image,
_cairo_gl_surface_clone_similar,
_cairo_gl_surface_composite,
_cairo_gl_surface_fill_rectangles,
_cairo_gl_surface_composite_trapezoids,
_cairo_gl_surface_create_span_renderer,
_cairo_gl_surface_check_span_renderer,
NULL,
NULL,
_cairo_gl_surface_get_extents,
NULL,
_cairo_gl_surface_get_font_options,
_cairo_gl_surface_flush,
NULL,
_cairo_gl_surface_scaled_font_fini,
_cairo_gl_surface_scaled_glyph_fini,
_cairo_gl_surface_paint,
NULL,
_cairo_gl_surface_stroke,
_cairo_gl_surface_fill,
_cairo_gl_surface_show_glyphs,
NULL
};