pixels.hpp 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. #ifndef SIMPLE_GRAPHICAL_PIXELS_HPP
  2. #define SIMPLE_GRAPHICAL_PIXELS_HPP
  3. #include "pixels.h"
  4. #include <memory>
  5. #include <cassert>
  6. #include "common_def.h"
  7. #include "color_vector.hpp"
  8. #include "simple/geom/algorithm.hpp"
  9. #include "color.h"
  10. // TODO: LERP: there is so much lerp here, but no lerp function to rule them all
  11. // meanwhile, by going against popular vote we have now a better name for lerp:
  12. // way (and it's evil twin - wayback), available under simple::support
  13. namespace simple::graphical
  14. {
  15. template <typename Function>
  16. void quantize(range2D spread, float2 sample, Function&& quant_handler)
  17. {
  18. // this is bilinear interpolation essentially,
  19. // but should work as is for N-linear case, right?
  20. assert(spread.upper() - spread.lower() <= int2::one(2));
  21. auto floor_ = floor(sample);
  22. auto table = sample - floor_;
  23. geom::loop(spread, int2::one(),
  24. [&](auto i)
  25. {
  26. float2 mask = float2(i);
  27. // and doesn't have to be linear either,
  28. // with another customization point here maybe?
  29. // LERP: whata lerpaderp is this? lerpception
  30. auto ratio = table * mask +
  31. (float2::one() - table)*(float2::one() - mask);
  32. auto magnitude = std::accumulate(
  33. ratio.begin(), ratio.end(),
  34. 1.f, std::multiplies{} );
  35. std::invoke(std::forward<Function>(quant_handler),
  36. floor_ + mask, magnitude);
  37. });
  38. }
  39. namespace pixel_view_details
  40. {
  41. template<typename T, typename P, typename R>
  42. const int2& impl<T,P,R>::raw_size() const noexcept
  43. { return _raw_size; }
  44. template<typename T, typename P, typename R>
  45. int2 impl<T,P,R>::size() const noexcept
  46. { return _raw_size / ratio; }
  47. template<typename T, typename P, typename R>
  48. auto impl<T,P,R>::row(int index) const noexcept
  49. -> raw_type*
  50. { return reinterpret_cast<raw_type*>(_raw + index * _pitch); }
  51. template<typename T, typename P, typename R>
  52. auto impl<T,P,R>::row_offset(raw_type* row, int offset) const noexcept
  53. -> raw_type*
  54. { return reinterpret_cast<raw_type*>(
  55. reinterpret_cast<byte_type*>(row) +
  56. offset * _pitch
  57. ); }
  58. template<typename T, typename P, typename R>
  59. auto impl<T,P,R>::next_row(raw_type* row) const noexcept
  60. -> raw_type*
  61. { return reinterpret_cast<raw_type*>(
  62. reinterpret_cast<byte_type*>(row) + _pitch
  63. ); }
  64. template<typename T, typename P, typename R>
  65. auto impl<T,P,R>::prev_row(raw_type* row) const noexcept
  66. -> raw_type*
  67. { return reinterpret_cast<raw_type*>(
  68. reinterpret_cast<byte_type*>(row) - _pitch
  69. ); }
  70. template<typename T, typename P, typename R>
  71. auto impl<T,P,R>::operator[](int2 position) const noexcept
  72. -> raw_type&
  73. {
  74. assert(int2::zero() <= position && position < raw_size());
  75. return row(position.y())[position.x()];
  76. }
  77. template<typename T, typename P, typename R>
  78. auto impl<T,P,R>::raw_range() const noexcept
  79. -> simple::support::range<raw_type*>
  80. {
  81. return {row(), row_offset(row(),raw_size().y())};
  82. }
  83. template<typename T, typename Pixel, typename RawType>
  84. auto impl<T,Pixel,RawType>::get(int2 position) const
  85. -> std::conditional_t<std::is_same_v<Pixel,RawType>, const Pixel&, Pixel>
  86. {
  87. assert(int2::zero() <= position && position < size());
  88. if constexpr(std::is_same_v<Pixel,RawType>)
  89. return (*this)[position];
  90. else
  91. {
  92. Pixel pixel;
  93. position.x() *= ratio.x();
  94. memcpy(&pixel, &(*this)[position], sizeof(Pixel));
  95. return pixel;
  96. }
  97. }
  98. template<typename T, typename Pixel, typename RawType>
  99. template <typename ColorVector>
  100. Pixel impl<T,Pixel,RawType>::get(float2 position) const
  101. {
  102. const float2 size(this->size());
  103. assert(float2::one(-1) < position && position < size);
  104. return get<ColorVector>(position,
  105. {
  106. int2(position < float2::zero()),
  107. int2::one(2) - int2(position >= (size - 1))
  108. });
  109. }
  110. template<typename T, typename Pixel, typename RawType>
  111. template <typename ColorVector>
  112. Pixel impl<T,Pixel,RawType>::get(float2 position, range2D spread) const
  113. {
  114. auto sample = ColorVector{};
  115. quantize(spread, position, [&](auto position, auto area)
  116. {
  117. // LERP: weighted sum is a generalization of lerp
  118. sample += static_cast<ColorVector>(get(int2(position))) * area;
  119. });
  120. return static_cast<Pixel>(sample);
  121. }
  122. template<typename T, typename P, typename R>
  123. impl<T,P,R>::impl(const impl & other, range2D range) :
  124. _raw(reinterpret_cast<tag::select_raw_type<T,pixel_byte>*>(other.row())
  125. + to_index(range.lower(), other.pitch())),
  126. _raw_size((range.upper() - range.lower())*ratio),
  127. _pitch(other.pitch())
  128. {}
  129. template<typename T, typename P, typename R>
  130. int impl<T,P,R>::pitch() const noexcept { return _pitch; }
  131. template<typename T, typename P, typename RawType>
  132. impl<T,P,RawType>::impl(tag::select_raw_type<T,pixel_byte>* target, int2 size, int pitch)
  133. : _raw(target), _raw_size(size), _pitch(pitch ? pitch : size.x() * sizeof(RawType))
  134. {}
  135. template<typename T, typename P, typename R>
  136. template<typename OtherTag>
  137. impl<T,P,R>::impl(const impl<OtherTag, P, R> & other) :
  138. _raw(reinterpret_cast<tag::select_raw_type<T,pixel_byte>*>(other.row())),
  139. _raw_size(other.raw_size()),
  140. _pitch(other.pitch())
  141. {}
  142. template<typename T, typename P, typename R>
  143. template<typename OtherTag>
  144. impl<T,P,R>::impl(const impl<OtherTag, P, R> & other, range2D range) :
  145. _raw(reinterpret_cast<tag::select_raw_type<T,pixel_byte>*>(other.row())
  146. + to_index(range.lower(), other.pitch())),
  147. _raw_size((range.upper() - range.lower())*ratio),
  148. _pitch(other.pitch())
  149. {}
  150. template<typename T, typename P, typename RawType>
  151. int impl<T,P,RawType>::to_index(const int2& position, int pitch) noexcept
  152. {
  153. return position.x() * ratio.x() * sizeof(RawType) + position.y() * pitch;
  154. }
  155. } // namespace pixel_view_details
  156. template <typename P, typename R>
  157. pixel_writer<P,R>::pixel_writer(pixel_byte* target, int2 size, int pitch)
  158. : impl(target, size, pitch)
  159. {}
  160. template<typename Pixel, typename RawType>
  161. void pixel_writer<Pixel,RawType>::set(const Pixel& pixel, int2 position) const
  162. {
  163. assert(int2::zero() <= position && position < this->size());
  164. if constexpr(std::is_same_v<Pixel,RawType>)
  165. (*this)[position] = pixel;
  166. else
  167. {
  168. position.x() *= pixel_writer::ratio.x();
  169. memcpy(&(*this)[position], &(pixel), sizeof(Pixel));
  170. }
  171. }
  172. template<typename Pixel, typename R>
  173. template <typename ColorVector>
  174. void pixel_writer<Pixel,R>::set(const Pixel& pixel, float2 position) const
  175. {
  176. set(static_cast<ColorVector>(pixel), position);
  177. }
  178. template<typename Pixel, typename R>
  179. template <typename ColorVector>
  180. void pixel_writer<Pixel,R>::set(const Pixel& pixel, float2 position, range2D spread) const
  181. {
  182. set(static_cast<ColorVector>(pixel), position, spread);
  183. }
  184. template<typename Pixel, typename R>
  185. template <typename ColorVector>
  186. void pixel_writer<Pixel,R>::set(const ColorVector& pixel, float2 position) const
  187. {
  188. const float2 size(this->size());
  189. assert(float2::one(-1) < position && position < size);
  190. set<ColorVector>(pixel, position,
  191. {
  192. int2(position < float2::zero()),
  193. int2::one(2) - int2(position >= (size - 1))
  194. });
  195. }
  196. template<typename Pixel, typename R>
  197. template <typename ColorVector>
  198. void pixel_writer<Pixel,R>::set(const ColorVector& pixel, float2 position, range2D spread) const
  199. {
  200. quantize(spread, position, [&](auto position, auto opacity)
  201. {
  202. // blending TODO: parameterize?
  203. if constexpr (ColorVector::dimensions >= 4)
  204. opacity *= pixel.a();
  205. assert(opacity >= 0.f && opacity <= 1.f);
  206. ColorVector old_color {this->get(int2(position))};
  207. // TODO: consider skipping setting pixels with opacity 0(or near 0)
  208. // LERP: finally just a simple lerp
  209. set(Pixel(pixel * opacity + old_color * (1 - opacity)), int2(position));
  210. });
  211. }
  212. template<typename Pixel, typename R>
  213. void pixel_writer<Pixel,R>::set(const color& pixel, int2 position) const
  214. {
  215. assert(int2::zero() <= position && position < this->size());
  216. static_assert( sizeof(color) >= sizeof(Pixel), "color should fill a pixel, at least" );
  217. position.x() *= pixel_writer::ratio.x();
  218. memcpy(&(*this)[position], &(pixel), sizeof(Pixel));
  219. }
  220. template <typename Pixel, typename RawType>
  221. pixel_reader<Pixel,RawType>::pixel_reader
  222. (const pixel_writer<Pixel, RawType> & other)
  223. : impl(other)
  224. {}
  225. template <typename Pixel, typename RawType>
  226. pixel_reader<Pixel,RawType>::pixel_reader
  227. (
  228. const pixel_writer<Pixel, RawType> & other,
  229. range2D range
  230. )
  231. : impl(other, range)
  232. {}
  233. /* i like this code, but it's inferior ;_;
  234. template<typename Pixel, typename R>
  235. template <typename ColorVector>
  236. void pixel_writer<Pixel,R>::set(const ColorVector& pixel, float2 position) const
  237. {
  238. auto floor = int2(position);
  239. auto fraction = position - float2(floor);
  240. auto i = float2::zero();
  241. auto begin = (floor[0] != this->size()[0] - 1) ? i.begin() : i.begin() + 1;
  242. auto end = (floor[1] != this->size()[1] - 1) ? i.end() : i.end() - 1;
  243. do
  244. {
  245. auto ratio = (float2::one() - fraction)*(float2::one() - i) + fraction * i;
  246. auto opacity = std::accumulate(ratio.begin(), ratio.end(), 1.f, std::multiplies{});
  247. if constexpr (ColorVector::dimensions >= 4)
  248. opacity *= pixel.a();
  249. assert(opacity >= 0.f && opacity <= 1.f);
  250. ColorVector old_color {this->get(floor + int2(i))};
  251. set(Pixel(pixel * opacity + old_color * (1 - opacity)), floor + int2(i));
  252. }
  253. while(simple::support::next_number(begin, end) != end);
  254. }
  255. */
  256. } // namespace simple::graphical
  257. #endif /* end of include guard */