povray/source/base/image/pgm.cpp
2013-11-06 13:07:19 -05:00

280 lines
8.5 KiB
C++

/*******************************************************************************
* pgm.cpp
*
* This module contains the code to read the PGM file format.
*
* ---------------------------------------------------------------------------
* Persistence of Vision Ray Tracer ('POV-Ray') version 3.7.
* Copyright 1991-2013 Persistence of Vision Raytracer Pty. Ltd.
*
* POV-Ray is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* POV-Ray is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* ---------------------------------------------------------------------------
* POV-Ray is based on the popular DKB raytracer version 2.12.
* DKBTrace was originally written by David K. Buck.
* DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
* ---------------------------------------------------------------------------
* $File: //depot/public/povray/3.x/source/base/image/pgm.cpp $
* $Revision: #1 $
* $Change: 6069 $
* $DateTime: 2013/11/06 11:59:40 $
* $Author: chrisc $
*******************************************************************************/
/****************************************************************************
*
* PGM format according to NetPBM specs (http://netpbm.sourceforge.net/doc/):
*
* This module implements read support for PGM (grayscale) image maps.
*
* Both ASCII and binary files are supported ('P2' and 'P5')
* in 8 and 16 bit color depth.
*
*****************************************************************************/
#include <vector>
#include <ctype.h>
// configbase.h must always be the first POV file included within base *.cpp files
#include "base/configbase.h"
#include "base/image/image.h"
#include "base/image/pgm.h"
// this must be the last file included
#include "base/povdebug.h"
namespace pov_base
{
namespace Pgm
{
/*****************************************************************************
*
* FUNCTION
*
* Read_ASCII_File_Number
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
* Christoph Hormann
*
* DESCRIPTION
*
* Reads an integer number from an ASCII file skipping whitespaces
*
* CHANGES
*
* August 2003 - Creation
*
******************************************************************************/
int Read_ASCII_File_Number(IStream *file)
{
int value;
int pos = 0;
char buffer[50] = "";
do
{
value = file->Read_Byte();
} while (isspace(value));
if (!isdigit (value))
throw POV_EXCEPTION(kFileDataErr, "Invalid data in PGM/PPM file");
buffer[pos] = (char)value;
while (pos < 48)
{
if ((value = file->Read_Byte()) == EOF)
break ;
if (isspace(value))
break ;
if (!isdigit(value))
throw POV_EXCEPTION(kFileDataErr, "Invalid data in PGM/PPM file");
buffer[++pos] = (char)value;
}
buffer[pos+1] = '\0';
value = atoi(buffer);
return value;
}
/*****************************************************************************
*
* FUNCTION
*
* Read_PGM_Image
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
* Christoph Hormann
*
* DESCRIPTION
*
* Reads an PGM image file
*
* CHANGES
*
* August 2003 - New implementation based on targa/png reading code
*
******************************************************************************/
// TODO: make sure we destroy the image if we throw an exception
Image *Read (IStream *file, const Image::ReadOptions& options)
{
int width;
int height;
int data_hi; // not unsigned as it may need to hold EOF
int data_lo; // not unsigned as it may need to hold EOF
char line[1024];
char *ptr;
Image *image = NULL;
unsigned int depth;
int v; // not unsigned as it may need to hold EOF
unsigned char header[2];
// PGM files may or may not be gamma-encoded.
GammaCurvePtr gamma;
if (options.gammacorrect && options.defaultGamma)
gamma = TranscodingGammaCurve::Get(options.workingGamma, options.defaultGamma);
// --- Read Header ---
if (!file->read(header, 2))
throw POV_EXCEPTION(kFileDataErr, "Cannot read header of PGM image");
if(header[0] != 'P')
throw POV_EXCEPTION(kFileDataErr, "File is not in PGM format");
if ((header[1] != '2') && (header[1] != '5'))
throw POV_EXCEPTION(kFileDataErr, "File is not in PGM format");
// TODO FIXME - Some valid PPM files may have a different header layout regarding line breaks
do
{
file->getline (line, 1024);
line[1023] = '\0';
if ((ptr = strchr(line, '#')) != NULL)
*ptr = '\0'; // remove comment
} while (line[0]=='\0'); // read until line without comment from beginning
// --- First: two numbers: width and height ---
if (sscanf(line,"%d %d",&width, &height) != 2)
throw POV_EXCEPTION(kFileDataErr, "Cannot read width and height from PGM image");
if (width <= 0 || height <= 0)
throw POV_EXCEPTION(kFileDataErr, "Invalid width or height read from PGM image");
do
{
file->getline (line, 1024) ;
line[1023] = '\0';
if ((ptr = strchr(line, '#')) != NULL)
*ptr = '\0'; // remove comment
} while (line[0]=='\0'); // read until line without comment from beginning
// --- Second: one number: color depth ---
if (sscanf(line,"%u",&depth) != 1)
throw POV_EXCEPTION(kFileDataErr, "Cannot read color depth from PGM image");
if ((depth > 65535) || (depth < 1))
throw POV_EXCEPTION(kFileDataErr, "Unsupported number of colors in PGM image");
if (depth < 256)
{
// We'll be using an image container that provides for automatic decoding if possible - unless there's no such decoding to do.
gamma = ScaledGammaCurve::GetByDecoding(255.0f/depth, gamma); // Note that we'll apply the scaling even if we don't officially gamma-correct
Image::ImageDataType imagetype = options.itype;
if (imagetype == Image::Undefined)
imagetype = ( GammaCurve::IsNeutral(gamma) ? Image::Gray_Int8 : Image::Gray_Gamma8 );
image = Image::Create (width, height, imagetype) ;
// NB: PGM files don't use alpha, so premultiplied vs. non-premultiplied is not an issue
image->TryDeferDecoding(gamma, 255); // try to have gamma adjustment being deferred until image evaluation.
for (int i = 0; i < height; i++)
{
if (header[1] == '2') // --- ASCII PGM file (type 2) ---
{
for (int x = 0; x < width; x++)
{
v = Read_ASCII_File_Number(file);
SetEncodedGrayValue (image, x, i, gamma, 255, v) ;
}
}
else // --- binary PGM file (type 5) ---
{
for (int x = 0; x < width; x++)
{
if ((v = file->Read_Byte ()) == EOF)
throw POV_EXCEPTION(kFileDataErr, "Unexpected EOF in PGM file");
SetEncodedGrayValue (image, x, i, gamma, 255, v) ;
}
}
}
}
else // --- 16 bit PGM (binary or ASCII) ---
{
// We'll be using an image container that provides for automatic decoding if possible - unless there's no such decoding to do.
gamma = ScaledGammaCurve::GetByDecoding(65535.0f/depth, gamma); // Note that we'll apply the scaling even if we don't officially gamma-correct
Image::ImageDataType imagetype = options.itype;
if (imagetype == Image::Undefined)
imagetype = ( GammaCurve::IsNeutral(gamma) ? Image::Gray_Int16 : Image::Gray_Gamma16 );
image = Image::Create (width, height, imagetype) ;
// NB: PGM files don't use alpha, so premultiplied vs. non-premultiplied is not an issue
image->TryDeferDecoding(gamma, 65535); // try to have gamma adjustment being deferred until image evaluation.
for (int i = 0; i < height; i++)
{
if (header[1] == '2') // --- ASCII PGM file (type 2) ---
{
for (int x = 0; x < width; x++)
{
v = Read_ASCII_File_Number(file);
SetEncodedGrayValue (image, x, i, gamma, 65535, v);
}
}
else // --- binary PGM file (type 5) ---
{
for (int x = 0; x < width; x++)
{
if ((data_hi = file->Read_Byte ()) == EOF)
throw POV_EXCEPTION(kFileDataErr, "Unexpected EOF in PGM file");
if ((data_lo = file->Read_Byte ()) == EOF)
throw POV_EXCEPTION(kFileDataErr, "Unexpected EOF in PGM file");
v = (256*data_hi + data_lo);
SetEncodedGrayValue (image, x, i, gamma, 65535, v);
}
}
}
}
return (image) ;
}
} // end of namespace Pgm
}