main.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639
  1. #include <stdlib.h>
  2. #include <stdio.h>
  3. #include <string.h>
  4. #include <sys/sysinfo.h>
  5. #include "font.h"
  6. #include "ds.h"
  7. static uint32_t* utf8_to_utf32( char* u8, size_t* outLen) {
  8. size_t len = 0;
  9. size_t u8len = strlen(u8);
  10. uint32_t* u32 = malloc((u8len + 1) * sizeof(u32)); // just overallocate
  11. uint8_t* s = (uint8_t*)u8;
  12. #define to32s(x, s) (((uint32_t)(x)) << (s))
  13. int i = 0;
  14. while(*s) {
  15. if((s[0] & 0x80) == 0x00) { // single byte
  16. u32[i] = s[0];
  17. s++;
  18. }
  19. else if((s[0] & 0xe0) == 0xc0) { // two bytes
  20. if(s[1] == 0) goto MALFORMED;
  21. u32[i] = to32s(s[0] & 0x1f, 6) | to32s(s[1] & 0x3f, 0);
  22. s += 2;
  23. }
  24. else if((s[0] & 0xf0) == 0xe0) { // three bytes
  25. if(s[1] == 0 || s[2] == 0) goto MALFORMED;
  26. u32[i] = to32s(s[0] & 0x1f, 12) | to32s(s[1] & 0x3f, 6) | to32s(s[2] & 0x3f, 0);
  27. s += 3;
  28. }
  29. else if((s[0] & 0xf8) == 0xf0) { // four bytes
  30. if(s[1] == 0 || s[2] == 0 || s[3] == 0) goto MALFORMED;
  31. u32[i] = to32s(s[0] & 0x1f, 18) | to32s(s[1] & 0x3f, 12) | to32s(s[2] & 0x3f, 6) | to32s(s[3] & 0x3f, 0);
  32. s += 4;
  33. }
  34. i++;
  35. }
  36. u32[i] = 0;
  37. if(outLen) *outLen = i;
  38. return u32;
  39. MALFORMED:
  40. fprintf(stderr, "Malformed UTF-8 sequence.\n");
  41. exit(1); // we ain't havin none of that shit
  42. }
  43. static char* strappend(char* a, const char* const b);
  44. static char* strappendf(char* a, const char* const b);
  45. static char* strappendf(char* a, const char* const b) {
  46. char* o = strappend(a, b);
  47. free(a);
  48. return o;
  49. }
  50. static char* strappend(char* a, const char* const b) {
  51. if(a == NULL) return strdup(b);
  52. if(b == NULL) return a;
  53. size_t la = strlen(a);
  54. size_t lb = strlen(b);
  55. char* o = malloc(la + lb + 1);
  56. strcpy(o, a);
  57. strcpy(o + la, b);
  58. o[la + lb] = '\0';
  59. return o;
  60. }
  61. static uint32_t* u32strdup(const uint32_t* const s) {
  62. size_t l = u32strlen(s);
  63. uint32_t* o = malloc(l * sizeof(*s));
  64. memcpy(o, s, (l + 1) * sizeof(*s));
  65. return o;
  66. }
  67. static uint32_t* u32strappend(uint32_t* a, const uint32_t* const b) {
  68. if(a == NULL) return u32strdup(b);
  69. if(b == NULL) return a;
  70. size_t la = u32strlen(a);
  71. size_t lb = u32strlen(b);
  72. uint32_t* o = malloc(la + lb + 1);
  73. memcpy(o, a, la * sizeof(*a));
  74. memcpy(o + (la * sizeof(*a)), b, lb * sizeof(*b));
  75. o[(la + lb) * sizeof(*o)] = 0;
  76. return o;
  77. }
  78. static uint32_t* u32strappendf(uint32_t* a, const uint32_t* const b) {
  79. uint32_t* o = u32strappend(a, b);
  80. free(a);
  81. return o;
  82. }
  83. static void struniq(char* s) {
  84. if(s == NULL || s[0] == 0 || s[1] == 0) return;
  85. char* b = s;
  86. s++;
  87. while(*s) {
  88. if(*s != *b) {
  89. b++;
  90. *b = *s;
  91. }
  92. s++;
  93. }
  94. *(b + 1) = 0;
  95. }
  96. static void u32struniq(uint32_t* s) {
  97. if(s == NULL || s[0] == 0 || s[1] == 0) return;
  98. uint32_t* b = s;
  99. s++;
  100. while(*s) {
  101. if(*s != *b) {
  102. b++;
  103. *b = *s;
  104. }
  105. s++;
  106. }
  107. *(b + 1) = 0;
  108. }
  109. static int cs_comp(uint32_t* a, uint32_t* b) {
  110. return *a - *b;
  111. }
  112. // frees a
  113. static uint32_t* appendCharset(uint32_t* a, const uint32_t* const b) {
  114. uint32_t* o = u32strappendf(a, b);
  115. qsort(o, u32strlen(o), sizeof(uint32_t), (void*)cs_comp);
  116. u32struniq(o);
  117. return o;
  118. }
  119. static int int_comp(int* a, int* b) {
  120. return *a - *b;
  121. }
  122. char* helpText = "" \
  123. "Usage:\n" \
  124. "sdfgen [GLOBAL OPTION]... [-f FONT SIZES [OPTION]...]... [outfile]\n" \
  125. "\n" \
  126. "EXAMPLE:\n" \
  127. " sdfgen -b -m 8 -o 4 -t 256 -f \"Courier\" 8,12,16 -i -f \"Arial\" 32 myfonts_sdf\n" \
  128. " -b - generate bold for all fonts\n" \
  129. " -m 8 - SDF magnitude of 8 pixels\n" \
  130. " -o 4 - oversample by 4x\n" \
  131. " -t 256 - output textures have 256px dimensions\n" \
  132. " -f \"Courier\" - first font name \n" \
  133. " 8,12,16 - generate outputs of size 8, 12, and 16 px for this font \n" \
  134. " -i - also generate italic for this font\n" \
  135. " -f \"Arial\" - second font name \n" \
  136. " 32 - generate outputs of size 32px for this font \n" \
  137. " myfonts_sdf - output will be myfonts_sdf_[n].png and myfonts_sdf.json\n" \
  138. "\n" \
  139. "OPTIONS:\n" \
  140. " All Font Options can be included as global options for all fonts.\n" \
  141. " -f ... Add a font to render\n" \
  142. " -J Specify the output JSON file name\n" \
  143. " -l N Limit computation to N threads\n" \
  144. " -m SDF magnitude in pixels. Default 8.\n" \
  145. " -o Amount of pixels to oversample by. Default 16.\n" \
  146. " -P FORMAT Specify the png file name; %d will be expanded to the index.\n" \
  147. " -q Quiet. Suppress all output, even warnings and errors.\n" \
  148. " -t [N|fit] Size of output texture in pixels\n" \
  149. " 'fit' generates a single texture large enough to hold all glyphs\n" \
  150. " -v Verbose.\n" \
  151. "\n" \
  152. "FONT OPTIONS:\n" \
  153. " -- Stop parsing a font and return to global options. Not required.\n" \
  154. " -b Generate Bold\n" \
  155. " -bi Generate Bold Italic\n" \
  156. " -c CHARSET Specify the charset. Default is every character on a US keyboard.\n" \
  157. " -i Generate Italic\n" \
  158. " --omit-regular Do not generate the regular font face\n" \
  159. " -s N[,...] Generate these output font sizes.\n" \
  160. "\n" \
  161. "MISC OPTIONS:\n" \
  162. " --help Print this message and exit\n" \
  163. " --pretend Verify parameters but do not actually generate output.\n" \
  164. ;
  165. typedef struct FontOpts {
  166. char* name;
  167. char bold;
  168. char italic;
  169. char bold_italic;
  170. char omit_regular;
  171. uint32_t* charset;
  172. VEC(int) sizes;
  173. } FontOpts;
  174. static FontOpts* newFontOpts(char* name);
  175. static FontOpts* newFontOpts(char* name) {
  176. FontOpts* fo;
  177. fo = calloc(1, sizeof(*fo));
  178. fo->name = strdup(name);
  179. VEC_INIT(&fo->sizes);
  180. return fo;
  181. }
  182. int main(int argc, char* argv[]) {
  183. int an;
  184. #define default_outfile "sdf-output"
  185. #define default_png_outfile default_outfile "-%d";
  186. char* outfile = NULL;
  187. char* json_outfile = NULL;
  188. char* png_outfile = NULL;
  189. uint32_t* g_charset = utf8_to_utf32("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 `~!@#$%^&*()_+|-=\\{}[]:;<>?,./'\"", NULL);
  190. char g_bold = 0;
  191. char g_italic = 0;
  192. char g_bold_italic = 0;
  193. char g_omit_regular = 0;
  194. VEC(int) g_sizes;
  195. VEC(FontOpts*) fonts;
  196. int magnitude = 8;
  197. int oversample = 16;
  198. int maxCores = get_nprocs();
  199. int verbose = 0;
  200. char pretend = 0;
  201. int texSize = 0;
  202. int totalFacesToRender = 0;
  203. int totalGlyphsToRender = 0;
  204. VEC_INIT(&g_sizes);
  205. VEC_INIT(&fonts);
  206. for(an = 1; an < argc; an++) {
  207. char* arg = argv[an];
  208. if(0 == strcmp(arg, "--")) {
  209. printf("invalid usage of '--'\n");
  210. exit(1);
  211. };
  212. if(0 == strcmp(arg, "--help")) {
  213. puts(helpText);
  214. exit(0);
  215. }
  216. if(0 == strcmp(arg, "-b")) {
  217. g_bold = 1;
  218. continue;
  219. }
  220. if(0 == strcmp(arg, "-bi")) {
  221. g_bold_italic = 1;
  222. continue;
  223. }
  224. if(0 == strcmp(arg, "-c")) {
  225. printf("oh no!!\n");
  226. // TODO: detect and strip quotes
  227. an++;
  228. g_charset = utf8_to_utf32(argv[an], NULL);
  229. continue;
  230. }
  231. if(0 == strcmp(arg, "-i")) {
  232. g_italic = 1;
  233. continue;
  234. }
  235. if(0 == strcmp(arg, "-J")) {
  236. an++;
  237. json_outfile = strdup(argv[an]);
  238. continue;
  239. }
  240. if(0 == strcmp(arg, "-l")) {
  241. an++;
  242. int n = strtol(argv[an], NULL, 10);
  243. if(n > 0) maxCores = n;
  244. continue;
  245. }
  246. if(0 == strcmp(arg, "-m")) {
  247. an++;
  248. int n = strtol(argv[an], NULL, 10);
  249. if(n > 0) magnitude = n;
  250. continue;
  251. }
  252. // oversample
  253. if(0 == strcmp(arg, "-o")) {
  254. an++;
  255. int n = strtol(argv[an], NULL, 10);
  256. if(n > 0) oversample = n;
  257. continue;
  258. }
  259. // don't generate regular fonts
  260. if(0 == strcmp(arg, "--omit-regular")) {
  261. g_omit_regular = 1;
  262. continue;
  263. }
  264. if(0 == strcmp(arg, "-P")) {
  265. an++;
  266. png_outfile = strdup(argv[an]);
  267. continue;
  268. }
  269. if(0 == strcmp(arg, "--pretend")) {
  270. pretend = 1;
  271. continue;
  272. }
  273. if(0 == strcmp(arg, "-q")) {
  274. verbose = -1;
  275. continue;
  276. }
  277. // global sizes
  278. if(0 == strcmp(arg, "-s")) {
  279. an++;
  280. arg = argv[an];
  281. while(arg[0]) {
  282. int n = strtol(arg, &arg, 10);
  283. if(n <= 0) break;
  284. VEC_PUSH(&g_sizes, n);
  285. if(arg[0] != ',') break;
  286. arg++;
  287. }
  288. continue;
  289. }
  290. if(0 == strcmp(arg, "-t")) {
  291. an++;
  292. arg = argv[an];
  293. if(0 == strcmp(arg, "fit")) {
  294. texSize = 0;
  295. }
  296. else {
  297. int n = strtol(arg, NULL, 10);
  298. if(n > 0) texSize = n;
  299. }
  300. continue;
  301. }
  302. if(0 == strcmp(arg, "-v")) {
  303. verbose = 1;
  304. continue;
  305. }
  306. if(0 == strcmp(arg, "-vv")) {
  307. verbose = 2;
  308. continue;
  309. }
  310. // font specification
  311. if(0 == strcmp(arg, "-f")) {
  312. an++;
  313. FontOpts* fo = newFontOpts(argv[an]);
  314. VEC_PUSH(&fonts, fo);
  315. for(an++; an < argc; an++) {
  316. arg = argv[an];
  317. // return to normal options
  318. if(0 == strcmp(arg, "--")) break;
  319. // bold
  320. if(0 == strcmp(arg, "-b")) {
  321. fo->bold = 1;
  322. continue;
  323. }
  324. if(0 == strcmp(arg, "-bi")) {
  325. fo->bold_italic = 1;
  326. continue;
  327. }
  328. // charset
  329. if(0 == strcmp(arg, "-c")) {
  330. // TODO: detect and strip quotes
  331. printf("C!!\n");
  332. an++;
  333. fo->charset = utf8_to_utf32(argv[an], NULL);
  334. continue;
  335. }
  336. // italics
  337. if(0 == strcmp(arg, "-i")) {
  338. fo->italic = 1;
  339. continue;
  340. }
  341. // don't generate the regular font
  342. if(0 == strcmp(arg, "--omit-regular")) {
  343. fo->omit_regular = 1;
  344. continue;
  345. }
  346. // sizes
  347. if(0 == strcmp(arg, "-s")) {
  348. an++;
  349. arg = argv[an];
  350. while(arg[0]) {
  351. int n = strtol(arg, &arg, 10);
  352. if(n == 0) break;
  353. VEC_PUSH(&fo->sizes, n);
  354. if(arg[0] != ',') break;
  355. arg++;
  356. }
  357. continue;
  358. }
  359. // next font
  360. // TODO: check for -'s and error
  361. an--;
  362. break;
  363. }
  364. continue;
  365. }
  366. // output file
  367. // TODO: check for -'s and error
  368. outfile = strdup(arg);
  369. }
  370. // check and print the options
  371. if(!outfile) {
  372. json_outfile = default_outfile;
  373. png_outfile = default_png_outfile;
  374. }
  375. else {
  376. if(!json_outfile) {
  377. json_outfile = outfile;
  378. }
  379. if(!png_outfile) {
  380. png_outfile = outfile;
  381. }
  382. }
  383. // TODO: check for and append %d if needed
  384. // TODO: check for and append .png and .json
  385. json_outfile = strappend(json_outfile, ".json");
  386. png_outfile = strappend(png_outfile, ".png");
  387. // clean things up a bit
  388. VEC_SORT(&g_sizes, (void*)int_comp);
  389. // expand global options into FontOpt list
  390. VEC_EACH(&fonts, fi, fo) {
  391. fo->charset = appendCharset(fo->charset, g_charset);
  392. fo->bold |= g_bold;
  393. fo->italic |= g_italic;
  394. fo->bold_italic |= g_bold_italic;
  395. fo->omit_regular |= g_omit_regular;
  396. int n = 0;
  397. if(fo->bold) n++;
  398. if(fo->italic) n++;
  399. if(fo->bold_italic) n++;
  400. if(!fo->omit_regular) n++;
  401. // yeah... shutup. there aren't many sizes
  402. VEC_EACH(&g_sizes, gi, gs) {
  403. int found = 0;
  404. VEC_EACH(&fo->sizes, fsi, fss) {
  405. if(gs == fss) {
  406. found = 1;
  407. break;
  408. }
  409. }
  410. if(!found) VEC_PUSH(&fo->sizes, gs);
  411. }
  412. VEC_SORT(&fo->sizes, (void*)int_comp);
  413. totalFacesToRender += n * VEC_LEN(&fo->sizes);
  414. totalGlyphsToRender += n * u32strlen(fo->charset) * VEC_LEN(&fo->sizes);
  415. }
  416. // TODO: check that each face can be loaded and is unique
  417. // sanity check
  418. if(texSize > 0 && texSize < 16 ) {
  419. if(verbose >= 0) printf("WARNING: Texure Size is suspiciously low: %dpx\n", texSize);
  420. }
  421. // check for power of 2
  422. if(texSize > 0 && (texSize != (texSize & (1 - texSize)))) {
  423. if(verbose >= 0) printf("WARNING: Texure Size is not a power of two: %dpx\n GPUs like power of two textures.\n", texSize);
  424. }
  425. if(verbose >= 1) {
  426. printf("JSON output: %s\n", json_outfile);
  427. printf("PNG output: %s\n", png_outfile);
  428. printf("Oversample: %d\n", oversample);
  429. printf("Magnitude: %d\n", magnitude);
  430. if(g_omit_regular) puts("Omitting regular font face.");
  431. //printf("Charset: %s\n", g_charset); // TODO: escape
  432. printf("Verbosity: %d\n", verbose);
  433. printf("Max Threads: %d\n", maxCores);
  434. if(texSize == 0) {
  435. printf("Tex Size: fit\n");
  436. }
  437. else {
  438. printf("Tex Size: %dx%d\n", texSize, texSize);
  439. }
  440. puts("Fonts:");
  441. VEC_EACH(&fonts, fi, fo) {
  442. printf(" %s\n", fo->name);
  443. printf(" Faces: ");
  444. int n = 0;
  445. if(!fo->omit_regular) { n++; printf("regular "); }
  446. if(fo->bold) { n++; printf("bold "); }
  447. if(fo->italic) { n++; printf("italic "); }
  448. if(fo->bold_italic) { n++; printf("bold/italic "); }
  449. if(n == 0) printf("--none--");
  450. printf("\n");
  451. printf(" Sizes: ");
  452. VEC_EACH(&fo->sizes, i, size) {
  453. printf("%d", size);
  454. if(i < VEC_LEN(&fo->sizes) - 1) printf(",");
  455. }
  456. printf("\n");
  457. }
  458. printf("Total Font Faces to Render: %d\n", totalFacesToRender);
  459. printf("Total Glyphs to Render: %d\n", totalGlyphsToRender);
  460. }
  461. // start rendering
  462. if(pretend) {
  463. if(verbose >= 0) printf("Skipping generation (--pretend)\n");
  464. }
  465. else {
  466. if(totalGlyphsToRender == 0) {
  467. if(verbose >= 0) printf("Nothing to generate. Check your options (use -v).\n");
  468. exit(0);
  469. }
  470. FontManager* fm;
  471. fm = FontManager_alloc();
  472. fm->pngFileFormat = png_outfile;
  473. fm->oversample = oversample;
  474. fm->magnitude = magnitude;
  475. fm->maxThreads = maxCores;
  476. fm->verbose = verbose;
  477. fm->maxAtlasSize = texSize;
  478. VEC_EACH(&fonts, fi, fo) {
  479. VEC_EACH(&fo->sizes, si, size) {
  480. if(!fo->omit_regular) FontManager_addFont2(fm, fo->name, fo->charset, size, 0, 0);
  481. if(fo->bold) FontManager_addFont2(fm, fo->name, fo->charset, size, 1, 0);
  482. if(fo->italic) FontManager_addFont2(fm, fo->name, fo->charset, size, 0, 1);
  483. if(fo->bold_italic) FontManager_addFont2(fm, fo->name, fo->charset, size, 1, 1);
  484. }
  485. }
  486. FontManager_finalize(fm);
  487. if(VEC_LEN(&fm->gen) == 0) {
  488. if(verbose >= 0) printf("No glyphs generated. Error?\n");
  489. exit(0);
  490. }
  491. FontManager_createAtlas(fm);
  492. FontManager_saveJSON(fm, json_outfile);
  493. // FontManager_saveAtlas(fm, "fonts.atlas");
  494. }
  495. return 0;
  496. }