Loading...
Searching...
No Matches
CaptureControlTree.hpp
Go to the documentation of this file.
1#pragma once
2
16#include <Gfx/ControlTree.hpp>
18
19#include <ossia/network/base/device.hpp>
20#include <ossia/network/base/node.hpp>
21#include <ossia/network/domain/domain.hpp>
22
23#include <QVector3D>
24
25#include <cmath>
26#include <memory>
27#include <optional>
28#include <mutex>
29#include <vector>
30
31namespace Gfx
32{
33
43{
44public:
46 score::gfx::CaptureAdjustSlot& slot, ossia::net::device_base& dev,
47 ossia::net::node_base& parent, std::string group = "render")
48 : m_slot{slot}
49 {
50 m_value = slot.value();
51
52 auto edit = [this](auto&& fn) {
53 std::lock_guard lk{m_mutex};
54 fn(m_value);
55 m_slot.set(m_value);
56 };
57
58 std::vector<TreeControl> controls;
59
60 {
62 t.name = "scale_mode";
64 = "How the frame is fitted to the viewport: Original, BlackBars, "
65 "Fill, Stretch";
66 t.type = ossia::val_type::STRING;
67 t.domain = ossia::make_domain(
68 std::vector<std::string>{"Original", "BlackBars", "Fill", "Stretch"});
69 t.initial = std::string{"Stretch"};
70 t.onSet = [edit](const ossia::value& v) {
71 const auto s = v.target<std::string>();
72 if(!s)
73 return;
74 // A name we do not know leaves the mode alone. Falling through to a
75 // default would mean an OSC client with a typo silently resetting the
76 // fit -- and the domain does not stop that, since a domain describes
77 // the values rather than enforcing them.
78 std::optional<score::gfx::ScaleMode> mode;
79 if(*s == "Original")
80 mode = score::gfx::ScaleMode::Original;
81 else if(*s == "BlackBars")
82 mode = score::gfx::ScaleMode::BlackBars;
83 else if(*s == "Fill")
84 mode = score::gfx::ScaleMode::Fill;
85 else if(*s == "Stretch")
86 mode = score::gfx::ScaleMode::Stretch;
87 if(!mode)
88 return;
89 edit([m = *mode](score::gfx::CaptureAdjust& a) { a.scaleMode = m; });
90 };
91 controls.push_back(std::move(t));
92 }
93
94 // Black level and white balance are per-channel, so one vec3 each rather
95 // than six scalars: they are set together in practice, and six nodes would
96 // mean six round-trips through the slot for one adjustment.
97 {
99 t.name = "black_level";
100 t.description = "Sensor pedestal per channel, normalised, subtracted "
101 "before any gain";
102 t.type = ossia::val_type::VEC3F;
103 t.domain = ossia::make_domain(0.f, 1.f);
104 t.initial = ossia::vec3f{0.f, 0.f, 0.f};
105 t.onSet = [edit](const ossia::value& v) {
106 if(auto p = v.target<ossia::vec3f>())
107 edit([p = *p](score::gfx::CaptureAdjust& a) {
108 for(int i = 0; i < 3; ++i)
109 a.blackLevel[i] = p[i];
110 });
111 };
112 controls.push_back(std::move(t));
113 }
114
115 {
116 TreeControl t;
117 t.name = "white_balance";
118 t.description = "Per-channel gain. A Bayer sensor reads green without it";
119 t.type = ossia::val_type::VEC3F;
120 t.domain = ossia::make_domain(0.f, 8.f);
121 t.initial = ossia::vec3f{1.f, 1.f, 1.f};
122 t.onSet = [edit](const ossia::value& v) {
123 if(auto p = v.target<ossia::vec3f>())
124 edit([p = *p](score::gfx::CaptureAdjust& a) {
125 for(int i = 0; i < 3; ++i)
126 a.whiteBalance[i] = p[i];
127 });
128 };
129 controls.push_back(std::move(t));
130 }
131
132 const auto scalar
133 = [&](std::string name, std::string desc, float lo, float hi, float init,
134 auto member) {
135 TreeControl t;
136 t.name = std::move(name);
137 t.description = std::move(desc);
138 t.type = ossia::val_type::FLOAT;
139 t.domain = ossia::make_domain(lo, hi);
140 t.initial = init;
141 t.onSet = [edit, member](const ossia::value& v) {
142 const auto f = ossia::convert<float>(v);
143 // A domain describes values, it does not enforce them, so a client can
144 // send anything. NaN would survive every clamp in the shader -- it is
145 // not greater than or less than anything -- and take the frame with it.
146 if(!std::isfinite(f))
147 return;
148 edit([f, member](score::gfx::CaptureAdjust& a) { a.*member = f; });
149 };
150 controls.push_back(std::move(t));
151 };
152
153 scalar("exposure", "Linear multiplier applied after white balance", 0.f,
155 // 1 is the identity and the default: raising it to 2.2 approximates sRGB,
156 // which is usually what makes a linear sensor frame stop looking dark.
157 scalar("gamma", "Encoding exponent; 1 leaves the signal linear, 2.2 is "
158 "roughly sRGB", 0.1f, 4.f, 1.f,
160 scalar("saturation", "0 collapses to luma, 1 leaves colour alone", 0.f, 4.f,
162
163 m_params = addControlGroup(dev, parent, group, controls);
164 }
165
167 {
168 // The parameters outlive this object -- the device owns them and is
169 // destroyed after -- and their callbacks capture `this`. Cut the link
170 // before it can dangle.
171 for(auto* p : m_params)
172 if(p)
173 p->callbacks_clear();
174 }
175
176 CaptureControlTree(const CaptureControlTree&) = delete;
177 CaptureControlTree& operator=(const CaptureControlTree&) = delete;
178
179 std::size_t count() const noexcept { return m_params.size(); }
180
181private:
183 std::mutex m_mutex;
185 std::vector<ossia::net::parameter_base*> m_params;
186};
187
188} // namespace Gfx
The corrections a capture node applies on its way out of the sensor.
Turn a list of control descriptions into device-tree nodes.
Builds /render/ for one capture stream.
Definition CaptureControlTree.hpp:43
Cross-thread handoff for CaptureAdjust.
Definition CaptureAdjust.hpp:80
void set(const CaptureAdjust &v)
Writer side. Any thread.
Definition CaptureAdjust.hpp:83
Binds the rendering pipeline to ossia processes.
Definition AssetTable.cpp:7
std::vector< ossia::net::parameter_base * > addControlGroup(ossia::net::device_base &dev, ossia::net::node_base &parent, const std::string &group, const std::vector< TreeControl > &controls)
Creates <parent>/<group>/<name> for each control.
Definition ControlTree.hpp:56
One settable thing, described independently of where it came from.
Definition ControlTree.hpp:35
std::string name
tree-safe; becomes the address component
Definition ControlTree.hpp:36
std::string description
shown in the explorer
Definition ControlTree.hpp:37
std::function< void(const ossia::value &)> onSet
Definition ControlTree.hpp:46
Sensor-side corrections, in the order the shader applies them.
Definition CaptureAdjust.hpp:39
float saturation
0 collapses to luma, 1 leaves it alone.
Definition CaptureAdjust.hpp:57
float gamma
Definition CaptureAdjust.hpp:54
float exposure
Linear multiplier, after white balance.
Definition CaptureAdjust.hpp:50