From 351eec551f58225f4c4845e2f1c7c14a1cdf1fde Mon Sep 17 00:00:00 2001 From: Devine Lu Linvega Date: Fri, 25 Sep 2020 13:31:14 -0700 Subject: [PATCH] Renamed chr6 to nasu --- README.md | 6 +- build.sh | 10 +- nasu.c | 444 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 452 insertions(+), 8 deletions(-) create mode 100644 nasu.c diff --git a/README.md b/README.md index b63ab3e..84c6b1b 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ -# Chr6 +# Nasu A minimal chr editor, written in ANSI C. ## Build -To build chr6, you must have [SDL2](https://wiki.libsdl.org/). +To build nasu, you must have [SDL2](https://wiki.libsdl.org/). ``` sudo apt-get install libsdl2-dev @@ -15,7 +15,7 @@ sudo apt-get install libsdl2-dev To resume working on a tileset: ``` -./chr6 export.chr +./nasu export.chr ``` ## Controls diff --git a/build.sh b/build.sh index baa6190..540d953 100755 --- a/build.sh +++ b/build.sh @@ -1,14 +1,14 @@ # format code -clang-format -i chr6.c +clang-format -i nasu.c # remove old -rm ./chr6 +rm ./nasu # debug -cc -std=c89 -DDEBUG -Wall -Wpedantic -Wshadow -Wextra -Werror=implicit-int -Werror=incompatible-pointer-types -Werror=int-conversion -Wvla -g -Og -fsanitize=address -fsanitize=undefined chr6.c -I/usr/local/include -L/usr/local/lib -lSDL2 -o chr6 +cc -std=c89 -DDEBUG -Wall -Wpedantic -Wshadow -Wextra -Werror=implicit-int -Werror=incompatible-pointer-types -Werror=int-conversion -Wvla -g -Og -fsanitize=address -fsanitize=undefined nasu.c -I/usr/local/include -L/usr/local/lib -lSDL2 -o nasu # build -# cc -std=c89 -Wall chr6.c -I/usr/local/include -L/usr/local/lib -lSDL2 -o chr6 +# cc -std=c89 -Wall nasu.c -I/usr/local/include -L/usr/local/lib -lSDL2 -o nasu # run -./chr6 sprite.chr +./nasu sprite.chr diff --git a/nasu.c b/nasu.c new file mode 100644 index 0000000..95cd331 --- /dev/null +++ b/nasu.c @@ -0,0 +1,444 @@ +#include +#include + +#define HOR 32 +#define VER 16 +#define PAD 32 +#define ZOOM 4 +#define color1 0x000000 +#define color2 0x72DEC2 +#define color3 0xFFFFFF +#define color4 0x444444 +#define color0 0x111111 + +#define SZ HOR* VER * 16 + +typedef struct Point { + int x; + int y; +} Point; + +typedef struct Brush { + int color; + int down; + int edit; + int mode; + int size; + int erase; + Point pos; + Point prev; +} Brush; + +char* modes[] = { + "line", + "tone", + "bold", + "full", + "hori", + "veri", + "exes"}; + +unsigned char buffer[SZ]; +int colors[] = {color1, color2, color3, color4, color0}; +int WIDTH = 8 * HOR * ZOOM + PAD * 2; +int HEIGHT = 8 * VER * ZOOM + PAD * 2; +int FPS = 30; +int GUIDES = 1; +SDL_Window* gWindow = NULL; +SDL_Renderer* gRenderer = NULL; +SDL_Texture* gTexture = NULL; +uint32_t* pixels; + +Point* +setpt(Point* p, int x, int y) +{ + p->x = x; + p->y = y; + return p; +} + +int +inspt(Point* p, int w, int h) +{ + return p->x >= 0 && p->y >= 0 && p->x < w && p->y < h; +} + +int +dispt(Point* a, Point* b) +{ + return (b->x - a->x) * (b->x - a->x) + (b->y - a->y) * (b->y - a->y); +} + +void +pixel(uint32_t* dst, int x, int y, int c) +{ + int h, v; + for(h = 0; h < ZOOM; ++h) + for(v = 0; v < ZOOM; ++v) + dst[((y * ZOOM) + PAD + v) * WIDTH + ((x * ZOOM) + PAD + h)] = c; +} + +void +draw(uint32_t* dst, int id, int color) +{ + int ti = id / 64; + int odd = (ti + (ti / HOR + 2)) % 2 == 0; + int px = (ti / (HOR * VER)) * (8 * HOR) + (ti % HOR) * 8 + (id % 8); + int py = ((ti / HOR) * 8) + ((id % 64) / 8); + pixel(dst, px, py, colors[GUIDES && odd && color == 0 ? 4 : color]); +} + +void +redraw(uint32_t* dst) +{ + int b, i, j, id = 0, ch1, ch2, color; + for(b = 0; b < SZ; b += 16) + for(i = 0; i < 8; i++) + for(j = 7; j >= 0; j--) { + ch1 = buffer[b + i]; + ch2 = buffer[b + i + 8]; + color = ((ch1 >> j) & 0x1) + (((ch2 >> j) & 0x1) << 1); + draw(dst, id, color); + id++; + } + SDL_UpdateTexture(gTexture, NULL, dst, WIDTH * sizeof(uint32_t)); + SDL_RenderClear(gRenderer); + SDL_RenderCopy(gRenderer, gTexture, NULL, NULL); + SDL_RenderPresent(gRenderer); +} + +void +write(int tx, int ty, int px, int py, int color) +{ + int id = tx + ty * HOR; + int row = py + (id * 16); + if(row > SZ - 8) + return; + if(color == 0) { + buffer[row] &= ~(1UL << (7 - px)); + buffer[row + 8] &= ~(1UL << (7 - px)); + } else if(color == 2) { + buffer[row] |= 1UL << (7 - px); + buffer[row + 8] &= ~(1UL << (7 - px)); + } else if(color == 1) { + buffer[row] &= ~(1UL << (7 - px)); + buffer[row + 8] |= 1UL << (7 - px); + } else if(color == 3) { + buffer[row] |= 1UL << (7 - px); + buffer[row + 8] |= 1UL << (7 - px); + } +} + +void +edit(int x, int y, int color) +{ + if(x < 0 || y < 0 || x > 8 * HOR || y > 8 * VER) + return; + write(x / 8, y / 8, x % 8, y % 8, color); +} + +int +patt(int x, int y, int mode, int size) +{ + if(mode == 1) + return ((x + y) % 4) == 0 && ((y - x) % 4) == 0; + if(mode == 2) + return ((x + y) % 2) == 0 && ((y - x) % 2) == 0; + if(mode == 3) + return 1; + if(mode == 4) + return y % size == 0; + if(mode == 5) + return x % size == 0; + if(mode == 6) + return (x + y) % size == 0 || (x - y) % size == 0; + return 0; +} + +void +fill(Brush* b, int mode, int size, Point p0, int color) +{ + int x, y; + Point p; + for(x = -size / 2; x < size; ++x) + for(y = -size / 2; y < size; ++y) { + setpt(&p, p0.x + x, p0.y + y); + if(patt(p.x, p.y, mode, size) && dispt(&p0, &p) < size) + edit(p.x, p.y, color); + } + b->edit = 1; + redraw(pixels); +} + +void +line(Point* p0, Point* p1, int color) +{ + int dx = abs(p1->x - p0->x), sx = p0->x < p1->x ? 1 : -1; + int dy = -abs(p1->y - p0->y), sy = p0->y < p1->y ? 1 : -1; + int err = dx + dy, e2; + for(;;) { + edit(p0->x, p0->y, color); + if(p0->x == p1->x && p0->y == p1->y) + break; + e2 = 2 * err; + if(e2 >= dy) { + err += dy; + p0->x += sx; + } + if(e2 <= dx) { + err += dx; + p0->y += sy; + } + } + redraw(pixels); +} + +void +update(Brush* b) +{ + char title[512]; + snprintf(title, 512, "%s%dc%d [%d:%dx%d]%c", + modes[b->mode], + b->size, + b->color, + HOR, + VER, + ZOOM, + b->edit ? '*' : ' '); + SDL_SetWindowTitle(gWindow, title); +} + +int +error(char* msg, const char* err) +{ + printf("Error %s: %s\n", msg, err); + return 0; +} + +void +create(void) +{ + int i; + for(i = 0; i < SZ; ++i) + buffer[i] = 0x00; + redraw(pixels); +} + +void +save(Brush* b) +{ + FILE* f = fopen("export.chr", "wb"); + if(!fwrite(buffer, sizeof(buffer), 1, f)) + error("Save", "Invalid output file"); + fclose(f); + b->edit = 0; +} + +void +load(char* path) +{ + FILE* f = fopen(path, "rb"); + if(f == NULL) + error("Load", "Invalid input file"); + if(!fread(buffer, sizeof(buffer), 1, f)) + error("Load", "Invalid input size"); + fclose(f); + redraw(pixels); +} + +void +render(void) +{ + SDL_Surface* surface = SDL_GetWindowSurface(gWindow); + GUIDES = 0; + redraw(pixels); + SDL_RenderReadPixels(gRenderer, + NULL, + SDL_PIXELFORMAT_ARGB8888, + surface->pixels, + surface->pitch); + SDL_SaveBMP(surface, "render.bmp"); + SDL_FreeSurface(surface); +} + +void +quit(void) +{ + free(pixels); + SDL_DestroyTexture(gTexture); + gTexture = NULL; + SDL_DestroyRenderer(gRenderer); + gRenderer = NULL; + SDL_DestroyWindow(gWindow); + gWindow = NULL; + SDL_Quit(); + exit(0); +} + +void +domouse(SDL_Event* event, Brush* b) +{ + switch(event->type) { + case SDL_MOUSEBUTTONUP: + if(event->button.button == SDL_BUTTON_LEFT) { + b->down = 0; + setpt(&b->prev, 0, 0); + } + if(event->button.button == SDL_BUTTON_RIGHT) + b->erase = 0; + b->edit = 1; + update(b); + break; + case SDL_MOUSEBUTTONDOWN: + if(event->button.button == SDL_BUTTON_LEFT) { + b->down = 1; + setpt(&b->prev, + (event->motion.x - PAD) / ZOOM, + (event->motion.y - PAD) / ZOOM); + } + if(event->button.button == SDL_BUTTON_RIGHT) + b->erase = 1; + case SDL_MOUSEMOTION: + if(b->down) { + setpt(&b->pos, + (event->motion.x - PAD) / ZOOM, + (event->motion.y - PAD) / ZOOM); + if(b->mode == 0) + line(&b->prev, &b->pos, b->erase ? 0 : b->color); + else + fill(b, b->mode, b->size, b->pos, b->erase ? 0 : b->color); + setpt(&b->prev, b->pos.x, b->pos.y); + } + break; + } +} + +void +dokey(SDL_Event* event, Brush* b) +{ + switch(event->key.keysym.sym) { + case SDLK_ESCAPE: + quit(); + break; + case SDLK_e: + save(b); + break; + case SDLK_r: + render(); + break; + case SDLK_TAB: + b->color = b->color > 2 ? 0 : b->color + 1; + break; + case SDLK_h: + GUIDES = !GUIDES; + redraw(pixels); + break; + case SDLK_n: + create(); + break; + case SDLK_1: + b->mode = 0; + break; + case SDLK_2: + b->mode = 1; + break; + case SDLK_3: + b->mode = 2; + break; + case SDLK_4: + b->mode = 3; + break; + case SDLK_5: + b->mode = 4; + break; + case SDLK_6: + b->mode = 5; + break; + case SDLK_7: + b->mode = 6; + break; + case SDLK_z: + if(b->size > 1) + b->size -= 1; + break; + case SDLK_x: + if(b->size < 30) + b->size += 1; + break; + } + update(b); +} + +int +init(void) +{ + int i, j; + if(SDL_Init(SDL_INIT_VIDEO) < 0) + return error("Init", SDL_GetError()); + gWindow = SDL_CreateWindow("nasu", + SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, + WIDTH, + HEIGHT, + SDL_WINDOW_SHOWN); + if(gWindow == NULL) + return error("Window", SDL_GetError()); + gRenderer = SDL_CreateRenderer(gWindow, -1, 0); + if(gRenderer == NULL) + return error("Renderer", SDL_GetError()); + gTexture = SDL_CreateTexture(gRenderer, + SDL_PIXELFORMAT_ARGB8888, + SDL_TEXTUREACCESS_STATIC, + WIDTH, + HEIGHT); + if(gTexture == NULL) + return error("Texture", SDL_GetError()); + pixels = (uint32_t*)malloc(WIDTH * HEIGHT * sizeof(uint32_t)); + if(pixels == NULL) + return error("Pixels", "Failed to allocate memory"); + for(i = 0; i < HEIGHT; i++) + for(j = 0; j < WIDTH; j++) + pixels[i * WIDTH + j] = color1; + return 1; +} + +int +main(int argc, char** argv) +{ + int ticknext = 0; + Brush brush; + brush.down = 0; + brush.color = 1; + brush.edit = 0; + brush.size = 10; + brush.mode = 2; + + if(!init()) + return error("Init", "Failure"); + + if(argc > 1) + load(argv[1]); + else + create(); + update(&brush); + + while(1) { + int tick = SDL_GetTicks(); + SDL_Event event; + if(event.type == SDL_QUIT) + quit(); + if(tick < ticknext) + SDL_Delay(ticknext - tick); + ticknext = tick + (1000 / FPS); + while(SDL_PollEvent(&event) != 0) { + if(event.type == SDL_MOUSEBUTTONUP || + event.type == SDL_MOUSEBUTTONDOWN || + event.type == SDL_MOUSEMOTION) { + domouse(&event, &brush); + } else if(event.type == SDL_KEYDOWN) + dokey(&event, &brush); + } + } + quit(); + return 0; +}