#pragma once #include "gp/algorithms/tmp_manip.hpp" #include "gp/math/boolean/bitops.hpp" #include "gp/containers/buffer.hpp" #include "gp/functional/function.hpp" #include "gp/math/rendering_math.hpp" 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::math::vec4_g is supported */ template class bmp_viewport { public: /** * @brief The type of data source expected */ using src_t = typename gp::either)>, gp::buffer> >::type; private: src_t source; gp::math::vec2_g 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::math::vec2_g 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::associated_iterator the byte after the last one that was written by the function */ gp::buffer::associated_iterator write(gp::buffer destination) { using sle16 = gp::endian_wrapper; using sbe16 = gp::endian_wrapper; using sle32 = gp::endian_wrapper; using sbe32 = gp::endian_wrapper; auto it = destination.begin(); *(it++) = 'B'; *(it++) = 'M'; auto& filesize = gp::buffer{it.data, (it+4).data}.cast()[0]; it = it+4; *(it++) = 0; *(it++) = 0; *(it++) = 0; *(it++) = 0; auto& pixel_array_offset = gp::buffer{it.data, (it+4).data}.cast()[0]; it = it+4; auto dib_start = it; auto& dibsize = gp::buffer{it.data, (it+4).data}.cast()[0]; it = it+4; auto& width = gp::buffer{it.data, (it+4).data}.cast()[0]; width = resolution.x; it = it+4; auto& height = gp::buffer{it.data, (it+4).data}.cast()[0]; it = it+4; height = resolution.y; auto& plane_cnt = gp::buffer{it.data, (it+2).data}.cast()[0]; it = it+2; plane_cnt = 1; auto& bit_per_pixel = gp::buffer{it.data, (it+2).data}.cast()[0]; it = it+2; bit_per_pixel = sizeof(color_type)*8; // TODO: correct the size auto& compression_method = gp::buffer{it.data, (it+4).data}.cast()[0]; it = it+4; compression_method = 0; auto& image_size = gp::buffer{it.data, (it+4).data}.cast()[0]; it = it+4; auto& h_pixel_per_meter = gp::buffer{it.data, (it+4).data}.cast()[0]; it = it+4; h_pixel_per_meter = 2835; auto& v_pixel_per_meter = gp::buffer{it.data, (it+4).data}.cast()[0]; it = it+4; v_pixel_per_meter = 2835; auto& colors_in_palette = gp::buffer{it.data, (it+4).data}.cast()[0]; it = it+4; colors_in_palette = 0; auto& important_colors = gp::buffer{it.data, (it+4).data}.cast()[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>::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; } }; }