From 9e660e594f6d3a43ea1427fb872801a2fcedad93 Mon Sep 17 00:00:00 2001 From: Krow Savcik Date: Tue, 26 Sep 2023 22:32:35 +0300 Subject: initial commit --- .gitignore | 4 + Makefile | 19 ++ assets/default.gpl | 68 ++++++ assets/small.cdr | Bin 0 -> 16392 bytes assets/small.png | Bin 0 -> 614 bytes assets/ui.png | Bin 0 -> 1214 bytes config.mk | 6 + conv | 1 + other/format_v1 | 3 + src/action.h | 22 ++ src/canvas.c | 534 +++++++++++++++++++++++++++++++++++++++++ src/canvas.h | 40 ++++ src/cdraw.c | 194 +++++++++++++++ src/cdraw.h | 35 +++ src/config.h | 34 +++ src/debug.c | 86 +++++++ src/debug.h | 14 ++ src/palette.c | 113 +++++++++ src/palette.h | 15 ++ src/tools.c | 39 +++ src/tools.h | 16 ++ src/types.h | 11 + src/ui.c | 692 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/ui.h | 36 +++ src/user.c | 238 ++++++++++++++++++ 25 files changed, 2220 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 assets/default.gpl create mode 100644 assets/small.cdr create mode 100644 assets/small.png create mode 100644 assets/ui.png create mode 100644 config.mk create mode 100755 conv create mode 100644 other/format_v1 create mode 100644 src/action.h create mode 100644 src/canvas.c create mode 100644 src/canvas.h create mode 100644 src/cdraw.c create mode 100644 src/cdraw.h create mode 100644 src/config.h create mode 100644 src/debug.c create mode 100644 src/debug.h create mode 100644 src/palette.c create mode 100644 src/palette.h create mode 100644 src/tools.c create mode 100644 src/tools.h create mode 100644 src/types.h create mode 100644 src/ui.c create mode 100644 src/ui.h create mode 100644 src/user.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b69f4c3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +test +todo +tags +*.o diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..2efa90a --- /dev/null +++ b/Makefile @@ -0,0 +1,19 @@ +include config.mk + +OBJ = ui.o canvas.o user.o palette.o tools.o debug.o + +all: build run + +build: ${OBJ} src/cdraw.c + mkdir build + ${CC} ${CFLAGS} ${LIBS} ${OBJ} src/cdraw.c -o build/cdraw + +run: + ./build/cdraw + +%.o: src/%.c + ${CC} ${CFLAGS} $< -c + +clean: + rm *.o + rm build/cdraw diff --git a/assets/default.gpl b/assets/default.gpl new file mode 100644 index 0000000..469f24c --- /dev/null +++ b/assets/default.gpl @@ -0,0 +1,68 @@ +GIMP Palette +#Palette Name: Endesga 64 +#Description: Honed over years of palette creation, refined for materialistic pixelart and design. High contrast, high saturation, shaped around painting the organic and structured life of the heptaverse. +#Colors: 64 +255 0 64 ff0040 +19 19 19 131313 +27 27 27 1b1b1b +39 39 39 272727 +61 61 61 3d3d3d +93 93 93 5d5d5d +133 133 133 858585 +180 180 180 b4b4b4 +255 255 255 ffffff +199 207 221 c7cfdd +146 161 185 92a1b9 +101 115 146 657392 +66 76 110 424c6e +42 47 78 2a2f4e +26 25 50 1a1932 +14 7 27 0e071b +28 18 28 1c121c +57 31 33 391f21 +93 44 40 5d2c28 +138 72 54 8a4836 +191 111 74 bf6f4a +230 156 105 e69c69 +246 202 159 f6ca9f +249 230 207 f9e6cf +237 171 80 edab50 +224 116 56 e07438 +198 69 36 c64524 +142 37 29 8e251d +255 80 0 ff5000 +237 118 20 ed7614 +255 162 20 ffa214 +255 200 37 ffc825 +255 235 87 ffeb57 +211 252 126 d3fc7e +153 230 95 99e65f +90 197 79 5ac54f +51 152 75 33984b +30 111 80 1e6f50 +19 76 76 134c4c +12 46 68 0c2e44 +0 57 109 00396d +0 105 170 0069aa +0 152 220 0098dc +0 205 249 00cdf9 +12 241 255 0cf1ff +148 253 255 94fdff +253 210 237 fdd2ed +243 137 245 f389f5 +219 63 253 db3ffd +122 9 250 7a09fa +48 3 217 3003d9 +12 2 147 0c0293 +3 25 63 03193f +59 20 67 3b1443 +98 36 97 622461 +147 56 143 93388f +202 82 201 ca52c9 +200 80 134 c85086 +246 129 135 f68187 +245 85 93 f5555d +234 50 60 ea323c +196 36 48 c42430 +137 30 43 891e2b +87 28 39 571c27 diff --git a/assets/small.cdr b/assets/small.cdr new file mode 100644 index 0000000..6f827c9 Binary files /dev/null and b/assets/small.cdr differ diff --git a/assets/small.png b/assets/small.png new file mode 100644 index 0000000..98a200d Binary files /dev/null and b/assets/small.png differ diff --git a/assets/ui.png b/assets/ui.png new file mode 100644 index 0000000..ef0d60e Binary files /dev/null and b/assets/ui.png differ diff --git a/config.mk b/config.mk new file mode 100644 index 0000000..e234f8b --- /dev/null +++ b/config.mk @@ -0,0 +1,6 @@ +# INCS = `sdl2-config --cflags` + +CFLAGS = -std=c89 -DSDL_DISABLE_IMMINTRIN_H -w -I. +LIBS = -lm -lSDL2 -lSDL2_image + +CC = cc diff --git a/conv b/conv new file mode 100755 index 0000000..10d562b --- /dev/null +++ b/conv @@ -0,0 +1 @@ +gm convert assets/small.png -scale 128 assets/ui.png diff --git a/other/format_v1 b/other/format_v1 new file mode 100644 index 0000000..c301790 --- /dev/null +++ b/other/format_v1 @@ -0,0 +1,3 @@ +VERSION NUMBER; +WIDTH;HEIGHT; +CANVAS DATA(4*H*W bytes) diff --git a/src/action.h b/src/action.h new file mode 100644 index 0000000..7405418 --- /dev/null +++ b/src/action.h @@ -0,0 +1,22 @@ +enum Actions_Types { + ACT_PIXELSCOLORS, +} + +struct act_pixcol { + long int p; /* Position */ + unsigned int c; /* Color */ +}; + +struct Action { + int type; + ActUnion act; +}; + +struct ActionPixelsColors { + long int cnt; + struct act_pixcol *pix; +} + +typedef union { + ActionPixelsColors px; +} ActUnion; diff --git a/src/canvas.c b/src/canvas.c new file mode 100644 index 0000000..5e29566 --- /dev/null +++ b/src/canvas.c @@ -0,0 +1,534 @@ +#include +#include +#include +#include + +#include "types.h" +#include "canvas.h" +#include "cdraw.h" +#include "palette.h" +#include "tools.h" +#include "debug.h" + +uint8 is_drawing; + +#define COORD(x,y) ((x) + (y) * c->w) + +static Layer *layer_create(uint, uint); +static void layer_destroy(Layer *); +static uint canvas_fill_bfs(Canvas *, int, int, uint, uint); +static uint canvas_blend_color(uint, uint); +static void canvas_point_redraw(Canvas *, long int, long int); +static void canvas_set_proj_path(Canvas *, const char *); + +static uint8 +canvas_coord_get(Canvas *c, long int tx, long int ty, long int *x, long int *y) +{ + if (tx < c->x || ty < c->y) return 1; + tx = (tx - c->x) / c->zoom; + ty = (ty - c->y) / c->zoom; + if (tx >= c->w || ty >= c->h) return 1; + *x = tx; + *y = ty; + return 0; +} + +Canvas * +canvas_init(uint w, uint h, void *ren) +{ + Canvas *c; + int i, j; + SDL_Rect dest; + + dest.w = dest.h = 16; + is_drawing = 0; + c = (Canvas *)malloc(sizeof(Canvas)); + c->w = w; + c->h = h; + c->cur_col = 0; + c->cur_layer = 0; + c->zoom = 1; + c->proj_path = NULL; + c->layer_arr_cnt = 1; + c->layer_arr_sz = 1; + c->x = c->y = 0; + c->layers = malloc(sizeof(*(c->layers))); + c->temp_pix = malloc(w * h * sizeof(SDL_Point)); + c->pres_pix = malloc(w * h * sizeof(* c->pres_pix)); + + c->layers[0] = layer_create(w, h); + + for (i = 0; i < c->w * c->h; ++i) + c->pres_pix[i] = 0; + + if (c->layers[0] == NULL) { + fprintf(stderr, "Error while creating layer"); + free(c); + return NULL; + } + + c->back = SDL_CreateTexture( + ren, SDL_PIXELFORMAT_RGBA8888, + SDL_TEXTUREACCESS_TARGET, + w, h); + + if (c->back == NULL) { + printf("SDL_CreateTexture failed: %s\n", SDL_GetError()); + free(c); + return NULL; + } + + c->pres = SDL_CreateTexture( + ren, SDL_PIXELFORMAT_RGBA8888, + SDL_TEXTUREACCESS_TARGET, + w, h); + + if (c->pres == NULL) { + printf("SDL_CreateTexture failed: %s\n", SDL_GetError()); + free(c); + return NULL; + } + + SDL_SetRenderTarget(ren, c->back); + for (i = 0; i < w; i += 16) { + dest.x = i; + for (j = 0; j < h; j += 16) { + dest.y = j; + if (((j+i)>>4)&1) + SDL_SetRenderDrawColor(ren, 225, 225, 225, 255); + else + SDL_SetRenderDrawColor(ren, 125, 125, 125, 255); + SDL_RenderFillRect(ren, &dest); + } + } + SDL_SetRenderTarget(ren, NULL); + + return c; +} + +Canvas * +canvas_open(const char *path, void *ren) +{ + int ver, w, h; + Canvas *c; + FILE *fp = fopen(path, "r"); + if (fp == NULL) return NULL; + fscanf(fp, "%d;%d;%d;", &ver, &w, &h); + c = canvas_init(w, h, ren); + fread(c->layers[0]->pix, sizeof *(c->layers[0]->pix), c->w*c->h, fp); + fclose(fp); + + canvas_refresh(c); + canvas_set_proj_path(c, path); + + return c; +} + +Canvas * +canvas_import_png(const char *path, void *ren) +{ +/* TODO: check path and SDL errors */ +/* TODO: import to layer */ + Canvas *c; +/* TODO: solve prob (maybe using STB) */ +/* TODO: really a hack. Rewrite */ + SDL_Texture *tex; + SDL_Surface *sur; + SDL_Rect rect; + rect.x = rect.y = 0; + sur = IMG_Load(path); + if (sur == NULL) return NULL; + tex = SDL_CreateTextureFromSurface(ren, sur); + if (tex == NULL) return NULL; + + c = canvas_init(sur->w, sur->h, ren); + if (c == NULL) return NULL; + + SDL_SetRenderTarget(ren, c->pres); + SDL_RenderCopy(ren, tex, NULL, NULL); + rect.w = sur->w; + rect.h = sur->h; + SDL_RenderReadPixels(ren, &rect, SDL_PIXELFORMAT_RGBA8888, + c->layers[0]->pix, (sizeof *(c->layers[0]->pix)) * c->w); + SDL_SetRenderTarget(ren, NULL); + + SDL_DestroyTexture(tex); + SDL_FreeSurface(sur); + canvas_refresh(c); + + return c; +} + +void +canvas_destroy(Canvas *c) +{ + int i; + if (c == NULL) return; + SDL_DestroyTexture(c->pres); + SDL_DestroyTexture(c->back); + for (i = 0; i < c->layer_arr_cnt; ++i) + layer_destroy(c->layers[i]); + free(c->layers); + free(c->temp_pix); + free(c->pres_pix); + free(c->proj_path); + free(c); + SDL_SetWindowTitle(win, "cdraw"); +} + +void +canvas_redraw(Canvas *c, void *ren, int mx, int my) +{ + long int x, y; + SDL_SetRenderTarget(ren, c->pres); + SDL_RenderCopy(ren,c->back,NULL,NULL); + /* TODO: Draw the pointer between selected layers instead of on top of all layers */ + /* TODO: Draw by copying data, not calling SDL function */ + for (x = 0; x < c->w; ++x) { + for (y = 0; y < c->h; ++y) { + SDL_SetRenderDrawColor(ren, INTTOCOLA(c->pres_pix[COORD(x, y)])); + SDL_RenderDrawPoint(ren, x, y); + } + } + if (mx > 0 && my > 0 && canvas_coord_get(c, mx, my, &x, &y) == 0) { + SDL_SetRenderDrawColor(ren, + ((Palette *)def_palette)->clist[c->cur_col].r, + ((Palette *)def_palette)->clist[c->cur_col].g, + ((Palette *)def_palette)->clist[c->cur_col].b, + 255); + SDL_RenderDrawPoint(ren, x, y); + } + if (is_drawing) { + SDL_SetRenderDrawColor(ren, 30, 30, 30, 255); + SDL_RenderDrawPoint(ren, 0, 0); + } + SDL_SetRenderTarget(ren, NULL); + return; +} + +void +canvas_present(Canvas *c, void *ren) +{ + SDL_Rect dest; + dest.x = c->x; + dest.y = c->y; + dest.h = c->h*c->zoom; + dest.w = c->w*c->zoom; + SDL_RenderCopy(ren,c->pres,NULL,&dest); +} + +void +canvas_zoom_change(Canvas *c, short int v) +{ + if (v < 0) { + if (c->zoom <= (-v)) + c->zoom = (-v)+1; + } + c->zoom += v; + if (c->zoom > maxzoom) + c->zoom = maxzoom; +} + +/* TODO: make static */ +void +canvas_point_draw(Canvas *c, long int x, long int y) +{ + unsigned int oldcol, newcol; + int i; +/* TODO: better */ + oldcol = c->layers[c->cur_layer]->pix[COORD(x,y)]; + newcol = COLTOINTA( + ((Palette *)def_palette)->clist[c->cur_col].r, + ((Palette *)def_palette)->clist[c->cur_col].g, + ((Palette *)def_palette)->clist[c->cur_col].b, + 255); + + if (tool_array[tool_cur] == TOOL_TYPE_ERASER) + newcol = 0; + + if (newcol == oldcol) + return; + + SDL_SetRenderTarget(ren, c->pres); + if (tool_array[tool_cur] == TOOL_TYPE_ERASER) { + c->layers[c->cur_layer]->pix[COORD(x,y)] = 0; + canvas_point_redraw(c, x, y); + } else if (tool_array[tool_cur] == TOOL_TYPE_PENCIL) { + c->layers[c->cur_layer]->pix[COORD(x,y)] = COLTOINTA( + ((Palette *)def_palette)->clist[c->cur_col].r, + ((Palette *)def_palette)->clist[c->cur_col].g, + ((Palette *)def_palette)->clist[c->cur_col].b, + 255); + canvas_point_redraw(c, x, y); + } else if (tool_array[tool_cur] == TOOL_TYPE_FILL) { + oldcol = canvas_fill_bfs(c, x, y, oldcol, newcol); + for (i = 0; i < oldcol; ++i) + canvas_point_redraw(c, ((SDL_Point *)c->temp_pix)[i].x, ((SDL_Point *)c->temp_pix)[i].y); + } + SDL_SetRenderTarget(ren, NULL); +} + +/* Mouse input */ +void +canvas_mousel_down(Canvas *c, long int x, long int y) +{ + int i; + if (canvas_coord_get(c, x, y, &x, &y)) + return; + switch (tool_array[tool_cur]) { + case TOOL_TYPE_CPICKER: + for (i = 0; i < ((Palette *)def_palette)->num; ++i) { + if (c->layers[c->cur_layer]->pix[COORD(x,y)] == COLTOINTA( + ((Palette *)def_palette)->clist[i].r, + ((Palette *)def_palette)->clist[i].g, + ((Palette *)def_palette)->clist[i].b, + 255)) { + c->cur_col = i; + /* ui_redraw_panel(UI_PANELTYPE_PALETTE); */ + break; + } + } + break; + case TOOL_TYPE_PENCIL: + case TOOL_TYPE_ERASER: + case TOOL_TYPE_FILL: + is_drawing = 1; + canvas_point_draw(c, x, y); + break; + } +} + +void +canvas_mouse_move(Canvas *c, long int x, long int y) +{ + if (is_drawing == 0) + return; + if (canvas_coord_get(c, x, y, &x, &y)) + return; + switch (tool_array[tool_cur]) { + case TOOL_TYPE_PENCIL: + case TOOL_TYPE_ERASER: + case TOOL_TYPE_FILL: + canvas_point_draw(c, x, y); + break; + } +} + +void +canvas_mousel_up(Canvas *c) +{ +/* if (is_drawing && (tool_array[tool_cur] == TOOL_TYPE_PENCIL || tool_array[tool_cur] = TOOL_TYPE_ERASER)) {*/ +/*TODO: save action*/ +/* }*/ + is_drawing = 0; +} + +void +canvas_move_x(Canvas *c, long int delta) +{ + c->x += delta; +} + +void +canvas_move_y(Canvas *c, long int delta) +{ + c->y += delta; +} + + +uint8 +canvas_save(Canvas *c, const char *path, short int s) +{ + FILE *fp = fopen(path, "w"); + if (fp == NULL) { + /* TODO: Error handling */ + return 1; + } + + fprintf(fp, "1;%d;%d;", c->w, c->h); + int u = fwrite(c->layers[0]->pix, sizeof *(c->layers[0]->pix), c->w*c->h, fp); + fclose(fp); + if (s) + canvas_set_proj_path(c, path); +} + +uint8 +canvas_export_png(Canvas *c, const char *path, void *ren) +{ +/* TODO: check path */ + SDL_Surface *surf; + /* SDL_Texture *tex; */ + SDL_Rect dest; + int i, j, k, ret = 0; + dest.x = dest.y = 0; + dest.w = c->w; + dest.h = c->h; + + + surf = SDL_CreateRGBSurfaceWithFormat( 0, c->w, c->h, 32, SDL_PIXELFORMAT_RGBA8888); + ret = ret || (surf == NULL); + + SDL_LockSurface(surf); + +/* TODO: Assuming bad stuff (pixel size) */ + for (j = 0; j < c->h; j++) { + for (k = 0; k < c->w; k++) { + canvas_point_redraw(c, k, j); + ((unsigned int *)surf->pixels)[COORD(k,j)] = c->pres_pix[COORD(k, j)]; + } + } + + SDL_UnlockSurface(surf); + ret = ret || (IMG_SavePNG(surf, path) == -1); + SDL_FreeSurface(surf); + + if (ret) + fprintf(stderr, "%s:%d:canvas_export_png: Error while exporting\n", __FILE__, __LINE__); + return ret; +} + +void +canvas_add_layer(Canvas *c, unsigned char pos) +{ + int i; + if (c == NULL) + return; + pos = (pos > c->layer_arr_cnt) ? c->layer_arr_cnt : pos; + + if (c->layer_arr_cnt == c->layer_arr_sz) { + c->layer_arr_sz *= 2; + c->layers = realloc(c->layers, c->layer_arr_sz * sizeof(* c->layers)); + } + + for (i = c->layer_arr_cnt; i > pos; i--) { + c->layers[i] = c->layers[i-1]; + } + + c->layer_arr_cnt++; + c->layers[pos] = layer_create(c->w, c->h); +} + +void +canvas_refresh(Canvas *c) +{ + int i, j; + for (i = 0; i < c->w; ++i) + for (j = 0; j < c->h; ++j) + canvas_point_redraw(c, i, j); +} + +static void +canvas_set_proj_path(Canvas *c, const char *path) +{ + char *title; + free(c->proj_path); + c->proj_path = malloc((strlen(path) + 1) * sizeof(char)); + memcpy(c->proj_path, path, (strlen(path) + 1) * sizeof(char)); + + + /* TODO Set title format from config.h */ + title = malloc((strlen(path) + 8) * sizeof(char)); + strcpy(title, "cdraw: "); + strcat(title, path); + + SDL_SetWindowTitle(win, title); + free(title); +} + +static Layer * +layer_create(uint w, uint h) +{ + Layer *res; + int i; + /* TODO: better error handling and maybe allocating together? */ + res = malloc(sizeof *res); + + res->pix = malloc(h * w * sizeof(* res->pix)); + if (res->pix == NULL) { + fprintf(stderr, "Error creating layer\n"); + layer_destroy(res); + return NULL; + } + + for (i = 0; i < w*h; ++i) + res->pix[i] = 0; + + return res; +} + +static void +layer_destroy(Layer *lay) +{ + if (lay->pix != NULL) + free(lay->pix); + free(lay); +} + +static uint +canvas_fill_bfs(Canvas *c, int x, int y, uint oldcol, uint newcol) +{ + int cnt; + SDL_Point *pnt; + + if (c == NULL) + return 0; + + pnt = c->temp_pix; + cnt = 1; + + ((SDL_Point *)c->temp_pix)[0].x = x; + ((SDL_Point *)c->temp_pix)[0].y = y; + c->layers[c->cur_layer]->pix[COORD(x,y)] = newcol; + + for (; pnt != &((SDL_Point *)c->temp_pix)[cnt]; ++pnt) { + x = pnt[0].x; + y = pnt[0].y; + if (x > 0 && c->layers[c->cur_layer]->pix[COORD(x - 1,y)] == oldcol) { + c->layers[c->cur_layer]->pix[COORD(x-1, y)] = newcol; + ((SDL_Point *)c->temp_pix)[cnt].x = x - 1; + ((SDL_Point *)c->temp_pix)[cnt].y = y; + ++cnt; + } + + if (x < c->w - 1 && c->layers[c->cur_layer]->pix[COORD(x+1, y)] == oldcol) { + c->layers[c->cur_layer]->pix[COORD(x+1, y)] = newcol; + ((SDL_Point *)c->temp_pix)[cnt].x = x + 1; + ((SDL_Point *)c->temp_pix)[cnt].y = y; + ++cnt; + } + + if (y > 0 && c->layers[c->cur_layer]->pix[COORD(x, y-1)] == oldcol) { + c->layers[c->cur_layer]->pix[COORD(x, y-1)] = newcol; + ((SDL_Point *)c->temp_pix)[cnt].x = x; + ((SDL_Point *)c->temp_pix)[cnt].y = y - 1; + ++cnt; + } + + if (y < c->h - 1 && c->layers[c->cur_layer]->pix[COORD(x, y+1)] == oldcol) { + c->layers[c->cur_layer]->pix[COORD(x, y+1)] = newcol; + ((SDL_Point *)c->temp_pix)[cnt].x = x; + ((SDL_Point *)c->temp_pix)[cnt].y = y + 1; + ++cnt; + } + } + + return cnt; +} + +static uint +canvas_blend_color(uint a, uint b) +{ + /* TODO: do actual blending */ + if ((b&0xff) == 0) return a; + return b; +} + +static void +canvas_point_redraw(Canvas *c, long int x, long int y) +{ + int i; + c->pres_pix[COORD(x, y)] = 0; + + for (i = 0; i < c->layer_arr_cnt; i++) + c->pres_pix[COORD(x, y)] = canvas_blend_color(c->pres_pix[COORD(x, y)], c->layers[i]->pix[COORD(x, y)]); +} diff --git a/src/canvas.h b/src/canvas.h new file mode 100644 index 0000000..2bdb775 --- /dev/null +++ b/src/canvas.h @@ -0,0 +1,40 @@ +struct Layer { + unsigned int *pix; +}; + +struct Canvas { + unsigned char layer_arr_cnt, layer_arr_sz; + unsigned int cur_col, cur_layer; + unsigned int w, h, zoom; + char *proj_path; + struct Layer **layers; + int x, y; + void *back, *pres; + void *temp_pix; + uint *pres_pix; +}; + +typedef struct Layer Layer; +typedef struct Canvas Canvas; + +extern unsigned char is_drawing; +extern const unsigned int maxzoom; /* move to another tab */ +extern Canvas* cur_canvas; /* current canvas */ + +Canvas *canvas_init(unsigned int, unsigned int, void *); +Canvas *canvas_import_png(const char *, void *); +Canvas *canvas_open(const char *, void *); /* TODO: open function per version */ +void canvas_destroy(Canvas *); +void canvas_redraw(Canvas *, void *, int, int); +void canvas_present(Canvas *, void *); +void canvas_zoom_change(Canvas *, short int); +void canvas_point_draw(Canvas *, long int, long int); +void canvas_mousel_up(Canvas *); +void canvas_mousel_down(Canvas *, long int, long int); +void canvas_mouse_move(Canvas *, long int, long int); +void canvas_add_layer(Canvas *, unsigned char); +void canvas_move_x(Canvas *, long int); +void canvas_move_y(Canvas *, long int); +void canvas_refresh(Canvas *); +unsigned char canvas_save(Canvas *, const char *, short int); +unsigned char canvas_export_png(Canvas *, const char *, void *); diff --git a/src/cdraw.c b/src/cdraw.c new file mode 100644 index 0000000..5f3a1b5 --- /dev/null +++ b/src/cdraw.c @@ -0,0 +1,194 @@ +#include +#include +#include + +#include "cdraw.h" +#include "types.h" +#include "canvas.h" +#include "debug.h" +#include "palette.h" +#include "ui.h" + +/* macros */ +#define LENGTH(X) (sizeof X / sizeof X[0]) + +typedef struct { + SDL_Keymod mod; + SDL_Keycode sym; + void (*func)(const Arg *); + const Arg arg; +} Key; + +#include "config.h" + +SDL_Window *win; +void *ren; +void *def_palette; +struct Canvas* cur_canvas; + +/* TODO: window resize change mouseX and mouse Y */ +int mouseX, mouseY; + +void keypress(SDL_Keysym *); + +void +keypress(SDL_Keysym *k) +{ + static Key *p, *end; + /* printf("Key pressed: %d %d %d\n", k->sym, k->mod, KMOD_SHIFT); */ + for (p = keys, end = &p[LENGTH(keys)]; p != end; ++p) { + if (k->sym == p->sym + && k->mod == p->mod) + p->func(&(p->arg)); + } +} + +uint +main_quit() +{ + /* TODO: checks */ + canvas_destroy(cur_canvas); + ui_destroy(); + SDL_DestroyWindow(win); + IMG_Quit(); + SDL_Quit(); + return 1; +} + +uint +main_window_init(const char *s) +{ + int ss; + if (SDL_Init(SDL_INIT_VIDEO) < 0) { + printf("SDL_Init failed: %s\n", SDL_GetError()); + return 1; + } + + if (IMG_Init(IMG_INIT_PNG) == 0) + return 1; + + win = SDL_CreateWindow("cdraw", + SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, + 512, 256, + SDL_WINDOW_RESIZABLE); + if (win == NULL) { + printf("SDL_CreateWindow failed: %s\n", SDL_GetError()); + return 1; + } + + ren = SDL_CreateRenderer(win, -1, 0); + if (ren == NULL) { + printf("SDL_CreateRenderer failed: %s\n", SDL_GetError()); + return 1; + } + + SDL_SetRenderDrawColor(ren, 18, 18, 18, 255); + SDL_SetRenderDrawBlendMode(ren, SDL_BLENDMODE_BLEND); + SDL_RenderPresent(ren); + + def_palette = palette_open_gpl(def_palette_path); + if (def_palette == NULL) + return 1; + + ui_create("assets/ui.png"); + if (s == NULL) + cur_canvas = canvas_init(64, 64, ren); + else { + ss = strlen(s); + if (ss > 4 && strcmp(&s[ss-4], ".cdr") == 0) + cur_canvas = canvas_open(s, ren); + else + cur_canvas = canvas_import_png(s, ren); + } + + ui_redraw_panel(UI_PANELTYPE_CANVAS); + if (cur_canvas == NULL) + return 1; + + return 0; +} + +uint +main_event_handle() +{ + static uint8 q, r; + static SDL_Event event; + q = r = 0; + while (SDL_PollEvent(&event)) { + switch (event.type) { + case SDL_QUIT: + q = 1; + break; + case SDL_WINDOWEVENT: + switch (event.window.event) { + case SDL_WINDOWEVENT_RESIZED: + r = 1; + /* FALLTROUGH */ + case SDL_WINDOWEVENT_MOVED: + break; + } + break; + case SDL_MOUSEBUTTONDOWN: + switch (event.button.button) { + case SDL_BUTTON_LEFT: + ui_mousel_down(event.button.x, event.button.y); + break; + case SDL_BUTTON_RIGHT: + break; + } + break; + case SDL_MOUSEBUTTONUP: + switch (event.button.button) { + case SDL_BUTTON_LEFT: + ui_mousel_up(event.button.x, event.button.y); + is_drawing = 0; + break; + } + break; + case SDL_MOUSEMOTION: + mouseX = event.motion.x; + mouseY = event.motion.y; + ui_mouse_move(mouseX, mouseY); + break; + case SDL_KEYDOWN: + keypress(&event.key.keysym); + break; + } + } + + if (r) + ui_resize(); + + SDL_SetRenderDrawColor(ren, INTTOCOL(back_c), 255); + SDL_RenderClear(ren); + ui_present(); + SDL_RenderPresent(ren); + + if (q) + return main_quit(); + return 0; +} + +int +main(int argc, char *argv[]) +{ + Uint32 ltick, fdif; + + if (main_window_init(argc > 1 ? argv[1] : NULL)) + return 1; + + fdif = 1000 / fps; + while (1) { + ltick = SDL_GetTicks(); + if (main_event_handle()) + break; + ltick = SDL_GetTicks() - ltick; + + if (ltick < fdif) + SDL_Delay(fdif-ltick); + } + +#ifdef F_MEMORY_DEBUG + f_debug_mem_show(); +#endif +} diff --git a/src/cdraw.h b/src/cdraw.h new file mode 100644 index 0000000..0f57a7f --- /dev/null +++ b/src/cdraw.h @@ -0,0 +1,35 @@ +typedef union { + int i; + unsigned int u; + float f; + const void *v; + const char *s; +} Arg; + +extern SDL_Window *win; +extern void *ren; +extern void *def_palette; +extern const char *def_palette_path; +extern int mouseX, mouseY; +extern const unsigned int back_c; + +unsigned int main_event_handle(); +unsigned int main_window_init(); +unsigned int main_quit(); + +/* Functions for user bind to something else */ +void user_canvas_zoom_change(const Arg *); +void user_canvas_move_x(const Arg *); +void user_canvas_move_y(const Arg *); +void user_canvas_save(const Arg *); +void user_canvas_open(const Arg *); +void user_canvas_export_png(const Arg *); +void user_canvas_import_png(const Arg *); +void user_canvas_refresh(const Arg *); +void user_canvas_create_new(const Arg *); +void user_canvas_pal_col_chng(const Arg *); +void user_tool_change(const Arg *); +void user_layer_chng(const Arg *); +void user_testing_layer_add(const Arg *); +void user_testing_reload_tex(const Arg *); +void user_debug_mem_show(const Arg *); diff --git a/src/config.h b/src/config.h new file mode 100644 index 0000000..e1a954a --- /dev/null +++ b/src/config.h @@ -0,0 +1,34 @@ +#define COLTOINT(R,G,B) (R<<16)+(G<<8)+B + +const unsigned int maxzoom = 20; +const unsigned int fps = 30; +const char *def_palette_path = "assets/default.gpl"; +const char *def_theme_file = "assets/ui.png"; +const unsigned int back_c = COLTOINT(146, 161, 185); + +static Key keys[] = { + /* modifier key function argument */ + {0, SDLK_EQUALS, user_canvas_zoom_change, {.i = 1}}, + {0, SDLK_MINUS, user_canvas_zoom_change, {.i = -1}}, + {0, SDLK_UP, user_canvas_move_y, {.i = 10}}, + {0, SDLK_DOWN, user_canvas_move_y, {.i = -10}}, + {0, SDLK_LEFT, user_canvas_move_x, {.i = 10}}, + {0, SDLK_RIGHT, user_canvas_move_x, {.i = -10}}, + {0, SDLK_s, user_canvas_save, {.v = NULL}}, + {0, SDLK_r, user_canvas_refresh, {.v = NULL}}, + {0, SDLK_o, user_canvas_open, {.s = NULL}}, + {KMOD_LSHIFT, SDLK_e, user_canvas_export_png, {.v = NULL}}, + {KMOD_LSHIFT, SDLK_i, user_canvas_import_png, {.s = NULL}}, + {0, SDLK_b, user_tool_change, {.i = 0}}, + {0, SDLK_e, user_tool_change, {.i = 1}}, + {0, SDLK_f, user_tool_change, {.i = 2}}, + {0, SDLK_i, user_tool_change, {.i = 3}}, + {0, SDLK_n, user_canvas_create_new, {.v = NULL}}, + {0, SDLK_LEFTBRACKET, user_canvas_pal_col_chng, {.i = -1}}, + {0, SDLK_RIGHTBRACKET, user_canvas_pal_col_chng, {.i = 1}}, +/* {0, SDLK_j, user_testing_layer_add, {.i = 1}},*/ + {KMOD_LSHIFT, SDLK_r, user_testing_reload_tex, {.i = 1}}, +/* {KMOD_LSHIFT, SDLK_LEFTBRACKET, user_layer_chng, {.i = -1}},*/ +/* {KMOD_LSHIFT, SDLK_RIGHTBRACKET, user_layer_chng, {.i = 1}},*/ + {0, SDLK_m, user_debug_mem_show, {.v = NULL}}, +}; diff --git a/src/debug.c b/src/debug.c new file mode 100644 index 0000000..3f2b85a --- /dev/null +++ b/src/debug.c @@ -0,0 +1,86 @@ +#include +#include +#include + +struct debug_header { + const char *f; + unsigned int l; + size_t s; + struct debug_header *next, *prev; +}; + +typedef struct debug_header d_header; + +d_header *d_head; + +void * +f_debug_mem_malloc(size_t s, const char *f, unsigned int l) +{ + d_header *m = malloc(s + sizeof(*m)); + /* fprintf(stdout, "%s:%u: malloc(%zi)\n", f, l, s); */ + if (m == NULL) return m; + m->f = f; + m->l = l; + m->s = s; + m->next = d_head; + if (d_head) + m->next->prev = m; + m->prev = NULL; + d_head = m; + return m+1; +} + +void +f_debug_mem_free(void *s, const char *f, unsigned int l) +{ + d_header *m; + if (s != NULL) { + m = (d_header *)s - 1; + m->s = ~m->s; + + if (m->prev == NULL) { + assert(d_head == m); + d_head = m->next; + } else { + m->prev->next = m->next; + } + + if (m->next) m->next->prev = m->prev; + /* fprintf(stdout, "%s:%u: free()\n", f, l); */ + free(m); + } +} + +void * +f_debug_mem_realloc(void *x, size_t s, const char *f, unsigned int l) +{ + d_header *m; + void *q; + if (x == NULL) { + return f_debug_mem_malloc(s, f, l); + } else if (s == 0) { + f_debug_mem_free(x, f, l); + return NULL; + } else { + m = (d_header *)x - 1; + if (s <= m->s) return x; + q = f_debug_mem_malloc(s, f, l); + if (q) { + memcpy(q, x, m->s); + f_debug_mem_free(x, f, l); + } + return q; + } +} + +void +f_debug_mem_show() +{ + d_header *m = d_head; + while (m) { + fprintf(stdout, "%s:%u: %zd bytes at %p\n", m->f, m->l, m->s, (void*)(m+1)); + m = m->next; + } +} + +#include "debug.h" diff --git a/src/debug.h b/src/debug.h new file mode 100644 index 0000000..20285c9 --- /dev/null +++ b/src/debug.h @@ -0,0 +1,14 @@ +void *f_debug_mem_malloc(size_t, const char *, unsigned int); +void *f_debug_mem_realloc(void *, size_t, const char *, unsigned int); +void f_debug_mem_free(void *, const char *, unsigned int); +void f_debug_mem_show(); + +#define F_MEMORY_DEBUG + +#ifdef F_MEMORY_DEBUG + +#define malloc(n) f_debug_mem_malloc(n, __FILE__, __LINE__) +#define realloc(n,s) f_debug_mem_realloc(n, s, __FILE__, __LINE__) +#define free(n) f_debug_mem_free(n, __FILE__, __LINE__) + +#endif diff --git a/src/palette.c b/src/palette.c new file mode 100644 index 0000000..3584e55 --- /dev/null +++ b/src/palette.c @@ -0,0 +1,113 @@ +#include +#include +#include + +#include "palette.h" +#include "debug.h" +#include "types.h" + +Palette * +palette_clone(const Palette *p) +{ + Palette *res; + + /* TODO: better malloc (place list and num together) */ + res = malloc(sizeof(Palette)); + res->clist = malloc(p->num*sizeof(*p->clist)); + + res->num = p->num; + memcpy(res->clist, p->clist, p->num * sizeof(*p->clist)); + + return res; +} + +static char * +palette_intern_get_num(char *s, int *a) +{ + while (*s == ' ' || *s == '\t') + ++s; + if (*s == '\n') + *a = -1; + else { + *a = 0; + while (*s >= '0' && *s <= '9') { + *a = (*a) * 10; + *a += (int) (*s-'0'); + ++s; + } + } + + return s; +} + +static uint8 +palette_intern_get_color(char *s, Color *c) +{ + int a; + char *st = s; + + st = palette_intern_get_num(st, &a); + if (a < 0 || a > 255) return 1; + c->r = a; + + st = palette_intern_get_num(st, &a); + if (a < 0 || a > 255) return 1; + c->g = a; + + st = palette_intern_get_num(st, &a); + if (a < 0 || a > 255) return 1; + c->b = a; + + return 0; +} + +static uint +palette_intern_get_color_number(const char *s) +{ + FILE *fp; + char line[1024]; + size_t len = 1024; + uint ans = 0; + + fp = fopen(s, "r"); + while (fgets(line, len, fp)) { + if (line[0] >= '0' && line[0] <= '9') + ++ans; + } + fclose(fp); + + return ans; +} + +Palette * +palette_open_gpl(const char *p) +{ + /* TODO: check if file exists */ + /* TODO: better parsing */ + /* TODO: better malloc (place list and num together) */ + Palette *res; + FILE *fp; + Color *c; + char line[1024]; + size_t len = 1024; + + res = malloc(sizeof(Palette)); + res->num = palette_intern_get_color_number(p); + res->clist = malloc(res->num*sizeof(*res->clist)); + c = res->clist; + + fp = fopen(p, "r"); + while (fgets(line, len, fp)) { + if (line[0] >= '0' && line[0] <= '9') { + if (palette_intern_get_color(line, c)) { + free(res->clist); + free(res); + return NULL; + } + c++; + } + } + fclose(fp); + + return res; +} diff --git a/src/palette.h b/src/palette.h new file mode 100644 index 0000000..3ac5c93 --- /dev/null +++ b/src/palette.h @@ -0,0 +1,15 @@ +struct Color { + unsigned char r, g, b; +}; + +struct Palette { + unsigned int num; + struct Color *clist; +}; + +typedef struct Color Color; +typedef struct Palette Palette; + +Palette *palette_open_gpl(const char *); +Palette *palette_clone(const Palette *); +Palette *palette_open_gpl(const char *); diff --git a/src/tools.c b/src/tools.c new file mode 100644 index 0000000..1ff979a --- /dev/null +++ b/src/tools.c @@ -0,0 +1,39 @@ +#include + +#include "tools.h" +#include "types.h" +#include "canvas.h" +#include "ui.h" + +uint8 tool_cur; +static uint8 prev_tool; + +/* TODO: Move to config.h */ +uint8 tool_array[] = { + TOOL_TYPE_PENCIL, + TOOL_TYPE_ERASER, + TOOL_TYPE_FILL, + TOOL_TYPE_CPICKER, + TOOL_TYPE_ZOOMOUT, + TOOL_TYPE_ZOOMIN, + TOOL_TYPE_EMPTY, + TOOL_TYPE_CLOWN, +}; + +void +tool_change(uint8 t) +{ + if (t != tool_cur) { + canvas_mousel_up(cur_canvas); + assert(t < tool_array_size()); + t = (t < tool_array_size()) ? t : tool_array_size() - 1; + tool_cur = t; + ui_redraw_panel(UI_PANELTYPE_BUTTONS); + } +} + +uint8 +tool_array_size() +{ + return (sizeof tool_array / sizeof *tool_array); +} diff --git a/src/tools.h b/src/tools.h new file mode 100644 index 0000000..c18afe3 --- /dev/null +++ b/src/tools.h @@ -0,0 +1,16 @@ +enum Tool_Types { + TOOL_TYPE_PENCIL, + TOOL_TYPE_ERASER, + TOOL_TYPE_FILL, + TOOL_TYPE_ZOOMIN, + TOOL_TYPE_ZOOMOUT, + TOOL_TYPE_CLOWN, + TOOL_TYPE_CPICKER, + TOOL_TYPE_EMPTY, +}; + +extern unsigned char tool_array[]; +extern unsigned char tool_cur; + +void tool_change(unsigned char); +unsigned char tool_array_size(); diff --git a/src/types.h b/src/types.h new file mode 100644 index 0000000..dc6789e --- /dev/null +++ b/src/types.h @@ -0,0 +1,11 @@ +#define uint unsigned int +#define uint8 unsigned char +#define uint16 uint16_t +#define uint32 unsigned int + +#define COLTOINT(R,G,B) (((unsigned int)R)<<16)|(((unsigned int)G)<<8)|(unsigned int)B +#define INTTOCOL(C) (C>>16), ((C>>8)%256), (C%256) +#define COLTOINTA(R,G,B,A) (((COLTOINT(R,G,B)) << 8)|(unsigned int)A) +#define INTTOCOLA(C) (C>>24), ((C>>16)&0xFF), ((C>>8)&0xFF), (C&0xFF) + +#define LENGTH(S) ((sizeof S)/(sizeof S[0])) diff --git a/src/ui.c b/src/ui.c new file mode 100644 index 0000000..ccc2de9 --- /dev/null +++ b/src/ui.c @@ -0,0 +1,692 @@ +#include +#include +#include +#include + +#include "ui.h" +#include "cdraw.h" +#include "types.h" +#include "canvas.h" +#include "tools.h" +#include "debug.h" +#include "palette.h" + +#define fpan main_ui->focus_panel +#define LENGTH(X) (sizeof X / sizeof *X) +#define MAX(a, b) (((a) > (b)) ? (a) : (b)); + +static const SDL_Rect ui_frame_rect[] = { + {0, 0, 8, 8}, + {16, 0, 8, 8}, + {0, 16, 8, 8}, + {16, 16, 8, 8}, + {8, 0, 8, 8}, + {8, 16, 8, 8}, + {0, 8, 8, 8}, + {16, 8, 8, 8}, +}; +static const SDL_Rect ui_buttons_rect[] = { + {0, 24, 28, 28}, + {0, 52, 28, 28}, + {0, 80, 32, 32}, + /* TODO: y -= 2 all the rects below */ + {28, 26, 28, 28}, /* Pencil */ + {28, 54, 28, 28}, /* Eraser */ + {84, 26, 28, 28}, /* Fill Bucket */ + {56, 26, 28, 28}, /* Zoom in */ + {56, 54, 28, 28}, /* Zoom out */ + {84, 54, 28, 28}, /* Clown */ + {56, 82, 28, 28}, /* Color Picker */ +}; + +struct UIPanel { + uint8 type, redraw; + SDL_Rect geom; + SDL_Texture *pres; +}; + +struct UIPanelCanvas { + UIPanel head; + /* Canvas *canv; */ +}; + +struct UIPanelPalette { + UIPanel head; +}; + +struct UIPanelButtons { + UIPanel head; +}; + +struct UIPanelDivider { + UIPanel head; + UIPanel *x, *y; + uint8 st; /* states: (0 bit - hor/vert, 1 bit - ra/lb) */ + int fw, lw, of; +}; + +struct UIPanelTimeline { + UIPanel head; +}; + +struct UI { + SDL_Texture *theme; + int w, h; + UIPanel *focus_panel; + UIPanelDivider p_divs[3]; + UIPanelCanvas p_canvas; + UIPanelPalette p_palette; + UIPanelButtons p_buttons; + UIPanelTimeline p_timeline; +}; + +UI *main_ui; + +static UIPanel *ui_focus_panel(int, int); +static void ui_focus_panel_change(int, int); + +/* Panel functions */ +static uint8 ui_panel_init(UIPanel *, uint8); +static uint8 ui_panel_resize(UIPanel *, int, int, uint, uint); +static uint8 ui_panel_draw_frame(UIPanel *); +static uint8 ui_panel_redraw(UIPanel *); +static uint8 ui_panel_present(UIPanel *); + +/* Panel canvas functions */ +static uint8 ui_panel_canvas_init(UIPanelCanvas *); +static uint8 ui_panel_canvas_redraw(UIPanelCanvas *); + +/* Panel palette functions */ +static uint8 ui_panel_palette_init(UIPanelPalette *); +static uint8 ui_panel_palette_redraw(UIPanelPalette *); + +/* Panel buttons functions */ +static uint8 ui_panel_buttons_init(UIPanelButtons *); +static uint8 ui_panel_buttons_redraw(UIPanelButtons *); + +/* Panel timeline functions */ +static uint8 ui_panel_timeline_init(UIPanelTimeline *); +static uint8 ui_panel_timeline_redraw(UIPanelTimeline *); + +/* Panel divider functions */ +static uint8 ui_panel_divider_init(UIPanelDivider *, int, int, int, uint8, UIPanel *, UIPanel *); +static uint8 ui_panel_divider_resize(UIPanelDivider *, int, int, int, int); + +void +ui_create(const char *path) +{ +/* TODO: error checking */ + int i; + main_ui = malloc(sizeof *main_ui); + main_ui->theme = NULL; + main_ui->focus_panel = NULL; + main_ui->w = main_ui->h = -1; + if (ui_theme_load(path)) + exit(1); + + SDL_SetTextureBlendMode(main_ui->theme, SDL_BLENDMODE_BLEND); + + ui_panel_canvas_init(&main_ui->p_canvas); + ui_panel_palette_init(&main_ui->p_palette); + ui_panel_buttons_init(&main_ui->p_buttons); + ui_panel_timeline_init(&main_ui->p_timeline); + + /* ui_panel_divider_init(&main_ui->p_divs[0], 5, 10, 100, 1, &main_ui->p_canvas, &main_ui->p_timeline); */ + ui_panel_divider_init(&main_ui->p_divs[0], 5, 5, 120, 0, &main_ui->p_divs[1], &main_ui->p_divs[2]); + ui_panel_divider_init(&main_ui->p_divs[1], 0, 8, 22+31*((tool_array_size()+2)/3), 3, &main_ui->p_palette, &main_ui->p_buttons); + ui_panel_divider_init(&main_ui->p_divs[2], 0, 5, 70, 3, &main_ui->p_canvas, &main_ui->p_timeline); + + ui_resize(); +} + +void +ui_destroy() +{ + if (main_ui == NULL) return; + if (main_ui->theme) SDL_DestroyTexture(main_ui->theme); + free(main_ui); +} + +uint8 +ui_resize() +{ + static int w, h; + if (main_ui == NULL) + return 1; + + SDL_GetRendererOutputSize(ren, &w, &h); + if (w != main_ui->w || h != main_ui->h) { + main_ui->w = w; + main_ui->h = h; + ui_panel_resize(&main_ui->p_divs[0], 0, 0, w, h); + /* ui_panel_resize(&main_ui->p_palette.head, 5, 5, 120, main_ui->h-42-31*((tool_array_size()+2)/3)); */ + /* ui_panel_resize(&main_ui->p_buttons.head, 5, main_ui->h-27-31*((tool_array_size()+2)/3), 120, 22+31*((tool_array_size()+2)/3)); */ + /* ui_panel_resize(&main_ui->p_canvas.head, 130, 5, main_ui->w - 135, main_ui->h-85); */ + /* ui_panel_resize(&main_ui->p_timeline.head, 130, main_ui->h-75, main_ui->w - 135,70); */ + ui_redraw(); + } + return 0; +} + +void +ui_redraw() +{ + main_ui->p_canvas.head.redraw = 1; + main_ui->p_palette.head.redraw = 1; + main_ui->p_buttons.head.redraw = 1; + main_ui->p_timeline.head.redraw = 1; +} + +void +ui_redraw_panel(uint8 type) +{ + switch (type) { + case UI_PANELTYPE_PALETTE: + main_ui->p_palette.head.redraw = 1; + break; + case UI_PANELTYPE_CANVAS: + main_ui->p_canvas.head.redraw = 1; + break; + case UI_PANELTYPE_BUTTONS: + main_ui->p_buttons.head.redraw = 1; + break; + case UI_PANELTYPE_TIMELINE: + main_ui->p_timeline.head.redraw = 1; + break; + default: + fprintf(stderr, "%s:%d:ui_redraw_panel: No implementation for %u\n", __FILE__, __LINE__, type); + break; + } +} + +void +ui_present() +{ + if (main_ui->p_palette.head.redraw) + ui_panel_redraw(&main_ui->p_palette.head); + if (main_ui->p_canvas.head.redraw) + ui_panel_redraw(&main_ui->p_canvas.head); + if (main_ui->p_buttons.head.redraw) + ui_panel_redraw(&main_ui->p_buttons.head); + if (main_ui->p_timeline.head.redraw) + ui_panel_redraw(&main_ui->p_timeline.head); + ui_panel_present(&main_ui->p_palette.head); + ui_panel_present(&main_ui->p_canvas.head); + ui_panel_present(&main_ui->p_buttons.head); + ui_panel_present(&main_ui->p_timeline.head); +} + +void +ui_mousel_down(int x, int y) +{ + int col; + Arg arg; + ui_focus_panel_change(x, y); + + if (main_ui->focus_panel == NULL) + return; + + switch (main_ui->focus_panel->type) { + case UI_PANELTYPE_CANVAS: + if (cur_canvas != NULL) { + canvas_mousel_down(cur_canvas, + x - fpan->geom.x - 8, + y - fpan->geom.y - 8); + fpan->redraw = 1; + } + break; + case UI_PANELTYPE_PALETTE: + if (cur_canvas == NULL) + return; + col = (x - fpan->geom.x - 8) / 26; + if (col > 3) col = 3; + col += (((y - fpan->geom.y - 8) / 26) << 2); + + + if (col < ((Palette *)def_palette)->num && col >= 0) + cur_canvas->cur_col = col; + break; + case UI_PANELTYPE_BUTTONS: + + /* TODO Better */ + col = (x - fpan->geom.x - 15) / 30; + if (col > 2) + break; + + col += 3 * ((y - fpan->geom.y - 2) / 30); + + if (col >= tool_array_size() || col < 0) + break; + switch (tool_array[col]) { + case TOOL_TYPE_ZOOMOUT: + /* TODO: user functions must mostly be wrappers for internal fucntions */ + arg.i = -1; + user_canvas_zoom_change(&arg); + break; + case TOOL_TYPE_ZOOMIN: + arg.i = 1; + user_canvas_zoom_change(&arg); + break; + case TOOL_TYPE_CLOWN: + fprintf(stdout, "You have been clowned 🤡\n"); + break; + case TOOL_TYPE_EMPTY: + break; + default: + tool_change(col); + break; + } + break; + } +} + +void +ui_mousel_up(int x, int y) +{ + ui_focus_panel_change(x, y); + + if (main_ui->focus_panel == NULL) + return; + + switch (main_ui->focus_panel->type) { + case UI_PANELTYPE_CANVAS: + if (cur_canvas != NULL) + canvas_mousel_up(cur_canvas); + fpan->redraw = 1; + break; + } +} + +void +ui_mouse_move(int x, int y) +{ + ui_focus_panel_change(x, y); + + if (main_ui->focus_panel == NULL) + return; + + switch (main_ui->focus_panel->type) { + case UI_PANELTYPE_CANVAS: + if (cur_canvas != NULL) { + canvas_mouse_move(cur_canvas, + x - fpan->geom.x - 8, + y - fpan->geom.y - 8); + fpan->redraw = 1; + } + break; + } +} + +uint8 +ui_theme_load(const char *path) +{ +/* TODO: check path */ + if (main_ui->theme != NULL) + SDL_DestroyTexture(main_ui->theme); + + SDL_Surface *surf = IMG_Load(path); + if (surf == NULL) return 1; + main_ui->theme = SDL_CreateTextureFromSurface(ren, surf); + SDL_FreeSurface(surf); + if (main_ui->theme == NULL) return 1; + + return 0; +} + +static UIPanel * +ui_focus_panel(int x, int y) +{ + SDL_Rect *r; + if (main_ui == NULL) return NULL; + + r = &main_ui->p_palette.head.geom; + if (r->x <= x && r->y <= y + && x - r->x < r->w && y - r->y < r->h) + return &main_ui->p_palette.head; + + r = &main_ui->p_buttons.head.geom; + if (r->x <= x && r->y <= y + && x - r->x < r->w && y - r->y < r->h) + return &main_ui->p_buttons.head; + + r = &main_ui->p_canvas.head.geom; + if (r->x <= x && r->y <= y + && x - r->x < r->w && y - r->y < r->h) + return &main_ui->p_canvas.head; + + return NULL; +} + +static void +ui_focus_panel_change(int x, int y) +{ + UIPanel *u; + u = ui_focus_panel(x, y); + + if (u == main_ui->focus_panel) + return; + + if (main_ui->focus_panel != NULL) { + switch (main_ui->focus_panel->type) { + case UI_PANELTYPE_CANVAS: + is_drawing = 0; + main_ui->focus_panel = u; + ui_redraw_panel(UI_PANELTYPE_CANVAS); + break; + default: + break; + } + } + + main_ui->focus_panel = u; +} + +/* Panel functions */ +static uint8 +ui_panel_init(UIPanel *p, uint8 type) +{ + p->pres = NULL; + p->type = type; + p->redraw = 1; + p->geom.w = p->geom.h = 0; + + return 0; +} + +static uint8 +ui_panel_resize(UIPanel *p, int x, int y, uint w, uint h) +{ + if (p->type == UI_PANELTYPE_DIVIDER) + return ui_panel_divider_resize((UIPanelDivider *)p, x, y, w, h); + p->geom.x = x; + p->geom.y = y; + + /* assert(w > 16 && h > 16); */ + if (w < 16 || h < 16) + return 1; + if (p->geom.w == w && p->geom.h == h) + return 0; + + p->geom.w = w; + p->geom.h = h; + + if (p->pres != NULL) + SDL_DestroyTexture(p->pres); + + p->pres = SDL_CreateTexture( + ren, SDL_PIXELFORMAT_RGBA8888, + SDL_TEXTUREACCESS_TARGET, + w, h); + + SDL_SetTextureBlendMode(p->pres, SDL_BLENDMODE_BLEND); + + return 0; +} + +static uint8 +ui_panel_redraw(UIPanel *p) +{ +/* TODO: something for paralel threads? */ +/* TODO: Correct switch case intendation(not here) */ + if (p->pres == NULL) + return 1; + + p->redraw = 0; + + SDL_SetRenderTarget(ren, p->pres); + SDL_SetRenderDrawColor(ren, 0, 0, 0, 0); + SDL_RenderClear(ren); + switch (p->type) { + case UI_PANELTYPE_CANVAS: + ui_panel_canvas_redraw((UIPanelCanvas *)p); + break; + case UI_PANELTYPE_PALETTE: + ui_panel_palette_redraw((UIPanelPalette *)p); + break; + case UI_PANELTYPE_BUTTONS: + ui_panel_buttons_redraw((UIPanelButtons *)p); + break; + case UI_PANELTYPE_TIMELINE: + ui_panel_draw_frame(p); + break; + default: + fprintf(stderr, "%s:%d:ui_panel_redraw: No redraw fuction for %u\n", __FILE__, __LINE__, p->type); + break; + } + SDL_SetRenderTarget(ren, NULL); + + return 0; +} + +static uint8 +ui_panel_draw_frame(UIPanel *p) +{ + static SDL_Rect dest; + dest.x = 0; + dest.y = 0; + dest.h = 8; + dest.w = p->geom.w; + SDL_RenderCopy(ren, main_ui->theme, &ui_frame_rect[4], &dest); + dest.y = p->geom.h-8; + SDL_RenderCopy(ren, main_ui->theme, &ui_frame_rect[5], &dest); + dest.w = 8; + dest.h = p->geom.h; + dest.y = 0; + SDL_RenderCopy(ren, main_ui->theme, &ui_frame_rect[6], &dest); + dest.x = p->geom.w-8; + SDL_RenderCopy(ren, main_ui->theme, &ui_frame_rect[7], &dest); + + dest.h = 8; + SDL_RenderCopy(ren, main_ui->theme, &ui_frame_rect[1], &dest); + dest.x = 0; + SDL_RenderCopy(ren, main_ui->theme, &ui_frame_rect[0], &dest); + dest.y = p->geom.h-8; + SDL_RenderCopy(ren, main_ui->theme, &ui_frame_rect[2], &dest); + dest.x = p->geom.w-8; + SDL_RenderCopy(ren, main_ui->theme, &ui_frame_rect[3], &dest); + + return 0; +} + +static uint8 +ui_panel_present(UIPanel *p) +{ + return SDL_RenderCopy(ren, p->pres, NULL, &p->geom); +} + +/* Panel canvas functions */ +static uint8 +ui_panel_canvas_init(UIPanelCanvas *p) +{ + return ui_panel_init((UIPanel *)p, UI_PANELTYPE_CANVAS); +} + +static uint8 +ui_panel_canvas_redraw(UIPanelCanvas *p) +{ + static SDL_Rect dest; + dest.x = 8; + dest.y = 8; + dest.w = p->head.geom.w - 16; + dest.h = p->head.geom.h - 16; + SDL_RenderSetViewport(ren, &dest); + SDL_SetRenderDrawColor(ren, 18, 18, 18, 255); + SDL_RenderClear(ren); + if (cur_canvas != NULL) { + SDL_RenderSetViewport(ren, NULL); + canvas_redraw(cur_canvas, ren, + mouseX - p->head.geom.x - 8, + mouseY - p->head.geom.y - 8); + SDL_SetRenderTarget(ren, p->head.pres); + SDL_RenderSetViewport(ren, &dest); + canvas_present(cur_canvas, ren); + } + SDL_RenderSetViewport(ren, NULL); + ui_panel_draw_frame(&p->head); + return 0; +} + +/* Panel palette functions */ +static uint8 +ui_panel_palette_init(UIPanelPalette *p) +{ + return ui_panel_init((UIPanel *)p, UI_PANELTYPE_PALETTE); +} + +static uint8 +ui_panel_palette_redraw(UIPanelPalette *p) +{ + static SDL_Rect dest; + Palette *pp; + int i; + + pp = def_palette; + dest.x = 8; + dest.y = 8; + dest.w = p->head.geom.w - 16; + dest.h = p->head.geom.h - 16; + SDL_RenderSetViewport(ren, &dest); + SDL_SetRenderDrawColor(ren, 18, 18, 18, 255); + SDL_RenderClear(ren); + + SDL_SetRenderDrawColor(ren, 0, 0, 0, 255); + if (pp != NULL) { + dest.x = dest.y = 0; + dest.w = 120; + if (pp->num > 3) { + dest.h = 26 * (pp->num >> 2); + SDL_RenderFillRect(ren, &dest); + } + + if (pp->num % 4) { + dest.w = 26 * (pp->num % 4); + dest.h = 26; + dest.y = 26 * (pp->num >> 2); + SDL_RenderFillRect(ren, &dest); + } + + dest.w = dest.h = 24; + for (i = 0; i < pp->num; ++i) { + dest.x = 1 + (i % 4) * 26; + dest.y = 1 + (i / 4) * 26; + SDL_SetRenderDrawColor(ren, + pp->clist[i].r, pp->clist[i].g, + pp->clist[i].b, 255); + SDL_RenderFillRect(ren, &dest); + } + } + SDL_RenderSetViewport(ren, NULL); + ui_panel_draw_frame(&p->head); + return 0; +} + +/* Panel buttons functions */ +static uint8 +ui_panel_buttons_init(UIPanelButtons *p) +{ + return ui_panel_init((UIPanel *)p, UI_PANELTYPE_BUTTONS); +} + +static uint8 +ui_panel_buttons_redraw(UIPanelButtons *p) +{ + static SDL_Rect dest1; + static SDL_Rect dest2; + int i; + + dest1.x = 14; + dest1.y = 0; + dest1.w = p->head.geom.w - 28; + dest1.h = p->head.geom.h; + SDL_RenderSetViewport(ren, &dest1); + SDL_SetRenderDrawColor(ren, INTTOCOL(back_c), 255); + SDL_RenderClear(ren); + dest1.w = 28; + dest1.h = 28; + dest2.w = 32; + dest2.h = 32; + for (i = 0; i < tool_array_size(); ++i) { + if (tool_array[i] == TOOL_TYPE_EMPTY) continue; + dest1.x = 2 + (i % 3) * 30; + dest1.y = 2 + (i / 3) * 30; + dest2.x = (i % 3) * 30; + dest2.y = (i / 3) * 30; + SDL_RenderCopy(ren, main_ui->theme, &ui_buttons_rect[2], &dest2); + if (i == tool_cur) + SDL_RenderCopy(ren, main_ui->theme, &ui_buttons_rect[0], &dest1); + else + SDL_RenderCopy(ren, main_ui->theme, &ui_buttons_rect[1], &dest1); + SDL_RenderCopy(ren, main_ui->theme, &ui_buttons_rect[tool_array[i]+3], &dest1); + } + SDL_RenderSetViewport(ren, NULL); + /* ui_panel_draw_frame(&p->head); */ + return 0; +} + +/* Panel timeline functions */ +static uint8 +ui_panel_timeline_init(UIPanelTimeline *p) +{ + return ui_panel_init((UIPanel *)p, UI_PANELTYPE_TIMELINE); +} + +/* Panel divider functions */ +static uint8 +ui_panel_divider_init(UIPanelDivider *p, int fw, int lw, int of, uint8 st, UIPanel *p1, UIPanel *p2) +{ + ui_panel_init((UIPanel *)p, UI_PANELTYPE_DIVIDER); + p->fw = fw; + p->lw = lw; + p->of = of; + p->st = st; + p->x = p1; + p->y = p2; + return 0; +} + +static uint8 +ui_panel_divider_resize(UIPanelDivider *p, int x, int y, int w, int h) +{ + SDL_Rect g1, g2; + p->head.geom.x = x; + p->head.geom.y = y; + p->head.geom.w = w; + p->head.geom.h = h; + + if (p->st & 1) { + /* Horizontal line */ + g1.w = g2.w = MAX(0, p->head.geom.w - 2 * p->fw); + + if (p->st & 2) { + g2.h = p->of; + g1.h = MAX(0, p->head.geom.h - 2 * p->fw - p->of - p->lw); + } else { + g1.h = p->of; + g2.h = MAX(0, p->head.geom.h - 2 * p->fw - p->of - p->lw); + } + + g1.x = g2.x = x + p->fw; + g1.y = y + p->fw; + g2.y = y + p->fw + p->lw + g1.h; + } else { + /* Vertical line */ + g1.h = g2.h = MAX(0, p->head.geom.h - 2 * p->fw); + + if (p->st & 2) { + g2.w = p->of; + g1.w = MAX(0, p->head.geom.w - 2 * p->fw - p->of - p->lw); + } else { + g1.w = p->of; + g2.w = MAX(0, p->head.geom.w - 2 * p->fw - p->of - p->lw); + } + + g1.y = g2.y = x + p->fw; + g1.x = x + p->fw; + g2.x = x + p->fw + p->lw + g1.w; + } + + ui_panel_resize(p->x, g1.x, g1.y, g1.w, g1.h); + ui_panel_resize(p->y, g2.x, g2.y, g2.w, g2.h); + + return 0; +} diff --git a/src/ui.h b/src/ui.h new file mode 100644 index 0000000..19c8881 --- /dev/null +++ b/src/ui.h @@ -0,0 +1,36 @@ +enum UI_PanelType { + UI_PANELTYPE_CANVAS, + UI_PANELTYPE_PALETTE, + UI_PANELTYPE_BUTTONS, + UI_PANELTYPE_TIMELINE, + UI_PANELTYPE_DIVIDER, +}; + +struct UI; +struct UIPanel; +struct UIPanelCanvas; +struct UIPanelPalette; +struct UIPanelButtons; +struct UIPanelTimeline; +struct UIPanelDivider; + +typedef struct UI UI; +typedef struct UIPanel UIPanel; +typedef struct UIPanelCanvas UIPanelCanvas; +typedef struct UIPanelPalette UIPanelPalette; +typedef struct UIPanelButtons UIPanelButtons; +typedef struct UIPanelTimeline UIPanelTimeline; +typedef struct UIPanelDivider UIPanelDivider; + +extern UI *main_ui; + +void ui_create(const char *); +void ui_destroy(); +unsigned char ui_resize(); +void ui_redraw(); +void ui_redraw_panel(unsigned char); +void ui_present(); +void ui_mousel_up(int, int); +void ui_mousel_down(int, int); +void ui_mouse_move(int, int); +unsigned char ui_theme_load(const char *); diff --git a/src/user.c b/src/user.c new file mode 100644 index 0000000..d5a545e --- /dev/null +++ b/src/user.c @@ -0,0 +1,238 @@ +#include +#include +#include + +#include "ui.h" +#include "debug.h" +#undef malloc() +#undef realloc() +#undef free() +#define SDL_Window void +#include "cdraw.h" +#include "types.h" +#include "tools.h" +#include "canvas.h" +#include "palette.h" + +void +user_canvas_zoom_change(const Arg *x) +{ + if (cur_canvas == NULL) return; + int oldzoom = cur_canvas->zoom; + canvas_zoom_change(cur_canvas, x->i); + /* canvas_move_x(cur_canvas, (oldzoom - (int)cur_canvas->zoom) * (int)cur_canvas->w / 2); */ + /* canvas_move_y(cur_canvas, (oldzoom - (int)cur_canvas->zoom) * (int)cur_canvas->h / 2); */ + ui_redraw_panel(UI_PANELTYPE_CANVAS); +} + +void +user_canvas_move_x(const Arg *x) +{ + if (cur_canvas == NULL) return; + canvas_move_x(cur_canvas, x->i); + ui_redraw_panel(UI_PANELTYPE_CANVAS); +} + +void +user_canvas_move_y(const Arg *x) +{ + if (cur_canvas == NULL) return; + canvas_move_y(cur_canvas, x->i); + ui_redraw_panel(UI_PANELTYPE_CANVAS); +} + +void +user_canvas_save(const Arg *x) +{ + char line[1024]; + if (cur_canvas == NULL) return; + if (cur_canvas->proj_path != NULL) + goto SAVENONSP; + printf("Project File Name (save): "); + fflush(stdout); + + do { + fgets(line, 1024, stdin); + } while (strlen(line) < 4); + line[strcspn(line, "\n")] = '\0'; + printf("Saved project to file %s\n", line); + canvas_save(cur_canvas, line, 1); + return; +SAVENONSP: + canvas_save(cur_canvas, cur_canvas->proj_path, 0); + printf("Saved project to file %s\n", cur_canvas->proj_path); +} + +void +user_canvas_open(const Arg *x) +{ + char line[1024]; + canvas_destroy(cur_canvas); + + if (x->s == NULL) { + printf("File Name (open): "); + fflush(stdout); + + do { + fgets(line, 1024, stdin); + } while (strlen(line) < 4); + line[strcspn(line, "\n")] = '\0'; + cur_canvas = canvas_open(line, ren); + } else { + cur_canvas = canvas_open(x->s, ren); + } + +/* TODO: show error window */ + if (cur_canvas == NULL) + puts("Error opening file"); + ui_redraw_panel(UI_PANELTYPE_CANVAS); +} + +void +user_canvas_export_png(const Arg *x) +{ + char line[1024]; + if (cur_canvas == NULL) return; + printf("File Name (export): "); + fflush(stdout); + + do { + fgets(line, 1024, stdin); + } while (strlen(line) < 4); + line[strcspn(line, "\n")] = '\0'; +/* TODO: show error window */ + if (canvas_export_png(cur_canvas, line, ren)) + puts("Error while saving file"); + else + puts("File saved"); +} + +void +user_canvas_refresh(const Arg *x) +{ + if (cur_canvas == NULL) return; + puts("Refreshed canvas"); + canvas_refresh(cur_canvas); + ui_redraw_panel(UI_PANELTYPE_CANVAS); +} + +void +user_canvas_create_new(const Arg *x) +{ + int w, h; + printf("Width: "); + fflush(stdout); + scanf("%d", &w); + printf("Height: "); + fflush(stdout); + scanf("%d", &h); + if (cur_canvas != NULL) + canvas_destroy(cur_canvas); + + cur_canvas = canvas_init(w, h, ren); + ui_redraw_panel(UI_PANELTYPE_CANVAS); +} + +void +user_canvas_import_png(const Arg *x) +{ + char line[1024]; + canvas_destroy(cur_canvas); + + if (x->s == NULL) { + printf("File Name (i): "); + fflush(stdout); + + do { + fgets(line, 1024, stdin); + } while (strlen(line) < 4); + line[strcspn(line, "\n")] = '\0'; + cur_canvas = canvas_import_png(line, ren); + } else { + cur_canvas = canvas_import_png(x->s, ren); + } + +/* TODO: show error window */ + if (cur_canvas == NULL) + puts("Error opening file"); + ui_redraw_panel(UI_PANELTYPE_CANVAS); +} + +void +user_canvas_pal_col_chng(const Arg *x) +{ + cur_canvas->cur_col += ((Palette*)def_palette)->num; + cur_canvas->cur_col += x->i; + cur_canvas->cur_col %= ((Palette*)def_palette)->num; + ui_redraw_panel(UI_PANELTYPE_CANVAS); +} + +void +user_tool_change(const Arg *x) +{ + tool_change(x->i); +} + +void +user_testing_layer_add(const Arg *x) +{ +/* TODO: temp function */ + canvas_add_layer(cur_canvas, -1); + fprintf(stdout, "%s:%u: added layer\n", __FILE__, __LINE__); + cur_canvas->cur_layer = cur_canvas->layer_arr_cnt-1; +} + +void +user_layer_chng(const Arg *x) +{ + cur_canvas->cur_layer += (unsigned int)cur_canvas->layer_arr_cnt; + cur_canvas->cur_layer += x->i; + cur_canvas->cur_layer %= cur_canvas->layer_arr_cnt; + + fprintf(stdout, "%s:%u: changed to layer %u\n", __FILE__, __LINE__, cur_canvas->cur_layer); + ui_redraw_panel(UI_PANELTYPE_CANVAS); +} + +void +user_debug_mem_show(const Arg *x) +{ + fprintf(stdout, "DEBUG INFO:\n"); + f_debug_mem_show(); +} + +void +user_testing_reload_tex(const Arg *_x) +{ +/* TODO: temp function */ + /* ui_redraw(); */ + /* return; */ + SDL_Rect dest; + int i; + long int x, y; + + if (cur_canvas == NULL) return; + dest.x = dest.y = 0; + dest.w = cur_canvas->w * 2; + dest.h = cur_canvas->h * 2; + + SDL_DestroyTexture(*((void **)main_ui)); + + *((void **)main_ui) = SDL_CreateTexture( ren, SDL_PIXELFORMAT_RGBA8888, + SDL_TEXTUREACCESS_TARGET, dest.w, dest.h); + SDL_SetTextureBlendMode(*((void **)main_ui), SDL_BLENDMODE_BLEND); + SDL_SetRenderTarget(ren, *((void **)main_ui)); + + for (x = 0; x < cur_canvas->w; ++x) { + for (y = 0; y < cur_canvas->h; ++y) { + SDL_SetRenderDrawColor(ren, INTTOCOLA(cur_canvas->pres_pix[x + y * cur_canvas->w])); + SDL_RenderDrawPoint(ren, 2*x, 2*y); + SDL_RenderDrawPoint(ren, 2*x+1, 2*y); + SDL_RenderDrawPoint(ren, 2*x+1, 2*y+1); + SDL_RenderDrawPoint(ren, 2*x, 2*y+1); + } + } + + SDL_SetRenderTarget(ren, NULL); + ui_redraw(); +} + -- cgit v1.2.3