diff options
Diffstat (limited to 'converter/ppm/ppmtosixel.c')
-rw-r--r-- | converter/ppm/ppmtosixel.c | 440 |
1 files changed, 270 insertions, 170 deletions
diff --git a/converter/ppm/ppmtosixel.c b/converter/ppm/ppmtosixel.c index 5f361e45..24454214 100644 --- a/converter/ppm/ppmtosixel.c +++ b/converter/ppm/ppmtosixel.c @@ -1,4 +1,4 @@ -/* ppmtosix.c - read a portable pixmap and produce a color sixel file +/* ppmtosix.c - read a PPM and produce a color sixel file ** ** Copyright (C) 1991 by Rick Vinci. ** @@ -8,208 +8,308 @@ ** copyright notice and this permission notice appear in supporting ** documentation. This software is provided "as is" without express or ** implied warranty. +** +** "-7bit" option added August 2023 by Scott Pakin <scott+pbm@pakin.org>. */ +#include "pm_c_util.h" +#include "mallocvar.h" +#include "shhopt.h" #include "ppm.h" -static void WriteHeader ARGS((void)); -static void WriteColorMap - ARGS((colorhist_vector chv, int colors, pixval maxval)); -static void WriteRawImage ARGS((colorhash_table cht, int rows, int cols)); -static void WritePackedImage ARGS((colorhash_table cht, int rows, int cols)); -static void WriteEnd ARGS((void)); -#define MAXVAL 100 -#define MAXCOLORS 256 +static unsigned int const SIXEL_MAXVAL = 100; +static unsigned int const MAXCOLORCT = 256; -#define DCS '\220' /* Device Control String */ -#define ST '\234' /* String Terminator */ -#define CSI '\233' /* Control String Introducer */ -#define ESC '\033' /* Escape character */ +static const char * const sixel = "@ACGO_"; -static pixel** pixels; /* stored ppm pixmap input */ -static colorhash_table cht; -int margin; +/* Named escape sequences */ +typedef struct { + const char * DCS; /* Device Control String */ + const char * ST; /* String Terminator */ + const char * CSI; /* Control String Introducer */ + const char * ESC; /* Escape character */ +} EscapeSequenceSet; -int -main( argc, argv ) - int argc; - char* argv[]; -{ - FILE* ifp; - int argn, rows, cols, colors; - int raw; - pixval maxval; - colorhist_vector chv; - const char* const usage = "[-raw] [-margin] [ppmfile]"; - - - ppm_init( &argc, argv ); - - argn = 1; - raw = 0; - margin = 0; - - /* Parse args. */ - while ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' ) - { - if ( pm_keymatch( argv[argn], "-raw", 2 ) ) - raw = 1; - else if ( pm_keymatch( argv[argn], "-margin", 2 ) ) - margin = 1; - else - pm_usage( usage ); - ++argn; - } - - if ( argn < argc ) - { - ifp = pm_openr( argv[argn] ); - ++argn; - } - else - ifp = stdin; - - if ( argn != argc ) - pm_usage( usage ); - - /* Read in the whole ppmfile. */ - pixels = ppm_readppm( ifp, &cols, &rows, &maxval ); - pm_close( ifp ); - - /* Print a warning if we could lose accuracy when rescaling colors. */ - if ( maxval > MAXVAL ) - pm_message( - "maxval is not %d - automatically rescaling colors", MAXVAL ); - - /* Figure out the colormap. */ - pm_message( "computing colormap..." ); - chv = ppm_computecolorhist( pixels, cols, rows, MAXCOLORS, &colors ); - if ( chv == (colorhist_vector) 0 ) - pm_error( "too many colors - try doing a 'pnmquant %d'", MAXCOLORS ); - pm_message( "%d colors found", colors ); - - /* Make a hash table for fast color lookup. */ - cht = ppm_colorhisttocolorhash( chv, colors ); - - WriteHeader(); - WriteColorMap( chv, colors, maxval ); - if ( raw == 1 ) - WriteRawImage( cht, rows, cols ); - else - WritePackedImage( cht, rows, cols ); - WriteEnd(); +enum Charwidth {CHARWIDTH_8BIT, CHARWIDTH_7BIT}; - /* If the program failed, it previously aborted with nonzero completion - code, via various function calls. +struct CmdlineInfo { + /* All the information the user supplied in the command line, in a form + easy for the program to use. */ - return 0; + const char * inputFileName; + unsigned int raw; + unsigned int margin; + enum Charwidth charWidth; +}; + + + +static EscapeSequenceSet +escapeSequenceSet(enum Charwidth const charWidth) { + + EscapeSequenceSet eseqs; + + switch (charWidth) { + case CHARWIDTH_8BIT: { + eseqs.DCS = "\220"; + eseqs.ST = "\234"; + eseqs.CSI = "\233"; + eseqs.ESC = "\033"; + } break; + case CHARWIDTH_7BIT: { + eseqs.DCS = "\033P"; + eseqs.ST = "\033\\"; + eseqs.CSI = "\033["; + eseqs.ESC = "\033"; + } break; + } + return eseqs; } static void -WriteHeader() - { - if ( margin == 1 ) - printf( "%c%d;%ds", CSI, 14, 72 ); - printf( "%c", DCS ); /* start with Device Control String */ - printf( "0;0;8q" ); /* Horizontal Grid Size at 1/90" and graphics On */ - printf( "\"1;1\n" ); /* set aspect ratio 1:1 */ - } +parseCommandLine(int argc, const char ** argv, + struct CmdlineInfo * const cmdlineP) { +/*---------------------------------------------------------------------------- + Parse the program arguments (given by argc and argv) into a form + the program can deal with more easily -- a cmdline_info structure. + If the syntax is invalid, issue a message and exit the program via + pm_error(). + + Note that the file spec array we return is stored in the storage that + was passed to us as the argv array. +-----------------------------------------------------------------------------*/ + optEntry * option_def; /* malloc'ed */ + optStruct3 opt; /* set by OPTENT3 */ + unsigned int option_def_index; + + unsigned int opt7Bit; + + MALLOCARRAY_NOFAIL(option_def, 100); + + option_def_index = 0; /* incremented by OPTENT3 */ + OPTENT3(0, "raw", OPT_FLAG, + NULL, &cmdlineP->raw, 0); + OPTENT3(0, "margin", OPT_FLAG, + NULL, &cmdlineP->margin, 0); + OPTENT3(0, "7bit", OPT_FLAG, + NULL, &opt7Bit, 0); + + opt.opt_table = option_def; + opt.short_allowed = false; /* We have no short (old-fashioned) options */ + opt.allowNegNum = false; /* We have no parms that are negative numbers */ + + pm_optParseOptions4(&argc, argv, opt, sizeof(opt), 0); + /* Uses and sets argc, argv, and some of *cmdlineP and others. */ + + cmdlineP->charWidth = opt7Bit ? CHARWIDTH_7BIT : CHARWIDTH_8BIT; + + if (argc-1 == 0) + cmdlineP->inputFileName = "-"; + else if (argc-1 != 1) + pm_error("Program takes zero or one argument (filename). You " + "specified %d", argc-1); + else + cmdlineP->inputFileName = argv[1]; + + free(option_def); +} static void -WriteColorMap( colorhist_vector chv, int colors, pixval maxval ) - { - register int colornum; - pixel p; - - for ( colornum = 0; colornum < colors ; ++colornum ) - { - p = chv[colornum].color; - if ( maxval != MAXVAL ) - PPM_DEPTH( p, p, maxval, MAXVAL ); - printf( "#%d;2;%d;%d;%d", colornum, - (int) PPM_GETR( p ), (int) PPM_GETG( p ), (int) PPM_GETB( p ) ); - } - printf( "\n" ); +writePackedImage(pixel ** const pixels, + unsigned int const rows, + unsigned int const cols, + colorhash_table const cht) { + + unsigned int row; + + for (row = 0; row < rows; ++row) { + + unsigned int const b = row % 6; + + unsigned int repeatCt; + unsigned int col; + + repeatCt = 1; + + for (col = 0; col < cols; ++col) { + const pixel * const thisRow = pixels[row]; + + unsigned int const thisColorIdx = + ppm_lookupcolor(cht, &thisRow[col]); + + if (col == cols - 1) /* last pixel in row */ + if (repeatCt == 1) + printf("#%d%c", thisColorIdx, sixel[b]); + else + printf("#%d!%d%c", thisColorIdx, repeatCt, sixel[b]); + else { /* not last pixel in row */ + unsigned int const nextColorIdx = + ppm_lookupcolor(cht, &thisRow[col+1]); + if (thisColorIdx == nextColorIdx) + ++repeatCt; + else { + if (repeatCt == 1) + printf("#%d%c", thisColorIdx, sixel[b]); + else { + printf("#%d!%d%c", thisColorIdx, repeatCt, sixel[b]); + repeatCt = 1; + } + } + } + } + printf( "$\n" ); /* Carriage Return */ + + if (b == 5) + printf("-\n"); /* Line Feed (one sixel height) */ } +} static void -WriteRawImage( cht, rows, cols ) - colorhash_table cht; - int rows, cols; - { - int rownum, colnum, b; - const char* sixel = "@ACGO_"; - register pixel* pP; - - for ( rownum = 0; rownum < rows; ++rownum ) - { - b = rownum % 6; - for ( colnum = 0, pP = pixels[rownum]; colnum < cols; ++colnum, ++pP ) - printf( "#%d%c", ppm_lookupcolor(cht, pP), sixel[b] ); - printf( "$\n" ); /* Carriage Return */ - if ( b == 5 ) - printf( "-\n" ); /* Line Feed (one sixel height) */ - } +writeHeader(bool const wantMargin, + EscapeSequenceSet const eseqs) { + + if (wantMargin) + printf("%s%d;%ds", eseqs.CSI, 14, 72); + + printf("%s", eseqs.DCS); /* start with Device Control String */ + + printf("0;0;8q"); /* Horizontal Grid Size at 1/90" and graphics On */ + + printf("\"1;1\n"); /* set aspect ratio 1:1 */ +} + + + +static void +writeColorMap(colorhist_vector const chv, + unsigned int const colorCt, + pixval const maxval) { + + unsigned int colorIdx; + + for (colorIdx = 0; colorIdx < colorCt ; ++colorIdx) { + pixel const p = chv[colorIdx].color; + + pixel scaledColor; + + if (maxval == SIXEL_MAXVAL) + scaledColor = p; + else + PPM_DEPTH(scaledColor, p, maxval, SIXEL_MAXVAL); + + printf("#%d;2;%d;%d;%d", colorIdx, + PPM_GETR(scaledColor), + PPM_GETG(scaledColor), + PPM_GETB(scaledColor)); } + printf("\n"); +} static void -WritePackedImage( cht, rows, cols ) - colorhash_table cht; - int rows, cols; - { - int rownum, colnum, b, repeat, thiscolor, nextcolor; - const char* sixel = "@ACGO_"; - register pixel* pP; - - for ( rownum = 0; rownum < rows; ++rownum ) - { - b = rownum % 6; - repeat = 1; - for ( colnum = 0, pP = pixels[rownum]; colnum < cols; ++colnum, ++pP ) - { - thiscolor = ppm_lookupcolor(cht, pP); - if ( colnum == cols -1 ) /* last pixel in row */ - if ( repeat == 1 ) - printf( "#%d%c", thiscolor, sixel[b] ); - else - printf( "#%d!%d%c", thiscolor, repeat, sixel[b] ); - else /* not last pixel in row */ - { - nextcolor = ppm_lookupcolor(cht, pP+1); - if ( thiscolor == nextcolor ) - ++repeat; - else - if ( repeat == 1 ) - printf( "#%d%c", thiscolor, sixel[b] ); - else - { - printf( "#%d!%d%c", thiscolor, repeat, sixel[b] ); - repeat = 1; - } - } - } /* end column loop */ - printf( "$\n" ); /* Carriage Return */ - if ( b == 5 ) - printf( "-\n" ); /* Line Feed (one sixel height) */ - } +writeRawImage(pixel ** const pixels, + unsigned int const rows, + unsigned int const cols, + colorhash_table const cht) { + + unsigned int row; + + for (row = 0; row < rows; ++row) { + pixel * const thisRow = pixels[row]; + unsigned int const b = row % 6; + + unsigned int col; + + for (col = 0; col < cols; ++col) + printf("#%d%c", ppm_lookupcolor(cht, &thisRow[col]), sixel[b]); + + printf("$\n"); /* Carriage Return */ + + if (b == 5) + printf("-\n"); /* Line Feed (one sixel height) */ } +} static void -WriteEnd() - { - if ( margin == 1 ) - printf ( "%c%d;%ds", CSI, 1, 80 ); - printf( "%c\n", ST ); +writeEnd(bool const wantMargin, + EscapeSequenceSet const eseqs) { + + if (wantMargin) + printf ("%s%d;%ds", eseqs.CSI, 1, 80); + + printf("%s\n", eseqs.ST); +} + + + +int +main(int argc, const char ** argv) { + + struct CmdlineInfo cmdline; + FILE * ifP; + int rows, cols; + pixval maxval; + int colorCt; + pixel ** pixels; + colorhist_vector chv; + /* List of colors in the image, indexed by colormap index */ + colorhash_table cht; + /* Hash table for fast colormap index lookup */ + EscapeSequenceSet eseqs; + /* The escape sequences we use in our output for various functions */ + + pm_proginit(&argc, argv); + + parseCommandLine(argc, argv, &cmdline); + + ifP = pm_openr(cmdline.inputFileName); + + pixels = ppm_readppm(ifP, &cols, &rows, &maxval); + + if (maxval > SIXEL_MAXVAL) { + pm_message( + "maxval of input is not the sixel maxval (%u) - " + "rescaling to fewer colors", SIXEL_MAXVAL); } + + pm_message("computing colormap..."); + chv = ppm_computecolorhist(pixels, cols, rows, MAXCOLORCT, &colorCt); + if (!chv) + pm_error("too many colors - try 'pnmquant %u'", MAXCOLORCT); + + pm_message("%d colors found", colorCt); + + cht = ppm_colorhisttocolorhash(chv, colorCt); + + eseqs = escapeSequenceSet(cmdline.charWidth); + + writeHeader(!!cmdline.margin, eseqs); + + writeColorMap(chv, colorCt, maxval); + + if (cmdline.raw) + writeRawImage(pixels, rows, cols, cht); + else + writePackedImage(pixels, rows, cols, cht); + + writeEnd(!!cmdline.margin, eseqs); + + /* If the program failed, it previously aborted with nonzero completion + code, via various function calls. + */ + return 0; +} + + + |