]> ncurses.scripts.mit.edu Git - ncurses.git/blobdiff - test/picsmap.c
ncurses 6.0 - patch 20170715
[ncurses.git] / test / picsmap.c
index 108b3bc1118067e58dcc08337de9e1853ce9d5ad..e360062aa9a66757ac02f99bf6cbfad3fc428781 100644 (file)
@@ -26,7 +26,7 @@
  * authorization.                                                           *
  ****************************************************************************/
 /*
- * $Id: picsmap.c,v 1.29 2017/05/28 00:19:58 tom Exp $
+ * $Id: picsmap.c,v 1.52 2017/07/15 22:25:06 tom Exp $
  *
  * Author: Thomas E. Dickey
  *
  * TODO write picture left-to-right/top-to-bottom
  * TODO write picture randomly
  * TODO add one-shot option vs repeat-count before exiting
- * TODO add option for assumed palette of terminal
- * TODO add option for init_color
- * TODO add option for init_color vs init_extended_color
- * TODO add option for init_pair vs alloc_pair
+ * TODO add option "-xc" for init_color vs init_extended_color
+ * TODO add option "-xa" for init_pair vs alloc_pair
  * TODO use pad to allow pictures larger than screen
+ * TODO improve load of image-file's color-table using tsearch.
+ * TODO add option to just use convert (which can scale) vs builtin xbm/xpm.
  */
 #include <test.priv.h>
 
 #include <sys/types.h>
 #include <sys/stat.h>
 
+#undef CUR                     /* use only the curses interface */
+
 #define  L_BLOCK '['
 #define  R_BLOCK ']'
 
 #define  L_CURLY '{'
 #define  R_CURLY '}'
 
+#define okCOLOR(n) ((n) >= 0 && (n) < COLORS)
+#define okRGB(n)   ((n) >= 0 && (n) <= 1000)
+#define Scaled256(n) (NCURSES_COLOR_T) (int)(((n) * 1000.0) / 256)
+#define ScaledColor(n) (NCURSES_COLOR_T) (int)(((n) * 1000.0) / scale)
+
 typedef struct {
     int ch;                    /* nominal character to display */
     int fg;                    /* foreground color */
@@ -76,24 +83,45 @@ typedef struct {
 typedef struct {
     const char *name;
     int value;
+} RGB_NAME;
+
+typedef struct {
+    short red;
+    short green;
+    short blue;
 } RGB_DATA;
 
+static void giveup(const char *fmt,...) GCC_PRINTFLIKE(1, 2);
+
 static bool in_curses = FALSE;
-static RGB_DATA *rgb_table;
+static RGB_NAME *rgb_table;
+static RGB_DATA *all_colors;
+
+#if HAVE_ALLOC_PAIR && HAVE_INIT_EXTENDED_COLOR
+#define USE_EXTENDED_COLORS 1
+static bool use_extended_pairs = FALSE;
+static bool use_extended_colors = FALSE;
+#else
+#define USE_EXTENDED_COLORS 0
+#endif
 
 static void
 free_data(char **data)
 {
-    free(data[0]);
-    free(data);
+    if (data != 0) {
+       free(data[0]);
+       free(data);
+    }
 }
 
 static void
 free_pics_head(PICS_HEAD * pics)
 {
-    free(pics->pairs);
-    free(pics->cells);
-    free(pics);
+    if (pics != 0) {
+       free(pics->pairs);
+       free(pics->cells);
+       free(pics);
+    }
 }
 
 /*
@@ -106,7 +134,11 @@ read_file(const char *filename)
     char **result = 0;
     struct stat sb;
 
+    if (in_curses)
+       endwin();
+
     printf("** %s\n", filename);
+
     if (stat(filename, &sb) == 0
        && (sb.st_mode & S_IFMT) == S_IFREG
        && sb.st_size != 0) {
@@ -149,10 +181,21 @@ usage(void)
 {
     static const char *msg[] =
     {
-       "Usage: picsmap [-x rgb-path] [xbm-file [...]]"
+       "Usage: picsmap [options] [imagefile [...]]"
+       ,"Read/display one or more xbm/xpm files (possibly use \"convert\")"
+       ,""
+       ,"Options:"
+       ,"  -p palette"
+       ,"  -r rgb-path"
+#if USE_EXTENDED_COLORS
+       ,"  -x [p]   use extension (p=init_extended_pair)"
+#endif
     };
     size_t n;
 
+    if (in_curses)
+       endwin();
+
     fflush(stdout);
     for (n = 0; n < SIZEOF(msg); n++)
        fprintf(stderr, "%s\n", msg[n]);
@@ -173,14 +216,116 @@ giveup(const char *fmt,...)
     usage();
 }
 
+static void
+init_palette(const char *palette_file)
+{
+    if (palette_file != 0) {
+       char **data = read_file(palette_file);
+       int cp;
+
+       all_colors = typeMalloc(RGB_DATA, (unsigned) COLORS);
+       for (cp = 0; cp < COLORS; ++cp) {
+           color_content((short) cp,
+                         &all_colors[cp].red,
+                         &all_colors[cp].green,
+                         &all_colors[cp].blue);
+       }
+       if (palette_file != 0 && data != 0) {
+           int n;
+           int red, green, blue;
+           int scale = 1000;
+           int c;
+           for (n = 0; data[n] != 0; ++n) {
+               if (sscanf(data[n], "scale:%d", &c) == 1) {
+                   scale = c;
+               } else if (sscanf(data[n], "%d:%d %d %d",
+                                 &c,
+                                 &red,
+                                 &green,
+                                 &blue) == 4
+                          && okCOLOR(c)
+                          && okRGB(red)
+                          && okRGB(green)
+                          && okRGB(blue)) {
+                   /* *INDENT-EQLS* */
+                   all_colors[c].red   = ScaledColor(red);
+                   all_colors[c].green = ScaledColor(green);
+                   all_colors[c].blue  = ScaledColor(blue);
+               }
+           }
+       }
+       free_data(data);
+    } else if (COLORS   > 1) {
+       /* *INDENT-EQLS* */
+       int power2 = 1;
+       int shift = 0;
+
+       while (power2 < COLORS) {
+           ++shift;
+           power2 <<= 1;
+       }
+
+       if ((power2 != COLORS) || ((shift % 3) != 0)) {
+           giveup("With %d colors, you need a palette-file", COLORS);
+       }
+    }
+}
+
+/*
+ * Map the 24-bit RGB value to a color index if using a palette, otherwise to a
+ * direct color value.
+ */
 static int
 map_color(int value)
 {
-    int r = (value & 0xff0000) >> 16;
-    int g = (value & 0x00ff00) >> 8;
-    int b = (value & 0x0000ff) >> 0;
-    /* TODO simple mapping into COLOR_BLACK .. COLOR_WHITE */
-    int result = ((r >= 128) << 2) + ((g >= 128) << 1) + (b >= 128);
+    int result = value;
+
+    if (result < 0) {
+       result = -1;
+    } else {
+       /* *INDENT-EQLS* */
+       int red   = (value & 0xff0000) >> 16;
+       int green = (value & 0x00ff00) >> 8;
+       int blue  = (value & 0x0000ff) >> 0;
+
+       if (all_colors != 0) {
+#define Diff2(n,m) ((m) - all_colors[n].m) * ((m) - all_colors[n].m)
+#define Diff2S(n) Diff2(n,red) + Diff2(n,green) + Diff2(n,blue)
+           int d2 = Diff2S(0);
+           int n;
+
+           /* *INDENT-EQLS* */
+           red   = Scaled256(red);
+           green = Scaled256(green);
+           blue  = Scaled256(blue);
+
+           for (result = 0, n = 1; n < COLORS; ++n) {
+               int d = Diff2(n, red) + Diff2(n, green) + Diff2(n, blue);
+               if (d < d2) {
+                   d2 = d;
+                   result = n;
+               }
+           }
+       } else {                /* direct color */
+           int power2 = 1;
+           int shifts = 8;
+
+           while (power2 < COLORS) {
+               power2 <<= 3;
+               shifts--;
+           }
+
+           if (shifts > 0) {
+               /* TODO: round up */
+               red >>= shifts;
+               green >>= shifts;
+               blue >>= shifts;
+               result = ((red << (2 * (8 - shifts)))
+                         + (green << (8 - shifts))
+                         + blue);
+           }
+       }
+    }
     return result;
 }
 
@@ -212,6 +357,15 @@ skip_cs(const char *s)
     return s;
 }
 
+static char *
+skip_word(char *s)
+{
+    s = skip_s(s);
+    while (isgraph(UChar(*s)))
+       s++;
+    return s;
+}
+
 static int
 match_c(const char *source, const char *pattern,...)
 {
@@ -332,7 +486,7 @@ match_colors(const char *source, int cpp, char *arg1, char *arg2, char *arg3)
     return result;
 }
 
-static RGB_DATA *
+static RGB_NAME *
 parse_rgb(char **data)
 {
     char buf[BUFSIZ];
@@ -341,10 +495,10 @@ parse_rgb(char **data)
     char *s, *t;
     size_t item = 0;
     size_t need;
-    RGB_DATA *result = 0;
+    RGB_NAME *result = 0;
 
     for (need = 0; data[need] != 0; ++need) ;
-    result = typeCalloc(RGB_DATA, need + 2);
+    result = typeCalloc(RGB_NAME, need + 2);
 
     for (n = 0; data[n] != 0; ++n) {
        if (strlen(t = data[n]) >= sizeof(buf) - 1)
@@ -375,10 +529,10 @@ parse_rgb(char **data)
     return result;
 }
 
-static RGB_DATA *
+static RGB_NAME *
 lookup_rgb(const char *name)
 {
-    RGB_DATA *result = 0;
+    RGB_NAME *result = 0;
     if (rgb_table != 0) {
        int n;
        for (n = 0; rgb_table[n].name != 0; ++n) {
@@ -504,7 +658,7 @@ parse_xpm(char **data)
 {
     int state = 0;
     PICS_HEAD *result = typeCalloc(PICS_HEAD, 1);
-    RGB_DATA *by_name;
+    RGB_NAME *by_name;
     int n;
     int cells = 0;
     int color = 0;
@@ -545,7 +699,7 @@ parse_xpm(char **data)
                result->high = num[1];
                result->colors = num[2];
                result->pairs = typeCalloc(PICS_PAIR, result->colors);
-               cells = (size_t) (result->wide * result->high);
+               cells = (result->wide * result->high);
                result->cells = typeCalloc(PICS_CELL, cells);
                list = typeCalloc(char *, result->colors);
                cpp = num[3];
@@ -631,13 +785,39 @@ parse_xpm(char **data)
 static PICS_HEAD *
 parse_img(const char *filename)
 {
-    char *cmd = malloc(strlen(filename) + 80);
+    char *cmd = malloc(strlen(filename) + 256);
     FILE *pp;
     char buffer[BUFSIZ];
     bool failed = FALSE;
     PICS_HEAD *result = typeCalloc(PICS_HEAD, 1);
+    int pic_x = 0;
+    int pic_y = 0;
+    int width = in_curses ? COLS : 80;
+
+    sprintf(cmd, "identify \"%s\"", filename);
+
+    if ((pp = popen(cmd, "r")) != 0) {
+       if (fgets(buffer, sizeof(buffer), pp) != 0) {
+           size_t n = strlen(filename);
+           if (strlen(buffer) > n &&
+               !strncmp(buffer, filename, n) &&
+               isspace(UChar(buffer[n])) &&
+               sscanf(skip_word(buffer + n), " %dx%d ", &pic_x, &pic_y) == 2) {
+               /* distort image to make it show normally on terminal */
+               pic_x = (166 * pic_x) / 100;
+           } else {
+               pic_x = pic_y = 0;
+           }
+       }
+       pclose(pp);
+    }
+    if (pic_x <= 0 || pic_y <= 0)
+       goto finish;
+
+    sprintf(cmd, "convert " "-resize %dx%d\\! " "-thumbnail %dx \"%s\" "
+           "-define txt:compliance=SVG txt:-",
+           pic_x, pic_y, width, filename);
 
-    sprintf(cmd, "convert -thumbnail %dx \"%s\" txt:-", COLS, filename);
     if ((pp = popen(cmd, "r")) != 0) {
        int count = 0;
        int col = 0;
@@ -664,10 +844,11 @@ parse_img(const char *filename)
                }
            } else {
                /* subsequent lines begin "col,row: (r,g,b,a) #RGB" */
-               int r, g, b;
+               int r, g, b, nocolor;
                unsigned check;
                int which, c;
-               char *s = strchr(buffer, '#');
+               char *t;
+               char *s = t = strchr(buffer, '#');
                if (s != 0) {
                    /* after the "#RGB", there are differences - just ignore */
                    while (*s != '\0' && !isspace(UChar(*s)))
@@ -675,10 +856,12 @@ parse_img(const char *filename)
                    *++s = '\0';
                }
                if (match_c(buffer,
-                           "%d,%d: (%d,%d,%d,255) #%x ",
+                           "%d,%d: (%d,%d,%d,%d) #%x ",
                            &col, &row,
-                           &r, &g, &b,
+                           &r, &g, &b, &nocolor,
                            &check)) {
+                   if ((s - t) > 8)    /* 6 hex digits vs 8 */
+                       check /= 256;
                    if (r > 255 ||
                        g > 255 ||
                        b > 255 ||
@@ -709,7 +892,10 @@ parse_img(const char *filename)
                        }
                    }
                    which = col + (row * result->wide);
-                   result->cells[which].ch = '#';      /* TODO: space? */
+                   result->cells[which].ch = ((in_curses ||
+                                               check == 0xffffff)
+                                              ? ' '
+                                              : '#');
                    result->cells[which].fg = (c < result->colors) ? c : -1;
                } else {
                    failed = TRUE;
@@ -728,6 +914,7 @@ parse_img(const char *filename)
            }
        }
     }
+  finish:
     free(cmd);
 
     if (failed) {
@@ -781,12 +968,22 @@ show_picture(PICS_HEAD * pics)
 {
     int y, x;
     int n;
+    int my_pair, my_color;
 
     if (has_colors()) {
        for (n = 0; n < pics->colors; ++n) {
-           init_pair((short) (n + 1),
-                     (short) map_color(pics->pairs[n].fg),
-                     COLOR_BLACK);
+           my_pair = (n + 1);
+           my_color = map_color(pics->pairs[n].fg);
+#if USE_EXTENDED_COLORS
+           if (use_extended_pairs) {
+               init_extended_pair(my_pair, my_color, my_color);
+           } else
+#endif
+           {
+               my_pair &= 0x7fff;
+               my_color &= 0x7fff;
+               init_pair((short) my_pair, (short) my_color, (short) my_color);
+           }
        }
        attrset(COLOR_PAIR(1));
        erase();
@@ -799,8 +996,21 @@ show_picture(PICS_HEAD * pics)
            if (x >= COLS)
                break;
            n = (y * pics->wide + x);
-           attrset(COLOR_PAIR(pics->cells[n].fg + 1));
-           addch((chtype) pics->cells[n].ch);
+           my_pair = pics->cells[n].fg + 1;
+#if USE_EXTENDED_COLORS
+           if (use_extended_pairs) {
+               cchar_t temp;
+               wchar_t wch[2];
+               wch[0] = (wchar_t) pics->cells[n].ch;
+               wch[1] = 0;
+               setcchar(&temp, wch, A_NORMAL, (short) my_pair, &my_pair);
+               add_wch(&temp);
+           } else
+#endif
+           {
+               attrset(COLOR_PAIR(my_pair));
+               addch((chtype) pics->cells[n].ch);
+           }
        }
     }
     mvgetch(0, 0);
@@ -811,20 +1021,44 @@ int
 main(int argc, char *argv[])
 {
     int n;
+    const char *palette_path = 0;
     const char *rgb_path = "/etc/X11/rgb.txt";
 
-    while ((n = getopt(argc, argv, "r:")) != -1) {
+    while ((n = getopt(argc, argv, "p:r:x:")) != -1) {
        switch (n) {
+       case 'p':
+           palette_path = optarg;
+           break;
        case 'r':
            rgb_path = optarg;
            break;
+#if USE_EXTENDED_COLORS
+       case 'x':
+           {
+               char *s = optarg;
+               while (*s) {
+                   switch (*s++) {
+                   case 'p':
+                       use_extended_pairs = TRUE;
+                       break;
+                   case 'c':
+                       use_extended_colors = TRUE;
+                       break;
+                   default:
+                       usage();
+                       break;
+                   }
+               }
+           }
+           break;
+#endif
        default:
            usage();
            break;
        }
     }
 
-    if (argc > 1) {
+    if (optind < argc) {
        char **rgb_data = read_file(rgb_path);
 
        if (rgb_data)
@@ -835,13 +1069,17 @@ main(int argc, char *argv[])
            initscr();
            cbreak();
            noecho();
-           if (has_colors())
+           if (has_colors()) {
                start_color();
+               init_palette(palette_path);
+           }
            scrollok(stdscr, FALSE);
            endwin();
        }
+       if (optind >= argc)
+           giveup("expected at least one image filename");
 
-       for (n = 1; n < argc; ++n) {
+       for (n = optind; n < argc; ++n) {
            PICS_HEAD *pics;
            char **data = read_file(argv[n]);