/* ppmhist.c - read a portable pixmap and compute a color histogram ** ** Copyright (C) 1989 by Jef Poskanzer. ** ** Permission to use, copy, modify, and distribute this software and its ** documentation for any purpose and without fee is hereby granted, provided ** that the above copyright notice appear in all copies and that both that ** copyright notice and this permission notice appear in supporting ** documentation. This software is provided "as is" without express or ** implied warranty. */ #include "ppm.h" #include "shhopt.h" #include "nstring.h" enum sort {SORT_BY_FREQUENCY, SORT_BY_RGB}; struct cmdline_info { /* All the information the user supplied in the command line, in a form easy for the program to use. */ const char *input_filespec; /* Filespecs of input files */ unsigned int map; /* -map option */ unsigned int noheader; /* -noheader option */ unsigned int hexcolor; /* -hexcolor option */ unsigned int colorname; /* -colorname option */ enum sort sort; /* -sort option */ }; static void parse_command_line(int argc, char ** argv, struct cmdline_info * const cmdlineP) { /*---------------------------------------------------------------------------- Note that the file spec array we return is stored in the storage that was passed to us as the argv array. -----------------------------------------------------------------------------*/ optStruct3 opt; /* set by OPTENT3 */ optEntry *option_def = malloc(100*sizeof(optEntry)); unsigned int option_def_index; unsigned int nomap; /* dummy option for backward compatibility */ const char * sort_type; option_def_index = 0; /* incremented by OPTENTRY */ OPTENT3(0, "map", OPT_FLAG, NULL, &cmdlineP->map, 0); OPTENT3(0, "nomap", OPT_FLAG, NULL, &nomap, 0); OPTENT3(0, "noheader", OPT_FLAG, NULL, &cmdlineP->noheader, 0); OPTENT3(0, "hexcolor", OPT_FLAG, NULL, &cmdlineP->hexcolor, 0); OPTENT3(0, "colorname", OPT_FLAG, NULL, &cmdlineP->colorname, 0); OPTENT3(0, "sort", OPT_STRING, &sort_type, NULL, 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 */ /* Set defaults */ sort_type = "frequency"; optParseOptions3(&argc, argv, opt, sizeof(opt), 0); /* Uses and sets argc, argv, and some of *cmdlineP and others. */ if (argc-1 == 0) cmdlineP->input_filespec = "-"; else if (argc-1 != 1) pm_error("Program takes zero or one argument (filename). You " "specified %d", argc-1); else cmdlineP->input_filespec = argv[1]; if (cmdlineP->hexcolor && cmdlineP->map) pm_error("You cannot specify both -hexcolor and -map"); if (strcmp(sort_type, "frequency") == 0) cmdlineP->sort = SORT_BY_FREQUENCY; else if (strcmp(sort_type, "rgb") == 0) cmdlineP->sort = SORT_BY_RGB; else pm_error("Invalid -sort value: '%s'. The valid values are " "'frequency' and 'rgb'.", sort_type); } static int countcompare(const void *ch1, const void *ch2) { return ((colorhist_vector)ch2)->value - ((colorhist_vector)ch1)->value; } static int rgbcompare(const void * arg1, const void * arg2) { colorhist_vector const ch1 = (colorhist_vector) arg1; colorhist_vector const ch2 = (colorhist_vector) arg2; int retval; retval = (PPM_GETR(ch1->color) - PPM_GETR(ch2->color)); if (retval == 0) { retval = (PPM_GETG(ch1->color) - PPM_GETG(ch2->color)); if (retval == 0) retval = (PPM_GETB(ch1->color) - PPM_GETB(ch2->color)); } return retval; } static const char * colornameLabel(pixel const color, pixval const maxval) { /*---------------------------------------------------------------------------- Return the name of the color 'color' or the closest color in the dictionary to it. If the name returned is not the exact color, prefix it with "*". Otherwise, prefix it with " ". Return the name in static storage within this subroutine. -----------------------------------------------------------------------------*/ static char retval[32]; char * colorname; colorname = ppm_colorname(&color, maxval, FALSE); if (PPM_EQUAL(ppm_parsecolor(colorname, maxval), color)) STRSCPY(retval, " "); else STRSCPY(retval, "*"); STRSCAT(retval, colorname); return retval; } static void printColors(colorhist_vector const chv, int const nColors, pixval const maxval, bool const hexcolor, bool const colorname, bool const map) { int i; for (i = 0; i < nColors; i++) { printf(hexcolor ? " %04x %04x %04x%s\t%5d\t%7d %s\n" : " %5d %5d %5d%s\t%5d\t%7d %s\n", PPM_GETR(chv[i].color), PPM_GETG(chv[i].color), PPM_GETB(chv[i].color), (map ? " #" : ""), (int) (PPM_LUMIN(chv[i].color) + 0.5), chv[i].value, (colorname ? colornameLabel(chv[i].color, maxval) : "") ); } } int main(int argc, char *argv[] ) { struct cmdline_info cmdline; FILE* ifP; colorhist_vector chv; int rows, cols; pixval maxval; int format; int nColors; int (*compare_function)(const void *, const void *); /* The compare function to be used with qsort() to sort the histogram for output */ ppm_init( &argc, argv ); parse_command_line(argc, argv, &cmdline); ifP = pm_openr(cmdline.input_filespec); ppm_readppminit(ifP, &cols, &rows, &maxval, &format); chv = ppm_computecolorhist2(ifP, cols, rows, maxval, format, 0, &nColors); pm_close(ifP); switch (cmdline.sort) { case SORT_BY_FREQUENCY: compare_function = countcompare; break; case SORT_BY_RGB: compare_function = rgbcompare; break; } qsort((char*) chv, nColors, sizeof(struct colorhist_item), compare_function); /* And print the histogram. */ if (cmdline.map) printf("P3\n# color map\n%d 1\n%d\n", nColors, maxval); if (!cmdline.noheader) { printf("%c r g b \t lum \t count %s\n", cmdline.map ? '#' : ' ', cmdline.colorname ? "name" : ""); printf("%c----- ----- ----- \t-----\t------- %s\n", cmdline.map ? '#' : ' ', cmdline.colorname ? "----" : ""); } printColors( chv, nColors, maxval, cmdline.hexcolor, cmdline.colorname, cmdline.map ); ppm_freecolorhist(chv); exit(0); }