mirror of
https://github.com/libretro/RetroArch
synced 2025-02-26 06:40:39 +00:00
Added messages support to the Raspberry Pi video driver
This commit is contained in:
parent
ec87f858f2
commit
eaff0d0f38
388
gfx/rpi.c
388
gfx/rpi.c
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user