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

ref: 119b8a923248e482064e6a1f20181470c73191b7 CTU-FEE-B0B35APO-Semestral-project/image-viewer/src/image.c -rw-r--r-- 5.1 KiB
119b8a92 — František Boháček feat: use NN for downscaling and optimize without using floats 4 years ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
#include "image.h"
#include "direction.h"
#include "display_utils.h"
#include <stdlib.h>



image_t image_create(char *path) {
  image_t image = {
    .path = path,
    .width = 0,
    .height = 0,
    .pixels = NULL,
    .type = IMG_UNKNOWN,
  };

  return image;
}

void image_destroy(image_t *image) {
  if (image->pixels == NULL) {
    free(image->pixels);
  }
}

image_region_t image_region_create(uint16_t x, uint16_t y, uint16_t width,
                                   uint16_t height) {
  image_region_t region = {
    .x = x,
    .y = y,
    .width = width,
    .height = height,
  };

  return region;
}

bool image_region_move_within(image_region_t *to_move, direction_t direction,
                              int amount, image_region_t *border) {
  int32_t x = to_move->x;
  int32_t y = to_move->y;
  direction_move_xy(direction, &x, &y, amount);

  if (x < border->x) {
    x = border->x;
  } else if (x + to_move->width >= border->width + border->x) {
    x = border->x + border->width - 1 - to_move->width;
  }

  if (y < border->y) {
    y = border->y;
  } else if (y + to_move->height >= border->height + border->y) {
    y = border->x + border->height - 1 - to_move->height;
  }

  bool changed = to_move->x != x || to_move->y != y;
  to_move->x = x;
  to_move->y = y;

  return changed;
}

display_pixel_t image_get_pixel(image_t *image, uint16_t x, uint16_t y) {
  if (y >= image->height || x >= image->width) {
    return BLACK_PIXEL;
  }
  return image->pixels[y * image->width + x];
}

void image_set_pixel(image_t *image, uint16_t x, uint16_t y,
                       display_pixel_t pixel) {
  if (y >= image->height || x >= image->width) {
    return;
  }

  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;
  const downscale_precision = 10000;
  uint32_t downscale_factor_i = downscale_factor * downscale_precision;
  uint16_t w = region.width, h = region.height;
  uint16_t sw = (uint16_t)(scale_factor * w), sh = (uint16_t)(scale_factor * h);

  unsigned long avg_range = ((unsigned long)downscale_factor);
  if (scale_factor - avg_range >= 0.5) {
    avg_range++;
  }

  uint16_t beg_x = (display_region.width - sw) / 2 + display_region.x;
  uint16_t beg_y = (display_region.height - sh) / 2 + display_region.y;

  for (int y = 0; y < sh; y++) {
    for (int x = 0; x < sw; x++) {
      uint16_t px = ((uint64_t)(downscale_factor_i * (2*x + 1)))/(downscale_precision * 2) + region.x;
      uint16_t py = ((uint64_t)(downscale_factor_i * (2*y + 1)))/(downscale_precision * 2) + region.y;
      display_pixel_t result_display = image_get_pixel(image, px, py);
      display_set_pixel(display, x + beg_x, y + beg_y, result_display);
    }
  }
}

static void image_write_upscale(image_t *image, display_t *display,
                                image_region_t region, image_region_t display_region, double scale_factor) {
  float downscale_factor = 1 / scale_factor;
  uint16_t w = region.width, h = region.height;
  uint16_t sw = (uint16_t)(scale_factor * w), sh = (uint16_t)(scale_factor * h);

  uint16_t beg_x = (display_region.width - sw) / 2 + display_region.x;
  uint16_t beg_y = (display_region.height - sh) / 2 + display_region.y;

  for (int y = 0; y < sh; y++) {
    for (int x = 0; x < sw; x++) {
      float px = (downscale_factor * (x + 0.5f)) + region.x;
      float py = (downscale_factor * (y + 0.5f)) + region.y;

      display_pixel_t result_display = image_get_pixel(image, px, py);
      display_set_pixel(display, x + beg_x, y + beg_y, result_display);
    }
  }
}

static void image_write_direct(image_t *image, display_t *display,
                               image_region_t region, image_region_t display_region) {
  for (int y = 0; y < region.height; y++) {
    for (int x = 0; x < region.width; x++) {
      display_set_pixel(display, x + display_region.x, y + display_region.y, image_get_pixel(image, x + region.x, y + region.y));
    }
  }
}

double image_write_to_display(image_t *image, display_t *display,
                              image_region_t region, image_region_t display_region) {
  display_clear(display, false);
  uint16_t w = region.width, h = region.height;
  if (w == display_region.width && h == display_region.height) {
    // write directly to image
    image_write_direct(image, display, region, display_region);
    return 1;
  }

  double scale = get_scale_factor(w, h, display_region);

  // scaling
  if (scale < 1) {
    image_write_downscale(image, display, region, display_region, scale);
  } else {
    image_write_upscale(image, display, region, display_region, scale);
  }

  return scale;
}