using UnityEngine; using PlasticGui; namespace Unity.PlasticSCM.Editor.UI.Avatar { internal static class AvatarGenerator { internal static Texture2D GenerateFromUserName(string userName) { if (string.IsNullOrEmpty(userName)) return null; Font font = GetDefaultFont(); if (font == null) return null; Color bgColor = AvatarColor.FromUserName(userName); string initials = GetAvatarInitial.ForUserName(userName); return Generate(bgColor, initials, font); } static Texture2D Generate(Color bgColor, string initials, Font font) { int fontSize = Mathf.RoundToInt(RENDER_SIZE * 0.48f); font.RequestCharactersInTexture(initials, fontSize, FontStyle.Normal); Texture2D textTexture = RenderTextToTexture(font, initials, fontSize); Texture2D highRes = CompositeCircleAndText(bgColor, textTexture); UnityEngine.Object.DestroyImmediate(textTexture, true); Texture2D result = Downsample(highRes); UnityEngine.Object.DestroyImmediate(highRes, true); return result; } static Texture2D RenderTextToTexture( Font font, string initials, int fontSize) { RenderTexture rt = RenderTexture.GetTemporary( RENDER_SIZE, RENDER_SIZE, 0, RenderTextureFormat.ARGB32); RenderTexture prev = RenderTexture.active; RenderTexture.active = rt; GL.Clear(true, true, Color.clear); GL.PushMatrix(); GL.LoadPixelMatrix(0, RENDER_SIZE, RENDER_SIZE, 0); DrawInitials(font, initials, fontSize); GL.PopMatrix(); Texture2D result = new Texture2D( RENDER_SIZE, RENDER_SIZE, TextureFormat.RGBA32, false); result.ReadPixels(new Rect(0, 0, RENDER_SIZE, RENDER_SIZE), 0, 0); result.Apply(); RenderTexture.active = prev; RenderTexture.ReleaseTemporary(rt); return result; } static Texture2D CompositeCircleAndText( Color bgColor, Texture2D textTexture) { Color[] textPixels = textTexture.GetPixels(); Color[] pixels = new Color[RENDER_SIZE * RENDER_SIZE]; float center = RENDER_SIZE / 2f; float radius = RENDER_SIZE / 2f; for (int y = 0; y < RENDER_SIZE; y++) { for (int x = 0; x < RENDER_SIZE; x++) { int idx = y * RENDER_SIZE + x; float dx = x + 0.5f - center; float dy = y + 0.5f - center; float dist = Mathf.Sqrt(dx * dx + dy * dy); float circleAlpha = Mathf.Clamp01(radius - dist + 0.5f); float textCoverage = textPixels[idx].r; pixels[idx] = new Color( Mathf.Lerp(bgColor.r, 1f, textCoverage), Mathf.Lerp(bgColor.g, 1f, textCoverage), Mathf.Lerp(bgColor.b, 1f, textCoverage), circleAlpha); } } Texture2D result = new Texture2D( RENDER_SIZE, RENDER_SIZE, TextureFormat.RGBA32, false); result.SetPixels(pixels); result.Apply(); return result; } static Texture2D Downsample(Texture2D source) { int blockSize = RENDER_SIZE / OUTPUT_SIZE; float invBlockArea = 1f / (blockSize * blockSize); Color[] sourcePixels = source.GetPixels(); Color[] targetPixels = new Color[OUTPUT_SIZE * OUTPUT_SIZE]; for (int ty = 0; ty < OUTPUT_SIZE; ty++) { for (int tx = 0; tx < OUTPUT_SIZE; tx++) { float r = 0, g = 0, b = 0, a = 0; for (int by = 0; by < blockSize; by++) { int rowOffset = (ty * blockSize + by) * RENDER_SIZE; for (int bx = 0; bx < blockSize; bx++) { Color c = sourcePixels[rowOffset + tx * blockSize + bx]; r += c.r; g += c.g; b += c.b; a += c.a; } } targetPixels[ty * OUTPUT_SIZE + tx] = new Color( r * invBlockArea, g * invBlockArea, b * invBlockArea, a * invBlockArea); } } Texture2D result = new Texture2D( OUTPUT_SIZE, OUTPUT_SIZE, TextureFormat.RGBA32, false); result.filterMode = FilterMode.Bilinear; result.SetPixels(targetPixels); result.Apply(); return result; } static void DrawInitials(Font font, string initials, int fontSize) { if (font.material == null) return; font.material.SetPass(0); CharacterInfo[] charInfos = new CharacterInfo[initials.Length]; float totalAdvance = 0; float maxAscent = 0; float maxDescent = 0; for (int i = 0; i < initials.Length; i++) { if (!font.GetCharacterInfo( initials[i], out charInfos[i], fontSize, FontStyle.Normal)) return; totalAdvance += charInfos[i].advance; maxAscent = Mathf.Max(maxAscent, charInfos[i].maxY); maxDescent = Mathf.Min(maxDescent, charInfos[i].minY); } float baselineY = (RENDER_SIZE + maxAscent + maxDescent) / 2f; float startX = (RENDER_SIZE - totalAdvance) / 2f; GL.Begin(GL.QUADS); GL.Color(Color.white); float curX = startX; for (int i = 0; i < initials.Length; i++) { CharacterInfo ci = charInfos[i]; float left = curX + ci.minX; float right = curX + ci.maxX; float top = baselineY - ci.maxY; float bottom = baselineY - ci.minY; GL.TexCoord2(ci.uvBottomLeft.x, ci.uvBottomLeft.y); GL.Vertex3(left, bottom, 0); GL.TexCoord2(ci.uvBottomRight.x, ci.uvBottomRight.y); GL.Vertex3(right, bottom, 0); GL.TexCoord2(ci.uvTopRight.x, ci.uvTopRight.y); GL.Vertex3(right, top, 0); GL.TexCoord2(ci.uvTopLeft.x, ci.uvTopLeft.y); GL.Vertex3(left, top, 0); curX += ci.advance; } GL.End(); } static Font GetDefaultFont() { if (mFont != null) return mFont; #if UNITY_2022_1_OR_NEWER mFont = Resources.GetBuiltinResource("LegacyRuntime.ttf"); #else mFont = Resources.GetBuiltinResource("Arial.ttf"); #endif return mFont; } static Font mFont; const int RENDER_SIZE = 256; const int OUTPUT_SIZE = 32; } }