| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| #include "Image.h" |
| #include "Error.h" |
| #include "ImageIO.h" |
| #include <vector> |
|
|
| |
| #define HAVE_PNG_LIB |
|
|
| #ifdef HAVE_PNG_LIB |
| |
| void ReadFilePNG(CByteImage& img, const char* filename); |
| void WriteFilePNG(CByteImage img, const char* filename); |
| #endif |
|
|
|
|
|
|
| |
| |
|
|
| #ifdef HAVE_JPEG_READER |
| |
| void ReadFileJPG(CByteImage& img, const char* filename); |
| #endif |
|
|
| |
| |
| |
|
|
| typedef unsigned char uchar; |
|
|
| struct CTargaHead |
| { |
| uchar idLength; |
| uchar colorMapType; |
| uchar imageType; |
| uchar cMapOrigin[2]; |
| uchar cMapLength[2]; |
| uchar cMapBits; |
| short x0; |
| short y0; |
| short width; |
| short height; |
| uchar pixelSize; |
| uchar descriptor; |
| }; |
|
|
| |
| const int TargaRawColormap = 1; |
| const int TargaRawRGB = 2; |
| const int TargaRawBW = 3; |
| const int TargaRunColormap = 9; |
| const int TargaRunRGB = 10; |
| const int TargaRunBW = 11; |
|
|
| |
| const int TargaAttrBits = 15; |
| const int TargaScreenOrigin = (1<<5); |
| const int TargaCMapSize = 256; |
| const int TargaCMapBands = 3; |
| |
| |
| |
| |
| |
| |
| |
|
|
| class CTargaRLC |
| { |
| |
| public: |
| CTargaRLC(bool RLC) : m_count(0), m_RLC(RLC) {} |
| uchar* getBytes(int nBytes, FILE *stream); |
| private: |
| int m_count; |
| bool m_RLC; |
| bool m_isRun; |
| uchar m_buffer[4]; |
| }; |
|
|
| inline uchar* CTargaRLC::getBytes(int nBytes, FILE *stream) |
| { |
| |
| if (nBytes > 4) |
| throw CError("ReadFileTGA: only support pixels up to 4 bytes long"); |
|
|
| if (! m_RLC) |
| { |
| if ((int)fread(m_buffer, sizeof(uchar), nBytes, stream) != nBytes) |
| throw CError("ReadFileTGA: file is too short"); |
| } |
| else |
| { |
| if (m_count == 0) |
| { |
| |
| m_count = fgetc(stream); |
| m_isRun = (m_count & 0x80) != 0; |
| m_count = (m_count & 0x7f) + 1; |
| if (m_isRun) |
| if ((int)fread(m_buffer, sizeof(uchar), nBytes, stream) != nBytes) |
| throw CError("ReadFileTGA: file is too short"); |
| } |
| if (! m_isRun) |
| { |
| if ((int)fread(m_buffer, sizeof(uchar), nBytes, stream) != nBytes) |
| throw CError("ReadFileTGA: file is too short"); |
| } |
| m_count -= 1; |
| } |
| return m_buffer; |
| } |
|
|
| void ReadFileTGA(CByteImage& img, const char* filename) |
| { |
| |
| FILE *stream = fopen(filename, "rb"); |
| if (stream == 0) |
| throw CError("ReadFileTGA: could not open %s", filename); |
| CTargaHead h; |
| if ((int)fread(&h, sizeof(CTargaHead), 1, stream) != 1) |
| throw CError("ReadFileTGA(%s): file is too short", filename); |
|
|
| |
| if (h.idLength > 0) |
| { |
| char* tmp = new char[h.idLength]; |
| int nread = (int)fread(tmp, sizeof(uchar), h.idLength, stream); |
| delete tmp; |
| if (nread != h.idLength) |
| throw CError("ReadFileTGA(%s): file is too short", filename); |
| } |
| |
| bool reverseRows = (h.descriptor & TargaScreenOrigin) != 0; |
| int fileBytes = (h.pixelSize + 7) / 8; |
|
|
| |
| uchar colormap[TargaCMapSize][TargaCMapBands]; |
| int cMapSize = 0; |
| bool grayRamp = false; |
| if (h.colorMapType == 1) |
| { |
| cMapSize = (h.cMapLength[1] << 8) + h.cMapLength[0]; |
| if (h.cMapBits != 24) |
| throw CError("ReadFileTGA(%s): only 24-bit colormap currently supported", filename); |
| int l = fileBytes * cMapSize; |
| if (l > TargaCMapSize * TargaCMapBands) |
| throw CError("ReadFileTGA(%s): colormap is too large", filename); |
| if ((int)fread(colormap, sizeof(uchar), l, stream) != l) |
| throw CError("ReadFileTGA(%s): could not read the colormap", filename); |
|
|
| |
| int i; |
| for (i = 0; i < cMapSize; i++) { |
| for (int j = 0; j < TargaCMapBands; j++) |
| if (colormap[i][j] != i) |
| break; |
| } |
| grayRamp = (i == cMapSize); |
| } |
| bool isGray = |
| h.imageType == TargaRawBW || h.imageType == TargaRunBW || |
| grayRamp && |
| (h.imageType == TargaRawColormap || h.imageType == TargaRunColormap); |
| bool isRaw = h.imageType == TargaRawBW || h.imageType == TargaRawRGB || |
| h.imageType == TargaRawRGB && isGray; |
|
|
| |
| CShape sh(h.width, h.height, (isGray) ? 1 : 4); |
| |
| |
| img.ReAllocate(sh, false); |
|
|
| |
| CTargaRLC rlc(! isRaw); |
|
|
| |
| for (int y = 0; y < sh.height; y++) |
| { |
| int yr = reverseRows ? sh.height-1-y : y; |
| uchar* ptr = (uchar *) img.PixelAddress(0, yr, 0); |
| if (fileBytes == sh.nBands && isRaw) |
| { |
| |
| int n = sh.width*sh.nBands; |
| if ((int)fread(ptr, sizeof(uchar), n, stream) != n) |
| throw CError("ReadFileTGA(%s): file is too short", filename); |
| } |
| else |
| { |
| |
| for (int x = 0; x < sh.width; x++, ptr += sh.nBands) |
| { |
| uchar* buf = rlc.getBytes(fileBytes, stream); |
| if (fileBytes == 1 && sh.nBands == 1) |
| { |
| ptr[0] = buf[0]; |
| } |
| else if (fileBytes == 1 && sh.nBands == 4) |
| { |
| for (int i = 0; i < 3; i++) |
| ptr[i] = (isGray) ? buf[0] : colormap[buf[0]][i]; |
| ptr[3] = 255; |
| } |
| else if ((fileBytes == 3 || fileBytes == 4) && sh.nBands == 4) |
| { |
| int i; |
| for (i = 0; i < fileBytes; i++) |
| ptr[i] = buf[i]; |
| if (i == 3) |
| ptr[3] = 255; |
| } |
| else |
| throw CError("ReadFileTGA(%s): unhandled pixel depth or # of bands", filename); |
| } |
| } |
| } |
|
|
| if (fclose(stream)) |
| throw CError("ReadFileTGA(%s): error closing file", filename); |
| } |
|
|
| void WriteFileTGA(CImage img, const char* filename) |
| { |
| |
| CShape sh = img.Shape(); |
| int nBands = sh.nBands; |
| if (nBands != 1 && nBands != 3 && nBands != 4) |
| throw CError("WriteFileTGA(%s): can only write 1, 3, or 4 bands", filename); |
|
|
| |
| #if 0 |
| if (img.PixType() != unsigned_8) |
| { |
| CImage u8img(sh, unsigned_8); |
| TypeConvert(img, u8img); |
| img = u8img; |
| } |
| #endif |
|
|
| |
| CTargaHead h; |
| memset(&h, 0, sizeof(h)); |
| h.imageType = (nBands == 1) ? TargaRawBW : TargaRawRGB; |
| |
| h.width = sh.width; |
| h.height = sh.height; |
| h.pixelSize = 8 * nBands; |
| bool reverseRows = false; |
|
|
| |
| FILE *stream = fopen(filename, "wb"); |
| if (stream == 0) |
| throw CError("WriteFileTGA: could not open %s", filename); |
| if (fwrite(&h, sizeof(CTargaHead), 1, stream) != 1) |
| throw CError("WriteFileTGA(%s): file is too short", filename); |
|
|
| |
| for (int y = 0; y < sh.height; y++) |
| { |
| int yr = reverseRows ? sh.height-1-y : y; |
| char* ptr = (char *) img.PixelAddress(0, yr, 0); |
| int n = sh.width*sh.nBands; |
| if ((int)fwrite(ptr, sizeof(uchar), n, stream) != n) |
| throw CError("WriteFileTGA(%s): file is too short", filename); |
| } |
|
|
| if (fclose(stream)) |
| throw CError("WriteFileTGA(%s): error closing file", filename); |
| } |
|
|
| |
| |
| |
|
|
| void skip_comment(FILE *fp) |
| { |
| |
|
|
| char c; |
| while ((c=getc(fp)) == '#') |
| while (getc(fp) != '\n'); |
| ungetc(c, fp); |
| } |
|
|
| void skip_space(FILE *fp) |
| { |
| |
|
|
| char c; |
| do { |
| c = getc(fp); |
| } while (c == '\n' || c == ' ' || c == '\t' || c == '\r'); |
| ungetc(c, fp); |
| } |
|
|
| void read_header(FILE *fp, char *imtype, char c1, char c2, |
| int *width, int *height, int *nbands, int thirdArg) |
| { |
| |
|
|
| char c; |
| |
| if (getc(fp) != c1 || getc(fp) != c2) |
| throw CError("ReadFilePGM: wrong magic code for %s file", imtype); |
| skip_space(fp); |
| skip_comment(fp); |
| skip_space(fp); |
| fscanf(fp, "%d", width); |
| skip_space(fp); |
| fscanf(fp, "%d", height); |
| if (thirdArg) { |
| skip_space(fp); |
| fscanf(fp, "%d", nbands); |
| } |
| |
| c = getc(fp); |
| if (c == '\r') |
| c = getc(fp); |
| if (c != '\n') { |
| if (c == ' ' || c == '\t' || c == '\r') |
| throw CError("newline expected in file after image height"); |
| else |
| throw CError("whitespace expected in file after image height"); |
| } |
| } |
|
|
|
|
| void ReadFilePGM(CByteImage& img, const char* filename) |
| { |
| |
| FILE *stream = fopen(filename, "rb"); |
| if (stream == 0) |
| throw CError("ReadFilePGM: could not open %s", filename); |
|
|
| int width, height, nBands; |
| char *dot = strrchr(filename, '.'); |
| int isGray = 0, isFloat = 0; |
|
|
| if (strcmp(dot, ".pgm") == 0) { |
| read_header(stream, "PGM", 'P', '5', &width, &height, &nBands, 1); |
| isGray = 1; |
| } |
| else if (strcmp(dot, ".ppm") == 0) { |
| read_header(stream, "PGM", 'P', '6', &width, &height, &nBands, 1); |
| isGray = 0; |
| } |
| else if (strcmp(dot, ".pmf") == 0) { |
| read_header(stream, "PMF", 'P', '9', &width, &height, &nBands, 1); |
| isGray = 0; |
| isFloat = 1; |
| } |
|
|
|
|
| |
| CShape sh(width, height, (isGray) ? 1 : (isFloat) ? nBands : 4); |
| |
| |
| if (isFloat) |
| ((CFloatImage *) &img)->ReAllocate(sh); |
| else |
| img.ReAllocate(sh); |
| |
| if (isGray || isFloat) { |
| |
| |
| int n = isFloat ? width * nBands * sizeof(float) : sh.width; |
| for (int y = 0; y<sh.height; y++) { |
| uchar* ptr = (uchar *) img.PixelAddress(0, y, 0); |
| if ((int)fread(ptr, sizeof(uchar), n, stream) != n) |
| throw CError("ReadFilePGM(%s): file is too short", filename); |
| } |
| } |
| else { |
|
|
| |
| int n = sh.width*3; |
| std::vector<uchar> rowBuf; |
| rowBuf.resize(n); |
| for (int y = 0; y<sh.height; y++) { |
| if ((int)fread(&rowBuf[0], sizeof(uchar), n, stream) != n) |
| throw CError("ReadFilePGM(%s): file is too short", filename); |
|
|
| uchar* ptr = (uchar *) img.PixelAddress(0, y, 0); |
| int x = 0; |
| while (x < n) { |
| ptr[2] = rowBuf[x++]; |
| ptr[1] = rowBuf[x++]; |
| ptr[0] = rowBuf[x++]; |
| ptr[3] = 255; |
| ptr += 4; |
| } |
| } |
| } |
|
|
| if (fclose(stream)) |
| throw CError("ReadFilePGM(%s): error closing file", filename); |
| } |
|
|
|
|
|
|
| void WriteFilePGM(CByteImage img, const char* filename) |
| { |
| |
| CShape sh = img.Shape(); |
| int nBands = sh.nBands; |
| int isFloat = img.PixType() == typeid(float); |
| |
| |
| char *dot = strrchr(filename, '.'); |
| if (strcmp(dot, ".pgm") == 0 && nBands != 1) |
| throw CError("WriteFilePGM(%s): can only write 1-band image as pgm", filename); |
| |
| if (strcmp(dot, ".ppm") == 0 && nBands != 3 && nBands != 4) |
| throw CError("WriteFilePGM(%s): can only write 3 or 4-band image as ppm", filename); |
|
|
| if (strcmp(dot, ".pmf") == 0 && ! isFloat) |
| throw CError("WriteFilePMF(%s): can only write floating point image as pmf", filename); |
| |
| FILE *stream = fopen(filename, "wb"); |
| if (stream == 0) |
| throw CError("WriteFilePGM: could not open %s", filename); |
|
|
| if (nBands == 1 || isFloat) { |
| |
| |
| fprintf(stream, "P%d\n%d %d\n%d\n", isFloat ? 9 : 5, sh.width, sh.height, |
| isFloat ? sh.nBands : 255); |
|
|
| |
| int n = isFloat ? sh.width * sh.nBands * sizeof(float) : sh.width; |
| for (int y = 0; y<sh.height; y++) { |
| char* ptr = (char *) img.PixelAddress(0, y, 0); |
| if ((int)fwrite(ptr, sizeof(uchar), n, stream) != n) |
| throw CError("WriteFilePGM(%s): file is too short", filename); |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| else if (nBands == 3 || nBands == 4) { |
| |
| |
| fprintf(stream, "P6\n%d %d\n%d\n", sh.width, sh.height, 255); |
|
|
| |
| int n = sh.width*3; |
| std::vector<uchar> rowBuf; |
| rowBuf.resize(n); |
| for (int y = 0; y<sh.height; y++) { |
| uchar* ptr = (uchar *) img.PixelAddress(0, y, 0); |
| int x = 0; |
| while (x < n) { |
| rowBuf[x++] = ptr[2]; |
| rowBuf[x++] = ptr[1]; |
| rowBuf[x++] = ptr[0]; |
| ptr += nBands; |
| } |
|
|
| if ((int)fwrite(&rowBuf[0], sizeof(uchar), n, stream) != n) |
| throw CError("WriteFilePGM(%s): file is too short", filename); |
| } |
| } |
| else |
| throw CError("WriteFilePGM(%s): unhandled # of bands %d", filename, nBands); |
|
|
| |
|
|
| if (fclose(stream)) |
| throw CError("WriteFilePGM(%s): error closing file", filename); |
| } |
|
|
|
|
| |
| |
| |
|
|
| void ReadImage (CImage& img, const char* filename) |
| { |
| if (filename == NULL) |
| throw CError("ReadImage: empty filename"); |
|
|
| |
| char *dot = strrchr(filename, '.'); |
| if (dot == NULL) |
| throw CError("ReadImage: extension required in filename '%s'", filename); |
|
|
| if (strcmp(dot, ".TGA") == 0 || strcmp(dot, ".tga") == 0) |
| { |
| if ((&img.PixType()) == 0) |
| img.ReAllocate(CShape(), typeid(uchar), sizeof(uchar), true); |
| if (img.PixType() == typeid(uchar)) |
| ReadFileTGA(*(CByteImage *) &img, filename); |
| else |
| throw CError("ReadImage(%s): can only read CByteImage in TGA format", filename); |
| } |
| else if (strcmp(dot, ".pgm") == 0 || strcmp(dot, ".ppm") == 0 || |
| strcmp(dot, ".pmf") == 0) |
| { |
| if ((&img.PixType()) == 0) |
| { |
| if (strcmp(dot, ".pmf") == 0) |
| img.ReAllocate(CShape(), typeid(float), sizeof(float), true); |
| else |
| img.ReAllocate(CShape(), typeid(uchar), sizeof(uchar), true); |
| } |
| if (img.PixType() == typeid(uchar) || |
| img.PixType() == typeid(float)) |
| ReadFilePGM(*(CByteImage *) &img, filename); |
| else |
| throw CError("ReadImage(%s): wrong image type for PGM/PPM/PMF", filename); |
| } |
| #ifdef HAVE_PNG_LIB |
| else if (strcmp(dot, ".PNG") == 0 || strcmp(dot, ".png") == 0) |
| { |
| if ((&img.PixType()) == 0) |
| img.ReAllocate(CShape(), typeid(uchar), sizeof(uchar), true); |
| if (img.PixType() == typeid(uchar)) |
| ReadFilePNG(*(CByteImage *) &img, filename); |
| else |
| throw CError("ReadImage(%s): can only read CByteImage in PNG format", filename); |
| } |
| #endif |
| #ifdef HAVE_JPEG_READER |
| else if (strcmp(dot, ".JPG") == 0 || strcmp(dot, ".jpg") == 0) |
| { |
| if ((&img.PixType()) == 0) |
| img.ReAllocate(CShape(), typeid(uchar), sizeof(uchar), true); |
| if (img.PixType() == typeid(uchar)) |
| ReadFileJPG(*(CByteImage *) &img, filename); |
| else |
| throw CError("ReadImage(%s): can only read CByteImage in JPG format", filename); |
| } |
| #endif |
| else |
| throw CError("ReadImage(%s): file type not supported", filename); |
| } |
|
|
|
|
|
|
| void WriteImage(CImage& img, const char* filename) |
| { |
| if (filename == NULL) |
| throw CError("WriteImage: empty filename"); |
|
|
| |
| char *dot = strrchr(filename, '.'); |
| if (dot == NULL) |
| throw CError("WriteImage: extension required in filename '%s'", filename); |
|
|
| if (strcmp(dot, ".TGA") == 0 || strcmp(dot, ".tga") == 0) |
| { |
| if (img.PixType() == typeid(uchar)) |
| WriteFileTGA(*(CByteImage *) &img, filename); |
| else |
| throw CError("WriteImage(%s): can only write CByteImage in TGA format", filename); |
| } |
| else if (strcmp(dot, ".pgm") == 0 || strcmp(dot, ".ppm") == 0 || |
| strcmp(dot, ".pmf") == 0) |
| { |
| if (img.PixType() == typeid(uchar) || |
| img.PixType() == typeid(float)) |
| WriteFilePGM(*(CByteImage *) &img, filename); |
| else |
| throw CError("WriteImage(%s): wrong image type for PGM/PPM/PMF", filename); |
| } |
| #ifdef HAVE_PNG_LIB |
| else if (strcmp(dot, ".PNG") == 0 || strcmp(dot, ".png") == 0) |
| { |
| if (img.PixType() == typeid(uchar)) |
| WriteFilePNG(*(CByteImage *) &img, filename); |
| else |
| throw CError("WriteImage(%s): can only write CByteImage in PNG format", filename); |
| } |
| #endif |
| else |
| throw CError("WriteImage(%s): file type not supported", filename); |
| } |
|
|
| |
| void ReadImageVerb(CImage& img, const char* filename, int verbose) { |
| if (verbose) |
| fprintf(stderr, "Reading image %s\n", filename); |
| ReadImage(img, filename); |
| } |
|
|
| |
| void WriteImageVerb(CImage& img, const char* filename, int verbose) { |
| if (verbose) |
| fprintf(stderr, "Writing image %s\n", filename); |
| WriteImage(img, filename); |
| } |
|
|