1 /****************************************************************************
2 * Copyright (c) 2017 Free Software Foundation, Inc. *
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: *
12 * The above copyright notice and this permission notice shall be included *
13 * in all copies or substantial portions of the Software. *
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. *
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 *
27 ****************************************************************************/
29 * $Id: picsmap.c,v 1.17 2017/05/21 00:22:22 tom Exp $
31 * Author: Thomas E. Dickey
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.
36 * TODO handle hex color-codes for xpm other than 3-bytes
37 * TODO read rgb.txt to handle xpm color names other than "None"
38 * TODO read "convert" via pipe (from ImageMagick)
39 * TODO write cells/second to stderr (or log)
40 * TODO write picture left-to-right/top-to-bottom
41 * TODO write picture randomly
42 * TODO add one-shot option vs repeat-count before exiting
43 * TODO add option for assumed palette of terminal
44 * TODO add option for init_color
45 * TODO use pad to allow pictures larger than screen
47 #include <test.priv.h>
49 #include <sys/types.h>
56 int ch; /* nominal character to display */
57 int fg; /* foreground color */
74 static bool in_curses = FALSE;
77 free_data(char **data)
84 free_pics_head(PICS_HEAD * pics)
92 * Simplify reading xbm/xpm files by first making an array of lines. Blank
93 * lines are filtered out.
96 read_file(const char *filename)
101 printf("** %s\n", filename);
102 if (stat(filename, &sb) == 0
103 && (sb.st_mode & S_IFMT) == S_IFREG
104 && sb.st_size != 0) {
105 size_t size = (size_t) sb.st_size;
106 char *blob = typeMalloc(char, size + 1);
107 bool had_line = TRUE;
111 result = typeCalloc(char *, size + 1);
112 if (blob != 0 && result != 0) {
113 FILE *fp = fopen(filename, "r");
115 if (fread(blob, sizeof(char), size, fp) == size) {
116 for (j = 0; (size_t) j < size; ++j) {
117 if (blob[j] == '\n') {
120 } else if (had_line) {
122 result[k++] = blob + j;
142 static const char *msg[] =
144 "Usage: picsmap [xbm-file [...]]"
149 for (n = 0; n < SIZEOF(msg); n++)
150 fprintf(stderr, "%s\n", msg[n]);
151 ExitProgram(EXIT_FAILURE);
155 giveup(const char *fmt,...)
162 vfprintf(stderr, fmt, ap);
171 int r = (value & 0xff0000) >> 16;
172 int g = (value & 0x00ff00) >> 8;
173 int b = (value & 0x0000ff) >> 0;
174 /* TODO simple mapping into COLOR_BLACK .. COLOR_WHITE */
175 int result = ((r >= 128) << 2) + ((g >= 128) << 1) + (b >= 128);
189 static int match_c(const char *, const char *,...) GCC_SCANFLIKE(2,3);
192 skip_s(const char *s)
194 while (isspace(UChar(*s)))
200 match_c(const char *source, const char *pattern,...)
202 const char *last_s = source + strlen(source);
209 va_start(ap, pattern);
211 while (*pattern != '\0') {
212 ch = UChar(*pattern++);
213 /* blank in the pattern matches zero-or-more blanks in source */
215 source = skip_s(source);
218 /* %c, %d, %s are like sscanf except for special treatment of blanks */
219 if (ch == '%' && *pattern != '\0' && strchr("cds", *pattern)) {
224 cp = va_arg(ap, char *);
228 ip = va_arg(ap, int *);
229 lv = strtol(source, &cp, 0);
230 if (cp != 0 && cp != source) {
238 cp = va_arg(ap, char *);
239 while (*source != '\0') {
243 } else if (found && (ch == *skip_s(pattern))) {
255 /* other characters are matched literally */
256 if (*source++ != ch) {
265 return (*source || *pattern) ? 0 : 1;
269 parse_xbm(char **data)
278 PICS_HEAD *result = typeCalloc(PICS_HEAD, 1);
282 for (n = 0; data[n] != 0; ++n) {
283 if (strlen(s = data[n]) >= sizeof(buf) - 1)
289 if (sscanf(s, "#define %s %d%c", buf, &num, &ch) >= 2) {
290 if ((t = strstr(buf, "_width")) != 0) {
292 result->wide = bytes_of(num);
293 } else if ((t = strstr(buf, "_height")) != 0) {
299 if (strcmp(result->name, buf)) {
303 result->name = strdup(buf);
308 if (sscanf(s, "static char %[^_ ]_bits[]%c", buf, &ch) >= 1) {
309 if (strcmp(result->name, buf)) {
313 cells = (size_t) (result->wide * result->high);
314 result->cells = typeCalloc(PICS_CELL, cells);
315 if ((s = strchr(s, L_CURL)) == 0)
323 while (isspace(UChar(*s))) {
326 if (isdigit(UChar(*s))) {
327 long value = strtol(s, &t, 0);
329 if (t != s || value > 255 || value < 0) {
335 /* TODO: which order? */
336 for (b = 0; b < 8; ++b) {
337 if (((1L << b) & value) != 0) {
338 result->cells[which].ch = '*';
339 result->cells[which].fg = 1;
341 result->cells[which].ch = ' ';
342 result->cells[which].fg = 0;
344 if (++which > cells) {
353 } else if (*s == ',') {
372 result->pairs = typeCalloc(PICS_PAIR, 2);
373 result->pairs[1].fg = 0xffffff;
379 parse_xpm(char **data)
382 PICS_HEAD *result = typeCalloc(PICS_HEAD, 1);
386 int cpp = 1; /* chars per pixel */
398 for (n = 0; data[n] != 0; ++n) {
399 if (strlen(s = data[n]) >= sizeof(buf) - 1)
403 if (match_c(s, " /* XPM */ ")) {
408 if (match_c(s, " static char * %s [] = %c ", arg1, &ch) &&
410 result->name = strdup(arg1);
415 if (match_c(s, " \" %d %d %d %d \" , ",
416 num + 0, num + 1, num + 2, num + 3) ||
417 match_c(s, " \" %d %d %d %d %d %d \" , ",
418 num + 0, num + 1, num + 2, num + 3, num + 4, num + 5)) {
419 result->wide = num[0];
420 result->high = num[1];
421 result->colors = num[2];
422 result->pairs = typeCalloc(PICS_PAIR, result->colors);
423 cells = (size_t) (result->wide * result->high);
424 result->cells = typeCalloc(PICS_CELL, cells);
425 list = typeCalloc(char *, result->colors);
431 if (match_c(s, " \" %s %s %s \" , ", arg1, arg2, arg3)) {
433 } else if (match_c(s, " \" %s %s \" , ", arg2, arg3)) {
438 while ((int) strlen(arg1) < cpp)
440 list[color] = strdup(arg1);
441 if (!strcmp(arg3, "None")) {
442 result->pairs[color].fg = -1;
443 } else if (*arg3 == '#') {
444 unsigned long value = strtoul(arg3 + 1, &s, 16);
445 result->pairs[color].fg = (int) value;
447 result->pairs[color].fg = 0; /* actually an error */
449 if (++color >= result->colors)
453 if (*(cs = skip_s(s)) == '"') {
455 while (*cs != '\0' && *cs != '"') {
458 for (c = 0; c < result->colors; ++c) {
459 if (!strncmp(cs, list[c], cpp)) {
460 result->cells[which].ch = list[c][0];
461 result->cells[which].fg = c;
466 if (result->cells[which].ch == 0) {
467 result->cells[which].ch = '?';
468 result->cells[which].fg = 0;
471 if (++which >= cells) {
475 for (c = cpp; c > 0; --c, ++cs) ;
482 if (result && list) {
483 for (n = 0; n < result->colors; ++n)
489 free_pics_head(result);
497 read_picture(const char *filename, char **data)
500 if ((pics = parse_xbm(data)) == 0) {
501 if ((pics = parse_xpm(data)) == 0) {
503 giveup("unexpected file-format for \"%s\"", filename);
510 dump_picture(PICS_HEAD * pics)
514 printf("Name %s\n", pics->name);
515 printf("Size %dx%d\n", pics->high, pics->wide);
517 for (y = 0; y < pics->colors; ++y) {
518 if (pics->pairs[y].fg < 0) {
519 printf(" %3d: %d\n", y, pics->pairs[y].fg);
521 printf(" %3d: #%06x\n", y, pics->pairs[y].fg);
524 for (y = 0; y < pics->high; ++y) {
525 for (x = 0; x < pics->wide; ++x) {
526 putchar(pics->cells[y * pics->wide + x].ch);
533 show_picture(PICS_HEAD * pics)
546 scrollok(stdscr, FALSE);
548 for (n = 0; n < pics->colors; ++n) {
549 init_pair((short) (n + 1),
550 (short) map_color(pics->pairs[n].fg),
553 attrset(COLOR_PAIR(1));
556 for (y = 0; y < pics->high; ++y) {
560 for (x = 0; x < pics->wide; ++x) {
563 n = (y * pics->wide + x);
564 attrset(COLOR_PAIR(pics->cells[n].fg + 1));
565 addch((chtype) pics->cells[n].ch);
573 main(int argc, char *argv[])
578 for (n = 1; n < argc; ++n) {
579 char **data = read_file(argv[n]);
582 giveup("cannot read \"%s\"", argv[n]);
584 if ((pics = read_picture(argv[n], data)) == 0) {
586 giveup("unexpected file-format for \"%s\"", argv[n]);
588 if (isatty(fileno(stdout))) {
594 free_pics_head(pics);
599 ExitProgram(EXIT_SUCCESS);