Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

High resolution icon #1987

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion app/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ src = [
'src/device.c',
'src/device_msg.c',
'src/event_converter.c',
'src/icon.c',
'src/file_handler.c',
'src/fps_counter.c',
'src/input_manager.c',
Expand All @@ -18,7 +19,6 @@ src = [
'src/screen.c',
'src/server.c',
'src/stream.c',
'src/tiny_xpm.c',
'src/video_buffer.c',
'src/util/net.c',
'src/util/str_util.c'
Expand Down Expand Up @@ -136,6 +136,9 @@ executable('scrcpy', src,
c_args: [])

install_man('scrcpy.1')
install_data('../data/icon.png',
rename: 'scrcpy.png',
install_dir: 'share/icons/hicolor/512x512/apps')


### TESTS
Expand Down
29 changes: 29 additions & 0 deletions app/src/command.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "command.h"

#include <assert.h>
#include <libgen.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
Expand Down Expand Up @@ -229,3 +230,31 @@ process_check_success(process_t proc, const char *name) {
}
return true;
}

char *
get_local_file_path(const char *name) {
char *executable_path = get_executable_path();
if (!executable_path) {
return NULL;
}

char *dir = dirname(executable_path);
size_t dirlen = strlen(dir);
size_t namelen = strlen(name);

size_t len = dirlen + namelen + 2; // +2: '/' and '\0`
char *file_path = SDL_malloc(len);
if (!file_path) {
LOGE("Could not alloc path");
SDL_free(executable_path);
}

memcpy(file_path, dir, dirlen);
file_path[dirlen] = PATH_SEPARATOR;
// namelen + 1 to copy the final '\0'
memcpy(&file_path[dirlen + 1], name, namelen + 1);

SDL_free(executable_path);

return file_path;
}
5 changes: 5 additions & 0 deletions app/src/command.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,9 @@ get_executable_path(void);
bool
is_regular_file(const char *path);

// return the absolute path of a file in the same directory as the executable
// may be NULL on error; to be freed by SDL_free
char *
get_local_file_path(const char *name);

#endif
282 changes: 282 additions & 0 deletions app/src/icon.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,282 @@
#include "icon.h"

#include <assert.h>
#include <stdbool.h>
#include <libavformat/avformat.h>
#include <libavutil/pixdesc.h>
#include <libavutil/pixfmt.h>

#include "config.h"
#include "command.h"
#include "compat.h"
#include "util/log.h"
#include "util/str_util.h"

#define SCRCPY_PORTABLE_ICON_FILENAME "icon.png"
#define SCRCPY_DEFAULT_ICON_PATH PREFIX \
"/share/icons/hicolor/512x512/apps/scrcpy.png"

static char *
get_icon_path(void) {
#ifdef __WINDOWS__
const wchar_t *icon_path_env = _wgetenv(L"SCRCPY_ICON_PATH");
#else
const char *icon_path_env = getenv("SCRCPY_ICON_PATH");
#endif
if (icon_path_env) {
// if the envvar is set, use it
#ifdef __WINDOWS__
char *icon_path = utf8_from_wide_char(icon_path_env);
#else
char *icon_path = SDL_strdup(icon_path_env);
#endif
if (!icon_path) {
LOGE("Could not allocate memory");
return NULL;
}
LOGD("Using SCRCPY_ICON_PATH: %s", icon_path);
return icon_path;
}

#ifndef PORTABLE
char *icon_path = SDL_strdup(SCRCPY_DEFAULT_ICON_PATH);
if (!icon_path) {
LOGE("Could not allocate memory");
return NULL;
}
#else
char *icon_path = get_local_file_path(SCRCPY_PORTABLE_ICON_FILENAME);
if (!icon_path) {
LOGE("Could not get icon path");
return NULL;
}
#endif

return icon_path;
}

static AVFrame *
decode_image(const char *path) {
AVFrame *result = NULL;

AVFormatContext *ctx = avformat_alloc_context();
if (!ctx) {
LOGE("Could not allocate image decoder context");
return NULL;
}

if (avformat_open_input(&ctx, path, NULL, NULL) < 0) {
LOGE("Could not open image codec");
goto free_ctx;
}

if (avformat_find_stream_info(ctx, NULL) < 0) {
LOGE("Could not find image stream info");
goto close_input;
}

int stream = av_find_best_stream(ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
if (stream < 0 ) {
LOGE("Could not find best image stream");
goto close_input;
}

#ifdef SCRCPY_LAVF_HAS_NEW_CODEC_PARAMS_API
AVCodecParameters *params = ctx->streams[stream]->codecpar;

AVCodec *codec = avcodec_find_decoder(params->codec_id);
if (!codec) {
LOGE("Could not find image decoder");
goto close_input;
}

AVCodecContext *codec_ctx = avcodec_alloc_context3(codec);
if (avcodec_parameters_to_context(codec_ctx, params) < 0) {
LOGE("Could not fill codec context");
goto free_codec_ctx;
}
#else
AVCodecContext *codec_ctx = ctx->streams[stream]->codec;

AVCodec *codec = avcodec_find_decoder(codec_ctx->codec_id);
if (!codec) {
LOGE("Could not find image decoder");
goto close_input;
}
#endif

if (avcodec_open2(codec_ctx, codec, NULL) < 0) {
LOGE("Could not open image codec");
goto free_codec_ctx;
}

AVFrame *frame = av_frame_alloc();
if (!frame) {
LOGE("Could not allocate frame");
goto close_codec;
}

AVPacket packet;
av_init_packet(&packet);

if (av_read_frame(ctx, &packet) < 0) {
LOGE("Could not read frame");
av_frame_free(&frame);
goto close_input;
}

#ifdef SCRCPY_LAVF_HAS_NEW_ENCODING_DECODING_API
int ret;
if ((ret = avcodec_send_packet(codec_ctx, &packet)) < 0) {
LOGE("Could not send icon packet: %d", ret);
av_frame_free(&frame);
goto close_input;
}

if ((ret = avcodec_receive_frame(codec_ctx, frame)) != 0) {
LOGE("Could not receive icon frame: %d", ret);
av_frame_free(&frame);
goto close_input;
}

#else
int got_picture;
int len = avcodec_decode_video2(codec_ctx, frame, &got_picture, &packet);
if (len < 0 || !got_picture) {
LOGE("Could not decode icon: %d", len);
av_frame_free(&frame);
goto close_input;
}
#endif

av_packet_unref(&packet);

result = frame;

close_codec:
avcodec_close(codec_ctx);
free_codec_ctx:
#ifdef SCRCPY_LAVF_HAS_NEW_CODEC_PARAMS_API
avcodec_free_context(&codec_ctx);
#endif
close_input:
avformat_close_input(&ctx);
free_ctx:
avformat_free_context(ctx);

return result;
}

static SDL_PixelFormatEnum
to_sdl_pixel_format(enum AVPixelFormat fmt) {
switch (fmt) {
case AV_PIX_FMT_RGB24: return SDL_PIXELFORMAT_RGB24;
case AV_PIX_FMT_BGR24: return SDL_PIXELFORMAT_BGR24;
case AV_PIX_FMT_ARGB: return SDL_PIXELFORMAT_ARGB32;
case AV_PIX_FMT_RGBA: return SDL_PIXELFORMAT_RGBA32;
case AV_PIX_FMT_ABGR: return SDL_PIXELFORMAT_ABGR32;
case AV_PIX_FMT_BGRA: return SDL_PIXELFORMAT_BGRA32;
case AV_PIX_FMT_RGB565BE: return SDL_PIXELFORMAT_RGB565;
case AV_PIX_FMT_RGB555BE: return SDL_PIXELFORMAT_RGB555;
case AV_PIX_FMT_BGR565BE: return SDL_PIXELFORMAT_BGR565;
case AV_PIX_FMT_BGR555BE: return SDL_PIXELFORMAT_BGR555;
case AV_PIX_FMT_RGB444BE: return SDL_PIXELFORMAT_RGB444;
case AV_PIX_FMT_BGR444BE: return SDL_PIXELFORMAT_BGR444;
case AV_PIX_FMT_PAL8: return SDL_PIXELFORMAT_INDEX8;
default: return SDL_PIXELFORMAT_UNKNOWN;
}
}

SDL_Surface *
scrcpy_icon_load() {
char *icon_path = get_icon_path();
assert(icon_path);

AVFrame *frame = decode_image(icon_path);
SDL_free(icon_path);
if (!frame) {
return NULL;
}

const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(frame->format);
if (!desc) {
LOGE("Could not get icon format descriptor");
goto error;
}

bool is_packed = !(desc->flags & AV_PIX_FMT_FLAG_PLANAR);
if (!is_packed) {
LOGE("Could not load non-packed icon");
goto error;
}

SDL_PixelFormatEnum format = to_sdl_pixel_format(frame->format);
if (format == SDL_PIXELFORMAT_UNKNOWN) {
LOGE("Unsupported icon pixel format: %s (%d)", desc->name,
frame->format);
goto error;
}

int bits_per_pixel = av_get_bits_per_pixel(desc);
SDL_Surface *surface =
SDL_CreateRGBSurfaceWithFormatFrom(frame->data[0],
frame->width, frame->height,
bits_per_pixel,
frame->linesize[0],
format);

if (!surface) {
LOGE("Could not create icon surface");
goto error;
}

if (frame->format == AV_PIX_FMT_PAL8) {
// Initialize the SDL palette
uint8_t *data = frame->data[1];
SDL_Color colors[256];
for (int i = 0; i < 256; ++i) {
SDL_Color *color = &colors[i];

// The palette is transported in AVFrame.data[1], is 1024 bytes
// long (256 4-byte entries) and is formatted the same as in
// AV_PIX_FMT_RGB32 described above (i.e., it is also
// endian-specific).
// <https://ffmpeg.org/doxygen/4.1/pixfmt_8h.html#a9a8e335cf3be472042bc9f0cf80cd4c5>
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
color->a = data[i * 4];
color->r = data[i * 4 + 1];
color->g = data[i * 4 + 2];
color->b = data[i * 4 + 3];
#else
color->a = data[i * 4 + 3];
color->r = data[i * 4 + 2];
color->g = data[i * 4 + 1];
color->b = data[i * 4];
#endif
}

SDL_Palette *palette = surface->format->palette;
assert(palette);
int ret = SDL_SetPaletteColors(palette, colors, 0, 256);
if (ret) {
LOGE("Could not set palette colors");
goto error;
}
}

surface->userdata = frame; // frame owns the data

return surface;

error:
av_frame_free(&frame);
return NULL;
}

void
scrcpy_icon_destroy(SDL_Surface *icon) {
AVFrame *frame = icon->userdata;
assert(frame);
av_frame_free(&frame);
SDL_FreeSurface(icon);
}
16 changes: 16 additions & 0 deletions app/src/icon.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#ifndef ICON_H
#define ICON_H

#include <stdbool.h>
#include <SDL2/SDL.h>
#include <libavformat/avformat.h>

#include "config.h"

SDL_Surface *
scrcpy_icon_load(void);

void
scrcpy_icon_destroy(SDL_Surface *icon);

#endif
Loading