/src/canvas/default/ftk_font.c

http://ftk.googlecode.com/ · C · 381 lines · 288 code · 56 blank · 37 comment · 69 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. * History:
  27. * ================================================================
  28. * 2010-07-18 Li XianJing <xianjimli@hotmail.com> created
  29. * 2011-04-08 Li XianJing <xianjimli@hotmail.com> add ftk_font_cache.
  30. * 2011-04-12 Li XianJing <xianjimli@hotmail.com> add lru replace algo.
  31. *
  32. */
  33. #include "ftk_log.h"
  34. #include "ftk_font.h"
  35. #include "ftk_util.h"
  36. int ftk_font_get_char_extent(FtkFont* thiz, unsigned short unicode)
  37. {
  38. int extent = 0;
  39. FtkGlyph glyph = {0};
  40. return_val_if_fail(thiz != NULL, 0);
  41. if(thiz->get_char_extent != NULL)
  42. {
  43. return thiz->get_char_extent(thiz, unicode);
  44. }
  45. if(unicode == ' ')
  46. {
  47. return FTK_SPACE_WIDTH;
  48. }
  49. else if(unicode == '\t')
  50. {
  51. return FTK_SPACE_WIDTH * FTK_TAB_WIDTH;
  52. }
  53. else if(unicode == '\r' || unicode == '\n')
  54. {
  55. return 0;
  56. }
  57. if(ftk_font_lookup(thiz, unicode, &glyph) == RET_OK)
  58. {
  59. extent = glyph.x + glyph.w + 1;
  60. }
  61. return extent;
  62. }
  63. FtkFontDesc* ftk_font_get_desc(FtkFont* thiz)
  64. {
  65. return thiz != NULL ? thiz->font_desc : NULL;
  66. }
  67. int ftk_font_get_extent(FtkFont* thiz, const char* str, int len)
  68. {
  69. int extent = 0;
  70. unsigned short code = 0;
  71. const char* iter = str;
  72. len = (len < 0 && str != NULL) ? strlen(str) : len;
  73. return_val_if_fail(thiz != NULL && str != NULL, 0);
  74. if(thiz->get_extent != NULL)
  75. {
  76. return thiz->get_extent(thiz, str, len);
  77. }
  78. len = len >= 0 ? len : (int)strlen(str);
  79. while(*iter && (iter - str) < len)
  80. {
  81. code = utf8_get_char(iter, &iter);
  82. if(code == 0 || code == 0xffff)
  83. {
  84. break;
  85. }
  86. extent += ftk_font_get_char_extent(thiz, code);
  87. }
  88. return extent;
  89. }
  90. //////////////////////////////////////////////////////
  91. typedef struct _FtkGlyphCache
  92. {
  93. FtkGlyph glyph;
  94. unsigned short access_nr;
  95. unsigned short load_time; /*unit: about one minute*/
  96. }FtkGlyphCache;
  97. typedef struct _FontPrivInfo
  98. {
  99. FtkFont* font;
  100. FtkGlyph* glyphs;
  101. FtkGlyph* free_glyphs;
  102. FtkGlyph** glyphs_ptr;
  103. int glyph_nr;
  104. int font_height;
  105. int max_glyph_nr;
  106. int one_glyph_size;
  107. int last_replaced;
  108. FtkGlyph* lrc_glyph;
  109. }PrivInfo;
  110. #define SHRINK_TIME(t) ((t) >> 16)
  111. static FtkGlyph* ftk_font_cache_alloc(FtkFont* thiz)
  112. {
  113. char* end = NULL;
  114. FtkGlyph* p = NULL;
  115. DECL_PRIV(thiz, priv);
  116. if(priv->lrc_glyph)
  117. {
  118. p = priv->lrc_glyph;
  119. priv->lrc_glyph = NULL;
  120. return p;
  121. }
  122. end = (char*)priv->glyphs + priv->max_glyph_nr * priv->one_glyph_size;
  123. if((char*)priv->free_glyphs < end)
  124. {
  125. p = priv->free_glyphs;
  126. priv->free_glyphs = (FtkGlyph*) ((char*) priv->free_glyphs + priv->one_glyph_size);
  127. }
  128. return p;
  129. }
  130. static Ret ftk_font_cache_remove_lru(FtkFont* thiz, unsigned short older_than)
  131. {
  132. int i = 0;
  133. int lru = 0;
  134. FtkGlyph* p = NULL;
  135. FtkGlyphCache* c = NULL;
  136. DECL_PRIV(thiz, priv);
  137. unsigned short lru_access_nr = 0xffff;
  138. unsigned short now = SHRINK_TIME(ftk_get_relative_time());
  139. return_val_if_fail(thiz != NULL, RET_FAIL);
  140. lru = priv->glyph_nr;
  141. i = (priv->last_replaced + 1) % priv->glyph_nr;
  142. for(; i < priv->glyph_nr; i++)
  143. {
  144. p = priv->glyphs_ptr[i];
  145. c = (FtkGlyphCache*)p;
  146. if((now - c->load_time) < older_than)
  147. {
  148. continue;
  149. }
  150. if(c->access_nr < lru_access_nr)
  151. {
  152. lru = i;
  153. lru_access_nr = c->access_nr;
  154. }
  155. }
  156. if(lru == priv->glyph_nr)
  157. {
  158. return ftk_font_cache_remove_lru(thiz, older_than >> 1);
  159. }
  160. i = lru;
  161. priv->last_replaced = lru;
  162. priv->lrc_glyph = priv->glyphs_ptr[i];
  163. for(; (i + 1) < priv->glyph_nr; i++)
  164. {
  165. priv->glyphs_ptr[i] = priv->glyphs_ptr[i+1];
  166. }
  167. priv->glyph_nr--;
  168. c = (FtkGlyphCache*)priv->lrc_glyph;
  169. ftk_logd("%s: remove %d access_nr=%d load_time =%d\n",
  170. __func__, lru, c->access_nr, c->load_time);
  171. return RET_OK;
  172. }
  173. static Ret ftk_font_cache_add(FtkFont* thiz, FtkGlyph* glyph)
  174. {
  175. int i = 0;
  176. int pos = 0;
  177. FtkGlyph* p = NULL;
  178. FtkGlyphCache* c = NULL;
  179. DECL_PRIV(thiz, priv);
  180. return_val_if_fail(thiz != NULL, RET_FAIL);
  181. if(glyph->w > priv->font_height || glyph->h > priv->font_height)
  182. {
  183. ftk_logd("%s: 0x%04x is too large to cache\n", __func__, glyph->code);
  184. return RET_FAIL;
  185. }
  186. for(pos = 0; pos < priv->glyph_nr; pos++)
  187. {
  188. if(priv->glyphs_ptr[pos]->code >= glyph->code)
  189. {
  190. break;
  191. }
  192. }
  193. if(priv->glyph_nr < priv->max_glyph_nr)
  194. {
  195. i = priv->glyph_nr;
  196. for(; i > pos; i--)
  197. {
  198. priv->glyphs_ptr[i] = priv->glyphs_ptr[i-1];
  199. }
  200. priv->glyph_nr++;
  201. priv->glyphs_ptr[pos] = ftk_font_cache_alloc(thiz);
  202. }
  203. else
  204. {
  205. return RET_FAIL;
  206. }
  207. p = priv->glyphs_ptr[pos];
  208. *p = *glyph;
  209. p->data = (unsigned char*)p + sizeof(FtkGlyphCache);
  210. memcpy(p->data, glyph->data, glyph->w * glyph->h);
  211. c = (FtkGlyphCache*)p;
  212. c->access_nr++;
  213. c->load_time = SHRINK_TIME(ftk_get_relative_time());
  214. ftk_logd("%s add 0x%04x at %d\n", __func__, p->code, pos);
  215. return RET_OK;
  216. }
  217. static Ret ftk_font_cache_lookup (FtkFont* thiz, unsigned short code, FtkGlyph* glyph)
  218. {
  219. int low = 0;
  220. int mid = 0;
  221. int high = 0;
  222. int result = 0;
  223. FtkGlyph* p = NULL;
  224. FtkGlyphCache* c = NULL;
  225. Ret ret = RET_FAIL;
  226. DECL_PRIV(thiz, priv);
  227. return_val_if_fail(thiz != NULL, RET_FAIL);
  228. high = priv->glyph_nr-1;
  229. while(low <= high)
  230. {
  231. mid = low + ((high - low) >> 1);
  232. p = priv->glyphs_ptr[mid];
  233. result = p->code - code;
  234. if(result == 0)
  235. {
  236. *glyph = *p;
  237. c = (FtkGlyphCache*)p;
  238. c->access_nr++;
  239. //ftk_logd("%s find %p at %d accces_nr=%d\n", __func__, p->code, mid, c->access_nr);
  240. return RET_OK;
  241. }
  242. else if(result < 0)
  243. {
  244. low = mid + 1;
  245. }
  246. else
  247. {
  248. high = mid - 1;
  249. }
  250. }
  251. if((ret = ftk_font_lookup(priv->font, code, glyph)) == RET_OK)
  252. {
  253. if(glyph->w > priv->font_height || glyph->h > priv->font_height)
  254. {
  255. ftk_logd("%s: 0x%04x is too large to cache%dx%d %dx%d\n",
  256. __func__, glyph->code, glyph->w, glyph->h, priv->font_height, priv->font_height);
  257. return RET_FAIL;
  258. }
  259. if(ftk_font_cache_add(thiz, glyph) != RET_OK)
  260. {
  261. if(ftk_font_cache_remove_lru(thiz, 64) == RET_OK)
  262. {
  263. ret = ftk_font_cache_add(thiz, glyph);
  264. //assert(ret == RET_OK);
  265. }
  266. }
  267. //for test
  268. #if 0
  269. FtkGlyph g = {0};
  270. assert(ftk_font_cache_lookup(thiz, code, &g) == RET_OK);
  271. assert(g.code == code);
  272. assert(memcmp(&g, glyph, sizeof(g) - 4) == 0);
  273. assert(memcmp(g.data, glyph->data, g.w * g.h) == 0);
  274. #endif
  275. }
  276. else
  277. {
  278. //assert(!"not found");
  279. }
  280. return RET_OK;
  281. }
  282. static int ftk_font_cache_height(FtkFont* thiz)
  283. {
  284. DECL_PRIV(thiz, priv);
  285. return ftk_font_height(priv->font);
  286. }
  287. static void ftk_font_cache_destroy(FtkFont* thiz)
  288. {
  289. if(thiz != NULL)
  290. {
  291. DECL_PRIV(thiz, priv);
  292. FTK_FREE(priv->glyphs);
  293. FTK_FREE(priv->glyphs_ptr);
  294. ftk_font_unref(priv->font);
  295. FTK_FREE(thiz);
  296. }
  297. return;
  298. }
  299. FtkFont* ftk_font_cache_create (FtkFont* font, int max_glyph_nr)
  300. {
  301. FtkFont* thiz = NULL;
  302. int font_height = ftk_font_height(font);
  303. return_val_if_fail(font != NULL && font_height > 0, NULL);
  304. if((thiz = FTK_NEW_PRIV(FtkFont)) != NULL)
  305. {
  306. DECL_PRIV(thiz, priv);
  307. thiz->ref = 1;
  308. thiz->height = ftk_font_cache_height;
  309. thiz->lookup = ftk_font_cache_lookup;
  310. thiz->destroy = ftk_font_cache_destroy;
  311. priv->font = font;
  312. ftk_font_ref(font);
  313. priv->glyph_nr = 0;
  314. thiz->font_desc = font->font_desc;
  315. priv->font_height = font_height;
  316. priv->max_glyph_nr = max_glyph_nr;
  317. priv->one_glyph_size = sizeof(FtkGlyphCache) + font_height * font_height ;
  318. priv->glyphs = (FtkGlyph*)FTK_ZALLOC(max_glyph_nr * priv->one_glyph_size);
  319. priv->glyphs_ptr = (FtkGlyph**)FTK_ZALLOC(max_glyph_nr * sizeof(FtkGlyphCache*));
  320. priv->free_glyphs = priv->glyphs;
  321. ftk_logd("%s: max_glyph_nr=%d memsize=%d\n", __func__,
  322. max_glyph_nr, max_glyph_nr * priv->one_glyph_size);
  323. }
  324. return thiz;
  325. }