private FontAtlas GetCurrentAtlas(ITexture2DManager device, int textureWidth, int textureHeight) #endif { if (_currentAtlas == null) { Texture2D existingTexture = null; if (ExistingTexture != null && Atlases.Count == 0) { existingTexture = ExistingTexture; } _currentAtlas = new FontAtlas(textureWidth, textureHeight, 256, existingTexture); // If existing texture is used, mark existing used rect as used if (existingTexture != null && !ExistingTextureUsedSpace.IsEmpty) { if (!_currentAtlas.AddSkylineLevel(0, ExistingTextureUsedSpace.X, ExistingTextureUsedSpace.Y, ExistingTextureUsedSpace.Width, ExistingTextureUsedSpace.Height)) { throw new Exception(string.Format("Unable to specify existing texture used space: {0}", ExistingTextureUsedSpace)); } // TODO: Clear remaining space } Atlases.Add(_currentAtlas); } return(_currentAtlas); }
public FontSystem(IFontLoader fontLoader, ITexture2DManager textureCreator, int width = 1024, int height = 1024, int blurAmount = 0, int strokeAmount = 0, bool premultiplyAlpha = true) #endif { if (fontLoader == null) { throw new ArgumentNullException(nameof(fontLoader)); } #if MONOGAME || FNA || STRIDE if (graphicsDevice == null) { throw new ArgumentNullException(nameof(graphicsDevice)); } _graphicsDevice = graphicsDevice; #else if (textureCreator == null) { throw new ArgumentNullException(nameof(textureCreator)); } _textureCreator = textureCreator; #endif _fontLoader = fontLoader; if (width <= 0) { throw new ArgumentOutOfRangeException(nameof(width)); } if (height <= 0) { throw new ArgumentOutOfRangeException(nameof(height)); } if (blurAmount < 0 || blurAmount > 20) { throw new ArgumentOutOfRangeException(nameof(blurAmount)); } if (strokeAmount < 0 || strokeAmount > 20) { throw new ArgumentOutOfRangeException(nameof(strokeAmount)); } if (strokeAmount != 0 && blurAmount != 0) { throw new ArgumentException("Cannot have both blur and stroke."); } BlurAmount = blurAmount; StrokeAmount = strokeAmount; PremultiplyAlpha = premultiplyAlpha; _size = new Point(width, height); }
protected internal override FontGlyph GetGlyph(ITexture2DManager device, int codepoint) #endif { var result = InternalGetGlyph(codepoint); if (result == null && DefaultCharacter != null) { result = InternalGetGlyph(DefaultCharacter.Value); } return(result); }
private DynamicFontGlyph GetDynamicGlyph(ITexture2DManager device, int codepoint) #endif { var result = GetGlyphInternal(device, codepoint); if (result == null && FontSystem.DefaultCharacter != null) { result = GetGlyphInternal(device, FontSystem.DefaultCharacter.Value); } return(result); }
private DynamicFontGlyph GetGlyphInternal(ITexture2DManager device, int codepoint) #endif { var glyph = GetGlyphWithoutBitmap(codepoint); if (glyph == null) { return(null); } if (device == null || glyph.Texture != null) { return(glyph); } FontSystem.RenderGlyphOnAtlas(device, glyph); return(glyph); }
internal void RenderGlyphOnAtlas(ITexture2DManager device, DynamicFontGlyph glyph) #endif { var textureSize = new Point(TextureWidth, TextureHeight); if (ExistingTexture != null) { #if MONOGAME || FNA || STRIDE textureSize = new Point(ExistingTexture.Width, ExistingTexture.Height); #else textureSize = device.GetTextureSize(ExistingTexture); #endif } int gx = 0, gy = 0; var gw = glyph.Bounds.Width; var gh = glyph.Bounds.Height; var currentAtlas = GetCurrentAtlas(device, textureSize.X, textureSize.Y); if (!currentAtlas.AddRect(gw, gh, ref gx, ref gy)) { CurrentAtlasFull?.Invoke(this, EventArgs.Empty); // This code will force creation of new atlas _currentAtlas = null; currentAtlas = GetCurrentAtlas(device, textureSize.X, textureSize.Y); // Try to add again if (!currentAtlas.AddRect(gw, gh, ref gx, ref gy)) { throw new Exception(string.Format("Could not add rect to the newly created atlas. gw={0}, gh={1}", gw, gh)); } } glyph.Bounds.X = gx; glyph.Bounds.Y = gy; currentAtlas.RenderGlyph(device, glyph, FontSources[glyph.FontSourceIndex], BlurAmount, StrokeAmount, PremultiplyAlpha, KernelWidth, KernelHeight); glyph.Texture = currentAtlas.Texture; }
public unsafe static StaticSpriteFont FromBMFont(string data, Func <string, Stream> imageStreamOpener, ITexture2DManager textureManager) #endif { var bmFont = LoadBMFont(data); var textures = new Dictionary <string, Texture2D>(); for (var i = 0; i < bmFont.Pages.Length; ++i) { var fileName = bmFont.Pages[i].FileName; Stream stream = null; try { stream = imageStreamOpener(fileName); if (!stream.CanSeek) { // If stream isn't seekable, use MemoryStream instead var ms = new MemoryStream(); stream.CopyTo(ms); ms.Seek(0, SeekOrigin.Begin); stream.Dispose(); stream = ms; } var image = ImageResult.FromStream(stream, ColorComponents.RedGreenBlueAlpha); if (image.SourceComp == ColorComponents.Grey) { // If input image is single byte per pixel, then StbImageSharp will set alpha to 255 in the resulting 32-bit image // Such behavior isn't acceptable for us // So reset alpha to color value for (var j = 0; j < image.Data.Length / 4; ++j) { image.Data[j * 4 + 3] = image.Data[j * 4]; } } #if MONOGAME || FNA || STRIDE var texture = Texture2DManager.CreateTexture(device, image.Width, image.Height); Texture2DManager.SetTextureData(texture, new Rectangle(0, 0, image.Width, image.Height), image.Data); #else var texture = textureManager.CreateTexture(image.Width, image.Height); textureManager.SetTextureData(texture, new Rectangle(0, 0, image.Width, image.Height), image.Data); #endif textures[fileName] = texture; } finally { stream.Dispose(); } } return(FromBMFont(bmFont, fileName => new TextureWithOffset(textures[fileName]))); }
public void RenderGlyph(ITexture2DManager textureManager, DynamicFontGlyph glyph, IFontSource fontSource, int blurAmount, int strokeAmount, bool premultiplyAlpha, int kernelWidth, int kernelHeight) #endif { var pad = Math.Max(DynamicFontGlyph.PadFromBlur(blurAmount), DynamicFontGlyph.PadFromBlur(strokeAmount)); // Render glyph to byte buffer var bufferSize = glyph.Bounds.Width * glyph.Bounds.Height; var buffer = _byteBuffer; if ((buffer == null) || (buffer.Length < bufferSize)) { buffer = new byte[bufferSize]; _byteBuffer = buffer; } Array.Clear(buffer, 0, bufferSize); var colorBuffer = _colorBuffer; if ((colorBuffer == null) || (colorBuffer.Length < bufferSize * 4)) { colorBuffer = new byte[bufferSize * 4]; _colorBuffer = colorBuffer; } fontSource.RasterizeGlyphBitmap(glyph.Id, glyph.Size, buffer, pad + pad * glyph.Bounds.Width, glyph.Bounds.Width - pad * 2, glyph.Bounds.Height - pad * 2, glyph.Bounds.Width); if (strokeAmount > 0) { var width = glyph.Bounds.Width; var top = width * strokeAmount; var bottom = (glyph.Bounds.Height - strokeAmount) * glyph.Bounds.Width; var right = glyph.Bounds.Width - strokeAmount; var left = strokeAmount; byte d; for (var i = 0; i < bufferSize; ++i) { var ci = i * 4; var col = buffer[i]; var black = 0; if (col == 255) { colorBuffer[ci] = colorBuffer[ci + 1] = colorBuffer[ci + 2] = colorBuffer[ci + 3] = 255; continue; } if (i >= top) { black = buffer[i - top]; } if (i < bottom) { d = buffer[i + top]; black = ((255 - d) * black + 255 * d) / 255; } if (i % width >= left) { d = buffer[i - strokeAmount]; black = ((255 - d) * black + 255 * d) / 255; } if (i % width < right) { d = buffer[i + strokeAmount]; black = ((255 - d) * black + 255 * d) / 255; } if (black == 0) { if (col == 0) { colorBuffer[ci] = colorBuffer[ci + 1] = colorBuffer[ci + 2] = colorBuffer[ci + 3] = 0; //black transparency to suit stroke continue; } if (premultiplyAlpha) { colorBuffer[ci] = colorBuffer[ci + 1] = colorBuffer[ci + 2] = colorBuffer[ci + 3] = col; } else { colorBuffer[ci] = colorBuffer[ci + 1] = colorBuffer[ci + 2] = 255; colorBuffer[ci + 3] = col; } } else { if (col == 0) { colorBuffer[ci] = colorBuffer[ci + 1] = colorBuffer[ci + 2] = 0; colorBuffer[ci + 3] = (byte)black; continue; } if (premultiplyAlpha) { var alpha = ((255 - col) * black + 255 * col) / 255; colorBuffer[ci] = colorBuffer[ci + 1] = colorBuffer[ci + 2] = (byte)((alpha * col) / 255); colorBuffer[ci + 3] = (byte)alpha; } else { colorBuffer[ci] = colorBuffer[ci + 1] = colorBuffer[ci + 2] = col; colorBuffer[ci + 3] = (byte)(((255 - col) * black + 255 * col) / 255); } } } } else { if (blurAmount > 0) { fixed(byte *bdst = &buffer[0]) { Blur(bdst, glyph.Bounds.Width, glyph.Bounds.Height, glyph.Bounds.Width, blurAmount); } } for (var i = 0; i < bufferSize; ++i) { var ci = i * 4; var c = buffer[i]; if (premultiplyAlpha) { colorBuffer[ci] = colorBuffer[ci + 1] = colorBuffer[ci + 2] = colorBuffer[ci + 3] = c; } else { colorBuffer[ci] = colorBuffer[ci + 1] = colorBuffer[ci + 2] = 255; colorBuffer[ci + 3] = c; } } } #if MONOGAME || FNA || STRIDE // Write to texture if (Texture == null) { Texture = Texture2DManager.CreateTexture(graphicsDevice, Width, Height); } Texture2DManager.SetTextureData(Texture, glyph.Bounds, colorBuffer); #else // Write to texture if (Texture == null) { Texture = textureManager.CreateTexture(Width, Height); } textureManager.SetTextureData(Texture, glyph.Bounds, colorBuffer); #endif }
protected internal abstract FontGlyph GetGlyph(ITexture2DManager device, int codepoint);
public FontSystem(ITexture2DManager textureCreator, int width, int height, int blurAmount = 0, int strokeAmount = 0, bool premultiplyAlpha = true) : this(StbTrueTypeSharpFontLoader.Instance, textureCreator, width, height, blurAmount, strokeAmount, premultiplyAlpha) { }
protected internal override FontGlyph GetGlyph(ITexture2DManager device, int codepoint) #endif { return(GetDynamicGlyph(device, codepoint)); }