hb-shape-plan.cc revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
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#include "hb-buffer-private.hh"
31
32#define HB_SHAPER_IMPLEMENT(shaper) \
33	HB_SHAPER_DATA_ENSURE_DECLARE(shaper, face) \
34	HB_SHAPER_DATA_ENSURE_DECLARE(shaper, font)
35#include "hb-shaper-list.hh"
36#undef HB_SHAPER_IMPLEMENT
37
38
39static void
40hb_shape_plan_plan (hb_shape_plan_t    *shape_plan,
41		    const hb_feature_t *user_features,
42		    unsigned int        num_user_features,
43		    const char * const *shaper_list)
44{
45  const hb_shaper_pair_t *shapers = _hb_shapers_get ();
46
47#define HB_SHAPER_PLAN(shaper) \
48	HB_STMT_START { \
49	  if (hb_##shaper##_shaper_face_data_ensure (shape_plan->face)) { \
50	    HB_SHAPER_DATA (shaper, shape_plan) = \
51	      HB_SHAPER_DATA_CREATE_FUNC (shaper, shape_plan) (shape_plan, user_features, num_user_features); \
52	    shape_plan->shaper_func = _hb_##shaper##_shape; \
53	    shape_plan->shaper_name = #shaper; \
54	    return; \
55	  } \
56	} HB_STMT_END
57
58  if (likely (!shaper_list)) {
59    for (unsigned int i = 0; i < HB_SHAPERS_COUNT; i++)
60      if (0)
61	;
62#define HB_SHAPER_IMPLEMENT(shaper) \
63      else if (shapers[i].func == _hb_##shaper##_shape) \
64	HB_SHAPER_PLAN (shaper);
65#include "hb-shaper-list.hh"
66#undef HB_SHAPER_IMPLEMENT
67  } else {
68    for (; *shaper_list; shaper_list++)
69      if (0)
70	;
71#define HB_SHAPER_IMPLEMENT(shaper) \
72      else if (0 == strcmp (*shaper_list, #shaper)) \
73	HB_SHAPER_PLAN (shaper);
74#include "hb-shaper-list.hh"
75#undef HB_SHAPER_IMPLEMENT
76  }
77
78#undef HB_SHAPER_PLAN
79}
80
81
82/*
83 * hb_shape_plan_t
84 */
85
86hb_shape_plan_t *
87hb_shape_plan_create (hb_face_t                     *face,
88		      const hb_segment_properties_t *props,
89		      const hb_feature_t            *user_features,
90		      unsigned int                   num_user_features,
91		      const char * const            *shaper_list)
92{
93  assert (props->direction != HB_DIRECTION_INVALID);
94
95  hb_shape_plan_t *shape_plan;
96
97  if (unlikely (!face))
98    face = hb_face_get_empty ();
99  if (unlikely (!props || hb_object_is_inert (face)))
100    return hb_shape_plan_get_empty ();
101  if (!(shape_plan = hb_object_create<hb_shape_plan_t> ()))
102    return hb_shape_plan_get_empty ();
103
104  hb_face_make_immutable (face);
105  shape_plan->default_shaper_list = shaper_list == NULL;
106  shape_plan->face = hb_face_reference (face);
107  shape_plan->props = *props;
108
109  hb_shape_plan_plan (shape_plan, user_features, num_user_features, shaper_list);
110
111  return shape_plan;
112}
113
114hb_shape_plan_t *
115hb_shape_plan_get_empty (void)
116{
117  static const hb_shape_plan_t _hb_shape_plan_nil = {
118    HB_OBJECT_HEADER_STATIC,
119
120    true, /* default_shaper_list */
121    NULL, /* face */
122    HB_SEGMENT_PROPERTIES_DEFAULT, /* props */
123
124    NULL, /* shaper_func */
125    NULL, /* shaper_name */
126
127    {
128#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_INVALID,
129#include "hb-shaper-list.hh"
130#undef HB_SHAPER_IMPLEMENT
131    }
132  };
133
134  return const_cast<hb_shape_plan_t *> (&_hb_shape_plan_nil);
135}
136
137hb_shape_plan_t *
138hb_shape_plan_reference (hb_shape_plan_t *shape_plan)
139{
140  return hb_object_reference (shape_plan);
141}
142
143void
144hb_shape_plan_destroy (hb_shape_plan_t *shape_plan)
145{
146  if (!hb_object_destroy (shape_plan)) return;
147
148#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_DESTROY(shaper, shape_plan);
149#include "hb-shaper-list.hh"
150#undef HB_SHAPER_IMPLEMENT
151
152  hb_face_destroy (shape_plan->face);
153
154  free (shape_plan);
155}
156
157hb_bool_t
158hb_shape_plan_set_user_data (hb_shape_plan_t    *shape_plan,
159			     hb_user_data_key_t *key,
160			     void *              data,
161			     hb_destroy_func_t   destroy,
162			     hb_bool_t           replace)
163{
164  return hb_object_set_user_data (shape_plan, key, data, destroy, replace);
165}
166
167void *
168hb_shape_plan_get_user_data (hb_shape_plan_t    *shape_plan,
169			     hb_user_data_key_t *key)
170{
171  return hb_object_get_user_data (shape_plan, key);
172}
173
174
175hb_bool_t
176hb_shape_plan_execute (hb_shape_plan_t    *shape_plan,
177		       hb_font_t          *font,
178		       hb_buffer_t        *buffer,
179		       const hb_feature_t *features,
180		       unsigned int        num_features)
181{
182  if (unlikely (hb_object_is_inert (shape_plan) ||
183		hb_object_is_inert (font) ||
184		hb_object_is_inert (buffer)))
185    return false;
186
187  assert (shape_plan->face == font->face);
188  assert (hb_segment_properties_equal (&shape_plan->props, &buffer->props));
189
190#define HB_SHAPER_EXECUTE(shaper) \
191	HB_STMT_START { \
192	  return HB_SHAPER_DATA (shaper, shape_plan) && \
193		 hb_##shaper##_shaper_font_data_ensure (font) && \
194		 _hb_##shaper##_shape (shape_plan, font, buffer, features, num_features); \
195	} HB_STMT_END
196
197  if (0)
198    ;
199#define HB_SHAPER_IMPLEMENT(shaper) \
200  else if (shape_plan->shaper_func == _hb_##shaper##_shape) \
201    HB_SHAPER_EXECUTE (shaper);
202#include "hb-shaper-list.hh"
203#undef HB_SHAPER_IMPLEMENT
204
205#undef HB_SHAPER_EXECUTE
206
207  return false;
208}
209
210
211/*
212 * caching
213 */
214
215#if 0
216static unsigned int
217hb_shape_plan_hash (const hb_shape_plan_t *shape_plan)
218{
219  return hb_segment_properties_hash (&shape_plan->props) +
220	 shape_plan->default_shaper_list ? 0 : (intptr_t) shape_plan->shaper_func;
221}
222#endif
223
224/* TODO no user-feature caching for now. */
225struct hb_shape_plan_proposal_t
226{
227  const hb_segment_properties_t  props;
228  const char * const            *shaper_list;
229  hb_shape_func_t               *shaper_func;
230};
231
232static hb_bool_t
233hb_shape_plan_matches (const hb_shape_plan_t          *shape_plan,
234		       const hb_shape_plan_proposal_t *proposal)
235{
236  return hb_segment_properties_equal (&shape_plan->props, &proposal->props) &&
237	 ((shape_plan->default_shaper_list && proposal->shaper_list == NULL) ||
238	  (shape_plan->shaper_func == proposal->shaper_func));
239}
240
241hb_shape_plan_t *
242hb_shape_plan_create_cached (hb_face_t                     *face,
243			     const hb_segment_properties_t *props,
244			     const hb_feature_t            *user_features,
245			     unsigned int                   num_user_features,
246			     const char * const            *shaper_list)
247{
248  if (num_user_features)
249    return hb_shape_plan_create (face, props, user_features, num_user_features, shaper_list);
250
251  hb_shape_plan_proposal_t proposal = {
252    *props,
253    shaper_list,
254    NULL
255  };
256
257  if (shaper_list) {
258    /* Choose shaper.  Adapted from hb_shape_plan_plan(). */
259#define HB_SHAPER_PLAN(shaper) \
260	  HB_STMT_START { \
261	    if (hb_##shaper##_shaper_face_data_ensure (face)) \
262	      proposal.shaper_func = _hb_##shaper##_shape; \
263	  } HB_STMT_END
264
265    for (const char * const *shaper_item = shaper_list; *shaper_item; shaper_item++)
266      if (0)
267	;
268#define HB_SHAPER_IMPLEMENT(shaper) \
269      else if (0 == strcmp (*shaper_item, #shaper)) \
270	HB_SHAPER_PLAN (shaper);
271#include "hb-shaper-list.hh"
272#undef HB_SHAPER_IMPLEMENT
273
274#undef HB_SHAPER_PLAN
275
276    if (unlikely (!proposal.shaper_list))
277      return hb_shape_plan_get_empty ();
278  }
279
280
281retry:
282  hb_face_t::plan_node_t *cached_plan_nodes = (hb_face_t::plan_node_t *) hb_atomic_ptr_get (&face->shape_plans);
283  for (hb_face_t::plan_node_t *node = cached_plan_nodes; node; node = node->next)
284    if (hb_shape_plan_matches (node->shape_plan, &proposal))
285      return hb_shape_plan_reference (node->shape_plan);
286
287  /* Not found. */
288
289  hb_shape_plan_t *shape_plan = hb_shape_plan_create (face, props, user_features, num_user_features, shaper_list);
290
291  hb_face_t::plan_node_t *node = (hb_face_t::plan_node_t *) calloc (1, sizeof (hb_face_t::plan_node_t));
292  if (unlikely (!node))
293    return shape_plan;
294
295  node->shape_plan = shape_plan;
296  node->next = cached_plan_nodes;
297
298  if (!hb_atomic_ptr_cmpexch (&face->shape_plans, cached_plan_nodes, node)) {
299    hb_shape_plan_destroy (shape_plan);
300    free (node);
301    goto retry;
302  }
303
304  /* Release our reference on face. */
305  hb_face_destroy (face);
306
307  return hb_shape_plan_reference (shape_plan);
308}
309
310const char *
311hb_shape_plan_get_shaper (hb_shape_plan_t *shape_plan)
312{
313  return shape_plan->shaper_name;
314}
315