PageRenderTime 29ms CodeModel.GetById 1ms app.highlight 24ms RepoModel.GetById 1ms app.codeStats 0ms

/src/canvas/default/ftk_font.c

http://ftk.googlecode.com/
C | 381 lines | 288 code | 56 blank | 37 comment | 67 complexity | 5a6612b6b20c993758d1843504842f1f MD5 | raw file
  1/*
  2 * File: ftk_font.c
  3 *
  4 * Author:  Li XianJing <xianjimli@hotmail.com>
  5 * Brief: common used font functions.
  6 *
  7 * Copyright (c) 2009 - 2011  Li XianJing <xianjimli@hotmail.com>
  8 *
  9 * Licensed under the Academic Free License version 2.1
 10 *
 11 * This program is free software; you can redistribute it and/or modify
 12 * it under the terms of the GNU General Public License as published by
 13 * the Free Software Foundation; either version 2 of the License, or
 14 * (at your option) any later version.
 15 *
 16 * This program is distributed in the hope that it will be useful,
 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 19 * GNU General Public License for more details.
 20 *
 21 * You should have received a copy of the GNU General Public License
 22 * along with this program; if not, write to the Free Software
 23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 24 */
 25
 26/*
 27 * History:
 28 * ================================================================
 29 * 2010-07-18 Li XianJing <xianjimli@hotmail.com> created
 30 * 2011-04-08 Li XianJing <xianjimli@hotmail.com> add ftk_font_cache.
 31 * 2011-04-12 Li XianJing <xianjimli@hotmail.com> add lru replace algo.
 32 *
 33 */
 34
 35#include "ftk_log.h"
 36#include "ftk_font.h"
 37#include "ftk_util.h"
 38
 39int ftk_font_get_char_extent(FtkFont* thiz, unsigned short unicode)
 40{
 41	int extent = 0;
 42	FtkGlyph glyph = {0};
 43	return_val_if_fail(thiz != NULL, 0);
 44
 45	if(thiz->get_char_extent != NULL)
 46	{
 47		return thiz->get_char_extent(thiz, unicode);
 48	}
 49
 50	if(unicode == ' ')
 51	{
 52		return FTK_SPACE_WIDTH;
 53	}
 54	else if(unicode == '\t')
 55	{
 56		return FTK_SPACE_WIDTH * FTK_TAB_WIDTH;
 57	}
 58	else if(unicode == '\r' || unicode == '\n')
 59	{
 60		return 0;
 61	}
 62
 63	if(ftk_font_lookup(thiz, unicode, &glyph) == RET_OK)
 64	{
 65		extent = glyph.x + glyph.w + 1;
 66	}
 67
 68	return extent;
 69}
 70
 71FtkFontDesc* ftk_font_get_desc(FtkFont* thiz)
 72{
 73	return thiz != NULL ? thiz->font_desc : NULL;
 74}
 75
 76int ftk_font_get_extent(FtkFont* thiz, const char* str, int len)
 77{
 78	int extent = 0;
 79	unsigned short code = 0;
 80	const char* iter = str;
 81	len = (len < 0 && str != NULL) ? strlen(str) : len;
 82	return_val_if_fail(thiz != NULL && str != NULL, 0);
 83	
 84	if(thiz->get_extent != NULL)
 85	{
 86		return thiz->get_extent(thiz, str, len);
 87	}
 88
 89	len = len >= 0 ? len : (int)strlen(str);
 90	while(*iter && (iter - str) < len)
 91	{
 92		code = utf8_get_char(iter, &iter);
 93		if(code == 0 || code == 0xffff)
 94		{
 95			break;
 96		}
 97		extent += ftk_font_get_char_extent(thiz, code);
 98	}
 99
100	return extent;
101}
102
103//////////////////////////////////////////////////////
104typedef struct _FtkGlyphCache
105{
106	FtkGlyph glyph;
107	unsigned short access_nr;
108	unsigned short load_time; /*unit: about one minute*/
109}FtkGlyphCache;
110
111typedef struct _FontPrivInfo
112{
113	FtkFont* font;
114	FtkGlyph* glyphs;
115	FtkGlyph* free_glyphs;
116	FtkGlyph** glyphs_ptr;
117	int glyph_nr;
118	int font_height;
119	int max_glyph_nr;
120	int one_glyph_size;
121	int last_replaced;
122	FtkGlyph* lrc_glyph;
123}PrivInfo;
124
125#define SHRINK_TIME(t) ((t) >> 16)
126
127static FtkGlyph* ftk_font_cache_alloc(FtkFont* thiz)
128{
129	char* end = NULL;
130	FtkGlyph* p = NULL;
131	DECL_PRIV(thiz, priv);
132
133	if(priv->lrc_glyph)
134	{
135		p = priv->lrc_glyph;
136		priv->lrc_glyph = NULL;
137
138		return p;
139	}
140
141	end = (char*)priv->glyphs + priv->max_glyph_nr * priv->one_glyph_size;
142
143	if((char*)priv->free_glyphs < end)
144	{
145		p = priv->free_glyphs;
146		priv->free_glyphs = (FtkGlyph*) ((char*) priv->free_glyphs + priv->one_glyph_size);
147	}
148
149	return p;
150}
151
152static Ret ftk_font_cache_remove_lru(FtkFont* thiz, unsigned short older_than)
153{
154	int i = 0;
155	int lru = 0;
156	FtkGlyph* p = NULL;
157	FtkGlyphCache* c = NULL;
158	DECL_PRIV(thiz, priv);
159	unsigned short lru_access_nr = 0xffff;
160	unsigned short now = SHRINK_TIME(ftk_get_relative_time());
161	return_val_if_fail(thiz != NULL, RET_FAIL);
162
163	lru = priv->glyph_nr;
164	i = (priv->last_replaced + 1) % priv->glyph_nr;
165	for(; i < priv->glyph_nr; i++)
166	{
167		p = priv->glyphs_ptr[i];
168		c = (FtkGlyphCache*)p;
169		if((now - c->load_time) < older_than)
170		{
171			continue;
172		}
173
174		if(c->access_nr < lru_access_nr)
175		{
176			lru = i;
177			lru_access_nr = c->access_nr;
178		}
179	}
180
181	if(lru == priv->glyph_nr)
182	{
183		return ftk_font_cache_remove_lru(thiz, older_than >> 1);
184	}
185
186	i = lru;
187	priv->last_replaced = lru;
188	priv->lrc_glyph = priv->glyphs_ptr[i];
189	for(; (i + 1) < priv->glyph_nr; i++)
190	{
191		priv->glyphs_ptr[i] = priv->glyphs_ptr[i+1];
192	}
193	priv->glyph_nr--;
194	c = (FtkGlyphCache*)priv->lrc_glyph;
195	
196	ftk_logd("%s: remove %d access_nr=%d load_time =%d\n", 
197		__func__, lru, c->access_nr, c->load_time);
198
199	return RET_OK;
200}
201
202static Ret ftk_font_cache_add(FtkFont* thiz, FtkGlyph* glyph)
203{
204	int i = 0;
205	int pos = 0;
206	FtkGlyph* p = NULL;
207	FtkGlyphCache* c = NULL;
208	DECL_PRIV(thiz, priv);
209	return_val_if_fail(thiz != NULL, RET_FAIL);
210
211	if(glyph->w > priv->font_height || glyph->h > priv->font_height)
212	{
213		ftk_logd("%s: 0x%04x is too large to cache\n", __func__, glyph->code);
214		return RET_FAIL;
215	}
216
217	for(pos = 0; pos < priv->glyph_nr; pos++)
218	{
219		if(priv->glyphs_ptr[pos]->code >= glyph->code)
220		{
221			break;
222		}
223	}
224
225	if(priv->glyph_nr < priv->max_glyph_nr)
226	{
227		i = priv->glyph_nr;
228		for(; i > pos; i--)
229		{
230			priv->glyphs_ptr[i] = priv->glyphs_ptr[i-1];
231		}
232		
233		priv->glyph_nr++;
234		priv->glyphs_ptr[pos] = ftk_font_cache_alloc(thiz);
235	}
236	else
237	{
238		return RET_FAIL;
239	}
240
241	p = priv->glyphs_ptr[pos];
242	
243	*p = *glyph;
244	p->data = (unsigned char*)p + sizeof(FtkGlyphCache);
245	memcpy(p->data, glyph->data, glyph->w * glyph->h);
246
247	c = (FtkGlyphCache*)p;
248	c->access_nr++;
249	c->load_time = SHRINK_TIME(ftk_get_relative_time());
250
251	ftk_logd("%s add 0x%04x at %d\n", __func__, p->code, pos);
252
253	return RET_OK;
254}
255
256static Ret ftk_font_cache_lookup (FtkFont* thiz, unsigned short code, FtkGlyph* glyph)
257{
258	int low    = 0;
259	int mid    = 0;
260	int high   = 0;
261	int result = 0;
262	FtkGlyph* p = NULL;
263	FtkGlyphCache* c = NULL;
264	Ret ret =  RET_FAIL;
265	DECL_PRIV(thiz, priv);
266	return_val_if_fail(thiz != NULL, RET_FAIL);
267
268	high = priv->glyph_nr-1;
269	while(low <= high)
270	{
271		mid  = low + ((high - low) >> 1);
272		p = priv->glyphs_ptr[mid];
273		result = p->code - code;
274		if(result == 0)
275		{
276			*glyph = *p;
277			c = (FtkGlyphCache*)p;
278			c->access_nr++;
279			//ftk_logd("%s find %p at %d accces_nr=%d\n", __func__, p->code, mid, c->access_nr);
280			return RET_OK;
281		}
282		else if(result < 0)
283		{
284			low = mid + 1;
285		}
286		else
287		{
288			high = mid - 1;
289		}
290	}
291
292	if((ret = ftk_font_lookup(priv->font, code, glyph)) == RET_OK)
293	{
294		if(glyph->w > priv->font_height || glyph->h > priv->font_height)
295		{
296			ftk_logd("%s: 0x%04x is too large to cache%dx%d %dx%d\n",
297				__func__, glyph->code, glyph->w, glyph->h, priv->font_height, priv->font_height);
298			return RET_FAIL;
299		}
300
301		if(ftk_font_cache_add(thiz, glyph) != RET_OK)
302		{
303			if(ftk_font_cache_remove_lru(thiz, 64) == RET_OK)
304			{
305				ret = ftk_font_cache_add(thiz, glyph);
306				//assert(ret == RET_OK);
307			}
308		}
309//for test
310#if 0
311		FtkGlyph g = {0};
312		assert(ftk_font_cache_lookup(thiz, code, &g) == RET_OK);
313		assert(g.code == code);
314		assert(memcmp(&g, glyph, sizeof(g) - 4) == 0);
315		assert(memcmp(g.data, glyph->data, g.w * g.h) == 0);
316#endif			
317	}
318	else
319	{
320		//assert(!"not found");
321	}
322
323	return RET_OK;
324}
325
326static int      ftk_font_cache_height(FtkFont* thiz)
327{
328	DECL_PRIV(thiz, priv);
329
330	return ftk_font_height(priv->font);
331}
332
333static void		ftk_font_cache_destroy(FtkFont* thiz)
334{
335	if(thiz != NULL)
336	{
337		DECL_PRIV(thiz, priv);
338		FTK_FREE(priv->glyphs);
339		FTK_FREE(priv->glyphs_ptr);
340		ftk_font_unref(priv->font);
341		FTK_FREE(thiz);
342	}
343
344	return;
345}
346
347FtkFont* ftk_font_cache_create (FtkFont* font, int max_glyph_nr)
348{
349	FtkFont* thiz = NULL;
350	int font_height = ftk_font_height(font);
351	return_val_if_fail(font != NULL && font_height > 0, NULL);
352
353	if((thiz = FTK_NEW_PRIV(FtkFont)) != NULL)
354	{
355		DECL_PRIV(thiz, priv);
356
357		thiz->ref = 1;
358		thiz->height = ftk_font_cache_height;
359		thiz->lookup = ftk_font_cache_lookup;
360		thiz->destroy = ftk_font_cache_destroy;
361
362		priv->font = font;
363		ftk_font_ref(font);
364
365		priv->glyph_nr = 0;
366		thiz->font_desc = font->font_desc;
367		priv->font_height = font_height;
368		priv->max_glyph_nr = max_glyph_nr;
369		priv->one_glyph_size = sizeof(FtkGlyphCache) + font_height * font_height ;
370
371		priv->glyphs = (FtkGlyph*)FTK_ZALLOC(max_glyph_nr * priv->one_glyph_size);
372		priv->glyphs_ptr = (FtkGlyph**)FTK_ZALLOC(max_glyph_nr * sizeof(FtkGlyphCache*));
373		priv->free_glyphs = priv->glyphs;
374		
375		ftk_logd("%s: max_glyph_nr=%d memsize=%d\n", __func__, 
376			max_glyph_nr, max_glyph_nr * priv->one_glyph_size);
377	}
378
379	return thiz;
380}
381