1f4f30311SClemens Zeidler /*
2a4dfc6aaSMáximo Castañeda * Copyright 2007-2022, Haiku. All rights reserved.
3f4f30311SClemens Zeidler * Distributed under the terms of the MIT License.
4f4f30311SClemens Zeidler *
5f4f30311SClemens Zeidler * Authors:
6f4f30311SClemens Zeidler * Stephan Aßmus <superstippi@gmx.de>
7f4f30311SClemens Zeidler */
8f4f30311SClemens Zeidler
9f4f30311SClemens Zeidler #ifndef GLYPH_LAYOUT_ENGINE_H
10f4f30311SClemens Zeidler #define GLYPH_LAYOUT_ENGINE_H
11f4f30311SClemens Zeidler
12f4f30311SClemens Zeidler #include "utf8_functions.h"
13f4f30311SClemens Zeidler
14f4f30311SClemens Zeidler #include "FontCache.h"
15f4f30311SClemens Zeidler #include "FontCacheEntry.h"
1685b82f85SDale Cieslak #include "GlobalFontManager.h"
17f4f30311SClemens Zeidler #include "ServerFont.h"
18f4f30311SClemens Zeidler
19290fd41dSPascal Abresch #include <Autolock.h>
20f4f30311SClemens Zeidler #include <Debug.h>
21a1c9aa9dSMáximo Castañeda #include <ObjectList.h>
22a1c9aa9dSMáximo Castañeda #include <SupportDefs.h>
23f4f30311SClemens Zeidler
24f4f30311SClemens Zeidler #include <ctype.h>
25f4f30311SClemens Zeidler
26f4f30311SClemens Zeidler class FontCacheReference {
27f4f30311SClemens Zeidler public:
FontCacheReference()28f4f30311SClemens Zeidler FontCacheReference()
29f4f30311SClemens Zeidler :
30f4f30311SClemens Zeidler fCacheEntry(NULL),
31a4dfc6aaSMáximo Castañeda fFallbackReference(NULL),
32a4dfc6aaSMáximo Castañeda fLocked(false),
33f4f30311SClemens Zeidler fWriteLocked(false)
34f4f30311SClemens Zeidler {
35f4f30311SClemens Zeidler }
36f4f30311SClemens Zeidler
~FontCacheReference()37f4f30311SClemens Zeidler ~FontCacheReference()
38f4f30311SClemens Zeidler {
39f4f30311SClemens Zeidler if (fCacheEntry == NULL)
40f4f30311SClemens Zeidler return;
41f4f30311SClemens Zeidler
42a4dfc6aaSMáximo Castañeda fFallbackReference = NULL;
43a4dfc6aaSMáximo Castañeda Unlock();
44a4dfc6aaSMáximo Castañeda if (fCacheEntry != NULL)
45a4dfc6aaSMáximo Castañeda FontCache::Default()->Recycle(fCacheEntry);
46a4dfc6aaSMáximo Castañeda }
47a4dfc6aaSMáximo Castañeda
SetTo(FontCacheEntry * entry)48a4dfc6aaSMáximo Castañeda void SetTo(FontCacheEntry* entry)
49a4dfc6aaSMáximo Castañeda {
50a4dfc6aaSMáximo Castañeda ASSERT(entry != NULL);
51a4dfc6aaSMáximo Castañeda ASSERT(fCacheEntry == NULL);
52a4dfc6aaSMáximo Castañeda
53a4dfc6aaSMáximo Castañeda fCacheEntry = entry;
54a4dfc6aaSMáximo Castañeda }
55a4dfc6aaSMáximo Castañeda
ReadLock()56a4dfc6aaSMáximo Castañeda bool ReadLock()
57a4dfc6aaSMáximo Castañeda {
58a4dfc6aaSMáximo Castañeda ASSERT(fCacheEntry != NULL);
59a4dfc6aaSMáximo Castañeda ASSERT(fWriteLocked == false);
60a4dfc6aaSMáximo Castañeda
61a4dfc6aaSMáximo Castañeda if (fLocked)
62a4dfc6aaSMáximo Castañeda return true;
63a4dfc6aaSMáximo Castañeda
64a4dfc6aaSMáximo Castañeda if (!fCacheEntry->ReadLock()) {
65a4dfc6aaSMáximo Castañeda _Cleanup();
66a4dfc6aaSMáximo Castañeda return false;
67a4dfc6aaSMáximo Castañeda }
68a4dfc6aaSMáximo Castañeda
69a4dfc6aaSMáximo Castañeda fLocked = true;
70a4dfc6aaSMáximo Castañeda return true;
71a4dfc6aaSMáximo Castañeda }
72a4dfc6aaSMáximo Castañeda
WriteLock()73a4dfc6aaSMáximo Castañeda bool WriteLock()
74a4dfc6aaSMáximo Castañeda {
75a4dfc6aaSMáximo Castañeda ASSERT(fCacheEntry != NULL);
76a4dfc6aaSMáximo Castañeda
77a4dfc6aaSMáximo Castañeda if (fWriteLocked)
78a4dfc6aaSMáximo Castañeda return true;
79a4dfc6aaSMáximo Castañeda
80a4dfc6aaSMáximo Castañeda if (fLocked) {
81a4dfc6aaSMáximo Castañeda if (!fCacheEntry->ReadUnlock()) {
82a4dfc6aaSMáximo Castañeda _Cleanup();
83a4dfc6aaSMáximo Castañeda return false;
84a4dfc6aaSMáximo Castañeda }
85a4dfc6aaSMáximo Castañeda }
86a4dfc6aaSMáximo Castañeda if (!fCacheEntry->WriteLock()) {
87a4dfc6aaSMáximo Castañeda _Cleanup();
88a4dfc6aaSMáximo Castañeda return false;
89a4dfc6aaSMáximo Castañeda }
90a4dfc6aaSMáximo Castañeda
91a4dfc6aaSMáximo Castañeda fLocked = true;
92a4dfc6aaSMáximo Castañeda fWriteLocked = true;
93a4dfc6aaSMáximo Castañeda return true;
94a4dfc6aaSMáximo Castañeda }
95a4dfc6aaSMáximo Castañeda
Unlock()96a4dfc6aaSMáximo Castañeda bool Unlock()
97a4dfc6aaSMáximo Castañeda {
98a4dfc6aaSMáximo Castañeda ASSERT(fCacheEntry != NULL);
99a4dfc6aaSMáximo Castañeda
100a4dfc6aaSMáximo Castañeda if (!fLocked)
101a4dfc6aaSMáximo Castañeda return true;
102a4dfc6aaSMáximo Castañeda
103a4dfc6aaSMáximo Castañeda if (fWriteLocked) {
104a4dfc6aaSMáximo Castañeda if (!fCacheEntry->WriteUnlock()) {
105a4dfc6aaSMáximo Castañeda _Cleanup();
106a4dfc6aaSMáximo Castañeda return false;
107a4dfc6aaSMáximo Castañeda }
108a4dfc6aaSMáximo Castañeda } else {
109a4dfc6aaSMáximo Castañeda if (!fCacheEntry->ReadUnlock()) {
110a4dfc6aaSMáximo Castañeda _Cleanup();
111a4dfc6aaSMáximo Castañeda return false;
112a4dfc6aaSMáximo Castañeda }
113a4dfc6aaSMáximo Castañeda }
114a4dfc6aaSMáximo Castañeda
115a4dfc6aaSMáximo Castañeda fLocked = false;
116a4dfc6aaSMáximo Castañeda fWriteLocked = false;
117a4dfc6aaSMáximo Castañeda return true;
118a4dfc6aaSMáximo Castañeda }
119a4dfc6aaSMáximo Castañeda
SetFallback(FontCacheReference * fallback)120a4dfc6aaSMáximo Castañeda bool SetFallback(FontCacheReference* fallback)
121a4dfc6aaSMáximo Castañeda {
122a4dfc6aaSMáximo Castañeda ASSERT(fCacheEntry != NULL);
123a4dfc6aaSMáximo Castañeda ASSERT(fallback != NULL);
124a4dfc6aaSMáximo Castañeda ASSERT(fallback->Entry() != NULL);
125a4dfc6aaSMáximo Castañeda ASSERT(fallback->Entry() != fCacheEntry);
126a4dfc6aaSMáximo Castañeda
127a4dfc6aaSMáximo Castañeda if (fFallbackReference == fallback)
128a4dfc6aaSMáximo Castañeda return true;
129a4dfc6aaSMáximo Castañeda
130a4dfc6aaSMáximo Castañeda if (fFallbackReference != NULL) {
131a4dfc6aaSMáximo Castañeda fFallbackReference->Unlock();
132a4dfc6aaSMáximo Castañeda fFallbackReference = NULL;
133a4dfc6aaSMáximo Castañeda }
134a4dfc6aaSMáximo Castañeda
135a4dfc6aaSMáximo Castañeda // We need to create new glyphs with the engine of the fallback font
136a4dfc6aaSMáximo Castañeda // and store them in the main font cache (not just transfer them from
137a4dfc6aaSMáximo Castañeda // one cache to the other). So we need both to be write-locked.
138a4dfc6aaSMáximo Castañeda if (fallback->Entry() < fCacheEntry) {
139a4dfc6aaSMáximo Castañeda if (fLocked && !Unlock())
140a4dfc6aaSMáximo Castañeda return false;
141a4dfc6aaSMáximo Castañeda if (!fallback->WriteLock()) {
142a4dfc6aaSMáximo Castañeda WriteLock();
143a4dfc6aaSMáximo Castañeda return false;
144a4dfc6aaSMáximo Castañeda }
145a4dfc6aaSMáximo Castañeda fFallbackReference = fallback;
146a4dfc6aaSMáximo Castañeda return WriteLock();
147a4dfc6aaSMáximo Castañeda }
148a4dfc6aaSMáximo Castañeda if (fLocked && !fWriteLocked && !Unlock())
149a4dfc6aaSMáximo Castañeda return false;
150a4dfc6aaSMáximo Castañeda if (!WriteLock() || !fallback->WriteLock())
151a4dfc6aaSMáximo Castañeda return false;
152a4dfc6aaSMáximo Castañeda fFallbackReference = fallback;
153a4dfc6aaSMáximo Castañeda return true;
154f4f30311SClemens Zeidler }
155f4f30311SClemens Zeidler
Entry()156f4f30311SClemens Zeidler inline FontCacheEntry* Entry() const
157f4f30311SClemens Zeidler {
158f4f30311SClemens Zeidler return fCacheEntry;
159f4f30311SClemens Zeidler }
160f4f30311SClemens Zeidler
WriteLocked()161f4f30311SClemens Zeidler inline bool WriteLocked() const
162f4f30311SClemens Zeidler {
163f4f30311SClemens Zeidler return fWriteLocked;
164f4f30311SClemens Zeidler }
165f4f30311SClemens Zeidler
166f4f30311SClemens Zeidler private:
167a4dfc6aaSMáximo Castañeda
_Cleanup()168a4dfc6aaSMáximo Castañeda void _Cleanup()
169a4dfc6aaSMáximo Castañeda {
170a4dfc6aaSMáximo Castañeda if (fFallbackReference != NULL) {
171a4dfc6aaSMáximo Castañeda fFallbackReference->Unlock();
172a4dfc6aaSMáximo Castañeda fFallbackReference = NULL;
173a4dfc6aaSMáximo Castañeda }
174a4dfc6aaSMáximo Castañeda if (fCacheEntry != NULL)
175a4dfc6aaSMáximo Castañeda FontCache::Default()->Recycle(fCacheEntry);
176a4dfc6aaSMáximo Castañeda fCacheEntry = NULL;
177a4dfc6aaSMáximo Castañeda fLocked = false;
178a4dfc6aaSMáximo Castañeda fWriteLocked = false;
179a4dfc6aaSMáximo Castañeda }
180a4dfc6aaSMáximo Castañeda
181a4dfc6aaSMáximo Castañeda private:
182f4f30311SClemens Zeidler FontCacheEntry* fCacheEntry;
183a4dfc6aaSMáximo Castañeda FontCacheReference* fFallbackReference;
184a4dfc6aaSMáximo Castañeda bool fLocked;
185f4f30311SClemens Zeidler bool fWriteLocked;
186f4f30311SClemens Zeidler };
187f4f30311SClemens Zeidler
188f4f30311SClemens Zeidler
189f4f30311SClemens Zeidler class GlyphLayoutEngine {
190f4f30311SClemens Zeidler public:
191f4f30311SClemens Zeidler static bool IsWhiteSpace(uint32 glyphCode);
192f4f30311SClemens Zeidler
193f4f30311SClemens Zeidler static FontCacheEntry* FontCacheEntryFor(const ServerFont& font,
194a1c9aa9dSMáximo Castañeda bool forceVector);
195f4f30311SClemens Zeidler
196f4f30311SClemens Zeidler template<class GlyphConsumer>
197f4f30311SClemens Zeidler static bool LayoutGlyphs(GlyphConsumer& consumer,
198f4f30311SClemens Zeidler const ServerFont& font,
199f4f30311SClemens Zeidler const char* utf8String,
2008071db32SMichael Lotz int32 length, int32 maxChars,
201f4f30311SClemens Zeidler const escapement_delta* delta = NULL,
202f4f30311SClemens Zeidler uint8 spacing = B_BITMAP_SPACING,
203f4f30311SClemens Zeidler const BPoint* offsets = NULL,
204f4f30311SClemens Zeidler FontCacheReference* cacheReference = NULL);
205f4f30311SClemens Zeidler
206a4dfc6aaSMáximo Castañeda static void PopulateFallbacks(
20722dda779SMáximo Castañeda BObjectList<FontCacheReference>& fallbacks,
208a4dfc6aaSMáximo Castañeda const ServerFont& font, bool forceVector);
209a4dfc6aaSMáximo Castañeda
210a4dfc6aaSMáximo Castañeda static FontCacheReference* GetFallbackReference(
211a4dfc6aaSMáximo Castañeda BObjectList<FontCacheReference>& fallbacks,
212a4dfc6aaSMáximo Castañeda uint32 charCode);
21322dda779SMáximo Castañeda
214f4f30311SClemens Zeidler private:
215a1c9aa9dSMáximo Castañeda static const GlyphCache* _CreateGlyph(
216f4f30311SClemens Zeidler FontCacheReference& cacheReference,
217a1c9aa9dSMáximo Castañeda BObjectList<FontCacheReference>& fallbacks,
218750958b8SStephan Aßmus const ServerFont& font, bool needsVector,
219a1c9aa9dSMáximo Castañeda uint32 glyphCode);
220a1c9aa9dSMáximo Castañeda
221f4f30311SClemens Zeidler GlyphLayoutEngine();
222f4f30311SClemens Zeidler virtual ~GlyphLayoutEngine();
223f4f30311SClemens Zeidler };
224f4f30311SClemens Zeidler
225f4f30311SClemens Zeidler
226f4f30311SClemens Zeidler inline bool
IsWhiteSpace(uint32 charCode)227f4f30311SClemens Zeidler GlyphLayoutEngine::IsWhiteSpace(uint32 charCode)
228f4f30311SClemens Zeidler {
229f4f30311SClemens Zeidler switch (charCode) {
230f4f30311SClemens Zeidler case 0x0009: /* tab */
231f4f30311SClemens Zeidler case 0x000b: /* vertical tab */
232f4f30311SClemens Zeidler case 0x000c: /* form feed */
233f4f30311SClemens Zeidler case 0x0020: /* space */
234f4f30311SClemens Zeidler case 0x00a0: /* non breaking space */
235f4f30311SClemens Zeidler case 0x000a: /* line feed */
236f4f30311SClemens Zeidler case 0x000d: /* carriage return */
237f4f30311SClemens Zeidler case 0x2028: /* line separator */
238f4f30311SClemens Zeidler case 0x2029: /* paragraph separator */
239f4f30311SClemens Zeidler return true;
240f4f30311SClemens Zeidler }
241f4f30311SClemens Zeidler
242f4f30311SClemens Zeidler return false;
243f4f30311SClemens Zeidler }
244f4f30311SClemens Zeidler
245f4f30311SClemens Zeidler
246f4f30311SClemens Zeidler inline FontCacheEntry*
FontCacheEntryFor(const ServerFont & font,bool forceVector)247a1c9aa9dSMáximo Castañeda GlyphLayoutEngine::FontCacheEntryFor(const ServerFont& font, bool forceVector)
248f4f30311SClemens Zeidler {
249f4f30311SClemens Zeidler FontCache* cache = FontCache::Default();
250750958b8SStephan Aßmus FontCacheEntry* entry = cache->FontCacheEntryFor(font, forceVector);
251f4f30311SClemens Zeidler return entry;
252f4f30311SClemens Zeidler }
253f4f30311SClemens Zeidler
254f4f30311SClemens Zeidler
255f4f30311SClemens Zeidler template<class GlyphConsumer>
256f4f30311SClemens Zeidler inline bool
LayoutGlyphs(GlyphConsumer & consumer,const ServerFont & font,const char * utf8String,int32 length,int32 maxChars,const escapement_delta * delta,uint8 spacing,const BPoint * offsets,FontCacheReference * _cacheReference)257f4f30311SClemens Zeidler GlyphLayoutEngine::LayoutGlyphs(GlyphConsumer& consumer,
258f4f30311SClemens Zeidler const ServerFont& font,
2598071db32SMichael Lotz const char* utf8String, int32 length, int32 maxChars,
26091233f88SStephan Aßmus const escapement_delta* delta, uint8 spacing,
261f4f30311SClemens Zeidler const BPoint* offsets, FontCacheReference* _cacheReference)
262f4f30311SClemens Zeidler {
263f4f30311SClemens Zeidler // TODO: implement spacing modes
264f4f30311SClemens Zeidler FontCacheEntry* entry = NULL;
265a1c9aa9dSMáximo Castañeda FontCacheReference* pCacheReference;
266f4f30311SClemens Zeidler FontCacheReference cacheReference;
267a1c9aa9dSMáximo Castañeda BObjectList<FontCacheReference> fallbacksList(21, true);
268a1c9aa9dSMáximo Castañeda
269f4f30311SClemens Zeidler if (_cacheReference != NULL) {
270a1c9aa9dSMáximo Castañeda pCacheReference = _cacheReference;
271f4f30311SClemens Zeidler entry = _cacheReference->Entry();
272f4f30311SClemens Zeidler // When there is already a cacheReference, it means there was already
273f4f30311SClemens Zeidler // an iteration over the glyphs. The use-case is for example to do
274f4f30311SClemens Zeidler // a layout pass to get the string width for the bounding box, then a
275f4f30311SClemens Zeidler // second layout pass to actually render the glyphs to the screen.
276f4f30311SClemens Zeidler // This means that the fallback entry mechanism will not do any good
277f4f30311SClemens Zeidler // for the second pass, since the fallback glyphs have been stored in
278f4f30311SClemens Zeidler // the original entry.
279a1c9aa9dSMáximo Castañeda } else
280a1c9aa9dSMáximo Castañeda pCacheReference = &cacheReference;
281f4f30311SClemens Zeidler
282f4f30311SClemens Zeidler if (entry == NULL) {
283a1c9aa9dSMáximo Castañeda entry = FontCacheEntryFor(font, consumer.NeedsVector());
284f4f30311SClemens Zeidler
285f4f30311SClemens Zeidler if (entry == NULL)
286f4f30311SClemens Zeidler return false;
287a4dfc6aaSMáximo Castañeda pCacheReference->SetTo(entry);
288a4dfc6aaSMáximo Castañeda if (!pCacheReference->ReadLock())
289a1c9aa9dSMáximo Castañeda return false;
290f4f30311SClemens Zeidler } // else the entry was already used and is still locked
291f4f30311SClemens Zeidler
292f4f30311SClemens Zeidler consumer.Start();
293f4f30311SClemens Zeidler
294f4f30311SClemens Zeidler double x = 0.0;
295f4f30311SClemens Zeidler double y = 0.0;
296f4f30311SClemens Zeidler if (offsets) {
297f4f30311SClemens Zeidler x = offsets[0].x;
298f4f30311SClemens Zeidler y = offsets[0].y;
299f4f30311SClemens Zeidler }
300f4f30311SClemens Zeidler
301f4f30311SClemens Zeidler double advanceX = 0.0;
302f4f30311SClemens Zeidler double advanceY = 0.0;
303b8f4968dSStephan Aßmus double size = font.Size();
304f4f30311SClemens Zeidler
30591233f88SStephan Aßmus uint32 lastCharCode = 0; // Needed for kerning in B_STRING_SPACING mode
306f4f30311SClemens Zeidler uint32 charCode;
307f4f30311SClemens Zeidler int32 index = 0;
308f4f30311SClemens Zeidler const char* start = utf8String;
3098071db32SMichael Lotz while (maxChars-- > 0 && (charCode = UTF8ToCharCode(&utf8String)) != 0) {
310f4f30311SClemens Zeidler
31124c156daSStephan Aßmus if (offsets != NULL) {
312f4f30311SClemens Zeidler // Use direct glyph locations instead of calculating them
313f4f30311SClemens Zeidler // from the advance values
314f4f30311SClemens Zeidler x = offsets[index].x;
315f4f30311SClemens Zeidler y = offsets[index].y;
316f4f30311SClemens Zeidler } else {
31791233f88SStephan Aßmus if (spacing == B_STRING_SPACING)
31891233f88SStephan Aßmus entry->GetKerning(lastCharCode, charCode, &advanceX, &advanceY);
319f4f30311SClemens Zeidler
320f4f30311SClemens Zeidler x += advanceX;
321f4f30311SClemens Zeidler y += advanceY;
322f4f30311SClemens Zeidler }
323f4f30311SClemens Zeidler
324f4f30311SClemens Zeidler const GlyphCache* glyph = entry->CachedGlyph(charCode);
325f4f30311SClemens Zeidler if (glyph == NULL) {
326a1c9aa9dSMáximo Castañeda glyph = _CreateGlyph(*pCacheReference, fallbacksList, font,
327a1c9aa9dSMáximo Castañeda consumer.NeedsVector(), charCode);
328f4f30311SClemens Zeidler
329a1c9aa9dSMáximo Castañeda // Something may have gone wrong while reacquiring the entry lock
330a1c9aa9dSMáximo Castañeda if (pCacheReference->Entry() == NULL)
331a1c9aa9dSMáximo Castañeda return false;
332f4f30311SClemens Zeidler }
333f4f30311SClemens Zeidler
334f4f30311SClemens Zeidler if (glyph == NULL) {
335f4f30311SClemens Zeidler consumer.ConsumeEmptyGlyph(index++, charCode, x, y);
336f4f30311SClemens Zeidler advanceX = 0;
337f4f30311SClemens Zeidler advanceY = 0;
338f4f30311SClemens Zeidler } else {
339f4f30311SClemens Zeidler // get next increment for pen position
340d9d14326SStephan Aßmus if (spacing == B_CHAR_SPACING) {
341b8f4968dSStephan Aßmus advanceX = glyph->precise_advance_x * size;
342b8f4968dSStephan Aßmus advanceY = glyph->precise_advance_y * size;
343d9d14326SStephan Aßmus } else {
344f4f30311SClemens Zeidler advanceX = glyph->advance_x;
345f4f30311SClemens Zeidler advanceY = glyph->advance_y;
346d9d14326SStephan Aßmus }
3474ccc40a1SStephan Aßmus
3484ccc40a1SStephan Aßmus // adjust for custom spacing
3494ccc40a1SStephan Aßmus if (delta != NULL) {
3504ccc40a1SStephan Aßmus advanceX += IsWhiteSpace(charCode)
3514ccc40a1SStephan Aßmus ? delta->space : delta->nonspace;
3524ccc40a1SStephan Aßmus }
3534ccc40a1SStephan Aßmus
3544ccc40a1SStephan Aßmus if (!consumer.ConsumeGlyph(index++, charCode, glyph, entry, x, y,
3554ccc40a1SStephan Aßmus advanceX, advanceY)) {
3564ccc40a1SStephan Aßmus advanceX = 0.0;
3574ccc40a1SStephan Aßmus advanceY = 0.0;
3584ccc40a1SStephan Aßmus break;
3594ccc40a1SStephan Aßmus }
360f4f30311SClemens Zeidler }
361f4f30311SClemens Zeidler
36291233f88SStephan Aßmus lastCharCode = charCode;
363f4f30311SClemens Zeidler if (utf8String - start + 1 > length)
364f4f30311SClemens Zeidler break;
365f4f30311SClemens Zeidler }
366f4f30311SClemens Zeidler
367f4f30311SClemens Zeidler x += advanceX;
368f4f30311SClemens Zeidler y += advanceY;
369f4f30311SClemens Zeidler consumer.Finish(x, y);
370f4f30311SClemens Zeidler
371f4f30311SClemens Zeidler return true;
372f4f30311SClemens Zeidler }
373f4f30311SClemens Zeidler
374f4f30311SClemens Zeidler
375a1c9aa9dSMáximo Castañeda inline const GlyphCache*
_CreateGlyph(FontCacheReference & cacheReference,BObjectList<FontCacheReference> & fallbacks,const ServerFont & font,bool forceVector,uint32 charCode)376a1c9aa9dSMáximo Castañeda GlyphLayoutEngine::_CreateGlyph(FontCacheReference& cacheReference,
377a1c9aa9dSMáximo Castañeda BObjectList<FontCacheReference>& fallbacks,
378a1c9aa9dSMáximo Castañeda const ServerFont& font, bool forceVector, uint32 charCode)
379f4f30311SClemens Zeidler {
380a1c9aa9dSMáximo Castañeda FontCacheEntry* entry = cacheReference.Entry();
381f4f30311SClemens Zeidler
382a4dfc6aaSMáximo Castañeda // Avoid loading the fallbacks if our font can create the glyph.
383a1c9aa9dSMáximo Castañeda if (entry->CanCreateGlyph(charCode)) {
384a4dfc6aaSMáximo Castañeda if (cacheReference.WriteLock())
385a1c9aa9dSMáximo Castañeda return entry->CreateGlyph(charCode);
386a1c9aa9dSMáximo Castañeda return NULL;
38715325401SAdrien Destugues }
388f4f30311SClemens Zeidler
389a4dfc6aaSMáximo Castañeda if (fallbacks.IsEmpty())
390a4dfc6aaSMáximo Castañeda PopulateFallbacks(fallbacks, font, forceVector);
391a4dfc6aaSMáximo Castañeda
392a4dfc6aaSMáximo Castañeda FontCacheReference* fallbackReference = GetFallbackReference(fallbacks, charCode);
393a4dfc6aaSMáximo Castañeda if (fallbackReference != NULL) {
394a4dfc6aaSMáximo Castañeda if (cacheReference.SetFallback(fallbackReference))
395a4dfc6aaSMáximo Castañeda return entry->CreateGlyph(charCode, fallbackReference->Entry());
396a4dfc6aaSMáximo Castañeda if (cacheReference.Entry() == NULL)
397a1c9aa9dSMáximo Castañeda return NULL;
398a1c9aa9dSMáximo Castañeda }
399a1c9aa9dSMáximo Castañeda
40022dda779SMáximo Castañeda // No one knows how to draw this, so use the missing glyph symbol.
401a4dfc6aaSMáximo Castañeda if (cacheReference.WriteLock())
402a4dfc6aaSMáximo Castañeda return entry->CreateGlyph(charCode);
403a4dfc6aaSMáximo Castañeda return NULL;
404a1c9aa9dSMáximo Castañeda }
405a1c9aa9dSMáximo Castañeda
406a1c9aa9dSMáximo Castañeda
407a1c9aa9dSMáximo Castañeda inline void
PopulateFallbacks(BObjectList<FontCacheReference> & fallbacksList,const ServerFont & font,bool forceVector)408a4dfc6aaSMáximo Castañeda GlyphLayoutEngine::PopulateFallbacks(
409a1c9aa9dSMáximo Castañeda BObjectList<FontCacheReference>& fallbacksList,
410a4dfc6aaSMáximo Castañeda const ServerFont& font, bool forceVector)
411a1c9aa9dSMáximo Castañeda {
412a1c9aa9dSMáximo Castañeda ASSERT(fallbacksList.IsEmpty());
413a1c9aa9dSMáximo Castañeda
41434fbc56bSAdrien Destugues // TODO: We always get the fallback glyphs from the Noto family, but of
41534fbc56bSAdrien Destugues // course the fallback font should a) contain the missing glyphs at all
41634fbc56bSAdrien Destugues // and b) be similar to the original font. So there should be a mapping
41734fbc56bSAdrien Destugues // of some kind to know the most suitable fallback font.
41834fbc56bSAdrien Destugues static const char* fallbacks[] = {
4194d6b8a57SJérôme Duval "Noto Sans",
420afc74b61SAdrien Destugues "Noto Sans Thai",
42134fbc56bSAdrien Destugues "Noto Sans CJK JP",
422c220175cSPulkoMandy "Noto Sans Cherokee",
4235d104205SAdrien Destugues "Noto Sans Symbols",
4246aec6b1cSAdrien Destugues "Noto Sans Symbols 2",
4256aec6b1cSAdrien Destugues "Noto Emoji",
42634fbc56bSAdrien Destugues };
42734fbc56bSAdrien Destugues
428a1c9aa9dSMáximo Castañeda if (!gFontManager->Lock())
429a1c9aa9dSMáximo Castañeda return;
43034fbc56bSAdrien Destugues
431a1c9aa9dSMáximo Castañeda static const int nFallbacks = B_COUNT_OF(fallbacks);
432*f657c49dSMáximo Castañeda static const int acceptAnyStyle = 2;
433a1c9aa9dSMáximo Castañeda
434*f657c49dSMáximo Castañeda for (int degradeLevel = 0; degradeLevel <= acceptAnyStyle; degradeLevel++) {
435290fd41dSPascal Abresch const char* fontStyle;
436*f657c49dSMáximo Castañeda if (degradeLevel == 0)
437290fd41dSPascal Abresch fontStyle = font.Style();
438*f657c49dSMáximo Castañeda else if (degradeLevel == 1)
4396ffd243dSPascal Abresch fontStyle = "Regular";
440290fd41dSPascal Abresch else
441290fd41dSPascal Abresch fontStyle = NULL;
4427a8d5a2dSPascal Abresch
443a1c9aa9dSMáximo Castañeda for (int i = 0; i < nFallbacks; i++) {
444290fd41dSPascal Abresch
445*f657c49dSMáximo Castañeda FontStyle* fallbackStyle;
446*f657c49dSMáximo Castañeda if (degradeLevel != acceptAnyStyle) {
447*f657c49dSMáximo Castañeda fallbackStyle = gFontManager->GetStyle(fallbacks[i], fontStyle);
448*f657c49dSMáximo Castañeda } else {
449*f657c49dSMáximo Castañeda // At this point we'll just take whatever we are given
450*f657c49dSMáximo Castañeda fallbackStyle = gFontManager->GetStyleByIndex(fallbacks[i], 0);
451*f657c49dSMáximo Castañeda }
452*f657c49dSMáximo Castañeda
453290fd41dSPascal Abresch if (fallbackStyle == NULL)
454290fd41dSPascal Abresch continue;
455c0e0ba1fSAdrien Destugues
456f4f30311SClemens Zeidler ServerFont fallbackFont(*fallbackStyle, font.Size());
45734fbc56bSAdrien Destugues
458a4dfc6aaSMáximo Castañeda FontCacheEntry* entry = FontCacheEntryFor(fallbackFont, forceVector);
459a1c9aa9dSMáximo Castañeda if (entry == NULL)
460a1c9aa9dSMáximo Castañeda continue;
461a1c9aa9dSMáximo Castañeda
462a4dfc6aaSMáximo Castañeda FontCacheReference* cacheReference = new(std::nothrow) FontCacheReference();
463a4dfc6aaSMáximo Castañeda if (cacheReference != NULL) {
464a4dfc6aaSMáximo Castañeda cacheReference->SetTo(entry);
465a4dfc6aaSMáximo Castañeda fallbacksList.AddItem(cacheReference);
466a4dfc6aaSMáximo Castañeda } else
467a4dfc6aaSMáximo Castañeda FontCache::Default()->Recycle(entry);
468a1c9aa9dSMáximo Castañeda }
469a1c9aa9dSMáximo Castañeda }
470a1c9aa9dSMáximo Castañeda
471a1c9aa9dSMáximo Castañeda gFontManager->Unlock();
472c0e0ba1fSAdrien Destugues }
473290fd41dSPascal Abresch
474a4dfc6aaSMáximo Castañeda
475a4dfc6aaSMáximo Castañeda inline FontCacheReference*
GetFallbackReference(BObjectList<FontCacheReference> & fallbacks,uint32 charCode)476a4dfc6aaSMáximo Castañeda GlyphLayoutEngine::GetFallbackReference(
477a4dfc6aaSMáximo Castañeda BObjectList<FontCacheReference>& fallbacks, uint32 charCode)
478a4dfc6aaSMáximo Castañeda {
479a4dfc6aaSMáximo Castañeda int32 count = fallbacks.CountItems();
480a4dfc6aaSMáximo Castañeda for (int32 index = 0; index < count; index++) {
481a4dfc6aaSMáximo Castañeda FontCacheReference* fallbackReference = fallbacks.ItemAt(index);
482a4dfc6aaSMáximo Castañeda FontCacheEntry* fallbackEntry = fallbackReference->Entry();
483a4dfc6aaSMáximo Castañeda if (fallbackEntry != NULL && fallbackEntry->CanCreateGlyph(charCode))
484a4dfc6aaSMáximo Castañeda return fallbackReference;
485a4dfc6aaSMáximo Castañeda }
486a4dfc6aaSMáximo Castañeda return NULL;
487f4f30311SClemens Zeidler }
488f4f30311SClemens Zeidler
489f4f30311SClemens Zeidler
490f4f30311SClemens Zeidler #endif // GLYPH_LAYOUT_ENGINE_H
491