diff --git a/README.md b/README.md index 73c5259..b7e082e 100644 --- a/README.md +++ b/README.md @@ -22,22 +22,19 @@ To resume working on a tileset: ### Generics -- `+` Zoom In -- `-` Zoom Out - -### IO - -- `E` Export(nasu-export.chr) -- `R` Render(nasu-render.bmp) +- `ctrl+n` New +- `ctrl+s` Save(.chr) +- `ctrl+shift+s` Save(.bmp) +- `ctrl+plus` Zoom In +- `ctrl+minus` Zoom Out +- `ctrl+H` Toggle Guides ### General - `1-4` Colors0-3 - `ASDFG` Modes0-4 -- `H` Toggle Guides - `Z` Decr. Brush Size - `X` Incr. Brush Size -- `N` Clear ### Paint diff --git a/nasu.c b/nasu.c index 5889c6c..902eaac 100644 --- a/nasu.c +++ b/nasu.c @@ -14,21 +14,27 @@ WITH REGARD TO THIS SOFTWARE. #define HOR 32 #define VER 16 -#define PAD 8 +#define PAD 2 #define SZ (HOR * VER * 16) typedef unsigned char Uint8; +typedef struct { + char name[256]; + Uint8 data[SZ]; +} Document; + typedef struct Brush { int x, y, px, py; int mode, size, color; int down, erase; } Brush; -int WIDTH = 8 * HOR + PAD * 2; -int HEIGHT = 8 * (VER + 2) + PAD * 2; +int WIDTH = 8 * HOR + 8 * PAD * 2; +int HEIGHT = 8 * (VER + 2) + 8 * PAD * 2; int FPS = 30, GUIDES = 1, ZOOM = 2; +Document doc; Brush brush; Uint32 theme[] = { @@ -50,7 +56,6 @@ Uint8 icons[][8] = { {0x00, 0x38, 0x44, 0x92, 0x28, 0x10, 0x00, 0x00} /* eye closed */ }; -Uint8 chrbuf[SZ]; SDL_Window *gWindow; SDL_Renderer *gRenderer; SDL_Texture *gTexture; @@ -58,6 +63,16 @@ Uint32 *pixels; /* helpers */ +char * +scpy(char *src, char *dst, int len) +{ + int i = 0; + while((dst[i] = src[i]) && i < len) + i++; + dst[i + 1] = '\0'; + return dst; +} + int distance(int ax, int ay, int bx, int by) { @@ -85,14 +100,6 @@ shex(char *s, int len) /* chr */ -void -newchr(void) -{ - int i; - for(i = 0; i < SZ; ++i) - chrbuf[i] = 0x00; -} - int rowchr(int x, int y) { @@ -105,8 +112,8 @@ getchr(int x, int y) int ch1, ch2, r = rowchr(x, y); if(r < 0 || r > SZ - 8) return 0; - ch1 = (chrbuf[r] >> (7 - x % 8)) & 1; - ch2 = (chrbuf[r + 8] >> (7 - x % 8)) & 1; + ch1 = (doc.data[r] >> (7 - x % 8)) & 1; + ch2 = (doc.data[r + 8] >> (7 - x % 8)) & 1; if(ch1 && !ch2) return 1; if(!ch1 && ch2) @@ -125,13 +132,13 @@ putchr(int x, int y, int color) if(y < 0 || y >= VER * 8) return; if(color == 0 || color == 2) - chrbuf[row] &= ~(1UL << (7 - col)); + doc.data[row] &= ~(1UL << (7 - col)); else - chrbuf[row] |= 1UL << (7 - col); + doc.data[row] |= 1UL << (7 - col); if(color == 0 || color == 1) - chrbuf[row + 8] &= ~(1UL << (7 - col)); + doc.data[row + 8] &= ~(1UL << (7 - col)); else - chrbuf[row + 8] |= 1UL << (7 - col); + doc.data[row + 8] |= 1UL << (7 - col); } int @@ -216,7 +223,7 @@ void putpixel(Uint32 *dst, int x, int y, int color) { if(x >= 0 && x < WIDTH - 8 && y >= 0 && y < HEIGHT - 8) - dst[(y + PAD) * WIDTH + (x + PAD)] = theme[color]; + dst[(y + PAD * 8) * WIDTH + (x + PAD * 8)] = theme[color]; } void @@ -227,8 +234,8 @@ drawchr(Uint32 *dst, int x, int y, int id) for(h = 0; h < 8; h++) { int px = (x * 8) + (8 - h); int py = (y * 8) + v; - int ch1 = chrbuf[offset + v]; - int ch2 = chrbuf[offset + v + 8]; + int ch1 = doc.data[offset + v]; + int ch2 = doc.data[offset + v + 8]; int clr = ((ch1 >> h) & 0x1) + (((ch2 >> h) & 0x1) << 1); int guides = GUIDES && !clr && (x + y) % 2; putpixel(dst, px, py, guides ? 4 : clr); @@ -236,13 +243,13 @@ drawchr(Uint32 *dst, int x, int y, int id) } void -drawicon(Uint32 *dst, int x, int y, Uint8 *icon, int color) +drawicon(Uint32 *dst, int x, int y, Uint8 *icon, int fg, int bg) { int v, h; for(v = 0; v < 8; v++) for(h = 0; h < 8; h++) { - int c = (icon[v] >> (8 - h)) & 0x1; - putpixel(dst, x + h, y + v, c ? color : 0); + int clr = (icon[v] >> (7 - h)) & 0x1; + putpixel(dst, x + h, y + v, clr == 1 ? fg : bg); } } @@ -250,16 +257,15 @@ void drawui(Uint32 *dst) { int bottom = VER * 8 + 8; - drawicon(dst, 0, bottom, brush.color == 1 ? icons[1] : icons[0], 1); - drawicon(dst, 8, bottom, brush.color == 2 ? icons[1] : icons[0], 2); - drawicon(dst, 16, bottom, brush.color == 3 ? icons[1] : icons[0], 3); - drawicon(dst, 4 * 8, bottom, icons[2], brush.mode == 0 ? 1 : 2); - drawicon(dst, 5 * 8, bottom, icons[3], brush.mode == 1 ? 1 : 2); - drawicon(dst, 6 * 8, bottom, icons[4], brush.mode == 2 ? 1 : 2); - drawicon(dst, 7 * 8, bottom, icons[5], brush.mode == 3 ? 1 : 2); - drawicon(dst, 8 * 8, bottom, icons[6], brush.mode == 4 ? 1 : 2); - - drawicon(dst, 10 * 8, bottom, icons[GUIDES ? 8 : 7], GUIDES ? 1 : 2); + drawicon(dst, 0, bottom, brush.color == 1 ? icons[1] : icons[0], 1, 0); + drawicon(dst, 8, bottom, brush.color == 2 ? icons[1] : icons[0], 2, 0); + drawicon(dst, 16, bottom, brush.color == 3 ? icons[1] : icons[0], 3, 0); + drawicon(dst, 4 * 8, bottom, icons[2], brush.mode == 0 ? 1 : 2, 0); + drawicon(dst, 5 * 8, bottom, icons[3], brush.mode == 1 ? 1 : 2, 0); + drawicon(dst, 6 * 8, bottom, icons[4], brush.mode == 2 ? 1 : 2, 0); + drawicon(dst, 7 * 8, bottom, icons[5], brush.mode == 3 ? 1 : 2, 0); + drawicon(dst, 8 * 8, bottom, icons[6], brush.mode == 4 ? 1 : 2, 0); + drawicon(dst, 10 * 8, bottom, icons[GUIDES ? 8 : 7], GUIDES ? 1 : 2, 0); } void @@ -330,21 +336,38 @@ setguides(int v) } void -destroy(void) +newchr(void) { - newchr(); + int i; + for(i = 0; i < SZ; ++i) + doc.data[i] = 0x00; + scpy("untitled.chr", doc.name, 256); + printf("New: %s\n", doc.name); redraw(pixels); - puts("Destroy"); } int -exportchr(void) +savechr(void) { - FILE *f = fopen("nasu-export.chr", "wb"); - if(!fwrite(chrbuf, sizeof(chrbuf), 1, f)) + FILE *f = fopen(doc.name, "wb"); + if(!fwrite(doc.data, sizeof(doc.data), 1, f)) return error("Export", "Failure"); fclose(f); - puts("Export: nasu-export.chr"); + printf("Save: %s\n", doc.name); + return 1; +} + +int +openchr(char *name) +{ + FILE *f = fopen(name, "r"); + if(!f) + return error("Load", "Invalid input file"); + if(!fread(doc.data, sizeof(doc.data), 1, f)) + return error("Load", "Invalid input size"); + scpy(name, doc.name, 256); + fclose(f); + printf("Load: %s\n", doc.name); return 1; } @@ -364,18 +387,6 @@ renderbmp(void) return 1; } -int -loadchr(FILE *f) -{ - if(!f) - return error("Load", "Invalid input file"); - if(!fread(chrbuf, sizeof(chrbuf), 1, f)) - return error("Load", "Invalid input size"); - puts("Load: Complete"); - fclose(f); - return 1; -} - void loadtheme(FILE *f) { @@ -434,6 +445,10 @@ domouse(SDL_Event *event) brush.erase = 0; break; case SDL_MOUSEBUTTONDOWN: + if(event->motion.y / ZOOM / 8 - PAD == VER + 1) { + selectoption(event->motion.x / ZOOM / 8 - PAD); + return; + } if(event->button.button == SDL_BUTTON_LEFT) brush.down = 1; if(event->button.button == SDL_BUTTON_RIGHT) @@ -441,16 +456,14 @@ domouse(SDL_Event *event) if(event->button.button == SDL_BUTTON_MIDDLE) { brush.erase = 0; if(brush.px != 0 && brush.py != 0) { - brush.x = (event->motion.x - (PAD * ZOOM)) / ZOOM; - brush.y = (event->motion.y - (PAD * ZOOM)) / ZOOM; + brush.x = (event->motion.x - (PAD * 8 * ZOOM)) / ZOOM; + brush.y = (event->motion.y - (PAD * 8 * ZOOM)) / ZOOM; line(brush.px - 1, brush.py, brush.x, brush.y, brush.erase ? 0 : brush.color); redraw(pixels); } } - brush.px = (event->motion.x - (PAD * ZOOM)) / ZOOM; - brush.py = (event->motion.y - (PAD * ZOOM)) / ZOOM; - if(event->motion.y / ZOOM / 8 == VER + 2) - selectoption(event->motion.x / ZOOM / 8 - 1); + brush.px = (event->motion.x - (PAD * 8 * ZOOM)) / ZOOM; + brush.py = (event->motion.y - (PAD * 8 * ZOOM)) / ZOOM; if(brush.down && brush.mode == 0) { putchr(brush.px - 1, brush.py, brush.erase ? 0 : brush.color); redraw(pixels); @@ -458,8 +471,8 @@ domouse(SDL_Event *event) break; case SDL_MOUSEMOTION: if(brush.down) { - brush.x = (event->motion.x - (PAD * ZOOM)) / ZOOM; - brush.y = (event->motion.y - (PAD * ZOOM)) / ZOOM; + brush.x = (event->motion.x - (PAD * 8 * ZOOM)) / ZOOM; + brush.y = (event->motion.y - (PAD * 8 * ZOOM)) / ZOOM; if(!brush.mode) line(brush.px - 1, brush.py, brush.x - 1, brush.y, brush.erase ? 0 : brush.color); else @@ -475,26 +488,45 @@ domouse(SDL_Event *event) void dokey(SDL_Event *event) { + int shift = SDL_GetModState() & KMOD_LSHIFT || SDL_GetModState() & KMOD_RSHIFT; + int ctrl = SDL_GetModState() & KMOD_LCTRL || SDL_GetModState() & KMOD_RCTRL; switch(event->key.keysym.sym) { case SDLK_EQUALS: - case SDLK_PLUS: modzoom(1); break; + case SDLK_PLUS: + if(ctrl) + modzoom(1); + break; case SDLK_UNDERSCORE: - case SDLK_MINUS: modzoom(-1); break; + case SDLK_MINUS: + if(ctrl) + modzoom(-1); + break; case SDLK_1: setcolor(&brush, 1); break; case SDLK_2: setcolor(&brush, 2); break; case SDLK_3: setcolor(&brush, 3); break; case SDLK_4: setcolor(&brush, 0); break; - case SDLK_e: exportchr(); break; - case SDLK_r: renderbmp(); break; case SDLK_a: setmode(&brush, 0); break; - case SDLK_s: setmode(&brush, 1); break; + case SDLK_s: + if(ctrl && shift) + renderbmp(); + else if(ctrl) + savechr(); + else + setmode(&brush, 1); + break; case SDLK_d: setmode(&brush, 2); break; case SDLK_f: setmode(&brush, 3); break; case SDLK_g: setmode(&brush, 4); break; - case SDLK_h: setguides(!GUIDES); break; + case SDLK_h: + if(ctrl) + setguides(!GUIDES); + break; case SDLK_z: modsize(&brush, -1); break; case SDLK_x: modsize(&brush, 1); break; - case SDLK_n: destroy(); break; + case SDLK_n: + if(ctrl) + newchr(); + break; } } @@ -532,17 +564,13 @@ int main(int argc, char **argv) { int ticknext = 0; - brush.erase = 0; - brush.down = 0; brush.color = 1; brush.size = 10; - brush.mode = 0; if(!init()) return error("Init", "Failure"); loadtheme(fopen("theme.svg", "r")); - newchr(); - if(argc > 1) - loadchr(fopen(argv[1], "r")); + if(argc > 1 && !openchr(argv[1])) + newchr(); redraw(pixels); while(1) { int tick = SDL_GetTicks();