#pragma once
|
|
|
|
#include "gp/algorithm/tmp_manip.hpp"
|
|
#include "gp/bitops.hpp"
|
|
#include "gp/buffer.hpp"
|
|
#include "gp/function.hpp"
|
|
#include "gp/math.hpp"
|
|
|
|
|
|
#include <iostream>
|
|
|
|
namespace gp{
|
|
/**
|
|
* @brief A bmp format viewport that can be used to generate and export bmp file contents
|
|
*
|
|
* @tparam lazy if true, will expect the source to be a function, else would expect it to be a buffer
|
|
* @tparam color_type the type that represents the color. as of now only gp::vec4_g<uint8_t> is supported
|
|
*/
|
|
template<bool lazy, typename color_type>
|
|
class bmp_viewport {
|
|
public:
|
|
/**
|
|
* @brief The type of data source expected
|
|
*/
|
|
using src_t = typename gp::either<lazy,
|
|
gp::function<color_type(gp::vec2_g<int32_t>)>,
|
|
gp::buffer<gp::buffer<color_type>>
|
|
>::type;
|
|
private:
|
|
src_t source;
|
|
gp::vec2_g<int32_t> resolution;
|
|
color_type get(int32_t x, int32_t y) {
|
|
gp_config::assertion(x>=0, "getting an x below zero");
|
|
gp_config::assertion(y>=0, "getting an y below zero");
|
|
|
|
if constexpr (lazy) {
|
|
return source({x, y});
|
|
} else {
|
|
return source[x][y];
|
|
}
|
|
}
|
|
public:
|
|
/**
|
|
* @brief Construct a new bmp viewport object
|
|
*
|
|
* @param res The viewport size in pixels
|
|
* @param src The viewport source @see src_t
|
|
*/
|
|
bmp_viewport(gp::vec2_g<int32_t> res, src_t src)
|
|
: source{src}
|
|
, resolution{res}
|
|
{}
|
|
|
|
/**
|
|
* @brief Wtrites the viewport into a buffer in the bmp file format
|
|
*
|
|
* Failure is not currently handled, hence more than enough buffer space is expected.
|
|
*
|
|
* @param destination a buffer wide enough to write the entire data on it at once
|
|
* @return gp::buffer<char>::associated_iterator the byte after the last one that was written by the function
|
|
*/
|
|
gp::buffer<char>::associated_iterator write(gp::buffer<char> destination) {
|
|
using sle16 = gp::endian_wrapper<int16_t, gp::endian::little>;
|
|
using sbe16 = gp::endian_wrapper<int16_t, gp::endian::big>;
|
|
using sle32 = gp::endian_wrapper<int32_t, gp::endian::little>;
|
|
using sbe32 = gp::endian_wrapper<int32_t, gp::endian::big>;
|
|
|
|
auto it = destination.begin();
|
|
*(it++) = 'B';
|
|
*(it++) = 'M';
|
|
auto& filesize = gp::buffer<char>{it.data, (it+4).data}.cast<sle32>()[0];
|
|
it = it+4;
|
|
|
|
*(it++) = 0;
|
|
*(it++) = 0;
|
|
*(it++) = 0;
|
|
*(it++) = 0;
|
|
auto& pixel_array_offset = gp::buffer<char>{it.data, (it+4).data}.cast<sle32>()[0];
|
|
it = it+4;
|
|
|
|
auto dib_start = it;
|
|
|
|
auto& dibsize = gp::buffer<char>{it.data, (it+4).data}.cast<sle32>()[0];
|
|
it = it+4;
|
|
|
|
auto& width = gp::buffer<char>{it.data, (it+4).data}.cast<sle32>()[0];
|
|
width = resolution.x;
|
|
it = it+4;
|
|
auto& height = gp::buffer<char>{it.data, (it+4).data}.cast<sle32>()[0];
|
|
it = it+4;
|
|
height = resolution.y;
|
|
|
|
auto& plane_cnt = gp::buffer<char>{it.data, (it+2).data}.cast<sle16>()[0];
|
|
it = it+2;
|
|
plane_cnt = 1;
|
|
auto& bit_per_pixel = gp::buffer<char>{it.data, (it+2).data}.cast<sle16>()[0];
|
|
it = it+2;
|
|
bit_per_pixel = sizeof(color_type)*8; // TODO: correct the size
|
|
|
|
auto& compression_method = gp::buffer<char>{it.data, (it+4).data}.cast<sle32>()[0];
|
|
it = it+4;
|
|
compression_method = 0;
|
|
|
|
auto& image_size = gp::buffer<char>{it.data, (it+4).data}.cast<sle32>()[0];
|
|
it = it+4;
|
|
|
|
auto& h_pixel_per_meter = gp::buffer<char>{it.data, (it+4).data}.cast<sle32>()[0];
|
|
it = it+4;
|
|
h_pixel_per_meter = 2835;
|
|
auto& v_pixel_per_meter = gp::buffer<char>{it.data, (it+4).data}.cast<sle32>()[0];
|
|
it = it+4;
|
|
v_pixel_per_meter = 2835;
|
|
|
|
auto& colors_in_palette = gp::buffer<char>{it.data, (it+4).data}.cast<sle32>()[0];
|
|
it = it+4;
|
|
colors_in_palette = 0;
|
|
auto& important_colors = gp::buffer<char>{it.data, (it+4).data}.cast<sle32>()[0];
|
|
it = it+4;
|
|
important_colors = 0;
|
|
|
|
auto dib_end = it;
|
|
dibsize = dib_end - dib_start;
|
|
auto pixel_array_start = it;
|
|
|
|
for(int32_t line = resolution.y - 1; line >= 0; line--)
|
|
{
|
|
int32_t len = 0;
|
|
for(int32_t row = 0; row < resolution.x; row++)
|
|
{
|
|
// TODO: add more default color modes
|
|
if constexpr (std::is_same<color_type, gp::vec4_g<uint8_t>>::value)
|
|
{
|
|
auto color = get(row, line);
|
|
*(it++) = color.b();
|
|
*(it++) = color.g();
|
|
*(it++) = color.r();
|
|
*(it++) = color.a();
|
|
} else {
|
|
it = it + sizeof(color_type);
|
|
}
|
|
len+=sizeof(color_type);
|
|
}
|
|
for(;len % 4; ++len)
|
|
{
|
|
*(it++) = 0;
|
|
}
|
|
}
|
|
|
|
auto pixel_array_end = it;
|
|
pixel_array_offset = pixel_array_start - destination.begin();
|
|
filesize = pixel_array_end - destination.begin();
|
|
image_size = pixel_array_end - pixel_array_start;
|
|
|
|
return it;
|
|
}
|
|
};
|
|
}
|