diff --git a/RayTracer/sampling/Image.cpp b/RayTracer/sampling/Image.cpp index b82813cbb4e66b4034fdbaff4b25de8e097c6ad2..7f839df3616001c419041e3e7579131bc0f511fd 100644 --- a/RayTracer/sampling/Image.cpp +++ b/RayTracer/sampling/Image.cpp @@ -1,5 +1,8 @@ #include "Image.h" +#include <fstream> +#include <iostream> + #include "../tools/Threadpool.h" #include "StratifiedSampler.h" @@ -65,4 +68,86 @@ Image raytrace(size_t threadcount, const cam::CamObs& cam, 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 \ No newline at end of file diff --git a/RayTracer/sampling/Image.h b/RayTracer/sampling/Image.h index 10f95f8160d1f7c2b770e529ee9a0e75460d43b1..902dca6078651cd7976fa4352de06756033f856d 100644 --- a/RayTracer/sampling/Image.h +++ b/RayTracer/sampling/Image.h @@ -29,5 +29,6 @@ class Image { Image raytrace(size_t threadcount, const cam::CamObs& cam, const std::shared_ptr<Sampler>& sampler, size_t n); +void writeBmp(const char* filename, Image img); } // namespace util \ No newline at end of file