Font.cpp 44 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233
  1. /******************************************************************************/
  2. #include "stdafx.h"
  3. #define USE_FREE_TYPE LINUX
  4. #define SPECIAL_CHARS 3 // Space, FullWidthSpace, Tab
  5. #if USE_FREE_TYPE
  6. #include "../../../ThirdPartyLibs/begin.h"
  7. #include "../../../ThirdPartyLibs/FreeType/lib/include/ft2build.h"
  8. #include FT_FREETYPE_H
  9. #include FT_LCD_FILTER_H
  10. #include "../../../ThirdPartyLibs/end.h"
  11. #endif
  12. #define TEST_FONT 0
  13. #if TEST_FONT
  14. #pragma message("!! Warning: Use this only for debugging !!")
  15. #endif
  16. namespace EE{
  17. /******************************************************************************/
  18. #define CC4_FONT CC4('F','O','N','T')
  19. /******************************************************************************/
  20. DEFINE_CACHE_EX(Font, Fonts, FontPtr, "Font", 2); // use small number of elements per block because Font class size is big (above 128 KB)
  21. /******************************************************************************/
  22. // FONT MANAGE
  23. /******************************************************************************/
  24. void Font::zero()
  25. {
  26. _sub_pixel=false;
  27. _height=0;
  28. _padd.zero();
  29. Zero(_char_to_font);
  30. Zero(_wide_to_font);
  31. }
  32. Font::Font() {zero();}
  33. Font& Font::del()
  34. {
  35. _chrs .del();
  36. _images.del();
  37. zero(); return T;
  38. }
  39. /******************************************************************************/
  40. UInt Font::memUsage()C
  41. {
  42. UInt size=0; REPA(_images)size+=_images[i].memUsage();
  43. return size;
  44. }
  45. Bool Font:: hasChar (Char8 c)C {UInt index=_char_to_font[U8 (c)]; return InRange(index, _chrs) ? true : false;}
  46. Bool Font:: hasChar (Char c)C {UInt index=_wide_to_font[U16(c)]; return InRange(index, _chrs) ? true : false;}
  47. Int Font::charIndex(Char8 c)C {UInt index=_char_to_font[U8 (c)]; return InRange(index, _chrs) ? index : -1;}
  48. Int Font::charIndex(Char c)C {UInt index=_wide_to_font[U16(c)]; return InRange(index, _chrs) ? index : -1;}
  49. Int Font::charWidth(Char c)C {UInt index=_wide_to_font[U16(c)]; return InRange(index, _chrs) ? _chrs[index].width : 0;}
  50. Int Font::charWidth(Char8 c)C {UInt index=_char_to_font[U8 (c)]; return InRange(index, _chrs) ? _chrs[index].width : 0;}
  51. /******************************************************************************/
  52. Int Font::charWidth(Char8 c0, Char8 c1, SPACING_MODE spacing)C
  53. {
  54. switch(spacing)
  55. {
  56. case SPACING_FAST: return charWidth(c0);
  57. case SPACING_NICE:
  58. {
  59. UShort index=_char_to_font[Unsigned(c0)]; if(InRange(index, _chrs))
  60. {
  61. C Chr &chr =_chrs[index];
  62. Int width= chr.width; // get width of the first character
  63. index=_char_to_font[Unsigned(c1)]; if(InRange(index, _chrs)) // if the second character exists, then adjust spacing between them
  64. {
  65. C Byte *width_0= chr.width2[1], // we're taking the right side of the first character
  66. *width_1=_chrs[index].width2[0]; // we're taking the left side of the second character
  67. Int min =255*2; // 255 (means no distance) * 2 (because of adding l+r)
  68. REP(FONT_WIDTH_TEST)
  69. {
  70. Byte l=width_0[i],
  71. r=width_1[i];
  72. if(l!=255 && r!=255)MIN(min, l+r);
  73. }
  74. if(min<255*2)
  75. {
  76. if(CharFlagFast(c0, c1)&CHARF_FONT_SPACE)min/=2; // if any of the 2 characters needs adjusting
  77. width-=min;
  78. }
  79. }
  80. return width;
  81. }
  82. } // here on purpose no break, to fall down and return 0
  83. default: return 0;
  84. }
  85. }
  86. Int Font::charWidth(Char c0, Char c1, SPACING_MODE spacing)C
  87. {
  88. switch(spacing)
  89. {
  90. case SPACING_FAST: return charWidth(c0);
  91. case SPACING_NICE:
  92. {
  93. UShort index=_wide_to_font[Unsigned(c0)]; if(InRange(index, _chrs))
  94. {
  95. C Chr &chr =_chrs[index];
  96. Int width= chr.width; // get width of the first character
  97. index=_wide_to_font[Unsigned(c1)]; if(InRange(index, _chrs)) // if the second character exists, then adjust spacing between them
  98. {
  99. C Byte *width_0= chr.width2[1], // we're taking the right side of the first character
  100. *width_1=_chrs[index].width2[0]; // we're taking the left side of the second character
  101. Int min =255*2; // 255 (means no distance) * 2 (because of adding l+r)
  102. REP(FONT_WIDTH_TEST)
  103. {
  104. Byte l=width_0[i],
  105. r=width_1[i];
  106. if(l!=255 && r!=255)MIN(min, l+r);
  107. }
  108. if(min<255*2)
  109. {
  110. if(CharFlagFast(c0, c1)&CHARF_FONT_SPACE)min/=2; // if any of the 2 characters needs adjusting
  111. width-=min;
  112. }
  113. }
  114. return width;
  115. }
  116. } // here on purpose no break, to fall down and return 0
  117. default: return 0;
  118. }
  119. }
  120. /******************************************************************************/
  121. Int Font::textWidth(Int &base_chars, SPACING_MODE spacing, CChar8 *text, Int max_length)C
  122. {
  123. Int width=0, bcs=0;
  124. if(spacing!=SPACING_CONST)
  125. {
  126. if(max_length<0) // unlimited
  127. {
  128. for(;;)
  129. {
  130. Char8 c=*text; if(!c)break;
  131. skip:
  132. Char8 next=*++text;
  133. if(CharFlagFast(next)&CHARF_COMBINING)goto skip;
  134. width+=charWidth(c, next, spacing); bcs++;
  135. }
  136. }else
  137. if(max_length)
  138. {
  139. for(;;)
  140. {
  141. Char8 c=*text; if(!c)break;
  142. skip1:
  143. if(!--max_length){/*if(spacing!=SPACING_CONST)*/width+=charWidth(c); bcs++; break;} // for the last character we need to process only its width and ignore the spacing between the next one
  144. Char8 next=*++text;
  145. if(CharFlagFast(next)&CHARF_COMBINING)goto skip1;
  146. width+=charWidth(c, next, spacing); bcs++;
  147. }
  148. }
  149. }else // for SPACING_CONST we don't calculate 'width' but just 'bcs'
  150. {
  151. if(max_length<0) // unlimited
  152. {
  153. for(;;)
  154. {
  155. Char8 c=*text; if(!c)break;
  156. skip2:
  157. Char8 next=*++text;
  158. if(CharFlagFast(next)&CHARF_COMBINING)goto skip2;
  159. bcs++;
  160. }
  161. }else
  162. if(max_length)
  163. {
  164. for(;;)
  165. {
  166. Char8 c=*text; if(!c)break;
  167. skip3:
  168. if(!--max_length){bcs++; break;} // for the last character we need to process only its width and ignore the spacing between the next one
  169. Char8 next=*++text;
  170. if(CharFlagFast(next)&CHARF_COMBINING)goto skip3;
  171. bcs++;
  172. }
  173. }
  174. }
  175. base_chars=bcs; return width;
  176. }
  177. Int Font::textWidth(Int &base_chars, SPACING_MODE spacing, CChar *text, Int max_length)C
  178. {
  179. Int width=0, bcs=0;
  180. if(spacing!=SPACING_CONST)
  181. {
  182. if(max_length<0) // unlimited
  183. {
  184. for(;;)
  185. {
  186. Char c=*text; if(!c)break;
  187. skip:
  188. Char next=*++text;
  189. if(CharFlagFast(next)&CHARF_COMBINING)goto skip;
  190. width+=charWidth(c, next, spacing); bcs++;
  191. }
  192. }else
  193. if(max_length)
  194. {
  195. for(;;)
  196. {
  197. Char c=*text; if(!c)break;
  198. skip1:
  199. if(!--max_length){/*if(spacing!=SPACING_CONST)*/width+=charWidth(c); bcs++; break;} // for the last character we need to process only its width and ignore the spacing between the next one
  200. Char next=*++text;
  201. if(CharFlagFast(next)&CHARF_COMBINING)goto skip1;
  202. width+=charWidth(c, next, spacing); bcs++;
  203. }
  204. }
  205. }else // for SPACING_CONST we don't calculate 'width' but just 'bcs'
  206. {
  207. if(max_length<0) // unlimited
  208. {
  209. for(;;)
  210. {
  211. Char c=*text; if(!c)break;
  212. skip2:
  213. Char next=*++text;
  214. if(CharFlagFast(next)&CHARF_COMBINING)goto skip2;
  215. bcs++;
  216. }
  217. }else
  218. if(max_length)
  219. {
  220. for(;;)
  221. {
  222. Char c=*text; if(!c)break;
  223. skip3:
  224. if(!--max_length){bcs++; break;} // for the last character we need to process only its width and ignore the spacing between the next one
  225. Char next=*++text;
  226. if(CharFlagFast(next)&CHARF_COMBINING)goto skip3;
  227. bcs++;
  228. }
  229. }
  230. }
  231. base_chars=bcs; return width;
  232. }
  233. /******************************************************************************/
  234. // OPERATIONS
  235. /******************************************************************************/
  236. Font& Font::freeOpenGLESData()
  237. {
  238. REPAO(_images).freeOpenGLESData();
  239. return T;
  240. }
  241. void Font::setGLFont()
  242. {
  243. REPAO(_images).setGLFont();
  244. }
  245. /******************************************************************************/
  246. Bool Font::imageType(IMAGE_TYPE type)
  247. {
  248. if(_sub_pixel && type!=IMAGE_B8G8R8A8 && type!=IMAGE_R8G8B8A8 && type!=IMAGE_BC2 && type!=IMAGE_BC3 && type!=IMAGE_BC7 && type!=IMAGE_ETC2_A8)return false; // sub pixel supports only these formats
  249. Bool changed=false;
  250. REPA(_images)
  251. {
  252. Image &image=_images[i];
  253. Int w=image.w(),
  254. h=image.h();
  255. if(type==IMAGE_PVRTC1_2 || type==IMAGE_PVRTC1_4)w=h=CeilPow2(Max(w, h)); // PVRTC1 requires square power of 2 anyway, so resize image to max size to gain some quality
  256. Int mip_maps=((type==IMAGE_PVRTC1_2 || type==IMAGE_PVRTC1_4) ? 1 : image.mipMaps()); // PVRTC has too low quality to enable mip-maps
  257. if(image.type()!=type || image.w()!=w || image.h()!=h || image.mipMaps()!=mip_maps) // if change is needed
  258. if(image.copyTry(image, w, h, -1, type, -1, mip_maps))changed=true;
  259. }
  260. if(changed)setGLFont();
  261. return changed;
  262. }
  263. void Font::toSoft()
  264. {
  265. IMAGE_TYPE type=(_sub_pixel ? IMAGE_R8G8B8A8 : IMAGE_L8A8);
  266. REPA(_images)
  267. {
  268. Image &image=_images[i];
  269. image.copyTry(image, -1, -1, -1, type, IMAGE_SOFT);
  270. }
  271. }
  272. Font& Font::replace(Char src, Char dest, Bool permanent)
  273. {
  274. if(src && src!=dest)
  275. {
  276. Int src_i=0xFFFF, // use 0xFFFF because it's set in 'setRemap' and it means out of range
  277. dest_i=0xFFFF;
  278. REPA(_chrs)
  279. {
  280. Char c=_chrs[i].chr;
  281. if(c==src ) src_i=i;else
  282. if(c==dest)dest_i=i;
  283. }
  284. if(permanent)
  285. {
  286. if(!dest || dest_i==0xFFFF) // want to remove or dest not found
  287. {
  288. if(src_i!=0xFFFF){_chrs.remove(src_i, true); setRemap();} // source exists
  289. }else // have a valid target
  290. {
  291. if(src_i==0xFFFF)src_i=_chrs.addNum(1); // source not found
  292. _chrs[src_i] =_chrs[dest_i];
  293. _chrs[src_i].chr=src;
  294. setRemap();
  295. }
  296. }else
  297. {
  298. Char8 c=Char16To8Fast(src); if(Char8To16Fast(c)!=src)c=0; // take 8-bit char, set zero if there's no 1:1 mapping, we can assume that Str was already initialized
  299. if(c )_char_to_font[U8 (c )]=dest_i;
  300. if(src)_wide_to_font[U16(src)]=dest_i;
  301. }
  302. }
  303. return T;
  304. }
  305. /******************************************************************************/
  306. void Font::setRemap()
  307. {
  308. SetMem(_char_to_font, 0xFF, SIZE(_char_to_font));
  309. SetMem(_wide_to_font, 0xFF, SIZE(_wide_to_font));
  310. Int invalid[5]={-1, -1, -1, -1, -1}, space=-1;
  311. REPA(_chrs)if(Char w=_chrs[i].chr)
  312. {
  313. Char8 c=Char16To8Fast(w); if(Char8To16Fast(c)!=w)c=0; // take 8-bit char, set zero if there's no 1:1 mapping, we can assume that Str was already initialized
  314. if(c)_char_to_font[U8 (c)]=i;
  315. if(w)_wide_to_font[U16(w)]=i;
  316. switch(w)
  317. {
  318. case L'�': invalid[0]=i; break;
  319. case '?': invalid[1]=i; break;
  320. case '*': invalid[2]=i; break;
  321. case '#': invalid[3]=i; break;
  322. case '.': invalid[4]=i; break;
  323. case ' ': space =i; break;
  324. }
  325. }
  326. // set all characters which were not found, to be displayed as based on 'invalid'
  327. FREPA(invalid)
  328. {
  329. Int inv=invalid[i]; if(inv>=0)
  330. {
  331. REPA(_char_to_font)if(_char_to_font[i]==0xFFFF && i)_char_to_font[i]=inv;
  332. REPA(_wide_to_font)if(_wide_to_font[i]==0xFFFF && i)_wide_to_font[i]=inv;
  333. break; // stop on first found
  334. }
  335. }
  336. if(space>=0)_wide_to_font[U16(Nbsp)]=space; // draw NBSP as space
  337. }
  338. /******************************************************************************/
  339. // FONT IO
  340. /******************************************************************************/
  341. #pragma pack(push, 4)
  342. struct FontChr3
  343. {
  344. Char chr;
  345. Byte image, offset, width, height, width2[FONT_WIDTH_TEST][2];
  346. Rect tex;
  347. };
  348. struct FontChr0
  349. {
  350. Char chr;
  351. Byte image, width, width2[FONT_WIDTH_TEST][2];
  352. Rect tex;
  353. };
  354. #pragma pack(pop)
  355. static void FontLoadChr3(Font &font, File &f)
  356. {
  357. Mems<FontChr3> chrs; chrs.loadRaw(f);
  358. font._chrs.setNum(chrs.elms()); REPA(font._chrs)
  359. {
  360. Font::Chr &dest=font._chrs[i];
  361. FontChr3 &src = chrs[i];
  362. dest.chr =src.chr;
  363. dest.image =src.image;
  364. dest.offset=src.offset;
  365. dest.width =src.width ; dest. width_padd=Min(dest. width+font.paddingL()+font.paddingR(), 255);
  366. dest.height=src.height; dest.height_padd=Min(dest.height+font.paddingT()+font.paddingB(), 255);
  367. dest.tex =src.tex;
  368. REPD(s, 2)
  369. REPD(i, FONT_WIDTH_TEST)dest.width2[s][i]=src.width2[i][s];
  370. }
  371. }
  372. static void FontLoadChr0(Font &font, File &f)
  373. {
  374. Mems<FontChr0> chrs; chrs._loadRaw(f);
  375. font._chrs.setNum(chrs.elms()); REPA(font._chrs)
  376. {
  377. Font::Chr &dest=font._chrs[i];
  378. FontChr0 &src = chrs[i];
  379. dest.chr =src.chr;
  380. dest.image =src.image;
  381. dest.tex =src.tex;
  382. dest.width =src.width; dest. width_padd=Min(dest. width+font.paddingL()+font.paddingR(), 255);
  383. dest.height=font.height(); dest.height_padd=Min(dest.height+font.paddingT()+font.paddingB(), 255);
  384. dest.offset=0;
  385. REPD(s, 2)
  386. REPD(i, FONT_WIDTH_TEST)dest.width2[s][i]=src.width2[i][s];
  387. }
  388. }
  389. static void SwapGreenAlpha(Font &font)
  390. {
  391. if(!font._sub_pixel)REPA(font._images)
  392. {
  393. Image &image=font._images[i]; if(image.copyTry(image, -1, -1, -1, IMAGE_L8A8, -1, -1, FILTER_NONE)) // disable filtering here as it will be done below
  394. {
  395. if(image.lock())
  396. {
  397. REPD(y, image.h())
  398. REPD(x, image.w())
  399. {
  400. Color c=image.color(x, y); Swap(c.g, c.a); c.r=c.b=c.g;
  401. image.color(x, y, c);
  402. }
  403. image.unlock();
  404. }
  405. image.updateMipMaps(FILTER_LINEAR); // for speed use linear filter
  406. }
  407. }
  408. }
  409. Bool Font::save(File &f)C
  410. {
  411. f.putMulti(UInt(CC4_FONT), Byte(5), _sub_pixel, _height, _padd); // version
  412. f.cmpUIntV(_images.elms()); FREPA(_images)if(!_images[i].saveData(f))return false;
  413. _chrs.saveRaw(f);
  414. return f.ok();
  415. }
  416. Bool Font::load(File &f)
  417. {
  418. del();
  419. if(f.getUInt()==CC4_FONT)switch(f.decUIntV()) // version
  420. {
  421. // always use IMAGE_DEFAULT for "loadData type_on_fail" because IMAGE_L8A8 is not supported on DX10+ and GL3+ and sub pixel requires all channels
  422. case 5:
  423. {
  424. f.getMulti(_sub_pixel, _height, _padd);
  425. _images.setNum(f.decUIntV()); FREPA(_images)if(!_images[i].loadData(f))goto error;
  426. _chrs.loadRaw(f);
  427. if(f.ok())
  428. {
  429. setRemap();
  430. setGLFont();
  431. return true;
  432. }
  433. }break;
  434. case 4:
  435. {
  436. f>>_sub_pixel>>_height>>_padd;
  437. _images.setNum(f.decUIntV());
  438. FontLoadChr3(T, f);
  439. FREPA(_images){f.skip(4); if(!_images[i].loadData(f))goto error;} // skip 'GFX' CC4 and use 'loadData'
  440. if(f.ok())
  441. {
  442. setRemap();
  443. setGLFont();
  444. return true;
  445. }
  446. }break;
  447. case 3:
  448. {
  449. f>>_sub_pixel>>_height>>_padd.x>>_padd.z>>_padd.y>>_padd.w;
  450. _images.setNum(f.decUIntV());
  451. FontLoadChr3(T, f);
  452. FREPA(_images){f.skip(4); if(!_images[i].loadData(f))goto error;} // skip 'GFX' CC4 and use 'loadData'
  453. if(f.ok())
  454. {
  455. SwapGreenAlpha(T);
  456. setRemap();
  457. setGLFont();
  458. return true;
  459. }
  460. }break;
  461. case 2:
  462. {
  463. f>>_sub_pixel>>_height>>_padd.x>>_padd.z>>_padd.y>>_padd.w;
  464. _images.setNum(f.getInt());
  465. FontLoadChr0(T, f);
  466. FREPA(_images){f.skip(4); if(!_images[i].loadData(f))goto error;} // skip 'GFX' CC4 and use 'loadData'
  467. if(f.ok())
  468. {
  469. SwapGreenAlpha(T);
  470. setRemap();
  471. setGLFont();
  472. return true;
  473. }
  474. }break;
  475. case 1:
  476. {
  477. f>>_height>>_padd.x>>_padd.z>>_padd.y>>_padd.w;
  478. _images.setNum(f.getInt());
  479. FontLoadChr0(T, f);
  480. FREPA(_images){f.skip(4); if(!_images[i].loadData(f))goto error;} // skip 'GFX' CC4 and use 'loadData'
  481. if(f.ok())
  482. {
  483. SwapGreenAlpha(T);
  484. setRemap();
  485. setGLFont();
  486. return true;
  487. }
  488. }break;
  489. case 0:
  490. {
  491. f>>_height>>_padd.x>>_padd.z; _padd.y=_padd.x; _padd.w=_padd.z;
  492. _images.setNum(f.getInt());
  493. FontLoadChr0(T, f);
  494. FREPA(_images){f.skip(4); if(!_images[i].loadData(f))goto error;} // skip 'GFX' CC4 and use 'loadData'
  495. if(f.ok())
  496. {
  497. SwapGreenAlpha(T);
  498. setRemap();
  499. setGLFont();
  500. return true;
  501. }
  502. }break;
  503. }
  504. error:
  505. del(); return false;
  506. }
  507. Bool Font::save(C Str &name)C
  508. {
  509. File f; if(f.writeTry(name)){if(save(f) && f.flush())return true; f.del(); FDelFile(name);}
  510. return false;
  511. }
  512. Bool Font::load(C Str &name)
  513. {
  514. File f; if(f.readTry(name))return load(f);
  515. del(); return false;
  516. }
  517. void Font::operator=(C UID &id ) {T=_EncodeFileName(id);}
  518. void Font::operator=(C Str &name)
  519. {
  520. if(!load(name))Exit(MLT(S+"Can't load font \"" +name+"\"",
  521. PL,S+u"Nie można wczytać czcionki \""+name+"\""));
  522. }
  523. /******************************************************************************/
  524. // FONT CREATE
  525. /******************************************************************************/
  526. struct SystemFont
  527. {
  528. Int base_line;
  529. #if USE_FREE_TYPE
  530. #if WINDOWS_OLD
  531. Mems<Byte> font_data;
  532. #endif
  533. Font::MODE mode;
  534. Int size;
  535. Str system_font;
  536. #elif WINDOWS_OLD
  537. HFONT font;
  538. #elif MAC
  539. NSFont *font;
  540. NSMutableParagraphStyle *style;
  541. NSMutableDictionary *attributes;
  542. #endif
  543. ~SystemFont() {del();}
  544. SystemFont()
  545. {
  546. base_line=-1; // unknown
  547. #if USE_FREE_TYPE
  548. mode=Font::DEFAULT;
  549. size=0;
  550. #elif WINDOWS_OLD
  551. font=null;
  552. #elif MAC
  553. attributes=null;
  554. style =null;
  555. font =null;
  556. #endif
  557. }
  558. void del()
  559. {
  560. #if USE_FREE_TYPE
  561. #if WINDOWS_OLD
  562. font_data.del();
  563. #endif
  564. #elif WINDOWS_OLD
  565. if(font){DeleteObject(font); font=null;}
  566. #elif MAC
  567. [attributes release]; attributes=null;
  568. [style release]; style =null;
  569. /*[font release];*/ font =null; // font is not manually allocated
  570. #endif
  571. }
  572. Bool createFont(Str system_font, Int size, Font::MODE mode, Flt weight, CHARSET_TYPE charset)
  573. {
  574. del();
  575. #if USE_FREE_TYPE
  576. #if WINDOWS_OLD
  577. if(HFONT font=CreateFont(size, 0, 0, 0, RoundU(Sat(weight)*1000), 0, 0, 0, charset, 0, 0, (mode==Font::DEFAULT) ? ANTIALIASED_QUALITY : CLEARTYPE_QUALITY, 0, system_font))
  578. {
  579. if(HDC hdc=CreateCompatibleDC(null))
  580. {
  581. SelectObject(hdc, font);
  582. Int size=GetFontData(hdc, 0, 0, null, 0);
  583. if( size>0)
  584. {
  585. font_data.setNum(size);
  586. if(GetFontData(hdc, 0, 0, font_data.data(), font_data.elms())!=font_data.elms())font_data.del();
  587. }
  588. DeleteDC(hdc);
  589. }
  590. DeleteObject(font);
  591. }
  592. #elif LINUX
  593. if(GetBase(system_font)==system_font) // not path
  594. {
  595. ConsoleProcess cp; if(cp.create("fc-match", S+"-v \""+system_font+'"')) // use FontConfig fc-match tool to get the font file name, "-v" needed to get full "file" path
  596. {
  597. cp.wait(1000);
  598. Str path=StrInside(cp.get(), "file: \"", "\"");
  599. if(path.is())system_font=path;
  600. }
  601. }
  602. #endif
  603. T.system_font=system_font;
  604. T.mode =mode;
  605. T.size =size;
  606. return true;
  607. #elif WINDOWS_OLD
  608. if(font=CreateFont(size, 0, 0, 0, RoundU(Sat(weight)*1000), 0, 0, 0, charset, 0, 0, (mode==Font::DEFAULT) ? ANTIALIASED_QUALITY : CLEARTYPE_QUALITY, 0, system_font))return true;
  609. #elif MAC
  610. Flt font_size=size*(64.0f/72); // to match Windows size
  611. if(NSStringAuto family=system_font)
  612. {
  613. font=[[NSFontManager sharedFontManager] fontWithFamily:family traits:0 weight:RoundU(Sat(weight)*15) size:font_size];
  614. if(!font)font=[ NSFont fontWithName :family size:font_size]; // does not accept weight
  615. } if(!font)font=[[NSFontManager sharedFontManager] fontWithFamily:@"Arial" traits:0 weight:RoundU(Sat(weight)*15) size:font_size];
  616. if(font)
  617. if(style=[[NSMutableParagraphStyle alloc] init])
  618. if(attributes=[[NSMutableDictionary alloc] init])
  619. {
  620. base_line=Round(font.ascender*size/(font.ascender-font.descender)); // 'descender' is negative
  621. [style setAlignment:NSCenterTextAlignment];
  622. [attributes setObject:font forKey:NSFontAttributeName];
  623. [attributes setObject:style forKey:NSParagraphStyleAttributeName];
  624. [attributes setObject:[NSColor whiteColor] forKey:NSForegroundColorAttributeName];
  625. return true;
  626. }
  627. #endif
  628. return false;
  629. }
  630. };
  631. /******************************************************************************/
  632. struct SystemFontDrawContext
  633. {
  634. SystemFont *font;
  635. Image image;
  636. #if USE_FREE_TYPE
  637. FT_Library library; // 'library' is not multi-thread safe so keep it in separate contexts
  638. FT_Face face;
  639. #elif WINDOWS_OLD
  640. HDC main, dc;
  641. HBITMAP bitmap;
  642. VecB4 *data;
  643. #elif MAC
  644. Image bitmap_image;
  645. NSBitmapImageRep *bitmap;
  646. NSGraphicsContext *context;
  647. #endif
  648. ~SystemFontDrawContext()
  649. {
  650. #if USE_FREE_TYPE
  651. if(face ){FT_Done_Face (face ); face =null;}
  652. if(library){FT_Done_FreeType(library); library=null;}
  653. #elif WINDOWS_OLD
  654. if(bitmap){DeleteObject(bitmap ); bitmap=null;}
  655. if(dc ){DeleteDC (dc ); dc =null;}
  656. if(main ){ReleaseDC (null, main); main =null;}
  657. #elif MAC
  658. if(context)
  659. {
  660. SyncLocker locker(D._lock);
  661. [NSGraphicsContext restoreGraphicsState];
  662. //[context release]; this causes crashes on 10.9
  663. context=null;
  664. }
  665. [bitmap release]; bitmap=null;
  666. #endif
  667. }
  668. SystemFontDrawContext()
  669. {
  670. font=null;
  671. #if USE_FREE_TYPE
  672. library=null;
  673. face =null;
  674. #elif WINDOWS_OLD
  675. main = GetDC(null);
  676. dc =CreateCompatibleDC(main);
  677. bitmap=null;
  678. data =null;
  679. #elif MAC
  680. context=null;
  681. bitmap =null;
  682. #endif
  683. }
  684. void createIfEmpty(Int w, Int h, SystemFont &font)
  685. {
  686. T.font=&font;
  687. #if USE_FREE_TYPE
  688. if(!image.is())
  689. {
  690. image.createSoft(w, h, 1, IMAGE_R8G8B8A8);
  691. if(!FT_Init_FreeType(&library))
  692. {
  693. FT_Library_SetLcdFilter(library, FT_LCD_FILTER_DEFAULT);
  694. #if WINDOWS_OLD
  695. if(!FT_New_Memory_Face(library, font.font_data.data(), font.font_data.elms(), 0, &face))
  696. #else
  697. if(!FT_New_Face(library, UnixPathUTF8(font.system_font), 0, &face))
  698. #endif
  699. #if 0 // in this version scale must be applied to match Windows Size, but prefer other version to have more precision
  700. if(!FT_Set_Pixel_Sizes(face, 0, font.size*64/72))
  701. #else // this gives same result as Windows
  702. if(!FT_Set_Char_Size(face, 0, font.size*64, 0, 64))
  703. #endif
  704. {
  705. // ok
  706. AtomicSet(font.base_line, DivRound(face->ascender*font.size, face->height));
  707. }
  708. }
  709. }
  710. #elif WINDOWS_OLD
  711. if(!bitmap)
  712. {
  713. image.createSoft(w, h, 1, IMAGE_B8G8R8A8);
  714. BITMAPV5HEADER bi; Zero(bi);
  715. bi.bV5Size =SIZE(bi);
  716. bi.bV5Width =w;
  717. bi.bV5Height =h;
  718. bi.bV5Planes =1;
  719. bi.bV5BitCount=32;
  720. bi.bV5Compression=BI_BITFIELDS;
  721. bi.bV5RedMask =0x00FF0000;
  722. bi.bV5GreenMask=0x0000FF00;
  723. bi.bV5BlueMask =0x000000FF;
  724. bi.bV5AlphaMask=0xFF000000;
  725. bitmap=CreateDIBSection(null, (BITMAPINFO*)&bi, DIB_RGB_COLORS, (Ptr*)&data, null, 0);
  726. SelectObject(dc, bitmap);
  727. SelectObject(dc, font.font);
  728. TEXTMETRIC metric; GetTextMetrics(dc, &metric);
  729. AtomicSet(font.base_line, metric.tmAscent);
  730. }
  731. #elif MAC
  732. if(!context)
  733. {
  734. if(!image .is()) image.createSoft(w, h, 1, IMAGE_R8G8B8A8);
  735. if(!bitmap_image.is())bitmap_image.createSoft(w, h, 1, IMAGE_R8G8B8A8);
  736. if(!bitmap)
  737. {
  738. unsigned char *image_data=bitmap_image.data();
  739. bitmap=[[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&image_data
  740. pixelsWide:bitmap_image.w()
  741. pixelsHigh:bitmap_image.h()
  742. bitsPerSample:8
  743. samplesPerPixel:4
  744. hasAlpha:YES
  745. isPlanar:NO
  746. colorSpaceName:NSCalibratedRGBColorSpace
  747. bytesPerRow:bitmap_image.pitch()
  748. bitsPerPixel:ImageTI[bitmap_image.hwType()].bit_pp];
  749. }
  750. if(!context)
  751. {
  752. SyncLocker locker(D._lock);
  753. if(context=[NSGraphicsContext graphicsContextWithBitmapImageRep:bitmap])
  754. {
  755. [NSGraphicsContext saveGraphicsState];
  756. [NSGraphicsContext setCurrentContext:context];
  757. }
  758. }
  759. }
  760. #endif
  761. }
  762. void draw(Char chr, Int border)
  763. {
  764. #if USE_FREE_TYPE
  765. image.clear();
  766. if(FT_UInt glyph_index=FT_Get_Char_Index(face, chr))
  767. {
  768. if(!FT_Load_Glyph (face, glyph_index, FT_LOAD_DEFAULT))
  769. if(!FT_Render_Glyph(face->glyph, (font->mode==Font::SUB_PIXEL) ? FT_RENDER_MODE_LCD : FT_RENDER_MODE_NORMAL))
  770. {
  771. FT_Bitmap &bitmap=face->glyph->bitmap;
  772. Int left =face->glyph->bitmap_left,
  773. top =face->glyph->bitmap_top ,
  774. offset=border+font->base_line-top;
  775. Int width=((bitmap.pixel_mode==FT_PIXEL_MODE_LCD) ? bitmap.width/3 : bitmap.width);
  776. REPD(y, bitmap.rows )
  777. REPD(x, width)
  778. {
  779. Color col;
  780. Byte *row=bitmap.buffer+y*bitmap.pitch;
  781. switch(bitmap.pixel_mode)
  782. {
  783. case FT_PIXEL_MODE_GRAY:
  784. {
  785. Byte *data=row+x;
  786. col.set(*data, *data, *data, 0xFF);
  787. }break;
  788. case FT_PIXEL_MODE_LCD:
  789. {
  790. Byte *data=row+x*3;
  791. col.set(data[0], data[1], data[2], 0xFF);
  792. }break;
  793. default: col=(((x^y)&1) ? WHITE : BLACK); break;
  794. }
  795. image.color(border+x, offset+y, col);
  796. }
  797. }
  798. }
  799. #elif WINDOWS_OLD
  800. // draw the character
  801. ZeroN (data, image.w()*image.h()); // RECT rect; rect.left=0; rect.top=0; rect.right=size_x; rect.bottom=size_y; FillRect(dc, &rect, HBRUSH(GetStockObject(BLACK_BRUSH)));
  802. SetTextColor(dc, 0xFFFFFF); // RGB(255, 255, 255) which was manually undefined in the headers
  803. SetBkMode (dc, 1); // 1 is macro for 'TRANSPARENT' which was manually undefined in the headers
  804. SetTextAlign(dc, TA_CENTER|TA_TOP|TA_NOUPDATECP);
  805. if(chr==u'ำ') // this character needs to be split in 2, because {Nbsp, u'ำ'} still draw a circle
  806. {
  807. wchar_t wc[]={Nbsp, u'ํ', u'า'};
  808. TextOut(dc, image.w()/2, border, wc, Elms(wc));
  809. }else
  810. if(CharFlag(chr)&CHARF_COMBINING) // Thai combining characters will draw with a circle ัิีึืฺุู็่้๊๋์ํ๎ but if we prepend them with NBSP they will draw without it
  811. {
  812. wchar_t wc[]={Nbsp, chr};
  813. TextOut(dc, image.w()/2, border, wc, Elms(wc));
  814. }else TextOut(dc, image.w()/2, border, WChar(&chr), 1);
  815. // copy BITMAP to Image
  816. REPD(y, image.h())CopyFast(image.data()+image.pitch()*y, (Byte*)data+image.pitch()*(image.h()-1-y), image.pitch()); // need to flip vertically
  817. #elif MAC
  818. bitmap_image.clear();
  819. if(NSStringAuto str=chr)
  820. {
  821. NSPoint p; p.x=border; p.y=border; [str() drawAtPoint:p withAttributes:font->attributes];
  822. [context flushGraphics];
  823. }
  824. // copy Bitmap to Image
  825. REPD(y, image.h())CopyFast(image.data()+image.pitch()*y, bitmap_image.data()+bitmap_image.pitch()*y, image.pitch());
  826. #endif
  827. }
  828. };
  829. /******************************************************************************/
  830. static const Byte WinGDIGamma[]={0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 12, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 17, 18, 18, 19, 19, 20, 20, 21, 22, 22, 23, 23, 24, 24, 25, 25, 26, 27, 27, 28, 29, 29, 30, 30, 31, 32, 32, 33, 34, 35, 35, 36, 37, 37, 38, 39, 40, 41, 41, 42, 43, 44, 45, 45, 46, 47, 48, 49, 50, 51, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 76, 77, 78, 79, 80, 81, 82, 84, 85, 86, 87, 88, 90, 91, 92, 93, 95, 96, 97, 99, 100, 101, 103, 104, 105, 107, 108, 109, 111, 112, 114, 115, 116, 118, 119, 121, 122, 124, 125, 127, 128, 130, 131, 133, 134, 136, 138, 139, 141, 142, 144, 146, 147, 149, 151, 152, 154, 156, 157, 159, 161, 163, 164, 166, 168, 170, 171, 173, 175, 177, 179, 181, 183, 184, 186, 188, 190, 192, 194, 196, 198, 200, 202, 204, 206, 208, 210, 212, 214, 216, 218, 220, 222, 224, 226, 229, 231, 233, 235, 237, 239, 242, 244, 246, 248, 250, 253, 255}; ASSERT(SIZE(WinGDIGamma)==256);
  831. #if 0
  832. #pragma message("!! Warning: Use this only for debugging !!")
  833. static struct WinGDIGammaCreate
  834. {
  835. WinGDIGammaCreate()
  836. {
  837. Str s; FREP(256){if(i)s+=", "; s+=RoundPos(SRGBToLinear(i/255.0f)*255);} ClipSet(s); Exit(s);
  838. }
  839. }WinGDIGammaCreator;
  840. #endif
  841. struct FontChar
  842. {
  843. Char chr;
  844. VecI2 ofs, size; // these are unaffected by shadow padding
  845. Byte width2[2][FONT_WIDTH_TEST];
  846. Image image;
  847. void setSpace(Int size)
  848. {
  849. T.chr=' ';
  850. T.ofs=0;
  851. T.size.set(DivRound(size, 4), 0);
  852. Zero(width2);
  853. image.del();
  854. }
  855. void setFullSpace(Int size)
  856. {
  857. T.chr=FullWidthSpace;
  858. T.ofs=0;
  859. T.size.set(DivRound(size, 2), 0);
  860. Zero(width2);
  861. image.del();
  862. }
  863. void setTab(Int size)
  864. {
  865. T.chr='\t';
  866. T.ofs=0;
  867. T.size.set(DivRound(size, 4)*3, 0); // make tab 3x wider than space
  868. Zero(width2);
  869. image.del();
  870. }
  871. static Int Compare(C FontChar &a, C FontChar &b) {return ::Compare(a.image.h(), b.image.h());}
  872. FontChar() {chr='\0'; ofs=size=0; Zero(width2);} // Zero (for example required for spaces)
  873. };
  874. struct FontCreate : Font::Params
  875. {
  876. Int scaled_size, draw_border, char_border, padd_ul, padd_dr, shadow_blur, shadow_offset;
  877. VecI2 draw_size; // size of the buffer used for drawing
  878. Memc<FontChar> chars; // chars to create
  879. Int processed; Int leftToProcess()C {return Max(0, chars.elms()-processed-SPECIAL_CHARS);} // skip special chars
  880. IMAGE_TYPE imageTypeTemp()C {return (mode==Font::SUB_PIXEL) ? IMAGE_R8G8B8A8 : IMAGE_L8A8;}
  881. SystemFont font;
  882. MemtN<SystemFontDrawContext, 16> dcs;
  883. FontCreate(C Params &src) : Params(src)
  884. {
  885. char_border=4;
  886. // adjust options
  887. MAX(size, 0);
  888. scaled_size=RoundU(size*scale);
  889. if(mode==Font::SUB_PIXEL)
  890. {
  891. image_type=IMAGE_R8G8B8A8;
  892. mip_maps=1;
  893. shadow_opacity=0; // disable shadows for sub pixels
  894. }
  895. Clamp(max_image_size, 0, 65536);
  896. if(image_type==IMAGE_PVRTC1_2 || image_type==IMAGE_PVRTC1_4)mip_maps=1; // PVRTC has too low quality to enable mip-maps
  897. // drawing
  898. T.draw_border=1;
  899. T.draw_size.set(scaled_size*5/4+draw_border*2, scaled_size+draw_border*2);
  900. // shadows
  901. if(shadow_opacity<=0)
  902. {
  903. padd_ul=padd_dr=shadow_blur=shadow_offset=0;
  904. }else
  905. {
  906. Int border= Max(0, RoundPos(size*src.shadow_blur)),
  907. offset=(shadow_diagonal ? Max(1, RoundPos(size*0.045f )) : 0);
  908. T.padd_ul=Max(0, border-offset);
  909. T.padd_dr=Max(0, border+offset);
  910. T.shadow_blur =border;
  911. T.shadow_offset=offset;
  912. }
  913. // list of characters (remove those that repeat)
  914. Str chars; chars.reserve(src.characters.length());
  915. FREPA(src.characters)
  916. {
  917. Char c=src.characters[i];
  918. ASSERT(SPECIAL_CHARS==3); // skip SPECIAL_CHARS, they will be manually inserted later
  919. if(c!=' '
  920. && c!=FullWidthSpace
  921. && c!='\t'
  922. && c!=Nbsp // no need to add nbsp because it's always drawn as space
  923. && !Contains(chars, c) // don't add same characters multiple times
  924. )chars+=c;
  925. }
  926. T.chars.reserve(chars.length()+SPECIAL_CHARS);
  927. T.chars.setNum (chars.length()); REPAO(T.chars).chr=chars[i];
  928. processed=0;
  929. }
  930. Bool createFont()
  931. {
  932. return font.createFont(system_font, scaled_size, mode, weight, charset);
  933. }
  934. Bool drawCharacters()
  935. {
  936. dcs.setNum(Cpu.threads());
  937. MultiThreadedCall(chars, FontCreate::DrawCharacter, T, dcs.elms());
  938. return true;
  939. }
  940. static void DrawCharacter(FontChar &font_char, FontCreate &font_create, Int thread_index)
  941. {
  942. font_create.drawCharacter(font_char, font_create.dcs[thread_index]);
  943. }
  944. static Bool TestCol(Image &image, Int x) {REPD(y, image.h()){Color c=image.color(x, y); if(c.r || c.g || c.b)return true;} return false;}
  945. static Bool TestRow(Image &image, Int y) {REPD(x, image.w()){Color c=image.color(x, y); if(c.r || c.g || c.b)return true;} return false;}
  946. void drawCharacter(FontChar &fc, SystemFontDrawContext &dc)
  947. {
  948. dc.createIfEmpty(draw_size.x, draw_size.y, font);
  949. dc.draw(fc.chr, draw_border);
  950. #if TEST_FONT
  951. if(fc.chr>='a' && fc.chr<='z'
  952. || fc.chr>='A' && fc.chr<='Z'
  953. || fc.chr>='0' && fc.chr<='9')dc.image.Export(S+"d:/"+fc.chr+".bmp");
  954. #endif
  955. // calculate width
  956. for(fc.ofs.x=0; fc.ofs.x<dc.image.w(); fc.ofs.x++)if(TestCol(dc.image, fc.ofs.x))break; // start from left
  957. fc.size.x=0; REPD(x, dc.image.w())if(TestCol(dc.image, x)){fc.size.x=x-fc.ofs.x+1; break;} // start from right
  958. // calculate height
  959. for(fc.ofs.y=0; fc.ofs.y<dc.image.h(); fc.ofs.y++)if(TestRow(dc.image, fc.ofs.y))break; // start from top
  960. fc.size.y=0; REPD(y, dc.image.h())if(TestRow(dc.image, y)){fc.size.y=y-fc.ofs.y+1; break;} // start from bottom
  961. // set char image
  962. Image &img=fc.image;
  963. if(img.createSoftTry(fc.size.x, fc.size.y, 1, imageTypeTemp()))
  964. {
  965. // minimum filter
  966. if(U16(fc.chr)<=0x4FF)dc.image.minimum(minimum_filter); // 0x4FF-range for typical characters (not CJK), this filter is not applied for CJK, because those characters are usually thin and don't need this, and also because there are thousands of them, and that would be slow
  967. // copy
  968. Bool clear_shadow=(mode!=Font::SUB_PIXEL && shadow_opacity<=0);
  969. REPD(y, img.h())
  970. REPD(x, img.w())
  971. {
  972. Color c=dc.image.color(fc.ofs.x+x, fc.ofs.y+y);
  973. c.a=AvgI(c.r, c.g, c.b);
  974. if(mode!=Font::SUB_PIXEL) // average colors
  975. {
  976. #if USE_FREE_TYPE
  977. // FreeType doesn't have gamma applied
  978. #elif WINDOWS_OLD
  979. c.a=WinGDIGamma[c.a]; // remove gamma applied by WinGDI
  980. #endif
  981. c.r=c.g=c.b=c.a;
  982. }
  983. if(clear_shadow)c.a=0;
  984. img.color(x, y, c);
  985. }
  986. // calculate widths precisely
  987. Int w=img.w(), offset_scale=DivRound(scaled_size-size, 2), offset=draw_border-fc.ofs.y; // map from 'fc.image' to 'dc.image'
  988. FREP(FONT_WIDTH_TEST)
  989. {
  990. Int y1=((i== 0) ? 0 : offset_scale + i *size/FONT_WIDTH_TEST)+offset, // for first step, ignore 'offset_scale' and always go from the start
  991. y2=((i==FONT_WIDTH_TEST-1) ? scaled_size : offset_scale + (i+1)*size/FONT_WIDTH_TEST)+offset; // for last step, ignore 'offset_scale' and always go to the end
  992. if(y2<=y1)y2=y1+1; // make sure that at least one iteration is performed, because search is exclusive "y<y2"
  993. Int x, y, found;
  994. for(x=0, found=255; x<w; x++) for(y=y1; y<y2; y++) if(img.pixel( x, y)){found=Min(254, x); goto found_l;} found_l: if(fc.chr==u'ำ' && found!=255)found=Min(found+scaled_size/9, 254); fc.width2[0][i]=found; // use "Min(254" because 255 means nothing was found, 'ำ' needs to be placed closer to other characters
  995. for(x=0, found=255; x<w; x++) for(y=y1; y<y2; y++) if(img.pixel(w-1-x, y)){found=Min(254, x); goto found_r;} found_r: fc.width2[1][i]=found; // use "Min(254" because 255 means nothing was found
  996. #if TEST_FONT && 0
  997. img.color(0, y1 , GREEN);
  998. img.color(0, y2-1, RED ); // -1 because search is exclusive "y<y2"
  999. #endif
  1000. }
  1001. #if TEST_FONT
  1002. if(fc.chr>='a' && fc.chr<='z'
  1003. || fc.chr>='A' && fc.chr<='Z'
  1004. || fc.chr>='0' && fc.chr<='9')img.Export(S+"d:/"+fc.chr+" clip.bmp");
  1005. #endif
  1006. // apply shadow
  1007. img.setShadow(shadow_blur, shadow_opacity, shadow_spread, false, shadow_offset, 0, false);
  1008. }
  1009. }
  1010. Bool clean()
  1011. {
  1012. // remove those that don't exist
  1013. REPA(chars)if(!chars[i].size.x)chars.remove(i, true); // check only width, because SPECIAL_CHARS have height=0
  1014. // sort by height
  1015. chars.sort(FontChar::Compare);
  1016. // insert SPECIAL_CHARS as last
  1017. ASSERT(SPECIAL_CHARS==3);
  1018. chars.New().setSpace (size);
  1019. chars.New().setFullSpace(size);
  1020. chars.New().setTab (size);
  1021. return true;
  1022. }
  1023. Bool prepareFont(Font &font)
  1024. {
  1025. // set font data
  1026. font._sub_pixel=(T.mode==Font::SUB_PIXEL);
  1027. font._height = T.size;
  1028. font._padd.x=font._padd.y=T.padd_ul;
  1029. font._padd.z=font._padd.w=T.padd_dr;
  1030. font._chrs.setNumZero(T.chars.elms());
  1031. Int offset=-draw_border;
  1032. if(T.font.base_line>=0)offset+=Round(0.8f*size-T.font.base_line); // 0.8 is the default baseline (was 80 when measured on Arial 100)
  1033. // set characters
  1034. REPA(font._chrs)
  1035. {
  1036. Font::Chr &dest=font._chrs[i];
  1037. FontChar &src = T.chars[i];
  1038. dest.image =0;
  1039. dest.tex .zero();
  1040. dest.chr =src.chr ;
  1041. dest.width =src.size.x; dest. width_padd=Min(dest.width +font.paddingL()+font.paddingR(), 255);
  1042. dest.height=src.size.y; dest.height_padd=Min(dest.height+font.paddingT()+font.paddingB(), 255);
  1043. dest.offset=Mid(src.ofs.y+offset, 0, 255); // use 'Mid' because we're storing as Byte
  1044. Copy(dest.width2, src.width2);
  1045. }
  1046. return true;
  1047. }
  1048. Bool setImages(Font &font)
  1049. {
  1050. for(; leftToProcess(); ) // until there are no chars left
  1051. {
  1052. Int w=0, h=0, fits=0;
  1053. for(Int size =16; size <=max_image_size; size<<=1)
  1054. for(Int aspect= 0; aspect< 3; aspect++)
  1055. {
  1056. w=h=size; switch(aspect)
  1057. {
  1058. case 0: w>>=1; break; // at 1st attempt try making (size/2, size ), as textures should preferrably be taller than wider
  1059. case 1: h>>=1; break; // at 2nd attempt try making (size , size/2), in case this fits the characters but above method doesn't
  1060. // at 3rd attempt try making (size , size )
  1061. }
  1062. fits= countFitsInSize(w, h);
  1063. if(fits==leftToProcess())goto all_fit;
  1064. }
  1065. if(fits==0)return false; // we still have characters to process but none of them fits
  1066. all_fit:;
  1067. applyFonts(font, w, h, fits);
  1068. }
  1069. return true;
  1070. }
  1071. Int countFitsInSize(Int w, Int h)
  1072. {
  1073. Int x=1, y=1, max_h=0; // start from 1, 1 because of texture filtering
  1074. FREP(leftToProcess()) // this skips SPECIAL_CHARS
  1075. {
  1076. VecI2 size=chars[i+processed].image.size();
  1077. if(y+size.y+1>h)return i; // exceeds height
  1078. if(x+size.x+1>w) // exceeds width
  1079. { // go to next line
  1080. x =1;
  1081. y+=max_h+char_border; max_h=0;
  1082. if(x+size.x+1>w
  1083. || y+size.y+1>h)return i; // doesn't fit
  1084. }
  1085. // write
  1086. x+=size.x+char_border;
  1087. MAX(max_h, size.y);
  1088. }
  1089. return leftToProcess(); // all of them fit
  1090. }
  1091. void applyFonts(Font &font, Int w, Int h, Int fits)
  1092. {
  1093. // set font image
  1094. Int image_index=font._images.elms();
  1095. Image &image =font._images.New ().createSoft(w, h, 1, imageTypeTemp()).clear(); // create as soft
  1096. Int x=1, y=1, max_h=0;
  1097. const Flt eps=0.5f*0; // don't add this epsilon because it disables per pixel font, and when enabled it's rarely even visible (only when font is very small but magnified)
  1098. FREP(fits)
  1099. {
  1100. FontChar &src =T . chars[i+processed];
  1101. Font::Chr &dest=font._chrs [i+processed];
  1102. VecI2 size=src .image.size();
  1103. if(x+size.x+1>w) // exceeds width
  1104. { // go to next line
  1105. x =1;
  1106. y+=max_h+char_border; max_h=0;
  1107. }
  1108. dest.image=image_index;
  1109. dest.tex.set(Flt(x-eps)/w, Flt(y-eps)/h, Flt(x+size.x+eps)/w, Flt(y+size.y+eps)/h);
  1110. REPD(sy, size.y)
  1111. REPD(sx, size.x)image.color(x+sx, y+sy, src.image.color(sx, sy));
  1112. x+=size.x+char_border;
  1113. MAX(max_h, size.y);
  1114. }
  1115. processed+=fits;
  1116. }
  1117. };
  1118. static void CompressTextures(Image &image, FontCreate &fc, Int thread_index)
  1119. {
  1120. ThreadMayUseGPUData();
  1121. image.copy(image, -1, -1, -1, fc.image_type, fc.software ? IMAGE_SOFT : IMAGE_2D, fc.mip_maps);
  1122. }
  1123. Bool Font::create(C Params &params)
  1124. {
  1125. del();
  1126. SyncUnlocker locker(D._lock); // if this is called in 'Draw' then 'D._lock' is already locked, however multi-threaded font creation may want to lock 'D._lock' resulting in deadlock, therefore we need to first unlock it
  1127. FontCreate fc(params);
  1128. if(fc.createFont())
  1129. if(fc.drawCharacters())
  1130. if(fc.clean())
  1131. if(fc.prepareFont(T))
  1132. if(fc.setImages(T))
  1133. {
  1134. #if USE_FREE_TYPE
  1135. #elif WINDOWS_OLD
  1136. if(fc.mode!=Font::DEFAULT) // font with extra smoothing uses anti-aliasing, 2 pixels with half transparency are used instead of 1, this means that widths are 1 extra pixel bigger
  1137. {
  1138. _padd.z++; // since we'll decrease the width by 1, then we need to increase the right padding by 1
  1139. REPA(_chrs)
  1140. {
  1141. Font::Chr &c=_chrs[i];
  1142. c.width=Max(c.width-1, 0);
  1143. //c.width_padd remains the same, because width was decreased but padding increased
  1144. REP(FONT_WIDTH_TEST)if(c.width2[1][i]!=0xFF)c.width2[1][i]=Mid(c.width2[1][i]+1, 0, 254); // here increase by 1 and not decrease, because 'width2[1]' means amount of distance from the right side
  1145. //REP(FONT_WIDTH_TEST)if(c.width2[0][i]!=0xFF)c.width2[0][i]=Mid(c.width2[0][i]+1, 0, 254); // don't use this as that's too much
  1146. }
  1147. }
  1148. #endif
  1149. MultiThreadedCall(_images, CompressTextures, fc);
  1150. setRemap ();
  1151. setGLFont();
  1152. return true;
  1153. }
  1154. del(); return false;
  1155. }
  1156. /******************************************************************************/
  1157. void DisplayDraw::textDepth(Bool use, Flt depth)
  1158. {
  1159. if(D._text_depth=use)Sh.h_FontDepth->set(depth);
  1160. }
  1161. /******************************************************************************/
  1162. // MAIN
  1163. /******************************************************************************/
  1164. void ShutFont()
  1165. {
  1166. TextStyles.del();
  1167. Fonts .del();
  1168. }
  1169. /******************************************************************************/
  1170. }
  1171. /******************************************************************************/