00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #include "stdafx.h"
00027 #include "imageio.h"
00028 #include "spectrum.h"
00029
00030
00031 static RGBSpectrum *ReadImageEXR(const string &name, int *width, int *height);
00032 static void WriteImageEXR(const string &name, float *pixels,
00033 float *alpha, int xRes, int yRes,
00034 int totalXRes, int totalYRes,
00035 int xOffset, int yOffset);
00036 static void WriteImageTGA(const string &name, float *pixels,
00037 float *alpha, int xRes, int yRes,
00038 int totalXRes, int totalYRes,
00039 int xOffset, int yOffset);
00040 static RGBSpectrum *ReadImageTGA(const string &name, int *w, int *h);
00041 static bool WriteImagePFM(const string &filename, const float *rgb, int xres, int yres);
00042 static RGBSpectrum *ReadImagePFM(const string &filename, int *xres, int *yres);
00043
00044
00045 RGBSpectrum *ReadImage(const string &name, int *width, int *height) {
00046 if (name.size() >= 5) {
00047 uint32_t suffixOffset = name.size() - 4;
00048 #ifdef PBRT_HAS_OPENEXR
00049 if (!strcmp(name.c_str() + suffixOffset, ".exr") ||
00050 !strcmp(name.c_str() + suffixOffset, ".EXR"))
00051 return ReadImageEXR(name, width, height);
00052 #endif // PBRT_HAS_OPENEXR
00053 if (!strcmp(name.c_str() + suffixOffset, ".tga") ||
00054 !strcmp(name.c_str() + suffixOffset, ".TGA"))
00055 return ReadImageTGA(name, width, height);
00056 if (!strcmp(name.c_str() + suffixOffset, ".pfm") ||
00057 !strcmp(name.c_str() + suffixOffset, ".PFM"))
00058 return ReadImagePFM(name, width, height);
00059 }
00060 Error("Can't determine image file type from suffix of filename \"%s\"",
00061 name.c_str());
00062 RGBSpectrum *ret = new RGBSpectrum[1];
00063 ret[0] = 0.5f;
00064 *width = *height = 1;
00065 return ret;
00066 }
00067
00068
00069 void WriteImage(const string &name, float *pixels, float *alpha, int xRes,
00070 int yRes, int totalXRes, int totalYRes, int xOffset, int yOffset) {
00071 if (name.size() >= 5) {
00072 uint32_t suffixOffset = name.size() - 4;
00073 #ifdef PBRT_HAS_OPENEXR
00074 if (!strcmp(name.c_str() + suffixOffset, ".exr") ||
00075 !strcmp(name.c_str() + suffixOffset, ".EXR")) {
00076 WriteImageEXR(name, pixels, alpha, xRes, yRes, totalXRes,
00077 totalYRes, xOffset, yOffset);
00078 return;
00079 }
00080 #endif // PBRT_HAS_OPENEXR
00081 if (!strcmp(name.c_str() + suffixOffset, ".tga") ||
00082 !strcmp(name.c_str() + suffixOffset, ".TGA")) {
00083 WriteImageTGA(name, pixels, alpha, xRes, yRes, totalXRes,
00084 totalYRes, xOffset, yOffset);
00085 return;
00086 }
00087 if (!strcmp(name.c_str() + suffixOffset, ".pfm") ||
00088 !strcmp(name.c_str() + suffixOffset, ".PFM")) {
00089 WriteImagePFM(name, pixels, xRes, yRes);
00090 return;
00091 }
00092 }
00093 Error("Can't determine image file type from suffix of filename \"%s\"",
00094 name.c_str());
00095 }
00096
00097
00098 #ifdef PBRT_HAS_OPENEXR
00099 #if defined(PBRT_IS_WINDOWS)
00100 #define hypotf hypot // For the OpenEXR headers
00101 #endif
00102 #include <ImfInputFile.h>
00103 #include <ImfRgbaFile.h>
00104 #include <ImfChannelList.h>
00105 #include <ImfFrameBuffer.h>
00106 #include <half.h>
00107 using namespace Imf;
00108 using namespace Imath;
00109
00110
00111 static RGBSpectrum *ReadImageEXR(const string &name, int *width, int *height) {
00112 try {
00113 InputFile file(name.c_str());
00114 Box2i dw = file.header().dataWindow();
00115 *width = dw.max.x - dw.min.x + 1;
00116 *height = dw.max.y - dw.min.y + 1;
00117
00118 half *rgb = new half[3 * *width * *height];
00119
00120 FrameBuffer frameBuffer;
00121 frameBuffer.insert("R", Slice(HALF, (char *)rgb,
00122 3*sizeof(half), *width * 3 * sizeof(half), 1, 1, 0.0));
00123 frameBuffer.insert("G", Slice(HALF, (char *)rgb+sizeof(half),
00124 3*sizeof(half), *width * 3 * sizeof(half), 1, 1, 0.0));
00125 frameBuffer.insert("B", Slice(HALF, (char *)rgb+2*sizeof(half),
00126 3*sizeof(half), *width * 3 * sizeof(half), 1, 1, 0.0));
00127
00128 file.setFrameBuffer(frameBuffer);
00129 file.readPixels(dw.min.y, dw.max.y);
00130
00131 RGBSpectrum *ret = new RGBSpectrum[*width * *height];
00132 for (int i = 0; i < *width * *height; ++i) {
00133 float frgb[3] = { rgb[3*i], rgb[3*i+1], rgb[3*i+2] };
00134 ret[i] = RGBSpectrum::FromRGB(frgb);
00135 }
00136 delete[] rgb;
00137 Info("Read EXR image %s (%d x %d)", name.c_str(), *width, *height);
00138 return ret;
00139 } catch (const std::exception &e) {
00140 Error("Unable to read image file \"%s\": %s", name.c_str(),
00141 e.what());
00142 return NULL;
00143 }
00144 }
00145
00146
00147 static void WriteImageEXR(const string &name, float *pixels,
00148 float *alpha, int xRes, int yRes,
00149 int totalXRes, int totalYRes,
00150 int xOffset, int yOffset) {
00151 Rgba *hrgba = new Rgba[xRes * yRes];
00152 for (int i = 0; i < xRes * yRes; ++i)
00153 hrgba[i] = Rgba(pixels[3*i], pixels[3*i+1], pixels[3*i+2],
00154 alpha ? alpha[i]: 1.f);
00155
00156 Box2i displayWindow(V2i(0,0), V2i(totalXRes-1, totalYRes-1));
00157 Box2i dataWindow(V2i(xOffset, yOffset), V2i(xOffset + xRes - 1, yOffset + yRes - 1));
00158
00159 try {
00160 RgbaOutputFile file(name.c_str(), displayWindow, dataWindow, WRITE_RGBA);
00161 file.setFrameBuffer(hrgba - xOffset - yOffset * xRes, 1, xRes);
00162 file.writePixels(yRes);
00163 }
00164 catch (const std::exception &e) {
00165 Error("Unable to write image file \"%s\": %s", name.c_str(),
00166 e.what());
00167 }
00168
00169 delete[] hrgba;
00170 }
00171
00172
00173 #endif // PBRT_HAS_OPENEXR
00174
00175
00204
00205
00206 #include <stdio.h>
00207 #include <stdlib.h>
00208 #include <string.h>
00209 #if defined(PBRT_IS_WINDOWS)
00210 typedef unsigned char byte;
00211 #else
00212 #include <stdint.h>
00213 typedef uint8_t byte;
00214 #endif
00215
00216 typedef unsigned char uchar;
00217
00218
00219
00220 #undef SHORT
00221 #ifdef __BIG_ENDIAN__
00222 #define SHORT(x) shortSwap(x)
00223 # else // Little-endian.
00224 #define SHORT(x) (x)
00225 #endif
00226
00227
00228
00229 typedef struct {
00230 uchar idLength;
00231 uchar colorMapType;
00232 uchar imageType;
00233 } tga_header_t;
00234
00235
00236 typedef struct {
00237 int16_t index;
00238 int16_t length;
00239 uchar entrySize;
00240 } tga_colormapspec_t;
00241
00242
00243 typedef struct {
00244 int16_t xOrigin;
00245 int16_t yOrigin;
00246 int16_t width;
00247 int16_t height;
00248 uchar pixelDepth;
00249 uchar attributeBits;
00250 } tga_imagespec_t;
00251
00252
00253 #ifdef __BIG_ENDIAN__
00254 static int16_t shortSwap(int16_t n)
00255 {
00256 return ((n & 0xff) << 8) | ((n & 0xff00) >> 8);
00257 }
00258
00259
00260 #endif
00261
00262 static bool writeByte(FILE* f, uchar b)
00263 {
00264 return (fwrite(&b, 1, 1, f) == 1);
00265 }
00266
00267
00268
00269 static bool writeShort(FILE* f, int16_t s)
00270 {
00271 int16_t v = SHORT(s);
00272 return (fwrite(&v, sizeof(v), 1, f) == 1);
00273 }
00274
00275
00276
00277 static uchar readByte(FILE* f)
00278 {
00279 uchar v;
00280 fread(&v, sizeof(v), 1, f);
00281 return v;
00282 }
00283
00284
00285
00286 static int16_t readShort(FILE* f)
00287 {
00288 int16_t v;
00289 fread(&v, sizeof(v), 1, f);
00290 return v;
00291 }
00292
00293
00294
00311 static void writeHeader(uchar idLength, uchar colorMapType, uchar imageType,
00312 FILE* file)
00313 {
00314 writeByte(file, idLength);
00315 writeByte(file, colorMapType? 1 : 0);
00316 writeByte(file, imageType);
00317 }
00318
00319
00320
00321 static void readHeader(tga_header_t* dst, FILE* file)
00322 {
00323 dst->idLength = readByte(file);
00324 dst->colorMapType = readByte(file);
00325 dst->imageType = readByte(file);
00326 }
00327
00328
00329
00336 static void writeColorMapSpec(int16_t index, int16_t length,
00337 uchar entrySize, FILE* file)
00338 {
00339 writeShort(file, index);
00340 writeShort(file, length);
00341 writeByte(file, entrySize);
00342 }
00343
00344
00345
00346 static void readColorMapSpec(tga_colormapspec_t* dst, FILE* file)
00347 {
00348 dst->index = readShort(file);
00349 dst->length = readShort(file);
00350 dst->entrySize = readByte(file);
00351 }
00352
00353
00354
00363 static void writeImageSpec(int16_t xOrigin, int16_t yOrigin,
00364 int16_t width, int16_t height, uchar pixDepth,
00365 FILE* file)
00366 {
00367 writeShort(file, xOrigin);
00368 writeShort(file, yOrigin);
00369 writeShort(file, width);
00370 writeShort(file, height);
00371 writeByte(file, pixDepth);
00372
00379 writeByte(file, 0);
00380 }
00381
00382
00383
00384 static void readImageSpec(tga_imagespec_t* dst, FILE* file)
00385 {
00386 dst->xOrigin = readShort(file);
00387 dst->yOrigin = readShort(file);
00388 dst->width = readShort(file);
00389 dst->height = readShort(file);
00390 dst->pixelDepth = readByte(file);
00391 dst->attributeBits = readByte(file);
00392 }
00393
00394
00405 void WriteImageTGA(const string &name, float *pixels,
00406 float *alpha, int xRes, int yRes,
00407 int totalXRes, int totalYRes,
00408 int xOffset, int yOffset)
00409 {
00410 FILE* file;
00411 uchar* outBuf;
00412
00413 if ((file = fopen(name.c_str(), "wb")) == NULL) {
00414 Error("Unable to open output filename \"%s\"", name.c_str());
00415 return;
00416 }
00417
00418
00419 writeHeader(0, 0, 2, file);
00420 writeColorMapSpec(0, 0, 0, file);
00421 writeImageSpec(0, 0, xRes, yRes, 24, file);
00422
00423
00424 outBuf = (uchar *)malloc(xRes * yRes * 3);
00425 uchar *dst = outBuf;
00426 for (int y = yRes-1; y >= 0; --y) {
00427 for (int x = 0; x < xRes; ++x) {
00428 #define TO_BYTE(v) (uint8_t(Clamp(255.f * powf((v), 1.f/2.3f), 0.f, 255.f)))
00429 dst[0] = TO_BYTE(pixels[3*(y*xRes+x)+2]);
00430 dst[1] = TO_BYTE(pixels[3*(y*xRes+x)+1]);
00431 dst[2] = TO_BYTE(pixels[3*(y*xRes+x)+0]);
00432 dst += 3;
00433 }
00434 }
00435 if (fwrite(outBuf, 1, 3 * xRes * yRes, file) != uint32_t(3*xRes*yRes))
00436 Error("Error writing TGA image file \"%s\"", name.c_str());
00437 free(outBuf);
00438 fclose(file);
00439 }
00440
00441
00442
00443
00447 static RGBSpectrum *ReadImageTGA(const string &name, int *width, int *height)
00448 {
00449 int x, y, pixbytes;
00450 tga_header_t header;
00451 tga_colormapspec_t colorMapSpec;
00452 tga_imagespec_t imageSpec;
00453 uchar* srcBuf;
00454 const uchar* src;
00455
00456 FILE *file = fopen(name.c_str(), "rb");
00457 if (!file) {
00458 Error("Unable to open TGA file \"%s\"", name.c_str());
00459 return NULL;
00460 }
00461
00462
00463 readHeader(&header, file);
00464 readColorMapSpec(&colorMapSpec, file);
00465 readImageSpec(&imageSpec, file);
00466
00467 if (((imageSpec.attributeBits & 0xf) != 8 &&
00468 (imageSpec.attributeBits & 0xf) != 0) ||
00469 ((imageSpec.attributeBits & 0xc0) != 0) ||
00470 (header.imageType == 2 &&
00471 (imageSpec.pixelDepth != 32 && imageSpec.pixelDepth != 24)) ||
00472 (header.imageType == 3 &&
00473 (imageSpec.pixelDepth != 8)) ||
00474 (header.imageType != 2 && header.imageType != 3)) {
00475 Error("ReadImageTGA: I don't know this format "
00476 "(type=%i pxsize=%i abits=%i)", header.imageType,
00477 imageSpec.pixelDepth,
00478 imageSpec.attributeBits);
00479 fclose(file);
00480 return NULL;
00481 }
00482
00483 *width = imageSpec.width;
00484 *height = imageSpec.height;
00485
00486
00487 if (imageSpec.pixelDepth == 32)
00488 pixbytes = 4;
00489 else if (imageSpec.pixelDepth == 24)
00490 pixbytes = 3;
00491 else {
00492 Assert(imageSpec.pixelDepth == 8);
00493 pixbytes = 1;
00494 }
00495
00496
00497 int size = *width * *height * pixbytes;
00498 srcBuf = (uchar *)malloc(size);
00499 if (fread(srcBuf, 1, size, file) != (uint32_t)size) {
00500 Error("Premature end-of-file when reading TGA image \"%s\"", name.c_str());
00501 free(srcBuf);
00502 fclose(file);
00503 return NULL;
00504 }
00505
00506
00507
00508 src = srcBuf;
00509 RGBSpectrum *ret = new RGBSpectrum[*width * *height];
00510 RGBSpectrum *dst = ret;
00511 for (y = *height - 1; y >= 0; y--)
00512 for (x = 0; x < *width; x++) {
00513 if (pixbytes == 1)
00514 *dst++ = RGBSpectrum((*src++) / 255.f);
00515 else {
00516 float c[3];
00517 c[2] = (*src++) / 255.f;
00518 c[1] = (*src++) / 255.f;
00519 c[0] = (*src++) / 255.f;
00520 *dst++ = RGBSpectrum::FromRGB(c);
00521 if (pixbytes == 4)
00522 ++src;
00523 }
00524 }
00525
00526 bool flipH = ((imageSpec.attributeBits & 0x10) == 0x10);
00527 bool flipV = ((imageSpec.attributeBits & 0x20) == 0x20);
00528 if (flipH) {
00529 for (y = 0; y < *height; ++y)
00530 for (x = 0; x < *width / 2; ++x)
00531 swap(ret[y * *width + x], ret[y * *width + (*width - 1 - x)]);
00532 }
00533 if (flipV) {
00534 for (y = 0; y < *height/2; ++y)
00535 for (x = 0; x < *width; ++x)
00536 swap(ret[y * *width + x], ret[(*height - 1 - y) * *width + x]);
00537 }
00538 free(srcBuf);
00539 fclose(file);
00540 Info("Read TGA image %s (%d x %d)", name.c_str(), *width, *height);
00541 return ret;
00542 }
00543
00544
00545
00546
00547
00548
00549
00550
00551 static bool hostLittleEndian =
00552 #if defined(__LITTLE_ENDIAN__) || defined(__i386__) || defined(__x86_64__) || defined(PBRT_IS_WINDOWS)
00553 true
00554 #elif defined(__BIG_ENDIAN__)
00555 false
00556 #else
00557 #error "Can't detect machine endian-ness at compile-time."
00558 #endif
00559 ;
00560
00561 #define BUFFER_SIZE 80
00562
00563 static inline int isWhitespace( char c )
00564 {
00565 return c == ' ' || c == '\n' || c == '\t';
00566 }
00567
00568
00569
00570
00571
00572
00573
00574
00575
00576 static int readWord(FILE* fp, char* buffer, int bufferLength) {
00577 int n;
00578 char c;
00579
00580 if (bufferLength < 1)
00581 return -1;
00582
00583 n = 0;
00584 c = fgetc( fp );
00585 while( c != EOF && !isWhitespace( c ) && n < bufferLength ) {
00586 buffer[ n ] = c;
00587 ++n;
00588 c = fgetc( fp );
00589 }
00590
00591 if( n < bufferLength ) {
00592 buffer[ n ] = '\0';
00593 return n;
00594 }
00595
00596 return -1;
00597 }
00598
00599
00600
00601 static RGBSpectrum *ReadImagePFM(const string &filename, int *xres, int *yres) {
00602 float *data = NULL;
00603 RGBSpectrum *rgb = NULL;
00604 char buffer[ BUFFER_SIZE ];
00605 unsigned int nFloats;
00606 int nChannels, width, height;
00607 float scale;
00608 bool fileLittleEndian;
00609
00610 FILE *fp = fopen(filename.c_str(), "rb");
00611 if (!fp)
00612 goto fail;
00613
00614
00615 if (readWord( fp, buffer, BUFFER_SIZE ) == -1)
00616 goto fail;
00617
00618 if (strcmp( buffer, "Pf" ) == 0)
00619 nChannels = 1;
00620 else if (strcmp( buffer, "PF" ) == 0)
00621 nChannels = 3;
00622 else
00623 goto fail;
00624
00625
00626
00627 if (readWord( fp, buffer, BUFFER_SIZE ) == -1)
00628 goto fail;
00629 width = atoi( buffer );
00630 *xres = width;
00631
00632
00633 if (readWord( fp, buffer, BUFFER_SIZE ) == -1)
00634 goto fail;
00635 height = atoi( buffer );
00636 *yres = height;
00637
00638
00639 if (readWord( fp, buffer, BUFFER_SIZE ) == -1)
00640 goto fail;
00641 sscanf( buffer, "%f", &scale );
00642
00643
00644 nFloats = nChannels * width * height;
00645 data = new float[nFloats];
00646 if (fread(data, sizeof( float ), nFloats, fp ) != nFloats)
00647 goto fail;
00648
00649
00650 fileLittleEndian = (scale < 0.f);
00651 if (hostLittleEndian ^ fileLittleEndian) {
00652 uint8_t bytes[4];
00653 for (unsigned int i = 0; i < nFloats; ++i) {
00654 memcpy(bytes, &data[i], 4);
00655 swap(bytes[0], bytes[3]);
00656 swap(bytes[1], bytes[2]);
00657 memcpy(&data[i], bytes, 4);
00658 }
00659 }
00660 if (fabsf(scale) != 1.f)
00661 for (unsigned int i = 0; i < nFloats; ++i)
00662 data[i] *= fabsf(scale);
00663
00664
00665 rgb = new RGBSpectrum[width*height];
00666 if (nChannels == 1) {
00667 for (int i = 0; i < width * height; ++i)
00668 rgb[i] = data[i];
00669 }
00670 else {
00671 for (int i = 0; i < width * height; ++i)
00672 rgb[i] = RGBSpectrum::FromRGB(&data[3*i]);
00673 }
00674
00675 delete[] data;
00676 fclose(fp);
00677 return rgb;
00678
00679 fail:
00680 Error("Error reading PFM file \"%s\"", filename.c_str());
00681 fclose(fp);
00682 delete[] data;
00683 delete[] rgb;
00684 return NULL;
00685 }
00686
00687
00688
00689
00690 static bool WriteImagePFM(const string &filename, const float *rgb,
00691 int width, int height) {
00692 FILE* fp;
00693 unsigned int nFloats;
00694 float scale;
00695
00696 fp = fopen(filename.c_str(), "wb");
00697 if (!fp) {
00698 Error("Unable to open output PFM file \"%s\"", filename.c_str());
00699 return false;
00700 }
00701
00702
00703 if (fprintf(fp, "PF\n") < 0)
00704 goto fail;
00705
00706
00707 if (fprintf(fp, "%d %d\n", width, height) < 0)
00708 goto fail;
00709
00710
00711 scale = hostLittleEndian ? -1.f : 1.f;
00712 if (fprintf(fp, "%f\n", scale) < 0)
00713 goto fail;
00714
00715
00716 nFloats = 3 * width * height;
00717 if (fwrite(rgb, sizeof(float), nFloats, fp) < nFloats)
00718 goto fail;
00719
00720 fclose(fp);
00721 return true;
00722
00723 fail:
00724 Error("Error writing PFM file \"%s\"", filename.c_str());
00725 fclose(fp);
00726 return false;
00727 }
00728
00729