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;

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();
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};
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) {
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) {
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;
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