public QFont(string fileName, float size, FontStyle style, QFontBuilderConfiguration config) { PrivateFontCollection pfc = new PrivateFontCollection(); pfc.AddFontFile(fileName); var fontFamily = pfc.Families[0]; if (!fontFamily.IsStyleAvailable(style)) throw new ArgumentException("Font file: " + fileName + " does not support style: " + style ); if (config == null) config = new QFontBuilderConfiguration(); TransformViewport? transToVp = null; float fontScale = 1f; if (config.TransformToCurrentOrthogProjection) transToVp = OrthogonalTransform(out fontScale); using(var font = new Font(fontFamily, size * fontScale * config.SuperSampleLevels, style)){ fontData = BuildFont(font, config, null); } if (config.ShadowConfig != null) Options.DropShadowActive = true; if (transToVp != null) Options.TransformToViewport = transToVp; if(config.UseVertexBuffer) InitVBOs(); }
public QFont(string fileName, float size, FontStyle style, QFontBuilderConfiguration config, QFontShadowConfiguration shadowConfig) { PrivateFontCollection pfc = new PrivateFontCollection(); pfc.AddFontFile(fileName); var fontFamily = pfc.Families[0]; if (!fontFamily.IsStyleAvailable(style)) { throw new ArgumentException("Font file: " + fileName + " does not support style: " + style); } if (config == null) { config = new QFontBuilderConfiguration(); } using (var font = new Font(fontFamily, size * config.SuperSampleLevels, style)) { fontData = BuildFont(font, config, shadowConfig, null); } if (shadowConfig != null) { options.DropShadowActive = true; } }
public static void CreateTextureFontFiles(string fileName, float size, FontStyle style, QFontBuilderConfiguration config, string newFontName) { QFontData fontData = null; if (config == null) { config = new QFontBuilderConfiguration(); } //dont move this into a separate method - it needs to stay in scope! PrivateFontCollection pfc = new PrivateFontCollection(); pfc.AddFontFile(fileName); var fontFamily = pfc.Families[0]; if (!fontFamily.IsStyleAvailable(style)) { throw new ArgumentException("Font file: " + fileName + " does not support style: " + style); } var font = new Font(fontFamily, size * config.SuperSampleLevels, style); //var font = ObtainFont(fileName, size * config.SuperSampleLevels, style); try { fontData = BuildFont(font, config, newFontName); } finally { if (font != null) { font.Dispose(); } } Builder.SaveQFontDataToFile(fontData, newFontName); }
public static void CreateTextureFontFiles(string fileName, float size, FontStyle style, QFontBuilderConfiguration config, string newFontName) { PrivateFontCollection pfc = new PrivateFontCollection(); pfc.AddFontFile(fileName); var fontFamily = pfc.Families[0]; if (!fontFamily.IsStyleAvailable(style)) { throw new ArgumentException("Font file: " + fileName + " does not support style: " + style); } QFontData fontData = null; if (config == null) { config = new QFontBuilderConfiguration(); } using (var font = new Font(fontFamily, size * config.SuperSampleLevels, style)) { fontData = BuildFont(font, config, newFontName); } Builder.SaveQFontDataToFile(fontData, newFontName); }
private void LoadQFontFromQFontFile(FontLoadDescription loadDescription) { var loaderConfig = loadDescription.LoaderConfig; var filePath = loadDescription.Path; var downSampleFactor = loadDescription.DownSampleFactor; if (loaderConfig == null) { loaderConfig = new QFontLoaderConfiguration(); } TransformViewport?transToVp = null; float fontScale = 1f; if (loaderConfig.TransformToCurrentOrthogProjection) { transToVp = OrthogonalTransform(out fontScale); } fontData = Builder.LoadQFontDataFromFile(filePath, downSampleFactor * fontScale, loaderConfig); fontData.scaleDueToTransformToViewport = fontScale; if (loaderConfig.ShadowConfig != null) { Options.DropShadowActive = true; } if (transToVp != null) { Options.TransformToViewport = transToVp; } }
public void Dispose() { foreach (var v in fontData.Pages) { v.Dispose(); } fontData = null; }
private static QFont BuildDropShadow(List <QBitmap> sourceFontSheets, QFontGlyph[] sourceFontGlyphs, QFontShadowConfiguration shadowConfig, char[] charSet, byte alphaTolerance) { QFontGlyph[] newGlyphs; var sourceBitmapData = new List <BitmapData>(); foreach (var sourceSheet in sourceFontSheets) { sourceBitmapData.Add(sourceSheet.bitmapData); } //GenerateBitmapSheetsAndRepack(QFontGlyph[] sourceGlyphs, BitmapData[] sourceBitmaps, int destSheetWidth, int destSheetHeight, out QFontGlyph[] destGlyphs, int destMargin, bool usePowerOfTwo) var bitmapSheets = GenerateBitmapSheetsAndRepack(sourceFontGlyphs, sourceBitmapData.ToArray(), shadowConfig.PageWidth, shadowConfig.PageHeight, out newGlyphs, shadowConfig.GlyphMargin + shadowConfig.blurRadius * 3, shadowConfig.ForcePowerOfTwo); //scale up in case we wanted bigger/smaller shadows if (shadowConfig.Scale != 1.0f) { ScaleSheetsAndGlyphs(bitmapSheets, newGlyphs, shadowConfig.Scale); //no point in retargeting yet, since we will do it after blur } //blacken and blur foreach (var bitmapSheet in bitmapSheets) { bitmapSheet.Colour32(0, 0, 0); bitmapSheet.BlurAlpha(shadowConfig.blurRadius, shadowConfig.blurPasses); } //retarget after blur and scale RetargetAllGlyphs(bitmapSheets, newGlyphs, alphaTolerance); //create list of texture pages var newTextureSheets = new List <TexturePage>(); foreach (var page in bitmapSheets) { newTextureSheets.Add(new TexturePage(page.bitmapData)); } var fontData = new QFontData(); fontData.CharSetMapping = new Dictionary <char, QFontGlyph>(); for (int i = 0; i < charSet.Length; i++) { fontData.CharSetMapping.Add(charSet[i], newGlyphs[i]); } fontData.Pages = newTextureSheets.ToArray(); fontData.CalculateMeanWidth(); fontData.CalculateMaxHeight(); foreach (var sheet in bitmapSheets) { sheet.Free(); } return(new QFont(fontData)); }
public void MeasureNodes(QFontData fontData, QFontRenderOptions options) { foreach (TextNode node in this) { if (node.Length == 0f) { node.Length = MeasureTextNodeLength(node, fontData, options); } } }
public static void SaveQFontDataToFile(QFontData data, string filePath) { var lines = data.Serialize(); StreamWriter writer = new StreamWriter(filePath + ".qfont"); foreach (var line in lines) writer.WriteLine(line); writer.Close(); }
/// <summary> /// Measures each text node using the specified font data and render options /// </summary> /// <param name="fontData">The font data to use for measuring</param> /// <param name="options">The render options</param> public void MeasureNodes(QFontData fontData, QFontRenderOptions options) { foreach (TextNode node in this) { if (Math.Abs(node.Length) < float.Epsilon) { node.Length = MeasureTextNodeLength(node, fontData, options); } } }
public QFont(Font font, QFontBuilderConfiguration config) { if (config == null) config = new QFontBuilderConfiguration(); fontData = BuildFont(font, config, null); if (config.ShadowConfig != null) Options.DropShadowActive = true; }
public QFont(IFontRenderer renderer, Font font, QFontBuilderConfiguration config = null) : this(renderer) { if (config == null) config = new QFontBuilderConfiguration(); fontData = BuildFont(font, config, null); LoadTextures(fontData); if (config.ShadowConfig != null) Options.DropShadowActive = true; }
public QFont(Font font, QFontBuilderConfiguration config) { if (config == null) { config = new QFontBuilderConfiguration(); } fontData = BuildFont(font, config, null); if (config.ShadowConfig != null) { Options.DropShadowActive = true; } }
private void LoadQFontFromFontFile(FontLoadDescription loadDescription) { var config = loadDescription.BuilderConfig; var fileName = loadDescription.Path; var size = loadDescription.Size; var style = loadDescription.Style; if (config == null) { config = new QFontBuilderConfiguration(); } TransformViewport?transToVp = null; float fontScale = 1f; if (config.TransformToCurrentOrthogProjection) { transToVp = OrthogonalTransform(out fontScale); } //dont move this into a separate method - it needs to stay in scope! PrivateFontCollection pfc = new PrivateFontCollection(); pfc.AddFontFile(fileName); var fontFamily = pfc.Families[0]; if (!fontFamily.IsStyleAvailable(style)) { throw new ArgumentException("Font file: " + fileName + " does not support style: " + style); } var font = new Font(fontFamily, size * fontScale * config.SuperSampleLevels, style); //var font = ObtainFont(fileName, size * fontScale * config.SuperSampleLevels, style) fontData = BuildFont(font, config, null); fontData.scaleDueToTransformToViewport = fontScale; font.Dispose(); if (config.ShadowConfig != null) { Options.DropShadowActive = true; } if (transToVp != null) { Options.TransformToViewport = transToVp; } }
/// <summary> /// Saves the <see cref="QFontData"/> to the specified file /// This is used for loading custom texture fonts /// </summary> /// <param name="data">The <see cref="QFontData"/> to save</param> /// <param name="filePath">The filepath</param> public static void SaveQFontDataToFile(QFontData data, string filePath) { // Serialize the font data var lines = data.Serialize(); // Write it to the file StreamWriter writer = new StreamWriter(filePath + ".qfont"); foreach (var line in lines) { writer.WriteLine(line); } writer.Close(); }
public QFont(Font font, QFontBuilderConfiguration config) { optionsStack.Push(new QFontRenderOptions()); if (config == null) config = new QFontBuilderConfiguration(); fontData = BuildFont(font, config, null); if (config.ShadowConfig != null) Options.DropShadowActive = true; if (config.UseVertexBuffer) InitVBOs(); }
public QFont(string fileName, float size, FontStyle style, QFontBuilderConfiguration config) { PrivateFontCollection pfc = new PrivateFontCollection(); pfc.AddFontFile(fileName); var fontFamily = pfc.Families[0]; if (!fontFamily.IsStyleAvailable(style)) { throw new ArgumentException("Font file: " + fileName + " does not support style: " + style); } if (config == null) { config = new QFontBuilderConfiguration(); } TransformViewport?transToVp = null; float fontScale = 1f; if (config.TransformToCurrentOrthogProjection) { transToVp = OrthogonalTransform(out fontScale); } using (var font = new Font(fontFamily, size * fontScale * config.SuperSampleLevels, style)) { fontData = BuildFont(font, config, null); } if (config.ShadowConfig != null) { Options.DropShadowActive = true; } if (transToVp != null) { Options.TransformToViewport = transToVp; } if (config.UseVertexBuffer) { InitVBOs(); } }
private void InitialiseGlFont(Font font, QFontBuilderConfiguration config, QFontData data = null) { _fontData = data ?? BuildFont(font, config, null); // Check and fail if more than one texture was generated. The original implementation of QFont supported // this by choosing them as the come but this ModernOpenGl -implementation would be handycapped by // allowing this degree of freedom. It is now possible to call DrawArrays for whole texts (requiring // shadows and text to be each 1 texture). This is quite efficient. // To cover it from another aspect: OpenGL 3.1 and more easily allow Textures of up to 8129² not // necessrily being base2 and square - this generousity should be hapily used and effiency be gained. // So there will be no implementation of VAO VBO based "Modern" OpenGL that is limited to 512 textures. // So this is a well takeable tradeoff if (_fontData.Pages.Length != 1 || (_fontData.dropShadowFont != null && _fontData.dropShadowFont.FontData.Pages.Length != 1)) { throw new NotSupportedException("The implementation of QFontDrawing does not support multiple textures per Font/Shadow. " + "Thus this font can not be properly rendered in all cases. Reduce number of characters " + "or increase QFontBuilderConfiguration.MaxTexSize QFontShadowConfiguration.PageMaxTextureSize " + "to contain all characters/char-shadows in one Bitmap=>Texture."); } }
private float MeasureTextNodeLength(TextNode node, QFontData fontData, QFontRenderOptions options) { bool monospaced = fontData.IsMonospacingActive(options); float monospaceWidth = fontData.GetMonoSpaceWidth(options); if (node.Type == TextNodeType.Space) { if (monospaced) { return(monospaceWidth); } return((float)Math.Ceiling(fontData.meanGlyphWidth * options.WordSpacing)); } float length = 0f; float height = 0f; if (node.Type == TextNodeType.Word) { for (int i = 0; i < node.Text.Length; i++) { char c = node.Text[i]; if (fontData.CharSetMapping.ContainsKey(c)) { var glyph = fontData.CharSetMapping[c]; if (monospaced) { length += monospaceWidth; } else { length += (float)Math.Ceiling(fontData.CharSetMapping[c].rect.Width + fontData.meanGlyphWidth * options.CharacterSpacing + fontData.GetKerningPairCorrection(i, node.Text, node)); } height = Math.Max(height, glyph.yOffset + glyph.rect.Height); } } } node.Height = height; return(length); }
public QFont(Font font, QFontBuilderConfiguration config) { optionsStack.Push(new QFontRenderOptions()); if (config == null) { config = new QFontBuilderConfiguration(); } fontData = BuildFont(font, config, null); if (config.ShadowConfig != null) { Options.DropShadowActive = true; } if (config.UseVertexBuffer) { InitVBOs(); } }
private float MeasureTextNodeLength(TextNode node, QFontData fontData, QFontRenderOptions options) { bool monospaced = fontData.IsMonospacingActive(options); float monospaceWidth = fontData.GetMonoSpaceWidth(options); if (node.Type == TextNodeType.Space) { if (monospaced) { return(monospaceWidth); } return((float)Math.Ceiling(fontData.meanGlyphWidth * options.WordSpacing)); } float length = 0f; if (node.Type == TextNodeType.Word) { for (int i = 0; i < node.Text.Length; i++) { char c = node.Text[i]; QFontGlyph glyph; if (fontData.CharSetMapping.TryGetValue(c, out glyph)) { if (monospaced) { length += monospaceWidth; } else { length += (float)Math.Ceiling(glyph.rect.Width + fontData.meanGlyphWidth * options.CharacterSpacing + fontData.GetKerningPairCorrection(i, node.Text, node)); } } } } return(length); }
public QFont(string fontname, byte[] fontresource, float size, FontStyle style, QFontBuilderConfiguration config, QFontShadowConfiguration shadowConfig) { // This should be probably a field of some class PrivateFontCollection pfc = new PrivateFontCollection(); // allocate memory and copy byte[] to the location IntPtr data = Marshal.AllocCoTaskMem(fontresource.Length); Marshal.Copy(fontresource, 0, data, fontresource.Length); // pass the font to the font collection pfc.AddMemoryFont(data, fontresource.Length); var fontFamily = pfc.Families[0]; if (!fontFamily.IsStyleAvailable(style)) { throw new ArgumentException("Font Resource: " + fontname + " does not support style: " + style); } if (config == null) { config = new QFontBuilderConfiguration(); } using (var font = new Font(fontFamily, size * config.SuperSampleLevels, style)) { fontData = BuildFont(font, config, shadowConfig, null); } if (shadowConfig != null) { options.DropShadowActive = true; } // Free the unsafe memory Marshal.FreeCoTaskMem(data); }
internal static ProcessedText ProcessText(QFontData font, QFontRenderOptions options, string text, SizeF maxSize, QFontAlignment alignment) { //TODO: bring justify and alignment calculations in here maxSize.Width = TransformWidthToViewport(maxSize.Width, options); var nodeList = new TextNodeList(text); nodeList.MeasureNodes(font, options); //we "crumble" words that are two long so that that can be split up var nodesToCrumble = new List <TextNode>(); foreach (TextNode node in nodeList) { if ((!options.WordWrap || node.Length >= maxSize.Width) && node.Type == TextNodeType.Word) { nodesToCrumble.Add(node); } } foreach (var node in nodesToCrumble) { nodeList.Crumble(node, 1); } //need to measure crumbled words nodeList.MeasureNodes(font, options); var processedText = new ProcessedText { textNodeList = nodeList, maxSize = maxSize, alignment = alignment }; return(processedText); }
private float MeasureTextNodeLength(TextNode node, QFontData fontData, QFontRenderOptions options) { bool monospaced = fontData.IsMonospacingActive(options); float monospaceWidth = fontData.GetMonoSpaceWidth(options); if (node.Type == TextNodeType.Space) { if (monospaced) return monospaceWidth; return (float)Math.Ceiling(fontData.meanGlyphWidth * options.WordSpacing); } float length = 0f; if (node.Type == TextNodeType.Word) { for (int i = 0; i < node.Text.Length; i++) { char c = node.Text[i]; if (fontData.CharSetMapping.ContainsKey(c)) { if (monospaced) length += monospaceWidth; else length += (float)Math.Ceiling(fontData.CharSetMapping[c].rect.Width + fontData.meanGlyphWidth * options.CharacterSpacing + fontData.GetKerningPairCorrection(i, node.Text, node)); } } } return length; }
private void LoadQFontFromFontFile(FontLoadDescription loadDescription) { var config = loadDescription.BuilderConfig; var fileName = loadDescription.Path; var size = loadDescription.Size; var style = loadDescription.Style; if (config == null) config = new QFontBuilderConfiguration(); TransformViewport? transToVp = null; float fontScale = 1f; if (config.TransformToCurrentOrthogProjection) transToVp = OrthogonalTransform(out fontScale); //dont move this into a separate method - it needs to stay in scope! PrivateFontCollection pfc = new PrivateFontCollection(); pfc.AddFontFile(fileName); var fontFamily = pfc.Families[0]; if (!fontFamily.IsStyleAvailable(style)) throw new ArgumentException("Font file: " + fileName + " does not support style: " + style); var font = new Font(fontFamily, size * fontScale * config.SuperSampleLevels, style); //var font = ObtainFont(fileName, size * fontScale * config.SuperSampleLevels, style) fontData = BuildFont(font, config, null); fontData.scaleDueToTransformToViewport = fontScale; font.Dispose(); if (config.ShadowConfig != null) Options.DropShadowActive = true; if (transToVp != null) Options.TransformToViewport = transToVp; }
public static void CreateTextureFontFiles(string fileName, float size, FontStyle style, QFontBuilderConfiguration config, string newFontName) { QFontData fontData = null; if (config == null) config = new QFontBuilderConfiguration(); //dont move this into a separate method - it needs to stay in scope! PrivateFontCollection pfc = new PrivateFontCollection(); pfc.AddFontFile(fileName); var fontFamily = pfc.Families[0]; if (!fontFamily.IsStyleAvailable(style)) throw new ArgumentException("Font file: " + fileName + " does not support style: " + style); var font = new Font(fontFamily, size * config.SuperSampleLevels, style); //var font = ObtainFont(fileName, size * config.SuperSampleLevels, style); try { fontData = BuildFont(font, config, newFontName); } finally { if (font != null) font.Dispose(); } Builder.SaveQFontDataToFile(fontData, newFontName); }
public static QFontData LoadQFontDataFromFile(string filePath, float downSampleFactor, QFontConfiguration loaderConfig) { var lines = new List <String>(); StreamReader reader = new StreamReader(filePath); string line; while ((line = reader.ReadLine()) != null) { lines.Add(line); } reader.Close(); var data = new QFontData(); int pageCount = 0; char[] charSet; data.Deserialize(lines, out pageCount, out charSet); string namePrefix = filePath.Replace(".qfont", "").Replace(" ", ""); var bitmapPages = new List <QBitmap>(); if (pageCount == 1) { bitmapPages.Add(new QBitmap(namePrefix + ".png")); } else { for (int i = 0; i < pageCount; i++) { bitmapPages.Add(new QBitmap(namePrefix + "_sheet_" + i)); } } foreach (var glyph in data.CharSetMapping.Values) { RetargetGlyphRectangleOutwards(bitmapPages[glyph.page].bitmapData, glyph, false, loaderConfig.KerningConfig.alphaEmptyPixelTolerance); } var intercept = FirstIntercept(data.CharSetMapping); if (intercept != null) { throw new Exception("Failed to load font from file. Glyphs '" + intercept[0] + "' and '" + intercept[1] + "' were overlapping. If you are texturing your font without locking pixel opacity, then consider using a larger glyph margin. This can be done by setting QFontBuilderConfiguration myQfontBuilderConfig.GlyphMargin, and passing it into CreateTextureFontFiles."); } if (downSampleFactor > 1.0f) { foreach (var page in bitmapPages) { page.DownScale32((int)(page.bitmap.Width * downSampleFactor), (int)(page.bitmap.Height * downSampleFactor)); } foreach (var glyph in data.CharSetMapping.Values) { glyph.rect = new Rectangle((int)(glyph.rect.X * downSampleFactor), (int)(glyph.rect.Y * downSampleFactor), (int)(glyph.rect.Width * downSampleFactor), (int)(glyph.rect.Height * downSampleFactor)); glyph.yOffset = (int)(glyph.yOffset * downSampleFactor); } } else if (downSampleFactor < 1.0f) { // If we were simply to shrink the entire texture, then at some point we will make glyphs overlap, breaking the font. // For this reason it is necessary to copy every glyph to a separate bitmap, and then shrink each bitmap individually. QFontGlyph[] shrunkGlyphs; QBitmap[] shrunkBitmapsPerGlyph; CreateBitmapPerGlyph(Helper.ToArray(data.CharSetMapping.Values), bitmapPages.ToArray(), out shrunkGlyphs, out shrunkBitmapsPerGlyph); //shrink each bitmap for (int i = 0; i < shrunkGlyphs.Length; i++) { var bmp = shrunkBitmapsPerGlyph[i]; bmp.DownScale32(Math.Max((int)(bmp.bitmap.Width * downSampleFactor), 1), Math.Max((int)(bmp.bitmap.Height * downSampleFactor), 1)); shrunkGlyphs[i].rect = new Rectangle(0, 0, bmp.bitmap.Width, bmp.bitmap.Height); shrunkGlyphs[i].yOffset = (int)(shrunkGlyphs[i].yOffset * downSampleFactor); } var shrunkBitmapData = new BitmapData[shrunkBitmapsPerGlyph.Length]; for (int i = 0; i < shrunkBitmapsPerGlyph.Length; i++) { shrunkBitmapData[i] = shrunkBitmapsPerGlyph[i].bitmapData; } //use roughly the same number of pages as before.. int newWidth = (int)(bitmapPages[0].bitmap.Width * (0.1f + downSampleFactor)); int newHeight = (int)(bitmapPages[0].bitmap.Height * (0.1f + downSampleFactor)); //free old bitmap pages since we are about to chuck them away for (int i = 0; i < pageCount; i++) { bitmapPages[i].Free(); } QFontGlyph[] shrunkRepackedGlyphs; bitmapPages = GenerateBitmapSheetsAndRepack(shrunkGlyphs, shrunkBitmapData, newWidth, newHeight, out shrunkRepackedGlyphs, 4); data.CharSetMapping = CreateCharGlyphMapping(shrunkRepackedGlyphs); foreach (var bmp in shrunkBitmapsPerGlyph) { bmp.Free(); } pageCount = bitmapPages.Count; } data.Pages = new TexturePage[pageCount]; for (int i = 0; i < pageCount; i++) { data.Pages[i] = new TexturePage(bitmapPages[i].bitmapData); } if (downSampleFactor != 1.0f) { foreach (var glyph in data.CharSetMapping.Values) { RetargetGlyphRectangleOutwards(bitmapPages[glyph.page].bitmapData, glyph, false, loaderConfig.KerningConfig.alphaEmptyPixelTolerance); } intercept = FirstIntercept(data.CharSetMapping); if (intercept != null) { throw new Exception("Failed to load font from file. Glyphs '" + intercept[0] + "' and '" + intercept[1] + "' were overlapping. This occurred only after resizing your texture font, implying that there is a bug in QFont. "); } } var glyphList = new List <QFontGlyph>(); foreach (var c in charSet) { glyphList.Add(data.CharSetMapping[c]); } if (loaderConfig.ShadowConfig != null) { data.dropShadowFont = BuildDropShadow(bitmapPages, glyphList.ToArray(), loaderConfig.ShadowConfig, Helper.ToArray(charSet), loaderConfig.KerningConfig.alphaEmptyPixelTolerance); } data.KerningPairs = KerningCalculator.CalculateKerning(Helper.ToArray(charSet), glyphList.ToArray(), bitmapPages, loaderConfig.KerningConfig); data.CalculateMeanWidth(); data.CalculateMaxHeight(); for (int i = 0; i < pageCount; i++) { bitmapPages[i].Free(); } return(data); }
public QFontData BuildFontData(string saveName) { if (config.SuperSampleLevels <= 0 || config.SuperSampleLevels > 8) { throw new ArgumentOutOfRangeException("SuperSampleLevels = [" + config.SuperSampleLevels + "] is an unsupported value. Please use values in the range [1,8]"); } int margin = 3; //margin in initial bitmap (don't bother to make configurable - likely to cause confusion int glyphMargin = config.GlyphMargin * config.SuperSampleLevels; QFontGlyph[] initialGlyphs; var sizes = GetGlyphSizes(font); var maxSize = GetMaxGlyphSize(sizes); var initialBmp = CreateInitialBitmap(font, maxSize, margin, out initialGlyphs, config.TextGenerationRenderHint); var initialBitmapData = initialBmp.LockBits(new Rectangle(0, 0, initialBmp.Width, initialBmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); int minYOffset = int.MaxValue; foreach (var glyph in initialGlyphs) { RetargetGlyphRectangleInwards(initialBitmapData, glyph, true, config.KerningConfig.alphaEmptyPixelTolerance); minYOffset = Math.Min(minYOffset, glyph.yOffset); } minYOffset--; //give one pixel of breathing room? foreach (var glyph in initialGlyphs) { glyph.yOffset -= minYOffset; } Size pagesize = GetOptimalPageSize(initialBmp.Width * config.SuperSampleLevels, initialBmp.Height * config.SuperSampleLevels, config.PageMaxTextureSize); QFontGlyph[] glyphs; List <QBitmap> bitmapPages = GenerateBitmapSheetsAndRepack(initialGlyphs, new BitmapData[1] { initialBitmapData }, pagesize.Width, pagesize.Height, out glyphs, glyphMargin); initialBmp.UnlockBits(initialBitmapData); initialBmp.Dispose(); if (config.SuperSampleLevels != 1) { ScaleSheetsAndGlyphs(bitmapPages, glyphs, 1.0f / config.SuperSampleLevels); RetargetAllGlyphs(bitmapPages, glyphs, config.KerningConfig.alphaEmptyPixelTolerance); } //create list of texture pages var pages = new List <TexturePage>(); foreach (var page in bitmapPages) { pages.Add(new TexturePage(page.bitmapData)); } var fontData = new QFontData(); fontData.CharSetMapping = CreateCharGlyphMapping(glyphs); fontData.Pages = pages.ToArray(); fontData.CalculateMeanWidth(); fontData.CalculateMaxHeight(); fontData.KerningPairs = KerningCalculator.CalculateKerning(charSet.ToCharArray(), glyphs, bitmapPages, config.KerningConfig); fontData.naturallyMonospaced = IsMonospaced(sizes); if (saveName != null) { if (bitmapPages.Count == 1) { bitmapPages[0].bitmap.UnlockBits(bitmapPages[0].bitmapData); bitmapPages[0].bitmap.Save(saveName + ".png", ImageFormat.Png); bitmapPages[0] = new QBitmap(bitmapPages[0].bitmap); } else { for (int i = 0; i < bitmapPages.Count; i++) { bitmapPages[i].bitmap.UnlockBits(bitmapPages[i].bitmapData); bitmapPages[i].bitmap.Save(saveName + "_sheet_" + i + ".png", ImageFormat.Png); bitmapPages[i] = new QBitmap(bitmapPages[i].bitmap); } } } if (config.ShadowConfig != null) { fontData.dropShadowFont = BuildDropShadow(bitmapPages, glyphs, config.ShadowConfig, charSet.ToCharArray(), config.KerningConfig.alphaEmptyPixelTolerance); } foreach (var page in bitmapPages) { page.Free(); } //validate glyphs var intercept = FirstIntercept(fontData.CharSetMapping); if (intercept != null) { throw new Exception("Failed to create glyph set. Glyphs '" + intercept[0] + "' and '" + intercept[1] + "' were overlapping. This is could be due to an error in the font, or a bug in Graphics.MeasureString()."); } return(fontData); }
/// <summary> /// Renders the glyph at the position given. /// </summary> /// <param name="x">The x.</param> /// <param name="y">The y.</param> /// <param name="c">The character to print.</param> /// <param name="font">font used for render</param> /// <param name="store">target vertex buffer</param> internal void RenderGlyph(float x, float y, char c, QFontData fontdata, List <QVertex> store, ref Rectangle clippingRectangle) { var glyph = fontdata.CharSetMapping[c]; //note: it's not immediately obvious, but this combined with the paramteters to //RenderGlyph for the shadow mean that we render the shadow centrally (despite it being a different size) //under the glyph if (fontdata.isDropShadow) { x -= (int)(glyph.rect.Width * 0.5f); y -= (int)(glyph.rect.Height * 0.5f + glyph.yOffset); } else { RenderDropShadow(x, y, c, glyph, fontdata.dropShadowFont?.FontData, ref clippingRectangle); } y = -y; var sheet = fontdata.Pages[glyph.page]; var tx1 = (float)glyph.rect.X / sheet.Width; var ty1 = (float)glyph.rect.Y / sheet.Height; var tx2 = (float)(glyph.rect.X + glyph.rect.Width) / sheet.Width; var ty2 = (float)(glyph.rect.Y + glyph.rect.Height) / sheet.Height; var vx = x + PrintOffset.X; var vy = y - glyph.yOffset + PrintOffset.Y; float vwidth = glyph.rect.Width; float vheight = glyph.rect.Height; if (clippingRectangle != default(Rectangle) && ScissorsTest(ref vx, ref vy, ref vwidth, ref vheight, ref tx1, ref ty1, ref tx2, ref ty2, clippingRectangle)) { return; } var tv1 = new Vector2(tx1, ty1); var tv2 = new Vector2(tx1, ty2); var tv3 = new Vector2(tx2, ty2); var tv4 = new Vector2(tx2, ty1); var v1 = new Vector3(vx, vy, PrintOffset.Z); var v2 = new Vector3(vx, vy - vheight, PrintOffset.Z); var v3 = new Vector3(vx + vwidth, vy - vheight, PrintOffset.Z); var v4 = new Vector3(vx + vwidth, vy, PrintOffset.Z); var color = fontdata.isDropShadow ? Options.DropShadowColour : Options.Colour; var colour = Helper.ToVector4(color); store.Add(new QVertex { Position = v1, TextureCoord = tv1, VertexColor = colour }); store.Add(new QVertex { Position = v2, TextureCoord = tv2, VertexColor = colour }); store.Add(new QVertex { Position = v3, TextureCoord = tv3, VertexColor = colour }); store.Add(new QVertex { Position = v1, TextureCoord = tv1, VertexColor = colour }); store.Add(new QVertex { Position = v3, TextureCoord = tv3, VertexColor = colour }); store.Add(new QVertex { Position = v4, TextureCoord = tv4, VertexColor = colour }); }
private void InitialiseGlFont(Font font, QFontBuilderConfiguration config, QFontData data = null) { _fontData = data ?? BuildFont(font, config, null); // Check and fail if more than one texture was generated. The original implementation of QFont supported // this by choosing them as the come but this ModernOpenGl -implementation would be handycapped by // allowing this degree of freedom. It is now possible to call DrawArrays for whole texts (requiring // shadows and text to be each 1 texture). This is quite efficient. // To cover it from another aspect: OpenGL 3.1 and more easily allow Textures of up to 8129² not // necessrily being base2 and square - this generousity should be hapily used and effiency be gained. // So there will be no implementation of VAO VBO based "Modern" OpenGL that is limited to 512 textures. // So this is a well takeable tradeoff if( _fontData.Pages.Length != 1 || (_fontData.dropShadowFont != null && _fontData.dropShadowFont.FontData.Pages.Length != 1)) { throw new NotSupportedException("The implementation of QFontDrawing does not support multiple textures per Font/Shadow. " + "Thus this font can not be properly rendered in all cases. Reduce number of characters " + "or increase QFontBuilderConfiguration.MaxTexSize QFontShadowConfiguration.PageMaxTextureSize " + "to contain all characters/char-shadows in one Bitmap=>Texture."); } }
public static void CreateTextureFontFiles(Font font, string newFontName, QFontBuilderConfiguration config) { QFontData fontData = BuildFont(font, config, newFontName); Builder.SaveQFontDataToFile(fontData, newFontName); }
public QFont(string fontname, byte[] fontresource, float size, FontStyle style, QFontBuilderConfiguration config, QFontShadowConfiguration shadowConfig) { // This should be probably a field of some class PrivateFontCollection pfc = new PrivateFontCollection(); // allocate memory and copy byte[] to the location IntPtr data = Marshal.AllocCoTaskMem(fontresource.Length); Marshal.Copy(fontresource, 0, data, fontresource.Length); // pass the font to the font collection pfc.AddMemoryFont(data, fontresource.Length); var fontFamily = pfc.Families[0]; if (!fontFamily.IsStyleAvailable(style)) throw new ArgumentException("Font Resource: " + fontname + " does not support style: " + style); if (config == null) config = new QFontBuilderConfiguration(); using (var font = new Font(fontFamily, size * config.SuperSampleLevels, style)) { fontData = BuildFont(font, config, shadowConfig, null); } if (shadowConfig != null) options.DropShadowActive = true; // Free the unsafe memory Marshal.FreeCoTaskMem(data); }
public static QFontData LoadQFontDataFromFile(string filePath, float downSampleFactor, QFontLoaderConfiguration loaderConfig) { var lines = new List<String>(); StreamReader reader = new StreamReader(filePath); string line; while((line = reader.ReadLine()) != null) lines.Add(line); reader.Close(); var data = new QFontData(); int pageCount = 0; char[] charSet; data.Deserialize(lines, out pageCount, out charSet); string namePrefix = filePath.Replace(".qfont","").Replace(" ", ""); var bitmapPages = new List<QBitmap>(); if (pageCount == 1) { bitmapPages.Add(new QBitmap(namePrefix + ".png")); } else { for (int i = 0; i < pageCount; i++) bitmapPages.Add(new QBitmap(namePrefix + "_sheet_" + i)); } foreach (var glyph in data.CharSetMapping.Values) RetargetGlyphRectangleOutwards(bitmapPages[glyph.page].BitmapData, glyph, false, loaderConfig.KerningConfig.alphaEmptyPixelTolerance); var intercept = FirstIntercept(data.CharSetMapping); if (intercept != null) { throw new Exception("Failed to load font from file. Glyphs '" + intercept[0] + "' and '" + intercept[1] + "' were overlapping. If you are texturing your font without locking pixel opacity, then consider using a larger glyph margin. This can be done by setting QFontBuilderConfiguration myQfontBuilderConfig.GlyphMargin, and passing it into CreateTextureFontFiles."); } if (downSampleFactor > 1.0f) { foreach (var page in bitmapPages) page.DownScale32((int)(page.Bitmap.Width * downSampleFactor), (int)(page.Bitmap.Height * downSampleFactor)); foreach (var glyph in data.CharSetMapping.Values) { glyph.rect = new Rectangle((int)(glyph.rect.X * downSampleFactor), (int)(glyph.rect.Y * downSampleFactor), (int)(glyph.rect.Width * downSampleFactor), (int)(glyph.rect.Height * downSampleFactor)); glyph.yOffset = (int)(glyph.yOffset * downSampleFactor); } } else if (downSampleFactor < 1.0f ) { // If we were simply to shrink the entire texture, then at some point we will make glyphs overlap, breaking the font. // For this reason it is necessary to copy every glyph to a separate bitmap, and then shrink each bitmap individually. QFontGlyph[] shrunkGlyphs; QBitmap[] shrunkBitmapsPerGlyph; CreateBitmapPerGlyph(Helper.ToArray(data.CharSetMapping.Values), bitmapPages.ToArray(), out shrunkGlyphs, out shrunkBitmapsPerGlyph); //shrink each bitmap for (int i = 0; i < shrunkGlyphs.Length; i++) { var bmp = shrunkBitmapsPerGlyph[i]; bmp.DownScale32(Math.Max((int)(bmp.Bitmap.Width * downSampleFactor),1), Math.Max((int)(bmp.Bitmap.Height * downSampleFactor),1)); shrunkGlyphs[i].rect = new Rectangle(0, 0, bmp.Bitmap.Width, bmp.Bitmap.Height); shrunkGlyphs[i].yOffset = (int)(shrunkGlyphs[i].yOffset * downSampleFactor); } var shrunkBitmapData = new BitmapData[shrunkBitmapsPerGlyph.Length]; for(int i = 0; i < shrunkBitmapsPerGlyph.Length; i ++ ){ shrunkBitmapData[i] = shrunkBitmapsPerGlyph[i].BitmapData; } //use roughly the same number of pages as before.. int newWidth = (int)(bitmapPages[0].Bitmap.Width * (0.1f + downSampleFactor)); int newHeight = (int)(bitmapPages[0].Bitmap.Height * (0.1f + downSampleFactor)); //free old bitmap pages since we are about to chuck them away for (int i = 0; i < pageCount; i++) bitmapPages[i].Free(); QFontGlyph[] shrunkRepackedGlyphs; bitmapPages = GenerateBitmapSheetsAndRepack(shrunkGlyphs, shrunkBitmapData, newWidth, newHeight, out shrunkRepackedGlyphs, 4, false); data.CharSetMapping = CreateCharGlyphMapping(shrunkRepackedGlyphs); pageCount = bitmapPages.Count; } data.Pages = bitmapPages.ToArray(); if (downSampleFactor != 1.0f) { foreach (var glyph in data.CharSetMapping.Values) RetargetGlyphRectangleOutwards(bitmapPages[glyph.page].BitmapData, glyph, false, loaderConfig.KerningConfig.alphaEmptyPixelTolerance); intercept = FirstIntercept(data.CharSetMapping); if (intercept != null) { throw new Exception("Failed to load font from file. Glyphs '" + intercept[0] + "' and '" + intercept[1] + "' were overlapping. This occurred only after resizing your texture font, implying that there is a bug in QFont. "); } } var glyphList = new List<QFontGlyph>(); foreach (var c in charSet) glyphList.Add(data.CharSetMapping[c]); if (loaderConfig.ShadowConfig != null) data.dropShadow = BuildDropShadow(bitmapPages, glyphList.ToArray(), loaderConfig.ShadowConfig, Helper.ToArray(charSet),loaderConfig.KerningConfig.alphaEmptyPixelTolerance); data.KerningPairs = KerningCalculator.CalculateKerning(Helper.ToArray(charSet), glyphList.ToArray(), bitmapPages, loaderConfig.KerningConfig); data.CalculateMeanWidth(); data.CalculateMaxHeight(); return data; }
private static QFontData BuildDropShadow(List<QBitmap> sourceFontSheets, QFontGlyph[] sourceFontGlyphs, QFontShadowConfiguration shadowConfig, char[] charSet, byte alphaTolerance) { QFontGlyph[] newGlyphs; var sourceBitmapData = new List<BitmapData>(); foreach(var sourceSheet in sourceFontSheets) sourceBitmapData.Add(sourceSheet.BitmapData); //GenerateBitmapSheetsAndRepack(QFontGlyph[] sourceGlyphs, BitmapData[] sourceBitmaps, int destSheetWidth, int destSheetHeight, out QFontGlyph[] destGlyphs, int destMargin, bool usePowerOfTwo) var bitmapSheets = GenerateBitmapSheetsAndRepack(sourceFontGlyphs, sourceBitmapData.ToArray(), shadowConfig.PageWidth, shadowConfig.PageHeight, out newGlyphs, shadowConfig.GlyphMargin + shadowConfig.blurRadius*3, shadowConfig.ForcePowerOfTwo); //scale up in case we wanted bigger/smaller shadows if (shadowConfig.Scale != 1.0f) ScaleSheetsAndGlyphs(bitmapSheets, newGlyphs, shadowConfig.Scale); //no point in retargeting yet, since we will do it after blur //blacken and blur foreach (var bitmapSheet in bitmapSheets) { bitmapSheet.Colour32(0, 0, 0); bitmapSheet.BlurAlpha(shadowConfig.blurRadius, shadowConfig.blurPasses); } //retarget after blur and scale RetargetAllGlyphs(bitmapSheets, newGlyphs, alphaTolerance); var fontData = new QFontData(); fontData.CharSetMapping = new Dictionary<char, QFontGlyph>(); for(int i = 0; i < charSet.Length; i++) fontData.CharSetMapping.Add(charSet[i],newGlyphs[i]); fontData.Pages = bitmapSheets.ToArray(); fontData.CalculateMeanWidth(); fontData.CalculateMaxHeight(); return fontData; }
public QFontData BuildFontData(string saveName) { if (config.ForcePowerOfTwo && config.SuperSampleLevels != PowerOfTwo(config.SuperSampleLevels)) { throw new ArgumentOutOfRangeException("SuperSampleLevels must be a power of two when using ForcePowerOfTwo."); } if (config.SuperSampleLevels <= 0 || config.SuperSampleLevels > 8) { throw new ArgumentOutOfRangeException("SuperSampleLevels = [" + config.SuperSampleLevels + "] is an unsupported value. Please use values in the range [1,8]"); } int margin = 2; //margin in initial bitmap (don't bother to make configurable - likely to cause confusion int pageWidth = config.PageWidth * config.SuperSampleLevels; //texture page width int pageHeight = config.PageHeight * config.SuperSampleLevels; //texture page height bool usePowerOfTwo = config.ForcePowerOfTwo; int glyphMargin = config.GlyphMargin * config.SuperSampleLevels; QFontGlyph[] initialGlyphs; var sizes = GetGlyphSizes(font); var maxSize = GetMaxGlyphSize(sizes); var initialBmp = CreateInitialBitmap(font, maxSize, margin, out initialGlyphs,config.TextGenerationRenderHint); var initialBitmapData = initialBmp.LockBits(new Rectangle(0, 0, initialBmp.Width, initialBmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); int minYOffset = int.MaxValue; foreach (var glyph in initialGlyphs){ RetargetGlyphRectangleInwards(initialBitmapData, glyph, true, config.KerningConfig.alphaEmptyPixelTolerance); minYOffset = Math.Min(minYOffset,glyph.yOffset); } minYOffset--; //give one pixel of breathing room? foreach (var glyph in initialGlyphs) glyph.yOffset -= minYOffset; QFontGlyph[] glyphs; var bitmapPages = GenerateBitmapSheetsAndRepack( initialGlyphs,new BitmapData[1] { initialBitmapData},pageWidth, pageHeight, out glyphs, glyphMargin, usePowerOfTwo); initialBmp.UnlockBits(initialBitmapData); initialBmp.Dispose(); if (config.SuperSampleLevels != 1) { ScaleSheetsAndGlyphs(bitmapPages, glyphs, 1.0f / config.SuperSampleLevels); RetargetAllGlyphs(bitmapPages, glyphs,config.KerningConfig.alphaEmptyPixelTolerance); } //create list of texture pages var fontData = new QFontData(); fontData.CharSetMapping = CreateCharGlyphMapping(glyphs); fontData.Pages = bitmapPages.ToArray(); fontData.CalculateMeanWidth(); fontData.CalculateMaxHeight(); fontData.KerningPairs = KerningCalculator.CalculateKerning(charSet.ToCharArray(), glyphs, bitmapPages,config.KerningConfig); fontData.naturallyMonospaced = IsMonospaced(sizes); if (saveName != null) { if (bitmapPages.Count == 1) bitmapPages[0].Bitmap.Save(saveName + ".png", System.Drawing.Imaging.ImageFormat.Png); else { for (int i = 0; i < bitmapPages.Count; i++) bitmapPages[i].Bitmap.Save(saveName + "_sheet_" + i + ".png", System.Drawing.Imaging.ImageFormat.Png); } } if (config.ShadowConfig != null) fontData.dropShadow = BuildDropShadow(bitmapPages, glyphs, config.ShadowConfig, charSet.ToCharArray(),config.KerningConfig.alphaEmptyPixelTolerance); //validate glyphs var intercept = FirstIntercept(fontData.CharSetMapping); if (intercept != null) throw new Exception("Failed to create glyph set. Glyphs '" + intercept[0] + "' and '" + intercept[1] + "' were overlapping. This is could be due to an error in the font, or a bug in Graphics.MeasureString()."); return fontData; }
public void SetData (QFontData data) { fontData = data; }
/* * public static QFontData LoadQFontDataFromFile(string filePath) * { * return LoadQFontDataFromFile(filePath, 1.0f); * }*/ public static QFontData LoadQFontDataFromFile(string filePath, float downSampleFactor, QFontShadowConfiguration shadowConfig) { var lines = new List <String>(); StreamReader reader = new StreamReader(filePath); string line; while ((line = reader.ReadLine()) != null) { lines.Add(line); } reader.Close(); var data = new QFontData(); int pageCount = 0; char[] charSet; data.Deserialize(lines, out pageCount, out charSet); string namePrefix = filePath.Replace(".qfont", "").Replace(" ", ""); data.Pages = new TexturePage[pageCount]; var bitmapPages = new List <QBitmap>(); if (pageCount == 1) { bitmapPages.Add(new QBitmap(namePrefix + ".png")); } else { for (int i = 0; i < pageCount; i++) { bitmapPages.Add(new QBitmap(namePrefix + "_sheet_" + i)); } } if (downSampleFactor != 1.0f) { foreach (var page in bitmapPages) { page.DownScale32((int)(page.bitmap.Width * downSampleFactor), (int)(page.bitmap.Height * downSampleFactor)); } } for (int i = 0; i < pageCount; i++) { data.Pages[i] = new TexturePage(bitmapPages[i].bitmapData); } foreach (var glyph in data.CharSetMapping.Values) { glyph.rect = new Rectangle((int)(glyph.rect.X * downSampleFactor), (int)(glyph.rect.Y * downSampleFactor), (int)(glyph.rect.Width * downSampleFactor), (int)(glyph.rect.Height * downSampleFactor)); RetargetGlyphRectangleOutwards(bitmapPages[glyph.page].bitmapData, glyph, false); glyph.yOffset = (int)(glyph.yOffset * downSampleFactor); } var intercept = FirstIntercept(data.CharSetMapping); if (intercept != null) { throw new Exception("Failed to load font from file. Glyphs '" + intercept[0] + "' and '" + intercept[1] + "' were overlapping. If you are texturing your font without locking pixel opacity, then consider using a larger glyph margin. This can be done by setting QFontBuilderConfiguration myQfontBuilderConfig.GlyphMargin, and passing it into CreateTextureFontFiles."); } var glyphList = new List <QFontGlyph>(); foreach (var c in charSet) { glyphList.Add(data.CharSetMapping[c]); } if (shadowConfig != null) { data.dropShadow = BuildDropShadow(bitmapPages, glyphList.ToArray(), shadowConfig, charSet.ToArray()); } data.KerningPairs = KerningCalculator.CalculateKerning(charSet.ToArray(), glyphList.ToArray(), bitmapPages); data.CalculateMeanWidth(); data.CalculateMaxHeight(); for (int i = 0; i < pageCount; i++) { bitmapPages[i].Free(); } return(data); }
/* public static QFontData LoadQFontDataFromFile(string filePath) { return LoadQFontDataFromFile(filePath, 1.0f); }*/ public static QFontData LoadQFontDataFromFile(string filePath, float downSampleFactor, QFontShadowConfiguration shadowConfig) { var lines = new List<String>(); StreamReader reader = new StreamReader(filePath); string line; while((line = reader.ReadLine()) != null) lines.Add(line); reader.Close(); var data = new QFontData(); int pageCount = 0; char[] charSet; data.Deserialize(lines, out pageCount, out charSet); string namePrefix = filePath.Replace(".qfont","").Replace(" ", ""); data.Pages = new TexturePage[pageCount]; var bitmapPages = new List<QBitmap>(); if (pageCount == 1) { bitmapPages.Add(new QBitmap(namePrefix + ".png")); } else { for (int i = 0; i < pageCount; i++) bitmapPages.Add(new QBitmap(namePrefix + "_sheet_" + i)); } if (downSampleFactor != 1.0f) { foreach (var page in bitmapPages) page.DownScale32((int)(page.bitmap.Width * downSampleFactor), (int)(page.bitmap.Height * downSampleFactor)); } for(int i = 0; i < pageCount; i ++ ) data.Pages[i] = new TexturePage(bitmapPages[i].bitmapData); foreach (var glyph in data.CharSetMapping.Values) { glyph.rect = new Rectangle((int)(glyph.rect.X * downSampleFactor), (int)(glyph.rect.Y * downSampleFactor), (int)(glyph.rect.Width * downSampleFactor), (int)(glyph.rect.Height * downSampleFactor)); RetargetGlyphRectangleOutwards(bitmapPages[glyph.page].bitmapData, glyph,false); glyph.yOffset = (int)(glyph.yOffset * downSampleFactor); } var intercept = FirstIntercept(data.CharSetMapping); if (intercept != null) { throw new Exception("Failed to load font from file. Glyphs '" + intercept[0] + "' and '" + intercept[1] + "' were overlapping. If you are texturing your font without locking pixel opacity, then consider using a larger glyph margin. This can be done by setting QFontBuilderConfiguration myQfontBuilderConfig.GlyphMargin, and passing it into CreateTextureFontFiles."); } var glyphList = new List<QFontGlyph>(); foreach (var c in charSet) glyphList.Add(data.CharSetMapping[c]); if (shadowConfig != null) data.dropShadow = BuildDropShadow(bitmapPages, glyphList.ToArray(), shadowConfig, charSet.ToArray()); data.KerningPairs = KerningCalculator.CalculateKerning(charSet.ToArray(), glyphList.ToArray(), bitmapPages); data.CalculateMeanWidth(); data.CalculateMaxHeight(); for (int i = 0; i < pageCount; i++) bitmapPages[i].Free(); return data; }
public QFontDrawingPimitive(QFont font, QFontRenderOptions options) { Font = font.FontData; Options = options; }
public NxFont Load(string filePath, float height, float downSampleFactor, NxFontLoaderConfiguration loaderConfig) { if (loaderConfig == null) { throw new ArgumentNullException ("loaderConfig"); } float fontScale; TransformViewport? transToVp = NxFont.SetupTransformViewport (height, loaderConfig.TransformToCurrentOrthogProjection, loaderConfig.Transform, out fontScale); var qfont = new NxFont(); var internalConfig = new QFontLoaderConfiguration(); var fontData = new QFontData (); qfont.SetData (fontData); QFontDataInformation fontInfo = null; using (var fs = File.OpenRead (filePath)) { fontInfo = fontData.LoadFromStream (fs); } var bitmapFiles = fontInfo.GenerateBitmapPageNames (filePath); var bitmapPages = new List<NxBitmap> (); foreach (var bitmapFileName in bitmapFiles) { // TODO : STREAM BASED REPLACEMENT // https://support.microsoft.com/en-us/kb/814675 // GDI+ require the bitmap files to be locked as indexed image // during the lifetime i.e. maybe reloaded from disk using (var fs = File.OpenRead (bitmapFileName)) { var parent = new Bitmap (fs); var data = parent.LockBits ( new Rectangle(0,0, parent.Width, parent.Height) ,System.Drawing.Imaging.ImageLockMode.ReadWrite ,parent.PixelFormat); var target = new QBitmapData (data); var qb = new NxBitmap (parent, target); bitmapPages.Add (qb); } } var glyphList = fontData.InitialiseQFontData (fontInfo, ref bitmapPages, downSampleFactor, internalConfig); if (loaderConfig.ShadowConfig != null) { qfont.DropShadow = Helper.BuildDropShadow<NxFont, NxBitmap> ( bitmapPages, glyphList.ToArray (), loaderConfig.ShadowConfig, Helper.ToArray (fontInfo.CharSet), internalConfig.KerningConfig.alphaEmptyPixelTolerance); } fontData.InitialiseKerningPairs (fontInfo, bitmapPages, glyphList, internalConfig); if (loaderConfig.ShadowConfig != null) qfont.Options.DropShadowActive = true; if (transToVp != null) qfont.Options.TransformToViewport = transToVp; qfont.InitialiseGlyphRenderer(loaderConfig.CharacterOutput, loaderConfig.FontGlyphRenderer, loaderConfig.DropShadowRenderer); return qfont; }
internal QFont(QFontData fontData) { FontData = fontData; }
private void RenderDropShadow(float x, float y, char c, QFontGlyph nonShadowGlyph, QFontData shadowFont, ref Rectangle clippingRectangle) { //note can cast drop shadow offset to int, but then you can't move the shadow smoothly... if (shadowFont != null && Options.DropShadowActive) { var xOffset = Font.meanGlyphWidth * Options.DropShadowOffset.X + nonShadowGlyph.rect.Width * 0.5f; var yOffset = Font.meanGlyphWidth * Options.DropShadowOffset.Y + nonShadowGlyph.rect.Height * 0.5f + nonShadowGlyph.yOffset; RenderGlyph(x + xOffset, y + yOffset, c, shadowFont, ShadowVertexRepr, ref clippingRectangle); } }
private static QFont BuildDropShadow(List <QBitmap> sourceFontSheets, QFontGlyph[] sourceFontGlyphs, QFontShadowConfiguration shadowConfig, char[] charSet, byte alphaTolerance) { QFontGlyph[] newGlyphs; var sourceBitmapData = new List <BitmapData>(); foreach (var sourceSheet in sourceFontSheets) { sourceBitmapData.Add(sourceSheet.bitmapData); } var bitmapSheets = GenerateBitmapSheetsAndRepack(sourceFontGlyphs, sourceBitmapData.ToArray(), shadowConfig.PageMaxTextureSize, shadowConfig.PageMaxTextureSize, out newGlyphs, shadowConfig.GlyphMargin + shadowConfig.blurRadius * 3); //scale up in case we wanted bigger/smaller shadows if (shadowConfig.Scale != 1.0f) { ScaleSheetsAndGlyphs(bitmapSheets, newGlyphs, shadowConfig.Scale); //no point in retargeting yet, since we will do it after blur } //whiten and blur foreach (var bitmapSheet in bitmapSheets) { bitmapSheet.Colour32(255, 255, 255); if (shadowConfig.Type == ShadowType.Blurred) { bitmapSheet.BlurAlpha(shadowConfig.blurRadius, shadowConfig.blurPasses); } else { bitmapSheet.ExpandAlpha(shadowConfig.blurRadius, shadowConfig.blurPasses); } } //retarget after blur and scale RetargetAllGlyphs(bitmapSheets, newGlyphs, alphaTolerance); //create list of texture pages var newTextureSheets = new List <TexturePage>(); foreach (var page in bitmapSheets) { newTextureSheets.Add(new TexturePage(page.bitmapData)); } var fontData = new QFontData(); fontData.CharSetMapping = new Dictionary <char, QFontGlyph>(); for (int i = 0; i < charSet.Length; i++) { fontData.CharSetMapping.Add(charSet[i], newGlyphs[i]); } fontData.Pages = newTextureSheets.ToArray(); fontData.CalculateMeanWidth(); fontData.CalculateMaxHeight(); foreach (var sheet in bitmapSheets) { sheet.Free(); } fontData.isDropShadow = true; return(new QFont(fontData)); }
public QFontDrawingPimitive(QFont font) { Font = font.FontData; Options = new QFontRenderOptions(); }
private void LoadQFontFromQFontFile(FontLoadDescription loadDescription) { var loaderConfig = loadDescription.LoaderConfig; var filePath = loadDescription.Path; var downSampleFactor = loadDescription.DownSampleFactor; if (loaderConfig == null) loaderConfig = new QFontLoaderConfiguration(); TransformViewport? transToVp = null; float fontScale = 1f; if (loaderConfig.TransformToCurrentOrthogProjection) transToVp = OrthogonalTransform(out fontScale); fontData = Builder.LoadQFontDataFromFile(filePath, downSampleFactor * fontScale, loaderConfig); fontData.scaleDueToTransformToViewport = fontScale; if (loaderConfig.ShadowConfig != null) Options.DropShadowActive = true; if (transToVp != null) Options.TransformToViewport = transToVp; }
internal QFont(IFontRenderer renderer, QFontData fontData) : this(renderer) { this.fontData = fontData; }
private void LoadQFontFromFontFile(FontLoadDescription loadDescription) { var config = loadDescription.BuilderConfig; var fileName = loadDescription.Path; var size = loadDescription.Size; var style = loadDescription.Style; if (config == null) config = new QFontBuilderConfiguration(); TransformViewport? transToVp = null; float dpiX, dpiY; float fontScale = 1.0f; using (Graphics graphics = Graphics.FromHwnd(IntPtr.Zero)) { dpiX = graphics.DpiX; dpiY = graphics.DpiY; } float distancefrom120 = 120 - dpiX; float percent = (120 - distancefrom120) / 120; if (dpiX != 120f) if (percent > 1) fontScale = 1 - Math.Abs(percent - 1); else fontScale = 1f + (1 - percent); fontScale = Math.Abs(fontScale); if (config.TransformToCurrentOrthogProjection) transToVp = OrthogonalTransform(out fontScale); //dont move this into a separate method - it needs to stay in scope! PrivateFontCollection pfc = new PrivateFontCollection(); pfc.AddFontFile(fileName); var fontFamily = pfc.Families[0]; if (!fontFamily.IsStyleAvailable(style)) throw new ArgumentException("Font file: " + fileName + " does not support style: " + style); var font = new Font(fontFamily, size * fontScale * config.SuperSampleLevels, style); //var font = ObtainFont(fileName, size * fontScale * config.SuperSampleLevels, style) fontData = BuildFont(font, config, null); fontData.scaleDueToTransformToViewport = fontScale; font.Dispose(); if (config.ShadowConfig != null) Options.DropShadowActive = true; if (transToVp != null) Options.TransformToViewport = transToVp; }
internal QFont(QFontData fontData) { this.fontData = fontData; }
public void MeasureNodes(QFontData fontData, QFontRenderOptions options) { foreach(TextNode node in this){ if(node.Length == 0f) node.Length = MeasureTextNodeLength(node,fontData,options); } }
public void MeasureNodes(QFontData fontData, QFontRenderOptions options){ foreach(TextNode node in this){ if (Math.Abs (node.Length) < float.Epsilon) node.Length = fontData.MeasureTextNodeLength (node, options); } }
public static void CreateTextureFontFiles(string fileName, float size, FontStyle style, QFontBuilderConfiguration config, string newFontName) { PrivateFontCollection pfc = new PrivateFontCollection(); pfc.AddFontFile(fileName); var fontFamily = pfc.Families[0]; if (!fontFamily.IsStyleAvailable(style)) throw new ArgumentException("Font file: " + fileName + " does not support style: " + style); QFontData fontData = null; if (config == null) config = new QFontBuilderConfiguration(); using(var font = new Font(fontFamily, size * config.SuperSampleLevels, style)){ fontData = BuildFont(font, config, newFontName); } Builder.SaveQFontDataToFile(fontData, newFontName); }
private void RenderDropShadow(float x, float y, char c, QFontGlyph nonShadowGlyph) { //note can cast drop shadow offset to int, but then you can't move the shadow smoothly... if (fontData.dropShadow != null && Options.DropShadowActive) { var fData = fontData; fontData = fontData.dropShadow; RenderGlyph( x + (fontData.meanGlyphWidth * Options.DropShadowOffset.X + nonShadowGlyph.rect.Width * 0.5f), y + (fontData.meanGlyphWidth * Options.DropShadowOffset.Y + nonShadowGlyph.rect.Height * 0.5f + nonShadowGlyph.yOffset), c, true); fontData = fData; } }
private void LoadQFontFromQFontFile(FontLoadDescription loadDescription) { var loaderConfig = loadDescription.LoaderConfig; var filePath = loadDescription.Path; var downSampleFactor = loadDescription.DownSampleFactor; if (loaderConfig == null) loaderConfig = new QFontLoaderConfiguration(); fontData = Builder.LoadQFontDataFromFile(filePath, downSampleFactor, loaderConfig); LoadTextures(fontData); fontData.scaleDueToTransformToViewport = 1f; if (loaderConfig.ShadowConfig != null) Options.DropShadowActive = true; }
public QFont(string fileName, float size, FontStyle style, QFontBuilderConfiguration config, QFontShadowConfiguration shadowConfig) { PrivateFontCollection pfc = new PrivateFontCollection(); pfc.AddFontFile(fileName); var fontFamily = pfc.Families[0]; if (!fontFamily.IsStyleAvailable(style)) throw new ArgumentException("Font file: " + fileName + " does not support style: " + style ); if (config == null) config = new QFontBuilderConfiguration(); using (var font = new Font(fontFamily, size * config.SuperSampleLevels, style)) { fontData = BuildFont(font, config, shadowConfig, null); } if (shadowConfig != null) options.DropShadowActive = true; }
private void LoadTextures(QFontData fdata) { if (fdata.dropShadow != null) LoadTextures(fdata.dropShadow); foreach (var page in fdata.Pages) { page.Texture = renderer.CreateTexture(page.BitmapData); page.Free(); } }
private static QFont BuildDropShadow(List<QBitmap> sourceFontSheets, QFontGlyph[] sourceFontGlyphs, QFontShadowConfiguration shadowConfig, char[] charSet, byte alphaTolerance) { QFontGlyph[] newGlyphs; var sourceBitmapData = new List<BitmapData>(); foreach(var sourceSheet in sourceFontSheets) sourceBitmapData.Add(sourceSheet.bitmapData); var bitmapSheets = GenerateBitmapSheetsAndRepack(sourceFontGlyphs, sourceBitmapData.ToArray(), shadowConfig.PageMaxTextureSize, shadowConfig.PageMaxTextureSize, out newGlyphs, shadowConfig.GlyphMargin + shadowConfig.blurRadius*3); //scale up in case we wanted bigger/smaller shadows if (shadowConfig.Scale != 1.0f) ScaleSheetsAndGlyphs(bitmapSheets, newGlyphs, shadowConfig.Scale); //no point in retargeting yet, since we will do it after blur //whiten and blur foreach (var bitmapSheet in bitmapSheets) { bitmapSheet.Colour32(255, 255, 255); if (shadowConfig.Type == ShadowType.Blurred) bitmapSheet.BlurAlpha(shadowConfig.blurRadius, shadowConfig.blurPasses); else bitmapSheet.ExpandAlpha(shadowConfig.blurRadius, shadowConfig.blurPasses); } //retarget after blur and scale RetargetAllGlyphs(bitmapSheets, newGlyphs, alphaTolerance); //create list of texture pages var newTextureSheets = new List<TexturePage>(); foreach (var page in bitmapSheets) newTextureSheets.Add(new TexturePage(page.bitmapData)); var fontData = new QFontData(); fontData.CharSetMapping = new Dictionary<char, QFontGlyph>(); for(int i = 0; i < charSet.Length; i++) fontData.CharSetMapping.Add(charSet[i],newGlyphs[i]); fontData.Pages = newTextureSheets.ToArray(); fontData.CalculateMeanWidth(); fontData.CalculateMaxHeight(); foreach (var sheet in bitmapSheets) sheet.Free(); fontData.isDropShadow = true; return new QFont(fontData); }
internal QFont(QFontData fontData) { this._fontData = fontData; }
public NxFont (string fileName, float size, float height, FontStyle style, NxFontBuilderConfiguration config) { optionsStack = new ConcurrentStack<QFontRenderOptions> (); PrivateFontCollection pfc = new PrivateFontCollection(); pfc.AddFontFile(fileName); var fontFamily = pfc.Families[0]; if (!fontFamily.IsStyleAvailable(style)) throw new ArgumentException("Font file: " + fileName + " does not support style: " + style ); if (config == null) { throw new ArgumentNullException ("config"); } float fontScale; var transToVp = SetupTransformViewport (height, config.TransformToCurrentOrthogProjection, config.Transform, out fontScale); var internalConfig = new QFontBuilderConfiguration (config.AddDropShadow); internalConfig.SuperSampleLevels = config.SuperSampleLevels; if (internalConfig.ShadowConfig != null) { internalConfig.ShadowConfig.blurRadius = config.BlurRadius; } using(var font = new Font(fontFamily, size * fontScale * config.SuperSampleLevels, style)){ var builder = new Builder<NxFont>(font, internalConfig); NxFont dropShadowFont; fontData = builder.BuildFontData(null, out dropShadowFont); DropShadow = dropShadowFont; } if (internalConfig.ShadowConfig != null) Options.DropShadowActive = true; if (transToVp != null) Options.TransformToViewport = transToVp; InitialiseGlyphRenderer(config.CharacterOutput, config.FontGlyphRenderer, config.DropShadowRenderer); }