[client] font: implemented font ABI and updated OpenGL to use it

This commit is contained in:
Geoffrey McRae 2018-11-20 04:38:11 +11:00
parent d235d076c4
commit 0ed9301ed9
7 changed files with 313 additions and 86 deletions

View File

@ -61,6 +61,7 @@ set(SOURCES
renderers/egl_shader.c renderers/egl_shader.c
renderers/egl_texture.c renderers/egl_texture.c
renderers/egl_model.c renderers/egl_model.c
fonts/sdl.c
) )
add_executable(looking-glass-client ${SOURCES}) add_executable(looking-glass-client ${SOURCES})

164
client/fonts/sdl.c Normal file
View File

@ -0,0 +1,164 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
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 <stdlib.h>
#include <stdbool.h>
#include "lg-font.h"
#include "debug.h"
#include <SDL2/SDL_ttf.h>
#include <fontconfig/fontconfig.h>
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
};

61
client/lg-font.h Normal file
View File

@ -0,0 +1,61 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
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 <stdint.h>
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;

31
client/lg-fonts.h Normal file
View File

@ -0,0 +1,31 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
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)

View File

@ -62,8 +62,8 @@ typedef LG_RendererOpt * LG_RendererOptions;
typedef struct LG_RendererParams typedef struct LG_RendererParams
{ {
TTF_Font * font; // TTF_Font * font;
TTF_Font * alertFont; // TTF_Font * alertFont;
bool showFPS; bool showFPS;
} }
LG_RendererParams; LG_RendererParams;

View File

@ -21,7 +21,6 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#include <signal.h> #include <signal.h>
#include <SDL2/SDL.h> #include <SDL2/SDL.h>
#include <SDL2/SDL_syswm.h> #include <SDL2/SDL_syswm.h>
#include <SDL2/SDL_ttf.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <pwd.h> #include <pwd.h>
@ -35,7 +34,6 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#include <stdbool.h> #include <stdbool.h>
#include <assert.h> #include <assert.h>
#include <libconfig.h> #include <libconfig.h>
#include <fontconfig/fontconfig.h>
#include "debug.h" #include "debug.h"
#include "utils.h" #include "utils.h"
@ -51,8 +49,6 @@ struct AppState
bool started; bool started;
bool keyDown[SDL_NUM_SCANCODES]; bool keyDown[SDL_NUM_SCANCODES];
TTF_Font * font;
TTF_Font * alertFont;
bool haveSrcSize; bool haveSrcSize;
int windowW, windowH; int windowW, windowH;
SDL_Point srcSize; SDL_Point srcSize;
@ -769,52 +765,7 @@ int run()
// SIGINT and the user sending a close event, such as ALT+F4 // SIGINT and the user sending a close event, such as ALT+F4
signal(SIGINT, intHandler); 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; LG_RendererParams lgrParams;
lgrParams.font = state.font;
lgrParams.alertFont = state.alertFont;
lgrParams.showFPS = params.showFPS; lgrParams.showFPS = params.showFPS;
Uint32 sdlFlags; Uint32 sdlFlags;
@ -1089,7 +1040,6 @@ int run()
close(state.shmFD); close(state.shmFD);
} }
TTF_Quit();
SDL_Quit(); SDL_Quit();
return 0; return 0;
} }

View File

@ -34,6 +34,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#include "debug.h" #include "debug.h"
#include "utils.h" #include "utils.h"
#include "lg-decoders.h" #include "lg-decoders.h"
#include "lg-fonts.h"
#include "ll.h" #include "ll.h"
#define BUFFER_COUNT 2 #define BUFFER_COUNT 2
@ -68,7 +69,7 @@ struct Alert
bool ready; bool ready;
bool useCloseFlag; bool useCloseFlag;
SDL_Surface *text; LG_FontBitmap *text;
float r, g, b, a; float r, g, b, a;
uint64_t timeout; uint64_t timeout;
bool closeFlag; bool closeFlag;
@ -88,6 +89,9 @@ struct Inst
SDL_Point window; SDL_Point window;
bool frameUpdate; bool frameUpdate;
const LG_Font * font;
LG_FontObj fontObj, alertFontObj;
LG_Lock formatLock; LG_Lock formatLock;
LG_RendererFormat format; LG_RendererFormat format;
GLuint intFormat; GLuint intFormat;
@ -177,7 +181,21 @@ bool opengl_create(void ** opaque, const LG_RendererParams params)
LG_LOCK_INIT(this->syncLock ); LG_LOCK_INIT(this->syncLock );
LG_LOCK_INIT(this->mouseLock ); 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(); this->alerts = ll_new();
return true; return true;
} }
@ -229,11 +247,14 @@ void opengl_deinitialize(void * opaque)
while(ll_shift(this->alerts, (void **)&alert)) while(ll_shift(this->alerts, (void **)&alert))
{ {
if (alert->text) if (alert->text)
SDL_FreeSurface(alert->text); this->font->release(this->alertFontObj, alert->text);
free(alert); free(alert);
} }
ll_free(this->alerts); ll_free(this->alerts);
if (this->font && this->fontObj)
this->font->destroy(this->fontObj);
free(this); 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 Inst * this = (struct Inst *)opaque;
struct Alert * a = malloc(sizeof(struct Alert)); struct Alert * a = malloc(sizeof(struct Alert));
memset(a, 0, sizeof(struct Alert)); memset(a, 0, sizeof(struct Alert));
const SDL_Color color = {0xff, 0xff, 0xff};
switch(alert) switch(alert)
{ {
@ -400,7 +420,7 @@ void opengl_on_alert(void * opaque, const LG_RendererAlert alert, const char * m
break; 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()); DEBUG_ERROR("Failed to render alert text: %s", TTF_GetError());
free(a); free(a);
@ -416,21 +436,21 @@ void opengl_on_alert(void * opaque, const LG_RendererAlert alert, const char * m
ll_push(this->alerts, a); 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 ); glBindTexture(GL_TEXTURE_2D , texture );
glPixelStorei(GL_UNPACK_ALIGNMENT , 4 ); glPixelStorei(GL_UNPACK_ALIGNMENT , 4 );
glPixelStorei(GL_UNPACK_ROW_LENGTH, surface->w); glPixelStorei(GL_UNPACK_ROW_LENGTH, bitmap->width);
glTexImage2D( glTexImage2D(
GL_TEXTURE_2D, GL_TEXTURE_2D,
0, 0,
surface->format->BytesPerPixel, bitmap->bpp,
surface->w, bitmap->width,
surface->h, bitmap->height,
0, 0,
GL_BGRA, GL_BGRA,
GL_UNSIGNED_BYTE, GL_UNSIGNED_BYTE,
surface->pixels bitmap->pixels
); );
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 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 avgFPS = 1000.0f / (((float)this->renderTime / this->frameCount ) / 1e6f);
const float renderFPS = 1000.0f / (((float)this->renderTime / this->renderCount) / 1e6f); const float renderFPS = 1000.0f / (((float)this->renderTime / this->renderCount) / 1e6f);
snprintf(str, sizeof(str), "UPS: %8.4f, FPS: %8.4f", avgFPS, renderFPS); snprintf(str, sizeof(str), "UPS: %8.4f, FPS: %8.4f", avgFPS, renderFPS);
const SDL_Color color = {0xff, 0xff, 0xff};
SDL_Surface *textSurface = NULL; LG_FontBitmap *textSurface = NULL;
if (!(textSurface = TTF_RenderText_Blended(this->params.font, str, color))) 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); LG_UNLOCK(this->formatLock);
return false; return false;
} }
surface_to_texture(textSurface, this->textures[FPS_TEXTURE]); bitmap_to_texture(textSurface, this->textures[FPS_TEXTURE]);
this->fpsRect.x = 5; this->fpsRect.x = 5;
this->fpsRect.y = 5; this->fpsRect.y = 5;
this->fpsRect.w = textSurface->w; this->fpsRect.w = textSurface->width;
this->fpsRect.h = textSurface->h; this->fpsRect.h = textSurface->height;
SDL_FreeSurface(textSurface); this->font->release(this->fontObj, textSurface);
this->renderTime = 0; this->renderTime = 0;
this->frameCount = 0; this->frameCount = 0;
@ -593,12 +613,12 @@ bool opengl_render(void * opaque, SDL_Window * window)
{ {
if (!alert->ready) 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); glNewList(this->alertList, GL_COMPILE);
const int p = 4; const int p = 4;
const int w = alert->text->w + p * 2; const int w = alert->text->width + p * 2;
const int h = alert->text->h + p * 2; const int h = alert->text->height + p * 2;
glTranslatef(-(w / 2), -(h / 2), 0.0f); glTranslatef(-(w / 2), -(h / 2), 0.0f);
glEnable(GL_BLEND); glEnable(GL_BLEND);
glDisable(GL_TEXTURE_2D); glDisable(GL_TEXTURE_2D);
@ -615,9 +635,9 @@ bool opengl_render(void * opaque, SDL_Window * window)
glTranslatef(p, p, 0.0f); glTranslatef(p, p, 0.0f);
glBegin(GL_TRIANGLE_STRIP); glBegin(GL_TRIANGLE_STRIP);
glTexCoord2f(0.0f, 0.0f); glVertex2i(0 , 0 ); glTexCoord2f(0.0f, 0.0f); glVertex2i(0 , 0 );
glTexCoord2f(1.0f, 0.0f); glVertex2i(alert->text->w, 0 ); glTexCoord2f(1.0f, 0.0f); glVertex2i(alert->text->width, 0 );
glTexCoord2f(0.0f, 1.0f); glVertex2i(0 , alert->text->h); glTexCoord2f(0.0f, 1.0f); glVertex2i(0 , alert->text->height);
glTexCoord2f(1.0f, 1.0f); glVertex2i(alert->text->w, alert->text->h); glTexCoord2f(1.0f, 1.0f); glVertex2i(alert->text->width, alert->text->height);
glEnd(); glEnd();
glBindTexture(GL_TEXTURE_2D, 0); glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_BLEND); glDisable(GL_BLEND);
@ -627,7 +647,7 @@ bool opengl_render(void * opaque, SDL_Window * window)
alert->timeout = microtime() + 2*1000000; alert->timeout = microtime() + 2*1000000;
alert->ready = true; alert->ready = true;
SDL_FreeSurface(alert->text); this->font->release(this->fontObj, alert->text);
alert->text = NULL; alert->text = NULL;
alert->ready = true; alert->ready = true;
} }