Blame converter/ppm/ppmtoascii.c

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
}