juniorcamp/main.c

223 lines
6.9 KiB
C

#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <math.h>
#include <memory.h>
#include <imago2.h>
#include "gfx.h"
#define MASK_MULTIPLIER 50
typedef struct {
int32_t distance;
int8_t direction;
} distance_T;
typedef struct {
uint32_t width;
uint32_t height;
uint32_t new_width;
uint32_t new_height;
uint32_t* buffer;
distance_T* dist_buffer;
uint32_t* mask_buffer;
} image_T;
int32_t pixel_distance(uint32_t pixel_a, uint32_t pixel_b, uint32_t pixel_m) {
uint8_t r_a = (pixel_a) & 0xFF;
uint8_t g_a = (pixel_a >> 8) & 0xFF;
uint8_t b_a = (pixel_a >> 16) & 0xFF;
uint8_t r_b = (pixel_b) & 0xFF;
uint8_t g_b = (pixel_b >> 8) & 0xFF;
uint8_t b_b = (pixel_b >> 16) & 0xFF;
uint8_t r_m = (pixel_m) & 0xFF;
uint8_t g_m = (pixel_m >> 8) & 0xFF;
int32_t dist = (int32_t)sqrt(pow(r_a - r_b, 2) +
pow(g_a - g_b, 2) +
pow(b_a - b_b, 2)) +
(r_m * MASK_MULTIPLIER) -
(g_m * MASK_MULTIPLIER);
return dist;
}
image_T* image_load(const char* filename, const char* filename_mask) {
printf("loading '%s'...\n", filename);
image_T* image = malloc(sizeof(image_T));
image->mask_buffer = img_load_pixels(filename_mask, (int*)&image->width, (int*)&image->height, IMG_FMT_RGBA32);
image->buffer = img_load_pixels(filename, (int*)&image->width, (int*)&image->height, IMG_FMT_RGBA32);
image->dist_buffer = calloc(image->width * image->height, sizeof(distance_T));
image->new_width = image->width;
image->new_height = image->height;
}
void image_destruct(image_T* image) {
printf("destructing resources\n");
img_free_pixels(image->buffer);
img_free_pixels(image->mask_buffer);
free(image->dist_buffer);
free(image);
}
void image_calculate_dist_buffer(image_T* image) {
memset(image->dist_buffer, 0, image->width * sizeof(distance_T));
uint32_t* mask_row = &image->mask_buffer[image->width];
uint32_t* last_row = image->buffer;
uint32_t* pixel_row = &last_row[image->width];
distance_T* dist_last_row = image->dist_buffer;
distance_T* dist_pixel_row = &dist_last_row[image->width];
for (int y = 1; y < image->new_height; y++) {
for (int x = 0; x < image->new_width; x++) {
// uint32_t pixel = pixel_row[x];
//
// distance_T shortest_dist;
// bool found_dist = false;
// for (int i = (x > 0 ? -1 : 0); i < (x < image->new_width-1 ? 2 : 1); i++) {
// int32_t dist = 0;
// dist += pixel_distance(pixel, last_row[x + i], image->mask_buffer[(y * image->width) + x + i]);
// dist += dist_last_row[x + i].distance;
//
// if (!found_dist || shortest_dist.distance > dist) {
// shortest_dist = (distance_T){dist, (int8_t)i};
// found_dist = true;
// }
// }
int32_t dist;
distance_T shortest_dist;
if (x > 0) {
dist = 0;
dist += dist_last_row[x - 1].distance;
dist += pixel_distance(pixel_row[x - 1], pixel_row[x + 1], mask_row[x]);
dist += pixel_distance(last_row [x], pixel_row[x - 1], mask_row[x]);
shortest_dist = (distance_T) {dist, -1};
}
dist = -10;
dist += dist_last_row[x].distance;
dist += pixel_distance(pixel_row[x - 1], pixel_row[x + 1], mask_row[x]);
if (x == 0 || shortest_dist.distance > dist) {
shortest_dist = (distance_T) {dist, 0};
}
if (x < image->new_width-1) {
dist = 0;
dist += dist_last_row[x + 1].distance;
dist += pixel_distance(pixel_row[x - 1], pixel_row[x + 1], mask_row[x]);
dist += pixel_distance(last_row [x], pixel_row[x + 1], mask_row[x]);
if (shortest_dist.distance > dist) {
shortest_dist = (distance_T) {dist, 1};
}
}
dist_pixel_row[x] = shortest_dist;
}
mask_row = &mask_row[image->width];
last_row = pixel_row;
pixel_row = &pixel_row[image->width];
dist_last_row = dist_pixel_row;
dist_pixel_row = &dist_pixel_row[image->width];
}
}
uint32_t image_find_best_line(image_T* image) {
distance_T* row = &image->dist_buffer[(image->new_height-1) * image->width];
uint32_t line_id = 0;
distance_T shortest_dist;
bool found_dist = false;
for (uint32_t x = 0; x < image->new_width; x++) {
if (!found_dist || shortest_dist.distance > row[x].distance) {
shortest_dist = row[x];
line_id = x;
found_dist = true;
}
}
return line_id;
}
void image_cut_line(image_T* image, uint32_t line_id) {
uint32_t x = line_id;
for (uint32_t y = image->new_height-1; y > 0; y--) {
distance_T dist = image->dist_buffer[(image->width * y) + x];
x += dist.direction;
memcpy(&image->buffer[(image->width * y) + x], &image->buffer[(image->width * y) + x + 1], (image->new_width - x) * sizeof(uint32_t));
memcpy(&image->mask_buffer[(image->width * y) + x], &image->mask_buffer[(image->width * y) + x + 1], (image->new_width - x) * sizeof(uint32_t));
gfx_color(0, 0xFF, 0);
gfx_point(x, y);
}
image->new_width -= 1;
gfx_flush();
}
void image_dump(image_T* image) {
for (int y = 0; y < image->new_height; y++) {
uint32_t *pixel_row = &image->buffer[y * image->width];
for (int x = 0; x < image->new_width; x++) {
gfx_color((uint8_t) (pixel_row[x] & 0xFF),
(uint8_t) ((pixel_row[x] >> 8) & 0xFF),
(uint8_t) ((pixel_row[x] >> 16) & 0xFF));
gfx_point(x, y);
}
}
gfx_color(0, 0, 0);
for (int y = 0; y < image->new_height; y++) {
gfx_point(image->new_width, y);
}
gfx_flush();
}
int main(int argc, char* argv[]) {
printf("Line Cutter - Juniorcamp Informatik\n");
image_T* image = image_load("../paris.png", "../paris_mask.png");
gfx_open(800, 600, "Juniorcamp Informatik - Line Cutter");
image_dump(image);
bool running = true;
while(running) {
char c = gfx_wait();
image_dump(image);
switch (c) {
case 's': {
if (image->new_width > 0) {
image_calculate_dist_buffer(image);
image_cut_line(image, image_find_best_line(image));
}
break;
}
case 'q': {
running = false;
break;
}
}
}
image_destruct(image);
return 0;
}