CFontTool.cpp 26 KB

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