diff --git a/CMakeLists.txt b/CMakeLists.txt index 8ff91bf..b531b90 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ project(Juniorcamp C) set(CMAKE_C_STANDARD 99) -add_executable(Juniorcamp main.c) +add_executable(Juniorcamp main.c gfx.c) -target_link_libraries(Juniorcamp imago m) +target_link_libraries(Juniorcamp imago m X11) target_link_directories(Juniorcamp PRIVATE /usr/local/lib/) \ No newline at end of file diff --git a/bench.png b/bench.png new file mode 100644 index 0000000..a54a577 Binary files /dev/null and b/bench.png differ diff --git a/gfx.c b/gfx.c new file mode 100644 index 0000000..21db48c --- /dev/null +++ b/gfx.c @@ -0,0 +1,206 @@ +/* +A simple graphics library for CSE 20211 by Douglas Thain + +This work is licensed under a Creative Commons Attribution 4.0 International License. https://creativecommons.org/licenses/by/4.0/ + +For complete documentation, see: +http://www.nd.edu/~dthain/courses/cse20211/fall2013/gfx +Version 3, 11/07/2012 - Now much faster at changing colors rapidly. +Version 2, 9/23/2011 - Fixes a bug that could result in jerky animation. +*/ + +#include +#include +#include +#include + +#include "gfx.h" + +/* +gfx_open creates several X11 objects, and stores them in globals +for use by the other functions in the library. +*/ + +static Display *gfx_display=0; +static Window gfx_window; +static GC gfx_gc; +static Colormap gfx_colormap; +static int gfx_fast_color_mode = 0; + +/* These values are saved by gfx_wait then retrieved later by gfx_xpos and gfx_ypos. */ + +static int saved_xpos = 0; +static int saved_ypos = 0; + +/* Open a new graphics window. */ + +void gfx_open( int width, int height, const char *title ) +{ + gfx_display = XOpenDisplay(0); + if(!gfx_display) { + fprintf(stderr,"gfx_open: unable to open the graphics window.\n"); + exit(1); + } + + Visual *visual = DefaultVisual(gfx_display,0); + if(visual && visual->class==TrueColor) { + gfx_fast_color_mode = 1; + } else { + gfx_fast_color_mode = 0; + } + + int blackColor = BlackPixel(gfx_display, DefaultScreen(gfx_display)); + int whiteColor = WhitePixel(gfx_display, DefaultScreen(gfx_display)); + + gfx_window = XCreateSimpleWindow(gfx_display, DefaultRootWindow(gfx_display), 0, 0, width, height, 0, blackColor, blackColor); + + XSetWindowAttributes attr; + attr.backing_store = Always; + + XChangeWindowAttributes(gfx_display,gfx_window,CWBackingStore,&attr); + + XStoreName(gfx_display,gfx_window,title); + + XSelectInput(gfx_display, gfx_window, StructureNotifyMask|KeyPressMask|ButtonPressMask); + + XMapWindow(gfx_display,gfx_window); + + gfx_gc = XCreateGC(gfx_display, gfx_window, 0, 0); + + gfx_colormap = DefaultColormap(gfx_display,0); + + XSetForeground(gfx_display, gfx_gc, whiteColor); + + // Wait for the MapNotify event + + for(;;) { + XEvent e; + XNextEvent(gfx_display, &e); + if (e.type == MapNotify) + break; + } +} + +/* Draw a single point at (x,y) */ + +void gfx_point( int x, int y ) +{ + XDrawPoint(gfx_display,gfx_window,gfx_gc,x,y); +} + +/* Draw a line from (x1,y1) to (x2,y2) */ + +void gfx_line( int x1, int y1, int x2, int y2 ) +{ + XDrawLine(gfx_display,gfx_window,gfx_gc,x1,y1,x2,y2); +} + +/* Change the current drawing color. */ + +void gfx_color( int r, int g, int b ) +{ + XColor color; + + if(gfx_fast_color_mode) { + /* If this is a truecolor display, we can just pick the color directly. */ + color.pixel = ((b&0xff) | ((g&0xff)<<8) | ((r&0xff)<<16) ); + } else { + /* Otherwise, we have to allocate it from the colormap of the display. */ + color.pixel = 0; + color.red = r<<8; + color.green = g<<8; + color.blue = b<<8; + XAllocColor(gfx_display,gfx_colormap,&color); + } + + XSetForeground(gfx_display, gfx_gc, color.pixel); +} + +/* Clear the graphics window to the background color. */ + +void gfx_clear() +{ + XClearWindow(gfx_display,gfx_window); +} + +/* Change the current background color. */ + +void gfx_clear_color( int r, int g, int b ) +{ + XColor color; + color.pixel = 0; + color.red = r<<8; + color.green = g<<8; + color.blue = b<<8; + XAllocColor(gfx_display,gfx_colormap,&color); + + XSetWindowAttributes attr; + attr.background_pixel = color.pixel; + XChangeWindowAttributes(gfx_display,gfx_window,CWBackPixel,&attr); +} + +int gfx_event_waiting() +{ + XEvent event; + + gfx_flush(); + + while (1) { + if(XCheckMaskEvent(gfx_display,-1,&event)) { + if(event.type==KeyPress) { + XPutBackEvent(gfx_display,&event); + return 1; + } else if (event.type==ButtonPress) { + XPutBackEvent(gfx_display,&event); + return 1; + } else { + return 0; + } + } else { + return 0; + } + } +} + +/* Wait for the user to press a key or mouse button. */ + +char gfx_wait() +{ + XEvent event; + + gfx_flush(); + + while(1) { + XNextEvent(gfx_display,&event); + + if(event.type==KeyPress) { + saved_xpos = event.xkey.x; + saved_ypos = event.xkey.y; + return XLookupKeysym(&event.xkey,0); + } else if(event.type==ButtonPress) { + saved_xpos = event.xkey.x; + saved_ypos = event.xkey.y; + return event.xbutton.button; + } + } +} + +/* Return the X and Y coordinates of the last event. */ + +int gfx_xpos() +{ + return saved_xpos; +} + +int gfx_ypos() +{ + return saved_ypos; +} + +/* Flush all previous output to the window. */ + +void gfx_flush() +{ + XFlush(gfx_display); +} + diff --git a/gfx.h b/gfx.h new file mode 100644 index 0000000..6d96143 --- /dev/null +++ b/gfx.h @@ -0,0 +1,51 @@ +/* +A simple graphics library for CSE 20211 by Douglas Thain + +This work is licensed under a Creative Commons Attribution 4.0 International License. https://creativecommons.org/licenses/by/4.0/ + +For course assignments, you should not change this file. +For complete documentation, see: +http://www.nd.edu/~dthain/courses/cse20211/fall2013/gfx +Version 3, 11/07/2012 - Now much faster at changing colors rapidly. +Version 2, 9/23/2011 - Fixes a bug that could result in jerky animation. +*/ + +#ifndef GFX_H +#define GFX_H + +/* Open a new graphics window. */ +void gfx_open( int width, int height, const char *title ); + +/* Draw a point at (x,y) */ +void gfx_point( int x, int y ); + +/* Draw a line from (x1,y1) to (x2,y2) */ +void gfx_line( int x1, int y1, int x2, int y2 ); + +/* Change the current drawing color. */ +void gfx_color( int red, int green, int blue ); + +/* Clear the graphics window to the background color. */ +void gfx_clear(); + +/* Change the current background color. */ +void gfx_clear_color( int red, int green, int blue ); + +/* Wait for the user to press a key or mouse button. */ +char gfx_wait(); + +/* Return the X and Y coordinates of the last event. */ +int gfx_xpos(); +int gfx_ypos(); + +/* Return the X and Y dimensions of the window. */ +int gfx_xsize(); +int gfx_ysize(); + +/* Check to see if an event is waiting. */ +int gfx_event_waiting(); + +/* Flush all previous output to the window. */ +void gfx_flush(); + +#endif diff --git a/landscape.jpg b/landscape.jpg new file mode 100644 index 0000000..aa36127 Binary files /dev/null and b/landscape.jpg differ diff --git a/main.c b/main.c index b58cb22..3cc3b68 100644 --- a/main.c +++ b/main.c @@ -1,15 +1,19 @@ #include +#include #include #include #include #include #include +#include "gfx.h" #define DEBUG_BUILD 0 typedef struct { uint32_t width; uint32_t height; + uint32_t new_width; + uint32_t new_height; uint32_t* buffer; uint32_t* dist_buffer; } image_T; @@ -40,6 +44,8 @@ image_T* image_load(const char* filename) { image_T* image = malloc(sizeof(image_T)); image->buffer = img_load_pixels(filename, (int*)&image->width, (int*)&image->height, IMG_FMT_RGBA32); image->dist_buffer = calloc(image->width * image->height, sizeof(uint32_t)); + image->new_width = image->width; + image->new_height = image->height; } void image_destruct(image_T* image) { @@ -54,15 +60,15 @@ void image_calculate_dist_buffer(image_T* image) { memset(image->dist_buffer, 0, image->height * image->width * sizeof(uint32_t)); uint32_t* last_row = image->buffer; - uint32_t* pixel_row = &last_row[image->height+1]; + uint32_t* pixel_row = &last_row[image->width]; uint32_t* dist_last_row = image->dist_buffer; - uint32_t* dist_pixel_row = &dist_last_row[image->height+1]; + uint32_t* dist_pixel_row = &dist_last_row[image->width]; - for (int y = 1; y < image->height; y++) { + for (int y = 1; y < image->new_height; y++) { #if DEBUG_BUILD printf(" Y\n"); #endif - for (int x = 0; x < image->width; x++) { + for (int x = 0; x < image->new_width; x++) { uint32_t pixel = pixel_row[x]; #if DEBUG_BUILD @@ -70,7 +76,7 @@ void image_calculate_dist_buffer(image_T* image) { #endif int32_t shortest_dist = -1; - for (int i = (x > 0 ? -1 : 0); i < (x < image->width-1 ? 2 : 1); i++) { + for (int i = (x > 0 ? -1 : 0); i < (x < image->new_width-1 ? 2 : 1); i++) { uint32_t dist = pixel_distance(pixel, last_row[x + i]) + dist_last_row[x + i]; if (shortest_dist == -1 || shortest_dist > dist) { @@ -89,62 +95,129 @@ void image_calculate_dist_buffer(image_T* image) { } last_row = pixel_row; - pixel_row = &last_row[image->height+1]; + pixel_row = &pixel_row[image->width]; dist_last_row = dist_pixel_row; - dist_pixel_row = &dist_last_row[image->height+1]; + dist_pixel_row = &dist_pixel_row[image->width]; } } +uint32_t image_find_best_line(image_T* image) { + uint32_t* row = &image->dist_buffer[(image->new_height-1) * image->width]; + uint32_t line_id = 0; + int32_t shortest_dist = -1; + + for (uint32_t x = 0; x < image->new_width; x++) { + if (shortest_dist == -1 || shortest_dist > row[x]) { + shortest_dist = (int32_t)row[x]; + line_id = x; + printf("id: %d dist: %d\n", x, row[x]); + } + } + + return line_id; +} + +void image_cut_line(image_T* image, uint32_t line_id) { + uint32_t x = line_id; + + for (uint32_t y = image->height-1; y > 0; y--) { + int32_t shortest_dist = -1; + int8_t offset; + for (int i = (x > 0 ? -1 : 0); i < (x < image->new_width-1 ? 2 : 1); i++) { + uint32_t dist = image->dist_buffer[(image->width * y) + x + i]; + + if (shortest_dist == -1 || shortest_dist > dist) { + shortest_dist = (int32_t)dist; + offset = i; + } + } + x += offset; + memcpy(&image->buffer[(image->width * y) + x], &image->buffer[(image->width * y) + x + 1], (image->new_width - x) * sizeof(uint32_t)); + gfx_color(0, 0xFF, 0); + gfx_point(x, y); + gfx_flush(); + } + image->new_width -= 1; +} + void image_dump(image_T* image) { - printf("Pixels:\n"); - for (int y = 0; y < image->height; y++) { - uint32_t* pixel_row = &image->buffer[y * (image->height+1)]; + #if DEBUG_BUILD + printf("Pixels:\n"); + #endif + for (int y = 0; y < image->new_height; y++) { + uint32_t* pixel_row = &image->buffer[y * image->width]; - printf(" "); - for (int x = 0; x < image->width; x++) { - printf("%x ", pixel_row[x]); + #if DEBUG_BUILD + printf(" "); + #endif + for (int x = 0; x < image->new_width; x++) { + #if DEBUG_BUILD + printf("%x ", pixel_row[x]); + #endif + 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); } - printf("\n"); + #if DEBUG_BUILD + printf("\n"); + #endif } - printf("Distances:\n"); - for (int y = 0; y < image->height; y++) { - uint32_t* dist_row = &image->dist_buffer[y * (image->height+1)]; + gfx_color(0, 0, 0); + for (int y = 0; y < image->new_height; y++) { + gfx_point(image->new_width, y); + } - printf(" "); + gfx_flush(); + + #if DEBUG_BUILD + printf("Distances:\n"); + #endif + for (int y = 0; y < image->height; y++) { + uint32_t* dist_row = &image->dist_buffer[y * image->width]; + + #if DEBUG_BUILD + printf(" "); + #endif for (int x = 0; x < image->width; x++) { - printf("%x ", dist_row[x]); + #if DEBUG_BUILD + printf("%x ", dist_row[x]); + #endif } - printf("\n"); + #if DEBUG_BUILD + printf("\n"); + #endif } } -uint32_t test_array[12] = { - 0x00000001, 0x00000003, 0x00000005, 0x00000001, - 0x00000001, 0x00000007, 0x00000001, 0x00000002, - 0x0000000A, 0x00000003, 0x00000002, 0x00000005 -}; - int main() { printf("Line Cutter - Juniorcamp Informatik\n"); -// image_T* image = image_load("../paris.png"); + image_T* image = image_load("../paris.png"); - image_T* image = malloc(sizeof(image_T)); - image->height = 3; - image->width = 4; - image->buffer = test_array; - image->dist_buffer = calloc(image->width * image->height, sizeof(uint32_t)); + gfx_open(800, 600, "Juniorcamp Informatik - Line Cutter"); - image_calculate_dist_buffer(image); - image_dump(image); + bool running = true; + while(running) { + image_dump(image); + char c = gfx_wait(); + switch (c) { + case 's': { + image_calculate_dist_buffer(image); + image_cut_line(image, image_find_best_line(image)); + break; + } + case 'q': { + running = false; + break; + } + } + } - -// image_destruct(image); - free(image->dist_buffer); - free(image); + image_destruct(image); return 0; }