1 /* Cairo - a vector graphics library with display and print output
3 * Copyright © 2009 Chris Wilson
4 * Copyright © 2010 Intel Corporation
5 * Copyright © 2010 Red Hat, Inc
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.
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
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/
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.
30 * The Original Code is the cairo graphics library.
32 * The Initial Developer of the Original Code is Chris Wilson.
35 * Benjamin Otte <otte@gnome.org>
36 * Chris Wilson <chris@chris-wilson.co.uk>
41 #include "cairo-gl-private.h"
43 #include "cairo-compositor-private.h"
44 #include "cairo-composite-rectangles-private.h"
45 #include "cairo-error-private.h"
46 #include "cairo-image-surface-private.h"
47 #include "cairo-rtree-private.h"
49 #define GLYPH_CACHE_WIDTH 1024
50 #define GLYPH_CACHE_HEIGHT 1024
51 #define GLYPH_CACHE_MIN_SIZE 4
52 #define GLYPH_CACHE_MAX_SIZE 128
54 typedef struct _cairo_gl_glyph
{
55 cairo_rtree_node_t node
;
56 cairo_scaled_glyph_private_t base
;
57 cairo_scaled_glyph_t
*glyph
;
58 cairo_gl_glyph_cache_t
*cache
;
59 struct { float x
, y
; } p1
, p2
;
63 _cairo_gl_node_destroy (cairo_rtree_node_t
*node
)
65 cairo_gl_glyph_t
*priv
= cairo_container_of (node
, cairo_gl_glyph_t
, node
);
66 cairo_scaled_glyph_t
*glyph
;
72 if (glyph
->dev_private_key
== priv
->cache
) {
73 glyph
->dev_private
= NULL
;
74 glyph
->dev_private_key
= NULL
;
76 cairo_list_del (&priv
->base
.link
);
81 _cairo_gl_glyph_fini (cairo_scaled_glyph_private_t
*glyph_private
,
82 cairo_scaled_glyph_t
*scaled_glyph
,
83 cairo_scaled_font_t
*scaled_font
)
85 cairo_gl_glyph_t
*priv
= cairo_container_of (glyph_private
,
91 _cairo_gl_node_destroy (&priv
->node
);
93 /* XXX thread-safety? Probably ok due to the frozen scaled-font. */
94 if (! priv
->node
.pinned
)
95 _cairo_rtree_node_remove (&priv
->cache
->rtree
, &priv
->node
);
97 assert (priv
->glyph
== NULL
);
100 static cairo_int_status_t
101 _cairo_gl_glyph_cache_add_glyph (cairo_gl_context_t
*ctx
,
102 cairo_gl_glyph_cache_t
*cache
,
103 cairo_scaled_glyph_t
*scaled_glyph
)
105 cairo_image_surface_t
*glyph_surface
= scaled_glyph
->surface
;
106 cairo_gl_glyph_t
*glyph_private
;
107 cairo_rtree_node_t
*node
= NULL
;
108 cairo_int_status_t status
;
111 width
= glyph_surface
->width
;
112 if (width
< GLYPH_CACHE_MIN_SIZE
)
113 width
= GLYPH_CACHE_MIN_SIZE
;
114 height
= glyph_surface
->height
;
115 if (height
< GLYPH_CACHE_MIN_SIZE
)
116 height
= GLYPH_CACHE_MIN_SIZE
;
118 /* search for an available slot */
119 status
= _cairo_rtree_insert (&cache
->rtree
, width
, height
, &node
);
120 /* search for an unlocked slot */
121 if (status
== CAIRO_INT_STATUS_UNSUPPORTED
) {
122 status
= _cairo_rtree_evict_random (&cache
->rtree
,
123 width
, height
, &node
);
124 if (status
== CAIRO_INT_STATUS_SUCCESS
) {
125 status
= _cairo_rtree_node_insert (&cache
->rtree
,
126 node
, width
, height
, &node
);
132 /* XXX: Make sure we use the mask texture. This should work automagically somehow */
133 glActiveTexture (GL_TEXTURE1
);
134 status
= _cairo_gl_surface_draw_image (cache
->surface
, glyph_surface
,
136 glyph_surface
->width
, glyph_surface
->height
,
137 node
->x
, node
->y
, FALSE
);
138 if (unlikely (status
))
141 glyph_private
= (cairo_gl_glyph_t
*) node
;
142 glyph_private
->cache
= cache
;
143 glyph_private
->glyph
= scaled_glyph
;
144 _cairo_scaled_glyph_attach_private (scaled_glyph
,
145 &glyph_private
->base
,
147 _cairo_gl_glyph_fini
);
149 scaled_glyph
->dev_private
= glyph_private
;
150 scaled_glyph
->dev_private_key
= cache
;
152 /* compute tex coords */
153 glyph_private
->p1
.x
= node
->x
;
154 glyph_private
->p1
.y
= node
->y
;
155 glyph_private
->p2
.x
= node
->x
+ glyph_surface
->width
;
156 glyph_private
->p2
.y
= node
->y
+ glyph_surface
->height
;
157 if (! _cairo_gl_device_requires_power_of_two_textures (&ctx
->base
)) {
158 glyph_private
->p1
.x
/= GLYPH_CACHE_WIDTH
;
159 glyph_private
->p2
.x
/= GLYPH_CACHE_WIDTH
;
160 glyph_private
->p1
.y
/= GLYPH_CACHE_HEIGHT
;
161 glyph_private
->p2
.y
/= GLYPH_CACHE_HEIGHT
;
164 return CAIRO_STATUS_SUCCESS
;
167 static cairo_gl_glyph_t
*
168 _cairo_gl_glyph_cache_lock (cairo_gl_glyph_cache_t
*cache
,
169 cairo_scaled_glyph_t
*scaled_glyph
)
171 return _cairo_rtree_pin (&cache
->rtree
, scaled_glyph
->dev_private
);
174 static cairo_status_t
175 cairo_gl_context_get_glyph_cache (cairo_gl_context_t
*ctx
,
176 cairo_format_t format
,
177 cairo_gl_glyph_cache_t
**cache_out
)
179 cairo_gl_glyph_cache_t
*cache
;
180 cairo_content_t content
;
183 case CAIRO_FORMAT_RGB30
:
184 case CAIRO_FORMAT_RGB16_565
:
185 case CAIRO_FORMAT_ARGB32
:
186 case CAIRO_FORMAT_RGB24
:
187 cache
= &ctx
->glyph_cache
[0];
188 content
= CAIRO_CONTENT_COLOR_ALPHA
;
190 case CAIRO_FORMAT_A8
:
191 case CAIRO_FORMAT_A1
:
192 cache
= &ctx
->glyph_cache
[1];
193 content
= CAIRO_CONTENT_ALPHA
;
196 case CAIRO_FORMAT_INVALID
:
198 return _cairo_error (CAIRO_STATUS_INVALID_FORMAT
);
201 if (unlikely (cache
->surface
== NULL
)) {
202 cairo_surface_t
*surface
;
204 surface
= _cairo_gl_surface_create_scratch_for_caching (ctx
,
208 if (unlikely (surface
->status
))
209 return surface
->status
;
211 _cairo_surface_release_device_reference (surface
);
213 cache
->surface
= (cairo_gl_surface_t
*)surface
;
214 cache
->surface
->operand
.texture
.attributes
.has_component_alpha
=
215 content
== CAIRO_CONTENT_COLOR_ALPHA
;
219 return CAIRO_STATUS_SUCCESS
;
222 static cairo_status_t
223 render_glyphs (cairo_gl_surface_t
*dst
,
224 int dst_x
, int dst_y
,
226 cairo_surface_t
*source
,
227 cairo_composite_glyphs_info_t
*info
,
228 cairo_bool_t
*has_component_alpha
,
231 cairo_format_t last_format
= CAIRO_FORMAT_INVALID
;
232 cairo_gl_glyph_cache_t
*cache
= NULL
;
233 cairo_gl_context_t
*ctx
;
234 cairo_gl_emit_glyph_t emit
= NULL
;
235 cairo_gl_composite_t setup
;
236 cairo_int_status_t status
;
239 TRACE ((stderr
, "%s (%d, %d)x(%d, %d)\n", __FUNCTION__
,
240 info
->extents
.x
, info
->extents
.y
,
241 info
->extents
.width
, info
->extents
.height
));
243 *has_component_alpha
= FALSE
;
245 status
= _cairo_gl_context_acquire (dst
->base
.device
, &ctx
);
246 if (unlikely (status
))
249 status
= _cairo_gl_composite_init (&setup
, op
, dst
, TRUE
);
250 if (unlikely (status
))
253 if (source
== NULL
) {
254 _cairo_gl_composite_set_solid_source (&setup
, CAIRO_COLOR_WHITE
);
256 _cairo_gl_composite_set_source_operand (&setup
,
257 source_to_operand (source
));
261 _cairo_gl_composite_set_clip (&setup
, clip
);
263 for (i
= 0; i
< info
->num_glyphs
; i
++) {
264 cairo_scaled_glyph_t
*scaled_glyph
;
265 cairo_gl_glyph_t
*glyph
;
266 double x_offset
, y_offset
;
267 double x1
, x2
, y1
, y2
;
269 status
= _cairo_scaled_glyph_lookup (info
->font
,
270 info
->glyphs
[i
].index
,
271 CAIRO_SCALED_GLYPH_INFO_SURFACE
,
273 if (unlikely (status
))
276 if (scaled_glyph
->surface
->width
== 0 ||
277 scaled_glyph
->surface
->height
== 0)
281 if (scaled_glyph
->surface
->format
!= last_format
) {
282 status
= cairo_gl_context_get_glyph_cache (ctx
,
283 scaled_glyph
->surface
->format
,
285 if (unlikely (status
))
288 last_format
= scaled_glyph
->surface
->format
;
290 _cairo_gl_composite_set_mask_operand (&setup
, &cache
->surface
->operand
);
291 *has_component_alpha
|= cache
->surface
->operand
.texture
.attributes
.has_component_alpha
;
294 status
= _cairo_gl_composite_begin (&setup
, &ctx
);
295 status
= _cairo_gl_context_release (ctx
, status
);
296 if (unlikely (status
))
299 emit
= _cairo_gl_context_choose_emit_glyph (ctx
);
302 if (scaled_glyph
->dev_private_key
!= cache
) {
303 cairo_scaled_glyph_private_t
*priv
;
305 priv
= _cairo_scaled_glyph_find_private (scaled_glyph
, cache
);
307 scaled_glyph
->dev_private_key
= cache
;
308 scaled_glyph
->dev_private
= cairo_container_of (priv
,
312 status
= _cairo_gl_glyph_cache_add_glyph (ctx
, cache
, scaled_glyph
);
314 if (status
== CAIRO_INT_STATUS_UNSUPPORTED
) {
315 /* Cache is full, so flush existing prims and try again. */
316 _cairo_gl_composite_flush (ctx
);
317 _cairo_gl_glyph_cache_unlock (cache
);
318 status
= _cairo_gl_glyph_cache_add_glyph (ctx
, cache
, scaled_glyph
);
321 if (unlikely (_cairo_int_status_is_error (status
)))
326 x_offset
= scaled_glyph
->surface
->base
.device_transform
.x0
;
327 y_offset
= scaled_glyph
->surface
->base
.device_transform
.y0
;
329 x1
= _cairo_lround (info
->glyphs
[i
].x
- x_offset
- dst_x
);
330 y1
= _cairo_lround (info
->glyphs
[i
].y
- y_offset
- dst_y
);
331 x2
= x1
+ scaled_glyph
->surface
->width
;
332 y2
= y1
+ scaled_glyph
->surface
->height
;
334 glyph
= _cairo_gl_glyph_cache_lock (cache
, scaled_glyph
);
338 glyph
->p1
.x
, glyph
->p1
.y
,
339 glyph
->p2
.x
, glyph
->p2
.y
);
342 status
= CAIRO_STATUS_SUCCESS
;
344 status
= _cairo_gl_context_release (ctx
, status
);
346 _cairo_gl_composite_fini (&setup
);
350 static cairo_int_status_t
351 render_glyphs_via_mask (cairo_gl_surface_t
*dst
,
352 int dst_x
, int dst_y
,
354 cairo_surface_t
*source
,
355 cairo_composite_glyphs_info_t
*info
,
358 cairo_surface_t
*mask
;
359 cairo_status_t status
;
360 cairo_bool_t has_component_alpha
;
362 TRACE ((stderr
, "%s\n", __FUNCTION__
));
364 /* XXX: For non-CA, this should be CAIRO_CONTENT_ALPHA to save memory */
365 mask
= cairo_gl_surface_create (dst
->base
.device
,
366 CAIRO_CONTENT_COLOR_ALPHA
,
368 info
->extents
.height
);
369 if (unlikely (mask
->status
))
372 status
= render_glyphs ((cairo_gl_surface_t
*) mask
,
373 info
->extents
.x
, info
->extents
.y
,
374 CAIRO_OPERATOR_ADD
, NULL
,
375 info
, &has_component_alpha
, NULL
);
376 if (likely (status
== CAIRO_STATUS_SUCCESS
)) {
377 cairo_surface_pattern_t mask_pattern
;
378 cairo_surface_pattern_t source_pattern
;
379 cairo_rectangle_int_t clip_extents
;
381 mask
->is_clear
= FALSE
;
382 _cairo_pattern_init_for_surface (&mask_pattern
, mask
);
383 mask_pattern
.base
.has_component_alpha
= has_component_alpha
;
384 mask_pattern
.base
.filter
= CAIRO_FILTER_NEAREST
;
385 mask_pattern
.base
.extend
= CAIRO_EXTEND_NONE
;
387 cairo_matrix_init_translate (&mask_pattern
.base
.matrix
,
388 dst_x
-info
->extents
.x
, dst_y
-info
->extents
.y
);
390 _cairo_pattern_init_for_surface (&source_pattern
, source
);
391 cairo_matrix_init_translate (&source_pattern
.base
.matrix
,
392 dst_x
-info
->extents
.x
, dst_y
-info
->extents
.y
);
394 clip
= _cairo_clip_copy (clip
);
395 clip_extents
.x
= info
->extents
.x
- dst_x
;
396 clip_extents
.y
= info
->extents
.y
- dst_y
;
397 clip_extents
.width
= info
->extents
.width
;
398 clip_extents
.height
= info
->extents
.height
;
399 clip
= _cairo_clip_intersect_rectangle (clip
, &clip_extents
);
401 status
= _cairo_surface_mask (&dst
->base
, op
,
402 &source_pattern
.base
,
406 _cairo_clip_destroy (clip
);
408 _cairo_pattern_fini (&mask_pattern
.base
);
409 _cairo_pattern_fini (&source_pattern
.base
);
412 cairo_surface_destroy (mask
);
418 _cairo_gl_check_composite_glyphs (const cairo_composite_rectangles_t
*extents
,
419 cairo_scaled_font_t
*scaled_font
,
420 cairo_glyph_t
*glyphs
,
423 if (! _cairo_gl_operator_is_supported (extents
->op
))
424 return UNSUPPORTED ("unsupported operator");
426 /* XXX use individual masks for large glyphs? */
427 if (ceil (scaled_font
->max_scale
) >= GLYPH_CACHE_MAX_SIZE
)
428 return UNSUPPORTED ("glyphs too large");
430 return CAIRO_STATUS_SUCCESS
;
434 _cairo_gl_composite_glyphs_with_clip (void *_dst
,
436 cairo_surface_t
*_src
,
441 cairo_composite_glyphs_info_t
*info
,
444 cairo_gl_surface_t
*dst
= _dst
;
445 cairo_bool_t has_component_alpha
;
447 TRACE ((stderr
, "%s\n", __FUNCTION__
));
449 /* If any of the glyphs require component alpha, we have to go through
450 * a mask, since only _cairo_gl_surface_composite() currently supports
453 if (!dst
->base
.is_clear
&& ! info
->use_mask
&& op
!= CAIRO_OPERATOR_OVER
&&
454 (info
->font
->options
.antialias
== CAIRO_ANTIALIAS_SUBPIXEL
||
455 info
->font
->options
.antialias
== CAIRO_ANTIALIAS_BEST
))
457 info
->use_mask
= TRUE
;
460 if (info
->use_mask
) {
461 return render_glyphs_via_mask (dst
, dst_x
, dst_y
,
462 op
, _src
, info
, clip
);
464 return render_glyphs (dst
, dst_x
, dst_y
,
466 &has_component_alpha
,
473 _cairo_gl_composite_glyphs (void *_dst
,
475 cairo_surface_t
*_src
,
480 cairo_composite_glyphs_info_t
*info
)
482 return _cairo_gl_composite_glyphs_with_clip (_dst
, op
, _src
, src_x
, src_y
,
483 dst_x
, dst_y
, info
, NULL
);
487 _cairo_gl_glyph_cache_init (cairo_gl_glyph_cache_t
*cache
)
489 _cairo_rtree_init (&cache
->rtree
,
492 GLYPH_CACHE_MIN_SIZE
,
493 sizeof (cairo_gl_glyph_t
),
494 _cairo_gl_node_destroy
);
498 _cairo_gl_glyph_cache_fini (cairo_gl_context_t
*ctx
,
499 cairo_gl_glyph_cache_t
*cache
)
501 _cairo_rtree_fini (&cache
->rtree
);
502 cairo_surface_destroy (&cache
->surface
->base
);