VSE: Text Strip supports text longer than 512 bytes
Text strip had a fixed size buffer of 512 bytes to hold the displayed text (this can be much fewer actual characters with non-English languages). Switch to dynamically allocated buffer instead, which can hold longer text. In order to support forward/backward compatibility, TextVars continues to hold the 512 byte buffer in memory. When writing out the .blend file, dynamic text buffer is copied into the fixed one. If it is longer, the text is truncated, so opening the .blend file in an older version will contain the first 512 bytes of the longer text. When reading existing files without the dynamic text buffer, it is created from the static buffer. Conceptually this approach is similar to constraints name length increase PR !137310. The text strip editing code was switched to operate on the dynamic buffer, resizing it as needed. seq::CharInfo internal struct was switched to be more independent of the actual buffer address; now each char entry just stores an index into the buffer instead of direct pointer (side effect: makes the struct smaller as well). Pull Request: https://projects.blender.org/blender/blender/pulls/140733
This commit is contained in:
committed by
Aras Pranckevicius
parent
695a03d9f4
commit
11bf3dd71e
@@ -12,6 +12,7 @@
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_node_types.h"
|
||||
#include "DNA_screen_types.h"
|
||||
#include "DNA_sequence_types.h"
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_math_vector.h"
|
||||
@@ -29,6 +30,8 @@
|
||||
#include "BKE_node_legacy_types.hh"
|
||||
#include "BKE_node_runtime.hh"
|
||||
|
||||
#include "SEQ_iterator.hh"
|
||||
|
||||
#include "readfile.hh"
|
||||
|
||||
#include "versioning_common.hh"
|
||||
@@ -437,6 +440,24 @@ static void do_version_normal_node_dot_product(bNodeTree *node_tree, bNode *node
|
||||
}
|
||||
}
|
||||
|
||||
static void version_seq_text_from_legacy(Main *bmain)
|
||||
{
|
||||
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
|
||||
if (scene->ed != nullptr) {
|
||||
blender::seq::for_each_callback(&scene->ed->seqbase, [&](Strip *strip) -> bool {
|
||||
if (strip->type == STRIP_TYPE_TEXT && strip->effectdata != nullptr) {
|
||||
TextVars *data = static_cast<TextVars *>(strip->effectdata);
|
||||
if (data->text_ptr == nullptr) {
|
||||
data->text_ptr = BLI_strdup(data->text_legacy);
|
||||
data->text_len_bytes = strlen(data->text_ptr);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void do_versions_after_linking_500(FileData * /*fd*/, Main * /*bmain*/)
|
||||
{
|
||||
/**
|
||||
@@ -597,6 +618,10 @@ void blo_do_versions_500(FileData * /*fd*/, Library * /*lib*/, Main *bmain)
|
||||
FOREACH_NODETREE_END;
|
||||
}
|
||||
|
||||
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 25)) {
|
||||
version_seq_text_from_legacy(bmain);
|
||||
}
|
||||
|
||||
/**
|
||||
* Always bump subversion in BKE_blender_version.h when adding versioning
|
||||
* code here, and wrap it inside a MAIN_VERSION_FILE_ATLEAST check.
|
||||
|
||||
@@ -3144,8 +3144,12 @@ static wmOperatorStatus sequencer_export_subtitles_exec(bContext *C, wmOperator
|
||||
FPS,
|
||||
USER_TIMECODE_SUBRIP);
|
||||
|
||||
fprintf(
|
||||
file, "%d\n%s --> %s\n%s\n\n", iter++, timecode_str_start, timecode_str_end, data->text);
|
||||
fprintf(file,
|
||||
"%d\n%s --> %s\n%s\n\n",
|
||||
iter++,
|
||||
timecode_str_start,
|
||||
timecode_str_end,
|
||||
data->text_ptr);
|
||||
|
||||
strip_next = static_cast<Strip *>(strip->next);
|
||||
MEM_freeN(strip);
|
||||
|
||||
@@ -150,10 +150,15 @@ static void delete_selected_text(TextVars *data)
|
||||
seq::CharInfo char_start = character_at_cursor_offset_get(text, sel_range.first());
|
||||
seq::CharInfo char_end = character_at_cursor_offset_get(text, sel_range.last());
|
||||
|
||||
char *addr_start = const_cast<char *>(char_start.str_ptr);
|
||||
char *addr_end = const_cast<char *>(char_end.str_ptr) + char_end.byte_length;
|
||||
const int offset_start = char_start.offset;
|
||||
const int offset_end = char_end.offset + char_end.byte_length;
|
||||
BLI_assert(offset_start >= 0 && offset_end <= data->text_len_bytes);
|
||||
BLI_assert(offset_end >= 0 && offset_end <= data->text_len_bytes);
|
||||
BLI_assert(offset_start <= offset_end);
|
||||
const int remaining = data->text_len_bytes - offset_end;
|
||||
|
||||
std::memmove(addr_start, addr_end, BLI_strnlen(addr_end, sizeof(data->text)) + 1);
|
||||
std::memmove(data->text_ptr + offset_start, data->text_ptr + offset_end, remaining + 1);
|
||||
data->text_len_bytes = offset_start + remaining;
|
||||
|
||||
const int2 sel_start = strip_text_cursor_offset_to_position(text, sel_range.first());
|
||||
data->cursor_offset = cursor_position_to_offset(text, sel_start);
|
||||
@@ -260,7 +265,9 @@ static bool is_whitespace_transition(char chr1, char chr2)
|
||||
return ELEM(chr1, ' ', '\t', '\n') && !ELEM(chr2, ' ', '\t', '\n');
|
||||
}
|
||||
|
||||
static int2 cursor_move_prev_word(int2 cursor_position, const TextVarsRuntime *text)
|
||||
static int2 cursor_move_prev_word(int2 cursor_position,
|
||||
const TextVarsRuntime *text,
|
||||
const char *text_ptr)
|
||||
{
|
||||
cursor_position = cursor_move_by_character(cursor_position, text, -1);
|
||||
|
||||
@@ -269,7 +276,7 @@ static int2 cursor_move_prev_word(int2 cursor_position, const TextVarsRuntime *t
|
||||
const int2 prev_cursor_pos = cursor_move_by_character(cursor_position, text, -1);
|
||||
const seq::CharInfo prev_character = character_at_cursor_pos_get(text, prev_cursor_pos);
|
||||
|
||||
if (is_whitespace_transition(prev_character.str_ptr[0], character.str_ptr[0])) {
|
||||
if (is_whitespace_transition(text_ptr[prev_character.offset], text_ptr[character.offset])) {
|
||||
break;
|
||||
}
|
||||
cursor_position = prev_cursor_pos;
|
||||
@@ -277,7 +284,9 @@ static int2 cursor_move_prev_word(int2 cursor_position, const TextVarsRuntime *t
|
||||
return cursor_position;
|
||||
}
|
||||
|
||||
static int2 cursor_move_next_word(int2 cursor_position, const TextVarsRuntime *text)
|
||||
static int2 cursor_move_next_word(int2 cursor_position,
|
||||
const TextVarsRuntime *text,
|
||||
const char *text_ptr)
|
||||
{
|
||||
const int maxline = text->lines.size() - 1;
|
||||
const int maxchar = text->lines.last().characters.size() - 1;
|
||||
@@ -287,7 +296,7 @@ static int2 cursor_move_next_word(int2 cursor_position, const TextVarsRuntime *t
|
||||
cursor_position = cursor_move_by_character(cursor_position, text, 1);
|
||||
const seq::CharInfo next_character = character_at_cursor_pos_get(text, cursor_position);
|
||||
|
||||
if (is_whitespace_transition(next_character.str_ptr[0], character.str_ptr[0])) {
|
||||
if (is_whitespace_transition(text_ptr[next_character.offset], text_ptr[character.offset])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -333,10 +342,10 @@ static wmOperatorStatus sequencer_text_cursor_move_exec(bContext *C, wmOperator
|
||||
cursor_position = cursor_move_line_end(cursor_position, text);
|
||||
break;
|
||||
case PREV_WORD:
|
||||
cursor_position = cursor_move_prev_word(cursor_position, text);
|
||||
cursor_position = cursor_move_prev_word(cursor_position, text, data->text_ptr);
|
||||
break;
|
||||
case NEXT_WORD:
|
||||
cursor_position = cursor_move_next_word(cursor_position, text);
|
||||
cursor_position = cursor_move_next_word(cursor_position, text, data->text_ptr);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -387,21 +396,21 @@ static bool text_insert(TextVars *data, const char *buf, const size_t buf_len)
|
||||
BLI_assert(strlen(buf) == buf_len);
|
||||
const TextVarsRuntime *text = data->runtime;
|
||||
|
||||
const bool selection_was_deleted = text_has_selection(data);
|
||||
delete_selected_text(data);
|
||||
|
||||
const size_t text_str_len = STRNLEN(data->text);
|
||||
|
||||
if (text_str_len + buf_len + 1 > sizeof(data->text)) {
|
||||
return selection_was_deleted;
|
||||
}
|
||||
size_t needed_size = data->text_len_bytes + buf_len + 1;
|
||||
char *new_text = MEM_malloc_arrayN<char>(needed_size, "text");
|
||||
|
||||
const seq::CharInfo cur_char = character_at_cursor_offset_get(text, data->cursor_offset);
|
||||
char *cursor_addr = const_cast<char *>(cur_char.str_ptr);
|
||||
const size_t move_str_len = BLI_strnlen(cursor_addr, sizeof(data->text)) + 1;
|
||||
|
||||
std::memmove(cursor_addr + buf_len, cursor_addr, move_str_len);
|
||||
std::memcpy(cursor_addr, buf, buf_len);
|
||||
BLI_assert(cur_char.offset >= 0 && cur_char.offset <= data->text_len_bytes);
|
||||
std::memcpy(new_text, data->text_ptr, cur_char.offset);
|
||||
std::memcpy(new_text + cur_char.offset, buf, buf_len);
|
||||
std::memcpy(new_text + cur_char.offset + buf_len,
|
||||
data->text_ptr + cur_char.offset,
|
||||
data->text_len_bytes - cur_char.offset + 1);
|
||||
data->text_len_bytes += buf_len;
|
||||
MEM_freeN(data->text_ptr);
|
||||
data->text_ptr = new_text;
|
||||
|
||||
data->cursor_offset += 1;
|
||||
return true;
|
||||
@@ -465,11 +474,16 @@ static const EnumPropertyItem delete_type_items[] = {
|
||||
{0, nullptr, 0, nullptr, nullptr},
|
||||
};
|
||||
|
||||
static void delete_character(const seq::CharInfo character, const TextVars *data)
|
||||
static void delete_character(const seq::CharInfo character, TextVars *data)
|
||||
{
|
||||
char *cursor_addr = const_cast<char *>(character.str_ptr);
|
||||
char *next_char_addr = cursor_addr + character.byte_length;
|
||||
std::memmove(cursor_addr, next_char_addr, BLI_strnlen(next_char_addr, sizeof(data->text)) + 1);
|
||||
const int offset_start = character.offset;
|
||||
const int offset_end = character.offset + character.byte_length;
|
||||
BLI_assert(offset_start >= 0 && offset_start <= data->text_len_bytes);
|
||||
BLI_assert(offset_end >= 0 && offset_end <= data->text_len_bytes);
|
||||
const int remaining = data->text_len_bytes - offset_end + 1;
|
||||
std::memmove(data->text_ptr + offset_start, data->text_ptr + offset_end, remaining);
|
||||
data->text_len_bytes -= character.byte_length;
|
||||
BLI_assert(data->text_len_bytes >= 0);
|
||||
}
|
||||
|
||||
static wmOperatorStatus sequencer_text_delete_exec(bContext *C, wmOperator *op)
|
||||
@@ -780,11 +794,19 @@ static void text_edit_copy(const TextVars *data)
|
||||
const IndexRange selection_range = strip_text_selection_range_get(data);
|
||||
const seq::CharInfo start = character_at_cursor_offset_get(text, selection_range.first());
|
||||
const seq::CharInfo end = character_at_cursor_offset_get(text, selection_range.last());
|
||||
const size_t len = end.str_ptr + end.byte_length - start.str_ptr;
|
||||
|
||||
char clipboard_buf[sizeof(data->text)] = {0};
|
||||
memcpy(clipboard_buf, start.str_ptr, math::min(len, sizeof(clipboard_buf)));
|
||||
WM_clipboard_text_set(clipboard_buf, false);
|
||||
const int offset_start = start.offset;
|
||||
const int offset_end = end.offset + end.byte_length;
|
||||
BLI_assert(offset_start >= 0 && offset_start <= data->text_len_bytes);
|
||||
BLI_assert(offset_end >= 0 && offset_end <= data->text_len_bytes);
|
||||
BLI_assert(offset_start <= offset_end);
|
||||
|
||||
const size_t len = offset_end - offset_start;
|
||||
char *buf = MEM_malloc_arrayN<char>(len + 1, "text clipboard");
|
||||
memcpy(buf, data->text_ptr + offset_start, len);
|
||||
buf[len] = 0;
|
||||
WM_clipboard_text_set(buf, false);
|
||||
MEM_freeN(buf);
|
||||
}
|
||||
|
||||
static wmOperatorStatus sequencer_text_edit_copy_exec(bContext *C, wmOperator * /*op*/)
|
||||
@@ -822,34 +844,31 @@ static wmOperatorStatus sequencer_text_edit_paste_exec(bContext *C, wmOperator *
|
||||
TextVars *data = static_cast<TextVars *>(strip->effectdata);
|
||||
const TextVarsRuntime *text = data->runtime;
|
||||
|
||||
int clipboard_len;
|
||||
char *clipboard_buf = WM_clipboard_text_get(false, true, &clipboard_len);
|
||||
int buf_len;
|
||||
char *buf = WM_clipboard_text_get(false, true, &buf_len);
|
||||
|
||||
if (clipboard_len == 0) {
|
||||
if (buf_len == 0) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
delete_selected_text(data);
|
||||
const int max_str_len = sizeof(data->text) - (STRNLEN(data->text) + 1);
|
||||
|
||||
/* Maximum bytes that can be filled into `data->text`. */
|
||||
const int fillable_len = std::min(clipboard_len, max_str_len);
|
||||
|
||||
/* Truncated string could contain invalid UTF8 sequence, thus ensure the length inserted is
|
||||
* always valid. */
|
||||
size_t valid_str_len;
|
||||
const int extra_offset = BLI_strnlen_utf8_ex(clipboard_buf, fillable_len, &valid_str_len);
|
||||
size_t needed_size = data->text_len_bytes + buf_len + 1;
|
||||
char *new_text = MEM_malloc_arrayN<char>(needed_size, "text");
|
||||
|
||||
const seq::CharInfo cur_char = character_at_cursor_offset_get(text, data->cursor_offset);
|
||||
char *cursor_addr = const_cast<char *>(cur_char.str_ptr);
|
||||
const size_t move_str_len = BLI_strnlen(cursor_addr, sizeof(data->text)) + 1;
|
||||
BLI_assert(cur_char.offset >= 0 && cur_char.offset <= data->text_len_bytes);
|
||||
std::memcpy(new_text, data->text_ptr, cur_char.offset);
|
||||
std::memcpy(new_text + cur_char.offset, buf, buf_len);
|
||||
std::memcpy(new_text + cur_char.offset + buf_len,
|
||||
data->text_ptr + cur_char.offset,
|
||||
data->text_len_bytes - cur_char.offset + 1);
|
||||
data->text_len_bytes += buf_len;
|
||||
MEM_freeN(data->text_ptr);
|
||||
data->text_ptr = new_text;
|
||||
|
||||
std::memmove(cursor_addr + valid_str_len, cursor_addr, move_str_len);
|
||||
std::memcpy(cursor_addr, clipboard_buf, valid_str_len);
|
||||
data->cursor_offset += BLI_strlen_utf8(buf);
|
||||
|
||||
data->cursor_offset += extra_offset;
|
||||
|
||||
MEM_freeN(clipboard_buf);
|
||||
MEM_freeN(buf);
|
||||
text_editing_update(C);
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
@@ -815,7 +815,7 @@ static void draw_seq_text_get_source(const Strip *strip, char *r_source, size_t
|
||||
}
|
||||
case STRIP_TYPE_TEXT: {
|
||||
const TextVars *textdata = static_cast<TextVars *>(strip->effectdata);
|
||||
BLI_strncpy(r_source, textdata->text, source_maxncpy);
|
||||
BLI_strncpy(r_source, textdata->text_ptr, source_maxncpy);
|
||||
break;
|
||||
}
|
||||
case STRIP_TYPE_SCENE: {
|
||||
|
||||
@@ -446,7 +446,11 @@ typedef struct GaussianBlurVars {
|
||||
} GaussianBlurVars;
|
||||
|
||||
typedef struct TextVars {
|
||||
char text[512];
|
||||
char *text_ptr;
|
||||
/* Text length in bytes, not including terminating zero (i.e. strlen of text). */
|
||||
int text_len_bytes;
|
||||
char _pad2[4];
|
||||
|
||||
struct VFont *text_font;
|
||||
int text_blf_id;
|
||||
float text_size;
|
||||
@@ -463,7 +467,7 @@ typedef struct TextVars {
|
||||
char align;
|
||||
char _pad[2];
|
||||
|
||||
/** Offsets in characters (unicode code-points) for #TextVars::text. */
|
||||
/** Offsets in characters (unicode code-points) for #TextVars::text_ptr. */
|
||||
int cursor_offset;
|
||||
int selection_start_offset;
|
||||
int selection_end_offset;
|
||||
@@ -472,6 +476,10 @@ typedef struct TextVars {
|
||||
char anchor_x, anchor_y;
|
||||
char _pad1;
|
||||
TextVarsRuntime *runtime;
|
||||
|
||||
/* Fixed size text buffer, only exists for forward/backward compatibility.
|
||||
* #TextVars::text_ptr and #TextVars::text_len_bytes are used for full text. */
|
||||
char text_legacy[512];
|
||||
} TextVars;
|
||||
|
||||
/** #TextVars.flag */
|
||||
|
||||
@@ -212,6 +212,7 @@ DNA_STRUCT_RENAME_MEMBER(SurfaceDeformModifierData, num_mesh_verts, mesh_verts_n
|
||||
DNA_STRUCT_RENAME_MEMBER(SurfaceDeformModifierData, numpoly, target_polys_num)
|
||||
DNA_STRUCT_RENAME_MEMBER(SurfaceDeformModifierData, numverts, bind_verts_num)
|
||||
DNA_STRUCT_RENAME_MEMBER(Text, name, filepath)
|
||||
DNA_STRUCT_RENAME_MEMBER(TextVars, text, text_legacy)
|
||||
DNA_STRUCT_RENAME_MEMBER(ThemeSpace, scrubbing_background, time_scrub_background)
|
||||
DNA_STRUCT_RENAME_MEMBER(ThemeSpace, show_back_grad, background_type)
|
||||
DNA_STRUCT_RENAME_MEMBER(UVProjectModifierData, num_projectors, projectors_num)
|
||||
|
||||
@@ -784,6 +784,32 @@ static void rna_Strip_name_set(PointerRNA *ptr, const char *value)
|
||||
}
|
||||
}
|
||||
|
||||
static int rna_Strip_text_length(PointerRNA *ptr)
|
||||
{
|
||||
Strip *strip = static_cast<Strip *>(ptr->data);
|
||||
TextVars *text = static_cast<TextVars *>(strip->effectdata);
|
||||
return text->text_len_bytes;
|
||||
}
|
||||
|
||||
static void rna_Strip_text_get(PointerRNA *ptr, char *value)
|
||||
{
|
||||
Strip *strip = static_cast<Strip *>(ptr->data);
|
||||
TextVars *text = static_cast<TextVars *>(strip->effectdata);
|
||||
memcpy(value, text->text_ptr, text->text_len_bytes + 1);
|
||||
}
|
||||
|
||||
static void rna_Strip_text_set(PointerRNA *ptr, const char *value)
|
||||
{
|
||||
Strip *strip = static_cast<Strip *>(ptr->data);
|
||||
TextVars *text = static_cast<TextVars *>(strip->effectdata);
|
||||
|
||||
if (text->text_ptr) {
|
||||
MEM_freeN(text->text_ptr);
|
||||
}
|
||||
text->text_ptr = BLI_strdup(value);
|
||||
text->text_len_bytes = strlen(text->text_ptr);
|
||||
}
|
||||
|
||||
static StructRNA *rna_Strip_refine(PointerRNA *ptr)
|
||||
{
|
||||
Strip *strip = (Strip *)ptr->data;
|
||||
@@ -3531,6 +3557,9 @@ static void rna_def_text(StructRNA *srna)
|
||||
RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Strip_invalidate_raw_update");
|
||||
|
||||
prop = RNA_def_property(srna, "text", PROP_STRING, PROP_NONE);
|
||||
RNA_def_property_string_sdna(prop, nullptr, "text_ptr");
|
||||
RNA_def_property_string_funcs(
|
||||
prop, "rna_Strip_text_get", "rna_Strip_text_length", "rna_Strip_text_set");
|
||||
RNA_def_property_ui_text(prop, "Text", "Text that will be displayed");
|
||||
RNA_def_property_flag(prop, PROP_TEXTEDIT_UPDATE);
|
||||
RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Strip_invalidate_raw_update");
|
||||
|
||||
@@ -85,7 +85,7 @@ bool effects_can_render_text(const Strip *strip);
|
||||
|
||||
struct CharInfo {
|
||||
int index = 0;
|
||||
const char *str_ptr = nullptr;
|
||||
int offset = 0; /* Offset in bytes within text buffer. */
|
||||
int byte_length = 0;
|
||||
float2 position{0.0f, 0.0f};
|
||||
int advance_x = 0;
|
||||
|
||||
@@ -196,7 +196,8 @@ static void init_text_effect(Strip *strip)
|
||||
data->outline_color[3] = 0.7f;
|
||||
data->outline_width = 0.05f;
|
||||
|
||||
STRNCPY(data->text, "Text");
|
||||
data->text_ptr = BLI_strdup("Text");
|
||||
data->text_len_bytes = strlen(data->text_ptr);
|
||||
|
||||
data->loc[0] = 0.5f;
|
||||
data->loc[1] = 0.5f;
|
||||
@@ -264,6 +265,7 @@ static void free_text_effect(Strip *strip, const bool do_id_user)
|
||||
effect_text_font_unload(data, do_id_user);
|
||||
|
||||
if (data) {
|
||||
MEM_SAFE_FREE(data->text_ptr);
|
||||
MEM_delete(data->runtime);
|
||||
MEM_freeN(data);
|
||||
strip->effectdata = nullptr;
|
||||
@@ -280,6 +282,7 @@ static void copy_text_effect(Strip *dst, const Strip *src, const int flag)
|
||||
{
|
||||
dst->effectdata = MEM_dupallocN(src->effectdata);
|
||||
TextVars *data = static_cast<TextVars *>(dst->effectdata);
|
||||
data->text_ptr = BLI_strdup_null(data->text_ptr);
|
||||
|
||||
data->runtime = nullptr;
|
||||
data->text_blf_id = -1;
|
||||
@@ -549,7 +552,7 @@ static void jump_flooding_pass(Span<JFACoord> input,
|
||||
});
|
||||
}
|
||||
|
||||
static void text_draw(const TextVarsRuntime *runtime, float color[4])
|
||||
static void text_draw(const char *text_ptr, const TextVarsRuntime *runtime, float color[4])
|
||||
{
|
||||
const bool use_fallback = BLF_is_builtin(runtime->font);
|
||||
if (!use_fallback) {
|
||||
@@ -560,7 +563,7 @@ static void text_draw(const TextVarsRuntime *runtime, float color[4])
|
||||
for (const CharInfo &character : line.characters) {
|
||||
BLF_position(runtime->font, character.position.x, character.position.y, 0.0f);
|
||||
BLF_buffer_col(runtime->font, color);
|
||||
BLF_draw_buffer(runtime->font, character.str_ptr, character.byte_length);
|
||||
BLF_draw_buffer(runtime->font, text_ptr + character.offset, character.byte_length);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -590,7 +593,7 @@ static rcti draw_text_outline(const RenderData *context,
|
||||
Array<uchar4> tmp_buf(pixel_count, uchar4(0));
|
||||
BLF_buffer(runtime->font, nullptr, (uchar *)tmp_buf.data(), size.x, size.y, display);
|
||||
|
||||
text_draw(runtime, float4(1.0f));
|
||||
text_draw(data->text_ptr, runtime, float4(1.0f));
|
||||
|
||||
rcti outline_rect = runtime->text_boundbox;
|
||||
BLI_rcti_pad(&outline_rect, outline_width + 1, outline_width + 1);
|
||||
@@ -815,7 +818,7 @@ int text_effect_font_init(const RenderData *context, const Strip *strip, int fon
|
||||
static Vector<CharInfo> build_character_info(const TextVars *data, int font)
|
||||
{
|
||||
Vector<CharInfo> characters;
|
||||
const size_t len_max = STRNLEN(data->text);
|
||||
const int len_max = data->text_len_bytes;
|
||||
int byte_offset = 0;
|
||||
int char_index = 0;
|
||||
|
||||
@@ -825,12 +828,12 @@ static Vector<CharInfo> build_character_info(const TextVars *data, int font)
|
||||
}
|
||||
|
||||
while (byte_offset <= len_max) {
|
||||
const char *str = data->text + byte_offset;
|
||||
const char *str = data->text_ptr + byte_offset;
|
||||
const int char_length = BLI_str_utf8_size_safe(str);
|
||||
|
||||
CharInfo char_info;
|
||||
char_info.index = char_index;
|
||||
char_info.str_ptr = str;
|
||||
char_info.offset = byte_offset;
|
||||
char_info.byte_length = char_length;
|
||||
char_info.advance_x = BLF_glyph_advance(font, str);
|
||||
characters.append(char_info);
|
||||
@@ -867,15 +870,16 @@ static void apply_word_wrapping(const TextVars *data,
|
||||
|
||||
/* First pass: Find characters where line has to be broken. */
|
||||
for (CharInfo &character : characters) {
|
||||
if (character.str_ptr[0] == ' ') {
|
||||
char ch = data->text_ptr[character.offset];
|
||||
if (ch == ' ') {
|
||||
character.position = char_position;
|
||||
last_space = &character;
|
||||
}
|
||||
if (character.str_ptr[0] == '\n') {
|
||||
if (ch == '\n') {
|
||||
char_position.x = 0;
|
||||
last_space = nullptr;
|
||||
}
|
||||
if (character.str_ptr[0] != '\0' && char_position.x > wrap_width && last_space != nullptr) {
|
||||
if (ch != '\0' && char_position.x > wrap_width && last_space != nullptr) {
|
||||
last_space->do_wrap = true;
|
||||
char_position -= last_space->position + last_space->advance_x;
|
||||
}
|
||||
@@ -892,7 +896,7 @@ static void apply_word_wrapping(const TextVars *data,
|
||||
|
||||
char_position.x += character.advance_x;
|
||||
|
||||
if (character.do_wrap || character.str_ptr[0] == '\n') {
|
||||
if (character.do_wrap || data->text_ptr[character.offset] == '\n') {
|
||||
runtime->lines.append(LineInfo());
|
||||
char_position.x = 0;
|
||||
char_position.y -= runtime->line_height;
|
||||
@@ -1006,7 +1010,7 @@ TextVarsRuntime *text_effect_calc_runtime(const Strip *strip, int font, const in
|
||||
runtime->font = font;
|
||||
runtime->line_height = BLF_height_max(font);
|
||||
runtime->font_descender = BLF_descender(font);
|
||||
runtime->character_count = BLI_strlen_utf8(data->text);
|
||||
runtime->character_count = BLI_strlen_utf8(data->text_ptr);
|
||||
|
||||
Vector<CharInfo> characters_temp = build_character_info(data, font);
|
||||
apply_word_wrapping(data, runtime, image_size, characters_temp);
|
||||
@@ -1046,7 +1050,7 @@ static ImBuf *do_text_effect(const RenderData *context,
|
||||
|
||||
rcti outline_rect = draw_text_outline(context, data, runtime, display, out);
|
||||
BLF_buffer(font, nullptr, out->byte_buffer.data, out->x, out->y, display);
|
||||
text_draw(runtime, data->color);
|
||||
text_draw(data->text_ptr, runtime, data->color);
|
||||
BLF_buffer(font, nullptr, nullptr, 0, 0, nullptr);
|
||||
BLF_disable(font, font_flags);
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_path_utils.hh"
|
||||
#include "BLI_string_utf8.h"
|
||||
|
||||
#include "BKE_fcurve.hh"
|
||||
#include "BKE_idprop.hh"
|
||||
@@ -762,9 +763,15 @@ static bool strip_write_data_cb(Strip *strip, void *userdata)
|
||||
case STRIP_TYPE_GAUSSIAN_BLUR:
|
||||
BLO_write_struct(writer, GaussianBlurVars, strip->effectdata);
|
||||
break;
|
||||
case STRIP_TYPE_TEXT:
|
||||
BLO_write_struct(writer, TextVars, strip->effectdata);
|
||||
break;
|
||||
case STRIP_TYPE_TEXT: {
|
||||
TextVars *text = static_cast<TextVars *>(strip->effectdata);
|
||||
if (!BLO_write_is_undo(writer)) {
|
||||
/* Copy current text into legacy buffer. */
|
||||
STRNCPY_UTF8(text->text_legacy, text->text_ptr);
|
||||
}
|
||||
BLO_write_struct(writer, TextVars, text);
|
||||
BLO_write_string(writer, text->text_ptr);
|
||||
} break;
|
||||
case STRIP_TYPE_COLORMIX:
|
||||
BLO_write_struct(writer, ColorMixVars, strip->effectdata);
|
||||
break;
|
||||
@@ -861,9 +868,12 @@ static bool strip_read_data_cb(Strip *strip, void *user_data)
|
||||
case STRIP_TYPE_GAUSSIAN_BLUR:
|
||||
BLO_read_struct(reader, GaussianBlurVars, &strip->effectdata);
|
||||
break;
|
||||
case STRIP_TYPE_TEXT:
|
||||
case STRIP_TYPE_TEXT: {
|
||||
BLO_read_struct(reader, TextVars, &strip->effectdata);
|
||||
break;
|
||||
TextVars *text = static_cast<TextVars *>(strip->effectdata);
|
||||
BLO_read_string(reader, &text->text_ptr);
|
||||
text->text_len_bytes = text->text_ptr ? strlen(text->text_ptr) : 0;
|
||||
} break;
|
||||
case STRIP_TYPE_COLORMIX:
|
||||
BLO_read_struct(reader, ColorMixVars, &strip->effectdata);
|
||||
break;
|
||||
|
||||
BIN
tests/files/sequence_editing/effects/reference/text_long_string.png
(Stored with Git LFS)
Normal file
BIN
tests/files/sequence_editing/effects/reference/text_long_string.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
tests/files/sequence_editing/effects/text_long_string.blend
(Stored with Git LFS)
Normal file
BIN
tests/files/sequence_editing/effects/text_long_string.blend
(Stored with Git LFS)
Normal file
Binary file not shown.
Reference in New Issue
Block a user