Photon Engine 2.0.0-beta
A physically based renderer.
Loading...
Searching...
No Matches
TFrame.ipp
Go to the documentation of this file.
1#pragma once
2
3#include "Frame/TFrame.h"
4#include "Math/TVector3.h"
5#include "Math/TVector2.h"
6#include "Math/math.h"
7
8#include <Common/assertion.h>
9
10#include <limits>
11#include <type_traits>
12#include <utility>
13#include <algorithm>
14#include <concepts>
15
16namespace ph
17{
18
19namespace frame_detail
20{
21
22template<typename Func, typename T, std::size_t N>
23concept CIsGetPixelOp = requires (Func func, typename TFrame<T, N>::PixelType pixel)
24{
25 // Requires returning `void`--to disambiguate with set ops
26 { func(pixel) } -> std::same_as<void>;
27};
28
29template<typename Func, typename T, std::size_t N>
30concept CIsGetPixelWithCoordsOp = requires (Func func, uint32 x, uint32 y, typename TFrame<T, N>::PixelType pixel)
31{
32 // Requires returning `void`--to disambiguate with set ops
33 { func(x, y, pixel) } -> std::same_as<void>;
34};
35
36template<typename Func, typename T, std::size_t N>
37concept CIsSetPixelOp = requires (Func func)
38{
39 { func() } -> std::convertible_to<typename TFrame<T, N>::PixelType>;
40};
41
42template<typename Func, typename T, std::size_t N>
43concept CIsSetPixelWithCoordsOp = requires (Func func, uint32 x, uint32 y)
44{
45 { func(x, y) } -> std::convertible_to<typename TFrame<T, N>::PixelType>;
46};
48template<typename Func, typename T, std::size_t N>
49concept CIsGetAndSetPixelOp = requires (Func func, typename TFrame<T, N>::PixelType pixel)
51 { func(pixel) } -> std::convertible_to<typename TFrame<T, N>::PixelType>;
52};
53
54template<typename Func, typename T, std::size_t N>
55concept CIsGetAndSetPixelWithCoordsOp = requires (Func func, uint32 x, uint32 y, typename TFrame<T, N>::PixelType pixel)
56{
57 { func(x, y, pixel) } -> std::convertible_to<typename TFrame<T, N>::PixelType>;
58};
59
60}// end namespace frame_detail
61
62template<typename T, std::size_t N>
63template<typename U>
65{
66 return TPixelType<U>(value);
68
69template<typename T, std::size_t N>
71 TFrame(0, 0)
72{}
73
74template<typename T, std::size_t N>
75inline TFrame<T, N>::TFrame(const uint32 wPx, const uint32 hPx) :
76 m_widthPx (wPx),
77 m_heightPx (hPx),
78 m_pixelData(wPx * hPx * N, 0)
79{}
80
81template<typename T, std::size_t N>
82inline TFrame<T, N>::TFrame(const TFrame& other) :
83 m_widthPx (other.m_widthPx),
84 m_heightPx (other.m_heightPx),
85 m_pixelData(other.m_pixelData)
86{}
88template<typename T, std::size_t N>
89inline TFrame<T, N>::TFrame(TFrame&& other) noexcept :
90 m_widthPx (other.m_widthPx),
91 m_heightPx (other.m_heightPx),
92 m_pixelData(std::move(other.m_pixelData))
93{}
95template<typename T, std::size_t N>
96inline void TFrame<T, N>::fill(const T value)
97{
98 std::fill(m_pixelData.begin(), m_pixelData.end(), value);
99}
100
101template<typename T, std::size_t N>
102inline void TFrame<T, N>::fill(const T value, const math::TAABB2D<uint32>& region)
103{
104 PH_ASSERT_MSG(!region.isEmpty(), region.toString());
105
106 const uint32 regionDataWidth = static_cast<uint32>(N) * region.getWidth();
107 for(uint32 y = region.getMinVertex().y(); y < region.getMaxVertex().y(); ++y)
108 {
109 const std::size_t offset = calcPixelDataBaseIndex(region.getMinVertex().x(), y);
111 std::fill(
112 m_pixelData.begin() + offset,
113 m_pixelData.begin() + offset + regionDataWidth,
114 value);
115 }
116}
117
118// TODO: wrap mode
119template<typename T, std::size_t N>
121 TFrame& sampled,
122 const math::TMathFunction2D<float64>& kernel,
123 const uint32 kernelRadiusPx) const
124{
125 if(isEmpty() || sampled.isEmpty() ||
126 kernelRadiusPx == 0)
127 {
128 sampled.fill(0);
129 return;
130 }
131
132 for(uint32 y = 0; y < sampled.heightPx(); ++y)
133 {
134 for(uint32 x = 0; x < sampled.widthPx(); ++x)
135 {
136 const math::Vector2D samplePosPx(
137 (x + 0.5) / sampled.widthPx() * widthPx(),
138 (y + 0.5) / sampled.heightPx() * heightPx());
139
140 // compute filter bounds
141 math::Vector2D filterMin(samplePosPx.sub(kernelRadiusPx));
142 math::Vector2D filterMax(samplePosPx.add(kernelRadiusPx));
143
144 // make filter bounds not to exceed frame bounds
145 filterMin = filterMin.max(math::Vector2D(0, 0));
146 filterMax = filterMax.min(math::Vector2D(widthPx(), heightPx()));
147
148 PH_ASSERT_LE(filterMin.x(), filterMax.x());
149 PH_ASSERT_LE(filterMin.y(), filterMax.y());
150
151 // compute pixel index bounds
152 math::TVector2<int64> x0y0(filterMin.sub(0.5).ceil());
153 math::TVector2<int64> x1y1(filterMax.sub(0.5).floor());
154
155 PH_ASSERT(x0y0.x() >= 0 && x0y0.y() >= 0 &&
156 x1y1.x() < widthPx() && x1y1.y() < heightPx());
157
158 TPixelType<float64> pixelSum = makeMonochromaticPixel<float64>(0);
159 float64 weightSum = 0.0;
160 for(int64 ky = x0y0.y(); ky <= x1y1.y(); ++ky)
161 {
162 for(int64 kx = x0y0.x(); kx <= x1y1.x(); ++kx)
163 {
164 const float64 kernelX = (kx + 0.5) - samplePosPx.x();
165 const float64 kernelY = (ky + 0.5) - samplePosPx.y();
166
167 PixelType pixel;
168 getPixel(static_cast<uint32>(kx), static_cast<uint32>(ky), &pixel);
169 const float64 weight = kernel.evaluate(kernelX, kernelY);
170
171 for(std::size_t i = 0; i < N; ++i)
172 {
173 pixelSum[i] += static_cast<float64>(pixel[i]);
174 }
175 weightSum += weight;
176 }//
177 } // end for each pixel in kernel support
178
179 PixelType sampledPixel;
180 if(weightSum > 0.0)
181 {
182 const float64 reciWeightSum = 1.0 / weightSum;
183 for(std::size_t i = 0; i < N; ++i)
184 {
185 const float64 sampledValue = pixelSum[i] * reciWeightSum;
186 sampledPixel[i] = static_cast<T>(sampledValue);
187 }
188 }
189 else
190 {
191 sampledPixel = makeMonochromaticPixel(T(0));
192 }
193 sampled.setPixel(x, y, sampledPixel);
194 }//
195 } // end for each pixel in sampled frame
196}
197
198template<typename T, std::size_t N>
200{
201 const uint32 halfWidthPx = m_widthPx / 2;
202
203 for(uint32 y = 0; y < m_heightPx; ++y)
204 {
205 for(uint32 x = 0; x < halfWidthPx; ++x)
206 {
207 const std::size_t leftPixelBegin = calcPixelDataBaseIndex(x, y);
208 const std::size_t rightPixelBegin = calcPixelDataBaseIndex(m_widthPx - 1 - x, y);
209
210 for(std::size_t i = 0; i < N; ++i)
211 {
212 std::swap(m_pixelData[leftPixelBegin + i], m_pixelData[rightPixelBegin + i]);
213 }
214 }
215 }
216}
217
218template<typename T, std::size_t N>
220{
221 const uint32 halfHeightPx = m_heightPx / 2;
222 const std::size_t numRowElements = m_widthPx * N;
223
224 for(uint32 y = 0; y < halfHeightPx; ++y)
225 {
226 const std::size_t bottomRowBegin = calcPixelDataBaseIndex(0, y);
227 const std::size_t topRowBegin = calcPixelDataBaseIndex(0, m_heightPx - 1 - y);
228
229 for(std::size_t i = 0; i < numRowElements; ++i)
230 {
231 std::swap(m_pixelData[bottomRowBegin + i], m_pixelData[topRowBegin + i]);
232 }
233 }
234}
235
236template<typename T, std::size_t N>
237inline void TFrame<T, N>::setSize(const uint32 wPx, const uint32 hPx)
238{
239 m_widthPx = wPx;
240 m_heightPx = hPx;
241 m_pixelData.resize(wPx * hPx * N);
242}
243
244template<typename T, std::size_t N>
246{
247 setSize(sizePx.x(), sizePx.y());
248}
249
250template<typename T, std::size_t N>
251template<typename PerPixelOperation>
252inline void TFrame<T, N>::forEachPixel(PerPixelOperation op)
253{
254 forEachPixel(math::TAABB2D<uint32>({0, 0}, {m_widthPx, m_heightPx}), std::move(op));
255}
256
257template<typename T, std::size_t N>
258template<typename PerPixelOperation>
259inline void TFrame<T, N>::forEachPixel(PerPixelOperation op) const
260{
261 forEachPixel(math::TAABB2D<uint32>({0, 0}, {m_widthPx, m_heightPx}), std::move(op));
262}
263
264template<typename T, std::size_t N>
265template<typename PerPixelOperation>
266inline void TFrame<T, N>::forEachPixel(const math::TAABB2D<uint32>& region, PerPixelOperation op)
267{
268 // OPT
269
270 PixelType pixel;
271 for(uint32 y = region.getMinVertex().y(); y < region.getMaxVertex().y(); ++y)
272 {
273 for(uint32 x = region.getMinVertex().x(); x < region.getMaxVertex().x(); ++x)
274 {
275 getPixel(x, y, &pixel);
276
278 {
279 op(pixel);
280 }
282 {
283 op(x, y, pixel);
284 }
286 {
287 setPixel(x, y, op());
288 }
290 {
291 setPixel(x, y, op(x, y));
292 }
294 {
295 setPixel(x, y, op(pixel));
296 }
298 {
299 setPixel(x, y, op(x, y, pixel));
300 }
301 else
302 {
303 // Unsupported per pixel operation
304 PH_ASSERT_UNREACHABLE_SECTION();
305 return;
306 }
307 }
308 }
309}
310
311template<typename T, std::size_t N>
312template<typename PerPixelOperation>
313inline void TFrame<T, N>::forEachPixel(const math::TAABB2D<uint32>& region, PerPixelOperation op) const
314{
315 // OPT
316
317 PixelType pixel;
318 for(uint32 y = region.getMinVertex().y(); y < region.getMaxVertex().y(); ++y)
319 {
320 for(uint32 x = region.getMinVertex().x(); x < region.getMaxVertex().x(); ++x)
321 {
322 getPixel(x, y, &pixel);
323
325 {
326 op(pixel);
327 }
329 {
330 op(x, y, pixel);
331 }
332 else
333 {
334 // Unsupported per pixel operation
335 PH_ASSERT_UNREACHABLE_SECTION();
336 return;
337 }
338 }
339 }
340}
341
342template<typename T, std::size_t N>
343inline auto TFrame<T, N>::getPixel(const math::TVector2<uint32>& coordPx) const
344 -> PixelType
345{
346 PixelType pixel;
347 getPixel(coordPx.x(), coordPx.y(), &pixel);
348 return pixel;
349}
350
351template<typename T, std::size_t N>
353 const uint32 x,
354 const uint32 y,
355 PixelType* const out_pixel) const
356{
357 PH_ASSERT(out_pixel);
358
359 const std::size_t baseIndex = calcPixelDataBaseIndex(x, y);
360
361 for(std::size_t i = 0; i < N; ++i)
362 {
363 PH_ASSERT_LT(baseIndex + i, m_pixelData.size());
364
365 (*out_pixel)[i] = m_pixelData[baseIndex + i];
366 }
367}
368
369template<typename T, std::size_t N>
370inline void TFrame<T, N>::setPixel(const math::TVector2<uint32>& coordPx, const PixelType& pixel)
371{
372 setPixel(coordPx.x(), coordPx.y(), pixel);
373}
374
375template<typename T, std::size_t N>
377 const uint32 x,
378 const uint32 y,
379 const PixelType& pixel)
380{
381 const std::size_t baseIndex = calcPixelDataBaseIndex(x, y);
382
383 for(std::size_t i = 0; i < N; ++i)
384 {
385 PH_ASSERT_LT(baseIndex + i, m_pixelData.size());
386
387 m_pixelData[baseIndex + i] = pixel[i];
388 }
389}
390
391template<typename T, std::size_t N>
392inline constexpr std::size_t TFrame<T, N>::numPixelComponents() const noexcept
393{
394 return N;
395}
396
397template<typename T, std::size_t N>
399{
400 return m_pixelData;
401}
402
403template<typename T, std::size_t N>
405{
406 return m_pixelData;
407}
408
409template<typename T, std::size_t N>
410inline void TFrame<T, N>::copyPixelData(const math::TAABB2D<uint32>& region, TSpan<T> out_data) const
411{
412 PH_ASSERT_MSG(!region.isEmpty(), region.toString());
413
414 const auto regionDataWidth = N * region.getWidth();
415 for(uint32 y = region.getMinVertex().y(); y < region.getMaxVertex().y(); ++y)
416 {
417 const auto srcOffset = calcPixelDataBaseIndex(region.getMinVertex().x(), y);
418 const auto dstOffset = (y - region.getMinVertex().y()) * regionDataWidth;
419
420 std::copy_n(
421 m_pixelData.begin() + srcOffset,
422 regionDataWidth,
423 out_data.begin() + dstOffset);
424 }
425}
426
427template<typename T, std::size_t N>
429{
430 return {m_widthPx, m_heightPx};
431}
432
433template<typename T, std::size_t N>
434inline uint32 TFrame<T, N>::widthPx() const
435{
436 return m_widthPx;
437}
438
439template<typename T, std::size_t N>
440inline uint32 TFrame<T, N>::heightPx() const
441{
442 return m_heightPx;
443}
444
445template<typename T, std::size_t N>
446inline bool TFrame<T, N>::isEmpty() const
447{
448 return m_pixelData.empty();
449}
450
451template<typename T, std::size_t N>
452inline std::size_t TFrame<T, N>::calcPixelDataBaseIndex(
453 const uint32 x,
454 const uint32 y) const
455{
456 PH_ASSERT_LT(x, m_widthPx);
457 PH_ASSERT_LT(y, m_heightPx);
458
459 return (y * static_cast<std::size_t>(m_widthPx) + x) * N;
460}
461
462template<typename T, std::size_t N>
464{
465 m_widthPx = rhs.m_widthPx;
466 m_heightPx = rhs.m_heightPx;
467 m_pixelData = rhs.m_pixelData;
468
469 return *this;
470}
471
472template<typename T, std::size_t N>
474{
475 m_widthPx = rhs.m_widthPx;
476 m_heightPx = rhs.m_heightPx;
477 m_pixelData = std::move(rhs.m_pixelData);
478
479 return *this;
480}
481
482}// end namespace ph
Definition TFrame.h:21
TPixelType< T > PixelType
Definition TFrame.h:28
uint32 heightPx() const
Definition TFrame.ipp:440
TSpan< T > getPixelData()
Get pixel data for the full frame.
Definition TFrame.ipp:398
void forEachPixel(PerPixelOperation op)
Iterate over all pixels in the frame in row-major order.
Definition TFrame.ipp:252
TFrame()
Constructs an empty frame.
Definition TFrame.ipp:70
void copyPixelData(const math::TAABB2D< uint32 > &region, TSpan< T > out_data) const
Copy a region of pixel data into a buffer.
Definition TFrame.ipp:410
static TPixelType< U > makeMonochromaticPixel(U value)
void sample(TFrame &sampled, const math::TMathFunction2D< float64 > &kernel, uint32 kernelRadiusPx) const
Definition TFrame.ipp:120
void fill(T value)
Definition TFrame.ipp:96
void setPixel(const math::TVector2< uint32 > &coordPx, const PixelType &pixel)
Definition TFrame.ipp:370
constexpr std::size_t numPixelComponents() const noexcept
Definition TFrame.ipp:392
void flipVertically()
Definition TFrame.ipp:219
uint32 widthPx() const
Definition TFrame.ipp:434
void flipHorizontally()
Definition TFrame.ipp:199
math::TVector2< uint32 > getSizePx() const
Definition TFrame.ipp:428
TFrame & operator=(const TFrame &rhs)
Definition TFrame.ipp:463
void setSize(uint32 wPx, uint32 hPx)
Definition TFrame.ipp:237
bool isEmpty() const
Definition TFrame.ipp:446
PixelType getPixel(const math::TVector2< uint32 > &coordPx) const
Definition TFrame.ipp:343
A 2-D Axis-Aligned Bounding Box (AABB).
Definition TAABB2D.h:26
T getWidth() const
Definition TAABB2D.ipp:138
bool isEmpty() const
Definition TAABB2D.ipp:193
const TVector2< T > & getMaxVertex() const
Definition TAABB2D.ipp:126
std::string toString() const
Definition TAABB2D.ipp:235
const TVector2< T > & getMinVertex() const
Definition TAABB2D.ipp:120
Definition TArithmeticArray.h:13
Definition TMathFunction2D.h:8
virtual Value evaluate(Value x, Value y) const =0
T & x()
Definition TVector2.ipp:38
T & y()
Definition TVector2.ipp:44
Derived floor() const
Definition TArithmeticArrayBase.ipp:447
Derived sub(const Derived &rhs) const
Definition TArithmeticArrayBase.ipp:62
T min() const
Definition TArithmeticArrayBase.ipp:364
T max() const
Definition TArithmeticArrayBase.ipp:396
Derived add(const Derived &rhs) const
Definition TArithmeticArrayBase.ipp:26
Derived ceil() const
Definition TArithmeticArrayBase.ipp:428
Definition TFrame.ipp:23
Definition TFrame.ipp:37
Miscellaneous math utilities.
The root for all renderer implementations.
Definition EEngineProject.h:6
std::span< const T, EXTENT > TSpanView
Same as TSpan, except that the objects are const-qualified. Note that for pointer types,...
Definition TSpan.h:19
std::span< T, EXTENT > TSpan
A contiguous sequence of objects of type T. Effectively the same as std::span.
Definition TSpan.h:12