diff options
author | Benjamin Otte <otte@redhat.com> | 2010-06-03 17:50:59 +0200 |
---|---|---|
committer | Benjamin Otte <otte@redhat.com> | 2010-06-07 13:37:49 +0200 |
commit | 35e219d08f1c202399655db183ca0105a1c2166f (patch) | |
tree | bc4afb9fa609052fc76458858f572a4678497d4c | |
parent | 9c17a477d29f633a4850ef8b458cbca82a19afc4 (diff) |
gl: Make gradient textures a separate object
This is necessary so we can do proper refcounting and don't delete the
gradient texture prematurely.
-rw-r--r-- | src/Makefile.sources | 4 | ||||
-rw-r--r-- | src/cairo-gl-composite.c | 156 | ||||
-rw-r--r-- | src/cairo-gl-gradient-private.h | 80 | ||||
-rw-r--r-- | src/cairo-gl-gradient.c | 223 | ||||
-rw-r--r-- | src/cairo-gl-private.h | 9 |
5 files changed, 327 insertions, 145 deletions
diff --git a/src/Makefile.sources b/src/Makefile.sources index 24f15c54..957e1088 100644 --- a/src/Makefile.sources +++ b/src/Makefile.sources @@ -312,10 +312,12 @@ cairo_beos_headers = cairo-beos.h #cairo_beos_sources = cairo-beos-surface.cpp cairo_gl_headers = cairo-gl.h -cairo_gl_private = cairo-gl-private.h +cairo_gl_private = cairo-gl-private.h \ + cairo-gl-gradient-private.h cairo_gl_sources = cairo-gl-composite.c \ cairo-gl-device.c \ cairo-gl-glyphs.c \ + cairo-gl-gradient.c \ cairo-gl-shaders.c \ cairo-gl-surface.c diff --git a/src/cairo-gl-composite.c b/src/cairo-gl-composite.c index 4b8fcdc3..1ca65092 100644 --- a/src/cairo-gl-composite.c +++ b/src/cairo-gl-composite.c @@ -43,153 +43,22 @@ #include "cairo-error-private.h" #include "cairo-gl-private.h" -static int -_cairo_gl_gradient_sample_width (const cairo_gradient_pattern_t *gradient) -{ - unsigned int n; - int width; - - width = 8; - for (n = 1; n < gradient->n_stops; n++) { - double dx = gradient->stops[n].offset - gradient->stops[n-1].offset; - double delta, max; - int ramp; - - if (dx == 0) - continue; - - max = gradient->stops[n].color.red - - gradient->stops[n-1].color.red; - - delta = gradient->stops[n].color.green - - gradient->stops[n-1].color.green; - if (delta > max) - max = delta; - - delta = gradient->stops[n].color.blue - - gradient->stops[n-1].color.blue; - if (delta > max) - max = delta; - - delta = gradient->stops[n].color.alpha - - gradient->stops[n-1].color.alpha; - if (delta > max) - max = delta; - - ramp = 128 * max / dx; - if (ramp > width) - width = ramp; - } - - width = (width + 7) & -8; - return MIN (width, 1024); -} - -static cairo_status_t -_render_gradient (const cairo_gl_context_t *ctx, - const cairo_gradient_pattern_t *pattern, - void *bytes, - int width) -{ - pixman_image_t *gradient, *image; - pixman_gradient_stop_t pixman_stops_stack[32]; - pixman_gradient_stop_t *pixman_stops; - pixman_point_fixed_t p1, p2; - unsigned int i; - - pixman_stops = pixman_stops_stack; - if (unlikely (pattern->n_stops > ARRAY_LENGTH (pixman_stops_stack))) { - pixman_stops = _cairo_malloc_ab (pattern->n_stops, - sizeof (pixman_gradient_stop_t)); - if (unlikely (pixman_stops == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - for (i = 0; i < pattern->n_stops; i++) { - pixman_stops[i].x = _cairo_fixed_16_16_from_double (pattern->stops[i].offset); - pixman_stops[i].color.red = pattern->stops[i].color.red_short; - pixman_stops[i].color.green = pattern->stops[i].color.green_short; - pixman_stops[i].color.blue = pattern->stops[i].color.blue_short; - pixman_stops[i].color.alpha = pattern->stops[i].color.alpha_short; - } - - p1.x = 0; - p1.y = 0; - p2.x = width << 16; - p2.y = 0; - - gradient = pixman_image_create_linear_gradient (&p1, &p2, - pixman_stops, - pattern->n_stops); - if (pixman_stops != pixman_stops_stack) - free (pixman_stops); - - if (unlikely (gradient == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - pixman_image_set_filter (gradient, PIXMAN_FILTER_BILINEAR, NULL, 0); - pixman_image_set_repeat (gradient, PIXMAN_REPEAT_PAD); - - image = pixman_image_create_bits (PIXMAN_a8r8g8b8, width, 1, - bytes, sizeof(uint32_t)*width); - if (unlikely (image == NULL)) { - pixman_image_unref (gradient); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - pixman_image_composite32 (PIXMAN_OP_SRC, - gradient, NULL, image, - 0, 0, - 0, 0, - 0, 0, - width, 1); - - pixman_image_unref (gradient); - pixman_image_unref (image); - return CAIRO_STATUS_SUCCESS; -} - static cairo_int_status_t _cairo_gl_create_gradient_texture (cairo_gl_surface_t *dst, const cairo_gradient_pattern_t *pattern, - GLuint *tex) + cairo_gl_gradient_t **gradient) { cairo_gl_context_t *ctx; cairo_status_t status; - int tex_width; - GLubyte *data; - - assert (pattern->n_stops != 0); status = _cairo_gl_context_acquire (dst->base.device, &ctx); if (unlikely (status)) return status; - if ((unsigned int) ctx->max_texture_size / 2 <= pattern->n_stops) { - _cairo_gl_context_release (ctx); - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - tex_width = _cairo_gl_gradient_sample_width (pattern); - - glBindBufferARB (GL_PIXEL_UNPACK_BUFFER_ARB, ctx->texture_load_pbo); - glBufferDataARB (GL_PIXEL_UNPACK_BUFFER_ARB, tex_width * sizeof (uint32_t), 0, GL_STREAM_DRAW); - data = glMapBufferARB (GL_PIXEL_UNPACK_BUFFER_ARB, GL_WRITE_ONLY); - - _render_gradient (ctx, pattern, data, tex_width); - - glUnmapBufferARB (GL_PIXEL_UNPACK_BUFFER_ARB); - - glGenTextures (1, tex); - _cairo_gl_context_activate (ctx, CAIRO_GL_TEX_TEMP); - glBindTexture (GL_TEXTURE_1D, *tex); - glTexImage1D (GL_TEXTURE_1D, 0, GL_RGBA8, tex_width, 0, - GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, 0); - - glBindBufferARB (GL_PIXEL_UNPACK_BUFFER_ARB, 0); + status = _cairo_gl_gradient_create (ctx, pattern->n_stops, pattern->stops, gradient); _cairo_gl_context_release (ctx); - return CAIRO_STATUS_SUCCESS; + return status; } /** @@ -301,7 +170,7 @@ _cairo_gl_gradient_operand_init (cairo_gl_operand_t *operand, status = _cairo_gl_create_gradient_texture (dst, gradient, - &operand->linear.tex); + &operand->linear.gradient); if (unlikely (status)) return status; @@ -335,7 +204,7 @@ _cairo_gl_gradient_operand_init (cairo_gl_operand_t *operand, status = _cairo_gl_create_gradient_texture (dst, gradient, - &operand->radial.tex); + &operand->radial.gradient); if (unlikely (status)) return status; @@ -370,10 +239,10 @@ _cairo_gl_operand_destroy (cairo_gl_operand_t *operand) case CAIRO_GL_OPERAND_CONSTANT: break; case CAIRO_GL_OPERAND_LINEAR_GRADIENT: - glDeleteTextures (1, &operand->linear.tex); + _cairo_gl_gradient_destroy (operand->linear.gradient); break; case CAIRO_GL_OPERAND_RADIAL_GRADIENT: - glDeleteTextures (1, &operand->radial.tex); + _cairo_gl_gradient_destroy (operand->radial.gradient); break; case CAIRO_GL_OPERAND_TEXTURE: _cairo_pattern_release_surface (NULL, /* XXX */ @@ -763,15 +632,17 @@ _cairo_gl_context_setup_operand (cairo_gl_context_t *ctx, glEnableClientState (GL_TEXTURE_COORD_ARRAY); break; case CAIRO_GL_OPERAND_LINEAR_GRADIENT: + _cairo_gl_gradient_reference (operand->linear.gradient); glActiveTexture (GL_TEXTURE0 + tex_unit); - glBindTexture (GL_TEXTURE_1D, operand->linear.tex); + glBindTexture (GL_TEXTURE_1D, operand->linear.gradient->tex); _cairo_gl_texture_set_extend (ctx, GL_TEXTURE_1D, operand->linear.extend); _cairo_gl_texture_set_filter (ctx, GL_TEXTURE_1D, CAIRO_FILTER_BILINEAR); glEnable (GL_TEXTURE_1D); break; case CAIRO_GL_OPERAND_RADIAL_GRADIENT: + _cairo_gl_gradient_reference (operand->radial.gradient); glActiveTexture (GL_TEXTURE0 + tex_unit); - glBindTexture (GL_TEXTURE_1D, operand->radial.tex); + glBindTexture (GL_TEXTURE_1D, operand->radial.gradient->tex); _cairo_gl_texture_set_extend (ctx, GL_TEXTURE_1D, operand->radial.extend); _cairo_gl_texture_set_filter (ctx, GL_TEXTURE_1D, CAIRO_FILTER_BILINEAR); glEnable (GL_TEXTURE_1D); @@ -810,7 +681,12 @@ _cairo_gl_context_destroy_operand (cairo_gl_context_t *ctx, glDisableClientState (GL_TEXTURE_COORD_ARRAY); break; case CAIRO_GL_OPERAND_LINEAR_GRADIENT: + _cairo_gl_gradient_destroy (ctx->operands[tex_unit].linear.gradient); + glActiveTexture (GL_TEXTURE0 + tex_unit); + glDisable (GL_TEXTURE_1D); + break; case CAIRO_GL_OPERAND_RADIAL_GRADIENT: + _cairo_gl_gradient_destroy (ctx->operands[tex_unit].radial.gradient); glActiveTexture (GL_TEXTURE0 + tex_unit); glDisable (GL_TEXTURE_1D); break; diff --git a/src/cairo-gl-gradient-private.h b/src/cairo-gl-gradient-private.h new file mode 100644 index 00000000..3c3b52e2 --- /dev/null +++ b/src/cairo-gl-gradient-private.h @@ -0,0 +1,80 @@ +/* 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 <otte@gnome.org> + * Carl Worth <cworth@cworth.org> + * Chris Wilson <chris@chris-wilson.co.uk> + * Eric Anholt <eric@anholt.net> + */ + +#ifndef CAIRO_GL_GRADIENT_PRIVATE_H +#define CAIRO_GL_GRADIENT_PRIVATE_H + +#include "cairo-device-private.h" +#include "cairo-reference-count-private.h" +#include "cairo-types-private.h" + +#include <GL/glew.h> + +#include "cairo-gl.h" + +#include <GL/gl.h> +#define GL_GLEXT_PROTOTYPES +#include <GL/glext.h> + +/* XXX: Declare in a better place */ +typedef struct _cairo_gl_context cairo_gl_context_t; + +typedef struct _cairo_gl_gradient { + cairo_reference_count_t ref_count; + cairo_device_t *device; /* NB: we don't hold a reference */ + GLuint tex; + unsigned int n_stops; + cairo_gradient_stop_t stops[2]; +} cairo_gl_gradient_t; + +cairo_private cairo_int_status_t +_cairo_gl_gradient_create (cairo_gl_context_t *ctx, + unsigned int n_stops, + const cairo_gradient_stop_t *stops, + cairo_gl_gradient_t **gradient_out); + +cairo_private_no_warn cairo_gl_gradient_t * +_cairo_gl_gradient_reference (cairo_gl_gradient_t *gradient); + +cairo_private void +_cairo_gl_gradient_destroy (cairo_gl_gradient_t *gradient); + + +#endif /* CAIRO_GL_GRADIENT_PRIVATE_H */ diff --git a/src/cairo-gl-gradient.c b/src/cairo-gl-gradient.c new file mode 100644 index 00000000..78d74f14 --- /dev/null +++ b/src/cairo-gl-gradient.c @@ -0,0 +1,223 @@ +/* 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 <otte@gnome.org> + * Carl Worth <cworth@cworth.org> + * Chris Wilson <chris@chris-wilson.co.uk> + * Eric Anholt <eric@anholt.net> + */ + +#include "cairo-gl-gradient-private.h" + +#include <string.h> + +#include "cairo-error-private.h" +#include "cairo-gl-private.h" + +static int +_cairo_gl_gradient_sample_width (unsigned int n_stops, + const cairo_gradient_stop_t *stops) +{ + unsigned int n; + int width; + + width = 8; + for (n = 1; n < n_stops; n++) { + double dx = stops[n].offset - stops[n-1].offset; + double delta, max; + int ramp; + + if (dx == 0) + continue; + + max = stops[n].color.red - stops[n-1].color.red; + + delta = stops[n].color.green - stops[n-1].color.green; + if (delta > max) + max = delta; + + delta = stops[n].color.blue - stops[n-1].color.blue; + if (delta > max) + max = delta; + + delta = stops[n].color.alpha - stops[n-1].color.alpha; + if (delta > max) + max = delta; + + ramp = 128 * max / dx; + if (ramp > width) + width = ramp; + } + + width = (width + 7) & -8; + return MIN (width, 1024); +} + +static cairo_status_t +_cairo_gl_gradient_render (const cairo_gl_context_t *ctx, + unsigned int n_stops, + const cairo_gradient_stop_t *stops, + void *bytes, + int width) +{ + pixman_image_t *gradient, *image; + pixman_gradient_stop_t pixman_stops_stack[32]; + pixman_gradient_stop_t *pixman_stops; + pixman_point_fixed_t p1, p2; + unsigned int i; + + pixman_stops = pixman_stops_stack; + if (unlikely (n_stops > ARRAY_LENGTH (pixman_stops_stack))) { + pixman_stops = _cairo_malloc_ab (n_stops, + sizeof (pixman_gradient_stop_t)); + if (unlikely (pixman_stops == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + for (i = 0; i < n_stops; i++) { + pixman_stops[i].x = _cairo_fixed_16_16_from_double (stops[i].offset); + pixman_stops[i].color.red = stops[i].color.red_short; + pixman_stops[i].color.green = stops[i].color.green_short; + pixman_stops[i].color.blue = stops[i].color.blue_short; + pixman_stops[i].color.alpha = stops[i].color.alpha_short; + } + + p1.x = 0; + p1.y = 0; + p2.x = width << 16; + p2.y = 0; + + gradient = pixman_image_create_linear_gradient (&p1, &p2, + pixman_stops, + n_stops); + if (pixman_stops != pixman_stops_stack) + free (pixman_stops); + + if (unlikely (gradient == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + pixman_image_set_filter (gradient, PIXMAN_FILTER_BILINEAR, NULL, 0); + pixman_image_set_repeat (gradient, PIXMAN_REPEAT_PAD); + + image = pixman_image_create_bits (PIXMAN_a8r8g8b8, width, 1, + bytes, sizeof(uint32_t)*width); + if (unlikely (image == NULL)) { + pixman_image_unref (gradient); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + pixman_image_composite32 (PIXMAN_OP_SRC, + gradient, NULL, image, + 0, 0, + 0, 0, + 0, 0, + width, 1); + + pixman_image_unref (gradient); + pixman_image_unref (image); + return CAIRO_STATUS_SUCCESS; +} + +cairo_int_status_t +_cairo_gl_gradient_create (cairo_gl_context_t *ctx, + unsigned int n_stops, + const cairo_gradient_stop_t *stops, + cairo_gl_gradient_t **gradient_out) +{ + cairo_gl_gradient_t *gradient; + int tex_width; + void *data; + + assert (n_stops >= 2); + if ((unsigned int) ctx->max_texture_size / 2 <= n_stops) + return CAIRO_INT_STATUS_UNSUPPORTED; + + gradient = malloc (sizeof (cairo_gl_gradient_t) + sizeof (cairo_gradient_stop_t) * (n_stops - 2)); + if (gradient == NULL) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + CAIRO_REFERENCE_COUNT_INIT (&gradient->ref_count, 1); + gradient->device = &ctx->base; + gradient->n_stops = n_stops; + memcpy (gradient->stops, stops, n_stops * sizeof (cairo_gradient_stop_t)); + + tex_width = _cairo_gl_gradient_sample_width (n_stops, stops); + + glBindBufferARB (GL_PIXEL_UNPACK_BUFFER_ARB, ctx->texture_load_pbo); + glBufferDataARB (GL_PIXEL_UNPACK_BUFFER_ARB, tex_width * sizeof (uint32_t), 0, GL_STREAM_DRAW); + data = glMapBufferARB (GL_PIXEL_UNPACK_BUFFER_ARB, GL_WRITE_ONLY); + + _cairo_gl_gradient_render (ctx, n_stops, stops, data, tex_width); + + glUnmapBufferARB (GL_PIXEL_UNPACK_BUFFER_ARB); + + glGenTextures (1, &gradient->tex); + _cairo_gl_context_activate (ctx, CAIRO_GL_TEX_TEMP); + glBindTexture (GL_TEXTURE_1D, gradient->tex); + glTexImage1D (GL_TEXTURE_1D, 0, GL_RGBA8, tex_width, 0, + GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, 0); + + glBindBufferARB (GL_PIXEL_UNPACK_BUFFER_ARB, 0); + + *gradient_out = gradient; + return CAIRO_STATUS_SUCCESS; +} + +cairo_gl_gradient_t * +_cairo_gl_gradient_reference (cairo_gl_gradient_t *gradient) +{ + assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&gradient->ref_count)); + + _cairo_reference_count_inc (&gradient->ref_count); + + return gradient; +} + +void +_cairo_gl_gradient_destroy (cairo_gl_gradient_t *gradient) +{ + cairo_gl_context_t *ctx; + + assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&gradient->ref_count)); + + if (! _cairo_reference_count_dec_and_test (&gradient->ref_count)) + return; + + if (_cairo_gl_context_acquire (gradient->device, &ctx) == CAIRO_STATUS_SUCCESS) { + glDeleteTextures (1, &gradient->tex); + _cairo_gl_context_release (ctx); + } + + free (gradient); +} diff --git a/src/cairo-gl-private.h b/src/cairo-gl-private.h index 0ec85b3c..689c7696 100644 --- a/src/cairo-gl-private.h +++ b/src/cairo-gl-private.h @@ -43,6 +43,7 @@ #define CAIRO_GL_PRIVATE_H #include "cairoint.h" +#include "cairo-gl-gradient-private.h" #include "cairo-device-private.h" #include "cairo-rtree-private.h" @@ -144,14 +145,14 @@ typedef struct cairo_gl_operand { GLfloat color[4]; } constant; struct { - GLuint tex; + cairo_gl_gradient_t *gradient; cairo_matrix_t m; float segment_x; float segment_y; cairo_extend_t extend; } linear; struct { - GLuint tex; + cairo_gl_gradient_t *gradient; cairo_matrix_t m; float circle_1_x; float circle_1_y; @@ -163,7 +164,7 @@ typedef struct cairo_gl_operand { unsigned int vertex_offset; } cairo_gl_operand_t; -typedef struct _cairo_gl_context { +struct _cairo_gl_context { cairo_device_t base; GLuint dummy_tex; @@ -201,7 +202,7 @@ typedef struct _cairo_gl_context { void (*make_current) (void *ctx, cairo_gl_surface_t *surface); void (*swap_buffers)(void *ctx, cairo_gl_surface_t *surface); void (*destroy) (void *ctx); -} cairo_gl_context_t; +}; typedef struct _cairo_gl_composite { cairo_gl_surface_t *dst; |