beta-0.89.2
[luatex.git] / source / libs / cairo / cairo-src / src / cairo-gl-glyphs.c
blob8a1a548d939718c83f9fa493d251301b841f6e3a
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.
34 * Contributors:
35 * Benjamin Otte <otte@gnome.org>
36 * Chris Wilson <chris@chris-wilson.co.uk>
39 #include "cairoint.h"
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;
60 } cairo_gl_glyph_t;
62 static void
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;
68 glyph = priv->glyph;
69 if (glyph == NULL)
70 return;
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);
77 priv->glyph = NULL;
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)
85 cairo_gl_glyph_t *priv = cairo_container_of (glyph_private,
86 cairo_gl_glyph_t,
87 base);
89 assert (priv->glyph);
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;
109 int width, height;
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);
129 if (status)
130 return status;
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,
135 0, 0,
136 glyph_surface->width, glyph_surface->height,
137 node->x, node->y, FALSE);
138 if (unlikely (status))
139 return 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,
146 cache,
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;
182 switch (format) {
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;
189 break;
190 case CAIRO_FORMAT_A8:
191 case CAIRO_FORMAT_A1:
192 cache = &ctx->glyph_cache[1];
193 content = CAIRO_CONTENT_ALPHA;
194 break;
195 default:
196 case CAIRO_FORMAT_INVALID:
197 ASSERT_NOT_REACHED;
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,
205 content,
206 GLYPH_CACHE_WIDTH,
207 GLYPH_CACHE_HEIGHT);
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;
218 *cache_out = cache;
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,
225 cairo_operator_t op,
226 cairo_surface_t *source,
227 cairo_composite_glyphs_info_t *info,
228 cairo_bool_t *has_component_alpha,
229 cairo_clip_t *clip)
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;
237 int i = 0;
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))
247 return status;
249 status = _cairo_gl_composite_init (&setup, op, dst, TRUE);
250 if (unlikely (status))
251 goto FINISH;
253 if (source == NULL) {
254 _cairo_gl_composite_set_solid_source (&setup, CAIRO_COLOR_WHITE);
255 } else {
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,
272 &scaled_glyph);
273 if (unlikely (status))
274 goto FINISH;
276 if (scaled_glyph->surface->width == 0 ||
277 scaled_glyph->surface->height == 0)
279 continue;
281 if (scaled_glyph->surface->format != last_format) {
282 status = cairo_gl_context_get_glyph_cache (ctx,
283 scaled_glyph->surface->format,
284 &cache);
285 if (unlikely (status))
286 goto FINISH;
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;
293 /* XXX Shoot me. */
294 status = _cairo_gl_composite_begin (&setup, &ctx);
295 status = _cairo_gl_context_release (ctx, status);
296 if (unlikely (status))
297 goto FINISH;
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);
306 if (priv) {
307 scaled_glyph->dev_private_key = cache;
308 scaled_glyph->dev_private = cairo_container_of (priv,
309 cairo_gl_glyph_t,
310 base);
311 } else {
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)))
322 goto FINISH;
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);
335 assert (emit);
336 emit (ctx,
337 x1, y1, x2, y2,
338 glyph->p1.x, glyph->p1.y,
339 glyph->p2.x, glyph->p2.y);
342 status = CAIRO_STATUS_SUCCESS;
343 FINISH:
344 status = _cairo_gl_context_release (ctx, status);
346 _cairo_gl_composite_fini (&setup);
347 return status;
350 static cairo_int_status_t
351 render_glyphs_via_mask (cairo_gl_surface_t *dst,
352 int dst_x, int dst_y,
353 cairo_operator_t op,
354 cairo_surface_t *source,
355 cairo_composite_glyphs_info_t *info,
356 cairo_clip_t *clip)
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,
367 info->extents.width,
368 info->extents.height);
369 if (unlikely (mask->status))
370 return 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,
403 &mask_pattern.base,
404 clip);
406 _cairo_clip_destroy (clip);
408 _cairo_pattern_fini (&mask_pattern.base);
409 _cairo_pattern_fini (&source_pattern.base);
412 cairo_surface_destroy (mask);
414 return status;
417 cairo_int_status_t
418 _cairo_gl_check_composite_glyphs (const cairo_composite_rectangles_t *extents,
419 cairo_scaled_font_t *scaled_font,
420 cairo_glyph_t *glyphs,
421 int *num_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;
433 cairo_int_status_t
434 _cairo_gl_composite_glyphs_with_clip (void *_dst,
435 cairo_operator_t op,
436 cairo_surface_t *_src,
437 int src_x,
438 int src_y,
439 int dst_x,
440 int dst_y,
441 cairo_composite_glyphs_info_t *info,
442 cairo_clip_t *clip)
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
451 * component alpha.
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);
463 } else {
464 return render_glyphs (dst, dst_x, dst_y,
465 op, _src, info,
466 &has_component_alpha,
467 clip);
472 cairo_int_status_t
473 _cairo_gl_composite_glyphs (void *_dst,
474 cairo_operator_t op,
475 cairo_surface_t *_src,
476 int src_x,
477 int src_y,
478 int dst_x,
479 int dst_y,
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);
486 void
487 _cairo_gl_glyph_cache_init (cairo_gl_glyph_cache_t *cache)
489 _cairo_rtree_init (&cache->rtree,
490 GLYPH_CACHE_WIDTH,
491 GLYPH_CACHE_HEIGHT,
492 GLYPH_CACHE_MIN_SIZE,
493 sizeof (cairo_gl_glyph_t),
494 _cairo_gl_node_destroy);
497 void
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);