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