Imported Upstream version 1.4.6
[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-shape-plan-private.hh"
28 #include "hb-shaper-private.hh"
29 #include "hb-font-private.hh"
30 #include "hb-buffer-private.hh"
31
32
33 #ifndef HB_DEBUG_SHAPE_PLAN
34 #define HB_DEBUG_SHAPE_PLAN (HB_DEBUG+0)
35 #endif
36
37
38 static void
39 hb_shape_plan_plan (hb_shape_plan_t    *shape_plan,
40                     const hb_feature_t *user_features,
41                     unsigned int        num_user_features,
42                     const int          *coords,
43                     unsigned int        num_coords,
44                     const char * const *shaper_list)
45 {
46   DEBUG_MSG_FUNC (SHAPE_PLAN, shape_plan,
47                   "num_features=%d num_coords=%d shaper_list=%p",
48                   num_user_features,
49                   num_coords,
50                   shaper_list);
51
52   const hb_shaper_pair_t *shapers = _hb_shapers_get ();
53
54 #define HB_SHAPER_PLAN(shaper) \
55         HB_STMT_START { \
56           if (hb_##shaper##_shaper_face_data_ensure (shape_plan->face_unsafe)) { \
57             HB_SHAPER_DATA (shaper, shape_plan) = \
58               HB_SHAPER_DATA_CREATE_FUNC (shaper, shape_plan) (shape_plan, \
59                                                                user_features, num_user_features, \
60                                                                coords, num_coords); \
61             shape_plan->shaper_func = _hb_##shaper##_shape; \
62             shape_plan->shaper_name = #shaper; \
63             return; \
64           } \
65         } HB_STMT_END
66
67   if (likely (!shaper_list)) {
68     for (unsigned int i = 0; i < HB_SHAPERS_COUNT; i++)
69       if (0)
70         ;
71 #define HB_SHAPER_IMPLEMENT(shaper) \
72       else if (shapers[i].func == _hb_##shaper##_shape) \
73         HB_SHAPER_PLAN (shaper);
74 #include "hb-shaper-list.hh"
75 #undef HB_SHAPER_IMPLEMENT
76   } else {
77     for (; *shaper_list; shaper_list++)
78       if (0)
79         ;
80 #define HB_SHAPER_IMPLEMENT(shaper) \
81       else if (0 == strcmp (*shaper_list, #shaper)) \
82         HB_SHAPER_PLAN (shaper);
83 #include "hb-shaper-list.hh"
84 #undef HB_SHAPER_IMPLEMENT
85   }
86
87 #undef HB_SHAPER_PLAN
88 }
89
90
91 /*
92  * hb_shape_plan_t
93  */
94
95 /**
96  * hb_shape_plan_create: (Xconstructor)
97  * @face: 
98  * @props: 
99  * @user_features: (array length=num_user_features):
100  * @num_user_features: 
101  * @shaper_list: (array zero-terminated=1):
102  *
103  * 
104  *
105  * Return value: (transfer full):
106  *
107  * Since: 0.9.7
108  **/
109 hb_shape_plan_t *
110 hb_shape_plan_create (hb_face_t                     *face,
111                       const hb_segment_properties_t *props,
112                       const hb_feature_t            *user_features,
113                       unsigned int                   num_user_features,
114                       const char * const            *shaper_list)
115 {
116   return hb_shape_plan_create2 (face, props,
117                                 user_features, num_user_features,
118                                 NULL, 0,
119                                 shaper_list);
120 }
121
122 hb_shape_plan_t *
123 hb_shape_plan_create2 (hb_face_t                     *face,
124                        const hb_segment_properties_t *props,
125                        const hb_feature_t            *user_features,
126                        unsigned int                   num_user_features,
127                        const int                     *orig_coords,
128                        unsigned int                   num_coords,
129                        const char * const            *shaper_list)
130 {
131   DEBUG_MSG_FUNC (SHAPE_PLAN, NULL,
132                   "face=%p num_features=%d num_coords=%d shaper_list=%p",
133                   face,
134                   num_user_features,
135                   num_coords,
136                   shaper_list);
137
138   hb_shape_plan_t *shape_plan;
139   hb_feature_t *features = NULL;
140   int *coords = NULL;
141
142   if (unlikely (!face))
143     face = hb_face_get_empty ();
144   if (unlikely (!props))
145     return hb_shape_plan_get_empty ();
146   if (num_user_features && !(features = (hb_feature_t *) calloc (num_user_features, sizeof (hb_feature_t))))
147     return hb_shape_plan_get_empty ();
148   if (num_coords && !(coords = (int *) calloc (num_coords, sizeof (int))))
149   {
150     free (features);
151     return hb_shape_plan_get_empty ();
152   }
153   if (!(shape_plan = hb_object_create<hb_shape_plan_t> ()))
154   {
155     free (coords);
156     free (features);
157     return hb_shape_plan_get_empty ();
158   }
159
160   assert (props->direction != HB_DIRECTION_INVALID);
161
162   hb_face_make_immutable (face);
163   shape_plan->default_shaper_list = shaper_list == NULL;
164   shape_plan->face_unsafe = face;
165   shape_plan->props = *props;
166   shape_plan->num_user_features = num_user_features;
167   shape_plan->user_features = features;
168   if (num_user_features)
169     memcpy (features, user_features, num_user_features * sizeof (hb_feature_t));
170   shape_plan->num_coords = num_coords;
171   shape_plan->coords = coords;
172   if (num_coords)
173     memcpy (coords, orig_coords, num_coords * sizeof (int));
174
175   hb_shape_plan_plan (shape_plan,
176                       user_features, num_user_features,
177                       coords, num_coords,
178                       shaper_list);
179
180   return shape_plan;
181 }
182
183 /**
184  * hb_shape_plan_get_empty:
185  *
186  * 
187  *
188  * Return value: (transfer full):
189  *
190  * Since: 0.9.7
191  **/
192 hb_shape_plan_t *
193 hb_shape_plan_get_empty (void)
194 {
195   static const hb_shape_plan_t _hb_shape_plan_nil = {
196     HB_OBJECT_HEADER_STATIC,
197
198     true, /* default_shaper_list */
199     NULL, /* face */
200     HB_SEGMENT_PROPERTIES_DEFAULT, /* props */
201
202     NULL, /* shaper_func */
203     NULL, /* shaper_name */
204
205     NULL, /* user_features */
206     0,    /* num_user_featurs */
207
208     NULL, /* coords */
209     0,    /* num_coords */
210
211     {
212 #define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_INVALID,
213 #include "hb-shaper-list.hh"
214 #undef HB_SHAPER_IMPLEMENT
215     }
216   };
217
218   return const_cast<hb_shape_plan_t *> (&_hb_shape_plan_nil);
219 }
220
221 /**
222  * hb_shape_plan_reference: (skip)
223  * @shape_plan: a shape plan.
224  *
225  * 
226  *
227  * Return value: (transfer full):
228  *
229  * Since: 0.9.7
230  **/
231 hb_shape_plan_t *
232 hb_shape_plan_reference (hb_shape_plan_t *shape_plan)
233 {
234   return hb_object_reference (shape_plan);
235 }
236
237 /**
238  * hb_shape_plan_destroy: (skip)
239  * @shape_plan: a shape plan.
240  *
241  * 
242  *
243  * Since: 0.9.7
244  **/
245 void
246 hb_shape_plan_destroy (hb_shape_plan_t *shape_plan)
247 {
248   if (!hb_object_destroy (shape_plan)) return;
249
250 #define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_DESTROY(shaper, shape_plan);
251 #include "hb-shaper-list.hh"
252 #undef HB_SHAPER_IMPLEMENT
253
254   free (shape_plan->user_features);
255   free (shape_plan->coords);
256
257   free (shape_plan);
258 }
259
260 /**
261  * hb_shape_plan_set_user_data: (skip)
262  * @shape_plan: a shape plan.
263  * @key: 
264  * @data: 
265  * @destroy: 
266  * @replace: 
267  *
268  * 
269  *
270  * Return value: 
271  *
272  * Since: 0.9.7
273  **/
274 hb_bool_t
275 hb_shape_plan_set_user_data (hb_shape_plan_t    *shape_plan,
276                              hb_user_data_key_t *key,
277                              void *              data,
278                              hb_destroy_func_t   destroy,
279                              hb_bool_t           replace)
280 {
281   return hb_object_set_user_data (shape_plan, key, data, destroy, replace);
282 }
283
284 /**
285  * hb_shape_plan_get_user_data: (skip)
286  * @shape_plan: a shape plan.
287  * @key: 
288  *
289  * 
290  *
291  * Return value: (transfer none):
292  *
293  * Since: 0.9.7
294  **/
295 void *
296 hb_shape_plan_get_user_data (hb_shape_plan_t    *shape_plan,
297                              hb_user_data_key_t *key)
298 {
299   return hb_object_get_user_data (shape_plan, key);
300 }
301
302
303 /**
304  * hb_shape_plan_execute:
305  * @shape_plan: a shape plan.
306  * @font: a font.
307  * @buffer: a buffer.
308  * @features: (array length=num_features):
309  * @num_features: 
310  *
311  * 
312  *
313  * Return value: 
314  *
315  * Since: 0.9.7
316  **/
317 hb_bool_t
318 hb_shape_plan_execute (hb_shape_plan_t    *shape_plan,
319                        hb_font_t          *font,
320                        hb_buffer_t        *buffer,
321                        const hb_feature_t *features,
322                        unsigned int        num_features)
323 {
324   DEBUG_MSG_FUNC (SHAPE_PLAN, shape_plan,
325                   "num_features=%d shaper_func=%p, shaper_name=%s",
326                   num_features,
327                   shape_plan->shaper_func,
328                   shape_plan->shaper_name);
329
330   if (unlikely (!buffer->len))
331     return true;
332
333   assert (!hb_object_is_inert (buffer));
334   assert (buffer->content_type == HB_BUFFER_CONTENT_TYPE_UNICODE);
335
336   if (unlikely (hb_object_is_inert (shape_plan)))
337     return false;
338
339   assert (shape_plan->face_unsafe == font->face);
340   assert (hb_segment_properties_equal (&shape_plan->props, &buffer->props));
341
342 #define HB_SHAPER_EXECUTE(shaper) \
343         HB_STMT_START { \
344           return HB_SHAPER_DATA (shaper, shape_plan) && \
345                  hb_##shaper##_shaper_font_data_ensure (font) && \
346                  _hb_##shaper##_shape (shape_plan, font, buffer, features, num_features); \
347         } HB_STMT_END
348
349   if (0)
350     ;
351 #define HB_SHAPER_IMPLEMENT(shaper) \
352   else if (shape_plan->shaper_func == _hb_##shaper##_shape) \
353     HB_SHAPER_EXECUTE (shaper);
354 #include "hb-shaper-list.hh"
355 #undef HB_SHAPER_IMPLEMENT
356
357 #undef HB_SHAPER_EXECUTE
358
359   return false;
360 }
361
362
363 /*
364  * caching
365  */
366
367 #if 0
368 static unsigned int
369 hb_shape_plan_hash (const hb_shape_plan_t *shape_plan)
370 {
371   return hb_segment_properties_hash (&shape_plan->props) +
372          shape_plan->default_shaper_list ? 0 : (intptr_t) shape_plan->shaper_func;
373 }
374 #endif
375
376 /* User-feature caching is currently somewhat dumb:
377  * it only finds matches where the feature array is identical,
378  * not cases where the feature lists would be compatible for plan purposes
379  * but have different ranges, for example.
380  */
381 struct hb_shape_plan_proposal_t
382 {
383   const hb_segment_properties_t  props;
384   const char * const            *shaper_list;
385   const hb_feature_t            *user_features;
386   unsigned int                   num_user_features;
387   const int                     *coords;
388   unsigned int                   num_coords;
389   hb_shape_func_t               *shaper_func;
390 };
391
392 static inline hb_bool_t
393 hb_shape_plan_user_features_match (const hb_shape_plan_t          *shape_plan,
394                                    const hb_shape_plan_proposal_t *proposal)
395 {
396   if (proposal->num_user_features != shape_plan->num_user_features)
397     return false;
398   for (unsigned int i = 0, n = proposal->num_user_features; i < n; i++)
399     if (proposal->user_features[i].tag   != shape_plan->user_features[i].tag   ||
400         proposal->user_features[i].value != shape_plan->user_features[i].value ||
401         proposal->user_features[i].start != shape_plan->user_features[i].start ||
402         proposal->user_features[i].end   != shape_plan->user_features[i].end)
403       return false;
404   return true;
405 }
406
407 static inline hb_bool_t
408 hb_shape_plan_coords_match (const hb_shape_plan_t          *shape_plan,
409                             const hb_shape_plan_proposal_t *proposal)
410 {
411   if (proposal->num_coords != shape_plan->num_coords)
412     return false;
413   for (unsigned int i = 0, n = proposal->num_coords; i < n; i++)
414     if (proposal->coords[i] != shape_plan->coords[i])
415       return false;
416   return true;
417 }
418
419 static hb_bool_t
420 hb_shape_plan_matches (const hb_shape_plan_t          *shape_plan,
421                        const hb_shape_plan_proposal_t *proposal)
422 {
423   return hb_segment_properties_equal (&shape_plan->props, &proposal->props) &&
424          hb_shape_plan_user_features_match (shape_plan, proposal) &&
425          hb_shape_plan_coords_match (shape_plan, proposal) &&
426          ((shape_plan->default_shaper_list && proposal->shaper_list == NULL) ||
427           (shape_plan->shaper_func == proposal->shaper_func));
428 }
429
430 static inline hb_bool_t
431 hb_non_global_user_features_present (const hb_feature_t *user_features,
432                                      unsigned int        num_user_features)
433 {
434   while (num_user_features) {
435     if (user_features->start != 0 || user_features->end != (unsigned int) -1)
436       return true;
437     num_user_features--;
438     user_features++;
439   }
440   return false;
441 }
442
443 static inline hb_bool_t
444 hb_coords_present (const int *coords,
445                    unsigned int num_coords)
446 {
447   return num_coords != 0;
448 }
449
450 /**
451  * hb_shape_plan_create_cached:
452  * @face: 
453  * @props: 
454  * @user_features: (array length=num_user_features):
455  * @num_user_features: 
456  * @shaper_list: (array zero-terminated=1):
457  *
458  * 
459  *
460  * Return value: (transfer full):
461  *
462  * Since: 0.9.7
463  **/
464 hb_shape_plan_t *
465 hb_shape_plan_create_cached (hb_face_t                     *face,
466                              const hb_segment_properties_t *props,
467                              const hb_feature_t            *user_features,
468                              unsigned int                   num_user_features,
469                              const char * const            *shaper_list)
470 {
471   return hb_shape_plan_create_cached2 (face, props,
472                                        user_features, num_user_features,
473                                        NULL, 0,
474                                        shaper_list);
475 }
476
477 hb_shape_plan_t *
478 hb_shape_plan_create_cached2 (hb_face_t                     *face,
479                               const hb_segment_properties_t *props,
480                               const hb_feature_t            *user_features,
481                               unsigned int                   num_user_features,
482                               const int                     *coords,
483                               unsigned int                   num_coords,
484                               const char * const            *shaper_list)
485 {
486   DEBUG_MSG_FUNC (SHAPE_PLAN, NULL,
487                   "face=%p num_features=%d shaper_list=%p",
488                   face,
489                   num_user_features,
490                   shaper_list);
491
492   hb_shape_plan_proposal_t proposal = {
493     *props,
494     shaper_list,
495     user_features,
496     num_user_features,
497     NULL
498   };
499
500   if (shaper_list) {
501     /* Choose shaper.  Adapted from hb_shape_plan_plan().
502      * Must choose shaper exactly the same way as that function. */
503     for (const char * const *shaper_item = shaper_list; *shaper_item; shaper_item++)
504       if (0)
505         ;
506 #define HB_SHAPER_IMPLEMENT(shaper) \
507       else if (0 == strcmp (*shaper_item, #shaper) && \
508                hb_##shaper##_shaper_face_data_ensure (face)) \
509       { \
510         proposal.shaper_func = _hb_##shaper##_shape; \
511         break; \
512       }
513 #include "hb-shaper-list.hh"
514 #undef HB_SHAPER_IMPLEMENT
515
516     if (unlikely (!proposal.shaper_func))
517       return hb_shape_plan_get_empty ();
518   }
519
520
521 retry:
522   hb_face_t::plan_node_t *cached_plan_nodes = (hb_face_t::plan_node_t *) hb_atomic_ptr_get (&face->shape_plans);
523   for (hb_face_t::plan_node_t *node = cached_plan_nodes; node; node = node->next)
524     if (hb_shape_plan_matches (node->shape_plan, &proposal))
525     {
526       DEBUG_MSG_FUNC (SHAPE_PLAN, node->shape_plan, "fulfilled from cache");
527       return hb_shape_plan_reference (node->shape_plan);
528     }
529
530   /* Not found. */
531
532   hb_shape_plan_t *shape_plan = hb_shape_plan_create2 (face, props,
533                                                        user_features, num_user_features,
534                                                        coords, num_coords,
535                                                        shaper_list);
536
537   /* Don't add to the cache if face is inert. */
538   if (unlikely (hb_object_is_inert (face)))
539     return shape_plan;
540
541   /* Don't add the plan to the cache if there were user features with non-global ranges */
542   if (hb_non_global_user_features_present (user_features, num_user_features))
543     return shape_plan;
544   /* Don't add the plan to the cache if there were variation coordinates XXX Fix me. */
545   if (hb_coords_present (coords, num_coords))
546     return shape_plan;
547
548   hb_face_t::plan_node_t *node = (hb_face_t::plan_node_t *) calloc (1, sizeof (hb_face_t::plan_node_t));
549   if (unlikely (!node))
550     return shape_plan;
551
552   node->shape_plan = shape_plan;
553   node->next = cached_plan_nodes;
554
555   if (!hb_atomic_ptr_cmpexch (&face->shape_plans, cached_plan_nodes, node)) {
556     hb_shape_plan_destroy (shape_plan);
557     free (node);
558     goto retry;
559   }
560   DEBUG_MSG_FUNC (SHAPE_PLAN, shape_plan, "inserted into cache");
561
562   return hb_shape_plan_reference (shape_plan);
563 }
564
565 /**
566  * hb_shape_plan_get_shaper:
567  * @shape_plan: a shape plan.
568  *
569  * 
570  *
571  * Return value: (transfer none):
572  *
573  * Since: 0.9.7
574  **/
575 const char *
576 hb_shape_plan_get_shaper (hb_shape_plan_t *shape_plan)
577 {
578   return shape_plan->shaper_name;
579 }