xref: /haiku/src/servers/app/font/GlyphLayoutEngine.h (revision b5efad4eae1a0754ce3e4eb87458279e81f61f61)
1 /*
2  * Copyright 2007-2014, 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 <Autolock.h>
20 #include <Debug.h>
21 #include <ObjectList.h>
22 #include <SupportDefs.h>
23 
24 #include <ctype.h>
25 
26 class FontCacheReference {
27 public:
28 	FontCacheReference()
29 		:
30 		fCacheEntry(NULL),
31 		fWriteLocked(false)
32 	{
33 	}
34 
35 	~FontCacheReference()
36 	{
37 		Unset();
38 	}
39 
40 	bool SetTo(FontCacheEntry* entry, bool writeLock)
41 	{
42 		ASSERT(entry != NULL);
43 
44 		if (entry == fCacheEntry) {
45 			if (writeLock == fWriteLocked)
46 				return true;
47 			UnlockAndDisown();
48 		} else if (fCacheEntry != NULL)
49 			Unset();
50 
51 		if (writeLock) {
52 			if (!entry->WriteLock()) {
53 				FontCache::Default()->Recycle(entry);
54 				return false;
55 			}
56 		} else if (!entry->ReadLock()) {
57 			FontCache::Default()->Recycle(entry);
58 			return false;
59 		}
60 
61 		fCacheEntry = entry;
62 		fWriteLocked = writeLock;
63 		return true;
64 	}
65 
66 	FontCacheEntry* UnlockAndDisown()
67 	{
68 		if (fCacheEntry == NULL)
69 			return NULL;
70 
71 		if (fWriteLocked)
72 			fCacheEntry->WriteUnlock();
73 		else
74 			fCacheEntry->ReadUnlock();
75 
76 		FontCacheEntry* entry = fCacheEntry;
77 		fCacheEntry = NULL;
78 		fWriteLocked = false;
79 		return entry;
80 	}
81 
82 	void Unset()
83 	{
84 		if (fCacheEntry == NULL)
85 			return;
86 
87 		FontCache::Default()->Recycle(UnlockAndDisown());
88 	}
89 
90 	inline FontCacheEntry* Entry() const
91 	{
92 		return fCacheEntry;
93 	}
94 
95 	inline bool WriteLocked() const
96 	{
97 		return fWriteLocked;
98 	}
99 
100 private:
101 			FontCacheEntry*		fCacheEntry;
102 			bool				fWriteLocked;
103 };
104 
105 
106 class GlyphLayoutEngine {
107 public:
108 	static	bool				IsWhiteSpace(uint32 glyphCode);
109 
110 	static	FontCacheEntry*		FontCacheEntryFor(const ServerFont& font,
111 									bool forceVector);
112 
113 			template<class GlyphConsumer>
114 	static	bool				LayoutGlyphs(GlyphConsumer& consumer,
115 									const ServerFont& font,
116 									const char* utf8String,
117 									int32 length, int32 maxChars,
118 									const escapement_delta* delta = NULL,
119 									uint8 spacing = B_BITMAP_SPACING,
120 									const BPoint* offsets = NULL,
121 									FontCacheReference* cacheReference = NULL);
122 
123 private:
124 	static	const GlyphCache*	_CreateGlyph(
125 									FontCacheReference& cacheReference,
126 									BObjectList<FontCacheReference>& fallbacks,
127 									const ServerFont& font, bool needsVector,
128 									uint32 glyphCode);
129 
130 	static	void				_PopulateAndLockFallbacks(
131 									BObjectList<FontCacheReference>& fallbacks,
132 									const ServerFont& font, bool forceVector);
133 
134 								GlyphLayoutEngine();
135 	virtual						~GlyphLayoutEngine();
136 };
137 
138 
139 inline bool
140 GlyphLayoutEngine::IsWhiteSpace(uint32 charCode)
141 {
142 	switch (charCode) {
143 		case 0x0009:	/* tab */
144 		case 0x000b:	/* vertical tab */
145 		case 0x000c:	/* form feed */
146 		case 0x0020:	/* space */
147 		case 0x00a0:	/* non breaking space */
148 		case 0x000a:	/* line feed */
149 		case 0x000d:	/* carriage return */
150 		case 0x2028:	/* line separator */
151 		case 0x2029:	/* paragraph separator */
152 			return true;
153 	}
154 
155 	return false;
156 }
157 
158 
159 inline FontCacheEntry*
160 GlyphLayoutEngine::FontCacheEntryFor(const ServerFont& font, bool forceVector)
161 {
162 	FontCache* cache = FontCache::Default();
163 	FontCacheEntry* entry = cache->FontCacheEntryFor(font, forceVector);
164 	return entry;
165 }
166 
167 
168 template<class GlyphConsumer>
169 inline bool
170 GlyphLayoutEngine::LayoutGlyphs(GlyphConsumer& consumer,
171 	const ServerFont& font,
172 	const char* utf8String, int32 length, int32 maxChars,
173 	const escapement_delta* delta, uint8 spacing,
174 	const BPoint* offsets, FontCacheReference* _cacheReference)
175 {
176 	// TODO: implement spacing modes
177 	FontCacheEntry* entry = NULL;
178 	FontCacheReference* pCacheReference;
179 	FontCacheReference cacheReference;
180 	BObjectList<FontCacheReference> fallbacksList(21, true);
181 
182 	if (_cacheReference != NULL) {
183 		pCacheReference = _cacheReference;
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 	} else
193 		pCacheReference = &cacheReference;
194 
195 	if (entry == NULL) {
196 		entry = FontCacheEntryFor(font, consumer.NeedsVector());
197 
198 		if (entry == NULL)
199 			return false;
200 		if (!pCacheReference->SetTo(entry, false))
201 			return false;
202 	} // else the entry was already used and is still locked
203 
204 	consumer.Start();
205 
206 	double x = 0.0;
207 	double y = 0.0;
208 	if (offsets) {
209 		x = offsets[0].x;
210 		y = offsets[0].y;
211 	}
212 
213 	double advanceX = 0.0;
214 	double advanceY = 0.0;
215 	double size = font.Size();
216 
217 	uint32 lastCharCode = 0; // Needed for kerning in B_STRING_SPACING mode
218 	uint32 charCode;
219 	int32 index = 0;
220 	const char* start = utf8String;
221 	while (maxChars-- > 0 && (charCode = UTF8ToCharCode(&utf8String)) != 0) {
222 
223 		if (offsets != NULL) {
224 			// Use direct glyph locations instead of calculating them
225 			// from the advance values
226 			x = offsets[index].x;
227 			y = offsets[index].y;
228 		} else {
229 			if (spacing == B_STRING_SPACING)
230 				entry->GetKerning(lastCharCode, charCode, &advanceX, &advanceY);
231 
232 			x += advanceX;
233 			y += advanceY;
234 		}
235 
236 		const GlyphCache* glyph = entry->CachedGlyph(charCode);
237 		if (glyph == NULL) {
238 			glyph = _CreateGlyph(*pCacheReference, fallbacksList, font,
239 				consumer.NeedsVector(), charCode);
240 
241 			// Something may have gone wrong while reacquiring the entry lock
242 			if (pCacheReference->Entry() == NULL)
243 				return false;
244 		}
245 
246 		if (glyph == NULL) {
247 			consumer.ConsumeEmptyGlyph(index++, charCode, x, y);
248 			advanceX = 0;
249 			advanceY = 0;
250 		} else {
251 			// get next increment for pen position
252 			if (spacing == B_CHAR_SPACING) {
253 				advanceX = glyph->precise_advance_x * size;
254 				advanceY = glyph->precise_advance_y * size;
255 			} else {
256 				advanceX = glyph->advance_x;
257 				advanceY = glyph->advance_y;
258 			}
259 
260 			// adjust for custom spacing
261 			if (delta != NULL) {
262 				advanceX += IsWhiteSpace(charCode)
263 					? delta->space : delta->nonspace;
264 			}
265 
266 			if (!consumer.ConsumeGlyph(index++, charCode, glyph, entry, x, y,
267 					advanceX, advanceY)) {
268 				advanceX = 0.0;
269 				advanceY = 0.0;
270 				break;
271 			}
272 		}
273 
274 		lastCharCode = charCode;
275 		if (utf8String - start + 1 > length)
276 			break;
277 	}
278 
279 	x += advanceX;
280 	y += advanceY;
281 	consumer.Finish(x, y);
282 
283 	return true;
284 }
285 
286 
287 inline const GlyphCache*
288 GlyphLayoutEngine::_CreateGlyph(FontCacheReference& cacheReference,
289 	BObjectList<FontCacheReference>& fallbacks,
290 	const ServerFont& font, bool forceVector, uint32 charCode)
291 {
292 	FontCacheEntry* entry = cacheReference.Entry();
293 
294 	// Avoid loading and locking the fallbacks if our font can create the glyph.
295 	if (entry->CanCreateGlyph(charCode)) {
296 		if (cacheReference.SetTo(entry, true))
297 			return entry->CreateGlyph(charCode);
298 		return NULL;
299 	}
300 
301 	if (fallbacks.IsEmpty()) {
302 		// We need to create new glyphs with the engine of the fallback font
303 		// and store them in the main font cache (not just transfer them from
304 		// one cache to the other). So we need both to be write-locked.
305 		// The main font is unlocked first, in case it is also in the fallback
306 		// list, so that we always keep the same order to avoid deadlocks.
307 		cacheReference.UnlockAndDisown();
308 		_PopulateAndLockFallbacks(fallbacks, font, forceVector);
309 		if (!cacheReference.SetTo(entry, true)) {
310 			return NULL;
311 		}
312 	}
313 
314 	int32 count = fallbacks.CountItems();
315 	for (int32 index = 0; index < count; index++) {
316 		FontCacheEntry* fallbackEntry = fallbacks.ItemAt(index)->Entry();
317 		if (fallbackEntry->CanCreateGlyph(charCode))
318 			return entry->CreateGlyph(charCode, fallbackEntry);
319 	}
320 
321 	return NULL;
322 }
323 
324 
325 inline void
326 GlyphLayoutEngine::_PopulateAndLockFallbacks(
327 	BObjectList<FontCacheReference>& fallbacksList,
328 	const ServerFont& font, bool forceVector)
329 {
330 	ASSERT(fallbacksList.IsEmpty());
331 
332 	// TODO: We always get the fallback glyphs from the Noto family, but of
333 	// course the fallback font should a) contain the missing glyphs at all
334 	// and b) be similar to the original font. So there should be a mapping
335 	// of some kind to know the most suitable fallback font.
336 	static const char* fallbacks[] = {
337 		"Noto Sans Display",
338 		"Noto Sans Thai",
339 		"Noto Sans CJK JP",
340 		"Noto Sans Symbols",
341 		"Noto Sans Symbols2"
342 	};
343 
344 	if (!gFontManager->Lock())
345 		return;
346 
347 	static const int nStyles = 3;
348 	static const int nFallbacks = B_COUNT_OF(fallbacks);
349 	FontCacheEntry* fallbackCacheEntries[nStyles * nFallbacks];
350 	int entries = 0;
351 
352 	for (int c = 0; c < nStyles; c++) {
353 		const char* fontStyle;
354 		if (c == 0)
355 			fontStyle = font.Style();
356 		else if (c == 1)
357 			fontStyle = "Regular";
358 		else
359 			fontStyle = NULL;
360 
361 		for (int i = 0; i < nFallbacks; i++) {
362 
363 			FontStyle* fallbackStyle = gFontManager->GetStyle(fallbacks[i],
364 				fontStyle, 0xffff, 0);
365 
366 			if (fallbackStyle == NULL)
367 				continue;
368 
369 			ServerFont fallbackFont(*fallbackStyle, font.Size());
370 
371 			FontCacheEntry* entry = FontCacheEntryFor(
372 				fallbackFont, forceVector);
373 
374 			if (entry == NULL)
375 				continue;
376 
377 			fallbackCacheEntries[entries++] = entry;
378 
379 		}
380 
381 	}
382 
383 	gFontManager->Unlock();
384 
385 	// Finally lock the entries and save their references
386 	for (int i = 0; i < entries; i++) {
387 		FontCacheReference* cacheReference =
388 			new(std::nothrow) FontCacheReference();
389 		if (cacheReference != NULL) {
390 			if (cacheReference->SetTo(fallbackCacheEntries[i], true))
391 				fallbacksList.AddItem(cacheReference);
392 			else
393 				delete cacheReference;
394 		}
395 	}
396 
397 	return;
398 }
399 
400 
401 #endif // GLYPH_LAYOUT_ENGINE_H
402