Blender V4.3
sequencer_scopes.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2006-2008 Peter Schlaile < peter [at] schlaile [dot] de >.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include <cmath>
10#include <cstring>
11
12#include "BLI_math_vector.hh"
13#include "BLI_task.hh"
14#include "BLI_utildefines.h"
15
17#include "IMB_imbuf.hh"
18#include "IMB_imbuf_types.hh"
19
20#include "sequencer_scopes.hh"
21
22// #define DEBUG_TIME
23
24#ifdef DEBUG_TIME
25# include "BLI_timeit.hh"
26#endif
27
28namespace blender::ed::seq {
29
34
36{
37 if (zebra_ibuf) {
39 zebra_ibuf = nullptr;
40 }
41 if (waveform_ibuf) {
43 waveform_ibuf = nullptr;
44 }
47 sep_waveform_ibuf = nullptr;
48 }
49 if (vector_ibuf) {
51 vector_ibuf = nullptr;
52 }
53 histogram.data.reinitialize(0);
54}
55
56static blender::float2 rgb_to_uv_normalized(const float rgb[3])
57{
58 /* Exact same math as rgb_to_yuv BT709 case. Duplicated here
59 * since this function is called a lot, and non-inline function
60 * call plus color-space switch in there overhead does add up. */
61 float r = rgb[0], g = rgb[1], b = rgb[2];
62 /* We don't need y. */
63 float u = -0.09991f * r - 0.33609f * g + 0.436f * b;
64 float v = 0.615f * r - 0.55861f * g - 0.05639f * b;
65
66 /* Normalize: possible range is +/- 0.615. */
67 u = clamp_f(u * (0.5f / 0.615f) + 0.5f, 0.0f, 1.0f);
68 v = clamp_f(v * (0.5f / 0.615f) + 0.5f, 0.0f, 1.0f);
69 return float2(u, v);
70}
71
72static void scope_put_pixel(const uchar *table, uchar *pos)
73{
74 uchar newval = table[*pos];
75 pos[0] = pos[1] = pos[2] = newval;
76 pos[3] = 255;
77}
78
79static void scope_put_pixel_single(const uchar *table, uchar *pos, int col)
80{
81 uint newval = table[pos[col]];
82 /* So that the separate waveforms are not just pure RGB primaries, put
83 * some amount of value into the other channels too: slightly reduce it,
84 * and raise to 4th power. */
85 uint other = newval * 31 / 32;
86 other = (other * other) >> 8;
87 other = (other * other) >> 8;
88 pos[0] = pos[1] = pos[2] = uchar(other);
89 pos[col] = uchar(newval);
90 pos[3] = 255;
91}
92
93static void init_wave_table(int height, uchar wtable[256])
94{
95 /* For each pixel column of the image, waveform plots the intensity values
96 * with height proportional to the intensity. So depending on the height of
97 * the image, different amount of pixels are expected to hit the same
98 * intensity. Adjust the waveform plotting table gamma factor so that
99 * the waveform has decent visibility without saturating or being too dark:
100 * 0.3 gamma at height=360 and below, 0.9 gamma at height 2160 (4K) and up,
101 * and interpolating between those. */
102 float alpha = clamp_f(ratiof(360.0f, 2160.0f, height), 0.0f, 1.0f);
103 float gamma = interpf(0.9f, 0.3f, alpha);
104 for (int x = 0; x < 256; x++) {
105 wtable[x] = uchar(pow((float(x) + 1.0f) / 256.0f, gamma) * 255.0f);
106 }
107}
108
110{
111#ifdef DEBUG_TIME
112 SCOPED_TIMER(__func__);
113#endif
114 const int w = ibuf->x;
115 const int h = 256;
116 ImBuf *rval = IMB_allocImBuf(w, h, 32, IB_rect);
117 uchar *tgt = rval->byte_buffer.data;
118
119 uchar wtable[256];
120 init_wave_table(ibuf->y, wtable);
121
122 /* IMB_colormanagement_get_luminance_byte for each pixel is quite a lot of
123 * overhead, so instead get luma coefficients as 16-bit integers. */
124 float coeffs[3];
126 const int muls[3] = {int(coeffs[0] * 65535), int(coeffs[1] * 65535), int(coeffs[2] * 65535)};
127
128 /* Parallel over x, since each column is easily independent from others. */
129 threading::parallel_for(IndexRange(ibuf->x), 32, [&](IndexRange x_range) {
130 if (ibuf->float_buffer.data) {
131 /* Float image. */
132 const float *src = ibuf->float_buffer.data;
133 for (int y = 0; y < ibuf->y; y++) {
134 for (const int x : x_range) {
135 const float *rgb = src + 4 * (ibuf->x * y + x);
136 float v = IMB_colormanagement_get_luminance(rgb);
137 uchar *p = tgt;
138
139 int iv = clamp_i(int(v * h), 0, h - 1);
140
141 p += 4 * (w * iv + x);
142 scope_put_pixel(wtable, p);
143 }
144 }
145 }
146 else {
147 /* Byte image. */
148 const uchar *src = ibuf->byte_buffer.data;
149 for (int y = 0; y < ibuf->y; y++) {
150 for (const int x : x_range) {
151 const uchar *rgb = src + 4 * (ibuf->x * y + x);
152 /* +1 is "Sree's solution" from http://stereopsis.com/doubleblend.html */
153 int rgb0 = rgb[0] + 1;
154 int rgb1 = rgb[1] + 1;
155 int rgb2 = rgb[2] + 1;
156 int luma = (rgb0 * muls[0] + rgb1 * muls[1] + rgb2 * muls[2]) >> 16;
157 int luma_y = clamp_i(luma, 0, 255);
158 uchar *p = tgt + 4 * (w * luma_y + x);
159 scope_put_pixel(wtable, p);
160 }
161 }
162 }
163 });
164
165 return rval;
166}
167
169{
170#ifdef DEBUG_TIME
171 SCOPED_TIMER(__func__);
172#endif
173 int w = ibuf->x;
174 int h = 256;
175 ImBuf *rval = IMB_allocImBuf(w, h, 32, IB_rect);
176 uchar *tgt = rval->byte_buffer.data;
177 int sw = ibuf->x / 3;
178
179 uchar wtable[256];
180 init_wave_table(ibuf->y, wtable);
181
182 /* Parallel over x, since each column is easily independent from others. */
183 threading::parallel_for(IndexRange(ibuf->x), 32, [&](IndexRange x_range) {
184 if (ibuf->float_buffer.data) {
185 /* Float image. */
186 const float *src = ibuf->float_buffer.data;
187 for (int y = 0; y < ibuf->y; y++) {
188 for (const int x : x_range) {
189 const float *rgb = src + 4 * (ibuf->x * y + x);
190 for (int c = 0; c < 3; c++) {
191 uchar *p = tgt;
192 float v = rgb[c];
193 int iv = clamp_i(int(v * h), 0, h - 1);
194
195 p += 4 * (w * iv + c * sw + x / 3);
196 scope_put_pixel_single(wtable, p, c);
197 }
198 }
199 }
200 }
201 else {
202 /* Byte image. */
203 const uchar *src = ibuf->byte_buffer.data;
204 for (int y = 0; y < ibuf->y; y++) {
205 for (const int x : x_range) {
206 const uchar *rgb = src + 4 * (ibuf->x * y + x);
207 for (int c = 0; c < 3; c++) {
208 uchar *p = tgt;
209 p += 4 * (w * rgb[c] + c * sw + x / 3);
210 scope_put_pixel_single(wtable, p, c);
211 }
212 }
213 }
214 }
215 });
216
217 return rval;
218}
219
220ImBuf *make_zebra_view_from_ibuf(const ImBuf *ibuf, float perc)
221{
222#ifdef DEBUG_TIME
223 SCOPED_TIMER(__func__);
224#endif
225 ImBuf *res = IMB_allocImBuf(ibuf->x, ibuf->y, 32, IB_rect | IB_uninitialized_pixels);
226
227 threading::parallel_for(IndexRange(ibuf->y), 16, [&](IndexRange y_range) {
228 if (ibuf->float_buffer.data) {
229 /* Float image. */
230 const float limit = perc / 100.0f;
231 const float *p = ibuf->float_buffer.data + y_range.first() * ibuf->x * 4;
232 uchar *o = res->byte_buffer.data + y_range.first() * ibuf->x * 4;
233 for (const int y : y_range) {
234 for (int x = 0; x < ibuf->x; x++) {
235 float pix[4];
236 memcpy(pix, p, sizeof(pix));
237 if (pix[0] >= limit || pix[1] >= limit || pix[2] >= limit) {
238 if (((x + y) & 0x08) != 0) {
239 pix[0] = 1.0f - pix[0];
240 pix[1] = 1.0f - pix[1];
241 pix[2] = 1.0f - pix[2];
242 }
243 }
244 rgba_float_to_uchar(o, pix);
245 p += 4;
246 o += 4;
247 }
248 }
249 }
250 else {
251 /* Byte image. */
252 const uint limit = 255.0f * perc / 100.0f;
253 const uchar *p = ibuf->byte_buffer.data + y_range.first() * ibuf->x * 4;
254 uchar *o = res->byte_buffer.data + y_range.first() * ibuf->x * 4;
255 for (const int y : y_range) {
256 for (int x = 0; x < ibuf->x; x++) {
257 uchar pix[4];
258 memcpy(pix, p, sizeof(pix));
259
260 if (pix[0] >= limit || pix[1] >= limit || pix[2] >= limit) {
261 if (((x + y) & 0x08) != 0) {
262 pix[0] = 255 - pix[0];
263 pix[1] = 255 - pix[1];
264 pix[2] = 255 - pix[2];
265 }
266 }
267 memcpy(o, pix, sizeof(pix));
268 p += 4;
269 o += 4;
270 }
271 }
272 }
273 });
274 return res;
275}
276
277static int get_bin_float(float f)
278{
279 int bin = int(((f - ScopeHistogram::FLOAT_VAL_MIN) /
280 (ScopeHistogram::FLOAT_VAL_MAX - ScopeHistogram::FLOAT_VAL_MIN)) *
281 ScopeHistogram::BINS_FLOAT);
282 return clamp_i(bin, 0, ScopeHistogram::BINS_FLOAT - 1);
283}
284
285void ScopeHistogram::calc_from_ibuf(const ImBuf *ibuf)
286{
287#ifdef DEBUG_TIME
288 SCOPED_TIMER(__func__);
289#endif
290
291 const bool is_float = ibuf->float_buffer.data != nullptr;
292 const int hist_size = is_float ? BINS_FLOAT : BINS_BYTE;
293
294 Array<uint3> counts(hist_size, uint3(0));
296 IndexRange(ibuf->y),
297 256,
298 counts,
299 [&](const IndexRange y_range, const Array<uint3> &init) {
300 Array<uint3> res = init;
301
302 if (is_float) {
303 for (const int y : y_range) {
304 const float *src = ibuf->float_buffer.data + y * ibuf->x * 4;
305 for (int x = 0; x < ibuf->x; x++) {
306 res[get_bin_float(src[0])].x++;
307 res[get_bin_float(src[1])].y++;
308 res[get_bin_float(src[2])].z++;
309 src += 4;
310 }
311 }
312 }
313 else {
314 /* Byte images just use 256 histogram bins, directly indexed by value. */
315 for (const int y : y_range) {
316 const uchar *src = ibuf->byte_buffer.data + y * ibuf->x * 4;
317 for (int x = 0; x < ibuf->x; x++) {
318 res[src[0]].x++;
319 res[src[1]].y++;
320 res[src[2]].z++;
321 src += 4;
322 }
323 }
324 }
325 return res;
326 },
327 [&](const Array<uint3> &a, const Array<uint3> &b) {
328 BLI_assert(a.size() == b.size());
329 Array<uint3> res(a.size());
330 for (int i = 0; i < a.size(); i++) {
331 res[i] = a[i] + b[i];
332 }
333 return res;
334 });
335
336 max_value = uint3(0);
337 for (const uint3 &v : data) {
338 max_value = math::max(max_value, v);
339 }
340}
341
343{
344#ifdef DEBUG_TIME
345 SCOPED_TIMER(__func__);
346#endif
347 const int size = 512;
348 const float size_mul = size - 1.0f;
349 ImBuf *rval = IMB_allocImBuf(size, size, 32, IB_rect);
350
351 uchar *dst = rval->byte_buffer.data;
352 float rgb[3];
353
354 uchar wtable[256];
355 init_wave_table(math::midpoint(ibuf->x, ibuf->y), wtable);
356
357 if (ibuf->float_buffer.data) {
358 /* Float image. */
359 const float *src = ibuf->float_buffer.data;
360 for (int y = 0; y < ibuf->y; y++) {
361 for (int x = 0; x < ibuf->x; x++) {
362 memcpy(rgb, src, sizeof(float[3]));
363 clamp_v3(rgb, 0.0f, 1.0f);
364
365 float2 uv = rgb_to_uv_normalized(rgb) * size_mul;
366
367 uchar *p = dst + 4 * (size * int(uv.y) + int(uv.x));
368 scope_put_pixel(wtable, p);
369
370 src += 4;
371 }
372 }
373 }
374 else {
375 /* Byte image. */
376 const uchar *src = ibuf->byte_buffer.data;
377 for (int y = 0; y < ibuf->y; y++) {
378 for (int x = 0; x < ibuf->x; x++) {
379 rgb[0] = float(src[0]) * (1.0f / 255.0f);
380 rgb[1] = float(src[1]) * (1.0f / 255.0f);
381 rgb[2] = float(src[2]) * (1.0f / 255.0f);
382
383 float2 uv = rgb_to_uv_normalized(rgb) * size_mul;
384
385 uchar *p = dst + 4 * (size * int(uv.y) + int(uv.x));
386 scope_put_pixel(wtable, p);
387
388 src += 4;
389 }
390 }
391 }
392
393 return rval;
394}
395
396} // namespace blender::ed::seq
#define BLI_assert(a)
Definition BLI_assert.h:50
MINLINE float clamp_f(float value, float min, float max)
MINLINE float interpf(float target, float origin, float t)
MINLINE int clamp_i(int value, int min, int max)
MINLINE float ratiof(float min, float max, float pos)
MINLINE void clamp_v3(float vec[3], float min, float max)
unsigned char uchar
unsigned int uint
#define SCOPED_TIMER(name)
Definition BLI_timeit.hh:61
BLI_INLINE void IMB_colormanagement_get_luminance_coefficients(float r_rgb[3])
Contains defines and structs used throughout the imbuf module.
@ IB_uninitialized_pixels
@ IB_rect
ATTR_WARN_UNUSED_RESULT const BMVert * v
void init()
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition btQuadWord.h:119
BLI_INLINE int get_bin_float(float f)
local_group_size(16, 16) .push_constant(Type b
pow(value.r - subtrahend, 2.0)") .do_static_compilation(true)
draw_view in_light_buf[] float
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
uint col
struct ImBuf * IMB_allocImBuf(unsigned int, unsigned int, unsigned char, unsigned int)
void IMB_freeImBuf(ImBuf *)
ImBuf * make_zebra_view_from_ibuf(const ImBuf *ibuf, float perc)
static void scope_put_pixel(const uchar *table, uchar *pos)
ImBuf * make_waveform_view_from_ibuf(const ImBuf *ibuf)
static void scope_put_pixel_single(const uchar *table, uchar *pos, int col)
ImBuf * make_vectorscope_view_from_ibuf(const ImBuf *ibuf)
static void init_wave_table(int height, uchar wtable[256])
ImBuf * make_sep_waveform_view_from_ibuf(const ImBuf *ibuf)
static blender::float2 rgb_to_uv_normalized(const float rgb[3])
T midpoint(const T &a, const T &b)
T max(const T &a, const T &b)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
Definition BLI_task.hh:95
Value parallel_reduce(IndexRange range, int64_t grain_size, const Value &identity, const Function &function, const Reduction &reduction)
Definition BLI_task.hh:153
VecBase< uint32_t, 3 > uint3
VecBase< float, 2 > float2
ImBufFloatBuffer float_buffer
ImBufByteBuffer byte_buffer