/* 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 #include #include #include #include #include "gl_helper.h" // Link with JPEG library on platforms that support it. #ifdef USE_JPEG #include #include #include "jerror.h" #endif #ifndef WIN32 #include #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; }