From 2211dc73e18206ea2c4d8dcd343ae3d0acbf95c6 Mon Sep 17 00:00:00 2001 From: Themaister Date: Sun, 23 Jan 2011 00:27:20 +0100 Subject: [PATCH] Progress on font rendering. :) --- Makefile | 6 ++ driver.h | 2 +- gfx/fonts.c | 10 ++- gfx/gl.c | 155 ++++++++++++++++++++++++++++++++++++++++++-- qb/config.libs.sh | 4 +- qb/config.params.sh | 1 + ssnes.c | 14 ++-- 7 files changed, 172 insertions(+), 20 deletions(-) diff --git a/Makefile b/Makefile index dd30da2245..05787fe883 100644 --- a/Makefile +++ b/Makefile @@ -68,6 +68,12 @@ ifeq ($(HAVE_FILTER), 1) OBJ += hqflt/snes_ntsc/snes_ntsc.o endif +ifeq ($(HAVE_FREETYPE), 1) + OBJ += gfx/fonts.o + LIBS += $(FREETYPE_LIBS) + DEFINES += $(FREETYPE_CFLAGS) +endif + ifeq ($(HAVE_FFMPEG), 1) OBJ += record/ffemu.o LIBS += $(AVCODEC_LIBS) $(AVCORE_LIBS) $(AVFORMAT_LIBS) $(AVUTIL_LIBS) $(SWSCALE_LIBS) diff --git a/driver.h b/driver.h index e2f3decfcf..19b95ef9f0 100644 --- a/driver.h +++ b/driver.h @@ -99,7 +99,7 @@ typedef struct video_driver { void* (*init)(video_info_t *video, const input_driver_t **input, void **input_data); // Should the video driver act as an input driver as well? :) The video init might preinitialize an input driver to override the settings in case the video driver relies on input driver for event handling, e.g. - bool (*frame)(void* data, const uint16_t* frame, int width, int height, int pitch); + bool (*frame)(void* data, const uint16_t* frame, unsigned width, unsigned height, unsigned pitch, const char *msg); // msg is for showing a message on the screen along with the video frame. void (*set_nonblock_state)(void* data, bool toggle); // Should we care about syncing to vblank? Fast forwarding. // Is the window still active? bool (*alive)(void *data); diff --git a/gfx/fonts.c b/gfx/fonts.c index 27ff6cd5c6..49b7983a56 100644 --- a/gfx/fonts.c +++ b/gfx/fonts.c @@ -45,11 +45,7 @@ font_renderer_t *font_renderer_new(const char *font_path, unsigned font_size) if (err) goto error; - err = FT_Set_Char_Size(handle->face, 0, 64*64, 1024, 1024); - if (err) - goto error; - - err = FT_Set_Pixel_Sizes(handle->face, 0, 64); + err = FT_Set_Pixel_Sizes(handle->face, 0, font_size); if (err) goto error; @@ -82,6 +78,8 @@ void font_renderer_msg(font_renderer_t *handle, const char *msg, struct font_out struct font_output *tmp = calloc(1, sizeof(*tmp)); assert(tmp); + //fprintf(stderr, "Char: %c, off_x: %d, off_y: %d, bmp_left: %d, bmp_top: %d\n", msg[i], off_x, off_y, slot->bitmap_left, slot->bitmap_top); + tmp->output = malloc(slot->bitmap.pitch * slot->bitmap.rows); assert(tmp->output); memcpy(tmp->output, slot->bitmap.buffer, slot->bitmap.pitch * slot->bitmap.rows); @@ -90,7 +88,7 @@ void font_renderer_msg(font_renderer_t *handle, const char *msg, struct font_out tmp->height = slot->bitmap.rows; tmp->pitch = slot->bitmap.pitch; tmp->off_x = off_x + slot->bitmap_left; - tmp->off_y = off_y - slot->bitmap_top; + tmp->off_y = off_y + slot->bitmap_top - slot->bitmap.rows; tmp->next = NULL; if (i == 0) diff --git a/gfx/gl.c b/gfx/gl.c index 9074846b4c..98d24666d4 100644 --- a/gfx/gl.c +++ b/gfx/gl.c @@ -51,6 +51,10 @@ #include "gl_common.h" +#ifdef HAVE_FREETYPE +#include "fonts.h" +#endif + static const GLfloat vertexes[] = { 0, 0, 0, 0, 1, 0, @@ -65,7 +69,6 @@ static const GLfloat tex_coords[] = { 1, 1 }; -static bool keep_aspect = true; typedef struct gl { bool vsync; @@ -74,6 +77,7 @@ typedef struct gl bool should_resize; bool quitting; + bool keep_aspect; unsigned win_width; unsigned win_height; @@ -83,9 +87,15 @@ typedef struct gl unsigned last_height; unsigned tex_w, tex_h; GLfloat tex_coords[8]; + +#ifdef HAVE_FREETYPE + font_renderer_t *font; + GLuint font_tex; +#endif + } gl_t; - +////////////////// Shaders static inline bool gl_shader_init(void) { if (strlen(g_settings.video.cg_shader_path) > 0 && strlen(g_settings.video.bsnes_shader_path) > 0) @@ -104,6 +114,32 @@ static inline bool gl_shader_init(void) return true; } +/* +static inline void gl_shader_deactivate(void) +{ +#ifdef HAVE_CG + gl_cg_deactivate(); +#endif + +#ifdef HAVE_XML + gl_glsl_deactivate(); +#endif +} +*/ + +/* +static inline void gl_shader_activate(void) +{ +#ifdef HAVE_CG + gl_cg_activate(); +#endif + +#ifdef HAVE_XML + gl_glsl_activate(); +#endif +} +*/ + static inline void gl_shader_deinit(void) { #ifdef HAVE_CG @@ -138,6 +174,109 @@ static inline void gl_shader_set_params(unsigned width, unsigned height, gl_glsl_set_params(width, height, tex_width, tex_height, out_width, out_height); #endif } +/////////////////// + +//////////////// Message rendering +static inline void gl_init_font(gl_t *gl, const char *font_path) +{ +#ifdef HAVE_FREETYPE + gl->font = font_renderer_new(font_path, 16); + if (gl->font) + { + glGenTextures(1, &gl->font_tex); + glBindTexture(GL_TEXTURE_2D, gl->font_tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glBindTexture(GL_TEXTURE_2D, gl->texture); + } + else + SSNES_WARN("Couldn't init font renderer...\n"); +#endif +} + +static inline void gl_deinit_font(gl_t *gl) +{ +#ifdef HAVE_FREETYPE + if (gl->font) + { + font_renderer_free(gl->font); + glDeleteTextures(1, &gl->font_tex); + } +#endif +} + +static inline unsigned get_alignment(unsigned pitch) +{ + if (pitch & 1) + return 1; + if (pitch & 2) + return 2; + if (pitch & 4) + return 4; + return 8; +} + +static void gl_render_msg(gl_t *gl, const char *msg) +{ +#ifdef HAVE_FREETYPE + if (!gl->font) + return; + + GLfloat font_vertex[12]; + + // Deactivate custom shaders. Enable the font texture. + //gl_shader_deactivate(); + glBindTexture(GL_TEXTURE_2D, gl->font_tex); + glVertexPointer(3, GL_FLOAT, 3 * sizeof(GLfloat), font_vertex); + glTexCoordPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), tex_coords); // Use the static one (uses whole texture). + + // Need blending. + // Using fixed function pipeline here since we cannot guarantee presence of shaders (would be kinda overkill anyways). + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR); + + struct font_output_list out; + font_renderer_msg(gl->font, msg, &out); + struct font_output *head = out.head; + + while (head != NULL) + { + GLfloat lx = (GLfloat)head->off_x / gl->vp_width + 0.200; + GLfloat hx = (GLfloat)(head->off_x + head->width) / gl->vp_width + 0.200; + GLfloat ly = (GLfloat)head->off_y / gl->vp_height + 0.200; + GLfloat hy = (GLfloat)(head->off_y + head->height) / gl->vp_height + 0.200; + + font_vertex[0] = lx; + font_vertex[1] = ly; + font_vertex[3] = lx; + font_vertex[4] = hy; + font_vertex[6] = hx; + font_vertex[7] = hy; + font_vertex[9] = hx; + font_vertex[10] = ly; + + glPixelStorei(GL_UNPACK_ALIGNMENT, get_alignment(head->pitch)); + glPixelStorei(GL_UNPACK_ROW_LENGTH, head->pitch); + glTexImage2D(GL_TEXTURE_2D, + 0, GL_RGBA, head->width, head->height, 0, GL_LUMINANCE, + GL_UNSIGNED_BYTE, head->output); + + head = head->next; + glDrawArrays(GL_QUADS, 0, 4); + } + font_renderer_free_output(&out); + + // Go back to old rendering path. + glTexCoordPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), gl->tex_coords); + glVertexPointer(3, GL_FLOAT, 3 * sizeof(GLfloat), vertexes); + glBindTexture(GL_TEXTURE_2D, gl->texture); + glDisable(GL_BLEND); + //gl_shader_activate(); +#endif +} +////////////// static void set_viewport(gl_t *gl) { @@ -145,7 +284,7 @@ static void set_viewport(gl_t *gl) glLoadIdentity(); GLuint out_width = gl->win_width, out_height = gl->win_height; - if ( keep_aspect ) + if (gl->keep_aspect) { float desired_aspect = g_settings.video.aspect_ratio; float device_aspect = (float)gl->win_width / gl->win_height; @@ -212,7 +351,7 @@ static void show_fps(void) frames++; } -static bool gl_frame(void *data, const uint16_t* frame, int width, int height, int pitch) +static bool gl_frame(void *data, const uint16_t* frame, unsigned width, unsigned height, unsigned pitch, const char *msg) { gl_t *gl = data; @@ -231,6 +370,7 @@ static bool gl_frame(void *data, const uint16_t* frame, int width, int height, i { gl->last_width = width; gl->last_height = height; + glPixelStorei(GL_UNPACK_ALIGNMENT, get_alignment(pitch)); glPixelStorei(GL_UNPACK_ROW_LENGTH, gl->tex_w); uint8_t *tmp = calloc(1, gl->tex_w * gl->tex_h * sizeof(uint16_t)); glTexSubImage2D(GL_TEXTURE_2D, @@ -255,6 +395,8 @@ static bool gl_frame(void *data, const uint16_t* frame, int width, int height, i GL_UNSIGNED_SHORT_1_5_5_5_REV, frame); glDrawArrays(GL_QUADS, 0, 4); + gl_render_msg(gl, "hei paa deg"); + show_fps(); glFlush(); SDL_GL_SwapBuffers(); @@ -266,6 +408,7 @@ static void gl_free(void *data) { gl_t *gl = data; + gl_deinit_font(gl); gl_shader_deinit(); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); @@ -325,6 +468,7 @@ static void* gl_init(video_info_t *video, const input_driver_t **input, void **i gl->win_width = video->width; gl->win_height = video->height; gl->vsync = video->vsync; + gl->keep_aspect = video->force_aspect; set_viewport(gl); if (!gl_shader_init()) @@ -338,7 +482,6 @@ static void* gl_init(video_info_t *video, const input_driver_t **input, void **i // Remove that ugly mouse :D SDL_ShowCursor(SDL_DISABLE); - keep_aspect = video->force_aspect; if ( video->smooth ) gl->tex_filter = GL_LINEAR; else @@ -394,6 +537,8 @@ static void* gl_init(video_info_t *video, const input_driver_t **input, void **i } else *input = NULL; + + gl_init_font(gl, "/usr/share/fonts/TTF/DroidSans.ttf"); if (!gl_check_error()) { diff --git a/qb/config.libs.sh b/qb/config.libs.sh index 12d9ce462b..2aad11cbb1 100644 --- a/qb/config.libs.sh +++ b/qb/config.libs.sh @@ -43,8 +43,10 @@ check_critical SRC "Cannot find libsamplerate." check_lib DYNAMIC -ldl dlopen +check_pkgconf FREETYPE freetype2 + # Creates config.mk and config.h. -VARS="ALSA OSS AL RSOUND ROAR JACK SDL FILTER CG XML DYNAMIC FFMPEG AVCODEC AVFORMAT AVCORE AVUTIL SWSCALE SRC CONFIGFILE" +VARS="ALSA OSS AL RSOUND ROAR JACK SDL FILTER CG XML DYNAMIC FFMPEG AVCODEC AVFORMAT AVCORE AVUTIL SWSCALE SRC CONFIGFILE FREETYPE" create_config_make config.mk $VARS create_config_header config.h $VARS diff --git a/qb/config.params.sh b/qb/config.params.sh index e49d11d877..f86b0326d6 100644 --- a/qb/config.params.sh +++ b/qb/config.params.sh @@ -20,3 +20,4 @@ add_command_line_enable RSOUND "Enable RSound support" auto add_command_line_enable ROAR "Enable RoarAudio support" auto add_command_line_enable AL "Enable OpenAL support" auto add_command_line_enable JACK "Enable JACK support" auto +add_command_line_enable FREETYPE "Enable FreeType support" auto diff --git a/ssnes.c b/ssnes.c index f01fcbfa7a..8e9dbb8da9 100644 --- a/ssnes.c +++ b/ssnes.c @@ -108,35 +108,35 @@ static void video_frame(const uint16_t *data, unsigned width, unsigned height) { case FILTER_HQ2X: ProcessHQ2x(output, output_filter); - if ( !driver.video->frame(driver.video_data, output_filter, width << 1, height << 1, width << 2) ) + if (!driver.video->frame(driver.video_data, output_filter, width << 1, height << 1, width << 2, NULL)) g_extern.video_active = false; break; case FILTER_HQ4X: ProcessHQ4x(output, output_filter); - if ( !driver.video->frame(driver.video_data, output_filter, width << 2, height << 2, width << 3) ) + if (!driver.video->frame(driver.video_data, output_filter, width << 2, height << 2, width << 3, NULL)) g_extern.video_active = false; break; case FILTER_GRAYSCALE: grayscale_filter(output, width, height); - if ( !driver.video->frame(driver.video_data, output, width, height, width << 1) ) + if (!driver.video->frame(driver.video_data, output, width, height, width << 1, NULL)) g_extern.video_active = false; break; case FILTER_BLEED: bleed_filter(output, width, height); - if ( !driver.video->frame(driver.video_data, output, width, height, width << 1) ) + if (!driver.video->frame(driver.video_data, output, width, height, width << 1, NULL)) g_extern.video_active = false; break; case FILTER_NTSC: ntsc_filter(output_filter, output, width, height); - if ( !driver.video->frame(driver.video_data, output_filter, SNES_NTSC_OUT_WIDTH(width), height, SNES_NTSC_OUT_WIDTH(width) << 1) ) + if (!driver.video->frame(driver.video_data, output_filter, SNES_NTSC_OUT_WIDTH(width), height, SNES_NTSC_OUT_WIDTH(width) << 1, NULL)) g_extern.video_active = false; break; default: - if ( !driver.video->frame(driver.video_data, data, width, height, (height == 448 || height == 478) ? 1024 : 2048) ) + if (!driver.video->frame(driver.video_data, data, width, height, (height == 448 || height == 478) ? 1024 : 2048, NULL)) g_extern.video_active = false; } #else - if ( !driver.video->frame(driver.video_data, data, width, height, (height == 448 || height == 478) ? 1024 : 2048) ) + if (!driver.video->frame(driver.video_data, data, width, height, (height == 448 || height == 478) ? 1024 : 2048, NULL)) g_extern.video_active = false; #endif }