xref: /haiku/src/servers/app/font/GlyphLayoutEngine.h (revision f657c49d1c8e661560d17d72223d915c16da7ef0)
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