Imported Upstream version 2.6.7
[platform/upstream/harfbuzz.git] / src / hb-shape-plan.cc
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.hh"
28 #include "hb-shape-plan.hh"
29 #include "hb-shaper.hh"
30 #include "hb-font.hh"
31 #include "hb-buffer.hh"
32
33
34 /**
35  * SECTION:hb-shape-plan
36  * @title: hb-shape-plan
37  * @short_description: Object representing a shaping plan
38  * @include: hb.h
39  *
40  * Shape plans are not used for shaping directly, but can be access to query
41  * certain information about how shaping will perform given a set of input
42  * parameters (script, language, direction, features, etc.)
43  * Most client would not need to deal with shape plans directly.
44  **/
45
46
47 /*
48  * hb_shape_plan_key_t
49  */
50
51 bool
52 hb_shape_plan_key_t::init (bool                           copy,
53                            hb_face_t                     *face,
54                            const hb_segment_properties_t *props,
55                            const hb_feature_t            *user_features,
56                            unsigned int                   num_user_features,
57                            const int                     *coords,
58                            unsigned int                   num_coords,
59                            const char * const            *shaper_list)
60 {
61   hb_feature_t *features = nullptr;
62   if (copy && num_user_features && !(features = (hb_feature_t *) calloc (num_user_features, sizeof (hb_feature_t))))
63     goto bail;
64
65   this->props = *props;
66   this->num_user_features = num_user_features;
67   this->user_features = copy ? features : user_features;
68   if (copy && num_user_features)
69   {
70     memcpy (features, user_features, num_user_features * sizeof (hb_feature_t));
71     /* Make start/end uniform to easier catch bugs. */
72     for (unsigned int i = 0; i < num_user_features; i++)
73     {
74       if (features[0].start != HB_FEATURE_GLOBAL_START)
75         features[0].start = 1;
76       if (features[0].end   != HB_FEATURE_GLOBAL_END)
77         features[0].end   = 2;
78     }
79   }
80   this->shaper_func = nullptr;
81   this->shaper_name = nullptr;
82 #ifndef HB_NO_OT_SHAPE
83   this->ot.init (face, coords, num_coords);
84 #endif
85
86   /*
87    * Choose shaper.
88    */
89
90 #define HB_SHAPER_PLAN(shaper) \
91         HB_STMT_START { \
92           if (face->data.shaper) \
93           { \
94             this->shaper_func = _hb_##shaper##_shape; \
95             this->shaper_name = #shaper; \
96             return true; \
97           } \
98         } HB_STMT_END
99
100   if (unlikely (shaper_list))
101   {
102     for (; *shaper_list; shaper_list++)
103       if (false)
104         ;
105 #define HB_SHAPER_IMPLEMENT(shaper) \
106       else if (0 == strcmp (*shaper_list, #shaper)) \
107         HB_SHAPER_PLAN (shaper);
108 #include "hb-shaper-list.hh"
109 #undef HB_SHAPER_IMPLEMENT
110   }
111   else
112   {
113     const hb_shaper_entry_t *shapers = _hb_shapers_get ();
114     for (unsigned int i = 0; i < HB_SHAPERS_COUNT; i++)
115       if (false)
116         ;
117 #define HB_SHAPER_IMPLEMENT(shaper) \
118       else if (shapers[i].func == _hb_##shaper##_shape) \
119         HB_SHAPER_PLAN (shaper);
120 #include "hb-shaper-list.hh"
121 #undef HB_SHAPER_IMPLEMENT
122   }
123 #undef HB_SHAPER_PLAN
124
125 bail:
126   ::free (features);
127   return false;
128 }
129
130 bool
131 hb_shape_plan_key_t::user_features_match (const hb_shape_plan_key_t *other)
132 {
133   if (this->num_user_features != other->num_user_features)
134     return false;
135   for (unsigned int i = 0; i < num_user_features; i++)
136   {
137     if (this->user_features[i].tag   != other->user_features[i].tag   ||
138         this->user_features[i].value != other->user_features[i].value ||
139         (this->user_features[i].start == HB_FEATURE_GLOBAL_START &&
140          this->user_features[i].end   == HB_FEATURE_GLOBAL_END) !=
141         (other->user_features[i].start == HB_FEATURE_GLOBAL_START &&
142          other->user_features[i].end   == HB_FEATURE_GLOBAL_END))
143       return false;
144   }
145   return true;
146 }
147
148 bool
149 hb_shape_plan_key_t::equal (const hb_shape_plan_key_t *other)
150 {
151   return hb_segment_properties_equal (&this->props, &other->props) &&
152          this->user_features_match (other) &&
153 #ifndef HB_NO_OT_SHAPE
154          this->ot.equal (&other->ot) &&
155 #endif
156          this->shaper_func == other->shaper_func;
157 }
158
159
160 /*
161  * hb_shape_plan_t
162  */
163
164
165 /**
166  * hb_shape_plan_create: (Xconstructor)
167  * @face:
168  * @props:
169  * @user_features: (array length=num_user_features):
170  * @num_user_features:
171  * @shaper_list: (array zero-terminated=1):
172  *
173  *
174  *
175  * Return value: (transfer full):
176  *
177  * Since: 0.9.7
178  **/
179 hb_shape_plan_t *
180 hb_shape_plan_create (hb_face_t                     *face,
181                       const hb_segment_properties_t *props,
182                       const hb_feature_t            *user_features,
183                       unsigned int                   num_user_features,
184                       const char * const            *shaper_list)
185 {
186   return hb_shape_plan_create2 (face, props,
187                                 user_features, num_user_features,
188                                 nullptr, 0,
189                                 shaper_list);
190 }
191
192 hb_shape_plan_t *
193 hb_shape_plan_create2 (hb_face_t                     *face,
194                        const hb_segment_properties_t *props,
195                        const hb_feature_t            *user_features,
196                        unsigned int                   num_user_features,
197                        const int                     *coords,
198                        unsigned int                   num_coords,
199                        const char * const            *shaper_list)
200 {
201   DEBUG_MSG_FUNC (SHAPE_PLAN, nullptr,
202                   "face=%p num_features=%d num_coords=%d shaper_list=%p",
203                   face,
204                   num_user_features,
205                   num_coords,
206                   shaper_list);
207
208   assert (props->direction != HB_DIRECTION_INVALID);
209
210   hb_shape_plan_t *shape_plan;
211
212   if (unlikely (!props))
213     goto bail;
214   if (!(shape_plan = hb_object_create<hb_shape_plan_t> ()))
215     goto bail;
216
217   if (unlikely (!face))
218     face = hb_face_get_empty ();
219   hb_face_make_immutable (face);
220   shape_plan->face_unsafe = face;
221
222   if (unlikely (!shape_plan->key.init (true,
223                                        face,
224                                        props,
225                                        user_features,
226                                        num_user_features,
227                                        coords,
228                                        num_coords,
229                                        shaper_list)))
230     goto bail2;
231 #ifndef HB_NO_OT_SHAPE
232   if (unlikely (!shape_plan->ot.init0 (face, &shape_plan->key)))
233     goto bail3;
234 #endif
235
236   return shape_plan;
237
238 #ifndef HB_NO_OT_SHAPE
239 bail3:
240 #endif
241   shape_plan->key.free ();
242 bail2:
243   free (shape_plan);
244 bail:
245   return hb_shape_plan_get_empty ();
246 }
247
248 /**
249  * hb_shape_plan_get_empty:
250  *
251  *
252  *
253  * Return value: (transfer full):
254  *
255  * Since: 0.9.7
256  **/
257 hb_shape_plan_t *
258 hb_shape_plan_get_empty ()
259 {
260   return const_cast<hb_shape_plan_t *> (&Null (hb_shape_plan_t));
261 }
262
263 /**
264  * hb_shape_plan_reference: (skip)
265  * @shape_plan: a shape plan.
266  *
267  *
268  *
269  * Return value: (transfer full):
270  *
271  * Since: 0.9.7
272  **/
273 hb_shape_plan_t *
274 hb_shape_plan_reference (hb_shape_plan_t *shape_plan)
275 {
276   return hb_object_reference (shape_plan);
277 }
278
279 /**
280  * hb_shape_plan_destroy: (skip)
281  * @shape_plan: a shape plan.
282  *
283  *
284  *
285  * Since: 0.9.7
286  **/
287 void
288 hb_shape_plan_destroy (hb_shape_plan_t *shape_plan)
289 {
290   if (!hb_object_destroy (shape_plan)) return;
291
292 #ifndef HB_NO_OT_SHAPE
293   shape_plan->ot.fini ();
294 #endif
295   shape_plan->key.free ();
296   free (shape_plan);
297 }
298
299 /**
300  * hb_shape_plan_set_user_data: (skip)
301  * @shape_plan: a shape plan.
302  * @key:
303  * @data:
304  * @destroy:
305  * @replace:
306  *
307  *
308  *
309  * Return value:
310  *
311  * Since: 0.9.7
312  **/
313 hb_bool_t
314 hb_shape_plan_set_user_data (hb_shape_plan_t    *shape_plan,
315                              hb_user_data_key_t *key,
316                              void *              data,
317                              hb_destroy_func_t   destroy,
318                              hb_bool_t           replace)
319 {
320   return hb_object_set_user_data (shape_plan, key, data, destroy, replace);
321 }
322
323 /**
324  * hb_shape_plan_get_user_data: (skip)
325  * @shape_plan: a shape plan.
326  * @key:
327  *
328  *
329  *
330  * Return value: (transfer none):
331  *
332  * Since: 0.9.7
333  **/
334 void *
335 hb_shape_plan_get_user_data (hb_shape_plan_t    *shape_plan,
336                              hb_user_data_key_t *key)
337 {
338   return hb_object_get_user_data (shape_plan, key);
339 }
340
341 /**
342  * hb_shape_plan_get_shaper:
343  * @shape_plan: a shape plan.
344  *
345  *
346  *
347  * Return value: (transfer none):
348  *
349  * Since: 0.9.7
350  **/
351 const char *
352 hb_shape_plan_get_shaper (hb_shape_plan_t *shape_plan)
353 {
354   return shape_plan->key.shaper_name;
355 }
356
357
358 /**
359  * hb_shape_plan_execute:
360  * @shape_plan: a shape plan.
361  * @font: a font.
362  * @buffer: a buffer.
363  * @features: (array length=num_features):
364  * @num_features:
365  *
366  *
367  *
368  * Return value:
369  *
370  * Since: 0.9.7
371  **/
372 hb_bool_t
373 hb_shape_plan_execute (hb_shape_plan_t    *shape_plan,
374                        hb_font_t          *font,
375                        hb_buffer_t        *buffer,
376                        const hb_feature_t *features,
377                        unsigned int        num_features)
378 {
379   DEBUG_MSG_FUNC (SHAPE_PLAN, shape_plan,
380                   "num_features=%d shaper_func=%p, shaper_name=%s",
381                   num_features,
382                   shape_plan->key.shaper_func,
383                   shape_plan->key.shaper_name);
384
385   if (unlikely (!buffer->len))
386     return true;
387
388   assert (!hb_object_is_immutable (buffer));
389   assert (buffer->content_type == HB_BUFFER_CONTENT_TYPE_UNICODE);
390
391   if (unlikely (hb_object_is_inert (shape_plan)))
392     return false;
393
394   assert (shape_plan->face_unsafe == font->face);
395   assert (hb_segment_properties_equal (&shape_plan->key.props, &buffer->props));
396
397 #define HB_SHAPER_EXECUTE(shaper) \
398         HB_STMT_START { \
399           return font->data.shaper && \
400                  _hb_##shaper##_shape (shape_plan, font, buffer, features, num_features); \
401         } HB_STMT_END
402
403   if (false)
404     ;
405 #define HB_SHAPER_IMPLEMENT(shaper) \
406   else if (shape_plan->key.shaper_func == _hb_##shaper##_shape) \
407     HB_SHAPER_EXECUTE (shaper);
408 #include "hb-shaper-list.hh"
409 #undef HB_SHAPER_IMPLEMENT
410
411 #undef HB_SHAPER_EXECUTE
412
413   return false;
414 }
415
416
417 /*
418  * Caching
419  */
420
421 /**
422  * hb_shape_plan_create_cached:
423  * @face:
424  * @props:
425  * @user_features: (array length=num_user_features):
426  * @num_user_features:
427  * @shaper_list: (array zero-terminated=1):
428  *
429  *
430  *
431  * Return value: (transfer full):
432  *
433  * Since: 0.9.7
434  **/
435 hb_shape_plan_t *
436 hb_shape_plan_create_cached (hb_face_t                     *face,
437                              const hb_segment_properties_t *props,
438                              const hb_feature_t            *user_features,
439                              unsigned int                   num_user_features,
440                              const char * const            *shaper_list)
441 {
442   return hb_shape_plan_create_cached2 (face, props,
443                                        user_features, num_user_features,
444                                        nullptr, 0,
445                                        shaper_list);
446 }
447
448 hb_shape_plan_t *
449 hb_shape_plan_create_cached2 (hb_face_t                     *face,
450                               const hb_segment_properties_t *props,
451                               const hb_feature_t            *user_features,
452                               unsigned int                   num_user_features,
453                               const int                     *coords,
454                               unsigned int                   num_coords,
455                               const char * const            *shaper_list)
456 {
457   DEBUG_MSG_FUNC (SHAPE_PLAN, nullptr,
458                   "face=%p num_features=%d shaper_list=%p",
459                   face,
460                   num_user_features,
461                   shaper_list);
462
463 retry:
464   hb_face_t::plan_node_t *cached_plan_nodes = face->shape_plans;
465
466   bool dont_cache = hb_object_is_inert (face);
467
468   if (likely (!dont_cache))
469   {
470     hb_shape_plan_key_t key;
471     if (!key.init (false,
472                    face,
473                    props,
474                    user_features,
475                    num_user_features,
476                    coords,
477                    num_coords,
478                    shaper_list))
479       return hb_shape_plan_get_empty ();
480
481     for (hb_face_t::plan_node_t *node = cached_plan_nodes; node; node = node->next)
482       if (node->shape_plan->key.equal (&key))
483       {
484         DEBUG_MSG_FUNC (SHAPE_PLAN, node->shape_plan, "fulfilled from cache");
485         return hb_shape_plan_reference (node->shape_plan);
486       }
487   }
488
489   hb_shape_plan_t *shape_plan = hb_shape_plan_create2 (face, props,
490                                                        user_features, num_user_features,
491                                                        coords, num_coords,
492                                                        shaper_list);
493
494   if (unlikely (dont_cache))
495     return shape_plan;
496
497   hb_face_t::plan_node_t *node = (hb_face_t::plan_node_t *) calloc (1, sizeof (hb_face_t::plan_node_t));
498   if (unlikely (!node))
499     return shape_plan;
500
501   node->shape_plan = shape_plan;
502   node->next = cached_plan_nodes;
503
504   if (unlikely (!face->shape_plans.cmpexch (cached_plan_nodes, node)))
505   {
506     hb_shape_plan_destroy (shape_plan);
507     free (node);
508     goto retry;
509   }
510   DEBUG_MSG_FUNC (SHAPE_PLAN, shape_plan, "inserted into cache");
511
512   return hb_shape_plan_reference (shape_plan);
513 }