1 /*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "face_data.h"
17
18 #include <algorithm>
19 #include <freetype/ftbitmap.h>
20 #include <freetype/ftsizes.h>
21
22 #include <core/log.h>
23
24 #include "font_buffer.h"
25 #include "font_data.h"
26 #include "font_manager.h"
27
28 using namespace BASE_NS;
29
30 FONT_BEGIN_NAMESPACE()
31
32 using namespace FontDefs;
33
FaceData(std::shared_ptr<FontBuffer> buf,FT_Face ftFace)34 FaceData::FaceData(std::shared_ptr<FontBuffer> buf, FT_Face ftFace) : fontBuffer_(buf), face_(ftFace) {}
35
~FaceData()36 FaceData::~FaceData()
37 {
38 {
39 std::lock_guard writerLock(mutex_);
40 // FontData must be destroyed first as it has a FT_Size which is used by FT_Face.
41 datas_.clear();
42 FT_Done_Face(face_);
43 }
44 fontBuffer_->Gc();
45 }
46
GetFontManager() const47 FontManager& FaceData::GetFontManager() const
48 {
49 return *(fontBuffer_->fontManager);
50 }
51
CreateFontData(float sizeInPt,uint16_t xDpi,uint16_t yDpi,bool sdf)52 FontData* FaceData::CreateFontData(float sizeInPt, uint16_t xDpi, uint16_t yDpi, bool sdf)
53 {
54 const FT_Pos pixelSize26Dot6 = FloatToFTPos(sizeInPt * yDpi / 72.f); // 72.0 : param
55 const int64_t pixelSize = (pixelSize26Dot6 >> 6) | (sdf ? (1ll << 32) : 0); // 6 32 : param
56
57 {
58 std::shared_lock readerLock(mutex_);
59 if (auto it = std::find_if(
60 datas_.cbegin(), datas_.cend(), [pixelSize](const Data& data) { return data.pixelSize == pixelSize; });
61 it != datas_.cend()) {
62 return it->fontData.get();
63 }
64 }
65
66 std::lock_guard writerLock(mutex_);
67 if (auto it = std::find_if(
68 datas_.cbegin(), datas_.cend(), [pixelSize](const Data& data) { return data.pixelSize == pixelSize; });
69 it != datas_.cend()) {
70 return it->fontData.get();
71 }
72
73 auto fontData = FontData::CreateFromFaceData(weak_from_this(), sdf);
74 if (!fontData) {
75 return nullptr;
76 }
77
78 FT_Error err = FT_Err_Ok;
79 if (FT_IS_SCALABLE(face_)) {
80 err = FT_Set_Char_Size(face_, 0, FloatToFTPos(sizeInPt), xDpi, yDpi);
81 } else if (FT_HAS_FIXED_SIZES(face_)) {
82 auto* sizes = face_->available_sizes;
83 int closestIdx = 0;
84 auto smallestDiff = std::abs(sizes[0].y_ppem - pixelSize26Dot6);
85 if (smallestDiff > 0) {
86 for (FT_Int i = 1; i < face_->num_fixed_sizes; i++) {
87 auto diff = std::abs(sizes[i].y_ppem - pixelSize26Dot6);
88 if (diff < smallestDiff) {
89 smallestDiff = diff;
90 closestIdx = i;
91 if (!smallestDiff) { // exact
92 break;
93 }
94 }
95 }
96 if (smallestDiff) {
97 CORE_LOG_N("use of closest match for bitmap font, request: %dpx , selected: %dpx",
98 pixelSize26Dot6 >> 6, sizes[closestIdx].y_ppem >> 6); // 6 : param
99 }
100 }
101 err = FT_Select_Size(face_, closestIdx);
102 } else {
103 CORE_LOG_E("face not scallable and no bitmap size available");
104 }
105
106 if (err) {
107 CORE_LOG_E("failed to select font face size: %d", err);
108 return nullptr;
109 }
110
111 auto fontDataPtr = fontData.get();
112 datas_.push_back(Data { pixelSize, std::move(fontData) });
113
114 CORE_LOG_N("create FontData PT: %f dpi: %d (pix: %d, yppem: %d, h: %d) %p", sizeInPt, yDpi, pixelSize,
115 fontDataPtr->sizeData_->metrics.y_ppem, fontDataPtr->sizeData_->metrics.height >> 6, this); // 6 : param
116 return fontDataPtr;
117 }
118
UpdateGlyph(bool sdf,FT_Size ftSize,uint32_t glyphIndex,FontDefs::Glyph & glyph)119 int FaceData::UpdateGlyph(bool sdf, FT_Size ftSize, uint32_t glyphIndex, FontDefs::Glyph& glyph)
120 {
121 std::lock_guard writerLock(mutex_);
122
123 // if face's active size is different, try to activate this one
124 if (face_->size != ftSize && FT_Activate_Size(ftSize)) {
125 return FontDefs::ATLAS_ERROR;
126 }
127
128 FT_Int32 flags = FT_LOAD_RENDER;
129
130 if (sdf == true) {
131 flags |= FT_LOAD_TARGET_(FT_RENDER_MODE_SDF);
132 } else if (FT_HAS_COLOR(face_)) {
133 flags |= FT_LOAD_COLOR;
134 }
135
136 if (FT_Load_Glyph(face_, glyphIndex, flags)) {
137 return FontDefs::ATLAS_ERROR;
138 }
139
140 FT_Glyph bmp;
141 if (FT_Get_Glyph(face_->glyph, &bmp)) {
142 return FontDefs::ATLAS_ERROR;
143 }
144
145 // sanity check
146 if (bmp->format != FT_GLYPH_FORMAT_BITMAP) {
147 FT_Done_Glyph(bmp);
148 return FontDefs::ATLAS_ERROR;
149 }
150
151 FT_BBox bbox;
152 FT_Glyph_Get_CBox(bmp, FT_GLYPH_BBOX_UNSCALED, &bbox);
153
154 glyph.xMin = FontDefs::FTPosToInt16(bbox.xMin);
155 glyph.xMax = FontDefs::FTPosToInt16(bbox.xMax);
156 glyph.yMin = FontDefs::FTPosToInt16(bbox.yMin);
157 glyph.yMax = FontDefs::FTPosToInt16(bbox.yMax);
158 glyph.hlsb = FontDefs::FTPosToInt16(face_->glyph->metrics.horiBearingX);
159 glyph.htsb = FontDefs::FTPosToInt16(face_->glyph->metrics.horiBearingY);
160 glyph.hAdv = FontDefs::FTPosToInt16(face_->glyph->metrics.horiAdvance);
161
162 if (FT_HAS_VERTICAL(face_)) {
163 glyph.vlsb = FontDefs::FTPosToInt16(face_->glyph->metrics.vertBearingX);
164 glyph.vtsb = FontDefs::FTPosToInt16(face_->glyph->metrics.vertBearingY);
165 glyph.vAdv = FontDefs::FTPosToInt16(face_->glyph->metrics.vertAdvance);
166 }
167
168 glyph.atlas.rect.w = 0;
169 bool isColor = false;
170 bool needsConversion = false;
171 switch (((FT_BitmapGlyph)bmp)->bitmap.pixel_mode) {
172 case FT_PIXEL_MODE_MONO:
173 needsConversion = true;
174 break;
175
176 case FT_PIXEL_MODE_GRAY:
177 needsConversion = false;
178 break;
179
180 case FT_PIXEL_MODE_GRAY2:
181 needsConversion = true;
182 break;
183
184 case FT_PIXEL_MODE_GRAY4:
185 needsConversion = true;
186 break;
187
188 case FT_PIXEL_MODE_BGRA:
189 needsConversion = false;
190 isColor = true;
191 break;
192
193 case FT_PIXEL_MODE_NONE:
194 case FT_PIXEL_MODE_LCD:
195 case FT_PIXEL_MODE_LCD_V:
196 case FT_PIXEL_MODE_MAX:
197 CORE_LOG_E("unhandled pixel mode");
198 return FontDefs::ATLAS_ERROR;
199 }
200
201 FT_Bitmap converted;
202 if (needsConversion) {
203 FT_Bitmap_Init(&converted);
204 FT_Bitmap_Convert(bmp->library, &((FT_BitmapGlyph)bmp)->bitmap, &converted, 1);
205
206 if (converted.num_grays == 2U) { // 2 : param
207 // convert created a binary bitmap, expand to 0..255 range.
208 std::transform(converted.buffer, converted.buffer + converted.pitch * converted.rows, converted.buffer,
209 [](const uint8_t& c) { return (c) ? uint8_t(0xffU) : uint8_t(0x00U); });
210 } else if (converted.num_grays == 4U) { // 4 ;param
211 // convert created a 2 bit bitmap, expand to 0..255 range.
212 std::transform(converted.buffer, converted.buffer + converted.pitch * converted.rows, converted.buffer,
213 [](const uint8_t& c) {
214 auto half = (c << 2U) | c; // 2 : param
215 return uint8_t((half << 4U) | half); // 4 : param
216 });
217 } else if (converted.num_grays == 16U) { // 16 : channel
218 // convert created a 4 bit bitmap, expand to 0..255 range.
219 std::transform(converted.buffer, converted.buffer + converted.pitch * converted.rows, converted.buffer,
220 [](const uint8_t& c) { return uint8_t((c << 4U) | c); }); // 4 : param
221 }
222 }
223
224 GetFontManager().UpdateAtlas(glyph, ((FT_BitmapGlyph)bmp)->bitmap, isColor);
225
226 if (needsConversion) {
227 FT_Bitmap_Done(bmp->library, &converted);
228 }
229
230 FT_Done_Glyph(bmp);
231 return 0;
232 }
233
GetGlyphIndex(uint32_t utfCode)234 uint32_t FaceData::GetGlyphIndex(uint32_t utfCode)
235 {
236 std::shared_lock readerLock(mutex_);
237
238 return FT_Get_Char_Index(face_, utfCode);
239 }
240 FONT_END_NAMESPACE()
241