//----------------------------------------------------------------------------- // Copyright (c) 2013 GarageGames, LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. //----------------------------------------------------------------------------- #include "graphics/gFont.h" #include "graphics/gBitmap.h" #include "math/mRect.h" #include "console/console.h" #include "string/unicode.h" #include "platformX86UNIX/platformX86UNIX.h" #include "platformX86UNIX/x86UNIXFont.h" // Needed by createFont #include #include #include #include #include #include // For XRenderColor // Needed for getenv in createFont #include XftFont *loadFont(const char *name, S32 size, Display *display) { XftFont *fontInfo = NULL; char* fontname = const_cast(name); if (dStrlen(fontname)==0) fontname = "arial"; else if (stristr(const_cast(name), "arial") != NULL) fontname = "arial"; else if (stristr(const_cast(name), "lucida console") != NULL) fontname = "lucida console"; char* weight = "medium"; char* slant = "roman"; // no slant if (stristr(const_cast(name), "bold") != NULL) weight = "bold"; if (stristr(const_cast(name), "italic") != NULL) slant = "italic"; int mSize = size - 2 - (int)((float)size * 0.1); char xfontName[512]; // We specify a lower DPI to get 'correct' looking fonts, if we go with the // native DPI the fonts are to big and don't fit the widgets. dSprintf(xfontName, 512, "%s-%d:%s:slant=%s:dpi=76", fontname, mSize, weight, slant); // Lets see if Xft can get a font for us. char xftname[1024]; fontInfo = XftFontOpenName(display, DefaultScreen(display), xfontName); // Cant find a suitabke font, default to a known font (6x10) if ( !fontInfo ) { dSprintf(xfontName, 512, "6x10-%d:%s:slant=%s:dpi=76", mSize, weight, slant); fontInfo = XftFontOpenName(display, DefaultScreen(display), xfontName); } XftNameUnparse(fontInfo->pattern, xftname, 1024); #ifdef DEBUG Con::printf("Font '%s %d' mapped to '%s'\n", name, size, xftname); #endif return fontInfo; } // XA: New class for the unix unicode font PlatformFont *createPlatformFont(const char *name, U32 size, U32 charset /* = TGE_ANSI_CHARSET */) { PlatformFont *retFont = new x86UNIXFont; if(retFont->create(name, size, charset)) return retFont; delete retFont; return NULL; } x86UNIXFont::x86UNIXFont() {} x86UNIXFont::~x86UNIXFont() {} bool x86UNIXFont::create(const char *name, U32 size, U32 charset) { Display *display = XOpenDisplay(getenv("DISPLAY")); if (display == NULL) AssertFatal(false, "createFont: cannot connect to X server"); XftFont *font = loadFont(name, size, display); if (!font) { Con::errorf("Error: Could not load font -%s-", name); return false; } char xfontname[1024]; XftNameUnparse(font->pattern, xfontname, 1024); #ifdef DEBUG Con::printf("CreateFont: request for %s %d, using %s", name, size, xfontname); #endif // store some info about the font baseline = font->ascent; height = font->height; mFontName = StringTable->insert(xfontname); XftFontClose (display, font); // DISPLAY XCloseDisplay(display); return true; } bool x86UNIXFont::isValidChar(const UTF16 str) const { // 0x20 == 32 // 0x100 == 256 if( str < 0x20 || str > 0x100 ) return false; return true; } bool x86UNIXFont::isValidChar(const UTF8 *str) const { return isValidChar(oneUTF32toUTF16(oneUTF8toUTF32(str,NULL))); } PlatformFont::CharInfo &x86UNIXFont::getCharInfo(const UTF16 ch) const { Display *display = XOpenDisplay(getenv("DISPLAY")); if (!display ) AssertFatal(false, "createFont: cannot connect to X server"); static PlatformFont::CharInfo c; dMemset(&c, 0, sizeof(c)); c.bitmapIndex = 0; c.xOffset = 0; c.yOffset = 0; XftFont *fontInfo = XftFontOpenName(display, DefaultScreen(display), mFontName); if (!fontInfo) { //Try a fall back font before crashing.. fontInfo = XftFontOpenName(display, DefaultScreen(display), "lucida console-10:dpi=76"); if (!fontInfo) { AssertFatal(false, "createFont: cannot load font"); } } int screen = DefaultScreen(display); // Create the pixmap to draw on. Drawable pixmap = XCreatePixmap(display, DefaultRootWindow(display), fontInfo->max_advance_width, fontInfo->height, DefaultDepth(display, screen)); // And the Xft wrapper around it. XftDraw *draw = XftDrawCreate(display, pixmap, DefaultVisual(display, screen), DefaultColormap(display, screen)); // Allocate some colors, we don't use XftColorAllocValue here as that // Don't appear to function correctly (or I'm using it wrong) As we only do // this twice per new un cached font it isn't that big of a penalty. (Each // call to XftColorAllocName involves a round trip to the X Server) XftColor black, white; XftColorAllocName(display, DefaultVisual(display, screen), DefaultColormap(display, screen), "black", &black); // White XftColorAllocName(display, DefaultVisual(display, screen), DefaultColormap(display, screen), "white", &white); XGlyphInfo charinfo; XftTextExtents16(display, fontInfo, &ch, 1, &charinfo); c.height = fontInfo->height; c.xOrigin = 0; c.yOrigin = fontInfo->ascent; c.xIncrement = charinfo.xOff; c.width = charinfo.xOff; // kick out early if the character is undrawable if( c.width == 0 || c.height == 0) return c; // allocate a greyscale bitmap and clear it. int bitmapDataSize = c.width * c.height; c.bitmapData = new U8[bitmapDataSize]; dMemset(c.bitmapData, 0, bitmapDataSize); XftDrawRect (draw, &black, 0, 0, fontInfo->max_advance_width, fontInfo->height); XftDrawString16 (draw, &white, fontInfo, 0, fontInfo->ascent, &ch, 1); // grab the pixmap image XImage *ximage = XGetImage(display, pixmap, 0, 0, charinfo.xOff, fontInfo->height, AllPlanes, XYPixmap); if (!ximage) AssertFatal(false, "cannot get x image"); int x, y; // grab each pixel and store it in the scratchPad for(y = 0; y < fontInfo->height; y++) { for(x = 0; x < charinfo.xOff; x++) c.bitmapData[y * charinfo.xOff + x] = static_cast(XGetPixel(ximage, x, y)); } XDestroyImage(ximage); XftColorFree(display, DefaultVisual(display, screen), DefaultColormap(display, screen), &black); XftColorFree(display, DefaultVisual(display, screen), DefaultColormap(display, screen), &white); XftDrawDestroy(draw); XFreePixmap(display, pixmap); XCloseDisplay(display); return c; } PlatformFont::CharInfo &x86UNIXFont::getCharInfo(const UTF8 *str) const { return getCharInfo(oneUTF32toUTF16(oneUTF8toUTF32(str,NULL))); }