From 9e660e594f6d3a43ea1427fb872801a2fcedad93 Mon Sep 17 00:00:00 2001 From: Krow Savcik Date: Tue, 26 Sep 2023 22:32:35 +0300 Subject: initial commit --- src/canvas.c | 534 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 534 insertions(+) create mode 100644 src/canvas.c (limited to 'src/canvas.c') 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)]); +} -- cgit v1.2.3