diff --git a/source/blender/blenloader/intern/versioning_260.cc b/source/blender/blenloader/intern/versioning_260.cc index 354b65732af..e3fc9481f64 100644 --- a/source/blender/blenloader/intern/versioning_260.cc +++ b/source/blender/blenloader/intern/versioning_260.cc @@ -34,6 +34,7 @@ #include "DNA_object_types.h" #include "DNA_screen_types.h" #include "DNA_sdna_types.h" +#include "DNA_sequence_types.h" #include "DNA_space_types.h" #include "DNA_text_types.h" #include "DNA_view3d_types.h" diff --git a/source/blender/blenloader/intern/versioning_280.cc b/source/blender/blenloader/intern/versioning_280.cc index 2a1673e80e9..c2d1cc60242 100644 --- a/source/blender/blenloader/intern/versioning_280.cc +++ b/source/blender/blenloader/intern/versioning_280.cc @@ -50,6 +50,7 @@ #include "DNA_rigidbody_types.h" #include "DNA_scene_types.h" #include "DNA_screen_types.h" +#include "DNA_sequence_types.h" #include "DNA_shader_fx_types.h" #include "DNA_text_types.h" #include "DNA_texture_types.h" diff --git a/source/blender/blenloader/intern/versioning_290.cc b/source/blender/blenloader/intern/versioning_290.cc index 75bdcfd5232..9015fa2e539 100644 --- a/source/blender/blenloader/intern/versioning_290.cc +++ b/source/blender/blenloader/intern/versioning_290.cc @@ -37,6 +37,7 @@ #include "DNA_pointcloud_types.h" #include "DNA_rigidbody_types.h" #include "DNA_screen_types.h" +#include "DNA_sequence_types.h" #include "DNA_shader_fx_types.h" #include "DNA_space_types.h" #include "DNA_text_types.h" diff --git a/source/blender/blenloader/intern/versioning_300.cc b/source/blender/blenloader/intern/versioning_300.cc index 11a22d0a4ac..cbe1ed2e4e2 100644 --- a/source/blender/blenloader/intern/versioning_300.cc +++ b/source/blender/blenloader/intern/versioning_300.cc @@ -46,6 +46,7 @@ #include "DNA_modifier_types.h" #include "DNA_movieclip_types.h" #include "DNA_screen_types.h" +#include "DNA_sequence_types.h" #include "DNA_space_types.h" #include "DNA_text_types.h" #include "DNA_tracking_types.h" diff --git a/source/blender/editors/space_sequencer/CMakeLists.txt b/source/blender/editors/space_sequencer/CMakeLists.txt index f6656599d85..2fe7439d926 100644 --- a/source/blender/editors/space_sequencer/CMakeLists.txt +++ b/source/blender/editors/space_sequencer/CMakeLists.txt @@ -46,6 +46,7 @@ set(SRC sequencer_intern.hh sequencer_quads_batch.hh + sequencer_scopes.hh ) set(LIB @@ -73,6 +74,16 @@ if(WITH_AUDASPACE) add_definitions(-DWITH_AUDASPACE) endif() +if(WITH_TBB) + add_definitions(-DWITH_TBB) + list(APPEND INC_SYS + ${TBB_INCLUDE_DIRS} + ) + list(APPEND LIB + ${TBB_LIBRARIES} + ) +endif() + blender_add_lib(bf_editor_space_sequencer "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/editors/space_sequencer/sequencer_channels_draw.cc b/source/blender/editors/space_sequencer/sequencer_channels_draw.cc index b43bce18097..279baf479a2 100644 --- a/source/blender/editors/space_sequencer/sequencer_channels_draw.cc +++ b/source/blender/editors/space_sequencer/sequencer_channels_draw.cc @@ -179,7 +179,7 @@ static float draw_channel_widget_lock(const SeqChannelDrawContext *context, static bool channel_is_being_renamed(const SpaceSeq *sseq, const int channel_index) { - return sseq->runtime.rename_channel_index == channel_index; + return sseq->runtime->rename_channel_index == channel_index; } static float text_size_get(const SeqChannelDrawContext *context) @@ -250,7 +250,7 @@ static void draw_channel_labels(const SeqChannelDrawContext *context, UI_block_emboss_set(block, UI_EMBOSS_NONE); if (UI_but_active_only(context->C, context->region, block, but) == false) { - sseq->runtime.rename_channel_index = 0; + sseq->runtime->rename_channel_index = 0; } WM_event_add_notifier(context->C, NC_SCENE | ND_SEQUENCER, context->scene); diff --git a/source/blender/editors/space_sequencer/sequencer_channels_edit.cc b/source/blender/editors/space_sequencer/sequencer_channels_edit.cc index df24b3f5a9c..549711cfbc3 100644 --- a/source/blender/editors/space_sequencer/sequencer_channels_edit.cc +++ b/source/blender/editors/space_sequencer/sequencer_channels_edit.cc @@ -38,7 +38,7 @@ static int sequencer_rename_channel_invoke(bContext *C, wmOperator * /*op*/, con channel_draw_context_init(C, CTX_wm_region(C), &context); float mouse_y = UI_view2d_region_to_view_y(context.timeline_region_v2d, event->mval[1]); - sseq->runtime.rename_channel_index = mouse_y; + sseq->runtime->rename_channel_index = mouse_y; WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, CTX_data_scene(C)); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/space_sequencer/sequencer_intern.hh b/source/blender/editors/space_sequencer/sequencer_intern.hh index cbbb220adcd..cb8d9c71069 100644 --- a/source/blender/editors/space_sequencer/sequencer_intern.hh +++ b/source/blender/editors/space_sequencer/sequencer_intern.hh @@ -8,11 +8,14 @@ #pragma once +#include "BLI_utility_mixins.hh" #include "BLI_vector.hh" #include "BLI_vector_set.hh" #include "DNA_sequence_types.h" #include "RNA_access.hh" +#include "sequencer_scopes.hh" + /* Internal exports only. */ class SeqQuadsBatch; @@ -40,6 +43,24 @@ struct ListBase; #define DEFAULT_IMG_STRIP_LENGTH 25 /* XXX arbitrary but ok for now. */ #define OVERLAP_ALPHA 180 +namespace blender::ed::seq { + +struct SpaceSeq_Runtime : public NonCopyable { + /** Required for Thumbnail job start condition. */ + rctf last_thumbnail_area = {0, 0, 0, 0}; + /** Stores lists of most recently displayed thumbnails. */ + GHash *last_displayed_thumbnails = nullptr; + int rename_channel_index = 0; + float timeline_clamp_custom_range = 0; + + blender::ed::seq::SeqScopes scopes; + + SpaceSeq_Runtime() = default; + ~SpaceSeq_Runtime(); +}; + +} // namespace blender::ed::seq + struct SeqChannelDrawContext { const bContext *C; ScrArea *area; @@ -258,14 +279,6 @@ void sequencer_dropboxes(); void sequencer_operatortypes(); void sequencer_keymap(wmKeyConfig *keyconf); -/* sequencer_scope.c */ - -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); - /* `sequencer_buttons.cc` */ void sequencer_buttons_register(ARegionType *art); diff --git a/source/blender/editors/space_sequencer/sequencer_preview_draw.cc b/source/blender/editors/space_sequencer/sequencer_preview_draw.cc index 8c7c685f1ef..0a963a4d83c 100644 --- a/source/blender/editors/space_sequencer/sequencer_preview_draw.cc +++ b/source/blender/editors/space_sequencer/sequencer_preview_draw.cc @@ -9,7 +9,10 @@ #include #include +#include "BLF_api.h" + #include "BLI_blenlib.h" +#include "BLI_math_rotation.h" #include "BLI_utildefines.h" #include "IMB_imbuf_types.h" @@ -56,8 +59,9 @@ #include "WM_api.hh" #include "WM_types.hh" -/* Own include. */ #include "sequencer_intern.hh" +#include "sequencer_quads_batch.hh" +#include "sequencer_scopes.hh" static Sequence *special_seq_update = nullptr; @@ -158,37 +162,9 @@ 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)) +static ImBuf *sequencer_make_scope(Scene *scene, + ImBuf *ibuf, + ImBuf *(*make_scope_fn)(const ImBuf *ibuf)) { ImBuf *display_ibuf = IMB_dupImBuf(ibuf); ImBuf *scope; @@ -451,11 +427,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 +446,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 +519,350 @@ 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(ARegion *region, + const blender::ed::seq::ScopeHistogram &hist, + SeqQuadsBatch &quads, + const rctf &area) { - ImBuf *scope = nullptr; - SequencerScopes *scopes = &sseq->scopes; + using namespace blender::ed::seq; + if (hist.data.is_empty()) { + return; + } - if (!draw_backdrop && (sseq->mainb != SEQ_DRAW_IMG_IMBUF || sseq->zebra != 0)) { - sequencer_check_scopes(scopes, ibuf); + /* Background. */ + uchar col_bg[4] = {0, 0, 0, 255}; + quads.add_quad(area.xmin, area.ymin, area.xmax, area.ymax, col_bg); - switch (sseq->mainb) { - case SEQ_DRAW_IMG_IMBUF: - if (!scopes->zebra_ibuf) { + /* Grid lines and labels. */ + uchar col_grid[4] = {128, 128, 128, 128}; + float grid_x_0 = area.xmin; + float grid_x_1 = area.xmax; + /* Float histograms show more than 0..1 range horizontally. */ + if (hist.is_float_hist()) { + float ratio_0 = ratiof(ScopeHistogram::FLOAT_VAL_MIN, ScopeHistogram::FLOAT_VAL_MAX, 0.0f); + float ratio_1 = ratiof(ScopeHistogram::FLOAT_VAL_MIN, ScopeHistogram::FLOAT_VAL_MAX, 1.0f); + grid_x_0 = area.xmin + (area.xmax - area.xmin) * ratio_0; + grid_x_1 = area.xmin + (area.xmax - area.xmin) * ratio_1; + } - 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; + View2D *v2d = ®ion->v2d; + float text_scale_x, text_scale_y; + UI_view2d_scale_get_inverse(v2d, &text_scale_x, &text_scale_y); + + for (int line = 0; line <= 4; line++) { + float val = float(line) / 4; + float x = grid_x_0 + (grid_x_1 - grid_x_0) * val; + quads.add_line(x, area.ymin, x, area.ymax, col_grid); + + /* Label. */ + char buf[10]; + BLI_snprintf(buf, sizeof(buf), "%.2f", val); + size_t buf_len = strlen(buf); + + float text_width, text_height; + BLF_width_and_height(BLF_default(), buf, buf_len, &text_width, &text_height); + text_width *= text_scale_x; + text_height *= text_scale_y; + UI_view2d_text_cache_add( + v2d, x - text_width / 2, area.ymax - text_height * 1.3f, buf, buf_len, col_grid); + } + + /* Border. */ + uchar col_border[4] = {64, 64, 64, 128}; + quads.add_wire_quad(area.xmin, area.ymin, area.xmax, area.ymax, col_border); + + /* 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; - /* 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); + + UI_view2d_text_cache_draw(region); +} + +static blender::float2 rgb_to_uv(const blender::float3 &rgb) +{ + float y, u, v; + rgb_to_yuv(rgb.x, rgb.y, rgb.z, &y, &u, &v, BLI_YUV_ITU_BT709); + return blender::float2(u, v); +} + +static void draw_waveform_graticule(ARegion *region, SeqQuadsBatch &quads, const rctf &area) +{ + /* Horizontal lines at 10%, 70%, 90%. */ + const float lines[3] = {0.1f, 0.7f, 0.9f}; + uchar col_grid[4] = {160, 64, 64, 128}; + const float x0 = area.xmin; + const float x1 = area.xmax; + + for (int i = 0; i < 3; i++) { + const float y = area.ymin + (area.ymax - area.ymin) * lines[i]; + char buf[10]; + BLI_snprintf(buf, sizeof(buf), "%.1f", lines[i]); + quads.add_line(x0, y, x1, y, col_grid); + UI_view2d_text_cache_add(®ion->v2d, x0 + 8, y + 8, buf, strlen(buf), col_grid); + } + /* Border. */ + uchar col_border[4] = {64, 64, 64, 128}; + quads.add_wire_quad(x0, area.ymin, x1, area.ymax, col_border); + + quads.draw(); + UI_view2d_text_cache_draw(region); +} + +static void draw_vectorscope_graticule(SeqQuadsBatch &quads, const rctf &area) +{ + using namespace blender; + GPU_blend(GPU_BLEND_ALPHA); + + const float skin_rad = DEG2RADF(123.0f); /* angle in radians of the skin tone line */ + + const float w = BLI_rctf_size_x(&area); + const float h = BLI_rctf_size_y(&area); + const float centerx = BLI_rctf_cent_x(&area); + const float centery = BLI_rctf_cent_y(&area); + const float rad_base = ((w < h) ? w : h) * 0.5f; + /* vectorscope image is scaled over YUV range, +/- (0.436, 0.615) */ + const float rad_x = rad_base * (0.5f / 0.436f); + const float rad_y = rad_base * (0.5f / 0.615f); + + /* center cross */ + uchar col_grid[4] = {128, 128, 128, 96}; + quads.add_line(centerx - rad_base * 0.1f, centery, centerx + rad_base * 0.1f, centery, col_grid); + quads.add_line(centerx, centery - rad_base * 0.1f, centerx, centery + rad_base * 0.1f, col_grid); + + /* fully saturated vs "safe" (0.75) colored areas */ + const float3 primaries[6] = { + {1, 0, 0}, + {1, 1, 0}, + {0, 1, 0}, + {0, 1, 1}, + {0, 0, 1}, + {1, 0, 1}, + }; + float2 center{centerx, centery}; + float2 rad_scale{rad_x * 2, rad_y * 2}; + for (int i = 0; i < 6; i++) { + float3 prim0 = primaries[i]; + float3 prim1 = primaries[(i + 1) % 6]; + float3 safe0 = prim0 * 0.75f; + float3 safe1 = prim1 * 0.75f; + float2 uv0 = center + rgb_to_uv(prim0) * rad_scale; + float2 uv1 = center + rgb_to_uv(prim1) * rad_scale; + float2 uv2 = center + rgb_to_uv(safe0) * rad_scale; + float2 uv3 = center + rgb_to_uv(safe1) * rad_scale; + uchar col0[4] = {uchar(prim0.x * 255), uchar(prim0.y * 255), uchar(prim0.z * 255), 64}; + uchar col1[4] = {uchar(prim1.x * 255), uchar(prim1.y * 255), uchar(prim1.z * 255), 64}; + uchar col2[4] = {uchar(safe0.x * 255), uchar(safe0.y * 255), uchar(safe0.z * 255), 64}; + uchar col3[4] = {uchar(safe1.x * 255), uchar(safe1.y * 255), uchar(safe1.z * 255), 64}; + quads.add_quad(uv0.x, uv0.y, uv1.x, uv1.y, uv2.x, uv2.y, uv3.x, uv3.y, col0, col1, col2, col3); + col0[3] = col1[3] = col2[3] = col3[3] = 192; + quads.add_line(uv0.x, uv0.y, uv1.x, uv1.y, col0, col1); + quads.add_line(uv2.x, uv2.y, uv3.x, uv3.y, col2, col3); + } + + /* skin tone line */ + uchar col_tone[4] = {255, 102, 0, 128}; + const float tone_line_len = 0.895f; /* makes it end at outer edge of saturation ring. */ + quads.add_line(centerx, + centery, + centerx + cosf(skin_rad) * rad_x * tone_line_len, + centery + sinf(skin_rad) * rad_y * tone_line_len, + col_tone); +} + +static void sequencer_draw_scopes(Scene *scene, ARegion *region, SpaceSeq *sseq) +{ + using namespace blender::ed::seq; + + /* Figure out draw coordinates. */ + rctf preview; + sequencer_preview_get_rect(&preview, scene, region, sseq, false, false); + + rctf uv; + BLI_rctf_init(&uv, 0.0f, 1.0f, 0.0f, 1.0f); + const bool keep_aspect = sseq->mainb == SEQ_DRAW_IMG_VECTORSCOPE; + float vecscope_aspect = 1.0f; + if (keep_aspect) { + float width = std::max(BLI_rctf_size_x(&preview), 0.1f); + float height = std::max(BLI_rctf_size_y(&preview), 0.1f); + vecscope_aspect = width / height; + if (vecscope_aspect >= 1.0f) { + BLI_rctf_resize_x(&uv, vecscope_aspect); + } + else { + BLI_rctf_resize_y(&uv, 1.0f / vecscope_aspect); + } + } + + 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); + GPU_texture_extend_mode(texture, GPU_SAMPLER_EXTEND_MODE_CLAMP_TO_BORDER); + + 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, uv.xmin, uv.ymin); + immVertex2f(pos, preview.xmin, preview.ymin); + + immAttr2f(texCoord, uv.xmin, uv.ymax); + immVertex2f(pos, preview.xmin, preview.ymax); + + immAttr2f(texCoord, uv.xmax, uv.ymax); + immVertex2f(pos, preview.xmax, preview.ymax); + + immAttr2f(texCoord, uv.xmax, uv.ymin); + immVertex2f(pos, preview.xmax, preview.ymin); + + immEnd(); + + GPU_texture_unbind(texture); + GPU_texture_free(texture); + + immUnbindProgram(); + } + + if (sseq->mainb == SEQ_DRAW_IMG_HISTOGRAM) { + draw_histogram(region, scopes->histogram, quads, preview); + } + if (sseq->mainb == SEQ_DRAW_IMG_WAVEFORM) { + use_blend = true; + draw_waveform_graticule(region, quads, preview); + } + if (sseq->mainb == SEQ_DRAW_IMG_VECTORSCOPE) { + use_blend = true; + draw_vectorscope_graticule(quads, preview); + } + + 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 +975,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 +1023,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); + } + 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 +1067,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); } diff --git a/source/blender/editors/space_sequencer/sequencer_quads_batch.cc b/source/blender/editors/space_sequencer/sequencer_quads_batch.cc index c1ffa191d14..eae6452cd48 100644 --- a/source/blender/editors/space_sequencer/sequencer_quads_batch.cc +++ b/source/blender/editors/space_sequencer/sequencer_quads_batch.cc @@ -53,10 +53,10 @@ SeqQuadsBatch::SeqQuadsBatch() batch_quads = GPU_batch_create_ex( GPU_PRIM_TRIS, vbo_quads, ibo_quads, GPU_BATCH_OWNS_VBO | GPU_BATCH_OWNS_INDEX); - GPU_batch_program_set_builtin(batch_quads, GPU_SHADER_3D_FLAT_COLOR); + GPU_batch_program_set_builtin(batch_quads, GPU_SHADER_3D_SMOOTH_COLOR); batch_lines = GPU_batch_create_ex(GPU_PRIM_LINES, vbo_lines, nullptr, GPU_BATCH_OWNS_VBO); - GPU_batch_program_set_builtin(batch_lines, GPU_SHADER_3D_FLAT_COLOR); + GPU_batch_program_set_builtin(batch_lines, GPU_SHADER_3D_SMOOTH_COLOR); } SeqQuadsBatch::~SeqQuadsBatch() @@ -93,7 +93,10 @@ void SeqQuadsBatch::add_quad(float x1, float y3, float x4, float y4, - const uchar color[4]) + const uchar color1[4], + const uchar color2[4], + const uchar color3[4], + const uchar color4[4]) { if (quads_num >= MAX_QUADS) { draw(); @@ -103,10 +106,10 @@ void SeqQuadsBatch::add_quad(float x1, BLI_assert(verts_quads != nullptr); } - ColorVertex v0 = {blender::float2(x1, y1), color}; - ColorVertex v1 = {blender::float2(x2, y2), color}; - ColorVertex v2 = {blender::float2(x3, y3), color}; - ColorVertex v3 = {blender::float2(x4, y4), color}; + ColorVertex v0 = {blender::float2(x1, y1), color1}; + ColorVertex v1 = {blender::float2(x2, y2), color2}; + ColorVertex v2 = {blender::float2(x3, y3), color3}; + ColorVertex v3 = {blender::float2(x4, y4), color4}; *verts_quads++ = v0; *verts_quads++ = v1; @@ -147,7 +150,8 @@ void SeqQuadsBatch::add_wire_quad(float x1, float y1, float x2, float y2, const lines_num += 4; } -void SeqQuadsBatch::add_line(float x1, float y1, float x2, float y2, const uchar color[4]) +void SeqQuadsBatch::add_line( + float x1, float y1, float x2, float y2, const uchar color1[4], const uchar color2[4]) { if (lines_num + 1 > MAX_LINES) { draw(); @@ -157,8 +161,8 @@ void SeqQuadsBatch::add_line(float x1, float y1, float x2, float y2, const uchar BLI_assert(verts_lines != nullptr); } - ColorVertex v0 = {blender::float2(x1, y1), color}; - ColorVertex v1 = {blender::float2(x2, y2), color}; + ColorVertex v0 = {blender::float2(x1, y1), color1}; + ColorVertex v1 = {blender::float2(x2, y2), color2}; *verts_lines++ = v0; *verts_lines++ = v1; diff --git a/source/blender/editors/space_sequencer/sequencer_quads_batch.hh b/source/blender/editors/space_sequencer/sequencer_quads_batch.hh index 05642467e96..b4bf85b521e 100644 --- a/source/blender/editors/space_sequencer/sequencer_quads_batch.hh +++ b/source/blender/editors/space_sequencer/sequencer_quads_batch.hh @@ -33,9 +33,9 @@ class SeqQuadsBatch { /** Add an axis-aligned quad. */ void add_quad(float x1, float y1, float x2, float y2, const uchar color[4]) { - add_quad(x1, y1, x1, y2, x2, y1, x2, y2, color); + add_quad(x1, y1, x1, y2, x2, y1, x2, y2, color, color, color, color); } - /** Add a quad. */ + /** Add a quad with four arbitrary coordinates and one color. */ void add_quad(float x1, float y1, float x2, @@ -44,11 +44,33 @@ class SeqQuadsBatch { float y3, float x4, float y4, - const uchar color[4]); + const uchar color[4]) + { + add_quad(x1, y1, x2, y2, x3, y3, x4, y4, color, color, color, color); + } + /** Add a quad with four arbitrary coordinates and a color for each. */ + void add_quad(float x1, + float y1, + float x2, + float y2, + float x3, + float y3, + float x4, + float y4, + const uchar color1[4], + const uchar color2[4], + const uchar color3[4], + const uchar color4[4]); /** Add four lines of an axis-aligned quad edges. */ void add_wire_quad(float x1, float y1, float x2, float y2, const uchar color[4]); - /** Add a line. */ - void add_line(float x1, float y1, float x2, float y2, const uchar color[4]); + /** Add a line with single color. */ + void add_line(float x1, float y1, float x2, float y2, const uchar color[4]) + { + add_line(x1, y1, x2, y2, color, color); + } + /** Add a line with two endpoint colors. */ + void add_line( + float x1, float y1, float x2, float y2, const uchar color1[4], const uchar color2[4]); private: static constexpr int MAX_QUADS = 1024; diff --git a/source/blender/editors/space_sequencer/sequencer_scopes.cc b/source/blender/editors/space_sequencer/sequencer_scopes.cc index df294930974..710a71820c5 100644 --- a/source/blender/editors/space_sequencer/sequencer_scopes.cc +++ b/source/blender/editors/space_sequencer/sequencer_scopes.cc @@ -9,6 +9,7 @@ #include #include +#include "BLI_math_vector.hh" #include "BLI_task.h" #include "BLI_task.hh" #include "BLI_utildefines.h" @@ -17,7 +18,7 @@ #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" -#include "sequencer_intern.hh" +#include "sequencer_scopes.hh" // #define DEBUG_TIME @@ -25,20 +26,48 @@ # include "BLI_timeit.hh" #endif -/* XXX(@ideasman42): why is this function better than BLI_math version? - * only difference is it does some normalize after, need to double check on this. */ -static void rgb_to_yuv_normalized(const float rgb[3], float yuv[3]) +namespace blender::ed::seq { + +SeqScopes::~SeqScopes() { - yuv[0] = 0.299f * rgb[0] + 0.587f * rgb[1] + 0.114f * rgb[2]; - yuv[1] = 0.492f * (rgb[2] - yuv[0]); - yuv[2] = 0.877f * (rgb[0] - yuv[0]); + cleanup(); +} - /* Normalize. */ - yuv[1] *= 255.0f / (122 * 2.0f); - yuv[1] += 0.5f; +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; + } + histogram.data.reinitialize(0); +} - yuv[2] *= 255.0f / (157 * 2.0f); - yuv[2] += 0.5f; +static blender::float2 rgb_to_uv_normalized(const float rgb[3]) +{ + /* Exact same math as rgb_to_yuv BT709 case. Duplicated here + * since this function is called a lot, and non-inline function + * call plus colorspace switch in there overhead does add up. */ + float r = rgb[0], g = rgb[1], b = rgb[2]; + /* We don't need y. */ + float u = -0.09991f * r - 0.33609f * g + 0.436f * b; + float v = 0.615f * r - 0.55861f * g - 0.05639f * b; + + /* Normalize: (U, V) range is +/- (0.436, 0.615) */ + u = clamp_f(u * (0.5f / 0.436f) + 0.5f, 0.0f, 1.0f); + v = clamp_f(v * (0.5f / 0.615f) + 0.5f, 0.0f, 1.0f); + return float2(u, v); } static void scope_put_pixel(const uchar *table, uchar *pos) @@ -50,106 +79,46 @@ static void scope_put_pixel(const uchar *table, uchar *pos) static void scope_put_pixel_single(const uchar *table, uchar *pos, int col) { - char newval = table[pos[col]]; - pos[col] = newval; + uint newval = table[pos[col]]; + /* So that the separate waveforms are not just pure RGB primaries, put + * some amount of value into the other channels too: slightly reduce it, + * and raise to 4th power. */ + uint other = newval * 31 / 32; + other = (other * other) >> 8; + other = (other * other) >> 8; + pos[0] = pos[1] = pos[2] = uchar(other); + pos[col] = uchar(newval); pos[3] = 255; } -static void wform_put_line(int w, uchar *last_pos, uchar *new_pos) +static void init_wave_table(int height, uchar wtable[256]) { - if (last_pos > new_pos) { - uchar *temp = new_pos; - new_pos = last_pos; - last_pos = temp; - } - - while (last_pos < new_pos) { - if (last_pos[0] == 0) { - last_pos[0] = last_pos[1] = last_pos[2] = 32; - last_pos[3] = 255; - } - last_pos += 4 * w; + /* For each pixel column of the image, waveform plots the intensity values + * with height proportional to the intensity. So depending on the height of + * the image, different amount of pixels are expected to hit the same + * intensity. Adjust the waveform plotting table gamma factor so that + * the waveform has decent visibility without saturating or being too dark: + * 0.3 gamma at height=360 and below, 0.9 gamma at height 2160 (4K) and up, + * and interpolating between those. */ + float alpha = clamp_f(ratiof(360.0f, 2160.0f, height), 0.0f, 1.0f); + float gamma = interpf(0.9f, 0.3f, alpha); + for (int x = 0; x < 256; x++) { + wtable[x] = uchar(pow((float(x) + 1.0f) / 256.0f, gamma) * 255.0f); } } -static void wform_put_line_single(int w, uchar *last_pos, uchar *new_pos, int col) -{ - if (last_pos > new_pos) { - uchar *temp = new_pos; - new_pos = last_pos; - last_pos = temp; - } - - while (last_pos < new_pos) { - if (last_pos[col] == 0) { - last_pos[col] = 32; - last_pos[3] = 255; - } - last_pos += 4 * w; - } -} - -static void wform_put_border(uchar *tgt, int w, int h) -{ - int x, y; - - for (x = 0; x < w; x++) { - uchar *p = tgt + 4 * x; - p[1] = p[3] = 155; - p[4 * w + 1] = p[4 * w + 3] = 155; - p = tgt + 4 * (w * (h - 1) + x); - p[1] = p[3] = 155; - p[-4 * w + 1] = p[-4 * w + 3] = 155; - } - - for (y = 0; y < h; y++) { - uchar *p = tgt + 4 * w * y; - p[1] = p[3] = 155; - p[4 + 1] = p[4 + 3] = 155; - p = tgt + 4 * (w * y + w - 1); - p[1] = p[3] = 155; - p[-4 + 1] = p[-4 + 3] = 155; - } -} - -static void wform_put_gridrow(uchar *tgt, float perc, int w, int h) -{ - tgt += int(perc / 100.0f * h) * w * 4; - - for (int i = 0; i < w * 2; i++) { - tgt[0] = 255; - - tgt += 4; - } -} - -static void wform_put_grid(uchar *tgt, int w, int h) -{ - wform_put_gridrow(tgt, 90.0, w, h); - wform_put_gridrow(tgt, 70.0, w, h); - wform_put_gridrow(tgt, 10.0, w, h); -} - -static ImBuf *make_waveform_view_from_ibuf_byte(ImBuf *ibuf) +ImBuf *make_waveform_view_from_ibuf(const ImBuf *ibuf) { #ifdef DEBUG_TIME - SCOPED_TIMER_AVERAGED(__func__); + SCOPED_TIMER(__func__); #endif - using namespace blender; - ImBuf *rval = IMB_allocImBuf(ibuf->x + 3, 515, 32, IB_rect); - const uchar *src = ibuf->byte_buffer.data; + const int w = ibuf->x; + const int h = 256; + ImBuf *rval = IMB_allocImBuf(w, h, 32, IB_rect); uchar *tgt = rval->byte_buffer.data; - int w = ibuf->x + 3; - int h = 515; - float waveform_gamma = 0.2; + uchar wtable[256]; - - wform_put_grid(tgt, w, h); - wform_put_border(tgt, w, h); - - for (int x = 0; x < 256; x++) { - wtable[x] = uchar(pow((float(x) + 1.0f) / 256.0f, waveform_gamma) * 255.0f); - } + init_wave_table(ibuf->y, wtable); /* IMB_colormanagement_get_luminance_byte for each pixel is quite a lot of * overhead, so instead get luma coefficients as 16-bit integers. */ @@ -158,620 +127,271 @@ static ImBuf *make_waveform_view_from_ibuf_byte(ImBuf *ibuf) int muls[3] = {int(coeffs[0] * 65535), int(coeffs[1] * 65535), int(coeffs[2] * 65535)}; /* Parallel over x, since each column is easily independent from others. */ - threading::parallel_for(IndexRange(ibuf->x), 16, [&](IndexRange x_range) { - for (int y = 0; y < ibuf->y; y++) { - uchar *last_p = nullptr; - - for (const int x : x_range) { - const uchar *rgb = src + 4 * (ibuf->x * y + x); - /* +1 is "Sree's solution" from http://stereopsis.com/doubleblend.html */ - int rgb0 = rgb[0] + 1; - int rgb1 = rgb[1] + 1; - int rgb2 = rgb[2] + 1; - int luma = (rgb0 * muls[0] + rgb1 * muls[1] + rgb2 * muls[2]) >> 16; - int luma_y = clamp_i(luma, 0, 255) * 2; - uchar *p = tgt + 4 * (w * (luma_y + 1) + x + 1); - - scope_put_pixel(wtable, p); - p += 4 * w; - scope_put_pixel(wtable, p); - - if (last_p != nullptr) { - wform_put_line(w, last_p, p); - } - last_p = p; - } - } - }); - - return rval; -} - -static ImBuf *make_waveform_view_from_ibuf_float(ImBuf *ibuf) -{ -#ifdef DEBUG_TIME - SCOPED_TIMER_AVERAGED(__func__); -#endif - using namespace blender; - ImBuf *rval = IMB_allocImBuf(ibuf->x + 3, 515, 32, IB_rect); - const float *src = ibuf->float_buffer.data; - uchar *tgt = rval->byte_buffer.data; - int w = ibuf->x + 3; - int h = 515; - float waveform_gamma = 0.2; - uchar wtable[256]; - - wform_put_grid(tgt, w, h); - - for (int x = 0; x < 256; x++) { - wtable[x] = uchar(pow((float(x) + 1.0f) / 256.0f, waveform_gamma) * 255.0f); - } - - /* Parallel over x, since each column is easily independent from others. */ - threading::parallel_for(IndexRange(ibuf->x), 16, [&](IndexRange x_range) { - for (int y = 0; y < ibuf->y; y++) { - uchar *last_p = nullptr; - - for (const int x : x_range) { - const float *rgb = src + 4 * (ibuf->x * y + x); - float v = IMB_colormanagement_get_luminance(rgb); - uchar *p = tgt; - - CLAMP(v, 0.0f, 1.0f); - - p += 4 * (w * (int(v * (h - 3)) + 1) + x + 1); - - scope_put_pixel(wtable, p); - p += 4 * w; - scope_put_pixel(wtable, p); - - if (last_p != nullptr) { - wform_put_line(w, last_p, p); - } - last_p = p; - } - } - }); - - wform_put_border(tgt, w, h); - - return rval; -} - -ImBuf *make_waveform_view_from_ibuf(ImBuf *ibuf) -{ - if (ibuf->float_buffer.data) { - return make_waveform_view_from_ibuf_float(ibuf); - } - return make_waveform_view_from_ibuf_byte(ibuf); -} - -static ImBuf *make_sep_waveform_view_from_ibuf_byte(ImBuf *ibuf) -{ -#ifdef DEBUG_TIME - SCOPED_TIMER_AVERAGED(__func__); -#endif - using namespace blender; - ImBuf *rval = IMB_allocImBuf(ibuf->x + 3, 515, 32, IB_rect); - const uchar *src = ibuf->byte_buffer.data; - uchar *tgt = rval->byte_buffer.data; - int w = ibuf->x + 3; - int sw = ibuf->x / 3; - int h = 515; - float waveform_gamma = 0.2; - uchar wtable[256]; - - wform_put_grid(tgt, w, h); - - for (int x = 0; x < 256; x++) { - wtable[x] = uchar(pow((float(x) + 1.0f) / 256.0f, waveform_gamma) * 255.0f); - } - - /* Parallel over x, since each column is easily independent from others. */ - threading::parallel_for(IndexRange(ibuf->x), 16, [&](IndexRange x_range) { - for (int y = 0; y < ibuf->y; y++) { - uchar *last_p[3] = {nullptr, nullptr, nullptr}; - - for (const int x : x_range) { - int c; - const uchar *rgb = src + 4 * (ibuf->x * y + x); - for (c = 0; c < 3; c++) { + threading::parallel_for(IndexRange(ibuf->x), 32, [&](IndexRange x_range) { + if (ibuf->float_buffer.data) { + /* Float image. */ + const float *src = ibuf->float_buffer.data; + for (int y = 0; y < ibuf->y; y++) { + for (const int x : x_range) { + const float *rgb = src + 4 * (ibuf->x * y + x); + float v = IMB_colormanagement_get_luminance(rgb); uchar *p = tgt; - p += 4 * (w * (rgb[c] * 2 + 1) + c * sw + x / 3 + 1); - scope_put_pixel_single(wtable, p, c); - p += 4 * w; - scope_put_pixel_single(wtable, p, c); + int iv = clamp_i(int(v * h), 0, h - 1); - if (last_p[c] != nullptr) { - wform_put_line_single(w, last_p[c], p, c); - } - last_p[c] = p; + p += 4 * (w * iv + x); + scope_put_pixel(wtable, p); + } + } + } + else { + /* Byte image. */ + const uchar *src = ibuf->byte_buffer.data; + for (int y = 0; y < ibuf->y; y++) { + for (const int x : x_range) { + const uchar *rgb = src + 4 * (ibuf->x * y + x); + /* +1 is "Sree's solution" from http://stereopsis.com/doubleblend.html */ + int rgb0 = rgb[0] + 1; + int rgb1 = rgb[1] + 1; + int rgb2 = rgb[2] + 1; + int luma = (rgb0 * muls[0] + rgb1 * muls[1] + rgb2 * muls[2]) >> 16; + int luma_y = clamp_i(luma, 0, 255); + uchar *p = tgt + 4 * (w * luma_y + x); + scope_put_pixel(wtable, p); } } } }); - wform_put_border(tgt, w, h); - return rval; } -static ImBuf *make_sep_waveform_view_from_ibuf_float(ImBuf *ibuf) +ImBuf *make_sep_waveform_view_from_ibuf(const ImBuf *ibuf) { #ifdef DEBUG_TIME - SCOPED_TIMER_AVERAGED(__func__); + SCOPED_TIMER(__func__); #endif - using namespace blender; - ImBuf *rval = IMB_allocImBuf(ibuf->x + 3, 515, 32, IB_rect); - const float *src = ibuf->float_buffer.data; + int w = ibuf->x; + int h = 256; + ImBuf *rval = IMB_allocImBuf(w, h, 32, IB_rect); uchar *tgt = rval->byte_buffer.data; - int w = ibuf->x + 3; int sw = ibuf->x / 3; - int h = 515; - float waveform_gamma = 0.2; + uchar wtable[256]; - - wform_put_grid(tgt, w, h); - - for (int x = 0; x < 256; x++) { - wtable[x] = uchar(pow((float(x) + 1.0f) / 256.0f, waveform_gamma) * 255.0f); - } + init_wave_table(ibuf->y, wtable); /* Parallel over x, since each column is easily independent from others. */ - threading::parallel_for(IndexRange(ibuf->x), 16, [&](IndexRange x_range) { - for (int y = 0; y < ibuf->y; y++) { - uchar *last_p[3] = {nullptr, nullptr, nullptr}; + threading::parallel_for(IndexRange(ibuf->x), 32, [&](IndexRange x_range) { + if (ibuf->float_buffer.data) { + /* Float image. */ + const float *src = ibuf->float_buffer.data; + for (int y = 0; y < ibuf->y; y++) { + for (const int x : x_range) { + const float *rgb = src + 4 * (ibuf->x * y + x); + for (int c = 0; c < 3; c++) { + uchar *p = tgt; + float v = rgb[c]; + int iv = clamp_i(int(v * h), 0, h - 1); - for (const int x : x_range) { - int c; - const float *rgb = src + 4 * (ibuf->x * y + x); - for (c = 0; c < 3; c++) { - uchar *p = tgt; - float v = rgb[c]; - - CLAMP(v, 0.0f, 1.0f); - - p += 4 * (w * (int(v * (h - 3)) + 1) + c * sw + x / 3 + 1); - - scope_put_pixel_single(wtable, p, c); - p += 4 * w; - scope_put_pixel_single(wtable, p, c); - - if (last_p[c] != nullptr) { - wform_put_line_single(w, last_p[c], p, c); + p += 4 * (w * iv + c * sw + x / 3); + scope_put_pixel_single(wtable, p, c); + } + } + } + } + else { + /* Byte image. */ + const uchar *src = ibuf->byte_buffer.data; + for (int y = 0; y < ibuf->y; y++) { + for (const int x : x_range) { + const uchar *rgb = src + 4 * (ibuf->x * y + x); + for (int c = 0; c < 3; c++) { + uchar *p = tgt; + p += 4 * (w * rgb[c] + c * sw + x / 3); + scope_put_pixel_single(wtable, p, c); } - last_p[c] = p; } } } }); - wform_put_border(tgt, w, h); - return rval; } -ImBuf *make_sep_waveform_view_from_ibuf(ImBuf *ibuf) -{ - if (ibuf->float_buffer.data) { - return make_sep_waveform_view_from_ibuf_float(ibuf); - } - return make_sep_waveform_view_from_ibuf_byte(ibuf); -} - -static void draw_zebra_byte(const ImBuf *src, ImBuf *ibuf, float perc) +ImBuf *make_zebra_view_from_ibuf(const ImBuf *ibuf, float perc) { #ifdef DEBUG_TIME - SCOPED_TIMER_AVERAGED(__func__); + SCOPED_TIMER(__func__); #endif - using namespace blender; - uint limit = 255.0f * perc / 100.0f; + ImBuf *res = IMB_allocImBuf(ibuf->x, ibuf->y, 32, IB_rect); threading::parallel_for(IndexRange(ibuf->y), 16, [&](IndexRange y_range) { - const uchar *p = src->byte_buffer.data + y_range.first() * ibuf->x * 4; - uchar *o = ibuf->byte_buffer.data + y_range.first() * ibuf->x * 4; - for (const int y : y_range) { - for (int x = 0; x < ibuf->x; x++) { - uchar r = *p++; - uchar g = *p++; - uchar b = *p++; - uchar a = *p++; - - if (r >= limit || g >= limit || b >= limit) { - if (((x + y) & 0x08) != 0) { - r = 255 - r; - g = 255 - g; - b = 255 - b; + if (ibuf->float_buffer.data) { + /* Float image. */ + const float limit = perc / 100.0f; + const float *p = ibuf->float_buffer.data + y_range.first() * ibuf->x * 4; + uchar *o = res->byte_buffer.data + y_range.first() * ibuf->x * 4; + for (const int y : y_range) { + for (int x = 0; x < ibuf->x; x++) { + float pix[4]; + memcpy(pix, p, sizeof(pix)); + if (pix[0] >= limit || pix[1] >= limit || pix[2] >= limit) { + if (((x + y) & 0x08) != 0) { + pix[0] = 1.0f - pix[0]; + pix[1] = 1.0f - pix[1]; + pix[2] = 1.0f - pix[2]; + } } + rgba_float_to_uchar(o, pix); + p += 4; + o += 4; + } + } + } + else { + /* Byte image. */ + const uint limit = 255.0f * perc / 100.0f; + const uchar *p = ibuf->byte_buffer.data + y_range.first() * ibuf->x * 4; + uchar *o = res->byte_buffer.data + y_range.first() * ibuf->x * 4; + for (const int y : y_range) { + for (int x = 0; x < ibuf->x; x++) { + uchar pix[4]; + memcpy(pix, p, sizeof(pix)); + + if (pix[0] >= limit || pix[1] >= limit || pix[2] >= limit) { + if (((x + y) & 0x08) != 0) { + pix[0] = 255 - pix[0]; + pix[1] = 255 - pix[1]; + pix[2] = 255 - pix[2]; + } + } + memcpy(o, pix, sizeof(pix)); + p += 4; + o += 4; } - *o++ = r; - *o++ = g; - *o++ = b; - *o++ = a; } } }); + return res; } -static void draw_zebra_float(ImBuf *src, ImBuf *ibuf, float perc) +static int get_bin_float(float f) +{ + int bin = int(((f - ScopeHistogram::FLOAT_VAL_MIN) / + (ScopeHistogram::FLOAT_VAL_MAX - ScopeHistogram::FLOAT_VAL_MIN)) * + ScopeHistogram::BINS_FLOAT); + return clamp_i(bin, 0, ScopeHistogram::BINS_FLOAT - 1); +} + +void ScopeHistogram::calc_from_ibuf(const ImBuf *ibuf) { #ifdef DEBUG_TIME - SCOPED_TIMER_AVERAGED(__func__); + SCOPED_TIMER(__func__); #endif - using namespace blender; - float limit = perc / 100.0f; + const bool is_float = ibuf->float_buffer.data != nullptr; + const int hist_size = is_float ? BINS_FLOAT : BINS_BYTE; - threading::parallel_for(IndexRange(ibuf->y), 16, [&](IndexRange y_range) { - const float *p = src->float_buffer.data + y_range.first() * ibuf->x * 4; - uchar *o = ibuf->byte_buffer.data + y_range.first() * ibuf->x * 4; - for (const int y : y_range) { - for (int x = 0; x < ibuf->x; x++) { - float pix[4]; - pix[0] = *p++; - pix[1] = *p++; - pix[2] = *p++; - pix[3] = *p++; - if (pix[0] >= limit || pix[1] >= limit || pix[2] >= limit) { - if (((x + y) & 0x08) != 0) { - pix[0] = -pix[0]; - pix[1] = -pix[1]; - pix[2] = -pix[2]; + Array counts(hist_size, uint3(0)); + data = threading::parallel_reduce( + IndexRange(ibuf->y), + 256, + counts, + [&](const IndexRange y_range, const Array &init) { + Array res = init; + + if (is_float) { + 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; + } } } - rgba_float_to_uchar(o, pix); - o += 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 &a, const Array &b) { + BLI_assert(a.size() == b.size()); + Array res(a.size()); + for (int i = 0; i < a.size(); i++) { + res[i] = a[i] + b[i]; + } + return res; + }); + + max_value = uint3(0); + for (const uint3 &v : data) { + max_value = math::max(max_value, v); + } } -ImBuf *make_zebra_view_from_ibuf(ImBuf *ibuf, float perc) +ImBuf *make_vectorscope_view_from_ibuf(const ImBuf *ibuf) { - ImBuf *new_ibuf = IMB_allocImBuf(ibuf->x, ibuf->y, 32, IB_rect); +#ifdef DEBUG_TIME + SCOPED_TIMER(__func__); +#endif + const int size = 512; + const float size_mul = size - 1.0f; + ImBuf *rval = IMB_allocImBuf(size, size, 32, IB_rect); + + uchar *dst = rval->byte_buffer.data; + float rgb[3]; + + uchar wtable[256]; + init_wave_table(math::midpoint(ibuf->x, ibuf->y), wtable); if (ibuf->float_buffer.data) { - draw_zebra_float(ibuf, new_ibuf, perc); + /* Float image. */ + const float *src = ibuf->float_buffer.data; + for (int y = 0; y < ibuf->y; y++) { + for (int x = 0; x < ibuf->x; x++) { + memcpy(rgb, src, sizeof(float[3])); + clamp_v3(rgb, 0.0f, 1.0f); + + float2 uv = rgb_to_uv_normalized(rgb) * size_mul; + + uchar *p = dst + 4 * (size * int(uv.y) + int(uv.x)); + scope_put_pixel(wtable, p); + + src += 4; + } + } } else { - draw_zebra_byte(ibuf, new_ibuf, perc); - } - return new_ibuf; -} + /* Byte image. */ + const uchar *src = ibuf->byte_buffer.data; + for (int y = 0; y < ibuf->y; y++) { + for (int x = 0; x < ibuf->x; x++) { + rgb[0] = float(src[0]) * (1.0f / 255.0f); + rgb[1] = float(src[1]) * (1.0f / 255.0f); + rgb[2] = float(src[2]) * (1.0f / 255.0f); -static void draw_histogram_marker(ImBuf *ibuf, int x) -{ - uchar *p = ibuf->byte_buffer.data; - int barh = ibuf->y * 0.1; + float2 uv = rgb_to_uv_normalized(rgb) * size_mul; - p += 4 * (x + ibuf->x * (ibuf->y - barh + 1)); + uchar *p = dst + 4 * (size * int(uv.y) + int(uv.x)); + scope_put_pixel(wtable, p); - 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(userdata); - const ImBuf *ibuf = data->ibuf; - const uchar *src = ibuf->byte_buffer.data; - - uint32_t(*cur_bins)[HIS_STEPS] = static_cast(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]]++; + src += 4; + } } } -} - -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(chunk_join); - uint32_t(*bins)[HIS_STEPS] = static_cast(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) -{ - 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) -{ - if (f < -0.25f) { - return 0; - } - 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(userdata); - const ImBuf *ibuf = static_cast(data->ibuf); - const float *src = ibuf->float_buffer.data; - - uint32_t(*cur_bins)[HIS_STEPS] = static_cast(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) -{ - ImBuf *rval = IMB_allocImBuf(515, 128, 32, IB_rect); - int nr, ng, nb; - int x; - - 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_float_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 + 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) -{ - float rgb[3], yuv[3]; - uchar *p; - - rgb[0] = float(r) / 255.0f; - rgb[1] = float(g) / 255.0f; - rgb[2] = float(b) / 255.0f; - rgb_to_yuv_normalized(rgb, yuv); - - p = tgt + 4 * (w * int(yuv[2] * (h - 3) + 1) + int(yuv[1] * (w - 3) + 1)); - - if (r == 0 && g == 0 && b == 0) { - r = 255; - } - - for (int y = -size; y <= size; y++) { - for (int x = -size; x <= size; x++) { - uchar *q = p + 4 * (y * w + x); - q[0] = r; - q[1] = g; - q[2] = b; - q[3] = 255; - } - } -} - -static ImBuf *make_vectorscope_view_from_ibuf_byte(ImBuf *ibuf) -{ - ImBuf *rval = IMB_allocImBuf(515, 515, 32, IB_rect); - int x, y; - const uchar *src = ibuf->byte_buffer.data; - uchar *tgt = rval->byte_buffer.data; - float rgb[3], yuv[3]; - int w = 515; - int h = 515; - float scope_gamma = 0.2; - uchar wtable[256]; - - for (x = 0; x < 256; x++) { - wtable[x] = uchar(pow((float(x) + 1.0f) / 256.0f, scope_gamma) * 255.0f); - } - - for (x = 0; x < 256; x++) { - vectorscope_put_cross(255, 0, 255 - x, tgt, w, h, 1); - vectorscope_put_cross(255, x, 0, tgt, w, h, 1); - vectorscope_put_cross(255 - x, 255, 0, tgt, w, h, 1); - vectorscope_put_cross(0, 255, x, tgt, w, h, 1); - vectorscope_put_cross(0, 255 - x, 255, tgt, w, h, 1); - vectorscope_put_cross(x, 0, 255, tgt, w, h, 1); - } - - for (y = 0; y < ibuf->y; y++) { - for (x = 0; x < ibuf->x; x++) { - const uchar *src1 = src + 4 * (ibuf->x * y + x); - uchar *p; - - rgb[0] = float(src1[0]) / 255.0f; - rgb[1] = float(src1[1]) / 255.0f; - rgb[2] = float(src1[2]) / 255.0f; - rgb_to_yuv_normalized(rgb, yuv); - - p = tgt + 4 * (w * int(yuv[2] * (h - 3) + 1) + int(yuv[1] * (w - 3) + 1)); - scope_put_pixel(wtable, (uchar *)p); - } - } - - vectorscope_put_cross(0, 0, 0, tgt, w, h, 3); - - return rval; -} - -static ImBuf *make_vectorscope_view_from_ibuf_float(ImBuf *ibuf) -{ - ImBuf *rval = IMB_allocImBuf(515, 515, 32, IB_rect); - int x, y; - const float *src = ibuf->float_buffer.data; - uchar *tgt = rval->byte_buffer.data; - float rgb[3], yuv[3]; - int w = 515; - int h = 515; - float scope_gamma = 0.2; - uchar wtable[256]; - - for (x = 0; x < 256; x++) { - wtable[x] = uchar(pow((float(x) + 1.0f) / 256.0f, scope_gamma) * 255.0f); - } - - for (x = 0; x <= 255; x++) { - vectorscope_put_cross(255, 0, 255 - x, tgt, w, h, 1); - vectorscope_put_cross(255, x, 0, tgt, w, h, 1); - vectorscope_put_cross(255 - x, 255, 0, tgt, w, h, 1); - vectorscope_put_cross(0, 255, x, tgt, w, h, 1); - vectorscope_put_cross(0, 255 - x, 255, tgt, w, h, 1); - vectorscope_put_cross(x, 0, 255, tgt, w, h, 1); - } - - for (y = 0; y < ibuf->y; y++) { - for (x = 0; x < ibuf->x; x++) { - const float *src1 = src + 4 * (ibuf->x * y + x); - const uchar *p; - - memcpy(rgb, src1, sizeof(float[3])); - - clamp_v3(rgb, 0.0f, 1.0f); - - rgb_to_yuv_normalized(rgb, yuv); - - p = tgt + 4 * (w * int(yuv[2] * (h - 3) + 1) + int(yuv[1] * (w - 3) + 1)); - scope_put_pixel(wtable, (uchar *)p); - } - } - - vectorscope_put_cross(0, 0, 0, tgt, w, h, 3); - - return rval; -} - -ImBuf *make_vectorscope_view_from_ibuf(ImBuf *ibuf) -{ - if (ibuf->float_buffer.data) { - return make_vectorscope_view_from_ibuf_float(ibuf); - } - return make_vectorscope_view_from_ibuf_byte(ibuf); -} +} // namespace blender::ed::seq diff --git a/source/blender/editors/space_sequencer/sequencer_scopes.hh b/source/blender/editors/space_sequencer/sequencer_scopes.hh new file mode 100644 index 00000000000..85c626f576d --- /dev/null +++ b/source/blender/editors/space_sequencer/sequencer_scopes.hh @@ -0,0 +1,55 @@ +/* SPDX-FileCopyrightText: 2024 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup spseq + */ + +#pragma once + +#include "BLI_array.hh" +#include "BLI_math_vector_types.hh" +#include "BLI_utility_mixins.hh" + +struct ImBuf; + +namespace blender::ed::seq { + +struct ScopeHistogram { + /* Byte images just have bins for the 0..255 range. */ + static constexpr int BINS_BYTE = 256; + /* Float images spead -0.25..+1.25 range over 512 bins. */ + static constexpr int BINS_FLOAT = 512; + static constexpr float FLOAT_VAL_MIN = -0.25f; + static constexpr float FLOAT_VAL_MAX = 1.25f; + Array data; + uint3 max_value; + + void calc_from_ibuf(const ImBuf *ibuf); + bool is_float_hist() const + { + return data.size() == BINS_FLOAT; + } +}; + +struct SeqScopes : public NonCopyable { + ImBuf *reference_ibuf = nullptr; + ImBuf *zebra_ibuf = nullptr; + ImBuf *waveform_ibuf = nullptr; + ImBuf *sep_waveform_ibuf = nullptr; + ImBuf *vector_ibuf = nullptr; + ScopeHistogram histogram; + + SeqScopes() = default; + ~SeqScopes(); + + void cleanup(); +}; + +ImBuf *make_waveform_view_from_ibuf(const ImBuf *ibuf); +ImBuf *make_sep_waveform_view_from_ibuf(const ImBuf *ibuf); +ImBuf *make_vectorscope_view_from_ibuf(const ImBuf *ibuf); +ImBuf *make_zebra_view_from_ibuf(const ImBuf *ibuf, float perc); + +} // namespace blender::ed::seq diff --git a/source/blender/editors/space_sequencer/sequencer_thumbnails.cc b/source/blender/editors/space_sequencer/sequencer_thumbnails.cc index 902f25c1f87..7f56052cfbb 100644 --- a/source/blender/editors/space_sequencer/sequencer_thumbnails.cc +++ b/source/blender/editors/space_sequencer/sequencer_thumbnails.cc @@ -295,21 +295,21 @@ static void sequencer_thumbnail_start_job_if_necessary( /* Job start requested, but over area which has been processed. Unless `thumbnail_is_missing` is * true, ignore this request as all images are in view. */ - if (v2d->cur.xmax == sseq->runtime.last_thumbnail_area.xmax && - v2d->cur.ymax == sseq->runtime.last_thumbnail_area.ymax && !thumbnail_is_missing) + if (v2d->cur.xmax == sseq->runtime->last_thumbnail_area.xmax && + v2d->cur.ymax == sseq->runtime->last_thumbnail_area.ymax && !thumbnail_is_missing) { return; } /* Stop the job first as view has changed. Pointless to continue old job. */ - if (v2d->cur.xmax != sseq->runtime.last_thumbnail_area.xmax || - v2d->cur.ymax != sseq->runtime.last_thumbnail_area.ymax) + if (v2d->cur.xmax != sseq->runtime->last_thumbnail_area.xmax || + v2d->cur.ymax != sseq->runtime->last_thumbnail_area.ymax) { WM_jobs_stop(CTX_wm_manager(C), nullptr, thumbnail_start_job); } sequencer_thumbnail_init_job(C, v2d, ed, thumb_height); - sseq->runtime.last_thumbnail_area = v2d->cur; + sseq->runtime->last_thumbnail_area = v2d->cur; } void last_displayed_thumbnails_list_free(void *val) @@ -320,15 +320,15 @@ void last_displayed_thumbnails_list_free(void *val) static GSet *last_displayed_thumbnails_list_ensure(const bContext *C, Sequence *seq) { SpaceSeq *sseq = CTX_wm_space_seq(C); - if (sseq->runtime.last_displayed_thumbnails == nullptr) { - sseq->runtime.last_displayed_thumbnails = BLI_ghash_ptr_new(__func__); + if (sseq->runtime->last_displayed_thumbnails == nullptr) { + sseq->runtime->last_displayed_thumbnails = BLI_ghash_ptr_new(__func__); } GSet *displayed_thumbnails = static_cast( - BLI_ghash_lookup(sseq->runtime.last_displayed_thumbnails, seq)); + BLI_ghash_lookup(sseq->runtime->last_displayed_thumbnails, seq)); if (displayed_thumbnails == nullptr) { displayed_thumbnails = BLI_gset_int_new(__func__); - BLI_ghash_insert(sseq->runtime.last_displayed_thumbnails, seq, displayed_thumbnails); + BLI_ghash_insert(sseq->runtime->last_displayed_thumbnails, seq, displayed_thumbnails); } return displayed_thumbnails; diff --git a/source/blender/editors/space_sequencer/space_sequencer.cc b/source/blender/editors/space_sequencer/space_sequencer.cc index f95859c22cd..9d673798f6f 100644 --- a/source/blender/editors/space_sequencer/space_sequencer.cc +++ b/source/blender/editors/space_sequencer/space_sequencer.cc @@ -62,8 +62,15 @@ static void sequencer_scopes_tag_refresh(ScrArea *area) { SpaceSeq *sseq = (SpaceSeq *)area->spacedata.first; + sseq->runtime->scopes.reference_ibuf = nullptr; +} - sseq->scopes.reference_ibuf = nullptr; +blender::ed::seq::SpaceSeq_Runtime::~SpaceSeq_Runtime() +{ + if (last_displayed_thumbnails != nullptr) { + BLI_ghash_free(last_displayed_thumbnails, nullptr, last_displayed_thumbnails_list_free); + last_displayed_thumbnails = nullptr; + } } /* ******************** manage regions ********************* */ @@ -98,9 +105,6 @@ static SpaceLink *sequencer_create(const ScrArea * /*area*/, const Scene *scene) SEQ_TIMELINE_SHOW_FCURVES | SEQ_TIMELINE_SHOW_STRIP_COLOR_TAG | SEQ_TIMELINE_SHOW_STRIP_RETIMING | SEQ_TIMELINE_ALL_WAVEFORMS; - BLI_rctf_init(&sseq->runtime.last_thumbnail_area, 0.0f, 0.0f, 0.0f, 0.0f); - sseq->runtime.last_displayed_thumbnails = nullptr; - /* Header. */ region = MEM_cnew("header for sequencer"); @@ -192,8 +196,6 @@ static SpaceLink *sequencer_create(const ScrArea * /*area*/, const Scene *scene) region->v2d.flag |= V2D_VIEWSYNC_AREA_VERTICAL; region->v2d.align = V2D_ALIGN_NO_NEG_Y; - sseq->runtime.last_displayed_thumbnails = nullptr; - return (SpaceLink *)sseq; } @@ -201,43 +203,23 @@ static SpaceLink *sequencer_create(const ScrArea * /*area*/, const Scene *scene) static void sequencer_free(SpaceLink *sl) { SpaceSeq *sseq = (SpaceSeq *)sl; - SequencerScopes *scopes = &sseq->scopes; + MEM_delete(sseq->runtime); #if 0 if (sseq->gpd) { BKE_gpencil_free_data(sseq->gpd); } #endif - - if (scopes->zebra_ibuf) { - IMB_freeImBuf(scopes->zebra_ibuf); - } - - if (scopes->waveform_ibuf) { - IMB_freeImBuf(scopes->waveform_ibuf); - } - - if (scopes->sep_waveform_ibuf) { - IMB_freeImBuf(scopes->sep_waveform_ibuf); - } - - if (scopes->vector_ibuf) { - IMB_freeImBuf(scopes->vector_ibuf); - } - - if (scopes->histogram_ibuf) { - IMB_freeImBuf(scopes->histogram_ibuf); - } - - if (sseq->runtime.last_displayed_thumbnails) { - BLI_ghash_free( - sseq->runtime.last_displayed_thumbnails, nullptr, last_displayed_thumbnails_list_free); - sseq->runtime.last_displayed_thumbnails = nullptr; - } } /* Space-type init callback. */ -static void sequencer_init(wmWindowManager * /*wm*/, ScrArea * /*area*/) {} +static void sequencer_init(wmWindowManager * /*wm*/, ScrArea *area) +{ + SpaceSeq *sseq = (SpaceSeq *)area->spacedata.first; + if (sseq->runtime == nullptr) { + sseq->runtime = MEM_new(__func__); + } +} static void sequencer_refresh(const bContext *C, ScrArea *area) { @@ -301,13 +283,11 @@ static void sequencer_refresh(const bContext *C, ScrArea *area) static SpaceLink *sequencer_duplicate(SpaceLink *sl) { SpaceSeq *sseqn = static_cast(MEM_dupallocN(sl)); + sseqn->runtime = MEM_new(__func__); /* Clear or remove stuff from old. */ // sseq->gpd = gpencil_data_duplicate(sseq->gpd, false); - memset(&sseqn->scopes, 0, sizeof(sseqn->scopes)); - memset(&sseqn->runtime, 0, sizeof(sseqn->runtime)); - return (SpaceLink *)sseqn; } @@ -534,7 +514,7 @@ static void sequencer_main_clamp_view(const bContext *C, ARegion *region) } /* If strip is deleted, don't move view automatically, keep current range until it is changed. */ - strip_boundbox.ymax = max_ff(sseq->runtime.timeline_clamp_custom_range, strip_boundbox.ymax); + strip_boundbox.ymax = max_ff(sseq->runtime->timeline_clamp_custom_range, strip_boundbox.ymax); rctf view_clamped = v2d->cur; @@ -557,7 +537,7 @@ static void sequencer_main_region_clamp_custom_set(const bContext *C, ARegion *r View2D *v2d = ®ion->v2d; if ((v2d->flag & V2D_IS_NAVIGATING) == 0) { - sseq->runtime.timeline_clamp_custom_range = v2d->cur.ymax; + sseq->runtime->timeline_clamp_custom_range = v2d->cur.ymax; } } @@ -960,6 +940,8 @@ static void sequencer_space_blend_read_data(BlendDataReader * /*reader*/, SpaceL { SpaceSeq *sseq = (SpaceSeq *)sl; + sseq->runtime = nullptr; + /* grease pencil data is not a direct data and can't be linked from direct_link* * functions, it should be linked from lib_link* functions instead * @@ -973,13 +955,6 @@ static void sequencer_space_blend_read_data(BlendDataReader * /*reader*/, SpaceL BKE_gpencil_blend_read_data(fd, sseq->gpd); } #endif - sseq->scopes.reference_ibuf = nullptr; - sseq->scopes.zebra_ibuf = nullptr; - sseq->scopes.waveform_ibuf = nullptr; - sseq->scopes.sep_waveform_ibuf = nullptr; - sseq->scopes.vector_ibuf = nullptr; - sseq->scopes.histogram_ibuf = nullptr; - memset(&sseq->runtime, 0x0, sizeof(sseq->runtime)); } static void sequencer_space_blend_write(BlendWriter *writer, SpaceLink *sl) diff --git a/source/blender/editors/transform/transform_convert_sequencer.cc b/source/blender/editors/transform/transform_convert_sequencer.cc index 68b115ae28d..6113600eb1e 100644 --- a/source/blender/editors/transform/transform_convert_sequencer.cc +++ b/source/blender/editors/transform/transform_convert_sequencer.cc @@ -6,6 +6,7 @@ * \ingroup edtransform */ +#include "DNA_sequence_types.h" #include "DNA_space_types.h" #include "MEM_guardedalloc.h" diff --git a/source/blender/editors/transform/transform_convert_sequencer_image.cc b/source/blender/editors/transform/transform_convert_sequencer_image.cc index 2f5594d8750..05d38c5bcd7 100644 --- a/source/blender/editors/transform/transform_convert_sequencer_image.cc +++ b/source/blender/editors/transform/transform_convert_sequencer_image.cc @@ -8,6 +8,7 @@ #include "MEM_guardedalloc.h" +#include "DNA_sequence_types.h" #include "DNA_space_types.h" #include "BLI_listbase.h" diff --git a/source/blender/editors/transform/transform_convert_sequencer_retiming.cc b/source/blender/editors/transform/transform_convert_sequencer_retiming.cc index 93eb527b2bf..5b4990fc8ba 100644 --- a/source/blender/editors/transform/transform_convert_sequencer_retiming.cc +++ b/source/blender/editors/transform/transform_convert_sequencer_retiming.cc @@ -8,6 +8,7 @@ #include "MEM_guardedalloc.h" +#include "DNA_sequence_types.h" #include "DNA_space_types.h" #include "BLI_listbase.h" diff --git a/source/blender/editors/transform/transform_gizmo_2d.cc b/source/blender/editors/transform/transform_gizmo_2d.cc index db4f88a3a2e..f410151dbb8 100644 --- a/source/blender/editors/transform/transform_gizmo_2d.cc +++ b/source/blender/editors/transform/transform_gizmo_2d.cc @@ -19,6 +19,7 @@ #include "DNA_object_types.h" #include "DNA_screen_types.h" +#include "DNA_sequence_types.h" #include "DNA_space_types.h" #include "DNA_view3d_types.h" diff --git a/source/blender/editors/transform/transform_snap_sequencer.cc b/source/blender/editors/transform/transform_snap_sequencer.cc index e46b602951f..e17caa7f2fe 100644 --- a/source/blender/editors/transform/transform_snap_sequencer.cc +++ b/source/blender/editors/transform/transform_snap_sequencer.cc @@ -14,6 +14,8 @@ #include "BKE_context.hh" +#include "DNA_sequence_types.h" + #include "ED_screen.hh" #include "ED_transform.hh" diff --git a/source/blender/imbuf/intern/colormanagement.cc b/source/blender/imbuf/intern/colormanagement.cc index 6a2c5527425..3cc84b1b0ef 100644 --- a/source/blender/imbuf/intern/colormanagement.cc +++ b/source/blender/imbuf/intern/colormanagement.cc @@ -16,6 +16,7 @@ #include "DNA_image_types.h" #include "DNA_movieclip_types.h" #include "DNA_scene_types.h" +#include "DNA_sequence_types.h" #include "DNA_space_types.h" #include "IMB_filetype.h" diff --git a/source/blender/makesdna/DNA_sequence_types.h b/source/blender/makesdna/DNA_sequence_types.h index d78c2fa7bdf..0806401c84b 100644 --- a/source/blender/makesdna/DNA_sequence_types.h +++ b/source/blender/makesdna/DNA_sequence_types.h @@ -545,19 +545,9 @@ typedef struct SoundEqualizerModifierData { /** \} */ /* -------------------------------------------------------------------- */ -/** \name Scopes +/** \name Flags & Types * \{ */ -typedef struct SequencerScopes { - struct ImBuf *reference_ibuf; - - struct ImBuf *zebra_ibuf; - struct ImBuf *waveform_ibuf; - struct ImBuf *sep_waveform_ibuf; - struct ImBuf *vector_ibuf; - struct ImBuf *histogram_ibuf; -} SequencerScopes; - #define MAXSEQ 128 /** #Editor::overlay_frame_flag */ @@ -583,12 +573,6 @@ enum { SEQ_SPEED_USE_INTERPOLATION = 1 << 3, }; -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Flags & Types - * \{ */ - #define SEQ_NAME_MAXSTR 64 /* From: `DNA_object_types.h`, see it's doc-string there. */ diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index 6fc3eea2580..244c7dd59fd 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -19,7 +19,6 @@ #include "DNA_movieclip_types.h" /* MovieClipUser */ #include "DNA_node_types.h" /* for bNodeInstanceKey */ #include "DNA_outliner_types.h" /* for TreeStoreElem */ -#include "DNA_sequence_types.h" /* SequencerScopes */ #include "DNA_vec_types.h" /* Hum ... Not really nice... but needed for spacebuts. */ #include "DNA_view2d_types.h" @@ -67,9 +66,15 @@ namespace blender::ed::outliner { struct SpaceOutliner_Runtime; } // namespace blender::ed::outliner using SpaceOutliner_Runtime = blender::ed::outliner::SpaceOutliner_Runtime; + +namespace blender::ed::seq { +struct SpaceSeq_Runtime; +} // namespace blender::ed::seq +using SpaceSeq_Runtime = blender::ed::seq::SpaceSeq_Runtime; #else typedef struct SpaceNode_Runtime SpaceNode_Runtime; typedef struct SpaceOutliner_Runtime SpaceOutliner_Runtime; +typedef struct SpaceSeq_Runtime SpaceSeq_Runtime; #endif /** Defined in `file_intern.hh`. */ @@ -631,15 +636,6 @@ typedef enum eSpaceSeq_SequencerTimelineOverlay_Flag { SEQ_TIMELINE_SHOW_GRID = (1 << 18), } eSpaceSeq_SequencerTimelineOverlay_Flag; -typedef struct SpaceSeqRuntime { - /** Required for Thumbnail job start condition. */ - struct rctf last_thumbnail_area; - /** Stores lists of most recently displayed thumbnails. */ - struct GHash *last_displayed_thumbnails; - int rename_channel_index; - float timeline_clamp_custom_range; -} SpaceSeqRuntime; - /** Sequencer. */ typedef struct SpaceSeq { SpaceLink *next, *prev; @@ -678,8 +674,6 @@ typedef struct SpaceSeq { /** Grease-pencil data. */ struct bGPdata *gpd; - /** Different scoped displayed in space. */ - struct SequencerScopes scopes; struct SequencerPreviewOverlay preview_overlay; struct SequencerTimelineOverlay timeline_overlay; @@ -687,7 +681,7 @@ typedef struct SpaceSeq { char multiview_eye; char _pad2[7]; - SpaceSeqRuntime runtime; + SpaceSeq_Runtime *runtime; } SpaceSeq; /** #SpaceSeq.mainb */ diff --git a/source/blender/makesrna/intern/rna_userdef.cc b/source/blender/makesrna/intern/rna_userdef.cc index 5daa118e21f..b1ae943a097 100644 --- a/source/blender/makesrna/intern/rna_userdef.cc +++ b/source/blender/makesrna/intern/rna_userdef.cc @@ -12,6 +12,7 @@ #include "DNA_brush_types.h" #include "DNA_curve_types.h" #include "DNA_scene_types.h" +#include "DNA_sequence_types.h" #include "DNA_space_types.h" #include "DNA_userdef_types.h" #include "DNA_view3d_types.h" diff --git a/source/blender/sequencer/SEQ_sound.hh b/source/blender/sequencer/SEQ_sound.hh index a5efbb79c7c..ca77e2973b7 100644 --- a/source/blender/sequencer/SEQ_sound.hh +++ b/source/blender/sequencer/SEQ_sound.hh @@ -13,6 +13,7 @@ struct Main; struct Scene; struct Sequence; struct bSound; +struct SequenceModifierData; struct SequencerSoundEqualizer; struct SoundModifierWorkerInfo; struct BlendWriter; diff --git a/source/blender/windowmanager/intern/wm_files.cc b/source/blender/windowmanager/intern/wm_files.cc index 70b9de4b84e..c03b4eed2da 100644 --- a/source/blender/windowmanager/intern/wm_files.cc +++ b/source/blender/windowmanager/intern/wm_files.cc @@ -54,6 +54,7 @@ #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_screen_types.h" +#include "DNA_sequence_types.h" #include "DNA_space_types.h" #include "DNA_userdef_types.h" #include "DNA_windowmanager_types.h"