Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | RSS feed

  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.     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;
  140.  
  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);
  148.  
  149.     scaled_glyph->dev_private = glyph_private;
  150.     scaled_glyph->dev_private_key = cache;
  151.  
  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;
  162.     }
  163.  
  164.     return CAIRO_STATUS_SUCCESS;
  165. }
  166.  
  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)
  170. {
  171.     return _cairo_rtree_pin (&cache->rtree, scaled_glyph->dev_private);
  172. }
  173.  
  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)
  178. {
  179.     cairo_gl_glyph_cache_t *cache;
  180.     cairo_content_t content;
  181.  
  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);
  199.     }
  200.  
  201.     if (unlikely (cache->surface == NULL)) {
  202.         cairo_surface_t *surface;
  203.  
  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;
  210.  
  211.         _cairo_surface_release_device_reference (surface);
  212.  
  213.         cache->surface = (cairo_gl_surface_t *)surface;
  214.         cache->surface->operand.texture.attributes.has_component_alpha =
  215.             content == CAIRO_CONTENT_COLOR_ALPHA;
  216.     }
  217.  
  218.     *cache_out = cache;
  219.     return CAIRO_STATUS_SUCCESS;
  220. }
  221.  
  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)
  230. {
  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;
  238.  
  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));
  242.  
  243.     *has_component_alpha = FALSE;
  244.  
  245.     status = _cairo_gl_context_acquire (dst->base.device, &ctx);
  246.     if (unlikely (status))
  247.         return status;
  248.  
  249.     status = _cairo_gl_composite_init (&setup, op, dst, TRUE);
  250.     if (unlikely (status))
  251.         goto FINISH;
  252.  
  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));
  258.  
  259.     }
  260.  
  261.     _cairo_gl_composite_set_clip (&setup, clip);
  262.  
  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;
  268.  
  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;
  275.  
  276.         if (scaled_glyph->surface->width  == 0 ||
  277.             scaled_glyph->surface->height == 0)
  278.         {
  279.             continue;
  280.         }
  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;
  287.  
  288.             last_format = scaled_glyph->surface->format;
  289.  
  290.             _cairo_gl_composite_set_mask_operand (&setup, &cache->surface->operand);
  291.             *has_component_alpha |= cache->surface->operand.texture.attributes.has_component_alpha;
  292.  
  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;
  298.  
  299.             emit = _cairo_gl_context_choose_emit_glyph (ctx);
  300.         }
  301.  
  302.         if (scaled_glyph->dev_private_key != cache) {
  303.             cairo_scaled_glyph_private_t *priv;
  304.  
  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);
  313.  
  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);
  319.                 }
  320.  
  321.                 if (unlikely (_cairo_int_status_is_error (status)))
  322.                     goto FINISH;
  323.             }
  324.         }
  325.  
  326.         x_offset = scaled_glyph->surface->base.device_transform.x0;
  327.         y_offset = scaled_glyph->surface->base.device_transform.y0;
  328.  
  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;
  333.  
  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);
  340.     }
  341.  
  342.     status = CAIRO_STATUS_SUCCESS;
  343.   FINISH:
  344.     status = _cairo_gl_context_release (ctx, status);
  345.  
  346.     _cairo_gl_composite_fini (&setup);
  347.     return status;
  348. }
  349.  
  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)
  357. {
  358.     cairo_surface_t *mask;
  359.     cairo_status_t status;
  360.     cairo_bool_t has_component_alpha;
  361.  
  362.     TRACE ((stderr, "%s\n", __FUNCTION__));
  363.  
  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;
  371.  
  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;
  380.  
  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;
  386.  
  387.         cairo_matrix_init_translate (&mask_pattern.base.matrix,
  388.                                      dst_x-info->extents.x, dst_y-info->extents.y);
  389.  
  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);
  393.  
  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);
  400.  
  401.         status = _cairo_surface_mask (&dst->base, op,
  402.                                       &source_pattern.base,
  403.                                       &mask_pattern.base,
  404.                                       clip);
  405.  
  406.         _cairo_clip_destroy (clip);
  407.  
  408.         _cairo_pattern_fini (&mask_pattern.base);
  409.         _cairo_pattern_fini (&source_pattern.base);
  410.     }
  411.  
  412.     cairo_surface_destroy (mask);
  413.  
  414.     return status;
  415. }
  416.  
  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)
  422. {
  423.     if (! _cairo_gl_operator_is_supported (extents->op))
  424.         return UNSUPPORTED ("unsupported operator");
  425.  
  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");
  429.  
  430.     return CAIRO_STATUS_SUCCESS;
  431. }
  432.  
  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)
  443. {
  444.     cairo_gl_surface_t *dst = _dst;
  445.     cairo_bool_t has_component_alpha;
  446.  
  447.     TRACE ((stderr, "%s\n", __FUNCTION__));
  448.  
  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.
  452.      */
  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))
  456.     {
  457.         info->use_mask = TRUE;
  458.     }
  459.  
  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);
  468.     }
  469.  
  470. }
  471.  
  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)
  481. {
  482.     return _cairo_gl_composite_glyphs_with_clip (_dst, op, _src, src_x, src_y,
  483.                                                  dst_x, dst_y, info, NULL);
  484. }
  485.  
  486. void
  487. _cairo_gl_glyph_cache_init (cairo_gl_glyph_cache_t *cache)
  488. {
  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);
  495. }
  496.  
  497. void
  498. _cairo_gl_glyph_cache_fini (cairo_gl_context_t *ctx,
  499.                             cairo_gl_glyph_cache_t *cache)
  500. {
  501.     _cairo_rtree_fini (&cache->rtree);
  502.     cairo_surface_destroy (&cache->surface->base);
  503. }
  504.