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.
This commit is contained in:
Romain Vimont 2018-02-05 17:29:40 +01:00
parent 920bafce73
commit 71c2bfdd22
5 changed files with 109 additions and 7 deletions

View file

@ -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

View file

@ -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'),
]

View file

@ -6,7 +6,6 @@
#include <libavformat/avformat.h>
#include <sys/time.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <SDL2/SDL_net.h>
#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;

100
app/src/tinyxpm.c Normal file
View file

@ -0,0 +1,100 @@
#include "tinyxpm.h"
#include <stdio.h>
#include <stdlib.h>
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;
}

3
app/src/tinyxpm.h Normal file
View file

@ -0,0 +1,3 @@
#include <SDL2/SDL.h>
SDL_Surface *read_xpm(char *xpm[]);