@@ 15,12 15,18 @@ 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 {
+ int unsaved;
+ char name[256];
+ Uint8 data[SZ];
+} Document;
+
+typedef struct {
int x, y;
} Point2d;
@@ 50,16 56,15 @@ typedef struct {
Path2d paths[256];
} Shape2d;
+Document doc;
Path2d stack;
Shape2d shape;
Brush 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;
-/* Interface */
-
Uint32 theme[] = {
0x000000,
0xFFFFFF,
@@ 77,7 82,8 @@ Uint8 icons[][8] = {
{0x1e, 0x06, 0x0a, 0x12, 0x20, 0x40, 0x80, 0x00},
{0x06, 0x18, 0x22, 0x40, 0x42, 0x80, 0xaa, 0x00},
{0x00, 0x00, 0x00, 0x82, 0x44, 0x38, 0x00, 0x00}, /* eye open */
- {0x00, 0x38, 0x44, 0x92, 0x28, 0x10, 0x00, 0x00} /* eye closed */
+ {0x00, 0x38, 0x44, 0x92, 0x28, 0x10, 0x00, 0x00}, /* eye closed */
+ {0x10, 0x54, 0x28, 0xc6, 0x28, 0x54, 0x10, 0x00} /* unsaved */
};
SDL_Window *gWindow = NULL;
@@ 85,7 91,7 @@ SDL_Renderer *gRenderer = NULL;
SDL_Texture *gTexture = NULL;
Uint32 *pixels;
-/* helpers */
+#pragma mark - Helpers
Point2d *
setpt2d(Point2d *p, int x, int y)
@@ 160,6 166,16 @@ scmp(char *a, char *b)
return 0;
}
+char *
+scpy(char *src, char *dst, int len)
+{
+ int i = 0;
+ while((dst[i] = src[i]) && i < len - 2)
+ i++;
+ dst[i + 1] = '\0';
+ return dst;
+}
+
int
sint(char *s, int len)
{
@@ 169,25 185,6 @@ sint(char *s, int len)
return num;
}
-Uint8
-chex(char c)
-{
- if(c >= 'a' && c <= 'f')
- return 10 + c - 'a';
- if(c >= 'A' && c <= 'F')
- return 10 + c - 'A';
- return (c - '0') & 0xF;
-}
-
-int
-shex(char *s, int len)
-{
- int i, n = 0;
- for(i = 0; i < len; ++i)
- n |= (chex(s[i]) << ((len - i - 1) * 4));
- return n;
-}
-
int
cancast(LineType type)
{
@@ 224,7 221,7 @@ colortheme(Uint32 hex)
return 0;
}
-/* draw */
+#pragma mark - Draw
void
clear(Uint32 *dst)
@@ 238,7 235,7 @@ clear(Uint32 *dst)
int
keypixel(int x, int y)
{
- return (y + PAD) * WIDTH + (x + PAD);
+ return (y + PAD * 8) * WIDTH + (x + PAD * 8);
}
int
@@ 251,7 248,7 @@ void
putpixel(Uint32 *dst, int x, int y, int color)
{
if(x >= 0 && x < WIDTH - 8 && y >= 0 && y < HEIGHT - 8)
- dst[keypixel(x, y)] = theme[color];
+ dst[(y + PAD * 8) * WIDTH + (x + PAD * 8)] = theme[color];
}
void
@@ 456,8 453,8 @@ void
drawguides(Uint32 *dst, int step)
{
int x, y;
- for(x = 2; x < WIDTH - 16; x++)
- for(y = 2; y < HEIGHT - 32; y++)
+ for(x = 2; x < HOR * 8; x++)
+ for(y = 2; y < VER * 8; y++)
if(x % (step * 2) == y % (step * 2) && x % (step * 2) == 0)
putpixel(dst, x, y, 3);
else if(x % step == 0 && y % 2 == 0)
@@ 467,13 464,13 @@ drawguides(Uint32 *dst, int step)
}
void
-drawicon(Uint32 *dst, int x, int y, Uint8 *icon, int color)
+drawicn(Uint32 *dst, int x, int y, Uint8 *sprite, 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 ch1 = (sprite[v] >> (7 - h)) & 0x1;
+ putpixel(dst, x + h, y + v, ch1 ? fg : bg);
}
}
@@ 482,17 479,17 @@ drawui(Uint32 *dst)
{
int clr = brush.selection >= 0 ? shape.paths[brush.selection].color : 1;
int bottom = VER * 8 + 8;
- drawicon(dst, 0, bottom, icons[clr == 1 ? 1 : 0], 1);
- drawicon(dst, 1 * 8, bottom, icons[clr == 2 ? 1 : 0], 2);
- drawicon(dst, 2 * 8, bottom, icons[clr == 3 ? 1 : 0], 3);
- drawicon(dst, 4 * 8, bottom, icons[2], cancast(LINE) ? 2 : 3);
- drawicon(dst, 5 * 8, bottom, icons[3], cancast(ARC) ? 2 : 3);
- drawicon(dst, 6 * 8, bottom, icons[4], cancast(BEZIER) ? 2 : 3);
- drawicon(dst, 7 * 8, bottom, icons[5], cancast(RECTANGLE) ? 2 : 3);
- drawicon(dst, 8 * 8, bottom, icons[1], cancast(ELLIPSE) ? 2 : 3);
- drawicon(dst, 9 * 8, bottom, icons[6], cancast(TRANSLATE) ? 2 : 3);
-
- drawicon(dst, 11 * 8, bottom, icons[GUIDES ? 9 : 8], GUIDES ? 1 : 2);
+ drawicn(dst, 0, bottom, icons[clr == 1 ? 1 : 0], 1, 0);
+ drawicn(dst, 1 * 8, bottom, icons[clr == 2 ? 1 : 0], 2, 0);
+ drawicn(dst, 2 * 8, bottom, icons[clr == 3 ? 1 : 0], 3, 0);
+ drawicn(dst, 4 * 8, bottom, icons[2], cancast(LINE) ? 2 : 3, 0);
+ drawicn(dst, 5 * 8, bottom, icons[3], cancast(ARC) ? 2 : 3, 0);
+ drawicn(dst, 6 * 8, bottom, icons[4], cancast(BEZIER) ? 2 : 3, 0);
+ drawicn(dst, 7 * 8, bottom, icons[5], cancast(RECTANGLE) ? 2 : 3, 0);
+ drawicn(dst, 8 * 8, bottom, icons[1], cancast(ELLIPSE) ? 2 : 3, 0);
+ drawicn(dst, 9 * 8, bottom, icons[6], cancast(TRANSLATE) ? 2 : 3, 0);
+ drawicn(dst, 11 * 8, bottom, icons[GUIDES ? 9 : 8], GUIDES ? 1 : 2, 0);
+ drawicn(dst, (HOR - 1) * 8, bottom, icons[10], doc.unsaved ? 2 : 3, 0); /* save state */
}
void
@@ 522,7 519,7 @@ redraw(Uint32 *dst)
SDL_RenderPresent(gRenderer);
}
-/* options */
+#pragma mark - Options
int
error(char *msg, const char *err)
@@ 532,6 529,108 @@ error(char *msg, const char *err)
}
void
+savemode(int *i, int v)
+{
+ *i = v;
+ redraw(pixels);
+}
+
+void
+makedoc(Document *d, char *name)
+{
+ int i;
+ for(i = 0; i < SZ; ++i)
+ d->data[i] = 0x00;
+ d->unsaved = 0;
+ scpy(name, d->name, 256);
+ printf("Made: %s\n", d->name);
+ redraw(pixels);
+}
+
+int
+savedoc(Document *d, char *name)
+{
+ int i, j, c = -1;
+ FILE *f = fopen(name, "w");
+ char *names[] = {
+ "point",
+ "line",
+ "arc",
+ "bezier",
+ "rectangle",
+ "ellipse",
+ "translate"};
+ for(i = 0; i < shape.len; ++i) {
+ Path2d *p = &shape.paths[i];
+ if(c != p->color) {
+ fprintf(f, "%d setcolor\n", p->color);
+ c = p->color;
+ }
+ for(j = 0; j < p->len; ++j)
+ fprintf(f, "%d,%d ", p->points[j].x, p->points[j].y);
+ fprintf(f, "%s\n", names[(int)p->type]);
+ }
+ fclose(f);
+ d->unsaved = 0;
+ scpy(name, d->name, 256);
+ fclose(f);
+ printf("Saved: %s\n", d->name);
+ redraw(pixels);
+ return 1;
+}
+
+int
+opendoc(Document *d, char *name)
+{
+ FILE *f = fopen(name, "r");
+ if(!f)
+ return error("Load", "Invalid input file");
+ fread(doc.data, sizeof(doc.data), 1, f);
+ d->unsaved = 0;
+ scpy(name, doc.name, 256);
+ fclose(f);
+ printf("Loaded: %s\n", doc.name);
+ redraw(pixels);
+ return 1;
+}
+
+void
+putchr(Uint8 *chrbuf, int row, int col, int color)
+{
+ if(row < 0 || row > SZ - 8)
+ return;
+ if(color == 0 || color == 2)
+ chrbuf[row] &= ~(1UL << (7 - col));
+ else
+ chrbuf[row] |= 1UL << (7 - col);
+ if(color == 0 || color == 1)
+ chrbuf[row + 8] &= ~(1UL << (7 - col));
+ else
+ chrbuf[row + 8] |= 1UL << (7 - col);
+}
+
+int
+savechr(void)
+{
+ int h, v, x, y;
+ Uint8 chrbuf[SZ];
+ FILE *f = fopen("dotgrid-export.chr", "wb");
+ for(v = 0; v < VER; ++v)
+ for(h = 0; h < HOR; ++h)
+ for(y = 0; y < 8; ++y)
+ for(x = 0; x < 8; ++x)
+ putchr(chrbuf,
+ y + (h + v * HOR) * 16,
+ x,
+ colortheme(getpixel(pixels, h * 8 + x, v * 8 + y)));
+ if(!fwrite(chrbuf, sizeof(chrbuf), 1, f))
+ error("Save", "Invalid output file");
+ fclose(f);
+ puts("Exported dotgrid-export.chr");
+ return 1;
+}
+
+void
modzoom(int mod)
{
if((mod > 0 && ZOOM < 4) || (mod < 0 && ZOOM > 1)) {
@@ 631,93 730,6 @@ setcolor(int c)
}
void
-setguides(int v)
-{
- GUIDES = v;
- redraw(pixels);
-}
-
-void
-renderbmp(void)
-{
- SDL_Surface *surface = SDL_GetWindowSurface(gWindow);
- setguides(0);
- SDL_RenderReadPixels(gRenderer,
- NULL,
- SDL_PIXELFORMAT_ARGB8888,
- surface->pixels,
- surface->pitch);
- if(SDL_SaveBMP(surface, "dotgrid-render.bmp"))
- printf("SDL_SaveBMP failed: %s\n", SDL_GetError());
- else
- puts("Rendered dotgrid-render.bmp");
- SDL_FreeSurface(surface);
-}
-
-void
-putchr(Uint8 *chrbuf, int row, int col, int color)
-{
- if(row < 0 || row > SZ - 8)
- return;
- if(color == 0 || color == 2)
- chrbuf[row] &= ~(1UL << (7 - col));
- else
- chrbuf[row] |= 1UL << (7 - col);
- if(color == 0 || color == 1)
- chrbuf[row + 8] &= ~(1UL << (7 - col));
- else
- chrbuf[row + 8] |= 1UL << (7 - col);
-}
-
-void
-exportchr(void)
-{
- int h, v, x, y;
- Uint8 chrbuf[SZ];
- FILE *f = fopen("dotgrid-export.chr", "wb");
- setguides(0);
- for(v = 0; v < VER; ++v)
- for(h = 0; h < HOR; ++h)
- for(y = 0; y < 8; ++y)
- for(x = 0; x < 8; ++x)
- putchr(chrbuf,
- y + (h + v * HOR) * 16,
- x,
- colortheme(getpixel(pixels, h * 8 + x, v * 8 + y)));
- if(!fwrite(chrbuf, sizeof(chrbuf), 1, f))
- error("Save", "Invalid output file");
- fclose(f);
- puts("Exported dotgrid-export.chr");
-}
-
-void
-savetxt(void)
-{
- int i, j, c = -1;
- FILE *f = fopen("dotgrid-shape.txt", "w");
- char *names[] = {
- "point",
- "line",
- "arc",
- "bezier",
- "rectangle",
- "ellipse",
- "translate"};
- for(i = 0; i < shape.len; ++i) {
- Path2d *p = &shape.paths[i];
- if(c != p->color) {
- fprintf(f, "%d setcolor\n", p->color);
- c = p->color;
- }
- for(j = 0; j < p->len; ++j)
- fprintf(f, "%d,%d ", p->points[j].x, p->points[j].y);
- fprintf(f, "%s\n", names[(int)p->type]);
- }
- fclose(f);
- puts("Saved dotgrid-shape.txt");
-}
-
-void
loadtxt(FILE *f)
{
char line[256], query[256];
@@ 766,23 778,6 @@ loadtxt(FILE *f)
}
void
-loadtheme(FILE *f)
-{
- int id = 0;
- char line[256];
- if(!f)
- return;
- while(fgets(line, 256, f)) {
- int i = 0;
- while(line[i++] && id < 5) {
- if(line[i] == '#')
- theme[id++] = shex(line + i + 1, 6);
- }
- }
- fclose(f);
-}
-
-void
selectoption(int option)
{
switch(option) {
@@ 795,7 790,7 @@ selectoption(int option)
case 7: cast(RECTANGLE); break;
case 8: cast(ELLIPSE); break;
case 10: cast(TRANSLATE); break;
- case 11: setguides(!GUIDES); break;
+ case 11: savemode(&GUIDES, !GUIDES); break;
}
}
@@ 813,13 808,15 @@ quit(void)
exit(0);
}
+#pragma mark - Triggers
+
void
domouse(SDL_Event *event)
{
Point2d magnet;
Point2d touch = Pt2d(
- (event->motion.x - (PAD * ZOOM)) / ZOOM,
- (event->motion.y - (PAD * ZOOM)) / ZOOM);
+ (event->motion.x - (PAD * 8 * ZOOM)) / ZOOM,
+ (event->motion.y - (PAD * 8 * ZOOM)) / ZOOM);
mag2d(setpt2d(&magnet, touch.x, touch.y), 8);
switch(event->type) {
case SDL_MOUSEBUTTONUP:
@@ 832,8 829,8 @@ domouse(SDL_Event *event)
brush.touch = NULL;
break;
case SDL_MOUSEBUTTONDOWN:
- if(event->motion.y / ZOOM / 8 == VER + 2)
- selectoption(event->motion.x / ZOOM / 8 - 1);
+ if(event->motion.y / ZOOM / 8 - PAD == VER + 1)
+ selectoption(event->motion.x / ZOOM / 8 - PAD);
else
selectpoint(&magnet);
break;
@@ 847,28 844,35 @@ domouse(SDL_Event *event)
void
dokey(SDL_Event *event)
{
- switch(event->key.keysym.sym) {
- case SDLK_EQUALS:
- case SDLK_PLUS: modzoom(1); break;
- case SDLK_UNDERSCORE:
- case SDLK_MINUS: modzoom(-1); break;
- case SDLK_ESCAPE: cancel(); break;
- case SDLK_BACKSPACE: erase(); break;
- case SDLK_1: setcolor(1); break;
- case SDLK_2: setcolor(2); break;
- case SDLK_3: setcolor(3); break;
- case SDLK_e: exportchr(); break;
- case SDLK_r: renderbmp(); break;
- case SDLK_t: savetxt(); break;
- case SDLK_a: cast(LINE); break;
- case SDLK_s: cast(ARC); break;
- case SDLK_d: cast(BEZIER); break;
- case SDLK_h: setguides(!GUIDES); break;
- case SDLK_z: cast(POINT); break;
- case SDLK_x: cast(RECTANGLE); break;
- case SDLK_c: cast(ELLIPSE); break;
- case SDLK_v: cast(TRANSLATE); break;
- case SDLK_n: destroy(); break;
+ int shift = SDL_GetModState() & KMOD_LSHIFT || SDL_GetModState() & KMOD_RSHIFT;
+ int ctrl = SDL_GetModState() & KMOD_LCTRL || SDL_GetModState() & KMOD_RCTRL;
+ if(ctrl) {
+ switch(event->key.keysym.sym) {
+ /* Generic */
+ case SDLK_n: makedoc(&doc, "untitled.chr"); break;
+ case SDLK_r: opendoc(&doc, doc.name); break;
+ case SDLK_s: shift ? savechr() : savedoc(&doc, doc.name); break;
+ case SDLK_h: savemode(&GUIDES, !GUIDES); break;
+ }
+ } else {
+ switch(event->key.keysym.sym) {
+ case SDLK_EQUALS:
+ case SDLK_PLUS: modzoom(1); break;
+ case SDLK_UNDERSCORE:
+ case SDLK_MINUS: modzoom(-1); break;
+ case SDLK_ESCAPE: cancel(); break;
+ case SDLK_BACKSPACE: erase(); break;
+ case SDLK_1: setcolor(1); break;
+ case SDLK_2: setcolor(2); break;
+ case SDLK_3: setcolor(3); break;
+ case SDLK_a: cast(LINE); break;
+ case SDLK_s: cast(ARC); break;
+ case SDLK_d: cast(BEZIER); break;
+ case SDLK_z: cast(POINT); break;
+ case SDLK_x: cast(RECTANGLE); break;
+ case SDLK_c: cast(ELLIPSE); break;
+ case SDLK_v: cast(TRANSLATE); break;
+ }
}
}
@@ 908,7 912,6 @@ main(int argc, char *argv[])
int ticknext = 0;
if(!init())
return error("Init", "Failure");
- loadtheme(fopen("theme.svg", "r"));
if(argc > 1)
loadtxt(fopen(argv[1], "r"));
cancel();