~ruther/CTU-FEE-B0B35APO-Semestral-project

62fe007cfcf4b3951bf7b2289c08a8a149f03714 — František Boháček 3 years ago 5350f89
feat: add zooming to cursor
A image-viewer/include/coords.h => image-viewer/include/coords.h +16 -0
@@ 0,0 1,16 @@
#include <stdint.h>
#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);

M image-viewer/include/cursor.h => image-viewer/include/cursor.h +2 -2
@@ 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__

M image-viewer/include/image.h => image-viewer/include/image.h +11 -3
@@ 42,6 42,12 @@ typedef struct {
typedef struct {
  uint16_t x;
  uint16_t y;
  double scale;
} image_zoom_t;

typedef struct {
  uint16_t x;
  uint16_t y;

  uint16_t width;
  uint16_t height;


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

M image-viewer/include/image_viewer.h => image-viewer/include/image_viewer.h +1 -3
@@ 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;


A image-viewer/src/coords.c => image-viewer/src/coords.c +80 -0
@@ 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;
}

M image-viewer/src/cursor.c => image-viewer/src/cursor.c +14 -7
@@ 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;

M image-viewer/src/image.c => image-viewer/src/image.c +64 -22
@@ 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;
}

M image-viewer/src/image_viewer.c => image-viewer/src/image_viewer.c +92 -13
@@ 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 <time.h>
#include <stdlib.h>

#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);
}

M image-viewer/src/input.c => image-viewer/src/input.c +1 -0
@@ 3,6 3,7 @@
#include <stdint.h>
#include <time.h>
#include <unistd.h>
#include <stdio.h>

#define ROTATION_DELTA 0
#define ENCODERS_MAX 255

Do not follow this link