General Purpose library for Freestanding C++ and POSIX systems
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

157 lines
4.6 KiB

#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;
}
};
}