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