├── Editor.meta ├── Editor ├── CodeWriter.TMP-Image.Editor.asmdef ├── CodeWriter.TMP-Image.Editor.asmdef.meta ├── TMPImageEditor.cs └── TMPImageEditor.cs.meta ├── LICENSE.md ├── LICENSE.md.meta ├── README.md ├── README.md.meta ├── Runtime.meta ├── Runtime ├── CodeWriter.TMP-Image.asmdef ├── CodeWriter.TMP-Image.asmdef.meta ├── TMPImage.cs └── TMPImage.cs.meta ├── Unity.TextMeshPro.meta ├── Unity.TextMeshPro ├── TMPImage_TMP_SpriteAssetProxy.cs ├── TMPImage_TMP_SpriteAssetProxy.cs.meta ├── Unity.TextMeshPro.asmref └── Unity.TextMeshPro.asmref.meta ├── UnityEditor.UI.meta ├── UnityEditor.UI ├── TMPImage_SpriteDrawUtilityProxy.cs ├── TMPImage_SpriteDrawUtilityProxy.cs.meta ├── UnityEditor.UI.asmref └── UnityEditor.UI.asmref.meta ├── package.json └── package.json.meta /Editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8d15a8f9afe16114c927f1661a16c00d 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/CodeWriter.TMP-Image.Editor.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "CodeWriter.TMP-Image.Editor", 3 | "rootNamespace": "", 4 | "references": [ 5 | "CodeWriter.TMP-Image", 6 | "Unity.TextMeshPro" 7 | ], 8 | "includePlatforms": [ 9 | "Editor" 10 | ], 11 | "excludePlatforms": [], 12 | "allowUnsafeCode": false, 13 | "overrideReferences": false, 14 | "precompiledReferences": [], 15 | "autoReferenced": false, 16 | "defineConstraints": [], 17 | "versionDefines": [], 18 | "noEngineReferences": false 19 | } -------------------------------------------------------------------------------- /Editor/CodeWriter.TMP-Image.Editor.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 483d5912e57d90b4698bc94932af8c9b 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Editor/TMPImageEditor.cs: -------------------------------------------------------------------------------- 1 | using UnityEditor; 2 | using UnityEditor.UI; 3 | using UnityEngine; 4 | 5 | namespace CodeWriter.UI 6 | { 7 | [CustomEditor(typeof(TMPImage), true)] 8 | [CanEditMultipleObjects] 9 | public class TmpImageEditor : GraphicEditor 10 | { 11 | private SerializedProperty m_SpriteName; 12 | private SerializedProperty m_PreserveAspect; 13 | 14 | protected override void OnEnable() 15 | { 16 | base.OnEnable(); 17 | 18 | m_SpriteName = serializedObject.FindProperty("m_SpriteName"); 19 | m_PreserveAspect = serializedObject.FindProperty("m_PreserveAspect"); 20 | 21 | SetShowNativeSize(true); 22 | } 23 | 24 | public override void OnInspectorGUI() 25 | { 26 | serializedObject.Update(); 27 | 28 | EditorGUILayout.PropertyField(m_SpriteName); 29 | 30 | AppearanceControlsGUI(); 31 | RaycastControlsGUI(); 32 | MaskableControlsGUI(); 33 | EditorGUILayout.PropertyField(m_PreserveAspect); 34 | SetShowNativeSize(false); 35 | NativeSizeButtonGUI(); 36 | 37 | serializedObject.ApplyModifiedProperties(); 38 | } 39 | 40 | void SetShowNativeSize(bool instant) 41 | { 42 | base.SetShowNativeSize(true, instant); 43 | } 44 | 45 | private static Rect Outer(TMPImage tmpImage) 46 | { 47 | var outer = tmpImage.uvRect; 48 | outer.xMin *= tmpImage.rectTransform.rect.width; 49 | outer.xMax *= tmpImage.rectTransform.rect.width; 50 | outer.yMin *= tmpImage.rectTransform.rect.height; 51 | outer.yMax *= tmpImage.rectTransform.rect.height; 52 | return outer; 53 | } 54 | 55 | public override bool HasPreviewGUI() 56 | { 57 | var tmpImage = target as TMPImage; 58 | if (tmpImage == null) 59 | { 60 | return false; 61 | } 62 | 63 | return true; 64 | } 65 | 66 | public override void OnPreviewGUI(Rect rect, GUIStyle background) 67 | { 68 | var tmpImage = target as TMPImage; 69 | if (tmpImage == null) 70 | { 71 | return; 72 | } 73 | 74 | var tex = tmpImage.mainTexture; 75 | if (tex == null) 76 | { 77 | return; 78 | } 79 | 80 | var outer = Outer(tmpImage); 81 | TMPImage_SpriteDrawUtilityProxy.DrawSprite(tex, rect, outer, tmpImage.uvRect, 82 | tmpImage.canvasRenderer.GetColor()); 83 | } 84 | 85 | public override string GetInfoString() 86 | { 87 | var tmpImage = target as TMPImage; 88 | if (tmpImage == null) 89 | { 90 | return string.Empty; 91 | } 92 | 93 | var text = string.Format("TMPImage Size: {0}x{1}", 94 | Mathf.RoundToInt(Mathf.Abs(tmpImage.rectTransform.rect.width)), 95 | Mathf.RoundToInt(Mathf.Abs(tmpImage.rectTransform.rect.height))); 96 | 97 | return text; 98 | } 99 | } 100 | } -------------------------------------------------------------------------------- /Editor/TMPImageEditor.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 308fe03f7943d6f4e9b4cef941d70cad 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 CodeWriter Packages 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LICENSE.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 36e18fd91e49ff74bbd5e761aca36cc8 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TMP-Image [![Github license](https://img.shields.io/github/license/codewriter-packages/TMP-Image.svg?style=flat-square)](#) [![Unity 2021.3](https://img.shields.io/badge/Unity-2021.3+-2296F3.svg?style=flat-square)](#) ![GitHub package.json version](https://img.shields.io/github/package-json/v/codewriter-packages/TMP-Image?style=flat-square) 2 | 3 | ## :thought_balloon: Motivation 4 | 5 | I like the way to use sprites in the [Text Mesh Pro](https://docs.unity3d.com/Packages/com.unity.ugui@2.0) by their name (e.g. ``). It would be very nice to be able to draw exactly the same sprites from the same atlases directly in UGUI. So I made a `TMPImage` - replacement for `Image` that able to do it 6 | 7 | ## :rocket: How to use? 8 | 9 | Just add a `TMP Image` component to the GameObject and specify the sprite name, and sprite will be fetched from Text Mesh Pro and displayed. Simple 10 | 11 | ## :open_book: How to Install 12 | 13 | Library distributed as git package ([How to install package from git URL](https://docs.unity3d.com/Manual/upm-ui-giturl.html)) 14 |
Git URL: `https://github.com/codewriter-packages/TMP-Image.git` 15 | 16 | ## :green_book: License 17 | 18 | TMP-Image is [MIT licensed](./LICENSE.md). 19 | -------------------------------------------------------------------------------- /README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 37edc318858c37141bf6e9cf24df7611 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Runtime.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: bf6ffe53a28938f4b8f7affe85aa1382 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/CodeWriter.TMP-Image.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "CodeWriter.TMP-Image", 3 | "rootNamespace": "", 4 | "references": [ 5 | "Unity.TextMeshPro" 6 | ], 7 | "includePlatforms": [], 8 | "excludePlatforms": [], 9 | "allowUnsafeCode": false, 10 | "overrideReferences": false, 11 | "precompiledReferences": [], 12 | "autoReferenced": true, 13 | "defineConstraints": [], 14 | "versionDefines": [ 15 | { 16 | "name": "com.unity.ugui", 17 | "expression": "2.0.0", 18 | "define": "UGUI_2_0" 19 | } 20 | ], 21 | "noEngineReferences": false 22 | } -------------------------------------------------------------------------------- /Runtime/CodeWriter.TMP-Image.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5f7c12c426bec7b4e81e2f1cb7620b13 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Runtime/TMPImage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using JetBrains.Annotations; 3 | using TMPro; 4 | using UnityEngine; 5 | using UnityEngine.Scripting; 6 | using UnityEngine.TextCore; 7 | using UnityEngine.UI; 8 | 9 | namespace CodeWriter.UI 10 | { 11 | [RequireComponent(typeof(RectTransform))] 12 | [RequireComponent(typeof(CanvasRenderer))] 13 | [AddComponentMenu("UI/TMP Image", 13)] 14 | public class TMPImage : MaskableGraphic 15 | { 16 | [SerializeField] private string m_SpriteName = string.Empty; 17 | [SerializeField] private bool m_PreserveAspect = false; 18 | 19 | [NonSerialized] private TMP_SpriteAsset m_CurrentSpriteAsset; 20 | 21 | [Preserve] 22 | public TMPImage() 23 | { 24 | useLegacyMeshGeneration = false; 25 | } 26 | 27 | [PublicAPI] 28 | public string spriteName 29 | { 30 | get => m_SpriteName; 31 | set 32 | { 33 | if (m_SpriteName == value) 34 | { 35 | return; 36 | } 37 | 38 | m_SpriteName = value ?? string.Empty; 39 | 40 | SetVerticesDirty(); 41 | SetMaterialDirty(); 42 | } 43 | } 44 | 45 | public bool preserveAspect 46 | { 47 | get => m_PreserveAspect; 48 | set 49 | { 50 | if (m_PreserveAspect == value) 51 | { 52 | return; 53 | } 54 | 55 | m_PreserveAspect = value; 56 | 57 | SetVerticesDirty(); 58 | } 59 | } 60 | 61 | public Rect uvRect 62 | { 63 | get 64 | { 65 | if (TrySearchAtlasWithUv(ref m_CurrentSpriteAsset, spriteName, out _, out var uv)) 66 | { 67 | return uv; 68 | } 69 | 70 | return new Rect(0, 0, 1, 1); 71 | } 72 | } 73 | 74 | public override Texture mainTexture 75 | { 76 | get 77 | { 78 | if (TrySearchAtlasWithUv(ref m_CurrentSpriteAsset, spriteName, out var texture, out _)) 79 | { 80 | return texture; 81 | } 82 | 83 | return s_WhiteTexture; 84 | } 85 | } 86 | 87 | public override void SetNativeSize() 88 | { 89 | var tex = mainTexture; 90 | if (tex != null && tex != s_WhiteTexture) 91 | { 92 | var w = Mathf.RoundToInt(tex.width * uvRect.width); 93 | var h = Mathf.RoundToInt(tex.height * uvRect.height); 94 | rectTransform.anchorMax = rectTransform.anchorMin; 95 | rectTransform.sizeDelta = new Vector2(w, h); 96 | } 97 | } 98 | 99 | protected override void OnPopulateMesh(VertexHelper vh) 100 | { 101 | vh.Clear(); 102 | 103 | if (!TrySearchAtlasWithUv(ref m_CurrentSpriteAsset, spriteName, out var texture, out var uv)) 104 | { 105 | return; 106 | } 107 | 108 | var r = GetPixelAdjustedRect(); 109 | 110 | if (preserveAspect) 111 | { 112 | var spriteSize = Vector2.Scale(uv.size, new Vector2(texture.width, texture.height)); 113 | PreserveSpriteAspectRatio(ref r, spriteSize); 114 | } 115 | 116 | var v = new Vector4(r.x, r.y, r.x + r.width, r.y + r.height); 117 | var scaleX = texture.width * texture.texelSize.x; 118 | var scaleY = texture.height * texture.texelSize.y; 119 | { 120 | var color32 = (Color32) color; 121 | vh.AddVert(new Vector3(v.x, v.y), color32, new Vector2(uv.xMin * scaleX, uv.yMin * scaleY)); 122 | vh.AddVert(new Vector3(v.x, v.w), color32, new Vector2(uv.xMin * scaleX, uv.yMax * scaleY)); 123 | vh.AddVert(new Vector3(v.z, v.w), color32, new Vector2(uv.xMax * scaleX, uv.yMax * scaleY)); 124 | vh.AddVert(new Vector3(v.z, v.y), color32, new Vector2(uv.xMax * scaleX, uv.yMin * scaleY)); 125 | 126 | vh.AddTriangle(0, 1, 2); 127 | vh.AddTriangle(2, 3, 0); 128 | } 129 | } 130 | 131 | protected override void OnDidApplyAnimationProperties() 132 | { 133 | SetMaterialDirty(); 134 | SetVerticesDirty(); 135 | SetRaycastDirty(); 136 | } 137 | 138 | private void PreserveSpriteAspectRatio(ref Rect rect, Vector2 spriteSize) 139 | { 140 | var spriteRatio = spriteSize.x / spriteSize.y; 141 | var rectRatio = rect.width / rect.height; 142 | 143 | if (spriteRatio > rectRatio) 144 | { 145 | var oldHeight = rect.height; 146 | rect.height = rect.width * (1.0f / spriteRatio); 147 | rect.y += (oldHeight - rect.height) * rectTransform.pivot.y; 148 | } 149 | else 150 | { 151 | var oldWidth = rect.width; 152 | rect.width = rect.height * spriteRatio; 153 | rect.x += (oldWidth - rect.width) * rectTransform.pivot.x; 154 | } 155 | } 156 | 157 | private static bool TrySearchAtlasWithUv(ref TMP_SpriteAsset currentSpriteAsset, string spriteName, 158 | out Texture texture, out Rect uvRect) 159 | { 160 | #if UGUI_2_0 161 | var spriteNameHash = TMP_TextUtilities.GetHashCode(spriteName); 162 | #else 163 | var spriteNameHash = TMP_TextParsingUtilities.GetHashCode(spriteName); 164 | #endif 165 | 166 | currentSpriteAsset = TMP_SpriteAsset.SearchForSpriteByHashCode(currentSpriteAsset, 167 | spriteNameHash, true, out var spriteIndex); 168 | 169 | if (spriteIndex == -1) 170 | { 171 | currentSpriteAsset = TMP_SpriteAsset.SearchForSpriteByHashCode(TMP_Settings.defaultSpriteAsset, 172 | spriteNameHash, true, out spriteIndex); 173 | } 174 | 175 | if (spriteIndex == -1 || currentSpriteAsset == null || currentSpriteAsset.spriteGlyphTable == null) 176 | { 177 | texture = null; 178 | uvRect = default; 179 | return false; 180 | } 181 | 182 | var spriteChar = currentSpriteAsset.spriteCharacterTable[spriteIndex]; 183 | var glyphIndexLookup = TMPImage_TMP_SpriteAssetProxy.GetGlyphIndexLookup(currentSpriteAsset); 184 | var spriteGlyphIndex = glyphIndexLookup[spriteChar.glyphIndex]; 185 | var spriteGlyph = currentSpriteAsset.spriteGlyphTable[spriteGlyphIndex]; 186 | 187 | texture = currentSpriteAsset.spriteSheet; 188 | uvRect = CalculateUvRect(spriteGlyph.glyphRect, texture); 189 | return texture != null; 190 | } 191 | 192 | private static Rect CalculateUvRect(GlyphRect r, Texture t) 193 | { 194 | var s = new Vector2(t.width, t.height); 195 | return new Rect(r.x / s.x, r.y / s.y, r.width / s.x, r.height / s.y); 196 | } 197 | } 198 | } -------------------------------------------------------------------------------- /Runtime/TMPImage.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3fc647387e5507c46b06082aedc648a1 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Unity.TextMeshPro.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 622f4710647536e4caad464bf8e2ed40 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Unity.TextMeshPro/TMPImage_TMP_SpriteAssetProxy.cs: -------------------------------------------------------------------------------- 1 | namespace TMPro { 2 | using System.Collections.Generic; 3 | 4 | public static class TMPImage_TMP_SpriteAssetProxy { 5 | public static Dictionary GetGlyphIndexLookup(TMP_SpriteAsset spriteAsset) { 6 | if (spriteAsset.m_GlyphIndexLookup == null) { 7 | spriteAsset.UpdateLookupTables(); 8 | } 9 | 10 | return spriteAsset.m_GlyphIndexLookup; 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /Unity.TextMeshPro/TMPImage_TMP_SpriteAssetProxy.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1d840e72e239b254bb140c6f2fee1b4b 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Unity.TextMeshPro/Unity.TextMeshPro.asmref: -------------------------------------------------------------------------------- 1 | { 2 | "reference": "Unity.TextMeshPro" 3 | } -------------------------------------------------------------------------------- /Unity.TextMeshPro/Unity.TextMeshPro.asmref.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f0a8b0ca77245c94782edb9beac999ce 3 | AssemblyDefinitionReferenceImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /UnityEditor.UI.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: bd9ac86fa21037e4abceb6f09d42400a 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /UnityEditor.UI/TMPImage_SpriteDrawUtilityProxy.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace UnityEditor.UI 4 | { 5 | public static class TMPImage_SpriteDrawUtilityProxy 6 | { 7 | public static void DrawSprite(Texture tex, Rect drawArea, Rect outer, Rect uv, Color color) 8 | { 9 | SpriteDrawUtility.DrawSprite(tex, drawArea, outer, uv, color); 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /UnityEditor.UI/TMPImage_SpriteDrawUtilityProxy.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1206121a401045cdb9eaf030031d7d8c 3 | timeCreated: 1729449113 -------------------------------------------------------------------------------- /UnityEditor.UI/UnityEditor.UI.asmref: -------------------------------------------------------------------------------- 1 | { 2 | "reference": "GUID:343deaaf83e0cee4ca978e7df0b80d21" 3 | } -------------------------------------------------------------------------------- /UnityEditor.UI/UnityEditor.UI.asmref.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 828937de0d867924d91607b5d27357b0 3 | AssemblyDefinitionReferenceImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "com.codewriter.tmp-image", 3 | "displayName": "TMP Image", 4 | "description": "Script for drawing sprite from Text Mesh Pro atlases in UGUI", 5 | "version": "1.0.0", 6 | "unity": "2021.3", 7 | "license": "MIT", 8 | "author": "CodeWriter (https://github.com/orgs/codewriter-packages)", 9 | "homepage": "https://github.com/codewriter-packages/TMP-Image#readme", 10 | "dependencies": { } 11 | } 12 | -------------------------------------------------------------------------------- /package.json.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2fca7853cb9830345ae159a26fb8d168 3 | PackageManifestImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | --------------------------------------------------------------------------------