#include "image.h" #include #include #include #include #include #include #include #include #include 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; } Image::Image (int width_, int height_) { width = width_; height = height_; channels = 3; 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];; memset(pixels, 0, width*height*channels); } Image::Image (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]; memset(pixels, 0, width*height*channels); } 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]; memset(pixels, 0, width*height*channels); } Image::Image (const char* filename) { width = 0; height = 0; channels = 0; bits = 0; maxValue = 0; pixels = NULL; read(filename); } Image::~Image () { if (pixels) 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]; 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]; for (int i = 0; i < width*height*channels; ++i) pixels[i] = image.pixels[i]; return *this; } 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 () { memset(pixels, 0, width*height*channels); } 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,ALPHA)] / 255.0; case 3: pixel.b = pixels[index(x,y,BLUE)] / 255.0; pixel.g = pixels[index(x,y,GREEN)] / 255.0; case 1: pixel.r = pixels[index(x,y,RED)] / 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,ALPHA)] / 255.0; case 3: pixel.b = pixels[index(x,y,BLUE)] / 255.0; pixel.g = pixels[index(x,y,GREEN)] / 255.0; case 1: pixel.r = pixels[index(x,y,RED)] / 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((x >= 0) && (x < width) && (y >= 0) && (y < height) && (channel >= 0) && (channel < channels)); assert((value >= 0.0) && (value <= 1.0)); 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::setPixel (int x, int y, Pixel& pixel) { assert(good()); assert((x >= 0) && (x < width) && (y >= 0) && (y < height)); switch (channels) { case 4: assert((pixel.a >= 0.0) && (pixel.a <= 1.0)); pixels[index(x,y,ALPHA)] = val2bits(pixel.a, bits); case 3: assert((pixel.b >= 0.0) && (pixel.b <= 1.0)); assert((pixel.g >= 0.0) && (pixel.g <= 1.0)); pixels[index(x,y,BLUE)] = val2bits(pixel.b, bits); pixels[index(x,y,GREEN)] = val2bits(pixel.g, bits); case 1: assert((pixel.r >= 0.0) && (pixel.r <= 1.0)); pixels[index(x,y,RED)] = val2bits(pixel.r, bits); default: break; } } void Image::setPixel_ (int x, int y, Pixel& pixel) { if (!good() || (x < 0) || (x >= width) || (y < 0) || (y >= height)) return; setPixel(x,y,pixel); } 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::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; } } int Image::read (const char* filename) { ifstream file(filename); if (file.bad()) { cerr << "Couldn't open file " << filename << endl; return -1; } unsigned char type[2]; file.read((char*) type, sizeof(type)); if (type[0] == 'P' && (type[1] == '1' || type[1] == '2' || type[1] == '3' || type[1] == '5' || type[1] == '6')) { file.seekg(0, ifstream::beg); return readPNM(file); } else if ((type[0] == 0x4D && type[1] == 0x42) || (type[1] == 0x4D && type[0] == 0x42)) { file.seekg(0, ifstream::beg); return readBMP(file); } else { cerr << "Unknown filetype!" << endl; file.close(); return -1; } } int Image::write (const char* filename) { int len = strlen(filename); const char* ext = &(filename[len-4]); if (strncmp(ext, ".pnm", 4) == 0) { cerr << "Writing PNM " << filename << endl; return writePNM(filename); } else if (strncmp(ext, ".bmp", 4) == 0) { cerr << "Writing BMP " << filename << endl; return writeBMP(filename); } else { char filenamewithext[1024]; strncpy(filenamewithext, filename, 1024 - 5); strncat(filenamewithext, ".pnm", 5); cerr << "Writing PNM " << filenamewithext << endl; return writePNM(filenamewithext); } } /****************************************************************** * .BMP file manipulation */ int Image::readBMP (const char* filename) { ifstream file(filename); if (file.good()) return readBMP(file); else return -1; } int Image::writeBMP (const char* filename) { ofstream file(filename); if (file.good()) { if (writeBMP(file) == -1) { file.close(); unlink(filename); return -1; } else { return 0; } } else { return -1; } } #if !defined(WIN32) || defined(_CONSOLE) typedef unsigned char BYTE; /* 8 bits */ typedef unsigned short int WORD; /* 16-bit unsigned integer. */ typedef unsigned int DWORD; /* 32-bit unsigned integer */ typedef int LONG; /* 32-bit signed integer */ 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) || defined(_CONSOLE) /* 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(ifstream& fp) { WORD lsb, msb; lsb = fp.get(); msb = fp.get(); return (msb << 8) | lsb; } /* Writes a WORD to a file in little endian format */ static void WordWriteLE(WORD x, ofstream& fp) { BYTE lsb, msb; lsb = (BYTE) (x & 0x00FF); msb = (BYTE) (x >> 8); fp.put(lsb); fp.put(msb); } /* Reads a DWORD word from a file in little endian format */ static DWORD DWordReadLE(ifstream& fp) { DWORD b1, b2, b3, b4; b1 = fp.get(); b2 = fp.get(); b3 = fp.get(); b4 = fp.get(); return (b4 << 24) | (b3 << 16) | (b2 << 8) | b1; } /* Writes a DWORD to a file in little endian format */ static void DWordWriteLE(DWORD x, ofstream& fp) { unsigned char b1, b2, b3, b4; b1 = (x & 0x000000FF); b2 = ((x >> 8) & 0x000000FF); b3 = ((x >> 16) & 0x000000FF); b4 = ((x >> 24) & 0x000000FF); fp.put(b1); fp.put(b2); fp.put(b3); fp.put(b4); } /* Reads a LONG word from a file in little endian format */ static LONG LongReadLE(ifstream& fp) { LONG b1, b2, b3, b4; b1 = fp.get(); b2 = fp.get(); b3 = fp.get(); b4 = fp.get(); return (b4 << 24) | (b3 << 16) | (b2 << 8) | b1; } /* Writes a LONG to a file in little endian format */ static void LongWriteLE(LONG x, ofstream& fp) { char b1, b2, b3, b4; b1 = (x & 0x000000FF); b2 = ((x >> 8) & 0x000000FF); b3 = ((x >> 16) & 0x000000FF); b4 = ((x >> 24) & 0x000000FF); fp.put(b1); fp.put(b2); fp.put(b3); fp.put(b4); } 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; } int Image::readBMP (ifstream& fp) { if (pixels) delete[] pixels; BITMAPFILEHEADER bmfh; BITMAPINFOHEADER bmih; /* Read file header */ /* fread(&bmfh, sizeof(bmfh), 1, fp); */ /* fread won't work on different platforms because of endian * issues. Sigh... */ bmfh.bfType = WordReadLE(fp); bmfh.bfSize = DWordReadLE(fp); bmfh.bfReserved1 = WordReadLE(fp); bmfh.bfReserved2 = WordReadLE(fp); bmfh.bfOffBits = DWordReadLE(fp); /* Debug file header fprintf(stderr, "file header:\n"); fprintf(stderr, "\tbfType: %x\n", (int) bmfh.bfType); fprintf(stderr, "\tbfSize: %d\n", (int) bmfh.bfSize); fprintf(stderr, "\tbfReserved1: %d\n", (int) bmfh.bfReserved1); fprintf(stderr, "\tbfReserved2: %d\n", (int) bmfh.bfReserved2); fprintf(stderr, "\tbfOffBits: %d\n", (int) bmfh.bfOffBits); */ /* Check file header */ if (bmfh.bfType != BMP_BF_TYPE) return -1; /* Read info header */ /* fread(&bmih, sizeof(bmih), 1, fp); */ /* same problem as above... */ 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); // for some reason the bmih.biSizeImage is not always // bmih.biWidth*bmih.biHeight ... we always use this // product as the image size bmih.biSizeImage = bmih.biHeight*bmih.biWidth; bmih.biXPelsPerMeter = LongReadLE(fp); bmih.biYPelsPerMeter = LongReadLE(fp); bmih.biClrUsed = DWordReadLE(fp); bmih.biClrImportant = DWordReadLE(fp); /* Debug info header fprintf(stderr, "info header:\n"); fprintf(stderr, "\tbiSize: %d\n", (int) bmih.biSize); fprintf(stderr, "\tbiWidth: %d\n", (int) bmih.biWidth); fprintf(stderr, "\tbiHeight: %d\n", (int) bmih.biHeight); fprintf(stderr, "\tbiPlanes: %d\n", (int) bmih.biPlanes); fprintf(stderr, "\tbiBitCount: %d\n", (int) bmih.biBitCount); fprintf(stderr, "\tbiCompression: %d\n", (int) bmih.biCompression); fprintf(stderr, "\tbiSizeImage: %d\n", (int) bmih.biSizeImage); fprintf(stderr, "\tbiXPelsPerMeter: %d\n", (int) bmih.biXPelsPerMeter); fprintf(stderr, "\tbiYPelsPerMeter: %d\n", (int) bmih.biYPelsPerMeter); fprintf(stderr, "\tbiClrUsed: %d\n", (int) bmih.biClrUsed); fprintf(stderr, "\tbiClrImportant: %d\n", (int) bmih.biClrImportant); */ /* Check info header */ if (bmih.biSize != BMP_BI_SIZE) cerr << "houston we may have a problem" << endl; if ((bmih.biWidth <= 0) || (bmih.biHeight <= 0) || (bmih.biPlanes != 1)) { return -1; } /* Creates the image */ width = bmih.biWidth; height = bmih.biHeight; channels = 3; bits = 8; maxValue = 255; pixels = new unsigned char[width*height*channels]; memset(pixels, 0, width*height*channels); int scanlinelength; if ((width * bmih.biBitCount) % 32 == 0) scanlinelength = width * bmih.biBitCount / 8; else scanlinelength = (width * bmih.biBitCount / 32 + 1) * 4; RGBQUAD *palette; BYTE *scanlineByte; WORD *scanlineWord; DWORD *scanlineDword; RGBTRIPLE *scanline24; RGBQUAD *scanline32; int index; Pixel pixel; WORD red, green, blue; DWORD bluemask, greenmask, redmask; int bluewidth, greenwidth; bool done; // read all the color info / data switch (bmih.biBitCount) { case 1: // 1 bit - monochrome, index mode // read the palette palette = new RGBQUAD[2]; memset(palette, 0, 2 * sizeof(RGBQUAD)); fp.read((char*) palette, 2 * sizeof(RGBQUAD)); // read the data line by line scanlineByte = new BYTE[scanlinelength]; fp.seekg((long) bmfh.bfOffBits, ifstream::beg); for (int y = 0; y < height; ++y) { fp.read((char*) scanlineByte, scanlinelength); if (fp.fail()) { cerr << "unknown stream error!" << endl; delete[] palette; delete[] pixels; pixels = NULL; return -1; } for (int x = 0; x < width; ++x) { index = (scanlineByte[x/8] >> (7 - (x % 8))) & 0x01; 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); } } delete[] scanlineByte; delete[] palette; break; case 4: // 4 bit - 16 color, index mode // read the palette palette = new RGBQUAD[16]; memset(palette, 0, 16 * sizeof(RGBQUAD)); fp.read((char*) palette, 16 * sizeof(RGBQUAD)); // detect a grayscale palette, and modify image accordingly done = false; for (int i = 0; !done && i < 16; ++i) { done = done || (palette[i].rgbRed != palette[i].rgbGreen) || (palette[i].rgbBlue != palette[i].rgbGreen); } if (!done) { delete[] pixels; channels = 1; pixels = new unsigned char[width*height*channels]; memset(pixels, 0, width*height*channels); } // read the data line by line if (bmih.biCompression == BI_RGB) // uncompressed data { scanlineByte = new BYTE[scanlinelength]; fp.seekg((long) bmfh.bfOffBits, ifstream::beg); for (int y = 0; y < height; ++y) { fp.read((char*) scanlineByte, scanlinelength); if (fp.fail()) { cerr << "unknown stream error!" << endl; delete[] palette; delete[] pixels; pixels = NULL; return -1; } for (int x = 0; x < width; ++x) { if (x % 2 == 0) index = (scanlineByte[x/2] >> 4) & 0x0F; else index = scanlineByte[x/2] & 0x0F; 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); } } delete[] scanlineByte; } else if (bmih.biCompression == BI_RLE4) // 4-bit RLE compression { unsigned char rleCode[2]; int curx = 0; int cury = 0; done = false; fp.seekg((long) bmfh.bfOffBits, ifstream::beg); while (!done && fp) { fp.read(rleCode, 2); 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 += (int) fp.get(); cury += (int) fp.get(); } } else if (rleCode[0] == 0) // absolute mode { BYTE byte; for (int i = 0; i < (rleCode[1] + 1) / 2; ++i) { byte = fp.get(); index = (byte >> 4) & 0x0F; 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); ++curx; index = byte & 0x0F; 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); ++curx; } if (((rleCode[1] + 1) / 2) % 2 != 0) fp.get(); } else // encoded mode { for (int i = 0; i < rleCode[0]; ++i) { if (i % 2 == 0) index = (rleCode[1] >> 4) & 0x0F; else index = rleCode[1] & 0x0F; 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); ++curx; } } } if (fp.fail()) { cerr << "unknown stream error!" << endl; delete[] palette; delete[] pixels; pixels = NULL; return -1; } } delete[] palette; break; case 8: // 8 bit - 256 color, index mode // read the palette palette = new RGBQUAD[256]; memset(palette, 0, 256 * sizeof(RGBQUAD)); fp.read((char*) palette, 256 * sizeof(RGBQUAD)); // detect a grayscale palette, and modify image accordingly done = false; for (int i = 0; !done && i < 256; ++i) { done = done || (palette[i].rgbRed != palette[i].rgbGreen) || (palette[i].rgbBlue != palette[i].rgbGreen); } if (!done) { delete[] pixels; channels = 1; pixels = new unsigned char[width*height*channels]; memset(pixels, 0, width*height*channels); } // read the data line by line if (bmih.biCompression == BI_RGB) // uncompressed data { scanlineByte = new BYTE[scanlinelength]; fp.seekg((long) bmfh.bfOffBits, ifstream::beg); for (int y = 0; y < height; ++y) { fp.read((char*) scanlineByte, scanlinelength); if (fp.fail()) { cerr << "unknown stream error!" << endl; delete[] palette; delete[] pixels; pixels = NULL; return -1; } for (int x = 0; x < width; ++x) { index = scanlineByte[x]; 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); } } delete[] scanlineByte; } else if (bmih.biCompression == BI_RLE8) // 8-bit RLE compression { unsigned char rleCode[2]; int curx = 0; int cury = 0; done = false; fp.seekg((long) bmfh.bfOffBits, ifstream::beg); while (!done && fp) { fp.read(rleCode, 2); 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 += (int) fp.get(); cury += (int) fp.get(); } } else if (rleCode[0] == 0) // absolute mode { for (int i = 0; i < rleCode[1]; ++i) { index = fp.get(); 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); ++curx; } if (rleCode[1] % 2 != 0) fp.get(); } 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; for (int i = 0; i < rleCode[0]; ++i) { setPixel(curx, height - 1 - cury, pixel); ++curx; } } } if (fp.fail()) { cerr << "unknown stream error!" << endl; delete[] palette; delete[] pixels; pixels = NULL; return -1; } } delete[] palette; break; case 16: // 16 bit - 2^16 color, rgb mode // set the color masks and shifts if (bmih.biCompression == BI_RGB) // using default values { bluemask = 0x001F; bluewidth = 5; greenmask = 0x03E0; greenwidth = 5; redmask = 0x7C00; } else if (bmih.biCompression == BI_BITFIELDS) // user specified { redmask = DWordReadLE(fp); greenmask = DWordReadLE(fp); bluemask = DWordReadLE(fp); bluewidth = bitcount(bluemask); greenwidth = bitcount(greenmask); } // get data line by line scanlineWord = new WORD[scanlinelength]; fp.seekg((long) bmfh.bfOffBits, ifstream::beg); for (int y = 0; y < height; ++y) { fp.read((char*) scanlineWord, scanlinelength); if (fp.fail()) { cerr << "unknown stream error!" << endl; delete[] pixels; pixels = NULL; return -1; } for (int x = 0; x < width; ++x) { 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); } } delete[] scanlineWord; break; case 24: // 24 bit - 2^24 color, rgb mode // read the data line by line scanline24 = new RGBTRIPLE[scanlinelength]; fp.seekg((long) bmfh.bfOffBits, ifstream::beg); for (int y = 0; y < height; ++y) { fp.read((char*) scanline24, scanlinelength); if (fp.fail()) { cerr << "unknown stream error!" << endl; delete[] pixels; pixels = NULL; return -1; } for (int x = 0; x < width; ++x) { 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); } } delete[] scanline24; break; case 32: // 32 bit - 2^32 color, rgb mode // read data line by line if (bmih.biCompression == BI_RGB) // default encoding { scanline32 = new RGBQUAD[scanlinelength]; fp.seekg((long) bmfh.bfOffBits, ifstream::beg); for (int y = 0; y < height; ++y) { fp.read((char*) scanline32, scanlinelength); if (fp.fail()) { cerr << "unknown stream error!" << endl; delete[] pixels; pixels = NULL; return -1; } for (int x = 0; x < width; ++x) { 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); } } 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); scanlineDword = new DWORD[scanlinelength]; fp.seekg((long) bmfh.bfOffBits, ifstream::beg); for (int y = 0; y < height; ++y) { fp.read((char*) scanlineDword, scanlinelength); if (fp.fail()) { cerr << "unknown stream error!" << endl; delete[] pixels; pixels = NULL; return -1; } for (int x = 0; x < width; ++x) { 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); } } delete[] scanlineDword; } break; } return 0; } int Image::writeBMP (ofstream& fp) { if (!good()) { cerr << "Invalid image" << endl; 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); fp.write(palettecolor, 4); } // 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); fp.put((unsigned char) (pixel.r * 255)), nbytes++; } while ((nbytes % 4) != 0) { fp.put((unsigned char) 0); 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); fp.put((unsigned char) (pixel.b * 255)), nbytes++; fp.put((unsigned char) (pixel.g * 255)), nbytes++; fp.put((unsigned char) (pixel.r * 255)), nbytes++; } while ((nbytes % 4) != 0) { fp.put((unsigned char) 0); nbytes++; } } } if (fp.fail()) { cerr << "unknown stream error" << endl; 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) { ifstream file(filename); if (file.good()) return readPNM(file); else return -1; } int Image::writePNM (const char* filename) { ofstream file(filename); if (file.good()) { if (writePNM(file) == -1) { file.close(); unlink(filename); return -1; } else { return 0; } } else { return -1; } } int Image::readPNM (ifstream& file) { if (pixels) delete[] pixels; pixels = NULL; char nextLine[1024]; file >> 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: cerr << "Not a PNM file!" << endl; return -1; } } else { cerr << "Not a PNM file!" << endl; return -1; } file >> nextLine; while (nextLine[0] == '#' && file) { file.getline(nextLine, 1024, '\n'); file >> nextLine; } width = atoi(nextLine); file >> nextLine; while (nextLine[0] == '#' && file) { file.getline(nextLine, 1024, '\n'); file >> nextLine; } height = atoi(nextLine); if (type != PNM_PBM) { file >> nextLine; while (nextLine[0] == '#' && file) { file.getline(nextLine, 1024, '\n'); file >> nextLine; } maxValue = atoi(nextLine); } else { maxValue = 1; } if (width <= 0 || height <= 0 || maxValue <= 0 || file.fail()) { return -1; } bits = (int) ceil(log10(maxValue + 1.0) / log10(2.0)); pixels = new unsigned char[width*height*channels]; 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) { file >> intensity; if (!file) { cerr << "unknown stream error!" << endl; 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) { file >> red >> green >> blue; if (!file) { cerr << "unknown stream error!" << endl; 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 file.get(); if (type == PNM_PGM) { scanlineByte = new BYTE[width]; for (int y = 0; y < height; ++y) { file.read((unsigned char*) scanline24, width*3); if (!file) { cerr << "unknown stream error!" << endl; 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) { file.read((unsigned char*) scanline24, width*3); if (!file) { cerr << "unknown stream error!" << endl; 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 (ofstream& file) { if (!good()) { cerr << "Invalid image" << endl; return -1; } if (channels == 1) { if (bits == 1) { file << "P1" << endl; file << width << " " << height << endl; for (int j = 0; j < height; ++j) { for (int i = 0; i < width; ++i) { file << pixels[index(i,j,0)] * maxValue / 255 << endl; } } } else { file << "P2" << endl; file << width << " " << height << " " << maxValue << endl; for (int j = 0; j < height; ++j) { for (int i = 0; i < width; ++i) { file << pixels[index(i,j,0)] * maxValue / 255 << endl; } } } } else { file << "P3" << endl; file << width << " " << height << " " << maxValue << endl; for (int j = 0; j < height; ++j) { for (int i = 0; i < width; ++i) { file << pixels[index(i,j,0)] * maxValue / 255 << " " << pixels[index(i,j,1)] * maxValue / 255 << " " << pixels[index(i,j,2)] * maxValue / 255 << endl; } } } return 0; }