From 312c7c4be721a530501dc14615880d4c1581f020 Mon Sep 17 00:00:00 2001 From: Johan Manuel Date: Wed, 25 Nov 2020 15:03:17 +0100 Subject: [PATCH] Paint: save functionality The pathname is fixed right now, this GUI isn't there yet to show a modal with a textfield. Better rename the saved file if you intend to save several in one session. Oh wait, you can't rename a file yet! Relatedly, fixes a memory leak when writing to a file on an ext2 file system. I found it because at first I wrote the saved image pixel by pixel... Which took several seconds, and made the kernel abort due to being out of memory. --- kernel/src/misc/ext2.c | 1 + modules/Makefile | 4 +- modules/src/background.c | 6 +-- modules/src/paint.c | 107 ++++++++++++++++++++++++++++++--------- snow/include/snow.h | 3 +- snow/src/graphics.c | 12 ++++- ui/src/titlebar.c | 2 +- 7 files changed, 102 insertions(+), 33 deletions(-) diff --git a/kernel/src/misc/ext2.c b/kernel/src/misc/ext2.c index b03af5e..277a95e 100644 --- a/kernel/src/misc/ext2.c +++ b/kernel/src/misc/ext2.c @@ -290,6 +290,7 @@ uint32_t ext2_append(ext2_fs_t* fs, uint32_t inode, uint8_t* data, uint32_t size in->size_lower += size; update_inode(fs, inode, in); kfree(tmp); + kfree(in); return size; } diff --git a/modules/Makefile b/modules/Makefile index 53a49e9..fb82e84 100644 --- a/modules/Makefile +++ b/modules/Makefile @@ -19,15 +19,13 @@ install-headers: clean: $(info [modules] cleaning up) @rm -f */*.o - @find . -executable -type f -delete -# TODO: every module depends on every other, a change in one rebuilds all of them $(MODS): $(OBJS) $(LIB_DEPS) src/start.o - $(info [modules] $(notdir $@)) @mkdir -p $(TARGETROOT) @$(LD) src/start.o src/$(@F).o -o $@ $(LDFLAGS) $(LIBS) %.o: %.c + $(info [modules] $(notdir $(basename $@))) @$(CC) -c $< -o $@ $(CFLAGS) %.o: %.S diff --git a/modules/src/background.c b/modules/src/background.c index 9318411..9655062 100644 --- a/modules/src/background.c +++ b/modules/src/background.c @@ -17,7 +17,7 @@ int main() { uint32_t n_pixels = win->fb.width*win->fb.height; uint8_t* bg = malloc(n_pixels * 3); fread(bg, 3, n_pixels, wp); - snow_draw_rgb(win->fb, bg, 0, 0, win->fb.width, win->fb.height, 0x000000); + snow_draw_rgb(win->fb, bg, 0, 0, win->fb.width, win->fb.height); free(bg); fclose(wp); } else { @@ -25,7 +25,7 @@ int main() { } snow_draw_rect(win->fb, 0, 0, win->fb.width, 22, 0x303030); - snow_draw_string(win->fb, "Snowflake OS 0.5", 3, 3, 0x00FFFFFF); + snow_draw_string(win->fb, "Snowflake OS 0.6", 3, 3, 0x00FFFFFF); snow_render_window(win); while (true) { @@ -53,7 +53,7 @@ int main() { }; snow_draw_rect(win->fb, 0, 0, win->fb.width, 22, 0x303030); - snow_draw_string(win->fb, "Snowflake OS 0.5", 3, 3, 0x00FFFFFF); + snow_draw_string(win->fb, "Snowflake OS 0.6", 3, 3, 0x00FFFFFF); snow_draw_string(win->fb, time_text, x, y, 0xFFFFFF); snow_render_window_partial(win, redraw); diff --git a/modules/src/paint.c b/modules/src/paint.c index a10e630..5e9711a 100644 --- a/modules/src/paint.c +++ b/modules/src/paint.c @@ -6,7 +6,11 @@ #include // callbacks -void on_clear_clicked(button_t* button); +void on_clear_clicked(); +void on_save_clicked(); + +bool load(const char* path, uint32_t w, uint32_t h); +void save(); const uint32_t width = 550; const uint32_t height = 320; @@ -36,24 +40,29 @@ int main(int argc, char* argv[]) { ui_app_t paint = ui_app_new(win, fd ? icon : NULL); vbox_t* vbox = vbox_new(); - ui_set_root(paint, (widget_t*) vbox); + ui_set_root(paint, W(vbox)); hbox_t* menu = hbox_new(); menu->widget.flags &= ~UI_EXPAND_VERTICAL; menu->widget.bounds.h = 20; - vbox_add(vbox, (widget_t*) menu); + vbox_add(vbox, W(menu)); canvas = canvas_new(); - vbox_add(vbox, (widget_t*) canvas); + canvas->color = colors[0]; + vbox_add(vbox, W(canvas)); for (uint32_t i = 0; i < sizeof(colors)/sizeof(colors[0]); i++) { color_button_t* cbutton = color_button_new(colors[i], &canvas->color); - hbox_add(menu, (widget_t*) cbutton); + hbox_add(menu, W(cbutton)); } button_t* button = button_new("Clear"); button->on_click = on_clear_clicked; - hbox_add(menu, (widget_t*) button); + hbox_add(menu, W(button)); + + button_t* save_button = button_new("Save"); + save_button->on_click = on_save_clicked; + hbox_add(menu, W(save_button)); ui_draw(paint); snow_render_window(win); @@ -64,22 +73,10 @@ int main(int argc, char* argv[]) { uint32_t w = strtol(argv[2], NULL, 10); uint32_t h = strtol(argv[3], NULL, 10); - FILE* fd = fopen(fname, "r"); - - if (fd) { - uint8_t* buf = malloc(w*h*3); - - if ((uint32_t) fread(buf, 3, w*h, fd) < w*h) { - printf("paint: file smaller than expected\n"); - } else { - rect_t r = ui_get_absolute_bounds(W(canvas)); - snow_draw_rgb(win->fb, buf, r.x, r.y, w, h, 0); - } - - free(buf); - fclose(fd); + if (load(argv[1], w, h)) { + printf("paint: opened '%s'\n", fname); } else { - printf("paint: failed to open '%s'\n", fname); + printf("paint: failed to load '%s'\n", fname); } } @@ -105,8 +102,70 @@ int main(int argc, char* argv[]) { return 0; } -void on_clear_clicked(button_t* button) { - (void) button; - +void on_clear_clicked() { canvas->needs_clearing = true; +} + +void on_save_clicked() { + save(NULL); +} + +bool load(const char* path, uint32_t w, uint32_t h) { + FILE* fd = fopen(path, "r"); + + if (!fd) { + return false; + } + + uint8_t* buf = malloc(w*h*3); + + if ((uint32_t) fread(buf, 3, w*h, fd) < w*h) { + free(buf); + return false; + } else { + rect_t r = ui_get_absolute_bounds(W(canvas)); + snow_draw_rgb(win->fb, buf, r.x, r.y, w, h); + } + + free(buf); + fclose(fd); + + return true; +} + +void save() { + char fname[256] = "drawing_"; + rect_t r = ui_get_absolute_bounds(W(canvas)); + + itoa(r.w, fname+strlen(fname), 10); + strcat(fname, "x"); + itoa(r.h, fname+strlen(fname), 10); + strcat(fname, ".rgb"); + + printf("paint: saving to '%s'.\n", fname); + + FILE* fd = fopen(fname, "w"); + + if (!fd) { + printf("paint: failed to open output file '%s'\n", fname); + return; + } + + uint8_t* buf = malloc(r.w*r.h*3); + + /* Convert from RGBA to RGB */ + for (int y = r.y; y < r.y+r.h; y++) { + for (int x = r.x; x < r.x+r.w; x++) { + uint32_t px = ((uint32_t*) win->fb.address)[y*win->fb.pitch/4 + x]; + uint8_t* pixbuf = (uint8_t*) &px; + uint32_t idx = (y - r.y)*3*r.w + (x - r.x)*3; + buf[idx] = pixbuf[2]; + buf[idx + 1] = pixbuf[1]; + buf[idx + 2] = pixbuf[0]; + } + } + + fwrite(buf, 3, r.w*r.h, fd); + free(buf); + fclose(fd); } \ No newline at end of file diff --git a/snow/include/snow.h b/snow/include/snow.h index 5ff0c9e..277eca6 100644 --- a/snow/include/snow.h +++ b/snow/include/snow.h @@ -36,7 +36,8 @@ void snow_draw_line(fb_t fb, int x0, int y0, int x1, int y1, uint32_t col); void snow_draw_border(fb_t fb, int x, int y, int w, int h, uint32_t col); void snow_draw_character(fb_t fb, char c, int x, int y, uint32_t col); void snow_draw_string(fb_t fb, char* str, int x, int y, uint32_t col); -void snow_draw_rgb(fb_t fb, uint8_t* rgb, int x, int y, int w, int h, uint32_t mask); +void snow_draw_rgb(fb_t fb, uint8_t* rgb, int x, int y, int w, int h); +void snow_draw_rgb_masked(fb_t fb, uint8_t* rgb, int x, int y, int w, int h, uint32_t mask); // GUI functions window_t* snow_open_window(char* title, int width, int height, uint32_t flags); diff --git a/snow/src/graphics.c b/snow/src/graphics.c index e4826fb..53358cc 100644 --- a/snow/src/graphics.c +++ b/snow/src/graphics.c @@ -170,7 +170,17 @@ void snow_draw_string(fb_t fb, char* str, int x, int y, uint32_t col) { } } -void snow_draw_rgb(fb_t fb, uint8_t* rgb, int x, int y, int w, int h, uint32_t mask) { +void snow_draw_rgb(fb_t fb, uint8_t* rgb, int x, int y, int w, int h) { + for (int i = 0, c = 0; i < h; i++) { + for (int j = 0; j < w; j++, c += 3) { + uint32_t col = rgb[c] << 16 | rgb[c+1] << 8 | rgb[c+2]; + + snow_draw_pixel(fb, x + j, y + i, col); + } + } +} + +void snow_draw_rgb_masked(fb_t fb, uint8_t* rgb, int x, int y, int w, int h, uint32_t mask) { for (int i = 0, c = 0; i < h; i++) { for (int j = 0; j < w; j++, c += 3) { uint32_t col = rgb[c] << 16 | rgb[c+1] << 8 | rgb[c+2]; diff --git a/ui/src/titlebar.c b/ui/src/titlebar.c index 322f1cd..d2b6c3d 100644 --- a/ui/src/titlebar.c +++ b/ui/src/titlebar.c @@ -10,7 +10,7 @@ void titlebar_on_draw(titlebar_t* tb, fb_t fb) { snow_draw_rect(fb, r.x, r.y, r.w, r.h, 0x303030); if (tb->icon) { - snow_draw_rgb(fb, tb->icon, r.x+2, r.y+2, 16, 16, 0xFFFFFF); + snow_draw_rgb_masked(fb, tb->icon, r.x+2, r.y+2, 16, 16, 0xFFFFFF); snow_draw_string(fb, tb->title, r.x+16+5, r.y+3, 0xFFFFFF); } else { snow_draw_string(fb, tb->title, r.x+3, r.y+3, 0xFFFFFF);