hb-shape-plan.cc revision f30641038ba96e83950729b1bd9d86d2e98e46c5
1/*
2 * Copyright © 2012  Google, Inc.
3 *
4 *  This is part of HarfBuzz, a text shaping library.
5 *
6 * Permission is hereby granted, without written agreement and without
7 * license or royalty fees, to use, copy, modify, and distribute this
8 * software and its documentation for any purpose, provided that the
9 * above copyright notice and the following two paragraphs appear in
10 * all copies of this software.
11 *
12 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
13 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
14 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
15 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
16 * DAMAGE.
17 *
18 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
19 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20 * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
21 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
22 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
23 *
24 * Google Author(s): Behdad Esfahbod
25 */
26
27#include "hb-shape-plan-private.hh"
28#include "hb-shaper-private.hh"
29#include "hb-font-private.hh"
30
31#define HB_SHAPER_IMPLEMENT(shaper) \
32	HB_SHAPER_DATA_ENSURE_DECLARE(shaper, face) \
33	HB_SHAPER_DATA_ENSURE_DECLARE(shaper, font)
34#include "hb-shaper-list.hh"
35#undef HB_SHAPER_IMPLEMENT
36
37
38static void
39hb_shape_plan_plan (hb_shape_plan_t    *shape_plan,
40		    const hb_feature_t *user_features,
41		    unsigned int        num_user_features,
42		    const char * const *shaper_list)
43{
44  const hb_shaper_pair_t *shapers = _hb_shapers_get ();
45
46#define HB_SHAPER_PLAN(shaper) \
47	HB_STMT_START { \
48	  if (hb_##shaper##_shaper_face_data_ensure (shape_plan->face)) { \
49	    HB_SHAPER_DATA (shaper, shape_plan) = \
50	      HB_SHAPER_DATA_CREATE_FUNC (shaper, shape_plan) (shape_plan, user_features, num_user_features); \
51	    shape_plan->shaper_func = _hb_##shaper##_shape; \
52	    return; \
53	  } \
54	} HB_STMT_END
55
56  if (likely (!shaper_list)) {
57    for (unsigned int i = 0; i < HB_SHAPERS_COUNT; i++)
58      if (0)
59	;
60#define HB_SHAPER_IMPLEMENT(shaper) \
61      else if (shapers[i].func == _hb_##shaper##_shape) \
62	HB_SHAPER_PLAN (shaper);
63#include "hb-shaper-list.hh"
64#undef HB_SHAPER_IMPLEMENT
65  } else {
66    for (; *shaper_list; shaper_list++)
67      if (0)
68	;
69#define HB_SHAPER_IMPLEMENT(shaper) \
70      else if (0 == strcmp (*shaper_list, #shaper)) \
71	HB_SHAPER_PLAN (shaper);
72#include "hb-shaper-list.hh"
73#undef HB_SHAPER_IMPLEMENT
74  }
75
76#undef HB_SHAPER_PLAN
77}
78
79
80/*
81 * hb_shape_plan_t
82 */
83
84hb_shape_plan_t *
85hb_shape_plan_create (hb_face_t                     *face,
86		      const hb_segment_properties_t *props,
87		      const hb_feature_t            *user_features,
88		      unsigned int                   num_user_features,
89		      const char * const            *shaper_list)
90{
91  assert (props->direction != HB_DIRECTION_INVALID);
92
93  hb_shape_plan_t *shape_plan;
94
95  if (unlikely (!face))
96    face = hb_face_get_empty ();
97  if (unlikely (!props || hb_object_is_inert (face)))
98    return hb_shape_plan_get_empty ();
99  if (!(shape_plan = hb_object_create<hb_shape_plan_t> ()))
100    return hb_shape_plan_get_empty ();
101
102  hb_face_make_immutable (face);
103  shape_plan->default_shaper_list = shaper_list == NULL;
104  shape_plan->face = hb_face_reference (face);
105  shape_plan->props = *props;
106
107  hb_shape_plan_plan (shape_plan, user_features, num_user_features, shaper_list);
108
109  return shape_plan;
110}
111
112hb_shape_plan_t *
113hb_shape_plan_get_empty (void)
114{
115  static const hb_shape_plan_t _hb_shape_plan_nil = {
116    HB_OBJECT_HEADER_STATIC,
117
118    true, /* default_shaper_list */
119    NULL, /* face */
120    HB_SEGMENT_PROPERTIES_DEFAULT, /* props */
121
122    NULL, /* shaper_func */
123
124    {
125#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_INVALID,
126#include "hb-shaper-list.hh"
127#undef HB_SHAPER_IMPLEMENT
128    }
129  };
130
131  return const_cast<hb_shape_plan_t *> (&_hb_shape_plan_nil);
132}
133
134hb_shape_plan_t *
135hb_shape_plan_reference (hb_shape_plan_t *shape_plan)
136{
137  return hb_object_reference (shape_plan);
138}
139
140void
141hb_shape_plan_destroy (hb_shape_plan_t *shape_plan)
142{
143  if (!hb_object_destroy (shape_plan)) return;
144
145#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_DESTROY(shaper, shape_plan);
146#include "hb-shaper-list.hh"
147#undef HB_SHAPER_IMPLEMENT
148
149  hb_face_destroy (shape_plan->face);
150
151  free (shape_plan);
152}
153
154hb_bool_t
155hb_shape_plan_set_user_data (hb_shape_plan_t    *shape_plan,
156			     hb_user_data_key_t *key,
157			     void *              data,
158			     hb_destroy_func_t   destroy,
159			     hb_bool_t           replace)
160{
161  return hb_object_set_user_data (shape_plan, key, data, destroy, replace);
162}
163
164void *
165hb_shape_plan_get_user_data (hb_shape_plan_t    *shape_plan,
166			     hb_user_data_key_t *key)
167{
168  return hb_object_get_user_data (shape_plan, key);
169}
170
171
172hb_bool_t
173hb_shape_plan_execute (hb_shape_plan_t    *shape_plan,
174		       hb_font_t          *font,
175		       hb_buffer_t        *buffer,
176		       const hb_feature_t *features,
177		       unsigned int        num_features)
178{
179  if (unlikely (shape_plan->face != font->face))
180    return false;
181
182#define HB_SHAPER_EXECUTE(shaper) \
183	HB_STMT_START { \
184	  return HB_SHAPER_DATA (shaper, shape_plan) && \
185		 hb_##shaper##_shaper_font_data_ensure (font) && \
186		 _hb_##shaper##_shape (shape_plan, font, buffer, features, num_features); \
187	} HB_STMT_END
188
189  if (0)
190    ;
191#define HB_SHAPER_IMPLEMENT(shaper) \
192  else if (shape_plan->shaper_func == _hb_##shaper##_shape) \
193    HB_SHAPER_EXECUTE (shaper);
194#include "hb-shaper-list.hh"
195#undef HB_SHAPER_IMPLEMENT
196
197#undef HB_SHAPER_EXECUTE
198
199  return false;
200}
201
202
203/*
204 * caching
205 */
206
207#if 0
208static unsigned int
209hb_shape_plan_hash (const hb_shape_plan_t *shape_plan)
210{
211  return hb_segment_properties_hash (&shape_plan->props) +
212	 shape_plan->default_shaper_list ? 0 : (intptr_t) shape_plan->shaper_func;
213}
214#endif
215
216/* TODO no user-feature caching for now. */
217struct hb_shape_plan_proposal_t
218{
219  const hb_segment_properties_t  props;
220  const char * const            *shaper_list;
221  hb_shape_func_t               *shaper_func;
222};
223
224static hb_bool_t
225hb_shape_plan_matches (const hb_shape_plan_t          *shape_plan,
226		       const hb_shape_plan_proposal_t *proposal)
227{
228  return hb_segment_properties_equal (&shape_plan->props, &proposal->props) &&
229	 ((shape_plan->default_shaper_list && proposal->shaper_list == NULL) ||
230	  (shape_plan->shaper_func == proposal->shaper_func));
231}
232
233hb_shape_plan_t *
234hb_shape_plan_create_cached (hb_face_t                     *face,
235			     const hb_segment_properties_t *props,
236			     const hb_feature_t            *user_features,
237			     unsigned int                   num_user_features,
238			     const char * const            *shaper_list)
239{
240  if (num_user_features)
241    return hb_shape_plan_create (face, props, user_features, num_user_features, shaper_list);
242
243  hb_shape_plan_proposal_t proposal = {
244    *props,
245    shaper_list,
246    NULL
247  };
248
249  if (shaper_list) {
250    /* Choose shaper.  Adapted from hb_shape_plan_plan(). */
251#define HB_SHAPER_PLAN(shaper) \
252	  HB_STMT_START { \
253	    if (hb_##shaper##_shaper_face_data_ensure (face)) \
254	      proposal.shaper_func = _hb_##shaper##_shape; \
255	  } HB_STMT_END
256
257    for (const char * const *shaper_item = shaper_list; *shaper_item; shaper_item++)
258      if (0)
259	;
260#define HB_SHAPER_IMPLEMENT(shaper) \
261      else if (0 == strcmp (*shaper_item, #shaper)) \
262	HB_SHAPER_PLAN (shaper);
263#include "hb-shaper-list.hh"
264#undef HB_SHAPER_IMPLEMENT
265
266#undef HB_SHAPER_PLAN
267
268    if (unlikely (!proposal.shaper_list))
269      return hb_shape_plan_get_empty ();
270  }
271
272
273retry:
274  hb_face_t::plan_node_t *cached_plan_nodes = (hb_face_t::plan_node_t *) hb_atomic_ptr_get (&face->shape_plans);
275  for (hb_face_t::plan_node_t *node = cached_plan_nodes; node; node = node->next)
276    if (hb_shape_plan_matches (node->shape_plan, &proposal))
277      return hb_shape_plan_reference (node->shape_plan);
278
279  /* Not found. */
280
281  hb_shape_plan_t *shape_plan = hb_shape_plan_create (face, props, user_features, num_user_features, shaper_list);
282
283  hb_face_t::plan_node_t *node = (hb_face_t::plan_node_t *) calloc (1, sizeof (hb_face_t::plan_node_t));
284  if (unlikely (!node))
285    return shape_plan;
286
287  node->shape_plan = shape_plan;
288  node->next = cached_plan_nodes;
289
290  if (!hb_atomic_ptr_cmpexch (&face->shape_plans, cached_plan_nodes, node)) {
291    hb_shape_plan_destroy (shape_plan);
292    free (node);
293    goto retry;
294  }
295
296  /* Release our reference on face. */
297  hb_face_destroy (face);
298
299  return hb_shape_plan_reference (shape_plan);
300}
301