VSE: Scopes improvements #116798
|
@ -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
|
||||
|
|
|
@ -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
Richard Antalik
commented
This looks a little bit cursed, along with 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?
Aras Pranckevicius
commented
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
Richard Antalik
commented
You can use 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
Richard Antalik
commented
Use 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
Richard Antalik
commented
Use 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
Richard Antalik
commented
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. 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
Richard Antalik
commented
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?
Aras Pranckevicius
commented
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 = ®ion->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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
Hans Goudey
commented
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
|
||||
|
|
Loading…
Reference in New Issue
BLI_utility_mixins.hh
makes this a bit nicer, withNonCopyable