Go to most recent revision | Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
4349 | Serge | 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 | * Copyright © 2011 Linaro Limited |
||
7 | * Copyright © 2011 Samsung Electronics |
||
8 | * |
||
9 | * This library is free software; you can redistribute it and/or |
||
10 | * modify it either under the terms of the GNU Lesser General Public |
||
11 | * License version 2.1 as published by the Free Software Foundation |
||
12 | * (the "LGPL") or, at your option, under the terms of the Mozilla |
||
13 | * Public License Version 1.1 (the "MPL"). If you do not alter this |
||
14 | * notice, a recipient may use your version of this file under either |
||
15 | * the MPL or the LGPL. |
||
16 | * |
||
17 | * You should have received a copy of the LGPL along with this library |
||
18 | * in the file COPYING-LGPL-2.1; if not, write to the Free Software |
||
19 | * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA |
||
20 | * You should have received a copy of the MPL along with this library |
||
21 | * in the file COPYING-MPL-1.1 |
||
22 | * |
||
23 | * The contents of this file are subject to the Mozilla Public License |
||
24 | * Version 1.1 (the "License"); you may not use this file except in |
||
25 | * compliance with the License. You may obtain a copy of the License at |
||
26 | * http://www.mozilla.org/MPL/ |
||
27 | * |
||
28 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY |
||
29 | * OF ANY KIND, either express or implied. See the LGPL or the MPL for |
||
30 | * the specific language governing rights and limitations. |
||
31 | * |
||
32 | * The Original Code is the cairo graphics library. |
||
33 | * |
||
34 | * The Initial Developer of the Original Code is Red Hat, Inc. |
||
35 | * |
||
36 | * Contributor(s): |
||
37 | * Benjamin Otte |
||
38 | * Carl Worth |
||
39 | * Chris Wilson |
||
40 | * Eric Anholt |
||
41 | * Alexandros Frantzis |
||
42 | * Henry Song |
||
43 | * Martin Robinson |
||
44 | */ |
||
45 | |||
46 | #include "cairoint.h" |
||
47 | |||
48 | #include "cairo-gl-private.h" |
||
49 | |||
50 | #include "cairo-composite-rectangles-private.h" |
||
51 | #include "cairo-clip-private.h" |
||
52 | #include "cairo-error-private.h" |
||
53 | #include "cairo-image-surface-private.h" |
||
54 | |||
55 | cairo_int_status_t |
||
56 | _cairo_gl_composite_set_source (cairo_gl_composite_t *setup, |
||
57 | const cairo_pattern_t *pattern, |
||
58 | const cairo_rectangle_int_t *sample, |
||
59 | const cairo_rectangle_int_t *extents, |
||
60 | cairo_bool_t use_texgen) |
||
61 | { |
||
62 | _cairo_gl_operand_destroy (&setup->src); |
||
63 | return _cairo_gl_operand_init (&setup->src, pattern, setup->dst, |
||
64 | sample, extents, use_texgen); |
||
65 | } |
||
66 | |||
67 | void |
||
68 | _cairo_gl_composite_set_source_operand (cairo_gl_composite_t *setup, |
||
69 | const cairo_gl_operand_t *source) |
||
70 | { |
||
71 | _cairo_gl_operand_destroy (&setup->src); |
||
72 | _cairo_gl_operand_copy (&setup->src, source); |
||
73 | } |
||
74 | |||
75 | void |
||
76 | _cairo_gl_composite_set_solid_source (cairo_gl_composite_t *setup, |
||
77 | const cairo_color_t *color) |
||
78 | { |
||
79 | _cairo_gl_operand_destroy (&setup->src); |
||
80 | _cairo_gl_solid_operand_init (&setup->src, color); |
||
81 | } |
||
82 | |||
83 | cairo_int_status_t |
||
84 | _cairo_gl_composite_set_mask (cairo_gl_composite_t *setup, |
||
85 | const cairo_pattern_t *pattern, |
||
86 | const cairo_rectangle_int_t *sample, |
||
87 | const cairo_rectangle_int_t *extents, |
||
88 | cairo_bool_t use_texgen) |
||
89 | { |
||
90 | _cairo_gl_operand_destroy (&setup->mask); |
||
91 | if (pattern == NULL) |
||
92 | return CAIRO_STATUS_SUCCESS; |
||
93 | |||
94 | return _cairo_gl_operand_init (&setup->mask, pattern, setup->dst, |
||
95 | sample, extents, use_texgen); |
||
96 | } |
||
97 | |||
98 | void |
||
99 | _cairo_gl_composite_set_mask_operand (cairo_gl_composite_t *setup, |
||
100 | const cairo_gl_operand_t *mask) |
||
101 | { |
||
102 | _cairo_gl_operand_destroy (&setup->mask); |
||
103 | if (mask) |
||
104 | _cairo_gl_operand_copy (&setup->mask, mask); |
||
105 | } |
||
106 | |||
107 | void |
||
108 | _cairo_gl_composite_set_spans (cairo_gl_composite_t *setup) |
||
109 | { |
||
110 | setup->spans = TRUE; |
||
111 | } |
||
112 | |||
113 | void |
||
114 | _cairo_gl_composite_set_multisample (cairo_gl_composite_t *setup) |
||
115 | { |
||
116 | setup->multisample = TRUE; |
||
117 | } |
||
118 | |||
119 | void |
||
120 | _cairo_gl_composite_set_clip_region (cairo_gl_composite_t *setup, |
||
121 | cairo_region_t *clip_region) |
||
122 | { |
||
123 | setup->clip_region = clip_region; |
||
124 | } |
||
125 | |||
126 | void |
||
127 | _cairo_gl_composite_set_clip (cairo_gl_composite_t *setup, |
||
128 | cairo_clip_t *clip) |
||
129 | { |
||
130 | setup->clip = clip; |
||
131 | } |
||
132 | |||
133 | static void |
||
134 | _cairo_gl_composite_bind_to_shader (cairo_gl_context_t *ctx, |
||
135 | cairo_gl_composite_t *setup) |
||
136 | { |
||
137 | _cairo_gl_shader_bind_matrix4f(ctx, ctx->current_shader->mvp_location, |
||
138 | ctx->modelviewprojection_matrix); |
||
139 | _cairo_gl_operand_bind_to_shader (ctx, &setup->src, CAIRO_GL_TEX_SOURCE); |
||
140 | _cairo_gl_operand_bind_to_shader (ctx, &setup->mask, CAIRO_GL_TEX_MASK); |
||
141 | } |
||
142 | |||
143 | static void |
||
144 | _cairo_gl_texture_set_filter (cairo_gl_context_t *ctx, |
||
145 | GLuint target, |
||
146 | cairo_filter_t filter) |
||
147 | { |
||
148 | switch (filter) { |
||
149 | case CAIRO_FILTER_FAST: |
||
150 | case CAIRO_FILTER_NEAREST: |
||
151 | glTexParameteri (target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
||
152 | glTexParameteri (target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
||
153 | break; |
||
154 | case CAIRO_FILTER_GOOD: |
||
155 | case CAIRO_FILTER_BEST: |
||
156 | case CAIRO_FILTER_BILINEAR: |
||
157 | glTexParameteri (target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
||
158 | glTexParameteri (target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
||
159 | break; |
||
160 | default: |
||
161 | case CAIRO_FILTER_GAUSSIAN: |
||
162 | ASSERT_NOT_REACHED; |
||
163 | } |
||
164 | } |
||
165 | |||
166 | static void |
||
167 | _cairo_gl_texture_set_extend (cairo_gl_context_t *ctx, |
||
168 | GLuint target, |
||
169 | cairo_extend_t extend) |
||
170 | { |
||
171 | GLint wrap_mode; |
||
172 | assert (! _cairo_gl_device_requires_power_of_two_textures (&ctx->base) || |
||
173 | (extend != CAIRO_EXTEND_REPEAT && extend != CAIRO_EXTEND_REFLECT)); |
||
174 | |||
175 | switch (extend) { |
||
176 | case CAIRO_EXTEND_NONE: |
||
177 | if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES) |
||
178 | wrap_mode = GL_CLAMP_TO_EDGE; |
||
179 | else |
||
180 | wrap_mode = GL_CLAMP_TO_BORDER; |
||
181 | break; |
||
182 | case CAIRO_EXTEND_PAD: |
||
183 | wrap_mode = GL_CLAMP_TO_EDGE; |
||
184 | break; |
||
185 | case CAIRO_EXTEND_REPEAT: |
||
186 | if (ctx->has_npot_repeat) |
||
187 | wrap_mode = GL_REPEAT; |
||
188 | else |
||
189 | wrap_mode = GL_CLAMP_TO_EDGE; |
||
190 | break; |
||
191 | case CAIRO_EXTEND_REFLECT: |
||
192 | if (ctx->has_npot_repeat) |
||
193 | wrap_mode = GL_MIRRORED_REPEAT; |
||
194 | else |
||
195 | wrap_mode = GL_CLAMP_TO_EDGE; |
||
196 | break; |
||
197 | default: |
||
198 | wrap_mode = 0; |
||
199 | } |
||
200 | |||
201 | if (likely (wrap_mode)) { |
||
202 | glTexParameteri (target, GL_TEXTURE_WRAP_S, wrap_mode); |
||
203 | glTexParameteri (target, GL_TEXTURE_WRAP_T, wrap_mode); |
||
204 | } |
||
205 | } |
||
206 | |||
207 | |||
208 | static void |
||
209 | _cairo_gl_context_setup_operand (cairo_gl_context_t *ctx, |
||
210 | cairo_gl_tex_t tex_unit, |
||
211 | cairo_gl_operand_t *operand, |
||
212 | unsigned int vertex_offset, |
||
213 | cairo_bool_t vertex_size_changed) |
||
214 | { |
||
215 | cairo_gl_dispatch_t *dispatch = &ctx->dispatch; |
||
216 | cairo_bool_t needs_setup; |
||
217 | |||
218 | /* XXX: we need to do setup when switching from shaders |
||
219 | * to no shaders (or back) */ |
||
220 | needs_setup = vertex_size_changed; |
||
221 | needs_setup |= _cairo_gl_operand_needs_setup (&ctx->operands[tex_unit], |
||
222 | operand, |
||
223 | vertex_offset); |
||
224 | |||
225 | if (needs_setup) { |
||
226 | _cairo_gl_composite_flush (ctx); |
||
227 | _cairo_gl_context_destroy_operand (ctx, tex_unit); |
||
228 | } |
||
229 | |||
230 | memcpy (&ctx->operands[tex_unit], operand, sizeof (cairo_gl_operand_t)); |
||
231 | ctx->operands[tex_unit].vertex_offset = vertex_offset; |
||
232 | |||
233 | if (! needs_setup) |
||
234 | return; |
||
235 | |||
236 | switch (operand->type) { |
||
237 | default: |
||
238 | case CAIRO_GL_OPERAND_COUNT: |
||
239 | ASSERT_NOT_REACHED; |
||
240 | case CAIRO_GL_OPERAND_NONE: |
||
241 | break; |
||
242 | /* fall through */ |
||
243 | case CAIRO_GL_OPERAND_CONSTANT: |
||
244 | break; |
||
245 | case CAIRO_GL_OPERAND_TEXTURE: |
||
246 | glActiveTexture (GL_TEXTURE0 + tex_unit); |
||
247 | glBindTexture (ctx->tex_target, operand->texture.tex); |
||
248 | _cairo_gl_texture_set_extend (ctx, ctx->tex_target, |
||
249 | operand->texture.attributes.extend); |
||
250 | _cairo_gl_texture_set_filter (ctx, ctx->tex_target, |
||
251 | operand->texture.attributes.filter); |
||
252 | |||
253 | if (! operand->texture.texgen) { |
||
254 | dispatch->VertexAttribPointer (CAIRO_GL_TEXCOORD0_ATTRIB_INDEX + tex_unit, 2, |
||
255 | GL_FLOAT, GL_FALSE, ctx->vertex_size, |
||
256 | ctx->vb + vertex_offset); |
||
257 | dispatch->EnableVertexAttribArray (CAIRO_GL_TEXCOORD0_ATTRIB_INDEX + tex_unit); |
||
258 | } |
||
259 | break; |
||
260 | case CAIRO_GL_OPERAND_LINEAR_GRADIENT: |
||
261 | case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0: |
||
262 | case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE: |
||
263 | case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT: |
||
264 | glActiveTexture (GL_TEXTURE0 + tex_unit); |
||
265 | glBindTexture (ctx->tex_target, operand->gradient.gradient->tex); |
||
266 | _cairo_gl_texture_set_extend (ctx, ctx->tex_target, operand->gradient.extend); |
||
267 | _cairo_gl_texture_set_filter (ctx, ctx->tex_target, CAIRO_FILTER_BILINEAR); |
||
268 | |||
269 | if (! operand->gradient.texgen) { |
||
270 | dispatch->VertexAttribPointer (CAIRO_GL_TEXCOORD0_ATTRIB_INDEX + tex_unit, 2, |
||
271 | GL_FLOAT, GL_FALSE, ctx->vertex_size, |
||
272 | ctx->vb + vertex_offset); |
||
273 | dispatch->EnableVertexAttribArray (CAIRO_GL_TEXCOORD0_ATTRIB_INDEX + tex_unit); |
||
274 | } |
||
275 | break; |
||
276 | } |
||
277 | } |
||
278 | |||
279 | static void |
||
280 | _cairo_gl_context_setup_spans (cairo_gl_context_t *ctx, |
||
281 | cairo_bool_t spans_enabled, |
||
282 | unsigned int vertex_size, |
||
283 | unsigned int vertex_offset) |
||
284 | { |
||
285 | cairo_gl_dispatch_t *dispatch = &ctx->dispatch; |
||
286 | |||
287 | if (! spans_enabled) { |
||
288 | dispatch->DisableVertexAttribArray (CAIRO_GL_COLOR_ATTRIB_INDEX); |
||
289 | ctx->spans = FALSE; |
||
290 | return; |
||
291 | } |
||
292 | |||
293 | dispatch->VertexAttribPointer (CAIRO_GL_COLOR_ATTRIB_INDEX, 4, |
||
294 | GL_UNSIGNED_BYTE, GL_TRUE, vertex_size, |
||
295 | ctx->vb + vertex_offset); |
||
296 | dispatch->EnableVertexAttribArray (CAIRO_GL_COLOR_ATTRIB_INDEX); |
||
297 | ctx->spans = TRUE; |
||
298 | } |
||
299 | |||
300 | void |
||
301 | _cairo_gl_context_destroy_operand (cairo_gl_context_t *ctx, |
||
302 | cairo_gl_tex_t tex_unit) |
||
303 | { |
||
304 | cairo_gl_dispatch_t *dispatch = &ctx->dispatch; |
||
305 | |||
306 | if (!_cairo_gl_context_is_flushed (ctx)) |
||
307 | _cairo_gl_composite_flush (ctx); |
||
308 | |||
309 | switch (ctx->operands[tex_unit].type) { |
||
310 | default: |
||
311 | case CAIRO_GL_OPERAND_COUNT: |
||
312 | ASSERT_NOT_REACHED; |
||
313 | case CAIRO_GL_OPERAND_NONE: |
||
314 | break; |
||
315 | /* fall through */ |
||
316 | case CAIRO_GL_OPERAND_CONSTANT: |
||
317 | break; |
||
318 | case CAIRO_GL_OPERAND_TEXTURE: |
||
319 | dispatch->DisableVertexAttribArray (CAIRO_GL_TEXCOORD0_ATTRIB_INDEX + tex_unit); |
||
320 | break; |
||
321 | case CAIRO_GL_OPERAND_LINEAR_GRADIENT: |
||
322 | case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0: |
||
323 | case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE: |
||
324 | case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT: |
||
325 | dispatch->DisableVertexAttribArray (CAIRO_GL_TEXCOORD0_ATTRIB_INDEX + tex_unit); |
||
326 | break; |
||
327 | } |
||
328 | |||
329 | memset (&ctx->operands[tex_unit], 0, sizeof (cairo_gl_operand_t)); |
||
330 | } |
||
331 | |||
332 | static void |
||
333 | _cairo_gl_set_operator (cairo_gl_context_t *ctx, |
||
334 | cairo_operator_t op, |
||
335 | cairo_bool_t component_alpha) |
||
336 | { |
||
337 | struct { |
||
338 | GLenum src; |
||
339 | GLenum dst; |
||
340 | } blend_factors[] = { |
||
341 | { GL_ZERO, GL_ZERO }, /* Clear */ |
||
342 | { GL_ONE, GL_ZERO }, /* Source */ |
||
343 | { GL_ONE, GL_ONE_MINUS_SRC_ALPHA }, /* Over */ |
||
344 | { GL_DST_ALPHA, GL_ZERO }, /* In */ |
||
345 | { GL_ONE_MINUS_DST_ALPHA, GL_ZERO }, /* Out */ |
||
346 | { GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA }, /* Atop */ |
||
347 | |||
348 | { GL_ZERO, GL_ONE }, /* Dest */ |
||
349 | { GL_ONE_MINUS_DST_ALPHA, GL_ONE }, /* DestOver */ |
||
350 | { GL_ZERO, GL_SRC_ALPHA }, /* DestIn */ |
||
351 | { GL_ZERO, GL_ONE_MINUS_SRC_ALPHA }, /* DestOut */ |
||
352 | { GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA }, /* DestAtop */ |
||
353 | |||
354 | { GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA }, /* Xor */ |
||
355 | { GL_ONE, GL_ONE }, /* Add */ |
||
356 | }; |
||
357 | GLenum src_factor, dst_factor; |
||
358 | |||
359 | assert (op < ARRAY_LENGTH (blend_factors)); |
||
360 | /* different dst and component_alpha changes cause flushes elsewhere */ |
||
361 | if (ctx->current_operator != op) |
||
362 | _cairo_gl_composite_flush (ctx); |
||
363 | ctx->current_operator = op; |
||
364 | |||
365 | src_factor = blend_factors[op].src; |
||
366 | dst_factor = blend_factors[op].dst; |
||
367 | |||
368 | /* Even when the user requests CAIRO_CONTENT_COLOR, we use GL_RGBA |
||
369 | * due to texture filtering of GL_CLAMP_TO_BORDER. So fix those |
||
370 | * bits in that case. |
||
371 | */ |
||
372 | if (ctx->current_target->base.content == CAIRO_CONTENT_COLOR) { |
||
373 | if (src_factor == GL_ONE_MINUS_DST_ALPHA) |
||
374 | src_factor = GL_ZERO; |
||
375 | if (src_factor == GL_DST_ALPHA) |
||
376 | src_factor = GL_ONE; |
||
377 | } |
||
378 | |||
379 | if (component_alpha) { |
||
380 | if (dst_factor == GL_ONE_MINUS_SRC_ALPHA) |
||
381 | dst_factor = GL_ONE_MINUS_SRC_COLOR; |
||
382 | if (dst_factor == GL_SRC_ALPHA) |
||
383 | dst_factor = GL_SRC_COLOR; |
||
384 | } |
||
385 | |||
386 | if (ctx->current_target->base.content == CAIRO_CONTENT_ALPHA) { |
||
387 | glBlendFuncSeparate (GL_ZERO, GL_ZERO, src_factor, dst_factor); |
||
388 | } else if (ctx->current_target->base.content == CAIRO_CONTENT_COLOR) { |
||
389 | glBlendFuncSeparate (src_factor, dst_factor, GL_ONE, GL_ONE); |
||
390 | } else { |
||
391 | glBlendFunc (src_factor, dst_factor); |
||
392 | } |
||
393 | } |
||
394 | |||
395 | static cairo_status_t |
||
396 | _cairo_gl_composite_begin_component_alpha (cairo_gl_context_t *ctx, |
||
397 | cairo_gl_composite_t *setup) |
||
398 | { |
||
399 | cairo_gl_shader_t *pre_shader = NULL; |
||
400 | cairo_status_t status; |
||
401 | |||
402 | /* For CLEAR, cairo's rendering equation (quoting Owen's description in: |
||
403 | * http://lists.cairographics.org/archives/cairo/2005-August/004992.html) |
||
404 | * is: |
||
405 | * mask IN clip ? src OP dest : dest |
||
406 | * or more simply: |
||
407 | * mask IN CLIP ? 0 : dest |
||
408 | * |
||
409 | * where the ternary operator A ? B : C is (A * B) + ((1 - A) * C). |
||
410 | * |
||
411 | * The model we use in _cairo_gl_set_operator() is Render's: |
||
412 | * src IN mask IN clip OP dest |
||
413 | * which would boil down to: |
||
414 | * 0 (bounded by the extents of the drawing). |
||
415 | * |
||
416 | * However, we can do a Render operation using an opaque source |
||
417 | * and DEST_OUT to produce: |
||
418 | * 1 IN mask IN clip DEST_OUT dest |
||
419 | * which is |
||
420 | * mask IN clip ? 0 : dest |
||
421 | */ |
||
422 | if (setup->op == CAIRO_OPERATOR_CLEAR) { |
||
423 | _cairo_gl_solid_operand_init (&setup->src, CAIRO_COLOR_WHITE); |
||
424 | setup->op = CAIRO_OPERATOR_DEST_OUT; |
||
425 | } |
||
426 | |||
427 | /* |
||
428 | * implements component-alpha %CAIRO_OPERATOR_OVER using two passes of |
||
429 | * the simpler operations %CAIRO_OPERATOR_DEST_OUT and %CAIRO_OPERATOR_ADD. |
||
430 | * |
||
431 | * From http://anholt.livejournal.com/32058.html: |
||
432 | * |
||
433 | * The trouble is that component-alpha rendering requires two different sources |
||
434 | * for blending: one for the source value to the blender, which is the |
||
435 | * per-channel multiplication of source and mask, and one for the source alpha |
||
436 | * for multiplying with the destination channels, which is the multiplication |
||
437 | * of the source channels by the mask alpha. So the equation for Over is: |
||
438 | * |
||
439 | * dst.A = src.A * mask.A + (1 - (src.A * mask.A)) * dst.A |
||
440 | * dst.R = src.R * mask.R + (1 - (src.A * mask.R)) * dst.R |
||
441 | * dst.G = src.G * mask.G + (1 - (src.A * mask.G)) * dst.G |
||
442 | * dst.B = src.B * mask.B + (1 - (src.A * mask.B)) * dst.B |
||
443 | * |
||
444 | * But we can do some simpler operations, right? How about PictOpOutReverse, |
||
445 | * which has a source factor of 0 and dest factor of (1 - source alpha). We |
||
446 | * can get the source alpha value (srca.X = src.A * mask.X) out of the texture |
||
447 | * blenders pretty easily. So we can do a component-alpha OutReverse, which |
||
448 | * gets us: |
||
449 | * |
||
450 | * dst.A = 0 + (1 - (src.A * mask.A)) * dst.A |
||
451 | * dst.R = 0 + (1 - (src.A * mask.R)) * dst.R |
||
452 | * dst.G = 0 + (1 - (src.A * mask.G)) * dst.G |
||
453 | * dst.B = 0 + (1 - (src.A * mask.B)) * dst.B |
||
454 | * |
||
455 | * OK. And if an op doesn't use the source alpha value for the destination |
||
456 | * factor, then we can do the channel multiplication in the texture blenders |
||
457 | * to get the source value, and ignore the source alpha that we wouldn't use. |
||
458 | * We've supported this in the Radeon driver for a long time. An example would |
||
459 | * be PictOpAdd, which does: |
||
460 | * |
||
461 | * dst.A = src.A * mask.A + dst.A |
||
462 | * dst.R = src.R * mask.R + dst.R |
||
463 | * dst.G = src.G * mask.G + dst.G |
||
464 | * dst.B = src.B * mask.B + dst.B |
||
465 | * |
||
466 | * Hey, this looks good! If we do a PictOpOutReverse and then a PictOpAdd right |
||
467 | * after it, we get: |
||
468 | * |
||
469 | * dst.A = src.A * mask.A + ((1 - (src.A * mask.A)) * dst.A) |
||
470 | * dst.R = src.R * mask.R + ((1 - (src.A * mask.R)) * dst.R) |
||
471 | * dst.G = src.G * mask.G + ((1 - (src.A * mask.G)) * dst.G) |
||
472 | * dst.B = src.B * mask.B + ((1 - (src.A * mask.B)) * dst.B) |
||
473 | * |
||
474 | * This two-pass trickery could be avoided using a new GL extension that |
||
475 | * lets two values come out of the shader and into the blend unit. |
||
476 | */ |
||
477 | if (setup->op == CAIRO_OPERATOR_OVER) { |
||
478 | setup->op = CAIRO_OPERATOR_ADD; |
||
479 | status = _cairo_gl_get_shader_by_type (ctx, |
||
480 | &setup->src, |
||
481 | &setup->mask, |
||
482 | setup->spans, |
||
483 | CAIRO_GL_SHADER_IN_CA_SOURCE_ALPHA, |
||
484 | &pre_shader); |
||
485 | if (unlikely (status)) |
||
486 | return status; |
||
487 | } |
||
488 | |||
489 | if (ctx->pre_shader != pre_shader) |
||
490 | _cairo_gl_composite_flush (ctx); |
||
491 | ctx->pre_shader = pre_shader; |
||
492 | |||
493 | return CAIRO_STATUS_SUCCESS; |
||
494 | } |
||
495 | |||
496 | static void |
||
497 | _scissor_to_doubles (cairo_gl_surface_t *surface, |
||
498 | double x1, double y1, |
||
499 | double x2, double y2) |
||
500 | { |
||
501 | double height; |
||
502 | |||
503 | height = y2 - y1; |
||
504 | if (_cairo_gl_surface_is_texture (surface) == FALSE) |
||
505 | y1 = surface->height - (y1 + height); |
||
506 | glScissor (x1, y1, x2 - x1, height); |
||
507 | glEnable (GL_SCISSOR_TEST); |
||
508 | } |
||
509 | |||
510 | void |
||
511 | _cairo_gl_scissor_to_rectangle (cairo_gl_surface_t *surface, |
||
512 | const cairo_rectangle_int_t *r) |
||
513 | { |
||
514 | _scissor_to_doubles (surface, r->x, r->y, r->x+r->width, r->y+r->height); |
||
515 | } |
||
516 | |||
517 | static void |
||
518 | _scissor_to_box (cairo_gl_surface_t *surface, |
||
519 | const cairo_box_t *box) |
||
520 | { |
||
521 | double x1, y1, x2, y2; |
||
522 | _cairo_box_to_doubles (box, &x1, &y1, &x2, &y2); |
||
523 | _scissor_to_doubles (surface, x1, y1, x2, y2); |
||
524 | } |
||
525 | |||
526 | static cairo_bool_t |
||
527 | _cairo_gl_composite_setup_vbo (cairo_gl_context_t *ctx, |
||
528 | unsigned int size_per_vertex) |
||
529 | { |
||
530 | cairo_bool_t vertex_size_changed = ctx->vertex_size != size_per_vertex; |
||
531 | if (vertex_size_changed) { |
||
532 | ctx->vertex_size = size_per_vertex; |
||
533 | _cairo_gl_composite_flush (ctx); |
||
534 | } |
||
535 | |||
536 | if (_cairo_gl_context_is_flushed (ctx)) { |
||
537 | ctx->dispatch.VertexAttribPointer (CAIRO_GL_VERTEX_ATTRIB_INDEX, 2, |
||
538 | GL_FLOAT, GL_FALSE, size_per_vertex, |
||
539 | ctx->vb); |
||
540 | ctx->dispatch.EnableVertexAttribArray (CAIRO_GL_VERTEX_ATTRIB_INDEX); |
||
541 | } |
||
542 | |||
543 | return vertex_size_changed; |
||
544 | } |
||
545 | |||
546 | static void |
||
547 | _disable_stencil_buffer (void) |
||
548 | { |
||
549 | glDisable (GL_STENCIL_TEST); |
||
550 | glDepthMask (GL_FALSE); |
||
551 | } |
||
552 | |||
553 | static cairo_int_status_t |
||
554 | _cairo_gl_composite_setup_painted_clipping (cairo_gl_composite_t *setup, |
||
555 | cairo_gl_context_t *ctx, |
||
556 | int vertex_size) |
||
557 | { |
||
558 | cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS; |
||
559 | |||
560 | cairo_gl_surface_t *dst = setup->dst; |
||
561 | cairo_clip_t *clip = setup->clip; |
||
562 | |||
563 | if (clip->num_boxes == 1 && clip->path == NULL) { |
||
564 | _scissor_to_box (dst, &clip->boxes[0]); |
||
565 | goto disable_stencil_buffer_and_return; |
||
566 | } |
||
567 | |||
568 | if (! _cairo_gl_ensure_stencil (ctx, setup->dst)) { |
||
569 | status = CAIRO_INT_STATUS_UNSUPPORTED; |
||
570 | goto disable_stencil_buffer_and_return; |
||
571 | } |
||
572 | |||
573 | /* We only want to clear the part of the stencil buffer |
||
574 | * that we are about to use. It also does not hurt to |
||
575 | * scissor around the painted clip. */ |
||
576 | _cairo_gl_scissor_to_rectangle (dst, _cairo_clip_get_extents (clip)); |
||
577 | |||
578 | /* The clip is not rectangular, so use the stencil buffer. */ |
||
579 | glDepthMask (GL_TRUE); |
||
580 | glEnable (GL_STENCIL_TEST); |
||
581 | |||
582 | /* Texture surfaces have private depth/stencil buffers, so we can |
||
583 | * rely on any previous clip being cached there. */ |
||
584 | if (_cairo_gl_surface_is_texture (setup->dst)) { |
||
585 | cairo_clip_t *old_clip = setup->dst->clip_on_stencil_buffer; |
||
586 | if (_cairo_clip_equal (old_clip, setup->clip)) |
||
587 | goto activate_stencil_buffer_and_return; |
||
588 | |||
589 | if (old_clip) { |
||
590 | _cairo_clip_destroy (setup->dst->clip_on_stencil_buffer); |
||
591 | } |
||
592 | |||
593 | setup->dst->clip_on_stencil_buffer = _cairo_clip_copy (setup->clip); |
||
594 | } |
||
595 | |||
596 | glClearStencil (0); |
||
597 | glClear (GL_STENCIL_BUFFER_BIT); |
||
598 | |||
599 | glStencilOp (GL_REPLACE, GL_REPLACE, GL_REPLACE); |
||
600 | glStencilFunc (GL_EQUAL, 1, 0xffffffff); |
||
601 | glColorMask (0, 0, 0, 0); |
||
602 | |||
603 | status = _cairo_gl_msaa_compositor_draw_clip (ctx, setup, clip); |
||
604 | |||
605 | if (unlikely (status)) { |
||
606 | glColorMask (1, 1, 1, 1); |
||
607 | goto disable_stencil_buffer_and_return; |
||
608 | } |
||
609 | |||
610 | /* We want to only render to the stencil buffer, so draw everything now. |
||
611 | Flushing also unbinds the VBO, which we want to rebind for regular |
||
612 | drawing. */ |
||
613 | _cairo_gl_composite_flush (ctx); |
||
614 | _cairo_gl_composite_setup_vbo (ctx, vertex_size); |
||
615 | |||
616 | activate_stencil_buffer_and_return: |
||
617 | glColorMask (1, 1, 1, 1); |
||
618 | glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP); |
||
619 | glStencilFunc (GL_EQUAL, 1, 0xffffffff); |
||
620 | return CAIRO_INT_STATUS_SUCCESS; |
||
621 | |||
622 | disable_stencil_buffer_and_return: |
||
623 | _disable_stencil_buffer (); |
||
624 | return status; |
||
625 | } |
||
626 | |||
627 | static cairo_int_status_t |
||
628 | _cairo_gl_composite_setup_clipping (cairo_gl_composite_t *setup, |
||
629 | cairo_gl_context_t *ctx, |
||
630 | int vertex_size) |
||
631 | { |
||
632 | cairo_bool_t clip_changing = TRUE; |
||
633 | cairo_bool_t clip_region_changing = TRUE; |
||
634 | |||
635 | if (! ctx->clip && ! setup->clip && ! setup->clip_region && ! ctx->clip_region) |
||
636 | goto disable_all_clipping; |
||
637 | |||
638 | clip_changing = ! _cairo_clip_equal (ctx->clip, setup->clip); |
||
639 | clip_region_changing = ! cairo_region_equal (ctx->clip_region, setup->clip_region); |
||
640 | if (! _cairo_gl_context_is_flushed (ctx) && |
||
641 | (clip_region_changing || clip_changing)) |
||
642 | _cairo_gl_composite_flush (ctx); |
||
643 | |||
644 | assert (!setup->clip_region || !setup->clip); |
||
645 | |||
646 | /* setup->clip is only used by the msaa compositor and setup->clip_region |
||
647 | * only by the other compositors, so it's safe to wait to clean up obsolete |
||
648 | * clips. */ |
||
649 | if (clip_region_changing) { |
||
650 | cairo_region_destroy (ctx->clip_region); |
||
651 | ctx->clip_region = cairo_region_reference (setup->clip_region); |
||
652 | } |
||
653 | if (clip_changing) { |
||
654 | _cairo_clip_destroy (ctx->clip); |
||
655 | ctx->clip = _cairo_clip_copy (setup->clip); |
||
656 | } |
||
657 | |||
658 | /* For clip regions, we scissor right before drawing. */ |
||
659 | if (setup->clip_region) |
||
660 | goto disable_all_clipping; |
||
661 | |||
662 | if (setup->clip) |
||
663 | return _cairo_gl_composite_setup_painted_clipping (setup, ctx, |
||
664 | vertex_size); |
||
665 | disable_all_clipping: |
||
666 | _disable_stencil_buffer (); |
||
667 | glDisable (GL_SCISSOR_TEST); |
||
668 | return CAIRO_INT_STATUS_SUCCESS; |
||
669 | } |
||
670 | |||
671 | cairo_status_t |
||
672 | _cairo_gl_set_operands_and_operator (cairo_gl_composite_t *setup, |
||
673 | cairo_gl_context_t *ctx) |
||
674 | { |
||
675 | unsigned int dst_size, src_size, mask_size, vertex_size; |
||
676 | cairo_status_t status; |
||
677 | cairo_gl_shader_t *shader; |
||
678 | cairo_bool_t component_alpha; |
||
679 | cairo_bool_t vertex_size_changed; |
||
680 | |||
681 | component_alpha = |
||
682 | setup->mask.type == CAIRO_GL_OPERAND_TEXTURE && |
||
683 | setup->mask.texture.attributes.has_component_alpha; |
||
684 | |||
685 | /* Do various magic for component alpha */ |
||
686 | if (component_alpha) { |
||
687 | status = _cairo_gl_composite_begin_component_alpha (ctx, setup); |
||
688 | if (unlikely (status)) |
||
689 | return status; |
||
690 | } else { |
||
691 | if (ctx->pre_shader) { |
||
692 | _cairo_gl_composite_flush (ctx); |
||
693 | ctx->pre_shader = NULL; |
||
694 | } |
||
695 | } |
||
696 | |||
697 | status = _cairo_gl_get_shader_by_type (ctx, |
||
698 | &setup->src, |
||
699 | &setup->mask, |
||
700 | setup->spans, |
||
701 | component_alpha ? |
||
702 | CAIRO_GL_SHADER_IN_CA_SOURCE : |
||
703 | CAIRO_GL_SHADER_IN_NORMAL, |
||
704 | &shader); |
||
705 | if (unlikely (status)) { |
||
706 | ctx->pre_shader = NULL; |
||
707 | return status; |
||
708 | } |
||
709 | if (ctx->current_shader != shader) |
||
710 | _cairo_gl_composite_flush (ctx); |
||
711 | |||
712 | status = CAIRO_STATUS_SUCCESS; |
||
713 | |||
714 | dst_size = 2 * sizeof (GLfloat); |
||
715 | src_size = _cairo_gl_operand_get_vertex_size (&setup->src); |
||
716 | mask_size = _cairo_gl_operand_get_vertex_size (&setup->mask); |
||
717 | vertex_size = dst_size + src_size + mask_size; |
||
718 | |||
719 | if (setup->spans) |
||
720 | vertex_size += sizeof (GLfloat); |
||
721 | |||
722 | vertex_size_changed = _cairo_gl_composite_setup_vbo (ctx, vertex_size); |
||
723 | |||
724 | _cairo_gl_context_setup_operand (ctx, CAIRO_GL_TEX_SOURCE, &setup->src, dst_size, vertex_size_changed); |
||
725 | _cairo_gl_context_setup_operand (ctx, CAIRO_GL_TEX_MASK, &setup->mask, dst_size + src_size, vertex_size_changed); |
||
726 | |||
727 | _cairo_gl_context_setup_spans (ctx, setup->spans, vertex_size, |
||
728 | dst_size + src_size + mask_size); |
||
729 | |||
730 | _cairo_gl_set_operator (ctx, setup->op, component_alpha); |
||
731 | |||
732 | if (_cairo_gl_context_is_flushed (ctx)) { |
||
733 | if (ctx->pre_shader) { |
||
734 | _cairo_gl_set_shader (ctx, ctx->pre_shader); |
||
735 | _cairo_gl_composite_bind_to_shader (ctx, setup); |
||
736 | } |
||
737 | _cairo_gl_set_shader (ctx, shader); |
||
738 | _cairo_gl_composite_bind_to_shader (ctx, setup); |
||
739 | } |
||
740 | |||
741 | return status; |
||
742 | } |
||
743 | |||
744 | cairo_status_t |
||
745 | _cairo_gl_composite_begin (cairo_gl_composite_t *setup, |
||
746 | cairo_gl_context_t **ctx_out) |
||
747 | { |
||
748 | cairo_gl_context_t *ctx; |
||
749 | cairo_status_t status; |
||
750 | |||
751 | assert (setup->dst); |
||
752 | |||
753 | status = _cairo_gl_context_acquire (setup->dst->base.device, &ctx); |
||
754 | if (unlikely (status)) |
||
755 | return status; |
||
756 | |||
757 | _cairo_gl_context_set_destination (ctx, setup->dst, setup->multisample); |
||
758 | glEnable (GL_BLEND); |
||
759 | |||
760 | status = _cairo_gl_set_operands_and_operator (setup, ctx); |
||
761 | if (unlikely (status)) |
||
762 | goto FAIL; |
||
763 | |||
764 | status = _cairo_gl_composite_setup_clipping (setup, ctx, ctx->vertex_size); |
||
765 | if (unlikely (status)) |
||
766 | goto FAIL; |
||
767 | |||
768 | *ctx_out = ctx; |
||
769 | |||
770 | FAIL: |
||
771 | if (unlikely (status)) |
||
772 | status = _cairo_gl_context_release (ctx, status); |
||
773 | |||
774 | return status; |
||
775 | } |
||
776 | |||
777 | static inline void |
||
778 | _cairo_gl_composite_draw_tristrip (cairo_gl_context_t *ctx) |
||
779 | { |
||
780 | cairo_array_t* indices = &ctx->tristrip_indices; |
||
781 | const unsigned short *indices_array = _cairo_array_index_const (indices, 0); |
||
782 | |||
783 | if (ctx->pre_shader) { |
||
784 | cairo_gl_shader_t *prev_shader = ctx->current_shader; |
||
785 | |||
786 | _cairo_gl_set_shader (ctx, ctx->pre_shader); |
||
787 | _cairo_gl_set_operator (ctx, CAIRO_OPERATOR_DEST_OUT, TRUE); |
||
788 | glDrawElements (GL_TRIANGLE_STRIP, _cairo_array_num_elements (indices), GL_UNSIGNED_SHORT, indices_array); |
||
789 | |||
790 | _cairo_gl_set_shader (ctx, prev_shader); |
||
791 | _cairo_gl_set_operator (ctx, CAIRO_OPERATOR_ADD, TRUE); |
||
792 | } |
||
793 | |||
794 | glDrawElements (GL_TRIANGLE_STRIP, _cairo_array_num_elements (indices), GL_UNSIGNED_SHORT, indices_array); |
||
795 | _cairo_array_truncate (indices, 0); |
||
796 | } |
||
797 | |||
798 | static inline void |
||
799 | _cairo_gl_composite_draw_triangles (cairo_gl_context_t *ctx, |
||
800 | unsigned int count) |
||
801 | { |
||
802 | if (! ctx->pre_shader) { |
||
803 | glDrawArrays (GL_TRIANGLES, 0, count); |
||
804 | } else { |
||
805 | cairo_gl_shader_t *prev_shader = ctx->current_shader; |
||
806 | |||
807 | _cairo_gl_set_shader (ctx, ctx->pre_shader); |
||
808 | _cairo_gl_set_operator (ctx, CAIRO_OPERATOR_DEST_OUT, TRUE); |
||
809 | glDrawArrays (GL_TRIANGLES, 0, count); |
||
810 | |||
811 | _cairo_gl_set_shader (ctx, prev_shader); |
||
812 | _cairo_gl_set_operator (ctx, CAIRO_OPERATOR_ADD, TRUE); |
||
813 | glDrawArrays (GL_TRIANGLES, 0, count); |
||
814 | } |
||
815 | } |
||
816 | |||
817 | static void |
||
818 | _cairo_gl_composite_draw_triangles_with_clip_region (cairo_gl_context_t *ctx, |
||
819 | unsigned int count) |
||
820 | { |
||
821 | int i, num_rectangles; |
||
822 | |||
823 | if (!ctx->clip_region) { |
||
824 | _cairo_gl_composite_draw_triangles (ctx, count); |
||
825 | return; |
||
826 | } |
||
827 | |||
828 | num_rectangles = cairo_region_num_rectangles (ctx->clip_region); |
||
829 | for (i = 0; i < num_rectangles; i++) { |
||
830 | cairo_rectangle_int_t rect; |
||
831 | |||
832 | cairo_region_get_rectangle (ctx->clip_region, i, &rect); |
||
833 | |||
834 | _cairo_gl_scissor_to_rectangle (ctx->current_target, &rect); |
||
835 | _cairo_gl_composite_draw_triangles (ctx, count); |
||
836 | } |
||
837 | } |
||
838 | |||
839 | static void |
||
840 | _cairo_gl_composite_unmap_vertex_buffer (cairo_gl_context_t *ctx) |
||
841 | { |
||
842 | ctx->vb_offset = 0; |
||
843 | } |
||
844 | |||
845 | void |
||
846 | _cairo_gl_composite_flush (cairo_gl_context_t *ctx) |
||
847 | { |
||
848 | unsigned int count; |
||
849 | int i; |
||
850 | |||
851 | if (_cairo_gl_context_is_flushed (ctx)) |
||
852 | return; |
||
853 | |||
854 | count = ctx->vb_offset / ctx->vertex_size; |
||
855 | _cairo_gl_composite_unmap_vertex_buffer (ctx); |
||
856 | |||
857 | if (ctx->primitive_type == CAIRO_GL_PRIMITIVE_TYPE_TRISTRIPS) { |
||
858 | _cairo_gl_composite_draw_tristrip (ctx); |
||
859 | } else { |
||
860 | assert (ctx->primitive_type == CAIRO_GL_PRIMITIVE_TYPE_TRIANGLES); |
||
861 | _cairo_gl_composite_draw_triangles_with_clip_region (ctx, count); |
||
862 | } |
||
863 | |||
864 | for (i = 0; i < ARRAY_LENGTH (&ctx->glyph_cache); i++) |
||
865 | _cairo_gl_glyph_cache_unlock (&ctx->glyph_cache[i]); |
||
866 | } |
||
867 | |||
868 | static void |
||
869 | _cairo_gl_composite_prepare_buffer (cairo_gl_context_t *ctx, |
||
870 | unsigned int n_vertices, |
||
871 | cairo_gl_primitive_type_t primitive_type) |
||
872 | { |
||
873 | if (ctx->primitive_type != primitive_type) { |
||
874 | _cairo_gl_composite_flush (ctx); |
||
875 | ctx->primitive_type = primitive_type; |
||
876 | } |
||
877 | |||
878 | if (ctx->vb_offset + n_vertices * ctx->vertex_size > CAIRO_GL_VBO_SIZE) |
||
879 | _cairo_gl_composite_flush (ctx); |
||
880 | } |
||
881 | |||
882 | static inline void |
||
883 | _cairo_gl_composite_emit_vertex (cairo_gl_context_t *ctx, |
||
884 | GLfloat x, GLfloat y) |
||
885 | { |
||
886 | GLfloat *vb = (GLfloat *) (void *) &ctx->vb[ctx->vb_offset]; |
||
887 | |||
888 | *vb++ = x; |
||
889 | *vb++ = y; |
||
890 | |||
891 | _cairo_gl_operand_emit (&ctx->operands[CAIRO_GL_TEX_SOURCE], &vb, x, y); |
||
892 | _cairo_gl_operand_emit (&ctx->operands[CAIRO_GL_TEX_MASK ], &vb, x, y); |
||
893 | |||
894 | ctx->vb_offset += ctx->vertex_size; |
||
895 | } |
||
896 | |||
897 | static inline void |
||
898 | _cairo_gl_composite_emit_alpha_vertex (cairo_gl_context_t *ctx, |
||
899 | GLfloat x, GLfloat y, uint8_t alpha) |
||
900 | { |
||
901 | GLfloat *vb = (GLfloat *) (void *) &ctx->vb[ctx->vb_offset]; |
||
902 | union fi { |
||
903 | float f; |
||
904 | GLbyte bytes[4]; |
||
905 | } fi; |
||
906 | |||
907 | *vb++ = x; |
||
908 | *vb++ = y; |
||
909 | |||
910 | _cairo_gl_operand_emit (&ctx->operands[CAIRO_GL_TEX_SOURCE], &vb, x, y); |
||
911 | _cairo_gl_operand_emit (&ctx->operands[CAIRO_GL_TEX_MASK ], &vb, x, y); |
||
912 | |||
913 | fi.bytes[0] = 0; |
||
914 | fi.bytes[1] = 0; |
||
915 | fi.bytes[2] = 0; |
||
916 | fi.bytes[3] = alpha; |
||
917 | *vb++ = fi.f; |
||
918 | |||
919 | ctx->vb_offset += ctx->vertex_size; |
||
920 | } |
||
921 | |||
922 | static void |
||
923 | _cairo_gl_composite_emit_point (cairo_gl_context_t *ctx, |
||
924 | const cairo_point_t *point) |
||
925 | { |
||
926 | _cairo_gl_composite_emit_vertex (ctx, |
||
927 | _cairo_fixed_to_double (point->x), |
||
928 | _cairo_fixed_to_double (point->y)); |
||
929 | } |
||
930 | |||
931 | static void |
||
932 | _cairo_gl_composite_emit_rect (cairo_gl_context_t *ctx, |
||
933 | GLfloat x1, GLfloat y1, |
||
934 | GLfloat x2, GLfloat y2) |
||
935 | { |
||
936 | _cairo_gl_composite_prepare_buffer (ctx, 6, |
||
937 | CAIRO_GL_PRIMITIVE_TYPE_TRIANGLES); |
||
938 | |||
939 | _cairo_gl_composite_emit_vertex (ctx, x1, y1); |
||
940 | _cairo_gl_composite_emit_vertex (ctx, x2, y1); |
||
941 | _cairo_gl_composite_emit_vertex (ctx, x1, y2); |
||
942 | |||
943 | _cairo_gl_composite_emit_vertex (ctx, x2, y1); |
||
944 | _cairo_gl_composite_emit_vertex (ctx, x2, y2); |
||
945 | _cairo_gl_composite_emit_vertex (ctx, x1, y2); |
||
946 | } |
||
947 | |||
948 | cairo_gl_emit_rect_t |
||
949 | _cairo_gl_context_choose_emit_rect (cairo_gl_context_t *ctx) |
||
950 | { |
||
951 | return _cairo_gl_composite_emit_rect; |
||
952 | } |
||
953 | |||
954 | void |
||
955 | _cairo_gl_context_emit_rect (cairo_gl_context_t *ctx, |
||
956 | GLfloat x1, GLfloat y1, |
||
957 | GLfloat x2, GLfloat y2) |
||
958 | { |
||
959 | _cairo_gl_composite_emit_rect (ctx, x1, y1, x2, y2); |
||
960 | } |
||
961 | |||
962 | static void |
||
963 | _cairo_gl_composite_emit_span (cairo_gl_context_t *ctx, |
||
964 | GLfloat x1, GLfloat y1, |
||
965 | GLfloat x2, GLfloat y2, |
||
966 | uint8_t alpha) |
||
967 | { |
||
968 | _cairo_gl_composite_prepare_buffer (ctx, 6, |
||
969 | CAIRO_GL_PRIMITIVE_TYPE_TRIANGLES); |
||
970 | |||
971 | _cairo_gl_composite_emit_alpha_vertex (ctx, x1, y1, alpha); |
||
972 | _cairo_gl_composite_emit_alpha_vertex (ctx, x2, y1, alpha); |
||
973 | _cairo_gl_composite_emit_alpha_vertex (ctx, x1, y2, alpha); |
||
974 | |||
975 | _cairo_gl_composite_emit_alpha_vertex (ctx, x2, y1, alpha); |
||
976 | _cairo_gl_composite_emit_alpha_vertex (ctx, x2, y2, alpha); |
||
977 | _cairo_gl_composite_emit_alpha_vertex (ctx, x1, y2, alpha); |
||
978 | } |
||
979 | |||
980 | static void |
||
981 | _cairo_gl_composite_emit_solid_span (cairo_gl_context_t *ctx, |
||
982 | GLfloat x1, GLfloat y1, |
||
983 | GLfloat x2, GLfloat y2, |
||
984 | uint8_t alpha) |
||
985 | { |
||
986 | GLfloat *v; |
||
987 | union fi { |
||
988 | float f; |
||
989 | GLbyte bytes[4]; |
||
990 | } fi; |
||
991 | |||
992 | _cairo_gl_composite_prepare_buffer (ctx, 6, |
||
993 | CAIRO_GL_PRIMITIVE_TYPE_TRIANGLES); |
||
994 | v = (GLfloat *) (void *) &ctx->vb[ctx->vb_offset]; |
||
995 | |||
996 | v[15] = v[ 6] = v[0] = x1; |
||
997 | v[10] = v[ 4] = v[1] = y1; |
||
998 | v[12] = v[ 9] = v[3] = x2; |
||
999 | v[16] = v[13] = v[7] = y2; |
||
1000 | |||
1001 | fi.bytes[0] = 0; |
||
1002 | fi.bytes[1] = 0; |
||
1003 | fi.bytes[2] = 0; |
||
1004 | fi.bytes[3] = alpha; |
||
1005 | v[17] =v[14] = v[11] = v[8] = v[5] = v[2] = fi.f; |
||
1006 | |||
1007 | ctx->vb_offset += 6*3 * sizeof(GLfloat); |
||
1008 | } |
||
1009 | |||
1010 | cairo_gl_emit_span_t |
||
1011 | _cairo_gl_context_choose_emit_span (cairo_gl_context_t *ctx) |
||
1012 | { |
||
1013 | if (ctx->operands[CAIRO_GL_TEX_MASK].type != CAIRO_GL_OPERAND_NONE) { |
||
1014 | switch (ctx->operands[CAIRO_GL_TEX_MASK].type) { |
||
1015 | default: |
||
1016 | case CAIRO_GL_OPERAND_COUNT: |
||
1017 | ASSERT_NOT_REACHED; |
||
1018 | case CAIRO_GL_OPERAND_NONE: |
||
1019 | case CAIRO_GL_OPERAND_CONSTANT: |
||
1020 | break; |
||
1021 | |||
1022 | case CAIRO_GL_OPERAND_LINEAR_GRADIENT: |
||
1023 | case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0: |
||
1024 | case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE: |
||
1025 | case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT: |
||
1026 | if (!ctx->operands[CAIRO_GL_TEX_MASK].gradient.texgen) |
||
1027 | return _cairo_gl_composite_emit_span; |
||
1028 | break; |
||
1029 | |||
1030 | case CAIRO_GL_OPERAND_TEXTURE: |
||
1031 | if (!ctx->operands[CAIRO_GL_TEX_MASK].texture.texgen) |
||
1032 | return _cairo_gl_composite_emit_span; |
||
1033 | break; |
||
1034 | } |
||
1035 | } |
||
1036 | |||
1037 | switch (ctx->operands[CAIRO_GL_TEX_SOURCE].type) { |
||
1038 | default: |
||
1039 | case CAIRO_GL_OPERAND_COUNT: |
||
1040 | ASSERT_NOT_REACHED; |
||
1041 | case CAIRO_GL_OPERAND_NONE: |
||
1042 | case CAIRO_GL_OPERAND_CONSTANT: |
||
1043 | break; |
||
1044 | |||
1045 | case CAIRO_GL_OPERAND_LINEAR_GRADIENT: |
||
1046 | case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0: |
||
1047 | case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE: |
||
1048 | case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT: |
||
1049 | if (!ctx->operands[CAIRO_GL_TEX_SOURCE].gradient.texgen) |
||
1050 | return _cairo_gl_composite_emit_span; |
||
1051 | break; |
||
1052 | |||
1053 | case CAIRO_GL_OPERAND_TEXTURE: |
||
1054 | if (!ctx->operands[CAIRO_GL_TEX_SOURCE].texture.texgen) |
||
1055 | return _cairo_gl_composite_emit_span; |
||
1056 | } |
||
1057 | |||
1058 | return _cairo_gl_composite_emit_solid_span; |
||
1059 | } |
||
1060 | |||
1061 | static inline void |
||
1062 | _cairo_gl_composite_emit_glyph_vertex (cairo_gl_context_t *ctx, |
||
1063 | GLfloat x, GLfloat y, |
||
1064 | GLfloat glyph_x, GLfloat glyph_y) |
||
1065 | { |
||
1066 | GLfloat *vb = (GLfloat *) (void *) &ctx->vb[ctx->vb_offset]; |
||
1067 | |||
1068 | *vb++ = x; |
||
1069 | *vb++ = y; |
||
1070 | |||
1071 | _cairo_gl_operand_emit (&ctx->operands[CAIRO_GL_TEX_SOURCE], &vb, x, y); |
||
1072 | |||
1073 | *vb++ = glyph_x; |
||
1074 | *vb++ = glyph_y; |
||
1075 | |||
1076 | ctx->vb_offset += ctx->vertex_size; |
||
1077 | } |
||
1078 | |||
1079 | static void |
||
1080 | _cairo_gl_composite_emit_glyph (cairo_gl_context_t *ctx, |
||
1081 | GLfloat x1, GLfloat y1, |
||
1082 | GLfloat x2, GLfloat y2, |
||
1083 | GLfloat glyph_x1, GLfloat glyph_y1, |
||
1084 | GLfloat glyph_x2, GLfloat glyph_y2) |
||
1085 | { |
||
1086 | _cairo_gl_composite_prepare_buffer (ctx, 6, |
||
1087 | CAIRO_GL_PRIMITIVE_TYPE_TRIANGLES); |
||
1088 | |||
1089 | _cairo_gl_composite_emit_glyph_vertex (ctx, x1, y1, glyph_x1, glyph_y1); |
||
1090 | _cairo_gl_composite_emit_glyph_vertex (ctx, x2, y1, glyph_x2, glyph_y1); |
||
1091 | _cairo_gl_composite_emit_glyph_vertex (ctx, x1, y2, glyph_x1, glyph_y2); |
||
1092 | |||
1093 | _cairo_gl_composite_emit_glyph_vertex (ctx, x2, y1, glyph_x2, glyph_y1); |
||
1094 | _cairo_gl_composite_emit_glyph_vertex (ctx, x2, y2, glyph_x2, glyph_y2); |
||
1095 | _cairo_gl_composite_emit_glyph_vertex (ctx, x1, y2, glyph_x1, glyph_y2); |
||
1096 | } |
||
1097 | |||
1098 | static void |
||
1099 | _cairo_gl_composite_emit_solid_glyph (cairo_gl_context_t *ctx, |
||
1100 | GLfloat x1, GLfloat y1, |
||
1101 | GLfloat x2, GLfloat y2, |
||
1102 | GLfloat glyph_x1, GLfloat glyph_y1, |
||
1103 | GLfloat glyph_x2, GLfloat glyph_y2) |
||
1104 | { |
||
1105 | GLfloat *v; |
||
1106 | |||
1107 | _cairo_gl_composite_prepare_buffer (ctx, 6, |
||
1108 | CAIRO_GL_PRIMITIVE_TYPE_TRIANGLES); |
||
1109 | |||
1110 | v = (GLfloat *) (void *) &ctx->vb[ctx->vb_offset]; |
||
1111 | |||
1112 | v[20] = v[ 8] = v[0] = x1; |
||
1113 | v[13] = v[ 5] = v[1] = y1; |
||
1114 | v[22] = v[10] = v[2] = glyph_x1; |
||
1115 | v[15] = v[ 7] = v[3] = glyph_y1; |
||
1116 | |||
1117 | v[16] = v[12] = v[4] = x2; |
||
1118 | v[18] = v[14] = v[6] = glyph_x2; |
||
1119 | |||
1120 | v[21] = v[17] = v[ 9] = y2; |
||
1121 | v[23] = v[19] = v[11] = glyph_y2; |
||
1122 | |||
1123 | ctx->vb_offset += 4 * 6 * sizeof (GLfloat); |
||
1124 | } |
||
1125 | |||
1126 | cairo_gl_emit_glyph_t |
||
1127 | _cairo_gl_context_choose_emit_glyph (cairo_gl_context_t *ctx) |
||
1128 | { |
||
1129 | switch (ctx->operands[CAIRO_GL_TEX_SOURCE].type) { |
||
1130 | default: |
||
1131 | case CAIRO_GL_OPERAND_COUNT: |
||
1132 | ASSERT_NOT_REACHED; |
||
1133 | case CAIRO_GL_OPERAND_NONE: |
||
1134 | case CAIRO_GL_OPERAND_CONSTANT: |
||
1135 | return _cairo_gl_composite_emit_solid_glyph; |
||
1136 | |||
1137 | case CAIRO_GL_OPERAND_LINEAR_GRADIENT: |
||
1138 | case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0: |
||
1139 | case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE: |
||
1140 | case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT: |
||
1141 | case CAIRO_GL_OPERAND_TEXTURE: |
||
1142 | return _cairo_gl_composite_emit_glyph; |
||
1143 | } |
||
1144 | } |
||
1145 | |||
1146 | void |
||
1147 | _cairo_gl_composite_fini (cairo_gl_composite_t *setup) |
||
1148 | { |
||
1149 | _cairo_gl_operand_destroy (&setup->src); |
||
1150 | _cairo_gl_operand_destroy (&setup->mask); |
||
1151 | } |
||
1152 | |||
1153 | cairo_status_t |
||
1154 | _cairo_gl_composite_set_operator (cairo_gl_composite_t *setup, |
||
1155 | cairo_operator_t op, |
||
1156 | cairo_bool_t assume_component_alpha) |
||
1157 | { |
||
1158 | if (assume_component_alpha) { |
||
1159 | if (op != CAIRO_OPERATOR_CLEAR && |
||
1160 | op != CAIRO_OPERATOR_OVER && |
||
1161 | op != CAIRO_OPERATOR_ADD) |
||
1162 | return UNSUPPORTED ("unsupported component alpha operator"); |
||
1163 | } else { |
||
1164 | if (! _cairo_gl_operator_is_supported (op)) |
||
1165 | return UNSUPPORTED ("unsupported operator"); |
||
1166 | } |
||
1167 | |||
1168 | setup->op = op; |
||
1169 | return CAIRO_STATUS_SUCCESS; |
||
1170 | } |
||
1171 | |||
1172 | cairo_status_t |
||
1173 | _cairo_gl_composite_init (cairo_gl_composite_t *setup, |
||
1174 | cairo_operator_t op, |
||
1175 | cairo_gl_surface_t *dst, |
||
1176 | cairo_bool_t assume_component_alpha) |
||
1177 | { |
||
1178 | cairo_status_t status; |
||
1179 | |||
1180 | memset (setup, 0, sizeof (cairo_gl_composite_t)); |
||
1181 | |||
1182 | status = _cairo_gl_composite_set_operator (setup, op, |
||
1183 | assume_component_alpha); |
||
1184 | if (status) |
||
1185 | return status; |
||
1186 | |||
1187 | setup->dst = dst; |
||
1188 | setup->clip_region = dst->clip_region; |
||
1189 | |||
1190 | return CAIRO_STATUS_SUCCESS; |
||
1191 | } |
||
1192 | |||
1193 | static cairo_int_status_t |
||
1194 | _cairo_gl_composite_append_vertex_indices (cairo_gl_context_t *ctx, |
||
1195 | int number_of_new_indices) |
||
1196 | { |
||
1197 | cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS; |
||
1198 | cairo_array_t *indices = &ctx->tristrip_indices; |
||
1199 | int number_of_indices = _cairo_array_num_elements (indices); |
||
1200 | unsigned short current_vertex_index = 0; |
||
1201 | int i; |
||
1202 | |||
1203 | assert (number_of_new_indices > 0); |
||
1204 | |||
1205 | /* If any preexisting triangle triangle strip indices exist on this |
||
1206 | context, we insert a set of degenerate triangles from the last |
||
1207 | preexisting vertex to our first one. */ |
||
1208 | if (number_of_indices > 0) { |
||
1209 | const unsigned short *indices_array = _cairo_array_index_const (indices, 0); |
||
1210 | current_vertex_index = indices_array[number_of_indices - 1]; |
||
1211 | |||
1212 | status = _cairo_array_append (indices, ¤t_vertex_index); |
||
1213 | if (unlikely (status)) |
||
1214 | return status; |
||
1215 | |||
1216 | current_vertex_index++; |
||
1217 | status =_cairo_array_append (indices, ¤t_vertex_index); |
||
1218 | if (unlikely (status)) |
||
1219 | return status; |
||
1220 | } |
||
1221 | |||
1222 | for (i = 0; i < number_of_new_indices; i++) { |
||
1223 | status = _cairo_array_append (indices, ¤t_vertex_index); |
||
1224 | current_vertex_index++; |
||
1225 | if (unlikely (status)) |
||
1226 | return status; |
||
1227 | } |
||
1228 | |||
1229 | return CAIRO_STATUS_SUCCESS; |
||
1230 | } |
||
1231 | |||
1232 | cairo_int_status_t |
||
1233 | _cairo_gl_composite_emit_quad_as_tristrip (cairo_gl_context_t *ctx, |
||
1234 | cairo_gl_composite_t *setup, |
||
1235 | const cairo_point_t quad[4]) |
||
1236 | { |
||
1237 | _cairo_gl_composite_prepare_buffer (ctx, 4, |
||
1238 | CAIRO_GL_PRIMITIVE_TYPE_TRISTRIPS); |
||
1239 | |||
1240 | _cairo_gl_composite_emit_point (ctx, &quad[0]); |
||
1241 | _cairo_gl_composite_emit_point (ctx, &quad[1]); |
||
1242 | |||
1243 | /* Cairo stores quad vertices in counter-clockwise order, but we need to |
||
1244 | emit them from top to bottom in the triangle strip, so we need to reverse |
||
1245 | the order of the last two vertices. */ |
||
1246 | _cairo_gl_composite_emit_point (ctx, &quad[3]); |
||
1247 | _cairo_gl_composite_emit_point (ctx, &quad[2]); |
||
1248 | |||
1249 | return _cairo_gl_composite_append_vertex_indices (ctx, 4); |
||
1250 | } |
||
1251 | |||
1252 | cairo_int_status_t |
||
1253 | _cairo_gl_composite_emit_triangle_as_tristrip (cairo_gl_context_t *ctx, |
||
1254 | cairo_gl_composite_t *setup, |
||
1255 | const cairo_point_t triangle[3]) |
||
1256 | { |
||
1257 | _cairo_gl_composite_prepare_buffer (ctx, 3, |
||
1258 | CAIRO_GL_PRIMITIVE_TYPE_TRISTRIPS); |
||
1259 | |||
1260 | _cairo_gl_composite_emit_point (ctx, &triangle[0]); |
||
1261 | _cairo_gl_composite_emit_point (ctx, &triangle[1]); |
||
1262 | _cairo_gl_composite_emit_point (ctx, &triangle[2]); |
||
1263 | return _cairo_gl_composite_append_vertex_indices (ctx, 3); |
||
1264 | }>>>> |