diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ec62a7..32e79c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,20 @@ # Changelog These are the release notes for the TextMesh Pro UPM package which was first introduced with Unity 2018.1. Please see the following link for the Release Notes for prior versions of TextMesh Pro. http://digitalnativestudios.com/forum/index.php?topic=1363.0 +## [2.1.0-preview.5] - 2020-02-25 +## [1.5.0-preview.5] +## [3.0.0-preview.5] +### Changes +- Revised SetText function formatting options to including ability to specify padding for integral part of the value. Revised format is as follows: {Arg Index:Integral Padding.Decimal Precision} Example: TMP_Text.SetText("Value = {0:000.00}", 10.375f); result in "Value = 010.38". +- Fixed issue where text objects isTextObjectScaleStatic property would be ignored when OnCanvasHierarchyChanged() is called. +- Added a Character, Glyph and Record count to those respective tables in the Font Asset Inspector. +- Fixed potential Null Reference Exception that would occur when using text Overflow Ellipsis mode with a primary font asset that doesn't contain the Ellipsis character. Case #1209771 +- Fixed a potential Editor lockup when using text Overflow Page mode. Case #1219055 +- Fixed Input Field incorrect caret vertical alignment when using the Midline / Vertical Geometry alignment option. +- Added initial / minimal support for the New Input System. Please use with caution and report any issues. +- Changes to Font Asset Generation Settings via the Font Asset Inspector will now update the existing glyphs and characters for the new settings instead of clearing them. +- Text object InternalUpdate() used to handle potential scale changes of text objects now uses willRenderCanvases event instead of onPreCull. This avoids a potential one frame delay in updating of objects and no impact on objects. Case #1216007 + ## [2.1.0-preview.4] - 2020-01-31 ## [1.5.0-preview.4] ## [3.0.0-preview.4] @@ -35,7 +49,7 @@ These are the release notes for the TextMesh Pro UPM package which was first int - Fixed Missing Reference Exception that would appear when editing the Vertex Color or Color Gradient of a TMP component Preset asset. Case #1201069 - Fixed Inspector layout issue preventing enabling or disabling the Outline, Underlay, Lighting and Glow features when selecting a Preset asset material. Case #1196963 - Revised the Create Material Preset context menu option to issue a warning and ignore materials outside the project. Case #1200109 -- Added experimental ITextPreprocessor interface to allow users to create custom components to handle text preprocessing and shaping. This interface includes a PreprocessText(string text) function that is called when the object contains a component that inherits from this interface. +- Added experimental ITextPreprocessor interface to allow users to create custom components to handle text preprocessing and shaping. This interface includes a PreprocessText(string text) function that is called when the object contains a component that inherits from this interface. - Added support for Unity Presets in the Editor for both and components. Case #1191793 - Optimization to ensure the TMP Update Manager only rebuilds text objects once per frame regardless of the number of cameras in the scene. @@ -44,7 +58,7 @@ These are the release notes for the TextMesh Pro UPM package which was first int ### Changes - Fixed Input Field issue when Read Only flag is set preventing the initial setting of the text. Also fixed Read Only flag not being respected when using IME input. - Fixed potential infinite loop when using text overflow mode ScrollRect. See Case #1188867 -- Fixed Input Field culling related issue(s) where text would be incorrectly culled. See https://forum.unity.com/threads/version-1-5-0-2-1-0-preview-1-now-available-for-testing.753587/#post-5023700 +- Fixed Input Field culling related issue(s) where text would be incorrectly culled. See https://forum.unity.com/threads/version-1-5-0-2-1-0-preview-1-now-available-for-testing.753587/#post-5023700 - Revised handling and referencing of the CanvasRenderer in anticipation of an incoming change to the MaskableGraphic class where it will no longer automatically add a CanvasRenderer to components inheriting from it. As a result, objects will no longer have a CanvasRenderer. - Fixed potential NRE when using Overflow Truncate mode with sprites. See https://forum.unity.com/threads/tmpro-stackoverflow-caused-by-tmpro-textmeshprougui-generatetextmesh.750398/page-2#post-5042822 - Fixed issue when using font weights in combination of font styles in the editor. @@ -56,7 +70,7 @@ These are the release notes for the TextMesh Pro UPM package which was first int - Fixed Fallback material not getting updated correctly when changing Generation Settings on the Fallback Font Asset. - Fixed a typo in the Font Weight section of the Font Asset Editor. - Fixed potential ArgumentOutOfRangeException in the Input Field when using Hide Mobile Input and deleting a long string. Case #1162514 -- Added "Is Scale Static" option in the Extra Settings to exclude text objects from InternalUpdate callbacks to improve performance when the object's scale is static. This InternalUpdate callback is used to track potential changes to the scale of text objects to update their SDF Scale. +- Added "Is Scale Static" option in the Extra Settings to exclude text objects from InternalUpdate callbacks to improve performance when the object's scale is static. This InternalUpdate callback is used to track potential changes to the scale of text objects to update their SDF Scale. - Added the ability to control culling modes for the TMP Shaders. This new option is available in the Debug section of the Material Inspector. New feature requires updating the TMP Essential Resources. See the following post https://forum.unity.com/threads/not-see-textmeshpro-rendering-from-the-back.767510/#post-5112461. - Fixed Material Inspector issue when toggling the Record button in the Animation window. Case #1174960 - Improved Line Breaking handling for CJK. This also addresses a few reported issues. Case #1171603 @@ -106,7 +120,7 @@ These are the release notes for the TextMesh Pro UPM package which was first int - Since the legacy TextContainer used by TMP has been deprecated, it was removed from the Layout Context Menu options. - Improved character positioning when using italic text where large angle / slant would potentially result in uneven spacing between normal and italic blocks of text. - Fixed an issue where <mspace> and <cspace> tags would not be handled correctly in conjunction with word wrapping. -- Fixed issue in the TMP_Dropdown.cs that was affecting navigation. Case 1162600. See https://forum.unity.com/threads/huge-bug-missing-a-code-line-since-1-4-0.693421/ +- Fixed issue in the TMP_Dropdown.cs that was affecting navigation. Case 1162600. See https://forum.unity.com/threads/huge-bug-missing-a-code-line-since-1-4-0.693421/ - Fixed an issue related to kerning where the glyph adjustment values did not account for the upsampling of the legacy SDF modes like SDF8 / SDF16 and SDF32. - Made the TMP_Text.text property virtual. - Fixed Material Preset of fallback materials getting modified when the TMP Settings Match Material Preset option is disabled. @@ -131,7 +145,7 @@ These are the release notes for the TextMesh Pro UPM package which was first int - Fixed potential issue related to SDF Scaling when the scale of the text object is negative. See https://forum.unity.com/threads/version-1-4-1-preview-1-with-dynamic-sdf-for-unity-2018-3-now-available.622420/page-5#post-4958240 for details. - Added validation check for Sprite Data Source file in the Sprite Asset Importer. Case #1186620 - Added warning when using Create - TextMeshPro - Sprite Asset menu when no valid texture is selected. Case #1163982 -- Fixed potential cosmetic issue in the text component inspector when using Overflow Linked mode. Case #1177640 +- Fixed potential cosmetic issue in the text component inspector when using Overflow Linked mode. Case #1177640 ## [1.4.1] - 2019-04-12 ### Changes @@ -155,13 +169,13 @@ These are the release notes for the TextMesh Pro UPM package which was first int - Fixed an issue with SDF Scale handling where the text object would not render correctly after the object scale had been set to zero. - Fixed an issue with the TMP_UpdateManager where text objects were not getting unregistered correctly. - Any changes to Font Asset Creation Settings' padding, atlas width and / or atlas height will now result in all Material Presets for the given font asset to also be updated. -- Added new section in the TMP Settings related to the new Dynamic Font System. +- Added new section in the TMP Settings related to the new Dynamic Font System. - Added new property in the Dynamic Font System section to determine if OpenType Font Features will be retrieved from source font files at runtime as new characters are added to font assets. Glyph Adjustment Data (Kerning) is the only feature currently supported. - Fix an issue where font assets created at runtime were not getting their asset version number set to "1.1.0". - Improved parsing of the text file used in the Font Asset Creator and "Characters from File" option to handle UTF16 "\u" and UTF32 "\U" escape character sequences. - Fixed a Null Reference Error (NRE) that could occur when using the <font> tag with an invalid font name followed by the <sprite> tag. - The Glyph Adjustment Table presentation and internal data structure has been changed to facilitate the future addition of OpenType font features. See https://forum.unity.com/threads/version-1-4-0-preview-with-dynamic-sdf-for-unity-2018-3-now-available.622420/#post-4206595 for more details. -- Fixed an issue with the <rotate> tag incorrectly affecting character spacing. +- Fixed an issue with the <rotate> tag incorrectly affecting character spacing. ## [1.4.0-preview.1] - 2019-01-30 ### Changes @@ -269,7 +283,7 @@ These are the release notes for the TextMesh Pro UPM package which was first int ## [1.1.0] - 2018-01-23 ### Changes -- Package version # increased to 1.1.0 which is the first release for Unity 2018.1. +- Package version # increased to 1.1.0 which is the first release for Unity 2018.1. ## [1.0.27] - 2018-01-16 ### Changes diff --git a/Editor Resources/Shaders/TMP_SDF Internal Editor.shader b/Editor Resources/Shaders/TMP_SDF Internal Editor.shader new file mode 100644 index 0000000..baf4501 --- /dev/null +++ b/Editor Resources/Shaders/TMP_SDF Internal Editor.shader @@ -0,0 +1,75 @@ +// Simplified SDF shader: +// - No Shading Option (bevel / bump / env map) +// - No Glow Option +// - Softness is applied on both side of the outline + +Shader "Hidden/TMP/Internal/Editor/Distance Field SSD" { + + Properties{ + _FaceColor("Face Color", Color) = (1,1,1,1) + _FaceDilate("Face Dilate", Range(-1,1)) = 0 + + _OutlineColor("Outline Color", Color) = (0,0,0,1) + _OutlineWidth("Outline Thickness", Range(0,1)) = 0 + _OutlineSoftness("Outline Softness", Range(0,1)) = 0 + + _UnderlayColor("Border Color", Color) = (0,0,0,.5) + _UnderlayOffsetX("Border OffsetX", Range(-1,1)) = 0 + _UnderlayOffsetY("Border OffsetY", Range(-1,1)) = 0 + _UnderlayDilate("Border Dilate", Range(-1,1)) = 0 + _UnderlaySoftness("Border Softness", Range(0,1)) = 0 + + _WeightNormal("Weight Normal", float) = 0 + _WeightBold("Weight Bold", float) = .5 + + _ShaderFlags("Flags", float) = 0 + _ScaleRatioA("Scale RatioA", float) = 1 + _ScaleRatioB("Scale RatioB", float) = 1 + _ScaleRatioC("Scale RatioC", float) = 1 + + _MainTex("Font Atlas", 2D) = "white" {} + _TextureWidth("Texture Width", float) = 1024 + _TextureHeight("Texture Height", float) = 1024 + _GradientScale("Gradient Scale", float) = 1 + _ScaleX("Scale X", float) = 1 + _ScaleY("Scale Y", float) = 1 + _PerspectiveFilter("Perspective Correction", Range(0, 1)) = 0.875 + _Sharpness("Sharpness", Range(-1,1)) = 0 + + _VertexOffsetX("Vertex OffsetX", float) = 0 + _VertexOffsetY("Vertex OffsetY", float) = 0 + } + + SubShader + { + Tags + { + "ForceSupported" = "True" + } + + Lighting Off + Blend One OneMinusSrcAlpha + Cull Off + ZWrite Off + ZTest Always + + Pass + { + CGPROGRAM + #pragma vertex VertShader + #pragma fragment PixShader + #pragma shader_feature __ OUTLINE_ON + #pragma shader_feature __ UNDERLAY_ON UNDERLAY_INNER + + #include "UnityCG.cginc" + #include "UnityUI.cginc" + #include "TMP_Properties.cginc" + + #include "TMP_SDF_SSD.cginc" + + ENDCG + } + } + + CustomEditor "TMPro.EditorUtilities.TMP_SDFShaderGUI" +} diff --git a/Editor Resources/Shaders/TMP_SDF Internal SSD.shader.meta b/Editor Resources/Shaders/TMP_SDF Internal Editor.shader.meta similarity index 80% rename from Editor Resources/Shaders/TMP_SDF Internal SSD.shader.meta rename to Editor Resources/Shaders/TMP_SDF Internal Editor.shader.meta index 7845e11..5ba708d 100644 --- a/Editor Resources/Shaders/TMP_SDF Internal SSD.shader.meta +++ b/Editor Resources/Shaders/TMP_SDF Internal Editor.shader.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: ce4ec0f498d1b1a4f90fe94e115b6f9a +guid: 9c442dc870b456e48b615cd8add0e9ef ShaderImporter: externalObjects: {} defaultTextures: [] diff --git a/Editor Resources/Shaders/TMP_SDF Internal SSD.shader b/Editor Resources/Shaders/TMP_SDF Internal SSD.shader deleted file mode 100644 index 7e28d74..0000000 --- a/Editor Resources/Shaders/TMP_SDF Internal SSD.shader +++ /dev/null @@ -1,126 +0,0 @@ -// Simplified SDF shader: -// - No Shading Option (bevel / bump / env map) -// - No Glow Option -// - Softness is applied on both side of the outline - -Shader "Hidden/TextMeshPro/Internal/Distance Field SSD" { - -Properties { - _FaceColor ("Face Color", Color) = (1,1,1,1) - _FaceDilate ("Face Dilate", Range(-1,1)) = 0 - - _OutlineSoftness ("Outline Softness", Range(0,1)) = 0.02 - - _WeightNormal ("Weight Normal", float) = 0 - _WeightBold ("Weight Bold", float) = .5 - - _MainTex ("Font Atlas", 2D) = "white" {} - _TextureWidth ("Texture Width", float) = 512 - _TextureHeight ("Texture Height", float) = 512 - _GradientScale ("Gradient Scale", float) = 5 - _ScaleX ("Scale X", float) = 1 - _ScaleY ("Scale Y", float) = 1 - _Sharpness ("Sharpness", Range(-1,1)) = 0 - - _VertexOffsetX ("Vertex OffsetX", float) = 0 - _VertexOffsetY ("Vertex OffsetY", float) = 0 - - _ColorMask ("Color Mask", Float) = 15 -} - -SubShader { - Tags - { - "ForceSupported" = "True" - } - - Lighting Off - Blend One OneMinusSrcAlpha - Cull Off - ZWrite Off - ZTest Always - - Pass { - CGPROGRAM - #pragma vertex VertShader - #pragma fragment PixShader - - #include "UnityCG.cginc" - #include "TMP_Properties.cginc" - - sampler2D _GUIClipTexture; - uniform float4x4 unity_GUIClipTextureMatrix; - - struct vertex_t { - float4 vertex : POSITION; - float3 normal : NORMAL; - fixed4 color : COLOR; - float2 texcoord0 : TEXCOORD0; - float2 texcoord1 : TEXCOORD1; - }; - - struct pixel_t { - float4 vertex : SV_POSITION; - fixed4 faceColor : COLOR; - float2 texcoord0 : TEXCOORD0; - float2 clipUV : TEXCOORD1; - }; - - - pixel_t VertShader(vertex_t input) - { - // Does not handle simulated bold correctly. - - float4 vert = input.vertex; - vert.x += _VertexOffsetX; - vert.y += _VertexOffsetY; - float4 vPosition = UnityObjectToClipPos(vert); - - float opacity = input.color.a; - - fixed4 faceColor = fixed4(input.color.rgb, opacity) * _FaceColor; - faceColor.rgb *= faceColor.a; - - // Generate UV for the Clip Texture - float3 eyePos = UnityObjectToViewPos(input.vertex); - float2 clipUV = mul(unity_GUIClipTextureMatrix, float4(eyePos.xy, 0, 1.0)); - - // Structure for pixel shader - pixel_t output = { - vPosition, - faceColor, - float2(input.texcoord0.x, input.texcoord0.y), - clipUV, - }; - - return output; - } - - half transition(half2 range, half distance) - { - return smoothstep(range.x, range.y, distance); - } - - // PIXEL SHADER - fixed4 PixShader(pixel_t input) : SV_Target - { - half distanceSample = tex2D(_MainTex, input.texcoord0).a; - half smoothing = fwidth(distanceSample) * (1 - _Sharpness) + _OutlineSoftness; - half contour = 0.5 - _FaceDilate * 0.5; - half2 edgeRange = half2(contour - smoothing, contour + smoothing); - - half4 c = input.faceColor; - - half edgeTransition = transition(edgeRange, distanceSample); - c *= edgeTransition; - - c *= tex2D(_GUIClipTexture, input.clipUV).a; - - return c; - } - ENDCG - } -} - -CustomEditor "TMPro.EditorUtilities.TMP_SDFShaderGUI" -} diff --git a/Editor Resources/Shaders/TMP_SDF_SSD.cginc b/Editor Resources/Shaders/TMP_SDF_SSD.cginc new file mode 100644 index 0000000..0f587bd --- /dev/null +++ b/Editor Resources/Shaders/TMP_SDF_SSD.cginc @@ -0,0 +1,132 @@ +struct vertex_t +{ + float4 position : POSITION; + float3 normal : NORMAL; + float4 color : COLOR; + float2 texcoord0 : TEXCOORD0; + float2 texcoord1 : TEXCOORD1; +}; + +struct pixel_t +{ + float4 position : SV_POSITION; + float4 faceColor : COLOR; + float4 outlineColor : COLOR1; + float2 texcoord0 : TEXCOORD0; + float4 param : TEXCOORD1; // weight, scaleRatio + float2 clipUV : TEXCOORD2; + #if (UNDERLAY_ON || UNDERLAY_INNER) + float4 texcoord2 : TEXCOORD3; + float4 underlayColor : COLOR2; + #endif +}; + +sampler2D _GUIClipTexture; +uniform float4x4 unity_GUIClipTextureMatrix; +float4 _MainTex_TexelSize; + +float4 SRGBToLinear(float4 rgba) +{ + return float4(lerp(rgba.rgb / 12.92f, pow((rgba.rgb + 0.055f) / 1.055f, 2.4f), step(0.04045f, rgba.rgb)), rgba.a); +} + +pixel_t VertShader(vertex_t input) +{ + pixel_t output; + + float bold = step(input.texcoord1.y, 0); + + float4 vert = input.position; + vert.x += _VertexOffsetX; + vert.y += _VertexOffsetY; + + float4 vPosition = UnityObjectToClipPos(vert); + + float weight = lerp(_WeightNormal, _WeightBold, bold) / 4.0; + weight = (weight + _FaceDilate) * _ScaleRatioA * 0.5; + + // Generate UV for the Clip Texture + float3 eyePos = UnityObjectToViewPos(input.position); + float2 clipUV = mul(unity_GUIClipTextureMatrix, float4(eyePos.xy, 0, 1.0)); + + float4 color = input.color; + #if (FORCE_LINEAR && !UNITY_COLORSPACE_GAMMA) + color = SRGBToLinear(input.color); + #endif + + float opacity = color.a; + #if (UNDERLAY_ON | UNDERLAY_INNER) + opacity = 1.0; + #endif + + float4 faceColor = float4(color.rgb, opacity) * _FaceColor; + faceColor.rgb *= faceColor.a; + + float4 outlineColor = _OutlineColor; + outlineColor.a *= opacity; + outlineColor.rgb *= outlineColor.a; + + output.position = vPosition; + output.faceColor = faceColor; + output.outlineColor = outlineColor; + output.texcoord0 = float2(input.texcoord0.xy); + output.param = float4(0.5 - weight, 1.3333 * _GradientScale * (_Sharpness + 1) / _MainTex_TexelSize.z , _OutlineWidth * _ScaleRatioA * 0.5, 0); + output.clipUV = clipUV; + + #if (UNDERLAY_ON || UNDERLAY_INNER) + float4 underlayColor = _UnderlayColor; + underlayColor.rgb *= underlayColor.a; + + float x = -(_UnderlayOffsetX * _ScaleRatioC) * _GradientScale / _MainTex_TexelSize.z; + float y = -(_UnderlayOffsetY * _ScaleRatioC) * _GradientScale / _MainTex_TexelSize.w; + + output.texcoord2 = float4(input.texcoord0 + float2(x, y), input.color.a, 0); + output.underlayColor = underlayColor; + #endif + + return output; +} + +float4 PixShader(pixel_t input) : SV_Target +{ + float d = tex2D(_MainTex, input.texcoord0.xy).a; + + float2 UV = input.texcoord0.xy; + float scale = rsqrt(abs(ddx(UV.x) * ddy(UV.y) - ddy(UV.x) * ddx(UV.y))) * input.param.y; + + #if (UNDERLAY_ON | UNDERLAY_INNER) + float layerScale = scale; + layerScale /= 1 + ((_UnderlaySoftness * _ScaleRatioC) * layerScale); + float layerBias = input.param.x * layerScale - .5 - ((_UnderlayDilate * _ScaleRatioC) * .5 * layerScale); + #endif + + scale /= 1 + (_OutlineSoftness * _ScaleRatioA * scale); + + float4 faceColor = input.faceColor * saturate((d - input.param.x) * scale + 0.5); + + #ifdef OUTLINE_ON + float4 outlineColor = lerp(input.faceColor, input.outlineColor, sqrt(min(1.0, input.param.z * scale * 2))); + faceColor = lerp(outlineColor, input.faceColor, saturate((d - input.param.x - input.param.z) * scale + 0.5)); + faceColor *= saturate((d - input.param.x + input.param.z) * scale + 0.5); + #endif + + #if UNDERLAY_ON + d = tex2D(_MainTex, input.texcoord2.xy).a * layerScale; + faceColor += float4(_UnderlayColor.rgb * _UnderlayColor.a, _UnderlayColor.a) * saturate(d - layerBias) * (1 - faceColor.a); + #endif + + #if UNDERLAY_INNER + float bias = input.param.x * scale - 0.5; + float sd = saturate(d * scale - bias - input.param.z); + d = tex2D(_MainTex, input.texcoord2.xy).a * layerScale; + faceColor += float4(_UnderlayColor.rgb * _UnderlayColor.a, _UnderlayColor.a) * (1 - saturate(d - layerBias)) * sd * (1 - faceColor.a); + #endif + + #if (UNDERLAY_ON | UNDERLAY_INNER) + faceColor *= input.texcoord2.z; + #endif + + faceColor *= tex2D(_GUIClipTexture, input.clipUV).a; + + return faceColor; +} diff --git a/Editor Resources/Shaders/TMP_SDF_SSD.cginc.meta b/Editor Resources/Shaders/TMP_SDF_SSD.cginc.meta new file mode 100644 index 0000000..3d0b3bf --- /dev/null +++ b/Editor Resources/Shaders/TMP_SDF_SSD.cginc.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: abe6991365a27d341a10580f3b7c0f44 +ShaderImporter: + externalObjects: {} + defaultTextures: [] + nonModifiableTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Editor/TMP_CharacterPropertyDrawer.cs b/Scripts/Editor/TMP_CharacterPropertyDrawer.cs index c1f5fb9..46e94f0 100644 --- a/Scripts/Editor/TMP_CharacterPropertyDrawer.cs +++ b/Scripts/Editor/TMP_CharacterPropertyDrawer.cs @@ -10,11 +10,7 @@ namespace TMPro.EditorUtilities [CustomPropertyDrawer(typeof(TMP_Character))] public class TMP_CharacterPropertyDrawer : PropertyDrawer { - //[SerializeField] - //static Material s_InternalSDFMaterial; - - //[SerializeField] - //static Material s_InternalBitmapMaterial; + private string k_ColorProperty = "_Color"; int m_GlyphSelectedForEditing = -1; @@ -78,7 +74,7 @@ public override void OnGUI(Rect position, SerializedProperty property, GUIConten { // Get a reference to the font asset TMP_FontAsset fontAsset = property.serializedObject.targetObject as TMP_FontAsset; - + // Make sure new glyph index is valid. int elementIndex = fontAsset.glyphTable.FindIndex(item => item.index == prop_GlyphIndex.intValue); @@ -89,7 +85,7 @@ public override void OnGUI(Rect position, SerializedProperty property, GUIConten } int glyphIndex = prop_GlyphIndex.intValue; - + // Reset glyph selection if new character has been selected. if (GUI.enabled && m_GlyphSelectedForEditing != glyphIndex) m_GlyphSelectedForEditing = -1; @@ -116,7 +112,7 @@ public override void OnGUI(Rect position, SerializedProperty property, GUIConten { // Get the index of the glyph in the font asset glyph table. int elementIndex = fontAsset.glyphTable.FindIndex(item => item.index == glyphIndex); - + if (elementIndex != -1) { SerializedProperty prop_GlyphTable = property.serializedObject.FindProperty("m_GlyphTable"); @@ -146,7 +142,7 @@ public override void OnGUI(Rect position, SerializedProperty property, GUIConten EditorGUIUtility.labelWidth = 39f; EditorGUI.PropertyField(new Rect(rect.x, rect.y + 36, 80, 18), prop_Scale, new GUIContent("Scale:")); - + // Draw Glyph (if exists) DrawGlyph(position, property); } @@ -192,7 +188,7 @@ void DrawGlyph(Rect position, SerializedProperty property) return; mat.mainTexture = atlasTexture; - mat.SetColor("_Color", Color.white); + mat.SetColor(k_ColorProperty, Color.white); } else { @@ -208,12 +204,14 @@ void DrawGlyph(Rect position, SerializedProperty property) // Draw glyph Rect glyphDrawPosition = new Rect(position.x, position.y, 48, 58); - SerializedProperty prop_GlyphRect = prop_Glyph.FindPropertyRelative("m_GlyphRect"); + SerializedProperty glyphRectProperty = prop_Glyph.FindPropertyRelative("m_GlyphRect"); + + int padding = fontAsset.atlasPadding; - int glyphOriginX = prop_GlyphRect.FindPropertyRelative("m_X").intValue; - int glyphOriginY = prop_GlyphRect.FindPropertyRelative("m_Y").intValue; - int glyphWidth = prop_GlyphRect.FindPropertyRelative("m_Width").intValue; - int glyphHeight = prop_GlyphRect.FindPropertyRelative("m_Height").intValue; + int glyphOriginX = glyphRectProperty.FindPropertyRelative("m_X").intValue - padding; + int glyphOriginY = glyphRectProperty.FindPropertyRelative("m_Y").intValue - padding; + int glyphWidth = glyphRectProperty.FindPropertyRelative("m_Width").intValue + padding * 2; + int glyphHeight = glyphRectProperty.FindPropertyRelative("m_Height").intValue + padding * 2; float normalizedHeight = fontAsset.faceInfo.ascentLine - fontAsset.faceInfo.descentLine; float scale = glyphDrawPosition.width / normalizedHeight; diff --git a/Scripts/Editor/TMP_FontAssetEditor.cs b/Scripts/Editor/TMP_FontAssetEditor.cs index 37074e4..3b8bb9b 100644 --- a/Scripts/Editor/TMP_FontAssetEditor.cs +++ b/Scripts/Editor/TMP_FontAssetEditor.cs @@ -1,7 +1,6 @@ using UnityEngine; using UnityEditor; using UnityEditorInternal; -using System.Collections; using System.Collections.Generic; using UnityEngine.TextCore; using UnityEngine.TextCore.LowLevel; @@ -74,7 +73,7 @@ internal static Material internalSDFMaterial { if (s_InternalSDFMaterial == null) { - Shader shader = Shader.Find("Hidden/TextMeshPro/Internal/Distance Field SSD"); + Shader shader = Shader.Find("Hidden/TMP/Internal/Editor/Distance Field SSD"); if (shader != null) s_InternalSDFMaterial = new Material(shader); @@ -144,7 +143,7 @@ private struct Warning private string m_KerningTableSearchPattern; private List m_KerningTableSearchList; - + private bool m_isSearchDirty; private const string k_UndoRedo = "UndoRedoPerformed"; @@ -262,6 +261,9 @@ public void OnEnable() m_GlyphSearchList = new List(); m_KerningTableSearchList = new List(); + + // Sort Font Asset Tables + m_fontAsset.SortAllTables(); } @@ -487,7 +489,7 @@ public override void OnInspectorGUI() } } - m_fontAsset.ClearFontAssetData(); + m_fontAsset.UpdateFontAssetData(); GUIUtility.keyboardControl = 0; isAssetDirty = true; @@ -561,7 +563,7 @@ public override void OnInspectorGUI() GUI.Label(rect, "Regular Typeface", EditorStyles.label); rect.x += rect.width; GUI.Label(rect, "Italic Typeface", EditorStyles.label); - + EditorGUI.indentLevel = 1; EditorGUILayout.PropertyField(fontWeights_prop.GetArrayElementAtIndex(1), new GUIContent("100 - Thin")); @@ -603,7 +605,7 @@ public override void OnInspectorGUI() m_materialPresets[i].SetFloat("_WeightBold", font_boldStyle_prop.floatValue); } EditorGUILayout.EndHorizontal(); - + EditorGUILayout.BeginHorizontal(); EditorGUILayout.PropertyField(font_normalSpacing_prop, new GUIContent("Spacing Offset")); font_normalSpacing_prop.floatValue = Mathf.Clamp(font_normalSpacing_prop.floatValue, -100, 100); @@ -619,11 +621,11 @@ public override void OnInspectorGUI() GUI.changed = false; } EditorGUILayout.EndHorizontal(); - + EditorGUILayout.BeginHorizontal(); EditorGUILayout.PropertyField(font_italicStyle_prop, new GUIContent("Italic Style")); font_italicStyle_prop.intValue = Mathf.Clamp(font_italicStyle_prop.intValue, 15, 60); - + EditorGUILayout.PropertyField(font_tabSize_prop, new GUIContent("Tab Multiple")); EditorGUILayout.EndHorizontal(); EditorGUILayout.EndVertical(); @@ -660,7 +662,9 @@ public override void OnInspectorGUI() EditorGUI.indentLevel = 0; rect = EditorGUILayout.GetControlRect(false, 24); - if (GUI.Button(rect, new GUIContent("Character Table", "List of characters contained in this font asset."), TMP_UIStyleManager.sectionHeader)) + int characterCount = m_fontAsset.characterTable.Count; + + if (GUI.Button(rect, new GUIContent("Character Table [" + characterCount + "]" + (rect.width > 320 ? " Characters" : ""), "List of characters contained in this font asset."), TMP_UIStyleManager.sectionHeader)) UI_PanelState.characterTablePanel = !UI_PanelState.characterTablePanel; GUI.Label(rect, (UI_PanelState.characterTablePanel ? "" : s_UiStateLabel[1]), TMP_UIStyleManager.rightLabel); @@ -857,7 +861,9 @@ public override void OnInspectorGUI() GUIStyle glyphPanelStyle = new GUIStyle(EditorStyles.helpBox); - if (GUI.Button(rect, new GUIContent("Glyph Table", "List of glyphs contained in this font asset."), TMP_UIStyleManager.sectionHeader)) + int glyphCount = m_fontAsset.glyphTable.Count; + + if (GUI.Button(rect, new GUIContent("Glyph Table [" + glyphCount + "]" + (rect.width > 275 ? " Glyphs" : ""), "List of glyphs contained in this font asset."), TMP_UIStyleManager.sectionHeader)) UI_PanelState.glyphTablePanel = !UI_PanelState.glyphTablePanel; GUI.Label(rect, (UI_PanelState.glyphTablePanel ? "" : s_UiStateLabel[1]), TMP_UIStyleManager.rightLabel); @@ -911,7 +917,7 @@ public override void OnInspectorGUI() EditorGUILayout.EndVertical(); // Display Glyph Table Elements - + if (arraySize > 0) { // Display each GlyphInfo entry using the GlyphInfo property drawer. @@ -1052,7 +1058,9 @@ public override void OnInspectorGUI() EditorGUI.indentLevel = 0; rect = EditorGUILayout.GetControlRect(false, 24); - if (GUI.Button(rect, new GUIContent("Glyph Adjustment Table", "List of glyph adjustment / advanced kerning pairs."), TMP_UIStyleManager.sectionHeader)) + int adjustmentPairCount = m_fontAsset.fontFeatureTable.glyphPairAdjustmentRecords.Count; + + if (GUI.Button(rect, new GUIContent("Glyph Adjustment Table [" + adjustmentPairCount + "]" + (rect.width > 340 ? " Records" : ""), "List of glyph adjustment / advanced kerning pairs."), TMP_UIStyleManager.sectionHeader)) UI_PanelState.fontFeatureTablePanel = !UI_PanelState.fontFeatureTablePanel; GUI.Label(rect, (UI_PanelState.fontFeatureTablePanel ? "" : s_UiStateLabel[1]), TMP_UIStyleManager.rightLabel); @@ -1263,7 +1271,7 @@ public override void OnInspectorGUI() } - // Clear selection if mouse event was not consumed. + // Clear selection if mouse event was not consumed. GUI.enabled = true; if (currentEvent.type == EventType.MouseDown && currentEvent.button == 0) m_SelectedAdjustmentRecord = -1; @@ -1400,7 +1408,7 @@ void DisplayPageNavigation(ref int currentPage, int arraySize, int itemsPerPage) /// - /// + /// /// /// /// @@ -1436,7 +1444,7 @@ bool AddNewGlyph(int srcIndex, int dstGlyphID) } /// - /// + /// /// /// void RemoveGlyphFromList(int index) @@ -1553,7 +1561,7 @@ void RemoveAdjustmentPairFromList(int index) } /// - /// + /// /// /// /// @@ -1600,7 +1608,7 @@ void CopyCharacterSerializedProperty(SerializedProperty source, ref SerializedPr /// - /// + /// /// /// /// @@ -1656,7 +1664,7 @@ void SearchCharacterTable(string searchPattern, ref List searchResults) searchResults.Add(i); else if (id.ToString("X").Contains(searchPattern)) searchResults.Add(i); - + // Check for potential match against decimal id //if (id.ToString().Contains(searchPattern)) // searchResults.Add(i); @@ -1706,4 +1714,4 @@ void SearchKerningTable(string searchPattern, ref List searchResults) } } } -} \ No newline at end of file +} diff --git a/Scripts/Editor/TMP_GlyphPropertyDrawer.cs b/Scripts/Editor/TMP_GlyphPropertyDrawer.cs index b92dfd6..d8d3700 100644 --- a/Scripts/Editor/TMP_GlyphPropertyDrawer.cs +++ b/Scripts/Editor/TMP_GlyphPropertyDrawer.cs @@ -1,4 +1,4 @@ - using UnityEngine; +using UnityEngine; using UnityEngine.TextCore; using UnityEngine.TextCore.LowLevel; using UnityEditor; @@ -11,6 +11,8 @@ namespace TMPro.EditorUtilities [CustomPropertyDrawer(typeof(Glyph))] public class TMP_GlyphPropertyDrawer : PropertyDrawer { + private string k_ColorProperty = "_Color"; + public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { SerializedProperty prop_GlyphIndex = property.FindPropertyRelative("m_Index"); @@ -73,7 +75,7 @@ void DrawGlyph(Rect position, SerializedProperty property) return; mat.mainTexture = atlasTexture; - mat.SetColor("_Color", Color.white); + mat.SetColor(k_ColorProperty, Color.white); } else { @@ -89,12 +91,14 @@ void DrawGlyph(Rect position, SerializedProperty property) // Draw glyph from atlas texture. Rect glyphDrawPosition = new Rect(position.x, position.y + 2, 64, 80); - SerializedProperty prop_GlyphRect = property.FindPropertyRelative("m_GlyphRect"); + SerializedProperty glyphRectProperty = property.FindPropertyRelative("m_GlyphRect"); + + int padding = fontAsset.atlasPadding; - int glyphOriginX = prop_GlyphRect.FindPropertyRelative("m_X").intValue; - int glyphOriginY = prop_GlyphRect.FindPropertyRelative("m_Y").intValue; - int glyphWidth = prop_GlyphRect.FindPropertyRelative("m_Width").intValue; - int glyphHeight = prop_GlyphRect.FindPropertyRelative("m_Height").intValue; + int glyphOriginX = glyphRectProperty.FindPropertyRelative("m_X").intValue - padding; + int glyphOriginY = glyphRectProperty.FindPropertyRelative("m_Y").intValue - padding; + int glyphWidth = glyphRectProperty.FindPropertyRelative("m_Width").intValue + padding * 2; + int glyphHeight = glyphRectProperty.FindPropertyRelative("m_Height").intValue + padding * 2; float normalizedHeight = fontAsset.faceInfo.ascentLine - fontAsset.faceInfo.descentLine; float scale = glyphDrawPosition.width / normalizedHeight; diff --git a/Scripts/Editor/TMPro_FontAssetCreatorWindow.cs b/Scripts/Editor/TMPro_FontAssetCreatorWindow.cs index 60e16be..f374577 100644 --- a/Scripts/Editor/TMPro_FontAssetCreatorWindow.cs +++ b/Scripts/Editor/TMPro_FontAssetCreatorWindow.cs @@ -1294,7 +1294,7 @@ void Save_Bitmap_FontAsset(string filePath) fontAsset.characterTable = m_FontCharacterTable; // Sort glyph and character tables. - fontAsset.SortGlyphAndCharacterTables(); + fontAsset.SortAllTables(); // Get and Add Kerning Pairs to Font Asset if (m_IncludeFontFeatures) @@ -1345,7 +1345,7 @@ void Save_Bitmap_FontAsset(string filePath) fontAsset.characterTable = m_FontCharacterTable; // Sort glyph and character tables. - fontAsset.SortGlyphAndCharacterTables(); + fontAsset.SortAllTables(); // Get and Add Kerning Pairs to Font Asset if (m_IncludeFontFeatures) @@ -1473,7 +1473,7 @@ void Save_SDF_FontAsset(string filePath) fontAsset.characterTable = m_FontCharacterTable; // Sort glyph and character tables. - fontAsset.SortGlyphAndCharacterTables(); + fontAsset.SortAllTables(); // Get and Add Kerning Pairs to Font Asset if (m_IncludeFontFeatures) @@ -1533,7 +1533,7 @@ void Save_SDF_FontAsset(string filePath) fontAsset.characterTable = m_FontCharacterTable; // Sort glyph and character tables. - fontAsset.SortGlyphAndCharacterTables(); + fontAsset.SortAllTables(); // Get and Add Kerning Pairs to Font Asset // TODO: Check and preserve existing adjustment pairs. diff --git a/Scripts/Runtime/TMP_Compatibility.cs b/Scripts/Runtime/TMP_Compatibility.cs index 8db0f58..8484a5c 100644 --- a/Scripts/Runtime/TMP_Compatibility.cs +++ b/Scripts/Runtime/TMP_Compatibility.cs @@ -7,8 +7,8 @@ namespace TMPro // Class used to convert scenes and objects saved in version 0.1.44 to the new Text Container public static class TMP_Compatibility { - public enum AnchorPositions { TopLeft, Top, TopRight, Left, Center, Right, BottomLeft, Bottom, BottomRight, BaseLine, None }; - + public enum AnchorPositions { TopLeft, Top, TopRight, Left, Center, Right, BottomLeft, Bottom, BottomRight, BaseLine, None }; + /// /// Function used to convert text alignment option enumeration format. /// diff --git a/Scripts/Runtime/TMP_EditorResourceManager.cs b/Scripts/Runtime/TMP_EditorResourceManager.cs index cbe4871..b9ffa7c 100644 --- a/Scripts/Runtime/TMP_EditorResourceManager.cs +++ b/Scripts/Runtime/TMP_EditorResourceManager.cs @@ -12,10 +12,10 @@ public class TMP_EditorResourceManager private static TMP_EditorResourceManager s_Instance; private readonly List m_ObjectUpdateQueue = new List(); - private Dictionary m_ObjectUpdateQueueLookup = new Dictionary(); + private HashSet m_ObjectUpdateQueueLookup = new HashSet(); private readonly List m_ObjectReImportQueue = new List(); - private Dictionary m_ObjectReImportQueueLookup = new Dictionary(); + private HashSet m_ObjectReImportQueueLookup = new HashSet(); /// /// Get a singleton instance of the manager. @@ -62,10 +62,10 @@ private void InternalRegisterResourceForReimport(Object obj) { int id = obj.GetInstanceID(); - if (m_ObjectReImportQueueLookup.ContainsKey(id)) + if (m_ObjectReImportQueueLookup.Contains(id)) return; - m_ObjectReImportQueueLookup[id] = id; + m_ObjectReImportQueueLookup.Add(id); m_ObjectReImportQueue.Add(obj); return; @@ -84,10 +84,10 @@ private void InternalRegisterResourceForUpdate(Object obj) { int id = obj.GetInstanceID(); - if (m_ObjectUpdateQueueLookup.ContainsKey(id)) + if (m_ObjectUpdateQueueLookup.Contains(id)) return; - m_ObjectUpdateQueueLookup[id] = id; + m_ObjectUpdateQueueLookup.Add(id); m_ObjectUpdateQueue.Add(obj); return; diff --git a/Scripts/Runtime/TMP_FontAsset.cs b/Scripts/Runtime/TMP_FontAsset.cs index 7b5efce..4b58a68 100644 --- a/Scripts/Runtime/TMP_FontAsset.cs +++ b/Scripts/Runtime/TMP_FontAsset.cs @@ -42,7 +42,7 @@ public string version /// [SerializeField] internal string m_SourceFontFileGUID; - + #if UNITY_EDITOR /// /// Persistent reference to the source font file maintained in the editor. @@ -52,7 +52,7 @@ public string version #endif /// - /// Source font file when atlas population mode is set to dynamic. Null when the atlas population mode is set to static. + /// Source font file when atlas population mode is set to dynamic. Null when the atlas population mode is set to static. /// public Font sourceFontFile { @@ -216,7 +216,7 @@ public Texture2D[] atlasTextures public int atlasTextureCount { get { return m_AtlasTextureIndex + 1; } } /// - /// + /// /// public bool isMultiAtlasTexturesEnabled { get { return m_IsMultiAtlasTexturesEnabled; } set { m_IsMultiAtlasTexturesEnabled = value; } } @@ -259,7 +259,7 @@ public FaceInfo_Legacy fontInfo private FaceInfo_Legacy m_fontInfo = null; /// - /// + /// /// [SerializeField] public Texture2D atlas; // Should add a property to make this read-only. @@ -314,7 +314,7 @@ public GlyphRenderMode atlasRenderMode internal KerningTable m_KerningTable = new KerningTable(); /// - /// Table containing the various font features of this font asset. + /// Table containing the various font features of this font asset. /// public TMP_FontFeatureTable fontFeatureTable { @@ -434,7 +434,7 @@ public static TMP_FontAsset CreateFontAsset(Font font, int samplingPointSize, in Debug.LogWarning("Unable to load font face for [" + font.name + "]. Make sure \"Include Font Data\" is enabled in the Font Import Settings.", font); return null; } - + // Create new font asset TMP_FontAsset fontAsset = ScriptableObject.CreateInstance(); @@ -530,10 +530,68 @@ void Awake() } + public void ReadFontAssetDefinition() + { + Profiler.BeginSample("TMP.ReadFontAssetDefinition"); + + //Debug.Log("Reading Font Definition for " + this.name + "."); + + // Check version number of font asset to see if it needs to be upgraded. + if (this.material != null && string.IsNullOrEmpty(m_Version)) + UpgradeFontAsset(); + + // Initialize lookup tables for characters and glyphs. + InitializeDictionaryLookupTables(); + + // Add synthesized characters and adjust face metrics + AddSynthesizedCharactersAndFaceMetrics(); + + // Adjust Font Scale for compatibility reasons + if (m_FaceInfo.scale == 0) + m_FaceInfo.scale = 1.0f; + + // Set Strikethrough Offset (if needed) + if (m_FaceInfo.strikethroughOffset == 0) + m_FaceInfo.strikethroughOffset = m_FaceInfo.capLine / 2.5f; + + // Set Padding value for legacy font assets. + if (m_AtlasPadding == 0) + { + if (material.HasProperty(ShaderUtilities.ID_GradientScale)) + m_AtlasPadding = (int)material.GetFloat(ShaderUtilities.ID_GradientScale) - 1; + } + + // Compute Hashcode for the font asset name + hashCode = TMP_TextUtilities.GetSimpleHashCode(this.name); + + // Compute Hashcode for the material name + materialHashCode = TMP_TextUtilities.GetSimpleHashCode(material.name); + + // Add reference to font asset in TMP Resource Manager + //TMP_ResourceManager.AddFontAsset(this); + + m_IsFontAssetLookupTablesDirty = false; + + Profiler.EndSample(); + } + + /// /// Read the various data tables of the font asset to populate its different dictionaries to allow for faster lookup of related font asset data. /// internal void InitializeDictionaryLookupTables() + { + // Initialize and populate glyph lookup dictionary + InitializeGlyphLookupDictionary(); + + // Initialize and populate character lookup dictionary + InitializeCharacterLookupDictionary(); + + // Initialize and populate character lookup dictionary + InitializeGlyphPaidAdjustmentRecordsLookupDictionary(); + } + + internal void InitializeGlyphLookupDictionary() { // Create new instance of the glyph lookup dictionary or clear the existing one. if (m_GlyphLookupDictionary == null) @@ -541,15 +599,22 @@ internal void InitializeDictionaryLookupTables() else m_GlyphLookupDictionary.Clear(); - int glyphCount = m_GlyphTable.Count; - - // Initialize glyph index array or clear the existing one. + // Initialize or clear list of glyph indexes. if (m_GlyphIndexList == null) m_GlyphIndexList = new List(); else m_GlyphIndexList.Clear(); - // Add the characters contained in the character table into the dictionary for faster lookup. + // Initialize or clear list of glyph indexes. + if (m_GlyphIndexListNewlyAdded == null) + m_GlyphIndexListNewlyAdded = new List(); + else + m_GlyphIndexListNewlyAdded.Clear(); + + // + int glyphCount = m_GlyphTable.Count; + + // Add glyphs contained in the glyph table to dictionary for faster lookup. for (int i = 0; i < glyphCount; i++) { Glyph glyph = m_GlyphTable[i]; @@ -563,7 +628,10 @@ internal void InitializeDictionaryLookupTables() m_GlyphIndexList.Add(index); } } + } + internal void InitializeCharacterLookupDictionary() + { // Create new instance of the character lookup dictionary or clear the existing one. if (m_CharacterLookupDictionary == null) m_CharacterLookupDictionary = new Dictionary(); @@ -586,7 +654,10 @@ internal void InitializeDictionaryLookupTables() character.glyph = m_GlyphLookupDictionary[glyphIndex]; } } + } + internal void InitializeGlyphPaidAdjustmentRecordsLookupDictionary() + { // Upgrade Glyph Adjustment Table to the new Font Feature table and Glyph Pair Adjustment Records if (m_KerningTable != null && m_KerningTable.kerningPairs != null && m_KerningTable.kerningPairs.Count > 0) UpgradeGlyphAdjustmentTableToFontFeatureTable(); @@ -612,22 +683,8 @@ internal void InitializeDictionaryLookupTables() } } - - /// - /// - /// - public void ReadFontAssetDefinition() + internal void AddSynthesizedCharactersAndFaceMetrics() { - //Debug.Log("Reading Font Definition for " + this.name + "."); - - // Check version number of font asset to see if it needs to be upgraded. - if (this.material != null && string.IsNullOrEmpty(m_Version)) - UpgradeFontAsset(); - - // Initialize lookup tables for characters and glyphs. - InitializeDictionaryLookupTables(); - - // Add Tab char(9) to Dictionary. if (m_CharacterLookupDictionary.ContainsKey(9) == false) { Glyph glyph = new Glyph(0, new GlyphMetrics(0, 0, 0, 0, m_FaceInfo.tabWidth * tabSize), GlyphRect.zero, 1.0f, 0); @@ -650,98 +707,76 @@ public void ReadFontAssetDefinition() m_CharacterLookupDictionary.Add(3, new TMP_Character(3, glyph)); } - // Add Arabic Letter Mark (0x061C) + // Add Arabic Letter Mark \u061C if (m_CharacterLookupDictionary.ContainsKey(0x061C) == false) { Glyph glyph = new Glyph(0, new GlyphMetrics(0, 0, 0, 0, 0), GlyphRect.zero, 1.0f, 0); m_CharacterLookupDictionary.Add(0x061C, new TMP_Character(0x061C, glyph)); } - // Add Zero Width Space 8203 (0x200B) - if (m_CharacterLookupDictionary.ContainsKey(8203) == false) + // Add Zero Width Space \u200B + if (m_CharacterLookupDictionary.ContainsKey(0x200B) == false) { Glyph glyph = new Glyph(0, new GlyphMetrics(0, 0, 0, 0, 0), GlyphRect.zero, 1.0f, 0); - m_CharacterLookupDictionary.Add(8203, new TMP_Character(8203, glyph)); + m_CharacterLookupDictionary.Add(0x200B, new TMP_Character(0x200B, glyph)); } - // Add Left-To-Right Mark (0x200E) + // Add Left-To-Right Mark \u200E if (m_CharacterLookupDictionary.ContainsKey(0x200E) == false) { Glyph glyph = new Glyph(0, new GlyphMetrics(0, 0, 0, 0, 0), GlyphRect.zero, 1.0f, 0); m_CharacterLookupDictionary.Add(0x200E, new TMP_Character(0x200E, glyph)); } - // Add Right-To-Left Mark (0x200F) + // Add Right-To-Left Mark \u200F if (m_CharacterLookupDictionary.ContainsKey(0x200F) == false) { Glyph glyph = new Glyph(0, new GlyphMetrics(0, 0, 0, 0, 0), GlyphRect.zero, 1.0f, 0); m_CharacterLookupDictionary.Add(0x200F, new TMP_Character(0x200F, glyph)); } - // Add Line Separator 0x2028 + // Add Line Separator \u2028 if (m_CharacterLookupDictionary.ContainsKey(0x2028) == false) { Glyph glyph = new Glyph(0, new GlyphMetrics(0, 0, 0, 0, 0), GlyphRect.zero, 1.0f, 0); m_CharacterLookupDictionary.Add(0x2028, new TMP_Character(0x2028, glyph)); } - // Add Line Separator 0x2029 + // Add Line Separator \u2029 if (m_CharacterLookupDictionary.ContainsKey(0x2029) == false) { Glyph glyph = new Glyph(0, new GlyphMetrics(0, 0, 0, 0, 0), GlyphRect.zero, 1.0f, 0); m_CharacterLookupDictionary.Add(0x2029, new TMP_Character(0x2029, glyph)); } - - // Add Zero Width Non-Breaking Space 8288 (0x2060) - if (m_CharacterLookupDictionary.ContainsKey(8288) == false) + // Add Zero Width Non-Breaking Space \u2060 + if (m_CharacterLookupDictionary.ContainsKey(0x2060) == false) { Glyph glyph = new Glyph(0, new GlyphMetrics(0, 0, 0, 0, 0), GlyphRect.zero, 1.0f, 0); - m_CharacterLookupDictionary.Add(8288, new TMP_Character(8288, glyph)); + m_CharacterLookupDictionary.Add(0x2060, new TMP_Character(0x2060, glyph)); } - // Add Non-Breaking Hyphen 8209 (0x2011) - if (m_CharacterLookupDictionary.ContainsKey(8209) == false) + // Add Non-Breaking Hyphen \u2011 + if (m_CharacterLookupDictionary.ContainsKey(0x2011) == false) { TMP_Character character; - if (m_CharacterLookupDictionary.TryGetValue(45, out character)) - m_CharacterLookupDictionary.Add(8209, new TMP_Character(8209, character.glyph)); + m_CharacterLookupDictionary.Add(0x2011, new TMP_Character(0x2011, character.glyph)); } - - // Set Cap Height - if (m_FaceInfo.capLine == 0 && m_CharacterLookupDictionary.ContainsKey(72)) + // Set Cap Line using the capital letter 'X' + if (m_FaceInfo.capLine == 0 && m_CharacterLookupDictionary.ContainsKey('X')) { - uint glyphIndex = m_CharacterLookupDictionary[72].glyphIndex; + uint glyphIndex = m_CharacterLookupDictionary['X'].glyphIndex; m_FaceInfo.capLine = m_GlyphLookupDictionary[glyphIndex].metrics.horizontalBearingY; } - // Adjust Font Scale for compatibility reasons - if (m_FaceInfo.scale == 0) - m_FaceInfo.scale = 1.0f; - - // Set Strikethrough Offset (if needed) - if (m_FaceInfo.strikethroughOffset == 0) - m_FaceInfo.strikethroughOffset = m_FaceInfo.capLine / 2.5f; - - // Set Padding value for legacy font assets. - if (m_AtlasPadding == 0) + // Set Mean Line using the lowercase letter 'x' + if (m_FaceInfo.meanLine == 0 && m_CharacterLookupDictionary.ContainsKey('x')) { - if (material.HasProperty(ShaderUtilities.ID_GradientScale)) - m_AtlasPadding = (int)material.GetFloat(ShaderUtilities.ID_GradientScale) - 1; + uint glyphIndex = m_CharacterLookupDictionary['x'].glyphIndex; + m_FaceInfo.meanLine = m_GlyphLookupDictionary[glyphIndex].metrics.horizontalBearingY; } - - // Compute Hashcode for the font asset name - hashCode = TMP_TextUtilities.GetSimpleHashCode(this.name); - - // Compute Hashcode for the material name - materialHashCode = TMP_TextUtilities.GetSimpleHashCode(material.name); - - // Add reference to font asset in TMP Resource Manager - //TMP_ResourceManager.AddFontAsset(this); - - m_IsFontAssetLookupTablesDirty = false; } @@ -763,13 +798,19 @@ internal void SortGlyphTable() m_GlyphTable = m_GlyphTable.OrderBy(c => c.index).ToList(); } + internal void SortFontFeatureTable() + { + m_FontFeatureTable.SortGlyphPairAdjustmentRecords(); + } + /// /// Sort both glyph and character tables. /// - internal void SortGlyphAndCharacterTables() + internal void SortAllTables() { SortGlyphTable(); SortCharacterTable(); + SortFontFeatureTable(); } @@ -944,7 +985,7 @@ public bool HasCharacters(string text, out List missingCharacters) } /// - /// + /// /// /// /// @@ -963,7 +1004,7 @@ public bool HasCharacters(string text, out uint[] missingCharacters, bool search return false; } - // Clear internal list of + // Clear internal list of s_MissingCharacterList.Clear(); for (int i = 0; i < text.Length; i++) @@ -1094,68 +1135,73 @@ public static int[] GetCharactersArray(TMP_FontAsset fontAsset) // ================================================================================ // List and HashSet used for tracking font assets whose font atlas texture and character data needs updating. - private static List k_FontAssetsToUpdate = new List(); - private static HashSet k_FontAssetsToUpdateLookup = new HashSet(); + private static List k_FontAssets_FontFeaturesUpdateQueue = new List(); + private static HashSet k_FontAssets_FontFeaturesUpdateQueueLookup = new HashSet(); - /// - /// Determines if the font asset is already registered to be updated. - /// - //private bool m_IsAlreadyRegisteredForUpdate; + private static List k_FontAssets_AtlasTexturesUpdateQueue = new List(); + private static HashSet k_FontAssets_AtlasTexturesUpdateQueueLookup = new HashSet(); /// - /// List of glyphs that need to be added / packed in atlas texture. + /// /// - private List m_GlyphsToPack = new List(); + //private bool m_IsAlreadyRegisteredForUpdate; /// - /// List of glyphs that have been packed in the atlas texture and ready to be rendered. + /// List of glyphs that need to be rendered and added to an atlas texture. /// - private List m_GlyphsPacked = new List(); + private List m_GlyphsToRender = new List(); /// - /// + /// List of glyphs that we just rendered and added to an atlas texture. /// - private List m_GlyphsToRender = new List(); + private List m_GlyphsRendered = new List(); /// - /// List used in the process of adding new glyphs to the atlas texture. + /// List of all the glyph indexes contained in the font asset. /// private List m_GlyphIndexList = new List(); /// - /// Internal static array used to avoid allocations when using the GetGlyphPairAdjustmentTable(). + /// List of glyph indexes newly added to the font asset. + /// This list is used in the process of retrieving font features. /// - internal static uint[] s_GlyphIndexArray = new uint[16]; + private List m_GlyphIndexListNewlyAdded = new List(); /// - /// + /// /// - internal static List s_GlyphsToAdd = new List(16); - internal static HashSet s_GlyphsToAddLookup = new HashSet(); + internal List m_GlyphsToAdd = new List(); + internal HashSet m_GlyphsToAddLookup = new HashSet(); - internal static List s_CharactersToAdd = new List(); - internal static HashSet s_CharactersToAddLookup = new HashSet(); + internal List m_CharactersToAdd = new List(); + internal HashSet m_CharactersToAddLookup = new HashSet(); /// - /// Internal static list used to track characters that could not be added to the font asset. + /// Internal list used to track characters that could not be added to the font asset. /// - internal static List s_MissingCharacterList = new List(16); + internal List s_MissingCharacterList = new List(); /// /// Hash table used to track characters that are known to be missing from the font file. /// internal HashSet m_MissingUnicodesFromFontFile = new HashSet(); + /// + /// Internal static array used to avoid allocations when using the GetGlyphPairAdjustmentTable(). + /// + internal static uint[] k_GlyphIndexArray; + /// /// Try adding the characters from the provided string to the font asset. /// /// Array that contains the characters to add to the font asset. + /// /// Returns true if all the characters were successfully added to the font asset. Return false otherwise. - public bool TryAddCharacters(uint[] unicodes) + public bool TryAddCharacters(uint[] unicodes, bool includeFontFeatures = false) { uint[] missingUnicodes; - return TryAddCharacters(unicodes, out missingUnicodes); + return TryAddCharacters(unicodes, out missingUnicodes, includeFontFeatures); } /// @@ -1163,8 +1209,9 @@ public bool TryAddCharacters(uint[] unicodes) /// /// Array that contains the characters to add to the font asset. /// Array containing the characters that could not be added to the font asset. + /// /// Returns true if all the characters were successfully added to the font asset. Return false otherwise. - public bool TryAddCharacters(uint[] unicodes, out uint[] missingUnicodes) + public bool TryAddCharacters(uint[] unicodes, out uint[] missingUnicodes, bool includeFontFeatures = false) { Profiler.BeginSample("TMP.TryAddCharacters"); @@ -1174,9 +1221,7 @@ public bool TryAddCharacters(uint[] unicodes, out uint[] missingUnicodes) if (m_AtlasPopulationMode == AtlasPopulationMode.Static) Debug.LogWarning("Unable to add characters to font asset [" + this.name + "] because its AtlasPopulationMode is set to Static.", this); else - { Debug.LogWarning("Unable to add characters to font asset [" + this.name + "] because the provided Unicode list is Null or Empty.", this); - } missingUnicodes = unicodes.ToArray(); Profiler.EndSample(); @@ -1192,10 +1237,10 @@ public bool TryAddCharacters(uint[] unicodes, out uint[] missingUnicodes) } // Clear lists used to track which character and glyphs were added or missing. - s_GlyphsToAdd.Clear(); - s_GlyphsToAddLookup.Clear(); - s_CharactersToAdd.Clear(); - s_CharactersToAddLookup.Clear(); + m_GlyphsToAdd.Clear(); + m_GlyphsToAddLookup.Clear(); + m_CharactersToAdd.Clear(); + m_CharactersToAddLookup.Clear(); s_MissingCharacterList.Clear(); bool isMissingCharacters = false; @@ -1209,7 +1254,7 @@ public bool TryAddCharacters(uint[] unicodes, out uint[] missingUnicodes) if (m_CharacterLookupDictionary.ContainsKey(unicode)) continue; - // Get the index of the glyph for this unicode value. + // Get the index of the glyph for this Unicode value. uint glyphIndex = FontEngine.GetGlyphIndex(unicode); // Skip missing glyphs @@ -1234,21 +1279,21 @@ public bool TryAddCharacters(uint[] unicodes, out uint[] missingUnicodes) } // Make sure glyph index has not already been added to list of glyphs to add. - if (s_GlyphsToAddLookup.Contains(glyphIndex) == false) + if (m_GlyphsToAddLookup.Contains(glyphIndex) == false) { - s_GlyphsToAddLookup.Add(glyphIndex); - s_GlyphsToAdd.Add(glyphIndex); + m_GlyphsToAddLookup.Add(glyphIndex); + m_GlyphsToAdd.Add(glyphIndex); } // Make sure unicode / character has not already been added. - if (s_CharactersToAddLookup.Contains(unicode) == false) + if (m_CharactersToAddLookup.Contains(unicode) == false) { - s_CharactersToAddLookup.Add(unicode); - s_CharactersToAdd.Add(character); + m_CharactersToAddLookup.Add(unicode); + m_CharactersToAdd.Add(character); } } - if (s_GlyphsToAdd.Count == 0) + if (m_GlyphsToAdd.Count == 0) { //Debug.LogWarning("No characters will be added to font asset [" + this.name + "] either because they are already present in the font asset or missing from the font file."); missingUnicodes = unicodes; @@ -1264,31 +1309,36 @@ public bool TryAddCharacters(uint[] unicodes, out uint[] missingUnicodes) } Glyph[] glyphs; - bool allGlyphsAddedToTexture = FontEngine.TryAddGlyphsToTexture(s_GlyphsToAdd, m_AtlasPadding, GlyphPackingMode.BestShortSideFit, m_FreeGlyphRects, m_UsedGlyphRects, m_AtlasRenderMode, m_AtlasTextures[m_AtlasTextureIndex], out glyphs); + bool allGlyphsAddedToTexture = FontEngine.TryAddGlyphsToTexture(m_GlyphsToAdd, m_AtlasPadding, GlyphPackingMode.BestShortSideFit, m_FreeGlyphRects, m_UsedGlyphRects, m_AtlasRenderMode, m_AtlasTextures[m_AtlasTextureIndex], out glyphs); // Add new glyphs to relevant font asset data structure for (int i = 0; i < glyphs.Length && glyphs[i] != null; i++) { Glyph glyph = glyphs[i]; + uint glyphIndex = glyph.index; + glyph.atlasIndex = m_AtlasTextureIndex; // Add new glyph to glyph table. m_GlyphTable.Add(glyph); - m_GlyphLookupDictionary.Add(glyph.index, glyph); + m_GlyphLookupDictionary.Add(glyphIndex, glyph); + + m_GlyphIndexListNewlyAdded.Add(glyphIndex); + m_GlyphIndexList.Add(glyphIndex); } // Clear glyph index list to allow - s_GlyphsToAdd.Clear(); + m_GlyphsToAdd.Clear(); // Add new characters to relevant data structures as well as track glyphs that could not be added to the current atlas texture. - for (int i = 0; i < s_CharactersToAdd.Count; i++) + for (int i = 0; i < m_CharactersToAdd.Count; i++) { - TMP_Character character = s_CharactersToAdd[i]; + TMP_Character character = m_CharactersToAdd[i]; Glyph glyph; if (m_GlyphLookupDictionary.TryGetValue(character.glyphIndex, out glyph) == false) { - s_GlyphsToAdd.Add(character.glyphIndex); + m_GlyphsToAdd.Add(character.glyphIndex); continue; } @@ -1297,17 +1347,21 @@ public bool TryAddCharacters(uint[] unicodes, out uint[] missingUnicodes) m_CharacterLookupDictionary.Add(character.unicode, character); // Remove character from list to add - s_CharactersToAdd.RemoveAt(i); + m_CharactersToAdd.RemoveAt(i); i -= 1; } - // Try adding missing glyphs to + // Try adding missing glyphs to if (m_IsMultiAtlasTexturesEnabled && allGlyphsAddedToTexture == false) { while (allGlyphsAddedToTexture == false) allGlyphsAddedToTexture = TryAddGlyphsToNewAtlasTexture(); } + // Get Font Features for the given characters + if (includeFontFeatures) + UpdateGlyphAdjustmentRecords(); + #if UNITY_EDITOR // Makes the changes to the font asset persistent. if (UnityEditor.EditorUtility.IsPersistent(this)) @@ -1317,9 +1371,9 @@ public bool TryAddCharacters(uint[] unicodes, out uint[] missingUnicodes) #endif // Populate list of missing characters - for (int i = 0; i < s_CharactersToAdd.Count; i++) + for (int i = 0; i < m_CharactersToAdd.Count; i++) { - TMP_Character character = s_CharactersToAdd[i]; + TMP_Character character = m_CharactersToAdd[i]; s_MissingCharacterList.Add(character.unicode); } @@ -1337,6 +1391,7 @@ public bool TryAddCharacters(uint[] unicodes, out uint[] missingUnicodes) /// Try adding the characters from the provided string to the font asset. /// /// String containing the characters to add to the font asset. + /// /// Returns true if all the characters were successfully added to the font asset. Return false otherwise. public bool TryAddCharacters(string characters, bool includeFontFeatures = false) { @@ -1351,6 +1406,7 @@ public bool TryAddCharacters(string characters, bool includeFontFeatures = false /// /// String containing the characters to add to the font asset. /// String containing the characters that could not be added to the font asset. + /// /// Returns true if all the characters were successfully added to the font asset. Return false otherwise. public bool TryAddCharacters(string characters, out string missingCharacters, bool includeFontFeatures = false) { @@ -1380,10 +1436,10 @@ public bool TryAddCharacters(string characters, out string missingCharacters, bo } // Clear data structures used to track which glyph needs to be added to atlas texture. - s_GlyphsToAdd.Clear(); - s_GlyphsToAddLookup.Clear(); - s_CharactersToAdd.Clear(); - s_CharactersToAddLookup.Clear(); + m_GlyphsToAdd.Clear(); + m_GlyphsToAddLookup.Clear(); + m_CharactersToAdd.Clear(); + m_CharactersToAddLookup.Clear(); s_MissingCharacterList.Clear(); bool isMissingCharacters = false; @@ -1423,21 +1479,21 @@ public bool TryAddCharacters(string characters, out string missingCharacters, bo } // Make sure glyph index has not already been added to list of glyphs to add. - if (s_GlyphsToAddLookup.Contains(glyphIndex) == false) + if (m_GlyphsToAddLookup.Contains(glyphIndex) == false) { - s_GlyphsToAddLookup.Add(glyphIndex); - s_GlyphsToAdd.Add(glyphIndex); + m_GlyphsToAddLookup.Add(glyphIndex); + m_GlyphsToAdd.Add(glyphIndex); } // Make sure unicode / character has not already been added. - if (s_CharactersToAddLookup.Contains(unicode) == false) + if (m_CharactersToAddLookup.Contains(unicode) == false) { - s_CharactersToAddLookup.Add(unicode); - s_CharactersToAdd.Add(character); + m_CharactersToAddLookup.Add(unicode); + m_CharactersToAdd.Add(character); } } - if (s_GlyphsToAdd.Count == 0) + if (m_GlyphsToAdd.Count == 0) { missingCharacters = characters; Profiler.EndSample(); @@ -1454,30 +1510,35 @@ public bool TryAddCharacters(string characters, out string missingCharacters, bo Glyph[] glyphs; - bool allGlyphsAddedToTexture = FontEngine.TryAddGlyphsToTexture(s_GlyphsToAdd, m_AtlasPadding, GlyphPackingMode.BestShortSideFit, m_FreeGlyphRects, m_UsedGlyphRects, m_AtlasRenderMode, m_AtlasTextures[m_AtlasTextureIndex], out glyphs); + bool allGlyphsAddedToTexture = FontEngine.TryAddGlyphsToTexture(m_GlyphsToAdd, m_AtlasPadding, GlyphPackingMode.BestShortSideFit, m_FreeGlyphRects, m_UsedGlyphRects, m_AtlasRenderMode, m_AtlasTextures[m_AtlasTextureIndex], out glyphs); for (int i = 0; i < glyphs.Length && glyphs[i] != null; i++) { Glyph glyph = glyphs[i]; + uint glyphIndex = glyph.index; + glyph.atlasIndex = m_AtlasTextureIndex; // Add new glyph to glyph table. m_GlyphTable.Add(glyph); - m_GlyphLookupDictionary.Add(glyph.index, glyph); + m_GlyphLookupDictionary.Add(glyphIndex, glyph); + + m_GlyphIndexListNewlyAdded.Add(glyphIndex); + m_GlyphIndexList.Add(glyphIndex); } - // Clear glyph index list to track glyphs that were not added to the atlas texture - s_GlyphsToAdd.Clear(); + // Clear glyph index list to track glyphs that were not added to the atlas texture + m_GlyphsToAdd.Clear(); // Add new characters to relevant data structures. - for (int i = 0; i < s_CharactersToAdd.Count; i++) + for (int i = 0; i < m_CharactersToAdd.Count; i++) { - TMP_Character character = s_CharactersToAdd[i]; + TMP_Character character = m_CharactersToAdd[i]; Glyph glyph; if (m_GlyphLookupDictionary.TryGetValue(character.glyphIndex, out glyph) == false) { - s_GlyphsToAdd.Add(character.glyphIndex); + m_GlyphsToAdd.Add(character.glyphIndex); continue; } @@ -1486,17 +1547,21 @@ public bool TryAddCharacters(string characters, out string missingCharacters, bo m_CharacterLookupDictionary.Add(character.unicode, character); // Remove character from list to add - s_CharactersToAdd.RemoveAt(i); + m_CharactersToAdd.RemoveAt(i); i -= 1; } - // Try adding glyphs that didn't fit in the current atlas texture to new atlas texture + // Try adding glyphs that didn't fit in the current atlas texture to new atlas texture if (m_IsMultiAtlasTexturesEnabled && allGlyphsAddedToTexture == false) { while (allGlyphsAddedToTexture == false) allGlyphsAddedToTexture = TryAddGlyphsToNewAtlasTexture(); } + // Get Font Features for the given characters + if (includeFontFeatures) + UpdateGlyphAdjustmentRecords(); + #if UNITY_EDITOR // Makes the changes to the font asset persistent. if (UnityEditor.EditorUtility.IsPersistent(this)) @@ -1505,15 +1570,12 @@ public bool TryAddCharacters(string characters, out string missingCharacters, bo } #endif - // Get Font Features for the given characters - //GetFontFeatures() - missingCharacters = string.Empty; // Populate list of missing characters - for (int i = 0; i < s_CharactersToAdd.Count; i++) + for (int i = 0; i < m_CharactersToAdd.Count; i++) { - TMP_Character character = s_CharactersToAdd[i]; + TMP_Character character = m_CharactersToAdd[i]; s_MissingCharacterList.Add(character.unicode); } @@ -1525,6 +1587,8 @@ public bool TryAddCharacters(string characters, out string missingCharacters, bo } + // Functions to remove... + /* /// /// NOT USED CURRENTLY - Try adding character using Unicode value to font asset. /// @@ -1534,7 +1598,7 @@ public bool TryAddCharacters(string characters, out string missingCharacters, bo internal bool TryAddCharacter_Internal(uint unicode) { TMP_Character character = null; - + // Check if character is already contained in the character table. if (m_CharacterLookupDictionary.ContainsKey(unicode)) return true; @@ -1628,7 +1692,7 @@ internal TMP_Character AddCharacter_Internal(uint unicode, Glyph glyph) } else { - // Try packing new glyph + // Try packing new glyph if (FontEngine.TryPackGlyphInAtlas(glyph, m_AtlasPadding, GlyphPackingMode.ContactPointRule, m_AtlasRenderMode, m_AtlasWidth, m_AtlasHeight, m_FreeGlyphRects, m_UsedGlyphRects) == false) { // TODO: Add handling to create new atlas texture to fit glyph. @@ -1636,7 +1700,7 @@ internal TMP_Character AddCharacter_Internal(uint unicode, Glyph glyph) return null; } - m_GlyphsToRender.Add(glyph); + //m_GlyphsToRender.Add(glyph); } } @@ -1649,7 +1713,7 @@ internal TMP_Character AddCharacter_Internal(uint unicode, Glyph glyph) // Schedule glyph to be added to the font atlas texture //TM_FontAssetUpdateManager.RegisterFontAssetForUpdate(this); - UpdateAtlasTexture(); // Temporary until callback system is revised. + //UpdateAtlasTexture(); // Temporary until callback system is revised. //#if UNITY_EDITOR // Makes the changes to the font asset persistent. @@ -1661,6 +1725,7 @@ internal TMP_Character AddCharacter_Internal(uint unicode, Glyph glyph) return character; } + */ /// @@ -1676,7 +1741,7 @@ internal bool TryAddCharacterInternal(uint unicode, out TMP_Character character) character = null; - // Check if the unicode character is already known to be missing from the source font file. + // Check if the Unicode character is already known to be missing from the source font file. if (m_MissingUnicodesFromFontFile.Contains(unicode)) { Profiler.EndSample(); @@ -1704,12 +1769,6 @@ internal bool TryAddCharacterInternal(uint unicode, out TMP_Character character) m_CharacterTable.Add(character); m_CharacterLookupDictionary.Add(unicode, character); - if (TMP_Settings.getFontFeaturesAtRuntime) - { - if (k_FontAssetsToUpdateLookup.Add(instanceID)) - k_FontAssetsToUpdate.Add(this); - } - #if UNITY_EDITOR // Makes the changes to the font asset persistent. // OPTIMIZATION: This could be handled when exiting Play mode if we added any new characters to the asset. @@ -1746,7 +1805,10 @@ internal bool TryAddCharacterInternal(uint unicode, out TMP_Character character) // m_GlyphIndexList.Add(glyphIndex); // if (TMP_Settings.getFontFeaturesAtRuntime) - // UpdateGlyphAdjustmentRecords(unicode, glyphIndex); + // { + // if (k_FontAssetsToUpdateLookup.Add(instanceID)) + // k_FontAssetsToUpdate.Add(this); + // } // //Profiler.EndSample(); @@ -1776,7 +1838,7 @@ internal bool TryAddCharacterInternal(uint unicode, out TMP_Character character) { // Update glyph atlas index glyph.atlasIndex = m_AtlasTextureIndex; - + // Add new glyph to glyph table. m_GlyphTable.Add(glyph); m_GlyphLookupDictionary.Add(glyphIndex, glyph); @@ -1787,12 +1849,10 @@ internal bool TryAddCharacterInternal(uint unicode, out TMP_Character character) m_CharacterLookupDictionary.Add(unicode, character); m_GlyphIndexList.Add(glyphIndex); + m_GlyphIndexListNewlyAdded.Add(glyphIndex); if (TMP_Settings.getFontFeaturesAtRuntime) - { - if (k_FontAssetsToUpdateLookup.Add(instanceID)) - k_FontAssetsToUpdate.Add(this); - } + RegisterFontAssetForFontFeatureUpdate(this); #if UNITY_EDITOR // Makes the changes to the font asset persistent. @@ -1821,7 +1881,7 @@ internal bool TryAddCharacterInternal(uint unicode, out TMP_Character character) { // Update glyph atlas index glyph.atlasIndex = m_AtlasTextureIndex; - + // Add new glyph to glyph table. m_GlyphTable.Add(glyph); m_GlyphLookupDictionary.Add(glyphIndex, glyph); @@ -1832,12 +1892,10 @@ internal bool TryAddCharacterInternal(uint unicode, out TMP_Character character) m_CharacterLookupDictionary.Add(unicode, character); m_GlyphIndexList.Add(glyphIndex); + m_GlyphIndexListNewlyAdded.Add(glyphIndex); if (TMP_Settings.getFontFeaturesAtRuntime) - { - if (k_FontAssetsToUpdateLookup.Add(instanceID)) - k_FontAssetsToUpdate.Add(this); - } + RegisterFontAssetForFontFeatureUpdate(this); #if UNITY_EDITOR //SortGlyphTable(); @@ -1859,96 +1917,253 @@ internal bool TryAddCharacterInternal(uint unicode, out TMP_Character character) } - /// - /// - /// - /// - bool TryAddGlyphsToNewAtlasTexture() + internal bool TryGetCharacter_and_QueueRenderToTexture(uint unicode, out TMP_Character character) { - // Create and prepare new atlas texture - SetupNewAtlasTexture(); - - Glyph[] glyphs; + Profiler.BeginSample("TMP.TryAddCharacter"); - // Try adding remaining glyphs in the newly created atlas texture - bool allGlyphsAddedToTexture = FontEngine.TryAddGlyphsToTexture(s_GlyphsToAdd, m_AtlasPadding, GlyphPackingMode.BestShortSideFit, m_FreeGlyphRects, m_UsedGlyphRects, m_AtlasRenderMode, m_AtlasTextures[m_AtlasTextureIndex], out glyphs); + character = null; - // Add new glyphs to relevant data structures. - for (int i = 0; i < glyphs.Length && glyphs[i] != null; i++) + // Check if the Unicode character is already known to be missing from the source font file. + if (m_MissingUnicodesFromFontFile.Contains(unicode)) { - Glyph glyph = glyphs[i]; - glyph.atlasIndex = m_AtlasTextureIndex; + Profiler.EndSample(); - // Add new glyph to glyph table. - m_GlyphTable.Add(glyph); - m_GlyphLookupDictionary.Add(glyph.index, glyph); + return false; } - // Clear glyph index list to allow us to track glyphs - s_GlyphsToAdd.Clear(); + // Load font face. + if (FontEngine.LoadFontFace(sourceFontFile, m_FaceInfo.pointSize) != FontEngineError.Success) + return false; - // Add new characters to relevant data structures as well as track glyphs that could not be added to the current atlas texture. - for (int i = 0; i < s_CharactersToAdd.Count; i++) + uint glyphIndex = FontEngine.GetGlyphIndex(unicode); + if (glyphIndex == 0) { - TMP_Character character = s_CharactersToAdd[i]; - Glyph glyph; + m_MissingUnicodesFromFontFile.Add(unicode); - if (m_GlyphLookupDictionary.TryGetValue(character.glyphIndex, out glyph) == false) + Profiler.EndSample(); + return false; + } + + // Check if glyph is already contained in the font asset as the same glyph might be referenced by multiple characters. + if (m_GlyphLookupDictionary.ContainsKey(glyphIndex)) + { + character = new TMP_Character(unicode, m_GlyphLookupDictionary[glyphIndex]); + m_CharacterTable.Add(character); + m_CharacterLookupDictionary.Add(unicode, character); + + #if UNITY_EDITOR + // Makes the changes to the font asset persistent. + // OPTIMIZATION: This could be handled when exiting Play mode if we added any new characters to the asset. + // Could also add some update registry to handle this. + //SortGlyphTable(); + if (UnityEditor.EditorUtility.IsPersistent(this)) { - s_GlyphsToAdd.Add(character.glyphIndex); - continue; + TMP_EditorResourceManager.RegisterResourceForUpdate(this); } + #endif - character.glyph = glyph; - m_CharacterTable.Add(character); - m_CharacterLookupDictionary.Add(character.unicode, character); + Profiler.EndSample(); - // Remove character - s_CharactersToAdd.RemoveAt(i); - i -= 1; + return true; } - return allGlyphsAddedToTexture; - } + GlyphLoadFlags glyphLoadFlags = ((GlyphRasterModes)m_AtlasRenderMode & GlyphRasterModes.RASTER_MODE_NO_HINTING) == GlyphRasterModes.RASTER_MODE_NO_HINTING ? GlyphLoadFlags.LOAD_NO_BITMAP | GlyphLoadFlags.LOAD_NO_HINTING : GlyphLoadFlags.LOAD_NO_BITMAP; + Glyph glyph = null; + if (FontEngine.TryGetGlyphWithIndexValue(glyphIndex, glyphLoadFlags, out glyph)) + { + // Add new glyph to glyph table. + m_GlyphTable.Add(glyph); + m_GlyphLookupDictionary.Add(glyphIndex, glyph); - /// - /// - /// - void SetupNewAtlasTexture() - { - m_AtlasTextureIndex += 1; + // Add new character + character = new TMP_Character(unicode, glyph); + m_CharacterTable.Add(character); + m_CharacterLookupDictionary.Add(unicode, character); - // Check size of atlas texture array - if (m_AtlasTextures.Length == m_AtlasTextureIndex) - Array.Resize(ref m_AtlasTextures, m_AtlasTextures.Length * 2); + m_GlyphIndexList.Add(glyphIndex); + m_GlyphIndexListNewlyAdded.Add(glyphIndex); - // Initialize new atlas texture - m_AtlasTextures[m_AtlasTextureIndex] = new Texture2D(m_AtlasWidth, m_AtlasHeight, TextureFormat.Alpha8, false); - FontEngine.ResetAtlasTexture(m_AtlasTextures[m_AtlasTextureIndex]); + if (TMP_Settings.getFontFeaturesAtRuntime) + RegisterFontAssetForFontFeatureUpdate(this); - // Clear packing GlyphRects - int packingModifier = ((GlyphRasterModes)m_AtlasRenderMode & GlyphRasterModes.RASTER_MODE_BITMAP) == GlyphRasterModes.RASTER_MODE_BITMAP ? 0 : 1; - m_FreeGlyphRects.Clear(); - m_FreeGlyphRects.Add(new GlyphRect(0, 0, m_AtlasWidth - packingModifier, m_AtlasHeight - packingModifier)); - m_UsedGlyphRects.Clear(); + // Add glyph to list of glyphs to be rendered + m_GlyphsToRender.Add(glyph); - #if UNITY_EDITOR - // Add new texture as sub asset to font asset - if (UnityEditor.EditorUtility.IsPersistent(this)) - { - Texture2D tex = m_AtlasTextures[m_AtlasTextureIndex]; - tex.name = m_AtlasTexture.name + " " + m_AtlasTextureIndex; + // Register font asset to render and add glyphs to atlas textures + RegisterFontAssetForAtlasTextureUpdate(this); - UnityEditor.AssetDatabase.AddObjectToAsset(m_AtlasTextures[m_AtlasTextureIndex], this); - TMP_EditorResourceManager.RegisterResourceForReimport(this); + #if UNITY_EDITOR + // Makes the changes to the font asset persistent. + // OPTIMIZATION: This could be handled when exiting Play mode if we added any new characters to the asset. + // Could also add some update registry to handle this. + //SortGlyphTable(); + if (UnityEditor.EditorUtility.IsPersistent(this)) + { + TMP_EditorResourceManager.RegisterResourceForUpdate(this); + } + #endif + + Profiler.EndSample(); + + return true; } - #endif - } + Profiler.EndSample(); + + return false; + } /// - /// Internal function used to get the glyph index for the given unicode. + /// This function requires an update to the TextCore:FontEngine + /// + internal void TryAddGlyphsToAtlasTextures() + { + /* + // Return if we don't have any glyphs to add. + if (m_GlyphsToRender.Count == 0) + return; + + Profiler.BeginSample("TMP.TryAddGlyphsToAtlasTextures"); + + // Resize the Atlas Texture to the appropriate size + if (m_AtlasTextures[m_AtlasTextureIndex].width == 0 || m_AtlasTextures[m_AtlasTextureIndex].height == 0) + { + //Debug.Log("Setting initial size of atlas texture used by font asset [" + this.name + "]."); + m_AtlasTextures[m_AtlasTextureIndex].Resize(m_AtlasWidth, m_AtlasHeight); + FontEngine.ResetAtlasTexture(m_AtlasTextures[m_AtlasTextureIndex]); + } + + bool allGlyphsAddedToTexture = FontEngine.TryAddGlyphsToTexture(m_GlyphsToRender, m_GlyphsRendered, m_AtlasPadding, GlyphPackingMode.BestShortSideFit, m_FreeGlyphRects, m_UsedGlyphRects, m_AtlasRenderMode, m_AtlasTextures[m_AtlasTextureIndex]); + + // Try adding glyphs that didn't fit in the current atlas texture to new atlas texture + if (m_IsMultiAtlasTexturesEnabled && allGlyphsAddedToTexture == false) + { + while (allGlyphsAddedToTexture == false) + { + // Create and prepare new atlas texture + SetupNewAtlasTexture(); + + // Try adding remaining glyphs in the newly created atlas texture + allGlyphsAddedToTexture = FontEngine.TryAddGlyphsToTexture(m_GlyphsToRender, m_GlyphsRendered, m_AtlasPadding, GlyphPackingMode.BestShortSideFit, m_FreeGlyphRects, m_UsedGlyphRects, m_AtlasRenderMode, m_AtlasTextures[m_AtlasTextureIndex]); + } + } + + if (allGlyphsAddedToTexture == false) + { + // TODO: Handle case when we have left over glyph to render that didn't fit in the atlas texture. + Debug.LogError("Unable to add some glyphs to atlas texture."); + } + + #if UNITY_EDITOR + // Makes the changes to the font asset persistent. + if (UnityEditor.EditorUtility.IsPersistent(this)) + { + //SortGlyphAndCharacterTables(); + TMP_EditorResourceManager.RegisterResourceForUpdate(this); + } + #endif + + Profiler.EndSample(); + */ + } + + + /// + /// + /// + /// + bool TryAddGlyphsToNewAtlasTexture() + { + // Create and prepare new atlas texture + SetupNewAtlasTexture(); + + Glyph[] glyphs; + + // Try adding remaining glyphs in the newly created atlas texture + bool allGlyphsAddedToTexture = FontEngine.TryAddGlyphsToTexture(m_GlyphsToAdd, m_AtlasPadding, GlyphPackingMode.BestShortSideFit, m_FreeGlyphRects, m_UsedGlyphRects, m_AtlasRenderMode, m_AtlasTextures[m_AtlasTextureIndex], out glyphs); + + // Add new glyphs to relevant data structures. + for (int i = 0; i < glyphs.Length && glyphs[i] != null; i++) + { + Glyph glyph = glyphs[i]; + uint glyphIndex = glyph.index; + + glyph.atlasIndex = m_AtlasTextureIndex; + + // Add new glyph to glyph table. + m_GlyphTable.Add(glyph); + m_GlyphLookupDictionary.Add(glyphIndex, glyph); + + m_GlyphIndexListNewlyAdded.Add(glyphIndex); + m_GlyphIndexList.Add(glyphIndex); + } + + // Clear glyph index list to allow us to track glyphs + m_GlyphsToAdd.Clear(); + + // Add new characters to relevant data structures as well as track glyphs that could not be added to the current atlas texture. + for (int i = 0; i < m_CharactersToAdd.Count; i++) + { + TMP_Character character = m_CharactersToAdd[i]; + Glyph glyph; + + if (m_GlyphLookupDictionary.TryGetValue(character.glyphIndex, out glyph) == false) + { + m_GlyphsToAdd.Add(character.glyphIndex); + continue; + } + + character.glyph = glyph; + m_CharacterTable.Add(character); + m_CharacterLookupDictionary.Add(character.unicode, character); + + // Remove character + m_CharactersToAdd.RemoveAt(i); + i -= 1; + } + + return allGlyphsAddedToTexture; + } + + + /// + /// + /// + void SetupNewAtlasTexture() + { + m_AtlasTextureIndex += 1; + + // Check size of atlas texture array + if (m_AtlasTextures.Length == m_AtlasTextureIndex) + Array.Resize(ref m_AtlasTextures, m_AtlasTextures.Length * 2); + + // Initialize new atlas texture + m_AtlasTextures[m_AtlasTextureIndex] = new Texture2D(m_AtlasWidth, m_AtlasHeight, TextureFormat.Alpha8, false); + FontEngine.ResetAtlasTexture(m_AtlasTextures[m_AtlasTextureIndex]); + + // Clear packing GlyphRects + int packingModifier = ((GlyphRasterModes)m_AtlasRenderMode & GlyphRasterModes.RASTER_MODE_BITMAP) == GlyphRasterModes.RASTER_MODE_BITMAP ? 0 : 1; + m_FreeGlyphRects.Clear(); + m_FreeGlyphRects.Add(new GlyphRect(0, 0, m_AtlasWidth - packingModifier, m_AtlasHeight - packingModifier)); + m_UsedGlyphRects.Clear(); + + #if UNITY_EDITOR + // Add new texture as sub asset to font asset + if (UnityEditor.EditorUtility.IsPersistent(this)) + { + Texture2D tex = m_AtlasTextures[m_AtlasTextureIndex]; + tex.name = m_AtlasTexture.name + " " + m_AtlasTextureIndex; + + UnityEditor.AssetDatabase.AddObjectToAsset(m_AtlasTextures[m_AtlasTextureIndex], this); + TMP_EditorResourceManager.RegisterResourceForReimport(this); + } + #endif + } + + + /// + /// Internal function used to get the glyph index for the given Unicode. /// /// /// @@ -1972,16 +2187,12 @@ internal uint GetGlyphIndex(uint unicode) internal void UpdateAtlasTexture() { // Return if we don't have any glyphs to add to atlas texture. - // This is possible if UpdateAtlasTexture() was called manually. - //if (m_GlyphsToPack.Count == 0) - // return; - if (m_GlyphsToRender.Count == 0) return; //Debug.Log("Updating [" + this.name + "]'s atlas texture."); - // Pack glyphs in the given atlas texture size. + // Pack glyphs in the given atlas texture size. // TODO: Packing and glyph render modes should be defined in the font asset. //FontEngine.PackGlyphsInAtlas(m_GlyphsToPack, m_GlyphsPacked, m_AtlasPadding, GlyphPackingMode.ContactPointRule, GlyphRenderMode.SDFAA, m_AtlasWidth, m_AtlasHeight, m_FreeGlyphRects, m_UsedGlyphRects); //FontEngine.RenderGlyphsToTexture(m_GlyphsPacked, m_AtlasPadding, GlyphRenderMode.SDFAA, m_AtlasTextures[m_AtlasTextureIndex]); @@ -1994,32 +2205,32 @@ internal void UpdateAtlasTexture() FontEngine.ResetAtlasTexture(m_AtlasTextures[m_AtlasTextureIndex]); } - FontEngine.RenderGlyphsToTexture(m_GlyphsToRender, m_AtlasPadding, m_AtlasRenderMode, m_AtlasTextures[m_AtlasTextureIndex]); + //FontEngine.RenderGlyphsToTexture(m_GlyphsToRender, m_AtlasPadding, m_AtlasRenderMode, m_AtlasTextures[m_AtlasTextureIndex]); // Apply changes to atlas texture m_AtlasTextures[m_AtlasTextureIndex].Apply(false, false); // Add glyphs that were successfully packed to the glyph table. - for (int i = 0; i < m_GlyphsToRender.Count /* m_GlyphsPacked.Count */; i++) - { - Glyph glyph = m_GlyphsToRender[i]; // m_GlyphsPacked[i]; + //for (int i = 0; i < m_GlyphsToRender.Count /* m_GlyphsPacked.Count */; i++) + //{ + // Glyph glyph = m_GlyphsToRender[i]; // m_GlyphsPacked[i]; - // Update atlas texture index - glyph.atlasIndex = m_AtlasTextureIndex; + // // Update atlas texture index + // glyph.atlasIndex = m_AtlasTextureIndex; - m_GlyphTable.Add(glyph); - m_GlyphLookupDictionary.Add(glyph.index, glyph); - } + // m_GlyphTable.Add(glyph); + // m_GlyphLookupDictionary.Add(glyph.index, glyph); + //} // Clear list of glyphs - m_GlyphsPacked.Clear(); - m_GlyphsToRender.Clear(); + //m_GlyphsPacked.Clear(); + //m_GlyphsToRender.Clear(); // Add any remaining glyphs into new atlas texture if multi texture support if enabled. - if (m_GlyphsToPack.Count > 0) - { + //if (m_GlyphsToPack.Count > 0) + //{ /* - // Create new atlas texture + // Create new atlas texture Texture2D tex = new Texture2D(m_AtlasWidth, m_AtlasHeight, TextureFormat.Alpha8, false, true); tex.SetPixels32(new Color32[m_AtlasWidth * m_AtlasHeight]); tex.Apply(); @@ -2031,15 +2242,58 @@ internal void UpdateAtlasTexture() m_AtlasTextures[m_AtlasTextureIndex] = tex; */ - } + //} #if UNITY_EDITOR // Makes the changes to the font asset persistent. - SortGlyphAndCharacterTables(); + //SortAllTables(); TMP_EditorResourceManager.RegisterResourceForUpdate(this); #endif } + internal static void RegisterFontAssetForFontFeatureUpdate(TMP_FontAsset fontAsset) + { + int instanceID = fontAsset.instanceID; + + if (k_FontAssets_FontFeaturesUpdateQueueLookup.Add(instanceID)) + k_FontAssets_FontFeaturesUpdateQueue.Add(fontAsset); + } + + internal static void UpdateFontFeaturesForFontAssetsInQueue() + { + int count = k_FontAssets_FontFeaturesUpdateQueue.Count; + + for (int i = 0; i < count; i++) + k_FontAssets_FontFeaturesUpdateQueue[i].UpdateGlyphAdjustmentRecords(); + + if (count > 0) + { + k_FontAssets_FontFeaturesUpdateQueue.Clear(); + k_FontAssets_FontFeaturesUpdateQueueLookup.Clear(); + } + } + + internal static void RegisterFontAssetForAtlasTextureUpdate(TMP_FontAsset fontAsset) + { + int instanceID = fontAsset.instanceID; + + if (k_FontAssets_AtlasTexturesUpdateQueueLookup.Add(instanceID)) + k_FontAssets_AtlasTexturesUpdateQueue.Add(fontAsset); + } + + internal static void UpdateAtlasTexturesForFontAssetsInQueue() + { + int count = k_FontAssets_AtlasTexturesUpdateQueueLookup.Count; + + for (int i = 0; i < count; i++) + k_FontAssets_AtlasTexturesUpdateQueue[i].TryAddGlyphsToAtlasTextures(); + + if (count > 0) + { + k_FontAssets_AtlasTexturesUpdateQueue.Clear(); + k_FontAssets_AtlasTexturesUpdateQueueLookup.Clear(); + } + } /// /// Function called to update the font atlas texture and character data of font assets to which @@ -2047,17 +2301,15 @@ internal void UpdateAtlasTexture() /// public static void UpdateFontAssets() { - int count = k_FontAssetsToUpdate.Count; + int count = k_FontAssets_FontFeaturesUpdateQueue.Count; for (int i = 0; i < count; i++) - { - k_FontAssetsToUpdate[i].UpdateGlyphAdjustmentRecords(); - } + k_FontAssets_FontFeaturesUpdateQueue[i].UpdateGlyphAdjustmentRecords(); if (count > 0) { - k_FontAssetsToUpdate.Clear(); - k_FontAssetsToUpdateLookup.Clear(); + k_FontAssets_FontFeaturesUpdateQueue.Clear(); + k_FontAssets_FontFeaturesUpdateQueueLookup.Clear(); } } @@ -2065,20 +2317,154 @@ internal void UpdateGlyphAdjustmentRecords() { Profiler.BeginSample("TMP.UpdateGlyphAdjustmentRecords"); - int glyphCount = m_GlyphIndexList.Count; + // TODO: This copying of glyph index from list to array is temporary and will be replaced once an updated version of the FontEngine is released. + // Copy glyph index to array + CopyListDataToArray(m_GlyphIndexList, ref k_GlyphIndexArray); - if (s_GlyphIndexArray.Length < glyphCount) - s_GlyphIndexArray = new uint[Mathf.NextPowerOfTwo(glyphCount + 1)]; + // Get glyph pair adjustment records from font file. + GlyphPairAdjustmentRecord[] pairAdjustmentRecords = FontEngine.GetGlyphPairAdjustmentTable(k_GlyphIndexArray); + //GlyphPairAdjustmentRecord[] pairAdjustmentRecords = FontEngine.GetGlyphPairAdjustmentRecords(m_GlyphIndexListNewlyAdded, m_GlyphIndexList); - for (int i = 0; i < glyphCount; i++) - s_GlyphIndexArray[i] = m_GlyphIndexList[i]; + // Clear newly added glyph list + m_GlyphIndexListNewlyAdded.Clear(); + + if (pairAdjustmentRecords == null || pairAdjustmentRecords.Length == 0) + { + Profiler.EndSample(); + return; + } + + if (m_FontFeatureTable == null) + m_FontFeatureTable = new TMP_FontFeatureTable(); + + for (int i = 0; i < pairAdjustmentRecords.Length && pairAdjustmentRecords[i].firstAdjustmentRecord.glyphIndex != 0; i++) + { + uint pairKey = pairAdjustmentRecords[i].secondAdjustmentRecord.glyphIndex << 16 | pairAdjustmentRecords[i].firstAdjustmentRecord.glyphIndex; - // Clear unused array elements - Array.Clear(s_GlyphIndexArray, glyphCount, s_GlyphIndexArray.Length - glyphCount); + // Check if table already contains a pair adjustment record for this key. + if (m_FontFeatureTable.m_GlyphPairAdjustmentRecordLookupDictionary.ContainsKey(pairKey)) + continue; + + TMP_GlyphPairAdjustmentRecord record = new TMP_GlyphPairAdjustmentRecord(pairAdjustmentRecords[i]); + + m_FontFeatureTable.m_GlyphPairAdjustmentRecords.Add(record); + m_FontFeatureTable.m_GlyphPairAdjustmentRecordLookupDictionary.Add(pairKey, record); + } + + #if UNITY_EDITOR + m_FontFeatureTable.SortGlyphPairAdjustmentRecords(); + #endif + + Profiler.EndSample(); + } + + + /// + /// Function used for debugging and performance testing. + /// + /// + internal void UpdateGlyphAdjustmentRecords(uint[] glyphIndexes) + { + Profiler.BeginSample("TMP.UpdateGlyphAdjustmentRecords"); // Get glyph pair adjustment records from font file. - // TODO: Revise FontEngine bindings to use a more efficient function where only the new glyph index is passed. - GlyphPairAdjustmentRecord[] pairAdjustmentRecords = FontEngine.GetGlyphPairAdjustmentTable(s_GlyphIndexArray); + GlyphPairAdjustmentRecord[] pairAdjustmentRecords = FontEngine.GetGlyphPairAdjustmentTable(glyphIndexes); + + // Clear newly added glyph list + //m_GlyphIndexListNewlyAdded.Clear(); + + if (pairAdjustmentRecords == null || pairAdjustmentRecords.Length == 0) + { + Profiler.EndSample(); + return; + } + + if (m_FontFeatureTable == null) + m_FontFeatureTable = new TMP_FontFeatureTable(); + + for (int i = 0; i < pairAdjustmentRecords.Length && pairAdjustmentRecords[i].firstAdjustmentRecord.glyphIndex != 0; i++) + { + uint pairKey = pairAdjustmentRecords[i].secondAdjustmentRecord.glyphIndex << 16 | pairAdjustmentRecords[i].firstAdjustmentRecord.glyphIndex; + + // Check if table already contains a pair adjustment record for this key. + if (m_FontFeatureTable.m_GlyphPairAdjustmentRecordLookupDictionary.ContainsKey(pairKey)) + continue; + + TMP_GlyphPairAdjustmentRecord record = new TMP_GlyphPairAdjustmentRecord(pairAdjustmentRecords[i]); + + m_FontFeatureTable.m_GlyphPairAdjustmentRecords.Add(record); + m_FontFeatureTable.m_GlyphPairAdjustmentRecordLookupDictionary.Add(pairKey, record); + } + + #if UNITY_EDITOR + m_FontFeatureTable.SortGlyphPairAdjustmentRecords(); + #endif + + Profiler.EndSample(); + } + + /// + /// Function requires an update to the TextCore:FontEngine + /// + /// + internal void UpdateGlyphAdjustmentRecords(List glyphIndexes) + { + /* + Profiler.BeginSample("TMP.UpdateGlyphAdjustmentRecords"); + + // Get glyph pair adjustment records from font file. + int recordCount; + GlyphPairAdjustmentRecord[] pairAdjustmentRecords = FontEngine.GetGlyphPairAdjustmentRecords(glyphIndexes, out recordCount); + + // Clear newly added glyph list + //m_GlyphIndexListNewlyAdded.Clear(); + + if (pairAdjustmentRecords == null || pairAdjustmentRecords.Length == 0) + { + Profiler.EndSample(); + return; + } + + if (m_FontFeatureTable == null) + m_FontFeatureTable = new TMP_FontFeatureTable(); + + for (int i = 0; i < pairAdjustmentRecords.Length && pairAdjustmentRecords[i].firstAdjustmentRecord.glyphIndex != 0; i++) + { + uint pairKey = pairAdjustmentRecords[i].secondAdjustmentRecord.glyphIndex << 16 | pairAdjustmentRecords[i].firstAdjustmentRecord.glyphIndex; + + // Check if table already contains a pair adjustment record for this key. + if (m_FontFeatureTable.m_GlyphPairAdjustmentRecordLookupDictionary.ContainsKey(pairKey)) + continue; + + TMP_GlyphPairAdjustmentRecord record = new TMP_GlyphPairAdjustmentRecord(pairAdjustmentRecords[i]); + + m_FontFeatureTable.m_GlyphPairAdjustmentRecords.Add(record); + m_FontFeatureTable.m_GlyphPairAdjustmentRecordLookupDictionary.Add(pairKey, record); + } + + #if UNITY_EDITOR + m_FontFeatureTable.SortGlyphPairAdjustmentRecords(); + #endif + + Profiler.EndSample(); + */ + } + + /// + /// Function requires an update to the TextCore:FontEngine + /// + /// + /// + internal void UpdateGlyphAdjustmentRecords(List newGlyphIndexes, List allGlyphIndexes) + { + /* + Profiler.BeginSample("TMP.UpdateGlyphAdjustmentRecords"); + + // Get glyph pair adjustment records from font file. + GlyphPairAdjustmentRecord[] pairAdjustmentRecords = FontEngine.GetGlyphPairAdjustmentRecords(newGlyphIndexes, allGlyphIndexes); + + // Clear newly added glyph list + m_GlyphIndexListNewlyAdded.Clear(); if (pairAdjustmentRecords == null || pairAdjustmentRecords.Length == 0) { @@ -2108,8 +2494,35 @@ internal void UpdateGlyphAdjustmentRecords() #endif Profiler.EndSample(); + */ } + /// + /// Internal method to copy generic list data to generic array of the same type. + /// + /// Source + /// Destination + /// Element type + void CopyListDataToArray(List srcList, ref T[] dstArray) + { + // Make sure destination array is appropriately sized. + if (dstArray == null || dstArray.Length < srcList.Count + 1) + { + int newSize = Mathf.NextPowerOfTwo(srcList.Count + 1); + + if (dstArray == null) + dstArray = new T[newSize]; + else + Array.Resize(ref dstArray, newSize); + } + + int count = srcList.Count; + for (int i = 0; i < count; i++) + dstArray[i] = srcList[i]; + + // Terminate last array element with default value + dstArray[count] = default(T); + } /// /// Clears font asset data including the glyph and character tables and textures. @@ -2118,11 +2531,68 @@ internal void UpdateGlyphAdjustmentRecords() /// Will set the atlas texture size to zero width and height if true. public void ClearFontAssetData(bool setAtlasSizeToZero = false) { + Profiler.BeginSample("TMP.ClearFontAssetData"); + #if UNITY_EDITOR // Record full object undo in the Editor. //UnityEditor.Undo.RecordObjects(new UnityEngine.Object[] { this, this.atlasTexture }, "Resetting Font Asset"); #endif + // Clear glyph, character and font feature tables + ClearFontAssetTables(); + + // Clear atlas textures + ClearAtlasTextures(setAtlasSizeToZero); + + #if UNITY_EDITOR + if (UnityEditor.EditorUtility.IsPersistent(this)) + { + TMP_EditorResourceManager.RegisterResourceForReimport(this); + } + #endif + + ReadFontAssetDefinition(); + + Profiler.EndSample(); + } + + /// + /// + /// + internal void UpdateFontAssetData() + { + Profiler.BeginSample("TMP.UpdateFontAssetData"); + + // Get list of all characters currently contained in the font asset. + uint[] unicodeCharacters = new uint[m_CharacterTable.Count]; + + for (int i = 0; i < m_CharacterTable.Count; i++) + unicodeCharacters[i] = m_CharacterTable[i].unicode; + + // Clear glyph, character and font feature tables + ClearFontAssetTables(); + + // Clear atlas textures + ClearAtlasTextures(true); + + ReadFontAssetDefinition(); + + // Add glyphs + TryAddCharacters(unicodeCharacters, true); + + #if UNITY_EDITOR + if (UnityEditor.EditorUtility.IsPersistent(this)) + { + TMP_EditorResourceManager.RegisterResourceForReimport(this); + } + #endif + + Profiler.EndSample(); + } + + + internal void ClearFontAssetTables() + { // Clear glyph and character tables if (m_GlyphTable != null) m_GlyphTable.Clear(); @@ -2141,74 +2611,73 @@ public void ClearFontAssetData(bool setAtlasSizeToZero = false) m_FreeGlyphRects.Add(new GlyphRect(0, 0, m_AtlasWidth - packingModifier, m_AtlasHeight - packingModifier)); } - if (m_GlyphsToPack != null) - m_GlyphsToPack.Clear(); + if (m_GlyphsToRender != null) + m_GlyphsToRender.Clear(); - if (m_GlyphsPacked != null) - m_GlyphsPacked.Clear(); + if (m_GlyphsRendered != null) + m_GlyphsRendered.Clear(); // Clear Glyph Adjustment Table if (m_FontFeatureTable != null && m_FontFeatureTable.m_GlyphPairAdjustmentRecords != null) m_FontFeatureTable.glyphPairAdjustmentRecords.Clear(); + } + /// + /// Internal function to clear all atlas textures. + /// + /// Set main atlas texture size to zero if true. + internal void ClearAtlasTextures(bool setAtlasSizeToZero = false) + { m_AtlasTextureIndex = 0; - // Clear atlas textures - if (m_AtlasTextures != null) + // Return if we don't have any atlas textures + if (m_AtlasTextures == null) + return; + + // Clear all atlas textures + for (int i = 0; i < m_AtlasTextures.Length; i++) { - for (int i = 0; i < m_AtlasTextures.Length; i++) - { - Texture2D texture = m_AtlasTextures[i]; + Texture2D texture = m_AtlasTextures[i]; - if (i > 0) - DestroyImmediate(texture, true); + if (i > 0) + DestroyImmediate(texture, true); - if (texture == null) - continue; + if (texture == null) + continue; - // TODO: Need texture to be readable. - if (m_AtlasTextures[i].isReadable == false) - { - #if UNITY_EDITOR - FontEngineEditorUtilities.SetAtlasTextureIsReadable(m_AtlasTextures[i], true); - #else + // TODO: Need texture to be readable. + if (m_AtlasTextures[i].isReadable == false) + { + #if UNITY_EDITOR + #if !(UNITY_2018_4_0 || UNITY_2018_4_1 || UNITY_2018_4_2 || UNITY_2018_4_3 || UNITY_2018_4_4) + FontEngineEditorUtilities.SetAtlasTextureIsReadable(m_AtlasTextures[i], true); + #endif + #else Debug.LogWarning("Unable to reset font asset [" + this.name + "]'s atlas texture. Please make the texture [" + m_AtlasTextures[i].name + "] readable.", m_AtlasTextures[i]); continue; - #endif - } - - if (setAtlasSizeToZero) - { - texture.Resize(0, 0, TextureFormat.Alpha8, false); - } - else if (texture.width != m_AtlasWidth || texture.height != m_AtlasHeight) - { - texture.Resize(m_AtlasWidth, m_AtlasHeight, TextureFormat.Alpha8, false); - } + #endif + } - // Clear texture atlas - FontEngine.ResetAtlasTexture(texture); - texture.Apply(); + if (setAtlasSizeToZero) + { + texture.Resize(0, 0, TextureFormat.Alpha8, false); + } + else if (texture.width != m_AtlasWidth || texture.height != m_AtlasHeight) + { + texture.Resize(m_AtlasWidth, m_AtlasHeight, TextureFormat.Alpha8, false); + } - if (i == 0) - m_AtlasTexture = texture; + // Clear texture atlas + FontEngine.ResetAtlasTexture(texture); + texture.Apply(); - m_AtlasTextures[i] = texture; - } - } + if (i == 0) + m_AtlasTexture = texture; - #if UNITY_EDITOR - if (UnityEditor.EditorUtility.IsPersistent(this)) - { - TMP_EditorResourceManager.RegisterResourceForReimport(this); + m_AtlasTextures[i] = texture; } - #endif - - ReadFontAssetDefinition(); } - - /// /// Internal method used to upgrade font asset to support Dynamic SDF. /// @@ -2335,7 +2804,7 @@ internal void UpgradeFontAsset() Glyph glyph = new Glyph(); uint glyphIndex = (uint)i + 1; - + //#if UNITY_EDITOR //if (m_SourceFontFile_EditorRef != null) // glyphIndex = FontEngine.GetGlyphIndex((uint)oldGlyph.id); @@ -2382,7 +2851,7 @@ internal void UpgradeFontAsset() } /// - /// + /// /// void UpgradeGlyphAdjustmentTableToFontFeatureTable() { @@ -2431,4 +2900,4 @@ void UpgradeGlyphAdjustmentTableToFontFeatureTable() } } -} \ No newline at end of file +} diff --git a/Scripts/Runtime/TMP_FontAssetUtilities.cs b/Scripts/Runtime/TMP_FontAssetUtilities.cs index a2d7f19..5e77c9d 100644 --- a/Scripts/Runtime/TMP_FontAssetUtilities.cs +++ b/Scripts/Runtime/TMP_FontAssetUtilities.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using UnityEngine; +using System.Collections.Generic; using UnityEngine.TextCore; using UnityEngine.TextCore.LowLevel; @@ -270,7 +267,7 @@ public static TMP_Character GetCharacterFromFontAssets(uint unicode, List { private float m_ScrollPosition; /// - /// + /// /// [SerializeField] protected float m_ScrollSensitivity = 1.0f; @@ -446,7 +446,7 @@ public bool shouldHideSoftKeyboard m_HideSoftKeyboard = true; break; } - + if (m_HideSoftKeyboard == true && m_SoftKeyboard != null && TouchScreenKeyboard.isSupported && m_SoftKeyboard.active) { m_SoftKeyboard.active = false; @@ -628,7 +628,7 @@ public Scrollbar verticalScrollbar if (m_VerticalScrollbar) { m_VerticalScrollbar.onValueChanged.AddListener(OnScrollbarValueChange); - + } } } @@ -681,13 +681,14 @@ public int characterLimit public float pointSize { get { return m_GlobalPointSize; } - set { - if (SetPropertyUtility.SetStruct(ref m_GlobalPointSize, Math.Max(0, value))) - { - SetGlobalPointSize(m_GlobalPointSize); - UpdateLabel(); - } + set + { + if (SetPropertyUtility.SetStruct(ref m_GlobalPointSize, Math.Max(0, value))) + { + SetGlobalPointSize(m_GlobalPointSize); + UpdateLabel(); } + } } /// @@ -815,7 +816,7 @@ public TMP_InputValidator inputValidator set { if (SetPropertyUtility.SetClass(ref m_InputValidator, value)) SetToCustom(CharacterValidation.CustomValidator); } } [SerializeField] - protected TMP_InputValidator m_InputValidator = null; + protected TMP_InputValidator m_InputValidator = null; public bool readOnly { get { return m_ReadOnly; } set { m_ReadOnly = value; } } @@ -914,7 +915,7 @@ public int selectionFocusPosition /// - /// + /// /// public int stringPosition { @@ -1426,34 +1427,59 @@ protected virtual void LateUpdate() if (selectedObject != null && selectedObject != this.gameObject) { - if (selectedObject != m_PreviouslySelectedObject) + if (selectedObject == m_PreviouslySelectedObject) + return; + + m_PreviouslySelectedObject = selectedObject; + + // Special handling for Vertical Scrollbar + if (m_VerticalScrollbar && selectedObject == m_VerticalScrollbar.gameObject) { - m_PreviouslySelectedObject = selectedObject; + // Do not release selection + return; + } - // Special handling for Vertical Scrollbar - if (m_VerticalScrollbar && selectedObject == m_VerticalScrollbar.gameObject) - { - // Do not release selection - return; - } - else - { - // Release selection for all objects when ResetOnDeActivation is true - if (m_ResetOnDeActivation) - { - ReleaseSelection(); - return; - } - - // Release current selection of selected object is another Input Field - if (selectedObject.GetComponent() != null) - ReleaseSelection(); - } + // Release selection for all objects when ResetOnDeActivation is true + if (m_ResetOnDeActivation) + { + ReleaseSelection(); + return; } + // Release current selection of selected object is another Input Field + if (selectedObject.GetComponent() != null) + ReleaseSelection(); + return; } + #if ENABLE_INPUT_SYSTEM + if (m_ProcessingEvent != null && m_ProcessingEvent.rawType == EventType.MouseDown && m_ProcessingEvent.button == 0) + { + // Check for Double Click + bool isDoubleClick = false; + float timeStamp = Time.unscaledTime; + + if (m_KeyDownStartTime + m_DoubleClickDelay > timeStamp) + isDoubleClick = true; + + m_KeyDownStartTime = timeStamp; + + if (isDoubleClick) + { + //m_StringPosition = m_StringSelectPosition = 0; + //m_CaretPosition = m_CaretSelectPosition = 0; + //m_TextComponent.rectTransform.localPosition = m_DefaultTransformPosition; + + //if (caretRectTrans != null) + // caretRectTrans.localPosition = Vector3.zero; + + ReleaseSelection(); + + return; + } + } + #else if (Input.GetKeyDown(KeyCode.Mouse0)) { // Check for Double Click @@ -1479,6 +1505,7 @@ protected virtual void LateUpdate() return; } } + #endif } UpdateMaskRegions(); @@ -1726,7 +1753,12 @@ public override void OnPointerDown(PointerEventData eventData) } } + #if ENABLE_INPUT_SYSTEM + Event.PopEvent(m_ProcessingEvent); + bool shift = m_ProcessingEvent != null && (m_ProcessingEvent.modifiers & EventModifiers.Shift) != 0; + #else bool shift = Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift); + #endif // Check for Double Click bool isDoubleClick = false; @@ -1819,7 +1851,7 @@ public override void OnPointerDown(PointerEventData eventData) else { // Select current character - caretPositionInternal = insertionIndex; + caretPositionInternal = insertionIndex; caretSelectPositionInternal = caretPositionInternal + 1; stringPositionInternal = m_TextComponent.textInfo.characterInfo[insertionIndex].index; @@ -2025,7 +2057,7 @@ protected virtual bool IsValidChar(char c) // Null character if (c == 0) return false; - + // Delete key on mac if (c == 127) return false; @@ -2052,7 +2084,7 @@ public void ProcessEvent(Event e) /// - /// + /// /// /// public virtual void OnUpdateSelected(BaseEventData eventData) @@ -2131,7 +2163,7 @@ public virtual void OnUpdateSelected(BaseEventData eventData) /// - /// + /// /// /// public virtual void OnScroll(PointerEventData eventData) @@ -2316,7 +2348,7 @@ private void MoveLeft(bool shift, bool ctrl) { stringSelectPositionInternal = stringPositionInternal = position; - // Only decrease caret position as we cross character boundary. + // Only decrease caret position as we cross character boundary. if (caretPositionInternal > 0 && stringPositionInternal <= m_TextComponent.textInfo.characterInfo[caretPositionInternal - 1].index) caretSelectPositionInternal = caretPositionInternal = GetCaretPositionFromStringIndex(stringSelectPositionInternal); } @@ -3205,7 +3237,7 @@ void OnScrollbarValueChange(float value) void UpdateMaskRegions() { // TODO: Figure out a better way to handle adding an offset to the masking region - // This region is defined by the RectTransform of the GameObject that contains the RectMask2D component. + // This region is defined by the RectTransform of the GameObject that contains the RectMask2D component. /* // Update Masking Region if (m_TextViewportRectMask != null) @@ -3458,14 +3490,23 @@ private void GenerateCaret(VertexHelper vbo, Vector2 roundingOffset) if (caretPositionInternal == m_TextComponent.textInfo.lineInfo[currentLine].firstCharacterIndex) { currentCharacter = m_TextComponent.textInfo.characterInfo[caretPositionInternal]; - startPosition = new Vector2(currentCharacter.origin, currentCharacter.descender); height = currentCharacter.ascender - currentCharacter.descender; + + if (m_TextComponent.verticalAlignment == VerticalAlignmentOptions.Geometry) + startPosition = new Vector2(currentCharacter.origin, 0 - height / 2); + else + startPosition = new Vector2(currentCharacter.origin, currentCharacter.descender); } else { currentCharacter = m_TextComponent.textInfo.characterInfo[caretPositionInternal - 1]; - startPosition = new Vector2(currentCharacter.xAdvance, currentCharacter.descender); height = currentCharacter.ascender - currentCharacter.descender; + + if (m_TextComponent.verticalAlignment == VerticalAlignmentOptions.Geometry) + startPosition = new Vector2(currentCharacter.xAdvance, 0 - height / 2); + else + startPosition = new Vector2(currentCharacter.xAdvance, currentCharacter.descender); + } if (m_SoftKeyboard != null) @@ -3525,7 +3566,8 @@ private void GenerateCaret(VertexHelper vbo, Vector2 roundingOffset) Vector2 screenPosition = RectTransformUtility.WorldToScreenPoint(cameraRef, cursorPosition); screenPosition.y = Screen.height - screenPosition.y; - inputSystem.compositionCursorPos = screenPosition; + if (inputSystem != null) + inputSystem.compositionCursorPos = screenPosition; //Debug.Log("[" + Time.frameCount + "] Updating IME Window position(" + screenPosition + ") with Composition Length: " + compositionLength); } @@ -3660,7 +3702,7 @@ private void GenerateHightlight(VertexHelper vbo, Vector2 roundingOffset) /// - /// + /// /// /// /// @@ -3907,7 +3949,7 @@ private void ActivateInputFieldInternal() OnFocus(); - // Opening the soft keyboard sets its selection to the end of the text. + // Opening the soft keyboard sets its selection to the end of the text. // As such, we set the selection to match the Input Field's internal selection. if (m_SoftKeyboard != null) { @@ -4007,12 +4049,13 @@ public void DeactivateInputField(bool clearSelection = false) //m_StringPosition = m_StringSelectPosition = 0; //m_CaretPosition = m_CaretSelectPosition = 0; //m_TextComponent.rectTransform.localPosition = m_DefaultTransformPosition; - + if (m_VerticalScrollbar == null) ReleaseSelection(); } - inputSystem.imeCompositionMode = IMECompositionMode.Auto; + if (inputSystem != null) + inputSystem.imeCompositionMode = IMECompositionMode.Auto; } MarkGeometryAsDirty(); diff --git a/Scripts/Runtime/TMP_Text.cs b/Scripts/Runtime/TMP_Text.cs index 00e6154..041ddea 100644 --- a/Scripts/Runtime/TMP_Text.cs +++ b/Scripts/Runtime/TMP_Text.cs @@ -133,7 +133,7 @@ public virtual string text protected string m_text; /// - /// The ITextPreprocessor component referenced by the text object (if any) + /// The ITextPreprocessor component referenced by the text object (if any) /// public ITextPreprocessor textPreprocessor { @@ -144,7 +144,7 @@ public ITextPreprocessor textPreprocessor protected ITextPreprocessor m_TextPreprocessor; /// - /// + /// /// public bool isRightToLeftText { @@ -282,7 +282,7 @@ public bool enableVertexGradient [SerializeField] protected ColorMode m_colorMode = ColorMode.FourCornersGradient; - + /// /// Sets the vertex colors for each of the 4 vertices of the character quads. /// @@ -345,7 +345,7 @@ public TMP_StyleSheet styleSheet protected TMP_StyleSheet m_StyleSheet; /// - /// + /// /// public TMP_Style textStyle { @@ -474,7 +474,7 @@ public FontWeight fontWeight protected TMP_RichTextTagStack m_FontWeightStack = new TMP_RichTextTagStack(8); /// - /// + /// /// public float pixelsPerUnit { @@ -576,7 +576,7 @@ public HorizontalAlignmentOptions horizontalAlignment protected HorizontalAlignmentOptions m_HorizontalAlignment = HorizontalAlignmentOptions.Left; /// - /// Vertical alignment options + /// Vertical alignment options /// public VerticalAlignmentOptions verticalAlignment { @@ -617,16 +617,12 @@ public TextAlignmentOptions alignment } [SerializeField] [UnityEngine.Serialization.FormerlySerializedAs("m_lineJustification")] - protected TextAlignmentOptions m_textAlignment = TextAlignmentOptions.TopLeft; - //protected TextAlignmentOptions m_lineJustification; + protected TextAlignmentOptions m_textAlignment = TextAlignmentOptions.Converted; protected HorizontalAlignmentOptions m_lineJustification; protected TMP_RichTextTagStack m_lineJustificationStack = new TMP_RichTextTagStack(new HorizontalAlignmentOptions[16]); protected Vector3[] m_textContainerLocalCorners = new Vector3[4]; - [SerializeField] - protected bool m_isAlignmentEnumConverted; - /// /// Use the extents of the text geometry for alignment instead of font metrics. /// @@ -675,6 +671,7 @@ public float lineSpacing protected float m_lineSpacing = 0; protected float m_lineSpacingDelta = 0; // Used with Text Auto Sizing feature protected float m_lineHeight = TMP_Math.FLOAT_UNSET; // Used with the tag. + protected bool m_IsDrivenLineSpacing; /// @@ -741,7 +738,7 @@ public float wordWrappingRatios /// - /// + /// /// //public bool enableAdaptiveJustification //{ @@ -919,7 +916,7 @@ public bool enableCulling [SerializeField] protected bool m_isCullingEnabled = false; - // + // protected bool m_isMaskingEnabled; protected bool isMaskUpdateRequired; @@ -1029,7 +1026,7 @@ public bool isTextObjectScaleStatic /// /// Determines if the data structures allocated to contain the geometry of the text object will be reduced in size if the number of characters required to display the text is reduced by more than 256 characters. - /// This reduction has the benefit of reducing the amount of vertex data being submitted to the graphic device but results in GC when it occurs. + /// This reduction has the benefit of reducing the amount of vertex data being submitted to the graphic device but results in GC when it occurs. /// public bool vertexBufferAutoSizeReduction { @@ -1132,7 +1129,7 @@ public TMP_TextInfo textInfo get { return m_textInfo; } } [SerializeField] - protected TMP_TextInfo m_textInfo; // Class which holds information about the Text object such as characters, lines, mesh data as well as metrics. + protected TMP_TextInfo m_textInfo; // Class which holds information about the Text object such as characters, lines, mesh data as well as metrics. /// /// Property tracking if any of the text properties have changed. Flag is set before the text is regenerated. @@ -1319,7 +1316,7 @@ protected TMP_SpriteAnimator spriteAnimator /// - /// + /// /// //public TMP_TextShaper textShaper //{ @@ -1336,43 +1333,43 @@ protected TMP_SpriteAnimator spriteAnimator // *** PROPERTIES RELATED TO UNITY LAYOUT SYSTEM *** /// - /// + /// /// public float flexibleHeight { get { return m_flexibleHeight; } } protected float m_flexibleHeight = -1f; /// - /// + /// /// public float flexibleWidth { get { return m_flexibleWidth; } } protected float m_flexibleWidth = -1f; /// - /// + /// /// public float minWidth { get { return m_minWidth; } } protected float m_minWidth; /// - /// + /// /// public float minHeight { get { return m_minHeight; } } protected float m_minHeight; /// - /// + /// /// public float maxWidth { get { return m_maxWidth; } } protected float m_maxWidth; /// - /// + /// /// public float maxHeight { get { return m_maxHeight; } } protected float m_maxHeight; /// - /// + /// /// protected LayoutElement layoutElement { @@ -1420,7 +1417,7 @@ protected LayoutElement layoutElement /// - /// + /// /// public int layoutPriority { get { return m_layoutPriority; } } protected int m_layoutPriority = 0; @@ -1457,7 +1454,7 @@ internal enum TextInputSources { Text = 0, SetText = 1, SetCharArray = 2, String //[SerializeField] internal TextInputSources m_inputSource; - protected float m_fontScale; // Scaling of the font based on Atlas true Font Size and Rendered Font Size. + protected float m_fontScale; // Scaling of the font based on Atlas true Font Size and Rendered Font Size. protected float m_fontScaleMultiplier; // Used for handling of superscript and subscript. protected char[] m_htmlTag = new char[128]; // Maximum length of rich text tag. This is preallocated to avoid GC. @@ -1492,6 +1489,14 @@ protected struct UnicodeChar public int length; } + protected struct SpecialCharacter + { + public TMP_Character character; + public TMP_FontAsset fontAsset; + public Material material; + public int materialIndex; + } + private TMP_CharacterInfo[] m_internalCharacterInfo; // Used by functions to calculate preferred values. protected char[] m_input_CharArray = new char[256]; // This array hold the characters from the SetText(); private int m_charArray_Length = 0; @@ -1515,6 +1520,7 @@ protected struct UnicodeChar protected int m_lineNumber; protected int m_lineVisibleCharacterCount; protected int m_pageNumber; + protected float m_PageAscender; protected float m_maxAscender; protected float m_maxCapHeight; protected float m_maxDescender; @@ -1556,8 +1562,9 @@ protected struct UnicodeChar protected TMP_TextElementType m_textElementType; protected TMP_TextElement m_cached_TextElement; // Glyph / Character information is cached into this variable which is faster than having to fetch from the Dictionary multiple times. - protected TMP_Character m_cached_Underline_Character; // Same as above but for the underline character which is used for Underline. - protected TMP_Character m_cached_Ellipsis_Character; + + protected SpecialCharacter m_Ellipsis; + protected SpecialCharacter m_Underline; protected TMP_SpriteAsset m_defaultSpriteAsset; protected TMP_SpriteAsset m_currentSpriteAsset; @@ -1595,7 +1602,7 @@ protected virtual void SetFontBaseMaterial(Material mat) { } protected virtual Material[] GetSharedMaterials() { return null; } /// - /// + /// /// protected virtual void SetSharedMaterials(Material[] materials) { } @@ -1642,7 +1649,7 @@ protected void SetVertexColorGradient(TMP_ColorGradient gradient) /// protected void SetTextSortingOrder(VertexSortingOrder order) { - + } /// @@ -1742,7 +1749,7 @@ public virtual void ForceMeshUpdate(bool ignoreActiveState = false, bool forceTe /// - /// Internal function used by the Text Input Field to populate TMP_TextInfo data. + /// Internal function used by the Text Input Field to populate TMP_TextInfo data. /// internal void SetTextInternal(string text) { @@ -1794,7 +1801,7 @@ public virtual void UpdateMeshPadding() { } /// - /// + /// /// //public virtual new void UpdateGeometry() { } @@ -1827,7 +1834,7 @@ public override void CrossFadeAlpha(float alpha, float duration, bool ignoreTime /// - /// + /// /// /// /// @@ -1838,7 +1845,7 @@ protected virtual void InternalCrossFadeColor(Color targetColor, float duration, /// - /// + /// /// /// /// @@ -1878,7 +1885,7 @@ protected void ParseInputText() /// - /// + /// /// /// public void SetText(string text, bool syncTextInputBox = true) @@ -1889,85 +1896,242 @@ public void SetText(string text, bool syncTextInputBox = true) /// /// Formatted string containing a pattern and a value representing the text to be rendered. - /// ex. TextMeshPro.SetText ("Number is {0:1}.", 5.56f); + /// Ex. TMP_Text.SetText("A = {0}, B = {1:00}, C = {2:000.0}", 10.75f, 10.75f, 10.75f); + /// Results "A = 10.75, B = 11, C = 010.8." /// - /// - /// String containing the pattern." - /// Value is a float. + /// String containing the pattern. + /// First float value. public void SetText(string text, float arg0) { - SetText(text, arg0, 255, 255); + SetText(text, arg0, 0, 0, 0, 0, 0, 0, 0); } /// /// Formatted string containing a pattern and a value representing the text to be rendered. - /// ex. TextMeshPro.SetText ("First number is {0} and second is {1:2}.", 10, 5.756f); + /// Ex. TMP_Text.SetText("A = {0}, B = {1:00}, C = {2:000.0}", 10.75f, 10.75f, 10.75f); + /// Results "A = 10.75, B = 11, C = 010.8." /// - /// - /// String containing the pattern." - /// Value is a float. - /// Value is a float. + /// String containing the pattern. + /// First float value. + /// Second float value. public void SetText(string text, float arg0, float arg1) { - SetText(text, arg0, arg1, 255); + SetText(text, arg0, arg1, 0, 0, 0, 0, 0, 0); } /// /// Formatted string containing a pattern and a value representing the text to be rendered. - /// ex. TextMeshPro.SetText ("A = {0}, B = {1} and C = {2}.", 2, 5, 7); + /// Ex. TMP_Text.SetText("A = {0}, B = {1:00}, C = {2:000.0}", 10.75f, 10.75f, 10.75f); + /// Results "A = 10.75, B = 11, C = 010.8." /// - /// - /// String containing the pattern." - /// Value is a float. - /// Value is a float. - /// Value is a float. + /// String containing the pattern. + /// First float value. + /// Second float value. + /// Third float value. public void SetText(string text, float arg0, float arg1, float arg2) { + SetText(text, arg0, arg1, arg2, 0, 0, 0, 0, 0); + } + + /// + /// Formatted string containing a pattern and a value representing the text to be rendered. + /// Ex. TMP_Text.SetText("A = {0}, B = {1:00}, C = {2:000.0}", 10.75f, 10.75f, 10.75f); + /// Results "A = 10.75, B = 11, C = 010.8." + /// + /// String containing the pattern. + /// First float value. + /// Second float value. + /// Third float value. + /// Forth float value. + public void SetText(string text, float arg0, float arg1, float arg2, float arg3) + { + SetText(text, arg0, arg1, arg2, arg3, 0, 0, 0, 0); + } + + /// + /// Formatted string containing a pattern and a value representing the text to be rendered. + /// Ex. TMP_Text.SetText("A = {0}, B = {1:00}, C = {2:000.0}", 10.75f, 10.75f, 10.75f); + /// Results "A = 10.75, B = 11, C = 010.8." + /// + /// String containing the pattern. + /// First float value. + /// Second float value. + /// Third float value. + /// Forth float value. + /// Fifth float value. + public void SetText(string text, float arg0, float arg1, float arg2, float arg3, float arg4) + { + SetText(text, arg0, arg1, arg2, arg3, arg4, 0, 0, 0); + } + + /// + /// Formatted string containing a pattern and a value representing the text to be rendered. + /// Ex. TMP_Text.SetText("A = {0}, B = {1:00}, C = {2:000.0}", 10.75f, 10.75f, 10.75f); + /// Results "A = 10.75, B = 11, C = 010.8." + /// + /// String containing the pattern. + /// First float value. + /// Second float value. + /// Third float value. + /// Forth float value. + /// Fifth float value. + /// Sixth float value. + public void SetText(string text, float arg0, float arg1, float arg2, float arg3, float arg4, float arg5) + { + SetText(text, arg0, arg1, arg2, arg3, arg4, arg5, 0, 0); + } + + /// + /// Formatted string containing a pattern and a value representing the text to be rendered. + /// Ex. TMP_Text.SetText("A = {0}, B = {1:00}, C = {2:000.0}", 10.75f, 10.75f, 10.75f); + /// Results "A = 10.75, B = 11, C = 010.8." + /// + /// String containing the pattern. + /// First float value. + /// Second float value. + /// Third float value. + /// Forth float value. + /// Fifth float value. + /// Sixth float value. + /// Seventh float value. + public void SetText(string text, float arg0, float arg1, float arg2, float arg3, float arg4, float arg5, float arg6) + { + SetText(text, arg0, arg1, arg2, arg3, arg4, arg5, arg6, 0); + } + + /// + /// Formatted string containing a pattern and a value representing the text to be rendered. + /// Ex. TMP_Text.SetText("A = {0}, B = {1:00}, C = {2:000.0}", 10.75f, 10.75f, 10.75f); + /// Results "A = 10.75, B = 11, C = 010.8." + /// + /// String containing the pattern. + /// First float value. + /// Second float value. + /// Third float value. + /// Forth float value. + /// Fifth float value. + /// Sixth float value. + /// Seventh float value. + /// Eighth float value. + public void SetText(string text, float arg0, float arg1, float arg2, float arg3, float arg4, float arg5, float arg6, float arg7) + { + int argIndex = 0; + int padding = 0; int decimalPrecision = 0; - int index = 0; - for (int i = 0; i < text.Length; i++) + int readFlag = 0; + + int readIndex = 0; + int writeIndex = 0; + + for (; readIndex < text.Length; readIndex++) { - char c = text[i]; + char c = text[readIndex]; - if (c == 123) // '{' + if (c == '{') { - // Check if user is requesting some decimal precision. Format is {0:2} - if (text[i + 2] == 58) // ':' - { - decimalPrecision = text[i + 3] - 48; - } + readFlag = 1; + continue; + } - switch (text[i + 1] - 48) + if (c == '}') + { + // Add arg(index) to array + switch (argIndex) { - case 0: // 1st Arg - AddFloatToCharArray(arg0, ref index, decimalPrecision); + case 0: + AddFloatToCharArray(arg0, padding, decimalPrecision, ref writeIndex); break; - case 1: // 2nd Arg - AddFloatToCharArray(arg1, ref index, decimalPrecision); + case 1: + AddFloatToCharArray(arg1, padding, decimalPrecision, ref writeIndex); break; - case 2: // 3rd Arg - AddFloatToCharArray(arg2, ref index, decimalPrecision); + case 2: + AddFloatToCharArray(arg2, padding, decimalPrecision, ref writeIndex); + break; + case 3: + AddFloatToCharArray(arg3, padding, decimalPrecision, ref writeIndex); + break; + case 4: + AddFloatToCharArray(arg4, padding, decimalPrecision, ref writeIndex); + break; + case 5: + AddFloatToCharArray(arg5, padding, decimalPrecision, ref writeIndex); + break; + case 6: + AddFloatToCharArray(arg6, padding, decimalPrecision, ref writeIndex); + break; + case 7: + AddFloatToCharArray(arg7, padding, decimalPrecision, ref writeIndex); break; } - if (text[i + 2] == 58) - i += 4; - else - i += 2; - + argIndex = 0; + readFlag = 0; + padding = 0; + decimalPrecision = 0; continue; } - m_input_CharArray[index] = c; - index += 1; + + // Read Argument index + if (readFlag == 1) + { + if (c >= '0' && c <= '8') + { + argIndex = c - 48; + readFlag = 2; + continue; + } + } + + // Read Padding value + if (readFlag == 2) + { + // Skip ':' separator + if (c == ':') + continue; + + // Done reading padding value + if (c == '.') + { + readFlag = 3; + continue; + } + + if (c == '0') + { + padding += 1; + continue; + } + + // Legacy mode + if (c >= '1' && c <= '9') + { + decimalPrecision = c - 48; + continue; + } + } + + // Read Decimal Precision value + if (readFlag == 3) + { + if (c == '0') + { + decimalPrecision += 1; + continue; + } + } + + // Write value + m_input_CharArray[writeIndex] = c; + writeIndex += 1; } - m_input_CharArray[index] = (char)0; - m_charArray_Length = index; // Set the length to where this '0' termination is. + m_input_CharArray[writeIndex] = (char)0; + m_charArray_Length = writeIndex; // Set the length to where this '0' termination is. #if UNITY_EDITOR // Create new string to be displayed in the Input Text Box of the Editor Panel. - m_text = new string(m_input_CharArray, 0, index); + m_text = new string(m_input_CharArray, 0, writeIndex - 1); #endif m_inputSource = TextInputSources.SetText; @@ -3117,7 +3281,7 @@ bool ReplaceOpeningStyleTag(ref string sourceText, int srcIndex, out int srcOffs m_TextStyleStackDepth += 1; ReplaceClosingStyleTag(ref tagDefinition, i, ref charBuffer, ref writeIndex); - + // Strip even if style is invalid. i += 7; continue; @@ -3240,7 +3404,7 @@ bool ReplaceOpeningStyleTag(ref int[] sourceText, int srcIndex, out int srcOffse m_TextStyleStackDepth += 1; ReplaceClosingStyleTag(ref tagDefinition, i, ref charBuffer, ref writeIndex); - + // Strip even if style is invalid. i += 7; continue; @@ -3977,7 +4141,7 @@ bool ReplaceClosingStyleTag(ref StringBuilder sourceText, int srcIndex, ref Unic } /// - /// + /// /// /// /// @@ -4225,7 +4389,7 @@ bool InsertClosingStyleTag(ref UnicodeChar[] charBuffer, ref int writeIndex) bool IsTagName (ref string text, string tag, int index) { if (text.Length < index + tag.Length) return false; - + for (int i = 0; i < tag.Length; i++) { if (TMP_TextUtilities.ToUpperFast(text[index + i]) != tag[i]) return false; @@ -4396,7 +4560,7 @@ int GetTagHashCode(ref StringBuilder text, int index, out int closeIndex) } /// - /// + /// /// void ResizeInternalArray (ref T[] array) { @@ -4412,79 +4576,88 @@ void ResizeInternalArray(ref T[] array, int size) System.Array.Resize(ref array, size); } - private readonly float[] k_Power = { 5e-1f, 5e-2f, 5e-3f, 5e-4f, 5e-5f, 5e-6f, 5e-7f, 5e-8f, 5e-9f, 5e-10f }; // Used by FormatText to enable rounding and avoid using Mathf.Pow. - /// - /// Function used in conjunction with SetText() - /// - /// - /// - /// - protected void AddFloatToCharArray(double number, ref int index, int precision) + private readonly decimal[] k_Power = { 5e-1m, 5e-2m, 5e-3m, 5e-4m, 5e-5m, 5e-6m, 5e-7m, 5e-8m, 5e-9m, 5e-10m }; // Used by FormatText to enable rounding and avoid using Mathf.Pow. + + + protected void AddFloatToCharArray(float value, int padding, int precision, ref int writeIndex) { - if (number < 0) + if (value < 0) { - m_input_CharArray[index++] = '-'; - number = -number; + m_input_CharArray[writeIndex] = '-'; + writeIndex += 1; + value = -value; } - number += k_Power[Mathf.Min(9, precision)]; + // Using decimal type due to floating point precision impacting formatting + decimal valueD = (decimal)value; + + // Round up value to the specified prevision otherwise set precision to max. + if (padding == 0 && precision == 0) + precision = 9; + else + valueD += k_Power[Mathf.Min(9, precision)]; - double integer = Math.Truncate(number); + long integer = (long)valueD; - AddIntToCharArray(integer, ref index, precision); + AddIntegerToCharArray(integer, padding, ref writeIndex); if (precision > 0) { - // Add the decimal point - m_input_CharArray[index++] = '.'; + valueD -= integer; - number -= integer; - for (int p = 0; p < precision; p++) + // Add decimal point and values only if remainder is not zero. + if (valueD != 0) { - number *= 10; - long d = (long)(number); + // Add decimal point + m_input_CharArray[writeIndex++] = '.'; - m_input_CharArray[index++] = (char)(d + 48); - number -= d; + for (int p = 0; p < precision; p++) + { + valueD *= 10; + long d = (long)valueD; + + m_input_CharArray[writeIndex++] = (char)(d + 48); + valueD -= d; + + if (valueD == 0) + p = precision; + } } } } /// - /// // Function used in conjunction with SetText() + /// /// /// - /// - /// - protected void AddIntToCharArray(double number, ref int index, int precision) + /// + /// + protected void AddIntegerToCharArray(double number, int padding, ref int writeIndex) { - if (number < 0) - { - m_input_CharArray[index++] = '-'; - number = -number; - } + int integralCount = 0; + int i = writeIndex; - int i = index; do { m_input_CharArray[i++] = (char)(number % 10 + 48); number /= 10; - } while (number > 0.999d); + integralCount += 1; + } while (number > 0.999d || integralCount < padding); int lastIndex = i; - // Reverse string - while (index + 1 < i) + //// Reverse string + while (writeIndex + 1 < i) { i -= 1; - char t = m_input_CharArray[index]; - m_input_CharArray[index] = m_input_CharArray[i]; + char t = m_input_CharArray[writeIndex]; + m_input_CharArray[writeIndex] = m_input_CharArray[i]; m_input_CharArray[i] = t; - index += 1; + writeIndex += 1; } - index = lastIndex; + writeIndex = lastIndex; } @@ -4721,7 +4894,7 @@ public Vector2 GetRenderedValues() } /// - /// + /// /// /// Should returned value only factor in visible characters and exclude those greater than maxVisibleCharacters for instance. /// @@ -4850,8 +5023,7 @@ protected virtual Vector2 CalculatePreferredValues(float defaultFontSize, Vector m_maxLineDescender = k_LargePositiveFloat; m_lineNumber = 0; m_startOfLineAscender = 0; - bool isDrivenLineSpacing = false; - + m_IsDrivenLineSpacing = false; float marginWidth = marginSize.x; float marginHeight = marginSize.y; @@ -4941,17 +5113,18 @@ protected virtual Vector2 CalculatePreferredValues(float defaultFontSize, Vector switch (charCode) { case 0x03: - // + m_internalCharacterInfo[m_characterCount].textElement = m_currentFontAsset.characterLookupTable[0x03]; + m_isTextTruncated = true; break; case 0x2D: - // + // break; case 0x2026: - m_internalCharacterInfo[m_characterCount].textElement = m_cached_Ellipsis_Character; + m_internalCharacterInfo[m_characterCount].textElement = m_Ellipsis.character; ; m_internalCharacterInfo[m_characterCount].elementType = TMP_TextElementType.Character; - m_internalCharacterInfo[m_characterCount].fontAsset = m_materialReferences[0].fontAsset; - m_internalCharacterInfo[m_characterCount].material = m_materialReferences[0].material; - m_internalCharacterInfo[m_characterCount].materialReferenceIndex = 0; + m_internalCharacterInfo[m_characterCount].fontAsset = m_Ellipsis.fontAsset; + m_internalCharacterInfo[m_characterCount].material = m_Ellipsis.material; + m_internalCharacterInfo[m_characterCount].materialReferenceIndex = m_Ellipsis.materialIndex; // Indicates the source parsing data has been modified. m_isTextTruncated = true; @@ -5012,10 +5185,10 @@ protected virtual Vector2 CalculatePreferredValues(float defaultFontSize, Vector // Look up Character Data from Dictionary and cache it. #region Look up Character Data - float baselineOffset = 0; + //float baselineOffset = 0; float spriteScale = 1; - float spriteAscentLine = 0; - float spriteDescentLine = 0; + float elementAscentLine = 0; + float elementDescentLine = 0; if (m_textElementType == TMP_TextElementType.Sprite) { // If a sprite is used as a fallback then get a reference to it and set the color to white. @@ -5036,17 +5209,17 @@ protected virtual Vector2 CalculatePreferredValues(float defaultFontSize, Vector { spriteScale = (m_currentFontSize / m_currentSpriteAsset.faceInfo.pointSize * m_currentSpriteAsset.faceInfo.scale * (m_isOrthographic ? 1 : 0.1f)); currentElementScale = sprite.scale * sprite.glyph.scale * spriteScale; - spriteAscentLine = m_currentSpriteAsset.faceInfo.ascentLine; - baselineOffset = m_currentSpriteAsset.faceInfo.baseline * m_fontScale * m_fontScaleMultiplier * m_currentSpriteAsset.faceInfo.scale; - spriteDescentLine = m_currentSpriteAsset.faceInfo.descentLine; + elementAscentLine = m_currentSpriteAsset.faceInfo.ascentLine; + //baselineOffset = m_currentSpriteAsset.faceInfo.baseline * m_fontScale * m_fontScaleMultiplier * m_currentSpriteAsset.faceInfo.scale; + elementDescentLine = m_currentSpriteAsset.faceInfo.descentLine; } else { spriteScale = (m_currentFontSize / m_currentFontAsset.faceInfo.pointSize * m_currentFontAsset.faceInfo.scale * (m_isOrthographic ? 1 : 0.1f)); currentElementScale = m_currentFontAsset.faceInfo.ascentLine / sprite.glyph.metrics.height * sprite.scale * sprite.glyph.scale * spriteScale; - spriteAscentLine = m_currentFontAsset.faceInfo.ascentLine; - baselineOffset = m_currentFontAsset.faceInfo.baseline * m_fontScale * m_fontScaleMultiplier * m_currentFontAsset.faceInfo.scale; - spriteDescentLine = m_currentFontAsset.faceInfo.descentLine; + elementAscentLine = m_currentFontAsset.faceInfo.ascentLine; + //baselineOffset = m_currentFontAsset.faceInfo.baseline * m_fontScale * m_fontScaleMultiplier * m_currentFontAsset.faceInfo.scale; + elementDescentLine = m_currentFontAsset.faceInfo.descentLine; } m_cached_TextElement = sprite; @@ -5063,11 +5236,16 @@ protected virtual Vector2 CalculatePreferredValues(float defaultFontSize, Vector m_currentMaterialIndex = m_textInfo.characterInfo[m_characterCount].materialReferenceIndex; - // Re-calculate font scale as the font asset may have changed. - m_fontScale = m_currentFontSize * smallCapsMultiplier / m_currentFontAsset.faceInfo.pointSize * m_currentFontAsset.faceInfo.scale * (m_isOrthographic ? 1 : 0.1f); + if (isInjectingCharacter && m_InternalParsingBuffer[i].unicode == 0x0A && m_characterCount != m_firstCharacterOfLine) + m_fontScale = m_textInfo.characterInfo[m_characterCount - 1].pointSize * smallCapsMultiplier / m_currentFontAsset.m_FaceInfo.pointSize * m_currentFontAsset.m_FaceInfo.scale * (m_isOrthographic ? 1 : 0.1f); + else + m_fontScale = m_currentFontSize * smallCapsMultiplier / m_currentFontAsset.m_FaceInfo.pointSize * m_currentFontAsset.m_FaceInfo.scale * (m_isOrthographic ? 1 : 0.1f); + + elementAscentLine = m_currentFontAsset.m_FaceInfo.ascentLine; + elementDescentLine = m_currentFontAsset.m_FaceInfo.descentLine; currentElementScale = m_fontScale * m_fontScaleMultiplier * m_cached_TextElement.scale; - baselineOffset = m_currentFontAsset.faceInfo.baseline * m_fontScale * m_fontScaleMultiplier * m_currentFontAsset.faceInfo.scale; + //baselineOffset = m_currentFontAsset.faceInfo.baseline * m_fontScale * m_fontScaleMultiplier * m_currentFontAsset.faceInfo.scale; m_internalCharacterInfo[m_characterCount].elementType = TMP_TextElementType.Character; } @@ -5169,15 +5347,15 @@ protected virtual Vector2 CalculatePreferredValues(float defaultFontSize, Vector // Compute and save text element Ascender and maximum line Ascender. #region Compute Ascender & Descender values float elementAscender = m_textElementType == TMP_TextElementType.Character - ? m_currentFontAsset.faceInfo.ascentLine * currentElementScale / smallCapsMultiplier + m_baselineOffset - : spriteAscentLine * spriteScale + m_baselineOffset; + ? elementAscentLine * currentElementScale / smallCapsMultiplier + m_baselineOffset + : elementAscentLine * spriteScale + m_baselineOffset; m_internalCharacterInfo[m_characterCount].ascender = elementAscender - m_lineOffset; // Compute and save text element Descender and maximum line Descender. float elementDescender = m_textElementType == TMP_TextElementType.Character - ? m_currentFontAsset.faceInfo.descentLine * currentElementScale / smallCapsMultiplier + m_baselineOffset - : spriteDescentLine * spriteScale + m_baselineOffset; + ? elementDescentLine * currentElementScale / smallCapsMultiplier + m_baselineOffset + : elementDescentLine * spriteScale + m_baselineOffset; float elementDescenderII = m_internalCharacterInfo[m_characterCount].descender = elementDescender - m_lineOffset; @@ -5282,9 +5460,9 @@ protected virtual Vector2 CalculatePreferredValues(float defaultFontSize, Vector if (m_useMaxVisibleDescender && (m_characterCount >= m_maxVisibleCharacters || m_lineNumber >= m_maxVisibleLines)) isMaxVisibleDescenderSet = true; - + // Store first character of the next line. - m_firstCharacterOfLine = m_characterCount; + m_firstCharacterOfLine = m_characterCount; m_lineVisibleCharacterCount = 0; // Compute Preferred Width & Height @@ -5305,12 +5483,12 @@ protected virtual Vector2 CalculatePreferredValues(float defaultFontSize, Vector { float ascender = m_internalCharacterInfo[m_characterCount].ascender - m_internalCharacterInfo[m_characterCount].baseLine; m_lineOffset += 0 - m_maxLineDescender + ascender + (lineGap + m_lineSpacingDelta) * baseScale + m_lineSpacing * currentEmScale; - isDrivenLineSpacing = false; + m_IsDrivenLineSpacing = false; } else { m_lineOffset += m_lineHeight + m_lineSpacing * currentEmScale; - isDrivenLineSpacing = true; + m_IsDrivenLineSpacing = true; } m_maxLineAscender = k_LargeNegativeFloat; @@ -5333,7 +5511,7 @@ protected virtual Vector2 CalculatePreferredValues(float defaultFontSize, Vector // Check if Line Spacing of previous line needs to be adjusted. #region Adjust Line Spacing - if (m_lineNumber > 0 && !TMP_Math.Approximately(m_maxLineAscender, m_startOfLineAscender) && isDrivenLineSpacing == false && !m_isNewPage && isInjectingCharacter == false) + if (m_lineOffset > 0 && !TMP_Math.Approximately(m_maxLineAscender, m_startOfLineAscender) && m_IsDrivenLineSpacing == false && !m_isNewPage) { float offsetDelta = m_maxLineAscender - m_startOfLineAscender; //AdjustLineOffset(m_firstCharacterOfLine, m_characterCount, offsetDelta); @@ -5342,7 +5520,7 @@ protected virtual Vector2 CalculatePreferredValues(float defaultFontSize, Vector m_startOfLineAscender += offsetDelta; internalWordWrapState.lineOffset = m_lineOffset; - internalWordWrapState.previousLineAscender = m_startOfLineAscender; + internalWordWrapState.startOfLineAscender = m_startOfLineAscender; } #endregion @@ -5388,7 +5566,7 @@ protected virtual Vector2 CalculatePreferredValues(float defaultFontSize, Vector if (charCode == 10 || charCode == 11 || charCode == 0x03 || charCode == 0x2028 || charCode == 0x2029 || m_characterCount == totalCharacterCount - 1) { // Check if Line Spacing of previous line needs to be adjusted. - if (m_lineNumber > 0 && !TMP_Math.Approximately(m_maxLineAscender, m_startOfLineAscender) && isDrivenLineSpacing == false && !m_isNewPage && isInjectingCharacter == false) + if (m_lineOffset > 0 && !TMP_Math.Approximately(m_maxLineAscender, m_startOfLineAscender) && m_IsDrivenLineSpacing == false && !m_isNewPage && isInjectingCharacter == false) { float offsetDelta = m_maxLineAscender - m_startOfLineAscender; elementDescenderII -= offsetDelta; @@ -5429,12 +5607,12 @@ protected virtual Vector2 CalculatePreferredValues(float defaultFontSize, Vector { float lineOffsetDelta = 0 - m_maxLineDescender + elementAscender + (lineGap + m_lineSpacingDelta) * baseScale + (m_lineSpacing + (charCode == 10 || charCode == 0x2029 ? m_paragraphSpacing : 0)) * currentEmScale; m_lineOffset += lineOffsetDelta; - isDrivenLineSpacing = false; + m_IsDrivenLineSpacing = false; } else { m_lineOffset += m_lineHeight + (m_lineSpacing + (charCode == 10 || charCode == 0x2029 ? m_paragraphSpacing : 0)) * currentEmScale; - isDrivenLineSpacing = true; + m_IsDrivenLineSpacing = true; } m_maxLineAscender = k_LargeNegativeFloat; @@ -5460,7 +5638,7 @@ protected virtual Vector2 CalculatePreferredValues(float defaultFontSize, Vector { if ((char.IsWhiteSpace((char)charCode) || charCode == 0x200B || charCode == 0x2D || charCode == 0xAD) && !m_isNonBreakingSpace && charCode != 0xA0 && charCode != 0x2007 && charCode != 0x2011 && charCode != 0x202F && charCode != 0x2060) { - // We store the state of numerous variables for the most recent Space, LineFeed or Carriage Return to enable them to be restored + // We store the state of numerous variables for the most recent Space, LineFeed or Carriage Return to enable them to be restored // for Word Wrapping. SaveWordWrappingState(ref internalWordWrapState, i, m_characterCount); isFirstWordOfLine = false; @@ -5831,9 +6009,11 @@ protected void SaveWordWrappingState(ref WordWrapState state, int index, int cou state.maxCapHeight = m_maxCapHeight; state.maxAscender = m_maxAscender; state.maxDescender = m_maxDescender; + state.startOfLineAscender = m_startOfLineAscender; state.maxLineAscender = m_maxLineAscender; state.maxLineDescender = m_maxLineDescender; - state.previousLineAscender = m_startOfLineAscender; + state.pageAscender = m_PageAscender; + state.preferredWidth = m_preferredWidth; state.preferredHeight = m_preferredHeight; state.meshExtents = m_meshExtents; @@ -5841,6 +6021,7 @@ protected void SaveWordWrappingState(ref WordWrapState state, int index, int cou state.lineNumber = m_lineNumber; state.lineOffset = m_lineOffset; state.baselineOffset = m_baselineOffset; + state.isDrivenLineSpacing = m_IsDrivenLineSpacing; state.cSpace = m_cSpacing; state.mSpace = m_monoSpacing; @@ -5919,9 +6100,11 @@ protected int RestoreWordWrappingState(ref WordWrapState state) m_maxCapHeight = state.maxCapHeight; m_maxAscender = state.maxAscender; m_maxDescender = state.maxDescender; + m_startOfLineAscender = state.startOfLineAscender; m_maxLineAscender = state.maxLineAscender; m_maxLineDescender = state.maxLineDescender; - m_startOfLineAscender = state.previousLineAscender; + m_PageAscender = state.pageAscender; + m_preferredWidth = state.preferredWidth; m_preferredHeight = state.preferredHeight; m_meshExtents = state.meshExtents; @@ -5929,6 +6112,7 @@ protected int RestoreWordWrappingState(ref WordWrapState state) m_lineNumber = state.lineNumber; m_lineOffset = state.lineOffset; m_baselineOffset = state.baselineOffset; + m_IsDrivenLineSpacing = state.isDrivenLineSpacing; m_cSpacing = state.cSpace; m_monoSpacing = state.mSpace; @@ -6399,29 +6583,37 @@ protected virtual void FillSpriteVertexBuffers(int i, int index_X4) /// protected virtual void DrawUnderlineMesh(Vector3 start, Vector3 end, ref int index, float startScale, float endScale, float maxScale, float sdfScale, Color32 underlineColor) { - if (m_cached_Underline_Character == null) + // Get Underline special character from the primary font asset. + GetUnderlineSpecialCharacter(m_fontAsset); + + if (m_Underline.character == null) { if (!TMP_Settings.warningsDisabled) - Debug.LogWarning("Unable to add underline since the Font Asset doesn't contain the underline character.", this); + Debug.LogWarning("Unable to add underline since the primary Font Asset doesn't contain the underline character.", this); return; } + int underlineMaterialIndex = m_Underline.materialIndex; + int verticesCount = index + 12; // Check to make sure our current mesh buffer allocations can hold these new Quads. - if (verticesCount > m_textInfo.meshInfo[0].vertices.Length) + if (verticesCount > m_textInfo.meshInfo[underlineMaterialIndex].vertices.Length) { // Resize Mesh Buffers - m_textInfo.meshInfo[0].ResizeMeshInfo(verticesCount / 4); + m_textInfo.meshInfo[underlineMaterialIndex].ResizeMeshInfo(verticesCount / 4); } // Adjust the position of the underline based on the lowest character. This matters for subscript character. start.y = Mathf.Min(start.y, end.y); end.y = Mathf.Min(start.y, end.y); - float segmentWidth = m_cached_Underline_Character.glyph.metrics.width / 2 * maxScale; + GlyphMetrics underlineGlyphMetrics = m_Underline.character.glyph.metrics; + GlyphRect underlineGlyphRect = m_Underline.character.glyph.glyphRect; - if (end.x - start.x < m_cached_Underline_Character.glyph.metrics.width * maxScale) + float segmentWidth = underlineGlyphMetrics.width / 2 * maxScale; + + if (end.x - start.x < underlineGlyphMetrics.width * maxScale) { segmentWidth = (end.x - start.x) / 2f; } @@ -6429,11 +6621,11 @@ protected virtual void DrawUnderlineMesh(Vector3 start, Vector3 end, ref int ind float startPadding = m_padding * startScale / maxScale; float endPadding = m_padding * endScale / maxScale; - float underlineThickness = m_fontAsset.faceInfo.underlineThickness; + float underlineThickness = m_Underline.fontAsset.faceInfo.underlineThickness; // UNDERLINE VERTICES FOR (3) LINE SEGMENTS #region UNDERLINE VERTICES - Vector3[] vertices = m_textInfo.meshInfo[0].vertices; + Vector3[] vertices = m_textInfo.meshInfo[underlineMaterialIndex].vertices; // Front Part of the Underline vertices[index + 0] = start + new Vector3(0, 0 - (underlineThickness + m_padding) * maxScale, 0); // BL @@ -6456,16 +6648,19 @@ protected virtual void DrawUnderlineMesh(Vector3 start, Vector3 end, ref int ind // UNDERLINE UV0 #region HANDLE UV0 - Vector2[] uvs0 = m_textInfo.meshInfo[0].uvs0; + Vector2[] uvs0 = m_textInfo.meshInfo[underlineMaterialIndex].uvs0; + + int atlasWidth = m_Underline.fontAsset.atlasWidth; + int atlasHeight = m_Underline.fontAsset.atlasHeight; // Calculate UV required to setup the 3 Quads for the Underline. - Vector2 uv0 = new Vector2((m_cached_Underline_Character.glyph.glyphRect.x - startPadding) / m_fontAsset.atlasWidth, (m_cached_Underline_Character.glyph.glyphRect.y - m_padding) / m_fontAsset.atlasHeight); // bottom left - Vector2 uv1 = new Vector2(uv0.x, (m_cached_Underline_Character.glyph.glyphRect.y + m_cached_Underline_Character.glyph.glyphRect.height + m_padding) / m_fontAsset.atlasHeight); // top left - Vector2 uv2 = new Vector2((m_cached_Underline_Character.glyph.glyphRect.x - startPadding + (float)m_cached_Underline_Character.glyph.glyphRect.width / 2) / m_fontAsset.atlasWidth, uv1.y); // Mid Top Left + Vector2 uv0 = new Vector2((underlineGlyphRect.x - startPadding) / atlasWidth, (underlineGlyphRect.y - m_padding) / atlasHeight); // bottom left + Vector2 uv1 = new Vector2(uv0.x, (underlineGlyphRect.y + underlineGlyphRect.height + m_padding) / atlasHeight); // top left + Vector2 uv2 = new Vector2((underlineGlyphRect.x - startPadding + (float)underlineGlyphRect.width / 2) / atlasWidth, uv1.y); // Mid Top Left Vector2 uv3 = new Vector2(uv2.x, uv0.y); // Mid Bottom Left - Vector2 uv4 = new Vector2((m_cached_Underline_Character.glyph.glyphRect.x + endPadding + (float)m_cached_Underline_Character.glyph.glyphRect.width / 2) / m_fontAsset.atlasWidth, uv1.y); // Mid Top Right + Vector2 uv4 = new Vector2((underlineGlyphRect.x + endPadding + (float)underlineGlyphRect.width / 2) / atlasWidth, uv1.y); // Mid Top Right Vector2 uv5 = new Vector2(uv4.x, uv0.y); // Mid Bottom right - Vector2 uv6 = new Vector2((m_cached_Underline_Character.glyph.glyphRect.x + endPadding + m_cached_Underline_Character.glyph.glyphRect.width) / m_fontAsset.atlasWidth, uv1.y); // End Part - Bottom Right + Vector2 uv6 = new Vector2((underlineGlyphRect.x + endPadding + underlineGlyphRect.width) / atlasWidth, uv1.y); // End Part - Bottom Right Vector2 uv7 = new Vector2(uv6.x, uv0.y); // End Part - Top Right // Left Part of the Underline @@ -6496,7 +6691,7 @@ protected virtual void DrawUnderlineMesh(Vector3 start, Vector3 end, ref int ind //Calculate the xScale or how much the UV's are getting stretched on the X axis for the middle section of the underline. float xScale = Mathf.Abs(sdfScale); - Vector2[] uvs2 = m_textInfo.meshInfo[0].uvs2; + Vector2[] uvs2 = m_textInfo.meshInfo[underlineMaterialIndex].uvs2; uvs2[0 + index] = PackUV(0, 0, xScale); uvs2[1 + index] = PackUV(0, 1, xScale); @@ -6521,11 +6716,11 @@ protected virtual void DrawUnderlineMesh(Vector3 start, Vector3 end, ref int ind #endregion // UNDERLINE VERTEX COLORS - #region + #region UNDERLINE VERTEX COLORS // Alpha is the lower of the vertex color or tag color alpha used. underlineColor.a = m_fontColor32.a < underlineColor.a ? (byte)(m_fontColor32.a) : (byte)(underlineColor.a); - Color32[] colors32 = m_textInfo.meshInfo[0].colors32; + Color32[] colors32 = m_textInfo.meshInfo[underlineMaterialIndex].colors32; colors32[0 + index] = underlineColor; colors32[1 + index] = underlineColor; colors32[2 + index] = underlineColor; @@ -6548,23 +6743,32 @@ protected virtual void DrawUnderlineMesh(Vector3 start, Vector3 end, ref int ind protected virtual void DrawTextHighlight(Vector3 start, Vector3 end, ref int index, Color32 highlightColor) { - if (m_cached_Underline_Character == null) + if (m_Underline.character == null) { - if (!TMP_Settings.warningsDisabled) Debug.LogWarning("Unable to add highlight since the Font Asset doesn't contain the underline character.", this); - return; + GetUnderlineSpecialCharacter(m_fontAsset); + + if (m_Underline.character == null) + { + if (!TMP_Settings.warningsDisabled) + Debug.LogWarning("Unable to add highlight since the primary Font Asset doesn't contain the underline character.", this); + + return; + } } + int underlineMaterialIndex = m_Underline.materialIndex; + int verticesCount = index + 4; // Check to make sure our current mesh buffer allocations can hold these new Quads. - if (verticesCount > m_textInfo.meshInfo[0].vertices.Length) + if (verticesCount > m_textInfo.meshInfo[underlineMaterialIndex].vertices.Length) { // Resize Mesh Buffers - m_textInfo.meshInfo[0].ResizeMeshInfo(verticesCount / 4); + m_textInfo.meshInfo[underlineMaterialIndex].ResizeMeshInfo(verticesCount / 4); } // UNDERLINE VERTICES FOR (3) LINE SEGMENTS #region HIGHLIGHT VERTICES - Vector3[] vertices = m_textInfo.meshInfo[0].vertices; + Vector3[] vertices = m_textInfo.meshInfo[underlineMaterialIndex].vertices; // Front Part of the Underline vertices[index + 0] = start; // BL @@ -6575,22 +6779,26 @@ protected virtual void DrawTextHighlight(Vector3 start, Vector3 end, ref int ind // UNDERLINE UV0 #region HANDLE UV0 - Vector2[] uvs0 = m_textInfo.meshInfo[0].uvs0; + Vector2[] uvs0 = m_textInfo.meshInfo[underlineMaterialIndex].uvs0; - // Calculate UV required to setup the 3 Quads for the Underline. - Vector2 uv0 = new Vector2(((float)m_cached_Underline_Character.glyph.glyphRect.x + m_cached_Underline_Character.glyph.glyphRect.width / 2) / m_fontAsset.atlasWidth, (m_cached_Underline_Character.glyph.glyphRect.y + (float)m_cached_Underline_Character.glyph.glyphRect.height / 2) / m_fontAsset.atlasHeight); // bottom left + int atlasWidth = m_fontAsset.atlasWidth; + int atlasHeight = m_fontAsset.atlasHeight; + GlyphRect glyphRect = m_Underline.character.glyph.glyphRect; + + // Calculate UV + Vector2 uv0 = new Vector2(((float)glyphRect.x + glyphRect.width / 2) / atlasWidth, (glyphRect.y + (float)glyphRect.height / 2) / atlasHeight); // bottom left //Vector2 uv1 = new Vector2(uv0.x, uv0.y); // top left //Vector2 uv2 = new Vector2(uv0.x, uv0.y); // Top Right //Vector2 uv3 = new Vector2(uv2.x, uv0.y); // Bottom Right - // Left Part of the Underline + // UVs for the Quad uvs0[0 + index] = uv0; // BL uvs0[1 + index] = uv0; // TL uvs0[2 + index] = uv0; // TR uvs0[3 + index] = uv0; // BR #endregion - // UNDERLINE UV2 + // HIGHLIGHT UV2 #region HANDLE UV2 - SDF SCALE // UV1 contains Face / Border UV layout. //float min_UvX = 0; @@ -6599,12 +6807,12 @@ protected virtual void DrawTextHighlight(Vector3 start, Vector3 end, ref int ind ////Calculate the xScale or how much the UV's are getting stretched on the X axis for the middle section of the underline. //float xScale = 0; // Mathf.Abs(sdfScale); - Vector2[] uvs2 = m_textInfo.meshInfo[0].uvs2; + Vector2[] uvs2 = m_textInfo.meshInfo[underlineMaterialIndex].uvs2; Vector2 customUV = new Vector2(0, 1); - uvs2[0 + index] = customUV; // PackUV(-0.2f, -0.2f, xScale); - uvs2[1 + index] = customUV; // PackUV(-0.2f, -0.1f, xScale); - uvs2[2 + index] = customUV; // PackUV(-0.1f, -0.1f, xScale); - uvs2[3 + index] = customUV; // PackUV(-0.1f, -0.2f, xScale); + uvs2[0 + index] = customUV; + uvs2[1 + index] = customUV; + uvs2[2 + index] = customUV; + uvs2[3 + index] = customUV; #endregion // HIGHLIGHT VERTEX COLORS @@ -6612,7 +6820,7 @@ protected virtual void DrawTextHighlight(Vector3 start, Vector3 end, ref int ind // Alpha is the lower of the vertex color or tag color alpha used. highlightColor.a = m_fontColor32.a < highlightColor.a ? m_fontColor32.a : highlightColor.a; - Color32[] colors32 = m_textInfo.meshInfo[0].colors32; + Color32[] colors32 = m_textInfo.meshInfo[underlineMaterialIndex].colors32; colors32[0 + index] = highlightColor; colors32[1 + index] = highlightColor; colors32[2 + index] = highlightColor; @@ -6659,7 +6867,6 @@ protected void LoadDefaultSettings() m_fontSize = m_fontSizeBase = TMP_Settings.defaultFontSize; m_fontSizeMin = m_fontSize * TMP_Settings.defaultTextAutoSizingMinRatio; m_fontSizeMax = m_fontSize * TMP_Settings.defaultTextAutoSizingMaxRatio; - m_isAlignmentEnumConverted = true; m_isWaitingOnResourceLoad = false; raycastTarget = TMP_Settings.enableRaycastTarget; } @@ -6684,32 +6891,97 @@ protected void LoadDefaultSettings() /// /// protected void GetSpecialCharacters(TMP_FontAsset fontAsset) + { + GetEllipsisSpecialCharacter(fontAsset); + + GetUnderlineSpecialCharacter(fontAsset); + } + + + protected void GetEllipsisSpecialCharacter(TMP_FontAsset fontAsset) { bool isUsingAlternativeTypeface; TMP_FontAsset tempFontAsset; - // Check & Assign Underline Character for use with the Underline tag. - if (!fontAsset.characterLookupTable.TryGetValue(95, out m_cached_Underline_Character)) + // Search base font asset + m_Ellipsis.character = TMP_FontAssetUtilities.GetCharacterFromFontAsset(0x2026, fontAsset, false, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface, out tempFontAsset); + + if (m_Ellipsis.character == null) { - m_cached_Underline_Character = TMP_FontAssetUtilities.GetCharacterFromFontAsset(95,fontAsset, false, m_FontStyleInternal, (FontWeight)m_FontWeightInternal, out isUsingAlternativeTypeface, out tempFontAsset); + // Search primary fallback list + if (fontAsset.m_FallbackFontAssetTable != null && fontAsset.m_FallbackFontAssetTable.Count > 0) + m_Ellipsis.character = TMP_FontAssetUtilities.GetCharacterFromFontAssets(0x2026, fontAsset.m_FallbackFontAssetTable, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface, out tempFontAsset); + } - if (m_cached_Underline_Character == null) - { - if (!TMP_Settings.warningsDisabled) - Debug.LogWarning("The character used for Underline and Strikethrough is not available in font asset [" + fontAsset.name + "].", this); - } + // Search TMP Settings general fallback list + if (m_Ellipsis.character == null) + { + if (TMP_Settings.fallbackFontAssets != null && TMP_Settings.fallbackFontAssets.Count > 0) + m_Ellipsis.character = TMP_FontAssetUtilities.GetCharacterFromFontAssets(0x2026, TMP_Settings.fallbackFontAssets, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface, out tempFontAsset); } - // Check & Assign Ellipsis Character - if (!fontAsset.characterLookupTable.TryGetValue(8230, out m_cached_Ellipsis_Character)) //95 + // Search TMP Settings' default font asset + if (m_Ellipsis.character == null) { - m_cached_Ellipsis_Character = TMP_FontAssetUtilities.GetCharacterFromFontAsset(8230, fontAsset, false, m_FontStyleInternal, (FontWeight)m_FontWeightInternal, out isUsingAlternativeTypeface, out tempFontAsset); + if (TMP_Settings.defaultFontAsset != null) + m_Ellipsis.character = TMP_FontAssetUtilities.GetCharacterFromFontAsset(0x2026, TMP_Settings.defaultFontAsset, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface, out tempFontAsset); + } - if (m_cached_Ellipsis_Character == null) - { - if (!TMP_Settings.warningsDisabled) - Debug.LogWarning("The character used for Ellipsis is not available in font asset [" + fontAsset.name + "].", this); - } + if (m_Ellipsis.character != null) + { + m_Ellipsis.fontAsset = tempFontAsset; + m_Ellipsis.material = tempFontAsset.material; + m_Ellipsis.materialIndex = 0; + } + else + { + if (!TMP_Settings.warningsDisabled) + Debug.LogWarning("The character used for Ellipsis is not available in font asset [" + fontAsset.name + "].", this); + } + } + + + protected void GetUnderlineSpecialCharacter(TMP_FontAsset fontAsset) + { + bool isUsingAlternativeTypeface; + TMP_FontAsset tempFontAsset; + + // Search base font asset + m_Underline.character = TMP_FontAssetUtilities.GetCharacterFromFontAsset(0x5F, fontAsset, false, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface, out tempFontAsset); + + /* + if (m_Underline.character == null) + { + // Search primary fallback list + if (fontAsset.m_FallbackFontAssetTable != null && fontAsset.m_FallbackFontAssetTable.Count > 0) + m_Underline.character = TMP_FontAssetUtilities.GetCharacterFromFontAssets(0x5F, fontAsset.m_FallbackFontAssetTable, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface, out tempFontAsset); + } + + // Search TMP Settings general fallback list + if (m_Underline.character == null) + { + if (TMP_Settings.fallbackFontAssets != null && TMP_Settings.fallbackFontAssets.Count > 0) + m_Underline.character = TMP_FontAssetUtilities.GetCharacterFromFontAssets(0x5F, TMP_Settings.fallbackFontAssets, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface, out tempFontAsset); + } + + // Search TMP Settings' default font asset + if (m_Underline.character == null) + { + if (TMP_Settings.defaultFontAsset != null) + m_Underline.character = TMP_FontAssetUtilities.GetCharacterFromFontAsset(0x5F, TMP_Settings.defaultFontAsset, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface, out tempFontAsset); + } + */ + + if (m_Underline.character != null) + { + m_Underline.fontAsset = tempFontAsset; + m_Underline.material = tempFontAsset.material; + m_Underline.materialIndex = 0; + } + else + { + if (!TMP_Settings.warningsDisabled) + Debug.LogWarning("The character used for Underline is not available in font asset [" + fontAsset.name + "].", this); } } @@ -6733,7 +7005,7 @@ protected void ReplaceTagWithCharacter(int[] chars, int insertionIndex, int tagL /// - /// + /// /// /// //protected int GetMaterialReferenceForFontWeight() @@ -6747,7 +7019,7 @@ protected void ReplaceTagWithCharacter(int[] chars, int insertionIndex, int tagL /// - /// + /// /// /// protected TMP_FontAsset GetFontAssetForWeight(int fontWeight) @@ -6891,7 +7163,7 @@ protected Vector2 PackUV(float x, float y, float scale) /// - /// + /// /// /// /// @@ -6930,7 +7202,7 @@ internal virtual void InternalUpdate() { } /// - /// + /// /// /// /// @@ -7229,7 +7501,7 @@ protected float ConvertToFloat(char[] chars, int startIndex, int length) /// - /// Extracts a float value from char[] given a start index and length. + /// Extracts a float value from char[] given a start index and length. /// /// The Char[] containing the numerical sequence. /// The index of the start of the numerical sequence. @@ -7540,14 +7812,14 @@ protected bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endI return true; } // Color <#FF00FF> - else if (m_htmlTag[0] == 35 && tagCharCount == 7) // if Tag begins with # and contains 7 characters. + else if (m_htmlTag[0] == 35 && tagCharCount == 7) // if Tag begins with # and contains 7 characters. { m_htmlColor = HexCharsToColor(m_htmlTag, tagCharCount); m_colorStack.Add(m_htmlColor); return true; } // Color <#FF00FF00> with alpha - else if (m_htmlTag[0] == 35 && tagCharCount == 9) // if Tag begins with # and contains 9 characters. + else if (m_htmlTag[0] == 35 && tagCharCount == 9) // if Tag begins with # and contains 9 characters. { m_htmlColor = HexCharsToColor(m_htmlTag, tagCharCount); m_colorStack.Add(m_htmlColor); @@ -7811,7 +8083,7 @@ protected bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endI case 6380: // case 4556: // value = ConvertToFloat(m_htmlTag, m_xmlAttribute[0].valueStartIndex, m_xmlAttribute[0].valueLength); - + // Reject tag if value is invalid. if (value == Int16.MinValue) return false; @@ -7838,7 +8110,7 @@ protected bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endI case 16034505: // case 11642281: // value = ConvertToFloat(m_htmlTag, m_xmlAttribute[0].valueStartIndex, m_xmlAttribute[0].valueLength); - + // Reject tag if value is invalid. if (value == Int16.MinValue) return false; @@ -8062,7 +8334,7 @@ protected bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endI } - // Check if material + // Check if material if (MaterialReferenceManager.TryGetMaterial(materialHashCode, out tempMaterial)) { // Check if material font atlas texture matches that of the current font asset. @@ -8111,7 +8383,7 @@ protected bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endI case 320078: // case 230446: // value = ConvertToFloat(m_htmlTag, m_xmlAttribute[0].valueStartIndex, m_xmlAttribute[0].valueLength); - + // Reject tag if value is invalid. if (value == Int16.MinValue) return false; @@ -8387,7 +8659,7 @@ protected bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endI case 1983971: // case 1356515: // value = ConvertToFloat(m_htmlTag, m_xmlAttribute[0].valueStartIndex, m_xmlAttribute[0].valueLength); - + // Reject tag if value is invalid. if (value == Int16.MinValue) return false; @@ -8542,7 +8814,7 @@ protected bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endI // Load Sprite Asset if (tempSpriteAsset == null) { - // + // if (onSpriteAssetRequest != null) tempSpriteAsset = onSpriteAssetRequest(spriteAssetHashCode, new string(m_htmlTag, m_xmlAttribute[0].valueStartIndex, m_xmlAttribute[0].valueLength)); @@ -8595,7 +8867,7 @@ protected bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endI case 295562: // case 205930: // index = (int)ConvertToFloat(m_htmlTag, m_xmlAttribute[1].valueStartIndex, m_xmlAttribute[1].valueLength); - + // Reject tag if value is invalid. if (index == Int16.MinValue) return false; @@ -8831,7 +9103,7 @@ protected bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endI switch (tagUnitType) { case TagUnitType.Pixels: - m_lineHeight = value * (m_isOrthographic ? 1 : 0.1f); + m_lineHeight = value * (m_isOrthographic ? 1 : 0.1f); break; case TagUnitType.FontUnits: m_lineHeight = value * (m_isOrthographic ? 1 : 0.1f) * m_currentFontSize; diff --git a/Scripts/Runtime/TMP_TextInfo.cs b/Scripts/Runtime/TMP_TextInfo.cs index d1bce41..9681305 100644 --- a/Scripts/Runtime/TMP_TextInfo.cs +++ b/Scripts/Runtime/TMP_TextInfo.cs @@ -162,6 +162,9 @@ public void ClearLineInfo() this.lineInfo[i].ascender = k_InfinityVectorNegative.x; this.lineInfo[i].descender = k_InfinityVectorPositive.x; + this.lineInfo[i].marginLeft = 0; + this.lineInfo[i].marginRight = 0; + this.lineInfo[i].lineExtents.min = k_InfinityVectorPositive; this.lineInfo[i].lineExtents.max = k_InfinityVectorNegative; diff --git a/Scripts/Runtime/TMP_UpdateManager.cs b/Scripts/Runtime/TMP_UpdateManager.cs index ce41ba0..8d51686 100644 --- a/Scripts/Runtime/TMP_UpdateManager.cs +++ b/Scripts/Runtime/TMP_UpdateManager.cs @@ -2,12 +2,6 @@ using UnityEngine.UI; using System.Collections.Generic; -#if UNITY_2019_1_OR_NEWER -using UnityEngine.Rendering; -#elif UNITY_2018_1_OR_NEWER -using UnityEngine.Experimental.Rendering; -#endif - namespace TMPro { @@ -17,52 +11,44 @@ public class TMP_UpdateManager private static TMP_UpdateManager s_Instance; private readonly List m_LayoutRebuildQueue = new List(); - private HashSet m_LayoutQueueLookup = new HashSet(); + private readonly HashSet m_LayoutQueueLookup = new HashSet(); private readonly List m_GraphicRebuildQueue = new List(); - private HashSet m_GraphicQueueLookup = new HashSet(); + private readonly HashSet m_GraphicQueueLookup = new HashSet(); private readonly List m_InternalUpdateQueue = new List(); - private HashSet m_InternalUpdateLookup = new HashSet(); + private readonly HashSet m_InternalUpdateLookup = new HashSet(); - private int m_LastFrame = 0; /// /// Get a singleton instance of the registry /// - public static TMP_UpdateManager instance + static TMP_UpdateManager instance { get { - if (TMP_UpdateManager.s_Instance == null) - TMP_UpdateManager.s_Instance = new TMP_UpdateManager(); - return TMP_UpdateManager.s_Instance; + if (s_Instance == null) + s_Instance = new TMP_UpdateManager(); + + return s_Instance; } } - /// /// Register to receive rendering callbacks. /// - protected TMP_UpdateManager() + TMP_UpdateManager() { - Camera.onPreCull += OnCameraPreCull; - - #if UNITY_2019_1_OR_NEWER - RenderPipelineManager.beginFrameRendering += OnBeginFrameRendering; - #elif UNITY_2018_1_OR_NEWER - RenderPipeline.beginFrameRendering += OnBeginFrameRendering; - #endif + Canvas.willRenderCanvases += DoRebuilds; } - /// /// Function used as a replacement for LateUpdate() to handle SDF Scale updates and Legacy Animation updates. /// /// internal static void RegisterTextObjectForUpdate(TMP_Text textObject) { - TMP_UpdateManager.instance.InternalRegisterTextObjectForUpdate(textObject); + instance.InternalRegisterTextObjectForUpdate(textObject); } private void InternalRegisterTextObjectForUpdate(TMP_Text textObject) @@ -74,97 +60,56 @@ private void InternalRegisterTextObjectForUpdate(TMP_Text textObject) m_InternalUpdateLookup.Add(id); m_InternalUpdateQueue.Add(textObject); - - return; } - /// /// Function to register elements which require a layout rebuild. /// /// public static void RegisterTextElementForLayoutRebuild(TMP_Text element) { - TMP_UpdateManager.instance.InternalRegisterTextElementForLayoutRebuild(element); + instance.InternalRegisterTextElementForLayoutRebuild(element); } - private bool InternalRegisterTextElementForLayoutRebuild(TMP_Text element) + private void InternalRegisterTextElementForLayoutRebuild(TMP_Text element) { int id = element.GetInstanceID(); if (m_LayoutQueueLookup.Contains(id)) - return false; + return; m_LayoutQueueLookup.Add(id); m_LayoutRebuildQueue.Add(element); - - return true; } - /// /// Function to register elements which require a layout rebuild. /// /// public static void RegisterTextElementForGraphicRebuild(TMP_Text element) { - TMP_UpdateManager.instance.InternalRegisterTextElementForGraphicRebuild(element); + instance.InternalRegisterTextElementForGraphicRebuild(element); } - private bool InternalRegisterTextElementForGraphicRebuild(TMP_Text element) + private void InternalRegisterTextElementForGraphicRebuild(TMP_Text element) { int id = element.GetInstanceID(); if (m_GraphicQueueLookup.Contains(id)) - return false; + return; m_GraphicQueueLookup.Add(id); m_GraphicRebuildQueue.Add(element); - - return true; - } - - - /// - /// Callback which occurs just before the Scriptable Render Pipeline (SRP) begins rendering. - /// - /// - #if UNITY_2019_1_OR_NEWER - void OnBeginFrameRendering(ScriptableRenderContext renderContext, Camera[] cameras) - #elif UNITY_2018_1_OR_NEWER - void OnBeginFrameRendering(Camera[] cameras) - #endif - { - int currentFrame = Time.frameCount; - - // Make sure we only rebuild text objects once per frame regardless of number of cameras in the scene. - if (currentFrame < 2 || currentFrame != m_LastFrame) - { - DoRebuilds(); - - m_LastFrame = currentFrame; - } } /// /// Callback which occurs just before the cam is rendered. /// - /// - void OnCameraPreCull(Camera cam) + void OnCameraPreCull() { - int currentFrame = Time.frameCount; - - // Make sure we only rebuild text objects once per frame regardless of number of cameras in the scene. - if (currentFrame < 2 || currentFrame != m_LastFrame) - { - DoRebuilds(); - - m_LastFrame = currentFrame; - //Debug.Log("Updating text objects at frame:" + m_LastFrame); - } + DoRebuilds(); } - /// /// Process the rebuild requests in the rebuild queues. /// @@ -204,7 +149,7 @@ void DoRebuilds() internal static void UnRegisterTextObjectForUpdate(TMP_Text textObject) { - TMP_UpdateManager.instance.InternalUnRegisterTextObjectForUpdate(textObject); + instance.InternalUnRegisterTextObjectForUpdate(textObject); } /// @@ -213,16 +158,16 @@ internal static void UnRegisterTextObjectForUpdate(TMP_Text textObject) /// public static void UnRegisterTextElementForRebuild(TMP_Text element) { - TMP_UpdateManager.instance.InternalUnRegisterTextElementForGraphicRebuild(element); - TMP_UpdateManager.instance.InternalUnRegisterTextElementForLayoutRebuild(element); - TMP_UpdateManager.instance.InternalUnRegisterTextObjectForUpdate(element); + instance.InternalUnRegisterTextElementForGraphicRebuild(element); + instance.InternalUnRegisterTextElementForLayoutRebuild(element); + instance.InternalUnRegisterTextObjectForUpdate(element); } private void InternalUnRegisterTextElementForGraphicRebuild(TMP_Text element) { int id = element.GetInstanceID(); - TMP_UpdateManager.instance.m_GraphicRebuildQueue.Remove(element); + instance.m_GraphicRebuildQueue.Remove(element); m_GraphicQueueLookup.Remove(id); } @@ -230,7 +175,7 @@ private void InternalUnRegisterTextElementForLayoutRebuild(TMP_Text element) { int id = element.GetInstanceID(); - TMP_UpdateManager.instance.m_LayoutRebuildQueue.Remove(element); + instance.m_LayoutRebuildQueue.Remove(element); m_LayoutQueueLookup.Remove(id); } @@ -238,7 +183,7 @@ private void InternalUnRegisterTextObjectForUpdate(TMP_Text textObject) { int id = textObject.GetInstanceID(); - TMP_UpdateManager.instance.m_InternalUpdateQueue.Remove(textObject); + instance.m_InternalUpdateQueue.Remove(textObject); m_InternalUpdateLookup.Remove(id); } } diff --git a/Scripts/Runtime/TMPro_MeshUtilities.cs b/Scripts/Runtime/TMPro_MeshUtilities.cs index b331ad9..cb5bb12 100644 --- a/Scripts/Runtime/TMPro_MeshUtilities.cs +++ b/Scripts/Runtime/TMPro_MeshUtilities.cs @@ -279,9 +279,10 @@ public struct WordWrapState public float maxCapHeight; public float maxAscender; public float maxDescender; + public float startOfLineAscender; public float maxLineAscender; public float maxLineDescender; - public float previousLineAscender; + public float pageAscender; public HorizontalAlignmentOptions horizontalAlignment; public float marginLeft; @@ -302,6 +303,7 @@ public struct WordWrapState public float currentFontSize; public float baselineOffset; public float lineOffset; + public bool isDrivenLineSpacing; public float cSpace; public float mSpace; diff --git a/Scripts/Runtime/TMPro_Private.cs b/Scripts/Runtime/TMPro_Private.cs index 12e1964..48e724c 100644 --- a/Scripts/Runtime/TMPro_Private.cs +++ b/Scripts/Runtime/TMPro_Private.cs @@ -30,7 +30,7 @@ public partial class TextMeshPro private bool m_isFirstAllocation; // Flag to determine if this is the first allocation of the buffers. private int m_max_characters = 8; // Determines the initial allocation and size of the character array / buffer. - private int m_max_numberOfLines = 4; // Determines the initial allocation and maximum number of lines of text. + private int m_max_numberOfLines = 4; // Determines the initial allocation and maximum number of lines of text. protected TMP_SubMesh[] m_subTextObjects = new TMP_SubMesh[8]; @@ -252,14 +252,6 @@ protected override void OnValidate() { //Debug.Log("***** OnValidate() called on object ID " + GetInstanceID() + ". *****", this); - // Convert text alignment to independent horizontal and vertical alignment properties - if (m_textAlignment != TextAlignmentOptions.Converted) - { - m_HorizontalAlignment = (HorizontalAlignmentOptions)((int)m_textAlignment & 0xFF); - m_VerticalAlignment = (VerticalAlignmentOptions)((int)m_textAlignment & 0xFF00); - m_textAlignment = TextAlignmentOptions.Converted; - } - if (m_isAwake == false) return; @@ -334,7 +326,7 @@ void ON_RESOURCES_LOADED() // Event received when custom material editor properties are changed. void ON_MATERIAL_PROPERTY_CHANGED(bool isChanged, Material mat) { - //Debug.Log("ON_MATERIAL_PROPERTY_CHANGED event received. Targeted Material is: " + mat.name + " m_sharedMaterial: " + m_sharedMaterial.name + " m_renderer.sharedMaterial: " + m_renderer.sharedMaterial); + //Debug.Log("ON_MATERIAL_PROPERTY_CHANGED event received. Targeted Material is: " + mat.name + " m_sharedMaterial: " + m_sharedMaterial.name + " m_renderer.sharedMaterial: " + m_renderer.sharedMaterial); if (m_renderer.sharedMaterial == null) { @@ -356,7 +348,7 @@ void ON_MATERIAL_PROPERTY_CHANGED(bool isChanged, Material mat) if (m_renderer.sharedMaterial != m_sharedMaterial) // || m_renderer.sharedMaterials.Contains(mat)) { - //Debug.Log("ON_MATERIAL_PROPERTY_CHANGED Called on Target ID: " + GetInstanceID() + ". Previous Material:" + m_sharedMaterial + " New Material:" + m_renderer.sharedMaterial); // on Object ID:" + GetInstanceID() + ". m_sharedMaterial: " + m_sharedMaterial.name + " m_renderer.sharedMaterial: " + m_renderer.sharedMaterial.name); + //Debug.Log("ON_MATERIAL_PROPERTY_CHANGED Called on Target ID: " + GetInstanceID() + ". Previous Material:" + m_sharedMaterial + " New Material:" + m_renderer.sharedMaterial); // on Object ID:" + GetInstanceID() + ". m_sharedMaterial: " + m_sharedMaterial.name + " m_renderer.sharedMaterial: " + m_renderer.sharedMaterial.name); m_sharedMaterial = m_renderer.sharedMaterial; } @@ -366,7 +358,7 @@ void ON_MATERIAL_PROPERTY_CHANGED(bool isChanged, Material mat) UpdateMask(); UpdateEnvMapMatrix(); m_havePropertiesChanged = true; - + SetVerticesDirty(); } @@ -387,7 +379,7 @@ void ON_FONT_PROPERTY_CHANGED(bool isChanged, TMP_FontAsset font) } } - + // Event received when UNDO / REDO Event alters the properties of the object. void ON_TEXTMESHPRO_PROPERTY_CHANGED(bool isChanged, TextMeshPro obj) { @@ -514,7 +506,7 @@ protected override void LoadFontAsset() if (m_renderer.sharedMaterial == null || m_renderer.sharedMaterial.GetTexture(ShaderUtilities.ID_MainTex) == null || m_fontAsset.atlasTexture.GetInstanceID() != m_renderer.sharedMaterial.GetTexture(ShaderUtilities.ID_MainTex).GetInstanceID()) { m_renderer.sharedMaterial = m_fontAsset.material; - m_sharedMaterial = m_fontAsset.material; + m_sharedMaterial = m_fontAsset.material; } else { @@ -640,7 +632,7 @@ void DisableMasking() void UpdateMask() { //Debug.Log("UpdateMask() called."); - + if (!m_isMaskingEnabled) { // Release Masking Material @@ -649,13 +641,13 @@ void UpdateMask() return; } - + if (m_isMaskingEnabled && m_fontMaterial == null) { CreateMaterialInstance(); } - + /* if (!m_isMaskingEnabled) { @@ -670,18 +662,18 @@ void UpdateMask() //else // Debug.Log("Updating Masking..."); */ - + // Compute Masking Coordinates & Softness //float softnessX = Mathf.Min(Mathf.Min(m_textContainer.margins.x, m_textContainer.margins.z), m_sharedMaterial.GetFloat(ShaderUtilities.ID_MaskSoftnessX)); //float softnessY = Mathf.Min(Mathf.Min(m_textContainer.margins.y, m_textContainer.margins.w), m_sharedMaterial.GetFloat(ShaderUtilities.ID_MaskSoftnessY)); //softnessX = softnessX > 0 ? softnessX : 0; //softnessY = softnessY > 0 ? softnessY : 0; - + //float width = (m_textContainer.width - Mathf.Max(m_textContainer.margins.x, 0) - Mathf.Max(m_textContainer.margins.z, 0)) / 2 + softnessX; //float height = (m_textContainer.height - Mathf.Max(m_textContainer.margins.y, 0) - Mathf.Max(m_textContainer.margins.w, 0)) / 2 + softnessY; - - //Vector2 center = new Vector2((0.5f - m_textContainer.pivot.x) * m_textContainer.width + (Mathf.Max(m_textContainer.margins.x, 0) - Mathf.Max(m_textContainer.margins.z, 0)) / 2, (0.5f - m_textContainer.pivot.y) * m_textContainer.height + (- Mathf.Max(m_textContainer.margins.y, 0) + Mathf.Max(m_textContainer.margins.w, 0)) / 2); + + //Vector2 center = new Vector2((0.5f - m_textContainer.pivot.x) * m_textContainer.width + (Mathf.Max(m_textContainer.margins.x, 0) - Mathf.Max(m_textContainer.margins.z, 0)) / 2, (0.5f - m_textContainer.pivot.y) * m_textContainer.height + (- Mathf.Max(m_textContainer.margins.y, 0) + Mathf.Max(m_textContainer.margins.w, 0)) / 2); //Vector4 mask = new Vector4(center.x, center.y, width, height); @@ -689,11 +681,11 @@ void UpdateMask() //m_fontMaterial.SetFloat(ShaderUtilities.ID_MaskSoftnessX, softnessX); //m_fontMaterial.SetFloat(ShaderUtilities.ID_MaskSoftnessY, softnessY); - /* + /* if(m_maskingPropertyBlock == null) - { + { m_maskingPropertyBlock = new MaterialPropertyBlock(); - + //m_maskingPropertyBlock.AddFloat(ShaderUtilities.ID_VertexOffsetX, m_sharedMaterial.GetFloat(ShaderUtilities.ID_VertexOffsetX)); //m_maskingPropertyBlock.AddFloat(ShaderUtilities.ID_VertexOffsetY, m_sharedMaterial.GetFloat(ShaderUtilities.ID_VertexOffsetY)); //Debug.Log("Creating new MaterialPropertyBlock."); @@ -705,7 +697,7 @@ void UpdateMask() m_maskingPropertyBlock.AddVector(ShaderUtilities.ID_MaskCoord, mask); m_maskingPropertyBlock.AddFloat(ShaderUtilities.ID_MaskSoftnessX, softnessX); m_maskingPropertyBlock.AddFloat(ShaderUtilities.ID_MaskSoftnessY, softnessY); - + m_renderer.SetPropertyBlock(m_maskingPropertyBlock); */ } @@ -897,7 +889,7 @@ void CreateMaterialInstance() } - // Sets the Render Queue and Ztest mode + // Sets the Render Queue and Ztest mode protected override void SetShaderDepth() { if (m_isOverlay) @@ -915,7 +907,7 @@ protected override void SetShaderDepth() // Should this use an instanced material? m_sharedMaterial.SetFloat(ShaderUtilities.ShaderTag_ZTestMode, 4); m_renderer.material.renderQueue = -1; - + m_sharedMaterial = m_renderer.material; //Debug.Log("Text set to Normal mode."); } @@ -995,7 +987,7 @@ protected override int SetArraySizes(UnicodeChar[] unicodeChars) m_materialReferenceIndexLookup.Clear(); MaterialReference.AddMaterialReference(m_currentMaterial, m_currentFontAsset, m_materialReferences, m_materialReferenceIndexLookup); - // Set allocations for the text object's TextInfo + // Set allocations for the text object's TextInfo if (m_textInfo == null) m_textInfo = new TMP_TextInfo(m_InternalParsingBufferSize); else if (m_textInfo.characterInfo.Length < m_InternalParsingBufferSize) @@ -1003,6 +995,50 @@ protected override int SetArraySizes(UnicodeChar[] unicodeChars) m_textElementType = TMP_TextElementType.Character; + // Handling for Underline special character + #region Setup Underline Special Character + /* + GetUnderlineSpecialCharacter(m_currentFontAsset); + if (m_Underline.character != null) + { + if (m_Underline.fontAsset.GetInstanceID() != m_currentFontAsset.GetInstanceID()) + { + if (TMP_Settings.matchMaterialPreset && m_currentMaterial.GetInstanceID() != m_Underline.fontAsset.material.GetInstanceID()) + m_Underline.material = TMP_MaterialManager.GetFallbackMaterial(m_currentMaterial, m_Underline.fontAsset.material); + else + m_Underline.material = m_Underline.fontAsset.material; + + m_Underline.materialIndex = MaterialReference.AddMaterialReference(m_Underline.material, m_Underline.fontAsset, m_materialReferences, m_materialReferenceIndexLookup); + m_materialReferences[m_Underline.materialIndex].referenceCount = 0; + } + } + */ + #endregion + + + // Handling for Ellipsis special character + #region Setup Ellipsis Special Character + if (m_overflowMode == TextOverflowModes.Ellipsis) + { + GetEllipsisSpecialCharacter(m_currentFontAsset); + + if (m_Ellipsis.character != null) + { + if (m_Ellipsis.fontAsset.GetInstanceID() != m_currentFontAsset.GetInstanceID()) + { + if (TMP_Settings.matchMaterialPreset && m_currentMaterial.GetInstanceID() != m_Ellipsis.fontAsset.material.GetInstanceID()) + m_Ellipsis.material = TMP_MaterialManager.GetFallbackMaterial(m_currentMaterial, m_Ellipsis.fontAsset.material); + else + m_Ellipsis.material = m_Ellipsis.fontAsset.material; + + m_Ellipsis.materialIndex = MaterialReference.AddMaterialReference(m_Ellipsis.material, m_Ellipsis.fontAsset, m_materialReferences, m_materialReferenceIndexLookup); + m_materialReferences[m_Ellipsis.materialIndex].referenceCount = 0; + } + } + } + #endregion + + // Clear Linked Text object if we have one. if (m_linkedTextComponent != null && !m_isCalculatingPreferredValues) m_linkedTextComponent.text = string.Empty; @@ -1290,7 +1326,7 @@ protected override int SetArraySizes(UnicodeChar[] unicodeChars) // Handle Multi Atlas Texture support if (character != null && character.glyph.atlasIndex > 0) { - m_currentMaterial = TMP_MaterialManager.GetFallbackMaterial( m_currentFontAsset, m_currentMaterial, character.glyph.atlasIndex); + m_currentMaterial = TMP_MaterialManager.GetFallbackMaterial(m_currentFontAsset, m_currentMaterial, character.glyph.atlasIndex); m_currentMaterialIndex = MaterialReference.AddMaterialReference(m_currentMaterial, m_currentFontAsset, m_materialReferences, m_materialReferenceIndexLookup); @@ -1741,7 +1777,7 @@ protected override void GenerateTextMesh() m_startOfLineAscender = 0; m_lineVisibleCharacterCount = 0; bool isStartOfNewLine = true; - bool isDrivenLineSpacing = false; + m_IsDrivenLineSpacing = false; m_firstOverflowCharacterIndex = -1; m_pageNumber = 0; @@ -1767,7 +1803,7 @@ protected override void GenerateTextMesh() m_maxCapHeight = 0; m_maxAscender = 0; m_maxDescender = 0; - float pageAscender = 0; + m_PageAscender = 0; float maxVisibleDescender = 0; bool isMaxVisibleDescenderSet = false; m_isNewPage = false; @@ -1855,17 +1891,18 @@ protected override void GenerateTextMesh() switch (charCode) { case 0x03: - // + m_textInfo.characterInfo[m_characterCount].textElement = m_currentFontAsset.characterLookupTable[0x03]; + m_isTextTruncated = true; break; case 0x2D: - // + // break; case 0x2026: - m_textInfo.characterInfo[m_characterCount].textElement = m_cached_Ellipsis_Character; + m_textInfo.characterInfo[m_characterCount].textElement = m_Ellipsis.character; m_textInfo.characterInfo[m_characterCount].elementType = TMP_TextElementType.Character; - m_textInfo.characterInfo[m_characterCount].fontAsset = m_materialReferences[0].fontAsset; - m_textInfo.characterInfo[m_characterCount].material = m_materialReferences[0].material; - m_textInfo.characterInfo[m_characterCount].materialReferenceIndex = 0; + m_textInfo.characterInfo[m_characterCount].fontAsset = m_Ellipsis.fontAsset; + m_textInfo.characterInfo[m_characterCount].material = m_Ellipsis.material; + m_textInfo.characterInfo[m_characterCount].materialReferenceIndex = m_Ellipsis.materialIndex; // Indicates the source parsing data has been modified. m_isTextTruncated = true; @@ -1931,8 +1968,8 @@ protected override void GenerateTextMesh() #endif float baselineOffset = 0; float spriteScale = 1; - float spriteAscentLine = 0; - float spriteDescentLine = 0; + float elementAscentLine = 0; + float elementDescentLine = 0; if (m_textElementType == TMP_TextElementType.Sprite) { // If a sprite is used as a fallback then get a reference to it and set the color to white. @@ -1951,19 +1988,19 @@ protected override void GenerateTextMesh() // The sprite scale calculations are based on the font asset assigned to the text object. if (m_currentSpriteAsset.m_FaceInfo.pointSize > 0) { - spriteScale = (m_currentFontSize / m_currentSpriteAsset.m_FaceInfo.pointSize * m_currentSpriteAsset.m_FaceInfo.scale * (m_isOrthographic ? 1 : 0.1f)); + spriteScale = m_currentFontSize / m_currentSpriteAsset.m_FaceInfo.pointSize * m_currentSpriteAsset.m_FaceInfo.scale * (m_isOrthographic ? 1 : 0.1f); currentElementScale = sprite.m_Scale * sprite.m_Glyph.scale * spriteScale; - spriteAscentLine = m_currentSpriteAsset.m_FaceInfo.ascentLine; + elementAscentLine = m_currentSpriteAsset.m_FaceInfo.ascentLine; baselineOffset = m_currentSpriteAsset.m_FaceInfo.baseline * m_fontScale * m_fontScaleMultiplier * m_currentSpriteAsset.m_FaceInfo.scale; - spriteDescentLine = m_currentSpriteAsset.m_FaceInfo.descentLine; + elementDescentLine = m_currentSpriteAsset.m_FaceInfo.descentLine; } else { - spriteScale = (m_currentFontSize / m_currentFontAsset.m_FaceInfo.pointSize * m_currentFontAsset.m_FaceInfo.scale * (m_isOrthographic ? 1 : 0.1f)); + spriteScale = m_currentFontSize / m_currentFontAsset.m_FaceInfo.pointSize * m_currentFontAsset.m_FaceInfo.scale * (m_isOrthographic ? 1 : 0.1f); currentElementScale = m_currentFontAsset.m_FaceInfo.ascentLine / sprite.m_Glyph.metrics.height * sprite.m_Scale * sprite.m_Glyph.scale * spriteScale; - spriteAscentLine = m_currentFontAsset.m_FaceInfo.ascentLine; + elementAscentLine = m_currentFontAsset.m_FaceInfo.ascentLine; baselineOffset = m_currentFontAsset.m_FaceInfo.baseline * m_fontScale * m_fontScaleMultiplier * m_currentFontAsset.m_FaceInfo.scale; - spriteDescentLine = m_currentFontAsset.m_FaceInfo.descentLine; + elementDescentLine = m_currentFontAsset.m_FaceInfo.descentLine; } m_cached_TextElement = sprite; @@ -1980,23 +2017,21 @@ protected override void GenerateTextMesh() } else if (m_textElementType == TMP_TextElementType.Character) { - if (isInjectingCharacter) - m_cached_TextElement = m_textInfo.characterInfo[m_characterCount].fontAsset.characterLookupTable[(uint)charCode]; - else - m_cached_TextElement = m_textInfo.characterInfo[m_characterCount].textElement; - + m_cached_TextElement = m_textInfo.characterInfo[m_characterCount].textElement; if (m_cached_TextElement == null) continue; m_currentFontAsset = m_textInfo.characterInfo[m_characterCount].fontAsset; m_currentMaterial = m_textInfo.characterInfo[m_characterCount].material; m_currentMaterialIndex = m_textInfo.characterInfo[m_characterCount].materialReferenceIndex; - // Re-calculate font scale as the font asset may have changed. if (isInjectingCharacter && m_InternalParsingBuffer[i].unicode == 0x0A && m_characterCount != m_firstCharacterOfLine) - m_fontScale = m_textInfo.characterInfo[m_characterCount - 1].scale; + m_fontScale = m_textInfo.characterInfo[m_characterCount - 1].pointSize * smallCapsMultiplier / m_currentFontAsset.m_FaceInfo.pointSize * m_currentFontAsset.m_FaceInfo.scale * (m_isOrthographic ? 1 : 0.1f); else m_fontScale = m_currentFontSize * smallCapsMultiplier / m_currentFontAsset.m_FaceInfo.pointSize * m_currentFontAsset.m_FaceInfo.scale * (m_isOrthographic ? 1 : 0.1f); + elementAscentLine = m_currentFontAsset.m_FaceInfo.ascentLine; + elementDescentLine = m_currentFontAsset.m_FaceInfo.descentLine; + currentElementScale = m_fontScale * m_fontScaleMultiplier * m_cached_TextElement.m_Scale * m_cached_TextElement.m_Glyph.scale; baselineOffset = m_currentFontAsset.m_FaceInfo.baseline * m_fontScale * m_fontScaleMultiplier * m_currentFontAsset.m_FaceInfo.scale; @@ -2229,15 +2264,21 @@ protected override void GenerateTextMesh() #endif float elementAscender = m_textElementType == TMP_TextElementType.Character - ? m_currentFontAsset.m_FaceInfo.ascentLine * currentElementScale / smallCapsMultiplier + m_baselineOffset - : spriteAscentLine * spriteScale + m_baselineOffset; + ? elementAscentLine * currentElementScale / smallCapsMultiplier + m_baselineOffset + : elementAscentLine * spriteScale + m_baselineOffset; + + if (isInjectingCharacter && charCode != 0x03) + elementAscender = m_maxLineAscender; m_textInfo.characterInfo[m_characterCount].ascender = elementAscender - m_lineOffset; // Compute and save text element Descender and maximum line Descender. float elementDescender = m_textElementType == TMP_TextElementType.Character - ? m_currentFontAsset.m_FaceInfo.descentLine * currentElementScale / smallCapsMultiplier + m_baselineOffset - : spriteDescentLine * spriteScale + m_baselineOffset; + ? elementDescentLine * currentElementScale / smallCapsMultiplier + m_baselineOffset + : elementDescentLine * spriteScale + m_baselineOffset; + + if (isInjectingCharacter && charCode != 0x03) + elementDescender = m_maxLineDescender; float elementDescenderII = m_textInfo.characterInfo[m_characterCount].descender = elementDescender - m_lineOffset; @@ -2261,11 +2302,7 @@ protected override void GenerateTextMesh() if (m_lineNumber == 0 || m_isNewPage) { - if (charCode == 0x0A && m_characterCount != m_firstCharacterOfLine) - { - // Skip Line Feed that are not at the start of a line. - } - else + if (charCode != 0x0A || m_characterCount == m_firstCharacterOfLine) { m_maxAscender = m_maxAscender > elementAscender ? m_maxAscender : elementAscender; m_maxCapHeight = Mathf.Max(m_maxCapHeight, m_currentFontAsset.m_FaceInfo.capLine * currentElementScale / smallCapsMultiplier); @@ -2273,7 +2310,10 @@ protected override void GenerateTextMesh() } if (m_lineOffset == 0) - pageAscender = pageAscender > elementAscender ? pageAscender : elementAscender; + { + if (charCode != 0x0A || m_characterCount == m_firstCharacterOfLine) + m_PageAscender = m_PageAscender > elementAscender ? m_PageAscender : elementAscender; + } #if TMP_PROFILE_ON Profiler.EndSample(); @@ -2345,7 +2385,7 @@ protected override void GenerateTextMesh() // Calculate the line breaking width of the text. float textWidth = Mathf.Abs(m_xAdvance) + (!m_isRightToLeft ? currentGlyphMetrics.horizontalAdvance : 0) * (1 - m_charWidthAdjDelta) * (charCode != 0xAD ? currentElementScale : unModifiedScale); - float textHeight = (m_maxAscender - elementDescenderII) + ((m_lineNumber > 0 && isDrivenLineSpacing == false) ? m_maxLineAscender - m_startOfLineAscender : 0); + float textHeight = (m_maxAscender - elementDescenderII) + ((m_lineOffset > 0 && m_IsDrivenLineSpacing == false) ? m_maxLineAscender - m_startOfLineAscender : 0); int testedCharacterCount = m_characterCount; @@ -2362,7 +2402,7 @@ protected override void GenerateTextMesh() { // Handle Line spacing adjustments #region Line Spacing Adjustments - if (m_lineSpacingDelta > m_lineSpacingMax && m_lineNumber > 0 && m_AutoSizeIterationCount < m_AutoSizeMaxIterationCount) + if (m_lineSpacingDelta > m_lineSpacingMax && m_lineOffset > 0 && m_AutoSizeIterationCount < m_AutoSizeMaxIterationCount) { float adjustmentDelta = (marginHeight - textHeight) / m_lineNumber; @@ -2451,15 +2491,30 @@ protected override void GenerateTextMesh() characterToSubstitute.unicode = 0x03; continue; } + else if (m_maxLineAscender - m_maxLineDescender > marginHeight + 0.0001f) + { + // Current line exceeds the height of the text container + // as such we stop on the previous line. + i = RestoreWordWrappingState(ref m_SavedLineState); - // Go back to previous line and re-layout + characterToSubstitute.index = testedCharacterCount; + characterToSubstitute.unicode = 0x03; + continue; + } + + // Go back to previous line and re-layout i = RestoreWordWrappingState(ref m_SavedLineState); m_isNewPage = true; + m_firstCharacterOfLine = m_characterCount; + m_maxLineAscender = k_LargeNegativeFloat; + m_maxLineDescender = k_LargePositiveFloat; + m_startOfLineAscender = 0; + m_xAdvance = 0 + tag_Indent; m_lineOffset = 0; m_maxAscender = 0; - pageAscender = 0; + m_PageAscender = 0; m_lineNumber += 1; m_pageNumber += 1; @@ -2494,7 +2549,7 @@ protected override void GenerateTextMesh() else { lineOffsetDelta = m_lineHeight + m_lineSpacing * currentEmScale; - isDrivenLineSpacing = true; + m_IsDrivenLineSpacing = true; } // Calculate new text height @@ -2694,9 +2749,10 @@ protected override void GenerateTextMesh() InsertNewLine(i, baseScale, currentEmScale, characterSpacingAdjustment, widthOfTextArea, lineGap, ref isMaxVisibleDescenderSet, ref maxVisibleDescender); + m_startOfLineAscender = 0; m_lineOffset = 0; m_maxAscender = 0; - pageAscender = 0; + m_PageAscender = 0; m_pageNumber += 1; isStartOfNewLine = true; @@ -2936,7 +2992,7 @@ protected override void GenerateTextMesh() // Check if Line Spacing of previous line needs to be adjusted. #region Adjust Line Spacing - if (m_lineNumber > 0 && !TMP_Math.Approximately(m_maxLineAscender, m_startOfLineAscender) && /* m_lineHeight == TMP_Math.FLOAT_UNSET && */ isDrivenLineSpacing == false && !m_isNewPage && isInjectingCharacter == false) + if (m_lineOffset > 0 && !TMP_Math.Approximately(m_maxLineAscender, m_startOfLineAscender) && m_IsDrivenLineSpacing == false && !m_isNewPage) { #if TMP_PROFILE_ON Profiler.BeginSample("TMP - Handle Line Spacing Adjustments"); @@ -2949,7 +3005,7 @@ protected override void GenerateTextMesh() m_startOfLineAscender += offsetDelta; m_SavedWordWrapState.lineOffset = m_lineOffset; - m_SavedWordWrapState.previousLineAscender = m_startOfLineAscender; + m_SavedWordWrapState.startOfLineAscender = m_startOfLineAscender; #if TMP_PROFILE_ON Profiler.EndSample(); @@ -2962,23 +3018,22 @@ protected override void GenerateTextMesh() #region Track Potential Insertion Location for Ellipsis if (m_overflowMode == TextOverflowModes.Ellipsis && isInjectingCharacter == false) { - float fontScale = m_currentFontSize / m_fontAsset.m_FaceInfo.pointSize * m_fontAsset.m_FaceInfo.scale * (m_isOrthographic ? 1 : 0.1f); - float scale = fontScale * m_fontScaleMultiplier * m_cached_Ellipsis_Character.m_Scale * m_cached_Ellipsis_Character.m_Glyph.scale; + float fontScale = m_currentFontSize / m_Ellipsis.fontAsset.m_FaceInfo.pointSize * m_Ellipsis.fontAsset.m_FaceInfo.scale * (m_isOrthographic ? 1 : 0.1f); + float scale = fontScale * m_fontScaleMultiplier * m_Ellipsis.character.m_Scale * m_Ellipsis.character.m_Glyph.scale; float marginLeft = m_marginLeft; float marginRight = m_marginRight; // Use the scale and margins of the previous character if Line Feed (LF) is not the first character of a line. if (charCode == 0x0A && m_characterCount != m_firstCharacterOfLine) { - scale = m_textInfo.characterInfo[m_characterCount - 1].scale; + 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); + scale = fontScale * m_fontScaleMultiplier * m_Ellipsis.character.m_Scale * m_Ellipsis.character.m_Glyph.scale; marginLeft = m_textInfo.lineInfo[m_lineNumber].marginLeft; marginRight = m_textInfo.lineInfo[m_lineNumber].marginRight; } - float descender = (m_fontAsset.m_FaceInfo.descentLine * scale) + m_baselineOffset; - float textHeight = m_maxAscender - (descender - m_lineOffset); - - float textWidth = Mathf.Abs(m_xAdvance) + (!m_isRightToLeft ? m_cached_Ellipsis_Character.m_Glyph.metrics.horizontalAdvance : 0) * (1 - m_charWidthAdjDelta) * scale; + float textHeight = m_maxAscender - (m_maxLineDescender - m_lineOffset); + float textWidth = Mathf.Abs(m_xAdvance) + (!m_isRightToLeft ? m_Ellipsis.character.m_Glyph.metrics.horizontalAdvance : 0) * (1 - m_charWidthAdjDelta) * scale; float widthOfTextAreaForEllipsis = m_width != -1 ? Mathf.Min(marginWidth + 0.0001f - marginLeft - marginRight, m_width) : marginWidth + 0.0001f - marginLeft - marginRight; if (textWidth < widthOfTextAreaForEllipsis * (isJustifiedOrFlush ? 1.05f : 1.0f) && textHeight < marginHeight + 0.0001f) @@ -3064,7 +3119,7 @@ protected override void GenerateTextMesh() #endif // Check if Line Spacing of previous line needs to be adjusted. - if (m_lineNumber > 0 && !TMP_Math.Approximately(m_maxLineAscender, m_startOfLineAscender) && isDrivenLineSpacing == false /* && m_lineHeight == TMP_Math.FLOAT_UNSET */ && !m_isNewPage && isInjectingCharacter == false) + if (m_lineOffset > 0 && !TMP_Math.Approximately(m_maxLineAscender, m_startOfLineAscender) && m_IsDrivenLineSpacing == false && !m_isNewPage && isInjectingCharacter == false) { //Debug.Log("Line Feed - Adjusting Line Spacing on line #" + m_lineNumber); float offsetDelta = m_maxLineAscender - m_startOfLineAscender; @@ -3137,12 +3192,12 @@ protected override void GenerateTextMesh() { float lineOffsetDelta = 0 - m_maxLineDescender + lastVisibleAscender + (lineGap + m_lineSpacingDelta) * baseScale + (m_lineSpacing + (charCode == 10 || charCode == 0x2029 ? m_paragraphSpacing : 0)) * currentEmScale; m_lineOffset += lineOffsetDelta; - isDrivenLineSpacing = false; + m_IsDrivenLineSpacing = false; } else { m_lineOffset += m_lineHeight + (m_lineSpacing + (charCode == 10 || charCode == 0x2029 ? m_paragraphSpacing : 0)) * currentEmScale; - isDrivenLineSpacing = true; + m_IsDrivenLineSpacing = true; } m_maxLineAscender = k_LargeNegativeFloat; @@ -3202,7 +3257,7 @@ protected override void GenerateTextMesh() if (m_pageNumber + 1 > m_textInfo.pageInfo.Length) TMP_TextInfo.Resize(ref m_textInfo.pageInfo, m_pageNumber + 1, true); - m_textInfo.pageInfo[m_pageNumber].ascender = pageAscender; + m_textInfo.pageInfo[m_pageNumber].ascender = m_PageAscender; m_textInfo.pageInfo[m_pageNumber].descender = elementDescenderII < m_textInfo.pageInfo[m_pageNumber].descender ? elementDescenderII : m_textInfo.pageInfo[m_pageNumber].descender; @@ -3233,7 +3288,7 @@ protected override void GenerateTextMesh() if ((isWhiteSpace || charCode == 0x200B || charCode == 0x2D || charCode == 0xAD) && (!m_isNonBreakingSpace || ignoreNonBreakingSpace) && charCode != 0xA0 && charCode != 0x2007 && charCode != 0x2011 && charCode != 0x202F && charCode != 0x2060) { - // We store the state of numerous variables for the most recent Space, LineFeed or Carriage Return to enable them to be restored + // We store the state of numerous variables for the most recent Space, LineFeed or Carriage Return to enable them to be restored // for Word Wrapping. SaveWordWrappingState(ref m_SavedWordWrapState, i, m_characterCount); isFirstWordOfLine = false; @@ -3337,7 +3392,7 @@ protected override void GenerateTextMesh() #endif // *** PHASE II of Text Generation *** - int last_vert_index = m_materialReferences[0].referenceCount * (!m_isVolumetricText ? 4 : 8); + int last_vert_index = m_materialReferences[m_Underline.materialIndex].referenceCount * (!m_isVolumetricText ? 4 : 8); // Partial clear of the vertices array to mark unused vertices as degenerate. m_textInfo.meshInfo[0].Clear(false); @@ -3379,12 +3434,12 @@ protected override void GenerateTextMesh() anchorOffset = (corners[0] + corners[1]) / 2 + new Vector3(0 + margins.x, 0, 0); break; - // Midline Vertically + // Midline Vertically case VerticalAlignmentOptions.Geometry: 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); break; - // Capline Vertically + // Capline Vertically case VerticalAlignmentOptions.Capline: anchorOffset = (corners[0] + corners[1]) / 2 + new Vector3(0 + margins.x, 0 - (m_maxCapHeight - margins.y - margins.w) / 2, 0); break; @@ -3690,7 +3745,7 @@ protected override void GenerateTextMesh() characterInfos[i].vertex_BR.uv2.x = PackUV(x1, y0); characterInfos[i].vertex_BR.uv2.y = xScale; #endregion break; - + // SPRITES case TMP_TextElementType.Sprite: // Nothing right now @@ -4160,6 +4215,8 @@ protected override void GenerateTextMesh() } #endregion + // Set vertex count for Underline geometry + //m_textInfo.meshInfo[m_Underline.materialIndex].vertexCount = last_vert_index; // METRICS ABOUT THE TEXT OBJECT m_textInfo.characterCount = m_characterCount; diff --git a/Scripts/Runtime/TMPro_UGUI_Private.cs b/Scripts/Runtime/TMPro_UGUI_Private.cs index 76f50a4..62b4b23 100644 --- a/Scripts/Runtime/TMPro_UGUI_Private.cs +++ b/Scripts/Runtime/TMPro_UGUI_Private.cs @@ -1048,6 +1048,50 @@ protected override int SetArraySizes(UnicodeChar[] unicodeChars) m_textElementType = TMP_TextElementType.Character; + // Handling for Underline special character + #region Setup Underline Special Character + /* + GetUnderlineSpecialCharacter(m_currentFontAsset); + if (m_Underline.character != null) + { + if (m_Underline.fontAsset.GetInstanceID() != m_currentFontAsset.GetInstanceID()) + { + if (TMP_Settings.matchMaterialPreset && m_currentMaterial.GetInstanceID() != m_Underline.fontAsset.material.GetInstanceID()) + m_Underline.material = TMP_MaterialManager.GetFallbackMaterial(m_currentMaterial, m_Underline.fontAsset.material); + else + m_Underline.material = m_Underline.fontAsset.material; + + m_Underline.materialIndex = MaterialReference.AddMaterialReference(m_Underline.material, m_Underline.fontAsset, m_materialReferences, m_materialReferenceIndexLookup); + m_materialReferences[m_Underline.materialIndex].referenceCount = 0; + } + } + */ + #endregion + + + // Handling for Ellipsis special character + #region Setup Ellipsis Special Character + if (m_overflowMode == TextOverflowModes.Ellipsis) + { + GetEllipsisSpecialCharacter(m_currentFontAsset); + + if (m_Ellipsis.character != null) + { + if (m_Ellipsis.fontAsset.GetInstanceID() != m_currentFontAsset.GetInstanceID()) + { + if (TMP_Settings.matchMaterialPreset && m_currentMaterial.GetInstanceID() != m_Ellipsis.fontAsset.material.GetInstanceID()) + m_Ellipsis.material = TMP_MaterialManager.GetFallbackMaterial(m_currentMaterial, m_Ellipsis.fontAsset.material); + else + m_Ellipsis.material = m_Ellipsis.fontAsset.material; + + m_Ellipsis.materialIndex = MaterialReference.AddMaterialReference(m_Ellipsis.material, m_Ellipsis.fontAsset, m_materialReferences, m_materialReferenceIndexLookup); + m_materialReferences[m_Ellipsis.materialIndex].referenceCount = 0; + } + } + } + #endregion + + // Clear Linked Text object if we have one. if (m_linkedTextComponent != null && !m_isCalculatingPreferredValues) { @@ -1077,7 +1121,8 @@ protected override int SetArraySizes(UnicodeChar[] unicodeChars) int tagStartIndex = unicodeChars[i].stringIndex; i = endTagIndex; - if ((m_FontStyleInternal & FontStyles.Bold) == FontStyles.Bold) m_isUsingBold = true; + if ((m_FontStyleInternal & FontStyles.Bold) == FontStyles.Bold) + m_isUsingBold = true; if (m_textElementType == TMP_TextElementType.Sprite) { @@ -1432,7 +1477,7 @@ protected override int SetArraySizes(UnicodeChar[] unicodeChars) m_subTextObjects[i].fallbackSourceMaterial = m_materialReferences[i].fallbackMaterial; } } - + int referenceCount = m_materialReferences[i].referenceCount; // Check to make sure our buffers allocations can accommodate the required text elements. @@ -1531,7 +1576,7 @@ protected override void OnCanvasHierarchyChanged() // Special handling to stop InternalUpdate calls when parent Canvas is disabled. if (m_canvas == null || m_canvas.enabled == false) TMP_UpdateManager.UnRegisterTextObjectForUpdate(this); - else + else if (m_IsTextObjectScaleStatic == false) TMP_UpdateManager.RegisterTextObjectForUpdate(this); } @@ -1737,9 +1782,9 @@ protected override void GenerateTextMesh() // Calculate the scale of the font based on selected font size and sampling point size. // baseScale is calculated using the font asset assigned to the text object. - float baseScale = m_fontScale = (m_fontSize / m_fontAsset.m_FaceInfo.pointSize * m_fontAsset.m_FaceInfo.scale); + float baseScale = m_fontScale = (m_fontSize / m_fontAsset.m_FaceInfo.pointSize * m_fontAsset.m_FaceInfo.scale * (m_isOrthographic ? 1 : 0.1f)); float currentElementScale = baseScale; - float currentEmScale = m_fontSize * 0.01f; + float currentEmScale = m_fontSize * 0.01f * (m_isOrthographic ? 1 : 0.1f); m_fontScaleMultiplier = 1; m_currentFontSize = m_fontSize; @@ -1831,7 +1876,7 @@ protected override void GenerateTextMesh() m_startOfLineAscender = 0; m_lineVisibleCharacterCount = 0; bool isStartOfNewLine = true; - bool isDrivenLineSpacing = false; + m_IsDrivenLineSpacing = false; m_firstOverflowCharacterIndex = -1; m_pageNumber = 0; @@ -1857,7 +1902,7 @@ protected override void GenerateTextMesh() m_maxCapHeight = 0; m_maxAscender = 0; m_maxDescender = 0; - float pageAscender = 0; + m_PageAscender = 0; float maxVisibleDescender = 0; bool isMaxVisibleDescenderSet = false; m_isNewPage = false; @@ -1945,17 +1990,18 @@ protected override void GenerateTextMesh() switch (charCode) { case 0x03: - // + m_textInfo.characterInfo[m_characterCount].textElement = m_currentFontAsset.characterLookupTable[0x03]; + m_isTextTruncated = true; break; case 0x2D: // break; case 0x2026: - m_textInfo.characterInfo[m_characterCount].textElement = m_cached_Ellipsis_Character; + m_textInfo.characterInfo[m_characterCount].textElement = m_Ellipsis.character; m_textInfo.characterInfo[m_characterCount].elementType = TMP_TextElementType.Character; - m_textInfo.characterInfo[m_characterCount].fontAsset = m_materialReferences[0].fontAsset; - m_textInfo.characterInfo[m_characterCount].material = m_materialReferences[0].material; - m_textInfo.characterInfo[m_characterCount].materialReferenceIndex = 0; + m_textInfo.characterInfo[m_characterCount].fontAsset = m_Ellipsis.fontAsset; + m_textInfo.characterInfo[m_characterCount].material = m_Ellipsis.material; + m_textInfo.characterInfo[m_characterCount].materialReferenceIndex = m_Ellipsis.materialIndex; // Indicates the source parsing data has been modified. m_isTextTruncated = true; @@ -2021,8 +2067,8 @@ protected override void GenerateTextMesh() #endif float baselineOffset = 0; float spriteScale = 1; - float spriteAscentLine = 0; - float spriteDescentLine = 0; + float elementAscentLine = 0; + float elementDescentLine = 0; if (m_textElementType == TMP_TextElementType.Sprite) { // If a sprite is used as a fallback then get a reference to it and set the color to white. @@ -2041,19 +2087,19 @@ protected override void GenerateTextMesh() // The sprite scale calculations are based on the font asset assigned to the text object. if (m_currentSpriteAsset.m_FaceInfo.pointSize > 0) { - spriteScale = (m_currentFontSize / m_currentSpriteAsset.m_FaceInfo.pointSize * m_currentSpriteAsset.m_FaceInfo.scale); + spriteScale = m_currentFontSize / m_currentSpriteAsset.m_FaceInfo.pointSize * m_currentSpriteAsset.m_FaceInfo.scale * (m_isOrthographic ? 1 : 0.1f); currentElementScale = sprite.m_Scale * sprite.m_Glyph.scale * spriteScale; - spriteAscentLine = m_currentSpriteAsset.m_FaceInfo.ascentLine; + elementAscentLine = m_currentSpriteAsset.m_FaceInfo.ascentLine; baselineOffset = m_currentSpriteAsset.m_FaceInfo.baseline * m_fontScale * m_fontScaleMultiplier * m_currentSpriteAsset.m_FaceInfo.scale; - spriteDescentLine = m_currentSpriteAsset.m_FaceInfo.descentLine; + elementDescentLine = m_currentSpriteAsset.m_FaceInfo.descentLine; } else { - spriteScale = (m_currentFontSize / m_currentFontAsset.m_FaceInfo.pointSize * m_currentFontAsset.m_FaceInfo.scale); + spriteScale = m_currentFontSize / m_currentFontAsset.m_FaceInfo.pointSize * m_currentFontAsset.m_FaceInfo.scale * (m_isOrthographic ? 1 : 0.1f); currentElementScale = m_currentFontAsset.m_FaceInfo.ascentLine / sprite.m_Glyph.metrics.height * sprite.m_Scale * sprite.m_Glyph.scale * spriteScale; - spriteAscentLine = m_currentFontAsset.m_FaceInfo.ascentLine; + elementAscentLine = m_currentFontAsset.m_FaceInfo.ascentLine; baselineOffset = m_currentFontAsset.m_FaceInfo.baseline * m_fontScale * m_fontScaleMultiplier * m_currentFontAsset.m_FaceInfo.scale; - spriteDescentLine = m_currentFontAsset.m_FaceInfo.descentLine; + elementDescentLine = m_currentFontAsset.m_FaceInfo.descentLine; } m_cached_TextElement = sprite; @@ -2070,23 +2116,21 @@ protected override void GenerateTextMesh() } else if (m_textElementType == TMP_TextElementType.Character) { - if (isInjectingCharacter) - m_cached_TextElement = m_textInfo.characterInfo[m_characterCount].fontAsset.characterLookupTable[(uint)charCode]; - else - m_cached_TextElement = m_textInfo.characterInfo[m_characterCount].textElement; - + m_cached_TextElement = m_textInfo.characterInfo[m_characterCount].textElement; if (m_cached_TextElement == null) continue; m_currentFontAsset = m_textInfo.characterInfo[m_characterCount].fontAsset; m_currentMaterial = m_textInfo.characterInfo[m_characterCount].material; m_currentMaterialIndex = m_textInfo.characterInfo[m_characterCount].materialReferenceIndex; - // Re-calculate font scale as the font asset may have changed. if (isInjectingCharacter && m_InternalParsingBuffer[i].unicode == 0x0A && m_characterCount != m_firstCharacterOfLine) - m_fontScale = m_textInfo.characterInfo[m_characterCount - 1].scale; + m_fontScale = m_textInfo.characterInfo[m_characterCount - 1].pointSize * smallCapsMultiplier / m_currentFontAsset.m_FaceInfo.pointSize * m_currentFontAsset.m_FaceInfo.scale * (m_isOrthographic ? 1 : 0.1f); else m_fontScale = m_currentFontSize * smallCapsMultiplier / m_currentFontAsset.m_FaceInfo.pointSize * m_currentFontAsset.m_FaceInfo.scale * (m_isOrthographic ? 1 : 0.1f); + elementAscentLine = m_currentFontAsset.m_FaceInfo.ascentLine; + elementDescentLine = m_currentFontAsset.m_FaceInfo.descentLine; + currentElementScale = m_fontScale * m_fontScaleMultiplier * m_cached_TextElement.m_Scale * m_cached_TextElement.m_Glyph.scale; baselineOffset = m_currentFontAsset.m_FaceInfo.baseline * m_fontScale * m_fontScaleMultiplier * m_currentFontAsset.m_FaceInfo.scale; @@ -2319,15 +2363,21 @@ protected override void GenerateTextMesh() #endif float elementAscender = m_textElementType == TMP_TextElementType.Character - ? m_currentFontAsset.m_FaceInfo.ascentLine * currentElementScale / smallCapsMultiplier + m_baselineOffset - : spriteAscentLine * spriteScale + m_baselineOffset; + ? elementAscentLine * currentElementScale / smallCapsMultiplier + m_baselineOffset + : elementAscentLine * spriteScale + m_baselineOffset; + + if (isInjectingCharacter && charCode != 0x03) + elementAscender = m_maxLineAscender; m_textInfo.characterInfo[m_characterCount].ascender = elementAscender - m_lineOffset; // Compute and save text element Descender and maximum line Descender. float elementDescender = m_textElementType == TMP_TextElementType.Character - ? m_currentFontAsset.m_FaceInfo.descentLine * currentElementScale / smallCapsMultiplier + m_baselineOffset - : spriteDescentLine * spriteScale + m_baselineOffset; + ? elementDescentLine * currentElementScale / smallCapsMultiplier + m_baselineOffset + : elementDescentLine * spriteScale + m_baselineOffset; + + if (isInjectingCharacter && charCode != 0x03) + elementDescender = m_maxLineDescender; float elementDescenderII = m_textInfo.characterInfo[m_characterCount].descender = elementDescender - m_lineOffset; @@ -2351,11 +2401,7 @@ protected override void GenerateTextMesh() if (m_lineNumber == 0 || m_isNewPage) { - if (charCode == 0x0A && m_characterCount != m_firstCharacterOfLine) - { - // Skip Line Feed that are not at the start of a line. - } - else + if (charCode != 0x0A || m_characterCount == m_firstCharacterOfLine) { m_maxAscender = m_maxAscender > elementAscender ? m_maxAscender : elementAscender; m_maxCapHeight = Mathf.Max(m_maxCapHeight, m_currentFontAsset.m_FaceInfo.capLine * currentElementScale / smallCapsMultiplier); @@ -2363,7 +2409,10 @@ protected override void GenerateTextMesh() } if (m_lineOffset == 0) - pageAscender = pageAscender > elementAscender ? pageAscender : elementAscender; + { + if (charCode != 0x0A || m_characterCount == m_firstCharacterOfLine) + m_PageAscender = m_PageAscender > elementAscender ? m_PageAscender : elementAscender; + } #if TMP_PROFILE_ON Profiler.EndSample(); @@ -2435,7 +2484,7 @@ protected override void GenerateTextMesh() // Calculate the line breaking width of the text. float textWidth = Mathf.Abs(m_xAdvance) + (!m_isRightToLeft ? currentGlyphMetrics.horizontalAdvance : 0) * (1 - m_charWidthAdjDelta) * (charCode != 0xAD ? currentElementScale : unModifiedScale); - float textHeight = (m_maxAscender - elementDescenderII) + ((m_lineNumber > 0 && isDrivenLineSpacing == false) ? m_maxLineAscender - m_startOfLineAscender : 0); + float textHeight = (m_maxAscender - elementDescenderII) + ((m_lineOffset > 0 && m_IsDrivenLineSpacing == false) ? m_maxLineAscender - m_startOfLineAscender : 0); int testedCharacterCount = m_characterCount; @@ -2452,7 +2501,7 @@ protected override void GenerateTextMesh() { // Handle Line spacing adjustments #region Line Spacing Adjustments - if (m_lineSpacingDelta > m_lineSpacingMax && m_lineNumber > 0 && m_AutoSizeIterationCount < m_AutoSizeMaxIterationCount) + if (m_lineSpacingDelta > m_lineSpacingMax && m_lineOffset > 0 && m_AutoSizeIterationCount < m_AutoSizeMaxIterationCount) { float adjustmentDelta = (marginHeight - textHeight) / m_lineNumber; @@ -2541,15 +2590,30 @@ protected override void GenerateTextMesh() characterToSubstitute.unicode = 0x03; continue; } + else if (m_maxLineAscender - m_maxLineDescender > marginHeight + 0.0001f) + { + // Current line exceeds the height of the text container + // as such we stop on the previous line. + i = RestoreWordWrappingState(ref m_SavedLineState); + + characterToSubstitute.index = testedCharacterCount; + characterToSubstitute.unicode = 0x03; + continue; + } // Go back to previous line and re-layout i = RestoreWordWrappingState(ref m_SavedLineState); m_isNewPage = true; + m_firstCharacterOfLine = m_characterCount; + m_maxLineAscender = k_LargeNegativeFloat; + m_maxLineDescender = k_LargePositiveFloat; + m_startOfLineAscender = 0; + m_xAdvance = 0 + tag_Indent; m_lineOffset = 0; m_maxAscender = 0; - pageAscender = 0; + m_PageAscender = 0; m_lineNumber += 1; m_pageNumber += 1; @@ -2584,7 +2648,7 @@ protected override void GenerateTextMesh() else { lineOffsetDelta = m_lineHeight + m_lineSpacing * currentEmScale; - isDrivenLineSpacing = true; + m_IsDrivenLineSpacing = true; } // Calculate new text height @@ -2784,9 +2848,10 @@ protected override void GenerateTextMesh() InsertNewLine(i, baseScale, currentEmScale, characterSpacingAdjustment, widthOfTextArea, lineGap, ref isMaxVisibleDescenderSet, ref maxVisibleDescender); + m_startOfLineAscender = 0; m_lineOffset = 0; m_maxAscender = 0; - pageAscender = 0; + m_PageAscender = 0; m_pageNumber += 1; isStartOfNewLine = true; @@ -3026,7 +3091,7 @@ protected override void GenerateTextMesh() // Check if Line Spacing of previous line needs to be adjusted. #region Adjust Line Spacing - if (m_lineNumber > 0 && !TMP_Math.Approximately(m_maxLineAscender, m_startOfLineAscender) && /* m_lineHeight == TMP_Math.FLOAT_UNSET && */ isDrivenLineSpacing == false && !m_isNewPage && isInjectingCharacter == false) + if (m_lineOffset > 0 && !TMP_Math.Approximately(m_maxLineAscender, m_startOfLineAscender) && m_IsDrivenLineSpacing == false && !m_isNewPage) { #if TMP_PROFILE_ON Profiler.BeginSample("TMP - Handle Line Spacing Adjustments"); @@ -3039,7 +3104,7 @@ protected override void GenerateTextMesh() m_startOfLineAscender += offsetDelta; m_SavedWordWrapState.lineOffset = m_lineOffset; - m_SavedWordWrapState.previousLineAscender = m_startOfLineAscender; + m_SavedWordWrapState.startOfLineAscender = m_startOfLineAscender; #if TMP_PROFILE_ON Profiler.EndSample(); @@ -3052,23 +3117,22 @@ protected override void GenerateTextMesh() #region Track Potential Insertion Location for Ellipsis if (m_overflowMode == TextOverflowModes.Ellipsis && isInjectingCharacter == false) { - float fontScale = m_currentFontSize / m_fontAsset.m_FaceInfo.pointSize * m_fontAsset.m_FaceInfo.scale * (m_isOrthographic ? 1 : 0.1f); - float scale = fontScale * m_fontScaleMultiplier * m_cached_Ellipsis_Character.m_Scale * m_cached_Ellipsis_Character.m_Glyph.scale; + float fontScale = m_currentFontSize / m_Ellipsis.fontAsset.m_FaceInfo.pointSize * m_Ellipsis.fontAsset.m_FaceInfo.scale * (m_isOrthographic ? 1 : 0.1f); + float scale = fontScale * m_fontScaleMultiplier * m_Ellipsis.character.m_Scale * m_Ellipsis.character.m_Glyph.scale; float marginLeft = m_marginLeft; float marginRight = m_marginRight; // Use the scale and margins of the previous character if Line Feed (LF) is not the first character of a line. if (charCode == 0x0A && m_characterCount != m_firstCharacterOfLine) { - scale = m_textInfo.characterInfo[m_characterCount - 1].scale; + 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); + scale = fontScale * m_fontScaleMultiplier * m_Ellipsis.character.m_Scale * m_Ellipsis.character.m_Glyph.scale; marginLeft = m_textInfo.lineInfo[m_lineNumber].marginLeft; marginRight = m_textInfo.lineInfo[m_lineNumber].marginRight; } - float descender = (m_fontAsset.m_FaceInfo.descentLine * scale) + m_baselineOffset; - float textHeight = m_maxAscender - (descender - m_lineOffset); - - float textWidth = Mathf.Abs(m_xAdvance) + (!m_isRightToLeft ? m_cached_Ellipsis_Character.m_Glyph.metrics.horizontalAdvance : 0) * (1 - m_charWidthAdjDelta) * scale; + float textHeight = m_maxAscender - (m_maxLineDescender - m_lineOffset); + float textWidth = Mathf.Abs(m_xAdvance) + (!m_isRightToLeft ? m_Ellipsis.character.m_Glyph.metrics.horizontalAdvance : 0) * (1 - m_charWidthAdjDelta) * scale; float widthOfTextAreaForEllipsis = m_width != -1 ? Mathf.Min(marginWidth + 0.0001f - marginLeft - marginRight, m_width) : marginWidth + 0.0001f - marginLeft - marginRight; if (textWidth < widthOfTextAreaForEllipsis * (isJustifiedOrFlush ? 1.05f : 1.0f) && textHeight < marginHeight + 0.0001f) @@ -3154,7 +3218,7 @@ protected override void GenerateTextMesh() #endif // Check if Line Spacing of previous line needs to be adjusted. - if (m_lineNumber > 0 && !TMP_Math.Approximately(m_maxLineAscender, m_startOfLineAscender) && isDrivenLineSpacing == false /* && m_lineHeight == TMP_Math.FLOAT_UNSET */ && !m_isNewPage && isInjectingCharacter == false) + if (m_lineOffset > 0 && !TMP_Math.Approximately(m_maxLineAscender, m_startOfLineAscender) && m_IsDrivenLineSpacing == false && !m_isNewPage && isInjectingCharacter == false) { //Debug.Log("Line Feed - Adjusting Line Spacing on line #" + m_lineNumber); float offsetDelta = m_maxLineAscender - m_startOfLineAscender; @@ -3227,12 +3291,12 @@ protected override void GenerateTextMesh() { float lineOffsetDelta = 0 - m_maxLineDescender + lastVisibleAscender + (lineGap + m_lineSpacingDelta) * baseScale + (m_lineSpacing + (charCode == 10 || charCode == 0x2029 ? m_paragraphSpacing : 0)) * currentEmScale; m_lineOffset += lineOffsetDelta; - isDrivenLineSpacing = false; + m_IsDrivenLineSpacing = false; } else { m_lineOffset += m_lineHeight + (m_lineSpacing + (charCode == 10 || charCode == 0x2029 ? m_paragraphSpacing : 0)) * currentEmScale; - isDrivenLineSpacing = true; + m_IsDrivenLineSpacing = true; } m_maxLineAscender = k_LargeNegativeFloat; @@ -3292,7 +3356,7 @@ protected override void GenerateTextMesh() if (m_pageNumber + 1 > m_textInfo.pageInfo.Length) TMP_TextInfo.Resize(ref m_textInfo.pageInfo, m_pageNumber + 1, true); - m_textInfo.pageInfo[m_pageNumber].ascender = pageAscender; + m_textInfo.pageInfo[m_pageNumber].ascender = m_PageAscender; m_textInfo.pageInfo[m_pageNumber].descender = elementDescenderII < m_textInfo.pageInfo[m_pageNumber].descender ? elementDescenderII : m_textInfo.pageInfo[m_pageNumber].descender; @@ -3427,7 +3491,7 @@ protected override void GenerateTextMesh() #endif // *** PHASE II of Text Generation *** - int last_vert_index = m_materialReferences[0].referenceCount * 4; + int last_vert_index = m_materialReferences[m_Underline.materialIndex].referenceCount * 4; // Partial clear of the vertices array to mark unused vertices as degenerate. m_textInfo.meshInfo[0].Clear(false); @@ -4275,6 +4339,9 @@ protected override void GenerateTextMesh() m_textInfo.wordCount = wordCount != 0 && m_characterCount > 0 ? wordCount : 1; m_textInfo.pageCount = m_pageNumber + 1; + // Set vertex count for Underline geometry + //m_textInfo.meshInfo[m_Underline.materialIndex].vertexCount = last_vert_index; + #if TMP_PROFILE_ON // End Sampling of Phase II Profiler.EndSample(); diff --git a/package.json b/package.json index ef2fa30..bd2fb28 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "com.unity.textmeshpro", "displayName": "TextMeshPro", - "version": "2.1.0-preview.4", + "version": "2.1.0-preview.5", "unity": "2019.1", "description": "TextMeshPro is the ultimate text solution for Unity. It's the perfect replacement for Unity's UI Text and the legacy Text Mesh.\n\nPowerful and easy to use, TextMeshPro (also known as TMP) uses Advanced Text Rendering techniques along with a set of custom shaders; delivering substantial visual quality improvements while giving users incredible flexibility when it comes to text styling and texturing.\n\nTextMeshPro provides Improved Control over text formatting and layout with features like character, word, line and paragraph spacing, kerning, justified text, Links, over 30 Rich Text Tags available, support for Multi Font & Sprites, Custom Styles and more.\n\nGreat performance. Since the geometry created by TextMeshPro uses two triangles per character just like Unity's text components, this improved visual quality and flexibility comes at no additional performance cost.", "keywords": [ @@ -16,8 +16,9 @@ "com.unity.ugui": "1.0.0" }, "repository": { + "footprint": "91fba2dd57fa1b3fcfc375dca268ff4078995fe6", "type": "git", "url": "https://github.cds.internal.unity3d.com/unity/com.unity.textmeshpro.git", - "revision": "f8a42cdb8875215e5cf9acca049b0bc3f06ea1c4" + "revision": "840c4dc6d0accc479ceee2bab01574e4b8e5945b" } }