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

ref: d2ba791f0db9f75f7984b67d64afeb8bb0e98748 CTU-FEE-B0B35APO-Semestral-project/image-viewer/src/image.c -rw-r--r-- 5.2 KiB
d2ba791f — František Boháček feat: add image write to display 3 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
170
171
172
173
174
175
176
177
#include "image.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) {
  uint16_t x = to_move->x;
  uint16_t y = to_move->y;

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

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

  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) {
  return image->pixels[y * image->width + x];
}

void image_set_pixel(image_t *image, uint16_t x, uint16_t y,
                       display_pixel_t pixel) {
  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, double scale_factor) {
  float downscale_factor = 1 / scale_factor;
  uint16_t avg_pixels = downscale_factor;
  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++;
  }

  uint32_t avg_count = avg_pixels * avg_pixels;
  uint32_t downhalf = downscale_factor / 2;

  uint16_t beg_x = (DISPLAY_WIDTH - sw) / 2;
  uint16_t beg_y = (DISPLAY_HEIGHT - sh) / 2;

  for (int y = 0; y < sh; y++) {
    for (int x = 0; x < sw; x++) {
      uint16_t px = (uint16_t)(downscale_factor * (x + 0.5f)) + region.x;
      uint16_t py = (uint16_t)(downscale_factor * (y + 0.5f)) + region.y;
      raw_pixel_t result = {.red = 0, .green = 0, .blue = 0};

      for (int avg_x = 0; avg_x < avg_pixels; avg_x++) {
        for (int avg_y = 0; avg_y < avg_pixels; avg_y++) {
          display_pixel_t pixel = image_get_pixel(image, px + avg_x - downhalf, py + avg_y - downhalf);

          result.red += pixel.fields.r;
          result.green += pixel.fields.g;
          result.blue += pixel.fields.b;
        }
      }

      result.red /= avg_count;
      result.green /= avg_count;
      result.blue /= avg_count;

      display_pixel_t result_display = {.fields = {.r = result.red, .g = result.green, .b = result.blue}};
      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, 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_WIDTH - sw) / 2;
  uint16_t beg_y = (DISPLAY_HEIGHT - sh) / 2;

  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) {
  for (int y = 0; y < region.height; y++) {
    for (int x = 0; x < region.width; x++) {
      display_set_pixel(display, x, 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) {
  uint16_t w = region.width, h = region.height;
  if (w == DISPLAY_WIDTH && h == DISPLAY_HEIGHT) {
    // write directly to image
    image_write_direct(image, display, region);
    return 1;
  }

  image_region_t display_region = image_region_create(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT);
  double scale = get_scale_factor(w, h, display_region);

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

  return scale;
}
Do not follow this link