Refactor: move run-time flags from CharInfo::flag to CharTrans

The Curve, Curve::strinfo & EditFont::textbufinfo are now read-only
during evaluation. CharTrans is now used to store some evaluation
options such as wrap/overflow/smallcaps.

Since it's no longer possible to inspect evaluation flags from operators
without evaluating the curve, line beginning/end functionality has been
moved from the operator into vfont_to_curve.
This commit is contained in:
Campbell Barton
2025-08-25 06:40:53 +00:00
parent e17677ce73
commit 98fd1a942c
6 changed files with 84 additions and 78 deletions

View File

@@ -20,7 +20,11 @@ struct CharTrans {
float xof, yof;
float rot;
short linenr, charnr;
char dobreak;
uint dobreak : 1;
uint is_overflow : 1;
uint is_wrap : 1;
uint is_smallcaps : 1;
};
struct EditFontSelBox {
@@ -77,11 +81,14 @@ enum eEditFontMode {
FO_DUPLI = 4,
FO_PAGEUP = 8,
FO_PAGEDOWN = 9,
FO_SELCHANGE = 10,
FO_LINE_BEGIN = 10,
FO_LINE_END = 11,
FO_SELCHANGE = 12,
};
/** #BKE_vfont_to_curve will move the cursor in these cases. */
#define FO_CURS_IS_MOTION(mode) (ELEM(mode, FO_CURSUP, FO_CURSDOWN, FO_PAGEUP, FO_PAGEDOWN))
#define FO_CURS_IS_MOTION(mode) \
(ELEM(mode, FO_CURSUP, FO_CURSDOWN, FO_PAGEUP, FO_PAGEDOWN, FO_LINE_BEGIN, FO_LINE_END))
/* -------------------------------------------------------------------- */
/** \name VFont API
@@ -132,6 +139,7 @@ void BKE_vfont_char_build(Curve *cu,
ListBase *nubase,
unsigned int charcode,
const CharInfo *info,
bool is_smallcaps,
float ofsx,
float ofsy,
float rot,

View File

@@ -297,7 +297,7 @@ static VChar *vfont_char_find_or_placeholder(const VFontData *vfd,
* \return The shape used for the underline which may be passed in
* as the `ul_prev_nu` in future calls to this function.
*/
static Nurb *build_underline(Curve *cu,
static Nurb *build_underline(const Curve *cu,
ListBase *nubase,
const rctf *rect,
float yofs,
@@ -371,10 +371,11 @@ static Nurb *build_underline(Curve *cu,
return nu;
}
static void vfont_char_build_impl(Curve *cu,
static void vfont_char_build_impl(const Curve *cu,
ListBase *nubase,
const VChar *che,
const CharInfo *info,
const bool is_smallcaps,
float ofsx,
float ofsy,
float rot,
@@ -452,7 +453,7 @@ static void vfont_char_build_impl(Curve *cu,
}
bezt = nu->bezt;
if (info->flag & CU_CHINFO_SMALLCAPS_CHECK) {
if (is_smallcaps) {
const float sca = cu->smallcaps_scale;
for (int i = nu->pntsu; i > 0; i--) {
float *fp = bezt->vec[0];
@@ -489,6 +490,7 @@ void BKE_vfont_char_build(Curve *cu,
ListBase *nubase,
uint charcode,
const CharInfo *info,
const bool is_smallcaps,
float ofsx,
float ofsy,
float rot,
@@ -501,25 +503,25 @@ void BKE_vfont_char_build(Curve *cu,
}
VChar *che;
vfont_char_find(vfd, charcode, &che);
vfont_char_build_impl(cu, nubase, che, info, ofsx, ofsy, rot, charidx, fsize);
vfont_char_build_impl(cu, nubase, che, info, is_smallcaps, ofsx, ofsy, rot, charidx, fsize);
}
static float vfont_char_width(Curve *cu, VChar *che, const CharInfo *info)
static float vfont_char_width(const Curve *cu, VChar *che, const bool is_smallcaps)
{
/* The character wasn't found, probably `charcode = 0`, then the width shall be 0 as well. */
if (che == nullptr) {
return 0.0f;
}
if (info->flag & CU_CHINFO_SMALLCAPS_CHECK) {
if (is_smallcaps) {
return che->width * cu->smallcaps_scale;
}
return che->width;
}
static char32_t vfont_char_apply_smallcaps(char32_t charcode, const CharInfo *info)
static char32_t vfont_char_apply_smallcaps(char32_t charcode, const bool is_smallcaps)
{
if (UNLIKELY(info->flag & CU_CHINFO_SMALLCAPS_CHECK)) {
if (UNLIKELY(is_smallcaps)) {
return toupper(charcode);
}
return charcode;
@@ -666,7 +668,7 @@ struct TempLineInfo {
* with font styles, text boxes as well as text cursor placement.
*/
static bool vfont_to_curve(Object *ob,
Curve *cu,
const Curve *cu,
const eEditFontMode mode,
VFontToCurveIter *iter_data,
VFontCursor_Params *cursor_params,
@@ -679,7 +681,7 @@ static bool vfont_to_curve(Object *ob,
{
EditFont *ef = cu->editfont;
EditFontSelBox *selboxes = nullptr;
CharInfo *info = nullptr, *custrinfo;
const CharInfo *info = nullptr, *custrinfo;
TextBox tb_scale;
bool use_textbox;
VChar *che;
@@ -693,6 +695,7 @@ static bool vfont_to_curve(Object *ob,
int selstart = 0, selend = 0;
int cnr = 0, lnr = 0, wsnr = 0;
const char32_t *mem = nullptr;
bool mem_alloc = false;
const float font_size = cu->fsize * iter_data->scale_to_fit;
/* Shift down vertically to be 25% below & 75% above baseline (before font scale is applied). */
const float font_select_y_offset = 0.25;
@@ -761,6 +764,7 @@ static bool vfont_to_curve(Object *ob,
BLI_str_utf8_as_utf32(mem_tmp, cu->str, slen + 1);
mem = mem_tmp;
mem_alloc = true;
custrinfo = cu->strinfo;
}
@@ -798,10 +802,6 @@ static bool vfont_to_curve(Object *ob,
xtrax = 0.5f * cu->spacing - 0.5f;
for (i = 0; i < slen; i++) {
custrinfo[i].flag &= ~(CU_CHINFO_WRAP | CU_CHINFO_SMALLCAPS_CHECK | CU_CHINFO_OVERFLOW);
}
TextBoxBounds_ForCursor *tb_bounds_for_cursor = nullptr;
if (cursor_params != nullptr) {
if (cu->textoncurve == nullptr && (cu->totbox > 1) && (slen > 0)) {
@@ -827,7 +827,8 @@ static bool vfont_to_curve(Object *ob,
if (info->flag & CU_CHINFO_SMALLCAPS) {
charcode = towupper(charcode);
if (mem[i] != charcode) {
info->flag |= CU_CHINFO_SMALLCAPS_CHECK;
BLI_assert(ct == &chartransdata[i]);
ct->is_smallcaps = true;
}
}
/* The #vfont_char_apply_smallcaps function can be used from now on. */
@@ -844,7 +845,7 @@ static bool vfont_to_curve(Object *ob,
che = nullptr;
}
twidth = vfont_char_width(cu, che, info);
twidth = vfont_char_width(cu, che, ct->is_smallcaps);
/* Calculate positions. */
@@ -893,8 +894,9 @@ static bool vfont_to_curve(Object *ob,
}
i = j - 1;
xof = ct->xof;
BLI_assert(&ct[1] == &chartransdata[i + 1]);
ct[1].dobreak = 1;
custrinfo[i + 1].flag |= CU_CHINFO_WRAP;
ct[1].is_wrap = 1;
dobreak = true;
break;
}
@@ -904,7 +906,8 @@ static bool vfont_to_curve(Object *ob,
if (dobreak) {
if (tb_scale.h == 0.0f) {
/* NOTE: If underlined text is truncated away, the extra space is also truncated. */
custrinfo[i + 1].flag |= CU_CHINFO_OVERFLOW;
BLI_assert(&chartransdata[i + 1] == &ct[1]);
ct[1].is_overflow = 1;
}
/* Since a break was added, re-run this loop with `i` at it's new value. */
continue;
@@ -940,7 +943,7 @@ static bool vfont_to_curve(Object *ob,
}
else if (last_line == -1) {
last_line = lnr + 1;
info->flag |= CU_CHINFO_OVERFLOW;
ct->is_overflow = 1;
}
}
@@ -995,7 +998,7 @@ static bool vfont_to_curve(Object *ob,
}
/* Set the width of the character. */
twidth = vfont_char_width(cu, che, info);
twidth = vfont_char_width(cu, che, ct->is_smallcaps);
xof += (twidth * wsfac * (1.0f + (info->kern / 40.0f))) + xtrax;
@@ -1296,12 +1299,13 @@ static bool vfont_to_curve(Object *ob,
/* Rotate around center character. */
info = &custrinfo[i];
const char32_t charcode = vfont_char_apply_smallcaps(mem[i], info);
BLI_assert(ct == &chartransdata[i]);
const char32_t charcode = vfont_char_apply_smallcaps(mem[i], ct->is_smallcaps);
vfont_info_context_update(&vfinfo_ctx, cu, info);
che = vfont_char_find_or_placeholder(vfinfo_ctx.vfd, charcode, che_placeholder);
twidth = vfont_char_width(cu, che, info);
twidth = vfont_char_width(cu, che, ct->is_smallcaps);
dtime = distfac * 0.5f * twidth;
@@ -1356,7 +1360,7 @@ static bool vfont_to_curve(Object *ob,
}
}
if (ELEM(mode, FO_CURSUP, FO_CURSDOWN, FO_PAGEUP, FO_PAGEDOWN) &&
if (ELEM(mode, FO_CURSUP, FO_CURSDOWN, FO_PAGEUP, FO_PAGEDOWN, FO_LINE_BEGIN, FO_LINE_END) &&
iter_data->status == VFONT_TO_CURVE_INIT)
{
ct = &chartransdata[ef->pos];
@@ -1367,6 +1371,18 @@ static bool vfont_to_curve(Object *ob,
else if (ELEM(mode, FO_CURSDOWN, FO_PAGEDOWN) && ct->linenr == lnr) {
/* Pass. */
}
else if (mode == FO_LINE_BEGIN) {
/* Line wrap aware line beginning. */
while ((ef->pos > 0) && (chartransdata[ef->pos - 1].linenr == ct->linenr)) {
ef->pos -= 1;
}
}
else if (mode == FO_LINE_END) {
/* Line wrap aware line end. */
while ((ef->pos + 1 < slen) && (chartransdata[ef->pos + 1].linenr == ct->linenr)) {
ef->pos += 1;
}
}
else {
switch (mode) {
case FO_CURSUP:
@@ -1386,6 +1402,8 @@ static bool vfont_to_curve(Object *ob,
case FO_CURS:
case FO_DUPLI:
case FO_SELCHANGE:
case FO_LINE_BEGIN:
case FO_LINE_END:
break;
}
cnr = ct->charnr;
@@ -1478,16 +1496,15 @@ static bool vfont_to_curve(Object *ob,
ct = chartransdata;
for (i = 0; i < slen; i++) {
info = &(custrinfo[i]);
if ((cu->overflow == CU_OVERFLOW_TRUNCATE) && (ob && ob->mode != OB_MODE_EDIT) &&
(info->flag & CU_CHINFO_OVERFLOW))
ct->is_overflow)
{
break;
}
const char32_t charcode = vfont_char_apply_smallcaps(mem[i], info);
info = &(custrinfo[i]);
const char32_t charcode = vfont_char_apply_smallcaps(mem[i], ct->is_smallcaps);
/* We don't want to see any character for `\n`. */
if (charcode != '\n') {
@@ -1495,20 +1512,22 @@ static bool vfont_to_curve(Object *ob,
/* Find the character, the characters has to be in the memory already
* since character checking has been done earlier already. */
che = vfont_char_find_or_placeholder(vfinfo_ctx.vfd, charcode, che_placeholder);
vfont_char_build_impl(cu, r_nubase, che, info, ct->xof, ct->yof, ct->rot, i, font_size);
vfont_char_build_impl(
cu, r_nubase, che, info, ct->is_smallcaps, ct->xof, ct->yof, ct->rot, i, font_size);
if (info->flag & CU_CHINFO_UNDERLINE) {
float ulwidth, uloverlap = 0.0f;
rctf rect;
BLI_assert(&ct[1] == &chartransdata[i + 1]);
if ((i < (slen - 1)) && (mem[i + 1] != '\n') &&
((mem[i + 1] != ' ') || (custrinfo[i + 1].flag & CU_CHINFO_UNDERLINE)) &&
((custrinfo[i + 1].flag & CU_CHINFO_WRAP) == 0))
((ct[1].is_wrap) == 0))
{
uloverlap = xtrax;
}
twidth = vfont_char_width(cu, che, info);
twidth = vfont_char_width(cu, che, ct->is_smallcaps);
ulwidth = (twidth * (1.0f + (info->kern / 40.0f))) + uloverlap;
rect.xmin = ct->xof;
@@ -1757,7 +1776,7 @@ static bool vfont_to_curve(Object *ob,
MEM_freeN(chartransdata);
}
if (ef == nullptr) {
if (mem_alloc) {
MEM_freeN(mem);
}
return true;
@@ -1766,10 +1785,10 @@ static bool vfont_to_curve(Object *ob,
if (r_text) {
*r_text = mem;
*r_text_len = slen;
*r_text_free = (ef == nullptr);
*r_text_free = mem_alloc;
}
else {
if (ef == nullptr) {
if (mem_alloc) {
MEM_freeN(mem);
}
}

View File

@@ -1350,32 +1350,10 @@ static wmOperatorStatus move_cursor(bContext *C, int type, const bool select)
switch (type) {
case LINE_BEGIN:
while (ef->pos > 0) {
if (ef->textbuf[ef->pos - 1] == '\n') {
break;
}
if (ef->textbufinfo[ef->pos - 1].flag & CU_CHINFO_WRAP) {
break;
}
ef->pos--;
}
cursmove = FO_CURS;
cursmove = FO_LINE_BEGIN;
break;
case LINE_END:
while (ef->pos < ef->len) {
if (ef->textbuf[ef->pos] == 0) {
break;
}
if (ef->textbuf[ef->pos] == '\n') {
break;
}
if (ef->textbufinfo[ef->pos].flag & CU_CHINFO_WRAP) {
break;
}
ef->pos++;
}
cursmove = FO_CURS;
cursmove = FO_LINE_END;
break;
case TEXT_BEGIN:

View File

@@ -213,19 +213,20 @@ typedef enum eBezTriple_KeyframeType {
/* *************** CHARINFO **************** */
/** #CharInfo.flag */
/**
* #CharInfo.flag
*
* \note This must *not* be used for run-time / evaluation flags.
* If this is needed, see: #CharTrans.
*/
enum {
/* NOTE: CU_CHINFO_WRAP, CU_CHINFO_SMALLCAPS_TEST and CU_CHINFO_TRUNCATE are set dynamically. */
CU_CHINFO_BOLD = 1 << 0,
CU_CHINFO_ITALIC = 1 << 1,
CU_CHINFO_UNDERLINE = 1 << 2,
/** Word-wrap occurred here. */
CU_CHINFO_WRAP = 1 << 3,
CU_CHINFO_UNUSED_3 = 1 << 3, /* Dirty. */
CU_CHINFO_SMALLCAPS = 1 << 4,
/** Set at runtime, checks if case switching is needed. */
CU_CHINFO_SMALLCAPS_CHECK = 1 << 5,
/** Set at runtime, indicates char that doesn't fit in text boxes. */
CU_CHINFO_OVERFLOW = 1 << 6,
CU_CHINFO_UNUSED_5 = 1 << 5, /* Dirty. */
CU_CHINFO_UNUSED_6 = 1 << 6, /* Dirty. */
};
/** User adjustable as styles (not relating to run-time layout calculation). */

View File

@@ -306,7 +306,14 @@ typedef struct Curve {
float bevfac1, bevfac2;
char bevfac1_mapping, bevfac2_mapping;
char _pad2[2];
char _pad2[1];
/**
* If non-zero, the #editfont and #editnurb pointers are not owned by this #Curve. That means
* this curve is a container for the result of object geometry evaluation. This only works
* because evaluated object data never outlives original data.
*/
char edit_data_from_original;
/**
* A pointer to curve data from evaluation. Owned by the object's #geometry_set_eval, either as a
@@ -316,13 +323,6 @@ typedef struct Curve {
* original object data.
*/
const struct Curves *curve_eval;
/**
* If non-zero, the #editfont and #editnurb pointers are not owned by this #Curve. That means
* this curve is a container for the result of object geometry evaluation. This only works
* because evaluated object data never outlives original data.
*/
char edit_data_from_original;
char _pad3[7];
void *batch_cache;

View File

@@ -228,7 +228,7 @@ static std::optional<TextLayout> get_text_layout(GeoNodeExecParams &params)
CharTrans &ct = chartransdata[i];
layout.positions.append(float2(ct.xof, ct.yof) * layout.final_font_size);
if ((info[i].flag & CU_CHINFO_OVERFLOW) && (cu.overflow == CU_OVERFLOW_TRUNCATE)) {
if (ct.is_overflow && (cu.overflow == CU_OVERFLOW_TRUNCATE)) {
const int offset = BLI_str_utf8_offset_from_index(
layout.text.c_str(), layout.text.size(), i + 1);
layout.truncated_text = layout.text.substr(offset);
@@ -279,7 +279,7 @@ static Map<int, int> create_curve_instances(GeoNodeExecParams &params,
CharInfo charinfo = {0};
charinfo.mat_nr = 1;
BKE_vfont_char_build(&cu, &cu.nurb, layout.char_codes[i], &charinfo, 0, 0, 0, i, 1);
BKE_vfont_char_build(&cu, &cu.nurb, layout.char_codes[i], &charinfo, false, 0, 0, 0, i, 1);
Curves *curves_id = bke::curve_legacy_to_curves(cu);
if (curves_id == nullptr) {
if (pivot_required) {