xref: /aosp_15_r20/frameworks/minikin/libs/minikin/FontFakery.cpp (revision 834a2baab5fdfc28e9a428ee87c7ea8f6a06a53d)
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