Scale image to fit window

pull/6/head
Andri Yngvason 2022-04-02 12:51:37 +00:00
parent 545be152ce
commit 15b5d5a33f
2 changed files with 113 additions and 38 deletions

View File

@ -70,6 +70,7 @@ sources = [
'src/strlcpy.c', 'src/strlcpy.c',
'src/evdev-to-qnum.c', 'src/evdev-to-qnum.c',
'src/pixels.c', 'src/pixels.c',
'src/region.c',
] ]
dependencies = [ dependencies = [

View File

@ -37,6 +37,7 @@
#include "keyboard.h" #include "keyboard.h"
#include "vnc.h" #include "vnc.h"
#include "pixels.h" #include "pixels.h"
#include "region.h"
struct buffer { struct buffer {
int width, height, stride; int width, height, stride;
@ -45,6 +46,7 @@ struct buffer {
struct wl_buffer* wl_buffer; struct wl_buffer* wl_buffer;
void* pixels; void* pixels;
bool is_attached; bool is_attached;
bool please_clean_up;
struct pixman_region16 damage; struct pixman_region16 damage;
}; };
@ -56,6 +58,7 @@ struct window {
struct buffer* front_buffer; struct buffer* front_buffer;
struct buffer* back_buffer; struct buffer* back_buffer;
struct vnc_client* vnc;
void* vnc_fb; void* vnc_fb;
}; };
@ -136,11 +139,28 @@ static const struct wl_registry_listener registry_listener = {
.global_remove = registry_remove, .global_remove = registry_remove,
}; };
static void buffer_destroy(struct buffer* self)
{
if (!self)
return;
if (self->is_attached)
self->please_clean_up = true;
wl_buffer_destroy(self->wl_buffer);
munmap(self->pixels, self->size);
free(self);
}
static void buffer_release(void* data, struct wl_buffer* wl_buffer) static void buffer_release(void* data, struct wl_buffer* wl_buffer)
{ {
(void)wl_buffer; (void)wl_buffer;
struct buffer* self = data; struct buffer* self = data;
self->is_attached = false; self->is_attached = false;
if (self->please_clean_up) {
buffer_destroy(self);
}
} }
static const struct wl_buffer_listener buffer_listener = { static const struct wl_buffer_listener buffer_listener = {
@ -197,13 +217,6 @@ failure:
return NULL; return NULL;
} }
static void buffer_destroy(struct buffer* self)
{
wl_buffer_destroy(self->wl_buffer);
munmap(self->pixels, self->size);
free(self);
}
static void shm_format(void* data, struct wl_shm* shm, uint32_t format) static void shm_format(void* data, struct wl_shm* shm, uint32_t format)
{ {
(void)data; (void)data;
@ -289,12 +302,36 @@ static void window_attach(struct window* w, int x, int y)
wl_surface_attach(w->wl_surface, w->back_buffer->wl_buffer, x, y); wl_surface_attach(w->wl_surface, w->back_buffer->wl_buffer, x, y);
} }
static void window_calculate_tranform(struct window* w, double* scale,
int* x_pos, int* y_pos)
{
double src_width = vnc_client_get_width(w->vnc);
double src_height = vnc_client_get_height(w->vnc);
double dst_width = w->back_buffer->width;
double dst_height = w->back_buffer->height;
double hratio = (double)dst_width / (double)src_width;
double vratio = (double)dst_height / (double)src_height;
*scale = fmin(hratio, vratio);
if (hratio < vratio + 0.01 && hratio > vratio - 0.01) {
*x_pos = 0;
*y_pos = 0;
} else if (hratio < vratio) {
*x_pos = 0;
*y_pos = round(dst_height / 2.0 - *scale * src_height / 2.0);
} else {
*x_pos = round(dst_width / 2.0 - *scale * src_width / 2.0);
*y_pos = 0;
}
}
static void window_transfer_pixels(struct window* w) static void window_transfer_pixels(struct window* w)
{ {
void* src_pixels = w->vnc_fb; void* src_pixels = w->vnc_fb;
int src_width = w->back_buffer->width; int src_width = vnc_client_get_width(w->vnc);
int src_height = w->back_buffer->height; int src_height = vnc_client_get_height(w->vnc);
int src_stride = w->back_buffer->stride; int src_stride = vnc_client_get_stride(w->vnc);
void* dst_pixels = w->back_buffer->pixels; void* dst_pixels = w->back_buffer->pixels;
int dst_width = w->back_buffer->width; int dst_width = w->back_buffer->width;
@ -317,26 +354,36 @@ static void window_transfer_pixels(struct window* w)
pixman_image_t* srcimg = pixman_image_create_bits_no_clear(src_fmt, pixman_image_t* srcimg = pixman_image_create_bits_no_clear(src_fmt,
src_width, src_height, src_pixels, src_stride); src_width, src_height, src_pixels, src_stride);
// pixman_transform_t xform; double scale;
// pixman_image_set_transform(srcimg, pxform); int x_pos, y_pos;
window_calculate_tranform(w, &scale, &x_pos, &y_pos);
pixman_fixed_t src_scale = pixman_double_to_fixed(1.0 / scale);
pixman_transform_t xform;
pixman_transform_init_scale(&xform, src_scale, src_scale);
pixman_image_set_transform(srcimg, &xform);
pixman_image_set_clip_region(dstimg, &w->back_buffer->damage); pixman_image_set_clip_region(dstimg, &w->back_buffer->damage);
pixman_image_composite(PIXMAN_OP_OVER, srcimg, NULL, dstimg, pixman_image_composite(PIXMAN_OP_OVER, srcimg, NULL, dstimg,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, x_pos, y_pos,
dst_width, dst_height); dst_width, dst_height);
pixman_image_unref(srcimg); pixman_image_unref(srcimg);
pixman_image_unref(dstimg); pixman_image_unref(dstimg);
pixman_region_clear(&w->back_buffer->damage);
} }
static void window_commit(struct window* w) static void window_commit(struct window* w)
{ {
window_transfer_pixels(w);
wl_surface_commit(w->wl_surface); wl_surface_commit(w->wl_surface);
}
static void window_swap(struct window* w)
{
struct buffer* tmp = w->front_buffer; struct buffer* tmp = w->front_buffer;
w->front_buffer = w->back_buffer; w->front_buffer = w->back_buffer;
w->back_buffer = tmp; w->back_buffer = tmp;
@ -344,10 +391,6 @@ static void window_commit(struct window* w)
static void window_damage(struct window* w, int x, int y, int width, int height) static void window_damage(struct window* w, int x, int y, int width, int height)
{ {
pixman_region_union_rect(&w->front_buffer->damage,
&w->front_buffer->damage, x, y, width, height);
pixman_region_union_rect(&w->back_buffer->damage,
&w->back_buffer->damage, x, y, width, height);
wl_surface_damage(w->wl_surface, x, y, width, height); wl_surface_damage(w->wl_surface, x, y, width, height);
} }
@ -368,10 +411,26 @@ static const struct xdg_surface_listener xdg_surface_listener = {
.configure = xdg_surface_configure, .configure = xdg_surface_configure,
}; };
static void window_resize(struct window* w, int width, int height)
{
if (width == 0 || height == 0)
return;
if (w->back_buffer && w->back_buffer->width == width &&
w->back_buffer->height == height)
return;
buffer_destroy(w->front_buffer);
buffer_destroy(w->back_buffer);
w->front_buffer = buffer_create(width, height, 4 * width, wl_shm_format);
w->back_buffer = buffer_create(width, height, 4 * width, wl_shm_format);
}
static void xdg_toplevel_configure(void* data, struct xdg_toplevel* toplevel, static void xdg_toplevel_configure(void* data, struct xdg_toplevel* toplevel,
int32_t width, int32_t height, struct wl_array* state) int32_t width, int32_t height, struct wl_array* state)
{ {
// What to do here? window_resize(data, width, height);
} }
static void xdg_toplevel_close(void* data, struct xdg_toplevel* toplevel) static void xdg_toplevel_close(void* data, struct xdg_toplevel* toplevel)
@ -423,11 +482,8 @@ wl_surface_failure:
static void window_destroy(struct window* w) static void window_destroy(struct window* w)
{ {
if (w->front_buffer) buffer_destroy(w->front_buffer);
buffer_destroy(w->front_buffer); buffer_destroy(w->back_buffer);
if (w->back_buffer)
buffer_destroy(w->back_buffer);
free(w->vnc_fb); free(w->vnc_fb);
xdg_toplevel_destroy(w->xdg_toplevel); xdg_toplevel_destroy(w->xdg_toplevel);
@ -441,8 +497,12 @@ void on_pointer_event(struct pointer_collection* collection,
{ {
struct vnc_client* client = collection->userdata; struct vnc_client* client = collection->userdata;
int x = wl_fixed_to_int(pointer->x); double scale;
int y = wl_fixed_to_int(pointer->y); int x_pos, y_pos;
window_calculate_tranform(window, &scale, &x_pos, &y_pos);
int x = round((wl_fixed_to_double(pointer->x) - (double)x_pos) / scale);
int y = round((wl_fixed_to_double(pointer->y) - (double)y_pos) / scale);
enum pointer_button_mask pressed = pointer->pressed; enum pointer_button_mask pressed = pointer->pressed;
int vertical_steps = pointer->vertical_scroll_steps; int vertical_steps = pointer->vertical_scroll_steps;
@ -495,21 +555,17 @@ void on_keyboard_event(struct keyboard_collection* collection,
int on_vnc_client_alloc_fb(struct vnc_client* client) int on_vnc_client_alloc_fb(struct vnc_client* client)
{ {
assert(!window); // TODO: Support resizing
int width = vnc_client_get_width(client); int width = vnc_client_get_width(client);
int height = vnc_client_get_height(client); int height = vnc_client_get_height(client);
int stride = vnc_client_get_stride(client); int stride = vnc_client_get_stride(client);
window = window_create(app_id, vnc_client_get_desktop_name(client)); if (!window) {
if (!window) window = window_create(app_id, vnc_client_get_desktop_name(client));
return -1; window->vnc = client;
window_resize(window, width, height);
window->front_buffer = buffer_create(width, height, stride, }
wl_shm_format);
window->back_buffer = buffer_create(width, height, stride,
wl_shm_format);
free(window->vnc_fb);
window->vnc_fb = malloc(height * stride); window->vnc_fb = malloc(height * stride);
assert(window->vnc_fb); assert(window->vnc_fb);
@ -527,9 +583,22 @@ void on_vnc_client_update_fb(struct vnc_client* client)
window_attach(window, 0, 0); window_attach(window, 0, 0);
double scale;
int x_pos, y_pos;
window_calculate_tranform(window, &scale, &x_pos, &y_pos);
struct pixman_region16 damage_scaled = { 0 }, damage = { 0 };
region_scale(&damage_scaled, &client->damage, scale);
region_translate(&damage, &damage_scaled, x_pos, y_pos);
pixman_region_fini(&damage_scaled);
pixman_region_union(&window->front_buffer->damage,
&window->front_buffer->damage, &damage);
pixman_region_union(&window->back_buffer->damage,
&window->back_buffer->damage, &damage);
int n_rects = 0; int n_rects = 0;
struct pixman_box16* box = pixman_region_rectangles(&client->damage, struct pixman_box16* box = pixman_region_rectangles(&damage, &n_rects);
&n_rects);
for (int i = 0; i < n_rects; ++i) { for (int i = 0; i < n_rects; ++i) {
int x = box[i].x1; int x = box[i].x1;
@ -540,7 +609,12 @@ void on_vnc_client_update_fb(struct vnc_client* client)
window_damage(window, x, y, width, height); window_damage(window, x, y, width, height);
} }
pixman_region_fini(&damage);
window_transfer_pixels(window);
window_commit(window); window_commit(window);
window_swap(window);
} }
void on_vnc_client_event(void* obj) void on_vnc_client_event(void* obj)