Add text framework

Signed-off-by: Yu-Chen Lin <npes87184@gmail.com>
This commit is contained in:
Yu-Chen Lin 2020-06-07 19:05:18 +08:00
parent 1e4ee547b5
commit 690548d24a
12 changed files with 8336 additions and 5 deletions

View file

@ -61,7 +61,7 @@ sudo apt install ffmpeg libsdl2-2.0-0 adb
# client build dependencies
sudo apt install gcc git pkg-config meson ninja-build \
libavcodec-dev libavformat-dev libavutil-dev \
libsdl2-dev
libsdl2-dev libsdl2-ttf-dev
# server build dependencies
sudo apt install openjdk-8-jdk

View file

@ -109,6 +109,7 @@ dist-win32: build-server build-win32 build-win32-noconsole
cp prebuilt-deps/platform-tools/AdbWinApi.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
cp prebuilt-deps/platform-tools/AdbWinUsbApi.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
cp prebuilt-deps/SDL2-2.0.12/i686-w64-mingw32/bin/SDL2.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
cp prebuilt-deps/SDL2_ttf-2.0.15/i686-w64-mingw32/bin/SDL2_ttf.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
dist-win64: build-server build-win64 build-win64-noconsole
mkdir -p "$(DIST)/$(WIN64_TARGET_DIR)"
@ -124,6 +125,7 @@ dist-win64: build-server build-win64 build-win64-noconsole
cp prebuilt-deps/platform-tools/AdbWinApi.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
cp prebuilt-deps/platform-tools/AdbWinUsbApi.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
cp prebuilt-deps/SDL2-2.0.12/x86_64-w64-mingw32/bin/SDL2.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
cp prebuilt-deps/SDL2_ttf-2.0.15/x86_64-w64-mingw32/bin/SDL2_ttf.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
zip-win32: dist-win32
cd "$(DIST)/$(WIN32_TARGET_DIR)"; \

View file

@ -12,6 +12,7 @@ src = [
'src/fps_counter.c',
'src/input_manager.c',
'src/opengl.c',
'src/open_sans_font.c',
'src/receiver.c',
'src/recorder.c',
'src/scrcpy.c',
@ -32,6 +33,7 @@ if not get_option('crossbuild_windows')
dependency('libavcodec'),
dependency('libavutil'),
dependency('sdl2'),
dependency('SDL2_ttf'),
]
else
@ -43,7 +45,6 @@ else
sdl2_bin_dir = meson.current_source_dir() + '/../prebuilt-deps/' + prebuilt_sdl2 + '/bin'
sdl2_lib_dir = meson.current_source_dir() + '/../prebuilt-deps/' + prebuilt_sdl2 + '/lib'
sdl2_include_dir = '../prebuilt-deps/' + prebuilt_sdl2 + '/include'
sdl2 = declare_dependency(
dependencies: [
cc.find_library('SDL2', dirs: sdl2_bin_dir),
@ -52,6 +53,19 @@ else
include_directories: include_directories(sdl2_include_dir)
)
prebuilt_sdl2_ttf = meson.get_cross_property('prebuilt_sdl2_ttf')
sdl2_include_sdl2_dir = '../prebuilt-deps/' + prebuilt_sdl2 + '/include/SDL2'
sdl2_ttf_bin_dir = meson.current_source_dir() + '/../prebuilt-deps/' + prebuilt_sdl2_ttf + '/bin'
sdl2_ttf_lib_dir = meson.current_source_dir() + '/../prebuilt-deps/' + prebuilt_sdl2_ttf + '/lib'
sdl2_ttf_include_dir = '../prebuilt-deps/' + prebuilt_sdl2_ttf + '/include'
sdl2_ttf = declare_dependency(
dependencies: [
cc.find_library('SDL2_ttf', dirs: sdl2_ttf_lib_dir),
cc.find_library('SDL2_ttf', dirs: sdl2_ttf_bin_dir),
],
include_directories: include_directories(sdl2_include_sdl2_dir, sdl2_ttf_include_dir)
)
prebuilt_ffmpeg_shared = meson.get_cross_property('prebuilt_ffmpeg_shared')
prebuilt_ffmpeg_dev = meson.get_cross_property('prebuilt_ffmpeg_dev')
ffmpeg_bin_dir = meson.current_source_dir() + '/../prebuilt-deps/' + prebuilt_ffmpeg_shared + '/bin'
@ -67,6 +81,7 @@ else
dependencies = [
ffmpeg,
sdl2_ttf,
sdl2,
cc.find_library('mingw32')
]

View file

@ -1,3 +1,5 @@
#define EVENT_NEW_SESSION SDL_USEREVENT
#define EVENT_NEW_FRAME (SDL_USEREVENT + 1)
#define EVENT_STREAM_STOPPED (SDL_USEREVENT + 2)
#define EVENT_SCREEN_RENDER (SDL_USEREVENT + 3)
#define EVENT_RENDER_TEXT (SDL_USEREVENT + 4)

8091
app/src/open_sans_font.c Normal file

File diff suppressed because it is too large Load diff

9
app/src/open_sans_font.h Normal file
View file

@ -0,0 +1,9 @@
#ifndef OPEN_SANS_FONT_H
#define OPEN_SANS_FONT_H
#include <SDL2/SDL.h>
SDL_RWops *
open_sans_font_raw();
#endif

View file

@ -6,6 +6,7 @@
#include <libavformat/avformat.h>
#include <sys/time.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
#ifdef _WIN32
# include <windows.h>
@ -61,15 +62,26 @@ BOOL WINAPI windows_ctrl_handler(DWORD ctrl_type) {
}
#endif // _WIN32
static void
ttf_destroy() {
TTF_Quit();
}
// init SDL and set appropriate hints
static bool
sdl_init_and_configure(bool display, const char *render_driver) {
uint32_t flags = display ? SDL_INIT_VIDEO : SDL_INIT_EVENTS;
if (SDL_Init(flags)) {
if (SDL_Init(flags | SDL_INIT_TIMER)) {
LOGC("Could not initialize SDL: %s", SDL_GetError());
return false;
}
if (0 > TTF_Init()) {
LOGC("Could not initialize TTF: %s", TTF_GetError());
return false;
}
atexit(ttf_destroy);
atexit(SDL_Quit);
#ifdef _WIN32
@ -163,6 +175,13 @@ handle_event(SDL_Event *event, bool control) {
case SDL_QUIT:
LOGD("User requested to quit");
return EVENT_RESULT_STOPPED_BY_USER;
case EVENT_SCREEN_RENDER:
screen_render(&screen, false);
break;
case EVENT_RENDER_TEXT:
screen_add_text_to_render_queue(&screen, event->user.data1);
screen_render(&screen, false);
break;
case EVENT_NEW_FRAME:
if (!screen.has_frame) {
screen.has_frame = true;

View file

@ -3,17 +3,21 @@
#include <assert.h>
#include <string.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
#include "config.h"
#include "common.h"
#include "compat.h"
#include "events.h"
#include "icon.xpm"
#include "open_sans_font.h"
#include "tiny_xpm.h"
#include "video_buffer.h"
#include "util/lock.h"
#include "util/log.h"
#define DISPLAY_MARGINS 96
#define OSD_TEXT_TIME 5
static inline struct size
get_rotated_size(struct size size, int rotation) {
@ -330,6 +334,21 @@ screen_init_rendering(struct screen *screen, const char *window_title,
// different HiDPI scaling are connected
SDL_SetWindowSize(screen->window, window_size.width, window_size.height);
if (!(screen->mutex = SDL_CreateMutex())) {
LOGC("Could not create mutex: %s", SDL_GetError());
screen_destroy(screen);
return false;
}
SDL_RWops *raw_font = open_sans_font_raw();
screen->font = TTF_OpenFontRW(raw_font, 1, 25);
if (!screen->font) {
LOGW("Init font failed: %s", SDL_GetError());
screen_destroy(screen);
return false;
}
screen_update_content_rect(screen);
return true;
@ -342,6 +361,18 @@ screen_show_window(struct screen *screen) {
void
screen_destroy(struct screen *screen) {
if (screen->text_texture) {
SDL_DestroyTexture(screen->text_texture);
}
if (screen->surface) {
SDL_FreeSurface(screen->surface);
}
if (screen->font) {
TTF_CloseFont(screen->font);
}
if (screen->mutex) {
SDL_DestroyMutex(screen->mutex);
}
if (screen->texture) {
SDL_DestroyTexture(screen->texture);
}
@ -454,6 +485,87 @@ update_texture(struct screen *screen, const AVFrame *frame) {
}
}
static uint32_t
screen_text_timer_callback(uint32_t interval __attribute__((unused)), void *param __attribute__((unused))) {
static SDL_Event render_event;
render_event.type = EVENT_SCREEN_RENDER;
SDL_PushEvent(&render_event);
return 0;
}
bool
screen_render_text(struct screen *screen) {
struct render_text_request req;
if (time(NULL) >= screen->expired_time) {
if (screen->text_texture) {
SDL_DestroyTexture(screen->text_texture);
screen->text_texture = NULL;
}
if (screen->surface) {
SDL_FreeSurface(screen->surface);
screen->surface = NULL;
}
}
if (screen->surface == NULL || screen->text_texture == NULL) {
mutex_lock(screen->mutex);
if (cbuf_is_empty(&screen->queue)) {
mutex_unlock(screen->mutex);
return true;
}
cbuf_take(&screen->queue, &req);
mutex_unlock(screen->mutex);
SDL_Color text_color = {0, 0, 0, 255};
SDL_Color bg_color = {255, 255, 255, 50};
int width = 0;
int height = 0;
screen->expired_time = req.expired_time;
screen->surface = TTF_RenderText_Shaded(screen->font, req.text, text_color, bg_color);
if (!screen->surface) {
LOGW("Init surface failed: %s", SDL_GetError());
SDL_free(req.text);
return false;
}
screen->text_texture = SDL_CreateTextureFromSurface(screen->renderer, screen->surface);
if (!screen->text_texture) {
LOGW("Init text texture failed: %s", SDL_GetError());
SDL_free(req.text);
SDL_FreeSurface(screen->surface);
return false;
}
if (TTF_SizeText(screen->font, req.text, &width, &height)) {
LOGW("Could not get the size of TTF: %s", TTF_GetError());
SDL_free(req.text);
SDL_DestroyTexture(screen->text_texture);
SDL_FreeSurface(screen->surface);
return false;
}
screen->text_rect.x = screen->rect.w / 2 - width / 2;
screen->text_rect.y = screen->rect.h / 3 * 2;
screen->text_rect.w = width;
screen->text_rect.h = height;
SDL_free(req.text);
if (!SDL_AddTimer((OSD_TEXT_TIME + 1) * 1000, screen_text_timer_callback, NULL)) {
LOGW("Failed to add render timer callback, err: %s", SDL_GetError());
}
}
SDL_RenderCopy(screen->renderer, screen->text_texture, NULL, &(screen->text_rect));
return true;
}
bool
screen_update_frame(struct screen *screen, struct video_buffer *vb) {
mutex_lock(vb->mutex);
@ -501,6 +613,7 @@ screen_render(struct screen *screen, bool update_content_rect) {
SDL_RenderCopyEx(screen->renderer, screen->texture, NULL, dstrect,
angle, NULL, 0);
}
screen_render_text(screen);
SDL_RenderPresent(screen->renderer);
}
@ -578,6 +691,41 @@ screen_handle_window_event(struct screen *screen,
}
}
bool
screen_add_text_event(const char *text) {
char *t = SDL_malloc(strlen(text) + 1);
if (!t) {
LOGW("Failed to allocate memory");
return false;
}
memset(t, 0, strlen(text) + 1);
strncpy(t, text, strlen(text));
static SDL_Event text_event;
text_event.type = EVENT_RENDER_TEXT;
text_event.user.data1 = t;
SDL_PushEvent(&text_event);
return true;
}
void
screen_add_text_to_render_queue(struct screen *screen, char *text) {
struct render_text_request req = {
.text = text,
.expired_time = time(NULL) + OSD_TEXT_TIME,
};
mutex_lock(screen->mutex);
if (!cbuf_push(&screen->queue, req)) {
LOGW("Failed to push request to queue");
}
mutex_unlock(screen->mutex);
}
struct point
screen_convert_to_frame_coords(struct screen *screen, int32_t x, int32_t y) {
unsigned rotation = screen->rotation;

View file

@ -3,16 +3,25 @@
#include <stdbool.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
#include <libavformat/avformat.h>
#include "config.h"
#include "common.h"
#include "opengl.h"
#include "util/cbuf.h"
#define WINDOW_POSITION_UNDEFINED (-0x8000)
struct video_buffer;
struct render_text_request {
char *text;
int64_t expired_time;
};
struct render_text_request_queue CBUF(struct render_text_request, 16);
struct screen {
SDL_Window *window;
SDL_Renderer *renderer;
@ -36,6 +45,15 @@ struct screen {
bool maximized;
bool no_window;
bool mipmaps;
// use for rendering text in screen
SDL_mutex *mutex;
struct render_text_request_queue queue;
TTF_Font *font;
SDL_Surface *surface;
SDL_Texture *text_texture;
int64_t expired_time;
SDL_Rect text_rect;
};
#define SCREEN_INITIALIZER { \
@ -69,6 +87,17 @@ struct screen {
.maximized = false, \
.no_window = false, \
.mipmaps = false, \
.mutex = NULL, \
.font = NULL, \
.surface = NULL, \
.text_texture = NULL, \
.expired_time = 0, \
.text_rect = { \
.x = 0, \
.y = 0, \
.w = 0, \
.h = 0, \
}, \
}
// initialize default values
@ -123,6 +152,14 @@ screen_set_rotation(struct screen *screen, unsigned rotation);
void
screen_handle_window_event(struct screen *screen, const SDL_WindowEvent *event);
// add text event
bool
screen_add_text_event(const char *text);
// add text to render queue
void
screen_add_text_to_render_queue(struct screen *screen, char *text);
// convert point from window coordinates to frame coordinates
// x and y are expressed in pixels
struct point

View file

@ -18,3 +18,4 @@ endian = 'little'
prebuilt_ffmpeg_shared = 'ffmpeg-4.2.2-win32-shared'
prebuilt_ffmpeg_dev = 'ffmpeg-4.2.2-win32-dev'
prebuilt_sdl2 = 'SDL2-2.0.12/i686-w64-mingw32'
prebuilt_sdl2_ttf = 'SDL2_ttf-2.0.15/i686-w64-mingw32'

View file

@ -18,3 +18,4 @@ endian = 'little'
prebuilt_ffmpeg_shared = 'ffmpeg-4.2.2-win64-shared'
prebuilt_ffmpeg_dev = 'ffmpeg-4.2.2-win64-dev'
prebuilt_sdl2 = 'SDL2-2.0.12/x86_64-w64-mingw32'
prebuilt_sdl2_ttf = 'SDL2_ttf-2.0.15/x86_64-w64-mingw32'

View file

@ -4,10 +4,11 @@
prepare-ffmpeg-shared-win64 \
prepare-ffmpeg-dev-win64 \
prepare-sdl2 \
prepare-sdl2-ttf \
prepare-adb
prepare-win32: prepare-sdl2 prepare-ffmpeg-shared-win32 prepare-ffmpeg-dev-win32 prepare-adb
prepare-win64: prepare-sdl2 prepare-ffmpeg-shared-win64 prepare-ffmpeg-dev-win64 prepare-adb
prepare-win32: prepare-sdl2 prepare-sdl2-ttf prepare-ffmpeg-shared-win32 prepare-ffmpeg-dev-win32 prepare-adb
prepare-win64: prepare-sdl2 prepare-sdl2-ttf prepare-ffmpeg-shared-win64 prepare-ffmpeg-dev-win64 prepare-adb
prepare-ffmpeg-shared-win32:
@./prepare-dep https://ffmpeg.zeranoe.com/builds/win32/shared/ffmpeg-4.2.2-win32-shared.zip \
@ -34,6 +35,11 @@ prepare-sdl2:
e614a60f797e35ef9f3f96aef3dc6a1d786de3cc7ca6216f97e435c0b6aafc46 \
SDL2-2.0.12
prepare-sdl2-ttf:
@./prepare-dep https://www.libsdl.org/projects/SDL_ttf/release/SDL2_ttf-devel-2.0.15-mingw.tar.gz \
78009f19c1145b1adf86816f0cc12c407ada2b77cf06ee7042d55d231b726d3b \
SDL2-ttf-2.0.15
prepare-adb:
@./prepare-dep https://dl.google.com/android/repository/platform-tools_r30.0.0-windows.zip \
854305f9a702f5ea2c3de73edde402bd26afa0ee944c9b0c4380420f5a862e0d \