main.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531
  1. #include <cmath>
  2. #include <ctime>
  3. #include <cstdio>
  4. #include <vector>
  5. #include <cstdlib>
  6. #include <freetype.h>
  7. #include <stdint.h>
  8. #include <stb_image.c>
  9. #include <string>
  10. #include "BinPacker.hpp"
  11. using namespace std;
  12. static int font_size = 4;
  13. static char font_name[100];
  14. //-----------------------------------------------------------------------------
  15. struct sdf_glyph
  16. {
  17. int ID;
  18. int width, height;
  19. int x, y;
  20. float xoff, yoff;
  21. float xadv;
  22. };
  23. //-----------------------------------------------------------------------------
  24. bool render_signed_distance_font(FT_Library &ft_lib, const char* font_file, int texture_size, bool export_c_header);
  25. unsigned char get_SDF_radial(unsigned char *fontmap, int w, int h, int x, int y, int max_radius);
  26. bool gen_pack_list(FT_Face &ft_face, int pixel_size, int pack_tex_size, const std::vector<int> &render_list, std::vector<sdf_glyph> &packed_glyphs);
  27. int save_png_SDFont(const char* orig_filename, int img_width, int img_height, const std::vector<unsigned char> &img_data, const std::vector<sdf_glyph> &packed_glyphs);
  28. // number of rendered pixels per SDF pixel (larger value means higher quality, up to a point)
  29. const int scaler = 16;
  30. //-----------------------------------------------------------------------------
  31. int main(int argc, char **argv)
  32. {
  33. printf( "Crown Font Generator\n\n" );
  34. if(argc != 4)
  35. {
  36. printf( "Usage: ./fontgen <src> <name> <texture_dim>" );
  37. return -1;
  38. }
  39. const char* src = argv[1];
  40. const char* name = argv[2];
  41. strncpy(font_name, name, strlen(name));
  42. int texture_size = atoi(argv[3]);
  43. bool export_c_header = false;
  44. // Checks texture size
  45. if (texture_size < 64) texture_size = 64;
  46. if (texture_size > 4096) texture_size = 4096;
  47. FT_Library ft_lib;
  48. int ft_err = FT_Init_FreeType( &ft_lib );
  49. if( ft_err )
  50. {
  51. printf( "Failed to initialize the FreeType library!\n" );
  52. return -1;
  53. }
  54. render_signed_distance_font(ft_lib, src, texture_size, export_c_header);
  55. ft_err = FT_Done_FreeType( ft_lib );
  56. return 0;
  57. }
  58. //-----------------------------------------------------------------------------
  59. bool render_signed_distance_font(FT_Library &ft_lib, const char* font_file, int texture_size, bool export_c_header)
  60. {
  61. FT_Face ft_face;
  62. int ft_err = FT_New_Face(ft_lib, font_file, 0, &ft_face);
  63. if(ft_err)
  64. {
  65. printf( "Failed to read the font file '%s'\n", font_file );
  66. return false;
  67. }
  68. else
  69. {
  70. printf("Font:%s\n", font_file);
  71. int max_unicode_char= 65535;
  72. std::vector< int > render_list;
  73. for (int char_idx = 0; char_idx <= max_unicode_char; ++char_idx)
  74. {
  75. render_list.push_back(char_idx);
  76. }
  77. // find the perfect size
  78. std::vector< sdf_glyph > all_glyphs;
  79. // initial guess for the size of the Signed Distance Field font
  80. // (intentionally low, the first trial will be at font_size*2, so 8x8)
  81. bool keep_going = true;
  82. while (keep_going)
  83. {
  84. font_size <<= 1;
  85. keep_going = gen_pack_list(ft_face, font_size, texture_size, render_list, all_glyphs);
  86. }
  87. int font_size_step = font_size >> 2;
  88. while (font_size_step)
  89. {
  90. if (keep_going)
  91. {
  92. font_size += font_size_step;
  93. }
  94. else
  95. {
  96. font_size -= font_size_step;
  97. }
  98. font_size_step >>= 1;
  99. keep_going = gen_pack_list(ft_face, font_size, texture_size, render_list, all_glyphs);
  100. }
  101. // just in case
  102. while((!keep_going) && (font_size > 1))
  103. {
  104. --font_size;
  105. keep_going = gen_pack_list(ft_face, font_size, texture_size, render_list, all_glyphs);
  106. }
  107. printf("Font size = %i pixels\n\n", font_size);
  108. if (!keep_going)
  109. {
  110. printf( "The data will not fit in a texture %i^2\n", texture_size);
  111. return -1;
  112. }
  113. // set up the RAM for the final rendering/compositing
  114. // (use all four channels, so PNG compression is simple)
  115. std::vector<unsigned char> pdata(4 * texture_size * texture_size, 0);
  116. // render all the glyphs individually
  117. printf("Rendering into image... ");
  118. int packed_glyph_index = 0;
  119. int tin = clock();
  120. for (unsigned int char_index = 0; char_index < render_list.size(); ++char_index)
  121. {
  122. int glyph_index = FT_Get_Char_Index(ft_face, render_list[char_index]);
  123. if (glyph_index)
  124. {
  125. ft_err = FT_Load_Glyph(ft_face, glyph_index, 0);
  126. if (!ft_err)
  127. {
  128. ft_err = FT_Render_Glyph(ft_face->glyph, FT_RENDER_MODE_MONO);
  129. if (!ft_err)
  130. {
  131. // we have the glyph, already rendered, get the data about it
  132. int w = ft_face->glyph->bitmap.width;
  133. int h = ft_face->glyph->bitmap.rows;
  134. int p = ft_face->glyph->bitmap.pitch;
  135. // oversize the holding buffer so I can smooth it!
  136. int sw = w + scaler * 4;
  137. int sh = h + scaler * 4;
  138. unsigned char *smooth_buf = new unsigned char[sw*sh];
  139. for (int i = 0; i < sw*sh; ++i)
  140. {
  141. smooth_buf[i] = 0;
  142. }
  143. // copy the glyph into the buffer to be smoothed
  144. unsigned char *buf = ft_face->glyph->bitmap.buffer;
  145. for (int j = 0; j < h; ++j)
  146. {
  147. for (int i = 0; i < w; ++i)
  148. {
  149. smooth_buf[scaler*2+i+(j+scaler*2)*sw] = 255 * ((buf[j*p+(i>>3)] >> (7 - (i & 7))) & 1);
  150. }
  151. }
  152. // do the SDF
  153. int sdfw = all_glyphs[packed_glyph_index].width;
  154. int sdfx = all_glyphs[packed_glyph_index].x;
  155. int sdfh = all_glyphs[packed_glyph_index].height;
  156. int sdfy = all_glyphs[packed_glyph_index].y;
  157. for (int j = 0; j < sdfh; ++j)
  158. {
  159. for (int i = 0; i < sdfw; ++i)
  160. {
  161. int pd_idx = (i+sdfx+(j+sdfy)*texture_size) * 4;
  162. pdata[pd_idx] = get_SDF_radial(smooth_buf, sw, sh, i*scaler + (scaler >>1), j*scaler + (scaler >>1), 2*scaler);
  163. pdata[pd_idx+1] = pdata[pd_idx];
  164. pdata[pd_idx+2] = pdata[pd_idx];
  165. pdata[pd_idx+3] = pdata[pd_idx];
  166. }
  167. }
  168. ++packed_glyph_index;
  169. delete [] smooth_buf;
  170. }
  171. //printf( "%i ", render_list[char_index] );
  172. }
  173. }
  174. }
  175. tin = clock() - tin;
  176. printf("Done. %.3f seconds.\n", ((float)tin) / CLOCKS_PER_SEC);
  177. printf("Exporting... ");
  178. tin = save_png_SDFont(font_file, texture_size, texture_size, pdata, all_glyphs);
  179. printf("Done. %.3f seconds.\n", ((float)tin) / CLOCKS_PER_SEC);
  180. // clean up my data
  181. all_glyphs.clear();
  182. pdata.clear();
  183. ft_err = FT_Done_Face( ft_face );
  184. }
  185. return true;
  186. }
  187. //-----------------------------------------------------------------------------
  188. int save_png_SDFont(const char* orig_filename, int img_width, int img_height, const std::vector<unsigned char> &img_data, const std::vector< sdf_glyph > &packed_glyphs)
  189. {
  190. // save my image
  191. string dest(orig_filename);
  192. unsigned offs = dest.find(".ttf");
  193. dest.replace(offs, 4, "");
  194. dest += ".tga";
  195. int tin = clock();
  196. FILE* file = fopen(dest.c_str(), "wb");
  197. if ( NULL != file )
  198. {
  199. uint8_t type = 2;
  200. uint8_t bpp = 32;
  201. uint8_t xorig = 0;
  202. uint8_t yorig = 0;
  203. putc(0, file);
  204. putc(0, file);
  205. putc(type, file);
  206. putc(0, file);
  207. putc(0, file);
  208. putc(0, file);
  209. putc(0, file);
  210. putc(0, file);
  211. putc(0, file);
  212. putc(xorig, file);
  213. putc(0, file);
  214. putc(yorig, file);
  215. putc(img_width&0xff, file);
  216. putc( (img_width>>8)&0xff, file);
  217. putc(img_height&0xff, file);
  218. putc( (img_height>>8)&0xff, file);
  219. putc(bpp, file);
  220. putc(32, file);
  221. uint32_t width = img_width * bpp / 8;
  222. uint8_t* data = (uint8_t*)img_data.data();
  223. for (uint32_t yy = 0; yy < img_height; ++yy)
  224. {
  225. fwrite(data, width, 1, file);
  226. data += width;
  227. }
  228. fclose(file);
  229. }
  230. tin = clock() - tin;
  231. // now save the acompanying info
  232. offs = dest.find(".tga");
  233. dest.replace(offs, 4, "");
  234. dest += ".font";
  235. FILE *fp = fopen(dest.c_str(), "w");
  236. // EDITED by CROWN DEVELOPERS -- json output
  237. if( fp )
  238. {
  239. fprintf(fp, "{\n");
  240. fprintf(fp, "\"material\": \"%s\",\n", font_name);
  241. fprintf(fp, "\"count\":%i,\n", packed_glyphs.size());
  242. fprintf(fp, "\"size\":%i,\n", img_width);
  243. fprintf(fp, "\"font_size\": %i, \n", font_size);
  244. fprintf(fp, "\"glyphs\" : [\n");
  245. for(unsigned int i = 0; i < packed_glyphs.size()-1; ++i)
  246. {
  247. fprintf( fp, "\t{\"id\":%i, \"x\":%i, \"y\":%i, \"width\":%i, \"height\":%i,",
  248. packed_glyphs[i].ID,
  249. packed_glyphs[i].x,
  250. img_height - packed_glyphs[i].y,
  251. packed_glyphs[i].width,
  252. packed_glyphs[i].height
  253. );
  254. fprintf( fp, "\"x_offset\":%.2f, \"y_offset\":%.2f, \"x_advance\":%.2f},\n",
  255. packed_glyphs[i].xoff,
  256. packed_glyphs[i].yoff,
  257. packed_glyphs[i].xadv
  258. );
  259. }
  260. fprintf( fp, "\t{\"id\":%i, \"x\":%i, \"y\":%i, \"width\":%i, \"height\":%i,",
  261. packed_glyphs[packed_glyphs.size()-1].ID,
  262. packed_glyphs[packed_glyphs.size()-1].x,
  263. img_height - packed_glyphs[packed_glyphs.size()-1].y,
  264. packed_glyphs[packed_glyphs.size()-1].width,
  265. packed_glyphs[packed_glyphs.size()-1].height
  266. );
  267. fprintf( fp, "\"x_offset\":%.2f, \"y_offset\":%.2f, \"x_advance\":%.2f}\n",
  268. packed_glyphs[packed_glyphs.size()-1].xoff,
  269. packed_glyphs[packed_glyphs.size()-1].yoff,
  270. packed_glyphs[packed_glyphs.size()-1].xadv
  271. );
  272. fprintf(fp, "\t]\n");
  273. fprintf(fp, "}");
  274. fclose( fp );
  275. }
  276. return tin;
  277. }
  278. //-----------------------------------------------------------------------------
  279. bool gen_pack_list(
  280. FT_Face &ft_face,
  281. int pixel_size,
  282. int pack_tex_size,
  283. const std::vector< int > &render_list,
  284. std::vector< sdf_glyph > &packed_glyphs )
  285. {
  286. int ft_err;
  287. packed_glyphs.clear();
  288. ft_err = FT_Set_Pixel_Sizes( ft_face, pixel_size*scaler, 0 );
  289. std::vector< int > rectangle_info;
  290. std::vector< std::vector<int> > packed_glyph_info;
  291. for( unsigned int char_index = 0; char_index < render_list.size(); ++char_index )
  292. {
  293. int glyph_index = FT_Get_Char_Index( ft_face, render_list[char_index] );
  294. if( glyph_index )
  295. {
  296. ft_err = FT_Load_Glyph( ft_face, glyph_index, 0 );
  297. if( !ft_err )
  298. {
  299. ft_err = FT_Render_Glyph( ft_face->glyph, FT_RENDER_MODE_MONO );
  300. if( !ft_err )
  301. {
  302. sdf_glyph add_me;
  303. // we have the glyph, already rendered, get the data about it
  304. int w = ft_face->glyph->bitmap.width;
  305. int h = ft_face->glyph->bitmap.rows;
  306. // oversize the holding buffer so I can smooth it!
  307. int sw = w + scaler * 4;
  308. int sh = h + scaler * 4;
  309. // do the SDF
  310. int sdfw = sw / scaler;
  311. int sdfh = sh / scaler;
  312. rectangle_info.push_back( sdfw );
  313. rectangle_info.push_back( sdfh );
  314. // add in the data I already know
  315. add_me.ID = render_list[char_index];
  316. add_me.width = sdfw;
  317. add_me.height = sdfh;
  318. // these need to be filled in later (after packing)
  319. add_me.x = -1;
  320. add_me.y = -1;
  321. // these need scaling...
  322. add_me.xoff = ft_face->glyph->bitmap_left;
  323. add_me.yoff = ft_face->glyph->bitmap_top;
  324. add_me.xadv = ft_face->glyph->advance.x / 64.0;
  325. // so scale them (the 1.5's have to do with the padding
  326. // border and the sampling locations for the SDF)
  327. add_me.xoff = add_me.xoff / scaler - 1.5;
  328. add_me.yoff = add_me.yoff / scaler + 1.5;
  329. add_me.xadv = add_me.xadv / scaler;
  330. // add it to my list
  331. packed_glyphs.push_back( add_me );
  332. }
  333. }
  334. }
  335. }
  336. const bool dont_allow_rotation = false;
  337. BinPacker bp;
  338. bp.Pack( rectangle_info, packed_glyph_info, pack_tex_size, dont_allow_rotation );
  339. // populate the actual coordinates
  340. if( packed_glyph_info.size() == 1 )
  341. {
  342. // it all fit into one!
  343. unsigned int lim = packed_glyph_info[0].size();
  344. for( unsigned int i = 0; i < lim; i += 4 )
  345. {
  346. // index, x, y, rotated
  347. unsigned int idx = packed_glyph_info[0][i+0];
  348. packed_glyphs[idx].x = packed_glyph_info[0][i+1];
  349. packed_glyphs[idx].y = packed_glyph_info[0][i+2];
  350. }
  351. return true;
  352. }
  353. return false;
  354. }
  355. //-----------------------------------------------------------------------------
  356. unsigned char get_SDF_radial(
  357. unsigned char *fontmap,
  358. int w, int h,
  359. int x, int y,
  360. int max_radius )
  361. {
  362. // hideous brute force method
  363. float d2 = max_radius*max_radius+1.0;
  364. unsigned char v = fontmap[x+y*w];
  365. for( int radius = 1; (radius <= max_radius) && (radius*radius < d2); ++radius )
  366. {
  367. int line, lo, hi;
  368. // north
  369. line = y - radius;
  370. if( (line >= 0) && (line < h) )
  371. {
  372. lo = x - radius;
  373. hi = x + radius;
  374. if( lo < 0 ) { lo = 0; }
  375. if( hi >= w ) { hi = w-1; }
  376. int idx = line * w + lo;
  377. for( int i = lo; i <= hi; ++i )
  378. {
  379. // check this pixel
  380. if( fontmap[idx] != v )
  381. {
  382. float nx = i - x;
  383. float ny = line - y;
  384. float nd2 = nx*nx+ny*ny;
  385. if( nd2 < d2 )
  386. {
  387. d2 = nd2;
  388. }
  389. }
  390. // move on
  391. ++idx;
  392. }
  393. }
  394. // south
  395. line = y + radius;
  396. if( (line >= 0) && (line < h) )
  397. {
  398. lo = x - radius;
  399. hi = x + radius;
  400. if( lo < 0 ) { lo = 0; }
  401. if( hi >= w ) { hi = w-1; }
  402. int idx = line * w + lo;
  403. for( int i = lo; i <= hi; ++i )
  404. {
  405. // check this pixel
  406. if( fontmap[idx] != v )
  407. {
  408. float nx = i - x;
  409. float ny = line - y;
  410. float nd2 = nx*nx+ny*ny;
  411. if( nd2 < d2 )
  412. {
  413. d2 = nd2;
  414. }
  415. }
  416. // move on
  417. ++idx;
  418. }
  419. }
  420. // west
  421. line = x - radius;
  422. if( (line >= 0) && (line < w) )
  423. {
  424. lo = y - radius + 1;
  425. hi = y + radius - 1;
  426. if( lo < 0 ) { lo = 0; }
  427. if( hi >= h ) { hi = h-1; }
  428. int idx = lo * w + line;
  429. for( int i = lo; i <= hi; ++i )
  430. {
  431. // check this pixel
  432. if( fontmap[idx] != v )
  433. {
  434. float nx = line - x;
  435. float ny = i - y;
  436. float nd2 = nx*nx+ny*ny;
  437. if( nd2 < d2 )
  438. {
  439. d2 = nd2;
  440. }
  441. }
  442. // move on
  443. idx += w;
  444. }
  445. }
  446. // east
  447. line = x + radius;
  448. if( (line >= 0) && (line < w) )
  449. {
  450. lo = y - radius + 1;
  451. hi = y + radius - 1;
  452. if( lo < 0 ) { lo = 0; }
  453. if( hi >= h ) { hi = h-1; }
  454. int idx = lo * w + line;
  455. for( int i = lo; i <= hi; ++i )
  456. {
  457. // check this pixel
  458. if( fontmap[idx] != v )
  459. {
  460. float nx = line - x;
  461. float ny = i - y;
  462. float nd2 = nx*nx+ny*ny;
  463. if( nd2 < d2 )
  464. {
  465. d2 = nd2;
  466. }
  467. }
  468. // move on
  469. idx += w;
  470. }
  471. }
  472. }
  473. d2 = sqrtf( d2 );
  474. if( v==0 )
  475. {
  476. d2 = -d2;
  477. }
  478. d2 *= 127.5 / max_radius;
  479. d2 += 127.5;
  480. if( d2 < 0.0 ) d2 = 0.0;
  481. if( d2 > 255.0 ) d2 = 255.0;
  482. return (unsigned char)(d2 + 0.5);
  483. }