00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #ifndef _LUXRAYS_UTILS_FILM_H
00023 #define _LUXRAYS_UTILS_FILM_H
00024
00025 #include <cstddef>
00026 #include <cmath>
00027 #include <cstdio>
00028 #include <cstdlib>
00029 #include <fstream>
00030 #include <iostream>
00031
00032 #if defined (WIN32)
00033 #include <windows.h>
00034 #endif
00035
00036 #include <FreeImage.h>
00037
00038 #include <boost/thread/mutex.hpp>
00039
00040 #include "luxrays/luxrays.h"
00041 #include "luxrays/core/pixel/samplebuffer.h"
00042 #include "luxrays/core/pixeldevice.h"
00043 #include "luxrays/core/pixel/samplebuffer.h"
00044
00045 namespace luxrays { namespace utils {
00046
00047
00048
00049
00050
00051 #define GAMMA_TABLE_SIZE 1024
00052
00053 class Film {
00054 public:
00055 Film(Context *context, const unsigned int w, const unsigned int h) {
00056 ctx = context;
00057 filterType = FILTER_GAUSSIAN;
00058 toneMapParams = new LinearToneMapParams();
00059
00060 InitGammaTable();
00061 Init(w, h);
00062 }
00063
00064 virtual ~Film() {
00065 delete toneMapParams;
00066 }
00067
00068 virtual void Init(const unsigned int w, const unsigned int h) {
00069 width = w;
00070 height = h;
00071 LR_LOG(ctx, "Film size " << width << "x" << height);
00072 pixelCount = w * h;
00073
00074 statsTotalSampleCount = 0;
00075 statsAvgSampleSec = 0.0;
00076 statsStartSampleTime = WallClockTime();
00077 }
00078
00079 virtual void InitGammaTable(const float gamma = 2.2f) {
00080 float x = 0.f;
00081 const float dx = 1.f / GAMMA_TABLE_SIZE;
00082 for (unsigned int i = 0; i < GAMMA_TABLE_SIZE; ++i, x += dx)
00083 gammaTable[i] = powf(Clamp(x, 0.f, 1.f), 1.f / gamma);
00084 }
00085
00086 void SetFilterType(const FilterType filter) {
00087 filterType = filter;
00088 }
00089
00090 const ToneMapParams *GetToneMapParams() const { return toneMapParams; }
00091 void SetToneMapParams(const ToneMapParams ¶ms) {
00092 delete toneMapParams;
00093
00094 toneMapParams = params.Copy();
00095 }
00096
00097 void AddFilm(const std::string &filmFile) {
00098 std::ifstream file;
00099 file.open(filmFile.c_str(), std::ios::in | std::ios::binary);
00100
00101
00102 if (file.is_open()) {
00103 file.exceptions(std::ifstream::eofbit | std::ifstream::failbit | std::ifstream::badbit);
00104
00105 LR_LOG(ctx, "Loading film file: " << filmFile);
00106
00107 SampleFrameBuffer sbe(width, height);
00108
00109 unsigned int tag;
00110 file.read((char *)&tag, sizeof(unsigned int));
00111 if (tag != (('S' << 24) | ('L' << 16) | ('G' << 8) | '0'))
00112 throw std::runtime_error("Unknown film file format");
00113
00114 unsigned int w;
00115 file.read((char *)&w, sizeof(unsigned int));
00116 if (w != width)
00117 throw std::runtime_error("Film file width doesn't match internal width");
00118
00119 unsigned int h;
00120 file.read((char *)&h, sizeof(unsigned int));
00121 if (h != height)
00122 throw std::runtime_error("Film file height doesn't match internal height");
00123
00124 Spectrum spectrum;
00125 float weight;
00126 for (unsigned int i = 0; i < pixelCount; ++i) {
00127 file.read((char *)&spectrum, sizeof(Spectrum));
00128 file.read((char *)&weight, sizeof(float));
00129
00130 sbe.SetPixel(i, spectrum, weight);
00131 }
00132 file.close();
00133
00134 AddSampleFrameBuffer(&sbe);
00135 } else
00136 LR_LOG(ctx, "Film file doesn't exist: " << filmFile);
00137 }
00138
00139 void SaveFilm(const std::string &filmFile) {
00140 LR_LOG(ctx, "Saving film file: " << filmFile);
00141
00142 const SampleFrameBuffer *sbe = GetSampleFrameBuffer();
00143
00144 std::ofstream file;
00145 file.exceptions(std::ifstream::eofbit | std::ifstream::failbit | std::ifstream::badbit);
00146 file.open(filmFile.c_str(), std::ios::out | std::ios::binary);
00147
00148 const unsigned int tag = ('S' << 24) | ('L' << 16) | ('G' << 8) | '0';
00149 file.write((char *)&tag, sizeof(unsigned int));
00150 file.write((char *)&width, sizeof(unsigned int));
00151 file.write((char *)&height, sizeof(unsigned int));
00152
00153 for (unsigned int i = 0; i < pixelCount; ++i) {
00154 const SamplePixel *sp = sbe->GetPixel(i);
00155
00156 file.write((char *)&(sp->radiance), sizeof(Spectrum));
00157 file.write((char *)&(sp->weight), sizeof(float));
00158 }
00159
00160 file.close();
00161 }
00162
00163 void StartSampleTime() {
00164 statsStartSampleTime = WallClockTime();
00165 }
00166
00167 virtual void Reset() {
00168 statsTotalSampleCount = 0;
00169 statsAvgSampleSec = 0.0;
00170 statsStartSampleTime = WallClockTime();
00171 }
00172
00173 virtual void UpdateScreenBuffer() = 0;
00174 virtual const float *GetScreenBuffer() const = 0;
00175
00176 virtual SampleBuffer *GetFreeSampleBuffer() = 0;
00177 virtual void FreeSampleBuffer(SampleBuffer *sampleBuffer) = 0;
00178 virtual void SplatSampleBuffer(const bool preview, SampleBuffer *sampleBuffer) {
00179
00180 statsTotalSampleCount += (unsigned int)sampleBuffer->GetSampleCount();
00181 }
00182
00183 unsigned int GetWidth() { return width; }
00184 unsigned int GetHeight() { return height; }
00185 unsigned int GetTotalSampleCount() { return statsTotalSampleCount; }
00186 double GetTotalTime() {
00187 return WallClockTime() - statsStartSampleTime;
00188 }
00189 double GetAvgSampleSec() {
00190
00191
00192
00193
00194
00195
00196
00197 return statsTotalSampleCount / GetTotalTime();
00198 }
00199
00200 virtual void Save(const std::string &fileName) = 0;
00201
00202 protected:
00203 void SaveImpl(const std::string &fileName) {
00204 LR_LOG(ctx, "Saving " << fileName);
00205
00206 FREE_IMAGE_FORMAT fif = FreeImage_GetFIFFromFilename(fileName.c_str());
00207 if (fif != FIF_UNKNOWN) {
00208 if ((fif == FIF_HDR) || (fif == FIF_EXR)) {
00209 FIBITMAP *dib = FreeImage_AllocateT(FIT_RGBF, width, height, 96);
00210
00211 if (dib) {
00212 unsigned int pitch = FreeImage_GetPitch(dib);
00213 BYTE *bits = (BYTE *)FreeImage_GetBits(dib);
00214 const SampleFrameBuffer *sbe = GetSampleFrameBuffer();
00215
00216 for (unsigned int y = 0; y < height; ++y) {
00217 FIRGBF *pixel = (FIRGBF *)bits;
00218 for (unsigned int x = 0; x < width; ++x) {
00219 const unsigned int ridx = y * width + x;
00220 const SamplePixel *sp = sbe->GetPixel(ridx);
00221 const float weight = sp->weight;
00222
00223 if (weight == 0.f) {
00224 pixel[x].red = 0.f;
00225 pixel[x].green = 0.f;
00226 pixel[x].blue = 0.f;
00227 } else {
00228 pixel[x].red = sp->radiance.r / weight;
00229 pixel[x].green = sp->radiance.g / weight;
00230 pixel[x].blue = sp->radiance.b / weight;
00231 }
00232 }
00233
00234
00235 bits += pitch;
00236 }
00237
00238 if (!FreeImage_Save(fif, dib, fileName.c_str(), 0))
00239 LR_LOG(ctx, "Failed image save: " << fileName);
00240
00241 FreeImage_Unload(dib);
00242 } else
00243 LR_LOG(ctx, "Unable to allocate FreeImage HDR image: " << fileName);
00244 } else {
00245 FIBITMAP *dib = FreeImage_Allocate(width, height, 24);
00246
00247 if (dib) {
00248 unsigned int pitch = FreeImage_GetPitch(dib);
00249 BYTE *bits = (BYTE *)FreeImage_GetBits(dib);
00250 const float *pixels = GetScreenBuffer();
00251
00252 for (unsigned int y = 0; y < height; ++y) {
00253 BYTE *pixel = (BYTE *)bits;
00254 for (unsigned int x = 0; x < width; ++x) {
00255 const int offset = 3 * (x + y * width);
00256 pixel[FI_RGBA_RED] = (BYTE)(pixels[offset] * 255.f + .5f);
00257 pixel[FI_RGBA_GREEN] = (BYTE)(pixels[offset + 1] * 255.f + .5f);
00258 pixel[FI_RGBA_BLUE] = (BYTE)(pixels[offset + 2] * 255.f + .5f);
00259 pixel += 3;
00260 }
00261
00262
00263 bits += pitch;
00264 }
00265
00266 if (!FreeImage_Save(fif, dib, fileName.c_str(), 0))
00267 LR_LOG(ctx, "Failed image save: " << fileName);
00268
00269 FreeImage_Unload(dib);
00270 } else
00271 LR_LOG(ctx, "Unable to allocate FreeImage image: " << fileName);
00272 }
00273 } else
00274 LR_LOG(ctx, "Image type unknown: " << fileName);
00275 }
00276
00277 float Radiance2PixelFloat(const float x) const {
00278
00279
00280
00281 const unsigned int index = Min<unsigned int>(
00282 Floor2UInt(GAMMA_TABLE_SIZE * Clamp(x, 0.f, 1.f)),
00283 GAMMA_TABLE_SIZE - 1);
00284 return gammaTable[index];
00285 }
00286
00287 Spectrum Radiance2Pixel(const Spectrum& c) const {
00288 return Spectrum(Radiance2PixelFloat(c.r), Radiance2PixelFloat(c.g), Radiance2PixelFloat(c.b));
00289 }
00290
00291 virtual const SampleFrameBuffer *GetSampleFrameBuffer() = 0;
00292 virtual void AddSampleFrameBuffer(const SampleFrameBuffer *sfb) = 0;
00293
00294 Context *ctx;
00295 unsigned int width, height;
00296 unsigned int pixelCount;
00297
00298 unsigned int statsTotalSampleCount;
00299 double statsStartSampleTime, statsAvgSampleSec;
00300
00301 float gammaTable[GAMMA_TABLE_SIZE];
00302
00303 FilterType filterType;
00304 ToneMapParams *toneMapParams;
00305 };
00306
00307
00308
00309
00310
00311 class LuxRaysFilm : public Film {
00312 public:
00313 LuxRaysFilm(Context *context, const unsigned int w,
00314 const unsigned int h, DeviceDescription *deviceDesc) : Film(context, w, h) {
00315 std::vector<DeviceDescription *> descs;
00316 descs.push_back(deviceDesc);
00317 pixelDevice = ctx->AddPixelDevices(descs)[0];
00318 pixelDevice->Init(w, h);
00319 }
00320
00321 virtual ~LuxRaysFilm() { }
00322
00323 virtual void Init(const unsigned int w, const unsigned int h) {
00324 pixelDevice->Init(w, h);
00325 Film::Init(w, h);
00326 }
00327
00328 virtual void InitGammaTable(const float gamma = 2.2f) {
00329 pixelDevice->SetGamma(gamma);
00330 }
00331
00332 virtual void Reset() {
00333 pixelDevice->ClearSampleFrameBuffer();
00334 Film::Reset();
00335 }
00336
00337 void UpdateScreenBuffer() {
00338 pixelDevice->UpdateFrameBuffer(*toneMapParams);
00339 }
00340
00341 const float *GetScreenBuffer() const {
00342 return (const float *)pixelDevice->GetFrameBuffer()->GetPixels();
00343 }
00344
00345 SampleBuffer *GetFreeSampleBuffer() {
00346 return pixelDevice->GetFreeSampleBuffer();
00347 }
00348
00349 void FreeSampleBuffer(SampleBuffer *sampleBuffer) {
00350 pixelDevice->FreeSampleBuffer(sampleBuffer);
00351 }
00352
00353 void SplatSampleBuffer(const bool preview, SampleBuffer *sampleBuffer) {
00354 Film::SplatSampleBuffer(preview, sampleBuffer);
00355
00356 if (preview)
00357 pixelDevice->AddSampleBuffer(filterType, sampleBuffer);
00358 else
00359 pixelDevice->AddSampleBuffer(FILTER_PREVIEW, sampleBuffer);
00360 }
00361
00362 void Save(const std::string &fileName) {
00363
00364 pixelDevice->UpdateFrameBuffer(*toneMapParams);
00365 SaveImpl(fileName);
00366 }
00367
00368 protected:
00369 const SampleFrameBuffer *GetSampleFrameBuffer() {
00370 return pixelDevice->GetSampleFrameBuffer();
00371 }
00372
00373 void AddSampleFrameBuffer(const SampleFrameBuffer *sfb) {
00374 pixelDevice->Merge(sfb);
00375 }
00376
00377 PixelDevice *pixelDevice;
00378 };
00379
00380 } }
00381
00382 #endif