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