VSE: Draw more correct text boundbox
Text strip along with other effect strips draws into buffer, which is defined by common render size. However this makes selection, snapping and other features not work correctly from user perspective. There are 2 ways to remedy this: - Draw text into buffer which size is defined by text boundbox - Just draw the correct size boundbox and make UI use it correctly This PR implements second option, as there are multiple difficulties asociated with first option. Main problem is, that one would need to calculate boundboxes for all individual text effects(blur, shadow, ...), but many of these effects need to know the size of buffer we are trying to calculate. Big question here was: What the boundox should look like? For selection it is best, if it encapsulates the text itself along with its effects like shadow, blur or box. However for snapping and pivot range it seems, that encapsulating only the text without any effects applied is the best solution. This was discussed with UI module and we agreed, that encapsulating only the text would provide better usability. The implementation: Most of the features are on "preview side", where all dimensions are related to scene render size. Temporary `TextVarsRuntime` is created to calculate apparent text strip image size. This allows boundbox of correct size to be drawn. However to draw boundbox and do transformations correctly, it is necessary to remap origin point to the subset of image defined by the boundbox. Since recalculated origin is relative, this function is used for image transformation during rendering as well. Main drawback of this method is, that boundbox and origin point calculation may be needed from multiple places, and `TextVarsRuntime` may be recalculated about 10x on redraw. `text_effect_calc_runtime` function can be executed from ~5us for single character to 0.5ms with 512 characters with default font(not sure if that matters). Drawing origin only during transformation would help with general UI responsiveness. During playback, these overlays are not drawn, so there should be no change in performance. Pull Request: https://projects.blender.org/blender/blender/pulls/140420
This commit is contained in:
committed by
Richard Antalik
parent
86d60ae663
commit
2f10db2377
@@ -1078,42 +1078,6 @@ static void text_edit_draw_cursor(const bContext *C, const Strip *strip, uint po
|
||||
immEnd();
|
||||
}
|
||||
|
||||
static void text_edit_draw_box(const bContext *C, const Strip *strip, uint pos)
|
||||
{
|
||||
const TextVars *data = static_cast<TextVars *>(strip->effectdata);
|
||||
const TextVarsRuntime *text = data->runtime;
|
||||
const Scene *scene = CTX_data_scene(C);
|
||||
|
||||
const blender::float2 view_offs{-scene->r.xsch / 2.0f, -scene->r.ysch / 2.0f};
|
||||
const float view_aspect = scene->r.xasp / scene->r.yasp;
|
||||
blender::float3x3 transform_mat = seq::image_transform_matrix_get(CTX_data_scene(C), strip);
|
||||
blender::float4x2 box_quad{
|
||||
{float(text->text_boundbox.xmin), float(text->text_boundbox.ymin)},
|
||||
{float(text->text_boundbox.xmin), float(text->text_boundbox.ymax)},
|
||||
{float(text->text_boundbox.xmax), float(text->text_boundbox.ymax)},
|
||||
{float(text->text_boundbox.xmax), float(text->text_boundbox.ymin)},
|
||||
};
|
||||
|
||||
GPU_blend(GPU_BLEND_NONE);
|
||||
immBindBuiltinProgram(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR);
|
||||
blender::float3 col;
|
||||
UI_GetThemeColorShade3fv(TH_SEQ_ACTIVE, -50, col);
|
||||
immUniformColor3fv(col);
|
||||
immUniform1f("lineWidth", U.pixelsize);
|
||||
immUniform1f("dash_width", 10.0f);
|
||||
immBegin(GPU_PRIM_LINE_LOOP, 4);
|
||||
|
||||
for (int i : blender::IndexRange(0, 4)) {
|
||||
box_quad[i] += view_offs;
|
||||
box_quad[i] = blender::math::transform_point(transform_mat, box_quad[i]);
|
||||
box_quad[i].x *= view_aspect;
|
||||
immVertex2f(pos, box_quad[i][0], box_quad[i][1]);
|
||||
}
|
||||
|
||||
immEnd();
|
||||
immUnbindProgram();
|
||||
}
|
||||
|
||||
static void text_edit_draw(const bContext *C)
|
||||
{
|
||||
if (!sequencer_text_editing_active_poll(const_cast<bContext *>(C))) {
|
||||
@@ -1137,8 +1101,6 @@ static void text_edit_draw(const bContext *C)
|
||||
immUnbindProgram();
|
||||
GPU_blend(GPU_BLEND_NONE);
|
||||
GPU_line_smooth(false);
|
||||
|
||||
text_edit_draw_box(C, strip, pos);
|
||||
}
|
||||
|
||||
/* Draw empty preview region.
|
||||
|
||||
@@ -333,14 +333,10 @@ static float2 calculate_new_origin_position(TransInfo *t, TransDataSeq *tdseq, T
|
||||
{
|
||||
Strip *strip = tdseq->strip;
|
||||
|
||||
float2 image_size(float(t->scene->r.xsch), float(t->scene->r.ysch));
|
||||
if (ELEM(strip->type, STRIP_TYPE_MOVIE, STRIP_TYPE_IMAGE)) {
|
||||
image_size.x = strip->data->stripdata->orig_width;
|
||||
image_size.y = strip->data->stripdata->orig_height;
|
||||
}
|
||||
const float2 image_size = seq::transform_image_raw_size_get(t->scene, strip);
|
||||
|
||||
const float2 viewport_pixel_aspect = {t->scene->r.xasp / t->scene->r.yasp, 1.0f};
|
||||
float2 mirror = seq::image_transform_mirror_factor_get(strip);
|
||||
const float2 mirror = seq::image_transform_mirror_factor_get(strip);
|
||||
|
||||
const float2 origin = tdseq->orig_origin_pixelspace;
|
||||
const float2 translation = transform_result_get(t, tdseq, td2d, strip).translation;
|
||||
|
||||
@@ -89,6 +89,24 @@ blender::float2 image_transform_mirror_factor_get(const Strip *strip);
|
||||
*/
|
||||
blender::float2 image_transform_origin_offset_pixelspace_get(const Scene *scene,
|
||||
const Strip *strip);
|
||||
|
||||
/**
|
||||
* Get strip transform origin relative value. This function is mainly needed to
|
||||
* recalculate text strip origin position.
|
||||
*
|
||||
* \param render_size: Size of image canvas in pixels
|
||||
* \param strip: Strip to calculate origin for
|
||||
*/
|
||||
float2 image_transform_origin_get(const Scene *scene, const Strip *strip);
|
||||
|
||||
/**
|
||||
* Get size of the image, which is produced by strip without any transformation.
|
||||
*
|
||||
* \param render_size: Size of image canvas in pixels
|
||||
* \param strip: Strip to calculate origin for
|
||||
*/
|
||||
float2 transform_image_raw_size_get(const Scene *scene, const Strip *strip);
|
||||
|
||||
/**
|
||||
* Get 4 corner points of strip image, optionally without rotation component applied.
|
||||
* Corner vectors are in viewport space.
|
||||
|
||||
@@ -144,4 +144,7 @@ static void apply_effect_op(const OpT &op, const ImBuf *src1, const ImBuf *src2,
|
||||
});
|
||||
}
|
||||
|
||||
TextVarsRuntime *text_effect_calc_runtime(const Strip *strip, int font, const int2 image_size);
|
||||
int text_effect_font_init(const RenderData *context, const Strip *strip, int font_flags);
|
||||
|
||||
} // namespace blender::seq
|
||||
|
||||
@@ -772,6 +772,12 @@ static void fill_rect_alpha_under(
|
||||
static int text_effect_line_size_get(const RenderData *context, const Strip *strip)
|
||||
{
|
||||
TextVars *data = static_cast<TextVars *>(strip->effectdata);
|
||||
|
||||
/* Used to calculate boundbox. Proxy size compensation is not needed there. */
|
||||
if (context == nullptr) {
|
||||
return data->text_size;
|
||||
}
|
||||
|
||||
/* Compensate text size for preview render size. */
|
||||
double proxy_size_comp = context->scene->r.size / 100.0;
|
||||
if (context->preview_render_size != SEQ_RENDER_SIZE_SCENE) {
|
||||
@@ -781,7 +787,7 @@ static int text_effect_line_size_get(const RenderData *context, const Strip *str
|
||||
return proxy_size_comp * data->text_size;
|
||||
}
|
||||
|
||||
static int text_effect_font_init(const RenderData *context, const Strip *strip, int font_flags)
|
||||
int text_effect_font_init(const RenderData *context, const Strip *strip, int font_flags)
|
||||
{
|
||||
TextVars *data = static_cast<TextVars *>(strip->effectdata);
|
||||
int font = blf_mono_font_render;
|
||||
@@ -992,16 +998,11 @@ static void apply_text_alignment(const TextVars *data,
|
||||
}
|
||||
}
|
||||
|
||||
static void calc_text_runtime(const Strip *strip, int font, const int2 image_size)
|
||||
TextVarsRuntime *text_effect_calc_runtime(const Strip *strip, int font, const int2 image_size)
|
||||
{
|
||||
TextVars *data = static_cast<TextVars *>(strip->effectdata);
|
||||
TextVarsRuntime *runtime = MEM_new<TextVarsRuntime>(__func__);
|
||||
|
||||
if (data->runtime != nullptr) {
|
||||
MEM_delete(data->runtime);
|
||||
}
|
||||
|
||||
data->runtime = MEM_new<TextVarsRuntime>(__func__);
|
||||
TextVarsRuntime *runtime = data->runtime;
|
||||
runtime->font = font;
|
||||
runtime->line_height = BLF_height_max(font);
|
||||
runtime->font_descender = BLF_descender(font);
|
||||
@@ -1011,6 +1012,7 @@ static void calc_text_runtime(const Strip *strip, int font, const int2 image_siz
|
||||
apply_word_wrapping(data, runtime, image_size, characters_temp);
|
||||
apply_text_alignment(data, runtime, image_size);
|
||||
calc_boundbox(data, runtime, image_size);
|
||||
return runtime;
|
||||
}
|
||||
|
||||
static ImBuf *do_text_effect(const RenderData *context,
|
||||
@@ -1035,8 +1037,12 @@ static ImBuf *do_text_effect(const RenderData *context,
|
||||
|
||||
const int font = text_effect_font_init(context, strip, font_flags);
|
||||
|
||||
calc_text_runtime(strip, font, {out->x, out->y});
|
||||
TextVarsRuntime *runtime = data->runtime;
|
||||
if (data->runtime != nullptr) {
|
||||
MEM_delete(data->runtime);
|
||||
}
|
||||
|
||||
TextVarsRuntime *runtime = text_effect_calc_runtime(strip, font, {out->x, out->y});
|
||||
data->runtime = runtime;
|
||||
|
||||
rcti outline_rect = draw_text_outline(context, data, runtime, display, out);
|
||||
BLF_buffer(font, nullptr, out->byte_buffer.data, out->x, out->y, display);
|
||||
|
||||
@@ -441,7 +441,8 @@ static bool seq_need_scale_to_render_size(const Strip *strip, bool is_proxy_imag
|
||||
return true;
|
||||
}
|
||||
|
||||
static float3x3 sequencer_image_crop_transform_matrix(const Strip *strip,
|
||||
static float3x3 sequencer_image_crop_transform_matrix(const Scene *scene,
|
||||
const Strip *strip,
|
||||
const ImBuf *in,
|
||||
const ImBuf *out,
|
||||
const float image_scale_factor,
|
||||
@@ -459,7 +460,9 @@ static float3x3 sequencer_image_crop_transform_matrix(const Strip *strip,
|
||||
const float rotation = transform->rotation;
|
||||
const float2 scale(transform->scale_x * image_scale_factor,
|
||||
transform->scale_y * image_scale_factor);
|
||||
const float2 pivot(in->x * transform->origin[0], in->y * transform->origin[1]);
|
||||
|
||||
const float2 origin = image_transform_origin_get(scene, strip);
|
||||
const float2 pivot(in->x * origin[0], in->y * origin[1]);
|
||||
|
||||
const float3x3 matrix = math::from_loc_rot_scale<float3x3>(
|
||||
translation + float2(image_center_offs), rotation, scale);
|
||||
@@ -540,7 +543,7 @@ static void sequencer_preprocess_transform_crop(
|
||||
const float image_scale_factor = do_scale_to_render_size ? preview_scale_factor : 1.0f;
|
||||
|
||||
float3x3 matrix = sequencer_image_crop_transform_matrix(
|
||||
strip, in, out, image_scale_factor, preview_scale_factor);
|
||||
scene, strip, in, out, image_scale_factor, preview_scale_factor);
|
||||
|
||||
/* Proxy image is smaller, so crop values must be corrected by proxy scale factor.
|
||||
* Proxy scale factor always matches preview_scale_factor. */
|
||||
|
||||
@@ -8,15 +8,18 @@
|
||||
* \ingroup bke
|
||||
*/
|
||||
|
||||
#include "BLI_bounds.hh"
|
||||
#include "BLI_math_base.hh"
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_sequence_types.h"
|
||||
|
||||
#include "BLI_bounds.hh"
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_math_base.h"
|
||||
#include "BLI_math_base.hh"
|
||||
#include "BLI_math_matrix.hh"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
#include "BLI_rect.h"
|
||||
|
||||
#include "BLF_api.hh"
|
||||
|
||||
#include "SEQ_animation.hh"
|
||||
#include "SEQ_channels.hh"
|
||||
@@ -28,6 +31,7 @@
|
||||
#include "SEQ_time.hh"
|
||||
#include "SEQ_transform.hh"
|
||||
|
||||
#include "effects/effects.hh"
|
||||
#include "sequencer.hh"
|
||||
#include "strip_time.hh"
|
||||
|
||||
@@ -587,8 +591,10 @@ float2 image_transform_mirror_factor_get(const Strip *strip)
|
||||
return mirror;
|
||||
}
|
||||
|
||||
static float2 strip_raw_image_size_get(const Scene *scene, const Strip *strip)
|
||||
float2 transform_image_raw_size_get(const Scene *scene, const Strip *strip)
|
||||
{
|
||||
float2 scene_render_size(scene->r.xsch, scene->r.ysch);
|
||||
|
||||
if (ELEM(strip->type, STRIP_TYPE_MOVIE, STRIP_TYPE_IMAGE)) {
|
||||
const StripElem *selem = strip->data->stripdata;
|
||||
return {float(selem->orig_width), float(selem->orig_height)};
|
||||
@@ -601,22 +607,56 @@ static float2 strip_raw_image_size_get(const Scene *scene, const Strip *strip)
|
||||
}
|
||||
}
|
||||
|
||||
return {float(scene->r.xsch), float(scene->r.ysch)};
|
||||
if (strip->type == STRIP_TYPE_TEXT) {
|
||||
const TextVars *data = static_cast<TextVars *>(strip->effectdata);
|
||||
const int font_flags = ((data->flag & SEQ_TEXT_BOLD) ? BLF_BOLD : 0) |
|
||||
((data->flag & SEQ_TEXT_ITALIC) ? BLF_ITALIC : 0);
|
||||
const int font = text_effect_font_init(nullptr, strip, font_flags);
|
||||
|
||||
const TextVarsRuntime *runtime = text_effect_calc_runtime(
|
||||
strip, font, int2(scene_render_size));
|
||||
|
||||
const float2 text_size(float(BLI_rcti_size_x(&runtime->text_boundbox)),
|
||||
float(BLI_rcti_size_y(&runtime->text_boundbox)));
|
||||
MEM_delete(runtime);
|
||||
return text_size;
|
||||
}
|
||||
|
||||
return scene_render_size;
|
||||
}
|
||||
|
||||
float2 image_transform_origin_get(const Scene *scene, const Strip *strip)
|
||||
{
|
||||
|
||||
const StripTransform *transform = strip->data->transform;
|
||||
if (strip->type != STRIP_TYPE_TEXT) {
|
||||
return {transform->origin[0], transform->origin[1]};
|
||||
}
|
||||
|
||||
/* Text image size is different from true image size, so the origin position must be
|
||||
* calculated. */
|
||||
float2 scene_render_size(scene->r.xsch, scene->r.ysch);
|
||||
const float2 text_image_size = transform_image_raw_size_get(scene, strip);
|
||||
const float2 scale = text_image_size / scene_render_size;
|
||||
const float2 origin_rel(transform->origin[0], transform->origin[1]);
|
||||
const float2 origin_center(0.5f, 0.5f);
|
||||
const float2 origin_diff = origin_rel - origin_center;
|
||||
|
||||
const float2 true_origin_relative = origin_center + origin_diff * scale;
|
||||
return true_origin_relative;
|
||||
}
|
||||
|
||||
float2 image_transform_origin_offset_pixelspace_get(const Scene *scene, const Strip *strip)
|
||||
{
|
||||
const float2 image_size = strip_raw_image_size_get(scene, strip);
|
||||
const StripTransform *transform = strip->data->transform;
|
||||
|
||||
const float2 origin(
|
||||
(image_size[0] * transform->origin[0]) - (image_size[0] * 0.5f) + transform->xofs,
|
||||
(image_size[1] * transform->origin[1]) - (image_size[1] * 0.5f) + transform->yofs);
|
||||
|
||||
const float2 image_size = transform_image_raw_size_get(scene, strip);
|
||||
const float2 origin_relative(transform->origin[0], transform->origin[1]);
|
||||
const float2 translation(transform->xofs, transform->yofs);
|
||||
const float2 origin_pos_pixels = (image_size * origin_relative) - (image_size * 0.5f) +
|
||||
translation;
|
||||
const float2 viewport_pixel_aspect(scene->r.xasp / scene->r.yasp, 1.0f);
|
||||
const float2 mirror = image_transform_mirror_factor_get(strip);
|
||||
|
||||
return origin * mirror * viewport_pixel_aspect;
|
||||
return origin_pos_pixels * mirror * viewport_pixel_aspect;
|
||||
}
|
||||
|
||||
static float3x3 seq_image_transform_matrix_get_ex(const Scene *scene,
|
||||
@@ -624,12 +664,13 @@ static float3x3 seq_image_transform_matrix_get_ex(const Scene *scene,
|
||||
bool apply_rotation = true)
|
||||
{
|
||||
const StripTransform *transform = strip->data->transform;
|
||||
const float2 image_size = strip_raw_image_size_get(scene, strip);
|
||||
const float2 origin(image_size.x * transform->origin[0], image_size[1] * transform->origin[1]);
|
||||
const float2 image_size = transform_image_raw_size_get(scene, strip);
|
||||
const float2 origin_relative(transform->origin[0], transform->origin[1]);
|
||||
const float2 origin_absolute = image_size * origin_relative;
|
||||
const float2 translation(transform->xofs, transform->yofs);
|
||||
const float rotation = apply_rotation ? transform->rotation : 0.0f;
|
||||
const float2 scale(transform->scale_x, transform->scale_y);
|
||||
const float2 pivot = origin - (image_size / 2);
|
||||
const float2 pivot = origin_absolute - (image_size / 2);
|
||||
|
||||
const float3x3 matrix = math::from_loc_rot_scale<float3x3>(translation, rotation, scale);
|
||||
return math::from_origin_transform(matrix, pivot);
|
||||
@@ -644,7 +685,7 @@ static Array<float2> strip_image_transform_quad_get_ex(const Scene *scene,
|
||||
const Strip *strip,
|
||||
bool apply_rotation)
|
||||
{
|
||||
const float2 image_size = strip_raw_image_size_get(scene, strip);
|
||||
const float2 image_size = transform_image_raw_size_get(scene, strip);
|
||||
|
||||
const StripCrop *crop = strip->data->crop;
|
||||
float2 quad[4]{
|
||||
|
||||
Reference in New Issue
Block a user