xref: /haiku/src/servers/app/font/FontCacheEntry.cpp (revision 04a0e9c7b68cbe3a43d38e2bca8e860fd80936fb)
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 insetLeft, float insetRight)
100 	{
101 		GlyphCache* glyph = fGlyphTable.Lookup(glyphIndex);
102 		if (glyph != NULL)
103 			return NULL;
104 
105 		glyph = new(std::nothrow) GlyphCache(glyphIndex, dataSize, dataType,
106 			bounds, advanceX, advanceY, insetLeft, insetRight);
107 		if (glyph == NULL || glyph->data == NULL) {
108 			delete glyph;
109 			return NULL;
110 		}
111 
112 		// TODO: The HashTable grows without bounds. We should cleanup
113 		// older entries from time to time.
114 
115 		fGlyphTable.Insert(glyph);
116 
117 		return glyph;
118 	}
119 
120 private:
121 	typedef BOpenHashTable<GlyphHashTableDefinition> GlyphTable;
122 
123 	GlyphTable	fGlyphTable;
124 };
125 
126 
127 // #pragma mark -
128 
129 
130 FontCacheEntry::FontCacheEntry()
131 	:
132 	MultiLocker("FontCacheEntry lock"),
133 	fGlyphCache(new(std::nothrow) GlyphCachePool()),
134 	fEngine(),
135 	fLastUsedTime(LONGLONG_MIN),
136 	fUseCounter(0)
137 {
138 }
139 
140 
141 FontCacheEntry::~FontCacheEntry()
142 {
143 //printf("~FontCacheEntry()\n");
144 	delete fGlyphCache;
145 }
146 
147 
148 bool
149 FontCacheEntry::Init(const ServerFont& font)
150 {
151 	if (fGlyphCache == NULL)
152 		return false;
153 
154 	glyph_rendering renderingType = _RenderTypeFor(font);
155 
156 	// TODO: encoding from font
157 	FT_Encoding charMap = FT_ENCODING_NONE;
158 	bool hinting = font.Hinting();
159 
160 	if (!fEngine.Init(font.Path(), 0, font.Size(), charMap,
161 			renderingType, hinting)) {
162 		fprintf(stderr, "FontCacheEntry::Init() - some error loading font "
163 			"file %s\n", font.Path());
164 		return false;
165 	}
166 	if (fGlyphCache->Init() != B_OK) {
167 		fprintf(stderr, "FontCacheEntry::Init() - failed to allocate "
168 			"GlyphCache table for font file %s\n", font.Path());
169 		return false;
170 	}
171 
172 	return true;
173 }
174 
175 
176 bool
177 FontCacheEntry::HasGlyphs(const char* utf8String, ssize_t length) const
178 {
179 	uint32 glyphCode;
180 	const char* start = utf8String;
181 	while ((glyphCode = UTF8ToCharCode(&utf8String))) {
182 		if (fGlyphCache->FindGlyph(glyphCode) == NULL)
183 			return false;
184 		if (utf8String - start + 1 > length)
185 			break;
186 	}
187 	return true;
188 }
189 
190 
191 inline bool
192 render_as_space(uint32 glyphCode)
193 {
194 	// whitespace: render as space
195 	// as per Unicode PropList.txt: White_Space
196 	return (glyphCode >= 0x0009 && glyphCode <= 0x000d)
197 			// control characters
198 		|| (glyphCode == 0x0085)
199 			// another control
200 		|| (glyphCode == 0x00a0)
201 			// no-break space
202 		|| (glyphCode == 0x1680)
203 			// ogham space mark
204 		|| (glyphCode == 0x180e)
205 			// mongolian vowel separator
206 		|| (glyphCode >= 0x2000 && glyphCode <= 0x200a)
207 			// en quand, hair space
208 		|| (glyphCode >= 0x2028 && glyphCode <= 0x2029)
209 			// line and paragraph separators
210 		|| (glyphCode == 0x202f)
211 			// narrow no-break space
212 		|| (glyphCode == 0x205f)
213 			// medium math space
214 		|| (glyphCode == 0x3000)
215 			// ideographic space
216 		;
217 }
218 
219 
220 inline bool
221 render_as_zero_width(uint32 glyphCode)
222 {
223 	// ignorable chars: render as invisible
224 	// as per Unicode DerivedCoreProperties.txt: Default_Ignorable_Code_Point
225 	return (glyphCode == 0x00ad)
226 			// soft hyphen
227 		|| (glyphCode == 0x034f)
228 			// combining grapheme joiner
229 		|| (glyphCode >= 0x115f && glyphCode <= 0x1160)
230 			// hangul fillers
231 		|| (glyphCode >= 0x17b4 && glyphCode <= 0x17b5)
232 			// ignorable khmer vowels
233 		|| (glyphCode >= 0x180b && glyphCode <= 0x180d)
234 			// variation selectors
235 		|| (glyphCode >= 0x200b && glyphCode <= 0x200f)
236 			// zero width space, cursive joiners, ltr marks
237 		|| (glyphCode >= 0x202a && glyphCode <= 0x202e)
238 			// left to right embed, override
239 		|| (glyphCode >= 0x2060 && glyphCode <= 0x206f)
240 			// word joiner, invisible math operators, reserved
241 		|| (glyphCode == 0x3164)
242 			// hangul filler
243 		|| (glyphCode >= 0xfe00 && glyphCode <= 0xfe0f)
244 			// variation selectors
245 		|| (glyphCode == 0xfeff)
246 			// zero width no-break space
247 		|| (glyphCode == 0xffa0)
248 			// halfwidth hangul filler
249 		|| (glyphCode >= 0xfff0 && glyphCode <= 0xfff8)
250 			// reserved
251 		|| (glyphCode >= 0x1d173 && glyphCode <= 0x1d17a)
252 			// musical symbols
253 		|| (glyphCode >= 0xe0000 && glyphCode <= 0xe01ef)
254 			// variation selectors, tag space, reserved
255 		;
256 }
257 
258 
259 const GlyphCache*
260 FontCacheEntry::CachedGlyph(uint32 glyphCode)
261 {
262 	// Only requires a read lock.
263 	return fGlyphCache->FindGlyph(glyphCode);
264 }
265 
266 
267 const GlyphCache*
268 FontCacheEntry::CreateGlyph(uint32 glyphCode, FontCacheEntry* fallbackEntry)
269 {
270 	// We cache the glyph by the requested glyphCode. The FontEngine of this
271 	// FontCacheEntry may not contain a glyph for the given code, in which case
272 	// we ask the fallbackEntry for the code to index translation and let it
273 	// generate the glyph data. We will still use our own cache for storing the
274 	// glyph. The next time it will be found (by glyphCode).
275 
276 	// NOTE: Both this and the fallback FontCacheEntry are expected to be
277 	// write-locked!
278 
279 	const GlyphCache* glyph = fGlyphCache->FindGlyph(glyphCode);
280 	if (glyph != NULL)
281 		return glyph;
282 
283 	FontEngine* engine = &fEngine;
284 	uint32 glyphIndex = engine->GlyphIndexForGlyphCode(glyphCode);
285 	if (glyphIndex == 0 && fallbackEntry != NULL) {
286 		// Our FontEngine does not contain this glyph, but we can retry with
287 		// the fallbackEntry.
288 		engine = &fallbackEntry->fEngine;
289 		glyphIndex = engine->GlyphIndexForGlyphCode(glyphCode);
290 	}
291 
292 	if (glyphIndex == 0) {
293 		if (render_as_zero_width(glyphCode)) {
294 			// cache and return a zero width glyph
295 			return fGlyphCache->CacheGlyph(glyphCode, 0, glyph_data_invalid,
296 				agg::rect_i(0, 0, -1, -1), 0, 0, 0, 0);
297 		}
298 
299 		// reset to our engine
300 		engine = &fEngine;
301 		if (render_as_space(glyphCode)) {
302 			// get the normal space glyph
303 			glyphIndex = engine->GlyphIndexForGlyphCode(0x20 /* space */);
304 		} else {
305 			// render the "missing glyph box" (by simply keeping glyphIndex 0)
306 		}
307 	}
308 
309 	if (engine->PrepareGlyph(glyphIndex)) {
310 		glyph = fGlyphCache->CacheGlyph(glyphCode,
311 			engine->DataSize(), engine->DataType(), engine->Bounds(),
312 			engine->AdvanceX(), engine->AdvanceY(),
313 			engine->InsetLeft(), engine->InsetRight());
314 
315 		if (glyph != NULL)
316 			engine->WriteGlyphTo(glyph->data);
317 	}
318 
319 	return glyph;
320 }
321 
322 
323 void
324 FontCacheEntry::InitAdaptors(const GlyphCache* glyph,
325 	double x, double y, GlyphMonoAdapter& monoAdapter,
326 	GlyphGray8Adapter& gray8Adapter, GlyphPathAdapter& pathAdapter,
327 	double scale)
328 {
329 	if (!glyph)
330 		return;
331 
332 	switch(glyph->data_type) {
333 		case glyph_data_mono:
334 			monoAdapter.init(glyph->data, glyph->data_size, x, y);
335 			break;
336 
337 		case glyph_data_gray8:
338 			gray8Adapter.init(glyph->data, glyph->data_size, x, y);
339 			break;
340 
341 		case glyph_data_subpix:
342 			gray8Adapter.init(glyph->data, glyph->data_size, x, y);
343 			break;
344 
345 		case glyph_data_outline:
346 			pathAdapter.init(glyph->data, glyph->data_size, x, y, scale);
347 			break;
348 
349 		default:
350 			break;
351 	}
352 }
353 
354 
355 bool
356 FontCacheEntry::GetKerning(uint32 glyphCode1, uint32 glyphCode2,
357 	double* x, double* y)
358 {
359 	return fEngine.GetKerning(glyphCode1, glyphCode2, x, y);
360 }
361 
362 
363 /*static*/ void
364 FontCacheEntry::GenerateSignature(char* signature, size_t signatureSize,
365 	const ServerFont& font)
366 {
367 	glyph_rendering renderingType = _RenderTypeFor(font);
368 
369 	// TODO: read more of these from the font
370 	FT_Encoding charMap = FT_ENCODING_NONE;
371 	bool hinting = font.Hinting();
372 	uint8 averageWeight = gSubpixelAverageWeight;
373 
374 	snprintf(signature, signatureSize, "%" B_PRId32 ",%u,%d,%d,%.1f,%d,%d",
375 		font.GetFamilyAndStyle(), charMap,
376 		font.Face(), int(renderingType), font.Size(), hinting, averageWeight);
377 }
378 
379 
380 void
381 FontCacheEntry::UpdateUsage()
382 {
383 	// this is a static lock to prevent usage of too many semaphores,
384 	// but on the other hand, it is not so nice to be using a lock
385 	// here at all
386 	// the hope is that the time is so short to hold this lock, that
387 	// there is not much contention
388 	BAutolock _(sUsageUpdateLock);
389 
390 	fLastUsedTime = system_time();
391 	fUseCounter++;
392 }
393 
394 
395 /*static*/ glyph_rendering
396 FontCacheEntry::_RenderTypeFor(const ServerFont& font)
397 {
398 	glyph_rendering renderingType = gSubpixelAntialiasing ?
399 		glyph_ren_subpix : glyph_ren_native_gray8;
400 
401 	if (font.Rotation() != 0.0 || font.Shear() != 90.0
402 		|| font.FalseBoldWidth() != 0.0
403 		|| (font.Flags() & B_DISABLE_ANTIALIASING) != 0
404 		|| font.Size() > 30
405 		|| !font.Hinting()) {
406 		renderingType = glyph_ren_outline;
407 	}
408 
409 	return renderingType;
410 }
411