A game about forced loneliness, made by TACStudios
at master 5607 lines 283 kB view raw
1using System; 2using System.Collections; 3using UnityEngine; 4using UnityEngine.Rendering; 5using UnityEngine.UI; 6using System.Collections.Generic; 7using Unity.Profiling; 8using UnityEngine.TextCore; 9using UnityEngine.TextCore.LowLevel; 10using Object = UnityEngine.Object; 11 12 13#pragma warning disable 0414 // Disabled a few warnings related to serialized variables not used in this script but used in the editor. 14#pragma warning disable 0618 // Disabled warning due to SetVertices being deprecated until new release with SetMesh() is available. 15 16 17namespace TMPro 18{ 19 [DisallowMultipleComponent] 20 [RequireComponent(typeof(RectTransform))] 21 [RequireComponent(typeof(CanvasRenderer))] 22 [AddComponentMenu("UI/TextMeshPro - Text (UI)", 11)] 23 [ExecuteAlways] 24 #if UNITY_2023_2_OR_NEWER 25 [HelpURL("https://docs.unity3d.com/Packages/com.unity.ugui@2.0/manual/TextMeshPro/index.html")] 26 #else 27 [HelpURL("https://docs.unity3d.com/Packages/com.unity.textmeshpro@3.2")] 28 #endif 29 public class TextMeshProUGUI : TMP_Text, ILayoutElement 30 { 31 /// <summary> 32 /// Get the material that will be used for rendering. 33 /// </summary> 34 public override Material materialForRendering 35 { 36 get { return TMP_MaterialManager.GetMaterialForRendering(this, m_sharedMaterial); } 37 } 38 39 40 /// <summary> 41 /// Determines if the size of the text container will be adjusted to fit the text object when it is first created. 42 /// </summary> 43 public override bool autoSizeTextContainer 44 { 45 get { return m_autoSizeTextContainer; } 46 47 set { if (m_autoSizeTextContainer == value) return; m_autoSizeTextContainer = value; if (m_autoSizeTextContainer) { CanvasUpdateRegistry.RegisterCanvasElementForLayoutRebuild(this); SetLayoutDirty(); } } 48 } 49 50 51 /// <summary> 52 /// Reference to the Mesh used by the text object. 53 /// </summary> 54 public override Mesh mesh 55 { 56 get { return m_mesh; } 57 } 58 59 60 /// <summary> 61 /// Reference to the CanvasRenderer used by the text object. 62 /// </summary> 63 public new CanvasRenderer canvasRenderer 64 { 65 get 66 { 67 if (m_canvasRenderer == null) m_canvasRenderer = GetComponent<CanvasRenderer>(); 68 69 return m_canvasRenderer; 70 } 71 } 72 73 74 /// <summary> 75 /// Anchor dampening prevents the anchor position from being adjusted unless the positional change exceeds about 40% of the width of the underline character. This essentially stabilizes the anchor position. 76 /// </summary> 77 //public bool anchorDampening 78 //{ 79 // get { return m_anchorDampening; } 80 // set { if (m_anchorDampening != value) { havePropertiesChanged = true; m_anchorDampening = value; /* ScheduleUpdate(); */ } } 81 //} 82 83 #if !UNITY_2019_3_OR_NEWER 84 [SerializeField] 85 private bool m_Maskable = true; 86 #endif 87 88 private bool m_isRebuildingLayout = false; 89 private Coroutine m_DelayedGraphicRebuild; 90 private Coroutine m_DelayedMaterialRebuild; 91 92 /// <summary> 93 /// Function called by Unity when the horizontal layout needs to be recalculated. 94 /// </summary> 95 public void CalculateLayoutInputHorizontal() 96 { 97 //Debug.Log("*** CalculateLayoutHorizontal() on Object ID: " + GetInstanceID() + " at frame: " + Time.frameCount + "***"); 98 } 99 100 101 /// <summary> 102 /// Function called by Unity when the vertical layout needs to be recalculated. 103 /// </summary> 104 public void CalculateLayoutInputVertical() 105 { 106 //Debug.Log("*** CalculateLayoutInputVertical() on Object ID: " + GetInstanceID() + " at frame: " + Time.frameCount + "***"); 107 } 108 109 110 public override void SetVerticesDirty() 111 { 112 if (this == null || !this.IsActive()) 113 return; 114 115 if (CanvasUpdateRegistry.IsRebuildingGraphics()) 116 return; 117 118 CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild(this); 119 120 if (m_OnDirtyVertsCallback != null) 121 m_OnDirtyVertsCallback(); 122 } 123 124 125 /// <summary> 126 /// 127 /// </summary> 128 public override void SetLayoutDirty() 129 { 130 m_isPreferredWidthDirty = true; 131 m_isPreferredHeightDirty = true; 132 133 if (this == null || !this.IsActive()) 134 return; 135 136 LayoutRebuilder.MarkLayoutForRebuild(this.rectTransform); 137 138 m_isLayoutDirty = true; 139 140 if (m_OnDirtyLayoutCallback != null) 141 m_OnDirtyLayoutCallback(); 142 } 143 144 145 /// <summary> 146 /// 147 /// </summary> 148 public override void SetMaterialDirty() 149 { 150 if (this == null || !this.IsActive()) 151 return; 152 153 if (CanvasUpdateRegistry.IsRebuildingGraphics()) 154 return; 155 156 m_isMaterialDirty = true; 157 CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild(this); 158 159 if (m_OnDirtyMaterialCallback != null) 160 m_OnDirtyMaterialCallback(); 161 } 162 163 164 /// <summary> 165 /// 166 /// </summary> 167 public override void SetAllDirty() 168 { 169 SetLayoutDirty(); 170 SetVerticesDirty(); 171 SetMaterialDirty(); 172 } 173 174 175 /// <summary> 176 /// Delay registration of text object for graphic rebuild by one frame. 177 /// </summary> 178 /// <returns></returns> 179 IEnumerator DelayedGraphicRebuild() 180 { 181 yield return null; 182 183 CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild(this); 184 185 if (m_OnDirtyVertsCallback != null) 186 m_OnDirtyVertsCallback(); 187 188 m_DelayedGraphicRebuild = null; 189 } 190 191 192 /// <summary> 193 /// Delay registration of text object for graphic rebuild by one frame. 194 /// </summary> 195 /// <returns></returns> 196 IEnumerator DelayedMaterialRebuild() 197 { 198 yield return null; 199 200 m_isMaterialDirty = true; 201 CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild(this); 202 203 if (m_OnDirtyMaterialCallback != null) 204 m_OnDirtyMaterialCallback(); 205 206 m_DelayedMaterialRebuild = null; 207 } 208 209 210 /// <summary> 211 /// 212 /// </summary> 213 /// <param name="update"></param> 214 public override void Rebuild(CanvasUpdate update) 215 { 216 if (this == null) return; 217 218 if (update == CanvasUpdate.Prelayout) 219 { 220 if (m_autoSizeTextContainer) 221 { 222 m_rectTransform.sizeDelta = GetPreferredValues(Mathf.Infinity, Mathf.Infinity); 223 } 224 } 225 else if (update == CanvasUpdate.PreRender) 226 { 227 OnPreRenderCanvas(); 228 229 if (!m_isMaterialDirty) return; 230 231 UpdateMaterial(); 232 m_isMaterialDirty = false; 233 } 234 } 235 236 237 /// <summary> 238 /// Method to keep the pivot of the sub text objects in sync with the parent pivot. 239 /// </summary> 240 private void UpdateSubObjectPivot() 241 { 242 if (m_textInfo == null) return; 243 244 for (int i = 1; i < m_subTextObjects.Length && m_subTextObjects[i] != null; i++) 245 { 246 m_subTextObjects[i].SetPivotDirty(); 247 } 248 //m_isPivotDirty = false; 249 } 250 251 252 /// <summary> 253 /// 254 /// </summary> 255 /// <param name="baseMaterial"></param> 256 /// <returns></returns> 257 public override Material GetModifiedMaterial(Material baseMaterial) 258 { 259 Material mat = baseMaterial; 260 261 if (m_ShouldRecalculateStencil) 262 { 263 var rootCanvas = MaskUtilities.FindRootSortOverrideCanvas(transform); 264 m_StencilValue = maskable ? MaskUtilities.GetStencilDepth(transform, rootCanvas) : 0; 265 m_ShouldRecalculateStencil = false; 266 } 267 268 if (m_StencilValue > 0) 269 { 270 var maskMat = StencilMaterial.Add(mat, (1 << m_StencilValue) - 1, StencilOp.Keep, CompareFunction.Equal, ColorWriteMask.All, (1 << m_StencilValue) - 1, 0); 271 StencilMaterial.Remove(m_MaskMaterial); 272 m_MaskMaterial = maskMat; 273 mat = m_MaskMaterial; 274 } 275 276 return mat; 277 } 278 279 280 /// <summary> 281 /// 282 /// </summary> 283 protected override void UpdateMaterial() 284 { 285 //Debug.Log("*** UpdateMaterial() ***"); 286 287 //if (!this.IsActive()) 288 // return; 289 290 if (m_sharedMaterial == null || canvasRenderer == null) return; 291 292 m_canvasRenderer.materialCount = 1; 293 m_canvasRenderer.SetMaterial(materialForRendering, 0); 294 //m_canvasRenderer.SetTexture(materialForRendering.mainTexture); 295 } 296 297 298 //public override void OnRebuildRequested() 299 //{ 300 // //Debug.Log("OnRebuildRequested"); 301 302 // base.OnRebuildRequested(); 303 //} 304 305 306 307 //public override bool Raycast(Vector2 sp, Camera eventCamera) 308 //{ 309 // //Debug.Log("Raycast Event. ScreenPoint: " + sp); 310 // return base.Raycast(sp, eventCamera); 311 //} 312 313 314 // MASKING RELATED PROPERTIES 315 /// <summary> 316 /// Sets the masking offset from the bounds of the object 317 /// </summary> 318 public Vector4 maskOffset 319 { 320 get { return m_maskOffset; } 321 set { m_maskOffset = value; UpdateMask(); m_havePropertiesChanged = true; } 322 } 323 324 325 //public override Material defaultMaterial 326 //{ 327 // get { Debug.Log("Default Material called."); return m_sharedMaterial; } 328 //} 329 330 331 332 //protected override void OnCanvasHierarchyChanged() 333 //{ 334 // //Debug.Log("OnCanvasHierarchyChanged..."); 335 //} 336 337 338 // IClippable implementation 339 /// <summary> 340 /// Method called when the state of a parent changes. 341 /// </summary> 342 public override void RecalculateClipping() 343 { 344 //Debug.Log("***** RecalculateClipping() *****"); 345 346 base.RecalculateClipping(); 347 } 348 349 350 // IMaskable Implementation 351 /// <summary> 352 /// Method called when Stencil Mask needs to be updated on this element and parents. 353 /// </summary> 354 // public override void RecalculateMasking() 355 // { 356 // //Debug.Log("***** RecalculateMasking() *****"); 357 // 358 // this.m_ShouldRecalculateStencil = true; 359 // SetMaterialDirty(); 360 // } 361 362 363 //public override void SetClipRect(Rect clipRect, bool validRect) 364 //{ 365 // //Debug.Log("***** SetClipRect (" + clipRect + ", " + validRect + ") *****"); 366 367 // base.SetClipRect(clipRect, validRect); 368 //} 369 370 371 /// <summary> 372 /// Override of the Cull function to provide for the ability to override the culling of the text object. 373 /// </summary> 374 /// <param name="clipRect"></param> 375 /// <param name="validRect"></param> 376 public override void Cull(Rect clipRect, bool validRect) 377 { 378 m_ShouldUpdateCulling = false; 379 380 // Delay culling check in the event the text layout is dirty and geometry has to be updated. 381 if (m_isLayoutDirty) 382 { 383 m_ShouldUpdateCulling = true; 384 m_ClipRect = clipRect; 385 m_ValidRect = validRect; 386 return; 387 } 388 389 // Get compound rect for the text object and sub text objects in local canvas space. 390 Rect rect = GetCanvasSpaceClippingRect(); 391 392 // No point culling if geometry bounds have no width or height. 393 //if (rect.width == 0 || rect.height == 0) 394 // return; 395 396 var cull = !validRect || !clipRect.Overlaps(rect, true); 397 if (m_canvasRenderer.cull != cull) 398 { 399 m_canvasRenderer.cull = cull; 400 onCullStateChanged.Invoke(cull); 401 OnCullingChanged(); 402 403 // Update any potential sub mesh objects 404 for (int i = 1; i < m_subTextObjects.Length && m_subTextObjects[i] != null; i++) 405 { 406 m_subTextObjects[i].canvasRenderer.cull = cull; 407 } 408 } 409 } 410 411 private bool m_ShouldUpdateCulling; 412 private Rect m_ClipRect; 413 private bool m_ValidRect; 414 415 /// <summary> 416 /// Internal function to allow delay of culling until the text geometry has been updated. 417 /// </summary> 418 internal override void UpdateCulling() 419 { 420 // Get compound rect for the text object and sub text objects in local canvas space. 421 Rect rect = GetCanvasSpaceClippingRect(); 422 423 // No point culling if geometry bounds have no width or height. 424 //if (rect.width == 0 || rect.height == 0) 425 // return; 426 427 var cull = !m_ValidRect || !m_ClipRect.Overlaps(rect, true); 428 if (m_canvasRenderer.cull != cull) 429 { 430 m_canvasRenderer.cull = cull; 431 onCullStateChanged.Invoke(cull); 432 OnCullingChanged(); 433 434 // Update any potential sub mesh objects 435 for (int i = 1; i < m_subTextObjects.Length && m_subTextObjects[i] != null; i++) 436 { 437 m_subTextObjects[i].canvasRenderer.cull = cull; 438 } 439 } 440 441 m_ShouldUpdateCulling = false; 442 } 443 444 445 /* 446 /// <summary> 447 /// Sets the mask type 448 /// </summary> 449 public MaskingTypes mask 450 { 451 get { return m_mask; } 452 set { m_mask = value; havePropertiesChanged = true; isMaskUpdateRequired = true; } 453 } 454 455 456 /// <summary> 457 /// Set the masking offset mode (as percentage or pixels) 458 /// </summary> 459 public MaskingOffsetMode maskOffsetMode 460 { 461 get { return m_maskOffsetMode; } 462 set { m_maskOffsetMode = value; havePropertiesChanged = true; isMaskUpdateRequired = true; } 463 } 464 */ 465 466 467 /* 468 /// <summary> 469 /// Sets the softness of the mask 470 /// </summary> 471 public Vector2 maskSoftness 472 { 473 get { return m_maskSoftness; } 474 set { m_maskSoftness = value; havePropertiesChanged = true; isMaskUpdateRequired = true; } 475 } 476 477 478 /// <summary> 479 /// Allows to move / offset the mesh vertices by a set amount 480 /// </summary> 481 public Vector2 vertexOffset 482 { 483 get { return m_vertexOffset; } 484 set { m_vertexOffset = value; havePropertiesChanged = true; isMaskUpdateRequired = true; } 485 } 486 */ 487 488 489 /// <summary> 490 /// Function to be used to force recomputing of character padding when Shader / Material properties have been changed via script. 491 /// </summary> 492 public override void UpdateMeshPadding() 493 { 494 m_padding = ShaderUtilities.GetPadding(m_sharedMaterial, m_enableExtraPadding, m_isUsingBold); 495 m_isMaskingEnabled = ShaderUtilities.IsMaskingEnabled(m_sharedMaterial); 496 m_havePropertiesChanged = true; 497 checkPaddingRequired = false; 498 499 // Return if text object is not awake yet. 500 if (m_textInfo == null) return; 501 502 // Update sub text objects 503 for (int i = 1; i < m_textInfo.materialCount; i++) 504 m_subTextObjects[i].UpdateMeshPadding(m_enableExtraPadding, m_isUsingBold); 505 } 506 507 508 /// <summary> 509 /// Tweens the CanvasRenderer color associated with this Graphic. 510 /// </summary> 511 /// <param name="targetColor">Target color.</param> 512 /// <param name="duration">Tween duration.</param> 513 /// <param name="ignoreTimeScale">Should ignore Time.scale?</param> 514 /// <param name="useAlpha">Should also Tween the alpha channel?</param> 515 protected override void InternalCrossFadeColor(Color targetColor, float duration, bool ignoreTimeScale, bool useAlpha) 516 { 517 if (m_textInfo == null) 518 return; 519 520 int materialCount = m_textInfo.materialCount; 521 522 for (int i = 1; i < materialCount; i++) 523 { 524 m_subTextObjects[i].CrossFadeColor(targetColor, duration, ignoreTimeScale, useAlpha); 525 } 526 } 527 528 529 /// <summary> 530 /// Tweens the alpha of the CanvasRenderer color associated with this Graphic. 531 /// </summary> 532 /// <param name="alpha">Target alpha.</param> 533 /// <param name="duration">Duration of the tween in seconds.</param> 534 /// <param name="ignoreTimeScale">Should ignore Time.scale?</param> 535 protected override void InternalCrossFadeAlpha(float alpha, float duration, bool ignoreTimeScale) 536 { 537 if (m_textInfo == null) 538 return; 539 540 int materialCount = m_textInfo.materialCount; 541 542 for (int i = 1; i < materialCount; i++) 543 { 544 m_subTextObjects[i].CrossFadeAlpha(alpha, duration, ignoreTimeScale); 545 } 546 } 547 548 549 /// <summary> 550 /// Function to force regeneration of the text object before its normal process time. This is useful when changes to the text object properties need to be applied immediately. 551 /// </summary> 552 /// <param name="ignoreActiveState">Ignore Active State of text objects. Inactive objects are ignored by default.</param> 553 /// <param name="forceTextReparsing">Force re-parsing of the text.</param> 554 public override void ForceMeshUpdate(bool ignoreActiveState = false, bool forceTextReparsing = false) 555 { 556 m_havePropertiesChanged = true; 557 m_ignoreActiveState = ignoreActiveState; 558 559 // Special handling in the event the Canvas is only disabled 560 if (m_canvas == null) 561 m_canvas = GetComponentInParent<Canvas>(); 562 563 OnPreRenderCanvas(); 564 } 565 566 567 /// <summary> 568 /// Function used to evaluate the length of a text string. 569 /// </summary> 570 /// <param name="text"></param> 571 /// <returns></returns> 572 public override TMP_TextInfo GetTextInfo(string text) 573 { 574 SetText(text); 575 SetArraySizes(m_TextProcessingArray); 576 577 m_renderMode = TextRenderFlags.DontRender; 578 579 ComputeMarginSize(); 580 581 // Need to make sure we have a valid reference to a Canvas. 582 if (m_canvas == null) m_canvas = this.canvas; 583 584 GenerateTextMesh(); 585 586 m_renderMode = TextRenderFlags.Render; 587 588 return this.textInfo; 589 } 590 591 592 /// <summary> 593 /// Function to clear the geometry of the Primary and Sub Text objects. 594 /// </summary> 595 public override void ClearMesh() 596 { 597 m_canvasRenderer.SetMesh(null); 598 599 for (int i = 1; i < m_subTextObjects.Length && m_subTextObjects[i] != null; i++) 600 m_subTextObjects[i].canvasRenderer.SetMesh(null); 601 } 602 603 604 /// <summary> 605 /// Event to allow users to modify the content of the text info before the text is rendered. 606 /// </summary> 607 public override event Action<TMP_TextInfo> OnPreRenderText; 608 609 610 /// <summary> 611 /// Function to update the geometry of the main and sub text objects. 612 /// </summary> 613 /// <param name="mesh"></param> 614 /// <param name="index"></param> 615 public override void UpdateGeometry(Mesh mesh, int index) 616 { 617 mesh.RecalculateBounds(); 618 619 if (index == 0) 620 { 621 m_canvasRenderer.SetMesh(mesh); 622 } 623 else 624 { 625 m_subTextObjects[index].canvasRenderer.SetMesh(mesh); 626 } 627 } 628 629 630 /// <summary> 631 /// Function to upload the updated vertex data and renderer. 632 /// </summary> 633 public override void UpdateVertexData(TMP_VertexDataUpdateFlags flags) 634 { 635 int materialCount = m_textInfo.materialCount; 636 637 for (int i = 0; i < materialCount; i++) 638 { 639 Mesh mesh; 640 641 if (i == 0) 642 mesh = m_mesh; 643 else 644 { 645 // Clear unused vertices 646 // TODO: Causes issues when sorting geometry as last vertex data attribute get wiped out. 647 //m_textInfo.meshInfo[i].ClearUnusedVertices(); 648 649 mesh = m_subTextObjects[i].mesh; 650 } 651 652 if ((flags & TMP_VertexDataUpdateFlags.Vertices) == TMP_VertexDataUpdateFlags.Vertices) 653 mesh.vertices = m_textInfo.meshInfo[i].vertices; 654 655 if ((flags & TMP_VertexDataUpdateFlags.Uv0) == TMP_VertexDataUpdateFlags.Uv0) 656 mesh.SetUVs(0, m_textInfo.meshInfo[i].uvs0); 657 658 if ((flags & TMP_VertexDataUpdateFlags.Uv2) == TMP_VertexDataUpdateFlags.Uv2) 659 mesh.uv2 = m_textInfo.meshInfo[i].uvs2; 660 661 //if ((flags & TMP_VertexDataUpdateFlags.Uv4) == TMP_VertexDataUpdateFlags.Uv4) 662 // mesh.uv4 = m_textInfo.meshInfo[i].uvs4; 663 664 if ((flags & TMP_VertexDataUpdateFlags.Colors32) == TMP_VertexDataUpdateFlags.Colors32) 665 mesh.colors32 = m_textInfo.meshInfo[i].colors32; 666 667 mesh.RecalculateBounds(); 668 669 if (i == 0) 670 m_canvasRenderer.SetMesh(mesh); 671 else 672 m_subTextObjects[i].canvasRenderer.SetMesh(mesh); 673 } 674 } 675 676 677 /// <summary> 678 /// Function to upload the updated vertex data and renderer. 679 /// </summary> 680 public override void UpdateVertexData() 681 { 682 int materialCount = m_textInfo.materialCount; 683 684 for (int i = 0; i < materialCount; i++) 685 { 686 Mesh mesh; 687 688 if (i == 0) 689 mesh = m_mesh; 690 else 691 { 692 // Clear unused vertices 693 m_textInfo.meshInfo[i].ClearUnusedVertices(); 694 695 mesh = m_subTextObjects[i].mesh; 696 } 697 698 //mesh.MarkDynamic(); 699 mesh.vertices = m_textInfo.meshInfo[i].vertices; 700 mesh.SetUVs(0, m_textInfo.meshInfo[i].uvs0); 701 mesh.uv2 = m_textInfo.meshInfo[i].uvs2; 702 //mesh.uv4 = m_textInfo.meshInfo[i].uvs4; 703 mesh.colors32 = m_textInfo.meshInfo[i].colors32; 704 705 mesh.RecalculateBounds(); 706 707 if (i == 0) 708 m_canvasRenderer.SetMesh(mesh); 709 else 710 m_subTextObjects[i].canvasRenderer.SetMesh(mesh); 711 } 712 } 713 714 715 public void UpdateFontAsset() 716 { 717 LoadFontAsset(); 718 } 719 720 #region TMPro_UGUI_Private 721 [SerializeField] 722 private bool m_hasFontAssetChanged = false; // Used to track when font properties have changed. 723 724 protected TMP_SubMeshUI[] m_subTextObjects = new TMP_SubMeshUI[8]; 725 726 private float m_previousLossyScaleY = -1; // Used for Tracking lossy scale changes in the transform; 727 728 private Vector3[] m_RectTransformCorners = new Vector3[4]; 729 private CanvasRenderer m_canvasRenderer; 730 private Canvas m_canvas; 731 private float m_CanvasScaleFactor; 732 733 734 private bool m_isFirstAllocation; // Flag to determine if this is the first allocation of the buffers. 735 private int m_max_characters = 8; // Determines the initial allocation and size of the character array / buffer. 736 //private int m_max_numberOfLines = 4; // Determines the initial allocation and maximum number of lines of text. 737 738 // MASKING RELATED PROPERTIES 739 // This property is now obsolete and used for compatibility with previous releases (prior to release 0.1.54). 740 [SerializeField] 741 private Material m_baseMaterial; 742 743 private bool m_isScrollRegionSet; 744 //private Mask m_mask; 745 746 [SerializeField] 747 private Vector4 m_maskOffset; 748 749 // Matrix used to animated Env Map 750 private Matrix4x4 m_EnvMapMatrix = new Matrix4x4(); 751 752 753 //private bool m_isEnabled; 754 [NonSerialized] 755 private bool m_isRegisteredForEvents; 756 757 // Profiler Marker declarations 758 private static ProfilerMarker k_GenerateTextMarker = new ProfilerMarker("TMP.GenerateText"); 759 private static ProfilerMarker k_SetArraySizesMarker = new ProfilerMarker("TMP.SetArraySizes"); 760 private static ProfilerMarker k_GenerateTextPhaseIMarker = new ProfilerMarker("TMP GenerateText - Phase I"); 761 private static ProfilerMarker k_ParseMarkupTextMarker = new ProfilerMarker("TMP Parse Markup Text"); 762 private static ProfilerMarker k_CharacterLookupMarker = new ProfilerMarker("TMP Lookup Character & Glyph Data"); 763 private static ProfilerMarker k_HandleGPOSFeaturesMarker = new ProfilerMarker("TMP Handle GPOS Features"); 764 private static ProfilerMarker k_CalculateVerticesPositionMarker = new ProfilerMarker("TMP Calculate Vertices Position"); 765 private static ProfilerMarker k_ComputeTextMetricsMarker = new ProfilerMarker("TMP Compute Text Metrics"); 766 private static ProfilerMarker k_HandleVisibleCharacterMarker = new ProfilerMarker("TMP Handle Visible Character"); 767 private static ProfilerMarker k_HandleWhiteSpacesMarker = new ProfilerMarker("TMP Handle White Space & Control Character"); 768 private static ProfilerMarker k_HandleHorizontalLineBreakingMarker = new ProfilerMarker("TMP Handle Horizontal Line Breaking"); 769 private static ProfilerMarker k_HandleVerticalLineBreakingMarker = new ProfilerMarker("TMP Handle Vertical Line Breaking"); 770 private static ProfilerMarker k_SaveGlyphVertexDataMarker = new ProfilerMarker("TMP Save Glyph Vertex Data"); 771 private static ProfilerMarker k_ComputeCharacterAdvanceMarker = new ProfilerMarker("TMP Compute Character Advance"); 772 private static ProfilerMarker k_HandleCarriageReturnMarker = new ProfilerMarker("TMP Handle Carriage Return"); 773 private static ProfilerMarker k_HandleLineTerminationMarker = new ProfilerMarker("TMP Handle Line Termination"); 774 private static ProfilerMarker k_SavePageInfoMarker = new ProfilerMarker("TMP Save Page Info"); 775 private static ProfilerMarker k_SaveTextExtentMarker = new ProfilerMarker("TMP Save Text Extent"); 776 private static ProfilerMarker k_SaveProcessingStatesMarker = new ProfilerMarker("TMP Save Processing States"); 777 private static ProfilerMarker k_GenerateTextPhaseIIMarker = new ProfilerMarker("TMP GenerateText - Phase II"); 778 private static ProfilerMarker k_GenerateTextPhaseIIIMarker = new ProfilerMarker("TMP GenerateText - Phase III"); 779 780 781 protected override void Awake() 782 { 783 //Debug.Log("***** Awake() called on object ID " + GetInstanceID() + ". *****"); 784 785 #if UNITY_EDITOR 786 // Special handling for TMP Settings and importing Essential Resources 787 if (TMP_Settings.instance == null) 788 { 789 if (m_isWaitingOnResourceLoad == false) 790 TMPro_EventManager.RESOURCE_LOAD_EVENT.Add(ON_RESOURCES_LOADED); 791 792 m_isWaitingOnResourceLoad = true; 793 return; 794 } 795 #endif 796 797 // Cache Reference to the Canvas 798 m_canvas = this.canvas; 799 800 m_isOrthographic = true; 801 802 // Cache Reference to RectTransform. 803 m_rectTransform = gameObject.GetComponent<RectTransform>(); 804 if (m_rectTransform == null) 805 m_rectTransform = gameObject.AddComponent<RectTransform>(); 806 807 // Cache a reference to the CanvasRenderer. 808 m_canvasRenderer = GetComponent<CanvasRenderer>(); 809 if (m_canvasRenderer == null) 810 m_canvasRenderer = gameObject.AddComponent<CanvasRenderer> (); 811 812 if (m_mesh == null) 813 { 814 m_mesh = new Mesh(); 815 m_mesh.hideFlags = HideFlags.HideAndDontSave; 816 #if DEVELOPMENT_BUILD || UNITY_EDITOR 817 m_mesh.name = "TextMeshPro UI Mesh"; 818 #endif 819 // Create new TextInfo for the text object. 820 m_textInfo = new TMP_TextInfo(this); 821 } 822 823 // Load TMP Settings for new text object instances. 824 LoadDefaultSettings(); 825 826 // Load the font asset and assign material to renderer. 827 LoadFontAsset(); 828 829 // Allocate our initial buffers. 830 if (m_TextProcessingArray == null) 831 m_TextProcessingArray = new TextProcessingElement[m_max_characters]; 832 833 m_cached_TextElement = new TMP_Character(); 834 m_isFirstAllocation = true; 835 836 // Set flags to ensure our text is parsed and redrawn. 837 m_havePropertiesChanged = true; 838 839 m_isAwake = true; 840 } 841 842 843 protected override void OnEnable() 844 { 845 //Debug.Log("***** OnEnable() called on object ID " + GetInstanceID() + ". *****"); 846 847 // Return if Awake() has not been called on the text object. 848 if (m_isAwake == false) 849 return; 850 851 if (!m_isRegisteredForEvents) 852 { 853 //Debug.Log("Registering for Events."); 854 855 #if UNITY_EDITOR 856 // Register Callbacks for various events. 857 TMPro_EventManager.MATERIAL_PROPERTY_EVENT.Add(ON_MATERIAL_PROPERTY_CHANGED); 858 TMPro_EventManager.FONT_PROPERTY_EVENT.Add(ON_FONT_PROPERTY_CHANGED); 859 TMPro_EventManager.TEXTMESHPRO_UGUI_PROPERTY_EVENT.Add(ON_TEXTMESHPRO_UGUI_PROPERTY_CHANGED); 860 TMPro_EventManager.DRAG_AND_DROP_MATERIAL_EVENT.Add(ON_DRAG_AND_DROP_MATERIAL); 861 TMPro_EventManager.TEXT_STYLE_PROPERTY_EVENT.Add(ON_TEXT_STYLE_CHANGED); 862 TMPro_EventManager.COLOR_GRADIENT_PROPERTY_EVENT.Add(ON_COLOR_GRADIENT_CHANGED); 863 TMPro_EventManager.TMP_SETTINGS_PROPERTY_EVENT.Add(ON_TMP_SETTINGS_CHANGED); 864 865 UnityEditor.PrefabUtility.prefabInstanceUpdated += OnPrefabInstanceUpdate; 866 #endif 867 m_isRegisteredForEvents = true; 868 } 869 870 // Cache Reference to the Canvas 871 m_canvas = GetCanvas(); 872 873 SetActiveSubMeshes(true); 874 875 // Register Graphic Component to receive event triggers 876 GraphicRegistry.RegisterGraphicForCanvas(m_canvas, this); 877 878 // Register text object for internal updates 879 if (m_IsTextObjectScaleStatic == false) 880 TMP_UpdateManager.RegisterTextObjectForUpdate(this); 881 882 ComputeMarginSize(); 883 884 SetAllDirty(); 885 886 RecalculateClipping(); 887 RecalculateMasking(); 888 } 889 890 891 protected override void OnDisable() 892 { 893 //Debug.Log("***** OnDisable() called on object ID " + GetInstanceID() + ". *****"); 894 895 // Return if Awake() has not been called on the text object. 896 if (m_isAwake == false) 897 return; 898 899 //if (m_MaskMaterial != null) 900 //{ 901 // TMP_MaterialManager.ReleaseStencilMaterial(m_MaskMaterial); 902 // m_MaskMaterial = null; 903 //} 904 905 // UnRegister Graphic Component 906 GraphicRegistry.UnregisterGraphicForCanvas(m_canvas, this); 907 CanvasUpdateRegistry.UnRegisterCanvasElementForRebuild((ICanvasElement)this); 908 909 TMP_UpdateManager.UnRegisterTextObjectForUpdate(this); 910 911 if (m_canvasRenderer != null) 912 m_canvasRenderer.Clear(); 913 914 SetActiveSubMeshes(false); 915 916 LayoutRebuilder.MarkLayoutForRebuild(m_rectTransform); 917 RecalculateClipping(); 918 RecalculateMasking(); 919 } 920 921 922 protected override void OnDestroy() 923 { 924 //Debug.Log("***** OnDestroy() called on object ID " + GetInstanceID() + ". *****"); 925 926 // UnRegister Graphic Component 927 GraphicRegistry.UnregisterGraphicForCanvas(m_canvas, this); 928 929 TMP_UpdateManager.UnRegisterTextObjectForUpdate(this); 930 931 // Clean up remaining mesh 932 if (m_mesh != null) 933 DestroyImmediate(m_mesh); 934 935 // Clean up mask material 936 if (m_MaskMaterial != null) 937 { 938 TMP_MaterialManager.ReleaseStencilMaterial(m_MaskMaterial); 939 m_MaskMaterial = null; 940 } 941 942 #if UNITY_EDITOR 943 // Unregister the event this object was listening to 944 TMPro_EventManager.MATERIAL_PROPERTY_EVENT.Remove(ON_MATERIAL_PROPERTY_CHANGED); 945 TMPro_EventManager.FONT_PROPERTY_EVENT.Remove(ON_FONT_PROPERTY_CHANGED); 946 TMPro_EventManager.TEXTMESHPRO_UGUI_PROPERTY_EVENT.Remove(ON_TEXTMESHPRO_UGUI_PROPERTY_CHANGED); 947 TMPro_EventManager.DRAG_AND_DROP_MATERIAL_EVENT.Remove(ON_DRAG_AND_DROP_MATERIAL); 948 TMPro_EventManager.TEXT_STYLE_PROPERTY_EVENT.Remove(ON_TEXT_STYLE_CHANGED); 949 TMPro_EventManager.COLOR_GRADIENT_PROPERTY_EVENT.Remove(ON_COLOR_GRADIENT_CHANGED); 950 TMPro_EventManager.TMP_SETTINGS_PROPERTY_EVENT.Remove(ON_TMP_SETTINGS_CHANGED); 951 TMPro_EventManager.RESOURCE_LOAD_EVENT.Remove(ON_RESOURCES_LOADED); 952 953 UnityEditor.PrefabUtility.prefabInstanceUpdated -= OnPrefabInstanceUpdate; 954 #endif 955 m_isRegisteredForEvents = false; 956 } 957 958 959 #if UNITY_EDITOR 960 protected override void Reset() 961 { 962 //Debug.Log("***** Reset() *****"); //has been called."); 963 964 // Return if Awake() has not been called on the text object. 965 if (m_isAwake == false) 966 return; 967 968 LoadDefaultSettings(); 969 LoadFontAsset(); 970 971 m_havePropertiesChanged = true; 972 } 973 974 975 protected override void OnValidate() 976 { 977 //Debug.Log("***** OnValidate() ***** Frame:" + Time.frameCount); // ID " + GetInstanceID()); // New Material [" + m_sharedMaterial.name + "] with ID " + m_sharedMaterial.GetInstanceID() + ". Base Material is [" + m_baseMaterial.name + "] with ID " + m_baseMaterial.GetInstanceID() + ". Previous Base Material is [" + (m_lastBaseMaterial == null ? "Null" : m_lastBaseMaterial.name) + "]."); 978 979 if (m_isAwake == false) 980 return; 981 982 // Handle Font Asset changes in the inspector. 983 if (m_fontAsset == null || m_hasFontAssetChanged) 984 { 985 LoadFontAsset(); 986 m_hasFontAssetChanged = false; 987 } 988 989 if (m_canvasRenderer == null || m_canvasRenderer.GetMaterial() == null || m_canvasRenderer.GetMaterial().GetTexture(ShaderUtilities.ID_MainTex) == null || m_fontAsset == null || m_fontAsset.atlasTexture.GetInstanceID() != m_canvasRenderer.GetMaterial().GetTexture(ShaderUtilities.ID_MainTex).GetInstanceID()) 990 { 991 LoadFontAsset(); 992 m_hasFontAssetChanged = false; 993 } 994 995 m_padding = GetPaddingForMaterial(); 996 ComputeMarginSize(); 997 998 m_inputSource = TextInputSources.TextInputBox; 999 m_havePropertiesChanged = true; 1000 m_isPreferredWidthDirty = true; 1001 m_isPreferredHeightDirty = true; 1002 1003 SetAllDirty(); 1004 } 1005 1006 1007 /// <summary> 1008 /// Callback received when Prefabs are updated. 1009 /// </summary> 1010 /// <param name="go">The affected GameObject</param> 1011 void OnPrefabInstanceUpdate(GameObject go) 1012 { 1013 // Remove Callback if this prefab has been deleted. 1014 if (this == null) 1015 { 1016 UnityEditor.PrefabUtility.prefabInstanceUpdated -= OnPrefabInstanceUpdate; 1017 return; 1018 } 1019 1020 if (go == this.gameObject) 1021 { 1022 TMP_SubMeshUI[] subTextObjects = GetComponentsInChildren<TMP_SubMeshUI>(); 1023 if (subTextObjects.Length > 0) 1024 { 1025 for (int i = 0; i < subTextObjects.Length; i++) 1026 m_subTextObjects[i + 1] = subTextObjects[i]; 1027 } 1028 } 1029 } 1030 1031 1032 // Event received when TMP resources have been loaded. 1033 void ON_RESOURCES_LOADED() 1034 { 1035 TMPro_EventManager.RESOURCE_LOAD_EVENT.Remove(ON_RESOURCES_LOADED); 1036 1037 if (this == null) 1038 return; 1039 1040 m_isWaitingOnResourceLoad = false; 1041 1042 Awake(); 1043 OnEnable(); 1044 } 1045 1046 1047 // Event received when custom material editor properties are changed. 1048 void ON_MATERIAL_PROPERTY_CHANGED(bool isChanged, Material mat) 1049 { 1050 //Debug.Log("ON_MATERIAL_PROPERTY_CHANGED event received."); // Targeted Material is: " + mat.name + " m_sharedMaterial: " + m_sharedMaterial.name + " with ID:" + m_sharedMaterial.GetInstanceID() + " m_renderer.sharedMaterial: " + m_canvasRenderer.GetMaterial() + " Masking Material:" + m_MaskMaterial.GetInstanceID()); 1051 1052 ShaderUtilities.GetShaderPropertyIDs(); // Initialize ShaderUtilities and get shader property IDs. 1053 1054 int materialID = mat.GetInstanceID(); 1055 int sharedMaterialID = m_sharedMaterial.GetInstanceID(); 1056 int maskingMaterialID = m_MaskMaterial == null ? 0 : m_MaskMaterial.GetInstanceID(); 1057 1058 if (m_canvasRenderer == null || m_canvasRenderer.GetMaterial() == null) 1059 { 1060 if (m_canvasRenderer == null) return; 1061 1062 if (m_fontAsset != null) 1063 { 1064 m_canvasRenderer.SetMaterial(m_fontAsset.material, m_sharedMaterial.GetTexture(ShaderUtilities.ID_MainTex)); 1065 //Debug.LogWarning("No Material was assigned to " + name + ". " + m_fontAsset.material.name + " was assigned."); 1066 } 1067 else 1068 Debug.LogWarning("No Font Asset assigned to " + name + ". Please assign a Font Asset.", this); 1069 } 1070 1071 1072 if (m_canvasRenderer.GetMaterial() != m_sharedMaterial && m_fontAsset == null) // || m_renderer.sharedMaterials.Contains(mat)) 1073 { 1074 //Debug.Log("ON_MATERIAL_PROPERTY_CHANGED Called on Target ID: " + GetInstanceID() + ". Previous Material:" + m_sharedMaterial + " New Material:" + m_uiRenderer.GetMaterial()); // on Object ID:" + GetInstanceID() + ". m_sharedMaterial: " + m_sharedMaterial.name + " m_renderer.sharedMaterial: " + m_renderer.sharedMaterial.name); 1075 m_sharedMaterial = m_canvasRenderer.GetMaterial(); 1076 } 1077 1078 1079 // Make sure material properties are synchronized between the assigned material and masking material. 1080 if (m_MaskMaterial != null) 1081 { 1082 UnityEditor.Undo.RecordObject(m_MaskMaterial, "Material Property Changes"); 1083 UnityEditor.Undo.RecordObject(m_sharedMaterial, "Material Property Changes"); 1084 1085 if (materialID == sharedMaterialID) 1086 { 1087 //Debug.Log("Copy base material properties to masking material if not null."); 1088 float stencilID = m_MaskMaterial.GetFloat(ShaderUtilities.ID_StencilID); 1089 float stencilComp = m_MaskMaterial.GetFloat(ShaderUtilities.ID_StencilComp); 1090 //float stencilOp = m_MaskMaterial.GetFloat(ShaderUtilities.ID_StencilOp); 1091 //float stencilRead = m_MaskMaterial.GetFloat(ShaderUtilities.ID_StencilReadMask); 1092 //float stencilWrite = m_MaskMaterial.GetFloat(ShaderUtilities.ID_StencilWriteMask); 1093 1094 m_MaskMaterial.CopyPropertiesFromMaterial(mat); 1095 m_MaskMaterial.shaderKeywords = mat.shaderKeywords; 1096 1097 m_MaskMaterial.SetFloat(ShaderUtilities.ID_StencilID, stencilID); 1098 m_MaskMaterial.SetFloat(ShaderUtilities.ID_StencilComp, stencilComp); 1099 //m_MaskMaterial.SetFloat(ShaderUtilities.ID_StencilOp, stencilOp); 1100 //m_MaskMaterial.SetFloat(ShaderUtilities.ID_StencilReadMask, stencilID); 1101 //m_MaskMaterial.SetFloat(ShaderUtilities.ID_StencilWriteMask, 0); 1102 } 1103 else if (materialID == maskingMaterialID) 1104 { 1105 // Update the padding 1106 GetPaddingForMaterial(mat); 1107 1108 m_sharedMaterial.CopyPropertiesFromMaterial(mat); 1109 m_sharedMaterial.shaderKeywords = mat.shaderKeywords; 1110 m_sharedMaterial.SetFloat(ShaderUtilities.ID_StencilID, 0); 1111 m_sharedMaterial.SetFloat(ShaderUtilities.ID_StencilComp, 8); 1112 //m_sharedMaterial.SetFloat(ShaderUtilities.ID_StencilOp, 0); 1113 //m_sharedMaterial.SetFloat(ShaderUtilities.ID_StencilReadMask, 255); 1114 //m_sharedMaterial.SetFloat(ShaderUtilities.ID_StencilWriteMask, 255); 1115 } 1116 1117 } 1118 1119 m_padding = GetPaddingForMaterial(); 1120 ValidateEnvMapProperty(); 1121 m_havePropertiesChanged = true; 1122 SetVerticesDirty(); 1123 //SetMaterialDirty(); 1124 } 1125 1126 1127 // Event received when font asset properties are changed in Font Inspector 1128 void ON_FONT_PROPERTY_CHANGED(bool isChanged, Object font) 1129 { 1130 //if (MaterialReference.Contains(m_materialReferences, (TMP_FontAsset) font)) 1131 { 1132 //Debug.Log("ON_FONT_PROPERTY_CHANGED event received."); 1133 m_havePropertiesChanged = true; 1134 1135 UpdateMeshPadding(); 1136 1137 SetLayoutDirty(); 1138 SetVerticesDirty(); 1139 } 1140 } 1141 1142 1143 // Event received when UNDO / REDO Event alters the properties of the object. 1144 void ON_TEXTMESHPRO_UGUI_PROPERTY_CHANGED(bool isChanged, Object obj) 1145 { 1146 //Debug.Log("Event Received by " + obj); 1147 1148 if (obj == this) 1149 { 1150 //Debug.Log("Undo / Redo Event Received by Object ID:" + GetInstanceID()); 1151 m_havePropertiesChanged = true; 1152 1153 ComputeMarginSize(); // Review this change 1154 SetVerticesDirty(); 1155 } 1156 } 1157 1158 1159 // Event to Track Material Changed resulting from Drag-n-drop. 1160 void ON_DRAG_AND_DROP_MATERIAL(GameObject obj, Material currentMaterial, Material newMaterial) 1161 { 1162 //Debug.Log("Drag-n-Drop Event - Receiving Object ID " + GetInstanceID() + ". Sender ID " + obj.GetInstanceID()); // + ". Prefab Parent is " + UnityEditor.PrefabUtility.GetPrefabParent(gameObject).GetInstanceID()); // + ". New Material is " + newMaterial.name + " with ID " + newMaterial.GetInstanceID() + ". Base Material is " + m_baseMaterial.name + " with ID " + m_baseMaterial.GetInstanceID()); 1163 1164 // Check if event applies to this current object 1165 if (obj == gameObject || UnityEditor.PrefabUtility.GetCorrespondingObjectFromSource(gameObject) == obj) 1166 { 1167 UnityEditor.Undo.RecordObject(this, "Material Assignment"); 1168 UnityEditor.Undo.RecordObject(m_canvasRenderer, "Material Assignment"); 1169 1170 m_sharedMaterial = newMaterial; 1171 1172 m_padding = GetPaddingForMaterial(); 1173 1174 m_havePropertiesChanged = true; 1175 SetVerticesDirty(); 1176 SetMaterialDirty(); 1177 } 1178 } 1179 1180 1181 // Event received when Text Styles are changed. 1182 void ON_TEXT_STYLE_CHANGED(bool isChanged) 1183 { 1184 m_havePropertiesChanged = true; 1185 SetVerticesDirty(); 1186 } 1187 1188 1189 /// <summary> 1190 /// Event received when a Color Gradient Preset is modified. 1191 /// </summary> 1192 /// <param name="textObject"></param> 1193 void ON_COLOR_GRADIENT_CHANGED(Object gradient) 1194 { 1195 m_havePropertiesChanged = true; 1196 SetVerticesDirty(); 1197 } 1198 1199 1200 /// <summary> 1201 /// Event received when the TMP Settings are changed. 1202 /// </summary> 1203 void ON_TMP_SETTINGS_CHANGED() 1204 { 1205 m_defaultSpriteAsset = null; 1206 m_havePropertiesChanged = true; 1207 SetAllDirty(); 1208 } 1209 #endif 1210 1211 1212 // Function which loads either the default font or a newly assigned font asset. This function also assigned the appropriate material to the renderer. 1213 protected override void LoadFontAsset() 1214 { 1215 //Debug.Log("***** LoadFontAsset() *****"); //TextMeshPro LoadFontAsset() has been called."); // Current Font Asset is " + (font != null ? font.name: "Null") ); 1216 1217 ShaderUtilities.GetShaderPropertyIDs(); // Initialize & Get shader property IDs. 1218 1219 if (m_fontAsset == null) 1220 { 1221 if (TMP_Settings.defaultFontAsset != null) 1222 m_fontAsset = TMP_Settings.defaultFontAsset; 1223 1224 if (m_fontAsset == null) 1225 { 1226 Debug.LogWarning("The LiberationSans SDF Font Asset was not found. There is no Font Asset assigned to " + gameObject.name + ".", this); 1227 return; 1228 } 1229 1230 if (m_fontAsset.characterLookupTable == null) 1231 { 1232 Debug.Log("Dictionary is Null!"); 1233 } 1234 1235 m_sharedMaterial = m_fontAsset.material; 1236 } 1237 else 1238 { 1239 // Read font definition if needed. 1240 if (m_fontAsset.characterLookupTable == null) 1241 m_fontAsset.ReadFontAssetDefinition(); 1242 1243 // Added for compatibility with previous releases. 1244 if (m_sharedMaterial == null && m_baseMaterial != null) 1245 { 1246 m_sharedMaterial = m_baseMaterial; 1247 m_baseMaterial = null; 1248 } 1249 1250 // If font atlas texture doesn't match the assigned material font atlas, switch back to default material specified in the Font Asset. 1251 if (m_sharedMaterial == null || m_sharedMaterial.GetTexture(ShaderUtilities.ID_MainTex) == null || m_fontAsset.atlasTexture.GetInstanceID() != m_sharedMaterial.GetTexture(ShaderUtilities.ID_MainTex).GetInstanceID()) 1252 { 1253 if (m_fontAsset.material == null) 1254 Debug.LogWarning("The Font Atlas Texture of the Font Asset " + m_fontAsset.name + " assigned to " + gameObject.name + " is missing.", this); 1255 else 1256 m_sharedMaterial = m_fontAsset.material; 1257 } 1258 } 1259 1260 // Cache environment map property validation. 1261 ValidateEnvMapProperty(); 1262 1263 // Find and cache Underline & Ellipsis characters. 1264 GetSpecialCharacters(m_fontAsset); 1265 1266 m_padding = GetPaddingForMaterial(); 1267 1268 SetMaterialDirty(); 1269 } 1270 1271 1272 /// <summary> 1273 /// Method to retrieve the parent Canvas. 1274 /// </summary> 1275 private Canvas GetCanvas() 1276 { 1277 Canvas canvas = null; 1278 var list = TMP_ListPool<Canvas>.Get(); 1279 1280 gameObject.GetComponentsInParent(false, list); 1281 if (list.Count > 0) 1282 { 1283 // Find the first active and enabled canvas. 1284 for (int i = 0; i < list.Count; ++i) 1285 { 1286 if (list[i].isActiveAndEnabled) 1287 { 1288 canvas = list[i]; 1289 break; 1290 } 1291 } 1292 } 1293 1294 TMP_ListPool<Canvas>.Release(list); 1295 1296 return canvas; 1297 } 1298 1299 /// <summary> 1300 /// Method to check if the environment map property is valid. 1301 /// </summary> 1302 void ValidateEnvMapProperty() 1303 { 1304 if (m_sharedMaterial != null) 1305 m_hasEnvMapProperty = m_sharedMaterial.HasProperty(ShaderUtilities.ID_EnvMap) && m_sharedMaterial.GetTexture(ShaderUtilities.ID_EnvMap) != null; 1306 else 1307 m_hasEnvMapProperty = false; 1308 } 1309 1310 /// <summary> 1311 /// Method used when animating the Env Map on the material. 1312 /// </summary> 1313 void UpdateEnvMapMatrix() 1314 { 1315 if (!m_hasEnvMapProperty) 1316 return; 1317 1318 //Debug.Log("Updating Env Matrix..."); 1319 Vector3 rotation = m_sharedMaterial.GetVector(ShaderUtilities.ID_EnvMatrixRotation); 1320 #if !UNITY_EDITOR 1321 // The matrix property is reverted on editor save because m_sharedMaterial will be replaced with a new material instance. 1322 // Disable rotation change check if editor to handle this material change. 1323 if (m_currentEnvMapRotation == rotation) 1324 return; 1325 #endif 1326 1327 m_currentEnvMapRotation = rotation; 1328 m_EnvMapMatrix = Matrix4x4.TRS(Vector3.zero, Quaternion.Euler(m_currentEnvMapRotation), Vector3.one); 1329 1330 m_sharedMaterial.SetMatrix(ShaderUtilities.ID_EnvMatrix, m_EnvMapMatrix); 1331 } 1332 1333 1334 // Enable Masking in the Shader 1335 void EnableMasking() 1336 { 1337 if (m_fontMaterial == null) 1338 { 1339 m_fontMaterial = CreateMaterialInstance(m_sharedMaterial); 1340 m_canvasRenderer.SetMaterial(m_fontMaterial, m_sharedMaterial.GetTexture(ShaderUtilities.ID_MainTex)); 1341 } 1342 1343 m_sharedMaterial = m_fontMaterial; 1344 if (m_sharedMaterial.HasProperty(ShaderUtilities.ID_ClipRect)) 1345 { 1346 m_sharedMaterial.EnableKeyword(ShaderUtilities.Keyword_MASK_SOFT); 1347 m_sharedMaterial.DisableKeyword(ShaderUtilities.Keyword_MASK_HARD); 1348 m_sharedMaterial.DisableKeyword(ShaderUtilities.Keyword_MASK_TEX); 1349 1350 UpdateMask(); // Update Masking Coordinates 1351 } 1352 1353 m_isMaskingEnabled = true; 1354 1355 //m_uiRenderer.SetMaterial(m_sharedMaterial, null); 1356 1357 //m_padding = ShaderUtilities.GetPadding(m_sharedMaterial, m_enableExtraPadding, m_isUsingBold); 1358 //m_alignmentPadding = ShaderUtilities.GetFontExtent(m_sharedMaterial); 1359 1360 /* 1361 Material mat = m_uiRenderer.GetMaterial(); 1362 if (mat.HasProperty(ShaderUtilities.ID_MaskCoord)) 1363 { 1364 mat.EnableKeyword("MASK_SOFT"); 1365 mat.DisableKeyword("MASK_HARD"); 1366 mat.DisableKeyword("MASK_OFF"); 1367 1368 m_isMaskingEnabled = true; 1369 UpdateMask(); 1370 } 1371 */ 1372 } 1373 1374 1375 // Enable Masking in the Shader 1376 void DisableMasking() 1377 { 1378 /* 1379 if (m_fontMaterial != null) 1380 { 1381 if (m_stencilID > 0) 1382 m_sharedMaterial = m_MaskMaterial; 1383 else 1384 m_sharedMaterial = m_baseMaterial; 1385 1386 m_canvasRenderer.SetMaterial(m_sharedMaterial, m_sharedMaterial.GetTexture(ShaderUtilities.ID_MainTex)); 1387 1388 DestroyImmediate(m_fontMaterial); 1389 } 1390 1391 m_isMaskingEnabled = false; 1392 */ 1393 1394 /* 1395 if (m_maskingMaterial != null && m_stencilID == 0) 1396 { 1397 m_sharedMaterial = m_baseMaterial; 1398 m_uiRenderer.SetMaterial(m_sharedMaterial, null); 1399 } 1400 else if (m_stencilID > 0) 1401 { 1402 m_sharedMaterial.EnableKeyword("MASK_OFF"); 1403 m_sharedMaterial.DisableKeyword("MASK_HARD"); 1404 m_sharedMaterial.DisableKeyword("MASK_SOFT"); 1405 } 1406 */ 1407 1408 1409 /* 1410 Material mat = m_uiRenderer.GetMaterial(); 1411 if (mat.HasProperty(ShaderUtilities.ID_MaskCoord)) 1412 { 1413 mat.EnableKeyword("MASK_OFF"); 1414 mat.DisableKeyword("MASK_HARD"); 1415 mat.DisableKeyword("MASK_SOFT"); 1416 1417 m_isMaskingEnabled = false; 1418 UpdateMask(); 1419 } 1420 */ 1421 } 1422 1423 1424 // Update & recompute Mask offset 1425 void UpdateMask() 1426 { 1427 //Debug.Log("Updating Mask..."); 1428 1429 if (m_rectTransform != null) 1430 { 1431 //Material mat = m_uiRenderer.GetMaterial(); 1432 //if (mat == null || (m_overflowMode == TextOverflowModes.ScrollRect && m_isScrollRegionSet)) 1433 // return; 1434 1435 if (!ShaderUtilities.isInitialized) 1436 ShaderUtilities.GetShaderPropertyIDs(); 1437 1438 //Debug.Log("Setting Mask for the first time."); 1439 1440 m_isScrollRegionSet = true; 1441 1442 float softnessX = Mathf.Min(Mathf.Min(m_margin.x, m_margin.z), m_sharedMaterial.GetFloat(ShaderUtilities.ID_MaskSoftnessX)); 1443 float softnessY = Mathf.Min(Mathf.Min(m_margin.y, m_margin.w), m_sharedMaterial.GetFloat(ShaderUtilities.ID_MaskSoftnessY)); 1444 1445 softnessX = softnessX > 0 ? softnessX : 0; 1446 softnessY = softnessY > 0 ? softnessY : 0; 1447 1448 float width = (m_rectTransform.rect.width - Mathf.Max(m_margin.x, 0) - Mathf.Max(m_margin.z, 0)) / 2 + softnessX; 1449 float height = (m_rectTransform.rect.height - Mathf.Max(m_margin.y, 0) - Mathf.Max(m_margin.w, 0)) / 2 + softnessY; 1450 1451 1452 Vector2 center = m_rectTransform.localPosition + new Vector3((0.5f - m_rectTransform.pivot.x) * m_rectTransform.rect.width + (Mathf.Max(m_margin.x, 0) - Mathf.Max(m_margin.z, 0)) / 2, (0.5f - m_rectTransform.pivot.y) * m_rectTransform.rect.height + (-Mathf.Max(m_margin.y, 0) + Mathf.Max(m_margin.w, 0)) / 2); 1453 1454 //Vector2 center = m_rectTransform.localPosition + new Vector3((0.5f - m_rectTransform.pivot.x) * m_rectTransform.rect.width + (margin.x - margin.z) / 2, (0.5f - m_rectTransform.pivot.y) * m_rectTransform.rect.height + (-margin.y + margin.w) / 2); 1455 Vector4 mask = new Vector4(center.x, center.y, width, height); 1456 //Debug.Log(mask); 1457 1458 1459 1460 //Rect rect = new Rect(0, 0, m_rectTransform.rect.width + margin.x + margin.z, m_rectTransform.rect.height + margin.y + margin.w); 1461 //int softness = (int)m_sharedMaterial.GetFloat(ShaderUtilities.ID_MaskSoftnessX) / 2; 1462 m_sharedMaterial.SetVector(ShaderUtilities.ID_ClipRect, mask); 1463 } 1464 } 1465 1466 1467 // Function called internally when a new material is assigned via the fontMaterial property. 1468 protected override Material GetMaterial(Material mat) 1469 { 1470 // Get Shader PropertyIDs if they haven't been cached already. 1471 ShaderUtilities.GetShaderPropertyIDs(); 1472 1473 // Check in case Object is disabled. If so, we don't have a valid reference to the Renderer. 1474 // This can occur when the Duplicate Material Context menu is used on an inactive object. 1475 //if (m_canvasRenderer == null) 1476 // m_canvasRenderer = GetComponent<CanvasRenderer>(); 1477 1478 // Create Instance Material only if the new material is not the same instance previously used. 1479 if (m_fontMaterial == null || m_fontMaterial.GetInstanceID() != mat.GetInstanceID()) 1480 m_fontMaterial = CreateMaterialInstance(mat); 1481 1482 m_sharedMaterial = m_fontMaterial; 1483 1484 m_padding = GetPaddingForMaterial(); 1485 1486 m_ShouldRecalculateStencil = true; 1487 SetVerticesDirty(); 1488 SetMaterialDirty(); 1489 1490 return m_sharedMaterial; 1491 } 1492 1493 1494 /// <summary> 1495 /// Method returning instances of the materials used by the text object. 1496 /// </summary> 1497 /// <returns></returns> 1498 protected override Material[] GetMaterials(Material[] mats) 1499 { 1500 int materialCount = m_textInfo.materialCount; 1501 1502 if (m_fontMaterials == null) 1503 m_fontMaterials = new Material[materialCount]; 1504 else if (m_fontMaterials.Length != materialCount) 1505 TMP_TextInfo.Resize(ref m_fontMaterials, materialCount, false); 1506 1507 // Get instances of the materials 1508 for (int i = 0; i < materialCount; i++) 1509 { 1510 if (i == 0) 1511 m_fontMaterials[i] = fontMaterial; 1512 else 1513 m_fontMaterials[i] = m_subTextObjects[i].material; 1514 } 1515 1516 m_fontSharedMaterials = m_fontMaterials; 1517 1518 return m_fontMaterials; 1519 } 1520 1521 1522 // Function called internally when a new shared material is assigned via the fontSharedMaterial property. 1523 protected override void SetSharedMaterial(Material mat) 1524 { 1525 // Check in case Object is disabled. If so, we don't have a valid reference to the Renderer. 1526 // This can occur when the Duplicate Material Context menu is used on an inactive object. 1527 //if (m_canvasRenderer == null) 1528 // m_canvasRenderer = GetComponent<CanvasRenderer>(); 1529 1530 m_sharedMaterial = mat; 1531 1532 m_padding = GetPaddingForMaterial(); 1533 1534 SetMaterialDirty(); 1535 } 1536 1537 1538 /// <summary> 1539 /// Method returning an array containing the materials used by the text object. 1540 /// </summary> 1541 /// <returns></returns> 1542 protected override Material[] GetSharedMaterials() 1543 { 1544 int materialCount = m_textInfo.materialCount; 1545 1546 if (m_fontSharedMaterials == null) 1547 m_fontSharedMaterials = new Material[materialCount]; 1548 else if (m_fontSharedMaterials.Length != materialCount) 1549 TMP_TextInfo.Resize(ref m_fontSharedMaterials, materialCount, false); 1550 1551 for (int i = 0; i < materialCount; i++) 1552 { 1553 if (i == 0) 1554 m_fontSharedMaterials[i] = m_sharedMaterial; 1555 else 1556 m_fontSharedMaterials[i] = m_subTextObjects[i].sharedMaterial; 1557 } 1558 1559 return m_fontSharedMaterials; 1560 } 1561 1562 1563 /// <summary> 1564 /// Method used to assign new materials to the text and sub text objects. 1565 /// </summary> 1566 protected override void SetSharedMaterials(Material[] materials) 1567 { 1568 int materialCount = m_textInfo.materialCount; 1569 1570 // Check allocation of the fontSharedMaterials array. 1571 if (m_fontSharedMaterials == null) 1572 m_fontSharedMaterials = new Material[materialCount]; 1573 else if (m_fontSharedMaterials.Length != materialCount) 1574 TMP_TextInfo.Resize(ref m_fontSharedMaterials, materialCount, false); 1575 1576 // Only assign as many materials as the text object contains. 1577 for (int i = 0; i < materialCount; i++) 1578 { 1579 if (i == 0) 1580 { 1581 // Only assign new material if the font atlas textures match. 1582 if (materials[i].GetTexture(ShaderUtilities.ID_MainTex) == null || materials[i].GetTexture(ShaderUtilities.ID_MainTex).GetInstanceID() != m_sharedMaterial.GetTexture(ShaderUtilities.ID_MainTex).GetInstanceID()) 1583 continue; 1584 1585 m_sharedMaterial = m_fontSharedMaterials[i] = materials[i]; 1586 m_padding = GetPaddingForMaterial(m_sharedMaterial); 1587 } 1588 else 1589 { 1590 // Only assign new material if the font atlas textures match. 1591 if (materials[i].GetTexture(ShaderUtilities.ID_MainTex) == null || materials[i].GetTexture(ShaderUtilities.ID_MainTex).GetInstanceID() != m_subTextObjects[i].sharedMaterial.GetTexture(ShaderUtilities.ID_MainTex).GetInstanceID()) 1592 continue; 1593 1594 // Only assign a new material if none were specified in the text input. 1595 if (m_subTextObjects[i].isDefaultMaterial) 1596 m_subTextObjects[i].sharedMaterial = m_fontSharedMaterials[i] = materials[i]; 1597 } 1598 } 1599 } 1600 1601 1602 // This function will create an instance of the Font Material. 1603 protected override void SetOutlineThickness(float thickness) 1604 { 1605 // Use material instance if one exists. Otherwise, create a new instance of the shared material. 1606 if (m_fontMaterial != null && m_sharedMaterial.GetInstanceID() != m_fontMaterial.GetInstanceID()) 1607 { 1608 m_sharedMaterial = m_fontMaterial; 1609 m_canvasRenderer.SetMaterial(m_sharedMaterial, m_sharedMaterial.GetTexture(ShaderUtilities.ID_MainTex)); 1610 } 1611 else if(m_fontMaterial == null) 1612 { 1613 m_fontMaterial = CreateMaterialInstance(m_sharedMaterial); 1614 m_sharedMaterial = m_fontMaterial; 1615 m_canvasRenderer.SetMaterial(m_sharedMaterial, m_sharedMaterial.GetTexture(ShaderUtilities.ID_MainTex)); 1616 } 1617 1618 thickness = Mathf.Clamp01(thickness); 1619 m_sharedMaterial.SetFloat(ShaderUtilities.ID_OutlineWidth, thickness); 1620 m_padding = GetPaddingForMaterial(); 1621 } 1622 1623 1624 // This function will create an instance of the Font Material. 1625 protected override void SetFaceColor(Color32 color) 1626 { 1627 // Use material instance if one exists. Otherwise, create a new instance of the shared material. 1628 if (m_fontMaterial == null) 1629 m_fontMaterial = CreateMaterialInstance(m_sharedMaterial); 1630 1631 m_sharedMaterial = m_fontMaterial; 1632 m_padding = GetPaddingForMaterial(); 1633 1634 m_sharedMaterial.SetColor(ShaderUtilities.ID_FaceColor, color); 1635 } 1636 1637 1638 // This function will create an instance of the Font Material. 1639 protected override void SetOutlineColor(Color32 color) 1640 { 1641 // Use material instance if one exists. Otherwise, create a new instance of the shared material. 1642 if (m_fontMaterial == null) 1643 m_fontMaterial = CreateMaterialInstance(m_sharedMaterial); 1644 1645 m_sharedMaterial = m_fontMaterial; 1646 m_padding = GetPaddingForMaterial(); 1647 1648 m_sharedMaterial.SetColor(ShaderUtilities.ID_OutlineColor, color); 1649 } 1650 1651 1652 // Sets the Render Queue and Ztest mode 1653 protected override void SetShaderDepth() 1654 { 1655 if (m_canvas == null || m_sharedMaterial == null) 1656 return; 1657 1658 if (m_canvas.renderMode == RenderMode.ScreenSpaceOverlay || m_isOverlay) 1659 { 1660 // Should this use an instanced material? 1661 //m_sharedMaterial.SetFloat(ShaderUtilities.ShaderTag_ZTestMode, 0); 1662 } 1663 else 1664 { // TODO: This section needs to be tested. 1665 //m_sharedMaterial.SetFloat(ShaderUtilities.ShaderTag_ZTestMode, 4); 1666 } 1667 } 1668 1669 1670 // Sets the Culling mode of the material 1671 protected override void SetCulling() 1672 { 1673 if (m_isCullingEnabled) 1674 { 1675 Material mat = materialForRendering; 1676 1677 if (mat != null) 1678 mat.SetFloat("_CullMode", 2); 1679 1680 for (int i = 1; i < m_subTextObjects.Length && m_subTextObjects[i] != null; i++) 1681 { 1682 mat = m_subTextObjects[i].materialForRendering; 1683 1684 if (mat != null) 1685 { 1686 mat.SetFloat(ShaderUtilities.ShaderTag_CullMode, 2); 1687 } 1688 } 1689 } 1690 else 1691 { 1692 Material mat = materialForRendering; 1693 1694 if (mat != null) 1695 mat.SetFloat("_CullMode", 0); 1696 1697 for (int i = 1; i < m_subTextObjects.Length && m_subTextObjects[i] != null; i++) 1698 { 1699 mat = m_subTextObjects[i].materialForRendering; 1700 1701 if (mat != null) 1702 { 1703 mat.SetFloat(ShaderUtilities.ShaderTag_CullMode, 0); 1704 } 1705 } 1706 } 1707 } 1708 1709 1710 // Set Perspective Correction Mode based on whether Camera is Orthographic or Perspective 1711 void SetPerspectiveCorrection() 1712 { 1713 if (m_isOrthographic) 1714 m_sharedMaterial.SetFloat(ShaderUtilities.ID_PerspectiveFilter, 0.0f); 1715 else 1716 m_sharedMaterial.SetFloat(ShaderUtilities.ID_PerspectiveFilter, 0.875f); 1717 } 1718 1719 1720 // Function to allocate the necessary buffers to render the text. This function is called whenever the buffer size needs to be increased. 1721 void SetMeshArrays(int size) 1722 { 1723 m_textInfo.meshInfo[0].ResizeMeshInfo(size); 1724 1725 m_canvasRenderer.SetMesh(m_textInfo.meshInfo[0].mesh); 1726 } 1727 1728 1729 // This function parses through the Char[] to determine how many characters will be visible. It then makes sure the arrays are large enough for all those characters. 1730 internal override int SetArraySizes(TextProcessingElement[] textProcessingArray) 1731 { 1732 k_SetArraySizesMarker.Begin(); 1733 1734 int spriteCount = 0; 1735 1736 m_totalCharacterCount = 0; 1737 m_isUsingBold = false; 1738 m_isTextLayoutPhase = false; 1739 tag_NoParsing = false; 1740 m_FontStyleInternal = m_fontStyle; 1741 m_fontStyleStack.Clear(); 1742 1743 m_FontWeightInternal = (m_FontStyleInternal & FontStyles.Bold) == FontStyles.Bold ? FontWeight.Bold : m_fontWeight; 1744 m_FontWeightStack.SetDefault(m_FontWeightInternal); 1745 1746 m_currentFontAsset = m_fontAsset; 1747 m_currentMaterial = m_sharedMaterial; 1748 m_currentMaterialIndex = 0; 1749 1750 m_materialReferenceStack.SetDefault(new MaterialReference(m_currentMaterialIndex, m_currentFontAsset, null, m_currentMaterial, m_padding)); 1751 1752 m_materialReferenceIndexLookup.Clear(); 1753 MaterialReference.AddMaterialReference(m_currentMaterial, m_currentFontAsset, ref m_materialReferences, m_materialReferenceIndexLookup); 1754 1755 // Set allocations for the text object's TextInfo 1756 if (m_textInfo == null) 1757 m_textInfo = new TMP_TextInfo(m_InternalTextProcessingArraySize); 1758 else if (m_textInfo.characterInfo.Length < m_InternalTextProcessingArraySize) 1759 TMP_TextInfo.Resize(ref m_textInfo.characterInfo, m_InternalTextProcessingArraySize, false); 1760 1761 m_textElementType = TMP_TextElementType.Character; 1762 1763 // Handling for Underline special character 1764 #region Setup Underline Special Character 1765 /* 1766 GetUnderlineSpecialCharacter(m_currentFontAsset); 1767 if (m_Underline.character != null) 1768 { 1769 if (m_Underline.fontAsset.GetInstanceID() != m_currentFontAsset.GetInstanceID()) 1770 { 1771 if (TMP_Settings.matchMaterialPreset && m_currentMaterial.GetInstanceID() != m_Underline.fontAsset.material.GetInstanceID()) 1772 m_Underline.material = TMP_MaterialManager.GetFallbackMaterial(m_currentMaterial, m_Underline.fontAsset.material); 1773 else 1774 m_Underline.material = m_Underline.fontAsset.material; 1775 1776 m_Underline.materialIndex = MaterialReference.AddMaterialReference(m_Underline.material, m_Underline.fontAsset, m_materialReferences, m_materialReferenceIndexLookup); 1777 m_materialReferences[m_Underline.materialIndex].referenceCount = 0; 1778 } 1779 } 1780 */ 1781 #endregion 1782 1783 1784 // Handling for Ellipsis special character 1785 #region Setup Ellipsis Special Character 1786 if (m_overflowMode == TextOverflowModes.Ellipsis) 1787 { 1788 GetEllipsisSpecialCharacter(m_currentFontAsset); 1789 1790 if (m_Ellipsis.character != null) 1791 { 1792 if (m_Ellipsis.fontAsset.GetInstanceID() != m_currentFontAsset.GetInstanceID()) 1793 { 1794 if (TMP_Settings.matchMaterialPreset && m_currentMaterial.GetInstanceID() != m_Ellipsis.fontAsset.material.GetInstanceID()) 1795 m_Ellipsis.material = TMP_MaterialManager.GetFallbackMaterial(m_currentMaterial, m_Ellipsis.fontAsset.material); 1796 else 1797 m_Ellipsis.material = m_Ellipsis.fontAsset.material; 1798 1799 m_Ellipsis.materialIndex = MaterialReference.AddMaterialReference(m_Ellipsis.material, m_Ellipsis.fontAsset, ref m_materialReferences, m_materialReferenceIndexLookup); 1800 m_materialReferences[m_Ellipsis.materialIndex].referenceCount = 0; 1801 } 1802 } 1803 else 1804 { 1805 m_overflowMode = TextOverflowModes.Truncate; 1806 1807 if (!TMP_Settings.warningsDisabled) 1808 Debug.LogWarning("The character used for Ellipsis is not available in font asset [" + m_currentFontAsset.name + "] or any potential fallbacks. Switching Text Overflow mode to Truncate.", this); 1809 } 1810 } 1811 #endregion 1812 1813 // Check if we should process Ligatures 1814 bool ligature = m_ActiveFontFeatures.Contains(OTL_FeatureTag.liga); 1815 1816 // Clear Linked Text object content if we have any. 1817 if (m_overflowMode == TextOverflowModes.Linked && m_linkedTextComponent != null && !m_isCalculatingPreferredValues) 1818 { 1819 TMP_Text linkedComponent = m_linkedTextComponent; 1820 1821 while (linkedComponent != null) 1822 { 1823 linkedComponent.text = String.Empty; 1824 linkedComponent.ClearMesh(); 1825 linkedComponent.textInfo.Clear(); 1826 1827 linkedComponent = linkedComponent.linkedTextComponent; 1828 } 1829 } 1830 1831 1832 // Parsing XML tags in the text 1833 for (int i = 0; i < textProcessingArray.Length && textProcessingArray[i].unicode != 0; i++) 1834 { 1835 //Make sure the characterInfo array can hold the next text element. 1836 if (m_textInfo.characterInfo == null || m_totalCharacterCount >= m_textInfo.characterInfo.Length) 1837 TMP_TextInfo.Resize(ref m_textInfo.characterInfo, m_totalCharacterCount + 1, true); 1838 1839 uint unicode = textProcessingArray[i].unicode; 1840 1841 // PARSE XML TAGS 1842 #region PARSE XML TAGS 1843 if (m_isRichText && unicode == 60) // if Char '<' 1844 { 1845 int prev_MaterialIndex = m_currentMaterialIndex; 1846 int endTagIndex; 1847 1848 // Check if Tag is Valid 1849 if (ValidateHtmlTag(textProcessingArray, i + 1, out endTagIndex)) 1850 { 1851 int tagStartIndex = textProcessingArray[i].stringIndex; 1852 i = endTagIndex; 1853 1854 if ((m_FontStyleInternal & FontStyles.Bold) == FontStyles.Bold) 1855 m_isUsingBold = true; 1856 1857 if (m_textElementType == TMP_TextElementType.Sprite) 1858 { 1859 m_materialReferences[m_currentMaterialIndex].referenceCount += 1; 1860 1861 m_textInfo.characterInfo[m_totalCharacterCount].character = (char)(57344 + m_spriteIndex); 1862 m_textInfo.characterInfo[m_totalCharacterCount].fontAsset = m_currentFontAsset; 1863 m_textInfo.characterInfo[m_totalCharacterCount].materialReferenceIndex = m_currentMaterialIndex; 1864 m_textInfo.characterInfo[m_totalCharacterCount].textElement = m_currentSpriteAsset.spriteCharacterTable[m_spriteIndex]; 1865 m_textInfo.characterInfo[m_totalCharacterCount].elementType = m_textElementType; 1866 m_textInfo.characterInfo[m_totalCharacterCount].index = tagStartIndex; 1867 m_textInfo.characterInfo[m_totalCharacterCount].stringLength = textProcessingArray[i].stringIndex - tagStartIndex + 1; 1868 1869 // Restore element type and material index to previous values. 1870 m_textElementType = TMP_TextElementType.Character; 1871 m_currentMaterialIndex = prev_MaterialIndex; 1872 1873 spriteCount += 1; 1874 m_totalCharacterCount += 1; 1875 } 1876 1877 continue; 1878 } 1879 } 1880 #endregion 1881 1882 bool isUsingAlternativeTypeface = false; 1883 bool isUsingFallbackOrAlternativeTypeface = false; 1884 1885 TMP_FontAsset prev_fontAsset = m_currentFontAsset; 1886 Material prev_material = m_currentMaterial; 1887 int prev_materialIndex = m_currentMaterialIndex; 1888 1889 // Handle Font Styles like LowerCase, UpperCase and SmallCaps. 1890 #region Handling of LowerCase, UpperCase and SmallCaps Font Styles 1891 if (m_textElementType == TMP_TextElementType.Character) 1892 { 1893 if ((m_FontStyleInternal & FontStyles.UpperCase) == FontStyles.UpperCase) 1894 { 1895 // If this character is lowercase, switch to uppercase. 1896 if (char.IsLower((char)unicode)) 1897 unicode = char.ToUpper((char)unicode); 1898 1899 } 1900 else if ((m_FontStyleInternal & FontStyles.LowerCase) == FontStyles.LowerCase) 1901 { 1902 // If this character is uppercase, switch to lowercase. 1903 if (char.IsUpper((char)unicode)) 1904 unicode = char.ToLower((char)unicode); 1905 } 1906 else if ((m_FontStyleInternal & FontStyles.SmallCaps) == FontStyles.SmallCaps) 1907 { 1908 // Only convert lowercase characters to uppercase. 1909 if (char.IsLower((char)unicode)) 1910 unicode = char.ToUpper((char)unicode); 1911 } 1912 } 1913 #endregion 1914 1915 // Lookup the Glyph data for each character and cache it. 1916 #region LOOKUP GLYPH 1917 TMP_TextElement character = null; 1918 1919 uint nextCharacter = i + 1 < textProcessingArray.Length ? textProcessingArray[i + 1].unicode : 0; 1920 1921 // Check Emoji Fallback first in the event the requested unicode code point is an Emoji 1922 if (emojiFallbackSupport && ((TMP_TextParsingUtilities.IsEmojiPresentationForm(unicode) && nextCharacter != 0xFE0E) || (TMP_TextParsingUtilities.IsEmoji(unicode) && nextCharacter == 0xFE0F))) 1923 { 1924 if (TMP_Settings.emojiFallbackTextAssets != null && TMP_Settings.emojiFallbackTextAssets.Count > 0) 1925 { 1926 character = TMP_FontAssetUtilities.GetTextElementFromTextAssets(unicode, m_currentFontAsset, TMP_Settings.emojiFallbackTextAssets, true, fontStyle, fontWeight, out isUsingAlternativeTypeface); 1927 1928 if (character != null) 1929 { 1930 // Add character to font asset lookup cache 1931 //fontAsset.AddCharacterToLookupCache(unicode, character); 1932 } 1933 } 1934 } 1935 1936 if (character == null) 1937 character = GetTextElement(unicode, m_currentFontAsset, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface); 1938 1939 // Check if Lowercase or Uppercase variant of the character is available. 1940 /* Not sure this is necessary anyone as it is very unlikely with recursive search through fallback fonts. 1941 if (glyph == null) 1942 { 1943 if (char.IsLower((char)c)) 1944 { 1945 if (m_currentFontAsset.characterDictionary.TryGetValue(char.ToUpper((char)c), out glyph)) 1946 c = chars[i] = char.ToUpper((char)c); 1947 } 1948 else if (char.IsUpper((char)c)) 1949 { 1950 if (m_currentFontAsset.characterDictionary.TryGetValue(char.ToLower((char)c), out glyph)) 1951 c = chars[i] = char.ToLower((char)c); 1952 } 1953 }*/ 1954 1955 #region MISSING CHARACTER HANDLING 1956 // Replace missing glyph by the Square (9633) glyph or possibly the Space (32) glyph. 1957 if (character == null) 1958 { 1959 DoMissingGlyphCallback((int)unicode, textProcessingArray[i].stringIndex, m_currentFontAsset); 1960 1961 // Save the original unicode character 1962 uint srcGlyph = unicode; 1963 1964 // Try replacing the missing glyph character by TMP Settings Missing Glyph or Square (9633) character. 1965 unicode = textProcessingArray[i].unicode = (uint)TMP_Settings.missingGlyphCharacter == 0 ? 9633 : (uint)TMP_Settings.missingGlyphCharacter; 1966 1967 // Check for the missing glyph character in the currently assigned font asset and its fallbacks 1968 character = TMP_FontAssetUtilities.GetCharacterFromFontAsset((uint)unicode, m_currentFontAsset, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface); 1969 1970 if (character == null) 1971 { 1972 // Search for the missing glyph character in the TMP Settings Fallback list. 1973 if (TMP_Settings.fallbackFontAssets != null && TMP_Settings.fallbackFontAssets.Count > 0) 1974 character = TMP_FontAssetUtilities.GetCharacterFromFontAssets(unicode, m_currentFontAsset, TMP_Settings.fallbackFontAssets, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface); 1975 } 1976 1977 if (character == null) 1978 { 1979 // Search for the missing glyph in the TMP Settings Default Font Asset. 1980 if (TMP_Settings.defaultFontAsset != null) 1981 character = TMP_FontAssetUtilities.GetCharacterFromFontAsset(unicode, TMP_Settings.defaultFontAsset, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface); 1982 } 1983 1984 if (character == null) 1985 { 1986 // Use Space (32) Glyph from the currently assigned font asset. 1987 unicode = textProcessingArray[i].unicode = 32; 1988 character = TMP_FontAssetUtilities.GetCharacterFromFontAsset(unicode, m_currentFontAsset, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface); 1989 } 1990 1991 if (character == null) 1992 { 1993 // Use End of Text (0x03) Glyph from the currently assigned font asset. 1994 unicode = textProcessingArray[i].unicode = 0x03; 1995 character = TMP_FontAssetUtilities.GetCharacterFromFontAsset(unicode, m_currentFontAsset, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface); 1996 } 1997 1998 if (!TMP_Settings.warningsDisabled) 1999 { 2000 string formattedWarning = srcGlyph > 0xFFFF 2001 ? string.Format("The character with Unicode value \\U{0:X8} was not found in the [{1}] font asset or any potential fallbacks. It was replaced by Unicode character \\u{2:X4} in text object [{3}].", srcGlyph, m_fontAsset.name, character.unicode, this.name) 2002 : string.Format("The character with Unicode value \\u{0:X4} was not found in the [{1}] font asset or any potential fallbacks. It was replaced by Unicode character \\u{2:X4} in text object [{3}].", srcGlyph, m_fontAsset.name, character.unicode, this.name); 2003 2004 Debug.LogWarning(formattedWarning, this); 2005 } 2006 } 2007 #endregion 2008 2009 m_textInfo.characterInfo[m_totalCharacterCount].alternativeGlyph = null; 2010 2011 if (character.elementType == TextElementType.Character) 2012 { 2013 if (character.textAsset.instanceID != m_currentFontAsset.instanceID) 2014 { 2015 isUsingFallbackOrAlternativeTypeface = true; 2016 m_currentFontAsset = character.textAsset as TMP_FontAsset; 2017 } 2018 2019 #region VARIATION SELECTOR 2020 if (nextCharacter >= 0xFE00 && nextCharacter <= 0xFE0F || nextCharacter >= 0xE0100 && nextCharacter <= 0xE01EF) 2021 { 2022 // Get potential variant glyph index 2023 uint variantGlyphIndex = m_currentFontAsset.GetGlyphVariantIndex((uint)unicode, nextCharacter); 2024 2025 if (variantGlyphIndex != 0) 2026 { 2027 if (m_currentFontAsset.TryAddGlyphInternal(variantGlyphIndex, out Glyph glyph)) 2028 { 2029 m_textInfo.characterInfo[m_totalCharacterCount].alternativeGlyph = glyph; 2030 } 2031 } 2032 2033 textProcessingArray[i + 1].unicode = 0x1A; 2034 i += 1; 2035 } 2036 #endregion 2037 2038 #region LIGATURES 2039 if (ligature && m_currentFontAsset.fontFeatureTable.m_LigatureSubstitutionRecordLookup.TryGetValue(character.glyphIndex, out List<LigatureSubstitutionRecord> records)) 2040 { 2041 if (records == null) 2042 break; 2043 2044 for (int j = 0; j < records.Count; j++) 2045 { 2046 LigatureSubstitutionRecord record = records[j]; 2047 2048 int componentCount = record.componentGlyphIDs.Length; 2049 uint ligatureGlyphID = record.ligatureGlyphID; 2050 2051 // 2052 for (int k = 1; k < componentCount; k++) 2053 { 2054 uint componentUnicode = (uint)textProcessingArray[i + k].unicode; 2055 2056 // Special Handling for Zero Width Joiner (ZWJ) 2057 //if (componentUnicode == 0x200D) 2058 // continue; 2059 2060 uint glyphIndex = m_currentFontAsset.GetGlyphIndex(componentUnicode); 2061 2062 if (glyphIndex == record.componentGlyphIDs[k]) 2063 continue; 2064 2065 ligatureGlyphID = 0; 2066 break; 2067 } 2068 2069 if (ligatureGlyphID != 0) 2070 { 2071 if (m_currentFontAsset.TryAddGlyphInternal(ligatureGlyphID, out Glyph glyph)) 2072 { 2073 m_textInfo.characterInfo[m_totalCharacterCount].alternativeGlyph = glyph; 2074 2075 // Update text processing array 2076 for (int c = 0; c < componentCount; c++) 2077 { 2078 if (c == 0) 2079 { 2080 textProcessingArray[i + c].length = componentCount; 2081 continue; 2082 } 2083 2084 textProcessingArray[i + c].unicode = 0x1A; 2085 } 2086 2087 i += componentCount - 1; 2088 break; 2089 } 2090 } 2091 } 2092 } 2093 #endregion 2094 } 2095 #endregion 2096 2097 // Save text element data 2098 m_textInfo.characterInfo[m_totalCharacterCount].elementType = TMP_TextElementType.Character; 2099 m_textInfo.characterInfo[m_totalCharacterCount].textElement = character; 2100 m_textInfo.characterInfo[m_totalCharacterCount].isUsingAlternateTypeface = isUsingAlternativeTypeface; 2101 m_textInfo.characterInfo[m_totalCharacterCount].character = (char)unicode; 2102 m_textInfo.characterInfo[m_totalCharacterCount].index = textProcessingArray[i].stringIndex; 2103 m_textInfo.characterInfo[m_totalCharacterCount].stringLength = textProcessingArray[i].length; 2104 m_textInfo.characterInfo[m_totalCharacterCount].fontAsset = m_currentFontAsset; 2105 2106 // Special handling if the character is a sprite. 2107 if (character.elementType == TextElementType.Sprite) 2108 { 2109 TMP_SpriteAsset spriteAssetRef = character.textAsset as TMP_SpriteAsset; 2110 m_currentMaterialIndex = MaterialReference.AddMaterialReference(spriteAssetRef.material, spriteAssetRef, ref m_materialReferences, m_materialReferenceIndexLookup); 2111 m_materialReferences[m_currentMaterialIndex].referenceCount += 1; 2112 2113 m_textInfo.characterInfo[m_totalCharacterCount].elementType = TMP_TextElementType.Sprite; 2114 m_textInfo.characterInfo[m_totalCharacterCount].materialReferenceIndex = m_currentMaterialIndex; 2115 2116 // Restore element type and material index to previous values. 2117 m_textElementType = TMP_TextElementType.Character; 2118 m_currentMaterialIndex = prev_materialIndex; 2119 2120 spriteCount += 1; 2121 m_totalCharacterCount += 1; 2122 2123 continue; 2124 } 2125 2126 if (isUsingFallbackOrAlternativeTypeface && m_currentFontAsset.instanceID != m_fontAsset.instanceID) 2127 { 2128 // Create Fallback material instance matching current material preset if necessary 2129 if (TMP_Settings.matchMaterialPreset) 2130 m_currentMaterial = TMP_MaterialManager.GetFallbackMaterial(m_currentMaterial, m_currentFontAsset.material); 2131 else 2132 m_currentMaterial = m_currentFontAsset.material; 2133 2134 m_currentMaterialIndex = MaterialReference.AddMaterialReference(m_currentMaterial, m_currentFontAsset, ref m_materialReferences, m_materialReferenceIndexLookup); 2135 } 2136 2137 // Handle Multi Atlas Texture support 2138 if (character != null && character.glyph.atlasIndex > 0) 2139 { 2140 m_currentMaterial = TMP_MaterialManager.GetFallbackMaterial(m_currentFontAsset, m_currentMaterial, character.glyph.atlasIndex); 2141 2142 m_currentMaterialIndex = MaterialReference.AddMaterialReference(m_currentMaterial, m_currentFontAsset, ref m_materialReferences, m_materialReferenceIndexLookup); 2143 2144 isUsingFallbackOrAlternativeTypeface = true; 2145 } 2146 2147 if (!char.IsWhiteSpace((char)unicode) && unicode != 0x200B) 2148 { 2149 // Limit the mesh of the main text object to 65535 vertices and use sub objects for the overflow. 2150 if (m_materialReferences[m_currentMaterialIndex].referenceCount < 16383) 2151 m_materialReferences[m_currentMaterialIndex].referenceCount += 1; 2152 else 2153 { 2154 m_currentMaterialIndex = MaterialReference.AddMaterialReference(new Material(m_currentMaterial), m_currentFontAsset, ref m_materialReferences, m_materialReferenceIndexLookup); 2155 m_materialReferences[m_currentMaterialIndex].referenceCount += 1; 2156 } 2157 } 2158 2159 m_textInfo.characterInfo[m_totalCharacterCount].material = m_currentMaterial; 2160 m_textInfo.characterInfo[m_totalCharacterCount].materialReferenceIndex = m_currentMaterialIndex; 2161 m_materialReferences[m_currentMaterialIndex].isFallbackMaterial = isUsingFallbackOrAlternativeTypeface; 2162 2163 // Restore previous font asset and material if fallback font was used. 2164 if (isUsingFallbackOrAlternativeTypeface) 2165 { 2166 m_materialReferences[m_currentMaterialIndex].fallbackMaterial = prev_material; 2167 m_currentFontAsset = prev_fontAsset; 2168 m_currentMaterial = prev_material; 2169 m_currentMaterialIndex = prev_materialIndex; 2170 } 2171 2172 m_totalCharacterCount += 1; 2173 } 2174 2175 // Early return if we are calculating the preferred values. 2176 if (m_isCalculatingPreferredValues) 2177 { 2178 m_isCalculatingPreferredValues = false; 2179 2180 k_SetArraySizesMarker.End(); 2181 return m_totalCharacterCount; 2182 } 2183 2184 // Save material and sprite count. 2185 m_textInfo.spriteCount = spriteCount; 2186 int materialCount = m_textInfo.materialCount = m_materialReferenceIndexLookup.Count; 2187 2188 // Check if we need to resize the MeshInfo array for handling different materials. 2189 if (materialCount > m_textInfo.meshInfo.Length) 2190 TMP_TextInfo.Resize(ref m_textInfo.meshInfo, materialCount, false); 2191 2192 // Resize SubTextObject array if necessary 2193 if (materialCount > m_subTextObjects.Length) 2194 TMP_TextInfo.Resize(ref m_subTextObjects, Mathf.NextPowerOfTwo(materialCount + 1)); 2195 2196 // Resize CharacterInfo[] if allocations are excessive 2197 if (m_VertexBufferAutoSizeReduction && m_textInfo.characterInfo.Length - m_totalCharacterCount > 256) 2198 TMP_TextInfo.Resize(ref m_textInfo.characterInfo, Mathf.Max(m_totalCharacterCount + 1, 256), true); 2199 2200 2201 // Iterate through the material references to set the mesh buffer allocations 2202 for (int i = 0; i < materialCount; i++) 2203 { 2204 // Add new sub text object for each material reference 2205 if (i > 0) 2206 { 2207 if (m_subTextObjects[i] == null) 2208 { 2209 m_subTextObjects[i] = TMP_SubMeshUI.AddSubTextObject(this, m_materialReferences[i]); 2210 2211 // Not sure this is necessary 2212 m_textInfo.meshInfo[i].vertices = null; 2213 } 2214 //else if (m_subTextObjects[i].gameObject.activeInHierarchy == false) 2215 // m_subTextObjects[i].gameObject.SetActive(true); 2216 2217 // Make sure the pivots are synchronized 2218 if (m_rectTransform.pivot != m_subTextObjects[i].rectTransform.pivot) 2219 m_subTextObjects[i].rectTransform.pivot = m_rectTransform.pivot; 2220 2221 // Check if the material has changed. 2222 if (m_subTextObjects[i].sharedMaterial == null || m_subTextObjects[i].sharedMaterial.GetInstanceID() != m_materialReferences[i].material.GetInstanceID()) 2223 { 2224 m_subTextObjects[i].sharedMaterial = m_materialReferences[i].material; 2225 m_subTextObjects[i].fontAsset = m_materialReferences[i].fontAsset; 2226 m_subTextObjects[i].spriteAsset = m_materialReferences[i].spriteAsset; 2227 } 2228 2229 // Check if we need to use a Fallback Material 2230 if (m_materialReferences[i].isFallbackMaterial) 2231 { 2232 m_subTextObjects[i].fallbackMaterial = m_materialReferences[i].material; 2233 m_subTextObjects[i].fallbackSourceMaterial = m_materialReferences[i].fallbackMaterial; 2234 } 2235 } 2236 2237 int referenceCount = m_materialReferences[i].referenceCount; 2238 2239 // Check to make sure our buffers allocations can accommodate the required text elements. 2240 if (m_textInfo.meshInfo[i].vertices == null || m_textInfo.meshInfo[i].vertices.Length < referenceCount * 4) 2241 { 2242 if (m_textInfo.meshInfo[i].vertices == null) 2243 { 2244 if (i == 0) 2245 m_textInfo.meshInfo[i] = new TMP_MeshInfo(m_mesh, referenceCount + 1); 2246 else 2247 m_textInfo.meshInfo[i] = new TMP_MeshInfo(m_subTextObjects[i].mesh, referenceCount + 1); 2248 } 2249 else 2250 m_textInfo.meshInfo[i].ResizeMeshInfo(referenceCount > 1024 ? referenceCount + 256 : Mathf.NextPowerOfTwo(referenceCount + 1)); 2251 } 2252 else if (m_VertexBufferAutoSizeReduction && referenceCount > 0 && m_textInfo.meshInfo[i].vertices.Length / 4 - referenceCount > 256) 2253 { 2254 // Resize vertex buffers if allocations are excessive. 2255 //Debug.Log("Reducing the size of the vertex buffers."); 2256 m_textInfo.meshInfo[i].ResizeMeshInfo(referenceCount > 1024 ? referenceCount + 256 : Mathf.NextPowerOfTwo(referenceCount + 1)); 2257 } 2258 2259 // Assign material reference 2260 m_textInfo.meshInfo[i].material = m_materialReferences[i].material; 2261 } 2262 2263 //TMP_MaterialManager.CleanupFallbackMaterials(); 2264 2265 // Clean up unused SubMeshes 2266 for (int i = materialCount; i < m_subTextObjects.Length && m_subTextObjects[i] != null; i++) 2267 { 2268 if (i < m_textInfo.meshInfo.Length) 2269 { 2270 m_subTextObjects[i].canvasRenderer.SetMesh(null); 2271 2272 // TODO: Figure out a way to handle this without running into Unity's Rebuild loop issue. 2273 //m_subTextObjects[i].gameObject.SetActive(false); 2274 } 2275 } 2276 2277 k_SetArraySizesMarker.End(); 2278 return m_totalCharacterCount; 2279 } 2280 2281 2282 // Added to sort handle the potential issue with OnWillRenderObject() not getting called when objects are not visible by camera. 2283 //void OnBecameInvisible() 2284 //{ 2285 // if (m_mesh != null) 2286 // m_mesh.bounds = new Bounds(transform.position, new Vector3(1000, 1000, 0)); 2287 //} 2288 2289 2290 /// <summary> 2291 /// Update the margin width and height 2292 /// </summary> 2293 public override void ComputeMarginSize() 2294 { 2295 if (this.rectTransform != null) 2296 { 2297 //Debug.Log("*** ComputeMarginSize() *** Current RectTransform's Width is " + m_rectTransform.rect.width + " and Height is " + m_rectTransform.rect.height); // + " and size delta is " + m_rectTransform.sizeDelta); 2298 Rect rect = m_rectTransform.rect; 2299 2300 m_marginWidth = rect.width - m_margin.x - m_margin.z; 2301 m_marginHeight = rect.height - m_margin.y - m_margin.w; 2302 2303 // Cache current RectTransform width and pivot referenced in OnRectTransformDimensionsChange() to get around potential rounding error in the reported width of the RectTransform. 2304 m_PreviousRectTransformSize = rect.size; 2305 m_PreviousPivotPosition = m_rectTransform.pivot; 2306 2307 // Update the corners of the RectTransform 2308 m_RectTransformCorners = GetTextContainerLocalCorners(); 2309 } 2310 } 2311 2312 2313 /// <summary> 2314 /// 2315 /// </summary> 2316 protected override void OnDidApplyAnimationProperties() 2317 { 2318 m_havePropertiesChanged = true; 2319 SetVerticesDirty(); 2320 SetLayoutDirty(); 2321 //Debug.Log("Animation Properties have changed."); 2322 } 2323 2324 2325 protected override void OnCanvasHierarchyChanged() 2326 { 2327 base.OnCanvasHierarchyChanged(); 2328 2329 m_canvas = canvas; 2330 2331 if (!m_isAwake || !isActiveAndEnabled) 2332 return; 2333 2334 // Special handling to stop InternalUpdate calls when parent Canvas is disabled. 2335 if (m_canvas == null || m_canvas.enabled == false) 2336 TMP_UpdateManager.UnRegisterTextObjectForUpdate(this); 2337 else if (m_IsTextObjectScaleStatic == false) 2338 TMP_UpdateManager.RegisterTextObjectForUpdate(this); 2339 } 2340 2341 2342 protected override void OnTransformParentChanged() 2343 { 2344 //Debug.Log("***** OnTransformParentChanged *****"); 2345 2346 base.OnTransformParentChanged(); 2347 2348 m_canvas = this.canvas; 2349 2350 ComputeMarginSize(); 2351 m_havePropertiesChanged = true; 2352 } 2353 2354 2355 protected override void OnRectTransformDimensionsChange() 2356 { 2357 //Debug.Log("*** OnRectTransformDimensionsChange() *** ActiveInHierarchy: " + this.gameObject.activeInHierarchy + " Frame: " + Time.frameCount); 2358 2359 // Make sure object is active in Hierarchy 2360 if (!this.gameObject.activeInHierarchy) 2361 return; 2362 2363 // Check if Canvas scale factor has changed as this requires an update of the SDF Scale. 2364 bool hasCanvasScaleFactorChanged = false; 2365 if (m_canvas != null && m_CanvasScaleFactor != m_canvas.scaleFactor) 2366 { 2367 m_CanvasScaleFactor = m_canvas.scaleFactor; 2368 hasCanvasScaleFactorChanged = true; 2369 } 2370 2371 // Ignore changes to RectTransform SizeDelta that are very small and typically the result of rounding errors when using RectTransform in Anchor Stretch mode. 2372 if (hasCanvasScaleFactorChanged == false && 2373 rectTransform != null && 2374 Mathf.Abs(m_rectTransform.rect.width - m_PreviousRectTransformSize.x) < 0.0001f && Mathf.Abs(m_rectTransform.rect.height - m_PreviousRectTransformSize.y) < 0.0001f && 2375 Mathf.Abs(m_rectTransform.pivot.x - m_PreviousPivotPosition.x) < 0.0001f && Mathf.Abs(m_rectTransform.pivot.y - m_PreviousPivotPosition.y) < 0.0001f) 2376 { 2377 return; 2378 } 2379 2380 ComputeMarginSize(); 2381 2382 UpdateSubObjectPivot(); 2383 2384 SetVerticesDirty(); 2385 SetLayoutDirty(); 2386 } 2387 2388 2389 /// <summary> 2390 /// Function used as a replacement for LateUpdate to check if the transform or scale of the text object has changed. 2391 /// </summary> 2392 internal override void InternalUpdate() 2393 { 2394 // We need to update the SDF scale or possibly regenerate the text object if lossy scale has changed. 2395 if (m_havePropertiesChanged == false) 2396 { 2397 float lossyScaleY = m_rectTransform.lossyScale.y; 2398 2399 if (lossyScaleY != m_previousLossyScaleY && m_TextProcessingArray[0].unicode != 0) 2400 { 2401 float scaleDelta = lossyScaleY / m_previousLossyScaleY; 2402 2403 // Only update SDF Scale when lossy scale has changed by more than 20% 2404 if (scaleDelta < 0.8f || scaleDelta > 1.25f) 2405 { 2406 UpdateSDFScale(scaleDelta); 2407 m_previousLossyScaleY = lossyScaleY; 2408 } 2409 } 2410 } 2411 2412 // Added to handle legacy animation mode. 2413 if (m_isUsingLegacyAnimationComponent) 2414 { 2415 m_havePropertiesChanged = true; 2416 OnPreRenderCanvas(); 2417 } 2418 2419 // Update Environment Matrix property to support changing the rotation via a script. 2420 UpdateEnvMapMatrix(); 2421 } 2422 2423 2424 /// <summary> 2425 /// Function called when the text needs to be updated. 2426 /// </summary> 2427 void OnPreRenderCanvas() 2428 { 2429 //Debug.Log("*** OnPreRenderCanvas() *** Frame: " + Time.frameCount); 2430 2431 // Make sure object is active and that we have a valid Canvas. 2432 if (!m_isAwake || (this.IsActive() == false && m_ignoreActiveState == false)) 2433 return; 2434 2435 if (m_canvas == null) { m_canvas = this.canvas; if (m_canvas == null) return; } 2436 2437 2438 if (m_havePropertiesChanged || m_isLayoutDirty) 2439 { 2440 //Debug.Log("Properties have changed!"); // Assigned Material is:" + m_sharedMaterial); // New Text is: " + m_text + "."); 2441 2442 // Check if we have a font asset assigned. Return if we don't because no one likes to see purple squares on screen. 2443 if (m_fontAsset == null) 2444 { 2445 Debug.LogWarning("Please assign a Font Asset to this " + transform.name + " gameobject.", this); 2446 return; 2447 } 2448 2449 // Update mesh padding if necessary. 2450 if (checkPaddingRequired) 2451 UpdateMeshPadding(); 2452 2453 // Reparse the text as input may have changed or been truncated. 2454 ParseInputText(); 2455 TMP_FontAsset.UpdateFontAssetsInUpdateQueue(); 2456 2457 // Reset Font min / max used with Auto-sizing 2458 if (m_enableAutoSizing) 2459 m_fontSize = Mathf.Clamp(m_fontSizeBase, m_fontSizeMin, m_fontSizeMax); 2460 2461 m_maxFontSize = m_fontSizeMax; 2462 m_minFontSize = m_fontSizeMin; 2463 m_lineSpacingDelta = 0; 2464 m_charWidthAdjDelta = 0; 2465 2466 m_isTextTruncated = false; 2467 2468 m_havePropertiesChanged = false; 2469 m_isLayoutDirty = false; 2470 m_ignoreActiveState = false; 2471 2472 // Reset Text Auto Size iteration tracking. 2473 m_IsAutoSizePointSizeSet = false; 2474 m_AutoSizeIterationCount = 0; 2475 2476 // The GenerateTextMesh function is potentially called repeatedly when text auto size is enabled. 2477 // This is a revised implementation to remove the use of recursion which could potentially result in stack overflow issues. 2478 while (m_IsAutoSizePointSizeSet == false) 2479 { 2480 GenerateTextMesh(); 2481 m_AutoSizeIterationCount += 1; 2482 } 2483 } 2484 } 2485 2486 2487 /// <summary> 2488 /// This is the main function that is responsible for creating / displaying the text. 2489 /// </summary> 2490 protected virtual void GenerateTextMesh() 2491 { 2492 k_GenerateTextMarker.Begin(); 2493 2494 // Early exit if no font asset was assigned. This should not be needed since LiberationSans SDF will be assigned by default. 2495 if (m_fontAsset == null || m_fontAsset.characterLookupTable == null) 2496 { 2497 Debug.LogWarning("Can't Generate Mesh! No Font Asset has been assigned to Object ID: " + this.GetInstanceID()); 2498 m_IsAutoSizePointSizeSet = true; 2499 k_GenerateTextMarker.End(); 2500 return; 2501 } 2502 2503 // Clear TextInfo 2504 if (m_textInfo != null) 2505 m_textInfo.Clear(); 2506 2507 // Early exit if we don't have any Text to generate. 2508 if (m_TextProcessingArray == null || m_TextProcessingArray.Length == 0 || m_TextProcessingArray[0].unicode == 0) 2509 { 2510 // Clear mesh and upload changes to the mesh. 2511 ClearMesh(); 2512 2513 m_preferredWidth = 0; 2514 m_preferredHeight = 0; 2515 2516 // Event indicating the text has been regenerated. 2517 TMPro_EventManager.ON_TEXT_CHANGED(this); 2518 m_IsAutoSizePointSizeSet = true; 2519 k_GenerateTextMarker.End(); 2520 return; 2521 } 2522 2523 m_currentFontAsset = m_fontAsset; 2524 m_currentMaterial = m_sharedMaterial; 2525 m_currentMaterialIndex = 0; 2526 m_materialReferenceStack.SetDefault(new MaterialReference(m_currentMaterialIndex, m_currentFontAsset, null, m_currentMaterial, m_padding)); 2527 2528 m_currentSpriteAsset = m_spriteAsset; 2529 2530 // Stop all Sprite Animations 2531 if (m_spriteAnimator != null) 2532 m_spriteAnimator.StopAllAnimations(); 2533 2534 // Total character count is computed when the text is parsed. 2535 int totalCharacterCount = m_totalCharacterCount; 2536 2537 // Calculate the scale of the font based on selected font size and sampling point size. 2538 // baseScale is calculated using the font asset assigned to the text object. 2539 float baseScale = (m_fontSize / m_fontAsset.m_FaceInfo.pointSize * m_fontAsset.m_FaceInfo.scale * (m_isOrthographic ? 1 : 0.1f)); 2540 float currentElementScale = baseScale; 2541 float currentEmScale = m_fontSize * 0.01f * (m_isOrthographic ? 1 : 0.1f); 2542 m_fontScaleMultiplier = 1; 2543 2544 m_currentFontSize = m_fontSize; 2545 m_sizeStack.SetDefault(m_currentFontSize); 2546 float fontSizeDelta = 0; 2547 2548 uint charCode = 0; // Holds the character code of the currently being processed character. 2549 2550 m_FontStyleInternal = m_fontStyle; // Set the default style. 2551 m_FontWeightInternal = (m_FontStyleInternal & FontStyles.Bold) == FontStyles.Bold ? FontWeight.Bold : m_fontWeight; 2552 m_FontWeightStack.SetDefault(m_FontWeightInternal); 2553 m_fontStyleStack.Clear(); 2554 2555 m_lineJustification = m_HorizontalAlignment; // m_textAlignment; // Sets the line justification mode to match editor alignment. 2556 m_lineJustificationStack.SetDefault(m_lineJustification); 2557 2558 float padding = 0; 2559 2560 m_baselineOffset = 0; // Used by subscript characters. 2561 m_baselineOffsetStack.Clear(); 2562 2563 // Underline 2564 bool beginUnderline = false; 2565 Vector3 underline_start = Vector3.zero; // Used to track where underline starts & ends. 2566 Vector3 underline_end = Vector3.zero; 2567 2568 // Strike-through 2569 bool beginStrikethrough = false; 2570 Vector3 strikethrough_start = Vector3.zero; 2571 Vector3 strikethrough_end = Vector3.zero; 2572 2573 // Text Highlight 2574 bool beginHighlight = false; 2575 Vector3 highlight_start = Vector3.zero; 2576 Vector3 highlight_end = Vector3.zero; 2577 2578 m_fontColor32 = m_fontColor; 2579 m_htmlColor = m_fontColor32; 2580 m_underlineColor = m_htmlColor; 2581 m_strikethroughColor = m_htmlColor; 2582 2583 m_colorStack.SetDefault(m_htmlColor); 2584 m_underlineColorStack.SetDefault(m_htmlColor); 2585 m_strikethroughColorStack.SetDefault(m_htmlColor); 2586 m_HighlightStateStack.SetDefault(new HighlightState(m_htmlColor, TMP_Offset.zero)); 2587 2588 m_colorGradientPreset = null; 2589 m_colorGradientStack.SetDefault(null); 2590 2591 m_ItalicAngle = m_currentFontAsset.italicStyle; 2592 m_ItalicAngleStack.SetDefault(m_ItalicAngle); 2593 2594 // Clear the Style stack. 2595 //m_styleStack.Clear(); 2596 2597 // Clear the Action stack. 2598 m_actionStack.Clear(); 2599 2600 m_FXScale = Vector3.one; 2601 m_FXRotation = Quaternion.identity; 2602 2603 m_lineOffset = 0; // Amount of space between lines (font line spacing + m_linespacing). 2604 m_lineHeight = TMP_Math.FLOAT_UNSET; 2605 float lineGap = m_currentFontAsset.m_FaceInfo.lineHeight - (m_currentFontAsset.m_FaceInfo.ascentLine - m_currentFontAsset.m_FaceInfo.descentLine); 2606 2607 m_cSpacing = 0; // Amount of space added between characters as a result of the use of the <cspace> tag. 2608 m_monoSpacing = 0; 2609 m_xAdvance = 0; // Used to track the position of each character. 2610 2611 tag_LineIndent = 0; // Used for indentation of text. 2612 tag_Indent = 0; 2613 m_indentStack.SetDefault(0); 2614 tag_NoParsing = false; 2615 //m_isIgnoringAlignment = false; 2616 2617 m_characterCount = 0; // Total characters in the char[] 2618 2619 // Tracking of line information 2620 m_firstCharacterOfLine = m_firstVisibleCharacter; 2621 m_lastCharacterOfLine = 0; 2622 m_firstVisibleCharacterOfLine = 0; 2623 m_lastVisibleCharacterOfLine = 0; 2624 m_maxLineAscender = k_LargeNegativeFloat; 2625 m_maxLineDescender = k_LargePositiveFloat; 2626 m_lineNumber = 0; 2627 m_startOfLineAscender = 0; 2628 m_startOfLineDescender = 0; 2629 m_lineVisibleCharacterCount = 0; 2630 m_lineVisibleSpaceCount = 0; 2631 bool isStartOfNewLine = true; 2632 m_IsDrivenLineSpacing = false; 2633 m_firstOverflowCharacterIndex = -1; 2634 m_LastBaseGlyphIndex = int.MinValue; 2635 2636 bool kerning = m_ActiveFontFeatures.Contains(OTL_FeatureTag.kern); 2637 bool markToBase = m_ActiveFontFeatures.Contains(OTL_FeatureTag.mark); 2638 bool markToMark = m_ActiveFontFeatures.Contains(OTL_FeatureTag.mkmk); 2639 2640 m_pageNumber = 0; 2641 int pageToDisplay = Mathf.Clamp(m_pageToDisplay - 1, 0, m_textInfo.pageInfo.Length - 1); 2642 m_textInfo.ClearPageInfo(); 2643 2644 Vector4 margins = m_margin; 2645 float marginWidth = m_marginWidth > 0 ? m_marginWidth : 0; 2646 float marginHeight = m_marginHeight > 0 ? m_marginHeight : 0; 2647 m_marginLeft = 0; 2648 m_marginRight = 0; 2649 m_width = -1; 2650 float widthOfTextArea = marginWidth + 0.0001f - m_marginLeft - m_marginRight; 2651 2652 // Need to initialize these Extents structures 2653 m_meshExtents.min = k_LargePositiveVector2; 2654 m_meshExtents.max = k_LargeNegativeVector2; 2655 2656 // Initialize lineInfo 2657 m_textInfo.ClearLineInfo(); 2658 2659 // Tracking of the highest Ascender 2660 m_maxCapHeight = 0; 2661 m_maxTextAscender = 0; 2662 m_ElementDescender = 0; 2663 m_PageAscender = 0; 2664 float maxVisibleDescender = 0; 2665 bool isMaxVisibleDescenderSet = false; 2666 m_isNewPage = false; 2667 2668 // Initialize struct to track states of word wrapping 2669 bool isFirstWordOfLine = true; 2670 m_isNonBreakingSpace = false; 2671 bool ignoreNonBreakingSpace = false; 2672 int lastSoftLineBreak = 0; 2673 2674 CharacterSubstitution characterToSubstitute = new CharacterSubstitution(-1, 0); 2675 bool isSoftHyphenIgnored = false; 2676 2677 // Save character and line state before we begin layout. 2678 SaveWordWrappingState(ref m_SavedWordWrapState, -1, -1); 2679 SaveWordWrappingState(ref m_SavedLineState, -1, -1); 2680 SaveWordWrappingState(ref m_SavedEllipsisState, -1, -1); 2681 SaveWordWrappingState(ref m_SavedLastValidState, -1, -1); 2682 SaveWordWrappingState(ref m_SavedSoftLineBreakState, -1, -1); 2683 2684 m_EllipsisInsertionCandidateStack.Clear(); 2685 2686 // Safety Tracker 2687 int restoreCount = 0; 2688 2689 k_GenerateTextPhaseIMarker.Begin(); 2690 2691 // Parse through Character buffer to read HTML tags and begin creating mesh. 2692 for (int i = 0; i < m_TextProcessingArray.Length && m_TextProcessingArray[i].unicode != 0; i++) 2693 { 2694 charCode = m_TextProcessingArray[i].unicode; 2695 2696 if (restoreCount > 5) 2697 { 2698 Debug.LogError("Line breaking recursion max threshold hit... Character [" + charCode + "] index: " + i); 2699 characterToSubstitute.index = m_characterCount; 2700 characterToSubstitute.unicode = 0x03; 2701 } 2702 2703 // Skip characters that have been substituted. 2704 if (charCode == 0x1A) 2705 continue; 2706 2707 // Parse Rich Text Tag 2708 #region Parse Rich Text Tag 2709 if (m_isRichText && charCode == '<') 2710 { 2711 k_ParseMarkupTextMarker.Begin(); 2712 2713 m_isTextLayoutPhase = true; 2714 m_textElementType = TMP_TextElementType.Character; 2715 int endTagIndex; 2716 2717 // Check if Tag is valid. If valid, skip to the end of the validated tag. 2718 if (ValidateHtmlTag(m_TextProcessingArray, i + 1, out endTagIndex)) 2719 { 2720 i = endTagIndex; 2721 2722 // Continue to next character or handle the sprite element 2723 if (m_textElementType == TMP_TextElementType.Character) 2724 { 2725 k_ParseMarkupTextMarker.End(); 2726 continue; 2727 } 2728 } 2729 k_ParseMarkupTextMarker.End(); 2730 } 2731 else 2732 { 2733 m_textElementType = m_textInfo.characterInfo[m_characterCount].elementType; 2734 m_currentMaterialIndex = m_textInfo.characterInfo[m_characterCount].materialReferenceIndex; 2735 m_currentFontAsset = m_textInfo.characterInfo[m_characterCount].fontAsset; 2736 } 2737 #endregion End Parse Rich Text Tag 2738 2739 int previousMaterialIndex = m_currentMaterialIndex; 2740 bool isUsingAltTypeface = m_textInfo.characterInfo[m_characterCount].isUsingAlternateTypeface; 2741 2742 m_isTextLayoutPhase = false; 2743 2744 // Handle potential character substitutions 2745 #region Character Substitutions 2746 bool isInjectedCharacter = false; 2747 2748 if (characterToSubstitute.index == m_characterCount) 2749 { 2750 charCode = characterToSubstitute.unicode; 2751 m_textElementType = TMP_TextElementType.Character; 2752 isInjectedCharacter = true; 2753 2754 switch (charCode) 2755 { 2756 case 0x03: 2757 m_textInfo.characterInfo[m_characterCount].textElement = m_currentFontAsset.characterLookupTable[0x03]; 2758 m_isTextTruncated = true; 2759 break; 2760 case 0x2D: 2761 // 2762 break; 2763 case 0x2026: 2764 m_textInfo.characterInfo[m_characterCount].textElement = m_Ellipsis.character; 2765 m_textInfo.characterInfo[m_characterCount].elementType = TMP_TextElementType.Character; 2766 m_textInfo.characterInfo[m_characterCount].fontAsset = m_Ellipsis.fontAsset; 2767 m_textInfo.characterInfo[m_characterCount].material = m_Ellipsis.material; 2768 m_textInfo.characterInfo[m_characterCount].materialReferenceIndex = m_Ellipsis.materialIndex; 2769 2770 // Need to increase reference count in the event the primary mesh has no characters. 2771 m_materialReferences[m_Underline.materialIndex].referenceCount += 1; 2772 2773 // Indicates the source parsing data has been modified. 2774 m_isTextTruncated = true; 2775 2776 // End Of Text 2777 characterToSubstitute.index = m_characterCount + 1; 2778 characterToSubstitute.unicode = 0x03; 2779 break; 2780 } 2781 } 2782 #endregion 2783 2784 2785 // When using Linked text, mark character as ignored and skip to next character. 2786 #region Linked Text 2787 if (m_characterCount < m_firstVisibleCharacter && charCode != 0x03) 2788 { 2789 m_textInfo.characterInfo[m_characterCount].isVisible = false; 2790 m_textInfo.characterInfo[m_characterCount].character = (char)0x200B; 2791 m_textInfo.characterInfo[m_characterCount].lineNumber = 0; 2792 m_characterCount += 1; 2793 continue; 2794 } 2795 #endregion 2796 2797 2798 // Handle Font Styles like LowerCase, UpperCase and SmallCaps. 2799 #region Handling of LowerCase, UpperCase and SmallCaps Font Styles 2800 2801 float smallCapsMultiplier = 1.0f; 2802 2803 if (m_textElementType == TMP_TextElementType.Character) 2804 { 2805 if ((m_FontStyleInternal & FontStyles.UpperCase) == FontStyles.UpperCase) 2806 { 2807 // If this character is lowercase, switch to uppercase. 2808 if (char.IsLower((char)charCode)) 2809 charCode = char.ToUpper((char)charCode); 2810 2811 } 2812 else if ((m_FontStyleInternal & FontStyles.LowerCase) == FontStyles.LowerCase) 2813 { 2814 // If this character is uppercase, switch to lowercase. 2815 if (char.IsUpper((char)charCode)) 2816 charCode = char.ToLower((char)charCode); 2817 } 2818 else if ((m_FontStyleInternal & FontStyles.SmallCaps) == FontStyles.SmallCaps) 2819 { 2820 if (char.IsLower((char)charCode)) 2821 { 2822 smallCapsMultiplier = 0.8f; 2823 charCode = char.ToUpper((char)charCode); 2824 } 2825 } 2826 } 2827 #endregion 2828 2829 2830 // Look up Character Data from Dictionary and cache it. 2831 #region Look up Character Data 2832 k_CharacterLookupMarker.Begin(); 2833 2834 float baselineOffset = 0; 2835 float elementAscentLine = 0; 2836 float elementDescentLine = 0; 2837 if (m_textElementType == TMP_TextElementType.Sprite) 2838 { 2839 // If a sprite is used as a fallback then get a reference to it and set the color to white. 2840 TMP_SpriteCharacter sprite = (TMP_SpriteCharacter)textInfo.characterInfo[m_characterCount].textElement; 2841 m_currentSpriteAsset = sprite.textAsset as TMP_SpriteAsset; 2842 m_spriteIndex = (int)sprite.glyphIndex; 2843 2844 if (sprite == null) 2845 { 2846 k_CharacterLookupMarker.End(); 2847 continue; 2848 } 2849 2850 // Sprites are assigned in the E000 Private Area + sprite Index 2851 if (charCode == '<') 2852 charCode = 57344 + (uint)m_spriteIndex; 2853 else 2854 m_spriteColor = s_colorWhite; 2855 2856 float fontScale = (m_currentFontSize / m_currentFontAsset.faceInfo.pointSize * m_currentFontAsset.faceInfo.scale * (m_isOrthographic ? 1 : 0.1f)); 2857 2858 // The sprite scale calculations are based on the font asset assigned to the text object. 2859 if (m_currentSpriteAsset.m_FaceInfo.pointSize > 0) 2860 { 2861 float spriteScale = m_currentFontSize / m_currentSpriteAsset.m_FaceInfo.pointSize * m_currentSpriteAsset.m_FaceInfo.scale * (m_isOrthographic ? 1 : 0.1f); 2862 currentElementScale = sprite.m_Scale * sprite.m_Glyph.scale * spriteScale; 2863 elementAscentLine = m_currentSpriteAsset.m_FaceInfo.ascentLine; 2864 baselineOffset = m_currentSpriteAsset.m_FaceInfo.baseline * fontScale * m_fontScaleMultiplier * m_currentSpriteAsset.m_FaceInfo.scale; 2865 elementDescentLine = m_currentSpriteAsset.m_FaceInfo.descentLine; 2866 } 2867 else 2868 { 2869 float spriteScale = m_currentFontSize / m_currentFontAsset.m_FaceInfo.pointSize * m_currentFontAsset.m_FaceInfo.scale * (m_isOrthographic ? 1 : 0.1f); 2870 currentElementScale = m_currentFontAsset.m_FaceInfo.ascentLine / sprite.m_Glyph.metrics.height * sprite.m_Scale * sprite.m_Glyph.scale * spriteScale; 2871 float scaleDelta = spriteScale / currentElementScale; 2872 elementAscentLine = m_currentFontAsset.m_FaceInfo.ascentLine * scaleDelta; 2873 baselineOffset = m_currentFontAsset.m_FaceInfo.baseline * fontScale * m_fontScaleMultiplier * m_currentFontAsset.m_FaceInfo.scale; 2874 elementDescentLine = m_currentFontAsset.m_FaceInfo.descentLine * scaleDelta; 2875 } 2876 2877 m_cached_TextElement = sprite; 2878 2879 m_textInfo.characterInfo[m_characterCount].elementType = TMP_TextElementType.Sprite; 2880 m_textInfo.characterInfo[m_characterCount].scale = currentElementScale; 2881 m_textInfo.characterInfo[m_characterCount].fontAsset = m_currentFontAsset; 2882 m_textInfo.characterInfo[m_characterCount].materialReferenceIndex = m_currentMaterialIndex; 2883 2884 m_currentMaterialIndex = previousMaterialIndex; 2885 2886 padding = 0; 2887 } 2888 else if (m_textElementType == TMP_TextElementType.Character) 2889 { 2890 m_cached_TextElement = m_textInfo.characterInfo[m_characterCount].textElement; 2891 if (m_cached_TextElement == null) 2892 { 2893 k_CharacterLookupMarker.End(); 2894 continue; 2895 } 2896 2897 m_currentFontAsset = m_textInfo.characterInfo[m_characterCount].fontAsset; 2898 m_currentMaterial = m_textInfo.characterInfo[m_characterCount].material; 2899 m_currentMaterialIndex = m_textInfo.characterInfo[m_characterCount].materialReferenceIndex; 2900 2901 // Special handling if replaced character was a line feed where in this case we have to use the scale of the previous character. 2902 float adjustedScale; 2903 if (isInjectedCharacter && m_TextProcessingArray[i].unicode == 0x0A && m_characterCount != m_firstCharacterOfLine) 2904 adjustedScale = m_textInfo.characterInfo[m_characterCount - 1].pointSize * smallCapsMultiplier / m_currentFontAsset.m_FaceInfo.pointSize * m_currentFontAsset.m_FaceInfo.scale * (m_isOrthographic ? 1 : 0.1f); 2905 else 2906 adjustedScale = m_currentFontSize * smallCapsMultiplier / m_currentFontAsset.m_FaceInfo.pointSize * m_currentFontAsset.m_FaceInfo.scale * (m_isOrthographic ? 1 : 0.1f); 2907 2908 // Special handling for injected Ellipsis 2909 if (isInjectedCharacter && charCode == 0x2026) 2910 { 2911 elementAscentLine = 0; 2912 elementDescentLine = 0; 2913 } 2914 else 2915 { 2916 elementAscentLine = m_currentFontAsset.m_FaceInfo.ascentLine; 2917 elementDescentLine = m_currentFontAsset.m_FaceInfo.descentLine; 2918 } 2919 2920 currentElementScale = adjustedScale * m_fontScaleMultiplier * m_cached_TextElement.m_Scale * m_cached_TextElement.m_Glyph.scale; 2921 baselineOffset = m_currentFontAsset.m_FaceInfo.baseline * adjustedScale * m_fontScaleMultiplier * m_currentFontAsset.m_FaceInfo.scale; 2922 2923 m_textInfo.characterInfo[m_characterCount].elementType = TMP_TextElementType.Character; 2924 m_textInfo.characterInfo[m_characterCount].scale = currentElementScale; 2925 2926 padding = m_currentMaterialIndex == 0 ? m_padding : m_subTextObjects[m_currentMaterialIndex].padding; 2927 } 2928 k_CharacterLookupMarker.End(); 2929 #endregion 2930 2931 2932 // Handle Soft Hyphen 2933 #region Handle Soft Hyphen 2934 float currentElementUnmodifiedScale = currentElementScale; 2935 if (charCode == 0xAD || charCode == 0x03) 2936 currentElementScale = 0; 2937 #endregion 2938 2939 2940 // Store some of the text object's information 2941 m_textInfo.characterInfo[m_characterCount].character = (char)charCode; 2942 m_textInfo.characterInfo[m_characterCount].pointSize = m_currentFontSize; 2943 m_textInfo.characterInfo[m_characterCount].color = m_htmlColor; 2944 m_textInfo.characterInfo[m_characterCount].underlineColor = m_underlineColor; 2945 m_textInfo.characterInfo[m_characterCount].strikethroughColor = m_strikethroughColor; 2946 m_textInfo.characterInfo[m_characterCount].highlightState = m_HighlightState; 2947 m_textInfo.characterInfo[m_characterCount].style = m_FontStyleInternal; 2948 2949 // Cache glyph metrics 2950 Glyph altGlyph = m_textInfo.characterInfo[m_characterCount].alternativeGlyph; 2951 GlyphMetrics currentGlyphMetrics = altGlyph == null ? m_cached_TextElement.m_Glyph.metrics : altGlyph.metrics; 2952 2953 // Optimization to avoid calling this more than once per character. 2954 bool isWhiteSpace = charCode <= 0xFFFF && char.IsWhiteSpace((char)charCode); 2955 2956 // Handle Kerning if Enabled. 2957 #region Handle Kerning 2958 GlyphValueRecord glyphAdjustments = new GlyphValueRecord(); 2959 float characterSpacingAdjustment = m_characterSpacing; 2960 if (kerning && m_textElementType == TMP_TextElementType.Character) 2961 { 2962 k_HandleGPOSFeaturesMarker.Begin(); 2963 2964 GlyphPairAdjustmentRecord adjustmentPair; 2965 uint baseGlyphIndex = m_cached_TextElement.m_GlyphIndex; 2966 2967 if (m_characterCount < totalCharacterCount - 1 && textInfo.characterInfo[m_characterCount + 1].elementType == TMP_TextElementType.Character) 2968 { 2969 uint nextGlyphIndex = m_textInfo.characterInfo[m_characterCount + 1].textElement.m_GlyphIndex; 2970 uint key = nextGlyphIndex << 16 | baseGlyphIndex; 2971 2972 if (m_currentFontAsset.m_FontFeatureTable.m_GlyphPairAdjustmentRecordLookup.TryGetValue(key, out adjustmentPair)) 2973 { 2974 glyphAdjustments = adjustmentPair.firstAdjustmentRecord.glyphValueRecord; 2975 characterSpacingAdjustment = (adjustmentPair.featureLookupFlags & UnityEngine.TextCore.LowLevel.FontFeatureLookupFlags.IgnoreSpacingAdjustments) == UnityEngine.TextCore.LowLevel.FontFeatureLookupFlags.IgnoreSpacingAdjustments ? 0 : characterSpacingAdjustment; 2976 } 2977 } 2978 2979 if (m_characterCount >= 1) 2980 { 2981 uint previousGlyphIndex = m_textInfo.characterInfo[m_characterCount - 1].textElement.m_GlyphIndex; 2982 uint key = baseGlyphIndex << 16 | previousGlyphIndex; 2983 2984 if (textInfo.characterInfo[m_characterCount - 1].elementType == TMP_TextElementType.Character && m_currentFontAsset.m_FontFeatureTable.m_GlyphPairAdjustmentRecordLookup.TryGetValue(key, out adjustmentPair)) 2985 { 2986 glyphAdjustments += adjustmentPair.secondAdjustmentRecord.glyphValueRecord; 2987 characterSpacingAdjustment = (adjustmentPair.featureLookupFlags & UnityEngine.TextCore.LowLevel.FontFeatureLookupFlags.IgnoreSpacingAdjustments) == UnityEngine.TextCore.LowLevel.FontFeatureLookupFlags.IgnoreSpacingAdjustments ? 0 : characterSpacingAdjustment; 2988 } 2989 } 2990 2991 k_HandleGPOSFeaturesMarker.End(); 2992 } 2993 2994 m_textInfo.characterInfo[m_characterCount].adjustedHorizontalAdvance = glyphAdjustments.xAdvance; 2995 #endregion 2996 2997 2998 // Handle Diacritical Marks 2999 #region Handle Diacritical Marks 3000 bool isBaseGlyph = TMP_TextParsingUtilities.IsBaseGlyph(charCode); 3001 3002 if (isBaseGlyph) 3003 m_LastBaseGlyphIndex = m_characterCount; 3004 3005 if (m_characterCount > 0 && !isBaseGlyph) 3006 { 3007 // Check for potential Mark-to-Base lookup if previous glyph was a base glyph 3008 if (markToBase && m_LastBaseGlyphIndex != int.MinValue && m_LastBaseGlyphIndex == m_characterCount - 1) 3009 { 3010 Glyph baseGlyph = m_textInfo.characterInfo[m_LastBaseGlyphIndex].textElement.glyph; 3011 uint baseGlyphIndex = baseGlyph.index; 3012 uint markGlyphIndex = m_cached_TextElement.glyphIndex; 3013 uint key = markGlyphIndex << 16 | baseGlyphIndex; 3014 3015 if (m_currentFontAsset.fontFeatureTable.m_MarkToBaseAdjustmentRecordLookup.TryGetValue(key, out MarkToBaseAdjustmentRecord glyphAdjustmentRecord)) 3016 { 3017 float advanceOffset = (m_textInfo.characterInfo[m_LastBaseGlyphIndex].origin - m_xAdvance) / currentElementScale; 3018 3019 glyphAdjustments.xPlacement = advanceOffset + glyphAdjustmentRecord.baseGlyphAnchorPoint.xCoordinate - glyphAdjustmentRecord.markPositionAdjustment.xPositionAdjustment; 3020 glyphAdjustments.yPlacement = glyphAdjustmentRecord.baseGlyphAnchorPoint.yCoordinate - glyphAdjustmentRecord.markPositionAdjustment.yPositionAdjustment; 3021 3022 characterSpacingAdjustment = 0; 3023 } 3024 } 3025 else 3026 { 3027 // Iterate from previous glyph to last base glyph checking for any potential Mark-to-Mark lookups to apply. Otherwise check for potential Mark-to-Base lookup between the current glyph and last base glyph 3028 bool wasLookupApplied = false; 3029 3030 // Check for any potential Mark-to-Mark lookups 3031 if (markToMark) 3032 { 3033 for (int characterLookupIndex = m_characterCount - 1; characterLookupIndex >= 0 && characterLookupIndex != m_LastBaseGlyphIndex; characterLookupIndex--) 3034 { 3035 // Handle any potential Mark-to-Mark lookup 3036 Glyph baseMarkGlyph = m_textInfo.characterInfo[characterLookupIndex].textElement.glyph; 3037 uint baseGlyphIndex = baseMarkGlyph.index; 3038 uint combiningMarkGlyphIndex = m_cached_TextElement.glyphIndex; 3039 uint key = combiningMarkGlyphIndex << 16 | baseGlyphIndex; 3040 3041 if (m_currentFontAsset.fontFeatureTable.m_MarkToMarkAdjustmentRecordLookup.TryGetValue(key, out MarkToMarkAdjustmentRecord glyphAdjustmentRecord)) 3042 { 3043 float baseMarkOrigin = (m_textInfo.characterInfo[characterLookupIndex].origin - m_xAdvance) / currentElementScale; 3044 float currentBaseline = baselineOffset - m_lineOffset + m_baselineOffset; 3045 float baseMarkBaseline = (m_textInfo.characterInfo[characterLookupIndex].baseLine - currentBaseline) / currentElementScale; 3046 3047 glyphAdjustments.xPlacement = baseMarkOrigin + glyphAdjustmentRecord.baseMarkGlyphAnchorPoint.xCoordinate - glyphAdjustmentRecord.combiningMarkPositionAdjustment.xPositionAdjustment; 3048 glyphAdjustments.yPlacement = baseMarkBaseline + glyphAdjustmentRecord.baseMarkGlyphAnchorPoint.yCoordinate - glyphAdjustmentRecord.combiningMarkPositionAdjustment.yPositionAdjustment; 3049 3050 characterSpacingAdjustment = 0; 3051 wasLookupApplied = true; 3052 break; 3053 } 3054 } 3055 } 3056 3057 // If no Mark-to-Mark lookups were applied, check for potential Mark-to-Base lookup. 3058 if (markToBase && m_LastBaseGlyphIndex != int.MinValue && !wasLookupApplied) 3059 { 3060 // Handle lookup for Mark-to-Base 3061 Glyph baseGlyph = m_textInfo.characterInfo[m_LastBaseGlyphIndex].textElement.glyph; 3062 uint baseGlyphIndex = baseGlyph.index; 3063 uint markGlyphIndex = m_cached_TextElement.glyphIndex; 3064 uint key = markGlyphIndex << 16 | baseGlyphIndex; 3065 3066 if (m_currentFontAsset.fontFeatureTable.m_MarkToBaseAdjustmentRecordLookup.TryGetValue(key, out MarkToBaseAdjustmentRecord glyphAdjustmentRecord)) 3067 { 3068 float advanceOffset = (m_textInfo.characterInfo[m_LastBaseGlyphIndex].origin - m_xAdvance) / currentElementScale; 3069 3070 glyphAdjustments.xPlacement = advanceOffset + glyphAdjustmentRecord.baseGlyphAnchorPoint.xCoordinate - glyphAdjustmentRecord.markPositionAdjustment.xPositionAdjustment; 3071 glyphAdjustments.yPlacement = glyphAdjustmentRecord.baseGlyphAnchorPoint.yCoordinate - glyphAdjustmentRecord.markPositionAdjustment.yPositionAdjustment; 3072 3073 characterSpacingAdjustment = 0; 3074 } 3075 } 3076 } 3077 } 3078 3079 // Adjust relevant text metrics 3080 elementAscentLine += glyphAdjustments.yPlacement; 3081 elementDescentLine += glyphAdjustments.yPlacement; 3082 #endregion 3083 3084 3085 // Initial Implementation for RTL support. 3086 #region Handle Right-to-Left 3087 if (m_isRightToLeft) 3088 { 3089 m_xAdvance -= currentGlyphMetrics.horizontalAdvance * (1 - m_charWidthAdjDelta) * currentElementScale; 3090 3091 if (isWhiteSpace || charCode == 0x200B) 3092 m_xAdvance -= m_wordSpacing * currentEmScale; 3093 } 3094 #endregion 3095 3096 3097 // Handle Mono Spacing 3098 #region Handle Mono Spacing 3099 float monoAdvance = 0; 3100 if (m_monoSpacing != 0) 3101 { 3102 if (m_duoSpace && (charCode == '.' || charCode == ':' || charCode == ',')) 3103 monoAdvance = (m_monoSpacing / 4 - (currentGlyphMetrics.width / 2 + currentGlyphMetrics.horizontalBearingX) * currentElementScale) * (1 - m_charWidthAdjDelta); 3104 else 3105 monoAdvance = (m_monoSpacing / 2 - (currentGlyphMetrics.width / 2 + currentGlyphMetrics.horizontalBearingX) * currentElementScale) * (1 - m_charWidthAdjDelta); 3106 3107 m_xAdvance += monoAdvance; 3108 } 3109 #endregion 3110 3111 3112 // Set Padding based on selected font style 3113 #region Handle Style Padding 3114 float boldSpacingAdjustment; 3115 float style_padding; 3116 if (m_textElementType == TMP_TextElementType.Character && !isUsingAltTypeface && ((m_FontStyleInternal & FontStyles.Bold) == FontStyles.Bold)) // Checks for any combination of Bold Style. 3117 { 3118 if (m_currentMaterial != null && m_currentMaterial.HasProperty(ShaderUtilities.ID_GradientScale)) 3119 { 3120 float gradientScale = m_currentMaterial.GetFloat(ShaderUtilities.ID_GradientScale); 3121 style_padding = m_currentFontAsset.boldStyle / 4.0f * gradientScale * m_currentMaterial.GetFloat(ShaderUtilities.ID_ScaleRatio_A); 3122 3123 // Clamp overall padding to Gradient Scale size. 3124 if (style_padding + padding > gradientScale) 3125 padding = gradientScale - style_padding; 3126 } 3127 else 3128 style_padding = 0; 3129 3130 boldSpacingAdjustment = m_currentFontAsset.boldSpacing; 3131 } 3132 else 3133 { 3134 if (m_currentMaterial != null && m_currentMaterial.HasProperty(ShaderUtilities.ID_GradientScale) && m_currentMaterial.HasProperty(ShaderUtilities.ID_ScaleRatio_A)) 3135 { 3136 float gradientScale = m_currentMaterial.GetFloat(ShaderUtilities.ID_GradientScale); 3137 style_padding = m_currentFontAsset.normalStyle / 4.0f * gradientScale * m_currentMaterial.GetFloat(ShaderUtilities.ID_ScaleRatio_A); 3138 3139 // Clamp overall padding to Gradient Scale size. 3140 if (style_padding + padding > gradientScale) 3141 padding = gradientScale - style_padding; 3142 } 3143 else 3144 style_padding = 0; 3145 3146 boldSpacingAdjustment = 0; 3147 } 3148 #endregion Handle Style Padding 3149 3150 3151 // Determine the position of the vertices of the Character or Sprite. 3152 #region Calculate Vertices Position 3153 k_CalculateVerticesPositionMarker.Begin(); 3154 Vector3 top_left; 3155 top_left.x = m_xAdvance + ((currentGlyphMetrics.horizontalBearingX * m_FXScale.x - padding - style_padding + glyphAdjustments.xPlacement) * currentElementScale * (1 - m_charWidthAdjDelta)); 3156 top_left.y = baselineOffset + (currentGlyphMetrics.horizontalBearingY + padding + glyphAdjustments.yPlacement) * currentElementScale - m_lineOffset + m_baselineOffset; 3157 top_left.z = 0; 3158 3159 Vector3 bottom_left; 3160 bottom_left.x = top_left.x; 3161 bottom_left.y = top_left.y - ((currentGlyphMetrics.height + padding * 2) * currentElementScale); 3162 bottom_left.z = 0; 3163 3164 Vector3 top_right; 3165 top_right.x = bottom_left.x + ((currentGlyphMetrics.width * m_FXScale.x + padding * 2 + style_padding * 2) * currentElementScale * (1 - m_charWidthAdjDelta)); 3166 top_right.y = top_left.y; 3167 top_right.z = 0; 3168 3169 Vector3 bottom_right; 3170 bottom_right.x = top_right.x; 3171 bottom_right.y = bottom_left.y; 3172 bottom_right.z = 0; 3173 3174 k_CalculateVerticesPositionMarker.End(); 3175 #endregion 3176 3177 3178 // Check if we need to Shear the rectangles for Italic styles 3179 #region Handle Italic & Shearing 3180 if (m_textElementType == TMP_TextElementType.Character && !isUsingAltTypeface && ((m_FontStyleInternal & FontStyles.Italic) == FontStyles.Italic)) 3181 { 3182 // Shift Top vertices forward by half (Shear Value * height of character) and Bottom vertices back by same amount. 3183 float shear_value = m_ItalicAngle * 0.01f; 3184 float midPoint = ((m_currentFontAsset.m_FaceInfo.capLine - (m_currentFontAsset.m_FaceInfo.baseline + m_baselineOffset)) / 2) * m_fontScaleMultiplier * m_currentFontAsset.m_FaceInfo.scale; 3185 Vector3 topShear = new Vector3(shear_value * ((currentGlyphMetrics.horizontalBearingY + padding + style_padding - midPoint) * currentElementScale), 0, 0); 3186 Vector3 bottomShear = new Vector3(shear_value * (((currentGlyphMetrics.horizontalBearingY - currentGlyphMetrics.height - padding - style_padding - midPoint)) * currentElementScale), 0, 0); 3187 3188 top_left += topShear; 3189 bottom_left += bottomShear; 3190 top_right += topShear; 3191 bottom_right += bottomShear; 3192 } 3193 #endregion Handle Italics & Shearing 3194 3195 3196 // Handle Character FX Rotation 3197 #region Handle Character FX Rotation 3198 if (m_FXRotation != Quaternion.identity) 3199 { 3200 Matrix4x4 rotationMatrix = Matrix4x4.Rotate(m_FXRotation); 3201 Vector3 positionOffset = (top_right + bottom_left) / 2; 3202 3203 top_left = rotationMatrix.MultiplyPoint3x4(top_left - positionOffset) + positionOffset; 3204 bottom_left = rotationMatrix.MultiplyPoint3x4(bottom_left - positionOffset) + positionOffset; 3205 top_right = rotationMatrix.MultiplyPoint3x4(top_right - positionOffset) + positionOffset; 3206 bottom_right = rotationMatrix.MultiplyPoint3x4(bottom_right - positionOffset) + positionOffset; 3207 } 3208 #endregion 3209 3210 3211 // Store vertex information for the character or sprite. 3212 m_textInfo.characterInfo[m_characterCount].bottomLeft = bottom_left; 3213 m_textInfo.characterInfo[m_characterCount].topLeft = top_left; 3214 m_textInfo.characterInfo[m_characterCount].topRight = top_right; 3215 m_textInfo.characterInfo[m_characterCount].bottomRight = bottom_right; 3216 3217 m_textInfo.characterInfo[m_characterCount].origin = m_xAdvance + glyphAdjustments.xPlacement * currentElementScale; 3218 m_textInfo.characterInfo[m_characterCount].baseLine = (baselineOffset - m_lineOffset + m_baselineOffset) + glyphAdjustments.yPlacement * currentElementScale; 3219 m_textInfo.characterInfo[m_characterCount].aspectRatio = (top_right.x - bottom_left.x) / (top_left.y - bottom_left.y); 3220 3221 3222 // Compute text metrics 3223 #region Compute Ascender & Descender values 3224 k_ComputeTextMetricsMarker.Begin(); 3225 // Element Ascender in line space 3226 float elementAscender = m_textElementType == TMP_TextElementType.Character 3227 ? elementAscentLine * currentElementScale / smallCapsMultiplier + m_baselineOffset 3228 : elementAscentLine * currentElementScale + m_baselineOffset; 3229 3230 // Element Descender in line space 3231 float elementDescender = m_textElementType == TMP_TextElementType.Character 3232 ? elementDescentLine * currentElementScale / smallCapsMultiplier + m_baselineOffset 3233 : elementDescentLine * currentElementScale + m_baselineOffset; 3234 3235 float adjustedAscender = elementAscender; 3236 float adjustedDescender = elementDescender; 3237 3238 // Max line ascender and descender in line space 3239 bool isFirstCharacterOfLine = m_characterCount == m_firstCharacterOfLine; 3240 if (isFirstCharacterOfLine || isWhiteSpace == false) 3241 { 3242 // Special handling for Superscript and Subscript where we use the unadjusted line ascender and descender 3243 if (m_baselineOffset != 0) 3244 { 3245 adjustedAscender = Mathf.Max((elementAscender - m_baselineOffset) / m_fontScaleMultiplier, adjustedAscender); 3246 adjustedDescender = Mathf.Min((elementDescender - m_baselineOffset) / m_fontScaleMultiplier, adjustedDescender); 3247 } 3248 3249 m_maxLineAscender = Mathf.Max(adjustedAscender, m_maxLineAscender); 3250 m_maxLineDescender = Mathf.Min(adjustedDescender, m_maxLineDescender); 3251 } 3252 3253 // Element Ascender and Descender in object space 3254 if (isFirstCharacterOfLine || isWhiteSpace == false) 3255 { 3256 m_textInfo.characterInfo[m_characterCount].adjustedAscender = adjustedAscender; 3257 m_textInfo.characterInfo[m_characterCount].adjustedDescender = adjustedDescender; 3258 3259 m_ElementAscender = m_textInfo.characterInfo[m_characterCount].ascender = elementAscender - m_lineOffset; 3260 m_ElementDescender = m_textInfo.characterInfo[m_characterCount].descender = elementDescender - m_lineOffset; 3261 } 3262 else 3263 { 3264 m_textInfo.characterInfo[m_characterCount].adjustedAscender = m_maxLineAscender; 3265 m_textInfo.characterInfo[m_characterCount].adjustedDescender = m_maxLineDescender; 3266 3267 m_ElementAscender = m_textInfo.characterInfo[m_characterCount].ascender = m_maxLineAscender - m_lineOffset; 3268 m_ElementDescender = m_textInfo.characterInfo[m_characterCount].descender = m_maxLineDescender - m_lineOffset; 3269 } 3270 3271 // Max text object ascender and cap height 3272 if (m_lineNumber == 0 || m_isNewPage) 3273 { 3274 if (isFirstCharacterOfLine || isWhiteSpace == false) 3275 { 3276 m_maxTextAscender = m_maxLineAscender; 3277 m_maxCapHeight = Mathf.Max(m_maxCapHeight, m_currentFontAsset.m_FaceInfo.capLine * currentElementScale / smallCapsMultiplier); 3278 } 3279 } 3280 3281 // Page ascender 3282 if (m_lineOffset == 0) 3283 { 3284 if (isFirstCharacterOfLine || isWhiteSpace == false) 3285 m_PageAscender = m_PageAscender > elementAscender ? m_PageAscender : elementAscender; 3286 } 3287 k_ComputeTextMetricsMarker.End(); 3288 #endregion 3289 3290 3291 // Set Characters to not visible by default. 3292 m_textInfo.characterInfo[m_characterCount].isVisible = false; 3293 3294 bool isJustifiedOrFlush = (m_lineJustification & HorizontalAlignmentOptions.Flush) == HorizontalAlignmentOptions.Flush || (m_lineJustification & HorizontalAlignmentOptions.Justified) == HorizontalAlignmentOptions.Justified; 3295 3296 // Setup Mesh for visible text elements. ie. not a SPACE / LINEFEED / CARRIAGE RETURN. 3297 #region Handle Visible Characters 3298 if (charCode == 9 || ((m_TextWrappingMode == TextWrappingModes.PreserveWhitespace || m_TextWrappingMode == TextWrappingModes.PreserveWhitespaceNoWrap) && (isWhiteSpace || charCode == 0x200B)) || (isWhiteSpace == false && charCode != 0x200B && charCode != 0xAD && charCode != 0x03) || (charCode == 0xAD && isSoftHyphenIgnored == false) || m_textElementType == TMP_TextElementType.Sprite) 3299 { 3300 k_HandleVisibleCharacterMarker.Begin(); 3301 3302 m_textInfo.characterInfo[m_characterCount].isVisible = true; 3303 3304 #region Experimental Margin Shaper 3305 //Vector2 shapedMargins; 3306 //if (marginShaper) 3307 //{ 3308 // shapedMargins = m_marginShaper.GetShapedMargins(m_textInfo.characterInfo[m_characterCount].baseLine); 3309 // if (shapedMargins.x < margins.x) 3310 // { 3311 // shapedMargins.x = m_marginLeft; 3312 // } 3313 // else 3314 // { 3315 // shapedMargins.x += m_marginLeft - margins.x; 3316 // } 3317 // if (shapedMargins.y < margins.z) 3318 // { 3319 // shapedMargins.y = m_marginRight; 3320 // } 3321 // else 3322 // { 3323 // shapedMargins.y += m_marginRight - margins.z; 3324 // } 3325 //} 3326 //else 3327 //{ 3328 // shapedMargins.x = m_marginLeft; 3329 // shapedMargins.y = m_marginRight; 3330 //} 3331 //width = marginWidth + 0.0001f - shapedMargins.x - shapedMargins.y; 3332 //if (m_width != -1 && m_width < width) 3333 //{ 3334 // width = m_width; 3335 //} 3336 //m_textInfo.lineInfo[m_lineNumber].marginLeft = shapedMargins.x; 3337 #endregion 3338 3339 float marginLeft = m_marginLeft; 3340 float marginRight = m_marginRight; 3341 3342 // Injected characters do not override margins 3343 if (isInjectedCharacter) 3344 { 3345 marginLeft = m_textInfo.lineInfo[m_lineNumber].marginLeft; 3346 marginRight = m_textInfo.lineInfo[m_lineNumber].marginRight; 3347 } 3348 3349 widthOfTextArea = m_width != -1 ? Mathf.Min(marginWidth + 0.0001f - marginLeft - marginRight, m_width) : marginWidth + 0.0001f - marginLeft - marginRight; 3350 3351 // Calculate the line breaking width of the text. 3352 float textWidth = Mathf.Abs(m_xAdvance) + (!m_isRightToLeft ? currentGlyphMetrics.horizontalAdvance : 0) * (1 - m_charWidthAdjDelta) * (charCode == 0xAD ? currentElementUnmodifiedScale : currentElementScale); 3353 float textHeight = m_maxTextAscender - (m_maxLineDescender - m_lineOffset) + (m_lineOffset > 0 && m_IsDrivenLineSpacing == false ? m_maxLineAscender - m_startOfLineAscender : 0); 3354 3355 int testedCharacterCount = m_characterCount; 3356 3357 // Handling of current line Vertical Bounds 3358 #region Current Line Vertical Bounds Check 3359 if (textHeight > marginHeight + 0.0001f) 3360 { 3361 k_HandleVerticalLineBreakingMarker.Begin(); 3362 3363 // Set isTextOverflowing and firstOverflowCharacterIndex 3364 if (m_firstOverflowCharacterIndex == -1) 3365 m_firstOverflowCharacterIndex = m_characterCount; 3366 3367 // Check if Auto-Size is enabled 3368 if (m_enableAutoSizing) 3369 { 3370 // Handle Line spacing adjustments 3371 #region Line Spacing Adjustments 3372 if (m_lineSpacingDelta > m_lineSpacingMax && m_lineOffset > 0 && m_AutoSizeIterationCount < m_AutoSizeMaxIterationCount) 3373 { 3374 float adjustmentDelta = (marginHeight - textHeight) / m_lineNumber; 3375 3376 m_lineSpacingDelta = Mathf.Max(m_lineSpacingDelta + adjustmentDelta / baseScale, m_lineSpacingMax); 3377 3378 //Debug.Log("[" + m_AutoSizeIterationCount + "] Reducing Line Spacing. Delta of [" + m_lineSpacingDelta.ToString("f3") + "]."); 3379 k_HandleVerticalLineBreakingMarker.End(); 3380 k_HandleVisibleCharacterMarker.End(); 3381 k_GenerateTextPhaseIMarker.End(); 3382 k_GenerateTextMarker.End(); 3383 return; 3384 } 3385 #endregion 3386 3387 3388 // Handle Text Auto-sizing resulting from text exceeding vertical bounds. 3389 #region Text Auto-Sizing (Text greater than vertical bounds) 3390 if (m_fontSize > m_fontSizeMin && m_AutoSizeIterationCount < m_AutoSizeMaxIterationCount) 3391 { 3392 m_maxFontSize = m_fontSize; 3393 3394 float sizeDelta = Mathf.Max((m_fontSize - m_minFontSize) / 2, 0.05f); 3395 m_fontSize -= sizeDelta; 3396 m_fontSize = Mathf.Max((int)(m_fontSize * 20 + 0.5f) / 20f, m_fontSizeMin); 3397 3398 //Debug.Log("[" + m_AutoSizeIterationCount + "] Reducing Point Size from [" + m_maxFontSize.ToString("f3") + "] to [" + m_fontSize.ToString("f3") + "] with delta of [" + sizeDelta.ToString("f3") + "]."); 3399 k_HandleVerticalLineBreakingMarker.End(); 3400 k_HandleVisibleCharacterMarker.End(); 3401 k_GenerateTextPhaseIMarker.End(); 3402 k_GenerateTextMarker.End(); 3403 return; 3404 } 3405 #endregion Text Auto-Sizing 3406 } 3407 3408 // Handle Vertical Overflow on current line 3409 switch (m_overflowMode) 3410 { 3411 case TextOverflowModes.Overflow: 3412 case TextOverflowModes.ScrollRect: 3413 case TextOverflowModes.Masking: 3414 // Nothing happens as vertical bounds are ignored in this mode. 3415 break; 3416 3417 case TextOverflowModes.Truncate: 3418 i = RestoreWordWrappingState(ref m_SavedLastValidState); 3419 3420 characterToSubstitute.index = testedCharacterCount; 3421 characterToSubstitute.unicode = 0x03; 3422 k_HandleVerticalLineBreakingMarker.End(); 3423 k_HandleVisibleCharacterMarker.End(); 3424 continue; 3425 3426 case TextOverflowModes.Ellipsis: 3427 if (m_EllipsisInsertionCandidateStack.Count == 0) 3428 { 3429 i = -1; 3430 m_characterCount = 0; 3431 characterToSubstitute.index = 0; 3432 characterToSubstitute.unicode = 0x03; 3433 m_firstCharacterOfLine = 0; 3434 k_HandleVerticalLineBreakingMarker.End(); 3435 k_HandleVisibleCharacterMarker.End(); 3436 continue; 3437 } 3438 3439 var ellipsisState = m_EllipsisInsertionCandidateStack.Pop(); 3440 i = RestoreWordWrappingState(ref ellipsisState); 3441 3442 i -= 1; 3443 m_characterCount -= 1; 3444 characterToSubstitute.index = m_characterCount; 3445 characterToSubstitute.unicode = 0x2026; 3446 3447 restoreCount += 1; 3448 k_HandleVerticalLineBreakingMarker.End(); 3449 k_HandleVisibleCharacterMarker.End(); 3450 continue; 3451 3452 case TextOverflowModes.Linked: 3453 i = RestoreWordWrappingState(ref m_SavedLastValidState); 3454 3455 if (m_linkedTextComponent != null) 3456 { 3457 m_linkedTextComponent.text = text; 3458 m_linkedTextComponent.m_inputSource = m_inputSource; 3459 m_linkedTextComponent.firstVisibleCharacter = m_characterCount; 3460 m_linkedTextComponent.ForceMeshUpdate(); 3461 3462 m_isTextTruncated = true; 3463 } 3464 3465 // Truncate remaining text 3466 characterToSubstitute.index = testedCharacterCount; 3467 characterToSubstitute.unicode = 0x03; 3468 k_HandleVerticalLineBreakingMarker.End(); 3469 k_HandleVisibleCharacterMarker.End(); 3470 continue; 3471 3472 case TextOverflowModes.Page: 3473 // End layout of text if first character / page doesn't fit. 3474 if (i < 0 || testedCharacterCount == 0) 3475 { 3476 i = -1; 3477 m_characterCount = 0; 3478 characterToSubstitute.index = 0; 3479 characterToSubstitute.unicode = 0x03; 3480 k_HandleVerticalLineBreakingMarker.End(); 3481 k_HandleVisibleCharacterMarker.End(); 3482 continue; 3483 } 3484 else if (m_maxLineAscender - m_maxLineDescender > marginHeight + 0.0001f) 3485 { 3486 // Current line exceeds the height of the text container 3487 // as such we stop on the previous line. 3488 i = RestoreWordWrappingState(ref m_SavedLineState); 3489 3490 characterToSubstitute.index = testedCharacterCount; 3491 characterToSubstitute.unicode = 0x03; 3492 k_HandleVerticalLineBreakingMarker.End(); 3493 k_HandleVisibleCharacterMarker.End(); 3494 continue; 3495 } 3496 3497 // Go back to previous line and re-layout 3498 i = RestoreWordWrappingState(ref m_SavedLineState); 3499 3500 m_isNewPage = true; 3501 m_firstCharacterOfLine = m_characterCount; 3502 m_maxLineAscender = k_LargeNegativeFloat; 3503 m_maxLineDescender = k_LargePositiveFloat; 3504 m_startOfLineAscender = 0; 3505 3506 m_xAdvance = 0 + tag_Indent; 3507 m_lineOffset = 0; 3508 m_maxTextAscender = 0; 3509 m_PageAscender = 0; 3510 m_lineNumber += 1; 3511 m_pageNumber += 1; 3512 3513 // Should consider saving page data here 3514 k_HandleVerticalLineBreakingMarker.End(); 3515 k_HandleVisibleCharacterMarker.End(); 3516 continue; 3517 } 3518 3519 k_HandleVerticalLineBreakingMarker.End(); 3520 } 3521 #endregion 3522 3523 3524 // Handling of Horizontal Bounds 3525 #region Current Line Horizontal Bounds Check 3526 if (isBaseGlyph && textWidth > widthOfTextArea * (isJustifiedOrFlush ? 1.05f : 1.0f)) 3527 { 3528 k_HandleHorizontalLineBreakingMarker.Begin(); 3529 3530 // Handle Line Breaking (if still possible) 3531 if (m_TextWrappingMode != TextWrappingModes.NoWrap && m_TextWrappingMode != TextWrappingModes.PreserveWhitespaceNoWrap && m_characterCount != m_firstCharacterOfLine) 3532 { 3533 // Restore state to previous safe line breaking 3534 i = RestoreWordWrappingState(ref m_SavedWordWrapState); 3535 3536 // Compute potential new line offset in the event a line break is needed. 3537 float lineOffsetDelta = 0; 3538 if (m_lineHeight == TMP_Math.FLOAT_UNSET) 3539 { 3540 float ascender = m_textInfo.characterInfo[m_characterCount].adjustedAscender; 3541 lineOffsetDelta = (m_lineOffset > 0 && m_IsDrivenLineSpacing == false ? m_maxLineAscender - m_startOfLineAscender : 0) - m_maxLineDescender + ascender + (lineGap + m_lineSpacingDelta) * baseScale + m_lineSpacing * currentEmScale; 3542 } 3543 else 3544 { 3545 lineOffsetDelta = m_lineHeight + m_lineSpacing * currentEmScale; 3546 m_IsDrivenLineSpacing = true; 3547 } 3548 3549 // Calculate new text height 3550 float newTextHeight = m_maxTextAscender + lineOffsetDelta + m_lineOffset - m_textInfo.characterInfo[m_characterCount].adjustedDescender; 3551 3552 // Replace Soft Hyphen by Hyphen Minus 0x2D 3553 #region Handle Soft Hyphenation 3554 if (m_textInfo.characterInfo[m_characterCount - 1].character == 0xAD && isSoftHyphenIgnored == false) 3555 { 3556 // Only inject Hyphen Minus if new line is possible 3557 if (m_overflowMode == TextOverflowModes.Overflow || newTextHeight < marginHeight + 0.0001f) 3558 { 3559 characterToSubstitute.index = m_characterCount - 1; 3560 characterToSubstitute.unicode = 0x2D; 3561 3562 i -= 1; 3563 m_characterCount -= 1; 3564 k_HandleHorizontalLineBreakingMarker.End(); 3565 k_HandleVisibleCharacterMarker.End(); 3566 continue; 3567 } 3568 } 3569 3570 isSoftHyphenIgnored = false; 3571 3572 // Ignore Soft Hyphen to prevent it from wrapping 3573 if (m_textInfo.characterInfo[m_characterCount].character == 0xAD) 3574 { 3575 isSoftHyphenIgnored = true; 3576 k_HandleHorizontalLineBreakingMarker.End(); 3577 k_HandleVisibleCharacterMarker.End(); 3578 continue; 3579 } 3580 #endregion 3581 3582 // Adjust character spacing before breaking up word if auto size is enabled 3583 if (m_enableAutoSizing && isFirstWordOfLine) 3584 { 3585 // Handle Character Width Adjustments 3586 #region Character Width Adjustments 3587 if (m_charWidthAdjDelta < m_charWidthMaxAdj / 100 && m_AutoSizeIterationCount < m_AutoSizeMaxIterationCount) 3588 { 3589 float adjustedTextWidth = textWidth; 3590 3591 // Determine full width of the text 3592 if (m_charWidthAdjDelta > 0) 3593 adjustedTextWidth /= 1f - m_charWidthAdjDelta; 3594 3595 float adjustmentDelta = textWidth - (widthOfTextArea - 0.0001f) * (isJustifiedOrFlush ? 1.05f : 1.0f); 3596 m_charWidthAdjDelta += adjustmentDelta / adjustedTextWidth; 3597 m_charWidthAdjDelta = Mathf.Min(m_charWidthAdjDelta, m_charWidthMaxAdj / 100); 3598 3599 //Debug.Log("[" + m_AutoSizeIterationCount + "] Reducing Character Width by " + (m_charWidthAdjDelta * 100) + "%"); 3600 k_HandleHorizontalLineBreakingMarker.End(); 3601 k_HandleVisibleCharacterMarker.End(); 3602 k_GenerateTextPhaseIMarker.End(); 3603 k_GenerateTextMarker.End(); 3604 return; 3605 } 3606 #endregion 3607 3608 // Handle Text Auto-sizing resulting from text exceeding vertical bounds. 3609 #region Text Auto-Sizing (Text greater than vertical bounds) 3610 if (m_fontSize > m_fontSizeMin && m_AutoSizeIterationCount < m_AutoSizeMaxIterationCount) 3611 { 3612 m_maxFontSize = m_fontSize; 3613 3614 float sizeDelta = Mathf.Max((m_fontSize - m_minFontSize) / 2, 0.05f); 3615 m_fontSize -= sizeDelta; 3616 m_fontSize = Mathf.Max((int)(m_fontSize * 20 + 0.5f) / 20f, m_fontSizeMin); 3617 3618 //Debug.Log("[" + m_AutoSizeIterationCount + "] Reducing Point Size from [" + m_maxFontSize.ToString("f3") + "] to [" + m_fontSize.ToString("f3") + "] with delta of [" + sizeDelta.ToString("f3") + "]."); 3619 k_HandleHorizontalLineBreakingMarker.End(); 3620 k_HandleVisibleCharacterMarker.End(); 3621 k_GenerateTextPhaseIMarker.End(); 3622 k_GenerateTextMarker.End(); 3623 return; 3624 } 3625 #endregion Text Auto-Sizing 3626 } 3627 3628 3629 // Special handling if first word of line and non breaking space 3630 int savedSoftLineBreakingSpace = m_SavedSoftLineBreakState.previous_WordBreak; 3631 if (isFirstWordOfLine && savedSoftLineBreakingSpace != -1) 3632 { 3633 if (savedSoftLineBreakingSpace != lastSoftLineBreak) 3634 { 3635 i = RestoreWordWrappingState(ref m_SavedSoftLineBreakState); 3636 lastSoftLineBreak = savedSoftLineBreakingSpace; 3637 3638 // check if soft hyphen 3639 if (m_textInfo.characterInfo[m_characterCount - 1].character == 0xAD) 3640 { 3641 characterToSubstitute.index = m_characterCount - 1; 3642 characterToSubstitute.unicode = 0x2D; 3643 3644 i -= 1; 3645 m_characterCount -= 1; 3646 k_HandleHorizontalLineBreakingMarker.End(); 3647 k_HandleVisibleCharacterMarker.End(); 3648 continue; 3649 } 3650 } 3651 } 3652 3653 // Determine if new line of text would exceed the vertical bounds of text container 3654 if (newTextHeight > marginHeight + 0.0001f) 3655 { 3656 k_HandleVerticalLineBreakingMarker.Begin(); 3657 3658 // Set isTextOverflowing and firstOverflowCharacterIndex 3659 if (m_firstOverflowCharacterIndex == -1) 3660 m_firstOverflowCharacterIndex = m_characterCount; 3661 3662 // Check if Auto-Size is enabled 3663 if (m_enableAutoSizing) 3664 { 3665 // Handle Line spacing adjustments 3666 #region Line Spacing Adjustments 3667 if (m_lineSpacingDelta > m_lineSpacingMax && m_AutoSizeIterationCount < m_AutoSizeMaxIterationCount) 3668 { 3669 float adjustmentDelta = (marginHeight - newTextHeight) / (m_lineNumber + 1); 3670 3671 m_lineSpacingDelta = Mathf.Max(m_lineSpacingDelta + adjustmentDelta / baseScale, m_lineSpacingMax); 3672 3673 //Debug.Log("[" + m_AutoSizeIterationCount + "] Reducing Line Spacing. Delta of [" + m_lineSpacingDelta.ToString("f3") + "]."); 3674 k_HandleVerticalLineBreakingMarker.End(); 3675 k_HandleHorizontalLineBreakingMarker.End(); 3676 k_HandleVisibleCharacterMarker.End(); 3677 k_GenerateTextPhaseIMarker.End(); 3678 k_GenerateTextMarker.End(); 3679 return; 3680 } 3681 #endregion 3682 3683 // Handle Character Width Adjustments 3684 #region Character Width Adjustments 3685 if (m_charWidthAdjDelta < m_charWidthMaxAdj / 100 && m_AutoSizeIterationCount < m_AutoSizeMaxIterationCount) 3686 { 3687 float adjustedTextWidth = textWidth; 3688 3689 // Determine full width of the text 3690 if (m_charWidthAdjDelta > 0) 3691 adjustedTextWidth /= 1f - m_charWidthAdjDelta; 3692 3693 float adjustmentDelta = textWidth - (widthOfTextArea - 0.0001f) * (isJustifiedOrFlush ? 1.05f : 1.0f); 3694 m_charWidthAdjDelta += adjustmentDelta / adjustedTextWidth; 3695 m_charWidthAdjDelta = Mathf.Min(m_charWidthAdjDelta, m_charWidthMaxAdj / 100); 3696 3697 //Debug.Log("[" + m_AutoSizeIterationCount + "] Reducing Character Width by " + (m_charWidthAdjDelta * 100) + "%"); 3698 k_HandleVerticalLineBreakingMarker.End(); 3699 k_HandleHorizontalLineBreakingMarker.End(); 3700 k_HandleVisibleCharacterMarker.End(); 3701 k_GenerateTextPhaseIMarker.End(); 3702 k_GenerateTextMarker.End(); 3703 return; 3704 } 3705 #endregion 3706 3707 // Handle Text Auto-sizing resulting from text exceeding vertical bounds. 3708 #region Text Auto-Sizing (Text greater than vertical bounds) 3709 if (m_fontSize > m_fontSizeMin && m_AutoSizeIterationCount < m_AutoSizeMaxIterationCount) 3710 { 3711 m_maxFontSize = m_fontSize; 3712 3713 float sizeDelta = Mathf.Max((m_fontSize - m_minFontSize) / 2, 0.05f); 3714 m_fontSize -= sizeDelta; 3715 m_fontSize = Mathf.Max((int)(m_fontSize * 20 + 0.5f) / 20f, m_fontSizeMin); 3716 3717 //Debug.Log("[" + m_AutoSizeIterationCount + "] Reducing Point Size from [" + m_maxFontSize.ToString("f3") + "] to [" + m_fontSize.ToString("f3") + "] with delta of [" + sizeDelta.ToString("f3") + "]."); 3718 k_HandleVerticalLineBreakingMarker.End(); 3719 k_HandleHorizontalLineBreakingMarker.End(); 3720 k_HandleVisibleCharacterMarker.End(); 3721 k_GenerateTextPhaseIMarker.End(); 3722 k_GenerateTextMarker.End(); 3723 return; 3724 } 3725 #endregion Text Auto-Sizing 3726 } 3727 3728 // Check Text Overflow Modes 3729 switch (m_overflowMode) 3730 { 3731 case TextOverflowModes.Overflow: 3732 case TextOverflowModes.ScrollRect: 3733 case TextOverflowModes.Masking: 3734 InsertNewLine(i, baseScale, currentElementScale, currentEmScale, boldSpacingAdjustment, characterSpacingAdjustment, widthOfTextArea, lineGap, ref isMaxVisibleDescenderSet, ref maxVisibleDescender); 3735 isStartOfNewLine = true; 3736 isFirstWordOfLine = true; 3737 k_HandleVerticalLineBreakingMarker.End(); 3738 k_HandleHorizontalLineBreakingMarker.End(); 3739 k_HandleVisibleCharacterMarker.End(); 3740 continue; 3741 3742 case TextOverflowModes.Truncate: 3743 i = RestoreWordWrappingState(ref m_SavedLastValidState); 3744 3745 characterToSubstitute.index = testedCharacterCount; 3746 characterToSubstitute.unicode = 0x03; 3747 k_HandleVerticalLineBreakingMarker.End(); 3748 k_HandleHorizontalLineBreakingMarker.End(); 3749 k_HandleVisibleCharacterMarker.End(); 3750 continue; 3751 3752 case TextOverflowModes.Ellipsis: 3753 if (m_EllipsisInsertionCandidateStack.Count == 0) 3754 { 3755 i = -1; 3756 m_characterCount = 0; 3757 characterToSubstitute.index = 0; 3758 characterToSubstitute.unicode = 0x03; 3759 m_firstCharacterOfLine = 0; 3760 k_HandleVerticalLineBreakingMarker.End(); 3761 k_HandleHorizontalLineBreakingMarker.End(); 3762 k_HandleVisibleCharacterMarker.End(); 3763 continue; 3764 } 3765 3766 var ellipsisState = m_EllipsisInsertionCandidateStack.Pop(); 3767 i = RestoreWordWrappingState(ref ellipsisState); 3768 3769 i -= 1; 3770 m_characterCount -= 1; 3771 characterToSubstitute.index = m_characterCount; 3772 characterToSubstitute.unicode = 0x2026; 3773 3774 restoreCount += 1; 3775 k_HandleVerticalLineBreakingMarker.End(); 3776 k_HandleHorizontalLineBreakingMarker.End(); 3777 k_HandleVisibleCharacterMarker.End(); 3778 continue; 3779 3780 case TextOverflowModes.Linked: 3781 if (m_linkedTextComponent != null) 3782 { 3783 m_linkedTextComponent.text = text; 3784 m_linkedTextComponent.m_inputSource = m_inputSource; 3785 m_linkedTextComponent.firstVisibleCharacter = m_characterCount; 3786 m_linkedTextComponent.ForceMeshUpdate(); 3787 3788 m_isTextTruncated = true; 3789 } 3790 3791 // Truncate remaining text 3792 characterToSubstitute.index = m_characterCount; 3793 characterToSubstitute.unicode = 0x03; 3794 k_HandleVerticalLineBreakingMarker.End(); 3795 k_HandleHorizontalLineBreakingMarker.End(); 3796 k_HandleVisibleCharacterMarker.End(); 3797 continue; 3798 3799 case TextOverflowModes.Page: 3800 // Add new page 3801 m_isNewPage = true; 3802 3803 InsertNewLine(i, baseScale, currentElementScale, currentEmScale, boldSpacingAdjustment, characterSpacingAdjustment, widthOfTextArea, lineGap, ref isMaxVisibleDescenderSet, ref maxVisibleDescender); 3804 3805 m_startOfLineAscender = 0; 3806 m_lineOffset = 0; 3807 m_maxTextAscender = 0; 3808 m_PageAscender = 0; 3809 m_pageNumber += 1; 3810 3811 isStartOfNewLine = true; 3812 isFirstWordOfLine = true; 3813 k_HandleVerticalLineBreakingMarker.End(); 3814 k_HandleHorizontalLineBreakingMarker.End(); 3815 k_HandleVisibleCharacterMarker.End(); 3816 continue; 3817 } 3818 } 3819 else 3820 { 3821 //if (m_enableAutoSizing && isFirstWordOfLine) 3822 //{ 3823 // // Handle Character Width Adjustments 3824 // #region Character Width Adjustments 3825 // if (m_charWidthAdjDelta < m_charWidthMaxAdj / 100 && m_AutoSizeIterationCount < m_AutoSizeMaxIterationCount) 3826 // { 3827 // //m_AutoSizeIterationCount = 0; 3828 // float adjustedTextWidth = textWidth; 3829 3830 // // Determine full width of the text 3831 // if (m_charWidthAdjDelta > 0) 3832 // adjustedTextWidth /= 1f - m_charWidthAdjDelta; 3833 3834 // float adjustmentDelta = textWidth - (widthOfTextArea - 0.0001f) * (isJustifiedOrFlush ? 1.05f : 1.0f); 3835 // m_charWidthAdjDelta += adjustmentDelta / adjustedTextWidth; 3836 // m_charWidthAdjDelta = Mathf.Min(m_charWidthAdjDelta, m_charWidthMaxAdj / 100); 3837 3838 // //Debug.Log("[" + m_AutoSizeIterationCount + "] Reducing Character Width by " + (m_charWidthAdjDelta * 100) + "%"); 3839 3840 // GenerateTextMesh(); 3841 // return; 3842 // } 3843 // #endregion 3844 //} 3845 3846 // New line of text does not exceed vertical bounds of text container 3847 InsertNewLine(i, baseScale, currentElementScale, currentEmScale, boldSpacingAdjustment, characterSpacingAdjustment, widthOfTextArea, lineGap, ref isMaxVisibleDescenderSet, ref maxVisibleDescender); 3848 isStartOfNewLine = true; 3849 isFirstWordOfLine = true; 3850 k_HandleHorizontalLineBreakingMarker.End(); 3851 k_HandleVisibleCharacterMarker.End(); 3852 continue; 3853 } 3854 } 3855 else 3856 { 3857 if (m_enableAutoSizing && m_AutoSizeIterationCount < m_AutoSizeMaxIterationCount) 3858 { 3859 // Handle Character Width Adjustments 3860 #region Character Width Adjustments 3861 if (m_charWidthAdjDelta < m_charWidthMaxAdj / 100) 3862 { 3863 float adjustedTextWidth = textWidth; 3864 3865 // Determine full width of the text 3866 if (m_charWidthAdjDelta > 0) 3867 adjustedTextWidth /= 1f - m_charWidthAdjDelta; 3868 3869 float adjustmentDelta = textWidth - (widthOfTextArea - 0.0001f) * (isJustifiedOrFlush ? 1.05f : 1.0f); 3870 m_charWidthAdjDelta += adjustmentDelta / adjustedTextWidth; 3871 m_charWidthAdjDelta = Mathf.Min(m_charWidthAdjDelta, m_charWidthMaxAdj / 100); 3872 3873 //Debug.Log("[" + m_AutoSizeIterationCount + "] Reducing Character Width by " + (m_charWidthAdjDelta * 100) + "%"); 3874 k_HandleHorizontalLineBreakingMarker.End(); 3875 k_HandleVisibleCharacterMarker.End(); 3876 k_GenerateTextPhaseIMarker.End(); 3877 k_GenerateTextMarker.End(); 3878 return; 3879 } 3880 #endregion 3881 3882 // Handle Text Auto-sizing resulting from text exceeding horizontal bounds. 3883 #region Text Exceeds Horizontal Bounds - Reducing Point Size 3884 if (m_fontSize > m_fontSizeMin) 3885 { 3886 // Reset character width adjustment delta 3887 //m_charWidthAdjDelta = 0; 3888 3889 // Adjust Point Size 3890 m_maxFontSize = m_fontSize; 3891 3892 float sizeDelta = Mathf.Max((m_fontSize - m_minFontSize) / 2, 0.05f); 3893 m_fontSize -= sizeDelta; 3894 m_fontSize = Mathf.Max((int)(m_fontSize * 20 + 0.5f) / 20f, m_fontSizeMin); 3895 3896 //Debug.Log("[" + m_AutoSizeIterationCount + "] Reducing Point Size from [" + m_maxFontSize.ToString("f3") + "] to [" + m_fontSize.ToString("f3") + "] with delta of [" + sizeDelta.ToString("f3") + "]."); 3897 k_HandleHorizontalLineBreakingMarker.End(); 3898 k_HandleVisibleCharacterMarker.End(); 3899 k_GenerateTextPhaseIMarker.End(); 3900 k_GenerateTextMarker.End(); 3901 return; 3902 } 3903 #endregion 3904 3905 } 3906 3907 // Check Text Overflow Modes 3908 switch (m_overflowMode) 3909 { 3910 case TextOverflowModes.Overflow: 3911 case TextOverflowModes.ScrollRect: 3912 case TextOverflowModes.Masking: 3913 // Nothing happens as horizontal bounds are ignored in this mode. 3914 break; 3915 3916 case TextOverflowModes.Truncate: 3917 i = RestoreWordWrappingState(ref m_SavedWordWrapState); 3918 3919 characterToSubstitute.index = testedCharacterCount; 3920 characterToSubstitute.unicode = 0x03; 3921 k_HandleHorizontalLineBreakingMarker.End(); 3922 k_HandleVisibleCharacterMarker.End(); 3923 continue; 3924 3925 case TextOverflowModes.Ellipsis: 3926 if (m_EllipsisInsertionCandidateStack.Count == 0) 3927 { 3928 i = -1; 3929 m_characterCount = 0; 3930 characterToSubstitute.index = 0; 3931 characterToSubstitute.unicode = 0x03; 3932 m_firstCharacterOfLine = 0; 3933 k_HandleHorizontalLineBreakingMarker.End(); 3934 k_HandleVisibleCharacterMarker.End(); 3935 continue; 3936 } 3937 3938 var ellipsisState = m_EllipsisInsertionCandidateStack.Pop(); 3939 i = RestoreWordWrappingState(ref ellipsisState); 3940 3941 i -= 1; 3942 m_characterCount -= 1; 3943 characterToSubstitute.index = m_characterCount; 3944 characterToSubstitute.unicode = 0x2026; 3945 3946 restoreCount += 1; 3947 k_HandleHorizontalLineBreakingMarker.End(); 3948 k_HandleVisibleCharacterMarker.End(); 3949 continue; 3950 3951 case TextOverflowModes.Linked: 3952 i = RestoreWordWrappingState(ref m_SavedWordWrapState); 3953 3954 if (m_linkedTextComponent != null) 3955 { 3956 m_linkedTextComponent.text = text; 3957 m_linkedTextComponent.m_inputSource = m_inputSource; 3958 m_linkedTextComponent.firstVisibleCharacter = m_characterCount; 3959 m_linkedTextComponent.ForceMeshUpdate(); 3960 3961 m_isTextTruncated = true; 3962 } 3963 3964 // Truncate text the overflows the vertical bounds 3965 characterToSubstitute.index = m_characterCount; 3966 characterToSubstitute.unicode = 0x03; 3967 k_HandleHorizontalLineBreakingMarker.End(); 3968 k_HandleVisibleCharacterMarker.End(); 3969 continue; 3970 } 3971 3972 } 3973 3974 k_HandleHorizontalLineBreakingMarker.End(); 3975 } 3976 #endregion 3977 3978 3979 // Special handling of characters that are not ignored at the end of a line. 3980 if (isWhiteSpace) 3981 { 3982 m_textInfo.characterInfo[m_characterCount].isVisible = false; 3983 m_lastVisibleCharacterOfLine = m_characterCount; 3984 m_lineVisibleSpaceCount = m_textInfo.lineInfo[m_lineNumber].spaceCount += 1; 3985 m_textInfo.lineInfo[m_lineNumber].marginLeft = marginLeft; 3986 m_textInfo.lineInfo[m_lineNumber].marginRight = marginRight; 3987 m_textInfo.spaceCount += 1; 3988 3989 if (charCode == 0xA0) 3990 m_textInfo.lineInfo[m_lineNumber].controlCharacterCount += 1; 3991 } 3992 else if (charCode == 0xAD) 3993 { 3994 m_textInfo.characterInfo[m_characterCount].isVisible = false; 3995 } 3996 else 3997 { 3998 // Determine Vertex Color 3999 Color32 vertexColor; 4000 if (m_overrideHtmlColors) 4001 vertexColor = m_fontColor32; 4002 else 4003 vertexColor = m_htmlColor; 4004 4005 k_SaveGlyphVertexDataMarker.Begin(); 4006 // Store Character & Sprite Vertex Information 4007 if (m_textElementType == TMP_TextElementType.Character) 4008 { 4009 // Save Character Vertex Data 4010 SaveGlyphVertexInfo(padding, style_padding, vertexColor); 4011 } 4012 else if (m_textElementType == TMP_TextElementType.Sprite) 4013 { 4014 SaveSpriteVertexInfo(vertexColor); 4015 } 4016 k_SaveGlyphVertexDataMarker.End(); 4017 4018 if (isStartOfNewLine) 4019 { 4020 isStartOfNewLine = false; 4021 m_firstVisibleCharacterOfLine = m_characterCount; 4022 } 4023 4024 m_lineVisibleCharacterCount += 1; 4025 m_lastVisibleCharacterOfLine = m_characterCount; 4026 m_textInfo.lineInfo[m_lineNumber].marginLeft = marginLeft; 4027 m_textInfo.lineInfo[m_lineNumber].marginRight = marginRight; 4028 } 4029 4030 k_HandleVisibleCharacterMarker.End(); 4031 } 4032 else 4033 { 4034 k_HandleWhiteSpacesMarker.Begin(); 4035 4036 // Special handling for text overflow linked mode 4037 #region Check Vertical Bounds 4038 if (m_overflowMode == TextOverflowModes.Linked && (charCode == 10 || charCode == 11)) 4039 { 4040 float textHeight = m_maxTextAscender - (m_maxLineDescender - m_lineOffset) + (m_lineOffset > 0 && m_IsDrivenLineSpacing == false ? m_maxLineAscender - m_startOfLineAscender : 0); 4041 4042 int testedCharacterCount = m_characterCount; 4043 4044 if (textHeight > marginHeight + 0.0001f) 4045 { 4046 // Set isTextOverflowing and firstOverflowCharacterIndex 4047 if (m_firstOverflowCharacterIndex == -1) 4048 m_firstOverflowCharacterIndex = m_characterCount; 4049 4050 i = RestoreWordWrappingState(ref m_SavedLastValidState); 4051 4052 if (m_linkedTextComponent != null) 4053 { 4054 m_linkedTextComponent.text = text; 4055 m_linkedTextComponent.m_inputSource = m_inputSource; 4056 m_linkedTextComponent.firstVisibleCharacter = m_characterCount; 4057 m_linkedTextComponent.ForceMeshUpdate(); 4058 4059 m_isTextTruncated = true; 4060 } 4061 4062 // Truncate remaining text 4063 characterToSubstitute.index = testedCharacterCount; 4064 characterToSubstitute.unicode = 0x03; 4065 k_HandleWhiteSpacesMarker.End(); 4066 continue; 4067 } 4068 } 4069 #endregion 4070 4071 // Track # of spaces per line which is used for line justification. 4072 if ((charCode == 10 || charCode == 11 || charCode == 0xA0 || charCode == 0x2007 || charCode == 0x2028 || charCode == 0x2029 || char.IsSeparator((char)charCode)) && charCode != 0xAD && charCode != 0x200B && charCode != 0x2060) 4073 { 4074 m_textInfo.lineInfo[m_lineNumber].spaceCount += 1; 4075 m_textInfo.spaceCount += 1; 4076 } 4077 4078 // Special handling for control characters like <NBSP> 4079 if (charCode == 0xA0) 4080 m_textInfo.lineInfo[m_lineNumber].controlCharacterCount += 1; 4081 4082 k_HandleWhiteSpacesMarker.End(); 4083 } 4084 #endregion Handle Visible Characters 4085 4086 4087 // Tracking of potential insertion positions for Ellipsis character 4088 #region Track Potential Insertion Location for Ellipsis 4089 if (m_overflowMode == TextOverflowModes.Ellipsis && (isInjectedCharacter == false || charCode == 0x2D)) 4090 { 4091 float fontScale = m_currentFontSize / m_Ellipsis.fontAsset.m_FaceInfo.pointSize * m_Ellipsis.fontAsset.m_FaceInfo.scale * (m_isOrthographic ? 1 : 0.1f); 4092 float scale = fontScale * m_fontScaleMultiplier * m_Ellipsis.character.m_Scale * m_Ellipsis.character.m_Glyph.scale; 4093 float marginLeft = m_marginLeft; 4094 float marginRight = m_marginRight; 4095 4096 // Use the scale and margins of the previous character if Line Feed (LF) is not the first character of a line. 4097 if (charCode == 0x0A && m_characterCount != m_firstCharacterOfLine) 4098 { 4099 fontScale = m_textInfo.characterInfo[m_characterCount - 1].pointSize / m_Ellipsis.fontAsset.m_FaceInfo.pointSize * m_Ellipsis.fontAsset.m_FaceInfo.scale * (m_isOrthographic ? 1 : 0.1f); 4100 scale = fontScale * m_fontScaleMultiplier * m_Ellipsis.character.m_Scale * m_Ellipsis.character.m_Glyph.scale; 4101 marginLeft = m_textInfo.lineInfo[m_lineNumber].marginLeft; 4102 marginRight = m_textInfo.lineInfo[m_lineNumber].marginRight; 4103 } 4104 4105 float textHeight = m_maxTextAscender - (m_maxLineDescender - m_lineOffset) + (m_lineOffset > 0 && m_IsDrivenLineSpacing == false ? m_maxLineAscender - m_startOfLineAscender : 0); 4106 float textWidth = Mathf.Abs(m_xAdvance) + (!m_isRightToLeft ? m_Ellipsis.character.m_Glyph.metrics.horizontalAdvance : 0) * (1 - m_charWidthAdjDelta) * scale; 4107 float widthOfTextAreaForEllipsis = m_width != -1 ? Mathf.Min(marginWidth + 0.0001f - marginLeft - marginRight, m_width) : marginWidth + 0.0001f - marginLeft - marginRight; 4108 4109 if (textWidth < widthOfTextAreaForEllipsis * (isJustifiedOrFlush ? 1.05f : 1.0f) && textHeight < marginHeight + 0.0001f) 4110 { 4111 SaveWordWrappingState(ref m_SavedEllipsisState, i, m_characterCount); 4112 m_EllipsisInsertionCandidateStack.Push(m_SavedEllipsisState); 4113 } 4114 } 4115 #endregion 4116 4117 4118 // Store Rectangle positions for each Character. 4119 #region Store Character Data 4120 m_textInfo.characterInfo[m_characterCount].lineNumber = m_lineNumber; 4121 m_textInfo.characterInfo[m_characterCount].pageNumber = m_pageNumber; 4122 4123 if (charCode != 10 && charCode != 11 && charCode != 13 && isInjectedCharacter == false /* && charCode != 8230 */ || m_textInfo.lineInfo[m_lineNumber].characterCount == 1) 4124 m_textInfo.lineInfo[m_lineNumber].alignment = m_lineJustification; 4125 #endregion Store Character Data 4126 4127 4128 // Handle xAdvance & Tabulation Stops. Tab stops at every 25% of Font Size. 4129 #region XAdvance, Tabulation & Stops 4130 k_ComputeCharacterAdvanceMarker.Begin(); 4131 if (charCode == 9) 4132 { 4133 float tabSize = m_currentFontAsset.m_FaceInfo.tabWidth * m_currentFontAsset.tabSize * currentElementScale; 4134 // Adjust horizontal tab depending on RTL 4135 if (m_isRightToLeft) 4136 { 4137 float tabs = Mathf.Floor(m_xAdvance / tabSize) * tabSize; 4138 m_xAdvance = tabs < m_xAdvance ? tabs : m_xAdvance - tabSize; 4139 } 4140 else 4141 { 4142 float tabs = Mathf.Ceil(m_xAdvance / tabSize) * tabSize; 4143 m_xAdvance = tabs > m_xAdvance ? tabs : m_xAdvance + tabSize; 4144 } 4145 } 4146 else if (m_monoSpacing != 0) 4147 { 4148 float monoAdjustment; 4149 if (m_duoSpace && (charCode == '.' || charCode == ':' || charCode == ',')) 4150 monoAdjustment = m_monoSpacing / 2 - monoAdvance; 4151 else 4152 monoAdjustment = m_monoSpacing - monoAdvance; 4153 4154 m_xAdvance += (monoAdjustment + ((m_currentFontAsset.normalSpacingOffset + characterSpacingAdjustment) * currentEmScale) + m_cSpacing) * (1 - m_charWidthAdjDelta); 4155 4156 if (isWhiteSpace || charCode == 0x200B) 4157 m_xAdvance += m_wordSpacing * currentEmScale; 4158 } 4159 else if (m_isRightToLeft) 4160 { 4161 m_xAdvance -= ((glyphAdjustments.xAdvance * currentElementScale + (m_currentFontAsset.normalSpacingOffset + characterSpacingAdjustment + boldSpacingAdjustment) * currentEmScale + m_cSpacing) * (1 - m_charWidthAdjDelta)); 4162 4163 if (isWhiteSpace || charCode == 0x200B) 4164 m_xAdvance -= m_wordSpacing * currentEmScale; 4165 } 4166 else 4167 { 4168 m_xAdvance += ((currentGlyphMetrics.horizontalAdvance * m_FXScale.x + glyphAdjustments.xAdvance) * currentElementScale + (m_currentFontAsset.normalSpacingOffset + characterSpacingAdjustment + boldSpacingAdjustment) * currentEmScale + m_cSpacing) * (1 - m_charWidthAdjDelta); 4169 4170 if (isWhiteSpace || charCode == 0x200B) 4171 m_xAdvance += m_wordSpacing * currentEmScale; 4172 } 4173 4174 // Store xAdvance information 4175 m_textInfo.characterInfo[m_characterCount].xAdvance = m_xAdvance; 4176 k_ComputeCharacterAdvanceMarker.End(); 4177 #endregion Tabulation & Stops 4178 4179 4180 // Handle Carriage Return 4181 #region Carriage Return 4182 if (charCode == 13) 4183 { 4184 k_HandleCarriageReturnMarker.Begin(); 4185 m_xAdvance = 0 + tag_Indent; 4186 k_HandleCarriageReturnMarker.End(); 4187 } 4188 #endregion Carriage Return 4189 4190 4191 // Tracking of text overflow page mode 4192 #region Save PageInfo 4193 k_SavePageInfoMarker.Begin(); 4194 if (m_overflowMode == TextOverflowModes.Page && charCode != 10 && charCode != 11 && charCode != 13 && charCode != 0x2028 && charCode != 0x2029) 4195 { 4196 // Check if we need to increase allocations for the pageInfo array. 4197 if (m_pageNumber + 1 > m_textInfo.pageInfo.Length) 4198 TMP_TextInfo.Resize(ref m_textInfo.pageInfo, m_pageNumber + 1, true); 4199 4200 m_textInfo.pageInfo[m_pageNumber].ascender = m_PageAscender; 4201 m_textInfo.pageInfo[m_pageNumber].descender = m_ElementDescender < m_textInfo.pageInfo[m_pageNumber].descender 4202 ? m_ElementDescender 4203 : m_textInfo.pageInfo[m_pageNumber].descender; 4204 4205 if (m_isNewPage) 4206 { 4207 m_isNewPage = false; 4208 m_textInfo.pageInfo[m_pageNumber].firstCharacterIndex = m_characterCount; 4209 } 4210 4211 // Last index 4212 m_textInfo.pageInfo[m_pageNumber].lastCharacterIndex = m_characterCount; 4213 } 4214 k_SavePageInfoMarker.End(); 4215 #endregion Save PageInfo 4216 4217 4218 // Handle Line Spacing Adjustments + Word Wrapping & special case for last line. 4219 #region Check for Line Feed and Last Character 4220 if (charCode == 10 || charCode == 11 || charCode == 0x03 || charCode == 0x2028 || charCode == 0x2029 || (charCode == 0x2D && isInjectedCharacter) || m_characterCount == totalCharacterCount - 1) 4221 { 4222 k_HandleLineTerminationMarker.Begin(); 4223 4224 // Adjust current line spacing (if necessary) before inserting new line 4225 float baselineAdjustmentDelta = m_maxLineAscender - m_startOfLineAscender; 4226 if (m_lineOffset > 0 && Math.Abs(baselineAdjustmentDelta) > 0.01f && m_IsDrivenLineSpacing == false && !m_isNewPage) 4227 { 4228 //Debug.Log("Line Feed - Adjusting Line Spacing on line #" + m_lineNumber); 4229 AdjustLineOffset(m_firstCharacterOfLine, m_characterCount, baselineAdjustmentDelta); 4230 m_ElementDescender -= baselineAdjustmentDelta; 4231 m_lineOffset += baselineAdjustmentDelta; 4232 4233 // Adjust saved ellipsis state only if we are adjusting the same line number 4234 if (m_SavedEllipsisState.lineNumber == m_lineNumber) 4235 { 4236 m_SavedEllipsisState = m_EllipsisInsertionCandidateStack.Pop(); 4237 m_SavedEllipsisState.startOfLineAscender += baselineAdjustmentDelta; 4238 m_SavedEllipsisState.lineOffset += baselineAdjustmentDelta; 4239 m_EllipsisInsertionCandidateStack.Push(m_SavedEllipsisState); 4240 } 4241 } 4242 m_isNewPage = false; 4243 4244 // Calculate lineAscender & make sure if last character is superscript or subscript that we check that as well. 4245 float lineAscender = m_maxLineAscender - m_lineOffset; 4246 float lineDescender = m_maxLineDescender - m_lineOffset; 4247 4248 // Update maxDescender and maxVisibleDescender 4249 m_ElementDescender = m_ElementDescender < lineDescender ? m_ElementDescender : lineDescender; 4250 if (!isMaxVisibleDescenderSet) 4251 maxVisibleDescender = m_ElementDescender; 4252 4253 if (m_useMaxVisibleDescender && (m_characterCount >= m_maxVisibleCharacters || m_lineNumber >= m_maxVisibleLines)) 4254 isMaxVisibleDescenderSet = true; 4255 4256 // Save Line Information 4257 m_textInfo.lineInfo[m_lineNumber].firstCharacterIndex = m_firstCharacterOfLine; 4258 m_textInfo.lineInfo[m_lineNumber].firstVisibleCharacterIndex = m_firstVisibleCharacterOfLine = m_firstCharacterOfLine > m_firstVisibleCharacterOfLine ? m_firstCharacterOfLine : m_firstVisibleCharacterOfLine; 4259 m_textInfo.lineInfo[m_lineNumber].lastCharacterIndex = m_lastCharacterOfLine = m_characterCount; 4260 m_textInfo.lineInfo[m_lineNumber].lastVisibleCharacterIndex = m_lastVisibleCharacterOfLine = m_lastVisibleCharacterOfLine < m_firstVisibleCharacterOfLine ? m_firstVisibleCharacterOfLine : m_lastVisibleCharacterOfLine; 4261 4262 m_textInfo.lineInfo[m_lineNumber].characterCount = m_textInfo.lineInfo[m_lineNumber].lastCharacterIndex - m_textInfo.lineInfo[m_lineNumber].firstCharacterIndex + 1; 4263 m_textInfo.lineInfo[m_lineNumber].visibleCharacterCount = m_lineVisibleCharacterCount; 4264 m_textInfo.lineInfo[m_lineNumber].visibleSpaceCount = (m_textInfo.lineInfo[m_lineNumber].lastVisibleCharacterIndex + 1) - m_lineVisibleCharacterCount; 4265 m_textInfo.lineInfo[m_lineNumber].lineExtents.min = new Vector2(m_textInfo.characterInfo[m_firstVisibleCharacterOfLine].bottomLeft.x, lineDescender); 4266 m_textInfo.lineInfo[m_lineNumber].lineExtents.max = new Vector2(m_textInfo.characterInfo[m_lastVisibleCharacterOfLine].topRight.x, lineAscender); 4267 m_textInfo.lineInfo[m_lineNumber].length = m_textInfo.lineInfo[m_lineNumber].lineExtents.max.x - (padding * currentElementScale); 4268 m_textInfo.lineInfo[m_lineNumber].width = widthOfTextArea; 4269 4270 if (m_textInfo.lineInfo[m_lineNumber].characterCount == 1) 4271 m_textInfo.lineInfo[m_lineNumber].alignment = m_lineJustification; 4272 4273 float maxAdvanceOffset = ((m_currentFontAsset.normalSpacingOffset + characterSpacingAdjustment + boldSpacingAdjustment) * currentEmScale + m_cSpacing) * (1 - m_charWidthAdjDelta); 4274 if (m_textInfo.characterInfo[m_lastVisibleCharacterOfLine].isVisible) 4275 m_textInfo.lineInfo[m_lineNumber].maxAdvance = m_textInfo.characterInfo[m_lastVisibleCharacterOfLine].xAdvance + (m_isRightToLeft ? maxAdvanceOffset : - maxAdvanceOffset); 4276 else 4277 m_textInfo.lineInfo[m_lineNumber].maxAdvance = m_textInfo.characterInfo[m_lastCharacterOfLine].xAdvance + (m_isRightToLeft ? maxAdvanceOffset : - maxAdvanceOffset); 4278 4279 m_textInfo.lineInfo[m_lineNumber].baseline = 0 - m_lineOffset; 4280 m_textInfo.lineInfo[m_lineNumber].ascender = lineAscender; 4281 m_textInfo.lineInfo[m_lineNumber].descender = lineDescender; 4282 m_textInfo.lineInfo[m_lineNumber].lineHeight = lineAscender - lineDescender + lineGap * baseScale; 4283 4284 // Add new line if not last line or character. 4285 if (charCode == 10 || charCode == 11 || (charCode == 0x2D && isInjectedCharacter) || charCode == 0x2028 || charCode == 0x2029) 4286 { 4287 // Store the state of the line before starting on the new line. 4288 SaveWordWrappingState(ref m_SavedLineState, i, m_characterCount); 4289 4290 m_lineNumber += 1; 4291 isStartOfNewLine = true; 4292 ignoreNonBreakingSpace = false; 4293 isFirstWordOfLine = true; 4294 4295 m_firstCharacterOfLine = m_characterCount + 1; 4296 m_lineVisibleCharacterCount = 0; 4297 m_lineVisibleSpaceCount = 0; 4298 4299 // Check to make sure Array is large enough to hold a new line. 4300 if (m_lineNumber >= m_textInfo.lineInfo.Length) 4301 ResizeLineExtents(m_lineNumber); 4302 4303 float lastVisibleAscender = m_textInfo.characterInfo[m_characterCount].adjustedAscender; 4304 4305 // Apply Line Spacing with special handling for VT char(11) 4306 if (m_lineHeight == TMP_Math.FLOAT_UNSET) 4307 { 4308 float lineOffsetDelta = 0 - m_maxLineDescender + lastVisibleAscender + (lineGap + m_lineSpacingDelta) * baseScale + (m_lineSpacing + (charCode == 10 || charCode == 0x2029 ? m_paragraphSpacing : 0)) * currentEmScale; 4309 m_lineOffset += lineOffsetDelta; 4310 m_IsDrivenLineSpacing = false; 4311 } 4312 else 4313 { 4314 m_lineOffset += m_lineHeight + (m_lineSpacing + (charCode == 10 || charCode == 0x2029 ? m_paragraphSpacing : 0)) * currentEmScale; 4315 m_IsDrivenLineSpacing = true; 4316 } 4317 4318 m_maxLineAscender = k_LargeNegativeFloat; 4319 m_maxLineDescender = k_LargePositiveFloat; 4320 m_startOfLineAscender = lastVisibleAscender; 4321 4322 m_xAdvance = 0 + tag_LineIndent + tag_Indent; 4323 4324 SaveWordWrappingState(ref m_SavedWordWrapState, i, m_characterCount); 4325 SaveWordWrappingState(ref m_SavedLastValidState, i, m_characterCount); 4326 4327 m_characterCount += 1; 4328 4329 k_HandleLineTerminationMarker.End(); 4330 4331 continue; 4332 } 4333 4334 // If End of Text 4335 if (charCode == 0x03) 4336 i = m_TextProcessingArray.Length; 4337 4338 k_HandleLineTerminationMarker.End(); 4339 } 4340 #endregion Check for Linefeed or Last Character 4341 4342 4343 // Track extents of the text 4344 #region Track Text Extents 4345 k_SaveTextExtentMarker.Begin(); 4346 // Determine the bounds of the Mesh. 4347 if (m_textInfo.characterInfo[m_characterCount].isVisible) 4348 { 4349 m_meshExtents.min.x = Mathf.Min(m_meshExtents.min.x, m_textInfo.characterInfo[m_characterCount].bottomLeft.x); 4350 m_meshExtents.min.y = Mathf.Min(m_meshExtents.min.y, m_textInfo.characterInfo[m_characterCount].bottomLeft.y); 4351 4352 m_meshExtents.max.x = Mathf.Max(m_meshExtents.max.x, m_textInfo.characterInfo[m_characterCount].topRight.x); 4353 m_meshExtents.max.y = Mathf.Max(m_meshExtents.max.y, m_textInfo.characterInfo[m_characterCount].topRight.y); 4354 4355 //m_meshExtents.min = new Vector2(Mathf.Min(m_meshExtents.min.x, m_textInfo.characterInfo[m_characterCount].bottomLeft.x), Mathf.Min(m_meshExtents.min.y, m_textInfo.characterInfo[m_characterCount].bottomLeft.y)); 4356 //m_meshExtents.max = new Vector2(Mathf.Max(m_meshExtents.max.x, m_textInfo.characterInfo[m_characterCount].topRight.x), Mathf.Max(m_meshExtents.max.y, m_textInfo.characterInfo[m_characterCount].topRight.y)); 4357 } 4358 k_SaveTextExtentMarker.End(); 4359 #endregion Track Text Extents 4360 4361 4362 // Save State of Mesh Creation for handling of Word Wrapping 4363 #region Save Word Wrapping State 4364 if ((m_TextWrappingMode != TextWrappingModes.NoWrap && m_TextWrappingMode != TextWrappingModes.PreserveWhitespaceNoWrap) || m_overflowMode == TextOverflowModes.Truncate || m_overflowMode == TextOverflowModes.Ellipsis || m_overflowMode == TextOverflowModes.Linked) 4365 { 4366 k_SaveProcessingStatesMarker.Begin(); 4367 4368 bool shouldSaveHardLineBreak = false; 4369 bool shouldSaveSoftLineBreak = false; 4370 4371 if ((isWhiteSpace || charCode == 0x200B || charCode == 0x2D || charCode == 0xAD) && (!m_isNonBreakingSpace || ignoreNonBreakingSpace) && charCode != 0xA0 && charCode != 0x2007 && charCode != 0x2011 && charCode != 0x202F && charCode != 0x2060) 4372 { 4373 // Ignore Hyphen (0x2D) when preceded by a whitespace 4374 if ((charCode == 0x2D && m_characterCount > 0 && char.IsWhiteSpace(m_textInfo.characterInfo[m_characterCount - 1].character)) == false) 4375 { 4376 isFirstWordOfLine = false; 4377 shouldSaveHardLineBreak = true; 4378 4379 // Reset soft line breaking point since we now have a valid hard break point. 4380 m_SavedSoftLineBreakState.previous_WordBreak = -1; 4381 } 4382 } 4383 // Handling for East Asian scripts 4384 else if (m_isNonBreakingSpace == false && (TMP_TextParsingUtilities.IsHangul(charCode) && TMP_Settings.useModernHangulLineBreakingRules == false || TMP_TextParsingUtilities.IsCJK(charCode))) 4385 { 4386 bool isCurrentLeadingCharacter = TMP_Settings.linebreakingRules.leadingCharacters.Contains(charCode); 4387 bool isNextFollowingCharacter = m_characterCount < totalCharacterCount - 1 && TMP_Settings.linebreakingRules.followingCharacters.Contains(m_textInfo.characterInfo[m_characterCount + 1].character); 4388 4389 if (isCurrentLeadingCharacter == false) 4390 { 4391 if (isNextFollowingCharacter == false) 4392 { 4393 isFirstWordOfLine = false; 4394 shouldSaveHardLineBreak = true; 4395 } 4396 4397 if (isFirstWordOfLine) 4398 { 4399 // Special handling for non-breaking space and soft line breaks 4400 if (isWhiteSpace) 4401 shouldSaveSoftLineBreak = true; 4402 4403 shouldSaveHardLineBreak = true; 4404 } 4405 } 4406 else 4407 { 4408 if (isFirstWordOfLine && isFirstCharacterOfLine) 4409 { 4410 // Special handling for non-breaking space and soft line breaks 4411 if (isWhiteSpace) 4412 shouldSaveSoftLineBreak = true; 4413 4414 shouldSaveHardLineBreak = true; 4415 } 4416 } 4417 } 4418 // Special handling for Latin characters followed by a CJK character. 4419 else if (m_isNonBreakingSpace == false && m_characterCount + 1 < totalCharacterCount && TMP_TextParsingUtilities.IsCJK(m_textInfo.characterInfo[m_characterCount + 1].character)) 4420 { 4421 shouldSaveHardLineBreak = true; 4422 } 4423 else if (isFirstWordOfLine) 4424 { 4425 // Special handling for non-breaking space and soft line breaks 4426 if (isWhiteSpace && charCode != 0xA0 || (charCode == 0xAD && isSoftHyphenIgnored == false)) 4427 shouldSaveSoftLineBreak = true; 4428 4429 shouldSaveHardLineBreak = true; 4430 } 4431 4432 // Save potential Hard lines break 4433 if (shouldSaveHardLineBreak) 4434 SaveWordWrappingState(ref m_SavedWordWrapState, i, m_characterCount); 4435 4436 // Save potential Soft line break 4437 if (shouldSaveSoftLineBreak) 4438 SaveWordWrappingState(ref m_SavedSoftLineBreakState, i, m_characterCount); 4439 4440 k_SaveProcessingStatesMarker.End(); 4441 } 4442 #endregion Save Word Wrapping State 4443 4444 // Consider only saving state on base glyphs 4445 SaveWordWrappingState(ref m_SavedLastValidState, i, m_characterCount); 4446 4447 m_characterCount += 1; 4448 } 4449 4450 // Check Auto Sizing and increase font size to fill text container. 4451 #region Check Auto-Sizing (Upper Font Size Bounds) 4452 fontSizeDelta = m_maxFontSize - m_minFontSize; 4453 if (/* !m_isCharacterWrappingEnabled && */ m_enableAutoSizing && fontSizeDelta > 0.051f && m_fontSize < m_fontSizeMax && m_AutoSizeIterationCount < m_AutoSizeMaxIterationCount) 4454 { 4455 // Reset character width adjustment delta 4456 if (m_charWidthAdjDelta < m_charWidthMaxAdj / 100) 4457 m_charWidthAdjDelta = 0; 4458 4459 m_minFontSize = m_fontSize; 4460 4461 float sizeDelta = Mathf.Max((m_maxFontSize - m_fontSize) / 2, 0.05f); 4462 m_fontSize += sizeDelta; 4463 m_fontSize = Mathf.Min((int)(m_fontSize * 20 + 0.5f) / 20f, m_fontSizeMax); 4464 4465 //Debug.Log("[" + m_AutoSizeIterationCount + "] Increasing Point Size from [" + m_minFontSize.ToString("f3") + "] to [" + m_fontSize.ToString("f3") + "] with delta of [" + sizeDelta.ToString("f3") + "]."); 4466 k_GenerateTextPhaseIMarker.End(); 4467 k_GenerateTextMarker.End(); 4468 return; 4469 } 4470 #endregion End Auto-sizing Check 4471 4472 m_IsAutoSizePointSizeSet = true; 4473 4474 if (m_AutoSizeIterationCount >= m_AutoSizeMaxIterationCount) 4475 Debug.Log("Auto Size Iteration Count: " + m_AutoSizeIterationCount + ". Final Point Size: " + m_fontSize); 4476 4477 // If there are no visible characters or only character is End of Text (0x03)... no need to continue 4478 if (m_characterCount == 0 || (m_characterCount == 1 && charCode == 0x03)) 4479 { 4480 ClearMesh(); 4481 4482 // Event indicating the text has been regenerated. 4483 TMPro_EventManager.ON_TEXT_CHANGED(this); 4484 k_GenerateTextPhaseIMarker.End(); 4485 k_GenerateTextMarker.End(); 4486 return; 4487 } 4488 4489 // End Sampling of Phase I 4490 k_GenerateTextPhaseIMarker.End(); 4491 4492 // *** PHASE II of Text Generation *** 4493 k_GenerateTextPhaseIIMarker.Begin(); 4494 int last_vert_index = m_materialReferences[m_Underline.materialIndex].referenceCount * 4; 4495 4496 // Partial clear of the vertices array to mark unused vertices as degenerate. 4497 m_textInfo.meshInfo[0].Clear(false); 4498 4499 // Handle Text Alignment 4500 #region Text Vertical Alignment 4501 Vector3 anchorOffset = Vector3.zero; 4502 Vector3[] corners = m_RectTransformCorners; // GetTextContainerLocalCorners(); 4503 4504 // Handle Vertical Text Alignment 4505 switch (m_VerticalAlignment) 4506 { 4507 // Top Vertically 4508 case VerticalAlignmentOptions.Top: 4509 if (m_overflowMode != TextOverflowModes.Page) 4510 anchorOffset = corners[1] + new Vector3(0 + margins.x, 0 - m_maxTextAscender - margins.y, 0); 4511 else 4512 anchorOffset = corners[1] + new Vector3(0 + margins.x, 0 - m_textInfo.pageInfo[pageToDisplay].ascender - margins.y, 0); 4513 break; 4514 4515 // Middle Vertically 4516 case VerticalAlignmentOptions.Middle: 4517 if (m_overflowMode != TextOverflowModes.Page) 4518 anchorOffset = (corners[0] + corners[1]) / 2 + new Vector3(0 + margins.x, 0 - (m_maxTextAscender + margins.y + maxVisibleDescender - margins.w) / 2, 0); 4519 else 4520 anchorOffset = (corners[0] + corners[1]) / 2 + new Vector3(0 + margins.x, 0 - (m_textInfo.pageInfo[pageToDisplay].ascender + margins.y + m_textInfo.pageInfo[pageToDisplay].descender - margins.w) / 2, 0); 4521 break; 4522 4523 // Bottom Vertically 4524 case VerticalAlignmentOptions.Bottom: 4525 if (m_overflowMode != TextOverflowModes.Page) 4526 anchorOffset = corners[0] + new Vector3(0 + margins.x, 0 - maxVisibleDescender + margins.w, 0); 4527 else 4528 anchorOffset = corners[0] + new Vector3(0 + margins.x, 0 - m_textInfo.pageInfo[pageToDisplay].descender + margins.w, 0); 4529 break; 4530 4531 // Baseline Vertically 4532 case VerticalAlignmentOptions.Baseline: 4533 anchorOffset = (corners[0] + corners[1]) / 2 + new Vector3(0 + margins.x, 0, 0); 4534 break; 4535 4536 // Midline Vertically 4537 case VerticalAlignmentOptions.Geometry: 4538 anchorOffset = (corners[0] + corners[1]) / 2 + new Vector3(0 + margins.x, 0 - (m_meshExtents.max.y + margins.y + m_meshExtents.min.y - margins.w) / 2, 0); 4539 break; 4540 4541 // Capline Vertically 4542 case VerticalAlignmentOptions.Capline: 4543 anchorOffset = (corners[0] + corners[1]) / 2 + new Vector3(0 + margins.x, 0 - (m_maxCapHeight - margins.y - margins.w) / 2, 0); 4544 break; 4545 } 4546 #endregion 4547 4548 // Initialization for Second Pass 4549 Vector3 justificationOffset = Vector3.zero; 4550 Vector3 offset = Vector3.zero; 4551 // int vert_index_X4 = 0; 4552 // int sprite_index_X4 = 0; 4553 4554 int wordCount = 0; 4555 int lineCount = 0; 4556 int lastLine = 0; 4557 bool isFirstSeperator = false; 4558 4559 bool isStartOfWord = false; 4560 int wordFirstChar = 0; 4561 int wordLastChar = 0; 4562 4563 // Second Pass : Line Justification, UV Mapping, Character & Line Visibility & more. 4564 // Variables used to handle Canvas Render Modes and SDF Scaling 4565 bool isCameraAssigned = m_canvas.worldCamera == null ? false : true; 4566 float lossyScale = m_previousLossyScaleY = this.transform.lossyScale.y; 4567 RenderMode canvasRenderMode = m_canvas.renderMode; 4568 float canvasScaleFactor = m_canvas.scaleFactor; 4569 4570 Color32 underlineColor = Color.white; 4571 Color32 strikethroughColor = Color.white; 4572 HighlightState highlightState = new HighlightState(new Color32(255, 255, 0, 64), TMP_Offset.zero); 4573 float xScale = 0; 4574 float xScaleMax = 0; 4575 float underlineStartScale = 0; 4576 float underlineEndScale = 0; 4577 float underlineMaxScale = 0; 4578 float underlineBaseLine = k_LargePositiveFloat; 4579 int lastPage = 0; 4580 4581 float strikethroughPointSize = 0; 4582 float strikethroughScale = 0; 4583 float strikethroughBaseline = 0; 4584 4585 TMP_CharacterInfo[] characterInfos = m_textInfo.characterInfo; 4586 #region Handle Line Justification & UV Mapping & Character Visibility & More 4587 for (int i = 0; i < m_characterCount; i++) 4588 { 4589 TMP_FontAsset currentFontAsset = characterInfos[i].fontAsset; 4590 4591 char unicode = characterInfos[i].character; 4592 bool isWhiteSpace = char.IsWhiteSpace(unicode); 4593 4594 int currentLine = characterInfos[i].lineNumber; 4595 TMP_LineInfo lineInfo = m_textInfo.lineInfo[currentLine]; 4596 lineCount = currentLine + 1; 4597 4598 HorizontalAlignmentOptions lineAlignment = lineInfo.alignment; 4599 4600 // Process Line Justification 4601 #region Handle Line Justification 4602 switch (lineAlignment) 4603 { 4604 case HorizontalAlignmentOptions.Left: 4605 if (!m_isRightToLeft) 4606 justificationOffset = new Vector3(0 + lineInfo.marginLeft, 0, 0); 4607 else 4608 justificationOffset = new Vector3(0 - lineInfo.maxAdvance, 0, 0); 4609 break; 4610 4611 case HorizontalAlignmentOptions.Center: 4612 justificationOffset = new Vector3(lineInfo.marginLeft + lineInfo.width / 2 - lineInfo.maxAdvance / 2, 0, 0); 4613 break; 4614 4615 case HorizontalAlignmentOptions.Geometry: 4616 justificationOffset = new Vector3(lineInfo.marginLeft + lineInfo.width / 2 - (lineInfo.lineExtents.min.x + lineInfo.lineExtents.max.x) / 2, 0, 0); 4617 break; 4618 4619 case HorizontalAlignmentOptions.Right: 4620 if (!m_isRightToLeft) 4621 justificationOffset = new Vector3(lineInfo.marginLeft + lineInfo.width - lineInfo.maxAdvance, 0, 0); 4622 else 4623 justificationOffset = new Vector3(lineInfo.marginLeft + lineInfo.width, 0, 0); 4624 break; 4625 4626 case HorizontalAlignmentOptions.Justified: 4627 case HorizontalAlignmentOptions.Flush: 4628 // Skip Zero Width Characters and spaces outside of the margins. 4629 if (i > lineInfo.lastVisibleCharacterIndex || unicode == 0x0A || unicode == 0xAD || unicode == 0x200B || unicode == 0x2060 || unicode == 0x03) break; 4630 4631 char lastCharOfCurrentLine = characterInfos[lineInfo.lastCharacterIndex].character; 4632 4633 bool isFlush = (lineAlignment & HorizontalAlignmentOptions.Flush) == HorizontalAlignmentOptions.Flush; 4634 4635 // In Justified mode, all lines are justified except the last one. 4636 // In Flush mode, all lines are justified. 4637 if (char.IsControl(lastCharOfCurrentLine) == false && currentLine < m_lineNumber || isFlush || lineInfo.maxAdvance > lineInfo.width) 4638 { 4639 // First character of each line. 4640 if (currentLine != lastLine || i == 0 || i == m_firstVisibleCharacter) 4641 { 4642 if (!m_isRightToLeft) 4643 justificationOffset = new Vector3(lineInfo.marginLeft, 0, 0); 4644 else 4645 justificationOffset = new Vector3(lineInfo.marginLeft + lineInfo.width, 0, 0); 4646 4647 if (char.IsSeparator(unicode)) 4648 isFirstSeperator = true; 4649 else 4650 isFirstSeperator = false; 4651 } 4652 else 4653 { 4654 float gap = !m_isRightToLeft ? lineInfo.width - lineInfo.maxAdvance : lineInfo.width + lineInfo.maxAdvance; 4655 int visibleCount = lineInfo.visibleCharacterCount - 1 + lineInfo.controlCharacterCount; 4656 int spaces = lineInfo.spaceCount - lineInfo.controlCharacterCount; 4657 4658 if (isFirstSeperator) { spaces -= 1; visibleCount += 1; } 4659 4660 float ratio = spaces > 0 ? m_wordWrappingRatios : 1; 4661 4662 if (spaces < 1) spaces = 1; 4663 4664 if (unicode != 0xA0 && (unicode == 9 || char.IsSeparator(unicode))) 4665 { 4666 if (!m_isRightToLeft) 4667 justificationOffset += new Vector3(gap * (1 - ratio) / spaces, 0, 0); 4668 else 4669 justificationOffset -= new Vector3(gap * (1 - ratio) / spaces, 0, 0); 4670 } 4671 else 4672 { 4673 if (!m_isRightToLeft) 4674 justificationOffset += new Vector3(gap * ratio / visibleCount, 0, 0); 4675 else 4676 justificationOffset -= new Vector3(gap * ratio / visibleCount, 0, 0); 4677 } 4678 } 4679 } 4680 else 4681 { 4682 if (!m_isRightToLeft) 4683 justificationOffset = new Vector3(lineInfo.marginLeft, 0, 0); // Keep last line left justified. 4684 else 4685 justificationOffset = new Vector3(lineInfo.marginLeft + lineInfo.width, 0, 0); // Keep last line right justified. 4686 } 4687 //Debug.Log("Char [" + (char)charCode + "] Code:" + charCode + " Line # " + currentLine + " Offset:" + justificationOffset + " # Spaces:" + lineInfo.spaceCount + " # Characters:" + lineInfo.characterCount); 4688 break; 4689 } 4690 #endregion End Text Justification 4691 4692 offset = anchorOffset + justificationOffset; 4693 4694 // Handle UV2 mapping options and packing of scale information into UV2. 4695 #region Handling of UV2 mapping & Scale packing 4696 bool isCharacterVisible = characterInfos[i].isVisible; 4697 if (isCharacterVisible) 4698 { 4699 TMP_TextElementType elementType = characterInfos[i].elementType; 4700 switch (elementType) 4701 { 4702 // CHARACTERS 4703 case TMP_TextElementType.Character: 4704 Extents lineExtents = lineInfo.lineExtents; 4705 float uvOffset = (m_uvLineOffset * currentLine) % 1; // + m_uvOffset.x; 4706 4707 // Setup UV2 based on Character Mapping Options Selected 4708 #region Handle UV Mapping Options 4709 switch (m_horizontalMapping) 4710 { 4711 case TextureMappingOptions.Character: 4712 characterInfos[i].vertex_BL.uv2.x = 0; //+ m_uvOffset.x; 4713 characterInfos[i].vertex_TL.uv2.x = 0; //+ m_uvOffset.x; 4714 characterInfos[i].vertex_TR.uv2.x = 1; //+ m_uvOffset.x; 4715 characterInfos[i].vertex_BR.uv2.x = 1; //+ m_uvOffset.x; 4716 break; 4717 4718 case TextureMappingOptions.Line: 4719 if (m_textAlignment != TextAlignmentOptions.Justified) 4720 { 4721 characterInfos[i].vertex_BL.uv2.x = (characterInfos[i].vertex_BL.position.x - lineExtents.min.x) / (lineExtents.max.x - lineExtents.min.x) + uvOffset; 4722 characterInfos[i].vertex_TL.uv2.x = (characterInfos[i].vertex_TL.position.x - lineExtents.min.x) / (lineExtents.max.x - lineExtents.min.x) + uvOffset; 4723 characterInfos[i].vertex_TR.uv2.x = (characterInfos[i].vertex_TR.position.x - lineExtents.min.x) / (lineExtents.max.x - lineExtents.min.x) + uvOffset; 4724 characterInfos[i].vertex_BR.uv2.x = (characterInfos[i].vertex_BR.position.x - lineExtents.min.x) / (lineExtents.max.x - lineExtents.min.x) + uvOffset; 4725 break; 4726 } 4727 else // Special Case if Justified is used in Line Mode. 4728 { 4729 characterInfos[i].vertex_BL.uv2.x = (characterInfos[i].vertex_BL.position.x + justificationOffset.x - m_meshExtents.min.x) / (m_meshExtents.max.x - m_meshExtents.min.x) + uvOffset; 4730 characterInfos[i].vertex_TL.uv2.x = (characterInfos[i].vertex_TL.position.x + justificationOffset.x - m_meshExtents.min.x) / (m_meshExtents.max.x - m_meshExtents.min.x) + uvOffset; 4731 characterInfos[i].vertex_TR.uv2.x = (characterInfos[i].vertex_TR.position.x + justificationOffset.x - m_meshExtents.min.x) / (m_meshExtents.max.x - m_meshExtents.min.x) + uvOffset; 4732 characterInfos[i].vertex_BR.uv2.x = (characterInfos[i].vertex_BR.position.x + justificationOffset.x - m_meshExtents.min.x) / (m_meshExtents.max.x - m_meshExtents.min.x) + uvOffset; 4733 break; 4734 } 4735 4736 case TextureMappingOptions.Paragraph: 4737 characterInfos[i].vertex_BL.uv2.x = (characterInfos[i].vertex_BL.position.x + justificationOffset.x - m_meshExtents.min.x) / (m_meshExtents.max.x - m_meshExtents.min.x) + uvOffset; 4738 characterInfos[i].vertex_TL.uv2.x = (characterInfos[i].vertex_TL.position.x + justificationOffset.x - m_meshExtents.min.x) / (m_meshExtents.max.x - m_meshExtents.min.x) + uvOffset; 4739 characterInfos[i].vertex_TR.uv2.x = (characterInfos[i].vertex_TR.position.x + justificationOffset.x - m_meshExtents.min.x) / (m_meshExtents.max.x - m_meshExtents.min.x) + uvOffset; 4740 characterInfos[i].vertex_BR.uv2.x = (characterInfos[i].vertex_BR.position.x + justificationOffset.x - m_meshExtents.min.x) / (m_meshExtents.max.x - m_meshExtents.min.x) + uvOffset; 4741 break; 4742 4743 case TextureMappingOptions.MatchAspect: 4744 4745 switch (m_verticalMapping) 4746 { 4747 case TextureMappingOptions.Character: 4748 characterInfos[i].vertex_BL.uv2.y = 0; // + m_uvOffset.y; 4749 characterInfos[i].vertex_TL.uv2.y = 1; // + m_uvOffset.y; 4750 characterInfos[i].vertex_TR.uv2.y = 0; // + m_uvOffset.y; 4751 characterInfos[i].vertex_BR.uv2.y = 1; // + m_uvOffset.y; 4752 break; 4753 4754 case TextureMappingOptions.Line: 4755 characterInfos[i].vertex_BL.uv2.y = (characterInfos[i].vertex_BL.position.y - lineExtents.min.y) / (lineExtents.max.y - lineExtents.min.y) + uvOffset; 4756 characterInfos[i].vertex_TL.uv2.y = (characterInfos[i].vertex_TL.position.y - lineExtents.min.y) / (lineExtents.max.y - lineExtents.min.y) + uvOffset; 4757 characterInfos[i].vertex_TR.uv2.y = characterInfos[i].vertex_BL.uv2.y; 4758 characterInfos[i].vertex_BR.uv2.y = characterInfos[i].vertex_TL.uv2.y; 4759 break; 4760 4761 case TextureMappingOptions.Paragraph: 4762 characterInfos[i].vertex_BL.uv2.y = (characterInfos[i].vertex_BL.position.y - m_meshExtents.min.y) / (m_meshExtents.max.y - m_meshExtents.min.y) + uvOffset; 4763 characterInfos[i].vertex_TL.uv2.y = (characterInfos[i].vertex_TL.position.y - m_meshExtents.min.y) / (m_meshExtents.max.y - m_meshExtents.min.y) + uvOffset; 4764 characterInfos[i].vertex_TR.uv2.y = characterInfos[i].vertex_BL.uv2.y; 4765 characterInfos[i].vertex_BR.uv2.y = characterInfos[i].vertex_TL.uv2.y; 4766 break; 4767 4768 case TextureMappingOptions.MatchAspect: 4769 Debug.Log("ERROR: Cannot Match both Vertical & Horizontal."); 4770 break; 4771 } 4772 4773 //float xDelta = 1 - (_uv2s[vert_index + 0].y * textMeshCharacterInfo[i].AspectRatio); // Left aligned 4774 float xDelta = (1 - ((characterInfos[i].vertex_BL.uv2.y + characterInfos[i].vertex_TL.uv2.y) * characterInfos[i].aspectRatio)) / 2; // Center of Rectangle 4775 4776 characterInfos[i].vertex_BL.uv2.x = (characterInfos[i].vertex_BL.uv2.y * characterInfos[i].aspectRatio) + xDelta + uvOffset; 4777 characterInfos[i].vertex_TL.uv2.x = characterInfos[i].vertex_BL.uv2.x; 4778 characterInfos[i].vertex_TR.uv2.x = (characterInfos[i].vertex_TL.uv2.y * characterInfos[i].aspectRatio) + xDelta + uvOffset; 4779 characterInfos[i].vertex_BR.uv2.x = characterInfos[i].vertex_TR.uv2.x; 4780 break; 4781 } 4782 4783 switch (m_verticalMapping) 4784 { 4785 case TextureMappingOptions.Character: 4786 characterInfos[i].vertex_BL.uv2.y = 0; // + m_uvOffset.y; 4787 characterInfos[i].vertex_TL.uv2.y = 1; // + m_uvOffset.y; 4788 characterInfos[i].vertex_TR.uv2.y = 1; // + m_uvOffset.y; 4789 characterInfos[i].vertex_BR.uv2.y = 0; // + m_uvOffset.y; 4790 break; 4791 4792 case TextureMappingOptions.Line: 4793 characterInfos[i].vertex_BL.uv2.y = (characterInfos[i].vertex_BL.position.y - lineInfo.descender) / (lineInfo.ascender - lineInfo.descender); // + m_uvOffset.y; 4794 characterInfos[i].vertex_TL.uv2.y = (characterInfos[i].vertex_TL.position.y - lineInfo.descender) / (lineInfo.ascender - lineInfo.descender); // + m_uvOffset.y; 4795 characterInfos[i].vertex_TR.uv2.y = characterInfos[i].vertex_TL.uv2.y; 4796 characterInfos[i].vertex_BR.uv2.y = characterInfos[i].vertex_BL.uv2.y; 4797 break; 4798 4799 case TextureMappingOptions.Paragraph: 4800 characterInfos[i].vertex_BL.uv2.y = (characterInfos[i].vertex_BL.position.y - m_meshExtents.min.y) / (m_meshExtents.max.y - m_meshExtents.min.y); // + m_uvOffset.y; 4801 characterInfos[i].vertex_TL.uv2.y = (characterInfos[i].vertex_TL.position.y - m_meshExtents.min.y) / (m_meshExtents.max.y - m_meshExtents.min.y); // + m_uvOffset.y; 4802 characterInfos[i].vertex_TR.uv2.y = characterInfos[i].vertex_TL.uv2.y; 4803 characterInfos[i].vertex_BR.uv2.y = characterInfos[i].vertex_BL.uv2.y; 4804 break; 4805 4806 case TextureMappingOptions.MatchAspect: 4807 float yDelta = (1 - ((characterInfos[i].vertex_BL.uv2.x + characterInfos[i].vertex_TR.uv2.x) / characterInfos[i].aspectRatio)) / 2; // Center of Rectangle 4808 4809 characterInfos[i].vertex_BL.uv2.y = yDelta + (characterInfos[i].vertex_BL.uv2.x / characterInfos[i].aspectRatio); // + m_uvOffset.y; 4810 characterInfos[i].vertex_TL.uv2.y = yDelta + (characterInfos[i].vertex_TR.uv2.x / characterInfos[i].aspectRatio); // + m_uvOffset.y; 4811 characterInfos[i].vertex_BR.uv2.y = characterInfos[i].vertex_BL.uv2.y; 4812 characterInfos[i].vertex_TR.uv2.y = characterInfos[i].vertex_TL.uv2.y; 4813 break; 4814 } 4815 #endregion 4816 4817 // Pack UV's so that we can pass Xscale needed for Shader to maintain 1:1 ratio. 4818 #region Pack Scale into UV2 4819 xScale = characterInfos[i].scale * (1 - m_charWidthAdjDelta); 4820 if (!characterInfos[i].isUsingAlternateTypeface && (characterInfos[i].style & FontStyles.Bold) == FontStyles.Bold) xScale *= -1; 4821 4822 switch (canvasRenderMode) 4823 { 4824 case RenderMode.ScreenSpaceOverlay: 4825 xScale *= Mathf.Abs(lossyScale) / canvasScaleFactor; 4826 break; 4827 case RenderMode.ScreenSpaceCamera: 4828 xScale *= isCameraAssigned ? Mathf.Abs(lossyScale) : 1; 4829 break; 4830 case RenderMode.WorldSpace: 4831 xScale *= Mathf.Abs(lossyScale); 4832 break; 4833 } 4834 4835 // Set SDF Scale 4836 characterInfos[i].vertex_BL.uv.w = xScale; 4837 characterInfos[i].vertex_TL.uv.w = xScale; 4838 characterInfos[i].vertex_TR.uv.w = xScale; 4839 characterInfos[i].vertex_BR.uv.w = xScale; 4840 #endregion 4841 break; 4842 4843 // SPRITES 4844 case TMP_TextElementType.Sprite: 4845 // Nothing right now 4846 break; 4847 } 4848 4849 // Handle maxVisibleCharacters, maxVisibleLines and Overflow Page Mode. 4850 #region Handle maxVisibleCharacters / maxVisibleLines / Page Mode 4851 if (i < m_maxVisibleCharacters && wordCount < m_maxVisibleWords && currentLine < m_maxVisibleLines && m_overflowMode != TextOverflowModes.Page) 4852 { 4853 characterInfos[i].vertex_BL.position += offset; 4854 characterInfos[i].vertex_TL.position += offset; 4855 characterInfos[i].vertex_TR.position += offset; 4856 characterInfos[i].vertex_BR.position += offset; 4857 } 4858 else if (i < m_maxVisibleCharacters && wordCount < m_maxVisibleWords && currentLine < m_maxVisibleLines && m_overflowMode == TextOverflowModes.Page && characterInfos[i].pageNumber == pageToDisplay) 4859 { 4860 characterInfos[i].vertex_BL.position += offset; 4861 characterInfos[i].vertex_TL.position += offset; 4862 characterInfos[i].vertex_TR.position += offset; 4863 characterInfos[i].vertex_BR.position += offset; 4864 } 4865 else 4866 { 4867 characterInfos[i].vertex_BL.position = Vector3.zero; 4868 characterInfos[i].vertex_TL.position = Vector3.zero; 4869 characterInfos[i].vertex_TR.position = Vector3.zero; 4870 characterInfos[i].vertex_BR.position = Vector3.zero; 4871 characterInfos[i].isVisible = false; 4872 } 4873 #endregion 4874 4875 4876 // Fill Vertex Buffers for the various types of element 4877 if (elementType == TMP_TextElementType.Character) 4878 { 4879 FillCharacterVertexBuffers(i); 4880 } 4881 else if (elementType == TMP_TextElementType.Sprite) 4882 { 4883 FillSpriteVertexBuffers(i); 4884 } 4885 } 4886 #endregion 4887 4888 // Apply Alignment and Justification Offset 4889 m_textInfo.characterInfo[i].bottomLeft += offset; 4890 m_textInfo.characterInfo[i].topLeft += offset; 4891 m_textInfo.characterInfo[i].topRight += offset; 4892 m_textInfo.characterInfo[i].bottomRight += offset; 4893 4894 m_textInfo.characterInfo[i].origin += offset.x; 4895 m_textInfo.characterInfo[i].xAdvance += offset.x; 4896 4897 m_textInfo.characterInfo[i].ascender += offset.y; 4898 m_textInfo.characterInfo[i].descender += offset.y; 4899 m_textInfo.characterInfo[i].baseLine += offset.y; 4900 4901 // Update MeshExtents 4902 if (isCharacterVisible) 4903 { 4904 //m_meshExtents.min = new Vector2(Mathf.Min(m_meshExtents.min.x, m_textInfo.characterInfo[i].bottomLeft.x), Mathf.Min(m_meshExtents.min.y, m_textInfo.characterInfo[i].bottomLeft.y)); 4905 //m_meshExtents.max = new Vector2(Mathf.Max(m_meshExtents.max.x, m_textInfo.characterInfo[i].topRight.x), Mathf.Max(m_meshExtents.max.y, m_textInfo.characterInfo[i].topLeft.y)); 4906 } 4907 4908 // Need to recompute lineExtent to account for the offset from justification. 4909 #region Adjust lineExtents resulting from alignment offset 4910 if (currentLine != lastLine || i == m_characterCount - 1) 4911 { 4912 // Update the previous line's extents 4913 if (currentLine != lastLine) 4914 { 4915 m_textInfo.lineInfo[lastLine].baseline += offset.y; 4916 m_textInfo.lineInfo[lastLine].ascender += offset.y; 4917 m_textInfo.lineInfo[lastLine].descender += offset.y; 4918 4919 m_textInfo.lineInfo[lastLine].maxAdvance += offset.x; 4920 4921 m_textInfo.lineInfo[lastLine].lineExtents.min = new Vector2(m_textInfo.characterInfo[m_textInfo.lineInfo[lastLine].firstCharacterIndex].bottomLeft.x, m_textInfo.lineInfo[lastLine].descender); 4922 m_textInfo.lineInfo[lastLine].lineExtents.max = new Vector2(m_textInfo.characterInfo[m_textInfo.lineInfo[lastLine].lastVisibleCharacterIndex].topRight.x, m_textInfo.lineInfo[lastLine].ascender); 4923 } 4924 4925 // Update the current line's extents 4926 if (i == m_characterCount - 1) 4927 { 4928 m_textInfo.lineInfo[currentLine].baseline += offset.y; 4929 m_textInfo.lineInfo[currentLine].ascender += offset.y; 4930 m_textInfo.lineInfo[currentLine].descender += offset.y; 4931 4932 m_textInfo.lineInfo[currentLine].maxAdvance += offset.x; 4933 4934 m_textInfo.lineInfo[currentLine].lineExtents.min = new Vector2(m_textInfo.characterInfo[m_textInfo.lineInfo[currentLine].firstCharacterIndex].bottomLeft.x, m_textInfo.lineInfo[currentLine].descender); 4935 m_textInfo.lineInfo[currentLine].lineExtents.max = new Vector2(m_textInfo.characterInfo[m_textInfo.lineInfo[currentLine].lastVisibleCharacterIndex].topRight.x, m_textInfo.lineInfo[currentLine].ascender); 4936 } 4937 } 4938 #endregion 4939 4940 4941 // Track Word Count per line and for the object 4942 #region Track Word Count 4943 if (char.IsLetterOrDigit(unicode) || unicode == 0x2D || unicode == 0xAD || unicode == 0x2010 || unicode == 0x2011) 4944 { 4945 if (isStartOfWord == false) 4946 { 4947 isStartOfWord = true; 4948 wordFirstChar = i; 4949 } 4950 4951 // If last character is a word 4952 if (isStartOfWord && i == m_characterCount - 1) 4953 { 4954 int size = m_textInfo.wordInfo.Length; 4955 int index = m_textInfo.wordCount; 4956 4957 if (m_textInfo.wordCount + 1 > size) 4958 TMP_TextInfo.Resize(ref m_textInfo.wordInfo, size + 1); 4959 4960 wordLastChar = i; 4961 4962 m_textInfo.wordInfo[index].firstCharacterIndex = wordFirstChar; 4963 m_textInfo.wordInfo[index].lastCharacterIndex = wordLastChar; 4964 m_textInfo.wordInfo[index].characterCount = wordLastChar - wordFirstChar + 1; 4965 m_textInfo.wordInfo[index].textComponent = this; 4966 4967 wordCount += 1; 4968 m_textInfo.wordCount += 1; 4969 m_textInfo.lineInfo[currentLine].wordCount += 1; 4970 } 4971 } 4972 else if (isStartOfWord || i == 0 && (!char.IsPunctuation(unicode) || isWhiteSpace || unicode == 0x200B || i == m_characterCount - 1)) 4973 { 4974 if (i > 0 && i < characterInfos.Length - 1 && i < m_characterCount && (unicode == 39 || unicode == 8217) && char.IsLetterOrDigit(characterInfos[i - 1].character) && char.IsLetterOrDigit(characterInfos[i + 1].character)) 4975 { 4976 4977 } 4978 else 4979 { 4980 wordLastChar = i == m_characterCount - 1 && char.IsLetterOrDigit(unicode) ? i : i - 1; 4981 isStartOfWord = false; 4982 4983 int size = m_textInfo.wordInfo.Length; 4984 int index = m_textInfo.wordCount; 4985 4986 if (m_textInfo.wordCount + 1 > size) 4987 TMP_TextInfo.Resize(ref m_textInfo.wordInfo, size + 1); 4988 4989 m_textInfo.wordInfo[index].firstCharacterIndex = wordFirstChar; 4990 m_textInfo.wordInfo[index].lastCharacterIndex = wordLastChar; 4991 m_textInfo.wordInfo[index].characterCount = wordLastChar - wordFirstChar + 1; 4992 m_textInfo.wordInfo[index].textComponent = this; 4993 4994 wordCount += 1; 4995 m_textInfo.wordCount += 1; 4996 m_textInfo.lineInfo[currentLine].wordCount += 1; 4997 } 4998 } 4999 #endregion 5000 5001 5002 // Setup & Handle Underline 5003 #region Underline 5004 // NOTE: Need to figure out how underline will be handled with multiple fonts and which font will be used for the underline. 5005 bool isUnderline = (m_textInfo.characterInfo[i].style & FontStyles.Underline) == FontStyles.Underline; 5006 if (isUnderline) 5007 { 5008 bool isUnderlineVisible = true; 5009 int currentPage = m_textInfo.characterInfo[i].pageNumber; 5010 m_textInfo.characterInfo[i].underlineVertexIndex = last_vert_index; 5011 5012 if (i > m_maxVisibleCharacters || currentLine > m_maxVisibleLines || (m_overflowMode == TextOverflowModes.Page && currentPage + 1 != m_pageToDisplay)) 5013 isUnderlineVisible = false; 5014 5015 // We only use the scale of visible characters. 5016 if (!isWhiteSpace && unicode != 0x200B) 5017 { 5018 underlineMaxScale = Mathf.Max(underlineMaxScale, m_textInfo.characterInfo[i].scale); 5019 xScaleMax = Mathf.Max(xScaleMax, Mathf.Abs(xScale)); 5020 underlineBaseLine = Mathf.Min(currentPage == lastPage ? underlineBaseLine : k_LargePositiveFloat, m_textInfo.characterInfo[i].baseLine + font.m_FaceInfo.underlineOffset * underlineMaxScale); 5021 lastPage = currentPage; // Need to track pages to ensure we reset baseline for the new pages. 5022 } 5023 5024 if (beginUnderline == false && isUnderlineVisible == true && i <= lineInfo.lastVisibleCharacterIndex && unicode != 10 && unicode != 11 && unicode != 13) 5025 { 5026 if (i == lineInfo.lastVisibleCharacterIndex && char.IsSeparator(unicode)) 5027 { } 5028 else 5029 { 5030 beginUnderline = true; 5031 underlineStartScale = m_textInfo.characterInfo[i].scale; 5032 if (underlineMaxScale == 0) 5033 { 5034 underlineMaxScale = underlineStartScale; 5035 xScaleMax = xScale; 5036 } 5037 underline_start = new Vector3(m_textInfo.characterInfo[i].bottomLeft.x, underlineBaseLine, 0); 5038 underlineColor = m_textInfo.characterInfo[i].underlineColor; 5039 } 5040 } 5041 5042 // End Underline if text only contains one character. 5043 if (beginUnderline && m_characterCount == 1) 5044 { 5045 beginUnderline = false; 5046 underline_end = new Vector3(m_textInfo.characterInfo[i].topRight.x, underlineBaseLine, 0); 5047 underlineEndScale = m_textInfo.characterInfo[i].scale; 5048 5049 DrawUnderlineMesh(underline_start, underline_end, ref last_vert_index, underlineStartScale, underlineEndScale, underlineMaxScale, xScaleMax, underlineColor); 5050 underlineMaxScale = 0; 5051 xScaleMax = 0; 5052 underlineBaseLine = k_LargePositiveFloat; 5053 } 5054 else if (beginUnderline && (i == lineInfo.lastCharacterIndex || i >= lineInfo.lastVisibleCharacterIndex)) 5055 { 5056 // Terminate underline at previous visible character if space or carriage return. 5057 if (isWhiteSpace || unicode == 0x200B) 5058 { 5059 int lastVisibleCharacterIndex = lineInfo.lastVisibleCharacterIndex; 5060 underline_end = new Vector3(m_textInfo.characterInfo[lastVisibleCharacterIndex].topRight.x, underlineBaseLine, 0); 5061 underlineEndScale = m_textInfo.characterInfo[lastVisibleCharacterIndex].scale; 5062 } 5063 else 5064 { // End underline if last character of the line. 5065 underline_end = new Vector3(m_textInfo.characterInfo[i].topRight.x, underlineBaseLine, 0); 5066 underlineEndScale = m_textInfo.characterInfo[i].scale; 5067 } 5068 5069 beginUnderline = false; 5070 DrawUnderlineMesh(underline_start, underline_end, ref last_vert_index, underlineStartScale, underlineEndScale, underlineMaxScale, xScaleMax, underlineColor); 5071 underlineMaxScale = 0; 5072 xScaleMax = 0; 5073 underlineBaseLine = k_LargePositiveFloat; 5074 } 5075 else if (beginUnderline && !isUnderlineVisible) 5076 { 5077 beginUnderline = false; 5078 underline_end = new Vector3(m_textInfo.characterInfo[i - 1].topRight.x, underlineBaseLine, 0); 5079 underlineEndScale = m_textInfo.characterInfo[i - 1].scale; 5080 5081 DrawUnderlineMesh(underline_start, underline_end, ref last_vert_index, underlineStartScale, underlineEndScale, underlineMaxScale, xScaleMax, underlineColor); 5082 underlineMaxScale = 0; 5083 xScaleMax = 0; 5084 underlineBaseLine = k_LargePositiveFloat; 5085 } 5086 else if (beginUnderline && i < m_characterCount - 1 && !underlineColor.Compare(m_textInfo.characterInfo[i + 1].underlineColor)) 5087 { 5088 // End underline if underline color has changed. 5089 beginUnderline = false; 5090 underline_end = new Vector3(m_textInfo.characterInfo[i].topRight.x, underlineBaseLine, 0); 5091 underlineEndScale = m_textInfo.characterInfo[i].scale; 5092 5093 DrawUnderlineMesh(underline_start, underline_end, ref last_vert_index, underlineStartScale, underlineEndScale, underlineMaxScale, xScaleMax, underlineColor); 5094 underlineMaxScale = 0; 5095 xScaleMax = 0; 5096 underlineBaseLine = k_LargePositiveFloat; 5097 } 5098 } 5099 else 5100 { 5101 // End Underline 5102 if (beginUnderline == true) 5103 { 5104 beginUnderline = false; 5105 underline_end = new Vector3(m_textInfo.characterInfo[i - 1].topRight.x, underlineBaseLine, 0); 5106 underlineEndScale = m_textInfo.characterInfo[i - 1].scale; 5107 5108 DrawUnderlineMesh(underline_start, underline_end, ref last_vert_index, underlineStartScale, underlineEndScale, underlineMaxScale, xScaleMax, underlineColor); 5109 underlineMaxScale = 0; 5110 xScaleMax = 0; 5111 underlineBaseLine = k_LargePositiveFloat; 5112 } 5113 } 5114 #endregion 5115 5116 5117 // Setup & Handle Strikethrough 5118 #region Strikethrough 5119 // NOTE: Need to figure out how underline will be handled with multiple fonts and which font will be used for the underline. 5120 bool isStrikethrough = (m_textInfo.characterInfo[i].style & FontStyles.Strikethrough) == FontStyles.Strikethrough; 5121 float strikethroughOffset = currentFontAsset.m_FaceInfo.strikethroughOffset; 5122 5123 if (isStrikethrough) 5124 { 5125 bool isStrikeThroughVisible = true; 5126 m_textInfo.characterInfo[i].strikethroughVertexIndex = last_vert_index; 5127 5128 if (i > m_maxVisibleCharacters || currentLine > m_maxVisibleLines || (m_overflowMode == TextOverflowModes.Page && m_textInfo.characterInfo[i].pageNumber + 1 != m_pageToDisplay)) 5129 isStrikeThroughVisible = false; 5130 5131 if (beginStrikethrough == false && isStrikeThroughVisible && i <= lineInfo.lastVisibleCharacterIndex && unicode != 10 && unicode != 11 && unicode != 13) 5132 { 5133 if (i == lineInfo.lastVisibleCharacterIndex && char.IsSeparator(unicode)) 5134 { } 5135 else 5136 { 5137 beginStrikethrough = true; 5138 strikethroughPointSize = m_textInfo.characterInfo[i].pointSize; 5139 strikethroughScale = m_textInfo.characterInfo[i].scale; 5140 strikethrough_start = new Vector3(m_textInfo.characterInfo[i].bottomLeft.x, m_textInfo.characterInfo[i].baseLine + strikethroughOffset * strikethroughScale, 0); 5141 strikethroughColor = m_textInfo.characterInfo[i].strikethroughColor; 5142 strikethroughBaseline = m_textInfo.characterInfo[i].baseLine; 5143 //Debug.Log("Char [" + currentCharacter + "] Start Strikethrough POS: " + strikethrough_start); 5144 } 5145 } 5146 5147 // End Strikethrough if text only contains one character. 5148 if (beginStrikethrough && m_characterCount == 1) 5149 { 5150 beginStrikethrough = false; 5151 strikethrough_end = new Vector3(m_textInfo.characterInfo[i].topRight.x, m_textInfo.characterInfo[i].baseLine + strikethroughOffset * strikethroughScale, 0); 5152 5153 DrawUnderlineMesh(strikethrough_start, strikethrough_end, ref last_vert_index, strikethroughScale, strikethroughScale, strikethroughScale, xScale, strikethroughColor); 5154 } 5155 else if (beginStrikethrough && i == lineInfo.lastCharacterIndex) 5156 { 5157 // Terminate Strikethrough at previous visible character if space or carriage return. 5158 if (isWhiteSpace || unicode == 0x200B) 5159 { 5160 int lastVisibleCharacterIndex = lineInfo.lastVisibleCharacterIndex; 5161 strikethrough_end = new Vector3(m_textInfo.characterInfo[lastVisibleCharacterIndex].topRight.x, m_textInfo.characterInfo[lastVisibleCharacterIndex].baseLine + strikethroughOffset * strikethroughScale, 0); 5162 } 5163 else 5164 { 5165 // Terminate Strikethrough at last character of line. 5166 strikethrough_end = new Vector3(m_textInfo.characterInfo[i].topRight.x, m_textInfo.characterInfo[i].baseLine + strikethroughOffset * strikethroughScale, 0); 5167 } 5168 5169 beginStrikethrough = false; 5170 DrawUnderlineMesh(strikethrough_start, strikethrough_end, ref last_vert_index, strikethroughScale, strikethroughScale, strikethroughScale, xScale, strikethroughColor); 5171 } 5172 else if (beginStrikethrough && i < m_characterCount && (m_textInfo.characterInfo[i + 1].pointSize != strikethroughPointSize || !TMP_Math.Approximately(m_textInfo.characterInfo[i + 1].baseLine + offset.y, strikethroughBaseline))) 5173 { 5174 // Terminate Strikethrough if scale changes. 5175 beginStrikethrough = false; 5176 5177 int lastVisibleCharacterIndex = lineInfo.lastVisibleCharacterIndex; 5178 if (i > lastVisibleCharacterIndex) 5179 strikethrough_end = new Vector3(m_textInfo.characterInfo[lastVisibleCharacterIndex].topRight.x, m_textInfo.characterInfo[lastVisibleCharacterIndex].baseLine + strikethroughOffset * strikethroughScale, 0); 5180 else 5181 strikethrough_end = new Vector3(m_textInfo.characterInfo[i].topRight.x, m_textInfo.characterInfo[i].baseLine + strikethroughOffset * strikethroughScale, 0); 5182 5183 DrawUnderlineMesh(strikethrough_start, strikethrough_end, ref last_vert_index, strikethroughScale, strikethroughScale, strikethroughScale, xScale, strikethroughColor); 5184 //Debug.Log("Char [" + currentCharacter + "] at Index: " + i + " End Strikethrough POS: " + strikethrough_end + " Baseline: " + m_textInfo.characterInfo[i].baseLine.ToString("f3")); 5185 } 5186 else if (beginStrikethrough && i < m_characterCount && currentFontAsset.GetInstanceID() != characterInfos[i + 1].fontAsset.GetInstanceID()) 5187 { 5188 // Terminate Strikethrough if font asset changes. 5189 beginStrikethrough = false; 5190 strikethrough_end = new Vector3(m_textInfo.characterInfo[i].topRight.x, m_textInfo.characterInfo[i].baseLine + strikethroughOffset * strikethroughScale, 0); 5191 5192 DrawUnderlineMesh(strikethrough_start, strikethrough_end, ref last_vert_index, strikethroughScale, strikethroughScale, strikethroughScale, xScale, strikethroughColor); 5193 } 5194 else if (beginStrikethrough && !isStrikeThroughVisible) 5195 { 5196 // Terminate Strikethrough if character is not visible. 5197 beginStrikethrough = false; 5198 strikethrough_end = new Vector3(m_textInfo.characterInfo[i - 1].topRight.x, m_textInfo.characterInfo[i - 1].baseLine + strikethroughOffset * strikethroughScale, 0); 5199 5200 DrawUnderlineMesh(strikethrough_start, strikethrough_end, ref last_vert_index, strikethroughScale, strikethroughScale, strikethroughScale, xScale, strikethroughColor); 5201 } 5202 } 5203 else 5204 { 5205 // End Strikethrough 5206 if (beginStrikethrough == true) 5207 { 5208 beginStrikethrough = false; 5209 strikethrough_end = new Vector3(m_textInfo.characterInfo[i - 1].topRight.x, m_textInfo.characterInfo[i - 1].baseLine + strikethroughOffset * strikethroughScale, 0); 5210 5211 DrawUnderlineMesh(strikethrough_start, strikethrough_end, ref last_vert_index, strikethroughScale, strikethroughScale, strikethroughScale, xScale, strikethroughColor); 5212 } 5213 } 5214 #endregion 5215 5216 5217 // HANDLE TEXT HIGHLIGHTING 5218 #region Text Highlighting 5219 bool isHighlight = (m_textInfo.characterInfo[i].style & FontStyles.Highlight) == FontStyles.Highlight; 5220 if (isHighlight) 5221 { 5222 bool isHighlightVisible = true; 5223 int currentPage = m_textInfo.characterInfo[i].pageNumber; 5224 5225 if (i > m_maxVisibleCharacters || currentLine > m_maxVisibleLines || (m_overflowMode == TextOverflowModes.Page && currentPage + 1 != m_pageToDisplay)) 5226 isHighlightVisible = false; 5227 5228 if (beginHighlight == false && isHighlightVisible == true && i <= lineInfo.lastVisibleCharacterIndex && unicode != 10 && unicode != 11 && unicode != 13) 5229 { 5230 if (i == lineInfo.lastVisibleCharacterIndex && char.IsSeparator(unicode)) 5231 { } 5232 else 5233 { 5234 beginHighlight = true; 5235 highlight_start = k_LargePositiveVector2; 5236 highlight_end = k_LargeNegativeVector2; 5237 highlightState = m_textInfo.characterInfo[i].highlightState; 5238 } 5239 } 5240 5241 if (beginHighlight) 5242 { 5243 TMP_CharacterInfo currentCharacter = m_textInfo.characterInfo[i]; 5244 HighlightState currentState = currentCharacter.highlightState; 5245 5246 bool isColorTransition = false; 5247 5248 // Handle Highlight color changes 5249 if (highlightState != currentState) 5250 { 5251 // Adjust previous highlight section to prevent a gaps between sections. 5252 if (isWhiteSpace) 5253 highlight_end.x = (highlight_end.x - highlightState.padding.right + currentCharacter.origin) / 2; 5254 else 5255 highlight_end.x = (highlight_end.x - highlightState.padding.right + currentCharacter.bottomLeft.x) / 2; 5256 5257 highlight_start.y = Mathf.Min(highlight_start.y, currentCharacter.descender); 5258 highlight_end.y = Mathf.Max(highlight_end.y, currentCharacter.ascender); 5259 5260 DrawTextHighlight(highlight_start, highlight_end, ref last_vert_index, highlightState.color); 5261 5262 beginHighlight = true; 5263 highlight_start = new Vector2(highlight_end.x, currentCharacter.descender - currentState.padding.bottom); 5264 5265 if (isWhiteSpace) 5266 highlight_end = new Vector2(currentCharacter.xAdvance + currentState.padding.right, currentCharacter.ascender + currentState.padding.top); 5267 else 5268 highlight_end = new Vector2(currentCharacter.topRight.x + currentState.padding.right, currentCharacter.ascender + currentState.padding.top); 5269 5270 highlightState = currentState; 5271 5272 isColorTransition = true; 5273 } 5274 5275 if (!isColorTransition) 5276 { 5277 if (isWhiteSpace) 5278 { 5279 // Use the Min / Max of glyph metrics if white space. 5280 highlight_start.x = Mathf.Min(highlight_start.x, currentCharacter.origin - highlightState.padding.left); 5281 highlight_end.x = Mathf.Max(highlight_end.x, currentCharacter.xAdvance + highlightState.padding.right); 5282 } 5283 else 5284 { 5285 // Use the Min / Max of character bounds 5286 highlight_start.x = Mathf.Min(highlight_start.x, currentCharacter.bottomLeft.x - highlightState.padding.left); 5287 highlight_end.x = Mathf.Max(highlight_end.x, currentCharacter.topRight.x + highlightState.padding.right); 5288 } 5289 5290 highlight_start.y = Mathf.Min(highlight_start.y, currentCharacter.descender - highlightState.padding.bottom); 5291 highlight_end.y = Mathf.Max(highlight_end.y, currentCharacter.ascender + highlightState.padding.top); 5292 } 5293 } 5294 5295 // End Highlight if text only contains one character. 5296 if (beginHighlight && m_characterCount == 1) 5297 { 5298 beginHighlight = false; 5299 5300 DrawTextHighlight(highlight_start, highlight_end, ref last_vert_index, highlightState.color); 5301 } 5302 else if (beginHighlight && (i == lineInfo.lastCharacterIndex || i >= lineInfo.lastVisibleCharacterIndex)) 5303 { 5304 beginHighlight = false; 5305 DrawTextHighlight(highlight_start, highlight_end, ref last_vert_index, highlightState.color); 5306 } 5307 else if (beginHighlight && !isHighlightVisible) 5308 { 5309 beginHighlight = false; 5310 DrawTextHighlight(highlight_start, highlight_end, ref last_vert_index, highlightState.color); 5311 } 5312 } 5313 else 5314 { 5315 // End Highlight 5316 if (beginHighlight == true) 5317 { 5318 beginHighlight = false; 5319 DrawTextHighlight(highlight_start, highlight_end, ref last_vert_index, highlightState.color); 5320 } 5321 } 5322 #endregion 5323 5324 lastLine = currentLine; 5325 } 5326 #endregion 5327 5328 // Set vertex count for Underline geometry 5329 m_textInfo.meshInfo[m_Underline.materialIndex].vertexCount = last_vert_index; 5330 5331 // METRICS ABOUT THE TEXT OBJECT 5332 m_textInfo.characterCount = m_characterCount; 5333 m_textInfo.spriteCount = m_spriteCount; 5334 m_textInfo.lineCount = lineCount; 5335 m_textInfo.wordCount = wordCount != 0 && m_characterCount > 0 ? wordCount : 1; 5336 m_textInfo.pageCount = m_pageNumber + 1; 5337 5338 // End Sampling of Phase II 5339 k_GenerateTextPhaseIIMarker.End(); 5340 5341 // Phase III - Update Mesh Vertex Data 5342 k_GenerateTextPhaseIIIMarker.Begin(); 5343 5344 if (m_renderMode == TextRenderFlags.Render && IsActive()) 5345 { 5346 // Event to allow users to modify the content of the text info before the text is rendered. 5347 OnPreRenderText?.Invoke(m_textInfo); 5348 5349 // Must ensure the Canvas support the additional vertex attributes used by TMP. 5350 // This could be optimized based on canvas render mode settings but gets complicated to handle with multiple text objects using different material presets. 5351 if (m_canvas.additionalShaderChannels != (AdditionalCanvasShaderChannels)25) 5352 m_canvas.additionalShaderChannels |= (AdditionalCanvasShaderChannels)25; 5353 5354 // Sort the geometry of the text object if needed. 5355 if (m_geometrySortingOrder != VertexSortingOrder.Normal) 5356 m_textInfo.meshInfo[0].SortGeometry(VertexSortingOrder.Reverse); 5357 5358 // Upload Mesh Data 5359 m_mesh.MarkDynamic(); 5360 m_mesh.vertices = m_textInfo.meshInfo[0].vertices; 5361 m_mesh.SetUVs(0, m_textInfo.meshInfo[0].uvs0); 5362 m_mesh.uv2 = m_textInfo.meshInfo[0].uvs2; 5363 //m_mesh.uv4 = m_textInfo.meshInfo[0].uvs4; 5364 m_mesh.colors32 = m_textInfo.meshInfo[0].colors32; 5365 5366 // Compute Bounds for the mesh. Manual computation is more efficient then using Mesh.RecalcualteBounds. 5367 m_mesh.RecalculateBounds(); 5368 //m_mesh.bounds = new Bounds(new Vector3((m_meshExtents.max.x + m_meshExtents.min.x) / 2, (m_meshExtents.max.y + m_meshExtents.min.y) / 2, 0) + offset, new Vector3(m_meshExtents.max.x - m_meshExtents.min.x, m_meshExtents.max.y - m_meshExtents.min.y, 0)); 5369 5370 m_canvasRenderer.SetMesh(m_mesh); 5371 5372 // Cache CanvasRenderer color of the parent text object. 5373 Color parentBaseColor = m_canvasRenderer.GetColor(); 5374 5375 bool isCullTransparentMeshEnabled = m_canvasRenderer.cullTransparentMesh; 5376 5377 for (int i = 1; i < m_textInfo.materialCount; i++) 5378 { 5379 // Clear unused vertices 5380 m_textInfo.meshInfo[i].ClearUnusedVertices(); 5381 5382 if (m_subTextObjects[i] == null) continue; 5383 5384 // Sort the geometry of the sub-text objects if needed. 5385 if (m_geometrySortingOrder != VertexSortingOrder.Normal) 5386 m_textInfo.meshInfo[i].SortGeometry(VertexSortingOrder.Reverse); 5387 5388 //m_subTextObjects[i].mesh.MarkDynamic(); 5389 m_subTextObjects[i].mesh.vertices = m_textInfo.meshInfo[i].vertices; 5390 m_subTextObjects[i].mesh.SetUVs(0, m_textInfo.meshInfo[i].uvs0); 5391 m_subTextObjects[i].mesh.uv2 = m_textInfo.meshInfo[i].uvs2; 5392 //m_subTextObjects[i].mesh.uv4 = m_textInfo.meshInfo[i].uvs4; 5393 m_subTextObjects[i].mesh.colors32 = m_textInfo.meshInfo[i].colors32; 5394 5395 m_subTextObjects[i].mesh.RecalculateBounds(); 5396 5397 m_subTextObjects[i].canvasRenderer.SetMesh(m_subTextObjects[i].mesh); 5398 5399 // Set CanvasRenderer color to match the parent text object. 5400 m_subTextObjects[i].canvasRenderer.SetColor(parentBaseColor); 5401 5402 // Make sure Cull Transparent Mesh of the sub objects matches the parent 5403 m_subTextObjects[i].canvasRenderer.cullTransparentMesh = isCullTransparentMeshEnabled; 5404 5405 // Sync RaycastTarget property with parent text object 5406 m_subTextObjects[i].raycastTarget = this.raycastTarget; 5407 } 5408 } 5409 5410 // Update culling if it has to be delayed due to text layout being dirty. 5411 if (m_ShouldUpdateCulling) 5412 UpdateCulling(); 5413 5414 // Event indicating the text has been regenerated. 5415 TMPro_EventManager.ON_TEXT_CHANGED(this); 5416 5417 //Debug.Log("***** Done rendering text object ID " + GetInstanceID() + ". *****"); 5418 5419 // End Sampling 5420 k_GenerateTextPhaseIIIMarker.End(); 5421 k_GenerateTextMarker.End(); 5422 } 5423 5424 5425 /// <summary> 5426 /// Method to return the local corners of the Text Container or RectTransform. 5427 /// </summary> 5428 /// <returns></returns> 5429 protected override Vector3[] GetTextContainerLocalCorners() 5430 { 5431 if (m_rectTransform == null) m_rectTransform = this.rectTransform; 5432 5433 m_rectTransform.GetLocalCorners(m_RectTransformCorners); 5434 5435 return m_RectTransformCorners; 5436 } 5437 5438 5439 /// <summary> 5440 /// Method to Enable or Disable child SubMesh objects. 5441 /// </summary> 5442 /// <param name="state"></param> 5443 protected override void SetActiveSubMeshes(bool state) 5444 { 5445 for (int i = 1; i < m_subTextObjects.Length && m_subTextObjects[i] != null; i++) 5446 { 5447 if (m_subTextObjects[i].enabled != state) 5448 m_subTextObjects[i].enabled = state; 5449 } 5450 } 5451 5452 5453 /// <summary> 5454 /// Destroy Sub Mesh Objects 5455 /// </summary> 5456 protected override void DestroySubMeshObjects() 5457 { 5458 for (int i = 1; i < m_subTextObjects.Length && m_subTextObjects[i] != null; i++) 5459 DestroyImmediate(m_subTextObjects[i]); 5460 } 5461 5462 5463 /// <summary> 5464 /// Method returning the compound bounds of the text object and child sub objects. 5465 /// </summary> 5466 /// <returns></returns> 5467 protected override Bounds GetCompoundBounds() 5468 { 5469 Bounds mainBounds = m_mesh.bounds; 5470 Vector3 min = mainBounds.min; 5471 Vector3 max = mainBounds.max; 5472 5473 for (int i = 1; i < m_subTextObjects.Length && m_subTextObjects[i] != null; i++) 5474 { 5475 Bounds subBounds = m_subTextObjects[i].mesh.bounds; 5476 min.x = min.x < subBounds.min.x ? min.x : subBounds.min.x; 5477 min.y = min.y < subBounds.min.y ? min.y : subBounds.min.y; 5478 5479 max.x = max.x > subBounds.max.x ? max.x : subBounds.max.x; 5480 max.y = max.y > subBounds.max.y ? max.y : subBounds.max.y; 5481 } 5482 5483 Vector3 center = (min + max) / 2; 5484 Vector2 size = max - min; 5485 return new Bounds(center, size); 5486 } 5487 5488 internal override Rect GetCanvasSpaceClippingRect() 5489 { 5490 if (m_canvas == null || m_canvas.rootCanvas == null || m_mesh == null) 5491 return Rect.zero; 5492 5493 Transform rootCanvasTransform = m_canvas.rootCanvas.transform; 5494 Bounds compoundBounds = GetCompoundBounds(); 5495 5496 Vector2 position = rootCanvasTransform.InverseTransformPoint(m_rectTransform.position); 5497 5498 Vector2 canvasLossyScale = rootCanvasTransform.lossyScale; 5499 Vector2 lossyScale = m_rectTransform.lossyScale / canvasLossyScale; 5500 5501 return new Rect(position + compoundBounds.min * lossyScale, compoundBounds.size * lossyScale); 5502 } 5503 5504 /// <summary> 5505 /// Method to Update Scale in UV2 5506 /// </summary> 5507 //void UpdateSDFScale(float lossyScale) 5508 //{ 5509 // // TODO: Resolve - Underline / Strikethrough segments not getting their SDF Scale adjusted. 5510 5511 // //Debug.Log("Updating SDF Scale."); 5512 5513 // // Return if we don't have a valid reference to a Canvas. 5514 // if (m_canvas == null) 5515 // { 5516 // m_canvas = GetCanvas(); 5517 // if (m_canvas == null) return; 5518 // } 5519 5520 // lossyScale = lossyScale == 0 ? 1 : lossyScale; 5521 5522 // float xScale = 0; 5523 // float canvasScaleFactor = m_canvas.scaleFactor; 5524 5525 // if (m_canvas.renderMode == RenderMode.ScreenSpaceOverlay) 5526 // xScale = lossyScale / canvasScaleFactor; 5527 // else if (m_canvas.renderMode == RenderMode.ScreenSpaceCamera) 5528 // xScale = m_canvas.worldCamera != null ? lossyScale : 1; 5529 // else 5530 // xScale = lossyScale; 5531 5532 // // Iterate through each of the characters. 5533 // for (int i = 0; i < m_textInfo.characterCount; i++) 5534 // { 5535 // // Only update scale for visible characters. 5536 // if (m_textInfo.characterInfo[i].isVisible && m_textInfo.characterInfo[i].elementType == TMP_TextElementType.Character) 5537 // { 5538 // float scale = xScale * m_textInfo.characterInfo[i].scale * (1 - m_charWidthAdjDelta); 5539 // if (!m_textInfo.characterInfo[i].isUsingAlternateTypeface && (m_textInfo.characterInfo[i].style & FontStyles.Bold) == FontStyles.Bold) scale *= -1; 5540 5541 // int index = m_textInfo.characterInfo[i].materialReferenceIndex; 5542 // int vertexIndex = m_textInfo.characterInfo[i].vertexIndex; 5543 5544 // m_textInfo.meshInfo[index].uvs2[vertexIndex + 0].y = scale; 5545 // m_textInfo.meshInfo[index].uvs2[vertexIndex + 1].y = scale; 5546 // m_textInfo.meshInfo[index].uvs2[vertexIndex + 2].y = scale; 5547 // m_textInfo.meshInfo[index].uvs2[vertexIndex + 3].y = scale; 5548 // } 5549 // } 5550 5551 // // Push the updated uv2 scale information to the meshes. 5552 // for (int i = 0; i < m_textInfo.materialCount; i++) 5553 // { 5554 // if (i == 0) 5555 // { 5556 // m_mesh.uv2 = m_textInfo.meshInfo[0].uvs2; 5557 // m_canvasRenderer.SetMesh(m_mesh); 5558 // } 5559 // else 5560 // { 5561 // m_subTextObjects[i].mesh.uv2 = m_textInfo.meshInfo[i].uvs2; 5562 // m_subTextObjects[i].canvasRenderer.SetMesh(m_subTextObjects[i].mesh); 5563 // } 5564 // } 5565 //} 5566 5567 /// <summary> 5568 /// Method to update the SDF Scale in UV2. 5569 /// </summary> 5570 /// <param name="scaleDelta"></param> 5571 void UpdateSDFScale(float scaleDelta) 5572 { 5573 if (scaleDelta == 0 || scaleDelta == float.PositiveInfinity || scaleDelta == float.NegativeInfinity) 5574 { 5575 m_havePropertiesChanged = true; 5576 OnPreRenderCanvas(); 5577 return; 5578 } 5579 5580 for (int materialIndex = 0; materialIndex < m_textInfo.materialCount; materialIndex ++) 5581 { 5582 TMP_MeshInfo meshInfo = m_textInfo.meshInfo[materialIndex]; 5583 5584 for (int i = 0; i < meshInfo.uvs0.Length; i++) 5585 { 5586 meshInfo.uvs0[i].w *= Mathf.Abs(scaleDelta); 5587 } 5588 } 5589 5590 // Push the updated uv0 scale information to the meshes. 5591 for (int i = 0; i < m_textInfo.materialCount; i++) 5592 { 5593 if (i == 0) 5594 { 5595 m_mesh.SetUVs(0, m_textInfo.meshInfo[0].uvs0); 5596 m_canvasRenderer.SetMesh(m_mesh); 5597 } 5598 else 5599 { 5600 m_subTextObjects[i].mesh.SetUVs(0, m_textInfo.meshInfo[i].uvs0); 5601 m_subTextObjects[i].canvasRenderer.SetMesh(m_subTextObjects[i].mesh); 5602 } 5603 } 5604 } 5605 #endregion 5606 } 5607}