xref: /haiku/src/servers/app/font/FontCacheEntry.cpp (revision 9f3bdf3d039430b5172c424def20ce5d9f7367d4)
1 /*
2  * Copyright 2007-2009, Haiku. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Maxim Shemanarev <mcseemagg@yahoo.com>
7  *		Stephan Aßmus <superstippi@gmx.de>
8  *		Andrej Spielmann, <andrej.spielmann@seh.ox.ac.uk>
9  */
10 
11 //----------------------------------------------------------------------------
12 // Anti-Grain Geometry - Version 2.4
13 // Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
14 //
15 // Permission to copy, use, modify, sell and distribute this software
16 // is granted provided this copyright notice appears in all copies.
17 // This software is provided "as is" without express or implied
18 // warranty, and with no claim as to its suitability for any purpose.
19 //
20 //----------------------------------------------------------------------------
21 // Contact: mcseem@antigrain.com
22 //			mcseemagg@yahoo.com
23 //			http://www.antigrain.com
24 //----------------------------------------------------------------------------
25 
26 
27 #include "FontCacheEntry.h"
28 
29 #include <string.h>
30 
31 #include <new>
32 
33 #include <Autolock.h>
34 
35 #include <agg_array.h>
36 #include <utf8_functions.h>
37 #include <util/OpenHashTable.h>
38 
39 #include "GlobalSubpixelSettings.h"
40 
41 
42 BLocker FontCacheEntry::sUsageUpdateLock("FontCacheEntry usage lock");
43 
44 
45 class FontCacheEntry::GlyphCachePool {
46 	// This class needs to be defined before any inline functions, as otherwise
47 	// gcc2 will barf in debug mode.
48 	struct GlyphHashTableDefinition {
49 		typedef uint32		KeyType;
50 		typedef	GlyphCache	ValueType;
51 
52 		size_t HashKey(uint32 key) const
53 		{
54 			return key;
55 		}
56 
57 		size_t Hash(GlyphCache* value) const
58 		{
59 			return value->glyph_index;
60 		}
61 
62 		bool Compare(uint32 key, GlyphCache* value) const
63 		{
64 			return value->glyph_index == key;
65 		}
66 
67 		GlyphCache*& GetLink(GlyphCache* value) const
68 		{
69 			return value->hash_link;
70 		}
71 	};
72 public:
73 	GlyphCachePool()
74 	{
75 	}
76 
77 	~GlyphCachePool()
78 	{
79 		GlyphCache* glyph = fGlyphTable.Clear(true);
80 		while (glyph != NULL) {
81 			GlyphCache* next = glyph->hash_link;
82 			delete glyph;
83 			glyph = next;
84 		}
85 	}
86 
87 	status_t Init()
88 	{
89 		return fGlyphTable.Init();
90 	}
91 
92 	const GlyphCache* FindGlyph(uint32 glyphIndex) const
93 	{
94 		return fGlyphTable.Lookup(glyphIndex);
95 	}
96 
97 	GlyphCache* CacheGlyph(uint32 glyphIndex,
98 		uint32 dataSize, glyph_data_type dataType, const agg::rect_i& bounds,
99 		float advanceX, float advanceY, float preciseAdvanceX,
100 		float preciseAdvanceY, float insetLeft, float insetRight)
101 	{
102 		GlyphCache* glyph = fGlyphTable.Lookup(glyphIndex);
103 		if (glyph != NULL)
104 			return NULL;
105 
106 		glyph = new(std::nothrow) GlyphCache(glyphIndex, dataSize, dataType,
107 			bounds, advanceX, advanceY, preciseAdvanceX, preciseAdvanceY,
108 			insetLeft, insetRight);
109 		if (glyph == NULL || glyph->data == NULL) {
110 			delete glyph;
111 			return NULL;
112 		}
113 
114 		// TODO: The HashTable grows without bounds. We should cleanup
115 		// older entries from time to time.
116 
117 		fGlyphTable.Insert(glyph);
118 
119 		return glyph;
120 	}
121 
122 private:
123 	typedef BOpenHashTable<GlyphHashTableDefinition> GlyphTable;
124 
125 	GlyphTable	fGlyphTable;
126 };
127 
128 
129 // #pragma mark -
130 
131 
132 FontCacheEntry::FontCacheEntry()
133 	:
134 	MultiLocker("FontCacheEntry lock"),
135 	fGlyphCache(new(std::nothrow) GlyphCachePool()),
136 	fEngine(),
137 	fLastUsedTime(LONGLONG_MIN),
138 	fUseCounter(0)
139 {
140 }
141 
142 
143 FontCacheEntry::~FontCacheEntry()
144 {
145 //printf("~FontCacheEntry()\n");
146 }
147 
148 
149 bool
150 FontCacheEntry::Init(const ServerFont& font, bool forceVector)
151 {
152 	if (!fGlyphCache.IsSet())
153 		return false;
154 
155 	glyph_rendering renderingType = _RenderTypeFor(font, forceVector);
156 
157 	// TODO: encoding from font
158 	FT_Encoding charMap = FT_ENCODING_NONE;
159 	bool hinting = font.Hinting();
160 
161 	bool success;
162 	if (font.FontData() != NULL)
163 		success = fEngine.Init(NULL, font.FaceIndex(), font.Size(), charMap,
164 			renderingType, hinting, (const void*)font.FontData(), font.FontDataSize());
165 	else
166 		success = fEngine.Init(font.Path(), font.FaceIndex(), font.Size(), charMap,
167 			renderingType, hinting);
168 
169 	if (!success) {
170 		fprintf(stderr, "FontCacheEntry::Init() - some error loading font "
171 			"file %s\n", font.Path());
172 		return false;
173 	}
174 
175 	if (fGlyphCache->Init() != B_OK) {
176 		fprintf(stderr, "FontCacheEntry::Init() - failed to allocate "
177 			"GlyphCache table for font file %s\n", font.Path());
178 		return false;
179 	}
180 
181 	return true;
182 }
183 
184 
185 bool
186 FontCacheEntry::HasGlyphs(const char* utf8String, ssize_t length) const
187 {
188 	uint32 glyphCode;
189 	const char* start = utf8String;
190 	while ((glyphCode = UTF8ToCharCode(&utf8String))) {
191 		if (fGlyphCache->FindGlyph(glyphCode) == NULL)
192 			return false;
193 		if (utf8String - start + 1 > length)
194 			break;
195 	}
196 	return true;
197 }
198 
199 
200 inline bool
201 render_as_space(uint32 glyphCode)
202 {
203 	// whitespace: render as space
204 	// as per Unicode PropList.txt: White_Space
205 	return (glyphCode >= 0x0009 && glyphCode <= 0x000d)
206 			// control characters
207 		|| (glyphCode == 0x0085)
208 			// another control
209 		|| (glyphCode == 0x00a0)
210 			// no-break space
211 		|| (glyphCode == 0x1680)
212 			// ogham space mark
213 		|| (glyphCode >= 0x2000 && glyphCode <= 0x200a)
214 			// en quand, hair space
215 		|| (glyphCode >= 0x2028 && glyphCode <= 0x2029)
216 			// line and paragraph separators
217 		|| (glyphCode == 0x202f)
218 			// narrow no-break space
219 		|| (glyphCode == 0x205f)
220 			// medium math space
221 		|| (glyphCode == 0x3000)
222 			// ideographic space
223 		;
224 }
225 
226 
227 inline bool
228 render_as_zero_width(uint32 glyphCode)
229 {
230 	// ignorable chars: render as invisible
231 	// as per Unicode DerivedCoreProperties.txt: Default_Ignorable_Code_Point.
232 	// We also don't want tofu for noncharacters if we ever get one.
233 	return (glyphCode == 0x00ad)
234 			// soft hyphen
235 		|| (glyphCode == 0x034f)
236 			// combining grapheme joiner
237 		|| (glyphCode == 0x061c)
238 			// arabic letter mark
239 		|| (glyphCode >= 0x115f && glyphCode <= 0x1160)
240 			// hangul fillers
241 		|| (glyphCode >= 0x17b4 && glyphCode <= 0x17b5)
242 			// ignorable khmer vowels
243 		|| (glyphCode >= 0x180b && glyphCode <= 0x180f)
244 			// mongolian variation selectors and vowel separator
245 		|| (glyphCode >= 0x200b && glyphCode <= 0x200f)
246 			// zero width space, cursive joiners, ltr marks
247 		|| (glyphCode >= 0x202a && glyphCode <= 0x202e)
248 			// left to right embed, override
249 		|| (glyphCode >= 0x2060 && glyphCode <= 0x206f)
250 			// word joiner, invisible math operators, reserved
251 		|| (glyphCode == 0x3164)
252 			// hangul filler
253 		|| (glyphCode >= 0xfe00 && glyphCode <= 0xfe0f)
254 			// variation selectors
255 		|| (glyphCode == 0xfeff)
256 			// zero width no-break space
257 		|| (glyphCode == 0xffa0)
258 			// halfwidth hangul filler
259 		|| (glyphCode >= 0xfff0 && glyphCode <= 0xfff8)
260 			// reserved
261 		|| (glyphCode >= 0x1bca0 && glyphCode <= 0x1bca3)
262 			// shorthand format controls
263 		|| (glyphCode >= 0x1d173 && glyphCode <= 0x1d17a)
264 			// musical symbols
265 		|| (glyphCode >= 0xe0000 && glyphCode <= 0xe01ef)
266 			// variation selectors, tag space, reserved
267 		|| (glyphCode >= 0xe01f0 && glyphCode <= 0xe0fff)
268 			// reserved
269 		|| ((glyphCode & 0xffff) >= 0xfffe)
270 			// noncharacters
271 		|| ((glyphCode >= 0xfdd0 && glyphCode <= 0xfdef)
272 			&& glyphCode != 0xfdd1)
273 			// noncharacters; 0xfdd1 is used internally to force .notdef glyph
274 		;
275 }
276 
277 
278 const GlyphCache*
279 FontCacheEntry::CachedGlyph(uint32 glyphCode)
280 {
281 	// Only requires a read lock.
282 	return fGlyphCache->FindGlyph(glyphCode);
283 }
284 
285 
286 bool
287 FontCacheEntry::CanCreateGlyph(uint32 glyphCode)
288 {
289 	// Note that this bypass any fallback or caching because it is used in
290 	// the fallback code itself.
291 	uint32 glyphIndex = fEngine.GlyphIndexForGlyphCode(glyphCode);
292 	return glyphIndex != 0;
293 }
294 
295 
296 const GlyphCache*
297 FontCacheEntry::CreateGlyph(uint32 glyphCode, FontCacheEntry* fallbackEntry)
298 {
299 	// We cache the glyph by the requested glyphCode. The FontEngine of this
300 	// FontCacheEntry may not contain a glyph for the given code, in which case
301 	// we ask the fallbackEntry for the code to index translation and let it
302 	// generate the glyph data. We will still use our own cache for storing the
303 	// glyph. The next time it will be found (by glyphCode).
304 
305 	// NOTE: Both this and the fallback FontCacheEntry are expected to be
306 	// write-locked!
307 
308 	const GlyphCache* glyph = fGlyphCache->FindGlyph(glyphCode);
309 	if (glyph != NULL)
310 		return glyph;
311 
312 	FontEngine* engine = &fEngine;
313 	uint32 glyphIndex = engine->GlyphIndexForGlyphCode(glyphCode);
314 	if (glyphIndex == 0 && fallbackEntry != NULL) {
315 		// Our FontEngine does not contain this glyph, but we can retry with
316 		// the fallbackEntry.
317 		engine = &fallbackEntry->fEngine;
318 		glyphIndex = engine->GlyphIndexForGlyphCode(glyphCode);
319 	}
320 
321 	if (glyphIndex == 0) {
322 		if (render_as_zero_width(glyphCode)) {
323 			// cache and return a zero width glyph
324 			return fGlyphCache->CacheGlyph(glyphCode, 0, glyph_data_invalid,
325 				agg::rect_i(0, 0, -1, -1), 0, 0, 0, 0, 0, 0);
326 		}
327 
328 		// reset to our engine
329 		engine = &fEngine;
330 		if (render_as_space(glyphCode)) {
331 			// get the normal space glyph
332 			glyphIndex = engine->GlyphIndexForGlyphCode(0x20 /* space */);
333 		}
334 	}
335 
336 	if (engine->PrepareGlyph(glyphIndex)) {
337 		glyph = fGlyphCache->CacheGlyph(glyphCode,
338 			engine->DataSize(), engine->DataType(), engine->Bounds(),
339 			engine->AdvanceX(), engine->AdvanceY(),
340 			engine->PreciseAdvanceX(), engine->PreciseAdvanceY(),
341 			engine->InsetLeft(), engine->InsetRight());
342 
343 		if (glyph != NULL)
344 			engine->WriteGlyphTo(glyph->data);
345 	}
346 
347 	return glyph;
348 }
349 
350 
351 void
352 FontCacheEntry::InitAdaptors(const GlyphCache* glyph,
353 	double x, double y, GlyphMonoAdapter& monoAdapter,
354 	GlyphGray8Adapter& gray8Adapter, GlyphPathAdapter& pathAdapter,
355 	double scale)
356 {
357 	if (glyph == NULL)
358 		return;
359 
360 	switch(glyph->data_type) {
361 		case glyph_data_mono:
362 			monoAdapter.init(glyph->data, glyph->data_size, x, y);
363 			break;
364 
365 		case glyph_data_gray8:
366 			gray8Adapter.init(glyph->data, glyph->data_size, x, y);
367 			break;
368 
369 		case glyph_data_subpix:
370 			gray8Adapter.init(glyph->data, glyph->data_size, x, y);
371 			break;
372 
373 		case glyph_data_outline:
374 			pathAdapter.init(glyph->data, glyph->data_size, x, y, scale);
375 			break;
376 
377 		default:
378 			break;
379 	}
380 }
381 
382 
383 bool
384 FontCacheEntry::GetKerning(uint32 glyphCode1, uint32 glyphCode2,
385 	double* x, double* y)
386 {
387 	return fEngine.GetKerning(glyphCode1, glyphCode2, x, y);
388 }
389 
390 
391 /*static*/ void
392 FontCacheEntry::GenerateSignature(char* signature, size_t signatureSize,
393 	const ServerFont& font, bool forceVector)
394 {
395 	glyph_rendering renderingType = _RenderTypeFor(font, forceVector);
396 
397 	// TODO: read more of these from the font
398 	FT_Encoding charMap = FT_ENCODING_NONE;
399 	bool hinting = font.Hinting();
400 	uint8 averageWeight = gSubpixelAverageWeight;
401 
402 	snprintf(signature, signatureSize, "%" B_PRId32 ",%p,%u,%d,%d,%.1f,%d,%d",
403 		font.GetFamilyAndStyle(), font.Manager(), charMap,
404 		font.Face(), int(renderingType), font.Size(), hinting, averageWeight);
405 }
406 
407 
408 void
409 FontCacheEntry::UpdateUsage()
410 {
411 	// this is a static lock to prevent usage of too many semaphores,
412 	// but on the other hand, it is not so nice to be using a lock
413 	// here at all
414 	// the hope is that the time is so short to hold this lock, that
415 	// there is not much contention
416 	BAutolock _(sUsageUpdateLock);
417 
418 	fLastUsedTime = system_time();
419 	fUseCounter++;
420 }
421 
422 
423 /*static*/ glyph_rendering
424 FontCacheEntry::_RenderTypeFor(const ServerFont& font, bool forceVector)
425 {
426 	glyph_rendering renderingType = gSubpixelAntialiasing ?
427 		glyph_ren_subpix : glyph_ren_native_gray8;
428 
429 	if (forceVector || font.Rotation() != 0.0 || font.Shear() != 90.0
430 		|| font.FalseBoldWidth() != 0.0
431 		|| (font.Flags() & B_DISABLE_ANTIALIASING) != 0
432 		|| font.Size() > 30
433 		|| !font.Hinting()) {
434 		renderingType = glyph_ren_outline;
435 	}
436 
437 	return renderingType;
438 }
439