1949 lines
43 KiB
C++
1949 lines
43 KiB
C++
|
/*
|
||
|
FLUIDS v.1 - SPH Fluid Simulator for CPU and GPU
|
||
|
Copyright (C) 2009. Rama Hoetzlein, http://www.rchoetzlein.com
|
||
|
|
||
|
ZLib license
|
||
|
This software is provided 'as-is', without any express or implied
|
||
|
warranty. In no event will the authors be held liable for any damages
|
||
|
arising from the use of this software.
|
||
|
|
||
|
Permission is granted to anyone to use this software for any purpose,
|
||
|
including commercial applications, and to alter it and redistribute it
|
||
|
freely, subject to the following restrictions:
|
||
|
|
||
|
1. The origin of this software must not be misrepresented; you must not
|
||
|
claim that you wrote the original software. If you use this software
|
||
|
in a product, an acknowledgment in the product documentation would be
|
||
|
appreciated but is not required.
|
||
|
2. Altered source versions must be plainly marked as such, and must not be
|
||
|
misrepresented as being the original software.
|
||
|
3. This notice may not be removed or altered from any source distribution.
|
||
|
*/
|
||
|
#include "image.h"
|
||
|
|
||
|
#include <math.h>
|
||
|
#include <assert.h>
|
||
|
#include <errno.h>
|
||
|
#include <string.h>
|
||
|
#include <stdio.h>
|
||
|
|
||
|
#include "gl_helper.h"
|
||
|
|
||
|
// Link with JPEG library on platforms that support it.
|
||
|
#ifdef USE_JPEG
|
||
|
#include <jpeglib.h>
|
||
|
#include <setjmp.h>
|
||
|
#include "jerror.h"
|
||
|
#endif
|
||
|
|
||
|
#ifndef WIN32
|
||
|
#include <unistd.h>
|
||
|
#endif
|
||
|
|
||
|
|
||
|
// Disable warnings on depricated functions
|
||
|
#pragma warning( disable : 4995) // removes warning 4995 - depricated function
|
||
|
#pragma warning( disable : 4996) // removes warning 4996 - depricated function
|
||
|
|
||
|
#ifdef WIN32
|
||
|
#define GL_TEXTURE_CUBE_MAP 0x8513
|
||
|
#define GL_TEXTURE_BINDING_CUBE_MAP 0x8514
|
||
|
#define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515
|
||
|
#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516
|
||
|
#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517
|
||
|
#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518
|
||
|
#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519
|
||
|
#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A
|
||
|
#endif
|
||
|
|
||
|
static const int maxvals[9] = { 0, 1, 3, 7, 15, 31, 63, 127, 255 };
|
||
|
|
||
|
static inline unsigned char val2bits (double v, int b)
|
||
|
{
|
||
|
return (unsigned char) (floor(v * maxvals[b] + 0.5) / maxvals[b] * 255);
|
||
|
}
|
||
|
|
||
|
|
||
|
Image::Image ()
|
||
|
{
|
||
|
width = 0;
|
||
|
height = 0;
|
||
|
channels = 0;
|
||
|
bits = 0;
|
||
|
maxValue = 0;
|
||
|
pixels = NULL;
|
||
|
owns = true;
|
||
|
}
|
||
|
|
||
|
|
||
|
Image::Image (int width_, int height_)
|
||
|
{
|
||
|
create ( width_, height_, 3 );
|
||
|
}
|
||
|
|
||
|
|
||
|
Image::Image (int width_, int height_, int channels_)
|
||
|
{
|
||
|
create (width_, height_, channels_ );
|
||
|
}
|
||
|
|
||
|
void Image::create ( int width_, int height_, int channels_ )
|
||
|
{
|
||
|
width = width_;
|
||
|
height = height_;
|
||
|
channels = channels_;
|
||
|
bits = 8;
|
||
|
maxValue = 255;
|
||
|
|
||
|
assert(width > 0 && height > 0 &&
|
||
|
(channels == 1 || channels == 3 || channels == 4) &&
|
||
|
bits > 0 && bits < 9
|
||
|
);
|
||
|
|
||
|
pixels = new unsigned char[width*height*channels];
|
||
|
owns = true;
|
||
|
memset(pixels, 0, width*height*channels);
|
||
|
|
||
|
clear ( Pixel(0,0,0 ) );
|
||
|
|
||
|
glGenTextures( 1, (GLuint*) &imgID );
|
||
|
glBindTexture ( GL_TEXTURE_2D, imgID );
|
||
|
glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||
|
glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||
|
glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
|
||
|
glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
|
||
|
glPixelStorei( GL_UNPACK_ALIGNMENT, 1);
|
||
|
refresh ();
|
||
|
}
|
||
|
|
||
|
void Image::draw ()
|
||
|
{
|
||
|
draw ( 0, 0 );
|
||
|
}
|
||
|
|
||
|
void Image::draw ( float x, float y )
|
||
|
{
|
||
|
glEnable ( GL_TEXTURE_2D );
|
||
|
glColor4f ( 1,1,1,1);
|
||
|
glBindTexture ( GL_TEXTURE_2D, (GLuint) imgID );
|
||
|
glBegin ( GL_QUADS );
|
||
|
glTexCoord2f ( 0, 1); glVertex2f ( x, y );
|
||
|
glTexCoord2f ( 1, 1 ); glVertex2f ( x+width, y );
|
||
|
glTexCoord2f ( 1, 0 ); glVertex2f ( x+width, y+height );
|
||
|
glTexCoord2f ( 0, 0 ); glVertex2f ( x, y+height );
|
||
|
glEnd ();
|
||
|
}
|
||
|
|
||
|
|
||
|
Image::Image (int width_, int height_, int channels_, int bits_)
|
||
|
{
|
||
|
width = width_;
|
||
|
height = height_;
|
||
|
channels = channels_;
|
||
|
bits = bits_;
|
||
|
maxValue = val2bits(1.0,bits);
|
||
|
|
||
|
assert(width > 0 && height > 0 &&
|
||
|
(channels == 1 || channels == 3 || channels == 4) &&
|
||
|
bits > 0 && bits < 9
|
||
|
);
|
||
|
|
||
|
pixels = new unsigned char[width*height*channels];
|
||
|
owns = true;
|
||
|
memset(pixels, 0, width*height*channels);
|
||
|
}
|
||
|
|
||
|
|
||
|
Image::Image (const char* filename)
|
||
|
{
|
||
|
width = 0;
|
||
|
height = 0;
|
||
|
channels = 0;
|
||
|
bits = 0;
|
||
|
maxValue = 0;
|
||
|
pixels = NULL;
|
||
|
owns = true;
|
||
|
|
||
|
read(filename);
|
||
|
}
|
||
|
|
||
|
|
||
|
Image::~Image ()
|
||
|
{
|
||
|
if (pixels && owns) delete[] pixels;
|
||
|
}
|
||
|
|
||
|
|
||
|
Image::Image (const Image& image)
|
||
|
{
|
||
|
width = image.width;
|
||
|
height = image.height;
|
||
|
channels = image.channels;
|
||
|
bits = image.bits;
|
||
|
maxValue = image.maxValue;
|
||
|
pixels = new unsigned char[width*height*channels];
|
||
|
owns = true;
|
||
|
|
||
|
for (int i = 0; i < width*height*channels; ++i)
|
||
|
pixels[i] = image.pixels[i];
|
||
|
}
|
||
|
|
||
|
|
||
|
Image& Image::operator= (const Image& image)
|
||
|
{
|
||
|
if (&image == this) return *this;
|
||
|
|
||
|
if (pixels) delete[] pixels;
|
||
|
|
||
|
width = image.width;
|
||
|
height = image.height;
|
||
|
channels = image.channels;
|
||
|
bits = image.bits;
|
||
|
maxValue = image.maxValue;
|
||
|
pixels = new unsigned char[width*height*channels];
|
||
|
owns = true;
|
||
|
|
||
|
for (int i = 0; i < width*height*channels; ++i)
|
||
|
pixels[i] = image.pixels[i];
|
||
|
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
|
||
|
void Image::setPixels ( unsigned char *newPixels )
|
||
|
{
|
||
|
if( bad() || ! newPixels )
|
||
|
return;
|
||
|
|
||
|
//if( owns && pixels )
|
||
|
// delete [] pixels;
|
||
|
|
||
|
memcpy ( pixels, newPixels, width*height*channels );
|
||
|
}
|
||
|
|
||
|
|
||
|
bool Image::good ()
|
||
|
{
|
||
|
return (width > 0 && height > 0 &&
|
||
|
(channels == 1 || channels == 3 || channels == 4) &&
|
||
|
bits > 0 && bits < 9 && pixels);
|
||
|
}
|
||
|
|
||
|
|
||
|
bool Image::bad ()
|
||
|
{
|
||
|
return !good();
|
||
|
}
|
||
|
|
||
|
void Image::clear ()
|
||
|
{
|
||
|
clear ( Pixel(0,0,0) );
|
||
|
}
|
||
|
|
||
|
|
||
|
void Image::clear ( Pixel pixel )
|
||
|
{
|
||
|
unsigned char* buf = pixels;
|
||
|
|
||
|
if ( channels == 3) {
|
||
|
for (int n=0; n < width*height; n++) {
|
||
|
*buf++ = (unsigned char) (pixel.r*255.0f);
|
||
|
*buf++ = (unsigned char) (pixel.g*255.0f);
|
||
|
*buf++ = (unsigned char) (pixel.b*255.0f);
|
||
|
}
|
||
|
} else {
|
||
|
for (int n=0; n < width*height; n++) {
|
||
|
*buf++ = (unsigned char) ( pixel.r*255.0f);
|
||
|
*buf++ = (unsigned char) (pixel.g*255.0f);
|
||
|
*buf++ = (unsigned char) (pixel.b*255.0f);
|
||
|
*buf++ = (unsigned char) (pixel.a*255.0f);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
int Image::index (int x, int y, int c)
|
||
|
{
|
||
|
return (((height - y - 1) * width + x) * channels + c);
|
||
|
}
|
||
|
|
||
|
|
||
|
double Image::getPixel (int x, int y, int channel)
|
||
|
{
|
||
|
assert(good());
|
||
|
assert((x >= 0) &&
|
||
|
(x < width) &&
|
||
|
(y >= 0) &&
|
||
|
(y < height) &&
|
||
|
(channel >= 0) &&
|
||
|
(channel < channels));
|
||
|
|
||
|
return pixels[index(x,y,channel)] / 255.0;
|
||
|
}
|
||
|
|
||
|
|
||
|
double Image::getPixel_ (int x, int y, int channel)
|
||
|
{
|
||
|
if (!good() ||
|
||
|
(x < 0) ||
|
||
|
(x >= width) ||
|
||
|
(y < 0) ||
|
||
|
(y >= height) ||
|
||
|
(channel < 0) ||
|
||
|
(channel >= channels))
|
||
|
return 0.0;
|
||
|
|
||
|
return getPixel(x,y,channel);
|
||
|
}
|
||
|
|
||
|
|
||
|
Pixel Image::getPixel (int x, int y)
|
||
|
{
|
||
|
assert(good());
|
||
|
assert((x >= 0) &&
|
||
|
(x < width) &&
|
||
|
(y >= 0) &&
|
||
|
(y < height));
|
||
|
|
||
|
Pixel pixel;
|
||
|
memset(&pixel, 0, sizeof(Pixel));
|
||
|
|
||
|
switch (channels)
|
||
|
{
|
||
|
case 4:
|
||
|
pixel.a = pixels[index(x,y,IALPHA)] / 255.0;
|
||
|
|
||
|
case 3:
|
||
|
pixel.b = pixels[index(x,y,IBLUE)] / 255.0;
|
||
|
pixel.g = pixels[index(x,y,IGREEN)] / 255.0;
|
||
|
|
||
|
case 1:
|
||
|
pixel.r = pixels[index(x,y,IRED)] / 255.0;
|
||
|
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
return pixel;
|
||
|
}
|
||
|
|
||
|
|
||
|
Pixel Image::getPixel_ (int x, int y)
|
||
|
{
|
||
|
if (!good() ||
|
||
|
(x < 0) ||
|
||
|
(x >= width) ||
|
||
|
(y < 0) ||
|
||
|
(y >= height))
|
||
|
{
|
||
|
Pixel pixel;
|
||
|
memset(&pixel, 0, sizeof(Pixel));
|
||
|
return pixel;
|
||
|
}
|
||
|
|
||
|
return getPixel(x,y);
|
||
|
}
|
||
|
|
||
|
|
||
|
Pixel& Image::getPixel (int x, int y, Pixel& pixel)
|
||
|
{
|
||
|
assert(good());
|
||
|
assert((x >= 0) &&
|
||
|
(x < width) &&
|
||
|
(y >= 0) &&
|
||
|
(y < height));
|
||
|
|
||
|
memset(&pixel, 0, sizeof(Pixel));
|
||
|
|
||
|
switch (channels)
|
||
|
{
|
||
|
case 4:
|
||
|
pixel.a = pixels[index(x,y,IALPHA)] / 255.0;
|
||
|
|
||
|
case 3:
|
||
|
pixel.b = pixels[index(x,y,IBLUE)] / 255.0;
|
||
|
pixel.g = pixels[index(x,y,IGREEN)] / 255.0;
|
||
|
|
||
|
case 1:
|
||
|
pixel.r = pixels[index(x,y,IRED)] / 255.0;
|
||
|
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
return pixel;
|
||
|
}
|
||
|
|
||
|
|
||
|
Pixel& Image::getPixel_ (int x, int y, Pixel& pixel)
|
||
|
{
|
||
|
if (!good() ||
|
||
|
(x < 0) ||
|
||
|
(x >= width) ||
|
||
|
(y < 0) ||
|
||
|
(y >= height))
|
||
|
{
|
||
|
memset(&pixel, 0, sizeof(Pixel));
|
||
|
return pixel;
|
||
|
}
|
||
|
|
||
|
return getPixel(x,y,pixel);
|
||
|
}
|
||
|
|
||
|
|
||
|
void Image::setPixel (int x, int y, int channel, double value)
|
||
|
{
|
||
|
assert(good());
|
||
|
assert ( (channel >= 0) && (channel < channels));
|
||
|
assert( (value >= 0.0) && (value <= 1.0));
|
||
|
|
||
|
if ( (x >= 0) && (x < width) && (y >= 0) && (y < height) )
|
||
|
pixels[index(x,y,channel)] = val2bits(value, bits);
|
||
|
}
|
||
|
|
||
|
void Image::setPixel_ (int x, int y, int channel, double value)
|
||
|
{
|
||
|
if (!good() ||
|
||
|
(x < 0) ||
|
||
|
(x >= width) ||
|
||
|
(y < 0) ||
|
||
|
(y >= height) ||
|
||
|
(channel < 0) ||
|
||
|
(channel >= channels) ||
|
||
|
(value < 0.0) ||
|
||
|
(value > 1.0))
|
||
|
return;
|
||
|
|
||
|
setPixel(x,y,channel,value);
|
||
|
}
|
||
|
|
||
|
void Image::setPixel4 ( int x, int y, Pixel pixel )
|
||
|
{
|
||
|
setPixel ( x, y, pixel );
|
||
|
setPixel ( x+1, y, pixel );
|
||
|
setPixel ( x, y+1, pixel );
|
||
|
setPixel ( x+1, y+1, pixel );
|
||
|
}
|
||
|
|
||
|
void Image::setPixel (int x, int y, Pixel pixel)
|
||
|
{
|
||
|
assert(good());
|
||
|
if ( x < 0 || x >= width || y < 0 || y >= height ) return;
|
||
|
|
||
|
switch (channels)
|
||
|
{
|
||
|
case 4:
|
||
|
pixels[index(x,y,IALPHA)] = val2bits(pixel.a, bits);
|
||
|
|
||
|
case 3:
|
||
|
pixels[index(x,y,IBLUE)] = val2bits(pixel.b, bits);
|
||
|
pixels[index(x,y,IGREEN)] = val2bits(pixel.g, bits);
|
||
|
|
||
|
case 1:
|
||
|
pixels[index(x,y,IRED)] = val2bits(pixel.r, bits);
|
||
|
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Image::setAlpha (int x, int y, double value)
|
||
|
{
|
||
|
assert(good());
|
||
|
pixels[index(x,y,IALPHA)] = val2bits(value, bits);
|
||
|
}
|
||
|
|
||
|
|
||
|
void Image::setPixel_ (int x, int y, Pixel pixel)
|
||
|
{
|
||
|
if (!good() ||
|
||
|
(x < 0) ||
|
||
|
(x >= width) ||
|
||
|
(y < 0) ||
|
||
|
(y >= height))
|
||
|
return;
|
||
|
|
||
|
setPixel(x,y,pixel);
|
||
|
}
|
||
|
|
||
|
|
||
|
#ifndef DISABLE_OPENGL
|
||
|
|
||
|
void Image::glReadPixelsWrapper ()
|
||
|
{
|
||
|
assert(good());
|
||
|
|
||
|
glPixelStorei(GL_PACK_ALIGNMENT, 1);
|
||
|
|
||
|
switch (channels)
|
||
|
{
|
||
|
case 1:
|
||
|
glReadPixels(0, 0, width, height, GL_LUMINANCE, GL_UNSIGNED_BYTE, pixels);
|
||
|
break;
|
||
|
case 3:
|
||
|
glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, pixels);
|
||
|
break;
|
||
|
case 4:
|
||
|
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Image::glDrawPixelsWrapper ()
|
||
|
{
|
||
|
assert(good());
|
||
|
|
||
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||
|
|
||
|
switch (channels)
|
||
|
{
|
||
|
case 1:
|
||
|
glDrawPixels(width, height, GL_LUMINANCE, GL_UNSIGNED_BYTE, pixels);
|
||
|
break;
|
||
|
case 3:
|
||
|
glDrawPixels(width, height, GL_RGB, GL_UNSIGNED_BYTE, pixels);
|
||
|
break;
|
||
|
|
||
|
case 4:
|
||
|
glDrawPixels(width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Image::refresh ()
|
||
|
{
|
||
|
glTexImage2DWrapper ();
|
||
|
}
|
||
|
|
||
|
|
||
|
void Image::glTexImage2DWrapper ()
|
||
|
{
|
||
|
assert(good());
|
||
|
|
||
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||
|
|
||
|
switch (channels)
|
||
|
{
|
||
|
case 1:
|
||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, width, height, 0,
|
||
|
GL_LUMINANCE, GL_UNSIGNED_BYTE, pixels);
|
||
|
break;
|
||
|
|
||
|
case 3:
|
||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0,
|
||
|
GL_RGB, GL_UNSIGNED_BYTE, pixels);
|
||
|
break;
|
||
|
|
||
|
case 4:
|
||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0,
|
||
|
GL_RGBA, GL_UNSIGNED_BYTE, pixels);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Image::glTexImageCubeWrapper ( int i )
|
||
|
{
|
||
|
assert(good());
|
||
|
|
||
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||
|
|
||
|
switch (channels)
|
||
|
{
|
||
|
case 1:
|
||
|
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X+i, 0, GL_LUMINANCE, width, height, 0,
|
||
|
GL_LUMINANCE, GL_UNSIGNED_BYTE, pixels);
|
||
|
break;
|
||
|
case 3:
|
||
|
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X+i, 0, GL_RGB, width, height, 0,
|
||
|
GL_RGB, GL_UNSIGNED_BYTE, pixels);
|
||
|
break;
|
||
|
case 4:
|
||
|
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X+i, 0, GL_RGBA, width, height, 0,
|
||
|
GL_RGBA, GL_UNSIGNED_BYTE, pixels);
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Image::glTexSubImage2DWrapper ( int x, int y)
|
||
|
{
|
||
|
assert(good());
|
||
|
|
||
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||
|
|
||
|
switch (channels)
|
||
|
{
|
||
|
case 1:
|
||
|
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height,
|
||
|
GL_LUMINANCE, GL_UNSIGNED_BYTE, pixels);
|
||
|
break;
|
||
|
|
||
|
case 3:
|
||
|
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height,
|
||
|
GL_RGB, GL_UNSIGNED_BYTE, pixels);
|
||
|
break;
|
||
|
|
||
|
case 4:
|
||
|
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height,
|
||
|
GL_RGBA, GL_UNSIGNED_BYTE, pixels);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
#endif // DISABLE_OPENGL
|
||
|
|
||
|
int Image::read (const char* filename )
|
||
|
{
|
||
|
return read ( filename, 0x0 );
|
||
|
}
|
||
|
|
||
|
int Image::read (const char* filename, const char* alphaname )
|
||
|
{
|
||
|
FILE *file, *file_a;
|
||
|
|
||
|
// Open image file
|
||
|
file = fopen( filename, "rb" );
|
||
|
if (!file) {
|
||
|
printf ( "Cannot find file: %s\n", filename );
|
||
|
perror("Image::read");
|
||
|
return -1;
|
||
|
}
|
||
|
// Open alpha file
|
||
|
if ( alphaname != 0x0 ) {
|
||
|
file_a = fopen( alphaname, "rb" );
|
||
|
if (!file_a) {
|
||
|
printf ( "Cannot find file: %s\n", alphaname );
|
||
|
perror("Image::read");
|
||
|
return -1;
|
||
|
}
|
||
|
} else {
|
||
|
file_a = 0x0;
|
||
|
}
|
||
|
// Get image file format
|
||
|
unsigned char type[4];
|
||
|
fread((void*) type, sizeof(char), 4, file);
|
||
|
if (ferror(file)) {
|
||
|
fclose(file);
|
||
|
printf ( "Error reading file: %s\n", filename );
|
||
|
perror("Image::read");
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
int result = 0;
|
||
|
|
||
|
fseek(file, 0, SEEK_SET);
|
||
|
|
||
|
if (type[0] == 'P' && (type[1] == '1' || type[1] == '2' || type[1] == '3' || type[1] == '5' || type[1] == '6')) {
|
||
|
result = readPNM(file);
|
||
|
} else if ((type[0] == 0x4D && type[1] == 0x42) || (type[1] == 0x4D && type[0] == 0x42)) {
|
||
|
result = readBMP(file, file_a, true );
|
||
|
} else if (type[0] == 0x49 && type[1] == 0x49 && type[2] == 0x42 ) {
|
||
|
//result = readTIF(file);
|
||
|
}
|
||
|
#ifdef USE_JPEG
|
||
|
else if ((type[0] == 0xD8 && type[1] == 0xFF) || (type[1] == 0xD8 && type[0] == 0xFF)) {
|
||
|
result = readJPG(file);
|
||
|
}
|
||
|
#endif
|
||
|
else {
|
||
|
fprintf( stderr, "Image::read: Unrecognized file type.\nMaybe it is a JPEG file. Either supply the file or set USE_JPEG.\n" );
|
||
|
result = -1;
|
||
|
}
|
||
|
fclose(file);
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
|
||
|
int Image::write (const char* filename)
|
||
|
{
|
||
|
size_t len = strlen(filename);
|
||
|
const char* ext = &(filename[len-4]);
|
||
|
int result = 0;
|
||
|
|
||
|
if (strncmp(ext, ".pnm", 4) == 0)
|
||
|
{
|
||
|
result = writePNM(filename);
|
||
|
}
|
||
|
else if (strncmp(ext, ".bmp", 4) == 0)
|
||
|
{
|
||
|
result = writeBMP(filename);
|
||
|
}
|
||
|
#ifdef USE_JPEG
|
||
|
else if (strncmp(ext, ".jpg", 4) == 0)
|
||
|
{
|
||
|
result = writeJPG(filename);
|
||
|
}
|
||
|
#endif
|
||
|
else
|
||
|
{
|
||
|
char filenamewithext[1024];
|
||
|
#ifdef WIN32
|
||
|
_snprintf( filenamewithext, 1024, "%.1019s.pnm", filename );
|
||
|
#else
|
||
|
snprintf( filenamewithext, 1024, "%.1019s.pnm", filename );
|
||
|
#endif
|
||
|
result = writePNM(filenamewithext);
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// .JPG file manipulation
|
||
|
//
|
||
|
// This code enabled only if JPEG is supported.
|
||
|
//
|
||
|
#ifdef USE_JPEG
|
||
|
|
||
|
int Image::readJPG (const char* filename)
|
||
|
{
|
||
|
FILE* file = fopen(filename, "rb" );
|
||
|
|
||
|
if (!file)
|
||
|
{
|
||
|
printf ( "Cannot find file: %s\n", filename );
|
||
|
perror("Image::readJPG");
|
||
|
//_getch();
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
int result = readJPG(file);
|
||
|
fclose(file);
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
|
||
|
int Image::writeJPG (const char* filename)
|
||
|
{
|
||
|
FILE* file = fopen(filename, "wb");
|
||
|
|
||
|
if (!file)
|
||
|
{
|
||
|
perror("Image::writeJPG");
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
int result = writeJPG(file);
|
||
|
fclose(file);
|
||
|
|
||
|
if (result == -1)
|
||
|
unlink(filename);
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
|
||
|
struct my_error_mgr
|
||
|
{
|
||
|
struct jpeg_error_mgr pub;
|
||
|
jmp_buf setjmp_buffer;
|
||
|
};
|
||
|
|
||
|
|
||
|
typedef struct my_error_mgr * my_error_ptr;
|
||
|
|
||
|
|
||
|
void my_error_exit (j_common_ptr cinfo)
|
||
|
{
|
||
|
my_error_ptr myerr = (my_error_ptr) cinfo->err;
|
||
|
(*cinfo->err->output_message) (cinfo);
|
||
|
longjmp(myerr->setjmp_buffer, 1);
|
||
|
}
|
||
|
|
||
|
|
||
|
int Image::readJPG (FILE* file)
|
||
|
{
|
||
|
struct jpeg_decompress_struct cinfo;
|
||
|
struct my_error_mgr jerr;
|
||
|
JSAMPROW row_pointer[1];
|
||
|
|
||
|
cinfo.err = jpeg_std_error(&jerr.pub);
|
||
|
jerr.pub.error_exit = my_error_exit;
|
||
|
|
||
|
// failure jump point
|
||
|
if (setjmp(jerr.setjmp_buffer))
|
||
|
{
|
||
|
jpeg_destroy_decompress(&cinfo);
|
||
|
fprintf(stderr, "Image::readJPG: jpeg decompression error");
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
jpeg_create_decompress(&cinfo);
|
||
|
jpeg_stdio_src(&cinfo, file);
|
||
|
jpeg_read_header(&cinfo, TRUE);
|
||
|
jpeg_start_decompress(&cinfo);
|
||
|
|
||
|
if (cinfo.out_color_space != JCS_RGB &&
|
||
|
cinfo.out_color_space != JCS_GRAYSCALE)
|
||
|
{
|
||
|
fprintf(stderr, "Image::readJPG: unrecognized colorspace");
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
width = cinfo.output_width;
|
||
|
height = cinfo.output_height;
|
||
|
bits = 8;
|
||
|
channels = cinfo.out_color_components;
|
||
|
pixels = new unsigned char[width*height*channels];
|
||
|
memset(pixels, 0, width*height*channels);
|
||
|
|
||
|
int row_stride = width * channels;
|
||
|
|
||
|
while (cinfo.output_scanline < cinfo.output_height)
|
||
|
{
|
||
|
row_pointer[0] = &pixels[(height - cinfo.output_scanline - 1) * row_stride];
|
||
|
jpeg_read_scanlines(&cinfo, row_pointer, 1);
|
||
|
}
|
||
|
|
||
|
jpeg_finish_decompress(&cinfo);
|
||
|
jpeg_destroy_decompress(&cinfo);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
int Image::writeJPG (FILE* file)
|
||
|
{
|
||
|
if (!good())
|
||
|
{
|
||
|
fprintf(stderr, "Image::writeJPG: bad image\n");
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
struct jpeg_compress_struct cinfo;
|
||
|
struct jpeg_error_mgr jerr;
|
||
|
JSAMPROW row_pointer[1];
|
||
|
int row_stride = width * channels;
|
||
|
|
||
|
cinfo.err = jpeg_std_error(&jerr);
|
||
|
jpeg_create_compress(&cinfo);
|
||
|
jpeg_stdio_dest(&cinfo, file);
|
||
|
|
||
|
cinfo.image_width = width;
|
||
|
cinfo.image_height = height;
|
||
|
cinfo.input_components = channels;
|
||
|
cinfo.in_color_space = (channels == 3) ? JCS_RGB : JCS_GRAYSCALE;
|
||
|
|
||
|
jpeg_set_defaults(&cinfo);
|
||
|
|
||
|
jpeg_set_quality (&cinfo, 100, TRUE);
|
||
|
|
||
|
jpeg_start_compress(&cinfo, TRUE);
|
||
|
|
||
|
while (cinfo.next_scanline < cinfo.image_height)
|
||
|
{
|
||
|
row_pointer[0] = & pixels[(height - cinfo.next_scanline - 1) * row_stride];
|
||
|
jpeg_write_scanlines(&cinfo, row_pointer, 1);
|
||
|
}
|
||
|
|
||
|
jpeg_finish_compress(&cinfo);
|
||
|
jpeg_destroy_compress(&cinfo);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
#endif // USE_JPEG
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// .BMP file manipulation
|
||
|
//
|
||
|
|
||
|
int Image::readBMP (const char* filename )
|
||
|
{
|
||
|
FILE* file = fopen(filename, "rb");
|
||
|
if (!file)
|
||
|
{
|
||
|
perror("Image::readBMP");
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
int result = readBMP (file, 0x0, true);
|
||
|
fclose(file);
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
|
||
|
int Image::writeBMP (const char* filename)
|
||
|
{
|
||
|
FILE* file = fopen(filename, "wb");
|
||
|
|
||
|
if (!file)
|
||
|
{
|
||
|
perror("Image::writeBMP");
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
int result = writeBMP(file);
|
||
|
fclose(file);
|
||
|
|
||
|
if (result == -1)
|
||
|
unlink(filename);
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
|
||
|
#if !defined(WIN32)
|
||
|
|
||
|
typedef unsigned char BYTE;
|
||
|
typedef unsigned short int WORD;
|
||
|
typedef unsigned int DWORD;
|
||
|
typedef int LONG;
|
||
|
|
||
|
|
||
|
typedef struct tagBITMAPFILEHEADER {
|
||
|
WORD bfType;
|
||
|
DWORD bfSize;
|
||
|
WORD bfReserved1;
|
||
|
WORD bfReserved2;
|
||
|
DWORD bfOffBits;
|
||
|
} BITMAPFILEHEADER;
|
||
|
|
||
|
|
||
|
|
||
|
typedef struct tagBITMAPINFOHEADER {
|
||
|
DWORD biSize;
|
||
|
LONG biWidth;
|
||
|
LONG biHeight;
|
||
|
WORD biPlanes;
|
||
|
WORD biBitCount;
|
||
|
DWORD biCompression;
|
||
|
DWORD biSizeImage;
|
||
|
LONG biXPelsPerMeter;
|
||
|
LONG biYPelsPerMeter;
|
||
|
DWORD biClrUsed;
|
||
|
DWORD biClrImportant;
|
||
|
} BITMAPINFOHEADER;
|
||
|
|
||
|
/* constants for the biCompression field */
|
||
|
#define BI_RGB 0L
|
||
|
#define BI_RLE8 1L
|
||
|
#define BI_RLE4 2L
|
||
|
#define BI_BITFIELDS 3L
|
||
|
|
||
|
|
||
|
typedef struct tagRGBTRIPLE {
|
||
|
BYTE rgbtBlue;
|
||
|
BYTE rgbtGreen;
|
||
|
BYTE rgbtRed;
|
||
|
} RGBTRIPLE;
|
||
|
|
||
|
|
||
|
typedef struct { //tagRGBQUAD
|
||
|
BYTE rgbBlue;
|
||
|
BYTE rgbGreen;
|
||
|
BYTE rgbRed;
|
||
|
BYTE rgbReserved;
|
||
|
} RGBQUAD;
|
||
|
|
||
|
#endif // !defined(WIN32)
|
||
|
|
||
|
|
||
|
/* Some magic numbers */
|
||
|
|
||
|
#define BMP_BF_TYPE 0x4D42
|
||
|
/* word BM */
|
||
|
|
||
|
#define BMP_BF_OFF_BITS 54
|
||
|
/* 14 for file header + 40 for info header (not sizeof(), but packed size) */
|
||
|
|
||
|
#define BMP_BI_SIZE 40
|
||
|
/* packed size of info header */
|
||
|
|
||
|
|
||
|
/* Reads a WORD from a file in little endian format */
|
||
|
static WORD WordReadLE (FILE* fp)
|
||
|
{
|
||
|
WORD lsb, msb;
|
||
|
|
||
|
lsb = fgetc(fp);
|
||
|
msb = fgetc(fp);
|
||
|
return (msb << 8) | lsb;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/* Writes a WORD to a file in little endian format */
|
||
|
static void WordWriteLE(WORD x, FILE* fp)
|
||
|
{
|
||
|
BYTE lsb, msb;
|
||
|
|
||
|
lsb = (BYTE) (x & 0x00FF);
|
||
|
msb = (BYTE) (x >> 8);
|
||
|
fputc(lsb, fp);
|
||
|
fputc(msb, fp);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/* Reads a DWORD word from a file in little endian format */
|
||
|
static DWORD DWordReadLE(FILE* fp)
|
||
|
{
|
||
|
DWORD b1, b2, b3, b4;
|
||
|
|
||
|
b1 = fgetc(fp);
|
||
|
b2 = fgetc(fp);
|
||
|
b3 = fgetc(fp);
|
||
|
b4 = fgetc(fp);
|
||
|
return (b4 << 24) | (b3 << 16) | (b2 << 8) | b1;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/* Writes a DWORD to a file in little endian format */
|
||
|
static void DWordWriteLE(DWORD x, FILE* fp)
|
||
|
{
|
||
|
unsigned char b1, b2, b3, b4;
|
||
|
|
||
|
b1 = (x & 0x000000FF);
|
||
|
b2 = ((x >> 8) & 0x000000FF);
|
||
|
b3 = ((x >> 16) & 0x000000FF);
|
||
|
b4 = ((x >> 24) & 0x000000FF);
|
||
|
fputc(b1, fp);
|
||
|
fputc(b2, fp);
|
||
|
fputc(b3, fp);
|
||
|
fputc(b4, fp);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/* Reads a LONG word from a file in little endian format */
|
||
|
static LONG LongReadLE(FILE* fp)
|
||
|
{
|
||
|
LONG b1, b2, b3, b4;
|
||
|
|
||
|
b1 = fgetc(fp);
|
||
|
b2 = fgetc(fp);
|
||
|
b3 = fgetc(fp);
|
||
|
b4 = fgetc(fp);
|
||
|
return (b4 << 24) | (b3 << 16) | (b2 << 8) | b1;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/* Writes a LONG to a file in little endian format */
|
||
|
static void LongWriteLE(LONG x, FILE* fp)
|
||
|
{
|
||
|
char b1, b2, b3, b4;
|
||
|
|
||
|
b1 = (x & 0x000000FF);
|
||
|
b2 = ((x >> 8) & 0x000000FF);
|
||
|
b3 = ((x >> 16) & 0x000000FF);
|
||
|
b4 = ((x >> 24) & 0x000000FF);
|
||
|
fputc(b1, fp);
|
||
|
fputc(b2, fp);
|
||
|
fputc(b3, fp);
|
||
|
fputc(b4, fp);
|
||
|
}
|
||
|
|
||
|
|
||
|
int bitcount (DWORD w)
|
||
|
{
|
||
|
w = (0x55555555 & w) + (0x55555555 & (w>> 1));
|
||
|
w = (0x33333333 & w) + (0x33333333 & (w>> 2));
|
||
|
w = (0x0f0f0f0f & w) + (0x0f0f0f0f & (w>> 4));
|
||
|
w = (0x00ff00ff & w) + (0x00ff00ff & (w>> 8));
|
||
|
w = (0x0000ffff & w) + (0x0000ffff & (w>>16));
|
||
|
return w;
|
||
|
}
|
||
|
|
||
|
|
||
|
// read BMP palette
|
||
|
int Image::readPaletteBMP ( FILE* fp, RGBQUAD*& palette, int bit_count )
|
||
|
{
|
||
|
bool bColor;
|
||
|
int palSize = 0;
|
||
|
switch ( bit_count ) {
|
||
|
case 1: palSize = 2; break;
|
||
|
case 4: palSize = 16; break;
|
||
|
case 8: palSize = 256; break;
|
||
|
}
|
||
|
if ( palSize != 0 ) {
|
||
|
palette = new RGBQUAD[ palSize ];
|
||
|
memset(palette, 0, palSize * sizeof(RGBQUAD));
|
||
|
fread((void*) palette, sizeof(RGBQUAD), palSize, fp );
|
||
|
bColor = false;
|
||
|
for (int i = 0; !bColor && i < 2; ++i) {
|
||
|
bColor = bColor || (palette[i].rgbRed != palette[i].rgbGreen) || (palette[i].rgbBlue != palette[i].rgbGreen);
|
||
|
}
|
||
|
} else {
|
||
|
palette = 0x0;
|
||
|
}
|
||
|
return palSize;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
int Image::readBMP (FILE* fp, FILE* fp_a, bool bBaseImg )
|
||
|
{
|
||
|
RGBQUAD* palette;
|
||
|
int palSize;
|
||
|
int index;
|
||
|
Pixel pixel;
|
||
|
WORD red, green, blue;
|
||
|
DWORD bluemask, greenmask, redmask;
|
||
|
int bluewidth, greenwidth;
|
||
|
bool done;
|
||
|
|
||
|
if ( bBaseImg ) {
|
||
|
if (pixels && owns) delete[] pixels;
|
||
|
}
|
||
|
|
||
|
BITMAPFILEHEADER bmfh;
|
||
|
BITMAPINFOHEADER bmih;
|
||
|
|
||
|
/* Read file header */
|
||
|
bmfh.bfType = WordReadLE(fp);
|
||
|
bmfh.bfSize = DWordReadLE(fp);
|
||
|
bmfh.bfReserved1 = WordReadLE(fp);
|
||
|
bmfh.bfReserved2 = WordReadLE(fp);
|
||
|
bmfh.bfOffBits = DWordReadLE(fp);
|
||
|
|
||
|
/* Check file header */
|
||
|
if (bmfh.bfType != BMP_BF_TYPE) {
|
||
|
fprintf( stderr, "Image::readBMP: unrecognized type\n" );
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
/* Read info header */
|
||
|
bmih.biSize = DWordReadLE(fp);
|
||
|
bmih.biWidth = LongReadLE(fp);
|
||
|
bmih.biHeight = LongReadLE(fp);
|
||
|
bmih.biPlanes = WordReadLE(fp);
|
||
|
bmih.biBitCount = WordReadLE(fp);
|
||
|
bmih.biCompression = DWordReadLE(fp);
|
||
|
bmih.biSizeImage = DWordReadLE(fp);
|
||
|
bmih.biXPelsPerMeter = LongReadLE(fp);
|
||
|
bmih.biYPelsPerMeter = LongReadLE(fp);
|
||
|
bmih.biClrUsed = DWordReadLE(fp);
|
||
|
bmih.biClrImportant = DWordReadLE(fp);
|
||
|
|
||
|
if ((bmih.biWidth <= 0) || (bmih.biHeight <= 0) || (bmih.biPlanes != 1)) {
|
||
|
fprintf(stderr, "Image::readBMP: malformed file\n");
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
/* Creates the image */
|
||
|
if ( bBaseImg ) {
|
||
|
// Create storage and load base image
|
||
|
width = bmih.biWidth;
|
||
|
height = bmih.biHeight;
|
||
|
|
||
|
// Read palette (if there is one)
|
||
|
palSize = readPaletteBMP ( fp, palette, bmih.biBitCount );
|
||
|
|
||
|
switch ( bmih.biBitCount ) {
|
||
|
case 1: channels = 1; // 1 byte per bit - *bad storage, 2 color (B & W)
|
||
|
case 4: channels = 1; // 1 byte per 4-bits - not great, 16 color, index mode
|
||
|
case 8: channels = 1; // 1 byte per pixel, 256 color, index mode
|
||
|
case 16: channels = 3; // 2 byte per pixel, 16-bit color
|
||
|
case 24: channels = 3; // 3 byte per pixel (RGB), 24-bit color
|
||
|
case 32: channels = 3; // 4 byte per pixel, 32-bit color, compressed down to 24-bit
|
||
|
}
|
||
|
if ( palSize != 0 ) channels += 2; // use 3 channels for images with color palettes
|
||
|
if ( fp_a != 0x0 && channels == 3 ) channels = 4; // extra channel for alpha
|
||
|
|
||
|
// Create storage for new image (including alpha)
|
||
|
bits = 8;
|
||
|
maxValue = 255;
|
||
|
pixels = new unsigned char[width*height*channels];
|
||
|
owns = true;
|
||
|
memset(pixels, 0, width*height*channels);
|
||
|
} else {
|
||
|
// Load alpha image
|
||
|
bits = 8;
|
||
|
maxValue = 255;
|
||
|
|
||
|
// Read palette (if there is one)
|
||
|
palSize = readPaletteBMP ( fp, palette, bmih.biBitCount );
|
||
|
}
|
||
|
|
||
|
// Determine line length
|
||
|
int scanlinelength;
|
||
|
if ((width * bmih.biBitCount) % 32 == 0) scanlinelength = width * bmih.biBitCount / 8;
|
||
|
else scanlinelength = (width * bmih.biBitCount / 32 + 1) * 4;
|
||
|
|
||
|
|
||
|
// Read all the color info / data
|
||
|
switch (bmih.biBitCount) {
|
||
|
case 1: // 1 bit - monochrome, index mode
|
||
|
BYTE* scanlineByte;
|
||
|
scanlineByte = new BYTE[scanlinelength];
|
||
|
fseek(fp, bmfh.bfOffBits, SEEK_SET);
|
||
|
for (int y = 0; y < height; ++y) {
|
||
|
fread((void*) scanlineByte, scanlinelength, 1, fp);
|
||
|
for (int x = 0; x < width; ++x) {
|
||
|
index = (scanlineByte[x/8] >> (7 - (x % 8))) & 0x01;
|
||
|
if ( bBaseImg ) {
|
||
|
pixel.r = palette[index].rgbRed / 255.0;
|
||
|
pixel.g = palette[index].rgbGreen / 255.0;
|
||
|
pixel.b = palette[index].rgbBlue / 255.0;
|
||
|
setPixel (x, height - 1 - y, pixel);
|
||
|
} else {
|
||
|
setAlpha (x, height - 1 - y, index / 255.0f );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
delete [] scanlineByte;
|
||
|
break;
|
||
|
case 4: // 4 bit - 16 color, index mode
|
||
|
if (bmih.biCompression == BI_RGB) { // 4-bit, uncompressed data
|
||
|
BYTE* scanlineByte;
|
||
|
scanlineByte = new BYTE[scanlinelength];
|
||
|
fseek(fp, bmfh.bfOffBits, SEEK_SET);
|
||
|
for (int y = 0; y < height; ++y) {
|
||
|
fread((void*) scanlineByte, scanlinelength, 1, fp);
|
||
|
for (int x = 0; x < width; ++x) {
|
||
|
if (x % 2 == 0) index = (scanlineByte[x/2] >> 4) & 0x0F;
|
||
|
else index = scanlineByte[x/2] & 0x0F;
|
||
|
if ( bBaseImg ) {
|
||
|
pixel.r = palette[index].rgbRed / 255.0;
|
||
|
pixel.g = palette[index].rgbGreen / 255.0;
|
||
|
pixel.b = palette[index].rgbBlue / 255.0;
|
||
|
setPixel (x, height - 1 - y, pixel);
|
||
|
} else {
|
||
|
setAlpha (x, height - 1 - y, index/255.0f);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
delete [] scanlineByte;
|
||
|
} else if (bmih.biCompression == BI_RLE4) { // 4-bit RLE compression
|
||
|
unsigned char rleCode[2];
|
||
|
int curx = 0;
|
||
|
int cury = 0;
|
||
|
done = false;
|
||
|
fseek(fp, bmfh.bfOffBits, SEEK_SET);
|
||
|
while (!done && fp) {
|
||
|
fread((void*) rleCode, sizeof(char), 2, fp);
|
||
|
if (ferror(fp)) done = true;
|
||
|
if (rleCode[0] == 0 && rleCode[1] < 3) { // escape code (next byte is how)
|
||
|
if (rleCode[1] == 0) { // code 0 - goto next line
|
||
|
curx = 0;
|
||
|
++cury;
|
||
|
if (cury >= height) done = true;
|
||
|
} else if (rleCode[1] == 1) { // code 1 - finished image
|
||
|
done = true;
|
||
|
} else { // otherwise - two bytes reposition read
|
||
|
curx += fgetc(fp);
|
||
|
cury += fgetc(fp);
|
||
|
}
|
||
|
} else if (rleCode[0] == 0) { // absolute code (next byte is length)
|
||
|
BYTE byte;
|
||
|
for (int i = 0; i < (rleCode[1] + 1) / 2; ++i) {
|
||
|
byte = fgetc(fp);
|
||
|
index = (byte >> 4) & 0x0F;
|
||
|
if ( bBaseImg ) {
|
||
|
pixel.r = palette[index].rgbRed / 255.0;
|
||
|
pixel.g = palette[index].rgbGreen / 255.0;
|
||
|
pixel.b = palette[index].rgbBlue / 255.0;
|
||
|
setPixel(curx, height - 1 - cury, pixel);
|
||
|
} else {
|
||
|
setAlpha(curx, height - 1 - cury, index/255.0f );
|
||
|
}
|
||
|
++curx;
|
||
|
index = byte & 0x0F;
|
||
|
if ( bBaseImg ) {
|
||
|
pixel.r = palette[index].rgbRed / 255.0;
|
||
|
pixel.g = palette[index].rgbGreen / 255.0;
|
||
|
pixel.b = palette[index].rgbBlue / 255.0;
|
||
|
setPixel(curx, height - 1 - cury, pixel);
|
||
|
} else {
|
||
|
setAlpha(curx, height - 1 - cury, index/255.0f );
|
||
|
}
|
||
|
++curx;
|
||
|
}
|
||
|
if (((rleCode[1] + 1) / 2) % 2 != 0) // byte align
|
||
|
fgetc(fp);
|
||
|
} else {
|
||
|
for (int i = 0; i < rleCode[0]; ++i) {
|
||
|
if (i % 2 == 0) index = (rleCode[1] >> 4) & 0x0F;
|
||
|
else index = rleCode[1] & 0x0F;
|
||
|
|
||
|
if ( bBaseImg ) {
|
||
|
pixel.r = palette[index].rgbRed / 255.0;
|
||
|
pixel.g = palette[index].rgbGreen / 255.0;
|
||
|
pixel.b = palette[index].rgbBlue / 255.0;
|
||
|
setPixel(curx, height - 1 - cury, pixel);
|
||
|
} else {
|
||
|
setAlpha(curx, height - 1 - cury, index/255.0f );
|
||
|
}
|
||
|
++curx;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
} // 8-bit compression cases
|
||
|
break;
|
||
|
case 8: // 8 bit - 256 color, index mode
|
||
|
// read the palette
|
||
|
if (bmih.biCompression == BI_RGB) { // uncompressed data
|
||
|
BYTE* scanlineByte;
|
||
|
scanlineByte = new BYTE[scanlinelength];
|
||
|
fseek(fp, bmfh.bfOffBits, SEEK_SET);
|
||
|
|
||
|
for (int y = 0; y < height; ++y) {
|
||
|
fread((void*) scanlineByte, scanlinelength, 1, fp);
|
||
|
for (int x = 0; x < width; ++x) {
|
||
|
index = scanlineByte[x];
|
||
|
if ( bBaseImg ) {
|
||
|
pixel.r = palette[index].rgbRed / 255.0;
|
||
|
pixel.g = palette[index].rgbGreen / 255.0;
|
||
|
pixel.b = palette[index].rgbBlue / 255.0;
|
||
|
setPixel(x, height - 1 - y, pixel);
|
||
|
} else {
|
||
|
setAlpha(x, height - 1 - y, index/255.0f );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
delete [] scanlineByte;
|
||
|
} else if (bmih.biCompression == BI_RLE8) { // 8-bit RLE compression
|
||
|
unsigned char rleCode[2];
|
||
|
int curx = 0;
|
||
|
int cury = 0;
|
||
|
done = false;
|
||
|
|
||
|
fseek(fp, bmfh.bfOffBits, SEEK_SET);
|
||
|
while (!done && fp) {
|
||
|
fread((void*) rleCode, sizeof(char), 2, fp);
|
||
|
if (ferror(fp)) done = true;
|
||
|
if (rleCode[0] == 0 && rleCode[1] < 3) { // escape
|
||
|
if (rleCode[1] == 0) {
|
||
|
curx = 0;
|
||
|
++cury;
|
||
|
if (cury >= height) done = true;
|
||
|
} else if (rleCode[1] == 1) {
|
||
|
done = true;
|
||
|
} else {
|
||
|
curx += fgetc(fp);
|
||
|
cury += fgetc(fp);
|
||
|
}
|
||
|
} else if (rleCode[0] == 0) { // absolute mode
|
||
|
for (int i = 0; i < rleCode[1]; ++i) {
|
||
|
index = fgetc(fp);
|
||
|
if ( bBaseImg ) {
|
||
|
pixel.r = palette[index].rgbRed / 255.0;
|
||
|
pixel.g = palette[index].rgbGreen / 255.0;
|
||
|
pixel.b = palette[index].rgbBlue / 255.0;
|
||
|
setPixel (curx, height - 1 - cury, pixel);
|
||
|
} else {
|
||
|
setAlpha (curx, height - 1 - cury, index/255.0f);
|
||
|
}
|
||
|
++curx;
|
||
|
}
|
||
|
if (rleCode[1] % 2 != 0) fgetc(fp);
|
||
|
} else { // encoded mode
|
||
|
pixel.r = palette[rleCode[1]].rgbRed / 255.0;
|
||
|
pixel.g = palette[rleCode[1]].rgbGreen / 255.0;
|
||
|
pixel.b = palette[rleCode[1]].rgbBlue / 255.0;
|
||
|
if ( bBaseImg ) {
|
||
|
for (int i = 0; i < rleCode[0]; ++i) {
|
||
|
setPixel(curx, height - 1 - cury, pixel);
|
||
|
++curx;
|
||
|
}
|
||
|
} else {
|
||
|
for (int i = 0; i < rleCode[0]; ++i) {
|
||
|
setAlpha(curx, height - 1 - cury, rleCode[1]/255.0f );
|
||
|
++curx;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
} // while not done
|
||
|
} // 8-bit compression cases
|
||
|
break;
|
||
|
case 16: // 16 bit - 2^16 color, rgb mode
|
||
|
if (bmih.biCompression == BI_BITFIELDS) { // user specified
|
||
|
redmask = DWordReadLE(fp);
|
||
|
greenmask = DWordReadLE(fp);
|
||
|
bluemask = DWordReadLE(fp);
|
||
|
bluewidth = bitcount(bluemask);
|
||
|
greenwidth = bitcount(greenmask);
|
||
|
} else { // bmih.biCompression == BI_RGB // using default values
|
||
|
bluemask = 0x001F;
|
||
|
bluewidth = 5;
|
||
|
greenmask = 0x03E0;
|
||
|
greenwidth = 5;
|
||
|
redmask = 0x7C00;
|
||
|
}
|
||
|
WORD* scanlineWord;
|
||
|
scanlineWord = new WORD[(scanlinelength+1)/2];
|
||
|
fseek(fp, bmfh.bfOffBits, SEEK_SET);
|
||
|
|
||
|
for (int y = 0; y < height; ++y) {
|
||
|
fread( (void*) scanlineWord, scanlinelength, 1, fp);
|
||
|
for (int x = 0; x < width; ++x) {
|
||
|
if ( bBaseImg ) {
|
||
|
red = (scanlineWord[x] & redmask)
|
||
|
>> (bluewidth + greenwidth);
|
||
|
green = (scanlineWord[x] & greenmask) >> bluewidth;
|
||
|
blue = (scanlineWord[x] & bluemask);
|
||
|
pixel.r = red / 255.0;
|
||
|
pixel.g = green / 255.0;
|
||
|
pixel.b = blue / 255.0;
|
||
|
setPixel(x, height - 1 - y, pixel);
|
||
|
} else {
|
||
|
setAlpha(x, height - 1 - y, (scanlineWord[x]/65535.0f) );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
delete [] scanlineWord;
|
||
|
break;
|
||
|
case 24: // 24 bit - 2^24 color, rgb mode
|
||
|
RGBTRIPLE *scanline24;
|
||
|
scanline24 = new RGBTRIPLE[(scanlinelength+2)/3];
|
||
|
fseek(fp, bmfh.bfOffBits, SEEK_SET);
|
||
|
|
||
|
for (int y = 0; y < height; ++y) {
|
||
|
fread((void*) scanline24, scanlinelength, 1, fp);
|
||
|
for (int x = 0; x < width; ++x) {
|
||
|
if ( bBaseImg ) {
|
||
|
pixel.r = scanline24[x].rgbtRed / 255.0;
|
||
|
pixel.g = scanline24[x].rgbtGreen / 255.0;
|
||
|
pixel.b = scanline24[x].rgbtBlue / 255.0;
|
||
|
setPixel(x, height - 1 - y, pixel);
|
||
|
} else {
|
||
|
setAlpha(x, height - 1 - y, scanline24[x].rgbtRed / 255.0f );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
delete [] scanline24;
|
||
|
break;
|
||
|
case 32: // 32 bit - 2^32 color, rgb mode
|
||
|
if (bmih.biCompression == BI_RGB) { // default encoding
|
||
|
RGBQUAD* scanline32;
|
||
|
scanline32 = new RGBQUAD[(scanlinelength+3)/4];
|
||
|
fseek(fp, bmfh.bfOffBits, SEEK_SET);
|
||
|
for (int y = 0; y < height; ++y) {
|
||
|
fread((void*) scanline32, scanlinelength, 1, fp);
|
||
|
for (int x = 0; x < width; ++x) {
|
||
|
if ( bBaseImg ) {
|
||
|
pixel.r = scanline32[x].rgbRed / 255.0;
|
||
|
pixel.g = scanline32[x].rgbGreen / 255.0;
|
||
|
pixel.b = scanline32[x].rgbBlue / 255.0;
|
||
|
setPixel(x, height - 1 - y, pixel);
|
||
|
} else {
|
||
|
setAlpha(x, height - 1 - y, scanline32[x].rgbRed / 255.0f );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
delete [] scanline32;
|
||
|
} else if (bmih.biCompression == BI_BITFIELDS) { // user specified
|
||
|
// get masks and shifts
|
||
|
redmask = DWordReadLE(fp);
|
||
|
greenmask = DWordReadLE(fp);
|
||
|
bluemask = DWordReadLE(fp);
|
||
|
bluewidth = bitcount(bluemask);
|
||
|
greenwidth = bitcount(greenmask);
|
||
|
DWORD* scanlineDword;
|
||
|
scanlineDword = new DWORD[(scanlinelength+3)/4];
|
||
|
fseek(fp, bmfh.bfOffBits, SEEK_SET);
|
||
|
|
||
|
for (int y = 0; y < height; ++y) {
|
||
|
fread((void*) scanlineDword, scanlinelength, 1, fp);
|
||
|
for (int x = 0; x < width; ++x) {
|
||
|
if ( bBaseImg ) {
|
||
|
red = (scanlineDword[x] & redmask)
|
||
|
>> (bluewidth + greenwidth);
|
||
|
green = (scanlineDword[x] & greenmask) >> bluewidth;
|
||
|
blue = (scanlineDword[x] & bluemask);
|
||
|
|
||
|
pixel.r = red / 255.0;
|
||
|
pixel.g = green / 255.0;
|
||
|
pixel.b = blue / 255.0;
|
||
|
setPixel(x, height - 1 - y, pixel);
|
||
|
} else {
|
||
|
setAlpha(x, height - 1 - y, (scanlineDword[x] / 65535.0f) );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
delete [] scanlineDword;
|
||
|
}
|
||
|
break;
|
||
|
}; // close select
|
||
|
|
||
|
// Read alpha image (recursive call)
|
||
|
if ( bBaseImg && fp_a != 0x0 ) {
|
||
|
readBMP ( fp_a, 0x0, false );
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
int Image::writeBMP (FILE* fp)
|
||
|
{
|
||
|
if (!good())
|
||
|
{
|
||
|
fprintf(stderr, "Image::writeBMP: bad image\n");
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
BITMAPFILEHEADER bmfh;
|
||
|
BITMAPINFOHEADER bmih;
|
||
|
int lineLength;
|
||
|
|
||
|
if (channels == 1)
|
||
|
lineLength = width;
|
||
|
else
|
||
|
lineLength = width * 3;
|
||
|
|
||
|
if ((lineLength % 4) != 0)
|
||
|
lineLength = (lineLength / 4 + 1) * 4;
|
||
|
|
||
|
/* Write file header */
|
||
|
|
||
|
bmfh.bfType = BMP_BF_TYPE;
|
||
|
bmfh.bfSize = BMP_BF_OFF_BITS + lineLength * height;
|
||
|
bmfh.bfReserved1 = 0;
|
||
|
bmfh.bfReserved2 = 0;
|
||
|
bmfh.bfOffBits = BMP_BF_OFF_BITS;
|
||
|
|
||
|
if (channels == 1)
|
||
|
bmfh.bfOffBits += 256 * 4;
|
||
|
|
||
|
WordWriteLE(bmfh.bfType, fp);
|
||
|
DWordWriteLE(bmfh.bfSize, fp);
|
||
|
WordWriteLE(bmfh.bfReserved1, fp);
|
||
|
WordWriteLE(bmfh.bfReserved2, fp);
|
||
|
DWordWriteLE(bmfh.bfOffBits, fp);
|
||
|
|
||
|
/* Write info header */
|
||
|
|
||
|
bmih.biSize = BMP_BI_SIZE;
|
||
|
bmih.biWidth = width;
|
||
|
bmih.biHeight = height;
|
||
|
bmih.biPlanes = 1;
|
||
|
bmih.biBitCount = (channels == 1) ? 8 : 24;
|
||
|
bmih.biCompression = BI_RGB;
|
||
|
bmih.biSizeImage = lineLength * (DWORD) bmih.biHeight;
|
||
|
bmih.biXPelsPerMeter = 2925;
|
||
|
bmih.biYPelsPerMeter = 2925;
|
||
|
bmih.biClrUsed = (channels == 1) ? 256 : 0;
|
||
|
bmih.biClrImportant = 0;
|
||
|
|
||
|
DWordWriteLE(bmih.biSize, fp);
|
||
|
LongWriteLE(bmih.biWidth, fp);
|
||
|
LongWriteLE(bmih.biHeight, fp);
|
||
|
WordWriteLE(bmih.biPlanes, fp);
|
||
|
WordWriteLE(bmih.biBitCount, fp);
|
||
|
DWordWriteLE(bmih.biCompression, fp);
|
||
|
DWordWriteLE(bmih.biSizeImage, fp);
|
||
|
LongWriteLE(bmih.biXPelsPerMeter, fp);
|
||
|
LongWriteLE(bmih.biYPelsPerMeter, fp);
|
||
|
DWordWriteLE(bmih.biClrUsed, fp);
|
||
|
DWordWriteLE(bmih.biClrImportant, fp);
|
||
|
|
||
|
/* Write pixels */
|
||
|
|
||
|
Pixel pixel;
|
||
|
|
||
|
if (channels == 1)
|
||
|
{
|
||
|
// write 8-bit grayscale palette
|
||
|
|
||
|
unsigned char palettecolor[4];
|
||
|
for (int i = 0; i < 256; ++i)
|
||
|
{
|
||
|
memset(palettecolor, (unsigned char) i, 4);
|
||
|
fwrite((void*) palettecolor, sizeof(char), 4, fp);
|
||
|
}
|
||
|
|
||
|
// write image data
|
||
|
|
||
|
for (int y = 0; y < height; ++y)
|
||
|
{
|
||
|
int nbytes = 0;
|
||
|
for (int x = 0; x < width; ++x)
|
||
|
{
|
||
|
getPixel(x, height - y - 1, pixel);
|
||
|
fputc((int) (pixel.r * 255), fp);
|
||
|
nbytes++;
|
||
|
}
|
||
|
|
||
|
while ((nbytes % 4) != 0)
|
||
|
{
|
||
|
fputc(0, fp); nbytes++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
for (int y = 0; y < height; ++y)
|
||
|
{
|
||
|
int nbytes = 0;
|
||
|
for (int x = 0; x < width; ++x)
|
||
|
{
|
||
|
getPixel(x, height - y - 1, pixel);
|
||
|
fputc((int) (pixel.b * 255), fp); nbytes++;
|
||
|
fputc((int) (pixel.g * 255), fp); nbytes++;
|
||
|
fputc((int) (pixel.r * 255), fp); nbytes++;
|
||
|
}
|
||
|
|
||
|
while ((nbytes % 4) != 0)
|
||
|
{
|
||
|
fputc(0, fp);
|
||
|
nbytes++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (ferror(fp))
|
||
|
{
|
||
|
perror("Image::writeBMP");
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/******************************************************************
|
||
|
* .PNM file manipulation
|
||
|
*/
|
||
|
|
||
|
|
||
|
#define PNM_ASCII 0
|
||
|
#define PNM_BINARY 1
|
||
|
#define PNM_PBM 10
|
||
|
#define PNM_PGM 11
|
||
|
#define PNM_PPM 12
|
||
|
|
||
|
|
||
|
int Image::readPNM (const char* filename)
|
||
|
{
|
||
|
FILE* file = fopen(filename, "rb");
|
||
|
|
||
|
if (!file)
|
||
|
{
|
||
|
perror("Image::readPNM");
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
int result = readPNM(file);
|
||
|
fclose(file);
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
|
||
|
int Image::writePNM (const char* filename)
|
||
|
{
|
||
|
FILE* file = fopen(filename, "wb");
|
||
|
|
||
|
if (!file)
|
||
|
{
|
||
|
perror("Image::writePNM");
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
int result = writePNM(file);
|
||
|
fclose(file);
|
||
|
|
||
|
if (result == -1)
|
||
|
unlink(filename);
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
|
||
|
static inline void getWord (FILE* fp, char* s)
|
||
|
{
|
||
|
fscanf(fp, "%1023s", s);
|
||
|
}
|
||
|
|
||
|
|
||
|
static inline void getLine (FILE* fp, char* s)
|
||
|
{
|
||
|
fgets(s, 1024, fp);
|
||
|
}
|
||
|
|
||
|
|
||
|
static inline void getWordSkipComments (FILE* fp, char* s)
|
||
|
{
|
||
|
getWord(fp, s);
|
||
|
while (s[0] == '#' && !ferror(fp))
|
||
|
{
|
||
|
getLine(fp, s);
|
||
|
getWord(fp, s);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
int Image::readPNM (FILE* fp)
|
||
|
{
|
||
|
if (pixels && owns)
|
||
|
delete[] pixels;
|
||
|
pixels = NULL;
|
||
|
|
||
|
char nextLine[1024];
|
||
|
|
||
|
getWord(fp, nextLine);
|
||
|
|
||
|
int mode, type;
|
||
|
|
||
|
if (nextLine[0] == 'P')
|
||
|
{
|
||
|
switch (nextLine[1])
|
||
|
{
|
||
|
case '1':
|
||
|
mode = PNM_ASCII;
|
||
|
type = PNM_PBM;
|
||
|
channels = 1;
|
||
|
break;
|
||
|
|
||
|
case '2':
|
||
|
mode = PNM_ASCII;
|
||
|
type = PNM_PGM;
|
||
|
channels = 1;
|
||
|
break;
|
||
|
|
||
|
case '3':
|
||
|
mode = PNM_ASCII;
|
||
|
type = PNM_PPM;
|
||
|
channels = 3;
|
||
|
break;
|
||
|
|
||
|
case '5':
|
||
|
mode = PNM_BINARY;
|
||
|
type = PNM_PGM;
|
||
|
channels = 1;
|
||
|
break;
|
||
|
|
||
|
case '6':
|
||
|
mode = PNM_BINARY;
|
||
|
type = PNM_PPM;
|
||
|
channels = 3;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
fprintf( stderr, "Image::readPNM: malformed file\n" );
|
||
|
return -1;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
fprintf( stderr, "Image::readPNM: malformed file\n" );
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
getWordSkipComments(fp, nextLine);
|
||
|
width = atoi(nextLine);
|
||
|
|
||
|
getWordSkipComments(fp, nextLine);
|
||
|
height = atoi(nextLine);
|
||
|
|
||
|
if (type != PNM_PBM)
|
||
|
{
|
||
|
getWordSkipComments(fp, nextLine);
|
||
|
maxValue = atoi(nextLine);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
maxValue = 1;
|
||
|
}
|
||
|
|
||
|
if (ferror(fp))
|
||
|
{
|
||
|
perror("Image::readPNM");
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
if (width <= 0 || height <= 0 || maxValue <= 0)
|
||
|
{
|
||
|
fprintf( stderr, "Image::readPNM: malformed file\n" );
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
bits = (int) ceil(log10(maxValue + 1.0) / log10(2.0));
|
||
|
pixels = new unsigned char[width*height*channels];
|
||
|
owns = true;
|
||
|
memset(pixels, 0, width*height*channels);
|
||
|
|
||
|
int red, blue, green, intensity;
|
||
|
Pixel pixel;
|
||
|
BYTE *scanlineByte;
|
||
|
RGBTRIPLE *scanline24;
|
||
|
|
||
|
if (mode == PNM_ASCII)
|
||
|
{
|
||
|
if (type == PNM_PBM || type == PNM_PGM)
|
||
|
{
|
||
|
for (int y = 0; y < height; ++y)
|
||
|
{
|
||
|
for (int x = 0; x < width; ++x)
|
||
|
{
|
||
|
fscanf(fp, "%d", &intensity);
|
||
|
if (ferror(fp))
|
||
|
{
|
||
|
perror("Image::readPNM");
|
||
|
delete[] pixels;
|
||
|
pixels = NULL;
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
pixel.r = intensity / (float) maxValue;
|
||
|
setPixel(x,y,pixel);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else if (type == PNM_PPM)
|
||
|
{
|
||
|
for (int y = 0; y < height; ++y)
|
||
|
{
|
||
|
for (int x = 0; x < width; ++x)
|
||
|
{
|
||
|
fscanf(fp, "%d %d %d", &red, &green, &blue);
|
||
|
if (ferror(fp))
|
||
|
{
|
||
|
perror("Image::readPNM");
|
||
|
delete[] pixels;
|
||
|
pixels = NULL;
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
pixel.r = red / (float) maxValue;
|
||
|
pixel.g = green / (float) maxValue;
|
||
|
pixel.b = blue / (float) maxValue;
|
||
|
setPixel(x,y,pixel);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else if (mode == PNM_BINARY)
|
||
|
{
|
||
|
// move buffer up to the data's start
|
||
|
fgetc(fp);
|
||
|
|
||
|
if (type == PNM_PGM)
|
||
|
{
|
||
|
scanlineByte = new BYTE[width];
|
||
|
|
||
|
for (int y = 0; y < height; ++y)
|
||
|
{
|
||
|
fread((void*) scanlineByte, sizeof(BYTE), width, fp);
|
||
|
if (ferror(fp))
|
||
|
{
|
||
|
perror("Image::readPNM");
|
||
|
delete[] scanlineByte;
|
||
|
delete[] pixels;
|
||
|
pixels = NULL;
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
for (int x = 0; x < width; ++x)
|
||
|
{
|
||
|
pixel.r = scanlineByte[x] / (float) maxValue;
|
||
|
setPixel(x,y,pixel);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
delete[] scanlineByte;
|
||
|
}
|
||
|
else if (type == PNM_PPM)
|
||
|
{
|
||
|
scanline24 = new RGBTRIPLE[width];
|
||
|
|
||
|
for (int y = 0; y < height; ++y)
|
||
|
{
|
||
|
fread((void*) scanline24, sizeof(RGBTRIPLE), width, fp);
|
||
|
if (ferror(fp))
|
||
|
{
|
||
|
perror("Image::readPNM");
|
||
|
delete[] scanline24;
|
||
|
delete[] pixels;
|
||
|
pixels = NULL;
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
for (int x = 0; x < width; ++x)
|
||
|
{
|
||
|
pixel.b = scanline24[x].rgbtRed / (float) maxValue;
|
||
|
pixel.g = scanline24[x].rgbtGreen / (float) maxValue;
|
||
|
pixel.r = scanline24[x].rgbtBlue / (float) maxValue;
|
||
|
|
||
|
setPixel(x,y,pixel);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
delete[] scanline24;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
int Image::writePNM (FILE* fp)
|
||
|
{
|
||
|
if (!good())
|
||
|
{
|
||
|
fprintf(stderr, "Image::writePNM: bad image\n");
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
if (channels == 1)
|
||
|
{
|
||
|
if (bits == 1)
|
||
|
{
|
||
|
fprintf(fp, "P1\n");
|
||
|
fprintf(fp, "%d %d\n", width, height);
|
||
|
|
||
|
for (int j = 0; j < height; ++j)
|
||
|
{
|
||
|
for (int i = 0; i < width; ++i)
|
||
|
{
|
||
|
fprintf(fp, "%d ", pixels[index(i,j,0)] * maxValue / 255);
|
||
|
}
|
||
|
fprintf(fp, "\n");
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
fprintf(fp, "P2\n");
|
||
|
fprintf(fp, "%d %d %d\n", width, height, maxValue);
|
||
|
|
||
|
for (int j = 0; j < height; ++j)
|
||
|
{
|
||
|
for (int i = 0; i < width; ++i)
|
||
|
{
|
||
|
fprintf(fp, "%d ", pixels[index(i,j,0)] * maxValue / 255);
|
||
|
}
|
||
|
fprintf(fp, "\n");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
fprintf(fp, "P3\n");
|
||
|
fprintf(fp, "%d %d %d\n", width, height, maxValue);
|
||
|
|
||
|
for (int j = 0; j < height; ++j)
|
||
|
{
|
||
|
for (int i = 0; i < width; ++i)
|
||
|
{
|
||
|
fprintf(fp, "%d %d %d ",
|
||
|
pixels[index(i,j,0)] * maxValue / 255,
|
||
|
pixels[index(i,j,1)] * maxValue / 255,
|
||
|
pixels[index(i,j,2)] * maxValue / 255);
|
||
|
}
|
||
|
fprintf(fp, "\n");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|