~rabbits/nasu

351eec551f58225f4c4845e2f1c7c14a1cdf1fde — Devine Lu Linvega 10 months ago 118672b
Renamed chr6 to nasu
3 files changed, 452 insertions(+), 8 deletions(-)

M README.md
M build.sh
A nasu.c
M README.md => README.md +3 -3
@@ 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

M build.sh => build.sh +5 -5
@@ 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

A nasu.c => nasu.c +444 -0
@@ 0,0 1,444 @@
#include <SDL2/SDL.h>
#include <stdio.h>

#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;
}