Fri, 16 Jan 2015 18:13:44 +0100
Integrate suggestion from review to improve consistency with existing code.
michael@0 | 1 | /* |
michael@0 | 2 | * Copyright © 2011 Martin Hosken |
michael@0 | 3 | * Copyright © 2011 SIL International |
michael@0 | 4 | * Copyright © 2011,2012 Google, Inc. |
michael@0 | 5 | * |
michael@0 | 6 | * This is part of HarfBuzz, a text shaping library. |
michael@0 | 7 | * |
michael@0 | 8 | * Permission is hereby granted, without written agreement and without |
michael@0 | 9 | * license or royalty fees, to use, copy, modify, and distribute this |
michael@0 | 10 | * software and its documentation for any purpose, provided that the |
michael@0 | 11 | * above copyright notice and the following two paragraphs appear in |
michael@0 | 12 | * all copies of this software. |
michael@0 | 13 | * |
michael@0 | 14 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR |
michael@0 | 15 | * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES |
michael@0 | 16 | * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN |
michael@0 | 17 | * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH |
michael@0 | 18 | * DAMAGE. |
michael@0 | 19 | * |
michael@0 | 20 | * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, |
michael@0 | 21 | * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND |
michael@0 | 22 | * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS |
michael@0 | 23 | * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO |
michael@0 | 24 | * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. |
michael@0 | 25 | * |
michael@0 | 26 | * Google Author(s): Behdad Esfahbod |
michael@0 | 27 | */ |
michael@0 | 28 | |
michael@0 | 29 | #define HB_SHAPER graphite2 |
michael@0 | 30 | #define hb_graphite2_shaper_font_data_t gr_font |
michael@0 | 31 | #include "hb-shaper-impl-private.hh" |
michael@0 | 32 | |
michael@0 | 33 | #include "hb-graphite2.h" |
michael@0 | 34 | |
michael@0 | 35 | #include <graphite2/Segment.h> |
michael@0 | 36 | |
michael@0 | 37 | |
michael@0 | 38 | HB_SHAPER_DATA_ENSURE_DECLARE(graphite2, face) |
michael@0 | 39 | HB_SHAPER_DATA_ENSURE_DECLARE(graphite2, font) |
michael@0 | 40 | |
michael@0 | 41 | |
michael@0 | 42 | /* |
michael@0 | 43 | * shaper face data |
michael@0 | 44 | */ |
michael@0 | 45 | |
michael@0 | 46 | typedef struct hb_graphite2_tablelist_t { |
michael@0 | 47 | struct hb_graphite2_tablelist_t *next; |
michael@0 | 48 | hb_blob_t *blob; |
michael@0 | 49 | unsigned int tag; |
michael@0 | 50 | } hb_graphite2_tablelist_t; |
michael@0 | 51 | |
michael@0 | 52 | struct hb_graphite2_shaper_face_data_t { |
michael@0 | 53 | hb_face_t *face; |
michael@0 | 54 | gr_face *grface; |
michael@0 | 55 | hb_graphite2_tablelist_t *tlist; |
michael@0 | 56 | }; |
michael@0 | 57 | |
michael@0 | 58 | static const void *hb_graphite2_get_table (const void *data, unsigned int tag, size_t *len) |
michael@0 | 59 | { |
michael@0 | 60 | hb_graphite2_shaper_face_data_t *face_data = (hb_graphite2_shaper_face_data_t *) data; |
michael@0 | 61 | hb_graphite2_tablelist_t *tlist = face_data->tlist; |
michael@0 | 62 | |
michael@0 | 63 | hb_blob_t *blob = NULL; |
michael@0 | 64 | |
michael@0 | 65 | for (hb_graphite2_tablelist_t *p = tlist; p; p = p->next) |
michael@0 | 66 | if (p->tag == tag) { |
michael@0 | 67 | blob = p->blob; |
michael@0 | 68 | break; |
michael@0 | 69 | } |
michael@0 | 70 | |
michael@0 | 71 | if (unlikely (!blob)) |
michael@0 | 72 | { |
michael@0 | 73 | blob = face_data->face->reference_table (tag); |
michael@0 | 74 | |
michael@0 | 75 | hb_graphite2_tablelist_t *p = (hb_graphite2_tablelist_t *) calloc (1, sizeof (hb_graphite2_tablelist_t)); |
michael@0 | 76 | if (unlikely (!p)) { |
michael@0 | 77 | hb_blob_destroy (blob); |
michael@0 | 78 | return NULL; |
michael@0 | 79 | } |
michael@0 | 80 | p->blob = blob; |
michael@0 | 81 | p->tag = tag; |
michael@0 | 82 | |
michael@0 | 83 | /* TODO Not thread-safe, but fairly harmless. |
michael@0 | 84 | * We can do the double-chcked pointer cmpexch thing here. */ |
michael@0 | 85 | p->next = face_data->tlist; |
michael@0 | 86 | face_data->tlist = p; |
michael@0 | 87 | } |
michael@0 | 88 | |
michael@0 | 89 | unsigned int tlen; |
michael@0 | 90 | const char *d = hb_blob_get_data (blob, &tlen); |
michael@0 | 91 | *len = tlen; |
michael@0 | 92 | return d; |
michael@0 | 93 | } |
michael@0 | 94 | |
michael@0 | 95 | hb_graphite2_shaper_face_data_t * |
michael@0 | 96 | _hb_graphite2_shaper_face_data_create (hb_face_t *face) |
michael@0 | 97 | { |
michael@0 | 98 | hb_blob_t *silf_blob = face->reference_table (HB_GRAPHITE2_TAG_SILF); |
michael@0 | 99 | /* Umm, we just reference the table to check whether it exists. |
michael@0 | 100 | * Maybe add better API for this? */ |
michael@0 | 101 | if (!hb_blob_get_length (silf_blob)) |
michael@0 | 102 | { |
michael@0 | 103 | hb_blob_destroy (silf_blob); |
michael@0 | 104 | return NULL; |
michael@0 | 105 | } |
michael@0 | 106 | hb_blob_destroy (silf_blob); |
michael@0 | 107 | |
michael@0 | 108 | hb_graphite2_shaper_face_data_t *data = (hb_graphite2_shaper_face_data_t *) calloc (1, sizeof (hb_graphite2_shaper_face_data_t)); |
michael@0 | 109 | if (unlikely (!data)) |
michael@0 | 110 | return NULL; |
michael@0 | 111 | |
michael@0 | 112 | data->face = face; |
michael@0 | 113 | data->grface = gr_make_face (data, &hb_graphite2_get_table, gr_face_preloadAll); |
michael@0 | 114 | |
michael@0 | 115 | if (unlikely (!data->grface)) { |
michael@0 | 116 | free (data); |
michael@0 | 117 | return NULL; |
michael@0 | 118 | } |
michael@0 | 119 | |
michael@0 | 120 | return data; |
michael@0 | 121 | } |
michael@0 | 122 | |
michael@0 | 123 | void |
michael@0 | 124 | _hb_graphite2_shaper_face_data_destroy (hb_graphite2_shaper_face_data_t *data) |
michael@0 | 125 | { |
michael@0 | 126 | hb_graphite2_tablelist_t *tlist = data->tlist; |
michael@0 | 127 | |
michael@0 | 128 | while (tlist) |
michael@0 | 129 | { |
michael@0 | 130 | hb_graphite2_tablelist_t *old = tlist; |
michael@0 | 131 | hb_blob_destroy (tlist->blob); |
michael@0 | 132 | tlist = tlist->next; |
michael@0 | 133 | free (old); |
michael@0 | 134 | } |
michael@0 | 135 | |
michael@0 | 136 | gr_face_destroy (data->grface); |
michael@0 | 137 | |
michael@0 | 138 | free (data); |
michael@0 | 139 | } |
michael@0 | 140 | |
michael@0 | 141 | gr_face * |
michael@0 | 142 | hb_graphite2_face_get_gr_face (hb_face_t *face) |
michael@0 | 143 | { |
michael@0 | 144 | if (unlikely (!hb_graphite2_shaper_face_data_ensure (face))) return NULL; |
michael@0 | 145 | return HB_SHAPER_DATA_GET (face)->grface; |
michael@0 | 146 | } |
michael@0 | 147 | |
michael@0 | 148 | |
michael@0 | 149 | /* |
michael@0 | 150 | * shaper font data |
michael@0 | 151 | */ |
michael@0 | 152 | |
michael@0 | 153 | static float hb_graphite2_get_advance (const void *hb_font, unsigned short gid) |
michael@0 | 154 | { |
michael@0 | 155 | return ((hb_font_t *) hb_font)->get_glyph_h_advance (gid); |
michael@0 | 156 | } |
michael@0 | 157 | |
michael@0 | 158 | hb_graphite2_shaper_font_data_t * |
michael@0 | 159 | _hb_graphite2_shaper_font_data_create (hb_font_t *font) |
michael@0 | 160 | { |
michael@0 | 161 | if (unlikely (!hb_graphite2_shaper_face_data_ensure (font->face))) return NULL; |
michael@0 | 162 | |
michael@0 | 163 | hb_face_t *face = font->face; |
michael@0 | 164 | hb_graphite2_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face); |
michael@0 | 165 | |
michael@0 | 166 | return gr_make_font_with_advance_fn (font->x_scale, font, &hb_graphite2_get_advance, face_data->grface); |
michael@0 | 167 | } |
michael@0 | 168 | |
michael@0 | 169 | void |
michael@0 | 170 | _hb_graphite2_shaper_font_data_destroy (hb_graphite2_shaper_font_data_t *data) |
michael@0 | 171 | { |
michael@0 | 172 | gr_font_destroy (data); |
michael@0 | 173 | } |
michael@0 | 174 | |
michael@0 | 175 | gr_font * |
michael@0 | 176 | hb_graphite2_font_get_gr_font (hb_font_t *font) |
michael@0 | 177 | { |
michael@0 | 178 | if (unlikely (!hb_graphite2_shaper_font_data_ensure (font))) return NULL; |
michael@0 | 179 | return HB_SHAPER_DATA_GET (font); |
michael@0 | 180 | } |
michael@0 | 181 | |
michael@0 | 182 | |
michael@0 | 183 | /* |
michael@0 | 184 | * shaper shape_plan data |
michael@0 | 185 | */ |
michael@0 | 186 | |
michael@0 | 187 | struct hb_graphite2_shaper_shape_plan_data_t {}; |
michael@0 | 188 | |
michael@0 | 189 | hb_graphite2_shaper_shape_plan_data_t * |
michael@0 | 190 | _hb_graphite2_shaper_shape_plan_data_create (hb_shape_plan_t *shape_plan HB_UNUSED, |
michael@0 | 191 | const hb_feature_t *user_features HB_UNUSED, |
michael@0 | 192 | unsigned int num_user_features HB_UNUSED) |
michael@0 | 193 | { |
michael@0 | 194 | return (hb_graphite2_shaper_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED; |
michael@0 | 195 | } |
michael@0 | 196 | |
michael@0 | 197 | void |
michael@0 | 198 | _hb_graphite2_shaper_shape_plan_data_destroy (hb_graphite2_shaper_shape_plan_data_t *data HB_UNUSED) |
michael@0 | 199 | { |
michael@0 | 200 | } |
michael@0 | 201 | |
michael@0 | 202 | |
michael@0 | 203 | /* |
michael@0 | 204 | * shaper |
michael@0 | 205 | */ |
michael@0 | 206 | |
michael@0 | 207 | struct hb_graphite2_cluster_t { |
michael@0 | 208 | unsigned int base_char; |
michael@0 | 209 | unsigned int num_chars; |
michael@0 | 210 | unsigned int base_glyph; |
michael@0 | 211 | unsigned int num_glyphs; |
michael@0 | 212 | }; |
michael@0 | 213 | |
michael@0 | 214 | hb_bool_t |
michael@0 | 215 | _hb_graphite2_shape (hb_shape_plan_t *shape_plan, |
michael@0 | 216 | hb_font_t *font, |
michael@0 | 217 | hb_buffer_t *buffer, |
michael@0 | 218 | const hb_feature_t *features, |
michael@0 | 219 | unsigned int num_features) |
michael@0 | 220 | { |
michael@0 | 221 | hb_face_t *face = font->face; |
michael@0 | 222 | gr_face *grface = HB_SHAPER_DATA_GET (face)->grface; |
michael@0 | 223 | gr_font *grfont = HB_SHAPER_DATA_GET (font); |
michael@0 | 224 | |
michael@0 | 225 | const char *lang = hb_language_to_string (hb_buffer_get_language (buffer)); |
michael@0 | 226 | const char *lang_end = lang ? strchr (lang, '-') : NULL; |
michael@0 | 227 | int lang_len = lang_end ? lang_end - lang : -1; |
michael@0 | 228 | gr_feature_val *feats = gr_face_featureval_for_lang (grface, lang ? hb_tag_from_string (lang, lang_len) : 0); |
michael@0 | 229 | |
michael@0 | 230 | while (num_features--) |
michael@0 | 231 | { |
michael@0 | 232 | const gr_feature_ref *fref = gr_face_find_fref (grface, features->tag); |
michael@0 | 233 | if (fref) |
michael@0 | 234 | gr_fref_set_feature_value (fref, features->value, feats); |
michael@0 | 235 | features++; |
michael@0 | 236 | } |
michael@0 | 237 | |
michael@0 | 238 | gr_segment *seg = NULL; |
michael@0 | 239 | const gr_slot *is; |
michael@0 | 240 | unsigned int ci = 0, ic = 0; |
michael@0 | 241 | float curradvx = 0., curradvy = 0.; |
michael@0 | 242 | |
michael@0 | 243 | unsigned int scratch_size; |
michael@0 | 244 | hb_buffer_t::scratch_buffer_t *scratch = buffer->get_scratch_buffer (&scratch_size); |
michael@0 | 245 | |
michael@0 | 246 | uint32_t *chars = (uint32_t *) scratch; |
michael@0 | 247 | |
michael@0 | 248 | for (unsigned int i = 0; i < buffer->len; ++i) |
michael@0 | 249 | chars[i] = buffer->info[i].codepoint; |
michael@0 | 250 | |
michael@0 | 251 | hb_tag_t script_tag[2]; |
michael@0 | 252 | hb_ot_tags_from_script (hb_buffer_get_script (buffer), &script_tag[0], &script_tag[1]); |
michael@0 | 253 | |
michael@0 | 254 | seg = gr_make_seg (grfont, grface, |
michael@0 | 255 | script_tag[1] == HB_TAG_NONE ? script_tag[0] : script_tag[1], |
michael@0 | 256 | feats, |
michael@0 | 257 | gr_utf32, chars, buffer->len, |
michael@0 | 258 | 2 | (hb_buffer_get_direction (buffer) == HB_DIRECTION_RTL ? 1 : 0)); |
michael@0 | 259 | |
michael@0 | 260 | if (unlikely (!seg)) { |
michael@0 | 261 | if (feats) gr_featureval_destroy (feats); |
michael@0 | 262 | return false; |
michael@0 | 263 | } |
michael@0 | 264 | |
michael@0 | 265 | unsigned int glyph_count = gr_seg_n_slots (seg); |
michael@0 | 266 | if (unlikely (!glyph_count)) { |
michael@0 | 267 | if (feats) gr_featureval_destroy (feats); |
michael@0 | 268 | gr_seg_destroy (seg); |
michael@0 | 269 | return false; |
michael@0 | 270 | } |
michael@0 | 271 | |
michael@0 | 272 | scratch = buffer->get_scratch_buffer (&scratch_size); |
michael@0 | 273 | while ((DIV_CEIL (sizeof (hb_graphite2_cluster_t) * buffer->len, sizeof (*scratch)) + |
michael@0 | 274 | DIV_CEIL (sizeof (hb_codepoint_t) * glyph_count, sizeof (*scratch))) > scratch_size) |
michael@0 | 275 | { |
michael@0 | 276 | buffer->ensure (buffer->allocated * 2); |
michael@0 | 277 | if (unlikely (buffer->in_error)) { |
michael@0 | 278 | if (feats) gr_featureval_destroy (feats); |
michael@0 | 279 | gr_seg_destroy (seg); |
michael@0 | 280 | return false; |
michael@0 | 281 | } |
michael@0 | 282 | scratch = buffer->get_scratch_buffer (&scratch_size); |
michael@0 | 283 | } |
michael@0 | 284 | |
michael@0 | 285 | #define ALLOCATE_ARRAY(Type, name, len) \ |
michael@0 | 286 | Type *name = (Type *) scratch; \ |
michael@0 | 287 | { \ |
michael@0 | 288 | unsigned int _consumed = DIV_CEIL ((len) * sizeof (Type), sizeof (*scratch)); \ |
michael@0 | 289 | assert (_consumed <= scratch_size); \ |
michael@0 | 290 | scratch += _consumed; \ |
michael@0 | 291 | scratch_size -= _consumed; \ |
michael@0 | 292 | } |
michael@0 | 293 | |
michael@0 | 294 | ALLOCATE_ARRAY (hb_graphite2_cluster_t, clusters, buffer->len); |
michael@0 | 295 | ALLOCATE_ARRAY (hb_codepoint_t, gids, glyph_count); |
michael@0 | 296 | |
michael@0 | 297 | #undef ALLOCATE_ARRAY |
michael@0 | 298 | |
michael@0 | 299 | memset (clusters, 0, sizeof (clusters[0]) * buffer->len); |
michael@0 | 300 | |
michael@0 | 301 | hb_codepoint_t *pg = gids; |
michael@0 | 302 | for (is = gr_seg_first_slot (seg), ic = 0; is; is = gr_slot_next_in_segment (is), ic++) |
michael@0 | 303 | { |
michael@0 | 304 | unsigned int before = gr_slot_before (is); |
michael@0 | 305 | unsigned int after = gr_slot_after (is); |
michael@0 | 306 | *pg = gr_slot_gid (is); |
michael@0 | 307 | pg++; |
michael@0 | 308 | while (clusters[ci].base_char > before && ci) |
michael@0 | 309 | { |
michael@0 | 310 | clusters[ci-1].num_chars += clusters[ci].num_chars; |
michael@0 | 311 | clusters[ci-1].num_glyphs += clusters[ci].num_glyphs; |
michael@0 | 312 | ci--; |
michael@0 | 313 | } |
michael@0 | 314 | |
michael@0 | 315 | if (gr_slot_can_insert_before (is) && clusters[ci].num_chars && before >= clusters[ci].base_char + clusters[ci].num_chars) |
michael@0 | 316 | { |
michael@0 | 317 | hb_graphite2_cluster_t *c = clusters + ci + 1; |
michael@0 | 318 | c->base_char = clusters[ci].base_char + clusters[ci].num_chars; |
michael@0 | 319 | c->num_chars = before - c->base_char; |
michael@0 | 320 | c->base_glyph = ic; |
michael@0 | 321 | c->num_glyphs = 0; |
michael@0 | 322 | ci++; |
michael@0 | 323 | } |
michael@0 | 324 | clusters[ci].num_glyphs++; |
michael@0 | 325 | |
michael@0 | 326 | if (clusters[ci].base_char + clusters[ci].num_chars < after + 1) |
michael@0 | 327 | clusters[ci].num_chars = after + 1 - clusters[ci].base_char; |
michael@0 | 328 | } |
michael@0 | 329 | ci++; |
michael@0 | 330 | |
michael@0 | 331 | //buffer->clear_output (); |
michael@0 | 332 | for (unsigned int i = 0; i < ci; ++i) |
michael@0 | 333 | { |
michael@0 | 334 | for (unsigned int j = 0; j < clusters[i].num_glyphs; ++j) |
michael@0 | 335 | { |
michael@0 | 336 | hb_glyph_info_t *info = &buffer->info[clusters[i].base_glyph + j]; |
michael@0 | 337 | info->codepoint = gids[clusters[i].base_glyph + j]; |
michael@0 | 338 | info->cluster = gr_cinfo_base(gr_seg_cinfo(seg, clusters[i].base_char)); |
michael@0 | 339 | } |
michael@0 | 340 | } |
michael@0 | 341 | buffer->len = glyph_count; |
michael@0 | 342 | //buffer->swap_buffers (); |
michael@0 | 343 | |
michael@0 | 344 | if (HB_DIRECTION_IS_BACKWARD(buffer->props.direction)) |
michael@0 | 345 | curradvx = gr_seg_advance_X(seg); |
michael@0 | 346 | |
michael@0 | 347 | hb_glyph_position_t *pPos; |
michael@0 | 348 | for (pPos = hb_buffer_get_glyph_positions (buffer, NULL), is = gr_seg_first_slot (seg); |
michael@0 | 349 | is; pPos++, is = gr_slot_next_in_segment (is)) |
michael@0 | 350 | { |
michael@0 | 351 | pPos->x_offset = gr_slot_origin_X (is) - curradvx; |
michael@0 | 352 | pPos->y_offset = gr_slot_origin_Y (is) - curradvy; |
michael@0 | 353 | pPos->x_advance = gr_slot_advance_X (is, grface, grfont); |
michael@0 | 354 | pPos->y_advance = gr_slot_advance_Y (is, grface, grfont); |
michael@0 | 355 | if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction)) |
michael@0 | 356 | curradvx -= pPos->x_advance; |
michael@0 | 357 | pPos->x_offset = gr_slot_origin_X (is) - curradvx; |
michael@0 | 358 | if (!HB_DIRECTION_IS_BACKWARD (buffer->props.direction)) |
michael@0 | 359 | curradvx += pPos->x_advance; |
michael@0 | 360 | pPos->y_offset = gr_slot_origin_Y (is) - curradvy; |
michael@0 | 361 | curradvy += pPos->y_advance; |
michael@0 | 362 | } |
michael@0 | 363 | if (!HB_DIRECTION_IS_BACKWARD (buffer->props.direction)) |
michael@0 | 364 | pPos[-1].x_advance += gr_seg_advance_X(seg) - curradvx; |
michael@0 | 365 | |
michael@0 | 366 | if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction)) |
michael@0 | 367 | hb_buffer_reverse_clusters (buffer); |
michael@0 | 368 | |
michael@0 | 369 | if (feats) gr_featureval_destroy (feats); |
michael@0 | 370 | gr_seg_destroy (seg); |
michael@0 | 371 | |
michael@0 | 372 | return true; |
michael@0 | 373 | } |