LCOV - code coverage report
Current view: directory - gfx/cairo/cairo/src - cairo-scaled-font.c (source / functions) Found Hit Coverage
Test: app.info Lines: 1056 15 1.4 %
Date: 2012-06-02 Functions: 73 3 4.1 %

       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               0 : cairo_scaled_font_status (cairo_scaled_font_t *scaled_font)
     315                 : {
     316               0 :     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               0 : _cairo_scaled_font_map_lock (void)
     357                 : {
     358                 :     CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex);
     359                 : 
     360               0 :     if (cairo_scaled_font_map == NULL) {
     361               0 :         cairo_scaled_font_map = malloc (sizeof (cairo_scaled_font_map_t));
     362               0 :         if (unlikely (cairo_scaled_font_map == NULL))
     363               0 :             goto CLEANUP_MUTEX_LOCK;
     364                 : 
     365               0 :         cairo_scaled_font_map->mru_scaled_font = NULL;
     366               0 :         cairo_scaled_font_map->hash_table =
     367               0 :             _cairo_hash_table_create (_cairo_scaled_font_keys_equal);
     368                 : 
     369               0 :         if (unlikely (cairo_scaled_font_map->hash_table == NULL))
     370               0 :             goto CLEANUP_SCALED_FONT_MAP;
     371                 : 
     372               0 :         cairo_scaled_font_map->num_holdovers = 0;
     373                 :     }
     374                 : 
     375               0 :     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                 :     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               0 : _cairo_scaled_font_map_unlock (void)
     388                 : {
     389                 :    CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex);
     390               0 : }
     391                 : 
     392                 : void
     393               3 : _cairo_scaled_font_map_destroy (void)
     394                 : {
     395                 :     cairo_scaled_font_map_t *font_map;
     396                 :     cairo_scaled_font_t *scaled_font;
     397                 : 
     398                 :     CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex);
     399                 : 
     400               3 :     font_map = cairo_scaled_font_map;
     401               3 :     if (unlikely (font_map == NULL)) {
     402               3 :         goto CLEANUP_MUTEX_LOCK;
     403                 :     }
     404                 : 
     405               0 :     scaled_font = font_map->mru_scaled_font;
     406               0 :     if (scaled_font != NULL) {
     407                 :         CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex);
     408               0 :         cairo_scaled_font_destroy (scaled_font);
     409                 :         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                 :     CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex);
     438               3 : }
     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                 :     CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex);
     508                 :     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                 :     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                 :     CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex);
     538                 : 
     539                 :     CAIRO_MUTEX_UNLOCK (placeholder_scaled_font->mutex);
     540               0 :     cairo_scaled_font_destroy (placeholder_scaled_font);
     541                 : 
     542                 :     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                 :     CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex);
     553                 : 
     554                 :     /* wait on placeholder mutex until we are awaken */
     555                 :     CAIRO_MUTEX_LOCK (placeholder_scaled_font->mutex);
     556                 : 
     557                 :     /* ok, creation done.  just clean up and back out */
     558                 :     CAIRO_MUTEX_UNLOCK (placeholder_scaled_font->mutex);
     559               0 :     cairo_scaled_font_destroy (placeholder_scaled_font);
     560                 : 
     561                 :     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               0 : _hash_matrix_fnv (const cairo_matrix_t  *matrix,
     575                 :                   uint32_t               hval)
     576                 : {
     577               0 :     const uint8_t *buffer = (const uint8_t *) matrix;
     578               0 :     int len = sizeof (cairo_matrix_t);
     579                 :     do {
     580               0 :         hval *= FNV_32_PRIME;
     581               0 :         hval ^= *buffer++;
     582               0 :     } while (--len);
     583                 : 
     584               0 :     return hval;
     585                 : }
     586                 : 
     587                 : static uint32_t
     588               0 : _hash_mix_bits (uint32_t hash)
     589                 : {
     590               0 :     hash += hash << 12;
     591               0 :     hash ^= hash >> 7;
     592               0 :     hash += hash << 3;
     593               0 :     hash ^= hash >> 17;
     594               0 :     hash += hash << 5;
     595               0 :     return hash;
     596                 : }
     597                 : 
     598                 : static void
     599               0 : _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               0 :     uint32_t hash = FNV1_32_INIT;
     606                 : 
     607               0 :     scaled_font->status = CAIRO_STATUS_SUCCESS;
     608               0 :     scaled_font->placeholder = FALSE;
     609               0 :     scaled_font->font_face = font_face;
     610               0 :     scaled_font->font_matrix = *font_matrix;
     611               0 :     scaled_font->ctm = *ctm;
     612                 :     /* ignore translation values in the ctm */
     613               0 :     scaled_font->ctm.x0 = 0.;
     614               0 :     scaled_font->ctm.y0 = 0.;
     615               0 :     _cairo_font_options_init_copy (&scaled_font->options, options);
     616                 : 
     617                 :     /* We do a bytewise hash on the font matrices */
     618               0 :     hash = _hash_matrix_fnv (&scaled_font->font_matrix, hash);
     619               0 :     hash = _hash_matrix_fnv (&scaled_font->ctm, hash);
     620               0 :     hash = _hash_mix_bits (hash);
     621                 : 
     622               0 :     hash ^= (uintptr_t) scaled_font->font_face;
     623               0 :     hash ^= cairo_font_options_hash (&scaled_font->options);
     624                 : 
     625                 :     /* final mixing of bits */
     626               0 :     hash = _hash_mix_bits (hash);
     627                 : 
     628               0 :     assert (hash != ZOMBIE);
     629               0 :     scaled_font->hash_entry.hash = hash;
     630               0 : }
     631                 : 
     632                 : static cairo_bool_t
     633               0 : _cairo_scaled_font_keys_equal (const void *abstract_key_a,
     634                 :                                const void *abstract_key_b)
     635                 : {
     636               0 :     const cairo_scaled_font_t *key_a = abstract_key_a;
     637               0 :     const cairo_scaled_font_t *key_b = abstract_key_b;
     638                 : 
     639               0 :     if (key_a->hash_entry.hash != key_b->hash_entry.hash)
     640               0 :         return FALSE;
     641                 : 
     642               0 :     return key_a->font_face == key_b->font_face &&
     643               0 :             memcmp ((unsigned char *)(&key_a->font_matrix.xx),
     644               0 :                     (unsigned char *)(&key_b->font_matrix.xx),
     645               0 :                     sizeof(cairo_matrix_t)) == 0 &&
     646               0 :             memcmp ((unsigned char *)(&key_a->ctm.xx),
     647               0 :                     (unsigned char *)(&key_b->ctm.xx),
     648               0 :                     sizeof(cairo_matrix_t)) == 0 &&
     649               0 :             cairo_font_options_equal (&key_a->options, &key_b->options);
     650                 : }
     651                 : 
     652                 : static cairo_bool_t
     653               0 : _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               0 :     return scaled_font->original_font_face == font_face &&
     660               0 :             memcmp ((unsigned char *)(&scaled_font->font_matrix.xx),
     661               0 :                     (unsigned char *)(&font_matrix->xx),
     662               0 :                     sizeof(cairo_matrix_t)) == 0 &&
     663               0 :             memcmp ((unsigned char *)(&scaled_font->ctm.xx),
     664               0 :                     (unsigned char *)(&ctm->xx),
     665               0 :                     sizeof(cairo_matrix_t)) == 0 &&
     666               0 :             cairo_font_options_equal (&scaled_font->options, options);
     667                 : }
     668                 : 
     669                 : static cairo_bool_t
     670               0 : _cairo_scaled_glyphs_equal (const void *abstract_a, const void *abstract_b)
     671                 : {
     672               0 :     const cairo_scaled_glyph_t *a = abstract_a;
     673               0 :     const cairo_scaled_glyph_t *b = abstract_b;
     674                 : 
     675               0 :     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               0 : _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               0 :     status = cairo_font_options_status ((cairo_font_options_t *) options);
     693               0 :     if (unlikely (status))
     694               0 :         return status;
     695                 : 
     696               0 :     _cairo_scaled_font_init_key (scaled_font, font_face,
     697                 :                                  font_matrix, ctm, options);
     698                 : 
     699               0 :     cairo_matrix_multiply (&scaled_font->scale,
     700               0 :                            &scaled_font->font_matrix,
     701               0 :                            &scaled_font->ctm);
     702                 : 
     703               0 :     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               0 :     scaled_font->scale_inverse = scaled_font->scale;
     706               0 :     status = cairo_matrix_invert (&scaled_font->scale_inverse);
     707               0 :     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               0 :     scaled_font->glyphs = _cairo_hash_table_create (_cairo_scaled_glyphs_equal);
     727               0 :     if (unlikely (scaled_font->glyphs == NULL))
     728               0 :         return _cairo_error (CAIRO_STATUS_NO_MEMORY);
     729                 : 
     730               0 :     cairo_list_init (&scaled_font->glyph_pages);
     731               0 :     scaled_font->cache_frozen = FALSE;
     732               0 :     scaled_font->global_cache_frozen = FALSE;
     733                 : 
     734               0 :     scaled_font->holdover = FALSE;
     735               0 :     scaled_font->finished = FALSE;
     736                 : 
     737               0 :     CAIRO_REFERENCE_COUNT_INIT (&scaled_font->ref_count, 1);
     738                 : 
     739               0 :     _cairo_user_data_array_init (&scaled_font->user_data);
     740                 : 
     741               0 :     cairo_font_face_reference (font_face);
     742               0 :     scaled_font->original_font_face = NULL;
     743                 : 
     744               0 :     CAIRO_MUTEX_INIT (scaled_font->mutex);
     745                 : 
     746               0 :     scaled_font->surface_backend = NULL;
     747               0 :     scaled_font->surface_private = NULL;
     748                 : 
     749               0 :     scaled_font->backend = backend;
     750               0 :     cairo_list_init (&scaled_font->link);
     751                 : 
     752               0 :     return CAIRO_STATUS_SUCCESS;
     753                 : }
     754                 : 
     755                 : void
     756               0 : _cairo_scaled_font_freeze_cache (cairo_scaled_font_t *scaled_font)
     757                 : {
     758                 :     /* ensure we do not modify an error object */
     759               0 :     assert (scaled_font->status == CAIRO_STATUS_SUCCESS);
     760                 : 
     761                 :     CAIRO_MUTEX_LOCK (scaled_font->mutex);
     762               0 :     scaled_font->cache_frozen = TRUE;
     763               0 : }
     764                 : 
     765                 : void
     766               0 : _cairo_scaled_font_thaw_cache (cairo_scaled_font_t *scaled_font)
     767                 : {
     768               0 :     scaled_font->cache_frozen = FALSE;
     769                 : 
     770               0 :     if (scaled_font->global_cache_frozen) {
     771                 :         CAIRO_MUTEX_LOCK (_cairo_scaled_glyph_page_cache_mutex);
     772               0 :         _cairo_cache_thaw (&cairo_scaled_glyph_page_cache);
     773                 :         CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex);
     774                 : 
     775               0 :         scaled_font->global_cache_frozen = FALSE;
     776                 :     }
     777                 : 
     778                 :     CAIRO_MUTEX_UNLOCK (scaled_font->mutex);
     779               0 : }
     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                 :     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                 :     CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex);
     794               0 : }
     795                 : 
     796                 : cairo_status_t
     797               0 : _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               0 :     scaled_font->fs_extents = *fs_metrics;
     804                 : 
     805               0 :     status = _cairo_matrix_compute_basis_scale_factors (&scaled_font->font_matrix,
     806                 :                                                   &font_scale_x, &font_scale_y,
     807                 :                                                   1);
     808               0 :     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               0 :     scaled_font->extents.ascent = fs_metrics->ascent * font_scale_y;
     817               0 :     scaled_font->extents.descent = fs_metrics->descent * font_scale_y;
     818               0 :     scaled_font->extents.height = fs_metrics->height * font_scale_y;
     819               0 :     scaled_font->extents.max_x_advance = fs_metrics->max_x_advance * font_scale_x;
     820               0 :     scaled_font->extents.max_y_advance = fs_metrics->max_y_advance * font_scale_y;
     821                 : 
     822               0 :     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                 :     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                 :     CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex);
     870               0 :     _cairo_scaled_font_fini_internal (scaled_font);
     871                 :     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               0 : 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               0 :     cairo_font_face_t *original_font_face = font_face;
     903               0 :     cairo_scaled_font_t key, *old = NULL, *scaled_font = NULL, *dead = NULL;
     904                 :     double det;
     905                 : 
     906               0 :     status = font_face->status;
     907               0 :     if (unlikely (status))
     908               0 :         return _cairo_scaled_font_create_in_error (status);
     909                 : 
     910               0 :     det = _cairo_matrix_compute_determinant (font_matrix);
     911               0 :     if (! ISFINITE (det))
     912               0 :         return _cairo_scaled_font_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_MATRIX));
     913                 : 
     914               0 :     det = _cairo_matrix_compute_determinant (ctm);
     915               0 :     if (! ISFINITE (det))
     916               0 :         return _cairo_scaled_font_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_MATRIX));
     917                 : 
     918               0 :     status = cairo_font_options_status ((cairo_font_options_t *) options);
     919               0 :     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               0 :     font_map = _cairo_scaled_font_map_lock ();
     926               0 :     if (unlikely (font_map == NULL))
     927               0 :         return _cairo_scaled_font_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
     928                 : 
     929               0 :     scaled_font = font_map->mru_scaled_font;
     930               0 :     if (scaled_font != NULL &&
     931               0 :         _cairo_scaled_font_matches (scaled_font,
     932                 :                                     font_face, font_matrix, ctm, options))
     933                 :     {
     934               0 :         assert (scaled_font->hash_entry.hash != ZOMBIE);
     935               0 :         assert (! scaled_font->placeholder);
     936                 : 
     937               0 :         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               0 :             _cairo_reference_count_inc (&scaled_font->ref_count);
     943               0 :             _cairo_scaled_font_map_unlock ();
     944               0 :             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               0 :         if (font_face->backend->get_implementation != NULL) {
     972               0 :             font_face = font_face->backend->get_implementation (font_face,
     973                 :                                                                 font_matrix,
     974                 :                                                                 ctm,
     975                 :                                                                 options);
     976               0 :             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               0 :         _cairo_scaled_font_init_key (&key, font_face,
     983                 :                                      font_matrix, ctm, options);
     984                 : 
     985               0 :         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               0 :         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               0 :     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               0 :     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               0 :     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               0 :     assert (scaled_font->font_face == font_face);
    1084                 : 
    1085               0 :     scaled_font->original_font_face =
    1086               0 :         cairo_font_face_reference (original_font_face);
    1087                 : 
    1088               0 :     status = _cairo_hash_table_insert (font_map->hash_table,
    1089               0 :                                        &scaled_font->hash_entry);
    1090               0 :     if (likely (status == CAIRO_STATUS_SUCCESS)) {
    1091               0 :         old = font_map->mru_scaled_font;
    1092               0 :         font_map->mru_scaled_font = scaled_font;
    1093               0 :         _cairo_reference_count_inc (&scaled_font->ref_count);
    1094                 :     }
    1095                 : 
    1096               0 :     _cairo_scaled_font_map_unlock ();
    1097                 : 
    1098               0 :     cairo_scaled_font_destroy (old);
    1099               0 :     if (font_face != original_font_face)
    1100               0 :         cairo_font_face_destroy (font_face);
    1101                 : 
    1102               0 :     if (dead != NULL)
    1103               0 :         cairo_scaled_font_destroy (dead);
    1104                 : 
    1105               0 :     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               0 :     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                 :     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                 :             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                 :     CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_error_mutex);
    1146                 : 
    1147               0 :     return scaled_font;
    1148                 : }
    1149                 : 
    1150                 : void
    1151               3 : _cairo_scaled_font_reset_static_data (void)
    1152                 : {
    1153                 :     int status;
    1154                 : 
    1155                 :     CAIRO_MUTEX_LOCK (_cairo_scaled_font_error_mutex);
    1156             120 :     for (status = CAIRO_STATUS_SUCCESS;
    1157                 :          status <= CAIRO_STATUS_LAST_STATUS;
    1158             114 :          status++)
    1159                 :     {
    1160             114 :         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                 :     CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_error_mutex);
    1166                 : 
    1167                 :     CAIRO_MUTEX_LOCK (_cairo_scaled_glyph_page_cache_mutex);
    1168               3 :     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                 :     CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex);
    1173               3 : }
    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               0 : cairo_scaled_font_reference (cairo_scaled_font_t *scaled_font)
    1191                 : {
    1192               0 :     if (scaled_font == NULL ||
    1193               0 :             CAIRO_REFERENCE_COUNT_IS_INVALID (&scaled_font->ref_count))
    1194               0 :         return scaled_font;
    1195                 : 
    1196               0 :     assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&scaled_font->ref_count));
    1197                 : 
    1198               0 :     _cairo_reference_count_inc (&scaled_font->ref_count);
    1199                 : 
    1200               0 :     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             128 : cairo_scaled_font_destroy (cairo_scaled_font_t *scaled_font)
    1214                 : {
    1215             128 :     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             128 :     if (scaled_font == NULL ||
    1221               0 :             CAIRO_REFERENCE_COUNT_IS_INVALID (&scaled_font->ref_count))
    1222             128 :         return;
    1223                 : 
    1224               0 :     assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&scaled_font->ref_count));
    1225                 : 
    1226               0 :     if (! _cairo_reference_count_dec_and_test (&scaled_font->ref_count))
    1227               0 :         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               0 : 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               0 :     double min_x = 0.0, min_y = 0.0, max_x = 0.0, max_y = 0.0;
    1474               0 :     cairo_bool_t visible = FALSE;
    1475               0 :     cairo_scaled_glyph_t *scaled_glyph = NULL;
    1476                 : 
    1477               0 :     extents->x_bearing = 0.0;
    1478               0 :     extents->y_bearing = 0.0;
    1479               0 :     extents->width  = 0.0;
    1480               0 :     extents->height = 0.0;
    1481               0 :     extents->x_advance = 0.0;
    1482               0 :     extents->y_advance = 0.0;
    1483                 : 
    1484               0 :     if (unlikely (scaled_font->status))
    1485               0 :         goto ZERO_EXTENTS;
    1486                 : 
    1487               0 :     if (num_glyphs == 0)
    1488               0 :         goto ZERO_EXTENTS;
    1489                 : 
    1490               0 :     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               0 :     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               0 :     _cairo_scaled_font_freeze_cache (scaled_font);
    1503                 : 
    1504               0 :     for (i = 0; i < num_glyphs; i++) {
    1505                 :         double                  left, top, right, bottom;
    1506                 : 
    1507               0 :         status = _cairo_scaled_glyph_lookup (scaled_font,
    1508               0 :                                              glyphs[i].index,
    1509                 :                                              CAIRO_SCALED_GLYPH_INFO_METRICS,
    1510                 :                                              &scaled_glyph);
    1511               0 :         if (unlikely (status)) {
    1512               0 :             status = _cairo_scaled_font_set_error (scaled_font, status);
    1513               0 :             goto UNLOCK;
    1514                 :         }
    1515                 : 
    1516                 :         /* "Ink" extents should skip "invisible" glyphs */
    1517               0 :         if (scaled_glyph->metrics.width == 0 || scaled_glyph->metrics.height == 0)
    1518               0 :             continue;
    1519                 : 
    1520               0 :         left = scaled_glyph->metrics.x_bearing + glyphs[i].x;
    1521               0 :         right = left + scaled_glyph->metrics.width;
    1522               0 :         top = scaled_glyph->metrics.y_bearing + glyphs[i].y;
    1523               0 :         bottom = top + scaled_glyph->metrics.height;
    1524                 : 
    1525               0 :         if (!visible) {
    1526               0 :             visible = TRUE;
    1527               0 :             min_x = left;
    1528               0 :             max_x = right;
    1529               0 :             min_y = top;
    1530               0 :             max_y = bottom;
    1531                 :         } else {
    1532               0 :             if (left < min_x) min_x = left;
    1533               0 :             if (right > max_x) max_x = right;
    1534               0 :             if (top < min_y) min_y = top;
    1535               0 :             if (bottom > max_y) max_y = bottom;
    1536                 :         }
    1537                 :     }
    1538                 : 
    1539               0 :     if (visible) {
    1540               0 :         extents->x_bearing = min_x - glyphs[0].x;
    1541               0 :         extents->y_bearing = min_y - glyphs[0].y;
    1542               0 :         extents->width = max_x - min_x;
    1543               0 :         extents->height = max_y - min_y;
    1544                 :     } else {
    1545               0 :         extents->x_bearing = 0.0;
    1546               0 :         extents->y_bearing = 0.0;
    1547               0 :         extents->width = 0.0;
    1548               0 :         extents->height = 0.0;
    1549                 :     }
    1550                 : 
    1551               0 :     if (num_glyphs) {
    1552                 :         double x0, y0, x1, y1;
    1553                 : 
    1554               0 :         x0 = glyphs[0].x;
    1555               0 :         y0 = glyphs[0].y;
    1556                 : 
    1557                 :         /* scaled_glyph contains the glyph for num_glyphs - 1 already. */
    1558               0 :         x1 = glyphs[num_glyphs - 1].x + scaled_glyph->metrics.x_advance;
    1559               0 :         y1 = glyphs[num_glyphs - 1].y + scaled_glyph->metrics.y_advance;
    1560                 : 
    1561               0 :         extents->x_advance = x1 - x0;
    1562               0 :         extents->y_advance = y1 - y0;
    1563                 :     } else {
    1564               0 :         extents->x_advance = 0.0;
    1565               0 :         extents->y_advance = 0.0;
    1566                 :     }
    1567                 : 
    1568                 :  UNLOCK:
    1569               0 :     _cairo_scaled_font_thaw_cache (scaled_font);
    1570               0 :     return;
    1571                 : 
    1572                 : ZERO_EXTENTS:
    1573               0 :     extents->x_bearing = 0.0;
    1574               0 :     extents->y_bearing = 0.0;
    1575               0 :     extents->width  = 0.0;
    1576               0 :     extents->height = 0.0;
    1577               0 :     extents->x_advance = 0.0;
    1578               0 :     extents->y_advance = 0.0;
    1579                 : }
    1580                 : slim_hidden_def (cairo_scaled_font_glyph_extents);
    1581                 : 
    1582                 : #define GLYPH_LUT_SIZE 64
    1583                 : static cairo_status_t
    1584               0 : cairo_scaled_font_text_to_glyphs_internal_cached (cairo_scaled_font_t            *scaled_font,
    1585                 :                                                     double                        x,
    1586                 :                                                     double                        y,
    1587                 :                                                     const char                   *utf8,
    1588                 :                                                     cairo_glyph_t                *glyphs,
    1589                 :                                                     cairo_text_cluster_t        **clusters,
    1590                 :                                                     int                           num_chars)
    1591                 : {
    1592                 :     struct glyph_lut_elt {
    1593                 :         unsigned long index;
    1594                 :         double x_advance;
    1595                 :         double y_advance;
    1596                 :     } glyph_lut[GLYPH_LUT_SIZE];
    1597                 :     uint32_t glyph_lut_unicode[GLYPH_LUT_SIZE];
    1598                 :     cairo_status_t status;
    1599                 :     const char *p;
    1600                 :     int i;
    1601                 : 
    1602               0 :     for (i = 0; i < GLYPH_LUT_SIZE; i++)
    1603               0 :         glyph_lut_unicode[i] = ~0U;
    1604                 : 
    1605               0 :     p = utf8;
    1606               0 :     for (i = 0; i < num_chars; i++) {
    1607                 :         int idx, num_bytes;
    1608                 :         uint32_t unicode;
    1609                 :         cairo_scaled_glyph_t *scaled_glyph;
    1610                 :         struct glyph_lut_elt *glyph_slot;
    1611                 : 
    1612               0 :         num_bytes = _cairo_utf8_get_char_validated (p, &unicode);
    1613               0 :         p += num_bytes;
    1614                 : 
    1615               0 :         glyphs[i].x = x;
    1616               0 :         glyphs[i].y = y;
    1617                 : 
    1618               0 :         idx = unicode % ARRAY_LENGTH (glyph_lut);
    1619               0 :         glyph_slot = &glyph_lut[idx];
    1620               0 :         if (glyph_lut_unicode[idx] == unicode) {
    1621               0 :             glyphs[i].index = glyph_slot->index;
    1622               0 :             x += glyph_slot->x_advance;
    1623               0 :             y += glyph_slot->y_advance;
    1624                 :         } else {
    1625                 :             unsigned long g;
    1626                 : 
    1627               0 :             g = scaled_font->backend->ucs4_to_index (scaled_font, unicode);
    1628               0 :             status = _cairo_scaled_glyph_lookup (scaled_font,
    1629                 :                                                  g,
    1630                 :                                                  CAIRO_SCALED_GLYPH_INFO_METRICS,
    1631                 :                                                  &scaled_glyph);
    1632               0 :             if (unlikely (status))
    1633               0 :                 return status;
    1634                 : 
    1635               0 :             x += scaled_glyph->metrics.x_advance;
    1636               0 :             y += scaled_glyph->metrics.y_advance;
    1637                 : 
    1638               0 :             glyph_lut_unicode[idx] = unicode;
    1639               0 :             glyph_slot->index = g;
    1640               0 :             glyph_slot->x_advance = scaled_glyph->metrics.x_advance;
    1641               0 :             glyph_slot->y_advance = scaled_glyph->metrics.y_advance;
    1642                 : 
    1643               0 :             glyphs[i].index = g;
    1644                 :         }
    1645                 : 
    1646               0 :         if (clusters) {
    1647               0 :             (*clusters)[i].num_bytes  = num_bytes;
    1648               0 :             (*clusters)[i].num_glyphs = 1;
    1649                 :         }
    1650                 :     }
    1651                 : 
    1652               0 :     return CAIRO_STATUS_SUCCESS;
    1653                 : }
    1654                 : 
    1655                 : static cairo_status_t
    1656               0 : cairo_scaled_font_text_to_glyphs_internal_uncached (cairo_scaled_font_t  *scaled_font,
    1657                 :                                                   double                  x,
    1658                 :                                                   double                  y,
    1659                 :                                                   const char             *utf8,
    1660                 :                                                   cairo_glyph_t          *glyphs,
    1661                 :                                                   cairo_text_cluster_t  **clusters,
    1662                 :                                                   int                     num_chars)
    1663                 : {
    1664                 :     const char *p;
    1665                 :     int i;
    1666                 : 
    1667               0 :     p = utf8;
    1668               0 :     for (i = 0; i < num_chars; i++) {
    1669                 :         unsigned long g;
    1670                 :         int num_bytes;
    1671                 :         uint32_t unicode;
    1672                 :         cairo_scaled_glyph_t *scaled_glyph;
    1673                 :         cairo_status_t status;
    1674                 : 
    1675               0 :         num_bytes = _cairo_utf8_get_char_validated (p, &unicode);
    1676               0 :         p += num_bytes;
    1677                 : 
    1678               0 :         glyphs[i].x = x;
    1679               0 :         glyphs[i].y = y;
    1680                 : 
    1681               0 :         g = scaled_font->backend->ucs4_to_index (scaled_font, unicode);
    1682                 : 
    1683                 :         /*
    1684                 :          * No advance needed for a single character string. So, let's speed up
    1685                 :          * one-character strings by skipping glyph lookup.
    1686                 :          */
    1687               0 :         if (num_chars > 1) {
    1688               0 :             status = _cairo_scaled_glyph_lookup (scaled_font,
    1689                 :                                              g,
    1690                 :                                              CAIRO_SCALED_GLYPH_INFO_METRICS,
    1691                 :                                              &scaled_glyph);
    1692               0 :             if (unlikely (status))
    1693               0 :                 return status;
    1694                 : 
    1695               0 :             x += scaled_glyph->metrics.x_advance;
    1696               0 :             y += scaled_glyph->metrics.y_advance;
    1697                 :         }
    1698                 : 
    1699               0 :         glyphs[i].index = g;
    1700                 : 
    1701               0 :         if (clusters) {
    1702               0 :             (*clusters)[i].num_bytes  = num_bytes;
    1703               0 :             (*clusters)[i].num_glyphs = 1;
    1704                 :         }
    1705                 :     }
    1706                 : 
    1707               0 :     return CAIRO_STATUS_SUCCESS;
    1708                 : }
    1709                 : 
    1710                 : /**
    1711                 :  * cairo_scaled_font_text_to_glyphs:
    1712                 :  * @x: X position to place first glyph
    1713                 :  * @y: Y position to place first glyph
    1714                 :  * @scaled_font: a #cairo_scaled_font_t
    1715                 :  * @utf8: a string of text encoded in UTF-8
    1716                 :  * @utf8_len: length of @utf8 in bytes, or -1 if it is NUL-terminated
    1717                 :  * @glyphs: pointer to array of glyphs to fill
    1718                 :  * @num_glyphs: pointer to number of glyphs
    1719                 :  * @clusters: pointer to array of cluster mapping information to fill, or %NULL
    1720                 :  * @num_clusters: pointer to number of clusters, or %NULL
    1721                 :  * @cluster_flags: pointer to location to store cluster flags corresponding to the
    1722                 :  *                 output @clusters, or %NULL
    1723                 :  *
    1724                 :  * Converts UTF-8 text to an array of glyphs, optionally with cluster
    1725                 :  * mapping, that can be used to render later using @scaled_font.
    1726                 :  *
    1727                 :  * If @glyphs initially points to a non-%NULL value, that array is used
    1728                 :  * as a glyph buffer, and @num_glyphs should point to the number of glyph
    1729                 :  * entries available there.  If the provided glyph array is too short for
    1730                 :  * the conversion, a new glyph array is allocated using cairo_glyph_allocate()
    1731                 :  * and placed in @glyphs.  Upon return, @num_glyphs always contains the
    1732                 :  * number of generated glyphs.  If the value @glyphs points to has changed
    1733                 :  * after the call, the user is responsible for freeing the allocated glyph
    1734                 :  * array using cairo_glyph_free().  This may happen even if the provided
    1735                 :  * array was large enough.
    1736                 :  *
    1737                 :  * If @clusters is not %NULL, @num_clusters and @cluster_flags should not be %NULL,
    1738                 :  * and cluster mapping will be computed.
    1739                 :  * The semantics of how cluster array allocation works is similar to the glyph
    1740                 :  * array.  That is,
    1741                 :  * if @clusters initially points to a non-%NULL value, that array is used
    1742                 :  * as a cluster buffer, and @num_clusters should point to the number of cluster
    1743                 :  * entries available there.  If the provided cluster array is too short for
    1744                 :  * the conversion, a new cluster array is allocated using cairo_text_cluster_allocate()
    1745                 :  * and placed in @clusters.  Upon return, @num_clusters always contains the
    1746                 :  * number of generated clusters.  If the value @clusters points at has changed
    1747                 :  * after the call, the user is responsible for freeing the allocated cluster
    1748                 :  * array using cairo_text_cluster_free().  This may happen even if the provided
    1749                 :  * array was large enough.
    1750                 :  *
    1751                 :  * In the simplest case, @glyphs and @clusters can point to %NULL initially
    1752                 :  * and a suitable array will be allocated.  In code:
    1753                 :  * <informalexample><programlisting>
    1754                 :  * cairo_status_t status;
    1755                 :  *
    1756                 :  * cairo_glyph_t *glyphs = NULL;
    1757                 :  * int num_glyphs;
    1758                 :  * cairo_text_cluster_t *clusters = NULL;
    1759                 :  * int num_clusters;
    1760                 :  * cairo_text_cluster_flags_t cluster_flags;
    1761                 :  *
    1762                 :  * status = cairo_scaled_font_text_to_glyphs (scaled_font,
    1763                 :  *                                            x, y,
    1764                 :  *                                            utf8, utf8_len,
    1765                 :  *                                            &amp;glyphs, &amp;num_glyphs,
    1766                 :  *                                            &amp;clusters, &amp;num_clusters, &amp;cluster_flags);
    1767                 :  *
    1768                 :  * if (status == CAIRO_STATUS_SUCCESS) {
    1769                 :  *     cairo_show_text_glyphs (cr,
    1770                 :  *                             utf8, utf8_len,
    1771                 :  *                             glyphs, num_glyphs,
    1772                 :  *                             clusters, num_clusters, cluster_flags);
    1773                 :  *
    1774                 :  *     cairo_glyph_free (glyphs);
    1775                 :  *     cairo_text_cluster_free (clusters);
    1776                 :  * }
    1777                 :  * </programlisting></informalexample>
    1778                 :  *
    1779                 :  * If no cluster mapping is needed:
    1780                 :  * <informalexample><programlisting>
    1781                 :  * cairo_status_t status;
    1782                 :  *
    1783                 :  * cairo_glyph_t *glyphs = NULL;
    1784                 :  * int num_glyphs;
    1785                 :  *
    1786                 :  * status = cairo_scaled_font_text_to_glyphs (scaled_font,
    1787                 :  *                                            x, y,
    1788                 :  *                                            utf8, utf8_len,
    1789                 :  *                                            &amp;glyphs, &amp;num_glyphs,
    1790                 :  *                                            NULL, NULL,
    1791                 :  *                                            NULL);
    1792                 :  *
    1793                 :  * if (status == CAIRO_STATUS_SUCCESS) {
    1794                 :  *     cairo_show_glyphs (cr, glyphs, num_glyphs);
    1795                 :  *     cairo_glyph_free (glyphs);
    1796                 :  * }
    1797                 :  * </programlisting></informalexample>
    1798                 :  *
    1799                 :  * If stack-based glyph and cluster arrays are to be used for small
    1800                 :  * arrays:
    1801                 :  * <informalexample><programlisting>
    1802                 :  * cairo_status_t status;
    1803                 :  *
    1804                 :  * cairo_glyph_t stack_glyphs[40];
    1805                 :  * cairo_glyph_t *glyphs = stack_glyphs;
    1806                 :  * int num_glyphs = sizeof (stack_glyphs) / sizeof (stack_glyphs[0]);
    1807                 :  * cairo_text_cluster_t stack_clusters[40];
    1808                 :  * cairo_text_cluster_t *clusters = stack_clusters;
    1809                 :  * int num_clusters = sizeof (stack_clusters) / sizeof (stack_clusters[0]);
    1810                 :  * cairo_text_cluster_flags_t cluster_flags;
    1811                 :  *
    1812                 :  * status = cairo_scaled_font_text_to_glyphs (scaled_font,
    1813                 :  *                                            x, y,
    1814                 :  *                                            utf8, utf8_len,
    1815                 :  *                                            &amp;glyphs, &amp;num_glyphs,
    1816                 :  *                                            &amp;clusters, &amp;num_clusters, &amp;cluster_flags);
    1817                 :  *
    1818                 :  * if (status == CAIRO_STATUS_SUCCESS) {
    1819                 :  *     cairo_show_text_glyphs (cr,
    1820                 :  *                             utf8, utf8_len,
    1821                 :  *                             glyphs, num_glyphs,
    1822                 :  *                             clusters, num_clusters, cluster_flags);
    1823                 :  *
    1824                 :  *     if (glyphs != stack_glyphs)
    1825                 :  *         cairo_glyph_free (glyphs);
    1826                 :  *     if (clusters != stack_clusters)
    1827                 :  *         cairo_text_cluster_free (clusters);
    1828                 :  * }
    1829                 :  * </programlisting></informalexample>
    1830                 :  *
    1831                 :  * For details of how @clusters, @num_clusters, and @cluster_flags map input
    1832                 :  * UTF-8 text to the output glyphs see cairo_show_text_glyphs().
    1833                 :  *
    1834                 :  * The output values can be readily passed to cairo_show_text_glyphs()
    1835                 :  * cairo_show_glyphs(), or related functions, assuming that the exact
    1836                 :  * same @scaled_font is used for the operation.
    1837                 :  *
    1838                 :  * Return value: %CAIRO_STATUS_SUCCESS upon success, or an error status
    1839                 :  * if the input values are wrong or if conversion failed.  If the input
    1840                 :  * values are correct but the conversion failed, the error status is also
    1841                 :  * set on @scaled_font.
    1842                 :  *
    1843                 :  * Since: 1.8
    1844                 :  **/
    1845                 : #define CACHING_THRESHOLD 16
    1846                 : cairo_status_t
    1847               0 : cairo_scaled_font_text_to_glyphs (cairo_scaled_font_t   *scaled_font,
    1848                 :                                   double                 x,
    1849                 :                                   double                 y,
    1850                 :                                   const char            *utf8,
    1851                 :                                   int                    utf8_len,
    1852                 :                                   cairo_glyph_t        **glyphs,
    1853                 :                                   int                   *num_glyphs,
    1854                 :                                   cairo_text_cluster_t **clusters,
    1855                 :                                   int                   *num_clusters,
    1856                 :                                   cairo_text_cluster_flags_t *cluster_flags)
    1857                 : {
    1858               0 :     int num_chars = 0;
    1859                 :     cairo_status_t status;
    1860                 :     cairo_glyph_t *orig_glyphs;
    1861                 :     cairo_text_cluster_t *orig_clusters;
    1862                 : 
    1863               0 :     status = scaled_font->status;
    1864               0 :     if (unlikely (status))
    1865               0 :         return status;
    1866                 : 
    1867                 :     /* A slew of sanity checks */
    1868                 : 
    1869                 :     /* glyphs and num_glyphs can't be NULL */
    1870               0 :     if (glyphs     == NULL ||
    1871                 :         num_glyphs == NULL) {
    1872               0 :         status = _cairo_error (CAIRO_STATUS_NULL_POINTER);
    1873               0 :         goto BAIL;
    1874                 :     }
    1875                 : 
    1876                 :     /* Special case for NULL and -1 */
    1877               0 :     if (utf8 == NULL && utf8_len == -1)
    1878               0 :         utf8_len = 0;
    1879                 : 
    1880                 :     /* No NULLs for non-NULLs! */
    1881               0 :     if ((utf8_len && utf8          == NULL) ||
    1882               0 :         (clusters && num_clusters  == NULL) ||
    1883               0 :         (clusters && cluster_flags == NULL)) {
    1884               0 :         status = _cairo_error (CAIRO_STATUS_NULL_POINTER);
    1885               0 :         goto BAIL;
    1886                 :     }
    1887                 : 
    1888                 :     /* A -1 for utf8_len means NUL-terminated */
    1889               0 :     if (utf8_len == -1)
    1890               0 :         utf8_len = strlen (utf8);
    1891                 : 
    1892                 :     /* A NULL *glyphs means no prealloced glyphs array */
    1893               0 :     if (glyphs && *glyphs == NULL)
    1894               0 :         *num_glyphs = 0;
    1895                 : 
    1896                 :     /* A NULL *clusters means no prealloced clusters array */
    1897               0 :     if (clusters && *clusters == NULL)
    1898               0 :         *num_clusters = 0;
    1899                 : 
    1900               0 :     if (!clusters && num_clusters) {
    1901               0 :         num_clusters = NULL;
    1902                 :     }
    1903                 : 
    1904               0 :     if (cluster_flags) {
    1905               0 :         *cluster_flags = FALSE;
    1906                 :     }
    1907                 : 
    1908               0 :     if (!clusters && cluster_flags) {
    1909               0 :         cluster_flags = NULL;
    1910                 :     }
    1911                 : 
    1912                 :     /* Apart from that, no negatives */
    1913               0 :     if (utf8_len < 0 ||
    1914               0 :         *num_glyphs < 0 ||
    1915               0 :         (num_clusters && *num_clusters < 0)) {
    1916               0 :         status = _cairo_error (CAIRO_STATUS_NEGATIVE_COUNT);
    1917               0 :         goto BAIL;
    1918                 :     }
    1919                 : 
    1920               0 :     if (utf8_len == 0) {
    1921               0 :         status = CAIRO_STATUS_SUCCESS;
    1922               0 :         goto BAIL;
    1923                 :     }
    1924                 : 
    1925                 :     /* validate input so backend does not have to */
    1926               0 :     status = _cairo_utf8_to_ucs4 (utf8, utf8_len, NULL, &num_chars);
    1927               0 :     if (unlikely (status))
    1928               0 :         goto BAIL;
    1929                 : 
    1930               0 :     _cairo_scaled_font_freeze_cache (scaled_font);
    1931                 : 
    1932               0 :     orig_glyphs = *glyphs;
    1933               0 :     orig_clusters = clusters ? *clusters : NULL;
    1934                 : 
    1935               0 :     if (scaled_font->backend->text_to_glyphs) {
    1936               0 :         status = scaled_font->backend->text_to_glyphs (scaled_font, x, y,
    1937                 :                                                        utf8, utf8_len,
    1938                 :                                                        glyphs, num_glyphs,
    1939                 :                                                        clusters, num_clusters,
    1940                 :                                                        cluster_flags);
    1941               0 :         if (status != CAIRO_INT_STATUS_UNSUPPORTED) {
    1942               0 :             if (status == CAIRO_STATUS_SUCCESS) {
    1943                 :                 /* The checks here are crude; we only should do them in
    1944                 :                  * user-font backend, but they don't hurt here.  This stuff
    1945                 :                  * can be hard to get right. */
    1946                 : 
    1947               0 :                 if (*num_glyphs < 0) {
    1948               0 :                     status = _cairo_error (CAIRO_STATUS_NEGATIVE_COUNT);
    1949               0 :                     goto DONE;
    1950                 :                 }
    1951               0 :                 if (num_glyphs && *glyphs == NULL) {
    1952               0 :                     status = _cairo_error (CAIRO_STATUS_NULL_POINTER);
    1953               0 :                     goto DONE;
    1954                 :                 }
    1955                 : 
    1956               0 :                 if (clusters) {
    1957               0 :                     if (*num_clusters < 0) {
    1958               0 :                         status = _cairo_error (CAIRO_STATUS_NEGATIVE_COUNT);
    1959               0 :                         goto DONE;
    1960                 :                     }
    1961               0 :                     if (num_clusters && *clusters == NULL) {
    1962               0 :                         status = _cairo_error (CAIRO_STATUS_NULL_POINTER);
    1963               0 :                         goto DONE;
    1964                 :                     }
    1965                 : 
    1966                 :                     /* Don't trust the backend, validate clusters! */
    1967               0 :                     status =
    1968               0 :                         _cairo_validate_text_clusters (utf8, utf8_len,
    1969               0 :                                                        *glyphs, *num_glyphs,
    1970               0 :                                                        *clusters, *num_clusters,
    1971                 :                                                        *cluster_flags);
    1972                 :                 }
    1973                 :             }
    1974                 : 
    1975               0 :             goto DONE;
    1976                 :         }
    1977                 :     }
    1978                 : 
    1979               0 :     if (*num_glyphs < num_chars) {
    1980               0 :         *glyphs = cairo_glyph_allocate (num_chars);
    1981               0 :         if (unlikely (*glyphs == NULL)) {
    1982               0 :             status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
    1983               0 :             goto DONE;
    1984                 :         }
    1985                 :     }
    1986               0 :     *num_glyphs = num_chars;
    1987                 : 
    1988               0 :     if (clusters) {
    1989               0 :         if (*num_clusters < num_chars) {
    1990               0 :             *clusters = cairo_text_cluster_allocate (num_chars);
    1991               0 :             if (unlikely (*clusters == NULL)) {
    1992               0 :                 status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
    1993               0 :                 goto DONE;
    1994                 :             }
    1995                 :         }
    1996               0 :         *num_clusters = num_chars;
    1997                 :     }
    1998                 : 
    1999               0 :     if (num_chars > CACHING_THRESHOLD)
    2000               0 :         status = cairo_scaled_font_text_to_glyphs_internal_cached (scaled_font,
    2001                 :                                                                      x, y,
    2002                 :                                                                      utf8,
    2003                 :                                                                      *glyphs,
    2004                 :                                                                      clusters,
    2005                 :                                                                      num_chars);
    2006                 :     else
    2007               0 :         status = cairo_scaled_font_text_to_glyphs_internal_uncached (scaled_font,
    2008                 :                                                                    x, y,
    2009                 :                                                                    utf8,
    2010                 :                                                                    *glyphs,
    2011                 :                                                                    clusters,
    2012                 :                                                                    num_chars);
    2013                 : 
    2014                 :  DONE: /* error that should be logged on scaled_font happened */
    2015               0 :     _cairo_scaled_font_thaw_cache (scaled_font);
    2016                 : 
    2017               0 :     if (unlikely (status)) {
    2018               0 :         *num_glyphs = 0;
    2019               0 :         if (*glyphs != orig_glyphs) {
    2020               0 :             cairo_glyph_free (*glyphs);
    2021               0 :             *glyphs = orig_glyphs;
    2022                 :         }
    2023                 : 
    2024               0 :         if (clusters) {
    2025               0 :             *num_clusters = 0;
    2026               0 :             if (*clusters != orig_clusters) {
    2027               0 :                 cairo_text_cluster_free (*clusters);
    2028               0 :                 *clusters = orig_clusters;
    2029                 :             }
    2030                 :         }
    2031                 :     }
    2032                 : 
    2033               0 :     return _cairo_scaled_font_set_error (scaled_font, status);
    2034                 : 
    2035                 :  BAIL: /* error with input arguments */
    2036                 : 
    2037               0 :     if (num_glyphs)
    2038               0 :         *num_glyphs = 0;
    2039                 : 
    2040               0 :     if (num_clusters)
    2041               0 :         *num_clusters = 0;
    2042                 : 
    2043               0 :     return status;
    2044                 : }
    2045                 : slim_hidden_def (cairo_scaled_font_text_to_glyphs);
    2046                 : 
    2047                 : static inline cairo_bool_t
    2048               0 : _range_contains_glyph (const cairo_box_t *extents,
    2049                 :                        cairo_fixed_t left,
    2050                 :                        cairo_fixed_t top,
    2051                 :                        cairo_fixed_t right,
    2052                 :                        cairo_fixed_t bottom)
    2053                 : {
    2054               0 :     return right > extents->p1.x &&
    2055               0 :            left < extents->p2.x &&
    2056               0 :            bottom > extents->p1.y &&
    2057               0 :            top < extents->p2.y;
    2058                 : }
    2059                 : 
    2060                 : /*
    2061                 :  * Compute a device-space bounding box for the glyphs.
    2062                 :  */
    2063                 : cairo_status_t
    2064               0 : _cairo_scaled_font_glyph_device_extents (cairo_scaled_font_t     *scaled_font,
    2065                 :                                          const cairo_glyph_t     *glyphs,
    2066                 :                                          int                      num_glyphs,
    2067                 :                                          cairo_rectangle_int_t   *extents,
    2068                 :                                          cairo_bool_t *overlap_out)
    2069                 : {
    2070               0 :     cairo_status_t status = CAIRO_STATUS_SUCCESS;
    2071               0 :     cairo_box_t box = { { INT_MAX, INT_MAX }, { INT_MIN, INT_MIN }};
    2072                 :     cairo_scaled_glyph_t *glyph_cache[64];
    2073               0 :     cairo_bool_t overlap = overlap_out ? FALSE : TRUE;
    2074               0 :     cairo_round_glyph_positions_t round_glyph_positions = _cairo_font_options_get_round_glyph_positions (&scaled_font->options);
    2075                 :     int i;
    2076                 : 
    2077               0 :     if (unlikely (scaled_font->status))
    2078               0 :         return scaled_font->status;
    2079                 : 
    2080               0 :     _cairo_scaled_font_freeze_cache (scaled_font);
    2081                 : 
    2082               0 :     memset (glyph_cache, 0, sizeof (glyph_cache));
    2083                 : 
    2084               0 :     for (i = 0; i < num_glyphs; i++) {
    2085                 :         cairo_scaled_glyph_t    *scaled_glyph;
    2086                 :         cairo_fixed_t x, y, x1, y1, x2, y2;
    2087               0 :         int cache_index = glyphs[i].index % ARRAY_LENGTH (glyph_cache);
    2088                 : 
    2089               0 :         scaled_glyph = glyph_cache[cache_index];
    2090               0 :         if (scaled_glyph == NULL ||
    2091               0 :             _cairo_scaled_glyph_index (scaled_glyph) != glyphs[i].index)
    2092                 :         {
    2093               0 :             status = _cairo_scaled_glyph_lookup (scaled_font,
    2094               0 :                                                  glyphs[i].index,
    2095                 :                                                  CAIRO_SCALED_GLYPH_INFO_METRICS,
    2096                 :                                                  &scaled_glyph);
    2097               0 :             if (unlikely (status))
    2098               0 :                 break;
    2099                 : 
    2100               0 :             glyph_cache[cache_index] = scaled_glyph;
    2101                 :         }
    2102                 : 
    2103               0 :         if (round_glyph_positions == CAIRO_ROUND_GLYPH_POS_ON)
    2104               0 :             x = _cairo_fixed_from_int (_cairo_lround (glyphs[i].x));
    2105                 :         else
    2106               0 :             x = _cairo_fixed_from_double (glyphs[i].x);
    2107               0 :         x1 = x + scaled_glyph->bbox.p1.x;
    2108               0 :         x2 = x + scaled_glyph->bbox.p2.x;
    2109                 : 
    2110               0 :         if (round_glyph_positions == CAIRO_ROUND_GLYPH_POS_ON)
    2111               0 :             y = _cairo_fixed_from_int (_cairo_lround (glyphs[i].y));
    2112                 :         else
    2113               0 :             y = _cairo_fixed_from_double (glyphs[i].y);
    2114               0 :         y1 = y + scaled_glyph->bbox.p1.y;
    2115               0 :         y2 = y + scaled_glyph->bbox.p2.y;
    2116                 : 
    2117               0 :         if (overlap == FALSE)
    2118               0 :             overlap = _range_contains_glyph (&box, x1, y1, x2, y2);
    2119                 : 
    2120               0 :         if (x1 < box.p1.x) box.p1.x = x1;
    2121               0 :         if (x2 > box.p2.x) box.p2.x = x2;
    2122               0 :         if (y1 < box.p1.y) box.p1.y = y1;
    2123               0 :         if (y2 > box.p2.y) box.p2.y = y2;
    2124                 :     }
    2125                 : 
    2126               0 :     _cairo_scaled_font_thaw_cache (scaled_font);
    2127               0 :     if (unlikely (status))
    2128               0 :         return _cairo_scaled_font_set_error (scaled_font, status);
    2129                 : 
    2130               0 :     if (box.p1.x < box.p2.x) {
    2131               0 :         _cairo_box_round_to_rectangle (&box, extents);
    2132                 :     } else {
    2133               0 :         extents->x = extents->y = 0;
    2134               0 :         extents->width = extents->height = 0;
    2135                 :     }
    2136                 : 
    2137               0 :     if (overlap_out != NULL)
    2138               0 :         *overlap_out = overlap;
    2139                 : 
    2140               0 :     return CAIRO_STATUS_SUCCESS;
    2141                 : }
    2142                 : 
    2143                 : void
    2144               0 : _cairo_scaled_font_glyph_approximate_extents (cairo_scaled_font_t        *scaled_font,
    2145                 :                                               const cairo_glyph_t        *glyphs,
    2146                 :                                               int                      num_glyphs,
    2147                 :                                               cairo_rectangle_int_t   *extents)
    2148                 : {
    2149               0 :     double x0 = HUGE_VAL, x1 = -HUGE_VAL;
    2150               0 :     double y0 = HUGE_VAL, y1 = -HUGE_VAL;
    2151                 :     int i;
    2152                 : 
    2153               0 :     for (i = 0; i < num_glyphs; i++) {
    2154                 :         double g;
    2155                 : 
    2156               0 :         g = glyphs[i].x;
    2157               0 :         if (g < x0) x0 = g;
    2158               0 :         if (g > x1) x1 = g;
    2159                 : 
    2160               0 :         g = glyphs[i].y;
    2161               0 :         if (g < y0) y0 = g;
    2162               0 :         if (g > y1) y1 = g;
    2163                 :     }
    2164                 : 
    2165               0 :     if (x0 <= x1 && y0 <= y1) {
    2166               0 :         extents->x = floor (x0 - scaled_font->extents.max_x_advance);
    2167               0 :         extents->width = ceil (x1 + scaled_font->extents.max_x_advance);
    2168               0 :         extents->width -= extents->x;
    2169                 : 
    2170               0 :         extents->y = floor (y0 - scaled_font->extents.ascent);
    2171               0 :         extents->height = ceil (y1 + scaled_font->extents.descent);
    2172               0 :         extents->height -= extents->y;
    2173                 :     } else {
    2174               0 :         extents->x = extents->y = 0;
    2175               0 :         extents->width = extents->height = 0;
    2176                 :     }
    2177               0 : }
    2178                 : 
    2179                 : cairo_status_t
    2180               0 : _cairo_scaled_font_show_glyphs (cairo_scaled_font_t     *scaled_font,
    2181                 :                                 cairo_operator_t         op,
    2182                 :                                 const cairo_pattern_t   *pattern,
    2183                 :                                 cairo_surface_t         *surface,
    2184                 :                                 int                      source_x,
    2185                 :                                 int                      source_y,
    2186                 :                                 int                      dest_x,
    2187                 :                                 int                      dest_y,
    2188                 :                                 unsigned int             width,
    2189                 :                                 unsigned int             height,
    2190                 :                                 cairo_glyph_t           *glyphs,
    2191                 :                                 int                      num_glyphs,
    2192                 :                                 cairo_region_t          *clip_region)
    2193                 : {
    2194                 :     cairo_status_t status;
    2195               0 :     cairo_surface_t *mask = NULL;
    2196               0 :     cairo_format_t mask_format = CAIRO_FORMAT_A1; /* shut gcc up */
    2197                 :     cairo_surface_pattern_t mask_pattern;
    2198                 :     int i;
    2199                 : 
    2200                 :     /* These operators aren't interpreted the same way by the backends;
    2201                 :      * they are implemented in terms of other operators in cairo-gstate.c
    2202                 :      */
    2203               0 :     assert (op != CAIRO_OPERATOR_SOURCE && op != CAIRO_OPERATOR_CLEAR);
    2204                 : 
    2205               0 :     if (scaled_font->status)
    2206               0 :         return scaled_font->status;
    2207                 : 
    2208               0 :     if (!num_glyphs)
    2209               0 :         return CAIRO_STATUS_SUCCESS;
    2210                 : 
    2211               0 :     if (scaled_font->backend->show_glyphs != NULL) {
    2212               0 :         int remaining_glyphs = num_glyphs;
    2213               0 :         status = scaled_font->backend->show_glyphs (scaled_font,
    2214                 :                                                     op, pattern,
    2215                 :                                                     surface,
    2216                 :                                                     source_x, source_y,
    2217                 :                                                     dest_x, dest_y,
    2218                 :                                                     width, height,
    2219                 :                                                     glyphs, num_glyphs,
    2220                 :                                                     clip_region,
    2221                 :                                                     &remaining_glyphs);
    2222               0 :         glyphs += num_glyphs - remaining_glyphs;
    2223               0 :         num_glyphs = remaining_glyphs;
    2224               0 :         if (remaining_glyphs == 0)
    2225               0 :             status = CAIRO_STATUS_SUCCESS;
    2226               0 :         if (status != CAIRO_INT_STATUS_UNSUPPORTED)
    2227               0 :             return _cairo_scaled_font_set_error (scaled_font, status);
    2228                 :     }
    2229                 : 
    2230                 :     /* Font display routine either does not exist or failed. */
    2231                 : 
    2232               0 :     _cairo_scaled_font_freeze_cache (scaled_font);
    2233                 : 
    2234               0 :     for (i = 0; i < num_glyphs; i++) {
    2235                 :         int x, y;
    2236                 :         cairo_image_surface_t *glyph_surface;
    2237                 :         cairo_scaled_glyph_t *scaled_glyph;
    2238                 : 
    2239               0 :         status = _cairo_scaled_glyph_lookup (scaled_font,
    2240               0 :                                              glyphs[i].index,
    2241                 :                                              CAIRO_SCALED_GLYPH_INFO_SURFACE,
    2242                 :                                              &scaled_glyph);
    2243                 : 
    2244               0 :         if (unlikely (status))
    2245               0 :             goto CLEANUP_MASK;
    2246                 : 
    2247               0 :         glyph_surface = scaled_glyph->surface;
    2248                 : 
    2249                 :         /* To start, create the mask using the format from the first
    2250                 :          * glyph. Later we'll deal with different formats. */
    2251               0 :         if (mask == NULL) {
    2252               0 :             mask_format = glyph_surface->format;
    2253               0 :             mask = cairo_image_surface_create (mask_format, width, height);
    2254               0 :             status = mask->status;
    2255               0 :             if (unlikely (status))
    2256               0 :                 goto CLEANUP_MASK;
    2257                 :         }
    2258                 : 
    2259                 :         /* If we have glyphs of different formats, we "upgrade" the mask
    2260                 :          * to the wider of the formats. */
    2261               0 :         if (glyph_surface->format != mask_format &&
    2262               0 :             _cairo_format_bits_per_pixel (mask_format) <
    2263               0 :             _cairo_format_bits_per_pixel (glyph_surface->format) )
    2264                 :         {
    2265                 :             cairo_surface_t *new_mask;
    2266                 : 
    2267               0 :             switch (glyph_surface->format) {
    2268                 :             case CAIRO_FORMAT_ARGB32:
    2269                 :             case CAIRO_FORMAT_A8:
    2270                 :             case CAIRO_FORMAT_A1:
    2271               0 :                 mask_format = glyph_surface->format;
    2272               0 :                 break;
    2273                 :             case CAIRO_FORMAT_RGB16_565:
    2274                 :             case CAIRO_FORMAT_RGB24:
    2275                 :             case CAIRO_FORMAT_INVALID:
    2276                 :             default:
    2277               0 :                 ASSERT_NOT_REACHED;
    2278               0 :                 mask_format = CAIRO_FORMAT_ARGB32;
    2279               0 :                 break;
    2280                 :             }
    2281                 : 
    2282               0 :             new_mask = cairo_image_surface_create (mask_format, width, height);
    2283               0 :             status = new_mask->status;
    2284               0 :             if (unlikely (status)) {
    2285               0 :                 cairo_surface_destroy (new_mask);
    2286               0 :                 goto CLEANUP_MASK;
    2287                 :             }
    2288                 : 
    2289               0 :             _cairo_pattern_init_for_surface (&mask_pattern, mask);
    2290                 :             /* Note that we only upgrade masks, i.e. A1 -> A8 -> ARGB32, so there is
    2291                 :              * never any component alpha here.
    2292                 :              */
    2293               0 :             status = _cairo_surface_composite (CAIRO_OPERATOR_ADD,
    2294                 :                                                &_cairo_pattern_white.base,
    2295                 :                                                &mask_pattern.base,
    2296                 :                                                new_mask,
    2297                 :                                                0, 0,
    2298                 :                                                0, 0,
    2299                 :                                                0, 0,
    2300                 :                                                width, height,
    2301                 :                                                NULL);
    2302                 : 
    2303               0 :             _cairo_pattern_fini (&mask_pattern.base);
    2304                 : 
    2305               0 :             if (unlikely (status)) {
    2306               0 :                 cairo_surface_destroy (new_mask);
    2307               0 :                 goto CLEANUP_MASK;
    2308                 :             }
    2309                 : 
    2310               0 :             cairo_surface_destroy (mask);
    2311               0 :             mask = new_mask;
    2312                 :         }
    2313                 : 
    2314               0 :         if (glyph_surface->width && glyph_surface->height) {
    2315                 :             cairo_surface_pattern_t glyph_pattern;
    2316                 : 
    2317                 :             /* round glyph locations to the nearest pixel */
    2318                 :             /* XXX: FRAGILE: We're ignoring device_transform scaling here. A bug? */
    2319               0 :             x = _cairo_lround (glyphs[i].x -
    2320               0 :                                glyph_surface->base.device_transform.x0);
    2321               0 :             y = _cairo_lround (glyphs[i].y -
    2322               0 :                                glyph_surface->base.device_transform.y0);
    2323                 : 
    2324               0 :             _cairo_pattern_init_for_surface (&glyph_pattern,
    2325                 :                                              &glyph_surface->base);
    2326               0 :             if (mask_format == CAIRO_FORMAT_ARGB32)
    2327               0 :                 glyph_pattern.base.has_component_alpha = TRUE;
    2328                 : 
    2329               0 :             status = _cairo_surface_composite (CAIRO_OPERATOR_ADD,
    2330                 :                                                &_cairo_pattern_white.base,
    2331                 :                                                &glyph_pattern.base,
    2332                 :                                                mask,
    2333                 :                                                0, 0,
    2334                 :                                                0, 0,
    2335                 :                                                x - dest_x, y - dest_y,
    2336               0 :                                                glyph_surface->width,
    2337               0 :                                                glyph_surface->height,
    2338                 :                                                NULL);
    2339                 : 
    2340               0 :             _cairo_pattern_fini (&glyph_pattern.base);
    2341                 : 
    2342               0 :             if (unlikely (status))
    2343               0 :                 goto CLEANUP_MASK;
    2344                 :         }
    2345                 :     }
    2346                 : 
    2347               0 :     _cairo_pattern_init_for_surface (&mask_pattern, mask);
    2348               0 :     if (mask_format == CAIRO_FORMAT_ARGB32)
    2349               0 :         mask_pattern.base.has_component_alpha = TRUE;
    2350                 : 
    2351               0 :     status = _cairo_surface_composite (op, pattern, &mask_pattern.base,
    2352                 :                                        surface,
    2353                 :                                        source_x, source_y,
    2354                 :                                        0,        0,
    2355                 :                                        dest_x,   dest_y,
    2356                 :                                        width,    height,
    2357                 :                                        clip_region);
    2358                 : 
    2359               0 :     _cairo_pattern_fini (&mask_pattern.base);
    2360                 : 
    2361                 : CLEANUP_MASK:
    2362               0 :     _cairo_scaled_font_thaw_cache (scaled_font);
    2363                 : 
    2364               0 :     if (mask != NULL)
    2365               0 :         cairo_surface_destroy (mask);
    2366               0 :     return _cairo_scaled_font_set_error (scaled_font, status);
    2367                 : }
    2368                 : 
    2369                 : /* Add a single-device-unit rectangle to a path. */
    2370                 : static cairo_status_t
    2371               0 : _add_unit_rectangle_to_path (cairo_path_fixed_t *path,
    2372                 :                              cairo_fixed_t x,
    2373                 :                              cairo_fixed_t y)
    2374                 : {
    2375                 :     cairo_status_t status;
    2376                 : 
    2377               0 :     status = _cairo_path_fixed_move_to (path, x, y);
    2378               0 :     if (unlikely (status))
    2379               0 :         return status;
    2380                 : 
    2381               0 :     status = _cairo_path_fixed_rel_line_to (path,
    2382                 :                                             _cairo_fixed_from_int (1),
    2383                 :                                             _cairo_fixed_from_int (0));
    2384               0 :     if (unlikely (status))
    2385               0 :         return status;
    2386                 : 
    2387               0 :     status = _cairo_path_fixed_rel_line_to (path,
    2388                 :                                             _cairo_fixed_from_int (0),
    2389                 :                                             _cairo_fixed_from_int (1));
    2390               0 :     if (unlikely (status))
    2391               0 :         return status;
    2392                 : 
    2393               0 :     status = _cairo_path_fixed_rel_line_to (path,
    2394                 :                                             _cairo_fixed_from_int (-1),
    2395                 :                                             _cairo_fixed_from_int (0));
    2396               0 :     if (unlikely (status))
    2397               0 :         return status;
    2398                 : 
    2399               0 :     return _cairo_path_fixed_close_path (path);
    2400                 : }
    2401                 : 
    2402                 : /**
    2403                 :  * _trace_mask_to_path:
    2404                 :  * @bitmap: An alpha mask (either %CAIRO_FORMAT_A1 or %CAIRO_FORMAT_A8)
    2405                 :  * @path: An initialized path to hold the result
    2406                 :  *
    2407                 :  * Given a mask surface, (an alpha image), fill out the provided path
    2408                 :  * so that when filled it would result in something that approximates
    2409                 :  * the mask.
    2410                 :  *
    2411                 :  * Note: The current tracing code here is extremely primitive. It
    2412                 :  * operates only on an A1 surface, (converting an A8 surface to A1 if
    2413                 :  * necessary), and performs the tracing by drawing a little square
    2414                 :  * around each pixel that is on in the mask. We do not pretend that
    2415                 :  * this is a high-quality result. But we are leaving it up to someone
    2416                 :  * who cares enough about getting a better result to implement
    2417                 :  * something more sophisticated.
    2418                 :  **/
    2419                 : static cairo_status_t
    2420               0 : _trace_mask_to_path (cairo_image_surface_t *mask,
    2421                 :                      cairo_path_fixed_t *path,
    2422                 :                      double tx, double ty)
    2423                 : {
    2424                 :     const uint8_t *row;
    2425                 :     int rows, cols, bytes_per_row;
    2426                 :     int x, y, bit;
    2427                 :     double xoff, yoff;
    2428                 :     cairo_fixed_t x0, y0;
    2429                 :     cairo_fixed_t px, py;
    2430                 :     cairo_status_t status;
    2431                 : 
    2432               0 :     mask = _cairo_image_surface_coerce_to_format (mask, CAIRO_FORMAT_A1);
    2433               0 :     status = mask->base.status;
    2434               0 :     if (unlikely (status))
    2435               0 :         return status;
    2436                 : 
    2437               0 :     cairo_surface_get_device_offset (&mask->base, &xoff, &yoff);
    2438               0 :     x0 = _cairo_fixed_from_double (tx - xoff);
    2439               0 :     y0 = _cairo_fixed_from_double (ty - yoff);
    2440                 : 
    2441               0 :     bytes_per_row = (mask->width + 7) / 8;
    2442               0 :     row = mask->data;
    2443               0 :     for (y = 0, rows = mask->height; rows--; row += mask->stride, y++) {
    2444               0 :         const uint8_t *byte_ptr = row;
    2445               0 :         x = 0;
    2446               0 :         py = _cairo_fixed_from_int (y);
    2447               0 :         for (cols = bytes_per_row; cols--; ) {
    2448               0 :             uint8_t byte = *byte_ptr++;
    2449               0 :             if (byte == 0) {
    2450               0 :                 x += 8;
    2451               0 :                 continue;
    2452                 :             }
    2453                 : 
    2454               0 :             byte = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (byte);
    2455               0 :             for (bit = 1 << 7; bit && x < mask->width; bit >>= 1, x++) {
    2456               0 :                 if (byte & bit) {
    2457               0 :                     px = _cairo_fixed_from_int (x);
    2458               0 :                     status = _add_unit_rectangle_to_path (path,
    2459                 :                                                           px + x0,
    2460                 :                                                           py + y0);
    2461               0 :                     if (unlikely (status))
    2462               0 :                         goto BAIL;
    2463                 :                 }
    2464                 :             }
    2465                 :         }
    2466                 :     }
    2467                 : 
    2468                 : BAIL:
    2469               0 :     cairo_surface_destroy (&mask->base);
    2470                 : 
    2471               0 :     return status;
    2472                 : }
    2473                 : 
    2474                 : cairo_status_t
    2475               0 : _cairo_scaled_font_glyph_path (cairo_scaled_font_t *scaled_font,
    2476                 :                                const cairo_glyph_t *glyphs,
    2477                 :                                int                  num_glyphs,
    2478                 :                                cairo_path_fixed_t  *path)
    2479                 : {
    2480                 :     cairo_status_t status;
    2481                 :     int i;
    2482                 : 
    2483               0 :     status = scaled_font->status;
    2484               0 :     if (unlikely (status))
    2485               0 :         return status;
    2486                 : 
    2487               0 :     _cairo_scaled_font_freeze_cache (scaled_font);
    2488               0 :     for (i = 0; i < num_glyphs; i++) {
    2489                 :         cairo_scaled_glyph_t *scaled_glyph;
    2490                 : 
    2491               0 :         status = _cairo_scaled_glyph_lookup (scaled_font,
    2492               0 :                                              glyphs[i].index,
    2493                 :                                              CAIRO_SCALED_GLYPH_INFO_PATH,
    2494                 :                                              &scaled_glyph);
    2495               0 :         if (status == CAIRO_STATUS_SUCCESS) {
    2496               0 :             status = _cairo_path_fixed_append (path,
    2497               0 :                                                scaled_glyph->path, CAIRO_DIRECTION_FORWARD,
    2498               0 :                                                _cairo_fixed_from_double (glyphs[i].x),
    2499               0 :                                                _cairo_fixed_from_double (glyphs[i].y));
    2500                 : 
    2501               0 :         } else if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
    2502                 :             /* If the font is incapable of providing a path, then we'll
    2503                 :              * have to trace our own from a surface.
    2504                 :              */
    2505               0 :             status = _cairo_scaled_glyph_lookup (scaled_font,
    2506               0 :                                                  glyphs[i].index,
    2507                 :                                                  CAIRO_SCALED_GLYPH_INFO_SURFACE,
    2508                 :                                                  &scaled_glyph);
    2509               0 :             if (unlikely (status))
    2510               0 :                 goto BAIL;
    2511                 : 
    2512               0 :             status = _trace_mask_to_path (scaled_glyph->surface, path,
    2513               0 :                                           glyphs[i].x, glyphs[i].y);
    2514                 :         }
    2515                 : 
    2516               0 :         if (unlikely (status))
    2517               0 :             goto BAIL;
    2518                 :     }
    2519                 :   BAIL:
    2520               0 :     _cairo_scaled_font_thaw_cache (scaled_font);
    2521                 : 
    2522               0 :     return _cairo_scaled_font_set_error (scaled_font, status);
    2523                 : }
    2524                 : 
    2525                 : /**
    2526                 :  * _cairo_scaled_glyph_set_metrics:
    2527                 :  * @scaled_glyph: a #cairo_scaled_glyph_t
    2528                 :  * @scaled_font: a #cairo_scaled_font_t
    2529                 :  * @fs_metrics: a #cairo_text_extents_t in font space
    2530                 :  *
    2531                 :  * _cairo_scaled_glyph_set_metrics() stores user space metrics
    2532                 :  * for the specified glyph given font space metrics. It is
    2533                 :  * called by the font backend when initializing a glyph with
    2534                 :  * %CAIRO_SCALED_GLYPH_INFO_METRICS.
    2535                 :  **/
    2536                 : void
    2537               0 : _cairo_scaled_glyph_set_metrics (cairo_scaled_glyph_t *scaled_glyph,
    2538                 :                                  cairo_scaled_font_t *scaled_font,
    2539                 :                                  cairo_text_extents_t *fs_metrics)
    2540                 : {
    2541               0 :     cairo_bool_t first = TRUE;
    2542                 :     double hm, wm;
    2543               0 :     double min_user_x = 0.0, max_user_x = 0.0, min_user_y = 0.0, max_user_y = 0.0;
    2544               0 :     double min_device_x = 0.0, max_device_x = 0.0, min_device_y = 0.0, max_device_y = 0.0;
    2545                 :     double device_x_advance, device_y_advance;
    2546                 : 
    2547               0 :     scaled_glyph->fs_metrics = *fs_metrics;
    2548                 : 
    2549               0 :     for (hm = 0.0; hm <= 1.0; hm += 1.0)
    2550               0 :         for (wm = 0.0; wm <= 1.0; wm += 1.0) {
    2551                 :             double x, y;
    2552                 : 
    2553                 :             /* Transform this corner to user space */
    2554               0 :             x = fs_metrics->x_bearing + fs_metrics->width * wm;
    2555               0 :             y = fs_metrics->y_bearing + fs_metrics->height * hm;
    2556               0 :             cairo_matrix_transform_point (&scaled_font->font_matrix,
    2557                 :                                           &x, &y);
    2558               0 :             if (first) {
    2559               0 :                 min_user_x = max_user_x = x;
    2560               0 :                 min_user_y = max_user_y = y;
    2561                 :             } else {
    2562               0 :                 if (x < min_user_x) min_user_x = x;
    2563               0 :                 if (x > max_user_x) max_user_x = x;
    2564               0 :                 if (y < min_user_y) min_user_y = y;
    2565               0 :                 if (y > max_user_y) max_user_y = y;
    2566                 :             }
    2567                 : 
    2568                 :             /* Transform this corner to device space from glyph origin */
    2569               0 :             x = fs_metrics->x_bearing + fs_metrics->width * wm;
    2570               0 :             y = fs_metrics->y_bearing + fs_metrics->height * hm;
    2571               0 :             cairo_matrix_transform_distance (&scaled_font->scale,
    2572                 :                                              &x, &y);
    2573                 : 
    2574               0 :             if (first) {
    2575               0 :                 min_device_x = max_device_x = x;
    2576               0 :                 min_device_y = max_device_y = y;
    2577                 :             } else {
    2578               0 :                 if (x < min_device_x) min_device_x = x;
    2579               0 :                 if (x > max_device_x) max_device_x = x;
    2580               0 :                 if (y < min_device_y) min_device_y = y;
    2581               0 :                 if (y > max_device_y) max_device_y = y;
    2582                 :             }
    2583               0 :             first = FALSE;
    2584                 :         }
    2585               0 :     scaled_glyph->metrics.x_bearing = min_user_x;
    2586               0 :     scaled_glyph->metrics.y_bearing = min_user_y;
    2587               0 :     scaled_glyph->metrics.width = max_user_x - min_user_x;
    2588               0 :     scaled_glyph->metrics.height = max_user_y - min_user_y;
    2589                 : 
    2590               0 :     scaled_glyph->metrics.x_advance = fs_metrics->x_advance;
    2591               0 :     scaled_glyph->metrics.y_advance = fs_metrics->y_advance;
    2592               0 :     cairo_matrix_transform_distance (&scaled_font->font_matrix,
    2593                 :                                      &scaled_glyph->metrics.x_advance,
    2594                 :                                      &scaled_glyph->metrics.y_advance);
    2595                 : 
    2596               0 :     device_x_advance = fs_metrics->x_advance;
    2597               0 :     device_y_advance = fs_metrics->y_advance;
    2598               0 :     cairo_matrix_transform_distance (&scaled_font->scale,
    2599                 :                                      &device_x_advance,
    2600                 :                                      &device_y_advance);
    2601                 : 
    2602               0 :     scaled_glyph->bbox.p1.x = _cairo_fixed_from_double (min_device_x);
    2603               0 :     scaled_glyph->bbox.p1.y = _cairo_fixed_from_double (min_device_y);
    2604               0 :     scaled_glyph->bbox.p2.x = _cairo_fixed_from_double (max_device_x);
    2605               0 :     scaled_glyph->bbox.p2.y = _cairo_fixed_from_double (max_device_y);
    2606                 : 
    2607               0 :     scaled_glyph->x_advance = _cairo_lround (device_x_advance);
    2608               0 :     scaled_glyph->y_advance = _cairo_lround (device_y_advance);
    2609                 : 
    2610               0 :     scaled_glyph->has_info |= CAIRO_SCALED_GLYPH_INFO_METRICS;
    2611               0 : }
    2612                 : 
    2613                 : void
    2614               0 : _cairo_scaled_glyph_set_surface (cairo_scaled_glyph_t *scaled_glyph,
    2615                 :                                  cairo_scaled_font_t *scaled_font,
    2616                 :                                  cairo_image_surface_t *surface)
    2617                 : {
    2618               0 :     if (scaled_glyph->surface != NULL)
    2619               0 :         cairo_surface_destroy (&scaled_glyph->surface->base);
    2620                 : 
    2621                 :     /* sanity check the backend glyph contents */
    2622                 :     _cairo_debug_check_image_surface_is_defined (&surface->base);
    2623               0 :     scaled_glyph->surface = surface;
    2624                 : 
    2625               0 :     if (surface != NULL)
    2626               0 :         scaled_glyph->has_info |= CAIRO_SCALED_GLYPH_INFO_SURFACE;
    2627                 :     else
    2628               0 :         scaled_glyph->has_info &= ~CAIRO_SCALED_GLYPH_INFO_SURFACE;
    2629               0 : }
    2630                 : 
    2631                 : void
    2632               0 : _cairo_scaled_glyph_set_path (cairo_scaled_glyph_t *scaled_glyph,
    2633                 :                               cairo_scaled_font_t *scaled_font,
    2634                 :                               cairo_path_fixed_t *path)
    2635                 : {
    2636               0 :     if (scaled_glyph->path != NULL)
    2637               0 :         _cairo_path_fixed_destroy (scaled_glyph->path);
    2638                 : 
    2639               0 :     scaled_glyph->path = path;
    2640                 : 
    2641               0 :     if (path != NULL)
    2642               0 :         scaled_glyph->has_info |= CAIRO_SCALED_GLYPH_INFO_PATH;
    2643                 :     else
    2644               0 :         scaled_glyph->has_info &= ~CAIRO_SCALED_GLYPH_INFO_PATH;
    2645               0 : }
    2646                 : 
    2647                 : void
    2648               0 : _cairo_scaled_glyph_set_recording_surface (cairo_scaled_glyph_t *scaled_glyph,
    2649                 :                                            cairo_scaled_font_t *scaled_font,
    2650                 :                                            cairo_surface_t *recording_surface)
    2651                 : {
    2652               0 :     if (scaled_glyph->recording_surface != NULL) {
    2653               0 :         cairo_surface_finish (scaled_glyph->recording_surface);
    2654               0 :         cairo_surface_destroy (scaled_glyph->recording_surface);
    2655                 :     }
    2656                 : 
    2657               0 :     scaled_glyph->recording_surface = recording_surface;
    2658                 : 
    2659               0 :     if (recording_surface != NULL)
    2660               0 :         scaled_glyph->has_info |= CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE;
    2661                 :     else
    2662               0 :         scaled_glyph->has_info &= ~CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE;
    2663               0 : }
    2664                 : 
    2665                 : static cairo_bool_t
    2666               0 : _cairo_scaled_glyph_page_can_remove (const void *closure)
    2667                 : {
    2668               0 :     const cairo_scaled_glyph_page_t *page = closure;
    2669                 :     const cairo_scaled_font_t *scaled_font;
    2670                 : 
    2671               0 :     scaled_font = (cairo_scaled_font_t *) page->cache_entry.hash;
    2672               0 :     return scaled_font->cache_frozen == 0;
    2673                 : }
    2674                 : 
    2675                 : static cairo_status_t
    2676               0 : _cairo_scaled_font_allocate_glyph (cairo_scaled_font_t *scaled_font,
    2677                 :                                    cairo_scaled_glyph_t **scaled_glyph)
    2678                 : {
    2679                 :     cairo_scaled_glyph_page_t *page;
    2680                 :     cairo_status_t status;
    2681                 : 
    2682                 :     /* only the first page in the list may contain available slots */
    2683               0 :     if (! cairo_list_is_empty (&scaled_font->glyph_pages)) {
    2684               0 :         page = cairo_list_last_entry (&scaled_font->glyph_pages,
    2685                 :                                       cairo_scaled_glyph_page_t,
    2686                 :                                       link);
    2687               0 :         if (page->num_glyphs < CAIRO_SCALED_GLYPH_PAGE_SIZE) {
    2688               0 :             *scaled_glyph = &page->glyphs[page->num_glyphs++];
    2689               0 :             return CAIRO_STATUS_SUCCESS;
    2690                 :         }
    2691                 :     }
    2692                 : 
    2693               0 :     page = malloc (sizeof (cairo_scaled_glyph_page_t));
    2694               0 :     if (unlikely (page == NULL))
    2695               0 :         return _cairo_error (CAIRO_STATUS_NO_MEMORY);
    2696                 : 
    2697               0 :     page->cache_entry.hash = (uintptr_t) scaled_font;
    2698               0 :     page->cache_entry.size = 1; /* XXX occupancy weighting? */
    2699               0 :     page->num_glyphs = 0;
    2700                 : 
    2701                 :     CAIRO_MUTEX_LOCK (_cairo_scaled_glyph_page_cache_mutex);
    2702               0 :     if (scaled_font->global_cache_frozen == FALSE) {
    2703               0 :         if (unlikely (cairo_scaled_glyph_page_cache.hash_table == NULL)) {
    2704               0 :             status = _cairo_cache_init (&cairo_scaled_glyph_page_cache,
    2705                 :                                         NULL,
    2706                 :                                         _cairo_scaled_glyph_page_can_remove,
    2707                 :                                         _cairo_scaled_glyph_page_destroy,
    2708                 :                                         MAX_GLYPH_PAGES_CACHED);
    2709               0 :             if (unlikely (status)) {
    2710                 :                 CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex);
    2711               0 :                 free (page);
    2712               0 :                 return status;
    2713                 :             }
    2714                 :         }
    2715                 : 
    2716               0 :         _cairo_cache_freeze (&cairo_scaled_glyph_page_cache);
    2717               0 :         scaled_font->global_cache_frozen = TRUE;
    2718                 :     }
    2719                 : 
    2720               0 :     status = _cairo_cache_insert (&cairo_scaled_glyph_page_cache,
    2721                 :                                   &page->cache_entry);
    2722                 :     CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex);
    2723               0 :     if (unlikely (status)) {
    2724               0 :         free (page);
    2725               0 :         return status;
    2726                 :     }
    2727                 : 
    2728               0 :     cairo_list_add_tail (&page->link, &scaled_font->glyph_pages);
    2729                 : 
    2730               0 :     *scaled_glyph = &page->glyphs[page->num_glyphs++];
    2731               0 :     return CAIRO_STATUS_SUCCESS;
    2732                 : }
    2733                 : 
    2734                 : static void
    2735               0 : _cairo_scaled_font_free_last_glyph (cairo_scaled_font_t *scaled_font,
    2736                 :                                    cairo_scaled_glyph_t *scaled_glyph)
    2737                 : {
    2738                 :     cairo_scaled_glyph_page_t *page;
    2739                 : 
    2740               0 :     assert (! cairo_list_is_empty (&scaled_font->glyph_pages));
    2741               0 :     page = cairo_list_last_entry (&scaled_font->glyph_pages,
    2742                 :                                   cairo_scaled_glyph_page_t,
    2743                 :                                   link);
    2744               0 :     assert (scaled_glyph == &page->glyphs[page->num_glyphs-1]);
    2745                 : 
    2746               0 :     _cairo_scaled_glyph_fini (scaled_font, scaled_glyph);
    2747                 : 
    2748               0 :     if (--page->num_glyphs == 0) {
    2749               0 :         _cairo_cache_remove (&cairo_scaled_glyph_page_cache,
    2750                 :                              &page->cache_entry);
    2751                 :     }
    2752               0 : }
    2753                 : 
    2754                 : /**
    2755                 :  * _cairo_scaled_glyph_lookup:
    2756                 :  * @scaled_font: a #cairo_scaled_font_t
    2757                 :  * @index: the glyph to create
    2758                 :  * @info: a #cairo_scaled_glyph_info_t marking which portions of
    2759                 :  * the glyph should be filled in.
    2760                 :  * @scaled_glyph_ret: a #cairo_scaled_glyph_t where the glyph
    2761                 :  * is returned.
    2762                 :  *
    2763                 :  * If the desired info is not available, (for example, when trying to
    2764                 :  * get INFO_PATH with a bitmapped font), this function will return
    2765                 :  * %CAIRO_INT_STATUS_UNSUPPORTED.
    2766                 :  *
    2767                 :  * Note: This function must be called with the scaled font frozen, and it must
    2768                 :  * remain frozen for as long as the @scaled_glyph_ret is alive. (If the scaled
    2769                 :  * font was not frozen, then there is no guarantee that the glyph would not be
    2770                 :  * evicted before you tried to access it.) See
    2771                 :  * _cairo_scaled_font_freeze_cache() and _cairo_scaled_font_thaw_cache().
    2772                 :  *
    2773                 :  * Returns: a glyph with the requested portions filled in. Glyph
    2774                 :  * lookup is cached and glyph will be automatically freed along
    2775                 :  * with the scaled_font so no explicit free is required.
    2776                 :  * @info can be one or more of:
    2777                 :  *  %CAIRO_SCALED_GLYPH_INFO_METRICS - glyph metrics and bounding box
    2778                 :  *  %CAIRO_SCALED_GLYPH_INFO_SURFACE - surface holding glyph image
    2779                 :  *  %CAIRO_SCALED_GLYPH_INFO_PATH - path holding glyph outline in device space
    2780                 :  **/
    2781                 : cairo_int_status_t
    2782               0 : _cairo_scaled_glyph_lookup (cairo_scaled_font_t *scaled_font,
    2783                 :                             unsigned long index,
    2784                 :                             cairo_scaled_glyph_info_t info,
    2785                 :                             cairo_scaled_glyph_t **scaled_glyph_ret)
    2786                 : {
    2787               0 :     cairo_status_t               status = CAIRO_STATUS_SUCCESS;
    2788                 :     cairo_scaled_glyph_t        *scaled_glyph;
    2789                 :     cairo_scaled_glyph_info_t    need_info;
    2790                 : 
    2791               0 :     *scaled_glyph_ret = NULL;
    2792                 : 
    2793               0 :     if (unlikely (scaled_font->status))
    2794               0 :         return scaled_font->status;
    2795                 : 
    2796                 :     if (CAIRO_INJECT_FAULT ())
    2797                 :         return _cairo_error (CAIRO_STATUS_NO_MEMORY);
    2798                 : 
    2799                 :     /*
    2800                 :      * Check cache for glyph
    2801                 :      */
    2802               0 :     scaled_glyph = _cairo_hash_table_lookup (scaled_font->glyphs,
    2803                 :                                              (cairo_hash_entry_t *) &index);
    2804               0 :     if (scaled_glyph == NULL) {
    2805               0 :         status = _cairo_scaled_font_allocate_glyph (scaled_font, &scaled_glyph);
    2806               0 :         if (unlikely (status))
    2807               0 :             goto err;
    2808                 : 
    2809               0 :         memset (scaled_glyph, 0, sizeof (cairo_scaled_glyph_t));
    2810               0 :         _cairo_scaled_glyph_set_index (scaled_glyph, index);
    2811                 : 
    2812                 :         /* ask backend to initialize metrics and shape fields */
    2813               0 :         status =
    2814               0 :             scaled_font->backend->scaled_glyph_init (scaled_font,
    2815                 :                                                      scaled_glyph,
    2816               0 :                                                      info | CAIRO_SCALED_GLYPH_INFO_METRICS);
    2817               0 :         if (unlikely (status)) {
    2818               0 :             _cairo_scaled_font_free_last_glyph (scaled_font, scaled_glyph);
    2819               0 :             goto err;
    2820                 :         }
    2821                 : 
    2822               0 :         status = _cairo_hash_table_insert (scaled_font->glyphs,
    2823               0 :                                            &scaled_glyph->hash_entry);
    2824               0 :         if (unlikely (status)) {
    2825               0 :             _cairo_scaled_font_free_last_glyph (scaled_font, scaled_glyph);
    2826               0 :             goto err;
    2827                 :         }
    2828                 :     }
    2829                 : 
    2830                 :     /*
    2831                 :      * Check and see if the glyph, as provided,
    2832                 :      * already has the requested data and amend it if not
    2833                 :      */
    2834               0 :     need_info = info & ~scaled_glyph->has_info;
    2835               0 :     if (need_info) {
    2836               0 :         status = scaled_font->backend->scaled_glyph_init (scaled_font,
    2837                 :                                                           scaled_glyph,
    2838                 :                                                           need_info);
    2839               0 :         if (unlikely (status))
    2840               0 :             goto err;
    2841                 : 
    2842                 :         /* Don't trust the scaled_glyph_init() return value, the font
    2843                 :          * backend may not even know about some of the info.  For example,
    2844                 :          * no backend other than the user-fonts knows about recording-surface
    2845                 :          * glyph info. */
    2846               0 :         if (info & ~scaled_glyph->has_info)
    2847               0 :             return CAIRO_INT_STATUS_UNSUPPORTED;
    2848                 :     }
    2849                 : 
    2850               0 :     *scaled_glyph_ret = scaled_glyph;
    2851               0 :     return CAIRO_STATUS_SUCCESS;
    2852                 : 
    2853                 : err:
    2854                 :     /* It's not an error for the backend to not support the info we want. */
    2855               0 :     if (status != CAIRO_INT_STATUS_UNSUPPORTED)
    2856               0 :         status = _cairo_scaled_font_set_error (scaled_font, status);
    2857               0 :     return status;
    2858                 : }
    2859                 : 
    2860                 : double
    2861               0 : _cairo_scaled_font_get_max_scale (cairo_scaled_font_t *scaled_font)
    2862                 : {
    2863               0 :     return scaled_font->max_scale;
    2864                 : }
    2865                 : 
    2866                 : 
    2867                 : /**
    2868                 :  * cairo_scaled_font_get_font_face:
    2869                 :  * @scaled_font: a #cairo_scaled_font_t
    2870                 :  *
    2871                 :  * Gets the font face that this scaled font uses.  This is the
    2872                 :  * font face passed to cairo_scaled_font_create().
    2873                 :  *
    2874                 :  * Return value: The #cairo_font_face_t with which @scaled_font was
    2875                 :  * created.
    2876                 :  *
    2877                 :  * Since: 1.2
    2878                 :  **/
    2879                 : cairo_font_face_t *
    2880               0 : cairo_scaled_font_get_font_face (cairo_scaled_font_t *scaled_font)
    2881                 : {
    2882               0 :     if (scaled_font->status)
    2883               0 :         return (cairo_font_face_t*) &_cairo_font_face_nil;
    2884                 : 
    2885               0 :     if (scaled_font->original_font_face != NULL)
    2886               0 :         return scaled_font->original_font_face;
    2887                 : 
    2888               0 :     return scaled_font->font_face;
    2889                 : }
    2890                 : slim_hidden_def (cairo_scaled_font_get_font_face);
    2891                 : 
    2892                 : /**
    2893                 :  * cairo_scaled_font_get_font_matrix:
    2894                 :  * @scaled_font: a #cairo_scaled_font_t
    2895                 :  * @font_matrix: return value for the matrix
    2896                 :  *
    2897                 :  * Stores the font matrix with which @scaled_font was created into
    2898                 :  * @matrix.
    2899                 :  *
    2900                 :  * Since: 1.2
    2901                 :  **/
    2902                 : void
    2903               0 : cairo_scaled_font_get_font_matrix (cairo_scaled_font_t  *scaled_font,
    2904                 :                                    cairo_matrix_t       *font_matrix)
    2905                 : {
    2906               0 :     if (scaled_font->status) {
    2907               0 :         cairo_matrix_init_identity (font_matrix);
    2908               0 :         return;
    2909                 :     }
    2910                 : 
    2911               0 :     *font_matrix = scaled_font->font_matrix;
    2912                 : }
    2913                 : slim_hidden_def (cairo_scaled_font_get_font_matrix);
    2914                 : 
    2915                 : /**
    2916                 :  * cairo_scaled_font_get_ctm:
    2917                 :  * @scaled_font: a #cairo_scaled_font_t
    2918                 :  * @ctm: return value for the CTM
    2919                 :  *
    2920                 :  * Stores the CTM with which @scaled_font was created into @ctm.
    2921                 :  * Note that the translation offsets (x0, y0) of the CTM are ignored
    2922                 :  * by cairo_scaled_font_create().  So, the matrix this
    2923                 :  * function returns always has 0,0 as x0,y0.
    2924                 :  *
    2925                 :  * Since: 1.2
    2926                 :  **/
    2927                 : void
    2928               0 : cairo_scaled_font_get_ctm (cairo_scaled_font_t  *scaled_font,
    2929                 :                            cairo_matrix_t       *ctm)
    2930                 : {
    2931               0 :     if (scaled_font->status) {
    2932               0 :         cairo_matrix_init_identity (ctm);
    2933               0 :         return;
    2934                 :     }
    2935                 : 
    2936               0 :     *ctm = scaled_font->ctm;
    2937                 : }
    2938                 : slim_hidden_def (cairo_scaled_font_get_ctm);
    2939                 : 
    2940                 : /**
    2941                 :  * cairo_scaled_font_get_scale_matrix:
    2942                 :  * @scaled_font: a #cairo_scaled_font_t
    2943                 :  * @scale_matrix: return value for the matrix
    2944                 :  *
    2945                 :  * Stores the scale matrix of @scaled_font into @matrix.
    2946                 :  * The scale matrix is product of the font matrix and the ctm
    2947                 :  * associated with the scaled font, and hence is the matrix mapping from
    2948                 :  * font space to device space.
    2949                 :  *
    2950                 :  * Since: 1.8
    2951                 :  **/
    2952                 : void
    2953               0 : cairo_scaled_font_get_scale_matrix (cairo_scaled_font_t *scaled_font,
    2954                 :                                     cairo_matrix_t      *scale_matrix)
    2955                 : {
    2956               0 :     if (scaled_font->status) {
    2957               0 :         cairo_matrix_init_identity (scale_matrix);
    2958               0 :         return;
    2959                 :     }
    2960                 : 
    2961               0 :     *scale_matrix = scaled_font->scale;
    2962                 : }
    2963                 : 
    2964                 : /**
    2965                 :  * cairo_scaled_font_get_font_options:
    2966                 :  * @scaled_font: a #cairo_scaled_font_t
    2967                 :  * @options: return value for the font options
    2968                 :  *
    2969                 :  * Stores the font options with which @scaled_font was created into
    2970                 :  * @options.
    2971                 :  *
    2972                 :  * Since: 1.2
    2973                 :  **/
    2974                 : void
    2975               0 : cairo_scaled_font_get_font_options (cairo_scaled_font_t         *scaled_font,
    2976                 :                                     cairo_font_options_t        *options)
    2977                 : {
    2978               0 :     if (cairo_font_options_status (options))
    2979               0 :         return;
    2980                 : 
    2981               0 :     if (scaled_font->status) {
    2982               0 :         _cairo_font_options_init_default (options);
    2983               0 :         return;
    2984                 :     }
    2985                 : 
    2986               0 :     _cairo_font_options_init_copy (options, &scaled_font->options);
    2987                 : }
    2988                 : slim_hidden_def (cairo_scaled_font_get_font_options);

Generated by: LCOV version 1.7