diff --git a/src/Makefile.sources b/src/Makefile.sources index 1e04ed2ea..71d0abd08 100644 --- a/src/Makefile.sources +++ b/src/Makefile.sources @@ -279,7 +279,7 @@ cairo_beos_headers = cairo-beos.h cairo_gl_headers = cairo-gl.h cairo_gl_private = cairo-gl-private.h -cairo_gl_sources = cairo-gl-surface.c cairo-gl-glyphs.c +cairo_gl_sources = cairo-gl-surface.c cairo-gl-glyphs.c cairo-gl-shaders.c cairo_glx_sources += cairo-glx-context.c cairo_eagle_sources += cairo-eagle-context.c diff --git a/src/cairo-gl-private.h b/src/cairo-gl-private.h index 79145cf3e..5a32dba7a 100644 --- a/src/cairo-gl-private.h +++ b/src/cairo-gl-private.h @@ -85,6 +85,8 @@ struct _cairo_gl_context { cairo_mutex_t mutex; /* needed? */ GLuint dummy_tex; + GLint fill_rectangles_shader; + GLint fill_rectangles_color_uniform; GLint max_framebuffer_size; GLint max_texture_size; @@ -202,6 +204,10 @@ _cairo_gl_surface_show_glyphs (void *abstract_dst, cairo_private void _cairo_gl_glyph_cache_fini (cairo_gl_glyph_cache_t *cache); +cairo_private cairo_status_t +_cairo_gl_load_glsl (GLint *shader, + const char *vs_source, const char *fs_source); + static inline int _cairo_gl_y_flip (cairo_gl_surface_t *surface, int y) { diff --git a/src/cairo-gl-shaders.c b/src/cairo-gl-shaders.c new file mode 100644 index 000000000..f06991eef --- /dev/null +++ b/src/cairo-gl-shaders.c @@ -0,0 +1,117 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2010 Eric Anholt + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Eric Anholt. + */ + +#include "cairoint.h" +#include "cairo-gl-private.h" + +static GLint +_cairo_gl_compile_glsl(GLenum type, GLint *shader_out, const char *source) +{ + GLint ok; + GLint shader; + + shader = glCreateShaderObjectARB (type); + glShaderSourceARB (shader, 1, (const GLchar **)&source, NULL); + glCompileShaderARB (shader); + glGetObjectParameterivARB (shader, GL_OBJECT_COMPILE_STATUS_ARB, &ok); + if (!ok) { + GLchar *info; + GLint size; + + glGetObjectParameterivARB (shader, GL_OBJECT_INFO_LOG_LENGTH_ARB, + &size); + info = malloc (size); + + if (info) + glGetInfoLogARB (shader, size, NULL, info); + fprintf (stderr, "Failed to compile %s: %s\n", + type == GL_FRAGMENT_SHADER ? "FS" : "VS", + info); + fprintf (stderr, "Shader source:\n%s", source); + fprintf (stderr, "GLSL compile failure\n"); + + glDeleteObjectARB (shader); + + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + *shader_out = shader; + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_gl_load_glsl (GLint *shader_out, + const char *vs_source, const char *fs_source) +{ + GLint ok; + GLint shader, vs, fs; + cairo_status_t status; + + shader = glCreateProgramObjectARB (); + + status = _cairo_gl_compile_glsl (GL_VERTEX_SHADER_ARB, &vs, vs_source); + if (_cairo_status_is_error (status)) + goto fail; + status = _cairo_gl_compile_glsl (GL_FRAGMENT_SHADER_ARB, &fs, fs_source); + if (_cairo_status_is_error (status)) + goto fail; + + glAttachObjectARB (shader, vs); + glAttachObjectARB (shader, fs); + glLinkProgram (shader); + glGetObjectParameterivARB (shader, GL_OBJECT_LINK_STATUS_ARB, &ok); + if (!ok) { + GLchar *info; + GLint size; + + glGetObjectParameterivARB (shader, GL_OBJECT_INFO_LOG_LENGTH_ARB, + &size); + info = malloc (size); + + if (info) + glGetInfoLogARB (shader, size, NULL, info); + fprintf (stderr, "Failed to link: %s\n", info); + free (info); + status = CAIRO_INT_STATUS_UNSUPPORTED; + + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + *shader_out = shader; + + return CAIRO_STATUS_SUCCESS; + +fail: + glDeleteObjectARB (shader); + return status; +} diff --git a/src/cairo-gl-surface.c b/src/cairo-gl-surface.c index af658cbc4..9170f0653 100644 --- a/src/cairo-gl-surface.c +++ b/src/cairo-gl-surface.c @@ -1549,11 +1549,11 @@ _cairo_gl_surface_composite_trapezoids (cairo_operator_t op, } static cairo_int_status_t -_cairo_gl_surface_fill_rectangles (void *abstract_surface, - cairo_operator_t op, - const cairo_color_t *color, - cairo_rectangle_int_t *rects, - int num_rects) +_cairo_gl_surface_fill_rectangles_fixed (void *abstract_surface, + cairo_operator_t op, + const cairo_color_t *color, + cairo_rectangle_int_t *rects, + int num_rects) { #define N_STACK_RECTS 4 cairo_gl_surface_t *surface = abstract_surface; @@ -1632,6 +1632,121 @@ _cairo_gl_surface_fill_rectangles (void *abstract_surface, #undef N_STACK_RECTS } +static cairo_int_status_t +_cairo_gl_surface_fill_rectangles_glsl (void *abstract_surface, + cairo_operator_t op, + const cairo_color_t *color, + cairo_rectangle_int_t *rects, + int num_rects) +{ +#define N_STACK_RECTS 4 + cairo_gl_surface_t *surface = abstract_surface; + GLfloat vertices_stack[N_STACK_RECTS*4*2]; + GLfloat gl_color[4]; + cairo_gl_context_t *ctx; + int i; + GLfloat *vertices; + static const char *fill_vs_source = + "void main()\n" + "{\n" + " gl_Position = ftransform();\n" + "}\n"; + static const char *fill_fs_source = + "uniform vec4 color;\n" + "void main()\n" + "{\n" + " gl_FragColor = color;\n" + "}\n"; + cairo_status_t status; + + if (! _cairo_gl_operator_is_supported (op)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + ctx = _cairo_gl_context_acquire (surface->ctx); + + if (ctx->fill_rectangles_shader == 0) { + status = _cairo_gl_load_glsl (&ctx->fill_rectangles_shader, + fill_vs_source, fill_fs_source); + if (_cairo_status_is_error (status)) + return status; + + ctx->fill_rectangles_color_uniform = + glGetUniformLocationARB (ctx->fill_rectangles_shader, "color"); + } + + if (num_rects > N_STACK_RECTS) { + vertices = _cairo_malloc_ab (num_rects, sizeof (GLfloat) * 4 * 2); + if (!vertices) { + _cairo_gl_context_release (ctx); + free (vertices); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + } else { + vertices = vertices_stack; + } + + glUseProgramObjectARB (ctx->fill_rectangles_shader); + + _cairo_gl_set_destination (surface); + _cairo_gl_set_operator (surface, op); + + gl_color[0] = color->red * color->alpha; + gl_color[1] = color->green * color->alpha; + gl_color[2] = color->blue * color->alpha; + gl_color[3] = color->alpha; + glUniform4fvARB (ctx->fill_rectangles_color_uniform, 1, gl_color); + + for (i = 0; i < num_rects; i++) { + vertices[i * 8 + 0] = rects[i].x; + vertices[i * 8 + 1] = rects[i].y; + vertices[i * 8 + 2] = rects[i].x + rects[i].width; + vertices[i * 8 + 3] = rects[i].y; + vertices[i * 8 + 4] = rects[i].x + rects[i].width; + vertices[i * 8 + 5] = rects[i].y + rects[i].height; + vertices[i * 8 + 6] = rects[i].x; + vertices[i * 8 + 7] = rects[i].y + rects[i].height; + } + + glVertexPointer (2, GL_FLOAT, sizeof (GLfloat)*2, vertices); + glEnableClientState (GL_VERTEX_ARRAY); + + glDrawArrays (GL_QUADS, 0, 4 * num_rects); + + glDisableClientState (GL_VERTEX_ARRAY); + glDisable (GL_BLEND); + glUseProgramObjectARB (0); + + _cairo_gl_context_release (ctx); + if (vertices != vertices_stack) + free (vertices); + + return CAIRO_STATUS_SUCCESS; +#undef N_STACK_RECTS +} + + +static cairo_int_status_t +_cairo_gl_surface_fill_rectangles (void *abstract_surface, + cairo_operator_t op, + const cairo_color_t *color, + cairo_rectangle_int_t *rects, + int num_rects) +{ + if (GLEW_ARB_fragment_shader && GLEW_ARB_vertex_shader) { + return _cairo_gl_surface_fill_rectangles_glsl(abstract_surface, + op, + color, + rects, + num_rects); + } else { + return _cairo_gl_surface_fill_rectangles_fixed(abstract_surface, + op, + color, + rects, + num_rects); + } +} + typedef struct _cairo_gl_surface_span_renderer { cairo_span_renderer_t base;