From 14f220b76159a2f2d926029e32b3fe975d1dc525 Mon Sep 17 00:00:00 2001 From: Khaled Hosny Date: Sun, 20 Jun 2021 18:59:13 +0200 Subject: [PATCH] [directwrite] Support feature ranges Code copied from hb-uniscribe.cc and adapted to use DirectWrite types. May be there is a better way to de-duplicate the code, though I'm not exactly sure how or if it is worth it. --- src/hb-directwrite.cc | 198 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 180 insertions(+), 18 deletions(-) diff --git a/src/hb-directwrite.cc b/src/hb-directwrite.cc index a362cea6c..937e2f6d2 100644 --- a/src/hb-directwrite.cc +++ b/src/hb-directwrite.cc @@ -510,6 +510,44 @@ protected: * shaper */ +struct active_feature_t { + DWRITE_FONT_FEATURE fea; + unsigned int order; + + HB_INTERNAL static int cmp (const void *pa, const void *pb) { + const active_feature_t *a = (const active_feature_t *) pa; + const active_feature_t *b = (const active_feature_t *) pb; + return a->fea.nameTag < b->fea.nameTag ? -1 : a->fea.nameTag > b->fea.nameTag ? 1 : + a->order < b->order ? -1 : a->order > b->order ? 1 : + a->fea.parameter < b->fea.parameter ? -1 : a->fea.parameter > b->fea.parameter ? 1 : + 0; + } + bool operator== (const active_feature_t *f) + { return cmp (this, f) == 0; } +}; + +struct feature_event_t { + unsigned int index; + bool start; + active_feature_t feature; + + HB_INTERNAL static int cmp (const void *pa, const void *pb) + { + const feature_event_t *a = (const feature_event_t *) pa; + const feature_event_t *b = (const feature_event_t *) pb; + return a->index < b->index ? -1 : a->index > b->index ? 1 : + a->start < b->start ? -1 : a->start > b->start ? 1 : + active_feature_t::cmp (&a->feature, &b->feature); + } +}; + +struct range_record_t { + DWRITE_TYPOGRAPHIC_FEATURES features; + unsigned int index_first; /* == start */ + unsigned int index_last; /* == end - 1 */ +}; + + hb_bool_t _hb_directwrite_shape (hb_shape_plan_t *shape_plan, hb_font_t *font, @@ -568,8 +606,6 @@ _hb_directwrite_shape (hb_shape_plan_t *shape_plan, log_clusters[chars_len++] = cluster; /* Surrogates. */ } - // TODO: Handle TEST_DISABLE_OPTIONAL_LIGATURES - DWRITE_READING_DIRECTION readingDirection; readingDirection = buffer->props.direction ? DWRITE_READING_DIRECTION_RIGHT_TO_LEFT : @@ -605,28 +641,157 @@ _hb_directwrite_shape (hb_shape_plan_t *shape_plan, mbstowcs ((wchar_t*) localeName, hb_language_to_string (buffer->props.language), 20); - // TODO: it does work but doesn't care about ranges - DWRITE_TYPOGRAPHIC_FEATURES typographic_features; - typographic_features.featureCount = num_features; + /* + * Set up features. + */ + hb_vector_t feature_records; + hb_vector_t range_records; if (num_features) { - typographic_features.features = new DWRITE_FONT_FEATURE[num_features]; - for (unsigned int i = 0; i < num_features; ++i) + /* Sort features by start/end events. */ + hb_vector_t feature_events; + for (unsigned int i = 0; i < num_features; i++) { - typographic_features.features[i].nameTag = (DWRITE_FONT_FEATURE_TAG) - hb_uint32_swap (features[i].tag); - typographic_features.features[i].parameter = features[i].value; + active_feature_t feature; + feature.fea.nameTag = (DWRITE_FONT_FEATURE_TAG) hb_uint32_swap (features[i].tag); + feature.fea.parameter = features[i].value; + feature.order = i; + + feature_event_t *event; + + event = feature_events.push (); + event->index = features[i].start; + event->start = true; + event->feature = feature; + + event = feature_events.push (); + event->index = features[i].end; + event->start = false; + event->feature = feature; + } + feature_events.qsort (); + /* Add a strategic final event. */ + { + active_feature_t feature; + feature.fea.nameTag = (DWRITE_FONT_FEATURE_TAG) 0; + feature.fea.parameter = 0; + feature.order = num_features + 1; + + feature_event_t *event = feature_events.push (); + event->index = 0; /* This value does magic. */ + event->start = false; + event->feature = feature; + } + + /* Scan events and save features for each range. */ + hb_vector_t active_features; + unsigned int last_index = 0; + for (unsigned int i = 0; i < feature_events.length; i++) + { + feature_event_t *event = &feature_events[i]; + + if (event->index != last_index) + { + /* Save a snapshot of active features and the range. */ + range_record_t *range = range_records.push (); + + unsigned int offset = feature_records.length; + + active_features.qsort (); + for (unsigned int j = 0; j < active_features.length; j++) + { + if (!j || active_features[j].fea.nameTag != feature_records[feature_records.length - 1].nameTag) + { + feature_records.push (active_features[j].fea); + } + else + { + /* Overrides value for existing feature. */ + feature_records[feature_records.length - 1].parameter = active_features[j].fea.parameter; + } + } + + /* Will convert to pointer after all is ready, since feature_records.array + * may move as we grow it. */ + range->features.features = reinterpret_cast (offset); + range->features.featureCount = feature_records.length - offset; + range->index_first = last_index; + range->index_last = event->index - 1; + + last_index = event->index; + } + + if (event->start) + { + active_features.push (event->feature); + } + else + { + active_feature_t *feature = active_features.find (&event->feature); + if (feature) + active_features.remove (feature - active_features.arrayZ); + } + } + + if (!range_records.length) /* No active feature found. */ + num_features = 0; + + /* Fixup the pointers. */ + for (unsigned int i = 0; i < range_records.length; i++) + { + range_record_t *range = &range_records[i]; + range->features.features = (DWRITE_FONT_FEATURE *) feature_records + reinterpret_cast (range->features.features); } } - const DWRITE_TYPOGRAPHIC_FEATURES* dwFeatures; - dwFeatures = (const DWRITE_TYPOGRAPHIC_FEATURES*) &typographic_features; - const uint32_t featureRangeLengths[] = { textLength }; + + hb_vector_t range_features; + hb_vector_t range_char_counts; + if (num_features) + { + range_features.shrink (0); + range_char_counts.shrink (0); + + range_record_t *last_range = &range_records[0]; + for (unsigned int i = 0; i < textLength; i++) + { + range_record_t *range = last_range; + while (log_clusters[i] < range->index_first) + range--; + while (log_clusters[i] > range->index_last) + range++; + if (!range_features.length || + &range->features != range_features[range_features.length - 1]) + { + auto **typoFeatures = range_features.push (); + auto *c = range_char_counts.push (); + if (unlikely (!typoFeatures || !c)) + { + range_features.shrink (0); + range_char_counts.shrink (0); + break; + } + *typoFeatures = &range->features; + *c = 1; + } + else + { + range_char_counts[range_char_counts.length - 1]++; + } + + last_range = range; + } + } + + const auto **dwFeatures = (const DWRITE_TYPOGRAPHIC_FEATURES**) range_features.arrayZ; + auto featureRanges = range_features.length; + const auto *featureRangeLengths = range_char_counts.arrayZ; // uint16_t* clusterMap; clusterMap = new uint16_t[textLength]; DWRITE_SHAPING_TEXT_PROPERTIES* textProperties; textProperties = new DWRITE_SHAPING_TEXT_PROPERTIES[textLength]; + retry_getglyphs: uint16_t* glyphIndices = new uint16_t[maxGlyphCount]; DWRITE_SHAPING_GLYPH_PROPERTIES* glyphProperties; @@ -634,7 +799,7 @@ retry_getglyphs: hr = analyzer->GetGlyphs (textString, textLength, fontFace, false, isRightToLeft, &runHead->mScript, localeName, - nullptr, &dwFeatures, featureRangeLengths, 1, + nullptr, dwFeatures, featureRangeLengths, featureRanges, maxGlyphCount, clusterMap, textProperties, glyphIndices, glyphProperties, &glyphCount); @@ -676,7 +841,7 @@ retry_getglyphs: textLength, glyphIndices, glyphProperties, glyphCount, fontFace, fontEmSize, false, isRightToLeft, &runHead->mScript, localeName, - &dwFeatures, featureRangeLengths, 1, + dwFeatures, featureRangeLengths, featureRanges, glyphAdvances, glyphOffsets); if (FAILED (hr)) @@ -742,9 +907,6 @@ retry_getglyphs: delete [] glyphAdvances; delete [] glyphOffsets; - if (num_features) - delete [] typographic_features.features; - /* Wow, done! */ return true; }