691 lines
17 KiB
C++
691 lines
17 KiB
C++
/*******************************************************************************
|
|
* bmp.cpp
|
|
*
|
|
* This module contains the code to read and write the BMP file format.
|
|
*
|
|
* Author: Wlodzimierz ABX Skiba (abx@abx.art.pl)
|
|
*
|
|
* ---------------------------------------------------------------------------
|
|
* 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/bmp.cpp $
|
|
* $Revision: #1 $
|
|
* $Change: 6069 $
|
|
* $DateTime: 2013/11/06 11:59:40 $
|
|
* $Author: chrisc $
|
|
*******************************************************************************/
|
|
|
|
/*****************************************************************************
|
|
* Local preprocessor defines
|
|
******************************************************************************/
|
|
|
|
#define WIN_NEW 40
|
|
#define WIN_OS2_OLD 12
|
|
#define BI_RGB 0L
|
|
#define BI_RLE8 1L
|
|
#define BI_RLE4 2L
|
|
|
|
#include <vector>
|
|
|
|
// 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/bmp.h"
|
|
#include "base/types.h"
|
|
|
|
// this must be the last file included
|
|
#include "base/povdebug.h"
|
|
|
|
namespace pov_base
|
|
{
|
|
|
|
namespace Bmp
|
|
{
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* Read_Safe_Char
|
|
*
|
|
* INPUT
|
|
*
|
|
* *filePUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* Wlodzimierz ABX Skiba
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* -
|
|
*
|
|
* CHANGES
|
|
*
|
|
* Aug 2003 : Creation.
|
|
* Jan 2004 : Added exception to allow cleanup on error [CJC]
|
|
*
|
|
******************************************************************************/
|
|
|
|
static inline unsigned char Read_Safe_Char (IStream& in)
|
|
{
|
|
unsigned char ch;
|
|
|
|
in >> ch;
|
|
if (!in)
|
|
throw POV_EXCEPTION(kFileDataErr, "Error reading data from BMP image.") ;
|
|
|
|
return (ch) ;
|
|
}
|
|
|
|
// skip forward without using seekg
|
|
static bool Skip (IStream *file, int bytes)
|
|
{
|
|
while (*file && bytes--)
|
|
file->Read_Byte () ;
|
|
return (*file) ;
|
|
}
|
|
|
|
// skip forward without using seekg
|
|
static bool Skip (OStream *file, int bytes)
|
|
{
|
|
while (*file && bytes--)
|
|
file->Write_Byte (0) ;
|
|
return (*file) ;
|
|
}
|
|
|
|
// write a long to a stream in little-endian (e.g. x86) format
|
|
static void Write_Long (OStream *file, unsigned long val)
|
|
{
|
|
file->Write_Byte (val & 0xff) ;
|
|
file->Write_Byte ((val >> 8) & 0xff) ;
|
|
file->Write_Byte ((val >> 16) & 0xff) ;
|
|
file->Write_Byte ((val >> 24) & 0xff) ;
|
|
}
|
|
|
|
// write a short to a stream in little-endian (e.g. x86) format
|
|
static void Write_Short (OStream *file, unsigned short val)
|
|
{
|
|
file->Write_Byte (val & 0xff) ;
|
|
file->Write_Byte ((val >> 8) & 0xff) ;
|
|
}
|
|
|
|
// read a long from a stream in little-endian (e.g. x86) format
|
|
static unsigned long Read_Long (IStream *file)
|
|
{
|
|
unsigned long ch1 = Read_Safe_Char (*file) ;
|
|
unsigned long ch2 = Read_Safe_Char (*file) ;
|
|
unsigned long ch3 = Read_Safe_Char (*file) ;
|
|
unsigned long ch4 = Read_Safe_Char (*file) ;
|
|
return ((ch4 << 24) | (ch3 << 16) | (ch2 << 8) | ch1) ;
|
|
}
|
|
|
|
// read a short from a stream in little-endian (e.g. x86) format
|
|
static unsigned short Read_Short (IStream *file)
|
|
{
|
|
unsigned short ch1 = Read_Safe_Char (*file) ;
|
|
unsigned short ch2 = Read_Safe_Char (*file) ;
|
|
return ((ch2 << 8) | ch1) ;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* Read_BMP_1b
|
|
*
|
|
* INPUT
|
|
*
|
|
* *filePUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* Wlodzimierz ABX Skiba
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* -
|
|
*
|
|
* CHANGES
|
|
*
|
|
* Aug 2003 : Creation.
|
|
*
|
|
******************************************************************************/
|
|
|
|
static void Read_BMP_1b(Image *image, IStream& in, unsigned width, unsigned height)
|
|
{
|
|
int c = 0;
|
|
unsigned pwidth = ((width+31)>>5)<<5; /* clear bits to get 4 byte boundary */
|
|
|
|
for (int y=height - 1; y >= 0; y--)
|
|
{
|
|
for (int x=0; x<pwidth; x++)
|
|
{
|
|
if ((x&7) == 0)
|
|
c = Read_Safe_Char (in);
|
|
if (x<width)
|
|
{
|
|
image->SetBitValue (x, y, (c & 0x80) ? 1 : 0);
|
|
c <<= 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* Read_BMP_4b_RGB
|
|
*
|
|
* INPUT
|
|
*
|
|
* *filePUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* Wlodzimierz ABX Skiba
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* -
|
|
*
|
|
* CHANGES
|
|
*
|
|
* Aug 2003 : Creation.
|
|
*
|
|
******************************************************************************/
|
|
|
|
static void Read_BMP_4b_RGB(Image *image, IStream& in, unsigned width, unsigned height)
|
|
{
|
|
int c = 0;
|
|
unsigned pwidth = ((width+7)>>3)<<3;
|
|
|
|
for (int y=height - 1; y >= 0; y--)
|
|
{
|
|
for (int x=0; x<pwidth; x++)
|
|
{
|
|
if ((x&1)==0)
|
|
c = Read_Safe_Char(in);
|
|
if (x<width)
|
|
{
|
|
image->SetIndexedValue (x, y, (c&0xf0)>>4) ;
|
|
c <<= 4;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* Read_BMP_4b_RLE
|
|
*
|
|
* INPUT
|
|
*
|
|
* *filePUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* Wlodzimierz ABX Skiba
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* -
|
|
*
|
|
* CHANGES
|
|
*
|
|
* Aug 2003 : Creation.
|
|
*
|
|
******************************************************************************/
|
|
|
|
static void Read_BMP_4b_RLE(Image *image, IStream& in, unsigned width, unsigned height)
|
|
{
|
|
int c, cc = 0;
|
|
unsigned x = 0;
|
|
unsigned y = height-1;
|
|
|
|
while (1)
|
|
{
|
|
c = Read_Safe_Char (in);
|
|
if (c)
|
|
{
|
|
cc = Read_Safe_Char (in);
|
|
for (int i=0; i<c; i++, x++)
|
|
if ((y<height) && (x<width))
|
|
image->SetIndexedValue (x, y, (i&1) ? (cc &0x0f) : ((cc>>4)&0x0f));
|
|
}
|
|
else
|
|
{
|
|
c = Read_Safe_Char (in);
|
|
if (c==0)
|
|
{
|
|
x=0;
|
|
y--;
|
|
}
|
|
else if (c==1)
|
|
return;
|
|
else if (c==2)
|
|
{
|
|
x += Read_Safe_Char (in);
|
|
y -= Read_Safe_Char (in);
|
|
}
|
|
else
|
|
{
|
|
for (int i=0; i<c; i++, x++)
|
|
{
|
|
if ((i&1)==0)
|
|
cc = Read_Safe_Char (in);
|
|
if ((y<height) && (x<width))
|
|
image->SetIndexedValue (x, y, ((i&1)?cc:(cc>>4))&0x0f) ;
|
|
}
|
|
if (((c&3)==1) || ((c&3)==2))
|
|
Read_Safe_Char (in);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* Read_BMP_8b_RGB
|
|
*
|
|
* INPUT
|
|
*
|
|
* *filePUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* Wlodzimierz ABX Skiba
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* -
|
|
*
|
|
* CHANGES
|
|
*
|
|
* Aug 2003 : Creation.
|
|
*
|
|
******************************************************************************/
|
|
|
|
static void Read_BMP_8b_RGB(Image *image, IStream& in, unsigned width, unsigned height)
|
|
{
|
|
int c;
|
|
unsigned pwidth = ((width+3)>>2)<<2;
|
|
|
|
for (unsigned y=height; (--y)<height;)
|
|
for (unsigned x=0; x<pwidth; x++)
|
|
{
|
|
c = Read_Safe_Char (in);
|
|
if (x<width)
|
|
image->SetIndexedValue (x, y, c);
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* Read_BMP_8b_RLE
|
|
*
|
|
* INPUT
|
|
*
|
|
* *filePUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* Wlodzimierz ABX Skiba
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* -
|
|
*
|
|
* CHANGES
|
|
*
|
|
* Aug 2003 : Creation.
|
|
*
|
|
******************************************************************************/
|
|
|
|
static void Read_BMP_8b_RLE(Image *image, IStream& in, unsigned width, unsigned height)
|
|
{
|
|
int c, cc;
|
|
unsigned x = 0;
|
|
unsigned y = height-1;
|
|
|
|
while (1)
|
|
{
|
|
c = Read_Safe_Char (in);
|
|
if (c)
|
|
{
|
|
cc = Read_Safe_Char (in);
|
|
for (int i=0; i<c; i++, x++)
|
|
if ((y<height) && (x<width))
|
|
image->SetIndexedValue (x, y, cc);
|
|
}
|
|
else
|
|
{
|
|
c = Read_Safe_Char (in);
|
|
switch(c)
|
|
{
|
|
case 0:
|
|
x = 0;
|
|
y--;
|
|
break;
|
|
case 1:
|
|
return;
|
|
break;
|
|
case 2:
|
|
x += Read_Safe_Char (in);
|
|
y -= Read_Safe_Char (in);
|
|
break;
|
|
default:
|
|
for (int i=0; i<c; i++, x++)
|
|
if ((y<height) && (x<width))
|
|
image->SetIndexedValue (x, y, Read_Safe_Char (in));
|
|
if (c & 1)
|
|
Read_Safe_Char (in); /* "absolute mode" runs are word-aligned */
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* Open_BMP_File
|
|
*
|
|
* INPUT
|
|
*
|
|
* *filePUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* Wlodzimierz ABX Skiba
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* -
|
|
*
|
|
* CHANGES
|
|
*
|
|
* Aug 2003 : Creation.
|
|
* Jan 2004 : Added exception handling to allow cleanup on error [CJC]
|
|
*
|
|
******************************************************************************/
|
|
|
|
void Write (OStream *file, const Image *image, const Image::WriteOptions& options)
|
|
{
|
|
int width = image->GetWidth();
|
|
int height = image->GetHeight();
|
|
int pad = (4 - ((width * 3) % 4)) & 0x03 ;
|
|
bool alpha = image->HasTransparency() && options.alphachannel;
|
|
unsigned int r ;
|
|
unsigned int g ;
|
|
unsigned int b ;
|
|
unsigned int a ;
|
|
GammaCurvePtr gamma;
|
|
DitherHandler* dither = options.dither.get();
|
|
|
|
if (options.encodingGamma)
|
|
gamma = TranscodingGammaCurve::Get(options.workingGamma, options.encodingGamma);
|
|
else
|
|
// BMP files used to have no clearly defined gamma by default, but a Microsoft recommendation exists to assume sRGB.
|
|
gamma = TranscodingGammaCurve::Get(options.workingGamma, SRGBGammaCurve::Get());
|
|
|
|
// TODO ALPHA - check if BMP should really keep presuming non-premultiplied alpha
|
|
// We presume non-premultiplied alpha, unless the user overrides
|
|
// (e.g. to handle a non-compliant file).
|
|
bool premul = false;
|
|
if (options.premultiplyOverride)
|
|
premul = options.premultiply;
|
|
|
|
int count = (width * (alpha ? 32 : 24) + 31) / 32 * 4 * height;
|
|
|
|
*file << 'B' << 'M' ;
|
|
Write_Long (file, 14 + 40 + count) ;
|
|
Write_Short (file, 0) ;
|
|
Write_Short (file, 0) ;
|
|
Write_Long (file, 14 + 40) ;
|
|
Write_Long (file, 40) ;
|
|
Write_Long (file, width) ;
|
|
Write_Long (file, height) ;
|
|
Write_Short (file, 1) ;
|
|
Write_Short (file, alpha ? 32 : 24) ;
|
|
Write_Long (file, BI_RGB) ;
|
|
Write_Long (file, count) ;
|
|
Write_Long (file, 0) ;
|
|
Write_Long (file, 0) ;
|
|
Write_Long (file, 0) ;
|
|
Write_Long (file, 0) ;
|
|
|
|
for (int y = height - 1 ; y >= 0 ; y--)
|
|
{
|
|
for (int x = 0 ; x < width ; x++)
|
|
{
|
|
if (alpha)
|
|
GetEncodedRGBAValue (image, x, y, gamma, 255, r, g, b, a, *dither, premul);
|
|
else
|
|
GetEncodedRGBValue (image, x, y, gamma, 255, r, g, b, *dither) ;
|
|
*file << (unsigned char) b;
|
|
*file << (unsigned char) g;
|
|
*file << (unsigned char) r;
|
|
if (alpha)
|
|
*file << (unsigned char) a;
|
|
}
|
|
if (!alpha)
|
|
for (int i = 0 ; i < pad; i++)
|
|
*file << (unsigned char) 0 ;
|
|
}
|
|
|
|
if (!*file)
|
|
throw POV_EXCEPTION(kFileDataErr, "Error writing to BMP file") ;
|
|
}
|
|
|
|
Image *Read (IStream *file, const Image::ReadOptions& options)
|
|
{
|
|
unsigned file_width, file_height;
|
|
unsigned file_depth, file_colors;
|
|
unsigned data_location, planes, compression;
|
|
unsigned info;
|
|
Image *image = NULL ;
|
|
|
|
// BMP files used to have no clearly defined gamma by default, but a Microsoft recommendation exists to assume sRGB.
|
|
// Since ~1995, a header extension (BITMAPV4HEADER) with gamma metadata exists, which could be used if present.
|
|
// However, as of now (2009), such information seems to be rarely included, if at all. (Same goes for BITMAPV5HEADER,
|
|
// which could include a full colorimetric profile.)
|
|
GammaCurvePtr gamma;
|
|
if (options.gammacorrect)
|
|
{
|
|
if (options.defaultGamma)
|
|
gamma = TranscodingGammaCurve::Get(options.workingGamma, options.defaultGamma);
|
|
else
|
|
gamma = TranscodingGammaCurve::Get(options.workingGamma, SRGBGammaCurve::Get());
|
|
}
|
|
|
|
// TODO ALPHA - check if BMP should really keep presuming non-premultiplied alpha
|
|
// We presume non-premultiplied alpha, so that's the preferred mode to use for the image container unless the user overrides
|
|
// (e.g. to handle a non-compliant file).
|
|
bool premul = false;
|
|
if (options.premultiplyOverride)
|
|
premul = options.premultiply;
|
|
|
|
if ((file->Read_Byte () != 'B') || (file->Read_Byte () !='M'))
|
|
throw POV_EXCEPTION(kFileDataErr, "Error reading magic number of BMP image");
|
|
|
|
// skip file size and reserved fields
|
|
Skip (file, 8) ;
|
|
data_location = Read_Long (file) ;
|
|
|
|
// read properties
|
|
if ((info = Read_Long (file)) != WIN_OS2_OLD)
|
|
{
|
|
file_width = Read_Long (file) ;
|
|
file_height = Read_Long (file) ;
|
|
planes = Read_Short (file) ;
|
|
file_depth = Read_Short (file) ;
|
|
compression = Read_Long (file) ;
|
|
Skip (file, 12) ; // skip image size in bytes, H&V pixels per meter
|
|
file_colors = Read_Long (file) ;
|
|
Skip (file, 4) ; // skip needed colors
|
|
}
|
|
else /* info == WIN_OS2_OLD */
|
|
{
|
|
file_width = Read_Short (file) ;
|
|
file_height = Read_Short (file) ;
|
|
planes = Read_Short (file) ;
|
|
file_depth = Read_Short (file) ;
|
|
compression = BI_RGB ;
|
|
file_colors = 0 ;
|
|
}
|
|
|
|
bool has_alpha = file_depth == 32 ;
|
|
|
|
/* do not allow other subtypes */
|
|
if (((file_depth!=1) && (file_depth!=4) && (file_depth!=8) && (file_depth!=24) && (file_depth!=32)) ||
|
|
(planes!=1) || (compression>BI_RLE4) ||
|
|
(((file_depth==1) || (file_depth==24) || (file_depth==32)) && (compression!=BI_RGB)) ||
|
|
((file_depth==4) && (compression==BI_RLE8)) ||
|
|
((file_depth==8) && (compression==BI_RLE4)))
|
|
throw POV_EXCEPTION(kFileDataErr, "Invalid or unsupported BMP file");
|
|
|
|
/* seek to colormap */
|
|
if (info != WIN_OS2_OLD)
|
|
Skip (file, info - 40) ;
|
|
|
|
if (file_depth < 24)
|
|
{
|
|
int color_map_length = file_colors ? file_colors : 1<<file_depth ;
|
|
vector<Image::RGBMapEntry> colormap ;
|
|
Image::RGBMapEntry entry;
|
|
|
|
for (int i=0; i<color_map_length; i++)
|
|
{
|
|
entry.blue = IntDecode(gamma, file->Read_Byte (), 255);
|
|
entry.green = IntDecode(gamma, file->Read_Byte (), 255);
|
|
entry.red = IntDecode(gamma, file->Read_Byte (), 255);
|
|
if (info != WIN_OS2_OLD)
|
|
file->Read_Byte() ;
|
|
colormap.push_back (entry) ;
|
|
}
|
|
gamma.reset(); // gamma has been taken care of by transforming the color table.
|
|
|
|
if (file->eof ())
|
|
throw POV_EXCEPTION(kFileDataErr, "Unexpected EOF while reading BMP file");
|
|
|
|
image = Image::Create (file_width, file_height, Image::Colour_Map, colormap) ;
|
|
image->SetPremultiplied(premul); // specify whether the color map data has premultiplied alpha
|
|
|
|
switch (file_depth)
|
|
{
|
|
case 1:
|
|
Read_BMP_1b(image, *file, file_width, file_height);
|
|
break;
|
|
case 4:
|
|
switch(compression)
|
|
{
|
|
case BI_RGB:
|
|
Read_BMP_4b_RGB(image, *file, file_width, file_height);
|
|
break;
|
|
case BI_RLE4:
|
|
Read_BMP_4b_RLE(image, *file, file_width, file_height);
|
|
break;
|
|
default:
|
|
throw POV_EXCEPTION(kFileDataErr, "Unknown compression scheme in BMP file");
|
|
}
|
|
break;
|
|
case 8:
|
|
switch(compression)
|
|
{
|
|
case BI_RGB:
|
|
Read_BMP_8b_RGB(image, *file, file_width, file_height);
|
|
break;
|
|
case BI_RLE8:
|
|
Read_BMP_8b_RLE(image, *file, file_width, file_height);
|
|
break;
|
|
default:
|
|
throw POV_EXCEPTION(kFileDataErr, "Unknown compression scheme in BMP file");
|
|
}
|
|
break;
|
|
default:
|
|
throw POV_EXCEPTION(kFileDataErr, "Unknown depth in BMP file");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* includes update from stefan maierhofer for 32bit */
|
|
Image::ImageDataType imagetype = options.itype ;
|
|
if (imagetype == Image::Undefined)
|
|
{
|
|
if (GammaCurve::IsNeutral(gamma))
|
|
// No gamma correction required, raw values can be stored "as is".
|
|
imagetype = has_alpha ? Image::RGBA_Int8 : Image::RGB_Int8 ;
|
|
else
|
|
// Gamma correction required; use an image container that will take care of that.
|
|
imagetype = has_alpha ? Image::RGBA_Gamma8 : Image::RGB_Gamma8 ;
|
|
}
|
|
image = Image::Create (file_width, file_height, imagetype) ;
|
|
image->SetPremultiplied(premul); // set desired storage mode regarding alpha premultiplication
|
|
image->TryDeferDecoding(gamma, 255); // try to have gamma adjustment being deferred until image evaluation.
|
|
|
|
int pad = has_alpha ? 0 : (4 - ((file_width * 3) % 4)) & 0x03 ;
|
|
unsigned int a = 255 ; // value to use for files that don't have an alpha channel (full opacity)
|
|
|
|
for (int y = file_height - 1 ; y >= 0 ; y--)
|
|
{
|
|
for (int x = 0 ; x < file_width ; x++)
|
|
{
|
|
unsigned int b = Read_Safe_Char (*file);
|
|
unsigned int g = Read_Safe_Char (*file);
|
|
unsigned int r = Read_Safe_Char (*file);
|
|
if (has_alpha)
|
|
a = Read_Safe_Char (*file);
|
|
SetEncodedRGBAValue (image, x, y, gamma, 255, r, g, b, a, premul);
|
|
}
|
|
if (pad && !Skip (file, pad))
|
|
throw POV_EXCEPTION(kFileDataErr, "Error reading data from BMP image.") ;
|
|
}
|
|
}
|
|
|
|
return (image) ;
|
|
|
|
}
|
|
|
|
} // end of namespace Bmp
|
|
|
|
}
|