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 #include "lux.h"
00026 #include "error.h"
00027 #include "color.h"
00028 #include "spectrum.h"
00029 #include "imagereader.h"
00030
00031 #include <algorithm>
00032
00033 #include <boost/filesystem/path.hpp>
00034 #include <boost/filesystem/convenience.hpp>
00035
00036 #define cimg_display_type 0
00037
00038 #ifdef LUX_USE_CONFIG_H
00039 #include "config.h"
00040
00041 #ifdef PNG_FOUND
00042 #define cimg_use_png 1
00043 #endif
00044
00045 #ifdef JPEG_FOUND
00046 #define cimg_use_jpeg 1
00047 #endif
00048
00049 #ifdef TIFF_FOUND
00050 #define cimg_use_tiff 1
00051 #endif
00052
00053
00054 #else //LUX_USE_CONFIG_H
00055 #define cimg_use_png 1
00056
00057 #define cimg_use_jpeg 1
00058 #endif //LUX_USE_CONFIG_H
00059
00060
00061 #define cimg_debug 0 // Disable modal window in CImg exceptions.
00062 #include "cimg.h"
00063 using namespace cimg_library;
00064
00065 #if defined(WIN32) && !defined(__CYGWIN__)
00066 #define hypotf hypot // For the OpenEXR headers
00067 #endif
00068
00069 #include <ImfInputFile.h>
00070 #include <ImfOutputFile.h>
00071 #include <ImfChannelList.h>
00072 #include <ImfFrameBuffer.h>
00073 #include <half.h>
00074
00075
00076
00077
00078
00079
00080 using namespace Imf;
00081 using namespace Imath;
00082 using namespace lux;
00083
00084 namespace lux {
00085
00086 template <class T >
00087 class StandardImageReader : public ImageReader {
00088 public:
00089
00090 StandardImageReader() {
00091 };
00092 ImageData* read(const string &name);
00093 };
00094
00095
00096
00097 ImageData *ExrImageReader::read(const string &name) {
00098 try {
00099 stringstream ss;
00100 ss << "Loading OpenEXR Texture: '" << name << "'...";
00101 luxError(LUX_NOERROR, LUX_INFO, ss.str().c_str());
00102
00103 InputFile file(name.c_str());
00104 Box2i dw = file.header().dataWindow();
00105 int width = dw.max.x - dw.min.x + 1;
00106 int height = dw.max.y - dw.min.y + 1;
00107
00108 int noChannels = 3;
00109
00110 ss.str("");
00111 ss << width << "x" << height << " (" << noChannels << " channels)";
00112 luxError(LUX_NOERROR, LUX_INFO, ss.str().c_str());
00113
00114 half *rgb = new half[noChannels * width * height];
00115
00116 FrameBuffer frameBuffer;
00117 frameBuffer.insert("R", Slice(HALF, (char *) rgb,
00118 3 * sizeof (half), width * 3 * sizeof (half), 1, 1, 0.0));
00119 frameBuffer.insert("G", Slice(HALF, (char *) rgb + sizeof (half),
00120 3 * sizeof (half), width * 3 * sizeof (half), 1, 1, 0.0));
00121 frameBuffer.insert("B", Slice(HALF, (char *) rgb + 2 * sizeof (half),
00122 3 * sizeof (half), width * 3 * sizeof (half), 1, 1, 0.0));
00123
00124 file.setFrameBuffer(frameBuffer);
00125 file.readPixels(dw.min.y, dw.max.y);
00126 TextureColor<float, 3 > *ret = new TextureColor<float, 3 > [width * height];
00127 ImageData* data = new ImageData(width, height, ImageData::FLOAT_TYPE, noChannels, ret);
00128
00129
00130 for (int i = 0; i < width * height; ++i) {
00131 float c[3] = { rgb[(3 * i)], rgb[(3 * i) + 1], rgb[(3 * i) + 2] };
00132 ret[i] = TextureColor<float, 3 > (c);
00133
00134
00135 }
00136
00137 delete[] rgb;
00138
00139 return data;
00140 } catch (const std::exception &e) {
00141 std::stringstream ss;
00142 ss << "Unable to read EXR image file '" << name << "': " << e.what();
00143 luxError(LUX_BUG, LUX_ERROR, ss.str().c_str());
00144 return NULL;
00145 }
00146 }
00147
00148 template <typename T> ImageData * StandardImageReader<T>::read(const string &name) {
00149 try {
00150 stringstream ss;
00151 ss << "Loading Cimg Texture: '" << name << "'...";
00152 luxError(LUX_NOERROR, LUX_INFO, ss.str().c_str());
00153
00154 CImg<T> image(name.c_str());
00155 size_t size = sizeof (T);
00156
00157 ImageData::PixelDataType type;
00158 if (size == 1)
00159 type = ImageData::UNSIGNED_CHAR_TYPE;
00160 if (size == 2)
00161 type = ImageData::UNSIGNED_SHORT_TYPE;
00162 if (size == 4)
00163 type = ImageData::FLOAT_TYPE;
00164
00165 int width = image.dimx();
00166 int height = image.dimy();
00167 int noChannels = image.dimv();
00168 int pixels = width * height;
00169
00170 ss.str("");
00171 ss << width << "x" << height << " (" << noChannels << " channels)";
00172 luxError(LUX_NOERROR, LUX_INFO, ss.str().c_str());
00173
00174 TextureColorBase* ret;
00175
00176 if (noChannels == 1)
00177 ret = new TextureColor<T, 1> [width * height];
00178 else if (noChannels == 3)
00179 ret = new TextureColor<T, 3> [width * height];
00180 else if (noChannels == 4)
00181 ret = new TextureColor<T, 4> [width * height];
00182 else {
00183 ss.str("");
00184 ss << "Unsupported channel count in StandardImageReader::read(" <<
00185 name << ": " << noChannels;
00186 luxError(LUX_SYSTEM, LUX_ERROR, ss.str().c_str());
00187
00188 return NULL;
00189 }
00190
00191 ImageData* data = new ImageData(width, height, type, noChannels, ret);
00192 T *c = new T[noChannels];
00193 c[0] = 0;
00194
00195 for (int i = 0; i < width; ++i)
00196 for (int j = 0; j < height; ++j) {
00197 for (int k = 0; k < noChannels; ++k) {
00198
00199 unsigned long off = i + (j * width) + (k * pixels);
00200
00201 if (noChannels == 1)
00202 ((TextureColor<T, 1> *)ret)[i + (j * width)].c[k] = image[off];
00203 else if (noChannels == 3)
00204 ((TextureColor<T, 3> *)ret)[i + (j * width)].c[k] = image[off];
00205 else
00206 ((TextureColor<T, 4> *)ret)[i + (j * width)].c[k] = image[off];
00207 }
00208 }
00209 delete [] c;
00210
00211 return data;
00212 } catch (CImgIOException &e) {
00213 std::stringstream ss;
00214 ss << "Unable to read Cimg image file '" << name << "': " << e.message;
00215 luxError(LUX_BUG, LUX_ERROR, ss.str().c_str());
00216 return NULL;
00217 }
00218 return NULL;
00219 }
00220
00221 ImageData *ReadImage(const string &name) {
00222 boost::filesystem::path imagePath(name);
00223 if (!boost::filesystem::exists(imagePath)) {
00224 std::stringstream ss;
00225 ss << "Unable to open image file '" << imagePath.string() << "'";
00226 luxError(LUX_NOFILE, LUX_ERROR, ss.str().c_str());
00227 return NULL;
00228 }
00229
00230 std::string extension = boost::filesystem::extension(imagePath).substr(1);
00231
00232 #if defined(WIN32) && !defined(__CYGWIN__)
00233 std::transform(extension.begin(), extension.end(), extension.begin(), (int(*)(int)) tolower);
00234 #else
00235 std::transform(extension.begin(), extension.end(), extension.begin(), (int(*)(int)) std::tolower);
00236 #endif
00237
00238 if (extension == "exr") {
00239 ExrImageReader exrReader;
00240 ImageData* data = exrReader.read(name);
00241 data->setIsExrImage(true);
00242
00243 return data;
00244 }
00245
00246
00247
00248
00249
00250
00251
00252
00253
00254
00255 if ((extension == "raw") ||
00256 (extension == "asc") ||
00257 (extension == "hdr") ||
00258 (extension == "inr") ||
00259 (extension == "ppm") ||
00260 (extension == "pgm") ||
00261 (extension == "bmp") ||
00262 (extension == "pan") ||
00263 (extension == "dlm")) {
00264
00265 StandardImageReader<unsigned char> stdImageReader;
00266 return stdImageReader.read(name);
00267 }
00268
00269 if ((extension == "png") ||
00270 (extension == "jpg") ||
00271 (extension == "jpeg")) {
00272
00273 StandardImageReader<unsigned char> stdImageReader;
00274 return stdImageReader.read(name);
00275 }
00276
00277
00278
00279 if ((extension == "tif") || (extension == "tiff")) {
00280
00281 StandardImageReader<unsigned char> stdImageReader;
00282 return stdImageReader.read(name);
00283 }
00284 if (extension == "tga") {
00285
00286 StandardImageReader<unsigned char> stdImageReader;
00287 return stdImageReader.read(name);
00288 }
00289
00290 std::stringstream ss;
00291 ss << "Cannot recognise file format for image '" << name << "'";
00292 luxError(LUX_BADFILE, LUX_ERROR, ss.str().c_str());
00293 return NULL;
00294 }
00295
00296 void WriteRGBAImage(const string &name, float *pixels,
00297 float *alpha, int xRes, int yRes,
00298 int totalXRes, int totalYRes,
00299 int xOffset, int yOffset) {
00300 Header header(totalXRes, totalYRes);
00301 Box2i dataWindow(V2i(xOffset, yOffset), V2i(xOffset + xRes - 1, yOffset + yRes - 1));
00302 header.dataWindow() = dataWindow;
00303 header.channels().insert("R", Channel(HALF));
00304 header.channels().insert("G", Channel(HALF));
00305 header.channels().insert("B", Channel(HALF));
00306 header.channels().insert("A", Channel(HALF));
00307
00308 half *hrgb = new half[3 * xRes * yRes];
00309 for (int i = 0; i < 3 * xRes * yRes; ++i)
00310 hrgb[i] = pixels[i];
00311 half *ha = new half[xRes * yRes];
00312 for (int i = 0; i < xRes * yRes; ++i)
00313 ha[i] = alpha[i];
00314
00315 hrgb -= 3 * (xOffset + yOffset * xRes);
00316 ha -= (xOffset + yOffset * xRes);
00317
00318 FrameBuffer fb;
00319 fb.insert("R", Slice(HALF, (char *) hrgb, 3 * sizeof (half),
00320 3 * xRes * sizeof (half)));
00321 fb.insert("G", Slice(HALF, (char *) hrgb + sizeof (half), 3 * sizeof (half),
00322 3 * xRes * sizeof (half)));
00323 fb.insert("B", Slice(HALF, (char *) hrgb + 2 * sizeof (half), 3 * sizeof (half),
00324 3 * xRes * sizeof (half)));
00325 fb.insert("A", Slice(HALF, (char *) ha, sizeof (half), xRes * sizeof (half)));
00326
00327 try {
00328 OutputFile file(name.c_str(), header);
00329 file.setFrameBuffer(fb);
00330 file.writePixels(yRes);
00331 } catch (const std::exception &e) {
00332
00333
00334 std::stringstream ss;
00335 ss << "Unable to write image file '" << name << "': " << e.what();
00336 luxError(LUX_BUG, LUX_SEVERE, ss.str().c_str());
00337 }
00338
00339 delete[] (hrgb + 3 * (xOffset + yOffset * xRes));
00340 delete[] (ha + (xOffset + yOffset * xRes));
00341 }
00342
00343 void WriteRGBAImageFloat(const string &name, float *pixels,
00344 float *alpha, int xRes, int yRes,
00345 int totalXRes, int totalYRes,
00346 int xOffset, int yOffset) {
00347 Header header(totalXRes, totalYRes);
00348 Box2i dataWindow(V2i(xOffset, yOffset), V2i(xOffset + xRes - 1, yOffset + yRes - 1));
00349 header.dataWindow() = dataWindow;
00350 header.channels().insert("R", Channel(Imf::FLOAT));
00351 header.channels().insert("G", Channel(Imf::FLOAT));
00352 header.channels().insert("B", Channel(Imf::FLOAT));
00353 header.channels().insert("A", Channel(Imf::FLOAT));
00354
00355 float *hrgb = new float[3 * xRes * yRes];
00356 for (int i = 0; i < 3 * xRes * yRes; ++i)
00357 hrgb[i] = pixels[i];
00358 float *ha = new float[xRes * yRes];
00359 for (int i = 0; i < xRes * yRes; ++i)
00360 ha[i] = alpha[i];
00361
00362 hrgb -= 3 * (xOffset + yOffset * xRes);
00363 ha -= (xOffset + yOffset * xRes);
00364
00365 FrameBuffer fb;
00366 fb.insert("R", Slice(Imf::FLOAT, (char *) hrgb, 3 * sizeof (float),
00367 3 * xRes * sizeof (float)));
00368 fb.insert("G", Slice(Imf::FLOAT, (char *) hrgb + sizeof (float), 3 * sizeof (float),
00369 3 * xRes * sizeof (float)));
00370 fb.insert("B", Slice(Imf::FLOAT, (char *) hrgb + 2 * sizeof (float), 3 * sizeof (float),
00371 3 * xRes * sizeof (float)));
00372 fb.insert("A", Slice(Imf::FLOAT, (char *) ha, sizeof (float), xRes * sizeof (float)));
00373
00374 try {
00375 OutputFile file(name.c_str(), header);
00376 file.setFrameBuffer(fb);
00377 file.writePixels(yRes);
00378 } catch (const std::exception &e) {
00379
00380
00381 std::stringstream ss;
00382 ss << "Unable to write image file '" << name << "': " << e.what();
00383 luxError(LUX_BUG, LUX_SEVERE, ss.str().c_str());
00384 }
00385
00386 delete[] (hrgb + 3 * (xOffset + yOffset * xRes));
00387 delete[] (ha + (xOffset + yOffset * xRes));
00388 }
00389 }