[PATCH 3/5] dwrite: Implement CheckTypographicFeature().

Nikolay Sivov nsivov at codeweavers.com
Mon Jun 8 08:29:45 CDT 2020


Signed-off-by: Nikolay Sivov <nsivov at codeweavers.com>
---
 dlls/dwrite/analyzer.c       |  29 +++-
 dlls/dwrite/dwrite_private.h |   5 +
 dlls/dwrite/opentype.c       | 249 +++++++++++++++++++++++++++++++++++
 dlls/dwrite/shape.c          |  25 ++++
 4 files changed, 304 insertions(+), 4 deletions(-)

diff --git a/dlls/dwrite/analyzer.c b/dlls/dwrite/analyzer.c
index 909d6e91e3e..af4325248a7 100644
--- a/dlls/dwrite/analyzer.c
+++ b/dlls/dwrite/analyzer.c
@@ -1787,12 +1787,33 @@ static HRESULT WINAPI dwritetextanalyzer2_GetTypographicFeatures(IDWriteTextAnal
 };
 
 static HRESULT WINAPI dwritetextanalyzer2_CheckTypographicFeature(IDWriteTextAnalyzer2 *iface,
-        IDWriteFontFace *face, DWRITE_SCRIPT_ANALYSIS sa, const WCHAR *locale, DWRITE_FONT_FEATURE_TAG feature,
+        IDWriteFontFace *fontface, DWRITE_SCRIPT_ANALYSIS sa, const WCHAR *locale, DWRITE_FONT_FEATURE_TAG feature,
         UINT32 glyph_count, const UINT16 *glyphs, UINT8 *feature_applies)
 {
-    FIXME("(%p %u %s %s %u %p %p): stub\n", face, sa.script, debugstr_w(locale), debugstr_tag(feature), glyph_count,
-            glyphs, feature_applies);
-    return E_NOTIMPL;
+    struct scriptshaping_context context = { 0 };
+    const struct dwritescript_properties *props;
+    struct dwrite_fontface *font_obj;
+    HRESULT hr;
+
+    TRACE("%p, %p, %u, %s, %s, %u, %p, %p.\n", iface, fontface, sa.script, debugstr_w(locale), debugstr_tag(feature),
+            glyph_count, glyphs, feature_applies);
+
+    if (sa.script > Script_LastId)
+        return E_INVALIDARG;
+
+    font_obj = unsafe_impl_from_IDWriteFontFace(fontface);
+
+    context.cache = fontface_get_shaping_cache(font_obj);
+    context.language_tag = get_opentype_language(locale);
+    context.glyph_infos = heap_calloc(glyph_count, sizeof(*context.glyph_infos));
+
+    props = &dwritescripts_properties[sa.script];
+
+    hr = shape_check_typographic_feature(&context, props->scripttags, feature, glyph_count, glyphs, feature_applies);
+
+    heap_free(context.glyph_infos);
+
+    return hr;
 }
 
 static const struct IDWriteTextAnalyzer2Vtbl textanalyzervtbl = {
diff --git a/dlls/dwrite/dwrite_private.h b/dlls/dwrite/dwrite_private.h
index 246dab69327..966750c12b4 100644
--- a/dlls/dwrite/dwrite_private.h
+++ b/dlls/dwrite/dwrite_private.h
@@ -591,8 +591,13 @@ extern void opentype_layout_apply_gsub_features(struct scriptshaping_context *co
         unsigned int language_index, const struct shaping_features *features) DECLSPEC_HIDDEN;
 extern void opentype_layout_apply_gpos_features(struct scriptshaping_context *context, unsigned int script_index,
         unsigned int language_index, const struct shaping_features *features) DECLSPEC_HIDDEN;
+extern BOOL opentype_layout_check_feature(struct scriptshaping_context *context, unsigned int script_index,
+        unsigned int language_index, struct shaping_feature *feature, unsigned int glyph_count,
+        const UINT16 *glyphs, UINT8 *feature_applies) DECLSPEC_HIDDEN;
 
 extern HRESULT shape_get_glyphs(struct scriptshaping_context *context, const unsigned int *scripts) DECLSPEC_HIDDEN;
 extern HRESULT shape_get_positions(struct scriptshaping_context *context, const unsigned int *scripts) DECLSPEC_HIDDEN;
 extern HRESULT shape_get_typographic_features(struct scriptshaping_context *context, const unsigned int *scripts,
         unsigned int max_tagcount, unsigned int *actual_tagcount, unsigned int *tags) DECLSPEC_HIDDEN;
+extern HRESULT shape_check_typographic_feature(struct scriptshaping_context *context, const unsigned int *scripts,
+        unsigned int tag, unsigned int glyph_count, const UINT16 *glyphs, UINT8 *feature_applies) DECLSPEC_HIDDEN;
diff --git a/dlls/dwrite/opentype.c b/dlls/dwrite/opentype.c
index a07d12c0f8c..cbd43bad495 100644
--- a/dlls/dwrite/opentype.c
+++ b/dlls/dwrite/opentype.c
@@ -5745,3 +5745,252 @@ void opentype_layout_apply_gsub_features(struct scriptshaping_context *context,
 
     heap_free(lookups.lookups);
 }
+
+static BOOL opentype_layout_contextual_lookup_is_glyph_covered(struct scriptshaping_context *context, UINT16 glyph,
+        unsigned int subtable_offset, unsigned int coverage, unsigned int format)
+{
+    const struct dwrite_fonttable *table = &context->table->table;
+    const UINT16 *offsets;
+    unsigned int count;
+
+    if (format == 1 || format == 2)
+    {
+        if (opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph) != GLYPH_NOT_COVERED)
+            return TRUE;
+    }
+    else if (format == 3)
+    {
+        count = table_read_be_word(table, subtable_offset + 2);
+        if (!count || !(offsets = table_read_ensure(table, subtable_offset + 6, count * sizeof(*offsets))))
+            return FALSE;
+
+        if (opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(offsets[0]), glyph) != GLYPH_NOT_COVERED)
+            return TRUE;
+    }
+
+    return FALSE;
+}
+
+static BOOL opentype_layout_chain_contextual_lookup_is_glyph_covered(struct scriptshaping_context *context, UINT16 glyph,
+        unsigned int subtable_offset, unsigned int coverage, unsigned int format)
+{
+    const struct dwrite_fonttable *table = &context->table->table;
+    unsigned int count, backtrack_count;
+    const UINT16 *offsets;
+
+    if (format == 1 || format == 2)
+    {
+        if (opentype_layout_is_glyph_covered(table, subtable_offset + coverage, glyph) != GLYPH_NOT_COVERED)
+            return TRUE;
+    }
+    else if (format == 3)
+    {
+        backtrack_count = table_read_be_word(table, subtable_offset + 2);
+
+        count = table_read_be_word(table, subtable_offset + 4 + backtrack_count * sizeof(*offsets));
+
+        if (!count || !(offsets = table_read_ensure(table, subtable_offset + 6 + backtrack_count * sizeof(*offsets),
+                count * sizeof(*offsets))))
+            return FALSE;
+
+        if (opentype_layout_is_glyph_covered(table, subtable_offset + GET_BE_WORD(offsets[0]), glyph) != GLYPH_NOT_COVERED)
+            return TRUE;
+    }
+
+    return FALSE;
+}
+
+static BOOL opentype_layout_gsub_lookup_is_glyph_covered(struct scriptshaping_context *context, UINT16 glyph,
+        const struct lookup *lookup)
+{
+    const struct dwrite_fonttable *gsub = &context->table->table;
+    static const unsigned short gsub_formats[] =
+    {
+        0, /* Unused  */
+        1, /* SingleSubst */
+        1, /* MultipleSubst */
+        1, /* AlternateSubst */
+        1, /* LigatureSubst */
+        3, /* ContextSubst */
+        3, /* ChainContextSubst */
+        0, /* Extension, unused */
+        1, /* ReverseChainSubst */
+    };
+    unsigned int i, coverage, lookup_type, format;
+
+    for (i = 0; i < lookup->subtable_count; ++i)
+    {
+        unsigned int subtable_offset = opentype_layout_get_gsubgpos_subtable(context, lookup->offset, i);
+
+        if (lookup->type == GSUB_LOOKUP_EXTENSION_SUBST)
+        {
+            lookup_type = opentype_layout_adjust_extension_subtable(context, &subtable_offset);
+            if (!lookup_type)
+                continue;
+        }
+        else
+            lookup_type = lookup->type;
+
+        format = table_read_be_word(gsub, subtable_offset);
+
+        if (!format || format > ARRAY_SIZE(gsub_formats) || format > gsub_formats[lookup_type])
+            break;
+
+        coverage = table_read_be_word(gsub, subtable_offset + 2);
+
+        switch (lookup_type)
+        {
+            case GSUB_LOOKUP_SINGLE_SUBST:
+            case GSUB_LOOKUP_MULTIPLE_SUBST:
+            case GSUB_LOOKUP_ALTERNATE_SUBST:
+            case GSUB_LOOKUP_LIGATURE_SUBST:
+            case GSUB_LOOKUP_REVERSE_CHAINING_CONTEXTUAL_SUBST:
+
+                if (opentype_layout_is_glyph_covered(gsub, subtable_offset + coverage, glyph) != GLYPH_NOT_COVERED)
+                    return TRUE;
+
+                break;
+
+            case GSUB_LOOKUP_CONTEXTUAL_SUBST:
+
+                if (opentype_layout_contextual_lookup_is_glyph_covered(context, glyph, subtable_offset, coverage, format))
+                    return TRUE;
+
+                break;
+
+            case GSUB_LOOKUP_CHAINING_CONTEXTUAL_SUBST:
+
+                if (opentype_layout_chain_contextual_lookup_is_glyph_covered(context, glyph, subtable_offset, coverage, format))
+                    return TRUE;
+
+                break;
+
+            default:
+                WARN("Unknown lookup type %u.\n", lookup_type);
+        }
+    }
+
+    return FALSE;
+}
+
+static BOOL opentype_layout_gpos_lookup_is_glyph_covered(struct scriptshaping_context *context, UINT16 glyph,
+        const struct lookup *lookup)
+{
+    const struct dwrite_fonttable *gpos = &context->table->table;
+    static const unsigned short gpos_formats[] =
+    {
+        0, /* Unused  */
+        2, /* SinglePos */
+        2, /* PairPos */
+        1, /* CursivePos */
+        1, /* MarkBasePos */
+        1, /* MarkLigPos */
+        1, /* MarkMarkPos */
+        3, /* ContextPos */
+        3, /* ChainContextPos */
+        0, /* Extension, unused */
+    };
+    unsigned int i, coverage, lookup_type, format;
+
+    for (i = 0; i < lookup->subtable_count; ++i)
+    {
+        unsigned int subtable_offset = opentype_layout_get_gsubgpos_subtable(context, lookup->offset, i);
+
+        if (lookup->type == GPOS_LOOKUP_EXTENSION_POSITION)
+        {
+            lookup_type = opentype_layout_adjust_extension_subtable(context, &subtable_offset);
+            if (!lookup_type)
+                continue;
+        }
+        else
+            lookup_type = lookup->type;
+
+        format = table_read_be_word(gpos, subtable_offset);
+
+        if (!format || format > ARRAY_SIZE(gpos_formats) || format > gpos_formats[lookup_type])
+            break;
+
+        coverage = table_read_be_word(gpos, subtable_offset + 2);
+
+        switch (lookup_type)
+        {
+            case GPOS_LOOKUP_SINGLE_ADJUSTMENT:
+            case GPOS_LOOKUP_PAIR_ADJUSTMENT:
+            case GPOS_LOOKUP_CURSIVE_ATTACHMENT:
+            case GPOS_LOOKUP_MARK_TO_BASE_ATTACHMENT:
+            case GPOS_LOOKUP_MARK_TO_LIGATURE_ATTACHMENT:
+            case GPOS_LOOKUP_MARK_TO_MARK_ATTACHMENT:
+
+                if (opentype_layout_is_glyph_covered(gpos, subtable_offset + coverage, glyph) != GLYPH_NOT_COVERED)
+                    return TRUE;
+
+                break;
+
+            case GPOS_LOOKUP_CONTEXTUAL_POSITION:
+
+                if (opentype_layout_contextual_lookup_is_glyph_covered(context, glyph, subtable_offset, coverage, format))
+                    return TRUE;
+
+                break;
+
+            case GPOS_LOOKUP_CONTEXTUAL_CHAINING_POSITION:
+
+                if (opentype_layout_chain_contextual_lookup_is_glyph_covered(context, glyph, subtable_offset, coverage, format))
+                    return TRUE;
+
+                break;
+
+            default:
+                WARN("Unknown lookup type %u.\n", lookup_type);
+        }
+    }
+
+    return FALSE;
+}
+
+typedef BOOL (*p_lookup_is_glyph_covered_func)(struct scriptshaping_context *context, UINT16 glyph, const struct lookup *lookup);
+
+BOOL opentype_layout_check_feature(struct scriptshaping_context *context, unsigned int script_index,
+        unsigned int language_index, struct shaping_feature *feature, unsigned int glyph_count,
+        const UINT16 *glyphs, UINT8 *feature_applies)
+{
+    p_lookup_is_glyph_covered_func func_is_covered;
+    struct shaping_features features = { 0 };
+    struct lookups lookups = { 0 };
+    BOOL ret = FALSE, is_covered;
+    unsigned int i, j, applies;
+
+    features.features = feature;
+    features.count = 1;
+
+    for (i = 0; i < context->glyph_count; ++i)
+        opentype_set_glyph_props(context, i);
+
+    opentype_layout_collect_lookups(context, script_index, language_index, &features, context->table, &lookups);
+
+    func_is_covered = context->table == &context->cache->gsub ? opentype_layout_gsub_lookup_is_glyph_covered :
+            opentype_layout_gpos_lookup_is_glyph_covered;
+
+    for (i = 0; i < lookups.count; ++i)
+    {
+        struct lookup *lookup = &lookups.lookups[i];
+
+        applies = 0;
+        for (j = 0; j < context->glyph_count; ++j)
+        {
+            if (lookup_is_glyph_match(context, j, lookup->flags))
+            {
+                if ((is_covered = func_is_covered(context, glyphs[i], lookup)))
+                    ++applies;
+                feature_applies[j] |= is_covered;
+            }
+        }
+
+        if ((ret = (applies == context->glyph_count)))
+            break;
+    }
+
+    heap_free(lookups.lookups);
+
+    return ret;
+}
diff --git a/dlls/dwrite/shape.c b/dlls/dwrite/shape.c
index 4968cd995d4..82eb5308da6 100644
--- a/dlls/dwrite/shape.c
+++ b/dlls/dwrite/shape.c
@@ -350,3 +350,28 @@ HRESULT shape_get_typographic_features(struct scriptshaping_context *context, co
 
     return t.count <= max_tagcount ? S_OK : E_NOT_SUFFICIENT_BUFFER;
 }
+
+HRESULT shape_check_typographic_feature(struct scriptshaping_context *context, const unsigned int *scripts,
+        unsigned int tag, unsigned int glyph_count, const UINT16 *glyphs, UINT8 *feature_applies)
+{
+    static const unsigned int tables[] = { MS_GSUB_TAG, MS_GPOS_TAG };
+    struct shaping_feature feature = { .tag = tag };
+    unsigned int script_index, language_index;
+    unsigned int i;
+
+    memset(feature_applies, 0, glyph_count * sizeof(*feature_applies));
+
+    for (i = 0; i < ARRAY_SIZE(tables); ++i)
+    {
+        shape_get_script_lang_index(context, scripts, tables[i], &script_index, &language_index);
+        context->table = tables[i] == MS_GSUB_TAG ? &context->cache->gsub : &context->cache->gpos;
+        /* Skip second table if feature applies to all. */
+        if (opentype_layout_check_feature(context, script_index, language_index, &feature, glyph_count,
+                glyphs, feature_applies))
+        {
+            break;
+        }
+    }
+
+    return S_OK;
+}
-- 
2.26.2




More information about the wine-devel mailing list