core/film.cpp
author Jens Verwiebe
Tue Apr 10 14:09:01 2012 +0200 (13 months ago)
changeset 3630 b70a5a4b2ccd
parent 3590 615410aec0a1
child 3636 9d4da7265e0a
permissions -rw-r--r--
Omniflux's stats patch
     1 /***************************************************************************
     2 *   Copyright (C) 1998-2008 by authors (see AUTHORS.txt )                 *
     3 *                                                                         *
     4 *   This file is part of LuxRender.                                       *
     5 *                                                                         *
     6 *   Lux Renderer is free software; you can redistribute it and/or modify  *
     7 *   it under the terms of the GNU General Public License as published by  *
     8 *   the Free Software Foundation; either version 3 of the License, or     *
     9 *   (at your option) any later version.                                   *
    10 *                                                                         *
    11 *   Lux Renderer is distributed in the hope that it will be useful,       *
    12 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
    13 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
    14 *   GNU General Public License for more details.                          *
    15 *                                                                         *
    16 *   You should have received a copy of the GNU General Public License     *
    17 *   along with this program.  If not, see <http://www.gnu.org/licenses/>. *
    18 *                                                                         *
    19 *   This project is based on PBRT ; see http://www.pbrt.org               *
    20 *   Lux Renderer website : http://www.luxrender.net                       *
    21 ***************************************************************************/
    22 
    23 // film.cpp*
    24 #include "film.h"
    25 #include "randomgen.h"
    26 #include "dynload.h"
    27 #include "paramset.h"
    28 #include "tonemap.h"
    29 #include "cameraresponse.h"
    30 #include "filter.h"
    31 #include "contribution.h"
    32 #include "blackbodyspd.h"
    33 #include "osfunc.h"
    34 #include "streamio.h"
    35 
    36 #include <iostream>
    37 #include <fstream>
    38 
    39 #include <boost/archive/text_oarchive.hpp>
    40 #include <boost/archive/text_iarchive.hpp>
    41 #include <boost/archive/binary_oarchive.hpp>
    42 #include <boost/archive/binary_iarchive.hpp>
    43 #include <boost/serialization/split_member.hpp>
    44 #include <boost/iostreams/filtering_stream.hpp>
    45 #include <boost/iostreams/filtering_streambuf.hpp>
    46 #include <boost/iostreams/copy.hpp>
    47 #include <boost/iostreams/filter/zlib.hpp>
    48 #include <boost/iostreams/filter/bzip2.hpp>
    49 #include <boost/iostreams/filter/gzip.hpp>
    50 #include <boost/filesystem.hpp>
    51 #include <boost/thread.hpp>
    52 
    53 #define cimg_display_type  0
    54 
    55 #ifdef LUX_USE_CONFIG_H
    56 #include "config.h"
    57 
    58 #ifdef PNG_FOUND
    59 #define cimg_use_png 1
    60 #endif
    61 
    62 #ifdef JPEG_FOUND
    63 #define cimg_use_jpeg 1
    64 #endif
    65 
    66 #ifdef TIFF_FOUND
    67 #define cimg_use_tiff 1
    68 #endif
    69 
    70 
    71 #else //LUX_USE_CONFIG_H
    72 #define cimg_use_png 1
    73 #define cimg_use_tiff 1
    74 #define cimg_use_jpeg 1
    75 #endif //LUX_USE_CONFIG_H
    76 
    77 #define cimg_debug 0     // Disable modal window in CImg exceptions.
    78 // Include the CImg Library, with the GREYCstoration plugin included
    79 #define cimg_plugin "greycstoration.h"
    80 
    81 #include "cimg.h"
    82 using namespace cimg_library;
    83 #if cimg_OS!=2
    84 #include <pthread.h>
    85 #endif
    86 
    87 using namespace boost::iostreams;
    88 using namespace lux;
    89 
    90 
    91 template<typename T> 
    92 static T bilinearSampleImage(const vector<T> &pixels,
    93 	const u_int xResolution, const u_int yResolution, 
    94 	const float x, const float y)
    95 {
    96 	u_int x1 = Clamp(Floor2UInt(x), 0U, xResolution - 1);
    97 	u_int y1 = Clamp(Floor2UInt(y), 0U, yResolution - 1);
    98 	u_int x2 = Clamp(x1 + 1, 0U, xResolution - 1);
    99 	u_int y2 = Clamp(y1 + 1, 0U, yResolution - 1);
   100 	float tx = Clamp(x - static_cast<float>(x1), 0.f, 1.f);
   101 	float ty = Clamp(y - static_cast<float>(y1), 0.f, 1.f);
   102 
   103 	T c(0.f);
   104 	c.AddWeighted((1.f - tx) * (1.f - ty), pixels[y1 * xResolution + x1]);
   105 	c.AddWeighted(tx         * (1.f - ty), pixels[y1 * xResolution + x2]);
   106 	c.AddWeighted((1.f - tx) * ty,         pixels[y2 * xResolution + x1]);
   107 	c.AddWeighted(tx         * ty,         pixels[y2 * xResolution + x2]);
   108 
   109 	return c;
   110 }
   111 
   112 // horizontal blur
   113 static void horizontalGaussianBlur(const vector<XYZColor> &in, vector<XYZColor> &out,
   114 	const u_int xResolution, const u_int yResolution, float std_dev)
   115 {
   116 	u_int rad_needed = Ceil2UInt(std_dev * 4.f);//kernel_radius;
   117 
   118 	const u_int lookup_size = rad_needed + 1;
   119 	//build filter lookup table
   120 	std::vector<float> filter_weights(lookup_size);
   121 	float sweight = 0.f;
   122 	for(u_int x = 0; x < lookup_size; ++x) {
   123 		filter_weights[x] = expf(-static_cast<float>(x) * static_cast<float>(x) / (std_dev * std_dev));
   124 		if (x > 0) {
   125 			if (filter_weights[x] < 1e-12f) {
   126 				rad_needed = x;
   127 				break;
   128 			}
   129 			sweight += 2.f * filter_weights[x];
   130 		} else
   131 			sweight += filter_weights[x];
   132 	}
   133 
   134 	//normalise filter kernel
   135 	sweight = 1.f / sweight;
   136 
   137 	const u_int pixel_rad = rad_needed;
   138 
   139 	//------------------------------------------------------------------
   140 	//blur in x direction
   141 	//------------------------------------------------------------------
   142 	for(u_int y = 0; y < yResolution; ++y) {
   143 		for(u_int x = 0; x < xResolution; ++x) {
   144 			const u_int a = y * xResolution + x;
   145 
   146 			out[a] = XYZColor(0.f);
   147 
   148 			for (u_int i = max(x, pixel_rad) - pixel_rad; i <= min(x + pixel_rad, xResolution - 1); ++i) {
   149 				if (i < x)
   150 					out[a].AddWeighted(filter_weights[x - i], in[a + i - x]);
   151 				else
   152 					out[a].AddWeighted(filter_weights[i - x], in[a + i - x]);
   153 			}
   154 		}
   155 	}
   156 }
   157 
   158 static void rotateImage(const vector<XYZColor> &in, vector<XYZColor> &out,
   159 	const u_int xResolution, const u_int yResolution, float angle)
   160 {
   161 	const u_int maxRes = max(xResolution, yResolution);
   162 
   163 	const float s = sinf(-angle);
   164 	const float c = cosf(-angle);
   165 
   166 	const float cx = xResolution * 0.5f;
   167 	const float cy = yResolution * 0.5f;
   168 
   169 	for(u_int y = 0; y < maxRes; ++y) {
   170 		float px = 0.f - maxRes * 0.5f;
   171 		float py = y - maxRes * 0.5f;
   172 
   173 		float rx = px * c - py * s + cx;
   174 		float ry = px * s + py * c + cy;
   175 		for(u_int x = 0; x < maxRes; ++x) {
   176 			out[y*maxRes + x] = bilinearSampleImage<XYZColor>(in, xResolution, yResolution, rx, ry);
   177 			// x = x + dx
   178 			rx += c;
   179 			ry += s;
   180 		}
   181 	}
   182 }
   183 
   184 namespace lux {
   185 
   186 // Image Pipeline Function Definitions
   187 void ApplyImagingPipeline(vector<XYZColor> &xyzpixels,
   188 	u_int xResolution, u_int yResolution,
   189 	const GREYCStorationParams &GREYCParams, const ChiuParams &chiuParams,
   190 	const ColorSystem &colorSpace, Histogram *histogram, bool HistogramEnabled,
   191 	bool &haveBloomImage, XYZColor *&bloomImage, bool bloomUpdate,
   192 	float bloomRadius, float bloomWeight,
   193 	bool VignettingEnabled, float VignetScale,
   194 	bool aberrationEnabled, float aberrationAmount,
   195 	bool &haveGlareImage, XYZColor *&glareImage, bool glareUpdate,
   196 	float glareAmount, float glareRadius, u_int glareBlades, float glareThreshold,
   197 	const char *toneMapName, const ParamSet *toneMapParams,
   198 	const CameraResponse *response, float dither)
   199 {
   200 	const u_int nPix = xResolution * yResolution;
   201 
   202 	// Clamp input
   203 	for (u_int i = 0; i < nPix; ++i)
   204 		xyzpixels[i] = xyzpixels[i].Clamp();
   205 
   206 
   207 	// Possibly apply bloom effect to image
   208 	if (bloomRadius > 0.f && bloomWeight > 0.f) {
   209 		if(bloomUpdate) {
   210 			// Compute image-space extent of bloom effect
   211 			const u_int bloomSupport = Float2UInt(bloomRadius *
   212 				max(xResolution, yResolution));
   213 			const u_int bloomWidth = bloomSupport / 2;
   214 			// Initialize bloom filter table
   215 			vector<float> bloomFilter(bloomWidth * bloomWidth);
   216 			for (u_int i = 0; i < bloomWidth * bloomWidth; ++i) {
   217 				float dist = sqrtf(i) / bloomWidth;
   218 				bloomFilter[i] = powf(max(0.f, 1.f - dist), 4.f);
   219 			}
   220 
   221 			// Allocate persisting bloom image layer if unallocated
   222 			if(!haveBloomImage) {
   223 				bloomImage = new XYZColor[nPix];
   224 				haveBloomImage = true;
   225 			}
   226 
   227 			// Apply bloom filter to image pixels
   228 			//			vector<Color> bloomImage(nPix);
   229 //			ProgressReporter prog(yResolution, "Bloom filter"); //NOBOOK //intermediate crashfix until imagepipelinerefactor is done - Jens
   230 			for (u_int y = 0; y < yResolution; ++y) {
   231 				for (u_int x = 0; x < xResolution; ++x) {
   232 					// Compute bloom for pixel _(x,y)_
   233 					// Compute extent of pixels contributing bloom
   234 					const u_int x0 = max(x, bloomWidth) - bloomWidth;
   235 					const u_int x1 = min(x + bloomWidth, xResolution - 1);
   236 					const u_int y0 = max(y, bloomWidth) - bloomWidth;
   237 					const u_int y1 = min(y + bloomWidth, yResolution - 1);
   238 					const u_int offset = y * xResolution + x;
   239 					float sumWt = 0.f;
   240 					for (u_int by = y0; by <= y1; ++by) {
   241 						for (u_int bx = x0; bx <= x1; ++bx) {
   242 							if (bx == x && by == y)
   243 								continue;
   244 							// Accumulate bloom from pixel $(bx,by)$
   245 							const u_int dist2 = (x - bx) * (x - bx) + (y - by) * (y - by);
   246 							if (dist2 < bloomWidth * bloomWidth) {
   247 								u_int bloomOffset = bx + by * xResolution;
   248 								float wt = bloomFilter[dist2];
   249 								sumWt += wt;
   250 								bloomImage[offset].AddWeighted(wt, xyzpixels[bloomOffset]);
   251 							}
   252 						}
   253 					}
   254 					bloomImage[offset] /= sumWt;
   255 				}
   256 //				prog.Update(); //NOBOOK //intermediate crashfix until imagepipelinerefactor is done - Jens
   257 			}
   258 //			prog.Done(); //NOBOOK //intermediate crashfix until imagepipelinerefactor is done - Jens
   259 		}
   260 
   261 		// Mix bloom effect into each pixel
   262 		if(haveBloomImage && bloomImage != NULL)
   263 			for (u_int i = 0; i < nPix; ++i)
   264 				xyzpixels[i] = Lerp(bloomWeight, xyzpixels[i], bloomImage[i]);
   265 	}
   266 
   267 	if (glareRadius > 0.f && glareAmount > 0.f) {
   268 		if (glareUpdate) {
   269 			// Allocate persisting glare image layer if unallocated
   270 			if(!haveGlareImage) {
   271 				glareImage = new XYZColor[nPix];
   272 				haveGlareImage = true;
   273 			}
   274 
   275 			u_int maxRes = max(xResolution, yResolution);
   276 			u_int nPix2 = maxRes * maxRes;
   277 
   278 			std::vector<XYZColor> rotatedImage(nPix2);
   279 			std::vector<XYZColor> blurredImage(nPix2);
   280 			std::vector<XYZColor> darkenedImage(nPix);
   281 			for(u_int i = 0; i < nPix; ++i)
   282 				glareImage[i] = XYZColor(0.f);
   283 			
   284 			// Search for the brightest pixel in the image
   285 			u_int max = 0;
   286 			for(u_int i = 1; i < nPix; ++i) {
   287 				if (xyzpixels[i].c[1] > xyzpixels[max].c[1])
   288 					max = i;
   289 			}
   290 			
   291 			// glareThreshold ranges between 0-1,
   292 			// but this relative value has to be converted to
   293 			//an absolute value fitting the image being processed
   294 			float glareAbsoluteThreshold = xyzpixels[max].c[1] *
   295 				glareThreshold;
   296 			// Every pixel that is not bright enough is made black
   297 			for(u_int i = 0; i < nPix; ++i) {
   298 				if(xyzpixels[i].c[1] < glareAbsoluteThreshold)
   299 					darkenedImage[i] = XYZColor(0.f);
   300 				else
   301 					darkenedImage[i] = xyzpixels[i];
   302 			}
   303 
   304 			const float radius = maxRes * glareRadius;
   305 
   306 			// add rotated versions of the blurred image
   307 			const float invBlades = 1.f / glareBlades;
   308 			float angle = 0.f;
   309 			for (u_int i = 0; i < glareBlades; ++i) {
   310 				rotateImage(darkenedImage, rotatedImage, xResolution, yResolution, angle);
   311 				horizontalGaussianBlur(rotatedImage, blurredImage, maxRes, maxRes, radius);
   312 				rotateImage(blurredImage, rotatedImage, maxRes, maxRes, -angle);
   313 
   314 				// add to output
   315 				for(u_int y = 0; y < yResolution; ++y) {
   316 					for(u_int x = 0; x < xResolution; ++x) {
   317 						const u_int sx = x + (maxRes - xResolution) / 2;
   318 						const u_int sy = y + (maxRes - yResolution) / 2;
   319 
   320 						glareImage[y * xResolution + x] += rotatedImage[sy * maxRes + sx];
   321 					}
   322 				}
   323 				angle += 2.f * M_PI * invBlades;
   324 			}
   325 
   326 			// normalize
   327 			for(u_int i = 0; i < nPix; ++i)
   328 				glareImage[i] *= invBlades;
   329 
   330 			rotatedImage.clear();
   331 			blurredImage.clear();
   332 			darkenedImage.clear();
   333 		}
   334 
   335 		if (haveGlareImage && glareImage != NULL) {
   336 			for(u_int i = 0; i < nPix; ++i) {
   337 				xyzpixels[i] += glareAmount * glareImage[i];
   338 			}
   339 		}
   340 	}
   341 
   342 	// Apply tone reproduction to image
   343 	if (toneMapName) {
   344 		ToneMap *toneMap = MakeToneMap(toneMapName,
   345 			toneMapParams ? *toneMapParams : ParamSet());
   346 		if (toneMap)
   347 			toneMap->Map(xyzpixels, xResolution, yResolution, 100.f);
   348 		delete toneMap;
   349 	}
   350 
   351 	// Convert to RGB
   352 	vector<RGBColor> &rgbpixels = reinterpret_cast<vector<RGBColor> &>(xyzpixels);
   353 	for (u_int i = 0; i < nPix; ++i)
   354 		rgbpixels[i] = colorSpace.ToRGBConstrained(xyzpixels[i]);
   355 
   356 	// DO NOT USE xyzpixels ANYMORE AFTER THIS POINT
   357 	if (response && response->validFile) {
   358 		for (u_int i = 0; i < nPix; ++i)
   359 			response->Map(rgbpixels[i]);
   360 	}
   361 
   362 	// Add vignetting & chromatic aberration effect
   363 	// These are paired in 1 loop as they can share quite a few calculations
   364 	if ((VignettingEnabled && VignetScale != 0.0f) ||
   365 		(aberrationEnabled && aberrationAmount > 0.f)) {
   366 
   367 		RGBColor *outp = &rgbpixels[0];
   368 		std::vector<RGBColor> aberrationImage;
   369 		if (aberrationEnabled) {
   370 			aberrationImage.resize(nPix, RGBColor(0.f));
   371 			outp = &aberrationImage[0];
   372 		}
   373 
   374 		const float invxRes = 1.f / xResolution;
   375 		const float invyRes = 1.f / yResolution;
   376 		//for each pixel in the source image
   377 		for(u_int y = 0; y < yResolution; ++y) {
   378 			for(u_int x = 0; x < xResolution; ++x) {
   379 				const float nPx = x * invxRes;
   380 				const float nPy = y * invyRes;
   381 				const float xOffset = nPx - 0.5f;
   382 				const float yOffset = nPy - 0.5f;
   383 				const float tOffset = sqrtf(xOffset * xOffset + yOffset * yOffset);
   384 					
   385 				if (aberrationEnabled && aberrationAmount > 0.f) {
   386 					const float rb_x = (0.5f + xOffset * (1.f + tOffset * aberrationAmount)) * xResolution;
   387 					const float rb_y = (0.5f + yOffset * (1.f + tOffset * aberrationAmount)) * yResolution;
   388 					const float g_x =  (0.5f + xOffset * (1.f - tOffset * aberrationAmount)) * xResolution;
   389 					const float g_y =  (0.5f + yOffset * (1.f - tOffset * aberrationAmount)) * yResolution;
   390 
   391 					const float redblue[] = {1.f, 0.f, 1.f};
   392 					const float green[] = {0.f, 1.f, 0.f};
   393 
   394 					outp[xResolution * y + x] += RGBColor(redblue) * bilinearSampleImage<RGBColor>(rgbpixels, xResolution, yResolution, rb_x, rb_y);
   395 					outp[xResolution * y + x] += RGBColor(green) * bilinearSampleImage<RGBColor>(rgbpixels, xResolution, yResolution, g_x, g_y);
   396 				}
   397 
   398 				// Vignetting
   399 				if(VignettingEnabled && VignetScale != 0.0f) {
   400 					// normalize to range [0.f - 1.f]
   401 					const float invNtOffset = 1.f - (fabsf(tOffset) * 1.42f);
   402 					float vWeight = Lerp(invNtOffset, 1.f - VignetScale, 1.f);
   403 					for (u_int i = 0; i < 3; ++i)
   404 						outp[xResolution*y + x].c[i] *= vWeight;
   405 				}
   406 			}
   407 		}
   408 
   409 		if (aberrationEnabled) {
   410 			for(u_int i = 0; i < nPix; ++i)
   411 				rgbpixels[i] = aberrationImage[i];
   412 		}
   413 
   414 		aberrationImage.clear();
   415 	}
   416 
   417 	// Calculate histogram (if it is enabled and exists)
   418 	if (HistogramEnabled && histogram)
   419 		histogram->Calculate(rgbpixels, xResolution, yResolution);
   420 
   421 	// Apply Chiu Noise Reduction Filter
   422 	if(chiuParams.enabled) {
   423 		std::vector<RGBColor> chiuImage(nPix, RGBColor(0.f));
   424 
   425 		// NOTE - lordcrc - if includecenter is false, make sure radius 
   426 		// is a tad higher than 1 to include other pixels
   427 		const float radius = max(chiuParams.radius, 1.f + (chiuParams.includecenter ? 0.f : 1e-6f));
   428 
   429 		const u_int pixel_rad = Ceil2UInt(radius);
   430 		const u_int lookup_size = 2 * pixel_rad + 1;
   431 
   432 		//build filter lookup table
   433 		std::vector<float> weights(lookup_size * lookup_size);
   434 
   435 		float sumweight = 0.f;
   436 		u_int offset = 0;
   437 		for(int y = -static_cast<int>(pixel_rad); y <= static_cast<int>(pixel_rad); ++y) {
   438 			for(int x = -static_cast<int>(pixel_rad); x <= static_cast<int>(pixel_rad); ++x) {
   439 				if(x == 0 && y == 0)
   440 					weights[offset] = chiuParams.includecenter ? 1.0f : 0.0f;
   441 				else {
   442 					const float dx = x;
   443 					const float dy = y;
   444 					const float dist = sqrtf(dx * dx + dy * dy);
   445 					const float weight = powf(max(0.0f, 1.0f - dist / radius), 4.0f);
   446 					weights[offset] = weight;
   447 				}
   448 				sumweight += weights[offset++];
   449 			}
   450 		}
   451 
   452 		//normalise filter kernel
   453 		for(u_int y = 0; y < lookup_size; ++y)
   454 			for(u_int x = 0; x < lookup_size; ++x)
   455 				weights[lookup_size*y + x] /= sumweight;
   456 
   457 		//for each pixel in the source image
   458 		for (u_int y = 0; y < yResolution; ++y) {
   459 			//get min and max of current filter rect along y axis
   460 			const u_int miny = max(y, pixel_rad) - pixel_rad;
   461 			const u_int maxy = min(yResolution - 1, y + pixel_rad);
   462 
   463 			for (u_int x = 0; x < xResolution; ++x) {
   464 				//get min and max of current filter rect along x axis
   465 				const u_int minx = max(x, pixel_rad) - pixel_rad;
   466 				const u_int maxx = min(xResolution - 1, x + pixel_rad);
   467 
   468 				// For each pixel in the out image, in the filter radius
   469 				for(u_int ty = miny; ty < maxy; ++ty) {
   470 					for(u_int tx = minx; tx < maxx; ++tx) {
   471 						const u_int dx = x - tx + pixel_rad;
   472 						const u_int dy = y - ty + pixel_rad;
   473 						const float factor = weights[lookup_size*dy + dx];
   474 						chiuImage[xResolution*ty + tx].AddWeighted(factor, rgbpixels[xResolution*y + x]);
   475 					}
   476 				}
   477 			}
   478 		}
   479 		// Copyback
   480 		for(u_int i = 0; i < nPix; ++i)
   481 			rgbpixels[i] = chiuImage[i];
   482 
   483 		// remove used intermediate memory
   484 		chiuImage.clear();
   485 		weights.clear();
   486 	}
   487 
   488 	// Apply GREYCStoration noise reduction filter
   489 	if(GREYCParams.enabled) {
   490 		// Define Cimg image buffer and copy data
   491 		CImg<float> img(xResolution, yResolution, 1, 3);
   492 		for(u_int y = 0; y < yResolution; ++y) {
   493 			for(u_int x = 0; x < xResolution; ++x) {
   494 				const u_int index = xResolution * y + x;
   495 				// Note - Cimg float data must be in range [0,255] for GREYCStoration to work %100
   496 				for(u_int j = 0; j < 3; ++j)
   497 					img(x, y, 0, j) = rgbpixels[index].c[j] * 255;
   498 			}
   499 		}
   500 
   501 		for (unsigned int iter=0; iter<GREYCParams.nb_iter; iter++) {
   502 			img.blur_anisotropic(GREYCParams.amplitude,
   503 				GREYCParams.sharpness,
   504 				GREYCParams.anisotropy,
   505 				GREYCParams.alpha,
   506 				GREYCParams.sigma,
   507 				GREYCParams.dl,
   508 				GREYCParams.da,
   509 				GREYCParams.gauss_prec,
   510 				GREYCParams.interp,
   511 				GREYCParams.fast_approx,
   512 				1.0f); // gfact
   513 		}
   514 
   515 		// Copy data from cimg buffer back to pixels vector
   516 		const float inv_byte = 1.f/255;
   517 		for(u_int y = 0; y < yResolution; ++y) {
   518 			for(u_int x = 0; x < xResolution; ++x) {
   519 				const u_int index = xResolution * y + x;
   520 				for(u_int j = 0; j < 3; ++j)
   521 					rgbpixels[index].c[j] = img(x, y, 0, j) * inv_byte;
   522 			}
   523 		}
   524 	}
   525 
   526 	// Dither image
   527 	if (dither > 0.f)
   528 		for (u_int i = 0; i < nPix; ++i)
   529 			rgbpixels[i] += 2.f * dither * (lux::random::floatValueP() - .5f);
   530 }
   531 
   532 
   533 // Filter Look Up Table Definitions
   534 
   535 FilterLUT::FilterLUT(Filter *filter, const float offsetX, const float offsetY) {
   536 	const int x0 = Ceil2Int(offsetX - filter->xWidth);
   537 	const int x1 = Floor2Int(offsetX + filter->xWidth);
   538 	const int y0 = Ceil2Int(offsetY - filter->yWidth);
   539 	const int y1 = Floor2Int(offsetY + filter->yWidth);
   540 	lutWidth = x1 - x0 + 1;
   541 	lutHeight = y1 - y0 + 1;
   542 	//lut = new float[lutWidth * lutHeight];
   543 	lut.resize(lutWidth * lutHeight);
   544 
   545 	float totalWeight = 0.f;
   546 	unsigned int index = 0;
   547 	for (int iy = y0; iy <= y1; ++iy) {
   548 		for (int ix = x0; ix <= x1; ++ix) {
   549 			const float filterVal = filter->Evaluate(fabsf(ix - offsetX), fabsf(iy - offsetY));
   550 			totalWeight += filterVal;
   551 			lut[index++] = filterVal;
   552 		}
   553 	}
   554 
   555 	// Normalize LUT
   556 	index = 0;
   557 	for (int iy = y0; iy <= y1; ++iy) {
   558 		for (int ix = x0; ix <= x1; ++ix)
   559 			lut[index++] /= totalWeight;
   560 	}
   561 }
   562 
   563 FilterLUTs::FilterLUTs(Filter *filter, const unsigned int size) {		
   564 	lutsSize = size + 1;
   565 	step = 1.f / float(size);
   566 
   567 	luts.resize(lutsSize * lutsSize);
   568 
   569 	for (unsigned int iy = 0; iy < lutsSize; ++iy) {
   570 		for (unsigned int ix = 0; ix < lutsSize; ++ix) {
   571 			const float x = ix * step - 0.5f + step / 2.f;
   572 			const float y = iy * step - 0.5f + step / 2.f;
   573 
   574 			luts[ix + iy * lutsSize] = FilterLUT(filter, x, y);
   575 		}
   576 	}
   577 }
   578 
   579 // OutlierData Definitions
   580 ColorSystem OutlierData::cs(0.63f, 0.34f, 0.31f, 0.595f, 0.155f, 0.07f, 0.314275f, 0.329411f);
   581 
   582 // Film Function Definitions
   583 
   584 u_int Film::GetXResolution()
   585 {
   586 	return xResolution;
   587 }
   588 
   589 u_int Film::GetYResolution()
   590 {
   591 	return yResolution;
   592 }
   593 
   594 Film::Film(u_int xres, u_int yres, Filter *filt, u_int filtRes, const float crop[4], 
   595 		   const string &filename1, bool premult, bool useZbuffer,
   596 		   bool w_resume_FLM, bool restart_resume_FLM, bool write_FLM_direct, int haltspp, int halttime,
   597 		   bool debugmode, int outlierk, int tilec) :
   598 	Queryable("film"),
   599 	xResolution(xres), yResolution(yres),
   600 	EV(0.f), averageLuminance(0.f),
   601 	numberOfSamplesFromNetwork(0), numberOfLocalSamples(0), numberOfResumedSamples(0),
   602 	contribPool(NULL), filter(filt), filterTable(NULL), filterLUTs(NULL),
   603 	filename(filename1),
   604 	colorSpace(0.63f, 0.34f, 0.31f, 0.595f, 0.155f, 0.07f, 0.314275f, 0.329411f), // default is SMPTE
   605 	ZBuffer(NULL), use_Zbuf(useZbuffer),
   606 	debug_mode(debugmode), premultiplyAlpha(premult),
   607 	writeResumeFlm(w_resume_FLM), restartResumeFlm(restart_resume_FLM), writeFlmDirect(write_FLM_direct),
   608 	outlierRejection_k(outlierk), haltSamplesPerPixel(haltspp),
   609 	haltTime(halttime), histogram(NULL), enoughSamplesPerPixel(false)
   610 {
   611 	// Compute film image extent
   612 	memcpy(cropWindow, crop, 4 * sizeof(float));
   613 	xPixelStart = Ceil2UInt(xResolution * cropWindow[0]);
   614 	xPixelCount = max(1U, Ceil2UInt(xResolution * cropWindow[1]) - xPixelStart);
   615 	yPixelStart = Ceil2UInt(yResolution * cropWindow[2]);
   616 	yPixelCount = max(1U, Ceil2UInt(yResolution * cropWindow[3]) - yPixelStart);
   617 	int xRealWidth = Floor2Int(xPixelStart + .5f + xPixelCount + filter->xWidth) - Floor2Int(xPixelStart + .5f - filter->xWidth);
   618 	int yRealHeight = Floor2Int(yPixelStart + .5f + yPixelCount + filter->yWidth) - Floor2Int(yPixelStart + .5f - filter->yWidth);
   619 	samplePerPass = xRealWidth * yRealHeight;
   620 
   621 	boost::xtime_get(&creationTime, boost::TIME_UTC);
   622 
   623 	//Queryable parameters
   624 	AddIntAttribute(*this, "xResolution", "Horizontal resolution (pixels)", &Film::GetXResolution);
   625 	AddIntAttribute(*this, "yResolution", "Vertical resolution (pixels)", &Film::GetYResolution);
   626 	AddBoolAttribute(*this, "premultiplyAlpha", "Premultiplied alpha enabled", &Film::premultiplyAlpha);
   627 	AddStringAttribute(*this, "filename", "Output filename", filename, &Film::filename, Queryable::ReadWriteAccess);
   628 	AddFloatAttribute(*this, "EV", "Exposure value", &Film::EV);
   629 	AddFloatAttribute(*this, "averageLuminance", "Average Image Luminance", &Film::averageLuminance);
   630 	AddDoubleAttribute(*this, "numberOfLocalSamples", "Number of samples contributed to film on the local machine", &Film::numberOfLocalSamples);
   631 	AddDoubleAttribute(*this, "numberOfSamplesFromNetwork", "Number of samples contributed from network slaves", &Film::numberOfSamplesFromNetwork);
   632 	AddDoubleAttribute(*this, "numberOfResumedSamples", "Number of samples loaded from saved film", &Film::numberOfResumedSamples);
   633 	AddBoolAttribute(*this, "enoughSamples", "Indicates if the halt condition been reached", &Film::enoughSamplesPerPixel);
   634 	AddIntAttribute(*this, "haltSamplesPerPixel", "Halt Samples per Pixel", haltSamplesPerPixel, &Film::haltSamplesPerPixel, Queryable::ReadWriteAccess);
   635 	AddIntAttribute(*this, "haltTime", "Halt time in seconds", haltTime, &Film::haltTime, Queryable::ReadWriteAccess);
   636 	AddBoolAttribute(*this, "writeResumeFlm", "Write resume file", writeResumeFlm, &Film::writeResumeFlm, Queryable::ReadWriteAccess);
   637 	AddBoolAttribute(*this, "restartResumeFlm", "Restart (overwrite) resume file", restartResumeFlm, &Film::restartResumeFlm, Queryable::ReadWriteAccess);
   638 	AddBoolAttribute(*this, "writeFlmDirect", "Write resume file directly to disk", writeFlmDirect, &Film::writeFlmDirect, Queryable::ReadWriteAccess);	
   639 
   640 	// Precompute filter tables
   641 	filterLUTs = new FilterLUTs(filt, max(min(filtRes, 64u), 2u));
   642 
   643 	outlierCellWidth = Floor2UInt(2 * filter->xWidth);
   644 	outlierInvCellWidth = 1.f / outlierCellWidth;
   645 	outlierCellHeight = Floor2UInt(2 * filter->yWidth);
   646 	outlierInvCellHeight = 1.f / outlierCellHeight;
   647 
   648 	const u_int thread_count = boost::thread::hardware_concurrency();
   649 	// base min tile size on outlier cell height, as it's a good measure anyway
   650 	const u_int minTileHeight = outlierCellHeight;
   651 	if (tilec > 0)
   652 		tileCount = tilec;
   653 	else
   654 		// TODO - thread_count * 2 is fairly arbitrary, find better choice?
   655 		tileCount = thread_count * (tilec < 0 ? -tilec : 2);
   656 	
   657 	LOG(LUX_DEBUG, LUX_NOERROR) << "Requested film tile count: " << tileCount;
   658 
   659 	tileCount = Clamp(tileCount, 1u, yRealHeight / minTileHeight);
   660 	//tileCount = 2;
   661 
   662 	tileHeight = max(Ceil2UInt(static_cast<float>(yRealHeight) / tileCount), minTileHeight);
   663 	if (outlierRejection_k > 0) {
   664 		// if outlier rejection is enabled, tileHeight must be multiple of outlierCellHeight
   665 		// increase tileHeight to ensure this
   666 		tileHeight = outlierCellHeight * max(Ceil2UInt(static_cast<float>(tileHeight) / outlierCellHeight), 1u);
   667 		tileCount = max(Ceil2UInt(static_cast<float>(yRealHeight) / tileHeight), 1u);
   668 	}
   669 
   670 	LOG(LUX_DEBUG, LUX_NOERROR) << "Actual film tile count: " << tileCount;
   671 
   672 	invTileHeight = 1.f / tileHeight;
   673 	tileOffset = -0.5f - filter->yWidth - yPixelStart;
   674 	tileOffset2 = 2 * filter->yWidth * invTileHeight;
   675 
   676 	if (outlierRejection_k > 0) {
   677 		const u_int outliers_width = xRealWidth / outlierCellWidth;
   678 		const u_int outliers_height = yRealHeight / outlierCellHeight;
   679 		outliers.resize(outliers_height);
   680 		for (size_t i = 0; i < outliers.size(); ++i)
   681 			outliers[i].resize(outliers_width);
   682 		// tiles need duplicate data for row above and below tile
   683 		tileborder_outliers.resize(2 * tileCount);
   684 		for (size_t i = 0; i < tileborder_outliers.size(); ++i)
   685 			tileborder_outliers[i].resize(outliers_width);
   686 	}
   687 }
   688 
   689 Film::~Film()
   690 {
   691 	delete filterLUTs;
   692 	delete filter;
   693 	delete ZBuffer;
   694 	delete histogram;
   695 	delete contribPool;
   696 }
   697 
   698 void Film::RequestBufferGroups(const vector<string> &bg)
   699 {
   700 	for (u_int i = 0; i < bg.size(); ++i)
   701 		bufferGroups.push_back(BufferGroup(bg[i]));
   702 }
   703 
   704 u_int Film::RequestBuffer(BufferType type, BufferOutputConfig output,
   705 	const string& filePostfix)
   706 {
   707 	bufferConfigs.push_back(BufferConfig(type, output, filePostfix));
   708 	return bufferConfigs.size() - 1;
   709 }
   710 
   711 void Film::CreateBuffers()
   712 {
   713 	if (bufferGroups.size() == 0)
   714 		bufferGroups.push_back(BufferGroup("default"));
   715 	for (u_int i = 0; i < bufferGroups.size(); ++i)
   716 		bufferGroups[i].CreateBuffers(bufferConfigs,xPixelCount,yPixelCount);
   717 
   718 	// Allocate ZBuf buffer if needed
   719 	if(use_Zbuf)
   720 		ZBuffer = new PerPixelNormalizedFloatBuffer(xPixelCount,yPixelCount);
   721 
   722     // Dade - check if we have to resume a rendering and restore the buffers
   723     if(writeResumeFlm) {
   724 		const string fname = filename+".flm";
   725 		if (restartResumeFlm) {
   726 			const string oldfname = fname + "1";
   727 			if (boost::filesystem::exists(fname)) {
   728 				if (boost::filesystem::exists(oldfname))
   729 					remove(oldfname.c_str());
   730 				rename(fname.c_str(), oldfname.c_str());
   731 			}
   732 		} else {
   733 			// Dade - check if the film file exists
   734 			std::ifstream ifs(fname.c_str(), std::ios_base::in | std::ios_base::binary);
   735 
   736 			if(ifs.good()) {
   737 				// Dade - read the data
   738 				LOG(LUX_INFO,LUX_NOERROR)<< "Reading film status from file " << fname;
   739 				numberOfResumedSamples = UpdateFilm(ifs);
   740 			}
   741 
   742 			ifs.close();
   743 		}
   744     }
   745 
   746 	// initialize the contribution pool
   747 	contribPool = new ContributionPool(this);
   748 }
   749 
   750 void Film::ClearBuffers()
   751 {
   752 	for (u_int i = 0; i < bufferGroups.size(); ++i) {
   753 
   754 		BufferGroup& bufferGroup = bufferGroups[i];
   755 
   756 		for (u_int j = 0; j < bufferConfigs.size(); ++j) {
   757 			Buffer* buffer = bufferGroup.getBuffer(j);
   758 
   759 			buffer->Clear();
   760 		}
   761 
   762 		bufferGroup.numberOfSamples = 0;
   763 	}
   764 }
   765 
   766 void Film::SetGroupName(u_int index, const string& name) 
   767 {
   768 	if( index >= bufferGroups.size())
   769 		return;
   770 	bufferGroups[index].name = name;
   771 }
   772 string Film::GetGroupName(u_int index) const
   773 {
   774 	if (index >= bufferGroups.size())
   775 		return "";
   776 	return bufferGroups[index].name;
   777 }
   778 void Film::SetGroupEnable(u_int index, bool status)
   779 {
   780 	if (index >= bufferGroups.size())
   781 		return;
   782 	bufferGroups[index].enable = status;
   783 }
   784 bool Film::GetGroupEnable(u_int index) const
   785 {
   786 	if (index >= bufferGroups.size())
   787 		return false;
   788 	return bufferGroups[index].enable;
   789 }
   790 void Film::SetGroupScale(u_int index, float value)
   791 {
   792 	if (index >= bufferGroups.size())
   793 		return;
   794 	bufferGroups[index].globalScale = value;
   795 	ComputeGroupScale(index);
   796 }
   797 float Film::GetGroupScale(u_int index) const
   798 {
   799 	if (index >= bufferGroups.size())
   800 		return 0.f;
   801 	return bufferGroups[index].globalScale;
   802 }
   803 void Film::SetGroupRGBScale(u_int index, const RGBColor &value)
   804 {
   805 	if (index >= bufferGroups.size())
   806 		return;
   807 	bufferGroups[index].rgbScale = value;
   808 	ComputeGroupScale(index);
   809 }
   810 RGBColor Film::GetGroupRGBScale(u_int index) const
   811 {
   812 	if (index >= bufferGroups.size())
   813 		return 0.f;
   814 	return bufferGroups[index].rgbScale;
   815 }
   816 void Film::SetGroupTemperature(u_int index, float value)
   817 {
   818 	if (index >= bufferGroups.size())
   819 		return;
   820 	bufferGroups[index].temperature = value;
   821 	ComputeGroupScale(index);
   822 }
   823 float Film::GetGroupTemperature(u_int index) const
   824 {
   825 	if (index >= bufferGroups.size())
   826 		return 0.f;
   827 	return bufferGroups[index].temperature;
   828 }
   829 void Film::ComputeGroupScale(u_int index)
   830 {
   831 	const XYZColor white(colorSpace.ToXYZ(RGBColor(1.f)));
   832 	if (bufferGroups[index].temperature > 0.f) {
   833 		XYZColor colorTemp(BlackbodySPD(bufferGroups[index].temperature));
   834 		colorTemp /= colorTemp.Y();
   835 		bufferGroups[index].convert = ColorAdaptator(white,
   836 			colorSpace.ToXYZ(bufferGroups[index].rgbScale)) *
   837 			ColorAdaptator(white, colorTemp);
   838 	} else {
   839 		bufferGroups[index].convert = ColorAdaptator(white,
   840 			colorSpace.ToXYZ(bufferGroups[index].rgbScale));
   841 	}
   842 	bufferGroups[index].convert *= bufferGroups[index].globalScale;
   843 }
   844 
   845 void Film::GetSampleExtent(int *xstart, int *xend,
   846 	int *ystart, int *yend) const
   847 {
   848 	*xstart = Floor2Int(xPixelStart + .5f - filter->xWidth);
   849 	*xend   = Floor2Int(xPixelStart + .5f + xPixelCount + filter->xWidth);
   850 	*ystart = Floor2Int(yPixelStart + .5f - filter->yWidth);
   851 	*yend   = Floor2Int(yPixelStart + .5f + yPixelCount + filter->yWidth);
   852 }
   853 
   854 void Film::AddSampleCount(float count) {
   855 	if (haltTime > 0) {
   856 		// Check if we have met the enough rendering time condition
   857 		boost::xtime t;
   858 		boost::xtime_get(&t, boost::TIME_UTC);
   859 		if (t.sec - creationTime.sec > haltTime)
   860 			enoughSamplesPerPixel = true;
   861 	}
   862 
   863 	numberOfLocalSamples += count;
   864 
   865 	for (u_int i = 0; i < bufferGroups.size(); ++i) {
   866 		bufferGroups[i].numberOfSamples += count;
   867 
   868 		// Dade - check if we have enough samples per pixel. The rendering stop
   869 		// when one of the buffer groups has enough samples (at the moment all
   870 		// buffer groups have always the same samples count; in the future
   871 		// it could be better to stop when all buffer groups have enough samples)
   872 		if ((haltSamplesPerPixel > 0) &&
   873 			(bufferGroups[i].numberOfSamples  >= haltSamplesPerPixel * samplePerPass))
   874 			enoughSamplesPerPixel = true;
   875 	}
   876 }
   877 
   878 
   879 std::vector<Film::OutlierAccel>& Film::GetOutlierAccelRow(u_int oY, u_int tileIndex, u_int tileStart, u_int tileEnd)
   880 {
   881 	if (oY < tileStart) {
   882 		// above currrent tile
   883 		return tileborder_outliers[2 * tileIndex];
   884 	} else if (oY >= tileEnd) {
   885 		// below current tile
   886 		return tileborder_outliers[2 * tileIndex + 1];
   887 	}
   888 
   889 	// inside current tile
   890 	return outliers[oY];
   891 }
   892 
   893 void Film::RejectTileOutliers(const Contribution* const contribs, u_int num_contribs, u_int tileIndex, int yTilePixelStart, int yTilePixelEnd)
   894 {
   895 	// outlier rejection
   896 	const float fnormTileStart = (yTilePixelStart + filter->yWidth) * outlierInvCellHeight;
   897 	const float fnormTileEnd   = (yTilePixelEnd   + filter->yWidth) * outlierInvCellHeight;
   898 
   899 	const u_int tileStart = static_cast<u_int>(max(0, min(Floor2Int(fnormTileStart), static_cast<int>(outliers.size() - 1))));
   900 	const u_int tileEnd =   static_cast<u_int>(max(0, min(Floor2Int(fnormTileEnd),   static_cast<int>(outliers.size() - 1))));
   901 
   902 	for (u_int ci = 0; ci < num_contribs; ++ci) {
   903 		const Contribution &contrib(contribs[ci]);
   904 
   905 		// filter-normalized pixel coordinates
   906 		const float fnormX = (contrib.imageX - 0.5f + filter->xWidth) * outlierInvCellWidth;
   907 		const float fnormY = (contrib.imageY - 0.5f + filter->yWidth) * outlierInvCellHeight;
   908 
   909 		OutlierData sd(fnormX, fnormY, contrib.color);
   910 
   911 		// perform lookup based on original position
   912 		// constrain to tile only if we need to add the outlier
   913 		const int oY = max(0, min(Floor2Int(fnormY), static_cast<int>(outliers.size() - 1)));
   914 		const int oX = max(0, min(Floor2Int(fnormX), static_cast<int>(outliers[0].size() - 1)));
   915 		
   916 		std::vector<OutlierAccel> &outlierRow = GetOutlierAccelRow(oY, tileIndex, tileStart, tileEnd);
   917 		OutlierAccel &outlierAccel = outlierRow[oX];
   918 
   919 	NearSetPointProcess<OutlierData::Point_t> proc(outlierRejection_k);
   920 	vector<ClosePoint<OutlierData::Point_t> > closest(outlierRejection_k);
   921 	proc.points = &closest[0];
   922 
   923 	float maxDist = INFINITY;
   924 
   925 	outlierAccel.Lookup(sd.p, proc, maxDist);
   926 
   927 	float kmeandist = 0.f;
   928 	for (u_int i = 0; i < proc.foundPoints; i++)
   929 		kmeandist += proc.points[i].distance;
   930 	
   931 	//kmeandist /= proc.foundPoints;
   932 		
   933 		if (proc.foundPoints < 1 || kmeandist > proc.foundPoints) { // kmeandist > 1.f
   934 			// add outlier and return
   935 			// include surrounding cells so we don't have to
   936 			// traverse multiple cells for each lookup
   937 			const u_int oLeft = static_cast<u_int>(max(0, oX - 1));
   938 			const u_int oRight = static_cast<u_int>(min(static_cast<int>(outliers[0].size() - 1), oX + 1));
   939 			const u_int oTop = static_cast<u_int>(max(0, oY - 1));
   940 			const u_int oBottom = static_cast<u_int>(min(static_cast<int>(outliers.size() - 1), oY + 1));
   941 
   942 			if (oTop < tileStart || oBottom >= tileEnd) {
   943 				// outlier spans tile borders
   944 				for (u_int i = oTop; i <= oBottom; ++i) {
   945 					std::vector<OutlierAccel> &row = GetOutlierAccelRow(oY, tileIndex, tileStart, tileEnd);
   946 					for (u_int j = oLeft; j <= oRight; ++j) {
   947 						row[j].AddNode(sd.p);
   948 					}
   949 				}
   950 			} else {
   951 				// we're all inside one tile
   952 				for (u_int i = oTop; i <= oBottom; ++i) {
   953 					std::vector<OutlierAccel> &row = outliers[oY];
   954 					for (u_int j = oLeft; j <= oRight; ++j) {
   955 						row[j].AddNode(sd.p);
   956 					}
   957 				}
   958 			}
   959 			// outlier, reject
   960 			contrib.variance = -1.f;
   961 		}
   962 		// not an outlier, splat
   963 	}
   964 }
   965 
   966 u_int Film::GetTileCount() const {
   967 	return tileCount;
   968 }
   969 
   970 u_int Film::GetTileIndexes(const Contribution &contrib, u_int *tile0, u_int *tile1) const {
   971 	const float imageY = contrib.imageY + tileOffset;
   972 
   973 	const float tileY = imageY * invTileHeight;
   974 
   975 	*tile0 = static_cast<u_int>(Clamp(static_cast<int>(tileY), 0, static_cast<int>(tileCount-1)));
   976 	*tile1 = *tile0 + 1;
   977 
   978 	if (*tile1 >= tileCount || tileY + tileOffset2 < *tile1)
   979 		return 1u;
   980 
   981 	return 2u;
   982 }
   983 
   984 void Film::GetTileExtent(u_int tileIndex, int *xstart, int *xend, int *ystart, int *yend) const {
   985 	*xstart = xPixelStart;
   986 	*xend = xPixelStart + xPixelCount;
   987 	*ystart = yPixelStart + min(tileIndex * tileHeight, yPixelCount);
   988 	*yend = yPixelStart + min((tileIndex+1) * tileHeight, yPixelCount);
   989 }
   990 
   991 
   992 void Film::AddTileSamples(const Contribution* const contribs, u_int num_contribs, u_int tileIndex) {
   993 	
   994 	int xTilePixelStart, xTilePixelEnd;
   995 	int yTilePixelStart, yTilePixelEnd;
   996 	GetTileExtent(tileIndex, &xTilePixelStart, &xTilePixelEnd, &yTilePixelStart, &yTilePixelEnd);
   997 
   998 	if (outlierRejection_k > 0) {
   999 		// reject outliers by setting their weight (variance field) to -1
  1000 		RejectTileOutliers(contribs, num_contribs, tileIndex, yTilePixelStart, yTilePixelEnd);
  1001 	}
  1002 
  1003 
  1004 	for (u_int ci = 0; ci < num_contribs; ci++) {
  1005 		const Contribution &contrib(contribs[ci]);
  1006 
  1007 		XYZColor xyz = contrib.color;
  1008 		const float alpha = contrib.alpha;
  1009 		const float weight = contrib.variance;
  1010 
  1011 		// negative weight means sample was rejected
  1012 		// so do this test first
  1013 		if (!(weight >= 0.f) || isinf(weight)) {
  1014 			if(debug_mode && (weight >= 0.f)) {
  1015 				LOG(LUX_WARNING,LUX_LIMIT) << "Out of bound  weight in Film::AddSample: "
  1016 				   << weight << ", sample discarded";
  1017 			}
  1018 			continue;
  1019 		}
  1020 
  1021 		// Issue warning if unexpected radiance value returned
  1022 		if (!(xyz.Y() >= 0.f) || isinf(xyz.Y())) {
  1023 			if(debug_mode) {
  1024 				LOG(LUX_WARNING,LUX_LIMIT) << "Out of bound intensity in Film::AddSample: "
  1025 				   << xyz.Y() << ", sample discarded";
  1026 			}
  1027 			continue;
  1028 		}
  1029 
  1030 		if (!(alpha >= 0.f) || isinf(alpha)) {
  1031 			if(debug_mode) {
  1032 				LOG(LUX_WARNING,LUX_LIMIT) << "Out of bound  alpha in Film::AddSample: "
  1033 				   << alpha << ", sample discarded";
  1034 			}
  1035 			continue;
  1036 		}
  1037 	
  1038 		if (premultiplyAlpha)
  1039 			xyz *= alpha;
  1040 
  1041 		BufferGroup &currentGroup = bufferGroups[contrib.bufferGroup];
  1042 		Buffer *buffer = currentGroup.getBuffer(contrib.buffer);
  1043 
  1044 		// Compute sample's raster extent
  1045 		float dImageX = contrib.imageX - 0.5f;
  1046 		float dImageY = contrib.imageY - 0.5f;
  1047 
  1048 		// Get filter coefficients
  1049 		const FilterLUT &filterLUT = 
  1050 			filterLUTs->GetLUT(dImageX - Floor2Int(contrib.imageX), dImageY - Floor2Int(contrib.imageY));
  1051 		const float *lut = filterLUT.GetLUT();
  1052 
  1053 		int x0 = Ceil2Int (dImageX - filter->xWidth);
  1054 		int x1 = x0 + filterLUT.GetWidth();
  1055 		int y0 = Ceil2Int (dImageY - filter->yWidth);
  1056 		int y1 = y0 + filterLUT.GetHeight();
  1057 		if (x1 < x0 || y1 < y0 || x1 < 0 || y1 < 0)
  1058 			continue;
  1059 
  1060 		const u_int xStart = static_cast<u_int>(max(x0, xTilePixelStart));
  1061 		const u_int yStart = static_cast<u_int>(max(y0, yTilePixelStart));
  1062 		const u_int xEnd = static_cast<u_int>(min(x1, xTilePixelEnd));
  1063 		const u_int yEnd = static_cast<u_int>(min(y1, yTilePixelEnd));
  1064 
  1065 		for (u_int y = yStart; y < yEnd; ++y) {
  1066 			const int yoffset = (y-y0) * filterLUT.GetWidth();
  1067 			for (u_int x = xStart; x < xEnd; ++x) {
  1068 				// Evaluate filter value at $(x,y)$ pixel
  1069 				const int xoffset = x-x0;
  1070 				const float filterWt = lut[yoffset + xoffset];
  1071 				// Update pixel values with filtered sample contribution
  1072 				buffer->Add(x - xPixelStart,y - yPixelStart,
  1073 					xyz, alpha, filterWt * weight);
  1074 				// Update ZBuffer values with filtered zdepth contribution
  1075 				if(use_Zbuf && contrib.zdepth != 0.f)
  1076 					ZBuffer->Add(x - xPixelStart, y - yPixelStart, contrib.zdepth, 1.0f);
  1077 			}
  1078 		}
  1079 	}
  1080 }
  1081 
  1082 void Film::AddSample(Contribution *contrib) {
  1083 	u_int tileIndex0, tileIndex1;
  1084 	u_int tiles = GetTileIndexes(*contrib, &tileIndex0, &tileIndex1);
  1085 	AddTileSamples(contrib, 1, tileIndex0);
  1086 	if (tiles > 1)
  1087 		AddTileSamples(contrib, 1, tileIndex1);
  1088 }
  1089 
  1090 void Film::SetSample(const Contribution *contrib) {
  1091 	XYZColor xyz = contrib->color;
  1092 	const float alpha = contrib->alpha;
  1093 	const float weight = contrib->variance;
  1094 	const int x = static_cast<int>(contrib->imageX);
  1095 	const int y = static_cast<int>(contrib->imageY);
  1096 
  1097 	if (x < static_cast<int>(xPixelStart) || x >= static_cast<int>(xPixelStart + xPixelCount) ||
  1098 		y < static_cast<int>(yPixelStart) || y >= static_cast<int>(yPixelStart + yPixelCount)) {
  1099 		if(debug_mode) {
  1100 			LOG(LUX_WARNING, LUX_LIMIT) << "Out of bound pixel coordinates in Film::SetSample: ("
  1101 					<< x << ", " << y << "), sample discarded";
  1102 		}
  1103 		return;
  1104 	}
  1105 
  1106 	// Issue warning if unexpected radiance value returned
  1107 	if (!(xyz.Y() >= 0.f) || isinf(xyz.Y())) {
  1108 		if(debug_mode) {
  1109 			LOG(LUX_WARNING, LUX_LIMIT) << "Out of bound intensity in Film::SetSample: "
  1110 			   << xyz.Y() << ", sample discarded";
  1111 		}
  1112 		return;
  1113 	}
  1114 
  1115 	if (!(alpha >= 0.f) || isinf(alpha)) {
  1116 		if(debug_mode) {
  1117 			LOG(LUX_WARNING, LUX_LIMIT) << "Out of bound  alpha in Film::SetSample: "
  1118 			   << alpha << ", sample discarded";
  1119 		}
  1120 		return;
  1121 	}
  1122 
  1123 	if (!(weight >= 0.f) || isinf(weight)) {
  1124 		if(debug_mode) {
  1125 			LOG(LUX_WARNING, LUX_LIMIT) << "Out of bound  weight in Film::SetSample: "
  1126 			   << weight << ", sample discarded";
  1127 		}
  1128 		return;
  1129 	}
  1130 
  1131 /*FIXME restore the functionality
  1132 	// Reject samples higher than max Y() after warmup period
  1133 	if (warmupComplete) {
  1134 		if (xyz.Y() > maxY)
  1135 			return;
  1136 	} else {
  1137 		maxY = max(maxY, xyz.Y());
  1138 		++warmupSamples;
  1139 		if (warmupSamples >= reject_warmup_samples)
  1140 			warmupComplete = true;
  1141 	}
  1142 */
  1143 
  1144 	if (premultiplyAlpha)
  1145 		xyz *= alpha;
  1146 
  1147 	BufferGroup &currentGroup = bufferGroups[contrib->bufferGroup];
  1148 	Buffer *buffer = currentGroup.getBuffer(contrib->buffer);
  1149 
  1150 	buffer->Set(x, y, xyz, alpha);
  1151 
  1152 	// Update ZBuffer values with filtered zdepth contribution
  1153 	if(use_Zbuf && contrib->zdepth != 0.f)
  1154 		ZBuffer->Add(x, y, contrib->zdepth, 1.0f);
  1155 }
  1156 
  1157 void Film::WriteResumeFilm(const string &filename)
  1158 {
  1159 	string fullfilename = boost::filesystem::complete(boost::filesystem::path(filename, boost::filesystem::native), boost::filesystem::current_path()).file_string();
  1160 	// Dade - save the status of the film to the file
  1161 	LOG(LUX_INFO, LUX_NOERROR) << "Writing resume film file";
  1162 
  1163 	const string tempfilename = fullfilename + ".temp";
  1164 
  1165     std::ofstream filestr(tempfilename.c_str(), std::ios_base::out | std::ios_base::binary);
  1166 	if(!filestr) {
  1167 		LOG(LUX_SEVERE,LUX_SYSTEM) << "Cannot open file '" << tempfilename << "' for writing resume film";
  1168 
  1169 		return;
  1170 	}
  1171 
  1172 	bool writeSuccessful = TransmitFilm(filestr,false,true, true, writeFlmDirect);
  1173 
  1174     filestr.close();
  1175 
  1176 	if (writeSuccessful) {
  1177 		try {
  1178 #if !defined(BOOST_FILESYSTEM_VERSION) || (BOOST_FILESYSTEM_VERSION < 3)
  1179 			// boost filesystem v2 does not have POSIX compliant rename()
  1180 			if (boost::filesystem::exists(fullfilename))
  1181 				boost::filesystem::remove(fullfilename);
  1182 #endif
  1183 			boost::filesystem::rename(tempfilename, fullfilename);
  1184 			LOG(LUX_INFO, LUX_NOERROR) << "Resume film written to '" << fullfilename << "'";
  1185 		} catch (std::runtime_error e) {
  1186 			LOG(LUX_ERROR, LUX_SYSTEM) << 
  1187 				"Failed to rename new resume film, leaving new resume film as '" << tempfilename << "' (" << e.what() << ")";
  1188 		}
  1189 	}
  1190 }
  1191 
  1192 
  1193 /**
  1194  * FLM format
  1195  * ----------
  1196  *
  1197  * Layout:
  1198  *
  1199  *   HEADER
  1200  *   magic_number                  - int   - the magic number number
  1201  *   version_number                - int   - the version number
  1202  *   x_resolution                  - int   - the x resolution of the buffers
  1203  *   y_resolution                  - int   - the y resolution of the buffers
  1204  *   #buffer_groups                - u_int - the number of lightgroups
  1205  *   #buffer_configs               - u_int - the number of buffers per light group
  1206  *   for i in 1:#buffer_configs
  1207  *     buffer_type                 - int   - the type of the i'th buffer
  1208  *   #parameters                   - u_int - the number of stored parameters
  1209  *   for i in 1:#parameters
  1210  *     param_type                  - int   - the type of the i'th parameter
  1211  *     param_size                  - int   - the size of the value of the i'th parameter in bytes
  1212  *     param_id                    - int   - the id of the i'th parameter
  1213  *     param_index                 - int   - the index of the i'th parameter
  1214  *     param_value                 - *     - the value of the i'th parameter
  1215  *
  1216  *   DATA
  1217  *   for i in 1:#buffer_groups
  1218  *     #samples                    - float - the number of samples in the i'th buffer group
  1219  *     for j in 1:#buffer_configs
  1220  *       for y in 1:y_resolution
  1221  *         for x in 1:x_resolution
  1222  *           X                     - float - the weighted sum of all X values added to the pixel
  1223  *           Y                     - float - the weighted sum of all Y values added to the pixel
  1224  *           Z                     - float - the weighted sum of all Z values added to the pixel
  1225  *           alpha                 - float - the weighted sum of all alpha values added to the pixel
  1226  *           weight_sum            - float - the sum of al weights of all values added to the pixel
  1227  *     
  1228  *
  1229  * Remarks:
  1230  *  - data is written as binary little-endian
  1231  *  - data is gzipped
  1232  *  - the version is not intended for backward/forward compatibility but just as a check
  1233  */
  1234 static const int FLM_MAGIC_NUMBER = 0xCEBCD816;
  1235 static const int FLM_VERSION = 0; // should be incremented on each change to the format to allow detecting unsupported FLM data!
  1236 enum FlmParameterType {
  1237 	FLM_PARAMETER_TYPE_FLOAT = 0,
  1238 	FLM_PARAMETER_TYPE_STRING = 1,
  1239 	FLM_PARAMETER_TYPE_DOUBLE = 2
  1240 };
  1241 
  1242 class FlmParameter {
  1243 public:
  1244 	FlmParameter() {}
  1245 	FlmParameter(Film *aFilm, FlmParameterType aType, luxComponentParameters aParam, u_int aIndex) {
  1246 		type = aType;
  1247 		id = aParam;
  1248 		index = aIndex;
  1249 		switch (type) {
  1250 			case FLM_PARAMETER_TYPE_FLOAT:
  1251 				size = 4;
  1252 				floatValue = static_cast<float>(aFilm->GetParameterValue(aParam, aIndex));
  1253 				break;
  1254 			case FLM_PARAMETER_TYPE_DOUBLE:
  1255 				size = 8;
  1256 				floatValue = static_cast<double>(aFilm->GetParameterValue(aParam, aIndex));
  1257 				break;
  1258 			case FLM_PARAMETER_TYPE_STRING:
  1259 				stringValue = aFilm->GetStringParameterValue(aParam, aIndex);
  1260 				size = stringValue.size();
  1261 				break;
  1262 			default: {
  1263 				LOG(LUX_ERROR,LUX_SYSTEM) << "Invalid parameter type (expected value in [0,2], got=" << type << ")";
  1264 				break;
  1265 			}
  1266 		}
  1267 	}
  1268 
  1269 	void Set(Film *aFilm) {
  1270 		switch (type) {
  1271 			case FLM_PARAMETER_TYPE_FLOAT:
  1272 				aFilm->SetParameterValue(id, floatValue, index);
  1273 				break;
  1274 			case FLM_PARAMETER_TYPE_DOUBLE:
  1275 				aFilm->SetParameterValue(id, floatValue, index);
  1276 				break;
  1277 			case FLM_PARAMETER_TYPE_STRING:
  1278 				aFilm->SetStringParameterValue(id, stringValue, index);
  1279 				break;
  1280 			default:
  1281 				// ignore invalid type (already reported in constructor)
  1282 				break;
  1283 		}
  1284 	}
  1285 
  1286 	bool Read(std::basic_istream<char> &is, bool isLittleEndian, Film *film ) {
  1287 		int tmpType;
  1288 		tmpType = osReadLittleEndianInt(isLittleEndian, is);
  1289 		type = FlmParameterType(tmpType);
  1290 		if (!is.good()) {
  1291 			LOG(LUX_ERROR,LUX_SYSTEM)<< "Error while receiving film";
  1292 			return false;
  1293 		}
  1294 		size = osReadLittleEndianUInt(isLittleEndian, is);
  1295 		if (!is.good()) {
  1296 			LOG(LUX_ERROR,LUX_SYSTEM)<< "Error while receiving film";
  1297 			return false;
  1298 		}
  1299 		id = static_cast<luxComponentParameters>(osReadLittleEndianInt(isLittleEndian, is));
  1300 		if (!is.good()) {
  1301 			LOG(LUX_ERROR,LUX_SYSTEM)<< "Error while receiving film";
  1302 			return false;
  1303 		}
  1304 		index = osReadLittleEndianUInt(isLittleEndian, is);
  1305 		if (!is.good()) {
  1306 			LOG(LUX_ERROR,LUX_SYSTEM)<< "Error while receiving film";
  1307 			return false;
  1308 		}
  1309 		switch (type) {
  1310 			case FLM_PARAMETER_TYPE_FLOAT:
  1311 				if (size != 4) {
  1312 					LOG(LUX_ERROR,LUX_SYSTEM) << "Invalid parameter size (expected value for float is 4, received=" << size << ")";
  1313 					return false;
  1314 				}
  1315 				floatValue = osReadLittleEndianFloat(isLittleEndian, is);
  1316 				break;
  1317 			case FLM_PARAMETER_TYPE_DOUBLE:
  1318 				if (size != 8) {
  1319 					LOG(LUX_ERROR,LUX_SYSTEM) << "Invalid parameter size (expected value for double is 8, received=" << size << ")";
  1320 					return false;
  1321 				}
  1322 				floatValue = osReadLittleEndianDouble(isLittleEndian, is);
  1323 				break;
  1324 			case FLM_PARAMETER_TYPE_STRING: {
  1325 				char* chars = new char[size+1];
  1326 				is.read(chars, size);
  1327 				chars[size] = '\0';
  1328 				stringValue = string(chars);
  1329 				delete[] chars;
  1330 				break;
  1331 			}
  1332 			default: {
  1333 				LOG(LUX_ERROR,LUX_SYSTEM) << "Invalid parameter type (expected value in [0,1], received=" << tmpType << ")";
  1334 				return false;
  1335 			}
  1336 		}
  1337 		return true;
  1338 	}
  1339 	void Write(std::basic_ostream<char> &os, bool isLittleEndian) const {
  1340 		osWriteLittleEndianInt(isLittleEndian, os, type);
  1341 		osWriteLittleEndianUInt(isLittleEndian, os, size);
  1342 		osWriteLittleEndianInt(isLittleEndian, os, id);
  1343 		osWriteLittleEndianUInt(isLittleEndian, os, index);
  1344 		switch (type) {
  1345 			case FLM_PARAMETER_TYPE_FLOAT:
  1346 				osWriteLittleEndianFloat(isLittleEndian, os, floatValue);
  1347 				break;
  1348 			case FLM_PARAMETER_TYPE_DOUBLE:
  1349 				osWriteLittleEndianDouble(isLittleEndian, os, floatValue);
  1350 				break;
  1351 			case FLM_PARAMETER_TYPE_STRING:
  1352 				os.write(stringValue.c_str(), size);
  1353 				break;
  1354 			default:
  1355 				// ignore invalid type (already reported in constructor)
  1356 				break;
  1357 		}
  1358 	}
  1359 
  1360 private:
  1361 	FlmParameterType type;
  1362 	u_int size;
  1363 	luxComponentParameters id;
  1364 	u_int index;
  1365 		
  1366 	double floatValue;
  1367 	string stringValue;
  1368 };
  1369 
  1370 class FlmHeader {
  1371 public:
  1372 	FlmHeader() {}
  1373 	bool Read(filtering_stream<input> &in, bool isLittleEndian, Film *film );
  1374 	void Write(std::basic_ostream<char> &os, bool isLittleEndian) const;
  1375 
  1376 	int magicNumber;
  1377 	int versionNumber;
  1378 	u_int xResolution;
  1379 	u_int yResolution;
  1380 	u_int numBufferGroups;
  1381 	u_int numBufferConfigs;
  1382 	vector<int> bufferTypes;
  1383 	u_int numParams;
  1384 	vector<FlmParameter> params;
  1385 };
  1386 
  1387 bool FlmHeader::Read(filtering_stream<input> &in, bool isLittleEndian, Film *film ) {
  1388 	// Read and verify magic number and version
  1389 	magicNumber = osReadLittleEndianInt(isLittleEndian, in);
  1390 	if (!in.good()) {
  1391 		LOG(LUX_ERROR,LUX_SYSTEM)<< "Error while receiving film";
  1392 		return false;
  1393 	}
  1394 	if (magicNumber != FLM_MAGIC_NUMBER) {
  1395 		LOG(LUX_ERROR,LUX_SYSTEM) << "Invalid FLM magic number (expected=" << FLM_MAGIC_NUMBER 
  1396 			<< ", received=" << magicNumber << ")";
  1397 		return false;
  1398 	}
  1399 	versionNumber = osReadLittleEndianInt(isLittleEndian, in);
  1400 	if (!in.good()) {
  1401 		LOG(LUX_ERROR,LUX_SYSTEM)<< "Error while receiving film";
  1402 		return false;
  1403 	}
  1404 	if (versionNumber != FLM_VERSION) {
  1405 		LOG(LUX_ERROR,LUX_SYSTEM) << "Invalid FLM version (expected=" << FLM_VERSION 
  1406 			<< ", received=" << versionNumber << ")";
  1407 		return false;
  1408 	}
  1409 	// Read and verify the buffer resolution
  1410 	xResolution = osReadLittleEndianUInt(isLittleEndian, in);
  1411 	yResolution = osReadLittleEndianUInt(isLittleEndian, in);
  1412 	if (xResolution == 0 || yResolution == 0 ) {
  1413 		LOG(LUX_ERROR,LUX_SYSTEM)
  1414 			<< "Invalid resolution (expected positive resolution, received="
  1415 			<< xResolution << "x" << yResolution
  1416 			<< ")";
  1417 		return false;
  1418 	}
  1419 	if (film != NULL &&
  1420 		(xResolution != film->GetXPixelCount() ||
  1421 		yResolution != film->GetYPixelCount())) {
  1422 		LOG(LUX_ERROR,LUX_SYSTEM)
  1423 			<< "Invalid resolution (expected=" << film->GetXPixelCount() << "x" << film->GetYPixelCount()
  1424 			<< ", received=" << xResolution << "x" << yResolution << ")";
  1425 		return false;
  1426 	}
  1427 	// Read and verify #buffer groups and buffer configs
  1428 	numBufferGroups = osReadLittleEndianUInt(isLittleEndian, in);
  1429 	if (!in.good()) {
  1430 		LOG(LUX_ERROR,LUX_SYSTEM)<< "Error while receiving film";
  1431 		return false;
  1432 	}
  1433 	if (film != NULL && numBufferGroups != film->GetNumBufferGroups()) {
  1434 		LOG(LUX_ERROR,LUX_SYSTEM)
  1435 			<< "Invalid number of buffer groups (expected=" << film->GetNumBufferGroups() 
  1436 			<< ", received=" << numBufferGroups << ")";
  1437 		return false;
  1438 	}
  1439 	numBufferConfigs = osReadLittleEndianUInt(isLittleEndian, in);
  1440 	if (!in.good()) {
  1441 		LOG(LUX_ERROR,LUX_SYSTEM)<< "Error while receiving film";
  1442 		return false;
  1443 	}
  1444 	if (film != NULL && numBufferConfigs != film->GetNumBufferConfigs()) {
  1445 		LOG(LUX_ERROR,LUX_SYSTEM)
  1446 			<< "Invalid number of buffers (expected=" << film->GetNumBufferConfigs()
  1447 			<< ", received=" << numBufferConfigs << ")";
  1448 		return false;
  1449 	}
  1450 	for (u_int i = 0; i < numBufferConfigs; ++i) {
  1451 		int type;
  1452 		type = osReadLittleEndianInt(isLittleEndian, in);
  1453 		if (!in.good()) {
  1454 			LOG(LUX_ERROR,LUX_SYSTEM)<< "Error while receiving film";
  1455 			return false;
  1456 		}
  1457 		if (type < 0 || type >= NUM_OF_BUFFER_TYPES) {
  1458 			LOG(LUX_ERROR,LUX_SYSTEM) << "Invalid buffer type for buffer " << i << "(expected number in [0," << NUM_OF_BUFFER_TYPES << "[, received=" << type << ")";
  1459 			return false;
  1460 		}
  1461 		if (film != NULL && type != film->GetBufferConfig(i).type) {
  1462 			LOG(LUX_ERROR,LUX_SYSTEM) << "Invalid buffer type for buffer " << i << " (expected=" << film->GetBufferConfig(i).type
  1463 				<< ", received=" << type << ")";
  1464 			return false;
  1465 		}
  1466 		bufferTypes.push_back(type);
  1467 	}
  1468 	// Read parameters
  1469 	numParams = osReadLittleEndianUInt(isLittleEndian, in);
  1470 	if (!in.good()) {
  1471 		LOG( LUX_ERROR,LUX_SYSTEM)<< "Error while receiving film";
  1472 		return false;
  1473 	}
  1474 	params.reserve(numParams);
  1475 	for(u_int i = 0; i < numParams; ++i) {
  1476 		FlmParameter param;
  1477 		bool ok = param.Read(in, isLittleEndian, film);
  1478 		if (!in.good()) {
  1479 			LOG( LUX_ERROR,LUX_SYSTEM)<< "Error while receiving film";
  1480 			return false;
  1481 		}
  1482 		if(!ok) {
  1483 			//LOG(LUX_ERROR,LUX_SYSTEM) << "Invalid parameter (id=" << param.id << ", index=" << param.index << ", value=" << param.value << ")";
  1484 			return false;
  1485 		}
  1486 		params.push_back(param);
  1487 	}
  1488 	return true;
  1489 }
  1490 
  1491 void FlmHeader::Write(std::basic_ostream<char> &os, bool isLittleEndian) const
  1492 {
  1493 	// Write magic number and version
  1494 	osWriteLittleEndianInt(isLittleEndian, os, magicNumber);
  1495 	osWriteLittleEndianInt(isLittleEndian, os, versionNumber);
  1496 	// Write buffer resolution
  1497 	osWriteLittleEndianUInt(isLittleEndian, os, xResolution);
  1498 	osWriteLittleEndianUInt(isLittleEndian, os, yResolution);
  1499 	// Write #buffer groups and buffer configs for verification
  1500 	osWriteLittleEndianUInt(isLittleEndian, os, numBufferGroups);
  1501 	osWriteLittleEndianUInt(isLittleEndian, os, numBufferConfigs);
  1502 	for (u_int i = 0; i < numBufferConfigs; ++i)
  1503 		osWriteLittleEndianInt(isLittleEndian, os, bufferTypes[i]);
  1504 	// Write parameters
  1505 	osWriteLittleEndianUInt(isLittleEndian, os, numParams);
  1506 	for(u_int i = 0; i < numParams; ++i) {
  1507 		params[i].Write(os, isLittleEndian);
  1508 	}
  1509 }
  1510 
  1511 double Film::DoTransmitFilm(
  1512 		std::basic_ostream<char> &os,
  1513 		bool clearBuffers,
  1514 		bool transmitParams)
  1515 {
  1516 	const bool isLittleEndian = osIsLittleEndian();
  1517 
  1518 	LOG(LUX_DEBUG,LUX_NOERROR)<< "Transmitting film (little endian=" <<(isLittleEndian ? "true" : "false") << ")";
  1519 
  1520 	// Write the header
  1521 	FlmHeader header;
  1522 	header.magicNumber = FLM_MAGIC_NUMBER;
  1523 	header.versionNumber = FLM_VERSION;
  1524 	header.xResolution = xPixelCount;
  1525 	header.yResolution = yPixelCount;
  1526 	header.numBufferGroups = bufferGroups.size();
  1527 	header.numBufferConfigs = bufferConfigs.size();
  1528 	for (u_int i = 0; i < bufferConfigs.size(); ++i)
  1529 		header.bufferTypes.push_back(bufferConfigs[i].type);
  1530 	// Write parameters
  1531 	if (transmitParams) {
  1532 		header.params.push_back(FlmParameter(this, FLM_PARAMETER_TYPE_FLOAT, LUX_FILM_TM_TONEMAPKERNEL, 0));
  1533 
  1534 		header.params.push_back(FlmParameter(this, FLM_PARAMETER_TYPE_FLOAT, LUX_FILM_TM_REINHARD_PRESCALE, 0));
  1535 		header.params.push_back(FlmParameter(this, FLM_PARAMETER_TYPE_FLOAT, LUX_FILM_TM_REINHARD_POSTSCALE, 0));
  1536 		header.params.push_back(FlmParameter(this, FLM_PARAMETER_TYPE_FLOAT, LUX_FILM_TM_REINHARD_BURN, 0));
  1537 
  1538 		header.params.push_back(FlmParameter(this, FLM_PARAMETER_TYPE_FLOAT, LUX_FILM_TM_LINEAR_SENSITIVITY, 0));
  1539 		header.params.push_back(FlmParameter(this, FLM_PARAMETER_TYPE_FLOAT, LUX_FILM_TM_LINEAR_EXPOSURE, 0));
  1540 		header.params.push_back(FlmParameter(this, FLM_PARAMETER_TYPE_FLOAT, LUX_FILM_TM_LINEAR_FSTOP, 0));
  1541 		header.params.push_back(FlmParameter(this, FLM_PARAMETER_TYPE_FLOAT, LUX_FILM_TM_LINEAR_GAMMA, 0));
  1542 
  1543 		header.params.push_back(FlmParameter(this, FLM_PARAMETER_TYPE_FLOAT, LUX_FILM_TM_CONTRAST_YWA, 0));
  1544 
  1545 		header.params.push_back(FlmParameter(this, FLM_PARAMETER_TYPE_FLOAT, LUX_FILM_LDR_CLAMP_METHOD, 0));		
  1546 
  1547 		header.params.push_back(FlmParameter(this, FLM_PARAMETER_TYPE_FLOAT, LUX_FILM_TORGB_X_WHITE, 0));
  1548 		header.params.push_back(FlmParameter(this, FLM_PARAMETER_TYPE_FLOAT, LUX_FILM_TORGB_Y_WHITE, 0));
  1549 		header.params.push_back(FlmParameter(this, FLM_PARAMETER_TYPE_FLOAT, LUX_FILM_TORGB_X_RED, 0));
  1550 		header.params.push_back(FlmParameter(this, FLM_PARAMETER_TYPE_FLOAT, LUX_FILM_TORGB_Y_RED, 0));
  1551 		header.params.push_back(FlmParameter(this, FLM_PARAMETER_TYPE_FLOAT, LUX_FILM_TORGB_X_GREEN, 0));
  1552 		header.params.push_back(FlmParameter(this, FLM_PARAMETER_TYPE_FLOAT, LUX_FILM_TORGB_Y_GREEN, 0));
  1553 		header.params.push_back(FlmParameter(this, FLM_PARAMETER_TYPE_FLOAT, LUX_FILM_TORGB_X_BLUE, 0));
  1554 		header.params.push_back(FlmParameter(this, FLM_PARAMETER_TYPE_FLOAT, LUX_FILM_TORGB_Y_BLUE, 0));
  1555 		header.params.push_back(FlmParameter(this, FLM_PARAMETER_TYPE_FLOAT, LUX_FILM_TORGB_GAMMA, 0));
  1556 
  1557 		header.params.push_back(FlmParameter(this, FLM_PARAMETER_TYPE_FLOAT, LUX_FILM_CAMERA_RESPONSE_ENABLED, 0));
  1558 		header.params.push_back(FlmParameter(this, FLM_PARAMETER_TYPE_STRING, LUX_FILM_CAMERA_RESPONSE_FILE, 0));
  1559 
  1560 		header.params.push_back(FlmParameter(this, FLM_PARAMETER_TYPE_FLOAT, LUX_FILM_UPDATEBLOOMLAYER, 0));
  1561 		header.params.push_back(FlmParameter(this, FLM_PARAMETER_TYPE_FLOAT, LUX_FILM_DELETEBLOOMLAYER, 0));
  1562 		header.params.push_back(FlmParameter(this, FLM_PARAMETER_TYPE_FLOAT, LUX_FILM_BLOOMRADIUS, 0));
  1563 		header.params.push_back(FlmParameter(this, FLM_PARAMETER_TYPE_FLOAT, LUX_FILM_BLOOMWEIGHT, 0));
  1564 
  1565 		header.params.push_back(FlmParameter(this, FLM_PARAMETER_TYPE_FLOAT, LUX_FILM_VIGNETTING_ENABLED, 0));
  1566 		header.params.push_back(FlmParameter(this, FLM_PARAMETER_TYPE_FLOAT, LUX_FILM_VIGNETTING_SCALE, 0));
  1567 
  1568 		header.params.push_back(FlmParameter(this, FLM_PARAMETER_TYPE_FLOAT, LUX_FILM_ABERRATION_ENABLED, 0));
  1569 		header.params.push_back(FlmParameter(this, FLM_PARAMETER_TYPE_FLOAT, LUX_FILM_ABERRATION_AMOUNT, 0));
  1570 
  1571 		header.params.push_back(FlmParameter(this, FLM_PARAMETER_TYPE_FLOAT, LUX_FILM_UPDATEGLARELAYER, 0));
  1572 		header.params.push_back(FlmParameter(this, FLM_PARAMETER_TYPE_FLOAT, LUX_FILM_DELETEGLARELAYER, 0));
  1573 		header.params.push_back(FlmParameter(this, FLM_PARAMETER_TYPE_FLOAT, LUX_FILM_GLARE_AMOUNT, 0));
  1574 		header.params.push_back(FlmParameter(this, FLM_PARAMETER_TYPE_FLOAT, LUX_FILM_GLARE_RADIUS, 0));
  1575 		header.params.push_back(FlmParameter(this, FLM_PARAMETER_TYPE_FLOAT, LUX_FILM_GLARE_BLADES, 0));
  1576 		header.params.push_back(FlmParameter(this, FLM_PARAMETER_TYPE_FLOAT, LUX_FILM_GLARE_THRESHOLD, 0));
  1577 
  1578 		header.params.push_back(FlmParameter(this, FLM_PARAMETER_TYPE_FLOAT, LUX_FILM_NOISE_CHIU_ENABLED, 0));
  1579 		header.params.push_back(FlmParameter(this, FLM_PARAMETER_TYPE_FLOAT, LUX_FILM_NOISE_CHIU_RADIUS, 0));
  1580 		header.params.push_back(FlmParameter(this, FLM_PARAMETER_TYPE_FLOAT, LUX_FILM_NOISE_CHIU_INCLUDECENTER, 0));
  1581 
  1582 		header.params.push_back(FlmParameter(this, FLM_PARAMETER_TYPE_FLOAT, LUX_FILM_NOISE_GREYC_ENABLED, 0));
  1583 		header.params.push_back(FlmParameter(this, FLM_PARAMETER_TYPE_FLOAT, LUX_FILM_NOISE_GREYC_AMPLITUDE, 0));
  1584 		header.params.push_back(FlmParameter(this, FLM_PARAMETER_TYPE_FLOAT, LUX_FILM_NOISE_GREYC_NBITER, 0));
  1585 		header.params.push_back(FlmParameter(this, FLM_PARAMETER_TYPE_FLOAT, LUX_FILM_NOISE_GREYC_SHARPNESS, 0));
  1586 		header.params.push_back(FlmParameter(this, FLM_PARAMETER_TYPE_FLOAT, LUX_FILM_NOISE_GREYC_ANISOTROPY, 0));
  1587 		header.params.push_back(FlmParameter(this, FLM_PARAMETER_TYPE_FLOAT, LUX_FILM_NOISE_GREYC_ALPHA, 0));
  1588 		header.params.push_back(FlmParameter(this, FLM_PARAMETER_TYPE_FLOAT, LUX_FILM_NOISE_GREYC_SIGMA, 0));
  1589 		header.params.push_back(FlmParameter(this, FLM_PARAMETER_TYPE_FLOAT, LUX_FILM_NOISE_GREYC_FASTAPPROX, 0));
  1590 		header.params.push_back(FlmParameter(this, FLM_PARAMETER_TYPE_FLOAT, LUX_FILM_NOISE_GREYC_GAUSSPREC, 0));
  1591 		header.params.push_back(FlmParameter(this, FLM_PARAMETER_TYPE_FLOAT, LUX_FILM_NOISE_GREYC_DL, 0));
  1592 		header.params.push_back(FlmParameter(this, FLM_PARAMETER_TYPE_FLOAT, LUX_FILM_NOISE_GREYC_DA, 0));
  1593 		header.params.push_back(FlmParameter(this, FLM_PARAMETER_TYPE_FLOAT, LUX_FILM_NOISE_GREYC_INTERP, 0));
  1594 		header.params.push_back(FlmParameter(this, FLM_PARAMETER_TYPE_FLOAT, LUX_FILM_NOISE_GREYC_TILE, 0));
  1595 		header.params.push_back(FlmParameter(this, FLM_PARAMETER_TYPE_FLOAT, LUX_FILM_NOISE_GREYC_BTILE, 0));
  1596 		header.params.push_back(FlmParameter(this, FLM_PARAMETER_TYPE_FLOAT, LUX_FILM_NOISE_GREYC_THREADS, 0));
  1597 
  1598 		for(u_int i = 0; i < GetNumBufferGroups(); ++i) {
  1599 			header.params.push_back(FlmParameter(this, FLM_PARAMETER_TYPE_FLOAT, LUX_FILM_LG_SCALE, i));
  1600 			header.params.push_back(FlmParameter(this, FLM_PARAMETER_TYPE_FLOAT, LUX_FILM_LG_ENABLE, i));
  1601 			header.params.push_back(FlmParameter(this, FLM_PARAMETER_TYPE_FLOAT, LUX_FILM_LG_SCALE_RED, i));
  1602 			header.params.push_back(FlmParameter(this, FLM_PARAMETER_TYPE_FLOAT, LUX_FILM_LG_SCALE_GREEN, i));
  1603 			header.params.push_back(FlmParameter(this, FLM_PARAMETER_TYPE_FLOAT, LUX_FILM_LG_SCALE_BLUE, i));
  1604 			header.params.push_back(FlmParameter(this, FLM_PARAMETER_TYPE_FLOAT, LUX_FILM_LG_TEMPERATURE, i));
  1605 
  1606 			header.params.push_back(FlmParameter(this, FLM_PARAMETER_TYPE_STRING, LUX_FILM_LG_NAME, i));
  1607 		}
  1608 
  1609 		header.numParams = header.params.size();
  1610 	} else {
  1611 		header.numParams = 0;
  1612 	}
  1613 	header.Write(os, isLittleEndian);
  1614 
  1615 	// Write each buffer group
  1616 	double totNumberOfSamples = 0.;
  1617 	for (u_int i = 0; i < bufferGroups.size(); ++i) {
  1618 		BufferGroup& bufferGroup = bufferGroups[i];
  1619 		// Write number of samples
  1620 		osWriteLittleEndianDouble(isLittleEndian, os, bufferGroup.numberOfSamples);
  1621 
  1622 		// Write each buffer
  1623 		for (u_int j = 0; j < bufferConfigs.size(); ++j) {
  1624 			Buffer* buffer = bufferGroup.getBuffer(j);
  1625 
  1626 			// Write pixels
  1627 			const BlockedArray<Pixel>* pixelBuf = buffer->pixels;
  1628 			for (u_int y = 0; y < pixelBuf->vSize(); ++y) {
  1629 				for (u_int x = 0; x < pixelBuf->uSize(); ++x) {
  1630 					const Pixel &pixel = (*pixelBuf)(x, y);
  1631 					osWriteLittleEndianFloat(isLittleEndian, os, pixel.L.c[0]);
  1632 					osWriteLittleEndianFloat(isLittleEndian, os, pixel.L.c[1]);
  1633 					osWriteLittleEndianFloat(isLittleEndian, os, pixel.L.c[2]);
  1634 					osWriteLittleEndianFloat(isLittleEndian, os, pixel.alpha);
  1635 					osWriteLittleEndianFloat(isLittleEndian, os, pixel.weightSum);
  1636 				}
  1637 				if (!os.good())
  1638 					// error during transmission, abort
  1639 					return 0;
  1640 			}
  1641 		}
  1642 
  1643 		totNumberOfSamples += bufferGroup.numberOfSamples;
  1644 		LOG(LUX_DEBUG,LUX_NOERROR) << "Transmitted " << bufferGroup.numberOfSamples << " samples for buffer group " << i <<
  1645 			" (buffer config size: " << bufferConfigs.size() << ")";
  1646 	}
  1647 
  1648 	// transmitted everything, now we can clear buffers if needed
  1649 	if (clearBuffers) {
  1650 		for (u_int i = 0; i < bufferGroups.size(); ++i) {
  1651 
  1652 			BufferGroup& bufferGroup = bufferGroups[i];
  1653 
  1654 			for (u_int j = 0; j < bufferConfigs.size(); ++j) {
  1655 				Buffer* buffer = bufferGroup.getBuffer(j);
  1656 
  1657 				// Dade - reset the rendering buffer
  1658 				buffer->Clear();
  1659 			}
  1660 
  1661 			// Dade - reset the rendering buffer
  1662 			bufferGroup.numberOfSamples = 0;
  1663 		}
  1664 	}
  1665 
  1666 	return totNumberOfSamples;
  1667 
  1668 }
  1669 
  1670 bool Film::TransmitFilm(
  1671         std::basic_ostream<char> &stream,
  1672         bool clearBuffers,
  1673 		bool transmitParams,
  1674 		bool useCompression, 
  1675 		bool directWrite)
  1676 {
  1677 	std::streamsize size;
  1678 
  1679 	double totNumberOfSamples = 0;
  1680 
  1681 	bool transmitError = false;
  1682 
  1683 	if (!directWrite) {
  1684 		//std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary);
  1685 		multibuffer_device mbdev;
  1686 		boost::iostreams::stream<multibuffer_device> ms(mbdev);
  1687 
  1688 		totNumberOfSamples = DoTransmitFilm(ms, clearBuffers, transmitParams);
  1689 
  1690 		transmitError = !ms.good();
  1691 		
  1692 		ms.seekg(0, BOOST_IOS::beg);
  1693 
  1694 		if (!transmitError) {
  1695 			if (useCompression) {
  1696 				filtering_streambuf<input> in;
  1697 				in.push(gzip_compressor(4));
  1698 				in.push(ms);
  1699 				size = boost::iostreams::copy(in, stream);
  1700 			} else {
  1701 				size = boost::iostreams::copy(ms, stream);
  1702 			}
  1703 			// ignore how the copy to stream goes for now, as
  1704 			// direct writing won't help with that
  1705 		} else {
  1706 			LOG(LUX_SEVERE,LUX_SYSTEM) << "Error while preparing film data for transmission, retrying without buffering.";
  1707 		}
  1708 	}
  1709 
  1710 	// if the memory buffered method fails it's most likely due
  1711 	// to low memory conditions, so fall back to direct writing
  1712 	if (directWrite || transmitError) {
  1713 		std::streampos stream_startpos = stream.tellp();
  1714 		if (useCompression) {
  1715 			filtering_stream<output> fs;
  1716 			fs.push(gzip_compressor(4));
  1717 			fs.push(stream);
  1718 			totNumberOfSamples = DoTransmitFilm(fs, clearBuffers, transmitParams);
  1719 
  1720 			flush(fs);
  1721 
  1722 			transmitError = !fs.good();
  1723 		} else {
  1724 			totNumberOfSamples = DoTransmitFilm(stream, clearBuffers, transmitParams);
  1725 			transmitError = !stream.good();
  1726 		}
  1727 		size = stream.tellp() - stream_startpos;
  1728 	}
  1729 	
  1730 	if (transmitError || !stream.good()) {
  1731 		LOG(LUX_SEVERE,LUX_SYSTEM) << "Error while transmitting film";
  1732 		return false;
  1733 	} else
  1734 		LOG(LUX_DEBUG,LUX_NOERROR) << "Transmitted a film with " << totNumberOfSamples << " samples";
  1735 
  1736 	LOG(LUX_INFO,LUX_NOERROR) << "Film transmission done (" << (size / 1024) << " Kbytes sent)";
  1737 	return true;
  1738 }
  1739 
  1740 
  1741 double Film::UpdateFilm(std::basic_istream<char> &stream) {
  1742 	const bool isLittleEndian = osIsLittleEndian();
  1743 
  1744 	filtering_stream<input> in;
  1745 	in.push(gzip_decompressor());
  1746 	in.push(stream);
  1747 
  1748 	LOG(LUX_DEBUG,LUX_NOERROR) << "Receiving film (little endian=" << (isLittleEndian ? "true" : "false") << ")";
  1749 
  1750 	// Read header
  1751 	FlmHeader header;
  1752 	if (!header.Read(in, isLittleEndian, this))
  1753 		return 0.f;
  1754 
  1755 	// Read buffer groups
  1756 	vector<double> bufferGroupNumSamples(bufferGroups.size());
  1757 	vector<BlockedArray<Pixel>*> tmpPixelArrays(bufferGroups.size() * bufferConfigs.size());
  1758 	for (u_int i = 0; i < bufferGroups.size(); i++) {
  1759 		double numberOfSamples;
  1760 		numberOfSamples = osReadLittleEndianDouble(isLittleEndian, in);
  1761 		if (!in.good())
  1762 			break;
  1763 		bufferGroupNumSamples[i] = numberOfSamples;
  1764 
  1765 		// Read buffers
  1766 		for(u_int j = 0; j < bufferConfigs.size(); ++j) {
  1767 			const Buffer* localBuffer = bufferGroups[i].getBuffer(j);
  1768 			// Read pixels
  1769 			BlockedArray<Pixel> *tmpPixelArr = new BlockedArray<Pixel>(
  1770 				localBuffer->xPixelCount, localBuffer->yPixelCount);
  1771 			tmpPixelArrays[i*bufferConfigs.size() + j] = tmpPixelArr;
  1772 			for (u_int y = 0; y < tmpPixelArr->vSize(); ++y) {
  1773 				for (u_int x = 0; x < tmpPixelArr->uSize(); ++x) {
  1774 					Pixel &pixel = (*tmpPixelArr)(x, y);
  1775 					pixel.L.c[0] = osReadLittleEndianFloat(isLittleEndian, in);
  1776 					pixel.L.c[1] = osReadLittleEndianFloat(isLittleEndian, in);
  1777 					pixel.L.c[2] = osReadLittleEndianFloat(isLittleEndian, in);
  1778 					pixel.alpha = osReadLittleEndianFloat(isLittleEndian, in);
  1779 					pixel.weightSum = osReadLittleEndianFloat(isLittleEndian, in);
  1780 				}
  1781 			}
  1782 			if (!in.good())
  1783 				break;
  1784 		}
  1785 		if (!in.good())
  1786 			break;
  1787 
  1788 		LOG( LUX_DEBUG,LUX_NOERROR)
  1789 			<< "Received " << bufferGroupNumSamples[i] << " samples for buffer group " << i
  1790 			<< " (buffer config size: " << bufferConfigs.size() << ")";
  1791 	}
  1792 
  1793 	// Dade - check for errors
  1794 	double totNumberOfSamples = 0.;
  1795 	double maxTotNumberOfSamples = 0.;
  1796 	if (in.good()) {
  1797 		// Update parameters
  1798 		for (vector<FlmParameter>::iterator it = header.params.begin(); it != header.params.end(); ++it)
  1799 			it->Set(this);
  1800 
  1801 		// Dade - add all received data
  1802 		for (u_int i = 0; i < bufferGroups.size(); ++i) {
  1803 			BufferGroup &currentGroup = bufferGroups[i];
  1804 			for (u_int j = 0; j < bufferConfigs.size(); ++j) {
  1805 				const BlockedArray<Pixel> *receivedPixels = tmpPixelArrays[ i * bufferConfigs.size() + j ];
  1806 				Buffer *buffer = currentGroup.getBuffer(j);
  1807 
  1808 				for (u_int y = 0; y < buffer->yPixelCount; ++y) {
  1809 					for (u_int x = 0; x < buffer->xPixelCount; ++x) {
  1810 						const Pixel &pixel = (*receivedPixels)(x, y);
  1811 						Pixel &pixelResult = (*buffer->pixels)(x, y);
  1812 						pixelResult.L.c[0] += pixel.L.c[0];
  1813 						pixelResult.L.c[1] += pixel.L.c[1];
  1814 						pixelResult.L.c[2] += pixel.L.c[2];
  1815 						pixelResult.alpha += pixel.alpha;
  1816 						pixelResult.weightSum += pixel.weightSum;
  1817 					}
  1818 				}
  1819 			}
  1820 
  1821 			currentGroup.numberOfSamples += bufferGroupNumSamples[i];
  1822 			// Check if we have enough samples per pixel
  1823 			if ((haltSamplesPerPixel > 0) &&
  1824 				(currentGroup.numberOfSamples >= haltSamplesPerPixel * samplePerPass))
  1825 				enoughSamplesPerPixel = true;
  1826 			totNumberOfSamples += bufferGroupNumSamples[i];
  1827 			maxTotNumberOfSamples = max(maxTotNumberOfSamples, bufferGroupNumSamples[i]);
  1828 		}
  1829 
  1830 		LOG( LUX_DEBUG,LUX_NOERROR) << "Received film with " << totNumberOfSamples << " samples";
  1831 	} else
  1832 		LOG( LUX_ERROR,LUX_SYSTEM)<< "IO error while receiving film buffers";
  1833 
  1834 	// Clean up
  1835 	for (u_int i = 0; i < tmpPixelArrays.size(); ++i)
  1836 		delete tmpPixelArrays[i];
  1837 
  1838 	return maxTotNumberOfSamples;
  1839 }
  1840 
  1841 bool Film::LoadResumeFilm(const string &filename)
  1842 {
  1843 	// Read the FLM header
  1844 	std::ifstream stream(filename.c_str(), std::ios_base::in | std::ios_base::binary);
  1845 	filtering_stream<input> in;
  1846 	in.push(gzip_decompressor());
  1847 	in.push(stream);
  1848 	const bool isLittleEndian = osIsLittleEndian();
  1849 	FlmHeader header;
  1850 	bool headerOk = header.Read(in, isLittleEndian, NULL);
  1851 	stream.close();
  1852 	if (!headerOk)
  1853 		return false;
  1854 	xResolution = static_cast<int>(header.xResolution);
  1855 	yResolution = static_cast<int>(header.yResolution);
  1856 	xPixelStart = 0; // by default use full resolution
  1857 	yPixelStart = 0; 
  1858 	xPixelCount = xResolution;
  1859 	yPixelCount = yResolution;
  1860 
  1861 	// Create the buffers (also loads the FLM file)
  1862 	for (u_int i = 0; i < header.numBufferConfigs; ++i)
  1863 		RequestBuffer(BufferType(header.bufferTypes[i]), BUF_FRAMEBUFFER, "");
  1864 
  1865 	vector<string> bufferGroups;
  1866 	for (u_int i = 0; i < header.numBufferGroups; ++i) {
  1867 		std::stringstream ss;
  1868 		ss << "lightgroup #" << (i + 1);
  1869 		bufferGroups.push_back(ss.str());
  1870 	}
  1871 	RequestBufferGroups(bufferGroups);
  1872 	CreateBuffers();
  1873 
  1874 	return true;
  1875 }
  1876 
  1877 
  1878 void Film::getHistogramImage(unsigned char *outPixels, u_int width, u_int height, int options)
  1879 {
  1880     boost::mutex::scoped_lock lock(histMutex);
  1881 	if (!histogram)
  1882 		histogram = new Histogram();
  1883 	histogram->MakeImage(outPixels, width, height, options);
  1884 }
  1885 
  1886 // Histogram Function Definitions
  1887 
  1888 Histogram::Histogram()
  1889 {
  1890 	m_buckets = NULL;
  1891 	m_displayGamma = 2.2f; //gamma of the display the histogram is viewed on
  1892 
  1893 	//calculate visible plot range
  1894 	float narrowRangeSize = -logf(powf(.5f, 10.f / m_displayGamma)); //size of 10 EV zones, display-gamma corrected
  1895 	float scalingFactor = 0.75f;
  1896 	m_lowRange = -(1.f + scalingFactor) * narrowRangeSize;
  1897 	m_highRange = scalingFactor * narrowRangeSize;
  1898 
  1899 	m_bucketNr = 0;
  1900 	m_newBucketNr = 300;
  1901 	CheckBucketNr();
  1902 	for (u_int i = 0; i < m_bucketNr * 4; ++i)
  1903 		m_buckets[i] = 0;
  1904 }
  1905 
  1906 Histogram::~Histogram()
  1907 {
  1908 	delete[] m_buckets;
  1909 }
  1910 
  1911 void Histogram::CheckBucketNr()
  1912 {
  1913 	if (m_newBucketNr > 0) { //if nr of buckets changed recalculate data that depends on it
  1914 		m_bucketNr = m_newBucketNr;
  1915 		m_newBucketNr = 0;
  1916 		delete[] m_buckets;
  1917 		m_buckets = new float[m_bucketNr * 4];
  1918 
  1919 		//new bucket size
  1920 		m_bucketSize = (m_highRange - m_lowRange) / m_bucketNr;
  1921 
  1922 		//calculate EV zone tick positions
  1923 		float zoneValue = 1.f;
  1924 		for (u_int i = 0; i < 11; ++i) {
  1925 			float value = logf(powf(zoneValue, 1.f / m_displayGamma));
  1926 			u_int bucket = Clamp(Round2UInt((value - m_lowRange) / m_bucketSize), 0U, m_bucketNr - 1);
  1927 			m_zones[i] = bucket;
  1928 			zoneValue /= 2;
  1929 		}
  1930 	}
  1931 }
  1932 
  1933 void Histogram::Calculate(vector<RGBColor> &pixels, u_int width, u_int height)
  1934 {
  1935 	boost::mutex::scoped_lock lock(this->m_mutex);
  1936 	if (pixels.empty() || width == 0 || height == 0)
  1937 		return;
  1938 	u_int pixelNr = width * height;
  1939 	float value;
  1940 
  1941 	CheckBucketNr();
  1942 
  1943 	//empty buckets
  1944 	for (u_int i = 0; i < m_bucketNr * 4; ++i)
  1945 		m_buckets[i] = 0;
  1946 
  1947 	//fill buckets
  1948 	for (u_int i = 0; i < pixelNr; ++i) {
  1949 		for (u_int j = 0; j < 3; ++j){ //each color channel
  1950 			value = pixels[i].c[j];
  1951 			if (value > 0.f) {
  1952 				value = logf(value);
  1953 				u_int bucket = Clamp(Round2UInt((value - m_lowRange) / m_bucketSize), 0U, m_bucketNr - 1);
  1954 				m_buckets[bucket * 4 + j] += 1.f;
  1955 			}
  1956 		}
  1957 		value = pixels[i].Y(); //brightness
  1958 		if (value > 0.f) {
  1959 			value = logf(value);
  1960 			u_int bucket = Clamp(Round2UInt((value - m_lowRange) / m_bucketSize), 0U, m_bucketNr - 1);
  1961 			m_buckets[bucket * 4 + 3] += 1.f;
  1962 		}
  1963 	}
  1964 }
  1965 
  1966 void Histogram::MakeImage(unsigned char *outPixels, u_int canvasW, u_int canvasH, int options){
  1967     boost::mutex::scoped_lock lock(this->m_mutex);
  1968 	#define PIXELIDX(x,y,w) ((y)*(w)*3+(x)*3)
  1969 	#define GETMAX(x,y) ((x)>(y)?(x):(y))
  1970 	#define OPTIONS_CHANNELS_MASK (LUX_HISTOGRAM_LOG-1)
  1971 	if (canvasW < 50 || canvasH < 25)
  1972 		return; //too small
  1973 	const u_int borderW = 3; //size of the plot border in pixels
  1974 	const u_int guideW = 3; //size of the brightness guide bar in pixels
  1975 	const u_int plotH = canvasH - borderW - (guideW + 2) - (borderW - 1);
  1976 	const u_int plotW = canvasW - 2 * borderW;
  1977    
  1978 	if (canvasW - 2 * borderW != m_bucketNr)
  1979 		m_newBucketNr = canvasW - 2 * borderW;
  1980 
  1981 	//clear drawing area
  1982 	unsigned char color = 64;
  1983 	for (u_int x = 0; x < plotW; ++x) {
  1984 		for (u_int y = 0; y < plotH; ++y) {
  1985 			const u_int idx = PIXELIDX(x + borderW, y + borderW, canvasW);
  1986 			outPixels[idx] = color;
  1987 			outPixels[idx + 1] = color;
  1988 			outPixels[idx + 2] = color;
  1989 		}
  1990 	}
  1991 
  1992 	//transform values to log if needed
  1993 	float *buckets;
  1994 	if (options & LUX_HISTOGRAM_LOG) {
  1995 		buckets = new float[m_bucketNr * 4];
  1996 		for (u_int i = 0; i < m_bucketNr * 4; ++i)
  1997 			buckets[i] = logf(1.f + m_buckets[i]);
  1998 	} else
  1999 		buckets = m_buckets;
  2000 
  2001 	//draw histogram bars
  2002 	u_int channel = 0;
  2003 	switch (options & OPTIONS_CHANNELS_MASK) {
  2004 		case LUX_HISTOGRAM_RGB: {
  2005 			//get maxima for scaling
  2006 			float max = 0.f;
  2007 			for (u_int i = 0; i < m_bucketNr * 4; ++i) {
  2008 				if (i % 4 != 3 && buckets[i] > max)
  2009 					max = buckets[i];
  2010 			}
  2011 			if (max > 0.f) {
  2012 				//draw bars
  2013 				for (u_int x = 0; x < plotW; ++x) {
  2014 					const u_int bucket = Clamp(x * m_bucketNr / (plotW - 1), 0U, m_bucketNr - 1);
  2015 					const float scale = plotH / max;
  2016 					for (u_int ch = 0; ch < 3; ++ch) {
  2017 						const u_int barHeight = Clamp(plotH - Round2UInt(buckets[bucket * 4 + ch] * scale), 0U, plotH);
  2018 						for(u_int y = barHeight; y < plotH; ++y)
  2019 							outPixels[PIXELIDX(x + borderW, y + borderW, canvasW) + ch] = 255;
  2020 					}
  2021 				}
  2022 			}
  2023 		} break;
  2024 		case LUX_HISTOGRAM_VALUE: channel++;
  2025 		case LUX_HISTOGRAM_BLUE:  channel++;
  2026 		case LUX_HISTOGRAM_GREEN: channel++;
  2027 		case LUX_HISTOGRAM_RED: {
  2028 			//get maxima for scaling
  2029 			float max = 0.f;
  2030 			for (u_int i = 0; i < m_bucketNr; ++i) {
  2031 				if (buckets[i * 4 + channel] > max)
  2032 					max = buckets[i * 4 + channel];
  2033 			}
  2034 			if (max > 0.f) {
  2035 				//draw bars
  2036 				for (u_int x = 0; x < plotW; ++x) {
  2037 					const u_int bucket = Clamp(x * m_bucketNr / (plotW - 1), 0U, m_bucketNr - 1);
  2038 					const float scale = plotH / max;
  2039 					const u_int barHeight = Clamp(plotH - Round2UInt(buckets[bucket * 4 + channel] * scale), 0U, plotH);
  2040 					for(u_int y = barHeight; y < plotH; ++y) {
  2041 						const u_int idx = PIXELIDX(x + borderW, y + borderW, canvasW);
  2042 						outPixels[idx] = 255;
  2043 						outPixels[idx + 1] = 255;
  2044 						outPixels[idx + 2] = 255;
  2045 					}
  2046 				}
  2047 			}
  2048 			break;
  2049 		}
  2050 		case LUX_HISTOGRAM_RGB_ADD: {
  2051 			//calculate maxima for scaling
  2052 			float max = 0.f;
  2053 			for (u_int i = 0; i < m_bucketNr; ++i) {
  2054 				const float val = buckets[i * 4] + buckets[i * 4 + 1] + buckets[i * 4 + 2];
  2055 				if (val > max)
  2056 					max = val;
  2057 			}
  2058 			if (max > 0.f) {
  2059 				//draw bars
  2060 				for (u_int x = 0; x < plotW; ++x) {
  2061 					const u_int bucket = Clamp(x * m_bucketNr / (plotW - 1), 0U, m_bucketNr - 1);
  2062 					const float scale = plotH / max;
  2063 					u_int barHeight = Clamp(plotH - Round2UInt((buckets[bucket * 4] + buckets[bucket * 4 + 1] + buckets[bucket * 4 + 2]) * scale), 0U, plotH);
  2064 					u_int newHeight = barHeight;
  2065 					for (u_int ch = 0; ch < 3; ++ch) {
  2066 						newHeight += Floor2UInt(buckets[bucket * 4 + ch] * scale + 0.5f);
  2067 						for (u_int y = barHeight; y < newHeight; ++y)
  2068 							outPixels[PIXELIDX(x + borderW, y + borderW, canvasW) + ch] = 255;
  2069 						barHeight = newHeight;
  2070 					}
  2071 				}
  2072 			}
  2073 			break;
  2074 		}
  2075 		default:
  2076 			break;
  2077 	}
  2078 
  2079 	if (buckets != m_buckets)
  2080 		delete[] buckets;
  2081 
  2082 	//draw brightness guide
  2083 	for (u_int x = 0; x < plotW; ++x) {
  2084 		u_int bucket = Clamp(x * m_bucketNr / (plotW - 1), 0U, m_bucketNr - 1);
  2085 		for (u_int y = plotH + 2; y < plotH + 2 + guideW; ++y) {
  2086 			const u_int idx = PIXELIDX(x + borderW, y + borderW, canvasW);
  2087 			const unsigned char color = static_cast<unsigned char>(Clamp(expf(bucket * m_bucketSize + m_lowRange) * 256.f, 0.f, 255.f)); //no need to gamma-correct, as we're already in display-gamma space
  2088 			switch (options & OPTIONS_CHANNELS_MASK) {
  2089 				case LUX_HISTOGRAM_RED:
  2090 					outPixels[idx] = color;
  2091 					outPixels[idx + 1] = 0;
  2092 					outPixels[idx + 2] = 0;
  2093 					break;
  2094 				case LUX_HISTOGRAM_GREEN:
  2095 					outPixels[idx] = 0;
  2096 					outPixels[idx + 1] = color;
  2097 					outPixels[idx + 2] = 0;
  2098 					break;
  2099 				case LUX_HISTOGRAM_BLUE:
  2100 					outPixels[idx] = 0;
  2101 					outPixels[idx + 1] = 0;
  2102 					outPixels[idx + 2] = color;
  2103 					break;
  2104 				default:
  2105 					outPixels[idx] = color;
  2106 					outPixels[idx + 1] = color;
  2107 					outPixels[idx + 2] = color;
  2108 					break;
  2109 			}
  2110 		}
  2111 	}
  2112 
  2113 	//draw EV zone markers
  2114 	for (u_int i = 0; i < 11; ++i) {
  2115 		switch (i) {
  2116 			case 0:
  2117 				color = 128;
  2118 				break;
  2119 			case 10:
  2120 				color = 128;
  2121 				break;
  2122 			default:
  2123 				color = 0;
  2124 				break;
  2125 		}
  2126 		const u_int bucket = m_zones[i];
  2127 		const u_int x = Clamp(bucket * plotW / (m_bucketNr - 1), 0U, plotW - 1);
  2128 		for (u_int y = plotH + 2; y < plotH + 2 + guideW; ++y) {
  2129 			const u_int idx = PIXELIDX(x + borderW, y + borderW, canvasW);
  2130 			outPixels[idx] = color;
  2131 			outPixels[idx + 1] = color;
  2132 			outPixels[idx + 2] = color;
  2133 		}
  2134 	}
  2135 
  2136 	//draw zone boundaries on the plot
  2137 	for (u_int i = 0; i < 2; ++i) {
  2138 		u_int bucket;
  2139 		switch (i) {
  2140 			case 0:
  2141 				bucket = m_zones[0];
  2142 				break;  //white
  2143 			default: // In order to suppress a GCC warning
  2144 			case 1:
  2145 				bucket = m_zones[10];
  2146 				break; //black
  2147 		}
  2148 		const u_int x = Clamp(bucket * plotW / (m_bucketNr - 1), 0U, plotW - 1);
  2149 		for (u_int y = 0; y < plotH; ++y) {
  2150 			const u_int idx = PIXELIDX(x + borderW, y + borderW, canvasW);
  2151 			if (outPixels[idx] == 255 &&
  2152 				outPixels[idx + 1] == 255 &&
  2153 				outPixels[idx + 2] == 255) {
  2154 				outPixels[idx] = 128;
  2155 				outPixels[idx + 1] = 128;
  2156 				outPixels[idx + 2] = 128;
  2157 			} else {
  2158 				if (outPixels[idx] == 64)
  2159 					outPixels[idx] = 128;
  2160 				if (outPixels[idx + 1] == 64)
  2161 					outPixels[idx + 1] = 128;
  2162 				if (outPixels[idx + 2] == 64)
  2163 					outPixels[idx + 2] = 128;
  2164 			}
  2165 		}
  2166 	}
  2167 }
  2168 
  2169 } // namespace lux