7eb0de9e0828e5544ead035b192d9f3713d0250a
[ncurses.git] / test / picsmap.c
1 /****************************************************************************
2  * Copyright (c) 2017 Free Software Foundation, Inc.                        *
3  *                                                                          *
4  * Permission is hereby granted, free of charge, to any person obtaining a  *
5  * copy of this software and associated documentation files (the            *
6  * "Software"), to deal in the Software without restriction, including      *
7  * without limitation the rights to use, copy, modify, merge, publish,      *
8  * distribute, distribute with modifications, sublicense, and/or sell       *
9  * copies of the Software, and to permit persons to whom the Software is    *
10  * furnished to do so, subject to the following conditions:                 *
11  *                                                                          *
12  * The above copyright notice and this permission notice shall be included  *
13  * in all copies or substantial portions of the Software.                   *
14  *                                                                          *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
16  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
18  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
19  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
20  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
21  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
22  *                                                                          *
23  * Except as contained in this notice, the name(s) of the above copyright   *
24  * holders shall not be used in advertising or otherwise to promote the     *
25  * sale, use or other dealings in this Software without prior written       *
26  * authorization.                                                           *
27  ****************************************************************************/
28 /*
29  * $Id: picsmap.c,v 1.8 2017/05/14 01:26:30 tom Exp $
30  *
31  * Author: Thomas E. Dickey
32  *
33  * A little more interesting than "dots", read a simple image into memory and
34  * measure the time taken to paint it normally vs randomly.
35  *
36  * TODO read xpm file
37  * TODO read "convert" via pipe (from ImageMagick)
38  * TODO write cells/second to stderr (or log)
39  * TODO write picture left-to-right/top-to-bottom
40  * TODO write picture randomly
41  * TODO add one-shot option vs repeat-count before exiting
42  * TODO add option for assumed palette of terminal
43  * TODO add option for init_color
44  * TODO use pad to allow pictures larger than screen
45  */
46 #include <test.priv.h>
47
48 #include <sys/types.h>
49 #include <sys/stat.h>
50
51 #define  L_CURL '{'
52 #define  R_CURL '}'
53
54 typedef struct {
55     int ch;                     /* nominal character to display */
56     int fg;                     /* foreground color */
57 } PICS_CELL;
58
59 typedef struct {
60     int fg;
61     int bg;
62 } PICS_PAIR;
63
64 typedef struct {
65     char *name;
66     int high;
67     int wide;
68     int colors;
69     PICS_PAIR *pairs;
70     PICS_CELL *cells;
71 } PICS_HEAD;
72
73 static bool in_curses = FALSE;
74
75 /*
76  * Simplify reading xbm/xpm files by first making an array of lines.  Blank
77  * lines are filtered out.
78  */
79 static char **
80 read_file(const char *filename)
81 {
82     char **result = 0;
83     struct stat sb;
84
85     printf("** %s\n", filename);
86     if (stat(filename, &sb) == 0
87         && (sb.st_mode & S_IFMT) == S_IFREG
88         && sb.st_size != 0) {
89         size_t size = (size_t) sb.st_size;
90         char *blob = typeMalloc(char, size + 1);
91         bool had_line = TRUE;
92         unsigned j;
93         unsigned k = 0;
94
95         result = typeCalloc(char *, size + 1);
96         if (blob != 0 && result != 0) {
97             FILE *fp = fopen(filename, "r");
98             if (fp != 0) {
99                 if (fread(blob, sizeof(char), size, fp) == size) {
100                     for (j = 0; (size_t) j < size; ++j) {
101                         if (blob[j] == '\n') {
102                             blob[j] = '\0';
103                             had_line = TRUE;
104                         } else if (had_line) {
105                             had_line = FALSE;
106                             result[k++] = blob + j;
107                         }
108                     }
109                     result[k] = 0;
110                 }
111                 fclose(fp);
112             }
113         }
114         if (k == 0) {
115             free(blob);
116             free(result);
117             result = 0;
118         }
119     }
120     return result;
121 }
122
123 static void
124 usage(void)
125 {
126     static const char *msg[] =
127     {
128         "Usage: picsmap [xbm-file [...]]"
129     };
130     size_t n;
131
132     fflush(stdout);
133     for (n = 0; n < SIZEOF(msg); n++)
134         fprintf(stderr, "%s\n", msg[n]);
135     ExitProgram(EXIT_FAILURE);
136 }
137
138 static void
139 giveup(const char *fmt,...)
140 {
141     va_list ap;
142     if (in_curses)
143         endwin();
144     fflush(stdout);
145     va_start(ap, fmt);
146     vfprintf(stderr, fmt, ap);
147     fputc('\n', stderr);
148     va_end(ap);
149     usage();
150 }
151
152 static int
153 map_color(int value)
154 {
155     int r = (value & 0xff0000) >> 16;
156     int g = (value & 0x00ff00) >> 8;
157     int b = (value & 0x0000ff) >> 0;
158     /* TODO simple mapping into COLOR_BLACK .. COLOR_WHITE */
159     int result = ((r >= 128) << 2) + ((g >= 128) << 1) + (b >= 128);
160     return result;
161 }
162
163 static int
164 bytes_of(int value)
165 {
166     if (value & 7) {
167         value |= 7;
168         value++;
169     }
170     return value;
171 }
172
173 static PICS_HEAD *
174 parse_xbm(char **data)
175 {
176     int n;
177     int state = 0;
178     char buf[BUFSIZ];
179     int num;
180     char ch;
181     char *s;
182     char *t;
183     PICS_HEAD *result = typeCalloc(PICS_HEAD, 1);
184     size_t which = 0;
185     size_t cells = 0;
186
187     for (n = 0; data[n] != 0; ++n) {
188         if (strlen(s = data[n]) >= sizeof(buf) - 1)
189             continue;
190         switch (state) {
191         case 0:
192         case 1:
193         case 2:
194             if (sscanf(s, "#define %s %d%c", buf, &num, &ch) >= 2) {
195                 if ((t = strstr(buf, "_width")) != 0) {
196                     state |= 1;
197                     result->wide = bytes_of(num);
198                 } else if ((t = strstr(buf, "_height")) != 0) {
199                     state |= 2;
200                     result->high = num;
201                 }
202                 *t = '\0';
203                 if (result->name) {
204                     if (strcmp(result->name, buf)) {
205                         goto finish;
206                     }
207                 } else {
208                     result->name = strdup(buf);
209                 }
210             }
211             break;
212         case 3:
213             if (sscanf(s, "static char %[^_ ]_bits[]%c", buf, &ch) >= 1) {
214                 if (strcmp(result->name, buf)) {
215                     goto finish;
216                 }
217                 state = 4;
218                 cells = (size_t) (result->wide * result->high);
219                 result->cells = typeCalloc(PICS_CELL, cells);
220                 if ((s = strchr(s, L_CURL)) == 0)
221                     break;
222                 ++s;
223             } else {
224                 break;
225             }
226         case 4:
227             while (*s != '\0') {
228                 while (isspace(UChar(*s))) {
229                     ++s;
230                 }
231                 if (isdigit(UChar(*s))) {
232                     long value = strtol(s, &t, 0);
233                     int b;
234                     if (t != s || value > 255 || value < 0) {
235                         s = t;
236                     } else {
237                         state = -1;
238                         goto finish;
239                     }
240                     /* TODO: which order? */
241                     for (b = 0; b < 8; ++b) {
242                         if (((1L << b) & value) != 0) {
243                             result->cells[which].ch = '*';
244                             result->cells[which].fg = 1;
245                         } else {
246                             result->cells[which].ch = ' ';
247                             result->cells[which].fg = 0;
248                         }
249                         if (++which > cells) {
250                             state = -1;
251                             goto finish;
252                         }
253                     }
254                 }
255                 if (*s == R_CURL) {
256                     state = 5;
257                     goto finish;
258                 } else if (*s == ',') {
259                     ++s;
260                 }
261             }
262             break;
263         default:
264             break;
265         }
266     }
267   finish:
268     if (state < 4) {
269         if (result) {
270             free(result->pairs);
271             free(result->cells);
272             free(result);
273             result = 0;
274         }
275     } else {
276         result->colors = 2;
277         result->pairs = typeCalloc(PICS_PAIR, 2);
278         result->pairs[1].fg = 0xffffff;
279     }
280     return result;
281 }
282
283 static void
284 dump_picture(PICS_HEAD * pics)
285 {
286     int y, x;
287
288     printf("Name %s\n", pics->name);
289     printf("Size %dx%d\n", pics->high, pics->wide);
290     for (y = 0; y < pics->high; ++y) {
291         for (x = 0; x < pics->wide; ++x) {
292             putchar(pics->cells[y * pics->wide + x].ch);
293         }
294         putchar('\n');
295     }
296 }
297
298 static void
299 show_picture(PICS_HEAD * pics)
300 {
301     int y, x;
302     int n;
303
304     if (!in_curses) {
305         in_curses = TRUE;
306         initscr();
307         cbreak();
308         noecho();
309         if (has_colors())
310             start_color();
311     }
312     scrollok(stdscr, FALSE);
313     if (has_colors()) {
314         for (n = 0; n < pics->colors; ++n) {
315             init_pair((short) (n + 1),
316                       (short) map_color(pics->pairs[n].fg),
317                       COLOR_BLACK);
318         }
319         attrset(COLOR_PAIR(1));
320         erase();
321     }
322     for (y = 0; y < pics->high; ++y) {
323         if (y >= LINES)
324             break;
325         move(y, 0);
326         for (x = 0; x < pics->wide; ++x) {
327             if (x >= COLS)
328                 break;
329             n = (y * pics->wide + x);
330             attrset(COLOR_PAIR(pics->cells[n].fg + 1));
331             addch((chtype) pics->cells[n].ch);
332         }
333     }
334     mvgetch(0, 0);
335     endwin();
336 }
337
338 int
339 main(int argc, char *argv[])
340 {
341     int n;
342
343     if (argc > 1) {
344         for (n = 1; n < argc; ++n) {
345             char **data = read_file(argv[n]);
346             PICS_HEAD *pics;
347             if (data == 0) {
348                 giveup("cannot read \"%s\"", argv[n]);
349             } else if ((pics = parse_xbm(data)) == 0) {
350                 giveup("unexpected file-format for \"%s\"", argv[n]);
351             } else if (isatty(fileno(stdout))) {
352                 show_picture(pics);
353             } else {
354                 dump_picture(pics);
355             }
356         }
357     } else {
358         usage();
359     }
360     ExitProgram(EXIT_SUCCESS);
361 }