Blender V5.0
vfont_curve.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
11
12#include <algorithm>
13#include <cmath>
14#include <cstdlib>
15#include <cstring>
16#include <cwctype>
17
18#include "MEM_guardedalloc.h"
19
20#include "BLI_ghash.h"
21#include "BLI_listbase.h"
22#include "BLI_math_base_safe.h"
23#include "BLI_math_matrix.h"
24#include "BLI_math_vector.h"
25#include "BLI_math_vector.hh"
26#include "BLI_rect.h"
27#include "BLI_string_utf8.h"
28#include "BLI_threads.h"
29#include "BLI_utildefines.h"
30
31#include "DNA_curve_types.h"
32#include "DNA_object_types.h"
33#include "DNA_vfont_types.h"
34
35#include "BKE_anim_path.h"
36#include "BKE_curve.hh"
37#include "BKE_object_types.hh"
38#include "BKE_vfont.hh"
39#include "BKE_vfontdata.hh"
40
47
48/* -------------------------------------------------------------------- */
51
55static void mid_v2v2(float a[2], float b[2])
56{
57 a[0] = b[0] = (a[0] * 0.5) + (b[0] * 0.5f);
58 a[1] = b[1] = (a[1] * 0.5) + (b[1] * 0.5f);
59}
60
61static float vfont_metrics_ascent(const VFontData_Metrics *metrics)
62{
63 return metrics->ascend_ratio * metrics->em_ratio;
64}
65static float vfont_metrics_descent(const VFontData_Metrics *metrics)
66{
67 return metrics->em_ratio - vfont_metrics_ascent(metrics);
68}
69
70static VFont *vfont_from_charinfo(const Curve &cu, const CharInfo *info)
71{
72 switch (info->flag & (CU_CHINFO_BOLD | CU_CHINFO_ITALIC)) {
73 case CU_CHINFO_BOLD:
74 return cu.vfontb ? cu.vfontb : cu.vfont;
76 return cu.vfonti ? cu.vfonti : cu.vfont;
78 return cu.vfontbi ? cu.vfontbi : cu.vfont;
79 default:
80 return cu.vfont;
81 }
82}
83
85{
86 if (vfont == nullptr) {
87 return nullptr;
88 }
89
90 /* Lazily initialize the data. */
91 if (!vfont->data) {
92
94
95 if (vfont->data) {
96 /* Check data again, since it might have been already initialized from other thread
97 * (previous check is not accurate or threading,
98 * just prevents unneeded lock if all the data is here for sure). */
100 return vfont->data;
101 }
102
104
106 }
107
108 return vfont->data;
109}
110
111static bool vfont_char_find(const VFontData *vfd, char32_t charcode, VChar **r_che)
112{
113 if (void **che_p = BLI_ghash_lookup_p(vfd->characters, POINTER_FROM_UINT(charcode))) {
114 *r_che = static_cast<VChar *>(*che_p);
115 return true;
116 }
117 *r_che = nullptr;
118 return false;
119}
120
127static VChar *vfont_char_ensure_with_lock(VFont *vfont, char32_t charcode)
128{
129 VChar *che;
130 if (vfont && vfont->data) {
131 VFontData *vfd = vfont->data;
133 bool che_found = vfont_char_find(vfd, charcode, &che);
135
136 /* The character wasn't in the current curve base so load it. */
137 if (che_found == false) {
139 /* Check it once again, char might have been already load
140 * between previous #BLI_rw_mutex_unlock() and this #BLI_rw_mutex_lock().
141 *
142 * Such a check should not be a bottleneck since it wouldn't
143 * happen often once all the chars are load. */
144 che_found = vfont_char_find(vfd, charcode, &che);
145 if (che_found == false) {
146 che = BKE_vfontdata_char_from_freetypefont(vfont, charcode);
147 }
149 }
150 }
151 else {
152 che = nullptr;
153 }
154 return che;
155}
156
158
159/* -------------------------------------------------------------------- */
165
168 const VFontData_Metrics *metrics = nullptr;
169
170 bool initialized = false;
171
173 struct {
175 VChar che[2] = {};
176
178 Nurb nu[2] = {};
180 BezTriple bezt[2][4] = {};
182};
183
188static VChar *vfont_placeholder_ensure(VCharPlaceHolder &che_placeholder, char32_t charcode)
189{
190 const int che_index = (charcode == ' ') ? 0 : 1;
191
192 if (!che_placeholder.initialized) {
193 const VFontData_Metrics *metrics = che_placeholder.metrics;
194
195 const float ascent = vfont_metrics_ascent(metrics);
196
197 const float line_width = 0.05 * metrics->em_ratio;
198
199 /* The rectangle size within the available bounds. */
200 const blender::float2 size_factor = {
201 0.9f,
202 0.9f - (line_width * 2),
203 };
204 const float size_factor_margin_y = ((1.0 - size_factor.x) / 2.0f);
205
206 /* Always initialize all placeholders, only if one is used. */
207 VChar *che;
208
209 /* Space - approximately en width. */
210 che = &che_placeholder.data.che[0];
211 che->width = 0.5f * metrics->em_ratio;
212
213 /* Hollow rectangle - approximately en width. */
214 che = &che_placeholder.data.che[1];
215 che->width = 0.5f * metrics->em_ratio;
216
217 for (int nu_index = 0; nu_index < ARRAY_SIZE(che_placeholder.data.nu); nu_index++) {
218 Nurb *nu = &che_placeholder.data.nu[nu_index];
219 BLI_addtail(&che->nurbsbase, nu);
220
221 /* In this case poly makes more sense, follow the convention for others. */
222 nu->type = CU_BEZIER;
223 nu->resolu = 8;
224
225 nu->bezt = &che_placeholder.data.bezt[nu_index][0];
226 nu->pntsu = 4;
227 nu->pntsv = 1;
228 nu->flagu |= CU_NURB_CYCLIC;
229
230 rctf bounds;
231 bounds.xmin = (che->width * size_factor_margin_y);
232 bounds.xmax = (che->width * 1.0 - size_factor_margin_y);
233 bounds.ymin = 0.0f;
234 bounds.ymax = ascent * size_factor.y;
235
236 if (nu_index == 1) {
237 bounds.xmin += line_width;
238 bounds.xmax -= line_width;
239 bounds.ymin += line_width;
240 bounds.ymax -= line_width;
241 }
242
243 if (nu_index == 0) {
244 ARRAY_SET_ITEMS(nu->bezt[0].vec[1], bounds.xmin, bounds.ymin);
245 ARRAY_SET_ITEMS(nu->bezt[1].vec[1], bounds.xmin, bounds.ymax);
246 ARRAY_SET_ITEMS(nu->bezt[2].vec[1], bounds.xmax, bounds.ymax);
247 ARRAY_SET_ITEMS(nu->bezt[3].vec[1], bounds.xmax, bounds.ymin);
248 }
249 else {
250 /* Holes are meant to use reverse winding, while not essential for Blender.
251 * Do this for the sake of correctness. */
252 ARRAY_SET_ITEMS(nu->bezt[3].vec[1], bounds.xmin, bounds.ymin);
253 ARRAY_SET_ITEMS(nu->bezt[2].vec[1], bounds.xmin, bounds.ymax);
254 ARRAY_SET_ITEMS(nu->bezt[1].vec[1], bounds.xmax, bounds.ymax);
255 ARRAY_SET_ITEMS(nu->bezt[0].vec[1], bounds.xmax, bounds.ymin);
256 }
257
258 for (int bezt_index = 0; bezt_index < 4; bezt_index++) {
259 BezTriple *bezt = &nu->bezt[bezt_index];
260 bezt->radius = 1.0;
261 bezt->h1 = HD_VECT;
262 bezt->h2 = HD_VECT;
263 }
264 }
265 che_placeholder.initialized = true;
266 }
267 return &che_placeholder.data.che[che_index];
268}
269
274 char32_t charcode,
275 VCharPlaceHolder &che_placeholder)
276{
277 VChar *che = nullptr;
278 if (vfd) {
279 vfont_char_find(vfd, charcode, &che);
280 }
281 if (UNLIKELY(che == nullptr)) {
282 che = vfont_placeholder_ensure(che_placeholder, charcode);
283 }
284 return che;
285}
286
288
289/* -------------------------------------------------------------------- */
292
301static Nurb *build_underline(const Curve &cu,
302 ListBase *nubase,
303 const rctf *rect,
304 const float yofs,
305 const float rotate,
306 const int charidx,
307 const short mat_nr,
308 const float font_size,
309 Nurb *ul_prev_nu)
310{
311 Nurb *nu;
312 BPoint *bp;
313
314 nu = MEM_callocN<Nurb>("underline_nurb");
315 nu->resolu = cu.resolu;
316 nu->bezt = nullptr;
317 nu->knotsu = nu->knotsv = nullptr;
318 nu->charidx = charidx + 1000;
319 if (mat_nr >= 0) {
320 nu->mat_nr = mat_nr;
321 }
322 nu->pntsu = 4;
323 nu->pntsv = 1;
324 nu->orderu = 4;
325 nu->orderv = 1;
326 nu->flagu = CU_NURB_CYCLIC;
327
328 bp = MEM_calloc_arrayN<BPoint>(4, "underline_bp");
329
330 copy_v4_fl4(bp[0].vec, rect->xmin, (rect->ymax + yofs), 0.0f, 1.0f);
331 copy_v4_fl4(bp[1].vec, rect->xmax, (rect->ymax + yofs), 0.0f, 1.0f);
332 copy_v4_fl4(bp[2].vec, rect->xmax, (rect->ymin + yofs), 0.0f, 1.0f);
333 copy_v4_fl4(bp[3].vec, rect->xmin, (rect->ymin + yofs), 0.0f, 1.0f);
334
335 /* Used by curve extrusion. */
336 bp[0].radius = bp[1].radius = bp[2].radius = bp[3].radius = 1.0f;
337
338 nu->bp = bp;
339 BLI_addtail(nubase, nu);
340
341 if (rotate != 0.0f) {
342 float si = sinf(rotate);
343 float co = cosf(rotate);
344
345 for (int i = nu->pntsu; i > 0; i--) {
346 float *fp = bp->vec;
347
348 float x = fp[0] - rect->xmin;
349 float y = fp[1] - rect->ymin;
350
351 fp[0] = (+co * x + si * y) + rect->xmin;
352 fp[1] = (-si * x + co * y) + rect->ymin;
353
354 bp++;
355 }
356
357 bp = nu->bp;
358 }
359
360 mul_v2_fl(bp[0].vec, font_size);
361 mul_v2_fl(bp[1].vec, font_size);
362 mul_v2_fl(bp[2].vec, font_size);
363 mul_v2_fl(bp[3].vec, font_size);
364
365 if (ul_prev_nu) {
366 /* Weld locations with the previous, adjacent underline. */
367 BPoint *bp_prev = ul_prev_nu->bp;
368 mid_v2v2(bp_prev[1].vec, bp[0].vec); /* Lower line. */
369 mid_v2v2(bp_prev[2].vec, bp[3].vec); /* Upper line. */
370 }
371
372 return nu;
373}
374
375static void vfont_char_build_impl(const Curve &cu,
376 ListBase *nubase,
377 const VChar *che,
378 const CharInfo *info,
379 const bool is_smallcaps,
380 const blender::float2 &offset,
381 const float rotate,
382 const int charidx,
383 const float fsize)
384{
385 /* Make a copy at distance `offset` with shear. */
386 float shear = cu.shear;
387 float si = sinf(rotate);
388 float co = cosf(rotate);
389
390 /* Select the glyph data */
391 const Nurb *nu_from_vchar = nullptr;
392 if (che) {
393 nu_from_vchar = static_cast<Nurb *>(che->nurbsbase.first);
394 }
395
396 /* Create the character. */
397 while (nu_from_vchar) {
398 const BezTriple *bezt_from_vchar = nu_from_vchar->bezt;
399 if (bezt_from_vchar) {
400 Nurb *nu = MEM_mallocN<Nurb>("duplichar_nurb");
401 if (nu == nullptr) {
402 break;
403 }
404 *nu = blender::dna::shallow_copy(*nu_from_vchar);
405 nu->resolu = cu.resolu;
406 nu->bp = nullptr;
407 nu->knotsu = nu->knotsv = nullptr;
408 nu->flag = CU_SMOOTH;
409 nu->charidx = charidx;
410 if (info->mat_nr > 0) {
411 nu->mat_nr = info->mat_nr;
412 }
413 else {
414 nu->mat_nr = 0;
415 }
416 int u = nu->pntsu;
417
418 BezTriple *bezt = MEM_malloc_arrayN<BezTriple>(size_t(u), "duplichar_bezt2");
419 if (bezt == nullptr) {
420 MEM_freeN(nu);
421 break;
422 }
423 memcpy(bezt, bezt_from_vchar, u * sizeof(BezTriple));
424 nu->bezt = bezt;
425
426 if (shear != 0.0f) {
427 bezt = nu->bezt;
428
429 for (int i = nu->pntsu; i > 0; i--) {
430 bezt->vec[0][0] += shear * bezt->vec[0][1];
431 bezt->vec[1][0] += shear * bezt->vec[1][1];
432 bezt->vec[2][0] += shear * bezt->vec[2][1];
433 bezt++;
434 }
435 }
436 if (rotate != 0.0f) {
437 bezt = nu->bezt;
438 for (int i = nu->pntsu; i > 0; i--) {
439 float *fp = bezt->vec[0];
440
441 float x = fp[0];
442 fp[0] = co * x + si * fp[1];
443 fp[1] = -si * x + co * fp[1];
444 x = fp[3];
445 fp[3] = co * x + si * fp[4];
446 fp[4] = -si * x + co * fp[4];
447 x = fp[6];
448 fp[6] = co * x + si * fp[7];
449 fp[7] = -si * x + co * fp[7];
450
451 bezt++;
452 }
453 }
454 bezt = nu->bezt;
455
456 if (is_smallcaps) {
457 const float sca = cu.smallcaps_scale;
458 for (int i = nu->pntsu; i > 0; i--) {
459 float *fp = bezt->vec[0];
460 fp[0] *= sca;
461 fp[1] *= sca;
462 fp[3] *= sca;
463 fp[4] *= sca;
464 fp[6] *= sca;
465 fp[7] *= sca;
466 bezt++;
467 }
468 }
469 bezt = nu->bezt;
470
471 for (int i = nu->pntsu; i > 0; i--) {
472 float *fp = bezt->vec[0];
473 fp[0] = (fp[0] + offset.x) * fsize;
474 fp[1] = (fp[1] + offset.y) * fsize;
475 fp[3] = (fp[3] + offset.x) * fsize;
476 fp[4] = (fp[4] + offset.y) * fsize;
477 fp[6] = (fp[6] + offset.x) * fsize;
478 fp[7] = (fp[7] + offset.y) * fsize;
479 bezt++;
480 }
481
482 BLI_addtail(nubase, nu);
483 }
484
485 nu_from_vchar = nu_from_vchar->next;
486 }
487}
488
490 ListBase *nubase,
491 uint charcode,
492 const CharInfo *info,
493 const bool is_smallcaps,
494 const blender::float2 &offset,
495 float rotate,
496 int charidx,
497 const float fsize)
498{
500 if (!vfd) {
501 return;
502 }
503 VChar *che;
504 vfont_char_find(vfd, charcode, &che);
505 vfont_char_build_impl(cu, nubase, che, info, is_smallcaps, offset, rotate, charidx, fsize);
506}
507
508static float vfont_char_width(const Curve &cu, VChar *che, const bool is_smallcaps)
509{
510 /* The character wasn't found, probably `charcode = 0`, then the width shall be 0 as well. */
511 if (che == nullptr) {
512 return 0.0f;
513 }
514 if (is_smallcaps) {
515 return che->width * cu.smallcaps_scale;
516 }
517
518 return che->width;
519}
520
521static char32_t vfont_char_apply_smallcaps(char32_t charcode, const bool is_smallcaps)
522{
523 if (UNLIKELY(is_smallcaps)) {
524 return toupper(charcode);
525 }
526 return charcode;
527}
528
529static void textbox_scale(TextBox *tb_dst, const TextBox *tb_src, float scale)
530{
531 tb_dst->x = tb_src->x * scale;
532 tb_dst->y = tb_src->y * scale;
533 tb_dst->w = tb_src->w * scale;
534 tb_dst->h = tb_src->h * scale;
535}
536
538
539/* -------------------------------------------------------------------- */
548
552 struct {
553 float min;
554 float max;
556 bool ok;
568};
569
571
572/* -------------------------------------------------------------------- */
579
587
588enum {
593};
594
595#define FONT_TO_CURVE_SCALE_ITERATIONS 20
596#define FONT_TO_CURVE_SCALE_THRESHOLD 0.0001f
597
599
600/* -------------------------------------------------------------------- */
603
608
609static void vfont_info_context_init(VFontInfoContext *vfinfo_ctx, const Curve &cu)
610{
611 BLI_assert(!vfinfo_ctx->vfont);
612 BLI_assert(!vfinfo_ctx->vfd);
613
614 vfinfo_ctx->vfont = cu.vfont;
615 vfinfo_ctx->vfd = vfont_data_ensure_with_lock(vfinfo_ctx->vfont);
616}
617
619 const Curve &cu,
620 const CharInfo *info)
621{
622 VFont *vfont = vfont_from_charinfo(cu, info);
623 if (vfinfo_ctx->vfont != vfont) {
624 vfinfo_ctx->vfont = vfont;
625 vfinfo_ctx->vfd = vfont_data_ensure_with_lock(vfont);
626 }
627}
628
630
631/* -------------------------------------------------------------------- */
634
650
656 float x_min;
658 float x_max;
663};
664
669static bool vfont_to_curve(Object *ob,
670 const Curve &cu,
671 const eEditFontMode mode,
672 VFontToCurveIter &iter_data,
673 VFontCursor_Params *cursor_params,
674 ListBase *r_nubase,
675 const char32_t **r_text,
676 int *r_text_len,
677 bool *r_text_free,
678 CharTrans **r_chartransdata,
679 float *r_font_size_eval)
680{
681 EditFont *ef = cu.editfont;
682 EditFontSelBox *selboxes = nullptr;
683 const CharInfo *info = nullptr, *custrinfo;
684 TextBox tb_scale;
685 VChar *che;
686 CharTrans *chartransdata = nullptr, *ct;
687 TempLineInfo *lineinfo;
688 float xtrax, linedist;
689 float twidth = 0;
690 int i, slen, j;
691 int curbox;
692 /* These values are only set to the selection range when `selboxes` is non-null. */
693 int selstart = 0, selend = 0;
694 int cnr = 0, lnr = 0, wsnr = 0;
695 const char32_t *mem = nullptr;
696 bool mem_alloc = false;
697 const float font_size = cu.fsize * iter_data.scale_to_fit;
698 /* Shift down vertically to be 25% below & 75% above baseline (before font scale is applied). */
699 const float font_select_y_offset = 0.25;
700 const bool word_wrap = iter_data.word_wrap;
701 const blender::float2 cu_offset_scale = {
702 safe_divide(cu.xof, font_size),
703 safe_divide(cu.yof, font_size),
704 };
705 int last_line = -1;
706 /* Length of the text disregarding \n breaks. */
707 float current_line_length = 0.0f;
708 float longest_line_length = 0.0f;
709
710 /* Text at the beginning of the last used text-box (use for y-axis alignment).
711 * We over-allocate by one to simplify logic of getting last char. */
712 blender::Array<int> i_textbox_array(cu.totbox + 1, 0);
713
714#define MARGIN_X_MIN (cu_offset_scale.x + tb_scale.x)
715#define MARGIN_Y_MIN (cu_offset_scale.y + tb_scale.y)
716
717 /* NOTE: do calculations including the trailing `\0` of a string
718 * because the cursor can be at that location. */
719
720 BLI_assert(ob == nullptr || ob->type == OB_FONT);
721
722 /* Read-file ensures non-null, must have become null at run-time, this is a bug! */
723 if (UNLIKELY(!(cu.str && cu.tb && (ef ? ef->textbufinfo : cu.strinfo)))) {
724 BLI_assert(0);
725 return false;
726 }
727
728 /* Set font data */
729 VFontInfoContext vfinfo_ctx = {nullptr};
730 vfont_info_context_init(&vfinfo_ctx, cu);
731
732 /* This must only be used for calculations which apply to all text,
733 * for character level queries, values from `vfinfo_ctx` must be updated & used.
734 * Note that this can be null. */
735 VFontData_Metrics _vfont_metrics_default_buf;
736 const VFontData_Metrics *metrics;
737 if (vfinfo_ctx.vfd) {
738 metrics = &vfinfo_ctx.vfd->metrics;
739 }
740 else {
741 BKE_vfontdata_metrics_get_defaults(&_vfont_metrics_default_buf);
742 metrics = &_vfont_metrics_default_buf;
743 }
744
745 VCharPlaceHolder che_placeholder = {
746 /*metrics*/ metrics,
747 };
748
749 if (ef) {
750 slen = ef->len;
751 mem = ef->textbuf;
752 custrinfo = ef->textbufinfo;
753 }
754 else {
755 char32_t *mem_tmp;
756 slen = cu.len_char32;
757
758 /* Create unicode string. */
759 mem_tmp = MEM_malloc_arrayN<char32_t>(size_t(slen) + 1, "convertedmem");
760 if (!mem_tmp) {
761 return false;
762 }
763
764 BLI_str_utf8_as_utf32(mem_tmp, cu.str, slen + 1);
765
766 mem = mem_tmp;
767 mem_alloc = true;
768 custrinfo = cu.strinfo;
769 }
770
771 /* Only manipulate the edit-font if this object is in edit-mode, otherwise it's unnecessary
772 * as well as crashing since manipulating the #EditFont here isn't thread-safe, see: #144970.
773 *
774 * NOTE(@ideasman42): Relying on the objects mode here isn't as fool-proof as I'd like,
775 * however, even in cases where object data is shared between two different objects,
776 * both active in different windows - it's not possible to enter edit on both at the same time.
777 * If problems are found with this method, other checks could be investigated. */
778 if (ef) {
779 if (ob && (ob->mode & OB_MODE_EDIT)) {
780 /* Pass. */
781 }
782 else {
783 /* Other modes manipulate `ef->pos` which must only be done when this object is in edit-mode.
784 * Not when a curve that happens to have edit-mode data is evaluated
785 * (typically a linked duplicate). */
787
788 /* Since all data has been accessed that's needed, set as null since it's
789 * important never to manipulate this data from multiple threads at once. */
790 ef = nullptr;
791 }
792 }
793
794 if (ef != nullptr) {
795 if (ef->selboxes) {
796 MEM_freeN(ef->selboxes);
797 }
798
799 if (BKE_vfont_select_get(&cu, &selstart, &selend)) {
800 ef->selboxes_len = (selend - selstart) + 1;
801 ef->selboxes = MEM_calloc_arrayN<EditFontSelBox>(ef->selboxes_len, "font selboxes");
802 }
803 else {
804 ef->selboxes_len = 0;
805 ef->selboxes = nullptr;
806 }
807
808 selboxes = ef->selboxes;
809 }
810
811 /* Calculate the offset and rotation of each char. */
812 ct = chartransdata = MEM_calloc_arrayN<CharTrans>(size_t(slen) + 1, "buildtext");
813
814 /* We assume the worst case: 1 character per line (is freed at end anyway). */
815 lineinfo = MEM_malloc_arrayN<TempLineInfo>(size_t(slen) * 2 + 1, "lineinfo");
816
817 linedist = cu.linedist;
818
819 curbox = 0;
820 textbox_scale(&tb_scale, &cu.tb[curbox], safe_divide(1.0f, font_size));
821 const bool use_textbox = (tb_scale.w != 0.0f);
822
823 blender::float2 offset{
826 };
827 xtrax = 0.5f * cu.spacing - 0.5f;
828
829 TextBoxBounds_ForCursor *tb_bounds_for_cursor = nullptr;
830 if (cursor_params != nullptr) {
831 if (cu.textoncurve == nullptr && (cu.totbox > 1) && (slen > 0)) {
832 tb_bounds_for_cursor = MEM_malloc_arrayN<TextBoxBounds_ForCursor>(size_t(cu.totbox),
833 "TextboxBounds_Cursor");
834 for (curbox = 0; curbox < cu.totbox; curbox++) {
835 TextBoxBounds_ForCursor *tb_bounds = &tb_bounds_for_cursor[curbox];
836 tb_bounds->char_index_last = -1;
837 tb_bounds->bounds.xmin = FLT_MAX;
838 tb_bounds->bounds.xmax = -FLT_MAX;
839 tb_bounds->bounds.ymin = FLT_MAX;
840 tb_bounds->bounds.ymax = -FLT_MAX;
841 }
842 }
843 curbox = 0;
844 }
845
846 i = 0;
847 while (i <= slen) {
848 /* Characters in the list. */
849 info = &custrinfo[i];
850 char32_t charcode = mem[i];
851 if (info->flag & CU_CHINFO_SMALLCAPS) {
852 charcode = towupper(charcode);
853 if (mem[i] != charcode) {
854 BLI_assert(ct == &chartransdata[i]);
855 ct->is_smallcaps = true;
856 }
857 }
858 /* The #vfont_char_apply_smallcaps function can be used from now on. */
859
860 vfont_info_context_update(&vfinfo_ctx, cu, info);
861
862 if (!ELEM(charcode, '\n', '\0')) {
863 che = vfont_char_ensure_with_lock(vfinfo_ctx.vfont, charcode);
864 if (che == nullptr) {
865 che = vfont_placeholder_ensure(che_placeholder, charcode);
866 }
867 }
868 else {
869 che = nullptr;
870 }
871
872 twidth = vfont_char_width(cu, che, ct->is_smallcaps);
873
874 /* Calculate positions. */
875
876 if ((tb_scale.w != 0.0f) && (ct->do_break == 0)) { /* May need wrapping. */
877 const float x_available = cu_offset_scale.x + tb_scale.w;
878 const float x_used = (offset.x - tb_scale.x) + twidth;
879
880 if (word_wrap == false) {
881 /* When scale to fit is used, don't do any wrapping.
882 *
883 * Floating precision error can cause the text to be slightly larger.
884 * Assert this is a small value as large values indicate incorrect
885 * calculations with scale-to-fit which shouldn't be ignored. See #89241. */
886 if (x_used > x_available) {
887 BLI_assert_msg(compare_ff_relative(x_used, x_available, FLT_EPSILON, 64),
888 "VFontToCurveIter.scale_to_fit not set correctly!");
889 }
890 }
891 else if (x_used > x_available) {
892 // CLOG_WARN(&LOG, "linewidth exceeded: %c%c%c...", mem[i], mem[i+1], mem[i+2]);
893 bool do_break = false;
894 for (j = i; (mem[j] != '\n') && (chartransdata[j].do_break == 0); j--) {
895
896 /* Special case when there are no breaks possible. */
897 if (UNLIKELY(j == 0)) {
898 if (i == slen) {
899 /* Use the behavior of zero a height text-box when a break cannot be inserted.
900 *
901 * Typically when a text-box has any height and overflow is set to scale
902 * the text will wrap to fit the width as necessary. When wrapping isn't
903 * possible it's important to use the same code-path as zero-height lines.
904 * Without this exception a single word will not scale-to-fit (see: #95116). */
905 tb_scale.h = 0.0f;
906 }
907 break;
908 }
909
910 if (ELEM(mem[j], ' ', '-')) {
911 ct -= (i - (j - 1));
912 cnr -= (i - (j - 1));
913 if (mem[j] == ' ') {
914 wsnr--;
915 }
916 if (mem[j] == '-') {
917 wsnr++;
918 }
919 i = j - 1;
920 offset.x = ct->offset.x;
921 BLI_assert(&ct[1] == &chartransdata[i + 1]);
922 ct[1].do_break = 1;
923 ct[1].is_wrap = 1;
924 do_break = true;
925 break;
926 }
927 BLI_assert(chartransdata[j].do_break == 0);
928 }
929
930 if (do_break) {
931 if (tb_scale.h == 0.0f) {
932 /* NOTE: If underlined text is truncated away, the extra space is also truncated. */
933 BLI_assert(&chartransdata[i + 1] == &ct[1]);
934 ct[1].is_overflow = 1;
935 }
936 /* Since a break was added, re-run this loop with `i` at it's new value. */
937 continue;
938 }
939 }
940 }
941
942 if (charcode == '\n' || charcode == 0 || ct->do_break) {
943 ct->offset = offset;
944 ct->linenr = lnr;
945 ct->charnr = cnr;
946
947 offset.y -= linedist;
948
949 lineinfo[lnr].x_min = (offset.x - xtrax) - tb_scale.x;
950 lineinfo[lnr].x_max = tb_scale.w;
951 lineinfo[lnr].char_nr = cnr;
952 lineinfo[lnr].wspace_nr = wsnr;
953
954 if (tb_bounds_for_cursor != nullptr) {
955 tb_bounds_for_cursor[curbox].char_index_last = i;
956 }
957
958 if ((tb_scale.h != 0.0f) &&
959 (-(offset.y - tb_scale.y) > (tb_scale.h - linedist) - cu_offset_scale.y))
960 {
961 if (cu.totbox > (curbox + 1)) {
962 curbox++;
963 i_textbox_array[curbox] = i + 1;
964
965 textbox_scale(&tb_scale, &cu.tb[curbox], 1.0f / font_size);
966
967 offset.y = MARGIN_Y_MIN;
968 }
969 else if (last_line == -1) {
970 last_line = lnr + 1;
971 ct->is_overflow = 1;
972 }
973 }
974
975 current_line_length += offset.x - MARGIN_X_MIN;
976 if (ct->do_break) {
977 current_line_length += twidth;
978 }
979 else {
980 longest_line_length = std::max(current_line_length, longest_line_length);
981 current_line_length = 0.0f;
982 }
983
984 offset.x = MARGIN_X_MIN;
985 lnr++;
986 cnr = 0;
987 wsnr = 0;
988 }
989 else if (charcode == '\t') { /* Tab character. */
990 float tabfac;
991
992 ct->offset = offset;
993 ct->linenr = lnr;
994 ct->charnr = cnr++;
995
996 tabfac = (offset.x - MARGIN_X_MIN + 0.01f);
997 tabfac = 2.0f * ceilf(tabfac / 2.0f);
998 offset.x = MARGIN_X_MIN + tabfac;
999 }
1000 else {
1001 EditFontSelBox *sb = nullptr;
1002 float wsfac;
1003
1004 ct->offset = offset;
1005 ct->linenr = lnr;
1006 ct->charnr = cnr++;
1007
1008 if (selboxes && (i >= selstart) && (i <= selend)) {
1009 sb = &selboxes[i - selstart];
1010 sb->y = (offset.y - font_select_y_offset) * font_size - linedist * font_size * 0.1f;
1011 sb->h = linedist * font_size;
1012 sb->w = offset.x * font_size;
1013 }
1014
1015 if (charcode == ' ') { /* Space character. */
1016 wsfac = cu.wordspace;
1017 wsnr++;
1018 }
1019 else {
1020 wsfac = 1.0f;
1021 }
1022
1023 /* Set the width of the character. */
1024 twidth = vfont_char_width(cu, che, ct->is_smallcaps);
1025
1026 offset.x += (twidth * wsfac * (1.0f + (info->kern / 40.0f))) + xtrax;
1027
1028 if (sb) {
1029 sb->w = (offset.x * font_size) - sb->w;
1030 }
1031 }
1032 ct++;
1033 i++;
1034 }
1035
1036 current_line_length += offset.x + twidth - MARGIN_X_MIN;
1037 longest_line_length = std::max(current_line_length, longest_line_length);
1038
1039 if (ef && selboxes) {
1040 /* Set combined style flags for the selected string. Start with all styles then
1041 * remove one if ANY characters do not have it. Break out if we've removed them all. */
1043 for (int k = selstart; k <= selend && ef->select_char_info_flag; k++) {
1044 info = &custrinfo[k];
1045 ef->select_char_info_flag &= info->flag;
1046 }
1047 }
1048
1049 if (cu.spacemode != CU_ALIGN_X_LEFT) {
1050 ct = chartransdata;
1051
1052 if (cu.spacemode == CU_ALIGN_X_RIGHT) {
1053 TempLineInfo *li;
1054
1055 for (i = 0, li = lineinfo; i < lnr; i++, li++) {
1056 li->x_min = (li->x_max - li->x_min) + cu_offset_scale.x;
1057 }
1058
1059 for (i = 0; i <= slen; i++) {
1060 ct->offset.x += lineinfo[ct->linenr].x_min;
1061 ct++;
1062 }
1063 }
1064 else if (cu.spacemode == CU_ALIGN_X_MIDDLE) {
1065 TempLineInfo *li;
1066
1067 for (i = 0, li = lineinfo; i < lnr; i++, li++) {
1068 li->x_min = ((li->x_max - li->x_min) + cu_offset_scale.x) / 2.0f;
1069 }
1070
1071 for (i = 0; i <= slen; i++) {
1072 ct->offset.x += lineinfo[ct->linenr].x_min;
1073 ct++;
1074 }
1075 }
1076 else if ((cu.spacemode == CU_ALIGN_X_FLUSH) && use_textbox) {
1077 TempLineInfo *li;
1078
1079 for (i = 0, li = lineinfo; i < lnr; i++, li++) {
1080 li->x_min = ((li->x_max - li->x_min) + cu_offset_scale.x);
1081
1082 if (li->char_nr > 1) {
1083 li->x_min /= float(li->char_nr - 1);
1084 }
1085 }
1086 for (i = 0; i <= slen; i++) {
1087 for (j = i; !ELEM(mem[j], '\0', '\n') && (chartransdata[j].do_break == 0) && (j < slen);
1088 j++)
1089 {
1090 /* Pass. */
1091 }
1092
1093 // if ((mem[j] != '\n') && (mem[j])) {
1094 ct->offset.x += ct->charnr * lineinfo[ct->linenr].x_min;
1095 // }
1096 ct++;
1097 }
1098 }
1099 else if ((cu.spacemode == CU_ALIGN_X_JUSTIFY) && use_textbox) {
1100 float curofs = 0.0f;
1101 for (i = 0; i <= slen; i++) {
1102 for (j = i; (mem[j]) && (mem[j] != '\n') && (chartransdata[j].do_break == 0) && (j < slen);
1103 j++)
1104 {
1105 /* Pass. */
1106 }
1107
1108 if ((mem[j] != '\n') && (chartransdata[j].do_break != 0)) {
1109 if (mem[i] == ' ') {
1110 TempLineInfo *li;
1111
1112 li = &lineinfo[ct->linenr];
1113 curofs += ((li->x_max - li->x_min) + cu_offset_scale.x) / float(li->wspace_nr);
1114 }
1115 ct->offset.x += curofs;
1116 }
1117 if (mem[i] == '\n' || chartransdata[i].do_break) {
1118 curofs = 0;
1119 }
1120 ct++;
1121 }
1122 }
1123 }
1124
1125 /* Top-baseline is default, in this case, do nothing. */
1126 if (cu.align_y != CU_ALIGN_Y_TOP_BASELINE) {
1127 if (tb_scale.h != 0.0f) {
1128 /* We need to loop all the text-boxes even the "full" ones.
1129 * This way they all get the same vertical padding. */
1130 for (int tb_index = 0; tb_index < cu.totbox; tb_index++) {
1131 CharTrans *ct_first, *ct_last;
1132 const int i_textbox = i_textbox_array[tb_index];
1133 const int i_textbox_next = i_textbox_array[tb_index + 1];
1134 const bool is_last_filled_textbox = ELEM(i_textbox_next, 0, slen + 1);
1135 int lines;
1136
1137 ct_first = chartransdata + i_textbox;
1138 ct_last = chartransdata + (is_last_filled_textbox ? slen : i_textbox_next - 1);
1139 lines = ct_last->linenr - ct_first->linenr + 1;
1140
1141 if (cu.overflow == CU_OVERFLOW_TRUNCATE) {
1142 /* Ensure overflow doesn't truncate text, before centering vertically
1143 * giving odd/buggy results, see: #66614. */
1144 if ((tb_index == cu.totbox - 1) && (last_line != -1)) {
1145 lines = last_line - ct_first->linenr;
1146 }
1147 }
1148
1149 textbox_scale(&tb_scale, &cu.tb[tb_index], 1.0f / font_size);
1150 /* The initial Y origin of the text-box is hard-coded to 1.0f * text scale. */
1151 const float textbox_y_origin = 1.0f;
1152 float yoff = 0.0f;
1153
1154 switch (cu.align_y) {
1156 break;
1157 case CU_ALIGN_Y_TOP:
1158 yoff = textbox_y_origin - vfont_metrics_ascent(metrics);
1159 break;
1160 case CU_ALIGN_Y_CENTER:
1161 yoff = ((((metrics->em_ratio + (lines - 1) * linedist) * 0.5f) -
1162 vfont_metrics_ascent(metrics)) -
1163 (tb_scale.h * 0.5f) + textbox_y_origin);
1164 break;
1166 yoff = textbox_y_origin + ((lines - 1) * linedist) - tb_scale.h;
1167 break;
1168 case CU_ALIGN_Y_BOTTOM:
1169 yoff = textbox_y_origin + ((lines - 1) * linedist) - tb_scale.h +
1170 vfont_metrics_descent(metrics);
1171 break;
1172 }
1173
1174 for (ct = ct_first; ct <= ct_last; ct++) {
1175 ct->offset.y += yoff;
1176 }
1177
1178 if (is_last_filled_textbox) {
1179 break;
1180 }
1181 }
1182 }
1183 else {
1184 /* Non text-box case handled separately. */
1185 float yoff = 0.0f;
1186
1187 switch (cu.align_y) {
1189 break;
1190 case CU_ALIGN_Y_TOP:
1191 yoff = -vfont_metrics_ascent(metrics);
1192 break;
1193 case CU_ALIGN_Y_CENTER:
1194 yoff = ((metrics->em_ratio + (lnr - 1) * linedist) * 0.5f) -
1195 vfont_metrics_ascent(metrics);
1196 break;
1198 yoff = (lnr - 1) * linedist;
1199 break;
1200 case CU_ALIGN_Y_BOTTOM:
1201 yoff = (lnr - 1) * linedist + vfont_metrics_descent(metrics);
1202 break;
1203 }
1204
1205 ct = chartransdata;
1206 for (i = 0; i <= slen; i++) {
1207 ct->offset.y += yoff;
1208 ct++;
1209 }
1210 }
1211 }
1212 if (tb_bounds_for_cursor != nullptr) {
1213 int char_beg_next = 0;
1214 for (curbox = 0; curbox < cu.totbox; curbox++) {
1215 TextBoxBounds_ForCursor *tb_bounds = &tb_bounds_for_cursor[curbox];
1216 if (tb_bounds->char_index_last == -1) {
1217 continue;
1218 }
1219 const int char_beg = char_beg_next;
1220 const int char_end = tb_bounds->char_index_last;
1221
1222 TempLineInfo *line_beg = &lineinfo[chartransdata[char_beg].linenr];
1223 TempLineInfo *line_end = &lineinfo[chartransdata[char_end].linenr];
1224
1225 int char_idx_offset = char_beg;
1226
1227 rctf *bounds = &tb_bounds->bounds;
1228 /* In a text-box with no curves, `offset.y` only decrements over lines, `ymax` and `ymin`
1229 * can be obtained from any character in the first and last line of the text-box. */
1230 bounds->ymax = chartransdata[char_beg].offset.y;
1231 bounds->ymin = chartransdata[char_end].offset.y;
1232
1233 for (TempLineInfo *line = line_beg; line <= line_end; line++) {
1234 const CharTrans *first_char_line = &chartransdata[char_idx_offset];
1235 const CharTrans *last_char_line = &chartransdata[char_idx_offset + line->char_nr];
1236
1237 bounds->xmin = min_ff(bounds->xmin, first_char_line->offset.x);
1238 bounds->xmax = max_ff(bounds->xmax, last_char_line->offset.x);
1239 char_idx_offset += line->char_nr + 1;
1240 }
1241 /* Move the bounds into a space compatible with `cursor_location`. */
1242 BLI_rctf_mul(bounds, font_size);
1243
1244 char_beg_next = tb_bounds->char_index_last + 1;
1245 }
1246 }
1247
1248 MEM_freeN(lineinfo);
1249
1250 /* TEXT ON CURVE */
1251 /* NOTE: Only #OB_CURVES_LEGACY objects could have a path. */
1252 if (cu.textoncurve && cu.textoncurve->type == OB_CURVES_LEGACY) {
1253 BLI_assert(cu.textoncurve->runtime->curve_cache != nullptr);
1254 if (cu.textoncurve->runtime->curve_cache != nullptr &&
1255 cu.textoncurve->runtime->curve_cache->anim_path_accum_length != nullptr)
1256 {
1257 float distfac, imat[4][4], imat3[3][3], cmat[3][3];
1258 float minx, maxx;
1259 float timeofs, sizefac;
1260
1261 if (ob != nullptr) {
1262 invert_m4_m4(imat, ob->object_to_world().ptr());
1263 }
1264 else {
1265 unit_m4(imat);
1266 }
1267 copy_m3_m4(imat3, imat);
1268
1269 copy_m3_m4(cmat, cu.textoncurve->object_to_world().ptr());
1270 mul_m3_m3m3(cmat, cmat, imat3);
1271 sizefac = normalize_v3(cmat[0]) / font_size;
1272
1273 ct = chartransdata;
1274 minx = maxx = ct->offset.x;
1275 ct++;
1276 for (i = 1; i <= slen; i++, ct++) {
1277 minx = std::min(minx, ct->offset.x);
1278 maxx = std::max(maxx, ct->offset.x);
1279 }
1280
1281 /* We put the x-coordinate exact at the curve, the y is rotated. */
1282
1283 /* Length correction. */
1284 const float chartrans_size_x = maxx - minx;
1285 if (chartrans_size_x != 0.0f) {
1286 const CurveCache *cc = cu.textoncurve->runtime->curve_cache;
1287 const float totdist = BKE_anim_path_get_length(cc);
1288 distfac = (sizefac * totdist) / chartrans_size_x;
1289 distfac = (distfac > 1.0f) ? (1.0f / distfac) : 1.0f;
1290 }
1291 else {
1292 /* Happens when there are no characters, set this value to place the text cursor. */
1293 distfac = 0.0f;
1294 }
1295
1296 timeofs = 0.0f;
1297
1298 if (distfac < 1.0f) {
1299 /* Path longer than text: space-mode is involved. */
1300
1301 if (cu.spacemode == CU_ALIGN_X_RIGHT) {
1302 timeofs = 1.0f - distfac;
1303 }
1304 else if (cu.spacemode == CU_ALIGN_X_MIDDLE) {
1305 timeofs = (1.0f - distfac) / 2.0f;
1306 }
1307 else if (cu.spacemode == CU_ALIGN_X_FLUSH) {
1308 distfac = 1.0f;
1309 }
1310 }
1311
1312 if (chartrans_size_x != 0.0f) {
1313 distfac /= chartrans_size_x;
1314 }
1315
1316 timeofs += distfac * cu.xof; /* Not cyclic. */
1317
1318 ct = chartransdata;
1319 for (i = 0; i <= slen; i++, ct++) {
1320 float ctime, dtime, vec[4], rotvec[3];
1321 float si, co;
1322
1323 /* Rotate around center character. */
1324 info = &custrinfo[i];
1325 BLI_assert(ct == &chartransdata[i]);
1326 const char32_t charcode = vfont_char_apply_smallcaps(mem[i], ct->is_smallcaps);
1327
1328 vfont_info_context_update(&vfinfo_ctx, cu, info);
1329 che = vfont_char_find_or_placeholder(vfinfo_ctx.vfd, charcode, che_placeholder);
1330
1331 twidth = vfont_char_width(cu, che, ct->is_smallcaps);
1332
1333 dtime = distfac * 0.5f * twidth;
1334
1335 ctime = timeofs + distfac * (ct->offset.x - minx);
1336 CLAMP(ctime, 0.0f, 1.0f);
1337
1338 /* Calculate the right loc AND the right rot separately. */
1339 BKE_where_on_path(cu.textoncurve, ctime, vec, nullptr, nullptr, nullptr, nullptr);
1341 cu.textoncurve, ctime + dtime, nullptr, rotvec, nullptr, nullptr, nullptr);
1342
1343 mul_v3_fl(vec, sizefac);
1344
1345 ct->rotate = float(M_PI) - atan2f(rotvec[1], rotvec[0]);
1346
1347 si = sinf(ct->rotate);
1348 co = cosf(ct->rotate);
1349
1350 offset.y = ct->offset.y;
1351
1352 ct->offset = {
1353 vec[0] + si * offset.y,
1354 vec[1] + co * offset.y,
1355 };
1356
1357 if (selboxes && (i >= selstart) && (i <= selend)) {
1358 EditFontSelBox *sb;
1359 sb = &selboxes[i - selstart];
1360 sb->rotate = -ct->rotate;
1361 }
1362 }
1363 }
1364 }
1365
1366 if (selboxes) {
1367 ct = chartransdata;
1368 for (i = 0; i <= selend; i++, ct++) {
1369 if (i >= selstart) {
1370 EditFontSelBox *sb = &selboxes[i - selstart];
1371 sb->x = ct->offset.x;
1372 sb->y = ct->offset.y;
1373 if (ct->rotate != 0.0f) {
1374 sb->x -= sinf(ct->rotate) * font_select_y_offset;
1375 sb->y -= cosf(ct->rotate) * font_select_y_offset;
1376 }
1377 else {
1378 /* Simple downward shift below baseline when not rotated. */
1379 sb->y -= font_select_y_offset;
1380 }
1381 sb->x *= font_size;
1382 sb->y *= font_size;
1383 selboxes[i - selstart].h = font_size;
1384 }
1385 }
1386 }
1387
1389 iter_data.status == VFONT_TO_CURVE_INIT)
1390 {
1391 ct = &chartransdata[ef->pos];
1392
1393 if (ELEM(mode, FO_CURSUP, FO_PAGEUP) && ct->linenr == 0) {
1394 /* Pass. */
1395 }
1396 else if (ELEM(mode, FO_CURSDOWN, FO_PAGEDOWN) && ct->linenr == lnr) {
1397 /* Pass. */
1398 }
1399 else if (mode == FO_LINE_BEGIN) {
1400 /* Line wrap aware line beginning. */
1401 while ((ef->pos > 0) && (chartransdata[ef->pos - 1].linenr == ct->linenr)) {
1402 ef->pos -= 1;
1403 }
1404 }
1405 else if (mode == FO_LINE_END) {
1406 /* Line wrap aware line end. */
1407 while ((ef->pos < slen) && (chartransdata[ef->pos + 1].linenr == ct->linenr)) {
1408 ef->pos += 1;
1409 }
1410 }
1411 else {
1412 switch (mode) {
1413 case FO_CURSUP:
1414 lnr = ct->linenr - 1;
1415 break;
1416 case FO_CURSDOWN:
1417 lnr = ct->linenr + 1;
1418 break;
1419 case FO_PAGEUP:
1420 lnr = ct->linenr - 10;
1421 break;
1422 case FO_PAGEDOWN:
1423 lnr = ct->linenr + 10;
1424 break;
1425 /* Ignored. */
1426 case FO_EDIT:
1427 case FO_CURS:
1428 case FO_DUPLI:
1429 case FO_SELCHANGE:
1430 case FO_LINE_BEGIN:
1431 case FO_LINE_END:
1432 break;
1433 }
1434 cnr = ct->charnr;
1435 /* Seek for char with `lnr` & `cnr`. */
1436 ef->pos = 0;
1437 ct = chartransdata;
1438 for (i = 0; i < slen; i++) {
1439 if (ct->linenr == lnr) {
1440 if ((ct->charnr == cnr) || ((ct + 1)->charnr == 0)) {
1441 break;
1442 }
1443 }
1444 else if (ct->linenr > lnr) {
1445 break;
1446 }
1447 ef->pos++;
1448 ct++;
1449 }
1450 }
1451 }
1452
1453 /* Cursor first. */
1454 if (ef) {
1455 ct = &chartransdata[ef->pos];
1456 const float cursor_width = 0.04f;
1457 const float cursor_half = 0.02f;
1458
1459 /* By default the cursor is exactly between the characters
1460 * and matches the rotation of the character to the right. */
1461 float cursor_left = 0.0f - cursor_half;
1462 float cursor_rotate = ct->rotate;
1463
1464 if (ef->selboxes) {
1465 if (ef->selend >= ef->selstart) {
1466 /* Cursor at right edge of a text selection. Match rotation to the character at the
1467 * end of selection. Cursor is further right to show the selected characters better. */
1468 cursor_rotate = chartransdata[max_ii(0, ef->selend - 1)].rotate;
1469 cursor_left = 0.0f;
1470 }
1471 else {
1472 /* Cursor at the left edge of a text selection. Cursor
1473 * is further left to show the selected characters better. */
1474 cursor_left = 0.0f - cursor_width;
1475 }
1476 }
1477 else if ((ef->pos == ef->len) && (ef->len > 0)) {
1478 /* Nothing selected, but at the end of the string. Match rotation to previous character. */
1479 cursor_rotate = chartransdata[ef->len - 1].rotate;
1480 }
1481
1482 /* We need the rotation to be around the bottom-left corner. So we make
1483 * that the zero point before rotation, rotate, then apply offsets afterward. */
1484
1485 /* Bottom left. */
1486 ef->textcurs[0] = blender::float2(cursor_left, 0.0f - font_select_y_offset);
1487 /* Bottom right. */
1488 ef->textcurs[1] = blender::float2(cursor_left + cursor_width, 0.0f - font_select_y_offset);
1489 /* Top left. */
1490 ef->textcurs[3] = blender::float2(cursor_left, 1.0f - font_select_y_offset);
1491 /* Top right. */
1492 ef->textcurs[2] = blender::float2(cursor_left + cursor_width, 1.0f - font_select_y_offset);
1493
1494 for (int vert = 0; vert < 4; vert++) {
1495 blender::float2 temp_fl;
1496 /* Rotate around the cursor's bottom-left corner. */
1497 rotate_v2_v2fl(temp_fl, &ef->textcurs[vert][0], -cursor_rotate);
1498 ef->textcurs[vert] = font_size * (ct->offset + temp_fl);
1499 }
1500 }
1501
1502 if (mode == FO_SELCHANGE) {
1503 MEM_freeN(chartransdata);
1504 chartransdata = nullptr;
1505 }
1506 else if (mode == FO_EDIT) {
1507 /* Make NURBS-data. */
1508 BKE_nurbList_free(r_nubase);
1509
1510 /* Track the previous underline so contiguous underlines can be welded together.
1511 * This is done to prevent overlapping geometry, see: #122540. */
1512 int ul_prev_i = -1;
1513 Nurb *ul_prev_nu = nullptr;
1514
1515 ct = chartransdata;
1516 for (i = 0; i < slen; i++) {
1517
1518 if ((cu.overflow == CU_OVERFLOW_TRUNCATE) && (ob && ob->mode != OB_MODE_EDIT) &&
1519 ct->is_overflow)
1520 {
1521 break;
1522 }
1523
1524 info = &(custrinfo[i]);
1525 const char32_t charcode = vfont_char_apply_smallcaps(mem[i], ct->is_smallcaps);
1526 /* We don't want to see any character for `\n`. */
1527 if (charcode != '\n') {
1528
1529 vfont_info_context_update(&vfinfo_ctx, cu, info);
1530 /* Find the character, the characters has to be in the memory already
1531 * since character checking has been done earlier already. */
1532 che = vfont_char_find_or_placeholder(vfinfo_ctx.vfd, charcode, che_placeholder);
1534 cu, r_nubase, che, info, ct->is_smallcaps, ct->offset, ct->rotate, i, font_size);
1535
1536 if (info->flag & CU_CHINFO_UNDERLINE) {
1537 float ulwidth, uloverlap = 0.0f;
1538 rctf rect;
1539
1540 BLI_assert(&ct[1] == &chartransdata[i + 1]);
1541 if ((i < (slen - 1)) && (mem[i + 1] != '\n') &&
1542 ((mem[i + 1] != ' ') || (custrinfo[i + 1].flag & CU_CHINFO_UNDERLINE)) &&
1543 ((ct[1].is_wrap) == 0))
1544 {
1545 uloverlap = xtrax;
1546 }
1547
1548 twidth = vfont_char_width(cu, che, ct->is_smallcaps);
1549 ulwidth = (twidth * (1.0f + (info->kern / 40.0f))) + uloverlap;
1550
1551 rect.xmin = ct->offset.x;
1552 rect.xmax = rect.xmin + ulwidth;
1553
1554 rect.ymin = ct->offset.y;
1555 rect.ymax = rect.ymin - cu.ulheight;
1556
1557 if ((ul_prev_i != -1) &&
1558 /* Skip welding underlines when there are gaps. */
1559 ((ul_prev_i + 1 != i) ||
1560 /* Skip welding on new lines. */
1561 (chartransdata[ul_prev_i].linenr != ct->linenr)))
1562 {
1563 ul_prev_nu = nullptr;
1564 }
1565
1566 ul_prev_nu = build_underline(cu,
1567 r_nubase,
1568 &rect,
1569 cu.ulpos - 0.05f,
1570 ct->rotate,
1571 i,
1572 info->mat_nr,
1573 font_size,
1574 ul_prev_nu);
1575 ul_prev_i = ul_prev_nu ? i : -1;
1576 }
1577 }
1578 ct++;
1579 }
1580 }
1581
1582 if (iter_data.status == VFONT_TO_CURVE_SCALE_ONCE) {
1583 /* That means we were in a final run, just exit. */
1585 iter_data.status = VFONT_TO_CURVE_DONE;
1586 }
1587 else if (cu.overflow == CU_OVERFLOW_NONE) {
1588 /* Pass. */
1589 }
1590 else if ((tb_scale.h == 0.0f) && (tb_scale.w == 0.0f)) {
1591 /* Pass. */
1592 }
1593 else if (cu.overflow == CU_OVERFLOW_SCALE) {
1594 if ((cu.totbox == 1) && ((tb_scale.w == 0.0f) || (tb_scale.h == 0.0f))) {
1595 /* These are special cases, simpler to deal with. */
1596 if (tb_scale.w == 0.0f) {
1597 /* This is a potential vertical overflow.
1598 * Since there is no width limit, all the new lines are from line breaks. */
1599 if ((last_line != -1) && (lnr > last_line)) {
1600 const float total_text_height = lnr * linedist;
1601 iter_data.scale_to_fit = tb_scale.h / total_text_height;
1603 iter_data.word_wrap = false;
1604 }
1605 }
1606 else if (tb_scale.h == 0.0f) {
1607 /* This is a horizontal overflow. */
1608 if (longest_line_length > tb_scale.w) {
1609 /* We make sure longest line before it broke can fit here. */
1610 float scale_to_fit = tb_scale.w / longest_line_length;
1611
1612 iter_data.scale_to_fit = scale_to_fit;
1614 iter_data.word_wrap = false;
1615 }
1616 }
1617 }
1618 else {
1619 /* This is the really complicated case, the best we can do is to iterate over
1620 * this function a few times until we get an acceptable result.
1621 *
1622 * Keep in mind that there is no single number that will make all fit to the end.
1623 * In a way, our ultimate goal is to get the highest scale that still leads to the
1624 * number of extra lines to zero. */
1625 if (iter_data.status == VFONT_TO_CURVE_INIT) {
1626 bool valid = true;
1627
1628 for (int tb_index = 0; tb_index <= curbox; tb_index++) {
1629 TextBox *tb = &cu.tb[tb_index];
1630 if ((tb->w == 0.0f) || (tb->h == 0.0f)) {
1631 valid = false;
1632 break;
1633 }
1634 }
1635
1636 if (valid && (last_line != -1) && (lnr > last_line)) {
1637 const float total_text_height = lnr * linedist;
1638 float scale_to_fit = tb_scale.h / total_text_height;
1639
1640 iter_data.bisect.max = 1.0f;
1641 iter_data.bisect.min = scale_to_fit;
1642
1643 iter_data.status = VFONT_TO_CURVE_BISECT;
1644 }
1645 }
1646 else {
1648 /* Try to get the highest scale that gives us the exactly
1649 * number of lines we need. */
1650 bool valid = false;
1651
1652 if ((last_line != -1) && (lnr > last_line)) {
1653 /* It is overflowing, scale it down. */
1654 iter_data.bisect.max = iter_data.scale_to_fit;
1655 }
1656 else {
1657 /* It fits inside the text-box, scale it up. */
1658 iter_data.bisect.min = iter_data.scale_to_fit;
1659 valid = true;
1660 }
1661
1662 /* Bisecting to try to find the best fit. */
1663 iter_data.scale_to_fit = (iter_data.bisect.max + iter_data.bisect.min) * 0.5f;
1664
1665 /* We iterated enough or got a good enough result. */
1666 if ((!iter_data.iteration--) || ((iter_data.bisect.max - iter_data.bisect.min) <
1668 {
1669 if (valid) {
1670 iter_data.status = VFONT_TO_CURVE_DONE;
1671 }
1672 else {
1673 iter_data.scale_to_fit = iter_data.bisect.min;
1675 }
1676 }
1677 }
1678 }
1679 }
1680
1681 if (cursor_params) {
1682 const blender::float2 &cursor_location = cursor_params->cursor_location;
1683 /* Erasing all text could give `slen = 0`. */
1684 if (slen == 0) {
1685 cursor_params->r_string_offset = -1;
1686 }
1687 else if (cu.textoncurve != nullptr) {
1688
1689 int closest_char = -1;
1690 float closest_dist_sq = FLT_MAX;
1691
1692 for (i = 0; i <= slen; i++) {
1693 const blender::float2 char_location = chartransdata[i].offset * font_size;
1694 const float test_dist_sq = blender::math::distance_squared(cursor_location, char_location);
1695 if (closest_dist_sq > test_dist_sq) {
1696 closest_char = i;
1697 closest_dist_sq = test_dist_sq;
1698 }
1699 }
1700
1701 cursor_params->r_string_offset = closest_char;
1702 }
1703 else {
1704 /* Find the first box closest to `cursor_location`. */
1705 int char_beg = 0;
1706 int char_end = slen;
1707
1708 if (tb_bounds_for_cursor != nullptr) {
1709 /* Search for the closest box. */
1710 int closest_box = -1;
1711 float closest_dist_sq = FLT_MAX;
1712 for (curbox = 0; curbox < cu.totbox; curbox++) {
1713 const TextBoxBounds_ForCursor *tb_bounds = &tb_bounds_for_cursor[curbox];
1714 if (tb_bounds->char_index_last == -1) {
1715 continue;
1716 }
1717 /* The closest point in the box to the `cursor_location`
1718 * by clamping it to the bounding box. */
1719 const blender::float2 cursor_location_clamped = {
1720 clamp_f(cursor_location.x, tb_bounds->bounds.xmin, tb_bounds->bounds.xmax),
1721 clamp_f(cursor_location.y, tb_bounds->bounds.ymin, tb_bounds->bounds.ymax),
1722 };
1723
1724 const float test_dist_sq = blender::math::distance_squared(cursor_location,
1725 cursor_location_clamped);
1726 if (test_dist_sq < closest_dist_sq) {
1727 closest_dist_sq = test_dist_sq;
1728 closest_box = curbox;
1729 }
1730 }
1731 if (closest_box != -1) {
1732 if (closest_box != 0) {
1733 char_beg = tb_bounds_for_cursor[closest_box - 1].char_index_last + 1;
1734 }
1735 char_end = tb_bounds_for_cursor[closest_box].char_index_last;
1736 }
1737 MEM_freeN(tb_bounds_for_cursor);
1738 tb_bounds_for_cursor = nullptr; /* Safety only. */
1739 }
1740 const float interline_offset = ((linedist - 0.5f) / 2.0f) * font_size;
1741 /* Loop until find the line where `cursor_location` is over. */
1742 for (i = char_beg; i <= char_end; i++) {
1743 if (cursor_location.y >= ((chartransdata[i].offset.y * font_size) - interline_offset)) {
1744 break;
1745 }
1746 }
1747
1748 i = min_ii(i, char_end);
1749 const float char_yof = chartransdata[i].offset.y;
1750
1751 /* Loop back until find the first character of the line, this because `cursor_location` can
1752 * be positioned further below the text, so #i can be the last character of the last line. */
1753 for (; i >= char_beg + 1 && chartransdata[i - 1].offset.y == char_yof; i--) {
1754 /* Pass. */
1755 }
1756 /* Loop until find the first character to the right of `cursor_location`
1757 * (using the character midpoint on the x-axis as a reference). */
1758 for (; i <= char_end && char_yof == chartransdata[i].offset.y; i++) {
1759 info = &custrinfo[i];
1760 const char32_t charcode = vfont_char_apply_smallcaps(mem[i], info);
1761
1762 vfont_info_context_update(&vfinfo_ctx, cu, info);
1763 che = vfont_char_find_or_placeholder(vfinfo_ctx.vfd, charcode, che_placeholder);
1764
1765 const float charwidth = vfont_char_width(cu, che, info);
1766 const float charhalf = (charwidth / 2.0f);
1767 if (cursor_location.x <= ((chartransdata[i].offset.x + charhalf) * font_size)) {
1768 break;
1769 }
1770 }
1771 i = min_ii(i, char_end);
1772
1773 /* If there is no character to the right of the cursor we are on the next line, go back to
1774 * the last character of the previous line. */
1775 if (i > char_beg && chartransdata[i].offset.y != char_yof) {
1776 i -= 1;
1777 }
1778 cursor_params->r_string_offset = i;
1779 }
1780 /* Must be cleared & freed. */
1781 BLI_assert(tb_bounds_for_cursor == nullptr);
1782 }
1783
1784 /* Scale to fit only works for single text box layouts. */
1786 /* Always cleanup before going to the scale-to-fit repetition. */
1787 if (r_nubase != nullptr) {
1788 BKE_nurbList_free(r_nubase);
1789 }
1790
1791 if (chartransdata != nullptr) {
1792 MEM_freeN(chartransdata);
1793 }
1794
1795 if (mem_alloc) {
1796 MEM_freeN(mem);
1797 }
1798 return true;
1799 }
1800
1801 if (r_text) {
1802 *r_text = mem;
1803 *r_text_len = slen;
1804 *r_text_free = mem_alloc;
1805 }
1806 else {
1807 if (mem_alloc) {
1808 MEM_freeN(mem);
1809 }
1810 }
1811
1812 if (chartransdata) {
1813 if (r_chartransdata) {
1814 *r_chartransdata = chartransdata;
1815 }
1816 else {
1817 MEM_freeN(chartransdata);
1818 }
1819 }
1820
1821 /* Store the effective scale, to use for the text-box lines. */
1822 if (ef != nullptr) {
1823 ef->font_size_eval = font_size;
1824 }
1825 if (r_font_size_eval) {
1826 *r_font_size_eval = font_size;
1827 }
1828 return true;
1829
1830#undef MARGIN_X_MIN
1831#undef MARGIN_Y_MIN
1832}
1833
1835
1836/* -------------------------------------------------------------------- */
1841
1843 const Curve &cu,
1844 const eEditFontMode mode,
1845 ListBase *r_nubase,
1846 const char32_t **r_text,
1847 int *r_text_len,
1848 bool *r_text_free,
1849 CharTrans **r_chartransdata,
1850 float *r_font_size_eval)
1851{
1854 data.scale_to_fit = 1.0f;
1855 data.word_wrap = true;
1856 data.ok = true;
1857 data.status = VFONT_TO_CURVE_INIT;
1858
1859 do {
1860 data.ok &= vfont_to_curve(ob,
1861 cu,
1862 mode,
1863 data,
1864 nullptr,
1865 r_nubase,
1866 r_text,
1867 r_text_len,
1868 r_text_free,
1869 r_chartransdata,
1870 r_font_size_eval);
1872
1873 return data.ok;
1874}
1875
1877{
1878 Curve &cu = *(Curve *)ob->data;
1879 ListBase *r_nubase = &cu.nurb;
1880
1881 /* TODO: iterating to calculate the scale can be avoided. */
1884 data.scale_to_fit = 1.0f;
1885 data.word_wrap = true;
1886 data.ok = true;
1887 data.status = VFONT_TO_CURVE_INIT;
1888
1889 VFontCursor_Params cursor_params = {};
1890 cursor_params.cursor_location = cursor_location;
1891 cursor_params.r_string_offset = -1;
1892
1893 do {
1894 data.ok &= vfont_to_curve(ob,
1895 cu,
1896 FO_CURS,
1897 data,
1898 &cursor_params,
1899 r_nubase,
1900 nullptr,
1901 nullptr,
1902 nullptr,
1903 nullptr,
1904 nullptr);
1906
1907 return cursor_params.r_string_offset;
1908}
1909
1910#undef FONT_TO_CURVE_SCALE_ITERATIONS
1911#undef FONT_TO_CURVE_SCALE_THRESHOLD
1912
1914{
1915 BLI_assert(ob->type == OB_FONT);
1916 const Curve &cu = *static_cast<const Curve *>(ob->data);
1917 return BKE_vfont_to_curve_ex(
1918 ob, cu, mode, r_nubase, nullptr, nullptr, nullptr, nullptr, nullptr);
1919}
1920
1922{
1923 Curve &cu = *static_cast<Curve *>(ob->data);
1924 return BKE_vfont_to_curve_ex(
1925 ob, cu, mode, &cu.nurb, nullptr, nullptr, nullptr, nullptr, nullptr);
1926}
1927
bool BKE_where_on_path(const struct Object *ob, float ctime, float r_vec[4], float r_dir[3], float r_quat[4], float *r_radius, float *r_weight)
float BKE_anim_path_get_length(const struct CurveCache *curve_cache)
void BKE_nurbList_free(ListBase *lb)
Definition curve.cc:597
void BKE_vfont_data_ensure(VFont *vfont)
Definition vfont.cc:202
int BKE_vfont_select_get(const Curve *cu, int *r_start, int *r_end)
Definition vfont.cc:408
eEditFontMode
Definition BKE_vfont.hh:77
@ FO_PAGEUP
Definition BKE_vfont.hh:83
@ FO_EDIT
Definition BKE_vfont.hh:78
@ FO_CURSUP
Definition BKE_vfont.hh:80
@ FO_LINE_END
Definition BKE_vfont.hh:86
@ FO_LINE_BEGIN
Definition BKE_vfont.hh:85
@ FO_SELCHANGE
Definition BKE_vfont.hh:87
@ FO_CURS
Definition BKE_vfont.hh:79
@ FO_CURSDOWN
Definition BKE_vfont.hh:81
@ FO_PAGEDOWN
Definition BKE_vfont.hh:84
@ FO_DUPLI
Definition BKE_vfont.hh:82
#define FO_CURS_IS_MOTION(mode)
Definition BKE_vfont.hh:91
A structure to represent vector fonts, and to load them from PostScript fonts.
VChar * BKE_vfontdata_char_from_freetypefont(VFont *vfont, unsigned int character)
void BKE_vfontdata_metrics_get_defaults(VFontData_Metrics *metrics)
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
void ** BLI_ghash_lookup_p(GHash *gh, const void *key) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.cc:745
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
MINLINE float max_ff(float a, float b)
MINLINE int min_ii(int a, int b)
MINLINE float clamp_f(float value, float min, float max)
MINLINE float min_ff(float a, float b)
MINLINE int max_ii(int a, int b)
MINLINE int compare_ff_relative(float a, float b, float max_diff, int max_ulps)
MINLINE float safe_divide(float a, float b)
#define M_PI
void copy_m3_m4(float m1[3][3], const float m2[4][4])
bool invert_m4_m4(float inverse[4][4], const float mat[4][4])
void mul_m3_m3m3(float R[3][3], const float A[3][3], const float B[3][3])
void unit_m4(float m[4][4])
MINLINE void copy_v4_fl4(float v[4], float x, float y, float z, float w)
MINLINE void mul_v2_fl(float r[2], float f)
MINLINE void mul_v3_fl(float r[3], float f)
void rotate_v2_v2fl(float r[2], const float p[2], float angle)
MINLINE float normalize_v3(float n[3])
void BLI_rctf_mul(struct rctf *rect, float factor)
Definition rct.cc:588
size_t BLI_str_utf8_as_utf32(char32_t *__restrict dst_w, const char *__restrict src_c, size_t dst_w_maxncpy) ATTR_NONNULL(1
unsigned int uint
pthread_rwlock_t ThreadRWMutex
#define THREAD_LOCK_READ
#define THREAD_LOCK_WRITE
#define BLI_RWLOCK_INITIALIZER
void BLI_rw_mutex_lock(ThreadRWMutex *mutex, int mode)
Definition threads.cc:467
void BLI_rw_mutex_unlock(ThreadRWMutex *mutex)
Definition threads.cc:477
#define CLAMP(a, b, c)
#define ARRAY_SIZE(arr)
#define ARRAY_SET_ITEMS(...)
#define UNLIKELY(x)
#define ELEM(...)
#define POINTER_FROM_UINT(i)
@ CU_ALIGN_Y_TOP
@ CU_ALIGN_Y_BOTTOM_BASELINE
@ CU_ALIGN_Y_CENTER
@ CU_ALIGN_Y_BOTTOM
@ CU_ALIGN_Y_TOP_BASELINE
@ CU_NURB_CYCLIC
@ CU_ALIGN_X_FLUSH
@ CU_ALIGN_X_MIDDLE
@ CU_ALIGN_X_LEFT
@ CU_ALIGN_X_JUSTIFY
@ CU_ALIGN_X_RIGHT
@ CU_OVERFLOW_SCALE
@ CU_OVERFLOW_TRUNCATE
@ CU_OVERFLOW_NONE
#define CU_CHINFO_STYLE_ALL
@ CU_SMOOTH
@ CU_BEZIER
@ HD_VECT
@ CU_CHINFO_UNDERLINE
@ CU_CHINFO_BOLD
@ CU_CHINFO_ITALIC
@ CU_CHINFO_SMALLCAPS
@ OB_MODE_EDIT
Object is a sort of wrapper for general info.
@ OB_FONT
@ OB_CURVES_LEGACY
Read Guarded memory(de)allocation.
BMesh const char void * data
static btDbvtVolume bounds(btDbvtNode **leaves, int count)
Definition btDbvt.cpp:299
nullptr float
void * MEM_mallocN(size_t len, const char *str)
Definition mallocn.cc:128
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
T distance_squared(const VecBase< T, Size > &a, const VecBase< T, Size > &b)
VecBase< float, 2 > float2
static void rotate(float new_co[3], float a, const float ax[3], const float co[3])
#define sinf
#define cosf
#define ceilf
#define atan2f
#define FLT_MAX
Definition stdcycles.h:14
float vec[4]
float vec[3][3]
blender::float2 offset
Definition BKE_vfont.hh:21
short linenr
Definition BKE_vfont.hh:23
uint do_break
Definition BKE_vfont.hh:25
float rotate
Definition BKE_vfont.hh:22
char overflow
float spacing
struct VFont * vfont
struct TextBox * tb
float ulheight
float ulpos
struct EditFont * editfont
short resolu
char spacemode
float wordspace
struct CharInfo * strinfo
struct VFont * vfontb
struct Object * textoncurve
float shear
char * str
struct VFont * vfonti
float smallcaps_scale
ListBase nurb
float linedist
float fsize
struct VFont * vfontbi
float font_size_eval
Definition BKE_vfont.hh:46
CharInfo * textbufinfo
Definition BKE_vfont.hh:43
int selend
Definition BKE_vfont.hh:61
EditFontSelBox * selboxes
Definition BKE_vfont.hh:50
int select_char_info_flag
Definition BKE_vfont.hh:68
int selboxes_len
Definition BKE_vfont.hh:51
blender::float2 textcurs[4]
Definition BKE_vfont.hh:49
char32_t * textbuf
Definition BKE_vfont.hh:41
int selstart
Definition BKE_vfont.hh:61
void * first
short flagu
short orderu
struct Nurb * next
short orderv
float * knotsu
short flag
short type
float * knotsv
BezTriple * bezt
BPoint * bp
short resolu
short mat_nr
ObjectRuntimeHandle * runtime
BezTriple bezt[2][4]
const VFontData_Metrics * metrics
struct VCharPlaceHolder::@174370020143114137261344213155320245107220326216 data
float width
ListBase nurbsbase
blender::float2 cursor_location
GHash * characters
VFontData_Metrics metrics
VFontData * vfd
struct VFontToCurveIter::@372112023252273212306276021266122152166373073216 bisect
struct VFontData * data
float xmax
float xmin
float ymax
float ymin
i
Definition text_draw.cc:230
static bool vfont_to_curve(Object *ob, const Curve &cu, const eEditFontMode mode, VFontToCurveIter &iter_data, VFontCursor_Params *cursor_params, ListBase *r_nubase, const char32_t **r_text, int *r_text_len, bool *r_text_free, CharTrans **r_chartransdata, float *r_font_size_eval)
static VChar * vfont_char_ensure_with_lock(VFont *vfont, char32_t charcode)
static VFontData * vfont_data_ensure_with_lock(VFont *vfont)
void BKE_vfont_char_build(const Curve &cu, ListBase *nubase, uint charcode, const CharInfo *info, const bool is_smallcaps, const blender::float2 &offset, float rotate, int charidx, const float fsize)
static VChar * vfont_placeholder_ensure(VCharPlaceHolder &che_placeholder, char32_t charcode)
static bool vfont_char_find(const VFontData *vfd, char32_t charcode, VChar **r_che)
static void vfont_info_context_update(VFontInfoContext *vfinfo_ctx, const Curve &cu, const CharInfo *info)
bool BKE_vfont_to_curve(Object *ob, const eEditFontMode mode)
static float vfont_char_width(const Curve &cu, VChar *che, const bool is_smallcaps)
static VFont * vfont_from_charinfo(const Curve &cu, const CharInfo *info)
static void vfont_char_build_impl(const Curve &cu, ListBase *nubase, const VChar *che, const CharInfo *info, const bool is_smallcaps, const blender::float2 &offset, const float rotate, const int charidx, const float fsize)
static float vfont_metrics_descent(const VFontData_Metrics *metrics)
static void mid_v2v2(float a[2], float b[2])
static Nurb * build_underline(const Curve &cu, ListBase *nubase, const rctf *rect, const float yofs, const float rotate, const int charidx, const short mat_nr, const float font_size, Nurb *ul_prev_nu)
static VChar * vfont_char_find_or_placeholder(const VFontData *vfd, char32_t charcode, VCharPlaceHolder &che_placeholder)
bool BKE_vfont_to_curve_ex(Object *ob, const Curve &cu, const eEditFontMode mode, ListBase *r_nubase, const char32_t **r_text, int *r_text_len, bool *r_text_free, CharTrans **r_chartransdata, float *r_font_size_eval)
bool BKE_vfont_to_curve_nubase(Object *ob, const eEditFontMode mode, ListBase *r_nubase)
static ThreadRWMutex vfont_rwlock
static char32_t vfont_char_apply_smallcaps(char32_t charcode, const bool is_smallcaps)
#define FONT_TO_CURVE_SCALE_THRESHOLD
@ VFONT_TO_CURVE_BISECT
@ VFONT_TO_CURVE_SCALE_ONCE
@ VFONT_TO_CURVE_INIT
@ VFONT_TO_CURVE_DONE
static float vfont_metrics_ascent(const VFontData_Metrics *metrics)
#define MARGIN_Y_MIN
#define FONT_TO_CURVE_SCALE_ITERATIONS
#define MARGIN_X_MIN
static void vfont_info_context_init(VFontInfoContext *vfinfo_ctx, const Curve &cu)
static void textbox_scale(TextBox *tb_dst, const TextBox *tb_src, float scale)
int BKE_vfont_cursor_to_text_index(Object *ob, const blender::float2 &cursor_location)
uint8_t flag
Definition wm_window.cc:145