CFontTool.cpp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845
  1. #include "CFontTool.h"
  2. #include "IXMLWriter.h"
  3. #include <iostream>
  4. #include <fstream>
  5. using namespace irr;
  6. const int fontsizes[] = {4,6,8,9,10,11,12,14,16,18,20,22,24,26,28,36,48,56,68,72,0};
  7. char bUsed[0x10000]={0};
  8. inline u32 getTextureSizeFromSurfaceSize(u32 size)
  9. {
  10. u32 ts = 0x01;
  11. while(ts < size)
  12. ts <<= 1;
  13. return ts;
  14. }
  15. bool LoadPoFiles(const char* sListFileName){
  16. char s[1024];
  17. std::ifstream fin(sListFileName);
  18. if(!fin){
  19. std::cout<<"Error: Can't open "<<sListFileName<<std::endl;
  20. return false;
  21. }
  22. std::cout<<"Opened list file "<<sListFileName<<std::endl;
  23. for(;;){
  24. fin.getline(s,1024);
  25. if(fin.eof()) break;
  26. std::ifstream fin2(s);
  27. if(!fin2){
  28. std::cout<<"Error: Can't open "<<s<<std::endl;
  29. }else{
  30. std::cout<<"Opened "<<s<<std::endl;
  31. //buggy code that convert UTF-8 to UCS-2
  32. for(;;){
  33. unsigned char c1=(unsigned char)fin2.get();
  34. unsigned short out=0;
  35. if(fin2.eof()) break;
  36. if(c1>=0xF0) continue;
  37. else if(c1>=0xE0){
  38. out=((c1&0xF)<<12)
  39. |((((unsigned char)fin2.get())&0x3F)<<6)
  40. |(((unsigned char)fin2.get())&0x3F);
  41. }else if(c1>=0xC0){
  42. out=((c1&0x1F)<<6)
  43. |(((unsigned char)fin2.get())&0x3F);
  44. }else{
  45. out=c1;
  46. }
  47. bUsed[out]=1;
  48. }
  49. }
  50. }
  51. return true;
  52. }
  53. // windows specific
  54. #ifdef _IRR_WINDOWS_
  55. const DWORD charsets[] = { ANSI_CHARSET, DEFAULT_CHARSET, OEM_CHARSET, BALTIC_CHARSET, GB2312_CHARSET, CHINESEBIG5_CHARSET,
  56. EASTEUROPE_CHARSET, GREEK_CHARSET, HANGUL_CHARSET, MAC_CHARSET, RUSSIAN_CHARSET,
  57. SHIFTJIS_CHARSET, SYMBOL_CHARSET, TURKISH_CHARSET, VIETNAMESE_CHARSET, JOHAB_CHARSET,
  58. ARABIC_CHARSET, HEBREW_CHARSET, THAI_CHARSET, 0};
  59. const wchar_t *setnames[] = {L"ANSI", L"All Available", L"OEM", L"Baltic", L"Chinese Simplified", L"Chinese Traditional",
  60. L"Eastern European", L"Greek", L"Hangul", L"Macintosh", L"Russian",
  61. L"Japanese", L"Symbol", L"Turkish", L"Vietnamese", L"Johab",
  62. L"Arabic", L"Hebrew", L"Thai", 0};
  63. // callback for adding font names
  64. int CALLBACK EnumFontFamExProc( ENUMLOGFONTEX *lpelfe, NEWTEXTMETRICEX *lpntme,
  65. DWORD FontType, LPARAM lParam)
  66. {
  67. CFontTool* t = (CFontTool*) lParam;
  68. t->FontNames.push_back( core::stringw(lpelfe->elfFullName));
  69. return 1;
  70. }
  71. //
  72. // Constructor
  73. //
  74. CFontTool::CFontTool(IrrlichtDevice* device) : FontSizes(fontsizes),
  75. Device(device), UseAlphaChannel(false),
  76. // win specific
  77. dc(0)
  78. {
  79. // init display context
  80. dc = CreateDC(L"DISPLAY", L"DISPLAY", 0 ,0 );
  81. // populate list of available character set names
  82. for (int i=0; setnames[i] != 0; ++i)
  83. CharSets.push_back( core::stringw(setnames[i]));
  84. selectCharSet(0);
  85. }
  86. void CFontTool::selectCharSet(u32 currentCharSet)
  87. {
  88. if ( currentCharSet >= CharSets.size() )
  89. return;
  90. LOGFONTW lf;
  91. lf.lfFaceName[0] = L'\0';
  92. lf.lfCharSet = (BYTE) charsets[currentCharSet];
  93. // HRESULT hr; // no error checking(!)
  94. // clear font list
  95. FontNames.clear();
  96. // create list of available fonts
  97. EnumFontFamiliesExW( dc, (LPLOGFONTW) &lf, (FONTENUMPROCW) EnumFontFamExProc, (LPARAM) this, 0);
  98. }
  99. bool CFontTool::makeBitmapFont(u32 fontIndex, u32 charsetIndex, s32 fontSize, u32 textureWidth, u32 textureHeight, bool bold, bool italic, bool aa, bool alpha,bool usedOnly,bool excludeLatin)
  100. {
  101. if (fontIndex >= FontNames.size() || charsetIndex >= CharSets.size() )
  102. return false;
  103. UseAlphaChannel = alpha;
  104. u32 currentImage = 0;
  105. // create the font
  106. HFONT font = CreateFontW(
  107. -MulDiv(fontSize, GetDeviceCaps(dc, LOGPIXELSY), 72), 0,
  108. 0,0,
  109. bold ? FW_BOLD : 0,
  110. italic, 0,0, charsets[charsetIndex], 0,0,
  111. aa ? ANTIALIASED_QUALITY : 0,
  112. 0, FontNames[fontIndex].c_str() );
  113. if (!font)
  114. return false;
  115. SelectObject(dc, font);
  116. SetTextAlign (dc,TA_LEFT | TA_TOP | TA_NOUPDATECP);
  117. // get rid of the current textures/images
  118. for (u32 i=0; i<currentTextures.size(); ++i)
  119. currentTextures[i]->drop();
  120. currentTextures.clear();
  121. for (u32 i=0; i<currentImages.size(); ++i)
  122. currentImages[i]->drop();
  123. currentImages.clear();
  124. // clear current image mappings
  125. CharMap.clear();
  126. // clear array
  127. Areas.clear();
  128. // get information about this font's unicode ranges.
  129. s32 size = GetFontUnicodeRanges( dc, 0);
  130. c8 *buf = new c8[size];
  131. LPGLYPHSET glyphs = (LPGLYPHSET)buf;
  132. GetFontUnicodeRanges( dc, glyphs);
  133. // s32 TotalCharCount = glyphs->cGlyphsSupported;
  134. s32 currentx=0, currenty=0, maxy=0;
  135. for (u32 range=0; range < glyphs->cRanges; range++)
  136. {
  137. WCRANGE* current = &glyphs->ranges[range];
  138. //maxy=0;
  139. // loop through each glyph and write its size and position
  140. for (s32 ch=current->wcLow; ch< current->wcLow + current->cGlyphs; ch++)
  141. {
  142. wchar_t currentchar = ch;
  143. /* if ( IsDBCSLeadByte((BYTE) ch))
  144. continue; // surragate pairs unsupported */
  145. if(excludeLatin && ch>=0 && ch<0x100) continue;
  146. if(usedOnly && !bUsed[(unsigned short)ch]) continue;
  147. // get the dimensions
  148. SIZE size;
  149. ABC abc;
  150. GetTextExtentPoint32W(dc, &currentchar, 1, &size);
  151. SFontArea fa;
  152. fa.underhang = 0;
  153. fa.overhang = 0;
  154. if (GetCharABCWidthsW(dc, currentchar, currentchar, &abc)) // for unicode fonts, get overhang, underhang, width
  155. {
  156. size.cx = abc.abcB;
  157. fa.underhang = abc.abcA;
  158. fa.overhang = abc.abcC;
  159. }
  160. if (size.cy < 1)
  161. continue;
  162. //GetGlyphOutline(dc, currentchar, GGO_METRICS, &gm, 0, 0, 0);
  163. //size.cx++; size.cy++;
  164. // wrap around?
  165. if (currentx + size.cx > (s32) textureWidth)
  166. {
  167. currenty += maxy;
  168. currentx = 0;
  169. if ((u32)(currenty + maxy) > textureHeight)
  170. {
  171. currentImage++; // increase Image count
  172. currenty=0;
  173. }
  174. maxy = 0;
  175. }
  176. // add this char dimension to the current map
  177. fa.rectangle = core::rect<s32>(currentx, currenty, currentx + size.cx, currenty + size.cy);
  178. fa.sourceimage = currentImage;
  179. CharMap.insert(currentchar, Areas.size());
  180. Areas.push_back( fa );
  181. currentx += size.cx +1;
  182. if (size.cy+1 > maxy)
  183. maxy = size.cy+1;
  184. }
  185. }
  186. currenty += maxy;
  187. u32 lastTextureHeight = getTextureSizeFromSurfaceSize(currenty);
  188. // delete the glyph set
  189. delete [] buf;
  190. currentImages.set_used(currentImage+1);
  191. currentTextures.set_used(currentImage+1);
  192. for (currentImage=0; currentImage < currentImages.size(); ++currentImage)
  193. {
  194. core::stringc logmsg = "Creating image ";
  195. logmsg += (s32) (currentImage+1);
  196. logmsg += " of ";
  197. logmsg += (s32) currentImages.size();
  198. Device->getLogger()->log(logmsg.c_str());
  199. // no need for a huge final texture
  200. u32 texHeight = textureHeight;
  201. if (currentImage == currentImages.size()-1 )
  202. texHeight = lastTextureHeight;
  203. // make a new bitmap
  204. HBITMAP bmp = CreateCompatibleBitmap(dc, textureWidth, texHeight);
  205. HDC bmpdc = CreateCompatibleDC(dc);
  206. LOGBRUSH lbrush;
  207. lbrush.lbColor = RGB(0,0,0);
  208. lbrush.lbHatch = 0;
  209. lbrush.lbStyle = BS_SOLID;
  210. HBRUSH brush = CreateBrushIndirect(&lbrush);
  211. HPEN pen = CreatePen(PS_NULL, 0, 0);
  212. HGDIOBJ oldbmp = SelectObject(bmpdc, bmp);
  213. HGDIOBJ oldbmppen = SelectObject(bmpdc, pen);
  214. HGDIOBJ oldbmpbrush = SelectObject(bmpdc, brush);
  215. HGDIOBJ oldbmpfont = SelectObject(bmpdc, font);
  216. SetTextColor(bmpdc, RGB(255,255,255));
  217. Rectangle(bmpdc, 0,0,textureWidth,texHeight);
  218. SetBkMode(bmpdc, TRANSPARENT);
  219. // draw the letters...
  220. // iterate through the tree
  221. core::map<wchar_t, u32>::Iterator it = CharMap.getIterator();
  222. while (!it.atEnd())
  223. {
  224. s32 currentArea = (*it).getValue();
  225. wchar_t wch = (*it).getKey();
  226. // sloppy but I couldnt be bothered rewriting it
  227. if (Areas[currentArea].sourceimage == currentImage)
  228. {
  229. // draw letter
  230. s32 sx = Areas[currentArea].rectangle.UpperLeftCorner.X - Areas[currentArea].underhang;
  231. TextOutW(bmpdc, sx, Areas[currentArea].rectangle.UpperLeftCorner.Y, &wch, 1);
  232. // if ascii font...
  233. //SetPixel(bmpdc, Areas[currentArea].rectangle.UpperLeftCorner.X, Areas[currentArea].rectangle.UpperLeftCorner.Y, RGB(255,255,0));// left upper corner mark
  234. }
  235. it++;
  236. }
  237. // copy the font bitmap into a new irrlicht image
  238. BITMAP b;
  239. PBITMAPINFO pbmi;
  240. WORD cClrBits;
  241. u32 cformat;
  242. // Retrieve the bitmap color format, width, and height.
  243. GetObject(bmp, sizeof(BITMAP), (LPSTR)&b);
  244. // Convert the color format to a count of bits.
  245. cClrBits = (WORD)(b.bmPlanes * b.bmBitsPixel);
  246. if (cClrBits <= 8) // we're not supporting these
  247. cformat = -1;
  248. else if (cClrBits <= 16)
  249. cformat = video::ECF_A1R5G5B5;
  250. else if (cClrBits <= 24)
  251. cformat = video::ECF_R8G8B8;
  252. else
  253. cformat = video::ECF_A8R8G8B8;
  254. pbmi = (PBITMAPINFO) LocalAlloc(LPTR,
  255. sizeof(BITMAPINFOHEADER));
  256. // Initialize the fields in the BITMAPINFO structure.
  257. pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
  258. pbmi->bmiHeader.biWidth = b.bmWidth;
  259. pbmi->bmiHeader.biHeight = b.bmHeight;
  260. pbmi->bmiHeader.biPlanes = b.bmPlanes;
  261. pbmi->bmiHeader.biBitCount = b.bmBitsPixel;
  262. // If the bitmap is not compressed, set the BI_RGB flag.
  263. pbmi->bmiHeader.biCompression = BI_RGB;
  264. // Compute the number of bytes in the array of color
  265. // indices and store the result in biSizeImage.
  266. // For Windows NT, the width must be DWORD aligned unless
  267. // the bitmap is RLE compressed. This example shows this.
  268. // For Windows 95/98/Me, the width must be WORD aligned unless the
  269. // bitmap is RLE compressed.
  270. pbmi->bmiHeader.biSizeImage = ((pbmi->bmiHeader.biWidth * cClrBits +31) & ~31) /8
  271. * pbmi->bmiHeader.biHeight;
  272. // Set biClrImportant to 0, indicating that all of the
  273. // device colors are important.
  274. pbmi->bmiHeader.biClrImportant = 0;
  275. LPBYTE lpBits; // memory pointer
  276. PBITMAPINFOHEADER pbih = (PBITMAPINFOHEADER) pbmi;
  277. lpBits = (LPBYTE) GlobalAlloc(GMEM_FIXED, pbih->biSizeImage);
  278. GetDIBits(dc, bmp, 0, (WORD) pbih->biHeight, lpBits, pbmi, DIB_RGB_COLORS);
  279. // DEBUG- copy to clipboard
  280. //OpenClipboard(hWnd);
  281. //EmptyClipboard();
  282. //SetClipboardData(CF_BITMAP, bmp);
  283. //CloseClipboard();
  284. // flip bitmap
  285. s32 rowsize = ((pbmi->bmiHeader.biWidth * cClrBits +31) & ~31) /8;
  286. c8 *row = new c8[rowsize];
  287. for (s32 i=0; i < (pbih->biHeight/2); ++i)
  288. {
  289. // grab a row
  290. memcpy(row, lpBits + (rowsize * i), rowsize);
  291. // swap row
  292. memcpy(lpBits + (rowsize * i), lpBits + ((pbih->biHeight-1 -i) * rowsize ) , rowsize);
  293. memcpy(lpBits + ((pbih->biHeight-1 -i) * rowsize ), row , rowsize);
  294. }
  295. bool ret = false;
  296. if (cformat == video::ECF_A8R8G8B8)
  297. {
  298. // in this case the font should have an alpha channel, but since windows doesn't draw one
  299. // we have to set one manually by going through all the pixels.. *sigh*
  300. u8* m;
  301. for (m = lpBits; m < lpBits + pbih->biSizeImage; m+=4)
  302. {
  303. if (UseAlphaChannel)
  304. {
  305. if (m[0] > 0) // pixel has colour
  306. {
  307. m[3]=m[0]; // set alpha
  308. m[0]=m[1]=m[2] = 255; // everything else is full
  309. }
  310. }
  311. else
  312. m[3]=255; // all pixels are full alpha
  313. }
  314. }
  315. else if (cformat == video::ECF_A1R5G5B5)
  316. {
  317. u8* m;
  318. for (m = lpBits; m < lpBits + pbih->biSizeImage; m+=2)
  319. {
  320. WORD *p = (WORD*)m;
  321. if (m[0] > 0 || !UseAlphaChannel) // alpha should be set
  322. *p |= 0x8000; // set alpha bit
  323. }
  324. }
  325. else
  326. {
  327. cformat = -1;
  328. }
  329. // make a texture from the image
  330. if (cformat != -1)
  331. {
  332. // turn mip-mapping off
  333. bool b = Device->getVideoDriver()->getTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS);
  334. currentImages[currentImage] = Device->getVideoDriver()->createImageFromData((video::ECOLOR_FORMAT)cformat, core::dimension2d<u32>(textureWidth,texHeight), (void*)lpBits);
  335. Device->getVideoDriver()->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS,b);
  336. }
  337. else
  338. {
  339. Device->getLogger()->log("Couldn't create font, your pixel format is unsupported.");
  340. }
  341. // free memory and windows resources
  342. // sloppy I know, but I only intended to create one image at first.
  343. delete [] row;
  344. LocalFree(pbmi);
  345. GlobalFree(lpBits);
  346. DeleteDC(bmpdc);
  347. DeleteObject(brush);
  348. DeleteObject(pen);
  349. DeleteObject(bmp);
  350. if (currentImages[currentImage])
  351. {
  352. // add texture
  353. currentTextures[currentImage] = Device->getVideoDriver()->addTexture("GUIFontImage",currentImages[currentImage]);
  354. currentTextures[currentImage]->grab();
  355. }
  356. else
  357. {
  358. Device->getLogger()->log("Something went wrong, aborting.");
  359. // drop all images
  360. DeleteObject(font);
  361. return false;
  362. }
  363. } // looping through each texture
  364. DeleteObject(font);
  365. return true;
  366. }
  367. #else
  368. CFontTool::CFontTool(IrrlichtDevice *device) : FontSizes(fontsizes), Device(device), UseAlphaChannel(false)
  369. {
  370. if (!XftInitFtLibrary())
  371. {
  372. core::stringc logmsg = "XFT not found\n";
  373. Device->getLogger()->log(logmsg.c_str());
  374. exit(EXIT_FAILURE);
  375. }
  376. /* Get a list of the font foundries, storing them in a set to sort */
  377. std::set<core::stringw> foundries;
  378. Display* display = (Display*)Device->getVideoDriver()->getExposedVideoData().OpenGLLinux.X11Display;
  379. XftFontSet* fonts = XftListFonts(display, DefaultScreen(display), 0, XFT_FOUNDRY, NULL);
  380. for (int i = 0; i < fonts->nfont; i++)
  381. {
  382. char *foundry;
  383. XftPatternGetString(fonts->fonts[i], XFT_FOUNDRY, 0, &foundry);
  384. core::stringw tmp(foundry);
  385. foundries.insert(tmp);
  386. }
  387. XftFontSetDestroy(fonts);
  388. /* Copy the sorted list into the array */
  389. CharSets.clear();
  390. for (std::set<core::stringw>::iterator i = foundries.begin(); i != foundries.end(); i++)
  391. CharSets.push_back((*i).c_str());
  392. selectCharSet(0);
  393. }
  394. /* Note: There must be some trick for using strings as pattern parameters to XftListFonts because
  395. no matter how I specify a string, I end up with an intermittent segfault. Since XftFontList is
  396. just calling FcFontList, that's what I'll do too since that works OK */
  397. void CFontTool::selectCharSet(u32 currentCharSet)
  398. {
  399. /* Get a list of the font families, storing them in a set to sort */
  400. char foundry[256];
  401. sprintf(&foundry[0],"%ls",CharSets[currentCharSet].c_str());
  402. std::set<core::stringw> families;
  403. XftPattern *pattern = FcPatternCreate();
  404. XftPatternAddString(pattern, FC_FOUNDRY, &foundry[0]);
  405. XftObjectSet *objectset = FcObjectSetCreate();
  406. XftObjectSetAdd(objectset, XFT_FOUNDRY);
  407. XftObjectSetAdd(objectset, XFT_FAMILY);
  408. FcFontSet *fonts = FcFontList(NULL, pattern, objectset);
  409. for (int i = 0; i < fonts->nfont; i++)
  410. {
  411. char* ptr;
  412. XftPatternGetString(fonts->fonts[i], XFT_FAMILY, 0, &ptr);
  413. core::stringw family(ptr);
  414. families.insert(family);
  415. }
  416. XftPatternDestroy(pattern);
  417. FcObjectSetDestroy(objectset);
  418. /* Copy the sorted list into the array */
  419. FontNames.clear();
  420. for (std::set<core::stringw>::iterator i = families.begin(); i != families.end(); i++)
  421. FontNames.push_back((*i).c_str());
  422. }
  423. bool CFontTool::makeBitmapFont(u32 fontIndex, u32 charsetIndex, s32 fontSize, u32 textureWidth, u32 textureHeight, bool bold, bool italic, bool aa, bool alpha,bool usedOnly,bool excludeLatin)
  424. {
  425. if (fontIndex >= FontNames.size() || charsetIndex >= CharSets.size() )
  426. return false;
  427. Display *display = (Display*) Device->getVideoDriver()->getExposedVideoData().OpenGLLinux.X11Display;
  428. u32 screen = DefaultScreen(display);
  429. Window win = RootWindow(display, screen);
  430. Visual *visual = DefaultVisual(display, screen);
  431. UseAlphaChannel = alpha;
  432. u32 currentImage = 0;
  433. XftResult result;
  434. XftPattern *request = XftPatternCreate();
  435. char foundry[256], family[256];
  436. sprintf(&foundry[0],"%ls",CharSets[charsetIndex].c_str());
  437. sprintf(&family[0],"%ls",FontNames[fontIndex].c_str());
  438. XftPatternAddString(request, XFT_FOUNDRY, &foundry[0]);
  439. XftPatternAddString(request, XFT_FAMILY, &family[0]);
  440. XftPatternAddInteger(request, XFT_PIXEL_SIZE, fontSize);
  441. XftPatternAddInteger(request, XFT_WEIGHT, bold ? XFT_WEIGHT_BLACK : XFT_WEIGHT_LIGHT);
  442. XftPatternAddInteger(request, XFT_SLANT, italic ? XFT_SLANT_ITALIC : XFT_SLANT_ROMAN);
  443. XftPatternAddBool(request, XFT_ANTIALIAS, aa);
  444. /* Find the closest font that matches the user choices and open it and check if the returned
  445. font has anti aliasing enabled by default, even if it wasn't requested */
  446. FcBool aaEnabled;
  447. XftPattern *found = XftFontMatch(display, DefaultScreen(display), request, &result);
  448. XftPatternGetBool(found, XFT_ANTIALIAS, 0, &aaEnabled);
  449. aa = aaEnabled;
  450. XftFont *font = XftFontOpenPattern(display, found);
  451. // get rid of the current textures/images
  452. for (u32 i=0; i<currentTextures.size(); ++i)
  453. currentTextures[i]->drop();
  454. currentTextures.clear();
  455. for (u32 i=0; i<currentImages.size(); ++i)
  456. currentImages[i]->drop();
  457. currentImages.clear();
  458. CharMap.clear();
  459. Areas.clear();
  460. /* Calculate the max height of the font. Annoyingly, it seems that the height property of the font
  461. is the maximum height of any single character, but a string of characters, aligned along their
  462. baselines, can exceed this figure. Because I don't know any better way of doing it, I'm going to
  463. have to use the brute force method.
  464. Note: There will be a certain number of charters in a font, however they may not be grouped
  465. consecutively, and could in fact be spread out with many gaps */
  466. u32 maxY = 0;
  467. u32 charsFound = 0;
  468. for (FT_UInt charCode = 0; charsFound < FcCharSetCount(font->charset); charCode++)
  469. {
  470. if (!XftCharExists(display, font, charCode))
  471. continue;
  472. charsFound++;
  473. XGlyphInfo extents;
  474. XftTextExtents32(display, font, &charCode, 1, &extents);
  475. if ((extents.xOff <= 0) && (extents.height <= 0))
  476. continue;
  477. /* Calculate the width and height, adding 1 extra pixel if anti aliasing is enabled */
  478. u32 chWidth = extents.xOff + (aa ? 1 : 0);
  479. u32 chHeight = (font->ascent - extents.y + extents.height) + (aa ? 1 : 0);
  480. if (chHeight > maxY)
  481. maxY = chHeight;
  482. /* Store the character details here */
  483. SFontArea fontArea;
  484. fontArea.rectangle = core::rect<s32>(0, 0, chWidth, chHeight);
  485. CharMap.insert(charCode, Areas.size());
  486. Areas.push_back(fontArea);
  487. }
  488. core::stringc logmsg = "Found ";
  489. logmsg += (s32) (CharMap.size() + 1);
  490. logmsg += " characters";
  491. Device->getLogger()->log(logmsg.c_str());
  492. /* Get the size of the chars and allocate them a position on a texture. If the next character that
  493. is added would be outside the width or height of the texture, then a new texture is added */
  494. u32 currentX = 0, currentY = 0, rowY = 0;
  495. for (core::map<wchar_t, u32>::Iterator it = CharMap.getIterator(); !it.atEnd(); it++)
  496. {
  497. s32 currentArea = (*it).getValue();
  498. SFontArea *fontArea = &Areas[currentArea];
  499. u32 chWidth = fontArea->rectangle.LowerRightCorner.X;
  500. u32 chHeight = fontArea->rectangle.LowerRightCorner.Y;
  501. /* If the width of this char will exceed the textureWidth then start a new row */
  502. if ((currentX + chWidth) > textureWidth)
  503. {
  504. currentY += rowY;
  505. currentX = 0;
  506. /* If the new row added to the texture exceeds the textureHeight then start a new texture */
  507. if ((currentY + rowY) > textureHeight)
  508. {
  509. currentImage++;
  510. currentY = 0;
  511. }
  512. rowY = 0;
  513. }
  514. /* Update the area with the current x and y and texture */
  515. fontArea->rectangle = core::rect<s32>(currentX, currentY, currentX + chWidth, currentY + chHeight);
  516. fontArea->sourceimage = currentImage;
  517. currentX += chWidth + 1;
  518. if (chHeight + 1 > rowY)
  519. rowY = chHeight + 1;
  520. }
  521. /* The last row of chars and the last texture weren't accounted for in the loop, so add them here */
  522. currentY += rowY;
  523. u32 lastTextureHeight = getTextureSizeFromSurfaceSize(currentY);
  524. currentImages.set_used(currentImage + 1);
  525. currentTextures.set_used(currentImage + 1);
  526. /* Initialise colours */
  527. XftColor colFore, colBack;
  528. XRenderColor xFore = {0xffff, 0xffff, 0xffff, 0xffff};
  529. XRenderColor xBack = {0x0000, 0x0000, 0x0000, 0xffff};
  530. XftColorAllocValue(display, DefaultVisual(display, screen), DefaultColormap(display, screen), &xFore, &colFore);
  531. XftColorAllocValue(display, DefaultVisual(display, screen), DefaultColormap(display, screen), &xBack, &colBack);
  532. /* Create a pixmap that is large enough to hold any character in the font */
  533. Pixmap pixmap = XCreatePixmap(display, win, textureWidth, maxY, DefaultDepth(display, screen));
  534. XftDraw *draw = XftDrawCreate(display, pixmap, visual, DefaultColormap(display, screen));
  535. /* Render the chars */
  536. for (currentImage = 0; currentImage < currentImages.size(); ++currentImage)
  537. {
  538. core::stringc logmsg = "Creating image ";
  539. logmsg += (s32) (currentImage+1);
  540. logmsg += " of ";
  541. logmsg += (s32) currentImages.size();
  542. Device->getLogger()->log(logmsg.c_str());
  543. /* The last texture that is saved is vertically shrunk to fit the characters drawn on it */
  544. u32 texHeight = textureHeight;
  545. if (currentImage == currentImages.size() - 1)
  546. texHeight = lastTextureHeight;
  547. /* The texture that holds this "page" of characters */
  548. currentImages[currentImage] = Device->getVideoDriver()->createImage(video::ECF_A8R8G8B8, core::dimension2du(textureWidth, texHeight));
  549. currentImages[currentImage]->fill(video::SColor(alpha ? 0 : 255,0,0,0));
  550. for (core::map<wchar_t, u32>::Iterator it = CharMap.getIterator(); !it.atEnd(); it++)
  551. {
  552. FcChar32 wch = (*it).getKey();
  553. s32 currentArea = (*it).getValue();
  554. if (Areas[currentArea].sourceimage == currentImage)
  555. {
  556. SFontArea *fontArea = &Areas[currentArea];
  557. u32 chWidth = fontArea->rectangle.LowerRightCorner.X - fontArea->rectangle.UpperLeftCorner.X;
  558. u32 chHeight = fontArea->rectangle.LowerRightCorner.Y - fontArea->rectangle.UpperLeftCorner.Y;
  559. /* Draw the glyph onto the pixmap */
  560. XGlyphInfo extents;
  561. XftDrawRect(draw, &colBack, 0, 0, chWidth, chHeight);
  562. XftTextExtents32(display, font, &wch, 1, &extents);
  563. XftDrawString32(draw, &colFore, font, extents.x, extents.y, &wch, 1);
  564. /* Convert the pixmap into an image, then copy it onto the Irrlicht texture, pixel by pixel.
  565. There's bound to be a faster way, but this is adequate */
  566. u32 xDest = fontArea->rectangle.UpperLeftCorner.X;
  567. u32 yDest = fontArea->rectangle.UpperLeftCorner.Y + font->ascent - extents.y;
  568. XImage *image = XGetImage(display, pixmap, 0, 0, chWidth, chHeight, 0xffffff, XYPixmap);
  569. if (image)
  570. {
  571. for (u32 ySrc = 0; ySrc < chHeight; ySrc++)
  572. for (u32 xSrc = 0; xSrc < chWidth; xSrc++)
  573. {
  574. /* Get the pixel colour and break it down into rgb components */
  575. u32 col = XGetPixel(image, xSrc, ySrc);
  576. u32 a = 255;
  577. u32 r = col & visual->red_mask;
  578. u32 g = col & visual->green_mask;
  579. u32 b = col & visual->blue_mask;
  580. while (r > 0xff) r >>= 8;
  581. while (g > 0xff) g >>= 8;
  582. while (b > 0xff) b >>= 8;
  583. /* To make the background transparent, set the colour to 100% white and the alpha to
  584. the average of the three rgb colour components to maintain the anti-aliasing */
  585. if (alpha)
  586. {
  587. a = (r + g + b) / 3;
  588. r = 255;
  589. g = 255;
  590. b = 255;
  591. }
  592. currentImages[currentImage]->setPixel(xDest + xSrc,yDest + ySrc,video::SColor(a,r,g,b));
  593. }
  594. image->f.destroy_image(image);
  595. }
  596. }
  597. }
  598. /* Add the texture to the list */
  599. currentTextures[currentImage] = Device->getVideoDriver()->addTexture("GUIFontImage",currentImages[currentImage]);
  600. currentTextures[currentImage]->grab();
  601. }
  602. XftColorFree (display, visual, DefaultColormap(display, screen), &colFore);
  603. XftColorFree (display, visual, DefaultColormap(display, screen), &colBack);
  604. XftFontClose(display,font);
  605. XftPatternDestroy(request);
  606. XftDrawDestroy(draw);
  607. XFreePixmap(display, pixmap);
  608. return true;
  609. }
  610. #endif
  611. CFontTool::~CFontTool()
  612. {
  613. #ifdef _IRR_WINDOWS_
  614. // destroy display context
  615. if (dc)
  616. DeleteDC(dc);
  617. #endif
  618. // drop textures+images
  619. for (u32 i=0; i<currentTextures.size(); ++i)
  620. currentTextures[i]->drop();
  621. currentTextures.clear();
  622. for (u32 i=0; i<currentImages.size(); ++i)
  623. currentImages[i]->drop();
  624. currentImages.clear();
  625. }
  626. bool CFontTool::saveBitmapFont(const c8 *filename, const c8* format)
  627. {
  628. if (currentImages.size() == 0)
  629. {
  630. Device->getLogger()->log("No image data to write, aborting.");
  631. return false;
  632. }
  633. core::stringc fn = filename;
  634. core::stringc imagename = filename;
  635. fn += ".xml";
  636. io::IXMLWriter *writer = Device->getFileSystem()->createXMLWriter(fn.c_str());
  637. // header and line breaks
  638. writer->writeXMLHeader();
  639. writer->writeLineBreak();
  640. // write information
  641. writer->writeElement(L"font", false, L"type", L"bitmap");
  642. writer->writeLineBreak();
  643. writer->writeLineBreak();
  644. // write images and link to them
  645. u32 offset_for_asian_fonts=100;
  646. for (u32 i=0; i<currentImages.size(); ++i)
  647. {
  648. imagename = filename;
  649. imagename += (s32)i;
  650. imagename += ".";
  651. imagename += format;
  652. Device->getVideoDriver()->writeImageToFile(currentImages[i],imagename.c_str());
  653. writer->writeElement(L"Texture", true,
  654. L"index", core::stringw(i+offset_for_asian_fonts).c_str(),
  655. L"filename", core::stringw(imagename.c_str()).c_str(),
  656. L"hasAlpha", UseAlphaChannel ? L"true" : L"false");
  657. writer->writeLineBreak();
  658. }
  659. writer->writeLineBreak();
  660. // write each character
  661. core::map<wchar_t, u32>::Iterator it = CharMap.getIterator();
  662. while (!it.atEnd())
  663. {
  664. SFontArea &fa = Areas[(*it).getValue()];
  665. wchar_t c[2];
  666. c[0] = (*it).getKey();
  667. c[1] = L'\0';
  668. core::stringw area, under, over, image;
  669. area = core::stringw(fa.rectangle.UpperLeftCorner.X);
  670. area += L", ";
  671. area += fa.rectangle.UpperLeftCorner.Y;
  672. area += L", ";
  673. area += fa.rectangle.LowerRightCorner.X;
  674. area += L", ";
  675. area += fa.rectangle.LowerRightCorner.Y;
  676. core::array<core::stringw> names;
  677. core::array<core::stringw> values;
  678. names.clear();
  679. values.clear();
  680. // char
  681. names.push_back(core::stringw(L"c"));
  682. values.push_back(core::stringw(c));
  683. // image number
  684. if (fa.sourceimage+offset_for_asian_fonts != 0)
  685. {
  686. image = core::stringw(fa.sourceimage+offset_for_asian_fonts);
  687. names.push_back(core::stringw(L"i"));
  688. values.push_back(image);
  689. }
  690. // rectangle
  691. names.push_back(core::stringw(L"r"));
  692. values.push_back(area);
  693. if (fa.underhang != 0)
  694. {
  695. under = core::stringw(fa.underhang);
  696. names.push_back(core::stringw(L"u"));
  697. values.push_back(under);
  698. }
  699. if (fa.overhang != 0)
  700. {
  701. over = core::stringw(fa.overhang);
  702. names.push_back(core::stringw(L"o"));
  703. values.push_back(over);
  704. }
  705. writer->writeElement(L"c", true, names, values);
  706. writer->writeLineBreak();
  707. it++;
  708. }
  709. writer->writeClosingTag(L"font");
  710. writer->drop();
  711. Device->getLogger()->log("Bitmap font saved.");
  712. return true;
  713. }