From 62fe007cfcf4b3951bf7b2289c08a8a149f03714 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franti=C5=A1ek=20Boh=C3=A1=C4=8Dek?= Date: Thu, 17 Jun 2021 13:57:21 +0200 Subject: [PATCH] feat: add zooming to cursor --- image-viewer/include/coords.h | 16 +++++ image-viewer/include/cursor.h | 4 +- image-viewer/include/image.h | 14 +++- image-viewer/include/image_viewer.h | 4 +- image-viewer/src/coords.c | 80 +++++++++++++++++++++ image-viewer/src/cursor.c | 21 ++++-- image-viewer/src/image.c | 86 +++++++++++++++++------ image-viewer/src/image_viewer.c | 105 ++++++++++++++++++++++++---- image-viewer/src/input.c | 1 + 9 files changed, 281 insertions(+), 50 deletions(-) create mode 100644 image-viewer/include/coords.h create mode 100644 image-viewer/src/coords.c diff --git a/image-viewer/include/coords.h b/image-viewer/include/coords.h new file mode 100644 index 0000000..20b0258 --- /dev/null +++ b/image-viewer/include/coords.h @@ -0,0 +1,16 @@ +#include +#include "image.h" +#include "display_utils.h" + +typedef struct { + int32_t x; + int32_t y; +} coords_t; + +coords_t coords_create(int32_t x, int32_t y); +coords_t coords_get_image_screen_beg_coord( + image_t *image, image_zoom_t zoom); +coords_t coords_get_image_screen_end_coords(image_t *image, image_zoom_t zoom); + +coords_t image_get_screen_coords(image_t *image, image_zoom_t zoom, coords_t image_coords); +coords_t image_get_image_coords(image_t *image, image_zoom_t zoom, coords_t screen_coords); diff --git a/image-viewer/include/cursor.h b/image-viewer/include/cursor.h index f354454..3d89626 100644 --- a/image-viewer/include/cursor.h +++ b/image-viewer/include/cursor.h @@ -25,7 +25,7 @@ cursor_t cursor_create(); void cursor_center(cursor_t *cursor, image_region_t region); bool cursor_move(cursor_t *cursor, image_region_t region, direction_t direction, int16_t amount); -void cursor_show(cursor_t *cursor, display_t *display); -void cursor_hide(cursor_t *cursor, display_t *display); +void cursor_show(cursor_t *cursor, image_t *image, image_zoom_t zoom, display_t *display); +void cursor_hide(cursor_t *cursor, image_t *image, image_zoom_t zoom, display_t *display); #endif // __CURSOR_H__ diff --git a/image-viewer/include/image.h b/image-viewer/include/image.h index 23b1ef3..b48f5a5 100644 --- a/image-viewer/include/image.h +++ b/image-viewer/include/image.h @@ -39,6 +39,12 @@ typedef struct { uint16_t height; } image_t; +typedef struct { + uint16_t x; + uint16_t y; + double scale; +} image_zoom_t; + typedef struct { uint16_t x; uint16_t y; @@ -59,8 +65,10 @@ image_region_t image_region_create(uint16_t x, uint16_t y, uint16_t width, uint1 bool image_region_move_within(image_region_t *to_move, direction_t direction, int amount, image_region_t *border); -double image_write_to_display(image_t *image, display_t *display, - image_region_t region, - image_region_t display_region); +image_zoom_t image_write_to_display(image_t *image, display_t *display, + image_zoom_t scale); + +image_zoom_t image_get_initial_zoom(image_t *image); +image_region_t image_get_zoom_region(image_t *image, image_zoom_t zoom); #endif // __IMAGE_H__ diff --git a/image-viewer/include/image_viewer.h b/image-viewer/include/image_viewer.h index 4738942..512f648 100644 --- a/image-viewer/include/image_viewer.h +++ b/image-viewer/include/image_viewer.h @@ -10,7 +10,7 @@ typedef struct { image_t image; cursor_t cursor; - image_region_t region; + image_zoom_t scale; image_region_t image_region; image_region_t display_region; image_error_t error; @@ -18,8 +18,6 @@ typedef struct { display_t *display; bool running; - double scale_factor; - logger_t *logger; } image_viewer_t; diff --git a/image-viewer/src/coords.c b/image-viewer/src/coords.c new file mode 100644 index 0000000..164c1c6 --- /dev/null +++ b/image-viewer/src/coords.c @@ -0,0 +1,80 @@ +#include "coords.h" + +coords_t coords_create(int32_t x, int32_t y) { + coords_t coords = { + .x = x, + .y = y + }; + + return coords; +} + +coords_t coords_get_image_screen_beg_coord(image_t *image, image_zoom_t zoom) { + uint16_t scaled_w = (uint16_t)(zoom.scale * image->width), + scaled_h = (uint16_t)(zoom.scale * image->height); + + int32_t beg_x = ((double)DISPLAY_WIDTH - (double)(scaled_w)) / 2; + int32_t beg_y = ((double)DISPLAY_HEIGHT - (double)(scaled_h)) / 2; + + if (beg_x < 0) { + beg_x = 0; + } + + if (beg_y < 0) { + beg_y = 0; + } + + return coords_create(beg_x, beg_y); +} + +coords_t coords_get_image_screen_end_coords(image_t *image, image_zoom_t zoom) { + uint16_t scaled_w = (uint16_t)(zoom.scale * image->width), + scaled_h = (uint16_t)(zoom.scale * image->height); + + int32_t end_x = ((double)DISPLAY_WIDTH + (double)(scaled_w)) / 2; + int32_t end_y = ((double)DISPLAY_HEIGHT + (double)(scaled_h)) / 2; + + if (end_x > DISPLAY_WIDTH - 1) { + end_x = DISPLAY_WIDTH - 1; + } + + if (end_y > DISPLAY_HEIGHT - 1) { + end_y = DISPLAY_HEIGHT - 1; + } + + return coords_create(end_x, end_y); +} + +coords_t image_get_screen_coords(image_t *image, image_zoom_t zoom, + coords_t image_coords) { + coords_t beg = coords_get_image_screen_beg_coord(image, zoom); + coords_t end = coords_get_image_screen_end_coords(image, zoom); + + image_region_t image_region = + image_get_zoom_region(image, zoom); + double relative_x = (double)(image_coords.x - (int32_t)image_region.x) / image_region.width; + double relative_y = (double)(image_coords.y - (int32_t)image_region.y) / image_region.height; + printf("(%d - %hu) - %hu = %f\r\n", image_coords.x, image_region.x, image_region.width, relative_x); + printf("image: %f %f\r\n", relative_x, relative_y); + + coords_t screen_coords = coords_create(beg.x + relative_x * (end.x - beg.x), + beg.y + relative_y * (end.y - beg.y)); + return screen_coords; +} + +coords_t image_get_image_coords(image_t *image, image_zoom_t zoom, + coords_t screen_coords) { + coords_t beg = coords_get_image_screen_beg_coord(image, zoom); + coords_t end = coords_get_image_screen_end_coords(image, zoom); + + image_region_t image_region = + image_get_zoom_region(image, zoom); + double relative_x = (double)(screen_coords.x - beg.x) / (end.x - beg.x); + double relative_y = (double)(screen_coords.y - beg.y) / (end.y - beg.y); + printf("screen: %f %f\r\n", relative_x, relative_y); + + coords_t image_coords = + coords_create(image_region.x + relative_x * image_region.width, + image_region.y + relative_y * image_region.height); + return image_coords; +} diff --git a/image-viewer/src/cursor.c b/image-viewer/src/cursor.c index c5ced6b..0bdcbbb 100644 --- a/image-viewer/src/cursor.c +++ b/image-viewer/src/cursor.c @@ -2,6 +2,7 @@ #include "direction.h" #include "display_utils.h" #include "image.h" +#include "coords.h" const display_pixel_t CURSOR_COLOR = {.fields = {.r = (uint8_t)DISPLAY_MAX_RED, .g = 0, .b = 0}}; @@ -43,13 +44,16 @@ bool cursor_move(cursor_t *cursor, image_region_t region, direction_t direction, return moved; } -void cursor_show(cursor_t *cursor, display_t *display) { - cursor_hide(cursor, display); +void cursor_show(cursor_t *cursor, image_t *image, image_zoom_t zoom, + display_t *display) { + cursor_hide(cursor, image, zoom, display); cursor->shown_at = time(NULL); cursor->shown = true; - uint16_t base_x = cursor->x; - uint16_t base_y = cursor->y; + coords_t screen_coords = + image_get_screen_coords(image, zoom, coords_create(cursor->x, cursor->y)); + uint16_t base_x = screen_coords.x; + uint16_t base_y = screen_coords.y; uint16_t first_x = base_x - CURSOR_SIZE / 2; uint16_t first_y = base_y - CURSOR_SIZE / 2; @@ -66,13 +70,16 @@ void cursor_show(cursor_t *cursor, display_t *display) { } } -void cursor_hide(cursor_t *cursor, display_t *display) { +void cursor_hide(cursor_t *cursor, image_t *image, image_zoom_t zoom, + display_t *display) { if (!cursor->shown) { return; } - uint16_t base_x = cursor->x; - uint16_t base_y = cursor->y; + coords_t screen_coords = + image_get_screen_coords(image, zoom, coords_create(cursor->x, cursor->y)); + uint16_t base_x = screen_coords.x; + uint16_t base_y = screen_coords.y; uint16_t first_x = base_x - CURSOR_SIZE / 2; uint16_t first_y = base_y - CURSOR_SIZE / 2; diff --git a/image-viewer/src/image.c b/image-viewer/src/image.c index 17724a9..5c5dc59 100644 --- a/image-viewer/src/image.c +++ b/image-viewer/src/image.c @@ -76,21 +76,6 @@ void image_set_pixel(image_t *image, uint16_t x, uint16_t y, image->pixels[y * image->width + x] = pixel; } -double get_scale_factor(uint16_t w, uint16_t h, image_region_t display_region) { - double scale_x = (double)display_region.width / (double)w; - double scale_y = (double)display_region.height / (double)h; - - double max = scale_x > scale_y ? scale_x : scale_y; - double min = scale_x <= scale_y ? scale_x : scale_y; - double scale = max; - - if (w * max > display_region.width || h * max > display_region.height) { - scale = min; - } - - return scale; -} - static void image_write_downscale(image_t *image, display_t *display, image_region_t region, image_region_t display_region, double scale_factor) { float downscale_factor = 1 / scale_factor; @@ -148,17 +133,74 @@ static void image_write_direct(image_t *image, display_t *display, } } -double image_write_to_display(image_t *image, display_t *display, - image_region_t region, image_region_t display_region) { +double min(double a, double b) { + return a < b ? a : b; +} + +image_zoom_t image_get_initial_zoom(image_t *image) { + double scale_x = (double)DISPLAY_WIDTH / image->width; + double scale_y = (double)DISPLAY_HEIGHT / image->height; + + double scale = min(min(scale_x, scale_y), 1); + + image_zoom_t zoom = { + .scale = scale, + .x = 0, + .y = 0, + }; + + return zoom; +} + +image_region_t image_get_zoom_region(image_t *image, image_zoom_t zoom) { + double fits_screen_width = DISPLAY_WIDTH / (double)(image->width * zoom.scale); + double fits_screen_height = + DISPLAY_HEIGHT / (double)(image->height * zoom.scale); + + uint16_t width = fits_screen_width * image->width; + uint16_t height = fits_screen_height * image->height; + + if (fits_screen_width > 1) { + width = image->width; + } + + if (fits_screen_height > 1) { + height = image->height; + } + + image_region_t region = { + .x = zoom.x, + .y = zoom.y, + .width = width, + .height = height + }; + + if (region.x + region.width > image->width) { + region.x = image->width - region.width; + } + + if (region.y + region.height > image->height) { + region.y = image->height - region.height; + } + + return region; +} + +image_zoom_t image_write_to_display(image_t *image, display_t *display, + image_zoom_t zoom) { display_clear(display, false); - uint16_t w = region.width, h = region.height; - if (w == display_region.width && h == display_region.height) { + image_region_t region = image_get_zoom_region(image, zoom); + zoom.x = region.x; + zoom.y = region.y; + + image_region_t display_region = image_region_create(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT); + if (zoom.scale == 1) { // write directly to image image_write_direct(image, display, region, display_region); - return 1; + return zoom; } - double scale = get_scale_factor(w, h, display_region); + double scale = zoom.scale; // scaling if (scale < 1) { @@ -167,5 +209,5 @@ double image_write_to_display(image_t *image, display_t *display, image_write_upscale(image, display, region, display_region, scale); } - return scale; + return zoom; } diff --git a/image-viewer/src/image_viewer.c b/image-viewer/src/image_viewer.c index 49383c8..55316cf 100644 --- a/image-viewer/src/image_viewer.c +++ b/image-viewer/src/image_viewer.c @@ -1,34 +1,38 @@ #include "image_viewer.h" #include "cursor.h" +#include "direction.h" #include "display_utils.h" #include "image.h" #include "input.h" #include "logger.h" #include "image_loader.h" +#include "coords.h" #include #include #define COMMANDS_NUM 30 #define CURSOR_SHOW_DURATION 3 // seconds +#define MAX_ZOOM 10 +#define MIN_ZOOM 0.02 + image_viewer_t image_viewer_create(char *filename, display_t *display, logger_t *logger) { image_viewer_t viewer = { .display = display, .image = image_create(filename), .cursor = cursor_create(), - .region = image_region_create(0, 0, 0, 0), .logger = logger, .display_region = image_region_create(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT), .image_region = image_region_create(0, 0, 0, 0), - .scale_factor = 0 }; viewer.error = image_loader_load(&viewer.image); if (viewer.error == IMERR_SUCCESS) { - viewer.region = viewer.image_region = + viewer.image_region = image_region_create(0, 0, viewer.image.width, viewer.image.height); - cursor_center(&viewer.cursor, viewer.display_region); + viewer.scale = image_get_initial_zoom(&viewer.image); + cursor_center(&viewer.cursor, viewer.image_region); } return viewer; @@ -41,13 +45,18 @@ void image_viewer_destroy(image_viewer_t *viewer) { void command_handler_move_cursor(void *data, direction_t direction, int amount) { image_viewer_t *viewer = (image_viewer_t *)data; - cursor_hide(&viewer->cursor, viewer->display); - if (!cursor_move(&viewer->cursor, viewer->display_region, direction, amount)) { - image_region_move_within(&viewer->region, direction, (int)(amount * viewer->scale_factor), &viewer->display_region); + cursor_hide(&viewer->cursor, &viewer->image, viewer->scale, viewer->display); + if (!cursor_move(&viewer->cursor, + image_get_zoom_region(&viewer->image, viewer->scale), + direction, amount / viewer->scale.scale)) { + direction_move_xy(direction, (int32_t*)&viewer->scale.x, (int32_t*)&viewer->scale.y, amount / viewer->scale.scale); + cursor_move(&viewer->cursor, + image_get_zoom_region(&viewer->image, viewer->scale), direction, + amount / viewer->scale.scale); image_viewer_display_image(viewer); } - cursor_show(&viewer->cursor, viewer->display); + cursor_show(&viewer->cursor, &viewer->image, viewer->scale, viewer->display); display_render(viewer->display); } @@ -79,32 +88,102 @@ void command_handler_move_down(void *data, int amount) { command_handler_move_cursor(data, DOWN, amount); } + void command_handler_exit(void *data, int amount) { image_viewer_t *viewer = (image_viewer_t *)data; viewer->running = false; } +void zoom(image_viewer_t *viewer, int amount) { + double zoom = viewer->scale.scale; + double new_zoom = zoom * (1 + amount * 0.05); + + if (new_zoom > MAX_ZOOM) { + logger_warn(viewer->logger, __FILE__, __FUNCTION__, __LINE__, + "Cannot zoom more than %f", MAX_ZOOM); + new_zoom = MAX_ZOOM; + } + + if (new_zoom < MIN_ZOOM) { + logger_warn(viewer->logger, __FILE__, __FUNCTION__, __LINE__, + "Cannot zoom less than %f", MIN_ZOOM); + new_zoom = MIN_ZOOM; + } + + image_zoom_t tmp = { + .scale = new_zoom, + .x = viewer->scale.x, + .y = viewer->scale.y, + }; + + image_region_t current_region = image_get_zoom_region(&viewer->image, viewer->scale); + image_region_t new_region = + image_get_zoom_region(&viewer->image, tmp); + + uint16_t scaled_w = (uint16_t)(zoom * viewer->image.width), + scaled_h = (uint16_t)(zoom * viewer->image.height); + + int32_t beg_x = ((double)DISPLAY_WIDTH - (double)(scaled_w)) / 2; + int32_t beg_y = ((double)DISPLAY_HEIGHT - (double)(scaled_h)) / 2; + + if (beg_x < 0) { + beg_x = 0; + } + + if (beg_y < 0) { + beg_y = 0; + } + + if (scaled_w > DISPLAY_WIDTH) { + scaled_w = DISPLAY_WIDTH; + } + + if (scaled_h > DISPLAY_HEIGHT) { + scaled_h = DISPLAY_HEIGHT; + } + + int32_t diff_w = current_region.width - new_region.width; + int32_t diff_h = current_region.height - new_region.height; + + double side_percentage_x = + (double)(viewer->cursor.x - current_region.x) / current_region.width; + double side_percentage_y = + (double)(viewer->cursor.y - current_region.y) / current_region.height; + + tmp.x += side_percentage_x * diff_w; + tmp.y += side_percentage_y * diff_h; + viewer->scale = tmp; + + image_viewer_display_image(viewer); + cursor_show(&viewer->cursor, &viewer->image, viewer->scale, viewer->display); +} + // TODO: implement zoom functions void command_handler_zoom_in(void *data, int amount) { // hide cursor, zoom, show cursor image_viewer_t *viewer = (image_viewer_t *)data; logger_debug(viewer->logger, __FILE__, __FUNCTION__, __LINE__, "Zooming in by %d", amount); + + zoom(viewer, amount); } void command_handler_zoom_out(void *data, int amount) { image_viewer_t *viewer = (image_viewer_t *)data; logger_debug(viewer->logger, __FILE__, __FUNCTION__, __LINE__, "Zooming out by %d", amount); + + zoom(viewer, -amount); } void command_handler_zoom_reset(void *data, int amount) { image_viewer_t *viewer = (image_viewer_t *)data; logger_debug(viewer->logger, __FILE__, __FUNCTION__, __LINE__, "Zoom reset by %d", amount); - viewer->region = image_region_create(0, 0, viewer->image.width, viewer->image.height); + viewer->scale = image_get_initial_zoom(&viewer->image); image_viewer_display_image(viewer); - cursor_show(&viewer->cursor, viewer->display); + cursor_center(&viewer->cursor, viewer->image_region); + cursor_show(&viewer->cursor, &viewer->image, viewer->scale, viewer->display); } void image_viewer_register_commands(image_viewer_t *viewer, commands_t *commands) { @@ -155,14 +234,14 @@ void image_viewer_start_loop(image_viewer_t *viewer, void *reg_knobs_base) { now = time(NULL); if (viewer->cursor.shown && difftime(now, viewer->cursor.shown_at) >= CURSOR_SHOW_DURATION) { - cursor_hide(&viewer->cursor, viewer->display); + cursor_hide(&viewer->cursor, &viewer->image, viewer->scale, viewer->display); display_render(viewer->display); } } } void image_viewer_display_image(image_viewer_t *viewer) { - cursor_hide(&viewer->cursor, viewer->display); - viewer->scale_factor = image_write_to_display(&viewer->image, viewer->display, viewer->region, viewer->display_region); + cursor_hide(&viewer->cursor, &viewer->image, viewer->scale, viewer->display); + viewer->scale = image_write_to_display(&viewer->image, viewer->display, viewer->scale); display_render(viewer->display); } diff --git a/image-viewer/src/input.c b/image-viewer/src/input.c index 50cf6d7..cad8e96 100644 --- a/image-viewer/src/input.c +++ b/image-viewer/src/input.c @@ -3,6 +3,7 @@ #include #include #include +#include #define ROTATION_DELTA 0 #define ENCODERS_MAX 255 -- 2.48.1