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_unsafe)) { \
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
86/**
87 * hb_shape_plan_create: (Xconstructor)
88 * @face:
89 * @props:
90 * @user_features: (array length=num_user_features):
91 * @num_user_features:
92 * @shaper_list: (array zero-terminated=1):
93 *
94 *
95 *
96 * Return value: (transfer full):
97 *
98 * Since: 1.0
99 **/
100hb_shape_plan_t *
101hb_shape_plan_create (hb_face_t                     *face,
102		      const hb_segment_properties_t *props,
103		      const hb_feature_t            *user_features,
104		      unsigned int                   num_user_features,
105		      const char * const            *shaper_list)
106{
107  hb_shape_plan_t *shape_plan;
108  hb_feature_t *features = NULL;
109
110  if (unlikely (!face))
111    face = hb_face_get_empty ();
112  if (unlikely (!props || hb_object_is_inert (face)))
113    return hb_shape_plan_get_empty ();
114  if (num_user_features && !(features = (hb_feature_t *) malloc (num_user_features * sizeof (hb_feature_t))))
115    return hb_shape_plan_get_empty ();
116  if (!(shape_plan = hb_object_create<hb_shape_plan_t> ())) {
117    free (features);
118    return hb_shape_plan_get_empty ();
119  }
120
121  assert (props->direction != HB_DIRECTION_INVALID);
122
123  hb_face_make_immutable (face);
124  shape_plan->default_shaper_list = shaper_list == NULL;
125  shape_plan->face_unsafe = face;
126  shape_plan->props = *props;
127  shape_plan->num_user_features = num_user_features;
128  shape_plan->user_features = features;
129  if (num_user_features)
130    memcpy (features, user_features, num_user_features * sizeof (hb_feature_t));
131
132  hb_shape_plan_plan (shape_plan, user_features, num_user_features, shaper_list);
133
134  return shape_plan;
135}
136
137/**
138 * hb_shape_plan_get_empty:
139 *
140 *
141 *
142 * Return value: (transfer full):
143 *
144 * Since: 1.0
145 **/
146hb_shape_plan_t *
147hb_shape_plan_get_empty (void)
148{
149  static const hb_shape_plan_t _hb_shape_plan_nil = {
150    HB_OBJECT_HEADER_STATIC,
151
152    true, /* default_shaper_list */
153    NULL, /* face */
154    HB_SEGMENT_PROPERTIES_DEFAULT, /* props */
155
156    NULL, /* shaper_func */
157    NULL, /* shaper_name */
158
159    NULL, /* user_features */
160    0,    /* num_user_featurs */
161
162    {
163#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_INVALID,
164#include "hb-shaper-list.hh"
165#undef HB_SHAPER_IMPLEMENT
166    }
167  };
168
169  return const_cast<hb_shape_plan_t *> (&_hb_shape_plan_nil);
170}
171
172/**
173 * hb_shape_plan_reference: (skip)
174 * @shape_plan: a shape plan.
175 *
176 *
177 *
178 * Return value: (transfer full):
179 *
180 * Since: 1.0
181 **/
182hb_shape_plan_t *
183hb_shape_plan_reference (hb_shape_plan_t *shape_plan)
184{
185  return hb_object_reference (shape_plan);
186}
187
188/**
189 * hb_shape_plan_destroy: (skip)
190 * @shape_plan: a shape plan.
191 *
192 *
193 *
194 * Since: 1.0
195 **/
196void
197hb_shape_plan_destroy (hb_shape_plan_t *shape_plan)
198{
199  if (!hb_object_destroy (shape_plan)) return;
200
201#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_DESTROY(shaper, shape_plan);
202#include "hb-shaper-list.hh"
203#undef HB_SHAPER_IMPLEMENT
204
205  free (shape_plan->user_features);
206
207  free (shape_plan);
208}
209
210/**
211 * hb_shape_plan_set_user_data: (skip)
212 * @shape_plan: a shape plan.
213 * @key:
214 * @data:
215 * @destroy:
216 * @replace:
217 *
218 *
219 *
220 * Return value:
221 *
222 * Since: 1.0
223 **/
224hb_bool_t
225hb_shape_plan_set_user_data (hb_shape_plan_t    *shape_plan,
226			     hb_user_data_key_t *key,
227			     void *              data,
228			     hb_destroy_func_t   destroy,
229			     hb_bool_t           replace)
230{
231  return hb_object_set_user_data (shape_plan, key, data, destroy, replace);
232}
233
234/**
235 * hb_shape_plan_get_user_data: (skip)
236 * @shape_plan: a shape plan.
237 * @key:
238 *
239 *
240 *
241 * Return value: (transfer none):
242 *
243 * Since: 1.0
244 **/
245void *
246hb_shape_plan_get_user_data (hb_shape_plan_t    *shape_plan,
247			     hb_user_data_key_t *key)
248{
249  return hb_object_get_user_data (shape_plan, key);
250}
251
252
253/**
254 * hb_shape_plan_execute:
255 * @shape_plan: a shape plan.
256 * @font: a font.
257 * @buffer: a buffer.
258 * @features: (array length=num_features):
259 * @num_features:
260 *
261 *
262 *
263 * Return value:
264 *
265 * Since: 1.0
266 **/
267hb_bool_t
268hb_shape_plan_execute (hb_shape_plan_t    *shape_plan,
269		       hb_font_t          *font,
270		       hb_buffer_t        *buffer,
271		       const hb_feature_t *features,
272		       unsigned int        num_features)
273{
274  if (unlikely (hb_object_is_inert (shape_plan) ||
275		hb_object_is_inert (font) ||
276		hb_object_is_inert (buffer)))
277    return false;
278
279  assert (shape_plan->face_unsafe == font->face);
280  assert (hb_segment_properties_equal (&shape_plan->props, &buffer->props));
281
282#define HB_SHAPER_EXECUTE(shaper) \
283	HB_STMT_START { \
284	  return HB_SHAPER_DATA (shaper, shape_plan) && \
285		 hb_##shaper##_shaper_font_data_ensure (font) && \
286		 _hb_##shaper##_shape (shape_plan, font, buffer, features, num_features); \
287	} HB_STMT_END
288
289  if (0)
290    ;
291#define HB_SHAPER_IMPLEMENT(shaper) \
292  else if (shape_plan->shaper_func == _hb_##shaper##_shape) \
293    HB_SHAPER_EXECUTE (shaper);
294#include "hb-shaper-list.hh"
295#undef HB_SHAPER_IMPLEMENT
296
297#undef HB_SHAPER_EXECUTE
298
299  return false;
300}
301
302
303/*
304 * caching
305 */
306
307#if 0
308static unsigned int
309hb_shape_plan_hash (const hb_shape_plan_t *shape_plan)
310{
311  return hb_segment_properties_hash (&shape_plan->props) +
312	 shape_plan->default_shaper_list ? 0 : (intptr_t) shape_plan->shaper_func;
313}
314#endif
315
316/* User-feature caching is currently somewhat dumb:
317 * it only finds matches where the feature array is identical,
318 * not cases where the feature lists would be compatible for plan purposes
319 * but have different ranges, for example.
320 */
321struct hb_shape_plan_proposal_t
322{
323  const hb_segment_properties_t  props;
324  const char * const            *shaper_list;
325  const hb_feature_t            *user_features;
326  unsigned int                   num_user_features;
327  hb_shape_func_t               *shaper_func;
328};
329
330static inline hb_bool_t
331hb_shape_plan_user_features_match (const hb_shape_plan_t          *shape_plan,
332				   const hb_shape_plan_proposal_t *proposal)
333{
334  if (proposal->num_user_features != shape_plan->num_user_features) return false;
335  for (unsigned int i = 0, n = proposal->num_user_features; i < n; i++)
336    if (proposal->user_features[i].tag   != shape_plan->user_features[i].tag   ||
337        proposal->user_features[i].value != shape_plan->user_features[i].value ||
338        proposal->user_features[i].start != shape_plan->user_features[i].start ||
339        proposal->user_features[i].end   != shape_plan->user_features[i].end) return false;
340  return true;
341}
342
343static hb_bool_t
344hb_shape_plan_matches (const hb_shape_plan_t          *shape_plan,
345		       const hb_shape_plan_proposal_t *proposal)
346{
347  return hb_segment_properties_equal (&shape_plan->props, &proposal->props) &&
348	 hb_shape_plan_user_features_match (shape_plan, proposal) &&
349	 ((shape_plan->default_shaper_list && proposal->shaper_list == NULL) ||
350	  (shape_plan->shaper_func == proposal->shaper_func));
351}
352
353static inline hb_bool_t
354hb_non_global_user_features_present (const hb_feature_t *user_features,
355				     unsigned int        num_user_features)
356{
357  while (num_user_features)
358    if (user_features->start != 0 || user_features->end != (unsigned int) -1)
359      return true;
360    else
361      num_user_features--, user_features++;
362  return false;
363}
364
365/**
366 * hb_shape_plan_create_cached:
367 * @face:
368 * @props:
369 * @user_features: (array length=num_user_features):
370 * @num_user_features:
371 * @shaper_list: (array zero-terminated=1):
372 *
373 *
374 *
375 * Return value: (transfer full):
376 *
377 * Since: 1.0
378 **/
379hb_shape_plan_t *
380hb_shape_plan_create_cached (hb_face_t                     *face,
381			     const hb_segment_properties_t *props,
382			     const hb_feature_t            *user_features,
383			     unsigned int                   num_user_features,
384			     const char * const            *shaper_list)
385{
386  hb_shape_plan_proposal_t proposal = {
387    *props,
388    shaper_list,
389    user_features,
390    num_user_features,
391    NULL
392  };
393
394  if (shaper_list) {
395    /* Choose shaper.  Adapted from hb_shape_plan_plan(). */
396#define HB_SHAPER_PLAN(shaper) \
397	  HB_STMT_START { \
398	    if (hb_##shaper##_shaper_face_data_ensure (face)) \
399	      proposal.shaper_func = _hb_##shaper##_shape; \
400	  } HB_STMT_END
401
402    for (const char * const *shaper_item = shaper_list; *shaper_item; shaper_item++)
403      if (0)
404	;
405#define HB_SHAPER_IMPLEMENT(shaper) \
406      else if (0 == strcmp (*shaper_item, #shaper)) \
407	HB_SHAPER_PLAN (shaper);
408#include "hb-shaper-list.hh"
409#undef HB_SHAPER_IMPLEMENT
410
411#undef HB_SHAPER_PLAN
412
413    if (unlikely (!proposal.shaper_list))
414      return hb_shape_plan_get_empty ();
415  }
416
417
418retry:
419  hb_face_t::plan_node_t *cached_plan_nodes = (hb_face_t::plan_node_t *) hb_atomic_ptr_get (&face->shape_plans);
420  for (hb_face_t::plan_node_t *node = cached_plan_nodes; node; node = node->next)
421    if (hb_shape_plan_matches (node->shape_plan, &proposal))
422      return hb_shape_plan_reference (node->shape_plan);
423
424  /* Not found. */
425
426  hb_shape_plan_t *shape_plan = hb_shape_plan_create (face, props, user_features, num_user_features, shaper_list);
427
428  /* Don't add the plan to the cache if there were user features with non-global ranges */
429
430  if (hb_non_global_user_features_present (user_features, num_user_features))
431    return shape_plan;
432
433  hb_face_t::plan_node_t *node = (hb_face_t::plan_node_t *) calloc (1, sizeof (hb_face_t::plan_node_t));
434  if (unlikely (!node))
435    return shape_plan;
436
437  node->shape_plan = shape_plan;
438  node->next = cached_plan_nodes;
439
440  if (!hb_atomic_ptr_cmpexch (&face->shape_plans, cached_plan_nodes, node)) {
441    hb_shape_plan_destroy (shape_plan);
442    free (node);
443    goto retry;
444  }
445
446  return hb_shape_plan_reference (shape_plan);
447}
448
449/**
450 * hb_shape_plan_get_shaper:
451 * @shape_plan: a shape plan.
452 *
453 *
454 *
455 * Return value: (transfer none):
456 *
457 * Since: 1.0
458 **/
459const char *
460hb_shape_plan_get_shaper (hb_shape_plan_t *shape_plan)
461{
462  return shape_plan->shaper_name;
463}
464