summaryrefslogtreecommitdiff
path: root/src/cairo-gl-glyphs.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cairo-gl-glyphs.c')
-rw-r--r--src/cairo-gl-glyphs.c503
1 files changed, 503 insertions, 0 deletions
diff --git a/src/cairo-gl-glyphs.c b/src/cairo-gl-glyphs.c
new file mode 100644
index 000000000..8a1a548d9
--- /dev/null
+++ b/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);
+}