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