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