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