1 /* edit.c - Bitmap file manipulation and editing
3 * Copyright 2002-2004 John Coffman
4 * Copyright 2009-2011 Joachim Wiedorn
7 * Licensed under the terms contained in the file 'COPYING'
8 * in the source directory.
13 #include <sys/types.h>
14 #include <sys/statfs.h>
25 #define _I386_STATFS_H /* two versions of statfs is not good ... */
38 #define USE_BSECT_PW_INPUT 0 /* use another's input routine */
39 #define BMP_CONF ".dat"
40 #define BMP_BMP ".bmp"
43 /* Luminance of a color 0..255 -- YIQ color model */
44 #define Y(c) (((c).red*0.30+(c).green*0.59+(c).blue*0.11)/255)
45 #define NORM(x) ((float)(x)/255.0)
47 static BITMAPFILEHEADER fh;
48 static BITMAPHEADER bmh;
49 static RGB palette[NPALETTE];
50 static int filepos, npal;
52 static BITMAPLILOHEADER lh0 = {
53 {sizeof(BITMAPLILOHEADER), 0}, "LILO",
54 5*16, 12*8, 1, /* row, col, ncol */
55 25, (MAX_IMAGE_NAME+1)*8, /* maxcol, xpitch */
56 7, 7, 7, /* normal text fg, bg, shadow */
57 15, 15, 15, /* highlight fg, bg, shadow */
58 7, 0, 7, /* timer text fg, bg, shadow */
59 2*16, 64*8, /* timer row, col */
60 4, {0, 0, 0} /* mincol, reserved[3] */
65 #if USE_BSECT_PW_INPUT
66 #define getLine pw_input
68 static char *getLine(void)
71 char buf[MAX_TOKEN+1];
76 while((ch=getchar())!='\n') if (i<MAX_TOKEN) buf[i++]=ch;
79 /* while (i) buf[--i]=0; */
85 int get_std_headers(int fd,
94 lseek(fd, 0, SEEK_SET);
95 if (read(fd, (void*)fh, sizeof(BITMAPFILEHEADER)) !=
96 sizeof(BITMAPFILEHEADER) ) return -1;
97 if (fh->magic != 0x4D42 /* "BM" */) return 1;
98 if (read(fd, &size, sizeof(size)) != sizeof(size)) return -1;
99 if (size==sizeof(BITMAPHEADER2)) { /* an OS/2 bitmap */
100 if (read(fd, (char*)&bmh2+sizeof(size), sizeof(BITMAPHEADER2)-sizeof(size))
101 != sizeof(BITMAPHEADER2)-sizeof(size) ) return -1;
102 memset(bmh, 0, sizeof(BITMAPHEADER));
103 bmh->width = bmh2.width;
104 bmh->height = bmh2.height;
105 n = bmh->numBitPlanes = bmh2.numBitPlanes;
106 n *= bmh->numBitsPerPlane = bmh2.numBitsPerPlane;
107 bmh->numColorsUsed = bmh->numImportantColors = 1 << n;
108 bmh->sizeImageData = *(int*)(fh->size) - *(int*)(fh->offsetToBits);
109 bmh->size = sizeof(*bmh); /* new size!! */
112 else if (size==sizeof(BITMAPHEADER)) {
113 if (read(fd, (char*)bmh+sizeof(size), sizeof(BITMAPHEADER)-sizeof(size))
114 != sizeof(BITMAPHEADER)-sizeof(size) ) return -1;
121 npal = 1 << (bmh->numBitPlanes * bmh->numBitsPerPlane);
123 if (npal > nelem(palette) ) return 3;
124 for (i=0; i<npal; i++) {
125 if (read(fd, &palette[i], n) != n) return -1;
126 if (n==sizeof(RGB2)) palette[i].null = 0;
128 if (*(int*)(fh->offsetToBits) == sizeof(BITMAPFILEHEADER) +
129 sizeof(BITMAPHEADER) + sizeof(BITMAPLILOHEADER) +
130 npal*sizeof(RGB) ) /* test will fail for OS/2 bitmaps */ {
131 /* get probable BITMAPLILOHEADER */
132 if (read(fd, &size, sizeof(size)) != sizeof(size)) return -1;
133 if (size != sizeof(BITMAPLILOHEADER)) return 4;
134 if (read(fd, (char*)lh+sizeof(size), sizeof(*lh)-sizeof(size)) !=
135 sizeof(*lh)-sizeof(size)) return -1;
136 *(int*)(lh->size) = size;
137 if (strncmp(lh->magic, "LILO", 4) != 0) return 5;
138 } else { /* there is no BITMAPLILOHEADER present */
140 printf("No BITMAPLILOHEADER\n");
144 /* file is left positioned at the start of the bitmap data */
145 filepos = lseek(fd, 0, SEEK_CUR);
150 int put_std_bmpfile(int fd, int ifd,
151 BITMAPFILEHEADER *fh,
153 BITMAPLILOHEADER *lh)
155 int n, total, npalette;
158 npalette = 1 << (bmh->numBitPlanes * bmh->numBitsPerPlane);
159 write(fd, fh, sizeof(*fh));
160 write(fd, bmh, sizeof(*bmh));
161 write(fd, palette, npalette*sizeof(palette[0]));
162 write(fd, lh, sizeof(*lh));
164 lseek(ifd, filepos, SEEK_SET);
166 n = read(ifd, buf, sizeof(buf));
168 if (write(fd, buf, n) != n) return -1;
171 else if (n<0) printf("Error reading input\n");
173 bmh->sizeImageData = total;
174 *(int*)(fh->offsetToBits) = n = sizeof(BITMAPFILEHEADER) +
175 sizeof(BITMAPHEADER) + sizeof(BITMAPLILOHEADER) +
176 npalette*sizeof(RGB);
177 *(int*)(fh->size) = total + n;
178 lseek(fd, 0, SEEK_SET);
179 write(fd, fh, sizeof(*fh));
180 write(fd, bmh, sizeof(*bmh));
187 static char *temp_file, *bitmap_file;
190 unsigned char buffer[256];
192 BITMAPLILOHEADER bmlh;
194 static MENUTABLE *menu = &tm.mt;
195 static BITMAPLILOHEADER *lh = (void*)(tm.buffer +
196 ((long)&tm.mt.row - (long)&tm.bmlh.row));
198 /* a convenience definition */
201 /* timer = 1 if timer is enabled, 0 if timer is disabled */
202 #define timer (mn.t_row>=0)
204 static int yesno(char *query, int def)
210 printf("%s (yes or no) [%c]: ", query, def?'Y':'N');
213 else if (toupper(*yn) == 'Y') ans = 1;
214 else if (toupper(*yn) == 'N') ans = 0;
221 static void dat_file_creat(char *bmp)
226 datfile = stralloc(bmp);
227 *strrchr(datfile,*(BMP_BMP)) = 0;
228 strcat(datfile, BMP_CONF);
229 if (!(fdat = fopen(datfile, "w"))) pdie("Open .dat file");
231 fprintf(fdat,"#\n# generated companion file to:\n#\n");
232 fprintf(fdat,"bitmap = %s\n", bmp);
234 fprintf(fdat,"bmp-table = %d%s,%d%s;%d,%d,%d%s,%d\n",
235 mn.col%8 ? mn.col : mn.col/8+1,
237 mn.row%16 ? mn.row : mn.row/16+1,
238 mn.row%16 ? "p" : "",
241 mn.xpitch%8 ? mn.xpitch : mn.xpitch/8,
242 mn.xpitch%8 ? "p" : "",
245 fprintf(fdat,"bmp-colors = %d,", mn.fg);
246 if (mn.bg != mn.fg) fprintf(fdat,"%d",mn.bg);
248 if (mn.sh != mn.fg) fprintf(fdat,"%d",mn.sh);
250 fprintf(fdat,"%d,", mn.h_fg);
251 if (mn.h_bg != mn.h_fg) fprintf(fdat,"%d",mn.h_bg);
253 if (mn.h_sh != mn.h_fg) fprintf(fdat,"%d",mn.h_sh);
256 fprintf(fdat,"bmp-timer = ");
257 if (mn.t_row < 0) fprintf(fdat,"none\n");
259 fprintf(fdat,"%d%s,%d%s;%d,",
260 mn.t_col%8 ? mn.t_col : mn.t_col/8+1,
261 mn.t_col%8 ? "p" : "",
262 mn.t_row%16 ? mn.t_row : mn.t_row/16+1,
263 mn.t_row%16 ? "p" : "",
265 if (mn.t_bg != mn.t_fg) fprintf(fdat,"%d", mn.t_bg);
267 if (mn.t_sh != mn.t_fg) fprintf(fdat,"%d", mn.t_sh);
273 static void bmp_file_open(char *bmp)
278 temp_file = strcat(strcpy(alloc(strlen(bitmap_file)+strlen(MAP_TMP_APP)+1),
281 ifd = open(bitmap_file, O_RDONLY);
282 if (ifd<0) pdie("Cannot open bitmap file");
283 ofd = open(temp_file, O_CREAT|O_WRONLY, 0644);
284 if (ofd<0) pdie("Cannot open temporary file");
285 temp_register(temp_file);
287 n = get_std_headers(ifd, &fh, &bmh, lh);
288 if (verbose >= 3) printf("get_std_headers: returns %d\n", n);
290 if (n<0) die("read file '%s': %s", bitmap_file, strerror(errno));
294 die("Not a bitmap file '%s'", bitmap_file);
296 die("Unsupported bitmap file '%s' (%d bit color)", bitmap_file,
297 bmh.numBitPlanes*bmh.numBitsPerPlane);
300 die("Unrecognized auxiliary header in file '%s'", bitmap_file);
307 static void bmp_file_close(int update)
311 if (update) n = put_std_bmpfile(ofd, ifd, &fh, &bmh, lh);
315 temp_unregister(temp_file);
316 if (!update || test) {
317 if (verbose < 9) remove(temp_file);
319 n = rename(temp_file, bitmap_file);
324 static void location(char *what, short x, short y)
326 printf("%sColumn(X): %d%s (chars) or %hdp (pixels)", what, x/8+1, x%8?"+":"", x);
327 printf(" Row(Y): %d%s (chars) or %hdp (pixels)\n", y/16+1, y%16?"+":"", y);
331 static void color(char *what, short fg, short bg, short sh)
333 static char sp[] = " ";
335 printf("%sForeground: %hd%sBackground: ", what, fg, sp);
336 if (bg==fg) printf("transparent%s",sp);
337 else printf("%hd%s", bg, sp);
339 if (sh==fg) printf("none\n");
340 else printf("%hd\n", sh);
344 static void get3colors(char *what, short *color)
346 static char *co[] = { "fg", "bg", "sh" };
347 static char *op[] = { "", ",transparent", ",none" };
351 char n[4], *line, *end;
354 for (i=0; i<3; i++) dcol[i] = color[i]; /* save inputs */
355 tr = (dcol[0] == dcol[1]);
356 no = (dcol[0] == dcol[2]);
359 for (i=0; i<3; i++) {
360 sprintf(n, "%hd", dcol[i]);
361 printf("%s text %s color (0..%d%s) [%s]: ", what, co[i], npal-1, op[i],
362 i==1 && tr ? "transparent" :
363 i==2 && no ? "none" : n);
365 if (!*line) c = dcol[i];
366 else if (toupper(*line)=='T' && i==1) c = color[0];
367 else if (toupper(*line)=='N' && i==2) c = color[0];
369 c = strtol(line, &end, 0);
370 if (line==end || c>=npal || *end) {
385 static void number(char *what, short *num, int min, int max)
390 printf("%s (%d..%d) [%hd]: ", what, min, max, *num);
392 if (!*line) val = *num;
394 val = strtol(line, &end, 0);
395 if (val < min || val > max || *end) {
405 static void getXY(char *what, short *locp, int scale, int abs)
409 int min = abs ? 1 : MAX_IMAGE_NAME;
410 int minp = min*scale;
411 int max = scale==8 ? 80 : 30;
412 int maxp = (max-abs)*scale;
413 int loc = *locp/scale + abs;
414 char *plus = *locp%scale ? "+" : "";
416 printf("%s (%d..%d) or (%dp..%dp) [%d%s or %dp]: ", what,
417 min, max, minp, maxp, loc, plus, (int)*locp);
420 if (!*line) val = *locp;
422 val = strtol(line, &end, 0);
423 if (line==end || (*end && toupper(*end)!='P')) {
427 if (toupper(*end)!='P') val = (val-1)*scale;
438 static void show_timer(void)
441 color(" Timer: ", mn.t_fg, mn.t_bg, mn.t_sh);
442 location("Timer position:\n ", mn.t_col, mn.t_row);
446 printf("\n\tThe timer is DISABLED.\n");
451 static void show_colors(int timopt)
453 color(" Normal: ", mn.fg, mn.bg, mn.sh);
454 color("Highlight: ", mn.h_fg, mn.h_bg, mn.h_sh);
456 color(" Timer: ", mn.t_fg, mn.t_bg, mn.t_sh);
459 static void show_layout(void)
461 printf("\nTable dimensions:\n");
462 printf(" Number of columns: %hd\n", mn.ncol);
463 printf(" Entries per column (number of rows): %hd\n", mn.maxcol);
465 printf( " Column pitch (X-spacing from character 1 of one column to character 1\n"
466 " of the next column): %d%s (chars) %hdp (pixels)\n", mn.xpitch/8,
467 mn.xpitch%8 ? "+" : "", mn.xpitch);
468 printf( " Spill threshold (number of entries filled-in in the first column\n"
469 " before entries are made in the second column): %hd\n", mn.mincol);
471 location("Table upper left corner:\n ", mn.col, mn.row);
475 static void edit_timer(void)
481 if (timer) printf("\nTimer colors:\n");
483 printf("\nTimer setup: ");
485 if (timer) printf("C)olors, P)osition, D)isable");
486 else printf("E)nable");
492 if (timer) switch(toupper(*cmd)) {
494 get3colors("Timer", &mn.t_fg);
502 getXY("\nTimer col", &mn.t_col, 8, 1);
503 getXY("Timer row", &mn.t_row, 16, 1);
511 else switch(toupper(*cmd)) {
529 static void edit_layout(void)
537 printf("\nLayout options: D)imensions, P)osition, B)ack: ");
540 switch(toupper(*cmd)) {
542 number("\nNumber of columns", &mn.ncol, 1, 80/MAX_IMAGE_NAME);
543 number("Entries per column", &mn.maxcol, 1, 30);
545 getXY("Column pitch", &mn.xpitch, 8, 0);
546 number("Spill threshold", &mn.mincol, 1, mn.maxcol);
550 getXY("\nTable UL column", &mn.col, 8, 1);
551 getXY("Table UL row", &mn.row, 16, 1);
565 static void edit_colors(void)
574 printf("\nText color options: N)ormal, H)ighlight, ");
575 if (timer) printf("T)imer, ");
579 switch(toupper(*cmd)) {
581 get3colors("Normal text", &mn.fg);
584 get3colors("Highlight text", &mn.h_fg);
587 if (timer) get3colors("Timer text", &mn.t_fg);
603 static void edit_bitmap(char *bitmap_file)
608 printf("Editing contents of bitmap file: %s\n", bitmap_file);
610 bmp_file_open(bitmap_file);
614 printf("\nText colors:\n");
618 printf("\nCommands are: L)ayout, C)olors, T)imer, Q)uit, W)rite: ");
620 switch(toupper(*cmd)) {
631 if (yesno("Save companion configuration file?", 0))
632 dat_file_creat(bitmap_file);
633 editing = !yesno("Save changes to bitmap file?", 0);
635 printf("Writing output file: %s\n", bitmap_file);
636 bmp_file_close(!test); /* update */
637 if (test) printf("***The bitmap file has not been changed***\n");
641 editing = !yesno("Abandon changes?", 0);
642 if (!editing) bmp_file_close(0); /* no update */
654 static void transfer_params(char *config_file)
657 char *bitmap_file, *opt;
661 cfg_bitmap_only(); /* disable everything but cf_bitmap */
663 cfd = cfg_open(config_file);
664 if (verbose >= 3) printf("cfg_open returns: %d\n", cfd);
665 n = cfg_parse(cf_bitmap);
666 if (verbose >= 3) printf("cfg_parse returns: %d\n", n);
668 die("Illegal token in '%s'", config_file);
670 if ((bitmap_file = cfg_get_strg(cf_bitmap, "bitmap")) != NULL) {
672 cp = strrchr(config_file, '/');
673 if (cp && bitmap_file[0] != '/') {
675 bitmap_file = strcat(strcpy(alloc(strlen(config_file) + strlen(bitmap_file) + 1),
682 cp = strrchr(config_file, '.');
684 bitmap_file = alloc(strlen(config_file) + strlen(BMP_BMP) + 1);
685 strcpy(bitmap_file, config_file);
686 strcat(bitmap_file, BMP_BMP);
690 printf("Transfer parameters from '%s' to '%s'", config_file, bitmap_file);
691 if (yesno("?", 0)==0) exit(0);
693 if (verbose > 0) printf("%s bitmap file: %s\n", opt, bitmap_file);
695 bmp_file_open(bitmap_file);
697 bmp_do_table(cfg_get_strg(cf_bitmap, "bmp-table"), menu);
698 bmp_do_colors(cfg_get_strg(cf_bitmap, "bmp-colors"), menu);
699 bmp_do_timer(cfg_get_strg(cf_bitmap, "bmp-timer"), menu);
701 bmp_file_close(1); /* update */
707 void do_bitmap_edit(char *filename)
710 char *fn = strrchr(filename, *bmp);
713 die ("'%s'/'%s' filename extension required: %s", BMP_BMP, BMP_CONF, filename);
715 if (strcmp(fn, BMP_CONF)==0) transfer_params(filename);
716 if (strcmp(fn, BMP_BMP)==0) edit_bitmap(filename);
718 die("Unknown filename extension: %s", filename);
722 #else /* STANDALONE */
724 static RGB vga_palette[16] = {
726 { 000, 000, 000, 0 }, /* k -- black */
727 { 170, 000, 000, 0 }, /* b -- blue */
728 { 000, 170, 000, 0 }, /* g -- green */
729 { 170, 170, 000, 0 }, /* c -- cyan */
732 { 000, 000, 170, 0 }, /* r -- red */
733 { 170, 000, 170, 0 }, /* m -- magenta */
734 { 000, 85, 170, 0 }, /* y -- yellow (amber) */
735 { 170, 170, 170, 0 }, /* w -- white */
738 { 85, 85, 85, 0 }, /* K -- BLACK (dark grey) */
739 { 255, 000, 000, 0 }, /* B -- BLUE */
740 { 000, 255, 000, 0 }, /* G -- GREEN */
741 { 255, 255, 000, 0 }, /* C -- CYAN */
744 { 000, 000, 255, 0 }, /* R -- RED */
745 { 255, 000, 255, 0 }, /* M -- MAGENTA */
746 { 000, 255, 255, 0 }, /* Y -- YELLOW */
747 { 255, 255, 255, 0 } /* W -- WHITE */
751 static BITMAPLILOHEADER lh;
754 static float hue[NPALETTE], y_yiq[NPALETTE], s_hsv[NPALETTE],
755 s_hls[NPALETTE], v_hsv[NPALETTE], l_hls[NPALETTE];
757 void gsort(float array[])
762 for (j=n-1; j>0; j--) {
763 for (i=0; i<j; i++) {
764 if (array[idx[i]] > array[idx[i+1]]) {
773 #define MAX(a,b) (a>b?a:b)
774 #define MIN(a,b) (a<b?a:b)
776 static void compute_arrays(RGB pal[], int n)
779 float r, g, b, max, min, delta;
780 float l, h, mm, hsv, hls;
782 for (i=0; i<n; i++) {
784 r = NORM(pal[i].red);
785 g = NORM(pal[i].green);
786 b = NORM(pal[i].blue);
795 hsv = (max!=0.0 ? delta/max : 0.0);
800 hls = delta / ( (mm <= 1.0) ? mm: (2.0 - mm) );
801 h = r==max ? (g - b)/delta :
802 g==max ? (b - r)/delta + 2 :
807 /* compute the YIQ luminance [0..1] */
808 y_yiq[i] = r*0.3 + g*0.59 + b*0.11;
823 static const char name[] = "RYGCBM";
834 if (fabs(h)<0.1) { val[0]=name[i]; val[1]=0; }
835 else sprintf(val,"%c%+3.1f", name[i], h);
841 void printline(RGB pal[], int i)
843 /* R G B i Y V S(hsv) S(hls) L H */
844 printf("(%3d,%3d,%3d)%3d %6.3f %6.3f %6.3f %6.3f %6.3f %+6.1f %s\n",
845 pal[i].red, pal[i].green, pal[i].blue, i,
846 y_yiq[i], v_hsv[i], s_hsv[i], s_hls[i], l_hls[i], hue[i], Hue(i) );
850 void printpalette(RGB pal[], int n)
853 /* R G B i Y V S(hsv) S(hls) L H */
854 printf(" R G B i Y V S-hsv S-hls L H\n");
855 for (i=0; i<n; i++) printline(pal, idx[i]);
860 int main(int argc, char *argv[])
865 /* char *cc = "kbgcrmywKBGCRMYW"; */
869 printf("Input file not specified\n");
872 ifd = open(argv[1], O_RDONLY);
873 if (ifd<0) pdie("opening input file");
874 if (argc > 2) die("Too many arguments");
875 if (argc < 3) outname = "out.bmp";
876 else outname = argv[2];
878 compute_arrays(vga_palette, 16);
879 printf("\nVGA palette:\n\n\n");
880 printpalette(vga_palette, 16);
882 printf("\n\nVGA pallette by luminance\n\n\n");
883 printpalette(vga_palette, 16);
885 i = get_std_headers(ifd, &fh, &bmh, &lh);
887 printf("Error exit on GET: %d\n", i);
890 printf("\n\n\nContained palette:\n\n");
892 compute_arrays(palette, 16);
895 printpalette(palette, 16);
902 #endif /* STANDALONE */