/// <summary> /// Upload a character's bitmap into the current cache. /// </summary> /// <param name="character">The character specifications corresponding to the bitmap</param> public void UploadCharacterBitmap(CommandList commandList, CharacterSpecification character) { if(character.Bitmap == null) throw new ArgumentNullException("character"); if(character.IsBitmapUploaded) throw new InvalidOperationException("The character '"+character.Character+"' upload has been requested while its current glyph is valid."); var targetSize = new Int2(character.Bitmap.Width, character.Bitmap.Rows); if (!packer.Insert(targetSize.X, targetSize.Y, ref character.Glyph.Subrect)) { // not enough space to place the new character -> remove less used characters and try again RemoveLessUsedCharacters(); if (!packer.Insert(targetSize.X, targetSize.Y, ref character.Glyph.Subrect)) { // memory is too fragmented in order to place the new character -> clear all the characters and restart. ClearCache(); if (!packer.Insert(targetSize.X, targetSize.Y, ref character.Glyph.Subrect)) throw new InvalidOperationException("The rendered character is too big for the cache texture"); } } // updload the bitmap on the texture (if the size in the bitmap is not null) if (character.Bitmap.Rows != 0 && character.Bitmap.Width != 0) { var dataBox = new DataBox(character.Bitmap.Buffer, character.Bitmap.Pitch, character.Bitmap.Pitch * character.Bitmap.Rows); var region = new ResourceRegion(character.Glyph.Subrect.Left, character.Glyph.Subrect.Top, 0, character.Glyph.Subrect.Right, character.Glyph.Subrect.Bottom, 1); commandList.UpdateSubresource(cacheTextures[0], 0, dataBox, region); } // update the glyph data character.IsBitmapUploaded = true; character.Glyph.BitmapIndex = 0; }
private static void ResetGlyph(CharacterSpecification character) { character.Glyph.Offset = Vector2.Zero; character.Glyph.XAdvance = 0; character.Glyph.BitmapIndex = 0; character.Glyph.Subrect.X = 0; character.Glyph.Subrect.Y = 0; character.Glyph.Subrect.Width = 0; character.Glyph.Subrect.Height = 0; }
public void NotifyCharacterUtilization(CharacterSpecification character) { character.LastUsedFrame = system.FrameCount; if (character.ListNode.List != null) { cachedCharacters.Remove(character.ListNode); } cachedCharacters.AddFirst(character.ListNode); }
private CharacterSpecification GetOrCreateCharacterData(Vector2 size, char character) { // build the dictionary look up key var lookUpKey = new CharacterKey(character, size); // get the entry (creates it if it does not exist) CharacterSpecification characterData; if (!sizedCharacterToCharacterData.TryGetValue(lookUpKey, out characterData)) { characterData = new CharacterSpecification(character, FontName, size, Style, AntiAlias); sizedCharacterToCharacterData[lookUpKey] = characterData; } return(characterData); }
/// <summary> /// Start the generation of the specified character's bitmap. /// </summary> /// <remarks>Does nothing if the bitmap already exist or if the generation is currently running.</remarks> /// <param name="characterSpecification">The character we want the bitmap of</param> /// <param name="synchronously">Indicate if the generation of the bitmap must by done synchronously or asynchronously</param> public void GenerateBitmap(CharacterSpecification characterSpecification, bool synchronously) { // generate the glyph info (and the bitmap if required) synchronously GenerateCharacterGlyph(characterSpecification, synchronously); // add the bitmap rendering job to a request queue if rendering is asynchronous if (!synchronously) { lock (dataStructuresLock) { if (characterSpecification.Bitmap == null && !bitmapsToGenerate.Contains(characterSpecification)) { bitmapsToGenerate.Enqueue(characterSpecification); bitmapBuildSignal.Set(); } } } }
private void GenerateCharacterGlyph(CharacterSpecification character, bool renderBitmap) { // first the possible current glyph info ResetGlyph(character); // let the glyph info null if the size is not valid if (character.Size.X < 1 || character.Size.Y < 1) { return; } // get the face of the font var fontFace = GetOrCreateFontFace(character.FontName, character.Style); lock (freetypeLibrary) { // set the font size SetFontFaceSize(fontFace, character.Size); // get the glyph and render the bitmap var glyphIndex = fontFace.GetCharIndex(character.Character); // the character does not exit => let the glyph info null if (glyphIndex == 0) { return; } // load the character glyph fontFace.LoadGlyph(glyphIndex, LoadFlags.Default, LoadTarget.Normal); // set glyph information character.Glyph.XAdvance = fontFace.Glyph.Advance.X.ToSingle(); // render the bitmap if (renderBitmap) { RenderBitmap(character, fontFace); } } }
/// <summary> /// Upload a character's bitmap into the current cache. /// </summary> /// <param name="character">The character specifications corresponding to the bitmap</param> public void UploadCharacterBitmap(CommandList commandList, CharacterSpecification character) { if (character.Bitmap == null) { throw new ArgumentNullException("character"); } if (character.IsBitmapUploaded) { throw new InvalidOperationException("The character '" + character.Character + "' upload has been requested while its current glyph is valid."); } var targetSize = new Int2(character.Bitmap.Width, character.Bitmap.Rows); if (!packer.Insert(targetSize.X, targetSize.Y, ref character.Glyph.Subrect)) { // not enough space to place the new character -> remove less used characters and try again RemoveLessUsedCharacters(); if (!packer.Insert(targetSize.X, targetSize.Y, ref character.Glyph.Subrect)) { // memory is too fragmented in order to place the new character -> clear all the characters and restart. ClearCache(); if (!packer.Insert(targetSize.X, targetSize.Y, ref character.Glyph.Subrect)) { throw new InvalidOperationException("The rendered character is too big for the cache texture"); } } } // updload the bitmap on the texture (if the size in the bitmap is not null) if (character.Bitmap.Rows != 0 && character.Bitmap.Width != 0) { var dataBox = new DataBox(character.Bitmap.Buffer, character.Bitmap.Pitch, character.Bitmap.Pitch * character.Bitmap.Rows); var region = new ResourceRegion(character.Glyph.Subrect.Left, character.Glyph.Subrect.Top, 0, character.Glyph.Subrect.Right, character.Glyph.Subrect.Bottom, 1); commandList.UpdateSubresource(cacheTextures[0], 0, dataBox, region); } // update the glyph data character.IsBitmapUploaded = true; character.Glyph.BitmapIndex = 0; }
private void RenderBitmap(CharacterSpecification character, Face fontFace) { // choose the rendering type and render the glyph var renderingMode = character.AntiAlias == FontAntiAliasMode.Aliased ? RenderMode.Mono : RenderMode.Normal; fontFace.Glyph.RenderGlyph(renderingMode); // create the bitmap var bitmap = fontFace.Glyph.Bitmap; if (bitmap.Width != 0 && bitmap.Rows != 0) { character.Bitmap = new CharacterBitmap(bitmap.Buffer, ref borderSize, bitmap.Width, bitmap.Rows, bitmap.Pitch, bitmap.GrayLevels, bitmap.PixelMode); } else { character.Bitmap = new CharacterBitmap(); } // set the glyph offsets character.Glyph.Offset = new Vector2(fontFace.Glyph.BitmapLeft - borderSize.X, -fontFace.Glyph.BitmapTop - borderSize.Y); }
public void TestGenerateBitmap() { var fontManager = new FontManager(); const int waitTime = 250; const int defaultSize = 4; // test that a simple bitmap generation success var characterA = new CharacterSpecification('a', "Arial", new Vector2(1.73f, 3.57f), FontStyle.Regular, FontAntiAliasMode.Default); fontManager.GenerateBitmap(characterA, false); WaitAndCheck(characterA, waitTime); Assert.AreEqual(4, characterA.Bitmap.Width); Assert.AreEqual(6, characterA.Bitmap.Rows); // test that rendering an already existing character to a new size works var characterA2 = new CharacterSpecification('a', "Arial", 10f * Vector2.One, FontStyle.Regular, FontAntiAliasMode.Default); fontManager.GenerateBitmap(characterA2, false); WaitAndCheck(characterA2, waitTime); Assert.AreNotEqual(2, characterA2.Bitmap.Width); Assert.AreNotEqual(4, characterA2.Bitmap.Rows); // test that trying to render a character that does not exist does not crash the system var characterTo = new CharacterSpecification('都', "Arial", defaultSize * Vector2.One, FontStyle.Regular, FontAntiAliasMode.Default); var characterB = new CharacterSpecification('b', "Arial", defaultSize * Vector2.One, FontStyle.Regular, FontAntiAliasMode.Default); fontManager.GenerateBitmap(characterTo, false); fontManager.GenerateBitmap(characterB, false); WaitAndCheck(characterB, 2 * waitTime); Assert.AreEqual(null, characterTo.Bitmap); // test that trying to render a character that does not exist does not crash the system var characterC = new CharacterSpecification('c', "Arial", -1 * Vector2.One, FontStyle.Regular, FontAntiAliasMode.Default); var characterD = new CharacterSpecification('d', "Arial", defaultSize * Vector2.One, FontStyle.Regular, FontAntiAliasMode.Default); fontManager.GenerateBitmap(characterC, false); fontManager.GenerateBitmap(characterD, false); WaitAndCheck(characterD, 2 * waitTime); Assert.AreEqual(null, characterC.Bitmap); fontManager.Dispose(); }
public static bool Equals(CharacterSpecification left, CharacterSpecification right) { return(left.Character == right.Character && left.FontName == right.FontName && left.Size == right.Size && left.Style == right.Style && left.AntiAlias == right.AntiAlias); }
private void GenerateCharacterGlyph(CharacterSpecification character, bool renderBitmap) { // first the possible current glyph info ResetGlyph(character); // let the glyph info null if the size is not valid if (character.Size.X < 1 || character.Size.Y < 1) return; // get the face of the font var fontFace = GetOrCreateFontFace(character.FontName, character.Style); lock (freetypeLibrary) { // set the font size SetFontFaceSize(fontFace, character.Size); // get the glyph and render the bitmap var glyphIndex = fontFace.GetCharIndex(character.Character); // the character does not exit => let the glyph info null if (glyphIndex == 0) return; // load the character glyph fontFace.LoadGlyph(glyphIndex, LoadFlags.Default, LoadTarget.Normal); // set glyph information character.Glyph.XAdvance = fontFace.Glyph.Advance.X.ToSingle(); // render the bitmap if (renderBitmap) RenderBitmap(character, fontFace); } }
private void RenderBitmap(CharacterSpecification character, Face fontFace) { // choose the rendering type and render the glyph var renderingMode = character.AntiAlias == FontAntiAliasMode.Aliased ? RenderMode.Mono : RenderMode.Normal; fontFace.Glyph.RenderGlyph(renderingMode); // create the bitmap var bitmap = fontFace.Glyph.Bitmap; if (bitmap.Width != 0 && bitmap.Rows != 0) character.Bitmap = new CharacterBitmap(bitmap.Buffer, ref borderSize, bitmap.Width, bitmap.Rows, bitmap.Pitch, bitmap.GrayLevels, bitmap.PixelMode); else character.Bitmap = new CharacterBitmap(); // set the glyph offsets character.Glyph.Offset = new Vector2(fontFace.Glyph.BitmapLeft - borderSize.X, -fontFace.Glyph.BitmapTop - borderSize.Y); }
public void NotifyCharacterUtilization(CharacterSpecification character) { character.LastUsedFrame = system.FrameCount; if(character.ListNode.List != null) cachedCharacters.Remove(character.ListNode); cachedCharacters.AddFirst(character.ListNode); }
public static bool Equals(CharacterSpecification left, CharacterSpecification right) { return left.Character == right.Character && left.FontName == right.FontName && left.Size == right.Size && left.Style == right.Style && left.AntiAlias == right.AntiAlias; }
private void WaitAndCheck(CharacterSpecification character, int sleepTime) { Thread.Sleep(sleepTime); Assert.AreNotEqual(null, character.Bitmap); }