1 /* cairo - a vector graphics library with display and print output
2 *
3 * Copyright © 2009 Eric Anholt
4 * Copyright © 2009 Chris Wilson
5 * Copyright © 2005,2010 Red Hat, Inc
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it either under the terms of the GNU Lesser General Public
9 * License version 2.1 as published by the Free Software Foundation
10 * (the "LGPL") or, at your option, under the terms of the Mozilla
11 * Public License Version 1.1 (the "MPL"). If you do not alter this
12 * notice, a recipient may use your version of this file under either
13 * the MPL or the LGPL.
14 *
15 * You should have received a copy of the LGPL along with this library
16 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
18 * You should have received a copy of the MPL along with this library
19 * in the file COPYING-MPL-1.1
20 *
21 * The contents of this file are subject to the Mozilla Public License
22 * Version 1.1 (the "License"); you may not use this file except in
23 * compliance with the License. You may obtain a copy of the License at
24 * http://www.mozilla.org/MPL/
25 *
26 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
27 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
28 * the specific language governing rights and limitations.
29 *
30 * The Original Code is the cairo graphics library.
31 *
32 * The Initial Developer of the Original Code is Red Hat, Inc.
33 *
34 * Contributor(s):
35 * Benjamin Otte <otte@gnome.org>
36 * Carl Worth <cworth@cworth.org>
37 * Chris Wilson <chris@chris-wilson.co.uk>
38 * Eric Anholt <eric@anholt.net>
39 */
40
41 #include "cairoint.h"
42
43 #include "cairo-error-private.h"
44 #include "cairo-gl-private.h"
45
46 static void
_gl_lock(void * device)47 _gl_lock (void *device)
48 {
49 cairo_gl_context_t *ctx = (cairo_gl_context_t *) device;
50
51 ctx->acquire (ctx);
52 }
53
54 static void
_gl_unlock(void * device)55 _gl_unlock (void *device)
56 {
57 cairo_gl_context_t *ctx = (cairo_gl_context_t *) device;
58
59 ctx->release (ctx);
60 }
61
62 static cairo_status_t
_gl_flush(void * device)63 _gl_flush (void *device)
64 {
65 cairo_gl_context_t *ctx;
66 cairo_status_t status;
67
68 status = _cairo_gl_context_acquire (device, &ctx);
69 if (unlikely (status))
70 return status;
71
72 _cairo_gl_composite_flush (ctx);
73
74 _cairo_gl_context_destroy_operand (ctx, CAIRO_GL_TEX_SOURCE);
75 _cairo_gl_context_destroy_operand (ctx, CAIRO_GL_TEX_MASK);
76
77 if (ctx->clip_region) {
78 cairo_region_destroy (ctx->clip_region);
79 ctx->clip_region = NULL;
80 }
81
82 ctx->current_target = NULL;
83 ctx->current_operator = -1;
84 ctx->vertex_size = 0;
85 ctx->pre_shader = NULL;
86 _cairo_gl_set_shader (ctx, NULL);
87
88 glBindBufferARB (GL_ARRAY_BUFFER_ARB, 0);
89
90 glDisableClientState (GL_VERTEX_ARRAY);
91
92 glDisable (GL_SCISSOR_TEST);
93 glDisable (GL_BLEND);
94
95 return _cairo_gl_context_release (ctx, status);
96 }
97
98 static void
_gl_finish(void * device)99 _gl_finish (void *device)
100 {
101 cairo_gl_context_t *ctx = device;
102
103 _gl_lock (device);
104
105 _cairo_cache_fini (&ctx->gradients);
106
107 _cairo_gl_context_fini_shaders (ctx);
108
109 _gl_unlock (device);
110 }
111
112 static void
_gl_destroy(void * device)113 _gl_destroy (void *device)
114 {
115 cairo_gl_context_t *ctx = device;
116 cairo_scaled_font_t *scaled_font, *next_scaled_font;
117 int n;
118
119 ctx->acquire (ctx);
120
121 cairo_list_foreach_entry_safe (scaled_font,
122 next_scaled_font,
123 cairo_scaled_font_t,
124 &ctx->fonts,
125 link)
126 {
127 _cairo_scaled_font_revoke_ownership (scaled_font);
128 }
129
130 for (n = 0; n < ARRAY_LENGTH (ctx->glyph_cache); n++)
131 _cairo_gl_glyph_cache_fini (ctx, &ctx->glyph_cache[n]);
132
133 cairo_region_destroy (ctx->clip_region);
134
135 ctx->destroy (ctx);
136
137 free (ctx);
138 }
139
140 static const cairo_device_backend_t _cairo_gl_device_backend = {
141 CAIRO_DEVICE_TYPE_GL,
142
143 _gl_lock,
144 _gl_unlock,
145
146 _gl_flush, /* flush */
147 _gl_finish,
148 _gl_destroy,
149 };
150
151 cairo_status_t
_cairo_gl_context_init(cairo_gl_context_t * ctx)152 _cairo_gl_context_init (cairo_gl_context_t *ctx)
153 {
154 cairo_status_t status;
155 int n;
156
157 _cairo_device_init (&ctx->base, &_cairo_gl_device_backend);
158
159 memset (ctx->glyph_cache, 0, sizeof (ctx->glyph_cache));
160 cairo_list_init (&ctx->fonts);
161
162 if (glewInit () != GLEW_OK)
163 return _cairo_error (CAIRO_STATUS_INVALID_FORMAT); /* XXX */
164
165 if (! GLEW_EXT_framebuffer_object ||
166 ! GLEW_ARB_texture_env_combine ||
167 ! GLEW_EXT_bgra)
168 {
169 fprintf (stderr,
170 "Required GL extensions not available:\n");
171 if (! GLEW_EXT_framebuffer_object)
172 fprintf (stderr, " GL_EXT_framebuffer_object\n");
173 if (! GLEW_ARB_texture_env_combine)
174 fprintf (stderr, " GL_ARB_texture_env_combine\n");
175 if (! GLEW_ARB_vertex_buffer_object)
176 fprintf (stderr, " GL_ARB_vertex_buffer_object\n");
177
178 /* EXT_bgra is used in two places:
179 * - draw_image to upload common pixman formats without hand-swizzling.
180 * - get_image to download common pixman formats without hand-swizzling.
181 */
182 if (! GLEW_EXT_bgra)
183 fprintf (stderr, " GL_EXT_bgra\n");
184
185 return _cairo_error (CAIRO_STATUS_INVALID_FORMAT); /* XXX */
186 }
187
188 if (! GLEW_ARB_texture_non_power_of_two &&
189 ! GLEW_ARB_texture_rectangle ) {
190 fprintf (stderr,
191 "Required GL extensions not available:\n");
192 fprintf (stderr, " GL_ARB_texture_non_power_of_two, GL_ARB_texture_rectangle\n");
193 }
194
195 if (! GLEW_ARB_texture_non_power_of_two)
196 ctx->tex_target = GL_TEXTURE_RECTANGLE_EXT;
197 else
198 ctx->tex_target = GL_TEXTURE_2D;
199
200 ctx->current_operator = -1;
201
202 status = _cairo_gl_context_init_shaders (ctx);
203 if (unlikely (status))
204 return status;
205
206 status = _cairo_cache_init (&ctx->gradients,
207 _cairo_gl_gradient_equal,
208 NULL,
209 (cairo_destroy_func_t) _cairo_gl_gradient_destroy,
210 CAIRO_GL_GRADIENT_CACHE_SIZE);
211 if (unlikely (status))
212 return status;
213
214 /* Set up the dummy texture for tex_env_combine with constant color. */
215 glGenTextures (1, &ctx->dummy_tex);
216 glBindTexture (ctx->tex_target, ctx->dummy_tex);
217 glTexImage2D (ctx->tex_target, 0, GL_RGBA, 1, 1, 0,
218 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
219
220 /* PBO for any sort of texture upload */
221 glGenBuffersARB (1, &ctx->texture_load_pbo);
222 glGenBuffersARB (1, &ctx->vbo);
223
224 ctx->max_framebuffer_size = 0;
225 glGetIntegerv (GL_MAX_RENDERBUFFER_SIZE, &ctx->max_framebuffer_size);
226 ctx->max_texture_size = 0;
227 glGetIntegerv (GL_MAX_TEXTURE_SIZE, &ctx->max_texture_size);
228 ctx->max_textures = 0;
229 glGetIntegerv (GL_MAX_TEXTURE_UNITS, &ctx->max_textures);
230
231 for (n = 0; n < ARRAY_LENGTH (ctx->glyph_cache); n++)
232 _cairo_gl_glyph_cache_init (&ctx->glyph_cache[n]);
233
234 return CAIRO_STATUS_SUCCESS;
235 }
236
237 void
_cairo_gl_context_activate(cairo_gl_context_t * ctx,cairo_gl_tex_t tex_unit)238 _cairo_gl_context_activate (cairo_gl_context_t *ctx,
239 cairo_gl_tex_t tex_unit)
240 {
241 if (ctx->max_textures <= (GLint) tex_unit) {
242 if (tex_unit < 2) {
243 _cairo_gl_composite_flush (ctx);
244 _cairo_gl_context_destroy_operand (ctx, ctx->max_textures - 1);
245 }
246 glActiveTexture (ctx->max_textures - 1);
247 } else {
248 glActiveTexture (GL_TEXTURE0 + tex_unit);
249 }
250 }
251
252 static void
_cairo_gl_ensure_framebuffer(cairo_gl_context_t * ctx,cairo_gl_surface_t * surface)253 _cairo_gl_ensure_framebuffer (cairo_gl_context_t *ctx,
254 cairo_gl_surface_t *surface)
255 {
256 GLenum status;
257
258 if (likely (surface->fb))
259 return;
260
261 /* Create a framebuffer object wrapping the texture so that we can render
262 * to it.
263 */
264 glGenFramebuffersEXT (1, &surface->fb);
265 glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, surface->fb);
266 glFramebufferTexture2DEXT (GL_FRAMEBUFFER_EXT,
267 GL_COLOR_ATTACHMENT0_EXT,
268 ctx->tex_target,
269 surface->tex,
270 0);
271
272 status = glCheckFramebufferStatusEXT (GL_FRAMEBUFFER_EXT);
273 if (status != GL_FRAMEBUFFER_COMPLETE_EXT) {
274 const char *str;
275 switch (status) {
276 //case GL_FRAMEBUFFER_UNDEFINED_EXT: str= "undefined"; break;
277 case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: str= "incomplete attachment"; break;
278 case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT: str= "incomplete/missing attachment"; break;
279 case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT: str= "incomplete draw buffer"; break;
280 case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT: str= "incomplete read buffer"; break;
281 case GL_FRAMEBUFFER_UNSUPPORTED_EXT: str= "unsupported"; break;
282 case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT: str= "incomplete multiple"; break;
283 default: str = "unknown error"; break;
284 }
285
286 fprintf (stderr,
287 "destination is framebuffer incomplete: %s [%#x]\n",
288 str, status);
289 }
290 }
291
292 void
_cairo_gl_context_set_destination(cairo_gl_context_t * ctx,cairo_gl_surface_t * surface)293 _cairo_gl_context_set_destination (cairo_gl_context_t *ctx,
294 cairo_gl_surface_t *surface)
295 {
296 if (ctx->current_target == surface)
297 return;
298
299 _cairo_gl_composite_flush (ctx);
300
301 ctx->current_target = surface;
302
303 if (_cairo_gl_surface_is_texture (surface)) {
304 _cairo_gl_ensure_framebuffer (ctx, surface);
305 glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, surface->fb);
306 glDrawBuffer (GL_COLOR_ATTACHMENT0_EXT);
307 glReadBuffer (GL_COLOR_ATTACHMENT0_EXT);
308 } else {
309 ctx->make_current (ctx, surface);
310 glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0);
311 glDrawBuffer (GL_BACK_LEFT);
312 glReadBuffer (GL_BACK_LEFT);
313 }
314
315 glViewport (0, 0, surface->width, surface->height);
316
317 glMatrixMode (GL_PROJECTION);
318 glLoadIdentity ();
319 if (_cairo_gl_surface_is_texture (surface))
320 glOrtho (0, surface->width, 0, surface->height, -1.0, 1.0);
321 else
322 glOrtho (0, surface->width, surface->height, 0, -1.0, 1.0);
323
324 glMatrixMode (GL_MODELVIEW);
325 glLoadIdentity ();
326 }
327