* authorization. *
****************************************************************************/
/*
- * $Id: picsmap.c,v 1.29 2017/05/28 00:19:58 tom Exp $
+ * $Id: picsmap.c,v 1.50 2017/06/11 00:37:27 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 */
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);
+ }
}
/*
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) {
{
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]);
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;
}
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,...)
{
return result;
}
-static RGB_DATA *
+static RGB_NAME *
parse_rgb(char **data)
{
char buf[BUFSIZ];
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)
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) {
{
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;
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];
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\" 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;
}
}
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;
}
}
}
+ finish:
free(cmd);
if (failed) {
{
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();
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);
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)
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]);