1 /*
2 * Copyright (C) 2024 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "minikin/FontFakery.h"
18
19 #include "minikin/Constants.h"
20 #include "minikin/FVarTable.h"
21 #include "minikin/FontStyle.h"
22 #include "minikin/FontVariation.h"
23
24 namespace minikin {
25
merge(const FVarTable & fvar,const VariationSettings & baseVS,const VariationSettings & targetVS,FontStyle baseStyle,FontStyle targetStyle)26 FontFakery merge(const FVarTable& fvar, const VariationSettings& baseVS,
27 const VariationSettings& targetVS, FontStyle baseStyle, FontStyle targetStyle) {
28 const bool hasItal = fvar.count(TAG_ital);
29 const bool hasSlnt = fvar.count(TAG_slnt);
30 const bool hasWght = fvar.count(TAG_wght);
31
32 // Reserve size of base and target plus 2 which is the upper bounds resolved axes.
33 FontVariation* adjustedVars;
34 constexpr uint32_t FIXED_BUFFER_SIZE = 8;
35 FontVariation fixedBuffer[FIXED_BUFFER_SIZE];
36 std::unique_ptr<FontVariation[]> heap;
37 if (baseVS.size() + targetVS.size() + 2 > FIXED_BUFFER_SIZE) {
38 heap = std::make_unique<FontVariation[]>(baseVS.size() + targetVS.size() + 2);
39 adjustedVars = heap.get();
40 } else {
41 adjustedVars = fixedBuffer;
42 }
43
44 // Convert target font style into font variation settings.
45 FontVariation styleVars[2];
46 uint32_t styleVarsSize = 0;
47 if (hasSlnt) {
48 if (targetStyle.slant() == FontStyle::Slant::ITALIC) {
49 styleVars[styleVarsSize++] = {TAG_slnt, -10};
50 } else {
51 styleVars[styleVarsSize++] = {TAG_slnt, 0};
52 }
53 } else if (hasItal) {
54 if (targetStyle.slant() == FontStyle::Slant::ITALIC) {
55 styleVars[styleVarsSize++] = {TAG_ital, 1};
56 } else {
57 styleVars[styleVarsSize++] = {TAG_ital, 0};
58 }
59 }
60 if (hasWght) {
61 styleVars[styleVarsSize++] = {TAG_wght, static_cast<float>(targetStyle.weight())};
62 }
63
64 // Main merge loop: do the three sorted array merge.
65 constexpr uint32_t END = 0xFFFFFFFF;
66 bool fakeBold;
67 uint32_t baseIdx = 0;
68 uint32_t targetIdx = 0;
69 uint32_t styleIdx = 0;
70
71 uint32_t adjustedHead = 0; // head of the output vector.
72 while (baseIdx < baseVS.size() || targetIdx < targetVS.size() || styleIdx < styleVarsSize) {
73 const AxisTag baseTag = baseIdx < baseVS.size() ? baseVS[baseIdx].axisTag : END;
74 const AxisTag targetTag = targetIdx < targetVS.size() ? targetVS[targetIdx].axisTag : END;
75 const AxisTag styleTag = styleIdx < styleVarsSize ? styleVars[styleIdx].axisTag : END;
76
77 AxisTag tag;
78 float value;
79 bool styleValueUsed = false;
80 if (baseTag < targetTag) {
81 if (styleTag < baseTag) {
82 // style < base < target: only process style.
83 tag = styleTag;
84 value = styleVars[styleIdx].value;
85 styleValueUsed = true;
86 styleIdx++;
87 } else if (styleTag == baseTag) {
88 // style == base < target: process base and style. base is used.
89 tag = styleTag;
90 value = baseVS[baseIdx].value;
91 baseIdx++;
92 styleIdx++;
93 } else {
94 // base < style < target: only process base.
95 tag = baseTag;
96 value = baseVS[baseIdx].value;
97 baseIdx++;
98 }
99 } else if (targetTag < baseTag) {
100 if (styleTag < targetTag) {
101 // style < target < base: process style only.
102 tag = styleTag;
103 value = styleVars[styleIdx].value;
104 styleValueUsed = true;
105 styleIdx++;
106 } else if (styleTag == targetTag) {
107 // style = target < base: process style and target. target is used.
108 tag = targetTag;
109 value = targetVS[targetIdx].value;
110 styleIdx++;
111 targetIdx++;
112 } else {
113 // target < style < base: process target only.
114 tag = targetTag;
115 value = targetVS[targetIdx].value;
116 targetIdx++;
117 }
118 } else {
119 if (styleTag < baseTag) {
120 // style < base == target: only process style.
121 tag = styleTag;
122 value = styleVars[styleIdx].value;
123 styleValueUsed = true;
124 styleIdx++;
125 } else if (styleTag == baseTag) {
126 // base == target == style: process all. target is used.
127 tag = targetTag;
128 value = targetVS[targetIdx].value;
129 baseIdx++;
130 targetIdx++;
131 styleIdx++;
132 } else {
133 // base == target < style: process base and target. target is used.
134 tag = targetTag;
135 value = targetVS[targetIdx].value;
136 baseIdx++;
137 targetIdx++;
138 }
139 }
140
141 const auto& it = fvar.find(tag);
142 if (it == fvar.end()) {
143 continue; // unsupported axis. Skip.
144 }
145 const FVarEntry& fvarEntry = it->second;
146
147 if (styleValueUsed && value == fvarEntry.defValue) {
148 // Skip the default value if it came from style.
149 continue;
150 }
151 const float clamped = std::clamp(value, fvarEntry.minValue, fvarEntry.maxValue);
152 adjustedVars[adjustedHead++] = {tag, clamped};
153 if (tag == TAG_wght) {
154 // Fake bold is enabled when the max value is more than 200 of difference.
155 fakeBold = targetStyle.weight() >= 600 && (targetStyle.weight() - clamped) >= 200;
156 }
157 }
158
159 // Fake weight is enabled when the TAG_wght is not supported and the weight value has more than
160 // 200 of difference.
161 if (!hasWght) {
162 fakeBold =
163 targetStyle.weight() >= 600 && (targetStyle.weight() - baseStyle.weight()) >= 200;
164 }
165 // Fake italic is enabled when the style is italic and font doesn't support ital or slnt axis.
166 bool fakeItalic = false;
167 if (targetStyle.isItalic()) {
168 if (hasItal || hasSlnt) {
169 fakeItalic = false;
170 } else {
171 fakeItalic = !baseStyle.isItalic();
172 }
173 }
174 return FontFakery(fakeBold, fakeItalic, VariationSettings(adjustedVars, adjustedHead));
175 }
176
177 } // namespace minikin
178