public void LoadFromDisk() { string json = PlayerPrefs.GetString(KeyPlayerPrefs); if (string.IsNullOrEmpty(json)) { Debug.Log("FileInfo is not exists in PlayerPrefs!"); return; } DynamicAtlas.FileInfo info = JsonUtility.FromJson <DynamicAtlas.FileInfo>(json); DynamicAtlas loadAtlas = DynamicAtlas.Load(info); if (loadAtlas == null) { Debug.Log(string.Format("Load atlas {0} is not exists!", info.Name)); return; } atlas = loadAtlas; Show(); Debug.Log(string.Format("Load atlas name: {0}, method: {1}", atlas.Name, atlas.Method)); }
public void DeleteFromDisk() { string json = PlayerPrefs.GetString(KeyPlayerPrefs); if (string.IsNullOrEmpty(json)) { Debug.Log("FileInfo is not exists in PlayerPrefs!"); return; } DynamicAtlas.FileInfo info = JsonUtility.FromJson <DynamicAtlas.FileInfo>(json); bool isDelete = DynamicAtlas.Delete(info); if (isDelete == false) { Debug.Log(string.Format("Atlas {0} is not exists!", info.Name)); return; } PlayerPrefs.DeleteKey(KeyPlayerPrefs); PlayerPrefs.Save(); Debug.Log(string.Format("Delete atlas: {0}", atlas.Name)); }
/// <summary> /// Save to disk the DynamicAtlas. /// </summary> /// <param name="atlas">DynamicAtlas.</param> /// <param name="info">Custom FileInfo.</param> /// <returns>Return FileInfo (Path and Name of saved atlas).</returns> public static FileInfo Save(DynamicAtlas atlas, FileInfo info = null) { if (info == null) { info = new FileInfo(atlas.Texture.name); } if (Directory.Exists(info.Path) == false) { Directory.CreateDirectory(info.Path); } if (atlas.IsApplied == false) { atlas.Apply(); } byte[] bytes = atlas.Texture.EncodeToPNG(); string json = JsonUtility.ToJson(atlas); File.WriteAllBytes(info.PathTexture, bytes); File.WriteAllText(info.PathData, json); return(info); }
void ClearFreeAreas() { DynamicAtlas dynamicAtlas = DynamicAtlasManager.Instance.GetDynamicAtlas(_mGroup); if (AtlasConfig.kUsingCopyTexture) { List <List <IntegerRectangle> > freeLists = dynamicAtlas.GetFreeAreas(); int freeListsCount = freeLists.Count; List <Texture2D> tex2DList = dynamicAtlas.tex2DList; for (int i = 0; i < freeListsCount; i++) { var freeList = freeLists[i]; int freeListCount = freeList.Count; Texture2D dstTex = tex2DList[i]; for (int j = 0; j < freeListCount; j++) { IntegerRectangle item = freeList[j]; Color32[] colors = new Color32[item.width * item.height]; for (int k = 0; k < colors.Length; ++k) { colors[k] = Color.clear; } dstTex.SetPixels32((int)item.x, (int)item.y, item.width, item.height, colors); dstTex.Apply(); } } } else { dynamicAtlas.ClearTextureWithBlit(); } }
void UpdateUV() { if (AtlasType == FXAtlasType.Static) { if (m_Atlas != null) { Texture texture = m_Atlas.spriteMaterial.mainTexture; if (texture != null) { m_SpriteUV.Set(m_Sprite.x, m_Sprite.y, m_Sprite.width, m_Sprite.height); m_SpriteUV = NGUIMath.ConvertToTexCoords(m_SpriteUV, texture.width, texture.height); } } } else if (AtlasType == FXAtlasType.Dynamic) { DynamicAtlasManager dynamic_atlas_manager = DynamicAtlasManager.GetInstance(); if (dynamic_atlas_manager != null) { DynamicAtlas the_atlas = dynamic_atlas_manager.GetDynamicAtlas(m_DynamicAtlasType); if (the_atlas != null) { Texture texture = the_atlas.m_Material.mainTexture; if (texture != null && m_Sprite != null) { m_SpriteUV.Set(m_Sprite.x, m_Sprite.y, m_Sprite.width, m_Sprite.height); m_SpriteUV = NGUIMath.ConvertToTexCoords(m_SpriteUV, texture.width, texture.height); } } } } }
public void SetGroup(DynamicAtlasGroup group) { if (m_Atlas != null) { return; } m_Group = group; m_Atlas = DynamicAtlasManager.Instance.GetDynamicAtlas(group); }
void DrawFreeArea(int index, DynamicAtlas dynamicAtlas) { Texture2D tex2D = null; if (texList.Count < index + 1) { tex2D = new Texture2D((int)_mGroup, (int)_mGroup, TextureFormat.ARGB32, false, true); texList.Add(tex2D); if (mFillColor == null) { mFillColor = tex2D.GetPixels32(); for (int i = 0; i < mFillColor.Length; ++i) { mFillColor[i] = Color.clear; } } } else { tex2D = texList[index]; } tex2D.SetPixels32(mFillColor); if (isRefreshFreeAreas) { Color32[] tmpColor; List <IntegerRectangle> freeList = dynamicAtlas.GetFreeAreas()[index]; foreach (IntegerRectangle item in freeList) { int size = item.width * item.height; tmpColor = new Color32[size]; for (int k = 0; k < size; ++k) { tmpColor[k] = Color.green;//画边 } tex2D.SetPixels32(item.x, item.y, item.width, item.height, tmpColor); int outLineSize = 2; if (item.width < outLineSize * 2 || item.height < outLineSize * 2) { outLineSize = 0; } size -= outLineSize * 4; tmpColor = new Color32[size]; for (int k = 0; k < size; ++k) { tmpColor[k] = Color.yellow; } tex2D.SetPixels32(item.x + outLineSize, item.y + outLineSize, item.width - outLineSize * 2, item.height - outLineSize * 2, tmpColor); tex2D.Apply(); } } float poxX = (index + 1) * 10 + index * dynamicAtlas.atlasWidth * scale; GUI.DrawTexture(new Rect(poxX, formPosY, dynamicAtlas.atlasWidth * scale, dynamicAtlas.atlasHeight * scale), tex2D); }
protected bool IsDynamicAtlasAvailable(eDynamicAtlasType type) { DynamicAtlas atlas = GetDynamicAtlas(type); if (atlas != null) { return(atlas.IsInitialized()); } return(false); }
public void Create() { atlas = new DynamicAtlas(sizeAtlas, sizeAtlas, nameAtlas, method); // or // atlas = new DynamicAtlas(sizeAtlas, sizeAtlas, nameAtlas); // or // atlas = new DynamicAtlas(sizeAtlas, nameAtlas); Debug.Log(string.Format("Create atlas: {0}, method: {1}", atlas.Name, atlas.Method)); Show(); }
public void UnloadDynamicSprite(string spriteName, eDynamicAtlasType type) { DynamicAtlas atlas = GetDynamicAtlas(type); if (atlas != null) { atlas.RemoveSpriteReference(spriteName); } else { EB.Debug.LogError("DynamicAtlasManager[UnloadDynamicSprite]: Dynamic atlas does not exist!"); } }
/// <summary> /// Loads the dynamic sprite. /// </summary> /// <param name="spriteName">Sprite name.</param> /// <param name="type">dynamic atlas type.</param> /// <param name="is_runtime">Set this to true if loading dynamic sprite at runtime(like uispirt & FX), set to false if trying to pre load dynamic sprite during loading process.</param> public void LoadDynamicSprite(string spriteName, eDynamicAtlasType type, bool is_runtime = true) { DynamicAtlas atlas = GetDynamicAtlas(type); if (atlas != null) { atlas.LoadDynamicSprite(spriteName, is_runtime); } else { EB.Debug.LogError("DynamicAtlasManager[LoadDynamicSprite]: Dynamic atlas does not exist!"); } }
public void RemoveSpriteUVUpdateCallback(eDynamicAtlasType type, EventDelegate.Callback onSpriteUVUpdate) { DynamicAtlas atlas = GetDynamicAtlas(type); if (atlas != null) { EventDelegate.Remove(atlas.onSpriteUVsUpdate, onSpriteUVUpdate); } else { EB.Debug.LogError("DynamicAtlasManager[RemoveSpriteUVUpdateCallback]: Dynamic atlas does not exist!"); } }
public void RemoveTextureAsyncCallback(eDynamicAtlasType type, EventDelegate.Callback onTextureAsyncSucceeded, EventDelegate.Callback onTextureAsyncFailed) { DynamicAtlas atlas = GetDynamicAtlas(type); if (atlas != null) { EventDelegate.Remove(atlas.onTextureAsyncSucceeded, onTextureAsyncSucceeded); EventDelegate.Remove(atlas.onTextureAsyncFailed, onTextureAsyncFailed); } else { EB.Debug.LogError("DynamicAtlasManager[RemoveTextureAsyncCallback]: Dynamic atlas does not exist!"); } }
public DynamicAtlas GetDynamicAtlas(DynamicAtlasGroup group, bool topFirst = true) { DynamicAtlas data; if (m_DynamicAtlas.ContainsKey(group)) { data = m_DynamicAtlas[group]; } else { data = new DynamicAtlas(group, topFirst); m_DynamicAtlas[group] = data; } return(data); }
public UISpriteData GetAtlasSprite(string spriteName, eDynamicAtlasType type) { DynamicAtlas atlas = GetDynamicAtlas(type); if (atlas != null) { return(atlas.GetSpriteData(spriteName)); } else { EB.Debug.LogError("DynamicAtlasManager[GetAtlasSprite]: Dynamic atlas does not exist!"); } return(null); }
public void SaveOnDisk() { if (AtlasIsNull()) { return; } DynamicAtlas.FileInfo info = DynamicAtlas.Save(atlas); string json = JsonUtility.ToJson(info); Debug.Log("Save complite, infoFile: " + json); PlayerPrefs.SetString(KeyPlayerPrefs, json); PlayerPrefs.Save(); }
/// <summary> /// Load from disk the DynamicAtlas. /// </summary> /// <param name="info">FileInfo.</param> /// <returns>Return DynamicAtlas.</returns> public static DynamicAtlas Load(FileInfo info) { if (File.Exists(info.PathTexture) == false || File.Exists(info.PathData) == false) { return(null); } byte[] bytes = File.ReadAllBytes(info.PathTexture); string json = File.ReadAllText(info.PathData); DynamicAtlas atlas = JsonUtility.FromJson <DynamicAtlas>(json); atlas.Texture = new Texture2D(0, 0, TextureFormat.RGBA32, false); atlas.Texture.LoadImage(bytes); atlas.Texture.name = info.Name; return(atlas); }
private unsafe DynamicAtlas <Color> .Reservation Upload(FTBitmap bitmap) { bool foundRoom = false; DynamicAtlas <Color> .Reservation result = default(DynamicAtlas <Color> .Reservation); int width = bitmap.Width, rows = bitmap.Rows, pitch = bitmap.Pitch; var widthW = width + (Font.GlyphMargin * 2); var heightW = rows + (Font.GlyphMargin * 2); foreach (var atlas in Atlases) { if (atlas.TryReserve(widthW, heightW, out result)) { foundRoom = true; break; } } if (!foundRoom) { var newAtlas = new DynamicAtlas <Color>( Font.RenderCoordinator, AtlasWidth, AtlasHeight, SurfaceFormat.Color, 4, Font.MipMapping ? PickMipGenerator(Font) : null ); Atlases.Add(newAtlas); if (!newAtlas.TryReserve(widthW, heightW, out result)) { throw new InvalidOperationException("Character too large for atlas"); } } var pSrc = (byte *)bitmap.Buffer; fixed(Color *pPixels = result.Atlas.Pixels) { var pDest = (byte *)pPixels; switch (bitmap.PixelMode) { case PixelMode.Gray: var table = Font.GammaTable; for (var y = 0; y < rows; y++) { var rowOffset = result.Atlas.Width * (y + result.Y + Font.GlyphMargin) + (result.X + Font.GlyphMargin); var pDestRow = pDest + (rowOffset * 4); int yPitch = y * pitch; for (var x = 0; x < width; x++) { var g = table[pSrc[x + yPitch]]; pDestRow[3] = pDestRow[2] = pDestRow[1] = pDestRow[0] = g; pDestRow += 4; } } break; case PixelMode.Mono: for (var y = 0; y < rows; y++) { var rowOffset = result.Atlas.Width * (y + result.Y + Font.GlyphMargin) + (result.X + Font.GlyphMargin); var pDestRow = pDest + (rowOffset * 4); int yPitch = y * pitch; for (int x = 0; x < pitch; x++, pDestRow += (8 * 4)) { var bits = pSrc[x + yPitch]; for (int i = 0; i < 8; i++) { int iy = 7 - i; byte g = ((bits & (1 << iy)) != 0) ? (byte)255 : (byte)0; var pElt = pDestRow + (i * 4); pElt[3] = pElt[2] = pElt[1] = pElt[0] = g; } } } break; default: throw new NotImplementedException("Unsupported pixel mode: " + bitmap.PixelMode); } } return(result); }
void UpdateGeometry() { #if UNITY_EDITOR if (mUpdateFrame != Time.frameCount || !Application.isPlaying) #else if (mUpdateFrame != Time.frameCount) #endif { mUpdateFrame = Time.frameCount; if (GetComponent <ParticleSystem>() != null) { // int max_particles = GetComponent <ParticleSystem>().maxParticles; m_Particles.Adjust(max_particles); bool isBufferChanged = m_Meshes.Adjust(max_particles, true); ParticleSystem.Particle[] particles = m_Particles.GetBuffer(); GetComponent <ParticleSystem>().GetParticles(particles); // ParticleMesh[] meshes = m_Meshes.GetBuffer(); int mesh_count = meshes.Length; int particle_count = GetComponent <ParticleSystem>().particleCount; if (isBufferChanged) { UnityEngine.Profiling.Profiler.BeginSample("FXBillboardParticle 001"); for (int i = particle_count; i < mesh_count; i++) { if (meshes[i].m_Renderer != null) { meshes[i].m_Renderer.enabled = false; } } UnityEngine.Profiling.Profiler.EndSample(); } UnityEngine.Profiling.Profiler.BeginSample("FXBillboardParticle 002"); for (int i = 0; i < particle_count; i++) { ParticleSystem.Particle particle = particles[i]; float factor = (particle.startLifetime - particle.remainingLifetime) / particle.startLifetime; float size = particle.GetCurrentSize(GetComponent <ParticleSystem>()) * Mathf.Lerp(FromSize, ToSize, factor); Matrix4x4 transform_matrix = Matrix4x4.TRS(particle.position, Quaternion.AngleAxis(particle.rotation, -Camera.main.transform.forward), new Vector3(size, size, size)); meshes[i].PreUpdate(transform); if (AtlasType == FXAtlasType.Static) { if (meshes[i].m_Renderer != null && m_Atlas != null) { meshes[i].m_Renderer.sharedMaterial = m_Atlas.spriteMaterial; meshes[i].m_Renderer.enabled = true; } } else if (AtlasType == FXAtlasType.Dynamic) { DynamicAtlasManager dynamic_atlas_manager = DynamicAtlasManager.GetInstance(); if (meshes[i].m_Renderer != null && dynamic_atlas_manager != null) { DynamicAtlas dynamic_atlas = dynamic_atlas_manager.GetDynamicAtlas(m_DynamicAtlasType); if (dynamic_atlas != null) { meshes[i].m_Renderer.sharedMaterial = dynamic_atlas.m_Material; meshes[i].m_Renderer.enabled = true; } } } if (GetComponent <ParticleSystem>().simulationSpace == ParticleSystemSimulationSpace.Local) { meshes[i].m_Vertices[0].Set(-0.5f, -0.5f, 0); meshes[i].m_Vertices[0] = transform_matrix.MultiplyPoint(meshes[i].m_Vertices[0]); meshes[i].m_Vertices[1].Set(-0.5f, 0.5f, 0); meshes[i].m_Vertices[1] = transform_matrix.MultiplyPoint(meshes[i].m_Vertices[1]); meshes[i].m_Vertices[2].Set(0.5f, 0.5f, 0); meshes[i].m_Vertices[2] = transform_matrix.MultiplyPoint(meshes[i].m_Vertices[2]); meshes[i].m_Vertices[3].Set(0.5f, -0.5f, 0); meshes[i].m_Vertices[3] = transform_matrix.MultiplyPoint(meshes[i].m_Vertices[3]); } else { Matrix4x4 world2local_matrix = transform.worldToLocalMatrix; meshes[i].m_Vertices[0].Set(-0.5f, -0.5f, 0); meshes[i].m_Vertices[0] = transform_matrix.MultiplyPoint(meshes[i].m_Vertices[0]); meshes[i].m_Vertices[0] = world2local_matrix.MultiplyPoint(meshes[i].m_Vertices[0]); meshes[i].m_Vertices[1].Set(-0.5f, 0.5f, 0); meshes[i].m_Vertices[1] = transform_matrix.MultiplyPoint(meshes[i].m_Vertices[1]); meshes[i].m_Vertices[1] = world2local_matrix.MultiplyPoint(meshes[i].m_Vertices[1]); meshes[i].m_Vertices[2].Set(0.5f, 0.5f, 0); meshes[i].m_Vertices[2] = transform_matrix.MultiplyPoint(meshes[i].m_Vertices[2]); meshes[i].m_Vertices[2] = world2local_matrix.MultiplyPoint(meshes[i].m_Vertices[2]); meshes[i].m_Vertices[3].Set(0.5f, -0.5f, 0); meshes[i].m_Vertices[3] = transform_matrix.MultiplyPoint(meshes[i].m_Vertices[3]); meshes[i].m_Vertices[3] = world2local_matrix.MultiplyPoint(meshes[i].m_Vertices[3]); } // Rect uv = m_SpriteUV; float frame = TileX * TileY * factor; int frame_index = (int)frame; int frameX = frame_index % TileX; int frameY = TileY - frame_index / TileY - 1; float stepX = m_SpriteUV.width / (float)TileX; float stepY = m_SpriteUV.height / (float)TileY; uv.Set(stepX * frameX + m_SpriteUV.x, stepY * frameY + m_SpriteUV.y, stepX, stepY); meshes[i].m_UVs[0].Set(uv.xMin, uv.yMin); meshes[i].m_UVs[1].Set(uv.xMin, uv.yMax); meshes[i].m_UVs[2].Set(uv.xMax, uv.yMax); meshes[i].m_UVs[3].Set(uv.xMax, uv.yMin); // Color color = particle.GetCurrentColor(GetComponent <ParticleSystem>()) * Color.Lerp(FromColor, ToColor, factor); meshes[i].m_Colors[0] = color; meshes[i].m_Colors[1] = color; meshes[i].m_Colors[2] = color; meshes[i].m_Colors[3] = color; meshes[i].m_Indices[0] = 0; meshes[i].m_Indices[1] = 1; meshes[i].m_Indices[2] = 2; meshes[i].m_Indices[3] = 0; meshes[i].m_Indices[4] = 2; meshes[i].m_Indices[5] = 3; meshes[i].Update(); } UnityEngine.Profiling.Profiler.EndSample(); } } }
public void OnGUI() { if (EditorApplication.isPlaying == false) { _DynamicAtlasWindow.Close(); return; } EditorGUILayout.BeginHorizontal(); EditorGUILayout.BeginVertical(); EditorGUILayout.LabelField("--------------------------------------------------------------------------------"); DynamicAtlas dynamicAtlas = DynamicAtlasManager.Instance.GetDynamicAtlas(_mGroup); EditorGUILayout.LabelField("图集尺寸:" + dynamicAtlas.atlasWidth + " x " + dynamicAtlas.atlasHeight); EditorGUILayout.LabelField("--------------------------------------------------------------------------------"); EditorGUILayout.EndVertical(); GUILayout.Space(10); EditorGUILayout.BeginVertical(); isShowFreeAreas = GUILayout.Toggle(isShowFreeAreas, "Show Free Areas", GUILayout.Width(200), GUILayout.Height(20)); EditorGUILayout.BeginHorizontal(); if (isShowFreeAreas) { if (GUILayout.Button("Refresh and Clear Free Area", GUILayout.Width(200), GUILayout.Height(20))) { isRefreshFreeAreas = true; ClearFreeAreas(); } } EditorGUILayout.EndHorizontal(); EditorGUILayout.EndVertical(); scale = EditorGUILayout.Slider(scale, 0.2f, 1); EditorGUILayout.EndHorizontal(); if (AtlasConfig.kUsingCopyTexture) { List <Texture2D> tex2DList = dynamicAtlas.tex2DList; int count = tex2DList.Count; for (int i = 0; i < count; i++) { Texture2D tex2D = tex2DList[i]; float poxX = (i + 1) * 10 + i * dynamicAtlas.atlasWidth * scale; if (isShowFreeAreas) { DrawFreeArea(i, dynamicAtlas); } GUI.DrawTexture(new Rect(poxX, formPosY, dynamicAtlas.atlasWidth * scale, dynamicAtlas.atlasHeight * scale), tex2D); } } else { List <RenderTexture> renderTexList = dynamicAtlas.renderTexList; int count = renderTexList.Count; for (int i = 0; i < count; i++) { float poxX = (i + 1) * 10 + i * dynamicAtlas.atlasWidth * scale; if (isShowFreeAreas) { DrawFreeArea(i, dynamicAtlas); } GUI.DrawTexture(new Rect(poxX, formPosY, dynamicAtlas.atlasWidth * scale, dynamicAtlas.atlasHeight * scale), renderTexList[i]); } } if (isShowFreeAreas) { isRefreshFreeAreas = false; } }
public bool GetGlyph(char ch, out Glyph glyph) { if (IsDisposed) { glyph = default(Glyph); return(false); } if (Cache.TryGetValue(ch, out glyph)) { return(true); } if ((ch == '\r') || (ch == '\n') || (ch == '\0')) { return(false); } Font.Face.SetCharSize( 0, _SizePoints, (uint)(BaseDPI * Font.DPIPercent / 100), (uint)(BaseDPI * Font.DPIPercent / 100) ); uint index; if (ch == '\t') { index = Font.Face.GetCharIndex(' '); } else { index = Font.Face.GetCharIndex(ch); } if (index <= 0) { return(false); } var flags = LoadFlags.Color | LoadFlags.Render; if (!Font.EnableBitmaps) { flags |= LoadFlags.NoBitmap; } if (!Font.Hinting) { flags |= LoadFlags.NoHinting; } Font.Face.LoadGlyph( index, flags, LoadTarget.Normal ); var ftgs = Font.Face.Glyph; var scaleX = Font.Face.Size.Metrics.ScaleX; var scaleY = Font.Face.Size.Metrics.ScaleY; var bitmap = ftgs.Bitmap; DynamicAtlas <Color> .Reservation texRegion = default(DynamicAtlas <Color> .Reservation); if ((bitmap.Width > 0) && (bitmap.Rows > 0)) { texRegion = Upload(bitmap); } var ascender = Font.Face.Size.Metrics.Ascender.ToSingle(); var metrics = ftgs.Metrics; var advance = metrics.HorizontalAdvance.ToSingle(); if (ch == '\t') { advance *= Font.TabSize; } var scaleFactor = 100f / Font.DPIPercent; var widthMetric = metrics.Width.ToSingle(); var bearingXMetric = metrics.HorizontalBearingX.ToSingle(); glyph = new SrGlyph { Character = ch, Width = widthMetric, LeftSideBearing = bearingXMetric, RightSideBearing = ( advance - widthMetric - metrics.HorizontalBearingX.ToSingle() ), XOffset = ftgs.BitmapLeft - bearingXMetric - Font.GlyphMargin, YOffset = -ftgs.BitmapTop + ascender - Font.GlyphMargin, Texture = texRegion.Texture, BoundsInTexture = texRegion.Rectangle, LineSpacing = Font.Face.Size.Metrics.Height.ToSingle() }; // Some fonts have weirdly-sized space characters if (Char.IsWhiteSpace(ch)) { glyph.RightSideBearing = (float)Math.Round(glyph.RightSideBearing); } Cache[ch] = glyph; return(true); }
private unsafe DynamicAtlas <Color> .Reservation Upload(FTBitmap bitmap) { bool foundRoom = false; DynamicAtlas <Color> .Reservation result = default(DynamicAtlas <Color> .Reservation); int width = bitmap.Width, rows = bitmap.Rows, pitch = bitmap.Pitch; var widthW = width + (Font.GlyphMargin * 2); var heightW = rows + (Font.GlyphMargin * 2); foreach (var atlas in Atlases) { if (atlas.TryReserve(widthW, heightW, out result)) { foundRoom = true; break; } } var surfaceFormat = Font.sRGB ? Evil.TextureUtils.ColorSrgbEXT : SurfaceFormat.Color; if (!foundRoom) { var isFirstAtlas = (Atlases.Count == 0) && (_SizePoints < SmallFirstAtlasThreshold); var newAtlas = new DynamicAtlas <Color>( Font.RenderCoordinator, isFirstAtlas ? FirstAtlasWidth : AtlasWidth, isFirstAtlas ? FirstAtlasHeight : AtlasHeight, surfaceFormat, 4, Font.MipMapping ? PickMipGenerator(Font) : null, tag: $"{Font.Face.FamilyName} {SizePoints}pt" ); Atlases.Add(newAtlas); if (!newAtlas.TryReserve(widthW, heightW, out result)) { throw new InvalidOperationException("Character too large for atlas"); } } var pSrc = (byte *)bitmap.Buffer; fixed(Color *pPixels = result.Atlas.Pixels) { var pDest = (byte *)pPixels; switch (bitmap.PixelMode) { case PixelMode.Gray: var table = Font.GammaRamp?.GammaTable; var srgb = (surfaceFormat != SurfaceFormat.Color); for (var y = 0; y < rows; y++) { var rowOffset = result.Atlas.Width * (y + result.Y + Font.GlyphMargin) + (result.X + Font.GlyphMargin); var pDestRow = pDest + (rowOffset * 4); int yPitch = y * pitch; if (table == null) { for (var x = 0; x < width; x++) { var a = pSrc[x + yPitch]; var g = srgb ? ColorSpace.LinearByteTosRGBByteTable[a] : a; pDestRow[3] = a; pDestRow[2] = pDestRow[1] = pDestRow[0] = g; pDestRow += 4; } } else { for (var x = 0; x < width; x++) { var a = table[pSrc[x + yPitch]]; var g = srgb ? ColorSpace.LinearByteTosRGBByteTable[a] : a; pDestRow[3] = a; pDestRow[2] = pDestRow[1] = pDestRow[0] = g; pDestRow += 4; } } } break; case PixelMode.Mono: for (var y = 0; y < rows; y++) { var rowOffset = result.Atlas.Width * (y + result.Y + Font.GlyphMargin) + (result.X + Font.GlyphMargin); var pDestRow = pDest + (rowOffset * 4); int yPitch = y * pitch; for (int x = 0; x < pitch; x++, pDestRow += (8 * 4)) { var bits = pSrc[x + yPitch]; for (int i = 0; i < 8; i++) { int iy = 7 - i; byte g = ((bits & (1 << iy)) != 0) ? (byte)255 : (byte)0; var pElt = pDestRow + (i * 4); pElt[3] = pElt[2] = pElt[1] = pElt[0] = g; } } } break; default: throw new NotImplementedException("Unsupported pixel mode: " + bitmap.PixelMode); } } result.Invalidate(); return(result); }
private bool PopulateGlyphCache(uint ch, out Glyph glyph, Color?defaultColor) { var resolution = (uint)(BaseDPI * Font.DPIPercent / 100); var size = Font.GetFTSize(_SizePoints, resolution); uint index; if (ch == '\t') { index = Font.Face.GetCharIndex(' '); } else { index = Font.Face.GetCharIndex(ch); } if (index <= 0) { glyph = default(Glyph); return(false); } var flags = LoadFlags.Render; if (!Font.EnableBitmaps) { flags |= LoadFlags.NoBitmap; } if (!Font.Hinting) { flags |= LoadFlags.NoHinting; } if (Font.Monochrome) { flags |= LoadFlags.Monochrome; } else { flags |= LoadFlags.Color; } Font.Face.LoadGlyph( index, flags, Font.Monochrome ? LoadTarget.Mono : LoadTarget.Normal ); var sizeMetrics = size.Metrics; var ftgs = Font.Face.Glyph; var scaleX = sizeMetrics.ScaleX; var scaleY = sizeMetrics.ScaleY; var bitmap = ftgs.Bitmap; DynamicAtlas <Color> .Reservation texRegion = default(DynamicAtlas <Color> .Reservation); if ((bitmap.Width > 0) && (bitmap.Rows > 0)) { texRegion = Upload(bitmap); } var ascender = sizeMetrics.Ascender.ToSingle(); var glyphMetrics = ftgs.Metrics; var advance = glyphMetrics.HorizontalAdvance.ToSingle(); if (ch == '\t') { advance *= Font.TabSize; } var scaleFactor = 100f / Font.DPIPercent; var widthMetric = glyphMetrics.Width.ToSingle(); var bearingXMetric = glyphMetrics.HorizontalBearingX.ToSingle(); var rect = texRegion.Rectangle; glyph = new SrGlyph { Character = ch, Width = widthMetric, LeftSideBearing = bearingXMetric, RightSideBearing = ( advance - widthMetric - glyphMetrics.HorizontalBearingX.ToSingle() ), XOffset = ftgs.BitmapLeft - bearingXMetric - Font.GlyphMargin, YOffset = -ftgs.BitmapTop + ascender - Font.GlyphMargin + Font.VerticalOffset + VerticalOffset, RectInTexture = rect, // FIXME: This will become invalid if the extra spacing changes // FIXME: Scale the spacing appropriately based on ratios LineSpacing = sizeMetrics.Height.ToSingle() + ExtraLineSpacing, DefaultColor = defaultColor, Baseline = sizeMetrics.Ascender.ToSingle() }; if (texRegion.Atlas != null) { glyph.Texture = texRegion.Atlas; glyph.BoundsInTexture = texRegion.Atlas.BoundsFromRectangle(in rect); } // HACK if (ch <= 0xCFFF) { // Some fonts have weirdly-sized space characters if (char.IsWhiteSpace((char)ch)) { glyph.RightSideBearing = (float)Math.Round(glyph.RightSideBearing); } } if (ch < LowCacheSize) { LowCache[ch] = glyph; } Cache[ch] = glyph; return(true); }