Added messages support to the Raspberry Pi video driver

This commit is contained in:
Toad King 2012-05-28 06:33:13 -04:00
parent ec87f858f2
commit eaff0d0f38

388
gfx/rpi.c
View File

@ -8,9 +8,20 @@
#include <EGL/eglext.h>
#include "../libretro.h"
#include "../general.h"
#include "../input/linuxraw_input.h"
//#include "../input/linuxraw_input.h"
// SDL include messing with some defines
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 "../file.h"
#endif
typedef struct {
EGLDisplay mDisplay;
EGLSurface mSurface;
@ -26,6 +37,19 @@ typedef struct {
VGImage mImage;
VGfloat mTransformMatrix[9];
VGint scissor[4];
#ifdef HAVE_FREETYPE
char *mLastMsg;
uint32_t mFontHeight;
FT_Library ftLib;
FT_Face ftFace;
VGFont mFont;
bool mFontsOn;
VGuint mMsgLength;
VGuint mGlyphIndices[1024];
VGPaint mPaintFg;
VGPaint mPaintBg;
#endif
} rpi_t;
static void rpi_set_nonblock_state(void *data, bool state)
@ -176,14 +200,99 @@ static void *rpi_init(const video_info_t *video, const input_driver_t **input, v
if (isatty(0))
{
linuxraw_input_t *linuxraw_input = (linuxraw_input_t*)input_linuxraw.init();
const linuxraw_input_t *linuxraw_input = (const linuxraw_input_t *) input_linuxraw.init();
if (linuxraw_input)
{
*input = &input_linuxraw;
*input = (const input_driver_t *) &input_linuxraw;
*input_data = linuxraw_input;
}
}
#ifdef HAVE_FREETYPE
static const char *font_paths[] = {
#if defined(_WIN32)
"C:\\Windows\\Fonts\\consola.ttf",
"C:\\Windows\\Fonts\\verdana.ttf",
#elif defined(__APPLE__)
"/Library/Fonts/Microsoft/Candara.ttf",
"/Library/Fonts/Verdana.ttf",
"/Library/Fonts/Tahoma.ttf",
#else
"/usr/share/fonts/TTF/DejaVuSansMono.ttf",
"/usr/share/fonts/TTF/DejaVuSans.ttf",
#endif
"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))
{
font = NULL;
for (int i = 0; i < sizeof(font_paths) / sizeof(font_paths[0]); i++)
{
if (path_file_exists(font_paths[i]))
{
font = font_paths[i];
break;
}
}
}
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);
// vgSetPaint(myFillPaint, VG_FILL_PATH);
// vgSetPaint(myStrokePaint, VG_STROKE_PATH);
fail:
#endif
return rpi;
}
@ -202,6 +311,274 @@ static void rpi_free(void *data)
free(rpi);
}
#ifdef HAVE_FREETYPE
// mostly adapted from example code in Mesa-3d
static int path_append(VGPath path, VGubyte segment, const FT_Vector **vectors)
{
VGfloat coords[6];
int i, num_vectors;
switch (segment)
{
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;
break;
}
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;
}
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;
}
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);
rpi->mMsgLength = 0;
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:
vgSeti(VG_SCISSORING, VG_FALSE);
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);
vgSetPaint(rpi->mPaintBg, VG_FILL_PATH);
vgDrawGlyphs(rpi->mFont, rpi->mMsgLength, rpi->mGlyphIndices, NULL, NULL, VG_FILL_PATH, VG_TRUE);
origins[0] += 2.0f;
origins[1] += 2.0f;
vgSetfv(VG_GLYPH_ORIGIN, 2, origins);
vgSetPaint(rpi->mPaintFg, VG_FILL_PATH);
vgDrawGlyphs(rpi->mFont, rpi->mMsgLength, rpi->mGlyphIndices, NULL, NULL, VG_FILL_PATH, VG_TRUE);
vgSeti(VG_SCISSORING, VG_TRUE);
}
#endif
static bool rpi_frame(void *data, const void *frame, unsigned width, unsigned height, unsigned pitch, const char *msg)
{
rpi_t *rpi = (rpi_t*)data;
@ -226,6 +603,11 @@ static bool rpi_frame(void *data, const void *frame, unsigned width, unsigned he
vgImageSubData(rpi->mImage, frame, pitch, rpi->mTexType, 0, 0, width, height);
vgDrawImage(rpi->mImage);
#ifdef HAVE_FREETYPE
if (msg && rpi->mFontsOn)
rpi_draw_message(rpi, msg);
#endif
eglSwapBuffers(rpi->mDisplay, rpi->mSurface);
return true;