/* ** 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. */ #define _BSD_SOURCE 1 /* Make sure strdup() is in string.h */ #include #include "ppm.h" #include "colorname.h" #include "mallocvar.h" static void computeHexTable(int hexit[]) { int i; for ( i = 0; i < 256; ++i ) hexit[i] = 1234567890; hexit['0'] = 0; hexit['1'] = 1; hexit['2'] = 2; hexit['3'] = 3; hexit['4'] = 4; hexit['5'] = 5; hexit['6'] = 6; hexit['7'] = 7; hexit['8'] = 8; hexit['9'] = 9; hexit['a'] = hexit['A'] = 10; hexit['b'] = hexit['B'] = 11; hexit['c'] = hexit['C'] = 12; hexit['d'] = hexit['D'] = 13; hexit['e'] = hexit['E'] = 14; hexit['f'] = hexit['F'] = 15; } static void parseNewHexX11(const char colorname[], long const lmaxval, long * const rP, long * const gP, long * const bP) { int hexit[256]; const char* cp; long r,g,b; int i; computeHexTable(hexit); cp = colorname + 4; r = g = b = 0; for ( i = 0; *cp != '/'; ++i, ++cp ) r = r * 16 + hexit[(int)*cp]; r = pm_rgbnorm( r, lmaxval, i, colorname ); for ( i = 0, ++cp; *cp != '/'; ++i, ++cp ) g = g * 16 + hexit[(int)*cp]; g = pm_rgbnorm( g, lmaxval, i, colorname ); for ( i = 0, ++cp; *cp != '\0'; ++i, ++cp ) b = b * 16 + hexit[(int)*cp]; b = pm_rgbnorm( b, lmaxval, i, colorname ); if ( r < 0 || r > lmaxval || g < 0 || g > lmaxval || b < 0 || b > lmaxval ) pm_error("invalid color specifier - \"%s\"", colorname ); *rP = r; *gP = g; *bP = b; } static void parseNewDecX11(const char colorname[], long const lmaxval, long * const rP, long * const gP, long * const bP) { float fr, fg, fb; if ( sscanf( colorname, "rgbi:%f/%f/%f", &fr, &fg, &fb ) != 3 ) pm_error("invalid color specifier - \"%s\"", colorname ); if ( fr < 0.0 || fr > 1.0 || fg < 0.0 || fg > 1.0 || fb < 0.0 || fb > 1.0 ) pm_error( "invalid color specifier - \"%s\" - " "values must be between 0.0 and 1.0", colorname ); *rP = fr * lmaxval; *gP = fg * lmaxval; *bP = fb * lmaxval; } static void parseOldX11(const char colorname[], long const lmaxval, long * const rP, long * const gP, long * const bP) { int hexit[256]; long r,g,b; computeHexTable(hexit); switch ( strlen( colorname ) - 1 /* (Number of hex digits) */ ) { case 3: r = hexit[(int)colorname[1]]; g = hexit[(int)colorname[2]]; b = hexit[(int)colorname[3]]; r = pm_rgbnorm( r, lmaxval, 1, colorname ); g = pm_rgbnorm( g, lmaxval, 1, colorname ); b = pm_rgbnorm( b, lmaxval, 1, colorname ); break; case 6: r = ( hexit[(int)colorname[1]] << 4 ) + hexit[(int)colorname[2]]; g = ( hexit[(int)colorname[3]] << 4 ) + hexit[(int)colorname[4]]; b = ( hexit[(int)colorname[5]] << 4 ) + hexit[(int)colorname[6]]; r = pm_rgbnorm( r, lmaxval, 2, colorname ); g = pm_rgbnorm( g, lmaxval, 2, colorname ); b = pm_rgbnorm( b, lmaxval, 2, colorname ); break; case 9: r = ( hexit[(int)colorname[1]] << 8 ) + ( hexit[(int)colorname[2]] << 4 ) + hexit[(int)colorname[3]]; g = ( hexit[(int)colorname[4]] << 8 ) + ( hexit[(int)colorname[5]] << 4 ) + hexit[(int)colorname[6]]; b = ( hexit[(int)colorname[7]] << 8 ) + ( hexit[(int)colorname[8]] << 4 ) + hexit[(int)colorname[9]]; r = pm_rgbnorm( r, lmaxval, 3, colorname ); g = pm_rgbnorm( g, lmaxval, 3, colorname ); b = pm_rgbnorm( b, lmaxval, 3, colorname ); break; case 12: r = ( hexit[(int)colorname[1]] << 12 ) + ( hexit[(int)colorname[2]] << 8 ) + ( hexit[(int)colorname[3]] << 4 ) + hexit[(int)colorname[4]]; g = ( hexit[(int)colorname[5]] << 12 ) + ( hexit[(int)colorname[6]] << 8 ) + ( hexit[(int)colorname[7]] << 4 ) + hexit[(int)colorname[8]]; b = ( hexit[(int)colorname[9]] << 12 ) + ( hexit[(int)colorname[10]] << 8 ) + ( hexit[(int)colorname[11]] << 4 ) + hexit[(int)colorname[12]]; r = pm_rgbnorm( r, lmaxval, 4, colorname ); g = pm_rgbnorm( g, lmaxval, 4, colorname ); b = pm_rgbnorm( b, lmaxval, 4, colorname ); break; default: pm_error("invalid color specifier - \"%s\"", colorname ); } if ( r < 0 || r > lmaxval || g < 0 || g > lmaxval || b < 0 || b > lmaxval ) pm_error("invalid color specifier - \"%s\"", colorname ); *rP = r; *gP = g; *bP = b; } static void parseOldX11Dec(const char colorname[], long const lmaxval, long * const rP, long * const gP, long * const bP) { float fr, fg, fb; if ( sscanf( colorname, "%f,%f,%f", &fr, &fg, &fb ) != 3 ) pm_error("invalid color specifier - \"%s\"", colorname ); if ( fr < 0.0 || fr > 1.0 || fg < 0.0 || fg > 1.0 || fb < 0.0 || fb > 1.0 ) pm_error( "invalid color specifier - \"%s\" - " "values must be between 0.0 and 1.0", colorname ); *rP = fr * lmaxval; *gP = fg * lmaxval; *bP = fb * lmaxval; } pixel ppm_parsecolor(const char * const colorname, pixval const maxval) { pixel p; long lmaxval, r, g, b; lmaxval = maxval; if ( strncmp( colorname, "rgb:", 4 ) == 0 ) /* It's a new-X11-style hexadecimal rgb specifier. */ parseNewHexX11(colorname, lmaxval, &r, &g, &b); else if ( strncmp( colorname, "rgbi:", 5 ) == 0 ) /* It's a new-X11-style decimal/float rgb specifier. */ parseNewDecX11(colorname, lmaxval, &r, &g, &b); else if ( colorname[0] == '#' ) /* It's an old-X11-style hexadecimal rgb specifier. */ parseOldX11(colorname, lmaxval, &r, &g, &b); else if ( ( colorname[0] >= '0' && colorname[0] <= '9' ) || colorname[0] == '.' ) /* It's an old-style decimal/float rgb specifier. */ parseOldX11Dec(colorname, lmaxval, &r, &g, &b); else /* Must be a name from the X-style rgb file. */ pm_parse_dictionary_name(colorname, lmaxval, &r, &g, &b); PPM_ASSIGN( p, r, g, b ); return p; } char* ppm_colorname(const pixel* const colorP, pixval const maxval, int const hexok) { int r, g, b; FILE* f; static char colorname[200]; if ( maxval == 255 ) { r = PPM_GETR( *colorP ); g = PPM_GETG( *colorP ); b = PPM_GETB( *colorP ); } else { r = (int) PPM_GETR( *colorP ) * 255 / (int) maxval; g = (int) PPM_GETG( *colorP ) * 255 / (int) maxval; b = (int) PPM_GETB( *colorP ) * 255 / (int) maxval; } f = pm_openColornameFile(NULL, !hexok); if (f != NULL) { int best_diff, this_diff; bool done; best_diff = 32767; done = FALSE; while ( !done ) { struct colorfile_entry const ce = pm_colorget( f ); if ( ce.colorname ) { this_diff = abs( r - ce.r ) + abs( g - ce.g ) + abs( b - ce.b ); if ( this_diff < best_diff ) { best_diff = this_diff; (void) strcpy( colorname, ce.colorname ); } } else done = TRUE; } fclose( f ); if ( best_diff != 32767 && ( best_diff == 0 || ! hexok ) ) return colorname; } /* Color lookup failed, but caller is willing to take an X11-style hex specifier, so return that. */ sprintf( colorname, "#%02x%02x%02x", r, g, b ); return colorname; } #define MAXCOLORNAMES 1000u static void processColorfileEntry(struct colorfile_entry const ce, colorhash_table const cht, const char ** const colornames, unsigned int * const colornameIndexP) { if (*colornameIndexP >= MAXCOLORNAMES) pm_error("Too many colors in colorname dictionary. " "Max allowed is %u", MAXCOLORNAMES); else { pixel color; PPM_ASSIGN(color, ce.r, ce.g, ce.b); if (ppm_lookupcolor(cht, &color) >= 0) { /* The color is already in the hash, which means we saw it earlier in the file. We prefer the first name that the file gives for each color, so we just ignore the current entry. */ } else { ppm_addtocolorhash(cht, &color, *colornameIndexP); colornames[*colornameIndexP] = strdup(ce.colorname); if (colornames[*colornameIndexP] == NULL) pm_error("Unable to allocate space for color name"); ++(*colornameIndexP); } } } void ppm_readcolornamefile(const char * const fileName, int const mustOpen, colorhash_table * const chtP, const char *** const colornamesP) { colorhash_table cht; const char ** colornames; FILE * colorFile; cht = ppm_alloccolorhash(); MALLOCARRAY(colornames, MAXCOLORNAMES); if (colornames == NULL) pm_error("Unable to allocate space for colorname table."); colorFile = pm_openColornameFile(fileName, mustOpen); if (colorFile != NULL) { unsigned int colornameIndex; bool done; colornameIndex = 0; /* initial value */ done = FALSE; while (!done) { struct colorfile_entry const ce = pm_colorget(colorFile); if (!ce.colorname) done = TRUE; else processColorfileEntry(ce, cht, colornames, &colornameIndex); } while (colornameIndex < MAXCOLORNAMES) colornames[colornameIndex++] = NULL; fclose(colorFile); } *chtP = cht; *colornamesP = colornames; } void ppm_freecolornames(const char ** const colornames) { unsigned int i; for (i = 0; i < MAXCOLORNAMES; ++i) if (colornames[i]) free((char *)colornames[i]); free(colornames); } static unsigned int nonnegative(unsigned int const arg) { if ((int)(arg) < 0) return 0; else return arg; } pixel color_from_ycbcr(unsigned int const y, int const cb, int const cr) { /*---------------------------------------------------------------------------- Return the color that has luminence 'y', blue chrominance 'cb', and red chrominance 'cr'. The 3 input values can be on any scale (as long as it's the same scale for all 3) and the maxval of the returned pixel value is the same as that for the input values. Rounding may cause an output value to be greater than the maxval. -----------------------------------------------------------------------------*/ pixel retval; PPM_ASSIGN(retval, y + 1.4022 * cr, nonnegative(y - 0.7145 * cr - 0.3456 * cb), y + 1.7710 * cb ); return retval; }