diff --git a/gfx/drivers_font/d3d_w32_font.cpp b/gfx/drivers_font/d3d_w32_font.cpp new file mode 100644 index 0000000000..bb32272272 --- /dev/null +++ b/gfx/drivers_font/d3d_w32_font.cpp @@ -0,0 +1,92 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * Copyright (C) 2011-2015 - Daniel De Matteis + * + * 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 . + */ + +#include "../d3d/d3d.h" +#include "../font_d3d_driver.h" +#include "../gfx_common.h" +#include "../../general.h" + +static LPD3DXFONT d3d_font; + +static bool d3dfonts_w32_init_font(void *data, + const char *font_path, unsigned font_size) +{ + uint32_t r, g, b; + d3d_video_t *d3d = (d3d_video_t*)data; + D3DXFONT_DESC desc = { + static_cast(font_size), 0, 400, 0, + false, DEFAULT_CHARSET, + OUT_TT_PRECIS, + CLIP_DEFAULT_PRECIS, + DEFAULT_PITCH, + "Verdana" // Hardcode ftl :( + }; + + (void)font_path; + + r = static_cast(g_settings.video.msg_color_r * 255) & 0xff; + g = static_cast(g_settings.video.msg_color_g * 255) & 0xff; + b = static_cast(g_settings.video.msg_color_b * 255) & 0xff; + + d3d->font_color = D3DCOLOR_XRGB(r, g, b); + + return SUCCEEDED(D3DXCreateFontIndirect(d3d->dev, &desc, &d3d_font)); +} + +static void d3dfonts_w32_deinit_font(void *data) +{ + (void)data; + + if (d3d_font) + d3d_font->Release(); + d3d_font = NULL; +} + +static void d3dfonts_w32_render_msg(void *data, const char *msg, + const struct font_params *params) +{ + d3d_video_t *d3d = (d3d_video_t*)data; + + if (!d3d) + return; + if (!msg) + return; + if (!(SUCCEEDED(d3d->dev->BeginScene()))) + return; + + d3d_font->DrawTextA(NULL, + msg, + -1, + &d3d->font_rect_shifted, + DT_LEFT, + ((d3d->font_color >> 2) & 0x3f3f3f) | 0xff000000); + + d3d_font->DrawTextA(NULL, + msg, + -1, + &d3d->font_rect, + DT_LEFT, + d3d->font_color | 0xff000000); + + d3d->dev->EndScene(); +} + +d3d_font_renderer_t d3d_win32_font = { + d3dfonts_w32_init_font, + d3dfonts_w32_deinit_font, + d3dfonts_w32_render_msg, + "d3d-fonts-w32", +}; diff --git a/gfx/drivers_font/gl_raster_font.c b/gfx/drivers_font/gl_raster_font.c new file mode 100644 index 0000000000..e3f003cf2d --- /dev/null +++ b/gfx/drivers_font/gl_raster_font.c @@ -0,0 +1,298 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * Copyright (C) 2011-2015 - Daniel De Matteis + * + * 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 . + */ + +#include "../gfx_common.h" +#include "../gl_common.h" +#include "../video_shader_driver.h" + +#define emit(c, vx, vy) do { \ + font_vertex[ 2 * (6 * i + c) + 0] = (x + (delta_x + off_x + vx * width) * scale) * inv_win_width; \ + font_vertex[ 2 * (6 * i + c) + 1] = (y + (delta_y - off_y - vy * height) * scale) * inv_win_height; \ + font_tex_coords[ 2 * (6 * i + c) + 0] = (tex_x + vx * width) * inv_tex_size_x; \ + font_tex_coords[ 2 * (6 * i + c) + 1] = (tex_y + vy * height) * inv_tex_size_y; \ + font_color[ 4 * (6 * i + c) + 0] = color[0]; \ + font_color[ 4 * (6 * i + c) + 1] = color[1]; \ + font_color[ 4 * (6 * i + c) + 2] = color[2]; \ + font_color[ 4 * (6 * i + c) + 3] = color[3]; \ +} while(0) + +#define MAX_MSG_LEN_CHUNK 64 + +typedef struct +{ + gl_t *gl; + GLuint tex; + unsigned tex_width, tex_height; + + const font_renderer_driver_t *font_driver; + void *font_data; +} gl_raster_t; + +static void *gl_raster_font_init_font(void *gl_data, + const char *font_path, float font_size) +{ + unsigned width, height; + uint8_t *tmp_buffer; + const struct font_atlas *atlas = NULL; + gl_raster_t *font = (gl_raster_t*)calloc(1, sizeof(*font)); + + if (!font) + return NULL; + + font->gl = (gl_t*)gl_data; + + if (!font_renderer_create_default(&font->font_driver, + &font->font_data, font_path, font_size)) + { + RARCH_WARN("Couldn't init font renderer.\n"); + free(font); + return NULL; + } + + glGenTextures(1, &font->tex); + glBindTexture(GL_TEXTURE_2D, font->tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + atlas = font->font_driver->get_atlas(font->font_data); + + width = next_pow2(atlas->width); + height = next_pow2(atlas->height); + + /* Ideally, we'd use single component textures, but the + * difference in ways to do that between core GL and GLES/legacy GL + * is too great to bother going down that route. */ + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, + 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + + tmp_buffer = (uint8_t*)malloc(atlas->width * atlas->height * 4); + + if (tmp_buffer) + { + unsigned i; + uint8_t *dst = tmp_buffer; + const uint8_t *src = atlas->buffer; + + for (i = 0; i < atlas->width * atlas->height; i++) + { + *dst++ = 0xff; + *dst++ = 0xff; + *dst++ = 0xff; + *dst++ = *src++; + } + + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, atlas->width, + atlas->height, GL_RGBA, GL_UNSIGNED_BYTE, tmp_buffer); + free(tmp_buffer); + } + + font->tex_width = width; + font->tex_height = height; + + glBindTexture(GL_TEXTURE_2D, font->gl->texture[font->gl->tex_index]); + return font; +} + +static void gl_raster_font_free_font(void *data) +{ + gl_raster_t *font = (gl_raster_t*)data; + if (!font) + return; + + if (font->font_driver && font->font_data) + font->font_driver->free(font->font_data); + + glDeleteTextures(1, &font->tex); + free(font); +} + + +static void render_message(gl_raster_t *font, const char *msg, GLfloat scale, + const GLfloat color[4], GLfloat pos_x, GLfloat pos_y) +{ + int x, y, delta_x, delta_y; + float inv_tex_size_x, inv_tex_size_y, inv_win_width, inv_win_height; + unsigned i, msg_len_full, msg_len; + GLfloat font_tex_coords[2 * 6 * MAX_MSG_LEN_CHUNK]; + GLfloat font_vertex[2 * 6 * MAX_MSG_LEN_CHUNK]; + GLfloat font_color[4 * 6 * MAX_MSG_LEN_CHUNK]; + gl_t *gl = font->gl; + + glBindTexture(GL_TEXTURE_2D, font->tex); + + msg_len_full = strlen(msg); + msg_len = min(msg_len_full, MAX_MSG_LEN_CHUNK); + + x = roundf(pos_x * gl->vp.width); + y = roundf(pos_y * gl->vp.height); + delta_x = 0; + delta_y = 0; + + inv_tex_size_x = 1.0f / font->tex_width; + inv_tex_size_y = 1.0f / font->tex_height; + inv_win_width = 1.0f / font->gl->vp.width; + inv_win_height = 1.0f / font->gl->vp.height; + + while (msg_len_full) + { + /* Rebind shaders so attrib cache gets reset. */ + if (gl->shader && gl->shader->use) + gl->shader->use(gl, GL_SHADER_STOCK_BLEND); + + for (i = 0; i < msg_len; i++) + { + int off_x, off_y, tex_x, tex_y, width, height; + const struct font_glyph *glyph = + font->font_driver->get_glyph(font->font_data, (uint8_t)msg[i]); + if (!glyph) + glyph = font->font_driver->get_glyph(font->font_data, '?'); /* Do something smarter here ... */ + if (!glyph) + continue; + + off_x = glyph->draw_offset_x; + off_y = glyph->draw_offset_y; + tex_x = glyph->atlas_offset_x; + tex_y = glyph->atlas_offset_y; + width = glyph->width; + height = glyph->height; + + emit(0, 0, 1); /* Bottom-left */ + emit(1, 1, 1); /* Bottom-right */ + emit(2, 0, 0); /* Top-left */ + + emit(3, 1, 0); /* Top-right */ + emit(4, 0, 0); /* Top-left */ + emit(5, 1, 1); /* Bottom-right */ +#undef emit + + delta_x += glyph->advance_x; + delta_y -= glyph->advance_y; + } + + gl->coords.tex_coord = font_tex_coords; + gl->coords.vertex = font_vertex; + gl->coords.color = font_color; + gl->coords.vertices = 6 * msg_len; + gl->shader->set_coords(&gl->coords); + gl->shader->set_mvp(gl, &gl->mvp_no_rot); + glDrawArrays(GL_TRIANGLES, 0, 6 * msg_len); + + msg_len_full -= msg_len; + msg += msg_len; + msg_len = min(msg_len_full, MAX_MSG_LEN_CHUNK); + } + + /* Post - Go back to old rendering path. */ + gl->coords.vertex = gl->vertex_ptr; + gl->coords.tex_coord = gl->tex_info.coord; + gl->coords.color = gl->white_color_ptr; + gl->coords.vertices = 4; + glBindTexture(GL_TEXTURE_2D, gl->texture[gl->tex_index]); +} + +static void gl_raster_font_render_msg(void *data, const char *msg, + const struct font_params *params) +{ + GLfloat x, y, scale, drop_mod; + GLfloat color[4], color_dark[4]; + int drop_x, drop_y; + bool full_screen; + gl_t *gl = NULL; + gl_raster_t *font = (gl_raster_t*)data; + + if (!font) + return; + + gl = font->gl; + + if (params) + { + x = params->x; + y = params->y; + scale = params->scale; + full_screen = params->full_screen; + drop_x = params->drop_x; + drop_y = params->drop_y; + drop_mod = params->drop_mod; + + color[0] = FONT_COLOR_GET_RED(params->color) / 255.0f; + color[1] = FONT_COLOR_GET_GREEN(params->color) / 255.0f; + color[2] = FONT_COLOR_GET_BLUE(params->color) / 255.0f; + color[3] = FONT_COLOR_GET_ALPHA(params->color) / 255.0f; + + /* If alpha is 0.0f, turn it into default 1.0f */ + if (color[3] <= 0.0f) + color[3] = 1.0f; + } + else + { + x = g_settings.video.msg_pos_x; + y = g_settings.video.msg_pos_y; + scale = 1.0f; + full_screen = false; + + color[0] = g_settings.video.msg_color_r; + color[1] = g_settings.video.msg_color_g; + color[2] = g_settings.video.msg_color_b; + color[3] = 1.0f; + + drop_x = -2; + drop_y = -2; + drop_mod = 0.3f; + } + + gl_set_viewport(gl, gl->win_width, gl->win_height, + full_screen, false); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glBlendEquation(GL_FUNC_ADD); + + if (drop_x || drop_y) + { + color_dark[0] = color[0] * drop_mod; + color_dark[1] = color[1] * drop_mod; + color_dark[2] = color[2] * drop_mod; + color_dark[3] = color[3]; + + render_message(font, msg, scale, color_dark, + x + scale * drop_x / gl->vp.width, y + + scale * drop_y / gl->vp.height); + } + render_message(font, msg, scale, color, x, y); + + glDisable(GL_BLEND); + gl_set_viewport(gl, gl->win_width, gl->win_height, false, true); +} + +static const struct font_glyph *gl_raster_font_get_glyph( + void *data, uint32_t code) +{ + gl_raster_t *font = (gl_raster_t*)data; + + if (!font) + return NULL; + return font->font_driver->get_glyph((void*)font->font_driver, code); +} + +gl_font_renderer_t gl_raster_font = { + gl_raster_font_init_font, + gl_raster_font_free_font, + gl_raster_font_render_msg, + "GL raster", + gl_raster_font_get_glyph, +}; diff --git a/gfx/drivers_font/ps_libdbgfont.c b/gfx/drivers_font/ps_libdbgfont.c new file mode 100644 index 0000000000..517f1f7ab4 --- /dev/null +++ b/gfx/drivers_font/ps_libdbgfont.c @@ -0,0 +1,104 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * Copyright (C) 2011-2015 - Daniel De Matteis + * + * 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 . + */ + +#include "../font_renderer_driver.h" +#include "../gfx_common.h" +#include "../gl_common.h" + +#if defined(SN_TARGET_PSP2) +#include +#define DbgFontPrint(x, y, scale, color, msg) sceDbgFontPrint(x, y, color, msg) +#define DbgFontConfig SceDbgFontConfig +#define DbgFontInit sceDbgFontInit +#define DbgFontExit sceDbgFontExit +#elif defined(__CELLOS_LV2__) +#include +#define SCE_DBGFONT_BUFSIZE_LARGE 2048 +#define DbgFontPrint(x, y, scale, color, msg) cellDbgFontPrintf(x, y, scale, color, msg) +#define DbgFontConfig CellDbgFontConfig +#define DbgFontInit cellDbgFontInit +#define DbgFontExit cellDbgFontExit +#endif + +static void *libdbg_font_init_font(void *gl_data, const char *font_path, float font_size) +{ + gl_t *gl = (gl_t*)gl_data; + + (void)font_path; + (void)font_size; + + DbgFontConfig cfg; +#if defined(SN_TARGET_PSP2) + cfg.fontSize = SCE_DBGFONT_FONTSIZE_LARGE; +#elif defined(__CELLOS_LV2__) + cfg.bufSize = SCE_DBGFONT_BUFSIZE_LARGE; + cfg.screenWidth = gl->win_width; + cfg.screenHeight = gl->win_height; +#endif + + DbgFontInit(&cfg); + + /* Doesn't need any state. */ + return (void*)-1; +} + +static void libdbg_font_deinit_font(void *data) +{ + (void)data; + DbgFontExit(); +} + +static void libdbg_font_render_msg(void *data, const char *msg, + const struct font_params *params) +{ + float x, y, scale; + unsigned color; + + (void)data; + + if (params) + { + x = params->x; + y = params->y; + scale = params->scale; + color = params->color; + } + else + { + x = g_settings.video.msg_pos_x; + y = 0.90f; + scale = 1.04f; + color = SILVER; + } + + DbgFontPrint(x, y, scale, color, msg); + + if (!params) + DbgFontPrint(x, y, scale - 0.01f, WHITE, msg); + +#ifdef SN_TARGET_PSP2 + /* FIXME - if we ever get around to this port, + * move this out to some better place */ + sceDbgFontFlush(); +#endif +} + +gl_font_renderer_t libdbg_font = { + libdbg_font_init_font, + libdbg_font_deinit_font, + libdbg_font_render_msg, + "GL raster", +}; diff --git a/gfx/drivers_font/xdk1_xfonts.c b/gfx/drivers_font/xdk1_xfonts.c new file mode 100644 index 0000000000..e72479ef2e --- /dev/null +++ b/gfx/drivers_font/xdk1_xfonts.c @@ -0,0 +1,77 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * Copyright (C) 2011-2015 - Daniel De Matteis + * + * 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 . + */ + +#include +#include "../font_d3d_driver.h" +#include "../gfx_common.h" +#include "../../general.h" + +static XFONT *debug_font; +static D3DSurface *pFrontBuffer; + +static bool xfonts_init_font(void *data, + const char *font_path, unsigned font_size) +{ + (void)font_path; + (void)font_size; + (void)data; + + XFONT_OpenDefaultFont(&debug_font); + debug_font->SetBkMode(XFONT_TRANSPARENT); + debug_font->SetBkColor(D3DCOLOR_ARGB(100,0,0,0)); + debug_font->SetTextHeight(14); + debug_font->SetTextAntialiasLevel(debug_font->GetTextAntialiasLevel()); + + return true; +} + +static void xfonts_deinit_font(void *data) +{ + (void)data; +} + +static void xfonts_render_msg(void *data, const char *msg, + const struct font_params *params) +{ + d3d_video_t *d3d = (d3d_video_t*)data; + wchar_t str[PATH_MAX_LENGTH]; + float x, y; + + if (params) + { + x = params->x; + y = params->y; + } + else + { + x = g_settings.video.msg_pos_x; + y = g_settings.video.msg_pos_y; + } + + d3d->dev->GetBackBuffer(-1, D3DBACKBUFFER_TYPE_MONO, &pFrontBuffer); + + mbstowcs(str, msg, sizeof(str) / sizeof(wchar_t)); + debug_font->TextOut(pFrontBuffer, str, (unsigned)-1, x, y); + + pFrontBuffer->Release(); +} + +d3d_font_renderer_t d3d_xdk1_font = { + xfonts_init_font, + xfonts_deinit_font, + xfonts_render_msg, + "XDK1 Xfonts", +}; diff --git a/gfx/drivers_font/xdk360_fonts.cpp b/gfx/drivers_font/xdk360_fonts.cpp new file mode 100644 index 0000000000..436f3054a3 --- /dev/null +++ b/gfx/drivers_font/xdk360_fonts.cpp @@ -0,0 +1,488 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * Copyright (C) 2011-2015 - Daniel De Matteis + * + * 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 . + */ + +#include +#include "../font_d3d_driver.h" +#include "../d3d/d3d.h" +#include "../gfx_common.h" +#include "../../general.h" +#include "../../xdk/xdk_resources.h" + +#define FONT_SCALE(d3d) ((d3d->resolution_hd_enable) ? 2 : 1) + +typedef struct GLYPH_ATTR +{ + unsigned short tu1, tv1, tu2, tv2; /* Texture coordinates for the image. */ + short wOffset; /* Pixel offset for glyph start. */ + short wWidth; /* Pixel width of the glyph. */ + short wAdvance; /* Pixels to advance after the glyph. */ + unsigned short wMask; +} GLYPH_ATTR; + +typedef struct +{ + unsigned long m_dwSavedState; + unsigned long m_cMaxGlyph; /* Number of entries in the translator table. */ + unsigned long m_dwNumGlyphs; /* Number of valid glyphs. */ + float m_fFontHeight; /* Height of the font strike in pixels. */ + float m_fFontTopPadding; /* Padding above the strike zone. */ + float m_fFontBottomPadding; /* Padding below the strike zone. */ + float m_fFontYAdvance; /* Number of pixels to move the cursor for a line feed. */ + wchar_t * m_TranslatorTable; /* ASCII to glyph lookup table. */ + D3DTexture* m_pFontTexture; + const GLYPH_ATTR* m_Glyphs; /* Array of glyphs. */ +} xdk360_video_font_t; + +static xdk360_video_font_t m_Font; +static PackedResource m_xprResource; + +#define CALCFONTFILEHEADERSIZE(x) ( sizeof(unsigned long) + (sizeof(float)* 4) + sizeof(unsigned short) + (sizeof(wchar_t)*(x)) ) +#define FONTFILEVERSION 5 + +typedef struct { + unsigned long m_dwFileVersion; /* Version of the font file (Must match FONTFILEVERSION). */ + float m_fFontHeight; /* Height of the font strike in pixels. */ + float m_fFontTopPadding; /* Padding above the strike zone. */ + float m_fFontBottomPadding; /* Padding below the strike zone. */ + float m_fFontYAdvance; /* Number of pixels to move the cursor for a line feed. */ + unsigned short m_cMaxGlyph; /* Number of font characters (Should be an odd number to maintain DWORD Alignment). */ + wchar_t m_TranslatorTable[1]; /* ASCII to Glyph lookup table, NOTE: It's m_cMaxGlyph+1 in size. */ +} FontFileHeaderImage_t; + +typedef struct { + unsigned long m_dwNumGlyphs; /* Size of font strike array (First entry is the unknown glyph). */ + GLYPH_ATTR m_Glyphs[1]; /* Array of font strike uv's etc... NOTE: It's m_dwNumGlyphs in size. */ +} FontFileStrikesImage_t; + +static const char g_strFontShader[] = +"struct VS_IN\n" +"{\n" +"float2 Pos : POSITION;\n" +"float2 Tex : TEXCOORD0;\n" +"};\n" +"struct VS_OUT\n" +"{\n" +"float4 Position : POSITION;\n" +"float2 TexCoord0 : TEXCOORD0;\n" +"};\n" +"uniform float4 Color : register(c1);\n" +"uniform float2 TexScale : register(c2);\n" +"sampler FontTexture : register(s0);\n" +"VS_OUT main_vertex( VS_IN In )\n" +"{\n" +"VS_OUT Out;\n" +"Out.Position.x = (In.Pos.x-0.5);\n" +"Out.Position.y = (In.Pos.y-0.5);\n" +"Out.Position.z = ( 0.0 );\n" +"Out.Position.w = ( 1.0 );\n" +"Out.TexCoord0.x = In.Tex.x * TexScale.x;\n" +"Out.TexCoord0.y = In.Tex.y * TexScale.y;\n" +"return Out;\n" +"}\n" +"float4 main_fragment( VS_OUT In ) : COLOR0\n" +"{\n" +"float4 FontTexel = tex2D( FontTexture, In.TexCoord0 );\n" +"return FontTexel;\n" +"}\n"; + +typedef struct { + D3DVertexDeclaration* m_pFontVertexDecl; + D3DVertexShader* m_pFontVertexShader; + D3DPixelShader* m_pFontPixelShader; +} Font_Locals_t; + +static Font_Locals_t s_FontLocals; + +static HRESULT xdk360_video_font_create_shaders( + void *data, xdk360_video_font_t * font) +{ + HRESULT hr; + d3d_video_t *d3d = (d3d_video_t*)data; + LPDIRECT3DDEVICE d3dr = d3d->dev; + + if (s_FontLocals.m_pFontVertexDecl) + { + s_FontLocals.m_pFontVertexDecl->AddRef(); + s_FontLocals.m_pFontVertexShader->AddRef(); + s_FontLocals.m_pFontPixelShader->AddRef(); + return 0; + } + + do + { + static const D3DVERTEXELEMENT9 decl[] = + { + { 0, 0, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 }, + { 0, 8, D3DDECLTYPE_USHORT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 }, + { 0, 12, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 1 }, + D3DDECL_END() + }; + + + hr = d3dr->CreateVertexDeclaration( decl, &s_FontLocals.m_pFontVertexDecl ); + + if (hr >= 0) + { + ID3DXBuffer* pShaderCode; + + hr = D3DXCompileShader( g_strFontShader, sizeof(g_strFontShader)-1 , + NULL, NULL, "main_vertex", "vs.2.0", 0,&pShaderCode, NULL, NULL ); + + if (hr >= 0) + { + hr = d3dr->CreateVertexShader( ( unsigned long * )pShaderCode->GetBufferPointer(), + &s_FontLocals.m_pFontVertexShader ); + pShaderCode->Release(); + + if (hr >= 0) + { + hr = D3DXCompileShader( g_strFontShader, sizeof(g_strFontShader)-1 , + NULL, NULL, "main_fragment", "ps.2.0", 0,&pShaderCode, NULL, NULL ); + + if (hr >= 0) + { + hr = d3dr->CreatePixelShader((DWORD*)pShaderCode->GetBufferPointer(), + &s_FontLocals.m_pFontPixelShader ); + pShaderCode->Release(); + + if (hr >= 0) + { + hr = 0; + break; + } + } + s_FontLocals.m_pFontVertexShader->Release(); + } + + s_FontLocals.m_pFontVertexShader = NULL; + } + + s_FontLocals.m_pFontVertexDecl->Release(); + } + s_FontLocals.m_pFontVertexDecl = NULL; + }while(0); + + return hr; +} + +static bool xdk360_init_font(void *data, + const char *font_path, unsigned font_size) +{ + unsigned long dwFileVersion; + const void *pFontData = NULL; + D3DTexture *pFontTexture = NULL; + const unsigned char * pData = NULL; + xdk360_video_font_t *font = &m_Font; + d3d_video_t *d3d = (d3d_video_t*)data; + + (void)font_size; + + font->m_pFontTexture = NULL; + font->m_dwNumGlyphs = 0L; + font->m_Glyphs = NULL; + font->m_cMaxGlyph = 0; + font->m_TranslatorTable = NULL; + + /* Create the font. */ + if (FAILED( m_xprResource.Create(font_path))) + goto error; + + pFontTexture = m_xprResource.GetTexture( "FontTexture" ); + pFontData = m_xprResource.GetData( "FontData"); + + /* Save a copy of the texture. */ + font->m_pFontTexture = pFontTexture; + + /* Check version of file (to make sure it matches up with the FontMaker tool). */ + pData = (const unsigned char*)pFontData; + dwFileVersion = ((const FontFileHeaderImage_t *)pData)->m_dwFileVersion; + + if (dwFileVersion != FONTFILEVERSION) + { + RARCH_ERR("Incorrect version number on font file.\n"); + goto error; + } + + font->m_fFontHeight = ((const FontFileHeaderImage_t *)pData)->m_fFontHeight; + font->m_fFontTopPadding = ((const FontFileHeaderImage_t *)pData)->m_fFontTopPadding; + font->m_fFontBottomPadding = ((const FontFileHeaderImage_t *)pData)->m_fFontBottomPadding; + font->m_fFontYAdvance = ((const FontFileHeaderImage_t *)pData)->m_fFontYAdvance; + + /* Point to the translator string which immediately follows the 4 floats. */ + font->m_cMaxGlyph = ((const FontFileHeaderImage_t *)pData)->m_cMaxGlyph; + font->m_TranslatorTable = const_cast((const FontFileHeaderImage_t *)pData)->m_TranslatorTable; + + pData += CALCFONTFILEHEADERSIZE( font->m_cMaxGlyph + 1 ); + + /* Read the glyph attributes from the file. */ + font->m_dwNumGlyphs = ((const FontFileStrikesImage_t *)pData)->m_dwNumGlyphs; + font->m_Glyphs = ((const FontFileStrikesImage_t *)pData)->m_Glyphs; + + /* Create the vertex and pixel shaders for rendering the font */ + if (FAILED(xdk360_video_font_create_shaders(d3d, font))) + { + RARCH_ERR( "Could not create font shaders.\n" ); + goto error; + } + + RARCH_LOG("Successfully initialized D3D9 HLSL fonts.\n"); + return true; +error: + RARCH_ERR("Could not initialize D3D9 HLSL fonts.\n"); + return false; +} + +static void xdk360_deinit_font(void *data) +{ + xdk360_video_font_t *font = &m_Font; + + /* Destroy the font */ + font->m_pFontTexture = NULL; + font->m_dwNumGlyphs = 0L; + font->m_Glyphs = NULL; + font->m_cMaxGlyph = 0; + font->m_TranslatorTable = NULL; + + if (s_FontLocals.m_pFontPixelShader) + s_FontLocals.m_pFontPixelShader->Release(); + if (s_FontLocals.m_pFontVertexShader) + s_FontLocals.m_pFontVertexShader->Release(); + if (s_FontLocals.m_pFontVertexDecl) + s_FontLocals.m_pFontVertexDecl->Release(); + + s_FontLocals.m_pFontPixelShader = NULL; + s_FontLocals.m_pFontVertexShader = NULL; + s_FontLocals.m_pFontVertexDecl = NULL; + + if (m_xprResource.Initialized()) + m_xprResource.Destroy(); +} + +static void xdk360_render_msg_post(xdk360_video_font_t * font, void *video_data) +{ + // Cache the global pointer into a register + d3d_video_t *d3d = (d3d_video_t*)video_data; + LPDIRECT3DDEVICE d3dr = d3d->dev; + + d3d_set_texture(d3dr, 0, NULL); + d3dr->SetVertexDeclaration(NULL); + d3d_set_vertex_shader(d3dr, 0, NULL); + D3DDevice_SetPixelShader(d3dr, NULL); + d3dr->SetRenderState( D3DRS_VIEWPORTENABLE, font->m_dwSavedState ); +} + +static void xdk360_render_msg_pre(xdk360_video_font_t * font, void *video_data) +{ + float vTexScale[4]; + D3DSURFACE_DESC TextureDesc; + d3d_video_t *d3d = (d3d_video_t*)video_data; + LPDIRECT3DDEVICE d3dr = d3d->dev; + + /* Save state. */ + d3dr->GetRenderState( D3DRS_VIEWPORTENABLE, &font->m_dwSavedState ); + + /* Set the texture scaling factor as a vertex shader constant. */ + D3DTexture_GetLevelDesc(font->m_pFontTexture, 0, &TextureDesc); // Get the description + + /* Set render state. */ + d3d_set_texture(d3dr, 0, font->m_pFontTexture); + + /* Read the TextureDesc here to ensure no load/hit/store from GetLevelDesc(). */ + vTexScale[0] = 1.0f / TextureDesc.Width; /* LHS due to int->float conversion. */ + vTexScale[1] = 1.0f / TextureDesc.Height; + vTexScale[2] = 0.0f; + vTexScale[3] = 0.0f; + + d3dr->SetRenderState( D3DRS_VIEWPORTENABLE, FALSE ); + d3dr->SetVertexDeclaration(s_FontLocals.m_pFontVertexDecl); + d3d_set_vertex_shader(d3dr, 0, s_FontLocals.m_pFontVertexShader); + d3dr->SetPixelShader(s_FontLocals.m_pFontPixelShader); + + /* Set the texture scaling factor as a vertex shader constant. + * Call here to avoid load hit store from writing to vTexScale above + */ + d3dr->SetVertexShaderConstantF( 2, vTexScale, 1 ); +} + +static void xdk360_draw_text(xdk360_video_font_t *font, void *video_data, + float x, float y, const wchar_t * strText) +{ + unsigned long dwNumChars; + volatile float *pVertex; + float vColor[4], m_fCursorX, m_fCursorY; + d3d_video_t *d3d = (d3d_video_t*)video_data; + LPDIRECT3DDEVICE d3dr = d3d->dev; + + /* Set the color as a vertex shader constant. */ + vColor[0] = ( ( 0xffffffff & 0x00ff0000 ) >> 16L ) / 255.0f; + vColor[1] = ( ( 0xffffffff & 0x0000ff00 ) >> 8L ) / 255.0f; + vColor[2] = ( ( 0xffffffff & 0x000000ff ) >> 0L ) / 255.0f; + vColor[3] = ( ( 0xffffffff & 0xff000000 ) >> 24L ) / 255.0f; + + /* Perform the actual storing of the color constant here to prevent + * a load-hit-store by inserting work between the store and the use of + * the vColor array. */ + d3dr->SetVertexShaderConstantF( 1, vColor, 1 ); + + m_fCursorX = floorf(x); + m_fCursorY = floorf(y); + + /* Adjust for padding. */ + y -= font->m_fFontTopPadding; + + /* Begin drawing the vertices + * Declared as volatile to force writing in ascending + * address order. + * + * It prevents out of sequence writing in write combined + * memory. + */ + + dwNumChars = wcslen(strText); + d3dr->BeginVertices(D3DPT_QUADLIST, 4 * dwNumChars, + sizeof(XMFLOAT4), (void**)&pVertex); + + /* Draw four vertices for each glyph. */ + while (*strText) + { + float fOffset, fAdvance, fWidth, fHeight; + unsigned long tu1, tu2, tv1, tv2; + const GLYPH_ATTR *pGlyph; + wchar_t letter = *strText++; /* Get the current letter in the string */ + + /* Handle the newline character. */ + if (letter == L'\n') + { + m_fCursorX = x; + m_fCursorY += font->m_fFontYAdvance * FONT_SCALE(d3d); + continue; + } + + /* Translate unprintable characters. */ + if (letter <= font->m_cMaxGlyph) + pGlyph = &font->m_Glyphs[font->m_TranslatorTable[letter]]; + else + pGlyph = &font->m_Glyphs[0]; + + fOffset = FONT_SCALE(d3d) * (float)pGlyph->wOffset; + fAdvance = FONT_SCALE(d3d) * (float)pGlyph->wAdvance; + fWidth = FONT_SCALE(d3d) * (float)pGlyph->wWidth; + fHeight = FONT_SCALE(d3d) * font->m_fFontHeight; + + m_fCursorX += fOffset; + + /* Add the vertices to draw this glyph. */ + + /* Convert shorts to 32 bit longs for in register merging */ + tu1 = pGlyph->tu1; + tv1 = pGlyph->tv1; + tu2 = pGlyph->tu2; + tv2 = pGlyph->tv2; + + /* NOTE: The vertexes are 2 floats for the screen coordinates, + * followed by two USHORTS for the u/vs of the character, + * terminated with the ARGB 32 bit color. + * + * This makes for 16 bytes per vertex data (Easier to read) + * + * Second NOTE: The U/V coordinates are merged and written + * using a DWORD due to the write combining hardware + * being only able to handle 32, 64 and 128 writes. + * + * Never store to write combined memory with 8 or 16bit + * instructions. You've been warned. + */ + + /* Setup the vertex/screen coordinates */ + + pVertex[0] = m_fCursorX; + pVertex[1] = m_fCursorY; + pVertex[3] = 0; + pVertex[4] = m_fCursorX + fWidth; + pVertex[5] = m_fCursorY; + pVertex[7] = 0; + pVertex[8] = m_fCursorX + fWidth; + pVertex[9] = m_fCursorY + fHeight; + pVertex[11] = 0; + pVertex[12] = m_fCursorX; + pVertex[13] = m_fCursorY + fHeight; +#ifndef LSB_FIRST + ((volatile unsigned long *)pVertex)[2] = (tu1 << 16) | tv1; // Merged using big endian rules + ((volatile unsigned long *)pVertex)[6] = (tu2 << 16) | tv1; // Merged using big endian rules + ((volatile unsigned long *)pVertex)[10] = (tu2 << 16) | tv2; // Merged using big endian rules + ((volatile unsigned long *)pVertex)[14] = (tu1 << 16) | tv2; // Merged using big endian rules +#endif + pVertex[15] = 0; + pVertex += 16; + + m_fCursorX += fAdvance; + + dwNumChars--; + } + + /* Since we allocated vertex data space + * based on the string length, we now need to + * add some dummy verts for any skipped + * characters (like newlines, etc.) + */ + while (dwNumChars) + { + unsigned i; + for (i = 0; i < 16; i++) + pVertex[i] = 0; + + pVertex += 16; + dwNumChars--; + } + + d3dr->EndVertices(); +} + +static void xdk360_render_msg(void *data, const char *str_msg, + const struct font_params *params) +{ + float x, y; + wchar_t msg[PATH_MAX_LENGTH]; + d3d_video_t *d3d = (d3d_video_t*)data; + xdk360_video_font_t *font = &m_Font; + + if (params) + { + x = params->x; + y = params->y; + } + else + { + x = d3d->resolution_hd_enable ? 160 : 100; + y = 120; + } + + mbstowcs(msg, str_msg, sizeof(msg) / sizeof(wchar_t)); + + if (msg || msg[0] != L'\0') + { + xdk360_render_msg_pre(font, d3d); + xdk360_draw_text(font, d3d, x, y, msg); + xdk360_render_msg_post(font, d3d); + } +} + +d3d_font_renderer_t d3d_xbox360_font = { + xdk360_init_font, + xdk360_deinit_font, + xdk360_render_msg, + "Xbox 360 fonts", +};