|
Packit |
78deda |
/*=============================================================================
|
|
Packit |
78deda |
ppmtoascii
|
|
Packit |
78deda |
===============================================================================
|
|
Packit |
78deda |
|
|
Packit |
78deda |
Convert a PPM image to ASCII with ANSI color graphics terminal controls
|
|
Packit |
78deda |
|
|
Packit |
78deda |
Based on pbmtoascii.
|
|
Packit |
78deda |
Copyright (C) 1988, 1992 by Jef Poskanzer.
|
|
Packit |
78deda |
Copyright (C) 2010 by Frank Ch. Eigler.
|
|
Packit |
78deda |
|
|
Packit |
78deda |
Permission to use, copy, modify, and distribute this software and its
|
|
Packit |
78deda |
documentation for any purpose and without fee is hereby granted, provided
|
|
Packit |
78deda |
that the above copyright notice appear in all copies and that both that
|
|
Packit |
78deda |
copyright notice and this permission notice appear in supporting
|
|
Packit |
78deda |
documentation. This software is provided "as is" without express or
|
|
Packit |
78deda |
implied warranty.
|
|
Packit |
78deda |
=============================================================================*/
|
|
Packit |
78deda |
|
|
Packit |
78deda |
#include "pm_c_util.h"
|
|
Packit |
78deda |
#include "mallocvar.h"
|
|
Packit |
78deda |
#include "shhopt.h"
|
|
Packit |
78deda |
#include "ppm.h"
|
|
Packit |
78deda |
#include "pm_gamma.h"
|
|
Packit |
78deda |
|
|
Packit |
78deda |
/* We use the same algorithm for converting many pixels into a character
|
|
Packit |
78deda |
as Pbmtoascii. See pbmtoascii.c for a full explanation.
|
|
Packit |
78deda |
*/
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
#define SQQ '\''
|
|
Packit |
78deda |
#define BSQ '\\'
|
|
Packit |
78deda |
|
|
Packit |
78deda |
/* Bit-map for 1x2 mode:
|
|
Packit |
78deda |
** 1
|
|
Packit |
78deda |
** 2
|
|
Packit |
78deda |
*/
|
|
Packit |
78deda |
static char const carr1x2[4] = {
|
|
Packit |
78deda |
/* 0 1 2 3 */
|
|
Packit |
78deda |
' ', '"', 'o', 'M' };
|
|
Packit |
78deda |
|
|
Packit |
78deda |
/* Bit-map for 2x4 mode (hex):
|
|
Packit |
78deda |
** 1 2
|
|
Packit |
78deda |
** 4 8
|
|
Packit |
78deda |
** 10 20
|
|
Packit |
78deda |
** 40 80
|
|
Packit |
78deda |
** The idea here is first to preserve geometry, then to show density.
|
|
Packit |
78deda |
*/
|
|
Packit |
78deda |
#define D08 'M'
|
|
Packit |
78deda |
#define D07 'H'
|
|
Packit |
78deda |
#define D06 '&'
|
|
Packit |
78deda |
#define D05 '$'
|
|
Packit |
78deda |
#define D04 '?'
|
|
Packit |
78deda |
static char const carr2x4[256] = {
|
|
Packit |
78deda |
/*0 1 2 3 4 5 6 7 8 9 A B C D E F */
|
|
Packit |
78deda |
' ',SQQ, '`','"', '-',SQQ, SQQ,SQQ, '-','`', '`','`', '-','^','^','"',/*00-0F*/
|
|
Packit |
78deda |
'.',':', ':',':', '|','|', '/',D04, '/','>', '/','>', '~','+','/','*',/*10-1F*/
|
|
Packit |
78deda |
'.',':', ':',':', BSQ,BSQ, '<','<', '|',BSQ, '|',D04, '~',BSQ,'+','*',/*20-2F*/
|
|
Packit |
78deda |
'-',':', ':',':', '~',D04, '<','<', '~','>', D04,'>', '=','b','d','#',/*30-3F*/
|
|
Packit |
78deda |
'.',':', ':',':', ':','!', '/',D04, ':',':', '/',D04, ':',D04,D04,'P',/*40-4F*/
|
|
Packit |
78deda |
',','i', '/',D04, '|','|', '|','T', '/',D04, '/','7', 'r','}','/','P',/*50-5F*/
|
|
Packit |
78deda |
',',':', ';',D04, '>',D04, 'S','S', '/',')', '|','7', '>',D05,D05,D06,/*60-6F*/
|
|
Packit |
78deda |
'v',D04, D04,D05, '+','}', D05,'F', '/',D05, '/',D06, 'p','D',D06,D07,/*70-7F*/
|
|
Packit |
78deda |
'.',':', ':',':', ':',BSQ, ':',D04, ':',BSQ, '!',D04, ':',D04,D04,D05,/*80-8F*/
|
|
Packit |
78deda |
BSQ,BSQ, ':',D04, BSQ,'|', '(',D05, '<','%', D04,'Z', '<',D05,D05,D06,/*90-9F*/
|
|
Packit |
78deda |
',',BSQ, 'i',D04, BSQ,BSQ, D04,BSQ, '|','|', '|','T', D04,BSQ,'4','9',/*A0-AF*/
|
|
Packit |
78deda |
'v',D04, D04,D05, BSQ,BSQ, D05,D06, '+',D05, '{',D06, 'q',D06,D06,D07,/*B0-BF*/
|
|
Packit |
78deda |
'_',':', ':',D04, ':',D04, D04,D05, ':',D04, D04,D05, ':',D05,D05,D06,/*C0-CF*/
|
|
Packit |
78deda |
BSQ,D04, D04,D05, D04,'L', D05,'[', '<','Z', '/','Z', 'c','k',D06,'R',/*D0-DF*/
|
|
Packit |
78deda |
',',D04, D04,D05, '>',BSQ, 'S','S', D04,D05, 'J',']', '>',D06,'1','9',/*E0-EF*/
|
|
Packit |
78deda |
'o','b', 'd',D06, 'b','b', D06,'6', 'd',D06, 'd',D07, '#',D07,D07,D08 /*F0-FF*/
|
|
Packit |
78deda |
};
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
static const char* rgb2x2x2fg[2][2][2] = {
|
|
Packit |
78deda |
{{"\x1b[30m", /* 000 black */
|
|
Packit |
78deda |
"\x1b[34m"}, /* 001 blue */
|
|
Packit |
78deda |
{"\x1b[32m", /* 010 green */
|
|
Packit |
78deda |
"\x1b[36m"}}, /* 011 cyan */
|
|
Packit |
78deda |
{{"\x1b[31m", /* 100 red */
|
|
Packit |
78deda |
"\x1b[35m"}, /* 101 magenta */
|
|
Packit |
78deda |
{"\x1b[33m", /* 110 yellow */
|
|
Packit |
78deda |
"\x1b[37m"}}, /* 111 white */
|
|
Packit |
78deda |
};
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
struct cmdlineInfo {
|
|
Packit |
78deda |
/* All the information the user supplied in the command line,
|
|
Packit |
78deda |
in a form easy for the program to use.
|
|
Packit |
78deda |
*/
|
|
Packit |
78deda |
const char * inputFileName; /* Name of input file */
|
|
Packit |
78deda |
unsigned int cellWidth;
|
|
Packit |
78deda |
unsigned int cellHeight;
|
|
Packit |
78deda |
const char * carr;
|
|
Packit |
78deda |
};
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
static void
|
|
Packit |
78deda |
parseCommandLine(int argc, const char **argv,
|
|
Packit |
78deda |
struct cmdlineInfo * const cmdlineP) {
|
|
Packit |
78deda |
|
|
Packit |
78deda |
optEntry * option_def;
|
|
Packit |
78deda |
/* Instructions to OptParseOptions3 on how to parse our options */
|
|
Packit |
78deda |
optStruct3 opt;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
unsigned int option_def_index;
|
|
Packit |
78deda |
unsigned int dim1x2Spec, dim2x4Spec;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
MALLOCARRAY_NOFAIL(option_def, 100);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
option_def_index = 0; /* incremented by OPTENTRY */
|
|
Packit |
78deda |
OPTENT3(0, "1x2", OPT_FLAG, NULL, &dim1x2Spec, 0);
|
|
Packit |
78deda |
OPTENT3(0, "2x4", OPT_FLAG, NULL, &dim2x4Spec, 0);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
opt.opt_table = option_def;
|
|
Packit |
78deda |
opt.short_allowed = false; /* We have no short (old-fashioned) options */
|
|
Packit |
78deda |
opt.allowNegNum = false; /* We have no parms that are negative numbers */
|
|
Packit |
78deda |
|
|
Packit |
78deda |
pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
|
|
Packit |
78deda |
/* Uses and sets argc, argv, and some of *cmdlineP and others. */
|
|
Packit |
78deda |
|
|
Packit |
78deda |
if (dim1x2Spec && dim2x4Spec)
|
|
Packit |
78deda |
pm_error("You cannot specify both -1x2 and -2x4");
|
|
Packit |
78deda |
else if (dim2x4Spec) {
|
|
Packit |
78deda |
cmdlineP->cellWidth = 2;
|
|
Packit |
78deda |
cmdlineP->cellHeight = 4;
|
|
Packit |
78deda |
cmdlineP->carr = carr2x4;
|
|
Packit |
78deda |
} else {
|
|
Packit |
78deda |
cmdlineP->cellWidth = 1;
|
|
Packit |
78deda |
cmdlineP->cellHeight = 2;
|
|
Packit |
78deda |
cmdlineP->carr = carr1x2;
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
if (argc-1 < 1)
|
|
Packit |
78deda |
cmdlineP->inputFileName = "-";
|
|
Packit |
78deda |
else {
|
|
Packit |
78deda |
cmdlineP->inputFileName = argv[1];
|
|
Packit |
78deda |
|
|
Packit |
78deda |
if (argc-1 > 1)
|
|
Packit |
78deda |
pm_error("Too many arguments: %u. The only possible argument "
|
|
Packit |
78deda |
"is the input file name", argc-1);
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
static void
|
|
Packit |
78deda |
ppmtoascii(pixel * const * const pixels,
|
|
Packit |
78deda |
unsigned int const cols,
|
|
Packit |
78deda |
unsigned int const rows,
|
|
Packit |
78deda |
pixval const maxval,
|
|
Packit |
78deda |
struct cmdlineInfo const cmdline,
|
|
Packit |
78deda |
FILE * const ofP) {
|
|
Packit |
78deda |
|
|
Packit |
78deda |
unsigned int const cellHeight = cmdline.cellHeight;
|
|
Packit |
78deda |
unsigned int const cellWidth = cmdline.cellWidth;
|
|
Packit |
78deda |
const char * const carr = cmdline.carr;
|
|
Packit |
78deda |
unsigned int row;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
fprintf(ofP, "\x1b[0m"); /* Clear initial ansi attributes */
|
|
Packit |
78deda |
/* TODO: set background to black */
|
|
Packit |
78deda |
|
|
Packit |
78deda |
for (row = 0; row < rows; row += cellHeight) {
|
|
Packit |
78deda |
unsigned int col;
|
|
Packit |
78deda |
for (col = 0; col < cols; col += cellWidth) {
|
|
Packit |
78deda |
unsigned int const sumthresh = cellWidth * cellHeight * 1.0 / 2;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
float sumr, sumg, sumb;
|
|
Packit |
78deda |
/* sum of intensity within cell so far, by component */
|
|
Packit |
78deda |
unsigned int b;
|
|
Packit |
78deda |
const char * colorstr;
|
|
Packit |
78deda |
unsigned int subrow;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
sumr = sumg = sumb = 0; /* initial value */
|
|
Packit |
78deda |
b = 0; /* initial value */
|
|
Packit |
78deda |
|
|
Packit |
78deda |
for (subrow = 0; subrow < cellHeight; ++subrow) {
|
|
Packit |
78deda |
unsigned int subcol;
|
|
Packit |
78deda |
for (subcol = 0; subcol < cellWidth; ++subcol) {
|
|
Packit |
78deda |
pixel color;
|
|
Packit |
78deda |
pixval value;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
if (row + subrow < rows && col + subcol < cols)
|
|
Packit |
78deda |
color = pixels[row + subrow][col + subcol];
|
|
Packit |
78deda |
else
|
|
Packit |
78deda |
color = ppm_whitepixel (maxval);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
sumr += pm_ungamma709((float)PPM_GETR(color)/maxval);
|
|
Packit |
78deda |
sumg += pm_ungamma709((float)PPM_GETG(color)/maxval);
|
|
Packit |
78deda |
sumb += pm_ungamma709((float)PPM_GETB(color)/maxval);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
value = ppm_colorvalue(color);
|
|
Packit |
78deda |
b <<= 1;
|
|
Packit |
78deda |
if (value > maxval/2)
|
|
Packit |
78deda |
b |= 1;
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
colorstr = rgb2x2x2fg
|
|
Packit |
78deda |
[sumr >= sumthresh]
|
|
Packit |
78deda |
[sumg >= sumthresh]
|
|
Packit |
78deda |
[sumb >= sumthresh];
|
|
Packit |
78deda |
fprintf(ofP, "%s%c", colorstr, carr[b]);
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
fprintf(ofP, "\n");
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
fprintf(ofP, "\x1b[0m"); /* Clear final ansi attributes */
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
int
|
|
Packit |
78deda |
main(int argc, const char ** argv) {
|
|
Packit |
78deda |
|
|
Packit |
78deda |
struct cmdlineInfo cmdline;
|
|
Packit |
78deda |
FILE * ifP;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
pixval maxval;
|
|
Packit |
78deda |
pixel ** pixels;
|
|
Packit |
78deda |
int rows, cols;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
pm_proginit(&argc, argv);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
parseCommandLine(argc, argv, &cmdline);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
ifP = pm_openr(cmdline.inputFileName);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
pixels = ppm_readppm(ifP, &cols, &rows, &maxval);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
pm_close(ifP);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
ppmtoascii(pixels, cols, rows, maxval, cmdline, stdout);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
return 0;
|
|
Packit |
78deda |
}
|