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