From 98fd1a942cc79508537645118ed94eae4c2b26e1 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 25 Aug 2025 06:40:53 +0000 Subject: [PATCH] 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. --- source/blender/blenkernel/BKE_vfont.hh | 14 ++- .../blender/blenkernel/intern/vfont_curve.cc | 85 ++++++++++++------- source/blender/editors/curve/editfont.cc | 26 +----- source/blender/makesdna/DNA_curve_enums.h | 17 ++-- source/blender/makesdna/DNA_curve_types.h | 16 ++-- .../nodes/node_geo_string_to_curves.cc | 4 +- 6 files changed, 84 insertions(+), 78 deletions(-) diff --git a/source/blender/blenkernel/BKE_vfont.hh b/source/blender/blenkernel/BKE_vfont.hh index 43eedc8876e..a0176ffa3f7 100644 --- a/source/blender/blenkernel/BKE_vfont.hh +++ b/source/blender/blenkernel/BKE_vfont.hh @@ -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, diff --git a/source/blender/blenkernel/intern/vfont_curve.cc b/source/blender/blenkernel/intern/vfont_curve.cc index e889779ff20..f219783687f 100644 --- a/source/blender/blenkernel/intern/vfont_curve.cc +++ b/source/blender/blenkernel/intern/vfont_curve.cc @@ -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); } } diff --git a/source/blender/editors/curve/editfont.cc b/source/blender/editors/curve/editfont.cc index 7c9dd8d7f1b..eada73a4526 100644 --- a/source/blender/editors/curve/editfont.cc +++ b/source/blender/editors/curve/editfont.cc @@ -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: diff --git a/source/blender/makesdna/DNA_curve_enums.h b/source/blender/makesdna/DNA_curve_enums.h index 493494b88ee..46053e7659e 100644 --- a/source/blender/makesdna/DNA_curve_enums.h +++ b/source/blender/makesdna/DNA_curve_enums.h @@ -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). */ diff --git a/source/blender/makesdna/DNA_curve_types.h b/source/blender/makesdna/DNA_curve_types.h index 44afa54e321..cf3bb201e8f 100644 --- a/source/blender/makesdna/DNA_curve_types.h +++ b/source/blender/makesdna/DNA_curve_types.h @@ -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; diff --git a/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc index 8aa9d3b1c5c..abdcb62b0c5 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc @@ -228,7 +228,7 @@ static std::optional get_text_layout(GeoNodeExecParams ¶ms) 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 create_curve_instances(GeoNodeExecParams ¶ms, 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) {