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 Intel Corporation
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it either under the terms of the GNU Lesser General Public
10  * License version 2.1 as published by the Free Software Foundation
11  * (the "LGPL") or, at your option, under the terms of the Mozilla
12  * Public License Version 1.1 (the "MPL"). If you do not alter this
13  * notice, a recipient may use your version of this file under either
14  * the MPL or the LGPL.
15  *
16  * You should have received a copy of the LGPL along with this library
17  * in the file COPYING-LGPL-2.1; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
19  * You should have received a copy of the MPL along with this library
20  * in the file COPYING-MPL-1.1
21  *
22  * The contents of this file are subject to the Mozilla Public License
23  * Version 1.1 (the "License"); you may not use this file except in
24  * compliance with the License. You may obtain a copy of the License at
25  * http://www.mozilla.org/MPL/
26  *
27  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
28  * OF ANY KIND, either express or implied. See the LGPL or the MPL for
29  * the specific language governing rights and limitations.
30  *
31  * The Original Code is the cairo graphics library.
32  *
33  * The Initial Developer of the Original Code is Red Hat, Inc.
34  *
35  * Contributor(s):
36  *	Benjamin Otte <otte@gnome.org>
37  *	Carl Worth <cworth@cworth.org>
38  *	Chris Wilson <chris@chris-wilson.co.uk>
39  *	Eric Anholt <eric@anholt.net>
40  */
41 
42 #include "cairoint.h"
43 
44 #include "cairo-gl-private.h"
45 
46 #include "cairo-composite-rectangles-private.h"
47 #include "cairo-compositor-private.h"
48 #include "cairo-default-context-private.h"
49 #include "cairo-error-private.h"
50 #include "cairo-image-surface-private.h"
51 #include "cairo-surface-backend-private.h"
52 #include "cairo-surface-offset-private.h"
53 #include "cairo-surface-subsurface-inline.h"
54 
55 static cairo_int_status_t
_cairo_gl_create_gradient_texture(cairo_gl_surface_t * dst,const cairo_gradient_pattern_t * pattern,cairo_gl_gradient_t ** gradient)56 _cairo_gl_create_gradient_texture (cairo_gl_surface_t *dst,
57 				   const cairo_gradient_pattern_t *pattern,
58 				   cairo_gl_gradient_t **gradient)
59 {
60     cairo_gl_context_t *ctx;
61     cairo_status_t status;
62 
63     status = _cairo_gl_context_acquire (dst->base.device, &ctx);
64     if (unlikely (status))
65 	return status;
66 
67     status = _cairo_gl_gradient_create (ctx, pattern->n_stops, pattern->stops, gradient);
68 
69     return _cairo_gl_context_release (ctx, status);
70 }
71 
72 static cairo_status_t
_cairo_gl_subsurface_clone_operand_init(cairo_gl_operand_t * operand,const cairo_pattern_t * _src,cairo_gl_surface_t * dst,const cairo_rectangle_int_t * sample,const cairo_rectangle_int_t * extents,cairo_bool_t use_texgen)73 _cairo_gl_subsurface_clone_operand_init (cairo_gl_operand_t *operand,
74 					 const cairo_pattern_t *_src,
75 					 cairo_gl_surface_t *dst,
76 					 const cairo_rectangle_int_t *sample,
77 					 const cairo_rectangle_int_t *extents,
78 					 cairo_bool_t use_texgen)
79 {
80     const cairo_surface_pattern_t *src = (cairo_surface_pattern_t *)_src;
81     cairo_surface_pattern_t local_pattern;
82     cairo_surface_subsurface_t *sub;
83     cairo_gl_surface_t *surface;
84     cairo_gl_context_t *ctx;
85     cairo_surface_attributes_t *attributes;
86     cairo_status_t status;
87 
88     sub = (cairo_surface_subsurface_t *) src->surface;
89 
90     if (sub->snapshot &&
91 	sub->snapshot->type == CAIRO_SURFACE_TYPE_GL &&
92 	sub->snapshot->device == dst->base.device)
93     {
94 	surface = (cairo_gl_surface_t *)
95 	    cairo_surface_reference (sub->snapshot);
96     }
97     else
98     {
99 	status = _cairo_gl_context_acquire (dst->base.device, &ctx);
100 	if (unlikely (status))
101 	    return status;
102 
103 	/* XXX Trim surface to the sample area within the subsurface? */
104 	surface = (cairo_gl_surface_t *)
105 	    _cairo_gl_surface_create_scratch (ctx,
106 					      sub->target->content,
107 					      sub->extents.width,
108 					      sub->extents.height);
109 	if (surface->base.status)
110 	    return _cairo_gl_context_release (ctx, surface->base.status);
111 
112 	_cairo_pattern_init_for_surface (&local_pattern, sub->target);
113 	cairo_matrix_init_translate (&local_pattern.base.matrix,
114 				     sub->extents.x, sub->extents.y);
115 	local_pattern.base.filter = CAIRO_FILTER_NEAREST;
116 	status = _cairo_surface_paint (&surface->base,
117 				       CAIRO_OPERATOR_SOURCE,
118 				       &local_pattern.base,
119 				       NULL);
120 	_cairo_pattern_fini (&local_pattern.base);
121 
122 	status = _cairo_gl_context_release (ctx, status);
123 	if (unlikely (status)) {
124 	    cairo_surface_destroy (&surface->base);
125 	    return status;
126 	}
127 
128 	_cairo_surface_subsurface_set_snapshot (&sub->base, &surface->base);
129     }
130 
131     status = _cairo_gl_surface_resolve_multisampling (surface);
132     if (unlikely (status))
133 	return status;
134 
135     attributes = &operand->texture.attributes;
136 
137     operand->type = CAIRO_GL_OPERAND_TEXTURE;
138     operand->texture.surface = surface;
139     operand->texture.owns_surface = surface;
140     operand->texture.tex = surface->tex;
141 
142     if (_cairo_gl_device_requires_power_of_two_textures (dst->base.device)) {
143 	attributes->matrix = src->base.matrix;
144     } else {
145 	cairo_matrix_t m;
146 
147 	cairo_matrix_init_scale (&m,
148 				 1.0 / surface->width,
149 				 1.0 / surface->height);
150 	cairo_matrix_multiply (&attributes->matrix, &src->base.matrix, &m);
151     }
152 
153     attributes->extend = src->base.extend;
154     attributes->filter = src->base.filter;
155     attributes->has_component_alpha = src->base.has_component_alpha;
156 
157     operand->texture.texgen = use_texgen;
158     return CAIRO_STATUS_SUCCESS;
159 }
160 
161 static cairo_status_t
_cairo_gl_subsurface_operand_init(cairo_gl_operand_t * operand,const cairo_pattern_t * _src,cairo_gl_surface_t * dst,const cairo_rectangle_int_t * sample,const cairo_rectangle_int_t * extents,cairo_bool_t use_texgen)162 _cairo_gl_subsurface_operand_init (cairo_gl_operand_t *operand,
163 				   const cairo_pattern_t *_src,
164 				   cairo_gl_surface_t *dst,
165 				   const cairo_rectangle_int_t *sample,
166 				   const cairo_rectangle_int_t *extents,
167 				   cairo_bool_t use_texgen)
168 {
169     const cairo_surface_pattern_t *src = (cairo_surface_pattern_t *)_src;
170     cairo_surface_subsurface_t *sub;
171     cairo_gl_surface_t *surface;
172     cairo_surface_attributes_t *attributes;
173     cairo_int_status_t status;
174 
175     sub = (cairo_surface_subsurface_t *) src->surface;
176 
177     if (sample->x < 0 || sample->y < 0 ||
178 	sample->x + sample->width  > sub->extents.width ||
179 	sample->y + sample->height > sub->extents.height)
180     {
181 	return _cairo_gl_subsurface_clone_operand_init (operand, _src,
182 							dst, sample, extents,
183 							use_texgen);
184     }
185 
186     surface = (cairo_gl_surface_t *) sub->target;
187     if (surface->base.device && surface->base.device != dst->base.device)
188 	return CAIRO_INT_STATUS_UNSUPPORTED;
189 
190     if (! _cairo_gl_surface_is_texture (surface))
191 	return CAIRO_INT_STATUS_UNSUPPORTED;
192 
193     status = _cairo_gl_surface_resolve_multisampling (surface);
194     if (unlikely (status))
195 	return status;
196 
197     /* Translate the matrix from
198      * (unnormalized src -> unnormalized src) to
199      * (unnormalized dst -> unnormalized src)
200      */
201     _cairo_gl_operand_copy(operand, &surface->operand);
202 
203     attributes = &operand->texture.attributes;
204     attributes->matrix = src->base.matrix;
205     attributes->matrix.x0 += sub->extents.x;
206     attributes->matrix.y0 += sub->extents.y;
207     cairo_matrix_multiply (&attributes->matrix,
208 			   &attributes->matrix,
209 			   &surface->operand.texture.attributes.matrix);
210 
211     attributes->extend = src->base.extend;
212     attributes->filter = src->base.filter;
213     attributes->has_component_alpha = src->base.has_component_alpha;
214 
215     operand->texture.texgen = use_texgen;
216     return CAIRO_STATUS_SUCCESS;
217 }
218 
219 static cairo_status_t
_cairo_gl_surface_operand_init(cairo_gl_operand_t * operand,const cairo_pattern_t * _src,cairo_gl_surface_t * dst,const cairo_rectangle_int_t * sample,const cairo_rectangle_int_t * extents,cairo_bool_t use_texgen)220 _cairo_gl_surface_operand_init (cairo_gl_operand_t *operand,
221 				const cairo_pattern_t *_src,
222 				cairo_gl_surface_t *dst,
223 				const cairo_rectangle_int_t *sample,
224 				const cairo_rectangle_int_t *extents,
225 				cairo_bool_t use_texgen)
226 {
227     const cairo_surface_pattern_t *src = (cairo_surface_pattern_t *)_src;
228     cairo_gl_surface_t *surface;
229     cairo_surface_attributes_t *attributes;
230     cairo_int_status_t status;
231 
232     surface = (cairo_gl_surface_t *) src->surface;
233     if (surface->base.type != CAIRO_SURFACE_TYPE_GL)
234 	return CAIRO_INT_STATUS_UNSUPPORTED;
235 
236     if (surface->base.backend->type != CAIRO_SURFACE_TYPE_GL) {
237 	if (_cairo_surface_is_subsurface (&surface->base))
238 	    return _cairo_gl_subsurface_operand_init (operand, _src, dst,
239 						      sample, extents,
240 						      use_texgen);
241 
242 	return CAIRO_INT_STATUS_UNSUPPORTED;
243     }
244 
245     if (surface->base.device && surface->base.device != dst->base.device)
246 	return CAIRO_INT_STATUS_UNSUPPORTED;
247 
248     if (surface->base.device && ! _cairo_gl_surface_is_texture (surface))
249 	return CAIRO_INT_STATUS_UNSUPPORTED;
250 
251     status = _cairo_gl_surface_resolve_multisampling (surface);
252     if (unlikely (status))
253 	return status;
254 
255     _cairo_gl_operand_copy(operand, &surface->operand);
256 
257     attributes = &operand->texture.attributes;
258     cairo_matrix_multiply (&attributes->matrix,
259 			   &src->base.matrix,
260 			   &attributes->matrix);
261 
262     attributes->extend = src->base.extend;
263     attributes->filter = src->base.filter;
264     attributes->has_component_alpha = src->base.has_component_alpha;
265 
266     operand->texture.texgen = use_texgen;
267     return CAIRO_STATUS_SUCCESS;
268 }
269 
270 static cairo_status_t
_cairo_gl_pattern_texture_setup(cairo_gl_operand_t * operand,const cairo_pattern_t * _src,cairo_gl_surface_t * dst,const cairo_rectangle_int_t * extents)271 _cairo_gl_pattern_texture_setup (cairo_gl_operand_t *operand,
272 				 const cairo_pattern_t *_src,
273 				 cairo_gl_surface_t *dst,
274 				 const cairo_rectangle_int_t *extents)
275 {
276     cairo_status_t status;
277     cairo_gl_surface_t *surface;
278     cairo_gl_context_t *ctx;
279     cairo_image_surface_t *image;
280     cairo_bool_t src_is_gl_surface = FALSE;
281     cairo_rectangle_int_t map_extents;
282 
283     if (_src->type == CAIRO_PATTERN_TYPE_SURFACE) {
284 	cairo_surface_t* src_surface = ((cairo_surface_pattern_t *) _src)->surface;
285 	src_is_gl_surface = src_surface->type == CAIRO_SURFACE_TYPE_GL;
286     }
287 
288     status = _cairo_gl_context_acquire (dst->base.device, &ctx);
289     if (unlikely (status))
290 	return status;
291 
292     surface = (cairo_gl_surface_t *)
293 	_cairo_gl_surface_create_scratch (ctx,
294 					  CAIRO_CONTENT_COLOR_ALPHA,
295 					  extents->width, extents->height);
296     map_extents = *extents;
297     map_extents.x = map_extents.y = 0;
298     image = _cairo_surface_map_to_image (&surface->base, &map_extents);
299 
300     /* If the pattern is a GL surface, it belongs to some other GL context,
301        so we need to release this device while we paint it to the image. */
302     if (src_is_gl_surface) {
303 	status = _cairo_gl_context_release (ctx, status);
304 	if (unlikely (status)) {
305 	    _cairo_surface_unmap_image (&surface->base, image);
306 	    goto fail;
307 	}
308     }
309 
310     status = _cairo_surface_offset_paint (&image->base, extents->x, extents->y,
311 					  CAIRO_OPERATOR_SOURCE, _src, NULL);
312 
313     if (src_is_gl_surface) {
314 	status = _cairo_gl_context_acquire (dst->base.device, &ctx);
315 	if (unlikely (status)) {
316 	    _cairo_surface_unmap_image (&surface->base, image);
317 	    goto fail;
318 	}
319     }
320 
321     status = _cairo_surface_unmap_image (&surface->base, image);
322     status = _cairo_gl_context_release (ctx, status);
323     if (unlikely (status))
324 	goto fail;
325 
326     *operand = surface->operand;
327     operand->texture.owns_surface = surface;
328     operand->texture.attributes.matrix.x0 -= extents->x * operand->texture.attributes.matrix.xx;
329     operand->texture.attributes.matrix.y0 -= extents->y * operand->texture.attributes.matrix.yy;
330     return CAIRO_STATUS_SUCCESS;
331 
332 fail:
333     cairo_surface_destroy (&surface->base);
334     return status;
335 }
336 
337 void
_cairo_gl_solid_operand_init(cairo_gl_operand_t * operand,const cairo_color_t * color)338 _cairo_gl_solid_operand_init (cairo_gl_operand_t *operand,
339 			      const cairo_color_t *color)
340 {
341     operand->type = CAIRO_GL_OPERAND_CONSTANT;
342     operand->constant.color[0] = color->red   * color->alpha;
343     operand->constant.color[1] = color->green * color->alpha;
344     operand->constant.color[2] = color->blue  * color->alpha;
345     operand->constant.color[3] = color->alpha;
346 }
347 
348 void
_cairo_gl_operand_translate(cairo_gl_operand_t * operand,double tx,double ty)349 _cairo_gl_operand_translate (cairo_gl_operand_t *operand,
350 			     double tx, double ty)
351 {
352     switch (operand->type) {
353     case CAIRO_GL_OPERAND_TEXTURE:
354 	operand->texture.attributes.matrix.x0 -= tx * operand->texture.attributes.matrix.xx;
355 	operand->texture.attributes.matrix.y0 -= ty * operand->texture.attributes.matrix.yy;
356 	break;
357 
358     case CAIRO_GL_OPERAND_LINEAR_GRADIENT:
359     case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0:
360     case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE:
361     case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT:
362 	operand->gradient.m.x0 -= tx * operand->gradient.m.xx;
363 	operand->gradient.m.y0 -= ty * operand->gradient.m.yy;
364 	break;
365 
366     case CAIRO_GL_OPERAND_NONE:
367     case CAIRO_GL_OPERAND_CONSTANT:
368     case CAIRO_GL_OPERAND_COUNT:
369     default:
370 	break;
371     }
372 }
373 
374 static cairo_status_t
_cairo_gl_gradient_operand_init(cairo_gl_operand_t * operand,const cairo_pattern_t * pattern,cairo_gl_surface_t * dst,cairo_bool_t use_texgen)375 _cairo_gl_gradient_operand_init (cairo_gl_operand_t *operand,
376 				 const cairo_pattern_t *pattern,
377 				 cairo_gl_surface_t *dst,
378 				 cairo_bool_t use_texgen)
379 {
380     const cairo_gradient_pattern_t *gradient = (const cairo_gradient_pattern_t *)pattern;
381     cairo_status_t status;
382 
383     assert (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR ||
384 	    gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL);
385 
386     if (! _cairo_gl_device_has_glsl (dst->base.device))
387 	return CAIRO_INT_STATUS_UNSUPPORTED;
388 
389     status = _cairo_gl_create_gradient_texture (dst,
390 						gradient,
391 						&operand->gradient.gradient);
392     if (unlikely (status))
393 	return status;
394 
395     if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
396 	cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient;
397 	double x0, y0, dx, dy, sf, offset;
398 
399 	dx = linear->pd2.x - linear->pd1.x;
400 	dy = linear->pd2.y - linear->pd1.y;
401 	sf = 1.0 / (dx * dx + dy * dy);
402 	dx *= sf;
403 	dy *= sf;
404 
405 	x0 = linear->pd1.x;
406 	y0 = linear->pd1.y;
407 	offset = dx * x0 + dy * y0;
408 
409 	operand->type = CAIRO_GL_OPERAND_LINEAR_GRADIENT;
410 
411 	cairo_matrix_init (&operand->gradient.m, dx, 0, dy, 1, -offset, 0);
412 	if (! _cairo_matrix_is_identity (&pattern->matrix)) {
413 	    cairo_matrix_multiply (&operand->gradient.m,
414 				   &pattern->matrix,
415 				   &operand->gradient.m);
416 	}
417     } else {
418 	cairo_matrix_t m;
419 	cairo_circle_double_t circles[2];
420 	double x0, y0, r0, dx, dy, dr;
421 
422 	/*
423 	 * Some fragment shader implementations use half-floats to
424 	 * represent numbers, so the maximum number they can represent
425 	 * is about 2^14. Some intermediate computations used in the
426 	 * radial gradient shaders can produce results of up to 2*k^4.
427 	 * Setting k=8 makes the maximum result about 8192 (assuming
428 	 * that the extreme circles are not much smaller than the
429 	 * destination image).
430 	 */
431 	_cairo_gradient_pattern_fit_to_range (gradient, 8.,
432 					      &operand->gradient.m, circles);
433 
434 	x0 = circles[0].center.x;
435 	y0 = circles[0].center.y;
436 	r0 = circles[0].radius;
437 	dx = circles[1].center.x - x0;
438 	dy = circles[1].center.y - y0;
439 	dr = circles[1].radius   - r0;
440 
441 	operand->gradient.a = dx * dx + dy * dy - dr * dr;
442 	operand->gradient.radius_0 = r0;
443 	operand->gradient.circle_d.center.x = dx;
444 	operand->gradient.circle_d.center.y = dy;
445 	operand->gradient.circle_d.radius   = dr;
446 
447 	if (operand->gradient.a == 0)
448 	    operand->type = CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0;
449 	else if (pattern->extend == CAIRO_EXTEND_NONE)
450 	    operand->type = CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE;
451 	else
452 	    operand->type = CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT;
453 
454 	cairo_matrix_init_translate (&m, -x0, -y0);
455 	cairo_matrix_multiply (&operand->gradient.m,
456 			       &operand->gradient.m,
457 			       &m);
458     }
459 
460     operand->gradient.extend = pattern->extend;
461     operand->gradient.texgen = use_texgen;
462 
463     return CAIRO_STATUS_SUCCESS;
464 }
465 
466 void
_cairo_gl_operand_copy(cairo_gl_operand_t * dst,const cairo_gl_operand_t * src)467 _cairo_gl_operand_copy (cairo_gl_operand_t *dst,
468 			const cairo_gl_operand_t *src)
469 {
470     *dst = *src;
471     switch (dst->type) {
472     case CAIRO_GL_OPERAND_CONSTANT:
473 	break;
474     case CAIRO_GL_OPERAND_LINEAR_GRADIENT:
475     case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0:
476     case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE:
477     case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT:
478 	_cairo_gl_gradient_reference (dst->gradient.gradient);
479 	break;
480     case CAIRO_GL_OPERAND_TEXTURE:
481 	cairo_surface_reference (&dst->texture.owns_surface->base);
482 	break;
483     default:
484     case CAIRO_GL_OPERAND_COUNT:
485 	ASSERT_NOT_REACHED;
486     case CAIRO_GL_OPERAND_NONE:
487 	break;
488     }
489 }
490 
491 void
_cairo_gl_operand_destroy(cairo_gl_operand_t * operand)492 _cairo_gl_operand_destroy (cairo_gl_operand_t *operand)
493 {
494     switch (operand->type) {
495     case CAIRO_GL_OPERAND_CONSTANT:
496 	break;
497     case CAIRO_GL_OPERAND_LINEAR_GRADIENT:
498     case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0:
499     case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE:
500     case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT:
501 	_cairo_gl_gradient_destroy (operand->gradient.gradient);
502 	break;
503     case CAIRO_GL_OPERAND_TEXTURE:
504 	cairo_surface_destroy (&operand->texture.owns_surface->base);
505 	break;
506     default:
507     case CAIRO_GL_OPERAND_COUNT:
508 	ASSERT_NOT_REACHED;
509     case CAIRO_GL_OPERAND_NONE:
510 	break;
511     }
512 
513     operand->type = CAIRO_GL_OPERAND_NONE;
514 }
515 
516 cairo_int_status_t
_cairo_gl_operand_init(cairo_gl_operand_t * operand,const cairo_pattern_t * pattern,cairo_gl_surface_t * dst,const cairo_rectangle_int_t * sample,const cairo_rectangle_int_t * extents,cairo_bool_t use_texgen)517 _cairo_gl_operand_init (cairo_gl_operand_t *operand,
518 			const cairo_pattern_t *pattern,
519 			cairo_gl_surface_t *dst,
520 			const cairo_rectangle_int_t *sample,
521 			const cairo_rectangle_int_t *extents,
522 			cairo_bool_t use_texgen)
523 {
524     cairo_int_status_t status;
525 
526     TRACE ((stderr, "%s: type=%d\n", __FUNCTION__, pattern->type));
527     switch (pattern->type) {
528     case CAIRO_PATTERN_TYPE_SOLID:
529 	_cairo_gl_solid_operand_init (operand,
530 				      &((cairo_solid_pattern_t *) pattern)->color);
531 	return CAIRO_STATUS_SUCCESS;
532     case CAIRO_PATTERN_TYPE_SURFACE:
533 	status = _cairo_gl_surface_operand_init (operand, pattern, dst,
534 						 sample, extents, use_texgen);
535 	if (status == CAIRO_INT_STATUS_UNSUPPORTED)
536 	    break;
537 
538 	return status;
539 
540     case CAIRO_PATTERN_TYPE_LINEAR:
541     case CAIRO_PATTERN_TYPE_RADIAL:
542 	status = _cairo_gl_gradient_operand_init (operand, pattern, dst,
543 						  use_texgen);
544 	if (status == CAIRO_INT_STATUS_UNSUPPORTED)
545 	    break;
546 
547 	return status;
548 
549     default:
550     case CAIRO_PATTERN_TYPE_MESH:
551     case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
552 	break;
553     }
554 
555     return _cairo_gl_pattern_texture_setup (operand, pattern, dst, extents);
556 }
557 
558 cairo_filter_t
_cairo_gl_operand_get_filter(cairo_gl_operand_t * operand)559 _cairo_gl_operand_get_filter (cairo_gl_operand_t *operand)
560 {
561     cairo_filter_t filter;
562 
563     switch ((int) operand->type) {
564     case CAIRO_GL_OPERAND_TEXTURE:
565 	filter = operand->texture.attributes.filter;
566 	break;
567     case CAIRO_GL_OPERAND_LINEAR_GRADIENT:
568     case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0:
569     case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE:
570     case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT:
571 	filter = CAIRO_FILTER_BILINEAR;
572 	break;
573     default:
574 	filter = CAIRO_FILTER_DEFAULT;
575 	break;
576     }
577 
578     return filter;
579 }
580 
581 GLint
_cairo_gl_operand_get_gl_filter(cairo_gl_operand_t * operand)582 _cairo_gl_operand_get_gl_filter (cairo_gl_operand_t *operand)
583 {
584     cairo_filter_t filter = _cairo_gl_operand_get_filter (operand);
585 
586     return filter != CAIRO_FILTER_FAST && filter != CAIRO_FILTER_NEAREST ?
587 	   GL_LINEAR :
588 	   GL_NEAREST;
589 }
590 
591 cairo_extend_t
_cairo_gl_operand_get_extend(cairo_gl_operand_t * operand)592 _cairo_gl_operand_get_extend (cairo_gl_operand_t *operand)
593 {
594     cairo_extend_t extend;
595 
596     switch ((int) operand->type) {
597     case CAIRO_GL_OPERAND_TEXTURE:
598 	extend = operand->texture.attributes.extend;
599 	break;
600     case CAIRO_GL_OPERAND_LINEAR_GRADIENT:
601     case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0:
602     case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE:
603     case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT:
604 	extend = operand->gradient.extend;
605 	break;
606     default:
607 	extend = CAIRO_EXTEND_NONE;
608 	break;
609     }
610 
611     return extend;
612 }
613 
614 
615 void
_cairo_gl_operand_bind_to_shader(cairo_gl_context_t * ctx,cairo_gl_operand_t * operand,cairo_gl_tex_t tex_unit)616 _cairo_gl_operand_bind_to_shader (cairo_gl_context_t *ctx,
617 				  cairo_gl_operand_t *operand,
618 				  cairo_gl_tex_t      tex_unit)
619 {
620     const cairo_matrix_t *texgen = NULL;
621 
622     switch (operand->type) {
623     default:
624     case CAIRO_GL_OPERAND_COUNT:
625 	ASSERT_NOT_REACHED;
626     case CAIRO_GL_OPERAND_NONE:
627 	return;
628 
629     case CAIRO_GL_OPERAND_CONSTANT:
630 	_cairo_gl_shader_bind_vec4 (ctx,
631 				    ctx->current_shader->constant_location[tex_unit],
632 				    operand->constant.color[0],
633 				    operand->constant.color[1],
634 				    operand->constant.color[2],
635 				    operand->constant.color[3]);
636 	return;
637 
638     case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE:
639     case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT:
640 	_cairo_gl_shader_bind_float  (ctx,
641 				      ctx->current_shader->a_location[tex_unit],
642 				      operand->gradient.a);
643 	/* fall through */
644     case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0:
645 	_cairo_gl_shader_bind_vec3   (ctx,
646 				      ctx->current_shader->circle_d_location[tex_unit],
647 				      operand->gradient.circle_d.center.x,
648 				      operand->gradient.circle_d.center.y,
649 				      operand->gradient.circle_d.radius);
650 	_cairo_gl_shader_bind_float  (ctx,
651 				      ctx->current_shader->radius_0_location[tex_unit],
652 				      operand->gradient.radius_0);
653 	/* fall through */
654     case CAIRO_GL_OPERAND_LINEAR_GRADIENT:
655     case CAIRO_GL_OPERAND_TEXTURE:
656 	/*
657 	 * For GLES2 we use shaders to implement GL_CLAMP_TO_BORDER (used
658 	 * with CAIRO_EXTEND_NONE). When bilinear filtering is enabled,
659 	 * these shaders need the texture dimensions for their calculations.
660 	 */
661 	if ((ctx->gl_flavor == CAIRO_GL_FLAVOR_ES3 ||
662 	     ctx->gl_flavor == CAIRO_GL_FLAVOR_ES2) &&
663 	    _cairo_gl_operand_get_extend (operand) == CAIRO_EXTEND_NONE &&
664 	    _cairo_gl_operand_get_gl_filter (operand) == GL_LINEAR)
665 	{
666 	    float width, height;
667 	    if (operand->type == CAIRO_GL_OPERAND_TEXTURE) {
668 		width = operand->texture.surface->width;
669 		height = operand->texture.surface->height;
670 	    }
671 	    else {
672 		width = operand->gradient.gradient->cache_entry.size,
673 		height = 1;
674 	    }
675 	    _cairo_gl_shader_bind_vec2 (ctx,
676 					ctx->current_shader->texdims_location[tex_unit],
677 					width, height);
678 	}
679 	break;
680     }
681 
682     if (operand->type == CAIRO_GL_OPERAND_TEXTURE) {
683 	    if (operand->texture.texgen)
684 		    texgen = &operand->texture.attributes.matrix;
685     } else {
686 	    if (operand->gradient.texgen)
687 		    texgen = &operand->gradient.m;
688     }
689     if (texgen) {
690 	    _cairo_gl_shader_bind_matrix(ctx,
691 					 ctx->current_shader->texgen_location[tex_unit],
692 					 texgen);
693     }
694 }
695 
696 
697 cairo_bool_t
_cairo_gl_operand_needs_setup(cairo_gl_operand_t * dest,cairo_gl_operand_t * source,unsigned int vertex_offset)698 _cairo_gl_operand_needs_setup (cairo_gl_operand_t *dest,
699 			       cairo_gl_operand_t *source,
700 			       unsigned int        vertex_offset)
701 {
702     if (dest->type != source->type)
703 	return TRUE;
704     if (dest->vertex_offset != vertex_offset)
705 	return TRUE;
706 
707     switch (source->type) {
708     case CAIRO_GL_OPERAND_NONE:
709 	return FALSE;
710     case CAIRO_GL_OPERAND_CONSTANT:
711 	return dest->constant.color[0] != source->constant.color[0] ||
712 	       dest->constant.color[1] != source->constant.color[1] ||
713 	       dest->constant.color[2] != source->constant.color[2] ||
714 	       dest->constant.color[3] != source->constant.color[3];
715     case CAIRO_GL_OPERAND_TEXTURE:
716 	return dest->texture.surface != source->texture.surface ||
717 	       dest->texture.attributes.extend != source->texture.attributes.extend ||
718 	       dest->texture.attributes.filter != source->texture.attributes.filter ||
719 	       dest->texture.attributes.has_component_alpha != source->texture.attributes.has_component_alpha;
720     case CAIRO_GL_OPERAND_LINEAR_GRADIENT:
721     case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0:
722     case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE:
723     case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT:
724 	/* XXX: improve this */
725 	return TRUE;
726     default:
727     case CAIRO_GL_OPERAND_COUNT:
728 	ASSERT_NOT_REACHED;
729 	break;
730     }
731     return TRUE;
732 }
733 
734 unsigned int
_cairo_gl_operand_get_vertex_size(const cairo_gl_operand_t * operand)735 _cairo_gl_operand_get_vertex_size (const cairo_gl_operand_t *operand)
736 {
737     switch (operand->type) {
738     default:
739     case CAIRO_GL_OPERAND_COUNT:
740 	ASSERT_NOT_REACHED;
741     case CAIRO_GL_OPERAND_NONE:
742     case CAIRO_GL_OPERAND_CONSTANT:
743 	return 0;
744     case CAIRO_GL_OPERAND_TEXTURE:
745 	return operand->texture.texgen ? 0 : 2 * sizeof (GLfloat);
746     case CAIRO_GL_OPERAND_LINEAR_GRADIENT:
747     case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0:
748     case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE:
749     case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT:
750 	return operand->gradient.texgen ? 0 : 2 * sizeof (GLfloat);
751     }
752 }
753 
754 void
_cairo_gl_operand_emit(cairo_gl_operand_t * operand,GLfloat ** vb,GLfloat x,GLfloat y)755 _cairo_gl_operand_emit (cairo_gl_operand_t *operand,
756 			GLfloat ** vb,
757 			GLfloat x,
758 			GLfloat y)
759 {
760     switch (operand->type) {
761     default:
762     case CAIRO_GL_OPERAND_COUNT:
763 	ASSERT_NOT_REACHED;
764     case CAIRO_GL_OPERAND_NONE:
765     case CAIRO_GL_OPERAND_CONSTANT:
766 	break;
767     case CAIRO_GL_OPERAND_LINEAR_GRADIENT:
768     case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0:
769     case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE:
770     case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT:
771 	if (! operand->gradient.texgen) {
772 	    double s = x;
773 	    double t = y;
774 
775 	    cairo_matrix_transform_point (&operand->gradient.m, &s, &t);
776 
777 	    *(*vb)++ = s;
778 	    *(*vb)++ = t;
779 	}
780 	break;
781     case CAIRO_GL_OPERAND_TEXTURE:
782 	if (! operand->texture.texgen) {
783 	    cairo_surface_attributes_t *src_attributes = &operand->texture.attributes;
784 	    double s = x;
785 	    double t = y;
786 
787 	    cairo_matrix_transform_point (&src_attributes->matrix, &s, &t);
788 	    *(*vb)++ = s;
789 	    *(*vb)++ = t;
790 	}
791 	break;
792     }
793 }
794