added MouseWheel event support for Silverlight 3.0
[moon.git] / cairo / src / cairo-scaled-font.c
blob3c5a95983331b137b9a86c6fbb4da6df54522b5d
1 /* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
2 /*
3 * Copyright © 2005 Keith Packard
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.
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 * You should have received a copy of the MPL along with this library
17 * in the file COPYING-MPL-1.1
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/
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.
28 * The Original Code is the cairo graphics library.
30 * The Initial Developer of the Original Code is Keith Packard
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>
40 #include "cairoint.h"
41 #include "cairo-scaled-font-private.h"
44 * Notes:
46 * To store rasterizations of glyphs, we use an image surface and the
47 * device offset to represent the glyph origin.
49 * A device_transform converts from device space (a conceptual space) to
50 * surface space. For simple cases of translation only, it's called a
51 * device_offset and is public API (cairo_surface_[gs]et_device_offset()).
52 * A possibly better name for those functions could have been
53 * cairo_surface_[gs]et_origin(). So, that's what they do: they set where
54 * the device-space origin (0,0) is in the surface. If the origin is inside
55 * the surface, device_offset values are positive. It may look like this:
57 * Device space:
58 * (-x,-y) <-- negative numbers
59 * +----------------+
60 * | . |
61 * | . |
62 * |......(0,0) <---|-- device-space origin
63 * | |
64 * | |
65 * +----------------+
66 * (width-x,height-y)
68 * Surface space:
69 * (0,0) <-- surface-space origin
70 * +---------------+
71 * | . |
72 * | . |
73 * |......(x,y) <--|-- device_offset
74 * | |
75 * | |
76 * +---------------+
77 * (width,height)
79 * In other words: device_offset is the coordinates of the device-space
80 * origin relative to the top-left of the surface.
82 * We use device offsets in a couple of places:
84 * - Public API: To let toolkits like Gtk+ give user a surface that
85 * only represents part of the final destination (say, the expose
86 * area), but has the same device space as the destination. In these
87 * cases device_offset is typically negative. Example:
89 * application window
90 * +---------------+
91 * | . |
92 * | (x,y). |
93 * |......+---+ |
94 * | | | <--|-- expose area
95 * | +---+ |
96 * +---------------+
98 * In this case, the user of cairo API can set the device_space on
99 * the expose area to (-x,-y) to move the device space origin to that
100 * of the application window, such that drawing in the expose area
101 * surface and painting it in the application window has the same
102 * effect as drawing in the application window directly. Gtk+ has
103 * been using this feature.
105 * - Glyph surfaces: In most font rendering systems, glyph surfaces
106 * have an origin at (0,0) and a bounding box that is typically
107 * represented as (x_bearing,y_bearing,width,height). Depending on
108 * which way y progresses in the system, y_bearing may typically be
109 * negative (for systems similar to cairo, with origin at top left),
110 * or be positive (in systems like PDF with origin at bottom left).
111 * No matter which is the case, it is important to note that
112 * (x_bearing,y_bearing) is the coordinates of top-left of the glyph
113 * relative to the glyph origin. That is, for example:
115 * Scaled-glyph space:
117 * (x_bearing,y_bearing) <-- negative numbers
118 * +----------------+
119 * | . |
120 * | . |
121 * |......(0,0) <---|-- glyph origin
122 * | |
123 * | |
124 * +----------------+
125 * (width+x_bearing,height+y_bearing)
127 * Note the similarity of the origin to the device space. That is
128 * exactly how we use the device_offset to represent scaled glyphs:
129 * to use the device-space origin as the glyph origin.
131 * Now compare the scaled-glyph space to device-space and surface-space
132 * and convince yourself that:
134 * (x_bearing,y_bearing) = (-x,-y) = - device_offset
136 * That's right. If you are not convinced yet, contrast the definition
137 * of the two:
139 * "(x_bearing,y_bearing) is the coordinates of top-left of the
140 * glyph relative to the glyph origin."
142 * "In other words: device_offset is the coordinates of the
143 * device-space origin relative to the top-left of the surface."
145 * and note that glyph origin = device-space origin.
148 static void
149 _cairo_scaled_font_fini_internal (cairo_scaled_font_t *scaled_font);
151 static cairo_bool_t
152 _cairo_scaled_glyph_keys_equal (const void *abstract_key_a, const void *abstract_key_b)
154 const cairo_scaled_glyph_t *key_a = abstract_key_a;
155 const cairo_scaled_glyph_t *key_b = abstract_key_b;
157 return (_cairo_scaled_glyph_index (key_a) ==
158 _cairo_scaled_glyph_index (key_b));
161 static void
162 _cairo_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph)
164 cairo_scaled_font_t *scaled_font = scaled_glyph->scaled_font;
165 const cairo_surface_backend_t *surface_backend = scaled_font->surface_backend;
167 if (surface_backend != NULL && surface_backend->scaled_glyph_fini != NULL)
168 surface_backend->scaled_glyph_fini (scaled_glyph, scaled_font);
169 if (scaled_glyph->surface != NULL)
170 cairo_surface_destroy (&scaled_glyph->surface->base);
171 if (scaled_glyph->path != NULL)
172 _cairo_path_fixed_destroy (scaled_glyph->path);
173 if (scaled_glyph->meta_surface != NULL)
174 cairo_surface_destroy (scaled_glyph->meta_surface);
177 static void
178 _cairo_scaled_glyph_destroy (void *abstract_glyph)
180 cairo_scaled_glyph_t *scaled_glyph = abstract_glyph;
181 _cairo_scaled_glyph_fini (scaled_glyph);
182 free (scaled_glyph);
185 #define ZOMBIE 0
186 static const cairo_scaled_font_t _cairo_scaled_font_nil = {
187 { ZOMBIE }, /* hash_entry */
188 CAIRO_STATUS_NO_MEMORY, /* status */
189 CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */
190 { 0, 0, 0, NULL }, /* user_data */
191 NULL, /* font_face */
192 { 1., 0., 0., 1., 0, 0}, /* font_matrix */
193 { 1., 0., 0., 1., 0, 0}, /* ctm */
194 { CAIRO_ANTIALIAS_DEFAULT, /* options */
195 CAIRO_SUBPIXEL_ORDER_DEFAULT,
196 CAIRO_HINT_STYLE_DEFAULT,
197 CAIRO_HINT_METRICS_DEFAULT} ,
198 FALSE, /* placeholder */
199 TRUE, /* finished */
200 { 1., 0., 0., 1., 0, 0}, /* scale */
201 { 1., 0., 0., 1., 0, 0}, /* scale_inverse */
202 1., /* max_scale */
203 { 0., 0., 0., 0., 0. }, /* extents */
204 CAIRO_MUTEX_NIL_INITIALIZER,/* mutex */
205 NULL, /* glyphs */
206 NULL, /* surface_backend */
207 NULL, /* surface_private */
208 CAIRO_SCALED_FONT_BACKEND_DEFAULT,
212 * _cairo_scaled_font_set_error:
213 * @scaled_font: a scaled_font
214 * @status: a status value indicating an error
216 * Atomically sets scaled_font->status to @status and calls _cairo_error;
217 * Does nothing if status is %CAIRO_STATUS_SUCCESS.
219 * All assignments of an error status to scaled_font->status should happen
220 * through _cairo_scaled_font_set_error(). Note that due to the nature of
221 * the atomic operation, it is not safe to call this function on the nil
222 * objects.
224 * The purpose of this function is to allow the user to set a
225 * breakpoint in _cairo_error() to generate a stack trace for when the
226 * user causes cairo to detect an error.
228 * Return value: the error status.
230 cairo_status_t
231 _cairo_scaled_font_set_error (cairo_scaled_font_t *scaled_font,
232 cairo_status_t status)
234 if (status == CAIRO_STATUS_SUCCESS)
235 return status;
237 /* Don't overwrite an existing error. This preserves the first
238 * error, which is the most significant. */
239 _cairo_status_set_error (&scaled_font->status, status);
241 return _cairo_error (status);
245 * cairo_scaled_font_get_type:
246 * @scaled_font: a #cairo_scaled_font_t
248 * This function returns the type of the backend used to create
249 * a scaled font. See #cairo_font_type_t for available types.
251 * Return value: The type of @scaled_font.
253 * Since: 1.2
255 cairo_font_type_t
256 cairo_scaled_font_get_type (cairo_scaled_font_t *scaled_font)
258 if (CAIRO_REFERENCE_COUNT_IS_INVALID (&scaled_font->ref_count))
259 return CAIRO_FONT_TYPE_TOY;
261 return scaled_font->backend->type;
265 * cairo_scaled_font_status:
266 * @scaled_font: a #cairo_scaled_font_t
268 * Checks whether an error has previously occurred for this
269 * scaled_font.
271 * Return value: %CAIRO_STATUS_SUCCESS or another error such as
272 * %CAIRO_STATUS_NO_MEMORY.
274 cairo_status_t
275 cairo_scaled_font_status (cairo_scaled_font_t *scaled_font)
277 return scaled_font->status;
279 slim_hidden_def (cairo_scaled_font_status);
281 /* Here we keep a unique mapping from
282 * font_face/matrix/ctm/font_options => #cairo_scaled_font_t.
284 * Here are the things that we want to map:
286 * a) All otherwise referenced #cairo_scaled_font_t's
287 * b) Some number of not otherwise referenced #cairo_scaled_font_t's
289 * The implementation uses a hash table which covers (a)
290 * completely. Then, for (b) we have an array of otherwise
291 * unreferenced fonts (holdovers) which are expired in
292 * least-recently-used order.
294 * The cairo_scaled_font_create() code gets to treat this like a regular
295 * hash table. All of the magic for the little holdover cache is in
296 * cairo_scaled_font_reference() and cairo_scaled_font_destroy().
299 /* This defines the size of the holdover array ... that is, the number
300 * of scaled fonts we keep around even when not otherwise referenced
302 #define CAIRO_SCALED_FONT_MAX_HOLDOVERS 256
304 typedef struct _cairo_scaled_font_map {
305 cairo_scaled_font_t *mru_scaled_font;
306 cairo_hash_table_t *hash_table;
307 cairo_scaled_font_t *holdovers[CAIRO_SCALED_FONT_MAX_HOLDOVERS];
308 int num_holdovers;
309 } cairo_scaled_font_map_t;
311 static cairo_scaled_font_map_t *cairo_scaled_font_map = NULL;
313 static int
314 _cairo_scaled_font_keys_equal (const void *abstract_key_a, const void *abstract_key_b);
316 static cairo_scaled_font_map_t *
317 _cairo_scaled_font_map_lock (void)
319 CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex);
321 if (cairo_scaled_font_map == NULL) {
322 cairo_scaled_font_map = malloc (sizeof (cairo_scaled_font_map_t));
323 if (cairo_scaled_font_map == NULL)
324 goto CLEANUP_MUTEX_LOCK;
326 cairo_scaled_font_map->mru_scaled_font = NULL;
327 cairo_scaled_font_map->hash_table =
328 _cairo_hash_table_create (_cairo_scaled_font_keys_equal);
330 if (cairo_scaled_font_map->hash_table == NULL)
331 goto CLEANUP_SCALED_FONT_MAP;
333 cairo_scaled_font_map->num_holdovers = 0;
336 return cairo_scaled_font_map;
338 CLEANUP_SCALED_FONT_MAP:
339 free (cairo_scaled_font_map);
340 cairo_scaled_font_map = NULL;
341 CLEANUP_MUTEX_LOCK:
342 CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex);
343 _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
344 return NULL;
347 static void
348 _cairo_scaled_font_map_unlock (void)
350 CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex);
353 void
354 _cairo_scaled_font_map_destroy (void)
356 cairo_scaled_font_map_t *font_map;
357 cairo_scaled_font_t *scaled_font;
359 CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex);
361 font_map = cairo_scaled_font_map;
362 if (font_map == NULL) {
363 goto CLEANUP_MUTEX_LOCK;
366 scaled_font = font_map->mru_scaled_font;
367 if (scaled_font != NULL) {
368 CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex);
369 cairo_scaled_font_destroy (scaled_font);
370 CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex);
373 /* remove scaled_fonts starting from the end so that font_map->holdovers
374 * is always in a consistent state when we release the mutex. */
375 while (font_map->num_holdovers) {
376 scaled_font = font_map->holdovers[font_map->num_holdovers-1];
377 assert (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&scaled_font->ref_count));
378 _cairo_hash_table_remove (font_map->hash_table,
379 &scaled_font->hash_entry);
381 font_map->num_holdovers--;
383 /* This releases the font_map lock to avoid the possibility of a
384 * recursive deadlock when the scaled font destroy closure gets
385 * called
387 _cairo_scaled_font_fini (scaled_font);
389 free (scaled_font);
392 _cairo_hash_table_destroy (font_map->hash_table);
394 free (cairo_scaled_font_map);
395 cairo_scaled_font_map = NULL;
397 CLEANUP_MUTEX_LOCK:
398 CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex);
402 /* If a scaled font wants to unlock the font map while still being
403 * created (needed for user-fonts), we need to take extra care not
404 * ending up with multiple identical scaled fonts being created.
406 * What we do is, we create a fake identical scaled font, and mark
407 * it as placeholder, lock its mutex, and insert that in the fontmap
408 * hash table. This makes other code trying to create an identical
409 * scaled font to just wait and retry.
411 * The reason we have to create a fake scaled font instead of just using
412 * scaled_font is for lifecycle management: we need to (or rather,
413 * other code needs to) reference the scaled_font in the hash table.
414 * We can't do that on the input scaled_font as it may be freed by
415 * font backend upon error.
418 cairo_status_t
419 _cairo_scaled_font_register_placeholder_and_unlock_font_map (cairo_scaled_font_t *scaled_font)
421 cairo_status_t status;
422 cairo_scaled_font_t *placeholder_scaled_font;
424 assert (CAIRO_MUTEX_IS_LOCKED (_cairo_scaled_font_map_mutex));
426 status = scaled_font->status;
427 if (status)
428 return status;
430 placeholder_scaled_font = malloc (sizeof (cairo_scaled_font_t));
431 if (placeholder_scaled_font == NULL)
432 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
434 /* full initialization is wasteful, but who cares... */
435 status = _cairo_scaled_font_init (placeholder_scaled_font,
436 scaled_font->font_face,
437 &scaled_font->font_matrix,
438 &scaled_font->ctm,
439 &scaled_font->options,
440 NULL);
441 if (status)
442 goto FREE_PLACEHOLDER;
444 placeholder_scaled_font->placeholder = TRUE;
446 status = _cairo_hash_table_insert (cairo_scaled_font_map->hash_table,
447 &placeholder_scaled_font->hash_entry);
448 if (status)
449 goto FINI_PLACEHOLDER;
451 CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex);
452 CAIRO_MUTEX_LOCK (placeholder_scaled_font->mutex);
454 return CAIRO_STATUS_SUCCESS;
456 FINI_PLACEHOLDER:
457 _cairo_scaled_font_fini_internal (placeholder_scaled_font);
458 FREE_PLACEHOLDER:
459 free (placeholder_scaled_font);
461 return _cairo_scaled_font_set_error (scaled_font, status);
464 void
465 _cairo_scaled_font_unregister_placeholder_and_lock_font_map (cairo_scaled_font_t *scaled_font)
467 cairo_scaled_font_t *placeholder_scaled_font;
468 cairo_bool_t found;
470 CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex);
472 found = _cairo_hash_table_lookup (cairo_scaled_font_map->hash_table,
473 &scaled_font->hash_entry,
474 (cairo_hash_entry_t**) &placeholder_scaled_font);
475 assert (found);
476 assert (placeholder_scaled_font->placeholder);
477 assert (CAIRO_MUTEX_IS_LOCKED (placeholder_scaled_font->mutex));
479 _cairo_hash_table_remove (cairo_scaled_font_map->hash_table,
480 &scaled_font->hash_entry);
482 CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex);
484 CAIRO_MUTEX_UNLOCK (placeholder_scaled_font->mutex);
485 cairo_scaled_font_destroy (placeholder_scaled_font);
487 CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex);
490 static void
491 _cairo_scaled_font_placeholder_wait_for_creation_to_finish (cairo_scaled_font_t *placeholder_scaled_font)
493 /* reference the place holder so it doesn't go away */
494 cairo_scaled_font_reference (placeholder_scaled_font);
496 /* now unlock the fontmap mutex so creation has a chance to finish */
497 CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex);
499 /* wait on placeholder mutex until we are awaken */
500 CAIRO_MUTEX_LOCK (placeholder_scaled_font->mutex);
502 /* ok, creation done. just clean up and back out */
503 CAIRO_MUTEX_UNLOCK (placeholder_scaled_font->mutex);
504 cairo_scaled_font_destroy (placeholder_scaled_font);
506 CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex);
509 /* Fowler / Noll / Vo (FNV) Hash (http://www.isthe.com/chongo/tech/comp/fnv/)
511 * Not necessarily better than a lot of other hashes, but should be OK, and
512 * well tested with binary data.
515 #define FNV_32_PRIME ((uint32_t)0x01000193)
516 #define FNV1_32_INIT ((uint32_t)0x811c9dc5)
518 static uint32_t
519 _hash_bytes_fnv (unsigned char *buffer,
520 int len,
521 uint32_t hval)
523 while (len--) {
524 hval *= FNV_32_PRIME;
525 hval ^= *buffer++;
528 return hval;
531 static void
532 _cairo_scaled_font_init_key (cairo_scaled_font_t *scaled_font,
533 cairo_font_face_t *font_face,
534 const cairo_matrix_t *font_matrix,
535 const cairo_matrix_t *ctm,
536 const cairo_font_options_t *options)
538 uint32_t hash = FNV1_32_INIT;
540 scaled_font->status = CAIRO_STATUS_SUCCESS;
541 scaled_font->placeholder = FALSE;
542 scaled_font->font_face = font_face;
543 scaled_font->font_matrix = *font_matrix;
544 scaled_font->ctm = *ctm;
545 /* ignore translation values in the ctm */
546 scaled_font->ctm.x0 = 0.;
547 scaled_font->ctm.y0 = 0.;
548 _cairo_font_options_init_copy (&scaled_font->options, options);
550 /* We do a bytewise hash on the font matrices */
551 hash = _hash_bytes_fnv ((unsigned char *)(&scaled_font->font_matrix.xx),
552 sizeof(cairo_matrix_t), hash);
553 hash = _hash_bytes_fnv ((unsigned char *)(&scaled_font->ctm.xx),
554 sizeof(cairo_matrix_t), hash);
556 hash ^= (unsigned long) scaled_font->font_face;
558 hash ^= cairo_font_options_hash (&scaled_font->options);
560 assert (hash != ZOMBIE);
561 scaled_font->hash_entry.hash = hash;
564 static cairo_bool_t
565 _cairo_scaled_font_keys_equal (const void *abstract_key_a, const void *abstract_key_b)
567 const cairo_scaled_font_t *key_a = abstract_key_a;
568 const cairo_scaled_font_t *key_b = abstract_key_b;
570 return (key_a->font_face == key_b->font_face &&
571 memcmp ((unsigned char *)(&key_a->font_matrix.xx),
572 (unsigned char *)(&key_b->font_matrix.xx),
573 sizeof(cairo_matrix_t)) == 0 &&
574 memcmp ((unsigned char *)(&key_a->ctm.xx),
575 (unsigned char *)(&key_b->ctm.xx),
576 sizeof(cairo_matrix_t)) == 0 &&
577 cairo_font_options_equal (&key_a->options, &key_b->options));
580 /* XXX: This 256 number is arbitrary---we've never done any measurement
581 * of this. In fact, having a per-font glyph caches each managed
582 * separately is probably not what we want anyway. Would probably be
583 * much better to have a single cache for glyphs with random
584 * replacement across all glyphs of all fonts. */
585 #define MAX_GLYPHS_CACHED_PER_FONT 256
588 * Basic #cairo_scaled_font_t object management
591 cairo_status_t
592 _cairo_scaled_font_init (cairo_scaled_font_t *scaled_font,
593 cairo_font_face_t *font_face,
594 const cairo_matrix_t *font_matrix,
595 const cairo_matrix_t *ctm,
596 const cairo_font_options_t *options,
597 const cairo_scaled_font_backend_t *backend)
599 cairo_status_t status;
601 status = cairo_font_options_status ((cairo_font_options_t *) options);
602 if (status)
603 return status;
605 _cairo_scaled_font_init_key (scaled_font, font_face,
606 font_matrix, ctm, options);
608 cairo_matrix_multiply (&scaled_font->scale,
609 &scaled_font->font_matrix,
610 &scaled_font->ctm);
612 scaled_font->max_scale = MAX (fabs (scaled_font->scale.xx) + fabs (scaled_font->scale.xy),
613 fabs (scaled_font->scale.yx) + fabs (scaled_font->scale.yy));
614 scaled_font->scale_inverse = scaled_font->scale;
615 status = cairo_matrix_invert (&scaled_font->scale_inverse);
616 if (status) {
617 /* If the font scale matrix is rank 0, just using an all-zero inverse matrix
618 * makes everything work correctly. This make font size 0 work without
619 * producing an error.
621 * FIXME: If the scale is rank 1, we still go into error mode. But then
622 * again, that's what we do everywhere in cairo.
624 * Also, the check for == 0. below may be too harsh...
626 if (scaled_font->scale.xx == 0. && scaled_font->scale.xy == 0. &&
627 scaled_font->scale.yx == 0. && scaled_font->scale.yy == 0.)
628 cairo_matrix_init (&scaled_font->scale_inverse,
629 0, 0, 0, 0,
630 -scaled_font->scale.x0,
631 -scaled_font->scale.y0);
632 else
633 return status;
636 scaled_font->finished = FALSE;
638 scaled_font->glyphs = _cairo_cache_create (_cairo_scaled_glyph_keys_equal,
639 _cairo_scaled_glyph_destroy,
640 MAX_GLYPHS_CACHED_PER_FONT);
641 if (scaled_font->glyphs == NULL)
642 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
644 CAIRO_REFERENCE_COUNT_INIT (&scaled_font->ref_count, 1);
646 _cairo_user_data_array_init (&scaled_font->user_data);
648 cairo_font_face_reference (font_face);
650 CAIRO_MUTEX_INIT (scaled_font->mutex);
652 scaled_font->surface_backend = NULL;
653 scaled_font->surface_private = NULL;
655 scaled_font->backend = backend;
657 return CAIRO_STATUS_SUCCESS;
660 void
661 _cairo_scaled_font_freeze_cache (cairo_scaled_font_t *scaled_font)
663 /* ensure we do not modify an error object */
664 assert (scaled_font->status == CAIRO_STATUS_SUCCESS);
666 CAIRO_MUTEX_LOCK (scaled_font->mutex);
667 _cairo_cache_freeze (scaled_font->glyphs);
670 void
671 _cairo_scaled_font_thaw_cache (cairo_scaled_font_t *scaled_font)
673 _cairo_cache_thaw (scaled_font->glyphs);
674 CAIRO_MUTEX_UNLOCK (scaled_font->mutex);
677 void
678 _cairo_scaled_font_reset_cache (cairo_scaled_font_t *scaled_font)
680 assert (CAIRO_MUTEX_IS_LOCKED (scaled_font->mutex));
682 _cairo_cache_destroy (scaled_font->glyphs);
683 scaled_font->glyphs = _cairo_cache_create (_cairo_scaled_glyph_keys_equal,
684 _cairo_scaled_glyph_destroy,
685 MAX_GLYPHS_CACHED_PER_FONT);
688 cairo_status_t
689 _cairo_scaled_font_set_metrics (cairo_scaled_font_t *scaled_font,
690 cairo_font_extents_t *fs_metrics)
692 cairo_status_t status;
693 double font_scale_x, font_scale_y;
695 status = _cairo_matrix_compute_basis_scale_factors (&scaled_font->font_matrix,
696 &font_scale_x, &font_scale_y,
698 if (status)
699 return status;
702 * The font responded in unscaled units, scale by the font
703 * matrix scale factors to get to user space
706 scaled_font->extents.ascent = fs_metrics->ascent * font_scale_y;
707 scaled_font->extents.descent = fs_metrics->descent * font_scale_y;
708 scaled_font->extents.height = fs_metrics->height * font_scale_y;
709 scaled_font->extents.max_x_advance = fs_metrics->max_x_advance * font_scale_x;
710 scaled_font->extents.max_y_advance = fs_metrics->max_y_advance * font_scale_y;
712 return CAIRO_STATUS_SUCCESS;
715 static void
716 _cairo_scaled_font_fini_internal (cairo_scaled_font_t *scaled_font)
718 scaled_font->finished = TRUE;
720 if (scaled_font->font_face != NULL)
721 cairo_font_face_destroy (scaled_font->font_face);
723 if (scaled_font->glyphs != NULL)
724 _cairo_cache_destroy (scaled_font->glyphs);
726 CAIRO_MUTEX_FINI (scaled_font->mutex);
728 if (scaled_font->surface_backend != NULL &&
729 scaled_font->surface_backend->scaled_font_fini != NULL)
730 scaled_font->surface_backend->scaled_font_fini (scaled_font);
732 if (scaled_font->backend != NULL && scaled_font->backend->fini != NULL)
733 scaled_font->backend->fini (scaled_font);
735 _cairo_user_data_array_fini (&scaled_font->user_data);
738 void
739 _cairo_scaled_font_fini (cairo_scaled_font_t *scaled_font)
741 /* Release the lock to avoid the possibility of a recursive
742 * deadlock when the scaled font destroy closure gets called. */
743 CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex);
744 _cairo_scaled_font_fini_internal (scaled_font);
745 CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex);
749 * cairo_scaled_font_create:
750 * @font_face: a #cairo_font_face_t
751 * @font_matrix: font space to user space transformation matrix for the
752 * font. In the simplest case of a N point font, this matrix is
753 * just a scale by N, but it can also be used to shear the font
754 * or stretch it unequally along the two axes. See
755 * cairo_set_font_matrix().
756 * @ctm: user to device transformation matrix with which the font will
757 * be used.
758 * @options: options to use when getting metrics for the font and
759 * rendering with it.
761 * Creates a #cairo_scaled_font_t object from a font face and matrices that
762 * describe the size of the font and the environment in which it will
763 * be used.
765 * Return value: a newly created #cairo_scaled_font_t. Destroy with
766 * cairo_scaled_font_destroy()
768 cairo_scaled_font_t *
769 cairo_scaled_font_create (cairo_font_face_t *font_face,
770 const cairo_matrix_t *font_matrix,
771 const cairo_matrix_t *ctm,
772 const cairo_font_options_t *options)
774 cairo_status_t status;
775 cairo_font_face_t *impl_face;
776 cairo_scaled_font_map_t *font_map;
777 cairo_scaled_font_t key, *old = NULL, *scaled_font = NULL;
779 if (font_face->status)
780 return _cairo_scaled_font_create_in_error (font_face->status);
782 status = cairo_font_options_status ((cairo_font_options_t *) options);
783 if (status)
784 return _cairo_scaled_font_create_in_error (status);
786 /* Note that degenerate ctm or font_matrix *are* allowed.
787 * We want to support a font size of 0. */
789 if (font_face->backend->get_implementation != NULL) {
790 /* indirect implementation, lookup the face that is used for the key */
791 status = font_face->backend->get_implementation (font_face, &impl_face);
792 if (status)
793 return _cairo_scaled_font_create_in_error (status);
794 } else
795 impl_face = font_face;
797 font_map = _cairo_scaled_font_map_lock ();
798 if (font_map == NULL)
799 return _cairo_scaled_font_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
801 _cairo_scaled_font_init_key (&key, impl_face,
802 font_matrix, ctm, options);
803 scaled_font = font_map->mru_scaled_font;
804 if (scaled_font != NULL &&
805 scaled_font->hash_entry.hash == key.hash_entry.hash &&
806 _cairo_scaled_font_keys_equal (scaled_font, &key))
808 assert (! scaled_font->placeholder);
810 if (scaled_font->status == CAIRO_STATUS_SUCCESS) {
811 /* We increment the reference count manually here, (rather
812 * than calling into cairo_scaled_font_reference), since we
813 * must modify the reference count while our lock is still
814 * held. */
815 _cairo_reference_count_inc (&scaled_font->ref_count);
816 _cairo_scaled_font_map_unlock ();
817 return scaled_font;
820 /* the font has been put into an error status - abandon the cache */
821 _cairo_hash_table_remove (font_map->hash_table, &key.hash_entry);
822 scaled_font->hash_entry.hash = ZOMBIE;
824 else
826 while (_cairo_hash_table_lookup (font_map->hash_table, &key.hash_entry,
827 (cairo_hash_entry_t**) &scaled_font))
829 if (! scaled_font->placeholder)
830 break;
832 /* If the scaled font is being created (happens for user-font),
833 * just wait until it's done, then retry */
834 _cairo_scaled_font_placeholder_wait_for_creation_to_finish (scaled_font);
837 /* Return existing scaled_font if it exists in the hash table. */
838 if (scaled_font != NULL) {
839 /* If the original reference count is 0, then this font must have
840 * been found in font_map->holdovers, (which means this caching is
841 * actually working). So now we remove it from the holdovers
842 * array. */
843 if (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&scaled_font->ref_count)) {
844 int i;
846 for (i = 0; i < font_map->num_holdovers; i++)
847 if (font_map->holdovers[i] == scaled_font)
848 break;
849 assert (i < font_map->num_holdovers);
851 font_map->num_holdovers--;
852 memmove (&font_map->holdovers[i],
853 &font_map->holdovers[i+1],
854 (font_map->num_holdovers - i) * sizeof (cairo_scaled_font_t*));
856 /* reset any error status */
857 scaled_font->status = CAIRO_STATUS_SUCCESS;
860 if (scaled_font->status == CAIRO_STATUS_SUCCESS) {
861 /* We increment the reference count manually here, (rather
862 * than calling into cairo_scaled_font_reference), since we
863 * must modify the reference count while our lock is still
864 * held. */
866 old = font_map->mru_scaled_font;
867 font_map->mru_scaled_font = scaled_font;
868 /* increment reference count for the mru cache */
869 _cairo_reference_count_inc (&scaled_font->ref_count);
870 /* and increment for the returned reference */
871 _cairo_reference_count_inc (&scaled_font->ref_count);
872 _cairo_scaled_font_map_unlock ();
874 cairo_scaled_font_destroy (old);
876 return scaled_font;
879 /* the font has been put into an error status - abandon the cache */
880 _cairo_hash_table_remove (font_map->hash_table, &key.hash_entry);
881 scaled_font->hash_entry.hash = ZOMBIE;
885 /* Otherwise create it and insert it into the hash table. */
886 status = font_face->backend->scaled_font_create (font_face, font_matrix,
887 ctm, options, &scaled_font);
888 if (status) {
889 _cairo_scaled_font_map_unlock ();
890 status = _cairo_font_face_set_error (font_face, status);
891 return _cairo_scaled_font_create_in_error (status);
894 status = _cairo_hash_table_insert (font_map->hash_table,
895 &scaled_font->hash_entry);
896 if (status == CAIRO_STATUS_SUCCESS) {
897 old = font_map->mru_scaled_font;
898 font_map->mru_scaled_font = scaled_font;
899 _cairo_reference_count_inc (&scaled_font->ref_count);
902 _cairo_scaled_font_map_unlock ();
904 if (status) {
905 /* We can't call _cairo_scaled_font_destroy here since it expects
906 * that the font has already been successfully inserted into the
907 * hash table. */
908 _cairo_scaled_font_fini_internal (scaled_font);
909 free (scaled_font);
910 return _cairo_scaled_font_create_in_error (status);
913 cairo_scaled_font_destroy (old);
915 return scaled_font;
917 slim_hidden_def (cairo_scaled_font_create);
919 static cairo_scaled_font_t *_cairo_scaled_font_nil_objects[CAIRO_STATUS_LAST_STATUS + 1];
921 /* XXX This should disappear in favour of a common pool of error objects. */
922 cairo_scaled_font_t *
923 _cairo_scaled_font_create_in_error (cairo_status_t status)
925 cairo_scaled_font_t *scaled_font;
927 assert (status != CAIRO_STATUS_SUCCESS);
929 if (status == CAIRO_STATUS_NO_MEMORY)
930 return (cairo_scaled_font_t *) &_cairo_scaled_font_nil;
932 CAIRO_MUTEX_LOCK (_cairo_scaled_font_error_mutex);
933 scaled_font = _cairo_scaled_font_nil_objects[status];
934 if (scaled_font == NULL) {
935 scaled_font = malloc (sizeof (cairo_scaled_font_t));
936 if (scaled_font == NULL) {
937 CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_error_mutex);
938 _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
939 return (cairo_scaled_font_t *) &_cairo_scaled_font_nil;
942 *scaled_font = _cairo_scaled_font_nil;
943 scaled_font->status = status;
944 _cairo_scaled_font_nil_objects[status] = scaled_font;
946 CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_error_mutex);
948 return scaled_font;
951 void
952 _cairo_scaled_font_reset_static_data (void)
954 int status;
956 CAIRO_MUTEX_LOCK (_cairo_scaled_font_error_mutex);
957 for (status = CAIRO_STATUS_SUCCESS;
958 status <= CAIRO_STATUS_LAST_STATUS;
959 status++)
961 if (_cairo_scaled_font_nil_objects[status] != NULL) {
962 free (_cairo_scaled_font_nil_objects[status]);
963 _cairo_scaled_font_nil_objects[status] = NULL;
966 CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_error_mutex);
970 * cairo_scaled_font_reference:
971 * @scaled_font: a #cairo_scaled_font_t, (may be %NULL in which case
972 * this function does nothing)
974 * Increases the reference count on @scaled_font by one. This prevents
975 * @scaled_font from being destroyed until a matching call to
976 * cairo_scaled_font_destroy() is made.
978 * The number of references to a #cairo_scaled_font_t can be get using
979 * cairo_scaled_font_get_reference_count().
981 * Returns: the referenced #cairo_scaled_font_t
983 cairo_scaled_font_t *
984 cairo_scaled_font_reference (cairo_scaled_font_t *scaled_font)
986 if (scaled_font == NULL ||
987 CAIRO_REFERENCE_COUNT_IS_INVALID (&scaled_font->ref_count))
988 return scaled_font;
990 assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&scaled_font->ref_count));
992 _cairo_reference_count_inc (&scaled_font->ref_count);
994 return scaled_font;
996 slim_hidden_def (cairo_scaled_font_reference);
999 * cairo_scaled_font_destroy:
1000 * @scaled_font: a #cairo_scaled_font_t
1002 * Decreases the reference count on @font by one. If the result
1003 * is zero, then @font and all associated resources are freed.
1004 * See cairo_scaled_font_reference().
1006 void
1007 cairo_scaled_font_destroy (cairo_scaled_font_t *scaled_font)
1009 cairo_scaled_font_t *lru = NULL;
1010 cairo_scaled_font_map_t *font_map;
1012 assert (CAIRO_MUTEX_IS_UNLOCKED (_cairo_scaled_font_map_mutex));
1014 if (scaled_font == NULL ||
1015 CAIRO_REFERENCE_COUNT_IS_INVALID (&scaled_font->ref_count))
1016 return;
1018 assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&scaled_font->ref_count));
1020 font_map = _cairo_scaled_font_map_lock ();
1021 assert (font_map != NULL);
1023 if (_cairo_reference_count_dec_and_test (&scaled_font->ref_count)) {
1026 if (!scaled_font->placeholder && scaled_font->hash_entry.hash != ZOMBIE) {
1027 /* Rather than immediately destroying this object, we put it into
1028 * the font_map->holdovers array in case it will get used again
1029 * soon (and is why we must hold the lock over the atomic op on
1030 * the reference count). To make room for it, we do actually
1031 * destroy the least-recently-used holdover.
1034 if (font_map->num_holdovers == CAIRO_SCALED_FONT_MAX_HOLDOVERS)
1036 lru = font_map->holdovers[0];
1037 assert (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&lru->ref_count));
1039 _cairo_hash_table_remove (font_map->hash_table, &lru->hash_entry);
1041 font_map->num_holdovers--;
1042 memmove (&font_map->holdovers[0],
1043 &font_map->holdovers[1],
1044 font_map->num_holdovers * sizeof (cairo_scaled_font_t*));
1047 font_map->holdovers[font_map->num_holdovers] = scaled_font;
1048 font_map->num_holdovers++;
1049 } else
1050 lru = scaled_font;
1054 _cairo_scaled_font_map_unlock ();
1056 /* If we pulled an item from the holdovers array, (while the font
1057 * map lock was held, of course), then there is no way that anyone
1058 * else could have acquired a reference to it. So we can now
1059 * safely call fini on it without any lock held. This is desirable
1060 * as we never want to call into any backend function with a lock
1061 * held. */
1062 if (lru) {
1063 _cairo_scaled_font_fini_internal (lru);
1064 free (lru);
1067 slim_hidden_def (cairo_scaled_font_destroy);
1070 * cairo_scaled_font_get_reference_count:
1071 * @scaled_font: a #cairo_scaled_font_t
1073 * Returns the current reference count of @scaled_font.
1075 * Return value: the current reference count of @scaled_font. If the
1076 * object is a nil object, 0 will be returned.
1078 * Since: 1.4
1080 unsigned int
1081 cairo_scaled_font_get_reference_count (cairo_scaled_font_t *scaled_font)
1083 if (scaled_font == NULL ||
1084 CAIRO_REFERENCE_COUNT_IS_INVALID (&scaled_font->ref_count))
1085 return 0;
1087 return CAIRO_REFERENCE_COUNT_GET_VALUE (&scaled_font->ref_count);
1091 * cairo_scaled_font_get_user_data:
1092 * @scaled_font: a #cairo_scaled_font_t
1093 * @key: the address of the #cairo_user_data_key_t the user data was
1094 * attached to
1096 * Return user data previously attached to @scaled_font using the
1097 * specified key. If no user data has been attached with the given
1098 * key this function returns %NULL.
1100 * Return value: the user data previously attached or %NULL.
1102 * Since: 1.4
1104 void *
1105 cairo_scaled_font_get_user_data (cairo_scaled_font_t *scaled_font,
1106 const cairo_user_data_key_t *key)
1108 return _cairo_user_data_array_get_data (&scaled_font->user_data,
1109 key);
1113 * cairo_scaled_font_set_user_data:
1114 * @scaled_font: a #cairo_scaled_font_t
1115 * @key: the address of a #cairo_user_data_key_t to attach the user data to
1116 * @user_data: the user data to attach to the #cairo_scaled_font_t
1117 * @destroy: a #cairo_destroy_func_t which will be called when the
1118 * #cairo_t is destroyed or when new user data is attached using the
1119 * same key.
1121 * Attach user data to @scaled_font. To remove user data from a surface,
1122 * call this function with the key that was used to set it and %NULL
1123 * for @data.
1125 * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a
1126 * slot could not be allocated for the user data.
1128 * Since: 1.4
1130 cairo_status_t
1131 cairo_scaled_font_set_user_data (cairo_scaled_font_t *scaled_font,
1132 const cairo_user_data_key_t *key,
1133 void *user_data,
1134 cairo_destroy_func_t destroy)
1136 if (CAIRO_REFERENCE_COUNT_IS_INVALID (&scaled_font->ref_count))
1137 return scaled_font->status;
1139 return _cairo_user_data_array_set_data (&scaled_font->user_data,
1140 key, user_data, destroy);
1143 static cairo_bool_t
1144 _cairo_scaled_font_is_frozen (cairo_scaled_font_t *scaled_font)
1146 return CAIRO_MUTEX_IS_LOCKED (scaled_font->mutex) &&
1147 scaled_font->glyphs->freeze_count > 0;
1150 /* Public font API follows. */
1153 * cairo_scaled_font_extents:
1154 * @scaled_font: a #cairo_scaled_font_t
1155 * @extents: a #cairo_font_extents_t which to store the retrieved extents.
1157 * Gets the metrics for a #cairo_scaled_font_t.
1159 void
1160 cairo_scaled_font_extents (cairo_scaled_font_t *scaled_font,
1161 cairo_font_extents_t *extents)
1163 if (scaled_font->status) {
1164 extents->ascent = 0.0;
1165 extents->descent = 0.0;
1166 extents->height = 0.0;
1167 extents->max_x_advance = 0.0;
1168 extents->max_y_advance = 0.0;
1169 return;
1172 *extents = scaled_font->extents;
1174 slim_hidden_def (cairo_scaled_font_extents);
1177 * cairo_scaled_font_text_extents:
1178 * @scaled_font: a #cairo_scaled_font_t
1179 * @utf8: a NUL-terminated string of text, encoded in UTF-8
1180 * @extents: a #cairo_text_extents_t which to store the retrieved extents.
1182 * Gets the extents for a string of text. The extents describe a
1183 * user-space rectangle that encloses the "inked" portion of the text
1184 * drawn at the origin (0,0) (as it would be drawn by cairo_show_text()
1185 * if the cairo graphics state were set to the same font_face,
1186 * font_matrix, ctm, and font_options as @scaled_font). Additionally,
1187 * the x_advance and y_advance values indicate the amount by which the
1188 * current point would be advanced by cairo_show_text().
1190 * Note that whitespace characters do not directly contribute to the
1191 * size of the rectangle (extents.width and extents.height). They do
1192 * contribute indirectly by changing the position of non-whitespace
1193 * characters. In particular, trailing whitespace characters are
1194 * likely to not affect the size of the rectangle, though they will
1195 * affect the x_advance and y_advance values.
1197 * Since: 1.2
1199 void
1200 cairo_scaled_font_text_extents (cairo_scaled_font_t *scaled_font,
1201 const char *utf8,
1202 cairo_text_extents_t *extents)
1204 cairo_status_t status;
1205 cairo_glyph_t *glyphs = NULL;
1206 int num_glyphs;
1208 if (scaled_font->status)
1209 goto ZERO_EXTENTS;
1211 if (utf8 == NULL)
1212 goto ZERO_EXTENTS;
1214 status = cairo_scaled_font_text_to_glyphs (scaled_font, 0., 0.,
1215 utf8, -1,
1216 &glyphs, &num_glyphs,
1217 NULL, NULL,
1218 NULL);
1219 if (status) {
1220 status = _cairo_scaled_font_set_error (scaled_font, status);
1221 goto ZERO_EXTENTS;
1224 cairo_scaled_font_glyph_extents (scaled_font, glyphs, num_glyphs, extents);
1225 free (glyphs);
1227 return;
1229 ZERO_EXTENTS:
1230 extents->x_bearing = 0.0;
1231 extents->y_bearing = 0.0;
1232 extents->width = 0.0;
1233 extents->height = 0.0;
1234 extents->x_advance = 0.0;
1235 extents->y_advance = 0.0;
1239 * cairo_scaled_font_glyph_extents:
1240 * @scaled_font: a #cairo_scaled_font_t
1241 * @glyphs: an array of glyph IDs with X and Y offsets.
1242 * @num_glyphs: the number of glyphs in the @glyphs array
1243 * @extents: a #cairo_text_extents_t which to store the retrieved extents.
1245 * Gets the extents for an array of glyphs. The extents describe a
1246 * user-space rectangle that encloses the "inked" portion of the
1247 * glyphs, (as they would be drawn by cairo_show_glyphs() if the cairo
1248 * graphics state were set to the same font_face, font_matrix, ctm,
1249 * and font_options as @scaled_font). Additionally, the x_advance and
1250 * y_advance values indicate the amount by which the current point
1251 * would be advanced by cairo_show_glyphs().
1253 * Note that whitespace glyphs do not contribute to the size of the
1254 * rectangle (extents.width and extents.height).
1256 void
1257 cairo_scaled_font_glyph_extents (cairo_scaled_font_t *scaled_font,
1258 const cairo_glyph_t *glyphs,
1259 int num_glyphs,
1260 cairo_text_extents_t *extents)
1262 cairo_status_t status;
1263 int i;
1264 double min_x = 0.0, min_y = 0.0, max_x = 0.0, max_y = 0.0;
1265 cairo_bool_t visible = FALSE;
1266 cairo_scaled_glyph_t *scaled_glyph = NULL;
1268 extents->x_bearing = 0.0;
1269 extents->y_bearing = 0.0;
1270 extents->width = 0.0;
1271 extents->height = 0.0;
1272 extents->x_advance = 0.0;
1273 extents->y_advance = 0.0;
1275 if (scaled_font->status)
1276 return;
1278 if (num_glyphs == 0)
1279 return;
1281 if (num_glyphs < 0) {
1282 _cairo_error_throw (CAIRO_STATUS_NEGATIVE_COUNT);
1283 /* XXX Can't propagate error */
1284 return;
1287 if (glyphs == NULL) {
1288 _cairo_error_throw (CAIRO_STATUS_NULL_POINTER);
1289 /* XXX Can't propagate error */
1290 return;
1293 _cairo_scaled_font_freeze_cache (scaled_font);
1295 for (i = 0; i < num_glyphs; i++) {
1296 double left, top, right, bottom;
1298 status = _cairo_scaled_glyph_lookup (scaled_font,
1299 glyphs[i].index,
1300 CAIRO_SCALED_GLYPH_INFO_METRICS,
1301 &scaled_glyph);
1302 if (status) {
1303 status = _cairo_scaled_font_set_error (scaled_font, status);
1304 goto UNLOCK;
1307 /* "Ink" extents should skip "invisible" glyphs */
1308 if (scaled_glyph->metrics.width == 0 || scaled_glyph->metrics.height == 0)
1309 continue;
1311 left = scaled_glyph->metrics.x_bearing + glyphs[i].x;
1312 right = left + scaled_glyph->metrics.width;
1313 top = scaled_glyph->metrics.y_bearing + glyphs[i].y;
1314 bottom = top + scaled_glyph->metrics.height;
1316 if (!visible) {
1317 visible = TRUE;
1318 min_x = left;
1319 max_x = right;
1320 min_y = top;
1321 max_y = bottom;
1322 } else {
1323 if (left < min_x) min_x = left;
1324 if (right > max_x) max_x = right;
1325 if (top < min_y) min_y = top;
1326 if (bottom > max_y) max_y = bottom;
1330 if (visible) {
1331 extents->x_bearing = min_x - glyphs[0].x;
1332 extents->y_bearing = min_y - glyphs[0].y;
1333 extents->width = max_x - min_x;
1334 extents->height = max_y - min_y;
1335 } else {
1336 extents->x_bearing = 0.0;
1337 extents->y_bearing = 0.0;
1338 extents->width = 0.0;
1339 extents->height = 0.0;
1342 if (num_glyphs) {
1343 double x0, y0, x1, y1;
1345 x0 = glyphs[0].x;
1346 y0 = glyphs[0].y;
1348 /* scaled_glyph contains the glyph for num_glyphs - 1 already. */
1349 x1 = glyphs[num_glyphs - 1].x + scaled_glyph->metrics.x_advance;
1350 y1 = glyphs[num_glyphs - 1].y + scaled_glyph->metrics.y_advance;
1352 extents->x_advance = x1 - x0;
1353 extents->y_advance = y1 - y0;
1354 } else {
1355 extents->x_advance = 0.0;
1356 extents->y_advance = 0.0;
1359 UNLOCK:
1360 _cairo_scaled_font_thaw_cache (scaled_font);
1362 slim_hidden_def (cairo_scaled_font_glyph_extents);
1365 * cairo_scaled_font_text_to_glyphs:
1366 * @x: X position to place first glyph
1367 * @y: Y position to place first glyph
1368 * @scaled_font: a #cairo_scaled_font_t
1369 * @utf8: a string of text encoded in UTF-8
1370 * @utf8_len: length of @utf8 in bytes, or -1 if it is NUL-terminated
1371 * @glyphs: pointer to array of glyphs to fill
1372 * @num_glyphs: pointer to number of glyphs
1373 * @clusters: pointer to array of cluster mapping information to fill, or %NULL
1374 * @num_clusters: pointer to number of clusters, or %NULL
1375 * @cluster_flags: pointer to location to store cluster flags corresponding to the
1376 * output @clusters, or %NULL
1378 * Converts UTF-8 text to an array of glyphs, optionally with cluster
1379 * mapping, that can be used to render later using @scaled_font.
1381 * If @glyphs initially points to a non-%NULL value, that array is used
1382 * as a glyph buffer, and @num_glyphs should point to the number of glyph
1383 * entries available there. If the provided glyph array is too short for
1384 * the conversion, a new glyph array is allocated using cairo_glyph_allocate()
1385 * and placed in @glyphs. Upon return, @num_glyphs always contains the
1386 * number of generated glyphs. If the value @glyphs points to has changed
1387 * after the call, the user is responsible for freeing the allocated glyph
1388 * array using cairo_glyph_free(). This may happen even if the provided
1389 * array was large enough.
1391 * If @clusters is not %NULL, @num_clusters and @cluster_flags should not be %NULL,
1392 * and cluster mapping will be computed.
1393 * The semantics of how cluster array allocation works is similar to the glyph
1394 * array. That is,
1395 * if @clusters initially points to a non-%NULL value, that array is used
1396 * as a cluster buffer, and @num_clusters should point to the number of cluster
1397 * entries available there. If the provided cluster array is too short for
1398 * the conversion, a new cluster array is allocated using cairo_text_cluster_allocate()
1399 * and placed in @clusters. Upon return, @num_clusters always contains the
1400 * number of generated clusters. If the value @clusters points at has changed
1401 * after the call, the user is responsible for freeing the allocated cluster
1402 * array using cairo_text_cluster_free(). This may happen even if the provided
1403 * array was large enough.
1405 * In the simplest case, @glyphs and @clusters can point to %NULL initially
1406 * and a suitable array will be allocated. In code:
1407 * <informalexample><programlisting>
1408 * cairo_status_t status;
1410 * cairo_glyph_t *glyphs = NULL;
1411 * int num_glyphs;
1412 * cairo_text_cluster_t *clusters = NULL;
1413 * int num_clusters;
1414 * cairo_text_cluster_flags_t cluster_flags;
1416 * status = cairo_scaled_font_text_to_glyphs (scaled_font,
1417 * x, y,
1418 * utf8, utf8_len,
1419 * &amp;glyphs, &amp;num_glyphs,
1420 * &amp;clusters, &amp;num_clusters, &amp;cluster_flags);
1422 * if (status == CAIRO_STATUS_SUCCESS) {
1423 * cairo_show_text_glyphs (cr,
1424 * utf8, utf8_len,
1425 * *glyphs, *num_glyphs,
1426 * *clusters, *num_clusters, *cluster_flags);
1428 * cairo_glyph_free (*glyphs);
1429 * cairo_text_cluster_free (*clusters);
1431 * </programlisting></informalexample>
1433 * If no cluster mapping is needed:
1434 * <informalexample><programlisting>
1435 * cairo_status_t status;
1437 * cairo_glyph_t *glyphs = NULL;
1438 * int num_glyphs;
1440 * status = cairo_scaled_font_text_to_glyphs (scaled_font,
1441 * x, y,
1442 * utf8, utf8_len,
1443 * &amp;glyphs, &amp;num_glyphs,
1444 * NULL, NULL,
1445 * NULL);
1447 * if (status == CAIRO_STATUS_SUCCESS) {
1448 * cairo_show_glyphs (cr, *glyphs, *num_glyphs);
1449 * cairo_glyph_free (*glyphs);
1451 * </programlisting></informalexample>
1453 * If stack-based glyph and cluster arrays are to be used for small
1454 * arrays:
1455 * <informalexample><programlisting>
1456 * cairo_status_t status;
1458 * cairo_glyph_t stack_glyphs[40];
1459 * cairo_glyph_t *glyphs = stack_glyphs;
1460 * int num_glyphs = sizeof (stack_glyphs) / sizeof (stack_glyphs[0]);
1461 * cairo_text_cluster_t stack_clusters[40];
1462 * cairo_text_cluster_t *clusters = stack_clusters;
1463 * int num_clusters = sizeof (stack_clusters) / sizeof (stack_clusters[0]);
1464 * cairo_text_cluster_flags_t cluster_flags;
1466 * status = cairo_scaled_font_text_to_glyphs (scaled_font,
1467 * x, y,
1468 * utf8, utf8_len,
1469 * &amp;glyphs, &amp;num_glyphs,
1470 * &amp;clusters, &amp;num_clusters, &amp;cluster_flags);
1472 * if (status == CAIRO_STATUS_SUCCESS) {
1473 * cairo_show_text_glyphs (cr,
1474 * utf8, utf8_len,
1475 * *glyphs, *num_glyphs,
1476 * *clusters, *num_clusters, *cluster_flags);
1478 * if (glyphs != stack_glyphs)
1479 * cairo_glyph_free (*glyphs);
1480 * if (clusters != stack_clusters)
1481 * cairo_text_cluster_free (*clusters);
1483 * </programlisting></informalexample>
1485 * For details of how @clusters, @num_clusters, and @cluster_flags map input
1486 * UTF-8 text to the output glyphs see cairo_show_text_glyphs().
1488 * The output values can be readily passed to cairo_show_text_glyphs()
1489 * cairo_show_glyphs(), or related functions, assuming that the exact
1490 * same @scaled_font is used for the operation.
1492 * Return value: %CAIRO_STATUS_SUCCESS upon success, or an error status
1493 * if the input values are wrong or if conversion failed. If the input
1494 * values are correct but the conversion failed, the error status is also
1495 * set on @scaled_font.
1497 * Since: 1.8
1499 cairo_status_t
1500 cairo_scaled_font_text_to_glyphs (cairo_scaled_font_t *scaled_font,
1501 double x,
1502 double y,
1503 const char *utf8,
1504 int utf8_len,
1505 cairo_glyph_t **glyphs,
1506 int *num_glyphs,
1507 cairo_text_cluster_t **clusters,
1508 int *num_clusters,
1509 cairo_text_cluster_flags_t *cluster_flags)
1511 int i;
1512 int num_chars = 0;
1513 const char *p;
1514 cairo_status_t status;
1515 cairo_glyph_t *orig_glyphs;
1516 cairo_text_cluster_t *orig_clusters;
1518 status = scaled_font->status;
1519 if (status)
1520 return status;
1522 /* A slew of sanity checks */
1524 /* glyphs and num_glyphs can't be NULL */
1525 if (glyphs == NULL ||
1526 num_glyphs == NULL) {
1527 status = CAIRO_STATUS_NULL_POINTER;
1528 goto BAIL;
1531 /* Special case for NULL and -1 */
1532 if (utf8 == NULL && utf8_len == -1)
1533 utf8_len = 0;
1535 /* No NULLs for non-NULLs! */
1536 if ((utf8_len && utf8 == NULL) ||
1537 (clusters && num_clusters == NULL) ||
1538 (clusters && cluster_flags == NULL)) {
1539 status = CAIRO_STATUS_NULL_POINTER;
1540 goto BAIL;
1543 /* A -1 for utf8_len means NUL-terminated */
1544 if (utf8_len == -1)
1545 utf8_len = strlen (utf8);
1547 /* A NULL *glyphs means no prealloced glyphs array */
1548 if (glyphs && *glyphs == NULL)
1549 *num_glyphs = 0;
1551 /* A NULL *clusters means no prealloced clusters array */
1552 if (clusters && *clusters == NULL)
1553 *num_clusters = 0;
1555 if (!clusters && num_clusters) {
1556 num_clusters = NULL;
1559 if (cluster_flags) {
1560 *cluster_flags = FALSE;
1563 if (!clusters && cluster_flags) {
1564 cluster_flags = NULL;
1567 /* Apart from that, no negatives */
1568 if (utf8_len < 0 ||
1569 *num_glyphs < 0 ||
1570 (num_clusters && *num_clusters < 0)) {
1571 status = CAIRO_STATUS_NEGATIVE_COUNT;
1572 goto BAIL;
1575 if (utf8_len == 0) {
1576 status = CAIRO_STATUS_SUCCESS;
1577 goto BAIL;
1580 /* validate input so backend does not have to */
1581 status = _cairo_utf8_to_ucs4 (utf8, utf8_len, NULL, &num_chars);
1582 if (status)
1583 goto BAIL;
1585 _cairo_scaled_font_freeze_cache (scaled_font);
1587 orig_glyphs = *glyphs;
1588 orig_clusters = clusters ? *clusters : NULL;
1590 if (scaled_font->backend->text_to_glyphs) {
1592 status = scaled_font->backend->text_to_glyphs (scaled_font, x, y,
1593 utf8, utf8_len,
1594 glyphs, num_glyphs,
1595 clusters, num_clusters,
1596 cluster_flags);
1598 if (status != CAIRO_INT_STATUS_UNSUPPORTED) {
1600 if (status == CAIRO_STATUS_SUCCESS) {
1602 /* The checks here are crude; we only should do them in
1603 * user-font backend, but they don't hurt here. This stuff
1604 * can be hard to get right. */
1606 if (*num_glyphs < 0) {
1607 status = CAIRO_STATUS_NEGATIVE_COUNT;
1608 goto DONE;
1610 if (num_glyphs && *glyphs == NULL) {
1611 status = CAIRO_STATUS_NULL_POINTER;
1612 goto DONE;
1615 if (clusters) {
1617 if (*num_clusters < 0) {
1618 status = CAIRO_STATUS_NEGATIVE_COUNT;
1619 goto DONE;
1621 if (num_clusters && *clusters == NULL) {
1622 status = CAIRO_STATUS_NULL_POINTER;
1623 goto DONE;
1626 /* Dont trust the backend, validate clusters! */
1627 status = _cairo_validate_text_clusters (utf8, utf8_len,
1628 *glyphs, *num_glyphs,
1629 *clusters, *num_clusters,
1630 *cluster_flags);
1634 goto DONE;
1638 if (*num_glyphs < num_chars) {
1639 *glyphs = cairo_glyph_allocate (num_chars);
1640 if (*glyphs == NULL) {
1641 status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
1642 goto DONE;
1645 *num_glyphs = num_chars;
1647 if (clusters) {
1648 if (*num_clusters < num_chars) {
1649 *clusters = cairo_text_cluster_allocate (num_chars);
1650 if (*clusters == NULL) {
1651 status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
1652 goto DONE;
1655 *num_clusters = num_chars;
1658 p = utf8;
1659 for (i = 0; i < num_chars; i++) {
1660 int num_bytes;
1661 uint32_t unicode;
1662 cairo_scaled_glyph_t *scaled_glyph;
1664 num_bytes = _cairo_utf8_get_char_validated (p, &unicode);
1665 p += num_bytes;
1667 (*glyphs)[i].index = (*scaled_font->backend->ucs4_to_index) (scaled_font, unicode);
1668 (*glyphs)[i].x = x;
1669 (*glyphs)[i].y = y;
1671 if (clusters) {
1672 (*clusters)[i].num_bytes = num_bytes;
1673 (*clusters)[i].num_glyphs = 1;
1676 status = _cairo_scaled_glyph_lookup (scaled_font,
1677 (*glyphs)[i].index,
1678 CAIRO_SCALED_GLYPH_INFO_METRICS,
1679 &scaled_glyph);
1680 if (status) {
1681 goto DONE;
1684 x += scaled_glyph->metrics.x_advance;
1685 y += scaled_glyph->metrics.y_advance;
1688 DONE: /* error that should be logged on scaled_font happened */
1689 _cairo_scaled_font_thaw_cache (scaled_font);
1691 if (status) {
1692 *num_glyphs = 0;
1693 if (*glyphs != orig_glyphs) {
1694 cairo_glyph_free (*glyphs);
1695 *glyphs = orig_glyphs;
1698 if (clusters) {
1699 *num_clusters = 0;
1700 if (*clusters != orig_clusters) {
1701 cairo_text_cluster_free (*clusters);
1702 *clusters = orig_clusters;
1707 return _cairo_scaled_font_set_error (scaled_font, status);
1709 BAIL: /* error with input arguments */
1711 if (num_glyphs)
1712 *num_glyphs = 0;
1714 if (num_clusters)
1715 *num_clusters = 0;
1717 return status;
1719 slim_hidden_def (cairo_scaled_font_text_to_glyphs);
1722 * Compute a device-space bounding box for the glyphs.
1724 cairo_status_t
1725 _cairo_scaled_font_glyph_device_extents (cairo_scaled_font_t *scaled_font,
1726 const cairo_glyph_t *glyphs,
1727 int num_glyphs,
1728 cairo_rectangle_int_t *extents)
1730 cairo_status_t status = CAIRO_STATUS_SUCCESS;
1731 int i;
1732 cairo_point_int_t min = { CAIRO_RECT_INT_MAX, CAIRO_RECT_INT_MAX };
1733 cairo_point_int_t max = { CAIRO_RECT_INT_MIN, CAIRO_RECT_INT_MIN };
1735 if (scaled_font->status)
1736 return scaled_font->status;
1738 _cairo_scaled_font_freeze_cache (scaled_font);
1740 for (i = 0; i < num_glyphs; i++) {
1741 cairo_scaled_glyph_t *scaled_glyph;
1742 int left, top;
1743 int right, bottom;
1744 int x, y;
1746 status = _cairo_scaled_glyph_lookup (scaled_font,
1747 glyphs[i].index,
1748 CAIRO_SCALED_GLYPH_INFO_METRICS,
1749 &scaled_glyph);
1750 if (status)
1751 break;
1753 /* XXX glyph images are snapped to pixel locations */
1754 x = _cairo_lround (glyphs[i].x);
1755 y = _cairo_lround (glyphs[i].y);
1757 left = x + _cairo_fixed_integer_floor(scaled_glyph->bbox.p1.x);
1758 top = y + _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.y);
1759 right = x + _cairo_fixed_integer_ceil(scaled_glyph->bbox.p2.x);
1760 bottom = y + _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.y);
1762 if (left < min.x) min.x = left;
1763 if (right > max.x) max.x = right;
1764 if (top < min.y) min.y = top;
1765 if (bottom > max.y) max.y = bottom;
1768 _cairo_scaled_font_thaw_cache (scaled_font);
1769 if (status)
1770 return _cairo_scaled_font_set_error (scaled_font, status);
1772 if (min.x < max.x && min.y < max.y) {
1773 extents->x = min.x;
1774 extents->width = max.x - min.x;
1775 extents->y = min.y;
1776 extents->height = max.y - min.y;
1777 } else {
1778 extents->x = extents->y = 0;
1779 extents->width = extents->height = 0;
1782 return CAIRO_STATUS_SUCCESS;
1785 cairo_status_t
1786 _cairo_scaled_font_show_glyphs (cairo_scaled_font_t *scaled_font,
1787 cairo_operator_t op,
1788 cairo_pattern_t *pattern,
1789 cairo_surface_t *surface,
1790 int source_x,
1791 int source_y,
1792 int dest_x,
1793 int dest_y,
1794 unsigned int width,
1795 unsigned int height,
1796 cairo_glyph_t *glyphs,
1797 int num_glyphs)
1799 cairo_status_t status;
1800 cairo_surface_t *mask = NULL;
1801 cairo_format_t mask_format = CAIRO_FORMAT_A1; /* shut gcc up */
1802 cairo_surface_pattern_t mask_pattern;
1803 cairo_solid_pattern_t white_pattern;
1804 int i;
1806 /* These operators aren't interpreted the same way by the backends;
1807 * they are implemented in terms of other operators in cairo-gstate.c
1809 assert (op != CAIRO_OPERATOR_SOURCE && op != CAIRO_OPERATOR_CLEAR);
1811 if (scaled_font->status)
1812 return scaled_font->status;
1814 if (!num_glyphs)
1815 return CAIRO_STATUS_SUCCESS;
1817 if (scaled_font->backend->show_glyphs != NULL) {
1818 int remaining_glyphs = num_glyphs;
1819 status = scaled_font->backend->show_glyphs (scaled_font,
1820 op, pattern,
1821 surface,
1822 source_x, source_y,
1823 dest_x, dest_y,
1824 width, height,
1825 glyphs, num_glyphs, &remaining_glyphs);
1826 glyphs += num_glyphs - remaining_glyphs;
1827 num_glyphs = remaining_glyphs;
1828 if (remaining_glyphs == 0)
1829 status = CAIRO_STATUS_SUCCESS;
1830 if (status != CAIRO_INT_STATUS_UNSUPPORTED)
1831 return _cairo_scaled_font_set_error (scaled_font, status);
1834 /* Font display routine either does not exist or failed. */
1836 _cairo_pattern_init_solid (&white_pattern, CAIRO_COLOR_WHITE, CAIRO_CONTENT_COLOR);
1838 _cairo_scaled_font_freeze_cache (scaled_font);
1840 for (i = 0; i < num_glyphs; i++) {
1841 int x, y;
1842 cairo_surface_pattern_t glyph_pattern;
1843 cairo_image_surface_t *glyph_surface;
1844 cairo_scaled_glyph_t *scaled_glyph;
1846 status = _cairo_scaled_glyph_lookup (scaled_font,
1847 glyphs[i].index,
1848 CAIRO_SCALED_GLYPH_INFO_SURFACE,
1849 &scaled_glyph);
1851 if (status)
1852 goto CLEANUP_MASK;
1854 glyph_surface = scaled_glyph->surface;
1856 /* To start, create the mask using the format from the first
1857 * glyph. Later we'll deal with different formats. */
1858 if (mask == NULL) {
1859 mask_format = glyph_surface->format;
1860 mask = cairo_image_surface_create (mask_format,
1861 width, height);
1862 if (mask->status) {
1863 status = mask->status;
1864 goto CLEANUP_MASK;
1868 /* If we have glyphs of different formats, we "upgrade" the mask
1869 * to the wider of the formats. */
1870 if (glyph_surface->format != mask_format &&
1871 _cairo_format_bits_per_pixel (mask_format) <
1872 _cairo_format_bits_per_pixel (glyph_surface->format) )
1874 cairo_surface_t *new_mask;
1875 cairo_surface_pattern_t mask_pattern;
1877 switch (glyph_surface->format) {
1878 case CAIRO_FORMAT_ARGB32:
1879 case CAIRO_FORMAT_A8:
1880 case CAIRO_FORMAT_A1:
1881 mask_format = glyph_surface->format;
1882 break;
1883 case CAIRO_FORMAT_RGB24:
1884 default:
1885 ASSERT_NOT_REACHED;
1886 mask_format = CAIRO_FORMAT_ARGB32;
1887 break;
1890 new_mask = cairo_image_surface_create (mask_format,
1891 width, height);
1892 if (new_mask->status) {
1893 status = new_mask->status;
1894 cairo_surface_destroy (new_mask);
1895 goto CLEANUP_MASK;
1898 _cairo_pattern_init_for_surface (&mask_pattern, mask);
1900 status = _cairo_surface_composite (CAIRO_OPERATOR_ADD,
1901 &white_pattern.base,
1902 &mask_pattern.base,
1903 new_mask,
1904 0, 0,
1905 0, 0,
1906 0, 0,
1907 width, height);
1909 _cairo_pattern_fini (&mask_pattern.base);
1911 if (status) {
1912 cairo_surface_destroy (new_mask);
1913 goto CLEANUP_MASK;
1916 cairo_surface_destroy (mask);
1917 mask = new_mask;
1920 /* round glyph locations to the nearest pixel */
1921 /* XXX: FRAGILE: We're ignoring device_transform scaling here. A bug? */
1922 x = _cairo_lround (glyphs[i].x - glyph_surface->base.device_transform.x0);
1923 y = _cairo_lround (glyphs[i].y - glyph_surface->base.device_transform.y0);
1925 _cairo_pattern_init_for_surface (&glyph_pattern, &glyph_surface->base);
1927 status = _cairo_surface_composite (CAIRO_OPERATOR_ADD,
1928 &white_pattern.base,
1929 &glyph_pattern.base,
1930 mask,
1931 0, 0,
1932 0, 0,
1933 x - dest_x, y - dest_y,
1934 glyph_surface->width,
1935 glyph_surface->height);
1937 _cairo_pattern_fini (&glyph_pattern.base);
1939 if (status)
1940 goto CLEANUP_MASK;
1943 if (mask_format == CAIRO_FORMAT_ARGB32)
1944 pixman_image_set_component_alpha (((cairo_image_surface_t*) mask)->
1945 pixman_image, TRUE);
1946 _cairo_pattern_init_for_surface (&mask_pattern, mask);
1948 status = _cairo_surface_composite (op, pattern, &mask_pattern.base,
1949 surface,
1950 source_x, source_y,
1951 0, 0,
1952 dest_x, dest_y,
1953 width, height);
1955 _cairo_pattern_fini (&mask_pattern.base);
1957 CLEANUP_MASK:
1958 _cairo_scaled_font_thaw_cache (scaled_font);
1960 _cairo_pattern_fini (&white_pattern.base);
1962 if (mask != NULL)
1963 cairo_surface_destroy (mask);
1964 return _cairo_scaled_font_set_error (scaled_font, status);
1967 typedef struct _cairo_scaled_glyph_path_closure {
1968 cairo_point_t offset;
1969 cairo_path_fixed_t *path;
1970 } cairo_scaled_glyph_path_closure_t;
1972 static cairo_status_t
1973 _scaled_glyph_path_move_to (void *abstract_closure, cairo_point_t *point)
1975 cairo_scaled_glyph_path_closure_t *closure = abstract_closure;
1977 return _cairo_path_fixed_move_to (closure->path,
1978 point->x + closure->offset.x,
1979 point->y + closure->offset.y);
1982 static cairo_status_t
1983 _scaled_glyph_path_line_to (void *abstract_closure, cairo_point_t *point)
1985 cairo_scaled_glyph_path_closure_t *closure = abstract_closure;
1987 return _cairo_path_fixed_line_to (closure->path,
1988 point->x + closure->offset.x,
1989 point->y + closure->offset.y);
1992 static cairo_status_t
1993 _scaled_glyph_path_curve_to (void *abstract_closure,
1994 cairo_point_t *p0,
1995 cairo_point_t *p1,
1996 cairo_point_t *p2)
1998 cairo_scaled_glyph_path_closure_t *closure = abstract_closure;
2000 return _cairo_path_fixed_curve_to (closure->path,
2001 p0->x + closure->offset.x,
2002 p0->y + closure->offset.y,
2003 p1->x + closure->offset.x,
2004 p1->y + closure->offset.y,
2005 p2->x + closure->offset.x,
2006 p2->y + closure->offset.y);
2009 static cairo_status_t
2010 _scaled_glyph_path_close_path (void *abstract_closure)
2012 cairo_scaled_glyph_path_closure_t *closure = abstract_closure;
2014 return _cairo_path_fixed_close_path (closure->path);
2017 /* Add a single-device-unit rectangle to a path. */
2018 static cairo_status_t
2019 _add_unit_rectangle_to_path (cairo_path_fixed_t *path, int x, int y)
2021 cairo_status_t status;
2023 status = _cairo_path_fixed_move_to (path,
2024 _cairo_fixed_from_int (x),
2025 _cairo_fixed_from_int (y));
2026 if (status)
2027 return status;
2029 status = _cairo_path_fixed_rel_line_to (path,
2030 _cairo_fixed_from_int (1),
2031 _cairo_fixed_from_int (0));
2032 if (status)
2033 return status;
2035 status = _cairo_path_fixed_rel_line_to (path,
2036 _cairo_fixed_from_int (0),
2037 _cairo_fixed_from_int (1));
2038 if (status)
2039 return status;
2041 status = _cairo_path_fixed_rel_line_to (path,
2042 _cairo_fixed_from_int (-1),
2043 _cairo_fixed_from_int (0));
2044 if (status)
2045 return status;
2047 status = _cairo_path_fixed_close_path (path);
2048 if (status)
2049 return status;
2051 return CAIRO_STATUS_SUCCESS;
2055 * _trace_mask_to_path:
2056 * @bitmap: An alpha mask (either %CAIRO_FORMAT_A1 or %CAIRO_FORMAT_A8)
2057 * @path: An initialized path to hold the result
2059 * Given a mask surface, (an alpha image), fill out the provided path
2060 * so that when filled it would result in something that approximates
2061 * the mask.
2063 * Note: The current tracing code here is extremely primitive. It
2064 * operates only on an A1 surface, (converting an A8 surface to A1 if
2065 * necessary), and performs the tracing by drawing a little square
2066 * around each pixel that is on in the mask. We do not pretend that
2067 * this is a high-quality result. But we are leaving it up to someone
2068 * who cares enough about getting a better result to implement
2069 * something more sophisticated.
2071 static cairo_status_t
2072 _trace_mask_to_path (cairo_image_surface_t *mask,
2073 cairo_path_fixed_t *path)
2075 cairo_status_t status;
2076 cairo_image_surface_t *a1_mask;
2077 uint8_t *row, *byte_ptr, byte;
2078 int rows, cols, bytes_per_row;
2079 int x, y, bit;
2080 double xoff, yoff;
2082 if (mask->format == CAIRO_FORMAT_A1)
2083 a1_mask = (cairo_image_surface_t *) cairo_surface_reference (&mask->base);
2084 else
2085 a1_mask = _cairo_image_surface_clone (mask, CAIRO_FORMAT_A1);
2087 status = cairo_surface_status (&a1_mask->base);
2088 if (status) {
2089 cairo_surface_destroy (&a1_mask->base);
2090 return status;
2093 cairo_surface_get_device_offset (&mask->base, &xoff, &yoff);
2095 bytes_per_row = (a1_mask->width + 7) / 8;
2096 for (y = 0, row = a1_mask->data, rows = a1_mask->height; rows; row += a1_mask->stride, rows--, y++) {
2097 for (x = 0, byte_ptr = row, cols = bytes_per_row; cols; byte_ptr++, cols--) {
2098 byte = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (*byte_ptr);
2099 for (bit = 7; bit >= 0 && x < a1_mask->width; bit--, x++) {
2100 if (byte & (1 << bit)) {
2101 status = _add_unit_rectangle_to_path (path,
2102 x - xoff, y - yoff);
2103 if (status)
2104 goto BAIL;
2110 BAIL:
2111 cairo_surface_destroy (&a1_mask->base);
2113 return status;
2116 cairo_status_t
2117 _cairo_scaled_font_glyph_path (cairo_scaled_font_t *scaled_font,
2118 const cairo_glyph_t *glyphs,
2119 int num_glyphs,
2120 cairo_path_fixed_t *path)
2122 cairo_status_t status;
2123 int i;
2124 cairo_scaled_glyph_path_closure_t closure;
2125 cairo_path_fixed_t *glyph_path;
2127 status = scaled_font->status;
2128 if (status)
2129 return status;
2131 closure.path = path;
2132 _cairo_scaled_font_freeze_cache (scaled_font);
2133 for (i = 0; i < num_glyphs; i++) {
2134 cairo_scaled_glyph_t *scaled_glyph;
2136 status = _cairo_scaled_glyph_lookup (scaled_font,
2137 glyphs[i].index,
2138 CAIRO_SCALED_GLYPH_INFO_PATH,
2139 &scaled_glyph);
2140 if (status == CAIRO_STATUS_SUCCESS)
2141 glyph_path = scaled_glyph->path;
2142 else if (status != CAIRO_INT_STATUS_UNSUPPORTED)
2143 goto BAIL;
2145 /* If the font is incapable of providing a path, then we'll
2146 * have to trace our own from a surface. */
2147 if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
2148 status = _cairo_scaled_glyph_lookup (scaled_font,
2149 glyphs[i].index,
2150 CAIRO_SCALED_GLYPH_INFO_SURFACE,
2151 &scaled_glyph);
2152 if (status)
2153 goto BAIL;
2155 glyph_path = _cairo_path_fixed_create ();
2156 if (glyph_path == NULL) {
2157 status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
2158 goto BAIL;
2161 status = _trace_mask_to_path (scaled_glyph->surface, glyph_path);
2162 if (status) {
2163 _cairo_path_fixed_destroy (glyph_path);
2164 goto BAIL;
2168 closure.offset.x = _cairo_fixed_from_double (glyphs[i].x);
2169 closure.offset.y = _cairo_fixed_from_double (glyphs[i].y);
2171 status = _cairo_path_fixed_interpret (glyph_path,
2172 CAIRO_DIRECTION_FORWARD,
2173 _scaled_glyph_path_move_to,
2174 _scaled_glyph_path_line_to,
2175 _scaled_glyph_path_curve_to,
2176 _scaled_glyph_path_close_path,
2177 &closure);
2178 if (glyph_path != scaled_glyph->path)
2179 _cairo_path_fixed_destroy (glyph_path);
2181 if (status)
2182 goto BAIL;
2184 BAIL:
2185 _cairo_scaled_font_thaw_cache (scaled_font);
2187 return _cairo_scaled_font_set_error (scaled_font, status);
2191 * _cairo_scaled_glyph_set_metrics:
2192 * @scaled_glyph: a #cairo_scaled_glyph_t
2193 * @scaled_font: a #cairo_scaled_font_t
2194 * @fs_metrics: a #cairo_text_extents_t in font space
2196 * _cairo_scaled_glyph_set_metrics() stores user space metrics
2197 * for the specified glyph given font space metrics. It is
2198 * called by the font backend when initializing a glyph with
2199 * %CAIRO_SCALED_GLYPH_INFO_METRICS.
2201 void
2202 _cairo_scaled_glyph_set_metrics (cairo_scaled_glyph_t *scaled_glyph,
2203 cairo_scaled_font_t *scaled_font,
2204 cairo_text_extents_t *fs_metrics)
2206 cairo_bool_t first = TRUE;
2207 double hm, wm;
2208 double min_user_x = 0.0, max_user_x = 0.0, min_user_y = 0.0, max_user_y = 0.0;
2209 double min_device_x = 0.0, max_device_x = 0.0, min_device_y = 0.0, max_device_y = 0.0;
2210 double device_x_advance, device_y_advance;
2212 for (hm = 0.0; hm <= 1.0; hm += 1.0)
2213 for (wm = 0.0; wm <= 1.0; wm += 1.0) {
2214 double x, y;
2216 /* Transform this corner to user space */
2217 x = fs_metrics->x_bearing + fs_metrics->width * wm;
2218 y = fs_metrics->y_bearing + fs_metrics->height * hm;
2219 cairo_matrix_transform_point (&scaled_font->font_matrix,
2220 &x, &y);
2221 if (first) {
2222 min_user_x = max_user_x = x;
2223 min_user_y = max_user_y = y;
2224 } else {
2225 if (x < min_user_x) min_user_x = x;
2226 if (x > max_user_x) max_user_x = x;
2227 if (y < min_user_y) min_user_y = y;
2228 if (y > max_user_y) max_user_y = y;
2231 /* Transform this corner to device space from glyph origin */
2232 x = fs_metrics->x_bearing + fs_metrics->width * wm;
2233 y = fs_metrics->y_bearing + fs_metrics->height * hm;
2234 cairo_matrix_transform_distance (&scaled_font->scale,
2235 &x, &y);
2237 if (first) {
2238 min_device_x = max_device_x = x;
2239 min_device_y = max_device_y = y;
2240 } else {
2241 if (x < min_device_x) min_device_x = x;
2242 if (x > max_device_x) max_device_x = x;
2243 if (y < min_device_y) min_device_y = y;
2244 if (y > max_device_y) max_device_y = y;
2246 first = FALSE;
2248 scaled_glyph->metrics.x_bearing = min_user_x;
2249 scaled_glyph->metrics.y_bearing = min_user_y;
2250 scaled_glyph->metrics.width = max_user_x - min_user_x;
2251 scaled_glyph->metrics.height = max_user_y - min_user_y;
2253 scaled_glyph->metrics.x_advance = fs_metrics->x_advance;
2254 scaled_glyph->metrics.y_advance = fs_metrics->y_advance;
2255 cairo_matrix_transform_distance (&scaled_font->font_matrix,
2256 &scaled_glyph->metrics.x_advance,
2257 &scaled_glyph->metrics.y_advance);
2259 device_x_advance = fs_metrics->x_advance;
2260 device_y_advance = fs_metrics->y_advance;
2261 cairo_matrix_transform_distance (&scaled_font->scale,
2262 &device_x_advance,
2263 &device_y_advance);
2265 scaled_glyph->bbox.p1.x = _cairo_fixed_from_double (min_device_x);
2266 scaled_glyph->bbox.p1.y = _cairo_fixed_from_double (min_device_y);
2267 scaled_glyph->bbox.p2.x = _cairo_fixed_from_double (max_device_x);
2268 scaled_glyph->bbox.p2.y = _cairo_fixed_from_double (max_device_y);
2270 scaled_glyph->x_advance = _cairo_lround (device_x_advance);
2271 scaled_glyph->y_advance = _cairo_lround (device_y_advance);
2274 void
2275 _cairo_scaled_glyph_set_surface (cairo_scaled_glyph_t *scaled_glyph,
2276 cairo_scaled_font_t *scaled_font,
2277 cairo_image_surface_t *surface)
2279 if (scaled_glyph->surface != NULL)
2280 cairo_surface_destroy (&scaled_glyph->surface->base);
2281 scaled_glyph->surface = surface;
2284 void
2285 _cairo_scaled_glyph_set_path (cairo_scaled_glyph_t *scaled_glyph,
2286 cairo_scaled_font_t *scaled_font,
2287 cairo_path_fixed_t *path)
2289 if (scaled_glyph->path != NULL)
2290 _cairo_path_fixed_destroy (scaled_glyph->path);
2291 scaled_glyph->path = path;
2294 void
2295 _cairo_scaled_glyph_set_meta_surface (cairo_scaled_glyph_t *scaled_glyph,
2296 cairo_scaled_font_t *scaled_font,
2297 cairo_surface_t *meta_surface)
2299 if (scaled_glyph->meta_surface != NULL)
2300 cairo_surface_destroy (meta_surface);
2301 scaled_glyph->meta_surface = meta_surface;
2305 * _cairo_scaled_glyph_lookup:
2306 * @scaled_font: a #cairo_scaled_font_t
2307 * @index: the glyph to create
2308 * @info: a #cairo_scaled_glyph_info_t marking which portions of
2309 * the glyph should be filled in.
2310 * @scaled_glyph_ret: a #cairo_scaled_glyph_t where the glyph
2311 * is returned.
2313 * If the desired info is not available, (for example, when trying to
2314 * get INFO_PATH with a bitmapped font), this function will return
2315 * %CAIRO_INT_STATUS_UNSUPPORTED.
2317 * Note: This function must be called with the scaled font frozen, and it must
2318 * remain frozen for as long as the @scaled_glyph_ret is alive. (If the scaled
2319 * font was not frozen, then there is no guarantee that the glyph would not be
2320 * evicted before you tried to access it.) See
2321 * _cairo_scaled_font_freeze_cache() and _cairo_scaled_font_thaw_cache().
2323 * Returns: a glyph with the requested portions filled in. Glyph
2324 * lookup is cached and glyph will be automatically freed along
2325 * with the scaled_font so no explicit free is required.
2326 * @info can be one or more of:
2327 * %CAIRO_SCALED_GLYPH_INFO_METRICS - glyph metrics and bounding box
2328 * %CAIRO_SCALED_GLYPH_INFO_SURFACE - surface holding glyph image
2329 * %CAIRO_SCALED_GLYPH_INFO_PATH - path holding glyph outline in device space
2331 cairo_int_status_t
2332 _cairo_scaled_glyph_lookup (cairo_scaled_font_t *scaled_font,
2333 unsigned long index,
2334 cairo_scaled_glyph_info_t info,
2335 cairo_scaled_glyph_t **scaled_glyph_ret)
2337 cairo_status_t status = CAIRO_STATUS_SUCCESS;
2338 cairo_cache_entry_t key;
2339 cairo_scaled_glyph_t *scaled_glyph;
2340 cairo_scaled_glyph_info_t need_info;
2342 if (scaled_font->status)
2343 return scaled_font->status;
2345 assert (_cairo_scaled_font_is_frozen (scaled_font));
2347 key.hash = index;
2349 * Check cache for glyph
2351 info |= CAIRO_SCALED_GLYPH_INFO_METRICS;
2352 if (!_cairo_cache_lookup (scaled_font->glyphs, &key,
2353 (cairo_cache_entry_t **) &scaled_glyph))
2356 * On miss, create glyph and insert into cache
2358 scaled_glyph = malloc (sizeof (cairo_scaled_glyph_t));
2359 if (scaled_glyph == NULL) {
2360 status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
2361 goto CLEANUP;
2364 _cairo_scaled_glyph_set_index(scaled_glyph, index);
2365 scaled_glyph->cache_entry.size = 1; /* We currently don't differentiate on glyph size at all */
2366 scaled_glyph->scaled_font = scaled_font;
2367 scaled_glyph->surface = NULL;
2368 scaled_glyph->path = NULL;
2369 scaled_glyph->meta_surface = NULL;
2370 scaled_glyph->surface_private = NULL;
2372 /* ask backend to initialize metrics and shape fields */
2373 status = (*scaled_font->backend->
2374 scaled_glyph_init) (scaled_font, scaled_glyph, info);
2375 if (status) {
2376 _cairo_scaled_glyph_destroy (scaled_glyph);
2377 goto CLEANUP;
2380 /* on success, the cache takes ownership of the scaled_glyph */
2381 status = _cairo_cache_insert (scaled_font->glyphs,
2382 &scaled_glyph->cache_entry);
2383 if (status) {
2384 _cairo_scaled_glyph_destroy (scaled_glyph);
2385 goto CLEANUP;
2389 * Check and see if the glyph, as provided,
2390 * already has the requested data and ammend it if not
2392 need_info = 0;
2393 if ((info & CAIRO_SCALED_GLYPH_INFO_SURFACE) != 0 &&
2394 scaled_glyph->surface == NULL)
2395 need_info |= CAIRO_SCALED_GLYPH_INFO_SURFACE;
2397 if (((info & CAIRO_SCALED_GLYPH_INFO_PATH) != 0 &&
2398 scaled_glyph->path == NULL))
2399 need_info |= CAIRO_SCALED_GLYPH_INFO_PATH;
2401 if (((info & CAIRO_SCALED_GLYPH_INFO_META_SURFACE) != 0 &&
2402 scaled_glyph->meta_surface == NULL))
2403 need_info |= CAIRO_SCALED_GLYPH_INFO_META_SURFACE;
2405 if (need_info) {
2406 status = (*scaled_font->backend->
2407 scaled_glyph_init) (scaled_font, scaled_glyph, need_info);
2408 if (status)
2409 goto CLEANUP;
2411 /* Don't trust the scaled_glyph_init() return value, the font
2412 * backend may not even know about some of the info. For example,
2413 * no backend other than the user-fonts knows about meta-surface
2414 * glyph info. */
2416 if ((info & CAIRO_SCALED_GLYPH_INFO_SURFACE) != 0 &&
2417 scaled_glyph->surface == NULL) {
2418 status = CAIRO_INT_STATUS_UNSUPPORTED;
2419 goto CLEANUP;
2422 if ((info & CAIRO_SCALED_GLYPH_INFO_PATH) != 0 &&
2423 scaled_glyph->path == NULL) {
2424 status = CAIRO_INT_STATUS_UNSUPPORTED;
2425 goto CLEANUP;
2428 if ((info & CAIRO_SCALED_GLYPH_INFO_META_SURFACE) != 0 &&
2429 scaled_glyph->meta_surface == NULL) {
2430 status = CAIRO_INT_STATUS_UNSUPPORTED;
2431 goto CLEANUP;
2435 CLEANUP:
2436 if (status) {
2437 /* It's not an error for the backend to not support the info we want. */
2438 if (status != CAIRO_INT_STATUS_UNSUPPORTED)
2439 status = _cairo_scaled_font_set_error (scaled_font, status);
2440 *scaled_glyph_ret = NULL;
2441 } else {
2442 *scaled_glyph_ret = scaled_glyph;
2445 return status;
2448 double
2449 _cairo_scaled_font_get_max_scale (cairo_scaled_font_t *scaled_font)
2451 return scaled_font->max_scale;
2456 * cairo_scaled_font_get_font_face:
2457 * @scaled_font: a #cairo_scaled_font_t
2459 * Gets the font face that this scaled font was created for.
2461 * Return value: The #cairo_font_face_t with which @scaled_font was
2462 * created.
2464 * Since: 1.2
2466 cairo_font_face_t *
2467 cairo_scaled_font_get_font_face (cairo_scaled_font_t *scaled_font)
2469 if (scaled_font->status)
2470 return (cairo_font_face_t*) &_cairo_font_face_nil;
2472 return scaled_font->font_face;
2474 slim_hidden_def (cairo_scaled_font_get_font_face);
2477 * cairo_scaled_font_get_font_matrix:
2478 * @scaled_font: a #cairo_scaled_font_t
2479 * @font_matrix: return value for the matrix
2481 * Stores the font matrix with which @scaled_font was created into
2482 * @matrix.
2484 * Since: 1.2
2486 void
2487 cairo_scaled_font_get_font_matrix (cairo_scaled_font_t *scaled_font,
2488 cairo_matrix_t *font_matrix)
2490 if (scaled_font->status) {
2491 cairo_matrix_init_identity (font_matrix);
2492 return;
2495 *font_matrix = scaled_font->font_matrix;
2497 slim_hidden_def (cairo_scaled_font_get_font_matrix);
2500 * cairo_scaled_font_get_ctm:
2501 * @scaled_font: a #cairo_scaled_font_t
2502 * @ctm: return value for the CTM
2504 * Stores the CTM with which @scaled_font was created into @ctm.
2506 * Since: 1.2
2508 void
2509 cairo_scaled_font_get_ctm (cairo_scaled_font_t *scaled_font,
2510 cairo_matrix_t *ctm)
2512 if (scaled_font->status) {
2513 cairo_matrix_init_identity (ctm);
2514 return;
2517 *ctm = scaled_font->ctm;
2519 slim_hidden_def (cairo_scaled_font_get_ctm);
2522 * cairo_scaled_font_get_scale_matrix:
2523 * @scaled_font: a #cairo_scaled_font_t
2524 * @scale_matrix: return value for the matrix
2526 * Stores the scale matrix of @scaled_font into @matrix.
2527 * The scale matrix is product of the font matrix and the ctm
2528 * associated with the scaled font, and hence is the matrix mapping from
2529 * font space to device space.
2531 * Since: 1.8
2533 void
2534 cairo_scaled_font_get_scale_matrix (cairo_scaled_font_t *scaled_font,
2535 cairo_matrix_t *scale_matrix)
2537 if (scaled_font->status) {
2538 cairo_matrix_init_identity (scale_matrix);
2539 return;
2542 *scale_matrix = scaled_font->scale;
2546 * cairo_scaled_font_get_font_options:
2547 * @scaled_font: a #cairo_scaled_font_t
2548 * @options: return value for the font options
2550 * Stores the font options with which @scaled_font was created into
2551 * @options.
2553 * Since: 1.2
2555 void
2556 cairo_scaled_font_get_font_options (cairo_scaled_font_t *scaled_font,
2557 cairo_font_options_t *options)
2559 if (cairo_font_options_status (options))
2560 return;
2562 if (scaled_font->status) {
2563 _cairo_font_options_init_default (options);
2564 return;
2567 _cairo_font_options_init_copy (options, &scaled_font->options);
2569 slim_hidden_def (cairo_scaled_font_get_font_options);