gambas-source-code/gb.sdl/SDLfont.cpp

586 lines
13 KiB
C++

/***************************************************************************
SDLfont.cpp
(c) 2006 Laurent Carlier <lordheavy@users.sourceforge.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301, USA.
***************************************************************************/
#include "SDLfont.h"
#include "SDLapp.h"
#include "gb_common.h"
#include "main.h"
#include <string>
#include <ctype.h>
#include "default_font.h"
#if 0
typedef struct {
std::string name;
std::string realname;
std::string foundry;
std::string path;
} fontdesc;
static std::vector<fontdesc> fontDB;
static StringList _FontList;
#define DEFAULT_DPI 72 /* Default DPI size in SDL_TTF */
inline bool cmp_db_nocase(const fontdesc x, const fontdesc y)
{
std::string a = x.name, b = y.name;
transform(a.begin(), a.end(), a.begin(), tolower);
transform(b.begin(), b.end(), b.begin(), tolower);
return (b>a) ? 1 : 0;
}
StringList SDLfont::GetFontList(void )
{
return _FontList;
}
void SDLfont::Init()
{
Display *disp = XOpenDisplay(NULL);
int scr = XDefaultScreen(disp);
int i;
XftFontSet *FntnameSet = XftListFonts(disp, scr, 0,
XFT_FAMILY, NULL);
// Get the fonts name
for (i = 0; i < FntnameSet->nfont; i++) {
char *name[255];
char *foundry[255];
fontdesc font;
unsigned int j;
XftResult res = XftPatternGetString(FntnameSet->fonts[i], XFT_FAMILY, 0, name);
if (res!=XftResultMatch)
continue;
XftFontSet *Fntdetail = XftListFonts(disp, scr,
XFT_FAMILY, XftTypeString, name[0], 0,
XFT_FOUNDRY, NULL);
j = Fntdetail->nfont;
if (j>1) {
while (j) {
XftPatternGetString(Fntdetail->fonts[j-1], XFT_FOUNDRY, 0, foundry);
font.name = font.realname = name[0];
font.foundry = foundry[0];
font.name = font.name + " [" +font.foundry+ "]";
fontDB.push_back(font);
j--;
}
}
else {
font.name = font.realname = name[0];
XftPatternGetString(Fntdetail->fonts[0], XFT_FOUNDRY, 0, foundry);
font.foundry = foundry[0];
fontDB.push_back(font);
}
XFree(Fntdetail);
}
std::sort(fontDB.begin(), fontDB.end(), cmp_db_nocase);
XFree(FntnameSet);
std::string font = "";
i = 0;
while (i<int(fontDB.size())) {
if (!fontDB[i].name.compare(font)) {
fontDB.erase(fontDB.begin() + i);
i++;
continue;
}
font = fontDB[i++].name;
}
i = 0;
while (i<int(fontDB.size())) {
char *res[255];
XftFontSet *Fntdetail = XftListFonts(disp, scr,
XFT_FAMILY, XftTypeString, fontDB[i].realname.c_str(),
XFT_FOUNDRY, XftTypeString, fontDB[i].foundry.c_str(), 0,
XFT_FILE, NULL);
XftPatternGetString(Fntdetail->fonts[0], XFT_FILE, 0, res);
fontDB[i].path = res[0];
_FontList.push_back(fontDB[i].name);
XFree(Fntdetail);
i++;
}
XCloseDisplay(disp);
}
#endif
#define UNICODE_INVALID 0xFFFFFFFFU
static const char _char_length[256] =
{
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,6,6,1,1
};
#define utf8_get_char_length(_c) ((int)_char_length[(unsigned char)(_c)])
static int utf8_get_length(const char *sstr, int len)
{
const uchar *str = (const uchar *)sstr;
int ulen;
int i;
ulen = 0;
for (i = 0; i < len; i++)
{
if ((str[i] & 0xC0) != 0x80)
ulen++;
}
return ulen;
}
static uint utf8_to_unicode(const char *sstr, int len)
{
const uchar *str = (const uchar *)sstr;
uint unicode;
switch (len)
{
case 2:
unicode = (str[1] & 0x3F) + ((str[0] & 0x1F) << 6);
if (unicode < 0x80)
goto _INVALID;
break;
case 3:
unicode = (str[2] & 0x3F) + ((str[1] & 0x3F) << 6) + ((str[0] & 0xF) << 12);
if (unicode < 0x800)
goto _INVALID;
break;
case 4:
unicode = (str[3] & 0x3F) + ((str[2] & 0x3F) << 6) + ((str[1] & 0x3F) << 12) + ((str[0] & 0x7) << 18);
if (unicode < 0x10000)
goto _INVALID;
break;
case 5:
unicode = (str[4] & 0x3F) + ((str[3] & 0x3F) << 6) + ((str[2] & 0x3F) << 12) + ((str[1] & 0x3F) << 18) + ((str[0] & 0x3) << 24);
if (unicode < 0x200000)
goto _INVALID;
break;
case 6:
unicode = (str[5] & 0x3F) + ((str[4] & 0x3F) << 6) + ((str[3] & 0x3F) << 12) + ((str[2] & 0x3F) << 18) + ((str[1] & 0x3F) << 24) + ((str[0] & 0x1) << 30);
if (unicode < 0x4000000)
goto _INVALID;
break;
default:
unicode = str[0];
break;
}
return unicode;
_INVALID:
return UNICODE_INVALID;
}
static void render_default_font(uint *dest, int size, const char *text, int len)
{
static void *jump[] = { &&__0, &&__1, &&__2, &&__3, &&__4, &&__5, &&__6, &&__7, &&__8, &&__9, &&__A, &&__B, &&__C, &&__D, &&__E, &&__F };
static void *jump2[] = { &&__00, &&__10, &&__20, &&__30, &&__40, &&__50, &&__60, &&__70, &&__80, &&__90, &&__A0, &&__B0, &&__C0, &&__D0, &&__E0, &&__F0 };
int lc;
const char *src;
uint *p;
int y;
uchar line;
uint code;
uint col = 0xFFFFFFFF;
size *= DEFAULT_FONT_WIDTH;
for(;;)
{
if (!*text)
break;
lc = utf8_get_char_length(*text);
code = utf8_to_unicode(text, lc);
text += lc;
if (code >= 33 && code <= 126)
src = _default_font_33_126 + DEFAULT_FONT_HEIGHT * (code - 33);
else if (code >= 160 && code <= 687)
src = _default_font_160_687 + DEFAULT_FONT_HEIGHT * (code - 160);
else
src = NULL;
if (src)
{
p = dest;
for (y = 0; y < DEFAULT_FONT_HEIGHT; y++)
{
line = *src++;
if (!line)
{
p += size;
continue;
}
goto *jump[line & 0xF];
__1: p[0] = col; goto __0;
__2: p[1] = col; goto __0;
__3: p[0] = p[1] = col; goto __0;
__4: p[2] = col; goto __0;
__5: p[0] = p[2] = col; goto __0;
__6: p[1] = p[2] = col; goto __0;
__7: p[0] = p[1] = p[2] = col; goto __0;
__8: p[3] = col; goto __0;
__9: p[0] = p[3] = col; goto __0;
__A: p[1] = p[3] = col; goto __0;
__B: p[0] = p[1] = p[3] = col; goto __0;
__C: p[2] = p[3] = col; goto __0;
__D: p[0] = p[2] = p[3] = col; goto __0;
__E: p[1] = p[2] = p[3] = col; goto __0;
__F: p[0] = p[1] = p[2] = p[3] = col; goto __0;
__0:
goto *jump2[line >> 4];
__10: p[4] = col; goto __00;
__20: p[5] = col; goto __00;
__30: p[4] = p[5] = col; goto __00;
__40: p[6] = col; goto __00;
__50: p[4] = p[6] = col; goto __00;
__60: p[5] = p[6] = col; goto __00;
__70: p[4] = p[5] = p[6] = col; goto __00;
__80: p[7] = col; goto __00;
__90: p[4] = p[7] = col; goto __00;
__A0: p[5] = p[7] = col; goto __00;
__B0: p[4] = p[5] = p[7] = col; goto __00;
__C0: p[6] = p[7] = col; goto __00;
__D0: p[4] = p[6] = p[7] = col; goto __00;
__E0: p[5] = p[6] = p[7] = col; goto __00;
__F0: p[4] = p[5] = p[6] = p[7] = col; goto __00;
__00:
p += size;
}
}
dest += DEFAULT_FONT_WIDTH;
}
}
//-------------------------------------------------------------------------
SDLfont::SDLfont(const char *fontfile)
{
hfontsize = DEFAULT_FONT_HEIGHT;
hSDLfont = 0;
_last_surface = 0;
_last_text = 0;
/*if (!fontfile)
{
hfontname = GB.System.Path();
hfontname += "/share/gambas" GAMBAS_VERSION_STRING "/gb.sdl/" DEFAULT_FONT;
}
else
hfontname = fontfile;*/
if (fontfile)
{
hfontname = fontfile;
OpenFont(hfontname.c_str());
}
}
SDLfont::~SDLfont()
{
GB.FreeString(&_last_text);
if (_last_surface)
_last_surface->Unref();
if (hSDLfont)
TTF_CloseFont(hSDLfont);
}
void SDLfont::OpenFont(const char* file)
{
if (hSDLfont)
TTF_CloseFont(hSDLfont);
hSDLfont = TTF_OpenFont(file, hfontsize);
if (UNLIKELY(hSDLfont == NULL))
SDLerror::RaiseError(TTF_GetError());
}
#if 0
void SDLfont::SetFontName(char* name)
{
std::string font = name;
int i=0;
while (i<int(fontDB.size())) {
if (!fontDB[i].name.compare(font)) {
hfonttype = X_font;
hfontsize = DEFAULT_FONT_SIZE;
hfontindex = i;
hfontname = fontDB[hfontindex].path;
OpenFont(hfontname.c_str());
return;
}
i++;
}
SDLerror::RaiseError("Font not found!");
}
#endif
const char* SDLfont::GetFontName(void )
{
if (!hSDLfont)
return "";
else
return hfontname.substr((hfontname.find_last_of("/"))+1).c_str();
}
void SDLfont::SetFontSize(int size)
{
hfontsize = size;
if (!hSDLfont)
return;
int style = TTF_GetFontStyle(hSDLfont);
OpenFont(hfontname.c_str());
TTF_SetFontStyle(hSDLfont, style);
}
int SDLfont::GetScale()
{
if (hSDLfont)
return 1;
if (hfontsize <= DEFAULT_FONT_HEIGHT)
return 1;
return hfontsize / DEFAULT_FONT_HEIGHT;
}
void SDLfont::SetFontBold(bool state)
{
if (!hSDLfont)
return;
if (state == (TTF_GetFontStyle(hSDLfont) & TTF_STYLE_BOLD))
return;
TTF_SetFontStyle(hSDLfont, (TTF_GetFontStyle(hSDLfont) ^ TTF_STYLE_BOLD));
}
bool SDLfont::IsFontBold(void )
{
if (!hSDLfont)
return false;
else
return (TTF_GetFontStyle(hSDLfont) & TTF_STYLE_BOLD);
}
void SDLfont::SetFontItalic(bool state)
{
if (!hSDLfont)
return;
if (state == (TTF_GetFontStyle(hSDLfont) & TTF_STYLE_ITALIC))
return;
TTF_SetFontStyle(hSDLfont, (TTF_GetFontStyle(hSDLfont) ^ TTF_STYLE_ITALIC));
}
bool SDLfont::IsFontItalic(void )
{
if (!hSDLfont)
return false;
else
return (TTF_GetFontStyle(hSDLfont) & TTF_STYLE_ITALIC);
}
void SDLfont::SetFontStrikeout(bool state)
{
/*
if (state == (TTF_GetFontStyle(hSDLfont) & TTF_STYLE_STRIKETHROUGH))
return;
TTF_SetFontStyle(hSDLfont, (TTF_GetFontStyle(hSDLfont) ^ TTF_STYLE_STRIKETHROUGH));
*/
}
bool SDLfont::IsFontStrikeout(void )
{
// return (TTF_GetFontStyle(hSDLfont) & TTF_STYLE_STRIKETHROUGH);
return false;
}
void SDLfont::SetFontUnderline(bool state)
{
if (!hSDLfont)
return;
if (state == (TTF_GetFontStyle(hSDLfont) & TTF_STYLE_UNDERLINE))
return;
TTF_SetFontStyle(hSDLfont, (TTF_GetFontStyle(hSDLfont) ^ TTF_STYLE_UNDERLINE));
}
bool SDLfont::IsFontUnderline(void )
{
if (!hSDLfont)
return false;
else
return (TTF_GetFontStyle(hSDLfont) & TTF_STYLE_UNDERLINE);
}
bool SDLfont::IsFontScalable(void )
{
if (!hSDLfont)
return false;
else
return true;
}
int SDLfont::GetFontAscent(void )
{
if (hSDLfont)
return TTF_FontAscent(hSDLfont);
else
return DEFAULT_FONT_ASCENT * GetScale();
}
int SDLfont::GetFontDescent(void )
{
if (hSDLfont)
return TTF_FontDescent(hSDLfont);
else
return DEFAULT_FONT_DESCENT * GetScale();
}
bool SDLfont::IsFontFixed(void )
{
if (hSDLfont)
return (TTF_FontFaceIsFixedWidth(hSDLfont));
else
return true;
}
void SDLfont::SizeText(const char *text, int len, int *width, int *height)
{
if (len == 0)
{
*width = 0;
*height = GetFontAscent() + GetFontDescent();
return;
}
if (hSDLfont)
{
TTF_SizeUTF8(hSDLfont, GB.TempString(text, len), width, height);
return;
}
*width = utf8_get_length(text, len) * DEFAULT_FONT_WIDTH * GetScale();
*height = DEFAULT_FONT_HEIGHT * GetScale();
}
SDLsurface* SDLfont::RenderText(const char *text, int len)
{
SDL_Surface *result;
if (len <= 0 || len >= 1024)
return NULL;
if (_last_surface)
{
if (len == GB.StringLength(_last_text) && strncmp(text, _last_text, len) == 0)
{
_last_surface->Ref();
return _last_surface;
}
}
if (hSDLfont)
{
SDL_Color fg = {0xFF, 0xFF, 0xFF};
result = TTF_RenderUTF8_Blended(hSDLfont, GB.TempString(text, len), fg);
}
else
{
int size = utf8_get_length(text, len);
result = SDL_CreateRGBSurface(SDL_SWSURFACE, size * DEFAULT_FONT_WIDTH, DEFAULT_FONT_HEIGHT, 32,
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
0x0000FF00, 0x00FF0000, 0xFF000000, 0x000000FF);
#else
0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000);
#endif
if (SDL_MUSTLOCK(result))
SDL_LockSurface(result);
render_default_font((uint *)result->pixels, size, text, len);
if (SDL_MUSTLOCK(result))
SDL_UnlockSurface(result);
}
GB.FreeString(&_last_text);
_last_text = GB.NewString(text, len);
if (_last_surface)
_last_surface->Unref();
_last_surface = new SDLsurface(result);
_last_surface->Ref();
return _last_surface;
}
int SDLfont::GetDefaultFontSize()
{
return DEFAULT_FONT_HEIGHT;
}