diff options
author | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2006-08-19 03:12:28 +0000 |
---|---|---|
committer | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2006-08-19 03:12:28 +0000 |
commit | 1fd361a1ea06e44286c213ca1f814f49306fdc43 (patch) | |
tree | 64c8c96cf54d8718847339a403e5e67b922e8c3f /converter/other/pamtouil.c | |
download | netpbm-mirror-1fd361a1ea06e44286c213ca1f814f49306fdc43.tar.gz netpbm-mirror-1fd361a1ea06e44286c213ca1f814f49306fdc43.tar.xz netpbm-mirror-1fd361a1ea06e44286c213ca1f814f49306fdc43.zip |
Create Subversion repository
git-svn-id: http://svn.code.sf.net/p/netpbm/code/trunk@1 9d0c8265-081b-0410-96cb-a4ca84ce46f8
Diffstat (limited to 'converter/other/pamtouil.c')
-rw-r--r-- | converter/other/pamtouil.c | 438 |
1 files changed, 438 insertions, 0 deletions
diff --git a/converter/other/pamtouil.c b/converter/other/pamtouil.c new file mode 100644 index 00000000..b9ddc749 --- /dev/null +++ b/converter/other/pamtouil.c @@ -0,0 +1,438 @@ +/* pamtouil.c - convert PBM, PGM, PPM, or PPM+alpha to Motif UIL icon file +** +** Bryan Henderson converted ppmtouil to pamtouil on 2002.05.04. +** +** Jef Poskanzer derived pamtouil from ppmtoxpm, which is +** Copyright (C) 1990 by Mark W. Snitily. +** +** 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. +*/ + +#define _BSD_SOURCE /* Make sure string.h contains strdup() */ +#define _XOPEN_SOURCE 500 /* Make sure strdup() is in string.h */ +#include <ctype.h> +#include <string.h> +#include "pam.h" +#include "pammap.h" +#include "colorname.h" +#include "shhopt.h" +#include "nstring.h" + +/* Max number of colors allowed in ppm input. */ +#define MAXCOLORS 256 + +/* Lower bound and upper bound of character-pixels printed in UIL output. */ +#define LOW_CHAR '`' +#define HIGH_CHAR '~' + +struct cmdlineInfo { + /* All the information the user supplied in the command line, + in a form easy for the program to use. + */ + const char *inputFilespec; /* Filespecs of input files */ + char *outname; /* output filename, less "_icon" */ + unsigned int verbose; +}; + + + +typedef struct { /* character-pixel mapping */ + const char* cixel; /* character string printed for pixel */ + const char* rgbname; /* ascii rgb color, either mnemonic or #rgb value */ + const char* uilname; /* same, with spaces replaced by underbars */ + bool transparent; +} cixel_map; + + + +static void +parseCommandLine(int argc, char ** argv, + struct cmdlineInfo * const cmdlineP) { +/*---------------------------------------------------------------------------- + Note that the file spec array we return is stored in the storage that + was passed to us as the argv array. The outname array is in newly + malloc'ed storage. +-----------------------------------------------------------------------------*/ + optEntry *option_def = malloc( 100*sizeof( optEntry ) ); + /* Instructions to optParseOptions3 on how to parse our options. + */ + optStruct3 opt; + + unsigned int option_def_index; + unsigned int outnameSpec; + const char *outnameOpt; + + option_def_index = 0; /* incremented by OPTENTRY */ + OPTENT3(0, "name", OPT_STRING, &outnameOpt, + &outnameSpec, 0); + OPTENT3(0, "verbose", OPT_FLAG, NULL, + &cmdlineP->verbose, 0); + + opt.opt_table = option_def; + opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ + opt.allowNegNum = FALSE; /* We may have parms that are negative numbers */ + + optParseOptions3(&argc, argv, opt, sizeof(opt), 0); + /* Uses and sets argc, argv, and some of *cmdlineP and others. */ + + if (argc-1 == 0) + cmdlineP->inputFilespec = "-"; /* stdin */ + else if (argc-1 == 1) + cmdlineP->inputFilespec = argv[1]; + else + pm_error("Too many arguments (%d). " + "Only need one: the input filespec", argc-1); + + if (outnameSpec) { + char * barPos; + + cmdlineP->outname = strdup(outnameOpt); + + /* Remove trailing "_icon" */ + barPos = strrchr(cmdlineP->outname, '_'); + if (STREQ(barPos, "_icon")) + *barPos = '\0'; + } else { + if (STREQ(cmdlineP->inputFilespec, "-")) + cmdlineP->outname = strdup("noname"); + else { + char * dotPos; + + cmdlineP->outname = strdup(cmdlineP->inputFilespec); + + /* remove extension */ + dotPos = strrchr(cmdlineP->outname, '.'); + if (dotPos) + *dotPos = '\0'; + } + } +} + + + + +static char* +genNumstr(int const number, + int const base, + char const low_char, + int const digits ) { +/*---------------------------------------------------------------------------- + Generate a string 'digits' characters long in newly malloc'ed + storage that is the number 'number' displayed in base 'base'. Fill it on + the left with zero digits and truncate on the left if it doesn't fit. + + Use the characters 'low_char' to 'low_char' + 'base' to represent the + digits 0 to 'base'. +-----------------------------------------------------------------------------*/ + char* str; + char* p; + int d; + int i; + + /* Allocate memory for printed number. Abort if error. */ + str = (char*) malloc(digits + 1); + if (str == NULL) + pm_error("out of memory allocating number string"); + + /* Generate characters starting with least significant digit. */ + i = number; + p = str + digits; + *p-- = '\0'; /* nul terminate string */ + while (p >= str) { + d = i % base; + i /= base; + *p-- = low_char + d; + } + return str; +} + + + +static const char * +uilName(const char * const rgbname, bool const transparent) { +/*---------------------------------------------------------------------------- + Return a string in newly malloc'ed storage which is an appropriate + color name for the UIL palette. It is the same as the rgb name, + except that blanks are replaced by underscores, and if 'transparent' + is true, it is "background color". The latter is a strange name of + a color, but it works pretty much the same in the UIL colortable() value. +-----------------------------------------------------------------------------*/ + char * output; + + if (transparent) + output = strdup("background color"); + else { + int i; + + output = malloc(strlen(rgbname) + 5 + 1); + if (output == NULL) + pm_error( "out of memory allocating color name" ); + + for (i = 0; i < strlen(rgbname); ++i) { + if (rgbname[i] == ' ') + output[i] = '_'; + else + output[i] = rgbname[i]; + } + output[strlen(rgbname)] = '\0'; + } + + return output; +} + + + +static void +genCmap(struct pam * const pamP, + tupletable const chv, + unsigned int const ncolors, + cixel_map cmap[MAXCOLORS], + unsigned int * const charsppP, + bool const verbose) { + + unsigned int const base = (int) HIGH_CHAR - (int) LOW_CHAR + 1; + char* colorname; + unsigned int colorIndex; + { + /* Figure out how many characters per pixel we'll be using. + ** Don't want to be forced to link with libm.a, so using a + ** division loop rather than a log function. + */ + unsigned int i; + for (*charsppP = 0, i = ncolors; i > 0; ++(*charsppP)) + i /= base; + } + + /* Generate the character-pixel string and the rgb name for each colormap + ** entry. + */ + for (colorIndex = 0; colorIndex < ncolors; ++colorIndex) { + bool nameAlreadyInCmap; + unsigned int indexOfName; + bool transparent; + int j; + + if (pamP->depth-1 < PAM_TRN_PLANE) + transparent = FALSE; + else + transparent = + chv[colorIndex]->tuple[PAM_TRN_PLANE] < pamP->maxval/2; + + /* Generate color name string. */ + colorname = pam_colorname(pamP, chv[colorIndex]->tuple, + PAM_COLORNAME_ENGLISH); + + /* We may have already assigned a character code to this color + name/transparency because the same color name can apply to + two different colors because we said we wanted the closest + matching color that has an English name, and we recognize + only one transparent color. If that's the case, we just + make a cross-reference. + */ + nameAlreadyInCmap = FALSE; /* initial assumption */ + for (j = 0; j < colorIndex; ++j) { + if (cmap[j].rgbname != NULL && + STREQ(colorname, cmap[j].rgbname) && + cmap[j].transparent == transparent) { + nameAlreadyInCmap = TRUE; + indexOfName = j; + } + } + if (nameAlreadyInCmap) { + /* Make the entry a cross-reference to the earlier entry */ + cmap[colorIndex].uilname = NULL; + cmap[colorIndex].rgbname = NULL; + cmap[colorIndex].cixel = cmap[indexOfName].cixel; + } else { + cmap[colorIndex].uilname = uilName(colorname, transparent); + cmap[colorIndex].rgbname = strdup(colorname); + if (cmap[colorIndex].rgbname == NULL) + pm_error( "out of memory allocating color name" ); + + cmap[colorIndex].transparent = transparent; + + /* Generate color value characters. */ + cmap[colorIndex].cixel = + genNumstr(colorIndex, base, LOW_CHAR, *charsppP); + if (verbose) + pm_message("Adding color '%s' %s = '%s' to UIL colormap", + cmap[colorIndex].rgbname, + cmap[colorIndex].transparent ? "TRANS" : "OPAQUE", + cmap[colorIndex].cixel); + } + } +} + + + +static void +writeUilHeader(const char * const outname) { + /* Write out the UIL header. */ + printf( "module %s\n", outname ); + printf( "version = 'V1.0'\n" ); + printf( "names = case_sensitive\n" ); + printf( "include file 'XmAppl.uil';\n" ); +} + + + +static void +writeColormap(const char * const outname, + cixel_map cmap[MAXCOLORS], + unsigned int const ncolors) { + + { + int i; + + /* Write out the colors. */ + printf("\n"); + printf("value\n"); + for (i = 0; i < ncolors; ++i) + if (cmap[i].uilname != NULL && !cmap[i].transparent) + printf(" %s : color( '%s' );\n", + cmap[i].uilname, cmap[i].rgbname ); + } + { + /* Write out the ascii colormap. */ + + int i; + bool printedOne; + + printf("\n"); + printf("value\n"); + printf(" %s_rgb : color_table (\n", outname); + printedOne = FALSE; + for (i = 0; i < ncolors; ++i) + if (cmap[i].uilname != NULL) { + if (printedOne) + printf(",\n"); + printf(" %s = '%s'", cmap[i].uilname, cmap[i].cixel); + printedOne = TRUE; + } + printf("\n"); + printf(" );\n"); + } +} + + + +static void +writeRaster(struct pam * const pamP, + tuple ** const tuples, + const char * const outname, + cixel_map cmap[MAXCOLORS], + unsigned int const ncolors, + tuplehash const cht, + unsigned int const charspp) { + + int row; + /* Write out the ascii character-pixel image. */ + + printf("\n"); + printf("%s_icon : exported icon( color_table = %s_rgb,\n", + outname, outname); + for (row = 0; row < pamP->height; ++row) { + int col; + + printf(" '"); + for (col = 0; col < pamP->width; ++col) { + int colorIndex; + int found; + if ((col * charspp) % 70 == 0 && col > 0) + printf( "\\\n" ); /* line continuation */ + pnm_lookuptuple(pamP, cht, tuples[row][col], &found, &colorIndex); + if (!found) + pm_error("INTERNAL ERROR: color not found in colormap"); + printf("%s", cmap[colorIndex].cixel); + } + if (row != pamP->height - 1) + printf("',\n"); + else + printf("'\n"); + } + printf(");\n"); + printf("\n"); +} + + +static void +freeString(const char * const s) { +/*---------------------------------------------------------------------------- + This is just free(), but with type checking for const char *. +-----------------------------------------------------------------------------*/ + free((void *)s); +} + + + +static void +freeCmap(cixel_map cmap[], unsigned int const ncolors) { + + int i; + + for (i = 0; i < ncolors; ++i) { + cixel_map const cmapEntry = cmap[i]; + if (cmapEntry.uilname) + freeString(cmapEntry.uilname); + if (cmapEntry.rgbname) + freeString(cmapEntry.uilname); + } +} + + + +int +main(int argc, char *argv[]) { + + struct cmdlineInfo cmdline; + struct pam pam; /* Input PAM image */ + FILE* ifP; + tuple** tuples; + unsigned int ncolors; + tuplehash cht; + tupletable chv; + cixel_map cmap[MAXCOLORS]; + unsigned int charspp; + + pnm_init(&argc, argv); + + parseCommandLine(argc, argv, &cmdline); + + ifP = pm_openr(cmdline.inputFilespec); + tuples = pnm_readpam(ifP, &pam, PAM_STRUCT_SIZE(tuple_type)); + pm_close(ifP); + + pm_message("computing colormap..."); + + chv = pnm_computetuplefreqtable(&pam, tuples, MAXCOLORS, &ncolors); + if (chv == NULL) + pm_error("too many colors - try doing a 'pnmquant %u'", MAXCOLORS); + if (cmdline.verbose) + pm_message("%u colors found", ncolors); + + /* Make a hash table for fast color lookup. */ + cht = pnm_computetupletablehash(&pam, chv, ncolors); + + /* Now generate the character-pixel colormap table. */ + pm_message("looking up color names, assigning character codes..."); + genCmap(&pam, chv, ncolors, cmap, &charspp, cmdline.verbose); + + pm_message("generating UIL..."); + writeUilHeader(cmdline.outname); + + writeColormap(cmdline.outname, cmap, ncolors); + + writeRaster(&pam, tuples, cmdline.outname, cmap, ncolors, cht, charspp); + + printf("end module;\n"); + + free(cmdline.outname); + freeCmap(cmap, ncolors); + + return 0; +} |