reuse font code for Raspberry Pi graphics core

also move bcm_host_init() to graphics core
This commit is contained in:
Toad King 2012-06-19 12:41:06 -04:00
parent 4f3e54529b
commit f21371f166
4 changed files with 121 additions and 334 deletions

View File

@ -93,8 +93,12 @@ void font_renderer_msg(font_renderer_t *handle, const char *msg, struct font_out
tmp->width = slot->bitmap.width;
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 - slot->bitmap.rows;
tmp->advance_x = slot->advance.x >> 6;
tmp->advance_y = slot->advance.y >> 6;
tmp->char_off_x = slot->bitmap_left;
tmp->char_off_y = slot->bitmap_top - slot->bitmap.rows;
tmp->off_x = off_x + tmp->char_off_x;
tmp->off_y = off_y + tmp->char_off_y;
tmp->next = NULL;
if (i == 0)
@ -147,6 +151,8 @@ static const char *font_paths[] = {
static const char *font_paths[] = {
"/usr/share/fonts/TTF/DejaVuSansMono.ttf",
"/usr/share/fonts/TTF/DejaVuSans.ttf",
"/usr/share/fonts/truetype/ttf-dejavu/DejaVuSansMono.ttf",
"/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf",
#endif
"osd-font.ttf", // Magic font to search for, useful for distribution.
};

View File

@ -26,6 +26,7 @@ struct font_output
uint8_t *output; // 8-bit intensity.
unsigned width, height, pitch;
int off_x, off_y;
int advance_x, advance_y, char_off_x, char_off_y; // for advanced font rendering
struct font_output *next; // linked list.
};

437
gfx/rpi.c
View File

@ -1,3 +1,19 @@
/* RetroArch - A frontend for libretro.
* Copyright (C) 2010-2012 - Hans-Kristian Arntzen
* Copyright (C) 2012 - Michael Lelli
*
* RetroArch 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 Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch 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 RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include <assert.h>
#include <math.h>
#include <unistd.h>
@ -14,10 +30,7 @@ typedef struct linuxraw_input linuxraw_input_t;
#include "../driver.h"
#ifdef HAVE_FREETYPE
#include <string.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_OUTLINE_H
#include "fonts/fonts.h"
#include "../file.h"
#endif
@ -43,9 +56,8 @@ typedef struct {
#ifdef HAVE_FREETYPE
char *mLastMsg;
uint32_t mFontHeight;
FT_Library ftLib;
FT_Face ftFace;
VGFont mFont;
font_renderer_t *mFontRenderer;
bool mFontsOn;
VGuint mMsgLength;
VGuint mGlyphIndices[1024];
@ -89,6 +101,8 @@ static void *rpi_init(const video_info_t *video, const input_driver_t **input, v
EGLConfig config;
bcm_host_init();
// get an EGL display connection
rpi->mDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
assert(rpi->mDisplay != EGL_NO_DISPLAY);
@ -160,87 +174,41 @@ static void *rpi_init(const video_info_t *video, const input_driver_t **input, v
rpi->mImage = vgCreateImage(video->rgb32 ? VG_sABGR_8888 : VG_sXBGR_8888, rpi->mTextureWidth, rpi->mTextureHeight, video->smooth ? VG_IMAGE_QUALITY_BETTER : VG_IMAGE_QUALITY_NONANTIALIASED);
rpi_set_nonblock_state(rpi, !video->vsync);
if (isatty(0))
linuxraw_input_t *linuxraw_input = (linuxraw_input_t *)input_linuxraw.init();
if (linuxraw_input)
{
const linuxraw_input_t *linuxraw_input = (const linuxraw_input_t *) input_linuxraw.init();
if (linuxraw_input)
{
*input = (const input_driver_t *) &input_linuxraw;
*input_data = linuxraw_input;
}
*input = (const input_driver_t *)&input_linuxraw;
*input_data = linuxraw_input;
}
#ifdef HAVE_FREETYPE
static const char *font_paths[] = {
"/usr/share/fonts/truetype/ttf-dejavu/DejaVuSansMono.ttf",
"/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf",
"osd-font.ttf", // Magic font to search for, useful for distribution.
};
const char *font = g_settings.video.font_path;
if (!g_settings.video.font_enable)
goto fail;
if (!path_file_exists(font))
if (g_settings.video.font_enable)
{
font = NULL;
rpi->mFont = vgCreateFont(0);
rpi->mFontHeight = g_settings.video.font_size * (g_settings.video.font_scale ? (float) rpi->mScreenWidth / 1280.0f : 1.0f);
for (int i = 0; i < sizeof(font_paths) / sizeof(font_paths[0]); i++)
const char *path = g_settings.video.font_path;
if (!*path || !path_file_exists(path))
path = font_renderer_get_default_font();
rpi->mFontRenderer = font_renderer_new(path, rpi->mFontHeight);
if (rpi->mFont != VG_INVALID_HANDLE && rpi->mFontRenderer)
{
if (path_file_exists(font_paths[i]))
{
font = font_paths[i];
break;
}
rpi->mFontsOn = true;
rpi->mPaintFg = vgCreatePaint();
rpi->mPaintBg = vgCreatePaint();
VGfloat paintFg[] = { g_settings.video.msg_color_r, g_settings.video.msg_color_g, g_settings.video.msg_color_b, 1.0f };
VGfloat paintBg[] = { g_settings.video.msg_color_r / 2.0f, g_settings.video.msg_color_g / 2.0f, g_settings.video.msg_color_b / 2.0f, 0.5f };
vgSetParameteri(rpi->mPaintFg, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR);
vgSetParameterfv(rpi->mPaintFg, VG_PAINT_COLOR, 4, paintFg);
vgSetParameteri(rpi->mPaintBg, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR);
vgSetParameterfv(rpi->mPaintBg, VG_PAINT_COLOR, 4, paintBg);
}
}
if (!font)
{
RARCH_WARN("No font found, messages disabled...\n");
goto fail;
}
if (FT_Init_FreeType(&rpi->ftLib)) {
RARCH_WARN("failed to initialize freetype\n");
goto fail;
}
if (FT_New_Face(rpi->ftLib, font, 0, &rpi->ftFace)) {
RARCH_WARN("failed to load %s\n", font);
goto fail;
}
if (FT_Select_Charmap(rpi->ftFace, FT_ENCODING_UNICODE)) {
RARCH_WARN("failed to select an unicode charmap\n");
goto fail;
}
uint32_t size = g_settings.video.font_size * (g_settings.video.font_scale ? (float) rpi->mScreenWidth / 1280.0f : 1.0f);
rpi->mFontHeight = size;
if (FT_Set_Pixel_Sizes(rpi->ftFace, size, size))
RARCH_WARN("failed to set pixel sizes\n");
rpi->mFont = vgCreateFont(0);
if (rpi->mFont)
rpi->mFontsOn = true;
rpi->mPaintFg = vgCreatePaint();
rpi->mPaintBg = vgCreatePaint();
VGfloat paintFg[] = { g_settings.video.msg_color_r, g_settings.video.msg_color_g, g_settings.video.msg_color_b, 1.0f };
VGfloat paintBg[] = { g_settings.video.msg_color_r / 2.0f, g_settings.video.msg_color_g / 2.0f, g_settings.video.msg_color_b / 2.0f, 0.5f };
vgSetParameteri(rpi->mPaintFg, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR);
vgSetParameterfv(rpi->mPaintFg, VG_PAINT_COLOR, 4, paintFg);
vgSetParameteri(rpi->mPaintBg, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR);
vgSetParameterfv(rpi->mPaintBg, VG_PAINT_COLOR, 4, paintBg);
fail:
#endif
return rpi;
@ -252,6 +220,16 @@ static void rpi_free(void *data)
vgDestroyImage(rpi->mImage);
#ifdef HAVE_FREETYPE
if (rpi->mFontsOn)
{
vgDestroyFont(rpi->mFont);
font_renderer_free(rpi->mFontRenderer);
vgDestroyPaint(rpi->mPaintFg);
vgDestroyPaint(rpi->mPaintBg);
}
#endif
// Release EGL resources
eglMakeCurrent(rpi->mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglDestroySurface(rpi->mDisplay, rpi->mSurface);
@ -263,254 +241,62 @@ static void rpi_free(void *data)
#ifdef HAVE_FREETYPE
// mostly adapted from example code in Mesa-3d
static int path_append(VGPath path, VGubyte segment, const FT_Vector **vectors)
static void rpi_render_message(rpi_t *rpi, const char *msg)
{
VGfloat coords[6];
int i, num_vectors;
free(rpi->mLastMsg);
rpi->mLastMsg = strdup(msg);
switch (segment)
if(rpi->mMsgLength)
{
case VG_MOVE_TO:
case VG_LINE_TO:
num_vectors = 1;
break;
case VG_QUAD_TO:
num_vectors = 2;
break;
case VG_CUBIC_TO:
num_vectors = 3;
break;
default:
return -1;
while (--rpi->mMsgLength)
vgClearGlyph(rpi->mFont, rpi->mMsgLength);
vgClearGlyph(rpi->mFont, 0);
}
struct font_output_list out;
font_renderer_msg(rpi->mFontRenderer, msg, &out);
struct font_output *head = out.head;
while (head)
{
if (rpi->mMsgLength >= 1024)
break;
VGfloat origin[2], escapement[2];
VGImage img;
escapement[0] = (VGfloat) (head->advance_x);
escapement[1] = (VGfloat) (head->advance_y);
origin[0] = (VGfloat) (-head->char_off_x);
origin[1] = (VGfloat) (head->char_off_y);
img = vgCreateImage(VG_A_8, head->width, head->height, VG_IMAGE_QUALITY_NONANTIALIASED);
// flip it
for (unsigned i = 0; i < head->height; i++)
vgImageSubData(img, head->output + head->pitch * i, head->pitch, VG_A_8, 0, head->height - i - 1, head->width, 1);
vgSetGlyphToImage(rpi->mFont, rpi->mMsgLength, img, origin, escapement);
vgDestroyImage(img);
rpi->mMsgLength++;
head = head->next;
}
for (i = 0; i < num_vectors; i++)
{
coords[2 * i + 0] = (float) vectors[i]->x / 64.0f;
coords[2 * i + 1] = (float) vectors[i]->y / 64.0f;
}
font_renderer_free_output(&out);
vgAppendPathData(path, 1, &segment, (const void *) coords);
return 0;
}
static int decompose_move_to(const FT_Vector *to, void *user)
{
VGPath path = (VGPath) (VGuint) user;
return path_append(path, VG_MOVE_TO, &to);
}
static int decompose_line_to(const FT_Vector *to, void *user)
{
VGPath path = (VGPath) (VGuint) user;
return path_append(path, VG_LINE_TO, &to);
}
static int decompose_conic_to(const FT_Vector *control, const FT_Vector *to, void *user)
{
VGPath path = (VGPath) (VGuint) user;
const FT_Vector *vectors[2] = { control, to };
return path_append(path, VG_QUAD_TO, vectors);
}
static int decompose_cubic_to(const FT_Vector *control1, const FT_Vector *control2, const FT_Vector *to, void *user)
{
VGPath path = (VGPath) (VGuint) user;
const FT_Vector *vectors[3] = { control1, control2, to };
return path_append(path, VG_CUBIC_TO, vectors);
}
static VGHandle convert_outline_glyph(rpi_t *rpi)
{
FT_GlyphSlot glyph = rpi->ftFace->glyph;
FT_Outline_Funcs funcs = {
decompose_move_to,
decompose_line_to,
decompose_conic_to,
decompose_cubic_to,
0, 0
};
VGHandle path;
path = vgCreatePath(VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F, 1.0f, 0.0f, 0, glyph->outline.n_points, VG_PATH_CAPABILITY_ALL);
if (FT_Outline_Decompose(&glyph->outline, &funcs, (void *) (VGuint) path))
{
vgDestroyPath(path);
path = VG_INVALID_HANDLE;
}
return path;
}
static VGint glyph_string_add_path(rpi_t *rpi, VGPath path, const VGfloat origin[2], VGfloat escapement[2])
{
if (rpi->mMsgLength >= 1024)
return -1;
if (rpi->mFont != VG_INVALID_HANDLE)
{
vgSetGlyphToPath(rpi->mFont, rpi->mMsgLength, path, VG_TRUE, origin, escapement);
return rpi->mMsgLength++;
}
return -1;
}
static VGHandle convert_bitmap_glyph(rpi_t *rpi)
{
FT_GlyphSlot glyph = rpi->ftFace->glyph;
VGImage image;
VGint width, height, stride;
unsigned char *data;
int i, j;
switch (glyph->bitmap.pixel_mode)
{
case FT_PIXEL_MODE_MONO:
case FT_PIXEL_MODE_GRAY:
break;
default:
return VG_INVALID_HANDLE;
break;
}
data = glyph->bitmap.buffer;
width = glyph->bitmap.width;
height = glyph->bitmap.rows;
stride = glyph->bitmap.pitch;
/* mono to gray, and flip if needed */
if (glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO)
{
data = malloc(width * height);
if (!data)
return VG_INVALID_HANDLE;
for (i = 0; i < height; i++)
{
unsigned char *dst = &data[width * i];
const unsigned char *src;
if (stride > 0)
src = glyph->bitmap.buffer + stride * (height - i - 1);
else
src = glyph->bitmap.buffer - stride * i;
for (j = 0; j < width; j++)
{
if (src[j / 8] & (1 << (7 - (j % 8))))
dst[j] = 0xff;
else
dst[j] = 0x0;
}
}
stride = -width;
}
image = vgCreateImage(VG_A_8, width, height, VG_IMAGE_QUALITY_NONANTIALIASED);
if (stride < 0)
{
stride = -stride;
vgImageSubData(image, data, stride, VG_A_8, 0, 0, width, height);
}
else
{
/* flip vertically */
for (i = 0; i < height; i++)
{
const unsigned char *row = data + stride * i;
vgImageSubData(image, row, stride, VG_A_8, 0, height - i - 1, width, 1);
}
}
if (data != glyph->bitmap.buffer)
free(data);
return (VGHandle) image;
}
static VGint glyph_string_add_image(rpi_t *rpi, VGImage image, const VGfloat origin[2], VGfloat escapement[2])
{
if (rpi->mMsgLength >= 1024)
return -1;
if (rpi->mFont != VG_INVALID_HANDLE)
{
vgSetGlyphToImage(rpi->mFont, rpi->mMsgLength, image, origin, escapement);
return rpi->mMsgLength++;
}
return -1;
for (unsigned i = 0; i < rpi->mMsgLength; i++)
rpi->mGlyphIndices[i] = i;
}
static void rpi_draw_message(rpi_t *rpi, const char *msg)
{
if (!rpi->mLastMsg || strcmp(rpi->mLastMsg, msg))
{
if (rpi->mLastMsg)
free(rpi->mLastMsg);
rpi->mLastMsg = strdup(msg);
if(rpi->mMsgLength)
while (--rpi->mMsgLength)
vgClearGlyph(rpi->mFont, rpi->mMsgLength);
for (int i = 0; msg[i]; i++)
{
VGfloat origin[2], escapement[2];
VGHandle handle;
/*
* if a character appears more than once, it will be loaded and converted
* again...
*/
if (FT_Load_Char(rpi->ftFace, msg[i], FT_LOAD_DEFAULT))
{
RARCH_WARN("failed to load glyph '%c'\n", msg[i]);
goto fail;
}
escapement[0] = (VGfloat) rpi->ftFace->glyph->advance.x / 64.0f;
escapement[1] = (VGfloat) rpi->ftFace->glyph->advance.y / 64.0f;
switch (rpi->ftFace->glyph->format)
{
case FT_GLYPH_FORMAT_OUTLINE:
handle = convert_outline_glyph(rpi);
origin[0] = 0.0f;
origin[1] = 0.0f;
glyph_string_add_path(rpi, (VGPath) handle, origin, escapement);
break;
case FT_GLYPH_FORMAT_BITMAP:
handle = convert_bitmap_glyph(rpi);
origin[0] = (VGfloat) (-rpi->ftFace->glyph->bitmap_left);
origin[1] = (VGfloat) (rpi->ftFace->glyph->bitmap.rows - rpi->ftFace->glyph->bitmap_top);
glyph_string_add_image(rpi, (VGImage) handle, origin, escapement);
break;
default:
break;
}
}
for (int i = 0; i < rpi->mMsgLength; i++)
rpi->mGlyphIndices[i] = i;
}
fail:
rpi_render_message(rpi, msg);
vgSeti(VG_SCISSORING, VG_FALSE);
vgSeti(VG_IMAGE_MODE, VG_DRAW_IMAGE_STENCIL);
VGfloat origins[] = { rpi->mScreenWidth * g_settings.video.msg_pos_x - 2.0f, rpi->mScreenHeight * g_settings.video.msg_pos_y - 2.0f };
vgSetfv(VG_GLYPH_ORIGIN, 2, origins);
@ -523,6 +309,7 @@ static void rpi_draw_message(rpi_t *rpi, const char *msg)
vgDrawGlyphs(rpi->mFont, rpi->mMsgLength, rpi->mGlyphIndices, NULL, NULL, VG_FILL_PATH, VG_TRUE);
vgSeti(VG_SCISSORING, VG_TRUE);
vgSeti(VG_IMAGE_MODE, VG_DRAW_IMAGE_NORMAL);
}
#endif
@ -614,14 +401,14 @@ static bool rpi_frame(void *data, const void *frame, unsigned width, unsigned he
static bool rpi_alive(void *data)
{
(void)data;
return true;
(void)data;
return true;
}
static bool rpi_focus(void *data)
{
(void)data;
return true;
(void)data;
return true;
}
static void rpi_set_rotation(void *data, unsigned rotation)
@ -631,13 +418,13 @@ static void rpi_set_rotation(void *data, unsigned rotation)
}
const video_driver_t video_rpi = {
rpi_init,
rpi_frame,
rpi_set_nonblock_state,
rpi_alive,
rpi_focus,
NULL,
rpi_free,
"rpi",
rpi_set_rotation,
rpi_init,
rpi_frame,
rpi_set_nonblock_state,
rpi_alive,
rpi_focus,
NULL,
rpi_free,
"rpi",
rpi_set_rotation,
};

View File

@ -55,10 +55,6 @@
// We want to use -mconsole in Win32, so we need main().
#endif
#ifdef HAVE_RPI
#include <bcm_host.h>
#endif
// To avoid continous switching if we hold the button down, we require that the button must go from pressed, unpressed back to pressed to be able to toggle between then.
static void set_fast_forward_button(bool new_button_state, bool new_hold_button_state)
{
@ -2628,9 +2624,6 @@ void rarch_main_deinit(void)
// Consoles use the higher level API.
int main(int argc, char *argv[])
{
#ifdef HAVE_RPI
bcm_host_init();
#endif
int init_ret;
if ((init_ret = rarch_main_init(argc, argv))) return init_ret;
rarch_init_msg_queue();