#include #include #include #include #include "types.h" #include "action.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) #define HNEXT(i) (i == HISTLENGTH-1 ? 0 : i+1) #define HPREV(i) (i == 0 ? HISTLENGTH-1 : i-1) 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 void action_do(Canvas *); static void action_remove(Action *); static void action_pixcols_redo(Canvas *, Action *); 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->hist_i = c->hist_s = c->hist_isend = 0; c->hist_e = HNEXT(c->hist_s); c->layers = malloc(sizeof(*(c->layers))); c->temp_pix = malloc(w * h * sizeof(* c->temp_pix)); for (i = 0; i < HISTLENGTH; ++i) c->history[i].type = ACT_NULL; 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); c->half_pres = 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; } if (c->half_pres == NULL) { printf("SDL_CreateTexture failed: %s\n", SDL_GetError()); free(c); return NULL; } SDL_SetTextureBlendMode(c->half_pres, SDL_BLENDMODE_BLEND); 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, c->half_pres); SDL_SetRenderDrawColor(ren, 0, 0, 0, 0); SDL_RenderClear(ren); 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); SDL_DestroyTexture(c->half_pres); for (i = 0; i < c->layer_arr_cnt; ++i) layer_destroy(c->layers[i]); for (i = 0; i < HISTLENGTH; ++i) action_remove(&c->history[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); SDL_RenderCopy(ren,c->half_pres,NULL,NULL); 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->half_pres); if (tool_array[tool_cur] == TOOL_TYPE_ERASER) { c->temp_pix[c->temp_cnt].x = x; c->temp_pix[c->temp_cnt].y = y; c->temp_pix[c->temp_cnt++].c = c->layers[c->cur_layer]->pix[COORD(x,y)]; c->layers[c->cur_layer]->pix[COORD(x,y)] = 0; canvas_point_redraw(c, x, y); SDL_SetRenderDrawColor(ren, INTTOCOLA(c->pres_pix[COORD(x,y)])); SDL_RenderDrawPoint(ren, x, y); } else if (tool_array[tool_cur] == TOOL_TYPE_PENCIL) { c->temp_pix[c->temp_cnt].x = x; c->temp_pix[c->temp_cnt].y = y; c->temp_pix[c->temp_cnt++].c = c->layers[c->cur_layer]->pix[COORD(x,y)]; 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); SDL_SetRenderDrawColor(ren, INTTOCOLA(c->pres_pix[COORD(x,y)])); SDL_RenderDrawPoint(ren, 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, c->temp_pix[i].x, c->temp_pix[i].y); SDL_SetRenderDrawColor(ren, INTTOCOLA(c->pres_pix[COORD(c->temp_pix[i].x, c->temp_pix[i].y)])); SDL_RenderDrawPoint(ren, c->temp_pix[i].x, c->temp_pix[i].y); } canvas_mousel_up(c); } 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; c->temp_cnt = 0; 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 || tool_array[tool_cur] == TOOL_TYPE_FILL) && c->temp_cnt != 0) { action_do(c); c->temp_cnt = 0; } 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); return 0; } 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) { /* Fully redraws the canvas */ int i, j; SDL_SetRenderTarget(ren, c->half_pres); for (i = 0; i < c->w; ++i) for (j = 0; j < c->h; ++j) { canvas_point_redraw(c, i, j); SDL_SetRenderDrawColor(ren, INTTOCOLA(c->pres_pix[COORD(i,j)])); SDL_RenderDrawPoint(ren, i, j); } SDL_SetRenderTarget(ren, NULL); } 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->visible = 1; 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) { struct action_pixcol *pnt; if (c == NULL) return 0; pnt = c->temp_pix; c->temp_cnt = 1; c->temp_pix[0].x = x; c->temp_pix[0].y = y; c->layers[c->cur_layer]->pix[COORD(x,y)] = newcol; for (; pnt != &(c->temp_pix)[c->temp_cnt]; ++pnt) { pnt[0].c = oldcol; 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; c->temp_pix[c->temp_cnt].x = x - 1; c->temp_pix[c->temp_cnt].y = y; ++c->temp_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; c->temp_pix[c->temp_cnt].x = x + 1; c->temp_pix[c->temp_cnt].y = y; ++c->temp_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; c->temp_pix[c->temp_cnt].x = x; c->temp_pix[c->temp_cnt].y = y - 1; ++c->temp_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; c->temp_pix[c->temp_cnt].x = x; c->temp_pix[c->temp_cnt].y = y + 1; ++c->temp_cnt; } } return c->temp_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++) if (c->layers[i]->visible) c->pres_pix[COORD(x, y)] = canvas_blend_color(c->pres_pix[COORD(x, y)], c->layers[i]->pix[COORD(x, y)]); } static void action_do(Canvas *c) { action_remove(&c->history[c->hist_i]); c->history[c->hist_i].type = ACT_PIXELSCOLORS; c->history[c->hist_i].act.px.cnt = c->temp_cnt; c->history[c->hist_i].act.px.pix = malloc(c->temp_cnt * sizeof(struct action_pixcol)); memcpy(c->history[c->hist_i].act.px.pix, c->temp_pix, c->temp_cnt * sizeof(struct action_pixcol)); if (c->hist_i == c->hist_e) c->hist_e = HNEXT(c->hist_e); if (c->hist_isend && c->hist_s == c->hist_i) c->hist_s = HNEXT(c->hist_s); c->hist_isend = 1; c->hist_i = HNEXT(c->hist_i); c->hist_e = c->hist_i; } void action_undo(Canvas *c) { if (!c->hist_isend && c->hist_i == c->hist_s) return; c->hist_i = HPREV(c->hist_i); c->hist_isend = 0; switch(c->history[c->hist_i].type) { case ACT_PIXELSCOLORS: action_pixcols_redo(c, &c->history[c->hist_i]); break; } } void action_redo(Canvas *c) { if (c->hist_isend) return; switch(c->history[c->hist_i].type) { case ACT_PIXELSCOLORS: action_pixcols_redo(c, &c->history[c->hist_i]); break; } c->hist_i = HNEXT(c->hist_i); c->hist_isend = (c->hist_i == c->hist_e); } static void action_remove(Action *a) { switch(a->type) { case ACT_PIXELSCOLORS: free(a->act.px.pix); break; } a->type = ACT_NULL; } static void action_pixcols_redo(Canvas *c, Action *a) { struct action_pixcol *pnt, *end; unsigned int col; pnt = a->act.px.pix; end = &pnt[a->act.px.cnt]; SDL_SetRenderTarget(ren, c->half_pres); for (; pnt != end; pnt++) { col = c->layers[c->cur_layer]->pix[COORD(pnt->x, pnt->y)]; c->layers[c->cur_layer]->pix[COORD(pnt->x, pnt->y)] = pnt->c; pnt->c = col; canvas_point_redraw(c, pnt->x, pnt->y); SDL_SetRenderDrawColor(ren, INTTOCOLA(c->pres_pix[COORD(pnt->x, pnt->y)])); SDL_RenderDrawPoint(ren, pnt->x, pnt->y); } SDL_SetRenderTarget(ren, NULL); }