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
42BLocker FontCacheEntry::sUsageUpdateLock("FontCacheEntry usage lock");
43
44
45class 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	};
72public:
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
122private:
123	typedef BOpenHashTable<GlyphHashTableDefinition> GlyphTable;
124
125	GlyphTable	fGlyphTable;
126};
127
128
129// #pragma mark -
130
131
132FontCacheEntry::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
143FontCacheEntry::~FontCacheEntry()
144{
145//printf("~FontCacheEntry()\n");
146}
147
148
149bool
150FontCacheEntry::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
185bool
186FontCacheEntry::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
200inline bool
201render_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
227inline bool
228render_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
278const GlyphCache*
279FontCacheEntry::CachedGlyph(uint32 glyphCode)
280{
281	// Only requires a read lock.
282	return fGlyphCache->FindGlyph(glyphCode);
283}
284
285
286bool
287FontCacheEntry::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
296const GlyphCache*
297FontCacheEntry::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
351void
352FontCacheEntry::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
383bool
384FontCacheEntry::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
392FontCacheEntry::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
408void
409FontCacheEntry::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
424FontCacheEntry::_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