blob: 7f38048a8e8b07c8a464253029aa0d1ee4b36f3b [file] [log] [blame]
/*
* jrdppm.c
*
* Copyright (C) 1991, 1992, Thomas G. Lane.
* This file is part of the Independent JPEG Group's software.
* For conditions of distribution and use, see the accompanying README file.
*
* This file contains routines to read input images in PPM format.
* The PBMPLUS library is NOT required to compile this software,
* but it is highly useful as a set of PPM image manipulation programs.
*
* These routines may need modification for non-Unix environments or
* specialized applications. As they stand, they assume input from
* an ordinary stdio stream. They further assume that reading begins
* at the start of the file; input_init may need work if the
* user interface has already read some data (e.g., to determine that
* the file is indeed PPM format).
*
* These routines are invoked via the methods get_input_row
* and input_init/term.
*/
#include "jinclude.h"
#ifdef PPM_SUPPORTED
static JSAMPLE * rescale; /* => maxval-remapping array, or NULL */
/* Portions of this code are based on the PBMPLUS library, which is:
**
** Copyright (C) 1988 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.
*/
LOCAL int
pbm_getc (FILE * file)
/* Read next char, skipping over any comments */
/* A comment/newline sequence is returned as a newline */
{
register int ch;
ch = getc(file);
if (ch == '#') {
do {
ch = getc(file);
} while (ch != '\n' && ch != EOF);
}
return ch;
}
LOCAL unsigned int
read_pbm_integer (compress_info_ptr cinfo)
/* Read an unsigned decimal integer from the PPM file */
/* Swallows one trailing character after the integer */
/* Note that on a 16-bit-int machine, only values up to 64k can be read. */
/* This should not be a problem in practice. */
{
register int ch;
register unsigned int val;
/* Skip any leading whitespace */
do {
ch = pbm_getc(cinfo->input_file);
if (ch == EOF)
ERREXIT(cinfo->emethods, "Premature EOF in PPM file");
} while (ch == ' ' || ch == '\t' || ch == '\n');
if (ch < '0' || ch > '9')
ERREXIT(cinfo->emethods, "Bogus data in PPM file");
val = ch - '0';
while ((ch = pbm_getc(cinfo->input_file)) >= '0' && ch <= '9') {
val *= 10;
val += ch - '0';
}
return val;
}
/*
* Read one row of pixels.
*
* We provide several different versions depending on input file format.
* In all cases, input is scaled to the size of JSAMPLE; it's possible that
* when JSAMPLE is 12 bits, this would not really be desirable.
*
* Note that a really fast path is provided for reading raw files with
* maxval = MAXJSAMPLE, which is the normal case (at least for 8-bit JSAMPLEs).
*/
METHODDEF void
get_text_gray_row (compress_info_ptr cinfo, JSAMPARRAY pixel_row)
/* This version is for reading text-format PGM files with any maxval */
{
register JSAMPROW ptr0;
register unsigned int val;
register long col;
ptr0 = pixel_row[0];
for (col = cinfo->image_width; col > 0; col--) {
val = read_pbm_integer(cinfo);
if (rescale != NULL)
val = rescale[val];
*ptr0++ = (JSAMPLE) val;
}
}
METHODDEF void
get_text_rgb_row (compress_info_ptr cinfo, JSAMPARRAY pixel_row)
/* This version is for reading text-format PPM files with any maxval */
{
register JSAMPROW ptr0, ptr1, ptr2;
register unsigned int val;
register long col;
ptr0 = pixel_row[0];
ptr1 = pixel_row[1];
ptr2 = pixel_row[2];
for (col = cinfo->image_width; col > 0; col--) {
val = read_pbm_integer(cinfo);
if (rescale != NULL)
val = rescale[val];
*ptr0++ = (JSAMPLE) val;
val = read_pbm_integer(cinfo);
if (rescale != NULL)
val = rescale[val];
*ptr1++ = (JSAMPLE) val;
val = read_pbm_integer(cinfo);
if (rescale != NULL)
val = rescale[val];
*ptr2++ = (JSAMPLE) val;
}
}
METHODDEF void
get_scaled_gray_row (compress_info_ptr cinfo, JSAMPARRAY pixel_row)
/* This version is for reading raw-format PGM files with any maxval */
{
register FILE * infile = cinfo->input_file;
register JSAMPROW ptr0;
register long col;
ptr0 = pixel_row[0];
for (col = cinfo->image_width; col > 0; col--) {
*ptr0++ = rescale[getc(infile)];
}
}
METHODDEF void
get_scaled_rgb_row (compress_info_ptr cinfo, JSAMPARRAY pixel_row)
/* This version is for reading raw-format PPM files with any maxval */
{
register FILE * infile = cinfo->input_file;
register JSAMPROW ptr0, ptr1, ptr2;
register long col;
ptr0 = pixel_row[0];
ptr1 = pixel_row[1];
ptr2 = pixel_row[2];
for (col = cinfo->image_width; col > 0; col--) {
*ptr0++ = rescale[getc(infile)];
*ptr1++ = rescale[getc(infile)];
*ptr2++ = rescale[getc(infile)];
}
}
METHODDEF void
get_raw_gray_row (compress_info_ptr cinfo, JSAMPARRAY pixel_row)
/* This version is for reading raw-format PGM files with maxval = MAXJSAMPLE */
{
register FILE * infile = cinfo->input_file;
register JSAMPROW ptr0;
register long col;
ptr0 = pixel_row[0];
for (col = cinfo->image_width; col > 0; col--) {
*ptr0++ = (JSAMPLE) getc(infile);
}
}
METHODDEF void
get_raw_rgb_row (compress_info_ptr cinfo, JSAMPARRAY pixel_row)
/* This version is for reading raw-format PPM files with maxval = MAXJSAMPLE */
{
register FILE * infile = cinfo->input_file;
register JSAMPROW ptr0, ptr1, ptr2;
register long col;
ptr0 = pixel_row[0];
ptr1 = pixel_row[1];
ptr2 = pixel_row[2];
for (col = cinfo->image_width; col > 0; col--) {
*ptr0++ = (JSAMPLE) getc(infile);
*ptr1++ = (JSAMPLE) getc(infile);
*ptr2++ = (JSAMPLE) getc(infile);
}
}
/*
* Read the file header; return image size and component count.
*/
METHODDEF void
input_init (compress_info_ptr cinfo)
{
int c;
unsigned int w, h, maxval;
if (getc(cinfo->input_file) != 'P')
ERREXIT(cinfo->emethods, "Not a PPM file");
c = getc(cinfo->input_file); /* save format discriminator for a sec */
w = read_pbm_integer(cinfo); /* while we fetch the header info */
h = read_pbm_integer(cinfo);
maxval = read_pbm_integer(cinfo);
switch (c) {
case '2': /* it's a text-format PGM file */
cinfo->methods->get_input_row = get_text_gray_row;
cinfo->input_components = 1;
cinfo->in_color_space = CS_GRAYSCALE;
break;
case '3': /* it's a text-format PPM file */
cinfo->methods->get_input_row = get_text_rgb_row;
cinfo->input_components = 3;
cinfo->in_color_space = CS_RGB;
break;
case '5': /* it's a raw-format PGM file */
if (maxval == MAXJSAMPLE)
cinfo->methods->get_input_row = get_raw_gray_row;
else
cinfo->methods->get_input_row = get_scaled_gray_row;
cinfo->input_components = 1;
cinfo->in_color_space = CS_GRAYSCALE;
break;
case '6': /* it's a raw-format PPM file */
if (maxval == MAXJSAMPLE)
cinfo->methods->get_input_row = get_raw_rgb_row;
else
cinfo->methods->get_input_row = get_scaled_rgb_row;
cinfo->input_components = 3;
cinfo->in_color_space = CS_RGB;
break;
default:
ERREXIT(cinfo->emethods, "Not a PPM file");
break;
}
if (w <= 0 || h <= 0 || maxval <= 0) /* error check */
ERREXIT(cinfo->emethods, "Not a PPM file");
/* Compute the rescaling array if necessary */
/* This saves per-pixel calculation */
if (maxval == MAXJSAMPLE)
rescale = NULL; /* no rescaling required */
else {
INT32 val, half_maxval;
/* On 16-bit-int machines we have to be careful of maxval = 65535 */
rescale = (JSAMPLE *) (*cinfo->emethods->alloc_small)
((size_t) (((long) maxval + 1L) * SIZEOF(JSAMPLE)));
half_maxval = maxval / 2;
for (val = 0; val <= (INT32) maxval; val++) {
/* The multiplication here must be done in 32 bits to avoid overflow */
rescale[val] = (JSAMPLE) ((val * MAXJSAMPLE + half_maxval) / maxval);
}
}
cinfo->image_width = w;
cinfo->image_height = h;
cinfo->data_precision = BITS_IN_JSAMPLE;
}
/*
* Finish up at the end of the file.
*/
METHODDEF void
input_term (compress_info_ptr cinfo)
{
/* no work (we let free_all release the workspace) */
}
/*
* The method selection routine for PPM format input.
* Note that this must be called by the user interface before calling
* jpeg_compress. If multiple input formats are supported, the
* user interface is responsible for discovering the file format and
* calling the appropriate method selection routine.
*/
GLOBAL void
jselrppm (compress_info_ptr cinfo)
{
cinfo->methods->input_init = input_init;
/* cinfo->methods->get_input_row is set by input_init */
cinfo->methods->input_term = input_term;
}
#endif /* PPM_SUPPORTED */