xref: /aosp_15_r20/system/teeui/libteeui/src/font_rendering.cpp (revision 20bfefbe1966c142a35ae1ab84a8af250b3fd403)
1*20bfefbeSAndroid Build Coastguard Worker /*
2*20bfefbeSAndroid Build Coastguard Worker  *
3*20bfefbeSAndroid Build Coastguard Worker  * Copyright 2019, The Android Open Source Project
4*20bfefbeSAndroid Build Coastguard Worker  *
5*20bfefbeSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
6*20bfefbeSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
7*20bfefbeSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
8*20bfefbeSAndroid Build Coastguard Worker  *
9*20bfefbeSAndroid Build Coastguard Worker  *     http://www.apache.org/licenses/LICENSE-2.0
10*20bfefbeSAndroid Build Coastguard Worker  *
11*20bfefbeSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
12*20bfefbeSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
13*20bfefbeSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14*20bfefbeSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
15*20bfefbeSAndroid Build Coastguard Worker  * limitations under the License.
16*20bfefbeSAndroid Build Coastguard Worker  */
17*20bfefbeSAndroid Build Coastguard Worker 
18*20bfefbeSAndroid Build Coastguard Worker #include <teeui/font_rendering.h>
19*20bfefbeSAndroid Build Coastguard Worker 
20*20bfefbeSAndroid Build Coastguard Worker namespace teeui {
21*20bfefbeSAndroid Build Coastguard Worker 
isBreakable(unsigned long codePoint)22*20bfefbeSAndroid Build Coastguard Worker bool isBreakable(unsigned long codePoint) {
23*20bfefbeSAndroid Build Coastguard Worker     switch (codePoint) {
24*20bfefbeSAndroid Build Coastguard Worker     case 9:
25*20bfefbeSAndroid Build Coastguard Worker     case 0xA:
26*20bfefbeSAndroid Build Coastguard Worker     case 0xB:
27*20bfefbeSAndroid Build Coastguard Worker     case 0xC:
28*20bfefbeSAndroid Build Coastguard Worker     case 0xD:
29*20bfefbeSAndroid Build Coastguard Worker     case 0x20:
30*20bfefbeSAndroid Build Coastguard Worker     case 0x85:
31*20bfefbeSAndroid Build Coastguard Worker     case 0x1680:  // Ogham Space Mark
32*20bfefbeSAndroid Build Coastguard Worker     case 0x180E:  // Mongolian Vowel Separator
33*20bfefbeSAndroid Build Coastguard Worker     case 0x2000:  // EN Quad
34*20bfefbeSAndroid Build Coastguard Worker     case 0x2001:  // EM Quad
35*20bfefbeSAndroid Build Coastguard Worker     case 0x2002:  // EN Space
36*20bfefbeSAndroid Build Coastguard Worker     case 0x2003:  // EM Space
37*20bfefbeSAndroid Build Coastguard Worker     case 0x2004:  // three per em space
38*20bfefbeSAndroid Build Coastguard Worker     case 0x2005:  // four per em space
39*20bfefbeSAndroid Build Coastguard Worker     case 0x2006:  // six per em space
40*20bfefbeSAndroid Build Coastguard Worker     case 0x2008:  // Punctuation space
41*20bfefbeSAndroid Build Coastguard Worker     case 0x2009:  // Thin Space
42*20bfefbeSAndroid Build Coastguard Worker     case 0x200A:  // Hair Space
43*20bfefbeSAndroid Build Coastguard Worker     case 0x200B:  // Zero Width Space
44*20bfefbeSAndroid Build Coastguard Worker     case 0x200C:  // Zero Width Non-Joiner
45*20bfefbeSAndroid Build Coastguard Worker     case 0x200D:  // Zero Width Joiner
46*20bfefbeSAndroid Build Coastguard Worker     case 0x2028:  // Line Separator
47*20bfefbeSAndroid Build Coastguard Worker     case 0x2029:  // Paragraph Separator
48*20bfefbeSAndroid Build Coastguard Worker     case 0x205F:  // Medium Mathematical Space
49*20bfefbeSAndroid Build Coastguard Worker     case 0x3000:  // Ideographic Space
50*20bfefbeSAndroid Build Coastguard Worker         return true;
51*20bfefbeSAndroid Build Coastguard Worker     default:
52*20bfefbeSAndroid Build Coastguard Worker         return false;
53*20bfefbeSAndroid Build Coastguard Worker     }
54*20bfefbeSAndroid Build Coastguard Worker }
55*20bfefbeSAndroid Build Coastguard Worker 
isNewline(unsigned long codePoint)56*20bfefbeSAndroid Build Coastguard Worker bool isNewline(unsigned long codePoint) {
57*20bfefbeSAndroid Build Coastguard Worker     return codePoint == '\n';
58*20bfefbeSAndroid Build Coastguard Worker }
59*20bfefbeSAndroid Build Coastguard Worker 
setCharSize(signed long char_size,unsigned int dpi)60*20bfefbeSAndroid Build Coastguard Worker Error TextFace::setCharSize(signed long char_size, unsigned int dpi) {
61*20bfefbeSAndroid Build Coastguard Worker     if (!face_) return Error::NotInitialized;
62*20bfefbeSAndroid Build Coastguard Worker     auto error = FT_Set_Char_Size(*face_, 0, char_size, 0, dpi);
63*20bfefbeSAndroid Build Coastguard Worker     if (error) return Error::CharSizeNotSet;
64*20bfefbeSAndroid Build Coastguard Worker     return Error::OK;
65*20bfefbeSAndroid Build Coastguard Worker }
66*20bfefbeSAndroid Build Coastguard Worker 
setCharSizeInPix(pxs size)67*20bfefbeSAndroid Build Coastguard Worker Error TextFace::setCharSizeInPix(pxs size) {
68*20bfefbeSAndroid Build Coastguard Worker     if (!face_) return Error::NotInitialized;
69*20bfefbeSAndroid Build Coastguard Worker     if (FT_Set_Pixel_Sizes(*face_, 0, size.count())) {
70*20bfefbeSAndroid Build Coastguard Worker         return Error::CharSizeNotSet;
71*20bfefbeSAndroid Build Coastguard Worker     }
72*20bfefbeSAndroid Build Coastguard Worker     return Error::OK;
73*20bfefbeSAndroid Build Coastguard Worker }
74*20bfefbeSAndroid Build Coastguard Worker 
getCharIndex(unsigned long codePoint)75*20bfefbeSAndroid Build Coastguard Worker GlyphIndex TextFace::getCharIndex(unsigned long codePoint) {
76*20bfefbeSAndroid Build Coastguard Worker     if (!face_) return 0;
77*20bfefbeSAndroid Build Coastguard Worker     return FT_Get_Char_Index(*face_, codePoint);
78*20bfefbeSAndroid Build Coastguard Worker }
79*20bfefbeSAndroid Build Coastguard Worker 
loadGlyph(GlyphIndex index)80*20bfefbeSAndroid Build Coastguard Worker Error TextFace::loadGlyph(GlyphIndex index) {
81*20bfefbeSAndroid Build Coastguard Worker     if (!face_) return Error::NotInitialized;
82*20bfefbeSAndroid Build Coastguard Worker     if (FT_Load_Glyph(*face_, index, FT_LOAD_DEFAULT)) {
83*20bfefbeSAndroid Build Coastguard Worker         return Error::GlyphNotLoaded;
84*20bfefbeSAndroid Build Coastguard Worker     }
85*20bfefbeSAndroid Build Coastguard Worker     return Error::OK;
86*20bfefbeSAndroid Build Coastguard Worker }
87*20bfefbeSAndroid Build Coastguard Worker 
renderGlyph()88*20bfefbeSAndroid Build Coastguard Worker Error TextFace::renderGlyph() {
89*20bfefbeSAndroid Build Coastguard Worker     if (!face_) return Error::NotInitialized;
90*20bfefbeSAndroid Build Coastguard Worker     if (FT_Render_Glyph(face_->glyph, FT_RENDER_MODE_NORMAL)) {
91*20bfefbeSAndroid Build Coastguard Worker         return Error::GlyphNotRendered;
92*20bfefbeSAndroid Build Coastguard Worker     }
93*20bfefbeSAndroid Build Coastguard Worker     return Error::OK;
94*20bfefbeSAndroid Build Coastguard Worker }
95*20bfefbeSAndroid Build Coastguard Worker 
advance() const96*20bfefbeSAndroid Build Coastguard Worker Vec2d<pxs> TextFace::advance() const {
97*20bfefbeSAndroid Build Coastguard Worker     return Vec2d<pxs>(face_->glyph->advance.x / 64.0, face_->glyph->advance.y / 64.0);
98*20bfefbeSAndroid Build Coastguard Worker }
99*20bfefbeSAndroid Build Coastguard Worker 
kern(GlyphIndex previous) const100*20bfefbeSAndroid Build Coastguard Worker Vec2d<pxs> TextFace::kern(GlyphIndex previous) const {
101*20bfefbeSAndroid Build Coastguard Worker     FT_Vector offset = {0, 0};
102*20bfefbeSAndroid Build Coastguard Worker     if (hasKerning_ && previous) {
103*20bfefbeSAndroid Build Coastguard Worker         if (!FT_Get_Kerning(*face_, previous, face_->glyph->glyph_index, FT_KERNING_DEFAULT,
104*20bfefbeSAndroid Build Coastguard Worker                             &offset)) {
105*20bfefbeSAndroid Build Coastguard Worker             offset = {0, 0};
106*20bfefbeSAndroid Build Coastguard Worker         }
107*20bfefbeSAndroid Build Coastguard Worker     }
108*20bfefbeSAndroid Build Coastguard Worker     return {offset.x / 64.0, offset.y / 64.0};
109*20bfefbeSAndroid Build Coastguard Worker }
110*20bfefbeSAndroid Build Coastguard Worker 
getGlyphBBox() const111*20bfefbeSAndroid Build Coastguard Worker optional<Box<pxs>> TextFace::getGlyphBBox() const {
112*20bfefbeSAndroid Build Coastguard Worker     FT_Glyph glyph;
113*20bfefbeSAndroid Build Coastguard Worker     if (!FT_Get_Glyph(face_->glyph, &glyph)) {
114*20bfefbeSAndroid Build Coastguard Worker         FT_BBox cbox;
115*20bfefbeSAndroid Build Coastguard Worker         FT_Glyph_Get_CBox(glyph, ft_glyph_bbox_pixels, &cbox);
116*20bfefbeSAndroid Build Coastguard Worker         FT_Done_Glyph(glyph);
117*20bfefbeSAndroid Build Coastguard Worker         return {{cbox.xMin, -cbox.yMax, cbox.xMax - cbox.xMin, cbox.yMax - cbox.yMin}};
118*20bfefbeSAndroid Build Coastguard Worker     }
119*20bfefbeSAndroid Build Coastguard Worker     return {};
120*20bfefbeSAndroid Build Coastguard Worker }
121*20bfefbeSAndroid Build Coastguard Worker 
create()122*20bfefbeSAndroid Build Coastguard Worker std::tuple<Error, TextContext> TextContext::create() {
123*20bfefbeSAndroid Build Coastguard Worker     std::tuple<Error, TextContext> result;
124*20bfefbeSAndroid Build Coastguard Worker     auto& [rc, lib] = result;
125*20bfefbeSAndroid Build Coastguard Worker     rc = Error::NotInitialized;
126*20bfefbeSAndroid Build Coastguard Worker     FT_Library library = nullptr;
127*20bfefbeSAndroid Build Coastguard Worker     auto error = FT_Init_FreeType(&library);
128*20bfefbeSAndroid Build Coastguard Worker     if (error) return result;
129*20bfefbeSAndroid Build Coastguard Worker     rc = Error::OK;
130*20bfefbeSAndroid Build Coastguard Worker     lib.library_ = Handle(library);
131*20bfefbeSAndroid Build Coastguard Worker     return result;
132*20bfefbeSAndroid Build Coastguard Worker }
133*20bfefbeSAndroid Build Coastguard Worker 
134*20bfefbeSAndroid Build Coastguard Worker std::tuple<Error, Box<pxs>, UTF8Range<const char*>>
findLongestWordSequence(TextFace * face,const UTF8Range<const char * > & text,const Box<pxs> & boundingBox)135*20bfefbeSAndroid Build Coastguard Worker findLongestWordSequence(TextFace* face, const UTF8Range<const char*>& text,
136*20bfefbeSAndroid Build Coastguard Worker                         const Box<pxs>& boundingBox) {
137*20bfefbeSAndroid Build Coastguard Worker     std::tuple<Error, Box<pxs>, UTF8Range<const char*>> result;
138*20bfefbeSAndroid Build Coastguard Worker     auto& [error, bBox, resultRange] = result;
139*20bfefbeSAndroid Build Coastguard Worker 
140*20bfefbeSAndroid Build Coastguard Worker     Vec2d<pxs> pen = {0, 0};
141*20bfefbeSAndroid Build Coastguard Worker     bBox = {pen, {0, 0}};
142*20bfefbeSAndroid Build Coastguard Worker 
143*20bfefbeSAndroid Build Coastguard Worker     GlyphIndex previous = 0;
144*20bfefbeSAndroid Build Coastguard Worker     UTF8WordRange<const char*> wordRange(text);
145*20bfefbeSAndroid Build Coastguard Worker     auto rangeBegin = wordRange.begin();
146*20bfefbeSAndroid Build Coastguard Worker     if (isBreakable((*rangeBegin).codePoint())) {
147*20bfefbeSAndroid Build Coastguard Worker         ++rangeBegin;
148*20bfefbeSAndroid Build Coastguard Worker     }
149*20bfefbeSAndroid Build Coastguard Worker     auto wordEnd = rangeBegin;
150*20bfefbeSAndroid Build Coastguard Worker     auto wordStart = wordEnd;
151*20bfefbeSAndroid Build Coastguard Worker     ++wordEnd;
152*20bfefbeSAndroid Build Coastguard Worker     auto sequenceEnd = *wordStart;
153*20bfefbeSAndroid Build Coastguard Worker     auto lastBreakableBegin = *wordStart;
154*20bfefbeSAndroid Build Coastguard Worker     Box<pxs> currentBox = bBox;
155*20bfefbeSAndroid Build Coastguard Worker     Box<pxs> currentFullWordBox = bBox;
156*20bfefbeSAndroid Build Coastguard Worker     while (wordStart != wordRange.end()) {
157*20bfefbeSAndroid Build Coastguard Worker         auto codePoint = UTF8Range<const char*>::codePoint(**wordStart);
158*20bfefbeSAndroid Build Coastguard Worker         if (isBreakable(codePoint)) {
159*20bfefbeSAndroid Build Coastguard Worker             lastBreakableBegin = *wordStart;
160*20bfefbeSAndroid Build Coastguard Worker             currentFullWordBox = currentBox;
161*20bfefbeSAndroid Build Coastguard Worker         }
162*20bfefbeSAndroid Build Coastguard Worker 
163*20bfefbeSAndroid Build Coastguard Worker         Box<pxs> workingBox = currentBox;
164*20bfefbeSAndroid Build Coastguard Worker         auto c = *wordStart;
165*20bfefbeSAndroid Build Coastguard Worker         bool exceedsBoundingBox = false;
166*20bfefbeSAndroid Build Coastguard Worker         while (c != *wordEnd) {
167*20bfefbeSAndroid Build Coastguard Worker             codePoint = c.codePoint();
168*20bfefbeSAndroid Build Coastguard Worker             auto gindex = face->getCharIndex(codePoint);
169*20bfefbeSAndroid Build Coastguard Worker             if (gindex == 0) {
170*20bfefbeSAndroid Build Coastguard Worker                 error = Error::GlyphNotLoaded;
171*20bfefbeSAndroid Build Coastguard Worker                 return result;
172*20bfefbeSAndroid Build Coastguard Worker             }
173*20bfefbeSAndroid Build Coastguard Worker             error = face->loadGlyph(gindex);
174*20bfefbeSAndroid Build Coastguard Worker             if (error != Error::OK) return result;
175*20bfefbeSAndroid Build Coastguard Worker             pen += face->kern(previous);
176*20bfefbeSAndroid Build Coastguard Worker             if (auto gBox = face->getGlyphBBox()) {
177*20bfefbeSAndroid Build Coastguard Worker                 TEEUI_LOG << "Glyph Box: " << *gBox << ENDL;
178*20bfefbeSAndroid Build Coastguard Worker                 workingBox = workingBox.merge(gBox->translateSelf(pen));
179*20bfefbeSAndroid Build Coastguard Worker                 TEEUI_LOG << "WorkingBox: " << workingBox << ENDL;
180*20bfefbeSAndroid Build Coastguard Worker             } else {
181*20bfefbeSAndroid Build Coastguard Worker                 error = Error::BBoxComputation;
182*20bfefbeSAndroid Build Coastguard Worker                 return result;
183*20bfefbeSAndroid Build Coastguard Worker             }
184*20bfefbeSAndroid Build Coastguard Worker             pen += face->advance();
185*20bfefbeSAndroid Build Coastguard Worker             previous = gindex;
186*20bfefbeSAndroid Build Coastguard Worker             ++c;
187*20bfefbeSAndroid Build Coastguard Worker             if (workingBox.fitsInside(boundingBox)) {
188*20bfefbeSAndroid Build Coastguard Worker                 currentBox = workingBox;
189*20bfefbeSAndroid Build Coastguard Worker                 sequenceEnd = c;
190*20bfefbeSAndroid Build Coastguard Worker             } else {
191*20bfefbeSAndroid Build Coastguard Worker                 exceedsBoundingBox = true;
192*20bfefbeSAndroid Build Coastguard Worker                 TEEUI_LOG << "exceeding bbox" << ENDL;
193*20bfefbeSAndroid Build Coastguard Worker                 break;
194*20bfefbeSAndroid Build Coastguard Worker             }
195*20bfefbeSAndroid Build Coastguard Worker         }
196*20bfefbeSAndroid Build Coastguard Worker         if (exceedsBoundingBox) break;
197*20bfefbeSAndroid Build Coastguard Worker         wordStart = wordEnd;
198*20bfefbeSAndroid Build Coastguard Worker         ++wordEnd;
199*20bfefbeSAndroid Build Coastguard Worker     }
200*20bfefbeSAndroid Build Coastguard Worker     if (wordStart == wordRange.end()) {
201*20bfefbeSAndroid Build Coastguard Worker         bBox = currentBox;
202*20bfefbeSAndroid Build Coastguard Worker         resultRange = {**rangeBegin, *text.end()};
203*20bfefbeSAndroid Build Coastguard Worker         TEEUI_LOG << "full range" << ENDL;
204*20bfefbeSAndroid Build Coastguard Worker     } else if (*rangeBegin != lastBreakableBegin) {
205*20bfefbeSAndroid Build Coastguard Worker         bBox = currentFullWordBox;
206*20bfefbeSAndroid Build Coastguard Worker         resultRange = {**rangeBegin, *lastBreakableBegin};
207*20bfefbeSAndroid Build Coastguard Worker         TEEUI_LOG << "partial range:" << ENDL;
208*20bfefbeSAndroid Build Coastguard Worker     } else {
209*20bfefbeSAndroid Build Coastguard Worker         bBox = currentBox;
210*20bfefbeSAndroid Build Coastguard Worker         resultRange = {**rangeBegin, *sequenceEnd};
211*20bfefbeSAndroid Build Coastguard Worker         TEEUI_LOG << "unbreakable" << ENDL;
212*20bfefbeSAndroid Build Coastguard Worker     }
213*20bfefbeSAndroid Build Coastguard Worker     error = Error::OK;
214*20bfefbeSAndroid Build Coastguard Worker     return result;
215*20bfefbeSAndroid Build Coastguard Worker }
216*20bfefbeSAndroid Build Coastguard Worker 
drawText(TextFace * face,const UTF8Range<const char * > & text,const PixelDrawer & drawPixel,PxPoint pen)217*20bfefbeSAndroid Build Coastguard Worker Error drawText(TextFace* face, const UTF8Range<const char*>& text, const PixelDrawer& drawPixel,
218*20bfefbeSAndroid Build Coastguard Worker                PxPoint pen) {
219*20bfefbeSAndroid Build Coastguard Worker     Error error;
220*20bfefbeSAndroid Build Coastguard Worker 
221*20bfefbeSAndroid Build Coastguard Worker     for (auto c : text) {
222*20bfefbeSAndroid Build Coastguard Worker         auto codePoint = UTF8Range<const char*>::codePoint(c);
223*20bfefbeSAndroid Build Coastguard Worker         auto gindex = face->getCharIndex(codePoint);
224*20bfefbeSAndroid Build Coastguard Worker         error = face->loadGlyph(gindex);
225*20bfefbeSAndroid Build Coastguard Worker         if (error == Error::OK) error = face->renderGlyph();
226*20bfefbeSAndroid Build Coastguard Worker         if (error == Error::OK) error = face->drawGlyph(pen, drawPixel);
227*20bfefbeSAndroid Build Coastguard Worker         if (error != Error::OK) return error;
228*20bfefbeSAndroid Build Coastguard Worker 
229*20bfefbeSAndroid Build Coastguard Worker         pen += face->advance();
230*20bfefbeSAndroid Build Coastguard Worker     }
231*20bfefbeSAndroid Build Coastguard Worker     return Error::OK;
232*20bfefbeSAndroid Build Coastguard Worker }
233*20bfefbeSAndroid Build Coastguard Worker 
234*20bfefbeSAndroid Build Coastguard Worker }  //  namespace teeui
235