diff options
| author | Krow Savcik <krow@savcik.xyz> | 2024-02-06 22:56:21 +0200 | 
|---|---|---|
| committer | Krow Savcik <krow@savcik.xyz> | 2024-02-06 22:56:21 +0200 | 
| commit | c918280fb5de6e6256cfd9a438b3578c04e3afc2 (patch) | |
| tree | ba72e1464c487b61d4376647769c93cdbccf7a21 /src | |
| parent | 009a890482edfb2247da1d69b86f4d193699e3cb (diff) | |
feautre: added undo/redo buttons
The change history is kept in a ring buffer of definite size 2000.
Diffstat (limited to 'src')
| -rw-r--r-- | src/action.h | 25 | ||||
| -rw-r--r-- | src/canvas.c | 166 | ||||
| -rw-r--r-- | src/canvas.h | 14 | ||||
| -rw-r--r-- | src/cdraw.c | 1 | ||||
| -rw-r--r-- | src/cdraw.h | 2 | ||||
| -rw-r--r-- | src/tools.c | 4 | ||||
| -rw-r--r-- | src/tools.h | 2 | ||||
| -rw-r--r-- | src/ui.c | 11 | ||||
| -rw-r--r-- | src/user.c | 17 | 
9 files changed, 196 insertions, 46 deletions
| diff --git a/src/action.h b/src/action.h index 7405418..d9b7698 100644 --- a/src/action.h +++ b/src/action.h @@ -1,22 +1,25 @@ +typedef struct Action Action; +  enum Actions_Types { +	ACT_NULL,  	ACT_PIXELSCOLORS, -} - -struct act_pixcol { -	long int p;      /* Position */ -	unsigned int c;  /* Color */  }; -struct Action { -	int type; -	ActUnion act; +struct action_pixcol { +	int x, y;      /* Position */ +	unsigned int c;  /* Color */  };  struct ActionPixelsColors {  	long int cnt; -	struct act_pixcol *pix; -} +	struct actiton_pixcol *pix; +};  typedef union { -	ActionPixelsColors px; +	struct ActionPixelsColors px;  } ActUnion; + +struct Action { +	int type; +	ActUnion act; +}; diff --git a/src/canvas.c b/src/canvas.c index ea50d53..ce8772b 100644 --- a/src/canvas.c +++ b/src/canvas.c @@ -4,6 +4,7 @@  #include <SDL2/SDL_image.h>  #include "types.h" +#include "action.h"  #include "canvas.h"  #include "cdraw.h"  #include "palette.h" @@ -13,6 +14,8 @@  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 *); @@ -20,6 +23,9 @@ 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) @@ -52,8 +58,12 @@ canvas_init(uint w, uint h, void *ren)  	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(SDL_Point)); +	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); @@ -184,6 +194,8 @@ canvas_destroy(Canvas *c)  	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); @@ -260,11 +272,17 @@ canvas_point_draw(Canvas *c, long int x, long int y)  	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, @@ -276,10 +294,11 @@ canvas_point_draw(Canvas *c, long int x, long int 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_SetRenderDrawColor(ren, INTTOCOLA(c->pres_pix[COORD(((SDL_Point *)c->temp_pix)[i].x,((SDL_Point *)c->temp_pix)[i].y)])); -			SDL_RenderDrawPoint(ren, ((SDL_Point *)c->temp_pix)[i].x, ((SDL_Point *)c->temp_pix)[i].y); +			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);  } @@ -309,6 +328,7 @@ canvas_mousel_down(Canvas *c, long int x, long int y)  	case TOOL_TYPE_ERASER:  	case TOOL_TYPE_FILL:  		is_drawing = 1; +		c->temp_cnt = 0;  		canvas_point_draw(c, x, y);  		break;  	} @@ -333,9 +353,11 @@ canvas_mouse_move(Canvas *c, long int x, long int y)  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*/ -/*	}*/ +	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;  } @@ -489,52 +511,52 @@ layer_destroy(Layer *lay)  static uint  canvas_fill_bfs(Canvas *c, int x, int y, uint oldcol, uint newcol)  { -    int cnt; -    SDL_Point *pnt; +	struct action_pixcol *pnt; -    if (c == NULL) -        return 0; +	if (c == NULL) +		return 0; -    pnt = c->temp_pix; -    cnt = 1; +	pnt = c->temp_pix; +	c->temp_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; +	c->temp_pix[0].x = x; +	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) { +    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; -            ((SDL_Point *)c->temp_pix)[cnt].x = x - 1; -            ((SDL_Point *)c->temp_pix)[cnt].y = y; -            ++cnt; +            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; -            ((SDL_Point *)c->temp_pix)[cnt].x = x + 1; -            ((SDL_Point *)c->temp_pix)[cnt].y = y; -            ++cnt; +            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; -            ((SDL_Point *)c->temp_pix)[cnt].x = x; -            ((SDL_Point *)c->temp_pix)[cnt].y = y - 1; -            ++cnt; +            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; -            ((SDL_Point *)c->temp_pix)[cnt].x = x; -            ((SDL_Point *)c->temp_pix)[cnt].y = y + 1; -            ++cnt; +            c->temp_pix[c->temp_cnt].x = x; +            c->temp_pix[c->temp_cnt].y = y + 1; +            ++c->temp_cnt;          }      } -    return cnt; +    return c->temp_cnt;  }  static uint @@ -548,10 +570,92 @@ canvas_blend_color(uint a, uint b)  static void  canvas_point_redraw(Canvas *c, long int x, long int y)  { -	/* expects for the render target to be c->half_pres */  	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)]);  } + +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); +} diff --git a/src/canvas.h b/src/canvas.h index 104f130..bc96ab9 100644 --- a/src/canvas.h +++ b/src/canvas.h @@ -1,5 +1,10 @@ +/* Should include action.h before it */ + +/* TODO: move to config.h */ +#define HISTLENGTH 2000 +  struct Layer { -    unsigned int *pix; +	unsigned int *pix;  };  struct Canvas { @@ -10,8 +15,11 @@ struct Canvas {  	struct Layer **layers;  	int x, y;  	void *back, *pres, *half_pres; -	void *temp_pix; +	struct action_pixcol *temp_pix; +	int temp_cnt;  	uint *pres_pix; +	Action history[HISTLENGTH]; +	int hist_s, hist_e, hist_i, hist_isend;  };  typedef struct Layer Layer; @@ -38,3 +46,5 @@ 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 *); +void            action_undo(Canvas *); +void            action_redo(Canvas *); diff --git a/src/cdraw.c b/src/cdraw.c index c0c06c5..25c0dba 100644 --- a/src/cdraw.c +++ b/src/cdraw.c @@ -4,6 +4,7 @@  #include "cdraw.h"  #include "types.h" +#include "action.h"  #include "canvas.h"  #include "debug.h"  #include "palette.h" diff --git a/src/cdraw.h b/src/cdraw.h index 38bb058..1d66b6b 100644 --- a/src/cdraw.h +++ b/src/cdraw.h @@ -29,6 +29,8 @@ 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_canvas_undo(const Arg *); +void            user_canvas_redo(const Arg *);  void            user_tool_change(const Arg *);  void            user_layer_chng(const Arg *);  void            user_testing_layer_add(const Arg *); diff --git a/src/tools.c b/src/tools.c index 1ff979a..97f335c 100644 --- a/src/tools.c +++ b/src/tools.c @@ -2,6 +2,7 @@  #include "tools.h"  #include "types.h" +#include "action.h"  #include "canvas.h"  #include "ui.h" @@ -16,8 +17,9 @@ uint8 tool_array[] = {  	TOOL_TYPE_CPICKER,  	TOOL_TYPE_ZOOMOUT,  	TOOL_TYPE_ZOOMIN, -	TOOL_TYPE_EMPTY, +	TOOL_TYPE_UNDO,  	TOOL_TYPE_CLOWN, +	TOOL_TYPE_REDO,  };  void diff --git a/src/tools.h b/src/tools.h index c18afe3..cd232de 100644 --- a/src/tools.h +++ b/src/tools.h @@ -6,6 +6,8 @@ enum Tool_Types {  	TOOL_TYPE_ZOOMOUT,  	TOOL_TYPE_CLOWN,  	TOOL_TYPE_CPICKER, +	TOOL_TYPE_UNDO, +	TOOL_TYPE_REDO,  	TOOL_TYPE_EMPTY,  }; @@ -6,6 +6,7 @@  #include "ui.h"  #include "cdraw.h"  #include "types.h" +#include "action.h"  #include "canvas.h"  #include "tools.h"  #include "debug.h" @@ -37,6 +38,8 @@ static const SDL_Rect ui_buttons_rect[] = {  	{56, 54, 28, 28},  /* Zoom out */  	{84, 54, 28, 28},  /* Clown */  	{56, 82, 28, 28},  /* Color Picker */ +	{84, 82, 28, 28},  /* Undo */ +	{84, 110, 28, 28}, /* Redo */  };  struct UIPanel { @@ -272,6 +275,12 @@ ui_mousel_down(int x, int y)  			break;  		case TOOL_TYPE_EMPTY:  			break; +		case TOOL_TYPE_UNDO: +			user_canvas_undo(NULL); +			break; +		case TOOL_TYPE_REDO: +			user_canvas_redo(NULL); +			break;  		default:  			tool_change(col);  			break; @@ -369,7 +378,7 @@ ui_focus_panel_change(int x, int y)      if (main_ui->focus_panel != NULL) {          switch (main_ui->focus_panel->type) {          case UI_PANELTYPE_CANVAS: -            is_drawing = 0; +			canvas_mousel_up(cur_canvas);              main_ui->focus_panel = u;              ui_redraw_panel(UI_PANELTYPE_CANVAS);              break; @@ -12,6 +12,7 @@  #include "cdraw.h"  #include "types.h"  #include "tools.h" +#include "action.h"  #include "canvas.h"  #include "palette.h" @@ -242,3 +243,19 @@ choose_file_path(const char *prompt, char *input)  	if (ft != stdin) pclose(ft);  } + +void +user_canvas_undo(const Arg *x) +{ +	if (cur_canvas == NULL) return; +	action_undo(cur_canvas); +	ui_redraw_panel(UI_PANELTYPE_CANVAS); +} + +void +user_canvas_redo(const Arg *x) +{ +	if (cur_canvas == NULL) return; +	action_redo(cur_canvas); +	ui_redraw_panel(UI_PANELTYPE_CANVAS); +} | 
