gambas 388bc70157 Don't crash when drawing one-character static strings.
[GB.SDL2]
* BUG: Don't crash when drawing one-character static strings.
2019-01-13 08:58:00 +01:00

528 lines
9.9 KiB
C

/***************************************************************************
c_font.c
(c) 2000-2017 Benoît Minisini <g4mba5@gmail.com>
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.
***************************************************************************/
#define __C_FONT_C
#include "default_font.h"
#include "c_font.h"
typedef
struct {
LIST list;
char *path;
char *name;
}
FONT_LOAD;
static FONT_LOAD *_font_list = NULL;
#define THIS ((CFONT *)_object)
static void create_cache(CFONT *_object)
{
if (!THIS->cache)
GB.HashTable.New(&THIS->cache, GB_COMP_BINARY);
}
CFONT *FONT_create()
{
CFONT *font = (CFONT *)GB.New(CLASS_Font, NULL, NULL);
create_cache(font);
return font;
}
static FONT_LOAD *find_font(const char *name)
{
FONT_LOAD *p;
LIST_for_each(p, _font_list)
{
if (strcasecmp(name, p->name) == 0)
return p;
}
return NULL;
}
static bool load_font(char *path, char *name)
{
FONT_LOAD *load;
char *addr;
int len;
if (GB.LoadFile(path, strlen(path), &addr, &len))
return TRUE;
GB.ReleaseFile(addr, len);
if (!name || !*name)
{
char *p = strrchr(path, '/');
if (p)
name = p + 1;
else
name = path;
p = strrchr(name, '.');
if (p)
name = GB.TempString(name, p - name);
}
if (find_font(name))
{
GB.Error("Font already exists");
return TRUE;
}
GB.AllocZero(POINTER(&load), sizeof(FONT_LOAD));
load->path = GB.NewZeroString(path);
load->name = GB.NewZeroString(name);
LIST_insert(&_font_list, load, &load->list);
return FALSE;
}
static void release_cache(CFONT *_object)
{
GB.HashTable.Enum(THIS->cache, (GB_HASHTABLE_ENUM_FUNC)SDL_FreeImage);
GB.HashTable.Free(&THIS->cache);
}
static bool init_font()
{
if (!TTF_WasInit() && TTF_Init())
{
GB.Error("Unable to initialize TTF library: &1", TTF_GetError());
return TRUE;
}
else
return FALSE;
}
static bool check_font(CFONT *_object)
{
FONT_LOAD *font;
int style;
char *addr;
int len;
if (!THIS->dirty)
return FALSE;
if (THIS->font)
{
if (init_font())
return TRUE;
TTF_CloseFont(THIS->font);
THIS->font = NULL;
}
release_cache(THIS);
create_cache(THIS);
if (!THIS->name)
{
THIS->dirty = FALSE;
return FALSE;
}
font = find_font(THIS->name);
if (!font)
{
GB.Error("Unknown font: &1", THIS->name);
return TRUE;
}
if (GB.LoadFile(font->path, strlen(font->path), &addr, &len))
return TRUE;
if (init_font())
return TRUE;
THIS->font = TTF_OpenFontRW(SDL_RWFromConstMem(addr, len), TRUE, THIS->size);
if (!THIS->font)
{
GB.Error("Unable to load font: &1: &2", THIS->name, TTF_GetError());
return TRUE;
}
style = TTF_STYLE_NORMAL;
if (THIS->bold) style |= TTF_STYLE_BOLD;
if (THIS->italic) style |= TTF_STYLE_ITALIC;
TTF_SetFontStyle(THIS->font, style);
THIS->dirty = FALSE;
return FALSE;
}
static int get_font_ascent(void *_object)
{
if (THIS->font)
return TTF_FontAscent(THIS->font);
else
return DEFAULT_FONT_ASCENT * THIS->size / DEFAULT_FONT_HEIGHT;
}
static int get_font_descent(void *_object)
{
if (THIS->font)
return TTF_FontDescent(THIS->font);
else
return DEFAULT_FONT_DESCENT * THIS->size / DEFAULT_FONT_HEIGHT;
}
static void get_text_size(void *_object, const char *text, int *width, int *height)
{
if (!text || !*text)
{
*width = 0;
*height = get_font_ascent(THIS) + get_font_descent(THIS);
return;
}
if (THIS->font)
{
TTF_SizeUTF8(THIS->font, text, width, height);
return;
}
*width = UTF8_get_length(text, strlen(text)) * DEFAULT_FONT_WIDTH * THIS->size / DEFAULT_FONT_HEIGHT;
*height = THIS->size;
}
SDL_Image *FONT_render_text(CFONT *_object, CWINDOW *window, char *text, int len, int *w, int *h)
{
SDL_Surface *surface;
SDL_Image *image;
bool exist;
if (check_font(THIS))
return NULL;
exist = !GB.HashTable.Get(THIS->cache, text, len, POINTER(&image));
if (exist)
{
if (image->window == window)
{
SDL_QueryTexture(image->texture, NULL, NULL, w, h);
if (!THIS->name)
{
*w = *w * THIS->size / DEFAULT_FONT_HEIGHT;
*h = *h * THIS->size / DEFAULT_FONT_HEIGHT;
}
return image;
}
SDL_FreeImage(image);
GB.HashTable.Remove(THIS->cache, text, len);
}
if (THIS->name)
{
SDL_Color color = { 0xFF, 0xFF, 0xFF, 0xFF };
char c = text[len];
if (c) text[len] = 0;
surface = TTF_RenderUTF8_Blended(THIS->font, text, color);
if (c) text[len] = c;
*w = surface->w;
*h = surface->h;
}
else
{
int size = UTF8_get_length(text, len);
surface = SDL_CreateRGBSurface(0, size * DEFAULT_FONT_WIDTH, DEFAULT_FONT_HEIGHT, 32, RMASK, GMASK, BMASK, AMASK);
if (SDL_MUSTLOCK(surface))
SDL_LockSurface(surface);
FONT_render_default((uint *)surface->pixels, size, text, len);
if (SDL_MUSTLOCK(surface))
SDL_UnlockSurface(surface);
*w = surface->w * THIS->size / DEFAULT_FONT_HEIGHT;
*h = surface->h * THIS->size / DEFAULT_FONT_HEIGHT;
}
image = SDL_CreateImage(surface);
if (GB.HashTable.Count(THIS->cache) >= 128)
{
release_cache(THIS);
create_cache(THIS);
}
GB.HashTable.Add(THIS->cache, text, len, image);
return image;
}
//-------------------------------------------------------------------------
BEGIN_METHOD_VOID(Font_exit)
FONT_LOAD *next;
while (_font_list)
{
next = _font_list->list.next;
GB.FreeString(&_font_list->path);
GB.FreeString(&_font_list->name);
GB.Free(POINTER(&_font_list));
_font_list = next;
}
END_METHOD
BEGIN_METHOD(Font_Load, GB_STRING path; GB_STRING name)
char *path = GB.ToZeroString(ARG(path));
char *name;
if (MISSING(name))
name = NULL;
else
name = GB.ToZeroString(ARG(name));
load_font(path, name);
END_METHOD
BEGIN_METHOD(Font_get, GB_STRING font)
CFONT *font;
char *desc = GB.ToZeroString(ARG(font));
char *elt;
int val;
bool bold = FALSE;
bool italic = FALSE;
int size = 10;
char *name = NULL;
for (elt = strtok(desc, ","); elt; elt = strtok(NULL, ","))
{
if (strcasecmp(elt, "bold") == 0)
{
bold = TRUE;
continue;
}
if (strcasecmp(elt, "italic") == 0)
{
italic = TRUE;
continue;
}
val = atoi(elt);
if (val)
{
size = val;
continue;
}
if (name)
{
GB.Error("Font name defined twice");
goto ERROR;
}
name = GB.NewZeroString(elt);
}
if (size < 1 || size > 1024)
{
GB.Error("Incorrect font size");
goto ERROR;
}
font = FONT_create();
font->name = name;
font->bold = bold;
font->italic = italic;
font->size = size;
font->dirty = TRUE;
GB.ReturnObject(font);
return;
ERROR:
GB.FreeString(&name);
return;
END_METHOD
BEGIN_METHOD_VOID(Font_free)
release_cache(THIS);
if (THIS->font)
TTF_CloseFont(THIS->font);
GB.FreeString(&THIS->name);
END_METHOD
BEGIN_PROPERTY(Font_Size)
if (READ_PROPERTY)
GB.ReturnInteger(THIS->size);
else
{
int size = VPROP(GB_INTEGER);
if (size < 1 || size > 1024)
GB.Error("Incorrect font size");
else if (THIS->size != size)
{
THIS->size = size;
if (THIS->name)
THIS->dirty = TRUE;
}
}
END_PROPERTY
BEGIN_PROPERTY(Font_Bold)
if (READ_PROPERTY)
GB.ReturnBoolean(THIS->bold);
else if (THIS->bold != VPROP(GB_BOOLEAN))
{
THIS->bold = VPROP(GB_BOOLEAN);
if (THIS->font && !THIS->dirty)
TTF_SetFontStyle(THIS->font, (TTF_GetFontStyle(THIS->font) ^ TTF_STYLE_BOLD));
}
END_PROPERTY
BEGIN_PROPERTY(Font_Italic)
if (READ_PROPERTY)
GB.ReturnBoolean(THIS->italic);
else if (THIS->italic != VPROP(GB_BOOLEAN))
{
THIS->italic = VPROP(GB_BOOLEAN);
if (THIS->font && !THIS->dirty)
TTF_SetFontStyle(THIS->font, (TTF_GetFontStyle(THIS->font) ^ TTF_STYLE_ITALIC));
}
END_PROPERTY
BEGIN_PROPERTY(Font_Name)
if (READ_PROPERTY)
GB.ReturnString(THIS->name);
else
{
GB.StoreString(PROP(GB_STRING), &THIS->name);
THIS->dirty = TRUE;
}
END_PROPERTY
BEGIN_PROPERTY(Font_Ascent)
if (check_font(THIS))
return;
GB.ReturnInteger(TTF_FontAscent(THIS->font));
END_PROPERTY
BEGIN_PROPERTY(Font_Descent)
if (check_font(THIS))
return;
GB.ReturnInteger(TTF_FontDescent(THIS->font));
END_PROPERTY
BEGIN_PROPERTY(Font_Fixed)
if (check_font(THIS))
return;
GB.ReturnBoolean(TTF_FontFaceIsFixedWidth(THIS->font));
END_PROPERTY
BEGIN_METHOD(Font_TextWidth, GB_STRING text)
int w, h;
if (check_font(THIS))
return;
get_text_size(THIS, GB.ToZeroString(ARG(text)), &w, &h);
GB.ReturnInteger(w);
END_METHOD
BEGIN_METHOD(Font_TextHeight, GB_STRING text)
int w, h;
if (check_font(THIS))
return;
get_text_size(THIS, GB.ToZeroString(ARG(text)), &w, &h);
GB.ReturnInteger(h);
END_METHOD
//-------------------------------------------------------------------------
GB_DESC FontDesc[] =
{
GB_DECLARE("Font", sizeof(CFONT)), GB_NOT_CREATABLE(),
GB_CONSTANT("DefaultHeight", "i", DEFAULT_FONT_HEIGHT),
GB_STATIC_METHOD("Load", NULL, Font_Load, "(Path)s[(Name)s]"),
GB_STATIC_METHOD("_get", "Font", Font_get, "(Font)s"),
GB_STATIC_METHOD("_exit", NULL, Font_exit, NULL),
GB_METHOD("_free", NULL, Font_free, NULL),
GB_PROPERTY("Size", "i", Font_Size),
GB_PROPERTY("Bold", "b", Font_Bold),
GB_PROPERTY("Italic", "b", Font_Italic),
//GB_PROPERTY("Underline", "b", Font_Underline),
GB_PROPERTY_READ("Name", "s", Font_Name),
GB_PROPERTY_READ("Ascent", "i", Font_Ascent),
GB_PROPERTY_READ("Descent", "i", Font_Descent),
GB_PROPERTY_READ("Fixed", "b", Font_Fixed),
GB_METHOD("TextWidth", "i", Font_TextWidth, "(Text)s"),
GB_METHOD("TextHeight", "i", Font_TextHeight, "(Text)s"),
/*GB_METHOD("GetImage", "Image", Font_GetImage, "(Text)s"),*/
GB_END_DECLARE
};