tizen 2.3.1 release
[framework/graphics/cairo.git] / src / cairo-gl-glyphs.c
1 /* Cairo - a vector graphics library with display and print output
2  *
3  * Copyright © 2009 Chris Wilson
4  * Copyright © 2010 Intel Corporation
5  * Copyright © 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 Chris Wilson.
33  *
34  * Contributors:
35  *      Benjamin Otte <otte@gnome.org>
36  *      Chris Wilson <chris@chris-wilson.co.uk>
37  */
38
39 #include "cairoint.h"
40
41 #include "cairo-gl-private.h"
42
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"
48
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
53
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;
60 } cairo_gl_glyph_t;
61
62 static void
63 _cairo_gl_node_destroy (cairo_rtree_node_t *node)
64 {
65     cairo_gl_glyph_t *priv = cairo_container_of (node, cairo_gl_glyph_t, node);
66     cairo_scaled_glyph_t *glyph;
67
68     glyph = priv->glyph;
69     if (glyph == NULL)
70             return;
71
72     if (glyph->dev_private_key == priv->cache) {
73             glyph->dev_private = NULL;
74             glyph->dev_private_key = NULL;
75     }
76     cairo_list_del (&priv->base.link);
77     priv->glyph = NULL;
78 }
79
80 static void
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)
84 {
85     cairo_gl_glyph_t *priv = cairo_container_of (glyph_private,
86                                                  cairo_gl_glyph_t,
87                                                  base);
88
89     assert (priv->glyph);
90
91     _cairo_gl_node_destroy (&priv->node);
92
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);
96
97     assert (priv->glyph == NULL);
98 }
99
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)
104 {
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;
109     int width, height;
110
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;
117
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);
127         }
128     }
129     if (status)
130         return status;
131
132     /* XXX: Make sure we use the mask texture. This should work automagically somehow */
133     if(ctx->states_cache.active_texture != GL_TEXTURE1)
134     {
135         ctx->dispatch.ActiveTexture (GL_TEXTURE1);
136         ctx->states_cache.active_texture = GL_TEXTURE1;
137     }
138     status = _cairo_gl_surface_draw_image (cache->surface, glyph_surface,
139                                            0, 0,
140                                            glyph_surface->width, glyph_surface->height,
141                                            node->x, node->y, FALSE);
142     if (unlikely (status))
143         return status;
144
145     glyph_private = (cairo_gl_glyph_t *) node;
146     glyph_private->cache = cache;
147     glyph_private->glyph = scaled_glyph;
148     _cairo_scaled_glyph_attach_private (scaled_glyph,
149                                         &glyph_private->base,
150                                         cache,
151                                         _cairo_gl_glyph_fini);
152
153     scaled_glyph->dev_private = glyph_private;
154     scaled_glyph->dev_private_key = cache;
155
156     /* compute tex coords */
157     glyph_private->p1.x = node->x;
158     glyph_private->p1.y = node->y;
159     glyph_private->p2.x = node->x + glyph_surface->width;
160     glyph_private->p2.y = node->y + glyph_surface->height;
161     if (! _cairo_gl_device_requires_power_of_two_textures (&ctx->base)) {
162         glyph_private->p1.x /= GLYPH_CACHE_WIDTH;
163         glyph_private->p2.x /= GLYPH_CACHE_WIDTH;
164         glyph_private->p1.y /= GLYPH_CACHE_HEIGHT;
165         glyph_private->p2.y /= GLYPH_CACHE_HEIGHT;
166     }
167
168     return CAIRO_STATUS_SUCCESS;
169 }
170
171 static cairo_gl_glyph_t *
172 _cairo_gl_glyph_cache_lock (cairo_gl_glyph_cache_t *cache,
173                             cairo_scaled_glyph_t *scaled_glyph)
174 {
175     return _cairo_rtree_pin (&cache->rtree, scaled_glyph->dev_private);
176 }
177
178 static cairo_status_t
179 cairo_gl_context_get_glyph_cache (cairo_gl_context_t *ctx,
180                                   cairo_format_t format,
181                                 cairo_bool_t has_component_alpha,
182                  cairo_gl_glyph_cache_t **cache_out)
183 {
184     cairo_gl_glyph_cache_t *cache;
185     cairo_content_t content;
186
187     switch (format) {
188     case CAIRO_FORMAT_RGB30:
189     case CAIRO_FORMAT_RGB16_565:
190     case CAIRO_FORMAT_ARGB32:
191     case CAIRO_FORMAT_RGB24:
192                 if (has_component_alpha) {
193                         cache = &ctx->glyph_cache[0];
194                 } else {
195                         cache = &ctx->glyph_cache[2];
196                 }
197         content = CAIRO_CONTENT_COLOR_ALPHA;
198                 break;
199         break;
200     case CAIRO_FORMAT_A8:
201     case CAIRO_FORMAT_A1:
202         cache = &ctx->glyph_cache[1];
203         content = CAIRO_CONTENT_ALPHA;
204         break;
205     default:
206     case CAIRO_FORMAT_INVALID:
207         ASSERT_NOT_REACHED;
208         return _cairo_error (CAIRO_STATUS_INVALID_FORMAT);
209     }
210
211     if (unlikely (cache->surface == NULL)) {
212         cairo_surface_t *surface;
213
214         surface = _cairo_gl_surface_create_scratch_for_caching (ctx,
215                                                                 content,
216                                                                 GLYPH_CACHE_WIDTH,
217                                                                 GLYPH_CACHE_HEIGHT);
218         if (unlikely (surface->status))
219             return surface->status;
220
221         _cairo_surface_release_device_reference (surface);
222
223         cache->surface = (cairo_gl_surface_t *)surface;
224         cache->surface->operand.texture.attributes.has_component_alpha = has_component_alpha;
225     }
226
227     *cache_out = cache;
228     return CAIRO_STATUS_SUCCESS;
229 }
230
231 /* Clear a partial surface, assumes context has already been acquired */
232 static void _cairo_gl_surface_clear_with_extent (cairo_gl_context_t *ctx,
233                                                  cairo_gl_surface_t * dst,
234                                                  cairo_rectangle_int_t *extent,
235                                                  cairo_bool_t use_multisample )
236 {
237         _cairo_gl_context_set_destination(ctx, dst, use_multisample);
238
239         if (ctx->states_cache.clear_red != 0 ||
240             ctx->states_cache.clear_green != 0 ||
241             ctx->states_cache.clear_blue != 0 ||
242             ctx->states_cache.clear_alpha != 0) {
243
244             ctx->states_cache.clear_red = 0;
245             ctx->states_cache.clear_green = 0;
246             ctx->states_cache.clear_blue = 0;
247             ctx->states_cache.clear_alpha = 0;
248
249             ctx->dispatch.ClearColor (0, 0, 0, 0);
250         }
251         if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP) {
252             _enable_scissor_buffer (ctx);
253             ctx->dispatch.Scissor(0, 0, extent->width, extent->height);
254             _disable_stencil_buffer (ctx);
255             ctx->dispatch.Clear (GL_COLOR_BUFFER_BIT);
256         }
257         else {
258             _disable_stencil_buffer (ctx);
259             _disable_scissor_buffer (ctx);
260             ctx->dispatch.Clear (GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
261         }
262 }
263
264
265 static cairo_status_t
266 render_glyphs (cairo_gl_surface_t *dst,
267                int dst_x, int dst_y,
268                cairo_operator_t op,
269                cairo_surface_t *source,
270                cairo_composite_glyphs_info_t *info,
271                cairo_bool_t *has_component_alpha,
272                cairo_clip_t *clip,
273                cairo_bool_t *is_color_glyph)
274 {
275     cairo_format_t last_format = CAIRO_FORMAT_INVALID;
276     cairo_gl_glyph_cache_t *cache = NULL;
277     cairo_gl_context_t *ctx;
278     cairo_gl_emit_glyph_t emit;
279     cairo_gl_composite_t setup;
280     cairo_int_status_t status;
281     int i = 0;
282
283     TRACE ((stderr, "%s (%d, %d)x(%d, %d)\n", __FUNCTION__,
284             info->extents.x, info->extents.y,
285             info->extents.width, info->extents.height));
286
287     *has_component_alpha = FALSE;
288
289     status = _cairo_gl_context_acquire (dst->base.device, &ctx);
290     if (unlikely (status))
291         return status;
292
293     status = _cairo_gl_composite_init (&setup, op, dst, TRUE);
294     if (unlikely (status))
295         goto FINISH;
296
297     if (source == NULL) {
298             _cairo_gl_composite_set_solid_source (&setup, CAIRO_COLOR_WHITE);
299     } else {
300             _cairo_gl_composite_set_source_operand (&setup,
301                                                     source_to_operand (source));
302
303     }
304
305     if (setup.src.type == CAIRO_GL_OPERAND_CONSTANT)
306         setup.src.constant.encode_as_attribute = TRUE;
307
308     _cairo_gl_composite_set_clip (&setup, clip);
309
310     for (i = 0; i < info->num_glyphs; i++) {
311         cairo_scaled_glyph_t *scaled_glyph;
312         cairo_gl_glyph_t *glyph;
313         double x_offset, y_offset;
314         double x1, x2, y1, y2;
315
316         status = _cairo_scaled_glyph_lookup (info->font,
317                                              info->glyphs[i].index,
318                                              CAIRO_SCALED_GLYPH_INFO_SURFACE,
319                                              &scaled_glyph);
320         if (unlikely (status))
321             goto FINISH;
322
323         if (scaled_glyph->surface->width  == 0 ||
324             scaled_glyph->surface->height == 0)
325         {
326             continue;
327         }
328         if (! *has_component_alpha)
329                 *has_component_alpha = pixman_image_get_component_alpha (scaled_glyph->surface->pixman_image);
330
331         /* color glyph has ARGB32 format and dst mask surface is ALPHA */
332         if (scaled_glyph->surface->format == CAIRO_FORMAT_ARGB32 &&
333                 dst->base.content == CAIRO_CONTENT_ALPHA &&
334                 *has_component_alpha == FALSE)
335                 return CAIRO_STATUS_SURFACE_TYPE_MISMATCH;
336
337         if (scaled_glyph->surface->format != last_format) {
338             status = cairo_gl_context_get_glyph_cache (ctx,
339                                                        scaled_glyph->surface->format,
340                                                         *has_component_alpha,
341                                                        &cache);
342             if (unlikely (status))
343                 goto FINISH;
344
345             last_format = scaled_glyph->surface->format;
346
347         if (! *has_component_alpha &&
348                 cache->surface->base.content == CAIRO_CONTENT_COLOR_ALPHA) {
349                 /* we have color glyph */
350                 _cairo_gl_composite_set_source_operand (&setup, &cache->surface->operand);
351                 *is_color_glyph = TRUE;
352         } else {
353                 _cairo_gl_composite_set_mask_operand (&setup, &cache->surface->operand);
354                 *is_color_glyph = FALSE;
355         }
356
357             /* XXX Shoot me. */
358             if (dst->msaa_active)
359                 _cairo_gl_composite_set_multisample (&setup);
360
361             status = _cairo_gl_composite_begin (&setup, &ctx);
362             status = _cairo_gl_context_release (ctx, status);
363             if (unlikely (status))
364                 goto FINISH;
365
366             emit = _cairo_gl_context_choose_emit_glyph (ctx, *is_color_glyph);
367         }
368
369         if (scaled_glyph->dev_private_key != cache) {
370             cairo_scaled_glyph_private_t *priv;
371
372             priv = _cairo_scaled_glyph_find_private (scaled_glyph, cache);
373             if (priv) {
374                 scaled_glyph->dev_private_key = cache;
375                 scaled_glyph->dev_private = cairo_container_of (priv,
376                                                                 cairo_gl_glyph_t,
377                                                                 base);
378             } else {
379                 if (cache == NULL) {
380                     status = CAIRO_STATUS_NULL_POINTER;
381                     goto FINISH;
382                 }
383                 status = _cairo_gl_glyph_cache_add_glyph (ctx, cache, scaled_glyph);
384
385                 if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
386                     /* Cache is full, so flush existing prims and try again. */
387                     _cairo_gl_composite_flush (ctx);
388                     _cairo_gl_glyph_cache_unlock (cache);
389                     status = _cairo_gl_glyph_cache_add_glyph (ctx, cache, scaled_glyph);
390                 }
391
392                 if (unlikely (_cairo_int_status_is_error (status)))
393                     goto FINISH;
394             }
395         }
396
397         x_offset = scaled_glyph->surface->base.device_transform.x0;
398         y_offset = scaled_glyph->surface->base.device_transform.y0;
399
400         x1 = _cairo_lround (info->glyphs[i].x - x_offset - dst_x);
401         y1 = _cairo_lround (info->glyphs[i].y - y_offset - dst_y);
402         x2 = x1 + scaled_glyph->surface->width;
403         y2 = y1 + scaled_glyph->surface->height;
404
405         if (cache == NULL) {
406             status = CAIRO_STATUS_NULL_POINTER;
407             goto FINISH;
408         }
409         glyph = _cairo_gl_glyph_cache_lock (cache, scaled_glyph);
410         emit (ctx,
411               x1, y1, x2, y2,
412               glyph->p1.x, glyph->p1.y,
413               glyph->p2.x, glyph->p2.y);
414     }
415
416     status = CAIRO_STATUS_SUCCESS;
417   FINISH:
418     status = _cairo_gl_context_release (ctx, status);
419
420     _cairo_gl_composite_fini (&setup);
421
422     return status;
423 }
424
425 static cairo_int_status_t
426 render_glyphs_via_mask (cairo_gl_surface_t *dst,
427                         int dst_x, int dst_y,
428                         cairo_operator_t  op,
429                         cairo_surface_t *source,
430                         cairo_composite_glyphs_info_t *info,
431                         cairo_clip_t *clip)
432 {
433     cairo_status_t status;
434     cairo_bool_t has_component_alpha;
435     cairo_gl_context_t *ctx;
436         cairo_bool_t is_color_glyph;
437
438     int width = info->extents.width;
439     int height = info->extents.height;
440
441     TRACE ((stderr, "%s\n", __FUNCTION__));
442
443     status = _cairo_gl_context_acquire (dst->base.device, &ctx);
444     if (unlikely (status))
445         return status;
446
447     if (ctx->glyph_mask &&
448         (ctx->glyph_mask->width < info->extents.width ||
449          ctx->glyph_mask->height < info->extents.height)) {
450         width = ctx->glyph_mask->width;
451         height = ctx->glyph_mask->height;
452
453         cairo_surface_destroy (&ctx->glyph_mask->base);
454         ctx->glyph_mask = NULL;
455     }
456
457     /* Create the mask if it has not yet been initialized or it was too small and deleted above. */
458     if (! ctx->glyph_mask) {
459         width = MAX (width, info->extents.width);
460         height = MAX (height, info->extents.height);
461         /* XXX: For non-CA, this should be CAIRO_CONTENT_ALPHA to save memory */
462         ctx->glyph_mask = (cairo_gl_surface_t *)
463              cairo_gl_surface_create (dst->base.device,
464                                       CAIRO_CONTENT_COLOR_ALPHA,
465                                       width, height);
466         if (unlikely (ctx->glyph_mask->base.status)) {
467             status = ctx->glyph_mask->base.status;
468             status = _cairo_gl_context_release (ctx, status);
469             return status;
470         }
471         _cairo_surface_release_device_reference (&ctx->glyph_mask->base);
472     }
473
474     /* clear it */
475     _cairo_gl_surface_clear_with_extent (ctx,
476                                          (cairo_gl_surface_t *)ctx->glyph_mask,
477                                           &info->extents, FALSE);
478
479     status = render_glyphs (ctx->glyph_mask,
480                             info->extents.x, info->extents.y,
481                             CAIRO_OPERATOR_ADD, NULL,
482                             info, &has_component_alpha, NULL,
483                             &is_color_glyph);
484
485     if (likely (status == CAIRO_STATUS_SUCCESS)) {
486         cairo_surface_pattern_t mask_pattern;
487         cairo_surface_pattern_t source_pattern;
488         cairo_rectangle_int_t clip_extents;
489
490         ctx->glyph_mask->base.is_clear = FALSE;
491         _cairo_pattern_init_for_surface (&mask_pattern, &ctx->glyph_mask->base);
492         mask_pattern.base.has_component_alpha = has_component_alpha;
493         mask_pattern.base.filter = CAIRO_FILTER_NEAREST;
494         mask_pattern.base.extend = CAIRO_EXTEND_NONE;
495
496         cairo_matrix_init_translate (&mask_pattern.base.matrix,
497                                      dst_x-info->extents.x, dst_y-info->extents.y);
498
499         _cairo_pattern_init_for_surface (&source_pattern, source);
500
501         clip = _cairo_clip_copy (clip);
502         clip_extents.x = info->extents.x - dst_x;
503         clip_extents.y = info->extents.y - dst_y;
504         clip_extents.width = info->extents.width;
505         clip_extents.height = info->extents.height;
506         clip = _cairo_clip_intersect_rectangle (clip, &clip_extents);
507
508         if(is_color_glyph) {
509                 if(op == CAIRO_OPERATOR_SOURCE) {
510                         /* do dest_out then add*/
511                         status = _cairo_surface_paint (&dst->base,
512                                                                         CAIRO_OPERATOR_DEST_OUT,
513                                                                   &mask_pattern.base,
514                                                                   clip);
515                         status = _cairo_surface_paint (&dst->base,
516                                                                         CAIRO_OPERATOR_ADD,
517                                                                   &mask_pattern.base, clip);
518                         } else {
519                                 status = _cairo_surface_paint (&dst->base,op,
520                                                                   &mask_pattern.base,
521                                                                   clip);
522                                 }
523                 }
524         else
525                 status = _cairo_surface_mask (&dst->base, op,
526                                       &source_pattern.base,
527                                                         &mask_pattern.base,
528                                                         clip);
529
530         _cairo_clip_destroy (clip);
531
532         _cairo_pattern_fini (&mask_pattern.base);
533         _cairo_pattern_fini (&source_pattern.base);
534     }
535
536     status = _cairo_gl_context_release(ctx, status);
537
538     return status;
539 }
540
541 cairo_int_status_t
542 _cairo_gl_check_composite_glyphs (const cairo_composite_rectangles_t *extents,
543                                   cairo_scaled_font_t *scaled_font,
544                                   cairo_glyph_t *glyphs,
545                                   int *num_glyphs)
546 {
547     if (! _cairo_gl_operator_is_supported (extents->op))
548         return UNSUPPORTED ("unsupported operator");
549
550     /* XXX use individual masks for large glyphs? */
551     if (ceil (scaled_font->max_scale) >= GLYPH_CACHE_MAX_SIZE)
552         return UNSUPPORTED ("glyphs too large");
553
554     return CAIRO_STATUS_SUCCESS;
555 }
556
557 cairo_int_status_t
558 _cairo_gl_composite_glyphs_with_clip (void                          *_dst,
559                                       cairo_operator_t               op,
560                                       cairo_surface_t               *_src,
561                                       int                            src_x,
562                                       int                            src_y,
563                                       int                            dst_x,
564                                       int                            dst_y,
565                                       cairo_composite_glyphs_info_t *info,
566                                       cairo_clip_t                  *clip)
567 {
568     cairo_gl_surface_t *dst = _dst;
569     cairo_bool_t has_component_alpha;
570         cairo_bool_t is_color_glyph;
571
572     TRACE ((stderr, "%s\n", __FUNCTION__));
573
574     /* If any of the glyphs require component alpha, we have to go through
575      * a mask, since only _cairo_gl_surface_composite() currently supports
576      * component alpha.
577      */
578     if (!dst->base.is_clear && ! info->use_mask && op != CAIRO_OPERATOR_OVER &&
579         (info->font->options.antialias == CAIRO_ANTIALIAS_SUBPIXEL ||
580          info->font->options.antialias == CAIRO_ANTIALIAS_BEST))
581     {
582         info->use_mask = TRUE;
583     }
584
585     if (info->use_mask) {
586         return render_glyphs_via_mask (dst, dst_x, dst_y,
587                                        op, _src, info, clip);
588     } else {
589         return render_glyphs (dst, dst_x, dst_y,
590                               op, _src, info,
591                               &has_component_alpha,
592                               clip, &is_color_glyph);
593     }
594
595 }
596
597 cairo_int_status_t
598 _cairo_gl_composite_glyphs (void                        *_dst,
599                             cairo_operator_t             op,
600                             cairo_surface_t             *_src,
601                             int                          src_x,
602                             int                          src_y,
603                             int                          dst_x,
604                             int                          dst_y,
605                             cairo_composite_glyphs_info_t *info)
606 {
607     return _cairo_gl_composite_glyphs_with_clip (_dst, op, _src, src_x, src_y,
608                                                  dst_x, dst_y, info, NULL);
609 }
610
611 void
612 _cairo_gl_glyph_cache_init (cairo_gl_glyph_cache_t *cache)
613 {
614     _cairo_rtree_init (&cache->rtree,
615                        GLYPH_CACHE_WIDTH,
616                        GLYPH_CACHE_HEIGHT,
617                        GLYPH_CACHE_MIN_SIZE,
618                        sizeof (cairo_gl_glyph_t),
619                        _cairo_gl_node_destroy);
620 }
621
622 void
623 _cairo_gl_glyph_cache_fini (cairo_gl_context_t *ctx,
624                             cairo_gl_glyph_cache_t *cache)
625 {
626     _cairo_rtree_fini (&cache->rtree);
627     cairo_surface_destroy (&cache->surface->base);
628 }