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