460 lines
13 KiB
C
460 lines
13 KiB
C
/*
|
|
* 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
|
|
|
|
|
|
/* 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.
|
|
*/
|
|
|
|
|
|
/* Macros to deal with unsigned chars as efficiently as compiler allows */
|
|
|
|
#ifdef HAVE_UNSIGNED_CHAR
|
|
typedef unsigned char U_CHAR;
|
|
#define UCH(x) ((int) (x))
|
|
#else /* !HAVE_UNSIGNED_CHAR */
|
|
#ifdef CHAR_IS_UNSIGNED
|
|
typedef char U_CHAR;
|
|
#define UCH(x) ((int) (x))
|
|
#else
|
|
typedef char U_CHAR;
|
|
#define UCH(x) ((int) (x) & 0xFF)
|
|
#endif
|
|
#endif /* HAVE_UNSIGNED_CHAR */
|
|
|
|
|
|
#define ReadOK(file,buffer,len) (JFREAD(file,buffer,len) == ((size_t) (len)))
|
|
|
|
|
|
/*
|
|
* On most systems, reading individual bytes with getc() is drastically less
|
|
* efficient than buffering a row at a time with fread(). But we must
|
|
* allocate the row buffer in near data space on PCs, because we are assuming
|
|
* small-data memory model, wherein fread() can't reach far memory. If you
|
|
* need to process very wide images on a PC, you may have to use the getc()
|
|
* approach. In that case, define USE_GETC_INPUT.
|
|
*/
|
|
|
|
#ifndef USE_GETC_INPUT
|
|
static U_CHAR * row_buffer; /* holds 1 pixel row's worth of raw input */
|
|
#endif
|
|
|
|
static JSAMPLE * rescale; /* => maxval-remapping array, or NULL */
|
|
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef USE_GETC_INPUT
|
|
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
|
|
#else /* use row buffering */
|
|
|
|
|
|
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 JSAMPROW ptr0;
|
|
register U_CHAR * row_bufferptr;
|
|
register long col;
|
|
|
|
if (! ReadOK(cinfo->input_file, row_buffer, cinfo->image_width))
|
|
ERREXIT(cinfo->emethods, "Premature EOF in PPM file");
|
|
ptr0 = pixel_row[0];
|
|
row_bufferptr = row_buffer;
|
|
for (col = cinfo->image_width; col > 0; col--) {
|
|
*ptr0++ = rescale[UCH(*row_bufferptr++)];
|
|
}
|
|
}
|
|
|
|
|
|
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 JSAMPROW ptr0, ptr1, ptr2;
|
|
register U_CHAR * row_bufferptr;
|
|
register long col;
|
|
|
|
if (! ReadOK(cinfo->input_file, row_buffer, 3 * cinfo->image_width))
|
|
ERREXIT(cinfo->emethods, "Premature EOF in PPM file");
|
|
ptr0 = pixel_row[0];
|
|
ptr1 = pixel_row[1];
|
|
ptr2 = pixel_row[2];
|
|
row_bufferptr = row_buffer;
|
|
for (col = cinfo->image_width; col > 0; col--) {
|
|
*ptr0++ = rescale[UCH(*row_bufferptr++)];
|
|
*ptr1++ = rescale[UCH(*row_bufferptr++)];
|
|
*ptr2++ = rescale[UCH(*row_bufferptr++)];
|
|
}
|
|
}
|
|
|
|
|
|
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 JSAMPROW ptr0;
|
|
register U_CHAR * row_bufferptr;
|
|
register long col;
|
|
|
|
if (! ReadOK(cinfo->input_file, row_buffer, cinfo->image_width))
|
|
ERREXIT(cinfo->emethods, "Premature EOF in PPM file");
|
|
ptr0 = pixel_row[0];
|
|
row_bufferptr = row_buffer;
|
|
for (col = cinfo->image_width; col > 0; col--) {
|
|
*ptr0++ = (JSAMPLE) UCH(*row_bufferptr++);
|
|
}
|
|
}
|
|
|
|
|
|
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 JSAMPROW ptr0, ptr1, ptr2;
|
|
register U_CHAR * row_bufferptr;
|
|
register long col;
|
|
|
|
if (! ReadOK(cinfo->input_file, row_buffer, 3 * cinfo->image_width))
|
|
ERREXIT(cinfo->emethods, "Premature EOF in PPM file");
|
|
ptr0 = pixel_row[0];
|
|
ptr1 = pixel_row[1];
|
|
ptr2 = pixel_row[2];
|
|
row_bufferptr = row_buffer;
|
|
for (col = cinfo->image_width; col > 0; col--) {
|
|
*ptr0++ = (JSAMPLE) UCH(*row_bufferptr++);
|
|
*ptr1++ = (JSAMPLE) UCH(*row_bufferptr++);
|
|
*ptr2++ = (JSAMPLE) UCH(*row_bufferptr++);
|
|
}
|
|
}
|
|
|
|
|
|
#endif /* USE_GETC_INPUT */
|
|
|
|
|
|
/*
|
|
* 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);
|
|
|
|
if (w <= 0 || h <= 0 || maxval <= 0) /* error check */
|
|
ERREXIT(cinfo->emethods, "Not a PPM file");
|
|
|
|
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;
|
|
TRACEMS2(cinfo->emethods, 1, "%ux%u text PGM image", w, h);
|
|
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;
|
|
TRACEMS2(cinfo->emethods, 1, "%ux%u text PPM image", w, h);
|
|
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;
|
|
#ifndef USE_GETC_INPUT
|
|
/* allocate space for row buffer: 1 byte/pixel */
|
|
row_buffer = (U_CHAR *) (*cinfo->emethods->alloc_small)
|
|
((size_t) (SIZEOF(U_CHAR) * (long) w));
|
|
#endif
|
|
TRACEMS2(cinfo->emethods, 1, "%ux%u PGM image", w, h);
|
|
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;
|
|
#ifndef USE_GETC_INPUT
|
|
/* allocate space for row buffer: 3 bytes/pixel */
|
|
row_buffer = (U_CHAR *) (*cinfo->emethods->alloc_small)
|
|
((size_t) (3 * SIZEOF(U_CHAR) * (long) w));
|
|
#endif
|
|
TRACEMS2(cinfo->emethods, 1, "%ux%u PPM image", w, h);
|
|
break;
|
|
|
|
default:
|
|
ERREXIT(cinfo->emethods, "Not a PPM file");
|
|
break;
|
|
}
|
|
|
|
/* 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 */
|