LCOV - code coverage report
Current view: top level - gfx/cairo/cairo/src - cairo-scaled-font.c (source / functions) Hit Total Coverage
Test: output.info Lines: 337 1091 30.9 %
Date: 2017-07-14 16:53:18 Functions: 23 60 38.3 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
       2             : /*
       3             :  * Copyright © 2005 Keith Packard
       4             :  *
       5             :  * This library is free software; you can redistribute it and/or
       6             :  * modify it either under the terms of the GNU Lesser General Public
       7             :  * License version 2.1 as published by the Free Software Foundation
       8             :  * (the "LGPL") or, at your option, under the terms of the Mozilla
       9             :  * Public License Version 1.1 (the "MPL"). If you do not alter this
      10             :  * notice, a recipient may use your version of this file under either
      11             :  * the MPL or the LGPL.
      12             :  *
      13             :  * You should have received a copy of the LGPL along with this library
      14             :  * in the file COPYING-LGPL-2.1; if not, write to the Free Software
      15             :  * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
      16             :  * You should have received a copy of the MPL along with this library
      17             :  * in the file COPYING-MPL-1.1
      18             :  *
      19             :  * The contents of this file are subject to the Mozilla Public License
      20             :  * Version 1.1 (the "License"); you may not use this file except in
      21             :  * compliance with the License. You may obtain a copy of the License at
      22             :  * http://www.mozilla.org/MPL/
      23             :  *
      24             :  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
      25             :  * OF ANY KIND, either express or implied. See the LGPL or the MPL for
      26             :  * the specific language governing rights and limitations.
      27             :  *
      28             :  * The Original Code is the cairo graphics library.
      29             :  *
      30             :  * The Initial Developer of the Original Code is Keith Packard
      31             :  *
      32             :  * Contributor(s):
      33             :  *      Keith Packard <keithp@keithp.com>
      34             :  *      Carl D. Worth <cworth@cworth.org>
      35             :  *      Graydon Hoare <graydon@redhat.com>
      36             :  *      Owen Taylor <otaylor@redhat.com>
      37             :  *      Behdad Esfahbod <behdad@behdad.org>
      38             :  *      Chris Wilson <chris@chris-wilson.co.uk>
      39             :  */
      40             : 
      41             : #include "cairoint.h"
      42             : #include "cairo-error-private.h"
      43             : #include "cairo-scaled-font-private.h"
      44             : 
      45             : #if _XOPEN_SOURCE >= 600 || defined (_ISOC99_SOURCE)
      46             : #define ISFINITE(x) isfinite (x)
      47             : #else
      48             : #define ISFINITE(x) ((x) * (x) >= 0.) /* check for NaNs */
      49             : #endif
      50             : 
      51             : /**
      52             :  * SECTION:cairo-scaled-font
      53             :  * @Title: cairo_scaled_font_t
      54             :  * @Short_Description: Font face at particular size and options
      55             :  * @See_Also: #cairo_font_face_t, #cairo_matrix_t, #cairo_font_options_t
      56             :  *
      57             :  * #cairo_scaled_font_t represents a realization of a font face at a particular
      58             :  * size and transformation and a certain set of font options.
      59             :  */
      60             : 
      61             : /* Global Glyph Cache
      62             :  *
      63             :  * We maintain a global pool of glyphs split between all active fonts. This
      64             :  * allows a heavily used individual font to cache more glyphs than we could
      65             :  * manage if we used per-font glyph caches, but at the same time maintains
      66             :  * fairness across all fonts and provides a cap on the maximum number of
      67             :  * global glyphs.
      68             :  *
      69             :  * The glyphs are allocated in pages, which are capped in the global pool.
      70             :  * Using pages means we can reduce the frequency at which we have to probe the
      71             :  * global pool and ameliorates the memory allocation pressure.
      72             :  */
      73             : 
      74             : /* XXX: This number is arbitrary---we've never done any measurement of this. */
      75             : #define MAX_GLYPH_PAGES_CACHED 256
      76             : static cairo_cache_t cairo_scaled_glyph_page_cache;
      77             : 
      78             : #define CAIRO_SCALED_GLYPH_PAGE_SIZE 32
      79             : struct _cairo_scaled_glyph_page {
      80             :     cairo_cache_entry_t cache_entry;
      81             : 
      82             :     cairo_list_t link;
      83             : 
      84             :     unsigned int num_glyphs;
      85             :     cairo_scaled_glyph_t glyphs[CAIRO_SCALED_GLYPH_PAGE_SIZE];
      86             : };
      87             : 
      88             : /*
      89             :  *  Notes:
      90             :  *
      91             :  *  To store rasterizations of glyphs, we use an image surface and the
      92             :  *  device offset to represent the glyph origin.
      93             :  *
      94             :  *  A device_transform converts from device space (a conceptual space) to
      95             :  *  surface space.  For simple cases of translation only, it's called a
      96             :  *  device_offset and is public API (cairo_surface_[gs]et_device_offset()).
      97             :  *  A possibly better name for those functions could have been
      98             :  *  cairo_surface_[gs]et_origin().  So, that's what they do: they set where
      99             :  *  the device-space origin (0,0) is in the surface.  If the origin is inside
     100             :  *  the surface, device_offset values are positive.  It may look like this:
     101             :  *
     102             :  *  Device space:
     103             :  *        (-x,-y) <-- negative numbers
     104             :  *           +----------------+
     105             :  *           |      .         |
     106             :  *           |      .         |
     107             :  *           |......(0,0) <---|-- device-space origin
     108             :  *           |                |
     109             :  *           |                |
     110             :  *           +----------------+
     111             :  *                    (width-x,height-y)
     112             :  *
     113             :  *  Surface space:
     114             :  *         (0,0) <-- surface-space origin
     115             :  *           +---------------+
     116             :  *           |      .        |
     117             :  *           |      .        |
     118             :  *           |......(x,y) <--|-- device_offset
     119             :  *           |               |
     120             :  *           |               |
     121             :  *           +---------------+
     122             :  *                     (width,height)
     123             :  *
     124             :  *  In other words: device_offset is the coordinates of the device-space
     125             :  *  origin relative to the top-left of the surface.
     126             :  *
     127             :  *  We use device offsets in a couple of places:
     128             :  *
     129             :  *    - Public API: To let toolkits like Gtk+ give user a surface that
     130             :  *      only represents part of the final destination (say, the expose
     131             :  *      area), but has the same device space as the destination.  In these
     132             :  *      cases device_offset is typically negative.  Example:
     133             :  *
     134             :  *           application window
     135             :  *           +---------------+
     136             :  *           |      .        |
     137             :  *           | (x,y).        |
     138             :  *           |......+---+    |
     139             :  *           |      |   | <--|-- expose area
     140             :  *           |      +---+    |
     141             :  *           +---------------+
     142             :  *
     143             :  *      In this case, the user of cairo API can set the device_space on
     144             :  *      the expose area to (-x,-y) to move the device space origin to that
     145             :  *      of the application window, such that drawing in the expose area
     146             :  *      surface and painting it in the application window has the same
     147             :  *      effect as drawing in the application window directly.  Gtk+ has
     148             :  *      been using this feature.
     149             :  *
     150             :  *    - Glyph surfaces: In most font rendering systems, glyph surfaces
     151             :  *      have an origin at (0,0) and a bounding box that is typically
     152             :  *      represented as (x_bearing,y_bearing,width,height).  Depending on
     153             :  *      which way y progresses in the system, y_bearing may typically be
     154             :  *      negative (for systems similar to cairo, with origin at top left),
     155             :  *      or be positive (in systems like PDF with origin at bottom left).
     156             :  *      No matter which is the case, it is important to note that
     157             :  *      (x_bearing,y_bearing) is the coordinates of top-left of the glyph
     158             :  *      relative to the glyph origin.  That is, for example:
     159             :  *
     160             :  *      Scaled-glyph space:
     161             :  *
     162             :  *        (x_bearing,y_bearing) <-- negative numbers
     163             :  *           +----------------+
     164             :  *           |      .         |
     165             :  *           |      .         |
     166             :  *           |......(0,0) <---|-- glyph origin
     167             :  *           |                |
     168             :  *           |                |
     169             :  *           +----------------+
     170             :  *                    (width+x_bearing,height+y_bearing)
     171             :  *
     172             :  *      Note the similarity of the origin to the device space.  That is
     173             :  *      exactly how we use the device_offset to represent scaled glyphs:
     174             :  *      to use the device-space origin as the glyph origin.
     175             :  *
     176             :  *  Now compare the scaled-glyph space to device-space and surface-space
     177             :  *  and convince yourself that:
     178             :  *
     179             :  *      (x_bearing,y_bearing) = (-x,-y) = - device_offset
     180             :  *
     181             :  *  That's right.  If you are not convinced yet, contrast the definition
     182             :  *  of the two:
     183             :  *
     184             :  *      "(x_bearing,y_bearing) is the coordinates of top-left of the
     185             :  *       glyph relative to the glyph origin."
     186             :  *
     187             :  *      "In other words: device_offset is the coordinates of the
     188             :  *       device-space origin relative to the top-left of the surface."
     189             :  *
     190             :  *  and note that glyph origin = device-space origin.
     191             :  */
     192             : 
     193             : static void
     194             : _cairo_scaled_font_fini_internal (cairo_scaled_font_t *scaled_font);
     195             : 
     196             : static void
     197           0 : _cairo_scaled_glyph_fini (cairo_scaled_font_t *scaled_font,
     198             :                           cairo_scaled_glyph_t *scaled_glyph)
     199             : {
     200           0 :     const cairo_surface_backend_t *surface_backend = scaled_font->surface_backend;
     201             : 
     202           0 :     if (surface_backend != NULL && surface_backend->scaled_glyph_fini != NULL)
     203           0 :         surface_backend->scaled_glyph_fini (scaled_glyph, scaled_font);
     204             : 
     205           0 :     if (scaled_glyph->surface != NULL)
     206           0 :         cairo_surface_destroy (&scaled_glyph->surface->base);
     207             : 
     208           0 :     if (scaled_glyph->path != NULL)
     209           0 :         _cairo_path_fixed_destroy (scaled_glyph->path);
     210             : 
     211           0 :     if (scaled_glyph->recording_surface != NULL) {
     212           0 :         cairo_surface_finish (scaled_glyph->recording_surface);
     213           0 :         cairo_surface_destroy (scaled_glyph->recording_surface);
     214             :     }
     215           0 : }
     216             : 
     217             : #define ZOMBIE 0
     218             : static const cairo_scaled_font_t _cairo_scaled_font_nil = {
     219             :     { ZOMBIE },                 /* hash_entry */
     220             :     CAIRO_STATUS_NO_MEMORY,     /* status */
     221             :     CAIRO_REFERENCE_COUNT_INVALID,      /* ref_count */
     222             :     { 0, 0, 0, NULL },          /* user_data */
     223             :     NULL,                       /* original_font_face */
     224             :     NULL,                       /* font_face */
     225             :     { 1., 0., 0., 1., 0, 0},    /* font_matrix */
     226             :     { 1., 0., 0., 1., 0, 0},    /* ctm */
     227             :     { CAIRO_ANTIALIAS_DEFAULT,  /* options */
     228             :       CAIRO_SUBPIXEL_ORDER_DEFAULT,
     229             :       CAIRO_HINT_STYLE_DEFAULT,
     230             :       CAIRO_HINT_METRICS_DEFAULT} ,
     231             :     FALSE,                      /* placeholder */
     232             :     FALSE,                      /* holdover */
     233             :     TRUE,                       /* finished */
     234             :     { 1., 0., 0., 1., 0, 0},    /* scale */
     235             :     { 1., 0., 0., 1., 0, 0},    /* scale_inverse */
     236             :     1.,                         /* max_scale */
     237             :     { 0., 0., 0., 0., 0. },     /* extents */
     238             :     { 0., 0., 0., 0., 0. },     /* fs_extents */
     239             :     CAIRO_MUTEX_NIL_INITIALIZER,/* mutex */
     240             :     NULL,                       /* glyphs */
     241             :     { NULL, NULL },             /* pages */
     242             :     FALSE,                      /* cache_frozen */
     243             :     FALSE,                      /* global_cache_frozen */
     244             :     NULL,                       /* surface_backend */
     245             :     NULL,                       /* surface_private */
     246             :     NULL                        /* backend */
     247             : };
     248             : 
     249             : /**
     250             :  * _cairo_scaled_font_set_error:
     251             :  * @scaled_font: a scaled_font
     252             :  * @status: a status value indicating an error
     253             :  *
     254             :  * Atomically sets scaled_font->status to @status and calls _cairo_error;
     255             :  * Does nothing if status is %CAIRO_STATUS_SUCCESS.
     256             :  *
     257             :  * All assignments of an error status to scaled_font->status should happen
     258             :  * through _cairo_scaled_font_set_error(). Note that due to the nature of
     259             :  * the atomic operation, it is not safe to call this function on the nil
     260             :  * objects.
     261             :  *
     262             :  * The purpose of this function is to allow the user to set a
     263             :  * breakpoint in _cairo_error() to generate a stack trace for when the
     264             :  * user causes cairo to detect an error.
     265             :  *
     266             :  * Return value: the error status.
     267             :  **/
     268             : cairo_status_t
     269           0 : _cairo_scaled_font_set_error (cairo_scaled_font_t *scaled_font,
     270             :                               cairo_status_t status)
     271             : {
     272           0 :     if (status == CAIRO_STATUS_SUCCESS)
     273           0 :         return status;
     274             : 
     275             :     /* Don't overwrite an existing error. This preserves the first
     276             :      * error, which is the most significant. */
     277           0 :     _cairo_status_set_error (&scaled_font->status, status);
     278             : 
     279           0 :     return _cairo_error (status);
     280             : }
     281             : 
     282             : /**
     283             :  * cairo_scaled_font_get_type:
     284             :  * @scaled_font: a #cairo_scaled_font_t
     285             :  *
     286             :  * This function returns the type of the backend used to create
     287             :  * a scaled font. See #cairo_font_type_t for available types.
     288             :  * However, this function never returns %CAIRO_FONT_TYPE_TOY.
     289             :  *
     290             :  * Return value: The type of @scaled_font.
     291             :  *
     292             :  * Since: 1.2
     293             :  **/
     294             : cairo_font_type_t
     295           0 : cairo_scaled_font_get_type (cairo_scaled_font_t *scaled_font)
     296             : {
     297           0 :     if (CAIRO_REFERENCE_COUNT_IS_INVALID (&scaled_font->ref_count))
     298           0 :         return CAIRO_FONT_TYPE_TOY;
     299             : 
     300           0 :     return scaled_font->backend->type;
     301             : }
     302             : 
     303             : /**
     304             :  * cairo_scaled_font_status:
     305             :  * @scaled_font: a #cairo_scaled_font_t
     306             :  *
     307             :  * Checks whether an error has previously occurred for this
     308             :  * scaled_font.
     309             :  *
     310             :  * Return value: %CAIRO_STATUS_SUCCESS or another error such as
     311             :  *   %CAIRO_STATUS_NO_MEMORY.
     312             :  **/
     313             : cairo_status_t
     314         175 : cairo_scaled_font_status (cairo_scaled_font_t *scaled_font)
     315             : {
     316         175 :     return scaled_font->status;
     317             : }
     318             : slim_hidden_def (cairo_scaled_font_status);
     319             : 
     320             : /* Here we keep a unique mapping from
     321             :  * font_face/matrix/ctm/font_options => #cairo_scaled_font_t.
     322             :  *
     323             :  * Here are the things that we want to map:
     324             :  *
     325             :  *  a) All otherwise referenced #cairo_scaled_font_t's
     326             :  *  b) Some number of not otherwise referenced #cairo_scaled_font_t's
     327             :  *
     328             :  * The implementation uses a hash table which covers (a)
     329             :  * completely. Then, for (b) we have an array of otherwise
     330             :  * unreferenced fonts (holdovers) which are expired in
     331             :  * least-recently-used order.
     332             :  *
     333             :  * The cairo_scaled_font_create() code gets to treat this like a regular
     334             :  * hash table. All of the magic for the little holdover cache is in
     335             :  * cairo_scaled_font_reference() and cairo_scaled_font_destroy().
     336             :  */
     337             : 
     338             : /* This defines the size of the holdover array ... that is, the number
     339             :  * of scaled fonts we keep around even when not otherwise referenced
     340             :  */
     341             : #define CAIRO_SCALED_FONT_MAX_HOLDOVERS 256
     342             : 
     343             : typedef struct _cairo_scaled_font_map {
     344             :     cairo_scaled_font_t *mru_scaled_font;
     345             :     cairo_hash_table_t *hash_table;
     346             :     cairo_scaled_font_t *holdovers[CAIRO_SCALED_FONT_MAX_HOLDOVERS];
     347             :     int num_holdovers;
     348             : } cairo_scaled_font_map_t;
     349             : 
     350             : static cairo_scaled_font_map_t *cairo_scaled_font_map;
     351             : 
     352             : static int
     353             : _cairo_scaled_font_keys_equal (const void *abstract_key_a, const void *abstract_key_b);
     354             : 
     355             : static cairo_scaled_font_map_t *
     356          12 : _cairo_scaled_font_map_lock (void)
     357             : {
     358          12 :     CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex);
     359             : 
     360          12 :     if (cairo_scaled_font_map == NULL) {
     361           3 :         cairo_scaled_font_map = malloc (sizeof (cairo_scaled_font_map_t));
     362           3 :         if (unlikely (cairo_scaled_font_map == NULL))
     363           0 :             goto CLEANUP_MUTEX_LOCK;
     364             : 
     365           3 :         cairo_scaled_font_map->mru_scaled_font = NULL;
     366           6 :         cairo_scaled_font_map->hash_table =
     367           3 :             _cairo_hash_table_create (_cairo_scaled_font_keys_equal);
     368             : 
     369           3 :         if (unlikely (cairo_scaled_font_map->hash_table == NULL))
     370           0 :             goto CLEANUP_SCALED_FONT_MAP;
     371             : 
     372           3 :         cairo_scaled_font_map->num_holdovers = 0;
     373             :     }
     374             : 
     375          12 :     return cairo_scaled_font_map;
     376             : 
     377             :  CLEANUP_SCALED_FONT_MAP:
     378           0 :     free (cairo_scaled_font_map);
     379           0 :     cairo_scaled_font_map = NULL;
     380             :  CLEANUP_MUTEX_LOCK:
     381           0 :     CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex);
     382           0 :     _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
     383           0 :     return NULL;
     384             : }
     385             : 
     386             : static void
     387          12 : _cairo_scaled_font_map_unlock (void)
     388             : {
     389          12 :    CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex);
     390          12 : }
     391             : 
     392             : void
     393           0 : _cairo_scaled_font_map_destroy (void)
     394             : {
     395             :     cairo_scaled_font_map_t *font_map;
     396             :     cairo_scaled_font_t *scaled_font;
     397             : 
     398           0 :     CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex);
     399             : 
     400           0 :     font_map = cairo_scaled_font_map;
     401           0 :     if (unlikely (font_map == NULL)) {
     402           0 :         goto CLEANUP_MUTEX_LOCK;
     403             :     }
     404             : 
     405           0 :     scaled_font = font_map->mru_scaled_font;
     406           0 :     if (scaled_font != NULL) {
     407           0 :         CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex);
     408           0 :         cairo_scaled_font_destroy (scaled_font);
     409           0 :         CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex);
     410             :     }
     411             : 
     412             :     /* remove scaled_fonts starting from the end so that font_map->holdovers
     413             :      * is always in a consistent state when we release the mutex. */
     414           0 :     while (font_map->num_holdovers) {
     415           0 :         scaled_font = font_map->holdovers[font_map->num_holdovers-1];
     416           0 :         assert (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&scaled_font->ref_count));
     417           0 :         _cairo_hash_table_remove (font_map->hash_table,
     418             :                                   &scaled_font->hash_entry);
     419             : 
     420           0 :         font_map->num_holdovers--;
     421             : 
     422             :         /* This releases the font_map lock to avoid the possibility of a
     423             :          * recursive deadlock when the scaled font destroy closure gets
     424             :          * called
     425             :          */
     426           0 :         _cairo_scaled_font_fini (scaled_font);
     427             : 
     428           0 :         free (scaled_font);
     429             :     }
     430             : 
     431           0 :     _cairo_hash_table_destroy (font_map->hash_table);
     432             : 
     433           0 :     free (cairo_scaled_font_map);
     434           0 :     cairo_scaled_font_map = NULL;
     435             : 
     436             :  CLEANUP_MUTEX_LOCK:
     437           0 :     CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex);
     438           0 : }
     439             : static void
     440           0 : _cairo_scaled_glyph_page_destroy (void *closure)
     441             : {
     442           0 :     cairo_scaled_glyph_page_t *page = closure;
     443             :     cairo_scaled_font_t *scaled_font;
     444             :     unsigned int n;
     445             : 
     446           0 :     scaled_font = (cairo_scaled_font_t *) page->cache_entry.hash;
     447           0 :     for (n = 0; n < page->num_glyphs; n++) {
     448           0 :         _cairo_hash_table_remove (scaled_font->glyphs,
     449             :                                   &page->glyphs[n].hash_entry);
     450           0 :         _cairo_scaled_glyph_fini (scaled_font, &page->glyphs[n]);
     451             :     }
     452             : 
     453           0 :     cairo_list_del (&page->link);
     454             : 
     455           0 :     free (page);
     456           0 : }
     457             : 
     458             : /* If a scaled font wants to unlock the font map while still being
     459             :  * created (needed for user-fonts), we need to take extra care not
     460             :  * ending up with multiple identical scaled fonts being created.
     461             :  *
     462             :  * What we do is, we create a fake identical scaled font, and mark
     463             :  * it as placeholder, lock its mutex, and insert that in the fontmap
     464             :  * hash table.  This makes other code trying to create an identical
     465             :  * scaled font to just wait and retry.
     466             :  *
     467             :  * The reason we have to create a fake scaled font instead of just using
     468             :  * scaled_font is for lifecycle management: we need to (or rather,
     469             :  * other code needs to) reference the scaled_font in the hash table.
     470             :  * We can't do that on the input scaled_font as it may be freed by
     471             :  * font backend upon error.
     472             :  */
     473             : 
     474             : cairo_status_t
     475           0 : _cairo_scaled_font_register_placeholder_and_unlock_font_map (cairo_scaled_font_t *scaled_font)
     476             : {
     477             :     cairo_status_t status;
     478             :     cairo_scaled_font_t *placeholder_scaled_font;
     479             : 
     480             :     assert (CAIRO_MUTEX_IS_LOCKED (_cairo_scaled_font_map_mutex));
     481             : 
     482           0 :     status = scaled_font->status;
     483           0 :     if (unlikely (status))
     484           0 :         return status;
     485             : 
     486           0 :     placeholder_scaled_font = malloc (sizeof (cairo_scaled_font_t));
     487           0 :     if (unlikely (placeholder_scaled_font == NULL))
     488           0 :         return _cairo_error (CAIRO_STATUS_NO_MEMORY);
     489             : 
     490             :     /* full initialization is wasteful, but who cares... */
     491           0 :     status = _cairo_scaled_font_init (placeholder_scaled_font,
     492             :                                       scaled_font->font_face,
     493           0 :                                       &scaled_font->font_matrix,
     494           0 :                                       &scaled_font->ctm,
     495           0 :                                       &scaled_font->options,
     496             :                                       NULL);
     497           0 :     if (unlikely (status))
     498           0 :         goto FREE_PLACEHOLDER;
     499             : 
     500           0 :     placeholder_scaled_font->placeholder = TRUE;
     501             : 
     502           0 :     status = _cairo_hash_table_insert (cairo_scaled_font_map->hash_table,
     503             :                                        &placeholder_scaled_font->hash_entry);
     504           0 :     if (unlikely (status))
     505           0 :         goto FINI_PLACEHOLDER;
     506             : 
     507           0 :     CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex);
     508           0 :     CAIRO_MUTEX_LOCK (placeholder_scaled_font->mutex);
     509             : 
     510           0 :     return CAIRO_STATUS_SUCCESS;
     511             : 
     512             :   FINI_PLACEHOLDER:
     513           0 :     _cairo_scaled_font_fini_internal (placeholder_scaled_font);
     514             :   FREE_PLACEHOLDER:
     515           0 :     free (placeholder_scaled_font);
     516             : 
     517           0 :     return _cairo_scaled_font_set_error (scaled_font, status);
     518             : }
     519             : 
     520             : void
     521           0 : _cairo_scaled_font_unregister_placeholder_and_lock_font_map (cairo_scaled_font_t *scaled_font)
     522             : {
     523             :     cairo_scaled_font_t *placeholder_scaled_font;
     524             : 
     525           0 :     CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex);
     526             : 
     527           0 :     placeholder_scaled_font =
     528           0 :         _cairo_hash_table_lookup (cairo_scaled_font_map->hash_table,
     529             :                                   &scaled_font->hash_entry);
     530           0 :     assert (placeholder_scaled_font != NULL);
     531           0 :     assert (placeholder_scaled_font->placeholder);
     532             :     assert (CAIRO_MUTEX_IS_LOCKED (placeholder_scaled_font->mutex));
     533             : 
     534           0 :     _cairo_hash_table_remove (cairo_scaled_font_map->hash_table,
     535             :                               &placeholder_scaled_font->hash_entry);
     536             : 
     537           0 :     CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex);
     538             : 
     539           0 :     CAIRO_MUTEX_UNLOCK (placeholder_scaled_font->mutex);
     540           0 :     cairo_scaled_font_destroy (placeholder_scaled_font);
     541             : 
     542           0 :     CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex);
     543           0 : }
     544             : 
     545             : static void
     546           0 : _cairo_scaled_font_placeholder_wait_for_creation_to_finish (cairo_scaled_font_t *placeholder_scaled_font)
     547             : {
     548             :     /* reference the place holder so it doesn't go away */
     549           0 :     cairo_scaled_font_reference (placeholder_scaled_font);
     550             : 
     551             :     /* now unlock the fontmap mutex so creation has a chance to finish */
     552           0 :     CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex);
     553             : 
     554             :     /* wait on placeholder mutex until we are awaken */
     555           0 :     CAIRO_MUTEX_LOCK (placeholder_scaled_font->mutex);
     556             : 
     557             :     /* ok, creation done.  just clean up and back out */
     558           0 :     CAIRO_MUTEX_UNLOCK (placeholder_scaled_font->mutex);
     559           0 :     cairo_scaled_font_destroy (placeholder_scaled_font);
     560             : 
     561           0 :     CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex);
     562           0 : }
     563             : 
     564             : /* Fowler / Noll / Vo (FNV) Hash (http://www.isthe.com/chongo/tech/comp/fnv/)
     565             :  *
     566             :  * Not necessarily better than a lot of other hashes, but should be OK, and
     567             :  * well tested with binary data.
     568             :  */
     569             : 
     570             : #define FNV_32_PRIME ((uint32_t)0x01000193)
     571             : #define FNV1_32_INIT ((uint32_t)0x811c9dc5)
     572             : 
     573             : static uint32_t
     574          44 : _hash_matrix_fnv (const cairo_matrix_t  *matrix,
     575             :                   uint32_t               hval)
     576             : {
     577          44 :     const uint8_t *buffer = (const uint8_t *) matrix;
     578          44 :     int len = sizeof (cairo_matrix_t);
     579             :     do {
     580        2112 :         hval *= FNV_32_PRIME;
     581        2112 :         hval ^= *buffer++;
     582        2112 :     } while (--len);
     583             : 
     584          44 :     return hval;
     585             : }
     586             : 
     587             : static uint32_t
     588          44 : _hash_mix_bits (uint32_t hash)
     589             : {
     590          44 :     hash += hash << 12;
     591          44 :     hash ^= hash >> 7;
     592          44 :     hash += hash << 3;
     593          44 :     hash ^= hash >> 17;
     594          44 :     hash += hash << 5;
     595          44 :     return hash;
     596             : }
     597             : 
     598             : static void
     599          22 : _cairo_scaled_font_init_key (cairo_scaled_font_t        *scaled_font,
     600             :                              cairo_font_face_t          *font_face,
     601             :                              const cairo_matrix_t       *font_matrix,
     602             :                              const cairo_matrix_t       *ctm,
     603             :                              const cairo_font_options_t *options)
     604             : {
     605          22 :     uint32_t hash = FNV1_32_INIT;
     606             : 
     607          22 :     scaled_font->status = CAIRO_STATUS_SUCCESS;
     608          22 :     scaled_font->placeholder = FALSE;
     609          22 :     scaled_font->font_face = font_face;
     610          22 :     scaled_font->font_matrix = *font_matrix;
     611          22 :     scaled_font->ctm = *ctm;
     612             :     /* ignore translation values in the ctm */
     613          22 :     scaled_font->ctm.x0 = 0.;
     614          22 :     scaled_font->ctm.y0 = 0.;
     615          22 :     _cairo_font_options_init_copy (&scaled_font->options, options);
     616             : 
     617             :     /* We do a bytewise hash on the font matrices */
     618          22 :     hash = _hash_matrix_fnv (&scaled_font->font_matrix, hash);
     619          22 :     hash = _hash_matrix_fnv (&scaled_font->ctm, hash);
     620          22 :     hash = _hash_mix_bits (hash);
     621             : 
     622          22 :     hash ^= (uintptr_t) scaled_font->font_face;
     623          22 :     hash ^= cairo_font_options_hash (&scaled_font->options);
     624             : 
     625             :     /* final mixing of bits */
     626          22 :     hash = _hash_mix_bits (hash);
     627             : 
     628          22 :     assert (hash != ZOMBIE);
     629          22 :     scaled_font->hash_entry.hash = hash;
     630          22 : }
     631             : 
     632             : static cairo_bool_t
     633           2 : _cairo_scaled_font_keys_equal (const void *abstract_key_a,
     634             :                                const void *abstract_key_b)
     635             : {
     636           2 :     const cairo_scaled_font_t *key_a = abstract_key_a;
     637           2 :     const cairo_scaled_font_t *key_b = abstract_key_b;
     638             : 
     639           2 :     if (key_a->hash_entry.hash != key_b->hash_entry.hash)
     640           0 :         return FALSE;
     641             : 
     642           4 :     return key_a->font_face == key_b->font_face &&
     643           2 :             memcmp ((unsigned char *)(&key_a->font_matrix.xx),
     644           2 :                     (unsigned char *)(&key_b->font_matrix.xx),
     645           2 :                     sizeof(cairo_matrix_t)) == 0 &&
     646           2 :             memcmp ((unsigned char *)(&key_a->ctm.xx),
     647           2 :                     (unsigned char *)(&key_b->ctm.xx),
     648           4 :                     sizeof(cairo_matrix_t)) == 0 &&
     649           2 :             cairo_font_options_equal (&key_a->options, &key_b->options);
     650             : }
     651             : 
     652             : static cairo_bool_t
     653           9 : _cairo_scaled_font_matches (const cairo_scaled_font_t *scaled_font,
     654             :                             const cairo_font_face_t *font_face,
     655             :                             const cairo_matrix_t *font_matrix,
     656             :                             const cairo_matrix_t *ctm,
     657             :                             const cairo_font_options_t *options)
     658             : {
     659          13 :     return scaled_font->original_font_face == font_face &&
     660           4 :             memcmp ((unsigned char *)(&scaled_font->font_matrix.xx),
     661           4 :                     (unsigned char *)(&font_matrix->xx),
     662           4 :                     sizeof(cairo_matrix_t)) == 0 &&
     663           4 :             memcmp ((unsigned char *)(&scaled_font->ctm.xx),
     664           4 :                     (unsigned char *)(&ctm->xx),
     665          13 :                     sizeof(cairo_matrix_t)) == 0 &&
     666           4 :             cairo_font_options_equal (&scaled_font->options, options);
     667             : }
     668             : 
     669             : static cairo_bool_t
     670         223 : _cairo_scaled_glyphs_equal (const void *abstract_a, const void *abstract_b)
     671             : {
     672         223 :     const cairo_scaled_glyph_t *a = abstract_a;
     673         223 :     const cairo_scaled_glyph_t *b = abstract_b;
     674             : 
     675         223 :     return a->hash_entry.hash == b->hash_entry.hash;
     676             : }
     677             : 
     678             : /*
     679             :  * Basic #cairo_scaled_font_t object management
     680             :  */
     681             : 
     682             : cairo_status_t
     683          11 : _cairo_scaled_font_init (cairo_scaled_font_t               *scaled_font,
     684             :                          cairo_font_face_t                 *font_face,
     685             :                          const cairo_matrix_t              *font_matrix,
     686             :                          const cairo_matrix_t              *ctm,
     687             :                          const cairo_font_options_t        *options,
     688             :                          const cairo_scaled_font_backend_t *backend)
     689             : {
     690             :     cairo_status_t status;
     691             : 
     692          11 :     status = cairo_font_options_status ((cairo_font_options_t *) options);
     693          11 :     if (unlikely (status))
     694           0 :         return status;
     695             : 
     696          11 :     _cairo_scaled_font_init_key (scaled_font, font_face,
     697             :                                  font_matrix, ctm, options);
     698             : 
     699          11 :     cairo_matrix_multiply (&scaled_font->scale,
     700          11 :                            &scaled_font->font_matrix,
     701          11 :                            &scaled_font->ctm);
     702             : 
     703          11 :     scaled_font->max_scale = MAX (fabs (scaled_font->scale.xx) + fabs (scaled_font->scale.xy),
     704             :                                   fabs (scaled_font->scale.yx) + fabs (scaled_font->scale.yy));
     705          11 :     scaled_font->scale_inverse = scaled_font->scale;
     706          11 :     status = cairo_matrix_invert (&scaled_font->scale_inverse);
     707          11 :     if (unlikely (status)) {
     708             :         /* If the font scale matrix is rank 0, just using an all-zero inverse matrix
     709             :          * makes everything work correctly.  This make font size 0 work without
     710             :          * producing an error.
     711             :          *
     712             :          * FIXME:  If the scale is rank 1, we still go into error mode.  But then
     713             :          * again, that's what we do everywhere in cairo.
     714             :          *
     715             :          * Also, the check for == 0. below may be too harsh...
     716             :          */
     717           0 :         if (_cairo_matrix_is_scale_0 (&scaled_font->scale)) {
     718           0 :             cairo_matrix_init (&scaled_font->scale_inverse,
     719             :                                0, 0, 0, 0,
     720           0 :                                -scaled_font->scale.x0,
     721           0 :                                -scaled_font->scale.y0);
     722             :         } else
     723           0 :             return status;
     724             :     }
     725             : 
     726          11 :     scaled_font->glyphs = _cairo_hash_table_create (_cairo_scaled_glyphs_equal);
     727          11 :     if (unlikely (scaled_font->glyphs == NULL))
     728           0 :         return _cairo_error (CAIRO_STATUS_NO_MEMORY);
     729             : 
     730          11 :     cairo_list_init (&scaled_font->glyph_pages);
     731          11 :     scaled_font->cache_frozen = FALSE;
     732          11 :     scaled_font->global_cache_frozen = FALSE;
     733             : 
     734          11 :     scaled_font->holdover = FALSE;
     735          11 :     scaled_font->finished = FALSE;
     736             : 
     737          11 :     CAIRO_REFERENCE_COUNT_INIT (&scaled_font->ref_count, 1);
     738             : 
     739          11 :     _cairo_user_data_array_init (&scaled_font->user_data);
     740             : 
     741          11 :     cairo_font_face_reference (font_face);
     742          11 :     scaled_font->original_font_face = NULL;
     743             : 
     744          11 :     CAIRO_MUTEX_INIT (scaled_font->mutex);
     745             : 
     746          11 :     scaled_font->surface_backend = NULL;
     747          11 :     scaled_font->surface_private = NULL;
     748             : 
     749          11 :     scaled_font->backend = backend;
     750          11 :     cairo_list_init (&scaled_font->link);
     751             : 
     752          11 :     return CAIRO_STATUS_SUCCESS;
     753             : }
     754             : 
     755             : void
     756         339 : _cairo_scaled_font_freeze_cache (cairo_scaled_font_t *scaled_font)
     757             : {
     758             :     /* ensure we do not modify an error object */
     759         339 :     assert (scaled_font->status == CAIRO_STATUS_SUCCESS);
     760             : 
     761         339 :     CAIRO_MUTEX_LOCK (scaled_font->mutex);
     762         339 :     scaled_font->cache_frozen = TRUE;
     763         339 : }
     764             : 
     765             : void
     766         339 : _cairo_scaled_font_thaw_cache (cairo_scaled_font_t *scaled_font)
     767             : {
     768         339 :     scaled_font->cache_frozen = FALSE;
     769             : 
     770         339 :     if (scaled_font->global_cache_frozen) {
     771           7 :         CAIRO_MUTEX_LOCK (_cairo_scaled_glyph_page_cache_mutex);
     772           7 :         _cairo_cache_thaw (&cairo_scaled_glyph_page_cache);
     773           7 :         CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex);
     774             : 
     775           7 :         scaled_font->global_cache_frozen = FALSE;
     776             :     }
     777             : 
     778         339 :     CAIRO_MUTEX_UNLOCK (scaled_font->mutex);
     779         339 : }
     780             : 
     781             : void
     782           0 : _cairo_scaled_font_reset_cache (cairo_scaled_font_t *scaled_font)
     783             : {
     784           0 :     assert (! scaled_font->cache_frozen);
     785             : 
     786           0 :     CAIRO_MUTEX_LOCK (_cairo_scaled_glyph_page_cache_mutex);
     787           0 :     while (! cairo_list_is_empty (&scaled_font->glyph_pages)) {
     788           0 :         _cairo_cache_remove (&cairo_scaled_glyph_page_cache,
     789           0 :                              &cairo_list_first_entry (&scaled_font->glyph_pages,
     790             :                                                       cairo_scaled_glyph_page_t,
     791             :                                                       link)->cache_entry);
     792             :     }
     793           0 :     CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex);
     794           0 : }
     795             : 
     796             : cairo_status_t
     797          11 : _cairo_scaled_font_set_metrics (cairo_scaled_font_t         *scaled_font,
     798             :                                 cairo_font_extents_t        *fs_metrics)
     799             : {
     800             :     cairo_status_t status;
     801             :     double  font_scale_x, font_scale_y;
     802             : 
     803          11 :     scaled_font->fs_extents = *fs_metrics;
     804             : 
     805          11 :     status = _cairo_matrix_compute_basis_scale_factors (&scaled_font->font_matrix,
     806             :                                                   &font_scale_x, &font_scale_y,
     807             :                                                   1);
     808          11 :     if (unlikely (status))
     809           0 :         return status;
     810             : 
     811             :     /*
     812             :      * The font responded in unscaled units, scale by the font
     813             :      * matrix scale factors to get to user space
     814             :      */
     815             : 
     816          11 :     scaled_font->extents.ascent = fs_metrics->ascent * font_scale_y;
     817          11 :     scaled_font->extents.descent = fs_metrics->descent * font_scale_y;
     818          11 :     scaled_font->extents.height = fs_metrics->height * font_scale_y;
     819          11 :     scaled_font->extents.max_x_advance = fs_metrics->max_x_advance * font_scale_x;
     820          11 :     scaled_font->extents.max_y_advance = fs_metrics->max_y_advance * font_scale_y;
     821             : 
     822          11 :     return CAIRO_STATUS_SUCCESS;
     823             : }
     824             : 
     825             : static void
     826           0 : _cairo_scaled_font_fini_internal (cairo_scaled_font_t *scaled_font)
     827             : {
     828           0 :     scaled_font->finished = TRUE;
     829             : 
     830           0 :     _cairo_scaled_font_reset_cache (scaled_font);
     831           0 :     _cairo_hash_table_destroy (scaled_font->glyphs);
     832             : 
     833           0 :     cairo_font_face_destroy (scaled_font->font_face);
     834           0 :     cairo_font_face_destroy (scaled_font->original_font_face);
     835             : 
     836           0 :     CAIRO_MUTEX_FINI (scaled_font->mutex);
     837             : 
     838           0 :     if (scaled_font->surface_backend != NULL &&
     839           0 :         scaled_font->surface_backend->scaled_font_fini != NULL)
     840           0 :         scaled_font->surface_backend->scaled_font_fini (scaled_font);
     841             : 
     842           0 :     if (scaled_font->backend != NULL && scaled_font->backend->fini != NULL)
     843           0 :         scaled_font->backend->fini (scaled_font);
     844             : 
     845           0 :     _cairo_user_data_array_fini (&scaled_font->user_data);
     846           0 : }
     847             : 
     848             : /* XXX: allow multiple backends to share the font */
     849             : void
     850           0 : _cairo_scaled_font_revoke_ownership (cairo_scaled_font_t *scaled_font)
     851             : {
     852           0 :     if (scaled_font->surface_backend == NULL)
     853           0 :         return;
     854             : 
     855           0 :     _cairo_scaled_font_reset_cache (scaled_font);
     856             : 
     857           0 :     if (scaled_font->surface_backend->scaled_font_fini != NULL)
     858           0 :         scaled_font->surface_backend->scaled_font_fini (scaled_font);
     859             : 
     860           0 :     scaled_font->surface_backend = NULL;
     861           0 :     scaled_font->surface_private = NULL;
     862             : }
     863             : 
     864             : void
     865           0 : _cairo_scaled_font_fini (cairo_scaled_font_t *scaled_font)
     866             : {
     867             :     /* Release the lock to avoid the possibility of a recursive
     868             :      * deadlock when the scaled font destroy closure gets called. */
     869           0 :     CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex);
     870           0 :     _cairo_scaled_font_fini_internal (scaled_font);
     871           0 :     CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex);
     872           0 : }
     873             : 
     874             : /**
     875             :  * cairo_scaled_font_create:
     876             :  * @font_face: a #cairo_font_face_t
     877             :  * @font_matrix: font space to user space transformation matrix for the
     878             :  *       font. In the simplest case of a N point font, this matrix is
     879             :  *       just a scale by N, but it can also be used to shear the font
     880             :  *       or stretch it unequally along the two axes. See
     881             :  *       cairo_set_font_matrix().
     882             :  * @ctm: user to device transformation matrix with which the font will
     883             :  *       be used.
     884             :  * @options: options to use when getting metrics for the font and
     885             :  *           rendering with it.
     886             :  *
     887             :  * Creates a #cairo_scaled_font_t object from a font face and matrices that
     888             :  * describe the size of the font and the environment in which it will
     889             :  * be used.
     890             :  *
     891             :  * Return value: a newly created #cairo_scaled_font_t. Destroy with
     892             :  *  cairo_scaled_font_destroy()
     893             :  **/
     894             : cairo_scaled_font_t *
     895          12 : cairo_scaled_font_create (cairo_font_face_t          *font_face,
     896             :                           const cairo_matrix_t       *font_matrix,
     897             :                           const cairo_matrix_t       *ctm,
     898             :                           const cairo_font_options_t *options)
     899             : {
     900             :     cairo_status_t status;
     901             :     cairo_scaled_font_map_t *font_map;
     902          12 :     cairo_font_face_t *original_font_face = font_face;
     903          12 :     cairo_scaled_font_t key, *old = NULL, *scaled_font = NULL, *dead = NULL;
     904             :     double det;
     905             : 
     906          12 :     status = font_face->status;
     907          12 :     if (unlikely (status))
     908           0 :         return _cairo_scaled_font_create_in_error (status);
     909             : 
     910          12 :     det = _cairo_matrix_compute_determinant (font_matrix);
     911          12 :     if (! ISFINITE (det))
     912           0 :         return _cairo_scaled_font_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_MATRIX));
     913             : 
     914          12 :     det = _cairo_matrix_compute_determinant (ctm);
     915          12 :     if (! ISFINITE (det))
     916           0 :         return _cairo_scaled_font_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_MATRIX));
     917             : 
     918          12 :     status = cairo_font_options_status ((cairo_font_options_t *) options);
     919          12 :     if (unlikely (status))
     920           0 :         return _cairo_scaled_font_create_in_error (status);
     921             : 
     922             :     /* Note that degenerate ctm or font_matrix *are* allowed.
     923             :      * We want to support a font size of 0. */
     924             : 
     925          12 :     font_map = _cairo_scaled_font_map_lock ();
     926          12 :     if (unlikely (font_map == NULL))
     927           0 :         return _cairo_scaled_font_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
     928             : 
     929          12 :     scaled_font = font_map->mru_scaled_font;
     930          21 :     if (scaled_font != NULL &&
     931           9 :         _cairo_scaled_font_matches (scaled_font,
     932             :                                     font_face, font_matrix, ctm, options))
     933             :     {
     934           1 :         assert (scaled_font->hash_entry.hash != ZOMBIE);
     935           1 :         assert (! scaled_font->placeholder);
     936             : 
     937           1 :         if (likely (scaled_font->status == CAIRO_STATUS_SUCCESS)) {
     938             :             /* We increment the reference count manually here, (rather
     939             :              * than calling into cairo_scaled_font_reference), since we
     940             :              * must modify the reference count while our lock is still
     941             :              * held. */
     942           1 :             _cairo_reference_count_inc (&scaled_font->ref_count);
     943           1 :             _cairo_scaled_font_map_unlock ();
     944           1 :             return scaled_font;
     945             :         }
     946             : 
     947             :         /* the font has been put into an error status - abandon the cache */
     948           0 :         _cairo_hash_table_remove (font_map->hash_table,
     949           0 :                                   &scaled_font->hash_entry);
     950           0 :         scaled_font->hash_entry.hash = ZOMBIE;
     951           0 :         dead = scaled_font;
     952           0 :         font_map->mru_scaled_font = NULL;
     953             : 
     954           0 :         if (font_face->backend->get_implementation != NULL) {
     955           0 :             font_face = font_face->backend->get_implementation (font_face,
     956             :                                                                 font_matrix,
     957             :                                                                 ctm,
     958             :                                                                 options);
     959           0 :             if (unlikely (font_face->status)) {
     960           0 :                 _cairo_scaled_font_map_unlock ();
     961           0 :                 cairo_scaled_font_destroy (scaled_font);
     962           0 :                 return _cairo_scaled_font_create_in_error (font_face->status);
     963             :             }
     964             :         }
     965             : 
     966           0 :         _cairo_scaled_font_init_key (&key, font_face,
     967             :                                      font_matrix, ctm, options);
     968             :     }
     969             :     else
     970             :     {
     971          11 :         if (font_face->backend->get_implementation != NULL) {
     972          11 :             font_face = font_face->backend->get_implementation (font_face,
     973             :                                                                 font_matrix,
     974             :                                                                 ctm,
     975             :                                                                 options);
     976          11 :             if (unlikely (font_face->status)) {
     977           0 :                 _cairo_scaled_font_map_unlock ();
     978           0 :                 return _cairo_scaled_font_create_in_error (font_face->status);
     979             :             }
     980             :         }
     981             : 
     982          11 :         _cairo_scaled_font_init_key (&key, font_face,
     983             :                                      font_matrix, ctm, options);
     984             : 
     985          22 :         while ((scaled_font = _cairo_hash_table_lookup (font_map->hash_table,
     986             :                                                         &key.hash_entry)))
     987             :         {
     988           0 :             if (! scaled_font->placeholder)
     989           0 :                 break;
     990             : 
     991             :             /* If the scaled font is being created (happens for user-font),
     992             :              * just wait until it's done, then retry */
     993           0 :             _cairo_scaled_font_placeholder_wait_for_creation_to_finish (scaled_font);
     994             :         }
     995             : 
     996             :         /* Return existing scaled_font if it exists in the hash table. */
     997          11 :         if (scaled_font != NULL) {
     998             :             /* If the original reference count is 0, then this font must have
     999             :              * been found in font_map->holdovers, (which means this caching is
    1000             :              * actually working). So now we remove it from the holdovers
    1001             :              * array, unless we caught the font in the middle of destruction.
    1002             :              */
    1003           0 :             if (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&scaled_font->ref_count)) {
    1004           0 :                 if (scaled_font->holdover) {
    1005             :                     int i;
    1006             : 
    1007           0 :                     for (i = 0; i < font_map->num_holdovers; i++) {
    1008           0 :                         if (font_map->holdovers[i] == scaled_font) {
    1009           0 :                             font_map->num_holdovers--;
    1010           0 :                             memmove (&font_map->holdovers[i],
    1011           0 :                                      &font_map->holdovers[i+1],
    1012           0 :                                      (font_map->num_holdovers - i) * sizeof (cairo_scaled_font_t*));
    1013           0 :                             break;
    1014             :                         }
    1015             :                     }
    1016             : 
    1017           0 :                     scaled_font->holdover = FALSE;
    1018             :                 }
    1019             : 
    1020             :                 /* reset any error status */
    1021           0 :                 scaled_font->status = CAIRO_STATUS_SUCCESS;
    1022             :             }
    1023             : 
    1024           0 :             if (likely (scaled_font->status == CAIRO_STATUS_SUCCESS)) {
    1025             :                 /* We increment the reference count manually here, (rather
    1026             :                  * than calling into cairo_scaled_font_reference), since we
    1027             :                  * must modify the reference count while our lock is still
    1028             :                  * held. */
    1029             : 
    1030           0 :                 old = font_map->mru_scaled_font;
    1031           0 :                 font_map->mru_scaled_font = scaled_font;
    1032             :                 /* increment reference count for the mru cache */
    1033           0 :                 _cairo_reference_count_inc (&scaled_font->ref_count);
    1034             :                 /* and increment for the returned reference */
    1035           0 :                 _cairo_reference_count_inc (&scaled_font->ref_count);
    1036           0 :                 _cairo_scaled_font_map_unlock ();
    1037             : 
    1038           0 :                 cairo_scaled_font_destroy (old);
    1039           0 :                 if (font_face != original_font_face)
    1040           0 :                     cairo_font_face_destroy (font_face);
    1041             : 
    1042           0 :                 return scaled_font;
    1043             :             }
    1044             : 
    1045             :             /* the font has been put into an error status - abandon the cache */
    1046           0 :             _cairo_hash_table_remove (font_map->hash_table,
    1047           0 :                                       &scaled_font->hash_entry);
    1048           0 :             scaled_font->hash_entry.hash = ZOMBIE;
    1049             :         }
    1050             :     }
    1051             : 
    1052             :     /* Otherwise create it and insert it into the hash table. */
    1053          11 :     status = font_face->backend->scaled_font_create (font_face, font_matrix,
    1054             :                                                      ctm, options, &scaled_font);
    1055             :     /* Did we leave the backend in an error state? */
    1056          11 :     if (unlikely (status)) {
    1057           0 :         _cairo_scaled_font_map_unlock ();
    1058           0 :         if (font_face != original_font_face)
    1059           0 :             cairo_font_face_destroy (font_face);
    1060             : 
    1061           0 :         if (dead != NULL)
    1062           0 :             cairo_scaled_font_destroy (dead);
    1063             : 
    1064           0 :         status = _cairo_font_face_set_error (font_face, status);
    1065           0 :         return _cairo_scaled_font_create_in_error (status);
    1066             :     }
    1067             :     /* Or did we encounter an error whilst constructing the scaled font? */
    1068          11 :     if (unlikely (scaled_font->status)) {
    1069           0 :         _cairo_scaled_font_map_unlock ();
    1070           0 :         if (font_face != original_font_face)
    1071           0 :             cairo_font_face_destroy (font_face);
    1072             : 
    1073           0 :         if (dead != NULL)
    1074           0 :             cairo_scaled_font_destroy (dead);
    1075             : 
    1076           0 :         return scaled_font;
    1077             :     }
    1078             : 
    1079             :     /* Our caching above is defeated if the backend switches fonts on us -
    1080             :      * e.g. old incarnations of toy-font-face and lazily resolved
    1081             :      * ft-font-faces
    1082             :      */
    1083          11 :     assert (scaled_font->font_face == font_face);
    1084             : 
    1085          22 :     scaled_font->original_font_face =
    1086          11 :         cairo_font_face_reference (original_font_face);
    1087             : 
    1088          11 :     status = _cairo_hash_table_insert (font_map->hash_table,
    1089          11 :                                        &scaled_font->hash_entry);
    1090          11 :     if (likely (status == CAIRO_STATUS_SUCCESS)) {
    1091          11 :         old = font_map->mru_scaled_font;
    1092          11 :         font_map->mru_scaled_font = scaled_font;
    1093          11 :         _cairo_reference_count_inc (&scaled_font->ref_count);
    1094             :     }
    1095             : 
    1096          11 :     _cairo_scaled_font_map_unlock ();
    1097             : 
    1098          11 :     cairo_scaled_font_destroy (old);
    1099          11 :     if (font_face != original_font_face)
    1100           0 :         cairo_font_face_destroy (font_face);
    1101             : 
    1102          11 :     if (dead != NULL)
    1103           0 :         cairo_scaled_font_destroy (dead);
    1104             : 
    1105          11 :     if (unlikely (status)) {
    1106             :         /* We can't call _cairo_scaled_font_destroy here since it expects
    1107             :          * that the font has already been successfully inserted into the
    1108             :          * hash table. */
    1109           0 :         _cairo_scaled_font_fini_internal (scaled_font);
    1110           0 :         free (scaled_font);
    1111           0 :         return _cairo_scaled_font_create_in_error (status);
    1112             :     }
    1113             : 
    1114          11 :     return scaled_font;
    1115             : }
    1116             : slim_hidden_def (cairo_scaled_font_create);
    1117             : 
    1118             : static cairo_scaled_font_t *_cairo_scaled_font_nil_objects[CAIRO_STATUS_LAST_STATUS + 1];
    1119             : 
    1120             : /* XXX This should disappear in favour of a common pool of error objects. */
    1121             : cairo_scaled_font_t *
    1122           0 : _cairo_scaled_font_create_in_error (cairo_status_t status)
    1123             : {
    1124             :     cairo_scaled_font_t *scaled_font;
    1125             : 
    1126           0 :     assert (status != CAIRO_STATUS_SUCCESS);
    1127             : 
    1128           0 :     if (status == CAIRO_STATUS_NO_MEMORY)
    1129           0 :         return (cairo_scaled_font_t *) &_cairo_scaled_font_nil;
    1130             : 
    1131           0 :     CAIRO_MUTEX_LOCK (_cairo_scaled_font_error_mutex);
    1132           0 :     scaled_font = _cairo_scaled_font_nil_objects[status];
    1133           0 :     if (unlikely (scaled_font == NULL)) {
    1134           0 :         scaled_font = malloc (sizeof (cairo_scaled_font_t));
    1135           0 :         if (unlikely (scaled_font == NULL)) {
    1136           0 :             CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_error_mutex);
    1137           0 :             _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
    1138           0 :             return (cairo_scaled_font_t *) &_cairo_scaled_font_nil;
    1139             :         }
    1140             : 
    1141           0 :         *scaled_font = _cairo_scaled_font_nil;
    1142           0 :         scaled_font->status = status;
    1143           0 :         _cairo_scaled_font_nil_objects[status] = scaled_font;
    1144             :     }
    1145           0 :     CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_error_mutex);
    1146             : 
    1147           0 :     return scaled_font;
    1148             : }
    1149             : 
    1150             : void
    1151           0 : _cairo_scaled_font_reset_static_data (void)
    1152             : {
    1153             :     int status;
    1154             : 
    1155           0 :     CAIRO_MUTEX_LOCK (_cairo_scaled_font_error_mutex);
    1156           0 :     for (status = CAIRO_STATUS_SUCCESS;
    1157             :          status <= CAIRO_STATUS_LAST_STATUS;
    1158           0 :          status++)
    1159             :     {
    1160           0 :         if (_cairo_scaled_font_nil_objects[status] != NULL) {
    1161           0 :             free (_cairo_scaled_font_nil_objects[status]);
    1162           0 :             _cairo_scaled_font_nil_objects[status] = NULL;
    1163             :         }
    1164             :     }
    1165           0 :     CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_error_mutex);
    1166             : 
    1167           0 :     CAIRO_MUTEX_LOCK (_cairo_scaled_glyph_page_cache_mutex);
    1168           0 :     if (cairo_scaled_glyph_page_cache.hash_table != NULL) {
    1169           0 :         _cairo_cache_fini (&cairo_scaled_glyph_page_cache);
    1170           0 :         cairo_scaled_glyph_page_cache.hash_table = NULL;
    1171             :     }
    1172           0 :     CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex);
    1173           0 : }
    1174             : 
    1175             : /**
    1176             :  * cairo_scaled_font_reference:
    1177             :  * @scaled_font: a #cairo_scaled_font_t, (may be %NULL in which case
    1178             :  * this function does nothing)
    1179             :  *
    1180             :  * Increases the reference count on @scaled_font by one. This prevents
    1181             :  * @scaled_font from being destroyed until a matching call to
    1182             :  * cairo_scaled_font_destroy() is made.
    1183             :  *
    1184             :  * The number of references to a #cairo_scaled_font_t can be get using
    1185             :  * cairo_scaled_font_get_reference_count().
    1186             :  *
    1187             :  * Returns: the referenced #cairo_scaled_font_t
    1188             :  **/
    1189             : cairo_scaled_font_t *
    1190          29 : cairo_scaled_font_reference (cairo_scaled_font_t *scaled_font)
    1191             : {
    1192          58 :     if (scaled_font == NULL ||
    1193          58 :             CAIRO_REFERENCE_COUNT_IS_INVALID (&scaled_font->ref_count))
    1194           0 :         return scaled_font;
    1195             : 
    1196          58 :     assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&scaled_font->ref_count));
    1197             : 
    1198          29 :     _cairo_reference_count_inc (&scaled_font->ref_count);
    1199             : 
    1200          29 :     return scaled_font;
    1201             : }
    1202             : slim_hidden_def (cairo_scaled_font_reference);
    1203             : 
    1204             : /**
    1205             :  * cairo_scaled_font_destroy:
    1206             :  * @scaled_font: a #cairo_scaled_font_t
    1207             :  *
    1208             :  * Decreases the reference count on @font by one. If the result
    1209             :  * is zero, then @font and all associated resources are freed.
    1210             :  * See cairo_scaled_font_reference().
    1211             :  **/
    1212             : void
    1213          40 : cairo_scaled_font_destroy (cairo_scaled_font_t *scaled_font)
    1214             : {
    1215          40 :     cairo_scaled_font_t *lru = NULL;
    1216             :     cairo_scaled_font_map_t *font_map;
    1217             : 
    1218             :     assert (CAIRO_MUTEX_IS_UNLOCKED (_cairo_scaled_font_map_mutex));
    1219             : 
    1220          77 :     if (scaled_font == NULL ||
    1221          74 :             CAIRO_REFERENCE_COUNT_IS_INVALID (&scaled_font->ref_count))
    1222           3 :         return;
    1223             : 
    1224          74 :     assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&scaled_font->ref_count));
    1225             : 
    1226          37 :     if (! _cairo_reference_count_dec_and_test (&scaled_font->ref_count))
    1227          37 :         return;
    1228             : 
    1229           0 :     font_map = _cairo_scaled_font_map_lock ();
    1230           0 :     assert (font_map != NULL);
    1231             : 
    1232             :     /* Another thread may have resurrected the font whilst we waited */
    1233           0 :     if (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&scaled_font->ref_count)) {
    1234           0 :         if (! scaled_font->placeholder &&
    1235           0 :             scaled_font->hash_entry.hash != ZOMBIE)
    1236             :         {
    1237             :             /* Another thread may have already inserted us into the holdovers */
    1238           0 :             if (scaled_font->holdover)
    1239           0 :                 goto unlock;
    1240             : 
    1241             :             /* Rather than immediately destroying this object, we put it into
    1242             :              * the font_map->holdovers array in case it will get used again
    1243             :              * soon (and is why we must hold the lock over the atomic op on
    1244             :              * the reference count). To make room for it, we do actually
    1245             :              * destroy the least-recently-used holdover.
    1246             :              */
    1247             : 
    1248           0 :             if (font_map->num_holdovers == CAIRO_SCALED_FONT_MAX_HOLDOVERS) {
    1249           0 :                 lru = font_map->holdovers[0];
    1250           0 :                 assert (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&lru->ref_count));
    1251             : 
    1252           0 :                 _cairo_hash_table_remove (font_map->hash_table,
    1253             :                                           &lru->hash_entry);
    1254             : 
    1255           0 :                 font_map->num_holdovers--;
    1256           0 :                 memmove (&font_map->holdovers[0],
    1257           0 :                          &font_map->holdovers[1],
    1258           0 :                          font_map->num_holdovers * sizeof (cairo_scaled_font_t*));
    1259             :             }
    1260             : 
    1261           0 :             font_map->holdovers[font_map->num_holdovers++] = scaled_font;
    1262           0 :             scaled_font->holdover = TRUE;
    1263             :         } else
    1264           0 :             lru = scaled_font;
    1265             :     }
    1266             : 
    1267             :   unlock:
    1268           0 :     _cairo_scaled_font_map_unlock ();
    1269             : 
    1270             :     /* If we pulled an item from the holdovers array, (while the font
    1271             :      * map lock was held, of course), then there is no way that anyone
    1272             :      * else could have acquired a reference to it. So we can now
    1273             :      * safely call fini on it without any lock held. This is desirable
    1274             :      * as we never want to call into any backend function with a lock
    1275             :      * held. */
    1276           0 :     if (lru != NULL) {
    1277           0 :         _cairo_scaled_font_fini_internal (lru);
    1278           0 :         free (lru);
    1279             :     }
    1280             : }
    1281             : slim_hidden_def (cairo_scaled_font_destroy);
    1282             : 
    1283             : /**
    1284             :  * cairo_scaled_font_get_reference_count:
    1285             :  * @scaled_font: a #cairo_scaled_font_t
    1286             :  *
    1287             :  * Returns the current reference count of @scaled_font.
    1288             :  *
    1289             :  * Return value: the current reference count of @scaled_font.  If the
    1290             :  * object is a nil object, 0 will be returned.
    1291             :  *
    1292             :  * Since: 1.4
    1293             :  **/
    1294             : unsigned int
    1295           0 : cairo_scaled_font_get_reference_count (cairo_scaled_font_t *scaled_font)
    1296             : {
    1297           0 :     if (scaled_font == NULL ||
    1298           0 :             CAIRO_REFERENCE_COUNT_IS_INVALID (&scaled_font->ref_count))
    1299           0 :         return 0;
    1300             : 
    1301           0 :     return CAIRO_REFERENCE_COUNT_GET_VALUE (&scaled_font->ref_count);
    1302             : }
    1303             : 
    1304             : /**
    1305             :  * cairo_scaled_font_get_user_data:
    1306             :  * @scaled_font: a #cairo_scaled_font_t
    1307             :  * @key: the address of the #cairo_user_data_key_t the user data was
    1308             :  * attached to
    1309             :  *
    1310             :  * Return user data previously attached to @scaled_font using the
    1311             :  * specified key.  If no user data has been attached with the given
    1312             :  * key this function returns %NULL.
    1313             :  *
    1314             :  * Return value: the user data previously attached or %NULL.
    1315             :  *
    1316             :  * Since: 1.4
    1317             :  **/
    1318             : void *
    1319           0 : cairo_scaled_font_get_user_data (cairo_scaled_font_t         *scaled_font,
    1320             :                                  const cairo_user_data_key_t *key)
    1321             : {
    1322           0 :     return _cairo_user_data_array_get_data (&scaled_font->user_data,
    1323             :                                             key);
    1324             : }
    1325             : slim_hidden_def (cairo_scaled_font_get_user_data);
    1326             : 
    1327             : /**
    1328             :  * cairo_scaled_font_set_user_data:
    1329             :  * @scaled_font: a #cairo_scaled_font_t
    1330             :  * @key: the address of a #cairo_user_data_key_t to attach the user data to
    1331             :  * @user_data: the user data to attach to the #cairo_scaled_font_t
    1332             :  * @destroy: a #cairo_destroy_func_t which will be called when the
    1333             :  * #cairo_t is destroyed or when new user data is attached using the
    1334             :  * same key.
    1335             :  *
    1336             :  * Attach user data to @scaled_font.  To remove user data from a surface,
    1337             :  * call this function with the key that was used to set it and %NULL
    1338             :  * for @data.
    1339             :  *
    1340             :  * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a
    1341             :  * slot could not be allocated for the user data.
    1342             :  *
    1343             :  * Since: 1.4
    1344             :  **/
    1345             : cairo_status_t
    1346           0 : cairo_scaled_font_set_user_data (cairo_scaled_font_t         *scaled_font,
    1347             :                                  const cairo_user_data_key_t *key,
    1348             :                                  void                        *user_data,
    1349             :                                  cairo_destroy_func_t         destroy)
    1350             : {
    1351           0 :     if (CAIRO_REFERENCE_COUNT_IS_INVALID (&scaled_font->ref_count))
    1352           0 :         return scaled_font->status;
    1353             : 
    1354           0 :     return _cairo_user_data_array_set_data (&scaled_font->user_data,
    1355             :                                             key, user_data, destroy);
    1356             : }
    1357             : slim_hidden_def (cairo_scaled_font_set_user_data);
    1358             : 
    1359             : /* Public font API follows. */
    1360             : 
    1361             : /**
    1362             :  * cairo_scaled_font_extents:
    1363             :  * @scaled_font: a #cairo_scaled_font_t
    1364             :  * @extents: a #cairo_font_extents_t which to store the retrieved extents.
    1365             :  *
    1366             :  * Gets the metrics for a #cairo_scaled_font_t.
    1367             :  **/
    1368             : void
    1369           0 : cairo_scaled_font_extents (cairo_scaled_font_t  *scaled_font,
    1370             :                            cairo_font_extents_t *extents)
    1371             : {
    1372           0 :     if (scaled_font->status) {
    1373           0 :         extents->ascent  = 0.0;
    1374           0 :         extents->descent = 0.0;
    1375           0 :         extents->height  = 0.0;
    1376           0 :         extents->max_x_advance = 0.0;
    1377           0 :         extents->max_y_advance = 0.0;
    1378           0 :         return;
    1379             :     }
    1380             : 
    1381           0 :     *extents = scaled_font->extents;
    1382             : }
    1383             : slim_hidden_def (cairo_scaled_font_extents);
    1384             : 
    1385             : /**
    1386             :  * cairo_scaled_font_text_extents:
    1387             :  * @scaled_font: a #cairo_scaled_font_t
    1388             :  * @utf8: a NUL-terminated string of text, encoded in UTF-8
    1389             :  * @extents: a #cairo_text_extents_t which to store the retrieved extents.
    1390             :  *
    1391             :  * Gets the extents for a string of text. The extents describe a
    1392             :  * user-space rectangle that encloses the "inked" portion of the text
    1393             :  * drawn at the origin (0,0) (as it would be drawn by cairo_show_text()
    1394             :  * if the cairo graphics state were set to the same font_face,
    1395             :  * font_matrix, ctm, and font_options as @scaled_font).  Additionally,
    1396             :  * the x_advance and y_advance values indicate the amount by which the
    1397             :  * current point would be advanced by cairo_show_text().
    1398             :  *
    1399             :  * Note that whitespace characters do not directly contribute to the
    1400             :  * size of the rectangle (extents.width and extents.height). They do
    1401             :  * contribute indirectly by changing the position of non-whitespace
    1402             :  * characters. In particular, trailing whitespace characters are
    1403             :  * likely to not affect the size of the rectangle, though they will
    1404             :  * affect the x_advance and y_advance values.
    1405             :  *
    1406             :  * Since: 1.2
    1407             :  **/
    1408             : void
    1409           0 : cairo_scaled_font_text_extents (cairo_scaled_font_t   *scaled_font,
    1410             :                                 const char            *utf8,
    1411             :                                 cairo_text_extents_t  *extents)
    1412             : {
    1413             :     cairo_status_t status;
    1414           0 :     cairo_glyph_t *glyphs = NULL;
    1415             :     int num_glyphs;
    1416             : 
    1417           0 :     if (scaled_font->status)
    1418           0 :         goto ZERO_EXTENTS;
    1419             : 
    1420           0 :     if (utf8 == NULL)
    1421           0 :         goto ZERO_EXTENTS;
    1422             : 
    1423           0 :     status = cairo_scaled_font_text_to_glyphs (scaled_font, 0., 0.,
    1424             :                                                utf8, -1,
    1425             :                                                &glyphs, &num_glyphs,
    1426             :                                                NULL, NULL,
    1427             :                                                NULL);
    1428           0 :     if (unlikely (status)) {
    1429           0 :         status = _cairo_scaled_font_set_error (scaled_font, status);
    1430           0 :         goto ZERO_EXTENTS;
    1431             :     }
    1432             : 
    1433           0 :     cairo_scaled_font_glyph_extents (scaled_font, glyphs, num_glyphs, extents);
    1434           0 :     free (glyphs);
    1435             : 
    1436           0 :     return;
    1437             : 
    1438             : ZERO_EXTENTS:
    1439           0 :     extents->x_bearing = 0.0;
    1440           0 :     extents->y_bearing = 0.0;
    1441           0 :     extents->width  = 0.0;
    1442           0 :     extents->height = 0.0;
    1443           0 :     extents->x_advance = 0.0;
    1444           0 :     extents->y_advance = 0.0;
    1445             : }
    1446             : 
    1447             : /**
    1448             :  * cairo_scaled_font_glyph_extents:
    1449             :  * @scaled_font: a #cairo_scaled_font_t
    1450             :  * @glyphs: an array of glyph IDs with X and Y offsets.
    1451             :  * @num_glyphs: the number of glyphs in the @glyphs array
    1452             :  * @extents: a #cairo_text_extents_t which to store the retrieved extents.
    1453             :  *
    1454             :  * Gets the extents for an array of glyphs. The extents describe a
    1455             :  * user-space rectangle that encloses the "inked" portion of the
    1456             :  * glyphs, (as they would be drawn by cairo_show_glyphs() if the cairo
    1457             :  * graphics state were set to the same font_face, font_matrix, ctm,
    1458             :  * and font_options as @scaled_font).  Additionally, the x_advance and
    1459             :  * y_advance values indicate the amount by which the current point
    1460             :  * would be advanced by cairo_show_glyphs().
    1461             :  *
    1462             :  * Note that whitespace glyphs do not contribute to the size of the
    1463             :  * rectangle (extents.width and extents.height).
    1464             :  **/
    1465             : void
    1466         339 : cairo_scaled_font_glyph_extents (cairo_scaled_font_t   *scaled_font,
    1467             :                                  const cairo_glyph_t   *glyphs,
    1468             :                                  int                    num_glyphs,
    1469             :                                  cairo_text_extents_t  *extents)
    1470             : {
    1471             :     cairo_status_t status;
    1472             :     int i;
    1473         339 :     double min_x = 0.0, min_y = 0.0, max_x = 0.0, max_y = 0.0;
    1474         339 :     cairo_bool_t visible = FALSE;
    1475         339 :     cairo_scaled_glyph_t *scaled_glyph = NULL;
    1476             : 
    1477         339 :     extents->x_bearing = 0.0;
    1478         339 :     extents->y_bearing = 0.0;
    1479         339 :     extents->width  = 0.0;
    1480         339 :     extents->height = 0.0;
    1481         339 :     extents->x_advance = 0.0;
    1482         339 :     extents->y_advance = 0.0;
    1483             : 
    1484         339 :     if (unlikely (scaled_font->status))
    1485           0 :         goto ZERO_EXTENTS;
    1486             : 
    1487         339 :     if (num_glyphs == 0)
    1488           0 :         goto ZERO_EXTENTS;
    1489             : 
    1490         339 :     if (unlikely (num_glyphs < 0)) {
    1491           0 :         _cairo_error_throw (CAIRO_STATUS_NEGATIVE_COUNT);
    1492             :         /* XXX Can't propagate error */
    1493           0 :         goto ZERO_EXTENTS;
    1494             :     }
    1495             : 
    1496         339 :     if (unlikely (glyphs == NULL)) {
    1497           0 :         _cairo_error_throw (CAIRO_STATUS_NULL_POINTER);
    1498             :         /* XXX Can't propagate error */
    1499           0 :         goto ZERO_EXTENTS;
    1500             :     }
    1501             : 
    1502         339 :     _cairo_scaled_font_freeze_cache (scaled_font);
    1503             : 
    1504         678 :     for (i = 0; i < num_glyphs; i++) {
    1505             :         double                  left, top, right, bottom;
    1506             : 
    1507         339 :         status = _cairo_scaled_glyph_lookup (scaled_font,
    1508         339 :                                              glyphs[i].index,
    1509             :                                              CAIRO_SCALED_GLYPH_INFO_METRICS,
    1510             :                                              &scaled_glyph);
    1511         339 :         if (unlikely (status)) {
    1512           0 :             if (status != CAIRO_INT_STATUS_UNSUPPORTED) {
    1513           0 :                 status = _cairo_scaled_font_set_error (scaled_font, status);
    1514             :             }
    1515           0 :             goto UNLOCK;
    1516             :         }
    1517             : 
    1518             :         /* "Ink" extents should skip "invisible" glyphs */
    1519         339 :         if (scaled_glyph->metrics.width == 0 || scaled_glyph->metrics.height == 0)
    1520           7 :             continue;
    1521             : 
    1522         332 :         left = scaled_glyph->metrics.x_bearing + glyphs[i].x;
    1523         332 :         right = left + scaled_glyph->metrics.width;
    1524         332 :         top = scaled_glyph->metrics.y_bearing + glyphs[i].y;
    1525         332 :         bottom = top + scaled_glyph->metrics.height;
    1526             : 
    1527         332 :         if (!visible) {
    1528         332 :             visible = TRUE;
    1529         332 :             min_x = left;
    1530         332 :             max_x = right;
    1531         332 :             min_y = top;
    1532         332 :             max_y = bottom;
    1533             :         } else {
    1534           0 :             if (left < min_x) min_x = left;
    1535           0 :             if (right > max_x) max_x = right;
    1536           0 :             if (top < min_y) min_y = top;
    1537           0 :             if (bottom > max_y) max_y = bottom;
    1538             :         }
    1539             :     }
    1540             : 
    1541         339 :     if (visible) {
    1542         332 :         extents->x_bearing = min_x - glyphs[0].x;
    1543         332 :         extents->y_bearing = min_y - glyphs[0].y;
    1544         332 :         extents->width = max_x - min_x;
    1545         332 :         extents->height = max_y - min_y;
    1546             :     } else {
    1547           7 :         extents->x_bearing = 0.0;
    1548           7 :         extents->y_bearing = 0.0;
    1549           7 :         extents->width = 0.0;
    1550           7 :         extents->height = 0.0;
    1551             :     }
    1552             : 
    1553         339 :     if (num_glyphs) {
    1554             :         double x0, y0, x1, y1;
    1555             : 
    1556         339 :         x0 = glyphs[0].x;
    1557         339 :         y0 = glyphs[0].y;
    1558             : 
    1559             :         /* scaled_glyph contains the glyph for num_glyphs - 1 already. */
    1560         339 :         x1 = glyphs[num_glyphs - 1].x + scaled_glyph->metrics.x_advance;
    1561         339 :         y1 = glyphs[num_glyphs - 1].y + scaled_glyph->metrics.y_advance;
    1562             : 
    1563         339 :         extents->x_advance = x1 - x0;
    1564         339 :         extents->y_advance = y1 - y0;
    1565             :     } else {
    1566           0 :         extents->x_advance = 0.0;
    1567           0 :         extents->y_advance = 0.0;
    1568             :     }
    1569             : 
    1570             :  UNLOCK:
    1571         339 :     _cairo_scaled_font_thaw_cache (scaled_font);
    1572         339 :     return;
    1573             : 
    1574             : ZERO_EXTENTS:
    1575           0 :     extents->x_bearing = 0.0;
    1576           0 :     extents->y_bearing = 0.0;
    1577           0 :     extents->width  = 0.0;
    1578           0 :     extents->height = 0.0;
    1579           0 :     extents->x_advance = 0.0;
    1580           0 :     extents->y_advance = 0.0;
    1581             : }
    1582             : slim_hidden_def (cairo_scaled_font_glyph_extents);
    1583             : 
    1584             : #define GLYPH_LUT_SIZE 64
    1585             : static cairo_status_t
    1586           0 : cairo_scaled_font_text_to_glyphs_internal_cached (cairo_scaled_font_t            *scaled_font,
    1587             :                                                     double                        x,
    1588             :                                                     double                        y,
    1589             :                                                     const char                   *utf8,
    1590             :                                                     cairo_glyph_t                *glyphs,
    1591             :                                                     cairo_text_cluster_t        **clusters,
    1592             :                                                     int                           num_chars)
    1593             : {
    1594             :     struct glyph_lut_elt {
    1595             :         unsigned long index;
    1596             :         double x_advance;
    1597             :         double y_advance;
    1598             :     } glyph_lut[GLYPH_LUT_SIZE];
    1599             :     uint32_t glyph_lut_unicode[GLYPH_LUT_SIZE];
    1600             :     cairo_status_t status;
    1601             :     const char *p;
    1602             :     int i;
    1603             : 
    1604           0 :     for (i = 0; i < GLYPH_LUT_SIZE; i++)
    1605           0 :         glyph_lut_unicode[i] = ~0U;
    1606             : 
    1607           0 :     p = utf8;
    1608           0 :     for (i = 0; i < num_chars; i++) {
    1609             :         int idx, num_bytes;
    1610             :         uint32_t unicode;
    1611             :         cairo_scaled_glyph_t *scaled_glyph;
    1612             :         struct glyph_lut_elt *glyph_slot;
    1613             : 
    1614           0 :         num_bytes = _cairo_utf8_get_char_validated (p, &unicode);
    1615           0 :         p += num_bytes;
    1616             : 
    1617           0 :         glyphs[i].x = x;
    1618           0 :         glyphs[i].y = y;
    1619             : 
    1620           0 :         idx = unicode % ARRAY_LENGTH (glyph_lut);
    1621           0 :         glyph_slot = &glyph_lut[idx];
    1622           0 :         if (glyph_lut_unicode[idx] == unicode) {
    1623           0 :             glyphs[i].index = glyph_slot->index;
    1624           0 :             x += glyph_slot->x_advance;
    1625           0 :             y += glyph_slot->y_advance;
    1626             :         } else {
    1627             :             unsigned long g;
    1628             : 
    1629           0 :             g = scaled_font->backend->ucs4_to_index (scaled_font, unicode);
    1630           0 :             status = _cairo_scaled_glyph_lookup (scaled_font,
    1631             :                                                  g,
    1632             :                                                  CAIRO_SCALED_GLYPH_INFO_METRICS,
    1633             :                                                  &scaled_glyph);
    1634           0 :             if (unlikely (status))
    1635           0 :                 return status;
    1636             : 
    1637           0 :             x += scaled_glyph->metrics.x_advance;
    1638           0 :             y += scaled_glyph->metrics.y_advance;
    1639             : 
    1640           0 :             glyph_lut_unicode[idx] = unicode;
    1641           0 :             glyph_slot->index = g;
    1642           0 :             glyph_slot->x_advance = scaled_glyph->metrics.x_advance;
    1643           0 :             glyph_slot->y_advance = scaled_glyph->metrics.y_advance;
    1644             : 
    1645           0 :             glyphs[i].index = g;
    1646             :         }
    1647             : 
    1648           0 :         if (clusters) {
    1649           0 :             (*clusters)[i].num_bytes  = num_bytes;
    1650           0 :             (*clusters)[i].num_glyphs = 1;
    1651             :         }
    1652             :     }
    1653             : 
    1654           0 :     return CAIRO_STATUS_SUCCESS;
    1655             : }
    1656             : 
    1657             : static cairo_status_t
    1658           0 : cairo_scaled_font_text_to_glyphs_internal_uncached (cairo_scaled_font_t  *scaled_font,
    1659             :                                                   double                  x,
    1660             :                                                   double                  y,
    1661             :                                                   const char             *utf8,
    1662             :                                                   cairo_glyph_t          *glyphs,
    1663             :                                                   cairo_text_cluster_t  **clusters,
    1664             :                                                   int                     num_chars)
    1665             : {
    1666             :     const char *p;
    1667             :     int i;
    1668             : 
    1669           0 :     p = utf8;
    1670           0 :     for (i = 0; i < num_chars; i++) {
    1671             :         unsigned long g;
    1672             :         int num_bytes;
    1673             :         uint32_t unicode;
    1674             :         cairo_scaled_glyph_t *scaled_glyph;
    1675             :         cairo_status_t status;
    1676             : 
    1677           0 :         num_bytes = _cairo_utf8_get_char_validated (p, &unicode);
    1678           0 :         p += num_bytes;
    1679             : 
    1680           0 :         glyphs[i].x = x;
    1681           0 :         glyphs[i].y = y;
    1682             : 
    1683           0 :         g = scaled_font->backend->ucs4_to_index (scaled_font, unicode);
    1684             : 
    1685             :         /*
    1686             :          * No advance needed for a single character string. So, let's speed up
    1687             :          * one-character strings by skipping glyph lookup.
    1688             :          */
    1689           0 :         if (num_chars > 1) {
    1690           0 :             status = _cairo_scaled_glyph_lookup (scaled_font,
    1691             :                                              g,
    1692             :                                              CAIRO_SCALED_GLYPH_INFO_METRICS,
    1693             :                                              &scaled_glyph);
    1694           0 :             if (unlikely (status))
    1695           0 :                 return status;
    1696             : 
    1697           0 :             x += scaled_glyph->metrics.x_advance;
    1698           0 :             y += scaled_glyph->metrics.y_advance;
    1699             :         }
    1700             : 
    1701           0 :         glyphs[i].index = g;
    1702             : 
    1703           0 :         if (clusters) {
    1704           0 :             (*clusters)[i].num_bytes  = num_bytes;
    1705           0 :             (*clusters)[i].num_glyphs = 1;
    1706             :         }
    1707             :     }
    1708             : 
    1709           0 :     return CAIRO_STATUS_SUCCESS;
    1710             : }
    1711             : 
    1712             : /**
    1713             :  * cairo_scaled_font_text_to_glyphs:
    1714             :  * @x: X position to place first glyph
    1715             :  * @y: Y position to place first glyph
    1716             :  * @scaled_font: a #cairo_scaled_font_t
    1717             :  * @utf8: a string of text encoded in UTF-8
    1718             :  * @utf8_len: length of @utf8 in bytes, or -1 if it is NUL-terminated
    1719             :  * @glyphs: pointer to array of glyphs to fill
    1720             :  * @num_glyphs: pointer to number of glyphs
    1721             :  * @clusters: pointer to array of cluster mapping information to fill, or %NULL
    1722             :  * @num_clusters: pointer to number of clusters, or %NULL
    1723             :  * @cluster_flags: pointer to location to store cluster flags corresponding to the
    1724             :  *                 output @clusters, or %NULL
    1725             :  *
    1726             :  * Converts UTF-8 text to an array of glyphs, optionally with cluster
    1727             :  * mapping, that can be used to render later using @scaled_font.
    1728             :  *
    1729             :  * If @glyphs initially points to a non-%NULL value, that array is used
    1730             :  * as a glyph buffer, and @num_glyphs should point to the number of glyph
    1731             :  * entries available there.  If the provided glyph array is too short for
    1732             :  * the conversion, a new glyph array is allocated using cairo_glyph_allocate()
    1733             :  * and placed in @glyphs.  Upon return, @num_glyphs always contains the
    1734             :  * number of generated glyphs.  If the value @glyphs points to has changed
    1735             :  * after the call, the user is responsible for freeing the allocated glyph
    1736             :  * array using cairo_glyph_free().  This may happen even if the provided
    1737             :  * array was large enough.
    1738             :  *
    1739             :  * If @clusters is not %NULL, @num_clusters and @cluster_flags should not be %NULL,
    1740             :  * and cluster mapping will be computed.
    1741             :  * The semantics of how cluster array allocation works is similar to the glyph
    1742             :  * array.  That is,
    1743             :  * if @clusters initially points to a non-%NULL value, that array is used
    1744             :  * as a cluster buffer, and @num_clusters should point to the number of cluster
    1745             :  * entries available there.  If the provided cluster array is too short for
    1746             :  * the conversion, a new cluster array is allocated using cairo_text_cluster_allocate()
    1747             :  * and placed in @clusters.  Upon return, @num_clusters always contains the
    1748             :  * number of generated clusters.  If the value @clusters points at has changed
    1749             :  * after the call, the user is responsible for freeing the allocated cluster
    1750             :  * array using cairo_text_cluster_free().  This may happen even if the provided
    1751             :  * array was large enough.
    1752             :  *
    1753             :  * In the simplest case, @glyphs and @clusters can point to %NULL initially
    1754             :  * and a suitable array will be allocated.  In code:
    1755             :  * <informalexample><programlisting>
    1756             :  * cairo_status_t status;
    1757             :  *
    1758             :  * cairo_glyph_t *glyphs = NULL;
    1759             :  * int num_glyphs;
    1760             :  * cairo_text_cluster_t *clusters = NULL;
    1761             :  * int num_clusters;
    1762             :  * cairo_text_cluster_flags_t cluster_flags;
    1763             :  *
    1764             :  * status = cairo_scaled_font_text_to_glyphs (scaled_font,
    1765             :  *                                            x, y,
    1766             :  *                                            utf8, utf8_len,
    1767             :  *                                            &amp;glyphs, &amp;num_glyphs,
    1768             :  *                                            &amp;clusters, &amp;num_clusters, &amp;cluster_flags);
    1769             :  *
    1770             :  * if (status == CAIRO_STATUS_SUCCESS) {
    1771             :  *     cairo_show_text_glyphs (cr,
    1772             :  *                             utf8, utf8_len,
    1773             :  *                             glyphs, num_glyphs,
    1774             :  *                             clusters, num_clusters, cluster_flags);
    1775             :  *
    1776             :  *     cairo_glyph_free (glyphs);
    1777             :  *     cairo_text_cluster_free (clusters);
    1778             :  * }
    1779             :  * </programlisting></informalexample>
    1780             :  *
    1781             :  * If no cluster mapping is needed:
    1782             :  * <informalexample><programlisting>
    1783             :  * cairo_status_t status;
    1784             :  *
    1785             :  * cairo_glyph_t *glyphs = NULL;
    1786             :  * int num_glyphs;
    1787             :  *
    1788             :  * status = cairo_scaled_font_text_to_glyphs (scaled_font,
    1789             :  *                                            x, y,
    1790             :  *                                            utf8, utf8_len,
    1791             :  *                                            &amp;glyphs, &amp;num_glyphs,
    1792             :  *                                            NULL, NULL,
    1793             :  *                                            NULL);
    1794             :  *
    1795             :  * if (status == CAIRO_STATUS_SUCCESS) {
    1796             :  *     cairo_show_glyphs (cr, glyphs, num_glyphs);
    1797             :  *     cairo_glyph_free (glyphs);
    1798             :  * }
    1799             :  * </programlisting></informalexample>
    1800             :  *
    1801             :  * If stack-based glyph and cluster arrays are to be used for small
    1802             :  * arrays:
    1803             :  * <informalexample><programlisting>
    1804             :  * cairo_status_t status;
    1805             :  *
    1806             :  * cairo_glyph_t stack_glyphs[40];
    1807             :  * cairo_glyph_t *glyphs = stack_glyphs;
    1808             :  * int num_glyphs = sizeof (stack_glyphs) / sizeof (stack_glyphs[0]);
    1809             :  * cairo_text_cluster_t stack_clusters[40];
    1810             :  * cairo_text_cluster_t *clusters = stack_clusters;
    1811             :  * int num_clusters = sizeof (stack_clusters) / sizeof (stack_clusters[0]);
    1812             :  * cairo_text_cluster_flags_t cluster_flags;
    1813             :  *
    1814             :  * status = cairo_scaled_font_text_to_glyphs (scaled_font,
    1815             :  *                                            x, y,
    1816             :  *                                            utf8, utf8_len,
    1817             :  *                                            &amp;glyphs, &amp;num_glyphs,
    1818             :  *                                            &amp;clusters, &amp;num_clusters, &amp;cluster_flags);
    1819             :  *
    1820             :  * if (status == CAIRO_STATUS_SUCCESS) {
    1821             :  *     cairo_show_text_glyphs (cr,
    1822             :  *                             utf8, utf8_len,
    1823             :  *                             glyphs, num_glyphs,
    1824             :  *                             clusters, num_clusters, cluster_flags);
    1825             :  *
    1826             :  *     if (glyphs != stack_glyphs)
    1827             :  *         cairo_glyph_free (glyphs);
    1828             :  *     if (clusters != stack_clusters)
    1829             :  *         cairo_text_cluster_free (clusters);
    1830             :  * }
    1831             :  * </programlisting></informalexample>
    1832             :  *
    1833             :  * For details of how @clusters, @num_clusters, and @cluster_flags map input
    1834             :  * UTF-8 text to the output glyphs see cairo_show_text_glyphs().
    1835             :  *
    1836             :  * The output values can be readily passed to cairo_show_text_glyphs()
    1837             :  * cairo_show_glyphs(), or related functions, assuming that the exact
    1838             :  * same @scaled_font is used for the operation.
    1839             :  *
    1840             :  * Return value: %CAIRO_STATUS_SUCCESS upon success, or an error status
    1841             :  * if the input values are wrong or if conversion failed.  If the input
    1842             :  * values are correct but the conversion failed, the error status is also
    1843             :  * set on @scaled_font.
    1844             :  *
    1845             :  * Since: 1.8
    1846             :  **/
    1847             : #define CACHING_THRESHOLD 16
    1848             : cairo_status_t
    1849           0 : cairo_scaled_font_text_to_glyphs (cairo_scaled_font_t   *scaled_font,
    1850             :                                   double                 x,
    1851             :                                   double                 y,
    1852             :                                   const char            *utf8,
    1853             :                                   int                    utf8_len,
    1854             :                                   cairo_glyph_t        **glyphs,
    1855             :                                   int                   *num_glyphs,
    1856             :                                   cairo_text_cluster_t **clusters,
    1857             :                                   int                   *num_clusters,
    1858             :                                   cairo_text_cluster_flags_t *cluster_flags)
    1859             : {
    1860           0 :     int num_chars = 0;
    1861             :     cairo_status_t status;
    1862             :     cairo_glyph_t *orig_glyphs;
    1863             :     cairo_text_cluster_t *orig_clusters;
    1864             : 
    1865           0 :     status = scaled_font->status;
    1866           0 :     if (unlikely (status))
    1867           0 :         return status;
    1868             : 
    1869             :     /* A slew of sanity checks */
    1870             : 
    1871             :     /* glyphs and num_glyphs can't be NULL */
    1872           0 :     if (glyphs     == NULL ||
    1873             :         num_glyphs == NULL) {
    1874           0 :         status = _cairo_error (CAIRO_STATUS_NULL_POINTER);
    1875           0 :         goto BAIL;
    1876             :     }
    1877             : 
    1878             :     /* Special case for NULL and -1 */
    1879           0 :     if (utf8 == NULL && utf8_len == -1)
    1880           0 :         utf8_len = 0;
    1881             : 
    1882             :     /* No NULLs for non-NULLs! */
    1883           0 :     if ((utf8_len && utf8          == NULL) ||
    1884           0 :         (clusters && num_clusters  == NULL) ||
    1885           0 :         (clusters && cluster_flags == NULL)) {
    1886           0 :         status = _cairo_error (CAIRO_STATUS_NULL_POINTER);
    1887           0 :         goto BAIL;
    1888             :     }
    1889             : 
    1890             :     /* A -1 for utf8_len means NUL-terminated */
    1891           0 :     if (utf8_len == -1)
    1892           0 :         utf8_len = strlen (utf8);
    1893             : 
    1894             :     /* A NULL *glyphs means no prealloced glyphs array */
    1895           0 :     if (glyphs && *glyphs == NULL)
    1896           0 :         *num_glyphs = 0;
    1897             : 
    1898             :     /* A NULL *clusters means no prealloced clusters array */
    1899           0 :     if (clusters && *clusters == NULL)
    1900           0 :         *num_clusters = 0;
    1901             : 
    1902           0 :     if (!clusters && num_clusters) {
    1903           0 :         num_clusters = NULL;
    1904             :     }
    1905             : 
    1906           0 :     if (cluster_flags) {
    1907           0 :         *cluster_flags = FALSE;
    1908             :     }
    1909             : 
    1910           0 :     if (!clusters && cluster_flags) {
    1911           0 :         cluster_flags = NULL;
    1912             :     }
    1913             : 
    1914             :     /* Apart from that, no negatives */
    1915           0 :     if (utf8_len < 0 ||
    1916           0 :         *num_glyphs < 0 ||
    1917           0 :         (num_clusters && *num_clusters < 0)) {
    1918           0 :         status = _cairo_error (CAIRO_STATUS_NEGATIVE_COUNT);
    1919           0 :         goto BAIL;
    1920             :     }
    1921             : 
    1922           0 :     if (utf8_len == 0) {
    1923           0 :         status = CAIRO_STATUS_SUCCESS;
    1924           0 :         goto BAIL;
    1925             :     }
    1926             : 
    1927             :     /* validate input so backend does not have to */
    1928           0 :     status = _cairo_utf8_to_ucs4 (utf8, utf8_len, NULL, &num_chars);
    1929           0 :     if (unlikely (status))
    1930           0 :         goto BAIL;
    1931             : 
    1932           0 :     _cairo_scaled_font_freeze_cache (scaled_font);
    1933             : 
    1934           0 :     orig_glyphs = *glyphs;
    1935           0 :     orig_clusters = clusters ? *clusters : NULL;
    1936             : 
    1937           0 :     if (scaled_font->backend->text_to_glyphs) {
    1938           0 :         status = scaled_font->backend->text_to_glyphs (scaled_font, x, y,
    1939             :                                                        utf8, utf8_len,
    1940             :                                                        glyphs, num_glyphs,
    1941             :                                                        clusters, num_clusters,
    1942             :                                                        cluster_flags);
    1943           0 :         if (status != CAIRO_INT_STATUS_UNSUPPORTED) {
    1944           0 :             if (status == CAIRO_STATUS_SUCCESS) {
    1945             :                 /* The checks here are crude; we only should do them in
    1946             :                  * user-font backend, but they don't hurt here.  This stuff
    1947             :                  * can be hard to get right. */
    1948             : 
    1949           0 :                 if (*num_glyphs < 0) {
    1950           0 :                     status = _cairo_error (CAIRO_STATUS_NEGATIVE_COUNT);
    1951           0 :                     goto DONE;
    1952             :                 }
    1953           0 :                 if (num_glyphs && *glyphs == NULL) {
    1954           0 :                     status = _cairo_error (CAIRO_STATUS_NULL_POINTER);
    1955           0 :                     goto DONE;
    1956             :                 }
    1957             : 
    1958           0 :                 if (clusters) {
    1959           0 :                     if (*num_clusters < 0) {
    1960           0 :                         status = _cairo_error (CAIRO_STATUS_NEGATIVE_COUNT);
    1961           0 :                         goto DONE;
    1962             :                     }
    1963           0 :                     if (num_clusters && *clusters == NULL) {
    1964           0 :                         status = _cairo_error (CAIRO_STATUS_NULL_POINTER);
    1965           0 :                         goto DONE;
    1966             :                     }
    1967             : 
    1968             :                     /* Don't trust the backend, validate clusters! */
    1969           0 :                     status =
    1970           0 :                         _cairo_validate_text_clusters (utf8, utf8_len,
    1971             :                                                        *glyphs, *num_glyphs,
    1972             :                                                        *clusters, *num_clusters,
    1973             :                                                        *cluster_flags);
    1974             :                 }
    1975             :             }
    1976             : 
    1977           0 :             goto DONE;
    1978             :         }
    1979             :     }
    1980             : 
    1981           0 :     if (*num_glyphs < num_chars) {
    1982           0 :         *glyphs = cairo_glyph_allocate (num_chars);
    1983           0 :         if (unlikely (*glyphs == NULL)) {
    1984           0 :             status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
    1985           0 :             goto DONE;
    1986             :         }
    1987             :     }
    1988           0 :     *num_glyphs = num_chars;
    1989             : 
    1990           0 :     if (clusters) {
    1991           0 :         if (*num_clusters < num_chars) {
    1992           0 :             *clusters = cairo_text_cluster_allocate (num_chars);
    1993           0 :             if (unlikely (*clusters == NULL)) {
    1994           0 :                 status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
    1995           0 :                 goto DONE;
    1996             :             }
    1997             :         }
    1998           0 :         *num_clusters = num_chars;
    1999             :     }
    2000             : 
    2001           0 :     if (num_chars > CACHING_THRESHOLD)
    2002           0 :         status = cairo_scaled_font_text_to_glyphs_internal_cached (scaled_font,
    2003             :                                                                      x, y,
    2004             :                                                                      utf8,
    2005             :                                                                      *glyphs,
    2006             :                                                                      clusters,
    2007             :                                                                      num_chars);
    2008             :     else
    2009           0 :         status = cairo_scaled_font_text_to_glyphs_internal_uncached (scaled_font,
    2010             :                                                                    x, y,
    2011             :                                                                    utf8,
    2012             :                                                                    *glyphs,
    2013             :                                                                    clusters,
    2014             :                                                                    num_chars);
    2015             : 
    2016             :  DONE: /* error that should be logged on scaled_font happened */
    2017           0 :     _cairo_scaled_font_thaw_cache (scaled_font);
    2018             : 
    2019           0 :     if (unlikely (status)) {
    2020           0 :         *num_glyphs = 0;
    2021           0 :         if (*glyphs != orig_glyphs) {
    2022           0 :             cairo_glyph_free (*glyphs);
    2023           0 :             *glyphs = orig_glyphs;
    2024             :         }
    2025             : 
    2026           0 :         if (clusters) {
    2027           0 :             *num_clusters = 0;
    2028           0 :             if (*clusters != orig_clusters) {
    2029           0 :                 cairo_text_cluster_free (*clusters);
    2030           0 :                 *clusters = orig_clusters;
    2031             :             }
    2032             :         }
    2033             :     }
    2034             : 
    2035           0 :     return _cairo_scaled_font_set_error (scaled_font, status);
    2036             : 
    2037             :  BAIL: /* error with input arguments */
    2038             : 
    2039           0 :     if (num_glyphs)
    2040           0 :         *num_glyphs = 0;
    2041             : 
    2042           0 :     if (num_clusters)
    2043           0 :         *num_clusters = 0;
    2044             : 
    2045           0 :     return status;
    2046             : }
    2047             : slim_hidden_def (cairo_scaled_font_text_to_glyphs);
    2048             : 
    2049             : static inline cairo_bool_t
    2050           0 : _range_contains_glyph (const cairo_box_t *extents,
    2051             :                        cairo_fixed_t left,
    2052             :                        cairo_fixed_t top,
    2053             :                        cairo_fixed_t right,
    2054             :                        cairo_fixed_t bottom)
    2055             : {
    2056           0 :     return right > extents->p1.x &&
    2057           0 :            left < extents->p2.x &&
    2058           0 :            bottom > extents->p1.y &&
    2059           0 :            top < extents->p2.y;
    2060             : }
    2061             : 
    2062             : /*
    2063             :  * Compute a device-space bounding box for the glyphs.
    2064             :  */
    2065             : cairo_status_t
    2066           0 : _cairo_scaled_font_glyph_device_extents (cairo_scaled_font_t     *scaled_font,
    2067             :                                          const cairo_glyph_t     *glyphs,
    2068             :                                          int                      num_glyphs,
    2069             :                                          cairo_rectangle_int_t   *extents,
    2070             :                                          cairo_bool_t *overlap_out)
    2071             : {
    2072           0 :     cairo_status_t status = CAIRO_STATUS_SUCCESS;
    2073           0 :     cairo_box_t box = { { INT_MAX, INT_MAX }, { INT_MIN, INT_MIN }};
    2074             :     cairo_scaled_glyph_t *glyph_cache[64];
    2075           0 :     cairo_bool_t overlap = overlap_out ? FALSE : TRUE;
    2076           0 :     cairo_round_glyph_positions_t round_glyph_positions = _cairo_font_options_get_round_glyph_positions (&scaled_font->options);
    2077             :     int i;
    2078             : 
    2079           0 :     if (unlikely (scaled_font->status))
    2080           0 :         return scaled_font->status;
    2081             : 
    2082           0 :     _cairo_scaled_font_freeze_cache (scaled_font);
    2083             : 
    2084           0 :     memset (glyph_cache, 0, sizeof (glyph_cache));
    2085             : 
    2086           0 :     for (i = 0; i < num_glyphs; i++) {
    2087             :         cairo_scaled_glyph_t    *scaled_glyph;
    2088             :         cairo_fixed_t x, y, x1, y1, x2, y2;
    2089           0 :         int cache_index = glyphs[i].index % ARRAY_LENGTH (glyph_cache);
    2090             : 
    2091           0 :         scaled_glyph = glyph_cache[cache_index];
    2092           0 :         if (scaled_glyph == NULL ||
    2093           0 :             _cairo_scaled_glyph_index (scaled_glyph) != glyphs[i].index)
    2094             :         {
    2095           0 :             status = _cairo_scaled_glyph_lookup (scaled_font,
    2096           0 :                                                  glyphs[i].index,
    2097             :                                                  CAIRO_SCALED_GLYPH_INFO_METRICS,
    2098             :                                                  &scaled_glyph);
    2099           0 :             if (unlikely (status))
    2100           0 :                 break;
    2101             : 
    2102           0 :             glyph_cache[cache_index] = scaled_glyph;
    2103             :         }
    2104             : 
    2105           0 :         if (round_glyph_positions == CAIRO_ROUND_GLYPH_POS_ON)
    2106           0 :             x = _cairo_fixed_from_int (_cairo_lround (glyphs[i].x));
    2107             :         else
    2108           0 :             x = _cairo_fixed_from_double (glyphs[i].x);
    2109           0 :         x1 = x + scaled_glyph->bbox.p1.x;
    2110           0 :         x2 = x + scaled_glyph->bbox.p2.x;
    2111             : 
    2112           0 :         if (round_glyph_positions == CAIRO_ROUND_GLYPH_POS_ON)
    2113           0 :             y = _cairo_fixed_from_int (_cairo_lround (glyphs[i].y));
    2114             :         else
    2115           0 :             y = _cairo_fixed_from_double (glyphs[i].y);
    2116           0 :         y1 = y + scaled_glyph->bbox.p1.y;
    2117           0 :         y2 = y + scaled_glyph->bbox.p2.y;
    2118             : 
    2119           0 :         if (overlap == FALSE)
    2120           0 :             overlap = _range_contains_glyph (&box, x1, y1, x2, y2);
    2121             : 
    2122           0 :         if (x1 < box.p1.x) box.p1.x = x1;
    2123           0 :         if (x2 > box.p2.x) box.p2.x = x2;
    2124           0 :         if (y1 < box.p1.y) box.p1.y = y1;
    2125           0 :         if (y2 > box.p2.y) box.p2.y = y2;
    2126             :     }
    2127             : 
    2128           0 :     _cairo_scaled_font_thaw_cache (scaled_font);
    2129           0 :     if (unlikely (status))
    2130           0 :         return _cairo_scaled_font_set_error (scaled_font, status);
    2131             : 
    2132           0 :     if (box.p1.x < box.p2.x) {
    2133           0 :         _cairo_box_round_to_rectangle (&box, extents);
    2134             :     } else {
    2135           0 :         extents->x = extents->y = 0;
    2136           0 :         extents->width = extents->height = 0;
    2137             :     }
    2138             : 
    2139           0 :     if (overlap_out != NULL)
    2140           0 :         *overlap_out = overlap;
    2141             : 
    2142           0 :     return CAIRO_STATUS_SUCCESS;
    2143             : }
    2144             : 
    2145             : void
    2146           0 : _cairo_scaled_font_glyph_approximate_extents (cairo_scaled_font_t        *scaled_font,
    2147             :                                               const cairo_glyph_t        *glyphs,
    2148             :                                               int                      num_glyphs,
    2149             :                                               cairo_rectangle_int_t   *extents)
    2150             : {
    2151           0 :     double x0 = HUGE_VAL, x1 = -HUGE_VAL;
    2152           0 :     double y0 = HUGE_VAL, y1 = -HUGE_VAL;
    2153             :     int i;
    2154             : 
    2155           0 :     for (i = 0; i < num_glyphs; i++) {
    2156             :         double g;
    2157             : 
    2158           0 :         g = glyphs[i].x;
    2159           0 :         if (g < x0) x0 = g;
    2160           0 :         if (g > x1) x1 = g;
    2161             : 
    2162           0 :         g = glyphs[i].y;
    2163           0 :         if (g < y0) y0 = g;
    2164           0 :         if (g > y1) y1 = g;
    2165             :     }
    2166             : 
    2167           0 :     if (x0 <= x1 && y0 <= y1) {
    2168           0 :         extents->x = floor (x0 - scaled_font->extents.max_x_advance);
    2169           0 :         extents->width = ceil (x1 + scaled_font->extents.max_x_advance);
    2170           0 :         extents->width -= extents->x;
    2171             : 
    2172           0 :         extents->y = floor (y0 - scaled_font->extents.ascent);
    2173           0 :         extents->height = ceil (y1 + scaled_font->extents.descent);
    2174           0 :         extents->height -= extents->y;
    2175             :     } else {
    2176           0 :         extents->x = extents->y = 0;
    2177           0 :         extents->width = extents->height = 0;
    2178             :     }
    2179           0 : }
    2180             : 
    2181             : cairo_status_t
    2182           0 : _cairo_scaled_font_show_glyphs (cairo_scaled_font_t     *scaled_font,
    2183             :                                 cairo_operator_t         op,
    2184             :                                 const cairo_pattern_t   *pattern,
    2185             :                                 cairo_surface_t         *surface,
    2186             :                                 int                      source_x,
    2187             :                                 int                      source_y,
    2188             :                                 int                      dest_x,
    2189             :                                 int                      dest_y,
    2190             :                                 unsigned int             width,
    2191             :                                 unsigned int             height,
    2192             :                                 cairo_glyph_t           *glyphs,
    2193             :                                 int                      num_glyphs,
    2194             :                                 cairo_region_t          *clip_region)
    2195             : {
    2196             :     cairo_status_t status;
    2197           0 :     cairo_surface_t *mask = NULL;
    2198           0 :     cairo_format_t mask_format = CAIRO_FORMAT_A1; /* shut gcc up */
    2199             :     cairo_surface_pattern_t mask_pattern;
    2200             :     int i;
    2201             : 
    2202             :     /* These operators aren't interpreted the same way by the backends;
    2203             :      * they are implemented in terms of other operators in cairo-gstate.c
    2204             :      */
    2205           0 :     assert (op != CAIRO_OPERATOR_SOURCE && op != CAIRO_OPERATOR_CLEAR);
    2206             : 
    2207           0 :     if (scaled_font->status)
    2208           0 :         return scaled_font->status;
    2209             : 
    2210           0 :     if (!num_glyphs)
    2211           0 :         return CAIRO_STATUS_SUCCESS;
    2212             : 
    2213           0 :     if (scaled_font->backend->show_glyphs != NULL) {
    2214           0 :         int remaining_glyphs = num_glyphs;
    2215           0 :         status = scaled_font->backend->show_glyphs (scaled_font,
    2216             :                                                     op, pattern,
    2217             :                                                     surface,
    2218             :                                                     source_x, source_y,
    2219             :                                                     dest_x, dest_y,
    2220             :                                                     width, height,
    2221             :                                                     glyphs, num_glyphs,
    2222             :                                                     clip_region,
    2223             :                                                     &remaining_glyphs);
    2224           0 :         glyphs += num_glyphs - remaining_glyphs;
    2225           0 :         num_glyphs = remaining_glyphs;
    2226           0 :         if (remaining_glyphs == 0)
    2227           0 :             status = CAIRO_STATUS_SUCCESS;
    2228           0 :         if (status != CAIRO_INT_STATUS_UNSUPPORTED)
    2229           0 :             return _cairo_scaled_font_set_error (scaled_font, status);
    2230             :     }
    2231             : 
    2232             :     /* Font display routine either does not exist or failed. */
    2233             : 
    2234           0 :     _cairo_scaled_font_freeze_cache (scaled_font);
    2235             : 
    2236           0 :     for (i = 0; i < num_glyphs; i++) {
    2237             :         int x, y;
    2238             :         cairo_image_surface_t *glyph_surface;
    2239             :         cairo_scaled_glyph_t *scaled_glyph;
    2240             : 
    2241           0 :         status = _cairo_scaled_glyph_lookup (scaled_font,
    2242           0 :                                              glyphs[i].index,
    2243             :                                              CAIRO_SCALED_GLYPH_INFO_SURFACE,
    2244             :                                              &scaled_glyph);
    2245             : 
    2246           0 :         if (unlikely (status))
    2247           0 :             goto CLEANUP_MASK;
    2248             : 
    2249           0 :         glyph_surface = scaled_glyph->surface;
    2250             : 
    2251             :         /* To start, create the mask using the format from the first
    2252             :          * glyph. Later we'll deal with different formats. */
    2253           0 :         if (mask == NULL) {
    2254           0 :             mask_format = glyph_surface->format;
    2255           0 :             mask = cairo_image_surface_create (mask_format, width, height);
    2256           0 :             status = mask->status;
    2257           0 :             if (unlikely (status))
    2258           0 :                 goto CLEANUP_MASK;
    2259             :         }
    2260             : 
    2261             :         /* If we have glyphs of different formats, we "upgrade" the mask
    2262             :          * to the wider of the formats. */
    2263           0 :         if (glyph_surface->format != mask_format &&
    2264           0 :             _cairo_format_bits_per_pixel (mask_format) <
    2265           0 :             _cairo_format_bits_per_pixel (glyph_surface->format) )
    2266             :         {
    2267             :             cairo_surface_t *new_mask;
    2268             : 
    2269           0 :             switch (glyph_surface->format) {
    2270             :             case CAIRO_FORMAT_ARGB32:
    2271             :             case CAIRO_FORMAT_A8:
    2272             :             case CAIRO_FORMAT_A1:
    2273           0 :                 mask_format = glyph_surface->format;
    2274           0 :                 break;
    2275             :             case CAIRO_FORMAT_RGB16_565:
    2276             :             case CAIRO_FORMAT_RGB24:
    2277             :             case CAIRO_FORMAT_INVALID:
    2278             :             default:
    2279           0 :                 ASSERT_NOT_REACHED;
    2280           0 :                 mask_format = CAIRO_FORMAT_ARGB32;
    2281           0 :                 break;
    2282             :             }
    2283             : 
    2284           0 :             new_mask = cairo_image_surface_create (mask_format, width, height);
    2285           0 :             status = new_mask->status;
    2286           0 :             if (unlikely (status)) {
    2287           0 :                 cairo_surface_destroy (new_mask);
    2288           0 :                 goto CLEANUP_MASK;
    2289             :             }
    2290             : 
    2291           0 :             _cairo_pattern_init_for_surface (&mask_pattern, mask);
    2292             :             /* Note that we only upgrade masks, i.e. A1 -> A8 -> ARGB32, so there is
    2293             :              * never any component alpha here.
    2294             :              */
    2295           0 :             status = _cairo_surface_composite (CAIRO_OPERATOR_ADD,
    2296             :                                                &_cairo_pattern_white.base,
    2297             :                                                &mask_pattern.base,
    2298             :                                                new_mask,
    2299             :                                                0, 0,
    2300             :                                                0, 0,
    2301             :                                                0, 0,
    2302             :                                                width, height,
    2303             :                                                NULL);
    2304             : 
    2305           0 :             _cairo_pattern_fini (&mask_pattern.base);
    2306             : 
    2307           0 :             if (unlikely (status)) {
    2308           0 :                 cairo_surface_destroy (new_mask);
    2309           0 :                 goto CLEANUP_MASK;
    2310             :             }
    2311             : 
    2312           0 :             cairo_surface_destroy (mask);
    2313           0 :             mask = new_mask;
    2314             :         }
    2315             : 
    2316           0 :         if (glyph_surface->width && glyph_surface->height) {
    2317             :             cairo_surface_pattern_t glyph_pattern;
    2318             : 
    2319             :             /* round glyph locations to the nearest pixel */
    2320             :             /* XXX: FRAGILE: We're ignoring device_transform scaling here. A bug? */
    2321           0 :             x = _cairo_lround (glyphs[i].x -
    2322           0 :                                glyph_surface->base.device_transform.x0);
    2323           0 :             y = _cairo_lround (glyphs[i].y -
    2324           0 :                                glyph_surface->base.device_transform.y0);
    2325             : 
    2326           0 :             _cairo_pattern_init_for_surface (&glyph_pattern,
    2327             :                                              &glyph_surface->base);
    2328           0 :             if (mask_format == CAIRO_FORMAT_ARGB32)
    2329           0 :                 glyph_pattern.base.has_component_alpha = TRUE;
    2330             : 
    2331           0 :             status = _cairo_surface_composite (CAIRO_OPERATOR_ADD,
    2332             :                                                &_cairo_pattern_white.base,
    2333             :                                                &glyph_pattern.base,
    2334             :                                                mask,
    2335             :                                                0, 0,
    2336             :                                                0, 0,
    2337             :                                                x - dest_x, y - dest_y,
    2338           0 :                                                glyph_surface->width,
    2339           0 :                                                glyph_surface->height,
    2340             :                                                NULL);
    2341             : 
    2342           0 :             _cairo_pattern_fini (&glyph_pattern.base);
    2343             : 
    2344           0 :             if (unlikely (status))
    2345           0 :                 goto CLEANUP_MASK;
    2346             :         }
    2347             :     }
    2348             : 
    2349           0 :     _cairo_pattern_init_for_surface (&mask_pattern, mask);
    2350           0 :     if (mask_format == CAIRO_FORMAT_ARGB32)
    2351           0 :         mask_pattern.base.has_component_alpha = TRUE;
    2352             : 
    2353           0 :     status = _cairo_surface_composite (op, pattern, &mask_pattern.base,
    2354             :                                        surface,
    2355             :                                        source_x, source_y,
    2356             :                                        0,        0,
    2357             :                                        dest_x,   dest_y,
    2358             :                                        width,    height,
    2359             :                                        clip_region);
    2360             : 
    2361           0 :     _cairo_pattern_fini (&mask_pattern.base);
    2362             : 
    2363             : CLEANUP_MASK:
    2364           0 :     _cairo_scaled_font_thaw_cache (scaled_font);
    2365             : 
    2366           0 :     if (mask != NULL)
    2367           0 :         cairo_surface_destroy (mask);
    2368           0 :     return _cairo_scaled_font_set_error (scaled_font, status);
    2369             : }
    2370             : 
    2371             : /* Add a single-device-unit rectangle to a path. */
    2372             : static cairo_status_t
    2373           0 : _add_unit_rectangle_to_path (cairo_path_fixed_t *path,
    2374             :                              cairo_fixed_t x,
    2375             :                              cairo_fixed_t y)
    2376             : {
    2377             :     cairo_status_t status;
    2378             : 
    2379           0 :     status = _cairo_path_fixed_move_to (path, x, y);
    2380           0 :     if (unlikely (status))
    2381           0 :         return status;
    2382             : 
    2383           0 :     status = _cairo_path_fixed_rel_line_to (path,
    2384             :                                             _cairo_fixed_from_int (1),
    2385             :                                             _cairo_fixed_from_int (0));
    2386           0 :     if (unlikely (status))
    2387           0 :         return status;
    2388             : 
    2389           0 :     status = _cairo_path_fixed_rel_line_to (path,
    2390             :                                             _cairo_fixed_from_int (0),
    2391             :                                             _cairo_fixed_from_int (1));
    2392           0 :     if (unlikely (status))
    2393           0 :         return status;
    2394             : 
    2395           0 :     status = _cairo_path_fixed_rel_line_to (path,
    2396             :                                             _cairo_fixed_from_int (-1),
    2397             :                                             _cairo_fixed_from_int (0));
    2398           0 :     if (unlikely (status))
    2399           0 :         return status;
    2400             : 
    2401           0 :     return _cairo_path_fixed_close_path (path);
    2402             : }
    2403             : 
    2404             : /**
    2405             :  * _trace_mask_to_path:
    2406             :  * @bitmap: An alpha mask (either %CAIRO_FORMAT_A1 or %CAIRO_FORMAT_A8)
    2407             :  * @path: An initialized path to hold the result
    2408             :  *
    2409             :  * Given a mask surface, (an alpha image), fill out the provided path
    2410             :  * so that when filled it would result in something that approximates
    2411             :  * the mask.
    2412             :  *
    2413             :  * Note: The current tracing code here is extremely primitive. It
    2414             :  * operates only on an A1 surface, (converting an A8 surface to A1 if
    2415             :  * necessary), and performs the tracing by drawing a little square
    2416             :  * around each pixel that is on in the mask. We do not pretend that
    2417             :  * this is a high-quality result. But we are leaving it up to someone
    2418             :  * who cares enough about getting a better result to implement
    2419             :  * something more sophisticated.
    2420             :  **/
    2421             : static cairo_status_t
    2422           0 : _trace_mask_to_path (cairo_image_surface_t *mask,
    2423             :                      cairo_path_fixed_t *path,
    2424             :                      double tx, double ty)
    2425             : {
    2426             :     const uint8_t *row;
    2427             :     int rows, cols, bytes_per_row;
    2428             :     int x, y, bit;
    2429             :     double xoff, yoff;
    2430             :     cairo_fixed_t x0, y0;
    2431             :     cairo_fixed_t px, py;
    2432             :     cairo_status_t status;
    2433             : 
    2434           0 :     mask = _cairo_image_surface_coerce_to_format (mask, CAIRO_FORMAT_A1);
    2435           0 :     status = mask->base.status;
    2436           0 :     if (unlikely (status))
    2437           0 :         return status;
    2438             : 
    2439           0 :     cairo_surface_get_device_offset (&mask->base, &xoff, &yoff);
    2440           0 :     x0 = _cairo_fixed_from_double (tx - xoff);
    2441           0 :     y0 = _cairo_fixed_from_double (ty - yoff);
    2442             : 
    2443           0 :     bytes_per_row = (mask->width + 7) / 8;
    2444           0 :     row = mask->data;
    2445           0 :     for (y = 0, rows = mask->height; rows--; row += mask->stride, y++) {
    2446           0 :         const uint8_t *byte_ptr = row;
    2447           0 :         x = 0;
    2448           0 :         py = _cairo_fixed_from_int (y);
    2449           0 :         for (cols = bytes_per_row; cols--; ) {
    2450           0 :             uint8_t byte = *byte_ptr++;
    2451           0 :             if (byte == 0) {
    2452           0 :                 x += 8;
    2453           0 :                 continue;
    2454             :             }
    2455             : 
    2456           0 :             byte = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (byte);
    2457           0 :             for (bit = 1 << 7; bit && x < mask->width; bit >>= 1, x++) {
    2458           0 :                 if (byte & bit) {
    2459           0 :                     px = _cairo_fixed_from_int (x);
    2460           0 :                     status = _add_unit_rectangle_to_path (path,
    2461             :                                                           px + x0,
    2462             :                                                           py + y0);
    2463           0 :                     if (unlikely (status))
    2464           0 :                         goto BAIL;
    2465             :                 }
    2466             :             }
    2467             :         }
    2468             :     }
    2469             : 
    2470             : BAIL:
    2471           0 :     cairo_surface_destroy (&mask->base);
    2472             : 
    2473           0 :     return status;
    2474             : }
    2475             : 
    2476             : cairo_status_t
    2477           0 : _cairo_scaled_font_glyph_path (cairo_scaled_font_t *scaled_font,
    2478             :                                const cairo_glyph_t *glyphs,
    2479             :                                int                  num_glyphs,
    2480             :                                cairo_path_fixed_t  *path)
    2481             : {
    2482             :     cairo_status_t status;
    2483             :     int i;
    2484             : 
    2485           0 :     status = scaled_font->status;
    2486           0 :     if (unlikely (status))
    2487           0 :         return status;
    2488             : 
    2489           0 :     _cairo_scaled_font_freeze_cache (scaled_font);
    2490           0 :     for (i = 0; i < num_glyphs; i++) {
    2491             :         cairo_scaled_glyph_t *scaled_glyph;
    2492             : 
    2493           0 :         status = _cairo_scaled_glyph_lookup (scaled_font,
    2494           0 :                                              glyphs[i].index,
    2495             :                                              CAIRO_SCALED_GLYPH_INFO_PATH,
    2496             :                                              &scaled_glyph);
    2497           0 :         if (status == CAIRO_STATUS_SUCCESS) {
    2498           0 :             status = _cairo_path_fixed_append (path,
    2499           0 :                                                scaled_glyph->path, CAIRO_DIRECTION_FORWARD,
    2500           0 :                                                _cairo_fixed_from_double (glyphs[i].x),
    2501           0 :                                                _cairo_fixed_from_double (glyphs[i].y));
    2502             : 
    2503           0 :         } else if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
    2504             :             /* If the font is incapable of providing a path, then we'll
    2505             :              * have to trace our own from a surface.
    2506             :              */
    2507           0 :             status = _cairo_scaled_glyph_lookup (scaled_font,
    2508           0 :                                                  glyphs[i].index,
    2509             :                                                  CAIRO_SCALED_GLYPH_INFO_SURFACE,
    2510             :                                                  &scaled_glyph);
    2511           0 :             if (unlikely (status))
    2512           0 :                 goto BAIL;
    2513             : 
    2514           0 :             status = _trace_mask_to_path (scaled_glyph->surface, path,
    2515           0 :                                           glyphs[i].x, glyphs[i].y);
    2516             :         }
    2517             : 
    2518           0 :         if (unlikely (status))
    2519           0 :             goto BAIL;
    2520             :     }
    2521             :   BAIL:
    2522           0 :     _cairo_scaled_font_thaw_cache (scaled_font);
    2523             : 
    2524           0 :     return _cairo_scaled_font_set_error (scaled_font, status);
    2525             : }
    2526             : 
    2527             : /**
    2528             :  * _cairo_scaled_glyph_set_metrics:
    2529             :  * @scaled_glyph: a #cairo_scaled_glyph_t
    2530             :  * @scaled_font: a #cairo_scaled_font_t
    2531             :  * @fs_metrics: a #cairo_text_extents_t in font space
    2532             :  *
    2533             :  * _cairo_scaled_glyph_set_metrics() stores user space metrics
    2534             :  * for the specified glyph given font space metrics. It is
    2535             :  * called by the font backend when initializing a glyph with
    2536             :  * %CAIRO_SCALED_GLYPH_INFO_METRICS.
    2537             :  **/
    2538             : void
    2539         132 : _cairo_scaled_glyph_set_metrics (cairo_scaled_glyph_t *scaled_glyph,
    2540             :                                  cairo_scaled_font_t *scaled_font,
    2541             :                                  cairo_text_extents_t *fs_metrics)
    2542             : {
    2543         132 :     cairo_bool_t first = TRUE;
    2544             :     double hm, wm;
    2545         132 :     double min_user_x = 0.0, max_user_x = 0.0, min_user_y = 0.0, max_user_y = 0.0;
    2546         132 :     double min_device_x = 0.0, max_device_x = 0.0, min_device_y = 0.0, max_device_y = 0.0;
    2547             :     double device_x_advance, device_y_advance;
    2548             : 
    2549         132 :     scaled_glyph->fs_metrics = *fs_metrics;
    2550             : 
    2551         396 :     for (hm = 0.0; hm <= 1.0; hm += 1.0)
    2552         792 :         for (wm = 0.0; wm <= 1.0; wm += 1.0) {
    2553             :             double x, y;
    2554             : 
    2555             :             /* Transform this corner to user space */
    2556         528 :             x = fs_metrics->x_bearing + fs_metrics->width * wm;
    2557         528 :             y = fs_metrics->y_bearing + fs_metrics->height * hm;
    2558         528 :             cairo_matrix_transform_point (&scaled_font->font_matrix,
    2559             :                                           &x, &y);
    2560         528 :             if (first) {
    2561         132 :                 min_user_x = max_user_x = x;
    2562         132 :                 min_user_y = max_user_y = y;
    2563             :             } else {
    2564         396 :                 if (x < min_user_x) min_user_x = x;
    2565         396 :                 if (x > max_user_x) max_user_x = x;
    2566         396 :                 if (y < min_user_y) min_user_y = y;
    2567         396 :                 if (y > max_user_y) max_user_y = y;
    2568             :             }
    2569             : 
    2570             :             /* Transform this corner to device space from glyph origin */
    2571         528 :             x = fs_metrics->x_bearing + fs_metrics->width * wm;
    2572         528 :             y = fs_metrics->y_bearing + fs_metrics->height * hm;
    2573         528 :             cairo_matrix_transform_distance (&scaled_font->scale,
    2574             :                                              &x, &y);
    2575             : 
    2576         528 :             if (first) {
    2577         132 :                 min_device_x = max_device_x = x;
    2578         132 :                 min_device_y = max_device_y = y;
    2579             :             } else {
    2580         396 :                 if (x < min_device_x) min_device_x = x;
    2581         396 :                 if (x > max_device_x) max_device_x = x;
    2582         396 :                 if (y < min_device_y) min_device_y = y;
    2583         396 :                 if (y > max_device_y) max_device_y = y;
    2584             :             }
    2585         528 :             first = FALSE;
    2586             :         }
    2587         132 :     scaled_glyph->metrics.x_bearing = min_user_x;
    2588         132 :     scaled_glyph->metrics.y_bearing = min_user_y;
    2589         132 :     scaled_glyph->metrics.width = max_user_x - min_user_x;
    2590         132 :     scaled_glyph->metrics.height = max_user_y - min_user_y;
    2591             : 
    2592         132 :     scaled_glyph->metrics.x_advance = fs_metrics->x_advance;
    2593         132 :     scaled_glyph->metrics.y_advance = fs_metrics->y_advance;
    2594         132 :     cairo_matrix_transform_distance (&scaled_font->font_matrix,
    2595             :                                      &scaled_glyph->metrics.x_advance,
    2596             :                                      &scaled_glyph->metrics.y_advance);
    2597             : 
    2598         132 :     device_x_advance = fs_metrics->x_advance;
    2599         132 :     device_y_advance = fs_metrics->y_advance;
    2600         132 :     cairo_matrix_transform_distance (&scaled_font->scale,
    2601             :                                      &device_x_advance,
    2602             :                                      &device_y_advance);
    2603             : 
    2604         132 :     scaled_glyph->bbox.p1.x = _cairo_fixed_from_double (min_device_x);
    2605         132 :     scaled_glyph->bbox.p1.y = _cairo_fixed_from_double (min_device_y);
    2606         132 :     scaled_glyph->bbox.p2.x = _cairo_fixed_from_double (max_device_x);
    2607         132 :     scaled_glyph->bbox.p2.y = _cairo_fixed_from_double (max_device_y);
    2608             : 
    2609         132 :     scaled_glyph->x_advance = _cairo_lround (device_x_advance);
    2610         132 :     scaled_glyph->y_advance = _cairo_lround (device_y_advance);
    2611             : 
    2612         132 :     scaled_glyph->has_info |= CAIRO_SCALED_GLYPH_INFO_METRICS;
    2613         132 : }
    2614             : 
    2615             : void
    2616           0 : _cairo_scaled_glyph_set_surface (cairo_scaled_glyph_t *scaled_glyph,
    2617             :                                  cairo_scaled_font_t *scaled_font,
    2618             :                                  cairo_image_surface_t *surface)
    2619             : {
    2620           0 :     if (scaled_glyph->surface != NULL)
    2621           0 :         cairo_surface_destroy (&scaled_glyph->surface->base);
    2622             : 
    2623             :     /* sanity check the backend glyph contents */
    2624             :     _cairo_debug_check_image_surface_is_defined (&surface->base);
    2625           0 :     scaled_glyph->surface = surface;
    2626             : 
    2627           0 :     if (surface != NULL)
    2628           0 :         scaled_glyph->has_info |= CAIRO_SCALED_GLYPH_INFO_SURFACE;
    2629             :     else
    2630           0 :         scaled_glyph->has_info &= ~CAIRO_SCALED_GLYPH_INFO_SURFACE;
    2631           0 : }
    2632             : 
    2633             : void
    2634           0 : _cairo_scaled_glyph_set_path (cairo_scaled_glyph_t *scaled_glyph,
    2635             :                               cairo_scaled_font_t *scaled_font,
    2636             :                               cairo_path_fixed_t *path)
    2637             : {
    2638           0 :     if (scaled_glyph->path != NULL)
    2639           0 :         _cairo_path_fixed_destroy (scaled_glyph->path);
    2640             : 
    2641           0 :     scaled_glyph->path = path;
    2642             : 
    2643           0 :     if (path != NULL)
    2644           0 :         scaled_glyph->has_info |= CAIRO_SCALED_GLYPH_INFO_PATH;
    2645             :     else
    2646           0 :         scaled_glyph->has_info &= ~CAIRO_SCALED_GLYPH_INFO_PATH;
    2647           0 : }
    2648             : 
    2649             : void
    2650           0 : _cairo_scaled_glyph_set_recording_surface (cairo_scaled_glyph_t *scaled_glyph,
    2651             :                                            cairo_scaled_font_t *scaled_font,
    2652             :                                            cairo_surface_t *recording_surface)
    2653             : {
    2654           0 :     if (scaled_glyph->recording_surface != NULL) {
    2655           0 :         cairo_surface_finish (scaled_glyph->recording_surface);
    2656           0 :         cairo_surface_destroy (scaled_glyph->recording_surface);
    2657             :     }
    2658             : 
    2659           0 :     scaled_glyph->recording_surface = recording_surface;
    2660             : 
    2661           0 :     if (recording_surface != NULL)
    2662           0 :         scaled_glyph->has_info |= CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE;
    2663             :     else
    2664           0 :         scaled_glyph->has_info &= ~CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE;
    2665           0 : }
    2666             : 
    2667             : static cairo_bool_t
    2668           0 : _cairo_scaled_glyph_page_can_remove (const void *closure)
    2669             : {
    2670           0 :     const cairo_scaled_glyph_page_t *page = closure;
    2671             :     const cairo_scaled_font_t *scaled_font;
    2672             : 
    2673           0 :     scaled_font = (cairo_scaled_font_t *) page->cache_entry.hash;
    2674           0 :     return scaled_font->cache_frozen == 0;
    2675             : }
    2676             : 
    2677             : static cairo_status_t
    2678         132 : _cairo_scaled_font_allocate_glyph (cairo_scaled_font_t *scaled_font,
    2679             :                                    cairo_scaled_glyph_t **scaled_glyph)
    2680             : {
    2681             :     cairo_scaled_glyph_page_t *page;
    2682             :     cairo_status_t status;
    2683             : 
    2684             :     /* only the first page in the list may contain available slots */
    2685         132 :     if (! cairo_list_is_empty (&scaled_font->glyph_pages)) {
    2686         126 :         page = cairo_list_last_entry (&scaled_font->glyph_pages,
    2687             :                                       cairo_scaled_glyph_page_t,
    2688             :                                       link);
    2689         126 :         if (page->num_glyphs < CAIRO_SCALED_GLYPH_PAGE_SIZE) {
    2690         125 :             *scaled_glyph = &page->glyphs[page->num_glyphs++];
    2691         125 :             return CAIRO_STATUS_SUCCESS;
    2692             :         }
    2693             :     }
    2694             : 
    2695           7 :     page = malloc (sizeof (cairo_scaled_glyph_page_t));
    2696           7 :     if (unlikely (page == NULL))
    2697           0 :         return _cairo_error (CAIRO_STATUS_NO_MEMORY);
    2698             : 
    2699           7 :     page->cache_entry.hash = (uintptr_t) scaled_font;
    2700           7 :     page->cache_entry.size = 1; /* XXX occupancy weighting? */
    2701           7 :     page->num_glyphs = 0;
    2702             : 
    2703           7 :     CAIRO_MUTEX_LOCK (_cairo_scaled_glyph_page_cache_mutex);
    2704           7 :     if (scaled_font->global_cache_frozen == FALSE) {
    2705           7 :         if (unlikely (cairo_scaled_glyph_page_cache.hash_table == NULL)) {
    2706           2 :             status = _cairo_cache_init (&cairo_scaled_glyph_page_cache,
    2707             :                                         NULL,
    2708             :                                         _cairo_scaled_glyph_page_can_remove,
    2709             :                                         _cairo_scaled_glyph_page_destroy,
    2710             :                                         MAX_GLYPH_PAGES_CACHED);
    2711           2 :             if (unlikely (status)) {
    2712           0 :                 CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex);
    2713           0 :                 free (page);
    2714           0 :                 return status;
    2715             :             }
    2716             :         }
    2717             : 
    2718           7 :         _cairo_cache_freeze (&cairo_scaled_glyph_page_cache);
    2719           7 :         scaled_font->global_cache_frozen = TRUE;
    2720             :     }
    2721             : 
    2722           7 :     status = _cairo_cache_insert (&cairo_scaled_glyph_page_cache,
    2723             :                                   &page->cache_entry);
    2724           7 :     CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex);
    2725           7 :     if (unlikely (status)) {
    2726           0 :         free (page);
    2727           0 :         return status;
    2728             :     }
    2729             : 
    2730           7 :     cairo_list_add_tail (&page->link, &scaled_font->glyph_pages);
    2731             : 
    2732           7 :     *scaled_glyph = &page->glyphs[page->num_glyphs++];
    2733           7 :     return CAIRO_STATUS_SUCCESS;
    2734             : }
    2735             : 
    2736             : static void
    2737           0 : _cairo_scaled_font_free_last_glyph (cairo_scaled_font_t *scaled_font,
    2738             :                                    cairo_scaled_glyph_t *scaled_glyph)
    2739             : {
    2740             :     cairo_scaled_glyph_page_t *page;
    2741             : 
    2742           0 :     assert (! cairo_list_is_empty (&scaled_font->glyph_pages));
    2743           0 :     page = cairo_list_last_entry (&scaled_font->glyph_pages,
    2744             :                                   cairo_scaled_glyph_page_t,
    2745             :                                   link);
    2746           0 :     assert (scaled_glyph == &page->glyphs[page->num_glyphs-1]);
    2747             : 
    2748           0 :     _cairo_scaled_glyph_fini (scaled_font, scaled_glyph);
    2749             : 
    2750           0 :     if (--page->num_glyphs == 0) {
    2751           0 :         _cairo_cache_remove (&cairo_scaled_glyph_page_cache,
    2752             :                              &page->cache_entry);
    2753             :     }
    2754           0 : }
    2755             : 
    2756             : /**
    2757             :  * _cairo_scaled_glyph_lookup:
    2758             :  * @scaled_font: a #cairo_scaled_font_t
    2759             :  * @index: the glyph to create
    2760             :  * @info: a #cairo_scaled_glyph_info_t marking which portions of
    2761             :  * the glyph should be filled in.
    2762             :  * @scaled_glyph_ret: a #cairo_scaled_glyph_t where the glyph
    2763             :  * is returned.
    2764             :  *
    2765             :  * If the desired info is not available, (for example, when trying to
    2766             :  * get INFO_PATH with a bitmapped font), this function will return
    2767             :  * %CAIRO_INT_STATUS_UNSUPPORTED.
    2768             :  *
    2769             :  * Note: This function must be called with the scaled font frozen, and it must
    2770             :  * remain frozen for as long as the @scaled_glyph_ret is alive. (If the scaled
    2771             :  * font was not frozen, then there is no guarantee that the glyph would not be
    2772             :  * evicted before you tried to access it.) See
    2773             :  * _cairo_scaled_font_freeze_cache() and _cairo_scaled_font_thaw_cache().
    2774             :  *
    2775             :  * Returns: a glyph with the requested portions filled in. Glyph
    2776             :  * lookup is cached and glyph will be automatically freed along
    2777             :  * with the scaled_font so no explicit free is required.
    2778             :  * @info can be one or more of:
    2779             :  *  %CAIRO_SCALED_GLYPH_INFO_METRICS - glyph metrics and bounding box
    2780             :  *  %CAIRO_SCALED_GLYPH_INFO_SURFACE - surface holding glyph image
    2781             :  *  %CAIRO_SCALED_GLYPH_INFO_PATH - path holding glyph outline in device space
    2782             :  **/
    2783             : cairo_int_status_t
    2784         339 : _cairo_scaled_glyph_lookup (cairo_scaled_font_t *scaled_font,
    2785             :                             unsigned long index,
    2786             :                             cairo_scaled_glyph_info_t info,
    2787             :                             cairo_scaled_glyph_t **scaled_glyph_ret)
    2788             : {
    2789         339 :     cairo_status_t               status = CAIRO_STATUS_SUCCESS;
    2790             :     cairo_scaled_glyph_t        *scaled_glyph;
    2791             :     cairo_scaled_glyph_info_t    need_info;
    2792             : 
    2793         339 :     *scaled_glyph_ret = NULL;
    2794             : 
    2795         339 :     if (unlikely (scaled_font->status))
    2796           0 :         return scaled_font->status;
    2797             : 
    2798             :     if (CAIRO_INJECT_FAULT ())
    2799             :         return _cairo_error (CAIRO_STATUS_NO_MEMORY);
    2800             : 
    2801             :     /*
    2802             :      * Check cache for glyph
    2803             :      */
    2804         339 :     scaled_glyph = _cairo_hash_table_lookup (scaled_font->glyphs,
    2805             :                                              (cairo_hash_entry_t *) &index);
    2806         339 :     if (scaled_glyph == NULL) {
    2807         132 :         status = _cairo_scaled_font_allocate_glyph (scaled_font, &scaled_glyph);
    2808         132 :         if (unlikely (status))
    2809           0 :             goto err;
    2810             : 
    2811         132 :         memset (scaled_glyph, 0, sizeof (cairo_scaled_glyph_t));
    2812         132 :         _cairo_scaled_glyph_set_index (scaled_glyph, index);
    2813             : 
    2814             :         /* ask backend to initialize metrics and shape fields */
    2815         132 :         status =
    2816         264 :             scaled_font->backend->scaled_glyph_init (scaled_font,
    2817             :                                                      scaled_glyph,
    2818         132 :                                                      info | CAIRO_SCALED_GLYPH_INFO_METRICS);
    2819         132 :         if (unlikely (status)) {
    2820           0 :             _cairo_scaled_font_free_last_glyph (scaled_font, scaled_glyph);
    2821           0 :             goto err;
    2822             :         }
    2823             : 
    2824         132 :         status = _cairo_hash_table_insert (scaled_font->glyphs,
    2825         132 :                                            &scaled_glyph->hash_entry);
    2826         132 :         if (unlikely (status)) {
    2827           0 :             _cairo_scaled_font_free_last_glyph (scaled_font, scaled_glyph);
    2828           0 :             goto err;
    2829             :         }
    2830             :     }
    2831             : 
    2832             :     /*
    2833             :      * Check and see if the glyph, as provided,
    2834             :      * already has the requested data and amend it if not
    2835             :      */
    2836         339 :     need_info = info & ~scaled_glyph->has_info;
    2837         339 :     if (need_info) {
    2838           0 :         status = scaled_font->backend->scaled_glyph_init (scaled_font,
    2839             :                                                           scaled_glyph,
    2840             :                                                           need_info);
    2841           0 :         if (unlikely (status))
    2842           0 :             goto err;
    2843             : 
    2844             :         /* Don't trust the scaled_glyph_init() return value, the font
    2845             :          * backend may not even know about some of the info.  For example,
    2846             :          * no backend other than the user-fonts knows about recording-surface
    2847             :          * glyph info. */
    2848           0 :         if (info & ~scaled_glyph->has_info)
    2849           0 :             return CAIRO_INT_STATUS_UNSUPPORTED;
    2850             :     }
    2851             : 
    2852         339 :     *scaled_glyph_ret = scaled_glyph;
    2853         339 :     return CAIRO_STATUS_SUCCESS;
    2854             : 
    2855             : err:
    2856             :     /* It's not an error for the backend to not support the info we want. */
    2857           0 :     if (status != CAIRO_INT_STATUS_UNSUPPORTED)
    2858           0 :         status = _cairo_scaled_font_set_error (scaled_font, status);
    2859           0 :     return status;
    2860             : }
    2861             : 
    2862             : double
    2863           0 : _cairo_scaled_font_get_max_scale (cairo_scaled_font_t *scaled_font)
    2864             : {
    2865           0 :     return scaled_font->max_scale;
    2866             : }
    2867             : 
    2868             : 
    2869             : /**
    2870             :  * cairo_scaled_font_get_font_face:
    2871             :  * @scaled_font: a #cairo_scaled_font_t
    2872             :  *
    2873             :  * Gets the font face that this scaled font uses.  This is the
    2874             :  * font face passed to cairo_scaled_font_create().
    2875             :  *
    2876             :  * Return value: The #cairo_font_face_t with which @scaled_font was
    2877             :  * created.
    2878             :  *
    2879             :  * Since: 1.2
    2880             :  **/
    2881             : cairo_font_face_t *
    2882         306 : cairo_scaled_font_get_font_face (cairo_scaled_font_t *scaled_font)
    2883             : {
    2884         306 :     if (scaled_font->status)
    2885           0 :         return (cairo_font_face_t*) &_cairo_font_face_nil;
    2886             : 
    2887         306 :     if (scaled_font->original_font_face != NULL)
    2888         306 :         return scaled_font->original_font_face;
    2889             : 
    2890           0 :     return scaled_font->font_face;
    2891             : }
    2892             : slim_hidden_def (cairo_scaled_font_get_font_face);
    2893             : 
    2894             : /**
    2895             :  * cairo_scaled_font_get_font_matrix:
    2896             :  * @scaled_font: a #cairo_scaled_font_t
    2897             :  * @font_matrix: return value for the matrix
    2898             :  *
    2899             :  * Stores the font matrix with which @scaled_font was created into
    2900             :  * @matrix.
    2901             :  *
    2902             :  * Since: 1.2
    2903             :  **/
    2904             : void
    2905          21 : cairo_scaled_font_get_font_matrix (cairo_scaled_font_t  *scaled_font,
    2906             :                                    cairo_matrix_t       *font_matrix)
    2907             : {
    2908          21 :     if (scaled_font->status) {
    2909           0 :         cairo_matrix_init_identity (font_matrix);
    2910           0 :         return;
    2911             :     }
    2912             : 
    2913          21 :     *font_matrix = scaled_font->font_matrix;
    2914             : }
    2915             : slim_hidden_def (cairo_scaled_font_get_font_matrix);
    2916             : 
    2917             : /**
    2918             :  * cairo_scaled_font_get_ctm:
    2919             :  * @scaled_font: a #cairo_scaled_font_t
    2920             :  * @ctm: return value for the CTM
    2921             :  *
    2922             :  * Stores the CTM with which @scaled_font was created into @ctm.
    2923             :  * Note that the translation offsets (x0, y0) of the CTM are ignored
    2924             :  * by cairo_scaled_font_create().  So, the matrix this
    2925             :  * function returns always has 0,0 as x0,y0.
    2926             :  *
    2927             :  * Since: 1.2
    2928             :  **/
    2929             : void
    2930           0 : cairo_scaled_font_get_ctm (cairo_scaled_font_t  *scaled_font,
    2931             :                            cairo_matrix_t       *ctm)
    2932             : {
    2933           0 :     if (scaled_font->status) {
    2934           0 :         cairo_matrix_init_identity (ctm);
    2935           0 :         return;
    2936             :     }
    2937             : 
    2938           0 :     *ctm = scaled_font->ctm;
    2939             : }
    2940             : slim_hidden_def (cairo_scaled_font_get_ctm);
    2941             : 
    2942             : /**
    2943             :  * cairo_scaled_font_get_scale_matrix:
    2944             :  * @scaled_font: a #cairo_scaled_font_t
    2945             :  * @scale_matrix: return value for the matrix
    2946             :  *
    2947             :  * Stores the scale matrix of @scaled_font into @matrix.
    2948             :  * The scale matrix is product of the font matrix and the ctm
    2949             :  * associated with the scaled font, and hence is the matrix mapping from
    2950             :  * font space to device space.
    2951             :  *
    2952             :  * Since: 1.8
    2953             :  **/
    2954             : void
    2955           0 : cairo_scaled_font_get_scale_matrix (cairo_scaled_font_t *scaled_font,
    2956             :                                     cairo_matrix_t      *scale_matrix)
    2957             : {
    2958           0 :     if (scaled_font->status) {
    2959           0 :         cairo_matrix_init_identity (scale_matrix);
    2960           0 :         return;
    2961             :     }
    2962             : 
    2963           0 :     *scale_matrix = scaled_font->scale;
    2964             : }
    2965             : 
    2966             : /**
    2967             :  * cairo_scaled_font_get_font_options:
    2968             :  * @scaled_font: a #cairo_scaled_font_t
    2969             :  * @options: return value for the font options
    2970             :  *
    2971             :  * Stores the font options with which @scaled_font was created into
    2972             :  * @options.
    2973             :  *
    2974             :  * Since: 1.2
    2975             :  **/
    2976             : void
    2977          71 : cairo_scaled_font_get_font_options (cairo_scaled_font_t         *scaled_font,
    2978             :                                     cairo_font_options_t        *options)
    2979             : {
    2980          71 :     if (cairo_font_options_status (options))
    2981           0 :         return;
    2982             : 
    2983          71 :     if (scaled_font->status) {
    2984           0 :         _cairo_font_options_init_default (options);
    2985           0 :         return;
    2986             :     }
    2987             : 
    2988          71 :     _cairo_font_options_init_copy (options, &scaled_font->options);
    2989             : }
    2990             : slim_hidden_def (cairo_scaled_font_get_font_options);

Generated by: LCOV version 1.13