From 0f51ad0b16e3836d7ed868bf3e075149dd00338f Mon Sep 17 00:00:00 2001 From: Ilari Liusvaara Date: Sun, 2 Nov 2014 15:01:57 +0200 Subject: [PATCH] C interface: render_text (version bump to v2) Bump C interface version to 2, in order to add render_text (render text to bitmap, using system font --- include/interface/c-interface.h | 30 +++++++++++ src/interface/c-interface.cpp | 88 ++++++++++++++++++++++++++++++++- 2 files changed, 117 insertions(+), 1 deletion(-) diff --git a/include/interface/c-interface.h b/include/interface/c-interface.h index 926af3d0..e4aa2462 100644 --- a/include/interface/c-interface.h +++ b/include/interface/c-interface.h @@ -198,6 +198,34 @@ struct lsnes_core_disassembler const char* (*fn)(uint64_t base, unsigned char(*fetch)(void* ctx), void* ctx); }; +//Font rendering request. +struct lsnes_core_fontrender_req +{ + //Input: Context to pass to callbacks. + void* cb_ctx; + //Input: Allocate bitmap memory. + //cb_ctx => cb_ctx from above. + //mem_size => Number of bytes needed to allocate. + //return buffer at least mem_size bytes. Or return NULL on error. + void* (*alloc)(void* cb_ctx, size_t mem_size); + //Input: Text to render (UTF-8). + const char* text; + //Input: Length of text in bytes. If negative, text is null-terminated. + long text_len; + //Input: Bytes per pixel to request. Can be 1, 2, 3 or 4. + unsigned bytes_pp; + //Input: Foreground color (native endian). + unsigned fg_color; + //Input: Background color (native endian). + unsigned bg_color; + //Output: The allocated bitmap (this comes from ->alloc). Not released on failure. + void* bitmap; + //Output: Width of the bitmap. + size_t width; + //Output: Height of the bitmap. + size_t height; +}; + //Request 0: Initialize and enumerate cores. //Item: 0. //Default action: (required) @@ -243,6 +271,8 @@ struct lsnes_core_enumerate_cores void* (*add_disasm)(struct lsnes_core_disassembler* disasm); //Input: Remove disassembler. Only available if emu_flags1>=1. void (*remove_disasm)(void* handle); + //Input: Render text into bitmap. Returns 0 on success, -1 on failure. Only available if emu_flags>=2. + int (*render_text)(struct lsnes_core_fontrender_req* req); }; //Request 1: Request information about core. diff --git a/src/interface/c-interface.cpp b/src/interface/c-interface.cpp index bb8eb441..086a286a 100644 --- a/src/interface/c-interface.cpp +++ b/src/interface/c-interface.cpp @@ -11,6 +11,7 @@ #include "library/framebuffer-pixfmt-rgb24.hpp" #include "library/framebuffer-pixfmt-rgb32.hpp" #include "library/framebuffer-pixfmt-lrgb.hpp" +#include "fonts/wrapper.hpp" #include "core/audioapi.hpp" #include "core/instance.hpp" #include "core/messages.hpp" @@ -916,6 +917,90 @@ failed: delete reinterpret_cast(handle); } + struct utf8_strlen_iter + { + utf8_strlen_iter() { str_len = 0; } + utf8_strlen_iter& operator++() { str_len++; return *this; } + uint32_t& operator*() { return dummy; } + size_t str_len; + uint32_t dummy; + }; + + template + void callback_render_text2(struct lsnes_core_fontrender_req& req, const std::string& str) + { + auto size = main_font.get_metrics(str); + auto layout = main_font.dolayout(str); + size_t memreq = size.first * size.second * sizeof(T); + if(!memreq) memreq = 1; + if(size.first && memreq / size.first / sizeof(T) < size.second) + throw std::bad_alloc(); //Not enough memory. + req.bitmap = req.alloc(req.cb_ctx, memreq); + if(!req.bitmap) + throw std::bad_alloc(); //Not enough memory. + T fg = (T)req.fg_color; + T bg = (T)req.bg_color; + T* bmp = (T*)req.bitmap; + + for(auto i : layout) { + auto& g = *i.dglyph; + T* _bmp = bmp + (i.y * size.first + i.x); + size_t w = g.wide ? 16 : 8; + size_t skip = size.first - w; + for(size_t _y = 0; _y < 16; _y++) { + uint32_t d = g.data[_y >> (g.wide ? 1 : 2)]; + if(g.wide) + d >>= 16 - ((_y & 1) << 4); + else + d >>= 24 - ((_y & 3) << 3); + for(size_t _x = 0; _x < w; _x++, _bmp++) { + uint32_t b = w - _x - 1; + *_bmp = ((d >> b) & 1) ? fg : bg; + } + _bmp = _bmp + skip; + } + } + req.width = size.first; + req.height = size.second; + } + + void callback_render_text1(struct lsnes_core_fontrender_req& req, const std::string& str) + { + switch(req.bytes_pp) { + case 1: + callback_render_text2(req, str); + return; + case 2: + callback_render_text2(req, str); + return; + case 3: + callback_render_text2(req, str); + return; + case 4: + callback_render_text2(req, str); + return; + default: + throw std::runtime_error("Invalid req.bytes_pp"); + } + } + + int callback_render_text(struct lsnes_core_fontrender_req* req) + { + req->bitmap = NULL; + //If indeterminate length, make it determinate. + if(req->text_len < 0) + req->text_len = strlen(req->text); + const char* text_start = req->text; + const char* text_end = req->text + req->text_len; + try { + std::string str(text_start, text_end); + callback_render_text1(*req, str); + return 0; + } catch(...) { + return -1; + } + } + core_sysregion* create_sysregion(entrypoint_fn& entrypoint, std::map& regions, std::map& types, unsigned sysreg) { @@ -1085,7 +1170,7 @@ no_parameters: //Enumerate what the thing supports. entrypoint_fn entrypoint(fn); lsnes_core_enumerate_cores r; - r.emu_flags1 = 1; + r.emu_flags1 = 2; r.message = callback_message; r.get_input = callback_get_input; r.notify_action_update = callback_notify_action_update; @@ -1103,6 +1188,7 @@ no_parameters: r.submit_frame = callback_submit_frame; r.add_disasm = callback_add_disasm; r.remove_disasm = callback_remove_disasm; + r.render_text = callback_render_text; entrypoint(0, r, [](const char* name, const char* err) { (stringfmt() << "LSNES_CORE_ENUMERATE_CORES(0) failed: " << err).throwex(); });