From 959831f4ef5a3e797f576c3de08cd65032c997ad Mon Sep 17 00:00:00 2001 From: David Walter Seikel Date: Sun, 13 Jan 2013 18:54:10 +1000 Subject: Remove damned ancient DOS line endings from Irrlicht. Hopefully I did not go overboard. --- .../tools/IrrFontTool/newFontTool/CFontTool.cpp | 1602 ++++++++++---------- 1 file changed, 801 insertions(+), 801 deletions(-) (limited to 'libraries/irrlicht-1.8/tools/IrrFontTool/newFontTool/CFontTool.cpp') diff --git a/libraries/irrlicht-1.8/tools/IrrFontTool/newFontTool/CFontTool.cpp b/libraries/irrlicht-1.8/tools/IrrFontTool/newFontTool/CFontTool.cpp index 780e9b0..7579d5d 100644 --- a/libraries/irrlicht-1.8/tools/IrrFontTool/newFontTool/CFontTool.cpp +++ b/libraries/irrlicht-1.8/tools/IrrFontTool/newFontTool/CFontTool.cpp @@ -1,801 +1,801 @@ -#include "CFontTool.h" -#include "IXMLWriter.h" - -using namespace irr; - -const int fontsizes[] = {4,6,8,9,10,11,12,14,16,18,20,22,24,26,28,36,48,56,68,72,0}; - -inline u32 getTextureSizeFromSurfaceSize(u32 size) -{ - u32 ts = 0x01; - while(ts < size) - ts <<= 1; - - return ts; -} - -// windows specific -#ifdef _IRR_WINDOWS_ - - const DWORD charsets[] = { ANSI_CHARSET, DEFAULT_CHARSET, OEM_CHARSET, BALTIC_CHARSET, GB2312_CHARSET, CHINESEBIG5_CHARSET, - EASTEUROPE_CHARSET, GREEK_CHARSET, HANGUL_CHARSET, MAC_CHARSET, RUSSIAN_CHARSET, - SHIFTJIS_CHARSET, SYMBOL_CHARSET, TURKISH_CHARSET, VIETNAMESE_CHARSET, JOHAB_CHARSET, - ARABIC_CHARSET, HEBREW_CHARSET, THAI_CHARSET, 0}; - - const wchar_t *setnames[] = {L"ANSI", L"All Available", L"OEM", L"Baltic", L"Chinese Simplified", L"Chinese Traditional", - L"Eastern European", L"Greek", L"Hangul", L"Macintosh", L"Russian", - L"Japanese", L"Symbol", L"Turkish", L"Vietnamese", L"Johab", - L"Arabic", L"Hebrew", L"Thai", 0}; - - // callback for adding font names - int CALLBACK EnumFontFamExProc( ENUMLOGFONTEX *lpelfe, NEWTEXTMETRICEX *lpntme, - DWORD FontType, LPARAM lParam) - { - CFontTool* t = (CFontTool*) lParam; - t->FontNames.push_back( core::stringw(lpelfe->elfFullName)); - return 1; - } - - // - // Constructor - // - - CFontTool::CFontTool(IrrlichtDevice* device) : FontSizes(fontsizes), - Device(device), UseAlphaChannel(false), - // win specific - dc(0) - { - // init display context - dc = CreateDC(L"DISPLAY", L"DISPLAY", 0 ,0 ); - - // populate list of available character set names - for (int i=0; setnames[i] != 0; ++i) - CharSets.push_back( core::stringw(setnames[i])); - - selectCharSet(0); - } - - void CFontTool::selectCharSet(u32 currentCharSet) - { - if ( currentCharSet >= CharSets.size() ) - return; - - LOGFONTW lf; - lf.lfFaceName[0] = L'\0'; - lf.lfCharSet = (BYTE) charsets[currentCharSet]; - // HRESULT hr; // no error checking(!) - - // clear font list - FontNames.clear(); - - // create list of available fonts - EnumFontFamiliesExW( dc, (LPLOGFONTW) &lf, (FONTENUMPROCW) EnumFontFamExProc, (LPARAM) this, 0); - } - - bool CFontTool::makeBitmapFont(u32 fontIndex, u32 charsetIndex, s32 fontSize, u32 textureWidth, u32 textureHeight, bool bold, bool italic, bool aa, bool alpha) - { - if (fontIndex >= FontNames.size() || charsetIndex >= CharSets.size() ) - return false; - - UseAlphaChannel = alpha; - u32 currentImage = 0; - - // create the font - HFONT font = CreateFontW( - -MulDiv(fontSize, GetDeviceCaps(dc, LOGPIXELSY), 72), 0, - 0,0, - bold ? FW_BOLD : 0, - italic, 0,0, charsets[charsetIndex], 0,0, - aa ? ANTIALIASED_QUALITY : 0, - 0, FontNames[fontIndex].c_str() ); - - if (!font) - return false; - - SelectObject(dc, font); - SetTextAlign (dc,TA_LEFT | TA_TOP | TA_NOUPDATECP); - - // get rid of the current textures/images - for (u32 i=0; idrop(); - currentTextures.clear(); - - for (u32 i=0; idrop(); - currentImages.clear(); - - // clear current image mappings - CharMap.clear(); - // clear array - Areas.clear(); - - // get information about this font's unicode ranges. - s32 size = GetFontUnicodeRanges( dc, 0); - c8 *buf = new c8[size]; - LPGLYPHSET glyphs = (LPGLYPHSET)buf; - - GetFontUnicodeRanges( dc, glyphs); - - // s32 TotalCharCount = glyphs->cGlyphsSupported; - - s32 currentx=0, currenty=0, maxy=0; - - for (u32 range=0; range < glyphs->cRanges; range++) - { - WCRANGE* current = &glyphs->ranges[range]; - - //maxy=0; - - // loop through each glyph and write its size and position - for (s32 ch=current->wcLow; ch< current->wcLow + current->cGlyphs; ch++) - { - wchar_t currentchar = ch; - - if ( IsDBCSLeadByte((BYTE) ch)) - continue; // surragate pairs unsupported - - // get the dimensions - SIZE size; - ABC abc; - GetTextExtentPoint32W(dc, ¤tchar, 1, &size); - SFontArea fa; - fa.underhang = 0; - fa.overhang = 0; - - if (GetCharABCWidthsW(dc, currentchar, currentchar, &abc)) // for unicode fonts, get overhang, underhang, width - { - size.cx = abc.abcB; - fa.underhang = abc.abcA; - fa.overhang = abc.abcC; - - if (abc.abcB-abc.abcA+abc.abcC<1) - continue; // nothing of width 0 - } - if (size.cy < 1) - continue; - - //GetGlyphOutline(dc, currentchar, GGO_METRICS, &gm, 0, 0, 0); - - //size.cx++; size.cy++; - - // wrap around? - if (currentx + size.cx > (s32) textureWidth) - { - currenty += maxy; - currentx = 0; - if ((u32)(currenty + maxy) > textureHeight) - { - currentImage++; // increase Image count - currenty=0; - } - maxy = 0; - } - // add this char dimension to the current map - - fa.rectangle = core::rect(currentx, currenty, currentx + size.cx, currenty + size.cy); - fa.sourceimage = currentImage; - - CharMap.insert(currentchar, Areas.size()); - Areas.push_back( fa ); - - currentx += size.cx +1; - - if (size.cy+1 > maxy) - maxy = size.cy+1; - } - } - currenty += maxy; - - u32 lastTextureHeight = getTextureSizeFromSurfaceSize(currenty); - - // delete the glyph set - delete [] buf; - - currentImages.set_used(currentImage+1); - currentTextures.set_used(currentImage+1); - - for (currentImage=0; currentImage < currentImages.size(); ++currentImage) - { - core::stringc logmsg = "Creating image "; - logmsg += (s32) (currentImage+1); - logmsg += " of "; - logmsg += (s32) currentImages.size(); - Device->getLogger()->log(logmsg.c_str()); - // no need for a huge final texture - u32 texHeight = textureHeight; - if (currentImage == currentImages.size()-1 ) - texHeight = lastTextureHeight; - - // make a new bitmap - HBITMAP bmp = CreateCompatibleBitmap(dc, textureWidth, texHeight); - HDC bmpdc = CreateCompatibleDC(dc); - - LOGBRUSH lbrush; - lbrush.lbColor = RGB(0,0,0); - lbrush.lbHatch = 0; - lbrush.lbStyle = BS_SOLID; - - HBRUSH brush = CreateBrushIndirect(&lbrush); - HPEN pen = CreatePen(PS_NULL, 0, 0); - - HGDIOBJ oldbmp = SelectObject(bmpdc, bmp); - HGDIOBJ oldbmppen = SelectObject(bmpdc, pen); - HGDIOBJ oldbmpbrush = SelectObject(bmpdc, brush); - HGDIOBJ oldbmpfont = SelectObject(bmpdc, font); - - SetTextColor(bmpdc, RGB(255,255,255)); - - Rectangle(bmpdc, 0,0,textureWidth,texHeight); - SetBkMode(bmpdc, TRANSPARENT); - - // draw the letters... - - // iterate through the tree - core::map::Iterator it = CharMap.getIterator(); - while (!it.atEnd()) - { - s32 currentArea = (*it).getValue(); - wchar_t wch = (*it).getKey(); - // sloppy but I couldnt be bothered rewriting it - if (Areas[currentArea].sourceimage == currentImage) - { - // draw letter - s32 sx = Areas[currentArea].rectangle.UpperLeftCorner.X - Areas[currentArea].underhang; - TextOutW(bmpdc, sx, Areas[currentArea].rectangle.UpperLeftCorner.Y, &wch, 1); - - // if ascii font... - //SetPixel(bmpdc, Areas[currentArea].rectangle.UpperLeftCorner.X, Areas[currentArea].rectangle.UpperLeftCorner.Y, RGB(255,255,0));// left upper corner mark - } - it++; - } - - // copy the font bitmap into a new irrlicht image - BITMAP b; - PBITMAPINFO pbmi; - WORD cClrBits; - u32 cformat; - - // Retrieve the bitmap color format, width, and height. - GetObject(bmp, sizeof(BITMAP), (LPSTR)&b); - - // Convert the color format to a count of bits. - cClrBits = (WORD)(b.bmPlanes * b.bmBitsPixel); - - if (cClrBits <= 8) // we're not supporting these - cformat = -1; - else if (cClrBits <= 16) - cformat = video::ECF_A1R5G5B5; - else if (cClrBits <= 24) - cformat = video::ECF_R8G8B8; - else - cformat = video::ECF_A8R8G8B8; - - pbmi = (PBITMAPINFO) LocalAlloc(LPTR, - sizeof(BITMAPINFOHEADER)); - - // Initialize the fields in the BITMAPINFO structure. - - pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); - pbmi->bmiHeader.biWidth = b.bmWidth; - pbmi->bmiHeader.biHeight = b.bmHeight; - pbmi->bmiHeader.biPlanes = b.bmPlanes; - pbmi->bmiHeader.biBitCount = b.bmBitsPixel; - - // If the bitmap is not compressed, set the BI_RGB flag. - pbmi->bmiHeader.biCompression = BI_RGB; - - // Compute the number of bytes in the array of color - // indices and store the result in biSizeImage. - // For Windows NT, the width must be DWORD aligned unless - // the bitmap is RLE compressed. This example shows this. - // For Windows 95/98/Me, the width must be WORD aligned unless the - // bitmap is RLE compressed. - pbmi->bmiHeader.biSizeImage = ((pbmi->bmiHeader.biWidth * cClrBits +31) & ~31) /8 - * pbmi->bmiHeader.biHeight; - // Set biClrImportant to 0, indicating that all of the - // device colors are important. - pbmi->bmiHeader.biClrImportant = 0; - - LPBYTE lpBits; // memory pointer - - PBITMAPINFOHEADER pbih = (PBITMAPINFOHEADER) pbmi; - lpBits = (LPBYTE) GlobalAlloc(GMEM_FIXED, pbih->biSizeImage); - - GetDIBits(dc, bmp, 0, (WORD) pbih->biHeight, lpBits, pbmi, DIB_RGB_COLORS); - - // DEBUG- copy to clipboard - //OpenClipboard(hWnd); - //EmptyClipboard(); - //SetClipboardData(CF_BITMAP, bmp); - //CloseClipboard(); - - // flip bitmap - s32 rowsize = ((pbmi->bmiHeader.biWidth * cClrBits +31) & ~31) /8; - c8 *row = new c8[rowsize]; - for (s32 i=0; i < (pbih->biHeight/2); ++i) - { - // grab a row - memcpy(row, lpBits + (rowsize * i), rowsize); - // swap row - memcpy(lpBits + (rowsize * i), lpBits + ((pbih->biHeight-1 -i) * rowsize ) , rowsize); - memcpy(lpBits + ((pbih->biHeight-1 -i) * rowsize ), row , rowsize); - } - - bool ret = false; - - if (cformat == video::ECF_A8R8G8B8) - { - // in this case the font should have an alpha channel, but since windows doesn't draw one - // we have to set one manually by going through all the pixels.. *sigh* - - u8* m; - for (m = lpBits; m < lpBits + pbih->biSizeImage; m+=4) - { - if (UseAlphaChannel) - { - if (m[0] > 0) // pixel has colour - { - m[3]=m[0]; // set alpha - m[0]=m[1]=m[2] = 255; // everything else is full - } - } - else - m[3]=255; // all pixels are full alpha - } - - } - else if (cformat == video::ECF_A1R5G5B5) - { - u8* m; - for (m = lpBits; m < lpBits + pbih->biSizeImage; m+=2) - { - WORD *p = (WORD*)m; - if (m[0] > 0 || !UseAlphaChannel) // alpha should be set - *p |= 0x8000; // set alpha bit - } - } - else - { - cformat = -1; - } - - // make a texture from the image - if (cformat != -1) - { - // turn mip-mapping off - bool b = Device->getVideoDriver()->getTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS); - currentImages[currentImage] = Device->getVideoDriver()->createImageFromData((video::ECOLOR_FORMAT)cformat, core::dimension2d(textureWidth,texHeight), (void*)lpBits); - Device->getVideoDriver()->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS,b); - } - else - { - Device->getLogger()->log("Couldn't create font, your pixel format is unsupported."); - } - - // free memory and windows resources - // sloppy I know, but I only intended to create one image at first. - delete [] row; - LocalFree(pbmi); - GlobalFree(lpBits); - DeleteDC(bmpdc); - DeleteObject(brush); - DeleteObject(pen); - DeleteObject(bmp); - - if (currentImages[currentImage]) - { - // add texture - currentTextures[currentImage] = Device->getVideoDriver()->addTexture("GUIFontImage",currentImages[currentImage]); - currentTextures[currentImage]->grab(); - } - else - { - Device->getLogger()->log("Something went wrong, aborting."); - // drop all images - DeleteObject(font); - return false; - } - } // looping through each texture - DeleteObject(font); - return true; - } - -#else - - CFontTool::CFontTool(IrrlichtDevice *device) : FontSizes(fontsizes), Device(device), UseAlphaChannel(false) - { - if (!XftInitFtLibrary()) - { - core::stringc logmsg = "XFT not found\n"; - Device->getLogger()->log(logmsg.c_str()); - exit(EXIT_FAILURE); - } - - /* Get a list of the font foundries, storing them in a set to sort */ - std::set foundries; - Display* display = (Display*)Device->getVideoDriver()->getExposedVideoData().OpenGLLinux.X11Display; - XftFontSet* fonts = XftListFonts(display, DefaultScreen(display), 0, XFT_FOUNDRY, 0); - for (int i = 0; i < fonts->nfont; i++) - { - char *foundry; - XftPatternGetString(fonts->fonts[i], XFT_FOUNDRY, 0, &foundry); - core::stringw tmp(foundry); - foundries.insert(tmp); - } - XftFontSetDestroy(fonts); - - /* Copy the sorted list into the array */ - CharSets.clear(); - for (std::set::iterator i = foundries.begin(); i != foundries.end(); i++) - CharSets.push_back((*i).c_str()); - selectCharSet(0); - } - - /* Note: There must be some trick for using strings as pattern parameters to XftListFonts because - no matter how I specify a string, I end up with an intermittent segfault. Since XftFontList is - just calling FcFontList, that's what I'll do too since that works OK */ - void CFontTool::selectCharSet(u32 currentCharSet) - { - /* Get a list of the font families, storing them in a set to sort */ - char foundry[256]; - sprintf(&foundry[0],"%ls",CharSets[currentCharSet].c_str()); - std::set families; - XftPattern *pattern = FcPatternCreate(); - XftPatternAddString(pattern, FC_FOUNDRY, &foundry[0]); - XftObjectSet *objectset = FcObjectSetCreate(); - XftObjectSetAdd(objectset, XFT_FOUNDRY); - XftObjectSetAdd(objectset, XFT_FAMILY); - FcFontSet *fonts = FcFontList(NULL, pattern, objectset); - - for (int i = 0; i < fonts->nfont; i++) - { - char* ptr; - XftPatternGetString(fonts->fonts[i], XFT_FAMILY, 0, &ptr); - core::stringw family(ptr); - families.insert(family); - } - XftPatternDestroy(pattern); - FcObjectSetDestroy(objectset); - - /* Copy the sorted list into the array */ - FontNames.clear(); - for (std::set::iterator i = families.begin(); i != families.end(); i++) - FontNames.push_back((*i).c_str()); - } - - bool CFontTool::makeBitmapFont(u32 fontIndex, u32 charsetIndex, s32 fontSize, u32 textureWidth, u32 textureHeight, bool bold, bool italic, bool aa, bool alpha) - { - if (fontIndex >= FontNames.size() || charsetIndex >= CharSets.size() ) - return false; - - Display *display = (Display*) Device->getVideoDriver()->getExposedVideoData().OpenGLLinux.X11Display; - u32 screen = DefaultScreen(display); - Window win = RootWindow(display, screen); - Visual *visual = DefaultVisual(display, screen); - UseAlphaChannel = alpha; - u32 currentImage = 0; - - XftResult result; - XftPattern *request = XftPatternCreate(); - char foundry[256], family[256]; - sprintf(&foundry[0],"%ls",CharSets[charsetIndex].c_str()); - sprintf(&family[0],"%ls",FontNames[fontIndex].c_str()); - XftPatternAddString(request, XFT_FOUNDRY, &foundry[0]); - XftPatternAddString(request, XFT_FAMILY, &family[0]); - XftPatternAddInteger(request, XFT_PIXEL_SIZE, fontSize); - XftPatternAddInteger(request, XFT_WEIGHT, bold ? XFT_WEIGHT_BLACK : XFT_WEIGHT_LIGHT); - XftPatternAddInteger(request, XFT_SLANT, italic ? XFT_SLANT_ITALIC : XFT_SLANT_ROMAN); - XftPatternAddBool(request, XFT_ANTIALIAS, aa); - - /* Find the closest font that matches the user choices and open it and check if the returned - font has anti aliasing enabled by default, even if it wasn't requested */ - FcBool aaEnabled; - XftPattern *found = XftFontMatch(display, DefaultScreen(display), request, &result); - XftPatternGetBool(found, XFT_ANTIALIAS, 0, &aaEnabled); - aa = aaEnabled; - XftFont *font = XftFontOpenPattern(display, found); - - // get rid of the current textures/images - for (u32 i=0; idrop(); - currentTextures.clear(); - for (u32 i=0; idrop(); - currentImages.clear(); - CharMap.clear(); - Areas.clear(); - - /* Calculate the max height of the font. Annoyingly, it seems that the height property of the font - is the maximum height of any single character, but a string of characters, aligned along their - baselines, can exceed this figure. Because I don't know any better way of doing it, I'm going to - have to use the brute force method. - - Note: There will be a certain number of charters in a font, however they may not be grouped - consecutively, and could in fact be spread out with many gaps */ - u32 maxY = 0; - u32 charsFound = 0; - for (FT_UInt charCode = 0; charsFound < FcCharSetCount(font->charset); charCode++) - { - if (!XftCharExists(display, font, charCode)) - continue; - - charsFound++; - - XGlyphInfo extents; - XftTextExtents32(display, font, &charCode, 1, &extents); - if ((extents.xOff <= 0) && (extents.height <= 0)) - continue; - - /* Calculate the width and height, adding 1 extra pixel if anti aliasing is enabled */ - u32 chWidth = extents.xOff + (aa ? 1 : 0); - u32 chHeight = (font->ascent - extents.y + extents.height) + (aa ? 1 : 0); - if (chHeight > maxY) - maxY = chHeight; - - /* Store the character details here */ - SFontArea fontArea; - fontArea.rectangle = core::rect(0, 0, chWidth, chHeight); - CharMap.insert(charCode, Areas.size()); - Areas.push_back(fontArea); - } - core::stringc logmsg = "Found "; - logmsg += (s32) (CharMap.size() + 1); - logmsg += " characters"; - Device->getLogger()->log(logmsg.c_str()); - - /* Get the size of the chars and allocate them a position on a texture. If the next character that - is added would be outside the width or height of the texture, then a new texture is added */ - u32 currentX = 0, currentY = 0, rowY = 0; - for (core::map::Iterator it = CharMap.getIterator(); !it.atEnd(); it++) - { - s32 currentArea = (*it).getValue(); - SFontArea *fontArea = &Areas[currentArea]; - u32 chWidth = fontArea->rectangle.LowerRightCorner.X; - u32 chHeight = fontArea->rectangle.LowerRightCorner.Y; - - /* If the width of this char will exceed the textureWidth then start a new row */ - if ((currentX + chWidth) > textureWidth) - { - currentY += rowY; - currentX = 0; - - /* If the new row added to the texture exceeds the textureHeight then start a new texture */ - if ((currentY + rowY) > textureHeight) - { - currentImage++; - currentY = 0; - } - rowY = 0; - } - - /* Update the area with the current x and y and texture */ - fontArea->rectangle = core::rect(currentX, currentY, currentX + chWidth, currentY + chHeight); - fontArea->sourceimage = currentImage; - currentX += chWidth + 1; - if (chHeight + 1 > rowY) - rowY = chHeight + 1; - } - - /* The last row of chars and the last texture weren't accounted for in the loop, so add them here */ - currentY += rowY; - u32 lastTextureHeight = getTextureSizeFromSurfaceSize(currentY); - currentImages.set_used(currentImage + 1); - currentTextures.set_used(currentImage + 1); - - /* Initialise colours */ - XftColor colFore, colBack; - XRenderColor xFore = {0xffff, 0xffff, 0xffff, 0xffff}; - XRenderColor xBack = {0x0000, 0x0000, 0x0000, 0xffff}; - XftColorAllocValue(display, DefaultVisual(display, screen), DefaultColormap(display, screen), &xFore, &colFore); - XftColorAllocValue(display, DefaultVisual(display, screen), DefaultColormap(display, screen), &xBack, &colBack); - - /* Create a pixmap that is large enough to hold any character in the font */ - Pixmap pixmap = XCreatePixmap(display, win, textureWidth, maxY, DefaultDepth(display, screen)); - XftDraw *draw = XftDrawCreate(display, pixmap, visual, DefaultColormap(display, screen)); - - /* Render the chars */ - for (currentImage = 0; currentImage < currentImages.size(); ++currentImage) - { - core::stringc logmsg = "Creating image "; - logmsg += (s32) (currentImage+1); - logmsg += " of "; - logmsg += (s32) currentImages.size(); - Device->getLogger()->log(logmsg.c_str()); - - /* The last texture that is saved is vertically shrunk to fit the characters drawn on it */ - u32 texHeight = textureHeight; - if (currentImage == currentImages.size() - 1) - texHeight = lastTextureHeight; - - /* The texture that holds this "page" of characters */ - currentImages[currentImage] = Device->getVideoDriver()->createImage(video::ECF_A8R8G8B8, core::dimension2du(textureWidth, texHeight)); - currentImages[currentImage]->fill(video::SColor(alpha ? 0 : 255,0,0,0)); - - for (core::map::Iterator it = CharMap.getIterator(); !it.atEnd(); it++) - { - FcChar32 wch = (*it).getKey(); - s32 currentArea = (*it).getValue(); - if (Areas[currentArea].sourceimage == currentImage) - { - SFontArea *fontArea = &Areas[currentArea]; - u32 chWidth = fontArea->rectangle.LowerRightCorner.X - fontArea->rectangle.UpperLeftCorner.X; - u32 chHeight = fontArea->rectangle.LowerRightCorner.Y - fontArea->rectangle.UpperLeftCorner.Y; - - /* Draw the glyph onto the pixmap */ - XGlyphInfo extents; - XftDrawRect(draw, &colBack, 0, 0, chWidth, chHeight); - XftTextExtents32(display, font, &wch, 1, &extents); - XftDrawString32(draw, &colFore, font, extents.x, extents.y, &wch, 1); - - /* Convert the pixmap into an image, then copy it onto the Irrlicht texture, pixel by pixel. - There's bound to be a faster way, but this is adequate */ - u32 xDest = fontArea->rectangle.UpperLeftCorner.X; - u32 yDest = fontArea->rectangle.UpperLeftCorner.Y + font->ascent - extents.y; - XImage *image = XGetImage(display, pixmap, 0, 0, chWidth, chHeight, 0xffffff, XYPixmap); - if (image) - { - for (u32 ySrc = 0; ySrc < chHeight; ySrc++) - for (u32 xSrc = 0; xSrc < chWidth; xSrc++) - { - /* Get the pixel colour and break it down into rgb components */ - u32 col = XGetPixel(image, xSrc, ySrc); - u32 a = 255; - u32 r = col & visual->red_mask; - u32 g = col & visual->green_mask; - u32 b = col & visual->blue_mask; - while (r > 0xff) r >>= 8; - while (g > 0xff) g >>= 8; - while (b > 0xff) b >>= 8; - - /* To make the background transparent, set the colour to 100% white and the alpha to - the average of the three rgb colour components to maintain the anti-aliasing */ - if (alpha) - { - a = (r + g + b) / 3; - r = 255; - g = 255; - b = 255; - } - currentImages[currentImage]->setPixel(xDest + xSrc,yDest + ySrc,video::SColor(a,r,g,b)); - } - image->f.destroy_image(image); - } - } - } - - /* Add the texture to the list */ - currentTextures[currentImage] = Device->getVideoDriver()->addTexture("GUIFontImage",currentImages[currentImage]); - currentTextures[currentImage]->grab(); - } - - XftColorFree (display, visual, DefaultColormap(display, screen), &colFore); - XftColorFree (display, visual, DefaultColormap(display, screen), &colBack); - XftFontClose(display,font); - XftPatternDestroy(request); - XftDrawDestroy(draw); - XFreePixmap(display, pixmap); - return true; - } -#endif - - CFontTool::~CFontTool() - { -#ifdef _IRR_WINDOWS_ - // destroy display context - if (dc) - DeleteDC(dc); -#endif - - // drop textures+images - for (u32 i=0; idrop(); - currentTextures.clear(); - - for (u32 i=0; idrop(); - currentImages.clear(); - } - -bool CFontTool::saveBitmapFont(const c8 *filename, const c8* format) -{ - if (currentImages.size() == 0) - { - Device->getLogger()->log("No image data to write, aborting."); - return false; - } - - core::stringc fn = filename; - core::stringc imagename = filename; - fn += ".xml"; - - io::IXMLWriter *writer = Device->getFileSystem()->createXMLWriter(fn.c_str()); - - // header and line breaks - writer->writeXMLHeader(); - writer->writeLineBreak(); - - // write information - writer->writeElement(L"font", false, L"type", L"bitmap"); - writer->writeLineBreak(); - writer->writeLineBreak(); - - // write images and link to them - for (u32 i=0; igetVideoDriver()->writeImageToFile(currentImages[i],imagename.c_str()); - - writer->writeElement(L"Texture", true, - L"index", core::stringw(i).c_str(), - L"filename", core::stringw(imagename.c_str()).c_str(), - L"hasAlpha", UseAlphaChannel ? L"true" : L"false"); - writer->writeLineBreak(); - } - - writer->writeLineBreak(); - - // write each character - core::map::Iterator it = CharMap.getIterator(); - while (!it.atEnd()) - { - SFontArea &fa = Areas[(*it).getValue()]; - - wchar_t c[2]; - c[0] = (*it).getKey(); - c[1] = L'\0'; - core::stringw area, under, over, image; - area = core::stringw(fa.rectangle.UpperLeftCorner.X); - area += L", "; - area += fa.rectangle.UpperLeftCorner.Y; - area += L", "; - area += fa.rectangle.LowerRightCorner.X; - area += L", "; - area += fa.rectangle.LowerRightCorner.Y; - - core::array names; - core::array values; - names.clear(); - values.clear(); - // char - names.push_back(core::stringw(L"c")); - values.push_back(core::stringw(c)); - // image number - if (fa.sourceimage != 0) - { - image = core::stringw(fa.sourceimage); - names.push_back(core::stringw(L"i")); - values.push_back(image); - } - // rectangle - names.push_back(core::stringw(L"r")); - values.push_back(area); - - if (fa.underhang != 0) - { - under = core::stringw(fa.underhang); - names.push_back(core::stringw(L"u")); - values.push_back(under); - } - if (fa.overhang != 0) - { - over = core::stringw(fa.overhang); - names.push_back(core::stringw(L"o")); - values.push_back(over); - } - writer->writeElement(L"c", true, names, values); - - writer->writeLineBreak(); - it++; - } - - writer->writeClosingTag(L"font"); - - writer->drop(); - - Device->getLogger()->log("Bitmap font saved."); - - return true; -} +#include "CFontTool.h" +#include "IXMLWriter.h" + +using namespace irr; + +const int fontsizes[] = {4,6,8,9,10,11,12,14,16,18,20,22,24,26,28,36,48,56,68,72,0}; + +inline u32 getTextureSizeFromSurfaceSize(u32 size) +{ + u32 ts = 0x01; + while(ts < size) + ts <<= 1; + + return ts; +} + +// windows specific +#ifdef _IRR_WINDOWS_ + + const DWORD charsets[] = { ANSI_CHARSET, DEFAULT_CHARSET, OEM_CHARSET, BALTIC_CHARSET, GB2312_CHARSET, CHINESEBIG5_CHARSET, + EASTEUROPE_CHARSET, GREEK_CHARSET, HANGUL_CHARSET, MAC_CHARSET, RUSSIAN_CHARSET, + SHIFTJIS_CHARSET, SYMBOL_CHARSET, TURKISH_CHARSET, VIETNAMESE_CHARSET, JOHAB_CHARSET, + ARABIC_CHARSET, HEBREW_CHARSET, THAI_CHARSET, 0}; + + const wchar_t *setnames[] = {L"ANSI", L"All Available", L"OEM", L"Baltic", L"Chinese Simplified", L"Chinese Traditional", + L"Eastern European", L"Greek", L"Hangul", L"Macintosh", L"Russian", + L"Japanese", L"Symbol", L"Turkish", L"Vietnamese", L"Johab", + L"Arabic", L"Hebrew", L"Thai", 0}; + + // callback for adding font names + int CALLBACK EnumFontFamExProc( ENUMLOGFONTEX *lpelfe, NEWTEXTMETRICEX *lpntme, + DWORD FontType, LPARAM lParam) + { + CFontTool* t = (CFontTool*) lParam; + t->FontNames.push_back( core::stringw(lpelfe->elfFullName)); + return 1; + } + + // + // Constructor + // + + CFontTool::CFontTool(IrrlichtDevice* device) : FontSizes(fontsizes), + Device(device), UseAlphaChannel(false), + // win specific + dc(0) + { + // init display context + dc = CreateDC(L"DISPLAY", L"DISPLAY", 0 ,0 ); + + // populate list of available character set names + for (int i=0; setnames[i] != 0; ++i) + CharSets.push_back( core::stringw(setnames[i])); + + selectCharSet(0); + } + + void CFontTool::selectCharSet(u32 currentCharSet) + { + if ( currentCharSet >= CharSets.size() ) + return; + + LOGFONTW lf; + lf.lfFaceName[0] = L'\0'; + lf.lfCharSet = (BYTE) charsets[currentCharSet]; + // HRESULT hr; // no error checking(!) + + // clear font list + FontNames.clear(); + + // create list of available fonts + EnumFontFamiliesExW( dc, (LPLOGFONTW) &lf, (FONTENUMPROCW) EnumFontFamExProc, (LPARAM) this, 0); + } + + bool CFontTool::makeBitmapFont(u32 fontIndex, u32 charsetIndex, s32 fontSize, u32 textureWidth, u32 textureHeight, bool bold, bool italic, bool aa, bool alpha) + { + if (fontIndex >= FontNames.size() || charsetIndex >= CharSets.size() ) + return false; + + UseAlphaChannel = alpha; + u32 currentImage = 0; + + // create the font + HFONT font = CreateFontW( + -MulDiv(fontSize, GetDeviceCaps(dc, LOGPIXELSY), 72), 0, + 0,0, + bold ? FW_BOLD : 0, + italic, 0,0, charsets[charsetIndex], 0,0, + aa ? ANTIALIASED_QUALITY : 0, + 0, FontNames[fontIndex].c_str() ); + + if (!font) + return false; + + SelectObject(dc, font); + SetTextAlign (dc,TA_LEFT | TA_TOP | TA_NOUPDATECP); + + // get rid of the current textures/images + for (u32 i=0; idrop(); + currentTextures.clear(); + + for (u32 i=0; idrop(); + currentImages.clear(); + + // clear current image mappings + CharMap.clear(); + // clear array + Areas.clear(); + + // get information about this font's unicode ranges. + s32 size = GetFontUnicodeRanges( dc, 0); + c8 *buf = new c8[size]; + LPGLYPHSET glyphs = (LPGLYPHSET)buf; + + GetFontUnicodeRanges( dc, glyphs); + + // s32 TotalCharCount = glyphs->cGlyphsSupported; + + s32 currentx=0, currenty=0, maxy=0; + + for (u32 range=0; range < glyphs->cRanges; range++) + { + WCRANGE* current = &glyphs->ranges[range]; + + //maxy=0; + + // loop through each glyph and write its size and position + for (s32 ch=current->wcLow; ch< current->wcLow + current->cGlyphs; ch++) + { + wchar_t currentchar = ch; + + if ( IsDBCSLeadByte((BYTE) ch)) + continue; // surragate pairs unsupported + + // get the dimensions + SIZE size; + ABC abc; + GetTextExtentPoint32W(dc, ¤tchar, 1, &size); + SFontArea fa; + fa.underhang = 0; + fa.overhang = 0; + + if (GetCharABCWidthsW(dc, currentchar, currentchar, &abc)) // for unicode fonts, get overhang, underhang, width + { + size.cx = abc.abcB; + fa.underhang = abc.abcA; + fa.overhang = abc.abcC; + + if (abc.abcB-abc.abcA+abc.abcC<1) + continue; // nothing of width 0 + } + if (size.cy < 1) + continue; + + //GetGlyphOutline(dc, currentchar, GGO_METRICS, &gm, 0, 0, 0); + + //size.cx++; size.cy++; + + // wrap around? + if (currentx + size.cx > (s32) textureWidth) + { + currenty += maxy; + currentx = 0; + if ((u32)(currenty + maxy) > textureHeight) + { + currentImage++; // increase Image count + currenty=0; + } + maxy = 0; + } + // add this char dimension to the current map + + fa.rectangle = core::rect(currentx, currenty, currentx + size.cx, currenty + size.cy); + fa.sourceimage = currentImage; + + CharMap.insert(currentchar, Areas.size()); + Areas.push_back( fa ); + + currentx += size.cx +1; + + if (size.cy+1 > maxy) + maxy = size.cy+1; + } + } + currenty += maxy; + + u32 lastTextureHeight = getTextureSizeFromSurfaceSize(currenty); + + // delete the glyph set + delete [] buf; + + currentImages.set_used(currentImage+1); + currentTextures.set_used(currentImage+1); + + for (currentImage=0; currentImage < currentImages.size(); ++currentImage) + { + core::stringc logmsg = "Creating image "; + logmsg += (s32) (currentImage+1); + logmsg += " of "; + logmsg += (s32) currentImages.size(); + Device->getLogger()->log(logmsg.c_str()); + // no need for a huge final texture + u32 texHeight = textureHeight; + if (currentImage == currentImages.size()-1 ) + texHeight = lastTextureHeight; + + // make a new bitmap + HBITMAP bmp = CreateCompatibleBitmap(dc, textureWidth, texHeight); + HDC bmpdc = CreateCompatibleDC(dc); + + LOGBRUSH lbrush; + lbrush.lbColor = RGB(0,0,0); + lbrush.lbHatch = 0; + lbrush.lbStyle = BS_SOLID; + + HBRUSH brush = CreateBrushIndirect(&lbrush); + HPEN pen = CreatePen(PS_NULL, 0, 0); + + HGDIOBJ oldbmp = SelectObject(bmpdc, bmp); + HGDIOBJ oldbmppen = SelectObject(bmpdc, pen); + HGDIOBJ oldbmpbrush = SelectObject(bmpdc, brush); + HGDIOBJ oldbmpfont = SelectObject(bmpdc, font); + + SetTextColor(bmpdc, RGB(255,255,255)); + + Rectangle(bmpdc, 0,0,textureWidth,texHeight); + SetBkMode(bmpdc, TRANSPARENT); + + // draw the letters... + + // iterate through the tree + core::map::Iterator it = CharMap.getIterator(); + while (!it.atEnd()) + { + s32 currentArea = (*it).getValue(); + wchar_t wch = (*it).getKey(); + // sloppy but I couldnt be bothered rewriting it + if (Areas[currentArea].sourceimage == currentImage) + { + // draw letter + s32 sx = Areas[currentArea].rectangle.UpperLeftCorner.X - Areas[currentArea].underhang; + TextOutW(bmpdc, sx, Areas[currentArea].rectangle.UpperLeftCorner.Y, &wch, 1); + + // if ascii font... + //SetPixel(bmpdc, Areas[currentArea].rectangle.UpperLeftCorner.X, Areas[currentArea].rectangle.UpperLeftCorner.Y, RGB(255,255,0));// left upper corner mark + } + it++; + } + + // copy the font bitmap into a new irrlicht image + BITMAP b; + PBITMAPINFO pbmi; + WORD cClrBits; + u32 cformat; + + // Retrieve the bitmap color format, width, and height. + GetObject(bmp, sizeof(BITMAP), (LPSTR)&b); + + // Convert the color format to a count of bits. + cClrBits = (WORD)(b.bmPlanes * b.bmBitsPixel); + + if (cClrBits <= 8) // we're not supporting these + cformat = -1; + else if (cClrBits <= 16) + cformat = video::ECF_A1R5G5B5; + else if (cClrBits <= 24) + cformat = video::ECF_R8G8B8; + else + cformat = video::ECF_A8R8G8B8; + + pbmi = (PBITMAPINFO) LocalAlloc(LPTR, + sizeof(BITMAPINFOHEADER)); + + // Initialize the fields in the BITMAPINFO structure. + + pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + pbmi->bmiHeader.biWidth = b.bmWidth; + pbmi->bmiHeader.biHeight = b.bmHeight; + pbmi->bmiHeader.biPlanes = b.bmPlanes; + pbmi->bmiHeader.biBitCount = b.bmBitsPixel; + + // If the bitmap is not compressed, set the BI_RGB flag. + pbmi->bmiHeader.biCompression = BI_RGB; + + // Compute the number of bytes in the array of color + // indices and store the result in biSizeImage. + // For Windows NT, the width must be DWORD aligned unless + // the bitmap is RLE compressed. This example shows this. + // For Windows 95/98/Me, the width must be WORD aligned unless the + // bitmap is RLE compressed. + pbmi->bmiHeader.biSizeImage = ((pbmi->bmiHeader.biWidth * cClrBits +31) & ~31) /8 + * pbmi->bmiHeader.biHeight; + // Set biClrImportant to 0, indicating that all of the + // device colors are important. + pbmi->bmiHeader.biClrImportant = 0; + + LPBYTE lpBits; // memory pointer + + PBITMAPINFOHEADER pbih = (PBITMAPINFOHEADER) pbmi; + lpBits = (LPBYTE) GlobalAlloc(GMEM_FIXED, pbih->biSizeImage); + + GetDIBits(dc, bmp, 0, (WORD) pbih->biHeight, lpBits, pbmi, DIB_RGB_COLORS); + + // DEBUG- copy to clipboard + //OpenClipboard(hWnd); + //EmptyClipboard(); + //SetClipboardData(CF_BITMAP, bmp); + //CloseClipboard(); + + // flip bitmap + s32 rowsize = ((pbmi->bmiHeader.biWidth * cClrBits +31) & ~31) /8; + c8 *row = new c8[rowsize]; + for (s32 i=0; i < (pbih->biHeight/2); ++i) + { + // grab a row + memcpy(row, lpBits + (rowsize * i), rowsize); + // swap row + memcpy(lpBits + (rowsize * i), lpBits + ((pbih->biHeight-1 -i) * rowsize ) , rowsize); + memcpy(lpBits + ((pbih->biHeight-1 -i) * rowsize ), row , rowsize); + } + + bool ret = false; + + if (cformat == video::ECF_A8R8G8B8) + { + // in this case the font should have an alpha channel, but since windows doesn't draw one + // we have to set one manually by going through all the pixels.. *sigh* + + u8* m; + for (m = lpBits; m < lpBits + pbih->biSizeImage; m+=4) + { + if (UseAlphaChannel) + { + if (m[0] > 0) // pixel has colour + { + m[3]=m[0]; // set alpha + m[0]=m[1]=m[2] = 255; // everything else is full + } + } + else + m[3]=255; // all pixels are full alpha + } + + } + else if (cformat == video::ECF_A1R5G5B5) + { + u8* m; + for (m = lpBits; m < lpBits + pbih->biSizeImage; m+=2) + { + WORD *p = (WORD*)m; + if (m[0] > 0 || !UseAlphaChannel) // alpha should be set + *p |= 0x8000; // set alpha bit + } + } + else + { + cformat = -1; + } + + // make a texture from the image + if (cformat != -1) + { + // turn mip-mapping off + bool b = Device->getVideoDriver()->getTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS); + currentImages[currentImage] = Device->getVideoDriver()->createImageFromData((video::ECOLOR_FORMAT)cformat, core::dimension2d(textureWidth,texHeight), (void*)lpBits); + Device->getVideoDriver()->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS,b); + } + else + { + Device->getLogger()->log("Couldn't create font, your pixel format is unsupported."); + } + + // free memory and windows resources + // sloppy I know, but I only intended to create one image at first. + delete [] row; + LocalFree(pbmi); + GlobalFree(lpBits); + DeleteDC(bmpdc); + DeleteObject(brush); + DeleteObject(pen); + DeleteObject(bmp); + + if (currentImages[currentImage]) + { + // add texture + currentTextures[currentImage] = Device->getVideoDriver()->addTexture("GUIFontImage",currentImages[currentImage]); + currentTextures[currentImage]->grab(); + } + else + { + Device->getLogger()->log("Something went wrong, aborting."); + // drop all images + DeleteObject(font); + return false; + } + } // looping through each texture + DeleteObject(font); + return true; + } + +#else + + CFontTool::CFontTool(IrrlichtDevice *device) : FontSizes(fontsizes), Device(device), UseAlphaChannel(false) + { + if (!XftInitFtLibrary()) + { + core::stringc logmsg = "XFT not found\n"; + Device->getLogger()->log(logmsg.c_str()); + exit(EXIT_FAILURE); + } + + /* Get a list of the font foundries, storing them in a set to sort */ + std::set foundries; + Display* display = (Display*)Device->getVideoDriver()->getExposedVideoData().OpenGLLinux.X11Display; + XftFontSet* fonts = XftListFonts(display, DefaultScreen(display), 0, XFT_FOUNDRY, 0); + for (int i = 0; i < fonts->nfont; i++) + { + char *foundry; + XftPatternGetString(fonts->fonts[i], XFT_FOUNDRY, 0, &foundry); + core::stringw tmp(foundry); + foundries.insert(tmp); + } + XftFontSetDestroy(fonts); + + /* Copy the sorted list into the array */ + CharSets.clear(); + for (std::set::iterator i = foundries.begin(); i != foundries.end(); i++) + CharSets.push_back((*i).c_str()); + selectCharSet(0); + } + + /* Note: There must be some trick for using strings as pattern parameters to XftListFonts because + no matter how I specify a string, I end up with an intermittent segfault. Since XftFontList is + just calling FcFontList, that's what I'll do too since that works OK */ + void CFontTool::selectCharSet(u32 currentCharSet) + { + /* Get a list of the font families, storing them in a set to sort */ + char foundry[256]; + sprintf(&foundry[0],"%ls",CharSets[currentCharSet].c_str()); + std::set families; + XftPattern *pattern = FcPatternCreate(); + XftPatternAddString(pattern, FC_FOUNDRY, &foundry[0]); + XftObjectSet *objectset = FcObjectSetCreate(); + XftObjectSetAdd(objectset, XFT_FOUNDRY); + XftObjectSetAdd(objectset, XFT_FAMILY); + FcFontSet *fonts = FcFontList(NULL, pattern, objectset); + + for (int i = 0; i < fonts->nfont; i++) + { + char* ptr; + XftPatternGetString(fonts->fonts[i], XFT_FAMILY, 0, &ptr); + core::stringw family(ptr); + families.insert(family); + } + XftPatternDestroy(pattern); + FcObjectSetDestroy(objectset); + + /* Copy the sorted list into the array */ + FontNames.clear(); + for (std::set::iterator i = families.begin(); i != families.end(); i++) + FontNames.push_back((*i).c_str()); + } + + bool CFontTool::makeBitmapFont(u32 fontIndex, u32 charsetIndex, s32 fontSize, u32 textureWidth, u32 textureHeight, bool bold, bool italic, bool aa, bool alpha) + { + if (fontIndex >= FontNames.size() || charsetIndex >= CharSets.size() ) + return false; + + Display *display = (Display*) Device->getVideoDriver()->getExposedVideoData().OpenGLLinux.X11Display; + u32 screen = DefaultScreen(display); + Window win = RootWindow(display, screen); + Visual *visual = DefaultVisual(display, screen); + UseAlphaChannel = alpha; + u32 currentImage = 0; + + XftResult result; + XftPattern *request = XftPatternCreate(); + char foundry[256], family[256]; + sprintf(&foundry[0],"%ls",CharSets[charsetIndex].c_str()); + sprintf(&family[0],"%ls",FontNames[fontIndex].c_str()); + XftPatternAddString(request, XFT_FOUNDRY, &foundry[0]); + XftPatternAddString(request, XFT_FAMILY, &family[0]); + XftPatternAddInteger(request, XFT_PIXEL_SIZE, fontSize); + XftPatternAddInteger(request, XFT_WEIGHT, bold ? XFT_WEIGHT_BLACK : XFT_WEIGHT_LIGHT); + XftPatternAddInteger(request, XFT_SLANT, italic ? XFT_SLANT_ITALIC : XFT_SLANT_ROMAN); + XftPatternAddBool(request, XFT_ANTIALIAS, aa); + + /* Find the closest font that matches the user choices and open it and check if the returned + font has anti aliasing enabled by default, even if it wasn't requested */ + FcBool aaEnabled; + XftPattern *found = XftFontMatch(display, DefaultScreen(display), request, &result); + XftPatternGetBool(found, XFT_ANTIALIAS, 0, &aaEnabled); + aa = aaEnabled; + XftFont *font = XftFontOpenPattern(display, found); + + // get rid of the current textures/images + for (u32 i=0; idrop(); + currentTextures.clear(); + for (u32 i=0; idrop(); + currentImages.clear(); + CharMap.clear(); + Areas.clear(); + + /* Calculate the max height of the font. Annoyingly, it seems that the height property of the font + is the maximum height of any single character, but a string of characters, aligned along their + baselines, can exceed this figure. Because I don't know any better way of doing it, I'm going to + have to use the brute force method. + + Note: There will be a certain number of charters in a font, however they may not be grouped + consecutively, and could in fact be spread out with many gaps */ + u32 maxY = 0; + u32 charsFound = 0; + for (FT_UInt charCode = 0; charsFound < FcCharSetCount(font->charset); charCode++) + { + if (!XftCharExists(display, font, charCode)) + continue; + + charsFound++; + + XGlyphInfo extents; + XftTextExtents32(display, font, &charCode, 1, &extents); + if ((extents.xOff <= 0) && (extents.height <= 0)) + continue; + + /* Calculate the width and height, adding 1 extra pixel if anti aliasing is enabled */ + u32 chWidth = extents.xOff + (aa ? 1 : 0); + u32 chHeight = (font->ascent - extents.y + extents.height) + (aa ? 1 : 0); + if (chHeight > maxY) + maxY = chHeight; + + /* Store the character details here */ + SFontArea fontArea; + fontArea.rectangle = core::rect(0, 0, chWidth, chHeight); + CharMap.insert(charCode, Areas.size()); + Areas.push_back(fontArea); + } + core::stringc logmsg = "Found "; + logmsg += (s32) (CharMap.size() + 1); + logmsg += " characters"; + Device->getLogger()->log(logmsg.c_str()); + + /* Get the size of the chars and allocate them a position on a texture. If the next character that + is added would be outside the width or height of the texture, then a new texture is added */ + u32 currentX = 0, currentY = 0, rowY = 0; + for (core::map::Iterator it = CharMap.getIterator(); !it.atEnd(); it++) + { + s32 currentArea = (*it).getValue(); + SFontArea *fontArea = &Areas[currentArea]; + u32 chWidth = fontArea->rectangle.LowerRightCorner.X; + u32 chHeight = fontArea->rectangle.LowerRightCorner.Y; + + /* If the width of this char will exceed the textureWidth then start a new row */ + if ((currentX + chWidth) > textureWidth) + { + currentY += rowY; + currentX = 0; + + /* If the new row added to the texture exceeds the textureHeight then start a new texture */ + if ((currentY + rowY) > textureHeight) + { + currentImage++; + currentY = 0; + } + rowY = 0; + } + + /* Update the area with the current x and y and texture */ + fontArea->rectangle = core::rect(currentX, currentY, currentX + chWidth, currentY + chHeight); + fontArea->sourceimage = currentImage; + currentX += chWidth + 1; + if (chHeight + 1 > rowY) + rowY = chHeight + 1; + } + + /* The last row of chars and the last texture weren't accounted for in the loop, so add them here */ + currentY += rowY; + u32 lastTextureHeight = getTextureSizeFromSurfaceSize(currentY); + currentImages.set_used(currentImage + 1); + currentTextures.set_used(currentImage + 1); + + /* Initialise colours */ + XftColor colFore, colBack; + XRenderColor xFore = {0xffff, 0xffff, 0xffff, 0xffff}; + XRenderColor xBack = {0x0000, 0x0000, 0x0000, 0xffff}; + XftColorAllocValue(display, DefaultVisual(display, screen), DefaultColormap(display, screen), &xFore, &colFore); + XftColorAllocValue(display, DefaultVisual(display, screen), DefaultColormap(display, screen), &xBack, &colBack); + + /* Create a pixmap that is large enough to hold any character in the font */ + Pixmap pixmap = XCreatePixmap(display, win, textureWidth, maxY, DefaultDepth(display, screen)); + XftDraw *draw = XftDrawCreate(display, pixmap, visual, DefaultColormap(display, screen)); + + /* Render the chars */ + for (currentImage = 0; currentImage < currentImages.size(); ++currentImage) + { + core::stringc logmsg = "Creating image "; + logmsg += (s32) (currentImage+1); + logmsg += " of "; + logmsg += (s32) currentImages.size(); + Device->getLogger()->log(logmsg.c_str()); + + /* The last texture that is saved is vertically shrunk to fit the characters drawn on it */ + u32 texHeight = textureHeight; + if (currentImage == currentImages.size() - 1) + texHeight = lastTextureHeight; + + /* The texture that holds this "page" of characters */ + currentImages[currentImage] = Device->getVideoDriver()->createImage(video::ECF_A8R8G8B8, core::dimension2du(textureWidth, texHeight)); + currentImages[currentImage]->fill(video::SColor(alpha ? 0 : 255,0,0,0)); + + for (core::map::Iterator it = CharMap.getIterator(); !it.atEnd(); it++) + { + FcChar32 wch = (*it).getKey(); + s32 currentArea = (*it).getValue(); + if (Areas[currentArea].sourceimage == currentImage) + { + SFontArea *fontArea = &Areas[currentArea]; + u32 chWidth = fontArea->rectangle.LowerRightCorner.X - fontArea->rectangle.UpperLeftCorner.X; + u32 chHeight = fontArea->rectangle.LowerRightCorner.Y - fontArea->rectangle.UpperLeftCorner.Y; + + /* Draw the glyph onto the pixmap */ + XGlyphInfo extents; + XftDrawRect(draw, &colBack, 0, 0, chWidth, chHeight); + XftTextExtents32(display, font, &wch, 1, &extents); + XftDrawString32(draw, &colFore, font, extents.x, extents.y, &wch, 1); + + /* Convert the pixmap into an image, then copy it onto the Irrlicht texture, pixel by pixel. + There's bound to be a faster way, but this is adequate */ + u32 xDest = fontArea->rectangle.UpperLeftCorner.X; + u32 yDest = fontArea->rectangle.UpperLeftCorner.Y + font->ascent - extents.y; + XImage *image = XGetImage(display, pixmap, 0, 0, chWidth, chHeight, 0xffffff, XYPixmap); + if (image) + { + for (u32 ySrc = 0; ySrc < chHeight; ySrc++) + for (u32 xSrc = 0; xSrc < chWidth; xSrc++) + { + /* Get the pixel colour and break it down into rgb components */ + u32 col = XGetPixel(image, xSrc, ySrc); + u32 a = 255; + u32 r = col & visual->red_mask; + u32 g = col & visual->green_mask; + u32 b = col & visual->blue_mask; + while (r > 0xff) r >>= 8; + while (g > 0xff) g >>= 8; + while (b > 0xff) b >>= 8; + + /* To make the background transparent, set the colour to 100% white and the alpha to + the average of the three rgb colour components to maintain the anti-aliasing */ + if (alpha) + { + a = (r + g + b) / 3; + r = 255; + g = 255; + b = 255; + } + currentImages[currentImage]->setPixel(xDest + xSrc,yDest + ySrc,video::SColor(a,r,g,b)); + } + image->f.destroy_image(image); + } + } + } + + /* Add the texture to the list */ + currentTextures[currentImage] = Device->getVideoDriver()->addTexture("GUIFontImage",currentImages[currentImage]); + currentTextures[currentImage]->grab(); + } + + XftColorFree (display, visual, DefaultColormap(display, screen), &colFore); + XftColorFree (display, visual, DefaultColormap(display, screen), &colBack); + XftFontClose(display,font); + XftPatternDestroy(request); + XftDrawDestroy(draw); + XFreePixmap(display, pixmap); + return true; + } +#endif + + CFontTool::~CFontTool() + { +#ifdef _IRR_WINDOWS_ + // destroy display context + if (dc) + DeleteDC(dc); +#endif + + // drop textures+images + for (u32 i=0; idrop(); + currentTextures.clear(); + + for (u32 i=0; idrop(); + currentImages.clear(); + } + +bool CFontTool::saveBitmapFont(const c8 *filename, const c8* format) +{ + if (currentImages.size() == 0) + { + Device->getLogger()->log("No image data to write, aborting."); + return false; + } + + core::stringc fn = filename; + core::stringc imagename = filename; + fn += ".xml"; + + io::IXMLWriter *writer = Device->getFileSystem()->createXMLWriter(fn.c_str()); + + // header and line breaks + writer->writeXMLHeader(); + writer->writeLineBreak(); + + // write information + writer->writeElement(L"font", false, L"type", L"bitmap"); + writer->writeLineBreak(); + writer->writeLineBreak(); + + // write images and link to them + for (u32 i=0; igetVideoDriver()->writeImageToFile(currentImages[i],imagename.c_str()); + + writer->writeElement(L"Texture", true, + L"index", core::stringw(i).c_str(), + L"filename", core::stringw(imagename.c_str()).c_str(), + L"hasAlpha", UseAlphaChannel ? L"true" : L"false"); + writer->writeLineBreak(); + } + + writer->writeLineBreak(); + + // write each character + core::map::Iterator it = CharMap.getIterator(); + while (!it.atEnd()) + { + SFontArea &fa = Areas[(*it).getValue()]; + + wchar_t c[2]; + c[0] = (*it).getKey(); + c[1] = L'\0'; + core::stringw area, under, over, image; + area = core::stringw(fa.rectangle.UpperLeftCorner.X); + area += L", "; + area += fa.rectangle.UpperLeftCorner.Y; + area += L", "; + area += fa.rectangle.LowerRightCorner.X; + area += L", "; + area += fa.rectangle.LowerRightCorner.Y; + + core::array names; + core::array values; + names.clear(); + values.clear(); + // char + names.push_back(core::stringw(L"c")); + values.push_back(core::stringw(c)); + // image number + if (fa.sourceimage != 0) + { + image = core::stringw(fa.sourceimage); + names.push_back(core::stringw(L"i")); + values.push_back(image); + } + // rectangle + names.push_back(core::stringw(L"r")); + values.push_back(area); + + if (fa.underhang != 0) + { + under = core::stringw(fa.underhang); + names.push_back(core::stringw(L"u")); + values.push_back(under); + } + if (fa.overhang != 0) + { + over = core::stringw(fa.overhang); + names.push_back(core::stringw(L"o")); + values.push_back(over); + } + writer->writeElement(L"c", true, names, values); + + writer->writeLineBreak(); + it++; + } + + writer->writeClosingTag(L"font"); + + writer->drop(); + + Device->getLogger()->log("Bitmap font saved."); + + return true; +} -- cgit v1.1