/* * pbrt source code Copyright(c) 1998-2004 Matt Pharr and Greg Humphreys * * All Rights Reserved. * For educational use only; commercial use expressly forbidden. * NO WARRANTY, express or implied, for this software. * (See file License.txt for complete license) */ // image.cpp* #include "pbrt.h" #include "film.h" #include "color.h" #include "paramset.h" #include "tonemap.h" #include "sampling.h" #include // TgaFilm Declarations class TgaFilm : public Film { public: // TgaFilm Public Methods TgaFilm::TgaFilm(int xres, int yres, Filter *filt, const float crop[4], const string &filename, bool premult, int wf, const string &toneMapper, float bloomWidth, float bloomRadius, float gamma, float dither); ~TgaFilm() { delete pixels; delete filter; delete[] filterTable; } void AddSample(const Sample &sample, const Ray &ray, const Spectrum &L, float alpha); void GetSampleExtent(int *xstart, int *xend, int *ystart, int *yend) const; void WriteImage(); private: // TgaFilm Private Data Filter *filter; int writeFrequency, sampleCount; string filename; bool premultiplyAlpha; float cropWindow[4]; int xPixelStart, yPixelStart, xPixelCount, yPixelCount; string toneMapper; float bloomWidth, bloomRadius, gamma, dither; struct Pixel { Pixel() : L(0.f) { alpha = 0.f; weightSum = 0.f; } Spectrum L; float alpha, weightSum; }; BlockedArray *pixels; float *filterTable; }; // TgaFilm Method Definitions TgaFilm::TgaFilm(int xres, int yres, Filter *filt, const float crop[4], const string &fn, bool premult, int wf, const string &tm, float bw, float br, float g, float d) : Film(xres, yres) { filter = filt; memcpy(cropWindow, crop, 4 * sizeof(float)); filename = fn; premultiplyAlpha = premult; writeFrequency = sampleCount = wf; // Compute film image extent xPixelStart = Ceil2Int(xResolution * cropWindow[0]); xPixelCount = Ceil2Int(xResolution * cropWindow[1]) - xPixelStart; yPixelStart = Ceil2Int(yResolution * cropWindow[2]); yPixelCount = Ceil2Int(yResolution * cropWindow[3]) - yPixelStart; // Allocate film image storage pixels = new BlockedArray(xPixelCount, yPixelCount); // Precompute filter weight table #define FILTER_TABLE_SIZE 16 filterTable = new float[FILTER_TABLE_SIZE * FILTER_TABLE_SIZE]; float *ftp = filterTable; for (int y = 0; y < FILTER_TABLE_SIZE; ++y) { float fy = ((float)y + .5f) * filter->yWidth / FILTER_TABLE_SIZE; for (int x = 0; x < FILTER_TABLE_SIZE; ++x) { float fx = ((float)x + .5f) * filter->xWidth / FILTER_TABLE_SIZE; *ftp++ = filter->Evaluate(fx, fy); } } toneMapper = tm; bloomWidth = bw; bloomRadius = br; gamma = g; dither = d; } void TgaFilm::AddSample(const Sample &sample, const Ray &ray, const Spectrum &L, float alpha) { // Compute sample's raster extent float dImageX = sample.imageX - 0.5f; float dImageY = sample.imageY - 0.5f; int x0 = Ceil2Int (dImageX - filter->xWidth); int x1 = Floor2Int(dImageX + filter->xWidth); int y0 = Ceil2Int (dImageY - filter->yWidth); int y1 = Floor2Int(dImageY + filter->yWidth); x0 = max(x0, xPixelStart); x1 = min(x1, xPixelStart + xPixelCount - 1); y0 = max(y0, yPixelStart); y1 = min(y1, yPixelStart + yPixelCount - 1); // Loop over filter support and add sample to pixel arrays // Precompute $x$ and $y$ filter table offsets int *ifx = (int *)alloca((x1-x0+1) * sizeof(int)); for (int x = x0; x <= x1; ++x) { float fx = fabsf((x - dImageX) * filter->invXWidth * FILTER_TABLE_SIZE); ifx[x-x0] = min(Floor2Int(fx), FILTER_TABLE_SIZE-1); } int *ify = (int *)alloca((y1-y0+1) * sizeof(int)); for (int y = y0; y <= y1; ++y) { float fy = fabsf((y - dImageY) * filter->invYWidth * FILTER_TABLE_SIZE); ify[y-y0] = min(Floor2Int(fy), FILTER_TABLE_SIZE-1); } for (int y = y0; y <= y1; ++y) for (int x = x0; x <= x1; ++x) { // Evaluate filter value at $(x,y)$ pixel int offset = ify[y-y0]*FILTER_TABLE_SIZE + ifx[x-x0]; float filterWt = filterTable[offset]; // Update pixel values with filtered sample contribution Pixel &pixel = (*pixels)(x - xPixelStart, y - yPixelStart); pixel.L.AddWeighted(filterWt, L); pixel.alpha += alpha * filterWt; pixel.weightSum += filterWt; } // Possibly write out in-progress image if (--sampleCount == 0) { WriteImage(); sampleCount = writeFrequency; } } void TgaFilm::GetSampleExtent(int *xstart, int *xend, int *ystart, int *yend) const { *xstart = Floor2Int(xPixelStart + .5f - filter->xWidth); *xend = Floor2Int(xPixelStart + .5f + xPixelCount + filter->xWidth); *ystart = Floor2Int(yPixelStart + .5f - filter->yWidth); *yend = Floor2Int(yPixelStart + .5f + yPixelCount + filter->yWidth); } void TgaFilm::WriteImage() { // Convert image to RGB and compute final pixel values int nPix = xPixelCount * yPixelCount; float *rgb = new float[3*nPix], *alpha = new float[nPix]; int offset = 0; for (int y = 0; y < yPixelCount; ++y) { for (int x = 0; x < xPixelCount; ++x) { // Convert pixel spectral radiance to RGB float xyz[3]; (*pixels)(x, y).L.XYZ(xyz); const float rWeight[3] = { 3.240479f, -1.537150f, -0.498535f }; const float gWeight[3] = {-0.969256f, 1.875991f, 0.041556f }; const float bWeight[3] = { 0.055648f, -0.204043f, 1.057311f }; rgb[3*offset ] = rWeight[0]*xyz[0] + rWeight[1]*xyz[1] + rWeight[2]*xyz[2]; rgb[3*offset+1] = gWeight[0]*xyz[0] + gWeight[1]*xyz[1] + gWeight[2]*xyz[2]; rgb[3*offset+2] = bWeight[0]*xyz[0] + bWeight[1]*xyz[1] + bWeight[2]*xyz[2]; alpha[offset] = (*pixels)(x, y).alpha; // Normalize pixel with weight sum float weightSum = (*pixels)(x, y).weightSum; if (weightSum != 0.f) { float invWt = 1.f / weightSum; rgb[3*offset ] = Clamp(rgb[3*offset ] * invWt, 0.f, INFINITY); rgb[3*offset+1] = Clamp(rgb[3*offset+1] * invWt, 0.f, INFINITY); rgb[3*offset+2] = Clamp(rgb[3*offset+2] * invWt, 0.f, INFINITY); alpha[offset] = Clamp(alpha[offset] * invWt, 0.f, 1.f); } // Compute premultiplied alpha color if (premultiplyAlpha) { rgb[3*offset ] *= alpha[offset]; rgb[3*offset+1] *= alpha[offset]; rgb[3*offset+2] *= alpha[offset]; } ++offset; } } //// CHANGES START HERE! //// // apply the tone mapper and such ParamSet toneParams; ApplyImagingPipeline(rgb,xPixelCount,yPixelCount,NULL,bloomRadius,bloomWidth,toneMapper.c_str(),&toneParams,gamma,dither,255); // All following code deals with the particulars of creating a targa file // cross platform // open the targa file for output FILE* tgaFile = fopen(filename.c_str(),"wb"); if (!tgaFile) { std::cout << "Error: Cannot open file for output" << std::endl; delete[] alpha; delete[] rgb; return; } // write the header // make sure its platform independent of little endian and big endian char header[18]; memset(header, 0,sizeof(char)*18); header[2] = 2; // set the data type of the targa (2 = uncompressed) short xResShort = xResolution; // set the resolution and make sure the bytes are in the right order header[13] = (char) (xResShort >> 8); header[12] = xResShort; short yResShort = yResolution; header[15] = (char) (yResShort >> 8); header[14] = yResShort; header[16] = 32; // set the bitdepth // put the header data into the file for (int i=0; i < 18; i++) fputc(header[i],tgaFile); // write the bytes of data out for (int i=yPixelCount-1; i >= 0 ; i--) { for (int j=0; j < xPixelCount; j++) { fputc((int) rgb[(i*xPixelCount+j)*3+2], tgaFile); fputc((int) rgb[(i*xPixelCount+j)*3+1], tgaFile); fputc((int) rgb[(i*xPixelCount+j)*3+0], tgaFile); fputc((int) (255.0*alpha[(i*xPixelCount+j)]), tgaFile); } } fclose(tgaFile); delete[] alpha; delete[] rgb; } extern "C" DLLEXPORT Film *CreateFilm(const ParamSet ¶ms, Filter *filter) { string filename = params.FindOneString("filename", "pbrt.tga"); bool premultiplyAlpha = params.FindOneBool("premultiplyalpha", true); int xres = params.FindOneInt("xresolution", 640); int yres = params.FindOneInt("yresolution", 480); float crop[4] = { 0, 1, 0, 1 }; int cwi; const float *cr = params.FindFloat("cropwindow", &cwi); if (cr && cwi == 4) { crop[0] = Clamp(min(cr[0], cr[1]), 0., 1.); crop[1] = Clamp(max(cr[0], cr[1]), 0., 1.); crop[2] = Clamp(min(cr[2], cr[3]), 0., 1.); crop[3] = Clamp(max(cr[2], cr[3]), 0., 1.); } int writeFrequency = params.FindOneInt("writefrequency", -1); // new variables string toneMapper = params.FindOneString("tonemapper", "maxwhite"); float bloomWidth = params.FindOneFloat("bloomwidth", 0.0f); float bloomRadius = params.FindOneFloat("bloomradius", 0.0f); float gamma = params.FindOneFloat("gamma", 1.0f); float dither = params.FindOneFloat("dither", 0.0f); return new TgaFilm(xres, yres, filter, crop, filename, premultiplyAlpha, writeFrequency, toneMapper, bloomWidth, bloomRadius, gamma, dither); }