xref: /plan9/sys/src/cmd/gs/jpeg/rdcolmap.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
17dd7cddfSDavid du Colombier /*
27dd7cddfSDavid du Colombier  * rdcolmap.c
37dd7cddfSDavid du Colombier  *
47dd7cddfSDavid du Colombier  * Copyright (C) 1994-1996, Thomas G. Lane.
57dd7cddfSDavid du Colombier  * This file is part of the Independent JPEG Group's software.
67dd7cddfSDavid du Colombier  * For conditions of distribution and use, see the accompanying README file.
77dd7cddfSDavid du Colombier  *
87dd7cddfSDavid du Colombier  * This file implements djpeg's "-map file" switch.  It reads a source image
97dd7cddfSDavid du Colombier  * and constructs a colormap to be supplied to the JPEG decompressor.
107dd7cddfSDavid du Colombier  *
117dd7cddfSDavid du Colombier  * Currently, these file formats are supported for the map file:
127dd7cddfSDavid du Colombier  *   GIF: the contents of the GIF's global colormap are used.
137dd7cddfSDavid du Colombier  *   PPM (either text or raw flavor): the entire file is read and
147dd7cddfSDavid du Colombier  *      each unique pixel value is entered in the map.
157dd7cddfSDavid du Colombier  * Note that reading a large PPM file will be horrendously slow.
167dd7cddfSDavid du Colombier  * Typically, a PPM-format map file should contain just one pixel
177dd7cddfSDavid du Colombier  * of each desired color.  Such a file can be extracted from an
187dd7cddfSDavid du Colombier  * ordinary image PPM file with ppmtomap(1).
197dd7cddfSDavid du Colombier  *
207dd7cddfSDavid du Colombier  * Rescaling a PPM that has a maxval unequal to MAXJSAMPLE is not
217dd7cddfSDavid du Colombier  * currently implemented.
227dd7cddfSDavid du Colombier  */
237dd7cddfSDavid du Colombier 
247dd7cddfSDavid du Colombier #include "cdjpeg.h"		/* Common decls for cjpeg/djpeg applications */
257dd7cddfSDavid du Colombier 
267dd7cddfSDavid du Colombier #ifdef QUANT_2PASS_SUPPORTED	/* otherwise can't quantize to supplied map */
277dd7cddfSDavid du Colombier 
287dd7cddfSDavid du Colombier /* Portions of this code are based on the PBMPLUS library, which is:
297dd7cddfSDavid du Colombier **
307dd7cddfSDavid du Colombier ** Copyright (C) 1988 by Jef Poskanzer.
317dd7cddfSDavid du Colombier **
327dd7cddfSDavid du Colombier ** Permission to use, copy, modify, and distribute this software and its
337dd7cddfSDavid du Colombier ** documentation for any purpose and without fee is hereby granted, provided
347dd7cddfSDavid du Colombier ** that the above copyright notice appear in all copies and that both that
357dd7cddfSDavid du Colombier ** copyright notice and this permission notice appear in supporting
367dd7cddfSDavid du Colombier ** documentation.  This software is provided "as is" without express or
377dd7cddfSDavid du Colombier ** implied warranty.
387dd7cddfSDavid du Colombier */
397dd7cddfSDavid du Colombier 
407dd7cddfSDavid du Colombier 
417dd7cddfSDavid du Colombier /*
427dd7cddfSDavid du Colombier  * Add a (potentially) new color to the color map.
437dd7cddfSDavid du Colombier  */
447dd7cddfSDavid du Colombier 
457dd7cddfSDavid du Colombier LOCAL(void)
add_map_entry(j_decompress_ptr cinfo,int R,int G,int B)467dd7cddfSDavid du Colombier add_map_entry (j_decompress_ptr cinfo, int R, int G, int B)
477dd7cddfSDavid du Colombier {
487dd7cddfSDavid du Colombier   JSAMPROW colormap0 = cinfo->colormap[0];
497dd7cddfSDavid du Colombier   JSAMPROW colormap1 = cinfo->colormap[1];
507dd7cddfSDavid du Colombier   JSAMPROW colormap2 = cinfo->colormap[2];
517dd7cddfSDavid du Colombier   int ncolors = cinfo->actual_number_of_colors;
527dd7cddfSDavid du Colombier   int index;
537dd7cddfSDavid du Colombier 
547dd7cddfSDavid du Colombier   /* Check for duplicate color. */
557dd7cddfSDavid du Colombier   for (index = 0; index < ncolors; index++) {
567dd7cddfSDavid du Colombier     if (GETJSAMPLE(colormap0[index]) == R &&
577dd7cddfSDavid du Colombier 	GETJSAMPLE(colormap1[index]) == G &&
587dd7cddfSDavid du Colombier 	GETJSAMPLE(colormap2[index]) == B)
597dd7cddfSDavid du Colombier       return;			/* color is already in map */
607dd7cddfSDavid du Colombier   }
617dd7cddfSDavid du Colombier 
627dd7cddfSDavid du Colombier   /* Check for map overflow. */
637dd7cddfSDavid du Colombier   if (ncolors >= (MAXJSAMPLE+1))
647dd7cddfSDavid du Colombier     ERREXIT1(cinfo, JERR_QUANT_MANY_COLORS, (MAXJSAMPLE+1));
657dd7cddfSDavid du Colombier 
667dd7cddfSDavid du Colombier   /* OK, add color to map. */
677dd7cddfSDavid du Colombier   colormap0[ncolors] = (JSAMPLE) R;
687dd7cddfSDavid du Colombier   colormap1[ncolors] = (JSAMPLE) G;
697dd7cddfSDavid du Colombier   colormap2[ncolors] = (JSAMPLE) B;
707dd7cddfSDavid du Colombier   cinfo->actual_number_of_colors++;
717dd7cddfSDavid du Colombier }
727dd7cddfSDavid du Colombier 
737dd7cddfSDavid du Colombier 
747dd7cddfSDavid du Colombier /*
757dd7cddfSDavid du Colombier  * Extract color map from a GIF file.
767dd7cddfSDavid du Colombier  */
777dd7cddfSDavid du Colombier 
787dd7cddfSDavid du Colombier LOCAL(void)
read_gif_map(j_decompress_ptr cinfo,FILE * infile)797dd7cddfSDavid du Colombier read_gif_map (j_decompress_ptr cinfo, FILE * infile)
807dd7cddfSDavid du Colombier {
817dd7cddfSDavid du Colombier   int header[13];
827dd7cddfSDavid du Colombier   int i, colormaplen;
837dd7cddfSDavid du Colombier   int R, G, B;
847dd7cddfSDavid du Colombier 
857dd7cddfSDavid du Colombier   /* Initial 'G' has already been read by read_color_map */
867dd7cddfSDavid du Colombier   /* Read the rest of the GIF header and logical screen descriptor */
877dd7cddfSDavid du Colombier   for (i = 1; i < 13; i++) {
887dd7cddfSDavid du Colombier     if ((header[i] = getc(infile)) == EOF)
897dd7cddfSDavid du Colombier       ERREXIT(cinfo, JERR_BAD_CMAP_FILE);
907dd7cddfSDavid du Colombier   }
917dd7cddfSDavid du Colombier 
927dd7cddfSDavid du Colombier   /* Verify GIF Header */
937dd7cddfSDavid du Colombier   if (header[1] != 'I' || header[2] != 'F')
947dd7cddfSDavid du Colombier     ERREXIT(cinfo, JERR_BAD_CMAP_FILE);
957dd7cddfSDavid du Colombier 
967dd7cddfSDavid du Colombier   /* There must be a global color map. */
977dd7cddfSDavid du Colombier   if ((header[10] & 0x80) == 0)
987dd7cddfSDavid du Colombier     ERREXIT(cinfo, JERR_BAD_CMAP_FILE);
997dd7cddfSDavid du Colombier 
1007dd7cddfSDavid du Colombier   /* OK, fetch it. */
1017dd7cddfSDavid du Colombier   colormaplen = 2 << (header[10] & 0x07);
1027dd7cddfSDavid du Colombier 
1037dd7cddfSDavid du Colombier   for (i = 0; i < colormaplen; i++) {
1047dd7cddfSDavid du Colombier     R = getc(infile);
1057dd7cddfSDavid du Colombier     G = getc(infile);
1067dd7cddfSDavid du Colombier     B = getc(infile);
1077dd7cddfSDavid du Colombier     if (R == EOF || G == EOF || B == EOF)
1087dd7cddfSDavid du Colombier       ERREXIT(cinfo, JERR_BAD_CMAP_FILE);
1097dd7cddfSDavid du Colombier     add_map_entry(cinfo,
1107dd7cddfSDavid du Colombier 		  R << (BITS_IN_JSAMPLE-8),
1117dd7cddfSDavid du Colombier 		  G << (BITS_IN_JSAMPLE-8),
1127dd7cddfSDavid du Colombier 		  B << (BITS_IN_JSAMPLE-8));
1137dd7cddfSDavid du Colombier   }
1147dd7cddfSDavid du Colombier }
1157dd7cddfSDavid du Colombier 
1167dd7cddfSDavid du Colombier 
1177dd7cddfSDavid du Colombier /* Support routines for reading PPM */
1187dd7cddfSDavid du Colombier 
1197dd7cddfSDavid du Colombier 
1207dd7cddfSDavid du Colombier LOCAL(int)
pbm_getc(FILE * infile)1217dd7cddfSDavid du Colombier pbm_getc (FILE * infile)
1227dd7cddfSDavid du Colombier /* Read next char, skipping over any comments */
1237dd7cddfSDavid du Colombier /* A comment/newline sequence is returned as a newline */
1247dd7cddfSDavid du Colombier {
1257dd7cddfSDavid du Colombier   register int ch;
1267dd7cddfSDavid du Colombier 
1277dd7cddfSDavid du Colombier   ch = getc(infile);
1287dd7cddfSDavid du Colombier   if (ch == '#') {
1297dd7cddfSDavid du Colombier     do {
1307dd7cddfSDavid du Colombier       ch = getc(infile);
1317dd7cddfSDavid du Colombier     } while (ch != '\n' && ch != EOF);
1327dd7cddfSDavid du Colombier   }
1337dd7cddfSDavid du Colombier   return ch;
1347dd7cddfSDavid du Colombier }
1357dd7cddfSDavid du Colombier 
1367dd7cddfSDavid du Colombier 
1377dd7cddfSDavid du Colombier LOCAL(unsigned int)
read_pbm_integer(j_decompress_ptr cinfo,FILE * infile)1387dd7cddfSDavid du Colombier read_pbm_integer (j_decompress_ptr cinfo, FILE * infile)
1397dd7cddfSDavid du Colombier /* Read an unsigned decimal integer from the PPM file */
1407dd7cddfSDavid du Colombier /* Swallows one trailing character after the integer */
1417dd7cddfSDavid du Colombier /* Note that on a 16-bit-int machine, only values up to 64k can be read. */
1427dd7cddfSDavid du Colombier /* This should not be a problem in practice. */
1437dd7cddfSDavid du Colombier {
1447dd7cddfSDavid du Colombier   register int ch;
1457dd7cddfSDavid du Colombier   register unsigned int val;
1467dd7cddfSDavid du Colombier 
1477dd7cddfSDavid du Colombier   /* Skip any leading whitespace */
1487dd7cddfSDavid du Colombier   do {
1497dd7cddfSDavid du Colombier     ch = pbm_getc(infile);
1507dd7cddfSDavid du Colombier     if (ch == EOF)
1517dd7cddfSDavid du Colombier       ERREXIT(cinfo, JERR_BAD_CMAP_FILE);
1527dd7cddfSDavid du Colombier   } while (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r');
1537dd7cddfSDavid du Colombier 
1547dd7cddfSDavid du Colombier   if (ch < '0' || ch > '9')
1557dd7cddfSDavid du Colombier     ERREXIT(cinfo, JERR_BAD_CMAP_FILE);
1567dd7cddfSDavid du Colombier 
1577dd7cddfSDavid du Colombier   val = ch - '0';
1587dd7cddfSDavid du Colombier   while ((ch = pbm_getc(infile)) >= '0' && ch <= '9') {
1597dd7cddfSDavid du Colombier     val *= 10;
1607dd7cddfSDavid du Colombier     val += ch - '0';
1617dd7cddfSDavid du Colombier   }
1627dd7cddfSDavid du Colombier   return val;
1637dd7cddfSDavid du Colombier }
1647dd7cddfSDavid du Colombier 
1657dd7cddfSDavid du Colombier 
1667dd7cddfSDavid du Colombier /*
1677dd7cddfSDavid du Colombier  * Extract color map from a PPM file.
1687dd7cddfSDavid du Colombier  */
1697dd7cddfSDavid du Colombier 
1707dd7cddfSDavid du Colombier LOCAL(void)
read_ppm_map(j_decompress_ptr cinfo,FILE * infile)1717dd7cddfSDavid du Colombier read_ppm_map (j_decompress_ptr cinfo, FILE * infile)
1727dd7cddfSDavid du Colombier {
1737dd7cddfSDavid du Colombier   int c;
1747dd7cddfSDavid du Colombier   unsigned int w, h, maxval, row, col;
1757dd7cddfSDavid du Colombier   int R, G, B;
1767dd7cddfSDavid du Colombier 
1777dd7cddfSDavid du Colombier   /* Initial 'P' has already been read by read_color_map */
1787dd7cddfSDavid du Colombier   c = getc(infile);		/* save format discriminator for a sec */
1797dd7cddfSDavid du Colombier 
1807dd7cddfSDavid du Colombier   /* while we fetch the remaining header info */
1817dd7cddfSDavid du Colombier   w = read_pbm_integer(cinfo, infile);
1827dd7cddfSDavid du Colombier   h = read_pbm_integer(cinfo, infile);
1837dd7cddfSDavid du Colombier   maxval = read_pbm_integer(cinfo, infile);
1847dd7cddfSDavid du Colombier 
1857dd7cddfSDavid du Colombier   if (w <= 0 || h <= 0 || maxval <= 0) /* error check */
1867dd7cddfSDavid du Colombier     ERREXIT(cinfo, JERR_BAD_CMAP_FILE);
1877dd7cddfSDavid du Colombier 
1887dd7cddfSDavid du Colombier   /* For now, we don't support rescaling from an unusual maxval. */
1897dd7cddfSDavid du Colombier   if (maxval != (unsigned int) MAXJSAMPLE)
1907dd7cddfSDavid du Colombier     ERREXIT(cinfo, JERR_BAD_CMAP_FILE);
1917dd7cddfSDavid du Colombier 
1927dd7cddfSDavid du Colombier   switch (c) {
1937dd7cddfSDavid du Colombier   case '3':			/* it's a text-format PPM file */
1947dd7cddfSDavid du Colombier     for (row = 0; row < h; row++) {
1957dd7cddfSDavid du Colombier       for (col = 0; col < w; col++) {
1967dd7cddfSDavid du Colombier 	R = read_pbm_integer(cinfo, infile);
1977dd7cddfSDavid du Colombier 	G = read_pbm_integer(cinfo, infile);
1987dd7cddfSDavid du Colombier 	B = read_pbm_integer(cinfo, infile);
1997dd7cddfSDavid du Colombier 	add_map_entry(cinfo, R, G, B);
2007dd7cddfSDavid du Colombier       }
2017dd7cddfSDavid du Colombier     }
2027dd7cddfSDavid du Colombier     break;
2037dd7cddfSDavid du Colombier 
2047dd7cddfSDavid du Colombier   case '6':			/* it's a raw-format PPM file */
2057dd7cddfSDavid du Colombier     for (row = 0; row < h; row++) {
2067dd7cddfSDavid du Colombier       for (col = 0; col < w; col++) {
207*593dc095SDavid du Colombier 	R = getc(infile);
208*593dc095SDavid du Colombier 	G = getc(infile);
209*593dc095SDavid du Colombier 	B = getc(infile);
2107dd7cddfSDavid du Colombier 	if (R == EOF || G == EOF || B == EOF)
2117dd7cddfSDavid du Colombier 	  ERREXIT(cinfo, JERR_BAD_CMAP_FILE);
2127dd7cddfSDavid du Colombier 	add_map_entry(cinfo, R, G, B);
2137dd7cddfSDavid du Colombier       }
2147dd7cddfSDavid du Colombier     }
2157dd7cddfSDavid du Colombier     break;
2167dd7cddfSDavid du Colombier 
2177dd7cddfSDavid du Colombier   default:
2187dd7cddfSDavid du Colombier     ERREXIT(cinfo, JERR_BAD_CMAP_FILE);
2197dd7cddfSDavid du Colombier     break;
2207dd7cddfSDavid du Colombier   }
2217dd7cddfSDavid du Colombier }
2227dd7cddfSDavid du Colombier 
2237dd7cddfSDavid du Colombier 
2247dd7cddfSDavid du Colombier /*
2257dd7cddfSDavid du Colombier  * Main entry point from djpeg.c.
2267dd7cddfSDavid du Colombier  *  Input: opened input file (from file name argument on command line).
2277dd7cddfSDavid du Colombier  *  Output: colormap and actual_number_of_colors fields are set in cinfo.
2287dd7cddfSDavid du Colombier  */
2297dd7cddfSDavid du Colombier 
2307dd7cddfSDavid du Colombier GLOBAL(void)
read_color_map(j_decompress_ptr cinfo,FILE * infile)2317dd7cddfSDavid du Colombier read_color_map (j_decompress_ptr cinfo, FILE * infile)
2327dd7cddfSDavid du Colombier {
2337dd7cddfSDavid du Colombier   /* Allocate space for a color map of maximum supported size. */
2347dd7cddfSDavid du Colombier   cinfo->colormap = (*cinfo->mem->alloc_sarray)
2357dd7cddfSDavid du Colombier     ((j_common_ptr) cinfo, JPOOL_IMAGE,
2367dd7cddfSDavid du Colombier      (JDIMENSION) (MAXJSAMPLE+1), (JDIMENSION) 3);
2377dd7cddfSDavid du Colombier   cinfo->actual_number_of_colors = 0; /* initialize map to empty */
2387dd7cddfSDavid du Colombier 
2397dd7cddfSDavid du Colombier   /* Read first byte to determine file format */
2407dd7cddfSDavid du Colombier   switch (getc(infile)) {
2417dd7cddfSDavid du Colombier   case 'G':
2427dd7cddfSDavid du Colombier     read_gif_map(cinfo, infile);
2437dd7cddfSDavid du Colombier     break;
2447dd7cddfSDavid du Colombier   case 'P':
2457dd7cddfSDavid du Colombier     read_ppm_map(cinfo, infile);
2467dd7cddfSDavid du Colombier     break;
2477dd7cddfSDavid du Colombier   default:
2487dd7cddfSDavid du Colombier     ERREXIT(cinfo, JERR_BAD_CMAP_FILE);
2497dd7cddfSDavid du Colombier     break;
2507dd7cddfSDavid du Colombier   }
2517dd7cddfSDavid du Colombier }
2527dd7cddfSDavid du Colombier 
2537dd7cddfSDavid du Colombier #endif /* QUANT_2PASS_SUPPORTED */
254