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