/* Carve up an image into letterforms to make a texture font. The idea here is that you draw the font on a sheet of paper, scan or photograph it, convert it to PNG, and then bring it into this program to carve it up. Grievously missing pieces: - Zoom, and maybe some kind of scrolling pangram display?, to handle images much larger than 1024×1024 - Rotation to handle images or parts of images that are rotated - Baseline adjustment and baseline alignment (so different glyphs can have different descents, or in TeX’s terminology, depths) - Slight adjustment of letter bounding boxes, as opposed to redrawing them from scratch - Some kind of softening or even erasing of the background Less fatal things that are missing but would be super nice: - Handle drawing boxes backwards (e.g., upwards or leftwards or both) - Start out new boxes at the same size as the previous box - Pangram choice; maybe just appending typed characters to the pangram if not present, and supporting backspace, would be adequate? - Maybe a less garish highlight rectangle - Timeouts on xshmu_wait */ #include #include #include #include #include #include #include #include "xshmu.h" xshmu_pic read_png(char *filename); /* from vpng.c */ char *pangram = "WALTZ, NYMPH; FOR QUICK-JIGS VEX BUD. " "the quick brown fox: jump'd over the lazy @dog!? " "2+2 = 4 /\\ (3*4) < 15 # { 7 < (5 & 6 ^ 8) > 9; ~s | 0 } \"[^_^]\" $%`"; void check_pangram() { int chars[256] = {0}; for (char *p = pangram; *p; p++) chars[(int)(unsigned char)*p] = 1; for (int i = ' '; i <= '~'; i++) { if (!chars[i]) printf("pangram missing '%c'\n", i); } } struct asciifont { xshmu_pic img; int max_height; struct { int x, y, w, h; } g[256]; }; void write_asciifont(struct asciifont *font, char *filename) { char namebuf[512]; strcpy(namebuf, filename); strcat(namebuf, ".tmp"); FILE *out = fopen(namebuf, "w"); for (int i = 0; i < 256; i++) { fprintf(out, "%d %d %d %d %d\n", i, font->g[i].x, font->g[i].y, font->g[i].w, font->g[i].h); } fflush(out); fsync(fileno(out)); fclose(out); rename(namebuf, filename); } void update_height(struct asciifont *font, int c, int new_height) { int old_height = font->g[c].h; font->g[c].h = new_height; if (new_height >= font->max_height) { font->max_height = new_height; return; } if (old_height < font->max_height && new_height < font->max_height) return; font->max_height = 0; for (int i = 0; i < 256; i++) { if (font->g[i].h > font->max_height) font->max_height = font->g[i].h; } } void read_asciifont(struct asciifont *font, char *filename) { FILE *in = fopen(filename, "r"); if (!in && errno == ENOENT) return; font->max_height = 0; for (int i = 0; i < 256; i++) { int j, h; fscanf(in, "%d %d %d %d %d\n", &j, &font->g[i].x, &font->g[i].y, &font->g[i].w, &h); if (j != i) { fprintf(stderr, "Uhoh, expected character %d ('%c') but got %d\n", i, i, j); } update_height(font, i, h); } fclose(in); } xshmu_pic glyph(struct asciifont *font, int c) { return xshmu_subpic(font->img, font->g[c].x, font->g[c].y, font->g[c].w, font->g[c].h); } void show(struct asciifont *font, char *s, xshmu_pic where) { int x = 0, y = 0; for (; *s; s++) { xshmu_pic g = glyph(font, *s); if (x + g.w > where.w) { x = 0; y += font->max_height; } if (x + g.w > where.w) return; /* Glyph is too wide to fit */ if (y > where.h) return; xshmu_copy(xshmu_subpic(where, x, y + font->max_height - g.h, g.w, g.h), g); x += g.w; } } int main(int argc, char **argv) { if (argc == 1) { fprintf(stderr, "usage: %s letters.png\n", argv[0]); return 1; } check_pangram(); xshmu_pic img = read_png(argv[1]); if (!img.p) return 1; /* XXX this is not a good way to report errors */ xshmu w = xshmu_open(argv[1], 1024, 1024, ""); struct asciifont font = {img}; char fontfile[256]; strcpy(fontfile, argv[1]); strcat(fontfile, ".glyphs"); read_asciifont(&font, fontfile); int current_char = 'A', buttons = 0, display_text = 1, xoff = 0, yoff = 0; for (;;) { for (xshmu_event *ev; (ev = xshmu_get_event(w));) { if (xshmu_as_die_event(ev)) { xshmu_close(w); free(img.p); img.p = 0; return 0; } xshmu_key_event *kev = xshmu_as_key_event(ev); if (kev && kev->down) { if (kev->keysym <= '~') current_char = kev->keysym; if (kev->keysym == xks_shift_l) display_text = 0; if (kev->keysym == xks_left && xoff) xoff -= 128; if (kev->keysym == xks_right) xoff += 128; if (kev->keysym == xks_up && yoff) yoff -= 128; if (kev->keysym == xks_down) yoff += 128; } if (kev && !kev->down && kev->keysym == xks_shift_l) display_text = 1; xshmu_mouse_event *mev = xshmu_as_mouse_event(ev); if (mev) { int x = mev->x + xoff, y = mev->y + yoff; if ((mev->buttons & 1) && !(buttons & 1)) { font.g[current_char].x = x; font.g[current_char].y = y; font.g[current_char].w = 0; update_height(&font, current_char, 0); } else if (mev->buttons & 1) { int dx = x - font.g[current_char].x, dy = y - font.g[current_char].y; if (dx >= 0) font.g[current_char].w = dx; if (dy >= 0) update_height(&font, current_char, dy); } else if ((buttons & 1) && !(mev->buttons & 1)) { write_asciifont(&font, fontfile); } buttons = mev->buttons; } } xshmu_pic fb = xshmu_framebuffer(w); xshmu_copy(fb, xshmu_subpic(img, xoff, yoff, img.w - xoff, img.h - yoff)); if (display_text) show(&font, pangram, fb); struct timeval tv; gettimeofday(&tv, 0); if (tv.tv_usec & 65536) { for (int x = 0; x < font.g[current_char].w; x++) { for (int y = 0; y < font.g[current_char].h; y++) { int rx = x + font.g[current_char].x - xoff, ry = y + font.g[current_char].y - yoff; if (rx < 0 || ry < 0 || rx >= fb.w || ry >= fb.h) continue; *xshmu_pix(fb, rx, ry) = tv.tv_usec; } } } xshmu_flush(w); usleep(50000); /* XXX xshmu_wait */ } }