aboutsummaryrefslogtreecommitdiff
path: root/src/canvas.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/canvas.c')
-rw-r--r--src/canvas.c534
1 files changed, 534 insertions, 0 deletions
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 <stdlib.h>
+#include <string.h>
+#include <SDL2/SDL.h>
+#include <SDL2/SDL_image.h>
+
+#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)]);
+}