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

Add --scale, --position-x-offset, --position-y-offset and --rotation-offset #4658

Open
wants to merge 4 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
80 changes: 80 additions & 0 deletions app/src/cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ enum {
OPT_DISPLAY_ORIENTATION,
OPT_RECORD_ORIENTATION,
OPT_ORIENTATION,
OPT_ROTATION_OFFSET,
OPT_SCALE,
OPT_POSITION_X_OFFSET,
OPT_POSITION_Y_OFFSET,
};

struct sc_option {
Expand Down Expand Up @@ -837,6 +841,36 @@ static const struct sc_option options[] = {
.text = "Set the initial window height.\n"
"Default is 0 (automatic).",
},
{
.longopt_id = OPT_ROTATION_OFFSET,
.longopt = "rotation-offset",
.argdesc = "value",
.text = "Set the display rotation offset in degrees.\n"
"Positive values rotate clockwise, negative values "
"rotate counter-clockwise.\n"
"Default is 0 (automatic).",
},
{
.longopt_id = OPT_SCALE,
.longopt = "scale",
.argdesc = "value",
.text = "Set the display scale in integer percentage.\n"
"Default is 100 (automatic).",
},
{
.longopt_id = OPT_POSITION_X_OFFSET,
.longopt = "position-x-offset",
.argdesc = "value",
.text = "Set the display horizontal position offset.\n"
"Default is 0 (automatic).",
},
{
.longopt_id = OPT_POSITION_Y_OFFSET,
.longopt = "position-y-offset",
.argdesc = "value",
.text = "Set the display vertical position offset.\n"
"Default is 0 (automatic).",
},
};

static const struct sc_shortcut shortcuts[] = {
Expand Down Expand Up @@ -1937,6 +1971,32 @@ parse_pause_on_exit(const char *s, enum sc_pause_on_exit *pause_on_exit) {

}

static bool
parse_rotation_offset(const char *s, int16_t *rotation_offset) {
long value;
bool ok = parse_integer_arg(s, &value, false, -360, 360,
"display rotation offset");
if (!ok) {
return false;
}

*rotation_offset = (int16_t) value;
return true;
}

static bool
parse_scale(const char *s, uint16_t *scale) {
long value;
bool ok = parse_integer_arg(s, &value, false, 1, 1000,
"display scale");
if (!ok) {
return false;
}

*scale = (uint16_t) value;
return true;
}

static bool
parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
const char *optstring, const struct option *longopts) {
Expand Down Expand Up @@ -2359,6 +2419,26 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
case OPT_CAMERA_HIGH_SPEED:
opts->camera_high_speed = true;
break;
case OPT_ROTATION_OFFSET:
if (!parse_rotation_offset(optarg, &opts->rotation_offset)) {
return false;
}
break;
case OPT_SCALE:
if (!parse_scale(optarg, &opts->scale)) {
return false;
}
break;
case OPT_POSITION_X_OFFSET:
if (!parse_window_position(optarg, &opts->position_x_offset)) {
return false;
}
break;
case OPT_POSITION_Y_OFFSET:
if (!parse_window_position(optarg, &opts->position_y_offset)) {
return false;
}
break;
default:
// getopt prints the error message on stderr
return false;
Expand Down
6 changes: 6 additions & 0 deletions app/src/coords.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,10 @@ struct sc_position {
struct sc_point point;
};

struct sc_transform {
int16_t rotation;
uint16_t scale;
struct sc_point position;
};

#endif
67 changes: 38 additions & 29 deletions app/src/display.c
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ sc_display_update_texture(struct sc_display *display, const AVFrame *frame) {

enum sc_display_result
sc_display_render(struct sc_display *display, const SDL_Rect *geometry,
enum sc_orientation orientation) {
enum sc_orientation orientation, struct sc_transform *transform_offsets) {
SDL_RenderClear(display->renderer);

if (display->pending.flags) {
Expand All @@ -247,37 +247,46 @@ sc_display_render(struct sc_display *display, const SDL_Rect *geometry,
SDL_Renderer *renderer = display->renderer;
SDL_Texture *texture = display->texture;

if (orientation == SC_ORIENTATION_0) {
int ret = SDL_RenderCopy(renderer, texture, NULL, geometry);
if (ret) {
LOGE("Could not render texture: %s", SDL_GetError());
return SC_DISPLAY_RESULT_ERROR;
}
int16_t rotation_offset = transform_offsets->rotation;
if (rotation_offset < 0) {
rotation_offset = rotation_offset + 360;
}

unsigned cw_rotation = sc_orientation_get_rotation(orientation);
double angle = (90 * cw_rotation) + rotation_offset;

const SDL_Rect *dstrect = NULL;
SDL_Rect rect;

rect.x = geometry->x + transform_offsets->position.x;
rect.y = geometry->y + transform_offsets->position.y;

if (transform_offsets->scale != 100) {
rect.w = geometry->w * transform_offsets->scale / 100;
rect.h = geometry->h * transform_offsets->scale / 100;
} else {
unsigned cw_rotation = sc_orientation_get_rotation(orientation);
double angle = 90 * cw_rotation;

const SDL_Rect *dstrect = NULL;
SDL_Rect rect;
if (sc_orientation_is_swap(orientation)) {
rect.x = geometry->x + (geometry->w - geometry->h) / 2;
rect.y = geometry->y + (geometry->h - geometry->w) / 2;
rect.w = geometry->h;
rect.h = geometry->w;
dstrect = &rect;
} else {
dstrect = geometry;
}
rect.w = geometry->w;
rect.h = geometry->h;
}

SDL_RendererFlip flip = sc_orientation_is_mirror(orientation)
? SDL_FLIP_HORIZONTAL : 0;
if (sc_orientation_is_swap(orientation)) {
rect.x = rect.x + (rect.w - rect.h) / 2;
rect.y = rect.y + (rect.h - rect.w) / 2;
int width = rect.w;
rect.w = rect.h;
rect.h = width;
}

int ret = SDL_RenderCopyEx(renderer, texture, NULL, dstrect, angle,
NULL, flip);
if (ret) {
LOGE("Could not render texture: %s", SDL_GetError());
return SC_DISPLAY_RESULT_ERROR;
}
dstrect = &rect;

SDL_RendererFlip flip = sc_orientation_is_mirror(orientation)
? SDL_FLIP_HORIZONTAL : 0;

int ret = SDL_RenderCopyEx(renderer, texture, NULL, dstrect, angle,
NULL, flip);
if (ret) {
LOGE("Could not render texture: %s", SDL_GetError());
return SC_DISPLAY_RESULT_ERROR;
}

SDL_RenderPresent(display->renderer);
Expand Down
2 changes: 1 addition & 1 deletion app/src/display.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,6 @@ sc_display_update_texture(struct sc_display *display, const AVFrame *frame);

enum sc_display_result
sc_display_render(struct sc_display *display, const SDL_Rect *geometry,
enum sc_orientation orientation);
enum sc_orientation orientation, struct sc_transform *transform_offsets);

#endif
4 changes: 4 additions & 0 deletions app/src/options.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ const struct scrcpy_options scrcpy_options_default = {
.lock_video_orientation = SC_LOCK_VIDEO_ORIENTATION_UNLOCKED,
.display_orientation = SC_ORIENTATION_0,
.record_orientation = SC_ORIENTATION_0,
.rotation_offset = 0,
.scale = 100,
.position_x_offset = 0,
.position_y_offset = 0,
.window_x = SC_WINDOW_POSITION_UNDEFINED,
.window_y = SC_WINDOW_POSITION_UNDEFINED,
.window_width = 0,
Expand Down
4 changes: 4 additions & 0 deletions app/src/options.h
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,10 @@ struct scrcpy_options {
enum sc_lock_video_orientation lock_video_orientation;
enum sc_orientation display_orientation;
enum sc_orientation record_orientation;
int16_t rotation_offset;
uint16_t scale;
int16_t position_x_offset;
int16_t position_y_offset;
int16_t window_x; // SC_WINDOW_POSITION_UNDEFINED for "auto"
int16_t window_y; // SC_WINDOW_POSITION_UNDEFINED for "auto"
uint16_t window_width;
Expand Down
4 changes: 4 additions & 0 deletions app/src/scrcpy.c
Original file line number Diff line number Diff line change
Expand Up @@ -702,6 +702,10 @@ scrcpy(struct scrcpy_options *options) {
.window_height = options->window_height,
.window_borderless = options->window_borderless,
.orientation = options->display_orientation,
.rotation_offset = options->rotation_offset,
.scale = options->scale,
.position_x_offset = options->position_x_offset,
.position_y_offset = options->position_y_offset,
.mipmaps = options->mipmaps,
.fullscreen = options->fullscreen,
.start_fps_counter = options->start_fps_counter,
Expand Down
89 changes: 70 additions & 19 deletions app/src/screen.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <assert.h>
#include <string.h>
#include <SDL2/SDL.h>
#include <math.h>

#include "events.h"
#include "icon.h"
Expand Down Expand Up @@ -251,7 +252,7 @@ sc_screen_render(struct sc_screen *screen, bool update_content_rect) {
}

enum sc_display_result res =
sc_display_render(&screen->display, &screen->rect, screen->orientation);
sc_display_render(&screen->display, &screen->rect, screen->orientation, &screen->transform_offsets);
(void) res; // any error already logged
}

Expand Down Expand Up @@ -380,6 +381,10 @@ sc_screen_init(struct sc_screen *screen,
}

screen->orientation = params->orientation;
screen->transform_offsets.rotation = params->rotation_offset;
screen->transform_offsets.scale = params->scale;
screen->transform_offsets.position.x = params->position_x_offset;
screen->transform_offsets.position.y = params->position_y_offset;
if (screen->orientation != SC_ORIENTATION_0) {
LOGI("Initial display orientation set to %s",
sc_orientation_get_name(screen->orientation));
Expand Down Expand Up @@ -841,57 +846,103 @@ sc_screen_handle_event(struct sc_screen *screen, const SDL_Event *event) {
return true;
}

static void
sc_rotate_point(struct sc_point *point,
struct sc_point *pivot,
int16_t angle_in_degrees) {
const double deg_to_rad = M_PI / 180.0;
float32_t angle_in_radians = (float32_t)angle_in_degrees * deg_to_rad;
float32_t cosine = (float32_t)cos(angle_in_radians);
float32_t sine = (float32_t)sin(angle_in_radians);

int32_t x = point->x;
int32_t y = point->y;
int32_t pivot_x = pivot->x;
int32_t pivot_y = pivot->y;

point->x = (int32_t)(((x - pivot_x) * cosine) + ((y - pivot_y) * -sine) + pivot_x);
point->y = (int32_t)(((x - pivot_x) * sine) + ((y - pivot_y) * cosine) + pivot_y);
}

struct sc_point
sc_screen_convert_drawable_to_frame_coords(struct sc_screen *screen,
int32_t x, int32_t y) {
enum sc_orientation orientation = screen->orientation;

int32_t w = screen->content_size.width;
int32_t h = screen->content_size.height;
int32_t w_half = w >> 1;
int32_t h_half = h >> 1;
struct sc_point pivot = {
.x = w_half,
.y = h_half,
};
int8_t flip_factor = -1;
float32_t scale_factor = 100.0 / screen->transform_offsets.scale;

// screen->rect must be initialized to avoid a division by zero
assert(screen->rect.w && screen->rect.h);

x = (int64_t) (x - screen->rect.x) * w / screen->rect.w;
y = (int64_t) (y - screen->rect.y) * h / screen->rect.h;
float32_t w_factor = w / (float32_t)screen->rect.w;
float32_t h_factor = h / (float32_t)screen->rect.h;
x = (int64_t) (x - screen->rect.x) * w_factor;
y = (int64_t) (y - screen->rect.y) * h_factor;
int32_t x_offset = screen->transform_offsets.position.x * w_factor;
int32_t y_offset = screen->transform_offsets.position.y * h_factor;

struct sc_point result;
switch (orientation) {
case SC_ORIENTATION_0:
result.x = x;
result.y = y;
result.x = (x - x_offset) * scale_factor;
result.y = (y - y_offset) * scale_factor;
break;
case SC_ORIENTATION_90:
result.x = y;
result.y = w - x;
result.x = (y - y_offset) * scale_factor;
result.y = w + ((x_offset - x) * scale_factor);
pivot.x = h_half;
pivot.y = w_half;
break;
case SC_ORIENTATION_180:
result.x = w - x;
result.y = h - y;
result.x = w + ((x_offset - x) * scale_factor);
result.y = h + ((y_offset - y) * scale_factor);
break;
case SC_ORIENTATION_270:
result.x = h - y;
result.y = x;
result.x = h + ((y_offset - y) * scale_factor);
result.y = (x - x_offset) * scale_factor;
pivot.x = h_half;
pivot.y = w_half;
break;
case SC_ORIENTATION_FLIP_0:
result.x = w - x;
result.y = y;
result.x = w + ((x_offset - x) * scale_factor);
result.y = (y - y_offset) * scale_factor;
flip_factor = 1;
break;
case SC_ORIENTATION_FLIP_90:
result.x = h - y;
result.y = w - x;
result.x = h + ((y_offset - y) * scale_factor);
result.y = w + ((x_offset - x) * scale_factor);
pivot.x = h_half;
pivot.y = w_half;
flip_factor = 1;
break;
case SC_ORIENTATION_FLIP_180:
result.x = x;
result.y = h - y;
result.x = (x - x_offset) * scale_factor;
result.y = h + ((y_offset - y) * scale_factor);
flip_factor = 1;
break;
default:
assert(orientation == SC_ORIENTATION_FLIP_270);
result.x = y;
result.y = x;
result.x = (y - y_offset) * scale_factor;
result.y = (x - x_offset) * scale_factor;
pivot.x = h_half;
pivot.y = w_half;
flip_factor = 1;
break;
}

if (screen->transform_offsets.rotation != 0) {
sc_rotate_point(&result, &pivot, flip_factor * screen->transform_offsets.rotation);
}

return result;
}

Expand Down