LCOV - app.info - gfx/cairo/cairo/src/cairo-scaled-font.c

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-07-07 | 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