xref: /haiku/src/servers/app/font/GlyphLayoutEngine.h (revision 750958b8571b7c77fb5f7305074c3bafc5795419)
1f4f30311SClemens Zeidler /*
2f4f30311SClemens Zeidler  * Copyright 2007, 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"
16f4f30311SClemens Zeidler #include "FontManager.h"
17f4f30311SClemens Zeidler #include "ServerFont.h"
18f4f30311SClemens Zeidler 
19f4f30311SClemens Zeidler #include <Debug.h>
20f4f30311SClemens Zeidler 
21f4f30311SClemens Zeidler #include <ctype.h>
22f4f30311SClemens Zeidler 
23f4f30311SClemens Zeidler class FontCacheReference {
24f4f30311SClemens Zeidler public:
25f4f30311SClemens Zeidler 	FontCacheReference()
26f4f30311SClemens Zeidler 		:
27f4f30311SClemens Zeidler 		fCacheEntry(NULL),
28f4f30311SClemens Zeidler 		fWriteLocked(false)
29f4f30311SClemens Zeidler 	{
30f4f30311SClemens Zeidler 	}
31f4f30311SClemens Zeidler 
32f4f30311SClemens Zeidler 	~FontCacheReference()
33f4f30311SClemens Zeidler 	{
34f4f30311SClemens Zeidler 		Unset();
35f4f30311SClemens Zeidler 	}
36f4f30311SClemens Zeidler 
37f4f30311SClemens Zeidler 	void SetTo(FontCacheEntry* entry, bool writeLocked)
38f4f30311SClemens Zeidler 	{
39f4f30311SClemens Zeidler 		// NOTE: If the semantics are changed such
40f4f30311SClemens Zeidler 		// that the reference to a previous entry
41f4f30311SClemens Zeidler 		// is properly released, then don't forget
42f4f30311SClemens Zeidler 		// to adapt existing which transfers
43f4f30311SClemens Zeidler 		// responsibility of entries between
44f4f30311SClemens Zeidler 		// references!
45f4f30311SClemens Zeidler 		fCacheEntry = entry;
46f4f30311SClemens Zeidler 		fWriteLocked = writeLocked;
47f4f30311SClemens Zeidler 	}
48f4f30311SClemens Zeidler 
49f4f30311SClemens Zeidler 	void Unset()
50f4f30311SClemens Zeidler 	{
51f4f30311SClemens Zeidler 		if (fCacheEntry == NULL)
52f4f30311SClemens Zeidler 			return;
53f4f30311SClemens Zeidler 
54f4f30311SClemens Zeidler 		if (fWriteLocked)
55f4f30311SClemens Zeidler 			fCacheEntry->WriteUnlock();
56f4f30311SClemens Zeidler 		else
57f4f30311SClemens Zeidler 			fCacheEntry->ReadUnlock();
58f4f30311SClemens Zeidler 
59f4f30311SClemens Zeidler 		FontCache::Default()->Recycle(fCacheEntry);
60f4f30311SClemens Zeidler 	}
61f4f30311SClemens Zeidler 
62f4f30311SClemens Zeidler 	inline FontCacheEntry* Entry() const
63f4f30311SClemens Zeidler 	{
64f4f30311SClemens Zeidler 		return fCacheEntry;
65f4f30311SClemens Zeidler 	}
66f4f30311SClemens Zeidler 
67f4f30311SClemens Zeidler 	inline bool WriteLocked() const
68f4f30311SClemens Zeidler 	{
69f4f30311SClemens Zeidler 		return fWriteLocked;
70f4f30311SClemens Zeidler 	}
71f4f30311SClemens Zeidler 
72f4f30311SClemens Zeidler private:
73f4f30311SClemens Zeidler 			FontCacheEntry*		fCacheEntry;
74f4f30311SClemens Zeidler 			bool				fWriteLocked;
75f4f30311SClemens Zeidler };
76f4f30311SClemens Zeidler 
77f4f30311SClemens Zeidler 
78f4f30311SClemens Zeidler class GlyphLayoutEngine {
79f4f30311SClemens Zeidler public:
80f4f30311SClemens Zeidler 	static	bool				IsWhiteSpace(uint32 glyphCode);
81f4f30311SClemens Zeidler 
82f4f30311SClemens Zeidler 	static	FontCacheEntry*		FontCacheEntryFor(const ServerFont& font,
83*750958b8SStephan Aßmus 									bool forceVector,
84f4f30311SClemens Zeidler 									const FontCacheEntry* disallowedEntry,
85f4f30311SClemens Zeidler 									const char* utf8String, int32 length,
86f4f30311SClemens Zeidler 									FontCacheReference& cacheReference,
87f4f30311SClemens Zeidler 									bool needsWriteLock);
88f4f30311SClemens Zeidler 
89f4f30311SClemens Zeidler 			template<class GlyphConsumer>
90f4f30311SClemens Zeidler 	static	bool				LayoutGlyphs(GlyphConsumer& consumer,
91f4f30311SClemens Zeidler 									const ServerFont& font,
92f4f30311SClemens Zeidler 									const char* utf8String,
93f4f30311SClemens Zeidler 									int32 length,
94f4f30311SClemens Zeidler 									const escapement_delta* delta = NULL,
95f4f30311SClemens Zeidler 									bool kerning = true,
96f4f30311SClemens Zeidler 									uint8 spacing = B_BITMAP_SPACING,
97f4f30311SClemens Zeidler 									const BPoint* offsets = NULL,
98f4f30311SClemens Zeidler 									FontCacheReference* cacheReference = NULL);
99f4f30311SClemens Zeidler 
100f4f30311SClemens Zeidler private:
101f4f30311SClemens Zeidler 	static	bool				_WriteLockAndAcquireFallbackEntry(
102f4f30311SClemens Zeidler 									FontCacheReference& cacheReference,
103f4f30311SClemens Zeidler 									FontCacheEntry* entry,
104*750958b8SStephan Aßmus 									const ServerFont& font, bool needsVector,
105f4f30311SClemens Zeidler 									const char* utf8String, int32 length,
106f4f30311SClemens Zeidler 									FontCacheReference& fallbackCacheReference,
107f4f30311SClemens Zeidler 									FontCacheEntry*& fallbackEntry);
108f4f30311SClemens Zeidler 
109f4f30311SClemens Zeidler 								GlyphLayoutEngine();
110f4f30311SClemens Zeidler 	virtual						~GlyphLayoutEngine();
111f4f30311SClemens Zeidler };
112f4f30311SClemens Zeidler 
113f4f30311SClemens Zeidler 
114f4f30311SClemens Zeidler inline bool
115f4f30311SClemens Zeidler GlyphLayoutEngine::IsWhiteSpace(uint32 charCode)
116f4f30311SClemens Zeidler {
117f4f30311SClemens Zeidler 	switch (charCode) {
118f4f30311SClemens Zeidler 		case 0x0009:	/* tab */
119f4f30311SClemens Zeidler 		case 0x000b:	/* vertical tab */
120f4f30311SClemens Zeidler 		case 0x000c:	/* form feed */
121f4f30311SClemens Zeidler 		case 0x0020:	/* space */
122f4f30311SClemens Zeidler 		case 0x00a0:	/* non breaking space */
123f4f30311SClemens Zeidler 		case 0x000a:	/* line feed */
124f4f30311SClemens Zeidler 		case 0x000d:	/* carriage return */
125f4f30311SClemens Zeidler 		case 0x2028:	/* line separator */
126f4f30311SClemens Zeidler 		case 0x2029:	/* paragraph separator */
127f4f30311SClemens Zeidler 			return true;
128f4f30311SClemens Zeidler 	}
129f4f30311SClemens Zeidler 
130f4f30311SClemens Zeidler 	return false;
131f4f30311SClemens Zeidler }
132f4f30311SClemens Zeidler 
133f4f30311SClemens Zeidler 
134f4f30311SClemens Zeidler inline FontCacheEntry*
135*750958b8SStephan Aßmus GlyphLayoutEngine::FontCacheEntryFor(const ServerFont& font, bool forceVector,
136f4f30311SClemens Zeidler 	const FontCacheEntry* disallowedEntry, const char* utf8String, int32 length,
137f4f30311SClemens Zeidler 	FontCacheReference& cacheReference, bool needsWriteLock)
138f4f30311SClemens Zeidler {
139f4f30311SClemens Zeidler 	ASSERT(cacheReference.Entry() == NULL);
140f4f30311SClemens Zeidler 
141f4f30311SClemens Zeidler 	FontCache* cache = FontCache::Default();
142*750958b8SStephan Aßmus 	FontCacheEntry* entry = cache->FontCacheEntryFor(font, forceVector);
143f4f30311SClemens Zeidler 	if (entry == NULL)
144f4f30311SClemens Zeidler 		return NULL;
145f4f30311SClemens Zeidler 
146f4f30311SClemens Zeidler 	if (entry == disallowedEntry) {
147f4f30311SClemens Zeidler 		cache->Recycle(entry);
148f4f30311SClemens Zeidler 		return NULL;
149f4f30311SClemens Zeidler 	}
150f4f30311SClemens Zeidler 
151f4f30311SClemens Zeidler 	if (needsWriteLock) {
152f4f30311SClemens Zeidler 		if (!entry->WriteLock()) {
153f4f30311SClemens Zeidler 			cache->Recycle(entry);
154f4f30311SClemens Zeidler 			return NULL;
155f4f30311SClemens Zeidler 		}
156f4f30311SClemens Zeidler 	} else {
157f4f30311SClemens Zeidler 		if (!entry->ReadLock()) {
158f4f30311SClemens Zeidler 			cache->Recycle(entry);
159f4f30311SClemens Zeidler 			return NULL;
160f4f30311SClemens Zeidler 		}
161f4f30311SClemens Zeidler 	}
162f4f30311SClemens Zeidler 
163f4f30311SClemens Zeidler 	// At this point, we have a valid FontCacheEntry and it is locked in the
164f4f30311SClemens Zeidler 	// proper mode. We can setup the FontCacheReference so it takes care of
165f4f30311SClemens Zeidler 	// the locking and recycling from now and return the entry.
166f4f30311SClemens Zeidler 	cacheReference.SetTo(entry, needsWriteLock);
167f4f30311SClemens Zeidler 	return entry;
168f4f30311SClemens Zeidler }
169f4f30311SClemens Zeidler 
170f4f30311SClemens Zeidler 
171f4f30311SClemens Zeidler template<class GlyphConsumer>
172f4f30311SClemens Zeidler inline bool
173f4f30311SClemens Zeidler GlyphLayoutEngine::LayoutGlyphs(GlyphConsumer& consumer,
174f4f30311SClemens Zeidler 	const ServerFont& font,
175f4f30311SClemens Zeidler 	const char* utf8String, int32 length,
176f4f30311SClemens Zeidler 	const escapement_delta* delta, bool kerning, uint8 spacing,
177f4f30311SClemens Zeidler 	const BPoint* offsets, FontCacheReference* _cacheReference)
178f4f30311SClemens Zeidler {
179f4f30311SClemens Zeidler 	// TODO: implement spacing modes
180f4f30311SClemens Zeidler 	FontCacheEntry* entry = NULL;
181f4f30311SClemens Zeidler 	FontCacheReference cacheReference;
182f4f30311SClemens Zeidler 	FontCacheEntry* fallbackEntry = NULL;
183f4f30311SClemens Zeidler 	FontCacheReference fallbackCacheReference;
184f4f30311SClemens Zeidler 	if (_cacheReference != NULL) {
185f4f30311SClemens Zeidler 		entry = _cacheReference->Entry();
186f4f30311SClemens Zeidler 		// When there is already a cacheReference, it means there was already
187f4f30311SClemens Zeidler 		// an iteration over the glyphs. The use-case is for example to do
188f4f30311SClemens Zeidler 		// a layout pass to get the string width for the bounding box, then a
189f4f30311SClemens Zeidler 		// second layout pass to actually render the glyphs to the screen.
190f4f30311SClemens Zeidler 		// This means that the fallback entry mechanism will not do any good
191f4f30311SClemens Zeidler 		// for the second pass, since the fallback glyphs have been stored in
192f4f30311SClemens Zeidler 		// the original entry.
193f4f30311SClemens Zeidler 	}
194f4f30311SClemens Zeidler 
195f4f30311SClemens Zeidler 	if (entry == NULL) {
196*750958b8SStephan Aßmus 		entry = FontCacheEntryFor(font, consumer.NeedsVector(), NULL,
197*750958b8SStephan Aßmus 			utf8String, length, cacheReference, false);
198f4f30311SClemens Zeidler 
199f4f30311SClemens Zeidler 		if (entry == NULL)
200f4f30311SClemens Zeidler 			return false;
201f4f30311SClemens Zeidler 	} // else the entry was already used and is still locked
202f4f30311SClemens Zeidler 
203f4f30311SClemens Zeidler 	consumer.Start();
204f4f30311SClemens Zeidler 
205f4f30311SClemens Zeidler 	double x = 0.0;
206f4f30311SClemens Zeidler 	double y = 0.0;
207f4f30311SClemens Zeidler 	if (offsets) {
208f4f30311SClemens Zeidler 		x = offsets[0].x;
209f4f30311SClemens Zeidler 		y = offsets[0].y;
210f4f30311SClemens Zeidler 	}
211f4f30311SClemens Zeidler 
212f4f30311SClemens Zeidler 	double advanceX = 0.0;
213f4f30311SClemens Zeidler 	double advanceY = 0.0;
214f4f30311SClemens Zeidler 
215*750958b8SStephan Aßmus //	uint32 lastCharCode = 0; // Needed for kerning, see below
216f4f30311SClemens Zeidler 	uint32 charCode;
217f4f30311SClemens Zeidler 	int32 index = 0;
218f4f30311SClemens Zeidler 	bool writeLocked = false;
219f4f30311SClemens Zeidler 	const char* start = utf8String;
220f4f30311SClemens Zeidler 	while ((charCode = UTF8ToCharCode(&utf8String))) {
221f4f30311SClemens Zeidler 
222f4f30311SClemens Zeidler 		if (offsets) {
223f4f30311SClemens Zeidler 			// Use direct glyph locations instead of calculating them
224f4f30311SClemens Zeidler 			// from the advance values
225f4f30311SClemens Zeidler 			x = offsets[index].x;
226f4f30311SClemens Zeidler 			y = offsets[index].y;
227f4f30311SClemens Zeidler 		} else {
228f4f30311SClemens Zeidler // TODO: Currently disabled, because it works much too slow (doesn't seem
229f4f30311SClemens Zeidler // to be properly cached in FreeType.)
230f4f30311SClemens Zeidler //			if (kerning)
231f4f30311SClemens Zeidler //				entry->GetKerning(lastCharCode, charCode, &advanceX, &advanceY);
232f4f30311SClemens Zeidler 
233f4f30311SClemens Zeidler 			x += advanceX;
234f4f30311SClemens Zeidler 			y += advanceY;
235f4f30311SClemens Zeidler 
236f4f30311SClemens Zeidler 			if (delta)
237f4f30311SClemens Zeidler 				x += IsWhiteSpace(charCode) ? delta->space : delta->nonspace;
238f4f30311SClemens Zeidler 		}
239f4f30311SClemens Zeidler 
240f4f30311SClemens Zeidler 		const GlyphCache* glyph = entry->CachedGlyph(charCode);
241f4f30311SClemens Zeidler 		if (glyph == NULL) {
242f4f30311SClemens Zeidler 			// The glyph has not been cached yet, switch to a write lock,
243f4f30311SClemens Zeidler 			// acquire the fallback entry and create the glyph. Note that
244f4f30311SClemens Zeidler 			// the write lock will persist (in the cacheReference) so that
245f4f30311SClemens Zeidler 			// we only have to do this switch once for the whole string.
246f4f30311SClemens Zeidler 			if (!writeLocked) {
247f4f30311SClemens Zeidler 				writeLocked = _WriteLockAndAcquireFallbackEntry(cacheReference,
248*750958b8SStephan Aßmus 					entry, font, consumer.NeedsVector(), utf8String, length,
249*750958b8SStephan Aßmus 					fallbackCacheReference, fallbackEntry);
250f4f30311SClemens Zeidler 			}
251f4f30311SClemens Zeidler 
252f4f30311SClemens Zeidler 			if (writeLocked)
253f4f30311SClemens Zeidler 				glyph = entry->CreateGlyph(charCode, fallbackEntry);
254f4f30311SClemens Zeidler 		}
255f4f30311SClemens Zeidler 
256f4f30311SClemens Zeidler 		if (glyph == NULL) {
257f4f30311SClemens Zeidler 			consumer.ConsumeEmptyGlyph(index++, charCode, x, y);
258f4f30311SClemens Zeidler 			advanceX = 0;
259f4f30311SClemens Zeidler 			advanceY = 0;
260f4f30311SClemens Zeidler 		} else {
261f4f30311SClemens Zeidler 			if (!consumer.ConsumeGlyph(index++, charCode, glyph, entry, x, y)) {
262f4f30311SClemens Zeidler 				advanceX = 0;
263f4f30311SClemens Zeidler 				advanceY = 0;
264f4f30311SClemens Zeidler 				break;
265f4f30311SClemens Zeidler 			}
266f4f30311SClemens Zeidler 
267f4f30311SClemens Zeidler 			// get next increment for pen position
268f4f30311SClemens Zeidler 			advanceX = glyph->advance_x;
269f4f30311SClemens Zeidler 			advanceY = glyph->advance_y;
270f4f30311SClemens Zeidler 		}
271f4f30311SClemens Zeidler 
2725c6b9eb0SJerome Duval //		lastCharCode = charCode;
273f4f30311SClemens Zeidler 		if (utf8String - start + 1 > length)
274f4f30311SClemens Zeidler 			break;
275f4f30311SClemens Zeidler 	}
276f4f30311SClemens Zeidler 
277f4f30311SClemens Zeidler 	x += advanceX;
278f4f30311SClemens Zeidler 	y += advanceY;
279f4f30311SClemens Zeidler 	consumer.Finish(x, y);
280f4f30311SClemens Zeidler 
281f4f30311SClemens Zeidler 	if (_cacheReference != NULL && _cacheReference->Entry() == NULL) {
282f4f30311SClemens Zeidler 		// The caller passed a FontCacheReference, but this is the first
283f4f30311SClemens Zeidler 		// iteration -> switch the ownership from the stack allocated
284f4f30311SClemens Zeidler 		// FontCacheReference to the one passed by the caller. The fallback
285f4f30311SClemens Zeidler 		// FontCacheReference is not affected by this, since it is never used
286f4f30311SClemens Zeidler 		// during a second iteration.
287f4f30311SClemens Zeidler 		_cacheReference->SetTo(entry, cacheReference.WriteLocked());
288f4f30311SClemens Zeidler 		cacheReference.SetTo(NULL, false);
289f4f30311SClemens Zeidler 	}
290f4f30311SClemens Zeidler 	return true;
291f4f30311SClemens Zeidler }
292f4f30311SClemens Zeidler 
293f4f30311SClemens Zeidler 
294f4f30311SClemens Zeidler inline bool
295f4f30311SClemens Zeidler GlyphLayoutEngine::_WriteLockAndAcquireFallbackEntry(
296f4f30311SClemens Zeidler 	FontCacheReference& cacheReference, FontCacheEntry* entry,
297*750958b8SStephan Aßmus 	const ServerFont& font, bool forceVector, const char* utf8String,
298*750958b8SStephan Aßmus 	int32 length, FontCacheReference& fallbackCacheReference,
299*750958b8SStephan Aßmus 	FontCacheEntry*& fallbackEntry)
300f4f30311SClemens Zeidler {
301f4f30311SClemens Zeidler 	// We need the fallback font, since potentially, we have to obtain missing
302f4f30311SClemens Zeidler 	// glyphs from it. We need to obtain the fallback font while we have not
303f4f30311SClemens Zeidler 	// locked anything, since locking the FontManager with the write-lock held
304f4f30311SClemens Zeidler 	// can obvisouly lead to a deadlock.
305f4f30311SClemens Zeidler 
306f4f30311SClemens Zeidler 	cacheReference.SetTo(NULL, false);
307f4f30311SClemens Zeidler 	entry->ReadUnlock();
308f4f30311SClemens Zeidler 
309f4f30311SClemens Zeidler 	if (gFontManager->Lock()) {
310f4f30311SClemens Zeidler 		// TODO: We always get the fallback glyphs from VL Gothic at the
311f4f30311SClemens Zeidler 		// moment, but of course the fallback font should a) contain the
312f4f30311SClemens Zeidler 		// missing glyphs at all and b) be similar to the original font.
313f4f30311SClemens Zeidler 		// So there should be a mapping of some kind to know the most
314f4f30311SClemens Zeidler 		// suitable fallback font.
315f4f30311SClemens Zeidler 		FontStyle* fallbackStyle = gFontManager->GetStyleByIndex(
316f4f30311SClemens Zeidler 			"VL Gothic", 0);
317f4f30311SClemens Zeidler 		if (fallbackStyle != NULL) {
318f4f30311SClemens Zeidler 			ServerFont fallbackFont(*fallbackStyle, font.Size());
319f4f30311SClemens Zeidler 			gFontManager->Unlock();
320f4f30311SClemens Zeidler 			// Force the write-lock on the fallback entry, since we
321f4f30311SClemens Zeidler 			// don't transfer or copy GlyphCache objects from one cache
322f4f30311SClemens Zeidler 			// to the other, but create new glyphs which are stored in
323f4f30311SClemens Zeidler 			// "entry" in any case, which requires the write cache for
324f4f30311SClemens Zeidler 			// sure (used FontEngine of fallbackEntry).
325*750958b8SStephan Aßmus 			fallbackEntry = FontCacheEntryFor(fallbackFont, forceVector, entry,
326f4f30311SClemens Zeidler 				utf8String, length, fallbackCacheReference, true);
327f4f30311SClemens Zeidler 			// NOTE: We don't care if fallbackEntry is NULL, fetching
328f4f30311SClemens Zeidler 			// alternate glyphs will simply not work.
329f4f30311SClemens Zeidler 		} else
330f4f30311SClemens Zeidler 			gFontManager->Unlock();
331f4f30311SClemens Zeidler 	}
332f4f30311SClemens Zeidler 
333f4f30311SClemens Zeidler 	if (!entry->WriteLock()) {
334f4f30311SClemens Zeidler 		FontCache::Default()->Recycle(entry);
335f4f30311SClemens Zeidler 		return false;
336f4f30311SClemens Zeidler 	}
337f4f30311SClemens Zeidler 
338f4f30311SClemens Zeidler 	// Update the FontCacheReference, since the locking kind changed.
339f4f30311SClemens Zeidler 	cacheReference.SetTo(entry, true);
340f4f30311SClemens Zeidler 	return true;
341f4f30311SClemens Zeidler }
342f4f30311SClemens Zeidler 
343f4f30311SClemens Zeidler 
344f4f30311SClemens Zeidler #endif // GLYPH_LAYOUT_ENGINE_H
345