Skip to content
Snippets Groups Projects
Image.cpp 4.76 KiB
#include "Image.h"

#include <fstream>
#include <iostream>

#include "../tools/Threadpool.h"
#include "StratifiedSampler.h"

namespace util {
Image::Image(int width, int height) : width(width), height(height) {
	Vec3 color({});
	for (int i = 0; i < width * height; i++) {
		vec.insert(vec.end(), color);
	}
}
/*
void Image::setPixels(std::shared_ptr<Sampler> sampler) {
    Threadpool tp(4);
    for (int x = 0; x != width; x++) {
        for (int y = 0; y != height; y++) {
            vec[width * y + x] = sampler->color(x, y);
        }
    }
} */
void Image::setPixel(int x, int y, Vec3 color) {
	vec[width * y + x] = color;
}
void Image::setPixels(size_t threadcount, std::shared_ptr<Sampler> sampler) {
	Threadpool tp(threadcount);
	for (int x = 0; x != width; x++) {
		for (int y = 0; y != height; y++) {
			tp.queueTask(std::bind([this, x, y, sampler]() {
				this->setPixelsTask(x, y, sampler);
			}));
		}
	}
	// std::cout << "Done queing" << std::endl;
}

void Image::setPixelsTask(int x, int y, std::shared_ptr<Sampler> sampler) {
	Vec3 v = sampler->color(x, y);
	setPixel(x, y, v);
}

void Image::gammaCorrect(float gamma) {
	// correct the whole data-array with the given gamma
	std::transform(vec.begin(), vec.end(), vec.begin(),
	               [gamma](util::Vec3 v) -> util::Vec3 {
		               return util::Vec3(std::powf(v.x(), 1 / gamma),
		                                 std::powf(v.y(), 1 / gamma),
		                                 std::powf(v.z(), 1 / gamma));
	               });
}
Vec3 Image::operator[](const std::array<int, 2>& i) const {
	return vec[width * i[1] + i[0]];
}
Vec3& Image::operator[](const std::array<int, 2>& i) {
	return vec[width * i[1] + i[0]];
}
Image raytrace(size_t threadcount, const cam::CamObs& cam,
               const std::shared_ptr<Sampler>& sampler, size_t n) {
	Image result(cam.width, cam.height);

	result.setPixels(threadcount, std::make_shared<StratifiedSampler>(
	                                  StratifiedSampler(sampler, n))
	                 // sampler
	);
	result.gammaCorrect(2.2);
	return result;
}
void writeBmp(const char* filename, Image img) {
	int width = img.width;    // Width of the image
	int height = img.height;  // Height of the image

	int horizontalBytes;  // Horizontal bytes to add, so the pixelarrays width
	                      // is a multiple of 4
	horizontalBytes = 4 - ((width * 3) % 4);
	if (horizontalBytes == 4) horizontalBytes = 0;

	int pixelArraySize;  // The size of the pixelArray
	pixelArraySize = ((width * 3) + horizontalBytes) * height;

	// Headers
	int header[12];
	// Bitmap file header
	char bb = 'B';  // bfType
	char mm = 'M';
	header[0] = pixelArraySize + 54;  // bfSize
	header[1] = 0;                    // bfReserved
	header[2] = 54;                   // bfOffbits
	// Bitmap information header
	header[3] = 40;              // biSize
	header[4] = width;           // biWidth
	header[5] = height;          // biHeight
	short biPLanes = 1;          // biPlanes
	short biBitCount = 24;       // biBitCount
	header[6] = 0;               // biCompression
	header[7] = pixelArraySize;  // biSizeImage
	header[8] = 0;               // biXPelsPerMeter
	header[9] = 0;               // biYPelsPerMeter
	header[10] = 0;              // biClrUsed
	header[11] = 0;              // biClrImportant

	std::ofstream ofile(filename, std::ios::binary);

	// Write the header in the right order
	// bfType, ...
	ofile.write(&bb, sizeof(bb));
	ofile.write(&mm, sizeof(mm));
	// ... bfSize, BfReserved, bfOffbits, biSize, biWidth, bitHeight, ...
	for (int i = 0; i < 6; i++) {
		ofile.write((char*)&header[i], sizeof(header[i]));
	}
	// ... biPlanes, bitBitCount, ...
	ofile.write((char*)&biPLanes, sizeof(biPLanes));
	ofile.write((char*)&biBitCount, sizeof(biBitCount));
	// ... biCompression, biSizeImage, biXPelsPerMeter, biYPelsPerMeter,
	// biClrUsed, biClrImportant
	for (int i = 6; i < 12; i++) {
		ofile.write((char*)&header[i], sizeof(header[i]));
	}
	// The colors can only have a value from 0 to 255, so a char is enough to
	// store them
	char blue, green, red;
	// Bmp is written from top to bottom
	for (int y = height - 1; y >= 0; y--) {
		for (int x = 0; x <= width - 1; x++) {
			red = std::clamp<float>((img[{x, y}][0]), 0, 1) * 255;
			green = std::clamp<float>((img[{x, y}][1]), 0, 1) * 255;
			blue = std::clamp<float>((img[{x, y}][2]), 0, 1) * 255;

			// bmp colors are bgr not rgb
			char bgr[3] = {blue, green, red};
			for (int i = 0; i < 3; i++) {
				char c0 = (bgr[i] & 0x00FF);
				// char c8 = ((bgr[i] & (unsigned int)0xFF00) >> 8);

				ofile.write(&c0, sizeof(c0));
				// ofile.write (&c8, sizeof (c8));
			}
		}
		// If needed add extra bytes after each row
		if (horizontalBytes != 0) {
			char null;
			for (int n = 0; n < horizontalBytes; n++) {
				ofile.write(&null, sizeof(null));
			}
		}
	}

	ofile.close();
}
}  // namespace util