From 71c2bfdd22f153d2660dc405aeb96b94021f4375 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Mon, 5 Feb 2018 17:29:40 +0100 Subject: [PATCH] Parse XPM without SDL_image We encounter some problems with SDL2_image on MSYS2 (Windows), so implement our own XPM parsing which does not depend on SDL_image. The input XPM is considered safe (it's in our source repo), so do not check XPM format errors. This implies that read_xpm() is not safe to call on any unsafe input. Although less straightforward, use SDL_CreateRGBSurfaceFrom() instead of SDL_CreateRGBSurfaceWithFormatFrom() because it is available with SDL versions older than 2.0.5. --- README.md | 7 ++-- app/meson.build | 2 +- app/src/scrcpy.c | 4 +- app/src/tinyxpm.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++ app/src/tinyxpm.h | 3 ++ 5 files changed, 109 insertions(+), 7 deletions(-) create mode 100644 app/src/tinyxpm.c create mode 100644 app/src/tinyxpm.h diff --git a/README.md b/README.md index 41b38538..bfd3f2e7 100644 --- a/README.md +++ b/README.md @@ -7,13 +7,13 @@ This project displays screens of Android devices plugged on USB in live. ### Runtime requirements -This projects requires _FFmpeg_, _LibSDL2_, _LibSDL2-image_ and _LibSDL2-net_. +This projects requires _FFmpeg_, _LibSDL2_ and _LibSDL2-net_. #### Linux Install the packages from your package manager. For example, on Debian: - sudo apt install ffmpeg libsdl2-2.0.0 libsdl2-image-2.0.0 libsdl2-net-2.0.0 + sudo apt install ffmpeg libsdl2-2.0.0 libsdl2-net-2.0.0 #### Windows @@ -21,7 +21,6 @@ Install the packages from your package manager. For example, on Debian: From [MSYS2]: pacman -S mingw-w64-x86_64-SDL2 - pacman -S mingw-w64-x86_64-SDL2_image pacman -S mingw-w64-x86_64-SDL2_net pacman -S mingw-w64-x86_64-ffmpeg @@ -65,7 +64,7 @@ described below. sudo apt install make gcc openjdk-8-jdk pkg-config meson zip \ libavcodec-dev libavformat-dev libavutil-dev \ - libsdl2-dev libsdl2-image-dev libsdl2-net-dev + libsdl2-dev libsdl2-net-dev #### Windows diff --git a/app/meson.build b/app/meson.build index 698ff573..a9a7ae90 100644 --- a/app/meson.build +++ b/app/meson.build @@ -13,6 +13,7 @@ src = [ 'src/scrcpy.c', 'src/server.c', 'src/strutil.c', + 'src/tinyxpm.c', ] if host_machine.system() == 'windows' @@ -26,7 +27,6 @@ dependencies = [ dependency('libavcodec'), dependency('libavutil'), dependency('sdl2'), - dependency('SDL2_image'), dependency('SDL2_net'), ] diff --git a/app/src/scrcpy.c b/app/src/scrcpy.c index f67024b0..be9c17d9 100644 --- a/app/src/scrcpy.c +++ b/app/src/scrcpy.c @@ -6,7 +6,6 @@ #include #include #include -#include #include #include "command.h" @@ -19,6 +18,7 @@ #include "lockutil.h" #include "netutil.h" #include "server.h" +#include "tinyxpm.h" #include "icon.xpm" @@ -651,7 +651,7 @@ SDL_bool scrcpy(const char *serial, Uint16 local_port, Uint16 max_size, Uint32 b goto screen_finally_destroy_renderer; } - SDL_Surface *icon = IMG_ReadXPMFromArray(icon_xpm); + SDL_Surface *icon = read_xpm(icon_xpm); if (!icon) { SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Could not load icon: %s", SDL_GetError()); ret = SDL_FALSE; diff --git a/app/src/tinyxpm.c b/app/src/tinyxpm.c new file mode 100644 index 00000000..ec404a02 --- /dev/null +++ b/app/src/tinyxpm.c @@ -0,0 +1,100 @@ +#include "tinyxpm.h" + +#include +#include + +struct index { + char c; + Uint32 color; +}; + +static SDL_bool find_color(struct index *index, int len, char c, Uint32 *color) { + // there are typically very few color, so it's ok to iterate over the array + for (int i = 0; i < len; ++i) { + if (index[i].c == c) { + *color = index[i].color; + return SDL_TRUE; + } + } + *color = 0; + return SDL_FALSE; +} + +// We encounter some problems with SDL2_image on MSYS2 (Windows), +// so here is our own XPM parsing not to depend on SDL_image. +// +// We do not hardcode the binary image to keep some flexibility to replace the +// icon easily (just by replacing icon.xpm). +// +// Parameter is not "const char *" because XPM formats are generally stored in a +// (non-const) "char *" +SDL_Surface *read_xpm(char *xpm[]) { + char *endptr; + // *** No error handling, assume the XPM source is valid *** + // (it's in our source repo) + // Assertions are only checked in debug + Uint32 width = strtol(xpm[0], &endptr, 10); + Uint32 height = strtol(endptr + 1, &endptr, 10); + Uint32 colors = strtol(endptr + 1, &endptr, 10); + Uint32 chars = strtol(endptr + 1, &endptr, 10); + + // sanity checks + SDL_assert(width < 256); + SDL_assert(height < 256); + SDL_assert(colors < 256); + SDL_assert(chars == 1); // this implementation does not support more + + // init index + struct index index[colors]; + for (int i = 0; i < colors; ++i) { + const char *line = xpm[1+i]; + index[i].c = line[0]; + SDL_assert(line[1] == '\t'); + SDL_assert(line[2] == 'c'); + SDL_assert(line[3] == ' '); + if (line[4] == '#') { + index[i].color = 0xff000000 | strtol(&line[5], &endptr, 0x10); + SDL_assert(*endptr == '\0'); + } else { + SDL_assert(!strcmp("None", &line[4])); + index[i].color = 0; + } + } + + // parse image + Uint32 *pixels = SDL_malloc(4 * width * height); + if (!pixels) { + SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Could not allocate icon memory"); + return NULL; + } + for (int y = 0; y < height; ++y) { + const char *line = xpm[1 + colors + y]; + for (int x = 0; x < width; ++x) { + char c = line[x]; + Uint32 color; + SDL_bool color_found = find_color(index, colors, c, &color); + SDL_assert(color_found); + pixels[y * width + x] = color; + } + } + +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + Uint32 amask = 0x000000ff; + Uint32 rmask = 0x0000ff00; + Uint32 gmask = 0x00ff0000; + Uint32 bmask = 0xff000000; +#else // little endian, like x86 + Uint32 amask = 0xff000000; + Uint32 rmask = 0x00ff0000; + Uint32 gmask = 0x0000ff00; + Uint32 bmask = 0x000000ff; +#endif + + SDL_Surface *surface = SDL_CreateRGBSurfaceFrom(pixels, + width, height, + 32, 4 * width, + rmask, gmask, bmask, amask); + // make the surface own the raw pixels + surface->flags &= ~SDL_PREALLOC; + return surface; +} diff --git a/app/src/tinyxpm.h b/app/src/tinyxpm.h new file mode 100644 index 00000000..60afb286 --- /dev/null +++ b/app/src/tinyxpm.h @@ -0,0 +1,3 @@ +#include + +SDL_Surface *read_xpm(char *xpm[]);