VSE: Scopes improvements #116798

Merged
Aras Pranckevicius merged 13 commits from aras_p/blender:vse-scopes into main 2024-01-05 22:03:13 +01:00
4 changed files with 306 additions and 316 deletions
Showing only changes of commit 981f5e926f - Show all commits

View File

@ -50,7 +50,7 @@ struct SpaceSeq_Runtime {
int rename_channel_index = 0;
float timeline_clamp_custom_range = 0;
SequencerScopes scopes;
blender::ed::seq::SeqScopes scopes;
SpaceSeq_Runtime() = default;
SpaceSeq_Runtime(const SpaceSeq_Runtime &) = delete;
aras_p marked this conversation as resolved Outdated

BLI_utility_mixins.hh makes this a bit nicer, with NonCopyable

`BLI_utility_mixins.hh` makes this a bit nicer, with `NonCopyable`

View File

@ -57,6 +57,7 @@
#include "WM_types.hh"
#include "sequencer_intern.hh"
#include "sequencer_quads_batch.hh"
#include "sequencer_scopes.hh"
static Sequence *special_seq_update = nullptr;
@ -158,36 +159,6 @@ ImBuf *sequencer_ibuf_get(Main *bmain,
return ibuf;
}
static void sequencer_check_scopes(SequencerScopes *scopes, ImBuf *ibuf)
{
if (scopes->reference_ibuf != ibuf) {
if (scopes->zebra_ibuf) {
IMB_freeImBuf(scopes->zebra_ibuf);
scopes->zebra_ibuf = nullptr;
}
if (scopes->waveform_ibuf) {
IMB_freeImBuf(scopes->waveform_ibuf);
scopes->waveform_ibuf = nullptr;
}
if (scopes->sep_waveform_ibuf) {
IMB_freeImBuf(scopes->sep_waveform_ibuf);
scopes->sep_waveform_ibuf = nullptr;
}
if (scopes->vector_ibuf) {
IMB_freeImBuf(scopes->vector_ibuf);
scopes->vector_ibuf = nullptr;
}
if (scopes->histogram_ibuf) {
IMB_freeImBuf(scopes->histogram_ibuf);
scopes->histogram_ibuf = nullptr;
}
}
}
static ImBuf *sequencer_make_scope(Scene *scene, ImBuf *ibuf, ImBuf *(*make_scope_fn)(ImBuf *ibuf))
{
ImBuf *display_ibuf = IMB_dupImBuf(ibuf);
@ -451,11 +422,9 @@ static void sequencer_draw_display_buffer(const bContext *C,
ARegion *region,
SpaceSeq *sseq,
ImBuf *ibuf,
ImBuf *scope,
bool draw_overlay,
bool draw_backdrop)
{
void *display_buffer;
void *buffer_cache_handle = nullptr;
if (sseq->mainb == SEQ_DRAW_IMG_IMBUF && sseq->flag & SEQ_USE_ALPHA) {
@ -472,21 +441,8 @@ static void sequencer_draw_display_buffer(const bContext *C,
uint texCoord = GPU_vertformat_attr_add(
imm_format, "texCoord", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
if (scope) {
ibuf = scope;
if (ibuf->float_buffer.data && ibuf->byte_buffer.data == nullptr) {
IMB_rect_from_float(ibuf);
}
display_buffer = ibuf->byte_buffer.data;
format = GPU_RGBA8;
data = GPU_DATA_UBYTE;
}
else {
display_buffer = sequencer_OCIO_transform_ibuf(
C, ibuf, &glsl_used, &format, &data, &buffer_cache_handle);
}
void *display_buffer = sequencer_OCIO_transform_ibuf(
C, ibuf, &glsl_used, &format, &data, &buffer_cache_handle);
if (draw_backdrop) {
GPU_matrix_push();
@ -558,68 +514,219 @@ static void sequencer_draw_display_buffer(const bContext *C,
}
}
static ImBuf *sequencer_get_scope(Scene *scene, SpaceSeq *sseq, ImBuf *ibuf, bool draw_backdrop)
static void draw_histogram(const blender::ed::seq::ScopeHistogram &hist,
SeqQuadsBatch &quads,
const rctf &area)
{
ImBuf *scope = nullptr;
SequencerScopes *scopes = &sseq->runtime->scopes;
if (hist.data.is_empty()) {
return;
}
if (!draw_backdrop && (sseq->mainb != SEQ_DRAW_IMG_IMBUF || sseq->zebra != 0)) {
sequencer_check_scopes(scopes, ibuf);
uchar col_grid[4] = {128, 128, 128, 128};
switch (sseq->mainb) {
case SEQ_DRAW_IMG_IMBUF:
if (!scopes->zebra_ibuf) {
/* Grid lines. */
float grid_x_0 = area.xmin;
float grid_x_1 = area.xmax;
if (hist.data.size() > 256) {
grid_x_0 = area.xmin + (area.xmax - area.xmin) * (0.25f / 1.5f);
grid_x_1 = area.xmin + (area.xmax - area.xmin) * (1.25f / 1.5f);
}
for (int line = 0; line <= 4; line++) {
float x = grid_x_0 + (grid_x_1 - grid_x_0) * line / 4;
quads.add_line(x, area.ymin, x, area.ymax, col_grid);
}
if (ibuf->float_buffer.data) {
ImBuf *display_ibuf = IMB_dupImBuf(ibuf);
IMB_colormanagement_imbuf_make_display_space(
display_ibuf, &scene->view_settings, &scene->display_settings);
scopes->zebra_ibuf = make_zebra_view_from_ibuf(display_ibuf, sseq->zebra);
IMB_freeImBuf(display_ibuf);
}
else {
scopes->zebra_ibuf = make_zebra_view_from_ibuf(ibuf, sseq->zebra);
}
}
scope = scopes->zebra_ibuf;
break;
case SEQ_DRAW_IMG_WAVEFORM:
if ((sseq->flag & SEQ_DRAW_COLOR_SEPARATED) != 0) {
if (!scopes->sep_waveform_ibuf) {
scopes->sep_waveform_ibuf = sequencer_make_scope(
scene, ibuf, make_sep_waveform_view_from_ibuf);
}
scope = scopes->sep_waveform_ibuf;
}
else {
if (!scopes->waveform_ibuf) {
scopes->waveform_ibuf = sequencer_make_scope(
scene, ibuf, make_waveform_view_from_ibuf);
}
scope = scopes->waveform_ibuf;
}
break;
case SEQ_DRAW_IMG_VECTORSCOPE:
if (!scopes->vector_ibuf) {
scopes->vector_ibuf = sequencer_make_scope(scene, ibuf, make_vectorscope_view_from_ibuf);
}
scope = scopes->vector_ibuf;
break;
case SEQ_DRAW_IMG_HISTOGRAM:
if (!scopes->histogram_ibuf) {
scopes->histogram_ibuf = sequencer_make_scope(
scene, ibuf, make_histogram_view_from_ibuf);
}
scope = scopes->histogram_ibuf;
break;
/* Histogram area & line for each R/G/B channels, additively blended. */
quads.draw();
aras_p marked this conversation as resolved Outdated

This looks a little bit cursed, along with (0.25f / 1.5f). It wasn't obvious to me, that float images do use 512 wide array and that this oversaples the image. Can you clarify this in a comment?

This looks a little bit cursed, along with `(0.25f / 1.5f)`. It wasn't obvious to me, that float images do use 512 wide array and that this oversaples the image. Can you clarify this in a comment?

Good point, will add named constants for these things

Good point, will add named constants for these things
GPU_blend(GPU_BLEND_ADDITIVE);
for (int ch = 0; ch < 3; ++ch) {
if (hist.max_value[ch] == 0) {
continue;
}
uchar col_line[4] = {32, 32, 32, 255};
aras_p marked this conversation as resolved Outdated

You can use UI_view2d_scale_get_x() here

You can use `UI_view2d_scale_get_x()` here
uchar col_area[4] = {64, 64, 64, 128};
col_line[ch] = 224;
col_area[ch] = 224;
float y_scale = (area.ymax - area.ymin) / hist.max_value[ch] * 0.95f;
float x_scale = (area.xmax - area.xmin) / hist.data.size();
float yb = area.ymin;
for (int bin = 0; bin < hist.data.size() - 1; bin++) {
float x0 = area.xmin + (bin + 0.5f) * x_scale;
float x1 = area.xmin + (bin + 1.5f) * x_scale;
aras_p marked this conversation as resolved
Review

Use BLI_snprintf()

Use `BLI_snprintf()`
/* Future files may have new scopes we don't catch above. */
if (scope) {
scopes->reference_ibuf = ibuf;
float y0 = area.ymin + hist.data[bin][ch] * y_scale;
float y1 = area.ymin + hist.data[bin + 1][ch] * y_scale;
quads.add_quad(x0, yb, x0, y0, x1, yb, x1, y1, col_area);
quads.add_line(x0, y0, x1, y1, col_line);
}
}
return scope;
quads.draw();
GPU_blend(GPU_BLEND_ALPHA);
}
static void sequencer_draw_scopes(Scene *scene, ARegion *region, SpaceSeq *sseq, bool draw_overlay)
{
using namespace blender::ed::seq;
/* Figure out draw coordinates. */
rctf preview;
rctf canvas;
sequencer_preview_get_rect(&preview, scene, region, sseq, draw_overlay, false);
if (draw_overlay && (sseq->overlay_frame_type == SEQ_OVERLAY_FRAME_TYPE_RECT)) {
canvas = scene->ed->overlay_frame_rect;
}
else {
BLI_rctf_init(&canvas, 0.0f, 1.0f, 0.0f, 1.0f);
}
SeqQuadsBatch quads;
SeqScopes *scopes = &sseq->runtime->scopes;
/* Draw scope image if there is one. */
bool use_blend = sseq->mainb == SEQ_DRAW_IMG_IMBUF && sseq->flag & SEQ_USE_ALPHA;
ImBuf *scope_image = nullptr;
if (sseq->mainb == SEQ_DRAW_IMG_IMBUF) {
scope_image = scopes->zebra_ibuf;
}
else if (sseq->mainb == SEQ_DRAW_IMG_WAVEFORM) {
scope_image = (sseq->flag & SEQ_DRAW_COLOR_SEPARATED) != 0 ? scopes->sep_waveform_ibuf :
scopes->waveform_ibuf;
}
else if (sseq->mainb == SEQ_DRAW_IMG_VECTORSCOPE) {
scope_image = scopes->vector_ibuf;
}
else if (sseq->mainb == SEQ_DRAW_IMG_HISTOGRAM) {
use_blend = true;
}
if (use_blend) {
GPU_blend(GPU_BLEND_ALPHA);
}
if (scope_image != nullptr) {
if (scope_image->float_buffer.data && scope_image->byte_buffer.data == nullptr) {
IMB_rect_from_float(scope_image);
}
eGPUTextureFormat format = GPU_RGBA8;
eGPUDataFormat data = GPU_DATA_UBYTE;
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_ATTACHMENT;
GPUTexture *texture = GPU_texture_create_2d(
"seq_display_buf", scope_image->x, scope_image->y, 1, format, usage, nullptr);
GPU_texture_update(texture, data, scope_image->byte_buffer.data);
GPU_texture_filter_mode(texture, false);
aras_p marked this conversation as resolved
Review

Use BLI_snprintf()

Use `BLI_snprintf()`
GPU_texture_bind(texture, 0);
GPUVertFormat *imm_format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(imm_format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
uint texCoord = GPU_vertformat_attr_add(
imm_format, "texCoord", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_3D_IMAGE_COLOR);
immUniformColor3f(1.0f, 1.0f, 1.0f);
immBegin(GPU_PRIM_TRI_FAN, 4);
immAttr2f(texCoord, canvas.xmin, canvas.ymin);
immVertex2f(pos, preview.xmin, preview.ymin);
immAttr2f(texCoord, canvas.xmin, canvas.ymax);
immVertex2f(pos, preview.xmin, preview.ymax);
immAttr2f(texCoord, canvas.xmax, canvas.ymax);
immVertex2f(pos, preview.xmax, preview.ymax);
immAttr2f(texCoord, canvas.xmax, canvas.ymin);
immVertex2f(pos, preview.xmax, preview.ymin);
immEnd();
GPU_texture_unbind(texture);
GPU_texture_free(texture);
immUnbindProgram();
}
uchar col_bg[4] = {0, 0, 0, 255};
uchar col_border[4] = {0, 160, 0, 255};
if (scope_image == nullptr) {
quads.add_quad(preview.xmin, preview.ymin, preview.xmax, preview.ymax, col_bg);
}
if (sseq->mainb == SEQ_DRAW_IMG_HISTOGRAM) {
draw_histogram(scopes->histogram, quads, preview);
}
if (scope_image == nullptr) {
quads.add_wire_quad(preview.xmin, preview.ymin, preview.xmax, preview.ymax, col_border);
}
quads.draw();
if (use_blend) {
GPU_blend(GPU_BLEND_NONE);
}
}
static bool sequencer_calc_scopes(Scene *scene, SpaceSeq *sseq, ImBuf *ibuf, bool draw_backdrop)
{
using namespace blender::ed::seq;
if (draw_backdrop || (sseq->mainb == SEQ_DRAW_IMG_IMBUF && sseq->zebra == 0)) {
return false; /* Not drawing any scopes. */
}
SeqScopes *scopes = &sseq->runtime->scopes;
if (scopes->reference_ibuf != ibuf) {
scopes->cleanup();
}
switch (sseq->mainb) {
aras_p marked this conversation as resolved Outdated

I mean, it is a solution :) Just to be clear, no need to do anything about this, just wanted to say, that I laughed a bit.
Eh actually, I noticed a missing full stop at the comment, so that should be fixed.

I mean, it is a solution :) Just to be clear, no need to do anything about this, just wanted to say, that I laughed a bit. Eh actually, I noticed a missing full stop at the comment, so that should be fixed.
case SEQ_DRAW_IMG_IMBUF:
if (!scopes->zebra_ibuf) {
if (ibuf->float_buffer.data) {
ImBuf *display_ibuf = IMB_dupImBuf(ibuf);
IMB_colormanagement_imbuf_make_display_space(
display_ibuf, &scene->view_settings, &scene->display_settings);
scopes->zebra_ibuf = make_zebra_view_from_ibuf(display_ibuf, sseq->zebra);
IMB_freeImBuf(display_ibuf);
}
else {
scopes->zebra_ibuf = make_zebra_view_from_ibuf(ibuf, sseq->zebra);
}
}
break;
case SEQ_DRAW_IMG_WAVEFORM:
if ((sseq->flag & SEQ_DRAW_COLOR_SEPARATED) != 0) {
aras_p marked this conversation as resolved Outdated

I am bit lost here, why do you need overlay rect for scope?

I am bit lost here, why do you need overlay rect for scope?

Nice catch! I don't.

Nice catch! I don't.
if (!scopes->sep_waveform_ibuf) {
scopes->sep_waveform_ibuf = sequencer_make_scope(
scene, ibuf, make_sep_waveform_view_from_ibuf);
}
}
else {
if (!scopes->waveform_ibuf) {
scopes->waveform_ibuf = sequencer_make_scope(scene, ibuf, make_waveform_view_from_ibuf);
}
}
break;
case SEQ_DRAW_IMG_VECTORSCOPE:
if (!scopes->vector_ibuf) {
scopes->vector_ibuf = sequencer_make_scope(scene, ibuf, make_vectorscope_view_from_ibuf);
}
break;
case SEQ_DRAW_IMG_HISTOGRAM: {
ImBuf *display_ibuf = IMB_dupImBuf(ibuf);
IMB_colormanagement_imbuf_make_display_space(
display_ibuf, &scene->view_settings, &scene->display_settings);
scopes->histogram.calc_from_ibuf(display_ibuf);
IMB_freeImBuf(display_ibuf);
} break;
default: /* Future files might have scopes we don't know about. */
return false;
}
scopes->reference_ibuf = ibuf;
return true;
}
bool sequencer_draw_get_transform_preview(SpaceSeq *sseq, Scene *scene)
@ -732,7 +839,6 @@ void sequencer_draw_preview(const bContext *C,
Depsgraph *depsgraph = CTX_data_expect_evaluated_depsgraph(C);
View2D *v2d = &region->v2d;
ImBuf *ibuf = nullptr;
ImBuf *scope = nullptr;
float viewrect[2];
const bool show_imbuf = ED_space_sequencer_check_show_imbuf(sseq);
const bool draw_gpencil = ((sseq->preview_overlay.flag & SEQ_PREVIEW_SHOW_GPENCIL) && sseq->gpd);
@ -781,13 +887,17 @@ void sequencer_draw_preview(const bContext *C,
}
if (ibuf) {
scope = sequencer_get_scope(scene, sseq, ibuf, draw_backdrop);
bool has_scope = sequencer_calc_scopes(scene, sseq, ibuf, draw_backdrop);
if (has_scope) {
/* Draw scope. */
sequencer_draw_scopes(scene, region, sseq, draw_overlay);
}
else {
/* Draw image. */
sequencer_draw_display_buffer(C, scene, region, sseq, ibuf, draw_overlay, draw_backdrop);
}
/* Draw image. */
sequencer_draw_display_buffer(
C, scene, region, sseq, ibuf, scope, draw_overlay, draw_backdrop);
/* Draw over image. */
/* Draw metadata. */
if (sseq->preview_overlay.flag & SEQ_PREVIEW_SHOW_METADATA && sseq->flag & SEQ_SHOW_OVERLAY) {
ED_region_image_metadata_draw(0.0, 0.0, ibuf, &v2d->tot, 1.0, 1.0);
}
@ -821,7 +931,6 @@ void sequencer_draw_preview(const bContext *C,
ED_region_draw_cb_draw(C, region, REGION_DRAW_POST_VIEW);
GPU_framebuffer_bind_no_srgb(framebuffer_overlay);
/* Scope is freed in sequencer_check_scopes when `ibuf` changes and redraw is needed. */
if (ibuf) {
IMB_freeImBuf(ibuf);
}

View File

@ -9,6 +9,7 @@
#include <cmath>
#include <cstring>
#include "BLI_math_vector.hh"
#include "BLI_task.h"
#include "BLI_task.hh"
#include "BLI_utildefines.h"
@ -25,23 +26,32 @@
# include "BLI_timeit.hh"
#endif
SequencerScopes::~SequencerScopes()
namespace blender::ed::seq {
SeqScopes::~SeqScopes()
{
cleanup();
}
void SeqScopes::cleanup()
{
if (zebra_ibuf) {
IMB_freeImBuf(zebra_ibuf);
zebra_ibuf = nullptr;
}
if (waveform_ibuf) {
IMB_freeImBuf(waveform_ibuf);
waveform_ibuf = nullptr;
}
if (sep_waveform_ibuf) {
IMB_freeImBuf(sep_waveform_ibuf);
sep_waveform_ibuf = nullptr;
}
if (vector_ibuf) {
IMB_freeImBuf(vector_ibuf);
vector_ibuf = nullptr;
}
if (histogram_ibuf) {
IMB_freeImBuf(histogram_ibuf);
}
histogram.data.reinitialize(0);
}
/* XXX(@ideasman42): why is this function better than BLI_math version?
@ -459,127 +469,7 @@ ImBuf *make_zebra_view_from_ibuf(ImBuf *ibuf, float perc)
return new_ibuf;
}
static void draw_histogram_marker(ImBuf *ibuf, int x)
{
uchar *p = ibuf->byte_buffer.data;
int barh = ibuf->y * 0.1;
p += 4 * (x + ibuf->x * (ibuf->y - barh + 1));
for (int i = 0; i < barh - 1; i++) {
p[0] = p[1] = p[2] = 255;
p += ibuf->x * 4;
}
}
static void draw_histogram_bar(ImBuf *ibuf, int x, float val, int col)
{
uchar *p = ibuf->byte_buffer.data;
int barh = ibuf->y * val * 0.9f;
p += 4 * (x + ibuf->x);
for (int i = 0; i < barh; i++) {
p[col] = 255;
p += ibuf->x * 4;
}
}
#define HIS_STEPS 512
struct MakeHistogramViewData {
const ImBuf *ibuf;
};
static void make_histogram_view_from_ibuf_byte_fn(void *__restrict userdata,
const int y,
const TaskParallelTLS *__restrict tls)
{
const MakeHistogramViewData *data = static_cast<MakeHistogramViewData *>(userdata);
const ImBuf *ibuf = data->ibuf;
const uchar *src = ibuf->byte_buffer.data;
uint32_t(*cur_bins)[HIS_STEPS] = static_cast<uint32_t(*)[HIS_STEPS]>(tls->userdata_chunk);
for (int x = 0; x < ibuf->x; x++) {
const uchar *pixel = src + (y * ibuf->x + x) * 4;
for (int j = 3; j--;) {
cur_bins[j][pixel[j]]++;
}
}
}
static void make_histogram_view_from_ibuf_reduce(const void *__restrict /*userdata*/,
void *__restrict chunk_join,
void *__restrict chunk)
{
uint32_t(*join_bins)[HIS_STEPS] = static_cast<uint32_t(*)[HIS_STEPS]>(chunk_join);
uint32_t(*bins)[HIS_STEPS] = static_cast<uint32_t(*)[HIS_STEPS]>(chunk);
for (int j = 3; j--;) {
for (int i = 0; i < HIS_STEPS; i++) {
join_bins[j][i] += bins[j][i];
}
}
}
static ImBuf *make_histogram_view_from_ibuf_byte(ImBuf *ibuf)
{
#ifdef DEBUG_TIME
SCOPED_TIMER_AVERAGED(__func__);
#endif
ImBuf *rval = IMB_allocImBuf(515, 128, 32, IB_rect);
int x;
uint nr, ng, nb;
uint bins[3][HIS_STEPS];
memset(bins, 0, sizeof(bins));
MakeHistogramViewData data{};
data.ibuf = ibuf;
TaskParallelSettings settings;
BLI_parallel_range_settings_defaults(&settings);
settings.use_threading = (ibuf->y >= 256);
settings.userdata_chunk = bins;
settings.userdata_chunk_size = sizeof(bins);
settings.func_reduce = make_histogram_view_from_ibuf_reduce;
BLI_task_parallel_range(0, ibuf->y, &data, make_histogram_view_from_ibuf_byte_fn, &settings);
nr = nb = ng = 0;
for (x = 0; x < HIS_STEPS; x++) {
if (bins[0][x] > nr) {
nr = bins[0][x];
}
if (bins[1][x] > ng) {
ng = bins[1][x];
}
if (bins[2][x] > nb) {
nb = bins[2][x];
}
}
for (x = 0; x < HIS_STEPS; x++) {
if (nr) {
draw_histogram_bar(rval, x * 2 + 1, float(bins[0][x]) / nr, 0);
draw_histogram_bar(rval, x * 2 + 2, float(bins[0][x]) / nr, 0);
}
if (ng) {
draw_histogram_bar(rval, x * 2 + 1, float(bins[1][x]) / ng, 1);
draw_histogram_bar(rval, x * 2 + 2, float(bins[1][x]) / ng, 1);
}
if (nb) {
draw_histogram_bar(rval, x * 2 + 1, float(bins[2][x]) / nb, 2);
draw_histogram_bar(rval, x * 2 + 2, float(bins[2][x]) / nb, 2);
}
}
wform_put_border(rval->byte_buffer.data, rval->x, rval->y);
return rval;
}
BLI_INLINE int get_bin_float(float f)
static int get_bin_float(float f)
{
if (f < -0.25f) {
return 0;
@ -587,92 +477,65 @@ BLI_INLINE int get_bin_float(float f)
if (f >= 1.25f) {
return 511;
}
return int(((f + 0.25f) / 1.5f) * 512);
}
static void make_histogram_view_from_ibuf_float_fn(void *__restrict userdata,
const int y,
const TaskParallelTLS *__restrict tls)
{
const MakeHistogramViewData *data = static_cast<const MakeHistogramViewData *>(userdata);
const ImBuf *ibuf = static_cast<const ImBuf *>(data->ibuf);
const float *src = ibuf->float_buffer.data;
uint32_t(*cur_bins)[HIS_STEPS] = static_cast<uint32_t(*)[HIS_STEPS]>(tls->userdata_chunk);
for (int x = 0; x < ibuf->x; x++) {
const float *pixel = src + (y * ibuf->x + x) * 4;
for (int j = 3; j--;) {
cur_bins[j][get_bin_float(pixel[j])]++;
}
}
}
static ImBuf *make_histogram_view_from_ibuf_float(ImBuf *ibuf)
void ScopeHistogram::calc_from_ibuf(const ImBuf *ibuf)
{
#ifdef DEBUG_TIME
SCOPED_TIMER_AVERAGED(__func__);
#endif
ImBuf *rval = IMB_allocImBuf(515, 128, 32, IB_rect);
int nr, ng, nb;
int x;
uint bins[3][HIS_STEPS];
const bool is_float = ibuf->float_buffer.data != nullptr;
const int hist_size = is_float ? 512 : 256;
memset(bins, 0, sizeof(bins));
Array<uint3> counts(hist_size, uint3(0));
data = threading::parallel_reduce(
IndexRange(ibuf->y),
256,
counts,
[&](const IndexRange y_range, const Array<uint3> &init) {
Array<uint3> res = init;
MakeHistogramViewData data{};
data.ibuf = ibuf;
TaskParallelSettings settings;
BLI_parallel_range_settings_defaults(&settings);
settings.use_threading = (ibuf->y >= 256);
settings.userdata_chunk = bins;
settings.userdata_chunk_size = sizeof(bins);
settings.func_reduce = make_histogram_view_from_ibuf_reduce;
BLI_task_parallel_range(0, ibuf->y, &data, make_histogram_view_from_ibuf_float_fn, &settings);
if (is_float) {
/* Float images spead -0.25..+1.25 range over 512 bins. */
for (const int y : y_range) {
const float *src = ibuf->float_buffer.data + y * ibuf->x * 4;
for (int x = 0; x < ibuf->x; x++) {
res[get_bin_float(src[0])].x++;
res[get_bin_float(src[1])].y++;
res[get_bin_float(src[2])].z++;
src += 4;
}
}
}
else {
/* Byte images just use 256 histogram bins, directly indexed by value. */
for (const int y : y_range) {
const uchar *src = ibuf->byte_buffer.data + y * ibuf->x * 4;
for (int x = 0; x < ibuf->x; x++) {
res[src[0]].x++;
res[src[1]].y++;
res[src[2]].z++;
src += 4;
}
}
}
return res;
},
[&](const Array<uint3> &a, const Array<uint3> &b) {
BLI_assert(a.size() == b.size());
Array<uint3> res(a.size());
for (int i = 0; i < a.size(); i++) {
res[i] = a[i] + b[i];
}
return res;
});
nr = nb = ng = 0;
for (x = 0; x < HIS_STEPS; x++) {
if (bins[0][x] > nr) {
nr = bins[0][x];
}
if (bins[1][x] > ng) {
ng = bins[1][x];
}
if (bins[2][x] > nb) {
nb = bins[2][x];
}
max_value = uint3(0);
for (const uint3 &v : data) {
max_value = math::max(max_value, v);
}
for (x = 0; x < HIS_STEPS; x++) {
if (nr) {
draw_histogram_bar(rval, x + 1, float(bins[0][x]) / nr, 0);
}
if (ng) {
draw_histogram_bar(rval, x + 1, float(bins[1][x]) / ng, 1);
}
if (nb) {
draw_histogram_bar(rval, x + 1, float(bins[2][x]) / nb, 2);
}
}
draw_histogram_marker(rval, get_bin_float(0.0));
draw_histogram_marker(rval, get_bin_float(1.0));
wform_put_border(rval->byte_buffer.data, rval->x, rval->y);
return rval;
}
#undef HIS_STEPS
ImBuf *make_histogram_view_from_ibuf(ImBuf *ibuf)
{
if (ibuf->float_buffer.data) {
return make_histogram_view_from_ibuf_float(ibuf);
}
return make_histogram_view_from_ibuf_byte(ibuf);
}
static void vectorscope_put_cross(uchar r, uchar g, uchar b, uchar *tgt, int w, int h, int size)
@ -806,3 +669,5 @@ ImBuf *make_vectorscope_view_from_ibuf(ImBuf *ibuf)
}
return make_vectorscope_view_from_ibuf_byte(ibuf);
}
} // namespace blender::ed::seq

View File

@ -8,24 +8,40 @@
#pragma once
#include "BLI_array.hh"
#include "BLI_math_vector_types.hh"
struct ImBuf;
struct SequencerScopes {
SequencerScopes() = default;
~SequencerScopes();
SequencerScopes(const SequencerScopes &) = delete;
void operator=(const SequencerScopes &) = delete;
namespace blender::ed::seq {
struct ScopeHistogram {
Array<uint3> data;
uint3 max_value;
void calc_from_ibuf(const ImBuf *ibuf);
};
struct SeqScopes {
SeqScopes() = default;
~SeqScopes();
SeqScopes(const SeqScopes &) = delete;
void operator=(const SeqScopes &) = delete;
void cleanup();
ImBuf *reference_ibuf = nullptr;
aras_p marked this conversation as resolved Outdated

The style guide mentions putting member variables before method declarations, might as well stay consistent

The style guide mentions putting member variables before method declarations, might as well stay consistent
ImBuf *zebra_ibuf = nullptr;
ImBuf *waveform_ibuf = nullptr;
ImBuf *sep_waveform_ibuf = nullptr;
ImBuf *vector_ibuf = nullptr;
ImBuf *histogram_ibuf = nullptr;
ScopeHistogram histogram;
};
ImBuf *make_waveform_view_from_ibuf(ImBuf *ibuf);
ImBuf *make_sep_waveform_view_from_ibuf(ImBuf *ibuf);
ImBuf *make_vectorscope_view_from_ibuf(ImBuf *ibuf);
ImBuf *make_zebra_view_from_ibuf(ImBuf *ibuf, float perc);
ImBuf *make_histogram_view_from_ibuf(ImBuf *ibuf);
} // namespace blender::ed::seq