Bug 1932613 - temporarily disable browser_ml_end_to_end.js for permanent failures...
[gecko.git] / gfx / thebes / gfxMathTable.cpp
blob29b1cc71d9934113a72faf963ba160cece9bb0f7
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "gfxMathTable.h"
7 #include "harfbuzz/hb.h"
8 #include "harfbuzz/hb-ot.h"
10 #define FloatToFixed(f) (65536 * (f))
11 #define FixedToFloat(f) ((f) * (1.0 / 65536.0))
13 using namespace mozilla;
15 gfxMathTable::gfxMathTable(hb_face_t* aFace, gfxFloat aSize) {
16 mMathVariantCache.vertical = false;
17 mHBFont = hb_font_create(aFace);
18 if (mHBFont) {
19 hb_font_set_ppem(mHBFont, aSize, aSize);
20 uint32_t scale = FloatToFixed(aSize);
21 hb_font_set_scale(mHBFont, scale, scale);
24 mMathVariantCache.glyphID = 0;
25 ClearCache();
28 gfxMathTable::~gfxMathTable() {
29 if (mHBFont) {
30 hb_font_destroy(mHBFont);
34 gfxFloat gfxMathTable::Constant(MathConstant aConstant) const {
35 int32_t value = hb_ot_math_get_constant(
36 mHBFont, static_cast<hb_ot_math_constant_t>(aConstant));
37 if (aConstant == ScriptPercentScaleDown ||
38 aConstant == ScriptScriptPercentScaleDown ||
39 aConstant == RadicalDegreeBottomRaisePercent) {
40 return value / 100.0;
42 return FixedToFloat(value);
45 gfxFloat gfxMathTable::ItalicsCorrection(uint32_t aGlyphID) const {
46 return FixedToFloat(
47 hb_ot_math_get_glyph_italics_correction(mHBFont, aGlyphID));
50 uint32_t gfxMathTable::VariantsSize(uint32_t aGlyphID, bool aVertical,
51 uint16_t aSize) const {
52 UpdateMathVariantCache(aGlyphID, aVertical);
53 if (aSize < kMaxCachedSizeCount) {
54 return mMathVariantCache.sizes[aSize];
57 // If the size index exceeds the cache size, we just read the value with
58 // hb_ot_math_get_glyph_variants.
59 hb_direction_t direction = aVertical ? HB_DIRECTION_BTT : HB_DIRECTION_LTR;
60 hb_ot_math_glyph_variant_t variant;
61 unsigned int count = 1;
62 hb_ot_math_get_glyph_variants(mHBFont, aGlyphID, direction, aSize, &count,
63 &variant);
64 return count > 0 ? variant.glyph : 0;
67 bool gfxMathTable::VariantsParts(uint32_t aGlyphID, bool aVertical,
68 uint32_t aGlyphs[4]) const {
69 UpdateMathVariantCache(aGlyphID, aVertical);
70 memcpy(aGlyphs, mMathVariantCache.parts, sizeof(mMathVariantCache.parts));
71 return mMathVariantCache.arePartsValid;
74 void gfxMathTable::ClearCache() const {
75 memset(mMathVariantCache.sizes, 0, sizeof(mMathVariantCache.sizes));
76 memset(mMathVariantCache.parts, 0, sizeof(mMathVariantCache.parts));
77 mMathVariantCache.arePartsValid = false;
80 void gfxMathTable::UpdateMathVariantCache(uint32_t aGlyphID,
81 bool aVertical) const {
82 if (aGlyphID == mMathVariantCache.glyphID &&
83 aVertical == mMathVariantCache.vertical)
84 return;
86 mMathVariantCache.glyphID = aGlyphID;
87 mMathVariantCache.vertical = aVertical;
88 ClearCache();
90 // Cache the first size variants.
91 hb_direction_t direction = aVertical ? HB_DIRECTION_BTT : HB_DIRECTION_LTR;
92 hb_ot_math_glyph_variant_t variant[kMaxCachedSizeCount];
93 unsigned int count = kMaxCachedSizeCount;
94 hb_ot_math_get_glyph_variants(mHBFont, aGlyphID, direction, 0, &count,
95 variant);
96 for (unsigned int i = 0; i < count; i++) {
97 mMathVariantCache.sizes[i] = variant[i].glyph;
100 // Try and cache the parts of the glyph assembly.
101 // XXXfredw The structure of the Open Type Math table is a bit more general
102 // than the one currently used by the nsMathMLChar code, so we try to fallback
103 // in reasonable way. We use the approach of the copyComponents function in
104 // github.com/mathjax/MathJax-dev/blob/master/fonts/OpenTypeMath/fontUtil.py
106 // The nsMathMLChar code can use at most 3 non extender pieces (aGlyphs[0],
107 // aGlyphs[1] and aGlyphs[2]) and the extenders between these pieces should
108 // all be the same (aGlyphs[4]). Also, the parts of vertical assembly are
109 // stored from bottom to top in the Open Type MATH table while they are
110 // stored from top to bottom in nsMathMLChar.
112 hb_ot_math_glyph_part_t parts[5];
113 count = std::size(parts);
114 unsigned int offset = 0;
115 if (hb_ot_math_get_glyph_assembly(mHBFont, aGlyphID, direction, offset,
116 &count, parts, NULL) > std::size(parts))
117 return; // Not supported: Too many pieces.
118 if (count <= 0) return; // Not supported: No pieces.
120 // Count the number of non extender pieces
121 uint16_t nonExtenderCount = 0;
122 for (uint16_t i = 0; i < count; i++) {
123 if (!(parts[i].flags & HB_MATH_GLYPH_PART_FLAG_EXTENDER)) {
124 nonExtenderCount++;
127 if (nonExtenderCount > 3) {
128 // Not supported: too many pieces
129 return;
132 // Now browse the list of pieces
134 // 0 = look for a left/bottom glyph
135 // 1 = look for an extender between left/bottom and mid
136 // 2 = look for a middle glyph
137 // 3 = look for an extender between middle and right/top
138 // 4 = look for a right/top glyph
139 // 5 = no more piece expected
140 uint8_t state = 0;
142 // First extender char found.
143 uint32_t extenderChar = 0;
145 for (uint16_t i = 0; i < count; i++) {
146 bool isExtender = parts[i].flags & HB_MATH_GLYPH_PART_FLAG_EXTENDER;
147 uint32_t glyph = parts[i].glyph;
149 if ((state == 1 || state == 2) && nonExtenderCount < 3) {
150 // do not try to find a middle glyph
151 state += 2;
154 if (isExtender) {
155 if (!extenderChar) {
156 extenderChar = glyph;
157 mMathVariantCache.parts[3] = extenderChar;
158 } else if (extenderChar != glyph) {
159 // Not supported: different extenders
160 return;
163 if (state == 0) { // or state == 1
164 // ignore left/bottom piece and multiple successive extenders
165 state = 1;
166 } else if (state == 2) { // or state == 3
167 // ignore middle piece and multiple successive extenders
168 state = 3;
169 } else if (state >= 4) {
170 // Not supported: unexpected extender
171 return;
174 continue;
177 if (state == 0) {
178 // copy left/bottom part
179 mMathVariantCache.parts[aVertical ? 2 : 0] = glyph;
180 state = 1;
181 continue;
184 if (state == 1 || state == 2) {
185 // copy middle part
186 mMathVariantCache.parts[1] = glyph;
187 state = 3;
188 continue;
191 if (state == 3 || state == 4) {
192 // copy right/top part
193 mMathVariantCache.parts[aVertical ? 0 : 2] = glyph;
194 state = 5;
198 mMathVariantCache.arePartsValid = true;