diff options
Diffstat (limited to 'libs/cairo-1.16.0/src/cairo-gl-glyphs.c')
-rw-r--r-- | libs/cairo-1.16.0/src/cairo-gl-glyphs.c | 503 |
1 files changed, 503 insertions, 0 deletions
diff --git a/libs/cairo-1.16.0/src/cairo-gl-glyphs.c b/libs/cairo-1.16.0/src/cairo-gl-glyphs.c new file mode 100644 index 0000000..5923af4 --- /dev/null +++ b/libs/cairo-1.16.0/src/cairo-gl-glyphs.c @@ -0,0 +1,503 @@ +/* Cairo - a vector graphics library with display and print output + * + * Copyright © 2009 Chris Wilson + * Copyright © 2010 Intel Corporation + * Copyright © 2010 Red Hat, Inc + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is Chris Wilson. + * + * Contributors: + * Benjamin Otte <otte@gnome.org> + * Chris Wilson <chris@chris-wilson.co.uk> + */ + +#include "cairoint.h" + +#include "cairo-gl-private.h" + +#include "cairo-compositor-private.h" +#include "cairo-composite-rectangles-private.h" +#include "cairo-error-private.h" +#include "cairo-image-surface-private.h" +#include "cairo-rtree-private.h" + +#define GLYPH_CACHE_WIDTH 1024 +#define GLYPH_CACHE_HEIGHT 1024 +#define GLYPH_CACHE_MIN_SIZE 4 +#define GLYPH_CACHE_MAX_SIZE 128 + +typedef struct _cairo_gl_glyph { + cairo_rtree_node_t node; + cairo_scaled_glyph_private_t base; + cairo_scaled_glyph_t *glyph; + cairo_gl_glyph_cache_t *cache; + struct { float x, y; } p1, p2; +} cairo_gl_glyph_t; + +static void +_cairo_gl_node_destroy (cairo_rtree_node_t *node) +{ + cairo_gl_glyph_t *priv = cairo_container_of (node, cairo_gl_glyph_t, node); + cairo_scaled_glyph_t *glyph; + + glyph = priv->glyph; + if (glyph == NULL) + return; + + if (glyph->dev_private_key == priv->cache) { + glyph->dev_private = NULL; + glyph->dev_private_key = NULL; + } + cairo_list_del (&priv->base.link); + priv->glyph = NULL; +} + +static void +_cairo_gl_glyph_fini (cairo_scaled_glyph_private_t *glyph_private, + cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_font_t *scaled_font) +{ + cairo_gl_glyph_t *priv = cairo_container_of (glyph_private, + cairo_gl_glyph_t, + base); + + assert (priv->glyph); + + _cairo_gl_node_destroy (&priv->node); + + /* XXX thread-safety? Probably ok due to the frozen scaled-font. */ + if (! priv->node.pinned) + _cairo_rtree_node_remove (&priv->cache->rtree, &priv->node); + + assert (priv->glyph == NULL); +} + +static cairo_int_status_t +_cairo_gl_glyph_cache_add_glyph (cairo_gl_context_t *ctx, + cairo_gl_glyph_cache_t *cache, + cairo_scaled_glyph_t *scaled_glyph) +{ + cairo_image_surface_t *glyph_surface = scaled_glyph->surface; + cairo_gl_glyph_t *glyph_private; + cairo_rtree_node_t *node = NULL; + cairo_int_status_t status; + int width, height; + + width = glyph_surface->width; + if (width < GLYPH_CACHE_MIN_SIZE) + width = GLYPH_CACHE_MIN_SIZE; + height = glyph_surface->height; + if (height < GLYPH_CACHE_MIN_SIZE) + height = GLYPH_CACHE_MIN_SIZE; + + /* search for an available slot */ + status = _cairo_rtree_insert (&cache->rtree, width, height, &node); + /* search for an unlocked slot */ + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + status = _cairo_rtree_evict_random (&cache->rtree, + width, height, &node); + if (status == CAIRO_INT_STATUS_SUCCESS) { + status = _cairo_rtree_node_insert (&cache->rtree, + node, width, height, &node); + } + } + if (status) + return status; + + /* XXX: Make sure we use the mask texture. This should work automagically somehow */ + glActiveTexture (GL_TEXTURE1); + status = _cairo_gl_surface_draw_image (cache->surface, glyph_surface, + 0, 0, + glyph_surface->width, glyph_surface->height, + node->x, node->y, FALSE); + if (unlikely (status)) + return status; + + glyph_private = (cairo_gl_glyph_t *) node; + glyph_private->cache = cache; + glyph_private->glyph = scaled_glyph; + _cairo_scaled_glyph_attach_private (scaled_glyph, + &glyph_private->base, + cache, + _cairo_gl_glyph_fini); + + scaled_glyph->dev_private = glyph_private; + scaled_glyph->dev_private_key = cache; + + /* compute tex coords */ + glyph_private->p1.x = node->x; + glyph_private->p1.y = node->y; + glyph_private->p2.x = node->x + glyph_surface->width; + glyph_private->p2.y = node->y + glyph_surface->height; + if (! _cairo_gl_device_requires_power_of_two_textures (&ctx->base)) { + glyph_private->p1.x /= GLYPH_CACHE_WIDTH; + glyph_private->p2.x /= GLYPH_CACHE_WIDTH; + glyph_private->p1.y /= GLYPH_CACHE_HEIGHT; + glyph_private->p2.y /= GLYPH_CACHE_HEIGHT; + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_gl_glyph_t * +_cairo_gl_glyph_cache_lock (cairo_gl_glyph_cache_t *cache, + cairo_scaled_glyph_t *scaled_glyph) +{ + return _cairo_rtree_pin (&cache->rtree, scaled_glyph->dev_private); +} + +static cairo_status_t +cairo_gl_context_get_glyph_cache (cairo_gl_context_t *ctx, + cairo_format_t format, + cairo_gl_glyph_cache_t **cache_out) +{ + cairo_gl_glyph_cache_t *cache; + cairo_content_t content; + + switch (format) { + case CAIRO_FORMAT_RGB30: + case CAIRO_FORMAT_RGB16_565: + case CAIRO_FORMAT_ARGB32: + case CAIRO_FORMAT_RGB24: + cache = &ctx->glyph_cache[0]; + content = CAIRO_CONTENT_COLOR_ALPHA; + break; + case CAIRO_FORMAT_A8: + case CAIRO_FORMAT_A1: + cache = &ctx->glyph_cache[1]; + content = CAIRO_CONTENT_ALPHA; + break; + default: + case CAIRO_FORMAT_INVALID: + ASSERT_NOT_REACHED; + return _cairo_error (CAIRO_STATUS_INVALID_FORMAT); + } + + if (unlikely (cache->surface == NULL)) { + cairo_surface_t *surface; + + surface = _cairo_gl_surface_create_scratch_for_caching (ctx, + content, + GLYPH_CACHE_WIDTH, + GLYPH_CACHE_HEIGHT); + if (unlikely (surface->status)) + return surface->status; + + _cairo_surface_release_device_reference (surface); + + cache->surface = (cairo_gl_surface_t *)surface; + cache->surface->operand.texture.attributes.has_component_alpha = + content == CAIRO_CONTENT_COLOR_ALPHA; + } + + *cache_out = cache; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +render_glyphs (cairo_gl_surface_t *dst, + int dst_x, int dst_y, + cairo_operator_t op, + cairo_surface_t *source, + cairo_composite_glyphs_info_t *info, + cairo_bool_t *has_component_alpha, + cairo_clip_t *clip) +{ + cairo_format_t last_format = CAIRO_FORMAT_INVALID; + cairo_gl_glyph_cache_t *cache = NULL; + cairo_gl_context_t *ctx; + cairo_gl_emit_glyph_t emit = NULL; + cairo_gl_composite_t setup; + cairo_int_status_t status; + int i = 0; + + TRACE ((stderr, "%s (%d, %d)x(%d, %d)\n", __FUNCTION__, + info->extents.x, info->extents.y, + info->extents.width, info->extents.height)); + + *has_component_alpha = FALSE; + + status = _cairo_gl_context_acquire (dst->base.device, &ctx); + if (unlikely (status)) + return status; + + status = _cairo_gl_composite_init (&setup, op, dst, TRUE); + if (unlikely (status)) + goto FINISH; + + if (source == NULL) { + _cairo_gl_composite_set_solid_source (&setup, CAIRO_COLOR_WHITE); + } else { + _cairo_gl_composite_set_source_operand (&setup, + source_to_operand (source)); + + } + + _cairo_gl_composite_set_clip (&setup, clip); + + for (i = 0; i < info->num_glyphs; i++) { + cairo_scaled_glyph_t *scaled_glyph; + cairo_gl_glyph_t *glyph; + double x_offset, y_offset; + double x1, x2, y1, y2; + + status = _cairo_scaled_glyph_lookup (info->font, + info->glyphs[i].index, + CAIRO_SCALED_GLYPH_INFO_SURFACE, + &scaled_glyph); + if (unlikely (status)) + goto FINISH; + + if (scaled_glyph->surface->width == 0 || + scaled_glyph->surface->height == 0) + { + continue; + } + if (scaled_glyph->surface->format != last_format) { + status = cairo_gl_context_get_glyph_cache (ctx, + scaled_glyph->surface->format, + &cache); + if (unlikely (status)) + goto FINISH; + + last_format = scaled_glyph->surface->format; + + _cairo_gl_composite_set_mask_operand (&setup, &cache->surface->operand); + *has_component_alpha |= cache->surface->operand.texture.attributes.has_component_alpha; + + /* XXX Shoot me. */ + status = _cairo_gl_composite_begin (&setup, &ctx); + status = _cairo_gl_context_release (ctx, status); + if (unlikely (status)) + goto FINISH; + + emit = _cairo_gl_context_choose_emit_glyph (ctx); + } + + if (scaled_glyph->dev_private_key != cache) { + cairo_scaled_glyph_private_t *priv; + + priv = _cairo_scaled_glyph_find_private (scaled_glyph, cache); + if (priv) { + scaled_glyph->dev_private_key = cache; + scaled_glyph->dev_private = cairo_container_of (priv, + cairo_gl_glyph_t, + base); + } else { + status = _cairo_gl_glyph_cache_add_glyph (ctx, cache, scaled_glyph); + + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + /* Cache is full, so flush existing prims and try again. */ + _cairo_gl_composite_flush (ctx); + _cairo_gl_glyph_cache_unlock (cache); + status = _cairo_gl_glyph_cache_add_glyph (ctx, cache, scaled_glyph); + } + + if (unlikely (_cairo_int_status_is_error (status))) + goto FINISH; + } + } + + x_offset = scaled_glyph->surface->base.device_transform.x0; + y_offset = scaled_glyph->surface->base.device_transform.y0; + + x1 = _cairo_lround (info->glyphs[i].x - x_offset - dst_x); + y1 = _cairo_lround (info->glyphs[i].y - y_offset - dst_y); + x2 = x1 + scaled_glyph->surface->width; + y2 = y1 + scaled_glyph->surface->height; + + glyph = _cairo_gl_glyph_cache_lock (cache, scaled_glyph); + assert (emit); + emit (ctx, + x1, y1, x2, y2, + glyph->p1.x, glyph->p1.y, + glyph->p2.x, glyph->p2.y); + } + + status = CAIRO_STATUS_SUCCESS; + FINISH: + status = _cairo_gl_context_release (ctx, status); + + _cairo_gl_composite_fini (&setup); + return status; +} + +static cairo_int_status_t +render_glyphs_via_mask (cairo_gl_surface_t *dst, + int dst_x, int dst_y, + cairo_operator_t op, + cairo_surface_t *source, + cairo_composite_glyphs_info_t *info, + cairo_clip_t *clip) +{ + cairo_surface_t *mask; + cairo_status_t status; + cairo_bool_t has_component_alpha; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + /* XXX: For non-CA, this should be CAIRO_CONTENT_ALPHA to save memory */ + mask = cairo_gl_surface_create (dst->base.device, + CAIRO_CONTENT_COLOR_ALPHA, + info->extents.width, + info->extents.height); + if (unlikely (mask->status)) + return mask->status; + + status = render_glyphs ((cairo_gl_surface_t *) mask, + info->extents.x, info->extents.y, + CAIRO_OPERATOR_ADD, NULL, + info, &has_component_alpha, NULL); + if (likely (status == CAIRO_STATUS_SUCCESS)) { + cairo_surface_pattern_t mask_pattern; + cairo_surface_pattern_t source_pattern; + cairo_rectangle_int_t clip_extents; + + mask->is_clear = FALSE; + _cairo_pattern_init_for_surface (&mask_pattern, mask); + mask_pattern.base.has_component_alpha = has_component_alpha; + mask_pattern.base.filter = CAIRO_FILTER_NEAREST; + mask_pattern.base.extend = CAIRO_EXTEND_NONE; + + cairo_matrix_init_translate (&mask_pattern.base.matrix, + dst_x-info->extents.x, dst_y-info->extents.y); + + _cairo_pattern_init_for_surface (&source_pattern, source); + cairo_matrix_init_translate (&source_pattern.base.matrix, + dst_x-info->extents.x, dst_y-info->extents.y); + + clip = _cairo_clip_copy (clip); + clip_extents.x = info->extents.x - dst_x; + clip_extents.y = info->extents.y - dst_y; + clip_extents.width = info->extents.width; + clip_extents.height = info->extents.height; + clip = _cairo_clip_intersect_rectangle (clip, &clip_extents); + + status = _cairo_surface_mask (&dst->base, op, + &source_pattern.base, + &mask_pattern.base, + clip); + + _cairo_clip_destroy (clip); + + _cairo_pattern_fini (&mask_pattern.base); + _cairo_pattern_fini (&source_pattern.base); + } + + cairo_surface_destroy (mask); + + return status; +} + +cairo_int_status_t +_cairo_gl_check_composite_glyphs (const cairo_composite_rectangles_t *extents, + cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + int *num_glyphs) +{ + if (! _cairo_gl_operator_is_supported (extents->op)) + return UNSUPPORTED ("unsupported operator"); + + /* XXX use individual masks for large glyphs? */ + if (ceil (scaled_font->max_scale) >= GLYPH_CACHE_MAX_SIZE) + return UNSUPPORTED ("glyphs too large"); + + return CAIRO_STATUS_SUCCESS; +} + +cairo_int_status_t +_cairo_gl_composite_glyphs_with_clip (void *_dst, + cairo_operator_t op, + cairo_surface_t *_src, + int src_x, + int src_y, + int dst_x, + int dst_y, + cairo_composite_glyphs_info_t *info, + cairo_clip_t *clip) +{ + cairo_gl_surface_t *dst = _dst; + cairo_bool_t has_component_alpha; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + /* If any of the glyphs require component alpha, we have to go through + * a mask, since only _cairo_gl_surface_composite() currently supports + * component alpha. + */ + if (!dst->base.is_clear && ! info->use_mask && op != CAIRO_OPERATOR_OVER && + (info->font->options.antialias == CAIRO_ANTIALIAS_SUBPIXEL || + info->font->options.antialias == CAIRO_ANTIALIAS_BEST)) + { + info->use_mask = TRUE; + } + + if (info->use_mask) { + return render_glyphs_via_mask (dst, dst_x, dst_y, + op, _src, info, clip); + } else { + return render_glyphs (dst, dst_x, dst_y, + op, _src, info, + &has_component_alpha, + clip); + } + +} + +cairo_int_status_t +_cairo_gl_composite_glyphs (void *_dst, + cairo_operator_t op, + cairo_surface_t *_src, + int src_x, + int src_y, + int dst_x, + int dst_y, + cairo_composite_glyphs_info_t *info) +{ + return _cairo_gl_composite_glyphs_with_clip (_dst, op, _src, src_x, src_y, + dst_x, dst_y, info, NULL); +} + +void +_cairo_gl_glyph_cache_init (cairo_gl_glyph_cache_t *cache) +{ + _cairo_rtree_init (&cache->rtree, + GLYPH_CACHE_WIDTH, + GLYPH_CACHE_HEIGHT, + GLYPH_CACHE_MIN_SIZE, + sizeof (cairo_gl_glyph_t), + _cairo_gl_node_destroy); +} + +void +_cairo_gl_glyph_cache_fini (cairo_gl_context_t *ctx, + cairo_gl_glyph_cache_t *cache) +{ + _cairo_rtree_fini (&cache->rtree); + cairo_surface_destroy (&cache->surface->base); +} |