From 0ed9301ed908311a3dc14f264a75938bcf794b0a Mon Sep 17 00:00:00 2001 From: Geoffrey McRae Date: Tue, 20 Nov 2018 04:38:11 +1100 Subject: [PATCH] [client] font: implemented font ABI and updated OpenGL to use it --- client/CMakeLists.txt | 1 + client/fonts/sdl.c | 164 ++++++++++++++++++++++++++++++++++++++ client/lg-font.h | 61 ++++++++++++++ client/lg-fonts.h | 31 +++++++ client/lg-renderer.h | 4 +- client/main.c | 52 +----------- client/renderers/opengl.c | 86 ++++++++++++-------- 7 files changed, 313 insertions(+), 86 deletions(-) create mode 100644 client/fonts/sdl.c create mode 100644 client/lg-font.h create mode 100644 client/lg-fonts.h diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index fc7847a2..90166c7c 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -61,6 +61,7 @@ set(SOURCES renderers/egl_shader.c renderers/egl_texture.c renderers/egl_model.c + fonts/sdl.c ) add_executable(looking-glass-client ${SOURCES}) diff --git a/client/fonts/sdl.c b/client/fonts/sdl.c new file mode 100644 index 00000000..e9acd6c5 --- /dev/null +++ b/client/fonts/sdl.c @@ -0,0 +1,164 @@ +/* +Looking Glass - KVM FrameRelay (KVMFR) Client +Copyright (C) 2017 Geoffrey McRae +https://looking-glass.hostfission.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 of the License, 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., 59 Temple +Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include + +#include "lg-font.h" +#include "debug.h" + +#include +#include + +static int g_initCount = 0; +static FcConfig * g_fontConfig = NULL; + +struct Inst +{ + TTF_Font * font; +}; + +static bool lgf_sdl_create(LG_FontObj * opaque, const char * font_name, unsigned int size) +{ + if (g_initCount++ == 0) + { + if (TTF_Init() < 0) + { + DEBUG_ERROR("TTF_Init Failed"); + return false; + } + + g_fontConfig = FcInitLoadConfigAndFonts(); + if (!g_fontConfig) + { + DEBUG_ERROR("FcInitLoadConfigAndFonts Failed"); + return false; + } + } + + *opaque = malloc(sizeof(struct Inst)); + if (!*opaque) + { + DEBUG_INFO("Failed to allocate %lu bytes", sizeof(struct Inst)); + return false; + } + memset(*opaque, 0, sizeof(struct Inst)); + + struct Inst * this = (struct Inst *)*opaque; + + if (!font_name) + font_name = "FreeMono"; + + FcPattern * pat = FcNameParse((const FcChar8*)font_name); + FcConfigSubstitute (g_fontConfig, pat, FcMatchPattern); + FcDefaultSubstitute(pat); + FcResult result; + FcChar8 * file = NULL; + FcPattern * font = FcFontMatch(g_fontConfig, pat, &result); + + if (font && (FcPatternGetString(font, FC_FILE, 0, &file) == FcResultMatch)) + { + this->font = TTF_OpenFont((char *)file, size); + if (!this->font) + { + DEBUG_ERROR("TTL_OpenFont Failed"); + return false; + } + } + else + { + DEBUG_ERROR("Failed to locate the requested font: %s", font_name); + return false; + } + FcPatternDestroy(pat); + + return true; +} + +static void lgf_sdl_destroy(LG_FontObj opaque) +{ + struct Inst * this = (struct Inst *)opaque; + if (this->font) + TTF_CloseFont(this->font); + free(this); + + if (--g_initCount == 0) + TTF_Quit(); +} + +static bool lgf_sdl_supports(LG_FontObj opaque, LG_FontMode mode) +{ + return (mode == LG_FONT_BITMAP); +} + +static LG_FontOut lgf_sdl_render(LG_FontObj opaque, LG_FontMode mode, unsigned int fg_color, const char * text) +{ + struct Inst * this = (struct Inst *)opaque; + + if (mode != LG_FONT_BITMAP) + { + DEBUG_ERROR("Unsupported render mode"); + return false; + } + + SDL_Surface * surface; + SDL_Color color; + color.r = (fg_color & 0xff000000) >> 24; + color.g = (fg_color & 0x00ff0000) >> 16; + color.b = (fg_color & 0x0000ff00) >> 8; + color.a = (fg_color & 0x000000ff) >> 0; + + if (!(surface = TTF_RenderText_Blended(this->font, text, color))) + { + DEBUG_ERROR("Failed to render text: %s", TTF_GetError()); + return false; + } + + LG_FontBitmap * out = malloc(sizeof(LG_FontBitmap)); + if (!out) + { + SDL_FreeSurface(surface); + DEBUG_ERROR("Failed to allocate memory for font bitmap"); + return false; + } + + out->reserved = surface; + out->width = surface->w; + out->height = surface->h; + out->bpp = surface->format->BytesPerPixel; + out->pixels = surface->pixels; + + return (LG_FontOut*)out; +} + +static void lgf_sdl_release(LG_FontObj opaque, LG_FontOut font) +{ + LG_FontBitmap * bitmap = (LG_FontBitmap *)font; + SDL_FreeSurface(bitmap->reserved); +} + +struct LG_Font LGF_SDL = +{ + .name = "SDL", + .create = lgf_sdl_create, + .destroy = lgf_sdl_destroy, + .supports = lgf_sdl_supports, + .render = lgf_sdl_render, + .release = lgf_sdl_release +}; \ No newline at end of file diff --git a/client/lg-font.h b/client/lg-font.h new file mode 100644 index 00000000..a10ea87c --- /dev/null +++ b/client/lg-font.h @@ -0,0 +1,61 @@ +/* +Looking Glass - KVM FrameRelay (KVMFR) Client +Copyright (C) 2017 Geoffrey McRae +https://looking-glass.hostfission.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 of the License, 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., 59 Temple +Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#pragma once + +#include + +typedef void * LG_FontObj; +typedef void * LG_FontOut; + +typedef enum LG_FontMode +{ + LG_FONT_BITMAP, + LG_FONT_OPENGL, + LG_FONT_VULKAN +} +LG_FontMode; + +typedef struct LG_FontBitmap +{ + void * reserved; + + unsigned int width, height; + unsigned int bpp; // bytes per pixel + uint8_t * pixels; +} +LG_FontBitmap; + +typedef bool (* LG_FontCreate )(LG_FontObj * opaque, const char * font_name, unsigned int size); +typedef void (* LG_FontDestroy )(LG_FontObj opaque); +typedef bool (* LG_FontSupports )(LG_FontObj opaque, LG_FontMode mode); +typedef LG_FontOut (* LG_FontRender )(LG_FontObj opaque, LG_FontMode mode, unsigned int fg_color, const char * text); +typedef void (* LG_FontRelease )(LG_FontObj opaque, LG_FontOut font); + +typedef struct LG_Font +{ + // mandatory support + const char * name; + LG_FontCreate create; + LG_FontDestroy destroy; + LG_FontSupports supports; + LG_FontRender render; + LG_FontRelease release; +} +LG_Font; \ No newline at end of file diff --git a/client/lg-fonts.h b/client/lg-fonts.h new file mode 100644 index 00000000..82c6d04c --- /dev/null +++ b/client/lg-fonts.h @@ -0,0 +1,31 @@ +/* +Looking Glass - KVM FrameRelay (KVMFR) Client +Copyright (C) 2017 Geoffrey McRae +https://looking-glass.hostfission.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 of the License, 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., 59 Temple +Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#pragma once +#include "lg-font.h" + +extern const LG_Font LGF_SDL; + +const LG_Font * LG_Fonts[] = +{ + &LGF_SDL, + NULL // end of array sentinal +}; + +#define LG_FONT_COUNT ((sizeof(LG_Font) / sizeof(LG_Font *)) - 1) \ No newline at end of file diff --git a/client/lg-renderer.h b/client/lg-renderer.h index 66e8b78a..fe57a5bb 100644 --- a/client/lg-renderer.h +++ b/client/lg-renderer.h @@ -62,8 +62,8 @@ typedef LG_RendererOpt * LG_RendererOptions; typedef struct LG_RendererParams { - TTF_Font * font; - TTF_Font * alertFont; +// TTF_Font * font; +// TTF_Font * alertFont; bool showFPS; } LG_RendererParams; diff --git a/client/main.c b/client/main.c index 8dccc5e7..f01d7964 100644 --- a/client/main.c +++ b/client/main.c @@ -21,7 +21,6 @@ Place, Suite 330, Boston, MA 02111-1307 USA #include #include #include -#include #include #include #include @@ -35,7 +34,6 @@ Place, Suite 330, Boston, MA 02111-1307 USA #include #include #include -#include #include "debug.h" #include "utils.h" @@ -51,8 +49,6 @@ struct AppState bool started; bool keyDown[SDL_NUM_SCANCODES]; - TTF_Font * font; - TTF_Font * alertFont; bool haveSrcSize; int windowW, windowH; SDL_Point srcSize; @@ -769,53 +765,8 @@ int run() // SIGINT and the user sending a close event, such as ALT+F4 signal(SIGINT, intHandler); - if (TTF_Init() < 0) - { - DEBUG_ERROR("TTL_Init Failed"); - return -1; - } - - FcConfig * config = FcInitLoadConfigAndFonts(); - if (!config) - { - DEBUG_ERROR("FcInitLoadConfigAndFonts Failed"); - return -1; - } - - FcPattern * pat = FcNameParse((const FcChar8*)"FreeMono"); - FcConfigSubstitute (config, pat, FcMatchPattern); - FcDefaultSubstitute(pat); - FcResult result; - FcChar8 * file = NULL; - FcPattern * font = FcFontMatch(config, pat, &result); - - if (font && (FcPatternGetString(font, FC_FILE, 0, &file) == FcResultMatch)) - { - state.font = TTF_OpenFont((char *)file, 14); - if (!state.font) - { - DEBUG_ERROR("TTL_OpenFont Failed"); - return -1; - } - - state.alertFont = TTF_OpenFont((char *)file, 18); - if (!state.alertFont) - { - DEBUG_ERROR("TTL_OpenFont Failed"); - return -1; - } - } - else - { - DEBUG_ERROR("Failed to locate a font for text display"); - return -1; - } - FcPatternDestroy(pat); - LG_RendererParams lgrParams; - lgrParams.font = state.font; - lgrParams.alertFont = state.alertFont; - lgrParams.showFPS = params.showFPS; + lgrParams.showFPS = params.showFPS; Uint32 sdlFlags; if (params.forceRenderer) @@ -1089,7 +1040,6 @@ int run() close(state.shmFD); } - TTF_Quit(); SDL_Quit(); return 0; } diff --git a/client/renderers/opengl.c b/client/renderers/opengl.c index 47773c3d..431db3c0 100644 --- a/client/renderers/opengl.c +++ b/client/renderers/opengl.c @@ -34,6 +34,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA #include "debug.h" #include "utils.h" #include "lg-decoders.h" +#include "lg-fonts.h" #include "ll.h" #define BUFFER_COUNT 2 @@ -65,13 +66,13 @@ static struct Options defaultOptions = struct Alert { - bool ready; - bool useCloseFlag; + bool ready; + bool useCloseFlag; - SDL_Surface *text; - float r, g, b, a; - uint64_t timeout; - bool closeFlag; + LG_FontBitmap *text; + float r, g, b, a; + uint64_t timeout; + bool closeFlag; }; struct Inst @@ -88,6 +89,9 @@ struct Inst SDL_Point window; bool frameUpdate; + const LG_Font * font; + LG_FontObj fontObj, alertFontObj; + LG_Lock formatLock; LG_RendererFormat format; GLuint intFormat; @@ -177,7 +181,21 @@ bool opengl_create(void ** opaque, const LG_RendererParams params) LG_LOCK_INIT(this->syncLock ); LG_LOCK_INIT(this->mouseLock ); + this->font = LG_Fonts[0]; + if (!this->font->create(&this->fontObj, NULL, 14)) + { + DEBUG_ERROR("Unable to create the font renderer"); + return false; + } + + if (!this->font->create(&this->alertFontObj, NULL, 18)) + { + DEBUG_ERROR("Unable to create the font renderer"); + return false; + } + this->alerts = ll_new(); + return true; } @@ -229,11 +247,14 @@ void opengl_deinitialize(void * opaque) while(ll_shift(this->alerts, (void **)&alert)) { if (alert->text) - SDL_FreeSurface(alert->text); + this->font->release(this->alertFontObj, alert->text); free(alert); } ll_free(this->alerts); + if (this->font && this->fontObj) + this->font->destroy(this->fontObj); + free(this); } @@ -367,7 +388,6 @@ void opengl_on_alert(void * opaque, const LG_RendererAlert alert, const char * m struct Inst * this = (struct Inst *)opaque; struct Alert * a = malloc(sizeof(struct Alert)); memset(a, 0, sizeof(struct Alert)); - const SDL_Color color = {0xff, 0xff, 0xff}; switch(alert) { @@ -400,7 +420,7 @@ void opengl_on_alert(void * opaque, const LG_RendererAlert alert, const char * m break; } - if (!(a->text = TTF_RenderText_Blended(this->params.alertFont, message, color))) + if (!(a->text = this->font->render(this->alertFontObj, LG_FONT_BITMAP, 0xffffff00, message))) { DEBUG_ERROR("Failed to render alert text: %s", TTF_GetError()); free(a); @@ -416,21 +436,21 @@ void opengl_on_alert(void * opaque, const LG_RendererAlert alert, const char * m ll_push(this->alerts, a); } -void surface_to_texture(SDL_Surface * surface, GLuint texture) +void bitmap_to_texture(LG_FontBitmap * bitmap, GLuint texture) { - glBindTexture(GL_TEXTURE_2D , texture ); - glPixelStorei(GL_UNPACK_ALIGNMENT , 4 ); - glPixelStorei(GL_UNPACK_ROW_LENGTH, surface->w); + glBindTexture(GL_TEXTURE_2D , texture ); + glPixelStorei(GL_UNPACK_ALIGNMENT , 4 ); + glPixelStorei(GL_UNPACK_ROW_LENGTH, bitmap->width); glTexImage2D( GL_TEXTURE_2D, 0, - surface->format->BytesPerPixel, - surface->w, - surface->h, + bitmap->bpp, + bitmap->width, + bitmap->height, 0, GL_BGRA, GL_UNSIGNED_BYTE, - surface->pixels + bitmap->pixels ); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); @@ -516,23 +536,23 @@ bool opengl_render(void * opaque, SDL_Window * window) const float avgFPS = 1000.0f / (((float)this->renderTime / this->frameCount ) / 1e6f); const float renderFPS = 1000.0f / (((float)this->renderTime / this->renderCount) / 1e6f); snprintf(str, sizeof(str), "UPS: %8.4f, FPS: %8.4f", avgFPS, renderFPS); - const SDL_Color color = {0xff, 0xff, 0xff}; - SDL_Surface *textSurface = NULL; - if (!(textSurface = TTF_RenderText_Blended(this->params.font, str, color))) + + LG_FontBitmap *textSurface = NULL; + if (!(textSurface = this->font->render(this->fontObj, LG_FONT_BITMAP, 0xffffff00, str))) { - DEBUG_ERROR("Failed to render text: %s", TTF_GetError()); + DEBUG_ERROR("Failed to render text"); LG_UNLOCK(this->formatLock); return false; } - surface_to_texture(textSurface, this->textures[FPS_TEXTURE]); + bitmap_to_texture(textSurface, this->textures[FPS_TEXTURE]); this->fpsRect.x = 5; this->fpsRect.y = 5; - this->fpsRect.w = textSurface->w; - this->fpsRect.h = textSurface->h; + this->fpsRect.w = textSurface->width; + this->fpsRect.h = textSurface->height; - SDL_FreeSurface(textSurface); + this->font->release(this->fontObj, textSurface); this->renderTime = 0; this->frameCount = 0; @@ -593,12 +613,12 @@ bool opengl_render(void * opaque, SDL_Window * window) { if (!alert->ready) { - surface_to_texture(alert->text, this->textures[ALERT_TEXTURE]); + bitmap_to_texture(alert->text, this->textures[ALERT_TEXTURE]); glNewList(this->alertList, GL_COMPILE); const int p = 4; - const int w = alert->text->w + p * 2; - const int h = alert->text->h + p * 2; + const int w = alert->text->width + p * 2; + const int h = alert->text->height + p * 2; glTranslatef(-(w / 2), -(h / 2), 0.0f); glEnable(GL_BLEND); glDisable(GL_TEXTURE_2D); @@ -614,10 +634,10 @@ bool opengl_render(void * opaque, SDL_Window * window) glColor4f(1.0f, 1.0f, 1.0f, 1.0f); glTranslatef(p, p, 0.0f); glBegin(GL_TRIANGLE_STRIP); - glTexCoord2f(0.0f, 0.0f); glVertex2i(0 , 0 ); - glTexCoord2f(1.0f, 0.0f); glVertex2i(alert->text->w, 0 ); - glTexCoord2f(0.0f, 1.0f); glVertex2i(0 , alert->text->h); - glTexCoord2f(1.0f, 1.0f); glVertex2i(alert->text->w, alert->text->h); + glTexCoord2f(0.0f, 0.0f); glVertex2i(0 , 0 ); + glTexCoord2f(1.0f, 0.0f); glVertex2i(alert->text->width, 0 ); + glTexCoord2f(0.0f, 1.0f); glVertex2i(0 , alert->text->height); + glTexCoord2f(1.0f, 1.0f); glVertex2i(alert->text->width, alert->text->height); glEnd(); glBindTexture(GL_TEXTURE_2D, 0); glDisable(GL_BLEND); @@ -627,7 +647,7 @@ bool opengl_render(void * opaque, SDL_Window * window) alert->timeout = microtime() + 2*1000000; alert->ready = true; - SDL_FreeSurface(alert->text); + this->font->release(this->fontObj, alert->text); alert->text = NULL; alert->ready = true; }