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 if(ctx->states_cache.active_texture != GL_TEXTURE1)
135 ctx->dispatch.ActiveTexture (GL_TEXTURE1);
136 ctx->states_cache.active_texture = GL_TEXTURE1;
138 status = _cairo_gl_surface_draw_image (cache->surface, glyph_surface,
140 glyph_surface->width, glyph_surface->height,
141 node->x, node->y, FALSE);
142 if (unlikely (status))
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,
151 _cairo_gl_glyph_fini);
153 scaled_glyph->dev_private = glyph_private;
154 scaled_glyph->dev_private_key = cache;
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;
168 return CAIRO_STATUS_SUCCESS;
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)
175 return _cairo_rtree_pin (&cache->rtree, scaled_glyph->dev_private);
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)
184 cairo_gl_glyph_cache_t *cache;
185 cairo_content_t content;
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];
195 cache = &ctx->glyph_cache[2];
197 content = CAIRO_CONTENT_COLOR_ALPHA;
200 case CAIRO_FORMAT_A8:
201 case CAIRO_FORMAT_A1:
202 cache = &ctx->glyph_cache[1];
203 content = CAIRO_CONTENT_ALPHA;
206 case CAIRO_FORMAT_INVALID:
208 return _cairo_error (CAIRO_STATUS_INVALID_FORMAT);
211 if (unlikely (cache->surface == NULL)) {
212 cairo_surface_t *surface;
214 surface = _cairo_gl_surface_create_scratch_for_caching (ctx,
218 if (unlikely (surface->status))
219 return surface->status;
221 _cairo_surface_release_device_reference (surface);
223 cache->surface = (cairo_gl_surface_t *)surface;
224 cache->surface->operand.texture.attributes.has_component_alpha = has_component_alpha;
228 return CAIRO_STATUS_SUCCESS;
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 )
237 _cairo_gl_context_set_destination(ctx, dst, use_multisample);
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) {
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;
249 ctx->dispatch.ClearColor (0, 0, 0, 0);
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);
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);
265 static cairo_status_t
266 render_glyphs (cairo_gl_surface_t *dst,
267 int dst_x, int dst_y,
269 cairo_surface_t *source,
270 cairo_composite_glyphs_info_t *info,
271 cairo_bool_t *has_component_alpha,
273 cairo_bool_t *is_color_glyph)
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;
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));
287 *has_component_alpha = FALSE;
289 status = _cairo_gl_context_acquire (dst->base.device, &ctx);
290 if (unlikely (status))
293 status = _cairo_gl_composite_init (&setup, op, dst, TRUE);
294 if (unlikely (status))
297 if (source == NULL) {
298 _cairo_gl_composite_set_solid_source (&setup, CAIRO_COLOR_WHITE);
300 _cairo_gl_composite_set_source_operand (&setup,
301 source_to_operand (source));
305 if (setup.src.type == CAIRO_GL_OPERAND_CONSTANT)
306 setup.src.constant.encode_as_attribute = TRUE;
308 _cairo_gl_composite_set_clip (&setup, clip);
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;
316 status = _cairo_scaled_glyph_lookup (info->font,
317 info->glyphs[i].index,
318 CAIRO_SCALED_GLYPH_INFO_SURFACE,
320 if (unlikely (status))
323 if (scaled_glyph->surface->width == 0 ||
324 scaled_glyph->surface->height == 0)
328 if (! *has_component_alpha)
329 *has_component_alpha = pixman_image_get_component_alpha (scaled_glyph->surface->pixman_image);
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;
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,
342 if (unlikely (status))
345 last_format = scaled_glyph->surface->format;
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;
353 _cairo_gl_composite_set_mask_operand (&setup, &cache->surface->operand);
354 *is_color_glyph = FALSE;
358 if (dst->msaa_active)
359 _cairo_gl_composite_set_multisample (&setup);
361 status = _cairo_gl_composite_begin (&setup, &ctx);
362 status = _cairo_gl_context_release (ctx, status);
363 if (unlikely (status))
366 emit = _cairo_gl_context_choose_emit_glyph (ctx, *is_color_glyph);
369 if (scaled_glyph->dev_private_key != cache) {
370 cairo_scaled_glyph_private_t *priv;
372 priv = _cairo_scaled_glyph_find_private (scaled_glyph, cache);
374 scaled_glyph->dev_private_key = cache;
375 scaled_glyph->dev_private = cairo_container_of (priv,
380 status = CAIRO_STATUS_NULL_POINTER;
383 status = _cairo_gl_glyph_cache_add_glyph (ctx, cache, scaled_glyph);
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);
392 if (unlikely (_cairo_int_status_is_error (status)))
397 x_offset = scaled_glyph->surface->base.device_transform.x0;
398 y_offset = scaled_glyph->surface->base.device_transform.y0;
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;
406 status = CAIRO_STATUS_NULL_POINTER;
409 glyph = _cairo_gl_glyph_cache_lock (cache, scaled_glyph);
412 glyph->p1.x, glyph->p1.y,
413 glyph->p2.x, glyph->p2.y);
416 status = CAIRO_STATUS_SUCCESS;
418 status = _cairo_gl_context_release (ctx, status);
420 _cairo_gl_composite_fini (&setup);
425 static cairo_int_status_t
426 render_glyphs_via_mask (cairo_gl_surface_t *dst,
427 int dst_x, int dst_y,
429 cairo_surface_t *source,
430 cairo_composite_glyphs_info_t *info,
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;
438 int width = info->extents.width;
439 int height = info->extents.height;
441 TRACE ((stderr, "%s\n", __FUNCTION__));
443 status = _cairo_gl_context_acquire (dst->base.device, &ctx);
444 if (unlikely (status))
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;
453 cairo_surface_destroy (&ctx->glyph_mask->base);
454 ctx->glyph_mask = NULL;
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,
466 if (unlikely (ctx->glyph_mask->base.status)) {
467 status = ctx->glyph_mask->base.status;
468 status = _cairo_gl_context_release (ctx, status);
471 _cairo_surface_release_device_reference (&ctx->glyph_mask->base);
475 _cairo_gl_surface_clear_with_extent (ctx,
476 (cairo_gl_surface_t *)ctx->glyph_mask,
477 &info->extents, FALSE);
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,
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;
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;
496 cairo_matrix_init_translate (&mask_pattern.base.matrix,
497 dst_x-info->extents.x, dst_y-info->extents.y);
499 _cairo_pattern_init_for_surface (&source_pattern, source);
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);
509 if(op == CAIRO_OPERATOR_SOURCE) {
510 /* do dest_out then add*/
511 status = _cairo_surface_paint (&dst->base,
512 CAIRO_OPERATOR_DEST_OUT,
515 status = _cairo_surface_paint (&dst->base,
517 &mask_pattern.base, clip);
519 status = _cairo_surface_paint (&dst->base,op,
525 status = _cairo_surface_mask (&dst->base, op,
526 &source_pattern.base,
530 _cairo_clip_destroy (clip);
532 _cairo_pattern_fini (&mask_pattern.base);
533 _cairo_pattern_fini (&source_pattern.base);
536 status = _cairo_gl_context_release(ctx, status);
542 _cairo_gl_check_composite_glyphs (const cairo_composite_rectangles_t *extents,
543 cairo_scaled_font_t *scaled_font,
544 cairo_glyph_t *glyphs,
547 if (! _cairo_gl_operator_is_supported (extents->op))
548 return UNSUPPORTED ("unsupported operator");
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");
554 return CAIRO_STATUS_SUCCESS;
558 _cairo_gl_composite_glyphs_with_clip (void *_dst,
560 cairo_surface_t *_src,
565 cairo_composite_glyphs_info_t *info,
568 cairo_gl_surface_t *dst = _dst;
569 cairo_bool_t has_component_alpha;
570 cairo_bool_t is_color_glyph;
572 TRACE ((stderr, "%s\n", __FUNCTION__));
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
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))
582 info->use_mask = TRUE;
585 if (info->use_mask) {
586 return render_glyphs_via_mask (dst, dst_x, dst_y,
587 op, _src, info, clip);
589 return render_glyphs (dst, dst_x, dst_y,
591 &has_component_alpha,
592 clip, &is_color_glyph);
598 _cairo_gl_composite_glyphs (void *_dst,
600 cairo_surface_t *_src,
605 cairo_composite_glyphs_info_t *info)
607 return _cairo_gl_composite_glyphs_with_clip (_dst, op, _src, src_x, src_y,
608 dst_x, dst_y, info, NULL);
612 _cairo_gl_glyph_cache_init (cairo_gl_glyph_cache_t *cache)
614 _cairo_rtree_init (&cache->rtree,
617 GLYPH_CACHE_MIN_SIZE,
618 sizeof (cairo_gl_glyph_t),
619 _cairo_gl_node_destroy);
623 _cairo_gl_glyph_cache_fini (cairo_gl_context_t *ctx,
624 cairo_gl_glyph_cache_t *cache)
626 _cairo_rtree_fini (&cache->rtree);
627 cairo_surface_destroy (&cache->surface->base);